From 48e6b5573016036676d9df90f418ec8b083c1cff Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Wed, 24 Jun 2015 18:18:49 +0200 Subject: bump all kernel versions --- target/linux/patches/2.6.32.65/cris-header.patch | 60 - .../linux/patches/2.6.32.65/cris-initramfs.patch | 22 - target/linux/patches/2.6.32.67/cris-header.patch | 60 + .../linux/patches/2.6.32.67/cris-initramfs.patch | 22 + .../linux/patches/3.10.75/bsd-compatibility.patch | 2538 -- target/linux/patches/3.10.75/headers-install.patch | 11 - target/linux/patches/3.10.75/mkpiggy.patch | 28 - target/linux/patches/3.10.75/relocs.patch | 2693 -- target/linux/patches/3.10.75/sgidefs.patch | 18 - target/linux/patches/3.10.75/sortext.patch | 33 - target/linux/patches/3.10.75/startup.patch | 34 - target/linux/patches/3.10.75/yaffs2.patch | 16547 --------- .../linux/patches/3.10.81/bsd-compatibility.patch | 2538 ++ target/linux/patches/3.10.81/headers-install.patch | 11 + target/linux/patches/3.10.81/mkpiggy.patch | 28 + target/linux/patches/3.10.81/relocs.patch | 2693 ++ target/linux/patches/3.10.81/sgidefs.patch | 18 + target/linux/patches/3.10.81/sortext.patch | 33 + target/linux/patches/3.10.81/startup.patch | 34 + target/linux/patches/3.10.81/yaffs2.patch | 16547 +++++++++ .../linux/patches/3.12.40/bsd-compatibility.patch | 2538 -- target/linux/patches/3.12.40/cleankernel.patch | 11 - target/linux/patches/3.12.40/defaults.patch | 46 - .../linux/patches/3.12.40/disable-netfilter.patch | 160 - .../patches/3.12.40/export-symbol-for-exmap.patch | 11 - target/linux/patches/3.12.40/gemalto.patch | 11 - target/linux/patches/3.12.40/lemote-rfkill.patch | 21 - .../patches/3.12.40/microblaze-ethernet.patch | 11 - .../linux/patches/3.12.40/microblaze-setup.patch | 12 - target/linux/patches/3.12.40/mips-lzo-fix.patch | 12 - target/linux/patches/3.12.40/mkpiggy.patch | 28 - target/linux/patches/3.12.40/mtd-rootfs.patch | 26 - target/linux/patches/3.12.40/non-static.patch | 33 - .../linux/patches/3.12.40/ppc64-missing-zlib.patch | 11 - target/linux/patches/3.12.40/regmap-bool.patch | 20 - target/linux/patches/3.12.40/relocs.patch | 2693 -- target/linux/patches/3.12.40/sgidefs.patch | 18 - target/linux/patches/3.12.40/sortext.patch | 33 - target/linux/patches/3.12.40/startup.patch | 34 - .../linux/patches/3.12.40/usb-defaults-off.patch | 20 - .../patches/3.12.40/vga-cons-default-off.patch | 12 - target/linux/patches/3.12.40/wlan-cf.patch | 11 - target/linux/patches/3.12.40/xargs.patch | 12 - target/linux/patches/3.12.40/yaffs2.patch | 16547 --------- target/linux/patches/3.12.40/zlib-inflate.patch | 12 - .../linux/patches/3.12.44/bsd-compatibility.patch | 2538 ++ target/linux/patches/3.12.44/cleankernel.patch | 11 + target/linux/patches/3.12.44/defaults.patch | 46 + .../linux/patches/3.12.44/disable-netfilter.patch | 160 + .../patches/3.12.44/export-symbol-for-exmap.patch | 11 + target/linux/patches/3.12.44/gemalto.patch | 11 + target/linux/patches/3.12.44/lemote-rfkill.patch | 21 + .../patches/3.12.44/microblaze-ethernet.patch | 11 + .../linux/patches/3.12.44/microblaze-setup.patch | 12 + target/linux/patches/3.12.44/mips-lzo-fix.patch | 12 + target/linux/patches/3.12.44/mkpiggy.patch | 28 + target/linux/patches/3.12.44/mtd-rootfs.patch | 26 + target/linux/patches/3.12.44/non-static.patch | 33 + .../linux/patches/3.12.44/ppc64-missing-zlib.patch | 11 + target/linux/patches/3.12.44/regmap-bool.patch | 20 + target/linux/patches/3.12.44/relocs.patch | 2693 ++ target/linux/patches/3.12.44/sgidefs.patch | 18 + target/linux/patches/3.12.44/sortext.patch | 33 + target/linux/patches/3.12.44/startup.patch | 34 + .../linux/patches/3.12.44/usb-defaults-off.patch | 20 + target/linux/patches/3.12.44/wlan-cf.patch | 11 + target/linux/patches/3.12.44/xargs.patch | 12 + target/linux/patches/3.12.44/yaffs2.patch | 16547 +++++++++ target/linux/patches/3.12.44/zlib-inflate.patch | 12 + .../linux/patches/3.14.43/bsd-compatibility.patch | 2538 -- target/linux/patches/3.14.43/cleankernel.patch | 11 - target/linux/patches/3.14.43/defaults.patch | 34 - .../linux/patches/3.14.43/disable-netfilter.patch | 160 - .../patches/3.14.43/export-symbol-for-exmap.patch | 11 - target/linux/patches/3.14.43/fblogo.patch | 2097 -- target/linux/patches/3.14.43/gemalto.patch | 11 - .../patches/3.14.43/initramfs-nosizelimit.patch | 57 - target/linux/patches/3.14.43/lemote-rfkill.patch | 21 - target/linux/patches/3.14.43/microblaze-axi.patch | 11 - .../patches/3.14.43/microblaze-ethernet.patch | 11 - target/linux/patches/3.14.43/mkpiggy.patch | 28 - target/linux/patches/3.14.43/mptcp.patch | 17203 --------- target/linux/patches/3.14.43/mtd-rootfs.patch | 26 - target/linux/patches/3.14.43/nfsv3-tcp.patch | 12 - target/linux/patches/3.14.43/non-static.patch | 33 - .../linux/patches/3.14.43/ppc64-missing-zlib.patch | 11 - target/linux/patches/3.14.43/regmap-boolean.patch | 24 - target/linux/patches/3.14.43/relocs.patch | 2709 -- target/linux/patches/3.14.43/sgidefs.patch | 18 - target/linux/patches/3.14.43/sortext.patch | 33 - target/linux/patches/3.14.43/startup.patch | 37 - target/linux/patches/3.14.43/wlan-cf.patch | 11 - target/linux/patches/3.14.43/xargs.patch | 12 - target/linux/patches/3.14.43/yaffs2.patch | 16559 --------- target/linux/patches/3.14.43/zlib-inflate.patch | 12 - .../linux/patches/3.14.45/bsd-compatibility.patch | 2538 ++ target/linux/patches/3.14.45/cleankernel.patch | 11 + target/linux/patches/3.14.45/defaults.patch | 34 + .../linux/patches/3.14.45/disable-netfilter.patch | 160 + .../patches/3.14.45/export-symbol-for-exmap.patch | 11 + target/linux/patches/3.14.45/fblogo.patch | 2097 ++ target/linux/patches/3.14.45/gemalto.patch | 11 + .../patches/3.14.45/initramfs-nosizelimit.patch | 57 + target/linux/patches/3.14.45/lemote-rfkill.patch | 21 + target/linux/patches/3.14.45/microblaze-axi.patch | 11 + .../patches/3.14.45/microblaze-ethernet.patch | 11 + target/linux/patches/3.14.45/mkpiggy.patch | 28 + target/linux/patches/3.14.45/mptcp.patch | 17133 +++++++++ target/linux/patches/3.14.45/mtd-rootfs.patch | 26 + target/linux/patches/3.14.45/nfsv3-tcp.patch | 12 + target/linux/patches/3.14.45/non-static.patch | 33 + .../linux/patches/3.14.45/ppc64-missing-zlib.patch | 11 + target/linux/patches/3.14.45/regmap-boolean.patch | 24 + target/linux/patches/3.14.45/relocs.patch | 2709 ++ target/linux/patches/3.14.45/sgidefs.patch | 18 + target/linux/patches/3.14.45/sortext.patch | 33 + target/linux/patches/3.14.45/startup.patch | 37 + target/linux/patches/3.14.45/wlan-cf.patch | 11 + target/linux/patches/3.14.45/xargs.patch | 12 + target/linux/patches/3.14.45/yaffs2.patch | 16559 +++++++++ target/linux/patches/3.14.45/zlib-inflate.patch | 12 + .../linux/patches/3.18.14/bsd-compatibility.patch | 2538 -- target/linux/patches/3.18.14/cleankernel.patch | 11 - target/linux/patches/3.18.14/cris-header.patch | 50 - target/linux/patches/3.18.14/cris-initramfs.patch | 22 - target/linux/patches/3.18.14/defaults.patch | 46 - .../patches/3.18.14/export-symbol-for-exmap.patch | 11 - target/linux/patches/3.18.14/fblogo.patch | 2057 -- target/linux/patches/3.18.14/gemalto.patch | 11 - .../patches/3.18.14/initramfs-nosizelimit.patch | 57 - target/linux/patches/3.18.14/lemote-rfkill.patch | 21 - .../patches/3.18.14/microblaze-ethernet.patch | 11 - target/linux/patches/3.18.14/mkpiggy.patch | 28 - target/linux/patches/3.18.14/mtd-rootfs.patch | 26 - target/linux/patches/3.18.14/nfsv3-tcp.patch | 12 - target/linux/patches/3.18.14/non-static.patch | 33 - .../linux/patches/3.18.14/ppc64-missing-zlib.patch | 11 - target/linux/patches/3.18.14/realtime.patch | 36846 ------------------- target/linux/patches/3.18.14/regmap-bool.patch | 27 - target/linux/patches/3.18.14/relocs.patch | 2709 -- target/linux/patches/3.18.14/sgidefs.patch | 18 - target/linux/patches/3.18.14/sortext.patch | 33 - target/linux/patches/3.18.14/startup.patch | 37 - target/linux/patches/3.18.14/wlan-cf.patch | 11 - target/linux/patches/3.18.14/xargs.patch | 12 - target/linux/patches/3.18.14/yaffs2.patch | 16559 --------- .../linux/patches/3.18.16/bsd-compatibility.patch | 2538 ++ target/linux/patches/3.18.16/cleankernel.patch | 11 + target/linux/patches/3.18.16/cris-header.patch | 50 + target/linux/patches/3.18.16/cris-initramfs.patch | 22 + target/linux/patches/3.18.16/defaults.patch | 46 + .../patches/3.18.16/export-symbol-for-exmap.patch | 11 + target/linux/patches/3.18.16/fblogo.patch | 2057 ++ target/linux/patches/3.18.16/gemalto.patch | 11 + .../patches/3.18.16/initramfs-nosizelimit.patch | 57 + target/linux/patches/3.18.16/lemote-rfkill.patch | 21 + .../patches/3.18.16/microblaze-ethernet.patch | 11 + target/linux/patches/3.18.16/mkpiggy.patch | 28 + target/linux/patches/3.18.16/mtd-rootfs.patch | 26 + target/linux/patches/3.18.16/nfsv3-tcp.patch | 12 + target/linux/patches/3.18.16/non-static.patch | 33 + .../linux/patches/3.18.16/ppc64-missing-zlib.patch | 11 + target/linux/patches/3.18.16/realtime.patch | 36846 +++++++++++++++++++ target/linux/patches/3.18.16/regmap-bool.patch | 27 + target/linux/patches/3.18.16/relocs.patch | 2709 ++ target/linux/patches/3.18.16/sgidefs.patch | 18 + target/linux/patches/3.18.16/sortext.patch | 33 + target/linux/patches/3.18.16/startup.patch | 37 + target/linux/patches/3.18.16/wlan-cf.patch | 11 + target/linux/patches/3.18.16/xargs.patch | 12 + target/linux/patches/3.18.16/yaffs2.patch | 16559 +++++++++ target/linux/patches/3.2.68/sparc-aout.patch | 24 - target/linux/patches/3.2.69/sparc-aout.patch | 24 + target/linux/patches/3.4.107/aufs2.patch | 238 - .../linux/patches/3.4.107/bsd-compatibility.patch | 2512 -- target/linux/patches/3.4.107/defaults.patch | 22 - target/linux/patches/3.4.107/gemalto.patch | 11 - target/linux/patches/3.4.107/lemote-rfkill.patch | 21 - target/linux/patches/3.4.107/linux-gcc-check.patch | 18 - target/linux/patches/3.4.107/mips-error.patch | 254 - target/linux/patches/3.4.107/mkpiggy.patch | 28 - .../patches/3.4.107/module-alloc-size-check.patch | 21 - target/linux/patches/3.4.107/non-static.patch | 33 - target/linux/patches/3.4.107/relocs.patch | 3131 -- target/linux/patches/3.4.107/sparc-aout.patch | 24 - target/linux/patches/3.4.107/sparc-include.patch | 11 - target/linux/patches/3.4.107/startup.patch | 34 - .../linux/patches/3.4.107/usb-defaults-off.patch | 32 - .../patches/3.4.107/vga-cons-default-off.patch | 12 - target/linux/patches/3.4.107/wlan-cf.patch | 11 - target/linux/patches/3.4.107/yaffs2.patch | 16550 --------- target/linux/patches/3.4.107/zlib-inflate.patch | 12 - target/linux/patches/3.4.108/aufs2.patch | 238 + .../linux/patches/3.4.108/bsd-compatibility.patch | 2512 ++ target/linux/patches/3.4.108/defaults.patch | 22 + target/linux/patches/3.4.108/gemalto.patch | 11 + target/linux/patches/3.4.108/lemote-rfkill.patch | 21 + target/linux/patches/3.4.108/linux-gcc-check.patch | 18 + target/linux/patches/3.4.108/mips-error.patch | 254 + target/linux/patches/3.4.108/mkpiggy.patch | 28 + .../patches/3.4.108/module-alloc-size-check.patch | 21 + target/linux/patches/3.4.108/non-static.patch | 33 + target/linux/patches/3.4.108/relocs.patch | 3131 ++ target/linux/patches/3.4.108/sparc-aout.patch | 24 + target/linux/patches/3.4.108/sparc-include.patch | 11 + target/linux/patches/3.4.108/startup.patch | 34 + .../linux/patches/3.4.108/usb-defaults-off.patch | 32 + .../patches/3.4.108/vga-cons-default-off.patch | 12 + target/linux/patches/3.4.108/wlan-cf.patch | 11 + target/linux/patches/3.4.108/yaffs2.patch | 16550 +++++++++ target/linux/patches/3.4.108/zlib-inflate.patch | 12 + target/linux/patches/4.0.5/aufs4.patch | 33819 ----------------- target/linux/patches/4.0.5/cleankernel.patch | 11 - target/linux/patches/4.0.5/cris-header.patch | 12 - .../patches/4.0.5/initramfs-nosizelimit.patch | 57 - target/linux/patches/4.0.5/startup.patch | 37 - target/linux/patches/4.0.6/aufs4.patch | 33819 +++++++++++++++++ target/linux/patches/4.0.6/cleankernel.patch | 11 + target/linux/patches/4.0.6/cris-header.patch | 12 + .../patches/4.0.6/initramfs-nosizelimit.patch | 57 + target/linux/patches/4.0.6/startup.patch | 37 + target/linux/patches/4.1-rc5/cris-header.patch | 12 - .../patches/4.1-rc5/initramfs-nosizelimit.patch | 57 - target/linux/patches/4.1-rc5/startup.patch | 37 - target/linux/patches/4.1/cris-header.patch | 12 + .../linux/patches/4.1/initramfs-nosizelimit.patch | 57 + target/linux/patches/4.1/startup.patch | 37 + 227 files changed, 204173 insertions(+), 204255 deletions(-) delete mode 100644 target/linux/patches/2.6.32.65/cris-header.patch delete mode 100644 target/linux/patches/2.6.32.65/cris-initramfs.patch create mode 100644 target/linux/patches/2.6.32.67/cris-header.patch create mode 100644 target/linux/patches/2.6.32.67/cris-initramfs.patch delete mode 100644 target/linux/patches/3.10.75/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.10.75/headers-install.patch delete mode 100644 target/linux/patches/3.10.75/mkpiggy.patch delete mode 100644 target/linux/patches/3.10.75/relocs.patch delete mode 100644 target/linux/patches/3.10.75/sgidefs.patch delete mode 100644 target/linux/patches/3.10.75/sortext.patch delete mode 100644 target/linux/patches/3.10.75/startup.patch delete mode 100644 target/linux/patches/3.10.75/yaffs2.patch create mode 100644 target/linux/patches/3.10.81/bsd-compatibility.patch create mode 100644 target/linux/patches/3.10.81/headers-install.patch create mode 100644 target/linux/patches/3.10.81/mkpiggy.patch create mode 100644 target/linux/patches/3.10.81/relocs.patch create mode 100644 target/linux/patches/3.10.81/sgidefs.patch create mode 100644 target/linux/patches/3.10.81/sortext.patch create mode 100644 target/linux/patches/3.10.81/startup.patch create mode 100644 target/linux/patches/3.10.81/yaffs2.patch delete mode 100644 target/linux/patches/3.12.40/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.12.40/cleankernel.patch delete mode 100644 target/linux/patches/3.12.40/defaults.patch delete mode 100644 target/linux/patches/3.12.40/disable-netfilter.patch delete mode 100644 target/linux/patches/3.12.40/export-symbol-for-exmap.patch delete mode 100644 target/linux/patches/3.12.40/gemalto.patch delete mode 100644 target/linux/patches/3.12.40/lemote-rfkill.patch delete mode 100644 target/linux/patches/3.12.40/microblaze-ethernet.patch delete mode 100644 target/linux/patches/3.12.40/microblaze-setup.patch delete mode 100644 target/linux/patches/3.12.40/mips-lzo-fix.patch delete mode 100644 target/linux/patches/3.12.40/mkpiggy.patch delete mode 100644 target/linux/patches/3.12.40/mtd-rootfs.patch delete mode 100644 target/linux/patches/3.12.40/non-static.patch delete mode 100644 target/linux/patches/3.12.40/ppc64-missing-zlib.patch delete mode 100644 target/linux/patches/3.12.40/regmap-bool.patch delete mode 100644 target/linux/patches/3.12.40/relocs.patch delete mode 100644 target/linux/patches/3.12.40/sgidefs.patch delete mode 100644 target/linux/patches/3.12.40/sortext.patch delete mode 100644 target/linux/patches/3.12.40/startup.patch delete mode 100644 target/linux/patches/3.12.40/usb-defaults-off.patch delete mode 100644 target/linux/patches/3.12.40/vga-cons-default-off.patch delete mode 100644 target/linux/patches/3.12.40/wlan-cf.patch delete mode 100644 target/linux/patches/3.12.40/xargs.patch delete mode 100644 target/linux/patches/3.12.40/yaffs2.patch delete mode 100644 target/linux/patches/3.12.40/zlib-inflate.patch create mode 100644 target/linux/patches/3.12.44/bsd-compatibility.patch create mode 100644 target/linux/patches/3.12.44/cleankernel.patch create mode 100644 target/linux/patches/3.12.44/defaults.patch create mode 100644 target/linux/patches/3.12.44/disable-netfilter.patch create mode 100644 target/linux/patches/3.12.44/export-symbol-for-exmap.patch create mode 100644 target/linux/patches/3.12.44/gemalto.patch create mode 100644 target/linux/patches/3.12.44/lemote-rfkill.patch create mode 100644 target/linux/patches/3.12.44/microblaze-ethernet.patch create mode 100644 target/linux/patches/3.12.44/microblaze-setup.patch create mode 100644 target/linux/patches/3.12.44/mips-lzo-fix.patch create mode 100644 target/linux/patches/3.12.44/mkpiggy.patch create mode 100644 target/linux/patches/3.12.44/mtd-rootfs.patch create mode 100644 target/linux/patches/3.12.44/non-static.patch create mode 100644 target/linux/patches/3.12.44/ppc64-missing-zlib.patch create mode 100644 target/linux/patches/3.12.44/regmap-bool.patch create mode 100644 target/linux/patches/3.12.44/relocs.patch create mode 100644 target/linux/patches/3.12.44/sgidefs.patch create mode 100644 target/linux/patches/3.12.44/sortext.patch create mode 100644 target/linux/patches/3.12.44/startup.patch create mode 100644 target/linux/patches/3.12.44/usb-defaults-off.patch create mode 100644 target/linux/patches/3.12.44/wlan-cf.patch create mode 100644 target/linux/patches/3.12.44/xargs.patch create mode 100644 target/linux/patches/3.12.44/yaffs2.patch create mode 100644 target/linux/patches/3.12.44/zlib-inflate.patch delete mode 100644 target/linux/patches/3.14.43/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.14.43/cleankernel.patch delete mode 100644 target/linux/patches/3.14.43/defaults.patch delete mode 100644 target/linux/patches/3.14.43/disable-netfilter.patch delete mode 100644 target/linux/patches/3.14.43/export-symbol-for-exmap.patch delete mode 100644 target/linux/patches/3.14.43/fblogo.patch delete mode 100644 target/linux/patches/3.14.43/gemalto.patch delete mode 100644 target/linux/patches/3.14.43/initramfs-nosizelimit.patch delete mode 100644 target/linux/patches/3.14.43/lemote-rfkill.patch delete mode 100644 target/linux/patches/3.14.43/microblaze-axi.patch delete mode 100644 target/linux/patches/3.14.43/microblaze-ethernet.patch delete mode 100644 target/linux/patches/3.14.43/mkpiggy.patch delete mode 100644 target/linux/patches/3.14.43/mptcp.patch delete mode 100644 target/linux/patches/3.14.43/mtd-rootfs.patch delete mode 100644 target/linux/patches/3.14.43/nfsv3-tcp.patch delete mode 100644 target/linux/patches/3.14.43/non-static.patch delete mode 100644 target/linux/patches/3.14.43/ppc64-missing-zlib.patch delete mode 100644 target/linux/patches/3.14.43/regmap-boolean.patch delete mode 100644 target/linux/patches/3.14.43/relocs.patch delete mode 100644 target/linux/patches/3.14.43/sgidefs.patch delete mode 100644 target/linux/patches/3.14.43/sortext.patch delete mode 100644 target/linux/patches/3.14.43/startup.patch delete mode 100644 target/linux/patches/3.14.43/wlan-cf.patch delete mode 100644 target/linux/patches/3.14.43/xargs.patch delete mode 100644 target/linux/patches/3.14.43/yaffs2.patch delete mode 100644 target/linux/patches/3.14.43/zlib-inflate.patch create mode 100644 target/linux/patches/3.14.45/bsd-compatibility.patch create mode 100644 target/linux/patches/3.14.45/cleankernel.patch create mode 100644 target/linux/patches/3.14.45/defaults.patch create mode 100644 target/linux/patches/3.14.45/disable-netfilter.patch create mode 100644 target/linux/patches/3.14.45/export-symbol-for-exmap.patch create mode 100644 target/linux/patches/3.14.45/fblogo.patch create mode 100644 target/linux/patches/3.14.45/gemalto.patch create mode 100644 target/linux/patches/3.14.45/initramfs-nosizelimit.patch create mode 100644 target/linux/patches/3.14.45/lemote-rfkill.patch create mode 100644 target/linux/patches/3.14.45/microblaze-axi.patch create mode 100644 target/linux/patches/3.14.45/microblaze-ethernet.patch create mode 100644 target/linux/patches/3.14.45/mkpiggy.patch create mode 100644 target/linux/patches/3.14.45/mptcp.patch create mode 100644 target/linux/patches/3.14.45/mtd-rootfs.patch create mode 100644 target/linux/patches/3.14.45/nfsv3-tcp.patch create mode 100644 target/linux/patches/3.14.45/non-static.patch create mode 100644 target/linux/patches/3.14.45/ppc64-missing-zlib.patch create mode 100644 target/linux/patches/3.14.45/regmap-boolean.patch create mode 100644 target/linux/patches/3.14.45/relocs.patch create mode 100644 target/linux/patches/3.14.45/sgidefs.patch create mode 100644 target/linux/patches/3.14.45/sortext.patch create mode 100644 target/linux/patches/3.14.45/startup.patch create mode 100644 target/linux/patches/3.14.45/wlan-cf.patch create mode 100644 target/linux/patches/3.14.45/xargs.patch create mode 100644 target/linux/patches/3.14.45/yaffs2.patch create mode 100644 target/linux/patches/3.14.45/zlib-inflate.patch delete mode 100644 target/linux/patches/3.18.14/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.18.14/cleankernel.patch delete mode 100644 target/linux/patches/3.18.14/cris-header.patch delete mode 100644 target/linux/patches/3.18.14/cris-initramfs.patch delete mode 100644 target/linux/patches/3.18.14/defaults.patch delete mode 100644 target/linux/patches/3.18.14/export-symbol-for-exmap.patch delete mode 100644 target/linux/patches/3.18.14/fblogo.patch delete mode 100644 target/linux/patches/3.18.14/gemalto.patch delete mode 100644 target/linux/patches/3.18.14/initramfs-nosizelimit.patch delete mode 100644 target/linux/patches/3.18.14/lemote-rfkill.patch delete mode 100644 target/linux/patches/3.18.14/microblaze-ethernet.patch delete mode 100644 target/linux/patches/3.18.14/mkpiggy.patch delete mode 100644 target/linux/patches/3.18.14/mtd-rootfs.patch delete mode 100644 target/linux/patches/3.18.14/nfsv3-tcp.patch delete mode 100644 target/linux/patches/3.18.14/non-static.patch delete mode 100644 target/linux/patches/3.18.14/ppc64-missing-zlib.patch delete mode 100644 target/linux/patches/3.18.14/realtime.patch delete mode 100644 target/linux/patches/3.18.14/regmap-bool.patch delete mode 100644 target/linux/patches/3.18.14/relocs.patch delete mode 100644 target/linux/patches/3.18.14/sgidefs.patch delete mode 100644 target/linux/patches/3.18.14/sortext.patch delete mode 100644 target/linux/patches/3.18.14/startup.patch delete mode 100644 target/linux/patches/3.18.14/wlan-cf.patch delete mode 100644 target/linux/patches/3.18.14/xargs.patch delete mode 100644 target/linux/patches/3.18.14/yaffs2.patch create mode 100644 target/linux/patches/3.18.16/bsd-compatibility.patch create mode 100644 target/linux/patches/3.18.16/cleankernel.patch create mode 100644 target/linux/patches/3.18.16/cris-header.patch create mode 100644 target/linux/patches/3.18.16/cris-initramfs.patch create mode 100644 target/linux/patches/3.18.16/defaults.patch create mode 100644 target/linux/patches/3.18.16/export-symbol-for-exmap.patch create mode 100644 target/linux/patches/3.18.16/fblogo.patch create mode 100644 target/linux/patches/3.18.16/gemalto.patch create mode 100644 target/linux/patches/3.18.16/initramfs-nosizelimit.patch create mode 100644 target/linux/patches/3.18.16/lemote-rfkill.patch create mode 100644 target/linux/patches/3.18.16/microblaze-ethernet.patch create mode 100644 target/linux/patches/3.18.16/mkpiggy.patch create mode 100644 target/linux/patches/3.18.16/mtd-rootfs.patch create mode 100644 target/linux/patches/3.18.16/nfsv3-tcp.patch create mode 100644 target/linux/patches/3.18.16/non-static.patch create mode 100644 target/linux/patches/3.18.16/ppc64-missing-zlib.patch create mode 100644 target/linux/patches/3.18.16/realtime.patch create mode 100644 target/linux/patches/3.18.16/regmap-bool.patch create mode 100644 target/linux/patches/3.18.16/relocs.patch create mode 100644 target/linux/patches/3.18.16/sgidefs.patch create mode 100644 target/linux/patches/3.18.16/sortext.patch create mode 100644 target/linux/patches/3.18.16/startup.patch create mode 100644 target/linux/patches/3.18.16/wlan-cf.patch create mode 100644 target/linux/patches/3.18.16/xargs.patch create mode 100644 target/linux/patches/3.18.16/yaffs2.patch delete mode 100644 target/linux/patches/3.2.68/sparc-aout.patch create mode 100644 target/linux/patches/3.2.69/sparc-aout.patch delete mode 100644 target/linux/patches/3.4.107/aufs2.patch delete mode 100644 target/linux/patches/3.4.107/bsd-compatibility.patch delete mode 100644 target/linux/patches/3.4.107/defaults.patch delete mode 100644 target/linux/patches/3.4.107/gemalto.patch delete mode 100644 target/linux/patches/3.4.107/lemote-rfkill.patch delete mode 100644 target/linux/patches/3.4.107/linux-gcc-check.patch delete mode 100644 target/linux/patches/3.4.107/mips-error.patch delete mode 100644 target/linux/patches/3.4.107/mkpiggy.patch delete mode 100644 target/linux/patches/3.4.107/module-alloc-size-check.patch delete mode 100644 target/linux/patches/3.4.107/non-static.patch delete mode 100644 target/linux/patches/3.4.107/relocs.patch delete mode 100644 target/linux/patches/3.4.107/sparc-aout.patch delete mode 100644 target/linux/patches/3.4.107/sparc-include.patch delete mode 100644 target/linux/patches/3.4.107/startup.patch delete mode 100644 target/linux/patches/3.4.107/usb-defaults-off.patch delete mode 100644 target/linux/patches/3.4.107/vga-cons-default-off.patch delete mode 100644 target/linux/patches/3.4.107/wlan-cf.patch delete mode 100644 target/linux/patches/3.4.107/yaffs2.patch delete mode 100644 target/linux/patches/3.4.107/zlib-inflate.patch create mode 100644 target/linux/patches/3.4.108/aufs2.patch create mode 100644 target/linux/patches/3.4.108/bsd-compatibility.patch create mode 100644 target/linux/patches/3.4.108/defaults.patch create mode 100644 target/linux/patches/3.4.108/gemalto.patch create mode 100644 target/linux/patches/3.4.108/lemote-rfkill.patch create mode 100644 target/linux/patches/3.4.108/linux-gcc-check.patch create mode 100644 target/linux/patches/3.4.108/mips-error.patch create mode 100644 target/linux/patches/3.4.108/mkpiggy.patch create mode 100644 target/linux/patches/3.4.108/module-alloc-size-check.patch create mode 100644 target/linux/patches/3.4.108/non-static.patch create mode 100644 target/linux/patches/3.4.108/relocs.patch create mode 100644 target/linux/patches/3.4.108/sparc-aout.patch create mode 100644 target/linux/patches/3.4.108/sparc-include.patch create mode 100644 target/linux/patches/3.4.108/startup.patch create mode 100644 target/linux/patches/3.4.108/usb-defaults-off.patch create mode 100644 target/linux/patches/3.4.108/vga-cons-default-off.patch create mode 100644 target/linux/patches/3.4.108/wlan-cf.patch create mode 100644 target/linux/patches/3.4.108/yaffs2.patch create mode 100644 target/linux/patches/3.4.108/zlib-inflate.patch delete mode 100644 target/linux/patches/4.0.5/aufs4.patch delete mode 100644 target/linux/patches/4.0.5/cleankernel.patch delete mode 100644 target/linux/patches/4.0.5/cris-header.patch delete mode 100644 target/linux/patches/4.0.5/initramfs-nosizelimit.patch delete mode 100644 target/linux/patches/4.0.5/startup.patch create mode 100644 target/linux/patches/4.0.6/aufs4.patch create mode 100644 target/linux/patches/4.0.6/cleankernel.patch create mode 100644 target/linux/patches/4.0.6/cris-header.patch create mode 100644 target/linux/patches/4.0.6/initramfs-nosizelimit.patch create mode 100644 target/linux/patches/4.0.6/startup.patch delete mode 100644 target/linux/patches/4.1-rc5/cris-header.patch delete mode 100644 target/linux/patches/4.1-rc5/initramfs-nosizelimit.patch delete mode 100644 target/linux/patches/4.1-rc5/startup.patch create mode 100644 target/linux/patches/4.1/cris-header.patch create mode 100644 target/linux/patches/4.1/initramfs-nosizelimit.patch create mode 100644 target/linux/patches/4.1/startup.patch (limited to 'target/linux') diff --git a/target/linux/patches/2.6.32.65/cris-header.patch b/target/linux/patches/2.6.32.65/cris-header.patch deleted file mode 100644 index 1645d519c..000000000 --- a/target/linux/patches/2.6.32.65/cris-header.patch +++ /dev/null @@ -1,60 +0,0 @@ -diff -Nur linux-2.6.32.65.orig/arch/cris/include/arch-v10/arch/Kbuild linux-2.6.32.65/arch/cris/include/arch-v10/arch/Kbuild ---- linux-2.6.32.65.orig/arch/cris/include/arch-v10/arch/Kbuild 2014-12-13 15:16:21.000000000 +0100 -+++ linux-2.6.32.65/arch/cris/include/arch-v10/arch/Kbuild 2015-05-20 10:00:19.000000000 +0200 -@@ -1,4 +1,5 @@ - header-y += user.h -+header-y += ptrace.h - header-y += svinto.h - header-y += sv_addr_ag.h - header-y += sv_addr.agh -diff -Nur linux-2.6.32.65.orig/arch/cris/include/arch-v32/arch/Kbuild linux-2.6.32.65/arch/cris/include/arch-v32/arch/Kbuild ---- linux-2.6.32.65.orig/arch/cris/include/arch-v32/arch/Kbuild 2014-12-13 15:16:21.000000000 +0100 -+++ linux-2.6.32.65/arch/cris/include/arch-v32/arch/Kbuild 2015-05-20 10:00:19.000000000 +0200 -@@ -1,2 +1,3 @@ - header-y += user.h -+header-y += ptrace.h - header-y += cryptocop.h -diff -Nur linux-2.6.32.65.orig/arch/cris/include/asm/Kbuild linux-2.6.32.65/arch/cris/include/asm/Kbuild ---- linux-2.6.32.65.orig/arch/cris/include/asm/Kbuild 2014-12-13 15:16:21.000000000 +0100 -+++ linux-2.6.32.65/arch/cris/include/asm/Kbuild 2015-05-20 10:39:33.000000000 +0200 -@@ -1,7 +1,7 @@ - include include/asm-generic/Kbuild.asm - --header-y += arch-v10/ --header-y += arch-v32/ -+header-y += ../arch-v10/arch/ -+header-y += ../arch-v32/arch/ - - header-y += ethernet.h - header-y += rtc.h -diff -Nur linux-2.6.32.65.orig/arch/cris/include/asm/pgtable.h linux-2.6.32.65/arch/cris/include/asm/pgtable.h ---- linux-2.6.32.65.orig/arch/cris/include/asm/pgtable.h 2014-12-13 15:16:21.000000000 +0100 -+++ linux-2.6.32.65/arch/cris/include/asm/pgtable.h 2015-05-20 11:56:24.000000000 +0200 -@@ -14,6 +14,9 @@ - #endif - #include - -+#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ -+ remap_pfn_range(vma, vaddr, pfn, size, prot) -+ - /* - * The Linux memory management assumes a three-level page table setup. On - * CRIS, we use that, but "fold" the mid level into the top-level page -diff -Nur linux-2.6.32.65.orig/include/asm-generic/bug.h linux-2.6.32.65/include/asm-generic/bug.h ---- linux-2.6.32.65.orig/include/asm-generic/bug.h 2014-12-13 15:16:21.000000000 +0100 -+++ linux-2.6.32.65/include/asm-generic/bug.h 2015-05-20 13:39:24.000000000 +0200 -@@ -119,6 +119,14 @@ - }) - #endif - -+#ifndef WARN_RATELIMIT -+#define WARN_RATELIMIT(condition, format...) ({ \ -+ int __ret_warn_on = !!(condition); \ -+ unlikely(__ret_warn_on); \ -+}) -+#endif -+ -+ - #ifndef WARN - #define WARN(condition, format...) ({ \ - int __ret_warn_on = !!(condition); \ diff --git a/target/linux/patches/2.6.32.65/cris-initramfs.patch b/target/linux/patches/2.6.32.65/cris-initramfs.patch deleted file mode 100644 index b709e705e..000000000 --- a/target/linux/patches/2.6.32.65/cris-initramfs.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff -Nur linux-3.18.12.orig/arch/cris/arch-v10/mm/init.c linux-3.18.12/arch/cris/arch-v10/mm/init.c ---- linux-3.18.12.orig/arch/cris/arch-v10/mm/init.c 2015-04-20 21:48:02.000000000 +0200 -+++ linux-3.18.12/arch/cris/arch-v10/mm/init.c 2015-05-16 01:46:37.000000000 +0200 -@@ -261,3 +261,7 @@ - { - flush_etrax_cacherange(0, 8192); - } -+ -+void free_initrd_mem(unsigned long start, unsigned long end) -+{ -+} -diff -Nur linux-3.18.12.orig/arch/cris/arch-v32/mm/init.c linux-3.18.12/arch/cris/arch-v32/mm/init.c ---- linux-3.18.12.orig/arch/cris/arch-v32/mm/init.c 2015-04-20 21:48:02.000000000 +0200 -+++ linux-3.18.12/arch/cris/arch-v32/mm/init.c 2015-05-16 01:46:54.000000000 +0200 -@@ -171,3 +171,7 @@ - - mem_map = contig_page_data.node_mem_map; - } -+ -+void free_initrd_mem(unsigned long start, unsigned long end) -+{ -+} diff --git a/target/linux/patches/2.6.32.67/cris-header.patch b/target/linux/patches/2.6.32.67/cris-header.patch new file mode 100644 index 000000000..1645d519c --- /dev/null +++ b/target/linux/patches/2.6.32.67/cris-header.patch @@ -0,0 +1,60 @@ +diff -Nur linux-2.6.32.65.orig/arch/cris/include/arch-v10/arch/Kbuild linux-2.6.32.65/arch/cris/include/arch-v10/arch/Kbuild +--- linux-2.6.32.65.orig/arch/cris/include/arch-v10/arch/Kbuild 2014-12-13 15:16:21.000000000 +0100 ++++ linux-2.6.32.65/arch/cris/include/arch-v10/arch/Kbuild 2015-05-20 10:00:19.000000000 +0200 +@@ -1,4 +1,5 @@ + header-y += user.h ++header-y += ptrace.h + header-y += svinto.h + header-y += sv_addr_ag.h + header-y += sv_addr.agh +diff -Nur linux-2.6.32.65.orig/arch/cris/include/arch-v32/arch/Kbuild linux-2.6.32.65/arch/cris/include/arch-v32/arch/Kbuild +--- linux-2.6.32.65.orig/arch/cris/include/arch-v32/arch/Kbuild 2014-12-13 15:16:21.000000000 +0100 ++++ linux-2.6.32.65/arch/cris/include/arch-v32/arch/Kbuild 2015-05-20 10:00:19.000000000 +0200 +@@ -1,2 +1,3 @@ + header-y += user.h ++header-y += ptrace.h + header-y += cryptocop.h +diff -Nur linux-2.6.32.65.orig/arch/cris/include/asm/Kbuild linux-2.6.32.65/arch/cris/include/asm/Kbuild +--- linux-2.6.32.65.orig/arch/cris/include/asm/Kbuild 2014-12-13 15:16:21.000000000 +0100 ++++ linux-2.6.32.65/arch/cris/include/asm/Kbuild 2015-05-20 10:39:33.000000000 +0200 +@@ -1,7 +1,7 @@ + include include/asm-generic/Kbuild.asm + +-header-y += arch-v10/ +-header-y += arch-v32/ ++header-y += ../arch-v10/arch/ ++header-y += ../arch-v32/arch/ + + header-y += ethernet.h + header-y += rtc.h +diff -Nur linux-2.6.32.65.orig/arch/cris/include/asm/pgtable.h linux-2.6.32.65/arch/cris/include/asm/pgtable.h +--- linux-2.6.32.65.orig/arch/cris/include/asm/pgtable.h 2014-12-13 15:16:21.000000000 +0100 ++++ linux-2.6.32.65/arch/cris/include/asm/pgtable.h 2015-05-20 11:56:24.000000000 +0200 +@@ -14,6 +14,9 @@ + #endif + #include + ++#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \ ++ remap_pfn_range(vma, vaddr, pfn, size, prot) ++ + /* + * The Linux memory management assumes a three-level page table setup. On + * CRIS, we use that, but "fold" the mid level into the top-level page +diff -Nur linux-2.6.32.65.orig/include/asm-generic/bug.h linux-2.6.32.65/include/asm-generic/bug.h +--- linux-2.6.32.65.orig/include/asm-generic/bug.h 2014-12-13 15:16:21.000000000 +0100 ++++ linux-2.6.32.65/include/asm-generic/bug.h 2015-05-20 13:39:24.000000000 +0200 +@@ -119,6 +119,14 @@ + }) + #endif + ++#ifndef WARN_RATELIMIT ++#define WARN_RATELIMIT(condition, format...) ({ \ ++ int __ret_warn_on = !!(condition); \ ++ unlikely(__ret_warn_on); \ ++}) ++#endif ++ ++ + #ifndef WARN + #define WARN(condition, format...) ({ \ + int __ret_warn_on = !!(condition); \ diff --git a/target/linux/patches/2.6.32.67/cris-initramfs.patch b/target/linux/patches/2.6.32.67/cris-initramfs.patch new file mode 100644 index 000000000..b709e705e --- /dev/null +++ b/target/linux/patches/2.6.32.67/cris-initramfs.patch @@ -0,0 +1,22 @@ +diff -Nur linux-3.18.12.orig/arch/cris/arch-v10/mm/init.c linux-3.18.12/arch/cris/arch-v10/mm/init.c +--- linux-3.18.12.orig/arch/cris/arch-v10/mm/init.c 2015-04-20 21:48:02.000000000 +0200 ++++ linux-3.18.12/arch/cris/arch-v10/mm/init.c 2015-05-16 01:46:37.000000000 +0200 +@@ -261,3 +261,7 @@ + { + flush_etrax_cacherange(0, 8192); + } ++ ++void free_initrd_mem(unsigned long start, unsigned long end) ++{ ++} +diff -Nur linux-3.18.12.orig/arch/cris/arch-v32/mm/init.c linux-3.18.12/arch/cris/arch-v32/mm/init.c +--- linux-3.18.12.orig/arch/cris/arch-v32/mm/init.c 2015-04-20 21:48:02.000000000 +0200 ++++ linux-3.18.12/arch/cris/arch-v32/mm/init.c 2015-05-16 01:46:54.000000000 +0200 +@@ -171,3 +171,7 @@ + + mem_map = contig_page_data.node_mem_map; + } ++ ++void free_initrd_mem(unsigned long start, unsigned long end) ++{ ++} diff --git a/target/linux/patches/3.10.75/bsd-compatibility.patch b/target/linux/patches/3.10.75/bsd-compatibility.patch deleted file mode 100644 index b954b658f..000000000 --- a/target/linux/patches/3.10.75/bsd-compatibility.patch +++ /dev/null @@ -1,2538 +0,0 @@ -diff -Nur linux-3.11.5.orig/scripts/Makefile.lib linux-3.11.5/scripts/Makefile.lib ---- linux-3.11.5.orig/scripts/Makefile.lib 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/Makefile.lib 2013-10-16 18:09:31.000000000 +0200 -@@ -281,7 +281,12 @@ - size_append = printf $(shell \ - dec_size=0; \ - for F in $1; do \ -- fsize=$$(stat -c "%s" $$F); \ -+ if stat -qs .>/dev/null 2>&1; then \ -+ statcmd='stat -f %z'; \ -+ else \ -+ statcmd='stat -c %s'; \ -+ fi; \ -+ fsize=$$($$statcmd $$F); \ - dec_size=$$(expr $$dec_size + $$fsize); \ - done; \ - printf "%08x\n" $$dec_size | \ -diff -Nur linux-3.11.5.orig/scripts/mod/mk_elfconfig.c linux-3.11.5/scripts/mod/mk_elfconfig.c ---- linux-3.11.5.orig/scripts/mod/mk_elfconfig.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/mk_elfconfig.c 2013-10-16 18:09:31.000000000 +0200 -@@ -1,7 +1,18 @@ - #include - #include - #include --#include -+ -+#define EI_NIDENT (16) -+#define ELFMAG "\177ELF" -+ -+#define SELFMAG 4 -+#define EI_CLASS 4 -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ - - int - main(int argc, char **argv) -diff -Nur linux-3.11.5.orig/scripts/mod/modpost.h linux-3.11.5/scripts/mod/modpost.h ---- linux-3.11.5.orig/scripts/mod/modpost.h 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/modpost.h 2013-10-16 18:09:31.000000000 +0200 -@@ -7,7 +7,2453 @@ - #include - #include - #include --#include -+ -+ -+/* This file defines standard ELF types, structures, and macros. -+ Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. -+ This file is part of the GNU C Library. -+ -+ The GNU C Library is free software; you can redistribute it and/or -+ modify it under the terms of the GNU Lesser General Public -+ License as published by the Free Software Foundation; either -+ version 2.1 of the License, or (at your option) any later version. -+ -+ The GNU C Library is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ Lesser General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public -+ License along with the GNU C Library; if not, write to the Free -+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ 02111-1307 USA. */ -+ -+#ifndef _ELF_H -+#define _ELF_H 1 -+ -+__BEGIN_DECLS -+ -+/* Standard ELF types. */ -+ -+#include -+ -+/* Type for a 16-bit quantity. */ -+typedef uint16_t Elf32_Half; -+typedef uint16_t Elf64_Half; -+ -+/* Types for signed and unsigned 32-bit quantities. */ -+typedef uint32_t Elf32_Word; -+typedef int32_t Elf32_Sword; -+typedef uint32_t Elf64_Word; -+typedef int32_t Elf64_Sword; -+ -+/* Types for signed and unsigned 64-bit quantities. */ -+typedef uint64_t Elf32_Xword; -+typedef int64_t Elf32_Sxword; -+typedef uint64_t Elf64_Xword; -+typedef int64_t Elf64_Sxword; -+ -+/* Type of addresses. */ -+typedef uint32_t Elf32_Addr; -+typedef uint64_t Elf64_Addr; -+ -+/* Type of file offsets. */ -+typedef uint32_t Elf32_Off; -+typedef uint64_t Elf64_Off; -+ -+/* Type for section indices, which are 16-bit quantities. */ -+typedef uint16_t Elf32_Section; -+typedef uint16_t Elf64_Section; -+ -+/* Type for version symbol information. */ -+typedef Elf32_Half Elf32_Versym; -+typedef Elf64_Half Elf64_Versym; -+ -+ -+/* The ELF file header. This appears at the start of every ELF file. */ -+ -+#define EI_NIDENT (16) -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf32_Half e_type; /* Object file type */ -+ Elf32_Half e_machine; /* Architecture */ -+ Elf32_Word e_version; /* Object file version */ -+ Elf32_Addr e_entry; /* Entry point virtual address */ -+ Elf32_Off e_phoff; /* Program header table file offset */ -+ Elf32_Off e_shoff; /* Section header table file offset */ -+ Elf32_Word e_flags; /* Processor-specific flags */ -+ Elf32_Half e_ehsize; /* ELF header size in bytes */ -+ Elf32_Half e_phentsize; /* Program header table entry size */ -+ Elf32_Half e_phnum; /* Program header table entry count */ -+ Elf32_Half e_shentsize; /* Section header table entry size */ -+ Elf32_Half e_shnum; /* Section header table entry count */ -+ Elf32_Half e_shstrndx; /* Section header string table index */ -+} Elf32_Ehdr; -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf64_Half e_type; /* Object file type */ -+ Elf64_Half e_machine; /* Architecture */ -+ Elf64_Word e_version; /* Object file version */ -+ Elf64_Addr e_entry; /* Entry point virtual address */ -+ Elf64_Off e_phoff; /* Program header table file offset */ -+ Elf64_Off e_shoff; /* Section header table file offset */ -+ Elf64_Word e_flags; /* Processor-specific flags */ -+ Elf64_Half e_ehsize; /* ELF header size in bytes */ -+ Elf64_Half e_phentsize; /* Program header table entry size */ -+ Elf64_Half e_phnum; /* Program header table entry count */ -+ Elf64_Half e_shentsize; /* Section header table entry size */ -+ Elf64_Half e_shnum; /* Section header table entry count */ -+ Elf64_Half e_shstrndx; /* Section header string table index */ -+} Elf64_Ehdr; -+ -+/* Fields in the e_ident array. The EI_* macros are indices into the -+ array. The macros under each EI_* macro are the values the byte -+ may have. */ -+ -+#define EI_MAG0 0 /* File identification byte 0 index */ -+#define ELFMAG0 0x7f /* Magic number byte 0 */ -+ -+#define EI_MAG1 1 /* File identification byte 1 index */ -+#define ELFMAG1 'E' /* Magic number byte 1 */ -+ -+#define EI_MAG2 2 /* File identification byte 2 index */ -+#define ELFMAG2 'L' /* Magic number byte 2 */ -+ -+#define EI_MAG3 3 /* File identification byte 3 index */ -+#define ELFMAG3 'F' /* Magic number byte 3 */ -+ -+/* Conglomeration of the identification bytes, for easy testing as a word. */ -+#define ELFMAG "\177ELF" -+#define SELFMAG 4 -+ -+#define EI_CLASS 4 /* File class byte index */ -+#define ELFCLASSNONE 0 /* Invalid class */ -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+#define ELFCLASSNUM 3 -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATANONE 0 /* Invalid data encoding */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ -+#define ELFDATANUM 3 -+ -+#define EI_VERSION 6 /* File version byte index */ -+ /* Value must be EV_CURRENT */ -+ -+#define EI_OSABI 7 /* OS ABI identification */ -+#define ELFOSABI_NONE 0 /* UNIX System V ABI */ -+#define ELFOSABI_SYSV 0 /* Alias. */ -+#define ELFOSABI_HPUX 1 /* HP-UX */ -+#define ELFOSABI_NETBSD 2 /* NetBSD. */ -+#define ELFOSABI_LINUX 3 /* Linux. */ -+#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ -+#define ELFOSABI_AIX 7 /* IBM AIX. */ -+#define ELFOSABI_IRIX 8 /* SGI Irix. */ -+#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ -+#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ -+#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ -+#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ -+#define ELFOSABI_ARM 97 /* ARM */ -+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ -+ -+#define EI_ABIVERSION 8 /* ABI version */ -+ -+#define EI_PAD 9 /* Byte index of padding bytes */ -+ -+/* Legal values for e_type (object file type). */ -+ -+#define ET_NONE 0 /* No file type */ -+#define ET_REL 1 /* Relocatable file */ -+#define ET_EXEC 2 /* Executable file */ -+#define ET_DYN 3 /* Shared object file */ -+#define ET_CORE 4 /* Core file */ -+#define ET_NUM 5 /* Number of defined types */ -+#define ET_LOOS 0xfe00 /* OS-specific range start */ -+#define ET_HIOS 0xfeff /* OS-specific range end */ -+#define ET_LOPROC 0xff00 /* Processor-specific range start */ -+#define ET_HIPROC 0xffff /* Processor-specific range end */ -+ -+/* Legal values for e_machine (architecture). */ -+ -+#define EM_NONE 0 /* No machine */ -+#define EM_M32 1 /* AT&T WE 32100 */ -+#define EM_SPARC 2 /* SUN SPARC */ -+#define EM_386 3 /* Intel 80386 */ -+#define EM_68K 4 /* Motorola m68k family */ -+#define EM_88K 5 /* Motorola m88k family */ -+#define EM_860 7 /* Intel 80860 */ -+#define EM_MIPS 8 /* MIPS R3000 big-endian */ -+#define EM_S370 9 /* IBM System/370 */ -+#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ -+ -+#define EM_PARISC 15 /* HPPA */ -+#define EM_VPP500 17 /* Fujitsu VPP500 */ -+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ -+#define EM_960 19 /* Intel 80960 */ -+#define EM_PPC 20 /* PowerPC */ -+#define EM_PPC64 21 /* PowerPC 64-bit */ -+#define EM_S390 22 /* IBM S390 */ -+ -+#define EM_V800 36 /* NEC V800 series */ -+#define EM_FR20 37 /* Fujitsu FR20 */ -+#define EM_RH32 38 /* TRW RH-32 */ -+#define EM_RCE 39 /* Motorola RCE */ -+#define EM_ARM 40 /* ARM */ -+#define EM_FAKE_ALPHA 41 /* Digital Alpha */ -+#define EM_SH 42 /* Hitachi SH */ -+#define EM_SPARCV9 43 /* SPARC v9 64-bit */ -+#define EM_TRICORE 44 /* Siemens Tricore */ -+#define EM_ARC 45 /* Argonaut RISC Core */ -+#define EM_H8_300 46 /* Hitachi H8/300 */ -+#define EM_H8_300H 47 /* Hitachi H8/300H */ -+#define EM_H8S 48 /* Hitachi H8S */ -+#define EM_H8_500 49 /* Hitachi H8/500 */ -+#define EM_IA_64 50 /* Intel Merced */ -+#define EM_MIPS_X 51 /* Stanford MIPS-X */ -+#define EM_COLDFIRE 52 /* Motorola Coldfire */ -+#define EM_68HC12 53 /* Motorola M68HC12 */ -+#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -+#define EM_PCP 55 /* Siemens PCP */ -+#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -+#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -+#define EM_STARCORE 58 /* Motorola Start*Core processor */ -+#define EM_ME16 59 /* Toyota ME16 processor */ -+#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -+#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -+#define EM_X86_64 62 /* AMD x86-64 architecture */ -+#define EM_PDSP 63 /* Sony DSP Processor */ -+ -+#define EM_FX66 66 /* Siemens FX66 microcontroller */ -+#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -+#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -+#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -+#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -+#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -+#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -+#define EM_SVX 73 /* Silicon Graphics SVx */ -+#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ -+#define EM_VAX 75 /* Digital VAX */ -+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ -+#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ -+#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -+#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -+#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ -+#define EM_HUANY 81 /* Harvard University machine-independent object files */ -+#define EM_PRISM 82 /* SiTera Prism */ -+#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -+#define EM_FR30 84 /* Fujitsu FR30 */ -+#define EM_D10V 85 /* Mitsubishi D10V */ -+#define EM_D30V 86 /* Mitsubishi D30V */ -+#define EM_V850 87 /* NEC v850 */ -+#define EM_M32R 88 /* Mitsubishi M32R */ -+#define EM_MN10300 89 /* Matsushita MN10300 */ -+#define EM_MN10200 90 /* Matsushita MN10200 */ -+#define EM_PJ 91 /* picoJava */ -+#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ -+#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ -+#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ -+#define EM_NUM 95 -+ -+/* If it is necessary to assign new unofficial EM_* values, please -+ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the -+ chances of collision with official or non-GNU unofficial values. */ -+ -+#define EM_ALPHA 0x9026 -+ -+/* Legal values for e_version (version). */ -+ -+#define EV_NONE 0 /* Invalid ELF version */ -+#define EV_CURRENT 1 /* Current version */ -+#define EV_NUM 2 -+ -+/* Section header. */ -+ -+typedef struct -+{ -+ Elf32_Word sh_name; /* Section name (string tbl index) */ -+ Elf32_Word sh_type; /* Section type */ -+ Elf32_Word sh_flags; /* Section flags */ -+ Elf32_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf32_Off sh_offset; /* Section file offset */ -+ Elf32_Word sh_size; /* Section size in bytes */ -+ Elf32_Word sh_link; /* Link to another section */ -+ Elf32_Word sh_info; /* Additional section information */ -+ Elf32_Word sh_addralign; /* Section alignment */ -+ Elf32_Word sh_entsize; /* Entry size if section holds table */ -+} Elf32_Shdr; -+ -+typedef struct -+{ -+ Elf64_Word sh_name; /* Section name (string tbl index) */ -+ Elf64_Word sh_type; /* Section type */ -+ Elf64_Xword sh_flags; /* Section flags */ -+ Elf64_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf64_Off sh_offset; /* Section file offset */ -+ Elf64_Xword sh_size; /* Section size in bytes */ -+ Elf64_Word sh_link; /* Link to another section */ -+ Elf64_Word sh_info; /* Additional section information */ -+ Elf64_Xword sh_addralign; /* Section alignment */ -+ Elf64_Xword sh_entsize; /* Entry size if section holds table */ -+} Elf64_Shdr; -+ -+/* Special section indices. */ -+ -+#define SHN_UNDEF 0 /* Undefined section */ -+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ -+#define SHN_LOPROC 0xff00 /* Start of processor-specific */ -+#define SHN_HIPROC 0xff1f /* End of processor-specific */ -+#define SHN_LOOS 0xff20 /* Start of OS-specific */ -+#define SHN_HIOS 0xff3f /* End of OS-specific */ -+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ -+#define SHN_COMMON 0xfff2 /* Associated symbol is common */ -+#define SHN_XINDEX 0xffff /* Index is in extra table. */ -+#define SHN_HIRESERVE 0xffff /* End of reserved indices */ -+ -+/* Legal values for sh_type (section type). */ -+ -+#define SHT_NULL 0 /* Section header table entry unused */ -+#define SHT_PROGBITS 1 /* Program data */ -+#define SHT_SYMTAB 2 /* Symbol table */ -+#define SHT_STRTAB 3 /* String table */ -+#define SHT_RELA 4 /* Relocation entries with addends */ -+#define SHT_HASH 5 /* Symbol hash table */ -+#define SHT_DYNAMIC 6 /* Dynamic linking information */ -+#define SHT_NOTE 7 /* Notes */ -+#define SHT_NOBITS 8 /* Program space with no data (bss) */ -+#define SHT_REL 9 /* Relocation entries, no addends */ -+#define SHT_SHLIB 10 /* Reserved */ -+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ -+#define SHT_INIT_ARRAY 14 /* Array of constructors */ -+#define SHT_FINI_ARRAY 15 /* Array of destructors */ -+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ -+#define SHT_GROUP 17 /* Section group */ -+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ -+#define SHT_NUM 19 /* Number of defined types. */ -+#define SHT_LOOS 0x60000000 /* Start OS-specific */ -+#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ -+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ -+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ -+#define SHT_SUNW_move 0x6ffffffa -+#define SHT_SUNW_COMDAT 0x6ffffffb -+#define SHT_SUNW_syminfo 0x6ffffffc -+#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ -+#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ -+#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ -+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ -+#define SHT_HIOS 0x6fffffff /* End OS-specific type */ -+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define SHT_LOUSER 0x80000000 /* Start of application-specific */ -+#define SHT_HIUSER 0x8fffffff /* End of application-specific */ -+ -+/* Legal values for sh_flags (section flags). */ -+ -+#define SHF_WRITE (1 << 0) /* Writable */ -+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ -+#define SHF_EXECINSTR (1 << 2) /* Executable */ -+#define SHF_MERGE (1 << 4) /* Might be merged */ -+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ -+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ -+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ -+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling -+ required */ -+#define SHF_GROUP (1 << 9) /* Section is member of a group. */ -+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ -+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ -+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Section group handling. */ -+#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ -+ -+/* Symbol table entry. */ -+ -+typedef struct -+{ -+ Elf32_Word st_name; /* Symbol name (string tbl index) */ -+ Elf32_Addr st_value; /* Symbol value */ -+ Elf32_Word st_size; /* Symbol size */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf32_Section st_shndx; /* Section index */ -+} Elf32_Sym; -+ -+typedef struct -+{ -+ Elf64_Word st_name; /* Symbol name (string tbl index) */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf64_Section st_shndx; /* Section index */ -+ Elf64_Addr st_value; /* Symbol value */ -+ Elf64_Xword st_size; /* Symbol size */ -+} Elf64_Sym; -+ -+/* The syminfo section if available contains additional information about -+ every dynamic symbol. */ -+ -+typedef struct -+{ -+ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf32_Half si_flags; /* Per symbol flags */ -+} Elf32_Syminfo; -+ -+typedef struct -+{ -+ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf64_Half si_flags; /* Per symbol flags */ -+} Elf64_Syminfo; -+ -+/* Possible values for si_boundto. */ -+#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ -+#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ -+#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ -+ -+/* Possible bitmasks for si_flags. */ -+#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ -+#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ -+#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ -+#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy -+ loaded */ -+/* Syminfo version values. */ -+#define SYMINFO_NONE 0 -+#define SYMINFO_CURRENT 1 -+#define SYMINFO_NUM 2 -+ -+ -+/* How to extract and insert information held in the st_info field. */ -+ -+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -+#define ELF32_ST_TYPE(val) ((val) & 0xf) -+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -+ -+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ -+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) -+ -+/* Legal values for ST_BIND subfield of st_info (symbol binding). */ -+ -+#define STB_LOCAL 0 /* Local symbol */ -+#define STB_GLOBAL 1 /* Global symbol */ -+#define STB_WEAK 2 /* Weak symbol */ -+#define STB_NUM 3 /* Number of defined types. */ -+#define STB_LOOS 10 /* Start of OS-specific */ -+#define STB_HIOS 12 /* End of OS-specific */ -+#define STB_LOPROC 13 /* Start of processor-specific */ -+#define STB_HIPROC 15 /* End of processor-specific */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_NOTYPE 0 /* Symbol type is unspecified */ -+#define STT_OBJECT 1 /* Symbol is a data object */ -+#define STT_FUNC 2 /* Symbol is a code object */ -+#define STT_SECTION 3 /* Symbol associated with a section */ -+#define STT_FILE 4 /* Symbol's name is file name */ -+#define STT_COMMON 5 /* Symbol is a common data object */ -+#define STT_TLS 6 /* Symbol is thread-local data object*/ -+#define STT_NUM 7 /* Number of defined types. */ -+#define STT_LOOS 10 /* Start of OS-specific */ -+#define STT_HIOS 12 /* End of OS-specific */ -+#define STT_LOPROC 13 /* Start of processor-specific */ -+#define STT_HIPROC 15 /* End of processor-specific */ -+ -+ -+/* Symbol table indices are found in the hash buckets and chain table -+ of a symbol hash table section. This special index value indicates -+ the end of a chain, meaning no further symbols are found in that bucket. */ -+ -+#define STN_UNDEF 0 /* End of a chain. */ -+ -+ -+/* How to extract and insert information held in the st_other field. */ -+ -+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) -+ -+/* For ELF64 the definitions are the same. */ -+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) -+ -+/* Symbol visibility specification encoded in the st_other field. */ -+#define STV_DEFAULT 0 /* Default symbol visibility rules */ -+#define STV_INTERNAL 1 /* Processor specific hidden class */ -+#define STV_HIDDEN 2 /* Sym unavailable in other modules */ -+#define STV_PROTECTED 3 /* Not preemptible, not exported */ -+ -+ -+/* Relocation table entry without addend (in section of type SHT_REL). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+} Elf32_Rel; -+ -+/* I have seen two different definitions of the Elf64_Rel and -+ Elf64_Rela structures, so we'll leave them out until Novell (or -+ whoever) gets their act together. */ -+/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+} Elf64_Rel; -+ -+/* Relocation table entry with addend (in section of type SHT_RELA). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+ Elf32_Sword r_addend; /* Addend */ -+} Elf32_Rela; -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+ Elf64_Sxword r_addend; /* Addend */ -+} Elf64_Rela; -+ -+/* How to extract and insert information held in the r_info field. */ -+ -+#define ELF32_R_SYM(val) ((val) >> 8) -+#define ELF32_R_TYPE(val) ((val) & 0xff) -+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) -+ -+#define ELF64_R_SYM(i) ((i) >> 32) -+#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) -+ -+/* Program segment header. */ -+ -+typedef struct -+{ -+ Elf32_Word p_type; /* Segment type */ -+ Elf32_Off p_offset; /* Segment file offset */ -+ Elf32_Addr p_vaddr; /* Segment virtual address */ -+ Elf32_Addr p_paddr; /* Segment physical address */ -+ Elf32_Word p_filesz; /* Segment size in file */ -+ Elf32_Word p_memsz; /* Segment size in memory */ -+ Elf32_Word p_flags; /* Segment flags */ -+ Elf32_Word p_align; /* Segment alignment */ -+} Elf32_Phdr; -+ -+typedef struct -+{ -+ Elf64_Word p_type; /* Segment type */ -+ Elf64_Word p_flags; /* Segment flags */ -+ Elf64_Off p_offset; /* Segment file offset */ -+ Elf64_Addr p_vaddr; /* Segment virtual address */ -+ Elf64_Addr p_paddr; /* Segment physical address */ -+ Elf64_Xword p_filesz; /* Segment size in file */ -+ Elf64_Xword p_memsz; /* Segment size in memory */ -+ Elf64_Xword p_align; /* Segment alignment */ -+} Elf64_Phdr; -+ -+/* Legal values for p_type (segment type). */ -+ -+#define PT_NULL 0 /* Program header table entry unused */ -+#define PT_LOAD 1 /* Loadable program segment */ -+#define PT_DYNAMIC 2 /* Dynamic linking information */ -+#define PT_INTERP 3 /* Program interpreter */ -+#define PT_NOTE 4 /* Auxiliary information */ -+#define PT_SHLIB 5 /* Reserved */ -+#define PT_PHDR 6 /* Entry for header table itself */ -+#define PT_TLS 7 /* Thread-local storage segment */ -+#define PT_NUM 8 /* Number of defined types */ -+#define PT_LOOS 0x60000000 /* Start of OS-specific */ -+#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ -+#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ -+#define PT_LOSUNW 0x6ffffffa -+#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ -+#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ -+#define PT_HISUNW 0x6fffffff -+#define PT_HIOS 0x6fffffff /* End of OS-specific */ -+#define PT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define PT_HIPROC 0x7fffffff /* End of processor-specific */ -+ -+/* Legal values for p_flags (segment flags). */ -+ -+#define PF_X (1 << 0) /* Segment is executable */ -+#define PF_W (1 << 1) /* Segment is writable */ -+#define PF_R (1 << 2) /* Segment is readable */ -+#define PF_MASKOS 0x0ff00000 /* OS-specific */ -+#define PF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Legal values for note segment descriptor types for core files. */ -+ -+#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ -+#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ -+#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ -+#define NT_PRXREG 4 /* Contains copy of prxregset struct */ -+#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ -+#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ -+#define NT_AUXV 6 /* Contains copy of auxv array */ -+#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ -+#define NT_ASRS 8 /* Contains copy of asrset struct */ -+#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ -+#define NT_PSINFO 13 /* Contains copy of psinfo struct */ -+#define NT_PRCRED 14 /* Contains copy of prcred struct */ -+#define NT_UTSNAME 15 /* Contains copy of utsname struct */ -+#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ -+#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ -+#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ -+ -+/* Legal values for the note segment descriptor types for object files. */ -+ -+#define NT_VERSION 1 /* Contains a version string. */ -+ -+ -+/* Dynamic section entry. */ -+ -+typedef struct -+{ -+ Elf32_Sword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf32_Word d_val; /* Integer value */ -+ Elf32_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf32_Dyn; -+ -+typedef struct -+{ -+ Elf64_Sxword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf64_Xword d_val; /* Integer value */ -+ Elf64_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf64_Dyn; -+ -+/* Legal values for d_tag (dynamic entry type). */ -+ -+#define DT_NULL 0 /* Marks end of dynamic section */ -+#define DT_NEEDED 1 /* Name of needed library */ -+#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ -+#define DT_PLTGOT 3 /* Processor defined value */ -+#define DT_HASH 4 /* Address of symbol hash table */ -+#define DT_STRTAB 5 /* Address of string table */ -+#define DT_SYMTAB 6 /* Address of symbol table */ -+#define DT_RELA 7 /* Address of Rela relocs */ -+#define DT_RELASZ 8 /* Total size of Rela relocs */ -+#define DT_RELAENT 9 /* Size of one Rela reloc */ -+#define DT_STRSZ 10 /* Size of string table */ -+#define DT_SYMENT 11 /* Size of one symbol table entry */ -+#define DT_INIT 12 /* Address of init function */ -+#define DT_FINI 13 /* Address of termination function */ -+#define DT_SONAME 14 /* Name of shared object */ -+#define DT_RPATH 15 /* Library search path (deprecated) */ -+#define DT_SYMBOLIC 16 /* Start symbol search here */ -+#define DT_REL 17 /* Address of Rel relocs */ -+#define DT_RELSZ 18 /* Total size of Rel relocs */ -+#define DT_RELENT 19 /* Size of one Rel reloc */ -+#define DT_PLTREL 20 /* Type of reloc in PLT */ -+#define DT_DEBUG 21 /* For debugging; unspecified */ -+#define DT_TEXTREL 22 /* Reloc might modify .text */ -+#define DT_JMPREL 23 /* Address of PLT relocs */ -+#define DT_BIND_NOW 24 /* Process relocations of object */ -+#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ -+#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ -+#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ -+#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ -+#define DT_RUNPATH 29 /* Library search path */ -+#define DT_FLAGS 30 /* Flags for the object being loaded */ -+#define DT_ENCODING 32 /* Start of encoded range */ -+#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ -+#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ -+#define DT_NUM 34 /* Number used */ -+#define DT_LOOS 0x6000000d /* Start of OS-specific */ -+#define DT_HIOS 0x6ffff000 /* End of OS-specific */ -+#define DT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define DT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ -+ -+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the -+ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's -+ approach. */ -+#define DT_VALRNGLO 0x6ffffd00 -+#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ -+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ -+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ -+#define DT_CHECKSUM 0x6ffffdf8 -+#define DT_PLTPADSZ 0x6ffffdf9 -+#define DT_MOVEENT 0x6ffffdfa -+#define DT_MOVESZ 0x6ffffdfb -+#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ -+#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting -+ the following DT_* entry. */ -+#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ -+#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ -+#define DT_VALRNGHI 0x6ffffdff -+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ -+#define DT_VALNUM 12 -+ -+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the -+ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. -+ -+ If any adjustment is made to the ELF object after it has been -+ built these entries will need to be adjusted. */ -+#define DT_ADDRRNGLO 0x6ffffe00 -+#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ -+#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ -+#define DT_CONFIG 0x6ffffefa /* Configuration information. */ -+#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ -+#define DT_AUDIT 0x6ffffefc /* Object auditing. */ -+#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ -+#define DT_MOVETAB 0x6ffffefe /* Move table. */ -+#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ -+#define DT_ADDRRNGHI 0x6ffffeff -+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ -+#define DT_ADDRNUM 10 -+ -+/* The versioning entry types. The next are defined as part of the -+ GNU extension. */ -+#define DT_VERSYM 0x6ffffff0 -+ -+#define DT_RELACOUNT 0x6ffffff9 -+#define DT_RELCOUNT 0x6ffffffa -+ -+/* These were chosen by Sun. */ -+#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ -+#define DT_VERDEF 0x6ffffffc /* Address of version definition -+ table */ -+#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ -+#define DT_VERNEED 0x6ffffffe /* Address of table with needed -+ versions */ -+#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ -+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ -+#define DT_VERSIONTAGNUM 16 -+ -+/* Sun added these machine-independent extensions in the "processor-specific" -+ range. Be compatible. */ -+#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ -+#define DT_FILTER 0x7fffffff /* Shared object to get values from */ -+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -+#define DT_EXTRANUM 3 -+ -+/* Values of `d_un.d_val' in the DT_FLAGS entry. */ -+#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ -+#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ -+#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ -+#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ -+#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ -+ -+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 -+ entry in the dynamic section. */ -+#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ -+#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ -+#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ -+#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ -+#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ -+#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ -+#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ -+#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ -+#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ -+#define DF_1_TRANS 0x00000200 -+#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ -+#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ -+#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ -+#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ -+#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ -+#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ -+#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ -+ -+/* Flags for the feature selection in DT_FEATURE_1. */ -+#define DTF_1_PARINIT 0x00000001 -+#define DTF_1_CONFEXP 0x00000002 -+ -+/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ -+#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ -+#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not -+ generally available. */ -+ -+/* Version definition sections. */ -+ -+typedef struct -+{ -+ Elf32_Half vd_version; /* Version revision */ -+ Elf32_Half vd_flags; /* Version information */ -+ Elf32_Half vd_ndx; /* Version Index */ -+ Elf32_Half vd_cnt; /* Number of associated aux entries */ -+ Elf32_Word vd_hash; /* Version name hash value */ -+ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf32_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf32_Verdef; -+ -+typedef struct -+{ -+ Elf64_Half vd_version; /* Version revision */ -+ Elf64_Half vd_flags; /* Version information */ -+ Elf64_Half vd_ndx; /* Version Index */ -+ Elf64_Half vd_cnt; /* Number of associated aux entries */ -+ Elf64_Word vd_hash; /* Version name hash value */ -+ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf64_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf64_Verdef; -+ -+ -+/* Legal values for vd_version (version revision). */ -+#define VER_DEF_NONE 0 /* No version */ -+#define VER_DEF_CURRENT 1 /* Current version */ -+#define VER_DEF_NUM 2 /* Given version number */ -+ -+/* Legal values for vd_flags (version information flags). */ -+#define VER_FLG_BASE 0x1 /* Version definition of file itself */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+/* Versym symbol index values. */ -+#define VER_NDX_LOCAL 0 /* Symbol is local. */ -+#define VER_NDX_GLOBAL 1 /* Symbol is global. */ -+#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ -+#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ -+ -+/* Auxialiary version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vda_name; /* Version or dependency names */ -+ Elf32_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf32_Verdaux; -+ -+typedef struct -+{ -+ Elf64_Word vda_name; /* Version or dependency names */ -+ Elf64_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf64_Verdaux; -+ -+ -+/* Version dependency section. */ -+ -+typedef struct -+{ -+ Elf32_Half vn_version; /* Version of structure */ -+ Elf32_Half vn_cnt; /* Number of associated aux entries */ -+ Elf32_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf32_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf32_Verneed; -+ -+typedef struct -+{ -+ Elf64_Half vn_version; /* Version of structure */ -+ Elf64_Half vn_cnt; /* Number of associated aux entries */ -+ Elf64_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf64_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf64_Verneed; -+ -+ -+/* Legal values for vn_version (version revision). */ -+#define VER_NEED_NONE 0 /* No version */ -+#define VER_NEED_CURRENT 1 /* Current version */ -+#define VER_NEED_NUM 2 /* Given version number */ -+ -+/* Auxiliary needed version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vna_hash; /* Hash value of dependency name */ -+ Elf32_Half vna_flags; /* Dependency specific information */ -+ Elf32_Half vna_other; /* Unused */ -+ Elf32_Word vna_name; /* Dependency name string offset */ -+ Elf32_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf32_Vernaux; -+ -+typedef struct -+{ -+ Elf64_Word vna_hash; /* Hash value of dependency name */ -+ Elf64_Half vna_flags; /* Dependency specific information */ -+ Elf64_Half vna_other; /* Unused */ -+ Elf64_Word vna_name; /* Dependency name string offset */ -+ Elf64_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf64_Vernaux; -+ -+ -+/* Legal values for vna_flags. */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+ -+/* Auxiliary vector. */ -+ -+/* This vector is normally only used by the program interpreter. The -+ usual definition in an ABI supplement uses the name auxv_t. The -+ vector is not usually defined in a standard file, but it -+ can't hurt. We rename it to avoid conflicts. The sizes of these -+ types are an arrangement between the exec server and the program -+ interpreter, so we don't fully specify them here. */ -+ -+typedef struct -+{ -+ int a_type; /* Entry type */ -+ union -+ { -+ long int a_val; /* Integer value */ -+ void *a_ptr; /* Pointer value */ -+ void (*a_fcn) (void); /* Function pointer value */ -+ } a_un; -+} Elf32_auxv_t; -+ -+typedef struct -+{ -+ long int a_type; /* Entry type */ -+ union -+ { -+ long int a_val; /* Integer value */ -+ void *a_ptr; /* Pointer value */ -+ void (*a_fcn) (void); /* Function pointer value */ -+ } a_un; -+} Elf64_auxv_t; -+ -+/* Legal values for a_type (entry type). */ -+ -+#define AT_NULL 0 /* End of vector */ -+#define AT_IGNORE 1 /* Entry should be ignored */ -+#define AT_EXECFD 2 /* File descriptor of program */ -+#define AT_PHDR 3 /* Program headers for program */ -+#define AT_PHENT 4 /* Size of program header entry */ -+#define AT_PHNUM 5 /* Number of program headers */ -+#define AT_PAGESZ 6 /* System page size */ -+#define AT_BASE 7 /* Base address of interpreter */ -+#define AT_FLAGS 8 /* Flags */ -+#define AT_ENTRY 9 /* Entry point of program */ -+#define AT_NOTELF 10 /* Program is not ELF */ -+#define AT_UID 11 /* Real uid */ -+#define AT_EUID 12 /* Effective uid */ -+#define AT_GID 13 /* Real gid */ -+#define AT_EGID 14 /* Effective gid */ -+#define AT_CLKTCK 17 /* Frequency of times() */ -+ -+/* Some more special a_type values describing the hardware. */ -+#define AT_PLATFORM 15 /* String identifying platform. */ -+#define AT_HWCAP 16 /* Machine dependent hints about -+ processor capabilities. */ -+ -+/* This entry gives some information about the FPU initialization -+ performed by the kernel. */ -+#define AT_FPUCW 18 /* Used FPU control word. */ -+ -+/* Cache block sizes. */ -+#define AT_DCACHEBSIZE 19 /* Data cache block size. */ -+#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ -+#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ -+ -+/* A special ignored value for PPC, used by the kernel to control the -+ interpretation of the AUXV. Must be > 16. */ -+#define AT_IGNOREPPC 22 /* Entry should be ignored. */ -+ -+#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ -+ -+/* Pointer to the global system page used for system calls and other -+ nice things. */ -+#define AT_SYSINFO 32 -+#define AT_SYSINFO_EHDR 33 -+ -+ -+/* Note section contents. Each entry in the note section begins with -+ a header of a fixed form. */ -+ -+typedef struct -+{ -+ Elf32_Word n_namesz; /* Length of the note's name. */ -+ Elf32_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf32_Word n_type; /* Type of the note. */ -+} Elf32_Nhdr; -+ -+typedef struct -+{ -+ Elf64_Word n_namesz; /* Length of the note's name. */ -+ Elf64_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf64_Word n_type; /* Type of the note. */ -+} Elf64_Nhdr; -+ -+/* Known names of notes. */ -+ -+/* Solaris entries in the note section have this name. */ -+#define ELF_NOTE_SOLARIS "SUNW Solaris" -+ -+/* Note entries for GNU systems have this name. */ -+#define ELF_NOTE_GNU "GNU" -+ -+ -+/* Defined types of notes for Solaris. */ -+ -+/* Value of descriptor (one word) is desired pagesize for the binary. */ -+#define ELF_NOTE_PAGESIZE_HINT 1 -+ -+ -+/* Defined note types for GNU systems. */ -+ -+/* ABI information. The descriptor consists of words: -+ word 0: OS descriptor -+ word 1: major version of the ABI -+ word 2: minor version of the ABI -+ word 3: subminor version of the ABI -+*/ -+#define ELF_NOTE_ABI 1 -+ -+/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI -+ note section entry. */ -+#define ELF_NOTE_OS_LINUX 0 -+#define ELF_NOTE_OS_GNU 1 -+#define ELF_NOTE_OS_SOLARIS2 2 -+#define ELF_NOTE_OS_FREEBSD 3 -+ -+ -+/* Move records. */ -+typedef struct -+{ -+ Elf32_Xword m_value; /* Symbol value. */ -+ Elf32_Word m_info; /* Size and index. */ -+ Elf32_Word m_poffset; /* Symbol offset. */ -+ Elf32_Half m_repeat; /* Repeat count. */ -+ Elf32_Half m_stride; /* Stride info. */ -+} Elf32_Move; -+ -+typedef struct -+{ -+ Elf64_Xword m_value; /* Symbol value. */ -+ Elf64_Xword m_info; /* Size and index. */ -+ Elf64_Xword m_poffset; /* Symbol offset. */ -+ Elf64_Half m_repeat; /* Repeat count. */ -+ Elf64_Half m_stride; /* Stride info. */ -+} Elf64_Move; -+ -+/* Macro to construct move records. */ -+#define ELF32_M_SYM(info) ((info) >> 8) -+#define ELF32_M_SIZE(info) ((unsigned char) (info)) -+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) -+ -+#define ELF64_M_SYM(info) ELF32_M_SYM (info) -+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) -+ -+ -+/* Motorola 68k specific definitions. */ -+ -+/* Values for Elf32_Ehdr.e_flags. */ -+#define EF_CPU32 0x00810000 -+ -+/* m68k relocs. */ -+ -+#define R_68K_NONE 0 /* No reloc */ -+#define R_68K_32 1 /* Direct 32 bit */ -+#define R_68K_16 2 /* Direct 16 bit */ -+#define R_68K_8 3 /* Direct 8 bit */ -+#define R_68K_PC32 4 /* PC relative 32 bit */ -+#define R_68K_PC16 5 /* PC relative 16 bit */ -+#define R_68K_PC8 6 /* PC relative 8 bit */ -+#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ -+#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ -+#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ -+#define R_68K_GOT32O 10 /* 32 bit GOT offset */ -+#define R_68K_GOT16O 11 /* 16 bit GOT offset */ -+#define R_68K_GOT8O 12 /* 8 bit GOT offset */ -+#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ -+#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ -+#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ -+#define R_68K_PLT32O 16 /* 32 bit PLT offset */ -+#define R_68K_PLT16O 17 /* 16 bit PLT offset */ -+#define R_68K_PLT8O 18 /* 8 bit PLT offset */ -+#define R_68K_COPY 19 /* Copy symbol at runtime */ -+#define R_68K_GLOB_DAT 20 /* Create GOT entry */ -+#define R_68K_JMP_SLOT 21 /* Create PLT entry */ -+#define R_68K_RELATIVE 22 /* Adjust by program base */ -+/* Keep this the last entry. */ -+#define R_68K_NUM 23 -+ -+/* Intel 80386 specific definitions. */ -+ -+/* i386 relocs. */ -+ -+#define R_386_NONE 0 /* No reloc */ -+#define R_386_32 1 /* Direct 32 bit */ -+#define R_386_PC32 2 /* PC relative 32 bit */ -+#define R_386_GOT32 3 /* 32 bit GOT entry */ -+#define R_386_PLT32 4 /* 32 bit PLT address */ -+#define R_386_COPY 5 /* Copy symbol at runtime */ -+#define R_386_GLOB_DAT 6 /* Create GOT entry */ -+#define R_386_JMP_SLOT 7 /* Create PLT entry */ -+#define R_386_RELATIVE 8 /* Adjust by program base */ -+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ -+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ -+#define R_386_32PLT 11 -+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ -+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS -+ block offset */ -+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block -+ offset */ -+#define R_386_TLS_LE 17 /* Offset relative to static TLS -+ block */ -+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of -+ general dynamic thread local data */ -+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of -+ local dynamic thread local data -+ in LE code */ -+#define R_386_16 20 -+#define R_386_PC16 21 -+#define R_386_8 22 -+#define R_386_PC8 23 -+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic -+ thread local data */ -+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ -+#define R_386_TLS_GD_CALL 26 /* Relocation for call to -+ __tls_get_addr() */ -+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ -+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic -+ thread local data in LE code */ -+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ -+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to -+ __tls_get_addr() in LDM code */ -+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ -+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ -+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS -+ block offset */ -+#define R_386_TLS_LE_32 34 /* Negated offset relative to static -+ TLS block */ -+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ -+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ -+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ -+/* Keep this the last entry. */ -+#define R_386_NUM 38 -+ -+/* SUN SPARC specific definitions. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_REGISTER 13 /* Global register reserved to app. */ -+ -+/* Values for Elf64_Ehdr.e_flags. */ -+ -+#define EF_SPARCV9_MM 3 -+#define EF_SPARCV9_TSO 0 -+#define EF_SPARCV9_PSO 1 -+#define EF_SPARCV9_RMO 2 -+#define EF_SPARC_LEDATA 0x800000 /* little endian data */ -+#define EF_SPARC_EXT_MASK 0xFFFF00 -+#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ -+#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ -+#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ -+#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ -+ -+/* SPARC relocs. */ -+ -+#define R_SPARC_NONE 0 /* No reloc */ -+#define R_SPARC_8 1 /* Direct 8 bit */ -+#define R_SPARC_16 2 /* Direct 16 bit */ -+#define R_SPARC_32 3 /* Direct 32 bit */ -+#define R_SPARC_DISP8 4 /* PC relative 8 bit */ -+#define R_SPARC_DISP16 5 /* PC relative 16 bit */ -+#define R_SPARC_DISP32 6 /* PC relative 32 bit */ -+#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ -+#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ -+#define R_SPARC_HI22 9 /* High 22 bit */ -+#define R_SPARC_22 10 /* Direct 22 bit */ -+#define R_SPARC_13 11 /* Direct 13 bit */ -+#define R_SPARC_LO10 12 /* Truncated 10 bit */ -+#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ -+#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ -+#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ -+#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ -+#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ -+#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ -+#define R_SPARC_COPY 19 /* Copy symbol at runtime */ -+#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ -+#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ -+#define R_SPARC_RELATIVE 22 /* Adjust by program base */ -+#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ -+ -+/* Additional Sparc64 relocs. */ -+ -+#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ -+#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ -+#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ -+#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ -+#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ -+#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ -+#define R_SPARC_10 30 /* Direct 10 bit */ -+#define R_SPARC_11 31 /* Direct 11 bit */ -+#define R_SPARC_64 32 /* Direct 64 bit */ -+#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ -+#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ -+#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ -+#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ -+#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ -+#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ -+#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ -+#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ -+#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ -+#define R_SPARC_7 43 /* Direct 7 bit */ -+#define R_SPARC_5 44 /* Direct 5 bit */ -+#define R_SPARC_6 45 /* Direct 6 bit */ -+#define R_SPARC_DISP64 46 /* PC relative 64 bit */ -+#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ -+#define R_SPARC_HIX22 48 /* High 22 bit complemented */ -+#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ -+#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ -+#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ -+#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ -+#define R_SPARC_REGISTER 53 /* Global register usage */ -+#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ -+#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ -+#define R_SPARC_TLS_GD_HI22 56 -+#define R_SPARC_TLS_GD_LO10 57 -+#define R_SPARC_TLS_GD_ADD 58 -+#define R_SPARC_TLS_GD_CALL 59 -+#define R_SPARC_TLS_LDM_HI22 60 -+#define R_SPARC_TLS_LDM_LO10 61 -+#define R_SPARC_TLS_LDM_ADD 62 -+#define R_SPARC_TLS_LDM_CALL 63 -+#define R_SPARC_TLS_LDO_HIX22 64 -+#define R_SPARC_TLS_LDO_LOX10 65 -+#define R_SPARC_TLS_LDO_ADD 66 -+#define R_SPARC_TLS_IE_HI22 67 -+#define R_SPARC_TLS_IE_LO10 68 -+#define R_SPARC_TLS_IE_LD 69 -+#define R_SPARC_TLS_IE_LDX 70 -+#define R_SPARC_TLS_IE_ADD 71 -+#define R_SPARC_TLS_LE_HIX22 72 -+#define R_SPARC_TLS_LE_LOX10 73 -+#define R_SPARC_TLS_DTPMOD32 74 -+#define R_SPARC_TLS_DTPMOD64 75 -+#define R_SPARC_TLS_DTPOFF32 76 -+#define R_SPARC_TLS_DTPOFF64 77 -+#define R_SPARC_TLS_TPOFF32 78 -+#define R_SPARC_TLS_TPOFF64 79 -+/* Keep this the last entry. */ -+#define R_SPARC_NUM 80 -+ -+/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ -+ -+#define DT_SPARC_REGISTER 0x70000001 -+#define DT_SPARC_NUM 2 -+ -+/* Bits present in AT_HWCAP, primarily for Sparc32. */ -+ -+#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ -+#define HWCAP_SPARC_STBAR 2 -+#define HWCAP_SPARC_SWAP 4 -+#define HWCAP_SPARC_MULDIV 8 -+#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ -+#define HWCAP_SPARC_ULTRA3 32 -+ -+/* MIPS R3000 specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ -+#define EF_MIPS_PIC 2 /* Contains PIC code */ -+#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ -+#define EF_MIPS_XGOT 8 -+#define EF_MIPS_64BIT_WHIRL 16 -+#define EF_MIPS_ABI2 32 -+#define EF_MIPS_ABI_ON32 64 -+#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ -+ -+/* Legal values for MIPS architecture level. */ -+ -+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* The following are non-official names and should not be used. */ -+ -+#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* Special section indices. */ -+ -+#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ -+#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ -+#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ -+#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ -+#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ -+#define SHT_MIPS_MSYM 0x70000001 -+#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ -+#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ -+#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ -+#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ -+#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ -+#define SHT_MIPS_PACKAGE 0x70000007 -+#define SHT_MIPS_PACKSYM 0x70000008 -+#define SHT_MIPS_RELD 0x70000009 -+#define SHT_MIPS_IFACE 0x7000000b -+#define SHT_MIPS_CONTENT 0x7000000c -+#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ -+#define SHT_MIPS_SHDR 0x70000010 -+#define SHT_MIPS_FDESC 0x70000011 -+#define SHT_MIPS_EXTSYM 0x70000012 -+#define SHT_MIPS_DENSE 0x70000013 -+#define SHT_MIPS_PDESC 0x70000014 -+#define SHT_MIPS_LOCSYM 0x70000015 -+#define SHT_MIPS_AUXSYM 0x70000016 -+#define SHT_MIPS_OPTSYM 0x70000017 -+#define SHT_MIPS_LOCSTR 0x70000018 -+#define SHT_MIPS_LINE 0x70000019 -+#define SHT_MIPS_RFDESC 0x7000001a -+#define SHT_MIPS_DELTASYM 0x7000001b -+#define SHT_MIPS_DELTAINST 0x7000001c -+#define SHT_MIPS_DELTACLASS 0x7000001d -+#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ -+#define SHT_MIPS_DELTADECL 0x7000001f -+#define SHT_MIPS_SYMBOL_LIB 0x70000020 -+#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ -+#define SHT_MIPS_TRANSLATE 0x70000022 -+#define SHT_MIPS_PIXIE 0x70000023 -+#define SHT_MIPS_XLATE 0x70000024 -+#define SHT_MIPS_XLATE_DEBUG 0x70000025 -+#define SHT_MIPS_WHIRL 0x70000026 -+#define SHT_MIPS_EH_REGION 0x70000027 -+#define SHT_MIPS_XLATE_OLD 0x70000028 -+#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ -+#define SHF_MIPS_MERGE 0x20000000 -+#define SHF_MIPS_ADDR 0x40000000 -+#define SHF_MIPS_STRINGS 0x80000000 -+#define SHF_MIPS_NOSTRIP 0x08000000 -+#define SHF_MIPS_LOCAL 0x04000000 -+#define SHF_MIPS_NAMES 0x02000000 -+#define SHF_MIPS_NODUPE 0x01000000 -+ -+ -+/* Symbol tables. */ -+ -+/* MIPS specific values for `st_other'. */ -+#define STO_MIPS_DEFAULT 0x0 -+#define STO_MIPS_INTERNAL 0x1 -+#define STO_MIPS_HIDDEN 0x2 -+#define STO_MIPS_PROTECTED 0x3 -+#define STO_MIPS_SC_ALIGN_UNUSED 0xff -+ -+/* MIPS specific values for `st_info'. */ -+#define STB_MIPS_SPLIT_COMMON 13 -+ -+/* Entries found in sections of type SHT_MIPS_GPTAB. */ -+ -+typedef union -+{ -+ struct -+ { -+ Elf32_Word gt_current_g_value; /* -G value used for compilation */ -+ Elf32_Word gt_unused; /* Not used */ -+ } gt_header; /* First entry in section */ -+ struct -+ { -+ Elf32_Word gt_g_value; /* If this value were used for -G */ -+ Elf32_Word gt_bytes; /* This many bytes would be used */ -+ } gt_entry; /* Subsequent entries in section */ -+} Elf32_gptab; -+ -+/* Entry found in sections of type SHT_MIPS_REGINFO. */ -+ -+typedef struct -+{ -+ Elf32_Word ri_gprmask; /* General registers used */ -+ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ -+ Elf32_Sword ri_gp_value; /* $gp register value */ -+} Elf32_RegInfo; -+ -+/* Entries found in sections of type SHT_MIPS_OPTIONS. */ -+ -+typedef struct -+{ -+ unsigned char kind; /* Determines interpretation of the -+ variable part of descriptor. */ -+ unsigned char size; /* Size of descriptor, including header. */ -+ Elf32_Section section; /* Section header index of section affected, -+ 0 for global options. */ -+ Elf32_Word info; /* Kind-specific information. */ -+} Elf_Options; -+ -+/* Values for `kind' field in Elf_Options. */ -+ -+#define ODK_NULL 0 /* Undefined. */ -+#define ODK_REGINFO 1 /* Register usage information. */ -+#define ODK_EXCEPTIONS 2 /* Exception processing options. */ -+#define ODK_PAD 3 /* Section padding options. */ -+#define ODK_HWPATCH 4 /* Hardware workarounds performed */ -+#define ODK_FILL 5 /* record the fill value used by the linker. */ -+#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ -+#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ -+#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ -+ -+/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ -+ -+#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ -+#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ -+#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ -+#define OEX_SMM 0x20000 /* Force sequential memory mode? */ -+#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ -+#define OEX_PRECISEFP OEX_FPDBUG -+#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ -+ -+#define OEX_FPU_INVAL 0x10 -+#define OEX_FPU_DIV0 0x08 -+#define OEX_FPU_OFLO 0x04 -+#define OEX_FPU_UFLO 0x02 -+#define OEX_FPU_INEX 0x01 -+ -+/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ -+ -+#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ -+#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ -+#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ -+#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ -+ -+#define OPAD_PREFIX 0x1 -+#define OPAD_POSTFIX 0x2 -+#define OPAD_SYMBOL 0x4 -+ -+/* Entry found in `.options' section. */ -+ -+typedef struct -+{ -+ Elf32_Word hwp_flags1; /* Extra flags. */ -+ Elf32_Word hwp_flags2; /* Extra flags. */ -+} Elf_Options_Hw; -+ -+/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ -+ -+#define OHWA0_R4KEOP_CHECKED 0x00000001 -+#define OHWA1_R4KEOP_CLEAN 0x00000002 -+ -+/* MIPS relocs. */ -+ -+#define R_MIPS_NONE 0 /* No reloc */ -+#define R_MIPS_16 1 /* Direct 16 bit */ -+#define R_MIPS_32 2 /* Direct 32 bit */ -+#define R_MIPS_REL32 3 /* PC relative 32 bit */ -+#define R_MIPS_26 4 /* Direct 26 bit shifted */ -+#define R_MIPS_HI16 5 /* High 16 bit */ -+#define R_MIPS_LO16 6 /* Low 16 bit */ -+#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ -+#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ -+#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ -+#define R_MIPS_PC16 10 /* PC relative 16 bit */ -+#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ -+#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ -+ -+#define R_MIPS_SHIFT5 16 -+#define R_MIPS_SHIFT6 17 -+#define R_MIPS_64 18 -+#define R_MIPS_GOT_DISP 19 -+#define R_MIPS_GOT_PAGE 20 -+#define R_MIPS_GOT_OFST 21 -+#define R_MIPS_GOT_HI16 22 -+#define R_MIPS_GOT_LO16 23 -+#define R_MIPS_SUB 24 -+#define R_MIPS_INSERT_A 25 -+#define R_MIPS_INSERT_B 26 -+#define R_MIPS_DELETE 27 -+#define R_MIPS_HIGHER 28 -+#define R_MIPS_HIGHEST 29 -+#define R_MIPS_CALL_HI16 30 -+#define R_MIPS_CALL_LO16 31 -+#define R_MIPS_SCN_DISP 32 -+#define R_MIPS_REL16 33 -+#define R_MIPS_ADD_IMMEDIATE 34 -+#define R_MIPS_PJUMP 35 -+#define R_MIPS_RELGOT 36 -+#define R_MIPS_JALR 37 -+/* Keep this the last entry. */ -+#define R_MIPS_NUM 38 -+ -+/* Legal values for p_type field of Elf32_Phdr. */ -+ -+#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ -+#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ -+#define PT_MIPS_OPTIONS 0x70000002 -+ -+/* Special program header types. */ -+ -+#define PF_MIPS_LOCAL 0x10000000 -+ -+/* Legal values for d_tag field of Elf32_Dyn. */ -+ -+#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ -+#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ -+#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ -+#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ -+#define DT_MIPS_FLAGS 0x70000005 /* Flags */ -+#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ -+#define DT_MIPS_MSYM 0x70000007 -+#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ -+#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ -+#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ -+#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ -+#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ -+#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ -+#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ -+#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ -+#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ -+#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ -+#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ -+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in -+ DT_MIPS_DELTA_CLASS. */ -+#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ -+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in -+ DT_MIPS_DELTA_INSTANCE. */ -+#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ -+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in -+ DT_MIPS_DELTA_RELOC. */ -+#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta -+ relocations refer to. */ -+#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in -+ DT_MIPS_DELTA_SYM. */ -+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the -+ class declaration. */ -+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in -+ DT_MIPS_DELTA_CLASSSYM. */ -+#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ -+#define DT_MIPS_PIXIE_INIT 0x70000023 -+#define DT_MIPS_SYMBOL_LIB 0x70000024 -+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -+#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -+#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ -+#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ -+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -+#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ -+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve -+ function stored in GOT. */ -+#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added -+ by rld on dlopen() calls. */ -+#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ -+#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ -+#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ -+#define DT_MIPS_NUM 0x32 -+ -+/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ -+ -+#define RHF_NONE 0 /* No flags */ -+#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ -+#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ -+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ -+#define RHF_NO_MOVE (1 << 3) -+#define RHF_SGI_ONLY (1 << 4) -+#define RHF_GUARANTEE_INIT (1 << 5) -+#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -+#define RHF_GUARANTEE_START_INIT (1 << 7) -+#define RHF_PIXIE (1 << 8) -+#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -+#define RHF_REQUICKSTART (1 << 10) -+#define RHF_REQUICKSTARTED (1 << 11) -+#define RHF_CORD (1 << 12) -+#define RHF_NO_UNRES_UNDEF (1 << 13) -+#define RHF_RLD_ORDER_SAFE (1 << 14) -+ -+/* Entries found in sections of type SHT_MIPS_LIBLIST. */ -+ -+typedef struct -+{ -+ Elf32_Word l_name; /* Name (string table index) */ -+ Elf32_Word l_time_stamp; /* Timestamp */ -+ Elf32_Word l_checksum; /* Checksum */ -+ Elf32_Word l_version; /* Interface version */ -+ Elf32_Word l_flags; /* Flags */ -+} Elf32_Lib; -+ -+typedef struct -+{ -+ Elf64_Word l_name; /* Name (string table index) */ -+ Elf64_Word l_time_stamp; /* Timestamp */ -+ Elf64_Word l_checksum; /* Checksum */ -+ Elf64_Word l_version; /* Interface version */ -+ Elf64_Word l_flags; /* Flags */ -+} Elf64_Lib; -+ -+ -+/* Legal values for l_flags. */ -+ -+#define LL_NONE 0 -+#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ -+#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ -+#define LL_REQUIRE_MINOR (1 << 2) -+#define LL_EXPORTS (1 << 3) -+#define LL_DELAY_LOAD (1 << 4) -+#define LL_DELTA (1 << 5) -+ -+/* Entries found in sections of type SHT_MIPS_CONFLICT. */ -+ -+typedef Elf32_Addr Elf32_Conflict; -+ -+ -+/* HPPA specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ -+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ -+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ -+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ -+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch -+ prediction. */ -+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ -+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ -+ -+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ -+ -+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ -+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ -+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ -+ -+/* Additional section indeces. */ -+ -+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared -+ symbols in ANSI C. */ -+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ -+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ -+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ -+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ -+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ -+ -+#define STT_HP_OPAQUE (STT_LOOS + 0x1) -+#define STT_HP_STUB (STT_LOOS + 0x2) -+ -+/* HPPA relocs. */ -+ -+#define R_PARISC_NONE 0 /* No reloc. */ -+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ -+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ -+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ -+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ -+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ -+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ -+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ -+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ -+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ -+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ -+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ -+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ -+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ -+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ -+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ -+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ -+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ -+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ -+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ -+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ -+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ -+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ -+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ -+#define R_PARISC_FPTR64 64 /* 64 bits function address. */ -+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ -+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ -+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ -+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ -+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ -+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ -+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ -+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ -+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ -+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ -+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ -+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ -+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ -+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ -+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ -+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LORESERVE 128 -+#define R_PARISC_COPY 128 /* Copy relocation. */ -+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ -+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ -+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ -+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ -+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ -+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ -+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ -+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ -+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_HIRESERVE 255 -+ -+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PT_HP_TLS (PT_LOOS + 0x0) -+#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -+#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -+#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -+#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -+#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -+#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -+#define PT_HP_PARALLEL (PT_LOOS + 0x10) -+#define PT_HP_FASTBIND (PT_LOOS + 0x11) -+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -+#define PT_HP_STACK (PT_LOOS + 0x14) -+ -+#define PT_PARISC_ARCHEXT 0x70000000 -+#define PT_PARISC_UNWIND 0x70000001 -+ -+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PF_PARISC_SBP 0x08000000 -+ -+#define PF_HP_PAGE_SIZE 0x00100000 -+#define PF_HP_FAR_SHARED 0x00200000 -+#define PF_HP_NEAR_SHARED 0x00400000 -+#define PF_HP_CODE 0x01000000 -+#define PF_HP_MODIFY 0x02000000 -+#define PF_HP_LAZYSWAP 0x04000000 -+#define PF_HP_SBP 0x08000000 -+ -+ -+/* Alpha specific definitions. */ -+ -+/* Legal values for e_flags field of Elf64_Ehdr. */ -+ -+#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ -+#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ -+ -+/* Legal values for sh_type field of Elf64_Shdr. */ -+ -+/* These two are primerily concerned with ECOFF debugging info. */ -+#define SHT_ALPHA_DEBUG 0x70000001 -+#define SHT_ALPHA_REGINFO 0x70000002 -+ -+/* Legal values for sh_flags field of Elf64_Shdr. */ -+ -+#define SHF_ALPHA_GPREL 0x10000000 -+ -+/* Legal values for st_other field of Elf64_Sym. */ -+#define STO_ALPHA_NOPV 0x80 /* No PV required. */ -+#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ -+ -+/* Alpha relocs. */ -+ -+#define R_ALPHA_NONE 0 /* No reloc */ -+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ -+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ -+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ -+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ -+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ -+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ -+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ -+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ -+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ -+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ -+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ -+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ -+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ -+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ -+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ -+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ -+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ -+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ -+#define R_ALPHA_TLS_GD_HI 28 -+#define R_ALPHA_TLSGD 29 -+#define R_ALPHA_TLS_LDM 30 -+#define R_ALPHA_DTPMOD64 31 -+#define R_ALPHA_GOTDTPREL 32 -+#define R_ALPHA_DTPREL64 33 -+#define R_ALPHA_DTPRELHI 34 -+#define R_ALPHA_DTPRELLO 35 -+#define R_ALPHA_DTPREL16 36 -+#define R_ALPHA_GOTTPREL 37 -+#define R_ALPHA_TPREL64 38 -+#define R_ALPHA_TPRELHI 39 -+#define R_ALPHA_TPRELLO 40 -+#define R_ALPHA_TPREL16 41 -+/* Keep this the last entry. */ -+#define R_ALPHA_NUM 46 -+ -+/* Magic values of the LITUSE relocation addend. */ -+#define LITUSE_ALPHA_ADDR 0 -+#define LITUSE_ALPHA_BASE 1 -+#define LITUSE_ALPHA_BYTOFF 2 -+#define LITUSE_ALPHA_JSR 3 -+#define LITUSE_ALPHA_TLS_GD 4 -+#define LITUSE_ALPHA_TLS_LDM 5 -+ -+ -+/* PowerPC specific declarations */ -+ -+/* Values for Elf32/64_Ehdr.e_flags. */ -+#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ -+ -+/* Cygnus local bits below */ -+#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ -+#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib -+ flag */ -+ -+/* PowerPC relocations defined by the ABIs */ -+#define R_PPC_NONE 0 -+#define R_PPC_ADDR32 1 /* 32bit absolute address */ -+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -+#define R_PPC_ADDR16 3 /* 16bit absolute address */ -+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -+#define R_PPC_ADDR14_BRTAKEN 8 -+#define R_PPC_ADDR14_BRNTAKEN 9 -+#define R_PPC_REL24 10 /* PC relative 26 bit */ -+#define R_PPC_REL14 11 /* PC relative 16 bit */ -+#define R_PPC_REL14_BRTAKEN 12 -+#define R_PPC_REL14_BRNTAKEN 13 -+#define R_PPC_GOT16 14 -+#define R_PPC_GOT16_LO 15 -+#define R_PPC_GOT16_HI 16 -+#define R_PPC_GOT16_HA 17 -+#define R_PPC_PLTREL24 18 -+#define R_PPC_COPY 19 -+#define R_PPC_GLOB_DAT 20 -+#define R_PPC_JMP_SLOT 21 -+#define R_PPC_RELATIVE 22 -+#define R_PPC_LOCAL24PC 23 -+#define R_PPC_UADDR32 24 -+#define R_PPC_UADDR16 25 -+#define R_PPC_REL32 26 -+#define R_PPC_PLT32 27 -+#define R_PPC_PLTREL32 28 -+#define R_PPC_PLT16_LO 29 -+#define R_PPC_PLT16_HI 30 -+#define R_PPC_PLT16_HA 31 -+#define R_PPC_SDAREL16 32 -+#define R_PPC_SECTOFF 33 -+#define R_PPC_SECTOFF_LO 34 -+#define R_PPC_SECTOFF_HI 35 -+#define R_PPC_SECTOFF_HA 36 -+ -+/* PowerPC relocations defined for the TLS access ABI. */ -+#define R_PPC_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ -+#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ -+#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ -+#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ -+#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ -+#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ -+#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ -+#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ -+#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ -+ -+/* Keep this the last entry. */ -+#define R_PPC_NUM 95 -+ -+/* The remaining relocs are from the Embedded ELF ABI, and are not -+ in the SVR4 ELF ABI. */ -+#define R_PPC_EMB_NADDR32 101 -+#define R_PPC_EMB_NADDR16 102 -+#define R_PPC_EMB_NADDR16_LO 103 -+#define R_PPC_EMB_NADDR16_HI 104 -+#define R_PPC_EMB_NADDR16_HA 105 -+#define R_PPC_EMB_SDAI16 106 -+#define R_PPC_EMB_SDA2I16 107 -+#define R_PPC_EMB_SDA2REL 108 -+#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ -+#define R_PPC_EMB_MRKREF 110 -+#define R_PPC_EMB_RELSEC16 111 -+#define R_PPC_EMB_RELST_LO 112 -+#define R_PPC_EMB_RELST_HI 113 -+#define R_PPC_EMB_RELST_HA 114 -+#define R_PPC_EMB_BIT_FLD 115 -+#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ -+ -+/* Diab tool relocations. */ -+#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ -+#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ -+#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ -+#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ -+#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ -+#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ -+ -+/* This is a phony reloc to handle any old fashioned TOC16 references -+ that may still be in object files. */ -+#define R_PPC_TOC16 255 -+ -+ -+/* PowerPC64 relocations defined by the ABIs */ -+#define R_PPC64_NONE R_PPC_NONE -+#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ -+#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ -+#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ -+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ -+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ -+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ -+#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ -+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -+#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ -+#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ -+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -+#define R_PPC64_GOT16 R_PPC_GOT16 -+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA -+ -+#define R_PPC64_COPY R_PPC_COPY -+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -+#define R_PPC64_RELATIVE R_PPC_RELATIVE -+ -+#define R_PPC64_UADDR32 R_PPC_UADDR32 -+#define R_PPC64_UADDR16 R_PPC_UADDR16 -+#define R_PPC64_REL32 R_PPC_REL32 -+#define R_PPC64_PLT32 R_PPC_PLT32 -+#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA -+ -+#define R_PPC64_SECTOFF R_PPC_SECTOFF -+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -+#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ -+#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ -+#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ -+#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ -+#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ -+#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ -+#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ -+#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ -+#define R_PPC64_PLT64 45 /* doubleword64 L + A */ -+#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ -+#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ -+#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ -+#define R_PPC64_TOC 51 /* doubleword64 .TOC */ -+#define R_PPC64_PLTGOT16 52 /* half16* M + A */ -+#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ -+#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ -+#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ -+ -+#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ -+#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ -+#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ -+#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ -+#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ -+#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ -+#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ -+#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ -+#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ -+#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ -+#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ -+ -+/* PowerPC64 relocations defined for the TLS access ABI. */ -+#define R_PPC64_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ -+#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ -+#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ -+#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ -+#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ -+#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ -+#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ -+#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ -+#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ -+#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ -+#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ -+#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ -+#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ -+#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ -+#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ -+#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ -+#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ -+ -+/* Keep this the last entry. */ -+#define R_PPC64_NUM 107 -+ -+/* PowerPC64 specific values for the Dyn d_tag field. */ -+#define DT_PPC64_GLINK (DT_LOPROC + 0) -+#define DT_PPC64_NUM 1 -+ -+ -+/* ARM specific declarations */ -+ -+/* Processor specific flags for the ELF header e_flags field. */ -+#define EF_ARM_RELEXEC 0x01 -+#define EF_ARM_HASENTRY 0x02 -+#define EF_ARM_INTERWORK 0x04 -+#define EF_ARM_APCS_26 0x08 -+#define EF_ARM_APCS_FLOAT 0x10 -+#define EF_ARM_PIC 0x20 -+#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ -+#define EF_ARM_NEW_ABI 0x80 -+#define EF_ARM_OLD_ABI 0x100 -+ -+/* Other constants defined in the ARM ELF spec. version B-01. */ -+/* NB. These conflict with values defined above. */ -+#define EF_ARM_SYMSARESORTED 0x04 -+#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -+#define EF_ARM_MAPSYMSFIRST 0x10 -+#define EF_ARM_EABIMASK 0XFF000000 -+ -+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -+#define EF_ARM_EABI_UNKNOWN 0x00000000 -+#define EF_ARM_EABI_VER1 0x01000000 -+#define EF_ARM_EABI_VER2 0x02000000 -+ -+/* Additional symbol types for Thumb */ -+#define STT_ARM_TFUNC 0xd -+ -+/* ARM-specific values for sh_flags */ -+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ -+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined -+ in the input to a link step */ -+ -+/* ARM-specific program header flags */ -+#define PF_ARM_SB 0x10000000 /* Segment contains the location -+ addressed by the static base */ -+ -+/* ARM relocs. */ -+#define R_ARM_NONE 0 /* No reloc */ -+#define R_ARM_PC24 1 /* PC relative 26 bit branch */ -+#define R_ARM_ABS32 2 /* Direct 32 bit */ -+#define R_ARM_REL32 3 /* PC relative 32 bit */ -+#define R_ARM_PC13 4 -+#define R_ARM_ABS16 5 /* Direct 16 bit */ -+#define R_ARM_ABS12 6 /* Direct 12 bit */ -+#define R_ARM_THM_ABS5 7 -+#define R_ARM_ABS8 8 /* Direct 8 bit */ -+#define R_ARM_SBREL32 9 -+#define R_ARM_THM_PC22 10 -+#define R_ARM_THM_PC8 11 -+#define R_ARM_AMP_VCALL9 12 -+#define R_ARM_SWI24 13 -+#define R_ARM_THM_SWI8 14 -+#define R_ARM_XPC25 15 -+#define R_ARM_THM_XPC22 16 -+#define R_ARM_COPY 20 /* Copy symbol at runtime */ -+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ -+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ -+#define R_ARM_RELATIVE 23 /* Adjust by program base */ -+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ -+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ -+#define R_ARM_GOT32 26 /* 32 bit GOT entry */ -+#define R_ARM_PLT32 27 /* 32 bit PLT address */ -+#define R_ARM_ALU_PCREL_7_0 32 -+#define R_ARM_ALU_PCREL_15_8 33 -+#define R_ARM_ALU_PCREL_23_15 34 -+#define R_ARM_LDR_SBREL_11_0 35 -+#define R_ARM_ALU_SBREL_19_12 36 -+#define R_ARM_ALU_SBREL_27_20 37 -+#define R_ARM_GNU_VTENTRY 100 -+#define R_ARM_GNU_VTINHERIT 101 -+#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ -+#define R_ARM_THM_PC9 103 /* thumb conditional branch */ -+#define R_ARM_RXPC25 249 -+#define R_ARM_RSBREL32 250 -+#define R_ARM_THM_RPC22 251 -+#define R_ARM_RREL32 252 -+#define R_ARM_RABS22 253 -+#define R_ARM_RPC24 254 -+#define R_ARM_RBASE 255 -+/* Keep this the last entry. */ -+#define R_ARM_NUM 256 -+ -+/* IA-64 specific declarations. */ -+ -+/* Processor specific flags for the Ehdr e_flags field. */ -+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ -+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ -+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ -+ -+/* Processor specific values for the Phdr p_type field. */ -+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ -+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ -+ -+/* Processor specific flags for the Phdr p_flags field. */ -+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Shdr sh_type field. */ -+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ -+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ -+ -+/* Processor specific flags for the Shdr sh_flags field. */ -+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ -+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Dyn d_tag field. */ -+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -+#define DT_IA_64_NUM 1 -+ -+/* IA-64 relocations. */ -+#define R_IA64_NONE 0x00 /* none */ -+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ -+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ -+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ -+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ -+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ -+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ -+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ -+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ -+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ -+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ -+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ -+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ -+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ -+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ -+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ -+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ -+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ -+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ -+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ -+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ -+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ -+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ -+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ -+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ -+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ -+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ -+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ -+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ -+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ -+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ -+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ -+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ -+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ -+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ -+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ -+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ -+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ -+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ -+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ -+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ -+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ -+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ -+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ -+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ -+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ -+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ -+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ -+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ -+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ -+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ -+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ -+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ -+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ -+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ -+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ -+#define R_IA64_COPY 0x84 /* copy relocation */ -+#define R_IA64_SUB 0x85 /* Addend and symbol difference */ -+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ -+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ -+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ -+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ -+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ -+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ -+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ -+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ -+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ -+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ -+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ -+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ -+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ -+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ -+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ -+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ -+ -+/* SH specific declarations */ -+ -+/* SH relocs. */ -+#define R_SH_NONE 0 -+#define R_SH_DIR32 1 -+#define R_SH_REL32 2 -+#define R_SH_DIR8WPN 3 -+#define R_SH_IND12W 4 -+#define R_SH_DIR8WPL 5 -+#define R_SH_DIR8WPZ 6 -+#define R_SH_DIR8BP 7 -+#define R_SH_DIR8W 8 -+#define R_SH_DIR8L 9 -+#define R_SH_SWITCH16 25 -+#define R_SH_SWITCH32 26 -+#define R_SH_USES 27 -+#define R_SH_COUNT 28 -+#define R_SH_ALIGN 29 -+#define R_SH_CODE 30 -+#define R_SH_DATA 31 -+#define R_SH_LABEL 32 -+#define R_SH_SWITCH8 33 -+#define R_SH_GNU_VTINHERIT 34 -+#define R_SH_GNU_VTENTRY 35 -+#define R_SH_TLS_GD_32 144 -+#define R_SH_TLS_LD_32 145 -+#define R_SH_TLS_LDO_32 146 -+#define R_SH_TLS_IE_32 147 -+#define R_SH_TLS_LE_32 148 -+#define R_SH_TLS_DTPMOD32 149 -+#define R_SH_TLS_DTPOFF32 150 -+#define R_SH_TLS_TPOFF32 151 -+#define R_SH_GOT32 160 -+#define R_SH_PLT32 161 -+#define R_SH_COPY 162 -+#define R_SH_GLOB_DAT 163 -+#define R_SH_JMP_SLOT 164 -+#define R_SH_RELATIVE 165 -+#define R_SH_GOTOFF 166 -+#define R_SH_GOTPC 167 -+/* Keep this the last entry. */ -+#define R_SH_NUM 256 -+ -+/* Additional s390 relocs */ -+ -+#define R_390_NONE 0 /* No reloc. */ -+#define R_390_8 1 /* Direct 8 bit. */ -+#define R_390_12 2 /* Direct 12 bit. */ -+#define R_390_16 3 /* Direct 16 bit. */ -+#define R_390_32 4 /* Direct 32 bit. */ -+#define R_390_PC32 5 /* PC relative 32 bit. */ -+#define R_390_GOT12 6 /* 12 bit GOT offset. */ -+#define R_390_GOT32 7 /* 32 bit GOT offset. */ -+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ -+#define R_390_COPY 9 /* Copy symbol at runtime. */ -+#define R_390_GLOB_DAT 10 /* Create GOT entry. */ -+#define R_390_JMP_SLOT 11 /* Create PLT entry. */ -+#define R_390_RELATIVE 12 /* Adjust by program base. */ -+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ -+#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ -+#define R_390_GOT16 15 /* 16 bit GOT offset. */ -+#define R_390_PC16 16 /* PC relative 16 bit. */ -+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ -+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ -+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ -+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ -+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ -+#define R_390_64 22 /* Direct 64 bit. */ -+#define R_390_PC64 23 /* PC relative 64 bit. */ -+#define R_390_GOT64 24 /* 64 bit GOT offset. */ -+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ -+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ -+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ -+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ -+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ -+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ -+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ -+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ -+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ -+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ -+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ -+#define R_390_TLS_GDCALL 38 /* Tag for function call in general -+ dynamic TLS code. */ -+#define R_390_TLS_LDCALL 39 /* Tag for function call in local -+ dynamic TLS code. */ -+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ -+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ -+#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS -+ block. */ -+ -+/* Keep this the last entry. */ -+#define R_390_NUM 57 -+ -+/* CRIS relocations. */ -+#define R_CRIS_NONE 0 -+#define R_CRIS_8 1 -+#define R_CRIS_16 2 -+#define R_CRIS_32 3 -+#define R_CRIS_8_PCREL 4 -+#define R_CRIS_16_PCREL 5 -+#define R_CRIS_32_PCREL 6 -+#define R_CRIS_GNU_VTINHERIT 7 -+#define R_CRIS_GNU_VTENTRY 8 -+#define R_CRIS_COPY 9 -+#define R_CRIS_GLOB_DAT 10 -+#define R_CRIS_JUMP_SLOT 11 -+#define R_CRIS_RELATIVE 12 -+#define R_CRIS_16_GOT 13 -+#define R_CRIS_32_GOT 14 -+#define R_CRIS_16_GOTPLT 15 -+#define R_CRIS_32_GOTPLT 16 -+#define R_CRIS_32_GOTREL 17 -+#define R_CRIS_32_PLT_GOTREL 18 -+#define R_CRIS_32_PLT_PCREL 19 -+ -+#define R_CRIS_NUM 20 -+ -+/* AMD x86-64 relocations. */ -+#define R_X86_64_NONE 0 /* No reloc */ -+#define R_X86_64_64 1 /* Direct 64 bit */ -+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ -+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ -+#define R_X86_64_PLT32 4 /* 32 bit PLT address */ -+#define R_X86_64_COPY 5 /* Copy symbol at runtime */ -+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ -+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ -+#define R_X86_64_RELATIVE 8 /* Adjust by program base */ -+#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative -+ offset to GOT */ -+#define R_X86_64_32 10 /* Direct 32 bit zero extended */ -+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ -+#define R_X86_64_16 12 /* Direct 16 bit zero extended */ -+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ -+#define R_X86_64_8 14 /* Direct 8 bit sign extended */ -+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ -+#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ -+#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ -+#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ -+#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset -+ to two GOT entries for GD symbol */ -+#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset -+ to two GOT entries for LD symbol */ -+#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ -+#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset -+ to GOT entry for IE symbol */ -+#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ -+ -+#define R_X86_64_NUM 24 -+ -+__END_DECLS -+ -+#endif /* elf.h */ - - #include "elfconfig.h" - -@@ -185,3 +2631,4 @@ - void fatal(const char *fmt, ...); - void warn(const char *fmt, ...); - void merror(const char *fmt, ...); -+ -diff -Nur linux-3.11.5.orig/scripts/mod/sumversion.c linux-3.11.5/scripts/mod/sumversion.c ---- linux-3.11.5.orig/scripts/mod/sumversion.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/sumversion.c 2013-10-16 18:09:31.000000000 +0200 -@@ -1,4 +1,4 @@ --#include -+/* #include */ - #ifdef __sun__ - #include - #else -diff -Nur linux-3.11.5.orig/tools/include/tools/linux_types.h linux-3.11.5/tools/include/tools/linux_types.h ---- linux-3.11.5.orig/tools/include/tools/linux_types.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.11.5/tools/include/tools/linux_types.h 2013-10-16 18:09:31.000000000 +0200 -@@ -0,0 +1,22 @@ -+#ifndef __LINUX_TYPES_H -+#define __LINUX_TYPES_H -+ -+#include -+ -+typedef uint8_t __u8; -+typedef uint8_t __be8; -+typedef uint8_t __le8; -+ -+typedef uint16_t __u16; -+typedef uint16_t __be16; -+typedef uint16_t __le16; -+ -+typedef uint32_t __u32; -+typedef uint32_t __be32; -+typedef uint32_t __le32; -+ -+typedef uint64_t __u64; -+typedef uint64_t __be64; -+typedef uint64_t __le64; -+ -+#endif diff --git a/target/linux/patches/3.10.75/headers-install.patch b/target/linux/patches/3.10.75/headers-install.patch deleted file mode 100644 index 46dc9fe13..000000000 --- a/target/linux/patches/3.10.75/headers-install.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.10.37.orig/scripts/Makefile.headersinst linux-3.10.37/scripts/Makefile.headersinst ---- linux-3.10.37.orig/scripts/Makefile.headersinst 2014-04-14 15:42:31.000000000 +0200 -+++ linux-3.10.37/scripts/Makefile.headersinst 2014-04-24 17:04:51.980248954 +0200 -@@ -107,7 +107,6 @@ - - targets += $(install-file) - $(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE -- $(if $(unwanted),$(call cmd,remove),) - $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) - $(call if_changed,install) - diff --git a/target/linux/patches/3.10.75/mkpiggy.patch b/target/linux/patches/3.10.75/mkpiggy.patch deleted file mode 100644 index d4e815cd2..000000000 --- a/target/linux/patches/3.10.75/mkpiggy.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff -Nur linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c ---- linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c 2012-06-22 20:37:50.000000000 +0200 -+++ linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c 2012-07-03 09:48:02.000000000 +0200 -@@ -29,7 +29,14 @@ - #include - #include - #include --#include -+ -+static uint32_t getle32(const void *p) -+{ -+ const uint8_t *cp = p; -+ -+ return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + -+ ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); -+} - - int main(int argc, char *argv[]) - { -@@ -62,7 +69,7 @@ - } - - ilen = ftell(f); -- olen = get_unaligned_le32(&olen); -+ olen = getle32(&olen); - fclose(f); - - /* diff --git a/target/linux/patches/3.10.75/relocs.patch b/target/linux/patches/3.10.75/relocs.patch deleted file mode 100644 index 649b9e73e..000000000 --- a/target/linux/patches/3.10.75/relocs.patch +++ /dev/null @@ -1,2693 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/x86/tools/relocs.h linux-3.11.5/arch/x86/tools/relocs.h ---- linux-3.11.5.orig/arch/x86/tools/relocs.h 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/x86/tools/relocs.h 2013-10-20 17:41:16.000000000 +0200 -@@ -9,10 +9,14 @@ - #include - #include - #include -+#ifdef __linux__ - #include - #include - #define USE_BSD - #include -+#else -+#include "elf.h" -+#endif - #include - #include - -diff -Nur linux-3.11.5.orig/tools/include/elf.h linux-3.11.5/tools/include/elf.h ---- linux-3.11.5.orig/tools/include/elf.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.11.5/tools/include/elf.h 2013-10-20 17:42:10.000000000 +0200 -@@ -0,0 +1,2671 @@ -+#ifndef _ELF_H -+#define _ELF_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#include -+ -+typedef uint16_t Elf32_Half; -+typedef uint16_t Elf64_Half; -+ -+typedef uint32_t Elf32_Word; -+typedef int32_t Elf32_Sword; -+typedef uint32_t Elf64_Word; -+typedef int32_t Elf64_Sword; -+ -+typedef uint64_t Elf32_Xword; -+typedef int64_t Elf32_Sxword; -+typedef uint64_t Elf64_Xword; -+typedef int64_t Elf64_Sxword; -+ -+typedef uint32_t Elf32_Addr; -+typedef uint64_t Elf64_Addr; -+ -+typedef uint32_t Elf32_Off; -+typedef uint64_t Elf64_Off; -+ -+typedef uint16_t Elf32_Section; -+typedef uint16_t Elf64_Section; -+ -+typedef Elf32_Half Elf32_Versym; -+typedef Elf64_Half Elf64_Versym; -+ -+#define EI_NIDENT (16) -+ -+typedef struct { -+ unsigned char e_ident[EI_NIDENT]; -+ Elf32_Half e_type; -+ Elf32_Half e_machine; -+ Elf32_Word e_version; -+ Elf32_Addr e_entry; -+ Elf32_Off e_phoff; -+ Elf32_Off e_shoff; -+ Elf32_Word e_flags; -+ Elf32_Half e_ehsize; -+ Elf32_Half e_phentsize; -+ Elf32_Half e_phnum; -+ Elf32_Half e_shentsize; -+ Elf32_Half e_shnum; -+ Elf32_Half e_shstrndx; -+} Elf32_Ehdr; -+ -+typedef struct { -+ unsigned char e_ident[EI_NIDENT]; -+ Elf64_Half e_type; -+ Elf64_Half e_machine; -+ Elf64_Word e_version; -+ Elf64_Addr e_entry; -+ Elf64_Off e_phoff; -+ Elf64_Off e_shoff; -+ Elf64_Word e_flags; -+ Elf64_Half e_ehsize; -+ Elf64_Half e_phentsize; -+ Elf64_Half e_phnum; -+ Elf64_Half e_shentsize; -+ Elf64_Half e_shnum; -+ Elf64_Half e_shstrndx; -+} Elf64_Ehdr; -+ -+#define EI_MAG0 0 -+#define ELFMAG0 0x7f -+ -+#define EI_MAG1 1 -+#define ELFMAG1 'E' -+ -+#define EI_MAG2 2 -+#define ELFMAG2 'L' -+ -+#define EI_MAG3 3 -+#define ELFMAG3 'F' -+ -+ -+#define ELFMAG "\177ELF" -+#define SELFMAG 4 -+ -+#define EI_CLASS 4 -+#define ELFCLASSNONE 0 -+#define ELFCLASS32 1 -+#define ELFCLASS64 2 -+#define ELFCLASSNUM 3 -+ -+#define EI_DATA 5 -+#define ELFDATANONE 0 -+#define ELFDATA2LSB 1 -+#define ELFDATA2MSB 2 -+#define ELFDATANUM 3 -+ -+#define EI_VERSION 6 -+ -+ -+#define EI_OSABI 7 -+#define ELFOSABI_NONE 0 -+#define ELFOSABI_SYSV 0 -+#define ELFOSABI_HPUX 1 -+#define ELFOSABI_NETBSD 2 -+#define ELFOSABI_LINUX 3 -+#define ELFOSABI_GNU 3 -+#define ELFOSABI_SOLARIS 6 -+#define ELFOSABI_AIX 7 -+#define ELFOSABI_IRIX 8 -+#define ELFOSABI_FREEBSD 9 -+#define ELFOSABI_TRU64 10 -+#define ELFOSABI_MODESTO 11 -+#define ELFOSABI_OPENBSD 12 -+#define ELFOSABI_ARM 97 -+#define ELFOSABI_STANDALONE 255 -+ -+#define EI_ABIVERSION 8 -+ -+#define EI_PAD 9 -+ -+ -+ -+#define ET_NONE 0 -+#define ET_REL 1 -+#define ET_EXEC 2 -+#define ET_DYN 3 -+#define ET_CORE 4 -+#define ET_NUM 5 -+#define ET_LOOS 0xfe00 -+#define ET_HIOS 0xfeff -+#define ET_LOPROC 0xff00 -+#define ET_HIPROC 0xffff -+ -+ -+ -+#define EM_NONE 0 -+#define EM_M32 1 -+#define EM_SPARC 2 -+#define EM_386 3 -+#define EM_68K 4 -+#define EM_88K 5 -+#define EM_860 7 -+#define EM_MIPS 8 -+#define EM_S370 9 -+#define EM_MIPS_RS3_LE 10 -+ -+#define EM_PARISC 15 -+#define EM_VPP500 17 -+#define EM_SPARC32PLUS 18 -+#define EM_960 19 -+#define EM_PPC 20 -+#define EM_PPC64 21 -+#define EM_S390 22 -+ -+#define EM_V800 36 -+#define EM_FR20 37 -+#define EM_RH32 38 -+#define EM_RCE 39 -+#define EM_ARM 40 -+#define EM_FAKE_ALPHA 41 -+#define EM_SH 42 -+#define EM_SPARCV9 43 -+#define EM_TRICORE 44 -+#define EM_ARC 45 -+#define EM_H8_300 46 -+#define EM_H8_300H 47 -+#define EM_H8S 48 -+#define EM_H8_500 49 -+#define EM_IA_64 50 -+#define EM_MIPS_X 51 -+#define EM_COLDFIRE 52 -+#define EM_68HC12 53 -+#define EM_MMA 54 -+#define EM_PCP 55 -+#define EM_NCPU 56 -+#define EM_NDR1 57 -+#define EM_STARCORE 58 -+#define EM_ME16 59 -+#define EM_ST100 60 -+#define EM_TINYJ 61 -+#define EM_X86_64 62 -+#define EM_PDSP 63 -+ -+#define EM_FX66 66 -+#define EM_ST9PLUS 67 -+#define EM_ST7 68 -+#define EM_68HC16 69 -+#define EM_68HC11 70 -+#define EM_68HC08 71 -+#define EM_68HC05 72 -+#define EM_SVX 73 -+#define EM_ST19 74 -+#define EM_VAX 75 -+#define EM_CRIS 76 -+#define EM_JAVELIN 77 -+#define EM_FIREPATH 78 -+#define EM_ZSP 79 -+#define EM_MMIX 80 -+#define EM_HUANY 81 -+#define EM_PRISM 82 -+#define EM_AVR 83 -+#define EM_FR30 84 -+#define EM_D10V 85 -+#define EM_D30V 86 -+#define EM_V850 87 -+#define EM_M32R 88 -+#define EM_MN10300 89 -+#define EM_MN10200 90 -+#define EM_PJ 91 -+#define EM_OPENRISC 92 -+#define EM_ARC_A5 93 -+#define EM_XTENSA 94 -+#define EM_AARCH64 183 -+#define EM_TILEPRO 188 -+#define EM_MICROBLAZE 189 -+#define EM_TILEGX 191 -+#define EM_NUM 192 -+#define EM_ALPHA 0x9026 -+ -+#define EV_NONE 0 -+#define EV_CURRENT 1 -+#define EV_NUM 2 -+ -+typedef struct { -+ Elf32_Word sh_name; -+ Elf32_Word sh_type; -+ Elf32_Word sh_flags; -+ Elf32_Addr sh_addr; -+ Elf32_Off sh_offset; -+ Elf32_Word sh_size; -+ Elf32_Word sh_link; -+ Elf32_Word sh_info; -+ Elf32_Word sh_addralign; -+ Elf32_Word sh_entsize; -+} Elf32_Shdr; -+ -+typedef struct { -+ Elf64_Word sh_name; -+ Elf64_Word sh_type; -+ Elf64_Xword sh_flags; -+ Elf64_Addr sh_addr; -+ Elf64_Off sh_offset; -+ Elf64_Xword sh_size; -+ Elf64_Word sh_link; -+ Elf64_Word sh_info; -+ Elf64_Xword sh_addralign; -+ Elf64_Xword sh_entsize; -+} Elf64_Shdr; -+ -+ -+ -+#define SHN_UNDEF 0 -+#define SHN_LORESERVE 0xff00 -+#define SHN_LOPROC 0xff00 -+#define SHN_BEFORE 0xff00 -+ -+#define SHN_AFTER 0xff01 -+ -+#define SHN_HIPROC 0xff1f -+#define SHN_LOOS 0xff20 -+#define SHN_HIOS 0xff3f -+#define SHN_ABS 0xfff1 -+#define SHN_COMMON 0xfff2 -+#define SHN_XINDEX 0xffff -+#define SHN_HIRESERVE 0xffff -+ -+ -+ -+#define SHT_NULL 0 -+#define SHT_PROGBITS 1 -+#define SHT_SYMTAB 2 -+#define SHT_STRTAB 3 -+#define SHT_RELA 4 -+#define SHT_HASH 5 -+#define SHT_DYNAMIC 6 -+#define SHT_NOTE 7 -+#define SHT_NOBITS 8 -+#define SHT_REL 9 -+#define SHT_SHLIB 10 -+#define SHT_DYNSYM 11 -+#define SHT_INIT_ARRAY 14 -+#define SHT_FINI_ARRAY 15 -+#define SHT_PREINIT_ARRAY 16 -+#define SHT_GROUP 17 -+#define SHT_SYMTAB_SHNDX 18 -+#define SHT_NUM 19 -+#define SHT_LOOS 0x60000000 -+#define SHT_GNU_ATTRIBUTES 0x6ffffff5 -+#define SHT_GNU_HASH 0x6ffffff6 -+#define SHT_GNU_LIBLIST 0x6ffffff7 -+#define SHT_CHECKSUM 0x6ffffff8 -+#define SHT_LOSUNW 0x6ffffffa -+#define SHT_SUNW_move 0x6ffffffa -+#define SHT_SUNW_COMDAT 0x6ffffffb -+#define SHT_SUNW_syminfo 0x6ffffffc -+#define SHT_GNU_verdef 0x6ffffffd -+#define SHT_GNU_verneed 0x6ffffffe -+#define SHT_GNU_versym 0x6fffffff -+#define SHT_HISUNW 0x6fffffff -+#define SHT_HIOS 0x6fffffff -+#define SHT_LOPROC 0x70000000 -+#define SHT_HIPROC 0x7fffffff -+#define SHT_LOUSER 0x80000000 -+#define SHT_HIUSER 0x8fffffff -+ -+#define SHF_WRITE (1 << 0) -+#define SHF_ALLOC (1 << 1) -+#define SHF_EXECINSTR (1 << 2) -+#define SHF_MERGE (1 << 4) -+#define SHF_STRINGS (1 << 5) -+#define SHF_INFO_LINK (1 << 6) -+#define SHF_LINK_ORDER (1 << 7) -+#define SHF_OS_NONCONFORMING (1 << 8) -+ -+#define SHF_GROUP (1 << 9) -+#define SHF_TLS (1 << 10) -+#define SHF_MASKOS 0x0ff00000 -+#define SHF_MASKPROC 0xf0000000 -+#define SHF_ORDERED (1 << 30) -+#define SHF_EXCLUDE (1 << 31) -+ -+#define GRP_COMDAT 0x1 -+ -+typedef struct { -+ Elf32_Word st_name; -+ Elf32_Addr st_value; -+ Elf32_Word st_size; -+ unsigned char st_info; -+ unsigned char st_other; -+ Elf32_Section st_shndx; -+} Elf32_Sym; -+ -+typedef struct { -+ Elf64_Word st_name; -+ unsigned char st_info; -+ unsigned char st_other; -+ Elf64_Section st_shndx; -+ Elf64_Addr st_value; -+ Elf64_Xword st_size; -+} Elf64_Sym; -+ -+typedef struct { -+ Elf32_Half si_boundto; -+ Elf32_Half si_flags; -+} Elf32_Syminfo; -+ -+typedef struct { -+ Elf64_Half si_boundto; -+ Elf64_Half si_flags; -+} Elf64_Syminfo; -+ -+#define SYMINFO_BT_SELF 0xffff -+#define SYMINFO_BT_PARENT 0xfffe -+#define SYMINFO_BT_LOWRESERVE 0xff00 -+ -+#define SYMINFO_FLG_DIRECT 0x0001 -+#define SYMINFO_FLG_PASSTHRU 0x0002 -+#define SYMINFO_FLG_COPY 0x0004 -+#define SYMINFO_FLG_LAZYLOAD 0x0008 -+ -+#define SYMINFO_NONE 0 -+#define SYMINFO_CURRENT 1 -+#define SYMINFO_NUM 2 -+ -+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -+#define ELF32_ST_TYPE(val) ((val) & 0xf) -+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -+ -+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) -+ -+#define STB_LOCAL 0 -+#define STB_GLOBAL 1 -+#define STB_WEAK 2 -+#define STB_NUM 3 -+#define STB_LOOS 10 -+#define STB_GNU_UNIQUE 10 -+#define STB_HIOS 12 -+#define STB_LOPROC 13 -+#define STB_HIPROC 15 -+ -+#define STT_NOTYPE 0 -+#define STT_OBJECT 1 -+#define STT_FUNC 2 -+#define STT_SECTION 3 -+#define STT_FILE 4 -+#define STT_COMMON 5 -+#define STT_TLS 6 -+#define STT_NUM 7 -+#define STT_LOOS 10 -+#define STT_GNU_IFUNC 10 -+#define STT_HIOS 12 -+#define STT_LOPROC 13 -+#define STT_HIPROC 15 -+ -+#define STN_UNDEF 0 -+ -+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) -+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) -+ -+#define STV_DEFAULT 0 -+#define STV_INTERNAL 1 -+#define STV_HIDDEN 2 -+#define STV_PROTECTED 3 -+ -+ -+ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; -+ Elf32_Word r_info; -+} Elf32_Rel; -+ -+typedef struct { -+ Elf64_Addr r_offset; -+ Elf64_Xword r_info; -+} Elf64_Rel; -+ -+ -+ -+typedef struct { -+ Elf32_Addr r_offset; -+ Elf32_Word r_info; -+ Elf32_Sword r_addend; -+} Elf32_Rela; -+ -+typedef struct { -+ Elf64_Addr r_offset; -+ Elf64_Xword r_info; -+ Elf64_Sxword r_addend; -+} Elf64_Rela; -+ -+ -+ -+#define ELF32_R_SYM(val) ((val) >> 8) -+#define ELF32_R_TYPE(val) ((val) & 0xff) -+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) -+ -+#define ELF64_R_SYM(i) ((i) >> 32) -+#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) -+ -+ -+ -+typedef struct { -+ Elf32_Word p_type; -+ Elf32_Off p_offset; -+ Elf32_Addr p_vaddr; -+ Elf32_Addr p_paddr; -+ Elf32_Word p_filesz; -+ Elf32_Word p_memsz; -+ Elf32_Word p_flags; -+ Elf32_Word p_align; -+} Elf32_Phdr; -+ -+typedef struct { -+ Elf64_Word p_type; -+ Elf64_Word p_flags; -+ Elf64_Off p_offset; -+ Elf64_Addr p_vaddr; -+ Elf64_Addr p_paddr; -+ Elf64_Xword p_filesz; -+ Elf64_Xword p_memsz; -+ Elf64_Xword p_align; -+} Elf64_Phdr; -+ -+ -+ -+#define PT_NULL 0 -+#define PT_LOAD 1 -+#define PT_DYNAMIC 2 -+#define PT_INTERP 3 -+#define PT_NOTE 4 -+#define PT_SHLIB 5 -+#define PT_PHDR 6 -+#define PT_TLS 7 -+#define PT_NUM 8 -+#define PT_LOOS 0x60000000 -+#define PT_GNU_EH_FRAME 0x6474e550 -+#define PT_GNU_STACK 0x6474e551 -+#define PT_GNU_RELRO 0x6474e552 -+#define PT_LOSUNW 0x6ffffffa -+#define PT_SUNWBSS 0x6ffffffa -+#define PT_SUNWSTACK 0x6ffffffb -+#define PT_HISUNW 0x6fffffff -+#define PT_HIOS 0x6fffffff -+#define PT_LOPROC 0x70000000 -+#define PT_HIPROC 0x7fffffff -+ -+ -+#define PN_XNUM 0xffff -+ -+ -+#define PF_X (1 << 0) -+#define PF_W (1 << 1) -+#define PF_R (1 << 2) -+#define PF_MASKOS 0x0ff00000 -+#define PF_MASKPROC 0xf0000000 -+ -+ -+ -+#define NT_PRSTATUS 1 -+#define NT_FPREGSET 2 -+#define NT_PRPSINFO 3 -+#define NT_PRXREG 4 -+#define NT_TASKSTRUCT 4 -+#define NT_PLATFORM 5 -+#define NT_AUXV 6 -+#define NT_GWINDOWS 7 -+#define NT_ASRS 8 -+#define NT_PSTATUS 10 -+#define NT_PSINFO 13 -+#define NT_PRCRED 14 -+#define NT_UTSNAME 15 -+#define NT_LWPSTATUS 16 -+#define NT_LWPSINFO 17 -+#define NT_PRFPXREG 20 -+#define NT_SIGINFO 0x53494749 -+#define NT_FILE 0x46494c45 -+#define NT_PRXFPREG 0x46e62b7f -+#define NT_PPC_VMX 0x100 -+#define NT_PPC_SPE 0x101 -+#define NT_PPC_VSX 0x102 -+#define NT_386_TLS 0x200 -+#define NT_386_IOPERM 0x201 -+#define NT_X86_XSTATE 0x202 -+#define NT_S390_HIGH_GPRS 0x300 -+#define NT_S390_TIMER 0x301 -+#define NT_S390_TODCMP 0x302 -+#define NT_S390_TODPREG 0x303 -+#define NT_S390_CTRS 0x304 -+#define NT_S390_PREFIX 0x305 -+#define NT_S390_LAST_BREAK 0x306 -+#define NT_S390_SYSTEM_CALL 0x307 -+#define NT_S390_TDB 0x308 -+#define NT_ARM_VFP 0x400 -+#define NT_ARM_TLS 0x401 -+#define NT_ARM_HW_BREAK 0x402 -+#define NT_ARM_HW_WATCH 0x403 -+#define NT_METAG_CBUF 0x500 -+#define NT_METAG_RPIPE 0x501 -+#define NT_METAG_TLS 0x502 -+#define NT_VERSION 1 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Sword d_tag; -+ union { -+ Elf32_Word d_val; -+ Elf32_Addr d_ptr; -+ } d_un; -+} Elf32_Dyn; -+ -+typedef struct { -+ Elf64_Sxword d_tag; -+ union { -+ Elf64_Xword d_val; -+ Elf64_Addr d_ptr; -+ } d_un; -+} Elf64_Dyn; -+ -+ -+ -+#define DT_NULL 0 -+#define DT_NEEDED 1 -+#define DT_PLTRELSZ 2 -+#define DT_PLTGOT 3 -+#define DT_HASH 4 -+#define DT_STRTAB 5 -+#define DT_SYMTAB 6 -+#define DT_RELA 7 -+#define DT_RELASZ 8 -+#define DT_RELAENT 9 -+#define DT_STRSZ 10 -+#define DT_SYMENT 11 -+#define DT_INIT 12 -+#define DT_FINI 13 -+#define DT_SONAME 14 -+#define DT_RPATH 15 -+#define DT_SYMBOLIC 16 -+#define DT_REL 17 -+#define DT_RELSZ 18 -+#define DT_RELENT 19 -+#define DT_PLTREL 20 -+#define DT_DEBUG 21 -+#define DT_TEXTREL 22 -+#define DT_JMPREL 23 -+#define DT_BIND_NOW 24 -+#define DT_INIT_ARRAY 25 -+#define DT_FINI_ARRAY 26 -+#define DT_INIT_ARRAYSZ 27 -+#define DT_FINI_ARRAYSZ 28 -+#define DT_RUNPATH 29 -+#define DT_FLAGS 30 -+#define DT_ENCODING 32 -+#define DT_PREINIT_ARRAY 32 -+#define DT_PREINIT_ARRAYSZ 33 -+#define DT_NUM 34 -+#define DT_LOOS 0x6000000d -+#define DT_HIOS 0x6ffff000 -+#define DT_LOPROC 0x70000000 -+#define DT_HIPROC 0x7fffffff -+#define DT_PROCNUM DT_MIPS_NUM -+ -+#define DT_VALRNGLO 0x6ffffd00 -+#define DT_GNU_PRELINKED 0x6ffffdf5 -+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 -+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 -+#define DT_CHECKSUM 0x6ffffdf8 -+#define DT_PLTPADSZ 0x6ffffdf9 -+#define DT_MOVEENT 0x6ffffdfa -+#define DT_MOVESZ 0x6ffffdfb -+#define DT_FEATURE_1 0x6ffffdfc -+#define DT_POSFLAG_1 0x6ffffdfd -+ -+#define DT_SYMINSZ 0x6ffffdfe -+#define DT_SYMINENT 0x6ffffdff -+#define DT_VALRNGHI 0x6ffffdff -+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) -+#define DT_VALNUM 12 -+ -+#define DT_ADDRRNGLO 0x6ffffe00 -+#define DT_GNU_HASH 0x6ffffef5 -+#define DT_TLSDESC_PLT 0x6ffffef6 -+#define DT_TLSDESC_GOT 0x6ffffef7 -+#define DT_GNU_CONFLICT 0x6ffffef8 -+#define DT_GNU_LIBLIST 0x6ffffef9 -+#define DT_CONFIG 0x6ffffefa -+#define DT_DEPAUDIT 0x6ffffefb -+#define DT_AUDIT 0x6ffffefc -+#define DT_PLTPAD 0x6ffffefd -+#define DT_MOVETAB 0x6ffffefe -+#define DT_SYMINFO 0x6ffffeff -+#define DT_ADDRRNGHI 0x6ffffeff -+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) -+#define DT_ADDRNUM 11 -+ -+ -+ -+#define DT_VERSYM 0x6ffffff0 -+ -+#define DT_RELACOUNT 0x6ffffff9 -+#define DT_RELCOUNT 0x6ffffffa -+ -+ -+#define DT_FLAGS_1 0x6ffffffb -+#define DT_VERDEF 0x6ffffffc -+ -+#define DT_VERDEFNUM 0x6ffffffd -+#define DT_VERNEED 0x6ffffffe -+ -+#define DT_VERNEEDNUM 0x6fffffff -+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) -+#define DT_VERSIONTAGNUM 16 -+ -+ -+ -+#define DT_AUXILIARY 0x7ffffffd -+#define DT_FILTER 0x7fffffff -+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -+#define DT_EXTRANUM 3 -+ -+ -+#define DF_ORIGIN 0x00000001 -+#define DF_SYMBOLIC 0x00000002 -+#define DF_TEXTREL 0x00000004 -+#define DF_BIND_NOW 0x00000008 -+#define DF_STATIC_TLS 0x00000010 -+ -+ -+ -+#define DF_1_NOW 0x00000001 -+#define DF_1_GLOBAL 0x00000002 -+#define DF_1_GROUP 0x00000004 -+#define DF_1_NODELETE 0x00000008 -+#define DF_1_LOADFLTR 0x00000010 -+#define DF_1_INITFIRST 0x00000020 -+#define DF_1_NOOPEN 0x00000040 -+#define DF_1_ORIGIN 0x00000080 -+#define DF_1_DIRECT 0x00000100 -+#define DF_1_TRANS 0x00000200 -+#define DF_1_INTERPOSE 0x00000400 -+#define DF_1_NODEFLIB 0x00000800 -+#define DF_1_NODUMP 0x00001000 -+#define DF_1_CONFALT 0x00002000 -+#define DF_1_ENDFILTEE 0x00004000 -+#define DF_1_DISPRELDNE 0x00008000 -+#define DF_1_DISPRELPND 0x00010000 -+#define DF_1_NODIRECT 0x00020000 -+#define DF_1_IGNMULDEF 0x00040000 -+#define DF_1_NOKSYMS 0x00080000 -+#define DF_1_NOHDR 0x00100000 -+#define DF_1_EDITED 0x00200000 -+#define DF_1_NORELOC 0x00400000 -+#define DF_1_SYMINTPOSE 0x00800000 -+#define DF_1_GLOBAUDIT 0x01000000 -+#define DF_1_SINGLETON 0x02000000 -+ -+#define DTF_1_PARINIT 0x00000001 -+#define DTF_1_CONFEXP 0x00000002 -+ -+ -+#define DF_P1_LAZYLOAD 0x00000001 -+#define DF_P1_GROUPPERM 0x00000002 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Half vd_version; -+ Elf32_Half vd_flags; -+ Elf32_Half vd_ndx; -+ Elf32_Half vd_cnt; -+ Elf32_Word vd_hash; -+ Elf32_Word vd_aux; -+ Elf32_Word vd_next; -+} Elf32_Verdef; -+ -+typedef struct { -+ Elf64_Half vd_version; -+ Elf64_Half vd_flags; -+ Elf64_Half vd_ndx; -+ Elf64_Half vd_cnt; -+ Elf64_Word vd_hash; -+ Elf64_Word vd_aux; -+ Elf64_Word vd_next; -+} Elf64_Verdef; -+ -+ -+ -+#define VER_DEF_NONE 0 -+#define VER_DEF_CURRENT 1 -+#define VER_DEF_NUM 2 -+ -+ -+#define VER_FLG_BASE 0x1 -+#define VER_FLG_WEAK 0x2 -+ -+ -+#define VER_NDX_LOCAL 0 -+#define VER_NDX_GLOBAL 1 -+#define VER_NDX_LORESERVE 0xff00 -+#define VER_NDX_ELIMINATE 0xff01 -+ -+ -+ -+typedef struct { -+ Elf32_Word vda_name; -+ Elf32_Word vda_next; -+} Elf32_Verdaux; -+ -+typedef struct { -+ Elf64_Word vda_name; -+ Elf64_Word vda_next; -+} Elf64_Verdaux; -+ -+ -+ -+ -+typedef struct { -+ Elf32_Half vn_version; -+ Elf32_Half vn_cnt; -+ Elf32_Word vn_file; -+ Elf32_Word vn_aux; -+ Elf32_Word vn_next; -+} Elf32_Verneed; -+ -+typedef struct { -+ Elf64_Half vn_version; -+ Elf64_Half vn_cnt; -+ Elf64_Word vn_file; -+ Elf64_Word vn_aux; -+ Elf64_Word vn_next; -+} Elf64_Verneed; -+ -+ -+ -+#define VER_NEED_NONE 0 -+#define VER_NEED_CURRENT 1 -+#define VER_NEED_NUM 2 -+ -+ -+ -+typedef struct { -+ Elf32_Word vna_hash; -+ Elf32_Half vna_flags; -+ Elf32_Half vna_other; -+ Elf32_Word vna_name; -+ Elf32_Word vna_next; -+} Elf32_Vernaux; -+ -+typedef struct { -+ Elf64_Word vna_hash; -+ Elf64_Half vna_flags; -+ Elf64_Half vna_other; -+ Elf64_Word vna_name; -+ Elf64_Word vna_next; -+} Elf64_Vernaux; -+ -+ -+ -+#define VER_FLG_WEAK 0x2 -+ -+ -+ -+typedef struct { -+ uint32_t a_type; -+ union { -+ uint32_t a_val; -+ } a_un; -+} Elf32_auxv_t; -+ -+typedef struct { -+ uint64_t a_type; -+ union { -+ uint64_t a_val; -+ } a_un; -+} Elf64_auxv_t; -+ -+ -+ -+#define AT_NULL 0 -+#define AT_IGNORE 1 -+#define AT_EXECFD 2 -+#define AT_PHDR 3 -+#define AT_PHENT 4 -+#define AT_PHNUM 5 -+#define AT_PAGESZ 6 -+#define AT_BASE 7 -+#define AT_FLAGS 8 -+#define AT_ENTRY 9 -+#define AT_NOTELF 10 -+#define AT_UID 11 -+#define AT_EUID 12 -+#define AT_GID 13 -+#define AT_EGID 14 -+#define AT_CLKTCK 17 -+ -+ -+#define AT_PLATFORM 15 -+#define AT_HWCAP 16 -+ -+ -+ -+ -+#define AT_FPUCW 18 -+ -+ -+#define AT_DCACHEBSIZE 19 -+#define AT_ICACHEBSIZE 20 -+#define AT_UCACHEBSIZE 21 -+ -+ -+ -+#define AT_IGNOREPPC 22 -+ -+#define AT_SECURE 23 -+ -+#define AT_BASE_PLATFORM 24 -+ -+#define AT_RANDOM 25 -+ -+#define AT_HWCAP2 26 -+ -+#define AT_EXECFN 31 -+ -+ -+ -+#define AT_SYSINFO 32 -+#define AT_SYSINFO_EHDR 33 -+ -+ -+ -+#define AT_L1I_CACHESHAPE 34 -+#define AT_L1D_CACHESHAPE 35 -+#define AT_L2_CACHESHAPE 36 -+#define AT_L3_CACHESHAPE 37 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Word n_namesz; -+ Elf32_Word n_descsz; -+ Elf32_Word n_type; -+} Elf32_Nhdr; -+ -+typedef struct { -+ Elf64_Word n_namesz; -+ Elf64_Word n_descsz; -+ Elf64_Word n_type; -+} Elf64_Nhdr; -+ -+ -+ -+ -+#define ELF_NOTE_SOLARIS "SUNW Solaris" -+ -+ -+#define ELF_NOTE_GNU "GNU" -+ -+ -+ -+ -+ -+#define ELF_NOTE_PAGESIZE_HINT 1 -+ -+ -+#define NT_GNU_ABI_TAG 1 -+#define ELF_NOTE_ABI NT_GNU_ABI_TAG -+ -+ -+ -+#define ELF_NOTE_OS_LINUX 0 -+#define ELF_NOTE_OS_GNU 1 -+#define ELF_NOTE_OS_SOLARIS2 2 -+#define ELF_NOTE_OS_FREEBSD 3 -+ -+#define NT_GNU_BUILD_ID 3 -+#define NT_GNU_GOLD_VERSION 4 -+ -+ -+ -+typedef struct { -+ Elf32_Xword m_value; -+ Elf32_Word m_info; -+ Elf32_Word m_poffset; -+ Elf32_Half m_repeat; -+ Elf32_Half m_stride; -+} Elf32_Move; -+ -+typedef struct { -+ Elf64_Xword m_value; -+ Elf64_Xword m_info; -+ Elf64_Xword m_poffset; -+ Elf64_Half m_repeat; -+ Elf64_Half m_stride; -+} Elf64_Move; -+ -+ -+#define ELF32_M_SYM(info) ((info) >> 8) -+#define ELF32_M_SIZE(info) ((unsigned char) (info)) -+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) -+ -+#define ELF64_M_SYM(info) ELF32_M_SYM (info) -+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) -+ -+#define EF_CPU32 0x00810000 -+ -+#define R_68K_NONE 0 -+#define R_68K_32 1 -+#define R_68K_16 2 -+#define R_68K_8 3 -+#define R_68K_PC32 4 -+#define R_68K_PC16 5 -+#define R_68K_PC8 6 -+#define R_68K_GOT32 7 -+#define R_68K_GOT16 8 -+#define R_68K_GOT8 9 -+#define R_68K_GOT32O 10 -+#define R_68K_GOT16O 11 -+#define R_68K_GOT8O 12 -+#define R_68K_PLT32 13 -+#define R_68K_PLT16 14 -+#define R_68K_PLT8 15 -+#define R_68K_PLT32O 16 -+#define R_68K_PLT16O 17 -+#define R_68K_PLT8O 18 -+#define R_68K_COPY 19 -+#define R_68K_GLOB_DAT 20 -+#define R_68K_JMP_SLOT 21 -+#define R_68K_RELATIVE 22 -+#define R_68K_NUM 23 -+ -+#define R_386_NONE 0 -+#define R_386_32 1 -+#define R_386_PC32 2 -+#define R_386_GOT32 3 -+#define R_386_PLT32 4 -+#define R_386_COPY 5 -+#define R_386_GLOB_DAT 6 -+#define R_386_JMP_SLOT 7 -+#define R_386_RELATIVE 8 -+#define R_386_GOTOFF 9 -+#define R_386_GOTPC 10 -+#define R_386_32PLT 11 -+#define R_386_TLS_TPOFF 14 -+#define R_386_TLS_IE 15 -+#define R_386_TLS_GOTIE 16 -+#define R_386_TLS_LE 17 -+#define R_386_TLS_GD 18 -+#define R_386_TLS_LDM 19 -+#define R_386_16 20 -+#define R_386_PC16 21 -+#define R_386_8 22 -+#define R_386_PC8 23 -+#define R_386_TLS_GD_32 24 -+#define R_386_TLS_GD_PUSH 25 -+#define R_386_TLS_GD_CALL 26 -+#define R_386_TLS_GD_POP 27 -+#define R_386_TLS_LDM_32 28 -+#define R_386_TLS_LDM_PUSH 29 -+#define R_386_TLS_LDM_CALL 30 -+#define R_386_TLS_LDM_POP 31 -+#define R_386_TLS_LDO_32 32 -+#define R_386_TLS_IE_32 33 -+#define R_386_TLS_LE_32 34 -+#define R_386_TLS_DTPMOD32 35 -+#define R_386_TLS_DTPOFF32 36 -+#define R_386_TLS_TPOFF32 37 -+#define R_386_SIZE32 38 -+#define R_386_TLS_GOTDESC 39 -+#define R_386_TLS_DESC_CALL 40 -+#define R_386_TLS_DESC 41 -+#define R_386_IRELATIVE 42 -+#define R_386_NUM 43 -+ -+ -+ -+ -+ -+#define STT_SPARC_REGISTER 13 -+ -+ -+ -+#define EF_SPARCV9_MM 3 -+#define EF_SPARCV9_TSO 0 -+#define EF_SPARCV9_PSO 1 -+#define EF_SPARCV9_RMO 2 -+#define EF_SPARC_LEDATA 0x800000 -+#define EF_SPARC_EXT_MASK 0xFFFF00 -+#define EF_SPARC_32PLUS 0x000100 -+#define EF_SPARC_SUN_US1 0x000200 -+#define EF_SPARC_HAL_R1 0x000400 -+#define EF_SPARC_SUN_US3 0x000800 -+ -+ -+ -+#define R_SPARC_NONE 0 -+#define R_SPARC_8 1 -+#define R_SPARC_16 2 -+#define R_SPARC_32 3 -+#define R_SPARC_DISP8 4 -+#define R_SPARC_DISP16 5 -+#define R_SPARC_DISP32 6 -+#define R_SPARC_WDISP30 7 -+#define R_SPARC_WDISP22 8 -+#define R_SPARC_HI22 9 -+#define R_SPARC_22 10 -+#define R_SPARC_13 11 -+#define R_SPARC_LO10 12 -+#define R_SPARC_GOT10 13 -+#define R_SPARC_GOT13 14 -+#define R_SPARC_GOT22 15 -+#define R_SPARC_PC10 16 -+#define R_SPARC_PC22 17 -+#define R_SPARC_WPLT30 18 -+#define R_SPARC_COPY 19 -+#define R_SPARC_GLOB_DAT 20 -+#define R_SPARC_JMP_SLOT 21 -+#define R_SPARC_RELATIVE 22 -+#define R_SPARC_UA32 23 -+ -+ -+ -+#define R_SPARC_PLT32 24 -+#define R_SPARC_HIPLT22 25 -+#define R_SPARC_LOPLT10 26 -+#define R_SPARC_PCPLT32 27 -+#define R_SPARC_PCPLT22 28 -+#define R_SPARC_PCPLT10 29 -+#define R_SPARC_10 30 -+#define R_SPARC_11 31 -+#define R_SPARC_64 32 -+#define R_SPARC_OLO10 33 -+#define R_SPARC_HH22 34 -+#define R_SPARC_HM10 35 -+#define R_SPARC_LM22 36 -+#define R_SPARC_PC_HH22 37 -+#define R_SPARC_PC_HM10 38 -+#define R_SPARC_PC_LM22 39 -+#define R_SPARC_WDISP16 40 -+#define R_SPARC_WDISP19 41 -+#define R_SPARC_GLOB_JMP 42 -+#define R_SPARC_7 43 -+#define R_SPARC_5 44 -+#define R_SPARC_6 45 -+#define R_SPARC_DISP64 46 -+#define R_SPARC_PLT64 47 -+#define R_SPARC_HIX22 48 -+#define R_SPARC_LOX10 49 -+#define R_SPARC_H44 50 -+#define R_SPARC_M44 51 -+#define R_SPARC_L44 52 -+#define R_SPARC_REGISTER 53 -+#define R_SPARC_UA64 54 -+#define R_SPARC_UA16 55 -+#define R_SPARC_TLS_GD_HI22 56 -+#define R_SPARC_TLS_GD_LO10 57 -+#define R_SPARC_TLS_GD_ADD 58 -+#define R_SPARC_TLS_GD_CALL 59 -+#define R_SPARC_TLS_LDM_HI22 60 -+#define R_SPARC_TLS_LDM_LO10 61 -+#define R_SPARC_TLS_LDM_ADD 62 -+#define R_SPARC_TLS_LDM_CALL 63 -+#define R_SPARC_TLS_LDO_HIX22 64 -+#define R_SPARC_TLS_LDO_LOX10 65 -+#define R_SPARC_TLS_LDO_ADD 66 -+#define R_SPARC_TLS_IE_HI22 67 -+#define R_SPARC_TLS_IE_LO10 68 -+#define R_SPARC_TLS_IE_LD 69 -+#define R_SPARC_TLS_IE_LDX 70 -+#define R_SPARC_TLS_IE_ADD 71 -+#define R_SPARC_TLS_LE_HIX22 72 -+#define R_SPARC_TLS_LE_LOX10 73 -+#define R_SPARC_TLS_DTPMOD32 74 -+#define R_SPARC_TLS_DTPMOD64 75 -+#define R_SPARC_TLS_DTPOFF32 76 -+#define R_SPARC_TLS_DTPOFF64 77 -+#define R_SPARC_TLS_TPOFF32 78 -+#define R_SPARC_TLS_TPOFF64 79 -+#define R_SPARC_GOTDATA_HIX22 80 -+#define R_SPARC_GOTDATA_LOX10 81 -+#define R_SPARC_GOTDATA_OP_HIX22 82 -+#define R_SPARC_GOTDATA_OP_LOX10 83 -+#define R_SPARC_GOTDATA_OP 84 -+#define R_SPARC_H34 85 -+#define R_SPARC_SIZE32 86 -+#define R_SPARC_SIZE64 87 -+#define R_SPARC_GNU_VTINHERIT 250 -+#define R_SPARC_GNU_VTENTRY 251 -+#define R_SPARC_REV32 252 -+ -+#define R_SPARC_NUM 253 -+ -+ -+ -+#define DT_SPARC_REGISTER 0x70000001 -+#define DT_SPARC_NUM 2 -+ -+ -+#define EF_MIPS_NOREORDER 1 -+#define EF_MIPS_PIC 2 -+#define EF_MIPS_CPIC 4 -+#define EF_MIPS_XGOT 8 -+#define EF_MIPS_64BIT_WHIRL 16 -+#define EF_MIPS_ABI2 32 -+#define EF_MIPS_ABI_ON32 64 -+#define EF_MIPS_ARCH 0xf0000000 -+ -+ -+ -+#define EF_MIPS_ARCH_1 0x00000000 -+#define EF_MIPS_ARCH_2 0x10000000 -+#define EF_MIPS_ARCH_3 0x20000000 -+#define EF_MIPS_ARCH_4 0x30000000 -+#define EF_MIPS_ARCH_5 0x40000000 -+#define EF_MIPS_ARCH_32 0x50000000 -+#define EF_MIPS_ARCH_64 0x60000000 -+#define EF_MIPS_ARCH_32R2 0x70000000 -+#define EF_MIPS_ARCH_64R2 0x80000000 -+ -+ -+#define E_MIPS_ARCH_1 0x00000000 -+#define E_MIPS_ARCH_2 0x10000000 -+#define E_MIPS_ARCH_3 0x20000000 -+#define E_MIPS_ARCH_4 0x30000000 -+#define E_MIPS_ARCH_5 0x40000000 -+#define E_MIPS_ARCH_32 0x50000000 -+#define E_MIPS_ARCH_64 0x60000000 -+ -+ -+ -+#define SHN_MIPS_ACOMMON 0xff00 -+#define SHN_MIPS_TEXT 0xff01 -+#define SHN_MIPS_DATA 0xff02 -+#define SHN_MIPS_SCOMMON 0xff03 -+#define SHN_MIPS_SUNDEFINED 0xff04 -+ -+ -+ -+#define SHT_MIPS_LIBLIST 0x70000000 -+#define SHT_MIPS_MSYM 0x70000001 -+#define SHT_MIPS_CONFLICT 0x70000002 -+#define SHT_MIPS_GPTAB 0x70000003 -+#define SHT_MIPS_UCODE 0x70000004 -+#define SHT_MIPS_DEBUG 0x70000005 -+#define SHT_MIPS_REGINFO 0x70000006 -+#define SHT_MIPS_PACKAGE 0x70000007 -+#define SHT_MIPS_PACKSYM 0x70000008 -+#define SHT_MIPS_RELD 0x70000009 -+#define SHT_MIPS_IFACE 0x7000000b -+#define SHT_MIPS_CONTENT 0x7000000c -+#define SHT_MIPS_OPTIONS 0x7000000d -+#define SHT_MIPS_SHDR 0x70000010 -+#define SHT_MIPS_FDESC 0x70000011 -+#define SHT_MIPS_EXTSYM 0x70000012 -+#define SHT_MIPS_DENSE 0x70000013 -+#define SHT_MIPS_PDESC 0x70000014 -+#define SHT_MIPS_LOCSYM 0x70000015 -+#define SHT_MIPS_AUXSYM 0x70000016 -+#define SHT_MIPS_OPTSYM 0x70000017 -+#define SHT_MIPS_LOCSTR 0x70000018 -+#define SHT_MIPS_LINE 0x70000019 -+#define SHT_MIPS_RFDESC 0x7000001a -+#define SHT_MIPS_DELTASYM 0x7000001b -+#define SHT_MIPS_DELTAINST 0x7000001c -+#define SHT_MIPS_DELTACLASS 0x7000001d -+#define SHT_MIPS_DWARF 0x7000001e -+#define SHT_MIPS_DELTADECL 0x7000001f -+#define SHT_MIPS_SYMBOL_LIB 0x70000020 -+#define SHT_MIPS_EVENTS 0x70000021 -+#define SHT_MIPS_TRANSLATE 0x70000022 -+#define SHT_MIPS_PIXIE 0x70000023 -+#define SHT_MIPS_XLATE 0x70000024 -+#define SHT_MIPS_XLATE_DEBUG 0x70000025 -+#define SHT_MIPS_WHIRL 0x70000026 -+#define SHT_MIPS_EH_REGION 0x70000027 -+#define SHT_MIPS_XLATE_OLD 0x70000028 -+#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -+ -+ -+ -+#define SHF_MIPS_GPREL 0x10000000 -+#define SHF_MIPS_MERGE 0x20000000 -+#define SHF_MIPS_ADDR 0x40000000 -+#define SHF_MIPS_STRINGS 0x80000000 -+#define SHF_MIPS_NOSTRIP 0x08000000 -+#define SHF_MIPS_LOCAL 0x04000000 -+#define SHF_MIPS_NAMES 0x02000000 -+#define SHF_MIPS_NODUPE 0x01000000 -+ -+ -+ -+ -+ -+#define STO_MIPS_DEFAULT 0x0 -+#define STO_MIPS_INTERNAL 0x1 -+#define STO_MIPS_HIDDEN 0x2 -+#define STO_MIPS_PROTECTED 0x3 -+#define STO_MIPS_PLT 0x8 -+#define STO_MIPS_SC_ALIGN_UNUSED 0xff -+ -+ -+#define STB_MIPS_SPLIT_COMMON 13 -+ -+ -+ -+typedef union { -+ struct { -+ Elf32_Word gt_current_g_value; -+ Elf32_Word gt_unused; -+ } gt_header; -+ struct { -+ Elf32_Word gt_g_value; -+ Elf32_Word gt_bytes; -+ } gt_entry; -+} Elf32_gptab; -+ -+ -+ -+typedef struct { -+ Elf32_Word ri_gprmask; -+ Elf32_Word ri_cprmask[4]; -+ Elf32_Sword ri_gp_value; -+} Elf32_RegInfo; -+ -+ -+ -+typedef struct { -+ unsigned char kind; -+ -+ unsigned char size; -+ Elf32_Section section; -+ -+ Elf32_Word info; -+} Elf_Options; -+ -+ -+ -+#define ODK_NULL 0 -+#define ODK_REGINFO 1 -+#define ODK_EXCEPTIONS 2 -+#define ODK_PAD 3 -+#define ODK_HWPATCH 4 -+#define ODK_FILL 5 -+#define ODK_TAGS 6 -+#define ODK_HWAND 7 -+#define ODK_HWOR 8 -+ -+ -+ -+#define OEX_FPU_MIN 0x1f -+#define OEX_FPU_MAX 0x1f00 -+#define OEX_PAGE0 0x10000 -+#define OEX_SMM 0x20000 -+#define OEX_FPDBUG 0x40000 -+#define OEX_PRECISEFP OEX_FPDBUG -+#define OEX_DISMISS 0x80000 -+ -+#define OEX_FPU_INVAL 0x10 -+#define OEX_FPU_DIV0 0x08 -+#define OEX_FPU_OFLO 0x04 -+#define OEX_FPU_UFLO 0x02 -+#define OEX_FPU_INEX 0x01 -+ -+ -+ -+#define OHW_R4KEOP 0x1 -+#define OHW_R8KPFETCH 0x2 -+#define OHW_R5KEOP 0x4 -+#define OHW_R5KCVTL 0x8 -+ -+#define OPAD_PREFIX 0x1 -+#define OPAD_POSTFIX 0x2 -+#define OPAD_SYMBOL 0x4 -+ -+ -+ -+typedef struct { -+ Elf32_Word hwp_flags1; -+ Elf32_Word hwp_flags2; -+} Elf_Options_Hw; -+ -+ -+ -+#define OHWA0_R4KEOP_CHECKED 0x00000001 -+#define OHWA1_R4KEOP_CLEAN 0x00000002 -+ -+ -+ -+#define R_MIPS_NONE 0 -+#define R_MIPS_16 1 -+#define R_MIPS_32 2 -+#define R_MIPS_REL32 3 -+#define R_MIPS_26 4 -+#define R_MIPS_HI16 5 -+#define R_MIPS_LO16 6 -+#define R_MIPS_GPREL16 7 -+#define R_MIPS_LITERAL 8 -+#define R_MIPS_GOT16 9 -+#define R_MIPS_PC16 10 -+#define R_MIPS_CALL16 11 -+#define R_MIPS_GPREL32 12 -+ -+#define R_MIPS_SHIFT5 16 -+#define R_MIPS_SHIFT6 17 -+#define R_MIPS_64 18 -+#define R_MIPS_GOT_DISP 19 -+#define R_MIPS_GOT_PAGE 20 -+#define R_MIPS_GOT_OFST 21 -+#define R_MIPS_GOT_HI16 22 -+#define R_MIPS_GOT_LO16 23 -+#define R_MIPS_SUB 24 -+#define R_MIPS_INSERT_A 25 -+#define R_MIPS_INSERT_B 26 -+#define R_MIPS_DELETE 27 -+#define R_MIPS_HIGHER 28 -+#define R_MIPS_HIGHEST 29 -+#define R_MIPS_CALL_HI16 30 -+#define R_MIPS_CALL_LO16 31 -+#define R_MIPS_SCN_DISP 32 -+#define R_MIPS_REL16 33 -+#define R_MIPS_ADD_IMMEDIATE 34 -+#define R_MIPS_PJUMP 35 -+#define R_MIPS_RELGOT 36 -+#define R_MIPS_JALR 37 -+#define R_MIPS_TLS_DTPMOD32 38 -+#define R_MIPS_TLS_DTPREL32 39 -+#define R_MIPS_TLS_DTPMOD64 40 -+#define R_MIPS_TLS_DTPREL64 41 -+#define R_MIPS_TLS_GD 42 -+#define R_MIPS_TLS_LDM 43 -+#define R_MIPS_TLS_DTPREL_HI16 44 -+#define R_MIPS_TLS_DTPREL_LO16 45 -+#define R_MIPS_TLS_GOTTPREL 46 -+#define R_MIPS_TLS_TPREL32 47 -+#define R_MIPS_TLS_TPREL64 48 -+#define R_MIPS_TLS_TPREL_HI16 49 -+#define R_MIPS_TLS_TPREL_LO16 50 -+#define R_MIPS_GLOB_DAT 51 -+#define R_MIPS_COPY 126 -+#define R_MIPS_JUMP_SLOT 127 -+ -+#define R_MIPS_NUM 128 -+ -+ -+ -+#define PT_MIPS_REGINFO 0x70000000 -+#define PT_MIPS_RTPROC 0x70000001 -+#define PT_MIPS_OPTIONS 0x70000002 -+ -+ -+ -+#define PF_MIPS_LOCAL 0x10000000 -+ -+ -+ -+#define DT_MIPS_RLD_VERSION 0x70000001 -+#define DT_MIPS_TIME_STAMP 0x70000002 -+#define DT_MIPS_ICHECKSUM 0x70000003 -+#define DT_MIPS_IVERSION 0x70000004 -+#define DT_MIPS_FLAGS 0x70000005 -+#define DT_MIPS_BASE_ADDRESS 0x70000006 -+#define DT_MIPS_MSYM 0x70000007 -+#define DT_MIPS_CONFLICT 0x70000008 -+#define DT_MIPS_LIBLIST 0x70000009 -+#define DT_MIPS_LOCAL_GOTNO 0x7000000a -+#define DT_MIPS_CONFLICTNO 0x7000000b -+#define DT_MIPS_LIBLISTNO 0x70000010 -+#define DT_MIPS_SYMTABNO 0x70000011 -+#define DT_MIPS_UNREFEXTNO 0x70000012 -+#define DT_MIPS_GOTSYM 0x70000013 -+#define DT_MIPS_HIPAGENO 0x70000014 -+#define DT_MIPS_RLD_MAP 0x70000016 -+#define DT_MIPS_DELTA_CLASS 0x70000017 -+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 -+ -+#define DT_MIPS_DELTA_INSTANCE 0x70000019 -+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a -+ -+#define DT_MIPS_DELTA_RELOC 0x7000001b -+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c -+ -+#define DT_MIPS_DELTA_SYM 0x7000001d -+ -+#define DT_MIPS_DELTA_SYM_NO 0x7000001e -+ -+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 -+ -+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 -+ -+#define DT_MIPS_CXX_FLAGS 0x70000022 -+#define DT_MIPS_PIXIE_INIT 0x70000023 -+#define DT_MIPS_SYMBOL_LIB 0x70000024 -+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -+#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -+#define DT_MIPS_OPTIONS 0x70000029 -+#define DT_MIPS_INTERFACE 0x7000002a -+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -+#define DT_MIPS_INTERFACE_SIZE 0x7000002c -+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d -+ -+#define DT_MIPS_PERF_SUFFIX 0x7000002e -+ -+#define DT_MIPS_COMPACT_SIZE 0x7000002f -+#define DT_MIPS_GP_VALUE 0x70000030 -+#define DT_MIPS_AUX_DYNAMIC 0x70000031 -+ -+#define DT_MIPS_PLTGOT 0x70000032 -+ -+#define DT_MIPS_RWPLT 0x70000034 -+#define DT_MIPS_NUM 0x35 -+ -+ -+ -+#define RHF_NONE 0 -+#define RHF_QUICKSTART (1 << 0) -+#define RHF_NOTPOT (1 << 1) -+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) -+#define RHF_NO_MOVE (1 << 3) -+#define RHF_SGI_ONLY (1 << 4) -+#define RHF_GUARANTEE_INIT (1 << 5) -+#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -+#define RHF_GUARANTEE_START_INIT (1 << 7) -+#define RHF_PIXIE (1 << 8) -+#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -+#define RHF_REQUICKSTART (1 << 10) -+#define RHF_REQUICKSTARTED (1 << 11) -+#define RHF_CORD (1 << 12) -+#define RHF_NO_UNRES_UNDEF (1 << 13) -+#define RHF_RLD_ORDER_SAFE (1 << 14) -+ -+ -+ -+typedef struct -+{ -+ Elf32_Word l_name; -+ Elf32_Word l_time_stamp; -+ Elf32_Word l_checksum; -+ Elf32_Word l_version; -+ Elf32_Word l_flags; -+} Elf32_Lib; -+ -+typedef struct -+{ -+ Elf64_Word l_name; -+ Elf64_Word l_time_stamp; -+ Elf64_Word l_checksum; -+ Elf64_Word l_version; -+ Elf64_Word l_flags; -+} Elf64_Lib; -+ -+ -+ -+ -+#define LL_NONE 0 -+#define LL_EXACT_MATCH (1 << 0) -+#define LL_IGNORE_INT_VER (1 << 1) -+#define LL_REQUIRE_MINOR (1 << 2) -+#define LL_EXPORTS (1 << 3) -+#define LL_DELAY_LOAD (1 << 4) -+#define LL_DELTA (1 << 5) -+ -+ -+ -+typedef Elf32_Addr Elf32_Conflict; -+ -+ -+ -+ -+ -+ -+#define EF_PARISC_TRAPNIL 0x00010000 -+#define EF_PARISC_EXT 0x00020000 -+#define EF_PARISC_LSB 0x00040000 -+#define EF_PARISC_WIDE 0x00080000 -+#define EF_PARISC_NO_KABP 0x00100000 -+ -+#define EF_PARISC_LAZYSWAP 0x00400000 -+#define EF_PARISC_ARCH 0x0000ffff -+ -+ -+ -+#define EFA_PARISC_1_0 0x020b -+#define EFA_PARISC_1_1 0x0210 -+#define EFA_PARISC_2_0 0x0214 -+ -+ -+ -+#define SHN_PARISC_ANSI_COMMON 0xff00 -+ -+#define SHN_PARISC_HUGE_COMMON 0xff01 -+ -+ -+ -+#define SHT_PARISC_EXT 0x70000000 -+#define SHT_PARISC_UNWIND 0x70000001 -+#define SHT_PARISC_DOC 0x70000002 -+ -+ -+ -+#define SHF_PARISC_SHORT 0x20000000 -+#define SHF_PARISC_HUGE 0x40000000 -+#define SHF_PARISC_SBP 0x80000000 -+ -+ -+ -+#define STT_PARISC_MILLICODE 13 -+ -+#define STT_HP_OPAQUE (STT_LOOS + 0x1) -+#define STT_HP_STUB (STT_LOOS + 0x2) -+ -+ -+ -+#define R_PARISC_NONE 0 -+#define R_PARISC_DIR32 1 -+#define R_PARISC_DIR21L 2 -+#define R_PARISC_DIR17R 3 -+#define R_PARISC_DIR17F 4 -+#define R_PARISC_DIR14R 6 -+#define R_PARISC_PCREL32 9 -+#define R_PARISC_PCREL21L 10 -+#define R_PARISC_PCREL17R 11 -+#define R_PARISC_PCREL17F 12 -+#define R_PARISC_PCREL14R 14 -+#define R_PARISC_DPREL21L 18 -+#define R_PARISC_DPREL14R 22 -+#define R_PARISC_GPREL21L 26 -+#define R_PARISC_GPREL14R 30 -+#define R_PARISC_LTOFF21L 34 -+#define R_PARISC_LTOFF14R 38 -+#define R_PARISC_SECREL32 41 -+#define R_PARISC_SEGBASE 48 -+#define R_PARISC_SEGREL32 49 -+#define R_PARISC_PLTOFF21L 50 -+#define R_PARISC_PLTOFF14R 54 -+#define R_PARISC_LTOFF_FPTR32 57 -+#define R_PARISC_LTOFF_FPTR21L 58 -+#define R_PARISC_LTOFF_FPTR14R 62 -+#define R_PARISC_FPTR64 64 -+#define R_PARISC_PLABEL32 65 -+#define R_PARISC_PLABEL21L 66 -+#define R_PARISC_PLABEL14R 70 -+#define R_PARISC_PCREL64 72 -+#define R_PARISC_PCREL22F 74 -+#define R_PARISC_PCREL14WR 75 -+#define R_PARISC_PCREL14DR 76 -+#define R_PARISC_PCREL16F 77 -+#define R_PARISC_PCREL16WF 78 -+#define R_PARISC_PCREL16DF 79 -+#define R_PARISC_DIR64 80 -+#define R_PARISC_DIR14WR 83 -+#define R_PARISC_DIR14DR 84 -+#define R_PARISC_DIR16F 85 -+#define R_PARISC_DIR16WF 86 -+#define R_PARISC_DIR16DF 87 -+#define R_PARISC_GPREL64 88 -+#define R_PARISC_GPREL14WR 91 -+#define R_PARISC_GPREL14DR 92 -+#define R_PARISC_GPREL16F 93 -+#define R_PARISC_GPREL16WF 94 -+#define R_PARISC_GPREL16DF 95 -+#define R_PARISC_LTOFF64 96 -+#define R_PARISC_LTOFF14WR 99 -+#define R_PARISC_LTOFF14DR 100 -+#define R_PARISC_LTOFF16F 101 -+#define R_PARISC_LTOFF16WF 102 -+#define R_PARISC_LTOFF16DF 103 -+#define R_PARISC_SECREL64 104 -+#define R_PARISC_SEGREL64 112 -+#define R_PARISC_PLTOFF14WR 115 -+#define R_PARISC_PLTOFF14DR 116 -+#define R_PARISC_PLTOFF16F 117 -+#define R_PARISC_PLTOFF16WF 118 -+#define R_PARISC_PLTOFF16DF 119 -+#define R_PARISC_LTOFF_FPTR64 120 -+#define R_PARISC_LTOFF_FPTR14WR 123 -+#define R_PARISC_LTOFF_FPTR14DR 124 -+#define R_PARISC_LTOFF_FPTR16F 125 -+#define R_PARISC_LTOFF_FPTR16WF 126 -+#define R_PARISC_LTOFF_FPTR16DF 127 -+#define R_PARISC_LORESERVE 128 -+#define R_PARISC_COPY 128 -+#define R_PARISC_IPLT 129 -+#define R_PARISC_EPLT 130 -+#define R_PARISC_TPREL32 153 -+#define R_PARISC_TPREL21L 154 -+#define R_PARISC_TPREL14R 158 -+#define R_PARISC_LTOFF_TP21L 162 -+#define R_PARISC_LTOFF_TP14R 166 -+#define R_PARISC_LTOFF_TP14F 167 -+#define R_PARISC_TPREL64 216 -+#define R_PARISC_TPREL14WR 219 -+#define R_PARISC_TPREL14DR 220 -+#define R_PARISC_TPREL16F 221 -+#define R_PARISC_TPREL16WF 222 -+#define R_PARISC_TPREL16DF 223 -+#define R_PARISC_LTOFF_TP64 224 -+#define R_PARISC_LTOFF_TP14WR 227 -+#define R_PARISC_LTOFF_TP14DR 228 -+#define R_PARISC_LTOFF_TP16F 229 -+#define R_PARISC_LTOFF_TP16WF 230 -+#define R_PARISC_LTOFF_TP16DF 231 -+#define R_PARISC_GNU_VTENTRY 232 -+#define R_PARISC_GNU_VTINHERIT 233 -+#define R_PARISC_TLS_GD21L 234 -+#define R_PARISC_TLS_GD14R 235 -+#define R_PARISC_TLS_GDCALL 236 -+#define R_PARISC_TLS_LDM21L 237 -+#define R_PARISC_TLS_LDM14R 238 -+#define R_PARISC_TLS_LDMCALL 239 -+#define R_PARISC_TLS_LDO21L 240 -+#define R_PARISC_TLS_LDO14R 241 -+#define R_PARISC_TLS_DTPMOD32 242 -+#define R_PARISC_TLS_DTPMOD64 243 -+#define R_PARISC_TLS_DTPOFF32 244 -+#define R_PARISC_TLS_DTPOFF64 245 -+#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L -+#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R -+#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L -+#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R -+#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 -+#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 -+#define R_PARISC_HIRESERVE 255 -+ -+ -+ -+#define PT_HP_TLS (PT_LOOS + 0x0) -+#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -+#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -+#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -+#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -+#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -+#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -+#define PT_HP_PARALLEL (PT_LOOS + 0x10) -+#define PT_HP_FASTBIND (PT_LOOS + 0x11) -+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -+#define PT_HP_STACK (PT_LOOS + 0x14) -+ -+#define PT_PARISC_ARCHEXT 0x70000000 -+#define PT_PARISC_UNWIND 0x70000001 -+ -+ -+ -+#define PF_PARISC_SBP 0x08000000 -+ -+#define PF_HP_PAGE_SIZE 0x00100000 -+#define PF_HP_FAR_SHARED 0x00200000 -+#define PF_HP_NEAR_SHARED 0x00400000 -+#define PF_HP_CODE 0x01000000 -+#define PF_HP_MODIFY 0x02000000 -+#define PF_HP_LAZYSWAP 0x04000000 -+#define PF_HP_SBP 0x08000000 -+ -+ -+ -+ -+ -+ -+#define EF_ALPHA_32BIT 1 -+#define EF_ALPHA_CANRELAX 2 -+ -+ -+ -+ -+#define SHT_ALPHA_DEBUG 0x70000001 -+#define SHT_ALPHA_REGINFO 0x70000002 -+ -+ -+ -+#define SHF_ALPHA_GPREL 0x10000000 -+ -+ -+#define STO_ALPHA_NOPV 0x80 -+#define STO_ALPHA_STD_GPLOAD 0x88 -+ -+ -+ -+#define R_ALPHA_NONE 0 -+#define R_ALPHA_REFLONG 1 -+#define R_ALPHA_REFQUAD 2 -+#define R_ALPHA_GPREL32 3 -+#define R_ALPHA_LITERAL 4 -+#define R_ALPHA_LITUSE 5 -+#define R_ALPHA_GPDISP 6 -+#define R_ALPHA_BRADDR 7 -+#define R_ALPHA_HINT 8 -+#define R_ALPHA_SREL16 9 -+#define R_ALPHA_SREL32 10 -+#define R_ALPHA_SREL64 11 -+#define R_ALPHA_GPRELHIGH 17 -+#define R_ALPHA_GPRELLOW 18 -+#define R_ALPHA_GPREL16 19 -+#define R_ALPHA_COPY 24 -+#define R_ALPHA_GLOB_DAT 25 -+#define R_ALPHA_JMP_SLOT 26 -+#define R_ALPHA_RELATIVE 27 -+#define R_ALPHA_TLS_GD_HI 28 -+#define R_ALPHA_TLSGD 29 -+#define R_ALPHA_TLS_LDM 30 -+#define R_ALPHA_DTPMOD64 31 -+#define R_ALPHA_GOTDTPREL 32 -+#define R_ALPHA_DTPREL64 33 -+#define R_ALPHA_DTPRELHI 34 -+#define R_ALPHA_DTPRELLO 35 -+#define R_ALPHA_DTPREL16 36 -+#define R_ALPHA_GOTTPREL 37 -+#define R_ALPHA_TPREL64 38 -+#define R_ALPHA_TPRELHI 39 -+#define R_ALPHA_TPRELLO 40 -+#define R_ALPHA_TPREL16 41 -+ -+#define R_ALPHA_NUM 46 -+ -+ -+#define LITUSE_ALPHA_ADDR 0 -+#define LITUSE_ALPHA_BASE 1 -+#define LITUSE_ALPHA_BYTOFF 2 -+#define LITUSE_ALPHA_JSR 3 -+#define LITUSE_ALPHA_TLS_GD 4 -+#define LITUSE_ALPHA_TLS_LDM 5 -+ -+ -+#define DT_ALPHA_PLTRO (DT_LOPROC + 0) -+#define DT_ALPHA_NUM 1 -+ -+ -+ -+ -+#define EF_PPC_EMB 0x80000000 -+ -+ -+#define EF_PPC_RELOCATABLE 0x00010000 -+#define EF_PPC_RELOCATABLE_LIB 0x00008000 -+ -+ -+ -+#define R_PPC_NONE 0 -+#define R_PPC_ADDR32 1 -+#define R_PPC_ADDR24 2 -+#define R_PPC_ADDR16 3 -+#define R_PPC_ADDR16_LO 4 -+#define R_PPC_ADDR16_HI 5 -+#define R_PPC_ADDR16_HA 6 -+#define R_PPC_ADDR14 7 -+#define R_PPC_ADDR14_BRTAKEN 8 -+#define R_PPC_ADDR14_BRNTAKEN 9 -+#define R_PPC_REL24 10 -+#define R_PPC_REL14 11 -+#define R_PPC_REL14_BRTAKEN 12 -+#define R_PPC_REL14_BRNTAKEN 13 -+#define R_PPC_GOT16 14 -+#define R_PPC_GOT16_LO 15 -+#define R_PPC_GOT16_HI 16 -+#define R_PPC_GOT16_HA 17 -+#define R_PPC_PLTREL24 18 -+#define R_PPC_COPY 19 -+#define R_PPC_GLOB_DAT 20 -+#define R_PPC_JMP_SLOT 21 -+#define R_PPC_RELATIVE 22 -+#define R_PPC_LOCAL24PC 23 -+#define R_PPC_UADDR32 24 -+#define R_PPC_UADDR16 25 -+#define R_PPC_REL32 26 -+#define R_PPC_PLT32 27 -+#define R_PPC_PLTREL32 28 -+#define R_PPC_PLT16_LO 29 -+#define R_PPC_PLT16_HI 30 -+#define R_PPC_PLT16_HA 31 -+#define R_PPC_SDAREL16 32 -+#define R_PPC_SECTOFF 33 -+#define R_PPC_SECTOFF_LO 34 -+#define R_PPC_SECTOFF_HI 35 -+#define R_PPC_SECTOFF_HA 36 -+ -+ -+#define R_PPC_TLS 67 -+#define R_PPC_DTPMOD32 68 -+#define R_PPC_TPREL16 69 -+#define R_PPC_TPREL16_LO 70 -+#define R_PPC_TPREL16_HI 71 -+#define R_PPC_TPREL16_HA 72 -+#define R_PPC_TPREL32 73 -+#define R_PPC_DTPREL16 74 -+#define R_PPC_DTPREL16_LO 75 -+#define R_PPC_DTPREL16_HI 76 -+#define R_PPC_DTPREL16_HA 77 -+#define R_PPC_DTPREL32 78 -+#define R_PPC_GOT_TLSGD16 79 -+#define R_PPC_GOT_TLSGD16_LO 80 -+#define R_PPC_GOT_TLSGD16_HI 81 -+#define R_PPC_GOT_TLSGD16_HA 82 -+#define R_PPC_GOT_TLSLD16 83 -+#define R_PPC_GOT_TLSLD16_LO 84 -+#define R_PPC_GOT_TLSLD16_HI 85 -+#define R_PPC_GOT_TLSLD16_HA 86 -+#define R_PPC_GOT_TPREL16 87 -+#define R_PPC_GOT_TPREL16_LO 88 -+#define R_PPC_GOT_TPREL16_HI 89 -+#define R_PPC_GOT_TPREL16_HA 90 -+#define R_PPC_GOT_DTPREL16 91 -+#define R_PPC_GOT_DTPREL16_LO 92 -+#define R_PPC_GOT_DTPREL16_HI 93 -+#define R_PPC_GOT_DTPREL16_HA 94 -+ -+ -+ -+#define R_PPC_EMB_NADDR32 101 -+#define R_PPC_EMB_NADDR16 102 -+#define R_PPC_EMB_NADDR16_LO 103 -+#define R_PPC_EMB_NADDR16_HI 104 -+#define R_PPC_EMB_NADDR16_HA 105 -+#define R_PPC_EMB_SDAI16 106 -+#define R_PPC_EMB_SDA2I16 107 -+#define R_PPC_EMB_SDA2REL 108 -+#define R_PPC_EMB_SDA21 109 -+#define R_PPC_EMB_MRKREF 110 -+#define R_PPC_EMB_RELSEC16 111 -+#define R_PPC_EMB_RELST_LO 112 -+#define R_PPC_EMB_RELST_HI 113 -+#define R_PPC_EMB_RELST_HA 114 -+#define R_PPC_EMB_BIT_FLD 115 -+#define R_PPC_EMB_RELSDA 116 -+ -+ -+#define R_PPC_DIAB_SDA21_LO 180 -+#define R_PPC_DIAB_SDA21_HI 181 -+#define R_PPC_DIAB_SDA21_HA 182 -+#define R_PPC_DIAB_RELSDA_LO 183 -+#define R_PPC_DIAB_RELSDA_HI 184 -+#define R_PPC_DIAB_RELSDA_HA 185 -+ -+ -+#define R_PPC_IRELATIVE 248 -+ -+ -+#define R_PPC_REL16 249 -+#define R_PPC_REL16_LO 250 -+#define R_PPC_REL16_HI 251 -+#define R_PPC_REL16_HA 252 -+ -+ -+ -+#define R_PPC_TOC16 255 -+ -+ -+#define DT_PPC_GOT (DT_LOPROC + 0) -+#define DT_PPC_NUM 1 -+ -+ -+#define R_PPC64_NONE R_PPC_NONE -+#define R_PPC64_ADDR32 R_PPC_ADDR32 -+#define R_PPC64_ADDR24 R_PPC_ADDR24 -+#define R_PPC64_ADDR16 R_PPC_ADDR16 -+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO -+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI -+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA -+#define R_PPC64_ADDR14 R_PPC_ADDR14 -+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -+#define R_PPC64_REL24 R_PPC_REL24 -+#define R_PPC64_REL14 R_PPC_REL14 -+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -+#define R_PPC64_GOT16 R_PPC_GOT16 -+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA -+ -+#define R_PPC64_COPY R_PPC_COPY -+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -+#define R_PPC64_RELATIVE R_PPC_RELATIVE -+ -+#define R_PPC64_UADDR32 R_PPC_UADDR32 -+#define R_PPC64_UADDR16 R_PPC_UADDR16 -+#define R_PPC64_REL32 R_PPC_REL32 -+#define R_PPC64_PLT32 R_PPC_PLT32 -+#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA -+ -+#define R_PPC64_SECTOFF R_PPC_SECTOFF -+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -+#define R_PPC64_ADDR30 37 -+#define R_PPC64_ADDR64 38 -+#define R_PPC64_ADDR16_HIGHER 39 -+#define R_PPC64_ADDR16_HIGHERA 40 -+#define R_PPC64_ADDR16_HIGHEST 41 -+#define R_PPC64_ADDR16_HIGHESTA 42 -+#define R_PPC64_UADDR64 43 -+#define R_PPC64_REL64 44 -+#define R_PPC64_PLT64 45 -+#define R_PPC64_PLTREL64 46 -+#define R_PPC64_TOC16 47 -+#define R_PPC64_TOC16_LO 48 -+#define R_PPC64_TOC16_HI 49 -+#define R_PPC64_TOC16_HA 50 -+#define R_PPC64_TOC 51 -+#define R_PPC64_PLTGOT16 52 -+#define R_PPC64_PLTGOT16_LO 53 -+#define R_PPC64_PLTGOT16_HI 54 -+#define R_PPC64_PLTGOT16_HA 55 -+ -+#define R_PPC64_ADDR16_DS 56 -+#define R_PPC64_ADDR16_LO_DS 57 -+#define R_PPC64_GOT16_DS 58 -+#define R_PPC64_GOT16_LO_DS 59 -+#define R_PPC64_PLT16_LO_DS 60 -+#define R_PPC64_SECTOFF_DS 61 -+#define R_PPC64_SECTOFF_LO_DS 62 -+#define R_PPC64_TOC16_DS 63 -+#define R_PPC64_TOC16_LO_DS 64 -+#define R_PPC64_PLTGOT16_DS 65 -+#define R_PPC64_PLTGOT16_LO_DS 66 -+ -+ -+#define R_PPC64_TLS 67 -+#define R_PPC64_DTPMOD64 68 -+#define R_PPC64_TPREL16 69 -+#define R_PPC64_TPREL16_LO 70 -+#define R_PPC64_TPREL16_HI 71 -+#define R_PPC64_TPREL16_HA 72 -+#define R_PPC64_TPREL64 73 -+#define R_PPC64_DTPREL16 74 -+#define R_PPC64_DTPREL16_LO 75 -+#define R_PPC64_DTPREL16_HI 76 -+#define R_PPC64_DTPREL16_HA 77 -+#define R_PPC64_DTPREL64 78 -+#define R_PPC64_GOT_TLSGD16 79 -+#define R_PPC64_GOT_TLSGD16_LO 80 -+#define R_PPC64_GOT_TLSGD16_HI 81 -+#define R_PPC64_GOT_TLSGD16_HA 82 -+#define R_PPC64_GOT_TLSLD16 83 -+#define R_PPC64_GOT_TLSLD16_LO 84 -+#define R_PPC64_GOT_TLSLD16_HI 85 -+#define R_PPC64_GOT_TLSLD16_HA 86 -+#define R_PPC64_GOT_TPREL16_DS 87 -+#define R_PPC64_GOT_TPREL16_LO_DS 88 -+#define R_PPC64_GOT_TPREL16_HI 89 -+#define R_PPC64_GOT_TPREL16_HA 90 -+#define R_PPC64_GOT_DTPREL16_DS 91 -+#define R_PPC64_GOT_DTPREL16_LO_DS 92 -+#define R_PPC64_GOT_DTPREL16_HI 93 -+#define R_PPC64_GOT_DTPREL16_HA 94 -+#define R_PPC64_TPREL16_DS 95 -+#define R_PPC64_TPREL16_LO_DS 96 -+#define R_PPC64_TPREL16_HIGHER 97 -+#define R_PPC64_TPREL16_HIGHERA 98 -+#define R_PPC64_TPREL16_HIGHEST 99 -+#define R_PPC64_TPREL16_HIGHESTA 100 -+#define R_PPC64_DTPREL16_DS 101 -+#define R_PPC64_DTPREL16_LO_DS 102 -+#define R_PPC64_DTPREL16_HIGHER 103 -+#define R_PPC64_DTPREL16_HIGHERA 104 -+#define R_PPC64_DTPREL16_HIGHEST 105 -+#define R_PPC64_DTPREL16_HIGHESTA 106 -+ -+ -+#define R_PPC64_JMP_IREL 247 -+#define R_PPC64_IRELATIVE 248 -+#define R_PPC64_REL16 249 -+#define R_PPC64_REL16_LO 250 -+#define R_PPC64_REL16_HI 251 -+#define R_PPC64_REL16_HA 252 -+ -+ -+#define DT_PPC64_GLINK (DT_LOPROC + 0) -+#define DT_PPC64_OPD (DT_LOPROC + 1) -+#define DT_PPC64_OPDSZ (DT_LOPROC + 2) -+#define DT_PPC64_NUM 3 -+ -+ -+ -+ -+ -+#define EF_ARM_RELEXEC 0x01 -+#define EF_ARM_HASENTRY 0x02 -+#define EF_ARM_INTERWORK 0x04 -+#define EF_ARM_APCS_26 0x08 -+#define EF_ARM_APCS_FLOAT 0x10 -+#define EF_ARM_PIC 0x20 -+#define EF_ARM_ALIGN8 0x40 -+#define EF_ARM_NEW_ABI 0x80 -+#define EF_ARM_OLD_ABI 0x100 -+#define EF_ARM_SOFT_FLOAT 0x200 -+#define EF_ARM_VFP_FLOAT 0x400 -+#define EF_ARM_MAVERICK_FLOAT 0x800 -+ -+#define EF_ARM_ABI_FLOAT_SOFT 0x200 -+#define EF_ARM_ABI_FLOAT_HARD 0x400 -+ -+ -+#define EF_ARM_SYMSARESORTED 0x04 -+#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -+#define EF_ARM_MAPSYMSFIRST 0x10 -+#define EF_ARM_EABIMASK 0XFF000000 -+ -+ -+#define EF_ARM_BE8 0x00800000 -+#define EF_ARM_LE8 0x00400000 -+ -+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -+#define EF_ARM_EABI_UNKNOWN 0x00000000 -+#define EF_ARM_EABI_VER1 0x01000000 -+#define EF_ARM_EABI_VER2 0x02000000 -+#define EF_ARM_EABI_VER3 0x03000000 -+#define EF_ARM_EABI_VER4 0x04000000 -+#define EF_ARM_EABI_VER5 0x05000000 -+ -+ -+#define STT_ARM_TFUNC STT_LOPROC -+#define STT_ARM_16BIT STT_HIPROC -+ -+ -+#define SHF_ARM_ENTRYSECT 0x10000000 -+#define SHF_ARM_COMDEF 0x80000000 -+ -+ -+ -+#define PF_ARM_SB 0x10000000 -+ -+#define PF_ARM_PI 0x20000000 -+#define PF_ARM_ABS 0x40000000 -+ -+ -+#define PT_ARM_EXIDX (PT_LOPROC + 1) -+ -+ -+#define SHT_ARM_EXIDX (SHT_LOPROC + 1) -+#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) -+#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) -+ -+ -+#define R_AARCH64_NONE 0 -+#define R_AARCH64_ABS64 257 -+#define R_AARCH64_ABS32 258 -+#define R_AARCH64_COPY 1024 -+#define R_AARCH64_GLOB_DAT 1025 -+#define R_AARCH64_JUMP_SLOT 1026 -+#define R_AARCH64_RELATIVE 1027 -+#define R_AARCH64_TLS_DTPMOD64 1028 -+#define R_AARCH64_TLS_DTPREL64 1029 -+#define R_AARCH64_TLS_TPREL64 1030 -+#define R_AARCH64_TLSDESC 1031 -+ -+ -+#define R_ARM_NONE 0 -+#define R_ARM_PC24 1 -+#define R_ARM_ABS32 2 -+#define R_ARM_REL32 3 -+#define R_ARM_PC13 4 -+#define R_ARM_ABS16 5 -+#define R_ARM_ABS12 6 -+#define R_ARM_THM_ABS5 7 -+#define R_ARM_ABS8 8 -+#define R_ARM_SBREL32 9 -+#define R_ARM_THM_PC22 10 -+#define R_ARM_THM_PC8 11 -+#define R_ARM_AMP_VCALL9 12 -+#define R_ARM_TLS_DESC 13 -+#define R_ARM_THM_SWI8 14 -+#define R_ARM_XPC25 15 -+#define R_ARM_THM_XPC22 16 -+#define R_ARM_TLS_DTPMOD32 17 -+#define R_ARM_TLS_DTPOFF32 18 -+#define R_ARM_TLS_TPOFF32 19 -+#define R_ARM_COPY 20 -+#define R_ARM_GLOB_DAT 21 -+#define R_ARM_JUMP_SLOT 22 -+#define R_ARM_RELATIVE 23 -+#define R_ARM_GOTOFF 24 -+#define R_ARM_GOTPC 25 -+#define R_ARM_GOT32 26 -+#define R_ARM_PLT32 27 -+#define R_ARM_CALL 28 -+#define R_ARM_JUMP24 29 -+#define R_ARM_THM_JUMP24 30 -+#define R_ARM_BASE_ABS 31 -+#define R_ARM_ALU_PCREL_7_0 32 -+#define R_ARM_ALU_PCREL_15_8 33 -+#define R_ARM_ALU_PCREL_23_15 34 -+#define R_ARM_LDR_SBREL_11_0 35 -+#define R_ARM_ALU_SBREL_19_12 36 -+#define R_ARM_ALU_SBREL_27_20 37 -+#define R_ARM_TARGET1 38 -+#define R_ARM_SBREL31 39 -+#define R_ARM_V4BX 40 -+#define R_ARM_TARGET2 41 -+#define R_ARM_PREL31 42 -+#define R_ARM_MOVW_ABS_NC 43 -+#define R_ARM_MOVT_ABS 44 -+#define R_ARM_MOVW_PREL_NC 45 -+#define R_ARM_MOVT_PREL 46 -+#define R_ARM_THM_MOVW_ABS_NC 47 -+#define R_ARM_THM_MOVT_ABS 48 -+#define R_ARM_THM_MOVW_PREL_NC 49 -+#define R_ARM_THM_MOVT_PREL 50 -+#define R_ARM_THM_JUMP19 51 -+#define R_ARM_THM_JUMP6 52 -+#define R_ARM_THM_ALU_PREL_11_0 53 -+#define R_ARM_THM_PC12 54 -+#define R_ARM_ABS32_NOI 55 -+#define R_ARM_REL32_NOI 56 -+#define R_ARM_ALU_PC_G0_NC 57 -+#define R_ARM_ALU_PC_G0 58 -+#define R_ARM_ALU_PC_G1_NC 59 -+#define R_ARM_ALU_PC_G1 60 -+#define R_ARM_ALU_PC_G2 61 -+#define R_ARM_LDR_PC_G1 62 -+#define R_ARM_LDR_PC_G2 63 -+#define R_ARM_LDRS_PC_G0 64 -+#define R_ARM_LDRS_PC_G1 65 -+#define R_ARM_LDRS_PC_G2 66 -+#define R_ARM_LDC_PC_G0 67 -+#define R_ARM_LDC_PC_G1 68 -+#define R_ARM_LDC_PC_G2 69 -+#define R_ARM_ALU_SB_G0_NC 70 -+#define R_ARM_ALU_SB_G0 71 -+#define R_ARM_ALU_SB_G1_NC 72 -+#define R_ARM_ALU_SB_G1 73 -+#define R_ARM_ALU_SB_G2 74 -+#define R_ARM_LDR_SB_G0 75 -+#define R_ARM_LDR_SB_G1 76 -+#define R_ARM_LDR_SB_G2 77 -+#define R_ARM_LDRS_SB_G0 78 -+#define R_ARM_LDRS_SB_G1 79 -+#define R_ARM_LDRS_SB_G2 80 -+#define R_ARM_LDC_SB_G0 81 -+#define R_ARM_LDC_SB_G1 82 -+#define R_ARM_LDC_SB_G2 83 -+#define R_ARM_MOVW_BREL_NC 84 -+#define R_ARM_MOVT_BREL 85 -+#define R_ARM_MOVW_BREL 86 -+#define R_ARM_THM_MOVW_BREL_NC 87 -+#define R_ARM_THM_MOVT_BREL 88 -+#define R_ARM_THM_MOVW_BREL 89 -+#define R_ARM_TLS_GOTDESC 90 -+#define R_ARM_TLS_CALL 91 -+#define R_ARM_TLS_DESCSEQ 92 -+#define R_ARM_THM_TLS_CALL 93 -+#define R_ARM_PLT32_ABS 94 -+#define R_ARM_GOT_ABS 95 -+#define R_ARM_GOT_PREL 96 -+#define R_ARM_GOT_BREL12 97 -+#define R_ARM_GOTOFF12 98 -+#define R_ARM_GOTRELAX 99 -+#define R_ARM_GNU_VTENTRY 100 -+#define R_ARM_GNU_VTINHERIT 101 -+#define R_ARM_THM_PC11 102 -+#define R_ARM_THM_PC9 103 -+#define R_ARM_TLS_GD32 104 -+ -+#define R_ARM_TLS_LDM32 105 -+ -+#define R_ARM_TLS_LDO32 106 -+ -+#define R_ARM_TLS_IE32 107 -+ -+#define R_ARM_TLS_LE32 108 -+#define R_ARM_TLS_LDO12 109 -+#define R_ARM_TLS_LE12 110 -+#define R_ARM_TLS_IE12GP 111 -+#define R_ARM_ME_TOO 128 -+#define R_ARM_THM_TLS_DESCSEQ 129 -+#define R_ARM_THM_TLS_DESCSEQ16 129 -+#define R_ARM_THM_TLS_DESCSEQ32 130 -+#define R_ARM_THM_GOT_BREL12 131 -+#define R_ARM_IRELATIVE 160 -+#define R_ARM_RXPC25 249 -+#define R_ARM_RSBREL32 250 -+#define R_ARM_THM_RPC22 251 -+#define R_ARM_RREL32 252 -+#define R_ARM_RABS22 253 -+#define R_ARM_RPC24 254 -+#define R_ARM_RBASE 255 -+ -+#define R_ARM_NUM 256 -+ -+ -+ -+ -+#define EF_IA_64_MASKOS 0x0000000f -+#define EF_IA_64_ABI64 0x00000010 -+#define EF_IA_64_ARCH 0xff000000 -+ -+ -+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) -+#define PT_IA_64_UNWIND (PT_LOPROC + 1) -+#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) -+#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) -+#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) -+ -+ -+#define PF_IA_64_NORECOV 0x80000000 -+ -+ -+#define SHT_IA_64_EXT (SHT_LOPROC + 0) -+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) -+ -+ -+#define SHF_IA_64_SHORT 0x10000000 -+#define SHF_IA_64_NORECOV 0x20000000 -+ -+ -+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -+#define DT_IA_64_NUM 1 -+ -+ -+#define R_IA64_NONE 0x00 -+#define R_IA64_IMM14 0x21 -+#define R_IA64_IMM22 0x22 -+#define R_IA64_IMM64 0x23 -+#define R_IA64_DIR32MSB 0x24 -+#define R_IA64_DIR32LSB 0x25 -+#define R_IA64_DIR64MSB 0x26 -+#define R_IA64_DIR64LSB 0x27 -+#define R_IA64_GPREL22 0x2a -+#define R_IA64_GPREL64I 0x2b -+#define R_IA64_GPREL32MSB 0x2c -+#define R_IA64_GPREL32LSB 0x2d -+#define R_IA64_GPREL64MSB 0x2e -+#define R_IA64_GPREL64LSB 0x2f -+#define R_IA64_LTOFF22 0x32 -+#define R_IA64_LTOFF64I 0x33 -+#define R_IA64_PLTOFF22 0x3a -+#define R_IA64_PLTOFF64I 0x3b -+#define R_IA64_PLTOFF64MSB 0x3e -+#define R_IA64_PLTOFF64LSB 0x3f -+#define R_IA64_FPTR64I 0x43 -+#define R_IA64_FPTR32MSB 0x44 -+#define R_IA64_FPTR32LSB 0x45 -+#define R_IA64_FPTR64MSB 0x46 -+#define R_IA64_FPTR64LSB 0x47 -+#define R_IA64_PCREL60B 0x48 -+#define R_IA64_PCREL21B 0x49 -+#define R_IA64_PCREL21M 0x4a -+#define R_IA64_PCREL21F 0x4b -+#define R_IA64_PCREL32MSB 0x4c -+#define R_IA64_PCREL32LSB 0x4d -+#define R_IA64_PCREL64MSB 0x4e -+#define R_IA64_PCREL64LSB 0x4f -+#define R_IA64_LTOFF_FPTR22 0x52 -+#define R_IA64_LTOFF_FPTR64I 0x53 -+#define R_IA64_LTOFF_FPTR32MSB 0x54 -+#define R_IA64_LTOFF_FPTR32LSB 0x55 -+#define R_IA64_LTOFF_FPTR64MSB 0x56 -+#define R_IA64_LTOFF_FPTR64LSB 0x57 -+#define R_IA64_SEGREL32MSB 0x5c -+#define R_IA64_SEGREL32LSB 0x5d -+#define R_IA64_SEGREL64MSB 0x5e -+#define R_IA64_SEGREL64LSB 0x5f -+#define R_IA64_SECREL32MSB 0x64 -+#define R_IA64_SECREL32LSB 0x65 -+#define R_IA64_SECREL64MSB 0x66 -+#define R_IA64_SECREL64LSB 0x67 -+#define R_IA64_REL32MSB 0x6c -+#define R_IA64_REL32LSB 0x6d -+#define R_IA64_REL64MSB 0x6e -+#define R_IA64_REL64LSB 0x6f -+#define R_IA64_LTV32MSB 0x74 -+#define R_IA64_LTV32LSB 0x75 -+#define R_IA64_LTV64MSB 0x76 -+#define R_IA64_LTV64LSB 0x77 -+#define R_IA64_PCREL21BI 0x79 -+#define R_IA64_PCREL22 0x7a -+#define R_IA64_PCREL64I 0x7b -+#define R_IA64_IPLTMSB 0x80 -+#define R_IA64_IPLTLSB 0x81 -+#define R_IA64_COPY 0x84 -+#define R_IA64_SUB 0x85 -+#define R_IA64_LTOFF22X 0x86 -+#define R_IA64_LDXMOV 0x87 -+#define R_IA64_TPREL14 0x91 -+#define R_IA64_TPREL22 0x92 -+#define R_IA64_TPREL64I 0x93 -+#define R_IA64_TPREL64MSB 0x96 -+#define R_IA64_TPREL64LSB 0x97 -+#define R_IA64_LTOFF_TPREL22 0x9a -+#define R_IA64_DTPMOD64MSB 0xa6 -+#define R_IA64_DTPMOD64LSB 0xa7 -+#define R_IA64_LTOFF_DTPMOD22 0xaa -+#define R_IA64_DTPREL14 0xb1 -+#define R_IA64_DTPREL22 0xb2 -+#define R_IA64_DTPREL64I 0xb3 -+#define R_IA64_DTPREL32MSB 0xb4 -+#define R_IA64_DTPREL32LSB 0xb5 -+#define R_IA64_DTPREL64MSB 0xb6 -+#define R_IA64_DTPREL64LSB 0xb7 -+#define R_IA64_LTOFF_DTPREL22 0xba -+ -+ -+ -+ -+#define R_SH_NONE 0 -+#define R_SH_DIR32 1 -+#define R_SH_REL32 2 -+#define R_SH_DIR8WPN 3 -+#define R_SH_IND12W 4 -+#define R_SH_DIR8WPL 5 -+#define R_SH_DIR8WPZ 6 -+#define R_SH_DIR8BP 7 -+#define R_SH_DIR8W 8 -+#define R_SH_DIR8L 9 -+#define R_SH_SWITCH16 25 -+#define R_SH_SWITCH32 26 -+#define R_SH_USES 27 -+#define R_SH_COUNT 28 -+#define R_SH_ALIGN 29 -+#define R_SH_CODE 30 -+#define R_SH_DATA 31 -+#define R_SH_LABEL 32 -+#define R_SH_SWITCH8 33 -+#define R_SH_GNU_VTINHERIT 34 -+#define R_SH_GNU_VTENTRY 35 -+#define R_SH_TLS_GD_32 144 -+#define R_SH_TLS_LD_32 145 -+#define R_SH_TLS_LDO_32 146 -+#define R_SH_TLS_IE_32 147 -+#define R_SH_TLS_LE_32 148 -+#define R_SH_TLS_DTPMOD32 149 -+#define R_SH_TLS_DTPOFF32 150 -+#define R_SH_TLS_TPOFF32 151 -+#define R_SH_GOT32 160 -+#define R_SH_PLT32 161 -+#define R_SH_COPY 162 -+#define R_SH_GLOB_DAT 163 -+#define R_SH_JMP_SLOT 164 -+#define R_SH_RELATIVE 165 -+#define R_SH_GOTOFF 166 -+#define R_SH_GOTPC 167 -+ -+#define R_SH_NUM 256 -+ -+ -+ -+#define R_390_NONE 0 -+#define R_390_8 1 -+#define R_390_12 2 -+#define R_390_16 3 -+#define R_390_32 4 -+#define R_390_PC32 5 -+#define R_390_GOT12 6 -+#define R_390_GOT32 7 -+#define R_390_PLT32 8 -+#define R_390_COPY 9 -+#define R_390_GLOB_DAT 10 -+#define R_390_JMP_SLOT 11 -+#define R_390_RELATIVE 12 -+#define R_390_GOTOFF32 13 -+#define R_390_GOTPC 14 -+#define R_390_GOT16 15 -+#define R_390_PC16 16 -+#define R_390_PC16DBL 17 -+#define R_390_PLT16DBL 18 -+#define R_390_PC32DBL 19 -+#define R_390_PLT32DBL 20 -+#define R_390_GOTPCDBL 21 -+#define R_390_64 22 -+#define R_390_PC64 23 -+#define R_390_GOT64 24 -+#define R_390_PLT64 25 -+#define R_390_GOTENT 26 -+#define R_390_GOTOFF16 27 -+#define R_390_GOTOFF64 28 -+#define R_390_GOTPLT12 29 -+#define R_390_GOTPLT16 30 -+#define R_390_GOTPLT32 31 -+#define R_390_GOTPLT64 32 -+#define R_390_GOTPLTENT 33 -+#define R_390_PLTOFF16 34 -+#define R_390_PLTOFF32 35 -+#define R_390_PLTOFF64 36 -+#define R_390_TLS_LOAD 37 -+#define R_390_TLS_GDCALL 38 -+ -+#define R_390_TLS_LDCALL 39 -+ -+#define R_390_TLS_GD32 40 -+ -+#define R_390_TLS_GD64 41 -+ -+#define R_390_TLS_GOTIE12 42 -+ -+#define R_390_TLS_GOTIE32 43 -+ -+#define R_390_TLS_GOTIE64 44 -+ -+#define R_390_TLS_LDM32 45 -+ -+#define R_390_TLS_LDM64 46 -+ -+#define R_390_TLS_IE32 47 -+ -+#define R_390_TLS_IE64 48 -+ -+#define R_390_TLS_IEENT 49 -+ -+#define R_390_TLS_LE32 50 -+ -+#define R_390_TLS_LE64 51 -+ -+#define R_390_TLS_LDO32 52 -+ -+#define R_390_TLS_LDO64 53 -+ -+#define R_390_TLS_DTPMOD 54 -+#define R_390_TLS_DTPOFF 55 -+#define R_390_TLS_TPOFF 56 -+ -+#define R_390_20 57 -+#define R_390_GOT20 58 -+#define R_390_GOTPLT20 59 -+#define R_390_TLS_GOTIE20 60 -+ -+ -+#define R_390_NUM 61 -+ -+ -+ -+#define R_CRIS_NONE 0 -+#define R_CRIS_8 1 -+#define R_CRIS_16 2 -+#define R_CRIS_32 3 -+#define R_CRIS_8_PCREL 4 -+#define R_CRIS_16_PCREL 5 -+#define R_CRIS_32_PCREL 6 -+#define R_CRIS_GNU_VTINHERIT 7 -+#define R_CRIS_GNU_VTENTRY 8 -+#define R_CRIS_COPY 9 -+#define R_CRIS_GLOB_DAT 10 -+#define R_CRIS_JUMP_SLOT 11 -+#define R_CRIS_RELATIVE 12 -+#define R_CRIS_16_GOT 13 -+#define R_CRIS_32_GOT 14 -+#define R_CRIS_16_GOTPLT 15 -+#define R_CRIS_32_GOTPLT 16 -+#define R_CRIS_32_GOTREL 17 -+#define R_CRIS_32_PLT_GOTREL 18 -+#define R_CRIS_32_PLT_PCREL 19 -+ -+#define R_CRIS_NUM 20 -+ -+ -+ -+#define R_X86_64_NONE 0 -+#define R_X86_64_64 1 -+#define R_X86_64_PC32 2 -+#define R_X86_64_GOT32 3 -+#define R_X86_64_PLT32 4 -+#define R_X86_64_COPY 5 -+#define R_X86_64_GLOB_DAT 6 -+#define R_X86_64_JUMP_SLOT 7 -+#define R_X86_64_RELATIVE 8 -+#define R_X86_64_GOTPCREL 9 -+ -+#define R_X86_64_32 10 -+#define R_X86_64_32S 11 -+#define R_X86_64_16 12 -+#define R_X86_64_PC16 13 -+#define R_X86_64_8 14 -+#define R_X86_64_PC8 15 -+#define R_X86_64_DTPMOD64 16 -+#define R_X86_64_DTPOFF64 17 -+#define R_X86_64_TPOFF64 18 -+#define R_X86_64_TLSGD 19 -+ -+#define R_X86_64_TLSLD 20 -+ -+#define R_X86_64_DTPOFF32 21 -+#define R_X86_64_GOTTPOFF 22 -+ -+#define R_X86_64_TPOFF32 23 -+#define R_X86_64_PC64 24 -+#define R_X86_64_GOTOFF64 25 -+#define R_X86_64_GOTPC32 26 -+#define R_X86_64_GOT64 27 -+#define R_X86_64_GOTPCREL64 28 -+#define R_X86_64_GOTPC64 29 -+#define R_X86_64_GOTPLT64 30 -+#define R_X86_64_PLTOFF64 31 -+#define R_X86_64_SIZE32 32 -+#define R_X86_64_SIZE64 33 -+ -+#define R_X86_64_GOTPC32_TLSDESC 34 -+#define R_X86_64_TLSDESC_CALL 35 -+ -+#define R_X86_64_TLSDESC 36 -+#define R_X86_64_IRELATIVE 37 -+#define R_X86_64_RELATIVE64 38 -+#define R_X86_64_NUM 39 -+ -+ -+ -+#define R_MN10300_NONE 0 -+#define R_MN10300_32 1 -+#define R_MN10300_16 2 -+#define R_MN10300_8 3 -+#define R_MN10300_PCREL32 4 -+#define R_MN10300_PCREL16 5 -+#define R_MN10300_PCREL8 6 -+#define R_MN10300_GNU_VTINHERIT 7 -+#define R_MN10300_GNU_VTENTRY 8 -+#define R_MN10300_24 9 -+#define R_MN10300_GOTPC32 10 -+#define R_MN10300_GOTPC16 11 -+#define R_MN10300_GOTOFF32 12 -+#define R_MN10300_GOTOFF24 13 -+#define R_MN10300_GOTOFF16 14 -+#define R_MN10300_PLT32 15 -+#define R_MN10300_PLT16 16 -+#define R_MN10300_GOT32 17 -+#define R_MN10300_GOT24 18 -+#define R_MN10300_GOT16 19 -+#define R_MN10300_COPY 20 -+#define R_MN10300_GLOB_DAT 21 -+#define R_MN10300_JMP_SLOT 22 -+#define R_MN10300_RELATIVE 23 -+ -+#define R_MN10300_NUM 24 -+ -+ -+ -+#define R_M32R_NONE 0 -+#define R_M32R_16 1 -+#define R_M32R_32 2 -+#define R_M32R_24 3 -+#define R_M32R_10_PCREL 4 -+#define R_M32R_18_PCREL 5 -+#define R_M32R_26_PCREL 6 -+#define R_M32R_HI16_ULO 7 -+#define R_M32R_HI16_SLO 8 -+#define R_M32R_LO16 9 -+#define R_M32R_SDA16 10 -+#define R_M32R_GNU_VTINHERIT 11 -+#define R_M32R_GNU_VTENTRY 12 -+ -+#define R_M32R_16_RELA 33 -+#define R_M32R_32_RELA 34 -+#define R_M32R_24_RELA 35 -+#define R_M32R_10_PCREL_RELA 36 -+#define R_M32R_18_PCREL_RELA 37 -+#define R_M32R_26_PCREL_RELA 38 -+#define R_M32R_HI16_ULO_RELA 39 -+#define R_M32R_HI16_SLO_RELA 40 -+#define R_M32R_LO16_RELA 41 -+#define R_M32R_SDA16_RELA 42 -+#define R_M32R_RELA_GNU_VTINHERIT 43 -+#define R_M32R_RELA_GNU_VTENTRY 44 -+#define R_M32R_REL32 45 -+ -+#define R_M32R_GOT24 48 -+#define R_M32R_26_PLTREL 49 -+#define R_M32R_COPY 50 -+#define R_M32R_GLOB_DAT 51 -+#define R_M32R_JMP_SLOT 52 -+#define R_M32R_RELATIVE 53 -+#define R_M32R_GOTOFF 54 -+#define R_M32R_GOTPC24 55 -+#define R_M32R_GOT16_HI_ULO 56 -+ -+#define R_M32R_GOT16_HI_SLO 57 -+ -+#define R_M32R_GOT16_LO 58 -+#define R_M32R_GOTPC_HI_ULO 59 -+ -+#define R_M32R_GOTPC_HI_SLO 60 -+ -+#define R_M32R_GOTPC_LO 61 -+ -+#define R_M32R_GOTOFF_HI_ULO 62 -+ -+#define R_M32R_GOTOFF_HI_SLO 63 -+ -+#define R_M32R_GOTOFF_LO 64 -+#define R_M32R_NUM 256 -+ -+#define R_MICROBLAZE_NONE 0 -+#define R_MICROBLAZE_32 1 -+#define R_MICROBLAZE_32_PCREL 2 -+#define R_MICROBLAZE_64_PCREL 3 -+#define R_MICROBLAZE_32_PCREL_LO 4 -+#define R_MICROBLAZE_64 5 -+#define R_MICROBLAZE_32_LO 6 -+#define R_MICROBLAZE_SRO32 7 -+#define R_MICROBLAZE_SRW32 8 -+#define R_MICROBLAZE_64_NONE 9 -+#define R_MICROBLAZE_32_SYM_OP_SYM 10 -+#define R_MICROBLAZE_GNU_VTINHERIT 11 -+#define R_MICROBLAZE_GNU_VTENTRY 12 -+#define R_MICROBLAZE_GOTPC_64 13 -+#define R_MICROBLAZE_GOT_64 14 -+#define R_MICROBLAZE_PLT_64 15 -+#define R_MICROBLAZE_REL 16 -+#define R_MICROBLAZE_JUMP_SLOT 17 -+#define R_MICROBLAZE_GLOB_DAT 18 -+#define R_MICROBLAZE_GOTOFF_64 19 -+#define R_MICROBLAZE_GOTOFF_32 20 -+#define R_MICROBLAZE_COPY 21 -+#define R_MICROBLAZE_TLS 22 -+#define R_MICROBLAZE_TLSGD 23 -+#define R_MICROBLAZE_TLSLD 24 -+#define R_MICROBLAZE_TLSDTPMOD32 25 -+#define R_MICROBLAZE_TLSDTPREL32 26 -+#define R_MICROBLAZE_TLSDTPREL64 27 -+#define R_MICROBLAZE_TLSGOTTPREL32 28 -+#define R_MICROBLAZE_TLSTPREL32 29 -+ -+#ifdef __cplusplus -+} -+#endif -+ -+ -+#endif diff --git a/target/linux/patches/3.10.75/sgidefs.patch b/target/linux/patches/3.10.75/sgidefs.patch deleted file mode 100644 index f00a284d9..000000000 --- a/target/linux/patches/3.10.75/sgidefs.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h ---- linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h 2013-11-08 22:01:28.000000000 +0100 -@@ -11,14 +11,6 @@ - #define __ASM_SGIDEFS_H - - /* -- * Using a Linux compiler for building Linux seems logic but not to -- * everybody. -- */ --#ifndef __linux__ --#error Use a Linux compiler or give up. --#endif -- --/* - * Definitions for the ISA levels - * - * With the introduction of MIPS32 / MIPS64 instruction sets definitions diff --git a/target/linux/patches/3.10.75/sortext.patch b/target/linux/patches/3.10.75/sortext.patch deleted file mode 100644 index 65bbbb64b..000000000 --- a/target/linux/patches/3.10.75/sortext.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/arm/Kconfig linux-3.11.5/arch/arm/Kconfig ---- linux-3.11.5.orig/arch/arm/Kconfig 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/arm/Kconfig 2013-10-16 10:36:41.000000000 +0200 -@@ -6,7 +6,6 @@ - select ARCH_HAVE_CUSTOM_GPIO_H - select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST - select ARCH_WANT_IPC_PARSE_VERSION -- select BUILDTIME_EXTABLE_SORT if MMU - select CPU_PM if (SUSPEND || CPU_IDLE) - select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && !CPU_BIG_ENDIAN && MMU - select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI) -diff -Nur linux-3.11.5.orig/arch/mips/Kconfig linux-3.11.5/arch/mips/Kconfig ---- linux-3.11.5.orig/arch/mips/Kconfig 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/mips/Kconfig 2013-10-16 10:36:41.000000000 +0200 -@@ -36,7 +36,6 @@ - select HAVE_MEMBLOCK_NODE_MAP - select ARCH_DISCARD_MEMBLOCK - select GENERIC_SMP_IDLE_THREAD -- select BUILDTIME_EXTABLE_SORT - select GENERIC_CLOCKEVENTS - select GENERIC_CMOS_UPDATE - select HAVE_MOD_ARCH_SPECIFIC -diff -Nur linux-3.11.5.orig/arch/x86/Kconfig linux-3.11.5/arch/x86/Kconfig ---- linux-3.11.5.orig/arch/x86/Kconfig 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/x86/Kconfig 2013-10-16 10:36:55.000000000 +0200 -@@ -101,7 +101,6 @@ - select GENERIC_SMP_IDLE_THREAD - select ARCH_WANT_IPC_PARSE_VERSION if X86_32 - select HAVE_ARCH_SECCOMP_FILTER -- select BUILDTIME_EXTABLE_SORT - select GENERIC_CMOS_UPDATE - select HAVE_ARCH_SOFT_DIRTY - select CLOCKSOURCE_WATCHDOG diff --git a/target/linux/patches/3.10.75/startup.patch b/target/linux/patches/3.10.75/startup.patch deleted file mode 100644 index 3ebc5db1e..000000000 --- a/target/linux/patches/3.10.75/startup.patch +++ /dev/null @@ -1,34 +0,0 @@ -diff -Nur linux-3.10.33.orig/init/initramfs.c linux-3.10.33/init/initramfs.c ---- linux-3.10.33.orig/init/initramfs.c 2014-03-07 06:58:45.000000000 +0100 -+++ linux-3.10.33/init/initramfs.c 2014-03-15 18:32:07.240436750 +0100 -@@ -622,6 +622,9 @@ - */ - load_default_modules(); - } -+#ifdef CONFIG_DEVTMPFS_MOUNT -+ devtmpfs_mount("dev"); -+#endif - return 0; - } - rootfs_initcall(populate_rootfs); -diff -Nur linux-3.10.33.orig/init/main.c linux-3.10.33/init/main.c ---- linux-3.10.33.orig/init/main.c 2014-03-07 06:58:45.000000000 +0100 -+++ linux-3.10.33/init/main.c 2014-03-15 18:32:07.240436750 +0100 -@@ -885,6 +885,8 @@ - if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - pr_err("Warning: unable to open an initial console.\n"); - -+ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); -+ - (void) sys_dup(0); - (void) sys_dup(0); - /* -@@ -893,7 +895,7 @@ - */ - - if (!ramdisk_execute_command) -- ramdisk_execute_command = "/init"; -+ ramdisk_execute_command = "/sbin/init"; - - if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { - ramdisk_execute_command = NULL; diff --git a/target/linux/patches/3.10.75/yaffs2.patch b/target/linux/patches/3.10.75/yaffs2.patch deleted file mode 100644 index 172629530..000000000 --- a/target/linux/patches/3.10.75/yaffs2.patch +++ /dev/null @@ -1,16547 +0,0 @@ -diff -Nur linux-3.10.40.orig/fs/Kconfig linux-3.10.40/fs/Kconfig ---- linux-3.10.40.orig/fs/Kconfig 2014-05-13 14:00:04.000000000 +0200 -+++ linux-3.10.40/fs/Kconfig 2014-05-17 12:48:10.000000000 +0200 -@@ -193,6 +193,7 @@ - source "fs/befs/Kconfig" - source "fs/bfs/Kconfig" - source "fs/efs/Kconfig" -+source "fs/yaffs2/Kconfig" - source "fs/jffs2/Kconfig" - # UBIFS File system configuration - source "fs/ubifs/Kconfig" -diff -Nur linux-3.10.40.orig/fs/Makefile linux-3.10.40/fs/Makefile ---- linux-3.10.40.orig/fs/Makefile 2014-05-13 14:00:04.000000000 +0200 -+++ linux-3.10.40/fs/Makefile 2014-05-17 12:48:10.000000000 +0200 -@@ -126,3 +126,4 @@ - obj-$(CONFIG_CEPH_FS) += ceph/ - obj-$(CONFIG_PSTORE) += pstore/ - obj-$(CONFIG_EFIVAR_FS) += efivarfs/ -+obj-$(CONFIG_YAFFS_FS) += yaffs2/ -diff -Nur linux-3.10.40.orig/fs/yaffs2/Kconfig linux-3.10.40/fs/yaffs2/Kconfig ---- linux-3.10.40.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/Kconfig 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,171 @@ -+# -+# yaffs file system configurations -+# -+ -+config YAFFS_FS -+ tristate "yaffs2 file system support" -+ default n -+ depends on MTD_BLOCK -+ select YAFFS_YAFFS1 -+ select YAFFS_YAFFS2 -+ help -+ yaffs2, or Yet Another Flash File System, is a file system -+ optimised for NAND Flash chips. -+ -+ To compile the yaffs2 file system support as a module, choose M -+ here: the module will be called yaffs2. -+ -+ If unsure, say N. -+ -+ Further information on yaffs2 is available at -+ . -+ -+config YAFFS_YAFFS1 -+ bool "512 byte / page devices" -+ depends on YAFFS_FS -+ default y -+ help -+ Enable yaffs1 support -- yaffs for 512 byte / page devices -+ -+ Not needed for 2K-page devices. -+ -+ If unsure, say Y. -+ -+config YAFFS_9BYTE_TAGS -+ bool "Use older-style on-NAND data format with pageStatus byte" -+ depends on YAFFS_YAFFS1 -+ default n -+ help -+ -+ Older-style on-NAND data format has a "pageStatus" byte to record -+ chunk/page state. This byte is zero when the page is discarded. -+ Choose this option if you have existing on-NAND data using this -+ format that you need to continue to support. New data written -+ also uses the older-style format. Note: Use of this option -+ generally requires that MTD's oob layout be adjusted to use the -+ older-style format. See notes on tags formats and MTD versions -+ in yaffs_mtdif1.c. -+ -+ If unsure, say N. -+ -+config YAFFS_DOES_ECC -+ bool "Lets yaffs do its own ECC" -+ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS -+ default n -+ help -+ This enables yaffs to use its own ECC functions instead of using -+ the ones from the generic MTD-NAND driver. -+ -+ If unsure, say N. -+ -+config YAFFS_ECC_WRONG_ORDER -+ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" -+ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS -+ default n -+ help -+ This makes yaffs_ecc.c use the same ecc byte order as Steven -+ Hill's nand_ecc.c. If not set, then you get the same ecc byte -+ order as SmartMedia. -+ -+ If unsure, say N. -+ -+config YAFFS_YAFFS2 -+ bool "2048 byte (or larger) / page devices" -+ depends on YAFFS_FS -+ default y -+ help -+ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices -+ -+ If unsure, say Y. -+ -+config YAFFS_AUTO_YAFFS2 -+ bool "Autoselect yaffs2 format" -+ depends on YAFFS_YAFFS2 -+ default y -+ help -+ Without this, you need to explicitely use yaffs2 as the file -+ system type. With this, you can say "yaffs" and yaffs or yaffs2 -+ will be used depending on the device page size (yaffs on -+ 512-byte page devices, yaffs2 on 2K page devices). -+ -+ If unsure, say Y. -+ -+config YAFFS_DISABLE_TAGS_ECC -+ bool "Disable yaffs from doing ECC on tags by default" -+ depends on YAFFS_FS && YAFFS_YAFFS2 -+ default n -+ help -+ This defaults yaffs to using its own ECC calculations on tags instead of -+ just relying on the MTD. -+ This behavior can also be overridden with tags_ecc_on and -+ tags_ecc_off mount options. -+ -+ If unsure, say N. -+ -+config YAFFS_ALWAYS_CHECK_CHUNK_ERASED -+ bool "Force chunk erase check" -+ depends on YAFFS_FS -+ default n -+ help -+ Normally yaffs only checks chunks before writing until an erased -+ chunk is found. This helps to detect any partially written -+ chunks that might have happened due to power loss. -+ -+ Enabling this forces on the test that chunks are erased in flash -+ before writing to them. This takes more time but is potentially -+ a bit more secure. -+ -+ Suggest setting Y during development and ironing out driver -+ issues etc. Suggest setting to N if you want faster writing. -+ -+ If unsure, say Y. -+ -+config YAFFS_EMPTY_LOST_AND_FOUND -+ bool "Empty lost and found on boot" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is enabled then the contents of lost and found is -+ automatically dumped at mount. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BLOCK_REFRESHING -+ bool "Disable yaffs2 block refreshing" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is set, then block refreshing is disabled. -+ Block refreshing infrequently refreshes the oldest block in -+ a yaffs2 file system. This mechanism helps to refresh flash to -+ mitigate against data loss. This is particularly useful for MLC. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BACKGROUND -+ bool "Disable yaffs2 background processing" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is set, then background processing is disabled. -+ Background processing makes many foreground activities faster. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BAD_BLOCK_MARKING -+ bool "Disable yaffs2 bad block marking" -+ depends on YAFFS_FS -+ default n -+ help -+ Useful during early flash bring up to prevent problems causing -+ lots of bad block marking. -+ -+ If unsure, say N. -+ -+config YAFFS_XATTR -+ bool "Enable yaffs2 xattr support" -+ depends on YAFFS_FS -+ default y -+ help -+ If this is set then yaffs2 will provide xattr support. -+ If unsure, say Y. -diff -Nur linux-3.10.40.orig/fs/yaffs2/Makefile linux-3.10.40/fs/yaffs2/Makefile ---- linux-3.10.40.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/Makefile 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,18 @@ -+# -+# Makefile for the linux YAFFS filesystem routines. -+# -+ -+obj-$(CONFIG_YAFFS_FS) += yaffs.o -+ -+yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o -+yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o -+yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o -+yaffs-y += yaffs_mtdif.o -+yaffs-y += yaffs_nameval.o yaffs_attribs.o -+yaffs-y += yaffs_allocator.o -+yaffs-y += yaffs_yaffs1.o -+yaffs-y += yaffs_yaffs2.o -+yaffs-y += yaffs_bitmap.o -+yaffs-y += yaffs_summary.o -+yaffs-y += yaffs_verify.o -+ -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_allocator.c linux-3.10.40/fs/yaffs2/yaffs_allocator.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_allocator.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,357 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_allocator.h" -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yportenv.h" -+ -+/* -+ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks -+ * of approx 100 objects that are themn allocated singly. -+ * This is basically a simplified slab allocator. -+ * -+ * We don't use the Linux slab allocator because slab does not allow -+ * us to dump all the objects in one hit when we do a umount and tear -+ * down all the tnodes and objects. slab requires that we first free -+ * the individual objects. -+ * -+ * Once yaffs has been mainlined I shall try to motivate for a change -+ * to slab to provide the extra features we need here. -+ */ -+ -+struct yaffs_tnode_list { -+ struct yaffs_tnode_list *next; -+ struct yaffs_tnode *tnodes; -+}; -+ -+struct yaffs_obj_list { -+ struct yaffs_obj_list *next; -+ struct yaffs_obj *objects; -+}; -+ -+struct yaffs_allocator { -+ int n_tnodes_created; -+ struct yaffs_tnode *free_tnodes; -+ int n_free_tnodes; -+ struct yaffs_tnode_list *alloc_tnode_list; -+ -+ int n_obj_created; -+ struct list_head free_objs; -+ int n_free_objects; -+ -+ struct yaffs_obj_list *allocated_obj_list; -+}; -+ -+static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ struct yaffs_tnode_list *tmp; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ while (allocator->alloc_tnode_list) { -+ tmp = allocator->alloc_tnode_list->next; -+ -+ kfree(allocator->alloc_tnode_list->tnodes); -+ kfree(allocator->alloc_tnode_list); -+ allocator->alloc_tnode_list = tmp; -+ } -+ -+ allocator->free_tnodes = NULL; -+ allocator->n_free_tnodes = 0; -+ allocator->n_tnodes_created = 0; -+} -+ -+static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator->alloc_tnode_list = NULL; -+ allocator->free_tnodes = NULL; -+ allocator->n_free_tnodes = 0; -+ allocator->n_tnodes_created = 0; -+} -+ -+static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ int i; -+ struct yaffs_tnode *new_tnodes; -+ u8 *mem; -+ struct yaffs_tnode *curr; -+ struct yaffs_tnode *next; -+ struct yaffs_tnode_list *tnl; -+ -+ if (!allocator) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ if (n_tnodes < 1) -+ return YAFFS_OK; -+ -+ /* make these things */ -+ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); -+ mem = (u8 *) new_tnodes; -+ -+ if (!new_tnodes) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs: Could not allocate Tnodes"); -+ return YAFFS_FAIL; -+ } -+ -+ /* New hookup for wide tnodes */ -+ for (i = 0; i < n_tnodes - 1; i++) { -+ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; -+ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; -+ curr->internal[0] = next; -+ } -+ -+ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; -+ curr->internal[0] = allocator->free_tnodes; -+ allocator->free_tnodes = (struct yaffs_tnode *)mem; -+ -+ allocator->n_free_tnodes += n_tnodes; -+ allocator->n_tnodes_created += n_tnodes; -+ -+ /* Now add this bunch of tnodes to a list for freeing up. -+ * NB If we can't add this to the management list it isn't fatal -+ * but it just means we can't free this bunch of tnodes later. -+ */ -+ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); -+ if (!tnl) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Could not add tnodes to management list"); -+ return YAFFS_FAIL; -+ } else { -+ tnl->tnodes = new_tnodes; -+ tnl->next = allocator->alloc_tnode_list; -+ allocator->alloc_tnode_list = tnl; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); -+ -+ return YAFFS_OK; -+} -+ -+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ struct yaffs_tnode *tn = NULL; -+ -+ if (!allocator) { -+ BUG(); -+ return NULL; -+ } -+ -+ /* If there are none left make more */ -+ if (!allocator->free_tnodes) -+ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); -+ -+ if (allocator->free_tnodes) { -+ tn = allocator->free_tnodes; -+ allocator->free_tnodes = allocator->free_tnodes->internal[0]; -+ allocator->n_free_tnodes--; -+ } -+ -+ return tn; -+} -+ -+/* FreeTnode frees up a tnode and puts it back on the free list */ -+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ if (tn) { -+ tn->internal[0] = allocator->free_tnodes; -+ allocator->free_tnodes = tn; -+ allocator->n_free_tnodes++; -+ } -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+/*--------------- yaffs_obj alloaction ------------------------ -+ * -+ * Free yaffs_objs are stored in a list using obj->siblings. -+ * The blocks of allocated objects are stored in a linked list. -+ */ -+ -+static void yaffs_init_raw_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator->allocated_obj_list = NULL; -+ INIT_LIST_HEAD(&allocator->free_objs); -+ allocator->n_free_objects = 0; -+} -+ -+static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ struct yaffs_obj_list *tmp; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ while (allocator->allocated_obj_list) { -+ tmp = allocator->allocated_obj_list->next; -+ kfree(allocator->allocated_obj_list->objects); -+ kfree(allocator->allocated_obj_list); -+ allocator->allocated_obj_list = tmp; -+ } -+ -+ INIT_LIST_HEAD(&allocator->free_objs); -+ allocator->n_free_objects = 0; -+ allocator->n_obj_created = 0; -+} -+ -+static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ int i; -+ struct yaffs_obj *new_objs; -+ struct yaffs_obj_list *list; -+ -+ if (!allocator) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ if (n_obj < 1) -+ return YAFFS_OK; -+ -+ /* make these things */ -+ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); -+ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); -+ -+ if (!new_objs || !list) { -+ kfree(new_objs); -+ new_objs = NULL; -+ kfree(list); -+ list = NULL; -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, -+ "Could not allocate more objects"); -+ return YAFFS_FAIL; -+ } -+ -+ /* Hook them into the free list */ -+ for (i = 0; i < n_obj; i++) -+ list_add(&new_objs[i].siblings, &allocator->free_objs); -+ -+ allocator->n_free_objects += n_obj; -+ allocator->n_obj_created += n_obj; -+ -+ /* Now add this bunch of Objects to a list for freeing up. */ -+ -+ list->objects = new_objs; -+ list->next = allocator->allocated_obj_list; -+ allocator->allocated_obj_list = list; -+ -+ return YAFFS_OK; -+} -+ -+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj = NULL; -+ struct list_head *lh; -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return obj; -+ } -+ -+ /* If there are none left make more */ -+ if (list_empty(&allocator->free_objs)) -+ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); -+ -+ if (!list_empty(&allocator->free_objs)) { -+ lh = allocator->free_objs.next; -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ list_del_init(lh); -+ allocator->n_free_objects--; -+ } -+ -+ return obj; -+} -+ -+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) -+{ -+ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ /* Link into the free list. */ -+ list_add(&obj->siblings, &allocator->free_objs); -+ allocator->n_free_objects++; -+} -+ -+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ -+ if (!dev->allocator) { -+ BUG(); -+ return; -+ } -+ -+ yaffs_deinit_raw_tnodes(dev); -+ yaffs_deinit_raw_objs(dev); -+ kfree(dev->allocator); -+ dev->allocator = NULL; -+} -+ -+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator; -+ -+ if (dev->allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); -+ if (allocator) { -+ dev->allocator = allocator; -+ yaffs_init_raw_tnodes(dev); -+ yaffs_init_raw_objs(dev); -+ } -+} -+ -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_allocator.h linux-3.10.40/fs/yaffs2/yaffs_allocator.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_allocator.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,30 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_ALLOCATOR_H__ -+#define __YAFFS_ALLOCATOR_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); -+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); -+ -+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); -+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); -+ -+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); -+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_attribs.c linux-3.10.40/fs/yaffs2/yaffs_attribs.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_attribs.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,166 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_attribs.h" -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+static inline uid_t ia_uid_read(const struct iattr *iattr) -+{ -+ return from_kuid(&init_user_ns, iattr->ia_uid); -+} -+ -+static inline gid_t ia_gid_read(const struct iattr *iattr) -+{ -+ return from_kgid(&init_user_ns, iattr->ia_gid); -+} -+ -+static inline void ia_uid_write(struct iattr *iattr, uid_t uid) -+{ -+ iattr->ia_uid = make_kuid(&init_user_ns, uid); -+} -+ -+static inline void ia_gid_write(struct iattr *iattr, gid_t gid) -+{ -+ iattr->ia_gid = make_kgid(&init_user_ns, gid); -+} -+#else -+static inline uid_t ia_uid_read(const struct iattr *iattr) -+{ -+ return iattr->ia_uid; -+} -+ -+static inline gid_t ia_gid_read(const struct iattr *inode) -+{ -+ return iattr->ia_gid; -+} -+ -+static inline void ia_uid_write(struct iattr *iattr, uid_t uid) -+{ -+ iattr->ia_uid = uid; -+} -+ -+static inline void ia_gid_write(struct iattr *iattr, gid_t gid) -+{ -+ iattr->ia_gid = gid; -+} -+#endif -+ -+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) -+{ -+ obj->yst_uid = oh->yst_uid; -+ obj->yst_gid = oh->yst_gid; -+ obj->yst_atime = oh->yst_atime; -+ obj->yst_mtime = oh->yst_mtime; -+ obj->yst_ctime = oh->yst_ctime; -+ obj->yst_rdev = oh->yst_rdev; -+} -+ -+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) -+{ -+ oh->yst_uid = obj->yst_uid; -+ oh->yst_gid = obj->yst_gid; -+ oh->yst_atime = obj->yst_atime; -+ oh->yst_mtime = obj->yst_mtime; -+ oh->yst_ctime = obj->yst_ctime; -+ oh->yst_rdev = obj->yst_rdev; -+ -+} -+ -+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) -+{ -+ obj->yst_mtime = Y_CURRENT_TIME; -+ if (do_a) -+ obj->yst_atime = obj->yst_mtime; -+ if (do_c) -+ obj->yst_ctime = obj->yst_mtime; -+} -+ -+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) -+{ -+ yaffs_load_current_time(obj, 1, 1); -+ obj->yst_rdev = rdev; -+ obj->yst_uid = uid; -+ obj->yst_gid = gid; -+} -+ -+static loff_t yaffs_get_file_size(struct yaffs_obj *obj) -+{ -+ YCHAR *alias = NULL; -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return obj->variant.file_variant.file_size; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ alias = obj->variant.symlink_variant.alias; -+ if (!alias) -+ return 0; -+ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); -+ default: -+ return 0; -+ } -+} -+ -+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) -+{ -+ unsigned int valid = attr->ia_valid; -+ -+ if (valid & ATTR_MODE) -+ obj->yst_mode = attr->ia_mode; -+ if (valid & ATTR_UID) -+ obj->yst_uid = ia_uid_read(attr); -+ if (valid & ATTR_GID) -+ obj->yst_gid = ia_gid_read(attr); -+ -+ if (valid & ATTR_ATIME) -+ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); -+ if (valid & ATTR_CTIME) -+ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); -+ if (valid & ATTR_MTIME) -+ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); -+ -+ if (valid & ATTR_SIZE) -+ yaffs_resize_file(obj, attr->ia_size); -+ -+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); -+ -+ return YAFFS_OK; -+ -+} -+ -+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) -+{ -+ unsigned int valid = 0; -+ -+ attr->ia_mode = obj->yst_mode; -+ valid |= ATTR_MODE; -+ ia_uid_write(attr, obj->yst_uid); -+ valid |= ATTR_UID; -+ ia_gid_write(attr, obj->yst_gid); -+ valid |= ATTR_GID; -+ -+ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; -+ valid |= ATTR_ATIME; -+ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; -+ valid |= ATTR_CTIME; -+ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; -+ valid |= ATTR_MTIME; -+ -+ attr->ia_size = yaffs_get_file_size(obj); -+ valid |= ATTR_SIZE; -+ -+ attr->ia_valid = valid; -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_attribs.h linux-3.10.40/fs/yaffs2/yaffs_attribs.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_attribs.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,28 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_ATTRIBS_H__ -+#define __YAFFS_ATTRIBS_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); -+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); -+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); -+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); -+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); -+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_bitmap.c linux-3.10.40/fs/yaffs2/yaffs_bitmap.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_bitmap.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,97 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_bitmap.h" -+#include "yaffs_trace.h" -+/* -+ * Chunk bitmap manipulations -+ */ -+ -+static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "BlockBits block %d is not valid", -+ blk); -+ BUG(); -+ } -+ return dev->chunk_bits + -+ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); -+} -+ -+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block || -+ chunk < 0 || chunk >= dev->param.chunks_per_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Chunk Id (%d:%d) invalid", -+ blk, chunk); -+ BUG(); -+ } -+} -+ -+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ memset(blk_bits, 0, dev->chunk_bit_stride); -+} -+ -+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); -+} -+ -+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ blk_bits[chunk / 8] |= (1 << (chunk & 7)); -+} -+ -+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; -+} -+ -+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ int i; -+ -+ for (i = 0; i < dev->chunk_bit_stride; i++) { -+ if (*blk_bits) -+ return 1; -+ blk_bits++; -+ } -+ return 0; -+} -+ -+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ int i; -+ int n = 0; -+ -+ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) -+ n += hweight8(*blk_bits); -+ -+ return n; -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_bitmap.h linux-3.10.40/fs/yaffs2/yaffs_bitmap.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_bitmap.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,33 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* -+ * Chunk bitmap manipulations -+ */ -+ -+#ifndef __YAFFS_BITMAP_H__ -+#define __YAFFS_BITMAP_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); -+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); -+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); -+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.10.40/fs/yaffs2/yaffs_checkptrw.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,474 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_checkptrw.h" -+#include "yaffs_getblockinfo.h" -+ -+struct yaffs_checkpt_chunk_hdr { -+ int version; -+ int seq; -+ u32 sum; -+ u32 xor; -+} ; -+ -+ -+static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) -+{ -+ return chunk - dev->chunk_offset; -+} -+ -+static int apply_block_offset(struct yaffs_dev *dev, int block) -+{ -+ return block - dev->block_offset; -+} -+ -+static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_chunk_hdr hdr; -+ -+ hdr.version = YAFFS_CHECKPOINT_VERSION; -+ hdr.seq = dev->checkpt_page_seq; -+ hdr.sum = dev->checkpt_sum; -+ hdr.xor = dev->checkpt_xor; -+ -+ dev->checkpt_byte_offs = sizeof(hdr); -+ -+ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); -+} -+ -+static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_chunk_hdr hdr; -+ -+ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); -+ -+ dev->checkpt_byte_offs = sizeof(hdr); -+ -+ return hdr.version == YAFFS_CHECKPOINT_VERSION && -+ hdr.seq == dev->checkpt_page_seq && -+ hdr.sum == dev->checkpt_sum && -+ hdr.xor == dev->checkpt_xor; -+} -+ -+static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) -+{ -+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpt blocks_avail = %d", blocks_avail); -+ -+ return (blocks_avail <= 0) ? 0 : 1; -+} -+ -+static int yaffs_checkpt_erase(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (!dev->drv.drv_erase_fn) -+ return 0; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checking blocks %d to %d", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); -+ int offset_i = apply_block_offset(dev, i); -+ int result; -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "erasing checkpt block %d", i); -+ -+ dev->n_erasures++; -+ -+ result = dev->drv.drv_erase_fn(dev, offset_i); -+ if(result) { -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += -+ dev->param.chunks_per_block; -+ } else { -+ dev->drv.drv_mark_bad_fn(dev, offset_i); -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ } -+ } -+ } -+ -+ dev->blocks_in_checkpt = 0; -+ -+ return 1; -+} -+ -+static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) -+{ -+ int i; -+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "allocating checkpt block: erased %d reserved %d avail %d next %d ", -+ dev->n_erased_blocks, dev->param.n_reserved_blocks, -+ blocks_avail, dev->checkpt_next_block); -+ -+ if (dev->checkpt_next_block >= 0 && -+ dev->checkpt_next_block <= dev->internal_end_block && -+ blocks_avail > 0) { -+ -+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; -+ i++) { -+ struct yaffs_block_info *bi; -+ -+ bi = yaffs_get_block_info(dev, i); -+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ dev->checkpt_next_block = i + 1; -+ dev->checkpt_cur_block = i; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "allocating checkpt block %d", i); -+ return; -+ } -+ } -+ } -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); -+ -+ dev->checkpt_next_block = -1; -+ dev->checkpt_cur_block = -1; -+} -+ -+static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) -+{ -+ int i; -+ struct yaffs_ext_tags tags; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "find next checkpt block: start: blocks %d next %d", -+ dev->blocks_in_checkpt, dev->checkpt_next_block); -+ -+ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) -+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; -+ i++) { -+ int chunk = i * dev->param.chunks_per_block; -+ enum yaffs_block_state state; -+ u32 seq; -+ -+ dev->tagger.read_chunk_tags_fn(dev, -+ apply_chunk_offset(dev, chunk), -+ NULL, &tags); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", -+ i, (int) state, -+ tags.obj_id, tags.seq_number, -+ tags.ecc_result); -+ -+ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ continue; -+ -+ dev->tagger.query_block_fn(dev, -+ apply_block_offset(dev, i), -+ &state, &seq); -+ if (state == YAFFS_BLOCK_STATE_DEAD) -+ continue; -+ -+ /* Right kind of block */ -+ dev->checkpt_next_block = tags.obj_id; -+ dev->checkpt_cur_block = i; -+ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; -+ dev->blocks_in_checkpt++; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "found checkpt block %d", i); -+ return; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); -+ -+ dev->checkpt_next_block = -1; -+ dev->checkpt_cur_block = -1; -+} -+ -+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) -+{ -+ int i; -+ -+ dev->checkpt_open_write = writing; -+ -+ /* Got the functions we need? */ -+ if (!dev->tagger.write_chunk_tags_fn || -+ !dev->tagger.read_chunk_tags_fn || -+ !dev->drv.drv_erase_fn || -+ !dev->drv.drv_mark_bad_fn) -+ return 0; -+ -+ if (writing && !yaffs2_checkpt_space_ok(dev)) -+ return 0; -+ -+ if (!dev->checkpt_buffer) -+ dev->checkpt_buffer = -+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ dev->checkpt_page_seq = 0; -+ dev->checkpt_byte_count = 0; -+ dev->checkpt_sum = 0; -+ dev->checkpt_xor = 0; -+ dev->checkpt_cur_block = -1; -+ dev->checkpt_cur_chunk = -1; -+ dev->checkpt_next_block = dev->internal_start_block; -+ -+ if (writing) { -+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); -+ yaffs2_checkpt_init_chunk_hdr(dev); -+ return yaffs_checkpt_erase(dev); -+ } -+ -+ /* Opening for a read */ -+ /* Set to a value that will kick off a read */ -+ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; -+ /* A checkpoint block list of 1 checkpoint block per 16 block is -+ * (hopefully) going to be way more than we need */ -+ dev->blocks_in_checkpt = 0; -+ dev->checkpt_max_blocks = -+ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; -+ dev->checkpt_block_list = -+ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); -+ -+ if (!dev->checkpt_block_list) -+ return 0; -+ -+ for (i = 0; i < dev->checkpt_max_blocks; i++) -+ dev->checkpt_block_list[i] = -1; -+ -+ return 1; -+} -+ -+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) -+{ -+ u32 composite_sum; -+ -+ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); -+ *sum = composite_sum; -+ return 1; -+} -+ -+static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) -+{ -+ int chunk; -+ int offset_chunk; -+ struct yaffs_ext_tags tags; -+ -+ if (dev->checkpt_cur_block < 0) { -+ yaffs2_checkpt_find_erased_block(dev); -+ dev->checkpt_cur_chunk = 0; -+ } -+ -+ if (dev->checkpt_cur_block < 0) -+ return 0; -+ -+ tags.is_deleted = 0; -+ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ -+ tags.chunk_id = dev->checkpt_page_seq + 1; -+ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; -+ tags.n_bytes = dev->data_bytes_per_chunk; -+ if (dev->checkpt_cur_chunk == 0) { -+ /* First chunk we write for the block? Set block state to -+ checkpoint */ -+ struct yaffs_block_info *bi = -+ yaffs_get_block_info(dev, dev->checkpt_cur_block); -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ dev->blocks_in_checkpt++; -+ } -+ -+ chunk = -+ dev->checkpt_cur_block * dev->param.chunks_per_block + -+ dev->checkpt_cur_chunk; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", -+ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, -+ tags.obj_id, tags.chunk_id); -+ -+ offset_chunk = apply_chunk_offset(dev, chunk); -+ -+ dev->n_page_writes++; -+ -+ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, -+ dev->checkpt_buffer, &tags); -+ dev->checkpt_page_seq++; -+ dev->checkpt_cur_chunk++; -+ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { -+ dev->checkpt_cur_chunk = 0; -+ dev->checkpt_cur_block = -1; -+ } -+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); -+ -+ yaffs2_checkpt_init_chunk_hdr(dev); -+ -+ -+ return 1; -+} -+ -+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) -+{ -+ int i = 0; -+ int ok = 1; -+ u8 *data_bytes = (u8 *) data; -+ -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ if (!dev->checkpt_open_write) -+ return -1; -+ -+ while (i < n_bytes && ok) { -+ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; -+ dev->checkpt_sum += *data_bytes; -+ dev->checkpt_xor ^= *data_bytes; -+ -+ dev->checkpt_byte_offs++; -+ i++; -+ data_bytes++; -+ dev->checkpt_byte_count++; -+ -+ if (dev->checkpt_byte_offs < 0 || -+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) -+ ok = yaffs2_checkpt_flush_buffer(dev); -+ } -+ -+ return i; -+} -+ -+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) -+{ -+ int i = 0; -+ int ok = 1; -+ struct yaffs_ext_tags tags; -+ int chunk; -+ int offset_chunk; -+ u8 *data_bytes = (u8 *) data; -+ -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ if (dev->checkpt_open_write) -+ return -1; -+ -+ while (i < n_bytes && ok) { -+ -+ if (dev->checkpt_byte_offs < 0 || -+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { -+ -+ if (dev->checkpt_cur_block < 0) { -+ yaffs2_checkpt_find_block(dev); -+ dev->checkpt_cur_chunk = 0; -+ } -+ -+ if (dev->checkpt_cur_block < 0) { -+ ok = 0; -+ break; -+ } -+ -+ chunk = dev->checkpt_cur_block * -+ dev->param.chunks_per_block + -+ dev->checkpt_cur_chunk; -+ -+ offset_chunk = apply_chunk_offset(dev, chunk); -+ dev->n_page_reads++; -+ -+ /* read in the next chunk */ -+ dev->tagger.read_chunk_tags_fn(dev, -+ offset_chunk, -+ dev->checkpt_buffer, -+ &tags); -+ -+ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || -+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || -+ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) { -+ ok = 0; -+ break; -+ } -+ if(!yaffs2_checkpt_check_chunk_hdr(dev)) { -+ ok = 0; -+ break; -+ } -+ -+ dev->checkpt_page_seq++; -+ dev->checkpt_cur_chunk++; -+ -+ if (dev->checkpt_cur_chunk >= -+ dev->param.chunks_per_block) -+ dev->checkpt_cur_block = -1; -+ -+ } -+ -+ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; -+ dev->checkpt_sum += *data_bytes; -+ dev->checkpt_xor ^= *data_bytes; -+ dev->checkpt_byte_offs++; -+ i++; -+ data_bytes++; -+ dev->checkpt_byte_count++; -+ } -+ -+ return i; -+} -+ -+int yaffs_checkpt_close(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (dev->checkpt_open_write) { -+ if (dev->checkpt_byte_offs != -+ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) -+ yaffs2_checkpt_flush_buffer(dev); -+ } else if (dev->checkpt_block_list) { -+ for (i = 0; -+ i < dev->blocks_in_checkpt && -+ dev->checkpt_block_list[i] >= 0; i++) { -+ int blk = dev->checkpt_block_list[i]; -+ struct yaffs_block_info *bi = NULL; -+ -+ if (dev->internal_start_block <= blk && -+ blk <= dev->internal_end_block) -+ bi = yaffs_get_block_info(dev, blk); -+ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ } -+ kfree(dev->checkpt_block_list); -+ dev->checkpt_block_list = NULL; -+ } -+ -+ dev->n_free_chunks -= -+ dev->blocks_in_checkpt * dev->param.chunks_per_block; -+ dev->n_erased_blocks -= dev->blocks_in_checkpt; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", -+ dev->checkpt_byte_count); -+ -+ if (dev->checkpt_buffer) { -+ /* free the buffer */ -+ kfree(dev->checkpt_buffer); -+ dev->checkpt_buffer = NULL; -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ -+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) -+{ -+ /* Erase the checkpoint data */ -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpoint invalidate of %d blocks", -+ dev->blocks_in_checkpt); -+ -+ return yaffs_checkpt_erase(dev); -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.10.40/fs/yaffs2/yaffs_checkptrw.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,33 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_CHECKPTRW_H__ -+#define __YAFFS_CHECKPTRW_H__ -+ -+#include "yaffs_guts.h" -+ -+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); -+ -+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); -+ -+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); -+ -+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); -+ -+int yaffs_checkpt_close(struct yaffs_dev *dev); -+ -+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_ecc.c linux-3.10.40/fs/yaffs2/yaffs_ecc.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_ecc.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,281 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * This code implements the ECC algorithm used in SmartMedia. -+ * -+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. -+ * The two unused bit are set to 1. -+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two -+ * such ECC blocks are used on a 512-byte NAND page. -+ * -+ */ -+ -+#include "yportenv.h" -+ -+#include "yaffs_ecc.h" -+ -+/* Table generated by gen-ecc.c -+ * Using a table means we do not have to calculate p1..p4 and p1'..p4' -+ * for each byte of data. These are instead provided in a table in bits7..2. -+ * Bit 0 of each entry indicates whether the entry has an odd or even parity, -+ * and therefore this bytes influence on the line parity. -+ */ -+ -+static const unsigned char column_parity_table[] = { -+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, -+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, -+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, -+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, -+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, -+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, -+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, -+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, -+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, -+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, -+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, -+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, -+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, -+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, -+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, -+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, -+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, -+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, -+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, -+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, -+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, -+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, -+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, -+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, -+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, -+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, -+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, -+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, -+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, -+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, -+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, -+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, -+}; -+ -+ -+/* Calculate the ECC for a 256-byte block of data */ -+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) -+{ -+ unsigned int i; -+ unsigned char col_parity = 0; -+ unsigned char line_parity = 0; -+ unsigned char line_parity_prime = 0; -+ unsigned char t; -+ unsigned char b; -+ -+ for (i = 0; i < 256; i++) { -+ b = column_parity_table[*data++]; -+ col_parity ^= b; -+ -+ if (b & 0x01) { /* odd number of bits in the byte */ -+ line_parity ^= i; -+ line_parity_prime ^= ~i; -+ } -+ } -+ -+ ecc[2] = (~col_parity) | 0x03; -+ -+ t = 0; -+ if (line_parity & 0x80) -+ t |= 0x80; -+ if (line_parity_prime & 0x80) -+ t |= 0x40; -+ if (line_parity & 0x40) -+ t |= 0x20; -+ if (line_parity_prime & 0x40) -+ t |= 0x10; -+ if (line_parity & 0x20) -+ t |= 0x08; -+ if (line_parity_prime & 0x20) -+ t |= 0x04; -+ if (line_parity & 0x10) -+ t |= 0x02; -+ if (line_parity_prime & 0x10) -+ t |= 0x01; -+ ecc[1] = ~t; -+ -+ t = 0; -+ if (line_parity & 0x08) -+ t |= 0x80; -+ if (line_parity_prime & 0x08) -+ t |= 0x40; -+ if (line_parity & 0x04) -+ t |= 0x20; -+ if (line_parity_prime & 0x04) -+ t |= 0x10; -+ if (line_parity & 0x02) -+ t |= 0x08; -+ if (line_parity_prime & 0x02) -+ t |= 0x04; -+ if (line_parity & 0x01) -+ t |= 0x02; -+ if (line_parity_prime & 0x01) -+ t |= 0x01; -+ ecc[0] = ~t; -+ -+} -+ -+/* Correct the ECC on a 256 byte block of data */ -+ -+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, -+ const unsigned char *test_ecc) -+{ -+ unsigned char d0, d1, d2; /* deltas */ -+ -+ d0 = read_ecc[0] ^ test_ecc[0]; -+ d1 = read_ecc[1] ^ test_ecc[1]; -+ d2 = read_ecc[2] ^ test_ecc[2]; -+ -+ if ((d0 | d1 | d2) == 0) -+ return 0; /* no error */ -+ -+ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && -+ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && -+ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { -+ /* Single bit (recoverable) error in data */ -+ -+ unsigned byte; -+ unsigned bit; -+ -+ bit = byte = 0; -+ -+ if (d1 & 0x80) -+ byte |= 0x80; -+ if (d1 & 0x20) -+ byte |= 0x40; -+ if (d1 & 0x08) -+ byte |= 0x20; -+ if (d1 & 0x02) -+ byte |= 0x10; -+ if (d0 & 0x80) -+ byte |= 0x08; -+ if (d0 & 0x20) -+ byte |= 0x04; -+ if (d0 & 0x08) -+ byte |= 0x02; -+ if (d0 & 0x02) -+ byte |= 0x01; -+ -+ if (d2 & 0x80) -+ bit |= 0x04; -+ if (d2 & 0x20) -+ bit |= 0x02; -+ if (d2 & 0x08) -+ bit |= 0x01; -+ -+ data[byte] ^= (1 << bit); -+ -+ return 1; /* Corrected the error */ -+ } -+ -+ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { -+ /* Reccoverable error in ecc */ -+ -+ read_ecc[0] = test_ecc[0]; -+ read_ecc[1] = test_ecc[1]; -+ read_ecc[2] = test_ecc[2]; -+ -+ return 1; /* Corrected the error */ -+ } -+ -+ /* Unrecoverable error */ -+ -+ return -1; -+ -+} -+ -+/* -+ * ECCxxxOther does ECC calcs on arbitrary n bytes of data -+ */ -+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *ecc_other) -+{ -+ unsigned int i; -+ unsigned char col_parity = 0; -+ unsigned line_parity = 0; -+ unsigned line_parity_prime = 0; -+ unsigned char b; -+ -+ for (i = 0; i < n_bytes; i++) { -+ b = column_parity_table[*data++]; -+ col_parity ^= b; -+ -+ if (b & 0x01) { -+ /* odd number of bits in the byte */ -+ line_parity ^= i; -+ line_parity_prime ^= ~i; -+ } -+ -+ } -+ -+ ecc_other->col_parity = (col_parity >> 2) & 0x3f; -+ ecc_other->line_parity = line_parity; -+ ecc_other->line_parity_prime = line_parity_prime; -+} -+ -+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *read_ecc, -+ const struct yaffs_ecc_other *test_ecc) -+{ -+ unsigned char delta_col; /* column parity delta */ -+ unsigned delta_line; /* line parity delta */ -+ unsigned delta_line_prime; /* line parity delta */ -+ unsigned bit; -+ -+ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; -+ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; -+ delta_line_prime = -+ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; -+ -+ if ((delta_col | delta_line | delta_line_prime) == 0) -+ return 0; /* no error */ -+ -+ if (delta_line == ~delta_line_prime && -+ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { -+ /* Single bit (recoverable) error in data */ -+ -+ bit = 0; -+ -+ if (delta_col & 0x20) -+ bit |= 0x04; -+ if (delta_col & 0x08) -+ bit |= 0x02; -+ if (delta_col & 0x02) -+ bit |= 0x01; -+ -+ if (delta_line >= n_bytes) -+ return -1; -+ -+ data[delta_line] ^= (1 << bit); -+ -+ return 1; /* corrected */ -+ } -+ -+ if ((hweight32(delta_line) + -+ hweight32(delta_line_prime) + -+ hweight8(delta_col)) == 1) { -+ /* Reccoverable error in ecc */ -+ -+ *read_ecc = *test_ecc; -+ return 1; /* corrected */ -+ } -+ -+ /* Unrecoverable error */ -+ -+ return -1; -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_ecc.h linux-3.10.40/fs/yaffs2/yaffs_ecc.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_ecc.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,44 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* -+ * This code implements the ECC algorithm used in SmartMedia. -+ * -+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. -+ * The two unused bit are set to 1. -+ * The ECC can correct single bit errors in a 256-byte page of data. -+ * Thus, two such ECC blocks are used on a 512-byte NAND page. -+ * -+ */ -+ -+#ifndef __YAFFS_ECC_H__ -+#define __YAFFS_ECC_H__ -+ -+struct yaffs_ecc_other { -+ unsigned char col_parity; -+ unsigned line_parity; -+ unsigned line_parity_prime; -+}; -+ -+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); -+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, -+ const unsigned char *test_ecc); -+ -+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *ecc); -+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *read_ecc, -+ const struct yaffs_ecc_other *test_ecc); -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.10.40/fs/yaffs2/yaffs_getblockinfo.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,35 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_GETBLOCKINFO_H__ -+#define __YAFFS_GETBLOCKINFO_H__ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+ -+/* Function to manipulate block info */ -+static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev -+ *dev, int blk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs: get_block_info block %d is not valid", -+ blk); -+ BUG(); -+ } -+ return &dev->block_info[blk - dev->internal_start_block]; -+} -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_guts.c linux-3.10.40/fs/yaffs2/yaffs_guts.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_guts.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,5146 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yportenv.h" -+#include "yaffs_trace.h" -+ -+#include "yaffs_guts.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_tagscompat.h" -+#include "yaffs_tagsmarshall.h" -+#include "yaffs_nand.h" -+#include "yaffs_yaffs1.h" -+#include "yaffs_yaffs2.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_verify.h" -+#include "yaffs_nand.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_nameval.h" -+#include "yaffs_allocator.h" -+#include "yaffs_attribs.h" -+#include "yaffs_summary.h" -+ -+/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ -+#define YAFFS_GC_GOOD_ENOUGH 2 -+#define YAFFS_GC_PASSIVE_THRESHOLD 4 -+ -+#include "yaffs_ecc.h" -+ -+/* Forward declarations */ -+ -+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, -+ const u8 *buffer, int n_bytes, int use_reserve); -+ -+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, -+ int buffer_size); -+ -+/* Function to calculate chunk and offset */ -+ -+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, -+ int *chunk_out, u32 *offset_out) -+{ -+ int chunk; -+ u32 offset; -+ -+ chunk = (u32) (addr >> dev->chunk_shift); -+ -+ if (dev->chunk_div == 1) { -+ /* easy power of 2 case */ -+ offset = (u32) (addr & dev->chunk_mask); -+ } else { -+ /* Non power-of-2 case */ -+ -+ loff_t chunk_base; -+ -+ chunk /= dev->chunk_div; -+ -+ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; -+ offset = (u32) (addr - chunk_base); -+ } -+ -+ *chunk_out = chunk; -+ *offset_out = offset; -+} -+ -+/* Function to return the number of shifts for a power of 2 greater than or -+ * equal to the given number -+ * Note we don't try to cater for all possible numbers and this does not have to -+ * be hellishly efficient. -+ */ -+ -+static inline u32 calc_shifts_ceiling(u32 x) -+{ -+ int extra_bits; -+ int shifts; -+ -+ shifts = extra_bits = 0; -+ -+ while (x > 1) { -+ if (x & 1) -+ extra_bits++; -+ x >>= 1; -+ shifts++; -+ } -+ -+ if (extra_bits) -+ shifts++; -+ -+ return shifts; -+} -+ -+/* Function to return the number of shifts to get a 1 in bit 0 -+ */ -+ -+static inline u32 calc_shifts(u32 x) -+{ -+ u32 shifts; -+ -+ shifts = 0; -+ -+ if (!x) -+ return 0; -+ -+ while (!(x & 1)) { -+ x >>= 1; -+ shifts++; -+ } -+ -+ return shifts; -+} -+ -+/* -+ * Temporary buffer manipulations. -+ */ -+ -+static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) -+{ -+ int i; -+ u8 *buf = (u8 *) 1; -+ -+ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); -+ -+ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { -+ dev->temp_buffer[i].in_use = 0; -+ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ dev->temp_buffer[i].buffer = buf; -+ } -+ -+ return buf ? YAFFS_OK : YAFFS_FAIL; -+} -+ -+u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) -+{ -+ int i; -+ -+ dev->temp_in_use++; -+ if (dev->temp_in_use > dev->max_temp) -+ dev->max_temp = dev->temp_in_use; -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ if (dev->temp_buffer[i].in_use == 0) { -+ dev->temp_buffer[i].in_use = 1; -+ return dev->temp_buffer[i].buffer; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); -+ /* -+ * If we got here then we have to allocate an unmanaged one -+ * This is not good. -+ */ -+ -+ dev->unmanaged_buffer_allocs++; -+ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); -+ -+} -+ -+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) -+{ -+ int i; -+ -+ dev->temp_in_use--; -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ if (dev->temp_buffer[i].buffer == buffer) { -+ dev->temp_buffer[i].in_use = 0; -+ return; -+ } -+ } -+ -+ if (buffer) { -+ /* assume it is an unmanaged one. */ -+ yaffs_trace(YAFFS_TRACE_BUFFERS, -+ "Releasing unmanaged temp buffer"); -+ kfree(buffer); -+ dev->unmanaged_buffer_deallocs++; -+ } -+ -+} -+ -+/* -+ * Functions for robustisizing TODO -+ * -+ */ -+ -+static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, -+ const struct yaffs_ext_tags *tags) -+{ -+ (void) dev; -+ (void) nand_chunk; -+ (void) data; -+ (void) tags; -+} -+ -+static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, -+ const struct yaffs_ext_tags *tags) -+{ -+ (void) dev; -+ (void) nand_chunk; -+ (void) tags; -+} -+ -+void yaffs_handle_chunk_error(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi) -+{ -+ if (!bi->gc_prioritise) { -+ bi->gc_prioritise = 1; -+ dev->has_pending_prioritised_gc = 1; -+ bi->chunk_error_strikes++; -+ -+ if (bi->chunk_error_strikes > 3) { -+ bi->needs_retiring = 1; /* Too many stikes, so retire */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Block struck out"); -+ -+ } -+ } -+} -+ -+static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, -+ int erased_ok) -+{ -+ int flash_block = nand_chunk / dev->param.chunks_per_block; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -+ -+ yaffs_handle_chunk_error(dev, bi); -+ -+ if (erased_ok) { -+ /* Was an actual write failure, -+ * so mark the block for retirement.*/ -+ bi->needs_retiring = 1; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Block %d needs retiring", flash_block); -+ } -+ -+ /* Delete the chunk */ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ yaffs_skip_rest_of_block(dev); -+} -+ -+/* -+ * Verification code -+ */ -+ -+/* -+ * Simple hash function. Needs to have a reasonable spread -+ */ -+ -+static inline int yaffs_hash_fn(int n) -+{ -+ if (n < 0) -+ n = -n; -+ return n % YAFFS_NOBJECT_BUCKETS; -+} -+ -+/* -+ * Access functions to useful fake objects. -+ * Note that root might have a presence in NAND if permissions are set. -+ */ -+ -+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) -+{ -+ return dev->root_dir; -+} -+ -+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) -+{ -+ return dev->lost_n_found; -+} -+ -+/* -+ * Erased NAND checking functions -+ */ -+ -+int yaffs_check_ff(u8 *buffer, int n_bytes) -+{ -+ /* Horrible, slow implementation */ -+ while (n_bytes--) { -+ if (*buffer != 0xff) -+ return 0; -+ buffer++; -+ } -+ return 1; -+} -+ -+static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) -+{ -+ int retval = YAFFS_OK; -+ u8 *data = yaffs_get_temp_buffer(dev); -+ struct yaffs_ext_tags tags; -+ int result; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); -+ -+ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) -+ retval = YAFFS_FAIL; -+ -+ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || -+ tags.chunk_used) { -+ yaffs_trace(YAFFS_TRACE_NANDACCESS, -+ "Chunk %d not erased", nand_chunk); -+ retval = YAFFS_FAIL; -+ } -+ -+ yaffs_release_temp_buffer(dev, data); -+ -+ return retval; -+ -+} -+ -+static int yaffs_verify_chunk_written(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, -+ struct yaffs_ext_tags *tags) -+{ -+ int retval = YAFFS_OK; -+ struct yaffs_ext_tags temp_tags; -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ int result; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); -+ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || -+ temp_tags.obj_id != tags->obj_id || -+ temp_tags.chunk_id != tags->chunk_id || -+ temp_tags.n_bytes != tags->n_bytes) -+ retval = YAFFS_FAIL; -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ return retval; -+} -+ -+ -+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) -+{ -+ int reserved_chunks; -+ int reserved_blocks = dev->param.n_reserved_blocks; -+ int checkpt_blocks; -+ -+ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); -+ -+ reserved_chunks = -+ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; -+ -+ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); -+} -+ -+static int yaffs_find_alloc_block(struct yaffs_dev *dev) -+{ -+ int i; -+ struct yaffs_block_info *bi; -+ -+ if (dev->n_erased_blocks < 1) { -+ /* Hoosterman we've got a problem. -+ * Can't get space to gc -+ */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: no more erased blocks"); -+ -+ return -1; -+ } -+ -+ /* Find an empty block. */ -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ dev->alloc_block_finder++; -+ if (dev->alloc_block_finder < dev->internal_start_block -+ || dev->alloc_block_finder > dev->internal_end_block) { -+ dev->alloc_block_finder = dev->internal_start_block; -+ } -+ -+ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->seq_number++; -+ bi->seq_number = dev->seq_number; -+ dev->n_erased_blocks--; -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, -+ "Allocated block %d, seq %d, %d left" , -+ dev->alloc_block_finder, dev->seq_number, -+ dev->n_erased_blocks); -+ return dev->alloc_block_finder; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs tragedy: no more erased blocks, but there should have been %d", -+ dev->n_erased_blocks); -+ -+ return -1; -+} -+ -+static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, -+ struct yaffs_block_info **block_ptr) -+{ -+ int ret_val; -+ struct yaffs_block_info *bi; -+ -+ if (dev->alloc_block < 0) { -+ /* Get next block to allocate off */ -+ dev->alloc_block = yaffs_find_alloc_block(dev); -+ dev->alloc_page = 0; -+ } -+ -+ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { -+ /* No space unless we're allowed to use the reserve. */ -+ return -1; -+ } -+ -+ if (dev->n_erased_blocks < dev->param.n_reserved_blocks -+ && dev->alloc_page == 0) -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); -+ -+ /* Next page please.... */ -+ if (dev->alloc_block >= 0) { -+ bi = yaffs_get_block_info(dev, dev->alloc_block); -+ -+ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + -+ dev->alloc_page; -+ bi->pages_in_use++; -+ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); -+ -+ dev->alloc_page++; -+ -+ dev->n_free_chunks--; -+ -+ /* If the block is full set the state to full */ -+ if (dev->alloc_page >= dev->param.chunks_per_block) { -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ -+ if (block_ptr) -+ *block_ptr = bi; -+ -+ return ret_val; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); -+ -+ return -1; -+} -+ -+static int yaffs_get_erased_chunks(struct yaffs_dev *dev) -+{ -+ int n; -+ -+ n = dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ if (dev->alloc_block > 0) -+ n += (dev->param.chunks_per_block - dev->alloc_page); -+ -+ return n; -+ -+} -+ -+/* -+ * yaffs_skip_rest_of_block() skips over the rest of the allocation block -+ * if we don't want to write to it. -+ */ -+void yaffs_skip_rest_of_block(struct yaffs_dev *dev) -+{ -+ struct yaffs_block_info *bi; -+ -+ if (dev->alloc_block > 0) { -+ bi = yaffs_get_block_info(dev, dev->alloc_block); -+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ } -+} -+ -+static int yaffs_write_new_chunk(struct yaffs_dev *dev, -+ const u8 *data, -+ struct yaffs_ext_tags *tags, int use_reserver) -+{ -+ int attempts = 0; -+ int write_ok = 0; -+ int chunk; -+ -+ yaffs2_checkpt_invalidate(dev); -+ -+ do { -+ struct yaffs_block_info *bi = 0; -+ int erased_ok = 0; -+ -+ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); -+ if (chunk < 0) { -+ /* no space */ -+ break; -+ } -+ -+ /* First check this chunk is erased, if it needs -+ * checking. The checking policy (unless forced -+ * always on) is as follows: -+ * -+ * Check the first page we try to write in a block. -+ * If the check passes then we don't need to check any -+ * more. If the check fails, we check again... -+ * If the block has been erased, we don't need to check. -+ * -+ * However, if the block has been prioritised for gc, -+ * then we think there might be something odd about -+ * this block and stop using it. -+ * -+ * Rationale: We should only ever see chunks that have -+ * not been erased if there was a partially written -+ * chunk due to power loss. This checking policy should -+ * catch that case with very few checks and thus save a -+ * lot of checks that are most likely not needed. -+ * -+ * Mods to the above -+ * If an erase check fails or the write fails we skip the -+ * rest of the block. -+ */ -+ -+ /* let's give it a try */ -+ attempts++; -+ -+ if (dev->param.always_check_erased) -+ bi->skip_erased_check = 0; -+ -+ if (!bi->skip_erased_check) { -+ erased_ok = yaffs_check_chunk_erased(dev, chunk); -+ if (erased_ok != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs chunk %d was not erased", -+ chunk); -+ -+ /* If not erased, delete this one, -+ * skip rest of block and -+ * try another chunk */ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ yaffs_skip_rest_of_block(dev); -+ continue; -+ } -+ } -+ -+ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); -+ -+ if (!bi->skip_erased_check) -+ write_ok = -+ yaffs_verify_chunk_written(dev, chunk, data, tags); -+ -+ if (write_ok != YAFFS_OK) { -+ /* Clean up aborted write, skip to next block and -+ * try another chunk */ -+ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); -+ continue; -+ } -+ -+ bi->skip_erased_check = 1; -+ -+ /* Copy the data into the robustification buffer */ -+ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); -+ -+ } while (write_ok != YAFFS_OK && -+ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); -+ -+ if (!write_ok) -+ chunk = -1; -+ -+ if (attempts > 1) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs write required %d attempts", -+ attempts); -+ dev->n_retried_writes += (attempts - 1); -+ } -+ -+ return chunk; -+} -+ -+/* -+ * Block retiring for handling a broken block. -+ */ -+ -+static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -+ -+ yaffs2_checkpt_invalidate(dev); -+ -+ yaffs2_clear_oldest_dirty_seq(dev, bi); -+ -+ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { -+ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Failed to mark bad and erase block %d", -+ flash_block); -+ } else { -+ struct yaffs_ext_tags tags; -+ int chunk_id = -+ flash_block * dev->param.chunks_per_block; -+ -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ memset(buffer, 0xff, dev->data_bytes_per_chunk); -+ memset(&tags, 0, sizeof(tags)); -+ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; -+ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - -+ dev->chunk_offset, -+ buffer, -+ &tags) != YAFFS_OK) -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Failed to write bad block marker to block %d", -+ flash_block); -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ } -+ -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ bi->gc_prioritise = 0; -+ bi->needs_retiring = 0; -+ -+ dev->n_retired_blocks++; -+} -+ -+/*---------------- Name handling functions ------------*/ -+ -+static u16 yaffs_calc_name_sum(const YCHAR *name) -+{ -+ u16 sum = 0; -+ u16 i = 1; -+ -+ if (!name) -+ return 0; -+ -+ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { -+ -+ /* 0x1f mask is case insensitive */ -+ sum += ((*name) & 0x1f) * i; -+ i++; -+ name++; -+ } -+ return sum; -+} -+ -+ -+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) -+{ -+ memset(obj->short_name, 0, sizeof(obj->short_name)); -+ -+ if (name && !name[0]) { -+ yaffs_fix_null_name(obj, obj->short_name, -+ YAFFS_SHORT_NAME_LENGTH); -+ name = obj->short_name; -+ } else if (name && -+ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= -+ YAFFS_SHORT_NAME_LENGTH) { -+ strcpy(obj->short_name, name); -+ } -+ -+ obj->sum = yaffs_calc_name_sum(name); -+} -+ -+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, -+ const struct yaffs_obj_hdr *oh) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; -+ memset(tmp_name, 0, sizeof(tmp_name)); -+ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, -+ YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_set_obj_name(obj, tmp_name); -+#else -+ yaffs_set_obj_name(obj, oh->name); -+#endif -+} -+ -+loff_t yaffs_max_file_size(struct yaffs_dev *dev) -+{ -+ if(sizeof(loff_t) < 8) -+ return YAFFS_MAX_FILE_SIZE_32; -+ else -+ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; -+} -+ -+/*-------------------- TNODES ------------------- -+ -+ * List of spare tnodes -+ * The list is hooked together using the first pointer -+ * in the tnode. -+ */ -+ -+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) -+{ -+ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); -+ -+ if (tn) { -+ memset(tn, 0, dev->tnode_size); -+ dev->n_tnodes++; -+ } -+ -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+ -+ return tn; -+} -+ -+/* FreeTnode frees up a tnode and puts it back on the free list */ -+static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) -+{ -+ yaffs_free_raw_tnode(dev, tn); -+ dev->n_tnodes--; -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ yaffs_deinit_raw_tnodes_and_objs(dev); -+ dev->n_obj = 0; -+ dev->n_tnodes = 0; -+} -+ -+static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos, unsigned val) -+{ -+ u32 *map = (u32 *) tn; -+ u32 bit_in_map; -+ u32 bit_in_word; -+ u32 word_in_map; -+ u32 mask; -+ -+ pos &= YAFFS_TNODES_LEVEL0_MASK; -+ val >>= dev->chunk_grp_bits; -+ -+ bit_in_map = pos * dev->tnode_width; -+ word_in_map = bit_in_map / 32; -+ bit_in_word = bit_in_map & (32 - 1); -+ -+ mask = dev->tnode_mask << bit_in_word; -+ -+ map[word_in_map] &= ~mask; -+ map[word_in_map] |= (mask & (val << bit_in_word)); -+ -+ if (dev->tnode_width > (32 - bit_in_word)) { -+ bit_in_word = (32 - bit_in_word); -+ word_in_map++; -+ mask = -+ dev->tnode_mask >> bit_in_word; -+ map[word_in_map] &= ~mask; -+ map[word_in_map] |= (mask & (val >> bit_in_word)); -+ } -+} -+ -+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos) -+{ -+ u32 *map = (u32 *) tn; -+ u32 bit_in_map; -+ u32 bit_in_word; -+ u32 word_in_map; -+ u32 val; -+ -+ pos &= YAFFS_TNODES_LEVEL0_MASK; -+ -+ bit_in_map = pos * dev->tnode_width; -+ word_in_map = bit_in_map / 32; -+ bit_in_word = bit_in_map & (32 - 1); -+ -+ val = map[word_in_map] >> bit_in_word; -+ -+ if (dev->tnode_width > (32 - bit_in_word)) { -+ bit_in_word = (32 - bit_in_word); -+ word_in_map++; -+ val |= (map[word_in_map] << bit_in_word); -+ } -+ -+ val &= dev->tnode_mask; -+ val <<= dev->chunk_grp_bits; -+ -+ return val; -+} -+ -+/* ------------------- End of individual tnode manipulation -----------------*/ -+ -+/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ -+ * The look up tree is represented by the top tnode and the number of top_level -+ * in the tree. 0 means only the level 0 tnode is in the tree. -+ */ -+ -+/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ -+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id) -+{ -+ struct yaffs_tnode *tn = file_struct->top; -+ u32 i; -+ int required_depth; -+ int level = file_struct->top_level; -+ -+ (void) dev; -+ -+ /* Check sane level and chunk Id */ -+ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) -+ return NULL; -+ -+ if (chunk_id > YAFFS_MAX_CHUNK_ID) -+ return NULL; -+ -+ /* First check we're tall enough (ie enough top_level) */ -+ -+ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (i) { -+ i >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ if (required_depth > file_struct->top_level) -+ return NULL; /* Not tall enough, so we can't find it */ -+ -+ /* Traverse down to level 0 */ -+ while (level > 0 && tn) { -+ tn = tn->internal[(chunk_id >> -+ (YAFFS_TNODES_LEVEL0_BITS + -+ (level - 1) * -+ YAFFS_TNODES_INTERNAL_BITS)) & -+ YAFFS_TNODES_INTERNAL_MASK]; -+ level--; -+ } -+ -+ return tn; -+} -+ -+/* add_find_tnode_0 finds the level 0 tnode if it exists, -+ * otherwise first expands the tree. -+ * This happens in two steps: -+ * 1. If the tree isn't tall enough, then make it taller. -+ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. -+ * -+ * Used when modifying the tree. -+ * -+ * If the tn argument is NULL, then a fresh tnode will be added otherwise the -+ * specified tn will be plugged into the ttree. -+ */ -+ -+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id, -+ struct yaffs_tnode *passed_tn) -+{ -+ int required_depth; -+ int i; -+ int l; -+ struct yaffs_tnode *tn; -+ u32 x; -+ -+ /* Check sane level and page Id */ -+ if (file_struct->top_level < 0 || -+ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) -+ return NULL; -+ -+ if (chunk_id > YAFFS_MAX_CHUNK_ID) -+ return NULL; -+ -+ /* First check we're tall enough (ie enough top_level) */ -+ -+ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (x) { -+ x >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ if (required_depth > file_struct->top_level) { -+ /* Not tall enough, gotta make the tree taller */ -+ for (i = file_struct->top_level; i < required_depth; i++) { -+ -+ tn = yaffs_get_tnode(dev); -+ -+ if (tn) { -+ tn->internal[0] = file_struct->top; -+ file_struct->top = tn; -+ file_struct->top_level++; -+ } else { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs: no more tnodes"); -+ return NULL; -+ } -+ } -+ } -+ -+ /* Traverse down to level 0, adding anything we need */ -+ -+ l = file_struct->top_level; -+ tn = file_struct->top; -+ -+ if (l > 0) { -+ while (l > 0 && tn) { -+ x = (chunk_id >> -+ (YAFFS_TNODES_LEVEL0_BITS + -+ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & -+ YAFFS_TNODES_INTERNAL_MASK; -+ -+ if ((l > 1) && !tn->internal[x]) { -+ /* Add missing non-level-zero tnode */ -+ tn->internal[x] = yaffs_get_tnode(dev); -+ if (!tn->internal[x]) -+ return NULL; -+ } else if (l == 1) { -+ /* Looking from level 1 at level 0 */ -+ if (passed_tn) { -+ /* If we already have one, release it */ -+ if (tn->internal[x]) -+ yaffs_free_tnode(dev, -+ tn->internal[x]); -+ tn->internal[x] = passed_tn; -+ -+ } else if (!tn->internal[x]) { -+ /* Don't have one, none passed in */ -+ tn->internal[x] = yaffs_get_tnode(dev); -+ if (!tn->internal[x]) -+ return NULL; -+ } -+ } -+ -+ tn = tn->internal[x]; -+ l--; -+ } -+ } else { -+ /* top is level 0 */ -+ if (passed_tn) { -+ memcpy(tn, passed_tn, -+ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); -+ yaffs_free_tnode(dev, passed_tn); -+ } -+ } -+ -+ return tn; -+} -+ -+static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, -+ int chunk_obj) -+{ -+ return (tags->chunk_id == chunk_obj && -+ tags->obj_id == obj_id && -+ !tags->is_deleted) ? 1 : 0; -+ -+} -+ -+static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, -+ struct yaffs_ext_tags *tags, int obj_id, -+ int inode_chunk) -+{ -+ int j; -+ -+ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { -+ if (yaffs_check_chunk_bit -+ (dev, the_chunk / dev->param.chunks_per_block, -+ the_chunk % dev->param.chunks_per_block)) { -+ -+ if (dev->chunk_grp_size == 1) -+ return the_chunk; -+ else { -+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, -+ tags); -+ if (yaffs_tags_match(tags, -+ obj_id, inode_chunk)) { -+ /* found it; */ -+ return the_chunk; -+ } -+ } -+ } -+ the_chunk++; -+ } -+ return -1; -+} -+ -+int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags) -+{ -+ /*Get the Tnode, then get the level 0 offset chunk offset */ -+ struct yaffs_tnode *tn; -+ int the_chunk = -1; -+ struct yaffs_ext_tags local_tags; -+ int ret_val = -1; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!tags) { -+ /* Passed a NULL, so use our own tags space */ -+ tags = &local_tags; -+ } -+ -+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -+ -+ if (!tn) -+ return ret_val; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, -+ inode_chunk); -+ return ret_val; -+} -+ -+static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags) -+{ -+ /* Get the Tnode, then get the level 0 offset chunk offset */ -+ struct yaffs_tnode *tn; -+ int the_chunk = -1; -+ struct yaffs_ext_tags local_tags; -+ struct yaffs_dev *dev = in->my_dev; -+ int ret_val = -1; -+ -+ if (!tags) { -+ /* Passed a NULL, so use our own tags space */ -+ tags = &local_tags; -+ } -+ -+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -+ -+ if (!tn) -+ return ret_val; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, -+ inode_chunk); -+ -+ /* Delete the entry in the filestructure (if found) */ -+ if (ret_val != -1) -+ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); -+ -+ return ret_val; -+} -+ -+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ int nand_chunk, int in_scan) -+{ -+ /* NB in_scan is zero unless scanning. -+ * For forward scanning, in_scan is > 0; -+ * for backward scanning in_scan is < 0 -+ * -+ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. -+ */ -+ -+ struct yaffs_tnode *tn; -+ struct yaffs_dev *dev = in->my_dev; -+ int existing_cunk; -+ struct yaffs_ext_tags existing_tags; -+ struct yaffs_ext_tags new_tags; -+ unsigned existing_serial, new_serial; -+ -+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { -+ /* Just ignore an attempt at putting a chunk into a non-file -+ * during scanning. -+ * If it is not during Scanning then something went wrong! -+ */ -+ if (!in_scan) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy:attempt to put data chunk into a non-file" -+ ); -+ BUG(); -+ } -+ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ return YAFFS_OK; -+ } -+ -+ tn = yaffs_add_find_tnode_0(dev, -+ &in->variant.file_variant, -+ inode_chunk, NULL); -+ if (!tn) -+ return YAFFS_FAIL; -+ -+ if (!nand_chunk) -+ /* Dummy insert, bail now */ -+ return YAFFS_OK; -+ -+ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ if (in_scan != 0) { -+ /* If we're scanning then we need to test for duplicates -+ * NB This does not need to be efficient since it should only -+ * happen when the power fails during a write, then only one -+ * chunk should ever be affected. -+ * -+ * Correction for YAFFS2: This could happen quite a lot and we -+ * need to think about efficiency! TODO -+ * Update: For backward scanning we don't need to re-read tags -+ * so this is quite cheap. -+ */ -+ -+ if (existing_cunk > 0) { -+ /* NB Right now existing chunk will not be real -+ * chunk_id if the chunk group size > 1 -+ * thus we have to do a FindChunkInFile to get the -+ * real chunk id. -+ * -+ * We have a duplicate now we need to decide which -+ * one to use: -+ * -+ * Backwards scanning YAFFS2: The old one is what -+ * we use, dump the new one. -+ * YAFFS1: Get both sets of tags and compare serial -+ * numbers. -+ */ -+ -+ if (in_scan > 0) { -+ /* Only do this for forward scanning */ -+ yaffs_rd_chunk_tags_nand(dev, -+ nand_chunk, -+ NULL, &new_tags); -+ -+ /* Do a proper find */ -+ existing_cunk = -+ yaffs_find_chunk_in_file(in, inode_chunk, -+ &existing_tags); -+ } -+ -+ if (existing_cunk <= 0) { -+ /*Hoosterman - how did this happen? */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: existing chunk < 0 in scan" -+ ); -+ -+ } -+ -+ /* NB The deleted flags should be false, otherwise -+ * the chunks will not be loaded during a scan -+ */ -+ -+ if (in_scan > 0) { -+ new_serial = new_tags.serial_number; -+ existing_serial = existing_tags.serial_number; -+ } -+ -+ if ((in_scan > 0) && -+ (existing_cunk <= 0 || -+ ((existing_serial + 1) & 3) == new_serial)) { -+ /* Forward scanning. -+ * Use new -+ * Delete the old one and drop through to -+ * update the tnode -+ */ -+ yaffs_chunk_del(dev, existing_cunk, 1, -+ __LINE__); -+ } else { -+ /* Backward scanning or we want to use the -+ * existing one -+ * Delete the new one and return early so that -+ * the tnode isn't changed -+ */ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ return YAFFS_OK; -+ } -+ } -+ -+ } -+ -+ if (existing_cunk == 0) -+ in->n_data_chunks++; -+ -+ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); -+ -+ return YAFFS_OK; -+} -+ -+static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) -+{ -+ struct yaffs_block_info *the_block; -+ unsigned block_no; -+ -+ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); -+ -+ block_no = chunk / dev->param.chunks_per_block; -+ the_block = yaffs_get_block_info(dev, block_no); -+ if (the_block) { -+ the_block->soft_del_pages++; -+ dev->n_free_chunks++; -+ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); -+ } -+} -+ -+/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all -+ * the chunks in the file. -+ * All soft deleting does is increment the block's softdelete count and pulls -+ * the chunk out of the tnode. -+ * Thus, essentially this is the same as DeleteWorker except that the chunks -+ * are soft deleted. -+ */ -+ -+static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, -+ u32 level, int chunk_offset) -+{ -+ int i; -+ int the_chunk; -+ int all_done = 1; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!tn) -+ return 1; -+ -+ if (level > 0) { -+ for (i = YAFFS_NTNODES_INTERNAL - 1; -+ all_done && i >= 0; -+ i--) { -+ if (tn->internal[i]) { -+ all_done = -+ yaffs_soft_del_worker(in, -+ tn->internal[i], -+ level - 1, -+ (chunk_offset << -+ YAFFS_TNODES_INTERNAL_BITS) -+ + i); -+ if (all_done) { -+ yaffs_free_tnode(dev, -+ tn->internal[i]); -+ tn->internal[i] = NULL; -+ } else { -+ /* Can this happen? */ -+ } -+ } -+ } -+ return (all_done) ? 1 : 0; -+ } -+ -+ /* level 0 */ -+ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { -+ the_chunk = yaffs_get_group_base(dev, tn, i); -+ if (the_chunk) { -+ yaffs_soft_del_chunk(dev, the_chunk); -+ yaffs_load_tnode_0(dev, tn, i, 0); -+ } -+ } -+ return 1; -+} -+ -+static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ struct yaffs_obj *parent; -+ -+ yaffs_verify_obj_in_dir(obj); -+ parent = obj->parent; -+ -+ yaffs_verify_dir(parent); -+ -+ if (dev && dev->param.remove_obj_fn) -+ dev->param.remove_obj_fn(obj); -+ -+ list_del_init(&obj->siblings); -+ obj->parent = NULL; -+ -+ yaffs_verify_dir(parent); -+} -+ -+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) -+{ -+ if (!directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: Trying to add an object to a null pointer directory" -+ ); -+ BUG(); -+ return; -+ } -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: Trying to add an object to a non-directory" -+ ); -+ BUG(); -+ } -+ -+ if (obj->siblings.prev == NULL) { -+ /* Not initialised */ -+ BUG(); -+ } -+ -+ yaffs_verify_dir(directory); -+ -+ yaffs_remove_obj_from_dir(obj); -+ -+ /* Now add it */ -+ list_add(&obj->siblings, &directory->variant.dir_variant.children); -+ obj->parent = directory; -+ -+ if (directory == obj->my_dev->unlinked_dir -+ || directory == obj->my_dev->del_dir) { -+ obj->unlinked = 1; -+ obj->my_dev->n_unlinked_files++; -+ obj->rename_allowed = 0; -+ } -+ -+ yaffs_verify_dir(directory); -+ yaffs_verify_obj_in_dir(obj); -+} -+ -+static int yaffs_change_obj_name(struct yaffs_obj *obj, -+ struct yaffs_obj *new_dir, -+ const YCHAR *new_name, int force, int shadows) -+{ -+ int unlink_op; -+ int del_op; -+ struct yaffs_obj *existing_target; -+ -+ if (new_dir == NULL) -+ new_dir = obj->parent; /* use the old directory */ -+ -+ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_change_obj_name: new_dir is not a directory" -+ ); -+ BUG(); -+ } -+ -+ unlink_op = (new_dir == obj->my_dev->unlinked_dir); -+ del_op = (new_dir == obj->my_dev->del_dir); -+ -+ existing_target = yaffs_find_by_name(new_dir, new_name); -+ -+ /* If the object is a file going into the unlinked directory, -+ * then it is OK to just stuff it in since duplicate names are OK. -+ * else only proceed if the new name does not exist and we're putting -+ * it into a directory. -+ */ -+ if (!(unlink_op || del_op || force || -+ shadows > 0 || !existing_target) || -+ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) -+ return YAFFS_FAIL; -+ -+ yaffs_set_obj_name(obj, new_name); -+ obj->dirty = 1; -+ yaffs_add_obj_to_dir(new_dir, obj); -+ -+ if (unlink_op) -+ obj->unlinked = 1; -+ -+ /* If it is a deletion then we mark it as a shrink for gc */ -+ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+/*------------------------ Short Operations Cache ------------------------------ -+ * In many situations where there is no high level buffering a lot of -+ * reads might be short sequential reads, and a lot of writes may be short -+ * sequential writes. eg. scanning/writing a jpeg file. -+ * In these cases, a short read/write cache can provide a huge perfomance -+ * benefit with dumb-as-a-rock code. -+ * In Linux, the page cache provides read buffering and the short op cache -+ * provides write buffering. -+ * -+ * There are a small number (~10) of cache chunks per device so that we don't -+ * need a very intelligent search. -+ */ -+ -+static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ struct yaffs_cache *cache; -+ int n_caches = obj->my_dev->param.n_caches; -+ -+ for (i = 0; i < n_caches; i++) { -+ cache = &dev->cache[i]; -+ if (cache->object == obj && cache->dirty) -+ return 1; -+ } -+ -+ return 0; -+} -+ -+static void yaffs_flush_file_cache(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int lowest = -99; /* Stop compiler whining. */ -+ int i; -+ struct yaffs_cache *cache; -+ int chunk_written = 0; -+ int n_caches = obj->my_dev->param.n_caches; -+ -+ if (n_caches < 1) -+ return; -+ do { -+ cache = NULL; -+ -+ /* Find the lowest dirty chunk for this object */ -+ for (i = 0; i < n_caches; i++) { -+ if (dev->cache[i].object == obj && -+ dev->cache[i].dirty) { -+ if (!cache || -+ dev->cache[i].chunk_id < lowest) { -+ cache = &dev->cache[i]; -+ lowest = cache->chunk_id; -+ } -+ } -+ } -+ -+ if (cache && !cache->locked) { -+ /* Write it out and free it up */ -+ chunk_written = -+ yaffs_wr_data_obj(cache->object, -+ cache->chunk_id, -+ cache->data, -+ cache->n_bytes, 1); -+ cache->dirty = 0; -+ cache->object = NULL; -+ } -+ } while (cache && chunk_written > 0); -+ -+ if (cache) -+ /* Hoosterman, disk full while writing cache out. */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: no space during cache write"); -+} -+ -+/*yaffs_flush_whole_cache(dev) -+ * -+ * -+ */ -+ -+void yaffs_flush_whole_cache(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ int n_caches = dev->param.n_caches; -+ int i; -+ -+ /* Find a dirty object in the cache and flush it... -+ * until there are no further dirty objects. -+ */ -+ do { -+ obj = NULL; -+ for (i = 0; i < n_caches && !obj; i++) { -+ if (dev->cache[i].object && dev->cache[i].dirty) -+ obj = dev->cache[i].object; -+ } -+ if (obj) -+ yaffs_flush_file_cache(obj); -+ } while (obj); -+ -+} -+ -+/* Grab us a cache chunk for use. -+ * First look for an empty one. -+ * Then look for the least recently used non-dirty one. -+ * Then look for the least recently used dirty one...., flush and look again. -+ */ -+static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (dev->param.n_caches > 0) { -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (!dev->cache[i].object) -+ return &dev->cache[i]; -+ } -+ } -+ return NULL; -+} -+ -+static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) -+{ -+ struct yaffs_cache *cache; -+ struct yaffs_obj *the_obj; -+ int usage; -+ int i; -+ int pushout; -+ -+ if (dev->param.n_caches < 1) -+ return NULL; -+ -+ /* Try find a non-dirty one... */ -+ -+ cache = yaffs_grab_chunk_worker(dev); -+ -+ if (!cache) { -+ /* They were all dirty, find the LRU object and flush -+ * its cache, then find again. -+ * NB what's here is not very accurate, -+ * we actually flush the object with the LRU chunk. -+ */ -+ -+ /* With locking we can't assume we can use entry zero, -+ * Set the_obj to a valid pointer for Coverity. */ -+ the_obj = dev->cache[0].object; -+ usage = -1; -+ cache = NULL; -+ pushout = -1; -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object && -+ !dev->cache[i].locked && -+ (dev->cache[i].last_use < usage || -+ !cache)) { -+ usage = dev->cache[i].last_use; -+ the_obj = dev->cache[i].object; -+ cache = &dev->cache[i]; -+ pushout = i; -+ } -+ } -+ -+ if (!cache || cache->dirty) { -+ /* Flush and try again */ -+ yaffs_flush_file_cache(the_obj); -+ cache = yaffs_grab_chunk_worker(dev); -+ } -+ } -+ return cache; -+} -+ -+/* Find a cached chunk */ -+static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, -+ int chunk_id) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return NULL; -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object == obj && -+ dev->cache[i].chunk_id == chunk_id) { -+ dev->cache_hits++; -+ -+ return &dev->cache[i]; -+ } -+ } -+ return NULL; -+} -+ -+/* Mark the chunk for the least recently used algorithym */ -+static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, -+ int is_write) -+{ -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return; -+ -+ if (dev->cache_last_use < 0 || -+ dev->cache_last_use > 100000000) { -+ /* Reset the cache usages */ -+ for (i = 1; i < dev->param.n_caches; i++) -+ dev->cache[i].last_use = 0; -+ -+ dev->cache_last_use = 0; -+ } -+ dev->cache_last_use++; -+ cache->last_use = dev->cache_last_use; -+ -+ if (is_write) -+ cache->dirty = 1; -+} -+ -+/* Invalidate a single cache page. -+ * Do this when a whole page gets written, -+ * ie the short cache for this page is no longer valid. -+ */ -+static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) -+{ -+ struct yaffs_cache *cache; -+ -+ if (object->my_dev->param.n_caches > 0) { -+ cache = yaffs_find_chunk_cache(object, chunk_id); -+ -+ if (cache) -+ cache->object = NULL; -+ } -+} -+ -+/* Invalidate all the cache pages associated with this object -+ * Do this whenever ther file is deleted or resized. -+ */ -+static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) -+{ -+ int i; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (dev->param.n_caches > 0) { -+ /* Invalidate it. */ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object == in) -+ dev->cache[i].object = NULL; -+ } -+ } -+} -+ -+static void yaffs_unhash_obj(struct yaffs_obj *obj) -+{ -+ int bucket; -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ /* If it is still linked into the bucket list, free from the list */ -+ if (!list_empty(&obj->hash_link)) { -+ list_del_init(&obj->hash_link); -+ bucket = yaffs_hash_fn(obj->obj_id); -+ dev->obj_bucket[bucket].count--; -+ } -+} -+ -+/* FreeObject frees up a Object and puts it back on the free list */ -+static void yaffs_free_obj(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ -+ if (!obj) { -+ BUG(); -+ return; -+ } -+ dev = obj->my_dev; -+ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", -+ obj, obj->my_inode); -+ if (obj->parent) -+ BUG(); -+ if (!list_empty(&obj->siblings)) -+ BUG(); -+ -+ if (obj->my_inode) { -+ /* We're still hooked up to a cached inode. -+ * Don't delete now, but mark for later deletion -+ */ -+ obj->defered_free = 1; -+ return; -+ } -+ -+ yaffs_unhash_obj(obj); -+ -+ yaffs_free_raw_obj(dev, obj); -+ dev->n_obj--; -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+void yaffs_handle_defered_free(struct yaffs_obj *obj) -+{ -+ if (obj->defered_free) -+ yaffs_free_obj(obj); -+} -+ -+static int yaffs_generic_obj_del(struct yaffs_obj *in) -+{ -+ /* Iinvalidate the file's data in the cache, without flushing. */ -+ yaffs_invalidate_whole_cache(in); -+ -+ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { -+ /* Move to unlinked directory so we have a deletion record */ -+ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, -+ 0); -+ } -+ -+ yaffs_remove_obj_from_dir(in); -+ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); -+ in->hdr_chunk = 0; -+ -+ yaffs_free_obj(in); -+ return YAFFS_OK; -+ -+} -+ -+static void yaffs_soft_del_file(struct yaffs_obj *obj) -+{ -+ if (!obj->deleted || -+ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || -+ obj->soft_del) -+ return; -+ -+ if (obj->n_data_chunks <= 0) { -+ /* Empty file with no duplicate object headers, -+ * just delete it immediately */ -+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); -+ obj->variant.file_variant.top = NULL; -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: Deleting empty file %d", -+ obj->obj_id); -+ yaffs_generic_obj_del(obj); -+ } else { -+ yaffs_soft_del_worker(obj, -+ obj->variant.file_variant.top, -+ obj->variant. -+ file_variant.top_level, 0); -+ obj->soft_del = 1; -+ } -+} -+ -+/* Pruning removes any part of the file structure tree that is beyond the -+ * bounds of the file (ie that does not point to chunks). -+ * -+ * A file should only get pruned when its size is reduced. -+ * -+ * Before pruning, the chunks must be pulled from the tree and the -+ * level 0 tnode entries must be zeroed out. -+ * Could also use this for file deletion, but that's probably better handled -+ * by a special case. -+ * -+ * This function is recursive. For levels > 0 the function is called again on -+ * any sub-tree. For level == 0 we just check if the sub-tree has data. -+ * If there is no data in a subtree then it is pruned. -+ */ -+ -+static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, -+ struct yaffs_tnode *tn, u32 level, -+ int del0) -+{ -+ int i; -+ int has_data; -+ -+ if (!tn) -+ return tn; -+ -+ has_data = 0; -+ -+ if (level > 0) { -+ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { -+ if (tn->internal[i]) { -+ tn->internal[i] = -+ yaffs_prune_worker(dev, -+ tn->internal[i], -+ level - 1, -+ (i == 0) ? del0 : 1); -+ } -+ -+ if (tn->internal[i]) -+ has_data++; -+ } -+ } else { -+ int tnode_size_u32 = dev->tnode_size / sizeof(u32); -+ u32 *map = (u32 *) tn; -+ -+ for (i = 0; !has_data && i < tnode_size_u32; i++) { -+ if (map[i]) -+ has_data++; -+ } -+ } -+ -+ if (has_data == 0 && del0) { -+ /* Free and return NULL */ -+ yaffs_free_tnode(dev, tn); -+ tn = NULL; -+ } -+ return tn; -+} -+ -+static int yaffs_prune_tree(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct) -+{ -+ int i; -+ int has_data; -+ int done = 0; -+ struct yaffs_tnode *tn; -+ -+ if (file_struct->top_level < 1) -+ return YAFFS_OK; -+ -+ file_struct->top = -+ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); -+ -+ /* Now we have a tree with all the non-zero branches NULL but -+ * the height is the same as it was. -+ * Let's see if we can trim internal tnodes to shorten the tree. -+ * We can do this if only the 0th element in the tnode is in use -+ * (ie all the non-zero are NULL) -+ */ -+ -+ while (file_struct->top_level && !done) { -+ tn = file_struct->top; -+ -+ has_data = 0; -+ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { -+ if (tn->internal[i]) -+ has_data++; -+ } -+ -+ if (!has_data) { -+ file_struct->top = tn->internal[0]; -+ file_struct->top_level--; -+ yaffs_free_tnode(dev, tn); -+ } else { -+ done = 1; -+ } -+ } -+ -+ return YAFFS_OK; -+} -+ -+/*-------------------- End of File Structure functions.-------------------*/ -+ -+/* alloc_empty_obj gets us a clean Object.*/ -+static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); -+ -+ if (!obj) -+ return obj; -+ -+ dev->n_obj++; -+ -+ /* Now sweeten it up... */ -+ -+ memset(obj, 0, sizeof(struct yaffs_obj)); -+ obj->being_created = 1; -+ -+ obj->my_dev = dev; -+ obj->hdr_chunk = 0; -+ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; -+ INIT_LIST_HEAD(&(obj->hard_links)); -+ INIT_LIST_HEAD(&(obj->hash_link)); -+ INIT_LIST_HEAD(&obj->siblings); -+ -+ /* Now make the directory sane */ -+ if (dev->root_dir) { -+ obj->parent = dev->root_dir; -+ list_add(&(obj->siblings), -+ &dev->root_dir->variant.dir_variant.children); -+ } -+ -+ /* Add it to the lost and found directory. -+ * NB Can't put root or lost-n-found in lost-n-found so -+ * check if lost-n-found exists first -+ */ -+ if (dev->lost_n_found) -+ yaffs_add_obj_to_dir(dev->lost_n_found, obj); -+ -+ obj->being_created = 0; -+ -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+ -+ return obj; -+} -+ -+static int yaffs_find_nice_bucket(struct yaffs_dev *dev) -+{ -+ int i; -+ int l = 999; -+ int lowest = 999999; -+ -+ /* Search for the shortest list or one that -+ * isn't too long. -+ */ -+ -+ for (i = 0; i < 10 && lowest > 4; i++) { -+ dev->bucket_finder++; -+ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; -+ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { -+ lowest = dev->obj_bucket[dev->bucket_finder].count; -+ l = dev->bucket_finder; -+ } -+ } -+ -+ return l; -+} -+ -+static int yaffs_new_obj_id(struct yaffs_dev *dev) -+{ -+ int bucket = yaffs_find_nice_bucket(dev); -+ int found = 0; -+ struct list_head *i; -+ u32 n = (u32) bucket; -+ -+ /* Now find an object value that has not already been taken -+ * by scanning the list. -+ */ -+ -+ while (!found) { -+ found = 1; -+ n += YAFFS_NOBJECT_BUCKETS; -+ if (1 || dev->obj_bucket[bucket].count > 0) { -+ list_for_each(i, &dev->obj_bucket[bucket].list) { -+ /* If there is already one in the list */ -+ if (i && list_entry(i, struct yaffs_obj, -+ hash_link)->obj_id == n) { -+ found = 0; -+ } -+ } -+ } -+ } -+ return n; -+} -+ -+static void yaffs_hash_obj(struct yaffs_obj *in) -+{ -+ int bucket = yaffs_hash_fn(in->obj_id); -+ struct yaffs_dev *dev = in->my_dev; -+ -+ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); -+ dev->obj_bucket[bucket].count++; -+} -+ -+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) -+{ -+ int bucket = yaffs_hash_fn(number); -+ struct list_head *i; -+ struct yaffs_obj *in; -+ -+ list_for_each(i, &dev->obj_bucket[bucket].list) { -+ /* Look if it is in the list */ -+ in = list_entry(i, struct yaffs_obj, hash_link); -+ if (in->obj_id == number) { -+ /* Don't show if it is defered free */ -+ if (in->defered_free) -+ return NULL; -+ return in; -+ } -+ } -+ -+ return NULL; -+} -+ -+static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, -+ enum yaffs_obj_type type) -+{ -+ struct yaffs_obj *the_obj = NULL; -+ struct yaffs_tnode *tn = NULL; -+ -+ if (number < 0) -+ number = yaffs_new_obj_id(dev); -+ -+ if (type == YAFFS_OBJECT_TYPE_FILE) { -+ tn = yaffs_get_tnode(dev); -+ if (!tn) -+ return NULL; -+ } -+ -+ the_obj = yaffs_alloc_empty_obj(dev); -+ if (!the_obj) { -+ if (tn) -+ yaffs_free_tnode(dev, tn); -+ return NULL; -+ } -+ -+ the_obj->fake = 0; -+ the_obj->rename_allowed = 1; -+ the_obj->unlink_allowed = 1; -+ the_obj->obj_id = number; -+ yaffs_hash_obj(the_obj); -+ the_obj->variant_type = type; -+ yaffs_load_current_time(the_obj, 1, 1); -+ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ the_obj->variant.file_variant.file_size = 0; -+ the_obj->variant.file_variant.scanned_size = 0; -+ the_obj->variant.file_variant.shrink_size = -+ yaffs_max_file_size(dev); -+ the_obj->variant.file_variant.top_level = 0; -+ the_obj->variant.file_variant.top = tn; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); -+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* No action required */ -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* todo this should not happen */ -+ break; -+ } -+ return the_obj; -+} -+ -+static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, -+ int number, u32 mode) -+{ -+ -+ struct yaffs_obj *obj = -+ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); -+ -+ if (!obj) -+ return NULL; -+ -+ obj->fake = 1; /* it is fake so it might not use NAND */ -+ obj->rename_allowed = 0; -+ obj->unlink_allowed = 0; -+ obj->deleted = 0; -+ obj->unlinked = 0; -+ obj->yst_mode = mode; -+ obj->my_dev = dev; -+ obj->hdr_chunk = 0; /* Not a valid chunk. */ -+ return obj; -+ -+} -+ -+ -+static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ dev->n_obj = 0; -+ dev->n_tnodes = 0; -+ yaffs_init_raw_tnodes_and_objs(dev); -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ INIT_LIST_HEAD(&dev->obj_bucket[i].list); -+ dev->obj_bucket[i].count = 0; -+ } -+} -+ -+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, -+ int number, -+ enum yaffs_obj_type type) -+{ -+ struct yaffs_obj *the_obj = NULL; -+ -+ if (number > 0) -+ the_obj = yaffs_find_by_number(dev, number); -+ -+ if (!the_obj) -+ the_obj = yaffs_new_obj(dev, number, type); -+ -+ return the_obj; -+ -+} -+ -+YCHAR *yaffs_clone_str(const YCHAR *str) -+{ -+ YCHAR *new_str = NULL; -+ int len; -+ -+ if (!str) -+ str = _Y(""); -+ -+ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); -+ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); -+ if (new_str) { -+ strncpy(new_str, str, len); -+ new_str[len] = 0; -+ } -+ return new_str; -+ -+} -+/* -+ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new -+ * link (ie. name) is created or deleted in the directory. -+ * -+ * ie. -+ * create dir/a : update dir's mtime/ctime -+ * rm dir/a: update dir's mtime/ctime -+ * modify dir/a: don't update dir's mtimme/ctime -+ * -+ * This can be handled immediately or defered. Defering helps reduce the number -+ * of updates when many files in a directory are changed within a brief period. -+ * -+ * If the directory updating is defered then yaffs_update_dirty_dirs must be -+ * called periodically. -+ */ -+ -+static void yaffs_update_parent(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ -+ if (!obj) -+ return; -+ dev = obj->my_dev; -+ obj->dirty = 1; -+ yaffs_load_current_time(obj, 0, 1); -+ if (dev->param.defered_dir_update) { -+ struct list_head *link = &obj->variant.dir_variant.dirty; -+ -+ if (list_empty(link)) { -+ list_add(link, &dev->dirty_dirs); -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "Added object %d to dirty directories", -+ obj->obj_id); -+ } -+ -+ } else { -+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); -+ } -+} -+ -+void yaffs_update_dirty_dirs(struct yaffs_dev *dev) -+{ -+ struct list_head *link; -+ struct yaffs_obj *obj; -+ struct yaffs_dir_var *d_s; -+ union yaffs_obj_var *o_v; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); -+ -+ while (!list_empty(&dev->dirty_dirs)) { -+ link = dev->dirty_dirs.next; -+ list_del_init(link); -+ -+ d_s = list_entry(link, struct yaffs_dir_var, dirty); -+ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); -+ obj = list_entry(o_v, struct yaffs_obj, variant); -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", -+ obj->obj_id); -+ -+ if (obj->dirty) -+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); -+ } -+} -+ -+/* -+ * Mknod (create) a new object. -+ * equiv_obj only has meaning for a hard link; -+ * alias_str only has meaning for a symlink. -+ * rdev only has meaning for devices (a subset of special objects) -+ */ -+ -+static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, -+ struct yaffs_obj *parent, -+ const YCHAR *name, -+ u32 mode, -+ u32 uid, -+ u32 gid, -+ struct yaffs_obj *equiv_obj, -+ const YCHAR *alias_str, u32 rdev) -+{ -+ struct yaffs_obj *in; -+ YCHAR *str = NULL; -+ struct yaffs_dev *dev = parent->my_dev; -+ -+ /* Check if the entry exists. -+ * If it does then fail the call since we don't want a dup. */ -+ if (yaffs_find_by_name(parent, name)) -+ return NULL; -+ -+ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ str = yaffs_clone_str(alias_str); -+ if (!str) -+ return NULL; -+ } -+ -+ in = yaffs_new_obj(dev, -1, type); -+ -+ if (!in) { -+ kfree(str); -+ return NULL; -+ } -+ -+ in->hdr_chunk = 0; -+ in->valid = 1; -+ in->variant_type = type; -+ -+ in->yst_mode = mode; -+ -+ yaffs_attribs_init(in, gid, uid, rdev); -+ -+ in->n_data_chunks = 0; -+ -+ yaffs_set_obj_name(in, name); -+ in->dirty = 1; -+ -+ yaffs_add_obj_to_dir(parent, in); -+ -+ in->my_dev = parent->my_dev; -+ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ in->variant.symlink_variant.alias = str; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ in->variant.hardlink_variant.equiv_obj = equiv_obj; -+ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; -+ list_add(&in->hard_links, &equiv_obj->hard_links); -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* do nothing */ -+ break; -+ } -+ -+ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { -+ /* Could not create the object header, fail */ -+ yaffs_del_obj(in); -+ in = NULL; -+ } -+ -+ if (in) -+ yaffs_update_parent(parent); -+ -+ return in; -+} -+ -+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, -+ uid, gid, NULL, NULL, 0); -+} -+ -+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, -+ u32 mode, u32 uid, u32 gid) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, -+ mode, uid, gid, NULL, NULL, 0); -+} -+ -+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, u32 rdev) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, -+ uid, gid, NULL, NULL, rdev); -+} -+ -+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, const YCHAR *alias) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, -+ uid, gid, NULL, alias, 0); -+} -+ -+/* yaffs_link_obj returns the object id of the equivalent object.*/ -+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, -+ struct yaffs_obj *equiv_obj) -+{ -+ /* Get the real object in case we were fed a hard link obj */ -+ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); -+ -+ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, -+ parent, name, 0, 0, 0, -+ equiv_obj, NULL, 0)) -+ return equiv_obj; -+ -+ return NULL; -+ -+} -+ -+ -+ -+/*---------------------- Block Management and Page Allocation -------------*/ -+ -+static void yaffs_deinit_blocks(struct yaffs_dev *dev) -+{ -+ if (dev->block_info_alt && dev->block_info) -+ vfree(dev->block_info); -+ else -+ kfree(dev->block_info); -+ -+ dev->block_info_alt = 0; -+ -+ dev->block_info = NULL; -+ -+ if (dev->chunk_bits_alt && dev->chunk_bits) -+ vfree(dev->chunk_bits); -+ else -+ kfree(dev->chunk_bits); -+ dev->chunk_bits_alt = 0; -+ dev->chunk_bits = NULL; -+} -+ -+static int yaffs_init_blocks(struct yaffs_dev *dev) -+{ -+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ -+ dev->block_info = NULL; -+ dev->chunk_bits = NULL; -+ dev->alloc_block = -1; /* force it to get a new one */ -+ -+ /* If the first allocation strategy fails, thry the alternate one */ -+ dev->block_info = -+ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); -+ if (!dev->block_info) { -+ dev->block_info = -+ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); -+ dev->block_info_alt = 1; -+ } else { -+ dev->block_info_alt = 0; -+ } -+ -+ if (!dev->block_info) -+ goto alloc_error; -+ -+ /* Set up dynamic blockinfo stuff. Round up bytes. */ -+ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; -+ dev->chunk_bits = -+ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); -+ if (!dev->chunk_bits) { -+ dev->chunk_bits = -+ vmalloc(dev->chunk_bit_stride * n_blocks); -+ dev->chunk_bits_alt = 1; -+ } else { -+ dev->chunk_bits_alt = 0; -+ } -+ if (!dev->chunk_bits) -+ goto alloc_error; -+ -+ -+ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); -+ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); -+ return YAFFS_OK; -+ -+alloc_error: -+ yaffs_deinit_blocks(dev); -+ return YAFFS_FAIL; -+} -+ -+ -+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); -+ int erased_ok = 0; -+ int i; -+ -+ /* If the block is still healthy erase it and mark as clean. -+ * If the block has had a data failure, then retire it. -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, -+ "yaffs_block_became_dirty block %d state %d %s", -+ block_no, bi->block_state, -+ (bi->needs_retiring) ? "needs retiring" : ""); -+ -+ yaffs2_clear_oldest_dirty_seq(dev, bi); -+ -+ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; -+ -+ /* If this is the block being garbage collected then stop gc'ing */ -+ if (block_no == dev->gc_block) -+ dev->gc_block = 0; -+ -+ /* If this block is currently the best candidate for gc -+ * then drop as a candidate */ -+ if (block_no == dev->gc_dirtiest) { -+ dev->gc_dirtiest = 0; -+ dev->gc_pages_in_use = 0; -+ } -+ -+ if (!bi->needs_retiring) { -+ yaffs2_checkpt_invalidate(dev); -+ erased_ok = yaffs_erase_block(dev, block_no); -+ if (!erased_ok) { -+ dev->n_erase_failures++; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Erasure failed %d", block_no); -+ } -+ } -+ -+ /* Verify erasure if needed */ -+ if (erased_ok && -+ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || -+ !yaffs_skip_verification(dev))) { -+ for (i = 0; i < dev->param.chunks_per_block; i++) { -+ if (!yaffs_check_chunk_erased(dev, -+ block_no * dev->param.chunks_per_block + i)) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ ">>Block %d erasure supposedly OK, but chunk %d not erased", -+ block_no, i); -+ } -+ } -+ } -+ -+ if (!erased_ok) { -+ /* We lost a block of free space */ -+ dev->n_free_chunks -= dev->param.chunks_per_block; -+ yaffs_retire_block(dev, block_no); -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Block %d retired", block_no); -+ return; -+ } -+ -+ /* Clean it up... */ -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ bi->seq_number = 0; -+ dev->n_erased_blocks++; -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ bi->has_shrink_hdr = 0; -+ bi->skip_erased_check = 1; /* Clean, so no need to check */ -+ bi->gc_prioritise = 0; -+ bi->has_summary = 0; -+ -+ yaffs_clear_chunk_bits(dev, block_no); -+ -+ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); -+} -+ -+static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, -+ int old_chunk, u8 *buffer) -+{ -+ int new_chunk; -+ int mark_flash = 1; -+ struct yaffs_ext_tags tags; -+ struct yaffs_obj *object; -+ int matching_chunk; -+ int ret_val = YAFFS_OK; -+ -+ memset(&tags, 0, sizeof(tags)); -+ yaffs_rd_chunk_tags_nand(dev, old_chunk, -+ buffer, &tags); -+ object = yaffs_find_by_number(dev, tags.obj_id); -+ -+ yaffs_trace(YAFFS_TRACE_GC_DETAIL, -+ "Collecting chunk in block %d, %d %d %d ", -+ dev->gc_chunk, tags.obj_id, -+ tags.chunk_id, tags.n_bytes); -+ -+ if (object && !yaffs_skip_verification(dev)) { -+ if (tags.chunk_id == 0) -+ matching_chunk = -+ object->hdr_chunk; -+ else if (object->soft_del) -+ /* Defeat the test */ -+ matching_chunk = old_chunk; -+ else -+ matching_chunk = -+ yaffs_find_chunk_in_file -+ (object, tags.chunk_id, -+ NULL); -+ -+ if (old_chunk != matching_chunk) -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "gc: page in gc mismatch: %d %d %d %d", -+ old_chunk, -+ matching_chunk, -+ tags.obj_id, -+ tags.chunk_id); -+ } -+ -+ if (!object) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "page %d in gc has no object: %d %d %d ", -+ old_chunk, -+ tags.obj_id, tags.chunk_id, -+ tags.n_bytes); -+ } -+ -+ if (object && -+ object->deleted && -+ object->soft_del && tags.chunk_id != 0) { -+ /* Data chunk in a soft deleted file, -+ * throw it away. -+ * It's a soft deleted data chunk, -+ * No need to copy this, just forget -+ * about it and fix up the object. -+ */ -+ -+ /* Free chunks already includes -+ * softdeleted chunks, how ever this -+ * chunk is going to soon be really -+ * deleted which will increment free -+ * chunks. We have to decrement free -+ * chunks so this works out properly. -+ */ -+ dev->n_free_chunks--; -+ bi->soft_del_pages--; -+ -+ object->n_data_chunks--; -+ if (object->n_data_chunks <= 0) { -+ /* remeber to clean up obj */ -+ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; -+ dev->n_clean_ups++; -+ } -+ mark_flash = 0; -+ } else if (object) { -+ /* It's either a data chunk in a live -+ * file or an ObjectHeader, so we're -+ * interested in it. -+ * NB Need to keep the ObjectHeaders of -+ * deleted files until the whole file -+ * has been deleted off -+ */ -+ tags.serial_number++; -+ dev->n_gc_copies++; -+ -+ if (tags.chunk_id == 0) { -+ /* It is an object Id, -+ * We need to nuke the -+ * shrinkheader flags since its -+ * work is done. -+ * Also need to clean up -+ * shadowing. -+ */ -+ struct yaffs_obj_hdr *oh; -+ oh = (struct yaffs_obj_hdr *) buffer; -+ -+ oh->is_shrink = 0; -+ tags.extra_is_shrink = 0; -+ oh->shadows_obj = 0; -+ oh->inband_shadowed_obj_id = 0; -+ tags.extra_shadows = 0; -+ -+ /* Update file size */ -+ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { -+ yaffs_oh_size_load(oh, -+ object->variant.file_variant.file_size); -+ tags.extra_file_size = -+ object->variant.file_variant.file_size; -+ } -+ -+ yaffs_verify_oh(object, oh, &tags, 1); -+ new_chunk = -+ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); -+ } else { -+ new_chunk = -+ yaffs_write_new_chunk(dev, buffer, &tags, 1); -+ } -+ -+ if (new_chunk < 0) { -+ ret_val = YAFFS_FAIL; -+ } else { -+ -+ /* Now fix up the Tnodes etc. */ -+ -+ if (tags.chunk_id == 0) { -+ /* It's a header */ -+ object->hdr_chunk = new_chunk; -+ object->serial = tags.serial_number; -+ } else { -+ /* It's a data chunk */ -+ yaffs_put_chunk_in_file(object, tags.chunk_id, -+ new_chunk, 0); -+ } -+ } -+ } -+ if (ret_val == YAFFS_OK) -+ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); -+ return ret_val; -+} -+ -+static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) -+{ -+ int old_chunk; -+ int ret_val = YAFFS_OK; -+ int i; -+ int is_checkpt_block; -+ int max_copies; -+ int chunks_before = yaffs_get_erased_chunks(dev); -+ int chunks_after; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); -+ -+ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "Collecting block %d, in use %d, shrink %d, whole_block %d", -+ block, bi->pages_in_use, bi->has_shrink_hdr, -+ whole_block); -+ -+ /*yaffs_verify_free_chunks(dev); */ -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) -+ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; -+ -+ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ -+ -+ dev->gc_disable = 1; -+ -+ yaffs_summary_gc(dev, block); -+ -+ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "Collecting block %d that has no chunks in use", -+ block); -+ yaffs_block_became_dirty(dev, block); -+ } else { -+ -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ yaffs_verify_blk(dev, bi, block); -+ -+ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; -+ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; -+ -+ for (/* init already done */ ; -+ ret_val == YAFFS_OK && -+ dev->gc_chunk < dev->param.chunks_per_block && -+ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && -+ max_copies > 0; -+ dev->gc_chunk++, old_chunk++) { -+ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { -+ /* Page is in use and might need to be copied */ -+ max_copies--; -+ ret_val = yaffs_gc_process_chunk(dev, bi, -+ old_chunk, buffer); -+ } -+ } -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ -+ yaffs_verify_collected_blk(dev, bi, block); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { -+ /* -+ * The gc did not complete. Set block state back to FULL -+ * because checkpointing does not restore gc. -+ */ -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ } else { -+ /* The gc completed. */ -+ /* Do any required cleanups */ -+ for (i = 0; i < dev->n_clean_ups; i++) { -+ /* Time to delete the file too */ -+ struct yaffs_obj *object = -+ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); -+ if (object) { -+ yaffs_free_tnode(dev, -+ object->variant.file_variant.top); -+ object->variant.file_variant.top = NULL; -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: About to finally delete object %d", -+ object->obj_id); -+ yaffs_generic_obj_del(object); -+ object->my_dev->n_deleted_files--; -+ } -+ -+ } -+ chunks_after = yaffs_get_erased_chunks(dev); -+ if (chunks_before >= chunks_after) -+ yaffs_trace(YAFFS_TRACE_GC, -+ "gc did not increase free chunks before %d after %d", -+ chunks_before, chunks_after); -+ dev->gc_block = 0; -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ -+ dev->gc_disable = 0; -+ -+ return ret_val; -+} -+ -+/* -+ * find_gc_block() selects the dirtiest block (or close enough) -+ * for garbage collection. -+ */ -+ -+static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, -+ int aggressive, int background) -+{ -+ int i; -+ int iterations; -+ unsigned selected = 0; -+ int prioritised = 0; -+ int prioritised_exist = 0; -+ struct yaffs_block_info *bi; -+ int threshold; -+ -+ /* First let's see if we need to grab a prioritised block */ -+ if (dev->has_pending_prioritised_gc && !aggressive) { -+ dev->gc_dirtiest = 0; -+ bi = dev->block_info; -+ for (i = dev->internal_start_block; -+ i <= dev->internal_end_block && !selected; i++) { -+ -+ if (bi->gc_prioritise) { -+ prioritised_exist = 1; -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && -+ yaffs_block_ok_for_gc(dev, bi)) { -+ selected = i; -+ prioritised = 1; -+ } -+ } -+ bi++; -+ } -+ -+ /* -+ * If there is a prioritised block and none was selected then -+ * this happened because there is at least one old dirty block -+ * gumming up the works. Let's gc the oldest dirty block. -+ */ -+ -+ if (prioritised_exist && -+ !selected && dev->oldest_dirty_block > 0) -+ selected = dev->oldest_dirty_block; -+ -+ if (!prioritised_exist) /* None found, so we can clear this */ -+ dev->has_pending_prioritised_gc = 0; -+ } -+ -+ /* If we're doing aggressive GC then we are happy to take a less-dirty -+ * block, and search harder. -+ * else (leasurely gc), then we only bother to do this if the -+ * block has only a few pages in use. -+ */ -+ -+ if (!selected) { -+ int pages_used; -+ int n_blocks = -+ dev->internal_end_block - dev->internal_start_block + 1; -+ if (aggressive) { -+ threshold = dev->param.chunks_per_block; -+ iterations = n_blocks; -+ } else { -+ int max_threshold; -+ -+ if (background) -+ max_threshold = dev->param.chunks_per_block / 2; -+ else -+ max_threshold = dev->param.chunks_per_block / 8; -+ -+ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) -+ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; -+ -+ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; -+ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) -+ threshold = YAFFS_GC_PASSIVE_THRESHOLD; -+ if (threshold > max_threshold) -+ threshold = max_threshold; -+ -+ iterations = n_blocks / 16 + 1; -+ if (iterations > 100) -+ iterations = 100; -+ } -+ -+ for (i = 0; -+ i < iterations && -+ (dev->gc_dirtiest < 1 || -+ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); -+ i++) { -+ dev->gc_block_finder++; -+ if (dev->gc_block_finder < dev->internal_start_block || -+ dev->gc_block_finder > dev->internal_end_block) -+ dev->gc_block_finder = -+ dev->internal_start_block; -+ -+ bi = yaffs_get_block_info(dev, dev->gc_block_finder); -+ -+ pages_used = bi->pages_in_use - bi->soft_del_pages; -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && -+ pages_used < dev->param.chunks_per_block && -+ (dev->gc_dirtiest < 1 || -+ pages_used < dev->gc_pages_in_use) && -+ yaffs_block_ok_for_gc(dev, bi)) { -+ dev->gc_dirtiest = dev->gc_block_finder; -+ dev->gc_pages_in_use = pages_used; -+ } -+ } -+ -+ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) -+ selected = dev->gc_dirtiest; -+ } -+ -+ /* -+ * If nothing has been selected for a while, try the oldest dirty -+ * because that's gumming up the works. -+ */ -+ -+ if (!selected && dev->param.is_yaffs2 && -+ dev->gc_not_done >= (background ? 10 : 20)) { -+ yaffs2_find_oldest_dirty_seq(dev); -+ if (dev->oldest_dirty_block > 0) { -+ selected = dev->oldest_dirty_block; -+ dev->gc_dirtiest = selected; -+ dev->oldest_dirty_gc_count++; -+ bi = yaffs_get_block_info(dev, selected); -+ dev->gc_pages_in_use = -+ bi->pages_in_use - bi->soft_del_pages; -+ } else { -+ dev->gc_not_done = 0; -+ } -+ } -+ -+ if (selected) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC Selected block %d with %d free, prioritised:%d", -+ selected, -+ dev->param.chunks_per_block - dev->gc_pages_in_use, -+ prioritised); -+ -+ dev->n_gc_blocks++; -+ if (background) -+ dev->bg_gcs++; -+ -+ dev->gc_dirtiest = 0; -+ dev->gc_pages_in_use = 0; -+ dev->gc_not_done = 0; -+ if (dev->refresh_skip > 0) -+ dev->refresh_skip--; -+ } else { -+ dev->gc_not_done++; -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", -+ dev->gc_block_finder, dev->gc_not_done, threshold, -+ dev->gc_dirtiest, dev->gc_pages_in_use, -+ dev->oldest_dirty_block, background ? " bg" : ""); -+ } -+ -+ return selected; -+} -+ -+/* New garbage collector -+ * If we're very low on erased blocks then we do aggressive garbage collection -+ * otherwise we do "leasurely" garbage collection. -+ * Aggressive gc looks further (whole array) and will accept less dirty blocks. -+ * Passive gc only inspects smaller areas and only accepts more dirty blocks. -+ * -+ * The idea is to help clear out space in a more spread-out manner. -+ * Dunno if it really does anything useful. -+ */ -+static int yaffs_check_gc(struct yaffs_dev *dev, int background) -+{ -+ int aggressive = 0; -+ int gc_ok = YAFFS_OK; -+ int max_tries = 0; -+ int min_erased; -+ int erased_chunks; -+ int checkpt_block_adjust; -+ -+ if (dev->param.gc_control_fn && -+ (dev->param.gc_control_fn(dev) & 1) == 0) -+ return YAFFS_OK; -+ -+ if (dev->gc_disable) -+ /* Bail out so we don't get recursive gc */ -+ return YAFFS_OK; -+ -+ /* This loop should pass the first time. -+ * Only loops here if the collection does not increase space. -+ */ -+ -+ do { -+ max_tries++; -+ -+ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); -+ -+ min_erased = -+ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; -+ erased_chunks = -+ dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ /* If we need a block soon then do aggressive gc. */ -+ if (dev->n_erased_blocks < min_erased) -+ aggressive = 1; -+ else { -+ if (!background -+ && erased_chunks > (dev->n_free_chunks / 4)) -+ break; -+ -+ if (dev->gc_skip > 20) -+ dev->gc_skip = 20; -+ if (erased_chunks < dev->n_free_chunks / 2 || -+ dev->gc_skip < 1 || background) -+ aggressive = 0; -+ else { -+ dev->gc_skip--; -+ break; -+ } -+ } -+ -+ dev->gc_skip = 5; -+ -+ /* If we don't already have a block being gc'd then see if we -+ * should start another */ -+ -+ if (dev->gc_block < 1 && !aggressive) { -+ dev->gc_block = yaffs2_find_refresh_block(dev); -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ if (dev->gc_block < 1) { -+ dev->gc_block = -+ yaffs_find_gc_block(dev, aggressive, background); -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ -+ if (dev->gc_block > 0) { -+ dev->all_gcs++; -+ if (!aggressive) -+ dev->passive_gc_count++; -+ -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: GC n_erased_blocks %d aggressive %d", -+ dev->n_erased_blocks, aggressive); -+ -+ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); -+ } -+ -+ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && -+ dev->gc_block > 0) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", -+ dev->n_erased_blocks, max_tries, -+ dev->gc_block); -+ } -+ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && -+ (dev->gc_block > 0) && (max_tries < 2)); -+ -+ return aggressive ? gc_ok : YAFFS_OK; -+} -+ -+/* -+ * yaffs_bg_gc() -+ * Garbage collects. Intended to be called from a background thread. -+ * Returns non-zero if at least half the free chunks are erased. -+ */ -+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) -+{ -+ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); -+ -+ yaffs_check_gc(dev, 1); -+ return erased_chunks > dev->n_free_chunks / 2; -+} -+ -+/*-------------------- Data file manipulation -----------------*/ -+ -+static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) -+{ -+ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); -+ -+ if (nand_chunk >= 0) -+ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, -+ buffer, NULL); -+ else { -+ yaffs_trace(YAFFS_TRACE_NANDACCESS, -+ "Chunk %d not found zero instead", -+ nand_chunk); -+ /* get sane (zero) data if you read a hole */ -+ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); -+ return 0; -+ } -+ -+} -+ -+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, -+ int lyn) -+{ -+ int block; -+ int page; -+ struct yaffs_ext_tags tags; -+ struct yaffs_block_info *bi; -+ -+ if (chunk_id <= 0) -+ return; -+ -+ dev->n_deletions++; -+ block = chunk_id / dev->param.chunks_per_block; -+ page = chunk_id % dev->param.chunks_per_block; -+ -+ if (!yaffs_check_chunk_bit(dev, block, page)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Deleting invalid chunk %d", chunk_id); -+ -+ bi = yaffs_get_block_info(dev, block); -+ -+ yaffs2_update_oldest_dirty_seq(dev, block, bi); -+ -+ yaffs_trace(YAFFS_TRACE_DELETION, -+ "line %d delete of chunk %d", -+ lyn, chunk_id); -+ -+ if (!dev->param.is_yaffs2 && mark_flash && -+ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { -+ -+ memset(&tags, 0, sizeof(tags)); -+ tags.is_deleted = 1; -+ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); -+ yaffs_handle_chunk_update(dev, chunk_id, &tags); -+ } else { -+ dev->n_unmarked_deletions++; -+ } -+ -+ /* Pull out of the management area. -+ * If the whole block became dirty, this will kick off an erasure. -+ */ -+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || -+ bi->block_state == YAFFS_BLOCK_STATE_FULL || -+ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { -+ dev->n_free_chunks++; -+ yaffs_clear_chunk_bit(dev, block, page); -+ bi->pages_in_use--; -+ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && -+ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ yaffs_block_became_dirty(dev, block); -+ } -+ } -+} -+ -+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, -+ const u8 *buffer, int n_bytes, int use_reserve) -+{ -+ /* Find old chunk Need to do this to get serial number -+ * Write new one and patch into tree. -+ * Invalidate old tags. -+ */ -+ -+ int prev_chunk_id; -+ struct yaffs_ext_tags prev_tags; -+ int new_chunk_id; -+ struct yaffs_ext_tags new_tags; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ yaffs_check_gc(dev, 0); -+ -+ /* Get the previous chunk at this location in the file if it exists. -+ * If it does not exist then put a zero into the tree. This creates -+ * the tnode now, rather than later when it is harder to clean up. -+ */ -+ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); -+ if (prev_chunk_id < 1 && -+ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) -+ return 0; -+ -+ /* Set up new tags */ -+ memset(&new_tags, 0, sizeof(new_tags)); -+ -+ new_tags.chunk_id = inode_chunk; -+ new_tags.obj_id = in->obj_id; -+ new_tags.serial_number = -+ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; -+ new_tags.n_bytes = n_bytes; -+ -+ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Writing %d bytes to chunk!!!!!!!!!", -+ n_bytes); -+ BUG(); -+ } -+ -+ new_chunk_id = -+ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); -+ -+ if (new_chunk_id > 0) { -+ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); -+ -+ if (prev_chunk_id > 0) -+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -+ -+ yaffs_verify_file_sane(in); -+ } -+ return new_chunk_id; -+ -+} -+ -+ -+ -+static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, -+ const YCHAR *name, const void *value, int size, -+ int flags) -+{ -+ struct yaffs_xattr_mod xmod; -+ int result; -+ -+ xmod.set = set; -+ xmod.name = name; -+ xmod.data = value; -+ xmod.size = size; -+ xmod.flags = flags; -+ xmod.result = -ENOSPC; -+ -+ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); -+ -+ if (result > 0) -+ return xmod.result; -+ else -+ return -ENOSPC; -+} -+ -+static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, -+ struct yaffs_xattr_mod *xmod) -+{ -+ int retval = 0; -+ int x_offs = sizeof(struct yaffs_obj_hdr); -+ struct yaffs_dev *dev = obj->my_dev; -+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); -+ char *x_buffer = buffer + x_offs; -+ -+ if (xmod->set) -+ retval = -+ nval_set(x_buffer, x_size, xmod->name, xmod->data, -+ xmod->size, xmod->flags); -+ else -+ retval = nval_del(x_buffer, x_size, xmod->name); -+ -+ obj->has_xattr = nval_hasvalues(x_buffer, x_size); -+ obj->xattr_known = 1; -+ xmod->result = retval; -+ -+ return retval; -+} -+ -+static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, -+ void *value, int size) -+{ -+ char *buffer = NULL; -+ int result; -+ struct yaffs_ext_tags tags; -+ struct yaffs_dev *dev = obj->my_dev; -+ int x_offs = sizeof(struct yaffs_obj_hdr); -+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); -+ char *x_buffer; -+ int retval = 0; -+ -+ if (obj->hdr_chunk < 1) -+ return -ENODATA; -+ -+ /* If we know that the object has no xattribs then don't do all the -+ * reading and parsing. -+ */ -+ if (obj->xattr_known && !obj->has_xattr) { -+ if (name) -+ return -ENODATA; -+ else -+ return 0; -+ } -+ -+ buffer = (char *)yaffs_get_temp_buffer(dev); -+ if (!buffer) -+ return -ENOMEM; -+ -+ result = -+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); -+ -+ if (result != YAFFS_OK) -+ retval = -ENOENT; -+ else { -+ x_buffer = buffer + x_offs; -+ -+ if (!obj->xattr_known) { -+ obj->has_xattr = nval_hasvalues(x_buffer, x_size); -+ obj->xattr_known = 1; -+ } -+ -+ if (name) -+ retval = nval_get(x_buffer, x_size, name, value, size); -+ else -+ retval = nval_list(x_buffer, x_size, value, size); -+ } -+ yaffs_release_temp_buffer(dev, (u8 *) buffer); -+ return retval; -+} -+ -+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, -+ const void *value, int size, int flags) -+{ -+ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); -+} -+ -+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) -+{ -+ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); -+} -+ -+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, -+ int size) -+{ -+ return yaffs_do_xattrib_fetch(obj, name, value, size); -+} -+ -+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) -+{ -+ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); -+} -+ -+static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) -+{ -+ u8 *buf; -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_dev *dev; -+ struct yaffs_ext_tags tags; -+ int result; -+ int alloc_failed = 0; -+ -+ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) -+ return; -+ -+ dev = in->my_dev; -+ in->lazy_loaded = 0; -+ buf = yaffs_get_temp_buffer(dev); -+ -+ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); -+ oh = (struct yaffs_obj_hdr *)buf; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ yaffs_set_obj_name_from_oh(in, oh); -+ -+ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ in->variant.symlink_variant.alias = -+ yaffs_clone_str(oh->alias); -+ if (!in->variant.symlink_variant.alias) -+ alloc_failed = 1; /* Not returned */ -+ } -+ yaffs_release_temp_buffer(dev, buf); -+} -+ -+static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, -+ const YCHAR *oh_name, int buff_size) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ if (dev->param.auto_unicode) { -+ if (*oh_name) { -+ /* It is an ASCII name, do an ASCII to -+ * unicode conversion */ -+ const char *ascii_oh_name = (const char *)oh_name; -+ int n = buff_size - 1; -+ while (n > 0 && *ascii_oh_name) { -+ *name = *ascii_oh_name; -+ name++; -+ ascii_oh_name++; -+ n--; -+ } -+ } else { -+ strncpy(name, oh_name + 1, buff_size - 1); -+ } -+ } else { -+#else -+ (void) dev; -+ { -+#endif -+ strncpy(name, oh_name, buff_size - 1); -+ } -+} -+ -+static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, -+ const YCHAR *name) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ -+ int is_ascii; -+ YCHAR *w; -+ -+ if (dev->param.auto_unicode) { -+ -+ is_ascii = 1; -+ w = name; -+ -+ /* Figure out if the name will fit in ascii character set */ -+ while (is_ascii && *w) { -+ if ((*w) & 0xff00) -+ is_ascii = 0; -+ w++; -+ } -+ -+ if (is_ascii) { -+ /* It is an ASCII name, so convert unicode to ascii */ -+ char *ascii_oh_name = (char *)oh_name; -+ int n = YAFFS_MAX_NAME_LENGTH - 1; -+ while (n > 0 && *name) { -+ *ascii_oh_name = *name; -+ name++; -+ ascii_oh_name++; -+ n--; -+ } -+ } else { -+ /* Unicode name, so save starting at the second YCHAR */ -+ *oh_name = 0; -+ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); -+ } -+ } else { -+#else -+ dev = dev; -+ { -+#endif -+ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); -+ } -+} -+ -+/* UpdateObjectHeader updates the header on NAND for an object. -+ * If name is not NULL, then that new name is used. -+ */ -+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, -+ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) -+{ -+ -+ struct yaffs_block_info *bi; -+ struct yaffs_dev *dev = in->my_dev; -+ int prev_chunk_id; -+ int ret_val = 0; -+ int result = 0; -+ int new_chunk_id; -+ struct yaffs_ext_tags new_tags; -+ struct yaffs_ext_tags old_tags; -+ const YCHAR *alias = NULL; -+ u8 *buffer = NULL; -+ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; -+ struct yaffs_obj_hdr *oh = NULL; -+ loff_t file_size = 0; -+ -+ strcpy(old_name, _Y("silly old name")); -+ -+ if (in->fake && in != dev->root_dir && !force && !xmod) -+ return ret_val; -+ -+ yaffs_check_gc(dev, 0); -+ yaffs_check_obj_details_loaded(in); -+ -+ buffer = yaffs_get_temp_buffer(in->my_dev); -+ oh = (struct yaffs_obj_hdr *)buffer; -+ -+ prev_chunk_id = in->hdr_chunk; -+ -+ if (prev_chunk_id > 0) { -+ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, -+ buffer, &old_tags); -+ -+ yaffs_verify_oh(in, oh, &old_tags, 0); -+ memcpy(old_name, oh->name, sizeof(oh->name)); -+ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); -+ } else { -+ memset(buffer, 0xff, dev->data_bytes_per_chunk); -+ } -+ -+ oh->type = in->variant_type; -+ oh->yst_mode = in->yst_mode; -+ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; -+ -+ yaffs_load_attribs_oh(oh, in); -+ -+ if (in->parent) -+ oh->parent_obj_id = in->parent->obj_id; -+ else -+ oh->parent_obj_id = 0; -+ -+ if (name && *name) { -+ memset(oh->name, 0, sizeof(oh->name)); -+ yaffs_load_oh_from_name(dev, oh->name, name); -+ } else if (prev_chunk_id > 0) { -+ memcpy(oh->name, old_name, sizeof(oh->name)); -+ } else { -+ memset(oh->name, 0, sizeof(oh->name)); -+ } -+ -+ oh->is_shrink = is_shrink; -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Should not happen */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && -+ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) -+ file_size = in->variant.file_variant.file_size; -+ yaffs_oh_size_load(oh, file_size); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ oh->equiv_id = in->variant.hardlink_variant.equiv_id; -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ alias = in->variant.symlink_variant.alias; -+ if (!alias) -+ alias = _Y("no alias"); -+ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); -+ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; -+ break; -+ } -+ -+ /* process any xattrib modifications */ -+ if (xmod) -+ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); -+ -+ /* Tags */ -+ memset(&new_tags, 0, sizeof(new_tags)); -+ in->serial++; -+ new_tags.chunk_id = 0; -+ new_tags.obj_id = in->obj_id; -+ new_tags.serial_number = in->serial; -+ -+ /* Add extra info for file header */ -+ new_tags.extra_available = 1; -+ new_tags.extra_parent_id = oh->parent_obj_id; -+ new_tags.extra_file_size = file_size; -+ new_tags.extra_is_shrink = oh->is_shrink; -+ new_tags.extra_equiv_id = oh->equiv_id; -+ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; -+ new_tags.extra_obj_type = in->variant_type; -+ yaffs_verify_oh(in, oh, &new_tags, 1); -+ -+ /* Create new chunk in NAND */ -+ new_chunk_id = -+ yaffs_write_new_chunk(dev, buffer, &new_tags, -+ (prev_chunk_id > 0) ? 1 : 0); -+ -+ if (buffer) -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ if (new_chunk_id < 0) -+ return new_chunk_id; -+ -+ in->hdr_chunk = new_chunk_id; -+ -+ if (prev_chunk_id > 0) -+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -+ -+ if (!yaffs_obj_cache_dirty(in)) -+ in->dirty = 0; -+ -+ /* If this was a shrink, then mark the block -+ * that the chunk lives on */ -+ if (is_shrink) { -+ bi = yaffs_get_block_info(in->my_dev, -+ new_chunk_id / -+ in->my_dev->param.chunks_per_block); -+ bi->has_shrink_hdr = 1; -+ } -+ -+ -+ return new_chunk_id; -+} -+ -+/*--------------------- File read/write ------------------------ -+ * Read and write have very similar structures. -+ * In general the read/write has three parts to it -+ * An incomplete chunk to start with (if the read/write is not chunk-aligned) -+ * Some complete chunks -+ * An incomplete chunk to end off with -+ * -+ * Curve-balls: the first chunk might also be the last chunk. -+ */ -+ -+int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) -+{ -+ int chunk; -+ u32 start; -+ int n_copy; -+ int n = n_bytes; -+ int n_done = 0; -+ struct yaffs_cache *cache; -+ struct yaffs_dev *dev; -+ -+ dev = in->my_dev; -+ -+ while (n > 0) { -+ yaffs_addr_to_chunk(dev, offset, &chunk, &start); -+ chunk++; -+ -+ /* OK now check for the curveball where the start and end are in -+ * the same chunk. -+ */ -+ if ((start + n) < dev->data_bytes_per_chunk) -+ n_copy = n; -+ else -+ n_copy = dev->data_bytes_per_chunk - start; -+ -+ cache = yaffs_find_chunk_cache(in, chunk); -+ -+ /* If the chunk is already in the cache or it is less than -+ * a whole chunk or we're using inband tags then use the cache -+ * (if there is caching) else bypass the cache. -+ */ -+ if (cache || n_copy != dev->data_bytes_per_chunk || -+ dev->param.inband_tags) { -+ if (dev->param.n_caches > 0) { -+ -+ /* If we can't find the data in the cache, -+ * then load it up. */ -+ -+ if (!cache) { -+ cache = -+ yaffs_grab_chunk_cache(in->my_dev); -+ cache->object = in; -+ cache->chunk_id = chunk; -+ cache->dirty = 0; -+ cache->locked = 0; -+ yaffs_rd_data_obj(in, chunk, -+ cache->data); -+ cache->n_bytes = 0; -+ } -+ -+ yaffs_use_cache(dev, cache, 0); -+ -+ cache->locked = 1; -+ -+ memcpy(buffer, &cache->data[start], n_copy); -+ -+ cache->locked = 0; -+ } else { -+ /* Read into the local buffer then copy.. */ -+ -+ u8 *local_buffer = -+ yaffs_get_temp_buffer(dev); -+ yaffs_rd_data_obj(in, chunk, local_buffer); -+ -+ memcpy(buffer, &local_buffer[start], n_copy); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ } else { -+ /* A full chunk. Read directly into the buffer. */ -+ yaffs_rd_data_obj(in, chunk, buffer); -+ } -+ n -= n_copy; -+ offset += n_copy; -+ buffer += n_copy; -+ n_done += n_copy; -+ } -+ return n_done; -+} -+ -+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_through) -+{ -+ -+ int chunk; -+ u32 start; -+ int n_copy; -+ int n = n_bytes; -+ int n_done = 0; -+ int n_writeback; -+ loff_t start_write = offset; -+ int chunk_written = 0; -+ u32 n_bytes_read; -+ loff_t chunk_start; -+ struct yaffs_dev *dev; -+ -+ dev = in->my_dev; -+ -+ while (n > 0 && chunk_written >= 0) { -+ yaffs_addr_to_chunk(dev, offset, &chunk, &start); -+ -+ if (((loff_t)chunk) * -+ dev->data_bytes_per_chunk + start != offset || -+ start >= dev->data_bytes_per_chunk) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "AddrToChunk of offset %lld gives chunk %d start %d", -+ offset, chunk, start); -+ } -+ chunk++; /* File pos to chunk in file offset */ -+ -+ /* OK now check for the curveball where the start and end are in -+ * the same chunk. -+ */ -+ -+ if ((start + n) < dev->data_bytes_per_chunk) { -+ n_copy = n; -+ -+ /* Now calculate how many bytes to write back.... -+ * If we're overwriting and not writing to then end of -+ * file then we need to write back as much as was there -+ * before. -+ */ -+ -+ chunk_start = (((loff_t)(chunk - 1)) * -+ dev->data_bytes_per_chunk); -+ -+ if (chunk_start > in->variant.file_variant.file_size) -+ n_bytes_read = 0; /* Past end of file */ -+ else -+ n_bytes_read = -+ in->variant.file_variant.file_size - -+ chunk_start; -+ -+ if (n_bytes_read > dev->data_bytes_per_chunk) -+ n_bytes_read = dev->data_bytes_per_chunk; -+ -+ n_writeback = -+ (n_bytes_read > -+ (start + n)) ? n_bytes_read : (start + n); -+ -+ if (n_writeback < 0 || -+ n_writeback > dev->data_bytes_per_chunk) -+ BUG(); -+ -+ } else { -+ n_copy = dev->data_bytes_per_chunk - start; -+ n_writeback = dev->data_bytes_per_chunk; -+ } -+ -+ if (n_copy != dev->data_bytes_per_chunk || -+ !dev->param.cache_bypass_aligned || -+ dev->param.inband_tags) { -+ /* An incomplete start or end chunk (or maybe both -+ * start and end chunk), or we're using inband tags, -+ * or we're forcing writes through the cache, -+ * so we want to use the cache buffers. -+ */ -+ if (dev->param.n_caches > 0) { -+ struct yaffs_cache *cache; -+ -+ /* If we can't find the data in the cache, then -+ * load the cache */ -+ cache = yaffs_find_chunk_cache(in, chunk); -+ -+ if (!cache && -+ yaffs_check_alloc_available(dev, 1)) { -+ cache = yaffs_grab_chunk_cache(dev); -+ cache->object = in; -+ cache->chunk_id = chunk; -+ cache->dirty = 0; -+ cache->locked = 0; -+ yaffs_rd_data_obj(in, chunk, -+ cache->data); -+ } else if (cache && -+ !cache->dirty && -+ !yaffs_check_alloc_available(dev, -+ 1)) { -+ /* Drop the cache if it was a read cache -+ * item and no space check has been made -+ * for it. -+ */ -+ cache = NULL; -+ } -+ -+ if (cache) { -+ yaffs_use_cache(dev, cache, 1); -+ cache->locked = 1; -+ -+ memcpy(&cache->data[start], buffer, -+ n_copy); -+ -+ cache->locked = 0; -+ cache->n_bytes = n_writeback; -+ -+ if (write_through) { -+ chunk_written = -+ yaffs_wr_data_obj -+ (cache->object, -+ cache->chunk_id, -+ cache->data, -+ cache->n_bytes, 1); -+ cache->dirty = 0; -+ } -+ } else { -+ chunk_written = -1; /* fail write */ -+ } -+ } else { -+ /* An incomplete start or end chunk (or maybe -+ * both start and end chunk). Read into the -+ * local buffer then copy over and write back. -+ */ -+ -+ u8 *local_buffer = yaffs_get_temp_buffer(dev); -+ -+ yaffs_rd_data_obj(in, chunk, local_buffer); -+ memcpy(&local_buffer[start], buffer, n_copy); -+ -+ chunk_written = -+ yaffs_wr_data_obj(in, chunk, -+ local_buffer, -+ n_writeback, 0); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ } else { -+ /* A full chunk. Write directly from the buffer. */ -+ -+ chunk_written = -+ yaffs_wr_data_obj(in, chunk, buffer, -+ dev->data_bytes_per_chunk, 0); -+ -+ /* Since we've overwritten the cached data, -+ * we better invalidate it. */ -+ yaffs_invalidate_chunk_cache(in, chunk); -+ } -+ -+ if (chunk_written >= 0) { -+ n -= n_copy; -+ offset += n_copy; -+ buffer += n_copy; -+ n_done += n_copy; -+ } -+ } -+ -+ /* Update file object */ -+ -+ if ((start_write + n_done) > in->variant.file_variant.file_size) -+ in->variant.file_variant.file_size = (start_write + n_done); -+ -+ in->dirty = 1; -+ return n_done; -+} -+ -+int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_through) -+{ -+ yaffs2_handle_hole(in, offset); -+ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); -+} -+ -+/* ---------------------- File resizing stuff ------------------ */ -+ -+static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) -+{ -+ -+ struct yaffs_dev *dev = in->my_dev; -+ loff_t old_size = in->variant.file_variant.file_size; -+ int i; -+ int chunk_id; -+ u32 dummy; -+ int last_del; -+ int start_del; -+ -+ if (old_size > 0) -+ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); -+ else -+ last_del = 0; -+ -+ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, -+ &start_del, &dummy); -+ last_del++; -+ start_del++; -+ -+ /* Delete backwards so that we don't end up with holes if -+ * power is lost part-way through the operation. -+ */ -+ for (i = last_del; i >= start_del; i--) { -+ /* NB this could be optimised somewhat, -+ * eg. could retrieve the tags and write them without -+ * using yaffs_chunk_del -+ */ -+ -+ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); -+ -+ if (chunk_id < 1) -+ continue; -+ -+ if (chunk_id < -+ (dev->internal_start_block * dev->param.chunks_per_block) || -+ chunk_id >= -+ ((dev->internal_end_block + 1) * -+ dev->param.chunks_per_block)) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Found daft chunk_id %d for %d", -+ chunk_id, i); -+ } else { -+ in->n_data_chunks--; -+ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); -+ } -+ } -+} -+ -+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) -+{ -+ int new_full; -+ u32 new_partial; -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); -+ -+ yaffs_prune_chunks(obj, new_size); -+ -+ if (new_partial != 0) { -+ int last_chunk = 1 + new_full; -+ u8 *local_buffer = yaffs_get_temp_buffer(dev); -+ -+ /* Rewrite the last chunk with its new size and zero pad */ -+ yaffs_rd_data_obj(obj, last_chunk, local_buffer); -+ memset(local_buffer + new_partial, 0, -+ dev->data_bytes_per_chunk - new_partial); -+ -+ yaffs_wr_data_obj(obj, last_chunk, local_buffer, -+ new_partial, 1); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ -+ obj->variant.file_variant.file_size = new_size; -+ -+ yaffs_prune_tree(dev, &obj->variant.file_variant); -+} -+ -+int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) -+{ -+ struct yaffs_dev *dev = in->my_dev; -+ loff_t old_size = in->variant.file_variant.file_size; -+ -+ yaffs_flush_file_cache(in); -+ yaffs_invalidate_whole_cache(in); -+ -+ yaffs_check_gc(dev, 0); -+ -+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return YAFFS_FAIL; -+ -+ if (new_size == old_size) -+ return YAFFS_OK; -+ -+ if (new_size > old_size) { -+ yaffs2_handle_hole(in, new_size); -+ in->variant.file_variant.file_size = new_size; -+ } else { -+ /* new_size < old_size */ -+ yaffs_resize_file_down(in, new_size); -+ } -+ -+ /* Write a new object header to reflect the resize. -+ * show we've shrunk the file, if need be -+ * Do this only if the file is not in the deleted directories -+ * and is not shadowed. -+ */ -+ if (in->parent && -+ !in->is_shadowed && -+ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && -+ in->parent->obj_id != YAFFS_OBJECTID_DELETED) -+ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); -+ -+ return YAFFS_OK; -+} -+ -+int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync) -+{ -+ if (!in->dirty) -+ return YAFFS_OK; -+ -+ yaffs_flush_file_cache(in); -+ -+ if (data_sync) -+ return YAFFS_OK; -+ -+ if (update_time) -+ yaffs_load_current_time(in, 0, 0); -+ -+ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? -+ YAFFS_OK : YAFFS_FAIL; -+} -+ -+ -+/* yaffs_del_file deletes the whole file data -+ * and the inode associated with the file. -+ * It does not delete the links associated with the file. -+ */ -+static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) -+{ -+ int ret_val; -+ int del_now = 0; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!in->my_inode) -+ del_now = 1; -+ -+ if (del_now) { -+ ret_val = -+ yaffs_change_obj_name(in, in->my_dev->del_dir, -+ _Y("deleted"), 0, 0); -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: immediate deletion of file %d", -+ in->obj_id); -+ in->deleted = 1; -+ in->my_dev->n_deleted_files++; -+ if (dev->param.disable_soft_del || dev->param.is_yaffs2) -+ yaffs_resize_file(in, 0); -+ yaffs_soft_del_file(in); -+ } else { -+ ret_val = -+ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, -+ _Y("unlinked"), 0, 0); -+ } -+ return ret_val; -+} -+ -+static int yaffs_del_file(struct yaffs_obj *in) -+{ -+ int ret_val = YAFFS_OK; -+ int deleted; /* Need to cache value on stack if in is freed */ -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (dev->param.disable_soft_del || dev->param.is_yaffs2) -+ yaffs_resize_file(in, 0); -+ -+ if (in->n_data_chunks > 0) { -+ /* Use soft deletion if there is data in the file. -+ * That won't be the case if it has been resized to zero. -+ */ -+ if (!in->unlinked) -+ ret_val = yaffs_unlink_file_if_needed(in); -+ -+ deleted = in->deleted; -+ -+ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { -+ in->deleted = 1; -+ deleted = 1; -+ in->my_dev->n_deleted_files++; -+ yaffs_soft_del_file(in); -+ } -+ return deleted ? YAFFS_OK : YAFFS_FAIL; -+ } else { -+ /* The file has no data chunks so we toss it immediately */ -+ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); -+ in->variant.file_variant.top = NULL; -+ yaffs_generic_obj_del(in); -+ -+ return YAFFS_OK; -+ } -+} -+ -+int yaffs_is_non_empty_dir(struct yaffs_obj *obj) -+{ -+ return (obj && -+ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && -+ !(list_empty(&obj->variant.dir_variant.children)); -+} -+ -+static int yaffs_del_dir(struct yaffs_obj *obj) -+{ -+ /* First check that the directory is empty. */ -+ if (yaffs_is_non_empty_dir(obj)) -+ return YAFFS_FAIL; -+ -+ return yaffs_generic_obj_del(obj); -+} -+ -+static int yaffs_del_symlink(struct yaffs_obj *in) -+{ -+ kfree(in->variant.symlink_variant.alias); -+ in->variant.symlink_variant.alias = NULL; -+ -+ return yaffs_generic_obj_del(in); -+} -+ -+static int yaffs_del_link(struct yaffs_obj *in) -+{ -+ /* remove this hardlink from the list associated with the equivalent -+ * object -+ */ -+ list_del_init(&in->hard_links); -+ return yaffs_generic_obj_del(in); -+} -+ -+int yaffs_del_obj(struct yaffs_obj *obj) -+{ -+ int ret_val = -1; -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ ret_val = yaffs_del_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ if (!list_empty(&obj->variant.dir_variant.dirty)) { -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "Remove object %d from dirty directories", -+ obj->obj_id); -+ list_del_init(&obj->variant.dir_variant.dirty); -+ } -+ return yaffs_del_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ ret_val = yaffs_del_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ ret_val = yaffs_del_link(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ ret_val = yaffs_generic_obj_del(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ ret_val = 0; -+ break; /* should not happen. */ -+ } -+ return ret_val; -+} -+ -+ -+static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, -+ struct yaffs_obj *to_dir) -+{ -+ struct yaffs_obj *obj; -+ struct list_head *lh; -+ struct list_head *n; -+ -+ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ yaffs_add_obj_to_dir(to_dir, obj); -+ } -+} -+ -+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, -+ enum yaffs_obj_type type) -+{ -+ /* Tear down the old variant */ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ /* Nuke file data */ -+ yaffs_resize_file(obj, 0); -+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); -+ obj->variant.file_variant.top = NULL; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Put the children in lost and found. */ -+ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); -+ if (!list_empty(&obj->variant.dir_variant.dirty)) -+ list_del_init(&obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ /* Nuke symplink data */ -+ kfree(obj->variant.symlink_variant.alias); -+ obj->variant.symlink_variant.alias = NULL; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ list_del_init(&obj->hard_links); -+ break; -+ default: -+ break; -+ } -+ -+ memset(&obj->variant, 0, sizeof(obj->variant)); -+ -+ /*Set up new variant if the memset is not enough. */ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ INIT_LIST_HEAD(&obj->variant.dir_variant.children); -+ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ default: -+ break; -+ } -+ -+ obj->variant_type = type; -+ -+ return obj; -+ -+} -+ -+static int yaffs_unlink_worker(struct yaffs_obj *obj) -+{ -+ int del_now = 0; -+ -+ if (!obj) -+ return YAFFS_FAIL; -+ -+ if (!obj->my_inode) -+ del_now = 1; -+ -+ yaffs_update_parent(obj->parent); -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { -+ return yaffs_del_link(obj); -+ } else if (!list_empty(&obj->hard_links)) { -+ /* Curve ball: We're unlinking an object that has a hardlink. -+ * -+ * This problem arises because we are not strictly following -+ * The Linux link/inode model. -+ * -+ * We can't really delete the object. -+ * Instead, we do the following: -+ * - Select a hardlink. -+ * - Unhook it from the hard links -+ * - Move it from its parent directory so that the rename works. -+ * - Rename the object to the hardlink's name. -+ * - Delete the hardlink -+ */ -+ -+ struct yaffs_obj *hl; -+ struct yaffs_obj *parent; -+ int ret_val; -+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ hl = list_entry(obj->hard_links.next, struct yaffs_obj, -+ hard_links); -+ -+ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); -+ parent = hl->parent; -+ -+ list_del_init(&hl->hard_links); -+ -+ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); -+ -+ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); -+ -+ if (ret_val == YAFFS_OK) -+ ret_val = yaffs_generic_obj_del(hl); -+ -+ return ret_val; -+ -+ } else if (del_now) { -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return yaffs_del_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ list_del_init(&obj->variant.dir_variant.dirty); -+ return yaffs_del_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ return yaffs_del_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ return yaffs_generic_obj_del(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ default: -+ return YAFFS_FAIL; -+ } -+ } else if (yaffs_is_non_empty_dir(obj)) { -+ return YAFFS_FAIL; -+ } else { -+ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, -+ _Y("unlinked"), 0, 0); -+ } -+} -+ -+static int yaffs_unlink_obj(struct yaffs_obj *obj) -+{ -+ if (obj && obj->unlink_allowed) -+ return yaffs_unlink_worker(obj); -+ -+ return YAFFS_FAIL; -+} -+ -+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) -+{ -+ struct yaffs_obj *obj; -+ -+ obj = yaffs_find_by_name(dir, name); -+ return yaffs_unlink_obj(obj); -+} -+ -+/* Note: -+ * If old_name is NULL then we take old_dir as the object to be renamed. -+ */ -+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, -+ struct yaffs_obj *new_dir, const YCHAR *new_name) -+{ -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_obj *existing_target = NULL; -+ int force = 0; -+ int result; -+ struct yaffs_dev *dev; -+ -+ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ dev = old_dir->my_dev; -+ -+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE -+ /* Special case for case insemsitive systems. -+ * While look-up is case insensitive, the name isn't. -+ * Therefore we might want to change x.txt to X.txt -+ */ -+ if (old_dir == new_dir && -+ old_name && new_name && -+ strcmp(old_name, new_name) == 0) -+ force = 1; -+#endif -+ -+ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > -+ YAFFS_MAX_NAME_LENGTH) -+ /* ENAMETOOLONG */ -+ return YAFFS_FAIL; -+ -+ if (old_name) -+ obj = yaffs_find_by_name(old_dir, old_name); -+ else{ -+ obj = old_dir; -+ old_dir = obj->parent; -+ } -+ -+ if (obj && obj->rename_allowed) { -+ /* Now handle an existing target, if there is one */ -+ existing_target = yaffs_find_by_name(new_dir, new_name); -+ if (yaffs_is_non_empty_dir(existing_target)) { -+ return YAFFS_FAIL; /* ENOTEMPTY */ -+ } else if (existing_target && existing_target != obj) { -+ /* Nuke the target first, using shadowing, -+ * but only if it isn't the same object. -+ * -+ * Note we must disable gc here otherwise it can mess -+ * up the shadowing. -+ * -+ */ -+ dev->gc_disable = 1; -+ yaffs_change_obj_name(obj, new_dir, new_name, force, -+ existing_target->obj_id); -+ existing_target->is_shadowed = 1; -+ yaffs_unlink_obj(existing_target); -+ dev->gc_disable = 0; -+ } -+ -+ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); -+ -+ yaffs_update_parent(old_dir); -+ if (new_dir != old_dir) -+ yaffs_update_parent(new_dir); -+ -+ return result; -+ } -+ return YAFFS_FAIL; -+} -+ -+/*----------------------- Initialisation Scanning ---------------------- */ -+ -+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, -+ int backward_scanning) -+{ -+ struct yaffs_obj *obj; -+ -+ if (backward_scanning) { -+ /* Handle YAFFS2 case (backward scanning) -+ * If the shadowed object exists then ignore. -+ */ -+ obj = yaffs_find_by_number(dev, obj_id); -+ if (obj) -+ return; -+ } -+ -+ /* Let's create it (if it does not exist) assuming it is a file so that -+ * it can do shrinking etc. -+ * We put it in unlinked dir to be cleaned up after the scanning -+ */ -+ obj = -+ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); -+ if (!obj) -+ return; -+ obj->is_shadowed = 1; -+ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); -+ obj->variant.file_variant.shrink_size = 0; -+ obj->valid = 1; /* So that we don't read any other info. */ -+} -+ -+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) -+{ -+ struct list_head *lh; -+ struct list_head *save; -+ struct yaffs_obj *hl; -+ struct yaffs_obj *in; -+ -+ list_for_each_safe(lh, save, hard_list) { -+ hl = list_entry(lh, struct yaffs_obj, hard_links); -+ in = yaffs_find_by_number(dev, -+ hl->variant.hardlink_variant.equiv_id); -+ -+ if (in) { -+ /* Add the hardlink pointers */ -+ hl->variant.hardlink_variant.equiv_obj = in; -+ list_add(&hl->hard_links, &in->hard_links); -+ } else { -+ /* Todo Need to report/handle this better. -+ * Got a problem... hardlink to a non-existant object -+ */ -+ hl->variant.hardlink_variant.equiv_obj = NULL; -+ INIT_LIST_HEAD(&hl->hard_links); -+ } -+ } -+} -+ -+static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) -+{ -+ /* -+ * Sort out state of unlinked and deleted objects after scanning. -+ */ -+ struct list_head *i; -+ struct list_head *n; -+ struct yaffs_obj *l; -+ -+ if (dev->read_only) -+ return; -+ -+ /* Soft delete all the unlinked files */ -+ list_for_each_safe(i, n, -+ &dev->unlinked_dir->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ yaffs_del_obj(l); -+ } -+ -+ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ yaffs_del_obj(l); -+ } -+} -+ -+/* -+ * This code iterates through all the objects making sure that they are rooted. -+ * Any unrooted objects are re-rooted in lost+found. -+ * An object needs to be in one of: -+ * - Directly under deleted, unlinked -+ * - Directly or indirectly under root. -+ * -+ * Note: -+ * This code assumes that we don't ever change the current relationships -+ * between directories: -+ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL -+ * lost-n-found->parent == root_dir -+ * -+ * This fixes the problem where directories might have inadvertently been -+ * deleted leaving the object "hanging" without being rooted in the -+ * directory tree. -+ */ -+ -+static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) -+{ -+ return (obj == dev->del_dir || -+ obj == dev->unlinked_dir || obj == dev->root_dir); -+} -+ -+static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_obj *parent; -+ int i; -+ struct list_head *lh; -+ struct list_head *n; -+ int depth_limit; -+ int hanging; -+ -+ if (dev->read_only) -+ return; -+ -+ /* Iterate through the objects in each hash entry, -+ * looking at each object. -+ * Make sure it is rooted. -+ */ -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ parent = obj->parent; -+ -+ if (yaffs_has_null_parent(dev, obj)) { -+ /* These directories are not hanging */ -+ hanging = 0; -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ hanging = 1; -+ } else if (yaffs_has_null_parent(dev, parent)) { -+ hanging = 0; -+ } else { -+ /* -+ * Need to follow the parent chain to -+ * see if it is hanging. -+ */ -+ hanging = 0; -+ depth_limit = 100; -+ -+ while (parent != dev->root_dir && -+ parent->parent && -+ parent->parent->variant_type == -+ YAFFS_OBJECT_TYPE_DIRECTORY && -+ depth_limit > 0) { -+ parent = parent->parent; -+ depth_limit--; -+ } -+ if (parent != dev->root_dir) -+ hanging = 1; -+ } -+ if (hanging) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Hanging object %d moved to lost and found", -+ obj->obj_id); -+ yaffs_add_obj_to_dir(dev->lost_n_found, obj); -+ } -+ } -+ } -+} -+ -+/* -+ * Delete directory contents for cleaning up lost and found. -+ */ -+static void yaffs_del_dir_contents(struct yaffs_obj *dir) -+{ -+ struct yaffs_obj *obj; -+ struct list_head *lh; -+ struct list_head *n; -+ -+ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) -+ BUG(); -+ -+ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) -+ yaffs_del_dir_contents(obj); -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Deleting lost_found object %d", -+ obj->obj_id); -+ yaffs_unlink_obj(obj); -+ } -+} -+ -+static void yaffs_empty_l_n_f(struct yaffs_dev *dev) -+{ -+ yaffs_del_dir_contents(dev->lost_n_found); -+} -+ -+ -+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, -+ const YCHAR *name) -+{ -+ int sum; -+ struct list_head *i; -+ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; -+ struct yaffs_obj *l; -+ -+ if (!name) -+ return NULL; -+ -+ if (!directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_find_by_name: null pointer directory" -+ ); -+ BUG(); -+ return NULL; -+ } -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_find_by_name: non-directory" -+ ); -+ BUG(); -+ } -+ -+ sum = yaffs_calc_name_sum(name); -+ -+ list_for_each(i, &directory->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ -+ if (l->parent != directory) -+ BUG(); -+ -+ yaffs_check_obj_details_loaded(l); -+ -+ /* Special case for lost-n-found */ -+ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { -+ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) -+ return l; -+ } else if (l->sum == sum || l->hdr_chunk <= 0) { -+ /* LostnFound chunk called Objxxx -+ * Do a real check -+ */ -+ yaffs_get_obj_name(l, buffer, -+ YAFFS_MAX_NAME_LENGTH + 1); -+ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) -+ return l; -+ } -+ } -+ return NULL; -+} -+ -+/* GetEquivalentObject dereferences any hard links to get to the -+ * actual object. -+ */ -+ -+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) -+{ -+ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { -+ obj = obj->variant.hardlink_variant.equiv_obj; -+ yaffs_check_obj_details_loaded(obj); -+ } -+ return obj; -+} -+ -+/* -+ * A note or two on object names. -+ * * If the object name is missing, we then make one up in the form objnnn -+ * -+ * * ASCII names are stored in the object header's name field from byte zero -+ * * Unicode names are historically stored starting from byte zero. -+ * -+ * Then there are automatic Unicode names... -+ * The purpose of these is to save names in a way that can be read as -+ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII -+ * system to share files. -+ * -+ * These automatic unicode are stored slightly differently... -+ * - If the name can fit in the ASCII character space then they are saved as -+ * ascii names as per above. -+ * - If the name needs Unicode then the name is saved in Unicode -+ * starting at oh->name[1]. -+ -+ */ -+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, -+ int buffer_size) -+{ -+ /* Create an object name if we could not find one. */ -+ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { -+ YCHAR local_name[20]; -+ YCHAR num_string[20]; -+ YCHAR *x = &num_string[19]; -+ unsigned v = obj->obj_id; -+ num_string[19] = 0; -+ while (v > 0) { -+ x--; -+ *x = '0' + (v % 10); -+ v /= 10; -+ } -+ /* make up a name */ -+ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); -+ strcat(local_name, x); -+ strncpy(name, local_name, buffer_size - 1); -+ } -+} -+ -+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) -+{ -+ memset(name, 0, buffer_size * sizeof(YCHAR)); -+ yaffs_check_obj_details_loaded(obj); -+ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { -+ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); -+ } else if (obj->short_name[0]) { -+ strcpy(name, obj->short_name); -+ } else if (obj->hdr_chunk > 0) { -+ int result; -+ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); -+ -+ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; -+ -+ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); -+ -+ if (obj->hdr_chunk > 0) { -+ result = yaffs_rd_chunk_tags_nand(obj->my_dev, -+ obj->hdr_chunk, -+ buffer, NULL); -+ } -+ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, -+ buffer_size); -+ -+ yaffs_release_temp_buffer(obj->my_dev, buffer); -+ } -+ -+ yaffs_fix_null_name(obj, name, buffer_size); -+ -+ return strnlen(name, YAFFS_MAX_NAME_LENGTH); -+} -+ -+loff_t yaffs_get_obj_length(struct yaffs_obj *obj) -+{ -+ /* Dereference any hard linking */ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ return obj->variant.file_variant.file_size; -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ if (!obj->variant.symlink_variant.alias) -+ return 0; -+ return strnlen(obj->variant.symlink_variant.alias, -+ YAFFS_MAX_ALIAS_LENGTH); -+ } else { -+ /* Only a directory should drop through to here */ -+ return obj->my_dev->data_bytes_per_chunk; -+ } -+} -+ -+int yaffs_get_obj_link_count(struct yaffs_obj *obj) -+{ -+ int count = 0; -+ struct list_head *i; -+ -+ if (!obj->unlinked) -+ count++; /* the object itself */ -+ -+ list_for_each(i, &obj->hard_links) -+ count++; /* add the hard links; */ -+ -+ return count; -+} -+ -+int yaffs_get_obj_inode(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ return obj->obj_id; -+} -+ -+unsigned yaffs_get_obj_type(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return DT_REG; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ return DT_DIR; -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ return DT_LNK; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ return DT_REG; -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ if (S_ISFIFO(obj->yst_mode)) -+ return DT_FIFO; -+ if (S_ISCHR(obj->yst_mode)) -+ return DT_CHR; -+ if (S_ISBLK(obj->yst_mode)) -+ return DT_BLK; -+ if (S_ISSOCK(obj->yst_mode)) -+ return DT_SOCK; -+ return DT_REG; -+ break; -+ default: -+ return DT_REG; -+ break; -+ } -+} -+ -+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) -+ return yaffs_clone_str(obj->variant.symlink_variant.alias); -+ else -+ return yaffs_clone_str(_Y("")); -+} -+ -+/*--------------------------- Initialisation code -------------------------- */ -+ -+static int yaffs_check_dev_fns(struct yaffs_dev *dev) -+{ -+ struct yaffs_driver *drv = &dev->drv; -+ struct yaffs_tags_handler *tagger = &dev->tagger; -+ -+ /* Common functions, gotta have */ -+ if (!drv->drv_read_chunk_fn || -+ !drv->drv_write_chunk_fn || -+ !drv->drv_erase_fn) -+ return 0; -+ -+ if (dev->param.is_yaffs2 && -+ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) -+ return 0; -+ -+ /* Install the default tags marshalling functions if needed. */ -+ yaffs_tags_compat_install(dev); -+ yaffs_tags_marshall_install(dev); -+ -+ /* Check we now have the marshalling functions required. */ -+ if (!tagger->write_chunk_tags_fn || -+ !tagger->read_chunk_tags_fn || -+ !tagger->query_block_fn || -+ !tagger->mark_bad_fn) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs_create_initial_dir(struct yaffs_dev *dev) -+{ -+ /* Initialise the unlinked, deleted, root and lost+found directories */ -+ dev->lost_n_found = dev->root_dir = NULL; -+ dev->unlinked_dir = dev->del_dir = NULL; -+ dev->unlinked_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); -+ dev->del_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); -+ dev->root_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, -+ YAFFS_ROOT_MODE | S_IFDIR); -+ dev->lost_n_found = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, -+ YAFFS_LOSTNFOUND_MODE | S_IFDIR); -+ -+ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir -+ && dev->del_dir) { -+ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); -+ return YAFFS_OK; -+ } -+ return YAFFS_FAIL; -+} -+ -+/* Low level init. -+ * Typically only used by yaffs_guts_initialise, but also used by the -+ * Low level yaffs driver tests. -+ */ -+ -+int yaffs_guts_ll_init(struct yaffs_dev *dev) -+{ -+ -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); -+ -+ if (!dev) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Need a device" -+ ); -+ return YAFFS_FAIL; -+ } -+ -+ if (dev->ll_init) -+ return YAFFS_OK; -+ -+ dev->internal_start_block = dev->param.start_block; -+ dev->internal_end_block = dev->param.end_block; -+ dev->block_offset = 0; -+ dev->chunk_offset = 0; -+ dev->n_free_chunks = 0; -+ -+ dev->gc_block = 0; -+ -+ if (dev->param.start_block == 0) { -+ dev->internal_start_block = dev->param.start_block + 1; -+ dev->internal_end_block = dev->param.end_block + 1; -+ dev->block_offset = 1; -+ dev->chunk_offset = dev->param.chunks_per_block; -+ } -+ -+ /* Check geometry parameters. */ -+ -+ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && -+ dev->param.total_bytes_per_chunk < 1024) || -+ (!dev->param.is_yaffs2 && -+ dev->param.total_bytes_per_chunk < 512) || -+ (dev->param.inband_tags && !dev->param.is_yaffs2) || -+ dev->param.chunks_per_block < 2 || -+ dev->param.n_reserved_blocks < 2 || -+ dev->internal_start_block <= 0 || -+ dev->internal_end_block <= 0 || -+ dev->internal_end_block <= -+ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) -+ ) { -+ /* otherwise it is too small */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", -+ dev->param.total_bytes_per_chunk, -+ dev->param.is_yaffs2 ? "2" : "", -+ dev->param.inband_tags); -+ return YAFFS_FAIL; -+ } -+ -+ /* Sort out space for inband tags, if required */ -+ if (dev->param.inband_tags) -+ dev->data_bytes_per_chunk = -+ dev->param.total_bytes_per_chunk - -+ sizeof(struct yaffs_packed_tags2_tags_only); -+ else -+ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; -+ -+ /* Got the right mix of functions? */ -+ if (!yaffs_check_dev_fns(dev)) { -+ /* Function missing */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "device function(s) missing or wrong"); -+ -+ return YAFFS_FAIL; -+ } -+ -+ if (yaffs_init_nand(dev) != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); -+ return YAFFS_FAIL; -+ } -+ -+ return YAFFS_OK; -+} -+ -+ -+int yaffs_guts_format_dev(struct yaffs_dev *dev) -+{ -+ int i; -+ enum yaffs_block_state state; -+ u32 dummy; -+ -+ if(yaffs_guts_ll_init(dev) != YAFFS_OK) -+ return YAFFS_FAIL; -+ -+ if(dev->is_mounted) -+ return YAFFS_FAIL; -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ yaffs_query_init_block_state(dev, i, &state, &dummy); -+ if (state != YAFFS_BLOCK_STATE_DEAD) -+ yaffs_erase_block(dev, i); -+ } -+ -+ return YAFFS_OK; -+} -+ -+ -+int yaffs_guts_initialise(struct yaffs_dev *dev) -+{ -+ int init_failed = 0; -+ unsigned x; -+ int bits; -+ -+ if(yaffs_guts_ll_init(dev) != YAFFS_OK) -+ return YAFFS_FAIL; -+ -+ if (dev->is_mounted) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); -+ return YAFFS_FAIL; -+ } -+ -+ dev->is_mounted = 1; -+ -+ /* OK now calculate a few things for the device */ -+ -+ /* -+ * Calculate all the chunk size manipulation numbers: -+ */ -+ x = dev->data_bytes_per_chunk; -+ /* We always use dev->chunk_shift and dev->chunk_div */ -+ dev->chunk_shift = calc_shifts(x); -+ x >>= dev->chunk_shift; -+ dev->chunk_div = x; -+ /* We only use chunk mask if chunk_div is 1 */ -+ dev->chunk_mask = (1 << dev->chunk_shift) - 1; -+ -+ /* -+ * Calculate chunk_grp_bits. -+ * We need to find the next power of 2 > than internal_end_block -+ */ -+ -+ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); -+ -+ bits = calc_shifts_ceiling(x); -+ -+ /* Set up tnode width if wide tnodes are enabled. */ -+ if (!dev->param.wide_tnodes_disabled) { -+ /* bits must be even so that we end up with 32-bit words */ -+ if (bits & 1) -+ bits++; -+ if (bits < 16) -+ dev->tnode_width = 16; -+ else -+ dev->tnode_width = bits; -+ } else { -+ dev->tnode_width = 16; -+ } -+ -+ dev->tnode_mask = (1 << dev->tnode_width) - 1; -+ -+ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), -+ * so if the bitwidth of the -+ * chunk range we're using is greater than 16 we need -+ * to figure out chunk shift and chunk_grp_size -+ */ -+ -+ if (bits <= dev->tnode_width) -+ dev->chunk_grp_bits = 0; -+ else -+ dev->chunk_grp_bits = bits - dev->tnode_width; -+ -+ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; -+ if (dev->tnode_size < sizeof(struct yaffs_tnode)) -+ dev->tnode_size = sizeof(struct yaffs_tnode); -+ -+ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; -+ -+ if (dev->param.chunks_per_block < dev->chunk_grp_size) { -+ /* We have a problem because the soft delete won't work if -+ * the chunk group size > chunks per block. -+ * This can be remedied by using larger "virtual blocks". -+ */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); -+ -+ return YAFFS_FAIL; -+ } -+ -+ /* Finished verifying the device, continue with initialisation */ -+ -+ /* More device initialisation */ -+ dev->all_gcs = 0; -+ dev->passive_gc_count = 0; -+ dev->oldest_dirty_gc_count = 0; -+ dev->bg_gcs = 0; -+ dev->gc_block_finder = 0; -+ dev->buffered_block = -1; -+ dev->doing_buffered_block_rewrite = 0; -+ dev->n_deleted_files = 0; -+ dev->n_bg_deletions = 0; -+ dev->n_unlinked_files = 0; -+ dev->n_ecc_fixed = 0; -+ dev->n_ecc_unfixed = 0; -+ dev->n_tags_ecc_fixed = 0; -+ dev->n_tags_ecc_unfixed = 0; -+ dev->n_erase_failures = 0; -+ dev->n_erased_blocks = 0; -+ dev->gc_disable = 0; -+ dev->has_pending_prioritised_gc = 1; -+ /* Assume the worst for now, will get fixed on first GC */ -+ INIT_LIST_HEAD(&dev->dirty_dirs); -+ dev->oldest_dirty_seq = 0; -+ dev->oldest_dirty_block = 0; -+ -+ /* Initialise temporary buffers and caches. */ -+ if (!yaffs_init_tmp_buffers(dev)) -+ init_failed = 1; -+ -+ dev->cache = NULL; -+ dev->gc_cleanup_list = NULL; -+ -+ if (!init_failed && dev->param.n_caches > 0) { -+ int i; -+ void *buf; -+ int cache_bytes = -+ dev->param.n_caches * sizeof(struct yaffs_cache); -+ -+ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) -+ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; -+ -+ dev->cache = kmalloc(cache_bytes, GFP_NOFS); -+ -+ buf = (u8 *) dev->cache; -+ -+ if (dev->cache) -+ memset(dev->cache, 0, cache_bytes); -+ -+ for (i = 0; i < dev->param.n_caches && buf; i++) { -+ dev->cache[i].object = NULL; -+ dev->cache[i].last_use = 0; -+ dev->cache[i].dirty = 0; -+ dev->cache[i].data = buf = -+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ } -+ if (!buf) -+ init_failed = 1; -+ -+ dev->cache_last_use = 0; -+ } -+ -+ dev->cache_hits = 0; -+ -+ if (!init_failed) { -+ dev->gc_cleanup_list = -+ kmalloc(dev->param.chunks_per_block * sizeof(u32), -+ GFP_NOFS); -+ if (!dev->gc_cleanup_list) -+ init_failed = 1; -+ } -+ -+ if (dev->param.is_yaffs2) -+ dev->param.use_header_file_size = 1; -+ -+ if (!init_failed && !yaffs_init_blocks(dev)) -+ init_failed = 1; -+ -+ yaffs_init_tnodes_and_objs(dev); -+ -+ if (!init_failed && !yaffs_create_initial_dir(dev)) -+ init_failed = 1; -+ -+ if (!init_failed && dev->param.is_yaffs2 && -+ !dev->param.disable_summary && -+ !yaffs_summary_init(dev)) -+ init_failed = 1; -+ -+ if (!init_failed) { -+ /* Now scan the flash. */ -+ if (dev->param.is_yaffs2) { -+ if (yaffs2_checkpt_restore(dev)) { -+ yaffs_check_obj_details_loaded(dev->root_dir); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | -+ YAFFS_TRACE_MOUNT, -+ "yaffs: restored from checkpoint" -+ ); -+ } else { -+ -+ /* Clean up the mess caused by an aborted -+ * checkpoint load then scan backwards. -+ */ -+ yaffs_deinit_blocks(dev); -+ -+ yaffs_deinit_tnodes_and_objs(dev); -+ -+ dev->n_erased_blocks = 0; -+ dev->n_free_chunks = 0; -+ dev->alloc_block = -1; -+ dev->alloc_page = -1; -+ dev->n_deleted_files = 0; -+ dev->n_unlinked_files = 0; -+ dev->n_bg_deletions = 0; -+ -+ if (!init_failed && !yaffs_init_blocks(dev)) -+ init_failed = 1; -+ -+ yaffs_init_tnodes_and_objs(dev); -+ -+ if (!init_failed -+ && !yaffs_create_initial_dir(dev)) -+ init_failed = 1; -+ -+ if (!init_failed && !yaffs2_scan_backwards(dev)) -+ init_failed = 1; -+ } -+ } else if (!yaffs1_scan(dev)) { -+ init_failed = 1; -+ } -+ -+ yaffs_strip_deleted_objs(dev); -+ yaffs_fix_hanging_objs(dev); -+ if (dev->param.empty_lost_n_found) -+ yaffs_empty_l_n_f(dev); -+ } -+ -+ if (init_failed) { -+ /* Clean up the mess */ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: yaffs_guts_initialise() aborted."); -+ -+ yaffs_deinitialise(dev); -+ return YAFFS_FAIL; -+ } -+ -+ /* Zero out stats */ -+ dev->n_page_reads = 0; -+ dev->n_page_writes = 0; -+ dev->n_erasures = 0; -+ dev->n_gc_copies = 0; -+ dev->n_retried_writes = 0; -+ -+ dev->n_retired_blocks = 0; -+ -+ yaffs_verify_free_chunks(dev); -+ yaffs_verify_blocks(dev); -+ -+ /* Clean up any aborted checkpoint data */ -+ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) -+ yaffs2_checkpt_invalidate(dev); -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: yaffs_guts_initialise() done."); -+ return YAFFS_OK; -+} -+ -+void yaffs_deinitialise(struct yaffs_dev *dev) -+{ -+ if (dev->is_mounted) { -+ int i; -+ -+ yaffs_deinit_blocks(dev); -+ yaffs_deinit_tnodes_and_objs(dev); -+ yaffs_summary_deinit(dev); -+ -+ if (dev->param.n_caches > 0 && dev->cache) { -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ kfree(dev->cache[i].data); -+ dev->cache[i].data = NULL; -+ } -+ -+ kfree(dev->cache); -+ dev->cache = NULL; -+ } -+ -+ kfree(dev->gc_cleanup_list); -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) -+ kfree(dev->temp_buffer[i].buffer); -+ -+ dev->is_mounted = 0; -+ -+ yaffs_deinit_nand(dev); -+ } -+} -+ -+int yaffs_count_free_chunks(struct yaffs_dev *dev) -+{ -+ int n_free = 0; -+ int b; -+ struct yaffs_block_info *blk; -+ -+ blk = dev->block_info; -+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { -+ switch (blk->block_state) { -+ case YAFFS_BLOCK_STATE_EMPTY: -+ case YAFFS_BLOCK_STATE_ALLOCATING: -+ case YAFFS_BLOCK_STATE_COLLECTING: -+ case YAFFS_BLOCK_STATE_FULL: -+ n_free += -+ (dev->param.chunks_per_block - blk->pages_in_use + -+ blk->soft_del_pages); -+ break; -+ default: -+ break; -+ } -+ blk++; -+ } -+ return n_free; -+} -+ -+int yaffs_get_n_free_chunks(struct yaffs_dev *dev) -+{ -+ /* This is what we report to the outside world */ -+ int n_free; -+ int n_dirty_caches; -+ int blocks_for_checkpt; -+ int i; -+ -+ n_free = dev->n_free_chunks; -+ n_free += dev->n_deleted_files; -+ -+ /* Now count and subtract the number of dirty chunks in the cache. */ -+ -+ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].dirty) -+ n_dirty_caches++; -+ } -+ -+ n_free -= n_dirty_caches; -+ -+ n_free -= -+ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); -+ -+ /* Now figure checkpoint space and report that... */ -+ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); -+ -+ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); -+ -+ if (n_free < 0) -+ n_free = 0; -+ -+ return n_free; -+} -+ -+ -+ -+/* -+ * Marshalling functions to get loff_t file sizes into and out of -+ * object headers. -+ */ -+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) -+{ -+ oh->file_size_low = (fsize & 0xFFFFFFFF); -+ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); -+} -+ -+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) -+{ -+ loff_t retval; -+ -+ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) -+ retval = (((loff_t) oh->file_size_high) << 32) | -+ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); -+ else -+ retval = (loff_t) oh->file_size_low; -+ -+ return retval; -+} -+ -+ -+void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) -+{ -+ int i; -+ struct yaffs_block_info *bi; -+ int s; -+ -+ for(i = 0; i < 10; i++) -+ bs[i] = 0; -+ -+ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ bi = yaffs_get_block_info(dev, i); -+ s = bi->block_state; -+ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) -+ bs[0]++; -+ else -+ bs[s]++; -+ } -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_guts.h linux-3.10.40/fs/yaffs2/yaffs_guts.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_guts.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,1007 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_GUTS_H__ -+#define __YAFFS_GUTS_H__ -+ -+#include "yportenv.h" -+ -+#define YAFFS_OK 1 -+#define YAFFS_FAIL 0 -+ -+/* Give us a Y=0x59, -+ * Give us an A=0x41, -+ * Give us an FF=0xff -+ * Give us an S=0x53 -+ * And what have we got... -+ */ -+#define YAFFS_MAGIC 0x5941ff53 -+ -+/* -+ * Tnodes form a tree with the tnodes in "levels" -+ * Levels greater than 0 hold 8 slots which point to other tnodes. -+ * Those at level 0 hold 16 slots which point to chunks in NAND. -+ * -+ * A maximum level of 8 thust supports files of size up to: -+ * -+ * 2^(3*MAX_LEVEL+4) -+ * -+ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives -+ * a maximum file size of around 512Gbytees with 2k chunks. -+ */ -+#define YAFFS_NTNODES_LEVEL0 16 -+#define YAFFS_TNODES_LEVEL0_BITS 4 -+#define YAFFS_TNODES_LEVEL0_MASK 0xf -+ -+#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) -+#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) -+#define YAFFS_TNODES_INTERNAL_MASK 0x7 -+#define YAFFS_TNODES_MAX_LEVEL 8 -+#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ -+ YAFFS_TNODES_INTERNAL_BITS * \ -+ YAFFS_TNODES_MAX_LEVEL) -+#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) -+ -+#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff -+ -+/* Constants for YAFFS1 mode */ -+#define YAFFS_BYTES_PER_SPARE 16 -+#define YAFFS_BYTES_PER_CHUNK 512 -+#define YAFFS_CHUNK_SIZE_SHIFT 9 -+#define YAFFS_CHUNKS_PER_BLOCK 32 -+#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) -+ -+#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 -+#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 -+ -+ -+ -+#define YAFFS_ALLOCATION_NOBJECTS 100 -+#define YAFFS_ALLOCATION_NTNODES 100 -+#define YAFFS_ALLOCATION_NLINKS 100 -+ -+#define YAFFS_NOBJECT_BUCKETS 256 -+ -+#define YAFFS_OBJECT_SPACE 0x40000 -+#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) -+ -+/* Binary data version stamps */ -+#define YAFFS_SUMMARY_VERSION 1 -+#define YAFFS_CHECKPOINT_VERSION 7 -+ -+#ifdef CONFIG_YAFFS_UNICODE -+#define YAFFS_MAX_NAME_LENGTH 127 -+#define YAFFS_MAX_ALIAS_LENGTH 79 -+#else -+#define YAFFS_MAX_NAME_LENGTH 255 -+#define YAFFS_MAX_ALIAS_LENGTH 159 -+#endif -+ -+#define YAFFS_SHORT_NAME_LENGTH 15 -+ -+/* Some special object ids for pseudo objects */ -+#define YAFFS_OBJECTID_ROOT 1 -+#define YAFFS_OBJECTID_LOSTNFOUND 2 -+#define YAFFS_OBJECTID_UNLINKED 3 -+#define YAFFS_OBJECTID_DELETED 4 -+ -+/* Fake object Id for summary data */ -+#define YAFFS_OBJECTID_SUMMARY 0x10 -+ -+/* Pseudo object ids for checkpointing */ -+#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 -+#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 -+ -+#define YAFFS_MAX_SHORT_OP_CACHES 20 -+ -+#define YAFFS_N_TEMP_BUFFERS 6 -+ -+/* We limit the number attempts at sucessfully saving a chunk of data. -+ * Small-page devices have 32 pages per block; large-page devices have 64. -+ * Default to something in the order of 5 to 10 blocks worth of chunks. -+ */ -+#define YAFFS_WR_ATTEMPTS (5*64) -+ -+/* Sequence numbers are used in YAFFS2 to determine block allocation order. -+ * The range is limited slightly to help distinguish bad numbers from good. -+ * This also allows us to perhaps in the future use special numbers for -+ * special purposes. -+ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, -+ * and is a larger number than the lifetime of a 2GB device. -+ */ -+#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 -+#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 -+ -+/* Special sequence number for bad block that failed to be marked bad */ -+#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 -+ -+/* ChunkCache is used for short read/write operations.*/ -+struct yaffs_cache { -+ struct yaffs_obj *object; -+ int chunk_id; -+ int last_use; -+ int dirty; -+ int n_bytes; /* Only valid if the cache is dirty */ -+ int locked; /* Can't push out or flush while locked. */ -+ u8 *data; -+}; -+ -+/* yaffs1 tags structures in RAM -+ * NB This uses bitfield. Bitfields should not straddle a u32 boundary -+ * otherwise the structure size will get blown out. -+ */ -+ -+struct yaffs_tags { -+ u32 chunk_id:20; -+ u32 serial_number:2; -+ u32 n_bytes_lsb:10; -+ u32 obj_id:18; -+ u32 ecc:12; -+ u32 n_bytes_msb:2; -+}; -+ -+union yaffs_tags_union { -+ struct yaffs_tags as_tags; -+ u8 as_bytes[8]; -+}; -+ -+ -+/* Stuff used for extended tags in YAFFS2 */ -+ -+enum yaffs_ecc_result { -+ YAFFS_ECC_RESULT_UNKNOWN, -+ YAFFS_ECC_RESULT_NO_ERROR, -+ YAFFS_ECC_RESULT_FIXED, -+ YAFFS_ECC_RESULT_UNFIXED -+}; -+ -+enum yaffs_obj_type { -+ YAFFS_OBJECT_TYPE_UNKNOWN, -+ YAFFS_OBJECT_TYPE_FILE, -+ YAFFS_OBJECT_TYPE_SYMLINK, -+ YAFFS_OBJECT_TYPE_DIRECTORY, -+ YAFFS_OBJECT_TYPE_HARDLINK, -+ YAFFS_OBJECT_TYPE_SPECIAL -+}; -+ -+#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL -+ -+struct yaffs_ext_tags { -+ unsigned chunk_used; /* Status of the chunk: used or unused */ -+ unsigned obj_id; /* If 0 this is not used */ -+ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ -+ unsigned n_bytes; /* Only valid for data chunks */ -+ -+ /* The following stuff only has meaning when we read */ -+ enum yaffs_ecc_result ecc_result; -+ unsigned block_bad; -+ -+ /* YAFFS 1 stuff */ -+ unsigned is_deleted; /* The chunk is marked deleted */ -+ unsigned serial_number; /* Yaffs1 2-bit serial number */ -+ -+ /* YAFFS2 stuff */ -+ unsigned seq_number; /* The sequence number of this block */ -+ -+ /* Extra info if this is an object header (YAFFS2 only) */ -+ -+ unsigned extra_available; /* Extra info available if not zero */ -+ unsigned extra_parent_id; /* The parent object */ -+ unsigned extra_is_shrink; /* Is it a shrink header? */ -+ unsigned extra_shadows; /* Does this shadow another object? */ -+ -+ enum yaffs_obj_type extra_obj_type; /* What object type? */ -+ -+ loff_t extra_file_size; /* Length if it is a file */ -+ unsigned extra_equiv_id; /* Equivalent object for a hard link */ -+}; -+ -+/* Spare structure for YAFFS1 */ -+struct yaffs_spare { -+ u8 tb0; -+ u8 tb1; -+ u8 tb2; -+ u8 tb3; -+ u8 page_status; /* set to 0 to delete the chunk */ -+ u8 block_status; -+ u8 tb4; -+ u8 tb5; -+ u8 ecc1[3]; -+ u8 tb6; -+ u8 tb7; -+ u8 ecc2[3]; -+}; -+ -+/*Special structure for passing through to mtd */ -+struct yaffs_nand_spare { -+ struct yaffs_spare spare; -+ int eccres1; -+ int eccres2; -+}; -+ -+/* Block data in RAM */ -+ -+enum yaffs_block_state { -+ YAFFS_BLOCK_STATE_UNKNOWN = 0, -+ -+ YAFFS_BLOCK_STATE_SCANNING, -+ /* Being scanned */ -+ -+ YAFFS_BLOCK_STATE_NEEDS_SCAN, -+ /* The block might have something on it (ie it is allocating or full, -+ * perhaps empty) but it needs to be scanned to determine its true -+ * state. -+ * This state is only valid during scanning. -+ * NB We tolerate empty because the pre-scanner might be incapable of -+ * deciding -+ * However, if this state is returned on a YAFFS2 device, -+ * then we expect a sequence number -+ */ -+ -+ YAFFS_BLOCK_STATE_EMPTY, -+ /* This block is empty */ -+ -+ YAFFS_BLOCK_STATE_ALLOCATING, -+ /* This block is partially allocated. -+ * At least one page holds valid data. -+ * This is the one currently being used for page -+ * allocation. Should never be more than one of these. -+ * If a block is only partially allocated at mount it is treated as -+ * full. -+ */ -+ -+ YAFFS_BLOCK_STATE_FULL, -+ /* All the pages in this block have been allocated. -+ * If a block was only partially allocated when mounted we treat -+ * it as fully allocated. -+ */ -+ -+ YAFFS_BLOCK_STATE_DIRTY, -+ /* The block was full and now all chunks have been deleted. -+ * Erase me, reuse me. -+ */ -+ -+ YAFFS_BLOCK_STATE_CHECKPOINT, -+ /* This block is assigned to holding checkpoint data. */ -+ -+ YAFFS_BLOCK_STATE_COLLECTING, -+ /* This block is being garbage collected */ -+ -+ YAFFS_BLOCK_STATE_DEAD -+ /* This block has failed and is not in use */ -+}; -+ -+#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) -+ -+struct yaffs_block_info { -+ -+ s32 soft_del_pages:10; /* number of soft deleted pages */ -+ s32 pages_in_use:10; /* number of pages in use */ -+ u32 block_state:4; /* One of the above block states. */ -+ /* NB use unsigned because enum is sometimes -+ * an int */ -+ u32 needs_retiring:1; /* Data has failed on this block, */ -+ /*need to get valid data off and retire*/ -+ u32 skip_erased_check:1;/* Skip the erased check on this block */ -+ u32 gc_prioritise:1; /* An ECC check or blank check has failed. -+ Block should be prioritised for GC */ -+ u32 chunk_error_strikes:3; /* How many times we've had ecc etc -+ failures on this block and tried to reuse it */ -+ u32 has_summary:1; /* The block has a summary */ -+ -+ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ -+ u32 seq_number; /* block sequence number for yaffs2 */ -+ -+}; -+ -+/* -------------------------- Object structure -------------------------------*/ -+/* This is the object structure as stored on NAND */ -+ -+struct yaffs_obj_hdr { -+ enum yaffs_obj_type type; -+ -+ /* Apply to everything */ -+ int parent_obj_id; -+ u16 sum_no_longer_used; /* checksum of name. No longer used */ -+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ /* The following apply to all object types except for hard links */ -+ u32 yst_mode; /* protection */ -+ -+ u32 yst_uid; -+ u32 yst_gid; -+ u32 yst_atime; -+ u32 yst_mtime; -+ u32 yst_ctime; -+ -+ /* File size applies to files only */ -+ u32 file_size_low; -+ -+ /* Equivalent object id applies to hard links only. */ -+ int equiv_id; -+ -+ /* Alias is for symlinks only. */ -+ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; -+ -+ u32 yst_rdev; /* stuff for block and char devices (major/min) */ -+ -+ u32 win_ctime[2]; -+ u32 win_atime[2]; -+ u32 win_mtime[2]; -+ -+ u32 inband_shadowed_obj_id; -+ u32 inband_is_shrink; -+ -+ u32 file_size_high; -+ u32 reserved[1]; -+ int shadows_obj; /* This object header shadows the -+ specified object if > 0 */ -+ -+ /* is_shrink applies to object headers written when wemake a hole. */ -+ u32 is_shrink; -+ -+}; -+ -+/*--------------------------- Tnode -------------------------- */ -+ -+struct yaffs_tnode { -+ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; -+}; -+ -+/*------------------------ Object -----------------------------*/ -+/* An object can be one of: -+ * - a directory (no data, has children links -+ * - a regular file (data.... not prunes :->). -+ * - a symlink [symbolic link] (the alias). -+ * - a hard link -+ */ -+ -+struct yaffs_file_var { -+ loff_t file_size; -+ loff_t scanned_size; -+ loff_t shrink_size; -+ int top_level; -+ struct yaffs_tnode *top; -+}; -+ -+struct yaffs_dir_var { -+ struct list_head children; /* list of child links */ -+ struct list_head dirty; /* Entry for list of dirty directories */ -+}; -+ -+struct yaffs_symlink_var { -+ YCHAR *alias; -+}; -+ -+struct yaffs_hardlink_var { -+ struct yaffs_obj *equiv_obj; -+ u32 equiv_id; -+}; -+ -+union yaffs_obj_var { -+ struct yaffs_file_var file_variant; -+ struct yaffs_dir_var dir_variant; -+ struct yaffs_symlink_var symlink_variant; -+ struct yaffs_hardlink_var hardlink_variant; -+}; -+ -+struct yaffs_obj { -+ u8 deleted:1; /* This should only apply to unlinked files. */ -+ u8 soft_del:1; /* it has also been soft deleted */ -+ u8 unlinked:1; /* An unlinked file.*/ -+ u8 fake:1; /* A fake object has no presence on NAND. */ -+ u8 rename_allowed:1; /* Some objects cannot be renamed. */ -+ u8 unlink_allowed:1; -+ u8 dirty:1; /* the object needs to be written to flash */ -+ u8 valid:1; /* When the file system is being loaded up, this -+ * object might be created before the data -+ * is available -+ * ie. file data chunks encountered before -+ * the header. -+ */ -+ u8 lazy_loaded:1; /* This object has been lazy loaded and -+ * is missing some detail */ -+ -+ u8 defered_free:1; /* Object is removed from NAND, but is -+ * still in the inode cache. -+ * Free of object is defered. -+ * until the inode is released. -+ */ -+ u8 being_created:1; /* This object is still being created -+ * so skip some verification checks. */ -+ u8 is_shadowed:1; /* This object is shadowed on the way -+ * to being renamed. */ -+ -+ u8 xattr_known:1; /* We know if this has object has xattribs -+ * or not. */ -+ u8 has_xattr:1; /* This object has xattribs. -+ * Only valid if xattr_known. */ -+ -+ u8 serial; /* serial number of chunk in NAND.*/ -+ u16 sum; /* sum of the name to speed searching */ -+ -+ struct yaffs_dev *my_dev; /* The device I'm on */ -+ -+ struct list_head hash_link; /* list of objects in hash bucket */ -+ -+ struct list_head hard_links; /* hard linked object chain*/ -+ -+ /* directory structure stuff */ -+ /* also used for linking up the free list */ -+ struct yaffs_obj *parent; -+ struct list_head siblings; -+ -+ /* Where's my object header in NAND? */ -+ int hdr_chunk; -+ -+ int n_data_chunks; /* Number of data chunks for this file. */ -+ -+ u32 obj_id; /* the object id value */ -+ -+ u32 yst_mode; -+ -+ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; -+ -+#ifdef CONFIG_YAFFS_WINCE -+ u32 win_ctime[2]; -+ u32 win_mtime[2]; -+ u32 win_atime[2]; -+#else -+ u32 yst_uid; -+ u32 yst_gid; -+ u32 yst_atime; -+ u32 yst_mtime; -+ u32 yst_ctime; -+#endif -+ -+ u32 yst_rdev; -+ -+ void *my_inode; -+ -+ enum yaffs_obj_type variant_type; -+ -+ union yaffs_obj_var variant; -+ -+}; -+ -+struct yaffs_obj_bucket { -+ struct list_head list; -+ int count; -+}; -+ -+/* yaffs_checkpt_obj holds the definition of an object as dumped -+ * by checkpointing. -+ */ -+ -+struct yaffs_checkpt_obj { -+ int struct_type; -+ u32 obj_id; -+ u32 parent_id; -+ int hdr_chunk; -+ enum yaffs_obj_type variant_type:3; -+ u8 deleted:1; -+ u8 soft_del:1; -+ u8 unlinked:1; -+ u8 fake:1; -+ u8 rename_allowed:1; -+ u8 unlink_allowed:1; -+ u8 serial; -+ int n_data_chunks; -+ loff_t size_or_equiv_obj; -+}; -+ -+/*--------------------- Temporary buffers ---------------- -+ * -+ * These are chunk-sized working buffers. Each device has a few. -+ */ -+ -+struct yaffs_buffer { -+ u8 *buffer; -+ int in_use; -+}; -+ -+/*----------------- Device ---------------------------------*/ -+ -+struct yaffs_param { -+ const YCHAR *name; -+ -+ /* -+ * Entry parameters set up way early. Yaffs sets up the rest. -+ * The structure should be zeroed out before use so that unused -+ * and default values are zero. -+ */ -+ -+ int inband_tags; /* Use unband tags */ -+ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to -+ be a power of 2 */ -+ int chunks_per_block; /* does not need to be a power of 2 */ -+ int spare_bytes_per_chunk; /* spare area size */ -+ int start_block; /* Start block we're allowed to use */ -+ int end_block; /* End block we're allowed to use */ -+ int n_reserved_blocks; /* Tuneable so that we can reduce -+ * reserved blocks on NOR and RAM. */ -+ -+ int n_caches; /* If <= 0, then short op caching is disabled, -+ * else the number of short op caches. -+ */ -+ int cache_bypass_aligned; /* If non-zero then bypass the cache for -+ * aligned writes. -+ */ -+ -+ int use_nand_ecc; /* Flag to decide whether or not to use -+ * NAND driver ECC on data (yaffs1) */ -+ int tags_9bytes; /* Use 9 byte tags */ -+ int no_tags_ecc; /* Flag to decide whether or not to do ECC -+ * on packed tags (yaffs2) */ -+ -+ int is_yaffs2; /* Use yaffs2 mode on this device */ -+ -+ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ -+ -+ int refresh_period; /* How often to check for a block refresh */ -+ -+ /* Checkpoint control. Can be set before or after initialisation */ -+ u8 skip_checkpt_rd; -+ u8 skip_checkpt_wr; -+ -+ int enable_xattr; /* Enable xattribs */ -+ -+ int max_objects; /* -+ * Set to limit the number of objects created. -+ * 0 = no limit. -+ */ -+ -+ /* The remove_obj_fn function must be supplied by OS flavours that -+ * need it. -+ * yaffs direct uses it to implement the faster readdir. -+ * Linux uses it to protect the directory during unlocking. -+ */ -+ void (*remove_obj_fn) (struct yaffs_obj *obj); -+ -+ /* Callback to mark the superblock dirty */ -+ void (*sb_dirty_fn) (struct yaffs_dev *dev); -+ -+ /* Callback to control garbage collection. */ -+ unsigned (*gc_control_fn) (struct yaffs_dev *dev); -+ -+ /* Debug control flags. Don't use unless you know what you're doing */ -+ int use_header_file_size; /* Flag to determine if we should use -+ * file sizes from the header */ -+ int disable_lazy_load; /* Disable lazy loading on this device */ -+ int wide_tnodes_disabled; /* Set to disable wide tnodes */ -+ int disable_soft_del; /* yaffs 1 only: Set to disable the use of -+ * softdeletion. */ -+ -+ int defered_dir_update; /* Set to defer directory updates */ -+ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ int auto_unicode; -+#endif -+ int always_check_erased; /* Force chunk erased check always on */ -+ -+ int disable_summary; -+ int disable_bad_block_marking; -+ -+}; -+ -+struct yaffs_driver { -+ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, int data_len, -+ const u8 *oob, int oob_len); -+ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, -+ u8 *data, int data_len, -+ u8 *oob, int oob_len, -+ enum yaffs_ecc_result *ecc_result); -+ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_initialise_fn) (struct yaffs_dev *dev); -+ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); -+}; -+ -+struct yaffs_tags_handler { -+ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ const struct yaffs_ext_tags *tags); -+ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, -+ int nand_chunk, u8 *data, -+ struct yaffs_ext_tags *tags); -+ -+ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number); -+ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); -+}; -+ -+struct yaffs_dev { -+ struct yaffs_param param; -+ struct yaffs_driver drv; -+ struct yaffs_tags_handler tagger; -+ -+ /* Context storage. Holds extra OS specific data for this device */ -+ -+ void *os_context; -+ void *driver_context; -+ -+ struct list_head dev_list; -+ -+ int ll_init; -+ /* Runtime parameters. Set up by YAFFS. */ -+ int data_bytes_per_chunk; -+ -+ /* Non-wide tnode stuff */ -+ u16 chunk_grp_bits; /* Number of bits that need to be resolved if -+ * the tnodes are not wide enough. -+ */ -+ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ -+ -+ /* Stuff to support wide tnodes */ -+ u32 tnode_width; -+ u32 tnode_mask; -+ u32 tnode_size; -+ -+ /* Stuff for figuring out file offset to chunk conversions */ -+ u32 chunk_shift; /* Shift value */ -+ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ -+ u32 chunk_mask; /* Mask to use for power-of-2 case */ -+ -+ int is_mounted; -+ int read_only; -+ int is_checkpointed; -+ -+ /* Stuff to support block offsetting to support start block zero */ -+ int internal_start_block; -+ int internal_end_block; -+ int block_offset; -+ int chunk_offset; -+ -+ /* Runtime checkpointing stuff */ -+ int checkpt_page_seq; /* running sequence number of checkpt pages */ -+ int checkpt_byte_count; -+ int checkpt_byte_offs; -+ u8 *checkpt_buffer; -+ int checkpt_open_write; -+ int blocks_in_checkpt; -+ int checkpt_cur_chunk; -+ int checkpt_cur_block; -+ int checkpt_next_block; -+ int *checkpt_block_list; -+ int checkpt_max_blocks; -+ u32 checkpt_sum; -+ u32 checkpt_xor; -+ -+ int checkpoint_blocks_required; /* Number of blocks needed to store -+ * current checkpoint set */ -+ -+ /* Block Info */ -+ struct yaffs_block_info *block_info; -+ u8 *chunk_bits; /* bitmap of chunks in use */ -+ u8 block_info_alt:1; /* allocated using alternative alloc */ -+ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ -+ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. -+ * Must be consistent with chunks_per_block. -+ */ -+ -+ int n_erased_blocks; -+ int alloc_block; /* Current block being allocated off */ -+ u32 alloc_page; -+ int alloc_block_finder; /* Used to search for next allocation block */ -+ -+ /* Object and Tnode memory management */ -+ void *allocator; -+ int n_obj; -+ int n_tnodes; -+ -+ int n_hardlinks; -+ -+ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; -+ u32 bucket_finder; -+ -+ int n_free_chunks; -+ -+ /* Garbage collection control */ -+ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ -+ u32 n_clean_ups; -+ -+ unsigned has_pending_prioritised_gc; /* We think this device might -+ have pending prioritised gcs */ -+ unsigned gc_disable; -+ unsigned gc_block_finder; -+ unsigned gc_dirtiest; -+ unsigned gc_pages_in_use; -+ unsigned gc_not_done; -+ unsigned gc_block; -+ unsigned gc_chunk; -+ unsigned gc_skip; -+ struct yaffs_summary_tags *gc_sum_tags; -+ -+ /* Special directories */ -+ struct yaffs_obj *root_dir; -+ struct yaffs_obj *lost_n_found; -+ -+ int buffered_block; /* Which block is buffered here? */ -+ int doing_buffered_block_rewrite; -+ -+ struct yaffs_cache *cache; -+ int cache_last_use; -+ -+ /* Stuff for background deletion and unlinked files. */ -+ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted -+ files live. */ -+ struct yaffs_obj *del_dir; /* Directory where deleted objects are -+ sent to disappear. */ -+ struct yaffs_obj *unlinked_deletion; /* Current file being -+ background deleted. */ -+ int n_deleted_files; /* Count of files awaiting deletion; */ -+ int n_unlinked_files; /* Count of unlinked files. */ -+ int n_bg_deletions; /* Count of background deletions. */ -+ -+ /* Temporary buffer management */ -+ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; -+ int max_temp; -+ int temp_in_use; -+ int unmanaged_buffer_allocs; -+ int unmanaged_buffer_deallocs; -+ -+ /* yaffs2 runtime stuff */ -+ unsigned seq_number; /* Sequence number of currently -+ allocating block */ -+ unsigned oldest_dirty_seq; -+ unsigned oldest_dirty_block; -+ -+ /* Block refreshing */ -+ int refresh_skip; /* A skip down counter. -+ * Refresh happens when this gets to zero. */ -+ -+ /* Dirty directory handling */ -+ struct list_head dirty_dirs; /* List of dirty directories */ -+ -+ /* Summary */ -+ int chunks_per_summary; -+ struct yaffs_summary_tags *sum_tags; -+ -+ /* Statistics */ -+ u32 n_page_writes; -+ u32 n_page_reads; -+ u32 n_erasures; -+ u32 n_bad_queries; -+ u32 n_bad_markings; -+ u32 n_erase_failures; -+ u32 n_gc_copies; -+ u32 all_gcs; -+ u32 passive_gc_count; -+ u32 oldest_dirty_gc_count; -+ u32 n_gc_blocks; -+ u32 bg_gcs; -+ u32 n_retried_writes; -+ u32 n_retired_blocks; -+ u32 n_ecc_fixed; -+ u32 n_ecc_unfixed; -+ u32 n_tags_ecc_fixed; -+ u32 n_tags_ecc_unfixed; -+ u32 n_deletions; -+ u32 n_unmarked_deletions; -+ u32 refresh_count; -+ u32 cache_hits; -+ u32 tags_used; -+ u32 summary_used; -+ -+}; -+ -+/* The CheckpointDevice structure holds the device information that changes -+ *at runtime and must be preserved over unmount/mount cycles. -+ */ -+struct yaffs_checkpt_dev { -+ int struct_type; -+ int n_erased_blocks; -+ int alloc_block; /* Current block being allocated off */ -+ u32 alloc_page; -+ int n_free_chunks; -+ -+ int n_deleted_files; /* Count of files awaiting deletion; */ -+ int n_unlinked_files; /* Count of unlinked files. */ -+ int n_bg_deletions; /* Count of background deletions. */ -+ -+ /* yaffs2 runtime stuff */ -+ unsigned seq_number; /* Sequence number of currently -+ * allocating block */ -+ -+}; -+ -+struct yaffs_checkpt_validity { -+ int struct_type; -+ u32 magic; -+ u32 version; -+ u32 head; -+}; -+ -+struct yaffs_shadow_fixer { -+ int obj_id; -+ int shadowed_id; -+ struct yaffs_shadow_fixer *next; -+}; -+ -+/* Structure for doing xattr modifications */ -+struct yaffs_xattr_mod { -+ int set; /* If 0 then this is a deletion */ -+ const YCHAR *name; -+ const void *data; -+ int size; -+ int flags; -+ int result; -+}; -+ -+/*----------------------- YAFFS Functions -----------------------*/ -+ -+int yaffs_guts_initialise(struct yaffs_dev *dev); -+void yaffs_deinitialise(struct yaffs_dev *dev); -+ -+int yaffs_get_n_free_chunks(struct yaffs_dev *dev); -+ -+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, -+ struct yaffs_obj *new_dir, const YCHAR * new_name); -+ -+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); -+int yaffs_del_obj(struct yaffs_obj *obj); -+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, -+ enum yaffs_obj_type type); -+ -+ -+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); -+loff_t yaffs_get_obj_length(struct yaffs_obj *obj); -+int yaffs_get_obj_inode(struct yaffs_obj *obj); -+unsigned yaffs_get_obj_type(struct yaffs_obj *obj); -+int yaffs_get_obj_link_count(struct yaffs_obj *obj); -+ -+/* File operations */ -+int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, -+ int n_bytes); -+int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, -+ int n_bytes, int write_trhrough); -+int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); -+ -+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid); -+ -+int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync); -+ -+/* Flushing and checkpointing */ -+void yaffs_flush_whole_cache(struct yaffs_dev *dev); -+ -+int yaffs_checkpoint_save(struct yaffs_dev *dev); -+int yaffs_checkpoint_restore(struct yaffs_dev *dev); -+ -+/* Directory operations */ -+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, -+ u32 mode, u32 uid, u32 gid); -+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, -+ const YCHAR *name); -+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); -+ -+/* Link operations */ -+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, -+ struct yaffs_obj *equiv_obj); -+ -+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); -+ -+/* Symlink operations */ -+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, const YCHAR *alias); -+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); -+ -+/* Special inodes (fifos, sockets and devices) */ -+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, u32 rdev); -+ -+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, -+ const void *value, int size, int flags); -+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, -+ int size); -+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); -+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); -+ -+/* Special directories */ -+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); -+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); -+ -+void yaffs_handle_defered_free(struct yaffs_obj *obj); -+ -+void yaffs_update_dirty_dirs(struct yaffs_dev *dev); -+ -+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); -+ -+/* Debug dump */ -+int yaffs_dump_obj(struct yaffs_obj *obj); -+ -+void yaffs_guts_test(struct yaffs_dev *dev); -+int yaffs_guts_ll_init(struct yaffs_dev *dev); -+ -+ -+/* A few useful functions to be used within the core files*/ -+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, -+ int lyn); -+int yaffs_check_ff(u8 *buffer, int n_bytes); -+void yaffs_handle_chunk_error(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi); -+ -+u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); -+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); -+ -+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, -+ int number, -+ enum yaffs_obj_type type); -+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ int nand_chunk, int in_scan); -+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); -+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, -+ const struct yaffs_obj_hdr *oh); -+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); -+YCHAR *yaffs_clone_str(const YCHAR *str); -+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); -+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); -+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, -+ int force, int is_shrink, int shadows, -+ struct yaffs_xattr_mod *xop); -+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, -+ int backward_scanning); -+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); -+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); -+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id, -+ struct yaffs_tnode *passed_tn); -+ -+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_trhrough); -+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); -+void yaffs_skip_rest_of_block(struct yaffs_dev *dev); -+ -+int yaffs_count_free_chunks(struct yaffs_dev *dev); -+ -+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id); -+ -+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos); -+ -+int yaffs_is_non_empty_dir(struct yaffs_obj *obj); -+ -+int yaffs_guts_format_dev(struct yaffs_dev *dev); -+ -+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, -+ int *chunk_out, u32 *offset_out); -+/* -+ * Marshalling functions to get loff_t file sizes into aand out of -+ * object headers. -+ */ -+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); -+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); -+loff_t yaffs_max_file_size(struct yaffs_dev *dev); -+ -+/* -+ * Debug function to count number of blocks in each state -+ * NB Needs to be called with correct number of integers -+ */ -+ -+void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); -+ -+int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_linux.h linux-3.10.40/fs/yaffs2/yaffs_linux.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_linux.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,48 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_LINUX_H__ -+#define __YAFFS_LINUX_H__ -+ -+#include "yportenv.h" -+ -+struct yaffs_linux_context { -+ struct list_head context_list; /* List of these we have mounted */ -+ struct yaffs_dev *dev; -+ struct super_block *super; -+ struct task_struct *bg_thread; /* Background thread for this device */ -+ int bg_running; -+ struct mutex gross_lock; /* Gross locking mutex*/ -+ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size -+ * at compile time so we have to allocate it. -+ */ -+ struct list_head search_contexts; -+ struct task_struct *readdir_process; -+ unsigned mount_id; -+ int dirty; -+}; -+ -+#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) -+#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+#define WRITE_SIZE_STR "writesize" -+#define WRITE_SIZE(mtd) ((mtd)->writesize) -+#else -+#define WRITE_SIZE_STR "oobblock" -+#define WRITE_SIZE(mtd) ((mtd)->oobblock) -+#endif -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_mtdif.c linux-3.10.40/fs/yaffs2/yaffs_mtdif.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_mtdif.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,309 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yportenv.h" -+ -+#include "yaffs_mtdif.h" -+ -+#include "linux/mtd/mtd.h" -+#include "linux/types.h" -+#include "linux/time.h" -+#include "linux/major.h" -+#include "linux/mtd/nand.h" -+#include "linux/kernel.h" -+#include "linux/version.h" -+#include "linux/types.h" -+ -+#include "yaffs_trace.h" -+#include "yaffs_guts.h" -+#include "yaffs_linux.h" -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) -+#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) -+#define mtd_erase(m, ei) (m)->erase(m, ei) -+#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) -+#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) -+#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) -+#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) -+#endif -+ -+ -+ -+int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ u32 addr = -+ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * -+ dev->param.chunks_per_block; -+ struct erase_info ei; -+ int retval = 0; -+ -+ ei.mtd = mtd; -+ ei.addr = addr; -+ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; -+ ei.time = 1000; -+ ei.retries = 2; -+ ei.callback = NULL; -+ ei.priv = (u_long) dev; -+ -+ retval = mtd_erase(mtd, &ei); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+ -+static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, int data_len, -+ const u8 *oob, int oob_len) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ loff_t addr; -+ struct mtd_oob_ops ops; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", -+ dev, nand_chunk, data, data_len, oob, oob_len); -+ -+ if (!data || !data_len) { -+ data = NULL; -+ data_len = 0; -+ } -+ -+ if (!oob || !oob_len) { -+ oob = NULL; -+ oob_len = 0; -+ } -+ -+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; -+ memset(&ops, 0, sizeof(ops)); -+ ops.mode = MTD_OPS_AUTO_OOB; -+ ops.len = (data) ? data_len : 0; -+ ops.ooblen = oob_len; -+ ops.datbuf = (u8 *)data; -+ ops.oobbuf = (u8 *)oob; -+ -+ retval = mtd_write_oob(mtd, addr, &ops); -+ if (retval) { -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "write_oob failed, chunk %d, mtd error %d", -+ nand_chunk, retval); -+ } -+ return retval ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, -+ u8 *data, int data_len, -+ u8 *oob, int oob_len, -+ enum yaffs_ecc_result *ecc_result) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ loff_t addr; -+ struct mtd_oob_ops ops; -+ int retval; -+ -+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; -+ memset(&ops, 0, sizeof(ops)); -+ ops.mode = MTD_OPS_AUTO_OOB; -+ ops.len = (data) ? data_len : 0; -+ ops.ooblen = oob_len; -+ ops.datbuf = data; -+ ops.oobbuf = oob; -+ -+#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) -+ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; -+ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. -+ */ -+ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; -+#endif -+ /* Read page and oob using MTD. -+ * Check status and determine ECC result. -+ */ -+ retval = mtd_read_oob(mtd, addr, &ops); -+ if (retval) -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "read_oob failed, chunk %d, mtd error %d", -+ nand_chunk, retval); -+ -+ switch (retval) { -+ case 0: -+ /* no error */ -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ break; -+ -+ case -EUCLEAN: -+ /* MTD's ECC fixed the data */ -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_FIXED; -+ dev->n_ecc_fixed++; -+ break; -+ -+ case -EBADMSG: -+ default: -+ /* MTD's ECC could not fix the data */ -+ dev->n_ecc_unfixed++; -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ return YAFFS_FAIL; -+ } -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ -+ loff_t addr; -+ struct erase_info ei; -+ int retval = 0; -+ u32 block_size; -+ -+ block_size = dev->param.total_bytes_per_chunk * -+ dev->param.chunks_per_block; -+ addr = ((loff_t) block_no) * block_size; -+ -+ ei.mtd = mtd; -+ ei.addr = addr; -+ ei.len = block_size; -+ ei.time = 1000; -+ ei.retries = 2; -+ ei.callback = NULL; -+ ei.priv = (u_long) dev; -+ -+ retval = mtd_erase(mtd, &ei); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); -+ -+ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); -+ return (retval) ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); -+ -+ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); -+ return (retval) ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_initialise(struct yaffs_dev *dev) -+{ -+ return YAFFS_OK; -+} -+ -+static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) -+{ -+ return YAFFS_OK; -+} -+ -+ -+void yaffs_mtd_drv_install(struct yaffs_dev *dev) -+{ -+ struct yaffs_driver *drv = &dev->drv; -+ -+ drv->drv_write_chunk_fn = yaffs_mtd_write; -+ drv->drv_read_chunk_fn = yaffs_mtd_read; -+ drv->drv_erase_fn = yaffs_mtd_erase; -+ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; -+ drv->drv_check_bad_fn = yaffs_mtd_check_bad; -+ drv->drv_initialise_fn = yaffs_mtd_initialise; -+ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; -+} -+ -+ -+struct mtd_info * yaffs_get_mtd_device(dev_t sdev) -+{ -+ struct mtd_info *mtd; -+ -+ mtd = yaffs_get_mtd_device(sdev); -+ -+ /* Check it's an mtd device..... */ -+ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) -+ return NULL; /* This isn't an mtd device */ -+ -+ /* Check it's NAND */ -+ if (mtd->type != MTD_NANDFLASH) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: MTD device is not NAND it's type %d", -+ mtd->type); -+ return NULL; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); -+ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); -+ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -+ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); -+#else -+ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); -+#endif -+ -+ return mtd; -+} -+ -+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) -+{ -+ if (yaffs_version == 2) { -+ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || -+ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && -+ !inband_tags) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device does not have the right page sizes" -+ ); -+ return -1; -+ } -+ } else { -+ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || -+ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device does not support have the right page sizes" -+ ); -+ return -1; -+ } -+ } -+ -+ return 0; -+} -+ -+ -+void yaffs_put_mtd_device(struct mtd_info *mtd) -+{ -+ if(mtd) -+ put_mtd_device(mtd); -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_mtdif.h linux-3.10.40/fs/yaffs2/yaffs_mtdif.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_mtdif.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,25 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_MTDIF_H__ -+#define __YAFFS_MTDIF_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_mtd_drv_install(struct yaffs_dev *dev); -+struct mtd_info * yaffs_get_mtd_device(dev_t sdev); -+void yaffs_put_mtd_device(struct mtd_info *mtd); -+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_nameval.c linux-3.10.40/fs/yaffs2/yaffs_nameval.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_nameval.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,208 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * This simple implementation of a name-value store assumes a small number of -+* values and fits into a small finite buffer. -+ * -+ * Each attribute is stored as a record: -+ * sizeof(int) bytes record size. -+ * strnlen+1 bytes name null terminated. -+ * nbytes value. -+ * ---------- -+ * total size stored in record size -+ * -+ * This code has not been tested with unicode yet. -+ */ -+ -+#include "yaffs_nameval.h" -+ -+#include "yportenv.h" -+ -+static int nval_find(const char *xb, int xb_size, const YCHAR *name, -+ int *exist_size) -+{ -+ int pos = 0; -+ int size; -+ -+ memcpy(&size, xb, sizeof(int)); -+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { -+ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), -+ name, size)) { -+ if (exist_size) -+ *exist_size = size; -+ return pos; -+ } -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ if (exist_size) -+ *exist_size = 0; -+ return -ENODATA; -+} -+ -+static int nval_used(const char *xb, int xb_size) -+{ -+ int pos = 0; -+ int size; -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ return pos; -+} -+ -+int nval_del(char *xb, int xb_size, const YCHAR *name) -+{ -+ int pos = nval_find(xb, xb_size, name, NULL); -+ int size; -+ -+ if (pos < 0 || pos >= xb_size) -+ return -ENODATA; -+ -+ /* Find size, shift rest over this record, -+ * then zero out the rest of buffer */ -+ memcpy(&size, xb + pos, sizeof(int)); -+ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); -+ memset(xb + (xb_size - size), 0, size); -+ return 0; -+} -+ -+int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, -+ int bsize, int flags) -+{ -+ int pos; -+ int namelen = strnlen(name, xb_size); -+ int reclen; -+ int size_exist = 0; -+ int space; -+ int start; -+ -+ pos = nval_find(xb, xb_size, name, &size_exist); -+ -+ if (flags & XATTR_CREATE && pos >= 0) -+ return -EEXIST; -+ if (flags & XATTR_REPLACE && pos < 0) -+ return -ENODATA; -+ -+ start = nval_used(xb, xb_size); -+ space = xb_size - start + size_exist; -+ -+ reclen = (sizeof(int) + namelen + 1 + bsize); -+ -+ if (reclen > space) -+ return -ENOSPC; -+ -+ if (pos >= 0) { -+ nval_del(xb, xb_size, name); -+ start = nval_used(xb, xb_size); -+ } -+ -+ pos = start; -+ -+ memcpy(xb + pos, &reclen, sizeof(int)); -+ pos += sizeof(int); -+ strncpy((YCHAR *) (xb + pos), name, reclen); -+ pos += (namelen + 1); -+ memcpy(xb + pos, buf, bsize); -+ return 0; -+} -+ -+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, -+ int bsize) -+{ -+ int pos = nval_find(xb, xb_size, name, NULL); -+ int size; -+ -+ if (pos >= 0 && pos < xb_size) { -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ pos += sizeof(int); /* advance past record length */ -+ size -= sizeof(int); -+ -+ /* Advance over name string */ -+ while (xb[pos] && size > 0 && pos < xb_size) { -+ pos++; -+ size--; -+ } -+ /*Advance over NUL */ -+ pos++; -+ size--; -+ -+ /* If bsize is zero then this is a size query. -+ * Return the size, but don't copy. -+ */ -+ if (!bsize) -+ return size; -+ -+ if (size <= bsize) { -+ memcpy(buf, xb + pos, size); -+ return size; -+ } -+ } -+ if (pos >= 0) -+ return -ERANGE; -+ -+ return -ENODATA; -+} -+ -+int nval_list(const char *xb, int xb_size, char *buf, int bsize) -+{ -+ int pos = 0; -+ int size; -+ int name_len; -+ int ncopied = 0; -+ int filled = 0; -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ while (size > sizeof(int) && -+ size <= xb_size && -+ (pos + size) < xb_size && -+ !filled) { -+ pos += sizeof(int); -+ size -= sizeof(int); -+ name_len = strnlen((YCHAR *) (xb + pos), size); -+ if (ncopied + name_len + 1 < bsize) { -+ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); -+ buf += name_len; -+ *buf = '\0'; -+ buf++; -+ if (sizeof(YCHAR) > 1) { -+ *buf = '\0'; -+ buf++; -+ } -+ ncopied += (name_len + 1); -+ } else { -+ filled = 1; -+ } -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ return ncopied; -+} -+ -+int nval_hasvalues(const char *xb, int xb_size) -+{ -+ return nval_used(xb, xb_size) > 0; -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_nameval.h linux-3.10.40/fs/yaffs2/yaffs_nameval.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_nameval.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,28 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __NAMEVAL_H__ -+#define __NAMEVAL_H__ -+ -+#include "yportenv.h" -+ -+int nval_del(char *xb, int xb_size, const YCHAR * name); -+int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, -+ int bsize, int flags); -+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, -+ int bsize); -+int nval_list(const char *xb, int xb_size, char *buf, int bsize); -+int nval_hasvalues(const char *xb, int xb_size); -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_nand.c linux-3.10.40/fs/yaffs2/yaffs_nand.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_nand.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,122 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_nand.h" -+#include "yaffs_tagscompat.h" -+ -+#include "yaffs_getblockinfo.h" -+#include "yaffs_summary.h" -+ -+static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) -+{ -+ return chunk - dev->chunk_offset; -+} -+ -+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, -+ u8 *buffer, struct yaffs_ext_tags *tags) -+{ -+ int result; -+ struct yaffs_ext_tags local_tags; -+ int flash_chunk = apply_chunk_offset(dev, nand_chunk); -+ -+ dev->n_page_reads++; -+ -+ /* If there are no tags provided use local tags. */ -+ if (!tags) -+ tags = &local_tags; -+ -+ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); -+ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { -+ -+ struct yaffs_block_info *bi; -+ bi = yaffs_get_block_info(dev, -+ nand_chunk / -+ dev->param.chunks_per_block); -+ yaffs_handle_chunk_error(dev, bi); -+ } -+ return result; -+} -+ -+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *buffer, struct yaffs_ext_tags *tags) -+{ -+ int result; -+ int flash_chunk = apply_chunk_offset(dev, nand_chunk); -+ -+ dev->n_page_writes++; -+ -+ if (!tags) { -+ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ tags->seq_number = dev->seq_number; -+ tags->chunk_used = 1; -+ yaffs_trace(YAFFS_TRACE_WRITE, -+ "Writing chunk %d tags %d %d", -+ nand_chunk, tags->obj_id, tags->chunk_id); -+ -+ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, -+ buffer, tags); -+ -+ yaffs_summary_add(dev, tags, nand_chunk); -+ -+ return result; -+} -+ -+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ block_no -= dev->block_offset; -+ dev->n_bad_markings++; -+ -+ if (dev->param.disable_bad_block_marking) -+ return YAFFS_OK; -+ -+ return dev->tagger.mark_bad_fn(dev, block_no); -+} -+ -+ -+int yaffs_query_init_block_state(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ block_no -= dev->block_offset; -+ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); -+} -+ -+int yaffs_erase_block(struct yaffs_dev *dev, int block_no) -+{ -+ int result; -+ -+ block_no -= dev->block_offset; -+ dev->n_erasures++; -+ result = dev->drv.drv_erase_fn(dev, block_no); -+ return result; -+} -+ -+int yaffs_init_nand(struct yaffs_dev *dev) -+{ -+ if (dev->drv.drv_initialise_fn) -+ return dev->drv.drv_initialise_fn(dev); -+ return YAFFS_OK; -+} -+ -+int yaffs_deinit_nand(struct yaffs_dev *dev) -+{ -+ if (dev->drv.drv_deinitialise_fn) -+ return dev->drv.drv_deinitialise_fn(dev); -+ return YAFFS_OK; -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_nand.h linux-3.10.40/fs/yaffs2/yaffs_nand.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_nand.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_NAND_H__ -+#define __YAFFS_NAND_H__ -+#include "yaffs_guts.h" -+ -+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, -+ u8 *buffer, struct yaffs_ext_tags *tags); -+ -+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *buffer, struct yaffs_ext_tags *tags); -+ -+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); -+ -+int yaffs_query_init_block_state(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ unsigned *seq_number); -+ -+int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); -+ -+int yaffs_init_nand(struct yaffs_dev *dev); -+int yaffs_deinit_nand(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.10.40/fs/yaffs2/yaffs_packedtags1.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,56 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_packedtags1.h" -+#include "yportenv.h" -+ -+static const u8 all_ff[20] = { -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff -+}; -+ -+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, -+ const struct yaffs_ext_tags *t) -+{ -+ pt->chunk_id = t->chunk_id; -+ pt->serial_number = t->serial_number; -+ pt->n_bytes = t->n_bytes; -+ pt->obj_id = t->obj_id; -+ pt->ecc = 0; -+ pt->deleted = (t->is_deleted) ? 0 : 1; -+ pt->unused_stuff = 0; -+ pt->should_be_ff = 0xffffffff; -+} -+ -+void yaffs_unpack_tags1(struct yaffs_ext_tags *t, -+ const struct yaffs_packed_tags1 *pt) -+{ -+ -+ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { -+ t->block_bad = 0; -+ if (pt->should_be_ff != 0xffffffff) -+ t->block_bad = 1; -+ t->chunk_used = 1; -+ t->obj_id = pt->obj_id; -+ t->chunk_id = pt->chunk_id; -+ t->n_bytes = pt->n_bytes; -+ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ t->is_deleted = (pt->deleted) ? 0 : 1; -+ t->serial_number = pt->serial_number; -+ } else { -+ memset(t, 0, sizeof(struct yaffs_ext_tags)); -+ } -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.10.40/fs/yaffs2/yaffs_packedtags1.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ -+ -+#ifndef __YAFFS_PACKEDTAGS1_H__ -+#define __YAFFS_PACKEDTAGS1_H__ -+ -+#include "yaffs_guts.h" -+ -+struct yaffs_packed_tags1 { -+ u32 chunk_id:20; -+ u32 serial_number:2; -+ u32 n_bytes:10; -+ u32 obj_id:18; -+ u32 ecc:12; -+ u32 deleted:1; -+ u32 unused_stuff:1; -+ unsigned should_be_ff; -+ -+}; -+ -+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, -+ const struct yaffs_ext_tags *t); -+void yaffs_unpack_tags1(struct yaffs_ext_tags *t, -+ const struct yaffs_packed_tags1 *pt); -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.10.40/fs/yaffs2/yaffs_packedtags2.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,197 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_packedtags2.h" -+#include "yportenv.h" -+#include "yaffs_trace.h" -+ -+/* This code packs a set of extended tags into a binary structure for -+ * NAND storage -+ */ -+ -+/* Some of the information is "extra" struff which can be packed in to -+ * speed scanning -+ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. -+ */ -+ -+/* Extra flags applied to chunk_id */ -+ -+#define EXTRA_HEADER_INFO_FLAG 0x80000000 -+#define EXTRA_SHRINK_FLAG 0x40000000 -+#define EXTRA_SHADOWS_FLAG 0x20000000 -+#define EXTRA_SPARE_FLAGS 0x10000000 -+ -+#define ALL_EXTRA_FLAGS 0xf0000000 -+ -+/* Also, the top 4 bits of the object Id are set to the object type. */ -+#define EXTRA_OBJECT_TYPE_SHIFT (28) -+#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) -+ -+static void yaffs_dump_packed_tags2_tags_only( -+ const struct yaffs_packed_tags2_tags_only *ptt) -+{ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "packed tags obj %d chunk %d byte %d seq %d", -+ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); -+} -+ -+static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) -+{ -+ yaffs_dump_packed_tags2_tags_only(&pt->t); -+} -+ -+static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) -+{ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", -+ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, -+ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, -+ t->seq_number); -+ -+} -+ -+static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) -+{ -+ if (t->chunk_id != 0 || !t->extra_available) -+ return 0; -+ -+ /* Check if the file size is too long to store */ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && -+ (t->extra_file_size >> 31) != 0) -+ return 0; -+ return 1; -+} -+ -+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, -+ const struct yaffs_ext_tags *t) -+{ -+ ptt->chunk_id = t->chunk_id; -+ ptt->seq_number = t->seq_number; -+ ptt->n_bytes = t->n_bytes; -+ ptt->obj_id = t->obj_id; -+ -+ /* Only store extra tags for object headers. -+ * If it is a file then only store if the file size is short\ -+ * enough to fit. -+ */ -+ if (yaffs_check_tags_extra_packable(t)) { -+ /* Store the extra header info instead */ -+ /* We save the parent object in the chunk_id */ -+ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; -+ if (t->extra_is_shrink) -+ ptt->chunk_id |= EXTRA_SHRINK_FLAG; -+ if (t->extra_shadows) -+ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; -+ -+ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; -+ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); -+ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ ptt->n_bytes = t->extra_equiv_id; -+ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) -+ ptt->n_bytes = (unsigned) t->extra_file_size; -+ else -+ ptt->n_bytes = 0; -+ } -+ -+ yaffs_dump_packed_tags2_tags_only(ptt); -+ yaffs_dump_tags2(t); -+} -+ -+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, -+ const struct yaffs_ext_tags *t, int tags_ecc) -+{ -+ yaffs_pack_tags2_tags_only(&pt->t, t); -+ -+ if (tags_ecc) -+ yaffs_ecc_calc_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &pt->ecc); -+} -+ -+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, -+ struct yaffs_packed_tags2_tags_only *ptt) -+{ -+ memset(t, 0, sizeof(struct yaffs_ext_tags)); -+ -+ if (ptt->seq_number == 0xffffffff) -+ return; -+ -+ t->block_bad = 0; -+ t->chunk_used = 1; -+ t->obj_id = ptt->obj_id; -+ t->chunk_id = ptt->chunk_id; -+ t->n_bytes = ptt->n_bytes; -+ t->is_deleted = 0; -+ t->serial_number = 0; -+ t->seq_number = ptt->seq_number; -+ -+ /* Do extra header info stuff */ -+ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { -+ t->chunk_id = 0; -+ t->n_bytes = 0; -+ -+ t->extra_available = 1; -+ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); -+ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; -+ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; -+ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; -+ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; -+ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ t->extra_equiv_id = ptt->n_bytes; -+ else -+ t->extra_file_size = ptt->n_bytes; -+ } -+ yaffs_dump_packed_tags2_tags_only(ptt); -+ yaffs_dump_tags2(t); -+} -+ -+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, -+ int tags_ecc) -+{ -+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ -+ if (pt->t.seq_number != 0xffffffff && tags_ecc) { -+ /* Chunk is in use and we need to do ECC */ -+ -+ struct yaffs_ecc_other ecc; -+ int result; -+ yaffs_ecc_calc_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &ecc); -+ result = -+ yaffs_ecc_correct_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &pt->ecc, &ecc); -+ switch (result) { -+ case 0: -+ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ break; -+ case 1: -+ ecc_result = YAFFS_ECC_RESULT_FIXED; -+ break; -+ case -1: -+ ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ break; -+ default: -+ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; -+ } -+ } -+ yaffs_unpack_tags2_tags_only(t, &pt->t); -+ -+ t->ecc_result = ecc_result; -+ -+ yaffs_dump_packed_tags2(pt); -+ yaffs_dump_tags2(t); -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.10.40/fs/yaffs2/yaffs_packedtags2.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,47 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ -+ -+#ifndef __YAFFS_PACKEDTAGS2_H__ -+#define __YAFFS_PACKEDTAGS2_H__ -+ -+#include "yaffs_guts.h" -+#include "yaffs_ecc.h" -+ -+struct yaffs_packed_tags2_tags_only { -+ unsigned seq_number; -+ unsigned obj_id; -+ unsigned chunk_id; -+ unsigned n_bytes; -+}; -+ -+struct yaffs_packed_tags2 { -+ struct yaffs_packed_tags2_tags_only t; -+ struct yaffs_ecc_other ecc; -+}; -+ -+/* Full packed tags with ECC, used for oob tags */ -+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, -+ const struct yaffs_ext_tags *t, int tags_ecc); -+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, -+ int tags_ecc); -+ -+/* Only the tags part (no ECC for use with inband tags */ -+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, -+ const struct yaffs_ext_tags *t); -+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, -+ struct yaffs_packed_tags2_tags_only *pt); -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_summary.c linux-3.10.40/fs/yaffs2/yaffs_summary.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_summary.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,312 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Summaries write the useful part of the tags for the chunks in a block into an -+ * an array which is written to the last n chunks of the block. -+ * Reading the summaries gives all the tags for the block in one read. Much -+ * faster. -+ * -+ * Chunks holding summaries are marked with tags making it look like -+ * they are part of a fake file. -+ * -+ * The summary could also be used during gc. -+ * -+ */ -+ -+#include "yaffs_summary.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_nand.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_bitmap.h" -+ -+/* -+ * The summary is built up in an array of summary tags. -+ * This gets written to the last one or two (maybe more) chunks in a block. -+ * A summary header is written as the first part of each chunk of summary data. -+ * The summary header must match or the summary is rejected. -+ */ -+ -+/* Summary tags don't need the sequence number because that is redundant. */ -+struct yaffs_summary_tags { -+ unsigned obj_id; -+ unsigned chunk_id; -+ unsigned n_bytes; -+}; -+ -+/* Summary header */ -+struct yaffs_summary_header { -+ unsigned version; /* Must match current version */ -+ unsigned block; /* Must be this block */ -+ unsigned seq; /* Must be this sequence number */ -+ unsigned sum; /* Just add up all the bytes in the tags */ -+}; -+ -+ -+static void yaffs_summary_clear(struct yaffs_dev *dev) -+{ -+ if (!dev->sum_tags) -+ return; -+ memset(dev->sum_tags, 0, dev->chunks_per_summary * -+ sizeof(struct yaffs_summary_tags)); -+} -+ -+ -+void yaffs_summary_deinit(struct yaffs_dev *dev) -+{ -+ kfree(dev->sum_tags); -+ dev->sum_tags = NULL; -+ kfree(dev->gc_sum_tags); -+ dev->gc_sum_tags = NULL; -+ dev->chunks_per_summary = 0; -+} -+ -+int yaffs_summary_init(struct yaffs_dev *dev) -+{ -+ int sum_bytes; -+ int chunks_used; /* Number of chunks used by summary */ -+ int sum_tags_bytes; -+ -+ sum_bytes = dev->param.chunks_per_block * -+ sizeof(struct yaffs_summary_tags); -+ -+ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ -+ (dev->data_bytes_per_chunk - -+ sizeof(struct yaffs_summary_header)); -+ -+ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; -+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); -+ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); -+ if (!dev->sum_tags || !dev->gc_sum_tags) { -+ yaffs_summary_deinit(dev); -+ return YAFFS_FAIL; -+ } -+ -+ yaffs_summary_clear(dev); -+ -+ return YAFFS_OK; -+} -+ -+static unsigned yaffs_summary_sum(struct yaffs_dev *dev) -+{ -+ u8 *sum_buffer = (u8 *)dev->sum_tags; -+ int i; -+ unsigned sum = 0; -+ -+ i = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ while (i > 0) { -+ sum += *sum_buffer; -+ sum_buffer++; -+ i--; -+ } -+ -+ return sum; -+} -+ -+static int yaffs_summary_write(struct yaffs_dev *dev, int blk) -+{ -+ struct yaffs_ext_tags tags; -+ u8 *buffer; -+ u8 *sum_buffer = (u8 *)dev->sum_tags; -+ int n_bytes; -+ int chunk_in_nand; -+ int chunk_in_block; -+ int result; -+ int this_tx; -+ struct yaffs_summary_header hdr; -+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ -+ buffer = yaffs_get_temp_buffer(dev); -+ n_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); -+ tags.obj_id = YAFFS_OBJECTID_SUMMARY; -+ tags.chunk_id = 1; -+ chunk_in_block = dev->chunks_per_summary; -+ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + -+ dev->chunks_per_summary; -+ hdr.version = YAFFS_SUMMARY_VERSION; -+ hdr.block = blk; -+ hdr.seq = bi->seq_number; -+ hdr.sum = yaffs_summary_sum(dev); -+ -+ do { -+ this_tx = n_bytes; -+ if (this_tx > sum_bytes_per_chunk) -+ this_tx = sum_bytes_per_chunk; -+ memcpy(buffer, &hdr, sizeof(hdr)); -+ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); -+ tags.n_bytes = this_tx + sizeof(hdr); -+ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, -+ buffer, &tags); -+ -+ if (result != YAFFS_OK) -+ break; -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ dev->n_free_chunks--; -+ -+ n_bytes -= this_tx; -+ sum_buffer += this_tx; -+ chunk_in_nand++; -+ chunk_in_block++; -+ tags.chunk_id++; -+ } while (result == YAFFS_OK && n_bytes > 0); -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ -+ if (result == YAFFS_OK) -+ bi->has_summary = 1; -+ -+ -+ return result; -+} -+ -+int yaffs_summary_read(struct yaffs_dev *dev, -+ struct yaffs_summary_tags *st, -+ int blk) -+{ -+ struct yaffs_ext_tags tags; -+ u8 *buffer; -+ u8 *sum_buffer = (u8 *)st; -+ int n_bytes; -+ int chunk_id; -+ int chunk_in_nand; -+ int chunk_in_block; -+ int result; -+ int this_tx; -+ struct yaffs_summary_header hdr; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); -+ int sum_tags_bytes; -+ -+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ buffer = yaffs_get_temp_buffer(dev); -+ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; -+ chunk_in_block = dev->chunks_per_summary; -+ chunk_in_nand = blk * dev->param.chunks_per_block + -+ dev->chunks_per_summary; -+ chunk_id = 1; -+ do { -+ this_tx = n_bytes; -+ if (this_tx > sum_bytes_per_chunk) -+ this_tx = sum_bytes_per_chunk; -+ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, -+ buffer, &tags); -+ -+ if (tags.chunk_id != chunk_id || -+ tags.obj_id != YAFFS_OBJECTID_SUMMARY || -+ tags.chunk_used == 0 || -+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || -+ tags.n_bytes != (this_tx + sizeof(hdr))) -+ result = YAFFS_FAIL; -+ if (result != YAFFS_OK) -+ break; -+ -+ if (st == dev->sum_tags) { -+ /* If we're scanning then update the block info */ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ } -+ memcpy(&hdr, buffer, sizeof(hdr)); -+ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); -+ n_bytes -= this_tx; -+ sum_buffer += this_tx; -+ chunk_in_nand++; -+ chunk_in_block++; -+ chunk_id++; -+ } while (result == YAFFS_OK && n_bytes > 0); -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ if (result == YAFFS_OK) { -+ /* Verify header */ -+ if (hdr.version != YAFFS_SUMMARY_VERSION || -+ hdr.seq != bi->seq_number || -+ hdr.sum != yaffs_summary_sum(dev)) -+ result = YAFFS_FAIL; -+ } -+ -+ if (st == dev->sum_tags && result == YAFFS_OK) -+ bi->has_summary = 1; -+ -+ return result; -+} -+ -+int yaffs_summary_add(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_nand) -+{ -+ struct yaffs_packed_tags2_tags_only tags_only; -+ struct yaffs_summary_tags *sum_tags; -+ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; -+ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; -+ -+ if (!dev->sum_tags) -+ return YAFFS_OK; -+ -+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { -+ yaffs_pack_tags2_tags_only(&tags_only, tags); -+ sum_tags = &dev->sum_tags[chunk_in_block]; -+ sum_tags->chunk_id = tags_only.chunk_id; -+ sum_tags->n_bytes = tags_only.n_bytes; -+ sum_tags->obj_id = tags_only.obj_id; -+ -+ if (chunk_in_block == dev->chunks_per_summary - 1) { -+ /* Time to write out the summary */ -+ yaffs_summary_write(dev, block_in_nand); -+ yaffs_summary_clear(dev); -+ yaffs_skip_rest_of_block(dev); -+ } -+ } -+ return YAFFS_OK; -+} -+ -+int yaffs_summary_fetch(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block) -+{ -+ struct yaffs_packed_tags2_tags_only tags_only; -+ struct yaffs_summary_tags *sum_tags; -+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { -+ sum_tags = &dev->sum_tags[chunk_in_block]; -+ tags_only.chunk_id = sum_tags->chunk_id; -+ tags_only.n_bytes = sum_tags->n_bytes; -+ tags_only.obj_id = sum_tags->obj_id; -+ yaffs_unpack_tags2_tags_only(tags, &tags_only); -+ return YAFFS_OK; -+ } -+ return YAFFS_FAIL; -+} -+ -+void yaffs_summary_gc(struct yaffs_dev *dev, int blk) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ int i; -+ -+ if (!bi->has_summary) -+ return; -+ -+ for (i = dev->chunks_per_summary; -+ i < dev->param.chunks_per_block; -+ i++) { -+ if (yaffs_check_chunk_bit(dev, blk, i)) { -+ yaffs_clear_chunk_bit(dev, blk, i); -+ bi->pages_in_use--; -+ dev->n_free_chunks++; -+ } -+ } -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_summary.h linux-3.10.40/fs/yaffs2/yaffs_summary.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_summary.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,37 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_SUMMARY_H__ -+#define __YAFFS_SUMMARY_H__ -+ -+#include "yaffs_packedtags2.h" -+ -+ -+int yaffs_summary_init(struct yaffs_dev *dev); -+void yaffs_summary_deinit(struct yaffs_dev *dev); -+ -+int yaffs_summary_add(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block); -+int yaffs_summary_fetch(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block); -+int yaffs_summary_read(struct yaffs_dev *dev, -+ struct yaffs_summary_tags *st, -+ int blk); -+void yaffs_summary_gc(struct yaffs_dev *dev, int blk); -+ -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.10.40/fs/yaffs2/yaffs_tagscompat.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,381 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_tagscompat.h" -+#include "yaffs_ecc.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_trace.h" -+ -+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); -+ -+ -+/********** Tags ECC calculations *********/ -+ -+ -+void yaffs_calc_tags_ecc(struct yaffs_tags *tags) -+{ -+ /* Calculate an ecc */ -+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; -+ unsigned i, j; -+ unsigned ecc = 0; -+ unsigned bit = 0; -+ -+ tags->ecc = 0; -+ -+ for (i = 0; i < 8; i++) { -+ for (j = 1; j & 0xff; j <<= 1) { -+ bit++; -+ if (b[i] & j) -+ ecc ^= bit; -+ } -+ } -+ tags->ecc = ecc; -+} -+ -+int yaffs_check_tags_ecc(struct yaffs_tags *tags) -+{ -+ unsigned ecc = tags->ecc; -+ -+ yaffs_calc_tags_ecc(tags); -+ -+ ecc ^= tags->ecc; -+ -+ if (ecc && ecc <= 64) { -+ /* TODO: Handle the failure better. Retire? */ -+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; -+ -+ ecc--; -+ -+ b[ecc / 8] ^= (1 << (ecc & 7)); -+ -+ /* Now recvalc the ecc */ -+ yaffs_calc_tags_ecc(tags); -+ -+ return 1; /* recovered error */ -+ } else if (ecc) { -+ /* Wierd ecc failure value */ -+ /* TODO Need to do somethiong here */ -+ return -1; /* unrecovered error */ -+ } -+ return 0; -+} -+ -+/********** Tags **********/ -+ -+static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, -+ struct yaffs_tags *tags_ptr) -+{ -+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; -+ -+ yaffs_calc_tags_ecc(tags_ptr); -+ -+ spare_ptr->tb0 = tu->as_bytes[0]; -+ spare_ptr->tb1 = tu->as_bytes[1]; -+ spare_ptr->tb2 = tu->as_bytes[2]; -+ spare_ptr->tb3 = tu->as_bytes[3]; -+ spare_ptr->tb4 = tu->as_bytes[4]; -+ spare_ptr->tb5 = tu->as_bytes[5]; -+ spare_ptr->tb6 = tu->as_bytes[6]; -+ spare_ptr->tb7 = tu->as_bytes[7]; -+} -+ -+static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, -+ struct yaffs_spare *spare_ptr, -+ struct yaffs_tags *tags_ptr) -+{ -+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; -+ int result; -+ -+ tu->as_bytes[0] = spare_ptr->tb0; -+ tu->as_bytes[1] = spare_ptr->tb1; -+ tu->as_bytes[2] = spare_ptr->tb2; -+ tu->as_bytes[3] = spare_ptr->tb3; -+ tu->as_bytes[4] = spare_ptr->tb4; -+ tu->as_bytes[5] = spare_ptr->tb5; -+ tu->as_bytes[6] = spare_ptr->tb6; -+ tu->as_bytes[7] = spare_ptr->tb7; -+ -+ result = yaffs_check_tags_ecc(tags_ptr); -+ if (result > 0) -+ dev->n_tags_ecc_fixed++; -+ else if (result < 0) -+ dev->n_tags_ecc_unfixed++; -+} -+ -+static void yaffs_spare_init(struct yaffs_spare *spare) -+{ -+ memset(spare, 0xff, sizeof(struct yaffs_spare)); -+} -+ -+static int yaffs_wr_nand(struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ struct yaffs_spare *spare) -+{ -+ int data_size = dev->data_bytes_per_chunk; -+ -+ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *) spare, sizeof(*spare)); -+} -+ -+static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, -+ struct yaffs_spare *spare, -+ enum yaffs_ecc_result *ecc_result, -+ int correct_errors) -+{ -+ int ret_val; -+ struct yaffs_spare local_spare; -+ int data_size; -+ int spare_size; -+ int ecc_result1, ecc_result2; -+ u8 calc_ecc[3]; -+ -+ if (!spare) { -+ /* If we don't have a real spare, then we use a local one. */ -+ /* Need this for the calculation of the ecc */ -+ spare = &local_spare; -+ } -+ data_size = dev->data_bytes_per_chunk; -+ spare_size = sizeof(struct yaffs_spare); -+ -+ if (dev->param.use_nand_ecc) -+ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *) spare, spare_size, -+ ecc_result); -+ -+ -+ /* Handle the ECC at this level. */ -+ -+ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *)spare, spare_size, -+ NULL); -+ if (!data || !correct_errors) -+ return ret_val; -+ -+ /* Do ECC correction if needed. */ -+ yaffs_ecc_calc(data, calc_ecc); -+ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); -+ yaffs_ecc_calc(&data[256], calc_ecc); -+ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); -+ -+ if (ecc_result1 > 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error fix performed on chunk %d:0", -+ nand_chunk); -+ dev->n_ecc_fixed++; -+ } else if (ecc_result1 < 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error unfixed on chunk %d:0", -+ nand_chunk); -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (ecc_result2 > 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error fix performed on chunk %d:1", -+ nand_chunk); -+ dev->n_ecc_fixed++; -+ } else if (ecc_result2 < 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error unfixed on chunk %d:1", -+ nand_chunk); -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (ecc_result1 || ecc_result2) { -+ /* We had a data problem on this page */ -+ yaffs_handle_rd_data_error(dev, nand_chunk); -+ } -+ -+ if (ecc_result1 < 0 || ecc_result2 < 0) -+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ else if (ecc_result1 > 0 || ecc_result2 > 0) -+ *ecc_result = YAFFS_ECC_RESULT_FIXED; -+ else -+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ -+ return ret_val; -+} -+ -+/* -+ * Functions for robustisizing -+ */ -+ -+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) -+{ -+ int flash_block = nand_chunk / dev->param.chunks_per_block; -+ -+ /* Mark the block for retirement */ -+ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> -+ needs_retiring = 1; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>>Block %d marked for retirement", -+ flash_block); -+ -+ /* TODO: -+ * Just do a garbage collection on the affected block -+ * then retire the block -+ * NB recursion -+ */ -+} -+ -+static int yaffs_tags_compat_wr(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, const struct yaffs_ext_tags *ext_tags) -+{ -+ struct yaffs_spare spare; -+ struct yaffs_tags tags; -+ -+ yaffs_spare_init(&spare); -+ -+ if (ext_tags->is_deleted) -+ spare.page_status = 0; -+ else { -+ tags.obj_id = ext_tags->obj_id; -+ tags.chunk_id = ext_tags->chunk_id; -+ -+ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); -+ -+ if (dev->data_bytes_per_chunk >= 1024) -+ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; -+ else -+ tags.n_bytes_msb = 3; -+ -+ tags.serial_number = ext_tags->serial_number; -+ -+ if (!dev->param.use_nand_ecc && data) { -+ yaffs_ecc_calc(data, spare.ecc1); -+ yaffs_ecc_calc(&data[256], spare.ecc2); -+ } -+ -+ yaffs_load_tags_to_spare(&spare, &tags); -+ } -+ return yaffs_wr_nand(dev, nand_chunk, data, &spare); -+} -+ -+static int yaffs_tags_compat_rd(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, struct yaffs_ext_tags *ext_tags) -+{ -+ struct yaffs_spare spare; -+ struct yaffs_tags tags; -+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; -+ static struct yaffs_spare spare_ff; -+ static int init; -+ int deleted; -+ -+ if (!init) { -+ memset(&spare_ff, 0xff, sizeof(spare_ff)); -+ init = 1; -+ } -+ -+ if (!yaffs_rd_chunk_nand(dev, nand_chunk, -+ data, &spare, &ecc_result, 1)) -+ return YAFFS_FAIL; -+ -+ /* ext_tags may be NULL */ -+ if (!ext_tags) -+ return YAFFS_OK; -+ -+ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; -+ -+ ext_tags->is_deleted = deleted; -+ ext_tags->ecc_result = ecc_result; -+ ext_tags->block_bad = 0; /* We're reading it */ -+ /* therefore it is not a bad block */ -+ ext_tags->chunk_used = -+ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; -+ -+ if (ext_tags->chunk_used) { -+ yaffs_get_tags_from_spare(dev, &spare, &tags); -+ ext_tags->obj_id = tags.obj_id; -+ ext_tags->chunk_id = tags.chunk_id; -+ ext_tags->n_bytes = tags.n_bytes_lsb; -+ -+ if (dev->data_bytes_per_chunk >= 1024) -+ ext_tags->n_bytes |= -+ (((unsigned)tags.n_bytes_msb) << 10); -+ -+ ext_tags->serial_number = tags.serial_number; -+ } -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) -+{ -+ struct yaffs_spare spare; -+ -+ memset(&spare, 0xff, sizeof(struct yaffs_spare)); -+ -+ spare.block_status = 'Y'; -+ -+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, -+ &spare); -+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, -+ NULL, &spare); -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ struct yaffs_spare spare0, spare1; -+ static struct yaffs_spare spare_ff; -+ static int init; -+ enum yaffs_ecc_result dummy; -+ -+ if (!init) { -+ memset(&spare_ff, 0xff, sizeof(spare_ff)); -+ init = 1; -+ } -+ -+ *seq_number = 0; -+ -+ /* Look for bad block markers in the first two chunks */ -+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, -+ NULL, &spare0, &dummy, 0); -+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, -+ NULL, &spare1, &dummy, 0); -+ -+ if (hweight8(spare0.block_status & spare1.block_status) < 7) -+ *state = YAFFS_BLOCK_STATE_DEAD; -+ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) -+ *state = YAFFS_BLOCK_STATE_EMPTY; -+ else -+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; -+ -+ return YAFFS_OK; -+} -+ -+void yaffs_tags_compat_install(struct yaffs_dev *dev) -+{ -+ if(dev->param.is_yaffs2) -+ return; -+ if(!dev->tagger.write_chunk_tags_fn) -+ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; -+ if(!dev->tagger.read_chunk_tags_fn) -+ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; -+ if(!dev->tagger.query_block_fn) -+ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; -+ if(!dev->tagger.mark_bad_fn) -+ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.10.40/fs/yaffs2/yaffs_tagscompat.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,44 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_TAGSCOMPAT_H__ -+#define __YAFFS_TAGSCOMPAT_H__ -+ -+ -+#include "yaffs_guts.h" -+ -+#if 0 -+ -+ -+int yaffs_tags_compat_wr(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, const struct yaffs_ext_tags *tags); -+int yaffs_tags_compat_rd(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, struct yaffs_ext_tags *tags); -+int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); -+int yaffs_tags_compat_query_block(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number); -+ -+#endif -+ -+ -+void yaffs_tags_compat_install(struct yaffs_dev *dev); -+void yaffs_calc_tags_ecc(struct yaffs_tags *tags); -+int yaffs_check_tags_ecc(struct yaffs_tags *tags); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.10.40/fs/yaffs2/yaffs_tagsmarshall.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,199 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yaffs_packedtags2.h" -+ -+static int yaffs_tags_marshall_write(struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ const struct yaffs_ext_tags *tags) -+{ -+ struct yaffs_packed_tags2 pt; -+ int retval; -+ -+ int packed_tags_size = -+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); -+ void *packed_tags_ptr = -+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_tags_marshall_write chunk %d data %p tags %p", -+ nand_chunk, data, tags); -+ -+ /* For yaffs2 writing there must be both data and tags. -+ * If we're using inband tags, then the tags are stuffed into -+ * the end of the data buffer. -+ */ -+ if (!data || !tags) -+ BUG(); -+ else if (dev->param.inband_tags) { -+ struct yaffs_packed_tags2_tags_only *pt2tp; -+ pt2tp = -+ (struct yaffs_packed_tags2_tags_only *)(data + -+ dev-> -+ data_bytes_per_chunk); -+ yaffs_pack_tags2_tags_only(pt2tp, tags); -+ } else { -+ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); -+ } -+ -+ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ (dev->param.inband_tags) ? NULL : packed_tags_ptr, -+ (dev->param.inband_tags) ? 0 : packed_tags_size); -+ -+ return retval; -+} -+ -+static int yaffs_tags_marshall_read(struct yaffs_dev *dev, -+ int nand_chunk, u8 *data, -+ struct yaffs_ext_tags *tags) -+{ -+ int retval = 0; -+ int local_data = 0; -+ u8 spare_buffer[100]; -+ enum yaffs_ecc_result ecc_result; -+ -+ struct yaffs_packed_tags2 pt; -+ -+ int packed_tags_size = -+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); -+ void *packed_tags_ptr = -+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_tags_marshall_read chunk %d data %p tags %p", -+ nand_chunk, data, tags); -+ -+ if (dev->param.inband_tags) { -+ if (!data) { -+ local_data = 1; -+ data = yaffs_get_temp_buffer(dev); -+ } -+ } -+ -+ if (dev->param.inband_tags || (data && !tags)) -+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ NULL, 0, -+ &ecc_result); -+ else if (tags) -+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ spare_buffer, packed_tags_size, -+ &ecc_result); -+ else -+ BUG(); -+ -+ -+ if (dev->param.inband_tags) { -+ if (tags) { -+ struct yaffs_packed_tags2_tags_only *pt2tp; -+ pt2tp = -+ (struct yaffs_packed_tags2_tags_only *) -+ &data[dev->data_bytes_per_chunk]; -+ yaffs_unpack_tags2_tags_only(tags, pt2tp); -+ } -+ } else if (tags) { -+ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); -+ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); -+ } -+ -+ if (local_data) -+ yaffs_release_temp_buffer(dev, data); -+ -+ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { -+ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { -+ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) -+ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; -+ dev->n_ecc_fixed++; -+ } -+ -+ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) -+ return YAFFS_OK; -+ else -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", -+ block_no); -+ -+ retval = dev->drv.drv_check_bad_fn(dev, block_no); -+ -+ if (retval== YAFFS_FAIL) { -+ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); -+ -+ *state = YAFFS_BLOCK_STATE_DEAD; -+ *seq_number = 0; -+ } else { -+ struct yaffs_ext_tags t; -+ -+ yaffs_tags_marshall_read(dev, -+ block_no * dev->param.chunks_per_block, -+ NULL, &t); -+ -+ if (t.chunk_used) { -+ *seq_number = t.seq_number; -+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; -+ } else { -+ *seq_number = 0; -+ *state = YAFFS_BLOCK_STATE_EMPTY; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "block query returns seq %d state %d", -+ *seq_number, *state); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ else -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ return dev->drv.drv_mark_bad_fn(dev, block_no); -+ -+} -+ -+ -+void yaffs_tags_marshall_install(struct yaffs_dev *dev) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!dev->tagger.write_chunk_tags_fn) -+ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; -+ -+ if (!dev->tagger.read_chunk_tags_fn) -+ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; -+ -+ if (!dev->tagger.query_block_fn) -+ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; -+ -+ if (!dev->tagger.mark_bad_fn) -+ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; -+ -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.10.40/fs/yaffs2/yaffs_tagsmarshall.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,22 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_TAGSMARSHALL_H__ -+#define __YAFFS_TAGSMARSHALL_H__ -+ -+#include "yaffs_guts.h" -+void yaffs_tags_marshall_install(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_trace.h linux-3.10.40/fs/yaffs2/yaffs_trace.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_trace.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,57 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YTRACE_H__ -+#define __YTRACE_H__ -+ -+extern unsigned int yaffs_trace_mask; -+extern unsigned int yaffs_wr_attempts; -+ -+/* -+ * Tracing flags. -+ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. -+ */ -+ -+#define YAFFS_TRACE_OS 0x00000002 -+#define YAFFS_TRACE_ALLOCATE 0x00000004 -+#define YAFFS_TRACE_SCAN 0x00000008 -+#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 -+#define YAFFS_TRACE_ERASE 0x00000020 -+#define YAFFS_TRACE_GC 0x00000040 -+#define YAFFS_TRACE_WRITE 0x00000080 -+#define YAFFS_TRACE_TRACING 0x00000100 -+#define YAFFS_TRACE_DELETION 0x00000200 -+#define YAFFS_TRACE_BUFFERS 0x00000400 -+#define YAFFS_TRACE_NANDACCESS 0x00000800 -+#define YAFFS_TRACE_GC_DETAIL 0x00001000 -+#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 -+#define YAFFS_TRACE_MTD 0x00004000 -+#define YAFFS_TRACE_CHECKPOINT 0x00008000 -+ -+#define YAFFS_TRACE_VERIFY 0x00010000 -+#define YAFFS_TRACE_VERIFY_NAND 0x00020000 -+#define YAFFS_TRACE_VERIFY_FULL 0x00040000 -+#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 -+ -+#define YAFFS_TRACE_SYNC 0x00100000 -+#define YAFFS_TRACE_BACKGROUND 0x00200000 -+#define YAFFS_TRACE_LOCK 0x00400000 -+#define YAFFS_TRACE_MOUNT 0x00800000 -+ -+#define YAFFS_TRACE_ERROR 0x40000000 -+#define YAFFS_TRACE_BUG 0x80000000 -+#define YAFFS_TRACE_ALWAYS 0xf0000000 -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_verify.c linux-3.10.40/fs/yaffs2/yaffs_verify.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_verify.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,529 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_verify.h" -+#include "yaffs_trace.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_nand.h" -+ -+int yaffs_skip_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & -+ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); -+} -+ -+static int yaffs_skip_full_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); -+} -+ -+static int yaffs_skip_nand_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); -+} -+ -+static const char * const block_state_name[] = { -+ "Unknown", -+ "Needs scan", -+ "Scanning", -+ "Empty", -+ "Allocating", -+ "Full", -+ "Dirty", -+ "Checkpoint", -+ "Collecting", -+ "Dead" -+}; -+ -+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) -+{ -+ int actually_used; -+ int in_use; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Report illegal runtime states */ -+ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has undefined state %d", -+ n, bi->block_state); -+ -+ switch (bi->block_state) { -+ case YAFFS_BLOCK_STATE_UNKNOWN: -+ case YAFFS_BLOCK_STATE_SCANNING: -+ case YAFFS_BLOCK_STATE_NEEDS_SCAN: -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has bad run-state %s", -+ n, block_state_name[bi->block_state]); -+ } -+ -+ /* Check pages in use and soft deletions are legal */ -+ -+ actually_used = bi->pages_in_use - bi->soft_del_pages; -+ -+ if (bi->pages_in_use < 0 || -+ bi->pages_in_use > dev->param.chunks_per_block || -+ bi->soft_del_pages < 0 || -+ bi->soft_del_pages > dev->param.chunks_per_block || -+ actually_used < 0 || actually_used > dev->param.chunks_per_block) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has illegal values pages_in_used %d soft_del_pages %d", -+ n, bi->pages_in_use, bi->soft_del_pages); -+ -+ /* Check chunk bitmap legal */ -+ in_use = yaffs_count_chunk_bits(dev, n); -+ if (in_use != bi->pages_in_use) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", -+ n, bi->pages_in_use, in_use); -+} -+ -+void yaffs_verify_collected_blk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, int n) -+{ -+ yaffs_verify_blk(dev, bi, n); -+ -+ /* After collection the block should be in the erased state */ -+ -+ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && -+ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Block %d is in state %d after gc, should be erased", -+ n, bi->block_state); -+ } -+} -+ -+void yaffs_verify_blocks(struct yaffs_dev *dev) -+{ -+ int i; -+ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; -+ int illegal_states = 0; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ memset(state_count, 0, sizeof(state_count)); -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); -+ yaffs_verify_blk(dev, bi, i); -+ -+ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) -+ state_count[bi->block_state]++; -+ else -+ illegal_states++; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); -+ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "%d blocks have illegal states", -+ illegal_states); -+ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Too many allocating blocks"); -+ -+ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "%s %d blocks", -+ block_state_name[i], state_count[i]); -+ -+ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Checkpoint block count wrong dev %d count %d", -+ dev->blocks_in_checkpt, -+ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); -+ -+ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Erased block count wrong dev %d count %d", -+ dev->n_erased_blocks, -+ state_count[YAFFS_BLOCK_STATE_EMPTY]); -+ -+ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Too many collecting blocks %d (max is 1)", -+ state_count[YAFFS_BLOCK_STATE_COLLECTING]); -+} -+ -+/* -+ * Verify the object header. oh must be valid, but obj and tags may be NULL in -+ * which case those tests will not be performed. -+ */ -+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, -+ struct yaffs_ext_tags *tags, int parent_check) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ if (!(tags && obj && oh)) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Verifying object header tags %p obj %p oh %p", -+ tags, obj, oh); -+ return; -+ } -+ -+ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || -+ oh->type > YAFFS_OBJECT_TYPE_MAX) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header type is illegal value 0x%x", -+ tags->obj_id, oh->type); -+ -+ if (tags->obj_id != obj->obj_id) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch obj_id %d", -+ tags->obj_id, obj->obj_id); -+ -+ /* -+ * Check that the object's parent ids match if parent_check requested. -+ * -+ * Tests do not apply to the root object. -+ */ -+ -+ if (parent_check && tags->obj_id > 1 && !obj->parent) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch parent_id %d obj->parent is NULL", -+ tags->obj_id, oh->parent_obj_id); -+ -+ if (parent_check && obj->parent && -+ oh->parent_obj_id != obj->parent->obj_id && -+ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || -+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch parent_id %d parent_obj_id %d", -+ tags->obj_id, oh->parent_obj_id, -+ obj->parent->obj_id); -+ -+ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header name is NULL", -+ obj->obj_id); -+ -+ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header name is 0xff", -+ obj->obj_id); -+} -+ -+void yaffs_verify_file(struct yaffs_obj *obj) -+{ -+ u32 x; -+ int required_depth; -+ int actual_depth; -+ int last_chunk; -+ u32 offset_in_chunk; -+ u32 the_chunk; -+ -+ u32 i; -+ struct yaffs_dev *dev; -+ struct yaffs_ext_tags tags; -+ struct yaffs_tnode *tn; -+ u32 obj_id; -+ -+ if (!obj) -+ return; -+ -+ if (yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ dev = obj->my_dev; -+ obj_id = obj->obj_id; -+ -+ -+ /* Check file size is consistent with tnode depth */ -+ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, -+ &last_chunk, &offset_in_chunk); -+ last_chunk++; -+ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (x > 0) { -+ x >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ actual_depth = obj->variant.file_variant.top_level; -+ -+ /* Check that the chunks in the tnode tree are all correct. -+ * We do this by scanning through the tnode tree and -+ * checking the tags for every chunk match. -+ */ -+ -+ if (yaffs_skip_nand_verification(dev)) -+ return; -+ -+ for (i = 1; i <= last_chunk; i++) { -+ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); -+ -+ if (!tn) -+ continue; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, i); -+ if (the_chunk > 0) { -+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, -+ &tags); -+ if (tags.obj_id != obj_id || tags.chunk_id != i) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", -+ obj_id, i, the_chunk, -+ tags.obj_id, tags.chunk_id); -+ } -+ } -+} -+ -+void yaffs_verify_link(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ /* Verify sane equivalent object */ -+} -+ -+void yaffs_verify_symlink(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ /* Verify symlink string */ -+} -+ -+void yaffs_verify_special(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+} -+ -+void yaffs_verify_obj(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ u32 chunk_min; -+ u32 chunk_max; -+ u32 chunk_id_ok; -+ u32 chunk_in_range; -+ u32 chunk_wrongly_deleted; -+ u32 chunk_valid; -+ -+ if (!obj) -+ return; -+ -+ if (obj->being_created) -+ return; -+ -+ dev = obj->my_dev; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Check sane object header chunk */ -+ -+ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; -+ chunk_max = -+ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; -+ -+ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && -+ ((unsigned)(obj->hdr_chunk)) <= chunk_max); -+ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); -+ chunk_valid = chunk_in_range && -+ yaffs_check_chunk_bit(dev, -+ obj->hdr_chunk / dev->param.chunks_per_block, -+ obj->hdr_chunk % dev->param.chunks_per_block); -+ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; -+ -+ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has chunk_id %d %s %s", -+ obj->obj_id, obj->hdr_chunk, -+ chunk_id_ok ? "" : ",out of range", -+ chunk_wrongly_deleted ? ",marked as deleted" : ""); -+ -+ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { -+ struct yaffs_ext_tags tags; -+ struct yaffs_obj_hdr *oh; -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ oh = (struct yaffs_obj_hdr *)buffer; -+ -+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); -+ -+ yaffs_verify_oh(obj, oh, &tags, 1); -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ -+ /* Verify it has a parent */ -+ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has parent pointer %p which does not look like an object", -+ obj->obj_id, obj->parent); -+ } -+ -+ /* Verify parent is a directory */ -+ if (obj->parent && -+ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d's parent is not a directory (type %d)", -+ obj->obj_id, obj->parent->variant_type); -+ } -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ yaffs_verify_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ yaffs_verify_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ yaffs_verify_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ yaffs_verify_link(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ yaffs_verify_special(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ default: -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has illegaltype %d", -+ obj->obj_id, obj->variant_type); -+ break; -+ } -+} -+ -+void yaffs_verify_objects(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ int i; -+ struct list_head *lh; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each(lh, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ yaffs_verify_obj(obj); -+ } -+ } -+} -+ -+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) -+{ -+ struct list_head *lh; -+ struct yaffs_obj *list_obj; -+ int count = 0; -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); -+ BUG(); -+ return; -+ } -+ -+ if (yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ if (!obj->parent) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); -+ BUG(); -+ return; -+ } -+ -+ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); -+ BUG(); -+ } -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ list_for_each(lh, &obj->parent->variant.dir_variant.children) { -+ list_obj = list_entry(lh, struct yaffs_obj, siblings); -+ yaffs_verify_obj(list_obj); -+ if (obj == list_obj) -+ count++; -+ } -+ -+ if (count != 1) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Object in directory %d times", -+ count); -+ BUG(); -+ } -+} -+ -+void yaffs_verify_dir(struct yaffs_obj *directory) -+{ -+ struct list_head *lh; -+ struct yaffs_obj *list_obj; -+ -+ if (!directory) { -+ BUG(); -+ return; -+ } -+ -+ if (yaffs_skip_full_verification(directory->my_dev)) -+ return; -+ -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Directory has wrong type: %d", -+ directory->variant_type); -+ BUG(); -+ } -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ list_for_each(lh, &directory->variant.dir_variant.children) { -+ list_obj = list_entry(lh, struct yaffs_obj, siblings); -+ if (list_obj->parent != directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Object in directory list has wrong parent %p", -+ list_obj->parent); -+ BUG(); -+ } -+ yaffs_verify_obj_in_dir(list_obj); -+ } -+} -+ -+static int yaffs_free_verification_failures; -+ -+void yaffs_verify_free_chunks(struct yaffs_dev *dev) -+{ -+ int counted; -+ int difference; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ counted = yaffs_count_free_chunks(dev); -+ -+ difference = dev->n_free_chunks - counted; -+ -+ if (difference) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Freechunks verification failure %d %d %d", -+ dev->n_free_chunks, counted, difference); -+ yaffs_free_verification_failures++; -+ } -+} -+ -+int yaffs_verify_file_sane(struct yaffs_obj *in) -+{ -+ (void) in; -+ return YAFFS_OK; -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_verify.h linux-3.10.40/fs/yaffs2/yaffs_verify.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_verify.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,43 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_VERIFY_H__ -+#define __YAFFS_VERIFY_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, -+ int n); -+void yaffs_verify_collected_blk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, int n); -+void yaffs_verify_blocks(struct yaffs_dev *dev); -+ -+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, -+ struct yaffs_ext_tags *tags, int parent_check); -+void yaffs_verify_file(struct yaffs_obj *obj); -+void yaffs_verify_link(struct yaffs_obj *obj); -+void yaffs_verify_symlink(struct yaffs_obj *obj); -+void yaffs_verify_special(struct yaffs_obj *obj); -+void yaffs_verify_obj(struct yaffs_obj *obj); -+void yaffs_verify_objects(struct yaffs_dev *dev); -+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); -+void yaffs_verify_dir(struct yaffs_obj *directory); -+void yaffs_verify_free_chunks(struct yaffs_dev *dev); -+ -+int yaffs_verify_file_sane(struct yaffs_obj *obj); -+ -+int yaffs_skip_verification(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_vfs.c linux-3.10.40/fs/yaffs2/yaffs_vfs.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_vfs.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,3600 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * Acknowledgements: -+ * Luc van OostenRyck for numerous patches. -+ * Nick Bane for numerous patches. -+ * Nick Bane for 2.5/2.6 integration. -+ * Andras Toth for mknod rdev issue. -+ * Michael Fischer for finding the problem with inode inconsistency. -+ * Some code bodily lifted from JFFS -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * -+ * This is the file system front-end to YAFFS that hooks it up to -+ * the VFS. -+ * -+ * Special notes: -+ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with -+ * this superblock -+ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this -+ * superblock -+ * >> inode->u.generic_ip points to the associated struct yaffs_obj. -+ */ -+ -+/* -+ * There are two variants of the VFS glue code. This variant should compile -+ * for any version of Linux. -+ */ -+#include -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) -+#define YAFFS_COMPILE_BACKGROUND -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) -+#define YAFFS_COMPILE_FREEZER -+#endif -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) -+#define YAFFS_COMPILE_EXPORTFS -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) -+#define YAFFS_USE_SETATTR_COPY -+#define YAFFS_USE_TRUNCATE_SETSIZE -+#endif -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) -+#define YAFFS_HAS_EVICT_INODE -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) -+#define YAFFS_NEW_FOLLOW_LINK 1 -+#else -+#define YAFFS_NEW_FOLLOW_LINK 0 -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) -+#define YAFFS_HAS_WRITE_SUPER -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+#include -+#endif -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+#include -+#endif -+ -+#ifdef YAFFS_COMPILE_BACKGROUND -+#include -+#include -+#endif -+#ifdef YAFFS_COMPILE_FREEZER -+#include -+#endif -+ -+#include -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ -+#include -+ -+#define UnlockPage(p) unlock_page(p) -+#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) -+ -+/* FIXME: use sb->s_id instead ? */ -+#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) -+ -+#else -+ -+#include -+#define BDEVNAME_SIZE 0 -+#define yaffs_devname(sb, buf) kdevname(sb->s_dev) -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) -+/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ -+#define __user -+#endif -+ -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) -+#define YPROC_ROOT (&proc_root) -+#else -+#define YPROC_ROOT NULL -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) -+#define Y_INIT_TIMER(a) init_timer(a) -+#else -+#define Y_INIT_TIMER(a) init_timer_on_stack(a) -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) -+#define YAFFS_USE_WRITE_BEGIN_END 1 -+#else -+#define YAFFS_USE_WRITE_BEGIN_END 0 -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) -+#define YAFFS_SUPER_HAS_DIRTY -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) -+#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) -+static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) -+{ -+ uint64_t result = partition_size; -+ do_div(result, block_size); -+ return (uint32_t) result; -+} -+#else -+#define YCALCBLOCKS(s, b) ((s)/(b)) -+#endif -+ -+#include -+#include -+ -+#include "yportenv.h" -+#include "yaffs_trace.h" -+#include "yaffs_guts.h" -+#include "yaffs_attribs.h" -+ -+#include "yaffs_linux.h" -+ -+#include "yaffs_mtdif.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_getblockinfo.h" -+ -+unsigned int yaffs_trace_mask = -+ YAFFS_TRACE_BAD_BLOCKS | -+ YAFFS_TRACE_ALWAYS | -+ 0; -+ -+unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; -+unsigned int yaffs_auto_checkpoint = 1; -+unsigned int yaffs_gc_control = 1; -+unsigned int yaffs_bg_enable = 1; -+unsigned int yaffs_auto_select = 1; -+/* Module Parameters */ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+module_param(yaffs_trace_mask, uint, 0644); -+module_param(yaffs_wr_attempts, uint, 0644); -+module_param(yaffs_auto_checkpoint, uint, 0644); -+module_param(yaffs_gc_control, uint, 0644); -+module_param(yaffs_bg_enable, uint, 0644); -+#else -+MODULE_PARM(yaffs_trace_mask, "i"); -+MODULE_PARM(yaffs_wr_attempts, "i"); -+MODULE_PARM(yaffs_auto_checkpoint, "i"); -+MODULE_PARM(yaffs_gc_control, "i"); -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) -+/* use iget and read_inode */ -+#define Y_IGET(sb, inum) iget((sb), (inum)) -+ -+#else -+/* Call local equivalent */ -+#define YAFFS_USE_OWN_IGET -+#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) -+ -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) -+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) -+#else -+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) -+#endif -+ -+#define yaffs_inode_to_obj(iptr) \ -+ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) -+#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) -+#else -+#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+#define Y_CLEAR_INODE(i) clear_inode(i) -+#else -+#define Y_CLEAR_INODE(i) end_writeback(i) -+#endif -+ -+ -+#define update_dir_time(dir) do {\ -+ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ -+ } while (0) -+ -+static void yaffs_fill_inode_from_obj(struct inode *inode, -+ struct yaffs_obj *obj); -+ -+ -+static void yaffs_gross_lock(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); -+ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); -+} -+ -+static void yaffs_gross_unlock(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); -+ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); -+} -+ -+ -+static int yaffs_readpage_nolock(struct file *f, struct page *pg) -+{ -+ /* Lifted from jffs2 */ -+ -+ struct yaffs_obj *obj; -+ unsigned char *pg_buf; -+ int ret; -+ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readpage_nolock at %lld, size %08x", -+ (long long)pos, -+ (unsigned)PAGE_CACHE_SIZE); -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ BUG_ON(!PageLocked(pg)); -+#else -+ if (!PageLocked(pg)) -+ PAGE_BUG(pg); -+#endif -+ -+ pg_buf = kmap(pg); -+ /* FIXME: Can kmap fail? */ -+ -+ yaffs_gross_lock(dev); -+ -+ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (ret >= 0) -+ ret = 0; -+ -+ if (ret) { -+ ClearPageUptodate(pg); -+ SetPageError(pg); -+ } else { -+ SetPageUptodate(pg); -+ ClearPageError(pg); -+ } -+ -+ flush_dcache_page(pg); -+ kunmap(pg); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); -+ return ret; -+} -+ -+static int yaffs_readpage_unlock(struct file *f, struct page *pg) -+{ -+ int ret = yaffs_readpage_nolock(f, pg); -+ UnlockPage(pg); -+ return ret; -+} -+ -+static int yaffs_readpage(struct file *f, struct page *pg) -+{ -+ int ret; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); -+ ret = yaffs_readpage_unlock(f, pg); -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); -+ return ret; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+#define YCRED_FSUID() from_kuid(&init_user_ns, current_fsuid()) -+#define YCRED_FSGID() from_kgid(&init_user_ns, current_fsgid()) -+#else -+#define YCRED_FSUID() YCRED(current)->fsuid -+#define YCRED_FSGID() YCRED(current)->fsgid -+ -+static inline uid_t i_uid_read(const struct inode *inode) -+{ -+ return inode->i_uid; -+} -+ -+static inline gid_t i_gid_read(const struct inode *inode) -+{ -+ return inode->i_gid; -+} -+ -+static inline void i_uid_write(struct inode *inode, uid_t uid) -+{ -+ inode->i_uid = uid; -+} -+ -+static inline void i_gid_write(struct inode *inode, gid_t gid) -+{ -+ inode->i_gid = gid; -+} -+#endif -+ -+static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) -+{ -+ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); -+ -+ if (lc) -+ lc->dirty = val; -+ -+# ifdef YAFFS_SUPER_HAS_DIRTY -+ { -+ struct super_block *sb = lc->super; -+ -+ if (sb) -+ sb->s_dirt = val; -+ } -+#endif -+ -+} -+ -+static void yaffs_set_super_dirty(struct yaffs_dev *dev) -+{ -+ yaffs_set_super_dirty_val(dev, 1); -+} -+ -+static void yaffs_clear_super_dirty(struct yaffs_dev *dev) -+{ -+ yaffs_set_super_dirty_val(dev, 0); -+} -+ -+static int yaffs_check_super_dirty(struct yaffs_dev *dev) -+{ -+ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); -+ -+ if (lc && lc->dirty) -+ return 1; -+ -+# ifdef YAFFS_SUPER_HAS_DIRTY -+ { -+ struct super_block *sb = lc->super; -+ -+ if (sb && sb->s_dirt) -+ return 1; -+ } -+#endif -+ return 0; -+ -+} -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_writepage(struct page *page, struct writeback_control *wbc) -+#else -+static int yaffs_writepage(struct page *page) -+#endif -+{ -+ struct yaffs_dev *dev; -+ struct address_space *mapping = page->mapping; -+ struct inode *inode; -+ unsigned long end_index; -+ char *buffer; -+ struct yaffs_obj *obj; -+ int n_written = 0; -+ unsigned n_bytes; -+ loff_t i_size; -+ -+ if (!mapping) -+ BUG(); -+ inode = mapping->host; -+ if (!inode) -+ BUG(); -+ i_size = i_size_read(inode); -+ -+ end_index = i_size >> PAGE_CACHE_SHIFT; -+ -+ if (page->index < end_index) -+ n_bytes = PAGE_CACHE_SIZE; -+ else { -+ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); -+ -+ if (page->index > end_index || !n_bytes) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_writepage at %lld, inode size = %lld!!", -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, -+ inode->i_size); -+ yaffs_trace(YAFFS_TRACE_OS, -+ " -> don't care!!"); -+ -+ zero_user_segment(page, 0, PAGE_CACHE_SIZE); -+ set_page_writeback(page); -+ unlock_page(page); -+ end_page_writeback(page); -+ return 0; -+ } -+ } -+ -+ if (n_bytes != PAGE_CACHE_SIZE) -+ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); -+ -+ get_page(page); -+ -+ buffer = kmap(page); -+ -+ obj = yaffs_inode_to_obj(inode); -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_writepage at %lld, size %08x", -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "writepag0: obj = %lld, ino = %lld", -+ obj->variant.file_variant.file_size, inode->i_size); -+ -+ n_written = yaffs_wr_file(obj, buffer, -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); -+ -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "writepag1: obj = %lld, ino = %lld", -+ obj->variant.file_variant.file_size, inode->i_size); -+ -+ yaffs_gross_unlock(dev); -+ -+ kunmap(page); -+ set_page_writeback(page); -+ unlock_page(page); -+ end_page_writeback(page); -+ put_page(page); -+ -+ return (n_written == n_bytes) ? 0 : -ENOSPC; -+} -+ -+/* Space holding and freeing is done to ensure we have space available for write_begin/end */ -+/* For now we just assume few parallel writes and check against a small number. */ -+/* Todo: need to do this with a counter to handle parallel reads better */ -+ -+static ssize_t yaffs_hold_space(struct file *f) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ int n_free_chunks; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ n_free_chunks = yaffs_get_n_free_chunks(dev); -+ -+ yaffs_gross_unlock(dev); -+ -+ return (n_free_chunks > 20) ? 1 : 0; -+} -+ -+static void yaffs_release_space(struct file *f) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_gross_unlock(dev); -+} -+ -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+static int yaffs_write_begin(struct file *filp, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned flags, -+ struct page **pagep, void **fsdata) -+{ -+ struct page *pg = NULL; -+ pgoff_t index = pos >> PAGE_CACHE_SHIFT; -+ -+ int ret = 0; -+ int space_held = 0; -+ -+ /* Get a page */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) -+ pg = grab_cache_page_write_begin(mapping, index, flags); -+#else -+ pg = __grab_cache_page(mapping, index); -+#endif -+ -+ *pagep = pg; -+ if (!pg) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "start yaffs_write_begin index %d(%x) uptodate %d", -+ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); -+ -+ /* Get fs space */ -+ space_held = yaffs_hold_space(filp); -+ -+ if (!space_held) { -+ ret = -ENOSPC; -+ goto out; -+ } -+ -+ /* Update page if required */ -+ -+ if (!Page_Uptodate(pg)) -+ ret = yaffs_readpage_nolock(filp, pg); -+ -+ if (ret) -+ goto out; -+ -+ /* Happy path return */ -+ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); -+ -+ return 0; -+ -+out: -+ yaffs_trace(YAFFS_TRACE_OS, -+ "end yaffs_write_begin fail returning %d", ret); -+ if (space_held) -+ yaffs_release_space(filp); -+ if (pg) { -+ unlock_page(pg); -+ page_cache_release(pg); -+ } -+ return ret; -+} -+ -+#else -+ -+static int yaffs_prepare_write(struct file *f, struct page *pg, -+ unsigned offset, unsigned to) -+{ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); -+ -+ if (!Page_Uptodate(pg)) -+ return yaffs_readpage_nolock(f, pg); -+ return 0; -+} -+#endif -+ -+ -+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, -+ loff_t * pos) -+{ -+ struct yaffs_obj *obj; -+ int n_written; -+ loff_t ipos; -+ struct inode *inode; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write: hey obj is null!"); -+ return -EINVAL; -+ } -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ inode = f->f_dentry->d_inode; -+ -+ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) -+ ipos = inode->i_size; -+ else -+ ipos = *pos; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", -+ (unsigned)n, (unsigned)n, obj->obj_id, ipos); -+ -+ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); -+ -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write: %d(%x) bytes written", -+ (unsigned)n, (unsigned)n); -+ -+ if (n_written > 0) { -+ ipos += n_written; -+ *pos = ipos; -+ if (ipos > inode->i_size) { -+ inode->i_size = ipos; -+ inode->i_blocks = (ipos + 511) >> 9; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write size updated to %lld bytes, %d blocks", -+ ipos, (int)(inode->i_blocks)); -+ } -+ -+ } -+ yaffs_gross_unlock(dev); -+ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; -+} -+ -+ -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+static int yaffs_write_end(struct file *filp, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned copied, -+ struct page *pg, void *fsdadata) -+{ -+ int ret = 0; -+ void *addr, *kva; -+ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); -+ -+ kva = kmap(pg); -+ addr = kva + offset_into_page; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_write_end addr %p pos %lld n_bytes %d", -+ addr, pos, copied); -+ -+ ret = yaffs_file_write(filp, addr, copied, &pos); -+ -+ if (ret != copied) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_write_end not same size ret %d copied %d", -+ ret, copied); -+ SetPageError(pg); -+ } -+ -+ kunmap(pg); -+ -+ yaffs_release_space(filp); -+ unlock_page(pg); -+ page_cache_release(pg); -+ return ret; -+} -+#else -+ -+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, -+ unsigned to) -+{ -+ void *addr, *kva; -+ -+ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; -+ int n_bytes = to - offset; -+ int n_written; -+ -+ kva = kmap(pg); -+ addr = kva + offset; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write addr %p pos %lld n_bytes %d", -+ addr, pos, n_bytes); -+ -+ n_written = yaffs_file_write(f, addr, n_bytes, &pos); -+ -+ if (n_written != n_bytes) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write not same size n_written %d n_bytes %d", -+ n_written, n_bytes); -+ SetPageError(pg); -+ } -+ kunmap(pg); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write returning %d", -+ n_written == n_bytes ? 0 : n_written); -+ -+ return n_written == n_bytes ? 0 : n_written; -+} -+#endif -+ -+static struct address_space_operations yaffs_file_address_operations = { -+ .readpage = yaffs_readpage, -+ .writepage = yaffs_writepage, -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+ .write_begin = yaffs_write_begin, -+ .write_end = yaffs_write_end, -+#else -+ .prepare_write = yaffs_prepare_write, -+ .commit_write = yaffs_commit_write, -+#endif -+}; -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_file_flush(struct file *file, fl_owner_t id) -+#else -+static int yaffs_file_flush(struct file *file) -+#endif -+{ -+ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); -+ -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_flush object %d (%s)", -+ obj->obj_id, -+ obj->dirty ? "dirty" : "clean"); -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_flush_file(obj, 1, 0); -+ -+ yaffs_gross_unlock(dev); -+ -+ return 0; -+} -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) -+static int yaffs_sync_object(struct file *file, int datasync) -+#else -+static int yaffs_sync_object(struct file *file, struct dentry *dentry, -+ int datasync) -+#endif -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) -+ struct dentry *dentry = file->f_path.dentry; -+#endif -+ -+ obj = yaffs_dentry_to_obj(dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, -+ "yaffs_sync_object"); -+ yaffs_gross_lock(dev); -+ yaffs_flush_file(obj, 1, datasync); -+ yaffs_gross_unlock(dev); -+ return 0; -+} -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) -+static const struct file_operations yaffs_file_operations = { -+ .read = do_sync_read, -+ .write = do_sync_write, -+ .aio_read = generic_file_aio_read, -+ .aio_write = generic_file_aio_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+ .splice_read = generic_file_splice_read, -+ .splice_write = generic_file_splice_write, -+ .llseek = generic_file_llseek, -+}; -+ -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) -+ -+static const struct file_operations yaffs_file_operations = { -+ .read = do_sync_read, -+ .write = do_sync_write, -+ .aio_read = generic_file_aio_read, -+ .aio_write = generic_file_aio_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+ .sendfile = generic_file_sendfile, -+}; -+ -+#else -+ -+static const struct file_operations yaffs_file_operations = { -+ .read = generic_file_read, -+ .write = generic_file_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ .sendfile = generic_file_sendfile, -+#endif -+}; -+#endif -+ -+ -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) -+static void zero_user_segment(struct page *page, unsigned start, unsigned end) -+{ -+ void *kaddr = kmap_atomic(page, KM_USER0); -+ memset(kaddr + start, 0, end - start); -+ kunmap_atomic(kaddr, KM_USER0); -+ flush_dcache_page(page); -+} -+#endif -+ -+ -+static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) -+{ -+#ifdef YAFFS_USE_TRUNCATE_SETSIZE -+ truncate_setsize(inode, newsize); -+ return 0; -+#else -+ truncate_inode_pages(&inode->i_data, newsize); -+ return 0; -+#endif -+ -+} -+ -+ -+static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) -+{ -+#ifdef YAFFS_USE_SETATTR_COPY -+ setattr_copy(inode, attr); -+ return 0; -+#else -+ return inode_setattr(inode, attr); -+#endif -+ -+} -+ -+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_setattr of object %d", -+ yaffs_inode_to_obj(inode)->obj_id); -+#if 0 -+ /* Fail if a requested resize >= 2GB */ -+ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) -+ error = -EINVAL; -+#endif -+ -+ if (error == 0) -+ error = inode_change_ok(inode, attr); -+ if (error == 0) { -+ int result; -+ if (!error) { -+ error = yaffs_vfs_setattr(inode, attr); -+ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); -+ if (attr->ia_valid & ATTR_SIZE) { -+ yaffs_vfs_setsize(inode, attr->ia_size); -+ inode->i_blocks = (inode->i_size + 511) >> 9; -+ } -+ } -+ dev = yaffs_inode_to_obj(inode)->my_dev; -+ if (attr->ia_valid & ATTR_SIZE) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "resize to %d(%x)", -+ (int)(attr->ia_size), -+ (int)(attr->ia_size)); -+ } -+ yaffs_gross_lock(dev); -+ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); -+ if (result == YAFFS_OK) { -+ error = 0; -+ } else { -+ error = -EPERM; -+ } -+ yaffs_gross_unlock(dev); -+ -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); -+ -+ return error; -+} -+ -+static int yaffs_setxattr(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ int result; -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ result = yaffs_set_xattrib(obj, name, value, size, flags); -+ if (result == YAFFS_OK) -+ error = 0; -+ else if (result < 0) -+ error = result; -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); -+ -+ return error; -+} -+ -+static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, -+ void *buff, size_t size) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_getxattr \"%s\" from object %d", -+ name, obj->obj_id); -+ -+ if (error == 0) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ error = yaffs_get_xattrib(obj, name, buff, size); -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); -+ -+ return error; -+} -+ -+static int yaffs_removexattr(struct dentry *dentry, const char *name) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_removexattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ int result; -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ result = yaffs_remove_xattrib(obj, name); -+ if (result == YAFFS_OK) -+ error = 0; -+ else if (result < 0) -+ error = result; -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_removexattr done returning %d", error); -+ -+ return error; -+} -+ -+static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_listxattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ error = yaffs_list_xattrib(obj, buff, size); -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_listxattr done returning %d", error); -+ -+ return error; -+} -+ -+ -+static const struct inode_operations yaffs_file_inode_operations = { -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+ -+static int yaffs_readlink(struct dentry *dentry, char __user * buffer, -+ int buflen) -+{ -+ unsigned char *alias; -+ int ret; -+ -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (!alias) -+ return -ENOMEM; -+ -+ ret = vfs_readlink(dentry, buffer, buflen, alias); -+ kfree(alias); -+ return ret; -+} -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ void *ret; -+#else -+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ int ret -+#endif -+ unsigned char *alias; -+ int ret_int = 0; -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); -+ yaffs_gross_unlock(dev); -+ -+ if (!alias) { -+ ret_int = -ENOMEM; -+ goto out; -+ } -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+ nd_set_link(nd, alias); -+ ret = alias; -+out: -+ if (ret_int) -+ ret = ERR_PTR(ret_int); -+ return ret; -+#else -+ ret = vfs_follow_link(nd, alias); -+ kfree(alias); -+out: -+ if (ret_int) -+ ret = ret_int; -+ return ret; -+#endif -+} -+ -+ -+#ifdef YAFFS_HAS_PUT_INODE -+ -+/* For now put inode is just for debugging -+ * Put inode is called when the inode **structure** is put. -+ */ -+static void yaffs_put_inode(struct inode *inode) -+{ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_put_inode: ino %d, count %d"), -+ (int)inode->i_ino, atomic_read(&inode->i_count); -+ -+} -+#endif -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) -+{ -+ kfree(alias); -+} -+#endif -+ -+static const struct inode_operations yaffs_symlink_inode_operations = { -+ .readlink = yaffs_readlink, -+ .follow_link = yaffs_follow_link, -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+ .put_link = yaffs_put_link, -+#endif -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+#ifdef YAFFS_USE_OWN_IGET -+ -+static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) -+{ -+ struct inode *inode; -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); -+ -+ inode = iget_locked(sb, ino); -+ if (!inode) -+ return ERR_PTR(-ENOMEM); -+ if (!(inode->i_state & I_NEW)) -+ return inode; -+ -+ /* NB This is called as a side effect of other functions, but -+ * we had to release the lock to prevent deadlocks, so -+ * need to lock again. -+ */ -+ -+ yaffs_gross_lock(dev); -+ -+ obj = yaffs_find_by_number(dev, inode->i_ino); -+ -+ yaffs_fill_inode_from_obj(inode, obj); -+ -+ yaffs_gross_unlock(dev); -+ -+ unlock_new_inode(inode); -+ return inode; -+} -+ -+#else -+ -+static void yaffs_read_inode(struct inode *inode) -+{ -+ /* NB This is called as a side effect of other functions, but -+ * we had to release the lock to prevent deadlocks, so -+ * need to lock again. -+ */ -+ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_inode for %d", (int)inode->i_ino); -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_lock(dev); -+ -+ obj = yaffs_find_by_number(dev, inode->i_ino); -+ -+ yaffs_fill_inode_from_obj(inode, obj); -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_unlock(dev); -+} -+ -+#endif -+ -+ -+ -+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, -+ struct yaffs_obj *obj) -+{ -+ struct inode *inode; -+ -+ if (!sb) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for NULL super_block!!"); -+ return NULL; -+ -+ } -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for NULL object!!"); -+ return NULL; -+ -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for object %d", obj->obj_id); -+ -+ inode = Y_IGET(sb, obj->obj_id); -+ if (IS_ERR(inode)) -+ return NULL; -+ -+ /* NB Side effect: iget calls back to yaffs_read_inode(). */ -+ /* iget also increments the inode's i_count */ -+ /* NB You can't be holding gross_lock or deadlock will happen! */ -+ -+ return inode; -+} -+ -+ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -+#define YCRED(x) x -+#else -+#define YCRED(x) (x->cred) -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t rdev) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, -+ dev_t rdev) -+#else -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, -+ int rdev) -+#endif -+{ -+ struct inode *inode; -+ -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_dev *dev; -+ -+ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); -+ -+ int error = -ENOSPC; -+ uid_t uid = YCRED_FSUID(); -+ gid_t gid = -+ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); -+ -+ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) -+ mode |= S_ISGID; -+ -+ if (parent) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: parent object %d type %d", -+ parent->obj_id, parent->variant_type); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: could not get parent object"); -+ return -EPERM; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: making oject for %s, mode %x dev %x", -+ dentry->d_name.name, mode, rdev); -+ -+ dev = parent->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ switch (mode & S_IFMT) { -+ default: -+ /* Special (socket, fifo, device...) */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ obj = -+ yaffs_create_special(parent, dentry->d_name.name, mode, uid, -+ gid, old_encode_dev(rdev)); -+#else -+ obj = -+ yaffs_create_special(parent, dentry->d_name.name, mode, uid, -+ gid, rdev); -+#endif -+ break; -+ case S_IFREG: /* file */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); -+ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, -+ gid); -+ break; -+ case S_IFDIR: /* directory */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); -+ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, -+ uid, gid); -+ break; -+ case S_IFLNK: /* symlink */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); -+ obj = NULL; /* Do we ever get here? */ -+ break; -+ } -+ -+ /* Can not call yaffs_get_inode() with gross lock held */ -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); -+ d_instantiate(dentry, inode); -+ update_dir_time(dir); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod created object %d count = %d", -+ obj->obj_id, atomic_read(&inode->i_count)); -+ error = 0; -+ yaffs_fill_inode_from_obj(dir, parent); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); -+ error = -ENOMEM; -+ } -+ -+ return error; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -+#else -+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) -+#endif -+{ -+ int ret_val; -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); -+ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); -+ return ret_val; -+} -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool dummy) -+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ struct nameidata *n) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, -+ struct nameidata *n) -+#else -+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) -+#endif -+{ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); -+ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, -+ unsigned int dummy) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, -+ struct nameidata *n) -+#else -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) -+#endif -+{ -+ struct yaffs_obj *obj; -+ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ -+ -+ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_lock(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", -+ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); -+ -+ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); -+ -+ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ -+ -+ /* Can't hold gross lock when calling yaffs_get_inode() */ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_lookup found %d", obj->obj_id); -+ -+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); -+ -+ } -+ -+/* added NCB for 2.5/6 compatability - forces add even if inode is -+ * NULL which creates dentry hash */ -+ d_add(dentry, inode); -+ -+ return NULL; -+} -+ -+/* -+ * Create a link... -+ */ -+static int yaffs_link(struct dentry *old_dentry, struct inode *dir, -+ struct dentry *dentry) -+{ -+ struct inode *inode = old_dentry->d_inode; -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_obj *link = NULL; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); -+ -+ obj = yaffs_inode_to_obj(inode); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ -+ link = -+ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, -+ obj); -+ -+ if (link) { -+ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); -+ d_instantiate(dentry, old_dentry->d_inode); -+ atomic_inc(&old_dentry->d_inode->i_count); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_link link count %d i_count %d", -+ old_dentry->d_inode->i_nlink, -+ atomic_read(&old_dentry->d_inode->i_count)); -+ } -+ -+ yaffs_gross_unlock(dev); -+ -+ if (link) { -+ update_dir_time(dir); -+ return 0; -+ } -+ -+ return -EPERM; -+} -+ -+static int yaffs_symlink(struct inode *dir, struct dentry *dentry, -+ const char *symname) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ uid_t uid = YCRED_FSUID(); -+ gid_t gid = -+ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); -+ -+ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > -+ YAFFS_MAX_NAME_LENGTH) -+ return -ENAMETOOLONG; -+ -+ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > -+ YAFFS_MAX_ALIAS_LENGTH) -+ return -ENAMETOOLONG; -+ -+ dev = yaffs_inode_to_obj(dir)->my_dev; -+ yaffs_gross_lock(dev); -+ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, -+ S_IFLNK | S_IRWXUGO, uid, gid, symname); -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ struct inode *inode; -+ -+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); -+ d_instantiate(dentry, inode); -+ update_dir_time(dir); -+ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); -+ return 0; -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); -+ } -+ -+ return -ENOMEM; -+} -+ -+/* -+ * The VFS layer already does all the dentry stuff for rename. -+ * -+ * NB: POSIX says you can rename an object over an old object of the same name -+ */ -+static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, -+ struct inode *new_dir, struct dentry *new_dentry) -+{ -+ struct yaffs_dev *dev; -+ int ret_val = YAFFS_FAIL; -+ struct yaffs_obj *target; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); -+ dev = yaffs_inode_to_obj(old_dir)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ /* Check if the target is an existing directory that is not empty. */ -+ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), -+ new_dentry->d_name.name); -+ -+ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && -+ !list_empty(&target->variant.dir_variant.children)) { -+ -+ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); -+ -+ ret_val = YAFFS_FAIL; -+ } else { -+ /* Now does unlinking internally using shadowing mechanism */ -+ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); -+ -+ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), -+ old_dentry->d_name.name, -+ yaffs_inode_to_obj(new_dir), -+ new_dentry->d_name.name); -+ } -+ yaffs_gross_unlock(dev); -+ -+ if (ret_val == YAFFS_OK) { -+ if (target) -+ inode_dec_link_count(new_dentry->d_inode); -+ -+ update_dir_time(old_dir); -+ if (old_dir != new_dir) -+ update_dir_time(new_dir); -+ return 0; -+ } else { -+ return -ENOTEMPTY; -+ } -+} -+ -+ -+ -+ -+static int yaffs_unlink(struct inode *dir, struct dentry *dentry) -+{ -+ int ret_val; -+ -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", -+ (int)(dir->i_ino), dentry->d_name.name); -+ obj = yaffs_inode_to_obj(dir); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ ret_val = yaffs_unlinker(obj, dentry->d_name.name); -+ -+ if (ret_val == YAFFS_OK) { -+ inode_dec_link_count(dentry->d_inode); -+ dir->i_version++; -+ yaffs_gross_unlock(dev); -+ update_dir_time(dir); -+ return 0; -+ } -+ yaffs_gross_unlock(dev); -+ return -ENOTEMPTY; -+} -+ -+ -+ -+static const struct inode_operations yaffs_dir_inode_operations = { -+ .create = yaffs_create, -+ .lookup = yaffs_lookup, -+ .link = yaffs_link, -+ .unlink = yaffs_unlink, -+ .symlink = yaffs_symlink, -+ .mkdir = yaffs_mkdir, -+ .rmdir = yaffs_unlink, -+ .mknod = yaffs_mknod, -+ .rename = yaffs_rename, -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+/*-----------------------------------------------------------------*/ -+/* Directory search context allows us to unlock access to yaffs during -+ * filldir without causing problems with the directory being modified. -+ * This is similar to the tried and tested mechanism used in yaffs direct. -+ * -+ * A search context iterates along a doubly linked list of siblings in the -+ * directory. If the iterating object is deleted then this would corrupt -+ * the list iteration, likely causing a crash. The search context avoids -+ * this by using the remove_obj_fn to move the search context to the -+ * next object before the object is deleted. -+ * -+ * Many readdirs (and thus seach conexts) may be alive simulateously so -+ * each struct yaffs_dev has a list of these. -+ * -+ * A seach context lives for the duration of a readdir. -+ * -+ * All these functions must be called while yaffs is locked. -+ */ -+ -+struct yaffs_search_context { -+ struct yaffs_dev *dev; -+ struct yaffs_obj *dir_obj; -+ struct yaffs_obj *next_return; -+ struct list_head others; -+}; -+ -+/* -+ * yaffs_new_search() creates a new search context, initialises it and -+ * adds it to the device's search context list. -+ * -+ * Called at start of readdir. -+ */ -+static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) -+{ -+ struct yaffs_dev *dev = dir->my_dev; -+ struct yaffs_search_context *sc = -+ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); -+ if (sc) { -+ sc->dir_obj = dir; -+ sc->dev = dev; -+ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) -+ sc->next_return = NULL; -+ else -+ sc->next_return = -+ list_entry(dir->variant.dir_variant.children.next, -+ struct yaffs_obj, siblings); -+ INIT_LIST_HEAD(&sc->others); -+ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); -+ } -+ return sc; -+} -+ -+/* -+ * yaffs_search_end() disposes of a search context and cleans up. -+ */ -+static void yaffs_search_end(struct yaffs_search_context *sc) -+{ -+ if (sc) { -+ list_del(&sc->others); -+ kfree(sc); -+ } -+} -+ -+/* -+ * yaffs_search_advance() moves a search context to the next object. -+ * Called when the search iterates or when an object removal causes -+ * the search context to be moved to the next object. -+ */ -+static void yaffs_search_advance(struct yaffs_search_context *sc) -+{ -+ if (!sc) -+ return; -+ -+ if (sc->next_return == NULL || -+ list_empty(&sc->dir_obj->variant.dir_variant.children)) -+ sc->next_return = NULL; -+ else { -+ struct list_head *next = sc->next_return->siblings.next; -+ -+ if (next == &sc->dir_obj->variant.dir_variant.children) -+ sc->next_return = NULL; /* end of list */ -+ else -+ sc->next_return = -+ list_entry(next, struct yaffs_obj, siblings); -+ } -+} -+ -+/* -+ * yaffs_remove_obj_callback() is called when an object is unlinked. -+ * We check open search contexts and advance any which are currently -+ * on the object being iterated. -+ */ -+static void yaffs_remove_obj_callback(struct yaffs_obj *obj) -+{ -+ -+ struct list_head *i; -+ struct yaffs_search_context *sc; -+ struct list_head *search_contexts = -+ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); -+ -+ /* Iterate through the directory search contexts. -+ * If any are currently on the object being removed, then advance -+ * the search context to the next object to prevent a hanging pointer. -+ */ -+ list_for_each(i, search_contexts) { -+ sc = list_entry(i, struct yaffs_search_context, others); -+ if (sc->next_return == obj) -+ yaffs_search_advance(sc); -+ } -+ -+} -+ -+ -+/*-----------------------------------------------------------------*/ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) -+static int yaffs_readdir(struct file *file, struct dir_context *ctx) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ struct yaffs_search_context *sc; -+ struct inode *inode = file->f_dentry->d_inode; -+ unsigned long offset, curoffs; -+ struct yaffs_obj *l; -+ int ret_val = 0; -+ -+ char name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ obj = yaffs_dentry_to_obj(file->f_dentry); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_dev_to_lc(dev)->readdir_process = current; -+ -+ offset = ctx->pos; -+ -+ sc = yaffs_new_search(obj); -+ if (!sc) { -+ ret_val = -ENOMEM; -+ goto out; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: starting at %d", (int)offset); -+ -+ if (offset == 0) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry . ino %d", -+ (int)inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (!dir_emit_dot(file, ctx)) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ ctx->pos++; -+ } -+ if (offset == 1) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry .. ino %d", -+ (int)file->f_dentry->d_parent->d_inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (!dir_emit_dotdot(file, ctx)) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ ctx->pos++; -+ } -+ -+ curoffs = 1; -+ -+ /* If the directory has changed since the open or last call to -+ readdir, rewind to after the 2 canned entries. */ -+ if (file->f_version != inode->i_version) { -+ offset = 2; -+ ctx->pos = offset; -+ file->f_version = inode->i_version; -+ } -+ -+ while (sc->next_return) { -+ curoffs++; -+ l = sc->next_return; -+ if (curoffs >= offset) { -+ int this_inode = yaffs_get_obj_inode(l); -+ int this_type = yaffs_get_obj_type(l); -+ -+ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: %s inode %d", -+ name, yaffs_get_obj_inode(l)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (!dir_emit(ctx, name, strlen(name), -+ this_inode, this_type) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ -+ yaffs_gross_lock(dev); -+ -+ offset++; -+ ctx->pos++; -+ } -+ yaffs_search_advance(sc); -+ } -+ -+out: -+ yaffs_search_end(sc); -+ yaffs_dev_to_lc(dev)->readdir_process = NULL; -+ yaffs_gross_unlock(dev); -+ -+ return ret_val; -+} -+#else -+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ struct yaffs_search_context *sc; -+ struct inode *inode = f->f_dentry->d_inode; -+ unsigned long offset, curoffs; -+ struct yaffs_obj *l; -+ int ret_val = 0; -+ -+ char name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_dev_to_lc(dev)->readdir_process = current; -+ -+ offset = f->f_pos; -+ -+ sc = yaffs_new_search(obj); -+ if (!sc) { -+ ret_val = -ENOMEM; -+ goto out; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: starting at %d", (int)offset); -+ -+ if (offset == 0) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry . ino %d", -+ (int)inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ f->f_pos++; -+ } -+ if (offset == 1) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry .. ino %d", -+ (int)f->f_dentry->d_parent->d_inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (filldir(dirent, "..", 2, offset, -+ f->f_dentry->d_parent->d_inode->i_ino, -+ DT_DIR) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ f->f_pos++; -+ } -+ -+ curoffs = 1; -+ -+ /* If the directory has changed since the open or last call to -+ readdir, rewind to after the 2 canned entries. */ -+ if (f->f_version != inode->i_version) { -+ offset = 2; -+ f->f_pos = offset; -+ f->f_version = inode->i_version; -+ } -+ -+ while (sc->next_return) { -+ curoffs++; -+ l = sc->next_return; -+ if (curoffs >= offset) { -+ int this_inode = yaffs_get_obj_inode(l); -+ int this_type = yaffs_get_obj_type(l); -+ -+ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: %s inode %d", -+ name, yaffs_get_obj_inode(l)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (filldir(dirent, -+ name, -+ strlen(name), -+ offset, this_inode, this_type) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ -+ yaffs_gross_lock(dev); -+ -+ offset++; -+ f->f_pos++; -+ } -+ yaffs_search_advance(sc); -+ } -+ -+out: -+ yaffs_search_end(sc); -+ yaffs_dev_to_lc(dev)->readdir_process = NULL; -+ yaffs_gross_unlock(dev); -+ -+ return ret_val; -+} -+#endif -+ -+static const struct file_operations yaffs_dir_operations = { -+ .read = generic_read_dir, -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) -+ .iterate = yaffs_readdir, -+#else -+ .readdir = yaffs_readdir, -+#endif -+ .fsync = yaffs_sync_object, -+ .llseek = generic_file_llseek, -+}; -+ -+static void yaffs_fill_inode_from_obj(struct inode *inode, -+ struct yaffs_obj *obj) -+{ -+ if (inode && obj) { -+ -+ /* Check mode against the variant type and attempt to repair if broken. */ -+ u32 mode = obj->yst_mode; -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (!S_ISREG(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFREG; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ if (!S_ISLNK(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFLNK; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ if (!S_ISDIR(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFDIR; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ default: -+ /* TODO? */ -+ break; -+ } -+ -+ inode->i_flags |= S_NOATIME; -+ -+ inode->i_ino = obj->obj_id; -+ inode->i_mode = obj->yst_mode; -+ i_uid_write(inode, obj->yst_uid); -+ i_gid_write(inode, obj->yst_gid); -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+ inode->i_blksize = inode->i_sb->s_blocksize; -+#endif -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ -+ inode->i_rdev = old_decode_dev(obj->yst_rdev); -+ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); -+ inode->i_atime.tv_nsec = 0; -+ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; -+ inode->i_mtime.tv_nsec = 0; -+ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; -+ inode->i_ctime.tv_nsec = 0; -+#else -+ inode->i_rdev = obj->yst_rdev; -+ inode->i_atime = obj->yst_atime; -+ inode->i_mtime = obj->yst_mtime; -+ inode->i_ctime = obj->yst_ctime; -+#endif -+ inode->i_size = yaffs_get_obj_length(obj); -+ inode->i_blocks = (inode->i_size + 511) >> 9; -+ -+ set_nlink(inode, yaffs_get_obj_link_count(obj)); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", -+ inode->i_mode, i_uid_read(inode), i_gid_read(inode), -+ inode->i_size, atomic_read(&inode->i_count)); -+ -+ switch (obj->yst_mode & S_IFMT) { -+ default: /* fifo, device or socket */ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ init_special_inode(inode, obj->yst_mode, -+ old_decode_dev(obj->yst_rdev)); -+#else -+ init_special_inode(inode, obj->yst_mode, -+ (dev_t) (obj->yst_rdev)); -+#endif -+ break; -+ case S_IFREG: /* file */ -+ inode->i_op = &yaffs_file_inode_operations; -+ inode->i_fop = &yaffs_file_operations; -+ inode->i_mapping->a_ops = -+ &yaffs_file_address_operations; -+ break; -+ case S_IFDIR: /* directory */ -+ inode->i_op = &yaffs_dir_inode_operations; -+ inode->i_fop = &yaffs_dir_operations; -+ break; -+ case S_IFLNK: /* symlink */ -+ inode->i_op = &yaffs_symlink_inode_operations; -+ break; -+ } -+ -+ yaffs_inode_to_obj_lv(inode) = obj; -+ -+ obj->my_inode = inode; -+ -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_fill_inode invalid parameters"); -+ } -+ -+} -+ -+ -+ -+/* -+ * yaffs background thread functions . -+ * yaffs_bg_thread_fn() the thread function -+ * yaffs_bg_start() launches the background thread. -+ * yaffs_bg_stop() cleans up the background thread. -+ * -+ * NB: -+ * The thread should only run after the yaffs is initialised -+ * The thread should be stopped before yaffs is unmounted. -+ * The thread should not do any writing while the fs is in read only. -+ */ -+ -+static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) -+{ -+ unsigned erased_chunks = -+ dev->n_erased_blocks * dev->param.chunks_per_block; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ unsigned scattered = 0; /* Free chunks not in an erased block */ -+ -+ if (erased_chunks < dev->n_free_chunks) -+ scattered = (dev->n_free_chunks - erased_chunks); -+ -+ if (!context->bg_running) -+ return 0; -+ else if (scattered < (dev->param.chunks_per_block * 2)) -+ return 0; -+ else if (erased_chunks > dev->n_free_chunks / 2) -+ return 0; -+ else if (erased_chunks > dev->n_free_chunks / 4) -+ return 1; -+ else -+ return 2; -+} -+ -+#ifdef YAFFS_COMPILE_BACKGROUND -+ -+void yaffs_background_waker(unsigned long data) -+{ -+ wake_up_process((struct task_struct *)data); -+} -+ -+static int yaffs_bg_thread_fn(void *data) -+{ -+ struct yaffs_dev *dev = (struct yaffs_dev *)data; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ unsigned long now = jiffies; -+ unsigned long next_dir_update = now; -+ unsigned long next_gc = now; -+ unsigned long expires; -+ unsigned int urgency; -+ -+ int gc_result; -+ struct timer_list timer; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "yaffs_background starting for dev %p", (void *)dev); -+ -+#ifdef YAFFS_COMPILE_FREEZER -+ set_freezable(); -+#endif -+ while (context->bg_running) { -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); -+ -+ if (kthread_should_stop()) -+ break; -+ -+#ifdef YAFFS_COMPILE_FREEZER -+ if (try_to_freeze()) -+ continue; -+#endif -+ yaffs_gross_lock(dev); -+ -+ now = jiffies; -+ -+ if (time_after(now, next_dir_update) && yaffs_bg_enable) { -+ yaffs_update_dirty_dirs(dev); -+ next_dir_update = now + HZ; -+ } -+ -+ if (time_after(now, next_gc) && yaffs_bg_enable) { -+ if (!dev->is_checkpointed) { -+ urgency = yaffs_bg_gc_urgency(dev); -+ gc_result = yaffs_bg_gc(dev, urgency); -+ if (urgency > 1) -+ next_gc = now + HZ / 20 + 1; -+ else if (urgency > 0) -+ next_gc = now + HZ / 10 + 1; -+ else -+ next_gc = now + HZ * 2; -+ } else { -+ /* -+ * gc not running so set to next_dir_update -+ * to cut down on wake ups -+ */ -+ next_gc = next_dir_update; -+ } -+ } -+ yaffs_gross_unlock(dev); -+#if 1 -+ expires = next_dir_update; -+ if (time_before(next_gc, expires)) -+ expires = next_gc; -+ if (time_before(expires, now)) -+ expires = now + HZ; -+ -+ Y_INIT_TIMER(&timer); -+ timer.expires = expires + 1; -+ timer.data = (unsigned long)current; -+ timer.function = yaffs_background_waker; -+ -+ set_current_state(TASK_INTERRUPTIBLE); -+ add_timer(&timer); -+ schedule(); -+ del_timer_sync(&timer); -+#else -+ msleep(10); -+#endif -+ } -+ -+ return 0; -+} -+ -+static int yaffs_bg_start(struct yaffs_dev *dev) -+{ -+ int retval = 0; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ -+ if (dev->read_only) -+ return -1; -+ -+ context->bg_running = 1; -+ -+ context->bg_thread = kthread_run(yaffs_bg_thread_fn, -+ (void *)dev, "yaffs-bg-%d", -+ context->mount_id); -+ -+ if (IS_ERR(context->bg_thread)) { -+ retval = PTR_ERR(context->bg_thread); -+ context->bg_thread = NULL; -+ context->bg_running = 0; -+ } -+ return retval; -+} -+ -+static void yaffs_bg_stop(struct yaffs_dev *dev) -+{ -+ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); -+ -+ ctxt->bg_running = 0; -+ -+ if (ctxt->bg_thread) { -+ kthread_stop(ctxt->bg_thread); -+ ctxt->bg_thread = NULL; -+ } -+} -+#else -+static int yaffs_bg_thread_fn(void *data) -+{ -+ return 0; -+} -+ -+static int yaffs_bg_start(struct yaffs_dev *dev) -+{ -+ return 0; -+} -+ -+static void yaffs_bg_stop(struct yaffs_dev *dev) -+{ -+} -+#endif -+ -+ -+static void yaffs_flush_inodes(struct super_block *sb) -+{ -+ struct inode *iptr; -+ struct yaffs_obj *obj; -+ -+ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { -+ obj = yaffs_inode_to_obj(iptr); -+ if (obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "flushing obj %d", -+ obj->obj_id); -+ yaffs_flush_file(obj, 1, 0); -+ } -+ } -+} -+ -+static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ if (!dev) -+ return; -+ -+ yaffs_flush_inodes(sb); -+ yaffs_update_dirty_dirs(dev); -+ yaffs_flush_whole_cache(dev); -+ if (do_checkpoint) -+ yaffs_checkpoint_save(dev); -+} -+ -+static LIST_HEAD(yaffs_context_list); -+struct mutex yaffs_context_lock; -+ -+static void yaffs_put_super(struct super_block *sb) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, -+ "yaffs_put_super"); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, -+ "Shutting down yaffs background thread"); -+ yaffs_bg_stop(dev); -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, -+ "yaffs background thread shut down"); -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_flush_super(sb, 1); -+ -+ yaffs_deinitialise(dev); -+ -+ yaffs_gross_unlock(dev); -+ -+ mutex_lock(&yaffs_context_lock); -+ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); -+ mutex_unlock(&yaffs_context_lock); -+ -+ if (yaffs_dev_to_lc(dev)->spare_buffer) { -+ kfree(yaffs_dev_to_lc(dev)->spare_buffer); -+ yaffs_dev_to_lc(dev)->spare_buffer = NULL; -+ } -+ -+ kfree(dev); -+ -+ yaffs_put_mtd_device(mtd); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, -+ "yaffs_put_super done"); -+} -+ -+ -+static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) -+{ -+ return yaffs_gc_control; -+} -+ -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+ -+static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, -+ uint32_t generation) -+{ -+ return Y_IGET(sb, ino); -+} -+ -+static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, -+ struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, -+ yaffs2_nfs_get_inode); -+} -+ -+static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, -+ struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ return generic_fh_to_parent(sb, fid, fh_len, fh_type, -+ yaffs2_nfs_get_inode); -+} -+ -+struct dentry *yaffs2_get_parent(struct dentry *dentry) -+{ -+ -+ struct super_block *sb = dentry->d_inode->i_sb; -+ struct dentry *parent = ERR_PTR(-ENOENT); -+ struct inode *inode; -+ unsigned long parent_ino; -+ struct yaffs_obj *d_obj; -+ struct yaffs_obj *parent_obj; -+ -+ d_obj = yaffs_inode_to_obj(dentry->d_inode); -+ -+ if (d_obj) { -+ parent_obj = d_obj->parent; -+ if (parent_obj) { -+ parent_ino = yaffs_get_obj_inode(parent_obj); -+ inode = Y_IGET(sb, parent_ino); -+ -+ if (IS_ERR(inode)) { -+ parent = ERR_CAST(inode); -+ } else { -+ parent = d_obtain_alias(inode); -+ if (!IS_ERR(parent)) { -+ parent = ERR_PTR(-ENOMEM); -+ iput(inode); -+ } -+ } -+ } -+ } -+ -+ return parent; -+} -+ -+/* Just declare a zero structure as a NULL value implies -+ * using the default functions of exportfs. -+ */ -+ -+static struct export_operations yaffs_export_ops = { -+ .fh_to_dentry = yaffs2_fh_to_dentry, -+ .fh_to_parent = yaffs2_fh_to_parent, -+ .get_parent = yaffs2_get_parent, -+}; -+ -+#endif -+ -+static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) -+{ -+ /* Clear the association between the inode and -+ * the struct yaffs_obj. -+ */ -+ obj->my_inode = NULL; -+ yaffs_inode_to_obj_lv(inode) = NULL; -+ -+ /* If the object freeing was deferred, then the real -+ * free happens now. -+ * This should fix the inode inconsistency problem. -+ */ -+ yaffs_handle_defered_free(obj); -+} -+ -+#ifdef YAFFS_HAS_EVICT_INODE -+/* yaffs_evict_inode combines into one operation what was previously done in -+ * yaffs_clear_inode() and yaffs_delete_inode() -+ * -+ */ -+static void yaffs_evict_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ int deleteme = 0; -+ -+ obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_evict_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (!inode->i_nlink && !is_bad_inode(inode)) -+ deleteme = 1; -+ truncate_inode_pages(&inode->i_data, 0); -+ Y_CLEAR_INODE(inode); -+ -+ if (deleteme && obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_del_obj(obj); -+ yaffs_gross_unlock(dev); -+ } -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_unstitch_obj(inode, obj); -+ yaffs_gross_unlock(dev); -+ } -+} -+#else -+ -+/* clear is called to tell the fs to release any per-inode data it holds. -+ * The object might still exist on disk and is just being thrown out of the cache -+ * or else the object has actually been deleted and we're being called via -+ * the chain -+ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() -+ */ -+ -+static void yaffs_clear_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_clear_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_unstitch_obj(inode, obj); -+ yaffs_gross_unlock(dev); -+ } -+ -+} -+ -+/* delete is called when the link count is zero and the inode -+ * is put (ie. nobody wants to know about it anymore, time to -+ * delete the file). -+ * NB Must call clear_inode() -+ */ -+static void yaffs_delete_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_delete_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_del_obj(obj); -+ yaffs_gross_unlock(dev); -+ } -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) -+ truncate_inode_pages(&inode->i_data, 0); -+#endif -+ clear_inode(inode); -+} -+#endif -+ -+ -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ struct super_block *sb = dentry->d_sb; -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+#else -+static int yaffs_statfs(struct super_block *sb, struct statfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+#endif -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); -+ -+ yaffs_gross_lock(dev); -+ -+ buf->f_type = YAFFS_MAGIC; -+ buf->f_bsize = sb->s_blocksize; -+ buf->f_namelen = 255; -+ -+ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { -+ /* Do this if chunk size is not a power of 2 */ -+ -+ uint64_t bytes_in_dev; -+ uint64_t bytes_free; -+ -+ bytes_in_dev = -+ ((uint64_t) -+ ((dev->param.end_block - dev->param.start_block + -+ 1))) * ((uint64_t) (dev->param.chunks_per_block * -+ dev->data_bytes_per_chunk)); -+ -+ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ -+ buf->f_blocks = bytes_in_dev; -+ -+ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * -+ ((uint64_t) (dev->data_bytes_per_chunk)); -+ -+ do_div(bytes_free, sb->s_blocksize); -+ -+ buf->f_bfree = bytes_free; -+ -+ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { -+ -+ buf->f_blocks = -+ (dev->param.end_block - dev->param.start_block + 1) * -+ dev->param.chunks_per_block / -+ (sb->s_blocksize / dev->data_bytes_per_chunk); -+ buf->f_bfree = -+ yaffs_get_n_free_chunks(dev) / -+ (sb->s_blocksize / dev->data_bytes_per_chunk); -+ } else { -+ buf->f_blocks = -+ (dev->param.end_block - dev->param.start_block + 1) * -+ dev->param.chunks_per_block * -+ (dev->data_bytes_per_chunk / sb->s_blocksize); -+ -+ buf->f_bfree = -+ yaffs_get_n_free_chunks(dev) * -+ (dev->data_bytes_per_chunk / sb->s_blocksize); -+ } -+ -+ buf->f_files = 0; -+ buf->f_ffree = 0; -+ buf->f_bavail = buf->f_bfree; -+ -+ yaffs_gross_unlock(dev); -+ return 0; -+} -+ -+ -+ -+static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) -+{ -+ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); -+ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); -+ int do_checkpoint; -+ int dirty = yaffs_check_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, -+ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", -+ gc_urgent, -+ dirty ? "dirty" : "clean", -+ request_checkpoint ? "checkpoint requested" : "no checkpoint", -+ oneshot_checkpoint ? " one-shot" : ""); -+ -+ yaffs_gross_lock(dev); -+ do_checkpoint = ((request_checkpoint && !gc_urgent) || -+ oneshot_checkpoint) && !dev->is_checkpointed; -+ -+ if (dirty || do_checkpoint) { -+ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); -+ yaffs_clear_super_dirty(dev); -+ if (oneshot_checkpoint) -+ yaffs_auto_checkpoint &= ~4; -+ } -+ yaffs_gross_unlock(dev); -+ -+ return 0; -+} -+ -+ -+#ifdef YAFFS_HAS_WRITE_SUPER -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static void yaffs_write_super(struct super_block *sb) -+#else -+static int yaffs_write_super(struct super_block *sb) -+#endif -+{ -+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, -+ "yaffs_write_super %s", -+ request_checkpoint ? " checkpt" : ""); -+ -+ yaffs_do_sync_fs(sb, request_checkpoint); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) -+ return 0; -+#endif -+} -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_sync_fs(struct super_block *sb, int wait) -+#else -+static int yaffs_sync_fs(struct super_block *sb) -+#endif -+{ -+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, -+ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); -+ -+ yaffs_do_sync_fs(sb, request_checkpoint); -+ -+ return 0; -+} -+ -+ -+ -+static const struct super_operations yaffs_super_ops = { -+ .statfs = yaffs_statfs, -+ -+#ifndef YAFFS_USE_OWN_IGET -+ .read_inode = yaffs_read_inode, -+#endif -+#ifdef YAFFS_HAS_PUT_INODE -+ .put_inode = yaffs_put_inode, -+#endif -+ .put_super = yaffs_put_super, -+#ifdef YAFFS_HAS_EVICT_INODE -+ .evict_inode = yaffs_evict_inode, -+#else -+ .delete_inode = yaffs_delete_inode, -+ .clear_inode = yaffs_clear_inode, -+#endif -+ .sync_fs = yaffs_sync_fs, -+#ifdef YAFFS_HAS_WRITE_SUPER -+ .write_super = yaffs_write_super, -+#endif -+}; -+ -+struct yaffs_options { -+ int inband_tags; -+ int skip_checkpoint_read; -+ int skip_checkpoint_write; -+ int no_cache; -+ int tags_ecc_on; -+ int tags_ecc_overridden; -+ int lazy_loading_enabled; -+ int lazy_loading_overridden; -+ int empty_lost_and_found; -+ int empty_lost_and_found_overridden; -+ int disable_summary; -+}; -+ -+#define MAX_OPT_LEN 30 -+static int yaffs_parse_options(struct yaffs_options *options, -+ const char *options_str) -+{ -+ char cur_opt[MAX_OPT_LEN + 1]; -+ int p; -+ int error = 0; -+ -+ /* Parse through the options which is a comma seperated list */ -+ -+ while (options_str && *options_str && !error) { -+ memset(cur_opt, 0, MAX_OPT_LEN + 1); -+ p = 0; -+ -+ while (*options_str == ',') -+ options_str++; -+ -+ while (*options_str && *options_str != ',') { -+ if (p < MAX_OPT_LEN) { -+ cur_opt[p] = *options_str; -+ p++; -+ } -+ options_str++; -+ } -+ -+ if (!strcmp(cur_opt, "inband-tags")) { -+ options->inband_tags = 1; -+ } else if (!strcmp(cur_opt, "tags-ecc-off")) { -+ options->tags_ecc_on = 0; -+ options->tags_ecc_overridden = 1; -+ } else if (!strcmp(cur_opt, "tags-ecc-on")) { -+ options->tags_ecc_on = 1; -+ options->tags_ecc_overridden = 1; -+ } else if (!strcmp(cur_opt, "lazy-loading-off")) { -+ options->lazy_loading_enabled = 0; -+ options->lazy_loading_overridden = 1; -+ } else if (!strcmp(cur_opt, "lazy-loading-on")) { -+ options->lazy_loading_enabled = 1; -+ options->lazy_loading_overridden = 1; -+ } else if (!strcmp(cur_opt, "disable-summary")) { -+ options->disable_summary = 1; -+ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { -+ options->empty_lost_and_found = 0; -+ options->empty_lost_and_found_overridden = 1; -+ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { -+ options->empty_lost_and_found = 1; -+ options->empty_lost_and_found_overridden = 1; -+ } else if (!strcmp(cur_opt, "no-cache")) { -+ options->no_cache = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { -+ options->skip_checkpoint_read = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { -+ options->skip_checkpoint_write = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint")) { -+ options->skip_checkpoint_read = 1; -+ options->skip_checkpoint_write = 1; -+ } else { -+ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", -+ cur_opt); -+ error = 1; -+ } -+ } -+ -+ return error; -+} -+ -+ -+static struct dentry *yaffs_make_root(struct inode *inode) -+{ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) -+ struct dentry *root = d_alloc_root(inode); -+ -+ if (!root) -+ iput(inode); -+ -+ return root; -+#else -+ return d_make_root(inode); -+#endif -+} -+ -+ -+ -+ -+static struct super_block *yaffs_internal_read_super(int yaffs_version, -+ struct super_block *sb, -+ void *data, int silent) -+{ -+ int n_blocks; -+ struct inode *inode = NULL; -+ struct dentry *root; -+ struct yaffs_dev *dev = 0; -+ char devname_buf[BDEVNAME_SIZE + 1]; -+ struct mtd_info *mtd; -+ int err; -+ char *data_str = (char *)data; -+ struct yaffs_linux_context *context = NULL; -+ struct yaffs_param *param; -+ -+ int read_only = 0; -+ int inband_tags = 0; -+ -+ struct yaffs_options options; -+ -+ unsigned mount_id; -+ int found; -+ struct yaffs_linux_context *context_iterator; -+ struct list_head *l; -+ -+ if (!sb) { -+ printk(KERN_INFO "yaffs: sb is NULL\n"); -+ return NULL; -+ } -+ -+ sb->s_magic = YAFFS_MAGIC; -+ sb->s_op = &yaffs_super_ops; -+ sb->s_flags |= MS_NOATIME; -+ -+ read_only = ((sb->s_flags & MS_RDONLY) != 0); -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+ sb->s_export_op = &yaffs_export_ops; -+#endif -+ -+ if (!sb->s_dev) -+ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); -+ else if (!yaffs_devname(sb, devname_buf)) -+ printk(KERN_INFO "yaffs: devname is NULL\n"); -+ else -+ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", -+ sb->s_dev, -+ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); -+ -+ if (!data_str) -+ data_str = ""; -+ -+ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); -+ -+ memset(&options, 0, sizeof(options)); -+ -+ if (yaffs_parse_options(&options, data_str)) { -+ /* Option parsing failed */ -+ return NULL; -+ } -+ -+ sb->s_blocksize = PAGE_CACHE_SIZE; -+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: Using yaffs%d", yaffs_version); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", -+ MAJOR(sb->s_dev), MINOR(sb->s_dev), -+ yaffs_devname(sb, devname_buf)); -+ -+ /* Get the device */ -+ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); -+ if (IS_ERR(mtd)) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: MTD device %u either not valid or unavailable", -+ MINOR(sb->s_dev)); -+ return NULL; -+ } -+ -+ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); -+ yaffs_version = 2; -+ } -+ -+ /* Added NCB 26/5/2006 for completeness */ -+ if (yaffs_version == 2 && !options.inband_tags -+ && WRITE_SIZE(mtd) == 512) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); -+ yaffs_version = 1; -+ } -+ -+ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || -+ options.inband_tags) -+ inband_tags = 1; -+ -+ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) -+ return NULL; -+ -+ /* OK, so if we got here, we have an MTD that's NAND and looks -+ * like it has the right capabilities -+ * Set the struct yaffs_dev up for mtd -+ */ -+ -+ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { -+ read_only = 1; -+ printk(KERN_INFO -+ "yaffs: mtd is read only, setting superblock read only\n" -+ ); -+ sb->s_flags |= MS_RDONLY; -+ } -+ -+ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); -+ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); -+ -+ if (!dev || !context) { -+ kfree(dev); -+ kfree(context); -+ dev = NULL; -+ context = NULL; -+ -+ /* Deep shit could not allocate device structure */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." -+ ); -+ return NULL; -+ } -+ memset(dev, 0, sizeof(struct yaffs_dev)); -+ param = &(dev->param); -+ -+ memset(context, 0, sizeof(struct yaffs_linux_context)); -+ dev->os_context = context; -+ INIT_LIST_HEAD(&(context->context_list)); -+ context->dev = dev; -+ context->super = sb; -+ -+ dev->read_only = read_only; -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ sb->s_fs_info = dev; -+#else -+ sb->u.generic_sbp = dev; -+#endif -+ -+ -+ dev->driver_context = mtd; -+ param->name = mtd->name; -+ -+ /* Set up the memory size parameters.... */ -+ -+ -+ param->n_reserved_blocks = 5; -+ param->n_caches = (options.no_cache) ? 0 : 10; -+ param->inband_tags = inband_tags; -+ -+ param->enable_xattr = 1; -+ if (options.lazy_loading_overridden) -+ param->disable_lazy_load = !options.lazy_loading_enabled; -+ -+ param->defered_dir_update = 1; -+ -+ if (options.tags_ecc_overridden) -+ param->no_tags_ecc = !options.tags_ecc_on; -+ -+ param->empty_lost_n_found = 1; -+ param->refresh_period = 500; -+ param->disable_summary = options.disable_summary; -+ -+ -+#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING -+ param->disable_bad_block_marking = 1; -+#endif -+ if (options.empty_lost_and_found_overridden) -+ param->empty_lost_n_found = options.empty_lost_and_found; -+ -+ /* ... and the functions. */ -+ if (yaffs_version == 2) { -+ param->is_yaffs2 = 1; -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+ param->total_bytes_per_chunk = mtd->writesize; -+ param->chunks_per_block = mtd->erasesize / mtd->writesize; -+#else -+ param->total_bytes_per_chunk = mtd->oobblock; -+ param->chunks_per_block = mtd->erasesize / mtd->oobblock; -+#endif -+ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); -+ -+ param->start_block = 0; -+ param->end_block = n_blocks - 1; -+ } else { -+ param->is_yaffs2 = 0; -+ n_blocks = YCALCBLOCKS(mtd->size, -+ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); -+ -+ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; -+ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; -+ } -+ -+ param->start_block = 0; -+ param->end_block = n_blocks - 1; -+ -+ yaffs_mtd_drv_install(dev); -+ -+ param->sb_dirty_fn = yaffs_set_super_dirty; -+ param->gc_control_fn = yaffs_gc_control_callback; -+ -+ yaffs_dev_to_lc(dev)->super = sb; -+ -+ param->use_nand_ecc = 1; -+ -+ param->skip_checkpt_rd = options.skip_checkpoint_read; -+ param->skip_checkpt_wr = options.skip_checkpoint_write; -+ -+ mutex_lock(&yaffs_context_lock); -+ /* Get a mount id */ -+ found = 0; -+ for (mount_id = 0; !found; mount_id++) { -+ found = 1; -+ list_for_each(l, &yaffs_context_list) { -+ context_iterator = -+ list_entry(l, struct yaffs_linux_context, -+ context_list); -+ if (context_iterator->mount_id == mount_id) -+ found = 0; -+ } -+ } -+ context->mount_id = mount_id; -+ -+ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), -+ &yaffs_context_list); -+ mutex_unlock(&yaffs_context_lock); -+ -+ /* Directory search handling... */ -+ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); -+ param->remove_obj_fn = yaffs_remove_obj_callback; -+ -+ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); -+ -+ yaffs_gross_lock(dev); -+ -+ err = yaffs_guts_initialise(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: guts initialised %s", -+ (err == YAFFS_OK) ? "OK" : "FAILED"); -+ -+ if (err == YAFFS_OK) -+ yaffs_bg_start(dev); -+ -+ if (!context->bg_thread) -+ param->defered_dir_update = 0; -+ -+ sb->s_maxbytes = yaffs_max_file_size(dev); -+ -+ /* Release lock before yaffs_get_inode() */ -+ yaffs_gross_unlock(dev); -+ -+ /* Create root inode */ -+ if (err == YAFFS_OK) -+ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); -+ -+ if (!inode) -+ return NULL; -+ -+ inode->i_op = &yaffs_dir_inode_operations; -+ inode->i_fop = &yaffs_dir_operations; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); -+ -+ root = yaffs_make_root(inode); -+ -+ if (!root) -+ return NULL; -+ -+ sb->s_root = root; -+ if(!dev->is_checkpointed) -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs_read_super: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); -+ return sb; -+} -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data) -+{ -+ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); -+} -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data, struct vfsmount *mnt) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs_internal_read_super_mtd, mnt); -+} -+#else -+static struct super_block *yaffs_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs_internal_read_super_mtd); -+} -+#endif -+ -+static struct file_system_type yaffs_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "yaffs", -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+ .mount = yaffs_mount, -+#else -+ .get_sb = yaffs_read_super, -+#endif -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV, -+}; -+#else -+static struct super_block *yaffs_read_super(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(1, sb, data, silent); -+} -+ -+static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, -+ FS_REQUIRES_DEV); -+#endif -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data) -+{ -+ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); -+} -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs2_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, void *data, -+ struct vfsmount *mnt) -+{ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs2_internal_read_super_mtd, mnt); -+} -+#else -+static struct super_block *yaffs2_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs2_internal_read_super_mtd); -+} -+#endif -+ -+static struct file_system_type yaffs2_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "yaffs2", -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+ .mount = yaffs2_mount, -+#else -+ .get_sb = yaffs2_read_super, -+#endif -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV, -+}; -+#else -+static struct super_block *yaffs2_read_super(struct super_block *sb, -+ void *data, int silent) -+{ -+ return yaffs_internal_read_super(2, sb, data, silent); -+} -+ -+static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, -+ FS_REQUIRES_DEV); -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) -+static struct proc_dir_entry *my_proc_entry; -+ -+static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) -+{ -+ struct yaffs_param *param = &dev->param; -+ int bs[10]; -+ -+ yaffs_count_blocks_by_state(dev,bs); -+ -+ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); -+ buf += sprintf(buf, "end_block............ %d\n", param->end_block); -+ buf += sprintf(buf, "total_bytes_per_chunk %d\n", -+ param->total_bytes_per_chunk); -+ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); -+ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); -+ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); -+ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); -+ buf += sprintf(buf, "empty_lost_n_found... %d\n", -+ param->empty_lost_n_found); -+ buf += sprintf(buf, "disable_lazy_load.... %d\n", -+ param->disable_lazy_load); -+ buf += sprintf(buf, "disable_bad_block_mrk %d\n", -+ param->disable_bad_block_marking); -+ buf += sprintf(buf, "refresh_period....... %d\n", -+ param->refresh_period); -+ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); -+ buf += sprintf(buf, "n_reserved_blocks.... %d\n", -+ param->n_reserved_blocks); -+ buf += sprintf(buf, "always_check_erased.. %d\n", -+ param->always_check_erased); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "block count by state\n"); -+ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", -+ bs[0], bs[1], bs[2], bs[3], bs[4]); -+ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", -+ bs[5], bs[6], bs[7], bs[8], bs[9]); -+ -+ return buf; -+} -+ -+static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) -+{ -+ buf += sprintf(buf, "max file size....... %lld\n", -+ (long long) yaffs_max_file_size(dev)); -+ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", -+ dev->data_bytes_per_chunk); -+ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); -+ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); -+ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); -+ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", -+ dev->blocks_in_checkpt); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); -+ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); -+ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); -+ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); -+ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); -+ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); -+ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); -+ buf += sprintf(buf, "passive_gc_count..... %u\n", -+ dev->passive_gc_count); -+ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", -+ dev->oldest_dirty_gc_count); -+ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); -+ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); -+ buf += sprintf(buf, "n_retried_writes..... %u\n", -+ dev->n_retried_writes); -+ buf += sprintf(buf, "n_retired_blocks..... %u\n", -+ dev->n_retired_blocks); -+ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); -+ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); -+ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", -+ dev->n_tags_ecc_fixed); -+ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", -+ dev->n_tags_ecc_unfixed); -+ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); -+ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); -+ buf += sprintf(buf, "n_unlinked_files..... %u\n", -+ dev->n_unlinked_files); -+ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); -+ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); -+ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); -+ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); -+ -+ return buf; -+} -+ -+static int yaffs_proc_read(char *page, -+ char **start, -+ off_t offset, int count, int *eof, void *data) -+{ -+ struct list_head *item; -+ char *buf = page; -+ int step = offset; -+ int n = 0; -+ -+ /* Get proc_file_read() to step 'offset' by one on each sucessive call. -+ * We use 'offset' (*ppos) to indicate where we are in dev_list. -+ * This also assumes the user has posted a read buffer large -+ * enough to hold the complete output; but that's life in /proc. -+ */ -+ -+ *(int *)start = 1; -+ -+ /* Print header first */ -+ if (step == 0) -+ buf += -+ sprintf(buf, -+ "Multi-version YAFFS built:" __DATE__ " " __TIME__ -+ "\n"); -+ else if (step == 1) -+ buf += sprintf(buf, "\n"); -+ else { -+ step -= 2; -+ -+ mutex_lock(&yaffs_context_lock); -+ -+ /* Locate and print the Nth entry. Order N-squared but N is small. */ -+ list_for_each(item, &yaffs_context_list) { -+ struct yaffs_linux_context *dc = -+ list_entry(item, struct yaffs_linux_context, -+ context_list); -+ struct yaffs_dev *dev = dc->dev; -+ -+ if (n < (step & ~1)) { -+ n += 2; -+ continue; -+ } -+ if ((step & 1) == 0) { -+ buf += -+ sprintf(buf, "\nDevice %d \"%s\"\n", n, -+ dev->param.name); -+ buf = yaffs_dump_dev_part0(buf, dev); -+ } else { -+ buf = yaffs_dump_dev_part1(buf, dev); -+ } -+ -+ break; -+ } -+ mutex_unlock(&yaffs_context_lock); -+ } -+ -+ return buf - page < count ? buf - page : count; -+} -+ -+/** -+ * Set the verbosity of the warnings and error messages. -+ * -+ * Note that the names can only be a..z or _ with the current code. -+ */ -+ -+static struct { -+ char *mask_name; -+ unsigned mask_bitfield; -+} mask_flags[] = { -+ {"allocate", YAFFS_TRACE_ALLOCATE}, -+ {"always", YAFFS_TRACE_ALWAYS}, -+ {"background", YAFFS_TRACE_BACKGROUND}, -+ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, -+ {"buffers", YAFFS_TRACE_BUFFERS}, -+ {"bug", YAFFS_TRACE_BUG}, -+ {"checkpt", YAFFS_TRACE_CHECKPOINT}, -+ {"deletion", YAFFS_TRACE_DELETION}, -+ {"erase", YAFFS_TRACE_ERASE}, -+ {"error", YAFFS_TRACE_ERROR}, -+ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, -+ {"gc", YAFFS_TRACE_GC}, -+ {"lock", YAFFS_TRACE_LOCK}, -+ {"mtd", YAFFS_TRACE_MTD}, -+ {"nandaccess", YAFFS_TRACE_NANDACCESS}, -+ {"os", YAFFS_TRACE_OS}, -+ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, -+ {"scan", YAFFS_TRACE_SCAN}, -+ {"mount", YAFFS_TRACE_MOUNT}, -+ {"tracing", YAFFS_TRACE_TRACING}, -+ {"sync", YAFFS_TRACE_SYNC}, -+ {"write", YAFFS_TRACE_WRITE}, -+ {"verify", YAFFS_TRACE_VERIFY}, -+ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, -+ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, -+ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, -+ {"all", 0xffffffff}, -+ {"none", 0}, -+ {NULL, 0}, -+}; -+ -+#define MAX_MASK_NAME_LENGTH 40 -+static int yaffs_proc_write_trace_options(struct file *file, const char *buf, -+ unsigned long count, void *data) -+{ -+ unsigned rg = 0, mask_bitfield; -+ char *end; -+ char *mask_name; -+ const char *x; -+ char substring[MAX_MASK_NAME_LENGTH + 1]; -+ int i; -+ int done = 0; -+ int add, len = 0; -+ int pos = 0; -+ -+ rg = yaffs_trace_mask; -+ -+ while (!done && (pos < count)) { -+ done = 1; -+ while ((pos < count) && isspace(buf[pos])) -+ pos++; -+ -+ switch (buf[pos]) { -+ case '+': -+ case '-': -+ case '=': -+ add = buf[pos]; -+ pos++; -+ break; -+ -+ default: -+ add = ' '; -+ break; -+ } -+ mask_name = NULL; -+ -+ mask_bitfield = simple_strtoul(buf + pos, &end, 0); -+ -+ if (end > buf + pos) { -+ mask_name = "numeral"; -+ len = end - (buf + pos); -+ pos += len; -+ done = 0; -+ } else { -+ for (x = buf + pos, i = 0; -+ (*x == '_' || (*x >= 'a' && *x <= 'z')) && -+ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) -+ substring[i] = *x; -+ substring[i] = '\0'; -+ -+ for (i = 0; mask_flags[i].mask_name != NULL; i++) { -+ if (strcmp(substring, mask_flags[i].mask_name) -+ == 0) { -+ mask_name = mask_flags[i].mask_name; -+ mask_bitfield = -+ mask_flags[i].mask_bitfield; -+ done = 0; -+ break; -+ } -+ } -+ } -+ -+ if (mask_name != NULL) { -+ done = 0; -+ switch (add) { -+ case '-': -+ rg &= ~mask_bitfield; -+ break; -+ case '+': -+ rg |= mask_bitfield; -+ break; -+ case '=': -+ rg = mask_bitfield; -+ break; -+ default: -+ rg |= mask_bitfield; -+ break; -+ } -+ } -+ } -+ -+ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; -+ -+ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); -+ -+ if (rg & YAFFS_TRACE_ALWAYS) { -+ for (i = 0; mask_flags[i].mask_name != NULL; i++) { -+ char flag; -+ flag = ((rg & mask_flags[i].mask_bitfield) == -+ mask_flags[i].mask_bitfield) ? '+' : '-'; -+ printk(KERN_DEBUG "%c%s\n", flag, -+ mask_flags[i].mask_name); -+ } -+ } -+ -+ return count; -+} -+ -+/* Debug strings are of the form: -+ * .bnnn print info on block n -+ * .cobjn,chunkn print nand chunk id for objn:chunkn -+ */ -+ -+static int yaffs_proc_debug_write(struct file *file, const char *buf, -+ unsigned long count, void *data) -+{ -+ -+ char str[100]; -+ char *p0; -+ char *p1; -+ long p1_val; -+ long p0_val; -+ char cmd; -+ struct list_head *item; -+ -+ memset(str, 0, sizeof(str)); -+ memcpy(str, buf, min(count, sizeof(str) -1)); -+ -+ cmd = str[1]; -+ -+ p0 = str + 2; -+ -+ p1 = p0; -+ -+ while (*p1 && *p1 != ',') { -+ p1++; -+ } -+ *p1 = '\0'; -+ p1++; -+ -+ p0_val = simple_strtol(p0, NULL, 0); -+ p1_val = simple_strtol(p1, NULL, 0); -+ -+ -+ mutex_lock(&yaffs_context_lock); -+ -+ /* Locate and print the Nth entry. Order N-squared but N is small. */ -+ list_for_each(item, &yaffs_context_list) { -+ struct yaffs_linux_context *dc = -+ list_entry(item, struct yaffs_linux_context, -+ context_list); -+ struct yaffs_dev *dev = dc->dev; -+ -+ if (cmd == 'b') { -+ struct yaffs_block_info *bi; -+ -+ bi = yaffs_get_block_info(dev,p0_val); -+ -+ if(bi) { -+ printk("Block %d: state %d, retire %d, use %d, seq %d\n", -+ (int)p0_val, bi->block_state, -+ bi->needs_retiring, bi->pages_in_use, -+ bi->seq_number); -+ } -+ } else if (cmd == 'c') { -+ struct yaffs_obj *obj; -+ int nand_chunk; -+ -+ obj = yaffs_find_by_number(dev, p0_val); -+ if (!obj) -+ printk("No obj %d\n", (int)p0_val); -+ else { -+ if(p1_val == 0) -+ nand_chunk = obj->hdr_chunk; -+ else -+ nand_chunk = -+ yaffs_find_chunk_in_file(obj, -+ p1_val, NULL); -+ printk("Nand chunk for %d:%d is %d\n", -+ (int)p0_val, (int)p1_val, nand_chunk); -+ } -+ } -+ } -+ -+ mutex_unlock(&yaffs_context_lock); -+ -+ return count; -+} -+ -+static int yaffs_proc_write(struct file *file, const char *buf, -+ unsigned long count, void *data) -+{ -+ if (buf[0] == '.') -+ return yaffs_proc_debug_write(file, buf, count, data); -+ return yaffs_proc_write_trace_options(file, buf, count, data); -+} -+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) */ -+ -+/* Stuff to handle installation of file systems */ -+struct file_system_to_install { -+ struct file_system_type *fst; -+ int installed; -+}; -+ -+static struct file_system_to_install fs_to_install[] = { -+ {&yaffs_fs_type, 0}, -+ {&yaffs2_fs_type, 0}, -+ {NULL, 0} -+}; -+ -+static int __init init_yaffs_fs(void) -+{ -+ int error = 0; -+ struct file_system_to_install *fsinst; -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs built " __DATE__ " " __TIME__ " Installing."); -+ -+ mutex_init(&yaffs_context_lock); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) -+ /* Install the proc_fs entries */ -+ my_proc_entry = create_proc_entry("yaffs", -+ S_IRUGO | S_IFREG, YPROC_ROOT); -+ -+ if (my_proc_entry) { -+ my_proc_entry->write_proc = yaffs_proc_write; -+ my_proc_entry->read_proc = yaffs_proc_read; -+ my_proc_entry->data = NULL; -+ } else { -+ return -ENOMEM; -+ } -+#endif -+ -+ /* Now add the file system entries */ -+ -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst && !error) { -+ error = register_filesystem(fsinst->fst); -+ if (!error) -+ fsinst->installed = 1; -+ fsinst++; -+ } -+ -+ /* Any errors? uninstall */ -+ if (error) { -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst) { -+ if (fsinst->installed) { -+ unregister_filesystem(fsinst->fst); -+ fsinst->installed = 0; -+ } -+ fsinst++; -+ } -+ } -+ -+ return error; -+} -+ -+static void __exit exit_yaffs_fs(void) -+{ -+ -+ struct file_system_to_install *fsinst; -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs built " __DATE__ " " __TIME__ " removing."); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) -+ remove_proc_entry("yaffs", YPROC_ROOT); -+#endif -+ -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst) { -+ if (fsinst->installed) { -+ unregister_filesystem(fsinst->fst); -+ fsinst->installed = 0; -+ } -+ fsinst++; -+ } -+} -+ -+module_init(init_yaffs_fs) -+ module_exit(exit_yaffs_fs) -+ -+ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); -+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); -+MODULE_LICENSE("GPL"); -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.10.40/fs/yaffs2/yaffs_yaffs1.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,422 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_yaffs1.h" -+#include "yportenv.h" -+#include "yaffs_trace.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_nand.h" -+#include "yaffs_attribs.h" -+ -+int yaffs1_scan(struct yaffs_dev *dev) -+{ -+ struct yaffs_ext_tags tags; -+ int blk; -+ int result; -+ int chunk; -+ int c; -+ int deleted; -+ enum yaffs_block_state state; -+ LIST_HEAD(hard_list); -+ struct yaffs_block_info *bi; -+ u32 seq_number; -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_obj *in; -+ struct yaffs_obj *parent; -+ int alloc_failed = 0; -+ struct yaffs_shadow_fixer *shadow_fixers = NULL; -+ u8 *chunk_data; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs1_scan starts intstartblk %d intendblk %d...", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ chunk_data = yaffs_get_temp_buffer(dev); -+ -+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; -+ -+ /* Scan all the blocks to determine their state */ -+ bi = dev->block_info; -+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; -+ blk++) { -+ yaffs_clear_chunk_bits(dev, blk); -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ -+ yaffs_query_init_block_state(dev, blk, &state, &seq_number); -+ -+ bi->block_state = state; -+ bi->seq_number = seq_number; -+ -+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) -+ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, -+ "Block scanning block %d state %d seq %d", -+ blk, state, seq_number); -+ -+ if (state == YAFFS_BLOCK_STATE_DEAD) { -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, -+ "block %d is bad", blk); -+ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += dev->param.chunks_per_block; -+ } -+ bi++; -+ } -+ -+ /* For each block.... */ -+ for (blk = dev->internal_start_block; -+ !alloc_failed && blk <= dev->internal_end_block; blk++) { -+ -+ cond_resched(); -+ -+ bi = yaffs_get_block_info(dev, blk); -+ state = bi->block_state; -+ -+ deleted = 0; -+ -+ /* For each chunk in each block that needs scanning.... */ -+ for (c = 0; -+ !alloc_failed && c < dev->param.chunks_per_block && -+ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { -+ /* Read the tags and decide what to do */ -+ chunk = blk * dev->param.chunks_per_block + c; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, -+ &tags); -+ -+ /* Let's have a good look at this chunk... */ -+ -+ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || -+ tags.is_deleted) { -+ /* YAFFS1 only... -+ * A deleted chunk -+ */ -+ deleted++; -+ dev->n_free_chunks++; -+ } else if (!tags.chunk_used) { -+ /* An unassigned chunk in the block -+ * This means that either the block is empty or -+ * this is the one being allocated from -+ */ -+ -+ if (c == 0) { -+ /* We're looking at the first chunk in -+ *the block so the block is unused */ -+ state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ } else { -+ /* this is the block being allocated */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Allocating from %d %d", -+ blk, c); -+ state = YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->alloc_block = blk; -+ dev->alloc_page = c; -+ dev->alloc_block_finder = blk; -+ -+ } -+ -+ dev->n_free_chunks += -+ (dev->param.chunks_per_block - c); -+ } else if (tags.chunk_id > 0) { -+ /* chunk_id > 0 so it is a data chunk... */ -+ unsigned int endpos; -+ -+ yaffs_set_chunk_bit(dev, blk, c); -+ bi->pages_in_use++; -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ YAFFS_OBJECT_TYPE_FILE); -+ /* PutChunkIntoFile checks for a clash -+ * (two data chunks with the same chunk_id). -+ */ -+ -+ if (!in) -+ alloc_failed = 1; -+ -+ if (in) { -+ if (!yaffs_put_chunk_in_file -+ (in, tags.chunk_id, chunk, 1)) -+ alloc_failed = 1; -+ } -+ -+ endpos = -+ (tags.chunk_id - 1) * -+ dev->data_bytes_per_chunk + -+ tags.n_bytes; -+ if (in && -+ in->variant_type == -+ YAFFS_OBJECT_TYPE_FILE && -+ in->variant.file_variant.scanned_size < -+ endpos) { -+ in->variant.file_variant.scanned_size = -+ endpos; -+ if (!dev->param.use_header_file_size) { -+ in->variant. -+ file_variant.file_size = -+ in->variant. -+ file_variant.scanned_size; -+ } -+ -+ } -+ } else { -+ /* chunk_id == 0, so it is an ObjectHeader. -+ * Make the object -+ */ -+ yaffs_set_chunk_bit(dev, blk, c); -+ bi->pages_in_use++; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, -+ chunk_data, -+ NULL); -+ -+ oh = (struct yaffs_obj_hdr *)chunk_data; -+ -+ in = yaffs_find_by_number(dev, tags.obj_id); -+ if (in && in->variant_type != oh->type) { -+ /* This should not happen, but somehow -+ * Wev'e ended up with an obj_id that -+ * has been reused but not yet deleted, -+ * and worse still it has changed type. -+ * Delete the old object. -+ */ -+ -+ yaffs_del_obj(in); -+ in = NULL; -+ } -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ oh->type); -+ -+ if (!in) -+ alloc_failed = 1; -+ -+ if (in && oh->shadows_obj > 0) { -+ -+ struct yaffs_shadow_fixer *fixer; -+ fixer = -+ kmalloc(sizeof -+ (struct yaffs_shadow_fixer), -+ GFP_NOFS); -+ if (fixer) { -+ fixer->next = shadow_fixers; -+ shadow_fixers = fixer; -+ fixer->obj_id = tags.obj_id; -+ fixer->shadowed_id = -+ oh->shadows_obj; -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Shadow fixer: %d shadows %d", -+ fixer->obj_id, -+ fixer->shadowed_id); -+ -+ } -+ -+ } -+ -+ if (in && in->valid) { -+ /* We have already filled this one. -+ * We have a duplicate and need to -+ * resolve it. */ -+ -+ unsigned existing_serial = in->serial; -+ unsigned new_serial = -+ tags.serial_number; -+ -+ if (((existing_serial + 1) & 3) == -+ new_serial) { -+ /* Use new one - destroy the -+ * exisiting one */ -+ yaffs_chunk_del(dev, -+ in->hdr_chunk, -+ 1, __LINE__); -+ in->valid = 0; -+ } else { -+ /* Use existing - destroy -+ * this one. */ -+ yaffs_chunk_del(dev, chunk, 1, -+ __LINE__); -+ } -+ } -+ -+ if (in && !in->valid && -+ (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == -+ YAFFS_OBJECTID_LOSTNFOUND)) { -+ /* We only load some info, don't fiddle -+ * with directory structure */ -+ in->valid = 1; -+ in->variant_type = oh->type; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->hdr_chunk = chunk; -+ in->serial = tags.serial_number; -+ -+ } else if (in && !in->valid) { -+ /* we need to load this info */ -+ -+ in->valid = 1; -+ in->variant_type = oh->type; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->hdr_chunk = chunk; -+ in->serial = tags.serial_number; -+ -+ yaffs_set_obj_name_from_oh(in, oh); -+ in->dirty = 0; -+ -+ /* directory stuff... -+ * hook up to parent -+ */ -+ -+ parent = -+ yaffs_find_or_create_by_number -+ (dev, oh->parent_obj_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ if (!parent) -+ alloc_failed = 1; -+ if (parent && parent->variant_type == -+ YAFFS_OBJECT_TYPE_UNKNOWN) { -+ /* Set up as a directory */ -+ parent->variant_type = -+ YAFFS_OBJECT_TYPE_DIRECTORY; -+ INIT_LIST_HEAD(&parent-> -+ variant.dir_variant. -+ children); -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ /* Hoosterman, a problem.... -+ * We're trying to use a -+ * non-directory as a directory -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." -+ ); -+ parent = dev->lost_n_found; -+ } -+ -+ yaffs_add_obj_to_dir(parent, in); -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Todo got a problem */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (dev->param. -+ use_header_file_size) -+ in->variant. -+ file_variant.file_size -+ = yaffs_oh_to_size(oh); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ in->variant. -+ hardlink_variant.equiv_id = -+ oh->equiv_id; -+ list_add(&in->hard_links, -+ &hard_list); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ in->variant.symlink_variant. -+ alias = -+ yaffs_clone_str(oh->alias); -+ if (!in->variant. -+ symlink_variant.alias) -+ alloc_failed = 1; -+ break; -+ } -+ } -+ } -+ } -+ -+ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* If we got this far while scanning, -+ * then the block is fully allocated. */ -+ state = YAFFS_BLOCK_STATE_FULL; -+ } -+ -+ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ /* If the block was partially allocated then -+ * treat it as fully allocated. */ -+ state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ -+ bi->block_state = state; -+ -+ /* Now let's see if it was dirty */ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state == YAFFS_BLOCK_STATE_FULL) -+ yaffs_block_became_dirty(dev, blk); -+ } -+ -+ /* Ok, we've done all the scanning. -+ * Fix up the hard link chains. -+ * We should now have scanned all the objects, now it's time to add -+ * these hardlinks. -+ */ -+ -+ yaffs_link_fixup(dev, &hard_list); -+ -+ /* -+ * Fix up any shadowed objects. -+ * There should not be more than one of these. -+ */ -+ { -+ struct yaffs_shadow_fixer *fixer; -+ struct yaffs_obj *obj; -+ -+ while (shadow_fixers) { -+ fixer = shadow_fixers; -+ shadow_fixers = fixer->next; -+ /* Complete the rename transaction by deleting the -+ * shadowed object then setting the object header -+ to unshadowed. -+ */ -+ obj = yaffs_find_by_number(dev, fixer->shadowed_id); -+ if (obj) -+ yaffs_del_obj(obj); -+ -+ obj = yaffs_find_by_number(dev, fixer->obj_id); -+ -+ if (obj) -+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); -+ -+ kfree(fixer); -+ } -+ } -+ -+ yaffs_release_temp_buffer(dev, chunk_data); -+ -+ if (alloc_failed) -+ return YAFFS_FAIL; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.10.40/fs/yaffs2/yaffs_yaffs1.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,22 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_YAFFS1_H__ -+#define __YAFFS_YAFFS1_H__ -+ -+#include "yaffs_guts.h" -+int yaffs1_scan(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.10.40/fs/yaffs2/yaffs_yaffs2.c ---- linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,1534 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yaffs_yaffs2.h" -+#include "yaffs_checkptrw.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_nand.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_verify.h" -+#include "yaffs_attribs.h" -+#include "yaffs_summary.h" -+ -+/* -+ * Checkpoints are really no benefit on very small partitions. -+ * -+ * To save space on small partitions don't bother with checkpoints unless -+ * the partition is at least this big. -+ */ -+#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 -+#define YAFFS_SMALL_HOLE_THRESHOLD 4 -+ -+/* -+ * Oldest Dirty Sequence Number handling. -+ */ -+ -+/* yaffs_calc_oldest_dirty_seq() -+ * yaffs2_find_oldest_dirty_seq() -+ * Calculate the oldest dirty sequence number if we don't know it. -+ */ -+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) -+{ -+ int i; -+ unsigned seq; -+ unsigned block_no = 0; -+ struct yaffs_block_info *b; -+ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ /* Find the oldest dirty sequence number. */ -+ seq = dev->seq_number + 1; -+ b = dev->block_info; -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ if (b->block_state == YAFFS_BLOCK_STATE_FULL && -+ (b->pages_in_use - b->soft_del_pages) < -+ dev->param.chunks_per_block && -+ b->seq_number < seq) { -+ seq = b->seq_number; -+ block_no = i; -+ } -+ b++; -+ } -+ -+ if (block_no) { -+ dev->oldest_dirty_seq = seq; -+ dev->oldest_dirty_block = block_no; -+ } -+} -+ -+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!dev->oldest_dirty_seq) -+ yaffs_calc_oldest_dirty_seq(dev); -+} -+ -+/* -+ * yaffs_clear_oldest_dirty_seq() -+ * Called when a block is erased or marked bad. (ie. when its seq_number -+ * becomes invalid). If the value matches the oldest then we clear -+ * dev->oldest_dirty_seq to force its recomputation. -+ */ -+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi) -+{ -+ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { -+ dev->oldest_dirty_seq = 0; -+ dev->oldest_dirty_block = 0; -+ } -+} -+ -+/* -+ * yaffs2_update_oldest_dirty_seq() -+ * Update the oldest dirty sequence number whenever we dirty a block. -+ * Only do this if the oldest_dirty_seq is actually being tracked. -+ */ -+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, -+ struct yaffs_block_info *bi) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (dev->oldest_dirty_seq) { -+ if (dev->oldest_dirty_seq > bi->seq_number) { -+ dev->oldest_dirty_seq = bi->seq_number; -+ dev->oldest_dirty_block = block_no; -+ } -+ } -+} -+ -+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) -+{ -+ -+ if (!dev->param.is_yaffs2) -+ return 1; /* disqualification only applies to yaffs2. */ -+ -+ if (!bi->has_shrink_hdr) -+ return 1; /* can gc */ -+ -+ yaffs2_find_oldest_dirty_seq(dev); -+ -+ /* Can't do gc of this block if there are any blocks older than this -+ * one that have discarded pages. -+ */ -+ return (bi->seq_number <= dev->oldest_dirty_seq); -+} -+ -+/* -+ * yaffs2_find_refresh_block() -+ * periodically finds the oldest full block by sequence number for refreshing. -+ * Only for yaffs2. -+ */ -+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) -+{ -+ u32 b; -+ u32 oldest = 0; -+ u32 oldest_seq = 0; -+ struct yaffs_block_info *bi; -+ -+ if (!dev->param.is_yaffs2) -+ return oldest; -+ -+ /* -+ * If refresh period < 10 then refreshing is disabled. -+ */ -+ if (dev->param.refresh_period < 10) -+ return oldest; -+ -+ /* -+ * Fix broken values. -+ */ -+ if (dev->refresh_skip > dev->param.refresh_period) -+ dev->refresh_skip = dev->param.refresh_period; -+ -+ if (dev->refresh_skip > 0) -+ return oldest; -+ -+ /* -+ * Refresh skip is now zero. -+ * We'll do a refresh this time around.... -+ * Update the refresh skip and find the oldest block. -+ */ -+ dev->refresh_skip = dev->param.refresh_period; -+ dev->refresh_count++; -+ bi = dev->block_info; -+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { -+ -+ if (oldest < 1 || bi->seq_number < oldest_seq) { -+ oldest = b; -+ oldest_seq = bi->seq_number; -+ } -+ } -+ bi++; -+ } -+ -+ if (oldest > 0) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC refresh count %d selected block %d with seq_number %d", -+ dev->refresh_count, oldest, oldest_seq); -+ } -+ -+ return oldest; -+} -+ -+int yaffs2_checkpt_required(struct yaffs_dev *dev) -+{ -+ int nblocks; -+ -+ if (!dev->param.is_yaffs2) -+ return 0; -+ -+ nblocks = dev->internal_end_block - dev->internal_start_block + 1; -+ -+ return !dev->param.skip_checkpt_wr && -+ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); -+} -+ -+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) -+{ -+ int retval; -+ int n_bytes = 0; -+ int n_blocks; -+ int dev_blocks; -+ -+ if (!dev->param.is_yaffs2) -+ return 0; -+ -+ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { -+ /* Not a valid value so recalculate */ -+ dev_blocks = dev->param.end_block - dev->param.start_block + 1; -+ n_bytes += sizeof(struct yaffs_checkpt_validity); -+ n_bytes += sizeof(struct yaffs_checkpt_dev); -+ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); -+ n_bytes += dev_blocks * dev->chunk_bit_stride; -+ n_bytes += -+ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * -+ dev->n_obj; -+ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; -+ n_bytes += sizeof(struct yaffs_checkpt_validity); -+ n_bytes += sizeof(u32); /* checksum */ -+ -+ /* Round up and add 2 blocks to allow for some bad blocks, -+ * so add 3 */ -+ -+ n_blocks = -+ (n_bytes / -+ (dev->data_bytes_per_chunk * -+ dev->param.chunks_per_block)) + 3; -+ -+ dev->checkpoint_blocks_required = n_blocks; -+ } -+ -+ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; -+ if (retval < 0) -+ retval = 0; -+ return retval; -+} -+ -+/*--------------------- Checkpointing --------------------*/ -+ -+static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) -+{ -+ struct yaffs_checkpt_validity cp; -+ -+ memset(&cp, 0, sizeof(cp)); -+ -+ cp.struct_type = sizeof(cp); -+ cp.magic = YAFFS_MAGIC; -+ cp.version = YAFFS_CHECKPOINT_VERSION; -+ cp.head = (head) ? 1 : 0; -+ -+ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) -+{ -+ struct yaffs_checkpt_validity cp; -+ int ok; -+ -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ -+ if (ok) -+ ok = (cp.struct_type == sizeof(cp)) && -+ (cp.magic == YAFFS_MAGIC) && -+ (cp.version == YAFFS_CHECKPOINT_VERSION) && -+ (cp.head == ((head) ? 1 : 0)); -+ return ok ? 1 : 0; -+} -+ -+static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, -+ struct yaffs_dev *dev) -+{ -+ cp->n_erased_blocks = dev->n_erased_blocks; -+ cp->alloc_block = dev->alloc_block; -+ cp->alloc_page = dev->alloc_page; -+ cp->n_free_chunks = dev->n_free_chunks; -+ -+ cp->n_deleted_files = dev->n_deleted_files; -+ cp->n_unlinked_files = dev->n_unlinked_files; -+ cp->n_bg_deletions = dev->n_bg_deletions; -+ cp->seq_number = dev->seq_number; -+ -+} -+ -+static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, -+ struct yaffs_checkpt_dev *cp) -+{ -+ dev->n_erased_blocks = cp->n_erased_blocks; -+ dev->alloc_block = cp->alloc_block; -+ dev->alloc_page = cp->alloc_page; -+ dev->n_free_chunks = cp->n_free_chunks; -+ -+ dev->n_deleted_files = cp->n_deleted_files; -+ dev->n_unlinked_files = cp->n_unlinked_files; -+ dev->n_bg_deletions = cp->n_bg_deletions; -+ dev->seq_number = cp->seq_number; -+} -+ -+static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_dev cp; -+ u32 n_bytes; -+ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ int ok; -+ -+ /* Write device runtime values */ -+ yaffs2_dev_to_checkpt_dev(&cp, dev); -+ cp.struct_type = sizeof(cp); -+ -+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (!ok) -+ return 0; -+ -+ /* Write block info */ -+ n_bytes = n_blocks * sizeof(struct yaffs_block_info); -+ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); -+ if (!ok) -+ return 0; -+ -+ /* Write chunk bits */ -+ n_bytes = n_blocks * dev->chunk_bit_stride; -+ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_dev cp; -+ u32 n_bytes; -+ u32 n_blocks = -+ (dev->internal_end_block - dev->internal_start_block + 1); -+ int ok; -+ -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (!ok) -+ return 0; -+ -+ if (cp.struct_type != sizeof(cp)) -+ return 0; -+ -+ yaffs_checkpt_dev_to_dev(dev, &cp); -+ -+ n_bytes = n_blocks * sizeof(struct yaffs_block_info); -+ -+ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); -+ -+ if (!ok) -+ return 0; -+ -+ n_bytes = n_blocks * dev->chunk_bit_stride; -+ -+ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); -+ -+ return ok ? 1 : 0; -+} -+ -+static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, -+ struct yaffs_obj *obj) -+{ -+ cp->obj_id = obj->obj_id; -+ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; -+ cp->hdr_chunk = obj->hdr_chunk; -+ cp->variant_type = obj->variant_type; -+ cp->deleted = obj->deleted; -+ cp->soft_del = obj->soft_del; -+ cp->unlinked = obj->unlinked; -+ cp->fake = obj->fake; -+ cp->rename_allowed = obj->rename_allowed; -+ cp->unlink_allowed = obj->unlink_allowed; -+ cp->serial = obj->serial; -+ cp->n_data_chunks = obj->n_data_chunks; -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; -+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; -+} -+ -+static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, -+ struct yaffs_checkpt_obj *cp) -+{ -+ struct yaffs_obj *parent; -+ -+ if (obj->variant_type != cp->variant_type) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", -+ cp->obj_id, cp->variant_type, cp->hdr_chunk, -+ obj->variant_type); -+ return 0; -+ } -+ -+ obj->obj_id = cp->obj_id; -+ -+ if (cp->parent_id) -+ parent = yaffs_find_or_create_by_number(obj->my_dev, -+ cp->parent_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ else -+ parent = NULL; -+ -+ if (parent) { -+ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", -+ cp->obj_id, cp->parent_id, -+ cp->variant_type, cp->hdr_chunk, -+ parent->variant_type); -+ return 0; -+ } -+ yaffs_add_obj_to_dir(parent, obj); -+ } -+ -+ obj->hdr_chunk = cp->hdr_chunk; -+ obj->variant_type = cp->variant_type; -+ obj->deleted = cp->deleted; -+ obj->soft_del = cp->soft_del; -+ obj->unlinked = cp->unlinked; -+ obj->fake = cp->fake; -+ obj->rename_allowed = cp->rename_allowed; -+ obj->unlink_allowed = cp->unlink_allowed; -+ obj->serial = cp->serial; -+ obj->n_data_chunks = cp->n_data_chunks; -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; -+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; -+ -+ if (obj->hdr_chunk > 0) -+ obj->lazy_loaded = 1; -+ return 1; -+} -+ -+static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, -+ struct yaffs_tnode *tn, u32 level, -+ int chunk_offset) -+{ -+ int i; -+ struct yaffs_dev *dev = in->my_dev; -+ int ok = 1; -+ u32 base_offset; -+ -+ if (!tn) -+ return 1; -+ -+ if (level > 0) { -+ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { -+ if (!tn->internal[i]) -+ continue; -+ ok = yaffs2_checkpt_tnode_worker(in, -+ tn->internal[i], -+ level - 1, -+ (chunk_offset << -+ YAFFS_TNODES_INTERNAL_BITS) + i); -+ } -+ return ok; -+ } -+ -+ /* Level 0 tnode */ -+ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; -+ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == -+ sizeof(base_offset)); -+ if (ok) -+ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == -+ dev->tnode_size); -+ -+ return ok; -+} -+ -+static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) -+{ -+ u32 end_marker = ~0; -+ int ok = 1; -+ -+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return ok; -+ -+ ok = yaffs2_checkpt_tnode_worker(obj, -+ obj->variant.file_variant.top, -+ obj->variant.file_variant. -+ top_level, 0); -+ if (ok) -+ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, -+ sizeof(end_marker)) == sizeof(end_marker)); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) -+{ -+ u32 base_chunk; -+ int ok = 1; -+ struct yaffs_dev *dev = obj->my_dev; -+ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; -+ struct yaffs_tnode *tn; -+ int nread = 0; -+ -+ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == -+ sizeof(base_chunk)); -+ -+ while (ok && (~base_chunk)) { -+ nread++; -+ /* Read level 0 tnode */ -+ -+ tn = yaffs_get_tnode(dev); -+ if (tn) -+ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == -+ dev->tnode_size); -+ else -+ ok = 0; -+ -+ if (tn && ok) -+ ok = yaffs_add_find_tnode_0(dev, -+ file_stuct_ptr, -+ base_chunk, tn) ? 1 : 0; -+ -+ if (ok) -+ ok = (yaffs2_checkpt_rd -+ (dev, &base_chunk, -+ sizeof(base_chunk)) == sizeof(base_chunk)); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint read tnodes %d records, last %d. ok %d", -+ nread, base_chunk, ok); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_checkpt_obj cp; -+ int i; -+ int ok = 1; -+ struct list_head *lh; -+ -+ /* Iterate through the objects in each hash entry, -+ * dumping them to the checkpointing stream. -+ */ -+ -+ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each(lh, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ if (!obj->defered_free) { -+ yaffs2_obj_checkpt_obj(&cp, obj); -+ cp.struct_type = sizeof(cp); -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", -+ cp.obj_id, cp.parent_id, -+ cp.variant_type, cp.hdr_chunk, obj); -+ -+ ok = (yaffs2_checkpt_wr(dev, &cp, -+ sizeof(cp)) == sizeof(cp)); -+ -+ if (ok && -+ obj->variant_type == -+ YAFFS_OBJECT_TYPE_FILE) -+ ok = yaffs2_wr_checkpt_tnodes(obj); -+ } -+ } -+ } -+ -+ /* Dump end of list */ -+ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); -+ cp.struct_type = sizeof(cp); -+ -+ if (ok) -+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_checkpt_obj cp; -+ int ok = 1; -+ int done = 0; -+ LIST_HEAD(hard_list); -+ -+ -+ while (ok && !done) { -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (cp.struct_type != sizeof(cp)) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "struct size %d instead of %d ok %d", -+ cp.struct_type, (int)sizeof(cp), ok); -+ ok = 0; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint read object %d parent %d type %d chunk %d ", -+ cp.obj_id, cp.parent_id, cp.variant_type, -+ cp.hdr_chunk); -+ -+ if (ok && cp.obj_id == ~0) { -+ done = 1; -+ } else if (ok) { -+ obj = -+ yaffs_find_or_create_by_number(dev, cp.obj_id, -+ cp.variant_type); -+ if (obj) { -+ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); -+ if (!ok) -+ break; -+ if (obj->variant_type == -+ YAFFS_OBJECT_TYPE_FILE) { -+ ok = yaffs2_rd_checkpt_tnodes(obj); -+ } else if (obj->variant_type == -+ YAFFS_OBJECT_TYPE_HARDLINK) { -+ list_add(&obj->hard_links, &hard_list); -+ } -+ } else { -+ ok = 0; -+ } -+ } -+ } -+ -+ if (ok) -+ yaffs_link_fixup(dev, &hard_list); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) -+{ -+ u32 checkpt_sum; -+ int ok; -+ -+ yaffs2_get_checkpt_sum(dev, &checkpt_sum); -+ -+ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == -+ sizeof(checkpt_sum)); -+ -+ if (!ok) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) -+{ -+ u32 checkpt_sum0; -+ u32 checkpt_sum1; -+ int ok; -+ -+ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); -+ -+ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == -+ sizeof(checkpt_sum1)); -+ -+ if (!ok) -+ return 0; -+ -+ if (checkpt_sum0 != checkpt_sum1) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) -+{ -+ int ok = 1; -+ -+ if (!yaffs2_checkpt_required(dev)) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "skipping checkpoint write"); -+ ok = 0; -+ } -+ -+ if (ok) -+ ok = yaffs2_checkpt_open(dev, 1); -+ -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint validity"); -+ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint device"); -+ ok = yaffs2_wr_checkpt_dev(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint objects"); -+ ok = yaffs2_wr_checkpt_objs(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint validity"); -+ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); -+ } -+ -+ if (ok) -+ ok = yaffs2_wr_checkpt_sum(dev); -+ -+ if (!yaffs_checkpt_close(dev)) -+ ok = 0; -+ -+ if (ok) -+ dev->is_checkpointed = 1; -+ else -+ dev->is_checkpointed = 0; -+ -+ return dev->is_checkpointed; -+} -+ -+static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) -+{ -+ int ok = 1; -+ -+ if (!dev->param.is_yaffs2) -+ ok = 0; -+ -+ if (ok && dev->param.skip_checkpt_rd) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "skipping checkpoint read"); -+ ok = 0; -+ } -+ -+ if (ok) -+ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ -+ -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint validity"); -+ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint device"); -+ ok = yaffs2_rd_checkpt_dev(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint objects"); -+ ok = yaffs2_rd_checkpt_objs(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint validity"); -+ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); -+ } -+ -+ if (ok) { -+ ok = yaffs2_rd_checkpt_sum(dev); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint checksum %d", ok); -+ } -+ -+ if (!yaffs_checkpt_close(dev)) -+ ok = 0; -+ -+ if (ok) -+ dev->is_checkpointed = 1; -+ else -+ dev->is_checkpointed = 0; -+ -+ return ok ? 1 : 0; -+} -+ -+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) -+{ -+ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { -+ dev->is_checkpointed = 0; -+ yaffs2_checkpt_invalidate_stream(dev); -+ } -+ if (dev->param.sb_dirty_fn) -+ dev->param.sb_dirty_fn(dev); -+} -+ -+int yaffs_checkpoint_save(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "save entry: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ yaffs_verify_objects(dev); -+ yaffs_verify_blocks(dev); -+ yaffs_verify_free_chunks(dev); -+ -+ if (!dev->is_checkpointed) { -+ yaffs2_checkpt_invalidate(dev); -+ yaffs2_wr_checkpt_data(dev); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, -+ "save exit: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ return dev->is_checkpointed; -+} -+ -+int yaffs2_checkpt_restore(struct yaffs_dev *dev) -+{ -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "restore entry: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ retval = yaffs2_rd_checkpt_data(dev); -+ -+ if (dev->is_checkpointed) { -+ yaffs_verify_objects(dev); -+ yaffs_verify_blocks(dev); -+ yaffs_verify_free_chunks(dev); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "restore exit: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ return retval; -+} -+ -+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) -+{ -+ /* if new_size > old_file_size. -+ * We're going to be writing a hole. -+ * If the hole is small then write zeros otherwise write a start -+ * of hole marker. -+ */ -+ loff_t old_file_size; -+ loff_t increase; -+ int small_hole; -+ int result = YAFFS_OK; -+ struct yaffs_dev *dev = NULL; -+ u8 *local_buffer = NULL; -+ int small_increase_ok = 0; -+ -+ if (!obj) -+ return YAFFS_FAIL; -+ -+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return YAFFS_FAIL; -+ -+ dev = obj->my_dev; -+ -+ /* Bail out if not yaffs2 mode */ -+ if (!dev->param.is_yaffs2) -+ return YAFFS_OK; -+ -+ old_file_size = obj->variant.file_variant.file_size; -+ -+ if (new_size <= old_file_size) -+ return YAFFS_OK; -+ -+ increase = new_size - old_file_size; -+ -+ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && -+ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) -+ small_hole = 1; -+ else -+ small_hole = 0; -+ -+ if (small_hole) -+ local_buffer = yaffs_get_temp_buffer(dev); -+ -+ if (local_buffer) { -+ /* fill hole with zero bytes */ -+ loff_t pos = old_file_size; -+ int this_write; -+ int written; -+ memset(local_buffer, 0, dev->data_bytes_per_chunk); -+ small_increase_ok = 1; -+ -+ while (increase > 0 && small_increase_ok) { -+ this_write = increase; -+ if (this_write > dev->data_bytes_per_chunk) -+ this_write = dev->data_bytes_per_chunk; -+ written = -+ yaffs_do_file_wr(obj, local_buffer, pos, this_write, -+ 0); -+ if (written == this_write) { -+ pos += this_write; -+ increase -= this_write; -+ } else { -+ small_increase_ok = 0; -+ } -+ } -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ -+ /* If out of space then reverse any chunks we've added */ -+ if (!small_increase_ok) -+ yaffs_resize_file_down(obj, old_file_size); -+ } -+ -+ if (!small_increase_ok && -+ obj->parent && -+ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && -+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { -+ /* Write a hole start header with the old file size */ -+ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); -+ } -+ -+ return result; -+} -+ -+struct yaffs_block_index { -+ int seq; -+ int block; -+}; -+ -+static int yaffs2_ybicmp(const void *a, const void *b) -+{ -+ int aseq = ((struct yaffs_block_index *)a)->seq; -+ int bseq = ((struct yaffs_block_index *)b)->seq; -+ int ablock = ((struct yaffs_block_index *)a)->block; -+ int bblock = ((struct yaffs_block_index *)b)->block; -+ -+ if (aseq == bseq) -+ return ablock - bblock; -+ -+ return aseq - bseq; -+} -+ -+static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, -+ int blk, int chunk_in_block, -+ int *found_chunks, -+ u8 *chunk_data, -+ struct list_head *hard_list, -+ int summary_available) -+{ -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_obj *in; -+ struct yaffs_obj *parent; -+ int equiv_id; -+ loff_t file_size; -+ int is_shrink; -+ int is_unlinked; -+ struct yaffs_ext_tags tags; -+ int result; -+ int alloc_failed = 0; -+ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; -+ struct yaffs_file_var *file_var; -+ struct yaffs_hardlink_var *hl_var; -+ struct yaffs_symlink_var *sl_var; -+ -+ if (summary_available) { -+ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); -+ tags.seq_number = bi->seq_number; -+ } -+ -+ if (!summary_available || tags.obj_id == 0) { -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); -+ dev->tags_used++; -+ } else { -+ dev->summary_used++; -+ } -+ -+ /* Let's have a good look at this chunk... */ -+ -+ if (!tags.chunk_used) { -+ /* An unassigned chunk in the block. -+ * If there are used chunks after this one, then -+ * it is a chunk that was skipped due to failing -+ * the erased check. Just skip it so that it can -+ * be deleted. -+ * But, more typically, We get here when this is -+ * an unallocated chunk and his means that -+ * either the block is empty or this is the one -+ * being allocated from -+ */ -+ -+ if (*found_chunks) { -+ /* This is a chunk that was skipped due -+ * to failing the erased check */ -+ } else if (chunk_in_block == 0) { -+ /* We're looking at the first chunk in -+ * the block so the block is unused */ -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ } else { -+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ if (dev->seq_number == bi->seq_number) { -+ /* Allocating from this block*/ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Allocating from %d %d", -+ blk, chunk_in_block); -+ -+ bi->block_state = -+ YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->alloc_block = blk; -+ dev->alloc_page = chunk_in_block; -+ dev->alloc_block_finder = blk; -+ } else { -+ /* This is a partially written block -+ * that is not the current -+ * allocation block. -+ */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Partially written block %d detected. gc will fix this.", -+ blk); -+ } -+ } -+ } -+ -+ dev->n_free_chunks++; -+ -+ } else if (tags.ecc_result == -+ YAFFS_ECC_RESULT_UNFIXED) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Unfixed ECC in chunk(%d:%d), chunk ignored", -+ blk, chunk_in_block); -+ dev->n_free_chunks++; -+ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || -+ tags.chunk_id > YAFFS_MAX_CHUNK_ID || -+ tags.obj_id == YAFFS_OBJECTID_SUMMARY || -+ (tags.chunk_id > 0 && -+ tags.n_bytes > dev->data_bytes_per_chunk) || -+ tags.seq_number != bi->seq_number) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", -+ blk, chunk_in_block, tags.obj_id, -+ tags.chunk_id, tags.n_bytes); -+ dev->n_free_chunks++; -+ } else if (tags.chunk_id > 0) { -+ /* chunk_id > 0 so it is a data chunk... */ -+ loff_t endpos; -+ loff_t chunk_base = (tags.chunk_id - 1) * -+ dev->data_bytes_per_chunk; -+ -+ *found_chunks = 1; -+ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ YAFFS_OBJECT_TYPE_FILE); -+ if (!in) -+ /* Out of memory */ -+ alloc_failed = 1; -+ -+ if (in && -+ in->variant_type == YAFFS_OBJECT_TYPE_FILE && -+ chunk_base < in->variant.file_variant.shrink_size) { -+ /* This has not been invalidated by -+ * a resize */ -+ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, -+ chunk, -1)) -+ alloc_failed = 1; -+ -+ /* File size is calculated by looking at -+ * the data chunks if we have not -+ * seen an object header yet. -+ * Stop this practice once we find an -+ * object header. -+ */ -+ endpos = chunk_base + tags.n_bytes; -+ -+ if (!in->valid && -+ in->variant.file_variant.scanned_size < endpos) { -+ in->variant.file_variant. -+ scanned_size = endpos; -+ in->variant.file_variant. -+ file_size = endpos; -+ } -+ } else if (in) { -+ /* This chunk has been invalidated by a -+ * resize, or a past file deletion -+ * so delete the chunk*/ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ } -+ } else { -+ /* chunk_id == 0, so it is an ObjectHeader. -+ * Thus, we read in the object header and make -+ * the object -+ */ -+ *found_chunks = 1; -+ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ -+ oh = NULL; -+ in = NULL; -+ -+ if (tags.extra_available) { -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ tags.extra_obj_type); -+ if (!in) -+ alloc_failed = 1; -+ } -+ -+ if (!in || -+ (!in->valid && dev->param.disable_lazy_load) || -+ tags.extra_shadows || -+ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { -+ -+ /* If we don't have valid info then we -+ * need to read the chunk -+ * TODO In future we can probably defer -+ * reading the chunk and living with -+ * invalid data until needed. -+ */ -+ -+ result = yaffs_rd_chunk_tags_nand(dev, -+ chunk, -+ chunk_data, -+ NULL); -+ -+ oh = (struct yaffs_obj_hdr *)chunk_data; -+ -+ if (dev->param.inband_tags) { -+ /* Fix up the header if they got -+ * corrupted by inband tags */ -+ oh->shadows_obj = -+ oh->inband_shadowed_obj_id; -+ oh->is_shrink = -+ oh->inband_is_shrink; -+ } -+ -+ if (!in) { -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, oh->type); -+ if (!in) -+ alloc_failed = 1; -+ } -+ } -+ -+ if (!in) { -+ /* TODO Hoosterman we have a problem! */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", -+ tags.obj_id, chunk); -+ return YAFFS_FAIL; -+ } -+ -+ if (in->valid) { -+ /* We have already filled this one. -+ * We have a duplicate that will be -+ * discarded, but we first have to suck -+ * out resize info if it is a file. -+ */ -+ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && -+ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || -+ (tags.extra_available && -+ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) -+ )) { -+ loff_t this_size = (oh) ? -+ yaffs_oh_to_size(oh) : -+ tags.extra_file_size; -+ u32 parent_obj_id = (oh) ? -+ oh->parent_obj_id : -+ tags.extra_parent_id; -+ -+ is_shrink = (oh) ? -+ oh->is_shrink : -+ tags.extra_is_shrink; -+ -+ /* If it is deleted (unlinked -+ * at start also means deleted) -+ * we treat the file size as -+ * being zeroed at this point. -+ */ -+ if (parent_obj_id == YAFFS_OBJECTID_DELETED || -+ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { -+ this_size = 0; -+ is_shrink = 1; -+ } -+ -+ if (is_shrink && -+ in->variant.file_variant.shrink_size > -+ this_size) -+ in->variant.file_variant.shrink_size = -+ this_size; -+ -+ if (is_shrink) -+ bi->has_shrink_hdr = 1; -+ } -+ /* Use existing - destroy this one. */ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ } -+ -+ if (!in->valid && in->variant_type != -+ (oh ? oh->type : tags.extra_obj_type)) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", -+ oh ? oh->type : tags.extra_obj_type, -+ in->variant_type, tags.obj_id, -+ chunk); -+ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); -+ } -+ -+ if (!in->valid && -+ (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { -+ /* We only load some info, don't fiddle -+ * with directory structure */ -+ in->valid = 1; -+ -+ if (oh) { -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->lazy_loaded = 0; -+ } else { -+ in->lazy_loaded = 1; -+ } -+ in->hdr_chunk = chunk; -+ -+ } else if (!in->valid) { -+ /* we need to load this info */ -+ in->valid = 1; -+ in->hdr_chunk = chunk; -+ if (oh) { -+ in->variant_type = oh->type; -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ -+ if (oh->shadows_obj > 0) -+ yaffs_handle_shadowed_obj(dev, -+ oh->shadows_obj, 1); -+ -+ yaffs_set_obj_name_from_oh(in, oh); -+ parent = yaffs_find_or_create_by_number(dev, -+ oh->parent_obj_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ file_size = yaffs_oh_to_size(oh); -+ is_shrink = oh->is_shrink; -+ equiv_id = oh->equiv_id; -+ } else { -+ in->variant_type = tags.extra_obj_type; -+ parent = yaffs_find_or_create_by_number(dev, -+ tags.extra_parent_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ file_size = tags.extra_file_size; -+ is_shrink = tags.extra_is_shrink; -+ equiv_id = tags.extra_equiv_id; -+ in->lazy_loaded = 1; -+ } -+ in->dirty = 0; -+ -+ if (!parent) -+ alloc_failed = 1; -+ -+ /* directory stuff... -+ * hook up to parent -+ */ -+ -+ if (parent && -+ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { -+ /* Set up as a directory */ -+ parent->variant_type = -+ YAFFS_OBJECT_TYPE_DIRECTORY; -+ INIT_LIST_HEAD(&parent-> -+ variant.dir_variant.children); -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ /* Hoosterman, another problem.... -+ * Trying to use a non-directory as a directory -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." -+ ); -+ parent = dev->lost_n_found; -+ } -+ yaffs_add_obj_to_dir(parent, in); -+ -+ is_unlinked = (parent == dev->del_dir) || -+ (parent == dev->unlinked_dir); -+ -+ if (is_shrink) -+ /* Mark the block */ -+ bi->has_shrink_hdr = 1; -+ -+ /* Note re hardlinks. -+ * Since we might scan a hardlink before its equivalent -+ * object is scanned we put them all in a list. -+ * After scanning is complete, we should have all the -+ * objects, so we run through this list and fix up all -+ * the chains. -+ */ -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Todo got a problem */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ file_var = &in->variant.file_variant; -+ if (file_var->scanned_size < file_size) { -+ /* This covers the case where the file -+ * size is greater than the data held. -+ * This will happen if the file is -+ * resized to be larger than its -+ * current data extents. -+ */ -+ file_var->file_size = file_size; -+ file_var->scanned_size = file_size; -+ } -+ -+ if (file_var->shrink_size > file_size) -+ file_var->shrink_size = file_size; -+ -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ hl_var = &in->variant.hardlink_variant; -+ if (!is_unlinked) { -+ hl_var->equiv_id = equiv_id; -+ list_add(&in->hard_links, hard_list); -+ } -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ sl_var = &in->variant.symlink_variant; -+ if (oh) { -+ sl_var->alias = -+ yaffs_clone_str(oh->alias); -+ if (!sl_var->alias) -+ alloc_failed = 1; -+ } -+ break; -+ } -+ } -+ } -+ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+int yaffs2_scan_backwards(struct yaffs_dev *dev) -+{ -+ int blk; -+ int block_iter; -+ int start_iter; -+ int end_iter; -+ int n_to_scan = 0; -+ enum yaffs_block_state state; -+ int c; -+ int deleted; -+ LIST_HEAD(hard_list); -+ struct yaffs_block_info *bi; -+ u32 seq_number; -+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ u8 *chunk_data; -+ int found_chunks; -+ int alloc_failed = 0; -+ struct yaffs_block_index *block_index = NULL; -+ int alt_block_index = 0; -+ int summary_available; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; -+ -+ block_index = -+ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); -+ -+ if (!block_index) { -+ block_index = -+ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); -+ alt_block_index = 1; -+ } -+ -+ if (!block_index) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs2_scan_backwards() could not allocate block index!" -+ ); -+ return YAFFS_FAIL; -+ } -+ -+ dev->blocks_in_checkpt = 0; -+ -+ chunk_data = yaffs_get_temp_buffer(dev); -+ -+ /* Scan all the blocks to determine their state */ -+ bi = dev->block_info; -+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; -+ blk++) { -+ yaffs_clear_chunk_bits(dev, blk); -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ -+ yaffs_query_init_block_state(dev, blk, &state, &seq_number); -+ -+ bi->block_state = state; -+ bi->seq_number = seq_number; -+ -+ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, -+ "Block scanning block %d state %d seq %d", -+ blk, bi->block_state, seq_number); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { -+ dev->blocks_in_checkpt++; -+ -+ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, -+ "block %d is bad", blk); -+ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += dev->param.chunks_per_block; -+ } else if (bi->block_state == -+ YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* Determine the highest sequence number */ -+ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && -+ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { -+ block_index[n_to_scan].seq = seq_number; -+ block_index[n_to_scan].block = blk; -+ n_to_scan++; -+ if (seq_number >= dev->seq_number) -+ dev->seq_number = seq_number; -+ } else { -+ /* TODO: Nasty sequence number! */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Block scanning block %d has bad sequence number %d", -+ blk, seq_number); -+ } -+ } -+ bi++; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); -+ -+ cond_resched(); -+ -+ /* Sort the blocks by sequence number */ -+ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), -+ yaffs2_ybicmp, NULL); -+ -+ cond_resched(); -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); -+ -+ /* Now scan the blocks looking at the data. */ -+ start_iter = 0; -+ end_iter = n_to_scan - 1; -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); -+ -+ /* For each block.... backwards */ -+ for (block_iter = end_iter; -+ !alloc_failed && block_iter >= start_iter; -+ block_iter--) { -+ /* Cooperative multitasking! This loop can run for so -+ long that watchdog timers expire. */ -+ cond_resched(); -+ -+ /* get the block to scan in the correct order */ -+ blk = block_index[block_iter].block; -+ bi = yaffs_get_block_info(dev, blk); -+ deleted = 0; -+ -+ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); -+ -+ /* For each chunk in each block that needs scanning.... */ -+ found_chunks = 0; -+ if (summary_available) -+ c = dev->chunks_per_summary - 1; -+ else -+ c = dev->param.chunks_per_block - 1; -+ -+ for (/* c is already initialised */; -+ !alloc_failed && c >= 0 && -+ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); -+ c--) { -+ /* Scan backwards... -+ * Read the tags and decide what to do -+ */ -+ if (yaffs2_scan_chunk(dev, bi, blk, c, -+ &found_chunks, chunk_data, -+ &hard_list, summary_available) == -+ YAFFS_FAIL) -+ alloc_failed = 1; -+ } -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* If we got this far while scanning, then the block -+ * is fully allocated. */ -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ } -+ -+ /* Now let's see if it was dirty */ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state == YAFFS_BLOCK_STATE_FULL) { -+ yaffs_block_became_dirty(dev, blk); -+ } -+ } -+ -+ yaffs_skip_rest_of_block(dev); -+ -+ if (alt_block_index) -+ vfree(block_index); -+ else -+ kfree(block_index); -+ -+ /* Ok, we've done all the scanning. -+ * Fix up the hard link chains. -+ * We have scanned all the objects, now it's time to add these -+ * hardlinks. -+ */ -+ yaffs_link_fixup(dev, &hard_list); -+ -+ yaffs_release_temp_buffer(dev, chunk_data); -+ -+ if (alloc_failed) -+ return YAFFS_FAIL; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.10.40/fs/yaffs2/yaffs_yaffs2.h ---- linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_YAFFS2_H__ -+#define __YAFFS_YAFFS2_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); -+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); -+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi); -+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, -+ struct yaffs_block_info *bi); -+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); -+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); -+int yaffs2_checkpt_required(struct yaffs_dev *dev); -+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); -+ -+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); -+int yaffs2_checkpt_save(struct yaffs_dev *dev); -+int yaffs2_checkpt_restore(struct yaffs_dev *dev); -+ -+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); -+int yaffs2_scan_backwards(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.10.40.orig/fs/yaffs2/yportenv.h linux-3.10.40/fs/yaffs2/yportenv.h ---- linux-3.10.40.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.10.40/fs/yaffs2/yportenv.h 2014-05-17 12:48:10.000000000 +0200 -@@ -0,0 +1,85 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YPORTENV_H__ -+#define __YPORTENV_H__ -+ -+/* -+ * Define the MTD version in terms of Linux Kernel versions -+ * This allows yaffs to be used independantly of the kernel -+ * as well as with it. -+ */ -+ -+#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) -+ -+#ifdef YAFFS_OUT_OF_TREE -+#include "moduleconfig.h" -+#endif -+ -+#include -+#define MTD_VERSION_CODE LINUX_VERSION_CODE -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* These type wrappings are used to support Unicode names in WinCE. */ -+#define YCHAR char -+#define YUCHAR unsigned char -+#define _Y(x) x -+ -+#define YAFFS_LOSTNFOUND_NAME "lost+found" -+#define YAFFS_LOSTNFOUND_PREFIX "obj" -+ -+ -+#define YAFFS_ROOT_MODE 0755 -+#define YAFFS_LOSTNFOUND_MODE 0700 -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+#define Y_CURRENT_TIME CURRENT_TIME.tv_sec -+#define Y_TIME_CONVERT(x) (x).tv_sec -+#else -+#define Y_CURRENT_TIME CURRENT_TIME -+#define Y_TIME_CONVERT(x) (x) -+#endif -+ -+#define compile_time_assertion(assertion) \ -+ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) -+ -+ -+#define yaffs_printf(msk, fmt, ...) \ -+ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) -+ -+#define yaffs_trace(msk, fmt, ...) do { \ -+ if (yaffs_trace_mask & (msk)) \ -+ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ -+} while (0) -+ -+ -+#endif diff --git a/target/linux/patches/3.10.81/bsd-compatibility.patch b/target/linux/patches/3.10.81/bsd-compatibility.patch new file mode 100644 index 000000000..b954b658f --- /dev/null +++ b/target/linux/patches/3.10.81/bsd-compatibility.patch @@ -0,0 +1,2538 @@ +diff -Nur linux-3.11.5.orig/scripts/Makefile.lib linux-3.11.5/scripts/Makefile.lib +--- linux-3.11.5.orig/scripts/Makefile.lib 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/Makefile.lib 2013-10-16 18:09:31.000000000 +0200 +@@ -281,7 +281,12 @@ + size_append = printf $(shell \ + dec_size=0; \ + for F in $1; do \ +- fsize=$$(stat -c "%s" $$F); \ ++ if stat -qs .>/dev/null 2>&1; then \ ++ statcmd='stat -f %z'; \ ++ else \ ++ statcmd='stat -c %s'; \ ++ fi; \ ++ fsize=$$($$statcmd $$F); \ + dec_size=$$(expr $$dec_size + $$fsize); \ + done; \ + printf "%08x\n" $$dec_size | \ +diff -Nur linux-3.11.5.orig/scripts/mod/mk_elfconfig.c linux-3.11.5/scripts/mod/mk_elfconfig.c +--- linux-3.11.5.orig/scripts/mod/mk_elfconfig.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/mk_elfconfig.c 2013-10-16 18:09:31.000000000 +0200 +@@ -1,7 +1,18 @@ + #include + #include + #include +-#include ++ ++#define EI_NIDENT (16) ++#define ELFMAG "\177ELF" ++ ++#define SELFMAG 4 ++#define EI_CLASS 4 ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ + + int + main(int argc, char **argv) +diff -Nur linux-3.11.5.orig/scripts/mod/modpost.h linux-3.11.5/scripts/mod/modpost.h +--- linux-3.11.5.orig/scripts/mod/modpost.h 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/modpost.h 2013-10-16 18:09:31.000000000 +0200 +@@ -7,7 +7,2453 @@ + #include + #include + #include +-#include ++ ++ ++/* This file defines standard ELF types, structures, and macros. ++ Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++#ifndef _ELF_H ++#define _ELF_H 1 ++ ++__BEGIN_DECLS ++ ++/* Standard ELF types. */ ++ ++#include ++ ++/* Type for a 16-bit quantity. */ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++/* Types for signed and unsigned 32-bit quantities. */ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++/* Types for signed and unsigned 64-bit quantities. */ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++/* Type of addresses. */ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++/* Type of file offsets. */ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++/* Type for section indices, which are 16-bit quantities. */ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++/* Type for version symbol information. */ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++ ++/* The ELF file header. This appears at the start of every ELF file. */ ++ ++#define EI_NIDENT (16) ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf32_Half e_type; /* Object file type */ ++ Elf32_Half e_machine; /* Architecture */ ++ Elf32_Word e_version; /* Object file version */ ++ Elf32_Addr e_entry; /* Entry point virtual address */ ++ Elf32_Off e_phoff; /* Program header table file offset */ ++ Elf32_Off e_shoff; /* Section header table file offset */ ++ Elf32_Word e_flags; /* Processor-specific flags */ ++ Elf32_Half e_ehsize; /* ELF header size in bytes */ ++ Elf32_Half e_phentsize; /* Program header table entry size */ ++ Elf32_Half e_phnum; /* Program header table entry count */ ++ Elf32_Half e_shentsize; /* Section header table entry size */ ++ Elf32_Half e_shnum; /* Section header table entry count */ ++ Elf32_Half e_shstrndx; /* Section header string table index */ ++} Elf32_Ehdr; ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf64_Half e_type; /* Object file type */ ++ Elf64_Half e_machine; /* Architecture */ ++ Elf64_Word e_version; /* Object file version */ ++ Elf64_Addr e_entry; /* Entry point virtual address */ ++ Elf64_Off e_phoff; /* Program header table file offset */ ++ Elf64_Off e_shoff; /* Section header table file offset */ ++ Elf64_Word e_flags; /* Processor-specific flags */ ++ Elf64_Half e_ehsize; /* ELF header size in bytes */ ++ Elf64_Half e_phentsize; /* Program header table entry size */ ++ Elf64_Half e_phnum; /* Program header table entry count */ ++ Elf64_Half e_shentsize; /* Section header table entry size */ ++ Elf64_Half e_shnum; /* Section header table entry count */ ++ Elf64_Half e_shstrndx; /* Section header string table index */ ++} Elf64_Ehdr; ++ ++/* Fields in the e_ident array. The EI_* macros are indices into the ++ array. The macros under each EI_* macro are the values the byte ++ may have. */ ++ ++#define EI_MAG0 0 /* File identification byte 0 index */ ++#define ELFMAG0 0x7f /* Magic number byte 0 */ ++ ++#define EI_MAG1 1 /* File identification byte 1 index */ ++#define ELFMAG1 'E' /* Magic number byte 1 */ ++ ++#define EI_MAG2 2 /* File identification byte 2 index */ ++#define ELFMAG2 'L' /* Magic number byte 2 */ ++ ++#define EI_MAG3 3 /* File identification byte 3 index */ ++#define ELFMAG3 'F' /* Magic number byte 3 */ ++ ++/* Conglomeration of the identification bytes, for easy testing as a word. */ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 /* File class byte index */ ++#define ELFCLASSNONE 0 /* Invalid class */ ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATANONE 0 /* Invalid data encoding */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 /* File version byte index */ ++ /* Value must be EV_CURRENT */ ++ ++#define EI_OSABI 7 /* OS ABI identification */ ++#define ELFOSABI_NONE 0 /* UNIX System V ABI */ ++#define ELFOSABI_SYSV 0 /* Alias. */ ++#define ELFOSABI_HPUX 1 /* HP-UX */ ++#define ELFOSABI_NETBSD 2 /* NetBSD. */ ++#define ELFOSABI_LINUX 3 /* Linux. */ ++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ ++#define ELFOSABI_AIX 7 /* IBM AIX. */ ++#define ELFOSABI_IRIX 8 /* SGI Irix. */ ++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ ++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ ++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ ++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ ++#define ELFOSABI_ARM 97 /* ARM */ ++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ ++ ++#define EI_ABIVERSION 8 /* ABI version */ ++ ++#define EI_PAD 9 /* Byte index of padding bytes */ ++ ++/* Legal values for e_type (object file type). */ ++ ++#define ET_NONE 0 /* No file type */ ++#define ET_REL 1 /* Relocatable file */ ++#define ET_EXEC 2 /* Executable file */ ++#define ET_DYN 3 /* Shared object file */ ++#define ET_CORE 4 /* Core file */ ++#define ET_NUM 5 /* Number of defined types */ ++#define ET_LOOS 0xfe00 /* OS-specific range start */ ++#define ET_HIOS 0xfeff /* OS-specific range end */ ++#define ET_LOPROC 0xff00 /* Processor-specific range start */ ++#define ET_HIPROC 0xffff /* Processor-specific range end */ ++ ++/* Legal values for e_machine (architecture). */ ++ ++#define EM_NONE 0 /* No machine */ ++#define EM_M32 1 /* AT&T WE 32100 */ ++#define EM_SPARC 2 /* SUN SPARC */ ++#define EM_386 3 /* Intel 80386 */ ++#define EM_68K 4 /* Motorola m68k family */ ++#define EM_88K 5 /* Motorola m88k family */ ++#define EM_860 7 /* Intel 80860 */ ++#define EM_MIPS 8 /* MIPS R3000 big-endian */ ++#define EM_S370 9 /* IBM System/370 */ ++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ ++ ++#define EM_PARISC 15 /* HPPA */ ++#define EM_VPP500 17 /* Fujitsu VPP500 */ ++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ ++#define EM_960 19 /* Intel 80960 */ ++#define EM_PPC 20 /* PowerPC */ ++#define EM_PPC64 21 /* PowerPC 64-bit */ ++#define EM_S390 22 /* IBM S390 */ ++ ++#define EM_V800 36 /* NEC V800 series */ ++#define EM_FR20 37 /* Fujitsu FR20 */ ++#define EM_RH32 38 /* TRW RH-32 */ ++#define EM_RCE 39 /* Motorola RCE */ ++#define EM_ARM 40 /* ARM */ ++#define EM_FAKE_ALPHA 41 /* Digital Alpha */ ++#define EM_SH 42 /* Hitachi SH */ ++#define EM_SPARCV9 43 /* SPARC v9 64-bit */ ++#define EM_TRICORE 44 /* Siemens Tricore */ ++#define EM_ARC 45 /* Argonaut RISC Core */ ++#define EM_H8_300 46 /* Hitachi H8/300 */ ++#define EM_H8_300H 47 /* Hitachi H8/300H */ ++#define EM_H8S 48 /* Hitachi H8S */ ++#define EM_H8_500 49 /* Hitachi H8/500 */ ++#define EM_IA_64 50 /* Intel Merced */ ++#define EM_MIPS_X 51 /* Stanford MIPS-X */ ++#define EM_COLDFIRE 52 /* Motorola Coldfire */ ++#define EM_68HC12 53 /* Motorola M68HC12 */ ++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ ++#define EM_PCP 55 /* Siemens PCP */ ++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ ++#define EM_NDR1 57 /* Denso NDR1 microprocessor */ ++#define EM_STARCORE 58 /* Motorola Start*Core processor */ ++#define EM_ME16 59 /* Toyota ME16 processor */ ++#define EM_ST100 60 /* STMicroelectronic ST100 processor */ ++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ ++#define EM_X86_64 62 /* AMD x86-64 architecture */ ++#define EM_PDSP 63 /* Sony DSP Processor */ ++ ++#define EM_FX66 66 /* Siemens FX66 microcontroller */ ++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ ++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ ++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ ++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ ++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ ++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ ++#define EM_SVX 73 /* Silicon Graphics SVx */ ++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ ++#define EM_VAX 75 /* Digital VAX */ ++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ ++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ ++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ ++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ ++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ ++#define EM_HUANY 81 /* Harvard University machine-independent object files */ ++#define EM_PRISM 82 /* SiTera Prism */ ++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ ++#define EM_FR30 84 /* Fujitsu FR30 */ ++#define EM_D10V 85 /* Mitsubishi D10V */ ++#define EM_D30V 86 /* Mitsubishi D30V */ ++#define EM_V850 87 /* NEC v850 */ ++#define EM_M32R 88 /* Mitsubishi M32R */ ++#define EM_MN10300 89 /* Matsushita MN10300 */ ++#define EM_MN10200 90 /* Matsushita MN10200 */ ++#define EM_PJ 91 /* picoJava */ ++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ ++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ ++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ ++#define EM_NUM 95 ++ ++/* If it is necessary to assign new unofficial EM_* values, please ++ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the ++ chances of collision with official or non-GNU unofficial values. */ ++ ++#define EM_ALPHA 0x9026 ++ ++/* Legal values for e_version (version). */ ++ ++#define EV_NONE 0 /* Invalid ELF version */ ++#define EV_CURRENT 1 /* Current version */ ++#define EV_NUM 2 ++ ++/* Section header. */ ++ ++typedef struct ++{ ++ Elf32_Word sh_name; /* Section name (string tbl index) */ ++ Elf32_Word sh_type; /* Section type */ ++ Elf32_Word sh_flags; /* Section flags */ ++ Elf32_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf32_Off sh_offset; /* Section file offset */ ++ Elf32_Word sh_size; /* Section size in bytes */ ++ Elf32_Word sh_link; /* Link to another section */ ++ Elf32_Word sh_info; /* Additional section information */ ++ Elf32_Word sh_addralign; /* Section alignment */ ++ Elf32_Word sh_entsize; /* Entry size if section holds table */ ++} Elf32_Shdr; ++ ++typedef struct ++{ ++ Elf64_Word sh_name; /* Section name (string tbl index) */ ++ Elf64_Word sh_type; /* Section type */ ++ Elf64_Xword sh_flags; /* Section flags */ ++ Elf64_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf64_Off sh_offset; /* Section file offset */ ++ Elf64_Xword sh_size; /* Section size in bytes */ ++ Elf64_Word sh_link; /* Link to another section */ ++ Elf64_Word sh_info; /* Additional section information */ ++ Elf64_Xword sh_addralign; /* Section alignment */ ++ Elf64_Xword sh_entsize; /* Entry size if section holds table */ ++} Elf64_Shdr; ++ ++/* Special section indices. */ ++ ++#define SHN_UNDEF 0 /* Undefined section */ ++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ ++#define SHN_LOPROC 0xff00 /* Start of processor-specific */ ++#define SHN_HIPROC 0xff1f /* End of processor-specific */ ++#define SHN_LOOS 0xff20 /* Start of OS-specific */ ++#define SHN_HIOS 0xff3f /* End of OS-specific */ ++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ ++#define SHN_COMMON 0xfff2 /* Associated symbol is common */ ++#define SHN_XINDEX 0xffff /* Index is in extra table. */ ++#define SHN_HIRESERVE 0xffff /* End of reserved indices */ ++ ++/* Legal values for sh_type (section type). */ ++ ++#define SHT_NULL 0 /* Section header table entry unused */ ++#define SHT_PROGBITS 1 /* Program data */ ++#define SHT_SYMTAB 2 /* Symbol table */ ++#define SHT_STRTAB 3 /* String table */ ++#define SHT_RELA 4 /* Relocation entries with addends */ ++#define SHT_HASH 5 /* Symbol hash table */ ++#define SHT_DYNAMIC 6 /* Dynamic linking information */ ++#define SHT_NOTE 7 /* Notes */ ++#define SHT_NOBITS 8 /* Program space with no data (bss) */ ++#define SHT_REL 9 /* Relocation entries, no addends */ ++#define SHT_SHLIB 10 /* Reserved */ ++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ ++#define SHT_INIT_ARRAY 14 /* Array of constructors */ ++#define SHT_FINI_ARRAY 15 /* Array of destructors */ ++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ ++#define SHT_GROUP 17 /* Section group */ ++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ ++#define SHT_NUM 19 /* Number of defined types. */ ++#define SHT_LOOS 0x60000000 /* Start OS-specific */ ++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ ++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ ++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ ++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ ++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ ++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ ++#define SHT_HIOS 0x6fffffff /* End OS-specific type */ ++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define SHT_LOUSER 0x80000000 /* Start of application-specific */ ++#define SHT_HIUSER 0x8fffffff /* End of application-specific */ ++ ++/* Legal values for sh_flags (section flags). */ ++ ++#define SHF_WRITE (1 << 0) /* Writable */ ++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ ++#define SHF_EXECINSTR (1 << 2) /* Executable */ ++#define SHF_MERGE (1 << 4) /* Might be merged */ ++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ ++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ ++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ ++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling ++ required */ ++#define SHF_GROUP (1 << 9) /* Section is member of a group. */ ++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ ++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ ++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Section group handling. */ ++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ ++ ++/* Symbol table entry. */ ++ ++typedef struct ++{ ++ Elf32_Word st_name; /* Symbol name (string tbl index) */ ++ Elf32_Addr st_value; /* Symbol value */ ++ Elf32_Word st_size; /* Symbol size */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf32_Section st_shndx; /* Section index */ ++} Elf32_Sym; ++ ++typedef struct ++{ ++ Elf64_Word st_name; /* Symbol name (string tbl index) */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf64_Section st_shndx; /* Section index */ ++ Elf64_Addr st_value; /* Symbol value */ ++ Elf64_Xword st_size; /* Symbol size */ ++} Elf64_Sym; ++ ++/* The syminfo section if available contains additional information about ++ every dynamic symbol. */ ++ ++typedef struct ++{ ++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf32_Half si_flags; /* Per symbol flags */ ++} Elf32_Syminfo; ++ ++typedef struct ++{ ++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf64_Half si_flags; /* Per symbol flags */ ++} Elf64_Syminfo; ++ ++/* Possible values for si_boundto. */ ++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ ++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ ++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ ++ ++/* Possible bitmasks for si_flags. */ ++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ ++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ ++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ ++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy ++ loaded */ ++/* Syminfo version values. */ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++ ++/* How to extract and insert information held in the st_info field. */ ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++/* Legal values for ST_BIND subfield of st_info (symbol binding). */ ++ ++#define STB_LOCAL 0 /* Local symbol */ ++#define STB_GLOBAL 1 /* Global symbol */ ++#define STB_WEAK 2 /* Weak symbol */ ++#define STB_NUM 3 /* Number of defined types. */ ++#define STB_LOOS 10 /* Start of OS-specific */ ++#define STB_HIOS 12 /* End of OS-specific */ ++#define STB_LOPROC 13 /* Start of processor-specific */ ++#define STB_HIPROC 15 /* End of processor-specific */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_NOTYPE 0 /* Symbol type is unspecified */ ++#define STT_OBJECT 1 /* Symbol is a data object */ ++#define STT_FUNC 2 /* Symbol is a code object */ ++#define STT_SECTION 3 /* Symbol associated with a section */ ++#define STT_FILE 4 /* Symbol's name is file name */ ++#define STT_COMMON 5 /* Symbol is a common data object */ ++#define STT_TLS 6 /* Symbol is thread-local data object*/ ++#define STT_NUM 7 /* Number of defined types. */ ++#define STT_LOOS 10 /* Start of OS-specific */ ++#define STT_HIOS 12 /* End of OS-specific */ ++#define STT_LOPROC 13 /* Start of processor-specific */ ++#define STT_HIPROC 15 /* End of processor-specific */ ++ ++ ++/* Symbol table indices are found in the hash buckets and chain table ++ of a symbol hash table section. This special index value indicates ++ the end of a chain, meaning no further symbols are found in that bucket. */ ++ ++#define STN_UNDEF 0 /* End of a chain. */ ++ ++ ++/* How to extract and insert information held in the st_other field. */ ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++ ++/* For ELF64 the definitions are the same. */ ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++/* Symbol visibility specification encoded in the st_other field. */ ++#define STV_DEFAULT 0 /* Default symbol visibility rules */ ++#define STV_INTERNAL 1 /* Processor specific hidden class */ ++#define STV_HIDDEN 2 /* Sym unavailable in other modules */ ++#define STV_PROTECTED 3 /* Not preemptible, not exported */ ++ ++ ++/* Relocation table entry without addend (in section of type SHT_REL). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++} Elf32_Rel; ++ ++/* I have seen two different definitions of the Elf64_Rel and ++ Elf64_Rela structures, so we'll leave them out until Novell (or ++ whoever) gets their act together. */ ++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++} Elf64_Rel; ++ ++/* Relocation table entry with addend (in section of type SHT_RELA). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++ Elf32_Sword r_addend; /* Addend */ ++} Elf32_Rela; ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++ Elf64_Sxword r_addend; /* Addend */ ++} Elf64_Rela; ++ ++/* How to extract and insert information held in the r_info field. */ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++/* Program segment header. */ ++ ++typedef struct ++{ ++ Elf32_Word p_type; /* Segment type */ ++ Elf32_Off p_offset; /* Segment file offset */ ++ Elf32_Addr p_vaddr; /* Segment virtual address */ ++ Elf32_Addr p_paddr; /* Segment physical address */ ++ Elf32_Word p_filesz; /* Segment size in file */ ++ Elf32_Word p_memsz; /* Segment size in memory */ ++ Elf32_Word p_flags; /* Segment flags */ ++ Elf32_Word p_align; /* Segment alignment */ ++} Elf32_Phdr; ++ ++typedef struct ++{ ++ Elf64_Word p_type; /* Segment type */ ++ Elf64_Word p_flags; /* Segment flags */ ++ Elf64_Off p_offset; /* Segment file offset */ ++ Elf64_Addr p_vaddr; /* Segment virtual address */ ++ Elf64_Addr p_paddr; /* Segment physical address */ ++ Elf64_Xword p_filesz; /* Segment size in file */ ++ Elf64_Xword p_memsz; /* Segment size in memory */ ++ Elf64_Xword p_align; /* Segment alignment */ ++} Elf64_Phdr; ++ ++/* Legal values for p_type (segment type). */ ++ ++#define PT_NULL 0 /* Program header table entry unused */ ++#define PT_LOAD 1 /* Loadable program segment */ ++#define PT_DYNAMIC 2 /* Dynamic linking information */ ++#define PT_INTERP 3 /* Program interpreter */ ++#define PT_NOTE 4 /* Auxiliary information */ ++#define PT_SHLIB 5 /* Reserved */ ++#define PT_PHDR 6 /* Entry for header table itself */ ++#define PT_TLS 7 /* Thread-local storage segment */ ++#define PT_NUM 8 /* Number of defined types */ ++#define PT_LOOS 0x60000000 /* Start of OS-specific */ ++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ ++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ ++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff /* End of OS-specific */ ++#define PT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define PT_HIPROC 0x7fffffff /* End of processor-specific */ ++ ++/* Legal values for p_flags (segment flags). */ ++ ++#define PF_X (1 << 0) /* Segment is executable */ ++#define PF_W (1 << 1) /* Segment is writable */ ++#define PF_R (1 << 2) /* Segment is readable */ ++#define PF_MASKOS 0x0ff00000 /* OS-specific */ ++#define PF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Legal values for note segment descriptor types for core files. */ ++ ++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ ++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ ++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ ++#define NT_PRXREG 4 /* Contains copy of prxregset struct */ ++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ ++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ ++#define NT_AUXV 6 /* Contains copy of auxv array */ ++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ ++#define NT_ASRS 8 /* Contains copy of asrset struct */ ++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ ++#define NT_PSINFO 13 /* Contains copy of psinfo struct */ ++#define NT_PRCRED 14 /* Contains copy of prcred struct */ ++#define NT_UTSNAME 15 /* Contains copy of utsname struct */ ++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ ++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ ++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ ++ ++/* Legal values for the note segment descriptor types for object files. */ ++ ++#define NT_VERSION 1 /* Contains a version string. */ ++ ++ ++/* Dynamic section entry. */ ++ ++typedef struct ++{ ++ Elf32_Sword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf32_Word d_val; /* Integer value */ ++ Elf32_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct ++{ ++ Elf64_Sxword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf64_Xword d_val; /* Integer value */ ++ Elf64_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf64_Dyn; ++ ++/* Legal values for d_tag (dynamic entry type). */ ++ ++#define DT_NULL 0 /* Marks end of dynamic section */ ++#define DT_NEEDED 1 /* Name of needed library */ ++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ ++#define DT_PLTGOT 3 /* Processor defined value */ ++#define DT_HASH 4 /* Address of symbol hash table */ ++#define DT_STRTAB 5 /* Address of string table */ ++#define DT_SYMTAB 6 /* Address of symbol table */ ++#define DT_RELA 7 /* Address of Rela relocs */ ++#define DT_RELASZ 8 /* Total size of Rela relocs */ ++#define DT_RELAENT 9 /* Size of one Rela reloc */ ++#define DT_STRSZ 10 /* Size of string table */ ++#define DT_SYMENT 11 /* Size of one symbol table entry */ ++#define DT_INIT 12 /* Address of init function */ ++#define DT_FINI 13 /* Address of termination function */ ++#define DT_SONAME 14 /* Name of shared object */ ++#define DT_RPATH 15 /* Library search path (deprecated) */ ++#define DT_SYMBOLIC 16 /* Start symbol search here */ ++#define DT_REL 17 /* Address of Rel relocs */ ++#define DT_RELSZ 18 /* Total size of Rel relocs */ ++#define DT_RELENT 19 /* Size of one Rel reloc */ ++#define DT_PLTREL 20 /* Type of reloc in PLT */ ++#define DT_DEBUG 21 /* For debugging; unspecified */ ++#define DT_TEXTREL 22 /* Reloc might modify .text */ ++#define DT_JMPREL 23 /* Address of PLT relocs */ ++#define DT_BIND_NOW 24 /* Process relocations of object */ ++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ ++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ ++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ ++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ ++#define DT_RUNPATH 29 /* Library search path */ ++#define DT_FLAGS 30 /* Flags for the object being loaded */ ++#define DT_ENCODING 32 /* Start of encoded range */ ++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ ++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ ++#define DT_NUM 34 /* Number used */ ++#define DT_LOOS 0x6000000d /* Start of OS-specific */ ++#define DT_HIOS 0x6ffff000 /* End of OS-specific */ ++#define DT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define DT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ ++ ++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the ++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's ++ approach. */ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ ++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting ++ the following DT_* entry. */ ++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ ++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ ++#define DT_VALNUM 12 ++ ++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the ++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. ++ ++ If any adjustment is made to the ELF object after it has been ++ built these entries will need to be adjusted. */ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ ++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ ++#define DT_CONFIG 0x6ffffefa /* Configuration information. */ ++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ ++#define DT_AUDIT 0x6ffffefc /* Object auditing. */ ++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ ++#define DT_MOVETAB 0x6ffffefe /* Move table. */ ++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ ++#define DT_ADDRNUM 10 ++ ++/* The versioning entry types. The next are defined as part of the ++ GNU extension. */ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++/* These were chosen by Sun. */ ++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ ++#define DT_VERDEF 0x6ffffffc /* Address of version definition ++ table */ ++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ ++#define DT_VERNEED 0x6ffffffe /* Address of table with needed ++ versions */ ++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ ++#define DT_VERSIONTAGNUM 16 ++ ++/* Sun added these machine-independent extensions in the "processor-specific" ++ range. Be compatible. */ ++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ ++#define DT_FILTER 0x7fffffff /* Shared object to get values from */ ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++/* Values of `d_un.d_val' in the DT_FLAGS entry. */ ++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ ++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ ++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ ++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ ++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ ++ ++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 ++ entry in the dynamic section. */ ++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ ++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ ++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ ++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ ++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ ++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ ++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ ++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ ++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ ++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ ++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ ++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ ++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ ++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ ++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ ++ ++/* Flags for the feature selection in DT_FEATURE_1. */ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ ++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ ++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not ++ generally available. */ ++ ++/* Version definition sections. */ ++ ++typedef struct ++{ ++ Elf32_Half vd_version; /* Version revision */ ++ Elf32_Half vd_flags; /* Version information */ ++ Elf32_Half vd_ndx; /* Version Index */ ++ Elf32_Half vd_cnt; /* Number of associated aux entries */ ++ Elf32_Word vd_hash; /* Version name hash value */ ++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf32_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf32_Verdef; ++ ++typedef struct ++{ ++ Elf64_Half vd_version; /* Version revision */ ++ Elf64_Half vd_flags; /* Version information */ ++ Elf64_Half vd_ndx; /* Version Index */ ++ Elf64_Half vd_cnt; /* Number of associated aux entries */ ++ Elf64_Word vd_hash; /* Version name hash value */ ++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf64_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf64_Verdef; ++ ++ ++/* Legal values for vd_version (version revision). */ ++#define VER_DEF_NONE 0 /* No version */ ++#define VER_DEF_CURRENT 1 /* Current version */ ++#define VER_DEF_NUM 2 /* Given version number */ ++ ++/* Legal values for vd_flags (version information flags). */ ++#define VER_FLG_BASE 0x1 /* Version definition of file itself */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++/* Versym symbol index values. */ ++#define VER_NDX_LOCAL 0 /* Symbol is local. */ ++#define VER_NDX_GLOBAL 1 /* Symbol is global. */ ++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ ++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ ++ ++/* Auxialiary version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vda_name; /* Version or dependency names */ ++ Elf32_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf32_Verdaux; ++ ++typedef struct ++{ ++ Elf64_Word vda_name; /* Version or dependency names */ ++ Elf64_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf64_Verdaux; ++ ++ ++/* Version dependency section. */ ++ ++typedef struct ++{ ++ Elf32_Half vn_version; /* Version of structure */ ++ Elf32_Half vn_cnt; /* Number of associated aux entries */ ++ Elf32_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf32_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf32_Verneed; ++ ++typedef struct ++{ ++ Elf64_Half vn_version; /* Version of structure */ ++ Elf64_Half vn_cnt; /* Number of associated aux entries */ ++ Elf64_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf64_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf64_Verneed; ++ ++ ++/* Legal values for vn_version (version revision). */ ++#define VER_NEED_NONE 0 /* No version */ ++#define VER_NEED_CURRENT 1 /* Current version */ ++#define VER_NEED_NUM 2 /* Given version number */ ++ ++/* Auxiliary needed version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vna_hash; /* Hash value of dependency name */ ++ Elf32_Half vna_flags; /* Dependency specific information */ ++ Elf32_Half vna_other; /* Unused */ ++ Elf32_Word vna_name; /* Dependency name string offset */ ++ Elf32_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf32_Vernaux; ++ ++typedef struct ++{ ++ Elf64_Word vna_hash; /* Hash value of dependency name */ ++ Elf64_Half vna_flags; /* Dependency specific information */ ++ Elf64_Half vna_other; /* Unused */ ++ Elf64_Word vna_name; /* Dependency name string offset */ ++ Elf64_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf64_Vernaux; ++ ++ ++/* Legal values for vna_flags. */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++ ++/* Auxiliary vector. */ ++ ++/* This vector is normally only used by the program interpreter. The ++ usual definition in an ABI supplement uses the name auxv_t. The ++ vector is not usually defined in a standard file, but it ++ can't hurt. We rename it to avoid conflicts. The sizes of these ++ types are an arrangement between the exec server and the program ++ interpreter, so we don't fully specify them here. */ ++ ++typedef struct ++{ ++ int a_type; /* Entry type */ ++ union ++ { ++ long int a_val; /* Integer value */ ++ void *a_ptr; /* Pointer value */ ++ void (*a_fcn) (void); /* Function pointer value */ ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct ++{ ++ long int a_type; /* Entry type */ ++ union ++ { ++ long int a_val; /* Integer value */ ++ void *a_ptr; /* Pointer value */ ++ void (*a_fcn) (void); /* Function pointer value */ ++ } a_un; ++} Elf64_auxv_t; ++ ++/* Legal values for a_type (entry type). */ ++ ++#define AT_NULL 0 /* End of vector */ ++#define AT_IGNORE 1 /* Entry should be ignored */ ++#define AT_EXECFD 2 /* File descriptor of program */ ++#define AT_PHDR 3 /* Program headers for program */ ++#define AT_PHENT 4 /* Size of program header entry */ ++#define AT_PHNUM 5 /* Number of program headers */ ++#define AT_PAGESZ 6 /* System page size */ ++#define AT_BASE 7 /* Base address of interpreter */ ++#define AT_FLAGS 8 /* Flags */ ++#define AT_ENTRY 9 /* Entry point of program */ ++#define AT_NOTELF 10 /* Program is not ELF */ ++#define AT_UID 11 /* Real uid */ ++#define AT_EUID 12 /* Effective uid */ ++#define AT_GID 13 /* Real gid */ ++#define AT_EGID 14 /* Effective gid */ ++#define AT_CLKTCK 17 /* Frequency of times() */ ++ ++/* Some more special a_type values describing the hardware. */ ++#define AT_PLATFORM 15 /* String identifying platform. */ ++#define AT_HWCAP 16 /* Machine dependent hints about ++ processor capabilities. */ ++ ++/* This entry gives some information about the FPU initialization ++ performed by the kernel. */ ++#define AT_FPUCW 18 /* Used FPU control word. */ ++ ++/* Cache block sizes. */ ++#define AT_DCACHEBSIZE 19 /* Data cache block size. */ ++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ ++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ ++ ++/* A special ignored value for PPC, used by the kernel to control the ++ interpretation of the AUXV. Must be > 16. */ ++#define AT_IGNOREPPC 22 /* Entry should be ignored. */ ++ ++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ ++ ++/* Pointer to the global system page used for system calls and other ++ nice things. */ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++ ++/* Note section contents. Each entry in the note section begins with ++ a header of a fixed form. */ ++ ++typedef struct ++{ ++ Elf32_Word n_namesz; /* Length of the note's name. */ ++ Elf32_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf32_Word n_type; /* Type of the note. */ ++} Elf32_Nhdr; ++ ++typedef struct ++{ ++ Elf64_Word n_namesz; /* Length of the note's name. */ ++ Elf64_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf64_Word n_type; /* Type of the note. */ ++} Elf64_Nhdr; ++ ++/* Known names of notes. */ ++ ++/* Solaris entries in the note section have this name. */ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++/* Note entries for GNU systems have this name. */ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++/* Defined types of notes for Solaris. */ ++ ++/* Value of descriptor (one word) is desired pagesize for the binary. */ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++/* Defined note types for GNU systems. */ ++ ++/* ABI information. The descriptor consists of words: ++ word 0: OS descriptor ++ word 1: major version of the ABI ++ word 2: minor version of the ABI ++ word 3: subminor version of the ABI ++*/ ++#define ELF_NOTE_ABI 1 ++ ++/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI ++ note section entry. */ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++ ++/* Move records. */ ++typedef struct ++{ ++ Elf32_Xword m_value; /* Symbol value. */ ++ Elf32_Word m_info; /* Size and index. */ ++ Elf32_Word m_poffset; /* Symbol offset. */ ++ Elf32_Half m_repeat; /* Repeat count. */ ++ Elf32_Half m_stride; /* Stride info. */ ++} Elf32_Move; ++ ++typedef struct ++{ ++ Elf64_Xword m_value; /* Symbol value. */ ++ Elf64_Xword m_info; /* Size and index. */ ++ Elf64_Xword m_poffset; /* Symbol offset. */ ++ Elf64_Half m_repeat; /* Repeat count. */ ++ Elf64_Half m_stride; /* Stride info. */ ++} Elf64_Move; ++ ++/* Macro to construct move records. */ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++ ++/* Motorola 68k specific definitions. */ ++ ++/* Values for Elf32_Ehdr.e_flags. */ ++#define EF_CPU32 0x00810000 ++ ++/* m68k relocs. */ ++ ++#define R_68K_NONE 0 /* No reloc */ ++#define R_68K_32 1 /* Direct 32 bit */ ++#define R_68K_16 2 /* Direct 16 bit */ ++#define R_68K_8 3 /* Direct 8 bit */ ++#define R_68K_PC32 4 /* PC relative 32 bit */ ++#define R_68K_PC16 5 /* PC relative 16 bit */ ++#define R_68K_PC8 6 /* PC relative 8 bit */ ++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ ++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ ++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ ++#define R_68K_GOT32O 10 /* 32 bit GOT offset */ ++#define R_68K_GOT16O 11 /* 16 bit GOT offset */ ++#define R_68K_GOT8O 12 /* 8 bit GOT offset */ ++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ ++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ ++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ ++#define R_68K_PLT32O 16 /* 32 bit PLT offset */ ++#define R_68K_PLT16O 17 /* 16 bit PLT offset */ ++#define R_68K_PLT8O 18 /* 8 bit PLT offset */ ++#define R_68K_COPY 19 /* Copy symbol at runtime */ ++#define R_68K_GLOB_DAT 20 /* Create GOT entry */ ++#define R_68K_JMP_SLOT 21 /* Create PLT entry */ ++#define R_68K_RELATIVE 22 /* Adjust by program base */ ++/* Keep this the last entry. */ ++#define R_68K_NUM 23 ++ ++/* Intel 80386 specific definitions. */ ++ ++/* i386 relocs. */ ++ ++#define R_386_NONE 0 /* No reloc */ ++#define R_386_32 1 /* Direct 32 bit */ ++#define R_386_PC32 2 /* PC relative 32 bit */ ++#define R_386_GOT32 3 /* 32 bit GOT entry */ ++#define R_386_PLT32 4 /* 32 bit PLT address */ ++#define R_386_COPY 5 /* Copy symbol at runtime */ ++#define R_386_GLOB_DAT 6 /* Create GOT entry */ ++#define R_386_JMP_SLOT 7 /* Create PLT entry */ ++#define R_386_RELATIVE 8 /* Adjust by program base */ ++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ ++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ ++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS ++ block offset */ ++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block ++ offset */ ++#define R_386_TLS_LE 17 /* Offset relative to static TLS ++ block */ ++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of ++ general dynamic thread local data */ ++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of ++ local dynamic thread local data ++ in LE code */ ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic ++ thread local data */ ++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ ++#define R_386_TLS_GD_CALL 26 /* Relocation for call to ++ __tls_get_addr() */ ++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ ++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic ++ thread local data in LE code */ ++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ ++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to ++ __tls_get_addr() in LDM code */ ++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ ++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ ++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS ++ block offset */ ++#define R_386_TLS_LE_32 34 /* Negated offset relative to static ++ TLS block */ ++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ ++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ ++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ ++/* Keep this the last entry. */ ++#define R_386_NUM 38 ++ ++/* SUN SPARC specific definitions. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_REGISTER 13 /* Global register reserved to app. */ ++ ++/* Values for Elf64_Ehdr.e_flags. */ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 /* little endian data */ ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ ++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ ++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ ++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ ++ ++/* SPARC relocs. */ ++ ++#define R_SPARC_NONE 0 /* No reloc */ ++#define R_SPARC_8 1 /* Direct 8 bit */ ++#define R_SPARC_16 2 /* Direct 16 bit */ ++#define R_SPARC_32 3 /* Direct 32 bit */ ++#define R_SPARC_DISP8 4 /* PC relative 8 bit */ ++#define R_SPARC_DISP16 5 /* PC relative 16 bit */ ++#define R_SPARC_DISP32 6 /* PC relative 32 bit */ ++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ ++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ ++#define R_SPARC_HI22 9 /* High 22 bit */ ++#define R_SPARC_22 10 /* Direct 22 bit */ ++#define R_SPARC_13 11 /* Direct 13 bit */ ++#define R_SPARC_LO10 12 /* Truncated 10 bit */ ++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ ++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ ++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ ++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ ++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ ++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ ++#define R_SPARC_COPY 19 /* Copy symbol at runtime */ ++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ ++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ ++#define R_SPARC_RELATIVE 22 /* Adjust by program base */ ++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ ++ ++/* Additional Sparc64 relocs. */ ++ ++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ ++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ ++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ ++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ ++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ ++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ ++#define R_SPARC_10 30 /* Direct 10 bit */ ++#define R_SPARC_11 31 /* Direct 11 bit */ ++#define R_SPARC_64 32 /* Direct 64 bit */ ++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ ++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ ++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ ++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ ++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ ++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ ++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ ++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ ++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ ++#define R_SPARC_7 43 /* Direct 7 bit */ ++#define R_SPARC_5 44 /* Direct 5 bit */ ++#define R_SPARC_6 45 /* Direct 6 bit */ ++#define R_SPARC_DISP64 46 /* PC relative 64 bit */ ++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ ++#define R_SPARC_HIX22 48 /* High 22 bit complemented */ ++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ ++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ ++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ ++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ ++#define R_SPARC_REGISTER 53 /* Global register usage */ ++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ ++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++/* Keep this the last entry. */ ++#define R_SPARC_NUM 80 ++ ++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++/* Bits present in AT_HWCAP, primarily for Sparc32. */ ++ ++#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ ++#define HWCAP_SPARC_STBAR 2 ++#define HWCAP_SPARC_SWAP 4 ++#define HWCAP_SPARC_MULDIV 8 ++#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ ++#define HWCAP_SPARC_ULTRA3 32 ++ ++/* MIPS R3000 specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ ++#define EF_MIPS_PIC 2 /* Contains PIC code */ ++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ ++ ++/* Legal values for MIPS architecture level. */ ++ ++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* The following are non-official names and should not be used. */ ++ ++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* Special section indices. */ ++ ++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ ++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ ++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ ++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ ++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ ++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ ++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ ++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ ++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++/* Symbol tables. */ ++ ++/* MIPS specific values for `st_other'. */ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++/* MIPS specific values for `st_info'. */ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++/* Entries found in sections of type SHT_MIPS_GPTAB. */ ++ ++typedef union ++{ ++ struct ++ { ++ Elf32_Word gt_current_g_value; /* -G value used for compilation */ ++ Elf32_Word gt_unused; /* Not used */ ++ } gt_header; /* First entry in section */ ++ struct ++ { ++ Elf32_Word gt_g_value; /* If this value were used for -G */ ++ Elf32_Word gt_bytes; /* This many bytes would be used */ ++ } gt_entry; /* Subsequent entries in section */ ++} Elf32_gptab; ++ ++/* Entry found in sections of type SHT_MIPS_REGINFO. */ ++ ++typedef struct ++{ ++ Elf32_Word ri_gprmask; /* General registers used */ ++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ ++ Elf32_Sword ri_gp_value; /* $gp register value */ ++} Elf32_RegInfo; ++ ++/* Entries found in sections of type SHT_MIPS_OPTIONS. */ ++ ++typedef struct ++{ ++ unsigned char kind; /* Determines interpretation of the ++ variable part of descriptor. */ ++ unsigned char size; /* Size of descriptor, including header. */ ++ Elf32_Section section; /* Section header index of section affected, ++ 0 for global options. */ ++ Elf32_Word info; /* Kind-specific information. */ ++} Elf_Options; ++ ++/* Values for `kind' field in Elf_Options. */ ++ ++#define ODK_NULL 0 /* Undefined. */ ++#define ODK_REGINFO 1 /* Register usage information. */ ++#define ODK_EXCEPTIONS 2 /* Exception processing options. */ ++#define ODK_PAD 3 /* Section padding options. */ ++#define ODK_HWPATCH 4 /* Hardware workarounds performed */ ++#define ODK_FILL 5 /* record the fill value used by the linker. */ ++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ ++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ ++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ ++ ++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ ++ ++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ ++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ ++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ ++#define OEX_SMM 0x20000 /* Force sequential memory mode? */ ++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ ++ ++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ ++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ ++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ ++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++/* Entry found in `.options' section. */ ++ ++typedef struct ++{ ++ Elf32_Word hwp_flags1; /* Extra flags. */ ++ Elf32_Word hwp_flags2; /* Extra flags. */ ++} Elf_Options_Hw; ++ ++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++/* MIPS relocs. */ ++ ++#define R_MIPS_NONE 0 /* No reloc */ ++#define R_MIPS_16 1 /* Direct 16 bit */ ++#define R_MIPS_32 2 /* Direct 32 bit */ ++#define R_MIPS_REL32 3 /* PC relative 32 bit */ ++#define R_MIPS_26 4 /* Direct 26 bit shifted */ ++#define R_MIPS_HI16 5 /* High 16 bit */ ++#define R_MIPS_LO16 6 /* Low 16 bit */ ++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ ++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ ++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ ++#define R_MIPS_PC16 10 /* PC relative 16 bit */ ++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ ++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++/* Keep this the last entry. */ ++#define R_MIPS_NUM 38 ++ ++/* Legal values for p_type field of Elf32_Phdr. */ ++ ++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ ++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++/* Special program header types. */ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++/* Legal values for d_tag field of Elf32_Dyn. */ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ ++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ ++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ ++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ ++#define DT_MIPS_FLAGS 0x70000005 /* Flags */ ++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ ++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ ++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ ++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ ++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ ++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ ++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ ++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ ++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ ++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in ++ DT_MIPS_DELTA_CLASS. */ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in ++ DT_MIPS_DELTA_INSTANCE. */ ++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in ++ DT_MIPS_DELTA_RELOC. */ ++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta ++ relocations refer to. */ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in ++ DT_MIPS_DELTA_SYM. */ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the ++ class declaration. */ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in ++ DT_MIPS_DELTA_CLASSSYM. */ ++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ ++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve ++ function stored in GOT. */ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added ++ by rld on dlopen() calls. */ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ ++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ ++#define DT_MIPS_NUM 0x32 ++ ++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ ++ ++#define RHF_NONE 0 /* No flags */ ++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ ++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++/* Entries found in sections of type SHT_MIPS_LIBLIST. */ ++ ++typedef struct ++{ ++ Elf32_Word l_name; /* Name (string table index) */ ++ Elf32_Word l_time_stamp; /* Timestamp */ ++ Elf32_Word l_checksum; /* Checksum */ ++ Elf32_Word l_version; /* Interface version */ ++ Elf32_Word l_flags; /* Flags */ ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; /* Name (string table index) */ ++ Elf64_Word l_time_stamp; /* Timestamp */ ++ Elf64_Word l_checksum; /* Checksum */ ++ Elf64_Word l_version; /* Interface version */ ++ Elf64_Word l_flags; /* Flags */ ++} Elf64_Lib; ++ ++ ++/* Legal values for l_flags. */ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ ++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++/* Entries found in sections of type SHT_MIPS_CONFLICT. */ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++/* HPPA specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ ++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ ++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ ++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ ++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch ++ prediction. */ ++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ ++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ ++ ++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ ++ ++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ ++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ ++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ ++ ++/* Additional section indeces. */ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared ++ symbols in ANSI C. */ ++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ ++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ ++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ ++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ ++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++/* HPPA relocs. */ ++ ++#define R_PARISC_NONE 0 /* No reloc. */ ++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ ++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ ++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ ++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ ++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ ++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ ++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ ++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ ++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ ++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ ++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ ++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ ++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ ++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ ++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ ++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ ++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ ++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ ++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ ++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ ++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ ++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ ++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ ++#define R_PARISC_FPTR64 64 /* 64 bits function address. */ ++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ ++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ ++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ ++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ ++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ ++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ ++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ ++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ ++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ ++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ ++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ ++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ ++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ ++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ ++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ ++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 /* Copy relocation. */ ++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ ++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ ++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ ++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ ++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ ++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ ++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ ++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ ++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_HIRESERVE 255 ++ ++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++/* Alpha specific definitions. */ ++ ++/* Legal values for e_flags field of Elf64_Ehdr. */ ++ ++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ ++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ ++ ++/* Legal values for sh_type field of Elf64_Shdr. */ ++ ++/* These two are primerily concerned with ECOFF debugging info. */ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++/* Legal values for sh_flags field of Elf64_Shdr. */ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++/* Legal values for st_other field of Elf64_Sym. */ ++#define STO_ALPHA_NOPV 0x80 /* No PV required. */ ++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ ++ ++/* Alpha relocs. */ ++ ++#define R_ALPHA_NONE 0 /* No reloc */ ++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ ++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ ++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ ++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ ++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ ++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ ++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ ++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ ++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ ++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ ++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ ++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ ++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ ++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ ++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ ++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ ++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ ++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++/* Keep this the last entry. */ ++#define R_ALPHA_NUM 46 ++ ++/* Magic values of the LITUSE relocation addend. */ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++ ++/* PowerPC specific declarations */ ++ ++/* Values for Elf32/64_Ehdr.e_flags. */ ++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ ++ ++/* Cygnus local bits below */ ++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib ++ flag */ ++ ++/* PowerPC relocations defined by the ABIs */ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 /* 32bit absolute address */ ++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ ++#define R_PPC_ADDR16 3 /* 16bit absolute address */ ++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ ++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ ++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ ++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 /* PC relative 26 bit */ ++#define R_PPC_REL14 11 /* PC relative 16 bit */ ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++/* PowerPC relocations defined for the TLS access ABI. */ ++#define R_PPC_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ ++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ ++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ ++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ ++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ ++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ ++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ ++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ ++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ ++ ++/* Keep this the last entry. */ ++#define R_PPC_NUM 95 ++ ++/* The remaining relocs are from the Embedded ELF ABI, and are not ++ in the SVR4 ELF ABI. */ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ ++ ++/* Diab tool relocations. */ ++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ ++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ ++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ ++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ ++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ ++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ ++ ++/* This is a phony reloc to handle any old fashioned TOC16 references ++ that may still be in object files. */ ++#define R_PPC_TOC16 255 ++ ++ ++/* PowerPC64 relocations defined by the ABIs */ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ ++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ ++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ ++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ ++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ ++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ ++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ ++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ ++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ ++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ ++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ ++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ ++#define R_PPC64_PLT64 45 /* doubleword64 L + A */ ++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ ++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ ++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ ++#define R_PPC64_TOC 51 /* doubleword64 .TOC */ ++#define R_PPC64_PLTGOT16 52 /* half16* M + A */ ++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ ++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ ++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ ++ ++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ ++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ ++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ ++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ ++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ ++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ ++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ ++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ ++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ ++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ ++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ ++ ++/* PowerPC64 relocations defined for the TLS access ABI. */ ++#define R_PPC64_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ ++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ ++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ ++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ ++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ ++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ ++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ ++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ ++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ ++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ ++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ ++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ ++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ ++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ ++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ ++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ ++ ++/* Keep this the last entry. */ ++#define R_PPC64_NUM 107 ++ ++/* PowerPC64 specific values for the Dyn d_tag field. */ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_NUM 1 ++ ++ ++/* ARM specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++ ++/* Other constants defined in the ARM ELF spec. version B-01. */ ++/* NB. These conflict with values defined above. */ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++ ++/* Additional symbol types for Thumb */ ++#define STT_ARM_TFUNC 0xd ++ ++/* ARM-specific values for sh_flags */ ++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ ++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined ++ in the input to a link step */ ++ ++/* ARM-specific program header flags */ ++#define PF_ARM_SB 0x10000000 /* Segment contains the location ++ addressed by the static base */ ++ ++/* ARM relocs. */ ++#define R_ARM_NONE 0 /* No reloc */ ++#define R_ARM_PC24 1 /* PC relative 26 bit branch */ ++#define R_ARM_ABS32 2 /* Direct 32 bit */ ++#define R_ARM_REL32 3 /* PC relative 32 bit */ ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 /* Direct 16 bit */ ++#define R_ARM_ABS12 6 /* Direct 12 bit */ ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 /* Direct 8 bit */ ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_SWI24 13 ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_COPY 20 /* Copy symbol at runtime */ ++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ ++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ ++#define R_ARM_RELATIVE 23 /* Adjust by program base */ ++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ ++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ ++#define R_ARM_GOT32 26 /* 32 bit GOT entry */ ++#define R_ARM_PLT32 27 /* 32 bit PLT address */ ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ ++#define R_ARM_THM_PC9 103 /* thumb conditional branch */ ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++/* Keep this the last entry. */ ++#define R_ARM_NUM 256 ++ ++/* IA-64 specific declarations. */ ++ ++/* Processor specific flags for the Ehdr e_flags field. */ ++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ ++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ ++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ ++ ++/* Processor specific flags for the Phdr p_flags field. */ ++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ ++ ++/* Processor specific flags for the Shdr sh_flags field. */ ++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ ++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Dyn d_tag field. */ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++/* IA-64 relocations. */ ++#define R_IA64_NONE 0x00 /* none */ ++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ ++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ ++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ ++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ ++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ ++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ ++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ ++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ ++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ ++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ ++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ ++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ ++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ ++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ ++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ ++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ ++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ ++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ ++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ ++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ ++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ ++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ ++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ ++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ ++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ ++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ ++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ ++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ ++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ ++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ ++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ ++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ ++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ ++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ ++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ ++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ ++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ ++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ ++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ ++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ ++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ ++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ ++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ ++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ ++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ ++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ ++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ ++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ ++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ ++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ ++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ ++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ ++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ ++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ ++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ ++#define R_IA64_COPY 0x84 /* copy relocation */ ++#define R_IA64_SUB 0x85 /* Addend and symbol difference */ ++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ ++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ ++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ ++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ ++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ ++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ ++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ ++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ ++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ ++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ ++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ ++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ ++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ ++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ ++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ ++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ ++ ++/* SH specific declarations */ ++ ++/* SH relocs. */ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++/* Keep this the last entry. */ ++#define R_SH_NUM 256 ++ ++/* Additional s390 relocs */ ++ ++#define R_390_NONE 0 /* No reloc. */ ++#define R_390_8 1 /* Direct 8 bit. */ ++#define R_390_12 2 /* Direct 12 bit. */ ++#define R_390_16 3 /* Direct 16 bit. */ ++#define R_390_32 4 /* Direct 32 bit. */ ++#define R_390_PC32 5 /* PC relative 32 bit. */ ++#define R_390_GOT12 6 /* 12 bit GOT offset. */ ++#define R_390_GOT32 7 /* 32 bit GOT offset. */ ++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ ++#define R_390_COPY 9 /* Copy symbol at runtime. */ ++#define R_390_GLOB_DAT 10 /* Create GOT entry. */ ++#define R_390_JMP_SLOT 11 /* Create PLT entry. */ ++#define R_390_RELATIVE 12 /* Adjust by program base. */ ++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ ++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ ++#define R_390_GOT16 15 /* 16 bit GOT offset. */ ++#define R_390_PC16 16 /* PC relative 16 bit. */ ++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ ++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ ++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ ++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ ++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ ++#define R_390_64 22 /* Direct 64 bit. */ ++#define R_390_PC64 23 /* PC relative 64 bit. */ ++#define R_390_GOT64 24 /* 64 bit GOT offset. */ ++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ ++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ ++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ ++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ ++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ ++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ ++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ ++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ ++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ ++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ ++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ ++#define R_390_TLS_GDCALL 38 /* Tag for function call in general ++ dynamic TLS code. */ ++#define R_390_TLS_LDCALL 39 /* Tag for function call in local ++ dynamic TLS code. */ ++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ ++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ ++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS ++ block. */ ++ ++/* Keep this the last entry. */ ++#define R_390_NUM 57 ++ ++/* CRIS relocations. */ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++/* AMD x86-64 relocations. */ ++#define R_X86_64_NONE 0 /* No reloc */ ++#define R_X86_64_64 1 /* Direct 64 bit */ ++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ ++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ ++#define R_X86_64_PLT32 4 /* 32 bit PLT address */ ++#define R_X86_64_COPY 5 /* Copy symbol at runtime */ ++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ ++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ ++#define R_X86_64_RELATIVE 8 /* Adjust by program base */ ++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative ++ offset to GOT */ ++#define R_X86_64_32 10 /* Direct 32 bit zero extended */ ++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ ++#define R_X86_64_16 12 /* Direct 16 bit zero extended */ ++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ ++#define R_X86_64_8 14 /* Direct 8 bit sign extended */ ++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ ++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ ++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ ++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ ++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset ++ to two GOT entries for GD symbol */ ++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset ++ to two GOT entries for LD symbol */ ++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ ++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset ++ to GOT entry for IE symbol */ ++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ ++ ++#define R_X86_64_NUM 24 ++ ++__END_DECLS ++ ++#endif /* elf.h */ + + #include "elfconfig.h" + +@@ -185,3 +2631,4 @@ + void fatal(const char *fmt, ...); + void warn(const char *fmt, ...); + void merror(const char *fmt, ...); ++ +diff -Nur linux-3.11.5.orig/scripts/mod/sumversion.c linux-3.11.5/scripts/mod/sumversion.c +--- linux-3.11.5.orig/scripts/mod/sumversion.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/sumversion.c 2013-10-16 18:09:31.000000000 +0200 +@@ -1,4 +1,4 @@ +-#include ++/* #include */ + #ifdef __sun__ + #include + #else +diff -Nur linux-3.11.5.orig/tools/include/tools/linux_types.h linux-3.11.5/tools/include/tools/linux_types.h +--- linux-3.11.5.orig/tools/include/tools/linux_types.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.11.5/tools/include/tools/linux_types.h 2013-10-16 18:09:31.000000000 +0200 +@@ -0,0 +1,22 @@ ++#ifndef __LINUX_TYPES_H ++#define __LINUX_TYPES_H ++ ++#include ++ ++typedef uint8_t __u8; ++typedef uint8_t __be8; ++typedef uint8_t __le8; ++ ++typedef uint16_t __u16; ++typedef uint16_t __be16; ++typedef uint16_t __le16; ++ ++typedef uint32_t __u32; ++typedef uint32_t __be32; ++typedef uint32_t __le32; ++ ++typedef uint64_t __u64; ++typedef uint64_t __be64; ++typedef uint64_t __le64; ++ ++#endif diff --git a/target/linux/patches/3.10.81/headers-install.patch b/target/linux/patches/3.10.81/headers-install.patch new file mode 100644 index 000000000..46dc9fe13 --- /dev/null +++ b/target/linux/patches/3.10.81/headers-install.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.10.37.orig/scripts/Makefile.headersinst linux-3.10.37/scripts/Makefile.headersinst +--- linux-3.10.37.orig/scripts/Makefile.headersinst 2014-04-14 15:42:31.000000000 +0200 ++++ linux-3.10.37/scripts/Makefile.headersinst 2014-04-24 17:04:51.980248954 +0200 +@@ -107,7 +107,6 @@ + + targets += $(install-file) + $(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE +- $(if $(unwanted),$(call cmd,remove),) + $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) + $(call if_changed,install) + diff --git a/target/linux/patches/3.10.81/mkpiggy.patch b/target/linux/patches/3.10.81/mkpiggy.patch new file mode 100644 index 000000000..d4e815cd2 --- /dev/null +++ b/target/linux/patches/3.10.81/mkpiggy.patch @@ -0,0 +1,28 @@ +diff -Nur linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c +--- linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c 2012-06-22 20:37:50.000000000 +0200 ++++ linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c 2012-07-03 09:48:02.000000000 +0200 +@@ -29,7 +29,14 @@ + #include + #include + #include +-#include ++ ++static uint32_t getle32(const void *p) ++{ ++ const uint8_t *cp = p; ++ ++ return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + ++ ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); ++} + + int main(int argc, char *argv[]) + { +@@ -62,7 +69,7 @@ + } + + ilen = ftell(f); +- olen = get_unaligned_le32(&olen); ++ olen = getle32(&olen); + fclose(f); + + /* diff --git a/target/linux/patches/3.10.81/relocs.patch b/target/linux/patches/3.10.81/relocs.patch new file mode 100644 index 000000000..649b9e73e --- /dev/null +++ b/target/linux/patches/3.10.81/relocs.patch @@ -0,0 +1,2693 @@ +diff -Nur linux-3.11.5.orig/arch/x86/tools/relocs.h linux-3.11.5/arch/x86/tools/relocs.h +--- linux-3.11.5.orig/arch/x86/tools/relocs.h 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/x86/tools/relocs.h 2013-10-20 17:41:16.000000000 +0200 +@@ -9,10 +9,14 @@ + #include + #include + #include ++#ifdef __linux__ + #include + #include + #define USE_BSD + #include ++#else ++#include "elf.h" ++#endif + #include + #include + +diff -Nur linux-3.11.5.orig/tools/include/elf.h linux-3.11.5/tools/include/elf.h +--- linux-3.11.5.orig/tools/include/elf.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.11.5/tools/include/elf.h 2013-10-20 17:42:10.000000000 +0200 +@@ -0,0 +1,2671 @@ ++#ifndef _ELF_H ++#define _ELF_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include ++ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++#define EI_NIDENT (16) ++ ++typedef struct { ++ unsigned char e_ident[EI_NIDENT]; ++ Elf32_Half e_type; ++ Elf32_Half e_machine; ++ Elf32_Word e_version; ++ Elf32_Addr e_entry; ++ Elf32_Off e_phoff; ++ Elf32_Off e_shoff; ++ Elf32_Word e_flags; ++ Elf32_Half e_ehsize; ++ Elf32_Half e_phentsize; ++ Elf32_Half e_phnum; ++ Elf32_Half e_shentsize; ++ Elf32_Half e_shnum; ++ Elf32_Half e_shstrndx; ++} Elf32_Ehdr; ++ ++typedef struct { ++ unsigned char e_ident[EI_NIDENT]; ++ Elf64_Half e_type; ++ Elf64_Half e_machine; ++ Elf64_Word e_version; ++ Elf64_Addr e_entry; ++ Elf64_Off e_phoff; ++ Elf64_Off e_shoff; ++ Elf64_Word e_flags; ++ Elf64_Half e_ehsize; ++ Elf64_Half e_phentsize; ++ Elf64_Half e_phnum; ++ Elf64_Half e_shentsize; ++ Elf64_Half e_shnum; ++ Elf64_Half e_shstrndx; ++} Elf64_Ehdr; ++ ++#define EI_MAG0 0 ++#define ELFMAG0 0x7f ++ ++#define EI_MAG1 1 ++#define ELFMAG1 'E' ++ ++#define EI_MAG2 2 ++#define ELFMAG2 'L' ++ ++#define EI_MAG3 3 ++#define ELFMAG3 'F' ++ ++ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 ++#define ELFCLASSNONE 0 ++#define ELFCLASS32 1 ++#define ELFCLASS64 2 ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 ++#define ELFDATANONE 0 ++#define ELFDATA2LSB 1 ++#define ELFDATA2MSB 2 ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 ++ ++ ++#define EI_OSABI 7 ++#define ELFOSABI_NONE 0 ++#define ELFOSABI_SYSV 0 ++#define ELFOSABI_HPUX 1 ++#define ELFOSABI_NETBSD 2 ++#define ELFOSABI_LINUX 3 ++#define ELFOSABI_GNU 3 ++#define ELFOSABI_SOLARIS 6 ++#define ELFOSABI_AIX 7 ++#define ELFOSABI_IRIX 8 ++#define ELFOSABI_FREEBSD 9 ++#define ELFOSABI_TRU64 10 ++#define ELFOSABI_MODESTO 11 ++#define ELFOSABI_OPENBSD 12 ++#define ELFOSABI_ARM 97 ++#define ELFOSABI_STANDALONE 255 ++ ++#define EI_ABIVERSION 8 ++ ++#define EI_PAD 9 ++ ++ ++ ++#define ET_NONE 0 ++#define ET_REL 1 ++#define ET_EXEC 2 ++#define ET_DYN 3 ++#define ET_CORE 4 ++#define ET_NUM 5 ++#define ET_LOOS 0xfe00 ++#define ET_HIOS 0xfeff ++#define ET_LOPROC 0xff00 ++#define ET_HIPROC 0xffff ++ ++ ++ ++#define EM_NONE 0 ++#define EM_M32 1 ++#define EM_SPARC 2 ++#define EM_386 3 ++#define EM_68K 4 ++#define EM_88K 5 ++#define EM_860 7 ++#define EM_MIPS 8 ++#define EM_S370 9 ++#define EM_MIPS_RS3_LE 10 ++ ++#define EM_PARISC 15 ++#define EM_VPP500 17 ++#define EM_SPARC32PLUS 18 ++#define EM_960 19 ++#define EM_PPC 20 ++#define EM_PPC64 21 ++#define EM_S390 22 ++ ++#define EM_V800 36 ++#define EM_FR20 37 ++#define EM_RH32 38 ++#define EM_RCE 39 ++#define EM_ARM 40 ++#define EM_FAKE_ALPHA 41 ++#define EM_SH 42 ++#define EM_SPARCV9 43 ++#define EM_TRICORE 44 ++#define EM_ARC 45 ++#define EM_H8_300 46 ++#define EM_H8_300H 47 ++#define EM_H8S 48 ++#define EM_H8_500 49 ++#define EM_IA_64 50 ++#define EM_MIPS_X 51 ++#define EM_COLDFIRE 52 ++#define EM_68HC12 53 ++#define EM_MMA 54 ++#define EM_PCP 55 ++#define EM_NCPU 56 ++#define EM_NDR1 57 ++#define EM_STARCORE 58 ++#define EM_ME16 59 ++#define EM_ST100 60 ++#define EM_TINYJ 61 ++#define EM_X86_64 62 ++#define EM_PDSP 63 ++ ++#define EM_FX66 66 ++#define EM_ST9PLUS 67 ++#define EM_ST7 68 ++#define EM_68HC16 69 ++#define EM_68HC11 70 ++#define EM_68HC08 71 ++#define EM_68HC05 72 ++#define EM_SVX 73 ++#define EM_ST19 74 ++#define EM_VAX 75 ++#define EM_CRIS 76 ++#define EM_JAVELIN 77 ++#define EM_FIREPATH 78 ++#define EM_ZSP 79 ++#define EM_MMIX 80 ++#define EM_HUANY 81 ++#define EM_PRISM 82 ++#define EM_AVR 83 ++#define EM_FR30 84 ++#define EM_D10V 85 ++#define EM_D30V 86 ++#define EM_V850 87 ++#define EM_M32R 88 ++#define EM_MN10300 89 ++#define EM_MN10200 90 ++#define EM_PJ 91 ++#define EM_OPENRISC 92 ++#define EM_ARC_A5 93 ++#define EM_XTENSA 94 ++#define EM_AARCH64 183 ++#define EM_TILEPRO 188 ++#define EM_MICROBLAZE 189 ++#define EM_TILEGX 191 ++#define EM_NUM 192 ++#define EM_ALPHA 0x9026 ++ ++#define EV_NONE 0 ++#define EV_CURRENT 1 ++#define EV_NUM 2 ++ ++typedef struct { ++ Elf32_Word sh_name; ++ Elf32_Word sh_type; ++ Elf32_Word sh_flags; ++ Elf32_Addr sh_addr; ++ Elf32_Off sh_offset; ++ Elf32_Word sh_size; ++ Elf32_Word sh_link; ++ Elf32_Word sh_info; ++ Elf32_Word sh_addralign; ++ Elf32_Word sh_entsize; ++} Elf32_Shdr; ++ ++typedef struct { ++ Elf64_Word sh_name; ++ Elf64_Word sh_type; ++ Elf64_Xword sh_flags; ++ Elf64_Addr sh_addr; ++ Elf64_Off sh_offset; ++ Elf64_Xword sh_size; ++ Elf64_Word sh_link; ++ Elf64_Word sh_info; ++ Elf64_Xword sh_addralign; ++ Elf64_Xword sh_entsize; ++} Elf64_Shdr; ++ ++ ++ ++#define SHN_UNDEF 0 ++#define SHN_LORESERVE 0xff00 ++#define SHN_LOPROC 0xff00 ++#define SHN_BEFORE 0xff00 ++ ++#define SHN_AFTER 0xff01 ++ ++#define SHN_HIPROC 0xff1f ++#define SHN_LOOS 0xff20 ++#define SHN_HIOS 0xff3f ++#define SHN_ABS 0xfff1 ++#define SHN_COMMON 0xfff2 ++#define SHN_XINDEX 0xffff ++#define SHN_HIRESERVE 0xffff ++ ++ ++ ++#define SHT_NULL 0 ++#define SHT_PROGBITS 1 ++#define SHT_SYMTAB 2 ++#define SHT_STRTAB 3 ++#define SHT_RELA 4 ++#define SHT_HASH 5 ++#define SHT_DYNAMIC 6 ++#define SHT_NOTE 7 ++#define SHT_NOBITS 8 ++#define SHT_REL 9 ++#define SHT_SHLIB 10 ++#define SHT_DYNSYM 11 ++#define SHT_INIT_ARRAY 14 ++#define SHT_FINI_ARRAY 15 ++#define SHT_PREINIT_ARRAY 16 ++#define SHT_GROUP 17 ++#define SHT_SYMTAB_SHNDX 18 ++#define SHT_NUM 19 ++#define SHT_LOOS 0x60000000 ++#define SHT_GNU_ATTRIBUTES 0x6ffffff5 ++#define SHT_GNU_HASH 0x6ffffff6 ++#define SHT_GNU_LIBLIST 0x6ffffff7 ++#define SHT_CHECKSUM 0x6ffffff8 ++#define SHT_LOSUNW 0x6ffffffa ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd ++#define SHT_GNU_verneed 0x6ffffffe ++#define SHT_GNU_versym 0x6fffffff ++#define SHT_HISUNW 0x6fffffff ++#define SHT_HIOS 0x6fffffff ++#define SHT_LOPROC 0x70000000 ++#define SHT_HIPROC 0x7fffffff ++#define SHT_LOUSER 0x80000000 ++#define SHT_HIUSER 0x8fffffff ++ ++#define SHF_WRITE (1 << 0) ++#define SHF_ALLOC (1 << 1) ++#define SHF_EXECINSTR (1 << 2) ++#define SHF_MERGE (1 << 4) ++#define SHF_STRINGS (1 << 5) ++#define SHF_INFO_LINK (1 << 6) ++#define SHF_LINK_ORDER (1 << 7) ++#define SHF_OS_NONCONFORMING (1 << 8) ++ ++#define SHF_GROUP (1 << 9) ++#define SHF_TLS (1 << 10) ++#define SHF_MASKOS 0x0ff00000 ++#define SHF_MASKPROC 0xf0000000 ++#define SHF_ORDERED (1 << 30) ++#define SHF_EXCLUDE (1 << 31) ++ ++#define GRP_COMDAT 0x1 ++ ++typedef struct { ++ Elf32_Word st_name; ++ Elf32_Addr st_value; ++ Elf32_Word st_size; ++ unsigned char st_info; ++ unsigned char st_other; ++ Elf32_Section st_shndx; ++} Elf32_Sym; ++ ++typedef struct { ++ Elf64_Word st_name; ++ unsigned char st_info; ++ unsigned char st_other; ++ Elf64_Section st_shndx; ++ Elf64_Addr st_value; ++ Elf64_Xword st_size; ++} Elf64_Sym; ++ ++typedef struct { ++ Elf32_Half si_boundto; ++ Elf32_Half si_flags; ++} Elf32_Syminfo; ++ ++typedef struct { ++ Elf64_Half si_boundto; ++ Elf64_Half si_flags; ++} Elf64_Syminfo; ++ ++#define SYMINFO_BT_SELF 0xffff ++#define SYMINFO_BT_PARENT 0xfffe ++#define SYMINFO_BT_LOWRESERVE 0xff00 ++ ++#define SYMINFO_FLG_DIRECT 0x0001 ++#define SYMINFO_FLG_PASSTHRU 0x0002 ++#define SYMINFO_FLG_COPY 0x0004 ++#define SYMINFO_FLG_LAZYLOAD 0x0008 ++ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++#define STB_LOCAL 0 ++#define STB_GLOBAL 1 ++#define STB_WEAK 2 ++#define STB_NUM 3 ++#define STB_LOOS 10 ++#define STB_GNU_UNIQUE 10 ++#define STB_HIOS 12 ++#define STB_LOPROC 13 ++#define STB_HIPROC 15 ++ ++#define STT_NOTYPE 0 ++#define STT_OBJECT 1 ++#define STT_FUNC 2 ++#define STT_SECTION 3 ++#define STT_FILE 4 ++#define STT_COMMON 5 ++#define STT_TLS 6 ++#define STT_NUM 7 ++#define STT_LOOS 10 ++#define STT_GNU_IFUNC 10 ++#define STT_HIOS 12 ++#define STT_LOPROC 13 ++#define STT_HIPROC 15 ++ ++#define STN_UNDEF 0 ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++#define STV_DEFAULT 0 ++#define STV_INTERNAL 1 ++#define STV_HIDDEN 2 ++#define STV_PROTECTED 3 ++ ++ ++ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; ++ Elf32_Word r_info; ++} Elf32_Rel; ++ ++typedef struct { ++ Elf64_Addr r_offset; ++ Elf64_Xword r_info; ++} Elf64_Rel; ++ ++ ++ ++typedef struct { ++ Elf32_Addr r_offset; ++ Elf32_Word r_info; ++ Elf32_Sword r_addend; ++} Elf32_Rela; ++ ++typedef struct { ++ Elf64_Addr r_offset; ++ Elf64_Xword r_info; ++ Elf64_Sxword r_addend; ++} Elf64_Rela; ++ ++ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++ ++ ++typedef struct { ++ Elf32_Word p_type; ++ Elf32_Off p_offset; ++ Elf32_Addr p_vaddr; ++ Elf32_Addr p_paddr; ++ Elf32_Word p_filesz; ++ Elf32_Word p_memsz; ++ Elf32_Word p_flags; ++ Elf32_Word p_align; ++} Elf32_Phdr; ++ ++typedef struct { ++ Elf64_Word p_type; ++ Elf64_Word p_flags; ++ Elf64_Off p_offset; ++ Elf64_Addr p_vaddr; ++ Elf64_Addr p_paddr; ++ Elf64_Xword p_filesz; ++ Elf64_Xword p_memsz; ++ Elf64_Xword p_align; ++} Elf64_Phdr; ++ ++ ++ ++#define PT_NULL 0 ++#define PT_LOAD 1 ++#define PT_DYNAMIC 2 ++#define PT_INTERP 3 ++#define PT_NOTE 4 ++#define PT_SHLIB 5 ++#define PT_PHDR 6 ++#define PT_TLS 7 ++#define PT_NUM 8 ++#define PT_LOOS 0x60000000 ++#define PT_GNU_EH_FRAME 0x6474e550 ++#define PT_GNU_STACK 0x6474e551 ++#define PT_GNU_RELRO 0x6474e552 ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa ++#define PT_SUNWSTACK 0x6ffffffb ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff ++#define PT_LOPROC 0x70000000 ++#define PT_HIPROC 0x7fffffff ++ ++ ++#define PN_XNUM 0xffff ++ ++ ++#define PF_X (1 << 0) ++#define PF_W (1 << 1) ++#define PF_R (1 << 2) ++#define PF_MASKOS 0x0ff00000 ++#define PF_MASKPROC 0xf0000000 ++ ++ ++ ++#define NT_PRSTATUS 1 ++#define NT_FPREGSET 2 ++#define NT_PRPSINFO 3 ++#define NT_PRXREG 4 ++#define NT_TASKSTRUCT 4 ++#define NT_PLATFORM 5 ++#define NT_AUXV 6 ++#define NT_GWINDOWS 7 ++#define NT_ASRS 8 ++#define NT_PSTATUS 10 ++#define NT_PSINFO 13 ++#define NT_PRCRED 14 ++#define NT_UTSNAME 15 ++#define NT_LWPSTATUS 16 ++#define NT_LWPSINFO 17 ++#define NT_PRFPXREG 20 ++#define NT_SIGINFO 0x53494749 ++#define NT_FILE 0x46494c45 ++#define NT_PRXFPREG 0x46e62b7f ++#define NT_PPC_VMX 0x100 ++#define NT_PPC_SPE 0x101 ++#define NT_PPC_VSX 0x102 ++#define NT_386_TLS 0x200 ++#define NT_386_IOPERM 0x201 ++#define NT_X86_XSTATE 0x202 ++#define NT_S390_HIGH_GPRS 0x300 ++#define NT_S390_TIMER 0x301 ++#define NT_S390_TODCMP 0x302 ++#define NT_S390_TODPREG 0x303 ++#define NT_S390_CTRS 0x304 ++#define NT_S390_PREFIX 0x305 ++#define NT_S390_LAST_BREAK 0x306 ++#define NT_S390_SYSTEM_CALL 0x307 ++#define NT_S390_TDB 0x308 ++#define NT_ARM_VFP 0x400 ++#define NT_ARM_TLS 0x401 ++#define NT_ARM_HW_BREAK 0x402 ++#define NT_ARM_HW_WATCH 0x403 ++#define NT_METAG_CBUF 0x500 ++#define NT_METAG_RPIPE 0x501 ++#define NT_METAG_TLS 0x502 ++#define NT_VERSION 1 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Sword d_tag; ++ union { ++ Elf32_Word d_val; ++ Elf32_Addr d_ptr; ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct { ++ Elf64_Sxword d_tag; ++ union { ++ Elf64_Xword d_val; ++ Elf64_Addr d_ptr; ++ } d_un; ++} Elf64_Dyn; ++ ++ ++ ++#define DT_NULL 0 ++#define DT_NEEDED 1 ++#define DT_PLTRELSZ 2 ++#define DT_PLTGOT 3 ++#define DT_HASH 4 ++#define DT_STRTAB 5 ++#define DT_SYMTAB 6 ++#define DT_RELA 7 ++#define DT_RELASZ 8 ++#define DT_RELAENT 9 ++#define DT_STRSZ 10 ++#define DT_SYMENT 11 ++#define DT_INIT 12 ++#define DT_FINI 13 ++#define DT_SONAME 14 ++#define DT_RPATH 15 ++#define DT_SYMBOLIC 16 ++#define DT_REL 17 ++#define DT_RELSZ 18 ++#define DT_RELENT 19 ++#define DT_PLTREL 20 ++#define DT_DEBUG 21 ++#define DT_TEXTREL 22 ++#define DT_JMPREL 23 ++#define DT_BIND_NOW 24 ++#define DT_INIT_ARRAY 25 ++#define DT_FINI_ARRAY 26 ++#define DT_INIT_ARRAYSZ 27 ++#define DT_FINI_ARRAYSZ 28 ++#define DT_RUNPATH 29 ++#define DT_FLAGS 30 ++#define DT_ENCODING 32 ++#define DT_PREINIT_ARRAY 32 ++#define DT_PREINIT_ARRAYSZ 33 ++#define DT_NUM 34 ++#define DT_LOOS 0x6000000d ++#define DT_HIOS 0x6ffff000 ++#define DT_LOPROC 0x70000000 ++#define DT_HIPROC 0x7fffffff ++#define DT_PROCNUM DT_MIPS_NUM ++ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc ++#define DT_POSFLAG_1 0x6ffffdfd ++ ++#define DT_SYMINSZ 0x6ffffdfe ++#define DT_SYMINENT 0x6ffffdff ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) ++#define DT_VALNUM 12 ++ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_HASH 0x6ffffef5 ++#define DT_TLSDESC_PLT 0x6ffffef6 ++#define DT_TLSDESC_GOT 0x6ffffef7 ++#define DT_GNU_CONFLICT 0x6ffffef8 ++#define DT_GNU_LIBLIST 0x6ffffef9 ++#define DT_CONFIG 0x6ffffefa ++#define DT_DEPAUDIT 0x6ffffefb ++#define DT_AUDIT 0x6ffffefc ++#define DT_PLTPAD 0x6ffffefd ++#define DT_MOVETAB 0x6ffffefe ++#define DT_SYMINFO 0x6ffffeff ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) ++#define DT_ADDRNUM 11 ++ ++ ++ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++ ++#define DT_FLAGS_1 0x6ffffffb ++#define DT_VERDEF 0x6ffffffc ++ ++#define DT_VERDEFNUM 0x6ffffffd ++#define DT_VERNEED 0x6ffffffe ++ ++#define DT_VERNEEDNUM 0x6fffffff ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) ++#define DT_VERSIONTAGNUM 16 ++ ++ ++ ++#define DT_AUXILIARY 0x7ffffffd ++#define DT_FILTER 0x7fffffff ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++ ++#define DF_ORIGIN 0x00000001 ++#define DF_SYMBOLIC 0x00000002 ++#define DF_TEXTREL 0x00000004 ++#define DF_BIND_NOW 0x00000008 ++#define DF_STATIC_TLS 0x00000010 ++ ++ ++ ++#define DF_1_NOW 0x00000001 ++#define DF_1_GLOBAL 0x00000002 ++#define DF_1_GROUP 0x00000004 ++#define DF_1_NODELETE 0x00000008 ++#define DF_1_LOADFLTR 0x00000010 ++#define DF_1_INITFIRST 0x00000020 ++#define DF_1_NOOPEN 0x00000040 ++#define DF_1_ORIGIN 0x00000080 ++#define DF_1_DIRECT 0x00000100 ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 ++#define DF_1_NODEFLIB 0x00000800 ++#define DF_1_NODUMP 0x00001000 ++#define DF_1_CONFALT 0x00002000 ++#define DF_1_ENDFILTEE 0x00004000 ++#define DF_1_DISPRELDNE 0x00008000 ++#define DF_1_DISPRELPND 0x00010000 ++#define DF_1_NODIRECT 0x00020000 ++#define DF_1_IGNMULDEF 0x00040000 ++#define DF_1_NOKSYMS 0x00080000 ++#define DF_1_NOHDR 0x00100000 ++#define DF_1_EDITED 0x00200000 ++#define DF_1_NORELOC 0x00400000 ++#define DF_1_SYMINTPOSE 0x00800000 ++#define DF_1_GLOBAUDIT 0x01000000 ++#define DF_1_SINGLETON 0x02000000 ++ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++ ++#define DF_P1_LAZYLOAD 0x00000001 ++#define DF_P1_GROUPPERM 0x00000002 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Half vd_version; ++ Elf32_Half vd_flags; ++ Elf32_Half vd_ndx; ++ Elf32_Half vd_cnt; ++ Elf32_Word vd_hash; ++ Elf32_Word vd_aux; ++ Elf32_Word vd_next; ++} Elf32_Verdef; ++ ++typedef struct { ++ Elf64_Half vd_version; ++ Elf64_Half vd_flags; ++ Elf64_Half vd_ndx; ++ Elf64_Half vd_cnt; ++ Elf64_Word vd_hash; ++ Elf64_Word vd_aux; ++ Elf64_Word vd_next; ++} Elf64_Verdef; ++ ++ ++ ++#define VER_DEF_NONE 0 ++#define VER_DEF_CURRENT 1 ++#define VER_DEF_NUM 2 ++ ++ ++#define VER_FLG_BASE 0x1 ++#define VER_FLG_WEAK 0x2 ++ ++ ++#define VER_NDX_LOCAL 0 ++#define VER_NDX_GLOBAL 1 ++#define VER_NDX_LORESERVE 0xff00 ++#define VER_NDX_ELIMINATE 0xff01 ++ ++ ++ ++typedef struct { ++ Elf32_Word vda_name; ++ Elf32_Word vda_next; ++} Elf32_Verdaux; ++ ++typedef struct { ++ Elf64_Word vda_name; ++ Elf64_Word vda_next; ++} Elf64_Verdaux; ++ ++ ++ ++ ++typedef struct { ++ Elf32_Half vn_version; ++ Elf32_Half vn_cnt; ++ Elf32_Word vn_file; ++ Elf32_Word vn_aux; ++ Elf32_Word vn_next; ++} Elf32_Verneed; ++ ++typedef struct { ++ Elf64_Half vn_version; ++ Elf64_Half vn_cnt; ++ Elf64_Word vn_file; ++ Elf64_Word vn_aux; ++ Elf64_Word vn_next; ++} Elf64_Verneed; ++ ++ ++ ++#define VER_NEED_NONE 0 ++#define VER_NEED_CURRENT 1 ++#define VER_NEED_NUM 2 ++ ++ ++ ++typedef struct { ++ Elf32_Word vna_hash; ++ Elf32_Half vna_flags; ++ Elf32_Half vna_other; ++ Elf32_Word vna_name; ++ Elf32_Word vna_next; ++} Elf32_Vernaux; ++ ++typedef struct { ++ Elf64_Word vna_hash; ++ Elf64_Half vna_flags; ++ Elf64_Half vna_other; ++ Elf64_Word vna_name; ++ Elf64_Word vna_next; ++} Elf64_Vernaux; ++ ++ ++ ++#define VER_FLG_WEAK 0x2 ++ ++ ++ ++typedef struct { ++ uint32_t a_type; ++ union { ++ uint32_t a_val; ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct { ++ uint64_t a_type; ++ union { ++ uint64_t a_val; ++ } a_un; ++} Elf64_auxv_t; ++ ++ ++ ++#define AT_NULL 0 ++#define AT_IGNORE 1 ++#define AT_EXECFD 2 ++#define AT_PHDR 3 ++#define AT_PHENT 4 ++#define AT_PHNUM 5 ++#define AT_PAGESZ 6 ++#define AT_BASE 7 ++#define AT_FLAGS 8 ++#define AT_ENTRY 9 ++#define AT_NOTELF 10 ++#define AT_UID 11 ++#define AT_EUID 12 ++#define AT_GID 13 ++#define AT_EGID 14 ++#define AT_CLKTCK 17 ++ ++ ++#define AT_PLATFORM 15 ++#define AT_HWCAP 16 ++ ++ ++ ++ ++#define AT_FPUCW 18 ++ ++ ++#define AT_DCACHEBSIZE 19 ++#define AT_ICACHEBSIZE 20 ++#define AT_UCACHEBSIZE 21 ++ ++ ++ ++#define AT_IGNOREPPC 22 ++ ++#define AT_SECURE 23 ++ ++#define AT_BASE_PLATFORM 24 ++ ++#define AT_RANDOM 25 ++ ++#define AT_HWCAP2 26 ++ ++#define AT_EXECFN 31 ++ ++ ++ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++ ++ ++#define AT_L1I_CACHESHAPE 34 ++#define AT_L1D_CACHESHAPE 35 ++#define AT_L2_CACHESHAPE 36 ++#define AT_L3_CACHESHAPE 37 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Word n_namesz; ++ Elf32_Word n_descsz; ++ Elf32_Word n_type; ++} Elf32_Nhdr; ++ ++typedef struct { ++ Elf64_Word n_namesz; ++ Elf64_Word n_descsz; ++ Elf64_Word n_type; ++} Elf64_Nhdr; ++ ++ ++ ++ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++ ++ ++ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++#define NT_GNU_ABI_TAG 1 ++#define ELF_NOTE_ABI NT_GNU_ABI_TAG ++ ++ ++ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++#define NT_GNU_BUILD_ID 3 ++#define NT_GNU_GOLD_VERSION 4 ++ ++ ++ ++typedef struct { ++ Elf32_Xword m_value; ++ Elf32_Word m_info; ++ Elf32_Word m_poffset; ++ Elf32_Half m_repeat; ++ Elf32_Half m_stride; ++} Elf32_Move; ++ ++typedef struct { ++ Elf64_Xword m_value; ++ Elf64_Xword m_info; ++ Elf64_Xword m_poffset; ++ Elf64_Half m_repeat; ++ Elf64_Half m_stride; ++} Elf64_Move; ++ ++ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++#define EF_CPU32 0x00810000 ++ ++#define R_68K_NONE 0 ++#define R_68K_32 1 ++#define R_68K_16 2 ++#define R_68K_8 3 ++#define R_68K_PC32 4 ++#define R_68K_PC16 5 ++#define R_68K_PC8 6 ++#define R_68K_GOT32 7 ++#define R_68K_GOT16 8 ++#define R_68K_GOT8 9 ++#define R_68K_GOT32O 10 ++#define R_68K_GOT16O 11 ++#define R_68K_GOT8O 12 ++#define R_68K_PLT32 13 ++#define R_68K_PLT16 14 ++#define R_68K_PLT8 15 ++#define R_68K_PLT32O 16 ++#define R_68K_PLT16O 17 ++#define R_68K_PLT8O 18 ++#define R_68K_COPY 19 ++#define R_68K_GLOB_DAT 20 ++#define R_68K_JMP_SLOT 21 ++#define R_68K_RELATIVE 22 ++#define R_68K_NUM 23 ++ ++#define R_386_NONE 0 ++#define R_386_32 1 ++#define R_386_PC32 2 ++#define R_386_GOT32 3 ++#define R_386_PLT32 4 ++#define R_386_COPY 5 ++#define R_386_GLOB_DAT 6 ++#define R_386_JMP_SLOT 7 ++#define R_386_RELATIVE 8 ++#define R_386_GOTOFF 9 ++#define R_386_GOTPC 10 ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 ++#define R_386_TLS_IE 15 ++#define R_386_TLS_GOTIE 16 ++#define R_386_TLS_LE 17 ++#define R_386_TLS_GD 18 ++#define R_386_TLS_LDM 19 ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 ++#define R_386_TLS_GD_PUSH 25 ++#define R_386_TLS_GD_CALL 26 ++#define R_386_TLS_GD_POP 27 ++#define R_386_TLS_LDM_32 28 ++#define R_386_TLS_LDM_PUSH 29 ++#define R_386_TLS_LDM_CALL 30 ++#define R_386_TLS_LDM_POP 31 ++#define R_386_TLS_LDO_32 32 ++#define R_386_TLS_IE_32 33 ++#define R_386_TLS_LE_32 34 ++#define R_386_TLS_DTPMOD32 35 ++#define R_386_TLS_DTPOFF32 36 ++#define R_386_TLS_TPOFF32 37 ++#define R_386_SIZE32 38 ++#define R_386_TLS_GOTDESC 39 ++#define R_386_TLS_DESC_CALL 40 ++#define R_386_TLS_DESC 41 ++#define R_386_IRELATIVE 42 ++#define R_386_NUM 43 ++ ++ ++ ++ ++ ++#define STT_SPARC_REGISTER 13 ++ ++ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 ++#define EF_SPARC_SUN_US1 0x000200 ++#define EF_SPARC_HAL_R1 0x000400 ++#define EF_SPARC_SUN_US3 0x000800 ++ ++ ++ ++#define R_SPARC_NONE 0 ++#define R_SPARC_8 1 ++#define R_SPARC_16 2 ++#define R_SPARC_32 3 ++#define R_SPARC_DISP8 4 ++#define R_SPARC_DISP16 5 ++#define R_SPARC_DISP32 6 ++#define R_SPARC_WDISP30 7 ++#define R_SPARC_WDISP22 8 ++#define R_SPARC_HI22 9 ++#define R_SPARC_22 10 ++#define R_SPARC_13 11 ++#define R_SPARC_LO10 12 ++#define R_SPARC_GOT10 13 ++#define R_SPARC_GOT13 14 ++#define R_SPARC_GOT22 15 ++#define R_SPARC_PC10 16 ++#define R_SPARC_PC22 17 ++#define R_SPARC_WPLT30 18 ++#define R_SPARC_COPY 19 ++#define R_SPARC_GLOB_DAT 20 ++#define R_SPARC_JMP_SLOT 21 ++#define R_SPARC_RELATIVE 22 ++#define R_SPARC_UA32 23 ++ ++ ++ ++#define R_SPARC_PLT32 24 ++#define R_SPARC_HIPLT22 25 ++#define R_SPARC_LOPLT10 26 ++#define R_SPARC_PCPLT32 27 ++#define R_SPARC_PCPLT22 28 ++#define R_SPARC_PCPLT10 29 ++#define R_SPARC_10 30 ++#define R_SPARC_11 31 ++#define R_SPARC_64 32 ++#define R_SPARC_OLO10 33 ++#define R_SPARC_HH22 34 ++#define R_SPARC_HM10 35 ++#define R_SPARC_LM22 36 ++#define R_SPARC_PC_HH22 37 ++#define R_SPARC_PC_HM10 38 ++#define R_SPARC_PC_LM22 39 ++#define R_SPARC_WDISP16 40 ++#define R_SPARC_WDISP19 41 ++#define R_SPARC_GLOB_JMP 42 ++#define R_SPARC_7 43 ++#define R_SPARC_5 44 ++#define R_SPARC_6 45 ++#define R_SPARC_DISP64 46 ++#define R_SPARC_PLT64 47 ++#define R_SPARC_HIX22 48 ++#define R_SPARC_LOX10 49 ++#define R_SPARC_H44 50 ++#define R_SPARC_M44 51 ++#define R_SPARC_L44 52 ++#define R_SPARC_REGISTER 53 ++#define R_SPARC_UA64 54 ++#define R_SPARC_UA16 55 ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++#define R_SPARC_GOTDATA_HIX22 80 ++#define R_SPARC_GOTDATA_LOX10 81 ++#define R_SPARC_GOTDATA_OP_HIX22 82 ++#define R_SPARC_GOTDATA_OP_LOX10 83 ++#define R_SPARC_GOTDATA_OP 84 ++#define R_SPARC_H34 85 ++#define R_SPARC_SIZE32 86 ++#define R_SPARC_SIZE64 87 ++#define R_SPARC_GNU_VTINHERIT 250 ++#define R_SPARC_GNU_VTENTRY 251 ++#define R_SPARC_REV32 252 ++ ++#define R_SPARC_NUM 253 ++ ++ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++ ++#define EF_MIPS_NOREORDER 1 ++#define EF_MIPS_PIC 2 ++#define EF_MIPS_CPIC 4 ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 ++ ++ ++ ++#define EF_MIPS_ARCH_1 0x00000000 ++#define EF_MIPS_ARCH_2 0x10000000 ++#define EF_MIPS_ARCH_3 0x20000000 ++#define EF_MIPS_ARCH_4 0x30000000 ++#define EF_MIPS_ARCH_5 0x40000000 ++#define EF_MIPS_ARCH_32 0x50000000 ++#define EF_MIPS_ARCH_64 0x60000000 ++#define EF_MIPS_ARCH_32R2 0x70000000 ++#define EF_MIPS_ARCH_64R2 0x80000000 ++ ++ ++#define E_MIPS_ARCH_1 0x00000000 ++#define E_MIPS_ARCH_2 0x10000000 ++#define E_MIPS_ARCH_3 0x20000000 ++#define E_MIPS_ARCH_4 0x30000000 ++#define E_MIPS_ARCH_5 0x40000000 ++#define E_MIPS_ARCH_32 0x50000000 ++#define E_MIPS_ARCH_64 0x60000000 ++ ++ ++ ++#define SHN_MIPS_ACOMMON 0xff00 ++#define SHN_MIPS_TEXT 0xff01 ++#define SHN_MIPS_DATA 0xff02 ++#define SHN_MIPS_SCOMMON 0xff03 ++#define SHN_MIPS_SUNDEFINED 0xff04 ++ ++ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 ++#define SHT_MIPS_GPTAB 0x70000003 ++#define SHT_MIPS_UCODE 0x70000004 ++#define SHT_MIPS_DEBUG 0x70000005 ++#define SHT_MIPS_REGINFO 0x70000006 ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++ ++ ++#define SHF_MIPS_GPREL 0x10000000 ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++ ++ ++ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_PLT 0x8 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++ ++ ++typedef union { ++ struct { ++ Elf32_Word gt_current_g_value; ++ Elf32_Word gt_unused; ++ } gt_header; ++ struct { ++ Elf32_Word gt_g_value; ++ Elf32_Word gt_bytes; ++ } gt_entry; ++} Elf32_gptab; ++ ++ ++ ++typedef struct { ++ Elf32_Word ri_gprmask; ++ Elf32_Word ri_cprmask[4]; ++ Elf32_Sword ri_gp_value; ++} Elf32_RegInfo; ++ ++ ++ ++typedef struct { ++ unsigned char kind; ++ ++ unsigned char size; ++ Elf32_Section section; ++ ++ Elf32_Word info; ++} Elf_Options; ++ ++ ++ ++#define ODK_NULL 0 ++#define ODK_REGINFO 1 ++#define ODK_EXCEPTIONS 2 ++#define ODK_PAD 3 ++#define ODK_HWPATCH 4 ++#define ODK_FILL 5 ++#define ODK_TAGS 6 ++#define ODK_HWAND 7 ++#define ODK_HWOR 8 ++ ++ ++ ++#define OEX_FPU_MIN 0x1f ++#define OEX_FPU_MAX 0x1f00 ++#define OEX_PAGE0 0x10000 ++#define OEX_SMM 0x20000 ++#define OEX_FPDBUG 0x40000 ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++ ++ ++#define OHW_R4KEOP 0x1 ++#define OHW_R8KPFETCH 0x2 ++#define OHW_R5KEOP 0x4 ++#define OHW_R5KCVTL 0x8 ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++ ++ ++typedef struct { ++ Elf32_Word hwp_flags1; ++ Elf32_Word hwp_flags2; ++} Elf_Options_Hw; ++ ++ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++ ++ ++#define R_MIPS_NONE 0 ++#define R_MIPS_16 1 ++#define R_MIPS_32 2 ++#define R_MIPS_REL32 3 ++#define R_MIPS_26 4 ++#define R_MIPS_HI16 5 ++#define R_MIPS_LO16 6 ++#define R_MIPS_GPREL16 7 ++#define R_MIPS_LITERAL 8 ++#define R_MIPS_GOT16 9 ++#define R_MIPS_PC16 10 ++#define R_MIPS_CALL16 11 ++#define R_MIPS_GPREL32 12 ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++#define R_MIPS_TLS_DTPMOD32 38 ++#define R_MIPS_TLS_DTPREL32 39 ++#define R_MIPS_TLS_DTPMOD64 40 ++#define R_MIPS_TLS_DTPREL64 41 ++#define R_MIPS_TLS_GD 42 ++#define R_MIPS_TLS_LDM 43 ++#define R_MIPS_TLS_DTPREL_HI16 44 ++#define R_MIPS_TLS_DTPREL_LO16 45 ++#define R_MIPS_TLS_GOTTPREL 46 ++#define R_MIPS_TLS_TPREL32 47 ++#define R_MIPS_TLS_TPREL64 48 ++#define R_MIPS_TLS_TPREL_HI16 49 ++#define R_MIPS_TLS_TPREL_LO16 50 ++#define R_MIPS_GLOB_DAT 51 ++#define R_MIPS_COPY 126 ++#define R_MIPS_JUMP_SLOT 127 ++ ++#define R_MIPS_NUM 128 ++ ++ ++ ++#define PT_MIPS_REGINFO 0x70000000 ++#define PT_MIPS_RTPROC 0x70000001 ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 ++#define DT_MIPS_TIME_STAMP 0x70000002 ++#define DT_MIPS_ICHECKSUM 0x70000003 ++#define DT_MIPS_IVERSION 0x70000004 ++#define DT_MIPS_FLAGS 0x70000005 ++#define DT_MIPS_BASE_ADDRESS 0x70000006 ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 ++#define DT_MIPS_LIBLIST 0x70000009 ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a ++#define DT_MIPS_CONFLICTNO 0x7000000b ++#define DT_MIPS_LIBLISTNO 0x70000010 ++#define DT_MIPS_SYMTABNO 0x70000011 ++#define DT_MIPS_UNREFEXTNO 0x70000012 ++#define DT_MIPS_GOTSYM 0x70000013 ++#define DT_MIPS_HIPAGENO 0x70000014 ++#define DT_MIPS_RLD_MAP 0x70000016 ++#define DT_MIPS_DELTA_CLASS 0x70000017 ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 ++ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a ++ ++#define DT_MIPS_DELTA_RELOC 0x7000001b ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c ++ ++#define DT_MIPS_DELTA_SYM 0x7000001d ++ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e ++ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 ++ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 ++ ++#define DT_MIPS_CXX_FLAGS 0x70000022 ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 ++#define DT_MIPS_INTERFACE 0x7000002a ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d ++ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e ++ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f ++#define DT_MIPS_GP_VALUE 0x70000030 ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 ++ ++#define DT_MIPS_PLTGOT 0x70000032 ++ ++#define DT_MIPS_RWPLT 0x70000034 ++#define DT_MIPS_NUM 0x35 ++ ++ ++ ++#define RHF_NONE 0 ++#define RHF_QUICKSTART (1 << 0) ++#define RHF_NOTPOT (1 << 1) ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++ ++ ++typedef struct ++{ ++ Elf32_Word l_name; ++ Elf32_Word l_time_stamp; ++ Elf32_Word l_checksum; ++ Elf32_Word l_version; ++ Elf32_Word l_flags; ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; ++ Elf64_Word l_time_stamp; ++ Elf64_Word l_checksum; ++ Elf64_Word l_version; ++ Elf64_Word l_flags; ++} Elf64_Lib; ++ ++ ++ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) ++#define LL_IGNORE_INT_VER (1 << 1) ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++ ++ ++ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 ++#define EF_PARISC_EXT 0x00020000 ++#define EF_PARISC_LSB 0x00040000 ++#define EF_PARISC_WIDE 0x00080000 ++#define EF_PARISC_NO_KABP 0x00100000 ++ ++#define EF_PARISC_LAZYSWAP 0x00400000 ++#define EF_PARISC_ARCH 0x0000ffff ++ ++ ++ ++#define EFA_PARISC_1_0 0x020b ++#define EFA_PARISC_1_1 0x0210 ++#define EFA_PARISC_2_0 0x0214 ++ ++ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 ++ ++#define SHN_PARISC_HUGE_COMMON 0xff01 ++ ++ ++ ++#define SHT_PARISC_EXT 0x70000000 ++#define SHT_PARISC_UNWIND 0x70000001 ++#define SHT_PARISC_DOC 0x70000002 ++ ++ ++ ++#define SHF_PARISC_SHORT 0x20000000 ++#define SHF_PARISC_HUGE 0x40000000 ++#define SHF_PARISC_SBP 0x80000000 ++ ++ ++ ++#define STT_PARISC_MILLICODE 13 ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++ ++ ++#define R_PARISC_NONE 0 ++#define R_PARISC_DIR32 1 ++#define R_PARISC_DIR21L 2 ++#define R_PARISC_DIR17R 3 ++#define R_PARISC_DIR17F 4 ++#define R_PARISC_DIR14R 6 ++#define R_PARISC_PCREL32 9 ++#define R_PARISC_PCREL21L 10 ++#define R_PARISC_PCREL17R 11 ++#define R_PARISC_PCREL17F 12 ++#define R_PARISC_PCREL14R 14 ++#define R_PARISC_DPREL21L 18 ++#define R_PARISC_DPREL14R 22 ++#define R_PARISC_GPREL21L 26 ++#define R_PARISC_GPREL14R 30 ++#define R_PARISC_LTOFF21L 34 ++#define R_PARISC_LTOFF14R 38 ++#define R_PARISC_SECREL32 41 ++#define R_PARISC_SEGBASE 48 ++#define R_PARISC_SEGREL32 49 ++#define R_PARISC_PLTOFF21L 50 ++#define R_PARISC_PLTOFF14R 54 ++#define R_PARISC_LTOFF_FPTR32 57 ++#define R_PARISC_LTOFF_FPTR21L 58 ++#define R_PARISC_LTOFF_FPTR14R 62 ++#define R_PARISC_FPTR64 64 ++#define R_PARISC_PLABEL32 65 ++#define R_PARISC_PLABEL21L 66 ++#define R_PARISC_PLABEL14R 70 ++#define R_PARISC_PCREL64 72 ++#define R_PARISC_PCREL22F 74 ++#define R_PARISC_PCREL14WR 75 ++#define R_PARISC_PCREL14DR 76 ++#define R_PARISC_PCREL16F 77 ++#define R_PARISC_PCREL16WF 78 ++#define R_PARISC_PCREL16DF 79 ++#define R_PARISC_DIR64 80 ++#define R_PARISC_DIR14WR 83 ++#define R_PARISC_DIR14DR 84 ++#define R_PARISC_DIR16F 85 ++#define R_PARISC_DIR16WF 86 ++#define R_PARISC_DIR16DF 87 ++#define R_PARISC_GPREL64 88 ++#define R_PARISC_GPREL14WR 91 ++#define R_PARISC_GPREL14DR 92 ++#define R_PARISC_GPREL16F 93 ++#define R_PARISC_GPREL16WF 94 ++#define R_PARISC_GPREL16DF 95 ++#define R_PARISC_LTOFF64 96 ++#define R_PARISC_LTOFF14WR 99 ++#define R_PARISC_LTOFF14DR 100 ++#define R_PARISC_LTOFF16F 101 ++#define R_PARISC_LTOFF16WF 102 ++#define R_PARISC_LTOFF16DF 103 ++#define R_PARISC_SECREL64 104 ++#define R_PARISC_SEGREL64 112 ++#define R_PARISC_PLTOFF14WR 115 ++#define R_PARISC_PLTOFF14DR 116 ++#define R_PARISC_PLTOFF16F 117 ++#define R_PARISC_PLTOFF16WF 118 ++#define R_PARISC_PLTOFF16DF 119 ++#define R_PARISC_LTOFF_FPTR64 120 ++#define R_PARISC_LTOFF_FPTR14WR 123 ++#define R_PARISC_LTOFF_FPTR14DR 124 ++#define R_PARISC_LTOFF_FPTR16F 125 ++#define R_PARISC_LTOFF_FPTR16WF 126 ++#define R_PARISC_LTOFF_FPTR16DF 127 ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 ++#define R_PARISC_IPLT 129 ++#define R_PARISC_EPLT 130 ++#define R_PARISC_TPREL32 153 ++#define R_PARISC_TPREL21L 154 ++#define R_PARISC_TPREL14R 158 ++#define R_PARISC_LTOFF_TP21L 162 ++#define R_PARISC_LTOFF_TP14R 166 ++#define R_PARISC_LTOFF_TP14F 167 ++#define R_PARISC_TPREL64 216 ++#define R_PARISC_TPREL14WR 219 ++#define R_PARISC_TPREL14DR 220 ++#define R_PARISC_TPREL16F 221 ++#define R_PARISC_TPREL16WF 222 ++#define R_PARISC_TPREL16DF 223 ++#define R_PARISC_LTOFF_TP64 224 ++#define R_PARISC_LTOFF_TP14WR 227 ++#define R_PARISC_LTOFF_TP14DR 228 ++#define R_PARISC_LTOFF_TP16F 229 ++#define R_PARISC_LTOFF_TP16WF 230 ++#define R_PARISC_LTOFF_TP16DF 231 ++#define R_PARISC_GNU_VTENTRY 232 ++#define R_PARISC_GNU_VTINHERIT 233 ++#define R_PARISC_TLS_GD21L 234 ++#define R_PARISC_TLS_GD14R 235 ++#define R_PARISC_TLS_GDCALL 236 ++#define R_PARISC_TLS_LDM21L 237 ++#define R_PARISC_TLS_LDM14R 238 ++#define R_PARISC_TLS_LDMCALL 239 ++#define R_PARISC_TLS_LDO21L 240 ++#define R_PARISC_TLS_LDO14R 241 ++#define R_PARISC_TLS_DTPMOD32 242 ++#define R_PARISC_TLS_DTPMOD64 243 ++#define R_PARISC_TLS_DTPOFF32 244 ++#define R_PARISC_TLS_DTPOFF64 245 ++#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L ++#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R ++#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L ++#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R ++#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 ++#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 ++#define R_PARISC_HIRESERVE 255 ++ ++ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++ ++ ++ ++ ++#define EF_ALPHA_32BIT 1 ++#define EF_ALPHA_CANRELAX 2 ++ ++ ++ ++ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++ ++#define STO_ALPHA_NOPV 0x80 ++#define STO_ALPHA_STD_GPLOAD 0x88 ++ ++ ++ ++#define R_ALPHA_NONE 0 ++#define R_ALPHA_REFLONG 1 ++#define R_ALPHA_REFQUAD 2 ++#define R_ALPHA_GPREL32 3 ++#define R_ALPHA_LITERAL 4 ++#define R_ALPHA_LITUSE 5 ++#define R_ALPHA_GPDISP 6 ++#define R_ALPHA_BRADDR 7 ++#define R_ALPHA_HINT 8 ++#define R_ALPHA_SREL16 9 ++#define R_ALPHA_SREL32 10 ++#define R_ALPHA_SREL64 11 ++#define R_ALPHA_GPRELHIGH 17 ++#define R_ALPHA_GPRELLOW 18 ++#define R_ALPHA_GPREL16 19 ++#define R_ALPHA_COPY 24 ++#define R_ALPHA_GLOB_DAT 25 ++#define R_ALPHA_JMP_SLOT 26 ++#define R_ALPHA_RELATIVE 27 ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++ ++#define R_ALPHA_NUM 46 ++ ++ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++ ++#define DT_ALPHA_PLTRO (DT_LOPROC + 0) ++#define DT_ALPHA_NUM 1 ++ ++ ++ ++ ++#define EF_PPC_EMB 0x80000000 ++ ++ ++#define EF_PPC_RELOCATABLE 0x00010000 ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 ++ ++ ++ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 ++#define R_PPC_ADDR24 2 ++#define R_PPC_ADDR16 3 ++#define R_PPC_ADDR16_LO 4 ++#define R_PPC_ADDR16_HI 5 ++#define R_PPC_ADDR16_HA 6 ++#define R_PPC_ADDR14 7 ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 ++#define R_PPC_REL14 11 ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++ ++#define R_PPC_TLS 67 ++#define R_PPC_DTPMOD32 68 ++#define R_PPC_TPREL16 69 ++#define R_PPC_TPREL16_LO 70 ++#define R_PPC_TPREL16_HI 71 ++#define R_PPC_TPREL16_HA 72 ++#define R_PPC_TPREL32 73 ++#define R_PPC_DTPREL16 74 ++#define R_PPC_DTPREL16_LO 75 ++#define R_PPC_DTPREL16_HI 76 ++#define R_PPC_DTPREL16_HA 77 ++#define R_PPC_DTPREL32 78 ++#define R_PPC_GOT_TLSGD16 79 ++#define R_PPC_GOT_TLSGD16_LO 80 ++#define R_PPC_GOT_TLSGD16_HI 81 ++#define R_PPC_GOT_TLSGD16_HA 82 ++#define R_PPC_GOT_TLSLD16 83 ++#define R_PPC_GOT_TLSLD16_LO 84 ++#define R_PPC_GOT_TLSLD16_HI 85 ++#define R_PPC_GOT_TLSLD16_HA 86 ++#define R_PPC_GOT_TPREL16 87 ++#define R_PPC_GOT_TPREL16_LO 88 ++#define R_PPC_GOT_TPREL16_HI 89 ++#define R_PPC_GOT_TPREL16_HA 90 ++#define R_PPC_GOT_DTPREL16 91 ++#define R_PPC_GOT_DTPREL16_LO 92 ++#define R_PPC_GOT_DTPREL16_HI 93 ++#define R_PPC_GOT_DTPREL16_HA 94 ++ ++ ++ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 ++ ++ ++#define R_PPC_DIAB_SDA21_LO 180 ++#define R_PPC_DIAB_SDA21_HI 181 ++#define R_PPC_DIAB_SDA21_HA 182 ++#define R_PPC_DIAB_RELSDA_LO 183 ++#define R_PPC_DIAB_RELSDA_HI 184 ++#define R_PPC_DIAB_RELSDA_HA 185 ++ ++ ++#define R_PPC_IRELATIVE 248 ++ ++ ++#define R_PPC_REL16 249 ++#define R_PPC_REL16_LO 250 ++#define R_PPC_REL16_HI 251 ++#define R_PPC_REL16_HA 252 ++ ++ ++ ++#define R_PPC_TOC16 255 ++ ++ ++#define DT_PPC_GOT (DT_LOPROC + 0) ++#define DT_PPC_NUM 1 ++ ++ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 ++#define R_PPC64_ADDR24 R_PPC_ADDR24 ++#define R_PPC64_ADDR16 R_PPC_ADDR16 ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA ++#define R_PPC64_ADDR14 R_PPC_ADDR14 ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 ++#define R_PPC64_REL14 R_PPC_REL14 ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 ++#define R_PPC64_ADDR64 38 ++#define R_PPC64_ADDR16_HIGHER 39 ++#define R_PPC64_ADDR16_HIGHERA 40 ++#define R_PPC64_ADDR16_HIGHEST 41 ++#define R_PPC64_ADDR16_HIGHESTA 42 ++#define R_PPC64_UADDR64 43 ++#define R_PPC64_REL64 44 ++#define R_PPC64_PLT64 45 ++#define R_PPC64_PLTREL64 46 ++#define R_PPC64_TOC16 47 ++#define R_PPC64_TOC16_LO 48 ++#define R_PPC64_TOC16_HI 49 ++#define R_PPC64_TOC16_HA 50 ++#define R_PPC64_TOC 51 ++#define R_PPC64_PLTGOT16 52 ++#define R_PPC64_PLTGOT16_LO 53 ++#define R_PPC64_PLTGOT16_HI 54 ++#define R_PPC64_PLTGOT16_HA 55 ++ ++#define R_PPC64_ADDR16_DS 56 ++#define R_PPC64_ADDR16_LO_DS 57 ++#define R_PPC64_GOT16_DS 58 ++#define R_PPC64_GOT16_LO_DS 59 ++#define R_PPC64_PLT16_LO_DS 60 ++#define R_PPC64_SECTOFF_DS 61 ++#define R_PPC64_SECTOFF_LO_DS 62 ++#define R_PPC64_TOC16_DS 63 ++#define R_PPC64_TOC16_LO_DS 64 ++#define R_PPC64_PLTGOT16_DS 65 ++#define R_PPC64_PLTGOT16_LO_DS 66 ++ ++ ++#define R_PPC64_TLS 67 ++#define R_PPC64_DTPMOD64 68 ++#define R_PPC64_TPREL16 69 ++#define R_PPC64_TPREL16_LO 70 ++#define R_PPC64_TPREL16_HI 71 ++#define R_PPC64_TPREL16_HA 72 ++#define R_PPC64_TPREL64 73 ++#define R_PPC64_DTPREL16 74 ++#define R_PPC64_DTPREL16_LO 75 ++#define R_PPC64_DTPREL16_HI 76 ++#define R_PPC64_DTPREL16_HA 77 ++#define R_PPC64_DTPREL64 78 ++#define R_PPC64_GOT_TLSGD16 79 ++#define R_PPC64_GOT_TLSGD16_LO 80 ++#define R_PPC64_GOT_TLSGD16_HI 81 ++#define R_PPC64_GOT_TLSGD16_HA 82 ++#define R_PPC64_GOT_TLSLD16 83 ++#define R_PPC64_GOT_TLSLD16_LO 84 ++#define R_PPC64_GOT_TLSLD16_HI 85 ++#define R_PPC64_GOT_TLSLD16_HA 86 ++#define R_PPC64_GOT_TPREL16_DS 87 ++#define R_PPC64_GOT_TPREL16_LO_DS 88 ++#define R_PPC64_GOT_TPREL16_HI 89 ++#define R_PPC64_GOT_TPREL16_HA 90 ++#define R_PPC64_GOT_DTPREL16_DS 91 ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 ++#define R_PPC64_GOT_DTPREL16_HI 93 ++#define R_PPC64_GOT_DTPREL16_HA 94 ++#define R_PPC64_TPREL16_DS 95 ++#define R_PPC64_TPREL16_LO_DS 96 ++#define R_PPC64_TPREL16_HIGHER 97 ++#define R_PPC64_TPREL16_HIGHERA 98 ++#define R_PPC64_TPREL16_HIGHEST 99 ++#define R_PPC64_TPREL16_HIGHESTA 100 ++#define R_PPC64_DTPREL16_DS 101 ++#define R_PPC64_DTPREL16_LO_DS 102 ++#define R_PPC64_DTPREL16_HIGHER 103 ++#define R_PPC64_DTPREL16_HIGHERA 104 ++#define R_PPC64_DTPREL16_HIGHEST 105 ++#define R_PPC64_DTPREL16_HIGHESTA 106 ++ ++ ++#define R_PPC64_JMP_IREL 247 ++#define R_PPC64_IRELATIVE 248 ++#define R_PPC64_REL16 249 ++#define R_PPC64_REL16_LO 250 ++#define R_PPC64_REL16_HI 251 ++#define R_PPC64_REL16_HA 252 ++ ++ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_OPD (DT_LOPROC + 1) ++#define DT_PPC64_OPDSZ (DT_LOPROC + 2) ++#define DT_PPC64_NUM 3 ++ ++ ++ ++ ++ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++#define EF_ARM_SOFT_FLOAT 0x200 ++#define EF_ARM_VFP_FLOAT 0x400 ++#define EF_ARM_MAVERICK_FLOAT 0x800 ++ ++#define EF_ARM_ABI_FLOAT_SOFT 0x200 ++#define EF_ARM_ABI_FLOAT_HARD 0x400 ++ ++ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++ ++#define EF_ARM_BE8 0x00800000 ++#define EF_ARM_LE8 0x00400000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++#define EF_ARM_EABI_VER3 0x03000000 ++#define EF_ARM_EABI_VER4 0x04000000 ++#define EF_ARM_EABI_VER5 0x05000000 ++ ++ ++#define STT_ARM_TFUNC STT_LOPROC ++#define STT_ARM_16BIT STT_HIPROC ++ ++ ++#define SHF_ARM_ENTRYSECT 0x10000000 ++#define SHF_ARM_COMDEF 0x80000000 ++ ++ ++ ++#define PF_ARM_SB 0x10000000 ++ ++#define PF_ARM_PI 0x20000000 ++#define PF_ARM_ABS 0x40000000 ++ ++ ++#define PT_ARM_EXIDX (PT_LOPROC + 1) ++ ++ ++#define SHT_ARM_EXIDX (SHT_LOPROC + 1) ++#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) ++#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) ++ ++ ++#define R_AARCH64_NONE 0 ++#define R_AARCH64_ABS64 257 ++#define R_AARCH64_ABS32 258 ++#define R_AARCH64_COPY 1024 ++#define R_AARCH64_GLOB_DAT 1025 ++#define R_AARCH64_JUMP_SLOT 1026 ++#define R_AARCH64_RELATIVE 1027 ++#define R_AARCH64_TLS_DTPMOD64 1028 ++#define R_AARCH64_TLS_DTPREL64 1029 ++#define R_AARCH64_TLS_TPREL64 1030 ++#define R_AARCH64_TLSDESC 1031 ++ ++ ++#define R_ARM_NONE 0 ++#define R_ARM_PC24 1 ++#define R_ARM_ABS32 2 ++#define R_ARM_REL32 3 ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 ++#define R_ARM_ABS12 6 ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_TLS_DESC 13 ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_TLS_DTPMOD32 17 ++#define R_ARM_TLS_DTPOFF32 18 ++#define R_ARM_TLS_TPOFF32 19 ++#define R_ARM_COPY 20 ++#define R_ARM_GLOB_DAT 21 ++#define R_ARM_JUMP_SLOT 22 ++#define R_ARM_RELATIVE 23 ++#define R_ARM_GOTOFF 24 ++#define R_ARM_GOTPC 25 ++#define R_ARM_GOT32 26 ++#define R_ARM_PLT32 27 ++#define R_ARM_CALL 28 ++#define R_ARM_JUMP24 29 ++#define R_ARM_THM_JUMP24 30 ++#define R_ARM_BASE_ABS 31 ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_TARGET1 38 ++#define R_ARM_SBREL31 39 ++#define R_ARM_V4BX 40 ++#define R_ARM_TARGET2 41 ++#define R_ARM_PREL31 42 ++#define R_ARM_MOVW_ABS_NC 43 ++#define R_ARM_MOVT_ABS 44 ++#define R_ARM_MOVW_PREL_NC 45 ++#define R_ARM_MOVT_PREL 46 ++#define R_ARM_THM_MOVW_ABS_NC 47 ++#define R_ARM_THM_MOVT_ABS 48 ++#define R_ARM_THM_MOVW_PREL_NC 49 ++#define R_ARM_THM_MOVT_PREL 50 ++#define R_ARM_THM_JUMP19 51 ++#define R_ARM_THM_JUMP6 52 ++#define R_ARM_THM_ALU_PREL_11_0 53 ++#define R_ARM_THM_PC12 54 ++#define R_ARM_ABS32_NOI 55 ++#define R_ARM_REL32_NOI 56 ++#define R_ARM_ALU_PC_G0_NC 57 ++#define R_ARM_ALU_PC_G0 58 ++#define R_ARM_ALU_PC_G1_NC 59 ++#define R_ARM_ALU_PC_G1 60 ++#define R_ARM_ALU_PC_G2 61 ++#define R_ARM_LDR_PC_G1 62 ++#define R_ARM_LDR_PC_G2 63 ++#define R_ARM_LDRS_PC_G0 64 ++#define R_ARM_LDRS_PC_G1 65 ++#define R_ARM_LDRS_PC_G2 66 ++#define R_ARM_LDC_PC_G0 67 ++#define R_ARM_LDC_PC_G1 68 ++#define R_ARM_LDC_PC_G2 69 ++#define R_ARM_ALU_SB_G0_NC 70 ++#define R_ARM_ALU_SB_G0 71 ++#define R_ARM_ALU_SB_G1_NC 72 ++#define R_ARM_ALU_SB_G1 73 ++#define R_ARM_ALU_SB_G2 74 ++#define R_ARM_LDR_SB_G0 75 ++#define R_ARM_LDR_SB_G1 76 ++#define R_ARM_LDR_SB_G2 77 ++#define R_ARM_LDRS_SB_G0 78 ++#define R_ARM_LDRS_SB_G1 79 ++#define R_ARM_LDRS_SB_G2 80 ++#define R_ARM_LDC_SB_G0 81 ++#define R_ARM_LDC_SB_G1 82 ++#define R_ARM_LDC_SB_G2 83 ++#define R_ARM_MOVW_BREL_NC 84 ++#define R_ARM_MOVT_BREL 85 ++#define R_ARM_MOVW_BREL 86 ++#define R_ARM_THM_MOVW_BREL_NC 87 ++#define R_ARM_THM_MOVT_BREL 88 ++#define R_ARM_THM_MOVW_BREL 89 ++#define R_ARM_TLS_GOTDESC 90 ++#define R_ARM_TLS_CALL 91 ++#define R_ARM_TLS_DESCSEQ 92 ++#define R_ARM_THM_TLS_CALL 93 ++#define R_ARM_PLT32_ABS 94 ++#define R_ARM_GOT_ABS 95 ++#define R_ARM_GOT_PREL 96 ++#define R_ARM_GOT_BREL12 97 ++#define R_ARM_GOTOFF12 98 ++#define R_ARM_GOTRELAX 99 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 ++#define R_ARM_THM_PC9 103 ++#define R_ARM_TLS_GD32 104 ++ ++#define R_ARM_TLS_LDM32 105 ++ ++#define R_ARM_TLS_LDO32 106 ++ ++#define R_ARM_TLS_IE32 107 ++ ++#define R_ARM_TLS_LE32 108 ++#define R_ARM_TLS_LDO12 109 ++#define R_ARM_TLS_LE12 110 ++#define R_ARM_TLS_IE12GP 111 ++#define R_ARM_ME_TOO 128 ++#define R_ARM_THM_TLS_DESCSEQ 129 ++#define R_ARM_THM_TLS_DESCSEQ16 129 ++#define R_ARM_THM_TLS_DESCSEQ32 130 ++#define R_ARM_THM_GOT_BREL12 131 ++#define R_ARM_IRELATIVE 160 ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++ ++#define R_ARM_NUM 256 ++ ++ ++ ++ ++#define EF_IA_64_MASKOS 0x0000000f ++#define EF_IA_64_ABI64 0x00000010 ++#define EF_IA_64_ARCH 0xff000000 ++ ++ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) ++#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) ++#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) ++#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) ++ ++ ++#define PF_IA_64_NORECOV 0x80000000 ++ ++ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) ++ ++ ++#define SHF_IA_64_SHORT 0x10000000 ++#define SHF_IA_64_NORECOV 0x20000000 ++ ++ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++ ++#define R_IA64_NONE 0x00 ++#define R_IA64_IMM14 0x21 ++#define R_IA64_IMM22 0x22 ++#define R_IA64_IMM64 0x23 ++#define R_IA64_DIR32MSB 0x24 ++#define R_IA64_DIR32LSB 0x25 ++#define R_IA64_DIR64MSB 0x26 ++#define R_IA64_DIR64LSB 0x27 ++#define R_IA64_GPREL22 0x2a ++#define R_IA64_GPREL64I 0x2b ++#define R_IA64_GPREL32MSB 0x2c ++#define R_IA64_GPREL32LSB 0x2d ++#define R_IA64_GPREL64MSB 0x2e ++#define R_IA64_GPREL64LSB 0x2f ++#define R_IA64_LTOFF22 0x32 ++#define R_IA64_LTOFF64I 0x33 ++#define R_IA64_PLTOFF22 0x3a ++#define R_IA64_PLTOFF64I 0x3b ++#define R_IA64_PLTOFF64MSB 0x3e ++#define R_IA64_PLTOFF64LSB 0x3f ++#define R_IA64_FPTR64I 0x43 ++#define R_IA64_FPTR32MSB 0x44 ++#define R_IA64_FPTR32LSB 0x45 ++#define R_IA64_FPTR64MSB 0x46 ++#define R_IA64_FPTR64LSB 0x47 ++#define R_IA64_PCREL60B 0x48 ++#define R_IA64_PCREL21B 0x49 ++#define R_IA64_PCREL21M 0x4a ++#define R_IA64_PCREL21F 0x4b ++#define R_IA64_PCREL32MSB 0x4c ++#define R_IA64_PCREL32LSB 0x4d ++#define R_IA64_PCREL64MSB 0x4e ++#define R_IA64_PCREL64LSB 0x4f ++#define R_IA64_LTOFF_FPTR22 0x52 ++#define R_IA64_LTOFF_FPTR64I 0x53 ++#define R_IA64_LTOFF_FPTR32MSB 0x54 ++#define R_IA64_LTOFF_FPTR32LSB 0x55 ++#define R_IA64_LTOFF_FPTR64MSB 0x56 ++#define R_IA64_LTOFF_FPTR64LSB 0x57 ++#define R_IA64_SEGREL32MSB 0x5c ++#define R_IA64_SEGREL32LSB 0x5d ++#define R_IA64_SEGREL64MSB 0x5e ++#define R_IA64_SEGREL64LSB 0x5f ++#define R_IA64_SECREL32MSB 0x64 ++#define R_IA64_SECREL32LSB 0x65 ++#define R_IA64_SECREL64MSB 0x66 ++#define R_IA64_SECREL64LSB 0x67 ++#define R_IA64_REL32MSB 0x6c ++#define R_IA64_REL32LSB 0x6d ++#define R_IA64_REL64MSB 0x6e ++#define R_IA64_REL64LSB 0x6f ++#define R_IA64_LTV32MSB 0x74 ++#define R_IA64_LTV32LSB 0x75 ++#define R_IA64_LTV64MSB 0x76 ++#define R_IA64_LTV64LSB 0x77 ++#define R_IA64_PCREL21BI 0x79 ++#define R_IA64_PCREL22 0x7a ++#define R_IA64_PCREL64I 0x7b ++#define R_IA64_IPLTMSB 0x80 ++#define R_IA64_IPLTLSB 0x81 ++#define R_IA64_COPY 0x84 ++#define R_IA64_SUB 0x85 ++#define R_IA64_LTOFF22X 0x86 ++#define R_IA64_LDXMOV 0x87 ++#define R_IA64_TPREL14 0x91 ++#define R_IA64_TPREL22 0x92 ++#define R_IA64_TPREL64I 0x93 ++#define R_IA64_TPREL64MSB 0x96 ++#define R_IA64_TPREL64LSB 0x97 ++#define R_IA64_LTOFF_TPREL22 0x9a ++#define R_IA64_DTPMOD64MSB 0xa6 ++#define R_IA64_DTPMOD64LSB 0xa7 ++#define R_IA64_LTOFF_DTPMOD22 0xaa ++#define R_IA64_DTPREL14 0xb1 ++#define R_IA64_DTPREL22 0xb2 ++#define R_IA64_DTPREL64I 0xb3 ++#define R_IA64_DTPREL32MSB 0xb4 ++#define R_IA64_DTPREL32LSB 0xb5 ++#define R_IA64_DTPREL64MSB 0xb6 ++#define R_IA64_DTPREL64LSB 0xb7 ++#define R_IA64_LTOFF_DTPREL22 0xba ++ ++ ++ ++ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++ ++#define R_SH_NUM 256 ++ ++ ++ ++#define R_390_NONE 0 ++#define R_390_8 1 ++#define R_390_12 2 ++#define R_390_16 3 ++#define R_390_32 4 ++#define R_390_PC32 5 ++#define R_390_GOT12 6 ++#define R_390_GOT32 7 ++#define R_390_PLT32 8 ++#define R_390_COPY 9 ++#define R_390_GLOB_DAT 10 ++#define R_390_JMP_SLOT 11 ++#define R_390_RELATIVE 12 ++#define R_390_GOTOFF32 13 ++#define R_390_GOTPC 14 ++#define R_390_GOT16 15 ++#define R_390_PC16 16 ++#define R_390_PC16DBL 17 ++#define R_390_PLT16DBL 18 ++#define R_390_PC32DBL 19 ++#define R_390_PLT32DBL 20 ++#define R_390_GOTPCDBL 21 ++#define R_390_64 22 ++#define R_390_PC64 23 ++#define R_390_GOT64 24 ++#define R_390_PLT64 25 ++#define R_390_GOTENT 26 ++#define R_390_GOTOFF16 27 ++#define R_390_GOTOFF64 28 ++#define R_390_GOTPLT12 29 ++#define R_390_GOTPLT16 30 ++#define R_390_GOTPLT32 31 ++#define R_390_GOTPLT64 32 ++#define R_390_GOTPLTENT 33 ++#define R_390_PLTOFF16 34 ++#define R_390_PLTOFF32 35 ++#define R_390_PLTOFF64 36 ++#define R_390_TLS_LOAD 37 ++#define R_390_TLS_GDCALL 38 ++ ++#define R_390_TLS_LDCALL 39 ++ ++#define R_390_TLS_GD32 40 ++ ++#define R_390_TLS_GD64 41 ++ ++#define R_390_TLS_GOTIE12 42 ++ ++#define R_390_TLS_GOTIE32 43 ++ ++#define R_390_TLS_GOTIE64 44 ++ ++#define R_390_TLS_LDM32 45 ++ ++#define R_390_TLS_LDM64 46 ++ ++#define R_390_TLS_IE32 47 ++ ++#define R_390_TLS_IE64 48 ++ ++#define R_390_TLS_IEENT 49 ++ ++#define R_390_TLS_LE32 50 ++ ++#define R_390_TLS_LE64 51 ++ ++#define R_390_TLS_LDO32 52 ++ ++#define R_390_TLS_LDO64 53 ++ ++#define R_390_TLS_DTPMOD 54 ++#define R_390_TLS_DTPOFF 55 ++#define R_390_TLS_TPOFF 56 ++ ++#define R_390_20 57 ++#define R_390_GOT20 58 ++#define R_390_GOTPLT20 59 ++#define R_390_TLS_GOTIE20 60 ++ ++ ++#define R_390_NUM 61 ++ ++ ++ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++ ++ ++#define R_X86_64_NONE 0 ++#define R_X86_64_64 1 ++#define R_X86_64_PC32 2 ++#define R_X86_64_GOT32 3 ++#define R_X86_64_PLT32 4 ++#define R_X86_64_COPY 5 ++#define R_X86_64_GLOB_DAT 6 ++#define R_X86_64_JUMP_SLOT 7 ++#define R_X86_64_RELATIVE 8 ++#define R_X86_64_GOTPCREL 9 ++ ++#define R_X86_64_32 10 ++#define R_X86_64_32S 11 ++#define R_X86_64_16 12 ++#define R_X86_64_PC16 13 ++#define R_X86_64_8 14 ++#define R_X86_64_PC8 15 ++#define R_X86_64_DTPMOD64 16 ++#define R_X86_64_DTPOFF64 17 ++#define R_X86_64_TPOFF64 18 ++#define R_X86_64_TLSGD 19 ++ ++#define R_X86_64_TLSLD 20 ++ ++#define R_X86_64_DTPOFF32 21 ++#define R_X86_64_GOTTPOFF 22 ++ ++#define R_X86_64_TPOFF32 23 ++#define R_X86_64_PC64 24 ++#define R_X86_64_GOTOFF64 25 ++#define R_X86_64_GOTPC32 26 ++#define R_X86_64_GOT64 27 ++#define R_X86_64_GOTPCREL64 28 ++#define R_X86_64_GOTPC64 29 ++#define R_X86_64_GOTPLT64 30 ++#define R_X86_64_PLTOFF64 31 ++#define R_X86_64_SIZE32 32 ++#define R_X86_64_SIZE64 33 ++ ++#define R_X86_64_GOTPC32_TLSDESC 34 ++#define R_X86_64_TLSDESC_CALL 35 ++ ++#define R_X86_64_TLSDESC 36 ++#define R_X86_64_IRELATIVE 37 ++#define R_X86_64_RELATIVE64 38 ++#define R_X86_64_NUM 39 ++ ++ ++ ++#define R_MN10300_NONE 0 ++#define R_MN10300_32 1 ++#define R_MN10300_16 2 ++#define R_MN10300_8 3 ++#define R_MN10300_PCREL32 4 ++#define R_MN10300_PCREL16 5 ++#define R_MN10300_PCREL8 6 ++#define R_MN10300_GNU_VTINHERIT 7 ++#define R_MN10300_GNU_VTENTRY 8 ++#define R_MN10300_24 9 ++#define R_MN10300_GOTPC32 10 ++#define R_MN10300_GOTPC16 11 ++#define R_MN10300_GOTOFF32 12 ++#define R_MN10300_GOTOFF24 13 ++#define R_MN10300_GOTOFF16 14 ++#define R_MN10300_PLT32 15 ++#define R_MN10300_PLT16 16 ++#define R_MN10300_GOT32 17 ++#define R_MN10300_GOT24 18 ++#define R_MN10300_GOT16 19 ++#define R_MN10300_COPY 20 ++#define R_MN10300_GLOB_DAT 21 ++#define R_MN10300_JMP_SLOT 22 ++#define R_MN10300_RELATIVE 23 ++ ++#define R_MN10300_NUM 24 ++ ++ ++ ++#define R_M32R_NONE 0 ++#define R_M32R_16 1 ++#define R_M32R_32 2 ++#define R_M32R_24 3 ++#define R_M32R_10_PCREL 4 ++#define R_M32R_18_PCREL 5 ++#define R_M32R_26_PCREL 6 ++#define R_M32R_HI16_ULO 7 ++#define R_M32R_HI16_SLO 8 ++#define R_M32R_LO16 9 ++#define R_M32R_SDA16 10 ++#define R_M32R_GNU_VTINHERIT 11 ++#define R_M32R_GNU_VTENTRY 12 ++ ++#define R_M32R_16_RELA 33 ++#define R_M32R_32_RELA 34 ++#define R_M32R_24_RELA 35 ++#define R_M32R_10_PCREL_RELA 36 ++#define R_M32R_18_PCREL_RELA 37 ++#define R_M32R_26_PCREL_RELA 38 ++#define R_M32R_HI16_ULO_RELA 39 ++#define R_M32R_HI16_SLO_RELA 40 ++#define R_M32R_LO16_RELA 41 ++#define R_M32R_SDA16_RELA 42 ++#define R_M32R_RELA_GNU_VTINHERIT 43 ++#define R_M32R_RELA_GNU_VTENTRY 44 ++#define R_M32R_REL32 45 ++ ++#define R_M32R_GOT24 48 ++#define R_M32R_26_PLTREL 49 ++#define R_M32R_COPY 50 ++#define R_M32R_GLOB_DAT 51 ++#define R_M32R_JMP_SLOT 52 ++#define R_M32R_RELATIVE 53 ++#define R_M32R_GOTOFF 54 ++#define R_M32R_GOTPC24 55 ++#define R_M32R_GOT16_HI_ULO 56 ++ ++#define R_M32R_GOT16_HI_SLO 57 ++ ++#define R_M32R_GOT16_LO 58 ++#define R_M32R_GOTPC_HI_ULO 59 ++ ++#define R_M32R_GOTPC_HI_SLO 60 ++ ++#define R_M32R_GOTPC_LO 61 ++ ++#define R_M32R_GOTOFF_HI_ULO 62 ++ ++#define R_M32R_GOTOFF_HI_SLO 63 ++ ++#define R_M32R_GOTOFF_LO 64 ++#define R_M32R_NUM 256 ++ ++#define R_MICROBLAZE_NONE 0 ++#define R_MICROBLAZE_32 1 ++#define R_MICROBLAZE_32_PCREL 2 ++#define R_MICROBLAZE_64_PCREL 3 ++#define R_MICROBLAZE_32_PCREL_LO 4 ++#define R_MICROBLAZE_64 5 ++#define R_MICROBLAZE_32_LO 6 ++#define R_MICROBLAZE_SRO32 7 ++#define R_MICROBLAZE_SRW32 8 ++#define R_MICROBLAZE_64_NONE 9 ++#define R_MICROBLAZE_32_SYM_OP_SYM 10 ++#define R_MICROBLAZE_GNU_VTINHERIT 11 ++#define R_MICROBLAZE_GNU_VTENTRY 12 ++#define R_MICROBLAZE_GOTPC_64 13 ++#define R_MICROBLAZE_GOT_64 14 ++#define R_MICROBLAZE_PLT_64 15 ++#define R_MICROBLAZE_REL 16 ++#define R_MICROBLAZE_JUMP_SLOT 17 ++#define R_MICROBLAZE_GLOB_DAT 18 ++#define R_MICROBLAZE_GOTOFF_64 19 ++#define R_MICROBLAZE_GOTOFF_32 20 ++#define R_MICROBLAZE_COPY 21 ++#define R_MICROBLAZE_TLS 22 ++#define R_MICROBLAZE_TLSGD 23 ++#define R_MICROBLAZE_TLSLD 24 ++#define R_MICROBLAZE_TLSDTPMOD32 25 ++#define R_MICROBLAZE_TLSDTPREL32 26 ++#define R_MICROBLAZE_TLSDTPREL64 27 ++#define R_MICROBLAZE_TLSGOTTPREL32 28 ++#define R_MICROBLAZE_TLSTPREL32 29 ++ ++#ifdef __cplusplus ++} ++#endif ++ ++ ++#endif diff --git a/target/linux/patches/3.10.81/sgidefs.patch b/target/linux/patches/3.10.81/sgidefs.patch new file mode 100644 index 000000000..f00a284d9 --- /dev/null +++ b/target/linux/patches/3.10.81/sgidefs.patch @@ -0,0 +1,18 @@ +diff -Nur linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h +--- linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h 2013-11-08 22:01:28.000000000 +0100 +@@ -11,14 +11,6 @@ + #define __ASM_SGIDEFS_H + + /* +- * Using a Linux compiler for building Linux seems logic but not to +- * everybody. +- */ +-#ifndef __linux__ +-#error Use a Linux compiler or give up. +-#endif +- +-/* + * Definitions for the ISA levels + * + * With the introduction of MIPS32 / MIPS64 instruction sets definitions diff --git a/target/linux/patches/3.10.81/sortext.patch b/target/linux/patches/3.10.81/sortext.patch new file mode 100644 index 000000000..65bbbb64b --- /dev/null +++ b/target/linux/patches/3.10.81/sortext.patch @@ -0,0 +1,33 @@ +diff -Nur linux-3.11.5.orig/arch/arm/Kconfig linux-3.11.5/arch/arm/Kconfig +--- linux-3.11.5.orig/arch/arm/Kconfig 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/arm/Kconfig 2013-10-16 10:36:41.000000000 +0200 +@@ -6,7 +6,6 @@ + select ARCH_HAVE_CUSTOM_GPIO_H + select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST + select ARCH_WANT_IPC_PARSE_VERSION +- select BUILDTIME_EXTABLE_SORT if MMU + select CPU_PM if (SUSPEND || CPU_IDLE) + select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && !CPU_BIG_ENDIAN && MMU + select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI) +diff -Nur linux-3.11.5.orig/arch/mips/Kconfig linux-3.11.5/arch/mips/Kconfig +--- linux-3.11.5.orig/arch/mips/Kconfig 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/mips/Kconfig 2013-10-16 10:36:41.000000000 +0200 +@@ -36,7 +36,6 @@ + select HAVE_MEMBLOCK_NODE_MAP + select ARCH_DISCARD_MEMBLOCK + select GENERIC_SMP_IDLE_THREAD +- select BUILDTIME_EXTABLE_SORT + select GENERIC_CLOCKEVENTS + select GENERIC_CMOS_UPDATE + select HAVE_MOD_ARCH_SPECIFIC +diff -Nur linux-3.11.5.orig/arch/x86/Kconfig linux-3.11.5/arch/x86/Kconfig +--- linux-3.11.5.orig/arch/x86/Kconfig 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/x86/Kconfig 2013-10-16 10:36:55.000000000 +0200 +@@ -101,7 +101,6 @@ + select GENERIC_SMP_IDLE_THREAD + select ARCH_WANT_IPC_PARSE_VERSION if X86_32 + select HAVE_ARCH_SECCOMP_FILTER +- select BUILDTIME_EXTABLE_SORT + select GENERIC_CMOS_UPDATE + select HAVE_ARCH_SOFT_DIRTY + select CLOCKSOURCE_WATCHDOG diff --git a/target/linux/patches/3.10.81/startup.patch b/target/linux/patches/3.10.81/startup.patch new file mode 100644 index 000000000..3ebc5db1e --- /dev/null +++ b/target/linux/patches/3.10.81/startup.patch @@ -0,0 +1,34 @@ +diff -Nur linux-3.10.33.orig/init/initramfs.c linux-3.10.33/init/initramfs.c +--- linux-3.10.33.orig/init/initramfs.c 2014-03-07 06:58:45.000000000 +0100 ++++ linux-3.10.33/init/initramfs.c 2014-03-15 18:32:07.240436750 +0100 +@@ -622,6 +622,9 @@ + */ + load_default_modules(); + } ++#ifdef CONFIG_DEVTMPFS_MOUNT ++ devtmpfs_mount("dev"); ++#endif + return 0; + } + rootfs_initcall(populate_rootfs); +diff -Nur linux-3.10.33.orig/init/main.c linux-3.10.33/init/main.c +--- linux-3.10.33.orig/init/main.c 2014-03-07 06:58:45.000000000 +0100 ++++ linux-3.10.33/init/main.c 2014-03-15 18:32:07.240436750 +0100 +@@ -885,6 +885,8 @@ + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + pr_err("Warning: unable to open an initial console.\n"); + ++ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); ++ + (void) sys_dup(0); + (void) sys_dup(0); + /* +@@ -893,7 +895,7 @@ + */ + + if (!ramdisk_execute_command) +- ramdisk_execute_command = "/init"; ++ ramdisk_execute_command = "/sbin/init"; + + if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { + ramdisk_execute_command = NULL; diff --git a/target/linux/patches/3.10.81/yaffs2.patch b/target/linux/patches/3.10.81/yaffs2.patch new file mode 100644 index 000000000..172629530 --- /dev/null +++ b/target/linux/patches/3.10.81/yaffs2.patch @@ -0,0 +1,16547 @@ +diff -Nur linux-3.10.40.orig/fs/Kconfig linux-3.10.40/fs/Kconfig +--- linux-3.10.40.orig/fs/Kconfig 2014-05-13 14:00:04.000000000 +0200 ++++ linux-3.10.40/fs/Kconfig 2014-05-17 12:48:10.000000000 +0200 +@@ -193,6 +193,7 @@ + source "fs/befs/Kconfig" + source "fs/bfs/Kconfig" + source "fs/efs/Kconfig" ++source "fs/yaffs2/Kconfig" + source "fs/jffs2/Kconfig" + # UBIFS File system configuration + source "fs/ubifs/Kconfig" +diff -Nur linux-3.10.40.orig/fs/Makefile linux-3.10.40/fs/Makefile +--- linux-3.10.40.orig/fs/Makefile 2014-05-13 14:00:04.000000000 +0200 ++++ linux-3.10.40/fs/Makefile 2014-05-17 12:48:10.000000000 +0200 +@@ -126,3 +126,4 @@ + obj-$(CONFIG_CEPH_FS) += ceph/ + obj-$(CONFIG_PSTORE) += pstore/ + obj-$(CONFIG_EFIVAR_FS) += efivarfs/ ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ +diff -Nur linux-3.10.40.orig/fs/yaffs2/Kconfig linux-3.10.40/fs/yaffs2/Kconfig +--- linux-3.10.40.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/Kconfig 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,171 @@ ++# ++# yaffs file system configurations ++# ++ ++config YAFFS_FS ++ tristate "yaffs2 file system support" ++ default n ++ depends on MTD_BLOCK ++ select YAFFS_YAFFS1 ++ select YAFFS_YAFFS2 ++ help ++ yaffs2, or Yet Another Flash File System, is a file system ++ optimised for NAND Flash chips. ++ ++ To compile the yaffs2 file system support as a module, choose M ++ here: the module will be called yaffs2. ++ ++ If unsure, say N. ++ ++ Further information on yaffs2 is available at ++ . ++ ++config YAFFS_YAFFS1 ++ bool "512 byte / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs1 support -- yaffs for 512 byte / page devices ++ ++ Not needed for 2K-page devices. ++ ++ If unsure, say Y. ++ ++config YAFFS_9BYTE_TAGS ++ bool "Use older-style on-NAND data format with pageStatus byte" ++ depends on YAFFS_YAFFS1 ++ default n ++ help ++ ++ Older-style on-NAND data format has a "pageStatus" byte to record ++ chunk/page state. This byte is zero when the page is discarded. ++ Choose this option if you have existing on-NAND data using this ++ format that you need to continue to support. New data written ++ also uses the older-style format. Note: Use of this option ++ generally requires that MTD's oob layout be adjusted to use the ++ older-style format. See notes on tags formats and MTD versions ++ in yaffs_mtdif1.c. ++ ++ If unsure, say N. ++ ++config YAFFS_DOES_ECC ++ bool "Lets yaffs do its own ECC" ++ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This enables yaffs to use its own ECC functions instead of using ++ the ones from the generic MTD-NAND driver. ++ ++ If unsure, say N. ++ ++config YAFFS_ECC_WRONG_ORDER ++ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" ++ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This makes yaffs_ecc.c use the same ecc byte order as Steven ++ Hill's nand_ecc.c. If not set, then you get the same ecc byte ++ order as SmartMedia. ++ ++ If unsure, say N. ++ ++config YAFFS_YAFFS2 ++ bool "2048 byte (or larger) / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices ++ ++ If unsure, say Y. ++ ++config YAFFS_AUTO_YAFFS2 ++ bool "Autoselect yaffs2 format" ++ depends on YAFFS_YAFFS2 ++ default y ++ help ++ Without this, you need to explicitely use yaffs2 as the file ++ system type. With this, you can say "yaffs" and yaffs or yaffs2 ++ will be used depending on the device page size (yaffs on ++ 512-byte page devices, yaffs2 on 2K page devices). ++ ++ If unsure, say Y. ++ ++config YAFFS_DISABLE_TAGS_ECC ++ bool "Disable yaffs from doing ECC on tags by default" ++ depends on YAFFS_FS && YAFFS_YAFFS2 ++ default n ++ help ++ This defaults yaffs to using its own ECC calculations on tags instead of ++ just relying on the MTD. ++ This behavior can also be overridden with tags_ecc_on and ++ tags_ecc_off mount options. ++ ++ If unsure, say N. ++ ++config YAFFS_ALWAYS_CHECK_CHUNK_ERASED ++ bool "Force chunk erase check" ++ depends on YAFFS_FS ++ default n ++ help ++ Normally yaffs only checks chunks before writing until an erased ++ chunk is found. This helps to detect any partially written ++ chunks that might have happened due to power loss. ++ ++ Enabling this forces on the test that chunks are erased in flash ++ before writing to them. This takes more time but is potentially ++ a bit more secure. ++ ++ Suggest setting Y during development and ironing out driver ++ issues etc. Suggest setting to N if you want faster writing. ++ ++ If unsure, say Y. ++ ++config YAFFS_EMPTY_LOST_AND_FOUND ++ bool "Empty lost and found on boot" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is enabled then the contents of lost and found is ++ automatically dumped at mount. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BLOCK_REFRESHING ++ bool "Disable yaffs2 block refreshing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then block refreshing is disabled. ++ Block refreshing infrequently refreshes the oldest block in ++ a yaffs2 file system. This mechanism helps to refresh flash to ++ mitigate against data loss. This is particularly useful for MLC. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BACKGROUND ++ bool "Disable yaffs2 background processing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then background processing is disabled. ++ Background processing makes many foreground activities faster. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BAD_BLOCK_MARKING ++ bool "Disable yaffs2 bad block marking" ++ depends on YAFFS_FS ++ default n ++ help ++ Useful during early flash bring up to prevent problems causing ++ lots of bad block marking. ++ ++ If unsure, say N. ++ ++config YAFFS_XATTR ++ bool "Enable yaffs2 xattr support" ++ depends on YAFFS_FS ++ default y ++ help ++ If this is set then yaffs2 will provide xattr support. ++ If unsure, say Y. +diff -Nur linux-3.10.40.orig/fs/yaffs2/Makefile linux-3.10.40/fs/yaffs2/Makefile +--- linux-3.10.40.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/Makefile 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,18 @@ ++# ++# Makefile for the linux YAFFS filesystem routines. ++# ++ ++obj-$(CONFIG_YAFFS_FS) += yaffs.o ++ ++yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o ++yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o ++yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o ++yaffs-y += yaffs_mtdif.o ++yaffs-y += yaffs_nameval.o yaffs_attribs.o ++yaffs-y += yaffs_allocator.o ++yaffs-y += yaffs_yaffs1.o ++yaffs-y += yaffs_yaffs2.o ++yaffs-y += yaffs_bitmap.o ++yaffs-y += yaffs_summary.o ++yaffs-y += yaffs_verify.o ++ +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_allocator.c linux-3.10.40/fs/yaffs2/yaffs_allocator.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_allocator.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,357 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_allocator.h" ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yportenv.h" ++ ++/* ++ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks ++ * of approx 100 objects that are themn allocated singly. ++ * This is basically a simplified slab allocator. ++ * ++ * We don't use the Linux slab allocator because slab does not allow ++ * us to dump all the objects in one hit when we do a umount and tear ++ * down all the tnodes and objects. slab requires that we first free ++ * the individual objects. ++ * ++ * Once yaffs has been mainlined I shall try to motivate for a change ++ * to slab to provide the extra features we need here. ++ */ ++ ++struct yaffs_tnode_list { ++ struct yaffs_tnode_list *next; ++ struct yaffs_tnode *tnodes; ++}; ++ ++struct yaffs_obj_list { ++ struct yaffs_obj_list *next; ++ struct yaffs_obj *objects; ++}; ++ ++struct yaffs_allocator { ++ int n_tnodes_created; ++ struct yaffs_tnode *free_tnodes; ++ int n_free_tnodes; ++ struct yaffs_tnode_list *alloc_tnode_list; ++ ++ int n_obj_created; ++ struct list_head free_objs; ++ int n_free_objects; ++ ++ struct yaffs_obj_list *allocated_obj_list; ++}; ++ ++static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->alloc_tnode_list) { ++ tmp = allocator->alloc_tnode_list->next; ++ ++ kfree(allocator->alloc_tnode_list->tnodes); ++ kfree(allocator->alloc_tnode_list); ++ allocator->alloc_tnode_list = tmp; ++ } ++ ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->alloc_tnode_list = NULL; ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ int i; ++ struct yaffs_tnode *new_tnodes; ++ u8 *mem; ++ struct yaffs_tnode *curr; ++ struct yaffs_tnode *next; ++ struct yaffs_tnode_list *tnl; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_tnodes < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); ++ mem = (u8 *) new_tnodes; ++ ++ if (!new_tnodes) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: Could not allocate Tnodes"); ++ return YAFFS_FAIL; ++ } ++ ++ /* New hookup for wide tnodes */ ++ for (i = 0; i < n_tnodes - 1; i++) { ++ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; ++ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; ++ curr->internal[0] = next; ++ } ++ ++ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; ++ curr->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = (struct yaffs_tnode *)mem; ++ ++ allocator->n_free_tnodes += n_tnodes; ++ allocator->n_tnodes_created += n_tnodes; ++ ++ /* Now add this bunch of tnodes to a list for freeing up. ++ * NB If we can't add this to the management list it isn't fatal ++ * but it just means we can't free this bunch of tnodes later. ++ */ ++ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); ++ if (!tnl) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Could not add tnodes to management list"); ++ return YAFFS_FAIL; ++ } else { ++ tnl->tnodes = new_tnodes; ++ tnl->next = allocator->alloc_tnode_list; ++ allocator->alloc_tnode_list = tnl; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (!allocator) { ++ BUG(); ++ return NULL; ++ } ++ ++ /* If there are none left make more */ ++ if (!allocator->free_tnodes) ++ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); ++ ++ if (allocator->free_tnodes) { ++ tn = allocator->free_tnodes; ++ allocator->free_tnodes = allocator->free_tnodes->internal[0]; ++ allocator->n_free_tnodes--; ++ } ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ if (tn) { ++ tn->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = tn; ++ allocator->n_free_tnodes++; ++ } ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++/*--------------- yaffs_obj alloaction ------------------------ ++ * ++ * Free yaffs_objs are stored in a list using obj->siblings. ++ * The blocks of allocated objects are stored in a linked list. ++ */ ++ ++static void yaffs_init_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->allocated_obj_list = NULL; ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++} ++ ++static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ struct yaffs_obj_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->allocated_obj_list) { ++ tmp = allocator->allocated_obj_list->next; ++ kfree(allocator->allocated_obj_list->objects); ++ kfree(allocator->allocated_obj_list); ++ allocator->allocated_obj_list = tmp; ++ } ++ ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++ allocator->n_obj_created = 0; ++} ++ ++static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ int i; ++ struct yaffs_obj *new_objs; ++ struct yaffs_obj_list *list; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_obj < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); ++ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); ++ ++ if (!new_objs || !list) { ++ kfree(new_objs); ++ new_objs = NULL; ++ kfree(list); ++ list = NULL; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Could not allocate more objects"); ++ return YAFFS_FAIL; ++ } ++ ++ /* Hook them into the free list */ ++ for (i = 0; i < n_obj; i++) ++ list_add(&new_objs[i].siblings, &allocator->free_objs); ++ ++ allocator->n_free_objects += n_obj; ++ allocator->n_obj_created += n_obj; ++ ++ /* Now add this bunch of Objects to a list for freeing up. */ ++ ++ list->objects = new_objs; ++ list->next = allocator->allocated_obj_list; ++ allocator->allocated_obj_list = list; ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct list_head *lh; ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return obj; ++ } ++ ++ /* If there are none left make more */ ++ if (list_empty(&allocator->free_objs)) ++ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); ++ ++ if (!list_empty(&allocator->free_objs)) { ++ lh = allocator->free_objs.next; ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ list_del_init(lh); ++ allocator->n_free_objects--; ++ } ++ ++ return obj; ++} ++ ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ /* Link into the free list. */ ++ list_add(&obj->siblings, &allocator->free_objs); ++ allocator->n_free_objects++; ++} ++ ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ ++ if (!dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ yaffs_deinit_raw_tnodes(dev); ++ yaffs_deinit_raw_objs(dev); ++ kfree(dev->allocator); ++ dev->allocator = NULL; ++} ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator; ++ ++ if (dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); ++ if (allocator) { ++ dev->allocator = allocator; ++ yaffs_init_raw_tnodes(dev); ++ yaffs_init_raw_objs(dev); ++ } ++} ++ +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_allocator.h linux-3.10.40/fs/yaffs2/yaffs_allocator.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_allocator.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,30 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ALLOCATOR_H__ ++#define __YAFFS_ALLOCATOR_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_attribs.c linux-3.10.40/fs/yaffs2/yaffs_attribs.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_attribs.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,166 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++static inline uid_t ia_uid_read(const struct iattr *iattr) ++{ ++ return from_kuid(&init_user_ns, iattr->ia_uid); ++} ++ ++static inline gid_t ia_gid_read(const struct iattr *iattr) ++{ ++ return from_kgid(&init_user_ns, iattr->ia_gid); ++} ++ ++static inline void ia_uid_write(struct iattr *iattr, uid_t uid) ++{ ++ iattr->ia_uid = make_kuid(&init_user_ns, uid); ++} ++ ++static inline void ia_gid_write(struct iattr *iattr, gid_t gid) ++{ ++ iattr->ia_gid = make_kgid(&init_user_ns, gid); ++} ++#else ++static inline uid_t ia_uid_read(const struct iattr *iattr) ++{ ++ return iattr->ia_uid; ++} ++ ++static inline gid_t ia_gid_read(const struct iattr *inode) ++{ ++ return iattr->ia_gid; ++} ++ ++static inline void ia_uid_write(struct iattr *iattr, uid_t uid) ++{ ++ iattr->ia_uid = uid; ++} ++ ++static inline void ia_gid_write(struct iattr *iattr, gid_t gid) ++{ ++ iattr->ia_gid = gid; ++} ++#endif ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) ++{ ++ obj->yst_uid = oh->yst_uid; ++ obj->yst_gid = oh->yst_gid; ++ obj->yst_atime = oh->yst_atime; ++ obj->yst_mtime = oh->yst_mtime; ++ obj->yst_ctime = oh->yst_ctime; ++ obj->yst_rdev = oh->yst_rdev; ++} ++ ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) ++{ ++ oh->yst_uid = obj->yst_uid; ++ oh->yst_gid = obj->yst_gid; ++ oh->yst_atime = obj->yst_atime; ++ oh->yst_mtime = obj->yst_mtime; ++ oh->yst_ctime = obj->yst_ctime; ++ oh->yst_rdev = obj->yst_rdev; ++ ++} ++ ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) ++{ ++ obj->yst_mtime = Y_CURRENT_TIME; ++ if (do_a) ++ obj->yst_atime = obj->yst_mtime; ++ if (do_c) ++ obj->yst_ctime = obj->yst_mtime; ++} ++ ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) ++{ ++ yaffs_load_current_time(obj, 1, 1); ++ obj->yst_rdev = rdev; ++ obj->yst_uid = uid; ++ obj->yst_gid = gid; ++} ++ ++static loff_t yaffs_get_file_size(struct yaffs_obj *obj) ++{ ++ YCHAR *alias = NULL; ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return obj->variant.file_variant.file_size; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = obj->variant.symlink_variant.alias; ++ if (!alias) ++ return 0; ++ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); ++ default: ++ return 0; ++ } ++} ++ ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = attr->ia_valid; ++ ++ if (valid & ATTR_MODE) ++ obj->yst_mode = attr->ia_mode; ++ if (valid & ATTR_UID) ++ obj->yst_uid = ia_uid_read(attr); ++ if (valid & ATTR_GID) ++ obj->yst_gid = ia_gid_read(attr); ++ ++ if (valid & ATTR_ATIME) ++ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); ++ if (valid & ATTR_CTIME) ++ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); ++ if (valid & ATTR_MTIME) ++ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); ++ ++ if (valid & ATTR_SIZE) ++ yaffs_resize_file(obj, attr->ia_size); ++ ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ return YAFFS_OK; ++ ++} ++ ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = 0; ++ ++ attr->ia_mode = obj->yst_mode; ++ valid |= ATTR_MODE; ++ ia_uid_write(attr, obj->yst_uid); ++ valid |= ATTR_UID; ++ ia_gid_write(attr, obj->yst_gid); ++ valid |= ATTR_GID; ++ ++ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; ++ valid |= ATTR_ATIME; ++ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; ++ valid |= ATTR_CTIME; ++ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; ++ valid |= ATTR_MTIME; ++ ++ attr->ia_size = yaffs_get_file_size(obj); ++ valid |= ATTR_SIZE; ++ ++ attr->ia_valid = valid; ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_attribs.h linux-3.10.40/fs/yaffs2/yaffs_attribs.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_attribs.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ATTRIBS_H__ ++#define __YAFFS_ATTRIBS_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_bitmap.c linux-3.10.40/fs/yaffs2/yaffs_bitmap.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_bitmap.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,97 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_bitmap.h" ++#include "yaffs_trace.h" ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "BlockBits block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return dev->chunk_bits + ++ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); ++} ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block || ++ chunk < 0 || chunk >= dev->param.chunks_per_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Chunk Id (%d:%d) invalid", ++ blk, chunk); ++ BUG(); ++ } ++} ++ ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ memset(blk_bits, 0, dev->chunk_bit_stride); ++} ++ ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); ++} ++ ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] |= (1 << (chunk & 7)); ++} ++ ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; ++} ++ ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++) { ++ if (*blk_bits) ++ return 1; ++ blk_bits++; ++ } ++ return 0; ++} ++ ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ int n = 0; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) ++ n += hweight8(*blk_bits); ++ ++ return n; ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_bitmap.h linux-3.10.40/fs/yaffs2/yaffs_bitmap.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_bitmap.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++#ifndef __YAFFS_BITMAP_H__ ++#define __YAFFS_BITMAP_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.10.40/fs/yaffs2/yaffs_checkptrw.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,474 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_checkptrw.h" ++#include "yaffs_getblockinfo.h" ++ ++struct yaffs_checkpt_chunk_hdr { ++ int version; ++ int seq; ++ u32 sum; ++ u32 xor; ++} ; ++ ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++static int apply_block_offset(struct yaffs_dev *dev, int block) ++{ ++ return block - dev->block_offset; ++} ++ ++static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ hdr.version = YAFFS_CHECKPOINT_VERSION; ++ hdr.seq = dev->checkpt_page_seq; ++ hdr.sum = dev->checkpt_sum; ++ hdr.xor = dev->checkpt_xor; ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); ++} ++ ++static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ return hdr.version == YAFFS_CHECKPOINT_VERSION && ++ hdr.seq == dev->checkpt_page_seq && ++ hdr.sum == dev->checkpt_sum && ++ hdr.xor == dev->checkpt_xor; ++} ++ ++static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) ++{ ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpt blocks_avail = %d", blocks_avail); ++ ++ return (blocks_avail <= 0) ? 0 : 1; ++} ++ ++static int yaffs_checkpt_erase(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (!dev->drv.drv_erase_fn) ++ return 0; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checking blocks %d to %d", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ int offset_i = apply_block_offset(dev, i); ++ int result; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "erasing checkpt block %d", i); ++ ++ dev->n_erasures++; ++ ++ result = dev->drv.drv_erase_fn(dev, offset_i); ++ if(result) { ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += ++ dev->param.chunks_per_block; ++ } else { ++ dev->drv.drv_mark_bad_fn(dev, offset_i); ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ } ++ } ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ return 1; ++} ++ ++static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) ++{ ++ int i; ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block: erased %d reserved %d avail %d next %d ", ++ dev->n_erased_blocks, dev->param.n_reserved_blocks, ++ blocks_avail, dev->checkpt_next_block); ++ ++ if (dev->checkpt_next_block >= 0 && ++ dev->checkpt_next_block <= dev->internal_end_block && ++ blocks_avail > 0) { ++ ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev, i); ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ dev->checkpt_next_block = i + 1; ++ dev->checkpt_cur_block = i; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block %d", i); ++ return; ++ } ++ } ++ } ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_ext_tags tags; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: start: blocks %d next %d", ++ dev->blocks_in_checkpt, dev->checkpt_next_block); ++ ++ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ int chunk = i * dev->param.chunks_per_block; ++ enum yaffs_block_state state; ++ u32 seq; ++ ++ dev->tagger.read_chunk_tags_fn(dev, ++ apply_chunk_offset(dev, chunk), ++ NULL, &tags); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", ++ i, (int) state, ++ tags.obj_id, tags.seq_number, ++ tags.ecc_result); ++ ++ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ continue; ++ ++ dev->tagger.query_block_fn(dev, ++ apply_block_offset(dev, i), ++ &state, &seq); ++ if (state == YAFFS_BLOCK_STATE_DEAD) ++ continue; ++ ++ /* Right kind of block */ ++ dev->checkpt_next_block = tags.obj_id; ++ dev->checkpt_cur_block = i; ++ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; ++ dev->blocks_in_checkpt++; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "found checkpt block %d", i); ++ return; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) ++{ ++ int i; ++ ++ dev->checkpt_open_write = writing; ++ ++ /* Got the functions we need? */ ++ if (!dev->tagger.write_chunk_tags_fn || ++ !dev->tagger.read_chunk_tags_fn || ++ !dev->drv.drv_erase_fn || ++ !dev->drv.drv_mark_bad_fn) ++ return 0; ++ ++ if (writing && !yaffs2_checkpt_space_ok(dev)) ++ return 0; ++ ++ if (!dev->checkpt_buffer) ++ dev->checkpt_buffer = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ dev->checkpt_page_seq = 0; ++ dev->checkpt_byte_count = 0; ++ dev->checkpt_sum = 0; ++ dev->checkpt_xor = 0; ++ dev->checkpt_cur_block = -1; ++ dev->checkpt_cur_chunk = -1; ++ dev->checkpt_next_block = dev->internal_start_block; ++ ++ if (writing) { ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ return yaffs_checkpt_erase(dev); ++ } ++ ++ /* Opening for a read */ ++ /* Set to a value that will kick off a read */ ++ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; ++ /* A checkpoint block list of 1 checkpoint block per 16 block is ++ * (hopefully) going to be way more than we need */ ++ dev->blocks_in_checkpt = 0; ++ dev->checkpt_max_blocks = ++ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; ++ dev->checkpt_block_list = ++ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); ++ ++ if (!dev->checkpt_block_list) ++ return 0; ++ ++ for (i = 0; i < dev->checkpt_max_blocks; i++) ++ dev->checkpt_block_list[i] = -1; ++ ++ return 1; ++} ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) ++{ ++ u32 composite_sum; ++ ++ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); ++ *sum = composite_sum; ++ return 1; ++} ++ ++static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) ++{ ++ int chunk; ++ int offset_chunk; ++ struct yaffs_ext_tags tags; ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_erased_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) ++ return 0; ++ ++ tags.is_deleted = 0; ++ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ ++ tags.chunk_id = dev->checkpt_page_seq + 1; ++ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; ++ tags.n_bytes = dev->data_bytes_per_chunk; ++ if (dev->checkpt_cur_chunk == 0) { ++ /* First chunk we write for the block? Set block state to ++ checkpoint */ ++ struct yaffs_block_info *bi = ++ yaffs_get_block_info(dev, dev->checkpt_cur_block); ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ dev->blocks_in_checkpt++; ++ } ++ ++ chunk = ++ dev->checkpt_cur_block * dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", ++ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, ++ tags.obj_id, tags.chunk_id); ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ ++ dev->n_page_writes++; ++ ++ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, ++ dev->checkpt_buffer, &tags); ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { ++ dev->checkpt_cur_chunk = 0; ++ dev->checkpt_cur_block = -1; ++ } ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ ++ ++ return 1; ++} ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (!dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) ++ ok = yaffs2_checkpt_flush_buffer(dev); ++ } ++ ++ return i; ++} ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ struct yaffs_ext_tags tags; ++ int chunk; ++ int offset_chunk; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) { ++ ok = 0; ++ break; ++ } ++ ++ chunk = dev->checkpt_cur_block * ++ dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ dev->n_page_reads++; ++ ++ /* read in the next chunk */ ++ dev->tagger.read_chunk_tags_fn(dev, ++ offset_chunk, ++ dev->checkpt_buffer, ++ &tags); ++ ++ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) { ++ ok = 0; ++ break; ++ } ++ if(!yaffs2_checkpt_check_chunk_hdr(dev)) { ++ ok = 0; ++ break; ++ } ++ ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ ++ if (dev->checkpt_cur_chunk >= ++ dev->param.chunks_per_block) ++ dev->checkpt_cur_block = -1; ++ ++ } ++ ++ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ } ++ ++ return i; ++} ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->checkpt_open_write) { ++ if (dev->checkpt_byte_offs != ++ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) ++ yaffs2_checkpt_flush_buffer(dev); ++ } else if (dev->checkpt_block_list) { ++ for (i = 0; ++ i < dev->blocks_in_checkpt && ++ dev->checkpt_block_list[i] >= 0; i++) { ++ int blk = dev->checkpt_block_list[i]; ++ struct yaffs_block_info *bi = NULL; ++ ++ if (dev->internal_start_block <= blk && ++ blk <= dev->internal_end_block) ++ bi = yaffs_get_block_info(dev, blk); ++ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ } ++ kfree(dev->checkpt_block_list); ++ dev->checkpt_block_list = NULL; ++ } ++ ++ dev->n_free_chunks -= ++ dev->blocks_in_checkpt * dev->param.chunks_per_block; ++ dev->n_erased_blocks -= dev->blocks_in_checkpt; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", ++ dev->checkpt_byte_count); ++ ++ if (dev->checkpt_buffer) { ++ /* free the buffer */ ++ kfree(dev->checkpt_buffer); ++ dev->checkpt_buffer = NULL; ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) ++{ ++ /* Erase the checkpoint data */ ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint invalidate of %d blocks", ++ dev->blocks_in_checkpt); ++ ++ return yaffs_checkpt_erase(dev); ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.10.40/fs/yaffs2/yaffs_checkptrw.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_CHECKPTRW_H__ ++#define __YAFFS_CHECKPTRW_H__ ++ ++#include "yaffs_guts.h" ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev); ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_ecc.c linux-3.10.40/fs/yaffs2/yaffs_ecc.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_ecc.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,281 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two ++ * such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_ecc.h" ++ ++/* Table generated by gen-ecc.c ++ * Using a table means we do not have to calculate p1..p4 and p1'..p4' ++ * for each byte of data. These are instead provided in a table in bits7..2. ++ * Bit 0 of each entry indicates whether the entry has an odd or even parity, ++ * and therefore this bytes influence on the line parity. ++ */ ++ ++static const unsigned char column_parity_table[] = { ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++}; ++ ++ ++/* Calculate the ECC for a 256-byte block of data */ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned char line_parity = 0; ++ unsigned char line_parity_prime = 0; ++ unsigned char t; ++ unsigned char b; ++ ++ for (i = 0; i < 256; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ } ++ ++ ecc[2] = (~col_parity) | 0x03; ++ ++ t = 0; ++ if (line_parity & 0x80) ++ t |= 0x80; ++ if (line_parity_prime & 0x80) ++ t |= 0x40; ++ if (line_parity & 0x40) ++ t |= 0x20; ++ if (line_parity_prime & 0x40) ++ t |= 0x10; ++ if (line_parity & 0x20) ++ t |= 0x08; ++ if (line_parity_prime & 0x20) ++ t |= 0x04; ++ if (line_parity & 0x10) ++ t |= 0x02; ++ if (line_parity_prime & 0x10) ++ t |= 0x01; ++ ecc[1] = ~t; ++ ++ t = 0; ++ if (line_parity & 0x08) ++ t |= 0x80; ++ if (line_parity_prime & 0x08) ++ t |= 0x40; ++ if (line_parity & 0x04) ++ t |= 0x20; ++ if (line_parity_prime & 0x04) ++ t |= 0x10; ++ if (line_parity & 0x02) ++ t |= 0x08; ++ if (line_parity_prime & 0x02) ++ t |= 0x04; ++ if (line_parity & 0x01) ++ t |= 0x02; ++ if (line_parity_prime & 0x01) ++ t |= 0x01; ++ ecc[0] = ~t; ++ ++} ++ ++/* Correct the ECC on a 256 byte block of data */ ++ ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc) ++{ ++ unsigned char d0, d1, d2; /* deltas */ ++ ++ d0 = read_ecc[0] ^ test_ecc[0]; ++ d1 = read_ecc[1] ^ test_ecc[1]; ++ d2 = read_ecc[2] ^ test_ecc[2]; ++ ++ if ((d0 | d1 | d2) == 0) ++ return 0; /* no error */ ++ ++ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && ++ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && ++ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { ++ /* Single bit (recoverable) error in data */ ++ ++ unsigned byte; ++ unsigned bit; ++ ++ bit = byte = 0; ++ ++ if (d1 & 0x80) ++ byte |= 0x80; ++ if (d1 & 0x20) ++ byte |= 0x40; ++ if (d1 & 0x08) ++ byte |= 0x20; ++ if (d1 & 0x02) ++ byte |= 0x10; ++ if (d0 & 0x80) ++ byte |= 0x08; ++ if (d0 & 0x20) ++ byte |= 0x04; ++ if (d0 & 0x08) ++ byte |= 0x02; ++ if (d0 & 0x02) ++ byte |= 0x01; ++ ++ if (d2 & 0x80) ++ bit |= 0x04; ++ if (d2 & 0x20) ++ bit |= 0x02; ++ if (d2 & 0x08) ++ bit |= 0x01; ++ ++ data[byte] ^= (1 << bit); ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ read_ecc[0] = test_ecc[0]; ++ read_ecc[1] = test_ecc[1]; ++ read_ecc[2] = test_ecc[2]; ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++ ++} ++ ++/* ++ * ECCxxxOther does ECC calcs on arbitrary n bytes of data ++ */ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc_other) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned line_parity = 0; ++ unsigned line_parity_prime = 0; ++ unsigned char b; ++ ++ for (i = 0; i < n_bytes; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { ++ /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ ++ } ++ ++ ecc_other->col_parity = (col_parity >> 2) & 0x3f; ++ ecc_other->line_parity = line_parity; ++ ecc_other->line_parity_prime = line_parity_prime; ++} ++ ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc) ++{ ++ unsigned char delta_col; /* column parity delta */ ++ unsigned delta_line; /* line parity delta */ ++ unsigned delta_line_prime; /* line parity delta */ ++ unsigned bit; ++ ++ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; ++ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; ++ delta_line_prime = ++ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; ++ ++ if ((delta_col | delta_line | delta_line_prime) == 0) ++ return 0; /* no error */ ++ ++ if (delta_line == ~delta_line_prime && ++ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { ++ /* Single bit (recoverable) error in data */ ++ ++ bit = 0; ++ ++ if (delta_col & 0x20) ++ bit |= 0x04; ++ if (delta_col & 0x08) ++ bit |= 0x02; ++ if (delta_col & 0x02) ++ bit |= 0x01; ++ ++ if (delta_line >= n_bytes) ++ return -1; ++ ++ data[delta_line] ^= (1 << bit); ++ ++ return 1; /* corrected */ ++ } ++ ++ if ((hweight32(delta_line) + ++ hweight32(delta_line_prime) + ++ hweight8(delta_col)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ *read_ecc = *test_ecc; ++ return 1; /* corrected */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_ecc.h linux-3.10.40/fs/yaffs2/yaffs_ecc.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_ecc.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. ++ * Thus, two such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#ifndef __YAFFS_ECC_H__ ++#define __YAFFS_ECC_H__ ++ ++struct yaffs_ecc_other { ++ unsigned char col_parity; ++ unsigned line_parity; ++ unsigned line_parity_prime; ++}; ++ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc); ++ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc); ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc); ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.10.40/fs/yaffs2/yaffs_getblockinfo.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,35 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GETBLOCKINFO_H__ ++#define __YAFFS_GETBLOCKINFO_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++ ++/* Function to manipulate block info */ ++static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev ++ *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs: get_block_info block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return &dev->block_info[blk - dev->internal_start_block]; ++} ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_guts.c linux-3.10.40/fs/yaffs2/yaffs_guts.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_guts.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,5146 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++#include "yaffs_guts.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_tagsmarshall.h" ++#include "yaffs_nand.h" ++#include "yaffs_yaffs1.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_verify.h" ++#include "yaffs_nand.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nameval.h" ++#include "yaffs_allocator.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ ++#define YAFFS_GC_GOOD_ENOUGH 2 ++#define YAFFS_GC_PASSIVE_THRESHOLD 4 ++ ++#include "yaffs_ecc.h" ++ ++/* Forward declarations */ ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve); ++ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size); ++ ++/* Function to calculate chunk and offset */ ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out) ++{ ++ int chunk; ++ u32 offset; ++ ++ chunk = (u32) (addr >> dev->chunk_shift); ++ ++ if (dev->chunk_div == 1) { ++ /* easy power of 2 case */ ++ offset = (u32) (addr & dev->chunk_mask); ++ } else { ++ /* Non power-of-2 case */ ++ ++ loff_t chunk_base; ++ ++ chunk /= dev->chunk_div; ++ ++ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; ++ offset = (u32) (addr - chunk_base); ++ } ++ ++ *chunk_out = chunk; ++ *offset_out = offset; ++} ++ ++/* Function to return the number of shifts for a power of 2 greater than or ++ * equal to the given number ++ * Note we don't try to cater for all possible numbers and this does not have to ++ * be hellishly efficient. ++ */ ++ ++static inline u32 calc_shifts_ceiling(u32 x) ++{ ++ int extra_bits; ++ int shifts; ++ ++ shifts = extra_bits = 0; ++ ++ while (x > 1) { ++ if (x & 1) ++ extra_bits++; ++ x >>= 1; ++ shifts++; ++ } ++ ++ if (extra_bits) ++ shifts++; ++ ++ return shifts; ++} ++ ++/* Function to return the number of shifts to get a 1 in bit 0 ++ */ ++ ++static inline u32 calc_shifts(u32 x) ++{ ++ u32 shifts; ++ ++ shifts = 0; ++ ++ if (!x) ++ return 0; ++ ++ while (!(x & 1)) { ++ x >>= 1; ++ shifts++; ++ } ++ ++ return shifts; ++} ++ ++/* ++ * Temporary buffer manipulations. ++ */ ++ ++static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) ++{ ++ int i; ++ u8 *buf = (u8 *) 1; ++ ++ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); ++ ++ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { ++ dev->temp_buffer[i].in_use = 0; ++ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ dev->temp_buffer[i].buffer = buf; ++ } ++ ++ return buf ? YAFFS_OK : YAFFS_FAIL; ++} ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) ++{ ++ int i; ++ ++ dev->temp_in_use++; ++ if (dev->temp_in_use > dev->max_temp) ++ dev->max_temp = dev->temp_in_use; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].in_use == 0) { ++ dev->temp_buffer[i].in_use = 1; ++ return dev->temp_buffer[i].buffer; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); ++ /* ++ * If we got here then we have to allocate an unmanaged one ++ * This is not good. ++ */ ++ ++ dev->unmanaged_buffer_allocs++; ++ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); ++ ++} ++ ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) ++{ ++ int i; ++ ++ dev->temp_in_use--; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].buffer == buffer) { ++ dev->temp_buffer[i].in_use = 0; ++ return; ++ } ++ } ++ ++ if (buffer) { ++ /* assume it is an unmanaged one. */ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, ++ "Releasing unmanaged temp buffer"); ++ kfree(buffer); ++ dev->unmanaged_buffer_deallocs++; ++ } ++ ++} ++ ++/* ++ * Functions for robustisizing TODO ++ * ++ */ ++ ++static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) data; ++ (void) tags; ++} ++ ++static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) tags; ++} ++ ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ if (!bi->gc_prioritise) { ++ bi->gc_prioritise = 1; ++ dev->has_pending_prioritised_gc = 1; ++ bi->chunk_error_strikes++; ++ ++ if (bi->chunk_error_strikes > 3) { ++ bi->needs_retiring = 1; /* Too many stikes, so retire */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Block struck out"); ++ ++ } ++ } ++} ++ ++static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, ++ int erased_ok) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs_handle_chunk_error(dev, bi); ++ ++ if (erased_ok) { ++ /* Was an actual write failure, ++ * so mark the block for retirement.*/ ++ bi->needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d needs retiring", flash_block); ++ } ++ ++ /* Delete the chunk */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++} ++ ++/* ++ * Verification code ++ */ ++ ++/* ++ * Simple hash function. Needs to have a reasonable spread ++ */ ++ ++static inline int yaffs_hash_fn(int n) ++{ ++ if (n < 0) ++ n = -n; ++ return n % YAFFS_NOBJECT_BUCKETS; ++} ++ ++/* ++ * Access functions to useful fake objects. ++ * Note that root might have a presence in NAND if permissions are set. ++ */ ++ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) ++{ ++ return dev->root_dir; ++} ++ ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) ++{ ++ return dev->lost_n_found; ++} ++ ++/* ++ * Erased NAND checking functions ++ */ ++ ++int yaffs_check_ff(u8 *buffer, int n_bytes) ++{ ++ /* Horrible, slow implementation */ ++ while (n_bytes--) { ++ if (*buffer != 0xff) ++ return 0; ++ buffer++; ++ } ++ return 1; ++} ++ ++static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int retval = YAFFS_OK; ++ u8 *data = yaffs_get_temp_buffer(dev); ++ struct yaffs_ext_tags tags; ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); ++ ++ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) ++ retval = YAFFS_FAIL; ++ ++ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || ++ tags.chunk_used) { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not erased", nand_chunk); ++ retval = YAFFS_FAIL; ++ } ++ ++ yaffs_release_temp_buffer(dev, data); ++ ++ return retval; ++ ++} ++ ++static int yaffs_verify_chunk_written(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = YAFFS_OK; ++ struct yaffs_ext_tags temp_tags; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); ++ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || ++ temp_tags.obj_id != tags->obj_id || ++ temp_tags.chunk_id != tags->chunk_id || ++ temp_tags.n_bytes != tags->n_bytes) ++ retval = YAFFS_FAIL; ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ return retval; ++} ++ ++ ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) ++{ ++ int reserved_chunks; ++ int reserved_blocks = dev->param.n_reserved_blocks; ++ int checkpt_blocks; ++ ++ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); ++ ++ reserved_chunks = ++ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; ++ ++ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); ++} ++ ++static int yaffs_find_alloc_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ ++ if (dev->n_erased_blocks < 1) { ++ /* Hoosterman we've got a problem. ++ * Can't get space to gc ++ */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no more erased blocks"); ++ ++ return -1; ++ } ++ ++ /* Find an empty block. */ ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ dev->alloc_block_finder++; ++ if (dev->alloc_block_finder < dev->internal_start_block ++ || dev->alloc_block_finder > dev->internal_end_block) { ++ dev->alloc_block_finder = dev->internal_start_block; ++ } ++ ++ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->seq_number++; ++ bi->seq_number = dev->seq_number; ++ dev->n_erased_blocks--; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Allocated block %d, seq %d, %d left" , ++ dev->alloc_block_finder, dev->seq_number, ++ dev->n_erased_blocks); ++ return dev->alloc_block_finder; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs tragedy: no more erased blocks, but there should have been %d", ++ dev->n_erased_blocks); ++ ++ return -1; ++} ++ ++static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, ++ struct yaffs_block_info **block_ptr) ++{ ++ int ret_val; ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block < 0) { ++ /* Get next block to allocate off */ ++ dev->alloc_block = yaffs_find_alloc_block(dev); ++ dev->alloc_page = 0; ++ } ++ ++ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { ++ /* No space unless we're allowed to use the reserve. */ ++ return -1; ++ } ++ ++ if (dev->n_erased_blocks < dev->param.n_reserved_blocks ++ && dev->alloc_page == 0) ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); ++ ++ /* Next page please.... */ ++ if (dev->alloc_block >= 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ ++ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + ++ dev->alloc_page; ++ bi->pages_in_use++; ++ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); ++ ++ dev->alloc_page++; ++ ++ dev->n_free_chunks--; ++ ++ /* If the block is full set the state to full */ ++ if (dev->alloc_page >= dev->param.chunks_per_block) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ if (block_ptr) ++ *block_ptr = bi; ++ ++ return ret_val; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); ++ ++ return -1; ++} ++ ++static int yaffs_get_erased_chunks(struct yaffs_dev *dev) ++{ ++ int n; ++ ++ n = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ if (dev->alloc_block > 0) ++ n += (dev->param.chunks_per_block - dev->alloc_page); ++ ++ return n; ++ ++} ++ ++/* ++ * yaffs_skip_rest_of_block() skips over the rest of the allocation block ++ * if we don't want to write to it. ++ */ ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev) ++{ ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block > 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ } ++} ++ ++static int yaffs_write_new_chunk(struct yaffs_dev *dev, ++ const u8 *data, ++ struct yaffs_ext_tags *tags, int use_reserver) ++{ ++ int attempts = 0; ++ int write_ok = 0; ++ int chunk; ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ do { ++ struct yaffs_block_info *bi = 0; ++ int erased_ok = 0; ++ ++ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); ++ if (chunk < 0) { ++ /* no space */ ++ break; ++ } ++ ++ /* First check this chunk is erased, if it needs ++ * checking. The checking policy (unless forced ++ * always on) is as follows: ++ * ++ * Check the first page we try to write in a block. ++ * If the check passes then we don't need to check any ++ * more. If the check fails, we check again... ++ * If the block has been erased, we don't need to check. ++ * ++ * However, if the block has been prioritised for gc, ++ * then we think there might be something odd about ++ * this block and stop using it. ++ * ++ * Rationale: We should only ever see chunks that have ++ * not been erased if there was a partially written ++ * chunk due to power loss. This checking policy should ++ * catch that case with very few checks and thus save a ++ * lot of checks that are most likely not needed. ++ * ++ * Mods to the above ++ * If an erase check fails or the write fails we skip the ++ * rest of the block. ++ */ ++ ++ /* let's give it a try */ ++ attempts++; ++ ++ if (dev->param.always_check_erased) ++ bi->skip_erased_check = 0; ++ ++ if (!bi->skip_erased_check) { ++ erased_ok = yaffs_check_chunk_erased(dev, chunk); ++ if (erased_ok != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs chunk %d was not erased", ++ chunk); ++ ++ /* If not erased, delete this one, ++ * skip rest of block and ++ * try another chunk */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++ continue; ++ } ++ } ++ ++ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); ++ ++ if (!bi->skip_erased_check) ++ write_ok = ++ yaffs_verify_chunk_written(dev, chunk, data, tags); ++ ++ if (write_ok != YAFFS_OK) { ++ /* Clean up aborted write, skip to next block and ++ * try another chunk */ ++ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); ++ continue; ++ } ++ ++ bi->skip_erased_check = 1; ++ ++ /* Copy the data into the robustification buffer */ ++ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); ++ ++ } while (write_ok != YAFFS_OK && ++ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); ++ ++ if (!write_ok) ++ chunk = -1; ++ ++ if (attempts > 1) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs write required %d attempts", ++ attempts); ++ dev->n_retried_writes += (attempts - 1); ++ } ++ ++ return chunk; ++} ++ ++/* ++ * Block retiring for handling a broken block. ++ */ ++ ++static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { ++ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to mark bad and erase block %d", ++ flash_block); ++ } else { ++ struct yaffs_ext_tags tags; ++ int chunk_id = ++ flash_block * dev->param.chunks_per_block; ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ memset(&tags, 0, sizeof(tags)); ++ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; ++ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - ++ dev->chunk_offset, ++ buffer, ++ &tags) != YAFFS_OK) ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to write bad block marker to block %d", ++ flash_block); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ } ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ bi->gc_prioritise = 0; ++ bi->needs_retiring = 0; ++ ++ dev->n_retired_blocks++; ++} ++ ++/*---------------- Name handling functions ------------*/ ++ ++static u16 yaffs_calc_name_sum(const YCHAR *name) ++{ ++ u16 sum = 0; ++ u16 i = 1; ++ ++ if (!name) ++ return 0; ++ ++ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { ++ ++ /* 0x1f mask is case insensitive */ ++ sum += ((*name) & 0x1f) * i; ++ i++; ++ name++; ++ } ++ return sum; ++} ++ ++ ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ memset(obj->short_name, 0, sizeof(obj->short_name)); ++ ++ if (name && !name[0]) { ++ yaffs_fix_null_name(obj, obj->short_name, ++ YAFFS_SHORT_NAME_LENGTH); ++ name = obj->short_name; ++ } else if (name && ++ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= ++ YAFFS_SHORT_NAME_LENGTH) { ++ strcpy(obj->short_name, name); ++ } ++ ++ obj->sum = yaffs_calc_name_sum(name); ++} ++ ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ memset(tmp_name, 0, sizeof(tmp_name)); ++ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_set_obj_name(obj, tmp_name); ++#else ++ yaffs_set_obj_name(obj, oh->name); ++#endif ++} ++ ++loff_t yaffs_max_file_size(struct yaffs_dev *dev) ++{ ++ if(sizeof(loff_t) < 8) ++ return YAFFS_MAX_FILE_SIZE_32; ++ else ++ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; ++} ++ ++/*-------------------- TNODES ------------------- ++ ++ * List of spare tnodes ++ * The list is hooked together using the first pointer ++ * in the tnode. ++ */ ++ ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); ++ ++ if (tn) { ++ memset(tn, 0, dev->tnode_size); ++ dev->n_tnodes++; ++ } ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ yaffs_free_raw_tnode(dev, tn); ++ dev->n_tnodes--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ yaffs_deinit_raw_tnodes_and_objs(dev); ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++} ++ ++static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos, unsigned val) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 mask; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ val >>= dev->chunk_grp_bits; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ mask = dev->tnode_mask << bit_in_word; ++ ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val << bit_in_word)); ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ mask = ++ dev->tnode_mask >> bit_in_word; ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val >> bit_in_word)); ++ } ++} ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 val; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ val = map[word_in_map] >> bit_in_word; ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ val |= (map[word_in_map] << bit_in_word); ++ } ++ ++ val &= dev->tnode_mask; ++ val <<= dev->chunk_grp_bits; ++ ++ return val; ++} ++ ++/* ------------------- End of individual tnode manipulation -----------------*/ ++ ++/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ ++ * The look up tree is represented by the top tnode and the number of top_level ++ * in the tree. 0 means only the level 0 tnode is in the tree. ++ */ ++ ++/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id) ++{ ++ struct yaffs_tnode *tn = file_struct->top; ++ u32 i; ++ int required_depth; ++ int level = file_struct->top_level; ++ ++ (void) dev; ++ ++ /* Check sane level and chunk Id */ ++ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (i) { ++ i >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) ++ return NULL; /* Not tall enough, so we can't find it */ ++ ++ /* Traverse down to level 0 */ ++ while (level > 0 && tn) { ++ tn = tn->internal[(chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (level - 1) * ++ YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK]; ++ level--; ++ } ++ ++ return tn; ++} ++ ++/* add_find_tnode_0 finds the level 0 tnode if it exists, ++ * otherwise first expands the tree. ++ * This happens in two steps: ++ * 1. If the tree isn't tall enough, then make it taller. ++ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. ++ * ++ * Used when modifying the tree. ++ * ++ * If the tn argument is NULL, then a fresh tnode will be added otherwise the ++ * specified tn will be plugged into the ttree. ++ */ ++ ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn) ++{ ++ int required_depth; ++ int i; ++ int l; ++ struct yaffs_tnode *tn; ++ u32 x; ++ ++ /* Check sane level and page Id */ ++ if (file_struct->top_level < 0 || ++ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) { ++ /* Not tall enough, gotta make the tree taller */ ++ for (i = file_struct->top_level; i < required_depth; i++) { ++ ++ tn = yaffs_get_tnode(dev); ++ ++ if (tn) { ++ tn->internal[0] = file_struct->top; ++ file_struct->top = tn; ++ file_struct->top_level++; ++ } else { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: no more tnodes"); ++ return NULL; ++ } ++ } ++ } ++ ++ /* Traverse down to level 0, adding anything we need */ ++ ++ l = file_struct->top_level; ++ tn = file_struct->top; ++ ++ if (l > 0) { ++ while (l > 0 && tn) { ++ x = (chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK; ++ ++ if ((l > 1) && !tn->internal[x]) { ++ /* Add missing non-level-zero tnode */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } else if (l == 1) { ++ /* Looking from level 1 at level 0 */ ++ if (passed_tn) { ++ /* If we already have one, release it */ ++ if (tn->internal[x]) ++ yaffs_free_tnode(dev, ++ tn->internal[x]); ++ tn->internal[x] = passed_tn; ++ ++ } else if (!tn->internal[x]) { ++ /* Don't have one, none passed in */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } ++ } ++ ++ tn = tn->internal[x]; ++ l--; ++ } ++ } else { ++ /* top is level 0 */ ++ if (passed_tn) { ++ memcpy(tn, passed_tn, ++ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); ++ yaffs_free_tnode(dev, passed_tn); ++ } ++ } ++ ++ return tn; ++} ++ ++static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, ++ int chunk_obj) ++{ ++ return (tags->chunk_id == chunk_obj && ++ tags->obj_id == obj_id && ++ !tags->is_deleted) ? 1 : 0; ++ ++} ++ ++static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, ++ struct yaffs_ext_tags *tags, int obj_id, ++ int inode_chunk) ++{ ++ int j; ++ ++ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { ++ if (yaffs_check_chunk_bit ++ (dev, the_chunk / dev->param.chunks_per_block, ++ the_chunk % dev->param.chunks_per_block)) { ++ ++ if (dev->chunk_grp_size == 1) ++ return the_chunk; ++ else { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ tags); ++ if (yaffs_tags_match(tags, ++ obj_id, inode_chunk)) { ++ /* found it; */ ++ return the_chunk; ++ } ++ } ++ } ++ the_chunk++; ++ } ++ return -1; ++} ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /*Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ int ret_val = -1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ return ret_val; ++} ++ ++static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /* Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ int ret_val = -1; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ ++ /* Delete the entry in the filestructure (if found) */ ++ if (ret_val != -1) ++ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); ++ ++ return ret_val; ++} ++ ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan) ++{ ++ /* NB in_scan is zero unless scanning. ++ * For forward scanning, in_scan is > 0; ++ * for backward scanning in_scan is < 0 ++ * ++ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. ++ */ ++ ++ struct yaffs_tnode *tn; ++ struct yaffs_dev *dev = in->my_dev; ++ int existing_cunk; ++ struct yaffs_ext_tags existing_tags; ++ struct yaffs_ext_tags new_tags; ++ unsigned existing_serial, new_serial; ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { ++ /* Just ignore an attempt at putting a chunk into a non-file ++ * during scanning. ++ * If it is not during Scanning then something went wrong! ++ */ ++ if (!in_scan) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy:attempt to put data chunk into a non-file" ++ ); ++ BUG(); ++ } ++ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ ++ tn = yaffs_add_find_tnode_0(dev, ++ &in->variant.file_variant, ++ inode_chunk, NULL); ++ if (!tn) ++ return YAFFS_FAIL; ++ ++ if (!nand_chunk) ++ /* Dummy insert, bail now */ ++ return YAFFS_OK; ++ ++ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ if (in_scan != 0) { ++ /* If we're scanning then we need to test for duplicates ++ * NB This does not need to be efficient since it should only ++ * happen when the power fails during a write, then only one ++ * chunk should ever be affected. ++ * ++ * Correction for YAFFS2: This could happen quite a lot and we ++ * need to think about efficiency! TODO ++ * Update: For backward scanning we don't need to re-read tags ++ * so this is quite cheap. ++ */ ++ ++ if (existing_cunk > 0) { ++ /* NB Right now existing chunk will not be real ++ * chunk_id if the chunk group size > 1 ++ * thus we have to do a FindChunkInFile to get the ++ * real chunk id. ++ * ++ * We have a duplicate now we need to decide which ++ * one to use: ++ * ++ * Backwards scanning YAFFS2: The old one is what ++ * we use, dump the new one. ++ * YAFFS1: Get both sets of tags and compare serial ++ * numbers. ++ */ ++ ++ if (in_scan > 0) { ++ /* Only do this for forward scanning */ ++ yaffs_rd_chunk_tags_nand(dev, ++ nand_chunk, ++ NULL, &new_tags); ++ ++ /* Do a proper find */ ++ existing_cunk = ++ yaffs_find_chunk_in_file(in, inode_chunk, ++ &existing_tags); ++ } ++ ++ if (existing_cunk <= 0) { ++ /*Hoosterman - how did this happen? */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: existing chunk < 0 in scan" ++ ); ++ ++ } ++ ++ /* NB The deleted flags should be false, otherwise ++ * the chunks will not be loaded during a scan ++ */ ++ ++ if (in_scan > 0) { ++ new_serial = new_tags.serial_number; ++ existing_serial = existing_tags.serial_number; ++ } ++ ++ if ((in_scan > 0) && ++ (existing_cunk <= 0 || ++ ((existing_serial + 1) & 3) == new_serial)) { ++ /* Forward scanning. ++ * Use new ++ * Delete the old one and drop through to ++ * update the tnode ++ */ ++ yaffs_chunk_del(dev, existing_cunk, 1, ++ __LINE__); ++ } else { ++ /* Backward scanning or we want to use the ++ * existing one ++ * Delete the new one and return early so that ++ * the tnode isn't changed ++ */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ } ++ ++ } ++ ++ if (existing_cunk == 0) ++ in->n_data_chunks++; ++ ++ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); ++ ++ return YAFFS_OK; ++} ++ ++static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) ++{ ++ struct yaffs_block_info *the_block; ++ unsigned block_no; ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); ++ ++ block_no = chunk / dev->param.chunks_per_block; ++ the_block = yaffs_get_block_info(dev, block_no); ++ if (the_block) { ++ the_block->soft_del_pages++; ++ dev->n_free_chunks++; ++ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); ++ } ++} ++ ++/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all ++ * the chunks in the file. ++ * All soft deleting does is increment the block's softdelete count and pulls ++ * the chunk out of the tnode. ++ * Thus, essentially this is the same as DeleteWorker except that the chunks ++ * are soft deleted. ++ */ ++ ++static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, ++ u32 level, int chunk_offset) ++{ ++ int i; ++ int the_chunk; ++ int all_done = 1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = YAFFS_NTNODES_INTERNAL - 1; ++ all_done && i >= 0; ++ i--) { ++ if (tn->internal[i]) { ++ all_done = ++ yaffs_soft_del_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) ++ + i); ++ if (all_done) { ++ yaffs_free_tnode(dev, ++ tn->internal[i]); ++ tn->internal[i] = NULL; ++ } else { ++ /* Can this happen? */ ++ } ++ } ++ } ++ return (all_done) ? 1 : 0; ++ } ++ ++ /* level 0 */ ++ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk) { ++ yaffs_soft_del_chunk(dev, the_chunk); ++ yaffs_load_tnode_0(dev, tn, i, 0); ++ } ++ } ++ return 1; ++} ++ ++static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_obj *parent; ++ ++ yaffs_verify_obj_in_dir(obj); ++ parent = obj->parent; ++ ++ yaffs_verify_dir(parent); ++ ++ if (dev && dev->param.remove_obj_fn) ++ dev->param.remove_obj_fn(obj); ++ ++ list_del_init(&obj->siblings); ++ obj->parent = NULL; ++ ++ yaffs_verify_dir(parent); ++} ++ ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) ++{ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a null pointer directory" ++ ); ++ BUG(); ++ return; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a non-directory" ++ ); ++ BUG(); ++ } ++ ++ if (obj->siblings.prev == NULL) { ++ /* Not initialised */ ++ BUG(); ++ } ++ ++ yaffs_verify_dir(directory); ++ ++ yaffs_remove_obj_from_dir(obj); ++ ++ /* Now add it */ ++ list_add(&obj->siblings, &directory->variant.dir_variant.children); ++ obj->parent = directory; ++ ++ if (directory == obj->my_dev->unlinked_dir ++ || directory == obj->my_dev->del_dir) { ++ obj->unlinked = 1; ++ obj->my_dev->n_unlinked_files++; ++ obj->rename_allowed = 0; ++ } ++ ++ yaffs_verify_dir(directory); ++ yaffs_verify_obj_in_dir(obj); ++} ++ ++static int yaffs_change_obj_name(struct yaffs_obj *obj, ++ struct yaffs_obj *new_dir, ++ const YCHAR *new_name, int force, int shadows) ++{ ++ int unlink_op; ++ int del_op; ++ struct yaffs_obj *existing_target; ++ ++ if (new_dir == NULL) ++ new_dir = obj->parent; /* use the old directory */ ++ ++ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_change_obj_name: new_dir is not a directory" ++ ); ++ BUG(); ++ } ++ ++ unlink_op = (new_dir == obj->my_dev->unlinked_dir); ++ del_op = (new_dir == obj->my_dev->del_dir); ++ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ ++ /* If the object is a file going into the unlinked directory, ++ * then it is OK to just stuff it in since duplicate names are OK. ++ * else only proceed if the new name does not exist and we're putting ++ * it into a directory. ++ */ ++ if (!(unlink_op || del_op || force || ++ shadows > 0 || !existing_target) || ++ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ return YAFFS_FAIL; ++ ++ yaffs_set_obj_name(obj, new_name); ++ obj->dirty = 1; ++ yaffs_add_obj_to_dir(new_dir, obj); ++ ++ if (unlink_op) ++ obj->unlinked = 1; ++ ++ /* If it is a deletion then we mark it as a shrink for gc */ ++ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++/*------------------------ Short Operations Cache ------------------------------ ++ * In many situations where there is no high level buffering a lot of ++ * reads might be short sequential reads, and a lot of writes may be short ++ * sequential writes. eg. scanning/writing a jpeg file. ++ * In these cases, a short read/write cache can provide a huge perfomance ++ * benefit with dumb-as-a-rock code. ++ * In Linux, the page cache provides read buffering and the short op cache ++ * provides write buffering. ++ * ++ * There are a small number (~10) of cache chunks per device so that we don't ++ * need a very intelligent search. ++ */ ++ ++static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ struct yaffs_cache *cache; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ for (i = 0; i < n_caches; i++) { ++ cache = &dev->cache[i]; ++ if (cache->object == obj && cache->dirty) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void yaffs_flush_file_cache(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int lowest = -99; /* Stop compiler whining. */ ++ int i; ++ struct yaffs_cache *cache; ++ int chunk_written = 0; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ if (n_caches < 1) ++ return; ++ do { ++ cache = NULL; ++ ++ /* Find the lowest dirty chunk for this object */ ++ for (i = 0; i < n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].dirty) { ++ if (!cache || ++ dev->cache[i].chunk_id < lowest) { ++ cache = &dev->cache[i]; ++ lowest = cache->chunk_id; ++ } ++ } ++ } ++ ++ if (cache && !cache->locked) { ++ /* Write it out and free it up */ ++ chunk_written = ++ yaffs_wr_data_obj(cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ cache->object = NULL; ++ } ++ } while (cache && chunk_written > 0); ++ ++ if (cache) ++ /* Hoosterman, disk full while writing cache out. */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no space during cache write"); ++} ++ ++/*yaffs_flush_whole_cache(dev) ++ * ++ * ++ */ ++ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int n_caches = dev->param.n_caches; ++ int i; ++ ++ /* Find a dirty object in the cache and flush it... ++ * until there are no further dirty objects. ++ */ ++ do { ++ obj = NULL; ++ for (i = 0; i < n_caches && !obj; i++) { ++ if (dev->cache[i].object && dev->cache[i].dirty) ++ obj = dev->cache[i].object; ++ } ++ if (obj) ++ yaffs_flush_file_cache(obj); ++ } while (obj); ++ ++} ++ ++/* Grab us a cache chunk for use. ++ * First look for an empty one. ++ * Then look for the least recently used non-dirty one. ++ * Then look for the least recently used dirty one...., flush and look again. ++ */ ++static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->param.n_caches > 0) { ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (!dev->cache[i].object) ++ return &dev->cache[i]; ++ } ++ } ++ return NULL; ++} ++ ++static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_cache *cache; ++ struct yaffs_obj *the_obj; ++ int usage; ++ int i; ++ int pushout; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ /* Try find a non-dirty one... */ ++ ++ cache = yaffs_grab_chunk_worker(dev); ++ ++ if (!cache) { ++ /* They were all dirty, find the LRU object and flush ++ * its cache, then find again. ++ * NB what's here is not very accurate, ++ * we actually flush the object with the LRU chunk. ++ */ ++ ++ /* With locking we can't assume we can use entry zero, ++ * Set the_obj to a valid pointer for Coverity. */ ++ the_obj = dev->cache[0].object; ++ usage = -1; ++ cache = NULL; ++ pushout = -1; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object && ++ !dev->cache[i].locked && ++ (dev->cache[i].last_use < usage || ++ !cache)) { ++ usage = dev->cache[i].last_use; ++ the_obj = dev->cache[i].object; ++ cache = &dev->cache[i]; ++ pushout = i; ++ } ++ } ++ ++ if (!cache || cache->dirty) { ++ /* Flush and try again */ ++ yaffs_flush_file_cache(the_obj); ++ cache = yaffs_grab_chunk_worker(dev); ++ } ++ } ++ return cache; ++} ++ ++/* Find a cached chunk */ ++static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, ++ int chunk_id) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].chunk_id == chunk_id) { ++ dev->cache_hits++; ++ ++ return &dev->cache[i]; ++ } ++ } ++ return NULL; ++} ++ ++/* Mark the chunk for the least recently used algorithym */ ++static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, ++ int is_write) ++{ ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return; ++ ++ if (dev->cache_last_use < 0 || ++ dev->cache_last_use > 100000000) { ++ /* Reset the cache usages */ ++ for (i = 1; i < dev->param.n_caches; i++) ++ dev->cache[i].last_use = 0; ++ ++ dev->cache_last_use = 0; ++ } ++ dev->cache_last_use++; ++ cache->last_use = dev->cache_last_use; ++ ++ if (is_write) ++ cache->dirty = 1; ++} ++ ++/* Invalidate a single cache page. ++ * Do this when a whole page gets written, ++ * ie the short cache for this page is no longer valid. ++ */ ++static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) ++{ ++ struct yaffs_cache *cache; ++ ++ if (object->my_dev->param.n_caches > 0) { ++ cache = yaffs_find_chunk_cache(object, chunk_id); ++ ++ if (cache) ++ cache->object = NULL; ++ } ++} ++ ++/* Invalidate all the cache pages associated with this object ++ * Do this whenever ther file is deleted or resized. ++ */ ++static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.n_caches > 0) { ++ /* Invalidate it. */ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == in) ++ dev->cache[i].object = NULL; ++ } ++ } ++} ++ ++static void yaffs_unhash_obj(struct yaffs_obj *obj) ++{ ++ int bucket; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ /* If it is still linked into the bucket list, free from the list */ ++ if (!list_empty(&obj->hash_link)) { ++ list_del_init(&obj->hash_link); ++ bucket = yaffs_hash_fn(obj->obj_id); ++ dev->obj_bucket[bucket].count--; ++ } ++} ++ ++/* FreeObject frees up a Object and puts it back on the free list */ ++static void yaffs_free_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) { ++ BUG(); ++ return; ++ } ++ dev = obj->my_dev; ++ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", ++ obj, obj->my_inode); ++ if (obj->parent) ++ BUG(); ++ if (!list_empty(&obj->siblings)) ++ BUG(); ++ ++ if (obj->my_inode) { ++ /* We're still hooked up to a cached inode. ++ * Don't delete now, but mark for later deletion ++ */ ++ obj->defered_free = 1; ++ return; ++ } ++ ++ yaffs_unhash_obj(obj); ++ ++ yaffs_free_raw_obj(dev, obj); ++ dev->n_obj--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj) ++{ ++ if (obj->defered_free) ++ yaffs_free_obj(obj); ++} ++ ++static int yaffs_generic_obj_del(struct yaffs_obj *in) ++{ ++ /* Iinvalidate the file's data in the cache, without flushing. */ ++ yaffs_invalidate_whole_cache(in); ++ ++ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { ++ /* Move to unlinked directory so we have a deletion record */ ++ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, ++ 0); ++ } ++ ++ yaffs_remove_obj_from_dir(in); ++ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); ++ in->hdr_chunk = 0; ++ ++ yaffs_free_obj(in); ++ return YAFFS_OK; ++ ++} ++ ++static void yaffs_soft_del_file(struct yaffs_obj *obj) ++{ ++ if (!obj->deleted || ++ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || ++ obj->soft_del) ++ return; ++ ++ if (obj->n_data_chunks <= 0) { ++ /* Empty file with no duplicate object headers, ++ * just delete it immediately */ ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: Deleting empty file %d", ++ obj->obj_id); ++ yaffs_generic_obj_del(obj); ++ } else { ++ yaffs_soft_del_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant. ++ file_variant.top_level, 0); ++ obj->soft_del = 1; ++ } ++} ++ ++/* Pruning removes any part of the file structure tree that is beyond the ++ * bounds of the file (ie that does not point to chunks). ++ * ++ * A file should only get pruned when its size is reduced. ++ * ++ * Before pruning, the chunks must be pulled from the tree and the ++ * level 0 tnode entries must be zeroed out. ++ * Could also use this for file deletion, but that's probably better handled ++ * by a special case. ++ * ++ * This function is recursive. For levels > 0 the function is called again on ++ * any sub-tree. For level == 0 we just check if the sub-tree has data. ++ * If there is no data in a subtree then it is pruned. ++ */ ++ ++static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, ++ struct yaffs_tnode *tn, u32 level, ++ int del0) ++{ ++ int i; ++ int has_data; ++ ++ if (!tn) ++ return tn; ++ ++ has_data = 0; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) { ++ tn->internal[i] = ++ yaffs_prune_worker(dev, ++ tn->internal[i], ++ level - 1, ++ (i == 0) ? del0 : 1); ++ } ++ ++ if (tn->internal[i]) ++ has_data++; ++ } ++ } else { ++ int tnode_size_u32 = dev->tnode_size / sizeof(u32); ++ u32 *map = (u32 *) tn; ++ ++ for (i = 0; !has_data && i < tnode_size_u32; i++) { ++ if (map[i]) ++ has_data++; ++ } ++ } ++ ++ if (has_data == 0 && del0) { ++ /* Free and return NULL */ ++ yaffs_free_tnode(dev, tn); ++ tn = NULL; ++ } ++ return tn; ++} ++ ++static int yaffs_prune_tree(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct) ++{ ++ int i; ++ int has_data; ++ int done = 0; ++ struct yaffs_tnode *tn; ++ ++ if (file_struct->top_level < 1) ++ return YAFFS_OK; ++ ++ file_struct->top = ++ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); ++ ++ /* Now we have a tree with all the non-zero branches NULL but ++ * the height is the same as it was. ++ * Let's see if we can trim internal tnodes to shorten the tree. ++ * We can do this if only the 0th element in the tnode is in use ++ * (ie all the non-zero are NULL) ++ */ ++ ++ while (file_struct->top_level && !done) { ++ tn = file_struct->top; ++ ++ has_data = 0; ++ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) ++ has_data++; ++ } ++ ++ if (!has_data) { ++ file_struct->top = tn->internal[0]; ++ file_struct->top_level--; ++ yaffs_free_tnode(dev, tn); ++ } else { ++ done = 1; ++ } ++ } ++ ++ return YAFFS_OK; ++} ++ ++/*-------------------- End of File Structure functions.-------------------*/ ++ ++/* alloc_empty_obj gets us a clean Object.*/ ++static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); ++ ++ if (!obj) ++ return obj; ++ ++ dev->n_obj++; ++ ++ /* Now sweeten it up... */ ++ ++ memset(obj, 0, sizeof(struct yaffs_obj)); ++ obj->being_created = 1; ++ ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; ++ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; ++ INIT_LIST_HEAD(&(obj->hard_links)); ++ INIT_LIST_HEAD(&(obj->hash_link)); ++ INIT_LIST_HEAD(&obj->siblings); ++ ++ /* Now make the directory sane */ ++ if (dev->root_dir) { ++ obj->parent = dev->root_dir; ++ list_add(&(obj->siblings), ++ &dev->root_dir->variant.dir_variant.children); ++ } ++ ++ /* Add it to the lost and found directory. ++ * NB Can't put root or lost-n-found in lost-n-found so ++ * check if lost-n-found exists first ++ */ ++ if (dev->lost_n_found) ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ ++ obj->being_created = 0; ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return obj; ++} ++ ++static int yaffs_find_nice_bucket(struct yaffs_dev *dev) ++{ ++ int i; ++ int l = 999; ++ int lowest = 999999; ++ ++ /* Search for the shortest list or one that ++ * isn't too long. ++ */ ++ ++ for (i = 0; i < 10 && lowest > 4; i++) { ++ dev->bucket_finder++; ++ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; ++ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { ++ lowest = dev->obj_bucket[dev->bucket_finder].count; ++ l = dev->bucket_finder; ++ } ++ } ++ ++ return l; ++} ++ ++static int yaffs_new_obj_id(struct yaffs_dev *dev) ++{ ++ int bucket = yaffs_find_nice_bucket(dev); ++ int found = 0; ++ struct list_head *i; ++ u32 n = (u32) bucket; ++ ++ /* Now find an object value that has not already been taken ++ * by scanning the list. ++ */ ++ ++ while (!found) { ++ found = 1; ++ n += YAFFS_NOBJECT_BUCKETS; ++ if (1 || dev->obj_bucket[bucket].count > 0) { ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* If there is already one in the list */ ++ if (i && list_entry(i, struct yaffs_obj, ++ hash_link)->obj_id == n) { ++ found = 0; ++ } ++ } ++ } ++ } ++ return n; ++} ++ ++static void yaffs_hash_obj(struct yaffs_obj *in) ++{ ++ int bucket = yaffs_hash_fn(in->obj_id); ++ struct yaffs_dev *dev = in->my_dev; ++ ++ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); ++ dev->obj_bucket[bucket].count++; ++} ++ ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) ++{ ++ int bucket = yaffs_hash_fn(number); ++ struct list_head *i; ++ struct yaffs_obj *in; ++ ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* Look if it is in the list */ ++ in = list_entry(i, struct yaffs_obj, hash_link); ++ if (in->obj_id == number) { ++ /* Don't show if it is defered free */ ++ if (in->defered_free) ++ return NULL; ++ return in; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (number < 0) ++ number = yaffs_new_obj_id(dev); ++ ++ if (type == YAFFS_OBJECT_TYPE_FILE) { ++ tn = yaffs_get_tnode(dev); ++ if (!tn) ++ return NULL; ++ } ++ ++ the_obj = yaffs_alloc_empty_obj(dev); ++ if (!the_obj) { ++ if (tn) ++ yaffs_free_tnode(dev, tn); ++ return NULL; ++ } ++ ++ the_obj->fake = 0; ++ the_obj->rename_allowed = 1; ++ the_obj->unlink_allowed = 1; ++ the_obj->obj_id = number; ++ yaffs_hash_obj(the_obj); ++ the_obj->variant_type = type; ++ yaffs_load_current_time(the_obj, 1, 1); ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ the_obj->variant.file_variant.file_size = 0; ++ the_obj->variant.file_variant.scanned_size = 0; ++ the_obj->variant.file_variant.shrink_size = ++ yaffs_max_file_size(dev); ++ the_obj->variant.file_variant.top_level = 0; ++ the_obj->variant.file_variant.top = tn; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* No action required */ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* todo this should not happen */ ++ break; ++ } ++ return the_obj; ++} ++ ++static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, ++ int number, u32 mode) ++{ ++ ++ struct yaffs_obj *obj = ++ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); ++ ++ if (!obj) ++ return NULL; ++ ++ obj->fake = 1; /* it is fake so it might not use NAND */ ++ obj->rename_allowed = 0; ++ obj->unlink_allowed = 0; ++ obj->deleted = 0; ++ obj->unlinked = 0; ++ obj->yst_mode = mode; ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; /* Not a valid chunk. */ ++ return obj; ++ ++} ++ ++ ++static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++ yaffs_init_raw_tnodes_and_objs(dev); ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ INIT_LIST_HEAD(&dev->obj_bucket[i].list); ++ dev->obj_bucket[i].count = 0; ++ } ++} ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ ++ if (number > 0) ++ the_obj = yaffs_find_by_number(dev, number); ++ ++ if (!the_obj) ++ the_obj = yaffs_new_obj(dev, number, type); ++ ++ return the_obj; ++ ++} ++ ++YCHAR *yaffs_clone_str(const YCHAR *str) ++{ ++ YCHAR *new_str = NULL; ++ int len; ++ ++ if (!str) ++ str = _Y(""); ++ ++ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); ++ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); ++ if (new_str) { ++ strncpy(new_str, str, len); ++ new_str[len] = 0; ++ } ++ return new_str; ++ ++} ++/* ++ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new ++ * link (ie. name) is created or deleted in the directory. ++ * ++ * ie. ++ * create dir/a : update dir's mtime/ctime ++ * rm dir/a: update dir's mtime/ctime ++ * modify dir/a: don't update dir's mtimme/ctime ++ * ++ * This can be handled immediately or defered. Defering helps reduce the number ++ * of updates when many files in a directory are changed within a brief period. ++ * ++ * If the directory updating is defered then yaffs_update_dirty_dirs must be ++ * called periodically. ++ */ ++ ++static void yaffs_update_parent(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) ++ return; ++ dev = obj->my_dev; ++ obj->dirty = 1; ++ yaffs_load_current_time(obj, 0, 1); ++ if (dev->param.defered_dir_update) { ++ struct list_head *link = &obj->variant.dir_variant.dirty; ++ ++ if (list_empty(link)) { ++ list_add(link, &dev->dirty_dirs); ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Added object %d to dirty directories", ++ obj->obj_id); ++ } ++ ++ } else { ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev) ++{ ++ struct list_head *link; ++ struct yaffs_obj *obj; ++ struct yaffs_dir_var *d_s; ++ union yaffs_obj_var *o_v; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); ++ ++ while (!list_empty(&dev->dirty_dirs)) { ++ link = dev->dirty_dirs.next; ++ list_del_init(link); ++ ++ d_s = list_entry(link, struct yaffs_dir_var, dirty); ++ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); ++ obj = list_entry(o_v, struct yaffs_obj, variant); ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", ++ obj->obj_id); ++ ++ if (obj->dirty) ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++/* ++ * Mknod (create) a new object. ++ * equiv_obj only has meaning for a hard link; ++ * alias_str only has meaning for a symlink. ++ * rdev only has meaning for devices (a subset of special objects) ++ */ ++ ++static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, ++ struct yaffs_obj *parent, ++ const YCHAR *name, ++ u32 mode, ++ u32 uid, ++ u32 gid, ++ struct yaffs_obj *equiv_obj, ++ const YCHAR *alias_str, u32 rdev) ++{ ++ struct yaffs_obj *in; ++ YCHAR *str = NULL; ++ struct yaffs_dev *dev = parent->my_dev; ++ ++ /* Check if the entry exists. ++ * If it does then fail the call since we don't want a dup. */ ++ if (yaffs_find_by_name(parent, name)) ++ return NULL; ++ ++ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ str = yaffs_clone_str(alias_str); ++ if (!str) ++ return NULL; ++ } ++ ++ in = yaffs_new_obj(dev, -1, type); ++ ++ if (!in) { ++ kfree(str); ++ return NULL; ++ } ++ ++ in->hdr_chunk = 0; ++ in->valid = 1; ++ in->variant_type = type; ++ ++ in->yst_mode = mode; ++ ++ yaffs_attribs_init(in, gid, uid, rdev); ++ ++ in->n_data_chunks = 0; ++ ++ yaffs_set_obj_name(in, name); ++ in->dirty = 1; ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ in->my_dev = parent->my_dev; ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant.alias = str; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant.hardlink_variant.equiv_obj = equiv_obj; ++ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; ++ list_add(&in->hard_links, &equiv_obj->hard_links); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* do nothing */ ++ break; ++ } ++ ++ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { ++ /* Could not create the object header, fail */ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ if (in) ++ yaffs_update_parent(parent); ++ ++ return in; ++} ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, ++ uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, ++ mode, uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, ++ uid, gid, NULL, NULL, rdev); ++} ++ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, ++ uid, gid, NULL, alias, 0); ++} ++ ++/* yaffs_link_obj returns the object id of the equivalent object.*/ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, ++ struct yaffs_obj *equiv_obj) ++{ ++ /* Get the real object in case we were fed a hard link obj */ ++ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); ++ ++ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, ++ parent, name, 0, 0, 0, ++ equiv_obj, NULL, 0)) ++ return equiv_obj; ++ ++ return NULL; ++ ++} ++ ++ ++ ++/*---------------------- Block Management and Page Allocation -------------*/ ++ ++static void yaffs_deinit_blocks(struct yaffs_dev *dev) ++{ ++ if (dev->block_info_alt && dev->block_info) ++ vfree(dev->block_info); ++ else ++ kfree(dev->block_info); ++ ++ dev->block_info_alt = 0; ++ ++ dev->block_info = NULL; ++ ++ if (dev->chunk_bits_alt && dev->chunk_bits) ++ vfree(dev->chunk_bits); ++ else ++ kfree(dev->chunk_bits); ++ dev->chunk_bits_alt = 0; ++ dev->chunk_bits = NULL; ++} ++ ++static int yaffs_init_blocks(struct yaffs_dev *dev) ++{ ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ dev->block_info = NULL; ++ dev->chunk_bits = NULL; ++ dev->alloc_block = -1; /* force it to get a new one */ ++ ++ /* If the first allocation strategy fails, thry the alternate one */ ++ dev->block_info = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); ++ if (!dev->block_info) { ++ dev->block_info = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); ++ dev->block_info_alt = 1; ++ } else { ++ dev->block_info_alt = 0; ++ } ++ ++ if (!dev->block_info) ++ goto alloc_error; ++ ++ /* Set up dynamic blockinfo stuff. Round up bytes. */ ++ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; ++ dev->chunk_bits = ++ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); ++ if (!dev->chunk_bits) { ++ dev->chunk_bits = ++ vmalloc(dev->chunk_bit_stride * n_blocks); ++ dev->chunk_bits_alt = 1; ++ } else { ++ dev->chunk_bits_alt = 0; ++ } ++ if (!dev->chunk_bits) ++ goto alloc_error; ++ ++ ++ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); ++ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); ++ return YAFFS_OK; ++ ++alloc_error: ++ yaffs_deinit_blocks(dev); ++ return YAFFS_FAIL; ++} ++ ++ ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); ++ int erased_ok = 0; ++ int i; ++ ++ /* If the block is still healthy erase it and mark as clean. ++ * If the block has had a data failure, then retire it. ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, ++ "yaffs_block_became_dirty block %d state %d %s", ++ block_no, bi->block_state, ++ (bi->needs_retiring) ? "needs retiring" : ""); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; ++ ++ /* If this is the block being garbage collected then stop gc'ing */ ++ if (block_no == dev->gc_block) ++ dev->gc_block = 0; ++ ++ /* If this block is currently the best candidate for gc ++ * then drop as a candidate */ ++ if (block_no == dev->gc_dirtiest) { ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ } ++ ++ if (!bi->needs_retiring) { ++ yaffs2_checkpt_invalidate(dev); ++ erased_ok = yaffs_erase_block(dev, block_no); ++ if (!erased_ok) { ++ dev->n_erase_failures++; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Erasure failed %d", block_no); ++ } ++ } ++ ++ /* Verify erasure if needed */ ++ if (erased_ok && ++ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || ++ !yaffs_skip_verification(dev))) { ++ for (i = 0; i < dev->param.chunks_per_block; i++) { ++ if (!yaffs_check_chunk_erased(dev, ++ block_no * dev->param.chunks_per_block + i)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ ">>Block %d erasure supposedly OK, but chunk %d not erased", ++ block_no, i); ++ } ++ } ++ } ++ ++ if (!erased_ok) { ++ /* We lost a block of free space */ ++ dev->n_free_chunks -= dev->param.chunks_per_block; ++ yaffs_retire_block(dev, block_no); ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d retired", block_no); ++ return; ++ } ++ ++ /* Clean it up... */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ bi->seq_number = 0; ++ dev->n_erased_blocks++; ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ bi->has_shrink_hdr = 0; ++ bi->skip_erased_check = 1; /* Clean, so no need to check */ ++ bi->gc_prioritise = 0; ++ bi->has_summary = 0; ++ ++ yaffs_clear_chunk_bits(dev, block_no); ++ ++ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); ++} ++ ++static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int old_chunk, u8 *buffer) ++{ ++ int new_chunk; ++ int mark_flash = 1; ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj *object; ++ int matching_chunk; ++ int ret_val = YAFFS_OK; ++ ++ memset(&tags, 0, sizeof(tags)); ++ yaffs_rd_chunk_tags_nand(dev, old_chunk, ++ buffer, &tags); ++ object = yaffs_find_by_number(dev, tags.obj_id); ++ ++ yaffs_trace(YAFFS_TRACE_GC_DETAIL, ++ "Collecting chunk in block %d, %d %d %d ", ++ dev->gc_chunk, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ ++ if (object && !yaffs_skip_verification(dev)) { ++ if (tags.chunk_id == 0) ++ matching_chunk = ++ object->hdr_chunk; ++ else if (object->soft_del) ++ /* Defeat the test */ ++ matching_chunk = old_chunk; ++ else ++ matching_chunk = ++ yaffs_find_chunk_in_file ++ (object, tags.chunk_id, ++ NULL); ++ ++ if (old_chunk != matching_chunk) ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "gc: page in gc mismatch: %d %d %d %d", ++ old_chunk, ++ matching_chunk, ++ tags.obj_id, ++ tags.chunk_id); ++ } ++ ++ if (!object) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "page %d in gc has no object: %d %d %d ", ++ old_chunk, ++ tags.obj_id, tags.chunk_id, ++ tags.n_bytes); ++ } ++ ++ if (object && ++ object->deleted && ++ object->soft_del && tags.chunk_id != 0) { ++ /* Data chunk in a soft deleted file, ++ * throw it away. ++ * It's a soft deleted data chunk, ++ * No need to copy this, just forget ++ * about it and fix up the object. ++ */ ++ ++ /* Free chunks already includes ++ * softdeleted chunks, how ever this ++ * chunk is going to soon be really ++ * deleted which will increment free ++ * chunks. We have to decrement free ++ * chunks so this works out properly. ++ */ ++ dev->n_free_chunks--; ++ bi->soft_del_pages--; ++ ++ object->n_data_chunks--; ++ if (object->n_data_chunks <= 0) { ++ /* remeber to clean up obj */ ++ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; ++ dev->n_clean_ups++; ++ } ++ mark_flash = 0; ++ } else if (object) { ++ /* It's either a data chunk in a live ++ * file or an ObjectHeader, so we're ++ * interested in it. ++ * NB Need to keep the ObjectHeaders of ++ * deleted files until the whole file ++ * has been deleted off ++ */ ++ tags.serial_number++; ++ dev->n_gc_copies++; ++ ++ if (tags.chunk_id == 0) { ++ /* It is an object Id, ++ * We need to nuke the ++ * shrinkheader flags since its ++ * work is done. ++ * Also need to clean up ++ * shadowing. ++ */ ++ struct yaffs_obj_hdr *oh; ++ oh = (struct yaffs_obj_hdr *) buffer; ++ ++ oh->is_shrink = 0; ++ tags.extra_is_shrink = 0; ++ oh->shadows_obj = 0; ++ oh->inband_shadowed_obj_id = 0; ++ tags.extra_shadows = 0; ++ ++ /* Update file size */ ++ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { ++ yaffs_oh_size_load(oh, ++ object->variant.file_variant.file_size); ++ tags.extra_file_size = ++ object->variant.file_variant.file_size; ++ } ++ ++ yaffs_verify_oh(object, oh, &tags, 1); ++ new_chunk = ++ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); ++ } else { ++ new_chunk = ++ yaffs_write_new_chunk(dev, buffer, &tags, 1); ++ } ++ ++ if (new_chunk < 0) { ++ ret_val = YAFFS_FAIL; ++ } else { ++ ++ /* Now fix up the Tnodes etc. */ ++ ++ if (tags.chunk_id == 0) { ++ /* It's a header */ ++ object->hdr_chunk = new_chunk; ++ object->serial = tags.serial_number; ++ } else { ++ /* It's a data chunk */ ++ yaffs_put_chunk_in_file(object, tags.chunk_id, ++ new_chunk, 0); ++ } ++ } ++ } ++ if (ret_val == YAFFS_OK) ++ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); ++ return ret_val; ++} ++ ++static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) ++{ ++ int old_chunk; ++ int ret_val = YAFFS_OK; ++ int i; ++ int is_checkpt_block; ++ int max_copies; ++ int chunks_before = yaffs_get_erased_chunks(dev); ++ int chunks_after; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); ++ ++ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d, in use %d, shrink %d, whole_block %d", ++ block, bi->pages_in_use, bi->has_shrink_hdr, ++ whole_block); ++ ++ /*yaffs_verify_free_chunks(dev); */ ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; ++ ++ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ ++ ++ dev->gc_disable = 1; ++ ++ yaffs_summary_gc(dev, block); ++ ++ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d that has no chunks in use", ++ block); ++ yaffs_block_became_dirty(dev, block); ++ } else { ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_verify_blk(dev, bi, block); ++ ++ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; ++ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; ++ ++ for (/* init already done */ ; ++ ret_val == YAFFS_OK && ++ dev->gc_chunk < dev->param.chunks_per_block && ++ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && ++ max_copies > 0; ++ dev->gc_chunk++, old_chunk++) { ++ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { ++ /* Page is in use and might need to be copied */ ++ max_copies--; ++ ret_val = yaffs_gc_process_chunk(dev, bi, ++ old_chunk, buffer); ++ } ++ } ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ yaffs_verify_collected_blk(dev, bi, block); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ /* ++ * The gc did not complete. Set block state back to FULL ++ * because checkpointing does not restore gc. ++ */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } else { ++ /* The gc completed. */ ++ /* Do any required cleanups */ ++ for (i = 0; i < dev->n_clean_ups; i++) { ++ /* Time to delete the file too */ ++ struct yaffs_obj *object = ++ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); ++ if (object) { ++ yaffs_free_tnode(dev, ++ object->variant.file_variant.top); ++ object->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: About to finally delete object %d", ++ object->obj_id); ++ yaffs_generic_obj_del(object); ++ object->my_dev->n_deleted_files--; ++ } ++ ++ } ++ chunks_after = yaffs_get_erased_chunks(dev); ++ if (chunks_before >= chunks_after) ++ yaffs_trace(YAFFS_TRACE_GC, ++ "gc did not increase free chunks before %d after %d", ++ chunks_before, chunks_after); ++ dev->gc_block = 0; ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ dev->gc_disable = 0; ++ ++ return ret_val; ++} ++ ++/* ++ * find_gc_block() selects the dirtiest block (or close enough) ++ * for garbage collection. ++ */ ++ ++static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, ++ int aggressive, int background) ++{ ++ int i; ++ int iterations; ++ unsigned selected = 0; ++ int prioritised = 0; ++ int prioritised_exist = 0; ++ struct yaffs_block_info *bi; ++ int threshold; ++ ++ /* First let's see if we need to grab a prioritised block */ ++ if (dev->has_pending_prioritised_gc && !aggressive) { ++ dev->gc_dirtiest = 0; ++ bi = dev->block_info; ++ for (i = dev->internal_start_block; ++ i <= dev->internal_end_block && !selected; i++) { ++ ++ if (bi->gc_prioritise) { ++ prioritised_exist = 1; ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ selected = i; ++ prioritised = 1; ++ } ++ } ++ bi++; ++ } ++ ++ /* ++ * If there is a prioritised block and none was selected then ++ * this happened because there is at least one old dirty block ++ * gumming up the works. Let's gc the oldest dirty block. ++ */ ++ ++ if (prioritised_exist && ++ !selected && dev->oldest_dirty_block > 0) ++ selected = dev->oldest_dirty_block; ++ ++ if (!prioritised_exist) /* None found, so we can clear this */ ++ dev->has_pending_prioritised_gc = 0; ++ } ++ ++ /* If we're doing aggressive GC then we are happy to take a less-dirty ++ * block, and search harder. ++ * else (leasurely gc), then we only bother to do this if the ++ * block has only a few pages in use. ++ */ ++ ++ if (!selected) { ++ int pages_used; ++ int n_blocks = ++ dev->internal_end_block - dev->internal_start_block + 1; ++ if (aggressive) { ++ threshold = dev->param.chunks_per_block; ++ iterations = n_blocks; ++ } else { ++ int max_threshold; ++ ++ if (background) ++ max_threshold = dev->param.chunks_per_block / 2; ++ else ++ max_threshold = dev->param.chunks_per_block / 8; ++ ++ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ ++ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; ++ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ if (threshold > max_threshold) ++ threshold = max_threshold; ++ ++ iterations = n_blocks / 16 + 1; ++ if (iterations > 100) ++ iterations = 100; ++ } ++ ++ for (i = 0; ++ i < iterations && ++ (dev->gc_dirtiest < 1 || ++ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); ++ i++) { ++ dev->gc_block_finder++; ++ if (dev->gc_block_finder < dev->internal_start_block || ++ dev->gc_block_finder > dev->internal_end_block) ++ dev->gc_block_finder = ++ dev->internal_start_block; ++ ++ bi = yaffs_get_block_info(dev, dev->gc_block_finder); ++ ++ pages_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ pages_used < dev->param.chunks_per_block && ++ (dev->gc_dirtiest < 1 || ++ pages_used < dev->gc_pages_in_use) && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ dev->gc_dirtiest = dev->gc_block_finder; ++ dev->gc_pages_in_use = pages_used; ++ } ++ } ++ ++ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) ++ selected = dev->gc_dirtiest; ++ } ++ ++ /* ++ * If nothing has been selected for a while, try the oldest dirty ++ * because that's gumming up the works. ++ */ ++ ++ if (!selected && dev->param.is_yaffs2 && ++ dev->gc_not_done >= (background ? 10 : 20)) { ++ yaffs2_find_oldest_dirty_seq(dev); ++ if (dev->oldest_dirty_block > 0) { ++ selected = dev->oldest_dirty_block; ++ dev->gc_dirtiest = selected; ++ dev->oldest_dirty_gc_count++; ++ bi = yaffs_get_block_info(dev, selected); ++ dev->gc_pages_in_use = ++ bi->pages_in_use - bi->soft_del_pages; ++ } else { ++ dev->gc_not_done = 0; ++ } ++ } ++ ++ if (selected) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC Selected block %d with %d free, prioritised:%d", ++ selected, ++ dev->param.chunks_per_block - dev->gc_pages_in_use, ++ prioritised); ++ ++ dev->n_gc_blocks++; ++ if (background) ++ dev->bg_gcs++; ++ ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ dev->gc_not_done = 0; ++ if (dev->refresh_skip > 0) ++ dev->refresh_skip--; ++ } else { ++ dev->gc_not_done++; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", ++ dev->gc_block_finder, dev->gc_not_done, threshold, ++ dev->gc_dirtiest, dev->gc_pages_in_use, ++ dev->oldest_dirty_block, background ? " bg" : ""); ++ } ++ ++ return selected; ++} ++ ++/* New garbage collector ++ * If we're very low on erased blocks then we do aggressive garbage collection ++ * otherwise we do "leasurely" garbage collection. ++ * Aggressive gc looks further (whole array) and will accept less dirty blocks. ++ * Passive gc only inspects smaller areas and only accepts more dirty blocks. ++ * ++ * The idea is to help clear out space in a more spread-out manner. ++ * Dunno if it really does anything useful. ++ */ ++static int yaffs_check_gc(struct yaffs_dev *dev, int background) ++{ ++ int aggressive = 0; ++ int gc_ok = YAFFS_OK; ++ int max_tries = 0; ++ int min_erased; ++ int erased_chunks; ++ int checkpt_block_adjust; ++ ++ if (dev->param.gc_control_fn && ++ (dev->param.gc_control_fn(dev) & 1) == 0) ++ return YAFFS_OK; ++ ++ if (dev->gc_disable) ++ /* Bail out so we don't get recursive gc */ ++ return YAFFS_OK; ++ ++ /* This loop should pass the first time. ++ * Only loops here if the collection does not increase space. ++ */ ++ ++ do { ++ max_tries++; ++ ++ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); ++ ++ min_erased = ++ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; ++ erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ /* If we need a block soon then do aggressive gc. */ ++ if (dev->n_erased_blocks < min_erased) ++ aggressive = 1; ++ else { ++ if (!background ++ && erased_chunks > (dev->n_free_chunks / 4)) ++ break; ++ ++ if (dev->gc_skip > 20) ++ dev->gc_skip = 20; ++ if (erased_chunks < dev->n_free_chunks / 2 || ++ dev->gc_skip < 1 || background) ++ aggressive = 0; ++ else { ++ dev->gc_skip--; ++ break; ++ } ++ } ++ ++ dev->gc_skip = 5; ++ ++ /* If we don't already have a block being gc'd then see if we ++ * should start another */ ++ ++ if (dev->gc_block < 1 && !aggressive) { ++ dev->gc_block = yaffs2_find_refresh_block(dev); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ if (dev->gc_block < 1) { ++ dev->gc_block = ++ yaffs_find_gc_block(dev, aggressive, background); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ if (dev->gc_block > 0) { ++ dev->all_gcs++; ++ if (!aggressive) ++ dev->passive_gc_count++; ++ ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC n_erased_blocks %d aggressive %d", ++ dev->n_erased_blocks, aggressive); ++ ++ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); ++ } ++ ++ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && ++ dev->gc_block > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", ++ dev->n_erased_blocks, max_tries, ++ dev->gc_block); ++ } ++ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && ++ (dev->gc_block > 0) && (max_tries < 2)); ++ ++ return aggressive ? gc_ok : YAFFS_OK; ++} ++ ++/* ++ * yaffs_bg_gc() ++ * Garbage collects. Intended to be called from a background thread. ++ * Returns non-zero if at least half the free chunks are erased. ++ */ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) ++{ ++ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); ++ ++ yaffs_check_gc(dev, 1); ++ return erased_chunks > dev->n_free_chunks / 2; ++} ++ ++/*-------------------- Data file manipulation -----------------*/ ++ ++static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) ++{ ++ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); ++ ++ if (nand_chunk >= 0) ++ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, ++ buffer, NULL); ++ else { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not found zero instead", ++ nand_chunk); ++ /* get sane (zero) data if you read a hole */ ++ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); ++ return 0; ++ } ++ ++} ++ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn) ++{ ++ int block; ++ int page; ++ struct yaffs_ext_tags tags; ++ struct yaffs_block_info *bi; ++ ++ if (chunk_id <= 0) ++ return; ++ ++ dev->n_deletions++; ++ block = chunk_id / dev->param.chunks_per_block; ++ page = chunk_id % dev->param.chunks_per_block; ++ ++ if (!yaffs_check_chunk_bit(dev, block, page)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Deleting invalid chunk %d", chunk_id); ++ ++ bi = yaffs_get_block_info(dev, block); ++ ++ yaffs2_update_oldest_dirty_seq(dev, block, bi); ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, ++ "line %d delete of chunk %d", ++ lyn, chunk_id); ++ ++ if (!dev->param.is_yaffs2 && mark_flash && ++ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { ++ ++ memset(&tags, 0, sizeof(tags)); ++ tags.is_deleted = 1; ++ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); ++ yaffs_handle_chunk_update(dev, chunk_id, &tags); ++ } else { ++ dev->n_unmarked_deletions++; ++ } ++ ++ /* Pull out of the management area. ++ * If the whole block became dirty, this will kick off an erasure. ++ */ ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || ++ bi->block_state == YAFFS_BLOCK_STATE_FULL || ++ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ dev->n_free_chunks++; ++ yaffs_clear_chunk_bit(dev, block, page); ++ bi->pages_in_use--; ++ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && ++ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ yaffs_block_became_dirty(dev, block); ++ } ++ } ++} ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve) ++{ ++ /* Find old chunk Need to do this to get serial number ++ * Write new one and patch into tree. ++ * Invalidate old tags. ++ */ ++ ++ int prev_chunk_id; ++ struct yaffs_ext_tags prev_tags; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ yaffs_check_gc(dev, 0); ++ ++ /* Get the previous chunk at this location in the file if it exists. ++ * If it does not exist then put a zero into the tree. This creates ++ * the tnode now, rather than later when it is harder to clean up. ++ */ ++ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); ++ if (prev_chunk_id < 1 && ++ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) ++ return 0; ++ ++ /* Set up new tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ ++ new_tags.chunk_id = inode_chunk; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = ++ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; ++ new_tags.n_bytes = n_bytes; ++ ++ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Writing %d bytes to chunk!!!!!!!!!", ++ n_bytes); ++ BUG(); ++ } ++ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); ++ ++ if (new_chunk_id > 0) { ++ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ yaffs_verify_file_sane(in); ++ } ++ return new_chunk_id; ++ ++} ++ ++ ++ ++static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, ++ const YCHAR *name, const void *value, int size, ++ int flags) ++{ ++ struct yaffs_xattr_mod xmod; ++ int result; ++ ++ xmod.set = set; ++ xmod.name = name; ++ xmod.data = value; ++ xmod.size = size; ++ xmod.flags = flags; ++ xmod.result = -ENOSPC; ++ ++ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); ++ ++ if (result > 0) ++ return xmod.result; ++ else ++ return -ENOSPC; ++} ++ ++static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, ++ struct yaffs_xattr_mod *xmod) ++{ ++ int retval = 0; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer = buffer + x_offs; ++ ++ if (xmod->set) ++ retval = ++ nval_set(x_buffer, x_size, xmod->name, xmod->data, ++ xmod->size, xmod->flags); ++ else ++ retval = nval_del(x_buffer, x_size, xmod->name); ++ ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ xmod->result = retval; ++ ++ return retval; ++} ++ ++static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, ++ void *value, int size) ++{ ++ char *buffer = NULL; ++ int result; ++ struct yaffs_ext_tags tags; ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer; ++ int retval = 0; ++ ++ if (obj->hdr_chunk < 1) ++ return -ENODATA; ++ ++ /* If we know that the object has no xattribs then don't do all the ++ * reading and parsing. ++ */ ++ if (obj->xattr_known && !obj->has_xattr) { ++ if (name) ++ return -ENODATA; ++ else ++ return 0; ++ } ++ ++ buffer = (char *)yaffs_get_temp_buffer(dev); ++ if (!buffer) ++ return -ENOMEM; ++ ++ result = ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ retval = -ENOENT; ++ else { ++ x_buffer = buffer + x_offs; ++ ++ if (!obj->xattr_known) { ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ } ++ ++ if (name) ++ retval = nval_get(x_buffer, x_size, name, value, size); ++ else ++ retval = nval_list(x_buffer, x_size, value, size); ++ } ++ yaffs_release_temp_buffer(dev, (u8 *) buffer); ++ return retval; ++} ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, ++ const void *value, int size, int flags) ++{ ++ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); ++} ++ ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); ++} ++ ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, ++ int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, name, value, size); ++} ++ ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); ++} ++ ++static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) ++{ ++ u8 *buf; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ ++ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) ++ return; ++ ++ dev = in->my_dev; ++ in->lazy_loaded = 0; ++ buf = yaffs_get_temp_buffer(dev); ++ ++ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); ++ oh = (struct yaffs_obj_hdr *)buf; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ yaffs_set_obj_name_from_oh(in, oh); ++ ++ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ in->variant.symlink_variant.alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant.symlink_variant.alias) ++ alloc_failed = 1; /* Not returned */ ++ } ++ yaffs_release_temp_buffer(dev, buf); ++} ++ ++static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, ++ const YCHAR *oh_name, int buff_size) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ if (dev->param.auto_unicode) { ++ if (*oh_name) { ++ /* It is an ASCII name, do an ASCII to ++ * unicode conversion */ ++ const char *ascii_oh_name = (const char *)oh_name; ++ int n = buff_size - 1; ++ while (n > 0 && *ascii_oh_name) { ++ *name = *ascii_oh_name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ strncpy(name, oh_name + 1, buff_size - 1); ++ } ++ } else { ++#else ++ (void) dev; ++ { ++#endif ++ strncpy(name, oh_name, buff_size - 1); ++ } ++} ++ ++static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, ++ const YCHAR *name) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ ++ int is_ascii; ++ YCHAR *w; ++ ++ if (dev->param.auto_unicode) { ++ ++ is_ascii = 1; ++ w = name; ++ ++ /* Figure out if the name will fit in ascii character set */ ++ while (is_ascii && *w) { ++ if ((*w) & 0xff00) ++ is_ascii = 0; ++ w++; ++ } ++ ++ if (is_ascii) { ++ /* It is an ASCII name, so convert unicode to ascii */ ++ char *ascii_oh_name = (char *)oh_name; ++ int n = YAFFS_MAX_NAME_LENGTH - 1; ++ while (n > 0 && *name) { ++ *ascii_oh_name = *name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ /* Unicode name, so save starting at the second YCHAR */ ++ *oh_name = 0; ++ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); ++ } ++ } else { ++#else ++ dev = dev; ++ { ++#endif ++ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); ++ } ++} ++ ++/* UpdateObjectHeader updates the header on NAND for an object. ++ * If name is not NULL, then that new name is used. ++ */ ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, ++ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) ++{ ++ ++ struct yaffs_block_info *bi; ++ struct yaffs_dev *dev = in->my_dev; ++ int prev_chunk_id; ++ int ret_val = 0; ++ int result = 0; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_ext_tags old_tags; ++ const YCHAR *alias = NULL; ++ u8 *buffer = NULL; ++ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj_hdr *oh = NULL; ++ loff_t file_size = 0; ++ ++ strcpy(old_name, _Y("silly old name")); ++ ++ if (in->fake && in != dev->root_dir && !force && !xmod) ++ return ret_val; ++ ++ yaffs_check_gc(dev, 0); ++ yaffs_check_obj_details_loaded(in); ++ ++ buffer = yaffs_get_temp_buffer(in->my_dev); ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ prev_chunk_id = in->hdr_chunk; ++ ++ if (prev_chunk_id > 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, ++ buffer, &old_tags); ++ ++ yaffs_verify_oh(in, oh, &old_tags, 0); ++ memcpy(old_name, oh->name, sizeof(oh->name)); ++ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); ++ } else { ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ } ++ ++ oh->type = in->variant_type; ++ oh->yst_mode = in->yst_mode; ++ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; ++ ++ yaffs_load_attribs_oh(oh, in); ++ ++ if (in->parent) ++ oh->parent_obj_id = in->parent->obj_id; ++ else ++ oh->parent_obj_id = 0; ++ ++ if (name && *name) { ++ memset(oh->name, 0, sizeof(oh->name)); ++ yaffs_load_oh_from_name(dev, oh->name, name); ++ } else if (prev_chunk_id > 0) { ++ memcpy(oh->name, old_name, sizeof(oh->name)); ++ } else { ++ memset(oh->name, 0, sizeof(oh->name)); ++ } ++ ++ oh->is_shrink = is_shrink; ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Should not happen */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && ++ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) ++ file_size = in->variant.file_variant.file_size; ++ yaffs_oh_size_load(oh, file_size); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ oh->equiv_id = in->variant.hardlink_variant.equiv_id; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = in->variant.symlink_variant.alias; ++ if (!alias) ++ alias = _Y("no alias"); ++ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); ++ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; ++ break; ++ } ++ ++ /* process any xattrib modifications */ ++ if (xmod) ++ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); ++ ++ /* Tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ in->serial++; ++ new_tags.chunk_id = 0; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = in->serial; ++ ++ /* Add extra info for file header */ ++ new_tags.extra_available = 1; ++ new_tags.extra_parent_id = oh->parent_obj_id; ++ new_tags.extra_file_size = file_size; ++ new_tags.extra_is_shrink = oh->is_shrink; ++ new_tags.extra_equiv_id = oh->equiv_id; ++ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; ++ new_tags.extra_obj_type = in->variant_type; ++ yaffs_verify_oh(in, oh, &new_tags, 1); ++ ++ /* Create new chunk in NAND */ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, ++ (prev_chunk_id > 0) ? 1 : 0); ++ ++ if (buffer) ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (new_chunk_id < 0) ++ return new_chunk_id; ++ ++ in->hdr_chunk = new_chunk_id; ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ if (!yaffs_obj_cache_dirty(in)) ++ in->dirty = 0; ++ ++ /* If this was a shrink, then mark the block ++ * that the chunk lives on */ ++ if (is_shrink) { ++ bi = yaffs_get_block_info(in->my_dev, ++ new_chunk_id / ++ in->my_dev->param.chunks_per_block); ++ bi->has_shrink_hdr = 1; ++ } ++ ++ ++ return new_chunk_id; ++} ++ ++/*--------------------- File read/write ------------------------ ++ * Read and write have very similar structures. ++ * In general the read/write has three parts to it ++ * An incomplete chunk to start with (if the read/write is not chunk-aligned) ++ * Some complete chunks ++ * An incomplete chunk to end off with ++ * ++ * Curve-balls: the first chunk might also be the last chunk. ++ */ ++ ++int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) ++{ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ struct yaffs_cache *cache; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ chunk++; ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ if ((start + n) < dev->data_bytes_per_chunk) ++ n_copy = n; ++ else ++ n_copy = dev->data_bytes_per_chunk - start; ++ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ /* If the chunk is already in the cache or it is less than ++ * a whole chunk or we're using inband tags then use the cache ++ * (if there is caching) else bypass the cache. ++ */ ++ if (cache || n_copy != dev->data_bytes_per_chunk || ++ dev->param.inband_tags) { ++ if (dev->param.n_caches > 0) { ++ ++ /* If we can't find the data in the cache, ++ * then load it up. */ ++ ++ if (!cache) { ++ cache = ++ yaffs_grab_chunk_cache(in->my_dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ cache->n_bytes = 0; ++ } ++ ++ yaffs_use_cache(dev, cache, 0); ++ ++ cache->locked = 1; ++ ++ memcpy(buffer, &cache->data[start], n_copy); ++ ++ cache->locked = 0; ++ } else { ++ /* Read into the local buffer then copy.. */ ++ ++ u8 *local_buffer = ++ yaffs_get_temp_buffer(dev); ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ ++ memcpy(buffer, &local_buffer[start], n_copy); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Read directly into the buffer. */ ++ yaffs_rd_data_obj(in, chunk, buffer); ++ } ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ return n_done; ++} ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ int n_writeback; ++ loff_t start_write = offset; ++ int chunk_written = 0; ++ u32 n_bytes_read; ++ loff_t chunk_start; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0 && chunk_written >= 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ ++ if (((loff_t)chunk) * ++ dev->data_bytes_per_chunk + start != offset || ++ start >= dev->data_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "AddrToChunk of offset %lld gives chunk %d start %d", ++ offset, chunk, start); ++ } ++ chunk++; /* File pos to chunk in file offset */ ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ ++ if ((start + n) < dev->data_bytes_per_chunk) { ++ n_copy = n; ++ ++ /* Now calculate how many bytes to write back.... ++ * If we're overwriting and not writing to then end of ++ * file then we need to write back as much as was there ++ * before. ++ */ ++ ++ chunk_start = (((loff_t)(chunk - 1)) * ++ dev->data_bytes_per_chunk); ++ ++ if (chunk_start > in->variant.file_variant.file_size) ++ n_bytes_read = 0; /* Past end of file */ ++ else ++ n_bytes_read = ++ in->variant.file_variant.file_size - ++ chunk_start; ++ ++ if (n_bytes_read > dev->data_bytes_per_chunk) ++ n_bytes_read = dev->data_bytes_per_chunk; ++ ++ n_writeback = ++ (n_bytes_read > ++ (start + n)) ? n_bytes_read : (start + n); ++ ++ if (n_writeback < 0 || ++ n_writeback > dev->data_bytes_per_chunk) ++ BUG(); ++ ++ } else { ++ n_copy = dev->data_bytes_per_chunk - start; ++ n_writeback = dev->data_bytes_per_chunk; ++ } ++ ++ if (n_copy != dev->data_bytes_per_chunk || ++ !dev->param.cache_bypass_aligned || ++ dev->param.inband_tags) { ++ /* An incomplete start or end chunk (or maybe both ++ * start and end chunk), or we're using inband tags, ++ * or we're forcing writes through the cache, ++ * so we want to use the cache buffers. ++ */ ++ if (dev->param.n_caches > 0) { ++ struct yaffs_cache *cache; ++ ++ /* If we can't find the data in the cache, then ++ * load the cache */ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ if (!cache && ++ yaffs_check_alloc_available(dev, 1)) { ++ cache = yaffs_grab_chunk_cache(dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ } else if (cache && ++ !cache->dirty && ++ !yaffs_check_alloc_available(dev, ++ 1)) { ++ /* Drop the cache if it was a read cache ++ * item and no space check has been made ++ * for it. ++ */ ++ cache = NULL; ++ } ++ ++ if (cache) { ++ yaffs_use_cache(dev, cache, 1); ++ cache->locked = 1; ++ ++ memcpy(&cache->data[start], buffer, ++ n_copy); ++ ++ cache->locked = 0; ++ cache->n_bytes = n_writeback; ++ ++ if (write_through) { ++ chunk_written = ++ yaffs_wr_data_obj ++ (cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ } ++ } else { ++ chunk_written = -1; /* fail write */ ++ } ++ } else { ++ /* An incomplete start or end chunk (or maybe ++ * both start and end chunk). Read into the ++ * local buffer then copy over and write back. ++ */ ++ ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ memcpy(&local_buffer[start], buffer, n_copy); ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, ++ local_buffer, ++ n_writeback, 0); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Write directly from the buffer. */ ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, buffer, ++ dev->data_bytes_per_chunk, 0); ++ ++ /* Since we've overwritten the cached data, ++ * we better invalidate it. */ ++ yaffs_invalidate_chunk_cache(in, chunk); ++ } ++ ++ if (chunk_written >= 0) { ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ } ++ ++ /* Update file object */ ++ ++ if ((start_write + n_done) > in->variant.file_variant.file_size) ++ in->variant.file_variant.file_size = (start_write + n_done); ++ ++ in->dirty = 1; ++ return n_done; ++} ++ ++int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ yaffs2_handle_hole(in, offset); ++ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); ++} ++ ++/* ---------------------- File resizing stuff ------------------ */ ++ ++static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) ++{ ++ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ int i; ++ int chunk_id; ++ u32 dummy; ++ int last_del; ++ int start_del; ++ ++ if (old_size > 0) ++ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); ++ else ++ last_del = 0; ++ ++ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, ++ &start_del, &dummy); ++ last_del++; ++ start_del++; ++ ++ /* Delete backwards so that we don't end up with holes if ++ * power is lost part-way through the operation. ++ */ ++ for (i = last_del; i >= start_del; i--) { ++ /* NB this could be optimised somewhat, ++ * eg. could retrieve the tags and write them without ++ * using yaffs_chunk_del ++ */ ++ ++ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); ++ ++ if (chunk_id < 1) ++ continue; ++ ++ if (chunk_id < ++ (dev->internal_start_block * dev->param.chunks_per_block) || ++ chunk_id >= ++ ((dev->internal_end_block + 1) * ++ dev->param.chunks_per_block)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Found daft chunk_id %d for %d", ++ chunk_id, i); ++ } else { ++ in->n_data_chunks--; ++ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); ++ } ++ } ++} ++ ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) ++{ ++ int new_full; ++ u32 new_partial; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); ++ ++ yaffs_prune_chunks(obj, new_size); ++ ++ if (new_partial != 0) { ++ int last_chunk = 1 + new_full; ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ /* Rewrite the last chunk with its new size and zero pad */ ++ yaffs_rd_data_obj(obj, last_chunk, local_buffer); ++ memset(local_buffer + new_partial, 0, ++ dev->data_bytes_per_chunk - new_partial); ++ ++ yaffs_wr_data_obj(obj, last_chunk, local_buffer, ++ new_partial, 1); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ ++ obj->variant.file_variant.file_size = new_size; ++ ++ yaffs_prune_tree(dev, &obj->variant.file_variant); ++} ++ ++int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) ++{ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ ++ yaffs_flush_file_cache(in); ++ yaffs_invalidate_whole_cache(in); ++ ++ yaffs_check_gc(dev, 0); ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ if (new_size == old_size) ++ return YAFFS_OK; ++ ++ if (new_size > old_size) { ++ yaffs2_handle_hole(in, new_size); ++ in->variant.file_variant.file_size = new_size; ++ } else { ++ /* new_size < old_size */ ++ yaffs_resize_file_down(in, new_size); ++ } ++ ++ /* Write a new object header to reflect the resize. ++ * show we've shrunk the file, if need be ++ * Do this only if the file is not in the deleted directories ++ * and is not shadowed. ++ */ ++ if (in->parent && ++ !in->is_shadowed && ++ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ in->parent->obj_id != YAFFS_OBJECTID_DELETED) ++ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); ++ ++ return YAFFS_OK; ++} ++ ++int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync) ++{ ++ if (!in->dirty) ++ return YAFFS_OK; ++ ++ yaffs_flush_file_cache(in); ++ ++ if (data_sync) ++ return YAFFS_OK; ++ ++ if (update_time) ++ yaffs_load_current_time(in, 0, 0); ++ ++ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? ++ YAFFS_OK : YAFFS_FAIL; ++} ++ ++ ++/* yaffs_del_file deletes the whole file data ++ * and the inode associated with the file. ++ * It does not delete the links associated with the file. ++ */ ++static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) ++{ ++ int ret_val; ++ int del_now = 0; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!in->my_inode) ++ del_now = 1; ++ ++ if (del_now) { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->del_dir, ++ _Y("deleted"), 0, 0); ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: immediate deletion of file %d", ++ in->obj_id); ++ in->deleted = 1; ++ in->my_dev->n_deleted_files++; ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ yaffs_soft_del_file(in); ++ } else { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++ return ret_val; ++} ++ ++static int yaffs_del_file(struct yaffs_obj *in) ++{ ++ int ret_val = YAFFS_OK; ++ int deleted; /* Need to cache value on stack if in is freed */ ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ ++ if (in->n_data_chunks > 0) { ++ /* Use soft deletion if there is data in the file. ++ * That won't be the case if it has been resized to zero. ++ */ ++ if (!in->unlinked) ++ ret_val = yaffs_unlink_file_if_needed(in); ++ ++ deleted = in->deleted; ++ ++ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { ++ in->deleted = 1; ++ deleted = 1; ++ in->my_dev->n_deleted_files++; ++ yaffs_soft_del_file(in); ++ } ++ return deleted ? YAFFS_OK : YAFFS_FAIL; ++ } else { ++ /* The file has no data chunks so we toss it immediately */ ++ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); ++ in->variant.file_variant.top = NULL; ++ yaffs_generic_obj_del(in); ++ ++ return YAFFS_OK; ++ } ++} ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj) ++{ ++ return (obj && ++ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && ++ !(list_empty(&obj->variant.dir_variant.children)); ++} ++ ++static int yaffs_del_dir(struct yaffs_obj *obj) ++{ ++ /* First check that the directory is empty. */ ++ if (yaffs_is_non_empty_dir(obj)) ++ return YAFFS_FAIL; ++ ++ return yaffs_generic_obj_del(obj); ++} ++ ++static int yaffs_del_symlink(struct yaffs_obj *in) ++{ ++ kfree(in->variant.symlink_variant.alias); ++ in->variant.symlink_variant.alias = NULL; ++ ++ return yaffs_generic_obj_del(in); ++} ++ ++static int yaffs_del_link(struct yaffs_obj *in) ++{ ++ /* remove this hardlink from the list associated with the equivalent ++ * object ++ */ ++ list_del_init(&in->hard_links); ++ return yaffs_generic_obj_del(in); ++} ++ ++int yaffs_del_obj(struct yaffs_obj *obj) ++{ ++ int ret_val = -1; ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ ret_val = yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!list_empty(&obj->variant.dir_variant.dirty)) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Remove object %d from dirty directories", ++ obj->obj_id); ++ list_del_init(&obj->variant.dir_variant.dirty); ++ } ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ ret_val = yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ ret_val = yaffs_del_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ ret_val = yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ ret_val = 0; ++ break; /* should not happen. */ ++ } ++ return ret_val; ++} ++ ++ ++static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, ++ struct yaffs_obj *to_dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_add_obj_to_dir(to_dir, obj); ++ } ++} ++ ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type) ++{ ++ /* Tear down the old variant */ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ /* Nuke file data */ ++ yaffs_resize_file(obj, 0); ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Put the children in lost and found. */ ++ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); ++ if (!list_empty(&obj->variant.dir_variant.dirty)) ++ list_del_init(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ /* Nuke symplink data */ ++ kfree(obj->variant.symlink_variant.alias); ++ obj->variant.symlink_variant.alias = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ list_del_init(&obj->hard_links); ++ break; ++ default: ++ break; ++ } ++ ++ memset(&obj->variant, 0, sizeof(obj->variant)); ++ ++ /*Set up new variant if the memset is not enough. */ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ default: ++ break; ++ } ++ ++ obj->variant_type = type; ++ ++ return obj; ++ ++} ++ ++static int yaffs_unlink_worker(struct yaffs_obj *obj) ++{ ++ int del_now = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (!obj->my_inode) ++ del_now = 1; ++ ++ yaffs_update_parent(obj->parent); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ return yaffs_del_link(obj); ++ } else if (!list_empty(&obj->hard_links)) { ++ /* Curve ball: We're unlinking an object that has a hardlink. ++ * ++ * This problem arises because we are not strictly following ++ * The Linux link/inode model. ++ * ++ * We can't really delete the object. ++ * Instead, we do the following: ++ * - Select a hardlink. ++ * - Unhook it from the hard links ++ * - Move it from its parent directory so that the rename works. ++ * - Rename the object to the hardlink's name. ++ * - Delete the hardlink ++ */ ++ ++ struct yaffs_obj *hl; ++ struct yaffs_obj *parent; ++ int ret_val; ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ hl = list_entry(obj->hard_links.next, struct yaffs_obj, ++ hard_links); ++ ++ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); ++ parent = hl->parent; ++ ++ list_del_init(&hl->hard_links); ++ ++ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); ++ ++ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); ++ ++ if (ret_val == YAFFS_OK) ++ ret_val = yaffs_generic_obj_del(hl); ++ ++ return ret_val; ++ ++ } else if (del_now) { ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ list_del_init(&obj->variant.dir_variant.dirty); ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ return yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ return YAFFS_FAIL; ++ } ++ } else if (yaffs_is_non_empty_dir(obj)) { ++ return YAFFS_FAIL; ++ } else { ++ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++} ++ ++static int yaffs_unlink_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->unlink_allowed) ++ return yaffs_unlink_worker(obj); ++ ++ return YAFFS_FAIL; ++} ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) ++{ ++ struct yaffs_obj *obj; ++ ++ obj = yaffs_find_by_name(dir, name); ++ return yaffs_unlink_obj(obj); ++} ++ ++/* Note: ++ * If old_name is NULL then we take old_dir as the object to be renamed. ++ */ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, ++ struct yaffs_obj *new_dir, const YCHAR *new_name) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *existing_target = NULL; ++ int force = 0; ++ int result; ++ struct yaffs_dev *dev; ++ ++ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ dev = old_dir->my_dev; ++ ++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE ++ /* Special case for case insemsitive systems. ++ * While look-up is case insensitive, the name isn't. ++ * Therefore we might want to change x.txt to X.txt ++ */ ++ if (old_dir == new_dir && ++ old_name && new_name && ++ strcmp(old_name, new_name) == 0) ++ force = 1; ++#endif ++ ++ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ /* ENAMETOOLONG */ ++ return YAFFS_FAIL; ++ ++ if (old_name) ++ obj = yaffs_find_by_name(old_dir, old_name); ++ else{ ++ obj = old_dir; ++ old_dir = obj->parent; ++ } ++ ++ if (obj && obj->rename_allowed) { ++ /* Now handle an existing target, if there is one */ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ if (yaffs_is_non_empty_dir(existing_target)) { ++ return YAFFS_FAIL; /* ENOTEMPTY */ ++ } else if (existing_target && existing_target != obj) { ++ /* Nuke the target first, using shadowing, ++ * but only if it isn't the same object. ++ * ++ * Note we must disable gc here otherwise it can mess ++ * up the shadowing. ++ * ++ */ ++ dev->gc_disable = 1; ++ yaffs_change_obj_name(obj, new_dir, new_name, force, ++ existing_target->obj_id); ++ existing_target->is_shadowed = 1; ++ yaffs_unlink_obj(existing_target); ++ dev->gc_disable = 0; ++ } ++ ++ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); ++ ++ yaffs_update_parent(old_dir); ++ if (new_dir != old_dir) ++ yaffs_update_parent(new_dir); ++ ++ return result; ++ } ++ return YAFFS_FAIL; ++} ++ ++/*----------------------- Initialisation Scanning ---------------------- */ ++ ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning) ++{ ++ struct yaffs_obj *obj; ++ ++ if (backward_scanning) { ++ /* Handle YAFFS2 case (backward scanning) ++ * If the shadowed object exists then ignore. ++ */ ++ obj = yaffs_find_by_number(dev, obj_id); ++ if (obj) ++ return; ++ } ++ ++ /* Let's create it (if it does not exist) assuming it is a file so that ++ * it can do shrinking etc. ++ * We put it in unlinked dir to be cleaned up after the scanning ++ */ ++ obj = ++ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); ++ if (!obj) ++ return; ++ obj->is_shadowed = 1; ++ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); ++ obj->variant.file_variant.shrink_size = 0; ++ obj->valid = 1; /* So that we don't read any other info. */ ++} ++ ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) ++{ ++ struct list_head *lh; ++ struct list_head *save; ++ struct yaffs_obj *hl; ++ struct yaffs_obj *in; ++ ++ list_for_each_safe(lh, save, hard_list) { ++ hl = list_entry(lh, struct yaffs_obj, hard_links); ++ in = yaffs_find_by_number(dev, ++ hl->variant.hardlink_variant.equiv_id); ++ ++ if (in) { ++ /* Add the hardlink pointers */ ++ hl->variant.hardlink_variant.equiv_obj = in; ++ list_add(&hl->hard_links, &in->hard_links); ++ } else { ++ /* Todo Need to report/handle this better. ++ * Got a problem... hardlink to a non-existant object ++ */ ++ hl->variant.hardlink_variant.equiv_obj = NULL; ++ INIT_LIST_HEAD(&hl->hard_links); ++ } ++ } ++} ++ ++static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) ++{ ++ /* ++ * Sort out state of unlinked and deleted objects after scanning. ++ */ ++ struct list_head *i; ++ struct list_head *n; ++ struct yaffs_obj *l; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Soft delete all the unlinked files */ ++ list_for_each_safe(i, n, ++ &dev->unlinked_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++ ++ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++} ++ ++/* ++ * This code iterates through all the objects making sure that they are rooted. ++ * Any unrooted objects are re-rooted in lost+found. ++ * An object needs to be in one of: ++ * - Directly under deleted, unlinked ++ * - Directly or indirectly under root. ++ * ++ * Note: ++ * This code assumes that we don't ever change the current relationships ++ * between directories: ++ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL ++ * lost-n-found->parent == root_dir ++ * ++ * This fixes the problem where directories might have inadvertently been ++ * deleted leaving the object "hanging" without being rooted in the ++ * directory tree. ++ */ ++ ++static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ return (obj == dev->del_dir || ++ obj == dev->unlinked_dir || obj == dev->root_dir); ++} ++ ++static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_obj *parent; ++ int i; ++ struct list_head *lh; ++ struct list_head *n; ++ int depth_limit; ++ int hanging; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Iterate through the objects in each hash entry, ++ * looking at each object. ++ * Make sure it is rooted. ++ */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ parent = obj->parent; ++ ++ if (yaffs_has_null_parent(dev, obj)) { ++ /* These directories are not hanging */ ++ hanging = 0; ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ hanging = 1; ++ } else if (yaffs_has_null_parent(dev, parent)) { ++ hanging = 0; ++ } else { ++ /* ++ * Need to follow the parent chain to ++ * see if it is hanging. ++ */ ++ hanging = 0; ++ depth_limit = 100; ++ ++ while (parent != dev->root_dir && ++ parent->parent && ++ parent->parent->variant_type == ++ YAFFS_OBJECT_TYPE_DIRECTORY && ++ depth_limit > 0) { ++ parent = parent->parent; ++ depth_limit--; ++ } ++ if (parent != dev->root_dir) ++ hanging = 1; ++ } ++ if (hanging) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Hanging object %d moved to lost and found", ++ obj->obj_id); ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ } ++ } ++ } ++} ++ ++/* ++ * Delete directory contents for cleaning up lost and found. ++ */ ++static void yaffs_del_dir_contents(struct yaffs_obj *dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ BUG(); ++ ++ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) ++ yaffs_del_dir_contents(obj); ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Deleting lost_found object %d", ++ obj->obj_id); ++ yaffs_unlink_obj(obj); ++ } ++} ++ ++static void yaffs_empty_l_n_f(struct yaffs_dev *dev) ++{ ++ yaffs_del_dir_contents(dev->lost_n_found); ++} ++ ++ ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, ++ const YCHAR *name) ++{ ++ int sum; ++ struct list_head *i; ++ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj *l; ++ ++ if (!name) ++ return NULL; ++ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: null pointer directory" ++ ); ++ BUG(); ++ return NULL; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: non-directory" ++ ); ++ BUG(); ++ } ++ ++ sum = yaffs_calc_name_sum(name); ++ ++ list_for_each(i, &directory->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ ++ if (l->parent != directory) ++ BUG(); ++ ++ yaffs_check_obj_details_loaded(l); ++ ++ /* Special case for lost-n-found */ ++ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) ++ return l; ++ } else if (l->sum == sum || l->hdr_chunk <= 0) { ++ /* LostnFound chunk called Objxxx ++ * Do a real check ++ */ ++ yaffs_get_obj_name(l, buffer, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) ++ return l; ++ } ++ } ++ return NULL; ++} ++ ++/* GetEquivalentObject dereferences any hard links to get to the ++ * actual object. ++ */ ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ obj = obj->variant.hardlink_variant.equiv_obj; ++ yaffs_check_obj_details_loaded(obj); ++ } ++ return obj; ++} ++ ++/* ++ * A note or two on object names. ++ * * If the object name is missing, we then make one up in the form objnnn ++ * ++ * * ASCII names are stored in the object header's name field from byte zero ++ * * Unicode names are historically stored starting from byte zero. ++ * ++ * Then there are automatic Unicode names... ++ * The purpose of these is to save names in a way that can be read as ++ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII ++ * system to share files. ++ * ++ * These automatic unicode are stored slightly differently... ++ * - If the name can fit in the ASCII character space then they are saved as ++ * ascii names as per above. ++ * - If the name needs Unicode then the name is saved in Unicode ++ * starting at oh->name[1]. ++ ++ */ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size) ++{ ++ /* Create an object name if we could not find one. */ ++ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { ++ YCHAR local_name[20]; ++ YCHAR num_string[20]; ++ YCHAR *x = &num_string[19]; ++ unsigned v = obj->obj_id; ++ num_string[19] = 0; ++ while (v > 0) { ++ x--; ++ *x = '0' + (v % 10); ++ v /= 10; ++ } ++ /* make up a name */ ++ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); ++ strcat(local_name, x); ++ strncpy(name, local_name, buffer_size - 1); ++ } ++} ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) ++{ ++ memset(name, 0, buffer_size * sizeof(YCHAR)); ++ yaffs_check_obj_details_loaded(obj); ++ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); ++ } else if (obj->short_name[0]) { ++ strcpy(name, obj->short_name); ++ } else if (obj->hdr_chunk > 0) { ++ int result; ++ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); ++ ++ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; ++ ++ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); ++ ++ if (obj->hdr_chunk > 0) { ++ result = yaffs_rd_chunk_tags_nand(obj->my_dev, ++ obj->hdr_chunk, ++ buffer, NULL); ++ } ++ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, ++ buffer_size); ++ ++ yaffs_release_temp_buffer(obj->my_dev, buffer); ++ } ++ ++ yaffs_fix_null_name(obj, name, buffer_size); ++ ++ return strnlen(name, YAFFS_MAX_NAME_LENGTH); ++} ++ ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj) ++{ ++ /* Dereference any hard linking */ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ return obj->variant.file_variant.file_size; ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ if (!obj->variant.symlink_variant.alias) ++ return 0; ++ return strnlen(obj->variant.symlink_variant.alias, ++ YAFFS_MAX_ALIAS_LENGTH); ++ } else { ++ /* Only a directory should drop through to here */ ++ return obj->my_dev->data_bytes_per_chunk; ++ } ++} ++ ++int yaffs_get_obj_link_count(struct yaffs_obj *obj) ++{ ++ int count = 0; ++ struct list_head *i; ++ ++ if (!obj->unlinked) ++ count++; /* the object itself */ ++ ++ list_for_each(i, &obj->hard_links) ++ count++; /* add the hard links; */ ++ ++ return count; ++} ++ ++int yaffs_get_obj_inode(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ return obj->obj_id; ++} ++ ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ return DT_DIR; ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return DT_LNK; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ if (S_ISFIFO(obj->yst_mode)) ++ return DT_FIFO; ++ if (S_ISCHR(obj->yst_mode)) ++ return DT_CHR; ++ if (S_ISBLK(obj->yst_mode)) ++ return DT_BLK; ++ if (S_ISSOCK(obj->yst_mode)) ++ return DT_SOCK; ++ return DT_REG; ++ break; ++ default: ++ return DT_REG; ++ break; ++ } ++} ++ ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) ++ return yaffs_clone_str(obj->variant.symlink_variant.alias); ++ else ++ return yaffs_clone_str(_Y("")); ++} ++ ++/*--------------------------- Initialisation code -------------------------- */ ++ ++static int yaffs_check_dev_fns(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ struct yaffs_tags_handler *tagger = &dev->tagger; ++ ++ /* Common functions, gotta have */ ++ if (!drv->drv_read_chunk_fn || ++ !drv->drv_write_chunk_fn || ++ !drv->drv_erase_fn) ++ return 0; ++ ++ if (dev->param.is_yaffs2 && ++ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) ++ return 0; ++ ++ /* Install the default tags marshalling functions if needed. */ ++ yaffs_tags_compat_install(dev); ++ yaffs_tags_marshall_install(dev); ++ ++ /* Check we now have the marshalling functions required. */ ++ if (!tagger->write_chunk_tags_fn || ++ !tagger->read_chunk_tags_fn || ++ !tagger->query_block_fn || ++ !tagger->mark_bad_fn) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs_create_initial_dir(struct yaffs_dev *dev) ++{ ++ /* Initialise the unlinked, deleted, root and lost+found directories */ ++ dev->lost_n_found = dev->root_dir = NULL; ++ dev->unlinked_dir = dev->del_dir = NULL; ++ dev->unlinked_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); ++ dev->del_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); ++ dev->root_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, ++ YAFFS_ROOT_MODE | S_IFDIR); ++ dev->lost_n_found = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, ++ YAFFS_LOSTNFOUND_MODE | S_IFDIR); ++ ++ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir ++ && dev->del_dir) { ++ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++/* Low level init. ++ * Typically only used by yaffs_guts_initialise, but also used by the ++ * Low level yaffs driver tests. ++ */ ++ ++int yaffs_guts_ll_init(struct yaffs_dev *dev) ++{ ++ ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); ++ ++ if (!dev) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Need a device" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ if (dev->ll_init) ++ return YAFFS_OK; ++ ++ dev->internal_start_block = dev->param.start_block; ++ dev->internal_end_block = dev->param.end_block; ++ dev->block_offset = 0; ++ dev->chunk_offset = 0; ++ dev->n_free_chunks = 0; ++ ++ dev->gc_block = 0; ++ ++ if (dev->param.start_block == 0) { ++ dev->internal_start_block = dev->param.start_block + 1; ++ dev->internal_end_block = dev->param.end_block + 1; ++ dev->block_offset = 1; ++ dev->chunk_offset = dev->param.chunks_per_block; ++ } ++ ++ /* Check geometry parameters. */ ++ ++ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 1024) || ++ (!dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 512) || ++ (dev->param.inband_tags && !dev->param.is_yaffs2) || ++ dev->param.chunks_per_block < 2 || ++ dev->param.n_reserved_blocks < 2 || ++ dev->internal_start_block <= 0 || ++ dev->internal_end_block <= 0 || ++ dev->internal_end_block <= ++ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) ++ ) { ++ /* otherwise it is too small */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", ++ dev->param.total_bytes_per_chunk, ++ dev->param.is_yaffs2 ? "2" : "", ++ dev->param.inband_tags); ++ return YAFFS_FAIL; ++ } ++ ++ /* Sort out space for inband tags, if required */ ++ if (dev->param.inband_tags) ++ dev->data_bytes_per_chunk = ++ dev->param.total_bytes_per_chunk - ++ sizeof(struct yaffs_packed_tags2_tags_only); ++ else ++ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; ++ ++ /* Got the right mix of functions? */ ++ if (!yaffs_check_dev_fns(dev)) { ++ /* Function missing */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "device function(s) missing or wrong"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ if (yaffs_init_nand(dev) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev) ++{ ++ int i; ++ enum yaffs_block_state state; ++ u32 dummy; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if(dev->is_mounted) ++ return YAFFS_FAIL; ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ yaffs_query_init_block_state(dev, i, &state, &dummy); ++ if (state != YAFFS_BLOCK_STATE_DEAD) ++ yaffs_erase_block(dev, i); ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev) ++{ ++ int init_failed = 0; ++ unsigned x; ++ int bits; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if (dev->is_mounted) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); ++ return YAFFS_FAIL; ++ } ++ ++ dev->is_mounted = 1; ++ ++ /* OK now calculate a few things for the device */ ++ ++ /* ++ * Calculate all the chunk size manipulation numbers: ++ */ ++ x = dev->data_bytes_per_chunk; ++ /* We always use dev->chunk_shift and dev->chunk_div */ ++ dev->chunk_shift = calc_shifts(x); ++ x >>= dev->chunk_shift; ++ dev->chunk_div = x; ++ /* We only use chunk mask if chunk_div is 1 */ ++ dev->chunk_mask = (1 << dev->chunk_shift) - 1; ++ ++ /* ++ * Calculate chunk_grp_bits. ++ * We need to find the next power of 2 > than internal_end_block ++ */ ++ ++ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); ++ ++ bits = calc_shifts_ceiling(x); ++ ++ /* Set up tnode width if wide tnodes are enabled. */ ++ if (!dev->param.wide_tnodes_disabled) { ++ /* bits must be even so that we end up with 32-bit words */ ++ if (bits & 1) ++ bits++; ++ if (bits < 16) ++ dev->tnode_width = 16; ++ else ++ dev->tnode_width = bits; ++ } else { ++ dev->tnode_width = 16; ++ } ++ ++ dev->tnode_mask = (1 << dev->tnode_width) - 1; ++ ++ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), ++ * so if the bitwidth of the ++ * chunk range we're using is greater than 16 we need ++ * to figure out chunk shift and chunk_grp_size ++ */ ++ ++ if (bits <= dev->tnode_width) ++ dev->chunk_grp_bits = 0; ++ else ++ dev->chunk_grp_bits = bits - dev->tnode_width; ++ ++ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; ++ if (dev->tnode_size < sizeof(struct yaffs_tnode)) ++ dev->tnode_size = sizeof(struct yaffs_tnode); ++ ++ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; ++ ++ if (dev->param.chunks_per_block < dev->chunk_grp_size) { ++ /* We have a problem because the soft delete won't work if ++ * the chunk group size > chunks per block. ++ * This can be remedied by using larger "virtual blocks". ++ */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ /* Finished verifying the device, continue with initialisation */ ++ ++ /* More device initialisation */ ++ dev->all_gcs = 0; ++ dev->passive_gc_count = 0; ++ dev->oldest_dirty_gc_count = 0; ++ dev->bg_gcs = 0; ++ dev->gc_block_finder = 0; ++ dev->buffered_block = -1; ++ dev->doing_buffered_block_rewrite = 0; ++ dev->n_deleted_files = 0; ++ dev->n_bg_deletions = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_ecc_fixed = 0; ++ dev->n_ecc_unfixed = 0; ++ dev->n_tags_ecc_fixed = 0; ++ dev->n_tags_ecc_unfixed = 0; ++ dev->n_erase_failures = 0; ++ dev->n_erased_blocks = 0; ++ dev->gc_disable = 0; ++ dev->has_pending_prioritised_gc = 1; ++ /* Assume the worst for now, will get fixed on first GC */ ++ INIT_LIST_HEAD(&dev->dirty_dirs); ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ ++ /* Initialise temporary buffers and caches. */ ++ if (!yaffs_init_tmp_buffers(dev)) ++ init_failed = 1; ++ ++ dev->cache = NULL; ++ dev->gc_cleanup_list = NULL; ++ ++ if (!init_failed && dev->param.n_caches > 0) { ++ int i; ++ void *buf; ++ int cache_bytes = ++ dev->param.n_caches * sizeof(struct yaffs_cache); ++ ++ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) ++ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; ++ ++ dev->cache = kmalloc(cache_bytes, GFP_NOFS); ++ ++ buf = (u8 *) dev->cache; ++ ++ if (dev->cache) ++ memset(dev->cache, 0, cache_bytes); ++ ++ for (i = 0; i < dev->param.n_caches && buf; i++) { ++ dev->cache[i].object = NULL; ++ dev->cache[i].last_use = 0; ++ dev->cache[i].dirty = 0; ++ dev->cache[i].data = buf = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ } ++ if (!buf) ++ init_failed = 1; ++ ++ dev->cache_last_use = 0; ++ } ++ ++ dev->cache_hits = 0; ++ ++ if (!init_failed) { ++ dev->gc_cleanup_list = ++ kmalloc(dev->param.chunks_per_block * sizeof(u32), ++ GFP_NOFS); ++ if (!dev->gc_cleanup_list) ++ init_failed = 1; ++ } ++ ++ if (dev->param.is_yaffs2) ++ dev->param.use_header_file_size = 1; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && dev->param.is_yaffs2 && ++ !dev->param.disable_summary && ++ !yaffs_summary_init(dev)) ++ init_failed = 1; ++ ++ if (!init_failed) { ++ /* Now scan the flash. */ ++ if (dev->param.is_yaffs2) { ++ if (yaffs2_checkpt_restore(dev)) { ++ yaffs_check_obj_details_loaded(dev->root_dir); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | ++ YAFFS_TRACE_MOUNT, ++ "yaffs: restored from checkpoint" ++ ); ++ } else { ++ ++ /* Clean up the mess caused by an aborted ++ * checkpoint load then scan backwards. ++ */ ++ yaffs_deinit_blocks(dev); ++ ++ yaffs_deinit_tnodes_and_objs(dev); ++ ++ dev->n_erased_blocks = 0; ++ dev->n_free_chunks = 0; ++ dev->alloc_block = -1; ++ dev->alloc_page = -1; ++ dev->n_deleted_files = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_bg_deletions = 0; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed ++ && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && !yaffs2_scan_backwards(dev)) ++ init_failed = 1; ++ } ++ } else if (!yaffs1_scan(dev)) { ++ init_failed = 1; ++ } ++ ++ yaffs_strip_deleted_objs(dev); ++ yaffs_fix_hanging_objs(dev); ++ if (dev->param.empty_lost_n_found) ++ yaffs_empty_l_n_f(dev); ++ } ++ ++ if (init_failed) { ++ /* Clean up the mess */ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() aborted."); ++ ++ yaffs_deinitialise(dev); ++ return YAFFS_FAIL; ++ } ++ ++ /* Zero out stats */ ++ dev->n_page_reads = 0; ++ dev->n_page_writes = 0; ++ dev->n_erasures = 0; ++ dev->n_gc_copies = 0; ++ dev->n_retried_writes = 0; ++ ++ dev->n_retired_blocks = 0; ++ ++ yaffs_verify_free_chunks(dev); ++ yaffs_verify_blocks(dev); ++ ++ /* Clean up any aborted checkpoint data */ ++ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() done."); ++ return YAFFS_OK; ++} ++ ++void yaffs_deinitialise(struct yaffs_dev *dev) ++{ ++ if (dev->is_mounted) { ++ int i; ++ ++ yaffs_deinit_blocks(dev); ++ yaffs_deinit_tnodes_and_objs(dev); ++ yaffs_summary_deinit(dev); ++ ++ if (dev->param.n_caches > 0 && dev->cache) { ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ kfree(dev->cache[i].data); ++ dev->cache[i].data = NULL; ++ } ++ ++ kfree(dev->cache); ++ dev->cache = NULL; ++ } ++ ++ kfree(dev->gc_cleanup_list); ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) ++ kfree(dev->temp_buffer[i].buffer); ++ ++ dev->is_mounted = 0; ++ ++ yaffs_deinit_nand(dev); ++ } ++} ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev) ++{ ++ int n_free = 0; ++ int b; ++ struct yaffs_block_info *blk; ++ ++ blk = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ switch (blk->block_state) { ++ case YAFFS_BLOCK_STATE_EMPTY: ++ case YAFFS_BLOCK_STATE_ALLOCATING: ++ case YAFFS_BLOCK_STATE_COLLECTING: ++ case YAFFS_BLOCK_STATE_FULL: ++ n_free += ++ (dev->param.chunks_per_block - blk->pages_in_use + ++ blk->soft_del_pages); ++ break; ++ default: ++ break; ++ } ++ blk++; ++ } ++ return n_free; ++} ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev) ++{ ++ /* This is what we report to the outside world */ ++ int n_free; ++ int n_dirty_caches; ++ int blocks_for_checkpt; ++ int i; ++ ++ n_free = dev->n_free_chunks; ++ n_free += dev->n_deleted_files; ++ ++ /* Now count and subtract the number of dirty chunks in the cache. */ ++ ++ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].dirty) ++ n_dirty_caches++; ++ } ++ ++ n_free -= n_dirty_caches; ++ ++ n_free -= ++ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); ++ ++ /* Now figure checkpoint space and report that... */ ++ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); ++ ++ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); ++ ++ if (n_free < 0) ++ n_free = 0; ++ ++ return n_free; ++} ++ ++ ++ ++/* ++ * Marshalling functions to get loff_t file sizes into and out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) ++{ ++ oh->file_size_low = (fsize & 0xFFFFFFFF); ++ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); ++} ++ ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) ++{ ++ loff_t retval; ++ ++ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) ++ retval = (((loff_t) oh->file_size_high) << 32) | ++ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); ++ else ++ retval = (loff_t) oh->file_size_low; ++ ++ return retval; ++} ++ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ int s; ++ ++ for(i = 0; i < 10; i++) ++ bs[i] = 0; ++ ++ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ bi = yaffs_get_block_info(dev, i); ++ s = bi->block_state; ++ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) ++ bs[0]++; ++ else ++ bs[s]++; ++ } ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_guts.h linux-3.10.40/fs/yaffs2/yaffs_guts.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_guts.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,1007 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GUTS_H__ ++#define __YAFFS_GUTS_H__ ++ ++#include "yportenv.h" ++ ++#define YAFFS_OK 1 ++#define YAFFS_FAIL 0 ++ ++/* Give us a Y=0x59, ++ * Give us an A=0x41, ++ * Give us an FF=0xff ++ * Give us an S=0x53 ++ * And what have we got... ++ */ ++#define YAFFS_MAGIC 0x5941ff53 ++ ++/* ++ * Tnodes form a tree with the tnodes in "levels" ++ * Levels greater than 0 hold 8 slots which point to other tnodes. ++ * Those at level 0 hold 16 slots which point to chunks in NAND. ++ * ++ * A maximum level of 8 thust supports files of size up to: ++ * ++ * 2^(3*MAX_LEVEL+4) ++ * ++ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives ++ * a maximum file size of around 512Gbytees with 2k chunks. ++ */ ++#define YAFFS_NTNODES_LEVEL0 16 ++#define YAFFS_TNODES_LEVEL0_BITS 4 ++#define YAFFS_TNODES_LEVEL0_MASK 0xf ++ ++#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) ++#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) ++#define YAFFS_TNODES_INTERNAL_MASK 0x7 ++#define YAFFS_TNODES_MAX_LEVEL 8 ++#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ ++ YAFFS_TNODES_INTERNAL_BITS * \ ++ YAFFS_TNODES_MAX_LEVEL) ++#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) ++ ++#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff ++ ++/* Constants for YAFFS1 mode */ ++#define YAFFS_BYTES_PER_SPARE 16 ++#define YAFFS_BYTES_PER_CHUNK 512 ++#define YAFFS_CHUNK_SIZE_SHIFT 9 ++#define YAFFS_CHUNKS_PER_BLOCK 32 ++#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) ++ ++#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 ++#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 ++ ++ ++ ++#define YAFFS_ALLOCATION_NOBJECTS 100 ++#define YAFFS_ALLOCATION_NTNODES 100 ++#define YAFFS_ALLOCATION_NLINKS 100 ++ ++#define YAFFS_NOBJECT_BUCKETS 256 ++ ++#define YAFFS_OBJECT_SPACE 0x40000 ++#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) ++ ++/* Binary data version stamps */ ++#define YAFFS_SUMMARY_VERSION 1 ++#define YAFFS_CHECKPOINT_VERSION 7 ++ ++#ifdef CONFIG_YAFFS_UNICODE ++#define YAFFS_MAX_NAME_LENGTH 127 ++#define YAFFS_MAX_ALIAS_LENGTH 79 ++#else ++#define YAFFS_MAX_NAME_LENGTH 255 ++#define YAFFS_MAX_ALIAS_LENGTH 159 ++#endif ++ ++#define YAFFS_SHORT_NAME_LENGTH 15 ++ ++/* Some special object ids for pseudo objects */ ++#define YAFFS_OBJECTID_ROOT 1 ++#define YAFFS_OBJECTID_LOSTNFOUND 2 ++#define YAFFS_OBJECTID_UNLINKED 3 ++#define YAFFS_OBJECTID_DELETED 4 ++ ++/* Fake object Id for summary data */ ++#define YAFFS_OBJECTID_SUMMARY 0x10 ++ ++/* Pseudo object ids for checkpointing */ ++#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 ++#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 ++ ++#define YAFFS_MAX_SHORT_OP_CACHES 20 ++ ++#define YAFFS_N_TEMP_BUFFERS 6 ++ ++/* We limit the number attempts at sucessfully saving a chunk of data. ++ * Small-page devices have 32 pages per block; large-page devices have 64. ++ * Default to something in the order of 5 to 10 blocks worth of chunks. ++ */ ++#define YAFFS_WR_ATTEMPTS (5*64) ++ ++/* Sequence numbers are used in YAFFS2 to determine block allocation order. ++ * The range is limited slightly to help distinguish bad numbers from good. ++ * This also allows us to perhaps in the future use special numbers for ++ * special purposes. ++ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, ++ * and is a larger number than the lifetime of a 2GB device. ++ */ ++#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 ++#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 ++ ++/* Special sequence number for bad block that failed to be marked bad */ ++#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 ++ ++/* ChunkCache is used for short read/write operations.*/ ++struct yaffs_cache { ++ struct yaffs_obj *object; ++ int chunk_id; ++ int last_use; ++ int dirty; ++ int n_bytes; /* Only valid if the cache is dirty */ ++ int locked; /* Can't push out or flush while locked. */ ++ u8 *data; ++}; ++ ++/* yaffs1 tags structures in RAM ++ * NB This uses bitfield. Bitfields should not straddle a u32 boundary ++ * otherwise the structure size will get blown out. ++ */ ++ ++struct yaffs_tags { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes_lsb:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 n_bytes_msb:2; ++}; ++ ++union yaffs_tags_union { ++ struct yaffs_tags as_tags; ++ u8 as_bytes[8]; ++}; ++ ++ ++/* Stuff used for extended tags in YAFFS2 */ ++ ++enum yaffs_ecc_result { ++ YAFFS_ECC_RESULT_UNKNOWN, ++ YAFFS_ECC_RESULT_NO_ERROR, ++ YAFFS_ECC_RESULT_FIXED, ++ YAFFS_ECC_RESULT_UNFIXED ++}; ++ ++enum yaffs_obj_type { ++ YAFFS_OBJECT_TYPE_UNKNOWN, ++ YAFFS_OBJECT_TYPE_FILE, ++ YAFFS_OBJECT_TYPE_SYMLINK, ++ YAFFS_OBJECT_TYPE_DIRECTORY, ++ YAFFS_OBJECT_TYPE_HARDLINK, ++ YAFFS_OBJECT_TYPE_SPECIAL ++}; ++ ++#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL ++ ++struct yaffs_ext_tags { ++ unsigned chunk_used; /* Status of the chunk: used or unused */ ++ unsigned obj_id; /* If 0 this is not used */ ++ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ ++ unsigned n_bytes; /* Only valid for data chunks */ ++ ++ /* The following stuff only has meaning when we read */ ++ enum yaffs_ecc_result ecc_result; ++ unsigned block_bad; ++ ++ /* YAFFS 1 stuff */ ++ unsigned is_deleted; /* The chunk is marked deleted */ ++ unsigned serial_number; /* Yaffs1 2-bit serial number */ ++ ++ /* YAFFS2 stuff */ ++ unsigned seq_number; /* The sequence number of this block */ ++ ++ /* Extra info if this is an object header (YAFFS2 only) */ ++ ++ unsigned extra_available; /* Extra info available if not zero */ ++ unsigned extra_parent_id; /* The parent object */ ++ unsigned extra_is_shrink; /* Is it a shrink header? */ ++ unsigned extra_shadows; /* Does this shadow another object? */ ++ ++ enum yaffs_obj_type extra_obj_type; /* What object type? */ ++ ++ loff_t extra_file_size; /* Length if it is a file */ ++ unsigned extra_equiv_id; /* Equivalent object for a hard link */ ++}; ++ ++/* Spare structure for YAFFS1 */ ++struct yaffs_spare { ++ u8 tb0; ++ u8 tb1; ++ u8 tb2; ++ u8 tb3; ++ u8 page_status; /* set to 0 to delete the chunk */ ++ u8 block_status; ++ u8 tb4; ++ u8 tb5; ++ u8 ecc1[3]; ++ u8 tb6; ++ u8 tb7; ++ u8 ecc2[3]; ++}; ++ ++/*Special structure for passing through to mtd */ ++struct yaffs_nand_spare { ++ struct yaffs_spare spare; ++ int eccres1; ++ int eccres2; ++}; ++ ++/* Block data in RAM */ ++ ++enum yaffs_block_state { ++ YAFFS_BLOCK_STATE_UNKNOWN = 0, ++ ++ YAFFS_BLOCK_STATE_SCANNING, ++ /* Being scanned */ ++ ++ YAFFS_BLOCK_STATE_NEEDS_SCAN, ++ /* The block might have something on it (ie it is allocating or full, ++ * perhaps empty) but it needs to be scanned to determine its true ++ * state. ++ * This state is only valid during scanning. ++ * NB We tolerate empty because the pre-scanner might be incapable of ++ * deciding ++ * However, if this state is returned on a YAFFS2 device, ++ * then we expect a sequence number ++ */ ++ ++ YAFFS_BLOCK_STATE_EMPTY, ++ /* This block is empty */ ++ ++ YAFFS_BLOCK_STATE_ALLOCATING, ++ /* This block is partially allocated. ++ * At least one page holds valid data. ++ * This is the one currently being used for page ++ * allocation. Should never be more than one of these. ++ * If a block is only partially allocated at mount it is treated as ++ * full. ++ */ ++ ++ YAFFS_BLOCK_STATE_FULL, ++ /* All the pages in this block have been allocated. ++ * If a block was only partially allocated when mounted we treat ++ * it as fully allocated. ++ */ ++ ++ YAFFS_BLOCK_STATE_DIRTY, ++ /* The block was full and now all chunks have been deleted. ++ * Erase me, reuse me. ++ */ ++ ++ YAFFS_BLOCK_STATE_CHECKPOINT, ++ /* This block is assigned to holding checkpoint data. */ ++ ++ YAFFS_BLOCK_STATE_COLLECTING, ++ /* This block is being garbage collected */ ++ ++ YAFFS_BLOCK_STATE_DEAD ++ /* This block has failed and is not in use */ ++}; ++ ++#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) ++ ++struct yaffs_block_info { ++ ++ s32 soft_del_pages:10; /* number of soft deleted pages */ ++ s32 pages_in_use:10; /* number of pages in use */ ++ u32 block_state:4; /* One of the above block states. */ ++ /* NB use unsigned because enum is sometimes ++ * an int */ ++ u32 needs_retiring:1; /* Data has failed on this block, */ ++ /*need to get valid data off and retire*/ ++ u32 skip_erased_check:1;/* Skip the erased check on this block */ ++ u32 gc_prioritise:1; /* An ECC check or blank check has failed. ++ Block should be prioritised for GC */ ++ u32 chunk_error_strikes:3; /* How many times we've had ecc etc ++ failures on this block and tried to reuse it */ ++ u32 has_summary:1; /* The block has a summary */ ++ ++ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ ++ u32 seq_number; /* block sequence number for yaffs2 */ ++ ++}; ++ ++/* -------------------------- Object structure -------------------------------*/ ++/* This is the object structure as stored on NAND */ ++ ++struct yaffs_obj_hdr { ++ enum yaffs_obj_type type; ++ ++ /* Apply to everything */ ++ int parent_obj_id; ++ u16 sum_no_longer_used; /* checksum of name. No longer used */ ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ /* The following apply to all object types except for hard links */ ++ u32 yst_mode; /* protection */ ++ ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++ ++ /* File size applies to files only */ ++ u32 file_size_low; ++ ++ /* Equivalent object id applies to hard links only. */ ++ int equiv_id; ++ ++ /* Alias is for symlinks only. */ ++ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; ++ ++ u32 yst_rdev; /* stuff for block and char devices (major/min) */ ++ ++ u32 win_ctime[2]; ++ u32 win_atime[2]; ++ u32 win_mtime[2]; ++ ++ u32 inband_shadowed_obj_id; ++ u32 inband_is_shrink; ++ ++ u32 file_size_high; ++ u32 reserved[1]; ++ int shadows_obj; /* This object header shadows the ++ specified object if > 0 */ ++ ++ /* is_shrink applies to object headers written when wemake a hole. */ ++ u32 is_shrink; ++ ++}; ++ ++/*--------------------------- Tnode -------------------------- */ ++ ++struct yaffs_tnode { ++ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; ++}; ++ ++/*------------------------ Object -----------------------------*/ ++/* An object can be one of: ++ * - a directory (no data, has children links ++ * - a regular file (data.... not prunes :->). ++ * - a symlink [symbolic link] (the alias). ++ * - a hard link ++ */ ++ ++struct yaffs_file_var { ++ loff_t file_size; ++ loff_t scanned_size; ++ loff_t shrink_size; ++ int top_level; ++ struct yaffs_tnode *top; ++}; ++ ++struct yaffs_dir_var { ++ struct list_head children; /* list of child links */ ++ struct list_head dirty; /* Entry for list of dirty directories */ ++}; ++ ++struct yaffs_symlink_var { ++ YCHAR *alias; ++}; ++ ++struct yaffs_hardlink_var { ++ struct yaffs_obj *equiv_obj; ++ u32 equiv_id; ++}; ++ ++union yaffs_obj_var { ++ struct yaffs_file_var file_variant; ++ struct yaffs_dir_var dir_variant; ++ struct yaffs_symlink_var symlink_variant; ++ struct yaffs_hardlink_var hardlink_variant; ++}; ++ ++struct yaffs_obj { ++ u8 deleted:1; /* This should only apply to unlinked files. */ ++ u8 soft_del:1; /* it has also been soft deleted */ ++ u8 unlinked:1; /* An unlinked file.*/ ++ u8 fake:1; /* A fake object has no presence on NAND. */ ++ u8 rename_allowed:1; /* Some objects cannot be renamed. */ ++ u8 unlink_allowed:1; ++ u8 dirty:1; /* the object needs to be written to flash */ ++ u8 valid:1; /* When the file system is being loaded up, this ++ * object might be created before the data ++ * is available ++ * ie. file data chunks encountered before ++ * the header. ++ */ ++ u8 lazy_loaded:1; /* This object has been lazy loaded and ++ * is missing some detail */ ++ ++ u8 defered_free:1; /* Object is removed from NAND, but is ++ * still in the inode cache. ++ * Free of object is defered. ++ * until the inode is released. ++ */ ++ u8 being_created:1; /* This object is still being created ++ * so skip some verification checks. */ ++ u8 is_shadowed:1; /* This object is shadowed on the way ++ * to being renamed. */ ++ ++ u8 xattr_known:1; /* We know if this has object has xattribs ++ * or not. */ ++ u8 has_xattr:1; /* This object has xattribs. ++ * Only valid if xattr_known. */ ++ ++ u8 serial; /* serial number of chunk in NAND.*/ ++ u16 sum; /* sum of the name to speed searching */ ++ ++ struct yaffs_dev *my_dev; /* The device I'm on */ ++ ++ struct list_head hash_link; /* list of objects in hash bucket */ ++ ++ struct list_head hard_links; /* hard linked object chain*/ ++ ++ /* directory structure stuff */ ++ /* also used for linking up the free list */ ++ struct yaffs_obj *parent; ++ struct list_head siblings; ++ ++ /* Where's my object header in NAND? */ ++ int hdr_chunk; ++ ++ int n_data_chunks; /* Number of data chunks for this file. */ ++ ++ u32 obj_id; /* the object id value */ ++ ++ u32 yst_mode; ++ ++ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; ++ ++#ifdef CONFIG_YAFFS_WINCE ++ u32 win_ctime[2]; ++ u32 win_mtime[2]; ++ u32 win_atime[2]; ++#else ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++#endif ++ ++ u32 yst_rdev; ++ ++ void *my_inode; ++ ++ enum yaffs_obj_type variant_type; ++ ++ union yaffs_obj_var variant; ++ ++}; ++ ++struct yaffs_obj_bucket { ++ struct list_head list; ++ int count; ++}; ++ ++/* yaffs_checkpt_obj holds the definition of an object as dumped ++ * by checkpointing. ++ */ ++ ++struct yaffs_checkpt_obj { ++ int struct_type; ++ u32 obj_id; ++ u32 parent_id; ++ int hdr_chunk; ++ enum yaffs_obj_type variant_type:3; ++ u8 deleted:1; ++ u8 soft_del:1; ++ u8 unlinked:1; ++ u8 fake:1; ++ u8 rename_allowed:1; ++ u8 unlink_allowed:1; ++ u8 serial; ++ int n_data_chunks; ++ loff_t size_or_equiv_obj; ++}; ++ ++/*--------------------- Temporary buffers ---------------- ++ * ++ * These are chunk-sized working buffers. Each device has a few. ++ */ ++ ++struct yaffs_buffer { ++ u8 *buffer; ++ int in_use; ++}; ++ ++/*----------------- Device ---------------------------------*/ ++ ++struct yaffs_param { ++ const YCHAR *name; ++ ++ /* ++ * Entry parameters set up way early. Yaffs sets up the rest. ++ * The structure should be zeroed out before use so that unused ++ * and default values are zero. ++ */ ++ ++ int inband_tags; /* Use unband tags */ ++ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to ++ be a power of 2 */ ++ int chunks_per_block; /* does not need to be a power of 2 */ ++ int spare_bytes_per_chunk; /* spare area size */ ++ int start_block; /* Start block we're allowed to use */ ++ int end_block; /* End block we're allowed to use */ ++ int n_reserved_blocks; /* Tuneable so that we can reduce ++ * reserved blocks on NOR and RAM. */ ++ ++ int n_caches; /* If <= 0, then short op caching is disabled, ++ * else the number of short op caches. ++ */ ++ int cache_bypass_aligned; /* If non-zero then bypass the cache for ++ * aligned writes. ++ */ ++ ++ int use_nand_ecc; /* Flag to decide whether or not to use ++ * NAND driver ECC on data (yaffs1) */ ++ int tags_9bytes; /* Use 9 byte tags */ ++ int no_tags_ecc; /* Flag to decide whether or not to do ECC ++ * on packed tags (yaffs2) */ ++ ++ int is_yaffs2; /* Use yaffs2 mode on this device */ ++ ++ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ ++ ++ int refresh_period; /* How often to check for a block refresh */ ++ ++ /* Checkpoint control. Can be set before or after initialisation */ ++ u8 skip_checkpt_rd; ++ u8 skip_checkpt_wr; ++ ++ int enable_xattr; /* Enable xattribs */ ++ ++ int max_objects; /* ++ * Set to limit the number of objects created. ++ * 0 = no limit. ++ */ ++ ++ /* The remove_obj_fn function must be supplied by OS flavours that ++ * need it. ++ * yaffs direct uses it to implement the faster readdir. ++ * Linux uses it to protect the directory during unlocking. ++ */ ++ void (*remove_obj_fn) (struct yaffs_obj *obj); ++ ++ /* Callback to mark the superblock dirty */ ++ void (*sb_dirty_fn) (struct yaffs_dev *dev); ++ ++ /* Callback to control garbage collection. */ ++ unsigned (*gc_control_fn) (struct yaffs_dev *dev); ++ ++ /* Debug control flags. Don't use unless you know what you're doing */ ++ int use_header_file_size; /* Flag to determine if we should use ++ * file sizes from the header */ ++ int disable_lazy_load; /* Disable lazy loading on this device */ ++ int wide_tnodes_disabled; /* Set to disable wide tnodes */ ++ int disable_soft_del; /* yaffs 1 only: Set to disable the use of ++ * softdeletion. */ ++ ++ int defered_dir_update; /* Set to defer directory updates */ ++ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ int auto_unicode; ++#endif ++ int always_check_erased; /* Force chunk erased check always on */ ++ ++ int disable_summary; ++ int disable_bad_block_marking; ++ ++}; ++ ++struct yaffs_driver { ++ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len); ++ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result); ++ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_initialise_fn) (struct yaffs_dev *dev); ++ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); ++}; ++ ++struct yaffs_tags_handler { ++ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags); ++ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags); ++ ++ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++}; ++ ++struct yaffs_dev { ++ struct yaffs_param param; ++ struct yaffs_driver drv; ++ struct yaffs_tags_handler tagger; ++ ++ /* Context storage. Holds extra OS specific data for this device */ ++ ++ void *os_context; ++ void *driver_context; ++ ++ struct list_head dev_list; ++ ++ int ll_init; ++ /* Runtime parameters. Set up by YAFFS. */ ++ int data_bytes_per_chunk; ++ ++ /* Non-wide tnode stuff */ ++ u16 chunk_grp_bits; /* Number of bits that need to be resolved if ++ * the tnodes are not wide enough. ++ */ ++ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ ++ ++ /* Stuff to support wide tnodes */ ++ u32 tnode_width; ++ u32 tnode_mask; ++ u32 tnode_size; ++ ++ /* Stuff for figuring out file offset to chunk conversions */ ++ u32 chunk_shift; /* Shift value */ ++ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ ++ u32 chunk_mask; /* Mask to use for power-of-2 case */ ++ ++ int is_mounted; ++ int read_only; ++ int is_checkpointed; ++ ++ /* Stuff to support block offsetting to support start block zero */ ++ int internal_start_block; ++ int internal_end_block; ++ int block_offset; ++ int chunk_offset; ++ ++ /* Runtime checkpointing stuff */ ++ int checkpt_page_seq; /* running sequence number of checkpt pages */ ++ int checkpt_byte_count; ++ int checkpt_byte_offs; ++ u8 *checkpt_buffer; ++ int checkpt_open_write; ++ int blocks_in_checkpt; ++ int checkpt_cur_chunk; ++ int checkpt_cur_block; ++ int checkpt_next_block; ++ int *checkpt_block_list; ++ int checkpt_max_blocks; ++ u32 checkpt_sum; ++ u32 checkpt_xor; ++ ++ int checkpoint_blocks_required; /* Number of blocks needed to store ++ * current checkpoint set */ ++ ++ /* Block Info */ ++ struct yaffs_block_info *block_info; ++ u8 *chunk_bits; /* bitmap of chunks in use */ ++ u8 block_info_alt:1; /* allocated using alternative alloc */ ++ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ ++ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. ++ * Must be consistent with chunks_per_block. ++ */ ++ ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int alloc_block_finder; /* Used to search for next allocation block */ ++ ++ /* Object and Tnode memory management */ ++ void *allocator; ++ int n_obj; ++ int n_tnodes; ++ ++ int n_hardlinks; ++ ++ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; ++ u32 bucket_finder; ++ ++ int n_free_chunks; ++ ++ /* Garbage collection control */ ++ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ ++ u32 n_clean_ups; ++ ++ unsigned has_pending_prioritised_gc; /* We think this device might ++ have pending prioritised gcs */ ++ unsigned gc_disable; ++ unsigned gc_block_finder; ++ unsigned gc_dirtiest; ++ unsigned gc_pages_in_use; ++ unsigned gc_not_done; ++ unsigned gc_block; ++ unsigned gc_chunk; ++ unsigned gc_skip; ++ struct yaffs_summary_tags *gc_sum_tags; ++ ++ /* Special directories */ ++ struct yaffs_obj *root_dir; ++ struct yaffs_obj *lost_n_found; ++ ++ int buffered_block; /* Which block is buffered here? */ ++ int doing_buffered_block_rewrite; ++ ++ struct yaffs_cache *cache; ++ int cache_last_use; ++ ++ /* Stuff for background deletion and unlinked files. */ ++ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted ++ files live. */ ++ struct yaffs_obj *del_dir; /* Directory where deleted objects are ++ sent to disappear. */ ++ struct yaffs_obj *unlinked_deletion; /* Current file being ++ background deleted. */ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* Temporary buffer management */ ++ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; ++ int max_temp; ++ int temp_in_use; ++ int unmanaged_buffer_allocs; ++ int unmanaged_buffer_deallocs; ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ allocating block */ ++ unsigned oldest_dirty_seq; ++ unsigned oldest_dirty_block; ++ ++ /* Block refreshing */ ++ int refresh_skip; /* A skip down counter. ++ * Refresh happens when this gets to zero. */ ++ ++ /* Dirty directory handling */ ++ struct list_head dirty_dirs; /* List of dirty directories */ ++ ++ /* Summary */ ++ int chunks_per_summary; ++ struct yaffs_summary_tags *sum_tags; ++ ++ /* Statistics */ ++ u32 n_page_writes; ++ u32 n_page_reads; ++ u32 n_erasures; ++ u32 n_bad_queries; ++ u32 n_bad_markings; ++ u32 n_erase_failures; ++ u32 n_gc_copies; ++ u32 all_gcs; ++ u32 passive_gc_count; ++ u32 oldest_dirty_gc_count; ++ u32 n_gc_blocks; ++ u32 bg_gcs; ++ u32 n_retried_writes; ++ u32 n_retired_blocks; ++ u32 n_ecc_fixed; ++ u32 n_ecc_unfixed; ++ u32 n_tags_ecc_fixed; ++ u32 n_tags_ecc_unfixed; ++ u32 n_deletions; ++ u32 n_unmarked_deletions; ++ u32 refresh_count; ++ u32 cache_hits; ++ u32 tags_used; ++ u32 summary_used; ++ ++}; ++ ++/* The CheckpointDevice structure holds the device information that changes ++ *at runtime and must be preserved over unmount/mount cycles. ++ */ ++struct yaffs_checkpt_dev { ++ int struct_type; ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int n_free_chunks; ++ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ * allocating block */ ++ ++}; ++ ++struct yaffs_checkpt_validity { ++ int struct_type; ++ u32 magic; ++ u32 version; ++ u32 head; ++}; ++ ++struct yaffs_shadow_fixer { ++ int obj_id; ++ int shadowed_id; ++ struct yaffs_shadow_fixer *next; ++}; ++ ++/* Structure for doing xattr modifications */ ++struct yaffs_xattr_mod { ++ int set; /* If 0 then this is a deletion */ ++ const YCHAR *name; ++ const void *data; ++ int size; ++ int flags; ++ int result; ++}; ++ ++/*----------------------- YAFFS Functions -----------------------*/ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev); ++void yaffs_deinitialise(struct yaffs_dev *dev); ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, ++ struct yaffs_obj *new_dir, const YCHAR * new_name); ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); ++int yaffs_del_obj(struct yaffs_obj *obj); ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type); ++ ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj); ++int yaffs_get_obj_inode(struct yaffs_obj *obj); ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj); ++int yaffs_get_obj_link_count(struct yaffs_obj *obj); ++ ++/* File operations */ ++int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, ++ int n_bytes); ++int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid); ++ ++int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync); ++ ++/* Flushing and checkpointing */ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev); ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev); ++int yaffs_checkpoint_restore(struct yaffs_dev *dev); ++ ++/* Directory operations */ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid); ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, ++ const YCHAR *name); ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); ++ ++/* Link operations */ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, ++ struct yaffs_obj *equiv_obj); ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); ++ ++/* Symlink operations */ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias); ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); ++ ++/* Special inodes (fifos, sockets and devices) */ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev); ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, ++ const void *value, int size, int flags); ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, ++ int size); ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); ++ ++/* Special directories */ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj); ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev); ++ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); ++ ++/* Debug dump */ ++int yaffs_dump_obj(struct yaffs_obj *obj); ++ ++void yaffs_guts_test(struct yaffs_dev *dev); ++int yaffs_guts_ll_init(struct yaffs_dev *dev); ++ ++ ++/* A few useful functions to be used within the core files*/ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn); ++int yaffs_check_ff(u8 *buffer, int n_bytes); ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type); ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan); ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh); ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); ++YCHAR *yaffs_clone_str(const YCHAR *str); ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, ++ int force, int is_shrink, int shadows, ++ struct yaffs_xattr_mod *xop); ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning); ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn); ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev); ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id); ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos); ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj); ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev); ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out); ++/* ++ * Marshalling functions to get loff_t file sizes into aand out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); ++loff_t yaffs_max_file_size(struct yaffs_dev *dev); ++ ++/* ++ * Debug function to count number of blocks in each state ++ * NB Needs to be called with correct number of integers ++ */ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_linux.h linux-3.10.40/fs/yaffs2/yaffs_linux.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_linux.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,48 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_LINUX_H__ ++#define __YAFFS_LINUX_H__ ++ ++#include "yportenv.h" ++ ++struct yaffs_linux_context { ++ struct list_head context_list; /* List of these we have mounted */ ++ struct yaffs_dev *dev; ++ struct super_block *super; ++ struct task_struct *bg_thread; /* Background thread for this device */ ++ int bg_running; ++ struct mutex gross_lock; /* Gross locking mutex*/ ++ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size ++ * at compile time so we have to allocate it. ++ */ ++ struct list_head search_contexts; ++ struct task_struct *readdir_process; ++ unsigned mount_id; ++ int dirty; ++}; ++ ++#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) ++#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++#define WRITE_SIZE_STR "writesize" ++#define WRITE_SIZE(mtd) ((mtd)->writesize) ++#else ++#define WRITE_SIZE_STR "oobblock" ++#define WRITE_SIZE(mtd) ((mtd)->oobblock) ++#endif ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_mtdif.c linux-3.10.40/fs/yaffs2/yaffs_mtdif.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_mtdif.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,309 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_mtdif.h" ++ ++#include "linux/mtd/mtd.h" ++#include "linux/types.h" ++#include "linux/time.h" ++#include "linux/major.h" ++#include "linux/mtd/nand.h" ++#include "linux/kernel.h" ++#include "linux/version.h" ++#include "linux/types.h" ++ ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_linux.h" ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++#define mtd_erase(m, ei) (m)->erase(m, ei) ++#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) ++#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) ++#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) ++#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) ++#endif ++ ++ ++ ++int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ u32 addr = ++ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ struct erase_info ei; ++ int retval = 0; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++ ++static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", ++ dev, nand_chunk, data, data_len, oob, oob_len); ++ ++ if (!data || !data_len) { ++ data = NULL; ++ data_len = 0; ++ } ++ ++ if (!oob || !oob_len) { ++ oob = NULL; ++ oob_len = 0; ++ } ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = (u8 *)data; ++ ops.oobbuf = (u8 *)oob; ++ ++ retval = mtd_write_oob(mtd, addr, &ops); ++ if (retval) { ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "write_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ } ++ return retval ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = data; ++ ops.oobbuf = oob; ++ ++#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) ++ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; ++ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. ++ */ ++ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; ++#endif ++ /* Read page and oob using MTD. ++ * Check status and determine ECC result. ++ */ ++ retval = mtd_read_oob(mtd, addr, &ops); ++ if (retval) ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "read_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ ++ switch (retval) { ++ case 0: ++ /* no error */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ ++ case -EUCLEAN: ++ /* MTD's ECC fixed the data */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ break; ++ ++ case -EBADMSG: ++ default: ++ /* MTD's ECC could not fix the data */ ++ dev->n_ecc_unfixed++; ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ loff_t addr; ++ struct erase_info ei; ++ int retval = 0; ++ u32 block_size; ++ ++ block_size = dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ addr = ((loff_t) block_no) * block_size; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = block_size; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); ++ ++ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); ++ ++ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_initialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ ++ drv->drv_write_chunk_fn = yaffs_mtd_write; ++ drv->drv_read_chunk_fn = yaffs_mtd_read; ++ drv->drv_erase_fn = yaffs_mtd_erase; ++ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; ++ drv->drv_check_bad_fn = yaffs_mtd_check_bad; ++ drv->drv_initialise_fn = yaffs_mtd_initialise; ++ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; ++} ++ ++ ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev) ++{ ++ struct mtd_info *mtd; ++ ++ mtd = yaffs_get_mtd_device(sdev); ++ ++ /* Check it's an mtd device..... */ ++ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) ++ return NULL; /* This isn't an mtd device */ ++ ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device is not NAND it's type %d", ++ mtd->type); ++ return NULL; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); ++ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); ++ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); ++#else ++ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); ++#endif ++ ++ return mtd; ++} ++ ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) ++{ ++ if (yaffs_version == 2) { ++ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || ++ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && ++ !inband_tags) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not have the right page sizes" ++ ); ++ return -1; ++ } ++ } else { ++ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || ++ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support have the right page sizes" ++ ); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++void yaffs_put_mtd_device(struct mtd_info *mtd) ++{ ++ if(mtd) ++ put_mtd_device(mtd); ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_mtdif.h linux-3.10.40/fs/yaffs2/yaffs_mtdif.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_mtdif.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,25 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_MTDIF_H__ ++#define __YAFFS_MTDIF_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev); ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev); ++void yaffs_put_mtd_device(struct mtd_info *mtd); ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_nameval.c linux-3.10.40/fs/yaffs2/yaffs_nameval.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_nameval.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,208 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This simple implementation of a name-value store assumes a small number of ++* values and fits into a small finite buffer. ++ * ++ * Each attribute is stored as a record: ++ * sizeof(int) bytes record size. ++ * strnlen+1 bytes name null terminated. ++ * nbytes value. ++ * ---------- ++ * total size stored in record size ++ * ++ * This code has not been tested with unicode yet. ++ */ ++ ++#include "yaffs_nameval.h" ++ ++#include "yportenv.h" ++ ++static int nval_find(const char *xb, int xb_size, const YCHAR *name, ++ int *exist_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), ++ name, size)) { ++ if (exist_size) ++ *exist_size = size; ++ return pos; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ if (exist_size) ++ *exist_size = 0; ++ return -ENODATA; ++} ++ ++static int nval_used(const char *xb, int xb_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return pos; ++} ++ ++int nval_del(char *xb, int xb_size, const YCHAR *name) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos < 0 || pos >= xb_size) ++ return -ENODATA; ++ ++ /* Find size, shift rest over this record, ++ * then zero out the rest of buffer */ ++ memcpy(&size, xb + pos, sizeof(int)); ++ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); ++ memset(xb + (xb_size - size), 0, size); ++ return 0; ++} ++ ++int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, ++ int bsize, int flags) ++{ ++ int pos; ++ int namelen = strnlen(name, xb_size); ++ int reclen; ++ int size_exist = 0; ++ int space; ++ int start; ++ ++ pos = nval_find(xb, xb_size, name, &size_exist); ++ ++ if (flags & XATTR_CREATE && pos >= 0) ++ return -EEXIST; ++ if (flags & XATTR_REPLACE && pos < 0) ++ return -ENODATA; ++ ++ start = nval_used(xb, xb_size); ++ space = xb_size - start + size_exist; ++ ++ reclen = (sizeof(int) + namelen + 1 + bsize); ++ ++ if (reclen > space) ++ return -ENOSPC; ++ ++ if (pos >= 0) { ++ nval_del(xb, xb_size, name); ++ start = nval_used(xb, xb_size); ++ } ++ ++ pos = start; ++ ++ memcpy(xb + pos, &reclen, sizeof(int)); ++ pos += sizeof(int); ++ strncpy((YCHAR *) (xb + pos), name, reclen); ++ pos += (namelen + 1); ++ memcpy(xb + pos, buf, bsize); ++ return 0; ++} ++ ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos >= 0 && pos < xb_size) { ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ pos += sizeof(int); /* advance past record length */ ++ size -= sizeof(int); ++ ++ /* Advance over name string */ ++ while (xb[pos] && size > 0 && pos < xb_size) { ++ pos++; ++ size--; ++ } ++ /*Advance over NUL */ ++ pos++; ++ size--; ++ ++ /* If bsize is zero then this is a size query. ++ * Return the size, but don't copy. ++ */ ++ if (!bsize) ++ return size; ++ ++ if (size <= bsize) { ++ memcpy(buf, xb + pos, size); ++ return size; ++ } ++ } ++ if (pos >= 0) ++ return -ERANGE; ++ ++ return -ENODATA; ++} ++ ++int nval_list(const char *xb, int xb_size, char *buf, int bsize) ++{ ++ int pos = 0; ++ int size; ++ int name_len; ++ int ncopied = 0; ++ int filled = 0; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > sizeof(int) && ++ size <= xb_size && ++ (pos + size) < xb_size && ++ !filled) { ++ pos += sizeof(int); ++ size -= sizeof(int); ++ name_len = strnlen((YCHAR *) (xb + pos), size); ++ if (ncopied + name_len + 1 < bsize) { ++ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); ++ buf += name_len; ++ *buf = '\0'; ++ buf++; ++ if (sizeof(YCHAR) > 1) { ++ *buf = '\0'; ++ buf++; ++ } ++ ncopied += (name_len + 1); ++ } else { ++ filled = 1; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return ncopied; ++} ++ ++int nval_hasvalues(const char *xb, int xb_size) ++{ ++ return nval_used(xb, xb_size) > 0; ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_nameval.h linux-3.10.40/fs/yaffs2/yaffs_nameval.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_nameval.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __NAMEVAL_H__ ++#define __NAMEVAL_H__ ++ ++#include "yportenv.h" ++ ++int nval_del(char *xb, int xb_size, const YCHAR * name); ++int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, ++ int bsize, int flags); ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize); ++int nval_list(const char *xb, int xb_size, char *buf, int bsize); ++int nval_hasvalues(const char *xb, int xb_size); ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_nand.c linux-3.10.40/fs/yaffs2/yaffs_nand.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_nand.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,122 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_nand.h" ++#include "yaffs_tagscompat.h" ++ ++#include "yaffs_getblockinfo.h" ++#include "yaffs_summary.h" ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ struct yaffs_ext_tags local_tags; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_reads++; ++ ++ /* If there are no tags provided use local tags. */ ++ if (!tags) ++ tags = &local_tags; ++ ++ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); ++ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { ++ ++ struct yaffs_block_info *bi; ++ bi = yaffs_get_block_info(dev, ++ nand_chunk / ++ dev->param.chunks_per_block); ++ yaffs_handle_chunk_error(dev, bi); ++ } ++ return result; ++} ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_writes++; ++ ++ if (!tags) { ++ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ tags->seq_number = dev->seq_number; ++ tags->chunk_used = 1; ++ yaffs_trace(YAFFS_TRACE_WRITE, ++ "Writing chunk %d tags %d %d", ++ nand_chunk, tags->obj_id, tags->chunk_id); ++ ++ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, ++ buffer, tags); ++ ++ yaffs_summary_add(dev, tags, nand_chunk); ++ ++ return result; ++} ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ block_no -= dev->block_offset; ++ dev->n_bad_markings++; ++ ++ if (dev->param.disable_bad_block_marking) ++ return YAFFS_OK; ++ ++ return dev->tagger.mark_bad_fn(dev, block_no); ++} ++ ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ block_no -= dev->block_offset; ++ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); ++} ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ int result; ++ ++ block_no -= dev->block_offset; ++ dev->n_erasures++; ++ result = dev->drv.drv_erase_fn(dev, block_no); ++ return result; ++} ++ ++int yaffs_init_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_initialise_fn) ++ return dev->drv.drv_initialise_fn(dev); ++ return YAFFS_OK; ++} ++ ++int yaffs_deinit_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_deinitialise_fn) ++ return dev->drv.drv_deinitialise_fn(dev); ++ return YAFFS_OK; ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_nand.h linux-3.10.40/fs/yaffs2/yaffs_nand.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_nand.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_NAND_H__ ++#define __YAFFS_NAND_H__ ++#include "yaffs_guts.h" ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ unsigned *seq_number); ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); ++ ++int yaffs_init_nand(struct yaffs_dev *dev); ++int yaffs_deinit_nand(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.10.40/fs/yaffs2/yaffs_packedtags1.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,56 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags1.h" ++#include "yportenv.h" ++ ++static const u8 all_ff[20] = { ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t) ++{ ++ pt->chunk_id = t->chunk_id; ++ pt->serial_number = t->serial_number; ++ pt->n_bytes = t->n_bytes; ++ pt->obj_id = t->obj_id; ++ pt->ecc = 0; ++ pt->deleted = (t->is_deleted) ? 0 : 1; ++ pt->unused_stuff = 0; ++ pt->should_be_ff = 0xffffffff; ++} ++ ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt) ++{ ++ ++ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { ++ t->block_bad = 0; ++ if (pt->should_be_ff != 0xffffffff) ++ t->block_bad = 1; ++ t->chunk_used = 1; ++ t->obj_id = pt->obj_id; ++ t->chunk_id = pt->chunk_id; ++ t->n_bytes = pt->n_bytes; ++ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ t->is_deleted = (pt->deleted) ? 0 : 1; ++ t->serial_number = pt->serial_number; ++ } else { ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ } ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.10.40/fs/yaffs2/yaffs_packedtags1.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS1_H__ ++#define __YAFFS_PACKEDTAGS1_H__ ++ ++#include "yaffs_guts.h" ++ ++struct yaffs_packed_tags1 { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 deleted:1; ++ u32 unused_stuff:1; ++ unsigned should_be_ff; ++ ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt); ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.10.40/fs/yaffs2/yaffs_packedtags2.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,197 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags2.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++/* This code packs a set of extended tags into a binary structure for ++ * NAND storage ++ */ ++ ++/* Some of the information is "extra" struff which can be packed in to ++ * speed scanning ++ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. ++ */ ++ ++/* Extra flags applied to chunk_id */ ++ ++#define EXTRA_HEADER_INFO_FLAG 0x80000000 ++#define EXTRA_SHRINK_FLAG 0x40000000 ++#define EXTRA_SHADOWS_FLAG 0x20000000 ++#define EXTRA_SPARE_FLAGS 0x10000000 ++ ++#define ALL_EXTRA_FLAGS 0xf0000000 ++ ++/* Also, the top 4 bits of the object Id are set to the object type. */ ++#define EXTRA_OBJECT_TYPE_SHIFT (28) ++#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) ++ ++static void yaffs_dump_packed_tags2_tags_only( ++ const struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "packed tags obj %d chunk %d byte %d seq %d", ++ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); ++} ++ ++static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) ++{ ++ yaffs_dump_packed_tags2_tags_only(&pt->t); ++} ++ ++static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", ++ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, ++ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, ++ t->seq_number); ++ ++} ++ ++static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) ++{ ++ if (t->chunk_id != 0 || !t->extra_available) ++ return 0; ++ ++ /* Check if the file size is too long to store */ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && ++ (t->extra_file_size >> 31) != 0) ++ return 0; ++ return 1; ++} ++ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, ++ const struct yaffs_ext_tags *t) ++{ ++ ptt->chunk_id = t->chunk_id; ++ ptt->seq_number = t->seq_number; ++ ptt->n_bytes = t->n_bytes; ++ ptt->obj_id = t->obj_id; ++ ++ /* Only store extra tags for object headers. ++ * If it is a file then only store if the file size is short\ ++ * enough to fit. ++ */ ++ if (yaffs_check_tags_extra_packable(t)) { ++ /* Store the extra header info instead */ ++ /* We save the parent object in the chunk_id */ ++ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; ++ if (t->extra_is_shrink) ++ ptt->chunk_id |= EXTRA_SHRINK_FLAG; ++ if (t->extra_shadows) ++ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; ++ ++ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ ptt->n_bytes = t->extra_equiv_id; ++ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ ptt->n_bytes = (unsigned) t->extra_file_size; ++ else ++ ptt->n_bytes = 0; ++ } ++ ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc) ++{ ++ yaffs_pack_tags2_tags_only(&pt->t, t); ++ ++ if (tags_ecc) ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc); ++} ++ ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ ++ if (ptt->seq_number == 0xffffffff) ++ return; ++ ++ t->block_bad = 0; ++ t->chunk_used = 1; ++ t->obj_id = ptt->obj_id; ++ t->chunk_id = ptt->chunk_id; ++ t->n_bytes = ptt->n_bytes; ++ t->is_deleted = 0; ++ t->serial_number = 0; ++ t->seq_number = ptt->seq_number; ++ ++ /* Do extra header info stuff */ ++ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { ++ t->chunk_id = 0; ++ t->n_bytes = 0; ++ ++ t->extra_available = 1; ++ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); ++ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; ++ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; ++ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; ++ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ t->extra_equiv_id = ptt->n_bytes; ++ else ++ t->extra_file_size = ptt->n_bytes; ++ } ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc) ++{ ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ if (pt->t.seq_number != 0xffffffff && tags_ecc) { ++ /* Chunk is in use and we need to do ECC */ ++ ++ struct yaffs_ecc_other ecc; ++ int result; ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &ecc); ++ result = ++ yaffs_ecc_correct_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc, &ecc); ++ switch (result) { ++ case 0: ++ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ case 1: ++ ecc_result = YAFFS_ECC_RESULT_FIXED; ++ break; ++ case -1: ++ ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ break; ++ default: ++ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ } ++ } ++ yaffs_unpack_tags2_tags_only(t, &pt->t); ++ ++ t->ecc_result = ecc_result; ++ ++ yaffs_dump_packed_tags2(pt); ++ yaffs_dump_tags2(t); ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.10.40/fs/yaffs2/yaffs_packedtags2.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,47 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS2_H__ ++#define __YAFFS_PACKEDTAGS2_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_ecc.h" ++ ++struct yaffs_packed_tags2_tags_only { ++ unsigned seq_number; ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++struct yaffs_packed_tags2 { ++ struct yaffs_packed_tags2_tags_only t; ++ struct yaffs_ecc_other ecc; ++}; ++ ++/* Full packed tags with ECC, used for oob tags */ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc); ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc); ++ ++/* Only the tags part (no ECC for use with inband tags */ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *pt); ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_summary.c linux-3.10.40/fs/yaffs2/yaffs_summary.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_summary.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,312 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Summaries write the useful part of the tags for the chunks in a block into an ++ * an array which is written to the last n chunks of the block. ++ * Reading the summaries gives all the tags for the block in one read. Much ++ * faster. ++ * ++ * Chunks holding summaries are marked with tags making it look like ++ * they are part of a fake file. ++ * ++ * The summary could also be used during gc. ++ * ++ */ ++ ++#include "yaffs_summary.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_bitmap.h" ++ ++/* ++ * The summary is built up in an array of summary tags. ++ * This gets written to the last one or two (maybe more) chunks in a block. ++ * A summary header is written as the first part of each chunk of summary data. ++ * The summary header must match or the summary is rejected. ++ */ ++ ++/* Summary tags don't need the sequence number because that is redundant. */ ++struct yaffs_summary_tags { ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++/* Summary header */ ++struct yaffs_summary_header { ++ unsigned version; /* Must match current version */ ++ unsigned block; /* Must be this block */ ++ unsigned seq; /* Must be this sequence number */ ++ unsigned sum; /* Just add up all the bytes in the tags */ ++}; ++ ++ ++static void yaffs_summary_clear(struct yaffs_dev *dev) ++{ ++ if (!dev->sum_tags) ++ return; ++ memset(dev->sum_tags, 0, dev->chunks_per_summary * ++ sizeof(struct yaffs_summary_tags)); ++} ++ ++ ++void yaffs_summary_deinit(struct yaffs_dev *dev) ++{ ++ kfree(dev->sum_tags); ++ dev->sum_tags = NULL; ++ kfree(dev->gc_sum_tags); ++ dev->gc_sum_tags = NULL; ++ dev->chunks_per_summary = 0; ++} ++ ++int yaffs_summary_init(struct yaffs_dev *dev) ++{ ++ int sum_bytes; ++ int chunks_used; /* Number of chunks used by summary */ ++ int sum_tags_bytes; ++ ++ sum_bytes = dev->param.chunks_per_block * ++ sizeof(struct yaffs_summary_tags); ++ ++ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ ++ (dev->data_bytes_per_chunk - ++ sizeof(struct yaffs_summary_header)); ++ ++ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ if (!dev->sum_tags || !dev->gc_sum_tags) { ++ yaffs_summary_deinit(dev); ++ return YAFFS_FAIL; ++ } ++ ++ yaffs_summary_clear(dev); ++ ++ return YAFFS_OK; ++} ++ ++static unsigned yaffs_summary_sum(struct yaffs_dev *dev) ++{ ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int i; ++ unsigned sum = 0; ++ ++ i = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ while (i > 0) { ++ sum += *sum_buffer; ++ sum_buffer++; ++ i--; ++ } ++ ++ return sum; ++} ++ ++static int yaffs_summary_write(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int n_bytes; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); ++ tags.obj_id = YAFFS_OBJECTID_SUMMARY; ++ tags.chunk_id = 1; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ hdr.version = YAFFS_SUMMARY_VERSION; ++ hdr.block = blk; ++ hdr.seq = bi->seq_number; ++ hdr.sum = yaffs_summary_sum(dev); ++ ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ memcpy(buffer, &hdr, sizeof(hdr)); ++ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); ++ tags.n_bytes = this_tx + sizeof(hdr); ++ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ break; ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ dev->n_free_chunks--; ++ ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ tags.chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ ++ if (result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ ++ return result; ++} ++ ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)st; ++ int n_bytes; ++ int chunk_id; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ int sum_tags_bytes; ++ ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = blk * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ chunk_id = 1; ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (tags.chunk_id != chunk_id || ++ tags.obj_id != YAFFS_OBJECTID_SUMMARY || ++ tags.chunk_used == 0 || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.n_bytes != (this_tx + sizeof(hdr))) ++ result = YAFFS_FAIL; ++ if (result != YAFFS_OK) ++ break; ++ ++ if (st == dev->sum_tags) { ++ /* If we're scanning then update the block info */ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ } ++ memcpy(&hdr, buffer, sizeof(hdr)); ++ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (result == YAFFS_OK) { ++ /* Verify header */ ++ if (hdr.version != YAFFS_SUMMARY_VERSION || ++ hdr.seq != bi->seq_number || ++ hdr.sum != yaffs_summary_sum(dev)) ++ result = YAFFS_FAIL; ++ } ++ ++ if (st == dev->sum_tags && result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ return result; ++} ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_nand) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; ++ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; ++ ++ if (!dev->sum_tags) ++ return YAFFS_OK; ++ ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ yaffs_pack_tags2_tags_only(&tags_only, tags); ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ sum_tags->chunk_id = tags_only.chunk_id; ++ sum_tags->n_bytes = tags_only.n_bytes; ++ sum_tags->obj_id = tags_only.obj_id; ++ ++ if (chunk_in_block == dev->chunks_per_summary - 1) { ++ /* Time to write out the summary */ ++ yaffs_summary_write(dev, block_in_nand); ++ yaffs_summary_clear(dev); ++ yaffs_skip_rest_of_block(dev); ++ } ++ } ++ return YAFFS_OK; ++} ++ ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ tags_only.chunk_id = sum_tags->chunk_id; ++ tags_only.n_bytes = sum_tags->n_bytes; ++ tags_only.obj_id = sum_tags->obj_id; ++ yaffs_unpack_tags2_tags_only(tags, &tags_only); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int i; ++ ++ if (!bi->has_summary) ++ return; ++ ++ for (i = dev->chunks_per_summary; ++ i < dev->param.chunks_per_block; ++ i++) { ++ if (yaffs_check_chunk_bit(dev, blk, i)) { ++ yaffs_clear_chunk_bit(dev, blk, i); ++ bi->pages_in_use--; ++ dev->n_free_chunks++; ++ } ++ } ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_summary.h linux-3.10.40/fs/yaffs2/yaffs_summary.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_summary.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,37 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_SUMMARY_H__ ++#define __YAFFS_SUMMARY_H__ ++ ++#include "yaffs_packedtags2.h" ++ ++ ++int yaffs_summary_init(struct yaffs_dev *dev); ++void yaffs_summary_deinit(struct yaffs_dev *dev); ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk); ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk); ++ ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.10.40/fs/yaffs2/yaffs_tagscompat.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,381 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_ecc.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_trace.h" ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); ++ ++ ++/********** Tags ECC calculations *********/ ++ ++ ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags) ++{ ++ /* Calculate an ecc */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ unsigned i, j; ++ unsigned ecc = 0; ++ unsigned bit = 0; ++ ++ tags->ecc = 0; ++ ++ for (i = 0; i < 8; i++) { ++ for (j = 1; j & 0xff; j <<= 1) { ++ bit++; ++ if (b[i] & j) ++ ecc ^= bit; ++ } ++ } ++ tags->ecc = ecc; ++} ++ ++int yaffs_check_tags_ecc(struct yaffs_tags *tags) ++{ ++ unsigned ecc = tags->ecc; ++ ++ yaffs_calc_tags_ecc(tags); ++ ++ ecc ^= tags->ecc; ++ ++ if (ecc && ecc <= 64) { ++ /* TODO: Handle the failure better. Retire? */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ ++ ecc--; ++ ++ b[ecc / 8] ^= (1 << (ecc & 7)); ++ ++ /* Now recvalc the ecc */ ++ yaffs_calc_tags_ecc(tags); ++ ++ return 1; /* recovered error */ ++ } else if (ecc) { ++ /* Wierd ecc failure value */ ++ /* TODO Need to do somethiong here */ ++ return -1; /* unrecovered error */ ++ } ++ return 0; ++} ++ ++/********** Tags **********/ ++ ++static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ ++ yaffs_calc_tags_ecc(tags_ptr); ++ ++ spare_ptr->tb0 = tu->as_bytes[0]; ++ spare_ptr->tb1 = tu->as_bytes[1]; ++ spare_ptr->tb2 = tu->as_bytes[2]; ++ spare_ptr->tb3 = tu->as_bytes[3]; ++ spare_ptr->tb4 = tu->as_bytes[4]; ++ spare_ptr->tb5 = tu->as_bytes[5]; ++ spare_ptr->tb6 = tu->as_bytes[6]; ++ spare_ptr->tb7 = tu->as_bytes[7]; ++} ++ ++static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, ++ struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ int result; ++ ++ tu->as_bytes[0] = spare_ptr->tb0; ++ tu->as_bytes[1] = spare_ptr->tb1; ++ tu->as_bytes[2] = spare_ptr->tb2; ++ tu->as_bytes[3] = spare_ptr->tb3; ++ tu->as_bytes[4] = spare_ptr->tb4; ++ tu->as_bytes[5] = spare_ptr->tb5; ++ tu->as_bytes[6] = spare_ptr->tb6; ++ tu->as_bytes[7] = spare_ptr->tb7; ++ ++ result = yaffs_check_tags_ecc(tags_ptr); ++ if (result > 0) ++ dev->n_tags_ecc_fixed++; ++ else if (result < 0) ++ dev->n_tags_ecc_unfixed++; ++} ++ ++static void yaffs_spare_init(struct yaffs_spare *spare) ++{ ++ memset(spare, 0xff, sizeof(struct yaffs_spare)); ++} ++ ++static int yaffs_wr_nand(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ struct yaffs_spare *spare) ++{ ++ int data_size = dev->data_bytes_per_chunk; ++ ++ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, sizeof(*spare)); ++} ++ ++static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, ++ struct yaffs_spare *spare, ++ enum yaffs_ecc_result *ecc_result, ++ int correct_errors) ++{ ++ int ret_val; ++ struct yaffs_spare local_spare; ++ int data_size; ++ int spare_size; ++ int ecc_result1, ecc_result2; ++ u8 calc_ecc[3]; ++ ++ if (!spare) { ++ /* If we don't have a real spare, then we use a local one. */ ++ /* Need this for the calculation of the ecc */ ++ spare = &local_spare; ++ } ++ data_size = dev->data_bytes_per_chunk; ++ spare_size = sizeof(struct yaffs_spare); ++ ++ if (dev->param.use_nand_ecc) ++ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, spare_size, ++ ecc_result); ++ ++ ++ /* Handle the ECC at this level. */ ++ ++ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *)spare, spare_size, ++ NULL); ++ if (!data || !correct_errors) ++ return ret_val; ++ ++ /* Do ECC correction if needed. */ ++ yaffs_ecc_calc(data, calc_ecc); ++ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); ++ yaffs_ecc_calc(&data[256], calc_ecc); ++ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); ++ ++ if (ecc_result1 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result1 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result2 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result2 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result1 || ecc_result2) { ++ /* We had a data problem on this page */ ++ yaffs_handle_rd_data_error(dev, nand_chunk); ++ } ++ ++ if (ecc_result1 < 0 || ecc_result2 < 0) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ else if (ecc_result1 > 0 || ecc_result2 > 0) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ else ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ return ret_val; ++} ++ ++/* ++ * Functions for robustisizing ++ */ ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ ++ /* Mark the block for retirement */ ++ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> ++ needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>>Block %d marked for retirement", ++ flash_block); ++ ++ /* TODO: ++ * Just do a garbage collection on the affected block ++ * then retire the block ++ * NB recursion ++ */ ++} ++ ++static int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ ++ yaffs_spare_init(&spare); ++ ++ if (ext_tags->is_deleted) ++ spare.page_status = 0; ++ else { ++ tags.obj_id = ext_tags->obj_id; ++ tags.chunk_id = ext_tags->chunk_id; ++ ++ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; ++ else ++ tags.n_bytes_msb = 3; ++ ++ tags.serial_number = ext_tags->serial_number; ++ ++ if (!dev->param.use_nand_ecc && data) { ++ yaffs_ecc_calc(data, spare.ecc1); ++ yaffs_ecc_calc(&data[256], spare.ecc2); ++ } ++ ++ yaffs_load_tags_to_spare(&spare, &tags); ++ } ++ return yaffs_wr_nand(dev, nand_chunk, data, &spare); ++} ++ ++static int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ int deleted; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ if (!yaffs_rd_chunk_nand(dev, nand_chunk, ++ data, &spare, &ecc_result, 1)) ++ return YAFFS_FAIL; ++ ++ /* ext_tags may be NULL */ ++ if (!ext_tags) ++ return YAFFS_OK; ++ ++ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; ++ ++ ext_tags->is_deleted = deleted; ++ ext_tags->ecc_result = ecc_result; ++ ext_tags->block_bad = 0; /* We're reading it */ ++ /* therefore it is not a bad block */ ++ ext_tags->chunk_used = ++ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; ++ ++ if (ext_tags->chunk_used) { ++ yaffs_get_tags_from_spare(dev, &spare, &tags); ++ ext_tags->obj_id = tags.obj_id; ++ ext_tags->chunk_id = tags.chunk_id; ++ ext_tags->n_bytes = tags.n_bytes_lsb; ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ ext_tags->n_bytes |= ++ (((unsigned)tags.n_bytes_msb) << 10); ++ ++ ext_tags->serial_number = tags.serial_number; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_spare spare; ++ ++ memset(&spare, 0xff, sizeof(struct yaffs_spare)); ++ ++ spare.block_status = 'Y'; ++ ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, ++ &spare); ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, ++ NULL, &spare); ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ struct yaffs_spare spare0, spare1; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ enum yaffs_ecc_result dummy; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ *seq_number = 0; ++ ++ /* Look for bad block markers in the first two chunks */ ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, ++ NULL, &spare0, &dummy, 0); ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, ++ NULL, &spare1, &dummy, 0); ++ ++ if (hweight8(spare0.block_status & spare1.block_status) < 7) ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ else ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ ++ return YAFFS_OK; ++} ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev) ++{ ++ if(dev->param.is_yaffs2) ++ return; ++ if(!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; ++ if(!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; ++ if(!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; ++ if(!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.10.40/fs/yaffs2/yaffs_tagscompat.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSCOMPAT_H__ ++#define __YAFFS_TAGSCOMPAT_H__ ++ ++ ++#include "yaffs_guts.h" ++ ++#if 0 ++ ++ ++int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); ++int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ ++#endif ++ ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev); ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags); ++int yaffs_check_tags_ecc(struct yaffs_tags *tags); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.10.40/fs/yaffs2/yaffs_tagsmarshall.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,199 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_packedtags2.h" ++ ++static int yaffs_tags_marshall_write(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ struct yaffs_packed_tags2 pt; ++ int retval; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_write chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ /* For yaffs2 writing there must be both data and tags. ++ * If we're using inband tags, then the tags are stuffed into ++ * the end of the data buffer. ++ */ ++ if (!data || !tags) ++ BUG(); ++ else if (dev->param.inband_tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *)(data + ++ dev-> ++ data_bytes_per_chunk); ++ yaffs_pack_tags2_tags_only(pt2tp, tags); ++ } else { ++ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); ++ } ++ ++ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ (dev->param.inband_tags) ? NULL : packed_tags_ptr, ++ (dev->param.inband_tags) ? 0 : packed_tags_size); ++ ++ return retval; ++} ++ ++static int yaffs_tags_marshall_read(struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = 0; ++ int local_data = 0; ++ u8 spare_buffer[100]; ++ enum yaffs_ecc_result ecc_result; ++ ++ struct yaffs_packed_tags2 pt; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_read chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ if (dev->param.inband_tags) { ++ if (!data) { ++ local_data = 1; ++ data = yaffs_get_temp_buffer(dev); ++ } ++ } ++ ++ if (dev->param.inband_tags || (data && !tags)) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ NULL, 0, ++ &ecc_result); ++ else if (tags) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ spare_buffer, packed_tags_size, ++ &ecc_result); ++ else ++ BUG(); ++ ++ ++ if (dev->param.inband_tags) { ++ if (tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *) ++ &data[dev->data_bytes_per_chunk]; ++ yaffs_unpack_tags2_tags_only(tags, pt2tp); ++ } ++ } else if (tags) { ++ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); ++ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); ++ } ++ ++ if (local_data) ++ yaffs_release_temp_buffer(dev, data); ++ ++ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { ++ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { ++ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) ++ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ } ++ ++ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", ++ block_no); ++ ++ retval = dev->drv.drv_check_bad_fn(dev, block_no); ++ ++ if (retval== YAFFS_FAIL) { ++ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); ++ ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ *seq_number = 0; ++ } else { ++ struct yaffs_ext_tags t; ++ ++ yaffs_tags_marshall_read(dev, ++ block_no * dev->param.chunks_per_block, ++ NULL, &t); ++ ++ if (t.chunk_used) { ++ *seq_number = t.seq_number; ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ } else { ++ *seq_number = 0; ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "block query returns seq %d state %d", ++ *seq_number, *state); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ return dev->drv.drv_mark_bad_fn(dev, block_no); ++ ++} ++ ++ ++void yaffs_tags_marshall_install(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; ++ ++ if (!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; ++ ++ if (!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; ++ ++ if (!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; ++ ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.10.40/fs/yaffs2/yaffs_tagsmarshall.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSMARSHALL_H__ ++#define __YAFFS_TAGSMARSHALL_H__ ++ ++#include "yaffs_guts.h" ++void yaffs_tags_marshall_install(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_trace.h linux-3.10.40/fs/yaffs2/yaffs_trace.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_trace.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,57 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YTRACE_H__ ++#define __YTRACE_H__ ++ ++extern unsigned int yaffs_trace_mask; ++extern unsigned int yaffs_wr_attempts; ++ ++/* ++ * Tracing flags. ++ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. ++ */ ++ ++#define YAFFS_TRACE_OS 0x00000002 ++#define YAFFS_TRACE_ALLOCATE 0x00000004 ++#define YAFFS_TRACE_SCAN 0x00000008 ++#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 ++#define YAFFS_TRACE_ERASE 0x00000020 ++#define YAFFS_TRACE_GC 0x00000040 ++#define YAFFS_TRACE_WRITE 0x00000080 ++#define YAFFS_TRACE_TRACING 0x00000100 ++#define YAFFS_TRACE_DELETION 0x00000200 ++#define YAFFS_TRACE_BUFFERS 0x00000400 ++#define YAFFS_TRACE_NANDACCESS 0x00000800 ++#define YAFFS_TRACE_GC_DETAIL 0x00001000 ++#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 ++#define YAFFS_TRACE_MTD 0x00004000 ++#define YAFFS_TRACE_CHECKPOINT 0x00008000 ++ ++#define YAFFS_TRACE_VERIFY 0x00010000 ++#define YAFFS_TRACE_VERIFY_NAND 0x00020000 ++#define YAFFS_TRACE_VERIFY_FULL 0x00040000 ++#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 ++ ++#define YAFFS_TRACE_SYNC 0x00100000 ++#define YAFFS_TRACE_BACKGROUND 0x00200000 ++#define YAFFS_TRACE_LOCK 0x00400000 ++#define YAFFS_TRACE_MOUNT 0x00800000 ++ ++#define YAFFS_TRACE_ERROR 0x40000000 ++#define YAFFS_TRACE_BUG 0x80000000 ++#define YAFFS_TRACE_ALWAYS 0xf0000000 ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_verify.c linux-3.10.40/fs/yaffs2/yaffs_verify.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_verify.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,529 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_verify.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++ ++int yaffs_skip_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & ++ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_full_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_nand_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); ++} ++ ++static const char * const block_state_name[] = { ++ "Unknown", ++ "Needs scan", ++ "Scanning", ++ "Empty", ++ "Allocating", ++ "Full", ++ "Dirty", ++ "Checkpoint", ++ "Collecting", ++ "Dead" ++}; ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) ++{ ++ int actually_used; ++ int in_use; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Report illegal runtime states */ ++ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has undefined state %d", ++ n, bi->block_state); ++ ++ switch (bi->block_state) { ++ case YAFFS_BLOCK_STATE_UNKNOWN: ++ case YAFFS_BLOCK_STATE_SCANNING: ++ case YAFFS_BLOCK_STATE_NEEDS_SCAN: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has bad run-state %s", ++ n, block_state_name[bi->block_state]); ++ } ++ ++ /* Check pages in use and soft deletions are legal */ ++ ++ actually_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->pages_in_use < 0 || ++ bi->pages_in_use > dev->param.chunks_per_block || ++ bi->soft_del_pages < 0 || ++ bi->soft_del_pages > dev->param.chunks_per_block || ++ actually_used < 0 || actually_used > dev->param.chunks_per_block) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has illegal values pages_in_used %d soft_del_pages %d", ++ n, bi->pages_in_use, bi->soft_del_pages); ++ ++ /* Check chunk bitmap legal */ ++ in_use = yaffs_count_chunk_bits(dev, n); ++ if (in_use != bi->pages_in_use) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", ++ n, bi->pages_in_use, in_use); ++} ++ ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n) ++{ ++ yaffs_verify_blk(dev, bi, n); ++ ++ /* After collection the block should be in the erased state */ ++ ++ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && ++ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Block %d is in state %d after gc, should be erased", ++ n, bi->block_state); ++ } ++} ++ ++void yaffs_verify_blocks(struct yaffs_dev *dev) ++{ ++ int i; ++ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; ++ int illegal_states = 0; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ memset(state_count, 0, sizeof(state_count)); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ yaffs_verify_blk(dev, bi, i); ++ ++ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) ++ state_count[bi->block_state]++; ++ else ++ illegal_states++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%d blocks have illegal states", ++ illegal_states); ++ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many allocating blocks"); ++ ++ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%s %d blocks", ++ block_state_name[i], state_count[i]); ++ ++ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Checkpoint block count wrong dev %d count %d", ++ dev->blocks_in_checkpt, ++ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); ++ ++ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Erased block count wrong dev %d count %d", ++ dev->n_erased_blocks, ++ state_count[YAFFS_BLOCK_STATE_EMPTY]); ++ ++ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many collecting blocks %d (max is 1)", ++ state_count[YAFFS_BLOCK_STATE_COLLECTING]); ++} ++ ++/* ++ * Verify the object header. oh must be valid, but obj and tags may be NULL in ++ * which case those tests will not be performed. ++ */ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!(tags && obj && oh)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Verifying object header tags %p obj %p oh %p", ++ tags, obj, oh); ++ return; ++ } ++ ++ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || ++ oh->type > YAFFS_OBJECT_TYPE_MAX) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header type is illegal value 0x%x", ++ tags->obj_id, oh->type); ++ ++ if (tags->obj_id != obj->obj_id) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch obj_id %d", ++ tags->obj_id, obj->obj_id); ++ ++ /* ++ * Check that the object's parent ids match if parent_check requested. ++ * ++ * Tests do not apply to the root object. ++ */ ++ ++ if (parent_check && tags->obj_id > 1 && !obj->parent) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d obj->parent is NULL", ++ tags->obj_id, oh->parent_obj_id); ++ ++ if (parent_check && obj->parent && ++ oh->parent_obj_id != obj->parent->obj_id && ++ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d parent_obj_id %d", ++ tags->obj_id, oh->parent_obj_id, ++ obj->parent->obj_id); ++ ++ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is NULL", ++ obj->obj_id); ++ ++ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is 0xff", ++ obj->obj_id); ++} ++ ++void yaffs_verify_file(struct yaffs_obj *obj) ++{ ++ u32 x; ++ int required_depth; ++ int actual_depth; ++ int last_chunk; ++ u32 offset_in_chunk; ++ u32 the_chunk; ++ ++ u32 i; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ struct yaffs_tnode *tn; ++ u32 obj_id; ++ ++ if (!obj) ++ return; ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ dev = obj->my_dev; ++ obj_id = obj->obj_id; ++ ++ ++ /* Check file size is consistent with tnode depth */ ++ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, ++ &last_chunk, &offset_in_chunk); ++ last_chunk++; ++ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x > 0) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ actual_depth = obj->variant.file_variant.top_level; ++ ++ /* Check that the chunks in the tnode tree are all correct. ++ * We do this by scanning through the tnode tree and ++ * checking the tags for every chunk match. ++ */ ++ ++ if (yaffs_skip_nand_verification(dev)) ++ return; ++ ++ for (i = 1; i <= last_chunk; i++) { ++ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); ++ ++ if (!tn) ++ continue; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk > 0) { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ &tags); ++ if (tags.obj_id != obj_id || tags.chunk_id != i) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", ++ obj_id, i, the_chunk, ++ tags.obj_id, tags.chunk_id); ++ } ++ } ++} ++ ++void yaffs_verify_link(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify sane equivalent object */ ++} ++ ++void yaffs_verify_symlink(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify symlink string */ ++} ++ ++void yaffs_verify_special(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++} ++ ++void yaffs_verify_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ u32 chunk_min; ++ u32 chunk_max; ++ u32 chunk_id_ok; ++ u32 chunk_in_range; ++ u32 chunk_wrongly_deleted; ++ u32 chunk_valid; ++ ++ if (!obj) ++ return; ++ ++ if (obj->being_created) ++ return; ++ ++ dev = obj->my_dev; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Check sane object header chunk */ ++ ++ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; ++ chunk_max = ++ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; ++ ++ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && ++ ((unsigned)(obj->hdr_chunk)) <= chunk_max); ++ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); ++ chunk_valid = chunk_in_range && ++ yaffs_check_chunk_bit(dev, ++ obj->hdr_chunk / dev->param.chunks_per_block, ++ obj->hdr_chunk % dev->param.chunks_per_block); ++ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; ++ ++ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has chunk_id %d %s %s", ++ obj->obj_id, obj->hdr_chunk, ++ chunk_id_ok ? "" : ",out of range", ++ chunk_wrongly_deleted ? ",marked as deleted" : ""); ++ ++ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj_hdr *oh; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); ++ ++ yaffs_verify_oh(obj, oh, &tags, 1); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ /* Verify it has a parent */ ++ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has parent pointer %p which does not look like an object", ++ obj->obj_id, obj->parent); ++ } ++ ++ /* Verify parent is a directory */ ++ if (obj->parent && ++ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d's parent is not a directory (type %d)", ++ obj->obj_id, obj->parent->variant_type); ++ } ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ yaffs_verify_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ yaffs_verify_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ yaffs_verify_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ yaffs_verify_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ yaffs_verify_special(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has illegaltype %d", ++ obj->obj_id, obj->variant_type); ++ break; ++ } ++} ++ ++void yaffs_verify_objects(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int i; ++ struct list_head *lh; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ yaffs_verify_obj(obj); ++ } ++ } ++} ++ ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ int count = 0; ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!obj->parent) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); ++ BUG(); ++ return; ++ } ++ ++ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &obj->parent->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_verify_obj(list_obj); ++ if (obj == list_obj) ++ count++; ++ } ++ ++ if (count != 1) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory %d times", ++ count); ++ BUG(); ++ } ++} ++ ++void yaffs_verify_dir(struct yaffs_obj *directory) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ ++ if (!directory) { ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_full_verification(directory->my_dev)) ++ return; ++ ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Directory has wrong type: %d", ++ directory->variant_type); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &directory->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (list_obj->parent != directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory list has wrong parent %p", ++ list_obj->parent); ++ BUG(); ++ } ++ yaffs_verify_obj_in_dir(list_obj); ++ } ++} ++ ++static int yaffs_free_verification_failures; ++ ++void yaffs_verify_free_chunks(struct yaffs_dev *dev) ++{ ++ int counted; ++ int difference; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ counted = yaffs_count_free_chunks(dev); ++ ++ difference = dev->n_free_chunks - counted; ++ ++ if (difference) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Freechunks verification failure %d %d %d", ++ dev->n_free_chunks, counted, difference); ++ yaffs_free_verification_failures++; ++ } ++} ++ ++int yaffs_verify_file_sane(struct yaffs_obj *in) ++{ ++ (void) in; ++ return YAFFS_OK; ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_verify.h linux-3.10.40/fs/yaffs2/yaffs_verify.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_verify.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,43 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_VERIFY_H__ ++#define __YAFFS_VERIFY_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, ++ int n); ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n); ++void yaffs_verify_blocks(struct yaffs_dev *dev); ++ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check); ++void yaffs_verify_file(struct yaffs_obj *obj); ++void yaffs_verify_link(struct yaffs_obj *obj); ++void yaffs_verify_symlink(struct yaffs_obj *obj); ++void yaffs_verify_special(struct yaffs_obj *obj); ++void yaffs_verify_obj(struct yaffs_obj *obj); ++void yaffs_verify_objects(struct yaffs_dev *dev); ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); ++void yaffs_verify_dir(struct yaffs_obj *directory); ++void yaffs_verify_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_verify_file_sane(struct yaffs_obj *obj); ++ ++int yaffs_skip_verification(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_vfs.c linux-3.10.40/fs/yaffs2/yaffs_vfs.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_vfs.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,3600 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * Acknowledgements: ++ * Luc van OostenRyck for numerous patches. ++ * Nick Bane for numerous patches. ++ * Nick Bane for 2.5/2.6 integration. ++ * Andras Toth for mknod rdev issue. ++ * Michael Fischer for finding the problem with inode inconsistency. ++ * Some code bodily lifted from JFFS ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * ++ * This is the file system front-end to YAFFS that hooks it up to ++ * the VFS. ++ * ++ * Special notes: ++ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with ++ * this superblock ++ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this ++ * superblock ++ * >> inode->u.generic_ip points to the associated struct yaffs_obj. ++ */ ++ ++/* ++ * There are two variants of the VFS glue code. This variant should compile ++ * for any version of Linux. ++ */ ++#include ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) ++#define YAFFS_COMPILE_BACKGROUND ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) ++#define YAFFS_COMPILE_FREEZER ++#endif ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) ++#define YAFFS_COMPILE_EXPORTFS ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_USE_SETATTR_COPY ++#define YAFFS_USE_TRUNCATE_SETSIZE ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_HAS_EVICT_INODE ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++#define YAFFS_NEW_FOLLOW_LINK 1 ++#else ++#define YAFFS_NEW_FOLLOW_LINK 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_HAS_WRITE_SUPER ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++#include ++#endif ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++#include ++#endif ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++#include ++#include ++#endif ++#ifdef YAFFS_COMPILE_FREEZER ++#include ++#endif ++ ++#include ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++#include ++ ++#define UnlockPage(p) unlock_page(p) ++#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) ++ ++/* FIXME: use sb->s_id instead ? */ ++#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) ++ ++#else ++ ++#include ++#define BDEVNAME_SIZE 0 ++#define yaffs_devname(sb, buf) kdevname(sb->s_dev) ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) ++/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ ++#define __user ++#endif ++ ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define YPROC_ROOT (&proc_root) ++#else ++#define YPROC_ROOT NULL ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define Y_INIT_TIMER(a) init_timer(a) ++#else ++#define Y_INIT_TIMER(a) init_timer_on_stack(a) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) ++#define YAFFS_USE_WRITE_BEGIN_END 1 ++#else ++#define YAFFS_USE_WRITE_BEGIN_END 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_SUPER_HAS_DIRTY ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) ++static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) ++{ ++ uint64_t result = partition_size; ++ do_div(result, block_size); ++ return (uint32_t) result; ++} ++#else ++#define YCALCBLOCKS(s, b) ((s)/(b)) ++#endif ++ ++#include ++#include ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#include "yaffs_linux.h" ++ ++#include "yaffs_mtdif.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_getblockinfo.h" ++ ++unsigned int yaffs_trace_mask = ++ YAFFS_TRACE_BAD_BLOCKS | ++ YAFFS_TRACE_ALWAYS | ++ 0; ++ ++unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; ++unsigned int yaffs_auto_checkpoint = 1; ++unsigned int yaffs_gc_control = 1; ++unsigned int yaffs_bg_enable = 1; ++unsigned int yaffs_auto_select = 1; ++/* Module Parameters */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++module_param(yaffs_trace_mask, uint, 0644); ++module_param(yaffs_wr_attempts, uint, 0644); ++module_param(yaffs_auto_checkpoint, uint, 0644); ++module_param(yaffs_gc_control, uint, 0644); ++module_param(yaffs_bg_enable, uint, 0644); ++#else ++MODULE_PARM(yaffs_trace_mask, "i"); ++MODULE_PARM(yaffs_wr_attempts, "i"); ++MODULE_PARM(yaffs_auto_checkpoint, "i"); ++MODULE_PARM(yaffs_gc_control, "i"); ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++/* use iget and read_inode */ ++#define Y_IGET(sb, inum) iget((sb), (inum)) ++ ++#else ++/* Call local equivalent */ ++#define YAFFS_USE_OWN_IGET ++#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) ++ ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) ++#else ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) ++#endif ++ ++#define yaffs_inode_to_obj(iptr) \ ++ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) ++#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) ++#else ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++#define Y_CLEAR_INODE(i) clear_inode(i) ++#else ++#define Y_CLEAR_INODE(i) end_writeback(i) ++#endif ++ ++ ++#define update_dir_time(dir) do {\ ++ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ ++ } while (0) ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj); ++ ++ ++static void yaffs_gross_lock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); ++ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); ++} ++ ++static void yaffs_gross_unlock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); ++ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++} ++ ++ ++static int yaffs_readpage_nolock(struct file *f, struct page *pg) ++{ ++ /* Lifted from jffs2 */ ++ ++ struct yaffs_obj *obj; ++ unsigned char *pg_buf; ++ int ret; ++ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readpage_nolock at %lld, size %08x", ++ (long long)pos, ++ (unsigned)PAGE_CACHE_SIZE); ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ BUG_ON(!PageLocked(pg)); ++#else ++ if (!PageLocked(pg)) ++ PAGE_BUG(pg); ++#endif ++ ++ pg_buf = kmap(pg); ++ /* FIXME: Can kmap fail? */ ++ ++ yaffs_gross_lock(dev); ++ ++ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (ret >= 0) ++ ret = 0; ++ ++ if (ret) { ++ ClearPageUptodate(pg); ++ SetPageError(pg); ++ } else { ++ SetPageUptodate(pg); ++ ClearPageError(pg); ++ } ++ ++ flush_dcache_page(pg); ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); ++ return ret; ++} ++ ++static int yaffs_readpage_unlock(struct file *f, struct page *pg) ++{ ++ int ret = yaffs_readpage_nolock(f, pg); ++ UnlockPage(pg); ++ return ret; ++} ++ ++static int yaffs_readpage(struct file *f, struct page *pg) ++{ ++ int ret; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); ++ ret = yaffs_readpage_unlock(f, pg); ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); ++ return ret; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++#define YCRED_FSUID() from_kuid(&init_user_ns, current_fsuid()) ++#define YCRED_FSGID() from_kgid(&init_user_ns, current_fsgid()) ++#else ++#define YCRED_FSUID() YCRED(current)->fsuid ++#define YCRED_FSGID() YCRED(current)->fsgid ++ ++static inline uid_t i_uid_read(const struct inode *inode) ++{ ++ return inode->i_uid; ++} ++ ++static inline gid_t i_gid_read(const struct inode *inode) ++{ ++ return inode->i_gid; ++} ++ ++static inline void i_uid_write(struct inode *inode, uid_t uid) ++{ ++ inode->i_uid = uid; ++} ++ ++static inline void i_gid_write(struct inode *inode, gid_t gid) ++{ ++ inode->i_gid = gid; ++} ++#endif ++ ++static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc) ++ lc->dirty = val; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb) ++ sb->s_dirt = val; ++ } ++#endif ++ ++} ++ ++static void yaffs_set_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 1); ++} ++ ++static void yaffs_clear_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 0); ++} ++ ++static int yaffs_check_super_dirty(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc && lc->dirty) ++ return 1; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb && sb->s_dirt) ++ return 1; ++ } ++#endif ++ return 0; ++ ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_writepage(struct page *page, struct writeback_control *wbc) ++#else ++static int yaffs_writepage(struct page *page) ++#endif ++{ ++ struct yaffs_dev *dev; ++ struct address_space *mapping = page->mapping; ++ struct inode *inode; ++ unsigned long end_index; ++ char *buffer; ++ struct yaffs_obj *obj; ++ int n_written = 0; ++ unsigned n_bytes; ++ loff_t i_size; ++ ++ if (!mapping) ++ BUG(); ++ inode = mapping->host; ++ if (!inode) ++ BUG(); ++ i_size = i_size_read(inode); ++ ++ end_index = i_size >> PAGE_CACHE_SHIFT; ++ ++ if (page->index < end_index) ++ n_bytes = PAGE_CACHE_SIZE; ++ else { ++ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); ++ ++ if (page->index > end_index || !n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, inode size = %lld!!", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, ++ inode->i_size); ++ yaffs_trace(YAFFS_TRACE_OS, ++ " -> don't care!!"); ++ ++ zero_user_segment(page, 0, PAGE_CACHE_SIZE); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ return 0; ++ } ++ } ++ ++ if (n_bytes != PAGE_CACHE_SIZE) ++ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); ++ ++ get_page(page); ++ ++ buffer = kmap(page); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, size %08x", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag0: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ n_written = yaffs_wr_file(obj, buffer, ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag1: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ yaffs_gross_unlock(dev); ++ ++ kunmap(page); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ put_page(page); ++ ++ return (n_written == n_bytes) ? 0 : -ENOSPC; ++} ++ ++/* Space holding and freeing is done to ensure we have space available for write_begin/end */ ++/* For now we just assume few parallel writes and check against a small number. */ ++/* Todo: need to do this with a counter to handle parallel reads better */ ++ ++static ssize_t yaffs_hold_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ int n_free_chunks; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ n_free_chunks = yaffs_get_n_free_chunks(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ return (n_free_chunks > 20) ? 1 : 0; ++} ++ ++static void yaffs_release_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_gross_unlock(dev); ++} ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_begin(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ ++ struct page *pg = NULL; ++ pgoff_t index = pos >> PAGE_CACHE_SHIFT; ++ ++ int ret = 0; ++ int space_held = 0; ++ ++ /* Get a page */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ++ pg = grab_cache_page_write_begin(mapping, index, flags); ++#else ++ pg = __grab_cache_page(mapping, index); ++#endif ++ ++ *pagep = pg; ++ if (!pg) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "start yaffs_write_begin index %d(%x) uptodate %d", ++ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); ++ ++ /* Get fs space */ ++ space_held = yaffs_hold_space(filp); ++ ++ if (!space_held) { ++ ret = -ENOSPC; ++ goto out; ++ } ++ ++ /* Update page if required */ ++ ++ if (!Page_Uptodate(pg)) ++ ret = yaffs_readpage_nolock(filp, pg); ++ ++ if (ret) ++ goto out; ++ ++ /* Happy path return */ ++ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); ++ ++ return 0; ++ ++out: ++ yaffs_trace(YAFFS_TRACE_OS, ++ "end yaffs_write_begin fail returning %d", ret); ++ if (space_held) ++ yaffs_release_space(filp); ++ if (pg) { ++ unlock_page(pg); ++ page_cache_release(pg); ++ } ++ return ret; ++} ++ ++#else ++ ++static int yaffs_prepare_write(struct file *f, struct page *pg, ++ unsigned offset, unsigned to) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); ++ ++ if (!Page_Uptodate(pg)) ++ return yaffs_readpage_nolock(f, pg); ++ return 0; ++} ++#endif ++ ++ ++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, ++ loff_t * pos) ++{ ++ struct yaffs_obj *obj; ++ int n_written; ++ loff_t ipos; ++ struct inode *inode; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: hey obj is null!"); ++ return -EINVAL; ++ } ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ inode = f->f_dentry->d_inode; ++ ++ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) ++ ipos = inode->i_size; ++ else ++ ipos = *pos; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", ++ (unsigned)n, (unsigned)n, obj->obj_id, ipos); ++ ++ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: %d(%x) bytes written", ++ (unsigned)n, (unsigned)n); ++ ++ if (n_written > 0) { ++ ipos += n_written; ++ *pos = ipos; ++ if (ipos > inode->i_size) { ++ inode->i_size = ipos; ++ inode->i_blocks = (ipos + 511) >> 9; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write size updated to %lld bytes, %d blocks", ++ ipos, (int)(inode->i_blocks)); ++ } ++ ++ } ++ yaffs_gross_unlock(dev); ++ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; ++} ++ ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_end(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *pg, void *fsdadata) ++{ ++ int ret = 0; ++ void *addr, *kva; ++ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); ++ ++ kva = kmap(pg); ++ addr = kva + offset_into_page; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end addr %p pos %lld n_bytes %d", ++ addr, pos, copied); ++ ++ ret = yaffs_file_write(filp, addr, copied, &pos); ++ ++ if (ret != copied) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end not same size ret %d copied %d", ++ ret, copied); ++ SetPageError(pg); ++ } ++ ++ kunmap(pg); ++ ++ yaffs_release_space(filp); ++ unlock_page(pg); ++ page_cache_release(pg); ++ return ret; ++} ++#else ++ ++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, ++ unsigned to) ++{ ++ void *addr, *kva; ++ ++ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; ++ int n_bytes = to - offset; ++ int n_written; ++ ++ kva = kmap(pg); ++ addr = kva + offset; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write addr %p pos %lld n_bytes %d", ++ addr, pos, n_bytes); ++ ++ n_written = yaffs_file_write(f, addr, n_bytes, &pos); ++ ++ if (n_written != n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write not same size n_written %d n_bytes %d", ++ n_written, n_bytes); ++ SetPageError(pg); ++ } ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write returning %d", ++ n_written == n_bytes ? 0 : n_written); ++ ++ return n_written == n_bytes ? 0 : n_written; ++} ++#endif ++ ++static struct address_space_operations yaffs_file_address_operations = { ++ .readpage = yaffs_readpage, ++ .writepage = yaffs_writepage, ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++ .write_begin = yaffs_write_begin, ++ .write_end = yaffs_write_end, ++#else ++ .prepare_write = yaffs_prepare_write, ++ .commit_write = yaffs_commit_write, ++#endif ++}; ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_file_flush(struct file *file, fl_owner_t id) ++#else ++static int yaffs_file_flush(struct file *file) ++#endif ++{ ++ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); ++ ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_flush object %d (%s)", ++ obj->obj_id, ++ obj->dirty ? "dirty" : "clean"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_file(obj, 1, 0); ++ ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++static int yaffs_sync_object(struct file *file, int datasync) ++#else ++static int yaffs_sync_object(struct file *file, struct dentry *dentry, ++ int datasync) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++ struct dentry *dentry = file->f_path.dentry; ++#endif ++ ++ obj = yaffs_dentry_to_obj(dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_object"); ++ yaffs_gross_lock(dev); ++ yaffs_flush_file(obj, 1, datasync); ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .splice_read = generic_file_splice_read, ++ .splice_write = generic_file_splice_write, ++ .llseek = generic_file_llseek, ++}; ++ ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .sendfile = generic_file_sendfile, ++}; ++ ++#else ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = generic_file_read, ++ .write = generic_file_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ .sendfile = generic_file_sendfile, ++#endif ++}; ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++static void zero_user_segment(struct page *page, unsigned start, unsigned end) ++{ ++ void *kaddr = kmap_atomic(page, KM_USER0); ++ memset(kaddr + start, 0, end - start); ++ kunmap_atomic(kaddr, KM_USER0); ++ flush_dcache_page(page); ++} ++#endif ++ ++ ++static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) ++{ ++#ifdef YAFFS_USE_TRUNCATE_SETSIZE ++ truncate_setsize(inode, newsize); ++ return 0; ++#else ++ truncate_inode_pages(&inode->i_data, newsize); ++ return 0; ++#endif ++ ++} ++ ++ ++static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) ++{ ++#ifdef YAFFS_USE_SETATTR_COPY ++ setattr_copy(inode, attr); ++ return 0; ++#else ++ return inode_setattr(inode, attr); ++#endif ++ ++} ++ ++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_setattr of object %d", ++ yaffs_inode_to_obj(inode)->obj_id); ++#if 0 ++ /* Fail if a requested resize >= 2GB */ ++ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) ++ error = -EINVAL; ++#endif ++ ++ if (error == 0) ++ error = inode_change_ok(inode, attr); ++ if (error == 0) { ++ int result; ++ if (!error) { ++ error = yaffs_vfs_setattr(inode, attr); ++ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_vfs_setsize(inode, attr->ia_size); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ } ++ } ++ dev = yaffs_inode_to_obj(inode)->my_dev; ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "resize to %d(%x)", ++ (int)(attr->ia_size), ++ (int)(attr->ia_size)); ++ } ++ yaffs_gross_lock(dev); ++ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); ++ if (result == YAFFS_OK) { ++ error = 0; ++ } else { ++ error = -EPERM; ++ } ++ yaffs_gross_unlock(dev); ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_set_xattrib(obj, name, value, size, flags); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, ++ void *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_getxattr \"%s\" from object %d", ++ name, obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_get_xattrib(obj, name, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_remove_xattrib(obj, name); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_list_xattrib(obj, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr done returning %d", error); ++ ++ return error; ++} ++ ++ ++static const struct inode_operations yaffs_file_inode_operations = { ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++ ++static int yaffs_readlink(struct dentry *dentry, char __user * buffer, ++ int buflen) ++{ ++ unsigned char *alias; ++ int ret; ++ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) ++ return -ENOMEM; ++ ++ ret = vfs_readlink(dentry, buffer, buflen, alias); ++ kfree(alias); ++ return ret; ++} ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ void *ret; ++#else ++static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int ret ++#endif ++ unsigned char *alias; ++ int ret_int = 0; ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) { ++ ret_int = -ENOMEM; ++ goto out; ++ } ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ nd_set_link(nd, alias); ++ ret = alias; ++out: ++ if (ret_int) ++ ret = ERR_PTR(ret_int); ++ return ret; ++#else ++ ret = vfs_follow_link(nd, alias); ++ kfree(alias); ++out: ++ if (ret_int) ++ ret = ret_int; ++ return ret; ++#endif ++} ++ ++ ++#ifdef YAFFS_HAS_PUT_INODE ++ ++/* For now put inode is just for debugging ++ * Put inode is called when the inode **structure** is put. ++ */ ++static void yaffs_put_inode(struct inode *inode) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_put_inode: ino %d, count %d"), ++ (int)inode->i_ino, atomic_read(&inode->i_count); ++ ++} ++#endif ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) ++{ ++ kfree(alias); ++} ++#endif ++ ++static const struct inode_operations yaffs_symlink_inode_operations = { ++ .readlink = yaffs_readlink, ++ .follow_link = yaffs_follow_link, ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ .put_link = yaffs_put_link, ++#endif ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++#ifdef YAFFS_USE_OWN_IGET ++ ++static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) ++{ ++ struct inode *inode; ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); ++ ++ inode = iget_locked(sb, ino); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ yaffs_gross_unlock(dev); ++ ++ unlock_new_inode(inode); ++ return inode; ++} ++ ++#else ++ ++static void yaffs_read_inode(struct inode *inode) ++{ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_inode for %d", (int)inode->i_ino); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++} ++ ++#endif ++ ++ ++ ++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, ++ struct yaffs_obj *obj) ++{ ++ struct inode *inode; ++ ++ if (!sb) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL super_block!!"); ++ return NULL; ++ ++ } ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL object!!"); ++ return NULL; ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for object %d", obj->obj_id); ++ ++ inode = Y_IGET(sb, obj->obj_id); ++ if (IS_ERR(inode)) ++ return NULL; ++ ++ /* NB Side effect: iget calls back to yaffs_read_inode(). */ ++ /* iget also increments the inode's i_count */ ++ /* NB You can't be holding gross_lock or deadlock will happen! */ ++ ++ return inode; ++} ++ ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++#define YCRED(x) x ++#else ++#define YCRED(x) (x->cred) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t rdev) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t rdev) ++#else ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ int rdev) ++#endif ++{ ++ struct inode *inode; ++ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_dev *dev; ++ ++ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); ++ ++ int error = -ENOSPC; ++ uid_t uid = YCRED_FSUID(); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); ++ ++ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) ++ mode |= S_ISGID; ++ ++ if (parent) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: parent object %d type %d", ++ parent->obj_id, parent->variant_type); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: could not get parent object"); ++ return -EPERM; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: making oject for %s, mode %x dev %x", ++ dentry->d_name.name, mode, rdev); ++ ++ dev = parent->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ switch (mode & S_IFMT) { ++ default: ++ /* Special (socket, fifo, device...) */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, old_encode_dev(rdev)); ++#else ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, rdev); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); ++ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, ++ gid); ++ break; ++ case S_IFDIR: /* directory */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); ++ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, ++ uid, gid); ++ break; ++ case S_IFLNK: /* symlink */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); ++ obj = NULL; /* Do we ever get here? */ ++ break; ++ } ++ ++ /* Can not call yaffs_get_inode() with gross lock held */ ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod created object %d count = %d", ++ obj->obj_id, atomic_read(&inode->i_count)); ++ error = 0; ++ yaffs_fill_inode_from_obj(dir, parent); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); ++ error = -ENOMEM; ++ } ++ ++ return error; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++#else ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ int ret_val; ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); ++ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); ++ return ret_val; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool dummy) ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ struct nameidata *n) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *n) ++#else ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); ++ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ unsigned int dummy) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *n) ++#else ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ ++ ++ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", ++ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); ++ ++ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); ++ ++ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ ++ ++ /* Can't hold gross lock when calling yaffs_get_inode() */ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_lookup found %d", obj->obj_id); ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); ++ ++ } ++ ++/* added NCB for 2.5/6 compatability - forces add even if inode is ++ * NULL which creates dentry hash */ ++ d_add(dentry, inode); ++ ++ return NULL; ++} ++ ++/* ++ * Create a link... ++ */ ++static int yaffs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ struct inode *inode = old_dentry->d_inode; ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *link = NULL; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ ++ link = ++ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ obj); ++ ++ if (link) { ++ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); ++ d_instantiate(dentry, old_dentry->d_inode); ++ atomic_inc(&old_dentry->d_inode->i_count); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_link link count %d i_count %d", ++ old_dentry->d_inode->i_nlink, ++ atomic_read(&old_dentry->d_inode->i_count)); ++ } ++ ++ yaffs_gross_unlock(dev); ++ ++ if (link) { ++ update_dir_time(dir); ++ return 0; ++ } ++ ++ return -EPERM; ++} ++ ++static int yaffs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ uid_t uid = YCRED_FSUID(); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); ++ ++ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ return -ENAMETOOLONG; ++ ++ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > ++ YAFFS_MAX_ALIAS_LENGTH) ++ return -ENAMETOOLONG; ++ ++ dev = yaffs_inode_to_obj(dir)->my_dev; ++ yaffs_gross_lock(dev); ++ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ S_IFLNK | S_IRWXUGO, uid, gid, symname); ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ struct inode *inode; ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); ++ return 0; ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); ++ } ++ ++ return -ENOMEM; ++} ++ ++/* ++ * The VFS layer already does all the dentry stuff for rename. ++ * ++ * NB: POSIX says you can rename an object over an old object of the same name ++ */ ++static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct yaffs_dev *dev; ++ int ret_val = YAFFS_FAIL; ++ struct yaffs_obj *target; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); ++ dev = yaffs_inode_to_obj(old_dir)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ /* Check if the target is an existing directory that is not empty. */ ++ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ ++ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && ++ !list_empty(&target->variant.dir_variant.children)) { ++ ++ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); ++ ++ ret_val = YAFFS_FAIL; ++ } else { ++ /* Now does unlinking internally using shadowing mechanism */ ++ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); ++ ++ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), ++ old_dentry->d_name.name, ++ yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ } ++ yaffs_gross_unlock(dev); ++ ++ if (ret_val == YAFFS_OK) { ++ if (target) ++ inode_dec_link_count(new_dentry->d_inode); ++ ++ update_dir_time(old_dir); ++ if (old_dir != new_dir) ++ update_dir_time(new_dir); ++ return 0; ++ } else { ++ return -ENOTEMPTY; ++ } ++} ++ ++ ++ ++ ++static int yaffs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int ret_val; ++ ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", ++ (int)(dir->i_ino), dentry->d_name.name); ++ obj = yaffs_inode_to_obj(dir); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ ret_val = yaffs_unlinker(obj, dentry->d_name.name); ++ ++ if (ret_val == YAFFS_OK) { ++ inode_dec_link_count(dentry->d_inode); ++ dir->i_version++; ++ yaffs_gross_unlock(dev); ++ update_dir_time(dir); ++ return 0; ++ } ++ yaffs_gross_unlock(dev); ++ return -ENOTEMPTY; ++} ++ ++ ++ ++static const struct inode_operations yaffs_dir_inode_operations = { ++ .create = yaffs_create, ++ .lookup = yaffs_lookup, ++ .link = yaffs_link, ++ .unlink = yaffs_unlink, ++ .symlink = yaffs_symlink, ++ .mkdir = yaffs_mkdir, ++ .rmdir = yaffs_unlink, ++ .mknod = yaffs_mknod, ++ .rename = yaffs_rename, ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++/*-----------------------------------------------------------------*/ ++/* Directory search context allows us to unlock access to yaffs during ++ * filldir without causing problems with the directory being modified. ++ * This is similar to the tried and tested mechanism used in yaffs direct. ++ * ++ * A search context iterates along a doubly linked list of siblings in the ++ * directory. If the iterating object is deleted then this would corrupt ++ * the list iteration, likely causing a crash. The search context avoids ++ * this by using the remove_obj_fn to move the search context to the ++ * next object before the object is deleted. ++ * ++ * Many readdirs (and thus seach conexts) may be alive simulateously so ++ * each struct yaffs_dev has a list of these. ++ * ++ * A seach context lives for the duration of a readdir. ++ * ++ * All these functions must be called while yaffs is locked. ++ */ ++ ++struct yaffs_search_context { ++ struct yaffs_dev *dev; ++ struct yaffs_obj *dir_obj; ++ struct yaffs_obj *next_return; ++ struct list_head others; ++}; ++ ++/* ++ * yaffs_new_search() creates a new search context, initialises it and ++ * adds it to the device's search context list. ++ * ++ * Called at start of readdir. ++ */ ++static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) ++{ ++ struct yaffs_dev *dev = dir->my_dev; ++ struct yaffs_search_context *sc = ++ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); ++ if (sc) { ++ sc->dir_obj = dir; ++ sc->dev = dev; ++ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else ++ sc->next_return = ++ list_entry(dir->variant.dir_variant.children.next, ++ struct yaffs_obj, siblings); ++ INIT_LIST_HEAD(&sc->others); ++ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); ++ } ++ return sc; ++} ++ ++/* ++ * yaffs_search_end() disposes of a search context and cleans up. ++ */ ++static void yaffs_search_end(struct yaffs_search_context *sc) ++{ ++ if (sc) { ++ list_del(&sc->others); ++ kfree(sc); ++ } ++} ++ ++/* ++ * yaffs_search_advance() moves a search context to the next object. ++ * Called when the search iterates or when an object removal causes ++ * the search context to be moved to the next object. ++ */ ++static void yaffs_search_advance(struct yaffs_search_context *sc) ++{ ++ if (!sc) ++ return; ++ ++ if (sc->next_return == NULL || ++ list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else { ++ struct list_head *next = sc->next_return->siblings.next; ++ ++ if (next == &sc->dir_obj->variant.dir_variant.children) ++ sc->next_return = NULL; /* end of list */ ++ else ++ sc->next_return = ++ list_entry(next, struct yaffs_obj, siblings); ++ } ++} ++ ++/* ++ * yaffs_remove_obj_callback() is called when an object is unlinked. ++ * We check open search contexts and advance any which are currently ++ * on the object being iterated. ++ */ ++static void yaffs_remove_obj_callback(struct yaffs_obj *obj) ++{ ++ ++ struct list_head *i; ++ struct yaffs_search_context *sc; ++ struct list_head *search_contexts = ++ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); ++ ++ /* Iterate through the directory search contexts. ++ * If any are currently on the object being removed, then advance ++ * the search context to the next object to prevent a hanging pointer. ++ */ ++ list_for_each(i, search_contexts) { ++ sc = list_entry(i, struct yaffs_search_context, others); ++ if (sc->next_return == obj) ++ yaffs_search_advance(sc); ++ } ++ ++} ++ ++ ++/*-----------------------------------------------------------------*/ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++static int yaffs_readdir(struct file *file, struct dir_context *ctx) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ struct inode *inode = file->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(file->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ offset = ctx->pos; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: starting at %d", (int)offset); ++ ++ if (offset == 0) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry . ino %d", ++ (int)inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (!dir_emit_dot(file, ctx)) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ ctx->pos++; ++ } ++ if (offset == 1) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry .. ino %d", ++ (int)file->f_dentry->d_parent->d_inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (!dir_emit_dotdot(file, ctx)) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ ctx->pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ if (file->f_version != inode->i_version) { ++ offset = 2; ++ ctx->pos = offset; ++ file->f_version = inode->i_version; ++ } ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= offset) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!dir_emit(ctx, name, strlen(name), ++ this_inode, this_type) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ offset++; ++ ctx->pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++#else ++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ struct inode *inode = f->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ offset = f->f_pos; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: starting at %d", (int)offset); ++ ++ if (offset == 0) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry . ino %d", ++ (int)inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ if (offset == 1) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry .. ino %d", ++ (int)f->f_dentry->d_parent->d_inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, "..", 2, offset, ++ f->f_dentry->d_parent->d_inode->i_ino, ++ DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ if (f->f_version != inode->i_version) { ++ offset = 2; ++ f->f_pos = offset; ++ f->f_version = inode->i_version; ++ } ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= offset) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (filldir(dirent, ++ name, ++ strlen(name), ++ offset, this_inode, this_type) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ offset++; ++ f->f_pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++#endif ++ ++static const struct file_operations yaffs_dir_operations = { ++ .read = generic_read_dir, ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++ .iterate = yaffs_readdir, ++#else ++ .readdir = yaffs_readdir, ++#endif ++ .fsync = yaffs_sync_object, ++ .llseek = generic_file_llseek, ++}; ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj) ++{ ++ if (inode && obj) { ++ ++ /* Check mode against the variant type and attempt to repair if broken. */ ++ u32 mode = obj->yst_mode; ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (!S_ISREG(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFREG; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ if (!S_ISLNK(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFLNK; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!S_ISDIR(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFDIR; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ default: ++ /* TODO? */ ++ break; ++ } ++ ++ inode->i_flags |= S_NOATIME; ++ ++ inode->i_ino = obj->obj_id; ++ inode->i_mode = obj->yst_mode; ++ i_uid_write(inode, obj->yst_uid); ++ i_gid_write(inode, obj->yst_gid); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++ inode->i_blksize = inode->i_sb->s_blocksize; ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++ inode->i_rdev = old_decode_dev(obj->yst_rdev); ++ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); ++ inode->i_atime.tv_nsec = 0; ++ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; ++ inode->i_mtime.tv_nsec = 0; ++ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; ++ inode->i_ctime.tv_nsec = 0; ++#else ++ inode->i_rdev = obj->yst_rdev; ++ inode->i_atime = obj->yst_atime; ++ inode->i_mtime = obj->yst_mtime; ++ inode->i_ctime = obj->yst_ctime; ++#endif ++ inode->i_size = yaffs_get_obj_length(obj); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ ++ set_nlink(inode, yaffs_get_obj_link_count(obj)); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", ++ inode->i_mode, i_uid_read(inode), i_gid_read(inode), ++ inode->i_size, atomic_read(&inode->i_count)); ++ ++ switch (obj->yst_mode & S_IFMT) { ++ default: /* fifo, device or socket */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ init_special_inode(inode, obj->yst_mode, ++ old_decode_dev(obj->yst_rdev)); ++#else ++ init_special_inode(inode, obj->yst_mode, ++ (dev_t) (obj->yst_rdev)); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ inode->i_op = &yaffs_file_inode_operations; ++ inode->i_fop = &yaffs_file_operations; ++ inode->i_mapping->a_ops = ++ &yaffs_file_address_operations; ++ break; ++ case S_IFDIR: /* directory */ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ break; ++ case S_IFLNK: /* symlink */ ++ inode->i_op = &yaffs_symlink_inode_operations; ++ break; ++ } ++ ++ yaffs_inode_to_obj_lv(inode) = obj; ++ ++ obj->my_inode = inode; ++ ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode invalid parameters"); ++ } ++ ++} ++ ++ ++ ++/* ++ * yaffs background thread functions . ++ * yaffs_bg_thread_fn() the thread function ++ * yaffs_bg_start() launches the background thread. ++ * yaffs_bg_stop() cleans up the background thread. ++ * ++ * NB: ++ * The thread should only run after the yaffs is initialised ++ * The thread should be stopped before yaffs is unmounted. ++ * The thread should not do any writing while the fs is in read only. ++ */ ++ ++static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) ++{ ++ unsigned erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned scattered = 0; /* Free chunks not in an erased block */ ++ ++ if (erased_chunks < dev->n_free_chunks) ++ scattered = (dev->n_free_chunks - erased_chunks); ++ ++ if (!context->bg_running) ++ return 0; ++ else if (scattered < (dev->param.chunks_per_block * 2)) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 2) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 4) ++ return 1; ++ else ++ return 2; ++} ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++ ++void yaffs_background_waker(unsigned long data) ++{ ++ wake_up_process((struct task_struct *)data); ++} ++ ++static int yaffs_bg_thread_fn(void *data) ++{ ++ struct yaffs_dev *dev = (struct yaffs_dev *)data; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned long now = jiffies; ++ unsigned long next_dir_update = now; ++ unsigned long next_gc = now; ++ unsigned long expires; ++ unsigned int urgency; ++ ++ int gc_result; ++ struct timer_list timer; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "yaffs_background starting for dev %p", (void *)dev); ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ set_freezable(); ++#endif ++ while (context->bg_running) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); ++ ++ if (kthread_should_stop()) ++ break; ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ if (try_to_freeze()) ++ continue; ++#endif ++ yaffs_gross_lock(dev); ++ ++ now = jiffies; ++ ++ if (time_after(now, next_dir_update) && yaffs_bg_enable) { ++ yaffs_update_dirty_dirs(dev); ++ next_dir_update = now + HZ; ++ } ++ ++ if (time_after(now, next_gc) && yaffs_bg_enable) { ++ if (!dev->is_checkpointed) { ++ urgency = yaffs_bg_gc_urgency(dev); ++ gc_result = yaffs_bg_gc(dev, urgency); ++ if (urgency > 1) ++ next_gc = now + HZ / 20 + 1; ++ else if (urgency > 0) ++ next_gc = now + HZ / 10 + 1; ++ else ++ next_gc = now + HZ * 2; ++ } else { ++ /* ++ * gc not running so set to next_dir_update ++ * to cut down on wake ups ++ */ ++ next_gc = next_dir_update; ++ } ++ } ++ yaffs_gross_unlock(dev); ++#if 1 ++ expires = next_dir_update; ++ if (time_before(next_gc, expires)) ++ expires = next_gc; ++ if (time_before(expires, now)) ++ expires = now + HZ; ++ ++ Y_INIT_TIMER(&timer); ++ timer.expires = expires + 1; ++ timer.data = (unsigned long)current; ++ timer.function = yaffs_background_waker; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_timer(&timer); ++ schedule(); ++ del_timer_sync(&timer); ++#else ++ msleep(10); ++#endif ++ } ++ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ int retval = 0; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ ++ if (dev->read_only) ++ return -1; ++ ++ context->bg_running = 1; ++ ++ context->bg_thread = kthread_run(yaffs_bg_thread_fn, ++ (void *)dev, "yaffs-bg-%d", ++ context->mount_id); ++ ++ if (IS_ERR(context->bg_thread)) { ++ retval = PTR_ERR(context->bg_thread); ++ context->bg_thread = NULL; ++ context->bg_running = 0; ++ } ++ return retval; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); ++ ++ ctxt->bg_running = 0; ++ ++ if (ctxt->bg_thread) { ++ kthread_stop(ctxt->bg_thread); ++ ctxt->bg_thread = NULL; ++ } ++} ++#else ++static int yaffs_bg_thread_fn(void *data) ++{ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ return 0; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++} ++#endif ++ ++ ++static void yaffs_flush_inodes(struct super_block *sb) ++{ ++ struct inode *iptr; ++ struct yaffs_obj *obj; ++ ++ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { ++ obj = yaffs_inode_to_obj(iptr); ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "flushing obj %d", ++ obj->obj_id); ++ yaffs_flush_file(obj, 1, 0); ++ } ++ } ++} ++ ++static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ if (!dev) ++ return; ++ ++ yaffs_flush_inodes(sb); ++ yaffs_update_dirty_dirs(dev); ++ yaffs_flush_whole_cache(dev); ++ if (do_checkpoint) ++ yaffs_checkpoint_save(dev); ++} ++ ++static LIST_HEAD(yaffs_context_list); ++struct mutex yaffs_context_lock; ++ ++static void yaffs_put_super(struct super_block *sb) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super"); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "Shutting down yaffs background thread"); ++ yaffs_bg_stop(dev); ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "yaffs background thread shut down"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_super(sb, 1); ++ ++ yaffs_deinitialise(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ mutex_lock(&yaffs_context_lock); ++ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); ++ mutex_unlock(&yaffs_context_lock); ++ ++ if (yaffs_dev_to_lc(dev)->spare_buffer) { ++ kfree(yaffs_dev_to_lc(dev)->spare_buffer); ++ yaffs_dev_to_lc(dev)->spare_buffer = NULL; ++ } ++ ++ kfree(dev); ++ ++ yaffs_put_mtd_device(mtd); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super done"); ++} ++ ++ ++static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) ++{ ++ return yaffs_gc_control; ++} ++ ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ ++static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, ++ uint32_t generation) ++{ ++ return Y_IGET(sb, ino); ++} ++ ++static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_parent(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++struct dentry *yaffs2_get_parent(struct dentry *dentry) ++{ ++ ++ struct super_block *sb = dentry->d_inode->i_sb; ++ struct dentry *parent = ERR_PTR(-ENOENT); ++ struct inode *inode; ++ unsigned long parent_ino; ++ struct yaffs_obj *d_obj; ++ struct yaffs_obj *parent_obj; ++ ++ d_obj = yaffs_inode_to_obj(dentry->d_inode); ++ ++ if (d_obj) { ++ parent_obj = d_obj->parent; ++ if (parent_obj) { ++ parent_ino = yaffs_get_obj_inode(parent_obj); ++ inode = Y_IGET(sb, parent_ino); ++ ++ if (IS_ERR(inode)) { ++ parent = ERR_CAST(inode); ++ } else { ++ parent = d_obtain_alias(inode); ++ if (!IS_ERR(parent)) { ++ parent = ERR_PTR(-ENOMEM); ++ iput(inode); ++ } ++ } ++ } ++ } ++ ++ return parent; ++} ++ ++/* Just declare a zero structure as a NULL value implies ++ * using the default functions of exportfs. ++ */ ++ ++static struct export_operations yaffs_export_ops = { ++ .fh_to_dentry = yaffs2_fh_to_dentry, ++ .fh_to_parent = yaffs2_fh_to_parent, ++ .get_parent = yaffs2_get_parent, ++}; ++ ++#endif ++ ++static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) ++{ ++ /* Clear the association between the inode and ++ * the struct yaffs_obj. ++ */ ++ obj->my_inode = NULL; ++ yaffs_inode_to_obj_lv(inode) = NULL; ++ ++ /* If the object freeing was deferred, then the real ++ * free happens now. ++ * This should fix the inode inconsistency problem. ++ */ ++ yaffs_handle_defered_free(obj); ++} ++ ++#ifdef YAFFS_HAS_EVICT_INODE ++/* yaffs_evict_inode combines into one operation what was previously done in ++ * yaffs_clear_inode() and yaffs_delete_inode() ++ * ++ */ ++static void yaffs_evict_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ int deleteme = 0; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_evict_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (!inode->i_nlink && !is_bad_inode(inode)) ++ deleteme = 1; ++ truncate_inode_pages(&inode->i_data, 0); ++ Y_CLEAR_INODE(inode); ++ ++ if (deleteme && obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++} ++#else ++ ++/* clear is called to tell the fs to release any per-inode data it holds. ++ * The object might still exist on disk and is just being thrown out of the cache ++ * or else the object has actually been deleted and we're being called via ++ * the chain ++ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() ++ */ ++ ++static void yaffs_clear_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_clear_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++ ++} ++ ++/* delete is called when the link count is zero and the inode ++ * is put (ie. nobody wants to know about it anymore, time to ++ * delete the file). ++ * NB Must call clear_inode() ++ */ ++static void yaffs_delete_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_delete_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++ truncate_inode_pages(&inode->i_data, 0); ++#endif ++ clear_inode(inode); ++} ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ struct super_block *sb = dentry->d_sb; ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#else ++static int yaffs_statfs(struct super_block *sb, struct statfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#endif ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); ++ ++ yaffs_gross_lock(dev); ++ ++ buf->f_type = YAFFS_MAGIC; ++ buf->f_bsize = sb->s_blocksize; ++ buf->f_namelen = 255; ++ ++ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { ++ /* Do this if chunk size is not a power of 2 */ ++ ++ uint64_t bytes_in_dev; ++ uint64_t bytes_free; ++ ++ bytes_in_dev = ++ ((uint64_t) ++ ((dev->param.end_block - dev->param.start_block + ++ 1))) * ((uint64_t) (dev->param.chunks_per_block * ++ dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ ++ buf->f_blocks = bytes_in_dev; ++ ++ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * ++ ((uint64_t) (dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_free, sb->s_blocksize); ++ ++ buf->f_bfree = bytes_free; ++ ++ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { ++ ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ } else { ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ } ++ ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_bavail = buf->f_bfree; ++ ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++ ++static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) ++{ ++ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); ++ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); ++ int do_checkpoint; ++ int dirty = yaffs_check_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", ++ gc_urgent, ++ dirty ? "dirty" : "clean", ++ request_checkpoint ? "checkpoint requested" : "no checkpoint", ++ oneshot_checkpoint ? " one-shot" : ""); ++ ++ yaffs_gross_lock(dev); ++ do_checkpoint = ((request_checkpoint && !gc_urgent) || ++ oneshot_checkpoint) && !dev->is_checkpointed; ++ ++ if (dirty || do_checkpoint) { ++ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); ++ yaffs_clear_super_dirty(dev); ++ if (oneshot_checkpoint) ++ yaffs_auto_checkpoint &= ~4; ++ } ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#ifdef YAFFS_HAS_WRITE_SUPER ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static void yaffs_write_super(struct super_block *sb) ++#else ++static int yaffs_write_super(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_write_super %s", ++ request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) ++ return 0; ++#endif ++} ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_sync_fs(struct super_block *sb, int wait) ++#else ++static int yaffs_sync_fs(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++ return 0; ++} ++ ++ ++ ++static const struct super_operations yaffs_super_ops = { ++ .statfs = yaffs_statfs, ++ ++#ifndef YAFFS_USE_OWN_IGET ++ .read_inode = yaffs_read_inode, ++#endif ++#ifdef YAFFS_HAS_PUT_INODE ++ .put_inode = yaffs_put_inode, ++#endif ++ .put_super = yaffs_put_super, ++#ifdef YAFFS_HAS_EVICT_INODE ++ .evict_inode = yaffs_evict_inode, ++#else ++ .delete_inode = yaffs_delete_inode, ++ .clear_inode = yaffs_clear_inode, ++#endif ++ .sync_fs = yaffs_sync_fs, ++#ifdef YAFFS_HAS_WRITE_SUPER ++ .write_super = yaffs_write_super, ++#endif ++}; ++ ++struct yaffs_options { ++ int inband_tags; ++ int skip_checkpoint_read; ++ int skip_checkpoint_write; ++ int no_cache; ++ int tags_ecc_on; ++ int tags_ecc_overridden; ++ int lazy_loading_enabled; ++ int lazy_loading_overridden; ++ int empty_lost_and_found; ++ int empty_lost_and_found_overridden; ++ int disable_summary; ++}; ++ ++#define MAX_OPT_LEN 30 ++static int yaffs_parse_options(struct yaffs_options *options, ++ const char *options_str) ++{ ++ char cur_opt[MAX_OPT_LEN + 1]; ++ int p; ++ int error = 0; ++ ++ /* Parse through the options which is a comma seperated list */ ++ ++ while (options_str && *options_str && !error) { ++ memset(cur_opt, 0, MAX_OPT_LEN + 1); ++ p = 0; ++ ++ while (*options_str == ',') ++ options_str++; ++ ++ while (*options_str && *options_str != ',') { ++ if (p < MAX_OPT_LEN) { ++ cur_opt[p] = *options_str; ++ p++; ++ } ++ options_str++; ++ } ++ ++ if (!strcmp(cur_opt, "inband-tags")) { ++ options->inband_tags = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-off")) { ++ options->tags_ecc_on = 0; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-on")) { ++ options->tags_ecc_on = 1; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-off")) { ++ options->lazy_loading_enabled = 0; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-on")) { ++ options->lazy_loading_enabled = 1; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "disable-summary")) { ++ options->disable_summary = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { ++ options->empty_lost_and_found = 0; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { ++ options->empty_lost_and_found = 1; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "no-cache")) { ++ options->no_cache = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { ++ options->skip_checkpoint_read = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { ++ options->skip_checkpoint_write = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint")) { ++ options->skip_checkpoint_read = 1; ++ options->skip_checkpoint_write = 1; ++ } else { ++ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", ++ cur_opt); ++ error = 1; ++ } ++ } ++ ++ return error; ++} ++ ++ ++static struct dentry *yaffs_make_root(struct inode *inode) ++{ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++ struct dentry *root = d_alloc_root(inode); ++ ++ if (!root) ++ iput(inode); ++ ++ return root; ++#else ++ return d_make_root(inode); ++#endif ++} ++ ++ ++ ++ ++static struct super_block *yaffs_internal_read_super(int yaffs_version, ++ struct super_block *sb, ++ void *data, int silent) ++{ ++ int n_blocks; ++ struct inode *inode = NULL; ++ struct dentry *root; ++ struct yaffs_dev *dev = 0; ++ char devname_buf[BDEVNAME_SIZE + 1]; ++ struct mtd_info *mtd; ++ int err; ++ char *data_str = (char *)data; ++ struct yaffs_linux_context *context = NULL; ++ struct yaffs_param *param; ++ ++ int read_only = 0; ++ int inband_tags = 0; ++ ++ struct yaffs_options options; ++ ++ unsigned mount_id; ++ int found; ++ struct yaffs_linux_context *context_iterator; ++ struct list_head *l; ++ ++ if (!sb) { ++ printk(KERN_INFO "yaffs: sb is NULL\n"); ++ return NULL; ++ } ++ ++ sb->s_magic = YAFFS_MAGIC; ++ sb->s_op = &yaffs_super_ops; ++ sb->s_flags |= MS_NOATIME; ++ ++ read_only = ((sb->s_flags & MS_RDONLY) != 0); ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ sb->s_export_op = &yaffs_export_ops; ++#endif ++ ++ if (!sb->s_dev) ++ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); ++ else if (!yaffs_devname(sb, devname_buf)) ++ printk(KERN_INFO "yaffs: devname is NULL\n"); ++ else ++ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", ++ sb->s_dev, ++ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); ++ ++ if (!data_str) ++ data_str = ""; ++ ++ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); ++ ++ memset(&options, 0, sizeof(options)); ++ ++ if (yaffs_parse_options(&options, data_str)) { ++ /* Option parsing failed */ ++ return NULL; ++ } ++ ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: Using yaffs%d", yaffs_version); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", ++ MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ yaffs_devname(sb, devname_buf)); ++ ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (IS_ERR(mtd)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device %u either not valid or unavailable", ++ MINOR(sb->s_dev)); ++ return NULL; ++ } ++ ++ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); ++ yaffs_version = 2; ++ } ++ ++ /* Added NCB 26/5/2006 for completeness */ ++ if (yaffs_version == 2 && !options.inband_tags ++ && WRITE_SIZE(mtd) == 512) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); ++ yaffs_version = 1; ++ } ++ ++ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || ++ options.inband_tags) ++ inband_tags = 1; ++ ++ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) ++ return NULL; ++ ++ /* OK, so if we got here, we have an MTD that's NAND and looks ++ * like it has the right capabilities ++ * Set the struct yaffs_dev up for mtd ++ */ ++ ++ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { ++ read_only = 1; ++ printk(KERN_INFO ++ "yaffs: mtd is read only, setting superblock read only\n" ++ ); ++ sb->s_flags |= MS_RDONLY; ++ } ++ ++ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); ++ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); ++ ++ if (!dev || !context) { ++ kfree(dev); ++ kfree(context); ++ dev = NULL; ++ context = NULL; ++ ++ /* Deep shit could not allocate device structure */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." ++ ); ++ return NULL; ++ } ++ memset(dev, 0, sizeof(struct yaffs_dev)); ++ param = &(dev->param); ++ ++ memset(context, 0, sizeof(struct yaffs_linux_context)); ++ dev->os_context = context; ++ INIT_LIST_HEAD(&(context->context_list)); ++ context->dev = dev; ++ context->super = sb; ++ ++ dev->read_only = read_only; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ sb->s_fs_info = dev; ++#else ++ sb->u.generic_sbp = dev; ++#endif ++ ++ ++ dev->driver_context = mtd; ++ param->name = mtd->name; ++ ++ /* Set up the memory size parameters.... */ ++ ++ ++ param->n_reserved_blocks = 5; ++ param->n_caches = (options.no_cache) ? 0 : 10; ++ param->inband_tags = inband_tags; ++ ++ param->enable_xattr = 1; ++ if (options.lazy_loading_overridden) ++ param->disable_lazy_load = !options.lazy_loading_enabled; ++ ++ param->defered_dir_update = 1; ++ ++ if (options.tags_ecc_overridden) ++ param->no_tags_ecc = !options.tags_ecc_on; ++ ++ param->empty_lost_n_found = 1; ++ param->refresh_period = 500; ++ param->disable_summary = options.disable_summary; ++ ++ ++#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING ++ param->disable_bad_block_marking = 1; ++#endif ++ if (options.empty_lost_and_found_overridden) ++ param->empty_lost_n_found = options.empty_lost_and_found; ++ ++ /* ... and the functions. */ ++ if (yaffs_version == 2) { ++ param->is_yaffs2 = 1; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++ param->total_bytes_per_chunk = mtd->writesize; ++ param->chunks_per_block = mtd->erasesize / mtd->writesize; ++#else ++ param->total_bytes_per_chunk = mtd->oobblock; ++ param->chunks_per_block = mtd->erasesize / mtd->oobblock; ++#endif ++ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ } else { ++ param->is_yaffs2 = 0; ++ n_blocks = YCALCBLOCKS(mtd->size, ++ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); ++ ++ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; ++ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; ++ } ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ ++ yaffs_mtd_drv_install(dev); ++ ++ param->sb_dirty_fn = yaffs_set_super_dirty; ++ param->gc_control_fn = yaffs_gc_control_callback; ++ ++ yaffs_dev_to_lc(dev)->super = sb; ++ ++ param->use_nand_ecc = 1; ++ ++ param->skip_checkpt_rd = options.skip_checkpoint_read; ++ param->skip_checkpt_wr = options.skip_checkpoint_write; ++ ++ mutex_lock(&yaffs_context_lock); ++ /* Get a mount id */ ++ found = 0; ++ for (mount_id = 0; !found; mount_id++) { ++ found = 1; ++ list_for_each(l, &yaffs_context_list) { ++ context_iterator = ++ list_entry(l, struct yaffs_linux_context, ++ context_list); ++ if (context_iterator->mount_id == mount_id) ++ found = 0; ++ } ++ } ++ context->mount_id = mount_id; ++ ++ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), ++ &yaffs_context_list); ++ mutex_unlock(&yaffs_context_lock); ++ ++ /* Directory search handling... */ ++ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); ++ param->remove_obj_fn = yaffs_remove_obj_callback; ++ ++ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ ++ yaffs_gross_lock(dev); ++ ++ err = yaffs_guts_initialise(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: guts initialised %s", ++ (err == YAFFS_OK) ? "OK" : "FAILED"); ++ ++ if (err == YAFFS_OK) ++ yaffs_bg_start(dev); ++ ++ if (!context->bg_thread) ++ param->defered_dir_update = 0; ++ ++ sb->s_maxbytes = yaffs_max_file_size(dev); ++ ++ /* Release lock before yaffs_get_inode() */ ++ yaffs_gross_unlock(dev); ++ ++ /* Create root inode */ ++ if (err == YAFFS_OK) ++ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); ++ ++ if (!inode) ++ return NULL; ++ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); ++ ++ root = yaffs_make_root(inode); ++ ++ if (!root) ++ return NULL; ++ ++ sb->s_root = root; ++ if(!dev->is_checkpointed) ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); ++ return sb; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data, struct vfsmount *mnt) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs_mount, ++#else ++ .get_sb = yaffs_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs_read_super(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, void *data, ++ struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs2_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs2", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs2_mount, ++#else ++ .get_sb = yaffs2_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs2_read_super(struct super_block *sb, ++ void *data, int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) ++static struct proc_dir_entry *my_proc_entry; ++ ++static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) ++{ ++ struct yaffs_param *param = &dev->param; ++ int bs[10]; ++ ++ yaffs_count_blocks_by_state(dev,bs); ++ ++ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); ++ buf += sprintf(buf, "end_block............ %d\n", param->end_block); ++ buf += sprintf(buf, "total_bytes_per_chunk %d\n", ++ param->total_bytes_per_chunk); ++ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); ++ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); ++ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); ++ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); ++ buf += sprintf(buf, "empty_lost_n_found... %d\n", ++ param->empty_lost_n_found); ++ buf += sprintf(buf, "disable_lazy_load.... %d\n", ++ param->disable_lazy_load); ++ buf += sprintf(buf, "disable_bad_block_mrk %d\n", ++ param->disable_bad_block_marking); ++ buf += sprintf(buf, "refresh_period....... %d\n", ++ param->refresh_period); ++ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); ++ buf += sprintf(buf, "n_reserved_blocks.... %d\n", ++ param->n_reserved_blocks); ++ buf += sprintf(buf, "always_check_erased.. %d\n", ++ param->always_check_erased); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "block count by state\n"); ++ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", ++ bs[0], bs[1], bs[2], bs[3], bs[4]); ++ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", ++ bs[5], bs[6], bs[7], bs[8], bs[9]); ++ ++ return buf; ++} ++ ++static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) ++{ ++ buf += sprintf(buf, "max file size....... %lld\n", ++ (long long) yaffs_max_file_size(dev)); ++ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", ++ dev->data_bytes_per_chunk); ++ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); ++ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); ++ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); ++ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", ++ dev->blocks_in_checkpt); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); ++ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); ++ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); ++ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); ++ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); ++ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); ++ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); ++ buf += sprintf(buf, "passive_gc_count..... %u\n", ++ dev->passive_gc_count); ++ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", ++ dev->oldest_dirty_gc_count); ++ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); ++ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); ++ buf += sprintf(buf, "n_retried_writes..... %u\n", ++ dev->n_retried_writes); ++ buf += sprintf(buf, "n_retired_blocks..... %u\n", ++ dev->n_retired_blocks); ++ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); ++ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); ++ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", ++ dev->n_tags_ecc_fixed); ++ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", ++ dev->n_tags_ecc_unfixed); ++ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); ++ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); ++ buf += sprintf(buf, "n_unlinked_files..... %u\n", ++ dev->n_unlinked_files); ++ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); ++ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); ++ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); ++ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); ++ ++ return buf; ++} ++ ++static int yaffs_proc_read(char *page, ++ char **start, ++ off_t offset, int count, int *eof, void *data) ++{ ++ struct list_head *item; ++ char *buf = page; ++ int step = offset; ++ int n = 0; ++ ++ /* Get proc_file_read() to step 'offset' by one on each sucessive call. ++ * We use 'offset' (*ppos) to indicate where we are in dev_list. ++ * This also assumes the user has posted a read buffer large ++ * enough to hold the complete output; but that's life in /proc. ++ */ ++ ++ *(int *)start = 1; ++ ++ /* Print header first */ ++ if (step == 0) ++ buf += ++ sprintf(buf, ++ "Multi-version YAFFS built:" __DATE__ " " __TIME__ ++ "\n"); ++ else if (step == 1) ++ buf += sprintf(buf, "\n"); ++ else { ++ step -= 2; ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (n < (step & ~1)) { ++ n += 2; ++ continue; ++ } ++ if ((step & 1) == 0) { ++ buf += ++ sprintf(buf, "\nDevice %d \"%s\"\n", n, ++ dev->param.name); ++ buf = yaffs_dump_dev_part0(buf, dev); ++ } else { ++ buf = yaffs_dump_dev_part1(buf, dev); ++ } ++ ++ break; ++ } ++ mutex_unlock(&yaffs_context_lock); ++ } ++ ++ return buf - page < count ? buf - page : count; ++} ++ ++/** ++ * Set the verbosity of the warnings and error messages. ++ * ++ * Note that the names can only be a..z or _ with the current code. ++ */ ++ ++static struct { ++ char *mask_name; ++ unsigned mask_bitfield; ++} mask_flags[] = { ++ {"allocate", YAFFS_TRACE_ALLOCATE}, ++ {"always", YAFFS_TRACE_ALWAYS}, ++ {"background", YAFFS_TRACE_BACKGROUND}, ++ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, ++ {"buffers", YAFFS_TRACE_BUFFERS}, ++ {"bug", YAFFS_TRACE_BUG}, ++ {"checkpt", YAFFS_TRACE_CHECKPOINT}, ++ {"deletion", YAFFS_TRACE_DELETION}, ++ {"erase", YAFFS_TRACE_ERASE}, ++ {"error", YAFFS_TRACE_ERROR}, ++ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, ++ {"gc", YAFFS_TRACE_GC}, ++ {"lock", YAFFS_TRACE_LOCK}, ++ {"mtd", YAFFS_TRACE_MTD}, ++ {"nandaccess", YAFFS_TRACE_NANDACCESS}, ++ {"os", YAFFS_TRACE_OS}, ++ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, ++ {"scan", YAFFS_TRACE_SCAN}, ++ {"mount", YAFFS_TRACE_MOUNT}, ++ {"tracing", YAFFS_TRACE_TRACING}, ++ {"sync", YAFFS_TRACE_SYNC}, ++ {"write", YAFFS_TRACE_WRITE}, ++ {"verify", YAFFS_TRACE_VERIFY}, ++ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, ++ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, ++ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, ++ {"all", 0xffffffff}, ++ {"none", 0}, ++ {NULL, 0}, ++}; ++ ++#define MAX_MASK_NAME_LENGTH 40 ++static int yaffs_proc_write_trace_options(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ unsigned rg = 0, mask_bitfield; ++ char *end; ++ char *mask_name; ++ const char *x; ++ char substring[MAX_MASK_NAME_LENGTH + 1]; ++ int i; ++ int done = 0; ++ int add, len = 0; ++ int pos = 0; ++ ++ rg = yaffs_trace_mask; ++ ++ while (!done && (pos < count)) { ++ done = 1; ++ while ((pos < count) && isspace(buf[pos])) ++ pos++; ++ ++ switch (buf[pos]) { ++ case '+': ++ case '-': ++ case '=': ++ add = buf[pos]; ++ pos++; ++ break; ++ ++ default: ++ add = ' '; ++ break; ++ } ++ mask_name = NULL; ++ ++ mask_bitfield = simple_strtoul(buf + pos, &end, 0); ++ ++ if (end > buf + pos) { ++ mask_name = "numeral"; ++ len = end - (buf + pos); ++ pos += len; ++ done = 0; ++ } else { ++ for (x = buf + pos, i = 0; ++ (*x == '_' || (*x >= 'a' && *x <= 'z')) && ++ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) ++ substring[i] = *x; ++ substring[i] = '\0'; ++ ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ if (strcmp(substring, mask_flags[i].mask_name) ++ == 0) { ++ mask_name = mask_flags[i].mask_name; ++ mask_bitfield = ++ mask_flags[i].mask_bitfield; ++ done = 0; ++ break; ++ } ++ } ++ } ++ ++ if (mask_name != NULL) { ++ done = 0; ++ switch (add) { ++ case '-': ++ rg &= ~mask_bitfield; ++ break; ++ case '+': ++ rg |= mask_bitfield; ++ break; ++ case '=': ++ rg = mask_bitfield; ++ break; ++ default: ++ rg |= mask_bitfield; ++ break; ++ } ++ } ++ } ++ ++ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; ++ ++ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); ++ ++ if (rg & YAFFS_TRACE_ALWAYS) { ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ char flag; ++ flag = ((rg & mask_flags[i].mask_bitfield) == ++ mask_flags[i].mask_bitfield) ? '+' : '-'; ++ printk(KERN_DEBUG "%c%s\n", flag, ++ mask_flags[i].mask_name); ++ } ++ } ++ ++ return count; ++} ++ ++/* Debug strings are of the form: ++ * .bnnn print info on block n ++ * .cobjn,chunkn print nand chunk id for objn:chunkn ++ */ ++ ++static int yaffs_proc_debug_write(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ ++ char str[100]; ++ char *p0; ++ char *p1; ++ long p1_val; ++ long p0_val; ++ char cmd; ++ struct list_head *item; ++ ++ memset(str, 0, sizeof(str)); ++ memcpy(str, buf, min(count, sizeof(str) -1)); ++ ++ cmd = str[1]; ++ ++ p0 = str + 2; ++ ++ p1 = p0; ++ ++ while (*p1 && *p1 != ',') { ++ p1++; ++ } ++ *p1 = '\0'; ++ p1++; ++ ++ p0_val = simple_strtol(p0, NULL, 0); ++ p1_val = simple_strtol(p1, NULL, 0); ++ ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (cmd == 'b') { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev,p0_val); ++ ++ if(bi) { ++ printk("Block %d: state %d, retire %d, use %d, seq %d\n", ++ (int)p0_val, bi->block_state, ++ bi->needs_retiring, bi->pages_in_use, ++ bi->seq_number); ++ } ++ } else if (cmd == 'c') { ++ struct yaffs_obj *obj; ++ int nand_chunk; ++ ++ obj = yaffs_find_by_number(dev, p0_val); ++ if (!obj) ++ printk("No obj %d\n", (int)p0_val); ++ else { ++ if(p1_val == 0) ++ nand_chunk = obj->hdr_chunk; ++ else ++ nand_chunk = ++ yaffs_find_chunk_in_file(obj, ++ p1_val, NULL); ++ printk("Nand chunk for %d:%d is %d\n", ++ (int)p0_val, (int)p1_val, nand_chunk); ++ } ++ } ++ } ++ ++ mutex_unlock(&yaffs_context_lock); ++ ++ return count; ++} ++ ++static int yaffs_proc_write(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ if (buf[0] == '.') ++ return yaffs_proc_debug_write(file, buf, count, data); ++ return yaffs_proc_write_trace_options(file, buf, count, data); ++} ++#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) */ ++ ++/* Stuff to handle installation of file systems */ ++struct file_system_to_install { ++ struct file_system_type *fst; ++ int installed; ++}; ++ ++static struct file_system_to_install fs_to_install[] = { ++ {&yaffs_fs_type, 0}, ++ {&yaffs2_fs_type, 0}, ++ {NULL, 0} ++}; ++ ++static int __init init_yaffs_fs(void) ++{ ++ int error = 0; ++ struct file_system_to_install *fsinst; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs built " __DATE__ " " __TIME__ " Installing."); ++ ++ mutex_init(&yaffs_context_lock); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) ++ /* Install the proc_fs entries */ ++ my_proc_entry = create_proc_entry("yaffs", ++ S_IRUGO | S_IFREG, YPROC_ROOT); ++ ++ if (my_proc_entry) { ++ my_proc_entry->write_proc = yaffs_proc_write; ++ my_proc_entry->read_proc = yaffs_proc_read; ++ my_proc_entry->data = NULL; ++ } else { ++ return -ENOMEM; ++ } ++#endif ++ ++ /* Now add the file system entries */ ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst && !error) { ++ error = register_filesystem(fsinst->fst); ++ if (!error) ++ fsinst->installed = 1; ++ fsinst++; ++ } ++ ++ /* Any errors? uninstall */ ++ if (error) { ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++ } ++ ++ return error; ++} ++ ++static void __exit exit_yaffs_fs(void) ++{ ++ ++ struct file_system_to_install *fsinst; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs built " __DATE__ " " __TIME__ " removing."); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) ++ remove_proc_entry("yaffs", YPROC_ROOT); ++#endif ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++} ++ ++module_init(init_yaffs_fs) ++ module_exit(exit_yaffs_fs) ++ ++ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); ++MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.10.40/fs/yaffs2/yaffs_yaffs1.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,422 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_yaffs1.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++#include "yaffs_attribs.h" ++ ++int yaffs1_scan(struct yaffs_dev *dev) ++{ ++ struct yaffs_ext_tags tags; ++ int blk; ++ int result; ++ int chunk; ++ int c; ++ int deleted; ++ enum yaffs_block_state state; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int alloc_failed = 0; ++ struct yaffs_shadow_fixer *shadow_fixers = NULL; ++ u8 *chunk_data; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs1_scan starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, state, seq_number); ++ ++ if (state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } ++ bi++; ++ } ++ ++ /* For each block.... */ ++ for (blk = dev->internal_start_block; ++ !alloc_failed && blk <= dev->internal_end_block; blk++) { ++ ++ cond_resched(); ++ ++ bi = yaffs_get_block_info(dev, blk); ++ state = bi->block_state; ++ ++ deleted = 0; ++ ++ /* For each chunk in each block that needs scanning.... */ ++ for (c = 0; ++ !alloc_failed && c < dev->param.chunks_per_block && ++ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { ++ /* Read the tags and decide what to do */ ++ chunk = blk * dev->param.chunks_per_block + c; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, ++ &tags); ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || ++ tags.is_deleted) { ++ /* YAFFS1 only... ++ * A deleted chunk ++ */ ++ deleted++; ++ dev->n_free_chunks++; ++ } else if (!tags.chunk_used) { ++ /* An unassigned chunk in the block ++ * This means that either the block is empty or ++ * this is the one being allocated from ++ */ ++ ++ if (c == 0) { ++ /* We're looking at the first chunk in ++ *the block so the block is unused */ ++ state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ /* this is the block being allocated */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, c); ++ state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = c; ++ dev->alloc_block_finder = blk; ++ ++ } ++ ++ dev->n_free_chunks += ++ (dev->param.chunks_per_block - c); ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ unsigned int endpos; ++ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ /* PutChunkIntoFile checks for a clash ++ * (two data chunks with the same chunk_id). ++ */ ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in) { ++ if (!yaffs_put_chunk_in_file ++ (in, tags.chunk_id, chunk, 1)) ++ alloc_failed = 1; ++ } ++ ++ endpos = ++ (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk + ++ tags.n_bytes; ++ if (in && ++ in->variant_type == ++ YAFFS_OBJECT_TYPE_FILE && ++ in->variant.file_variant.scanned_size < ++ endpos) { ++ in->variant.file_variant.scanned_size = ++ endpos; ++ if (!dev->param.use_header_file_size) { ++ in->variant. ++ file_variant.file_size = ++ in->variant. ++ file_variant.scanned_size; ++ } ++ ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Make the object ++ */ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ in = yaffs_find_by_number(dev, tags.obj_id); ++ if (in && in->variant_type != oh->type) { ++ /* This should not happen, but somehow ++ * Wev'e ended up with an obj_id that ++ * has been reused but not yet deleted, ++ * and worse still it has changed type. ++ * Delete the old object. ++ */ ++ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ oh->type); ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in && oh->shadows_obj > 0) { ++ ++ struct yaffs_shadow_fixer *fixer; ++ fixer = ++ kmalloc(sizeof ++ (struct yaffs_shadow_fixer), ++ GFP_NOFS); ++ if (fixer) { ++ fixer->next = shadow_fixers; ++ shadow_fixers = fixer; ++ fixer->obj_id = tags.obj_id; ++ fixer->shadowed_id = ++ oh->shadows_obj; ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Shadow fixer: %d shadows %d", ++ fixer->obj_id, ++ fixer->shadowed_id); ++ ++ } ++ ++ } ++ ++ if (in && in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate and need to ++ * resolve it. */ ++ ++ unsigned existing_serial = in->serial; ++ unsigned new_serial = ++ tags.serial_number; ++ ++ if (((existing_serial + 1) & 3) == ++ new_serial) { ++ /* Use new one - destroy the ++ * exisiting one */ ++ yaffs_chunk_del(dev, ++ in->hdr_chunk, ++ 1, __LINE__); ++ in->valid = 0; ++ } else { ++ /* Use existing - destroy ++ * this one. */ ++ yaffs_chunk_del(dev, chunk, 1, ++ __LINE__); ++ } ++ } ++ ++ if (in && !in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == ++ YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ } else if (in && !in->valid) { ++ /* we need to load this info */ ++ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ in->dirty = 0; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ parent = ++ yaffs_find_or_create_by_number ++ (dev, oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ if (!parent) ++ alloc_failed = 1; ++ if (parent && parent->variant_type == ++ YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant. ++ children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, a problem.... ++ * We're trying to use a ++ * non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (dev->param. ++ use_header_file_size) ++ in->variant. ++ file_variant.file_size ++ = yaffs_oh_to_size(oh); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant. ++ hardlink_variant.equiv_id = ++ oh->equiv_id; ++ list_add(&in->hard_links, ++ &hard_list); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant. ++ alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant. ++ symlink_variant.alias) ++ alloc_failed = 1; ++ break; ++ } ++ } ++ } ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, ++ * then the block is fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ /* If the block was partially allocated then ++ * treat it as fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ bi->block_state = state; ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ yaffs_block_became_dirty(dev, blk); ++ } ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We should now have scanned all the objects, now it's time to add ++ * these hardlinks. ++ */ ++ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ /* ++ * Fix up any shadowed objects. ++ * There should not be more than one of these. ++ */ ++ { ++ struct yaffs_shadow_fixer *fixer; ++ struct yaffs_obj *obj; ++ ++ while (shadow_fixers) { ++ fixer = shadow_fixers; ++ shadow_fixers = fixer->next; ++ /* Complete the rename transaction by deleting the ++ * shadowed object then setting the object header ++ to unshadowed. ++ */ ++ obj = yaffs_find_by_number(dev, fixer->shadowed_id); ++ if (obj) ++ yaffs_del_obj(obj); ++ ++ obj = yaffs_find_by_number(dev, fixer->obj_id); ++ ++ if (obj) ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ kfree(fixer); ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.10.40/fs/yaffs2/yaffs_yaffs1.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS1_H__ ++#define __YAFFS_YAFFS1_H__ ++ ++#include "yaffs_guts.h" ++int yaffs1_scan(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.10.40/fs/yaffs2/yaffs_yaffs2.c +--- linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,1534 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_checkptrw.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_verify.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* ++ * Checkpoints are really no benefit on very small partitions. ++ * ++ * To save space on small partitions don't bother with checkpoints unless ++ * the partition is at least this big. ++ */ ++#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 ++#define YAFFS_SMALL_HOLE_THRESHOLD 4 ++ ++/* ++ * Oldest Dirty Sequence Number handling. ++ */ ++ ++/* yaffs_calc_oldest_dirty_seq() ++ * yaffs2_find_oldest_dirty_seq() ++ * Calculate the oldest dirty sequence number if we don't know it. ++ */ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ int i; ++ unsigned seq; ++ unsigned block_no = 0; ++ struct yaffs_block_info *b; ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ /* Find the oldest dirty sequence number. */ ++ seq = dev->seq_number + 1; ++ b = dev->block_info; ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ if (b->block_state == YAFFS_BLOCK_STATE_FULL && ++ (b->pages_in_use - b->soft_del_pages) < ++ dev->param.chunks_per_block && ++ b->seq_number < seq) { ++ seq = b->seq_number; ++ block_no = i; ++ } ++ b++; ++ } ++ ++ if (block_no) { ++ dev->oldest_dirty_seq = seq; ++ dev->oldest_dirty_block = block_no; ++ } ++} ++ ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->oldest_dirty_seq) ++ yaffs_calc_oldest_dirty_seq(dev); ++} ++ ++/* ++ * yaffs_clear_oldest_dirty_seq() ++ * Called when a block is erased or marked bad. (ie. when its seq_number ++ * becomes invalid). If the value matches the oldest then we clear ++ * dev->oldest_dirty_seq to force its recomputation. ++ */ ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ } ++} ++ ++/* ++ * yaffs2_update_oldest_dirty_seq() ++ * Update the oldest dirty sequence number whenever we dirty a block. ++ * Only do this if the oldest_dirty_seq is actually being tracked. ++ */ ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (dev->oldest_dirty_seq) { ++ if (dev->oldest_dirty_seq > bi->seq_number) { ++ dev->oldest_dirty_seq = bi->seq_number; ++ dev->oldest_dirty_block = block_no; ++ } ++ } ++} ++ ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return 1; /* disqualification only applies to yaffs2. */ ++ ++ if (!bi->has_shrink_hdr) ++ return 1; /* can gc */ ++ ++ yaffs2_find_oldest_dirty_seq(dev); ++ ++ /* Can't do gc of this block if there are any blocks older than this ++ * one that have discarded pages. ++ */ ++ return (bi->seq_number <= dev->oldest_dirty_seq); ++} ++ ++/* ++ * yaffs2_find_refresh_block() ++ * periodically finds the oldest full block by sequence number for refreshing. ++ * Only for yaffs2. ++ */ ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) ++{ ++ u32 b; ++ u32 oldest = 0; ++ u32 oldest_seq = 0; ++ struct yaffs_block_info *bi; ++ ++ if (!dev->param.is_yaffs2) ++ return oldest; ++ ++ /* ++ * If refresh period < 10 then refreshing is disabled. ++ */ ++ if (dev->param.refresh_period < 10) ++ return oldest; ++ ++ /* ++ * Fix broken values. ++ */ ++ if (dev->refresh_skip > dev->param.refresh_period) ++ dev->refresh_skip = dev->param.refresh_period; ++ ++ if (dev->refresh_skip > 0) ++ return oldest; ++ ++ /* ++ * Refresh skip is now zero. ++ * We'll do a refresh this time around.... ++ * Update the refresh skip and find the oldest block. ++ */ ++ dev->refresh_skip = dev->param.refresh_period; ++ dev->refresh_count++; ++ bi = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ ++ if (oldest < 1 || bi->seq_number < oldest_seq) { ++ oldest = b; ++ oldest_seq = bi->seq_number; ++ } ++ } ++ bi++; ++ } ++ ++ if (oldest > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC refresh count %d selected block %d with seq_number %d", ++ dev->refresh_count, oldest, oldest_seq); ++ } ++ ++ return oldest; ++} ++ ++int yaffs2_checkpt_required(struct yaffs_dev *dev) ++{ ++ int nblocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ nblocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ return !dev->param.skip_checkpt_wr && ++ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); ++} ++ ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) ++{ ++ int retval; ++ int n_bytes = 0; ++ int n_blocks; ++ int dev_blocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { ++ /* Not a valid value so recalculate */ ++ dev_blocks = dev->param.end_block - dev->param.start_block + 1; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(struct yaffs_checkpt_dev); ++ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); ++ n_bytes += dev_blocks * dev->chunk_bit_stride; ++ n_bytes += ++ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * ++ dev->n_obj; ++ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(u32); /* checksum */ ++ ++ /* Round up and add 2 blocks to allow for some bad blocks, ++ * so add 3 */ ++ ++ n_blocks = ++ (n_bytes / ++ (dev->data_bytes_per_chunk * ++ dev->param.chunks_per_block)) + 3; ++ ++ dev->checkpoint_blocks_required = n_blocks; ++ } ++ ++ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; ++ if (retval < 0) ++ retval = 0; ++ return retval; ++} ++ ++/*--------------------- Checkpointing --------------------*/ ++ ++static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ ++ memset(&cp, 0, sizeof(cp)); ++ ++ cp.struct_type = sizeof(cp); ++ cp.magic = YAFFS_MAGIC; ++ cp.version = YAFFS_CHECKPOINT_VERSION; ++ cp.head = (head) ? 1 : 0; ++ ++ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ if (ok) ++ ok = (cp.struct_type == sizeof(cp)) && ++ (cp.magic == YAFFS_MAGIC) && ++ (cp.version == YAFFS_CHECKPOINT_VERSION) && ++ (cp.head == ((head) ? 1 : 0)); ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, ++ struct yaffs_dev *dev) ++{ ++ cp->n_erased_blocks = dev->n_erased_blocks; ++ cp->alloc_block = dev->alloc_block; ++ cp->alloc_page = dev->alloc_page; ++ cp->n_free_chunks = dev->n_free_chunks; ++ ++ cp->n_deleted_files = dev->n_deleted_files; ++ cp->n_unlinked_files = dev->n_unlinked_files; ++ cp->n_bg_deletions = dev->n_bg_deletions; ++ cp->seq_number = dev->seq_number; ++ ++} ++ ++static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, ++ struct yaffs_checkpt_dev *cp) ++{ ++ dev->n_erased_blocks = cp->n_erased_blocks; ++ dev->alloc_block = cp->alloc_block; ++ dev->alloc_page = cp->alloc_page; ++ dev->n_free_chunks = cp->n_free_chunks; ++ ++ dev->n_deleted_files = cp->n_deleted_files; ++ dev->n_unlinked_files = cp->n_unlinked_files; ++ dev->n_bg_deletions = cp->n_bg_deletions; ++ dev->seq_number = cp->seq_number; ++} ++ ++static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ int ok; ++ ++ /* Write device runtime values */ ++ yaffs2_dev_to_checkpt_dev(&cp, dev); ++ cp.struct_type = sizeof(cp); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ /* Write block info */ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); ++ if (!ok) ++ return 0; ++ ++ /* Write chunk bits */ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = ++ (dev->internal_end_block - dev->internal_start_block + 1); ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ if (cp.struct_type != sizeof(cp)) ++ return 0; ++ ++ yaffs_checkpt_dev_to_dev(dev, &cp); ++ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); ++ ++ if (!ok) ++ return 0; ++ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, ++ struct yaffs_obj *obj) ++{ ++ cp->obj_id = obj->obj_id; ++ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; ++ cp->hdr_chunk = obj->hdr_chunk; ++ cp->variant_type = obj->variant_type; ++ cp->deleted = obj->deleted; ++ cp->soft_del = obj->soft_del; ++ cp->unlinked = obj->unlinked; ++ cp->fake = obj->fake; ++ cp->rename_allowed = obj->rename_allowed; ++ cp->unlink_allowed = obj->unlink_allowed; ++ cp->serial = obj->serial; ++ cp->n_data_chunks = obj->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; ++} ++ ++static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, ++ struct yaffs_checkpt_obj *cp) ++{ ++ struct yaffs_obj *parent; ++ ++ if (obj->variant_type != cp->variant_type) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", ++ cp->obj_id, cp->variant_type, cp->hdr_chunk, ++ obj->variant_type); ++ return 0; ++ } ++ ++ obj->obj_id = cp->obj_id; ++ ++ if (cp->parent_id) ++ parent = yaffs_find_or_create_by_number(obj->my_dev, ++ cp->parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ else ++ parent = NULL; ++ ++ if (parent) { ++ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", ++ cp->obj_id, cp->parent_id, ++ cp->variant_type, cp->hdr_chunk, ++ parent->variant_type); ++ return 0; ++ } ++ yaffs_add_obj_to_dir(parent, obj); ++ } ++ ++ obj->hdr_chunk = cp->hdr_chunk; ++ obj->variant_type = cp->variant_type; ++ obj->deleted = cp->deleted; ++ obj->soft_del = cp->soft_del; ++ obj->unlinked = cp->unlinked; ++ obj->fake = cp->fake; ++ obj->rename_allowed = cp->rename_allowed; ++ obj->unlink_allowed = cp->unlink_allowed; ++ obj->serial = cp->serial; ++ obj->n_data_chunks = cp->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; ++ ++ if (obj->hdr_chunk > 0) ++ obj->lazy_loaded = 1; ++ return 1; ++} ++ ++static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, ++ struct yaffs_tnode *tn, u32 level, ++ int chunk_offset) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ int ok = 1; ++ u32 base_offset; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { ++ if (!tn->internal[i]) ++ continue; ++ ok = yaffs2_checkpt_tnode_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) + i); ++ } ++ return ok; ++ } ++ ++ /* Level 0 tnode */ ++ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; ++ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == ++ sizeof(base_offset)); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ ++ return ok; ++} ++ ++static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 end_marker = ~0; ++ int ok = 1; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return ok; ++ ++ ok = yaffs2_checkpt_tnode_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant.file_variant. ++ top_level, 0); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, ++ sizeof(end_marker)) == sizeof(end_marker)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 base_chunk; ++ int ok = 1; ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; ++ struct yaffs_tnode *tn; ++ int nread = 0; ++ ++ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == ++ sizeof(base_chunk)); ++ ++ while (ok && (~base_chunk)) { ++ nread++; ++ /* Read level 0 tnode */ ++ ++ tn = yaffs_get_tnode(dev); ++ if (tn) ++ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ else ++ ok = 0; ++ ++ if (tn && ok) ++ ok = yaffs_add_find_tnode_0(dev, ++ file_stuct_ptr, ++ base_chunk, tn) ? 1 : 0; ++ ++ if (ok) ++ ok = (yaffs2_checkpt_rd ++ (dev, &base_chunk, ++ sizeof(base_chunk)) == sizeof(base_chunk)); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read tnodes %d records, last %d. ok %d", ++ nread, base_chunk, ok); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int i; ++ int ok = 1; ++ struct list_head *lh; ++ ++ /* Iterate through the objects in each hash entry, ++ * dumping them to the checkpointing stream. ++ */ ++ ++ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ if (!obj->defered_free) { ++ yaffs2_obj_checkpt_obj(&cp, obj); ++ cp.struct_type = sizeof(cp); ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", ++ cp.obj_id, cp.parent_id, ++ cp.variant_type, cp.hdr_chunk, obj); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, ++ sizeof(cp)) == sizeof(cp)); ++ ++ if (ok && ++ obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) ++ ok = yaffs2_wr_checkpt_tnodes(obj); ++ } ++ } ++ } ++ ++ /* Dump end of list */ ++ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); ++ cp.struct_type = sizeof(cp); ++ ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int ok = 1; ++ int done = 0; ++ LIST_HEAD(hard_list); ++ ++ ++ while (ok && !done) { ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (cp.struct_type != sizeof(cp)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "struct size %d instead of %d ok %d", ++ cp.struct_type, (int)sizeof(cp), ok); ++ ok = 0; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read object %d parent %d type %d chunk %d ", ++ cp.obj_id, cp.parent_id, cp.variant_type, ++ cp.hdr_chunk); ++ ++ if (ok && cp.obj_id == ~0) { ++ done = 1; ++ } else if (ok) { ++ obj = ++ yaffs_find_or_create_by_number(dev, cp.obj_id, ++ cp.variant_type); ++ if (obj) { ++ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); ++ if (!ok) ++ break; ++ if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) { ++ ok = yaffs2_rd_checkpt_tnodes(obj); ++ } else if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_HARDLINK) { ++ list_add(&obj->hard_links, &hard_list); ++ } ++ } else { ++ ok = 0; ++ } ++ } ++ } ++ ++ if (ok) ++ yaffs_link_fixup(dev, &hard_list); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum); ++ ++ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == ++ sizeof(checkpt_sum)); ++ ++ if (!ok) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum0; ++ u32 checkpt_sum1; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); ++ ++ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == ++ sizeof(checkpt_sum1)); ++ ++ if (!ok) ++ return 0; ++ ++ if (checkpt_sum0 != checkpt_sum1) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!yaffs2_checkpt_required(dev)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint write"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 1); ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint device"); ++ ok = yaffs2_wr_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint objects"); ++ ok = yaffs2_wr_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) ++ ok = yaffs2_wr_checkpt_sum(dev); ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return dev->is_checkpointed; ++} ++ ++static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!dev->param.is_yaffs2) ++ ok = 0; ++ ++ if (ok && dev->param.skip_checkpt_rd) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint read"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint device"); ++ ok = yaffs2_rd_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint objects"); ++ ok = yaffs2_rd_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) { ++ ok = yaffs2_rd_checkpt_sum(dev); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint checksum %d", ok); ++ } ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return ok ? 1 : 0; ++} ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) ++{ ++ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { ++ dev->is_checkpointed = 0; ++ yaffs2_checkpt_invalidate_stream(dev); ++ } ++ if (dev->param.sb_dirty_fn) ++ dev->param.sb_dirty_fn(dev); ++} ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "save entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ ++ if (!dev->is_checkpointed) { ++ yaffs2_checkpt_invalidate(dev); ++ yaffs2_wr_checkpt_data(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, ++ "save exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return dev->is_checkpointed; ++} ++ ++int yaffs2_checkpt_restore(struct yaffs_dev *dev) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ retval = yaffs2_rd_checkpt_data(dev); ++ ++ if (dev->is_checkpointed) { ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return retval; ++} ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) ++{ ++ /* if new_size > old_file_size. ++ * We're going to be writing a hole. ++ * If the hole is small then write zeros otherwise write a start ++ * of hole marker. ++ */ ++ loff_t old_file_size; ++ loff_t increase; ++ int small_hole; ++ int result = YAFFS_OK; ++ struct yaffs_dev *dev = NULL; ++ u8 *local_buffer = NULL; ++ int small_increase_ok = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ dev = obj->my_dev; ++ ++ /* Bail out if not yaffs2 mode */ ++ if (!dev->param.is_yaffs2) ++ return YAFFS_OK; ++ ++ old_file_size = obj->variant.file_variant.file_size; ++ ++ if (new_size <= old_file_size) ++ return YAFFS_OK; ++ ++ increase = new_size - old_file_size; ++ ++ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && ++ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) ++ small_hole = 1; ++ else ++ small_hole = 0; ++ ++ if (small_hole) ++ local_buffer = yaffs_get_temp_buffer(dev); ++ ++ if (local_buffer) { ++ /* fill hole with zero bytes */ ++ loff_t pos = old_file_size; ++ int this_write; ++ int written; ++ memset(local_buffer, 0, dev->data_bytes_per_chunk); ++ small_increase_ok = 1; ++ ++ while (increase > 0 && small_increase_ok) { ++ this_write = increase; ++ if (this_write > dev->data_bytes_per_chunk) ++ this_write = dev->data_bytes_per_chunk; ++ written = ++ yaffs_do_file_wr(obj, local_buffer, pos, this_write, ++ 0); ++ if (written == this_write) { ++ pos += this_write; ++ increase -= this_write; ++ } else { ++ small_increase_ok = 0; ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ ++ /* If out of space then reverse any chunks we've added */ ++ if (!small_increase_ok) ++ yaffs_resize_file_down(obj, old_file_size); ++ } ++ ++ if (!small_increase_ok && ++ obj->parent && ++ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { ++ /* Write a hole start header with the old file size */ ++ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); ++ } ++ ++ return result; ++} ++ ++struct yaffs_block_index { ++ int seq; ++ int block; ++}; ++ ++static int yaffs2_ybicmp(const void *a, const void *b) ++{ ++ int aseq = ((struct yaffs_block_index *)a)->seq; ++ int bseq = ((struct yaffs_block_index *)b)->seq; ++ int ablock = ((struct yaffs_block_index *)a)->block; ++ int bblock = ((struct yaffs_block_index *)b)->block; ++ ++ if (aseq == bseq) ++ return ablock - bblock; ++ ++ return aseq - bseq; ++} ++ ++static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int blk, int chunk_in_block, ++ int *found_chunks, ++ u8 *chunk_data, ++ struct list_head *hard_list, ++ int summary_available) ++{ ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int equiv_id; ++ loff_t file_size; ++ int is_shrink; ++ int is_unlinked; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; ++ struct yaffs_file_var *file_var; ++ struct yaffs_hardlink_var *hl_var; ++ struct yaffs_symlink_var *sl_var; ++ ++ if (summary_available) { ++ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); ++ tags.seq_number = bi->seq_number; ++ } ++ ++ if (!summary_available || tags.obj_id == 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); ++ dev->tags_used++; ++ } else { ++ dev->summary_used++; ++ } ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (!tags.chunk_used) { ++ /* An unassigned chunk in the block. ++ * If there are used chunks after this one, then ++ * it is a chunk that was skipped due to failing ++ * the erased check. Just skip it so that it can ++ * be deleted. ++ * But, more typically, We get here when this is ++ * an unallocated chunk and his means that ++ * either the block is empty or this is the one ++ * being allocated from ++ */ ++ ++ if (*found_chunks) { ++ /* This is a chunk that was skipped due ++ * to failing the erased check */ ++ } else if (chunk_in_block == 0) { ++ /* We're looking at the first chunk in ++ * the block so the block is unused */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ if (dev->seq_number == bi->seq_number) { ++ /* Allocating from this block*/ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, chunk_in_block); ++ ++ bi->block_state = ++ YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = chunk_in_block; ++ dev->alloc_block_finder = blk; ++ } else { ++ /* This is a partially written block ++ * that is not the current ++ * allocation block. ++ */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Partially written block %d detected. gc will fix this.", ++ blk); ++ } ++ } ++ } ++ ++ dev->n_free_chunks++; ++ ++ } else if (tags.ecc_result == ++ YAFFS_ECC_RESULT_UNFIXED) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Unfixed ECC in chunk(%d:%d), chunk ignored", ++ blk, chunk_in_block); ++ dev->n_free_chunks++; ++ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || ++ tags.chunk_id > YAFFS_MAX_CHUNK_ID || ++ tags.obj_id == YAFFS_OBJECTID_SUMMARY || ++ (tags.chunk_id > 0 && ++ tags.n_bytes > dev->data_bytes_per_chunk) || ++ tags.seq_number != bi->seq_number) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", ++ blk, chunk_in_block, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ dev->n_free_chunks++; ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ loff_t endpos; ++ loff_t chunk_base = (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk; ++ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ if (!in) ++ /* Out of memory */ ++ alloc_failed = 1; ++ ++ if (in && ++ in->variant_type == YAFFS_OBJECT_TYPE_FILE && ++ chunk_base < in->variant.file_variant.shrink_size) { ++ /* This has not been invalidated by ++ * a resize */ ++ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, ++ chunk, -1)) ++ alloc_failed = 1; ++ ++ /* File size is calculated by looking at ++ * the data chunks if we have not ++ * seen an object header yet. ++ * Stop this practice once we find an ++ * object header. ++ */ ++ endpos = chunk_base + tags.n_bytes; ++ ++ if (!in->valid && ++ in->variant.file_variant.scanned_size < endpos) { ++ in->variant.file_variant. ++ scanned_size = endpos; ++ in->variant.file_variant. ++ file_size = endpos; ++ } ++ } else if (in) { ++ /* This chunk has been invalidated by a ++ * resize, or a past file deletion ++ * so delete the chunk*/ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Thus, we read in the object header and make ++ * the object ++ */ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ oh = NULL; ++ in = NULL; ++ ++ if (tags.extra_available) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ tags.extra_obj_type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ ++ if (!in || ++ (!in->valid && dev->param.disable_lazy_load) || ++ tags.extra_shadows || ++ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { ++ ++ /* If we don't have valid info then we ++ * need to read the chunk ++ * TODO In future we can probably defer ++ * reading the chunk and living with ++ * invalid data until needed. ++ */ ++ ++ result = yaffs_rd_chunk_tags_nand(dev, ++ chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ if (dev->param.inband_tags) { ++ /* Fix up the header if they got ++ * corrupted by inband tags */ ++ oh->shadows_obj = ++ oh->inband_shadowed_obj_id; ++ oh->is_shrink = ++ oh->inband_is_shrink; ++ } ++ ++ if (!in) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, oh->type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ } ++ ++ if (!in) { ++ /* TODO Hoosterman we have a problem! */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", ++ tags.obj_id, chunk); ++ return YAFFS_FAIL; ++ } ++ ++ if (in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate that will be ++ * discarded, but we first have to suck ++ * out resize info if it is a file. ++ */ ++ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && ++ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || ++ (tags.extra_available && ++ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ )) { ++ loff_t this_size = (oh) ? ++ yaffs_oh_to_size(oh) : ++ tags.extra_file_size; ++ u32 parent_obj_id = (oh) ? ++ oh->parent_obj_id : ++ tags.extra_parent_id; ++ ++ is_shrink = (oh) ? ++ oh->is_shrink : ++ tags.extra_is_shrink; ++ ++ /* If it is deleted (unlinked ++ * at start also means deleted) ++ * we treat the file size as ++ * being zeroed at this point. ++ */ ++ if (parent_obj_id == YAFFS_OBJECTID_DELETED || ++ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { ++ this_size = 0; ++ is_shrink = 1; ++ } ++ ++ if (is_shrink && ++ in->variant.file_variant.shrink_size > ++ this_size) ++ in->variant.file_variant.shrink_size = ++ this_size; ++ ++ if (is_shrink) ++ bi->has_shrink_hdr = 1; ++ } ++ /* Use existing - destroy this one. */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ ++ if (!in->valid && in->variant_type != ++ (oh ? oh->type : tags.extra_obj_type)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", ++ oh ? oh->type : tags.extra_obj_type, ++ in->variant_type, tags.obj_id, ++ chunk); ++ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); ++ } ++ ++ if (!in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ ++ if (oh) { ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->lazy_loaded = 0; ++ } else { ++ in->lazy_loaded = 1; ++ } ++ in->hdr_chunk = chunk; ++ ++ } else if (!in->valid) { ++ /* we need to load this info */ ++ in->valid = 1; ++ in->hdr_chunk = chunk; ++ if (oh) { ++ in->variant_type = oh->type; ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ ++ if (oh->shadows_obj > 0) ++ yaffs_handle_shadowed_obj(dev, ++ oh->shadows_obj, 1); ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ parent = yaffs_find_or_create_by_number(dev, ++ oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = yaffs_oh_to_size(oh); ++ is_shrink = oh->is_shrink; ++ equiv_id = oh->equiv_id; ++ } else { ++ in->variant_type = tags.extra_obj_type; ++ parent = yaffs_find_or_create_by_number(dev, ++ tags.extra_parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = tags.extra_file_size; ++ is_shrink = tags.extra_is_shrink; ++ equiv_id = tags.extra_equiv_id; ++ in->lazy_loaded = 1; ++ } ++ in->dirty = 0; ++ ++ if (!parent) ++ alloc_failed = 1; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ if (parent && ++ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant.children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, another problem.... ++ * Trying to use a non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ yaffs_add_obj_to_dir(parent, in); ++ ++ is_unlinked = (parent == dev->del_dir) || ++ (parent == dev->unlinked_dir); ++ ++ if (is_shrink) ++ /* Mark the block */ ++ bi->has_shrink_hdr = 1; ++ ++ /* Note re hardlinks. ++ * Since we might scan a hardlink before its equivalent ++ * object is scanned we put them all in a list. ++ * After scanning is complete, we should have all the ++ * objects, so we run through this list and fix up all ++ * the chains. ++ */ ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ file_var = &in->variant.file_variant; ++ if (file_var->scanned_size < file_size) { ++ /* This covers the case where the file ++ * size is greater than the data held. ++ * This will happen if the file is ++ * resized to be larger than its ++ * current data extents. ++ */ ++ file_var->file_size = file_size; ++ file_var->scanned_size = file_size; ++ } ++ ++ if (file_var->shrink_size > file_size) ++ file_var->shrink_size = file_size; ++ ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ hl_var = &in->variant.hardlink_variant; ++ if (!is_unlinked) { ++ hl_var->equiv_id = equiv_id; ++ list_add(&in->hard_links, hard_list); ++ } ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ sl_var = &in->variant.symlink_variant; ++ if (oh) { ++ sl_var->alias = ++ yaffs_clone_str(oh->alias); ++ if (!sl_var->alias) ++ alloc_failed = 1; ++ } ++ break; ++ } ++ } ++ } ++ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++int yaffs2_scan_backwards(struct yaffs_dev *dev) ++{ ++ int blk; ++ int block_iter; ++ int start_iter; ++ int end_iter; ++ int n_to_scan = 0; ++ enum yaffs_block_state state; ++ int c; ++ int deleted; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ u8 *chunk_data; ++ int found_chunks; ++ int alloc_failed = 0; ++ struct yaffs_block_index *block_index = NULL; ++ int alt_block_index = 0; ++ int summary_available; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ block_index = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); ++ ++ if (!block_index) { ++ block_index = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); ++ alt_block_index = 1; ++ } ++ ++ if (!block_index) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards() could not allocate block index!" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, bi->block_state, seq_number); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ dev->blocks_in_checkpt++; ++ ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } else if (bi->block_state == ++ YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* Determine the highest sequence number */ ++ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && ++ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { ++ block_index[n_to_scan].seq = seq_number; ++ block_index[n_to_scan].block = blk; ++ n_to_scan++; ++ if (seq_number >= dev->seq_number) ++ dev->seq_number = seq_number; ++ } else { ++ /* TODO: Nasty sequence number! */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Block scanning block %d has bad sequence number %d", ++ blk, seq_number); ++ } ++ } ++ bi++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); ++ ++ cond_resched(); ++ ++ /* Sort the blocks by sequence number */ ++ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), ++ yaffs2_ybicmp, NULL); ++ ++ cond_resched(); ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); ++ ++ /* Now scan the blocks looking at the data. */ ++ start_iter = 0; ++ end_iter = n_to_scan - 1; ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); ++ ++ /* For each block.... backwards */ ++ for (block_iter = end_iter; ++ !alloc_failed && block_iter >= start_iter; ++ block_iter--) { ++ /* Cooperative multitasking! This loop can run for so ++ long that watchdog timers expire. */ ++ cond_resched(); ++ ++ /* get the block to scan in the correct order */ ++ blk = block_index[block_iter].block; ++ bi = yaffs_get_block_info(dev, blk); ++ deleted = 0; ++ ++ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); ++ ++ /* For each chunk in each block that needs scanning.... */ ++ found_chunks = 0; ++ if (summary_available) ++ c = dev->chunks_per_summary - 1; ++ else ++ c = dev->param.chunks_per_block - 1; ++ ++ for (/* c is already initialised */; ++ !alloc_failed && c >= 0 && ++ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); ++ c--) { ++ /* Scan backwards... ++ * Read the tags and decide what to do ++ */ ++ if (yaffs2_scan_chunk(dev, bi, blk, c, ++ &found_chunks, chunk_data, ++ &hard_list, summary_available) == ++ YAFFS_FAIL) ++ alloc_failed = 1; ++ } ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, then the block ++ * is fully allocated. */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ yaffs_block_became_dirty(dev, blk); ++ } ++ } ++ ++ yaffs_skip_rest_of_block(dev); ++ ++ if (alt_block_index) ++ vfree(block_index); ++ else ++ kfree(block_index); ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We have scanned all the objects, now it's time to add these ++ * hardlinks. ++ */ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.10.40/fs/yaffs2/yaffs_yaffs2.h +--- linux-3.10.40.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS2_H__ ++#define __YAFFS_YAFFS2_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi); ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); ++int yaffs2_checkpt_required(struct yaffs_dev *dev); ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); ++int yaffs2_checkpt_save(struct yaffs_dev *dev); ++int yaffs2_checkpt_restore(struct yaffs_dev *dev); ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); ++int yaffs2_scan_backwards(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.10.40.orig/fs/yaffs2/yportenv.h linux-3.10.40/fs/yaffs2/yportenv.h +--- linux-3.10.40.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.10.40/fs/yaffs2/yportenv.h 2014-05-17 12:48:10.000000000 +0200 +@@ -0,0 +1,85 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YPORTENV_H__ ++#define __YPORTENV_H__ ++ ++/* ++ * Define the MTD version in terms of Linux Kernel versions ++ * This allows yaffs to be used independantly of the kernel ++ * as well as with it. ++ */ ++ ++#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) ++ ++#ifdef YAFFS_OUT_OF_TREE ++#include "moduleconfig.h" ++#endif ++ ++#include ++#define MTD_VERSION_CODE LINUX_VERSION_CODE ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* These type wrappings are used to support Unicode names in WinCE. */ ++#define YCHAR char ++#define YUCHAR unsigned char ++#define _Y(x) x ++ ++#define YAFFS_LOSTNFOUND_NAME "lost+found" ++#define YAFFS_LOSTNFOUND_PREFIX "obj" ++ ++ ++#define YAFFS_ROOT_MODE 0755 ++#define YAFFS_LOSTNFOUND_MODE 0700 ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define Y_CURRENT_TIME CURRENT_TIME.tv_sec ++#define Y_TIME_CONVERT(x) (x).tv_sec ++#else ++#define Y_CURRENT_TIME CURRENT_TIME ++#define Y_TIME_CONVERT(x) (x) ++#endif ++ ++#define compile_time_assertion(assertion) \ ++ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) ++ ++ ++#define yaffs_printf(msk, fmt, ...) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) ++ ++#define yaffs_trace(msk, fmt, ...) do { \ ++ if (yaffs_trace_mask & (msk)) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ ++} while (0) ++ ++ ++#endif diff --git a/target/linux/patches/3.12.40/bsd-compatibility.patch b/target/linux/patches/3.12.40/bsd-compatibility.patch deleted file mode 100644 index b954b658f..000000000 --- a/target/linux/patches/3.12.40/bsd-compatibility.patch +++ /dev/null @@ -1,2538 +0,0 @@ -diff -Nur linux-3.11.5.orig/scripts/Makefile.lib linux-3.11.5/scripts/Makefile.lib ---- linux-3.11.5.orig/scripts/Makefile.lib 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/Makefile.lib 2013-10-16 18:09:31.000000000 +0200 -@@ -281,7 +281,12 @@ - size_append = printf $(shell \ - dec_size=0; \ - for F in $1; do \ -- fsize=$$(stat -c "%s" $$F); \ -+ if stat -qs .>/dev/null 2>&1; then \ -+ statcmd='stat -f %z'; \ -+ else \ -+ statcmd='stat -c %s'; \ -+ fi; \ -+ fsize=$$($$statcmd $$F); \ - dec_size=$$(expr $$dec_size + $$fsize); \ - done; \ - printf "%08x\n" $$dec_size | \ -diff -Nur linux-3.11.5.orig/scripts/mod/mk_elfconfig.c linux-3.11.5/scripts/mod/mk_elfconfig.c ---- linux-3.11.5.orig/scripts/mod/mk_elfconfig.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/mk_elfconfig.c 2013-10-16 18:09:31.000000000 +0200 -@@ -1,7 +1,18 @@ - #include - #include - #include --#include -+ -+#define EI_NIDENT (16) -+#define ELFMAG "\177ELF" -+ -+#define SELFMAG 4 -+#define EI_CLASS 4 -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ - - int - main(int argc, char **argv) -diff -Nur linux-3.11.5.orig/scripts/mod/modpost.h linux-3.11.5/scripts/mod/modpost.h ---- linux-3.11.5.orig/scripts/mod/modpost.h 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/modpost.h 2013-10-16 18:09:31.000000000 +0200 -@@ -7,7 +7,2453 @@ - #include - #include - #include --#include -+ -+ -+/* This file defines standard ELF types, structures, and macros. -+ Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. -+ This file is part of the GNU C Library. -+ -+ The GNU C Library is free software; you can redistribute it and/or -+ modify it under the terms of the GNU Lesser General Public -+ License as published by the Free Software Foundation; either -+ version 2.1 of the License, or (at your option) any later version. -+ -+ The GNU C Library is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ Lesser General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public -+ License along with the GNU C Library; if not, write to the Free -+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ 02111-1307 USA. */ -+ -+#ifndef _ELF_H -+#define _ELF_H 1 -+ -+__BEGIN_DECLS -+ -+/* Standard ELF types. */ -+ -+#include -+ -+/* Type for a 16-bit quantity. */ -+typedef uint16_t Elf32_Half; -+typedef uint16_t Elf64_Half; -+ -+/* Types for signed and unsigned 32-bit quantities. */ -+typedef uint32_t Elf32_Word; -+typedef int32_t Elf32_Sword; -+typedef uint32_t Elf64_Word; -+typedef int32_t Elf64_Sword; -+ -+/* Types for signed and unsigned 64-bit quantities. */ -+typedef uint64_t Elf32_Xword; -+typedef int64_t Elf32_Sxword; -+typedef uint64_t Elf64_Xword; -+typedef int64_t Elf64_Sxword; -+ -+/* Type of addresses. */ -+typedef uint32_t Elf32_Addr; -+typedef uint64_t Elf64_Addr; -+ -+/* Type of file offsets. */ -+typedef uint32_t Elf32_Off; -+typedef uint64_t Elf64_Off; -+ -+/* Type for section indices, which are 16-bit quantities. */ -+typedef uint16_t Elf32_Section; -+typedef uint16_t Elf64_Section; -+ -+/* Type for version symbol information. */ -+typedef Elf32_Half Elf32_Versym; -+typedef Elf64_Half Elf64_Versym; -+ -+ -+/* The ELF file header. This appears at the start of every ELF file. */ -+ -+#define EI_NIDENT (16) -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf32_Half e_type; /* Object file type */ -+ Elf32_Half e_machine; /* Architecture */ -+ Elf32_Word e_version; /* Object file version */ -+ Elf32_Addr e_entry; /* Entry point virtual address */ -+ Elf32_Off e_phoff; /* Program header table file offset */ -+ Elf32_Off e_shoff; /* Section header table file offset */ -+ Elf32_Word e_flags; /* Processor-specific flags */ -+ Elf32_Half e_ehsize; /* ELF header size in bytes */ -+ Elf32_Half e_phentsize; /* Program header table entry size */ -+ Elf32_Half e_phnum; /* Program header table entry count */ -+ Elf32_Half e_shentsize; /* Section header table entry size */ -+ Elf32_Half e_shnum; /* Section header table entry count */ -+ Elf32_Half e_shstrndx; /* Section header string table index */ -+} Elf32_Ehdr; -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf64_Half e_type; /* Object file type */ -+ Elf64_Half e_machine; /* Architecture */ -+ Elf64_Word e_version; /* Object file version */ -+ Elf64_Addr e_entry; /* Entry point virtual address */ -+ Elf64_Off e_phoff; /* Program header table file offset */ -+ Elf64_Off e_shoff; /* Section header table file offset */ -+ Elf64_Word e_flags; /* Processor-specific flags */ -+ Elf64_Half e_ehsize; /* ELF header size in bytes */ -+ Elf64_Half e_phentsize; /* Program header table entry size */ -+ Elf64_Half e_phnum; /* Program header table entry count */ -+ Elf64_Half e_shentsize; /* Section header table entry size */ -+ Elf64_Half e_shnum; /* Section header table entry count */ -+ Elf64_Half e_shstrndx; /* Section header string table index */ -+} Elf64_Ehdr; -+ -+/* Fields in the e_ident array. The EI_* macros are indices into the -+ array. The macros under each EI_* macro are the values the byte -+ may have. */ -+ -+#define EI_MAG0 0 /* File identification byte 0 index */ -+#define ELFMAG0 0x7f /* Magic number byte 0 */ -+ -+#define EI_MAG1 1 /* File identification byte 1 index */ -+#define ELFMAG1 'E' /* Magic number byte 1 */ -+ -+#define EI_MAG2 2 /* File identification byte 2 index */ -+#define ELFMAG2 'L' /* Magic number byte 2 */ -+ -+#define EI_MAG3 3 /* File identification byte 3 index */ -+#define ELFMAG3 'F' /* Magic number byte 3 */ -+ -+/* Conglomeration of the identification bytes, for easy testing as a word. */ -+#define ELFMAG "\177ELF" -+#define SELFMAG 4 -+ -+#define EI_CLASS 4 /* File class byte index */ -+#define ELFCLASSNONE 0 /* Invalid class */ -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+#define ELFCLASSNUM 3 -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATANONE 0 /* Invalid data encoding */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ -+#define ELFDATANUM 3 -+ -+#define EI_VERSION 6 /* File version byte index */ -+ /* Value must be EV_CURRENT */ -+ -+#define EI_OSABI 7 /* OS ABI identification */ -+#define ELFOSABI_NONE 0 /* UNIX System V ABI */ -+#define ELFOSABI_SYSV 0 /* Alias. */ -+#define ELFOSABI_HPUX 1 /* HP-UX */ -+#define ELFOSABI_NETBSD 2 /* NetBSD. */ -+#define ELFOSABI_LINUX 3 /* Linux. */ -+#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ -+#define ELFOSABI_AIX 7 /* IBM AIX. */ -+#define ELFOSABI_IRIX 8 /* SGI Irix. */ -+#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ -+#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ -+#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ -+#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ -+#define ELFOSABI_ARM 97 /* ARM */ -+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ -+ -+#define EI_ABIVERSION 8 /* ABI version */ -+ -+#define EI_PAD 9 /* Byte index of padding bytes */ -+ -+/* Legal values for e_type (object file type). */ -+ -+#define ET_NONE 0 /* No file type */ -+#define ET_REL 1 /* Relocatable file */ -+#define ET_EXEC 2 /* Executable file */ -+#define ET_DYN 3 /* Shared object file */ -+#define ET_CORE 4 /* Core file */ -+#define ET_NUM 5 /* Number of defined types */ -+#define ET_LOOS 0xfe00 /* OS-specific range start */ -+#define ET_HIOS 0xfeff /* OS-specific range end */ -+#define ET_LOPROC 0xff00 /* Processor-specific range start */ -+#define ET_HIPROC 0xffff /* Processor-specific range end */ -+ -+/* Legal values for e_machine (architecture). */ -+ -+#define EM_NONE 0 /* No machine */ -+#define EM_M32 1 /* AT&T WE 32100 */ -+#define EM_SPARC 2 /* SUN SPARC */ -+#define EM_386 3 /* Intel 80386 */ -+#define EM_68K 4 /* Motorola m68k family */ -+#define EM_88K 5 /* Motorola m88k family */ -+#define EM_860 7 /* Intel 80860 */ -+#define EM_MIPS 8 /* MIPS R3000 big-endian */ -+#define EM_S370 9 /* IBM System/370 */ -+#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ -+ -+#define EM_PARISC 15 /* HPPA */ -+#define EM_VPP500 17 /* Fujitsu VPP500 */ -+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ -+#define EM_960 19 /* Intel 80960 */ -+#define EM_PPC 20 /* PowerPC */ -+#define EM_PPC64 21 /* PowerPC 64-bit */ -+#define EM_S390 22 /* IBM S390 */ -+ -+#define EM_V800 36 /* NEC V800 series */ -+#define EM_FR20 37 /* Fujitsu FR20 */ -+#define EM_RH32 38 /* TRW RH-32 */ -+#define EM_RCE 39 /* Motorola RCE */ -+#define EM_ARM 40 /* ARM */ -+#define EM_FAKE_ALPHA 41 /* Digital Alpha */ -+#define EM_SH 42 /* Hitachi SH */ -+#define EM_SPARCV9 43 /* SPARC v9 64-bit */ -+#define EM_TRICORE 44 /* Siemens Tricore */ -+#define EM_ARC 45 /* Argonaut RISC Core */ -+#define EM_H8_300 46 /* Hitachi H8/300 */ -+#define EM_H8_300H 47 /* Hitachi H8/300H */ -+#define EM_H8S 48 /* Hitachi H8S */ -+#define EM_H8_500 49 /* Hitachi H8/500 */ -+#define EM_IA_64 50 /* Intel Merced */ -+#define EM_MIPS_X 51 /* Stanford MIPS-X */ -+#define EM_COLDFIRE 52 /* Motorola Coldfire */ -+#define EM_68HC12 53 /* Motorola M68HC12 */ -+#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -+#define EM_PCP 55 /* Siemens PCP */ -+#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -+#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -+#define EM_STARCORE 58 /* Motorola Start*Core processor */ -+#define EM_ME16 59 /* Toyota ME16 processor */ -+#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -+#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -+#define EM_X86_64 62 /* AMD x86-64 architecture */ -+#define EM_PDSP 63 /* Sony DSP Processor */ -+ -+#define EM_FX66 66 /* Siemens FX66 microcontroller */ -+#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -+#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -+#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -+#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -+#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -+#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -+#define EM_SVX 73 /* Silicon Graphics SVx */ -+#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ -+#define EM_VAX 75 /* Digital VAX */ -+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ -+#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ -+#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -+#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -+#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ -+#define EM_HUANY 81 /* Harvard University machine-independent object files */ -+#define EM_PRISM 82 /* SiTera Prism */ -+#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -+#define EM_FR30 84 /* Fujitsu FR30 */ -+#define EM_D10V 85 /* Mitsubishi D10V */ -+#define EM_D30V 86 /* Mitsubishi D30V */ -+#define EM_V850 87 /* NEC v850 */ -+#define EM_M32R 88 /* Mitsubishi M32R */ -+#define EM_MN10300 89 /* Matsushita MN10300 */ -+#define EM_MN10200 90 /* Matsushita MN10200 */ -+#define EM_PJ 91 /* picoJava */ -+#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ -+#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ -+#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ -+#define EM_NUM 95 -+ -+/* If it is necessary to assign new unofficial EM_* values, please -+ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the -+ chances of collision with official or non-GNU unofficial values. */ -+ -+#define EM_ALPHA 0x9026 -+ -+/* Legal values for e_version (version). */ -+ -+#define EV_NONE 0 /* Invalid ELF version */ -+#define EV_CURRENT 1 /* Current version */ -+#define EV_NUM 2 -+ -+/* Section header. */ -+ -+typedef struct -+{ -+ Elf32_Word sh_name; /* Section name (string tbl index) */ -+ Elf32_Word sh_type; /* Section type */ -+ Elf32_Word sh_flags; /* Section flags */ -+ Elf32_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf32_Off sh_offset; /* Section file offset */ -+ Elf32_Word sh_size; /* Section size in bytes */ -+ Elf32_Word sh_link; /* Link to another section */ -+ Elf32_Word sh_info; /* Additional section information */ -+ Elf32_Word sh_addralign; /* Section alignment */ -+ Elf32_Word sh_entsize; /* Entry size if section holds table */ -+} Elf32_Shdr; -+ -+typedef struct -+{ -+ Elf64_Word sh_name; /* Section name (string tbl index) */ -+ Elf64_Word sh_type; /* Section type */ -+ Elf64_Xword sh_flags; /* Section flags */ -+ Elf64_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf64_Off sh_offset; /* Section file offset */ -+ Elf64_Xword sh_size; /* Section size in bytes */ -+ Elf64_Word sh_link; /* Link to another section */ -+ Elf64_Word sh_info; /* Additional section information */ -+ Elf64_Xword sh_addralign; /* Section alignment */ -+ Elf64_Xword sh_entsize; /* Entry size if section holds table */ -+} Elf64_Shdr; -+ -+/* Special section indices. */ -+ -+#define SHN_UNDEF 0 /* Undefined section */ -+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ -+#define SHN_LOPROC 0xff00 /* Start of processor-specific */ -+#define SHN_HIPROC 0xff1f /* End of processor-specific */ -+#define SHN_LOOS 0xff20 /* Start of OS-specific */ -+#define SHN_HIOS 0xff3f /* End of OS-specific */ -+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ -+#define SHN_COMMON 0xfff2 /* Associated symbol is common */ -+#define SHN_XINDEX 0xffff /* Index is in extra table. */ -+#define SHN_HIRESERVE 0xffff /* End of reserved indices */ -+ -+/* Legal values for sh_type (section type). */ -+ -+#define SHT_NULL 0 /* Section header table entry unused */ -+#define SHT_PROGBITS 1 /* Program data */ -+#define SHT_SYMTAB 2 /* Symbol table */ -+#define SHT_STRTAB 3 /* String table */ -+#define SHT_RELA 4 /* Relocation entries with addends */ -+#define SHT_HASH 5 /* Symbol hash table */ -+#define SHT_DYNAMIC 6 /* Dynamic linking information */ -+#define SHT_NOTE 7 /* Notes */ -+#define SHT_NOBITS 8 /* Program space with no data (bss) */ -+#define SHT_REL 9 /* Relocation entries, no addends */ -+#define SHT_SHLIB 10 /* Reserved */ -+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ -+#define SHT_INIT_ARRAY 14 /* Array of constructors */ -+#define SHT_FINI_ARRAY 15 /* Array of destructors */ -+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ -+#define SHT_GROUP 17 /* Section group */ -+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ -+#define SHT_NUM 19 /* Number of defined types. */ -+#define SHT_LOOS 0x60000000 /* Start OS-specific */ -+#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ -+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ -+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ -+#define SHT_SUNW_move 0x6ffffffa -+#define SHT_SUNW_COMDAT 0x6ffffffb -+#define SHT_SUNW_syminfo 0x6ffffffc -+#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ -+#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ -+#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ -+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ -+#define SHT_HIOS 0x6fffffff /* End OS-specific type */ -+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define SHT_LOUSER 0x80000000 /* Start of application-specific */ -+#define SHT_HIUSER 0x8fffffff /* End of application-specific */ -+ -+/* Legal values for sh_flags (section flags). */ -+ -+#define SHF_WRITE (1 << 0) /* Writable */ -+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ -+#define SHF_EXECINSTR (1 << 2) /* Executable */ -+#define SHF_MERGE (1 << 4) /* Might be merged */ -+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ -+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ -+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ -+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling -+ required */ -+#define SHF_GROUP (1 << 9) /* Section is member of a group. */ -+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ -+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ -+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Section group handling. */ -+#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ -+ -+/* Symbol table entry. */ -+ -+typedef struct -+{ -+ Elf32_Word st_name; /* Symbol name (string tbl index) */ -+ Elf32_Addr st_value; /* Symbol value */ -+ Elf32_Word st_size; /* Symbol size */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf32_Section st_shndx; /* Section index */ -+} Elf32_Sym; -+ -+typedef struct -+{ -+ Elf64_Word st_name; /* Symbol name (string tbl index) */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf64_Section st_shndx; /* Section index */ -+ Elf64_Addr st_value; /* Symbol value */ -+ Elf64_Xword st_size; /* Symbol size */ -+} Elf64_Sym; -+ -+/* The syminfo section if available contains additional information about -+ every dynamic symbol. */ -+ -+typedef struct -+{ -+ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf32_Half si_flags; /* Per symbol flags */ -+} Elf32_Syminfo; -+ -+typedef struct -+{ -+ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf64_Half si_flags; /* Per symbol flags */ -+} Elf64_Syminfo; -+ -+/* Possible values for si_boundto. */ -+#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ -+#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ -+#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ -+ -+/* Possible bitmasks for si_flags. */ -+#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ -+#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ -+#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ -+#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy -+ loaded */ -+/* Syminfo version values. */ -+#define SYMINFO_NONE 0 -+#define SYMINFO_CURRENT 1 -+#define SYMINFO_NUM 2 -+ -+ -+/* How to extract and insert information held in the st_info field. */ -+ -+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -+#define ELF32_ST_TYPE(val) ((val) & 0xf) -+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -+ -+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ -+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) -+ -+/* Legal values for ST_BIND subfield of st_info (symbol binding). */ -+ -+#define STB_LOCAL 0 /* Local symbol */ -+#define STB_GLOBAL 1 /* Global symbol */ -+#define STB_WEAK 2 /* Weak symbol */ -+#define STB_NUM 3 /* Number of defined types. */ -+#define STB_LOOS 10 /* Start of OS-specific */ -+#define STB_HIOS 12 /* End of OS-specific */ -+#define STB_LOPROC 13 /* Start of processor-specific */ -+#define STB_HIPROC 15 /* End of processor-specific */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_NOTYPE 0 /* Symbol type is unspecified */ -+#define STT_OBJECT 1 /* Symbol is a data object */ -+#define STT_FUNC 2 /* Symbol is a code object */ -+#define STT_SECTION 3 /* Symbol associated with a section */ -+#define STT_FILE 4 /* Symbol's name is file name */ -+#define STT_COMMON 5 /* Symbol is a common data object */ -+#define STT_TLS 6 /* Symbol is thread-local data object*/ -+#define STT_NUM 7 /* Number of defined types. */ -+#define STT_LOOS 10 /* Start of OS-specific */ -+#define STT_HIOS 12 /* End of OS-specific */ -+#define STT_LOPROC 13 /* Start of processor-specific */ -+#define STT_HIPROC 15 /* End of processor-specific */ -+ -+ -+/* Symbol table indices are found in the hash buckets and chain table -+ of a symbol hash table section. This special index value indicates -+ the end of a chain, meaning no further symbols are found in that bucket. */ -+ -+#define STN_UNDEF 0 /* End of a chain. */ -+ -+ -+/* How to extract and insert information held in the st_other field. */ -+ -+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) -+ -+/* For ELF64 the definitions are the same. */ -+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) -+ -+/* Symbol visibility specification encoded in the st_other field. */ -+#define STV_DEFAULT 0 /* Default symbol visibility rules */ -+#define STV_INTERNAL 1 /* Processor specific hidden class */ -+#define STV_HIDDEN 2 /* Sym unavailable in other modules */ -+#define STV_PROTECTED 3 /* Not preemptible, not exported */ -+ -+ -+/* Relocation table entry without addend (in section of type SHT_REL). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+} Elf32_Rel; -+ -+/* I have seen two different definitions of the Elf64_Rel and -+ Elf64_Rela structures, so we'll leave them out until Novell (or -+ whoever) gets their act together. */ -+/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+} Elf64_Rel; -+ -+/* Relocation table entry with addend (in section of type SHT_RELA). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+ Elf32_Sword r_addend; /* Addend */ -+} Elf32_Rela; -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+ Elf64_Sxword r_addend; /* Addend */ -+} Elf64_Rela; -+ -+/* How to extract and insert information held in the r_info field. */ -+ -+#define ELF32_R_SYM(val) ((val) >> 8) -+#define ELF32_R_TYPE(val) ((val) & 0xff) -+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) -+ -+#define ELF64_R_SYM(i) ((i) >> 32) -+#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) -+ -+/* Program segment header. */ -+ -+typedef struct -+{ -+ Elf32_Word p_type; /* Segment type */ -+ Elf32_Off p_offset; /* Segment file offset */ -+ Elf32_Addr p_vaddr; /* Segment virtual address */ -+ Elf32_Addr p_paddr; /* Segment physical address */ -+ Elf32_Word p_filesz; /* Segment size in file */ -+ Elf32_Word p_memsz; /* Segment size in memory */ -+ Elf32_Word p_flags; /* Segment flags */ -+ Elf32_Word p_align; /* Segment alignment */ -+} Elf32_Phdr; -+ -+typedef struct -+{ -+ Elf64_Word p_type; /* Segment type */ -+ Elf64_Word p_flags; /* Segment flags */ -+ Elf64_Off p_offset; /* Segment file offset */ -+ Elf64_Addr p_vaddr; /* Segment virtual address */ -+ Elf64_Addr p_paddr; /* Segment physical address */ -+ Elf64_Xword p_filesz; /* Segment size in file */ -+ Elf64_Xword p_memsz; /* Segment size in memory */ -+ Elf64_Xword p_align; /* Segment alignment */ -+} Elf64_Phdr; -+ -+/* Legal values for p_type (segment type). */ -+ -+#define PT_NULL 0 /* Program header table entry unused */ -+#define PT_LOAD 1 /* Loadable program segment */ -+#define PT_DYNAMIC 2 /* Dynamic linking information */ -+#define PT_INTERP 3 /* Program interpreter */ -+#define PT_NOTE 4 /* Auxiliary information */ -+#define PT_SHLIB 5 /* Reserved */ -+#define PT_PHDR 6 /* Entry for header table itself */ -+#define PT_TLS 7 /* Thread-local storage segment */ -+#define PT_NUM 8 /* Number of defined types */ -+#define PT_LOOS 0x60000000 /* Start of OS-specific */ -+#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ -+#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ -+#define PT_LOSUNW 0x6ffffffa -+#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ -+#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ -+#define PT_HISUNW 0x6fffffff -+#define PT_HIOS 0x6fffffff /* End of OS-specific */ -+#define PT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define PT_HIPROC 0x7fffffff /* End of processor-specific */ -+ -+/* Legal values for p_flags (segment flags). */ -+ -+#define PF_X (1 << 0) /* Segment is executable */ -+#define PF_W (1 << 1) /* Segment is writable */ -+#define PF_R (1 << 2) /* Segment is readable */ -+#define PF_MASKOS 0x0ff00000 /* OS-specific */ -+#define PF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Legal values for note segment descriptor types for core files. */ -+ -+#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ -+#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ -+#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ -+#define NT_PRXREG 4 /* Contains copy of prxregset struct */ -+#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ -+#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ -+#define NT_AUXV 6 /* Contains copy of auxv array */ -+#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ -+#define NT_ASRS 8 /* Contains copy of asrset struct */ -+#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ -+#define NT_PSINFO 13 /* Contains copy of psinfo struct */ -+#define NT_PRCRED 14 /* Contains copy of prcred struct */ -+#define NT_UTSNAME 15 /* Contains copy of utsname struct */ -+#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ -+#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ -+#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ -+ -+/* Legal values for the note segment descriptor types for object files. */ -+ -+#define NT_VERSION 1 /* Contains a version string. */ -+ -+ -+/* Dynamic section entry. */ -+ -+typedef struct -+{ -+ Elf32_Sword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf32_Word d_val; /* Integer value */ -+ Elf32_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf32_Dyn; -+ -+typedef struct -+{ -+ Elf64_Sxword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf64_Xword d_val; /* Integer value */ -+ Elf64_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf64_Dyn; -+ -+/* Legal values for d_tag (dynamic entry type). */ -+ -+#define DT_NULL 0 /* Marks end of dynamic section */ -+#define DT_NEEDED 1 /* Name of needed library */ -+#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ -+#define DT_PLTGOT 3 /* Processor defined value */ -+#define DT_HASH 4 /* Address of symbol hash table */ -+#define DT_STRTAB 5 /* Address of string table */ -+#define DT_SYMTAB 6 /* Address of symbol table */ -+#define DT_RELA 7 /* Address of Rela relocs */ -+#define DT_RELASZ 8 /* Total size of Rela relocs */ -+#define DT_RELAENT 9 /* Size of one Rela reloc */ -+#define DT_STRSZ 10 /* Size of string table */ -+#define DT_SYMENT 11 /* Size of one symbol table entry */ -+#define DT_INIT 12 /* Address of init function */ -+#define DT_FINI 13 /* Address of termination function */ -+#define DT_SONAME 14 /* Name of shared object */ -+#define DT_RPATH 15 /* Library search path (deprecated) */ -+#define DT_SYMBOLIC 16 /* Start symbol search here */ -+#define DT_REL 17 /* Address of Rel relocs */ -+#define DT_RELSZ 18 /* Total size of Rel relocs */ -+#define DT_RELENT 19 /* Size of one Rel reloc */ -+#define DT_PLTREL 20 /* Type of reloc in PLT */ -+#define DT_DEBUG 21 /* For debugging; unspecified */ -+#define DT_TEXTREL 22 /* Reloc might modify .text */ -+#define DT_JMPREL 23 /* Address of PLT relocs */ -+#define DT_BIND_NOW 24 /* Process relocations of object */ -+#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ -+#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ -+#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ -+#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ -+#define DT_RUNPATH 29 /* Library search path */ -+#define DT_FLAGS 30 /* Flags for the object being loaded */ -+#define DT_ENCODING 32 /* Start of encoded range */ -+#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ -+#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ -+#define DT_NUM 34 /* Number used */ -+#define DT_LOOS 0x6000000d /* Start of OS-specific */ -+#define DT_HIOS 0x6ffff000 /* End of OS-specific */ -+#define DT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define DT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ -+ -+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the -+ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's -+ approach. */ -+#define DT_VALRNGLO 0x6ffffd00 -+#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ -+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ -+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ -+#define DT_CHECKSUM 0x6ffffdf8 -+#define DT_PLTPADSZ 0x6ffffdf9 -+#define DT_MOVEENT 0x6ffffdfa -+#define DT_MOVESZ 0x6ffffdfb -+#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ -+#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting -+ the following DT_* entry. */ -+#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ -+#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ -+#define DT_VALRNGHI 0x6ffffdff -+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ -+#define DT_VALNUM 12 -+ -+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the -+ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. -+ -+ If any adjustment is made to the ELF object after it has been -+ built these entries will need to be adjusted. */ -+#define DT_ADDRRNGLO 0x6ffffe00 -+#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ -+#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ -+#define DT_CONFIG 0x6ffffefa /* Configuration information. */ -+#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ -+#define DT_AUDIT 0x6ffffefc /* Object auditing. */ -+#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ -+#define DT_MOVETAB 0x6ffffefe /* Move table. */ -+#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ -+#define DT_ADDRRNGHI 0x6ffffeff -+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ -+#define DT_ADDRNUM 10 -+ -+/* The versioning entry types. The next are defined as part of the -+ GNU extension. */ -+#define DT_VERSYM 0x6ffffff0 -+ -+#define DT_RELACOUNT 0x6ffffff9 -+#define DT_RELCOUNT 0x6ffffffa -+ -+/* These were chosen by Sun. */ -+#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ -+#define DT_VERDEF 0x6ffffffc /* Address of version definition -+ table */ -+#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ -+#define DT_VERNEED 0x6ffffffe /* Address of table with needed -+ versions */ -+#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ -+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ -+#define DT_VERSIONTAGNUM 16 -+ -+/* Sun added these machine-independent extensions in the "processor-specific" -+ range. Be compatible. */ -+#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ -+#define DT_FILTER 0x7fffffff /* Shared object to get values from */ -+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -+#define DT_EXTRANUM 3 -+ -+/* Values of `d_un.d_val' in the DT_FLAGS entry. */ -+#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ -+#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ -+#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ -+#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ -+#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ -+ -+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 -+ entry in the dynamic section. */ -+#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ -+#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ -+#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ -+#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ -+#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ -+#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ -+#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ -+#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ -+#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ -+#define DF_1_TRANS 0x00000200 -+#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ -+#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ -+#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ -+#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ -+#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ -+#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ -+#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ -+ -+/* Flags for the feature selection in DT_FEATURE_1. */ -+#define DTF_1_PARINIT 0x00000001 -+#define DTF_1_CONFEXP 0x00000002 -+ -+/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ -+#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ -+#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not -+ generally available. */ -+ -+/* Version definition sections. */ -+ -+typedef struct -+{ -+ Elf32_Half vd_version; /* Version revision */ -+ Elf32_Half vd_flags; /* Version information */ -+ Elf32_Half vd_ndx; /* Version Index */ -+ Elf32_Half vd_cnt; /* Number of associated aux entries */ -+ Elf32_Word vd_hash; /* Version name hash value */ -+ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf32_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf32_Verdef; -+ -+typedef struct -+{ -+ Elf64_Half vd_version; /* Version revision */ -+ Elf64_Half vd_flags; /* Version information */ -+ Elf64_Half vd_ndx; /* Version Index */ -+ Elf64_Half vd_cnt; /* Number of associated aux entries */ -+ Elf64_Word vd_hash; /* Version name hash value */ -+ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf64_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf64_Verdef; -+ -+ -+/* Legal values for vd_version (version revision). */ -+#define VER_DEF_NONE 0 /* No version */ -+#define VER_DEF_CURRENT 1 /* Current version */ -+#define VER_DEF_NUM 2 /* Given version number */ -+ -+/* Legal values for vd_flags (version information flags). */ -+#define VER_FLG_BASE 0x1 /* Version definition of file itself */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+/* Versym symbol index values. */ -+#define VER_NDX_LOCAL 0 /* Symbol is local. */ -+#define VER_NDX_GLOBAL 1 /* Symbol is global. */ -+#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ -+#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ -+ -+/* Auxialiary version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vda_name; /* Version or dependency names */ -+ Elf32_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf32_Verdaux; -+ -+typedef struct -+{ -+ Elf64_Word vda_name; /* Version or dependency names */ -+ Elf64_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf64_Verdaux; -+ -+ -+/* Version dependency section. */ -+ -+typedef struct -+{ -+ Elf32_Half vn_version; /* Version of structure */ -+ Elf32_Half vn_cnt; /* Number of associated aux entries */ -+ Elf32_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf32_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf32_Verneed; -+ -+typedef struct -+{ -+ Elf64_Half vn_version; /* Version of structure */ -+ Elf64_Half vn_cnt; /* Number of associated aux entries */ -+ Elf64_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf64_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf64_Verneed; -+ -+ -+/* Legal values for vn_version (version revision). */ -+#define VER_NEED_NONE 0 /* No version */ -+#define VER_NEED_CURRENT 1 /* Current version */ -+#define VER_NEED_NUM 2 /* Given version number */ -+ -+/* Auxiliary needed version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vna_hash; /* Hash value of dependency name */ -+ Elf32_Half vna_flags; /* Dependency specific information */ -+ Elf32_Half vna_other; /* Unused */ -+ Elf32_Word vna_name; /* Dependency name string offset */ -+ Elf32_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf32_Vernaux; -+ -+typedef struct -+{ -+ Elf64_Word vna_hash; /* Hash value of dependency name */ -+ Elf64_Half vna_flags; /* Dependency specific information */ -+ Elf64_Half vna_other; /* Unused */ -+ Elf64_Word vna_name; /* Dependency name string offset */ -+ Elf64_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf64_Vernaux; -+ -+ -+/* Legal values for vna_flags. */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+ -+/* Auxiliary vector. */ -+ -+/* This vector is normally only used by the program interpreter. The -+ usual definition in an ABI supplement uses the name auxv_t. The -+ vector is not usually defined in a standard file, but it -+ can't hurt. We rename it to avoid conflicts. The sizes of these -+ types are an arrangement between the exec server and the program -+ interpreter, so we don't fully specify them here. */ -+ -+typedef struct -+{ -+ int a_type; /* Entry type */ -+ union -+ { -+ long int a_val; /* Integer value */ -+ void *a_ptr; /* Pointer value */ -+ void (*a_fcn) (void); /* Function pointer value */ -+ } a_un; -+} Elf32_auxv_t; -+ -+typedef struct -+{ -+ long int a_type; /* Entry type */ -+ union -+ { -+ long int a_val; /* Integer value */ -+ void *a_ptr; /* Pointer value */ -+ void (*a_fcn) (void); /* Function pointer value */ -+ } a_un; -+} Elf64_auxv_t; -+ -+/* Legal values for a_type (entry type). */ -+ -+#define AT_NULL 0 /* End of vector */ -+#define AT_IGNORE 1 /* Entry should be ignored */ -+#define AT_EXECFD 2 /* File descriptor of program */ -+#define AT_PHDR 3 /* Program headers for program */ -+#define AT_PHENT 4 /* Size of program header entry */ -+#define AT_PHNUM 5 /* Number of program headers */ -+#define AT_PAGESZ 6 /* System page size */ -+#define AT_BASE 7 /* Base address of interpreter */ -+#define AT_FLAGS 8 /* Flags */ -+#define AT_ENTRY 9 /* Entry point of program */ -+#define AT_NOTELF 10 /* Program is not ELF */ -+#define AT_UID 11 /* Real uid */ -+#define AT_EUID 12 /* Effective uid */ -+#define AT_GID 13 /* Real gid */ -+#define AT_EGID 14 /* Effective gid */ -+#define AT_CLKTCK 17 /* Frequency of times() */ -+ -+/* Some more special a_type values describing the hardware. */ -+#define AT_PLATFORM 15 /* String identifying platform. */ -+#define AT_HWCAP 16 /* Machine dependent hints about -+ processor capabilities. */ -+ -+/* This entry gives some information about the FPU initialization -+ performed by the kernel. */ -+#define AT_FPUCW 18 /* Used FPU control word. */ -+ -+/* Cache block sizes. */ -+#define AT_DCACHEBSIZE 19 /* Data cache block size. */ -+#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ -+#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ -+ -+/* A special ignored value for PPC, used by the kernel to control the -+ interpretation of the AUXV. Must be > 16. */ -+#define AT_IGNOREPPC 22 /* Entry should be ignored. */ -+ -+#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ -+ -+/* Pointer to the global system page used for system calls and other -+ nice things. */ -+#define AT_SYSINFO 32 -+#define AT_SYSINFO_EHDR 33 -+ -+ -+/* Note section contents. Each entry in the note section begins with -+ a header of a fixed form. */ -+ -+typedef struct -+{ -+ Elf32_Word n_namesz; /* Length of the note's name. */ -+ Elf32_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf32_Word n_type; /* Type of the note. */ -+} Elf32_Nhdr; -+ -+typedef struct -+{ -+ Elf64_Word n_namesz; /* Length of the note's name. */ -+ Elf64_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf64_Word n_type; /* Type of the note. */ -+} Elf64_Nhdr; -+ -+/* Known names of notes. */ -+ -+/* Solaris entries in the note section have this name. */ -+#define ELF_NOTE_SOLARIS "SUNW Solaris" -+ -+/* Note entries for GNU systems have this name. */ -+#define ELF_NOTE_GNU "GNU" -+ -+ -+/* Defined types of notes for Solaris. */ -+ -+/* Value of descriptor (one word) is desired pagesize for the binary. */ -+#define ELF_NOTE_PAGESIZE_HINT 1 -+ -+ -+/* Defined note types for GNU systems. */ -+ -+/* ABI information. The descriptor consists of words: -+ word 0: OS descriptor -+ word 1: major version of the ABI -+ word 2: minor version of the ABI -+ word 3: subminor version of the ABI -+*/ -+#define ELF_NOTE_ABI 1 -+ -+/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI -+ note section entry. */ -+#define ELF_NOTE_OS_LINUX 0 -+#define ELF_NOTE_OS_GNU 1 -+#define ELF_NOTE_OS_SOLARIS2 2 -+#define ELF_NOTE_OS_FREEBSD 3 -+ -+ -+/* Move records. */ -+typedef struct -+{ -+ Elf32_Xword m_value; /* Symbol value. */ -+ Elf32_Word m_info; /* Size and index. */ -+ Elf32_Word m_poffset; /* Symbol offset. */ -+ Elf32_Half m_repeat; /* Repeat count. */ -+ Elf32_Half m_stride; /* Stride info. */ -+} Elf32_Move; -+ -+typedef struct -+{ -+ Elf64_Xword m_value; /* Symbol value. */ -+ Elf64_Xword m_info; /* Size and index. */ -+ Elf64_Xword m_poffset; /* Symbol offset. */ -+ Elf64_Half m_repeat; /* Repeat count. */ -+ Elf64_Half m_stride; /* Stride info. */ -+} Elf64_Move; -+ -+/* Macro to construct move records. */ -+#define ELF32_M_SYM(info) ((info) >> 8) -+#define ELF32_M_SIZE(info) ((unsigned char) (info)) -+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) -+ -+#define ELF64_M_SYM(info) ELF32_M_SYM (info) -+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) -+ -+ -+/* Motorola 68k specific definitions. */ -+ -+/* Values for Elf32_Ehdr.e_flags. */ -+#define EF_CPU32 0x00810000 -+ -+/* m68k relocs. */ -+ -+#define R_68K_NONE 0 /* No reloc */ -+#define R_68K_32 1 /* Direct 32 bit */ -+#define R_68K_16 2 /* Direct 16 bit */ -+#define R_68K_8 3 /* Direct 8 bit */ -+#define R_68K_PC32 4 /* PC relative 32 bit */ -+#define R_68K_PC16 5 /* PC relative 16 bit */ -+#define R_68K_PC8 6 /* PC relative 8 bit */ -+#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ -+#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ -+#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ -+#define R_68K_GOT32O 10 /* 32 bit GOT offset */ -+#define R_68K_GOT16O 11 /* 16 bit GOT offset */ -+#define R_68K_GOT8O 12 /* 8 bit GOT offset */ -+#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ -+#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ -+#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ -+#define R_68K_PLT32O 16 /* 32 bit PLT offset */ -+#define R_68K_PLT16O 17 /* 16 bit PLT offset */ -+#define R_68K_PLT8O 18 /* 8 bit PLT offset */ -+#define R_68K_COPY 19 /* Copy symbol at runtime */ -+#define R_68K_GLOB_DAT 20 /* Create GOT entry */ -+#define R_68K_JMP_SLOT 21 /* Create PLT entry */ -+#define R_68K_RELATIVE 22 /* Adjust by program base */ -+/* Keep this the last entry. */ -+#define R_68K_NUM 23 -+ -+/* Intel 80386 specific definitions. */ -+ -+/* i386 relocs. */ -+ -+#define R_386_NONE 0 /* No reloc */ -+#define R_386_32 1 /* Direct 32 bit */ -+#define R_386_PC32 2 /* PC relative 32 bit */ -+#define R_386_GOT32 3 /* 32 bit GOT entry */ -+#define R_386_PLT32 4 /* 32 bit PLT address */ -+#define R_386_COPY 5 /* Copy symbol at runtime */ -+#define R_386_GLOB_DAT 6 /* Create GOT entry */ -+#define R_386_JMP_SLOT 7 /* Create PLT entry */ -+#define R_386_RELATIVE 8 /* Adjust by program base */ -+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ -+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ -+#define R_386_32PLT 11 -+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ -+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS -+ block offset */ -+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block -+ offset */ -+#define R_386_TLS_LE 17 /* Offset relative to static TLS -+ block */ -+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of -+ general dynamic thread local data */ -+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of -+ local dynamic thread local data -+ in LE code */ -+#define R_386_16 20 -+#define R_386_PC16 21 -+#define R_386_8 22 -+#define R_386_PC8 23 -+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic -+ thread local data */ -+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ -+#define R_386_TLS_GD_CALL 26 /* Relocation for call to -+ __tls_get_addr() */ -+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ -+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic -+ thread local data in LE code */ -+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ -+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to -+ __tls_get_addr() in LDM code */ -+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ -+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ -+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS -+ block offset */ -+#define R_386_TLS_LE_32 34 /* Negated offset relative to static -+ TLS block */ -+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ -+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ -+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ -+/* Keep this the last entry. */ -+#define R_386_NUM 38 -+ -+/* SUN SPARC specific definitions. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_REGISTER 13 /* Global register reserved to app. */ -+ -+/* Values for Elf64_Ehdr.e_flags. */ -+ -+#define EF_SPARCV9_MM 3 -+#define EF_SPARCV9_TSO 0 -+#define EF_SPARCV9_PSO 1 -+#define EF_SPARCV9_RMO 2 -+#define EF_SPARC_LEDATA 0x800000 /* little endian data */ -+#define EF_SPARC_EXT_MASK 0xFFFF00 -+#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ -+#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ -+#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ -+#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ -+ -+/* SPARC relocs. */ -+ -+#define R_SPARC_NONE 0 /* No reloc */ -+#define R_SPARC_8 1 /* Direct 8 bit */ -+#define R_SPARC_16 2 /* Direct 16 bit */ -+#define R_SPARC_32 3 /* Direct 32 bit */ -+#define R_SPARC_DISP8 4 /* PC relative 8 bit */ -+#define R_SPARC_DISP16 5 /* PC relative 16 bit */ -+#define R_SPARC_DISP32 6 /* PC relative 32 bit */ -+#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ -+#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ -+#define R_SPARC_HI22 9 /* High 22 bit */ -+#define R_SPARC_22 10 /* Direct 22 bit */ -+#define R_SPARC_13 11 /* Direct 13 bit */ -+#define R_SPARC_LO10 12 /* Truncated 10 bit */ -+#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ -+#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ -+#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ -+#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ -+#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ -+#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ -+#define R_SPARC_COPY 19 /* Copy symbol at runtime */ -+#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ -+#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ -+#define R_SPARC_RELATIVE 22 /* Adjust by program base */ -+#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ -+ -+/* Additional Sparc64 relocs. */ -+ -+#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ -+#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ -+#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ -+#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ -+#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ -+#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ -+#define R_SPARC_10 30 /* Direct 10 bit */ -+#define R_SPARC_11 31 /* Direct 11 bit */ -+#define R_SPARC_64 32 /* Direct 64 bit */ -+#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ -+#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ -+#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ -+#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ -+#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ -+#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ -+#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ -+#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ -+#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ -+#define R_SPARC_7 43 /* Direct 7 bit */ -+#define R_SPARC_5 44 /* Direct 5 bit */ -+#define R_SPARC_6 45 /* Direct 6 bit */ -+#define R_SPARC_DISP64 46 /* PC relative 64 bit */ -+#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ -+#define R_SPARC_HIX22 48 /* High 22 bit complemented */ -+#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ -+#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ -+#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ -+#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ -+#define R_SPARC_REGISTER 53 /* Global register usage */ -+#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ -+#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ -+#define R_SPARC_TLS_GD_HI22 56 -+#define R_SPARC_TLS_GD_LO10 57 -+#define R_SPARC_TLS_GD_ADD 58 -+#define R_SPARC_TLS_GD_CALL 59 -+#define R_SPARC_TLS_LDM_HI22 60 -+#define R_SPARC_TLS_LDM_LO10 61 -+#define R_SPARC_TLS_LDM_ADD 62 -+#define R_SPARC_TLS_LDM_CALL 63 -+#define R_SPARC_TLS_LDO_HIX22 64 -+#define R_SPARC_TLS_LDO_LOX10 65 -+#define R_SPARC_TLS_LDO_ADD 66 -+#define R_SPARC_TLS_IE_HI22 67 -+#define R_SPARC_TLS_IE_LO10 68 -+#define R_SPARC_TLS_IE_LD 69 -+#define R_SPARC_TLS_IE_LDX 70 -+#define R_SPARC_TLS_IE_ADD 71 -+#define R_SPARC_TLS_LE_HIX22 72 -+#define R_SPARC_TLS_LE_LOX10 73 -+#define R_SPARC_TLS_DTPMOD32 74 -+#define R_SPARC_TLS_DTPMOD64 75 -+#define R_SPARC_TLS_DTPOFF32 76 -+#define R_SPARC_TLS_DTPOFF64 77 -+#define R_SPARC_TLS_TPOFF32 78 -+#define R_SPARC_TLS_TPOFF64 79 -+/* Keep this the last entry. */ -+#define R_SPARC_NUM 80 -+ -+/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ -+ -+#define DT_SPARC_REGISTER 0x70000001 -+#define DT_SPARC_NUM 2 -+ -+/* Bits present in AT_HWCAP, primarily for Sparc32. */ -+ -+#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ -+#define HWCAP_SPARC_STBAR 2 -+#define HWCAP_SPARC_SWAP 4 -+#define HWCAP_SPARC_MULDIV 8 -+#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ -+#define HWCAP_SPARC_ULTRA3 32 -+ -+/* MIPS R3000 specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ -+#define EF_MIPS_PIC 2 /* Contains PIC code */ -+#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ -+#define EF_MIPS_XGOT 8 -+#define EF_MIPS_64BIT_WHIRL 16 -+#define EF_MIPS_ABI2 32 -+#define EF_MIPS_ABI_ON32 64 -+#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ -+ -+/* Legal values for MIPS architecture level. */ -+ -+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* The following are non-official names and should not be used. */ -+ -+#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* Special section indices. */ -+ -+#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ -+#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ -+#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ -+#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ -+#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ -+#define SHT_MIPS_MSYM 0x70000001 -+#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ -+#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ -+#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ -+#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ -+#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ -+#define SHT_MIPS_PACKAGE 0x70000007 -+#define SHT_MIPS_PACKSYM 0x70000008 -+#define SHT_MIPS_RELD 0x70000009 -+#define SHT_MIPS_IFACE 0x7000000b -+#define SHT_MIPS_CONTENT 0x7000000c -+#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ -+#define SHT_MIPS_SHDR 0x70000010 -+#define SHT_MIPS_FDESC 0x70000011 -+#define SHT_MIPS_EXTSYM 0x70000012 -+#define SHT_MIPS_DENSE 0x70000013 -+#define SHT_MIPS_PDESC 0x70000014 -+#define SHT_MIPS_LOCSYM 0x70000015 -+#define SHT_MIPS_AUXSYM 0x70000016 -+#define SHT_MIPS_OPTSYM 0x70000017 -+#define SHT_MIPS_LOCSTR 0x70000018 -+#define SHT_MIPS_LINE 0x70000019 -+#define SHT_MIPS_RFDESC 0x7000001a -+#define SHT_MIPS_DELTASYM 0x7000001b -+#define SHT_MIPS_DELTAINST 0x7000001c -+#define SHT_MIPS_DELTACLASS 0x7000001d -+#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ -+#define SHT_MIPS_DELTADECL 0x7000001f -+#define SHT_MIPS_SYMBOL_LIB 0x70000020 -+#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ -+#define SHT_MIPS_TRANSLATE 0x70000022 -+#define SHT_MIPS_PIXIE 0x70000023 -+#define SHT_MIPS_XLATE 0x70000024 -+#define SHT_MIPS_XLATE_DEBUG 0x70000025 -+#define SHT_MIPS_WHIRL 0x70000026 -+#define SHT_MIPS_EH_REGION 0x70000027 -+#define SHT_MIPS_XLATE_OLD 0x70000028 -+#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ -+#define SHF_MIPS_MERGE 0x20000000 -+#define SHF_MIPS_ADDR 0x40000000 -+#define SHF_MIPS_STRINGS 0x80000000 -+#define SHF_MIPS_NOSTRIP 0x08000000 -+#define SHF_MIPS_LOCAL 0x04000000 -+#define SHF_MIPS_NAMES 0x02000000 -+#define SHF_MIPS_NODUPE 0x01000000 -+ -+ -+/* Symbol tables. */ -+ -+/* MIPS specific values for `st_other'. */ -+#define STO_MIPS_DEFAULT 0x0 -+#define STO_MIPS_INTERNAL 0x1 -+#define STO_MIPS_HIDDEN 0x2 -+#define STO_MIPS_PROTECTED 0x3 -+#define STO_MIPS_SC_ALIGN_UNUSED 0xff -+ -+/* MIPS specific values for `st_info'. */ -+#define STB_MIPS_SPLIT_COMMON 13 -+ -+/* Entries found in sections of type SHT_MIPS_GPTAB. */ -+ -+typedef union -+{ -+ struct -+ { -+ Elf32_Word gt_current_g_value; /* -G value used for compilation */ -+ Elf32_Word gt_unused; /* Not used */ -+ } gt_header; /* First entry in section */ -+ struct -+ { -+ Elf32_Word gt_g_value; /* If this value were used for -G */ -+ Elf32_Word gt_bytes; /* This many bytes would be used */ -+ } gt_entry; /* Subsequent entries in section */ -+} Elf32_gptab; -+ -+/* Entry found in sections of type SHT_MIPS_REGINFO. */ -+ -+typedef struct -+{ -+ Elf32_Word ri_gprmask; /* General registers used */ -+ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ -+ Elf32_Sword ri_gp_value; /* $gp register value */ -+} Elf32_RegInfo; -+ -+/* Entries found in sections of type SHT_MIPS_OPTIONS. */ -+ -+typedef struct -+{ -+ unsigned char kind; /* Determines interpretation of the -+ variable part of descriptor. */ -+ unsigned char size; /* Size of descriptor, including header. */ -+ Elf32_Section section; /* Section header index of section affected, -+ 0 for global options. */ -+ Elf32_Word info; /* Kind-specific information. */ -+} Elf_Options; -+ -+/* Values for `kind' field in Elf_Options. */ -+ -+#define ODK_NULL 0 /* Undefined. */ -+#define ODK_REGINFO 1 /* Register usage information. */ -+#define ODK_EXCEPTIONS 2 /* Exception processing options. */ -+#define ODK_PAD 3 /* Section padding options. */ -+#define ODK_HWPATCH 4 /* Hardware workarounds performed */ -+#define ODK_FILL 5 /* record the fill value used by the linker. */ -+#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ -+#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ -+#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ -+ -+/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ -+ -+#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ -+#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ -+#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ -+#define OEX_SMM 0x20000 /* Force sequential memory mode? */ -+#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ -+#define OEX_PRECISEFP OEX_FPDBUG -+#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ -+ -+#define OEX_FPU_INVAL 0x10 -+#define OEX_FPU_DIV0 0x08 -+#define OEX_FPU_OFLO 0x04 -+#define OEX_FPU_UFLO 0x02 -+#define OEX_FPU_INEX 0x01 -+ -+/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ -+ -+#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ -+#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ -+#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ -+#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ -+ -+#define OPAD_PREFIX 0x1 -+#define OPAD_POSTFIX 0x2 -+#define OPAD_SYMBOL 0x4 -+ -+/* Entry found in `.options' section. */ -+ -+typedef struct -+{ -+ Elf32_Word hwp_flags1; /* Extra flags. */ -+ Elf32_Word hwp_flags2; /* Extra flags. */ -+} Elf_Options_Hw; -+ -+/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ -+ -+#define OHWA0_R4KEOP_CHECKED 0x00000001 -+#define OHWA1_R4KEOP_CLEAN 0x00000002 -+ -+/* MIPS relocs. */ -+ -+#define R_MIPS_NONE 0 /* No reloc */ -+#define R_MIPS_16 1 /* Direct 16 bit */ -+#define R_MIPS_32 2 /* Direct 32 bit */ -+#define R_MIPS_REL32 3 /* PC relative 32 bit */ -+#define R_MIPS_26 4 /* Direct 26 bit shifted */ -+#define R_MIPS_HI16 5 /* High 16 bit */ -+#define R_MIPS_LO16 6 /* Low 16 bit */ -+#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ -+#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ -+#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ -+#define R_MIPS_PC16 10 /* PC relative 16 bit */ -+#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ -+#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ -+ -+#define R_MIPS_SHIFT5 16 -+#define R_MIPS_SHIFT6 17 -+#define R_MIPS_64 18 -+#define R_MIPS_GOT_DISP 19 -+#define R_MIPS_GOT_PAGE 20 -+#define R_MIPS_GOT_OFST 21 -+#define R_MIPS_GOT_HI16 22 -+#define R_MIPS_GOT_LO16 23 -+#define R_MIPS_SUB 24 -+#define R_MIPS_INSERT_A 25 -+#define R_MIPS_INSERT_B 26 -+#define R_MIPS_DELETE 27 -+#define R_MIPS_HIGHER 28 -+#define R_MIPS_HIGHEST 29 -+#define R_MIPS_CALL_HI16 30 -+#define R_MIPS_CALL_LO16 31 -+#define R_MIPS_SCN_DISP 32 -+#define R_MIPS_REL16 33 -+#define R_MIPS_ADD_IMMEDIATE 34 -+#define R_MIPS_PJUMP 35 -+#define R_MIPS_RELGOT 36 -+#define R_MIPS_JALR 37 -+/* Keep this the last entry. */ -+#define R_MIPS_NUM 38 -+ -+/* Legal values for p_type field of Elf32_Phdr. */ -+ -+#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ -+#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ -+#define PT_MIPS_OPTIONS 0x70000002 -+ -+/* Special program header types. */ -+ -+#define PF_MIPS_LOCAL 0x10000000 -+ -+/* Legal values for d_tag field of Elf32_Dyn. */ -+ -+#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ -+#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ -+#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ -+#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ -+#define DT_MIPS_FLAGS 0x70000005 /* Flags */ -+#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ -+#define DT_MIPS_MSYM 0x70000007 -+#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ -+#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ -+#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ -+#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ -+#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ -+#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ -+#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ -+#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ -+#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ -+#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ -+#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ -+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in -+ DT_MIPS_DELTA_CLASS. */ -+#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ -+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in -+ DT_MIPS_DELTA_INSTANCE. */ -+#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ -+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in -+ DT_MIPS_DELTA_RELOC. */ -+#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta -+ relocations refer to. */ -+#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in -+ DT_MIPS_DELTA_SYM. */ -+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the -+ class declaration. */ -+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in -+ DT_MIPS_DELTA_CLASSSYM. */ -+#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ -+#define DT_MIPS_PIXIE_INIT 0x70000023 -+#define DT_MIPS_SYMBOL_LIB 0x70000024 -+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -+#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -+#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ -+#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ -+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -+#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ -+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve -+ function stored in GOT. */ -+#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added -+ by rld on dlopen() calls. */ -+#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ -+#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ -+#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ -+#define DT_MIPS_NUM 0x32 -+ -+/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ -+ -+#define RHF_NONE 0 /* No flags */ -+#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ -+#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ -+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ -+#define RHF_NO_MOVE (1 << 3) -+#define RHF_SGI_ONLY (1 << 4) -+#define RHF_GUARANTEE_INIT (1 << 5) -+#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -+#define RHF_GUARANTEE_START_INIT (1 << 7) -+#define RHF_PIXIE (1 << 8) -+#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -+#define RHF_REQUICKSTART (1 << 10) -+#define RHF_REQUICKSTARTED (1 << 11) -+#define RHF_CORD (1 << 12) -+#define RHF_NO_UNRES_UNDEF (1 << 13) -+#define RHF_RLD_ORDER_SAFE (1 << 14) -+ -+/* Entries found in sections of type SHT_MIPS_LIBLIST. */ -+ -+typedef struct -+{ -+ Elf32_Word l_name; /* Name (string table index) */ -+ Elf32_Word l_time_stamp; /* Timestamp */ -+ Elf32_Word l_checksum; /* Checksum */ -+ Elf32_Word l_version; /* Interface version */ -+ Elf32_Word l_flags; /* Flags */ -+} Elf32_Lib; -+ -+typedef struct -+{ -+ Elf64_Word l_name; /* Name (string table index) */ -+ Elf64_Word l_time_stamp; /* Timestamp */ -+ Elf64_Word l_checksum; /* Checksum */ -+ Elf64_Word l_version; /* Interface version */ -+ Elf64_Word l_flags; /* Flags */ -+} Elf64_Lib; -+ -+ -+/* Legal values for l_flags. */ -+ -+#define LL_NONE 0 -+#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ -+#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ -+#define LL_REQUIRE_MINOR (1 << 2) -+#define LL_EXPORTS (1 << 3) -+#define LL_DELAY_LOAD (1 << 4) -+#define LL_DELTA (1 << 5) -+ -+/* Entries found in sections of type SHT_MIPS_CONFLICT. */ -+ -+typedef Elf32_Addr Elf32_Conflict; -+ -+ -+/* HPPA specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ -+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ -+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ -+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ -+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch -+ prediction. */ -+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ -+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ -+ -+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ -+ -+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ -+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ -+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ -+ -+/* Additional section indeces. */ -+ -+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared -+ symbols in ANSI C. */ -+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ -+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ -+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ -+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ -+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ -+ -+#define STT_HP_OPAQUE (STT_LOOS + 0x1) -+#define STT_HP_STUB (STT_LOOS + 0x2) -+ -+/* HPPA relocs. */ -+ -+#define R_PARISC_NONE 0 /* No reloc. */ -+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ -+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ -+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ -+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ -+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ -+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ -+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ -+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ -+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ -+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ -+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ -+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ -+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ -+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ -+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ -+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ -+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ -+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ -+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ -+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ -+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ -+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ -+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ -+#define R_PARISC_FPTR64 64 /* 64 bits function address. */ -+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ -+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ -+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ -+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ -+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ -+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ -+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ -+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ -+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ -+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ -+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ -+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ -+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ -+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ -+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ -+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LORESERVE 128 -+#define R_PARISC_COPY 128 /* Copy relocation. */ -+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ -+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ -+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ -+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ -+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ -+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ -+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ -+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ -+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_HIRESERVE 255 -+ -+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PT_HP_TLS (PT_LOOS + 0x0) -+#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -+#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -+#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -+#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -+#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -+#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -+#define PT_HP_PARALLEL (PT_LOOS + 0x10) -+#define PT_HP_FASTBIND (PT_LOOS + 0x11) -+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -+#define PT_HP_STACK (PT_LOOS + 0x14) -+ -+#define PT_PARISC_ARCHEXT 0x70000000 -+#define PT_PARISC_UNWIND 0x70000001 -+ -+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PF_PARISC_SBP 0x08000000 -+ -+#define PF_HP_PAGE_SIZE 0x00100000 -+#define PF_HP_FAR_SHARED 0x00200000 -+#define PF_HP_NEAR_SHARED 0x00400000 -+#define PF_HP_CODE 0x01000000 -+#define PF_HP_MODIFY 0x02000000 -+#define PF_HP_LAZYSWAP 0x04000000 -+#define PF_HP_SBP 0x08000000 -+ -+ -+/* Alpha specific definitions. */ -+ -+/* Legal values for e_flags field of Elf64_Ehdr. */ -+ -+#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ -+#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ -+ -+/* Legal values for sh_type field of Elf64_Shdr. */ -+ -+/* These two are primerily concerned with ECOFF debugging info. */ -+#define SHT_ALPHA_DEBUG 0x70000001 -+#define SHT_ALPHA_REGINFO 0x70000002 -+ -+/* Legal values for sh_flags field of Elf64_Shdr. */ -+ -+#define SHF_ALPHA_GPREL 0x10000000 -+ -+/* Legal values for st_other field of Elf64_Sym. */ -+#define STO_ALPHA_NOPV 0x80 /* No PV required. */ -+#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ -+ -+/* Alpha relocs. */ -+ -+#define R_ALPHA_NONE 0 /* No reloc */ -+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ -+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ -+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ -+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ -+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ -+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ -+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ -+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ -+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ -+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ -+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ -+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ -+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ -+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ -+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ -+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ -+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ -+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ -+#define R_ALPHA_TLS_GD_HI 28 -+#define R_ALPHA_TLSGD 29 -+#define R_ALPHA_TLS_LDM 30 -+#define R_ALPHA_DTPMOD64 31 -+#define R_ALPHA_GOTDTPREL 32 -+#define R_ALPHA_DTPREL64 33 -+#define R_ALPHA_DTPRELHI 34 -+#define R_ALPHA_DTPRELLO 35 -+#define R_ALPHA_DTPREL16 36 -+#define R_ALPHA_GOTTPREL 37 -+#define R_ALPHA_TPREL64 38 -+#define R_ALPHA_TPRELHI 39 -+#define R_ALPHA_TPRELLO 40 -+#define R_ALPHA_TPREL16 41 -+/* Keep this the last entry. */ -+#define R_ALPHA_NUM 46 -+ -+/* Magic values of the LITUSE relocation addend. */ -+#define LITUSE_ALPHA_ADDR 0 -+#define LITUSE_ALPHA_BASE 1 -+#define LITUSE_ALPHA_BYTOFF 2 -+#define LITUSE_ALPHA_JSR 3 -+#define LITUSE_ALPHA_TLS_GD 4 -+#define LITUSE_ALPHA_TLS_LDM 5 -+ -+ -+/* PowerPC specific declarations */ -+ -+/* Values for Elf32/64_Ehdr.e_flags. */ -+#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ -+ -+/* Cygnus local bits below */ -+#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ -+#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib -+ flag */ -+ -+/* PowerPC relocations defined by the ABIs */ -+#define R_PPC_NONE 0 -+#define R_PPC_ADDR32 1 /* 32bit absolute address */ -+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -+#define R_PPC_ADDR16 3 /* 16bit absolute address */ -+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -+#define R_PPC_ADDR14_BRTAKEN 8 -+#define R_PPC_ADDR14_BRNTAKEN 9 -+#define R_PPC_REL24 10 /* PC relative 26 bit */ -+#define R_PPC_REL14 11 /* PC relative 16 bit */ -+#define R_PPC_REL14_BRTAKEN 12 -+#define R_PPC_REL14_BRNTAKEN 13 -+#define R_PPC_GOT16 14 -+#define R_PPC_GOT16_LO 15 -+#define R_PPC_GOT16_HI 16 -+#define R_PPC_GOT16_HA 17 -+#define R_PPC_PLTREL24 18 -+#define R_PPC_COPY 19 -+#define R_PPC_GLOB_DAT 20 -+#define R_PPC_JMP_SLOT 21 -+#define R_PPC_RELATIVE 22 -+#define R_PPC_LOCAL24PC 23 -+#define R_PPC_UADDR32 24 -+#define R_PPC_UADDR16 25 -+#define R_PPC_REL32 26 -+#define R_PPC_PLT32 27 -+#define R_PPC_PLTREL32 28 -+#define R_PPC_PLT16_LO 29 -+#define R_PPC_PLT16_HI 30 -+#define R_PPC_PLT16_HA 31 -+#define R_PPC_SDAREL16 32 -+#define R_PPC_SECTOFF 33 -+#define R_PPC_SECTOFF_LO 34 -+#define R_PPC_SECTOFF_HI 35 -+#define R_PPC_SECTOFF_HA 36 -+ -+/* PowerPC relocations defined for the TLS access ABI. */ -+#define R_PPC_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ -+#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ -+#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ -+#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ -+#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ -+#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ -+#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ -+#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ -+#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ -+ -+/* Keep this the last entry. */ -+#define R_PPC_NUM 95 -+ -+/* The remaining relocs are from the Embedded ELF ABI, and are not -+ in the SVR4 ELF ABI. */ -+#define R_PPC_EMB_NADDR32 101 -+#define R_PPC_EMB_NADDR16 102 -+#define R_PPC_EMB_NADDR16_LO 103 -+#define R_PPC_EMB_NADDR16_HI 104 -+#define R_PPC_EMB_NADDR16_HA 105 -+#define R_PPC_EMB_SDAI16 106 -+#define R_PPC_EMB_SDA2I16 107 -+#define R_PPC_EMB_SDA2REL 108 -+#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ -+#define R_PPC_EMB_MRKREF 110 -+#define R_PPC_EMB_RELSEC16 111 -+#define R_PPC_EMB_RELST_LO 112 -+#define R_PPC_EMB_RELST_HI 113 -+#define R_PPC_EMB_RELST_HA 114 -+#define R_PPC_EMB_BIT_FLD 115 -+#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ -+ -+/* Diab tool relocations. */ -+#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ -+#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ -+#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ -+#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ -+#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ -+#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ -+ -+/* This is a phony reloc to handle any old fashioned TOC16 references -+ that may still be in object files. */ -+#define R_PPC_TOC16 255 -+ -+ -+/* PowerPC64 relocations defined by the ABIs */ -+#define R_PPC64_NONE R_PPC_NONE -+#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ -+#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ -+#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ -+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ -+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ -+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ -+#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ -+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -+#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ -+#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ -+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -+#define R_PPC64_GOT16 R_PPC_GOT16 -+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA -+ -+#define R_PPC64_COPY R_PPC_COPY -+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -+#define R_PPC64_RELATIVE R_PPC_RELATIVE -+ -+#define R_PPC64_UADDR32 R_PPC_UADDR32 -+#define R_PPC64_UADDR16 R_PPC_UADDR16 -+#define R_PPC64_REL32 R_PPC_REL32 -+#define R_PPC64_PLT32 R_PPC_PLT32 -+#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA -+ -+#define R_PPC64_SECTOFF R_PPC_SECTOFF -+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -+#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ -+#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ -+#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ -+#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ -+#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ -+#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ -+#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ -+#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ -+#define R_PPC64_PLT64 45 /* doubleword64 L + A */ -+#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ -+#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ -+#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ -+#define R_PPC64_TOC 51 /* doubleword64 .TOC */ -+#define R_PPC64_PLTGOT16 52 /* half16* M + A */ -+#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ -+#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ -+#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ -+ -+#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ -+#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ -+#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ -+#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ -+#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ -+#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ -+#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ -+#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ -+#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ -+#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ -+#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ -+ -+/* PowerPC64 relocations defined for the TLS access ABI. */ -+#define R_PPC64_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ -+#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ -+#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ -+#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ -+#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ -+#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ -+#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ -+#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ -+#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ -+#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ -+#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ -+#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ -+#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ -+#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ -+#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ -+#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ -+#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ -+ -+/* Keep this the last entry. */ -+#define R_PPC64_NUM 107 -+ -+/* PowerPC64 specific values for the Dyn d_tag field. */ -+#define DT_PPC64_GLINK (DT_LOPROC + 0) -+#define DT_PPC64_NUM 1 -+ -+ -+/* ARM specific declarations */ -+ -+/* Processor specific flags for the ELF header e_flags field. */ -+#define EF_ARM_RELEXEC 0x01 -+#define EF_ARM_HASENTRY 0x02 -+#define EF_ARM_INTERWORK 0x04 -+#define EF_ARM_APCS_26 0x08 -+#define EF_ARM_APCS_FLOAT 0x10 -+#define EF_ARM_PIC 0x20 -+#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ -+#define EF_ARM_NEW_ABI 0x80 -+#define EF_ARM_OLD_ABI 0x100 -+ -+/* Other constants defined in the ARM ELF spec. version B-01. */ -+/* NB. These conflict with values defined above. */ -+#define EF_ARM_SYMSARESORTED 0x04 -+#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -+#define EF_ARM_MAPSYMSFIRST 0x10 -+#define EF_ARM_EABIMASK 0XFF000000 -+ -+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -+#define EF_ARM_EABI_UNKNOWN 0x00000000 -+#define EF_ARM_EABI_VER1 0x01000000 -+#define EF_ARM_EABI_VER2 0x02000000 -+ -+/* Additional symbol types for Thumb */ -+#define STT_ARM_TFUNC 0xd -+ -+/* ARM-specific values for sh_flags */ -+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ -+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined -+ in the input to a link step */ -+ -+/* ARM-specific program header flags */ -+#define PF_ARM_SB 0x10000000 /* Segment contains the location -+ addressed by the static base */ -+ -+/* ARM relocs. */ -+#define R_ARM_NONE 0 /* No reloc */ -+#define R_ARM_PC24 1 /* PC relative 26 bit branch */ -+#define R_ARM_ABS32 2 /* Direct 32 bit */ -+#define R_ARM_REL32 3 /* PC relative 32 bit */ -+#define R_ARM_PC13 4 -+#define R_ARM_ABS16 5 /* Direct 16 bit */ -+#define R_ARM_ABS12 6 /* Direct 12 bit */ -+#define R_ARM_THM_ABS5 7 -+#define R_ARM_ABS8 8 /* Direct 8 bit */ -+#define R_ARM_SBREL32 9 -+#define R_ARM_THM_PC22 10 -+#define R_ARM_THM_PC8 11 -+#define R_ARM_AMP_VCALL9 12 -+#define R_ARM_SWI24 13 -+#define R_ARM_THM_SWI8 14 -+#define R_ARM_XPC25 15 -+#define R_ARM_THM_XPC22 16 -+#define R_ARM_COPY 20 /* Copy symbol at runtime */ -+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ -+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ -+#define R_ARM_RELATIVE 23 /* Adjust by program base */ -+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ -+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ -+#define R_ARM_GOT32 26 /* 32 bit GOT entry */ -+#define R_ARM_PLT32 27 /* 32 bit PLT address */ -+#define R_ARM_ALU_PCREL_7_0 32 -+#define R_ARM_ALU_PCREL_15_8 33 -+#define R_ARM_ALU_PCREL_23_15 34 -+#define R_ARM_LDR_SBREL_11_0 35 -+#define R_ARM_ALU_SBREL_19_12 36 -+#define R_ARM_ALU_SBREL_27_20 37 -+#define R_ARM_GNU_VTENTRY 100 -+#define R_ARM_GNU_VTINHERIT 101 -+#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ -+#define R_ARM_THM_PC9 103 /* thumb conditional branch */ -+#define R_ARM_RXPC25 249 -+#define R_ARM_RSBREL32 250 -+#define R_ARM_THM_RPC22 251 -+#define R_ARM_RREL32 252 -+#define R_ARM_RABS22 253 -+#define R_ARM_RPC24 254 -+#define R_ARM_RBASE 255 -+/* Keep this the last entry. */ -+#define R_ARM_NUM 256 -+ -+/* IA-64 specific declarations. */ -+ -+/* Processor specific flags for the Ehdr e_flags field. */ -+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ -+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ -+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ -+ -+/* Processor specific values for the Phdr p_type field. */ -+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ -+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ -+ -+/* Processor specific flags for the Phdr p_flags field. */ -+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Shdr sh_type field. */ -+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ -+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ -+ -+/* Processor specific flags for the Shdr sh_flags field. */ -+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ -+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Dyn d_tag field. */ -+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -+#define DT_IA_64_NUM 1 -+ -+/* IA-64 relocations. */ -+#define R_IA64_NONE 0x00 /* none */ -+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ -+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ -+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ -+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ -+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ -+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ -+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ -+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ -+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ -+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ -+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ -+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ -+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ -+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ -+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ -+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ -+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ -+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ -+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ -+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ -+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ -+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ -+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ -+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ -+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ -+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ -+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ -+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ -+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ -+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ -+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ -+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ -+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ -+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ -+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ -+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ -+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ -+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ -+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ -+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ -+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ -+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ -+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ -+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ -+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ -+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ -+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ -+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ -+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ -+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ -+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ -+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ -+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ -+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ -+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ -+#define R_IA64_COPY 0x84 /* copy relocation */ -+#define R_IA64_SUB 0x85 /* Addend and symbol difference */ -+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ -+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ -+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ -+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ -+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ -+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ -+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ -+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ -+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ -+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ -+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ -+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ -+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ -+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ -+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ -+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ -+ -+/* SH specific declarations */ -+ -+/* SH relocs. */ -+#define R_SH_NONE 0 -+#define R_SH_DIR32 1 -+#define R_SH_REL32 2 -+#define R_SH_DIR8WPN 3 -+#define R_SH_IND12W 4 -+#define R_SH_DIR8WPL 5 -+#define R_SH_DIR8WPZ 6 -+#define R_SH_DIR8BP 7 -+#define R_SH_DIR8W 8 -+#define R_SH_DIR8L 9 -+#define R_SH_SWITCH16 25 -+#define R_SH_SWITCH32 26 -+#define R_SH_USES 27 -+#define R_SH_COUNT 28 -+#define R_SH_ALIGN 29 -+#define R_SH_CODE 30 -+#define R_SH_DATA 31 -+#define R_SH_LABEL 32 -+#define R_SH_SWITCH8 33 -+#define R_SH_GNU_VTINHERIT 34 -+#define R_SH_GNU_VTENTRY 35 -+#define R_SH_TLS_GD_32 144 -+#define R_SH_TLS_LD_32 145 -+#define R_SH_TLS_LDO_32 146 -+#define R_SH_TLS_IE_32 147 -+#define R_SH_TLS_LE_32 148 -+#define R_SH_TLS_DTPMOD32 149 -+#define R_SH_TLS_DTPOFF32 150 -+#define R_SH_TLS_TPOFF32 151 -+#define R_SH_GOT32 160 -+#define R_SH_PLT32 161 -+#define R_SH_COPY 162 -+#define R_SH_GLOB_DAT 163 -+#define R_SH_JMP_SLOT 164 -+#define R_SH_RELATIVE 165 -+#define R_SH_GOTOFF 166 -+#define R_SH_GOTPC 167 -+/* Keep this the last entry. */ -+#define R_SH_NUM 256 -+ -+/* Additional s390 relocs */ -+ -+#define R_390_NONE 0 /* No reloc. */ -+#define R_390_8 1 /* Direct 8 bit. */ -+#define R_390_12 2 /* Direct 12 bit. */ -+#define R_390_16 3 /* Direct 16 bit. */ -+#define R_390_32 4 /* Direct 32 bit. */ -+#define R_390_PC32 5 /* PC relative 32 bit. */ -+#define R_390_GOT12 6 /* 12 bit GOT offset. */ -+#define R_390_GOT32 7 /* 32 bit GOT offset. */ -+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ -+#define R_390_COPY 9 /* Copy symbol at runtime. */ -+#define R_390_GLOB_DAT 10 /* Create GOT entry. */ -+#define R_390_JMP_SLOT 11 /* Create PLT entry. */ -+#define R_390_RELATIVE 12 /* Adjust by program base. */ -+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ -+#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ -+#define R_390_GOT16 15 /* 16 bit GOT offset. */ -+#define R_390_PC16 16 /* PC relative 16 bit. */ -+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ -+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ -+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ -+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ -+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ -+#define R_390_64 22 /* Direct 64 bit. */ -+#define R_390_PC64 23 /* PC relative 64 bit. */ -+#define R_390_GOT64 24 /* 64 bit GOT offset. */ -+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ -+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ -+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ -+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ -+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ -+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ -+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ -+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ -+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ -+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ -+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ -+#define R_390_TLS_GDCALL 38 /* Tag for function call in general -+ dynamic TLS code. */ -+#define R_390_TLS_LDCALL 39 /* Tag for function call in local -+ dynamic TLS code. */ -+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ -+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ -+#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS -+ block. */ -+ -+/* Keep this the last entry. */ -+#define R_390_NUM 57 -+ -+/* CRIS relocations. */ -+#define R_CRIS_NONE 0 -+#define R_CRIS_8 1 -+#define R_CRIS_16 2 -+#define R_CRIS_32 3 -+#define R_CRIS_8_PCREL 4 -+#define R_CRIS_16_PCREL 5 -+#define R_CRIS_32_PCREL 6 -+#define R_CRIS_GNU_VTINHERIT 7 -+#define R_CRIS_GNU_VTENTRY 8 -+#define R_CRIS_COPY 9 -+#define R_CRIS_GLOB_DAT 10 -+#define R_CRIS_JUMP_SLOT 11 -+#define R_CRIS_RELATIVE 12 -+#define R_CRIS_16_GOT 13 -+#define R_CRIS_32_GOT 14 -+#define R_CRIS_16_GOTPLT 15 -+#define R_CRIS_32_GOTPLT 16 -+#define R_CRIS_32_GOTREL 17 -+#define R_CRIS_32_PLT_GOTREL 18 -+#define R_CRIS_32_PLT_PCREL 19 -+ -+#define R_CRIS_NUM 20 -+ -+/* AMD x86-64 relocations. */ -+#define R_X86_64_NONE 0 /* No reloc */ -+#define R_X86_64_64 1 /* Direct 64 bit */ -+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ -+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ -+#define R_X86_64_PLT32 4 /* 32 bit PLT address */ -+#define R_X86_64_COPY 5 /* Copy symbol at runtime */ -+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ -+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ -+#define R_X86_64_RELATIVE 8 /* Adjust by program base */ -+#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative -+ offset to GOT */ -+#define R_X86_64_32 10 /* Direct 32 bit zero extended */ -+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ -+#define R_X86_64_16 12 /* Direct 16 bit zero extended */ -+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ -+#define R_X86_64_8 14 /* Direct 8 bit sign extended */ -+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ -+#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ -+#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ -+#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ -+#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset -+ to two GOT entries for GD symbol */ -+#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset -+ to two GOT entries for LD symbol */ -+#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ -+#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset -+ to GOT entry for IE symbol */ -+#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ -+ -+#define R_X86_64_NUM 24 -+ -+__END_DECLS -+ -+#endif /* elf.h */ - - #include "elfconfig.h" - -@@ -185,3 +2631,4 @@ - void fatal(const char *fmt, ...); - void warn(const char *fmt, ...); - void merror(const char *fmt, ...); -+ -diff -Nur linux-3.11.5.orig/scripts/mod/sumversion.c linux-3.11.5/scripts/mod/sumversion.c ---- linux-3.11.5.orig/scripts/mod/sumversion.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/sumversion.c 2013-10-16 18:09:31.000000000 +0200 -@@ -1,4 +1,4 @@ --#include -+/* #include */ - #ifdef __sun__ - #include - #else -diff -Nur linux-3.11.5.orig/tools/include/tools/linux_types.h linux-3.11.5/tools/include/tools/linux_types.h ---- linux-3.11.5.orig/tools/include/tools/linux_types.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.11.5/tools/include/tools/linux_types.h 2013-10-16 18:09:31.000000000 +0200 -@@ -0,0 +1,22 @@ -+#ifndef __LINUX_TYPES_H -+#define __LINUX_TYPES_H -+ -+#include -+ -+typedef uint8_t __u8; -+typedef uint8_t __be8; -+typedef uint8_t __le8; -+ -+typedef uint16_t __u16; -+typedef uint16_t __be16; -+typedef uint16_t __le16; -+ -+typedef uint32_t __u32; -+typedef uint32_t __be32; -+typedef uint32_t __le32; -+ -+typedef uint64_t __u64; -+typedef uint64_t __be64; -+typedef uint64_t __le64; -+ -+#endif diff --git a/target/linux/patches/3.12.40/cleankernel.patch b/target/linux/patches/3.12.40/cleankernel.patch deleted file mode 100644 index d8c055dc3..000000000 --- a/target/linux/patches/3.12.40/cleankernel.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.5.orig/scripts/Makefile.headersinst linux-3.11.5/scripts/Makefile.headersinst ---- linux-3.11.5.orig/scripts/Makefile.headersinst 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/Makefile.headersinst 2013-10-15 16:33:10.000000000 +0200 -@@ -107,7 +107,6 @@ - - targets += $(install-file) - $(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE -- $(if $(unwanted),$(call cmd,remove),) - $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) - $(call if_changed,install) - diff --git a/target/linux/patches/3.12.40/defaults.patch b/target/linux/patches/3.12.40/defaults.patch deleted file mode 100644 index 6cdca084e..000000000 --- a/target/linux/patches/3.12.40/defaults.patch +++ /dev/null @@ -1,46 +0,0 @@ -diff -Nur linux-3.0.4.orig/fs/Kconfig linux-3.0.4/fs/Kconfig ---- linux-3.0.4.orig/fs/Kconfig 2011-08-29 22:56:30.000000000 +0200 -+++ linux-3.0.4/fs/Kconfig 2011-10-15 22:08:44.000000000 +0200 -@@ -47,7 +47,7 @@ - def_bool n - - config EXPORTFS -- tristate -+ def_bool y - - config FILE_LOCKING - bool "Enable POSIX file locking API" if EXPERT -diff -Nur linux-3.0.4.orig/fs/notify/Kconfig linux-3.0.4/fs/notify/Kconfig ---- linux-3.0.4.orig/fs/notify/Kconfig 2011-08-29 22:56:30.000000000 +0200 -+++ linux-3.0.4/fs/notify/Kconfig 2011-10-15 22:02:00.000000000 +0200 -@@ -1,5 +1,5 @@ - config FSNOTIFY -- def_bool n -+ def_bool y - - source "fs/notify/dnotify/Kconfig" - source "fs/notify/inotify/Kconfig" -diff -Nur linux-3.11.10.orig/drivers/scsi/Kconfig linux-3.11.10/drivers/scsi/Kconfig ---- linux-3.11.10.orig/drivers/scsi/Kconfig 2013-11-29 19:42:37.000000000 +0100 -+++ linux-3.11.10/drivers/scsi/Kconfig 2013-12-27 19:13:21.000000000 +0100 -@@ -2,7 +2,7 @@ - - config SCSI_MOD - tristate -- default y if SCSI=n || SCSI=y -+ default y if SCSI=y - default m if SCSI=m - - config RAID_ATTRS -diff -Nur linux-3.11.10.orig/usr/Kconfig linux-3.11.10/usr/Kconfig ---- linux-3.11.10.orig/usr/Kconfig 2013-11-29 19:42:37.000000000 +0100 -+++ linux-3.11.10/usr/Kconfig 2013-12-27 19:15:16.000000000 +0100 -@@ -47,7 +47,7 @@ - - config RD_GZIP - bool "Support initial ramdisks compressed using gzip" if EXPERT -- default y -+ default n - depends on BLK_DEV_INITRD - select DECOMPRESS_GZIP - help diff --git a/target/linux/patches/3.12.40/disable-netfilter.patch b/target/linux/patches/3.12.40/disable-netfilter.patch deleted file mode 100644 index 7b1ca013a..000000000 --- a/target/linux/patches/3.12.40/disable-netfilter.patch +++ /dev/null @@ -1,160 +0,0 @@ -diff -Nur linux-3.7.3.orig/net/Kconfig linux-3.7.3/net/Kconfig ---- linux-3.7.3.orig/net/Kconfig 2013-01-17 17:47:40.000000000 +0100 -+++ linux-3.7.3/net/Kconfig 2013-01-19 18:19:55.000000000 +0100 -@@ -163,7 +163,7 @@ - config NETFILTER_ADVANCED - bool "Advanced netfilter configuration" - depends on NETFILTER -- default y -+ default n - help - If you say Y here you can select between all the netfilter modules. - If you say N the more unusual ones will not be shown and the -@@ -175,7 +175,7 @@ - bool "Bridged IP/ARP packets filtering" - depends on BRIDGE && NETFILTER && INET - depends on NETFILTER_ADVANCED -- default y -+ default n - ---help--- - Enabling this option will let arptables resp. iptables see bridged - ARP resp. IP traffic. If you want a bridging firewall, you probably -diff -Nur linux-3.7.3.orig/net/netfilter/Kconfig linux-3.7.3/net/netfilter/Kconfig ---- linux-3.7.3.orig/net/netfilter/Kconfig 2013-01-17 17:47:40.000000000 +0100 -+++ linux-3.7.3/net/netfilter/Kconfig 2013-01-19 18:21:41.000000000 +0100 -@@ -22,7 +22,6 @@ - - config NETFILTER_NETLINK_LOG - tristate "Netfilter LOG over NFNETLINK interface" -- default m if NETFILTER_ADVANCED=n - select NETFILTER_NETLINK - help - If this option is enabled, the kernel will include support -@@ -34,7 +33,6 @@ - - config NF_CONNTRACK - tristate "Netfilter connection tracking support" -- default m if NETFILTER_ADVANCED=n - help - Connection tracking keeps a record of what packets have passed - through your machine, in order to figure out how they are related -@@ -60,7 +58,6 @@ - config NF_CONNTRACK_SECMARK - bool 'Connection tracking security mark support' - depends on NETWORK_SECMARK -- default m if NETFILTER_ADVANCED=n - help - This option enables security markings to be applied to - connections. Typically they are copied to connections from -@@ -177,7 +174,6 @@ - - config NF_CONNTRACK_FTP - tristate "FTP protocol support" -- default m if NETFILTER_ADVANCED=n - help - Tracking FTP connections is problematic: special helpers are - required for tracking them, and doing masquerading and other forms -@@ -211,7 +207,6 @@ - - config NF_CONNTRACK_IRC - tristate "IRC protocol support" -- default m if NETFILTER_ADVANCED=n - help - There is a commonly-used extension to IRC called - Direct Client-to-Client Protocol (DCC). This enables users to send -@@ -296,7 +291,6 @@ - - config NF_CONNTRACK_SIP - tristate "SIP protocol support" -- default m if NETFILTER_ADVANCED=n - help - SIP is an application-layer control protocol that can establish, - modify, and terminate multimedia sessions (conferences) such as -@@ -320,7 +314,6 @@ - config NF_CT_NETLINK - tristate 'Connection tracking netlink interface' - select NETFILTER_NETLINK -- default m if NETFILTER_ADVANCED=n - help - This option enables support for a netlink-based userspace interface - -@@ -424,7 +417,6 @@ - - config NETFILTER_XTABLES - tristate "Netfilter Xtables support (required for ip_tables)" -- default m if NETFILTER_ADVANCED=n - help - This is required if you intend to use any of ip_tables, - ip6_tables or arp_tables. -@@ -435,7 +427,6 @@ - - config NETFILTER_XT_MARK - tristate 'nfmark target and match support' -- default m if NETFILTER_ADVANCED=n - ---help--- - This option adds the "MARK" target and "mark" match. - -@@ -527,7 +518,6 @@ - config NETFILTER_XT_TARGET_CONNSECMARK - tristate '"CONNSECMARK" target support' - depends on NF_CONNTRACK && NF_CONNTRACK_SECMARK -- default m if NETFILTER_ADVANCED=n - help - The CONNSECMARK target copies security markings from packets - to connections, and restores security markings from connections -@@ -632,7 +622,6 @@ - - config NETFILTER_XT_TARGET_LOG - tristate "LOG target support" -- default m if NETFILTER_ADVANCED=n - help - This option adds a `LOG' target, which allows you to create rules in - any iptables table which records the packet header to the syslog. -@@ -660,7 +649,6 @@ - - config NETFILTER_XT_TARGET_NFLOG - tristate '"NFLOG" target support' -- default m if NETFILTER_ADVANCED=n - select NETFILTER_NETLINK_LOG - help - This option enables the NFLOG target, which allows to LOG -@@ -741,7 +729,6 @@ - config NETFILTER_XT_TARGET_SECMARK - tristate '"SECMARK" target support' - depends on NETWORK_SECMARK -- default m if NETFILTER_ADVANCED=n - help - The SECMARK target allows security marking of network - packets, for use with security subsystems. -@@ -751,7 +738,6 @@ - config NETFILTER_XT_TARGET_TCPMSS - tristate '"TCPMSS" target support' - depends on (IPV6 || IPV6=n) -- default m if NETFILTER_ADVANCED=n - ---help--- - This option adds a `TCPMSS' target, which allows you to alter the - MSS value of TCP SYN packets, to control the maximum size for that -@@ -856,7 +842,6 @@ - config NETFILTER_XT_MATCH_CONNTRACK - tristate '"conntrack" connection tracking match support' - depends on NF_CONNTRACK -- default m if NETFILTER_ADVANCED=n - help - This is a general conntrack match module, a superset of the state match. - -@@ -1063,7 +1048,6 @@ - config NETFILTER_XT_MATCH_POLICY - tristate 'IPsec "policy" match support' - depends on XFRM -- default m if NETFILTER_ADVANCED=n - help - Policy matching allows you to match packets based on the - IPsec policy that was used during decapsulation/will -@@ -1170,7 +1154,6 @@ - config NETFILTER_XT_MATCH_STATE - tristate '"state" match support' - depends on NF_CONNTRACK -- default m if NETFILTER_ADVANCED=n - help - Connection state matching allows you to match packets based on their - relationship to a tracked connection (ie. previous packets). This diff --git a/target/linux/patches/3.12.40/export-symbol-for-exmap.patch b/target/linux/patches/3.12.40/export-symbol-for-exmap.patch deleted file mode 100644 index 4f0fc8449..000000000 --- a/target/linux/patches/3.12.40/export-symbol-for-exmap.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.5.orig/kernel/pid.c linux-3.11.5/kernel/pid.c ---- linux-3.11.5.orig/kernel/pid.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/kernel/pid.c 2013-10-29 15:37:02.000000000 +0100 -@@ -450,6 +450,7 @@ - { - return find_task_by_pid_ns(vnr, task_active_pid_ns(current)); - } -+EXPORT_SYMBOL(find_task_by_vpid); - - struct pid *get_task_pid(struct task_struct *task, enum pid_type type) - { diff --git a/target/linux/patches/3.12.40/gemalto.patch b/target/linux/patches/3.12.40/gemalto.patch deleted file mode 100644 index 65f7af1d7..000000000 --- a/target/linux/patches/3.12.40/gemalto.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c linux-2.6.36/drivers/serial/8250/serial_cs.c ---- linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c 2010-10-20 22:30:22.000000000 +0200 -+++ linux-2.6.36/drivers/tty/serial/8250/serial_cs.c 2010-12-13 23:03:40.000000000 +0100 -@@ -794,6 +794,7 @@ - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), -+ PCMCIA_DEVICE_MANF_CARD(0x0157, 0x0100), /* Gemalto SCR */ - PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ - PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ - PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), diff --git a/target/linux/patches/3.12.40/lemote-rfkill.patch b/target/linux/patches/3.12.40/lemote-rfkill.patch deleted file mode 100644 index a61488434..000000000 --- a/target/linux/patches/3.12.40/lemote-rfkill.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -Nur linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c ---- linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-19 00:15:34.000000000 +0100 -+++ linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-27 23:29:46.000000000 +0200 -@@ -22,6 +22,9 @@ - - static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) - { -+#ifdef CONFIG_LEMOTE_MACH2F -+ return 1; -+#else - u8 gpio; - - gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); -@@ -29,6 +32,7 @@ - gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); - - return gpio & priv->rfkill_mask; -+#endif - } - - void rtl8187_rfkill_init(struct ieee80211_hw *hw) diff --git a/target/linux/patches/3.12.40/microblaze-ethernet.patch b/target/linux/patches/3.12.40/microblaze-ethernet.patch deleted file mode 100644 index 742ab477e..000000000 --- a/target/linux/patches/3.12.40/microblaze-ethernet.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c ---- linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-11-29 19:42:37.000000000 +0100 -+++ linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-12-23 20:01:14.000000000 +0100 -@@ -1282,6 +1282,7 @@ - { .compatible = "xlnx,opb-ethernetlite-1.01.b", }, - { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, - { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, -+ { .compatible = "xlnx,xps-ethernetlite-2.00.b", }, - { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, - { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, - { /* end of list */ }, diff --git a/target/linux/patches/3.12.40/microblaze-setup.patch b/target/linux/patches/3.12.40/microblaze-setup.patch deleted file mode 100644 index 43815f274..000000000 --- a/target/linux/patches/3.12.40/microblaze-setup.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-3.11.10.orig/arch/microblaze/kernel/setup.c linux-3.11.10/arch/microblaze/kernel/setup.c ---- linux-3.11.10.orig/arch/microblaze/kernel/setup.c 2013-11-29 19:42:37.000000000 +0100 -+++ linux-3.11.10/arch/microblaze/kernel/setup.c 2013-12-08 13:02:37.000000000 +0100 -@@ -177,7 +177,7 @@ - #else - if (!msr) { - pr_info("!!!Your kernel not setup MSR instruction but "); -- pr_cont"CPU have it %x\n", msr); -+ pr_cont("CPU have it %x\n", msr); - } - #endif - diff --git a/target/linux/patches/3.12.40/mips-lzo-fix.patch b/target/linux/patches/3.12.40/mips-lzo-fix.patch deleted file mode 100644 index 0740bdc6a..000000000 --- a/target/linux/patches/3.12.40/mips-lzo-fix.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/mips/boot/compressed/decompress.c linux-3.11.5/arch/mips/boot/compressed/decompress.c ---- linux-3.11.5.orig/arch/mips/boot/compressed/decompress.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/mips/boot/compressed/decompress.c 2013-11-08 18:19:47.000000000 +0100 -@@ -43,7 +43,7 @@ - /* activate the code for pre-boot environment */ - #define STATIC static - --#ifdef CONFIG_KERNEL_GZIP -+#if defined(CONFIG_KERNEL_GZIP) || defined(CONFIG_KERNEL_LZO) - void *memcpy(void *dest, const void *src, size_t n) - { - int i; diff --git a/target/linux/patches/3.12.40/mkpiggy.patch b/target/linux/patches/3.12.40/mkpiggy.patch deleted file mode 100644 index d4e815cd2..000000000 --- a/target/linux/patches/3.12.40/mkpiggy.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff -Nur linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c ---- linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c 2012-06-22 20:37:50.000000000 +0200 -+++ linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c 2012-07-03 09:48:02.000000000 +0200 -@@ -29,7 +29,14 @@ - #include - #include - #include --#include -+ -+static uint32_t getle32(const void *p) -+{ -+ const uint8_t *cp = p; -+ -+ return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + -+ ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); -+} - - int main(int argc, char *argv[]) - { -@@ -62,7 +69,7 @@ - } - - ilen = ftell(f); -- olen = get_unaligned_le32(&olen); -+ olen = getle32(&olen); - fclose(f); - - /* diff --git a/target/linux/patches/3.12.40/mtd-rootfs.patch b/target/linux/patches/3.12.40/mtd-rootfs.patch deleted file mode 100644 index 775d5fc80..000000000 --- a/target/linux/patches/3.12.40/mtd-rootfs.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff -Nur linux-3.5.orig//drivers/mtd/mtdpart.c linux-3.5/drivers/mtd/mtdpart.c ---- linux-3.5.orig//drivers/mtd/mtdpart.c 2012-07-21 22:58:29.000000000 +0200 -+++ linux-3.5/drivers/mtd/mtdpart.c 2012-07-31 23:59:07.000000000 +0200 -@@ -30,6 +30,7 @@ - #include - #include - #include -+#include - - #include "mtdcore.h" - -@@ -637,6 +638,14 @@ - if (IS_ERR(slave)) - return PTR_ERR(slave); - -+ if (strcmp(parts[i].name, "rootfs") == 0) { -+ if (ROOT_DEV == 0) { -+ printk(KERN_NOTICE "mtd: partition \"rootfs\" " -+ "set to be root filesystem\n"); -+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, i); -+ } -+ } -+ - mutex_lock(&mtd_partitions_mutex); - list_add(&slave->list, &mtd_partitions); - mutex_unlock(&mtd_partitions_mutex); diff --git a/target/linux/patches/3.12.40/non-static.patch b/target/linux/patches/3.12.40/non-static.patch deleted file mode 100644 index a967703d0..000000000 --- a/target/linux/patches/3.12.40/non-static.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -Nur linux-2.6.39-rc6.orig/fs/namei.c linux-2.6.39-rc6/fs/namei.c ---- linux-2.6.39-rc6.orig/fs/namei.c 2011-05-04 04:59:13.000000000 +0200 -+++ linux-2.6.39-rc6/fs/namei.c 2011-05-05 11:30:14.000000000 +0200 -@@ -1769,7 +1769,7 @@ - * needs parent already locked. Doesn't follow mounts. - * SMP-safe. - */ --static struct dentry *lookup_hash(struct nameidata *nd) -+struct dentry *lookup_hash(struct nameidata *nd) - { - return __lookup_hash(&nd->last, nd->path.dentry, nd); - } -diff -Nur linux-2.6.39-rc6.orig/fs/splice.c linux-2.6.39-rc6/fs/splice.c ---- linux-2.6.39-rc6.orig/fs/splice.c 2011-05-04 04:59:13.000000000 +0200 -+++ linux-2.6.39-rc6/fs/splice.c 2011-05-05 11:31:04.000000000 +0200 -@@ -1081,7 +1081,7 @@ - /* - * Attempt to initiate a splice from pipe to file. - */ --static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags) - { - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, -@@ -1109,7 +1109,7 @@ - /* - * Attempt to initiate a splice from a file to a pipe. - */ --static long do_splice_to(struct file *in, loff_t *ppos, -+long do_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) - { diff --git a/target/linux/patches/3.12.40/ppc64-missing-zlib.patch b/target/linux/patches/3.12.40/ppc64-missing-zlib.patch deleted file mode 100644 index c6e0616be..000000000 --- a/target/linux/patches/3.12.40/ppc64-missing-zlib.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig ---- linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig 2013-11-01 15:23:09.000000000 +0100 -@@ -17,6 +17,7 @@ - select PPC_NATIVE - select PPC_PCI_CHOICE if EXPERT - select ZLIB_DEFLATE -+ select ZLIB_INFLATE - select PPC_DOORBELL - select HAVE_CONTEXT_TRACKING - select HOTPLUG_CPU if SMP diff --git a/target/linux/patches/3.12.40/regmap-bool.patch b/target/linux/patches/3.12.40/regmap-bool.patch deleted file mode 100644 index c3fd9a318..000000000 --- a/target/linux/patches/3.12.40/regmap-bool.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff -Nur linux-3.12.33.orig/drivers/base/regmap/Kconfig linux-3.12.33/drivers/base/regmap/Kconfig ---- linux-3.12.33.orig/drivers/base/regmap/Kconfig 2014-11-15 13:28:07.000000000 +0100 -+++ linux-3.12.33/drivers/base/regmap/Kconfig 2014-12-04 11:18:59.146540398 +0100 -@@ -10,13 +10,13 @@ - bool - - config REGMAP_I2C -- tristate -+ bool - - config REGMAP_SPI -- tristate -+ bool - - config REGMAP_MMIO -- tristate -+ bool - - config REGMAP_IRQ - bool diff --git a/target/linux/patches/3.12.40/relocs.patch b/target/linux/patches/3.12.40/relocs.patch deleted file mode 100644 index 649b9e73e..000000000 --- a/target/linux/patches/3.12.40/relocs.patch +++ /dev/null @@ -1,2693 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/x86/tools/relocs.h linux-3.11.5/arch/x86/tools/relocs.h ---- linux-3.11.5.orig/arch/x86/tools/relocs.h 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/x86/tools/relocs.h 2013-10-20 17:41:16.000000000 +0200 -@@ -9,10 +9,14 @@ - #include - #include - #include -+#ifdef __linux__ - #include - #include - #define USE_BSD - #include -+#else -+#include "elf.h" -+#endif - #include - #include - -diff -Nur linux-3.11.5.orig/tools/include/elf.h linux-3.11.5/tools/include/elf.h ---- linux-3.11.5.orig/tools/include/elf.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.11.5/tools/include/elf.h 2013-10-20 17:42:10.000000000 +0200 -@@ -0,0 +1,2671 @@ -+#ifndef _ELF_H -+#define _ELF_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#include -+ -+typedef uint16_t Elf32_Half; -+typedef uint16_t Elf64_Half; -+ -+typedef uint32_t Elf32_Word; -+typedef int32_t Elf32_Sword; -+typedef uint32_t Elf64_Word; -+typedef int32_t Elf64_Sword; -+ -+typedef uint64_t Elf32_Xword; -+typedef int64_t Elf32_Sxword; -+typedef uint64_t Elf64_Xword; -+typedef int64_t Elf64_Sxword; -+ -+typedef uint32_t Elf32_Addr; -+typedef uint64_t Elf64_Addr; -+ -+typedef uint32_t Elf32_Off; -+typedef uint64_t Elf64_Off; -+ -+typedef uint16_t Elf32_Section; -+typedef uint16_t Elf64_Section; -+ -+typedef Elf32_Half Elf32_Versym; -+typedef Elf64_Half Elf64_Versym; -+ -+#define EI_NIDENT (16) -+ -+typedef struct { -+ unsigned char e_ident[EI_NIDENT]; -+ Elf32_Half e_type; -+ Elf32_Half e_machine; -+ Elf32_Word e_version; -+ Elf32_Addr e_entry; -+ Elf32_Off e_phoff; -+ Elf32_Off e_shoff; -+ Elf32_Word e_flags; -+ Elf32_Half e_ehsize; -+ Elf32_Half e_phentsize; -+ Elf32_Half e_phnum; -+ Elf32_Half e_shentsize; -+ Elf32_Half e_shnum; -+ Elf32_Half e_shstrndx; -+} Elf32_Ehdr; -+ -+typedef struct { -+ unsigned char e_ident[EI_NIDENT]; -+ Elf64_Half e_type; -+ Elf64_Half e_machine; -+ Elf64_Word e_version; -+ Elf64_Addr e_entry; -+ Elf64_Off e_phoff; -+ Elf64_Off e_shoff; -+ Elf64_Word e_flags; -+ Elf64_Half e_ehsize; -+ Elf64_Half e_phentsize; -+ Elf64_Half e_phnum; -+ Elf64_Half e_shentsize; -+ Elf64_Half e_shnum; -+ Elf64_Half e_shstrndx; -+} Elf64_Ehdr; -+ -+#define EI_MAG0 0 -+#define ELFMAG0 0x7f -+ -+#define EI_MAG1 1 -+#define ELFMAG1 'E' -+ -+#define EI_MAG2 2 -+#define ELFMAG2 'L' -+ -+#define EI_MAG3 3 -+#define ELFMAG3 'F' -+ -+ -+#define ELFMAG "\177ELF" -+#define SELFMAG 4 -+ -+#define EI_CLASS 4 -+#define ELFCLASSNONE 0 -+#define ELFCLASS32 1 -+#define ELFCLASS64 2 -+#define ELFCLASSNUM 3 -+ -+#define EI_DATA 5 -+#define ELFDATANONE 0 -+#define ELFDATA2LSB 1 -+#define ELFDATA2MSB 2 -+#define ELFDATANUM 3 -+ -+#define EI_VERSION 6 -+ -+ -+#define EI_OSABI 7 -+#define ELFOSABI_NONE 0 -+#define ELFOSABI_SYSV 0 -+#define ELFOSABI_HPUX 1 -+#define ELFOSABI_NETBSD 2 -+#define ELFOSABI_LINUX 3 -+#define ELFOSABI_GNU 3 -+#define ELFOSABI_SOLARIS 6 -+#define ELFOSABI_AIX 7 -+#define ELFOSABI_IRIX 8 -+#define ELFOSABI_FREEBSD 9 -+#define ELFOSABI_TRU64 10 -+#define ELFOSABI_MODESTO 11 -+#define ELFOSABI_OPENBSD 12 -+#define ELFOSABI_ARM 97 -+#define ELFOSABI_STANDALONE 255 -+ -+#define EI_ABIVERSION 8 -+ -+#define EI_PAD 9 -+ -+ -+ -+#define ET_NONE 0 -+#define ET_REL 1 -+#define ET_EXEC 2 -+#define ET_DYN 3 -+#define ET_CORE 4 -+#define ET_NUM 5 -+#define ET_LOOS 0xfe00 -+#define ET_HIOS 0xfeff -+#define ET_LOPROC 0xff00 -+#define ET_HIPROC 0xffff -+ -+ -+ -+#define EM_NONE 0 -+#define EM_M32 1 -+#define EM_SPARC 2 -+#define EM_386 3 -+#define EM_68K 4 -+#define EM_88K 5 -+#define EM_860 7 -+#define EM_MIPS 8 -+#define EM_S370 9 -+#define EM_MIPS_RS3_LE 10 -+ -+#define EM_PARISC 15 -+#define EM_VPP500 17 -+#define EM_SPARC32PLUS 18 -+#define EM_960 19 -+#define EM_PPC 20 -+#define EM_PPC64 21 -+#define EM_S390 22 -+ -+#define EM_V800 36 -+#define EM_FR20 37 -+#define EM_RH32 38 -+#define EM_RCE 39 -+#define EM_ARM 40 -+#define EM_FAKE_ALPHA 41 -+#define EM_SH 42 -+#define EM_SPARCV9 43 -+#define EM_TRICORE 44 -+#define EM_ARC 45 -+#define EM_H8_300 46 -+#define EM_H8_300H 47 -+#define EM_H8S 48 -+#define EM_H8_500 49 -+#define EM_IA_64 50 -+#define EM_MIPS_X 51 -+#define EM_COLDFIRE 52 -+#define EM_68HC12 53 -+#define EM_MMA 54 -+#define EM_PCP 55 -+#define EM_NCPU 56 -+#define EM_NDR1 57 -+#define EM_STARCORE 58 -+#define EM_ME16 59 -+#define EM_ST100 60 -+#define EM_TINYJ 61 -+#define EM_X86_64 62 -+#define EM_PDSP 63 -+ -+#define EM_FX66 66 -+#define EM_ST9PLUS 67 -+#define EM_ST7 68 -+#define EM_68HC16 69 -+#define EM_68HC11 70 -+#define EM_68HC08 71 -+#define EM_68HC05 72 -+#define EM_SVX 73 -+#define EM_ST19 74 -+#define EM_VAX 75 -+#define EM_CRIS 76 -+#define EM_JAVELIN 77 -+#define EM_FIREPATH 78 -+#define EM_ZSP 79 -+#define EM_MMIX 80 -+#define EM_HUANY 81 -+#define EM_PRISM 82 -+#define EM_AVR 83 -+#define EM_FR30 84 -+#define EM_D10V 85 -+#define EM_D30V 86 -+#define EM_V850 87 -+#define EM_M32R 88 -+#define EM_MN10300 89 -+#define EM_MN10200 90 -+#define EM_PJ 91 -+#define EM_OPENRISC 92 -+#define EM_ARC_A5 93 -+#define EM_XTENSA 94 -+#define EM_AARCH64 183 -+#define EM_TILEPRO 188 -+#define EM_MICROBLAZE 189 -+#define EM_TILEGX 191 -+#define EM_NUM 192 -+#define EM_ALPHA 0x9026 -+ -+#define EV_NONE 0 -+#define EV_CURRENT 1 -+#define EV_NUM 2 -+ -+typedef struct { -+ Elf32_Word sh_name; -+ Elf32_Word sh_type; -+ Elf32_Word sh_flags; -+ Elf32_Addr sh_addr; -+ Elf32_Off sh_offset; -+ Elf32_Word sh_size; -+ Elf32_Word sh_link; -+ Elf32_Word sh_info; -+ Elf32_Word sh_addralign; -+ Elf32_Word sh_entsize; -+} Elf32_Shdr; -+ -+typedef struct { -+ Elf64_Word sh_name; -+ Elf64_Word sh_type; -+ Elf64_Xword sh_flags; -+ Elf64_Addr sh_addr; -+ Elf64_Off sh_offset; -+ Elf64_Xword sh_size; -+ Elf64_Word sh_link; -+ Elf64_Word sh_info; -+ Elf64_Xword sh_addralign; -+ Elf64_Xword sh_entsize; -+} Elf64_Shdr; -+ -+ -+ -+#define SHN_UNDEF 0 -+#define SHN_LORESERVE 0xff00 -+#define SHN_LOPROC 0xff00 -+#define SHN_BEFORE 0xff00 -+ -+#define SHN_AFTER 0xff01 -+ -+#define SHN_HIPROC 0xff1f -+#define SHN_LOOS 0xff20 -+#define SHN_HIOS 0xff3f -+#define SHN_ABS 0xfff1 -+#define SHN_COMMON 0xfff2 -+#define SHN_XINDEX 0xffff -+#define SHN_HIRESERVE 0xffff -+ -+ -+ -+#define SHT_NULL 0 -+#define SHT_PROGBITS 1 -+#define SHT_SYMTAB 2 -+#define SHT_STRTAB 3 -+#define SHT_RELA 4 -+#define SHT_HASH 5 -+#define SHT_DYNAMIC 6 -+#define SHT_NOTE 7 -+#define SHT_NOBITS 8 -+#define SHT_REL 9 -+#define SHT_SHLIB 10 -+#define SHT_DYNSYM 11 -+#define SHT_INIT_ARRAY 14 -+#define SHT_FINI_ARRAY 15 -+#define SHT_PREINIT_ARRAY 16 -+#define SHT_GROUP 17 -+#define SHT_SYMTAB_SHNDX 18 -+#define SHT_NUM 19 -+#define SHT_LOOS 0x60000000 -+#define SHT_GNU_ATTRIBUTES 0x6ffffff5 -+#define SHT_GNU_HASH 0x6ffffff6 -+#define SHT_GNU_LIBLIST 0x6ffffff7 -+#define SHT_CHECKSUM 0x6ffffff8 -+#define SHT_LOSUNW 0x6ffffffa -+#define SHT_SUNW_move 0x6ffffffa -+#define SHT_SUNW_COMDAT 0x6ffffffb -+#define SHT_SUNW_syminfo 0x6ffffffc -+#define SHT_GNU_verdef 0x6ffffffd -+#define SHT_GNU_verneed 0x6ffffffe -+#define SHT_GNU_versym 0x6fffffff -+#define SHT_HISUNW 0x6fffffff -+#define SHT_HIOS 0x6fffffff -+#define SHT_LOPROC 0x70000000 -+#define SHT_HIPROC 0x7fffffff -+#define SHT_LOUSER 0x80000000 -+#define SHT_HIUSER 0x8fffffff -+ -+#define SHF_WRITE (1 << 0) -+#define SHF_ALLOC (1 << 1) -+#define SHF_EXECINSTR (1 << 2) -+#define SHF_MERGE (1 << 4) -+#define SHF_STRINGS (1 << 5) -+#define SHF_INFO_LINK (1 << 6) -+#define SHF_LINK_ORDER (1 << 7) -+#define SHF_OS_NONCONFORMING (1 << 8) -+ -+#define SHF_GROUP (1 << 9) -+#define SHF_TLS (1 << 10) -+#define SHF_MASKOS 0x0ff00000 -+#define SHF_MASKPROC 0xf0000000 -+#define SHF_ORDERED (1 << 30) -+#define SHF_EXCLUDE (1 << 31) -+ -+#define GRP_COMDAT 0x1 -+ -+typedef struct { -+ Elf32_Word st_name; -+ Elf32_Addr st_value; -+ Elf32_Word st_size; -+ unsigned char st_info; -+ unsigned char st_other; -+ Elf32_Section st_shndx; -+} Elf32_Sym; -+ -+typedef struct { -+ Elf64_Word st_name; -+ unsigned char st_info; -+ unsigned char st_other; -+ Elf64_Section st_shndx; -+ Elf64_Addr st_value; -+ Elf64_Xword st_size; -+} Elf64_Sym; -+ -+typedef struct { -+ Elf32_Half si_boundto; -+ Elf32_Half si_flags; -+} Elf32_Syminfo; -+ -+typedef struct { -+ Elf64_Half si_boundto; -+ Elf64_Half si_flags; -+} Elf64_Syminfo; -+ -+#define SYMINFO_BT_SELF 0xffff -+#define SYMINFO_BT_PARENT 0xfffe -+#define SYMINFO_BT_LOWRESERVE 0xff00 -+ -+#define SYMINFO_FLG_DIRECT 0x0001 -+#define SYMINFO_FLG_PASSTHRU 0x0002 -+#define SYMINFO_FLG_COPY 0x0004 -+#define SYMINFO_FLG_LAZYLOAD 0x0008 -+ -+#define SYMINFO_NONE 0 -+#define SYMINFO_CURRENT 1 -+#define SYMINFO_NUM 2 -+ -+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -+#define ELF32_ST_TYPE(val) ((val) & 0xf) -+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -+ -+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) -+ -+#define STB_LOCAL 0 -+#define STB_GLOBAL 1 -+#define STB_WEAK 2 -+#define STB_NUM 3 -+#define STB_LOOS 10 -+#define STB_GNU_UNIQUE 10 -+#define STB_HIOS 12 -+#define STB_LOPROC 13 -+#define STB_HIPROC 15 -+ -+#define STT_NOTYPE 0 -+#define STT_OBJECT 1 -+#define STT_FUNC 2 -+#define STT_SECTION 3 -+#define STT_FILE 4 -+#define STT_COMMON 5 -+#define STT_TLS 6 -+#define STT_NUM 7 -+#define STT_LOOS 10 -+#define STT_GNU_IFUNC 10 -+#define STT_HIOS 12 -+#define STT_LOPROC 13 -+#define STT_HIPROC 15 -+ -+#define STN_UNDEF 0 -+ -+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) -+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) -+ -+#define STV_DEFAULT 0 -+#define STV_INTERNAL 1 -+#define STV_HIDDEN 2 -+#define STV_PROTECTED 3 -+ -+ -+ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; -+ Elf32_Word r_info; -+} Elf32_Rel; -+ -+typedef struct { -+ Elf64_Addr r_offset; -+ Elf64_Xword r_info; -+} Elf64_Rel; -+ -+ -+ -+typedef struct { -+ Elf32_Addr r_offset; -+ Elf32_Word r_info; -+ Elf32_Sword r_addend; -+} Elf32_Rela; -+ -+typedef struct { -+ Elf64_Addr r_offset; -+ Elf64_Xword r_info; -+ Elf64_Sxword r_addend; -+} Elf64_Rela; -+ -+ -+ -+#define ELF32_R_SYM(val) ((val) >> 8) -+#define ELF32_R_TYPE(val) ((val) & 0xff) -+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) -+ -+#define ELF64_R_SYM(i) ((i) >> 32) -+#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) -+ -+ -+ -+typedef struct { -+ Elf32_Word p_type; -+ Elf32_Off p_offset; -+ Elf32_Addr p_vaddr; -+ Elf32_Addr p_paddr; -+ Elf32_Word p_filesz; -+ Elf32_Word p_memsz; -+ Elf32_Word p_flags; -+ Elf32_Word p_align; -+} Elf32_Phdr; -+ -+typedef struct { -+ Elf64_Word p_type; -+ Elf64_Word p_flags; -+ Elf64_Off p_offset; -+ Elf64_Addr p_vaddr; -+ Elf64_Addr p_paddr; -+ Elf64_Xword p_filesz; -+ Elf64_Xword p_memsz; -+ Elf64_Xword p_align; -+} Elf64_Phdr; -+ -+ -+ -+#define PT_NULL 0 -+#define PT_LOAD 1 -+#define PT_DYNAMIC 2 -+#define PT_INTERP 3 -+#define PT_NOTE 4 -+#define PT_SHLIB 5 -+#define PT_PHDR 6 -+#define PT_TLS 7 -+#define PT_NUM 8 -+#define PT_LOOS 0x60000000 -+#define PT_GNU_EH_FRAME 0x6474e550 -+#define PT_GNU_STACK 0x6474e551 -+#define PT_GNU_RELRO 0x6474e552 -+#define PT_LOSUNW 0x6ffffffa -+#define PT_SUNWBSS 0x6ffffffa -+#define PT_SUNWSTACK 0x6ffffffb -+#define PT_HISUNW 0x6fffffff -+#define PT_HIOS 0x6fffffff -+#define PT_LOPROC 0x70000000 -+#define PT_HIPROC 0x7fffffff -+ -+ -+#define PN_XNUM 0xffff -+ -+ -+#define PF_X (1 << 0) -+#define PF_W (1 << 1) -+#define PF_R (1 << 2) -+#define PF_MASKOS 0x0ff00000 -+#define PF_MASKPROC 0xf0000000 -+ -+ -+ -+#define NT_PRSTATUS 1 -+#define NT_FPREGSET 2 -+#define NT_PRPSINFO 3 -+#define NT_PRXREG 4 -+#define NT_TASKSTRUCT 4 -+#define NT_PLATFORM 5 -+#define NT_AUXV 6 -+#define NT_GWINDOWS 7 -+#define NT_ASRS 8 -+#define NT_PSTATUS 10 -+#define NT_PSINFO 13 -+#define NT_PRCRED 14 -+#define NT_UTSNAME 15 -+#define NT_LWPSTATUS 16 -+#define NT_LWPSINFO 17 -+#define NT_PRFPXREG 20 -+#define NT_SIGINFO 0x53494749 -+#define NT_FILE 0x46494c45 -+#define NT_PRXFPREG 0x46e62b7f -+#define NT_PPC_VMX 0x100 -+#define NT_PPC_SPE 0x101 -+#define NT_PPC_VSX 0x102 -+#define NT_386_TLS 0x200 -+#define NT_386_IOPERM 0x201 -+#define NT_X86_XSTATE 0x202 -+#define NT_S390_HIGH_GPRS 0x300 -+#define NT_S390_TIMER 0x301 -+#define NT_S390_TODCMP 0x302 -+#define NT_S390_TODPREG 0x303 -+#define NT_S390_CTRS 0x304 -+#define NT_S390_PREFIX 0x305 -+#define NT_S390_LAST_BREAK 0x306 -+#define NT_S390_SYSTEM_CALL 0x307 -+#define NT_S390_TDB 0x308 -+#define NT_ARM_VFP 0x400 -+#define NT_ARM_TLS 0x401 -+#define NT_ARM_HW_BREAK 0x402 -+#define NT_ARM_HW_WATCH 0x403 -+#define NT_METAG_CBUF 0x500 -+#define NT_METAG_RPIPE 0x501 -+#define NT_METAG_TLS 0x502 -+#define NT_VERSION 1 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Sword d_tag; -+ union { -+ Elf32_Word d_val; -+ Elf32_Addr d_ptr; -+ } d_un; -+} Elf32_Dyn; -+ -+typedef struct { -+ Elf64_Sxword d_tag; -+ union { -+ Elf64_Xword d_val; -+ Elf64_Addr d_ptr; -+ } d_un; -+} Elf64_Dyn; -+ -+ -+ -+#define DT_NULL 0 -+#define DT_NEEDED 1 -+#define DT_PLTRELSZ 2 -+#define DT_PLTGOT 3 -+#define DT_HASH 4 -+#define DT_STRTAB 5 -+#define DT_SYMTAB 6 -+#define DT_RELA 7 -+#define DT_RELASZ 8 -+#define DT_RELAENT 9 -+#define DT_STRSZ 10 -+#define DT_SYMENT 11 -+#define DT_INIT 12 -+#define DT_FINI 13 -+#define DT_SONAME 14 -+#define DT_RPATH 15 -+#define DT_SYMBOLIC 16 -+#define DT_REL 17 -+#define DT_RELSZ 18 -+#define DT_RELENT 19 -+#define DT_PLTREL 20 -+#define DT_DEBUG 21 -+#define DT_TEXTREL 22 -+#define DT_JMPREL 23 -+#define DT_BIND_NOW 24 -+#define DT_INIT_ARRAY 25 -+#define DT_FINI_ARRAY 26 -+#define DT_INIT_ARRAYSZ 27 -+#define DT_FINI_ARRAYSZ 28 -+#define DT_RUNPATH 29 -+#define DT_FLAGS 30 -+#define DT_ENCODING 32 -+#define DT_PREINIT_ARRAY 32 -+#define DT_PREINIT_ARRAYSZ 33 -+#define DT_NUM 34 -+#define DT_LOOS 0x6000000d -+#define DT_HIOS 0x6ffff000 -+#define DT_LOPROC 0x70000000 -+#define DT_HIPROC 0x7fffffff -+#define DT_PROCNUM DT_MIPS_NUM -+ -+#define DT_VALRNGLO 0x6ffffd00 -+#define DT_GNU_PRELINKED 0x6ffffdf5 -+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 -+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 -+#define DT_CHECKSUM 0x6ffffdf8 -+#define DT_PLTPADSZ 0x6ffffdf9 -+#define DT_MOVEENT 0x6ffffdfa -+#define DT_MOVESZ 0x6ffffdfb -+#define DT_FEATURE_1 0x6ffffdfc -+#define DT_POSFLAG_1 0x6ffffdfd -+ -+#define DT_SYMINSZ 0x6ffffdfe -+#define DT_SYMINENT 0x6ffffdff -+#define DT_VALRNGHI 0x6ffffdff -+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) -+#define DT_VALNUM 12 -+ -+#define DT_ADDRRNGLO 0x6ffffe00 -+#define DT_GNU_HASH 0x6ffffef5 -+#define DT_TLSDESC_PLT 0x6ffffef6 -+#define DT_TLSDESC_GOT 0x6ffffef7 -+#define DT_GNU_CONFLICT 0x6ffffef8 -+#define DT_GNU_LIBLIST 0x6ffffef9 -+#define DT_CONFIG 0x6ffffefa -+#define DT_DEPAUDIT 0x6ffffefb -+#define DT_AUDIT 0x6ffffefc -+#define DT_PLTPAD 0x6ffffefd -+#define DT_MOVETAB 0x6ffffefe -+#define DT_SYMINFO 0x6ffffeff -+#define DT_ADDRRNGHI 0x6ffffeff -+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) -+#define DT_ADDRNUM 11 -+ -+ -+ -+#define DT_VERSYM 0x6ffffff0 -+ -+#define DT_RELACOUNT 0x6ffffff9 -+#define DT_RELCOUNT 0x6ffffffa -+ -+ -+#define DT_FLAGS_1 0x6ffffffb -+#define DT_VERDEF 0x6ffffffc -+ -+#define DT_VERDEFNUM 0x6ffffffd -+#define DT_VERNEED 0x6ffffffe -+ -+#define DT_VERNEEDNUM 0x6fffffff -+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) -+#define DT_VERSIONTAGNUM 16 -+ -+ -+ -+#define DT_AUXILIARY 0x7ffffffd -+#define DT_FILTER 0x7fffffff -+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -+#define DT_EXTRANUM 3 -+ -+ -+#define DF_ORIGIN 0x00000001 -+#define DF_SYMBOLIC 0x00000002 -+#define DF_TEXTREL 0x00000004 -+#define DF_BIND_NOW 0x00000008 -+#define DF_STATIC_TLS 0x00000010 -+ -+ -+ -+#define DF_1_NOW 0x00000001 -+#define DF_1_GLOBAL 0x00000002 -+#define DF_1_GROUP 0x00000004 -+#define DF_1_NODELETE 0x00000008 -+#define DF_1_LOADFLTR 0x00000010 -+#define DF_1_INITFIRST 0x00000020 -+#define DF_1_NOOPEN 0x00000040 -+#define DF_1_ORIGIN 0x00000080 -+#define DF_1_DIRECT 0x00000100 -+#define DF_1_TRANS 0x00000200 -+#define DF_1_INTERPOSE 0x00000400 -+#define DF_1_NODEFLIB 0x00000800 -+#define DF_1_NODUMP 0x00001000 -+#define DF_1_CONFALT 0x00002000 -+#define DF_1_ENDFILTEE 0x00004000 -+#define DF_1_DISPRELDNE 0x00008000 -+#define DF_1_DISPRELPND 0x00010000 -+#define DF_1_NODIRECT 0x00020000 -+#define DF_1_IGNMULDEF 0x00040000 -+#define DF_1_NOKSYMS 0x00080000 -+#define DF_1_NOHDR 0x00100000 -+#define DF_1_EDITED 0x00200000 -+#define DF_1_NORELOC 0x00400000 -+#define DF_1_SYMINTPOSE 0x00800000 -+#define DF_1_GLOBAUDIT 0x01000000 -+#define DF_1_SINGLETON 0x02000000 -+ -+#define DTF_1_PARINIT 0x00000001 -+#define DTF_1_CONFEXP 0x00000002 -+ -+ -+#define DF_P1_LAZYLOAD 0x00000001 -+#define DF_P1_GROUPPERM 0x00000002 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Half vd_version; -+ Elf32_Half vd_flags; -+ Elf32_Half vd_ndx; -+ Elf32_Half vd_cnt; -+ Elf32_Word vd_hash; -+ Elf32_Word vd_aux; -+ Elf32_Word vd_next; -+} Elf32_Verdef; -+ -+typedef struct { -+ Elf64_Half vd_version; -+ Elf64_Half vd_flags; -+ Elf64_Half vd_ndx; -+ Elf64_Half vd_cnt; -+ Elf64_Word vd_hash; -+ Elf64_Word vd_aux; -+ Elf64_Word vd_next; -+} Elf64_Verdef; -+ -+ -+ -+#define VER_DEF_NONE 0 -+#define VER_DEF_CURRENT 1 -+#define VER_DEF_NUM 2 -+ -+ -+#define VER_FLG_BASE 0x1 -+#define VER_FLG_WEAK 0x2 -+ -+ -+#define VER_NDX_LOCAL 0 -+#define VER_NDX_GLOBAL 1 -+#define VER_NDX_LORESERVE 0xff00 -+#define VER_NDX_ELIMINATE 0xff01 -+ -+ -+ -+typedef struct { -+ Elf32_Word vda_name; -+ Elf32_Word vda_next; -+} Elf32_Verdaux; -+ -+typedef struct { -+ Elf64_Word vda_name; -+ Elf64_Word vda_next; -+} Elf64_Verdaux; -+ -+ -+ -+ -+typedef struct { -+ Elf32_Half vn_version; -+ Elf32_Half vn_cnt; -+ Elf32_Word vn_file; -+ Elf32_Word vn_aux; -+ Elf32_Word vn_next; -+} Elf32_Verneed; -+ -+typedef struct { -+ Elf64_Half vn_version; -+ Elf64_Half vn_cnt; -+ Elf64_Word vn_file; -+ Elf64_Word vn_aux; -+ Elf64_Word vn_next; -+} Elf64_Verneed; -+ -+ -+ -+#define VER_NEED_NONE 0 -+#define VER_NEED_CURRENT 1 -+#define VER_NEED_NUM 2 -+ -+ -+ -+typedef struct { -+ Elf32_Word vna_hash; -+ Elf32_Half vna_flags; -+ Elf32_Half vna_other; -+ Elf32_Word vna_name; -+ Elf32_Word vna_next; -+} Elf32_Vernaux; -+ -+typedef struct { -+ Elf64_Word vna_hash; -+ Elf64_Half vna_flags; -+ Elf64_Half vna_other; -+ Elf64_Word vna_name; -+ Elf64_Word vna_next; -+} Elf64_Vernaux; -+ -+ -+ -+#define VER_FLG_WEAK 0x2 -+ -+ -+ -+typedef struct { -+ uint32_t a_type; -+ union { -+ uint32_t a_val; -+ } a_un; -+} Elf32_auxv_t; -+ -+typedef struct { -+ uint64_t a_type; -+ union { -+ uint64_t a_val; -+ } a_un; -+} Elf64_auxv_t; -+ -+ -+ -+#define AT_NULL 0 -+#define AT_IGNORE 1 -+#define AT_EXECFD 2 -+#define AT_PHDR 3 -+#define AT_PHENT 4 -+#define AT_PHNUM 5 -+#define AT_PAGESZ 6 -+#define AT_BASE 7 -+#define AT_FLAGS 8 -+#define AT_ENTRY 9 -+#define AT_NOTELF 10 -+#define AT_UID 11 -+#define AT_EUID 12 -+#define AT_GID 13 -+#define AT_EGID 14 -+#define AT_CLKTCK 17 -+ -+ -+#define AT_PLATFORM 15 -+#define AT_HWCAP 16 -+ -+ -+ -+ -+#define AT_FPUCW 18 -+ -+ -+#define AT_DCACHEBSIZE 19 -+#define AT_ICACHEBSIZE 20 -+#define AT_UCACHEBSIZE 21 -+ -+ -+ -+#define AT_IGNOREPPC 22 -+ -+#define AT_SECURE 23 -+ -+#define AT_BASE_PLATFORM 24 -+ -+#define AT_RANDOM 25 -+ -+#define AT_HWCAP2 26 -+ -+#define AT_EXECFN 31 -+ -+ -+ -+#define AT_SYSINFO 32 -+#define AT_SYSINFO_EHDR 33 -+ -+ -+ -+#define AT_L1I_CACHESHAPE 34 -+#define AT_L1D_CACHESHAPE 35 -+#define AT_L2_CACHESHAPE 36 -+#define AT_L3_CACHESHAPE 37 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Word n_namesz; -+ Elf32_Word n_descsz; -+ Elf32_Word n_type; -+} Elf32_Nhdr; -+ -+typedef struct { -+ Elf64_Word n_namesz; -+ Elf64_Word n_descsz; -+ Elf64_Word n_type; -+} Elf64_Nhdr; -+ -+ -+ -+ -+#define ELF_NOTE_SOLARIS "SUNW Solaris" -+ -+ -+#define ELF_NOTE_GNU "GNU" -+ -+ -+ -+ -+ -+#define ELF_NOTE_PAGESIZE_HINT 1 -+ -+ -+#define NT_GNU_ABI_TAG 1 -+#define ELF_NOTE_ABI NT_GNU_ABI_TAG -+ -+ -+ -+#define ELF_NOTE_OS_LINUX 0 -+#define ELF_NOTE_OS_GNU 1 -+#define ELF_NOTE_OS_SOLARIS2 2 -+#define ELF_NOTE_OS_FREEBSD 3 -+ -+#define NT_GNU_BUILD_ID 3 -+#define NT_GNU_GOLD_VERSION 4 -+ -+ -+ -+typedef struct { -+ Elf32_Xword m_value; -+ Elf32_Word m_info; -+ Elf32_Word m_poffset; -+ Elf32_Half m_repeat; -+ Elf32_Half m_stride; -+} Elf32_Move; -+ -+typedef struct { -+ Elf64_Xword m_value; -+ Elf64_Xword m_info; -+ Elf64_Xword m_poffset; -+ Elf64_Half m_repeat; -+ Elf64_Half m_stride; -+} Elf64_Move; -+ -+ -+#define ELF32_M_SYM(info) ((info) >> 8) -+#define ELF32_M_SIZE(info) ((unsigned char) (info)) -+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) -+ -+#define ELF64_M_SYM(info) ELF32_M_SYM (info) -+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) -+ -+#define EF_CPU32 0x00810000 -+ -+#define R_68K_NONE 0 -+#define R_68K_32 1 -+#define R_68K_16 2 -+#define R_68K_8 3 -+#define R_68K_PC32 4 -+#define R_68K_PC16 5 -+#define R_68K_PC8 6 -+#define R_68K_GOT32 7 -+#define R_68K_GOT16 8 -+#define R_68K_GOT8 9 -+#define R_68K_GOT32O 10 -+#define R_68K_GOT16O 11 -+#define R_68K_GOT8O 12 -+#define R_68K_PLT32 13 -+#define R_68K_PLT16 14 -+#define R_68K_PLT8 15 -+#define R_68K_PLT32O 16 -+#define R_68K_PLT16O 17 -+#define R_68K_PLT8O 18 -+#define R_68K_COPY 19 -+#define R_68K_GLOB_DAT 20 -+#define R_68K_JMP_SLOT 21 -+#define R_68K_RELATIVE 22 -+#define R_68K_NUM 23 -+ -+#define R_386_NONE 0 -+#define R_386_32 1 -+#define R_386_PC32 2 -+#define R_386_GOT32 3 -+#define R_386_PLT32 4 -+#define R_386_COPY 5 -+#define R_386_GLOB_DAT 6 -+#define R_386_JMP_SLOT 7 -+#define R_386_RELATIVE 8 -+#define R_386_GOTOFF 9 -+#define R_386_GOTPC 10 -+#define R_386_32PLT 11 -+#define R_386_TLS_TPOFF 14 -+#define R_386_TLS_IE 15 -+#define R_386_TLS_GOTIE 16 -+#define R_386_TLS_LE 17 -+#define R_386_TLS_GD 18 -+#define R_386_TLS_LDM 19 -+#define R_386_16 20 -+#define R_386_PC16 21 -+#define R_386_8 22 -+#define R_386_PC8 23 -+#define R_386_TLS_GD_32 24 -+#define R_386_TLS_GD_PUSH 25 -+#define R_386_TLS_GD_CALL 26 -+#define R_386_TLS_GD_POP 27 -+#define R_386_TLS_LDM_32 28 -+#define R_386_TLS_LDM_PUSH 29 -+#define R_386_TLS_LDM_CALL 30 -+#define R_386_TLS_LDM_POP 31 -+#define R_386_TLS_LDO_32 32 -+#define R_386_TLS_IE_32 33 -+#define R_386_TLS_LE_32 34 -+#define R_386_TLS_DTPMOD32 35 -+#define R_386_TLS_DTPOFF32 36 -+#define R_386_TLS_TPOFF32 37 -+#define R_386_SIZE32 38 -+#define R_386_TLS_GOTDESC 39 -+#define R_386_TLS_DESC_CALL 40 -+#define R_386_TLS_DESC 41 -+#define R_386_IRELATIVE 42 -+#define R_386_NUM 43 -+ -+ -+ -+ -+ -+#define STT_SPARC_REGISTER 13 -+ -+ -+ -+#define EF_SPARCV9_MM 3 -+#define EF_SPARCV9_TSO 0 -+#define EF_SPARCV9_PSO 1 -+#define EF_SPARCV9_RMO 2 -+#define EF_SPARC_LEDATA 0x800000 -+#define EF_SPARC_EXT_MASK 0xFFFF00 -+#define EF_SPARC_32PLUS 0x000100 -+#define EF_SPARC_SUN_US1 0x000200 -+#define EF_SPARC_HAL_R1 0x000400 -+#define EF_SPARC_SUN_US3 0x000800 -+ -+ -+ -+#define R_SPARC_NONE 0 -+#define R_SPARC_8 1 -+#define R_SPARC_16 2 -+#define R_SPARC_32 3 -+#define R_SPARC_DISP8 4 -+#define R_SPARC_DISP16 5 -+#define R_SPARC_DISP32 6 -+#define R_SPARC_WDISP30 7 -+#define R_SPARC_WDISP22 8 -+#define R_SPARC_HI22 9 -+#define R_SPARC_22 10 -+#define R_SPARC_13 11 -+#define R_SPARC_LO10 12 -+#define R_SPARC_GOT10 13 -+#define R_SPARC_GOT13 14 -+#define R_SPARC_GOT22 15 -+#define R_SPARC_PC10 16 -+#define R_SPARC_PC22 17 -+#define R_SPARC_WPLT30 18 -+#define R_SPARC_COPY 19 -+#define R_SPARC_GLOB_DAT 20 -+#define R_SPARC_JMP_SLOT 21 -+#define R_SPARC_RELATIVE 22 -+#define R_SPARC_UA32 23 -+ -+ -+ -+#define R_SPARC_PLT32 24 -+#define R_SPARC_HIPLT22 25 -+#define R_SPARC_LOPLT10 26 -+#define R_SPARC_PCPLT32 27 -+#define R_SPARC_PCPLT22 28 -+#define R_SPARC_PCPLT10 29 -+#define R_SPARC_10 30 -+#define R_SPARC_11 31 -+#define R_SPARC_64 32 -+#define R_SPARC_OLO10 33 -+#define R_SPARC_HH22 34 -+#define R_SPARC_HM10 35 -+#define R_SPARC_LM22 36 -+#define R_SPARC_PC_HH22 37 -+#define R_SPARC_PC_HM10 38 -+#define R_SPARC_PC_LM22 39 -+#define R_SPARC_WDISP16 40 -+#define R_SPARC_WDISP19 41 -+#define R_SPARC_GLOB_JMP 42 -+#define R_SPARC_7 43 -+#define R_SPARC_5 44 -+#define R_SPARC_6 45 -+#define R_SPARC_DISP64 46 -+#define R_SPARC_PLT64 47 -+#define R_SPARC_HIX22 48 -+#define R_SPARC_LOX10 49 -+#define R_SPARC_H44 50 -+#define R_SPARC_M44 51 -+#define R_SPARC_L44 52 -+#define R_SPARC_REGISTER 53 -+#define R_SPARC_UA64 54 -+#define R_SPARC_UA16 55 -+#define R_SPARC_TLS_GD_HI22 56 -+#define R_SPARC_TLS_GD_LO10 57 -+#define R_SPARC_TLS_GD_ADD 58 -+#define R_SPARC_TLS_GD_CALL 59 -+#define R_SPARC_TLS_LDM_HI22 60 -+#define R_SPARC_TLS_LDM_LO10 61 -+#define R_SPARC_TLS_LDM_ADD 62 -+#define R_SPARC_TLS_LDM_CALL 63 -+#define R_SPARC_TLS_LDO_HIX22 64 -+#define R_SPARC_TLS_LDO_LOX10 65 -+#define R_SPARC_TLS_LDO_ADD 66 -+#define R_SPARC_TLS_IE_HI22 67 -+#define R_SPARC_TLS_IE_LO10 68 -+#define R_SPARC_TLS_IE_LD 69 -+#define R_SPARC_TLS_IE_LDX 70 -+#define R_SPARC_TLS_IE_ADD 71 -+#define R_SPARC_TLS_LE_HIX22 72 -+#define R_SPARC_TLS_LE_LOX10 73 -+#define R_SPARC_TLS_DTPMOD32 74 -+#define R_SPARC_TLS_DTPMOD64 75 -+#define R_SPARC_TLS_DTPOFF32 76 -+#define R_SPARC_TLS_DTPOFF64 77 -+#define R_SPARC_TLS_TPOFF32 78 -+#define R_SPARC_TLS_TPOFF64 79 -+#define R_SPARC_GOTDATA_HIX22 80 -+#define R_SPARC_GOTDATA_LOX10 81 -+#define R_SPARC_GOTDATA_OP_HIX22 82 -+#define R_SPARC_GOTDATA_OP_LOX10 83 -+#define R_SPARC_GOTDATA_OP 84 -+#define R_SPARC_H34 85 -+#define R_SPARC_SIZE32 86 -+#define R_SPARC_SIZE64 87 -+#define R_SPARC_GNU_VTINHERIT 250 -+#define R_SPARC_GNU_VTENTRY 251 -+#define R_SPARC_REV32 252 -+ -+#define R_SPARC_NUM 253 -+ -+ -+ -+#define DT_SPARC_REGISTER 0x70000001 -+#define DT_SPARC_NUM 2 -+ -+ -+#define EF_MIPS_NOREORDER 1 -+#define EF_MIPS_PIC 2 -+#define EF_MIPS_CPIC 4 -+#define EF_MIPS_XGOT 8 -+#define EF_MIPS_64BIT_WHIRL 16 -+#define EF_MIPS_ABI2 32 -+#define EF_MIPS_ABI_ON32 64 -+#define EF_MIPS_ARCH 0xf0000000 -+ -+ -+ -+#define EF_MIPS_ARCH_1 0x00000000 -+#define EF_MIPS_ARCH_2 0x10000000 -+#define EF_MIPS_ARCH_3 0x20000000 -+#define EF_MIPS_ARCH_4 0x30000000 -+#define EF_MIPS_ARCH_5 0x40000000 -+#define EF_MIPS_ARCH_32 0x50000000 -+#define EF_MIPS_ARCH_64 0x60000000 -+#define EF_MIPS_ARCH_32R2 0x70000000 -+#define EF_MIPS_ARCH_64R2 0x80000000 -+ -+ -+#define E_MIPS_ARCH_1 0x00000000 -+#define E_MIPS_ARCH_2 0x10000000 -+#define E_MIPS_ARCH_3 0x20000000 -+#define E_MIPS_ARCH_4 0x30000000 -+#define E_MIPS_ARCH_5 0x40000000 -+#define E_MIPS_ARCH_32 0x50000000 -+#define E_MIPS_ARCH_64 0x60000000 -+ -+ -+ -+#define SHN_MIPS_ACOMMON 0xff00 -+#define SHN_MIPS_TEXT 0xff01 -+#define SHN_MIPS_DATA 0xff02 -+#define SHN_MIPS_SCOMMON 0xff03 -+#define SHN_MIPS_SUNDEFINED 0xff04 -+ -+ -+ -+#define SHT_MIPS_LIBLIST 0x70000000 -+#define SHT_MIPS_MSYM 0x70000001 -+#define SHT_MIPS_CONFLICT 0x70000002 -+#define SHT_MIPS_GPTAB 0x70000003 -+#define SHT_MIPS_UCODE 0x70000004 -+#define SHT_MIPS_DEBUG 0x70000005 -+#define SHT_MIPS_REGINFO 0x70000006 -+#define SHT_MIPS_PACKAGE 0x70000007 -+#define SHT_MIPS_PACKSYM 0x70000008 -+#define SHT_MIPS_RELD 0x70000009 -+#define SHT_MIPS_IFACE 0x7000000b -+#define SHT_MIPS_CONTENT 0x7000000c -+#define SHT_MIPS_OPTIONS 0x7000000d -+#define SHT_MIPS_SHDR 0x70000010 -+#define SHT_MIPS_FDESC 0x70000011 -+#define SHT_MIPS_EXTSYM 0x70000012 -+#define SHT_MIPS_DENSE 0x70000013 -+#define SHT_MIPS_PDESC 0x70000014 -+#define SHT_MIPS_LOCSYM 0x70000015 -+#define SHT_MIPS_AUXSYM 0x70000016 -+#define SHT_MIPS_OPTSYM 0x70000017 -+#define SHT_MIPS_LOCSTR 0x70000018 -+#define SHT_MIPS_LINE 0x70000019 -+#define SHT_MIPS_RFDESC 0x7000001a -+#define SHT_MIPS_DELTASYM 0x7000001b -+#define SHT_MIPS_DELTAINST 0x7000001c -+#define SHT_MIPS_DELTACLASS 0x7000001d -+#define SHT_MIPS_DWARF 0x7000001e -+#define SHT_MIPS_DELTADECL 0x7000001f -+#define SHT_MIPS_SYMBOL_LIB 0x70000020 -+#define SHT_MIPS_EVENTS 0x70000021 -+#define SHT_MIPS_TRANSLATE 0x70000022 -+#define SHT_MIPS_PIXIE 0x70000023 -+#define SHT_MIPS_XLATE 0x70000024 -+#define SHT_MIPS_XLATE_DEBUG 0x70000025 -+#define SHT_MIPS_WHIRL 0x70000026 -+#define SHT_MIPS_EH_REGION 0x70000027 -+#define SHT_MIPS_XLATE_OLD 0x70000028 -+#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -+ -+ -+ -+#define SHF_MIPS_GPREL 0x10000000 -+#define SHF_MIPS_MERGE 0x20000000 -+#define SHF_MIPS_ADDR 0x40000000 -+#define SHF_MIPS_STRINGS 0x80000000 -+#define SHF_MIPS_NOSTRIP 0x08000000 -+#define SHF_MIPS_LOCAL 0x04000000 -+#define SHF_MIPS_NAMES 0x02000000 -+#define SHF_MIPS_NODUPE 0x01000000 -+ -+ -+ -+ -+ -+#define STO_MIPS_DEFAULT 0x0 -+#define STO_MIPS_INTERNAL 0x1 -+#define STO_MIPS_HIDDEN 0x2 -+#define STO_MIPS_PROTECTED 0x3 -+#define STO_MIPS_PLT 0x8 -+#define STO_MIPS_SC_ALIGN_UNUSED 0xff -+ -+ -+#define STB_MIPS_SPLIT_COMMON 13 -+ -+ -+ -+typedef union { -+ struct { -+ Elf32_Word gt_current_g_value; -+ Elf32_Word gt_unused; -+ } gt_header; -+ struct { -+ Elf32_Word gt_g_value; -+ Elf32_Word gt_bytes; -+ } gt_entry; -+} Elf32_gptab; -+ -+ -+ -+typedef struct { -+ Elf32_Word ri_gprmask; -+ Elf32_Word ri_cprmask[4]; -+ Elf32_Sword ri_gp_value; -+} Elf32_RegInfo; -+ -+ -+ -+typedef struct { -+ unsigned char kind; -+ -+ unsigned char size; -+ Elf32_Section section; -+ -+ Elf32_Word info; -+} Elf_Options; -+ -+ -+ -+#define ODK_NULL 0 -+#define ODK_REGINFO 1 -+#define ODK_EXCEPTIONS 2 -+#define ODK_PAD 3 -+#define ODK_HWPATCH 4 -+#define ODK_FILL 5 -+#define ODK_TAGS 6 -+#define ODK_HWAND 7 -+#define ODK_HWOR 8 -+ -+ -+ -+#define OEX_FPU_MIN 0x1f -+#define OEX_FPU_MAX 0x1f00 -+#define OEX_PAGE0 0x10000 -+#define OEX_SMM 0x20000 -+#define OEX_FPDBUG 0x40000 -+#define OEX_PRECISEFP OEX_FPDBUG -+#define OEX_DISMISS 0x80000 -+ -+#define OEX_FPU_INVAL 0x10 -+#define OEX_FPU_DIV0 0x08 -+#define OEX_FPU_OFLO 0x04 -+#define OEX_FPU_UFLO 0x02 -+#define OEX_FPU_INEX 0x01 -+ -+ -+ -+#define OHW_R4KEOP 0x1 -+#define OHW_R8KPFETCH 0x2 -+#define OHW_R5KEOP 0x4 -+#define OHW_R5KCVTL 0x8 -+ -+#define OPAD_PREFIX 0x1 -+#define OPAD_POSTFIX 0x2 -+#define OPAD_SYMBOL 0x4 -+ -+ -+ -+typedef struct { -+ Elf32_Word hwp_flags1; -+ Elf32_Word hwp_flags2; -+} Elf_Options_Hw; -+ -+ -+ -+#define OHWA0_R4KEOP_CHECKED 0x00000001 -+#define OHWA1_R4KEOP_CLEAN 0x00000002 -+ -+ -+ -+#define R_MIPS_NONE 0 -+#define R_MIPS_16 1 -+#define R_MIPS_32 2 -+#define R_MIPS_REL32 3 -+#define R_MIPS_26 4 -+#define R_MIPS_HI16 5 -+#define R_MIPS_LO16 6 -+#define R_MIPS_GPREL16 7 -+#define R_MIPS_LITERAL 8 -+#define R_MIPS_GOT16 9 -+#define R_MIPS_PC16 10 -+#define R_MIPS_CALL16 11 -+#define R_MIPS_GPREL32 12 -+ -+#define R_MIPS_SHIFT5 16 -+#define R_MIPS_SHIFT6 17 -+#define R_MIPS_64 18 -+#define R_MIPS_GOT_DISP 19 -+#define R_MIPS_GOT_PAGE 20 -+#define R_MIPS_GOT_OFST 21 -+#define R_MIPS_GOT_HI16 22 -+#define R_MIPS_GOT_LO16 23 -+#define R_MIPS_SUB 24 -+#define R_MIPS_INSERT_A 25 -+#define R_MIPS_INSERT_B 26 -+#define R_MIPS_DELETE 27 -+#define R_MIPS_HIGHER 28 -+#define R_MIPS_HIGHEST 29 -+#define R_MIPS_CALL_HI16 30 -+#define R_MIPS_CALL_LO16 31 -+#define R_MIPS_SCN_DISP 32 -+#define R_MIPS_REL16 33 -+#define R_MIPS_ADD_IMMEDIATE 34 -+#define R_MIPS_PJUMP 35 -+#define R_MIPS_RELGOT 36 -+#define R_MIPS_JALR 37 -+#define R_MIPS_TLS_DTPMOD32 38 -+#define R_MIPS_TLS_DTPREL32 39 -+#define R_MIPS_TLS_DTPMOD64 40 -+#define R_MIPS_TLS_DTPREL64 41 -+#define R_MIPS_TLS_GD 42 -+#define R_MIPS_TLS_LDM 43 -+#define R_MIPS_TLS_DTPREL_HI16 44 -+#define R_MIPS_TLS_DTPREL_LO16 45 -+#define R_MIPS_TLS_GOTTPREL 46 -+#define R_MIPS_TLS_TPREL32 47 -+#define R_MIPS_TLS_TPREL64 48 -+#define R_MIPS_TLS_TPREL_HI16 49 -+#define R_MIPS_TLS_TPREL_LO16 50 -+#define R_MIPS_GLOB_DAT 51 -+#define R_MIPS_COPY 126 -+#define R_MIPS_JUMP_SLOT 127 -+ -+#define R_MIPS_NUM 128 -+ -+ -+ -+#define PT_MIPS_REGINFO 0x70000000 -+#define PT_MIPS_RTPROC 0x70000001 -+#define PT_MIPS_OPTIONS 0x70000002 -+ -+ -+ -+#define PF_MIPS_LOCAL 0x10000000 -+ -+ -+ -+#define DT_MIPS_RLD_VERSION 0x70000001 -+#define DT_MIPS_TIME_STAMP 0x70000002 -+#define DT_MIPS_ICHECKSUM 0x70000003 -+#define DT_MIPS_IVERSION 0x70000004 -+#define DT_MIPS_FLAGS 0x70000005 -+#define DT_MIPS_BASE_ADDRESS 0x70000006 -+#define DT_MIPS_MSYM 0x70000007 -+#define DT_MIPS_CONFLICT 0x70000008 -+#define DT_MIPS_LIBLIST 0x70000009 -+#define DT_MIPS_LOCAL_GOTNO 0x7000000a -+#define DT_MIPS_CONFLICTNO 0x7000000b -+#define DT_MIPS_LIBLISTNO 0x70000010 -+#define DT_MIPS_SYMTABNO 0x70000011 -+#define DT_MIPS_UNREFEXTNO 0x70000012 -+#define DT_MIPS_GOTSYM 0x70000013 -+#define DT_MIPS_HIPAGENO 0x70000014 -+#define DT_MIPS_RLD_MAP 0x70000016 -+#define DT_MIPS_DELTA_CLASS 0x70000017 -+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 -+ -+#define DT_MIPS_DELTA_INSTANCE 0x70000019 -+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a -+ -+#define DT_MIPS_DELTA_RELOC 0x7000001b -+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c -+ -+#define DT_MIPS_DELTA_SYM 0x7000001d -+ -+#define DT_MIPS_DELTA_SYM_NO 0x7000001e -+ -+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 -+ -+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 -+ -+#define DT_MIPS_CXX_FLAGS 0x70000022 -+#define DT_MIPS_PIXIE_INIT 0x70000023 -+#define DT_MIPS_SYMBOL_LIB 0x70000024 -+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -+#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -+#define DT_MIPS_OPTIONS 0x70000029 -+#define DT_MIPS_INTERFACE 0x7000002a -+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -+#define DT_MIPS_INTERFACE_SIZE 0x7000002c -+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d -+ -+#define DT_MIPS_PERF_SUFFIX 0x7000002e -+ -+#define DT_MIPS_COMPACT_SIZE 0x7000002f -+#define DT_MIPS_GP_VALUE 0x70000030 -+#define DT_MIPS_AUX_DYNAMIC 0x70000031 -+ -+#define DT_MIPS_PLTGOT 0x70000032 -+ -+#define DT_MIPS_RWPLT 0x70000034 -+#define DT_MIPS_NUM 0x35 -+ -+ -+ -+#define RHF_NONE 0 -+#define RHF_QUICKSTART (1 << 0) -+#define RHF_NOTPOT (1 << 1) -+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) -+#define RHF_NO_MOVE (1 << 3) -+#define RHF_SGI_ONLY (1 << 4) -+#define RHF_GUARANTEE_INIT (1 << 5) -+#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -+#define RHF_GUARANTEE_START_INIT (1 << 7) -+#define RHF_PIXIE (1 << 8) -+#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -+#define RHF_REQUICKSTART (1 << 10) -+#define RHF_REQUICKSTARTED (1 << 11) -+#define RHF_CORD (1 << 12) -+#define RHF_NO_UNRES_UNDEF (1 << 13) -+#define RHF_RLD_ORDER_SAFE (1 << 14) -+ -+ -+ -+typedef struct -+{ -+ Elf32_Word l_name; -+ Elf32_Word l_time_stamp; -+ Elf32_Word l_checksum; -+ Elf32_Word l_version; -+ Elf32_Word l_flags; -+} Elf32_Lib; -+ -+typedef struct -+{ -+ Elf64_Word l_name; -+ Elf64_Word l_time_stamp; -+ Elf64_Word l_checksum; -+ Elf64_Word l_version; -+ Elf64_Word l_flags; -+} Elf64_Lib; -+ -+ -+ -+ -+#define LL_NONE 0 -+#define LL_EXACT_MATCH (1 << 0) -+#define LL_IGNORE_INT_VER (1 << 1) -+#define LL_REQUIRE_MINOR (1 << 2) -+#define LL_EXPORTS (1 << 3) -+#define LL_DELAY_LOAD (1 << 4) -+#define LL_DELTA (1 << 5) -+ -+ -+ -+typedef Elf32_Addr Elf32_Conflict; -+ -+ -+ -+ -+ -+ -+#define EF_PARISC_TRAPNIL 0x00010000 -+#define EF_PARISC_EXT 0x00020000 -+#define EF_PARISC_LSB 0x00040000 -+#define EF_PARISC_WIDE 0x00080000 -+#define EF_PARISC_NO_KABP 0x00100000 -+ -+#define EF_PARISC_LAZYSWAP 0x00400000 -+#define EF_PARISC_ARCH 0x0000ffff -+ -+ -+ -+#define EFA_PARISC_1_0 0x020b -+#define EFA_PARISC_1_1 0x0210 -+#define EFA_PARISC_2_0 0x0214 -+ -+ -+ -+#define SHN_PARISC_ANSI_COMMON 0xff00 -+ -+#define SHN_PARISC_HUGE_COMMON 0xff01 -+ -+ -+ -+#define SHT_PARISC_EXT 0x70000000 -+#define SHT_PARISC_UNWIND 0x70000001 -+#define SHT_PARISC_DOC 0x70000002 -+ -+ -+ -+#define SHF_PARISC_SHORT 0x20000000 -+#define SHF_PARISC_HUGE 0x40000000 -+#define SHF_PARISC_SBP 0x80000000 -+ -+ -+ -+#define STT_PARISC_MILLICODE 13 -+ -+#define STT_HP_OPAQUE (STT_LOOS + 0x1) -+#define STT_HP_STUB (STT_LOOS + 0x2) -+ -+ -+ -+#define R_PARISC_NONE 0 -+#define R_PARISC_DIR32 1 -+#define R_PARISC_DIR21L 2 -+#define R_PARISC_DIR17R 3 -+#define R_PARISC_DIR17F 4 -+#define R_PARISC_DIR14R 6 -+#define R_PARISC_PCREL32 9 -+#define R_PARISC_PCREL21L 10 -+#define R_PARISC_PCREL17R 11 -+#define R_PARISC_PCREL17F 12 -+#define R_PARISC_PCREL14R 14 -+#define R_PARISC_DPREL21L 18 -+#define R_PARISC_DPREL14R 22 -+#define R_PARISC_GPREL21L 26 -+#define R_PARISC_GPREL14R 30 -+#define R_PARISC_LTOFF21L 34 -+#define R_PARISC_LTOFF14R 38 -+#define R_PARISC_SECREL32 41 -+#define R_PARISC_SEGBASE 48 -+#define R_PARISC_SEGREL32 49 -+#define R_PARISC_PLTOFF21L 50 -+#define R_PARISC_PLTOFF14R 54 -+#define R_PARISC_LTOFF_FPTR32 57 -+#define R_PARISC_LTOFF_FPTR21L 58 -+#define R_PARISC_LTOFF_FPTR14R 62 -+#define R_PARISC_FPTR64 64 -+#define R_PARISC_PLABEL32 65 -+#define R_PARISC_PLABEL21L 66 -+#define R_PARISC_PLABEL14R 70 -+#define R_PARISC_PCREL64 72 -+#define R_PARISC_PCREL22F 74 -+#define R_PARISC_PCREL14WR 75 -+#define R_PARISC_PCREL14DR 76 -+#define R_PARISC_PCREL16F 77 -+#define R_PARISC_PCREL16WF 78 -+#define R_PARISC_PCREL16DF 79 -+#define R_PARISC_DIR64 80 -+#define R_PARISC_DIR14WR 83 -+#define R_PARISC_DIR14DR 84 -+#define R_PARISC_DIR16F 85 -+#define R_PARISC_DIR16WF 86 -+#define R_PARISC_DIR16DF 87 -+#define R_PARISC_GPREL64 88 -+#define R_PARISC_GPREL14WR 91 -+#define R_PARISC_GPREL14DR 92 -+#define R_PARISC_GPREL16F 93 -+#define R_PARISC_GPREL16WF 94 -+#define R_PARISC_GPREL16DF 95 -+#define R_PARISC_LTOFF64 96 -+#define R_PARISC_LTOFF14WR 99 -+#define R_PARISC_LTOFF14DR 100 -+#define R_PARISC_LTOFF16F 101 -+#define R_PARISC_LTOFF16WF 102 -+#define R_PARISC_LTOFF16DF 103 -+#define R_PARISC_SECREL64 104 -+#define R_PARISC_SEGREL64 112 -+#define R_PARISC_PLTOFF14WR 115 -+#define R_PARISC_PLTOFF14DR 116 -+#define R_PARISC_PLTOFF16F 117 -+#define R_PARISC_PLTOFF16WF 118 -+#define R_PARISC_PLTOFF16DF 119 -+#define R_PARISC_LTOFF_FPTR64 120 -+#define R_PARISC_LTOFF_FPTR14WR 123 -+#define R_PARISC_LTOFF_FPTR14DR 124 -+#define R_PARISC_LTOFF_FPTR16F 125 -+#define R_PARISC_LTOFF_FPTR16WF 126 -+#define R_PARISC_LTOFF_FPTR16DF 127 -+#define R_PARISC_LORESERVE 128 -+#define R_PARISC_COPY 128 -+#define R_PARISC_IPLT 129 -+#define R_PARISC_EPLT 130 -+#define R_PARISC_TPREL32 153 -+#define R_PARISC_TPREL21L 154 -+#define R_PARISC_TPREL14R 158 -+#define R_PARISC_LTOFF_TP21L 162 -+#define R_PARISC_LTOFF_TP14R 166 -+#define R_PARISC_LTOFF_TP14F 167 -+#define R_PARISC_TPREL64 216 -+#define R_PARISC_TPREL14WR 219 -+#define R_PARISC_TPREL14DR 220 -+#define R_PARISC_TPREL16F 221 -+#define R_PARISC_TPREL16WF 222 -+#define R_PARISC_TPREL16DF 223 -+#define R_PARISC_LTOFF_TP64 224 -+#define R_PARISC_LTOFF_TP14WR 227 -+#define R_PARISC_LTOFF_TP14DR 228 -+#define R_PARISC_LTOFF_TP16F 229 -+#define R_PARISC_LTOFF_TP16WF 230 -+#define R_PARISC_LTOFF_TP16DF 231 -+#define R_PARISC_GNU_VTENTRY 232 -+#define R_PARISC_GNU_VTINHERIT 233 -+#define R_PARISC_TLS_GD21L 234 -+#define R_PARISC_TLS_GD14R 235 -+#define R_PARISC_TLS_GDCALL 236 -+#define R_PARISC_TLS_LDM21L 237 -+#define R_PARISC_TLS_LDM14R 238 -+#define R_PARISC_TLS_LDMCALL 239 -+#define R_PARISC_TLS_LDO21L 240 -+#define R_PARISC_TLS_LDO14R 241 -+#define R_PARISC_TLS_DTPMOD32 242 -+#define R_PARISC_TLS_DTPMOD64 243 -+#define R_PARISC_TLS_DTPOFF32 244 -+#define R_PARISC_TLS_DTPOFF64 245 -+#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L -+#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R -+#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L -+#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R -+#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 -+#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 -+#define R_PARISC_HIRESERVE 255 -+ -+ -+ -+#define PT_HP_TLS (PT_LOOS + 0x0) -+#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -+#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -+#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -+#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -+#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -+#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -+#define PT_HP_PARALLEL (PT_LOOS + 0x10) -+#define PT_HP_FASTBIND (PT_LOOS + 0x11) -+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -+#define PT_HP_STACK (PT_LOOS + 0x14) -+ -+#define PT_PARISC_ARCHEXT 0x70000000 -+#define PT_PARISC_UNWIND 0x70000001 -+ -+ -+ -+#define PF_PARISC_SBP 0x08000000 -+ -+#define PF_HP_PAGE_SIZE 0x00100000 -+#define PF_HP_FAR_SHARED 0x00200000 -+#define PF_HP_NEAR_SHARED 0x00400000 -+#define PF_HP_CODE 0x01000000 -+#define PF_HP_MODIFY 0x02000000 -+#define PF_HP_LAZYSWAP 0x04000000 -+#define PF_HP_SBP 0x08000000 -+ -+ -+ -+ -+ -+ -+#define EF_ALPHA_32BIT 1 -+#define EF_ALPHA_CANRELAX 2 -+ -+ -+ -+ -+#define SHT_ALPHA_DEBUG 0x70000001 -+#define SHT_ALPHA_REGINFO 0x70000002 -+ -+ -+ -+#define SHF_ALPHA_GPREL 0x10000000 -+ -+ -+#define STO_ALPHA_NOPV 0x80 -+#define STO_ALPHA_STD_GPLOAD 0x88 -+ -+ -+ -+#define R_ALPHA_NONE 0 -+#define R_ALPHA_REFLONG 1 -+#define R_ALPHA_REFQUAD 2 -+#define R_ALPHA_GPREL32 3 -+#define R_ALPHA_LITERAL 4 -+#define R_ALPHA_LITUSE 5 -+#define R_ALPHA_GPDISP 6 -+#define R_ALPHA_BRADDR 7 -+#define R_ALPHA_HINT 8 -+#define R_ALPHA_SREL16 9 -+#define R_ALPHA_SREL32 10 -+#define R_ALPHA_SREL64 11 -+#define R_ALPHA_GPRELHIGH 17 -+#define R_ALPHA_GPRELLOW 18 -+#define R_ALPHA_GPREL16 19 -+#define R_ALPHA_COPY 24 -+#define R_ALPHA_GLOB_DAT 25 -+#define R_ALPHA_JMP_SLOT 26 -+#define R_ALPHA_RELATIVE 27 -+#define R_ALPHA_TLS_GD_HI 28 -+#define R_ALPHA_TLSGD 29 -+#define R_ALPHA_TLS_LDM 30 -+#define R_ALPHA_DTPMOD64 31 -+#define R_ALPHA_GOTDTPREL 32 -+#define R_ALPHA_DTPREL64 33 -+#define R_ALPHA_DTPRELHI 34 -+#define R_ALPHA_DTPRELLO 35 -+#define R_ALPHA_DTPREL16 36 -+#define R_ALPHA_GOTTPREL 37 -+#define R_ALPHA_TPREL64 38 -+#define R_ALPHA_TPRELHI 39 -+#define R_ALPHA_TPRELLO 40 -+#define R_ALPHA_TPREL16 41 -+ -+#define R_ALPHA_NUM 46 -+ -+ -+#define LITUSE_ALPHA_ADDR 0 -+#define LITUSE_ALPHA_BASE 1 -+#define LITUSE_ALPHA_BYTOFF 2 -+#define LITUSE_ALPHA_JSR 3 -+#define LITUSE_ALPHA_TLS_GD 4 -+#define LITUSE_ALPHA_TLS_LDM 5 -+ -+ -+#define DT_ALPHA_PLTRO (DT_LOPROC + 0) -+#define DT_ALPHA_NUM 1 -+ -+ -+ -+ -+#define EF_PPC_EMB 0x80000000 -+ -+ -+#define EF_PPC_RELOCATABLE 0x00010000 -+#define EF_PPC_RELOCATABLE_LIB 0x00008000 -+ -+ -+ -+#define R_PPC_NONE 0 -+#define R_PPC_ADDR32 1 -+#define R_PPC_ADDR24 2 -+#define R_PPC_ADDR16 3 -+#define R_PPC_ADDR16_LO 4 -+#define R_PPC_ADDR16_HI 5 -+#define R_PPC_ADDR16_HA 6 -+#define R_PPC_ADDR14 7 -+#define R_PPC_ADDR14_BRTAKEN 8 -+#define R_PPC_ADDR14_BRNTAKEN 9 -+#define R_PPC_REL24 10 -+#define R_PPC_REL14 11 -+#define R_PPC_REL14_BRTAKEN 12 -+#define R_PPC_REL14_BRNTAKEN 13 -+#define R_PPC_GOT16 14 -+#define R_PPC_GOT16_LO 15 -+#define R_PPC_GOT16_HI 16 -+#define R_PPC_GOT16_HA 17 -+#define R_PPC_PLTREL24 18 -+#define R_PPC_COPY 19 -+#define R_PPC_GLOB_DAT 20 -+#define R_PPC_JMP_SLOT 21 -+#define R_PPC_RELATIVE 22 -+#define R_PPC_LOCAL24PC 23 -+#define R_PPC_UADDR32 24 -+#define R_PPC_UADDR16 25 -+#define R_PPC_REL32 26 -+#define R_PPC_PLT32 27 -+#define R_PPC_PLTREL32 28 -+#define R_PPC_PLT16_LO 29 -+#define R_PPC_PLT16_HI 30 -+#define R_PPC_PLT16_HA 31 -+#define R_PPC_SDAREL16 32 -+#define R_PPC_SECTOFF 33 -+#define R_PPC_SECTOFF_LO 34 -+#define R_PPC_SECTOFF_HI 35 -+#define R_PPC_SECTOFF_HA 36 -+ -+ -+#define R_PPC_TLS 67 -+#define R_PPC_DTPMOD32 68 -+#define R_PPC_TPREL16 69 -+#define R_PPC_TPREL16_LO 70 -+#define R_PPC_TPREL16_HI 71 -+#define R_PPC_TPREL16_HA 72 -+#define R_PPC_TPREL32 73 -+#define R_PPC_DTPREL16 74 -+#define R_PPC_DTPREL16_LO 75 -+#define R_PPC_DTPREL16_HI 76 -+#define R_PPC_DTPREL16_HA 77 -+#define R_PPC_DTPREL32 78 -+#define R_PPC_GOT_TLSGD16 79 -+#define R_PPC_GOT_TLSGD16_LO 80 -+#define R_PPC_GOT_TLSGD16_HI 81 -+#define R_PPC_GOT_TLSGD16_HA 82 -+#define R_PPC_GOT_TLSLD16 83 -+#define R_PPC_GOT_TLSLD16_LO 84 -+#define R_PPC_GOT_TLSLD16_HI 85 -+#define R_PPC_GOT_TLSLD16_HA 86 -+#define R_PPC_GOT_TPREL16 87 -+#define R_PPC_GOT_TPREL16_LO 88 -+#define R_PPC_GOT_TPREL16_HI 89 -+#define R_PPC_GOT_TPREL16_HA 90 -+#define R_PPC_GOT_DTPREL16 91 -+#define R_PPC_GOT_DTPREL16_LO 92 -+#define R_PPC_GOT_DTPREL16_HI 93 -+#define R_PPC_GOT_DTPREL16_HA 94 -+ -+ -+ -+#define R_PPC_EMB_NADDR32 101 -+#define R_PPC_EMB_NADDR16 102 -+#define R_PPC_EMB_NADDR16_LO 103 -+#define R_PPC_EMB_NADDR16_HI 104 -+#define R_PPC_EMB_NADDR16_HA 105 -+#define R_PPC_EMB_SDAI16 106 -+#define R_PPC_EMB_SDA2I16 107 -+#define R_PPC_EMB_SDA2REL 108 -+#define R_PPC_EMB_SDA21 109 -+#define R_PPC_EMB_MRKREF 110 -+#define R_PPC_EMB_RELSEC16 111 -+#define R_PPC_EMB_RELST_LO 112 -+#define R_PPC_EMB_RELST_HI 113 -+#define R_PPC_EMB_RELST_HA 114 -+#define R_PPC_EMB_BIT_FLD 115 -+#define R_PPC_EMB_RELSDA 116 -+ -+ -+#define R_PPC_DIAB_SDA21_LO 180 -+#define R_PPC_DIAB_SDA21_HI 181 -+#define R_PPC_DIAB_SDA21_HA 182 -+#define R_PPC_DIAB_RELSDA_LO 183 -+#define R_PPC_DIAB_RELSDA_HI 184 -+#define R_PPC_DIAB_RELSDA_HA 185 -+ -+ -+#define R_PPC_IRELATIVE 248 -+ -+ -+#define R_PPC_REL16 249 -+#define R_PPC_REL16_LO 250 -+#define R_PPC_REL16_HI 251 -+#define R_PPC_REL16_HA 252 -+ -+ -+ -+#define R_PPC_TOC16 255 -+ -+ -+#define DT_PPC_GOT (DT_LOPROC + 0) -+#define DT_PPC_NUM 1 -+ -+ -+#define R_PPC64_NONE R_PPC_NONE -+#define R_PPC64_ADDR32 R_PPC_ADDR32 -+#define R_PPC64_ADDR24 R_PPC_ADDR24 -+#define R_PPC64_ADDR16 R_PPC_ADDR16 -+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO -+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI -+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA -+#define R_PPC64_ADDR14 R_PPC_ADDR14 -+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -+#define R_PPC64_REL24 R_PPC_REL24 -+#define R_PPC64_REL14 R_PPC_REL14 -+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -+#define R_PPC64_GOT16 R_PPC_GOT16 -+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA -+ -+#define R_PPC64_COPY R_PPC_COPY -+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -+#define R_PPC64_RELATIVE R_PPC_RELATIVE -+ -+#define R_PPC64_UADDR32 R_PPC_UADDR32 -+#define R_PPC64_UADDR16 R_PPC_UADDR16 -+#define R_PPC64_REL32 R_PPC_REL32 -+#define R_PPC64_PLT32 R_PPC_PLT32 -+#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA -+ -+#define R_PPC64_SECTOFF R_PPC_SECTOFF -+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -+#define R_PPC64_ADDR30 37 -+#define R_PPC64_ADDR64 38 -+#define R_PPC64_ADDR16_HIGHER 39 -+#define R_PPC64_ADDR16_HIGHERA 40 -+#define R_PPC64_ADDR16_HIGHEST 41 -+#define R_PPC64_ADDR16_HIGHESTA 42 -+#define R_PPC64_UADDR64 43 -+#define R_PPC64_REL64 44 -+#define R_PPC64_PLT64 45 -+#define R_PPC64_PLTREL64 46 -+#define R_PPC64_TOC16 47 -+#define R_PPC64_TOC16_LO 48 -+#define R_PPC64_TOC16_HI 49 -+#define R_PPC64_TOC16_HA 50 -+#define R_PPC64_TOC 51 -+#define R_PPC64_PLTGOT16 52 -+#define R_PPC64_PLTGOT16_LO 53 -+#define R_PPC64_PLTGOT16_HI 54 -+#define R_PPC64_PLTGOT16_HA 55 -+ -+#define R_PPC64_ADDR16_DS 56 -+#define R_PPC64_ADDR16_LO_DS 57 -+#define R_PPC64_GOT16_DS 58 -+#define R_PPC64_GOT16_LO_DS 59 -+#define R_PPC64_PLT16_LO_DS 60 -+#define R_PPC64_SECTOFF_DS 61 -+#define R_PPC64_SECTOFF_LO_DS 62 -+#define R_PPC64_TOC16_DS 63 -+#define R_PPC64_TOC16_LO_DS 64 -+#define R_PPC64_PLTGOT16_DS 65 -+#define R_PPC64_PLTGOT16_LO_DS 66 -+ -+ -+#define R_PPC64_TLS 67 -+#define R_PPC64_DTPMOD64 68 -+#define R_PPC64_TPREL16 69 -+#define R_PPC64_TPREL16_LO 70 -+#define R_PPC64_TPREL16_HI 71 -+#define R_PPC64_TPREL16_HA 72 -+#define R_PPC64_TPREL64 73 -+#define R_PPC64_DTPREL16 74 -+#define R_PPC64_DTPREL16_LO 75 -+#define R_PPC64_DTPREL16_HI 76 -+#define R_PPC64_DTPREL16_HA 77 -+#define R_PPC64_DTPREL64 78 -+#define R_PPC64_GOT_TLSGD16 79 -+#define R_PPC64_GOT_TLSGD16_LO 80 -+#define R_PPC64_GOT_TLSGD16_HI 81 -+#define R_PPC64_GOT_TLSGD16_HA 82 -+#define R_PPC64_GOT_TLSLD16 83 -+#define R_PPC64_GOT_TLSLD16_LO 84 -+#define R_PPC64_GOT_TLSLD16_HI 85 -+#define R_PPC64_GOT_TLSLD16_HA 86 -+#define R_PPC64_GOT_TPREL16_DS 87 -+#define R_PPC64_GOT_TPREL16_LO_DS 88 -+#define R_PPC64_GOT_TPREL16_HI 89 -+#define R_PPC64_GOT_TPREL16_HA 90 -+#define R_PPC64_GOT_DTPREL16_DS 91 -+#define R_PPC64_GOT_DTPREL16_LO_DS 92 -+#define R_PPC64_GOT_DTPREL16_HI 93 -+#define R_PPC64_GOT_DTPREL16_HA 94 -+#define R_PPC64_TPREL16_DS 95 -+#define R_PPC64_TPREL16_LO_DS 96 -+#define R_PPC64_TPREL16_HIGHER 97 -+#define R_PPC64_TPREL16_HIGHERA 98 -+#define R_PPC64_TPREL16_HIGHEST 99 -+#define R_PPC64_TPREL16_HIGHESTA 100 -+#define R_PPC64_DTPREL16_DS 101 -+#define R_PPC64_DTPREL16_LO_DS 102 -+#define R_PPC64_DTPREL16_HIGHER 103 -+#define R_PPC64_DTPREL16_HIGHERA 104 -+#define R_PPC64_DTPREL16_HIGHEST 105 -+#define R_PPC64_DTPREL16_HIGHESTA 106 -+ -+ -+#define R_PPC64_JMP_IREL 247 -+#define R_PPC64_IRELATIVE 248 -+#define R_PPC64_REL16 249 -+#define R_PPC64_REL16_LO 250 -+#define R_PPC64_REL16_HI 251 -+#define R_PPC64_REL16_HA 252 -+ -+ -+#define DT_PPC64_GLINK (DT_LOPROC + 0) -+#define DT_PPC64_OPD (DT_LOPROC + 1) -+#define DT_PPC64_OPDSZ (DT_LOPROC + 2) -+#define DT_PPC64_NUM 3 -+ -+ -+ -+ -+ -+#define EF_ARM_RELEXEC 0x01 -+#define EF_ARM_HASENTRY 0x02 -+#define EF_ARM_INTERWORK 0x04 -+#define EF_ARM_APCS_26 0x08 -+#define EF_ARM_APCS_FLOAT 0x10 -+#define EF_ARM_PIC 0x20 -+#define EF_ARM_ALIGN8 0x40 -+#define EF_ARM_NEW_ABI 0x80 -+#define EF_ARM_OLD_ABI 0x100 -+#define EF_ARM_SOFT_FLOAT 0x200 -+#define EF_ARM_VFP_FLOAT 0x400 -+#define EF_ARM_MAVERICK_FLOAT 0x800 -+ -+#define EF_ARM_ABI_FLOAT_SOFT 0x200 -+#define EF_ARM_ABI_FLOAT_HARD 0x400 -+ -+ -+#define EF_ARM_SYMSARESORTED 0x04 -+#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -+#define EF_ARM_MAPSYMSFIRST 0x10 -+#define EF_ARM_EABIMASK 0XFF000000 -+ -+ -+#define EF_ARM_BE8 0x00800000 -+#define EF_ARM_LE8 0x00400000 -+ -+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -+#define EF_ARM_EABI_UNKNOWN 0x00000000 -+#define EF_ARM_EABI_VER1 0x01000000 -+#define EF_ARM_EABI_VER2 0x02000000 -+#define EF_ARM_EABI_VER3 0x03000000 -+#define EF_ARM_EABI_VER4 0x04000000 -+#define EF_ARM_EABI_VER5 0x05000000 -+ -+ -+#define STT_ARM_TFUNC STT_LOPROC -+#define STT_ARM_16BIT STT_HIPROC -+ -+ -+#define SHF_ARM_ENTRYSECT 0x10000000 -+#define SHF_ARM_COMDEF 0x80000000 -+ -+ -+ -+#define PF_ARM_SB 0x10000000 -+ -+#define PF_ARM_PI 0x20000000 -+#define PF_ARM_ABS 0x40000000 -+ -+ -+#define PT_ARM_EXIDX (PT_LOPROC + 1) -+ -+ -+#define SHT_ARM_EXIDX (SHT_LOPROC + 1) -+#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) -+#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) -+ -+ -+#define R_AARCH64_NONE 0 -+#define R_AARCH64_ABS64 257 -+#define R_AARCH64_ABS32 258 -+#define R_AARCH64_COPY 1024 -+#define R_AARCH64_GLOB_DAT 1025 -+#define R_AARCH64_JUMP_SLOT 1026 -+#define R_AARCH64_RELATIVE 1027 -+#define R_AARCH64_TLS_DTPMOD64 1028 -+#define R_AARCH64_TLS_DTPREL64 1029 -+#define R_AARCH64_TLS_TPREL64 1030 -+#define R_AARCH64_TLSDESC 1031 -+ -+ -+#define R_ARM_NONE 0 -+#define R_ARM_PC24 1 -+#define R_ARM_ABS32 2 -+#define R_ARM_REL32 3 -+#define R_ARM_PC13 4 -+#define R_ARM_ABS16 5 -+#define R_ARM_ABS12 6 -+#define R_ARM_THM_ABS5 7 -+#define R_ARM_ABS8 8 -+#define R_ARM_SBREL32 9 -+#define R_ARM_THM_PC22 10 -+#define R_ARM_THM_PC8 11 -+#define R_ARM_AMP_VCALL9 12 -+#define R_ARM_TLS_DESC 13 -+#define R_ARM_THM_SWI8 14 -+#define R_ARM_XPC25 15 -+#define R_ARM_THM_XPC22 16 -+#define R_ARM_TLS_DTPMOD32 17 -+#define R_ARM_TLS_DTPOFF32 18 -+#define R_ARM_TLS_TPOFF32 19 -+#define R_ARM_COPY 20 -+#define R_ARM_GLOB_DAT 21 -+#define R_ARM_JUMP_SLOT 22 -+#define R_ARM_RELATIVE 23 -+#define R_ARM_GOTOFF 24 -+#define R_ARM_GOTPC 25 -+#define R_ARM_GOT32 26 -+#define R_ARM_PLT32 27 -+#define R_ARM_CALL 28 -+#define R_ARM_JUMP24 29 -+#define R_ARM_THM_JUMP24 30 -+#define R_ARM_BASE_ABS 31 -+#define R_ARM_ALU_PCREL_7_0 32 -+#define R_ARM_ALU_PCREL_15_8 33 -+#define R_ARM_ALU_PCREL_23_15 34 -+#define R_ARM_LDR_SBREL_11_0 35 -+#define R_ARM_ALU_SBREL_19_12 36 -+#define R_ARM_ALU_SBREL_27_20 37 -+#define R_ARM_TARGET1 38 -+#define R_ARM_SBREL31 39 -+#define R_ARM_V4BX 40 -+#define R_ARM_TARGET2 41 -+#define R_ARM_PREL31 42 -+#define R_ARM_MOVW_ABS_NC 43 -+#define R_ARM_MOVT_ABS 44 -+#define R_ARM_MOVW_PREL_NC 45 -+#define R_ARM_MOVT_PREL 46 -+#define R_ARM_THM_MOVW_ABS_NC 47 -+#define R_ARM_THM_MOVT_ABS 48 -+#define R_ARM_THM_MOVW_PREL_NC 49 -+#define R_ARM_THM_MOVT_PREL 50 -+#define R_ARM_THM_JUMP19 51 -+#define R_ARM_THM_JUMP6 52 -+#define R_ARM_THM_ALU_PREL_11_0 53 -+#define R_ARM_THM_PC12 54 -+#define R_ARM_ABS32_NOI 55 -+#define R_ARM_REL32_NOI 56 -+#define R_ARM_ALU_PC_G0_NC 57 -+#define R_ARM_ALU_PC_G0 58 -+#define R_ARM_ALU_PC_G1_NC 59 -+#define R_ARM_ALU_PC_G1 60 -+#define R_ARM_ALU_PC_G2 61 -+#define R_ARM_LDR_PC_G1 62 -+#define R_ARM_LDR_PC_G2 63 -+#define R_ARM_LDRS_PC_G0 64 -+#define R_ARM_LDRS_PC_G1 65 -+#define R_ARM_LDRS_PC_G2 66 -+#define R_ARM_LDC_PC_G0 67 -+#define R_ARM_LDC_PC_G1 68 -+#define R_ARM_LDC_PC_G2 69 -+#define R_ARM_ALU_SB_G0_NC 70 -+#define R_ARM_ALU_SB_G0 71 -+#define R_ARM_ALU_SB_G1_NC 72 -+#define R_ARM_ALU_SB_G1 73 -+#define R_ARM_ALU_SB_G2 74 -+#define R_ARM_LDR_SB_G0 75 -+#define R_ARM_LDR_SB_G1 76 -+#define R_ARM_LDR_SB_G2 77 -+#define R_ARM_LDRS_SB_G0 78 -+#define R_ARM_LDRS_SB_G1 79 -+#define R_ARM_LDRS_SB_G2 80 -+#define R_ARM_LDC_SB_G0 81 -+#define R_ARM_LDC_SB_G1 82 -+#define R_ARM_LDC_SB_G2 83 -+#define R_ARM_MOVW_BREL_NC 84 -+#define R_ARM_MOVT_BREL 85 -+#define R_ARM_MOVW_BREL 86 -+#define R_ARM_THM_MOVW_BREL_NC 87 -+#define R_ARM_THM_MOVT_BREL 88 -+#define R_ARM_THM_MOVW_BREL 89 -+#define R_ARM_TLS_GOTDESC 90 -+#define R_ARM_TLS_CALL 91 -+#define R_ARM_TLS_DESCSEQ 92 -+#define R_ARM_THM_TLS_CALL 93 -+#define R_ARM_PLT32_ABS 94 -+#define R_ARM_GOT_ABS 95 -+#define R_ARM_GOT_PREL 96 -+#define R_ARM_GOT_BREL12 97 -+#define R_ARM_GOTOFF12 98 -+#define R_ARM_GOTRELAX 99 -+#define R_ARM_GNU_VTENTRY 100 -+#define R_ARM_GNU_VTINHERIT 101 -+#define R_ARM_THM_PC11 102 -+#define R_ARM_THM_PC9 103 -+#define R_ARM_TLS_GD32 104 -+ -+#define R_ARM_TLS_LDM32 105 -+ -+#define R_ARM_TLS_LDO32 106 -+ -+#define R_ARM_TLS_IE32 107 -+ -+#define R_ARM_TLS_LE32 108 -+#define R_ARM_TLS_LDO12 109 -+#define R_ARM_TLS_LE12 110 -+#define R_ARM_TLS_IE12GP 111 -+#define R_ARM_ME_TOO 128 -+#define R_ARM_THM_TLS_DESCSEQ 129 -+#define R_ARM_THM_TLS_DESCSEQ16 129 -+#define R_ARM_THM_TLS_DESCSEQ32 130 -+#define R_ARM_THM_GOT_BREL12 131 -+#define R_ARM_IRELATIVE 160 -+#define R_ARM_RXPC25 249 -+#define R_ARM_RSBREL32 250 -+#define R_ARM_THM_RPC22 251 -+#define R_ARM_RREL32 252 -+#define R_ARM_RABS22 253 -+#define R_ARM_RPC24 254 -+#define R_ARM_RBASE 255 -+ -+#define R_ARM_NUM 256 -+ -+ -+ -+ -+#define EF_IA_64_MASKOS 0x0000000f -+#define EF_IA_64_ABI64 0x00000010 -+#define EF_IA_64_ARCH 0xff000000 -+ -+ -+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) -+#define PT_IA_64_UNWIND (PT_LOPROC + 1) -+#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) -+#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) -+#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) -+ -+ -+#define PF_IA_64_NORECOV 0x80000000 -+ -+ -+#define SHT_IA_64_EXT (SHT_LOPROC + 0) -+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) -+ -+ -+#define SHF_IA_64_SHORT 0x10000000 -+#define SHF_IA_64_NORECOV 0x20000000 -+ -+ -+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -+#define DT_IA_64_NUM 1 -+ -+ -+#define R_IA64_NONE 0x00 -+#define R_IA64_IMM14 0x21 -+#define R_IA64_IMM22 0x22 -+#define R_IA64_IMM64 0x23 -+#define R_IA64_DIR32MSB 0x24 -+#define R_IA64_DIR32LSB 0x25 -+#define R_IA64_DIR64MSB 0x26 -+#define R_IA64_DIR64LSB 0x27 -+#define R_IA64_GPREL22 0x2a -+#define R_IA64_GPREL64I 0x2b -+#define R_IA64_GPREL32MSB 0x2c -+#define R_IA64_GPREL32LSB 0x2d -+#define R_IA64_GPREL64MSB 0x2e -+#define R_IA64_GPREL64LSB 0x2f -+#define R_IA64_LTOFF22 0x32 -+#define R_IA64_LTOFF64I 0x33 -+#define R_IA64_PLTOFF22 0x3a -+#define R_IA64_PLTOFF64I 0x3b -+#define R_IA64_PLTOFF64MSB 0x3e -+#define R_IA64_PLTOFF64LSB 0x3f -+#define R_IA64_FPTR64I 0x43 -+#define R_IA64_FPTR32MSB 0x44 -+#define R_IA64_FPTR32LSB 0x45 -+#define R_IA64_FPTR64MSB 0x46 -+#define R_IA64_FPTR64LSB 0x47 -+#define R_IA64_PCREL60B 0x48 -+#define R_IA64_PCREL21B 0x49 -+#define R_IA64_PCREL21M 0x4a -+#define R_IA64_PCREL21F 0x4b -+#define R_IA64_PCREL32MSB 0x4c -+#define R_IA64_PCREL32LSB 0x4d -+#define R_IA64_PCREL64MSB 0x4e -+#define R_IA64_PCREL64LSB 0x4f -+#define R_IA64_LTOFF_FPTR22 0x52 -+#define R_IA64_LTOFF_FPTR64I 0x53 -+#define R_IA64_LTOFF_FPTR32MSB 0x54 -+#define R_IA64_LTOFF_FPTR32LSB 0x55 -+#define R_IA64_LTOFF_FPTR64MSB 0x56 -+#define R_IA64_LTOFF_FPTR64LSB 0x57 -+#define R_IA64_SEGREL32MSB 0x5c -+#define R_IA64_SEGREL32LSB 0x5d -+#define R_IA64_SEGREL64MSB 0x5e -+#define R_IA64_SEGREL64LSB 0x5f -+#define R_IA64_SECREL32MSB 0x64 -+#define R_IA64_SECREL32LSB 0x65 -+#define R_IA64_SECREL64MSB 0x66 -+#define R_IA64_SECREL64LSB 0x67 -+#define R_IA64_REL32MSB 0x6c -+#define R_IA64_REL32LSB 0x6d -+#define R_IA64_REL64MSB 0x6e -+#define R_IA64_REL64LSB 0x6f -+#define R_IA64_LTV32MSB 0x74 -+#define R_IA64_LTV32LSB 0x75 -+#define R_IA64_LTV64MSB 0x76 -+#define R_IA64_LTV64LSB 0x77 -+#define R_IA64_PCREL21BI 0x79 -+#define R_IA64_PCREL22 0x7a -+#define R_IA64_PCREL64I 0x7b -+#define R_IA64_IPLTMSB 0x80 -+#define R_IA64_IPLTLSB 0x81 -+#define R_IA64_COPY 0x84 -+#define R_IA64_SUB 0x85 -+#define R_IA64_LTOFF22X 0x86 -+#define R_IA64_LDXMOV 0x87 -+#define R_IA64_TPREL14 0x91 -+#define R_IA64_TPREL22 0x92 -+#define R_IA64_TPREL64I 0x93 -+#define R_IA64_TPREL64MSB 0x96 -+#define R_IA64_TPREL64LSB 0x97 -+#define R_IA64_LTOFF_TPREL22 0x9a -+#define R_IA64_DTPMOD64MSB 0xa6 -+#define R_IA64_DTPMOD64LSB 0xa7 -+#define R_IA64_LTOFF_DTPMOD22 0xaa -+#define R_IA64_DTPREL14 0xb1 -+#define R_IA64_DTPREL22 0xb2 -+#define R_IA64_DTPREL64I 0xb3 -+#define R_IA64_DTPREL32MSB 0xb4 -+#define R_IA64_DTPREL32LSB 0xb5 -+#define R_IA64_DTPREL64MSB 0xb6 -+#define R_IA64_DTPREL64LSB 0xb7 -+#define R_IA64_LTOFF_DTPREL22 0xba -+ -+ -+ -+ -+#define R_SH_NONE 0 -+#define R_SH_DIR32 1 -+#define R_SH_REL32 2 -+#define R_SH_DIR8WPN 3 -+#define R_SH_IND12W 4 -+#define R_SH_DIR8WPL 5 -+#define R_SH_DIR8WPZ 6 -+#define R_SH_DIR8BP 7 -+#define R_SH_DIR8W 8 -+#define R_SH_DIR8L 9 -+#define R_SH_SWITCH16 25 -+#define R_SH_SWITCH32 26 -+#define R_SH_USES 27 -+#define R_SH_COUNT 28 -+#define R_SH_ALIGN 29 -+#define R_SH_CODE 30 -+#define R_SH_DATA 31 -+#define R_SH_LABEL 32 -+#define R_SH_SWITCH8 33 -+#define R_SH_GNU_VTINHERIT 34 -+#define R_SH_GNU_VTENTRY 35 -+#define R_SH_TLS_GD_32 144 -+#define R_SH_TLS_LD_32 145 -+#define R_SH_TLS_LDO_32 146 -+#define R_SH_TLS_IE_32 147 -+#define R_SH_TLS_LE_32 148 -+#define R_SH_TLS_DTPMOD32 149 -+#define R_SH_TLS_DTPOFF32 150 -+#define R_SH_TLS_TPOFF32 151 -+#define R_SH_GOT32 160 -+#define R_SH_PLT32 161 -+#define R_SH_COPY 162 -+#define R_SH_GLOB_DAT 163 -+#define R_SH_JMP_SLOT 164 -+#define R_SH_RELATIVE 165 -+#define R_SH_GOTOFF 166 -+#define R_SH_GOTPC 167 -+ -+#define R_SH_NUM 256 -+ -+ -+ -+#define R_390_NONE 0 -+#define R_390_8 1 -+#define R_390_12 2 -+#define R_390_16 3 -+#define R_390_32 4 -+#define R_390_PC32 5 -+#define R_390_GOT12 6 -+#define R_390_GOT32 7 -+#define R_390_PLT32 8 -+#define R_390_COPY 9 -+#define R_390_GLOB_DAT 10 -+#define R_390_JMP_SLOT 11 -+#define R_390_RELATIVE 12 -+#define R_390_GOTOFF32 13 -+#define R_390_GOTPC 14 -+#define R_390_GOT16 15 -+#define R_390_PC16 16 -+#define R_390_PC16DBL 17 -+#define R_390_PLT16DBL 18 -+#define R_390_PC32DBL 19 -+#define R_390_PLT32DBL 20 -+#define R_390_GOTPCDBL 21 -+#define R_390_64 22 -+#define R_390_PC64 23 -+#define R_390_GOT64 24 -+#define R_390_PLT64 25 -+#define R_390_GOTENT 26 -+#define R_390_GOTOFF16 27 -+#define R_390_GOTOFF64 28 -+#define R_390_GOTPLT12 29 -+#define R_390_GOTPLT16 30 -+#define R_390_GOTPLT32 31 -+#define R_390_GOTPLT64 32 -+#define R_390_GOTPLTENT 33 -+#define R_390_PLTOFF16 34 -+#define R_390_PLTOFF32 35 -+#define R_390_PLTOFF64 36 -+#define R_390_TLS_LOAD 37 -+#define R_390_TLS_GDCALL 38 -+ -+#define R_390_TLS_LDCALL 39 -+ -+#define R_390_TLS_GD32 40 -+ -+#define R_390_TLS_GD64 41 -+ -+#define R_390_TLS_GOTIE12 42 -+ -+#define R_390_TLS_GOTIE32 43 -+ -+#define R_390_TLS_GOTIE64 44 -+ -+#define R_390_TLS_LDM32 45 -+ -+#define R_390_TLS_LDM64 46 -+ -+#define R_390_TLS_IE32 47 -+ -+#define R_390_TLS_IE64 48 -+ -+#define R_390_TLS_IEENT 49 -+ -+#define R_390_TLS_LE32 50 -+ -+#define R_390_TLS_LE64 51 -+ -+#define R_390_TLS_LDO32 52 -+ -+#define R_390_TLS_LDO64 53 -+ -+#define R_390_TLS_DTPMOD 54 -+#define R_390_TLS_DTPOFF 55 -+#define R_390_TLS_TPOFF 56 -+ -+#define R_390_20 57 -+#define R_390_GOT20 58 -+#define R_390_GOTPLT20 59 -+#define R_390_TLS_GOTIE20 60 -+ -+ -+#define R_390_NUM 61 -+ -+ -+ -+#define R_CRIS_NONE 0 -+#define R_CRIS_8 1 -+#define R_CRIS_16 2 -+#define R_CRIS_32 3 -+#define R_CRIS_8_PCREL 4 -+#define R_CRIS_16_PCREL 5 -+#define R_CRIS_32_PCREL 6 -+#define R_CRIS_GNU_VTINHERIT 7 -+#define R_CRIS_GNU_VTENTRY 8 -+#define R_CRIS_COPY 9 -+#define R_CRIS_GLOB_DAT 10 -+#define R_CRIS_JUMP_SLOT 11 -+#define R_CRIS_RELATIVE 12 -+#define R_CRIS_16_GOT 13 -+#define R_CRIS_32_GOT 14 -+#define R_CRIS_16_GOTPLT 15 -+#define R_CRIS_32_GOTPLT 16 -+#define R_CRIS_32_GOTREL 17 -+#define R_CRIS_32_PLT_GOTREL 18 -+#define R_CRIS_32_PLT_PCREL 19 -+ -+#define R_CRIS_NUM 20 -+ -+ -+ -+#define R_X86_64_NONE 0 -+#define R_X86_64_64 1 -+#define R_X86_64_PC32 2 -+#define R_X86_64_GOT32 3 -+#define R_X86_64_PLT32 4 -+#define R_X86_64_COPY 5 -+#define R_X86_64_GLOB_DAT 6 -+#define R_X86_64_JUMP_SLOT 7 -+#define R_X86_64_RELATIVE 8 -+#define R_X86_64_GOTPCREL 9 -+ -+#define R_X86_64_32 10 -+#define R_X86_64_32S 11 -+#define R_X86_64_16 12 -+#define R_X86_64_PC16 13 -+#define R_X86_64_8 14 -+#define R_X86_64_PC8 15 -+#define R_X86_64_DTPMOD64 16 -+#define R_X86_64_DTPOFF64 17 -+#define R_X86_64_TPOFF64 18 -+#define R_X86_64_TLSGD 19 -+ -+#define R_X86_64_TLSLD 20 -+ -+#define R_X86_64_DTPOFF32 21 -+#define R_X86_64_GOTTPOFF 22 -+ -+#define R_X86_64_TPOFF32 23 -+#define R_X86_64_PC64 24 -+#define R_X86_64_GOTOFF64 25 -+#define R_X86_64_GOTPC32 26 -+#define R_X86_64_GOT64 27 -+#define R_X86_64_GOTPCREL64 28 -+#define R_X86_64_GOTPC64 29 -+#define R_X86_64_GOTPLT64 30 -+#define R_X86_64_PLTOFF64 31 -+#define R_X86_64_SIZE32 32 -+#define R_X86_64_SIZE64 33 -+ -+#define R_X86_64_GOTPC32_TLSDESC 34 -+#define R_X86_64_TLSDESC_CALL 35 -+ -+#define R_X86_64_TLSDESC 36 -+#define R_X86_64_IRELATIVE 37 -+#define R_X86_64_RELATIVE64 38 -+#define R_X86_64_NUM 39 -+ -+ -+ -+#define R_MN10300_NONE 0 -+#define R_MN10300_32 1 -+#define R_MN10300_16 2 -+#define R_MN10300_8 3 -+#define R_MN10300_PCREL32 4 -+#define R_MN10300_PCREL16 5 -+#define R_MN10300_PCREL8 6 -+#define R_MN10300_GNU_VTINHERIT 7 -+#define R_MN10300_GNU_VTENTRY 8 -+#define R_MN10300_24 9 -+#define R_MN10300_GOTPC32 10 -+#define R_MN10300_GOTPC16 11 -+#define R_MN10300_GOTOFF32 12 -+#define R_MN10300_GOTOFF24 13 -+#define R_MN10300_GOTOFF16 14 -+#define R_MN10300_PLT32 15 -+#define R_MN10300_PLT16 16 -+#define R_MN10300_GOT32 17 -+#define R_MN10300_GOT24 18 -+#define R_MN10300_GOT16 19 -+#define R_MN10300_COPY 20 -+#define R_MN10300_GLOB_DAT 21 -+#define R_MN10300_JMP_SLOT 22 -+#define R_MN10300_RELATIVE 23 -+ -+#define R_MN10300_NUM 24 -+ -+ -+ -+#define R_M32R_NONE 0 -+#define R_M32R_16 1 -+#define R_M32R_32 2 -+#define R_M32R_24 3 -+#define R_M32R_10_PCREL 4 -+#define R_M32R_18_PCREL 5 -+#define R_M32R_26_PCREL 6 -+#define R_M32R_HI16_ULO 7 -+#define R_M32R_HI16_SLO 8 -+#define R_M32R_LO16 9 -+#define R_M32R_SDA16 10 -+#define R_M32R_GNU_VTINHERIT 11 -+#define R_M32R_GNU_VTENTRY 12 -+ -+#define R_M32R_16_RELA 33 -+#define R_M32R_32_RELA 34 -+#define R_M32R_24_RELA 35 -+#define R_M32R_10_PCREL_RELA 36 -+#define R_M32R_18_PCREL_RELA 37 -+#define R_M32R_26_PCREL_RELA 38 -+#define R_M32R_HI16_ULO_RELA 39 -+#define R_M32R_HI16_SLO_RELA 40 -+#define R_M32R_LO16_RELA 41 -+#define R_M32R_SDA16_RELA 42 -+#define R_M32R_RELA_GNU_VTINHERIT 43 -+#define R_M32R_RELA_GNU_VTENTRY 44 -+#define R_M32R_REL32 45 -+ -+#define R_M32R_GOT24 48 -+#define R_M32R_26_PLTREL 49 -+#define R_M32R_COPY 50 -+#define R_M32R_GLOB_DAT 51 -+#define R_M32R_JMP_SLOT 52 -+#define R_M32R_RELATIVE 53 -+#define R_M32R_GOTOFF 54 -+#define R_M32R_GOTPC24 55 -+#define R_M32R_GOT16_HI_ULO 56 -+ -+#define R_M32R_GOT16_HI_SLO 57 -+ -+#define R_M32R_GOT16_LO 58 -+#define R_M32R_GOTPC_HI_ULO 59 -+ -+#define R_M32R_GOTPC_HI_SLO 60 -+ -+#define R_M32R_GOTPC_LO 61 -+ -+#define R_M32R_GOTOFF_HI_ULO 62 -+ -+#define R_M32R_GOTOFF_HI_SLO 63 -+ -+#define R_M32R_GOTOFF_LO 64 -+#define R_M32R_NUM 256 -+ -+#define R_MICROBLAZE_NONE 0 -+#define R_MICROBLAZE_32 1 -+#define R_MICROBLAZE_32_PCREL 2 -+#define R_MICROBLAZE_64_PCREL 3 -+#define R_MICROBLAZE_32_PCREL_LO 4 -+#define R_MICROBLAZE_64 5 -+#define R_MICROBLAZE_32_LO 6 -+#define R_MICROBLAZE_SRO32 7 -+#define R_MICROBLAZE_SRW32 8 -+#define R_MICROBLAZE_64_NONE 9 -+#define R_MICROBLAZE_32_SYM_OP_SYM 10 -+#define R_MICROBLAZE_GNU_VTINHERIT 11 -+#define R_MICROBLAZE_GNU_VTENTRY 12 -+#define R_MICROBLAZE_GOTPC_64 13 -+#define R_MICROBLAZE_GOT_64 14 -+#define R_MICROBLAZE_PLT_64 15 -+#define R_MICROBLAZE_REL 16 -+#define R_MICROBLAZE_JUMP_SLOT 17 -+#define R_MICROBLAZE_GLOB_DAT 18 -+#define R_MICROBLAZE_GOTOFF_64 19 -+#define R_MICROBLAZE_GOTOFF_32 20 -+#define R_MICROBLAZE_COPY 21 -+#define R_MICROBLAZE_TLS 22 -+#define R_MICROBLAZE_TLSGD 23 -+#define R_MICROBLAZE_TLSLD 24 -+#define R_MICROBLAZE_TLSDTPMOD32 25 -+#define R_MICROBLAZE_TLSDTPREL32 26 -+#define R_MICROBLAZE_TLSDTPREL64 27 -+#define R_MICROBLAZE_TLSGOTTPREL32 28 -+#define R_MICROBLAZE_TLSTPREL32 29 -+ -+#ifdef __cplusplus -+} -+#endif -+ -+ -+#endif diff --git a/target/linux/patches/3.12.40/sgidefs.patch b/target/linux/patches/3.12.40/sgidefs.patch deleted file mode 100644 index f00a284d9..000000000 --- a/target/linux/patches/3.12.40/sgidefs.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h ---- linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h 2013-11-08 22:01:28.000000000 +0100 -@@ -11,14 +11,6 @@ - #define __ASM_SGIDEFS_H - - /* -- * Using a Linux compiler for building Linux seems logic but not to -- * everybody. -- */ --#ifndef __linux__ --#error Use a Linux compiler or give up. --#endif -- --/* - * Definitions for the ISA levels - * - * With the introduction of MIPS32 / MIPS64 instruction sets definitions diff --git a/target/linux/patches/3.12.40/sortext.patch b/target/linux/patches/3.12.40/sortext.patch deleted file mode 100644 index 8fd4e1d6b..000000000 --- a/target/linux/patches/3.12.40/sortext.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -Nur linux-3.12.6.orig/arch/arm/Kconfig linux-3.12.6/arch/arm/Kconfig ---- linux-3.12.6.orig/arch/arm/Kconfig 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/arch/arm/Kconfig 2013-12-28 19:29:33.000000000 +0100 -@@ -6,7 +6,6 @@ - select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST - select ARCH_HAVE_CUSTOM_GPIO_H - select ARCH_WANT_IPC_PARSE_VERSION -- select BUILDTIME_EXTABLE_SORT if MMU - select CLONE_BACKWARDS - select CPU_PM if (SUSPEND || CPU_IDLE) - select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && !CPU_BIG_ENDIAN && MMU -diff -Nur linux-3.12.6.orig/arch/mips/Kconfig linux-3.12.6/arch/mips/Kconfig ---- linux-3.12.6.orig/arch/mips/Kconfig 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/arch/mips/Kconfig 2013-12-28 19:30:06.000000000 +0100 -@@ -35,7 +35,6 @@ - select HAVE_MEMBLOCK_NODE_MAP - select ARCH_DISCARD_MEMBLOCK - select GENERIC_SMP_IDLE_THREAD -- select BUILDTIME_EXTABLE_SORT - select GENERIC_CLOCKEVENTS - select GENERIC_CMOS_UPDATE - select HAVE_MOD_ARCH_SPECIFIC -diff -Nur linux-3.12.6.orig/arch/x86/Kconfig linux-3.12.6/arch/x86/Kconfig ---- linux-3.12.6.orig/arch/x86/Kconfig 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/arch/x86/Kconfig 2013-12-28 19:29:50.000000000 +0100 -@@ -100,7 +100,6 @@ - select GENERIC_SMP_IDLE_THREAD - select ARCH_WANT_IPC_PARSE_VERSION if X86_32 - select HAVE_ARCH_SECCOMP_FILTER -- select BUILDTIME_EXTABLE_SORT - select GENERIC_CMOS_UPDATE - select HAVE_ARCH_SOFT_DIRTY - select CLOCKSOURCE_WATCHDOG diff --git a/target/linux/patches/3.12.40/startup.patch b/target/linux/patches/3.12.40/startup.patch deleted file mode 100644 index 4cd477da1..000000000 --- a/target/linux/patches/3.12.40/startup.patch +++ /dev/null @@ -1,34 +0,0 @@ -diff -Nur linux-3.12.13.orig/init/initramfs.c linux-3.12.13/init/initramfs.c ---- linux-3.12.13.orig/init/initramfs.c 2014-02-22 22:32:50.000000000 +0100 -+++ linux-3.12.13/init/initramfs.c 2014-03-15 18:09:39.040618589 +0100 -@@ -622,6 +622,9 @@ - */ - load_default_modules(); - } -+#ifdef CONFIG_DEVTMPFS_MOUNT -+ devtmpfs_mount("dev"); -+#endif - return 0; - } - rootfs_initcall(populate_rootfs); -diff -Nur linux-3.12.13.orig/init/main.c linux-3.12.13/init/main.c ---- linux-3.12.13.orig/init/main.c 2014-02-22 22:32:50.000000000 +0100 -+++ linux-3.12.13/init/main.c 2014-03-15 18:09:39.044618600 +0100 -@@ -888,6 +888,8 @@ - if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - pr_err("Warning: unable to open an initial console.\n"); - -+ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); -+ - (void) sys_dup(0); - (void) sys_dup(0); - /* -@@ -896,7 +898,7 @@ - */ - - if (!ramdisk_execute_command) -- ramdisk_execute_command = "/init"; -+ ramdisk_execute_command = "/sbin/init"; - - if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { - ramdisk_execute_command = NULL; diff --git a/target/linux/patches/3.12.40/usb-defaults-off.patch b/target/linux/patches/3.12.40/usb-defaults-off.patch deleted file mode 100644 index 54dff2bd4..000000000 --- a/target/linux/patches/3.12.40/usb-defaults-off.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff -Nur linux-3.5.orig//drivers/usb/host/Kconfig linux-3.5/drivers/usb/host/Kconfig ---- linux-3.5.orig//drivers/usb/host/Kconfig 2012-07-21 22:58:29.000000000 +0200 -+++ linux-3.5/drivers/usb/host/Kconfig 2012-07-29 11:59:56.000000000 +0200 -@@ -66,6 +66,7 @@ - config USB_EHCI_ROOT_HUB_TT - bool "Root Hub Transaction Translators" - depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST -+ default n - ---help--- - Some EHCI chips have vendor-specific extensions to integrate - transaction translators, so that no OHCI or UHCI companion -@@ -78,7 +79,7 @@ - config USB_EHCI_TT_NEWSCHED - bool "Improved Transaction Translator scheduling" - depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST -- default y -+ default n - ---help--- - This changes the periodic scheduling code to fill more of the low - and full speed bandwidth available from the Transaction Translator diff --git a/target/linux/patches/3.12.40/vga-cons-default-off.patch b/target/linux/patches/3.12.40/vga-cons-default-off.patch deleted file mode 100644 index 08a57f783..000000000 --- a/target/linux/patches/3.12.40/vga-cons-default-off.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-3.11.5.orig/drivers/video/console/Kconfig linux-3.11.5/drivers/video/console/Kconfig ---- linux-3.11.5.orig/drivers/video/console/Kconfig 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/drivers/video/console/Kconfig 2013-10-16 18:04:45.000000000 +0200 -@@ -9,7 +9,7 @@ - depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && \ - !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && !CRIS && \ - (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) -- default y -+ default n - help - Saying Y here will allow you to use Linux in text mode through a - display that complies with the generic VGA standard. Virtually diff --git a/target/linux/patches/3.12.40/wlan-cf.patch b/target/linux/patches/3.12.40/wlan-cf.patch deleted file mode 100644 index fc20759e2..000000000 --- a/target/linux/patches/3.12.40/wlan-cf.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c ---- linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c 2011-05-19 06:06:34.000000000 +0200 -+++ linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c 2011-09-12 02:46:26.987984145 +0200 -@@ -623,6 +623,7 @@ - static struct pcmcia_device_id hostap_cs_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), -+ PCMCIA_DEVICE_MANF_CARD(0x0004, 0x2003), - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), - PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), - PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), diff --git a/target/linux/patches/3.12.40/xargs.patch b/target/linux/patches/3.12.40/xargs.patch deleted file mode 100644 index 2c7b3df59..000000000 --- a/target/linux/patches/3.12.40/xargs.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-3.12.6.orig/scripts/Makefile.modpost linux-3.12.6/scripts/Makefile.modpost ---- linux-3.12.6.orig/scripts/Makefile.modpost 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/scripts/Makefile.modpost 2014-01-25 14:55:33.000000000 +0100 -@@ -60,7 +60,7 @@ - modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers - - # Step 1), find all modules listed in $(MODVERDIR)/ --MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs -r grep -h '\.ko$$' | sort -u -+MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs grep -h '\.ko$$' | sort -u - __modules := $(shell $(MODLISTCMD)) - modules := $(patsubst %.o,%.ko, $(wildcard $(__modules:.ko=.o))) - diff --git a/target/linux/patches/3.12.40/yaffs2.patch b/target/linux/patches/3.12.40/yaffs2.patch deleted file mode 100644 index 306814439..000000000 --- a/target/linux/patches/3.12.40/yaffs2.patch +++ /dev/null @@ -1,16547 +0,0 @@ -diff -Nur linux-3.12.20.orig/fs/Kconfig linux-3.12.20/fs/Kconfig ---- linux-3.12.20.orig/fs/Kconfig 2014-05-15 09:56:19.000000000 +0200 -+++ linux-3.12.20/fs/Kconfig 2014-05-17 11:28:49.000000000 +0200 -@@ -193,6 +193,7 @@ - source "fs/befs/Kconfig" - source "fs/bfs/Kconfig" - source "fs/efs/Kconfig" -+source "fs/yaffs2/Kconfig" - source "fs/jffs2/Kconfig" - # UBIFS File system configuration - source "fs/ubifs/Kconfig" -diff -Nur linux-3.12.20.orig/fs/Makefile linux-3.12.20/fs/Makefile ---- linux-3.12.20.orig/fs/Makefile 2014-05-15 09:56:19.000000000 +0200 -+++ linux-3.12.20/fs/Makefile 2014-05-17 11:28:49.000000000 +0200 -@@ -126,3 +126,4 @@ - obj-$(CONFIG_CEPH_FS) += ceph/ - obj-$(CONFIG_PSTORE) += pstore/ - obj-$(CONFIG_EFIVAR_FS) += efivarfs/ -+obj-$(CONFIG_YAFFS_FS) += yaffs2/ -diff -Nur linux-3.12.20.orig/fs/yaffs2/Kconfig linux-3.12.20/fs/yaffs2/Kconfig ---- linux-3.12.20.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/Kconfig 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,171 @@ -+# -+# yaffs file system configurations -+# -+ -+config YAFFS_FS -+ tristate "yaffs2 file system support" -+ default n -+ depends on MTD_BLOCK -+ select YAFFS_YAFFS1 -+ select YAFFS_YAFFS2 -+ help -+ yaffs2, or Yet Another Flash File System, is a file system -+ optimised for NAND Flash chips. -+ -+ To compile the yaffs2 file system support as a module, choose M -+ here: the module will be called yaffs2. -+ -+ If unsure, say N. -+ -+ Further information on yaffs2 is available at -+ . -+ -+config YAFFS_YAFFS1 -+ bool "512 byte / page devices" -+ depends on YAFFS_FS -+ default y -+ help -+ Enable yaffs1 support -- yaffs for 512 byte / page devices -+ -+ Not needed for 2K-page devices. -+ -+ If unsure, say Y. -+ -+config YAFFS_9BYTE_TAGS -+ bool "Use older-style on-NAND data format with pageStatus byte" -+ depends on YAFFS_YAFFS1 -+ default n -+ help -+ -+ Older-style on-NAND data format has a "pageStatus" byte to record -+ chunk/page state. This byte is zero when the page is discarded. -+ Choose this option if you have existing on-NAND data using this -+ format that you need to continue to support. New data written -+ also uses the older-style format. Note: Use of this option -+ generally requires that MTD's oob layout be adjusted to use the -+ older-style format. See notes on tags formats and MTD versions -+ in yaffs_mtdif1.c. -+ -+ If unsure, say N. -+ -+config YAFFS_DOES_ECC -+ bool "Lets yaffs do its own ECC" -+ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS -+ default n -+ help -+ This enables yaffs to use its own ECC functions instead of using -+ the ones from the generic MTD-NAND driver. -+ -+ If unsure, say N. -+ -+config YAFFS_ECC_WRONG_ORDER -+ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" -+ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS -+ default n -+ help -+ This makes yaffs_ecc.c use the same ecc byte order as Steven -+ Hill's nand_ecc.c. If not set, then you get the same ecc byte -+ order as SmartMedia. -+ -+ If unsure, say N. -+ -+config YAFFS_YAFFS2 -+ bool "2048 byte (or larger) / page devices" -+ depends on YAFFS_FS -+ default y -+ help -+ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices -+ -+ If unsure, say Y. -+ -+config YAFFS_AUTO_YAFFS2 -+ bool "Autoselect yaffs2 format" -+ depends on YAFFS_YAFFS2 -+ default y -+ help -+ Without this, you need to explicitely use yaffs2 as the file -+ system type. With this, you can say "yaffs" and yaffs or yaffs2 -+ will be used depending on the device page size (yaffs on -+ 512-byte page devices, yaffs2 on 2K page devices). -+ -+ If unsure, say Y. -+ -+config YAFFS_DISABLE_TAGS_ECC -+ bool "Disable yaffs from doing ECC on tags by default" -+ depends on YAFFS_FS && YAFFS_YAFFS2 -+ default n -+ help -+ This defaults yaffs to using its own ECC calculations on tags instead of -+ just relying on the MTD. -+ This behavior can also be overridden with tags_ecc_on and -+ tags_ecc_off mount options. -+ -+ If unsure, say N. -+ -+config YAFFS_ALWAYS_CHECK_CHUNK_ERASED -+ bool "Force chunk erase check" -+ depends on YAFFS_FS -+ default n -+ help -+ Normally yaffs only checks chunks before writing until an erased -+ chunk is found. This helps to detect any partially written -+ chunks that might have happened due to power loss. -+ -+ Enabling this forces on the test that chunks are erased in flash -+ before writing to them. This takes more time but is potentially -+ a bit more secure. -+ -+ Suggest setting Y during development and ironing out driver -+ issues etc. Suggest setting to N if you want faster writing. -+ -+ If unsure, say Y. -+ -+config YAFFS_EMPTY_LOST_AND_FOUND -+ bool "Empty lost and found on boot" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is enabled then the contents of lost and found is -+ automatically dumped at mount. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BLOCK_REFRESHING -+ bool "Disable yaffs2 block refreshing" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is set, then block refreshing is disabled. -+ Block refreshing infrequently refreshes the oldest block in -+ a yaffs2 file system. This mechanism helps to refresh flash to -+ mitigate against data loss. This is particularly useful for MLC. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BACKGROUND -+ bool "Disable yaffs2 background processing" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is set, then background processing is disabled. -+ Background processing makes many foreground activities faster. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BAD_BLOCK_MARKING -+ bool "Disable yaffs2 bad block marking" -+ depends on YAFFS_FS -+ default n -+ help -+ Useful during early flash bring up to prevent problems causing -+ lots of bad block marking. -+ -+ If unsure, say N. -+ -+config YAFFS_XATTR -+ bool "Enable yaffs2 xattr support" -+ depends on YAFFS_FS -+ default y -+ help -+ If this is set then yaffs2 will provide xattr support. -+ If unsure, say Y. -diff -Nur linux-3.12.20.orig/fs/yaffs2/Makefile linux-3.12.20/fs/yaffs2/Makefile ---- linux-3.12.20.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/Makefile 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,18 @@ -+# -+# Makefile for the linux YAFFS filesystem routines. -+# -+ -+obj-$(CONFIG_YAFFS_FS) += yaffs.o -+ -+yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o -+yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o -+yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o -+yaffs-y += yaffs_mtdif.o -+yaffs-y += yaffs_nameval.o yaffs_attribs.o -+yaffs-y += yaffs_allocator.o -+yaffs-y += yaffs_yaffs1.o -+yaffs-y += yaffs_yaffs2.o -+yaffs-y += yaffs_bitmap.o -+yaffs-y += yaffs_summary.o -+yaffs-y += yaffs_verify.o -+ -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_allocator.c linux-3.12.20/fs/yaffs2/yaffs_allocator.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_allocator.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,357 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_allocator.h" -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yportenv.h" -+ -+/* -+ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks -+ * of approx 100 objects that are themn allocated singly. -+ * This is basically a simplified slab allocator. -+ * -+ * We don't use the Linux slab allocator because slab does not allow -+ * us to dump all the objects in one hit when we do a umount and tear -+ * down all the tnodes and objects. slab requires that we first free -+ * the individual objects. -+ * -+ * Once yaffs has been mainlined I shall try to motivate for a change -+ * to slab to provide the extra features we need here. -+ */ -+ -+struct yaffs_tnode_list { -+ struct yaffs_tnode_list *next; -+ struct yaffs_tnode *tnodes; -+}; -+ -+struct yaffs_obj_list { -+ struct yaffs_obj_list *next; -+ struct yaffs_obj *objects; -+}; -+ -+struct yaffs_allocator { -+ int n_tnodes_created; -+ struct yaffs_tnode *free_tnodes; -+ int n_free_tnodes; -+ struct yaffs_tnode_list *alloc_tnode_list; -+ -+ int n_obj_created; -+ struct list_head free_objs; -+ int n_free_objects; -+ -+ struct yaffs_obj_list *allocated_obj_list; -+}; -+ -+static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ struct yaffs_tnode_list *tmp; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ while (allocator->alloc_tnode_list) { -+ tmp = allocator->alloc_tnode_list->next; -+ -+ kfree(allocator->alloc_tnode_list->tnodes); -+ kfree(allocator->alloc_tnode_list); -+ allocator->alloc_tnode_list = tmp; -+ } -+ -+ allocator->free_tnodes = NULL; -+ allocator->n_free_tnodes = 0; -+ allocator->n_tnodes_created = 0; -+} -+ -+static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator->alloc_tnode_list = NULL; -+ allocator->free_tnodes = NULL; -+ allocator->n_free_tnodes = 0; -+ allocator->n_tnodes_created = 0; -+} -+ -+static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ int i; -+ struct yaffs_tnode *new_tnodes; -+ u8 *mem; -+ struct yaffs_tnode *curr; -+ struct yaffs_tnode *next; -+ struct yaffs_tnode_list *tnl; -+ -+ if (!allocator) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ if (n_tnodes < 1) -+ return YAFFS_OK; -+ -+ /* make these things */ -+ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); -+ mem = (u8 *) new_tnodes; -+ -+ if (!new_tnodes) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs: Could not allocate Tnodes"); -+ return YAFFS_FAIL; -+ } -+ -+ /* New hookup for wide tnodes */ -+ for (i = 0; i < n_tnodes - 1; i++) { -+ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; -+ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; -+ curr->internal[0] = next; -+ } -+ -+ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; -+ curr->internal[0] = allocator->free_tnodes; -+ allocator->free_tnodes = (struct yaffs_tnode *)mem; -+ -+ allocator->n_free_tnodes += n_tnodes; -+ allocator->n_tnodes_created += n_tnodes; -+ -+ /* Now add this bunch of tnodes to a list for freeing up. -+ * NB If we can't add this to the management list it isn't fatal -+ * but it just means we can't free this bunch of tnodes later. -+ */ -+ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); -+ if (!tnl) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Could not add tnodes to management list"); -+ return YAFFS_FAIL; -+ } else { -+ tnl->tnodes = new_tnodes; -+ tnl->next = allocator->alloc_tnode_list; -+ allocator->alloc_tnode_list = tnl; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); -+ -+ return YAFFS_OK; -+} -+ -+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ struct yaffs_tnode *tn = NULL; -+ -+ if (!allocator) { -+ BUG(); -+ return NULL; -+ } -+ -+ /* If there are none left make more */ -+ if (!allocator->free_tnodes) -+ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); -+ -+ if (allocator->free_tnodes) { -+ tn = allocator->free_tnodes; -+ allocator->free_tnodes = allocator->free_tnodes->internal[0]; -+ allocator->n_free_tnodes--; -+ } -+ -+ return tn; -+} -+ -+/* FreeTnode frees up a tnode and puts it back on the free list */ -+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ if (tn) { -+ tn->internal[0] = allocator->free_tnodes; -+ allocator->free_tnodes = tn; -+ allocator->n_free_tnodes++; -+ } -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+/*--------------- yaffs_obj alloaction ------------------------ -+ * -+ * Free yaffs_objs are stored in a list using obj->siblings. -+ * The blocks of allocated objects are stored in a linked list. -+ */ -+ -+static void yaffs_init_raw_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator->allocated_obj_list = NULL; -+ INIT_LIST_HEAD(&allocator->free_objs); -+ allocator->n_free_objects = 0; -+} -+ -+static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ struct yaffs_obj_list *tmp; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ while (allocator->allocated_obj_list) { -+ tmp = allocator->allocated_obj_list->next; -+ kfree(allocator->allocated_obj_list->objects); -+ kfree(allocator->allocated_obj_list); -+ allocator->allocated_obj_list = tmp; -+ } -+ -+ INIT_LIST_HEAD(&allocator->free_objs); -+ allocator->n_free_objects = 0; -+ allocator->n_obj_created = 0; -+} -+ -+static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ int i; -+ struct yaffs_obj *new_objs; -+ struct yaffs_obj_list *list; -+ -+ if (!allocator) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ if (n_obj < 1) -+ return YAFFS_OK; -+ -+ /* make these things */ -+ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); -+ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); -+ -+ if (!new_objs || !list) { -+ kfree(new_objs); -+ new_objs = NULL; -+ kfree(list); -+ list = NULL; -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, -+ "Could not allocate more objects"); -+ return YAFFS_FAIL; -+ } -+ -+ /* Hook them into the free list */ -+ for (i = 0; i < n_obj; i++) -+ list_add(&new_objs[i].siblings, &allocator->free_objs); -+ -+ allocator->n_free_objects += n_obj; -+ allocator->n_obj_created += n_obj; -+ -+ /* Now add this bunch of Objects to a list for freeing up. */ -+ -+ list->objects = new_objs; -+ list->next = allocator->allocated_obj_list; -+ allocator->allocated_obj_list = list; -+ -+ return YAFFS_OK; -+} -+ -+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj = NULL; -+ struct list_head *lh; -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return obj; -+ } -+ -+ /* If there are none left make more */ -+ if (list_empty(&allocator->free_objs)) -+ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); -+ -+ if (!list_empty(&allocator->free_objs)) { -+ lh = allocator->free_objs.next; -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ list_del_init(lh); -+ allocator->n_free_objects--; -+ } -+ -+ return obj; -+} -+ -+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) -+{ -+ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ /* Link into the free list. */ -+ list_add(&obj->siblings, &allocator->free_objs); -+ allocator->n_free_objects++; -+} -+ -+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ -+ if (!dev->allocator) { -+ BUG(); -+ return; -+ } -+ -+ yaffs_deinit_raw_tnodes(dev); -+ yaffs_deinit_raw_objs(dev); -+ kfree(dev->allocator); -+ dev->allocator = NULL; -+} -+ -+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator; -+ -+ if (dev->allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); -+ if (allocator) { -+ dev->allocator = allocator; -+ yaffs_init_raw_tnodes(dev); -+ yaffs_init_raw_objs(dev); -+ } -+} -+ -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_allocator.h linux-3.12.20/fs/yaffs2/yaffs_allocator.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_allocator.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,30 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_ALLOCATOR_H__ -+#define __YAFFS_ALLOCATOR_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); -+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); -+ -+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); -+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); -+ -+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); -+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_attribs.c linux-3.12.20/fs/yaffs2/yaffs_attribs.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_attribs.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,166 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_attribs.h" -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+static inline uid_t ia_uid_read(const struct iattr *iattr) -+{ -+ return from_kuid(&init_user_ns, iattr->ia_uid); -+} -+ -+static inline gid_t ia_gid_read(const struct iattr *iattr) -+{ -+ return from_kgid(&init_user_ns, iattr->ia_gid); -+} -+ -+static inline void ia_uid_write(struct iattr *iattr, uid_t uid) -+{ -+ iattr->ia_uid = make_kuid(&init_user_ns, uid); -+} -+ -+static inline void ia_gid_write(struct iattr *iattr, gid_t gid) -+{ -+ iattr->ia_gid = make_kgid(&init_user_ns, gid); -+} -+#else -+static inline uid_t ia_uid_read(const struct iattr *iattr) -+{ -+ return iattr->ia_uid; -+} -+ -+static inline gid_t ia_gid_read(const struct iattr *inode) -+{ -+ return iattr->ia_gid; -+} -+ -+static inline void ia_uid_write(struct iattr *iattr, uid_t uid) -+{ -+ iattr->ia_uid = uid; -+} -+ -+static inline void ia_gid_write(struct iattr *iattr, gid_t gid) -+{ -+ iattr->ia_gid = gid; -+} -+#endif -+ -+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) -+{ -+ obj->yst_uid = oh->yst_uid; -+ obj->yst_gid = oh->yst_gid; -+ obj->yst_atime = oh->yst_atime; -+ obj->yst_mtime = oh->yst_mtime; -+ obj->yst_ctime = oh->yst_ctime; -+ obj->yst_rdev = oh->yst_rdev; -+} -+ -+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) -+{ -+ oh->yst_uid = obj->yst_uid; -+ oh->yst_gid = obj->yst_gid; -+ oh->yst_atime = obj->yst_atime; -+ oh->yst_mtime = obj->yst_mtime; -+ oh->yst_ctime = obj->yst_ctime; -+ oh->yst_rdev = obj->yst_rdev; -+ -+} -+ -+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) -+{ -+ obj->yst_mtime = Y_CURRENT_TIME; -+ if (do_a) -+ obj->yst_atime = obj->yst_mtime; -+ if (do_c) -+ obj->yst_ctime = obj->yst_mtime; -+} -+ -+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) -+{ -+ yaffs_load_current_time(obj, 1, 1); -+ obj->yst_rdev = rdev; -+ obj->yst_uid = uid; -+ obj->yst_gid = gid; -+} -+ -+static loff_t yaffs_get_file_size(struct yaffs_obj *obj) -+{ -+ YCHAR *alias = NULL; -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return obj->variant.file_variant.file_size; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ alias = obj->variant.symlink_variant.alias; -+ if (!alias) -+ return 0; -+ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); -+ default: -+ return 0; -+ } -+} -+ -+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) -+{ -+ unsigned int valid = attr->ia_valid; -+ -+ if (valid & ATTR_MODE) -+ obj->yst_mode = attr->ia_mode; -+ if (valid & ATTR_UID) -+ obj->yst_uid = ia_uid_read(attr); -+ if (valid & ATTR_GID) -+ obj->yst_gid = ia_gid_read(attr); -+ -+ if (valid & ATTR_ATIME) -+ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); -+ if (valid & ATTR_CTIME) -+ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); -+ if (valid & ATTR_MTIME) -+ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); -+ -+ if (valid & ATTR_SIZE) -+ yaffs_resize_file(obj, attr->ia_size); -+ -+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); -+ -+ return YAFFS_OK; -+ -+} -+ -+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) -+{ -+ unsigned int valid = 0; -+ -+ attr->ia_mode = obj->yst_mode; -+ valid |= ATTR_MODE; -+ ia_uid_write(attr, obj->yst_uid); -+ valid |= ATTR_UID; -+ ia_gid_write(attr, obj->yst_gid); -+ valid |= ATTR_GID; -+ -+ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; -+ valid |= ATTR_ATIME; -+ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; -+ valid |= ATTR_CTIME; -+ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; -+ valid |= ATTR_MTIME; -+ -+ attr->ia_size = yaffs_get_file_size(obj); -+ valid |= ATTR_SIZE; -+ -+ attr->ia_valid = valid; -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_attribs.h linux-3.12.20/fs/yaffs2/yaffs_attribs.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_attribs.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,28 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_ATTRIBS_H__ -+#define __YAFFS_ATTRIBS_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); -+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); -+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); -+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); -+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); -+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_bitmap.c linux-3.12.20/fs/yaffs2/yaffs_bitmap.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_bitmap.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,97 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_bitmap.h" -+#include "yaffs_trace.h" -+/* -+ * Chunk bitmap manipulations -+ */ -+ -+static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "BlockBits block %d is not valid", -+ blk); -+ BUG(); -+ } -+ return dev->chunk_bits + -+ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); -+} -+ -+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block || -+ chunk < 0 || chunk >= dev->param.chunks_per_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Chunk Id (%d:%d) invalid", -+ blk, chunk); -+ BUG(); -+ } -+} -+ -+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ memset(blk_bits, 0, dev->chunk_bit_stride); -+} -+ -+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); -+} -+ -+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ blk_bits[chunk / 8] |= (1 << (chunk & 7)); -+} -+ -+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; -+} -+ -+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ int i; -+ -+ for (i = 0; i < dev->chunk_bit_stride; i++) { -+ if (*blk_bits) -+ return 1; -+ blk_bits++; -+ } -+ return 0; -+} -+ -+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ int i; -+ int n = 0; -+ -+ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) -+ n += hweight8(*blk_bits); -+ -+ return n; -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_bitmap.h linux-3.12.20/fs/yaffs2/yaffs_bitmap.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_bitmap.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,33 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* -+ * Chunk bitmap manipulations -+ */ -+ -+#ifndef __YAFFS_BITMAP_H__ -+#define __YAFFS_BITMAP_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); -+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); -+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); -+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.12.20/fs/yaffs2/yaffs_checkptrw.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,474 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_checkptrw.h" -+#include "yaffs_getblockinfo.h" -+ -+struct yaffs_checkpt_chunk_hdr { -+ int version; -+ int seq; -+ u32 sum; -+ u32 xor; -+} ; -+ -+ -+static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) -+{ -+ return chunk - dev->chunk_offset; -+} -+ -+static int apply_block_offset(struct yaffs_dev *dev, int block) -+{ -+ return block - dev->block_offset; -+} -+ -+static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_chunk_hdr hdr; -+ -+ hdr.version = YAFFS_CHECKPOINT_VERSION; -+ hdr.seq = dev->checkpt_page_seq; -+ hdr.sum = dev->checkpt_sum; -+ hdr.xor = dev->checkpt_xor; -+ -+ dev->checkpt_byte_offs = sizeof(hdr); -+ -+ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); -+} -+ -+static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_chunk_hdr hdr; -+ -+ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); -+ -+ dev->checkpt_byte_offs = sizeof(hdr); -+ -+ return hdr.version == YAFFS_CHECKPOINT_VERSION && -+ hdr.seq == dev->checkpt_page_seq && -+ hdr.sum == dev->checkpt_sum && -+ hdr.xor == dev->checkpt_xor; -+} -+ -+static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) -+{ -+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpt blocks_avail = %d", blocks_avail); -+ -+ return (blocks_avail <= 0) ? 0 : 1; -+} -+ -+static int yaffs_checkpt_erase(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (!dev->drv.drv_erase_fn) -+ return 0; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checking blocks %d to %d", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); -+ int offset_i = apply_block_offset(dev, i); -+ int result; -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "erasing checkpt block %d", i); -+ -+ dev->n_erasures++; -+ -+ result = dev->drv.drv_erase_fn(dev, offset_i); -+ if(result) { -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += -+ dev->param.chunks_per_block; -+ } else { -+ dev->drv.drv_mark_bad_fn(dev, offset_i); -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ } -+ } -+ } -+ -+ dev->blocks_in_checkpt = 0; -+ -+ return 1; -+} -+ -+static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) -+{ -+ int i; -+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "allocating checkpt block: erased %d reserved %d avail %d next %d ", -+ dev->n_erased_blocks, dev->param.n_reserved_blocks, -+ blocks_avail, dev->checkpt_next_block); -+ -+ if (dev->checkpt_next_block >= 0 && -+ dev->checkpt_next_block <= dev->internal_end_block && -+ blocks_avail > 0) { -+ -+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; -+ i++) { -+ struct yaffs_block_info *bi; -+ -+ bi = yaffs_get_block_info(dev, i); -+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ dev->checkpt_next_block = i + 1; -+ dev->checkpt_cur_block = i; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "allocating checkpt block %d", i); -+ return; -+ } -+ } -+ } -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); -+ -+ dev->checkpt_next_block = -1; -+ dev->checkpt_cur_block = -1; -+} -+ -+static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) -+{ -+ int i; -+ struct yaffs_ext_tags tags; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "find next checkpt block: start: blocks %d next %d", -+ dev->blocks_in_checkpt, dev->checkpt_next_block); -+ -+ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) -+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; -+ i++) { -+ int chunk = i * dev->param.chunks_per_block; -+ enum yaffs_block_state state; -+ u32 seq; -+ -+ dev->tagger.read_chunk_tags_fn(dev, -+ apply_chunk_offset(dev, chunk), -+ NULL, &tags); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", -+ i, (int) state, -+ tags.obj_id, tags.seq_number, -+ tags.ecc_result); -+ -+ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ continue; -+ -+ dev->tagger.query_block_fn(dev, -+ apply_block_offset(dev, i), -+ &state, &seq); -+ if (state == YAFFS_BLOCK_STATE_DEAD) -+ continue; -+ -+ /* Right kind of block */ -+ dev->checkpt_next_block = tags.obj_id; -+ dev->checkpt_cur_block = i; -+ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; -+ dev->blocks_in_checkpt++; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "found checkpt block %d", i); -+ return; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); -+ -+ dev->checkpt_next_block = -1; -+ dev->checkpt_cur_block = -1; -+} -+ -+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) -+{ -+ int i; -+ -+ dev->checkpt_open_write = writing; -+ -+ /* Got the functions we need? */ -+ if (!dev->tagger.write_chunk_tags_fn || -+ !dev->tagger.read_chunk_tags_fn || -+ !dev->drv.drv_erase_fn || -+ !dev->drv.drv_mark_bad_fn) -+ return 0; -+ -+ if (writing && !yaffs2_checkpt_space_ok(dev)) -+ return 0; -+ -+ if (!dev->checkpt_buffer) -+ dev->checkpt_buffer = -+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ dev->checkpt_page_seq = 0; -+ dev->checkpt_byte_count = 0; -+ dev->checkpt_sum = 0; -+ dev->checkpt_xor = 0; -+ dev->checkpt_cur_block = -1; -+ dev->checkpt_cur_chunk = -1; -+ dev->checkpt_next_block = dev->internal_start_block; -+ -+ if (writing) { -+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); -+ yaffs2_checkpt_init_chunk_hdr(dev); -+ return yaffs_checkpt_erase(dev); -+ } -+ -+ /* Opening for a read */ -+ /* Set to a value that will kick off a read */ -+ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; -+ /* A checkpoint block list of 1 checkpoint block per 16 block is -+ * (hopefully) going to be way more than we need */ -+ dev->blocks_in_checkpt = 0; -+ dev->checkpt_max_blocks = -+ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; -+ dev->checkpt_block_list = -+ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); -+ -+ if (!dev->checkpt_block_list) -+ return 0; -+ -+ for (i = 0; i < dev->checkpt_max_blocks; i++) -+ dev->checkpt_block_list[i] = -1; -+ -+ return 1; -+} -+ -+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) -+{ -+ u32 composite_sum; -+ -+ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); -+ *sum = composite_sum; -+ return 1; -+} -+ -+static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) -+{ -+ int chunk; -+ int offset_chunk; -+ struct yaffs_ext_tags tags; -+ -+ if (dev->checkpt_cur_block < 0) { -+ yaffs2_checkpt_find_erased_block(dev); -+ dev->checkpt_cur_chunk = 0; -+ } -+ -+ if (dev->checkpt_cur_block < 0) -+ return 0; -+ -+ tags.is_deleted = 0; -+ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ -+ tags.chunk_id = dev->checkpt_page_seq + 1; -+ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; -+ tags.n_bytes = dev->data_bytes_per_chunk; -+ if (dev->checkpt_cur_chunk == 0) { -+ /* First chunk we write for the block? Set block state to -+ checkpoint */ -+ struct yaffs_block_info *bi = -+ yaffs_get_block_info(dev, dev->checkpt_cur_block); -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ dev->blocks_in_checkpt++; -+ } -+ -+ chunk = -+ dev->checkpt_cur_block * dev->param.chunks_per_block + -+ dev->checkpt_cur_chunk; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", -+ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, -+ tags.obj_id, tags.chunk_id); -+ -+ offset_chunk = apply_chunk_offset(dev, chunk); -+ -+ dev->n_page_writes++; -+ -+ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, -+ dev->checkpt_buffer, &tags); -+ dev->checkpt_page_seq++; -+ dev->checkpt_cur_chunk++; -+ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { -+ dev->checkpt_cur_chunk = 0; -+ dev->checkpt_cur_block = -1; -+ } -+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); -+ -+ yaffs2_checkpt_init_chunk_hdr(dev); -+ -+ -+ return 1; -+} -+ -+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) -+{ -+ int i = 0; -+ int ok = 1; -+ u8 *data_bytes = (u8 *) data; -+ -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ if (!dev->checkpt_open_write) -+ return -1; -+ -+ while (i < n_bytes && ok) { -+ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; -+ dev->checkpt_sum += *data_bytes; -+ dev->checkpt_xor ^= *data_bytes; -+ -+ dev->checkpt_byte_offs++; -+ i++; -+ data_bytes++; -+ dev->checkpt_byte_count++; -+ -+ if (dev->checkpt_byte_offs < 0 || -+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) -+ ok = yaffs2_checkpt_flush_buffer(dev); -+ } -+ -+ return i; -+} -+ -+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) -+{ -+ int i = 0; -+ int ok = 1; -+ struct yaffs_ext_tags tags; -+ int chunk; -+ int offset_chunk; -+ u8 *data_bytes = (u8 *) data; -+ -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ if (dev->checkpt_open_write) -+ return -1; -+ -+ while (i < n_bytes && ok) { -+ -+ if (dev->checkpt_byte_offs < 0 || -+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { -+ -+ if (dev->checkpt_cur_block < 0) { -+ yaffs2_checkpt_find_block(dev); -+ dev->checkpt_cur_chunk = 0; -+ } -+ -+ if (dev->checkpt_cur_block < 0) { -+ ok = 0; -+ break; -+ } -+ -+ chunk = dev->checkpt_cur_block * -+ dev->param.chunks_per_block + -+ dev->checkpt_cur_chunk; -+ -+ offset_chunk = apply_chunk_offset(dev, chunk); -+ dev->n_page_reads++; -+ -+ /* read in the next chunk */ -+ dev->tagger.read_chunk_tags_fn(dev, -+ offset_chunk, -+ dev->checkpt_buffer, -+ &tags); -+ -+ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || -+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || -+ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) { -+ ok = 0; -+ break; -+ } -+ if(!yaffs2_checkpt_check_chunk_hdr(dev)) { -+ ok = 0; -+ break; -+ } -+ -+ dev->checkpt_page_seq++; -+ dev->checkpt_cur_chunk++; -+ -+ if (dev->checkpt_cur_chunk >= -+ dev->param.chunks_per_block) -+ dev->checkpt_cur_block = -1; -+ -+ } -+ -+ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; -+ dev->checkpt_sum += *data_bytes; -+ dev->checkpt_xor ^= *data_bytes; -+ dev->checkpt_byte_offs++; -+ i++; -+ data_bytes++; -+ dev->checkpt_byte_count++; -+ } -+ -+ return i; -+} -+ -+int yaffs_checkpt_close(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (dev->checkpt_open_write) { -+ if (dev->checkpt_byte_offs != -+ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) -+ yaffs2_checkpt_flush_buffer(dev); -+ } else if (dev->checkpt_block_list) { -+ for (i = 0; -+ i < dev->blocks_in_checkpt && -+ dev->checkpt_block_list[i] >= 0; i++) { -+ int blk = dev->checkpt_block_list[i]; -+ struct yaffs_block_info *bi = NULL; -+ -+ if (dev->internal_start_block <= blk && -+ blk <= dev->internal_end_block) -+ bi = yaffs_get_block_info(dev, blk); -+ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ } -+ kfree(dev->checkpt_block_list); -+ dev->checkpt_block_list = NULL; -+ } -+ -+ dev->n_free_chunks -= -+ dev->blocks_in_checkpt * dev->param.chunks_per_block; -+ dev->n_erased_blocks -= dev->blocks_in_checkpt; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", -+ dev->checkpt_byte_count); -+ -+ if (dev->checkpt_buffer) { -+ /* free the buffer */ -+ kfree(dev->checkpt_buffer); -+ dev->checkpt_buffer = NULL; -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ -+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) -+{ -+ /* Erase the checkpoint data */ -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpoint invalidate of %d blocks", -+ dev->blocks_in_checkpt); -+ -+ return yaffs_checkpt_erase(dev); -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.12.20/fs/yaffs2/yaffs_checkptrw.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,33 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_CHECKPTRW_H__ -+#define __YAFFS_CHECKPTRW_H__ -+ -+#include "yaffs_guts.h" -+ -+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); -+ -+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); -+ -+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); -+ -+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); -+ -+int yaffs_checkpt_close(struct yaffs_dev *dev); -+ -+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_ecc.c linux-3.12.20/fs/yaffs2/yaffs_ecc.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_ecc.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,281 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * This code implements the ECC algorithm used in SmartMedia. -+ * -+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. -+ * The two unused bit are set to 1. -+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two -+ * such ECC blocks are used on a 512-byte NAND page. -+ * -+ */ -+ -+#include "yportenv.h" -+ -+#include "yaffs_ecc.h" -+ -+/* Table generated by gen-ecc.c -+ * Using a table means we do not have to calculate p1..p4 and p1'..p4' -+ * for each byte of data. These are instead provided in a table in bits7..2. -+ * Bit 0 of each entry indicates whether the entry has an odd or even parity, -+ * and therefore this bytes influence on the line parity. -+ */ -+ -+static const unsigned char column_parity_table[] = { -+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, -+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, -+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, -+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, -+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, -+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, -+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, -+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, -+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, -+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, -+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, -+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, -+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, -+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, -+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, -+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, -+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, -+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, -+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, -+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, -+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, -+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, -+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, -+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, -+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, -+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, -+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, -+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, -+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, -+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, -+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, -+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, -+}; -+ -+ -+/* Calculate the ECC for a 256-byte block of data */ -+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) -+{ -+ unsigned int i; -+ unsigned char col_parity = 0; -+ unsigned char line_parity = 0; -+ unsigned char line_parity_prime = 0; -+ unsigned char t; -+ unsigned char b; -+ -+ for (i = 0; i < 256; i++) { -+ b = column_parity_table[*data++]; -+ col_parity ^= b; -+ -+ if (b & 0x01) { /* odd number of bits in the byte */ -+ line_parity ^= i; -+ line_parity_prime ^= ~i; -+ } -+ } -+ -+ ecc[2] = (~col_parity) | 0x03; -+ -+ t = 0; -+ if (line_parity & 0x80) -+ t |= 0x80; -+ if (line_parity_prime & 0x80) -+ t |= 0x40; -+ if (line_parity & 0x40) -+ t |= 0x20; -+ if (line_parity_prime & 0x40) -+ t |= 0x10; -+ if (line_parity & 0x20) -+ t |= 0x08; -+ if (line_parity_prime & 0x20) -+ t |= 0x04; -+ if (line_parity & 0x10) -+ t |= 0x02; -+ if (line_parity_prime & 0x10) -+ t |= 0x01; -+ ecc[1] = ~t; -+ -+ t = 0; -+ if (line_parity & 0x08) -+ t |= 0x80; -+ if (line_parity_prime & 0x08) -+ t |= 0x40; -+ if (line_parity & 0x04) -+ t |= 0x20; -+ if (line_parity_prime & 0x04) -+ t |= 0x10; -+ if (line_parity & 0x02) -+ t |= 0x08; -+ if (line_parity_prime & 0x02) -+ t |= 0x04; -+ if (line_parity & 0x01) -+ t |= 0x02; -+ if (line_parity_prime & 0x01) -+ t |= 0x01; -+ ecc[0] = ~t; -+ -+} -+ -+/* Correct the ECC on a 256 byte block of data */ -+ -+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, -+ const unsigned char *test_ecc) -+{ -+ unsigned char d0, d1, d2; /* deltas */ -+ -+ d0 = read_ecc[0] ^ test_ecc[0]; -+ d1 = read_ecc[1] ^ test_ecc[1]; -+ d2 = read_ecc[2] ^ test_ecc[2]; -+ -+ if ((d0 | d1 | d2) == 0) -+ return 0; /* no error */ -+ -+ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && -+ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && -+ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { -+ /* Single bit (recoverable) error in data */ -+ -+ unsigned byte; -+ unsigned bit; -+ -+ bit = byte = 0; -+ -+ if (d1 & 0x80) -+ byte |= 0x80; -+ if (d1 & 0x20) -+ byte |= 0x40; -+ if (d1 & 0x08) -+ byte |= 0x20; -+ if (d1 & 0x02) -+ byte |= 0x10; -+ if (d0 & 0x80) -+ byte |= 0x08; -+ if (d0 & 0x20) -+ byte |= 0x04; -+ if (d0 & 0x08) -+ byte |= 0x02; -+ if (d0 & 0x02) -+ byte |= 0x01; -+ -+ if (d2 & 0x80) -+ bit |= 0x04; -+ if (d2 & 0x20) -+ bit |= 0x02; -+ if (d2 & 0x08) -+ bit |= 0x01; -+ -+ data[byte] ^= (1 << bit); -+ -+ return 1; /* Corrected the error */ -+ } -+ -+ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { -+ /* Reccoverable error in ecc */ -+ -+ read_ecc[0] = test_ecc[0]; -+ read_ecc[1] = test_ecc[1]; -+ read_ecc[2] = test_ecc[2]; -+ -+ return 1; /* Corrected the error */ -+ } -+ -+ /* Unrecoverable error */ -+ -+ return -1; -+ -+} -+ -+/* -+ * ECCxxxOther does ECC calcs on arbitrary n bytes of data -+ */ -+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *ecc_other) -+{ -+ unsigned int i; -+ unsigned char col_parity = 0; -+ unsigned line_parity = 0; -+ unsigned line_parity_prime = 0; -+ unsigned char b; -+ -+ for (i = 0; i < n_bytes; i++) { -+ b = column_parity_table[*data++]; -+ col_parity ^= b; -+ -+ if (b & 0x01) { -+ /* odd number of bits in the byte */ -+ line_parity ^= i; -+ line_parity_prime ^= ~i; -+ } -+ -+ } -+ -+ ecc_other->col_parity = (col_parity >> 2) & 0x3f; -+ ecc_other->line_parity = line_parity; -+ ecc_other->line_parity_prime = line_parity_prime; -+} -+ -+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *read_ecc, -+ const struct yaffs_ecc_other *test_ecc) -+{ -+ unsigned char delta_col; /* column parity delta */ -+ unsigned delta_line; /* line parity delta */ -+ unsigned delta_line_prime; /* line parity delta */ -+ unsigned bit; -+ -+ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; -+ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; -+ delta_line_prime = -+ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; -+ -+ if ((delta_col | delta_line | delta_line_prime) == 0) -+ return 0; /* no error */ -+ -+ if (delta_line == ~delta_line_prime && -+ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { -+ /* Single bit (recoverable) error in data */ -+ -+ bit = 0; -+ -+ if (delta_col & 0x20) -+ bit |= 0x04; -+ if (delta_col & 0x08) -+ bit |= 0x02; -+ if (delta_col & 0x02) -+ bit |= 0x01; -+ -+ if (delta_line >= n_bytes) -+ return -1; -+ -+ data[delta_line] ^= (1 << bit); -+ -+ return 1; /* corrected */ -+ } -+ -+ if ((hweight32(delta_line) + -+ hweight32(delta_line_prime) + -+ hweight8(delta_col)) == 1) { -+ /* Reccoverable error in ecc */ -+ -+ *read_ecc = *test_ecc; -+ return 1; /* corrected */ -+ } -+ -+ /* Unrecoverable error */ -+ -+ return -1; -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_ecc.h linux-3.12.20/fs/yaffs2/yaffs_ecc.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_ecc.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,44 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* -+ * This code implements the ECC algorithm used in SmartMedia. -+ * -+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. -+ * The two unused bit are set to 1. -+ * The ECC can correct single bit errors in a 256-byte page of data. -+ * Thus, two such ECC blocks are used on a 512-byte NAND page. -+ * -+ */ -+ -+#ifndef __YAFFS_ECC_H__ -+#define __YAFFS_ECC_H__ -+ -+struct yaffs_ecc_other { -+ unsigned char col_parity; -+ unsigned line_parity; -+ unsigned line_parity_prime; -+}; -+ -+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); -+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, -+ const unsigned char *test_ecc); -+ -+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *ecc); -+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *read_ecc, -+ const struct yaffs_ecc_other *test_ecc); -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.12.20/fs/yaffs2/yaffs_getblockinfo.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,35 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_GETBLOCKINFO_H__ -+#define __YAFFS_GETBLOCKINFO_H__ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+ -+/* Function to manipulate block info */ -+static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev -+ *dev, int blk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs: get_block_info block %d is not valid", -+ blk); -+ BUG(); -+ } -+ return &dev->block_info[blk - dev->internal_start_block]; -+} -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_guts.c linux-3.12.20/fs/yaffs2/yaffs_guts.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_guts.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,5146 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yportenv.h" -+#include "yaffs_trace.h" -+ -+#include "yaffs_guts.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_tagscompat.h" -+#include "yaffs_tagsmarshall.h" -+#include "yaffs_nand.h" -+#include "yaffs_yaffs1.h" -+#include "yaffs_yaffs2.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_verify.h" -+#include "yaffs_nand.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_nameval.h" -+#include "yaffs_allocator.h" -+#include "yaffs_attribs.h" -+#include "yaffs_summary.h" -+ -+/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ -+#define YAFFS_GC_GOOD_ENOUGH 2 -+#define YAFFS_GC_PASSIVE_THRESHOLD 4 -+ -+#include "yaffs_ecc.h" -+ -+/* Forward declarations */ -+ -+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, -+ const u8 *buffer, int n_bytes, int use_reserve); -+ -+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, -+ int buffer_size); -+ -+/* Function to calculate chunk and offset */ -+ -+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, -+ int *chunk_out, u32 *offset_out) -+{ -+ int chunk; -+ u32 offset; -+ -+ chunk = (u32) (addr >> dev->chunk_shift); -+ -+ if (dev->chunk_div == 1) { -+ /* easy power of 2 case */ -+ offset = (u32) (addr & dev->chunk_mask); -+ } else { -+ /* Non power-of-2 case */ -+ -+ loff_t chunk_base; -+ -+ chunk /= dev->chunk_div; -+ -+ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; -+ offset = (u32) (addr - chunk_base); -+ } -+ -+ *chunk_out = chunk; -+ *offset_out = offset; -+} -+ -+/* Function to return the number of shifts for a power of 2 greater than or -+ * equal to the given number -+ * Note we don't try to cater for all possible numbers and this does not have to -+ * be hellishly efficient. -+ */ -+ -+static inline u32 calc_shifts_ceiling(u32 x) -+{ -+ int extra_bits; -+ int shifts; -+ -+ shifts = extra_bits = 0; -+ -+ while (x > 1) { -+ if (x & 1) -+ extra_bits++; -+ x >>= 1; -+ shifts++; -+ } -+ -+ if (extra_bits) -+ shifts++; -+ -+ return shifts; -+} -+ -+/* Function to return the number of shifts to get a 1 in bit 0 -+ */ -+ -+static inline u32 calc_shifts(u32 x) -+{ -+ u32 shifts; -+ -+ shifts = 0; -+ -+ if (!x) -+ return 0; -+ -+ while (!(x & 1)) { -+ x >>= 1; -+ shifts++; -+ } -+ -+ return shifts; -+} -+ -+/* -+ * Temporary buffer manipulations. -+ */ -+ -+static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) -+{ -+ int i; -+ u8 *buf = (u8 *) 1; -+ -+ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); -+ -+ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { -+ dev->temp_buffer[i].in_use = 0; -+ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ dev->temp_buffer[i].buffer = buf; -+ } -+ -+ return buf ? YAFFS_OK : YAFFS_FAIL; -+} -+ -+u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) -+{ -+ int i; -+ -+ dev->temp_in_use++; -+ if (dev->temp_in_use > dev->max_temp) -+ dev->max_temp = dev->temp_in_use; -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ if (dev->temp_buffer[i].in_use == 0) { -+ dev->temp_buffer[i].in_use = 1; -+ return dev->temp_buffer[i].buffer; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); -+ /* -+ * If we got here then we have to allocate an unmanaged one -+ * This is not good. -+ */ -+ -+ dev->unmanaged_buffer_allocs++; -+ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); -+ -+} -+ -+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) -+{ -+ int i; -+ -+ dev->temp_in_use--; -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ if (dev->temp_buffer[i].buffer == buffer) { -+ dev->temp_buffer[i].in_use = 0; -+ return; -+ } -+ } -+ -+ if (buffer) { -+ /* assume it is an unmanaged one. */ -+ yaffs_trace(YAFFS_TRACE_BUFFERS, -+ "Releasing unmanaged temp buffer"); -+ kfree(buffer); -+ dev->unmanaged_buffer_deallocs++; -+ } -+ -+} -+ -+/* -+ * Functions for robustisizing TODO -+ * -+ */ -+ -+static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, -+ const struct yaffs_ext_tags *tags) -+{ -+ (void) dev; -+ (void) nand_chunk; -+ (void) data; -+ (void) tags; -+} -+ -+static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, -+ const struct yaffs_ext_tags *tags) -+{ -+ (void) dev; -+ (void) nand_chunk; -+ (void) tags; -+} -+ -+void yaffs_handle_chunk_error(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi) -+{ -+ if (!bi->gc_prioritise) { -+ bi->gc_prioritise = 1; -+ dev->has_pending_prioritised_gc = 1; -+ bi->chunk_error_strikes++; -+ -+ if (bi->chunk_error_strikes > 3) { -+ bi->needs_retiring = 1; /* Too many stikes, so retire */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Block struck out"); -+ -+ } -+ } -+} -+ -+static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, -+ int erased_ok) -+{ -+ int flash_block = nand_chunk / dev->param.chunks_per_block; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -+ -+ yaffs_handle_chunk_error(dev, bi); -+ -+ if (erased_ok) { -+ /* Was an actual write failure, -+ * so mark the block for retirement.*/ -+ bi->needs_retiring = 1; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Block %d needs retiring", flash_block); -+ } -+ -+ /* Delete the chunk */ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ yaffs_skip_rest_of_block(dev); -+} -+ -+/* -+ * Verification code -+ */ -+ -+/* -+ * Simple hash function. Needs to have a reasonable spread -+ */ -+ -+static inline int yaffs_hash_fn(int n) -+{ -+ if (n < 0) -+ n = -n; -+ return n % YAFFS_NOBJECT_BUCKETS; -+} -+ -+/* -+ * Access functions to useful fake objects. -+ * Note that root might have a presence in NAND if permissions are set. -+ */ -+ -+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) -+{ -+ return dev->root_dir; -+} -+ -+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) -+{ -+ return dev->lost_n_found; -+} -+ -+/* -+ * Erased NAND checking functions -+ */ -+ -+int yaffs_check_ff(u8 *buffer, int n_bytes) -+{ -+ /* Horrible, slow implementation */ -+ while (n_bytes--) { -+ if (*buffer != 0xff) -+ return 0; -+ buffer++; -+ } -+ return 1; -+} -+ -+static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) -+{ -+ int retval = YAFFS_OK; -+ u8 *data = yaffs_get_temp_buffer(dev); -+ struct yaffs_ext_tags tags; -+ int result; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); -+ -+ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) -+ retval = YAFFS_FAIL; -+ -+ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || -+ tags.chunk_used) { -+ yaffs_trace(YAFFS_TRACE_NANDACCESS, -+ "Chunk %d not erased", nand_chunk); -+ retval = YAFFS_FAIL; -+ } -+ -+ yaffs_release_temp_buffer(dev, data); -+ -+ return retval; -+ -+} -+ -+static int yaffs_verify_chunk_written(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, -+ struct yaffs_ext_tags *tags) -+{ -+ int retval = YAFFS_OK; -+ struct yaffs_ext_tags temp_tags; -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ int result; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); -+ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || -+ temp_tags.obj_id != tags->obj_id || -+ temp_tags.chunk_id != tags->chunk_id || -+ temp_tags.n_bytes != tags->n_bytes) -+ retval = YAFFS_FAIL; -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ return retval; -+} -+ -+ -+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) -+{ -+ int reserved_chunks; -+ int reserved_blocks = dev->param.n_reserved_blocks; -+ int checkpt_blocks; -+ -+ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); -+ -+ reserved_chunks = -+ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; -+ -+ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); -+} -+ -+static int yaffs_find_alloc_block(struct yaffs_dev *dev) -+{ -+ int i; -+ struct yaffs_block_info *bi; -+ -+ if (dev->n_erased_blocks < 1) { -+ /* Hoosterman we've got a problem. -+ * Can't get space to gc -+ */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: no more erased blocks"); -+ -+ return -1; -+ } -+ -+ /* Find an empty block. */ -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ dev->alloc_block_finder++; -+ if (dev->alloc_block_finder < dev->internal_start_block -+ || dev->alloc_block_finder > dev->internal_end_block) { -+ dev->alloc_block_finder = dev->internal_start_block; -+ } -+ -+ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->seq_number++; -+ bi->seq_number = dev->seq_number; -+ dev->n_erased_blocks--; -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, -+ "Allocated block %d, seq %d, %d left" , -+ dev->alloc_block_finder, dev->seq_number, -+ dev->n_erased_blocks); -+ return dev->alloc_block_finder; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs tragedy: no more erased blocks, but there should have been %d", -+ dev->n_erased_blocks); -+ -+ return -1; -+} -+ -+static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, -+ struct yaffs_block_info **block_ptr) -+{ -+ int ret_val; -+ struct yaffs_block_info *bi; -+ -+ if (dev->alloc_block < 0) { -+ /* Get next block to allocate off */ -+ dev->alloc_block = yaffs_find_alloc_block(dev); -+ dev->alloc_page = 0; -+ } -+ -+ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { -+ /* No space unless we're allowed to use the reserve. */ -+ return -1; -+ } -+ -+ if (dev->n_erased_blocks < dev->param.n_reserved_blocks -+ && dev->alloc_page == 0) -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); -+ -+ /* Next page please.... */ -+ if (dev->alloc_block >= 0) { -+ bi = yaffs_get_block_info(dev, dev->alloc_block); -+ -+ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + -+ dev->alloc_page; -+ bi->pages_in_use++; -+ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); -+ -+ dev->alloc_page++; -+ -+ dev->n_free_chunks--; -+ -+ /* If the block is full set the state to full */ -+ if (dev->alloc_page >= dev->param.chunks_per_block) { -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ -+ if (block_ptr) -+ *block_ptr = bi; -+ -+ return ret_val; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); -+ -+ return -1; -+} -+ -+static int yaffs_get_erased_chunks(struct yaffs_dev *dev) -+{ -+ int n; -+ -+ n = dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ if (dev->alloc_block > 0) -+ n += (dev->param.chunks_per_block - dev->alloc_page); -+ -+ return n; -+ -+} -+ -+/* -+ * yaffs_skip_rest_of_block() skips over the rest of the allocation block -+ * if we don't want to write to it. -+ */ -+void yaffs_skip_rest_of_block(struct yaffs_dev *dev) -+{ -+ struct yaffs_block_info *bi; -+ -+ if (dev->alloc_block > 0) { -+ bi = yaffs_get_block_info(dev, dev->alloc_block); -+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ } -+} -+ -+static int yaffs_write_new_chunk(struct yaffs_dev *dev, -+ const u8 *data, -+ struct yaffs_ext_tags *tags, int use_reserver) -+{ -+ int attempts = 0; -+ int write_ok = 0; -+ int chunk; -+ -+ yaffs2_checkpt_invalidate(dev); -+ -+ do { -+ struct yaffs_block_info *bi = 0; -+ int erased_ok = 0; -+ -+ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); -+ if (chunk < 0) { -+ /* no space */ -+ break; -+ } -+ -+ /* First check this chunk is erased, if it needs -+ * checking. The checking policy (unless forced -+ * always on) is as follows: -+ * -+ * Check the first page we try to write in a block. -+ * If the check passes then we don't need to check any -+ * more. If the check fails, we check again... -+ * If the block has been erased, we don't need to check. -+ * -+ * However, if the block has been prioritised for gc, -+ * then we think there might be something odd about -+ * this block and stop using it. -+ * -+ * Rationale: We should only ever see chunks that have -+ * not been erased if there was a partially written -+ * chunk due to power loss. This checking policy should -+ * catch that case with very few checks and thus save a -+ * lot of checks that are most likely not needed. -+ * -+ * Mods to the above -+ * If an erase check fails or the write fails we skip the -+ * rest of the block. -+ */ -+ -+ /* let's give it a try */ -+ attempts++; -+ -+ if (dev->param.always_check_erased) -+ bi->skip_erased_check = 0; -+ -+ if (!bi->skip_erased_check) { -+ erased_ok = yaffs_check_chunk_erased(dev, chunk); -+ if (erased_ok != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs chunk %d was not erased", -+ chunk); -+ -+ /* If not erased, delete this one, -+ * skip rest of block and -+ * try another chunk */ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ yaffs_skip_rest_of_block(dev); -+ continue; -+ } -+ } -+ -+ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); -+ -+ if (!bi->skip_erased_check) -+ write_ok = -+ yaffs_verify_chunk_written(dev, chunk, data, tags); -+ -+ if (write_ok != YAFFS_OK) { -+ /* Clean up aborted write, skip to next block and -+ * try another chunk */ -+ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); -+ continue; -+ } -+ -+ bi->skip_erased_check = 1; -+ -+ /* Copy the data into the robustification buffer */ -+ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); -+ -+ } while (write_ok != YAFFS_OK && -+ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); -+ -+ if (!write_ok) -+ chunk = -1; -+ -+ if (attempts > 1) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs write required %d attempts", -+ attempts); -+ dev->n_retried_writes += (attempts - 1); -+ } -+ -+ return chunk; -+} -+ -+/* -+ * Block retiring for handling a broken block. -+ */ -+ -+static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -+ -+ yaffs2_checkpt_invalidate(dev); -+ -+ yaffs2_clear_oldest_dirty_seq(dev, bi); -+ -+ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { -+ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Failed to mark bad and erase block %d", -+ flash_block); -+ } else { -+ struct yaffs_ext_tags tags; -+ int chunk_id = -+ flash_block * dev->param.chunks_per_block; -+ -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ memset(buffer, 0xff, dev->data_bytes_per_chunk); -+ memset(&tags, 0, sizeof(tags)); -+ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; -+ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - -+ dev->chunk_offset, -+ buffer, -+ &tags) != YAFFS_OK) -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Failed to write bad block marker to block %d", -+ flash_block); -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ } -+ -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ bi->gc_prioritise = 0; -+ bi->needs_retiring = 0; -+ -+ dev->n_retired_blocks++; -+} -+ -+/*---------------- Name handling functions ------------*/ -+ -+static u16 yaffs_calc_name_sum(const YCHAR *name) -+{ -+ u16 sum = 0; -+ u16 i = 1; -+ -+ if (!name) -+ return 0; -+ -+ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { -+ -+ /* 0x1f mask is case insensitive */ -+ sum += ((*name) & 0x1f) * i; -+ i++; -+ name++; -+ } -+ return sum; -+} -+ -+ -+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) -+{ -+ memset(obj->short_name, 0, sizeof(obj->short_name)); -+ -+ if (name && !name[0]) { -+ yaffs_fix_null_name(obj, obj->short_name, -+ YAFFS_SHORT_NAME_LENGTH); -+ name = obj->short_name; -+ } else if (name && -+ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= -+ YAFFS_SHORT_NAME_LENGTH) { -+ strcpy(obj->short_name, name); -+ } -+ -+ obj->sum = yaffs_calc_name_sum(name); -+} -+ -+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, -+ const struct yaffs_obj_hdr *oh) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; -+ memset(tmp_name, 0, sizeof(tmp_name)); -+ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, -+ YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_set_obj_name(obj, tmp_name); -+#else -+ yaffs_set_obj_name(obj, oh->name); -+#endif -+} -+ -+loff_t yaffs_max_file_size(struct yaffs_dev *dev) -+{ -+ if(sizeof(loff_t) < 8) -+ return YAFFS_MAX_FILE_SIZE_32; -+ else -+ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; -+} -+ -+/*-------------------- TNODES ------------------- -+ -+ * List of spare tnodes -+ * The list is hooked together using the first pointer -+ * in the tnode. -+ */ -+ -+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) -+{ -+ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); -+ -+ if (tn) { -+ memset(tn, 0, dev->tnode_size); -+ dev->n_tnodes++; -+ } -+ -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+ -+ return tn; -+} -+ -+/* FreeTnode frees up a tnode and puts it back on the free list */ -+static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) -+{ -+ yaffs_free_raw_tnode(dev, tn); -+ dev->n_tnodes--; -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ yaffs_deinit_raw_tnodes_and_objs(dev); -+ dev->n_obj = 0; -+ dev->n_tnodes = 0; -+} -+ -+static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos, unsigned val) -+{ -+ u32 *map = (u32 *) tn; -+ u32 bit_in_map; -+ u32 bit_in_word; -+ u32 word_in_map; -+ u32 mask; -+ -+ pos &= YAFFS_TNODES_LEVEL0_MASK; -+ val >>= dev->chunk_grp_bits; -+ -+ bit_in_map = pos * dev->tnode_width; -+ word_in_map = bit_in_map / 32; -+ bit_in_word = bit_in_map & (32 - 1); -+ -+ mask = dev->tnode_mask << bit_in_word; -+ -+ map[word_in_map] &= ~mask; -+ map[word_in_map] |= (mask & (val << bit_in_word)); -+ -+ if (dev->tnode_width > (32 - bit_in_word)) { -+ bit_in_word = (32 - bit_in_word); -+ word_in_map++; -+ mask = -+ dev->tnode_mask >> bit_in_word; -+ map[word_in_map] &= ~mask; -+ map[word_in_map] |= (mask & (val >> bit_in_word)); -+ } -+} -+ -+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos) -+{ -+ u32 *map = (u32 *) tn; -+ u32 bit_in_map; -+ u32 bit_in_word; -+ u32 word_in_map; -+ u32 val; -+ -+ pos &= YAFFS_TNODES_LEVEL0_MASK; -+ -+ bit_in_map = pos * dev->tnode_width; -+ word_in_map = bit_in_map / 32; -+ bit_in_word = bit_in_map & (32 - 1); -+ -+ val = map[word_in_map] >> bit_in_word; -+ -+ if (dev->tnode_width > (32 - bit_in_word)) { -+ bit_in_word = (32 - bit_in_word); -+ word_in_map++; -+ val |= (map[word_in_map] << bit_in_word); -+ } -+ -+ val &= dev->tnode_mask; -+ val <<= dev->chunk_grp_bits; -+ -+ return val; -+} -+ -+/* ------------------- End of individual tnode manipulation -----------------*/ -+ -+/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ -+ * The look up tree is represented by the top tnode and the number of top_level -+ * in the tree. 0 means only the level 0 tnode is in the tree. -+ */ -+ -+/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ -+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id) -+{ -+ struct yaffs_tnode *tn = file_struct->top; -+ u32 i; -+ int required_depth; -+ int level = file_struct->top_level; -+ -+ (void) dev; -+ -+ /* Check sane level and chunk Id */ -+ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) -+ return NULL; -+ -+ if (chunk_id > YAFFS_MAX_CHUNK_ID) -+ return NULL; -+ -+ /* First check we're tall enough (ie enough top_level) */ -+ -+ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (i) { -+ i >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ if (required_depth > file_struct->top_level) -+ return NULL; /* Not tall enough, so we can't find it */ -+ -+ /* Traverse down to level 0 */ -+ while (level > 0 && tn) { -+ tn = tn->internal[(chunk_id >> -+ (YAFFS_TNODES_LEVEL0_BITS + -+ (level - 1) * -+ YAFFS_TNODES_INTERNAL_BITS)) & -+ YAFFS_TNODES_INTERNAL_MASK]; -+ level--; -+ } -+ -+ return tn; -+} -+ -+/* add_find_tnode_0 finds the level 0 tnode if it exists, -+ * otherwise first expands the tree. -+ * This happens in two steps: -+ * 1. If the tree isn't tall enough, then make it taller. -+ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. -+ * -+ * Used when modifying the tree. -+ * -+ * If the tn argument is NULL, then a fresh tnode will be added otherwise the -+ * specified tn will be plugged into the ttree. -+ */ -+ -+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id, -+ struct yaffs_tnode *passed_tn) -+{ -+ int required_depth; -+ int i; -+ int l; -+ struct yaffs_tnode *tn; -+ u32 x; -+ -+ /* Check sane level and page Id */ -+ if (file_struct->top_level < 0 || -+ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) -+ return NULL; -+ -+ if (chunk_id > YAFFS_MAX_CHUNK_ID) -+ return NULL; -+ -+ /* First check we're tall enough (ie enough top_level) */ -+ -+ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (x) { -+ x >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ if (required_depth > file_struct->top_level) { -+ /* Not tall enough, gotta make the tree taller */ -+ for (i = file_struct->top_level; i < required_depth; i++) { -+ -+ tn = yaffs_get_tnode(dev); -+ -+ if (tn) { -+ tn->internal[0] = file_struct->top; -+ file_struct->top = tn; -+ file_struct->top_level++; -+ } else { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs: no more tnodes"); -+ return NULL; -+ } -+ } -+ } -+ -+ /* Traverse down to level 0, adding anything we need */ -+ -+ l = file_struct->top_level; -+ tn = file_struct->top; -+ -+ if (l > 0) { -+ while (l > 0 && tn) { -+ x = (chunk_id >> -+ (YAFFS_TNODES_LEVEL0_BITS + -+ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & -+ YAFFS_TNODES_INTERNAL_MASK; -+ -+ if ((l > 1) && !tn->internal[x]) { -+ /* Add missing non-level-zero tnode */ -+ tn->internal[x] = yaffs_get_tnode(dev); -+ if (!tn->internal[x]) -+ return NULL; -+ } else if (l == 1) { -+ /* Looking from level 1 at level 0 */ -+ if (passed_tn) { -+ /* If we already have one, release it */ -+ if (tn->internal[x]) -+ yaffs_free_tnode(dev, -+ tn->internal[x]); -+ tn->internal[x] = passed_tn; -+ -+ } else if (!tn->internal[x]) { -+ /* Don't have one, none passed in */ -+ tn->internal[x] = yaffs_get_tnode(dev); -+ if (!tn->internal[x]) -+ return NULL; -+ } -+ } -+ -+ tn = tn->internal[x]; -+ l--; -+ } -+ } else { -+ /* top is level 0 */ -+ if (passed_tn) { -+ memcpy(tn, passed_tn, -+ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); -+ yaffs_free_tnode(dev, passed_tn); -+ } -+ } -+ -+ return tn; -+} -+ -+static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, -+ int chunk_obj) -+{ -+ return (tags->chunk_id == chunk_obj && -+ tags->obj_id == obj_id && -+ !tags->is_deleted) ? 1 : 0; -+ -+} -+ -+static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, -+ struct yaffs_ext_tags *tags, int obj_id, -+ int inode_chunk) -+{ -+ int j; -+ -+ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { -+ if (yaffs_check_chunk_bit -+ (dev, the_chunk / dev->param.chunks_per_block, -+ the_chunk % dev->param.chunks_per_block)) { -+ -+ if (dev->chunk_grp_size == 1) -+ return the_chunk; -+ else { -+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, -+ tags); -+ if (yaffs_tags_match(tags, -+ obj_id, inode_chunk)) { -+ /* found it; */ -+ return the_chunk; -+ } -+ } -+ } -+ the_chunk++; -+ } -+ return -1; -+} -+ -+int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags) -+{ -+ /*Get the Tnode, then get the level 0 offset chunk offset */ -+ struct yaffs_tnode *tn; -+ int the_chunk = -1; -+ struct yaffs_ext_tags local_tags; -+ int ret_val = -1; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!tags) { -+ /* Passed a NULL, so use our own tags space */ -+ tags = &local_tags; -+ } -+ -+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -+ -+ if (!tn) -+ return ret_val; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, -+ inode_chunk); -+ return ret_val; -+} -+ -+static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags) -+{ -+ /* Get the Tnode, then get the level 0 offset chunk offset */ -+ struct yaffs_tnode *tn; -+ int the_chunk = -1; -+ struct yaffs_ext_tags local_tags; -+ struct yaffs_dev *dev = in->my_dev; -+ int ret_val = -1; -+ -+ if (!tags) { -+ /* Passed a NULL, so use our own tags space */ -+ tags = &local_tags; -+ } -+ -+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -+ -+ if (!tn) -+ return ret_val; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, -+ inode_chunk); -+ -+ /* Delete the entry in the filestructure (if found) */ -+ if (ret_val != -1) -+ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); -+ -+ return ret_val; -+} -+ -+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ int nand_chunk, int in_scan) -+{ -+ /* NB in_scan is zero unless scanning. -+ * For forward scanning, in_scan is > 0; -+ * for backward scanning in_scan is < 0 -+ * -+ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. -+ */ -+ -+ struct yaffs_tnode *tn; -+ struct yaffs_dev *dev = in->my_dev; -+ int existing_cunk; -+ struct yaffs_ext_tags existing_tags; -+ struct yaffs_ext_tags new_tags; -+ unsigned existing_serial, new_serial; -+ -+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { -+ /* Just ignore an attempt at putting a chunk into a non-file -+ * during scanning. -+ * If it is not during Scanning then something went wrong! -+ */ -+ if (!in_scan) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy:attempt to put data chunk into a non-file" -+ ); -+ BUG(); -+ } -+ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ return YAFFS_OK; -+ } -+ -+ tn = yaffs_add_find_tnode_0(dev, -+ &in->variant.file_variant, -+ inode_chunk, NULL); -+ if (!tn) -+ return YAFFS_FAIL; -+ -+ if (!nand_chunk) -+ /* Dummy insert, bail now */ -+ return YAFFS_OK; -+ -+ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ if (in_scan != 0) { -+ /* If we're scanning then we need to test for duplicates -+ * NB This does not need to be efficient since it should only -+ * happen when the power fails during a write, then only one -+ * chunk should ever be affected. -+ * -+ * Correction for YAFFS2: This could happen quite a lot and we -+ * need to think about efficiency! TODO -+ * Update: For backward scanning we don't need to re-read tags -+ * so this is quite cheap. -+ */ -+ -+ if (existing_cunk > 0) { -+ /* NB Right now existing chunk will not be real -+ * chunk_id if the chunk group size > 1 -+ * thus we have to do a FindChunkInFile to get the -+ * real chunk id. -+ * -+ * We have a duplicate now we need to decide which -+ * one to use: -+ * -+ * Backwards scanning YAFFS2: The old one is what -+ * we use, dump the new one. -+ * YAFFS1: Get both sets of tags and compare serial -+ * numbers. -+ */ -+ -+ if (in_scan > 0) { -+ /* Only do this for forward scanning */ -+ yaffs_rd_chunk_tags_nand(dev, -+ nand_chunk, -+ NULL, &new_tags); -+ -+ /* Do a proper find */ -+ existing_cunk = -+ yaffs_find_chunk_in_file(in, inode_chunk, -+ &existing_tags); -+ } -+ -+ if (existing_cunk <= 0) { -+ /*Hoosterman - how did this happen? */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: existing chunk < 0 in scan" -+ ); -+ -+ } -+ -+ /* NB The deleted flags should be false, otherwise -+ * the chunks will not be loaded during a scan -+ */ -+ -+ if (in_scan > 0) { -+ new_serial = new_tags.serial_number; -+ existing_serial = existing_tags.serial_number; -+ } -+ -+ if ((in_scan > 0) && -+ (existing_cunk <= 0 || -+ ((existing_serial + 1) & 3) == new_serial)) { -+ /* Forward scanning. -+ * Use new -+ * Delete the old one and drop through to -+ * update the tnode -+ */ -+ yaffs_chunk_del(dev, existing_cunk, 1, -+ __LINE__); -+ } else { -+ /* Backward scanning or we want to use the -+ * existing one -+ * Delete the new one and return early so that -+ * the tnode isn't changed -+ */ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ return YAFFS_OK; -+ } -+ } -+ -+ } -+ -+ if (existing_cunk == 0) -+ in->n_data_chunks++; -+ -+ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); -+ -+ return YAFFS_OK; -+} -+ -+static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) -+{ -+ struct yaffs_block_info *the_block; -+ unsigned block_no; -+ -+ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); -+ -+ block_no = chunk / dev->param.chunks_per_block; -+ the_block = yaffs_get_block_info(dev, block_no); -+ if (the_block) { -+ the_block->soft_del_pages++; -+ dev->n_free_chunks++; -+ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); -+ } -+} -+ -+/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all -+ * the chunks in the file. -+ * All soft deleting does is increment the block's softdelete count and pulls -+ * the chunk out of the tnode. -+ * Thus, essentially this is the same as DeleteWorker except that the chunks -+ * are soft deleted. -+ */ -+ -+static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, -+ u32 level, int chunk_offset) -+{ -+ int i; -+ int the_chunk; -+ int all_done = 1; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!tn) -+ return 1; -+ -+ if (level > 0) { -+ for (i = YAFFS_NTNODES_INTERNAL - 1; -+ all_done && i >= 0; -+ i--) { -+ if (tn->internal[i]) { -+ all_done = -+ yaffs_soft_del_worker(in, -+ tn->internal[i], -+ level - 1, -+ (chunk_offset << -+ YAFFS_TNODES_INTERNAL_BITS) -+ + i); -+ if (all_done) { -+ yaffs_free_tnode(dev, -+ tn->internal[i]); -+ tn->internal[i] = NULL; -+ } else { -+ /* Can this happen? */ -+ } -+ } -+ } -+ return (all_done) ? 1 : 0; -+ } -+ -+ /* level 0 */ -+ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { -+ the_chunk = yaffs_get_group_base(dev, tn, i); -+ if (the_chunk) { -+ yaffs_soft_del_chunk(dev, the_chunk); -+ yaffs_load_tnode_0(dev, tn, i, 0); -+ } -+ } -+ return 1; -+} -+ -+static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ struct yaffs_obj *parent; -+ -+ yaffs_verify_obj_in_dir(obj); -+ parent = obj->parent; -+ -+ yaffs_verify_dir(parent); -+ -+ if (dev && dev->param.remove_obj_fn) -+ dev->param.remove_obj_fn(obj); -+ -+ list_del_init(&obj->siblings); -+ obj->parent = NULL; -+ -+ yaffs_verify_dir(parent); -+} -+ -+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) -+{ -+ if (!directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: Trying to add an object to a null pointer directory" -+ ); -+ BUG(); -+ return; -+ } -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: Trying to add an object to a non-directory" -+ ); -+ BUG(); -+ } -+ -+ if (obj->siblings.prev == NULL) { -+ /* Not initialised */ -+ BUG(); -+ } -+ -+ yaffs_verify_dir(directory); -+ -+ yaffs_remove_obj_from_dir(obj); -+ -+ /* Now add it */ -+ list_add(&obj->siblings, &directory->variant.dir_variant.children); -+ obj->parent = directory; -+ -+ if (directory == obj->my_dev->unlinked_dir -+ || directory == obj->my_dev->del_dir) { -+ obj->unlinked = 1; -+ obj->my_dev->n_unlinked_files++; -+ obj->rename_allowed = 0; -+ } -+ -+ yaffs_verify_dir(directory); -+ yaffs_verify_obj_in_dir(obj); -+} -+ -+static int yaffs_change_obj_name(struct yaffs_obj *obj, -+ struct yaffs_obj *new_dir, -+ const YCHAR *new_name, int force, int shadows) -+{ -+ int unlink_op; -+ int del_op; -+ struct yaffs_obj *existing_target; -+ -+ if (new_dir == NULL) -+ new_dir = obj->parent; /* use the old directory */ -+ -+ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_change_obj_name: new_dir is not a directory" -+ ); -+ BUG(); -+ } -+ -+ unlink_op = (new_dir == obj->my_dev->unlinked_dir); -+ del_op = (new_dir == obj->my_dev->del_dir); -+ -+ existing_target = yaffs_find_by_name(new_dir, new_name); -+ -+ /* If the object is a file going into the unlinked directory, -+ * then it is OK to just stuff it in since duplicate names are OK. -+ * else only proceed if the new name does not exist and we're putting -+ * it into a directory. -+ */ -+ if (!(unlink_op || del_op || force || -+ shadows > 0 || !existing_target) || -+ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) -+ return YAFFS_FAIL; -+ -+ yaffs_set_obj_name(obj, new_name); -+ obj->dirty = 1; -+ yaffs_add_obj_to_dir(new_dir, obj); -+ -+ if (unlink_op) -+ obj->unlinked = 1; -+ -+ /* If it is a deletion then we mark it as a shrink for gc */ -+ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+/*------------------------ Short Operations Cache ------------------------------ -+ * In many situations where there is no high level buffering a lot of -+ * reads might be short sequential reads, and a lot of writes may be short -+ * sequential writes. eg. scanning/writing a jpeg file. -+ * In these cases, a short read/write cache can provide a huge perfomance -+ * benefit with dumb-as-a-rock code. -+ * In Linux, the page cache provides read buffering and the short op cache -+ * provides write buffering. -+ * -+ * There are a small number (~10) of cache chunks per device so that we don't -+ * need a very intelligent search. -+ */ -+ -+static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ struct yaffs_cache *cache; -+ int n_caches = obj->my_dev->param.n_caches; -+ -+ for (i = 0; i < n_caches; i++) { -+ cache = &dev->cache[i]; -+ if (cache->object == obj && cache->dirty) -+ return 1; -+ } -+ -+ return 0; -+} -+ -+static void yaffs_flush_file_cache(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int lowest = -99; /* Stop compiler whining. */ -+ int i; -+ struct yaffs_cache *cache; -+ int chunk_written = 0; -+ int n_caches = obj->my_dev->param.n_caches; -+ -+ if (n_caches < 1) -+ return; -+ do { -+ cache = NULL; -+ -+ /* Find the lowest dirty chunk for this object */ -+ for (i = 0; i < n_caches; i++) { -+ if (dev->cache[i].object == obj && -+ dev->cache[i].dirty) { -+ if (!cache || -+ dev->cache[i].chunk_id < lowest) { -+ cache = &dev->cache[i]; -+ lowest = cache->chunk_id; -+ } -+ } -+ } -+ -+ if (cache && !cache->locked) { -+ /* Write it out and free it up */ -+ chunk_written = -+ yaffs_wr_data_obj(cache->object, -+ cache->chunk_id, -+ cache->data, -+ cache->n_bytes, 1); -+ cache->dirty = 0; -+ cache->object = NULL; -+ } -+ } while (cache && chunk_written > 0); -+ -+ if (cache) -+ /* Hoosterman, disk full while writing cache out. */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: no space during cache write"); -+} -+ -+/*yaffs_flush_whole_cache(dev) -+ * -+ * -+ */ -+ -+void yaffs_flush_whole_cache(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ int n_caches = dev->param.n_caches; -+ int i; -+ -+ /* Find a dirty object in the cache and flush it... -+ * until there are no further dirty objects. -+ */ -+ do { -+ obj = NULL; -+ for (i = 0; i < n_caches && !obj; i++) { -+ if (dev->cache[i].object && dev->cache[i].dirty) -+ obj = dev->cache[i].object; -+ } -+ if (obj) -+ yaffs_flush_file_cache(obj); -+ } while (obj); -+ -+} -+ -+/* Grab us a cache chunk for use. -+ * First look for an empty one. -+ * Then look for the least recently used non-dirty one. -+ * Then look for the least recently used dirty one...., flush and look again. -+ */ -+static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (dev->param.n_caches > 0) { -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (!dev->cache[i].object) -+ return &dev->cache[i]; -+ } -+ } -+ return NULL; -+} -+ -+static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) -+{ -+ struct yaffs_cache *cache; -+ struct yaffs_obj *the_obj; -+ int usage; -+ int i; -+ int pushout; -+ -+ if (dev->param.n_caches < 1) -+ return NULL; -+ -+ /* Try find a non-dirty one... */ -+ -+ cache = yaffs_grab_chunk_worker(dev); -+ -+ if (!cache) { -+ /* They were all dirty, find the LRU object and flush -+ * its cache, then find again. -+ * NB what's here is not very accurate, -+ * we actually flush the object with the LRU chunk. -+ */ -+ -+ /* With locking we can't assume we can use entry zero, -+ * Set the_obj to a valid pointer for Coverity. */ -+ the_obj = dev->cache[0].object; -+ usage = -1; -+ cache = NULL; -+ pushout = -1; -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object && -+ !dev->cache[i].locked && -+ (dev->cache[i].last_use < usage || -+ !cache)) { -+ usage = dev->cache[i].last_use; -+ the_obj = dev->cache[i].object; -+ cache = &dev->cache[i]; -+ pushout = i; -+ } -+ } -+ -+ if (!cache || cache->dirty) { -+ /* Flush and try again */ -+ yaffs_flush_file_cache(the_obj); -+ cache = yaffs_grab_chunk_worker(dev); -+ } -+ } -+ return cache; -+} -+ -+/* Find a cached chunk */ -+static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, -+ int chunk_id) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return NULL; -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object == obj && -+ dev->cache[i].chunk_id == chunk_id) { -+ dev->cache_hits++; -+ -+ return &dev->cache[i]; -+ } -+ } -+ return NULL; -+} -+ -+/* Mark the chunk for the least recently used algorithym */ -+static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, -+ int is_write) -+{ -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return; -+ -+ if (dev->cache_last_use < 0 || -+ dev->cache_last_use > 100000000) { -+ /* Reset the cache usages */ -+ for (i = 1; i < dev->param.n_caches; i++) -+ dev->cache[i].last_use = 0; -+ -+ dev->cache_last_use = 0; -+ } -+ dev->cache_last_use++; -+ cache->last_use = dev->cache_last_use; -+ -+ if (is_write) -+ cache->dirty = 1; -+} -+ -+/* Invalidate a single cache page. -+ * Do this when a whole page gets written, -+ * ie the short cache for this page is no longer valid. -+ */ -+static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) -+{ -+ struct yaffs_cache *cache; -+ -+ if (object->my_dev->param.n_caches > 0) { -+ cache = yaffs_find_chunk_cache(object, chunk_id); -+ -+ if (cache) -+ cache->object = NULL; -+ } -+} -+ -+/* Invalidate all the cache pages associated with this object -+ * Do this whenever ther file is deleted or resized. -+ */ -+static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) -+{ -+ int i; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (dev->param.n_caches > 0) { -+ /* Invalidate it. */ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object == in) -+ dev->cache[i].object = NULL; -+ } -+ } -+} -+ -+static void yaffs_unhash_obj(struct yaffs_obj *obj) -+{ -+ int bucket; -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ /* If it is still linked into the bucket list, free from the list */ -+ if (!list_empty(&obj->hash_link)) { -+ list_del_init(&obj->hash_link); -+ bucket = yaffs_hash_fn(obj->obj_id); -+ dev->obj_bucket[bucket].count--; -+ } -+} -+ -+/* FreeObject frees up a Object and puts it back on the free list */ -+static void yaffs_free_obj(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ -+ if (!obj) { -+ BUG(); -+ return; -+ } -+ dev = obj->my_dev; -+ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", -+ obj, obj->my_inode); -+ if (obj->parent) -+ BUG(); -+ if (!list_empty(&obj->siblings)) -+ BUG(); -+ -+ if (obj->my_inode) { -+ /* We're still hooked up to a cached inode. -+ * Don't delete now, but mark for later deletion -+ */ -+ obj->defered_free = 1; -+ return; -+ } -+ -+ yaffs_unhash_obj(obj); -+ -+ yaffs_free_raw_obj(dev, obj); -+ dev->n_obj--; -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+void yaffs_handle_defered_free(struct yaffs_obj *obj) -+{ -+ if (obj->defered_free) -+ yaffs_free_obj(obj); -+} -+ -+static int yaffs_generic_obj_del(struct yaffs_obj *in) -+{ -+ /* Iinvalidate the file's data in the cache, without flushing. */ -+ yaffs_invalidate_whole_cache(in); -+ -+ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { -+ /* Move to unlinked directory so we have a deletion record */ -+ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, -+ 0); -+ } -+ -+ yaffs_remove_obj_from_dir(in); -+ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); -+ in->hdr_chunk = 0; -+ -+ yaffs_free_obj(in); -+ return YAFFS_OK; -+ -+} -+ -+static void yaffs_soft_del_file(struct yaffs_obj *obj) -+{ -+ if (!obj->deleted || -+ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || -+ obj->soft_del) -+ return; -+ -+ if (obj->n_data_chunks <= 0) { -+ /* Empty file with no duplicate object headers, -+ * just delete it immediately */ -+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); -+ obj->variant.file_variant.top = NULL; -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: Deleting empty file %d", -+ obj->obj_id); -+ yaffs_generic_obj_del(obj); -+ } else { -+ yaffs_soft_del_worker(obj, -+ obj->variant.file_variant.top, -+ obj->variant. -+ file_variant.top_level, 0); -+ obj->soft_del = 1; -+ } -+} -+ -+/* Pruning removes any part of the file structure tree that is beyond the -+ * bounds of the file (ie that does not point to chunks). -+ * -+ * A file should only get pruned when its size is reduced. -+ * -+ * Before pruning, the chunks must be pulled from the tree and the -+ * level 0 tnode entries must be zeroed out. -+ * Could also use this for file deletion, but that's probably better handled -+ * by a special case. -+ * -+ * This function is recursive. For levels > 0 the function is called again on -+ * any sub-tree. For level == 0 we just check if the sub-tree has data. -+ * If there is no data in a subtree then it is pruned. -+ */ -+ -+static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, -+ struct yaffs_tnode *tn, u32 level, -+ int del0) -+{ -+ int i; -+ int has_data; -+ -+ if (!tn) -+ return tn; -+ -+ has_data = 0; -+ -+ if (level > 0) { -+ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { -+ if (tn->internal[i]) { -+ tn->internal[i] = -+ yaffs_prune_worker(dev, -+ tn->internal[i], -+ level - 1, -+ (i == 0) ? del0 : 1); -+ } -+ -+ if (tn->internal[i]) -+ has_data++; -+ } -+ } else { -+ int tnode_size_u32 = dev->tnode_size / sizeof(u32); -+ u32 *map = (u32 *) tn; -+ -+ for (i = 0; !has_data && i < tnode_size_u32; i++) { -+ if (map[i]) -+ has_data++; -+ } -+ } -+ -+ if (has_data == 0 && del0) { -+ /* Free and return NULL */ -+ yaffs_free_tnode(dev, tn); -+ tn = NULL; -+ } -+ return tn; -+} -+ -+static int yaffs_prune_tree(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct) -+{ -+ int i; -+ int has_data; -+ int done = 0; -+ struct yaffs_tnode *tn; -+ -+ if (file_struct->top_level < 1) -+ return YAFFS_OK; -+ -+ file_struct->top = -+ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); -+ -+ /* Now we have a tree with all the non-zero branches NULL but -+ * the height is the same as it was. -+ * Let's see if we can trim internal tnodes to shorten the tree. -+ * We can do this if only the 0th element in the tnode is in use -+ * (ie all the non-zero are NULL) -+ */ -+ -+ while (file_struct->top_level && !done) { -+ tn = file_struct->top; -+ -+ has_data = 0; -+ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { -+ if (tn->internal[i]) -+ has_data++; -+ } -+ -+ if (!has_data) { -+ file_struct->top = tn->internal[0]; -+ file_struct->top_level--; -+ yaffs_free_tnode(dev, tn); -+ } else { -+ done = 1; -+ } -+ } -+ -+ return YAFFS_OK; -+} -+ -+/*-------------------- End of File Structure functions.-------------------*/ -+ -+/* alloc_empty_obj gets us a clean Object.*/ -+static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); -+ -+ if (!obj) -+ return obj; -+ -+ dev->n_obj++; -+ -+ /* Now sweeten it up... */ -+ -+ memset(obj, 0, sizeof(struct yaffs_obj)); -+ obj->being_created = 1; -+ -+ obj->my_dev = dev; -+ obj->hdr_chunk = 0; -+ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; -+ INIT_LIST_HEAD(&(obj->hard_links)); -+ INIT_LIST_HEAD(&(obj->hash_link)); -+ INIT_LIST_HEAD(&obj->siblings); -+ -+ /* Now make the directory sane */ -+ if (dev->root_dir) { -+ obj->parent = dev->root_dir; -+ list_add(&(obj->siblings), -+ &dev->root_dir->variant.dir_variant.children); -+ } -+ -+ /* Add it to the lost and found directory. -+ * NB Can't put root or lost-n-found in lost-n-found so -+ * check if lost-n-found exists first -+ */ -+ if (dev->lost_n_found) -+ yaffs_add_obj_to_dir(dev->lost_n_found, obj); -+ -+ obj->being_created = 0; -+ -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+ -+ return obj; -+} -+ -+static int yaffs_find_nice_bucket(struct yaffs_dev *dev) -+{ -+ int i; -+ int l = 999; -+ int lowest = 999999; -+ -+ /* Search for the shortest list or one that -+ * isn't too long. -+ */ -+ -+ for (i = 0; i < 10 && lowest > 4; i++) { -+ dev->bucket_finder++; -+ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; -+ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { -+ lowest = dev->obj_bucket[dev->bucket_finder].count; -+ l = dev->bucket_finder; -+ } -+ } -+ -+ return l; -+} -+ -+static int yaffs_new_obj_id(struct yaffs_dev *dev) -+{ -+ int bucket = yaffs_find_nice_bucket(dev); -+ int found = 0; -+ struct list_head *i; -+ u32 n = (u32) bucket; -+ -+ /* Now find an object value that has not already been taken -+ * by scanning the list. -+ */ -+ -+ while (!found) { -+ found = 1; -+ n += YAFFS_NOBJECT_BUCKETS; -+ if (1 || dev->obj_bucket[bucket].count > 0) { -+ list_for_each(i, &dev->obj_bucket[bucket].list) { -+ /* If there is already one in the list */ -+ if (i && list_entry(i, struct yaffs_obj, -+ hash_link)->obj_id == n) { -+ found = 0; -+ } -+ } -+ } -+ } -+ return n; -+} -+ -+static void yaffs_hash_obj(struct yaffs_obj *in) -+{ -+ int bucket = yaffs_hash_fn(in->obj_id); -+ struct yaffs_dev *dev = in->my_dev; -+ -+ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); -+ dev->obj_bucket[bucket].count++; -+} -+ -+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) -+{ -+ int bucket = yaffs_hash_fn(number); -+ struct list_head *i; -+ struct yaffs_obj *in; -+ -+ list_for_each(i, &dev->obj_bucket[bucket].list) { -+ /* Look if it is in the list */ -+ in = list_entry(i, struct yaffs_obj, hash_link); -+ if (in->obj_id == number) { -+ /* Don't show if it is defered free */ -+ if (in->defered_free) -+ return NULL; -+ return in; -+ } -+ } -+ -+ return NULL; -+} -+ -+static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, -+ enum yaffs_obj_type type) -+{ -+ struct yaffs_obj *the_obj = NULL; -+ struct yaffs_tnode *tn = NULL; -+ -+ if (number < 0) -+ number = yaffs_new_obj_id(dev); -+ -+ if (type == YAFFS_OBJECT_TYPE_FILE) { -+ tn = yaffs_get_tnode(dev); -+ if (!tn) -+ return NULL; -+ } -+ -+ the_obj = yaffs_alloc_empty_obj(dev); -+ if (!the_obj) { -+ if (tn) -+ yaffs_free_tnode(dev, tn); -+ return NULL; -+ } -+ -+ the_obj->fake = 0; -+ the_obj->rename_allowed = 1; -+ the_obj->unlink_allowed = 1; -+ the_obj->obj_id = number; -+ yaffs_hash_obj(the_obj); -+ the_obj->variant_type = type; -+ yaffs_load_current_time(the_obj, 1, 1); -+ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ the_obj->variant.file_variant.file_size = 0; -+ the_obj->variant.file_variant.scanned_size = 0; -+ the_obj->variant.file_variant.shrink_size = -+ yaffs_max_file_size(dev); -+ the_obj->variant.file_variant.top_level = 0; -+ the_obj->variant.file_variant.top = tn; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); -+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* No action required */ -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* todo this should not happen */ -+ break; -+ } -+ return the_obj; -+} -+ -+static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, -+ int number, u32 mode) -+{ -+ -+ struct yaffs_obj *obj = -+ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); -+ -+ if (!obj) -+ return NULL; -+ -+ obj->fake = 1; /* it is fake so it might not use NAND */ -+ obj->rename_allowed = 0; -+ obj->unlink_allowed = 0; -+ obj->deleted = 0; -+ obj->unlinked = 0; -+ obj->yst_mode = mode; -+ obj->my_dev = dev; -+ obj->hdr_chunk = 0; /* Not a valid chunk. */ -+ return obj; -+ -+} -+ -+ -+static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ dev->n_obj = 0; -+ dev->n_tnodes = 0; -+ yaffs_init_raw_tnodes_and_objs(dev); -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ INIT_LIST_HEAD(&dev->obj_bucket[i].list); -+ dev->obj_bucket[i].count = 0; -+ } -+} -+ -+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, -+ int number, -+ enum yaffs_obj_type type) -+{ -+ struct yaffs_obj *the_obj = NULL; -+ -+ if (number > 0) -+ the_obj = yaffs_find_by_number(dev, number); -+ -+ if (!the_obj) -+ the_obj = yaffs_new_obj(dev, number, type); -+ -+ return the_obj; -+ -+} -+ -+YCHAR *yaffs_clone_str(const YCHAR *str) -+{ -+ YCHAR *new_str = NULL; -+ int len; -+ -+ if (!str) -+ str = _Y(""); -+ -+ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); -+ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); -+ if (new_str) { -+ strncpy(new_str, str, len); -+ new_str[len] = 0; -+ } -+ return new_str; -+ -+} -+/* -+ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new -+ * link (ie. name) is created or deleted in the directory. -+ * -+ * ie. -+ * create dir/a : update dir's mtime/ctime -+ * rm dir/a: update dir's mtime/ctime -+ * modify dir/a: don't update dir's mtimme/ctime -+ * -+ * This can be handled immediately or defered. Defering helps reduce the number -+ * of updates when many files in a directory are changed within a brief period. -+ * -+ * If the directory updating is defered then yaffs_update_dirty_dirs must be -+ * called periodically. -+ */ -+ -+static void yaffs_update_parent(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ -+ if (!obj) -+ return; -+ dev = obj->my_dev; -+ obj->dirty = 1; -+ yaffs_load_current_time(obj, 0, 1); -+ if (dev->param.defered_dir_update) { -+ struct list_head *link = &obj->variant.dir_variant.dirty; -+ -+ if (list_empty(link)) { -+ list_add(link, &dev->dirty_dirs); -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "Added object %d to dirty directories", -+ obj->obj_id); -+ } -+ -+ } else { -+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); -+ } -+} -+ -+void yaffs_update_dirty_dirs(struct yaffs_dev *dev) -+{ -+ struct list_head *link; -+ struct yaffs_obj *obj; -+ struct yaffs_dir_var *d_s; -+ union yaffs_obj_var *o_v; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); -+ -+ while (!list_empty(&dev->dirty_dirs)) { -+ link = dev->dirty_dirs.next; -+ list_del_init(link); -+ -+ d_s = list_entry(link, struct yaffs_dir_var, dirty); -+ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); -+ obj = list_entry(o_v, struct yaffs_obj, variant); -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", -+ obj->obj_id); -+ -+ if (obj->dirty) -+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); -+ } -+} -+ -+/* -+ * Mknod (create) a new object. -+ * equiv_obj only has meaning for a hard link; -+ * alias_str only has meaning for a symlink. -+ * rdev only has meaning for devices (a subset of special objects) -+ */ -+ -+static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, -+ struct yaffs_obj *parent, -+ const YCHAR *name, -+ u32 mode, -+ u32 uid, -+ u32 gid, -+ struct yaffs_obj *equiv_obj, -+ const YCHAR *alias_str, u32 rdev) -+{ -+ struct yaffs_obj *in; -+ YCHAR *str = NULL; -+ struct yaffs_dev *dev = parent->my_dev; -+ -+ /* Check if the entry exists. -+ * If it does then fail the call since we don't want a dup. */ -+ if (yaffs_find_by_name(parent, name)) -+ return NULL; -+ -+ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ str = yaffs_clone_str(alias_str); -+ if (!str) -+ return NULL; -+ } -+ -+ in = yaffs_new_obj(dev, -1, type); -+ -+ if (!in) { -+ kfree(str); -+ return NULL; -+ } -+ -+ in->hdr_chunk = 0; -+ in->valid = 1; -+ in->variant_type = type; -+ -+ in->yst_mode = mode; -+ -+ yaffs_attribs_init(in, gid, uid, rdev); -+ -+ in->n_data_chunks = 0; -+ -+ yaffs_set_obj_name(in, name); -+ in->dirty = 1; -+ -+ yaffs_add_obj_to_dir(parent, in); -+ -+ in->my_dev = parent->my_dev; -+ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ in->variant.symlink_variant.alias = str; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ in->variant.hardlink_variant.equiv_obj = equiv_obj; -+ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; -+ list_add(&in->hard_links, &equiv_obj->hard_links); -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* do nothing */ -+ break; -+ } -+ -+ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { -+ /* Could not create the object header, fail */ -+ yaffs_del_obj(in); -+ in = NULL; -+ } -+ -+ if (in) -+ yaffs_update_parent(parent); -+ -+ return in; -+} -+ -+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, -+ uid, gid, NULL, NULL, 0); -+} -+ -+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, -+ u32 mode, u32 uid, u32 gid) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, -+ mode, uid, gid, NULL, NULL, 0); -+} -+ -+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, u32 rdev) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, -+ uid, gid, NULL, NULL, rdev); -+} -+ -+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, const YCHAR *alias) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, -+ uid, gid, NULL, alias, 0); -+} -+ -+/* yaffs_link_obj returns the object id of the equivalent object.*/ -+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, -+ struct yaffs_obj *equiv_obj) -+{ -+ /* Get the real object in case we were fed a hard link obj */ -+ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); -+ -+ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, -+ parent, name, 0, 0, 0, -+ equiv_obj, NULL, 0)) -+ return equiv_obj; -+ -+ return NULL; -+ -+} -+ -+ -+ -+/*---------------------- Block Management and Page Allocation -------------*/ -+ -+static void yaffs_deinit_blocks(struct yaffs_dev *dev) -+{ -+ if (dev->block_info_alt && dev->block_info) -+ vfree(dev->block_info); -+ else -+ kfree(dev->block_info); -+ -+ dev->block_info_alt = 0; -+ -+ dev->block_info = NULL; -+ -+ if (dev->chunk_bits_alt && dev->chunk_bits) -+ vfree(dev->chunk_bits); -+ else -+ kfree(dev->chunk_bits); -+ dev->chunk_bits_alt = 0; -+ dev->chunk_bits = NULL; -+} -+ -+static int yaffs_init_blocks(struct yaffs_dev *dev) -+{ -+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ -+ dev->block_info = NULL; -+ dev->chunk_bits = NULL; -+ dev->alloc_block = -1; /* force it to get a new one */ -+ -+ /* If the first allocation strategy fails, thry the alternate one */ -+ dev->block_info = -+ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); -+ if (!dev->block_info) { -+ dev->block_info = -+ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); -+ dev->block_info_alt = 1; -+ } else { -+ dev->block_info_alt = 0; -+ } -+ -+ if (!dev->block_info) -+ goto alloc_error; -+ -+ /* Set up dynamic blockinfo stuff. Round up bytes. */ -+ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; -+ dev->chunk_bits = -+ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); -+ if (!dev->chunk_bits) { -+ dev->chunk_bits = -+ vmalloc(dev->chunk_bit_stride * n_blocks); -+ dev->chunk_bits_alt = 1; -+ } else { -+ dev->chunk_bits_alt = 0; -+ } -+ if (!dev->chunk_bits) -+ goto alloc_error; -+ -+ -+ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); -+ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); -+ return YAFFS_OK; -+ -+alloc_error: -+ yaffs_deinit_blocks(dev); -+ return YAFFS_FAIL; -+} -+ -+ -+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); -+ int erased_ok = 0; -+ int i; -+ -+ /* If the block is still healthy erase it and mark as clean. -+ * If the block has had a data failure, then retire it. -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, -+ "yaffs_block_became_dirty block %d state %d %s", -+ block_no, bi->block_state, -+ (bi->needs_retiring) ? "needs retiring" : ""); -+ -+ yaffs2_clear_oldest_dirty_seq(dev, bi); -+ -+ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; -+ -+ /* If this is the block being garbage collected then stop gc'ing */ -+ if (block_no == dev->gc_block) -+ dev->gc_block = 0; -+ -+ /* If this block is currently the best candidate for gc -+ * then drop as a candidate */ -+ if (block_no == dev->gc_dirtiest) { -+ dev->gc_dirtiest = 0; -+ dev->gc_pages_in_use = 0; -+ } -+ -+ if (!bi->needs_retiring) { -+ yaffs2_checkpt_invalidate(dev); -+ erased_ok = yaffs_erase_block(dev, block_no); -+ if (!erased_ok) { -+ dev->n_erase_failures++; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Erasure failed %d", block_no); -+ } -+ } -+ -+ /* Verify erasure if needed */ -+ if (erased_ok && -+ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || -+ !yaffs_skip_verification(dev))) { -+ for (i = 0; i < dev->param.chunks_per_block; i++) { -+ if (!yaffs_check_chunk_erased(dev, -+ block_no * dev->param.chunks_per_block + i)) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ ">>Block %d erasure supposedly OK, but chunk %d not erased", -+ block_no, i); -+ } -+ } -+ } -+ -+ if (!erased_ok) { -+ /* We lost a block of free space */ -+ dev->n_free_chunks -= dev->param.chunks_per_block; -+ yaffs_retire_block(dev, block_no); -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Block %d retired", block_no); -+ return; -+ } -+ -+ /* Clean it up... */ -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ bi->seq_number = 0; -+ dev->n_erased_blocks++; -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ bi->has_shrink_hdr = 0; -+ bi->skip_erased_check = 1; /* Clean, so no need to check */ -+ bi->gc_prioritise = 0; -+ bi->has_summary = 0; -+ -+ yaffs_clear_chunk_bits(dev, block_no); -+ -+ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); -+} -+ -+static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, -+ int old_chunk, u8 *buffer) -+{ -+ int new_chunk; -+ int mark_flash = 1; -+ struct yaffs_ext_tags tags; -+ struct yaffs_obj *object; -+ int matching_chunk; -+ int ret_val = YAFFS_OK; -+ -+ memset(&tags, 0, sizeof(tags)); -+ yaffs_rd_chunk_tags_nand(dev, old_chunk, -+ buffer, &tags); -+ object = yaffs_find_by_number(dev, tags.obj_id); -+ -+ yaffs_trace(YAFFS_TRACE_GC_DETAIL, -+ "Collecting chunk in block %d, %d %d %d ", -+ dev->gc_chunk, tags.obj_id, -+ tags.chunk_id, tags.n_bytes); -+ -+ if (object && !yaffs_skip_verification(dev)) { -+ if (tags.chunk_id == 0) -+ matching_chunk = -+ object->hdr_chunk; -+ else if (object->soft_del) -+ /* Defeat the test */ -+ matching_chunk = old_chunk; -+ else -+ matching_chunk = -+ yaffs_find_chunk_in_file -+ (object, tags.chunk_id, -+ NULL); -+ -+ if (old_chunk != matching_chunk) -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "gc: page in gc mismatch: %d %d %d %d", -+ old_chunk, -+ matching_chunk, -+ tags.obj_id, -+ tags.chunk_id); -+ } -+ -+ if (!object) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "page %d in gc has no object: %d %d %d ", -+ old_chunk, -+ tags.obj_id, tags.chunk_id, -+ tags.n_bytes); -+ } -+ -+ if (object && -+ object->deleted && -+ object->soft_del && tags.chunk_id != 0) { -+ /* Data chunk in a soft deleted file, -+ * throw it away. -+ * It's a soft deleted data chunk, -+ * No need to copy this, just forget -+ * about it and fix up the object. -+ */ -+ -+ /* Free chunks already includes -+ * softdeleted chunks, how ever this -+ * chunk is going to soon be really -+ * deleted which will increment free -+ * chunks. We have to decrement free -+ * chunks so this works out properly. -+ */ -+ dev->n_free_chunks--; -+ bi->soft_del_pages--; -+ -+ object->n_data_chunks--; -+ if (object->n_data_chunks <= 0) { -+ /* remeber to clean up obj */ -+ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; -+ dev->n_clean_ups++; -+ } -+ mark_flash = 0; -+ } else if (object) { -+ /* It's either a data chunk in a live -+ * file or an ObjectHeader, so we're -+ * interested in it. -+ * NB Need to keep the ObjectHeaders of -+ * deleted files until the whole file -+ * has been deleted off -+ */ -+ tags.serial_number++; -+ dev->n_gc_copies++; -+ -+ if (tags.chunk_id == 0) { -+ /* It is an object Id, -+ * We need to nuke the -+ * shrinkheader flags since its -+ * work is done. -+ * Also need to clean up -+ * shadowing. -+ */ -+ struct yaffs_obj_hdr *oh; -+ oh = (struct yaffs_obj_hdr *) buffer; -+ -+ oh->is_shrink = 0; -+ tags.extra_is_shrink = 0; -+ oh->shadows_obj = 0; -+ oh->inband_shadowed_obj_id = 0; -+ tags.extra_shadows = 0; -+ -+ /* Update file size */ -+ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { -+ yaffs_oh_size_load(oh, -+ object->variant.file_variant.file_size); -+ tags.extra_file_size = -+ object->variant.file_variant.file_size; -+ } -+ -+ yaffs_verify_oh(object, oh, &tags, 1); -+ new_chunk = -+ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); -+ } else { -+ new_chunk = -+ yaffs_write_new_chunk(dev, buffer, &tags, 1); -+ } -+ -+ if (new_chunk < 0) { -+ ret_val = YAFFS_FAIL; -+ } else { -+ -+ /* Now fix up the Tnodes etc. */ -+ -+ if (tags.chunk_id == 0) { -+ /* It's a header */ -+ object->hdr_chunk = new_chunk; -+ object->serial = tags.serial_number; -+ } else { -+ /* It's a data chunk */ -+ yaffs_put_chunk_in_file(object, tags.chunk_id, -+ new_chunk, 0); -+ } -+ } -+ } -+ if (ret_val == YAFFS_OK) -+ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); -+ return ret_val; -+} -+ -+static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) -+{ -+ int old_chunk; -+ int ret_val = YAFFS_OK; -+ int i; -+ int is_checkpt_block; -+ int max_copies; -+ int chunks_before = yaffs_get_erased_chunks(dev); -+ int chunks_after; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); -+ -+ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "Collecting block %d, in use %d, shrink %d, whole_block %d", -+ block, bi->pages_in_use, bi->has_shrink_hdr, -+ whole_block); -+ -+ /*yaffs_verify_free_chunks(dev); */ -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) -+ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; -+ -+ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ -+ -+ dev->gc_disable = 1; -+ -+ yaffs_summary_gc(dev, block); -+ -+ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "Collecting block %d that has no chunks in use", -+ block); -+ yaffs_block_became_dirty(dev, block); -+ } else { -+ -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ yaffs_verify_blk(dev, bi, block); -+ -+ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; -+ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; -+ -+ for (/* init already done */ ; -+ ret_val == YAFFS_OK && -+ dev->gc_chunk < dev->param.chunks_per_block && -+ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && -+ max_copies > 0; -+ dev->gc_chunk++, old_chunk++) { -+ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { -+ /* Page is in use and might need to be copied */ -+ max_copies--; -+ ret_val = yaffs_gc_process_chunk(dev, bi, -+ old_chunk, buffer); -+ } -+ } -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ -+ yaffs_verify_collected_blk(dev, bi, block); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { -+ /* -+ * The gc did not complete. Set block state back to FULL -+ * because checkpointing does not restore gc. -+ */ -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ } else { -+ /* The gc completed. */ -+ /* Do any required cleanups */ -+ for (i = 0; i < dev->n_clean_ups; i++) { -+ /* Time to delete the file too */ -+ struct yaffs_obj *object = -+ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); -+ if (object) { -+ yaffs_free_tnode(dev, -+ object->variant.file_variant.top); -+ object->variant.file_variant.top = NULL; -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: About to finally delete object %d", -+ object->obj_id); -+ yaffs_generic_obj_del(object); -+ object->my_dev->n_deleted_files--; -+ } -+ -+ } -+ chunks_after = yaffs_get_erased_chunks(dev); -+ if (chunks_before >= chunks_after) -+ yaffs_trace(YAFFS_TRACE_GC, -+ "gc did not increase free chunks before %d after %d", -+ chunks_before, chunks_after); -+ dev->gc_block = 0; -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ -+ dev->gc_disable = 0; -+ -+ return ret_val; -+} -+ -+/* -+ * find_gc_block() selects the dirtiest block (or close enough) -+ * for garbage collection. -+ */ -+ -+static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, -+ int aggressive, int background) -+{ -+ int i; -+ int iterations; -+ unsigned selected = 0; -+ int prioritised = 0; -+ int prioritised_exist = 0; -+ struct yaffs_block_info *bi; -+ int threshold; -+ -+ /* First let's see if we need to grab a prioritised block */ -+ if (dev->has_pending_prioritised_gc && !aggressive) { -+ dev->gc_dirtiest = 0; -+ bi = dev->block_info; -+ for (i = dev->internal_start_block; -+ i <= dev->internal_end_block && !selected; i++) { -+ -+ if (bi->gc_prioritise) { -+ prioritised_exist = 1; -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && -+ yaffs_block_ok_for_gc(dev, bi)) { -+ selected = i; -+ prioritised = 1; -+ } -+ } -+ bi++; -+ } -+ -+ /* -+ * If there is a prioritised block and none was selected then -+ * this happened because there is at least one old dirty block -+ * gumming up the works. Let's gc the oldest dirty block. -+ */ -+ -+ if (prioritised_exist && -+ !selected && dev->oldest_dirty_block > 0) -+ selected = dev->oldest_dirty_block; -+ -+ if (!prioritised_exist) /* None found, so we can clear this */ -+ dev->has_pending_prioritised_gc = 0; -+ } -+ -+ /* If we're doing aggressive GC then we are happy to take a less-dirty -+ * block, and search harder. -+ * else (leasurely gc), then we only bother to do this if the -+ * block has only a few pages in use. -+ */ -+ -+ if (!selected) { -+ int pages_used; -+ int n_blocks = -+ dev->internal_end_block - dev->internal_start_block + 1; -+ if (aggressive) { -+ threshold = dev->param.chunks_per_block; -+ iterations = n_blocks; -+ } else { -+ int max_threshold; -+ -+ if (background) -+ max_threshold = dev->param.chunks_per_block / 2; -+ else -+ max_threshold = dev->param.chunks_per_block / 8; -+ -+ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) -+ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; -+ -+ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; -+ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) -+ threshold = YAFFS_GC_PASSIVE_THRESHOLD; -+ if (threshold > max_threshold) -+ threshold = max_threshold; -+ -+ iterations = n_blocks / 16 + 1; -+ if (iterations > 100) -+ iterations = 100; -+ } -+ -+ for (i = 0; -+ i < iterations && -+ (dev->gc_dirtiest < 1 || -+ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); -+ i++) { -+ dev->gc_block_finder++; -+ if (dev->gc_block_finder < dev->internal_start_block || -+ dev->gc_block_finder > dev->internal_end_block) -+ dev->gc_block_finder = -+ dev->internal_start_block; -+ -+ bi = yaffs_get_block_info(dev, dev->gc_block_finder); -+ -+ pages_used = bi->pages_in_use - bi->soft_del_pages; -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && -+ pages_used < dev->param.chunks_per_block && -+ (dev->gc_dirtiest < 1 || -+ pages_used < dev->gc_pages_in_use) && -+ yaffs_block_ok_for_gc(dev, bi)) { -+ dev->gc_dirtiest = dev->gc_block_finder; -+ dev->gc_pages_in_use = pages_used; -+ } -+ } -+ -+ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) -+ selected = dev->gc_dirtiest; -+ } -+ -+ /* -+ * If nothing has been selected for a while, try the oldest dirty -+ * because that's gumming up the works. -+ */ -+ -+ if (!selected && dev->param.is_yaffs2 && -+ dev->gc_not_done >= (background ? 10 : 20)) { -+ yaffs2_find_oldest_dirty_seq(dev); -+ if (dev->oldest_dirty_block > 0) { -+ selected = dev->oldest_dirty_block; -+ dev->gc_dirtiest = selected; -+ dev->oldest_dirty_gc_count++; -+ bi = yaffs_get_block_info(dev, selected); -+ dev->gc_pages_in_use = -+ bi->pages_in_use - bi->soft_del_pages; -+ } else { -+ dev->gc_not_done = 0; -+ } -+ } -+ -+ if (selected) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC Selected block %d with %d free, prioritised:%d", -+ selected, -+ dev->param.chunks_per_block - dev->gc_pages_in_use, -+ prioritised); -+ -+ dev->n_gc_blocks++; -+ if (background) -+ dev->bg_gcs++; -+ -+ dev->gc_dirtiest = 0; -+ dev->gc_pages_in_use = 0; -+ dev->gc_not_done = 0; -+ if (dev->refresh_skip > 0) -+ dev->refresh_skip--; -+ } else { -+ dev->gc_not_done++; -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", -+ dev->gc_block_finder, dev->gc_not_done, threshold, -+ dev->gc_dirtiest, dev->gc_pages_in_use, -+ dev->oldest_dirty_block, background ? " bg" : ""); -+ } -+ -+ return selected; -+} -+ -+/* New garbage collector -+ * If we're very low on erased blocks then we do aggressive garbage collection -+ * otherwise we do "leasurely" garbage collection. -+ * Aggressive gc looks further (whole array) and will accept less dirty blocks. -+ * Passive gc only inspects smaller areas and only accepts more dirty blocks. -+ * -+ * The idea is to help clear out space in a more spread-out manner. -+ * Dunno if it really does anything useful. -+ */ -+static int yaffs_check_gc(struct yaffs_dev *dev, int background) -+{ -+ int aggressive = 0; -+ int gc_ok = YAFFS_OK; -+ int max_tries = 0; -+ int min_erased; -+ int erased_chunks; -+ int checkpt_block_adjust; -+ -+ if (dev->param.gc_control_fn && -+ (dev->param.gc_control_fn(dev) & 1) == 0) -+ return YAFFS_OK; -+ -+ if (dev->gc_disable) -+ /* Bail out so we don't get recursive gc */ -+ return YAFFS_OK; -+ -+ /* This loop should pass the first time. -+ * Only loops here if the collection does not increase space. -+ */ -+ -+ do { -+ max_tries++; -+ -+ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); -+ -+ min_erased = -+ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; -+ erased_chunks = -+ dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ /* If we need a block soon then do aggressive gc. */ -+ if (dev->n_erased_blocks < min_erased) -+ aggressive = 1; -+ else { -+ if (!background -+ && erased_chunks > (dev->n_free_chunks / 4)) -+ break; -+ -+ if (dev->gc_skip > 20) -+ dev->gc_skip = 20; -+ if (erased_chunks < dev->n_free_chunks / 2 || -+ dev->gc_skip < 1 || background) -+ aggressive = 0; -+ else { -+ dev->gc_skip--; -+ break; -+ } -+ } -+ -+ dev->gc_skip = 5; -+ -+ /* If we don't already have a block being gc'd then see if we -+ * should start another */ -+ -+ if (dev->gc_block < 1 && !aggressive) { -+ dev->gc_block = yaffs2_find_refresh_block(dev); -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ if (dev->gc_block < 1) { -+ dev->gc_block = -+ yaffs_find_gc_block(dev, aggressive, background); -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ -+ if (dev->gc_block > 0) { -+ dev->all_gcs++; -+ if (!aggressive) -+ dev->passive_gc_count++; -+ -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: GC n_erased_blocks %d aggressive %d", -+ dev->n_erased_blocks, aggressive); -+ -+ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); -+ } -+ -+ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && -+ dev->gc_block > 0) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", -+ dev->n_erased_blocks, max_tries, -+ dev->gc_block); -+ } -+ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && -+ (dev->gc_block > 0) && (max_tries < 2)); -+ -+ return aggressive ? gc_ok : YAFFS_OK; -+} -+ -+/* -+ * yaffs_bg_gc() -+ * Garbage collects. Intended to be called from a background thread. -+ * Returns non-zero if at least half the free chunks are erased. -+ */ -+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) -+{ -+ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); -+ -+ yaffs_check_gc(dev, 1); -+ return erased_chunks > dev->n_free_chunks / 2; -+} -+ -+/*-------------------- Data file manipulation -----------------*/ -+ -+static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) -+{ -+ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); -+ -+ if (nand_chunk >= 0) -+ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, -+ buffer, NULL); -+ else { -+ yaffs_trace(YAFFS_TRACE_NANDACCESS, -+ "Chunk %d not found zero instead", -+ nand_chunk); -+ /* get sane (zero) data if you read a hole */ -+ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); -+ return 0; -+ } -+ -+} -+ -+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, -+ int lyn) -+{ -+ int block; -+ int page; -+ struct yaffs_ext_tags tags; -+ struct yaffs_block_info *bi; -+ -+ if (chunk_id <= 0) -+ return; -+ -+ dev->n_deletions++; -+ block = chunk_id / dev->param.chunks_per_block; -+ page = chunk_id % dev->param.chunks_per_block; -+ -+ if (!yaffs_check_chunk_bit(dev, block, page)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Deleting invalid chunk %d", chunk_id); -+ -+ bi = yaffs_get_block_info(dev, block); -+ -+ yaffs2_update_oldest_dirty_seq(dev, block, bi); -+ -+ yaffs_trace(YAFFS_TRACE_DELETION, -+ "line %d delete of chunk %d", -+ lyn, chunk_id); -+ -+ if (!dev->param.is_yaffs2 && mark_flash && -+ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { -+ -+ memset(&tags, 0, sizeof(tags)); -+ tags.is_deleted = 1; -+ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); -+ yaffs_handle_chunk_update(dev, chunk_id, &tags); -+ } else { -+ dev->n_unmarked_deletions++; -+ } -+ -+ /* Pull out of the management area. -+ * If the whole block became dirty, this will kick off an erasure. -+ */ -+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || -+ bi->block_state == YAFFS_BLOCK_STATE_FULL || -+ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { -+ dev->n_free_chunks++; -+ yaffs_clear_chunk_bit(dev, block, page); -+ bi->pages_in_use--; -+ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && -+ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ yaffs_block_became_dirty(dev, block); -+ } -+ } -+} -+ -+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, -+ const u8 *buffer, int n_bytes, int use_reserve) -+{ -+ /* Find old chunk Need to do this to get serial number -+ * Write new one and patch into tree. -+ * Invalidate old tags. -+ */ -+ -+ int prev_chunk_id; -+ struct yaffs_ext_tags prev_tags; -+ int new_chunk_id; -+ struct yaffs_ext_tags new_tags; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ yaffs_check_gc(dev, 0); -+ -+ /* Get the previous chunk at this location in the file if it exists. -+ * If it does not exist then put a zero into the tree. This creates -+ * the tnode now, rather than later when it is harder to clean up. -+ */ -+ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); -+ if (prev_chunk_id < 1 && -+ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) -+ return 0; -+ -+ /* Set up new tags */ -+ memset(&new_tags, 0, sizeof(new_tags)); -+ -+ new_tags.chunk_id = inode_chunk; -+ new_tags.obj_id = in->obj_id; -+ new_tags.serial_number = -+ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; -+ new_tags.n_bytes = n_bytes; -+ -+ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Writing %d bytes to chunk!!!!!!!!!", -+ n_bytes); -+ BUG(); -+ } -+ -+ new_chunk_id = -+ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); -+ -+ if (new_chunk_id > 0) { -+ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); -+ -+ if (prev_chunk_id > 0) -+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -+ -+ yaffs_verify_file_sane(in); -+ } -+ return new_chunk_id; -+ -+} -+ -+ -+ -+static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, -+ const YCHAR *name, const void *value, int size, -+ int flags) -+{ -+ struct yaffs_xattr_mod xmod; -+ int result; -+ -+ xmod.set = set; -+ xmod.name = name; -+ xmod.data = value; -+ xmod.size = size; -+ xmod.flags = flags; -+ xmod.result = -ENOSPC; -+ -+ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); -+ -+ if (result > 0) -+ return xmod.result; -+ else -+ return -ENOSPC; -+} -+ -+static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, -+ struct yaffs_xattr_mod *xmod) -+{ -+ int retval = 0; -+ int x_offs = sizeof(struct yaffs_obj_hdr); -+ struct yaffs_dev *dev = obj->my_dev; -+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); -+ char *x_buffer = buffer + x_offs; -+ -+ if (xmod->set) -+ retval = -+ nval_set(x_buffer, x_size, xmod->name, xmod->data, -+ xmod->size, xmod->flags); -+ else -+ retval = nval_del(x_buffer, x_size, xmod->name); -+ -+ obj->has_xattr = nval_hasvalues(x_buffer, x_size); -+ obj->xattr_known = 1; -+ xmod->result = retval; -+ -+ return retval; -+} -+ -+static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, -+ void *value, int size) -+{ -+ char *buffer = NULL; -+ int result; -+ struct yaffs_ext_tags tags; -+ struct yaffs_dev *dev = obj->my_dev; -+ int x_offs = sizeof(struct yaffs_obj_hdr); -+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); -+ char *x_buffer; -+ int retval = 0; -+ -+ if (obj->hdr_chunk < 1) -+ return -ENODATA; -+ -+ /* If we know that the object has no xattribs then don't do all the -+ * reading and parsing. -+ */ -+ if (obj->xattr_known && !obj->has_xattr) { -+ if (name) -+ return -ENODATA; -+ else -+ return 0; -+ } -+ -+ buffer = (char *)yaffs_get_temp_buffer(dev); -+ if (!buffer) -+ return -ENOMEM; -+ -+ result = -+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); -+ -+ if (result != YAFFS_OK) -+ retval = -ENOENT; -+ else { -+ x_buffer = buffer + x_offs; -+ -+ if (!obj->xattr_known) { -+ obj->has_xattr = nval_hasvalues(x_buffer, x_size); -+ obj->xattr_known = 1; -+ } -+ -+ if (name) -+ retval = nval_get(x_buffer, x_size, name, value, size); -+ else -+ retval = nval_list(x_buffer, x_size, value, size); -+ } -+ yaffs_release_temp_buffer(dev, (u8 *) buffer); -+ return retval; -+} -+ -+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, -+ const void *value, int size, int flags) -+{ -+ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); -+} -+ -+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) -+{ -+ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); -+} -+ -+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, -+ int size) -+{ -+ return yaffs_do_xattrib_fetch(obj, name, value, size); -+} -+ -+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) -+{ -+ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); -+} -+ -+static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) -+{ -+ u8 *buf; -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_dev *dev; -+ struct yaffs_ext_tags tags; -+ int result; -+ int alloc_failed = 0; -+ -+ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) -+ return; -+ -+ dev = in->my_dev; -+ in->lazy_loaded = 0; -+ buf = yaffs_get_temp_buffer(dev); -+ -+ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); -+ oh = (struct yaffs_obj_hdr *)buf; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ yaffs_set_obj_name_from_oh(in, oh); -+ -+ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ in->variant.symlink_variant.alias = -+ yaffs_clone_str(oh->alias); -+ if (!in->variant.symlink_variant.alias) -+ alloc_failed = 1; /* Not returned */ -+ } -+ yaffs_release_temp_buffer(dev, buf); -+} -+ -+static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, -+ const YCHAR *oh_name, int buff_size) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ if (dev->param.auto_unicode) { -+ if (*oh_name) { -+ /* It is an ASCII name, do an ASCII to -+ * unicode conversion */ -+ const char *ascii_oh_name = (const char *)oh_name; -+ int n = buff_size - 1; -+ while (n > 0 && *ascii_oh_name) { -+ *name = *ascii_oh_name; -+ name++; -+ ascii_oh_name++; -+ n--; -+ } -+ } else { -+ strncpy(name, oh_name + 1, buff_size - 1); -+ } -+ } else { -+#else -+ (void) dev; -+ { -+#endif -+ strncpy(name, oh_name, buff_size - 1); -+ } -+} -+ -+static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, -+ const YCHAR *name) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ -+ int is_ascii; -+ YCHAR *w; -+ -+ if (dev->param.auto_unicode) { -+ -+ is_ascii = 1; -+ w = name; -+ -+ /* Figure out if the name will fit in ascii character set */ -+ while (is_ascii && *w) { -+ if ((*w) & 0xff00) -+ is_ascii = 0; -+ w++; -+ } -+ -+ if (is_ascii) { -+ /* It is an ASCII name, so convert unicode to ascii */ -+ char *ascii_oh_name = (char *)oh_name; -+ int n = YAFFS_MAX_NAME_LENGTH - 1; -+ while (n > 0 && *name) { -+ *ascii_oh_name = *name; -+ name++; -+ ascii_oh_name++; -+ n--; -+ } -+ } else { -+ /* Unicode name, so save starting at the second YCHAR */ -+ *oh_name = 0; -+ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); -+ } -+ } else { -+#else -+ dev = dev; -+ { -+#endif -+ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); -+ } -+} -+ -+/* UpdateObjectHeader updates the header on NAND for an object. -+ * If name is not NULL, then that new name is used. -+ */ -+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, -+ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) -+{ -+ -+ struct yaffs_block_info *bi; -+ struct yaffs_dev *dev = in->my_dev; -+ int prev_chunk_id; -+ int ret_val = 0; -+ int result = 0; -+ int new_chunk_id; -+ struct yaffs_ext_tags new_tags; -+ struct yaffs_ext_tags old_tags; -+ const YCHAR *alias = NULL; -+ u8 *buffer = NULL; -+ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; -+ struct yaffs_obj_hdr *oh = NULL; -+ loff_t file_size = 0; -+ -+ strcpy(old_name, _Y("silly old name")); -+ -+ if (in->fake && in != dev->root_dir && !force && !xmod) -+ return ret_val; -+ -+ yaffs_check_gc(dev, 0); -+ yaffs_check_obj_details_loaded(in); -+ -+ buffer = yaffs_get_temp_buffer(in->my_dev); -+ oh = (struct yaffs_obj_hdr *)buffer; -+ -+ prev_chunk_id = in->hdr_chunk; -+ -+ if (prev_chunk_id > 0) { -+ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, -+ buffer, &old_tags); -+ -+ yaffs_verify_oh(in, oh, &old_tags, 0); -+ memcpy(old_name, oh->name, sizeof(oh->name)); -+ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); -+ } else { -+ memset(buffer, 0xff, dev->data_bytes_per_chunk); -+ } -+ -+ oh->type = in->variant_type; -+ oh->yst_mode = in->yst_mode; -+ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; -+ -+ yaffs_load_attribs_oh(oh, in); -+ -+ if (in->parent) -+ oh->parent_obj_id = in->parent->obj_id; -+ else -+ oh->parent_obj_id = 0; -+ -+ if (name && *name) { -+ memset(oh->name, 0, sizeof(oh->name)); -+ yaffs_load_oh_from_name(dev, oh->name, name); -+ } else if (prev_chunk_id > 0) { -+ memcpy(oh->name, old_name, sizeof(oh->name)); -+ } else { -+ memset(oh->name, 0, sizeof(oh->name)); -+ } -+ -+ oh->is_shrink = is_shrink; -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Should not happen */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && -+ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) -+ file_size = in->variant.file_variant.file_size; -+ yaffs_oh_size_load(oh, file_size); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ oh->equiv_id = in->variant.hardlink_variant.equiv_id; -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ alias = in->variant.symlink_variant.alias; -+ if (!alias) -+ alias = _Y("no alias"); -+ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); -+ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; -+ break; -+ } -+ -+ /* process any xattrib modifications */ -+ if (xmod) -+ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); -+ -+ /* Tags */ -+ memset(&new_tags, 0, sizeof(new_tags)); -+ in->serial++; -+ new_tags.chunk_id = 0; -+ new_tags.obj_id = in->obj_id; -+ new_tags.serial_number = in->serial; -+ -+ /* Add extra info for file header */ -+ new_tags.extra_available = 1; -+ new_tags.extra_parent_id = oh->parent_obj_id; -+ new_tags.extra_file_size = file_size; -+ new_tags.extra_is_shrink = oh->is_shrink; -+ new_tags.extra_equiv_id = oh->equiv_id; -+ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; -+ new_tags.extra_obj_type = in->variant_type; -+ yaffs_verify_oh(in, oh, &new_tags, 1); -+ -+ /* Create new chunk in NAND */ -+ new_chunk_id = -+ yaffs_write_new_chunk(dev, buffer, &new_tags, -+ (prev_chunk_id > 0) ? 1 : 0); -+ -+ if (buffer) -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ if (new_chunk_id < 0) -+ return new_chunk_id; -+ -+ in->hdr_chunk = new_chunk_id; -+ -+ if (prev_chunk_id > 0) -+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -+ -+ if (!yaffs_obj_cache_dirty(in)) -+ in->dirty = 0; -+ -+ /* If this was a shrink, then mark the block -+ * that the chunk lives on */ -+ if (is_shrink) { -+ bi = yaffs_get_block_info(in->my_dev, -+ new_chunk_id / -+ in->my_dev->param.chunks_per_block); -+ bi->has_shrink_hdr = 1; -+ } -+ -+ -+ return new_chunk_id; -+} -+ -+/*--------------------- File read/write ------------------------ -+ * Read and write have very similar structures. -+ * In general the read/write has three parts to it -+ * An incomplete chunk to start with (if the read/write is not chunk-aligned) -+ * Some complete chunks -+ * An incomplete chunk to end off with -+ * -+ * Curve-balls: the first chunk might also be the last chunk. -+ */ -+ -+int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) -+{ -+ int chunk; -+ u32 start; -+ int n_copy; -+ int n = n_bytes; -+ int n_done = 0; -+ struct yaffs_cache *cache; -+ struct yaffs_dev *dev; -+ -+ dev = in->my_dev; -+ -+ while (n > 0) { -+ yaffs_addr_to_chunk(dev, offset, &chunk, &start); -+ chunk++; -+ -+ /* OK now check for the curveball where the start and end are in -+ * the same chunk. -+ */ -+ if ((start + n) < dev->data_bytes_per_chunk) -+ n_copy = n; -+ else -+ n_copy = dev->data_bytes_per_chunk - start; -+ -+ cache = yaffs_find_chunk_cache(in, chunk); -+ -+ /* If the chunk is already in the cache or it is less than -+ * a whole chunk or we're using inband tags then use the cache -+ * (if there is caching) else bypass the cache. -+ */ -+ if (cache || n_copy != dev->data_bytes_per_chunk || -+ dev->param.inband_tags) { -+ if (dev->param.n_caches > 0) { -+ -+ /* If we can't find the data in the cache, -+ * then load it up. */ -+ -+ if (!cache) { -+ cache = -+ yaffs_grab_chunk_cache(in->my_dev); -+ cache->object = in; -+ cache->chunk_id = chunk; -+ cache->dirty = 0; -+ cache->locked = 0; -+ yaffs_rd_data_obj(in, chunk, -+ cache->data); -+ cache->n_bytes = 0; -+ } -+ -+ yaffs_use_cache(dev, cache, 0); -+ -+ cache->locked = 1; -+ -+ memcpy(buffer, &cache->data[start], n_copy); -+ -+ cache->locked = 0; -+ } else { -+ /* Read into the local buffer then copy.. */ -+ -+ u8 *local_buffer = -+ yaffs_get_temp_buffer(dev); -+ yaffs_rd_data_obj(in, chunk, local_buffer); -+ -+ memcpy(buffer, &local_buffer[start], n_copy); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ } else { -+ /* A full chunk. Read directly into the buffer. */ -+ yaffs_rd_data_obj(in, chunk, buffer); -+ } -+ n -= n_copy; -+ offset += n_copy; -+ buffer += n_copy; -+ n_done += n_copy; -+ } -+ return n_done; -+} -+ -+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_through) -+{ -+ -+ int chunk; -+ u32 start; -+ int n_copy; -+ int n = n_bytes; -+ int n_done = 0; -+ int n_writeback; -+ loff_t start_write = offset; -+ int chunk_written = 0; -+ u32 n_bytes_read; -+ loff_t chunk_start; -+ struct yaffs_dev *dev; -+ -+ dev = in->my_dev; -+ -+ while (n > 0 && chunk_written >= 0) { -+ yaffs_addr_to_chunk(dev, offset, &chunk, &start); -+ -+ if (((loff_t)chunk) * -+ dev->data_bytes_per_chunk + start != offset || -+ start >= dev->data_bytes_per_chunk) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "AddrToChunk of offset %lld gives chunk %d start %d", -+ offset, chunk, start); -+ } -+ chunk++; /* File pos to chunk in file offset */ -+ -+ /* OK now check for the curveball where the start and end are in -+ * the same chunk. -+ */ -+ -+ if ((start + n) < dev->data_bytes_per_chunk) { -+ n_copy = n; -+ -+ /* Now calculate how many bytes to write back.... -+ * If we're overwriting and not writing to then end of -+ * file then we need to write back as much as was there -+ * before. -+ */ -+ -+ chunk_start = (((loff_t)(chunk - 1)) * -+ dev->data_bytes_per_chunk); -+ -+ if (chunk_start > in->variant.file_variant.file_size) -+ n_bytes_read = 0; /* Past end of file */ -+ else -+ n_bytes_read = -+ in->variant.file_variant.file_size - -+ chunk_start; -+ -+ if (n_bytes_read > dev->data_bytes_per_chunk) -+ n_bytes_read = dev->data_bytes_per_chunk; -+ -+ n_writeback = -+ (n_bytes_read > -+ (start + n)) ? n_bytes_read : (start + n); -+ -+ if (n_writeback < 0 || -+ n_writeback > dev->data_bytes_per_chunk) -+ BUG(); -+ -+ } else { -+ n_copy = dev->data_bytes_per_chunk - start; -+ n_writeback = dev->data_bytes_per_chunk; -+ } -+ -+ if (n_copy != dev->data_bytes_per_chunk || -+ !dev->param.cache_bypass_aligned || -+ dev->param.inband_tags) { -+ /* An incomplete start or end chunk (or maybe both -+ * start and end chunk), or we're using inband tags, -+ * or we're forcing writes through the cache, -+ * so we want to use the cache buffers. -+ */ -+ if (dev->param.n_caches > 0) { -+ struct yaffs_cache *cache; -+ -+ /* If we can't find the data in the cache, then -+ * load the cache */ -+ cache = yaffs_find_chunk_cache(in, chunk); -+ -+ if (!cache && -+ yaffs_check_alloc_available(dev, 1)) { -+ cache = yaffs_grab_chunk_cache(dev); -+ cache->object = in; -+ cache->chunk_id = chunk; -+ cache->dirty = 0; -+ cache->locked = 0; -+ yaffs_rd_data_obj(in, chunk, -+ cache->data); -+ } else if (cache && -+ !cache->dirty && -+ !yaffs_check_alloc_available(dev, -+ 1)) { -+ /* Drop the cache if it was a read cache -+ * item and no space check has been made -+ * for it. -+ */ -+ cache = NULL; -+ } -+ -+ if (cache) { -+ yaffs_use_cache(dev, cache, 1); -+ cache->locked = 1; -+ -+ memcpy(&cache->data[start], buffer, -+ n_copy); -+ -+ cache->locked = 0; -+ cache->n_bytes = n_writeback; -+ -+ if (write_through) { -+ chunk_written = -+ yaffs_wr_data_obj -+ (cache->object, -+ cache->chunk_id, -+ cache->data, -+ cache->n_bytes, 1); -+ cache->dirty = 0; -+ } -+ } else { -+ chunk_written = -1; /* fail write */ -+ } -+ } else { -+ /* An incomplete start or end chunk (or maybe -+ * both start and end chunk). Read into the -+ * local buffer then copy over and write back. -+ */ -+ -+ u8 *local_buffer = yaffs_get_temp_buffer(dev); -+ -+ yaffs_rd_data_obj(in, chunk, local_buffer); -+ memcpy(&local_buffer[start], buffer, n_copy); -+ -+ chunk_written = -+ yaffs_wr_data_obj(in, chunk, -+ local_buffer, -+ n_writeback, 0); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ } else { -+ /* A full chunk. Write directly from the buffer. */ -+ -+ chunk_written = -+ yaffs_wr_data_obj(in, chunk, buffer, -+ dev->data_bytes_per_chunk, 0); -+ -+ /* Since we've overwritten the cached data, -+ * we better invalidate it. */ -+ yaffs_invalidate_chunk_cache(in, chunk); -+ } -+ -+ if (chunk_written >= 0) { -+ n -= n_copy; -+ offset += n_copy; -+ buffer += n_copy; -+ n_done += n_copy; -+ } -+ } -+ -+ /* Update file object */ -+ -+ if ((start_write + n_done) > in->variant.file_variant.file_size) -+ in->variant.file_variant.file_size = (start_write + n_done); -+ -+ in->dirty = 1; -+ return n_done; -+} -+ -+int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_through) -+{ -+ yaffs2_handle_hole(in, offset); -+ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); -+} -+ -+/* ---------------------- File resizing stuff ------------------ */ -+ -+static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) -+{ -+ -+ struct yaffs_dev *dev = in->my_dev; -+ loff_t old_size = in->variant.file_variant.file_size; -+ int i; -+ int chunk_id; -+ u32 dummy; -+ int last_del; -+ int start_del; -+ -+ if (old_size > 0) -+ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); -+ else -+ last_del = 0; -+ -+ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, -+ &start_del, &dummy); -+ last_del++; -+ start_del++; -+ -+ /* Delete backwards so that we don't end up with holes if -+ * power is lost part-way through the operation. -+ */ -+ for (i = last_del; i >= start_del; i--) { -+ /* NB this could be optimised somewhat, -+ * eg. could retrieve the tags and write them without -+ * using yaffs_chunk_del -+ */ -+ -+ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); -+ -+ if (chunk_id < 1) -+ continue; -+ -+ if (chunk_id < -+ (dev->internal_start_block * dev->param.chunks_per_block) || -+ chunk_id >= -+ ((dev->internal_end_block + 1) * -+ dev->param.chunks_per_block)) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Found daft chunk_id %d for %d", -+ chunk_id, i); -+ } else { -+ in->n_data_chunks--; -+ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); -+ } -+ } -+} -+ -+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) -+{ -+ int new_full; -+ u32 new_partial; -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); -+ -+ yaffs_prune_chunks(obj, new_size); -+ -+ if (new_partial != 0) { -+ int last_chunk = 1 + new_full; -+ u8 *local_buffer = yaffs_get_temp_buffer(dev); -+ -+ /* Rewrite the last chunk with its new size and zero pad */ -+ yaffs_rd_data_obj(obj, last_chunk, local_buffer); -+ memset(local_buffer + new_partial, 0, -+ dev->data_bytes_per_chunk - new_partial); -+ -+ yaffs_wr_data_obj(obj, last_chunk, local_buffer, -+ new_partial, 1); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ -+ obj->variant.file_variant.file_size = new_size; -+ -+ yaffs_prune_tree(dev, &obj->variant.file_variant); -+} -+ -+int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) -+{ -+ struct yaffs_dev *dev = in->my_dev; -+ loff_t old_size = in->variant.file_variant.file_size; -+ -+ yaffs_flush_file_cache(in); -+ yaffs_invalidate_whole_cache(in); -+ -+ yaffs_check_gc(dev, 0); -+ -+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return YAFFS_FAIL; -+ -+ if (new_size == old_size) -+ return YAFFS_OK; -+ -+ if (new_size > old_size) { -+ yaffs2_handle_hole(in, new_size); -+ in->variant.file_variant.file_size = new_size; -+ } else { -+ /* new_size < old_size */ -+ yaffs_resize_file_down(in, new_size); -+ } -+ -+ /* Write a new object header to reflect the resize. -+ * show we've shrunk the file, if need be -+ * Do this only if the file is not in the deleted directories -+ * and is not shadowed. -+ */ -+ if (in->parent && -+ !in->is_shadowed && -+ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && -+ in->parent->obj_id != YAFFS_OBJECTID_DELETED) -+ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); -+ -+ return YAFFS_OK; -+} -+ -+int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync) -+{ -+ if (!in->dirty) -+ return YAFFS_OK; -+ -+ yaffs_flush_file_cache(in); -+ -+ if (data_sync) -+ return YAFFS_OK; -+ -+ if (update_time) -+ yaffs_load_current_time(in, 0, 0); -+ -+ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? -+ YAFFS_OK : YAFFS_FAIL; -+} -+ -+ -+/* yaffs_del_file deletes the whole file data -+ * and the inode associated with the file. -+ * It does not delete the links associated with the file. -+ */ -+static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) -+{ -+ int ret_val; -+ int del_now = 0; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!in->my_inode) -+ del_now = 1; -+ -+ if (del_now) { -+ ret_val = -+ yaffs_change_obj_name(in, in->my_dev->del_dir, -+ _Y("deleted"), 0, 0); -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: immediate deletion of file %d", -+ in->obj_id); -+ in->deleted = 1; -+ in->my_dev->n_deleted_files++; -+ if (dev->param.disable_soft_del || dev->param.is_yaffs2) -+ yaffs_resize_file(in, 0); -+ yaffs_soft_del_file(in); -+ } else { -+ ret_val = -+ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, -+ _Y("unlinked"), 0, 0); -+ } -+ return ret_val; -+} -+ -+static int yaffs_del_file(struct yaffs_obj *in) -+{ -+ int ret_val = YAFFS_OK; -+ int deleted; /* Need to cache value on stack if in is freed */ -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (dev->param.disable_soft_del || dev->param.is_yaffs2) -+ yaffs_resize_file(in, 0); -+ -+ if (in->n_data_chunks > 0) { -+ /* Use soft deletion if there is data in the file. -+ * That won't be the case if it has been resized to zero. -+ */ -+ if (!in->unlinked) -+ ret_val = yaffs_unlink_file_if_needed(in); -+ -+ deleted = in->deleted; -+ -+ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { -+ in->deleted = 1; -+ deleted = 1; -+ in->my_dev->n_deleted_files++; -+ yaffs_soft_del_file(in); -+ } -+ return deleted ? YAFFS_OK : YAFFS_FAIL; -+ } else { -+ /* The file has no data chunks so we toss it immediately */ -+ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); -+ in->variant.file_variant.top = NULL; -+ yaffs_generic_obj_del(in); -+ -+ return YAFFS_OK; -+ } -+} -+ -+int yaffs_is_non_empty_dir(struct yaffs_obj *obj) -+{ -+ return (obj && -+ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && -+ !(list_empty(&obj->variant.dir_variant.children)); -+} -+ -+static int yaffs_del_dir(struct yaffs_obj *obj) -+{ -+ /* First check that the directory is empty. */ -+ if (yaffs_is_non_empty_dir(obj)) -+ return YAFFS_FAIL; -+ -+ return yaffs_generic_obj_del(obj); -+} -+ -+static int yaffs_del_symlink(struct yaffs_obj *in) -+{ -+ kfree(in->variant.symlink_variant.alias); -+ in->variant.symlink_variant.alias = NULL; -+ -+ return yaffs_generic_obj_del(in); -+} -+ -+static int yaffs_del_link(struct yaffs_obj *in) -+{ -+ /* remove this hardlink from the list associated with the equivalent -+ * object -+ */ -+ list_del_init(&in->hard_links); -+ return yaffs_generic_obj_del(in); -+} -+ -+int yaffs_del_obj(struct yaffs_obj *obj) -+{ -+ int ret_val = -1; -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ ret_val = yaffs_del_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ if (!list_empty(&obj->variant.dir_variant.dirty)) { -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "Remove object %d from dirty directories", -+ obj->obj_id); -+ list_del_init(&obj->variant.dir_variant.dirty); -+ } -+ return yaffs_del_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ ret_val = yaffs_del_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ ret_val = yaffs_del_link(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ ret_val = yaffs_generic_obj_del(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ ret_val = 0; -+ break; /* should not happen. */ -+ } -+ return ret_val; -+} -+ -+ -+static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, -+ struct yaffs_obj *to_dir) -+{ -+ struct yaffs_obj *obj; -+ struct list_head *lh; -+ struct list_head *n; -+ -+ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ yaffs_add_obj_to_dir(to_dir, obj); -+ } -+} -+ -+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, -+ enum yaffs_obj_type type) -+{ -+ /* Tear down the old variant */ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ /* Nuke file data */ -+ yaffs_resize_file(obj, 0); -+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); -+ obj->variant.file_variant.top = NULL; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Put the children in lost and found. */ -+ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); -+ if (!list_empty(&obj->variant.dir_variant.dirty)) -+ list_del_init(&obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ /* Nuke symplink data */ -+ kfree(obj->variant.symlink_variant.alias); -+ obj->variant.symlink_variant.alias = NULL; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ list_del_init(&obj->hard_links); -+ break; -+ default: -+ break; -+ } -+ -+ memset(&obj->variant, 0, sizeof(obj->variant)); -+ -+ /*Set up new variant if the memset is not enough. */ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ INIT_LIST_HEAD(&obj->variant.dir_variant.children); -+ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ default: -+ break; -+ } -+ -+ obj->variant_type = type; -+ -+ return obj; -+ -+} -+ -+static int yaffs_unlink_worker(struct yaffs_obj *obj) -+{ -+ int del_now = 0; -+ -+ if (!obj) -+ return YAFFS_FAIL; -+ -+ if (!obj->my_inode) -+ del_now = 1; -+ -+ yaffs_update_parent(obj->parent); -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { -+ return yaffs_del_link(obj); -+ } else if (!list_empty(&obj->hard_links)) { -+ /* Curve ball: We're unlinking an object that has a hardlink. -+ * -+ * This problem arises because we are not strictly following -+ * The Linux link/inode model. -+ * -+ * We can't really delete the object. -+ * Instead, we do the following: -+ * - Select a hardlink. -+ * - Unhook it from the hard links -+ * - Move it from its parent directory so that the rename works. -+ * - Rename the object to the hardlink's name. -+ * - Delete the hardlink -+ */ -+ -+ struct yaffs_obj *hl; -+ struct yaffs_obj *parent; -+ int ret_val; -+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ hl = list_entry(obj->hard_links.next, struct yaffs_obj, -+ hard_links); -+ -+ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); -+ parent = hl->parent; -+ -+ list_del_init(&hl->hard_links); -+ -+ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); -+ -+ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); -+ -+ if (ret_val == YAFFS_OK) -+ ret_val = yaffs_generic_obj_del(hl); -+ -+ return ret_val; -+ -+ } else if (del_now) { -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return yaffs_del_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ list_del_init(&obj->variant.dir_variant.dirty); -+ return yaffs_del_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ return yaffs_del_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ return yaffs_generic_obj_del(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ default: -+ return YAFFS_FAIL; -+ } -+ } else if (yaffs_is_non_empty_dir(obj)) { -+ return YAFFS_FAIL; -+ } else { -+ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, -+ _Y("unlinked"), 0, 0); -+ } -+} -+ -+static int yaffs_unlink_obj(struct yaffs_obj *obj) -+{ -+ if (obj && obj->unlink_allowed) -+ return yaffs_unlink_worker(obj); -+ -+ return YAFFS_FAIL; -+} -+ -+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) -+{ -+ struct yaffs_obj *obj; -+ -+ obj = yaffs_find_by_name(dir, name); -+ return yaffs_unlink_obj(obj); -+} -+ -+/* Note: -+ * If old_name is NULL then we take old_dir as the object to be renamed. -+ */ -+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, -+ struct yaffs_obj *new_dir, const YCHAR *new_name) -+{ -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_obj *existing_target = NULL; -+ int force = 0; -+ int result; -+ struct yaffs_dev *dev; -+ -+ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ dev = old_dir->my_dev; -+ -+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE -+ /* Special case for case insemsitive systems. -+ * While look-up is case insensitive, the name isn't. -+ * Therefore we might want to change x.txt to X.txt -+ */ -+ if (old_dir == new_dir && -+ old_name && new_name && -+ strcmp(old_name, new_name) == 0) -+ force = 1; -+#endif -+ -+ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > -+ YAFFS_MAX_NAME_LENGTH) -+ /* ENAMETOOLONG */ -+ return YAFFS_FAIL; -+ -+ if (old_name) -+ obj = yaffs_find_by_name(old_dir, old_name); -+ else{ -+ obj = old_dir; -+ old_dir = obj->parent; -+ } -+ -+ if (obj && obj->rename_allowed) { -+ /* Now handle an existing target, if there is one */ -+ existing_target = yaffs_find_by_name(new_dir, new_name); -+ if (yaffs_is_non_empty_dir(existing_target)) { -+ return YAFFS_FAIL; /* ENOTEMPTY */ -+ } else if (existing_target && existing_target != obj) { -+ /* Nuke the target first, using shadowing, -+ * but only if it isn't the same object. -+ * -+ * Note we must disable gc here otherwise it can mess -+ * up the shadowing. -+ * -+ */ -+ dev->gc_disable = 1; -+ yaffs_change_obj_name(obj, new_dir, new_name, force, -+ existing_target->obj_id); -+ existing_target->is_shadowed = 1; -+ yaffs_unlink_obj(existing_target); -+ dev->gc_disable = 0; -+ } -+ -+ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); -+ -+ yaffs_update_parent(old_dir); -+ if (new_dir != old_dir) -+ yaffs_update_parent(new_dir); -+ -+ return result; -+ } -+ return YAFFS_FAIL; -+} -+ -+/*----------------------- Initialisation Scanning ---------------------- */ -+ -+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, -+ int backward_scanning) -+{ -+ struct yaffs_obj *obj; -+ -+ if (backward_scanning) { -+ /* Handle YAFFS2 case (backward scanning) -+ * If the shadowed object exists then ignore. -+ */ -+ obj = yaffs_find_by_number(dev, obj_id); -+ if (obj) -+ return; -+ } -+ -+ /* Let's create it (if it does not exist) assuming it is a file so that -+ * it can do shrinking etc. -+ * We put it in unlinked dir to be cleaned up after the scanning -+ */ -+ obj = -+ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); -+ if (!obj) -+ return; -+ obj->is_shadowed = 1; -+ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); -+ obj->variant.file_variant.shrink_size = 0; -+ obj->valid = 1; /* So that we don't read any other info. */ -+} -+ -+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) -+{ -+ struct list_head *lh; -+ struct list_head *save; -+ struct yaffs_obj *hl; -+ struct yaffs_obj *in; -+ -+ list_for_each_safe(lh, save, hard_list) { -+ hl = list_entry(lh, struct yaffs_obj, hard_links); -+ in = yaffs_find_by_number(dev, -+ hl->variant.hardlink_variant.equiv_id); -+ -+ if (in) { -+ /* Add the hardlink pointers */ -+ hl->variant.hardlink_variant.equiv_obj = in; -+ list_add(&hl->hard_links, &in->hard_links); -+ } else { -+ /* Todo Need to report/handle this better. -+ * Got a problem... hardlink to a non-existant object -+ */ -+ hl->variant.hardlink_variant.equiv_obj = NULL; -+ INIT_LIST_HEAD(&hl->hard_links); -+ } -+ } -+} -+ -+static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) -+{ -+ /* -+ * Sort out state of unlinked and deleted objects after scanning. -+ */ -+ struct list_head *i; -+ struct list_head *n; -+ struct yaffs_obj *l; -+ -+ if (dev->read_only) -+ return; -+ -+ /* Soft delete all the unlinked files */ -+ list_for_each_safe(i, n, -+ &dev->unlinked_dir->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ yaffs_del_obj(l); -+ } -+ -+ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ yaffs_del_obj(l); -+ } -+} -+ -+/* -+ * This code iterates through all the objects making sure that they are rooted. -+ * Any unrooted objects are re-rooted in lost+found. -+ * An object needs to be in one of: -+ * - Directly under deleted, unlinked -+ * - Directly or indirectly under root. -+ * -+ * Note: -+ * This code assumes that we don't ever change the current relationships -+ * between directories: -+ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL -+ * lost-n-found->parent == root_dir -+ * -+ * This fixes the problem where directories might have inadvertently been -+ * deleted leaving the object "hanging" without being rooted in the -+ * directory tree. -+ */ -+ -+static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) -+{ -+ return (obj == dev->del_dir || -+ obj == dev->unlinked_dir || obj == dev->root_dir); -+} -+ -+static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_obj *parent; -+ int i; -+ struct list_head *lh; -+ struct list_head *n; -+ int depth_limit; -+ int hanging; -+ -+ if (dev->read_only) -+ return; -+ -+ /* Iterate through the objects in each hash entry, -+ * looking at each object. -+ * Make sure it is rooted. -+ */ -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ parent = obj->parent; -+ -+ if (yaffs_has_null_parent(dev, obj)) { -+ /* These directories are not hanging */ -+ hanging = 0; -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ hanging = 1; -+ } else if (yaffs_has_null_parent(dev, parent)) { -+ hanging = 0; -+ } else { -+ /* -+ * Need to follow the parent chain to -+ * see if it is hanging. -+ */ -+ hanging = 0; -+ depth_limit = 100; -+ -+ while (parent != dev->root_dir && -+ parent->parent && -+ parent->parent->variant_type == -+ YAFFS_OBJECT_TYPE_DIRECTORY && -+ depth_limit > 0) { -+ parent = parent->parent; -+ depth_limit--; -+ } -+ if (parent != dev->root_dir) -+ hanging = 1; -+ } -+ if (hanging) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Hanging object %d moved to lost and found", -+ obj->obj_id); -+ yaffs_add_obj_to_dir(dev->lost_n_found, obj); -+ } -+ } -+ } -+} -+ -+/* -+ * Delete directory contents for cleaning up lost and found. -+ */ -+static void yaffs_del_dir_contents(struct yaffs_obj *dir) -+{ -+ struct yaffs_obj *obj; -+ struct list_head *lh; -+ struct list_head *n; -+ -+ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) -+ BUG(); -+ -+ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) -+ yaffs_del_dir_contents(obj); -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Deleting lost_found object %d", -+ obj->obj_id); -+ yaffs_unlink_obj(obj); -+ } -+} -+ -+static void yaffs_empty_l_n_f(struct yaffs_dev *dev) -+{ -+ yaffs_del_dir_contents(dev->lost_n_found); -+} -+ -+ -+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, -+ const YCHAR *name) -+{ -+ int sum; -+ struct list_head *i; -+ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; -+ struct yaffs_obj *l; -+ -+ if (!name) -+ return NULL; -+ -+ if (!directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_find_by_name: null pointer directory" -+ ); -+ BUG(); -+ return NULL; -+ } -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_find_by_name: non-directory" -+ ); -+ BUG(); -+ } -+ -+ sum = yaffs_calc_name_sum(name); -+ -+ list_for_each(i, &directory->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ -+ if (l->parent != directory) -+ BUG(); -+ -+ yaffs_check_obj_details_loaded(l); -+ -+ /* Special case for lost-n-found */ -+ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { -+ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) -+ return l; -+ } else if (l->sum == sum || l->hdr_chunk <= 0) { -+ /* LostnFound chunk called Objxxx -+ * Do a real check -+ */ -+ yaffs_get_obj_name(l, buffer, -+ YAFFS_MAX_NAME_LENGTH + 1); -+ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) -+ return l; -+ } -+ } -+ return NULL; -+} -+ -+/* GetEquivalentObject dereferences any hard links to get to the -+ * actual object. -+ */ -+ -+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) -+{ -+ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { -+ obj = obj->variant.hardlink_variant.equiv_obj; -+ yaffs_check_obj_details_loaded(obj); -+ } -+ return obj; -+} -+ -+/* -+ * A note or two on object names. -+ * * If the object name is missing, we then make one up in the form objnnn -+ * -+ * * ASCII names are stored in the object header's name field from byte zero -+ * * Unicode names are historically stored starting from byte zero. -+ * -+ * Then there are automatic Unicode names... -+ * The purpose of these is to save names in a way that can be read as -+ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII -+ * system to share files. -+ * -+ * These automatic unicode are stored slightly differently... -+ * - If the name can fit in the ASCII character space then they are saved as -+ * ascii names as per above. -+ * - If the name needs Unicode then the name is saved in Unicode -+ * starting at oh->name[1]. -+ -+ */ -+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, -+ int buffer_size) -+{ -+ /* Create an object name if we could not find one. */ -+ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { -+ YCHAR local_name[20]; -+ YCHAR num_string[20]; -+ YCHAR *x = &num_string[19]; -+ unsigned v = obj->obj_id; -+ num_string[19] = 0; -+ while (v > 0) { -+ x--; -+ *x = '0' + (v % 10); -+ v /= 10; -+ } -+ /* make up a name */ -+ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); -+ strcat(local_name, x); -+ strncpy(name, local_name, buffer_size - 1); -+ } -+} -+ -+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) -+{ -+ memset(name, 0, buffer_size * sizeof(YCHAR)); -+ yaffs_check_obj_details_loaded(obj); -+ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { -+ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); -+ } else if (obj->short_name[0]) { -+ strcpy(name, obj->short_name); -+ } else if (obj->hdr_chunk > 0) { -+ int result; -+ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); -+ -+ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; -+ -+ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); -+ -+ if (obj->hdr_chunk > 0) { -+ result = yaffs_rd_chunk_tags_nand(obj->my_dev, -+ obj->hdr_chunk, -+ buffer, NULL); -+ } -+ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, -+ buffer_size); -+ -+ yaffs_release_temp_buffer(obj->my_dev, buffer); -+ } -+ -+ yaffs_fix_null_name(obj, name, buffer_size); -+ -+ return strnlen(name, YAFFS_MAX_NAME_LENGTH); -+} -+ -+loff_t yaffs_get_obj_length(struct yaffs_obj *obj) -+{ -+ /* Dereference any hard linking */ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ return obj->variant.file_variant.file_size; -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ if (!obj->variant.symlink_variant.alias) -+ return 0; -+ return strnlen(obj->variant.symlink_variant.alias, -+ YAFFS_MAX_ALIAS_LENGTH); -+ } else { -+ /* Only a directory should drop through to here */ -+ return obj->my_dev->data_bytes_per_chunk; -+ } -+} -+ -+int yaffs_get_obj_link_count(struct yaffs_obj *obj) -+{ -+ int count = 0; -+ struct list_head *i; -+ -+ if (!obj->unlinked) -+ count++; /* the object itself */ -+ -+ list_for_each(i, &obj->hard_links) -+ count++; /* add the hard links; */ -+ -+ return count; -+} -+ -+int yaffs_get_obj_inode(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ return obj->obj_id; -+} -+ -+unsigned yaffs_get_obj_type(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return DT_REG; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ return DT_DIR; -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ return DT_LNK; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ return DT_REG; -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ if (S_ISFIFO(obj->yst_mode)) -+ return DT_FIFO; -+ if (S_ISCHR(obj->yst_mode)) -+ return DT_CHR; -+ if (S_ISBLK(obj->yst_mode)) -+ return DT_BLK; -+ if (S_ISSOCK(obj->yst_mode)) -+ return DT_SOCK; -+ return DT_REG; -+ break; -+ default: -+ return DT_REG; -+ break; -+ } -+} -+ -+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) -+ return yaffs_clone_str(obj->variant.symlink_variant.alias); -+ else -+ return yaffs_clone_str(_Y("")); -+} -+ -+/*--------------------------- Initialisation code -------------------------- */ -+ -+static int yaffs_check_dev_fns(struct yaffs_dev *dev) -+{ -+ struct yaffs_driver *drv = &dev->drv; -+ struct yaffs_tags_handler *tagger = &dev->tagger; -+ -+ /* Common functions, gotta have */ -+ if (!drv->drv_read_chunk_fn || -+ !drv->drv_write_chunk_fn || -+ !drv->drv_erase_fn) -+ return 0; -+ -+ if (dev->param.is_yaffs2 && -+ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) -+ return 0; -+ -+ /* Install the default tags marshalling functions if needed. */ -+ yaffs_tags_compat_install(dev); -+ yaffs_tags_marshall_install(dev); -+ -+ /* Check we now have the marshalling functions required. */ -+ if (!tagger->write_chunk_tags_fn || -+ !tagger->read_chunk_tags_fn || -+ !tagger->query_block_fn || -+ !tagger->mark_bad_fn) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs_create_initial_dir(struct yaffs_dev *dev) -+{ -+ /* Initialise the unlinked, deleted, root and lost+found directories */ -+ dev->lost_n_found = dev->root_dir = NULL; -+ dev->unlinked_dir = dev->del_dir = NULL; -+ dev->unlinked_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); -+ dev->del_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); -+ dev->root_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, -+ YAFFS_ROOT_MODE | S_IFDIR); -+ dev->lost_n_found = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, -+ YAFFS_LOSTNFOUND_MODE | S_IFDIR); -+ -+ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir -+ && dev->del_dir) { -+ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); -+ return YAFFS_OK; -+ } -+ return YAFFS_FAIL; -+} -+ -+/* Low level init. -+ * Typically only used by yaffs_guts_initialise, but also used by the -+ * Low level yaffs driver tests. -+ */ -+ -+int yaffs_guts_ll_init(struct yaffs_dev *dev) -+{ -+ -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); -+ -+ if (!dev) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Need a device" -+ ); -+ return YAFFS_FAIL; -+ } -+ -+ if (dev->ll_init) -+ return YAFFS_OK; -+ -+ dev->internal_start_block = dev->param.start_block; -+ dev->internal_end_block = dev->param.end_block; -+ dev->block_offset = 0; -+ dev->chunk_offset = 0; -+ dev->n_free_chunks = 0; -+ -+ dev->gc_block = 0; -+ -+ if (dev->param.start_block == 0) { -+ dev->internal_start_block = dev->param.start_block + 1; -+ dev->internal_end_block = dev->param.end_block + 1; -+ dev->block_offset = 1; -+ dev->chunk_offset = dev->param.chunks_per_block; -+ } -+ -+ /* Check geometry parameters. */ -+ -+ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && -+ dev->param.total_bytes_per_chunk < 1024) || -+ (!dev->param.is_yaffs2 && -+ dev->param.total_bytes_per_chunk < 512) || -+ (dev->param.inband_tags && !dev->param.is_yaffs2) || -+ dev->param.chunks_per_block < 2 || -+ dev->param.n_reserved_blocks < 2 || -+ dev->internal_start_block <= 0 || -+ dev->internal_end_block <= 0 || -+ dev->internal_end_block <= -+ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) -+ ) { -+ /* otherwise it is too small */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", -+ dev->param.total_bytes_per_chunk, -+ dev->param.is_yaffs2 ? "2" : "", -+ dev->param.inband_tags); -+ return YAFFS_FAIL; -+ } -+ -+ /* Sort out space for inband tags, if required */ -+ if (dev->param.inband_tags) -+ dev->data_bytes_per_chunk = -+ dev->param.total_bytes_per_chunk - -+ sizeof(struct yaffs_packed_tags2_tags_only); -+ else -+ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; -+ -+ /* Got the right mix of functions? */ -+ if (!yaffs_check_dev_fns(dev)) { -+ /* Function missing */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "device function(s) missing or wrong"); -+ -+ return YAFFS_FAIL; -+ } -+ -+ if (yaffs_init_nand(dev) != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); -+ return YAFFS_FAIL; -+ } -+ -+ return YAFFS_OK; -+} -+ -+ -+int yaffs_guts_format_dev(struct yaffs_dev *dev) -+{ -+ int i; -+ enum yaffs_block_state state; -+ u32 dummy; -+ -+ if(yaffs_guts_ll_init(dev) != YAFFS_OK) -+ return YAFFS_FAIL; -+ -+ if(dev->is_mounted) -+ return YAFFS_FAIL; -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ yaffs_query_init_block_state(dev, i, &state, &dummy); -+ if (state != YAFFS_BLOCK_STATE_DEAD) -+ yaffs_erase_block(dev, i); -+ } -+ -+ return YAFFS_OK; -+} -+ -+ -+int yaffs_guts_initialise(struct yaffs_dev *dev) -+{ -+ int init_failed = 0; -+ unsigned x; -+ int bits; -+ -+ if(yaffs_guts_ll_init(dev) != YAFFS_OK) -+ return YAFFS_FAIL; -+ -+ if (dev->is_mounted) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); -+ return YAFFS_FAIL; -+ } -+ -+ dev->is_mounted = 1; -+ -+ /* OK now calculate a few things for the device */ -+ -+ /* -+ * Calculate all the chunk size manipulation numbers: -+ */ -+ x = dev->data_bytes_per_chunk; -+ /* We always use dev->chunk_shift and dev->chunk_div */ -+ dev->chunk_shift = calc_shifts(x); -+ x >>= dev->chunk_shift; -+ dev->chunk_div = x; -+ /* We only use chunk mask if chunk_div is 1 */ -+ dev->chunk_mask = (1 << dev->chunk_shift) - 1; -+ -+ /* -+ * Calculate chunk_grp_bits. -+ * We need to find the next power of 2 > than internal_end_block -+ */ -+ -+ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); -+ -+ bits = calc_shifts_ceiling(x); -+ -+ /* Set up tnode width if wide tnodes are enabled. */ -+ if (!dev->param.wide_tnodes_disabled) { -+ /* bits must be even so that we end up with 32-bit words */ -+ if (bits & 1) -+ bits++; -+ if (bits < 16) -+ dev->tnode_width = 16; -+ else -+ dev->tnode_width = bits; -+ } else { -+ dev->tnode_width = 16; -+ } -+ -+ dev->tnode_mask = (1 << dev->tnode_width) - 1; -+ -+ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), -+ * so if the bitwidth of the -+ * chunk range we're using is greater than 16 we need -+ * to figure out chunk shift and chunk_grp_size -+ */ -+ -+ if (bits <= dev->tnode_width) -+ dev->chunk_grp_bits = 0; -+ else -+ dev->chunk_grp_bits = bits - dev->tnode_width; -+ -+ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; -+ if (dev->tnode_size < sizeof(struct yaffs_tnode)) -+ dev->tnode_size = sizeof(struct yaffs_tnode); -+ -+ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; -+ -+ if (dev->param.chunks_per_block < dev->chunk_grp_size) { -+ /* We have a problem because the soft delete won't work if -+ * the chunk group size > chunks per block. -+ * This can be remedied by using larger "virtual blocks". -+ */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); -+ -+ return YAFFS_FAIL; -+ } -+ -+ /* Finished verifying the device, continue with initialisation */ -+ -+ /* More device initialisation */ -+ dev->all_gcs = 0; -+ dev->passive_gc_count = 0; -+ dev->oldest_dirty_gc_count = 0; -+ dev->bg_gcs = 0; -+ dev->gc_block_finder = 0; -+ dev->buffered_block = -1; -+ dev->doing_buffered_block_rewrite = 0; -+ dev->n_deleted_files = 0; -+ dev->n_bg_deletions = 0; -+ dev->n_unlinked_files = 0; -+ dev->n_ecc_fixed = 0; -+ dev->n_ecc_unfixed = 0; -+ dev->n_tags_ecc_fixed = 0; -+ dev->n_tags_ecc_unfixed = 0; -+ dev->n_erase_failures = 0; -+ dev->n_erased_blocks = 0; -+ dev->gc_disable = 0; -+ dev->has_pending_prioritised_gc = 1; -+ /* Assume the worst for now, will get fixed on first GC */ -+ INIT_LIST_HEAD(&dev->dirty_dirs); -+ dev->oldest_dirty_seq = 0; -+ dev->oldest_dirty_block = 0; -+ -+ /* Initialise temporary buffers and caches. */ -+ if (!yaffs_init_tmp_buffers(dev)) -+ init_failed = 1; -+ -+ dev->cache = NULL; -+ dev->gc_cleanup_list = NULL; -+ -+ if (!init_failed && dev->param.n_caches > 0) { -+ int i; -+ void *buf; -+ int cache_bytes = -+ dev->param.n_caches * sizeof(struct yaffs_cache); -+ -+ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) -+ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; -+ -+ dev->cache = kmalloc(cache_bytes, GFP_NOFS); -+ -+ buf = (u8 *) dev->cache; -+ -+ if (dev->cache) -+ memset(dev->cache, 0, cache_bytes); -+ -+ for (i = 0; i < dev->param.n_caches && buf; i++) { -+ dev->cache[i].object = NULL; -+ dev->cache[i].last_use = 0; -+ dev->cache[i].dirty = 0; -+ dev->cache[i].data = buf = -+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ } -+ if (!buf) -+ init_failed = 1; -+ -+ dev->cache_last_use = 0; -+ } -+ -+ dev->cache_hits = 0; -+ -+ if (!init_failed) { -+ dev->gc_cleanup_list = -+ kmalloc(dev->param.chunks_per_block * sizeof(u32), -+ GFP_NOFS); -+ if (!dev->gc_cleanup_list) -+ init_failed = 1; -+ } -+ -+ if (dev->param.is_yaffs2) -+ dev->param.use_header_file_size = 1; -+ -+ if (!init_failed && !yaffs_init_blocks(dev)) -+ init_failed = 1; -+ -+ yaffs_init_tnodes_and_objs(dev); -+ -+ if (!init_failed && !yaffs_create_initial_dir(dev)) -+ init_failed = 1; -+ -+ if (!init_failed && dev->param.is_yaffs2 && -+ !dev->param.disable_summary && -+ !yaffs_summary_init(dev)) -+ init_failed = 1; -+ -+ if (!init_failed) { -+ /* Now scan the flash. */ -+ if (dev->param.is_yaffs2) { -+ if (yaffs2_checkpt_restore(dev)) { -+ yaffs_check_obj_details_loaded(dev->root_dir); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | -+ YAFFS_TRACE_MOUNT, -+ "yaffs: restored from checkpoint" -+ ); -+ } else { -+ -+ /* Clean up the mess caused by an aborted -+ * checkpoint load then scan backwards. -+ */ -+ yaffs_deinit_blocks(dev); -+ -+ yaffs_deinit_tnodes_and_objs(dev); -+ -+ dev->n_erased_blocks = 0; -+ dev->n_free_chunks = 0; -+ dev->alloc_block = -1; -+ dev->alloc_page = -1; -+ dev->n_deleted_files = 0; -+ dev->n_unlinked_files = 0; -+ dev->n_bg_deletions = 0; -+ -+ if (!init_failed && !yaffs_init_blocks(dev)) -+ init_failed = 1; -+ -+ yaffs_init_tnodes_and_objs(dev); -+ -+ if (!init_failed -+ && !yaffs_create_initial_dir(dev)) -+ init_failed = 1; -+ -+ if (!init_failed && !yaffs2_scan_backwards(dev)) -+ init_failed = 1; -+ } -+ } else if (!yaffs1_scan(dev)) { -+ init_failed = 1; -+ } -+ -+ yaffs_strip_deleted_objs(dev); -+ yaffs_fix_hanging_objs(dev); -+ if (dev->param.empty_lost_n_found) -+ yaffs_empty_l_n_f(dev); -+ } -+ -+ if (init_failed) { -+ /* Clean up the mess */ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: yaffs_guts_initialise() aborted."); -+ -+ yaffs_deinitialise(dev); -+ return YAFFS_FAIL; -+ } -+ -+ /* Zero out stats */ -+ dev->n_page_reads = 0; -+ dev->n_page_writes = 0; -+ dev->n_erasures = 0; -+ dev->n_gc_copies = 0; -+ dev->n_retried_writes = 0; -+ -+ dev->n_retired_blocks = 0; -+ -+ yaffs_verify_free_chunks(dev); -+ yaffs_verify_blocks(dev); -+ -+ /* Clean up any aborted checkpoint data */ -+ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) -+ yaffs2_checkpt_invalidate(dev); -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: yaffs_guts_initialise() done."); -+ return YAFFS_OK; -+} -+ -+void yaffs_deinitialise(struct yaffs_dev *dev) -+{ -+ if (dev->is_mounted) { -+ int i; -+ -+ yaffs_deinit_blocks(dev); -+ yaffs_deinit_tnodes_and_objs(dev); -+ yaffs_summary_deinit(dev); -+ -+ if (dev->param.n_caches > 0 && dev->cache) { -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ kfree(dev->cache[i].data); -+ dev->cache[i].data = NULL; -+ } -+ -+ kfree(dev->cache); -+ dev->cache = NULL; -+ } -+ -+ kfree(dev->gc_cleanup_list); -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) -+ kfree(dev->temp_buffer[i].buffer); -+ -+ dev->is_mounted = 0; -+ -+ yaffs_deinit_nand(dev); -+ } -+} -+ -+int yaffs_count_free_chunks(struct yaffs_dev *dev) -+{ -+ int n_free = 0; -+ int b; -+ struct yaffs_block_info *blk; -+ -+ blk = dev->block_info; -+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { -+ switch (blk->block_state) { -+ case YAFFS_BLOCK_STATE_EMPTY: -+ case YAFFS_BLOCK_STATE_ALLOCATING: -+ case YAFFS_BLOCK_STATE_COLLECTING: -+ case YAFFS_BLOCK_STATE_FULL: -+ n_free += -+ (dev->param.chunks_per_block - blk->pages_in_use + -+ blk->soft_del_pages); -+ break; -+ default: -+ break; -+ } -+ blk++; -+ } -+ return n_free; -+} -+ -+int yaffs_get_n_free_chunks(struct yaffs_dev *dev) -+{ -+ /* This is what we report to the outside world */ -+ int n_free; -+ int n_dirty_caches; -+ int blocks_for_checkpt; -+ int i; -+ -+ n_free = dev->n_free_chunks; -+ n_free += dev->n_deleted_files; -+ -+ /* Now count and subtract the number of dirty chunks in the cache. */ -+ -+ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].dirty) -+ n_dirty_caches++; -+ } -+ -+ n_free -= n_dirty_caches; -+ -+ n_free -= -+ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); -+ -+ /* Now figure checkpoint space and report that... */ -+ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); -+ -+ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); -+ -+ if (n_free < 0) -+ n_free = 0; -+ -+ return n_free; -+} -+ -+ -+ -+/* -+ * Marshalling functions to get loff_t file sizes into and out of -+ * object headers. -+ */ -+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) -+{ -+ oh->file_size_low = (fsize & 0xFFFFFFFF); -+ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); -+} -+ -+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) -+{ -+ loff_t retval; -+ -+ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) -+ retval = (((loff_t) oh->file_size_high) << 32) | -+ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); -+ else -+ retval = (loff_t) oh->file_size_low; -+ -+ return retval; -+} -+ -+ -+void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) -+{ -+ int i; -+ struct yaffs_block_info *bi; -+ int s; -+ -+ for(i = 0; i < 10; i++) -+ bs[i] = 0; -+ -+ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ bi = yaffs_get_block_info(dev, i); -+ s = bi->block_state; -+ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) -+ bs[0]++; -+ else -+ bs[s]++; -+ } -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_guts.h linux-3.12.20/fs/yaffs2/yaffs_guts.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_guts.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,1007 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_GUTS_H__ -+#define __YAFFS_GUTS_H__ -+ -+#include "yportenv.h" -+ -+#define YAFFS_OK 1 -+#define YAFFS_FAIL 0 -+ -+/* Give us a Y=0x59, -+ * Give us an A=0x41, -+ * Give us an FF=0xff -+ * Give us an S=0x53 -+ * And what have we got... -+ */ -+#define YAFFS_MAGIC 0x5941ff53 -+ -+/* -+ * Tnodes form a tree with the tnodes in "levels" -+ * Levels greater than 0 hold 8 slots which point to other tnodes. -+ * Those at level 0 hold 16 slots which point to chunks in NAND. -+ * -+ * A maximum level of 8 thust supports files of size up to: -+ * -+ * 2^(3*MAX_LEVEL+4) -+ * -+ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives -+ * a maximum file size of around 512Gbytees with 2k chunks. -+ */ -+#define YAFFS_NTNODES_LEVEL0 16 -+#define YAFFS_TNODES_LEVEL0_BITS 4 -+#define YAFFS_TNODES_LEVEL0_MASK 0xf -+ -+#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) -+#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) -+#define YAFFS_TNODES_INTERNAL_MASK 0x7 -+#define YAFFS_TNODES_MAX_LEVEL 8 -+#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ -+ YAFFS_TNODES_INTERNAL_BITS * \ -+ YAFFS_TNODES_MAX_LEVEL) -+#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) -+ -+#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff -+ -+/* Constants for YAFFS1 mode */ -+#define YAFFS_BYTES_PER_SPARE 16 -+#define YAFFS_BYTES_PER_CHUNK 512 -+#define YAFFS_CHUNK_SIZE_SHIFT 9 -+#define YAFFS_CHUNKS_PER_BLOCK 32 -+#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) -+ -+#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 -+#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 -+ -+ -+ -+#define YAFFS_ALLOCATION_NOBJECTS 100 -+#define YAFFS_ALLOCATION_NTNODES 100 -+#define YAFFS_ALLOCATION_NLINKS 100 -+ -+#define YAFFS_NOBJECT_BUCKETS 256 -+ -+#define YAFFS_OBJECT_SPACE 0x40000 -+#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) -+ -+/* Binary data version stamps */ -+#define YAFFS_SUMMARY_VERSION 1 -+#define YAFFS_CHECKPOINT_VERSION 7 -+ -+#ifdef CONFIG_YAFFS_UNICODE -+#define YAFFS_MAX_NAME_LENGTH 127 -+#define YAFFS_MAX_ALIAS_LENGTH 79 -+#else -+#define YAFFS_MAX_NAME_LENGTH 255 -+#define YAFFS_MAX_ALIAS_LENGTH 159 -+#endif -+ -+#define YAFFS_SHORT_NAME_LENGTH 15 -+ -+/* Some special object ids for pseudo objects */ -+#define YAFFS_OBJECTID_ROOT 1 -+#define YAFFS_OBJECTID_LOSTNFOUND 2 -+#define YAFFS_OBJECTID_UNLINKED 3 -+#define YAFFS_OBJECTID_DELETED 4 -+ -+/* Fake object Id for summary data */ -+#define YAFFS_OBJECTID_SUMMARY 0x10 -+ -+/* Pseudo object ids for checkpointing */ -+#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 -+#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 -+ -+#define YAFFS_MAX_SHORT_OP_CACHES 20 -+ -+#define YAFFS_N_TEMP_BUFFERS 6 -+ -+/* We limit the number attempts at sucessfully saving a chunk of data. -+ * Small-page devices have 32 pages per block; large-page devices have 64. -+ * Default to something in the order of 5 to 10 blocks worth of chunks. -+ */ -+#define YAFFS_WR_ATTEMPTS (5*64) -+ -+/* Sequence numbers are used in YAFFS2 to determine block allocation order. -+ * The range is limited slightly to help distinguish bad numbers from good. -+ * This also allows us to perhaps in the future use special numbers for -+ * special purposes. -+ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, -+ * and is a larger number than the lifetime of a 2GB device. -+ */ -+#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 -+#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 -+ -+/* Special sequence number for bad block that failed to be marked bad */ -+#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 -+ -+/* ChunkCache is used for short read/write operations.*/ -+struct yaffs_cache { -+ struct yaffs_obj *object; -+ int chunk_id; -+ int last_use; -+ int dirty; -+ int n_bytes; /* Only valid if the cache is dirty */ -+ int locked; /* Can't push out or flush while locked. */ -+ u8 *data; -+}; -+ -+/* yaffs1 tags structures in RAM -+ * NB This uses bitfield. Bitfields should not straddle a u32 boundary -+ * otherwise the structure size will get blown out. -+ */ -+ -+struct yaffs_tags { -+ u32 chunk_id:20; -+ u32 serial_number:2; -+ u32 n_bytes_lsb:10; -+ u32 obj_id:18; -+ u32 ecc:12; -+ u32 n_bytes_msb:2; -+}; -+ -+union yaffs_tags_union { -+ struct yaffs_tags as_tags; -+ u8 as_bytes[8]; -+}; -+ -+ -+/* Stuff used for extended tags in YAFFS2 */ -+ -+enum yaffs_ecc_result { -+ YAFFS_ECC_RESULT_UNKNOWN, -+ YAFFS_ECC_RESULT_NO_ERROR, -+ YAFFS_ECC_RESULT_FIXED, -+ YAFFS_ECC_RESULT_UNFIXED -+}; -+ -+enum yaffs_obj_type { -+ YAFFS_OBJECT_TYPE_UNKNOWN, -+ YAFFS_OBJECT_TYPE_FILE, -+ YAFFS_OBJECT_TYPE_SYMLINK, -+ YAFFS_OBJECT_TYPE_DIRECTORY, -+ YAFFS_OBJECT_TYPE_HARDLINK, -+ YAFFS_OBJECT_TYPE_SPECIAL -+}; -+ -+#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL -+ -+struct yaffs_ext_tags { -+ unsigned chunk_used; /* Status of the chunk: used or unused */ -+ unsigned obj_id; /* If 0 this is not used */ -+ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ -+ unsigned n_bytes; /* Only valid for data chunks */ -+ -+ /* The following stuff only has meaning when we read */ -+ enum yaffs_ecc_result ecc_result; -+ unsigned block_bad; -+ -+ /* YAFFS 1 stuff */ -+ unsigned is_deleted; /* The chunk is marked deleted */ -+ unsigned serial_number; /* Yaffs1 2-bit serial number */ -+ -+ /* YAFFS2 stuff */ -+ unsigned seq_number; /* The sequence number of this block */ -+ -+ /* Extra info if this is an object header (YAFFS2 only) */ -+ -+ unsigned extra_available; /* Extra info available if not zero */ -+ unsigned extra_parent_id; /* The parent object */ -+ unsigned extra_is_shrink; /* Is it a shrink header? */ -+ unsigned extra_shadows; /* Does this shadow another object? */ -+ -+ enum yaffs_obj_type extra_obj_type; /* What object type? */ -+ -+ loff_t extra_file_size; /* Length if it is a file */ -+ unsigned extra_equiv_id; /* Equivalent object for a hard link */ -+}; -+ -+/* Spare structure for YAFFS1 */ -+struct yaffs_spare { -+ u8 tb0; -+ u8 tb1; -+ u8 tb2; -+ u8 tb3; -+ u8 page_status; /* set to 0 to delete the chunk */ -+ u8 block_status; -+ u8 tb4; -+ u8 tb5; -+ u8 ecc1[3]; -+ u8 tb6; -+ u8 tb7; -+ u8 ecc2[3]; -+}; -+ -+/*Special structure for passing through to mtd */ -+struct yaffs_nand_spare { -+ struct yaffs_spare spare; -+ int eccres1; -+ int eccres2; -+}; -+ -+/* Block data in RAM */ -+ -+enum yaffs_block_state { -+ YAFFS_BLOCK_STATE_UNKNOWN = 0, -+ -+ YAFFS_BLOCK_STATE_SCANNING, -+ /* Being scanned */ -+ -+ YAFFS_BLOCK_STATE_NEEDS_SCAN, -+ /* The block might have something on it (ie it is allocating or full, -+ * perhaps empty) but it needs to be scanned to determine its true -+ * state. -+ * This state is only valid during scanning. -+ * NB We tolerate empty because the pre-scanner might be incapable of -+ * deciding -+ * However, if this state is returned on a YAFFS2 device, -+ * then we expect a sequence number -+ */ -+ -+ YAFFS_BLOCK_STATE_EMPTY, -+ /* This block is empty */ -+ -+ YAFFS_BLOCK_STATE_ALLOCATING, -+ /* This block is partially allocated. -+ * At least one page holds valid data. -+ * This is the one currently being used for page -+ * allocation. Should never be more than one of these. -+ * If a block is only partially allocated at mount it is treated as -+ * full. -+ */ -+ -+ YAFFS_BLOCK_STATE_FULL, -+ /* All the pages in this block have been allocated. -+ * If a block was only partially allocated when mounted we treat -+ * it as fully allocated. -+ */ -+ -+ YAFFS_BLOCK_STATE_DIRTY, -+ /* The block was full and now all chunks have been deleted. -+ * Erase me, reuse me. -+ */ -+ -+ YAFFS_BLOCK_STATE_CHECKPOINT, -+ /* This block is assigned to holding checkpoint data. */ -+ -+ YAFFS_BLOCK_STATE_COLLECTING, -+ /* This block is being garbage collected */ -+ -+ YAFFS_BLOCK_STATE_DEAD -+ /* This block has failed and is not in use */ -+}; -+ -+#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) -+ -+struct yaffs_block_info { -+ -+ s32 soft_del_pages:10; /* number of soft deleted pages */ -+ s32 pages_in_use:10; /* number of pages in use */ -+ u32 block_state:4; /* One of the above block states. */ -+ /* NB use unsigned because enum is sometimes -+ * an int */ -+ u32 needs_retiring:1; /* Data has failed on this block, */ -+ /*need to get valid data off and retire*/ -+ u32 skip_erased_check:1;/* Skip the erased check on this block */ -+ u32 gc_prioritise:1; /* An ECC check or blank check has failed. -+ Block should be prioritised for GC */ -+ u32 chunk_error_strikes:3; /* How many times we've had ecc etc -+ failures on this block and tried to reuse it */ -+ u32 has_summary:1; /* The block has a summary */ -+ -+ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ -+ u32 seq_number; /* block sequence number for yaffs2 */ -+ -+}; -+ -+/* -------------------------- Object structure -------------------------------*/ -+/* This is the object structure as stored on NAND */ -+ -+struct yaffs_obj_hdr { -+ enum yaffs_obj_type type; -+ -+ /* Apply to everything */ -+ int parent_obj_id; -+ u16 sum_no_longer_used; /* checksum of name. No longer used */ -+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ /* The following apply to all object types except for hard links */ -+ u32 yst_mode; /* protection */ -+ -+ u32 yst_uid; -+ u32 yst_gid; -+ u32 yst_atime; -+ u32 yst_mtime; -+ u32 yst_ctime; -+ -+ /* File size applies to files only */ -+ u32 file_size_low; -+ -+ /* Equivalent object id applies to hard links only. */ -+ int equiv_id; -+ -+ /* Alias is for symlinks only. */ -+ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; -+ -+ u32 yst_rdev; /* stuff for block and char devices (major/min) */ -+ -+ u32 win_ctime[2]; -+ u32 win_atime[2]; -+ u32 win_mtime[2]; -+ -+ u32 inband_shadowed_obj_id; -+ u32 inband_is_shrink; -+ -+ u32 file_size_high; -+ u32 reserved[1]; -+ int shadows_obj; /* This object header shadows the -+ specified object if > 0 */ -+ -+ /* is_shrink applies to object headers written when wemake a hole. */ -+ u32 is_shrink; -+ -+}; -+ -+/*--------------------------- Tnode -------------------------- */ -+ -+struct yaffs_tnode { -+ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; -+}; -+ -+/*------------------------ Object -----------------------------*/ -+/* An object can be one of: -+ * - a directory (no data, has children links -+ * - a regular file (data.... not prunes :->). -+ * - a symlink [symbolic link] (the alias). -+ * - a hard link -+ */ -+ -+struct yaffs_file_var { -+ loff_t file_size; -+ loff_t scanned_size; -+ loff_t shrink_size; -+ int top_level; -+ struct yaffs_tnode *top; -+}; -+ -+struct yaffs_dir_var { -+ struct list_head children; /* list of child links */ -+ struct list_head dirty; /* Entry for list of dirty directories */ -+}; -+ -+struct yaffs_symlink_var { -+ YCHAR *alias; -+}; -+ -+struct yaffs_hardlink_var { -+ struct yaffs_obj *equiv_obj; -+ u32 equiv_id; -+}; -+ -+union yaffs_obj_var { -+ struct yaffs_file_var file_variant; -+ struct yaffs_dir_var dir_variant; -+ struct yaffs_symlink_var symlink_variant; -+ struct yaffs_hardlink_var hardlink_variant; -+}; -+ -+struct yaffs_obj { -+ u8 deleted:1; /* This should only apply to unlinked files. */ -+ u8 soft_del:1; /* it has also been soft deleted */ -+ u8 unlinked:1; /* An unlinked file.*/ -+ u8 fake:1; /* A fake object has no presence on NAND. */ -+ u8 rename_allowed:1; /* Some objects cannot be renamed. */ -+ u8 unlink_allowed:1; -+ u8 dirty:1; /* the object needs to be written to flash */ -+ u8 valid:1; /* When the file system is being loaded up, this -+ * object might be created before the data -+ * is available -+ * ie. file data chunks encountered before -+ * the header. -+ */ -+ u8 lazy_loaded:1; /* This object has been lazy loaded and -+ * is missing some detail */ -+ -+ u8 defered_free:1; /* Object is removed from NAND, but is -+ * still in the inode cache. -+ * Free of object is defered. -+ * until the inode is released. -+ */ -+ u8 being_created:1; /* This object is still being created -+ * so skip some verification checks. */ -+ u8 is_shadowed:1; /* This object is shadowed on the way -+ * to being renamed. */ -+ -+ u8 xattr_known:1; /* We know if this has object has xattribs -+ * or not. */ -+ u8 has_xattr:1; /* This object has xattribs. -+ * Only valid if xattr_known. */ -+ -+ u8 serial; /* serial number of chunk in NAND.*/ -+ u16 sum; /* sum of the name to speed searching */ -+ -+ struct yaffs_dev *my_dev; /* The device I'm on */ -+ -+ struct list_head hash_link; /* list of objects in hash bucket */ -+ -+ struct list_head hard_links; /* hard linked object chain*/ -+ -+ /* directory structure stuff */ -+ /* also used for linking up the free list */ -+ struct yaffs_obj *parent; -+ struct list_head siblings; -+ -+ /* Where's my object header in NAND? */ -+ int hdr_chunk; -+ -+ int n_data_chunks; /* Number of data chunks for this file. */ -+ -+ u32 obj_id; /* the object id value */ -+ -+ u32 yst_mode; -+ -+ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; -+ -+#ifdef CONFIG_YAFFS_WINCE -+ u32 win_ctime[2]; -+ u32 win_mtime[2]; -+ u32 win_atime[2]; -+#else -+ u32 yst_uid; -+ u32 yst_gid; -+ u32 yst_atime; -+ u32 yst_mtime; -+ u32 yst_ctime; -+#endif -+ -+ u32 yst_rdev; -+ -+ void *my_inode; -+ -+ enum yaffs_obj_type variant_type; -+ -+ union yaffs_obj_var variant; -+ -+}; -+ -+struct yaffs_obj_bucket { -+ struct list_head list; -+ int count; -+}; -+ -+/* yaffs_checkpt_obj holds the definition of an object as dumped -+ * by checkpointing. -+ */ -+ -+struct yaffs_checkpt_obj { -+ int struct_type; -+ u32 obj_id; -+ u32 parent_id; -+ int hdr_chunk; -+ enum yaffs_obj_type variant_type:3; -+ u8 deleted:1; -+ u8 soft_del:1; -+ u8 unlinked:1; -+ u8 fake:1; -+ u8 rename_allowed:1; -+ u8 unlink_allowed:1; -+ u8 serial; -+ int n_data_chunks; -+ loff_t size_or_equiv_obj; -+}; -+ -+/*--------------------- Temporary buffers ---------------- -+ * -+ * These are chunk-sized working buffers. Each device has a few. -+ */ -+ -+struct yaffs_buffer { -+ u8 *buffer; -+ int in_use; -+}; -+ -+/*----------------- Device ---------------------------------*/ -+ -+struct yaffs_param { -+ const YCHAR *name; -+ -+ /* -+ * Entry parameters set up way early. Yaffs sets up the rest. -+ * The structure should be zeroed out before use so that unused -+ * and default values are zero. -+ */ -+ -+ int inband_tags; /* Use unband tags */ -+ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to -+ be a power of 2 */ -+ int chunks_per_block; /* does not need to be a power of 2 */ -+ int spare_bytes_per_chunk; /* spare area size */ -+ int start_block; /* Start block we're allowed to use */ -+ int end_block; /* End block we're allowed to use */ -+ int n_reserved_blocks; /* Tuneable so that we can reduce -+ * reserved blocks on NOR and RAM. */ -+ -+ int n_caches; /* If <= 0, then short op caching is disabled, -+ * else the number of short op caches. -+ */ -+ int cache_bypass_aligned; /* If non-zero then bypass the cache for -+ * aligned writes. -+ */ -+ -+ int use_nand_ecc; /* Flag to decide whether or not to use -+ * NAND driver ECC on data (yaffs1) */ -+ int tags_9bytes; /* Use 9 byte tags */ -+ int no_tags_ecc; /* Flag to decide whether or not to do ECC -+ * on packed tags (yaffs2) */ -+ -+ int is_yaffs2; /* Use yaffs2 mode on this device */ -+ -+ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ -+ -+ int refresh_period; /* How often to check for a block refresh */ -+ -+ /* Checkpoint control. Can be set before or after initialisation */ -+ u8 skip_checkpt_rd; -+ u8 skip_checkpt_wr; -+ -+ int enable_xattr; /* Enable xattribs */ -+ -+ int max_objects; /* -+ * Set to limit the number of objects created. -+ * 0 = no limit. -+ */ -+ -+ /* The remove_obj_fn function must be supplied by OS flavours that -+ * need it. -+ * yaffs direct uses it to implement the faster readdir. -+ * Linux uses it to protect the directory during unlocking. -+ */ -+ void (*remove_obj_fn) (struct yaffs_obj *obj); -+ -+ /* Callback to mark the superblock dirty */ -+ void (*sb_dirty_fn) (struct yaffs_dev *dev); -+ -+ /* Callback to control garbage collection. */ -+ unsigned (*gc_control_fn) (struct yaffs_dev *dev); -+ -+ /* Debug control flags. Don't use unless you know what you're doing */ -+ int use_header_file_size; /* Flag to determine if we should use -+ * file sizes from the header */ -+ int disable_lazy_load; /* Disable lazy loading on this device */ -+ int wide_tnodes_disabled; /* Set to disable wide tnodes */ -+ int disable_soft_del; /* yaffs 1 only: Set to disable the use of -+ * softdeletion. */ -+ -+ int defered_dir_update; /* Set to defer directory updates */ -+ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ int auto_unicode; -+#endif -+ int always_check_erased; /* Force chunk erased check always on */ -+ -+ int disable_summary; -+ int disable_bad_block_marking; -+ -+}; -+ -+struct yaffs_driver { -+ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, int data_len, -+ const u8 *oob, int oob_len); -+ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, -+ u8 *data, int data_len, -+ u8 *oob, int oob_len, -+ enum yaffs_ecc_result *ecc_result); -+ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_initialise_fn) (struct yaffs_dev *dev); -+ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); -+}; -+ -+struct yaffs_tags_handler { -+ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ const struct yaffs_ext_tags *tags); -+ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, -+ int nand_chunk, u8 *data, -+ struct yaffs_ext_tags *tags); -+ -+ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number); -+ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); -+}; -+ -+struct yaffs_dev { -+ struct yaffs_param param; -+ struct yaffs_driver drv; -+ struct yaffs_tags_handler tagger; -+ -+ /* Context storage. Holds extra OS specific data for this device */ -+ -+ void *os_context; -+ void *driver_context; -+ -+ struct list_head dev_list; -+ -+ int ll_init; -+ /* Runtime parameters. Set up by YAFFS. */ -+ int data_bytes_per_chunk; -+ -+ /* Non-wide tnode stuff */ -+ u16 chunk_grp_bits; /* Number of bits that need to be resolved if -+ * the tnodes are not wide enough. -+ */ -+ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ -+ -+ /* Stuff to support wide tnodes */ -+ u32 tnode_width; -+ u32 tnode_mask; -+ u32 tnode_size; -+ -+ /* Stuff for figuring out file offset to chunk conversions */ -+ u32 chunk_shift; /* Shift value */ -+ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ -+ u32 chunk_mask; /* Mask to use for power-of-2 case */ -+ -+ int is_mounted; -+ int read_only; -+ int is_checkpointed; -+ -+ /* Stuff to support block offsetting to support start block zero */ -+ int internal_start_block; -+ int internal_end_block; -+ int block_offset; -+ int chunk_offset; -+ -+ /* Runtime checkpointing stuff */ -+ int checkpt_page_seq; /* running sequence number of checkpt pages */ -+ int checkpt_byte_count; -+ int checkpt_byte_offs; -+ u8 *checkpt_buffer; -+ int checkpt_open_write; -+ int blocks_in_checkpt; -+ int checkpt_cur_chunk; -+ int checkpt_cur_block; -+ int checkpt_next_block; -+ int *checkpt_block_list; -+ int checkpt_max_blocks; -+ u32 checkpt_sum; -+ u32 checkpt_xor; -+ -+ int checkpoint_blocks_required; /* Number of blocks needed to store -+ * current checkpoint set */ -+ -+ /* Block Info */ -+ struct yaffs_block_info *block_info; -+ u8 *chunk_bits; /* bitmap of chunks in use */ -+ u8 block_info_alt:1; /* allocated using alternative alloc */ -+ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ -+ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. -+ * Must be consistent with chunks_per_block. -+ */ -+ -+ int n_erased_blocks; -+ int alloc_block; /* Current block being allocated off */ -+ u32 alloc_page; -+ int alloc_block_finder; /* Used to search for next allocation block */ -+ -+ /* Object and Tnode memory management */ -+ void *allocator; -+ int n_obj; -+ int n_tnodes; -+ -+ int n_hardlinks; -+ -+ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; -+ u32 bucket_finder; -+ -+ int n_free_chunks; -+ -+ /* Garbage collection control */ -+ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ -+ u32 n_clean_ups; -+ -+ unsigned has_pending_prioritised_gc; /* We think this device might -+ have pending prioritised gcs */ -+ unsigned gc_disable; -+ unsigned gc_block_finder; -+ unsigned gc_dirtiest; -+ unsigned gc_pages_in_use; -+ unsigned gc_not_done; -+ unsigned gc_block; -+ unsigned gc_chunk; -+ unsigned gc_skip; -+ struct yaffs_summary_tags *gc_sum_tags; -+ -+ /* Special directories */ -+ struct yaffs_obj *root_dir; -+ struct yaffs_obj *lost_n_found; -+ -+ int buffered_block; /* Which block is buffered here? */ -+ int doing_buffered_block_rewrite; -+ -+ struct yaffs_cache *cache; -+ int cache_last_use; -+ -+ /* Stuff for background deletion and unlinked files. */ -+ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted -+ files live. */ -+ struct yaffs_obj *del_dir; /* Directory where deleted objects are -+ sent to disappear. */ -+ struct yaffs_obj *unlinked_deletion; /* Current file being -+ background deleted. */ -+ int n_deleted_files; /* Count of files awaiting deletion; */ -+ int n_unlinked_files; /* Count of unlinked files. */ -+ int n_bg_deletions; /* Count of background deletions. */ -+ -+ /* Temporary buffer management */ -+ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; -+ int max_temp; -+ int temp_in_use; -+ int unmanaged_buffer_allocs; -+ int unmanaged_buffer_deallocs; -+ -+ /* yaffs2 runtime stuff */ -+ unsigned seq_number; /* Sequence number of currently -+ allocating block */ -+ unsigned oldest_dirty_seq; -+ unsigned oldest_dirty_block; -+ -+ /* Block refreshing */ -+ int refresh_skip; /* A skip down counter. -+ * Refresh happens when this gets to zero. */ -+ -+ /* Dirty directory handling */ -+ struct list_head dirty_dirs; /* List of dirty directories */ -+ -+ /* Summary */ -+ int chunks_per_summary; -+ struct yaffs_summary_tags *sum_tags; -+ -+ /* Statistics */ -+ u32 n_page_writes; -+ u32 n_page_reads; -+ u32 n_erasures; -+ u32 n_bad_queries; -+ u32 n_bad_markings; -+ u32 n_erase_failures; -+ u32 n_gc_copies; -+ u32 all_gcs; -+ u32 passive_gc_count; -+ u32 oldest_dirty_gc_count; -+ u32 n_gc_blocks; -+ u32 bg_gcs; -+ u32 n_retried_writes; -+ u32 n_retired_blocks; -+ u32 n_ecc_fixed; -+ u32 n_ecc_unfixed; -+ u32 n_tags_ecc_fixed; -+ u32 n_tags_ecc_unfixed; -+ u32 n_deletions; -+ u32 n_unmarked_deletions; -+ u32 refresh_count; -+ u32 cache_hits; -+ u32 tags_used; -+ u32 summary_used; -+ -+}; -+ -+/* The CheckpointDevice structure holds the device information that changes -+ *at runtime and must be preserved over unmount/mount cycles. -+ */ -+struct yaffs_checkpt_dev { -+ int struct_type; -+ int n_erased_blocks; -+ int alloc_block; /* Current block being allocated off */ -+ u32 alloc_page; -+ int n_free_chunks; -+ -+ int n_deleted_files; /* Count of files awaiting deletion; */ -+ int n_unlinked_files; /* Count of unlinked files. */ -+ int n_bg_deletions; /* Count of background deletions. */ -+ -+ /* yaffs2 runtime stuff */ -+ unsigned seq_number; /* Sequence number of currently -+ * allocating block */ -+ -+}; -+ -+struct yaffs_checkpt_validity { -+ int struct_type; -+ u32 magic; -+ u32 version; -+ u32 head; -+}; -+ -+struct yaffs_shadow_fixer { -+ int obj_id; -+ int shadowed_id; -+ struct yaffs_shadow_fixer *next; -+}; -+ -+/* Structure for doing xattr modifications */ -+struct yaffs_xattr_mod { -+ int set; /* If 0 then this is a deletion */ -+ const YCHAR *name; -+ const void *data; -+ int size; -+ int flags; -+ int result; -+}; -+ -+/*----------------------- YAFFS Functions -----------------------*/ -+ -+int yaffs_guts_initialise(struct yaffs_dev *dev); -+void yaffs_deinitialise(struct yaffs_dev *dev); -+ -+int yaffs_get_n_free_chunks(struct yaffs_dev *dev); -+ -+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, -+ struct yaffs_obj *new_dir, const YCHAR * new_name); -+ -+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); -+int yaffs_del_obj(struct yaffs_obj *obj); -+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, -+ enum yaffs_obj_type type); -+ -+ -+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); -+loff_t yaffs_get_obj_length(struct yaffs_obj *obj); -+int yaffs_get_obj_inode(struct yaffs_obj *obj); -+unsigned yaffs_get_obj_type(struct yaffs_obj *obj); -+int yaffs_get_obj_link_count(struct yaffs_obj *obj); -+ -+/* File operations */ -+int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, -+ int n_bytes); -+int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, -+ int n_bytes, int write_trhrough); -+int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); -+ -+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid); -+ -+int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync); -+ -+/* Flushing and checkpointing */ -+void yaffs_flush_whole_cache(struct yaffs_dev *dev); -+ -+int yaffs_checkpoint_save(struct yaffs_dev *dev); -+int yaffs_checkpoint_restore(struct yaffs_dev *dev); -+ -+/* Directory operations */ -+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, -+ u32 mode, u32 uid, u32 gid); -+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, -+ const YCHAR *name); -+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); -+ -+/* Link operations */ -+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, -+ struct yaffs_obj *equiv_obj); -+ -+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); -+ -+/* Symlink operations */ -+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, const YCHAR *alias); -+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); -+ -+/* Special inodes (fifos, sockets and devices) */ -+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, u32 rdev); -+ -+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, -+ const void *value, int size, int flags); -+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, -+ int size); -+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); -+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); -+ -+/* Special directories */ -+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); -+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); -+ -+void yaffs_handle_defered_free(struct yaffs_obj *obj); -+ -+void yaffs_update_dirty_dirs(struct yaffs_dev *dev); -+ -+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); -+ -+/* Debug dump */ -+int yaffs_dump_obj(struct yaffs_obj *obj); -+ -+void yaffs_guts_test(struct yaffs_dev *dev); -+int yaffs_guts_ll_init(struct yaffs_dev *dev); -+ -+ -+/* A few useful functions to be used within the core files*/ -+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, -+ int lyn); -+int yaffs_check_ff(u8 *buffer, int n_bytes); -+void yaffs_handle_chunk_error(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi); -+ -+u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); -+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); -+ -+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, -+ int number, -+ enum yaffs_obj_type type); -+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ int nand_chunk, int in_scan); -+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); -+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, -+ const struct yaffs_obj_hdr *oh); -+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); -+YCHAR *yaffs_clone_str(const YCHAR *str); -+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); -+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); -+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, -+ int force, int is_shrink, int shadows, -+ struct yaffs_xattr_mod *xop); -+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, -+ int backward_scanning); -+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); -+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); -+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id, -+ struct yaffs_tnode *passed_tn); -+ -+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_trhrough); -+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); -+void yaffs_skip_rest_of_block(struct yaffs_dev *dev); -+ -+int yaffs_count_free_chunks(struct yaffs_dev *dev); -+ -+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id); -+ -+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos); -+ -+int yaffs_is_non_empty_dir(struct yaffs_obj *obj); -+ -+int yaffs_guts_format_dev(struct yaffs_dev *dev); -+ -+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, -+ int *chunk_out, u32 *offset_out); -+/* -+ * Marshalling functions to get loff_t file sizes into aand out of -+ * object headers. -+ */ -+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); -+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); -+loff_t yaffs_max_file_size(struct yaffs_dev *dev); -+ -+/* -+ * Debug function to count number of blocks in each state -+ * NB Needs to be called with correct number of integers -+ */ -+ -+void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); -+ -+int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_linux.h linux-3.12.20/fs/yaffs2/yaffs_linux.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_linux.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,48 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_LINUX_H__ -+#define __YAFFS_LINUX_H__ -+ -+#include "yportenv.h" -+ -+struct yaffs_linux_context { -+ struct list_head context_list; /* List of these we have mounted */ -+ struct yaffs_dev *dev; -+ struct super_block *super; -+ struct task_struct *bg_thread; /* Background thread for this device */ -+ int bg_running; -+ struct mutex gross_lock; /* Gross locking mutex*/ -+ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size -+ * at compile time so we have to allocate it. -+ */ -+ struct list_head search_contexts; -+ struct task_struct *readdir_process; -+ unsigned mount_id; -+ int dirty; -+}; -+ -+#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) -+#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+#define WRITE_SIZE_STR "writesize" -+#define WRITE_SIZE(mtd) ((mtd)->writesize) -+#else -+#define WRITE_SIZE_STR "oobblock" -+#define WRITE_SIZE(mtd) ((mtd)->oobblock) -+#endif -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_mtdif.c linux-3.12.20/fs/yaffs2/yaffs_mtdif.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_mtdif.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,309 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yportenv.h" -+ -+#include "yaffs_mtdif.h" -+ -+#include "linux/mtd/mtd.h" -+#include "linux/types.h" -+#include "linux/time.h" -+#include "linux/major.h" -+#include "linux/mtd/nand.h" -+#include "linux/kernel.h" -+#include "linux/version.h" -+#include "linux/types.h" -+ -+#include "yaffs_trace.h" -+#include "yaffs_guts.h" -+#include "yaffs_linux.h" -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) -+#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) -+#define mtd_erase(m, ei) (m)->erase(m, ei) -+#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) -+#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) -+#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) -+#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) -+#endif -+ -+ -+ -+int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ u32 addr = -+ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * -+ dev->param.chunks_per_block; -+ struct erase_info ei; -+ int retval = 0; -+ -+ ei.mtd = mtd; -+ ei.addr = addr; -+ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; -+ ei.time = 1000; -+ ei.retries = 2; -+ ei.callback = NULL; -+ ei.priv = (u_long) dev; -+ -+ retval = mtd_erase(mtd, &ei); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+ -+static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, int data_len, -+ const u8 *oob, int oob_len) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ loff_t addr; -+ struct mtd_oob_ops ops; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", -+ dev, nand_chunk, data, data_len, oob, oob_len); -+ -+ if (!data || !data_len) { -+ data = NULL; -+ data_len = 0; -+ } -+ -+ if (!oob || !oob_len) { -+ oob = NULL; -+ oob_len = 0; -+ } -+ -+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; -+ memset(&ops, 0, sizeof(ops)); -+ ops.mode = MTD_OPS_AUTO_OOB; -+ ops.len = (data) ? data_len : 0; -+ ops.ooblen = oob_len; -+ ops.datbuf = (u8 *)data; -+ ops.oobbuf = (u8 *)oob; -+ -+ retval = mtd_write_oob(mtd, addr, &ops); -+ if (retval) { -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "write_oob failed, chunk %d, mtd error %d", -+ nand_chunk, retval); -+ } -+ return retval ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, -+ u8 *data, int data_len, -+ u8 *oob, int oob_len, -+ enum yaffs_ecc_result *ecc_result) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ loff_t addr; -+ struct mtd_oob_ops ops; -+ int retval; -+ -+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; -+ memset(&ops, 0, sizeof(ops)); -+ ops.mode = MTD_OPS_AUTO_OOB; -+ ops.len = (data) ? data_len : 0; -+ ops.ooblen = oob_len; -+ ops.datbuf = data; -+ ops.oobbuf = oob; -+ -+#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) -+ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; -+ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. -+ */ -+ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; -+#endif -+ /* Read page and oob using MTD. -+ * Check status and determine ECC result. -+ */ -+ retval = mtd_read_oob(mtd, addr, &ops); -+ if (retval) -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "read_oob failed, chunk %d, mtd error %d", -+ nand_chunk, retval); -+ -+ switch (retval) { -+ case 0: -+ /* no error */ -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ break; -+ -+ case -EUCLEAN: -+ /* MTD's ECC fixed the data */ -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_FIXED; -+ dev->n_ecc_fixed++; -+ break; -+ -+ case -EBADMSG: -+ default: -+ /* MTD's ECC could not fix the data */ -+ dev->n_ecc_unfixed++; -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ return YAFFS_FAIL; -+ } -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ -+ loff_t addr; -+ struct erase_info ei; -+ int retval = 0; -+ u32 block_size; -+ -+ block_size = dev->param.total_bytes_per_chunk * -+ dev->param.chunks_per_block; -+ addr = ((loff_t) block_no) * block_size; -+ -+ ei.mtd = mtd; -+ ei.addr = addr; -+ ei.len = block_size; -+ ei.time = 1000; -+ ei.retries = 2; -+ ei.callback = NULL; -+ ei.priv = (u_long) dev; -+ -+ retval = mtd_erase(mtd, &ei); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); -+ -+ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); -+ return (retval) ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); -+ -+ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); -+ return (retval) ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_initialise(struct yaffs_dev *dev) -+{ -+ return YAFFS_OK; -+} -+ -+static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) -+{ -+ return YAFFS_OK; -+} -+ -+ -+void yaffs_mtd_drv_install(struct yaffs_dev *dev) -+{ -+ struct yaffs_driver *drv = &dev->drv; -+ -+ drv->drv_write_chunk_fn = yaffs_mtd_write; -+ drv->drv_read_chunk_fn = yaffs_mtd_read; -+ drv->drv_erase_fn = yaffs_mtd_erase; -+ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; -+ drv->drv_check_bad_fn = yaffs_mtd_check_bad; -+ drv->drv_initialise_fn = yaffs_mtd_initialise; -+ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; -+} -+ -+ -+struct mtd_info * yaffs_get_mtd_device(dev_t sdev) -+{ -+ struct mtd_info *mtd; -+ -+ mtd = yaffs_get_mtd_device(sdev); -+ -+ /* Check it's an mtd device..... */ -+ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) -+ return NULL; /* This isn't an mtd device */ -+ -+ /* Check it's NAND */ -+ if (mtd->type != MTD_NANDFLASH) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: MTD device is not NAND it's type %d", -+ mtd->type); -+ return NULL; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); -+ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); -+ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -+ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); -+#else -+ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); -+#endif -+ -+ return mtd; -+} -+ -+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) -+{ -+ if (yaffs_version == 2) { -+ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || -+ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && -+ !inband_tags) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device does not have the right page sizes" -+ ); -+ return -1; -+ } -+ } else { -+ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || -+ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device does not support have the right page sizes" -+ ); -+ return -1; -+ } -+ } -+ -+ return 0; -+} -+ -+ -+void yaffs_put_mtd_device(struct mtd_info *mtd) -+{ -+ if(mtd) -+ put_mtd_device(mtd); -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_mtdif.h linux-3.12.20/fs/yaffs2/yaffs_mtdif.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_mtdif.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,25 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_MTDIF_H__ -+#define __YAFFS_MTDIF_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_mtd_drv_install(struct yaffs_dev *dev); -+struct mtd_info * yaffs_get_mtd_device(dev_t sdev); -+void yaffs_put_mtd_device(struct mtd_info *mtd); -+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_nameval.c linux-3.12.20/fs/yaffs2/yaffs_nameval.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_nameval.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,208 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * This simple implementation of a name-value store assumes a small number of -+* values and fits into a small finite buffer. -+ * -+ * Each attribute is stored as a record: -+ * sizeof(int) bytes record size. -+ * strnlen+1 bytes name null terminated. -+ * nbytes value. -+ * ---------- -+ * total size stored in record size -+ * -+ * This code has not been tested with unicode yet. -+ */ -+ -+#include "yaffs_nameval.h" -+ -+#include "yportenv.h" -+ -+static int nval_find(const char *xb, int xb_size, const YCHAR *name, -+ int *exist_size) -+{ -+ int pos = 0; -+ int size; -+ -+ memcpy(&size, xb, sizeof(int)); -+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { -+ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), -+ name, size)) { -+ if (exist_size) -+ *exist_size = size; -+ return pos; -+ } -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ if (exist_size) -+ *exist_size = 0; -+ return -ENODATA; -+} -+ -+static int nval_used(const char *xb, int xb_size) -+{ -+ int pos = 0; -+ int size; -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ return pos; -+} -+ -+int nval_del(char *xb, int xb_size, const YCHAR *name) -+{ -+ int pos = nval_find(xb, xb_size, name, NULL); -+ int size; -+ -+ if (pos < 0 || pos >= xb_size) -+ return -ENODATA; -+ -+ /* Find size, shift rest over this record, -+ * then zero out the rest of buffer */ -+ memcpy(&size, xb + pos, sizeof(int)); -+ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); -+ memset(xb + (xb_size - size), 0, size); -+ return 0; -+} -+ -+int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, -+ int bsize, int flags) -+{ -+ int pos; -+ int namelen = strnlen(name, xb_size); -+ int reclen; -+ int size_exist = 0; -+ int space; -+ int start; -+ -+ pos = nval_find(xb, xb_size, name, &size_exist); -+ -+ if (flags & XATTR_CREATE && pos >= 0) -+ return -EEXIST; -+ if (flags & XATTR_REPLACE && pos < 0) -+ return -ENODATA; -+ -+ start = nval_used(xb, xb_size); -+ space = xb_size - start + size_exist; -+ -+ reclen = (sizeof(int) + namelen + 1 + bsize); -+ -+ if (reclen > space) -+ return -ENOSPC; -+ -+ if (pos >= 0) { -+ nval_del(xb, xb_size, name); -+ start = nval_used(xb, xb_size); -+ } -+ -+ pos = start; -+ -+ memcpy(xb + pos, &reclen, sizeof(int)); -+ pos += sizeof(int); -+ strncpy((YCHAR *) (xb + pos), name, reclen); -+ pos += (namelen + 1); -+ memcpy(xb + pos, buf, bsize); -+ return 0; -+} -+ -+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, -+ int bsize) -+{ -+ int pos = nval_find(xb, xb_size, name, NULL); -+ int size; -+ -+ if (pos >= 0 && pos < xb_size) { -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ pos += sizeof(int); /* advance past record length */ -+ size -= sizeof(int); -+ -+ /* Advance over name string */ -+ while (xb[pos] && size > 0 && pos < xb_size) { -+ pos++; -+ size--; -+ } -+ /*Advance over NUL */ -+ pos++; -+ size--; -+ -+ /* If bsize is zero then this is a size query. -+ * Return the size, but don't copy. -+ */ -+ if (!bsize) -+ return size; -+ -+ if (size <= bsize) { -+ memcpy(buf, xb + pos, size); -+ return size; -+ } -+ } -+ if (pos >= 0) -+ return -ERANGE; -+ -+ return -ENODATA; -+} -+ -+int nval_list(const char *xb, int xb_size, char *buf, int bsize) -+{ -+ int pos = 0; -+ int size; -+ int name_len; -+ int ncopied = 0; -+ int filled = 0; -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ while (size > sizeof(int) && -+ size <= xb_size && -+ (pos + size) < xb_size && -+ !filled) { -+ pos += sizeof(int); -+ size -= sizeof(int); -+ name_len = strnlen((YCHAR *) (xb + pos), size); -+ if (ncopied + name_len + 1 < bsize) { -+ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); -+ buf += name_len; -+ *buf = '\0'; -+ buf++; -+ if (sizeof(YCHAR) > 1) { -+ *buf = '\0'; -+ buf++; -+ } -+ ncopied += (name_len + 1); -+ } else { -+ filled = 1; -+ } -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ return ncopied; -+} -+ -+int nval_hasvalues(const char *xb, int xb_size) -+{ -+ return nval_used(xb, xb_size) > 0; -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_nameval.h linux-3.12.20/fs/yaffs2/yaffs_nameval.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_nameval.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,28 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __NAMEVAL_H__ -+#define __NAMEVAL_H__ -+ -+#include "yportenv.h" -+ -+int nval_del(char *xb, int xb_size, const YCHAR * name); -+int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, -+ int bsize, int flags); -+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, -+ int bsize); -+int nval_list(const char *xb, int xb_size, char *buf, int bsize); -+int nval_hasvalues(const char *xb, int xb_size); -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_nand.c linux-3.12.20/fs/yaffs2/yaffs_nand.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_nand.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,122 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_nand.h" -+#include "yaffs_tagscompat.h" -+ -+#include "yaffs_getblockinfo.h" -+#include "yaffs_summary.h" -+ -+static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) -+{ -+ return chunk - dev->chunk_offset; -+} -+ -+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, -+ u8 *buffer, struct yaffs_ext_tags *tags) -+{ -+ int result; -+ struct yaffs_ext_tags local_tags; -+ int flash_chunk = apply_chunk_offset(dev, nand_chunk); -+ -+ dev->n_page_reads++; -+ -+ /* If there are no tags provided use local tags. */ -+ if (!tags) -+ tags = &local_tags; -+ -+ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); -+ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { -+ -+ struct yaffs_block_info *bi; -+ bi = yaffs_get_block_info(dev, -+ nand_chunk / -+ dev->param.chunks_per_block); -+ yaffs_handle_chunk_error(dev, bi); -+ } -+ return result; -+} -+ -+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *buffer, struct yaffs_ext_tags *tags) -+{ -+ int result; -+ int flash_chunk = apply_chunk_offset(dev, nand_chunk); -+ -+ dev->n_page_writes++; -+ -+ if (!tags) { -+ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ tags->seq_number = dev->seq_number; -+ tags->chunk_used = 1; -+ yaffs_trace(YAFFS_TRACE_WRITE, -+ "Writing chunk %d tags %d %d", -+ nand_chunk, tags->obj_id, tags->chunk_id); -+ -+ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, -+ buffer, tags); -+ -+ yaffs_summary_add(dev, tags, nand_chunk); -+ -+ return result; -+} -+ -+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ block_no -= dev->block_offset; -+ dev->n_bad_markings++; -+ -+ if (dev->param.disable_bad_block_marking) -+ return YAFFS_OK; -+ -+ return dev->tagger.mark_bad_fn(dev, block_no); -+} -+ -+ -+int yaffs_query_init_block_state(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ block_no -= dev->block_offset; -+ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); -+} -+ -+int yaffs_erase_block(struct yaffs_dev *dev, int block_no) -+{ -+ int result; -+ -+ block_no -= dev->block_offset; -+ dev->n_erasures++; -+ result = dev->drv.drv_erase_fn(dev, block_no); -+ return result; -+} -+ -+int yaffs_init_nand(struct yaffs_dev *dev) -+{ -+ if (dev->drv.drv_initialise_fn) -+ return dev->drv.drv_initialise_fn(dev); -+ return YAFFS_OK; -+} -+ -+int yaffs_deinit_nand(struct yaffs_dev *dev) -+{ -+ if (dev->drv.drv_deinitialise_fn) -+ return dev->drv.drv_deinitialise_fn(dev); -+ return YAFFS_OK; -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_nand.h linux-3.12.20/fs/yaffs2/yaffs_nand.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_nand.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_NAND_H__ -+#define __YAFFS_NAND_H__ -+#include "yaffs_guts.h" -+ -+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, -+ u8 *buffer, struct yaffs_ext_tags *tags); -+ -+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *buffer, struct yaffs_ext_tags *tags); -+ -+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); -+ -+int yaffs_query_init_block_state(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ unsigned *seq_number); -+ -+int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); -+ -+int yaffs_init_nand(struct yaffs_dev *dev); -+int yaffs_deinit_nand(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.12.20/fs/yaffs2/yaffs_packedtags1.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,56 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_packedtags1.h" -+#include "yportenv.h" -+ -+static const u8 all_ff[20] = { -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff -+}; -+ -+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, -+ const struct yaffs_ext_tags *t) -+{ -+ pt->chunk_id = t->chunk_id; -+ pt->serial_number = t->serial_number; -+ pt->n_bytes = t->n_bytes; -+ pt->obj_id = t->obj_id; -+ pt->ecc = 0; -+ pt->deleted = (t->is_deleted) ? 0 : 1; -+ pt->unused_stuff = 0; -+ pt->should_be_ff = 0xffffffff; -+} -+ -+void yaffs_unpack_tags1(struct yaffs_ext_tags *t, -+ const struct yaffs_packed_tags1 *pt) -+{ -+ -+ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { -+ t->block_bad = 0; -+ if (pt->should_be_ff != 0xffffffff) -+ t->block_bad = 1; -+ t->chunk_used = 1; -+ t->obj_id = pt->obj_id; -+ t->chunk_id = pt->chunk_id; -+ t->n_bytes = pt->n_bytes; -+ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ t->is_deleted = (pt->deleted) ? 0 : 1; -+ t->serial_number = pt->serial_number; -+ } else { -+ memset(t, 0, sizeof(struct yaffs_ext_tags)); -+ } -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.12.20/fs/yaffs2/yaffs_packedtags1.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ -+ -+#ifndef __YAFFS_PACKEDTAGS1_H__ -+#define __YAFFS_PACKEDTAGS1_H__ -+ -+#include "yaffs_guts.h" -+ -+struct yaffs_packed_tags1 { -+ u32 chunk_id:20; -+ u32 serial_number:2; -+ u32 n_bytes:10; -+ u32 obj_id:18; -+ u32 ecc:12; -+ u32 deleted:1; -+ u32 unused_stuff:1; -+ unsigned should_be_ff; -+ -+}; -+ -+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, -+ const struct yaffs_ext_tags *t); -+void yaffs_unpack_tags1(struct yaffs_ext_tags *t, -+ const struct yaffs_packed_tags1 *pt); -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.12.20/fs/yaffs2/yaffs_packedtags2.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,197 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_packedtags2.h" -+#include "yportenv.h" -+#include "yaffs_trace.h" -+ -+/* This code packs a set of extended tags into a binary structure for -+ * NAND storage -+ */ -+ -+/* Some of the information is "extra" struff which can be packed in to -+ * speed scanning -+ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. -+ */ -+ -+/* Extra flags applied to chunk_id */ -+ -+#define EXTRA_HEADER_INFO_FLAG 0x80000000 -+#define EXTRA_SHRINK_FLAG 0x40000000 -+#define EXTRA_SHADOWS_FLAG 0x20000000 -+#define EXTRA_SPARE_FLAGS 0x10000000 -+ -+#define ALL_EXTRA_FLAGS 0xf0000000 -+ -+/* Also, the top 4 bits of the object Id are set to the object type. */ -+#define EXTRA_OBJECT_TYPE_SHIFT (28) -+#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) -+ -+static void yaffs_dump_packed_tags2_tags_only( -+ const struct yaffs_packed_tags2_tags_only *ptt) -+{ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "packed tags obj %d chunk %d byte %d seq %d", -+ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); -+} -+ -+static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) -+{ -+ yaffs_dump_packed_tags2_tags_only(&pt->t); -+} -+ -+static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) -+{ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", -+ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, -+ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, -+ t->seq_number); -+ -+} -+ -+static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) -+{ -+ if (t->chunk_id != 0 || !t->extra_available) -+ return 0; -+ -+ /* Check if the file size is too long to store */ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && -+ (t->extra_file_size >> 31) != 0) -+ return 0; -+ return 1; -+} -+ -+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, -+ const struct yaffs_ext_tags *t) -+{ -+ ptt->chunk_id = t->chunk_id; -+ ptt->seq_number = t->seq_number; -+ ptt->n_bytes = t->n_bytes; -+ ptt->obj_id = t->obj_id; -+ -+ /* Only store extra tags for object headers. -+ * If it is a file then only store if the file size is short\ -+ * enough to fit. -+ */ -+ if (yaffs_check_tags_extra_packable(t)) { -+ /* Store the extra header info instead */ -+ /* We save the parent object in the chunk_id */ -+ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; -+ if (t->extra_is_shrink) -+ ptt->chunk_id |= EXTRA_SHRINK_FLAG; -+ if (t->extra_shadows) -+ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; -+ -+ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; -+ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); -+ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ ptt->n_bytes = t->extra_equiv_id; -+ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) -+ ptt->n_bytes = (unsigned) t->extra_file_size; -+ else -+ ptt->n_bytes = 0; -+ } -+ -+ yaffs_dump_packed_tags2_tags_only(ptt); -+ yaffs_dump_tags2(t); -+} -+ -+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, -+ const struct yaffs_ext_tags *t, int tags_ecc) -+{ -+ yaffs_pack_tags2_tags_only(&pt->t, t); -+ -+ if (tags_ecc) -+ yaffs_ecc_calc_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &pt->ecc); -+} -+ -+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, -+ struct yaffs_packed_tags2_tags_only *ptt) -+{ -+ memset(t, 0, sizeof(struct yaffs_ext_tags)); -+ -+ if (ptt->seq_number == 0xffffffff) -+ return; -+ -+ t->block_bad = 0; -+ t->chunk_used = 1; -+ t->obj_id = ptt->obj_id; -+ t->chunk_id = ptt->chunk_id; -+ t->n_bytes = ptt->n_bytes; -+ t->is_deleted = 0; -+ t->serial_number = 0; -+ t->seq_number = ptt->seq_number; -+ -+ /* Do extra header info stuff */ -+ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { -+ t->chunk_id = 0; -+ t->n_bytes = 0; -+ -+ t->extra_available = 1; -+ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); -+ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; -+ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; -+ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; -+ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; -+ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ t->extra_equiv_id = ptt->n_bytes; -+ else -+ t->extra_file_size = ptt->n_bytes; -+ } -+ yaffs_dump_packed_tags2_tags_only(ptt); -+ yaffs_dump_tags2(t); -+} -+ -+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, -+ int tags_ecc) -+{ -+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ -+ if (pt->t.seq_number != 0xffffffff && tags_ecc) { -+ /* Chunk is in use and we need to do ECC */ -+ -+ struct yaffs_ecc_other ecc; -+ int result; -+ yaffs_ecc_calc_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &ecc); -+ result = -+ yaffs_ecc_correct_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &pt->ecc, &ecc); -+ switch (result) { -+ case 0: -+ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ break; -+ case 1: -+ ecc_result = YAFFS_ECC_RESULT_FIXED; -+ break; -+ case -1: -+ ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ break; -+ default: -+ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; -+ } -+ } -+ yaffs_unpack_tags2_tags_only(t, &pt->t); -+ -+ t->ecc_result = ecc_result; -+ -+ yaffs_dump_packed_tags2(pt); -+ yaffs_dump_tags2(t); -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.12.20/fs/yaffs2/yaffs_packedtags2.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,47 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ -+ -+#ifndef __YAFFS_PACKEDTAGS2_H__ -+#define __YAFFS_PACKEDTAGS2_H__ -+ -+#include "yaffs_guts.h" -+#include "yaffs_ecc.h" -+ -+struct yaffs_packed_tags2_tags_only { -+ unsigned seq_number; -+ unsigned obj_id; -+ unsigned chunk_id; -+ unsigned n_bytes; -+}; -+ -+struct yaffs_packed_tags2 { -+ struct yaffs_packed_tags2_tags_only t; -+ struct yaffs_ecc_other ecc; -+}; -+ -+/* Full packed tags with ECC, used for oob tags */ -+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, -+ const struct yaffs_ext_tags *t, int tags_ecc); -+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, -+ int tags_ecc); -+ -+/* Only the tags part (no ECC for use with inband tags */ -+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, -+ const struct yaffs_ext_tags *t); -+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, -+ struct yaffs_packed_tags2_tags_only *pt); -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_summary.c linux-3.12.20/fs/yaffs2/yaffs_summary.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_summary.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,312 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Summaries write the useful part of the tags for the chunks in a block into an -+ * an array which is written to the last n chunks of the block. -+ * Reading the summaries gives all the tags for the block in one read. Much -+ * faster. -+ * -+ * Chunks holding summaries are marked with tags making it look like -+ * they are part of a fake file. -+ * -+ * The summary could also be used during gc. -+ * -+ */ -+ -+#include "yaffs_summary.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_nand.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_bitmap.h" -+ -+/* -+ * The summary is built up in an array of summary tags. -+ * This gets written to the last one or two (maybe more) chunks in a block. -+ * A summary header is written as the first part of each chunk of summary data. -+ * The summary header must match or the summary is rejected. -+ */ -+ -+/* Summary tags don't need the sequence number because that is redundant. */ -+struct yaffs_summary_tags { -+ unsigned obj_id; -+ unsigned chunk_id; -+ unsigned n_bytes; -+}; -+ -+/* Summary header */ -+struct yaffs_summary_header { -+ unsigned version; /* Must match current version */ -+ unsigned block; /* Must be this block */ -+ unsigned seq; /* Must be this sequence number */ -+ unsigned sum; /* Just add up all the bytes in the tags */ -+}; -+ -+ -+static void yaffs_summary_clear(struct yaffs_dev *dev) -+{ -+ if (!dev->sum_tags) -+ return; -+ memset(dev->sum_tags, 0, dev->chunks_per_summary * -+ sizeof(struct yaffs_summary_tags)); -+} -+ -+ -+void yaffs_summary_deinit(struct yaffs_dev *dev) -+{ -+ kfree(dev->sum_tags); -+ dev->sum_tags = NULL; -+ kfree(dev->gc_sum_tags); -+ dev->gc_sum_tags = NULL; -+ dev->chunks_per_summary = 0; -+} -+ -+int yaffs_summary_init(struct yaffs_dev *dev) -+{ -+ int sum_bytes; -+ int chunks_used; /* Number of chunks used by summary */ -+ int sum_tags_bytes; -+ -+ sum_bytes = dev->param.chunks_per_block * -+ sizeof(struct yaffs_summary_tags); -+ -+ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ -+ (dev->data_bytes_per_chunk - -+ sizeof(struct yaffs_summary_header)); -+ -+ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; -+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); -+ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); -+ if (!dev->sum_tags || !dev->gc_sum_tags) { -+ yaffs_summary_deinit(dev); -+ return YAFFS_FAIL; -+ } -+ -+ yaffs_summary_clear(dev); -+ -+ return YAFFS_OK; -+} -+ -+static unsigned yaffs_summary_sum(struct yaffs_dev *dev) -+{ -+ u8 *sum_buffer = (u8 *)dev->sum_tags; -+ int i; -+ unsigned sum = 0; -+ -+ i = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ while (i > 0) { -+ sum += *sum_buffer; -+ sum_buffer++; -+ i--; -+ } -+ -+ return sum; -+} -+ -+static int yaffs_summary_write(struct yaffs_dev *dev, int blk) -+{ -+ struct yaffs_ext_tags tags; -+ u8 *buffer; -+ u8 *sum_buffer = (u8 *)dev->sum_tags; -+ int n_bytes; -+ int chunk_in_nand; -+ int chunk_in_block; -+ int result; -+ int this_tx; -+ struct yaffs_summary_header hdr; -+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ -+ buffer = yaffs_get_temp_buffer(dev); -+ n_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); -+ tags.obj_id = YAFFS_OBJECTID_SUMMARY; -+ tags.chunk_id = 1; -+ chunk_in_block = dev->chunks_per_summary; -+ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + -+ dev->chunks_per_summary; -+ hdr.version = YAFFS_SUMMARY_VERSION; -+ hdr.block = blk; -+ hdr.seq = bi->seq_number; -+ hdr.sum = yaffs_summary_sum(dev); -+ -+ do { -+ this_tx = n_bytes; -+ if (this_tx > sum_bytes_per_chunk) -+ this_tx = sum_bytes_per_chunk; -+ memcpy(buffer, &hdr, sizeof(hdr)); -+ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); -+ tags.n_bytes = this_tx + sizeof(hdr); -+ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, -+ buffer, &tags); -+ -+ if (result != YAFFS_OK) -+ break; -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ dev->n_free_chunks--; -+ -+ n_bytes -= this_tx; -+ sum_buffer += this_tx; -+ chunk_in_nand++; -+ chunk_in_block++; -+ tags.chunk_id++; -+ } while (result == YAFFS_OK && n_bytes > 0); -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ -+ if (result == YAFFS_OK) -+ bi->has_summary = 1; -+ -+ -+ return result; -+} -+ -+int yaffs_summary_read(struct yaffs_dev *dev, -+ struct yaffs_summary_tags *st, -+ int blk) -+{ -+ struct yaffs_ext_tags tags; -+ u8 *buffer; -+ u8 *sum_buffer = (u8 *)st; -+ int n_bytes; -+ int chunk_id; -+ int chunk_in_nand; -+ int chunk_in_block; -+ int result; -+ int this_tx; -+ struct yaffs_summary_header hdr; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); -+ int sum_tags_bytes; -+ -+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ buffer = yaffs_get_temp_buffer(dev); -+ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; -+ chunk_in_block = dev->chunks_per_summary; -+ chunk_in_nand = blk * dev->param.chunks_per_block + -+ dev->chunks_per_summary; -+ chunk_id = 1; -+ do { -+ this_tx = n_bytes; -+ if (this_tx > sum_bytes_per_chunk) -+ this_tx = sum_bytes_per_chunk; -+ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, -+ buffer, &tags); -+ -+ if (tags.chunk_id != chunk_id || -+ tags.obj_id != YAFFS_OBJECTID_SUMMARY || -+ tags.chunk_used == 0 || -+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || -+ tags.n_bytes != (this_tx + sizeof(hdr))) -+ result = YAFFS_FAIL; -+ if (result != YAFFS_OK) -+ break; -+ -+ if (st == dev->sum_tags) { -+ /* If we're scanning then update the block info */ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ } -+ memcpy(&hdr, buffer, sizeof(hdr)); -+ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); -+ n_bytes -= this_tx; -+ sum_buffer += this_tx; -+ chunk_in_nand++; -+ chunk_in_block++; -+ chunk_id++; -+ } while (result == YAFFS_OK && n_bytes > 0); -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ if (result == YAFFS_OK) { -+ /* Verify header */ -+ if (hdr.version != YAFFS_SUMMARY_VERSION || -+ hdr.seq != bi->seq_number || -+ hdr.sum != yaffs_summary_sum(dev)) -+ result = YAFFS_FAIL; -+ } -+ -+ if (st == dev->sum_tags && result == YAFFS_OK) -+ bi->has_summary = 1; -+ -+ return result; -+} -+ -+int yaffs_summary_add(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_nand) -+{ -+ struct yaffs_packed_tags2_tags_only tags_only; -+ struct yaffs_summary_tags *sum_tags; -+ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; -+ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; -+ -+ if (!dev->sum_tags) -+ return YAFFS_OK; -+ -+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { -+ yaffs_pack_tags2_tags_only(&tags_only, tags); -+ sum_tags = &dev->sum_tags[chunk_in_block]; -+ sum_tags->chunk_id = tags_only.chunk_id; -+ sum_tags->n_bytes = tags_only.n_bytes; -+ sum_tags->obj_id = tags_only.obj_id; -+ -+ if (chunk_in_block == dev->chunks_per_summary - 1) { -+ /* Time to write out the summary */ -+ yaffs_summary_write(dev, block_in_nand); -+ yaffs_summary_clear(dev); -+ yaffs_skip_rest_of_block(dev); -+ } -+ } -+ return YAFFS_OK; -+} -+ -+int yaffs_summary_fetch(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block) -+{ -+ struct yaffs_packed_tags2_tags_only tags_only; -+ struct yaffs_summary_tags *sum_tags; -+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { -+ sum_tags = &dev->sum_tags[chunk_in_block]; -+ tags_only.chunk_id = sum_tags->chunk_id; -+ tags_only.n_bytes = sum_tags->n_bytes; -+ tags_only.obj_id = sum_tags->obj_id; -+ yaffs_unpack_tags2_tags_only(tags, &tags_only); -+ return YAFFS_OK; -+ } -+ return YAFFS_FAIL; -+} -+ -+void yaffs_summary_gc(struct yaffs_dev *dev, int blk) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ int i; -+ -+ if (!bi->has_summary) -+ return; -+ -+ for (i = dev->chunks_per_summary; -+ i < dev->param.chunks_per_block; -+ i++) { -+ if (yaffs_check_chunk_bit(dev, blk, i)) { -+ yaffs_clear_chunk_bit(dev, blk, i); -+ bi->pages_in_use--; -+ dev->n_free_chunks++; -+ } -+ } -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_summary.h linux-3.12.20/fs/yaffs2/yaffs_summary.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_summary.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,37 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_SUMMARY_H__ -+#define __YAFFS_SUMMARY_H__ -+ -+#include "yaffs_packedtags2.h" -+ -+ -+int yaffs_summary_init(struct yaffs_dev *dev); -+void yaffs_summary_deinit(struct yaffs_dev *dev); -+ -+int yaffs_summary_add(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block); -+int yaffs_summary_fetch(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block); -+int yaffs_summary_read(struct yaffs_dev *dev, -+ struct yaffs_summary_tags *st, -+ int blk); -+void yaffs_summary_gc(struct yaffs_dev *dev, int blk); -+ -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.12.20/fs/yaffs2/yaffs_tagscompat.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,381 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_tagscompat.h" -+#include "yaffs_ecc.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_trace.h" -+ -+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); -+ -+ -+/********** Tags ECC calculations *********/ -+ -+ -+void yaffs_calc_tags_ecc(struct yaffs_tags *tags) -+{ -+ /* Calculate an ecc */ -+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; -+ unsigned i, j; -+ unsigned ecc = 0; -+ unsigned bit = 0; -+ -+ tags->ecc = 0; -+ -+ for (i = 0; i < 8; i++) { -+ for (j = 1; j & 0xff; j <<= 1) { -+ bit++; -+ if (b[i] & j) -+ ecc ^= bit; -+ } -+ } -+ tags->ecc = ecc; -+} -+ -+int yaffs_check_tags_ecc(struct yaffs_tags *tags) -+{ -+ unsigned ecc = tags->ecc; -+ -+ yaffs_calc_tags_ecc(tags); -+ -+ ecc ^= tags->ecc; -+ -+ if (ecc && ecc <= 64) { -+ /* TODO: Handle the failure better. Retire? */ -+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; -+ -+ ecc--; -+ -+ b[ecc / 8] ^= (1 << (ecc & 7)); -+ -+ /* Now recvalc the ecc */ -+ yaffs_calc_tags_ecc(tags); -+ -+ return 1; /* recovered error */ -+ } else if (ecc) { -+ /* Wierd ecc failure value */ -+ /* TODO Need to do somethiong here */ -+ return -1; /* unrecovered error */ -+ } -+ return 0; -+} -+ -+/********** Tags **********/ -+ -+static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, -+ struct yaffs_tags *tags_ptr) -+{ -+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; -+ -+ yaffs_calc_tags_ecc(tags_ptr); -+ -+ spare_ptr->tb0 = tu->as_bytes[0]; -+ spare_ptr->tb1 = tu->as_bytes[1]; -+ spare_ptr->tb2 = tu->as_bytes[2]; -+ spare_ptr->tb3 = tu->as_bytes[3]; -+ spare_ptr->tb4 = tu->as_bytes[4]; -+ spare_ptr->tb5 = tu->as_bytes[5]; -+ spare_ptr->tb6 = tu->as_bytes[6]; -+ spare_ptr->tb7 = tu->as_bytes[7]; -+} -+ -+static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, -+ struct yaffs_spare *spare_ptr, -+ struct yaffs_tags *tags_ptr) -+{ -+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; -+ int result; -+ -+ tu->as_bytes[0] = spare_ptr->tb0; -+ tu->as_bytes[1] = spare_ptr->tb1; -+ tu->as_bytes[2] = spare_ptr->tb2; -+ tu->as_bytes[3] = spare_ptr->tb3; -+ tu->as_bytes[4] = spare_ptr->tb4; -+ tu->as_bytes[5] = spare_ptr->tb5; -+ tu->as_bytes[6] = spare_ptr->tb6; -+ tu->as_bytes[7] = spare_ptr->tb7; -+ -+ result = yaffs_check_tags_ecc(tags_ptr); -+ if (result > 0) -+ dev->n_tags_ecc_fixed++; -+ else if (result < 0) -+ dev->n_tags_ecc_unfixed++; -+} -+ -+static void yaffs_spare_init(struct yaffs_spare *spare) -+{ -+ memset(spare, 0xff, sizeof(struct yaffs_spare)); -+} -+ -+static int yaffs_wr_nand(struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ struct yaffs_spare *spare) -+{ -+ int data_size = dev->data_bytes_per_chunk; -+ -+ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *) spare, sizeof(*spare)); -+} -+ -+static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, -+ struct yaffs_spare *spare, -+ enum yaffs_ecc_result *ecc_result, -+ int correct_errors) -+{ -+ int ret_val; -+ struct yaffs_spare local_spare; -+ int data_size; -+ int spare_size; -+ int ecc_result1, ecc_result2; -+ u8 calc_ecc[3]; -+ -+ if (!spare) { -+ /* If we don't have a real spare, then we use a local one. */ -+ /* Need this for the calculation of the ecc */ -+ spare = &local_spare; -+ } -+ data_size = dev->data_bytes_per_chunk; -+ spare_size = sizeof(struct yaffs_spare); -+ -+ if (dev->param.use_nand_ecc) -+ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *) spare, spare_size, -+ ecc_result); -+ -+ -+ /* Handle the ECC at this level. */ -+ -+ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *)spare, spare_size, -+ NULL); -+ if (!data || !correct_errors) -+ return ret_val; -+ -+ /* Do ECC correction if needed. */ -+ yaffs_ecc_calc(data, calc_ecc); -+ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); -+ yaffs_ecc_calc(&data[256], calc_ecc); -+ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); -+ -+ if (ecc_result1 > 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error fix performed on chunk %d:0", -+ nand_chunk); -+ dev->n_ecc_fixed++; -+ } else if (ecc_result1 < 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error unfixed on chunk %d:0", -+ nand_chunk); -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (ecc_result2 > 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error fix performed on chunk %d:1", -+ nand_chunk); -+ dev->n_ecc_fixed++; -+ } else if (ecc_result2 < 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error unfixed on chunk %d:1", -+ nand_chunk); -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (ecc_result1 || ecc_result2) { -+ /* We had a data problem on this page */ -+ yaffs_handle_rd_data_error(dev, nand_chunk); -+ } -+ -+ if (ecc_result1 < 0 || ecc_result2 < 0) -+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ else if (ecc_result1 > 0 || ecc_result2 > 0) -+ *ecc_result = YAFFS_ECC_RESULT_FIXED; -+ else -+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ -+ return ret_val; -+} -+ -+/* -+ * Functions for robustisizing -+ */ -+ -+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) -+{ -+ int flash_block = nand_chunk / dev->param.chunks_per_block; -+ -+ /* Mark the block for retirement */ -+ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> -+ needs_retiring = 1; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>>Block %d marked for retirement", -+ flash_block); -+ -+ /* TODO: -+ * Just do a garbage collection on the affected block -+ * then retire the block -+ * NB recursion -+ */ -+} -+ -+static int yaffs_tags_compat_wr(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, const struct yaffs_ext_tags *ext_tags) -+{ -+ struct yaffs_spare spare; -+ struct yaffs_tags tags; -+ -+ yaffs_spare_init(&spare); -+ -+ if (ext_tags->is_deleted) -+ spare.page_status = 0; -+ else { -+ tags.obj_id = ext_tags->obj_id; -+ tags.chunk_id = ext_tags->chunk_id; -+ -+ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); -+ -+ if (dev->data_bytes_per_chunk >= 1024) -+ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; -+ else -+ tags.n_bytes_msb = 3; -+ -+ tags.serial_number = ext_tags->serial_number; -+ -+ if (!dev->param.use_nand_ecc && data) { -+ yaffs_ecc_calc(data, spare.ecc1); -+ yaffs_ecc_calc(&data[256], spare.ecc2); -+ } -+ -+ yaffs_load_tags_to_spare(&spare, &tags); -+ } -+ return yaffs_wr_nand(dev, nand_chunk, data, &spare); -+} -+ -+static int yaffs_tags_compat_rd(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, struct yaffs_ext_tags *ext_tags) -+{ -+ struct yaffs_spare spare; -+ struct yaffs_tags tags; -+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; -+ static struct yaffs_spare spare_ff; -+ static int init; -+ int deleted; -+ -+ if (!init) { -+ memset(&spare_ff, 0xff, sizeof(spare_ff)); -+ init = 1; -+ } -+ -+ if (!yaffs_rd_chunk_nand(dev, nand_chunk, -+ data, &spare, &ecc_result, 1)) -+ return YAFFS_FAIL; -+ -+ /* ext_tags may be NULL */ -+ if (!ext_tags) -+ return YAFFS_OK; -+ -+ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; -+ -+ ext_tags->is_deleted = deleted; -+ ext_tags->ecc_result = ecc_result; -+ ext_tags->block_bad = 0; /* We're reading it */ -+ /* therefore it is not a bad block */ -+ ext_tags->chunk_used = -+ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; -+ -+ if (ext_tags->chunk_used) { -+ yaffs_get_tags_from_spare(dev, &spare, &tags); -+ ext_tags->obj_id = tags.obj_id; -+ ext_tags->chunk_id = tags.chunk_id; -+ ext_tags->n_bytes = tags.n_bytes_lsb; -+ -+ if (dev->data_bytes_per_chunk >= 1024) -+ ext_tags->n_bytes |= -+ (((unsigned)tags.n_bytes_msb) << 10); -+ -+ ext_tags->serial_number = tags.serial_number; -+ } -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) -+{ -+ struct yaffs_spare spare; -+ -+ memset(&spare, 0xff, sizeof(struct yaffs_spare)); -+ -+ spare.block_status = 'Y'; -+ -+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, -+ &spare); -+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, -+ NULL, &spare); -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ struct yaffs_spare spare0, spare1; -+ static struct yaffs_spare spare_ff; -+ static int init; -+ enum yaffs_ecc_result dummy; -+ -+ if (!init) { -+ memset(&spare_ff, 0xff, sizeof(spare_ff)); -+ init = 1; -+ } -+ -+ *seq_number = 0; -+ -+ /* Look for bad block markers in the first two chunks */ -+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, -+ NULL, &spare0, &dummy, 0); -+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, -+ NULL, &spare1, &dummy, 0); -+ -+ if (hweight8(spare0.block_status & spare1.block_status) < 7) -+ *state = YAFFS_BLOCK_STATE_DEAD; -+ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) -+ *state = YAFFS_BLOCK_STATE_EMPTY; -+ else -+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; -+ -+ return YAFFS_OK; -+} -+ -+void yaffs_tags_compat_install(struct yaffs_dev *dev) -+{ -+ if(dev->param.is_yaffs2) -+ return; -+ if(!dev->tagger.write_chunk_tags_fn) -+ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; -+ if(!dev->tagger.read_chunk_tags_fn) -+ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; -+ if(!dev->tagger.query_block_fn) -+ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; -+ if(!dev->tagger.mark_bad_fn) -+ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.12.20/fs/yaffs2/yaffs_tagscompat.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,44 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_TAGSCOMPAT_H__ -+#define __YAFFS_TAGSCOMPAT_H__ -+ -+ -+#include "yaffs_guts.h" -+ -+#if 0 -+ -+ -+int yaffs_tags_compat_wr(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, const struct yaffs_ext_tags *tags); -+int yaffs_tags_compat_rd(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, struct yaffs_ext_tags *tags); -+int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); -+int yaffs_tags_compat_query_block(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number); -+ -+#endif -+ -+ -+void yaffs_tags_compat_install(struct yaffs_dev *dev); -+void yaffs_calc_tags_ecc(struct yaffs_tags *tags); -+int yaffs_check_tags_ecc(struct yaffs_tags *tags); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.12.20/fs/yaffs2/yaffs_tagsmarshall.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,199 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yaffs_packedtags2.h" -+ -+static int yaffs_tags_marshall_write(struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ const struct yaffs_ext_tags *tags) -+{ -+ struct yaffs_packed_tags2 pt; -+ int retval; -+ -+ int packed_tags_size = -+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); -+ void *packed_tags_ptr = -+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_tags_marshall_write chunk %d data %p tags %p", -+ nand_chunk, data, tags); -+ -+ /* For yaffs2 writing there must be both data and tags. -+ * If we're using inband tags, then the tags are stuffed into -+ * the end of the data buffer. -+ */ -+ if (!data || !tags) -+ BUG(); -+ else if (dev->param.inband_tags) { -+ struct yaffs_packed_tags2_tags_only *pt2tp; -+ pt2tp = -+ (struct yaffs_packed_tags2_tags_only *)(data + -+ dev-> -+ data_bytes_per_chunk); -+ yaffs_pack_tags2_tags_only(pt2tp, tags); -+ } else { -+ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); -+ } -+ -+ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ (dev->param.inband_tags) ? NULL : packed_tags_ptr, -+ (dev->param.inband_tags) ? 0 : packed_tags_size); -+ -+ return retval; -+} -+ -+static int yaffs_tags_marshall_read(struct yaffs_dev *dev, -+ int nand_chunk, u8 *data, -+ struct yaffs_ext_tags *tags) -+{ -+ int retval = 0; -+ int local_data = 0; -+ u8 spare_buffer[100]; -+ enum yaffs_ecc_result ecc_result; -+ -+ struct yaffs_packed_tags2 pt; -+ -+ int packed_tags_size = -+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); -+ void *packed_tags_ptr = -+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_tags_marshall_read chunk %d data %p tags %p", -+ nand_chunk, data, tags); -+ -+ if (dev->param.inband_tags) { -+ if (!data) { -+ local_data = 1; -+ data = yaffs_get_temp_buffer(dev); -+ } -+ } -+ -+ if (dev->param.inband_tags || (data && !tags)) -+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ NULL, 0, -+ &ecc_result); -+ else if (tags) -+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ spare_buffer, packed_tags_size, -+ &ecc_result); -+ else -+ BUG(); -+ -+ -+ if (dev->param.inband_tags) { -+ if (tags) { -+ struct yaffs_packed_tags2_tags_only *pt2tp; -+ pt2tp = -+ (struct yaffs_packed_tags2_tags_only *) -+ &data[dev->data_bytes_per_chunk]; -+ yaffs_unpack_tags2_tags_only(tags, pt2tp); -+ } -+ } else if (tags) { -+ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); -+ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); -+ } -+ -+ if (local_data) -+ yaffs_release_temp_buffer(dev, data); -+ -+ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { -+ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { -+ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) -+ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; -+ dev->n_ecc_fixed++; -+ } -+ -+ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) -+ return YAFFS_OK; -+ else -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", -+ block_no); -+ -+ retval = dev->drv.drv_check_bad_fn(dev, block_no); -+ -+ if (retval== YAFFS_FAIL) { -+ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); -+ -+ *state = YAFFS_BLOCK_STATE_DEAD; -+ *seq_number = 0; -+ } else { -+ struct yaffs_ext_tags t; -+ -+ yaffs_tags_marshall_read(dev, -+ block_no * dev->param.chunks_per_block, -+ NULL, &t); -+ -+ if (t.chunk_used) { -+ *seq_number = t.seq_number; -+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; -+ } else { -+ *seq_number = 0; -+ *state = YAFFS_BLOCK_STATE_EMPTY; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "block query returns seq %d state %d", -+ *seq_number, *state); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ else -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ return dev->drv.drv_mark_bad_fn(dev, block_no); -+ -+} -+ -+ -+void yaffs_tags_marshall_install(struct yaffs_dev *dev) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!dev->tagger.write_chunk_tags_fn) -+ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; -+ -+ if (!dev->tagger.read_chunk_tags_fn) -+ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; -+ -+ if (!dev->tagger.query_block_fn) -+ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; -+ -+ if (!dev->tagger.mark_bad_fn) -+ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; -+ -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.12.20/fs/yaffs2/yaffs_tagsmarshall.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,22 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_TAGSMARSHALL_H__ -+#define __YAFFS_TAGSMARSHALL_H__ -+ -+#include "yaffs_guts.h" -+void yaffs_tags_marshall_install(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_trace.h linux-3.12.20/fs/yaffs2/yaffs_trace.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_trace.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,57 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YTRACE_H__ -+#define __YTRACE_H__ -+ -+extern unsigned int yaffs_trace_mask; -+extern unsigned int yaffs_wr_attempts; -+ -+/* -+ * Tracing flags. -+ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. -+ */ -+ -+#define YAFFS_TRACE_OS 0x00000002 -+#define YAFFS_TRACE_ALLOCATE 0x00000004 -+#define YAFFS_TRACE_SCAN 0x00000008 -+#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 -+#define YAFFS_TRACE_ERASE 0x00000020 -+#define YAFFS_TRACE_GC 0x00000040 -+#define YAFFS_TRACE_WRITE 0x00000080 -+#define YAFFS_TRACE_TRACING 0x00000100 -+#define YAFFS_TRACE_DELETION 0x00000200 -+#define YAFFS_TRACE_BUFFERS 0x00000400 -+#define YAFFS_TRACE_NANDACCESS 0x00000800 -+#define YAFFS_TRACE_GC_DETAIL 0x00001000 -+#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 -+#define YAFFS_TRACE_MTD 0x00004000 -+#define YAFFS_TRACE_CHECKPOINT 0x00008000 -+ -+#define YAFFS_TRACE_VERIFY 0x00010000 -+#define YAFFS_TRACE_VERIFY_NAND 0x00020000 -+#define YAFFS_TRACE_VERIFY_FULL 0x00040000 -+#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 -+ -+#define YAFFS_TRACE_SYNC 0x00100000 -+#define YAFFS_TRACE_BACKGROUND 0x00200000 -+#define YAFFS_TRACE_LOCK 0x00400000 -+#define YAFFS_TRACE_MOUNT 0x00800000 -+ -+#define YAFFS_TRACE_ERROR 0x40000000 -+#define YAFFS_TRACE_BUG 0x80000000 -+#define YAFFS_TRACE_ALWAYS 0xf0000000 -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_verify.c linux-3.12.20/fs/yaffs2/yaffs_verify.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_verify.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,529 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_verify.h" -+#include "yaffs_trace.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_nand.h" -+ -+int yaffs_skip_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & -+ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); -+} -+ -+static int yaffs_skip_full_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); -+} -+ -+static int yaffs_skip_nand_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); -+} -+ -+static const char * const block_state_name[] = { -+ "Unknown", -+ "Needs scan", -+ "Scanning", -+ "Empty", -+ "Allocating", -+ "Full", -+ "Dirty", -+ "Checkpoint", -+ "Collecting", -+ "Dead" -+}; -+ -+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) -+{ -+ int actually_used; -+ int in_use; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Report illegal runtime states */ -+ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has undefined state %d", -+ n, bi->block_state); -+ -+ switch (bi->block_state) { -+ case YAFFS_BLOCK_STATE_UNKNOWN: -+ case YAFFS_BLOCK_STATE_SCANNING: -+ case YAFFS_BLOCK_STATE_NEEDS_SCAN: -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has bad run-state %s", -+ n, block_state_name[bi->block_state]); -+ } -+ -+ /* Check pages in use and soft deletions are legal */ -+ -+ actually_used = bi->pages_in_use - bi->soft_del_pages; -+ -+ if (bi->pages_in_use < 0 || -+ bi->pages_in_use > dev->param.chunks_per_block || -+ bi->soft_del_pages < 0 || -+ bi->soft_del_pages > dev->param.chunks_per_block || -+ actually_used < 0 || actually_used > dev->param.chunks_per_block) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has illegal values pages_in_used %d soft_del_pages %d", -+ n, bi->pages_in_use, bi->soft_del_pages); -+ -+ /* Check chunk bitmap legal */ -+ in_use = yaffs_count_chunk_bits(dev, n); -+ if (in_use != bi->pages_in_use) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", -+ n, bi->pages_in_use, in_use); -+} -+ -+void yaffs_verify_collected_blk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, int n) -+{ -+ yaffs_verify_blk(dev, bi, n); -+ -+ /* After collection the block should be in the erased state */ -+ -+ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && -+ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Block %d is in state %d after gc, should be erased", -+ n, bi->block_state); -+ } -+} -+ -+void yaffs_verify_blocks(struct yaffs_dev *dev) -+{ -+ int i; -+ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; -+ int illegal_states = 0; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ memset(state_count, 0, sizeof(state_count)); -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); -+ yaffs_verify_blk(dev, bi, i); -+ -+ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) -+ state_count[bi->block_state]++; -+ else -+ illegal_states++; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); -+ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "%d blocks have illegal states", -+ illegal_states); -+ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Too many allocating blocks"); -+ -+ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "%s %d blocks", -+ block_state_name[i], state_count[i]); -+ -+ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Checkpoint block count wrong dev %d count %d", -+ dev->blocks_in_checkpt, -+ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); -+ -+ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Erased block count wrong dev %d count %d", -+ dev->n_erased_blocks, -+ state_count[YAFFS_BLOCK_STATE_EMPTY]); -+ -+ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Too many collecting blocks %d (max is 1)", -+ state_count[YAFFS_BLOCK_STATE_COLLECTING]); -+} -+ -+/* -+ * Verify the object header. oh must be valid, but obj and tags may be NULL in -+ * which case those tests will not be performed. -+ */ -+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, -+ struct yaffs_ext_tags *tags, int parent_check) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ if (!(tags && obj && oh)) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Verifying object header tags %p obj %p oh %p", -+ tags, obj, oh); -+ return; -+ } -+ -+ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || -+ oh->type > YAFFS_OBJECT_TYPE_MAX) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header type is illegal value 0x%x", -+ tags->obj_id, oh->type); -+ -+ if (tags->obj_id != obj->obj_id) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch obj_id %d", -+ tags->obj_id, obj->obj_id); -+ -+ /* -+ * Check that the object's parent ids match if parent_check requested. -+ * -+ * Tests do not apply to the root object. -+ */ -+ -+ if (parent_check && tags->obj_id > 1 && !obj->parent) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch parent_id %d obj->parent is NULL", -+ tags->obj_id, oh->parent_obj_id); -+ -+ if (parent_check && obj->parent && -+ oh->parent_obj_id != obj->parent->obj_id && -+ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || -+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch parent_id %d parent_obj_id %d", -+ tags->obj_id, oh->parent_obj_id, -+ obj->parent->obj_id); -+ -+ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header name is NULL", -+ obj->obj_id); -+ -+ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header name is 0xff", -+ obj->obj_id); -+} -+ -+void yaffs_verify_file(struct yaffs_obj *obj) -+{ -+ u32 x; -+ int required_depth; -+ int actual_depth; -+ int last_chunk; -+ u32 offset_in_chunk; -+ u32 the_chunk; -+ -+ u32 i; -+ struct yaffs_dev *dev; -+ struct yaffs_ext_tags tags; -+ struct yaffs_tnode *tn; -+ u32 obj_id; -+ -+ if (!obj) -+ return; -+ -+ if (yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ dev = obj->my_dev; -+ obj_id = obj->obj_id; -+ -+ -+ /* Check file size is consistent with tnode depth */ -+ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, -+ &last_chunk, &offset_in_chunk); -+ last_chunk++; -+ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (x > 0) { -+ x >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ actual_depth = obj->variant.file_variant.top_level; -+ -+ /* Check that the chunks in the tnode tree are all correct. -+ * We do this by scanning through the tnode tree and -+ * checking the tags for every chunk match. -+ */ -+ -+ if (yaffs_skip_nand_verification(dev)) -+ return; -+ -+ for (i = 1; i <= last_chunk; i++) { -+ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); -+ -+ if (!tn) -+ continue; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, i); -+ if (the_chunk > 0) { -+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, -+ &tags); -+ if (tags.obj_id != obj_id || tags.chunk_id != i) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", -+ obj_id, i, the_chunk, -+ tags.obj_id, tags.chunk_id); -+ } -+ } -+} -+ -+void yaffs_verify_link(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ /* Verify sane equivalent object */ -+} -+ -+void yaffs_verify_symlink(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ /* Verify symlink string */ -+} -+ -+void yaffs_verify_special(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+} -+ -+void yaffs_verify_obj(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ u32 chunk_min; -+ u32 chunk_max; -+ u32 chunk_id_ok; -+ u32 chunk_in_range; -+ u32 chunk_wrongly_deleted; -+ u32 chunk_valid; -+ -+ if (!obj) -+ return; -+ -+ if (obj->being_created) -+ return; -+ -+ dev = obj->my_dev; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Check sane object header chunk */ -+ -+ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; -+ chunk_max = -+ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; -+ -+ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && -+ ((unsigned)(obj->hdr_chunk)) <= chunk_max); -+ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); -+ chunk_valid = chunk_in_range && -+ yaffs_check_chunk_bit(dev, -+ obj->hdr_chunk / dev->param.chunks_per_block, -+ obj->hdr_chunk % dev->param.chunks_per_block); -+ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; -+ -+ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has chunk_id %d %s %s", -+ obj->obj_id, obj->hdr_chunk, -+ chunk_id_ok ? "" : ",out of range", -+ chunk_wrongly_deleted ? ",marked as deleted" : ""); -+ -+ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { -+ struct yaffs_ext_tags tags; -+ struct yaffs_obj_hdr *oh; -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ oh = (struct yaffs_obj_hdr *)buffer; -+ -+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); -+ -+ yaffs_verify_oh(obj, oh, &tags, 1); -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ -+ /* Verify it has a parent */ -+ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has parent pointer %p which does not look like an object", -+ obj->obj_id, obj->parent); -+ } -+ -+ /* Verify parent is a directory */ -+ if (obj->parent && -+ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d's parent is not a directory (type %d)", -+ obj->obj_id, obj->parent->variant_type); -+ } -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ yaffs_verify_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ yaffs_verify_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ yaffs_verify_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ yaffs_verify_link(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ yaffs_verify_special(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ default: -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has illegaltype %d", -+ obj->obj_id, obj->variant_type); -+ break; -+ } -+} -+ -+void yaffs_verify_objects(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ int i; -+ struct list_head *lh; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each(lh, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ yaffs_verify_obj(obj); -+ } -+ } -+} -+ -+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) -+{ -+ struct list_head *lh; -+ struct yaffs_obj *list_obj; -+ int count = 0; -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); -+ BUG(); -+ return; -+ } -+ -+ if (yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ if (!obj->parent) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); -+ BUG(); -+ return; -+ } -+ -+ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); -+ BUG(); -+ } -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ list_for_each(lh, &obj->parent->variant.dir_variant.children) { -+ list_obj = list_entry(lh, struct yaffs_obj, siblings); -+ yaffs_verify_obj(list_obj); -+ if (obj == list_obj) -+ count++; -+ } -+ -+ if (count != 1) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Object in directory %d times", -+ count); -+ BUG(); -+ } -+} -+ -+void yaffs_verify_dir(struct yaffs_obj *directory) -+{ -+ struct list_head *lh; -+ struct yaffs_obj *list_obj; -+ -+ if (!directory) { -+ BUG(); -+ return; -+ } -+ -+ if (yaffs_skip_full_verification(directory->my_dev)) -+ return; -+ -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Directory has wrong type: %d", -+ directory->variant_type); -+ BUG(); -+ } -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ list_for_each(lh, &directory->variant.dir_variant.children) { -+ list_obj = list_entry(lh, struct yaffs_obj, siblings); -+ if (list_obj->parent != directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Object in directory list has wrong parent %p", -+ list_obj->parent); -+ BUG(); -+ } -+ yaffs_verify_obj_in_dir(list_obj); -+ } -+} -+ -+static int yaffs_free_verification_failures; -+ -+void yaffs_verify_free_chunks(struct yaffs_dev *dev) -+{ -+ int counted; -+ int difference; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ counted = yaffs_count_free_chunks(dev); -+ -+ difference = dev->n_free_chunks - counted; -+ -+ if (difference) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Freechunks verification failure %d %d %d", -+ dev->n_free_chunks, counted, difference); -+ yaffs_free_verification_failures++; -+ } -+} -+ -+int yaffs_verify_file_sane(struct yaffs_obj *in) -+{ -+ (void) in; -+ return YAFFS_OK; -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_verify.h linux-3.12.20/fs/yaffs2/yaffs_verify.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_verify.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,43 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_VERIFY_H__ -+#define __YAFFS_VERIFY_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, -+ int n); -+void yaffs_verify_collected_blk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, int n); -+void yaffs_verify_blocks(struct yaffs_dev *dev); -+ -+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, -+ struct yaffs_ext_tags *tags, int parent_check); -+void yaffs_verify_file(struct yaffs_obj *obj); -+void yaffs_verify_link(struct yaffs_obj *obj); -+void yaffs_verify_symlink(struct yaffs_obj *obj); -+void yaffs_verify_special(struct yaffs_obj *obj); -+void yaffs_verify_obj(struct yaffs_obj *obj); -+void yaffs_verify_objects(struct yaffs_dev *dev); -+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); -+void yaffs_verify_dir(struct yaffs_obj *directory); -+void yaffs_verify_free_chunks(struct yaffs_dev *dev); -+ -+int yaffs_verify_file_sane(struct yaffs_obj *obj); -+ -+int yaffs_skip_verification(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_vfs.c linux-3.12.20/fs/yaffs2/yaffs_vfs.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_vfs.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,3600 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * Acknowledgements: -+ * Luc van OostenRyck for numerous patches. -+ * Nick Bane for numerous patches. -+ * Nick Bane for 2.5/2.6 integration. -+ * Andras Toth for mknod rdev issue. -+ * Michael Fischer for finding the problem with inode inconsistency. -+ * Some code bodily lifted from JFFS -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * -+ * This is the file system front-end to YAFFS that hooks it up to -+ * the VFS. -+ * -+ * Special notes: -+ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with -+ * this superblock -+ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this -+ * superblock -+ * >> inode->u.generic_ip points to the associated struct yaffs_obj. -+ */ -+ -+/* -+ * There are two variants of the VFS glue code. This variant should compile -+ * for any version of Linux. -+ */ -+#include -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) -+#define YAFFS_COMPILE_BACKGROUND -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) -+#define YAFFS_COMPILE_FREEZER -+#endif -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) -+#define YAFFS_COMPILE_EXPORTFS -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) -+#define YAFFS_USE_SETATTR_COPY -+#define YAFFS_USE_TRUNCATE_SETSIZE -+#endif -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) -+#define YAFFS_HAS_EVICT_INODE -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) -+#define YAFFS_NEW_FOLLOW_LINK 1 -+#else -+#define YAFFS_NEW_FOLLOW_LINK 0 -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) -+#define YAFFS_HAS_WRITE_SUPER -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+#include -+#endif -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+#include -+#endif -+ -+#ifdef YAFFS_COMPILE_BACKGROUND -+#include -+#include -+#endif -+#ifdef YAFFS_COMPILE_FREEZER -+#include -+#endif -+ -+#include -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ -+#include -+ -+#define UnlockPage(p) unlock_page(p) -+#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) -+ -+/* FIXME: use sb->s_id instead ? */ -+#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) -+ -+#else -+ -+#include -+#define BDEVNAME_SIZE 0 -+#define yaffs_devname(sb, buf) kdevname(sb->s_dev) -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) -+/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ -+#define __user -+#endif -+ -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) -+#define YPROC_ROOT (&proc_root) -+#else -+#define YPROC_ROOT NULL -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) -+#define Y_INIT_TIMER(a) init_timer(a) -+#else -+#define Y_INIT_TIMER(a) init_timer_on_stack(a) -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) -+#define YAFFS_USE_WRITE_BEGIN_END 1 -+#else -+#define YAFFS_USE_WRITE_BEGIN_END 0 -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) -+#define YAFFS_SUPER_HAS_DIRTY -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) -+#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) -+static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) -+{ -+ uint64_t result = partition_size; -+ do_div(result, block_size); -+ return (uint32_t) result; -+} -+#else -+#define YCALCBLOCKS(s, b) ((s)/(b)) -+#endif -+ -+#include -+#include -+ -+#include "yportenv.h" -+#include "yaffs_trace.h" -+#include "yaffs_guts.h" -+#include "yaffs_attribs.h" -+ -+#include "yaffs_linux.h" -+ -+#include "yaffs_mtdif.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_getblockinfo.h" -+ -+unsigned int yaffs_trace_mask = -+ YAFFS_TRACE_BAD_BLOCKS | -+ YAFFS_TRACE_ALWAYS | -+ 0; -+ -+unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; -+unsigned int yaffs_auto_checkpoint = 1; -+unsigned int yaffs_gc_control = 1; -+unsigned int yaffs_bg_enable = 1; -+unsigned int yaffs_auto_select = 1; -+/* Module Parameters */ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+module_param(yaffs_trace_mask, uint, 0644); -+module_param(yaffs_wr_attempts, uint, 0644); -+module_param(yaffs_auto_checkpoint, uint, 0644); -+module_param(yaffs_gc_control, uint, 0644); -+module_param(yaffs_bg_enable, uint, 0644); -+#else -+MODULE_PARM(yaffs_trace_mask, "i"); -+MODULE_PARM(yaffs_wr_attempts, "i"); -+MODULE_PARM(yaffs_auto_checkpoint, "i"); -+MODULE_PARM(yaffs_gc_control, "i"); -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) -+/* use iget and read_inode */ -+#define Y_IGET(sb, inum) iget((sb), (inum)) -+ -+#else -+/* Call local equivalent */ -+#define YAFFS_USE_OWN_IGET -+#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) -+ -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) -+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) -+#else -+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) -+#endif -+ -+#define yaffs_inode_to_obj(iptr) \ -+ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) -+#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) -+#else -+#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+#define Y_CLEAR_INODE(i) clear_inode(i) -+#else -+#define Y_CLEAR_INODE(i) end_writeback(i) -+#endif -+ -+ -+#define update_dir_time(dir) do {\ -+ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ -+ } while (0) -+ -+static void yaffs_fill_inode_from_obj(struct inode *inode, -+ struct yaffs_obj *obj); -+ -+ -+static void yaffs_gross_lock(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); -+ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); -+} -+ -+static void yaffs_gross_unlock(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); -+ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); -+} -+ -+ -+static int yaffs_readpage_nolock(struct file *f, struct page *pg) -+{ -+ /* Lifted from jffs2 */ -+ -+ struct yaffs_obj *obj; -+ unsigned char *pg_buf; -+ int ret; -+ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readpage_nolock at %lld, size %08x", -+ (long long)pos, -+ (unsigned)PAGE_CACHE_SIZE); -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ BUG_ON(!PageLocked(pg)); -+#else -+ if (!PageLocked(pg)) -+ PAGE_BUG(pg); -+#endif -+ -+ pg_buf = kmap(pg); -+ /* FIXME: Can kmap fail? */ -+ -+ yaffs_gross_lock(dev); -+ -+ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (ret >= 0) -+ ret = 0; -+ -+ if (ret) { -+ ClearPageUptodate(pg); -+ SetPageError(pg); -+ } else { -+ SetPageUptodate(pg); -+ ClearPageError(pg); -+ } -+ -+ flush_dcache_page(pg); -+ kunmap(pg); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); -+ return ret; -+} -+ -+static int yaffs_readpage_unlock(struct file *f, struct page *pg) -+{ -+ int ret = yaffs_readpage_nolock(f, pg); -+ UnlockPage(pg); -+ return ret; -+} -+ -+static int yaffs_readpage(struct file *f, struct page *pg) -+{ -+ int ret; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); -+ ret = yaffs_readpage_unlock(f, pg); -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); -+ return ret; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+#define YCRED_FSUID() from_kuid(&init_user_ns, current_fsuid()) -+#define YCRED_FSGID() from_kgid(&init_user_ns, current_fsgid()) -+#else -+#define YCRED_FSUID() YCRED(current)->fsuid -+#define YCRED_FSGID() YCRED(current)->fsgid -+ -+static inline uid_t i_uid_read(const struct inode *inode) -+{ -+ return inode->i_uid; -+} -+ -+static inline gid_t i_gid_read(const struct inode *inode) -+{ -+ return inode->i_gid; -+} -+ -+static inline void i_uid_write(struct inode *inode, uid_t uid) -+{ -+ inode->i_uid = uid; -+} -+ -+static inline void i_gid_write(struct inode *inode, gid_t gid) -+{ -+ inode->i_gid = gid; -+} -+#endif -+ -+static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) -+{ -+ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); -+ -+ if (lc) -+ lc->dirty = val; -+ -+# ifdef YAFFS_SUPER_HAS_DIRTY -+ { -+ struct super_block *sb = lc->super; -+ -+ if (sb) -+ sb->s_dirt = val; -+ } -+#endif -+ -+} -+ -+static void yaffs_set_super_dirty(struct yaffs_dev *dev) -+{ -+ yaffs_set_super_dirty_val(dev, 1); -+} -+ -+static void yaffs_clear_super_dirty(struct yaffs_dev *dev) -+{ -+ yaffs_set_super_dirty_val(dev, 0); -+} -+ -+static int yaffs_check_super_dirty(struct yaffs_dev *dev) -+{ -+ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); -+ -+ if (lc && lc->dirty) -+ return 1; -+ -+# ifdef YAFFS_SUPER_HAS_DIRTY -+ { -+ struct super_block *sb = lc->super; -+ -+ if (sb && sb->s_dirt) -+ return 1; -+ } -+#endif -+ return 0; -+ -+} -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_writepage(struct page *page, struct writeback_control *wbc) -+#else -+static int yaffs_writepage(struct page *page) -+#endif -+{ -+ struct yaffs_dev *dev; -+ struct address_space *mapping = page->mapping; -+ struct inode *inode; -+ unsigned long end_index; -+ char *buffer; -+ struct yaffs_obj *obj; -+ int n_written = 0; -+ unsigned n_bytes; -+ loff_t i_size; -+ -+ if (!mapping) -+ BUG(); -+ inode = mapping->host; -+ if (!inode) -+ BUG(); -+ i_size = i_size_read(inode); -+ -+ end_index = i_size >> PAGE_CACHE_SHIFT; -+ -+ if (page->index < end_index) -+ n_bytes = PAGE_CACHE_SIZE; -+ else { -+ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); -+ -+ if (page->index > end_index || !n_bytes) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_writepage at %lld, inode size = %lld!!", -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, -+ inode->i_size); -+ yaffs_trace(YAFFS_TRACE_OS, -+ " -> don't care!!"); -+ -+ zero_user_segment(page, 0, PAGE_CACHE_SIZE); -+ set_page_writeback(page); -+ unlock_page(page); -+ end_page_writeback(page); -+ return 0; -+ } -+ } -+ -+ if (n_bytes != PAGE_CACHE_SIZE) -+ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); -+ -+ get_page(page); -+ -+ buffer = kmap(page); -+ -+ obj = yaffs_inode_to_obj(inode); -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_writepage at %lld, size %08x", -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "writepag0: obj = %lld, ino = %lld", -+ obj->variant.file_variant.file_size, inode->i_size); -+ -+ n_written = yaffs_wr_file(obj, buffer, -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); -+ -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "writepag1: obj = %lld, ino = %lld", -+ obj->variant.file_variant.file_size, inode->i_size); -+ -+ yaffs_gross_unlock(dev); -+ -+ kunmap(page); -+ set_page_writeback(page); -+ unlock_page(page); -+ end_page_writeback(page); -+ put_page(page); -+ -+ return (n_written == n_bytes) ? 0 : -ENOSPC; -+} -+ -+/* Space holding and freeing is done to ensure we have space available for write_begin/end */ -+/* For now we just assume few parallel writes and check against a small number. */ -+/* Todo: need to do this with a counter to handle parallel reads better */ -+ -+static ssize_t yaffs_hold_space(struct file *f) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ int n_free_chunks; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ n_free_chunks = yaffs_get_n_free_chunks(dev); -+ -+ yaffs_gross_unlock(dev); -+ -+ return (n_free_chunks > 20) ? 1 : 0; -+} -+ -+static void yaffs_release_space(struct file *f) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_gross_unlock(dev); -+} -+ -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+static int yaffs_write_begin(struct file *filp, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned flags, -+ struct page **pagep, void **fsdata) -+{ -+ struct page *pg = NULL; -+ pgoff_t index = pos >> PAGE_CACHE_SHIFT; -+ -+ int ret = 0; -+ int space_held = 0; -+ -+ /* Get a page */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) -+ pg = grab_cache_page_write_begin(mapping, index, flags); -+#else -+ pg = __grab_cache_page(mapping, index); -+#endif -+ -+ *pagep = pg; -+ if (!pg) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "start yaffs_write_begin index %d(%x) uptodate %d", -+ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); -+ -+ /* Get fs space */ -+ space_held = yaffs_hold_space(filp); -+ -+ if (!space_held) { -+ ret = -ENOSPC; -+ goto out; -+ } -+ -+ /* Update page if required */ -+ -+ if (!Page_Uptodate(pg)) -+ ret = yaffs_readpage_nolock(filp, pg); -+ -+ if (ret) -+ goto out; -+ -+ /* Happy path return */ -+ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); -+ -+ return 0; -+ -+out: -+ yaffs_trace(YAFFS_TRACE_OS, -+ "end yaffs_write_begin fail returning %d", ret); -+ if (space_held) -+ yaffs_release_space(filp); -+ if (pg) { -+ unlock_page(pg); -+ page_cache_release(pg); -+ } -+ return ret; -+} -+ -+#else -+ -+static int yaffs_prepare_write(struct file *f, struct page *pg, -+ unsigned offset, unsigned to) -+{ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); -+ -+ if (!Page_Uptodate(pg)) -+ return yaffs_readpage_nolock(f, pg); -+ return 0; -+} -+#endif -+ -+ -+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, -+ loff_t * pos) -+{ -+ struct yaffs_obj *obj; -+ int n_written; -+ loff_t ipos; -+ struct inode *inode; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write: hey obj is null!"); -+ return -EINVAL; -+ } -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ inode = f->f_dentry->d_inode; -+ -+ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) -+ ipos = inode->i_size; -+ else -+ ipos = *pos; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", -+ (unsigned)n, (unsigned)n, obj->obj_id, ipos); -+ -+ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); -+ -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write: %d(%x) bytes written", -+ (unsigned)n, (unsigned)n); -+ -+ if (n_written > 0) { -+ ipos += n_written; -+ *pos = ipos; -+ if (ipos > inode->i_size) { -+ inode->i_size = ipos; -+ inode->i_blocks = (ipos + 511) >> 9; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write size updated to %lld bytes, %d blocks", -+ ipos, (int)(inode->i_blocks)); -+ } -+ -+ } -+ yaffs_gross_unlock(dev); -+ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; -+} -+ -+ -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+static int yaffs_write_end(struct file *filp, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned copied, -+ struct page *pg, void *fsdadata) -+{ -+ int ret = 0; -+ void *addr, *kva; -+ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); -+ -+ kva = kmap(pg); -+ addr = kva + offset_into_page; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_write_end addr %p pos %lld n_bytes %d", -+ addr, pos, copied); -+ -+ ret = yaffs_file_write(filp, addr, copied, &pos); -+ -+ if (ret != copied) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_write_end not same size ret %d copied %d", -+ ret, copied); -+ SetPageError(pg); -+ } -+ -+ kunmap(pg); -+ -+ yaffs_release_space(filp); -+ unlock_page(pg); -+ page_cache_release(pg); -+ return ret; -+} -+#else -+ -+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, -+ unsigned to) -+{ -+ void *addr, *kva; -+ -+ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; -+ int n_bytes = to - offset; -+ int n_written; -+ -+ kva = kmap(pg); -+ addr = kva + offset; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write addr %p pos %lld n_bytes %d", -+ addr, pos, n_bytes); -+ -+ n_written = yaffs_file_write(f, addr, n_bytes, &pos); -+ -+ if (n_written != n_bytes) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write not same size n_written %d n_bytes %d", -+ n_written, n_bytes); -+ SetPageError(pg); -+ } -+ kunmap(pg); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write returning %d", -+ n_written == n_bytes ? 0 : n_written); -+ -+ return n_written == n_bytes ? 0 : n_written; -+} -+#endif -+ -+static struct address_space_operations yaffs_file_address_operations = { -+ .readpage = yaffs_readpage, -+ .writepage = yaffs_writepage, -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+ .write_begin = yaffs_write_begin, -+ .write_end = yaffs_write_end, -+#else -+ .prepare_write = yaffs_prepare_write, -+ .commit_write = yaffs_commit_write, -+#endif -+}; -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_file_flush(struct file *file, fl_owner_t id) -+#else -+static int yaffs_file_flush(struct file *file) -+#endif -+{ -+ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); -+ -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_flush object %d (%s)", -+ obj->obj_id, -+ obj->dirty ? "dirty" : "clean"); -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_flush_file(obj, 1, 0); -+ -+ yaffs_gross_unlock(dev); -+ -+ return 0; -+} -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) -+static int yaffs_sync_object(struct file *file, int datasync) -+#else -+static int yaffs_sync_object(struct file *file, struct dentry *dentry, -+ int datasync) -+#endif -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) -+ struct dentry *dentry = file->f_path.dentry; -+#endif -+ -+ obj = yaffs_dentry_to_obj(dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, -+ "yaffs_sync_object"); -+ yaffs_gross_lock(dev); -+ yaffs_flush_file(obj, 1, datasync); -+ yaffs_gross_unlock(dev); -+ return 0; -+} -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) -+static const struct file_operations yaffs_file_operations = { -+ .read = do_sync_read, -+ .write = do_sync_write, -+ .aio_read = generic_file_aio_read, -+ .aio_write = generic_file_aio_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+ .splice_read = generic_file_splice_read, -+ .splice_write = generic_file_splice_write, -+ .llseek = generic_file_llseek, -+}; -+ -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) -+ -+static const struct file_operations yaffs_file_operations = { -+ .read = do_sync_read, -+ .write = do_sync_write, -+ .aio_read = generic_file_aio_read, -+ .aio_write = generic_file_aio_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+ .sendfile = generic_file_sendfile, -+}; -+ -+#else -+ -+static const struct file_operations yaffs_file_operations = { -+ .read = generic_file_read, -+ .write = generic_file_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ .sendfile = generic_file_sendfile, -+#endif -+}; -+#endif -+ -+ -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) -+static void zero_user_segment(struct page *page, unsigned start, unsigned end) -+{ -+ void *kaddr = kmap_atomic(page, KM_USER0); -+ memset(kaddr + start, 0, end - start); -+ kunmap_atomic(kaddr, KM_USER0); -+ flush_dcache_page(page); -+} -+#endif -+ -+ -+static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) -+{ -+#ifdef YAFFS_USE_TRUNCATE_SETSIZE -+ truncate_setsize(inode, newsize); -+ return 0; -+#else -+ truncate_inode_pages(&inode->i_data, newsize); -+ return 0; -+#endif -+ -+} -+ -+ -+static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) -+{ -+#ifdef YAFFS_USE_SETATTR_COPY -+ setattr_copy(inode, attr); -+ return 0; -+#else -+ return inode_setattr(inode, attr); -+#endif -+ -+} -+ -+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_setattr of object %d", -+ yaffs_inode_to_obj(inode)->obj_id); -+#if 0 -+ /* Fail if a requested resize >= 2GB */ -+ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) -+ error = -EINVAL; -+#endif -+ -+ if (error == 0) -+ error = inode_change_ok(inode, attr); -+ if (error == 0) { -+ int result; -+ if (!error) { -+ error = yaffs_vfs_setattr(inode, attr); -+ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); -+ if (attr->ia_valid & ATTR_SIZE) { -+ yaffs_vfs_setsize(inode, attr->ia_size); -+ inode->i_blocks = (inode->i_size + 511) >> 9; -+ } -+ } -+ dev = yaffs_inode_to_obj(inode)->my_dev; -+ if (attr->ia_valid & ATTR_SIZE) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "resize to %d(%x)", -+ (int)(attr->ia_size), -+ (int)(attr->ia_size)); -+ } -+ yaffs_gross_lock(dev); -+ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); -+ if (result == YAFFS_OK) { -+ error = 0; -+ } else { -+ error = -EPERM; -+ } -+ yaffs_gross_unlock(dev); -+ -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); -+ -+ return error; -+} -+ -+static int yaffs_setxattr(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ int result; -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ result = yaffs_set_xattrib(obj, name, value, size, flags); -+ if (result == YAFFS_OK) -+ error = 0; -+ else if (result < 0) -+ error = result; -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); -+ -+ return error; -+} -+ -+static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, -+ void *buff, size_t size) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_getxattr \"%s\" from object %d", -+ name, obj->obj_id); -+ -+ if (error == 0) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ error = yaffs_get_xattrib(obj, name, buff, size); -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); -+ -+ return error; -+} -+ -+static int yaffs_removexattr(struct dentry *dentry, const char *name) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_removexattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ int result; -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ result = yaffs_remove_xattrib(obj, name); -+ if (result == YAFFS_OK) -+ error = 0; -+ else if (result < 0) -+ error = result; -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_removexattr done returning %d", error); -+ -+ return error; -+} -+ -+static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_listxattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ error = yaffs_list_xattrib(obj, buff, size); -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_listxattr done returning %d", error); -+ -+ return error; -+} -+ -+ -+static const struct inode_operations yaffs_file_inode_operations = { -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+ -+static int yaffs_readlink(struct dentry *dentry, char __user * buffer, -+ int buflen) -+{ -+ unsigned char *alias; -+ int ret; -+ -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (!alias) -+ return -ENOMEM; -+ -+ ret = vfs_readlink(dentry, buffer, buflen, alias); -+ kfree(alias); -+ return ret; -+} -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ void *ret; -+#else -+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ int ret -+#endif -+ unsigned char *alias; -+ int ret_int = 0; -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); -+ yaffs_gross_unlock(dev); -+ -+ if (!alias) { -+ ret_int = -ENOMEM; -+ goto out; -+ } -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+ nd_set_link(nd, alias); -+ ret = alias; -+out: -+ if (ret_int) -+ ret = ERR_PTR(ret_int); -+ return ret; -+#else -+ ret = vfs_follow_link(nd, alias); -+ kfree(alias); -+out: -+ if (ret_int) -+ ret = ret_int; -+ return ret; -+#endif -+} -+ -+ -+#ifdef YAFFS_HAS_PUT_INODE -+ -+/* For now put inode is just for debugging -+ * Put inode is called when the inode **structure** is put. -+ */ -+static void yaffs_put_inode(struct inode *inode) -+{ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_put_inode: ino %d, count %d"), -+ (int)inode->i_ino, atomic_read(&inode->i_count); -+ -+} -+#endif -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) -+{ -+ kfree(alias); -+} -+#endif -+ -+static const struct inode_operations yaffs_symlink_inode_operations = { -+ .readlink = yaffs_readlink, -+ .follow_link = yaffs_follow_link, -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+ .put_link = yaffs_put_link, -+#endif -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+#ifdef YAFFS_USE_OWN_IGET -+ -+static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) -+{ -+ struct inode *inode; -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); -+ -+ inode = iget_locked(sb, ino); -+ if (!inode) -+ return ERR_PTR(-ENOMEM); -+ if (!(inode->i_state & I_NEW)) -+ return inode; -+ -+ /* NB This is called as a side effect of other functions, but -+ * we had to release the lock to prevent deadlocks, so -+ * need to lock again. -+ */ -+ -+ yaffs_gross_lock(dev); -+ -+ obj = yaffs_find_by_number(dev, inode->i_ino); -+ -+ yaffs_fill_inode_from_obj(inode, obj); -+ -+ yaffs_gross_unlock(dev); -+ -+ unlock_new_inode(inode); -+ return inode; -+} -+ -+#else -+ -+static void yaffs_read_inode(struct inode *inode) -+{ -+ /* NB This is called as a side effect of other functions, but -+ * we had to release the lock to prevent deadlocks, so -+ * need to lock again. -+ */ -+ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_inode for %d", (int)inode->i_ino); -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_lock(dev); -+ -+ obj = yaffs_find_by_number(dev, inode->i_ino); -+ -+ yaffs_fill_inode_from_obj(inode, obj); -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_unlock(dev); -+} -+ -+#endif -+ -+ -+ -+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, -+ struct yaffs_obj *obj) -+{ -+ struct inode *inode; -+ -+ if (!sb) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for NULL super_block!!"); -+ return NULL; -+ -+ } -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for NULL object!!"); -+ return NULL; -+ -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for object %d", obj->obj_id); -+ -+ inode = Y_IGET(sb, obj->obj_id); -+ if (IS_ERR(inode)) -+ return NULL; -+ -+ /* NB Side effect: iget calls back to yaffs_read_inode(). */ -+ /* iget also increments the inode's i_count */ -+ /* NB You can't be holding gross_lock or deadlock will happen! */ -+ -+ return inode; -+} -+ -+ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -+#define YCRED(x) x -+#else -+#define YCRED(x) (x->cred) -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t rdev) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, -+ dev_t rdev) -+#else -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, -+ int rdev) -+#endif -+{ -+ struct inode *inode; -+ -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_dev *dev; -+ -+ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); -+ -+ int error = -ENOSPC; -+ uid_t uid = YCRED_FSUID(); -+ gid_t gid = -+ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); -+ -+ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) -+ mode |= S_ISGID; -+ -+ if (parent) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: parent object %d type %d", -+ parent->obj_id, parent->variant_type); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: could not get parent object"); -+ return -EPERM; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: making oject for %s, mode %x dev %x", -+ dentry->d_name.name, mode, rdev); -+ -+ dev = parent->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ switch (mode & S_IFMT) { -+ default: -+ /* Special (socket, fifo, device...) */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ obj = -+ yaffs_create_special(parent, dentry->d_name.name, mode, uid, -+ gid, old_encode_dev(rdev)); -+#else -+ obj = -+ yaffs_create_special(parent, dentry->d_name.name, mode, uid, -+ gid, rdev); -+#endif -+ break; -+ case S_IFREG: /* file */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); -+ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, -+ gid); -+ break; -+ case S_IFDIR: /* directory */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); -+ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, -+ uid, gid); -+ break; -+ case S_IFLNK: /* symlink */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); -+ obj = NULL; /* Do we ever get here? */ -+ break; -+ } -+ -+ /* Can not call yaffs_get_inode() with gross lock held */ -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); -+ d_instantiate(dentry, inode); -+ update_dir_time(dir); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod created object %d count = %d", -+ obj->obj_id, atomic_read(&inode->i_count)); -+ error = 0; -+ yaffs_fill_inode_from_obj(dir, parent); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); -+ error = -ENOMEM; -+ } -+ -+ return error; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -+#else -+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) -+#endif -+{ -+ int ret_val; -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); -+ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); -+ return ret_val; -+} -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool dummy) -+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ struct nameidata *n) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, -+ struct nameidata *n) -+#else -+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) -+#endif -+{ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); -+ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, -+ unsigned int dummy) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, -+ struct nameidata *n) -+#else -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) -+#endif -+{ -+ struct yaffs_obj *obj; -+ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ -+ -+ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_lock(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", -+ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); -+ -+ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); -+ -+ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ -+ -+ /* Can't hold gross lock when calling yaffs_get_inode() */ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_lookup found %d", obj->obj_id); -+ -+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); -+ -+ } -+ -+/* added NCB for 2.5/6 compatability - forces add even if inode is -+ * NULL which creates dentry hash */ -+ d_add(dentry, inode); -+ -+ return NULL; -+} -+ -+/* -+ * Create a link... -+ */ -+static int yaffs_link(struct dentry *old_dentry, struct inode *dir, -+ struct dentry *dentry) -+{ -+ struct inode *inode = old_dentry->d_inode; -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_obj *link = NULL; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); -+ -+ obj = yaffs_inode_to_obj(inode); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ -+ link = -+ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, -+ obj); -+ -+ if (link) { -+ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); -+ d_instantiate(dentry, old_dentry->d_inode); -+ atomic_inc(&old_dentry->d_inode->i_count); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_link link count %d i_count %d", -+ old_dentry->d_inode->i_nlink, -+ atomic_read(&old_dentry->d_inode->i_count)); -+ } -+ -+ yaffs_gross_unlock(dev); -+ -+ if (link) { -+ update_dir_time(dir); -+ return 0; -+ } -+ -+ return -EPERM; -+} -+ -+static int yaffs_symlink(struct inode *dir, struct dentry *dentry, -+ const char *symname) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ uid_t uid = YCRED_FSUID(); -+ gid_t gid = -+ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); -+ -+ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > -+ YAFFS_MAX_NAME_LENGTH) -+ return -ENAMETOOLONG; -+ -+ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > -+ YAFFS_MAX_ALIAS_LENGTH) -+ return -ENAMETOOLONG; -+ -+ dev = yaffs_inode_to_obj(dir)->my_dev; -+ yaffs_gross_lock(dev); -+ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, -+ S_IFLNK | S_IRWXUGO, uid, gid, symname); -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ struct inode *inode; -+ -+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); -+ d_instantiate(dentry, inode); -+ update_dir_time(dir); -+ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); -+ return 0; -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); -+ } -+ -+ return -ENOMEM; -+} -+ -+/* -+ * The VFS layer already does all the dentry stuff for rename. -+ * -+ * NB: POSIX says you can rename an object over an old object of the same name -+ */ -+static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, -+ struct inode *new_dir, struct dentry *new_dentry) -+{ -+ struct yaffs_dev *dev; -+ int ret_val = YAFFS_FAIL; -+ struct yaffs_obj *target; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); -+ dev = yaffs_inode_to_obj(old_dir)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ /* Check if the target is an existing directory that is not empty. */ -+ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), -+ new_dentry->d_name.name); -+ -+ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && -+ !list_empty(&target->variant.dir_variant.children)) { -+ -+ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); -+ -+ ret_val = YAFFS_FAIL; -+ } else { -+ /* Now does unlinking internally using shadowing mechanism */ -+ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); -+ -+ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), -+ old_dentry->d_name.name, -+ yaffs_inode_to_obj(new_dir), -+ new_dentry->d_name.name); -+ } -+ yaffs_gross_unlock(dev); -+ -+ if (ret_val == YAFFS_OK) { -+ if (target) -+ inode_dec_link_count(new_dentry->d_inode); -+ -+ update_dir_time(old_dir); -+ if (old_dir != new_dir) -+ update_dir_time(new_dir); -+ return 0; -+ } else { -+ return -ENOTEMPTY; -+ } -+} -+ -+ -+ -+ -+static int yaffs_unlink(struct inode *dir, struct dentry *dentry) -+{ -+ int ret_val; -+ -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", -+ (int)(dir->i_ino), dentry->d_name.name); -+ obj = yaffs_inode_to_obj(dir); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ ret_val = yaffs_unlinker(obj, dentry->d_name.name); -+ -+ if (ret_val == YAFFS_OK) { -+ inode_dec_link_count(dentry->d_inode); -+ dir->i_version++; -+ yaffs_gross_unlock(dev); -+ update_dir_time(dir); -+ return 0; -+ } -+ yaffs_gross_unlock(dev); -+ return -ENOTEMPTY; -+} -+ -+ -+ -+static const struct inode_operations yaffs_dir_inode_operations = { -+ .create = yaffs_create, -+ .lookup = yaffs_lookup, -+ .link = yaffs_link, -+ .unlink = yaffs_unlink, -+ .symlink = yaffs_symlink, -+ .mkdir = yaffs_mkdir, -+ .rmdir = yaffs_unlink, -+ .mknod = yaffs_mknod, -+ .rename = yaffs_rename, -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+/*-----------------------------------------------------------------*/ -+/* Directory search context allows us to unlock access to yaffs during -+ * filldir without causing problems with the directory being modified. -+ * This is similar to the tried and tested mechanism used in yaffs direct. -+ * -+ * A search context iterates along a doubly linked list of siblings in the -+ * directory. If the iterating object is deleted then this would corrupt -+ * the list iteration, likely causing a crash. The search context avoids -+ * this by using the remove_obj_fn to move the search context to the -+ * next object before the object is deleted. -+ * -+ * Many readdirs (and thus seach conexts) may be alive simulateously so -+ * each struct yaffs_dev has a list of these. -+ * -+ * A seach context lives for the duration of a readdir. -+ * -+ * All these functions must be called while yaffs is locked. -+ */ -+ -+struct yaffs_search_context { -+ struct yaffs_dev *dev; -+ struct yaffs_obj *dir_obj; -+ struct yaffs_obj *next_return; -+ struct list_head others; -+}; -+ -+/* -+ * yaffs_new_search() creates a new search context, initialises it and -+ * adds it to the device's search context list. -+ * -+ * Called at start of readdir. -+ */ -+static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) -+{ -+ struct yaffs_dev *dev = dir->my_dev; -+ struct yaffs_search_context *sc = -+ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); -+ if (sc) { -+ sc->dir_obj = dir; -+ sc->dev = dev; -+ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) -+ sc->next_return = NULL; -+ else -+ sc->next_return = -+ list_entry(dir->variant.dir_variant.children.next, -+ struct yaffs_obj, siblings); -+ INIT_LIST_HEAD(&sc->others); -+ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); -+ } -+ return sc; -+} -+ -+/* -+ * yaffs_search_end() disposes of a search context and cleans up. -+ */ -+static void yaffs_search_end(struct yaffs_search_context *sc) -+{ -+ if (sc) { -+ list_del(&sc->others); -+ kfree(sc); -+ } -+} -+ -+/* -+ * yaffs_search_advance() moves a search context to the next object. -+ * Called when the search iterates or when an object removal causes -+ * the search context to be moved to the next object. -+ */ -+static void yaffs_search_advance(struct yaffs_search_context *sc) -+{ -+ if (!sc) -+ return; -+ -+ if (sc->next_return == NULL || -+ list_empty(&sc->dir_obj->variant.dir_variant.children)) -+ sc->next_return = NULL; -+ else { -+ struct list_head *next = sc->next_return->siblings.next; -+ -+ if (next == &sc->dir_obj->variant.dir_variant.children) -+ sc->next_return = NULL; /* end of list */ -+ else -+ sc->next_return = -+ list_entry(next, struct yaffs_obj, siblings); -+ } -+} -+ -+/* -+ * yaffs_remove_obj_callback() is called when an object is unlinked. -+ * We check open search contexts and advance any which are currently -+ * on the object being iterated. -+ */ -+static void yaffs_remove_obj_callback(struct yaffs_obj *obj) -+{ -+ -+ struct list_head *i; -+ struct yaffs_search_context *sc; -+ struct list_head *search_contexts = -+ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); -+ -+ /* Iterate through the directory search contexts. -+ * If any are currently on the object being removed, then advance -+ * the search context to the next object to prevent a hanging pointer. -+ */ -+ list_for_each(i, search_contexts) { -+ sc = list_entry(i, struct yaffs_search_context, others); -+ if (sc->next_return == obj) -+ yaffs_search_advance(sc); -+ } -+ -+} -+ -+ -+/*-----------------------------------------------------------------*/ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) -+static int yaffs_readdir(struct file *file, struct dir_context *ctx) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ struct yaffs_search_context *sc; -+ struct inode *inode = file->f_dentry->d_inode; -+ unsigned long offset, curoffs; -+ struct yaffs_obj *l; -+ int ret_val = 0; -+ -+ char name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ obj = yaffs_dentry_to_obj(file->f_dentry); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_dev_to_lc(dev)->readdir_process = current; -+ -+ offset = ctx->pos; -+ -+ sc = yaffs_new_search(obj); -+ if (!sc) { -+ ret_val = -ENOMEM; -+ goto out; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: starting at %d", (int)offset); -+ -+ if (offset == 0) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry . ino %d", -+ (int)inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (!dir_emit_dot(file, ctx)) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ ctx->pos++; -+ } -+ if (offset == 1) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry .. ino %d", -+ (int)file->f_dentry->d_parent->d_inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (!dir_emit_dotdot(file, ctx)) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ ctx->pos++; -+ } -+ -+ curoffs = 1; -+ -+ /* If the directory has changed since the open or last call to -+ readdir, rewind to after the 2 canned entries. */ -+ if (file->f_version != inode->i_version) { -+ offset = 2; -+ ctx->pos = offset; -+ file->f_version = inode->i_version; -+ } -+ -+ while (sc->next_return) { -+ curoffs++; -+ l = sc->next_return; -+ if (curoffs >= offset) { -+ int this_inode = yaffs_get_obj_inode(l); -+ int this_type = yaffs_get_obj_type(l); -+ -+ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: %s inode %d", -+ name, yaffs_get_obj_inode(l)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (!dir_emit(ctx, name, strlen(name), -+ this_inode, this_type) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ -+ yaffs_gross_lock(dev); -+ -+ offset++; -+ ctx->pos++; -+ } -+ yaffs_search_advance(sc); -+ } -+ -+out: -+ yaffs_search_end(sc); -+ yaffs_dev_to_lc(dev)->readdir_process = NULL; -+ yaffs_gross_unlock(dev); -+ -+ return ret_val; -+} -+#else -+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ struct yaffs_search_context *sc; -+ struct inode *inode = f->f_dentry->d_inode; -+ unsigned long offset, curoffs; -+ struct yaffs_obj *l; -+ int ret_val = 0; -+ -+ char name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_dev_to_lc(dev)->readdir_process = current; -+ -+ offset = f->f_pos; -+ -+ sc = yaffs_new_search(obj); -+ if (!sc) { -+ ret_val = -ENOMEM; -+ goto out; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: starting at %d", (int)offset); -+ -+ if (offset == 0) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry . ino %d", -+ (int)inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ f->f_pos++; -+ } -+ if (offset == 1) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry .. ino %d", -+ (int)f->f_dentry->d_parent->d_inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (filldir(dirent, "..", 2, offset, -+ f->f_dentry->d_parent->d_inode->i_ino, -+ DT_DIR) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ f->f_pos++; -+ } -+ -+ curoffs = 1; -+ -+ /* If the directory has changed since the open or last call to -+ readdir, rewind to after the 2 canned entries. */ -+ if (f->f_version != inode->i_version) { -+ offset = 2; -+ f->f_pos = offset; -+ f->f_version = inode->i_version; -+ } -+ -+ while (sc->next_return) { -+ curoffs++; -+ l = sc->next_return; -+ if (curoffs >= offset) { -+ int this_inode = yaffs_get_obj_inode(l); -+ int this_type = yaffs_get_obj_type(l); -+ -+ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: %s inode %d", -+ name, yaffs_get_obj_inode(l)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (filldir(dirent, -+ name, -+ strlen(name), -+ offset, this_inode, this_type) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ -+ yaffs_gross_lock(dev); -+ -+ offset++; -+ f->f_pos++; -+ } -+ yaffs_search_advance(sc); -+ } -+ -+out: -+ yaffs_search_end(sc); -+ yaffs_dev_to_lc(dev)->readdir_process = NULL; -+ yaffs_gross_unlock(dev); -+ -+ return ret_val; -+} -+#endif -+ -+static const struct file_operations yaffs_dir_operations = { -+ .read = generic_read_dir, -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) -+ .iterate = yaffs_readdir, -+#else -+ .readdir = yaffs_readdir, -+#endif -+ .fsync = yaffs_sync_object, -+ .llseek = generic_file_llseek, -+}; -+ -+static void yaffs_fill_inode_from_obj(struct inode *inode, -+ struct yaffs_obj *obj) -+{ -+ if (inode && obj) { -+ -+ /* Check mode against the variant type and attempt to repair if broken. */ -+ u32 mode = obj->yst_mode; -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (!S_ISREG(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFREG; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ if (!S_ISLNK(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFLNK; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ if (!S_ISDIR(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFDIR; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ default: -+ /* TODO? */ -+ break; -+ } -+ -+ inode->i_flags |= S_NOATIME; -+ -+ inode->i_ino = obj->obj_id; -+ inode->i_mode = obj->yst_mode; -+ i_uid_write(inode, obj->yst_uid); -+ i_gid_write(inode, obj->yst_gid); -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+ inode->i_blksize = inode->i_sb->s_blocksize; -+#endif -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ -+ inode->i_rdev = old_decode_dev(obj->yst_rdev); -+ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); -+ inode->i_atime.tv_nsec = 0; -+ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; -+ inode->i_mtime.tv_nsec = 0; -+ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; -+ inode->i_ctime.tv_nsec = 0; -+#else -+ inode->i_rdev = obj->yst_rdev; -+ inode->i_atime = obj->yst_atime; -+ inode->i_mtime = obj->yst_mtime; -+ inode->i_ctime = obj->yst_ctime; -+#endif -+ inode->i_size = yaffs_get_obj_length(obj); -+ inode->i_blocks = (inode->i_size + 511) >> 9; -+ -+ set_nlink(inode, yaffs_get_obj_link_count(obj)); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", -+ inode->i_mode, i_uid_read(inode), i_gid_read(inode), -+ inode->i_size, atomic_read(&inode->i_count)); -+ -+ switch (obj->yst_mode & S_IFMT) { -+ default: /* fifo, device or socket */ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ init_special_inode(inode, obj->yst_mode, -+ old_decode_dev(obj->yst_rdev)); -+#else -+ init_special_inode(inode, obj->yst_mode, -+ (dev_t) (obj->yst_rdev)); -+#endif -+ break; -+ case S_IFREG: /* file */ -+ inode->i_op = &yaffs_file_inode_operations; -+ inode->i_fop = &yaffs_file_operations; -+ inode->i_mapping->a_ops = -+ &yaffs_file_address_operations; -+ break; -+ case S_IFDIR: /* directory */ -+ inode->i_op = &yaffs_dir_inode_operations; -+ inode->i_fop = &yaffs_dir_operations; -+ break; -+ case S_IFLNK: /* symlink */ -+ inode->i_op = &yaffs_symlink_inode_operations; -+ break; -+ } -+ -+ yaffs_inode_to_obj_lv(inode) = obj; -+ -+ obj->my_inode = inode; -+ -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_fill_inode invalid parameters"); -+ } -+ -+} -+ -+ -+ -+/* -+ * yaffs background thread functions . -+ * yaffs_bg_thread_fn() the thread function -+ * yaffs_bg_start() launches the background thread. -+ * yaffs_bg_stop() cleans up the background thread. -+ * -+ * NB: -+ * The thread should only run after the yaffs is initialised -+ * The thread should be stopped before yaffs is unmounted. -+ * The thread should not do any writing while the fs is in read only. -+ */ -+ -+static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) -+{ -+ unsigned erased_chunks = -+ dev->n_erased_blocks * dev->param.chunks_per_block; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ unsigned scattered = 0; /* Free chunks not in an erased block */ -+ -+ if (erased_chunks < dev->n_free_chunks) -+ scattered = (dev->n_free_chunks - erased_chunks); -+ -+ if (!context->bg_running) -+ return 0; -+ else if (scattered < (dev->param.chunks_per_block * 2)) -+ return 0; -+ else if (erased_chunks > dev->n_free_chunks / 2) -+ return 0; -+ else if (erased_chunks > dev->n_free_chunks / 4) -+ return 1; -+ else -+ return 2; -+} -+ -+#ifdef YAFFS_COMPILE_BACKGROUND -+ -+void yaffs_background_waker(unsigned long data) -+{ -+ wake_up_process((struct task_struct *)data); -+} -+ -+static int yaffs_bg_thread_fn(void *data) -+{ -+ struct yaffs_dev *dev = (struct yaffs_dev *)data; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ unsigned long now = jiffies; -+ unsigned long next_dir_update = now; -+ unsigned long next_gc = now; -+ unsigned long expires; -+ unsigned int urgency; -+ -+ int gc_result; -+ struct timer_list timer; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "yaffs_background starting for dev %p", (void *)dev); -+ -+#ifdef YAFFS_COMPILE_FREEZER -+ set_freezable(); -+#endif -+ while (context->bg_running) { -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); -+ -+ if (kthread_should_stop()) -+ break; -+ -+#ifdef YAFFS_COMPILE_FREEZER -+ if (try_to_freeze()) -+ continue; -+#endif -+ yaffs_gross_lock(dev); -+ -+ now = jiffies; -+ -+ if (time_after(now, next_dir_update) && yaffs_bg_enable) { -+ yaffs_update_dirty_dirs(dev); -+ next_dir_update = now + HZ; -+ } -+ -+ if (time_after(now, next_gc) && yaffs_bg_enable) { -+ if (!dev->is_checkpointed) { -+ urgency = yaffs_bg_gc_urgency(dev); -+ gc_result = yaffs_bg_gc(dev, urgency); -+ if (urgency > 1) -+ next_gc = now + HZ / 20 + 1; -+ else if (urgency > 0) -+ next_gc = now + HZ / 10 + 1; -+ else -+ next_gc = now + HZ * 2; -+ } else { -+ /* -+ * gc not running so set to next_dir_update -+ * to cut down on wake ups -+ */ -+ next_gc = next_dir_update; -+ } -+ } -+ yaffs_gross_unlock(dev); -+#if 1 -+ expires = next_dir_update; -+ if (time_before(next_gc, expires)) -+ expires = next_gc; -+ if (time_before(expires, now)) -+ expires = now + HZ; -+ -+ Y_INIT_TIMER(&timer); -+ timer.expires = expires + 1; -+ timer.data = (unsigned long)current; -+ timer.function = yaffs_background_waker; -+ -+ set_current_state(TASK_INTERRUPTIBLE); -+ add_timer(&timer); -+ schedule(); -+ del_timer_sync(&timer); -+#else -+ msleep(10); -+#endif -+ } -+ -+ return 0; -+} -+ -+static int yaffs_bg_start(struct yaffs_dev *dev) -+{ -+ int retval = 0; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ -+ if (dev->read_only) -+ return -1; -+ -+ context->bg_running = 1; -+ -+ context->bg_thread = kthread_run(yaffs_bg_thread_fn, -+ (void *)dev, "yaffs-bg-%d", -+ context->mount_id); -+ -+ if (IS_ERR(context->bg_thread)) { -+ retval = PTR_ERR(context->bg_thread); -+ context->bg_thread = NULL; -+ context->bg_running = 0; -+ } -+ return retval; -+} -+ -+static void yaffs_bg_stop(struct yaffs_dev *dev) -+{ -+ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); -+ -+ ctxt->bg_running = 0; -+ -+ if (ctxt->bg_thread) { -+ kthread_stop(ctxt->bg_thread); -+ ctxt->bg_thread = NULL; -+ } -+} -+#else -+static int yaffs_bg_thread_fn(void *data) -+{ -+ return 0; -+} -+ -+static int yaffs_bg_start(struct yaffs_dev *dev) -+{ -+ return 0; -+} -+ -+static void yaffs_bg_stop(struct yaffs_dev *dev) -+{ -+} -+#endif -+ -+ -+static void yaffs_flush_inodes(struct super_block *sb) -+{ -+ struct inode *iptr; -+ struct yaffs_obj *obj; -+ -+ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { -+ obj = yaffs_inode_to_obj(iptr); -+ if (obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "flushing obj %d", -+ obj->obj_id); -+ yaffs_flush_file(obj, 1, 0); -+ } -+ } -+} -+ -+static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ if (!dev) -+ return; -+ -+ yaffs_flush_inodes(sb); -+ yaffs_update_dirty_dirs(dev); -+ yaffs_flush_whole_cache(dev); -+ if (do_checkpoint) -+ yaffs_checkpoint_save(dev); -+} -+ -+static LIST_HEAD(yaffs_context_list); -+struct mutex yaffs_context_lock; -+ -+static void yaffs_put_super(struct super_block *sb) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, -+ "yaffs_put_super"); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, -+ "Shutting down yaffs background thread"); -+ yaffs_bg_stop(dev); -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, -+ "yaffs background thread shut down"); -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_flush_super(sb, 1); -+ -+ yaffs_deinitialise(dev); -+ -+ yaffs_gross_unlock(dev); -+ -+ mutex_lock(&yaffs_context_lock); -+ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); -+ mutex_unlock(&yaffs_context_lock); -+ -+ if (yaffs_dev_to_lc(dev)->spare_buffer) { -+ kfree(yaffs_dev_to_lc(dev)->spare_buffer); -+ yaffs_dev_to_lc(dev)->spare_buffer = NULL; -+ } -+ -+ kfree(dev); -+ -+ yaffs_put_mtd_device(mtd); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, -+ "yaffs_put_super done"); -+} -+ -+ -+static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) -+{ -+ return yaffs_gc_control; -+} -+ -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+ -+static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, -+ uint32_t generation) -+{ -+ return Y_IGET(sb, ino); -+} -+ -+static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, -+ struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, -+ yaffs2_nfs_get_inode); -+} -+ -+static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, -+ struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ return generic_fh_to_parent(sb, fid, fh_len, fh_type, -+ yaffs2_nfs_get_inode); -+} -+ -+struct dentry *yaffs2_get_parent(struct dentry *dentry) -+{ -+ -+ struct super_block *sb = dentry->d_inode->i_sb; -+ struct dentry *parent = ERR_PTR(-ENOENT); -+ struct inode *inode; -+ unsigned long parent_ino; -+ struct yaffs_obj *d_obj; -+ struct yaffs_obj *parent_obj; -+ -+ d_obj = yaffs_inode_to_obj(dentry->d_inode); -+ -+ if (d_obj) { -+ parent_obj = d_obj->parent; -+ if (parent_obj) { -+ parent_ino = yaffs_get_obj_inode(parent_obj); -+ inode = Y_IGET(sb, parent_ino); -+ -+ if (IS_ERR(inode)) { -+ parent = ERR_CAST(inode); -+ } else { -+ parent = d_obtain_alias(inode); -+ if (!IS_ERR(parent)) { -+ parent = ERR_PTR(-ENOMEM); -+ iput(inode); -+ } -+ } -+ } -+ } -+ -+ return parent; -+} -+ -+/* Just declare a zero structure as a NULL value implies -+ * using the default functions of exportfs. -+ */ -+ -+static struct export_operations yaffs_export_ops = { -+ .fh_to_dentry = yaffs2_fh_to_dentry, -+ .fh_to_parent = yaffs2_fh_to_parent, -+ .get_parent = yaffs2_get_parent, -+}; -+ -+#endif -+ -+static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) -+{ -+ /* Clear the association between the inode and -+ * the struct yaffs_obj. -+ */ -+ obj->my_inode = NULL; -+ yaffs_inode_to_obj_lv(inode) = NULL; -+ -+ /* If the object freeing was deferred, then the real -+ * free happens now. -+ * This should fix the inode inconsistency problem. -+ */ -+ yaffs_handle_defered_free(obj); -+} -+ -+#ifdef YAFFS_HAS_EVICT_INODE -+/* yaffs_evict_inode combines into one operation what was previously done in -+ * yaffs_clear_inode() and yaffs_delete_inode() -+ * -+ */ -+static void yaffs_evict_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ int deleteme = 0; -+ -+ obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_evict_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (!inode->i_nlink && !is_bad_inode(inode)) -+ deleteme = 1; -+ truncate_inode_pages(&inode->i_data, 0); -+ Y_CLEAR_INODE(inode); -+ -+ if (deleteme && obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_del_obj(obj); -+ yaffs_gross_unlock(dev); -+ } -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_unstitch_obj(inode, obj); -+ yaffs_gross_unlock(dev); -+ } -+} -+#else -+ -+/* clear is called to tell the fs to release any per-inode data it holds. -+ * The object might still exist on disk and is just being thrown out of the cache -+ * or else the object has actually been deleted and we're being called via -+ * the chain -+ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() -+ */ -+ -+static void yaffs_clear_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_clear_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_unstitch_obj(inode, obj); -+ yaffs_gross_unlock(dev); -+ } -+ -+} -+ -+/* delete is called when the link count is zero and the inode -+ * is put (ie. nobody wants to know about it anymore, time to -+ * delete the file). -+ * NB Must call clear_inode() -+ */ -+static void yaffs_delete_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_delete_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_del_obj(obj); -+ yaffs_gross_unlock(dev); -+ } -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) -+ truncate_inode_pages(&inode->i_data, 0); -+#endif -+ clear_inode(inode); -+} -+#endif -+ -+ -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ struct super_block *sb = dentry->d_sb; -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+#else -+static int yaffs_statfs(struct super_block *sb, struct statfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+#endif -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); -+ -+ yaffs_gross_lock(dev); -+ -+ buf->f_type = YAFFS_MAGIC; -+ buf->f_bsize = sb->s_blocksize; -+ buf->f_namelen = 255; -+ -+ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { -+ /* Do this if chunk size is not a power of 2 */ -+ -+ uint64_t bytes_in_dev; -+ uint64_t bytes_free; -+ -+ bytes_in_dev = -+ ((uint64_t) -+ ((dev->param.end_block - dev->param.start_block + -+ 1))) * ((uint64_t) (dev->param.chunks_per_block * -+ dev->data_bytes_per_chunk)); -+ -+ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ -+ buf->f_blocks = bytes_in_dev; -+ -+ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * -+ ((uint64_t) (dev->data_bytes_per_chunk)); -+ -+ do_div(bytes_free, sb->s_blocksize); -+ -+ buf->f_bfree = bytes_free; -+ -+ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { -+ -+ buf->f_blocks = -+ (dev->param.end_block - dev->param.start_block + 1) * -+ dev->param.chunks_per_block / -+ (sb->s_blocksize / dev->data_bytes_per_chunk); -+ buf->f_bfree = -+ yaffs_get_n_free_chunks(dev) / -+ (sb->s_blocksize / dev->data_bytes_per_chunk); -+ } else { -+ buf->f_blocks = -+ (dev->param.end_block - dev->param.start_block + 1) * -+ dev->param.chunks_per_block * -+ (dev->data_bytes_per_chunk / sb->s_blocksize); -+ -+ buf->f_bfree = -+ yaffs_get_n_free_chunks(dev) * -+ (dev->data_bytes_per_chunk / sb->s_blocksize); -+ } -+ -+ buf->f_files = 0; -+ buf->f_ffree = 0; -+ buf->f_bavail = buf->f_bfree; -+ -+ yaffs_gross_unlock(dev); -+ return 0; -+} -+ -+ -+ -+static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) -+{ -+ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); -+ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); -+ int do_checkpoint; -+ int dirty = yaffs_check_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, -+ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", -+ gc_urgent, -+ dirty ? "dirty" : "clean", -+ request_checkpoint ? "checkpoint requested" : "no checkpoint", -+ oneshot_checkpoint ? " one-shot" : ""); -+ -+ yaffs_gross_lock(dev); -+ do_checkpoint = ((request_checkpoint && !gc_urgent) || -+ oneshot_checkpoint) && !dev->is_checkpointed; -+ -+ if (dirty || do_checkpoint) { -+ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); -+ yaffs_clear_super_dirty(dev); -+ if (oneshot_checkpoint) -+ yaffs_auto_checkpoint &= ~4; -+ } -+ yaffs_gross_unlock(dev); -+ -+ return 0; -+} -+ -+ -+#ifdef YAFFS_HAS_WRITE_SUPER -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static void yaffs_write_super(struct super_block *sb) -+#else -+static int yaffs_write_super(struct super_block *sb) -+#endif -+{ -+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, -+ "yaffs_write_super %s", -+ request_checkpoint ? " checkpt" : ""); -+ -+ yaffs_do_sync_fs(sb, request_checkpoint); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) -+ return 0; -+#endif -+} -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_sync_fs(struct super_block *sb, int wait) -+#else -+static int yaffs_sync_fs(struct super_block *sb) -+#endif -+{ -+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, -+ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); -+ -+ yaffs_do_sync_fs(sb, request_checkpoint); -+ -+ return 0; -+} -+ -+ -+ -+static const struct super_operations yaffs_super_ops = { -+ .statfs = yaffs_statfs, -+ -+#ifndef YAFFS_USE_OWN_IGET -+ .read_inode = yaffs_read_inode, -+#endif -+#ifdef YAFFS_HAS_PUT_INODE -+ .put_inode = yaffs_put_inode, -+#endif -+ .put_super = yaffs_put_super, -+#ifdef YAFFS_HAS_EVICT_INODE -+ .evict_inode = yaffs_evict_inode, -+#else -+ .delete_inode = yaffs_delete_inode, -+ .clear_inode = yaffs_clear_inode, -+#endif -+ .sync_fs = yaffs_sync_fs, -+#ifdef YAFFS_HAS_WRITE_SUPER -+ .write_super = yaffs_write_super, -+#endif -+}; -+ -+struct yaffs_options { -+ int inband_tags; -+ int skip_checkpoint_read; -+ int skip_checkpoint_write; -+ int no_cache; -+ int tags_ecc_on; -+ int tags_ecc_overridden; -+ int lazy_loading_enabled; -+ int lazy_loading_overridden; -+ int empty_lost_and_found; -+ int empty_lost_and_found_overridden; -+ int disable_summary; -+}; -+ -+#define MAX_OPT_LEN 30 -+static int yaffs_parse_options(struct yaffs_options *options, -+ const char *options_str) -+{ -+ char cur_opt[MAX_OPT_LEN + 1]; -+ int p; -+ int error = 0; -+ -+ /* Parse through the options which is a comma seperated list */ -+ -+ while (options_str && *options_str && !error) { -+ memset(cur_opt, 0, MAX_OPT_LEN + 1); -+ p = 0; -+ -+ while (*options_str == ',') -+ options_str++; -+ -+ while (*options_str && *options_str != ',') { -+ if (p < MAX_OPT_LEN) { -+ cur_opt[p] = *options_str; -+ p++; -+ } -+ options_str++; -+ } -+ -+ if (!strcmp(cur_opt, "inband-tags")) { -+ options->inband_tags = 1; -+ } else if (!strcmp(cur_opt, "tags-ecc-off")) { -+ options->tags_ecc_on = 0; -+ options->tags_ecc_overridden = 1; -+ } else if (!strcmp(cur_opt, "tags-ecc-on")) { -+ options->tags_ecc_on = 1; -+ options->tags_ecc_overridden = 1; -+ } else if (!strcmp(cur_opt, "lazy-loading-off")) { -+ options->lazy_loading_enabled = 0; -+ options->lazy_loading_overridden = 1; -+ } else if (!strcmp(cur_opt, "lazy-loading-on")) { -+ options->lazy_loading_enabled = 1; -+ options->lazy_loading_overridden = 1; -+ } else if (!strcmp(cur_opt, "disable-summary")) { -+ options->disable_summary = 1; -+ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { -+ options->empty_lost_and_found = 0; -+ options->empty_lost_and_found_overridden = 1; -+ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { -+ options->empty_lost_and_found = 1; -+ options->empty_lost_and_found_overridden = 1; -+ } else if (!strcmp(cur_opt, "no-cache")) { -+ options->no_cache = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { -+ options->skip_checkpoint_read = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { -+ options->skip_checkpoint_write = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint")) { -+ options->skip_checkpoint_read = 1; -+ options->skip_checkpoint_write = 1; -+ } else { -+ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", -+ cur_opt); -+ error = 1; -+ } -+ } -+ -+ return error; -+} -+ -+ -+static struct dentry *yaffs_make_root(struct inode *inode) -+{ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) -+ struct dentry *root = d_alloc_root(inode); -+ -+ if (!root) -+ iput(inode); -+ -+ return root; -+#else -+ return d_make_root(inode); -+#endif -+} -+ -+ -+ -+ -+static struct super_block *yaffs_internal_read_super(int yaffs_version, -+ struct super_block *sb, -+ void *data, int silent) -+{ -+ int n_blocks; -+ struct inode *inode = NULL; -+ struct dentry *root; -+ struct yaffs_dev *dev = 0; -+ char devname_buf[BDEVNAME_SIZE + 1]; -+ struct mtd_info *mtd; -+ int err; -+ char *data_str = (char *)data; -+ struct yaffs_linux_context *context = NULL; -+ struct yaffs_param *param; -+ -+ int read_only = 0; -+ int inband_tags = 0; -+ -+ struct yaffs_options options; -+ -+ unsigned mount_id; -+ int found; -+ struct yaffs_linux_context *context_iterator; -+ struct list_head *l; -+ -+ if (!sb) { -+ printk(KERN_INFO "yaffs: sb is NULL\n"); -+ return NULL; -+ } -+ -+ sb->s_magic = YAFFS_MAGIC; -+ sb->s_op = &yaffs_super_ops; -+ sb->s_flags |= MS_NOATIME; -+ -+ read_only = ((sb->s_flags & MS_RDONLY) != 0); -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+ sb->s_export_op = &yaffs_export_ops; -+#endif -+ -+ if (!sb->s_dev) -+ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); -+ else if (!yaffs_devname(sb, devname_buf)) -+ printk(KERN_INFO "yaffs: devname is NULL\n"); -+ else -+ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", -+ sb->s_dev, -+ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); -+ -+ if (!data_str) -+ data_str = ""; -+ -+ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); -+ -+ memset(&options, 0, sizeof(options)); -+ -+ if (yaffs_parse_options(&options, data_str)) { -+ /* Option parsing failed */ -+ return NULL; -+ } -+ -+ sb->s_blocksize = PAGE_CACHE_SIZE; -+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: Using yaffs%d", yaffs_version); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", -+ MAJOR(sb->s_dev), MINOR(sb->s_dev), -+ yaffs_devname(sb, devname_buf)); -+ -+ /* Get the device */ -+ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); -+ if (IS_ERR(mtd)) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: MTD device %u either not valid or unavailable", -+ MINOR(sb->s_dev)); -+ return NULL; -+ } -+ -+ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); -+ yaffs_version = 2; -+ } -+ -+ /* Added NCB 26/5/2006 for completeness */ -+ if (yaffs_version == 2 && !options.inband_tags -+ && WRITE_SIZE(mtd) == 512) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); -+ yaffs_version = 1; -+ } -+ -+ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || -+ options.inband_tags) -+ inband_tags = 1; -+ -+ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) -+ return NULL; -+ -+ /* OK, so if we got here, we have an MTD that's NAND and looks -+ * like it has the right capabilities -+ * Set the struct yaffs_dev up for mtd -+ */ -+ -+ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { -+ read_only = 1; -+ printk(KERN_INFO -+ "yaffs: mtd is read only, setting superblock read only\n" -+ ); -+ sb->s_flags |= MS_RDONLY; -+ } -+ -+ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); -+ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); -+ -+ if (!dev || !context) { -+ kfree(dev); -+ kfree(context); -+ dev = NULL; -+ context = NULL; -+ -+ /* Deep shit could not allocate device structure */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." -+ ); -+ return NULL; -+ } -+ memset(dev, 0, sizeof(struct yaffs_dev)); -+ param = &(dev->param); -+ -+ memset(context, 0, sizeof(struct yaffs_linux_context)); -+ dev->os_context = context; -+ INIT_LIST_HEAD(&(context->context_list)); -+ context->dev = dev; -+ context->super = sb; -+ -+ dev->read_only = read_only; -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ sb->s_fs_info = dev; -+#else -+ sb->u.generic_sbp = dev; -+#endif -+ -+ -+ dev->driver_context = mtd; -+ param->name = mtd->name; -+ -+ /* Set up the memory size parameters.... */ -+ -+ -+ param->n_reserved_blocks = 5; -+ param->n_caches = (options.no_cache) ? 0 : 10; -+ param->inband_tags = inband_tags; -+ -+ param->enable_xattr = 1; -+ if (options.lazy_loading_overridden) -+ param->disable_lazy_load = !options.lazy_loading_enabled; -+ -+ param->defered_dir_update = 1; -+ -+ if (options.tags_ecc_overridden) -+ param->no_tags_ecc = !options.tags_ecc_on; -+ -+ param->empty_lost_n_found = 1; -+ param->refresh_period = 500; -+ param->disable_summary = options.disable_summary; -+ -+ -+#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING -+ param->disable_bad_block_marking = 1; -+#endif -+ if (options.empty_lost_and_found_overridden) -+ param->empty_lost_n_found = options.empty_lost_and_found; -+ -+ /* ... and the functions. */ -+ if (yaffs_version == 2) { -+ param->is_yaffs2 = 1; -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+ param->total_bytes_per_chunk = mtd->writesize; -+ param->chunks_per_block = mtd->erasesize / mtd->writesize; -+#else -+ param->total_bytes_per_chunk = mtd->oobblock; -+ param->chunks_per_block = mtd->erasesize / mtd->oobblock; -+#endif -+ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); -+ -+ param->start_block = 0; -+ param->end_block = n_blocks - 1; -+ } else { -+ param->is_yaffs2 = 0; -+ n_blocks = YCALCBLOCKS(mtd->size, -+ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); -+ -+ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; -+ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; -+ } -+ -+ param->start_block = 0; -+ param->end_block = n_blocks - 1; -+ -+ yaffs_mtd_drv_install(dev); -+ -+ param->sb_dirty_fn = yaffs_set_super_dirty; -+ param->gc_control_fn = yaffs_gc_control_callback; -+ -+ yaffs_dev_to_lc(dev)->super = sb; -+ -+ param->use_nand_ecc = 1; -+ -+ param->skip_checkpt_rd = options.skip_checkpoint_read; -+ param->skip_checkpt_wr = options.skip_checkpoint_write; -+ -+ mutex_lock(&yaffs_context_lock); -+ /* Get a mount id */ -+ found = 0; -+ for (mount_id = 0; !found; mount_id++) { -+ found = 1; -+ list_for_each(l, &yaffs_context_list) { -+ context_iterator = -+ list_entry(l, struct yaffs_linux_context, -+ context_list); -+ if (context_iterator->mount_id == mount_id) -+ found = 0; -+ } -+ } -+ context->mount_id = mount_id; -+ -+ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), -+ &yaffs_context_list); -+ mutex_unlock(&yaffs_context_lock); -+ -+ /* Directory search handling... */ -+ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); -+ param->remove_obj_fn = yaffs_remove_obj_callback; -+ -+ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); -+ -+ yaffs_gross_lock(dev); -+ -+ err = yaffs_guts_initialise(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: guts initialised %s", -+ (err == YAFFS_OK) ? "OK" : "FAILED"); -+ -+ if (err == YAFFS_OK) -+ yaffs_bg_start(dev); -+ -+ if (!context->bg_thread) -+ param->defered_dir_update = 0; -+ -+ sb->s_maxbytes = yaffs_max_file_size(dev); -+ -+ /* Release lock before yaffs_get_inode() */ -+ yaffs_gross_unlock(dev); -+ -+ /* Create root inode */ -+ if (err == YAFFS_OK) -+ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); -+ -+ if (!inode) -+ return NULL; -+ -+ inode->i_op = &yaffs_dir_inode_operations; -+ inode->i_fop = &yaffs_dir_operations; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); -+ -+ root = yaffs_make_root(inode); -+ -+ if (!root) -+ return NULL; -+ -+ sb->s_root = root; -+ if(!dev->is_checkpointed) -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs_read_super: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); -+ return sb; -+} -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data) -+{ -+ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); -+} -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data, struct vfsmount *mnt) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs_internal_read_super_mtd, mnt); -+} -+#else -+static struct super_block *yaffs_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs_internal_read_super_mtd); -+} -+#endif -+ -+static struct file_system_type yaffs_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "yaffs", -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+ .mount = yaffs_mount, -+#else -+ .get_sb = yaffs_read_super, -+#endif -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV, -+}; -+#else -+static struct super_block *yaffs_read_super(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(1, sb, data, silent); -+} -+ -+static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, -+ FS_REQUIRES_DEV); -+#endif -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data) -+{ -+ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); -+} -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs2_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, void *data, -+ struct vfsmount *mnt) -+{ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs2_internal_read_super_mtd, mnt); -+} -+#else -+static struct super_block *yaffs2_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs2_internal_read_super_mtd); -+} -+#endif -+ -+static struct file_system_type yaffs2_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "yaffs2", -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+ .mount = yaffs2_mount, -+#else -+ .get_sb = yaffs2_read_super, -+#endif -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV, -+}; -+#else -+static struct super_block *yaffs2_read_super(struct super_block *sb, -+ void *data, int silent) -+{ -+ return yaffs_internal_read_super(2, sb, data, silent); -+} -+ -+static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, -+ FS_REQUIRES_DEV); -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) -+static struct proc_dir_entry *my_proc_entry; -+ -+static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) -+{ -+ struct yaffs_param *param = &dev->param; -+ int bs[10]; -+ -+ yaffs_count_blocks_by_state(dev,bs); -+ -+ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); -+ buf += sprintf(buf, "end_block............ %d\n", param->end_block); -+ buf += sprintf(buf, "total_bytes_per_chunk %d\n", -+ param->total_bytes_per_chunk); -+ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); -+ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); -+ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); -+ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); -+ buf += sprintf(buf, "empty_lost_n_found... %d\n", -+ param->empty_lost_n_found); -+ buf += sprintf(buf, "disable_lazy_load.... %d\n", -+ param->disable_lazy_load); -+ buf += sprintf(buf, "disable_bad_block_mrk %d\n", -+ param->disable_bad_block_marking); -+ buf += sprintf(buf, "refresh_period....... %d\n", -+ param->refresh_period); -+ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); -+ buf += sprintf(buf, "n_reserved_blocks.... %d\n", -+ param->n_reserved_blocks); -+ buf += sprintf(buf, "always_check_erased.. %d\n", -+ param->always_check_erased); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "block count by state\n"); -+ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", -+ bs[0], bs[1], bs[2], bs[3], bs[4]); -+ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", -+ bs[5], bs[6], bs[7], bs[8], bs[9]); -+ -+ return buf; -+} -+ -+static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) -+{ -+ buf += sprintf(buf, "max file size....... %lld\n", -+ (long long) yaffs_max_file_size(dev)); -+ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", -+ dev->data_bytes_per_chunk); -+ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); -+ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); -+ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); -+ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", -+ dev->blocks_in_checkpt); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); -+ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); -+ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); -+ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); -+ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); -+ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); -+ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); -+ buf += sprintf(buf, "passive_gc_count..... %u\n", -+ dev->passive_gc_count); -+ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", -+ dev->oldest_dirty_gc_count); -+ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); -+ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); -+ buf += sprintf(buf, "n_retried_writes..... %u\n", -+ dev->n_retried_writes); -+ buf += sprintf(buf, "n_retired_blocks..... %u\n", -+ dev->n_retired_blocks); -+ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); -+ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); -+ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", -+ dev->n_tags_ecc_fixed); -+ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", -+ dev->n_tags_ecc_unfixed); -+ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); -+ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); -+ buf += sprintf(buf, "n_unlinked_files..... %u\n", -+ dev->n_unlinked_files); -+ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); -+ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); -+ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); -+ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); -+ -+ return buf; -+} -+ -+static int yaffs_proc_read(char *page, -+ char **start, -+ off_t offset, int count, int *eof, void *data) -+{ -+ struct list_head *item; -+ char *buf = page; -+ int step = offset; -+ int n = 0; -+ -+ /* Get proc_file_read() to step 'offset' by one on each sucessive call. -+ * We use 'offset' (*ppos) to indicate where we are in dev_list. -+ * This also assumes the user has posted a read buffer large -+ * enough to hold the complete output; but that's life in /proc. -+ */ -+ -+ *(int *)start = 1; -+ -+ /* Print header first */ -+ if (step == 0) -+ buf += -+ sprintf(buf, -+ "Multi-version YAFFS built:" __DATE__ " " __TIME__ -+ "\n"); -+ else if (step == 1) -+ buf += sprintf(buf, "\n"); -+ else { -+ step -= 2; -+ -+ mutex_lock(&yaffs_context_lock); -+ -+ /* Locate and print the Nth entry. Order N-squared but N is small. */ -+ list_for_each(item, &yaffs_context_list) { -+ struct yaffs_linux_context *dc = -+ list_entry(item, struct yaffs_linux_context, -+ context_list); -+ struct yaffs_dev *dev = dc->dev; -+ -+ if (n < (step & ~1)) { -+ n += 2; -+ continue; -+ } -+ if ((step & 1) == 0) { -+ buf += -+ sprintf(buf, "\nDevice %d \"%s\"\n", n, -+ dev->param.name); -+ buf = yaffs_dump_dev_part0(buf, dev); -+ } else { -+ buf = yaffs_dump_dev_part1(buf, dev); -+ } -+ -+ break; -+ } -+ mutex_unlock(&yaffs_context_lock); -+ } -+ -+ return buf - page < count ? buf - page : count; -+} -+ -+/** -+ * Set the verbosity of the warnings and error messages. -+ * -+ * Note that the names can only be a..z or _ with the current code. -+ */ -+ -+static struct { -+ char *mask_name; -+ unsigned mask_bitfield; -+} mask_flags[] = { -+ {"allocate", YAFFS_TRACE_ALLOCATE}, -+ {"always", YAFFS_TRACE_ALWAYS}, -+ {"background", YAFFS_TRACE_BACKGROUND}, -+ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, -+ {"buffers", YAFFS_TRACE_BUFFERS}, -+ {"bug", YAFFS_TRACE_BUG}, -+ {"checkpt", YAFFS_TRACE_CHECKPOINT}, -+ {"deletion", YAFFS_TRACE_DELETION}, -+ {"erase", YAFFS_TRACE_ERASE}, -+ {"error", YAFFS_TRACE_ERROR}, -+ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, -+ {"gc", YAFFS_TRACE_GC}, -+ {"lock", YAFFS_TRACE_LOCK}, -+ {"mtd", YAFFS_TRACE_MTD}, -+ {"nandaccess", YAFFS_TRACE_NANDACCESS}, -+ {"os", YAFFS_TRACE_OS}, -+ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, -+ {"scan", YAFFS_TRACE_SCAN}, -+ {"mount", YAFFS_TRACE_MOUNT}, -+ {"tracing", YAFFS_TRACE_TRACING}, -+ {"sync", YAFFS_TRACE_SYNC}, -+ {"write", YAFFS_TRACE_WRITE}, -+ {"verify", YAFFS_TRACE_VERIFY}, -+ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, -+ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, -+ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, -+ {"all", 0xffffffff}, -+ {"none", 0}, -+ {NULL, 0}, -+}; -+ -+#define MAX_MASK_NAME_LENGTH 40 -+static int yaffs_proc_write_trace_options(struct file *file, const char *buf, -+ unsigned long count, void *data) -+{ -+ unsigned rg = 0, mask_bitfield; -+ char *end; -+ char *mask_name; -+ const char *x; -+ char substring[MAX_MASK_NAME_LENGTH + 1]; -+ int i; -+ int done = 0; -+ int add, len = 0; -+ int pos = 0; -+ -+ rg = yaffs_trace_mask; -+ -+ while (!done && (pos < count)) { -+ done = 1; -+ while ((pos < count) && isspace(buf[pos])) -+ pos++; -+ -+ switch (buf[pos]) { -+ case '+': -+ case '-': -+ case '=': -+ add = buf[pos]; -+ pos++; -+ break; -+ -+ default: -+ add = ' '; -+ break; -+ } -+ mask_name = NULL; -+ -+ mask_bitfield = simple_strtoul(buf + pos, &end, 0); -+ -+ if (end > buf + pos) { -+ mask_name = "numeral"; -+ len = end - (buf + pos); -+ pos += len; -+ done = 0; -+ } else { -+ for (x = buf + pos, i = 0; -+ (*x == '_' || (*x >= 'a' && *x <= 'z')) && -+ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) -+ substring[i] = *x; -+ substring[i] = '\0'; -+ -+ for (i = 0; mask_flags[i].mask_name != NULL; i++) { -+ if (strcmp(substring, mask_flags[i].mask_name) -+ == 0) { -+ mask_name = mask_flags[i].mask_name; -+ mask_bitfield = -+ mask_flags[i].mask_bitfield; -+ done = 0; -+ break; -+ } -+ } -+ } -+ -+ if (mask_name != NULL) { -+ done = 0; -+ switch (add) { -+ case '-': -+ rg &= ~mask_bitfield; -+ break; -+ case '+': -+ rg |= mask_bitfield; -+ break; -+ case '=': -+ rg = mask_bitfield; -+ break; -+ default: -+ rg |= mask_bitfield; -+ break; -+ } -+ } -+ } -+ -+ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; -+ -+ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); -+ -+ if (rg & YAFFS_TRACE_ALWAYS) { -+ for (i = 0; mask_flags[i].mask_name != NULL; i++) { -+ char flag; -+ flag = ((rg & mask_flags[i].mask_bitfield) == -+ mask_flags[i].mask_bitfield) ? '+' : '-'; -+ printk(KERN_DEBUG "%c%s\n", flag, -+ mask_flags[i].mask_name); -+ } -+ } -+ -+ return count; -+} -+ -+/* Debug strings are of the form: -+ * .bnnn print info on block n -+ * .cobjn,chunkn print nand chunk id for objn:chunkn -+ */ -+ -+static int yaffs_proc_debug_write(struct file *file, const char *buf, -+ unsigned long count, void *data) -+{ -+ -+ char str[100]; -+ char *p0; -+ char *p1; -+ long p1_val; -+ long p0_val; -+ char cmd; -+ struct list_head *item; -+ -+ memset(str, 0, sizeof(str)); -+ memcpy(str, buf, min(count, sizeof(str) -1)); -+ -+ cmd = str[1]; -+ -+ p0 = str + 2; -+ -+ p1 = p0; -+ -+ while (*p1 && *p1 != ',') { -+ p1++; -+ } -+ *p1 = '\0'; -+ p1++; -+ -+ p0_val = simple_strtol(p0, NULL, 0); -+ p1_val = simple_strtol(p1, NULL, 0); -+ -+ -+ mutex_lock(&yaffs_context_lock); -+ -+ /* Locate and print the Nth entry. Order N-squared but N is small. */ -+ list_for_each(item, &yaffs_context_list) { -+ struct yaffs_linux_context *dc = -+ list_entry(item, struct yaffs_linux_context, -+ context_list); -+ struct yaffs_dev *dev = dc->dev; -+ -+ if (cmd == 'b') { -+ struct yaffs_block_info *bi; -+ -+ bi = yaffs_get_block_info(dev,p0_val); -+ -+ if(bi) { -+ printk("Block %d: state %d, retire %d, use %d, seq %d\n", -+ (int)p0_val, bi->block_state, -+ bi->needs_retiring, bi->pages_in_use, -+ bi->seq_number); -+ } -+ } else if (cmd == 'c') { -+ struct yaffs_obj *obj; -+ int nand_chunk; -+ -+ obj = yaffs_find_by_number(dev, p0_val); -+ if (!obj) -+ printk("No obj %d\n", (int)p0_val); -+ else { -+ if(p1_val == 0) -+ nand_chunk = obj->hdr_chunk; -+ else -+ nand_chunk = -+ yaffs_find_chunk_in_file(obj, -+ p1_val, NULL); -+ printk("Nand chunk for %d:%d is %d\n", -+ (int)p0_val, (int)p1_val, nand_chunk); -+ } -+ } -+ } -+ -+ mutex_unlock(&yaffs_context_lock); -+ -+ return count; -+} -+ -+static int yaffs_proc_write(struct file *file, const char *buf, -+ unsigned long count, void *data) -+{ -+ if (buf[0] == '.') -+ return yaffs_proc_debug_write(file, buf, count, data); -+ return yaffs_proc_write_trace_options(file, buf, count, data); -+} -+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) */ -+ -+/* Stuff to handle installation of file systems */ -+struct file_system_to_install { -+ struct file_system_type *fst; -+ int installed; -+}; -+ -+static struct file_system_to_install fs_to_install[] = { -+ {&yaffs_fs_type, 0}, -+ {&yaffs2_fs_type, 0}, -+ {NULL, 0} -+}; -+ -+static int __init init_yaffs_fs(void) -+{ -+ int error = 0; -+ struct file_system_to_install *fsinst; -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs built " __DATE__ " " __TIME__ " Installing."); -+ -+ mutex_init(&yaffs_context_lock); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) -+ /* Install the proc_fs entries */ -+ my_proc_entry = create_proc_entry("yaffs", -+ S_IRUGO | S_IFREG, YPROC_ROOT); -+ -+ if (my_proc_entry) { -+ my_proc_entry->write_proc = yaffs_proc_write; -+ my_proc_entry->read_proc = yaffs_proc_read; -+ my_proc_entry->data = NULL; -+ } else { -+ return -ENOMEM; -+ } -+#endif -+ -+ /* Now add the file system entries */ -+ -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst && !error) { -+ error = register_filesystem(fsinst->fst); -+ if (!error) -+ fsinst->installed = 1; -+ fsinst++; -+ } -+ -+ /* Any errors? uninstall */ -+ if (error) { -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst) { -+ if (fsinst->installed) { -+ unregister_filesystem(fsinst->fst); -+ fsinst->installed = 0; -+ } -+ fsinst++; -+ } -+ } -+ -+ return error; -+} -+ -+static void __exit exit_yaffs_fs(void) -+{ -+ -+ struct file_system_to_install *fsinst; -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs built " __DATE__ " " __TIME__ " removing."); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) -+ remove_proc_entry("yaffs", YPROC_ROOT); -+#endif -+ -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst) { -+ if (fsinst->installed) { -+ unregister_filesystem(fsinst->fst); -+ fsinst->installed = 0; -+ } -+ fsinst++; -+ } -+} -+ -+module_init(init_yaffs_fs) -+ module_exit(exit_yaffs_fs) -+ -+ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); -+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); -+MODULE_LICENSE("GPL"); -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.12.20/fs/yaffs2/yaffs_yaffs1.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,422 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_yaffs1.h" -+#include "yportenv.h" -+#include "yaffs_trace.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_nand.h" -+#include "yaffs_attribs.h" -+ -+int yaffs1_scan(struct yaffs_dev *dev) -+{ -+ struct yaffs_ext_tags tags; -+ int blk; -+ int result; -+ int chunk; -+ int c; -+ int deleted; -+ enum yaffs_block_state state; -+ LIST_HEAD(hard_list); -+ struct yaffs_block_info *bi; -+ u32 seq_number; -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_obj *in; -+ struct yaffs_obj *parent; -+ int alloc_failed = 0; -+ struct yaffs_shadow_fixer *shadow_fixers = NULL; -+ u8 *chunk_data; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs1_scan starts intstartblk %d intendblk %d...", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ chunk_data = yaffs_get_temp_buffer(dev); -+ -+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; -+ -+ /* Scan all the blocks to determine their state */ -+ bi = dev->block_info; -+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; -+ blk++) { -+ yaffs_clear_chunk_bits(dev, blk); -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ -+ yaffs_query_init_block_state(dev, blk, &state, &seq_number); -+ -+ bi->block_state = state; -+ bi->seq_number = seq_number; -+ -+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) -+ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, -+ "Block scanning block %d state %d seq %d", -+ blk, state, seq_number); -+ -+ if (state == YAFFS_BLOCK_STATE_DEAD) { -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, -+ "block %d is bad", blk); -+ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += dev->param.chunks_per_block; -+ } -+ bi++; -+ } -+ -+ /* For each block.... */ -+ for (blk = dev->internal_start_block; -+ !alloc_failed && blk <= dev->internal_end_block; blk++) { -+ -+ cond_resched(); -+ -+ bi = yaffs_get_block_info(dev, blk); -+ state = bi->block_state; -+ -+ deleted = 0; -+ -+ /* For each chunk in each block that needs scanning.... */ -+ for (c = 0; -+ !alloc_failed && c < dev->param.chunks_per_block && -+ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { -+ /* Read the tags and decide what to do */ -+ chunk = blk * dev->param.chunks_per_block + c; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, -+ &tags); -+ -+ /* Let's have a good look at this chunk... */ -+ -+ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || -+ tags.is_deleted) { -+ /* YAFFS1 only... -+ * A deleted chunk -+ */ -+ deleted++; -+ dev->n_free_chunks++; -+ } else if (!tags.chunk_used) { -+ /* An unassigned chunk in the block -+ * This means that either the block is empty or -+ * this is the one being allocated from -+ */ -+ -+ if (c == 0) { -+ /* We're looking at the first chunk in -+ *the block so the block is unused */ -+ state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ } else { -+ /* this is the block being allocated */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Allocating from %d %d", -+ blk, c); -+ state = YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->alloc_block = blk; -+ dev->alloc_page = c; -+ dev->alloc_block_finder = blk; -+ -+ } -+ -+ dev->n_free_chunks += -+ (dev->param.chunks_per_block - c); -+ } else if (tags.chunk_id > 0) { -+ /* chunk_id > 0 so it is a data chunk... */ -+ unsigned int endpos; -+ -+ yaffs_set_chunk_bit(dev, blk, c); -+ bi->pages_in_use++; -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ YAFFS_OBJECT_TYPE_FILE); -+ /* PutChunkIntoFile checks for a clash -+ * (two data chunks with the same chunk_id). -+ */ -+ -+ if (!in) -+ alloc_failed = 1; -+ -+ if (in) { -+ if (!yaffs_put_chunk_in_file -+ (in, tags.chunk_id, chunk, 1)) -+ alloc_failed = 1; -+ } -+ -+ endpos = -+ (tags.chunk_id - 1) * -+ dev->data_bytes_per_chunk + -+ tags.n_bytes; -+ if (in && -+ in->variant_type == -+ YAFFS_OBJECT_TYPE_FILE && -+ in->variant.file_variant.scanned_size < -+ endpos) { -+ in->variant.file_variant.scanned_size = -+ endpos; -+ if (!dev->param.use_header_file_size) { -+ in->variant. -+ file_variant.file_size = -+ in->variant. -+ file_variant.scanned_size; -+ } -+ -+ } -+ } else { -+ /* chunk_id == 0, so it is an ObjectHeader. -+ * Make the object -+ */ -+ yaffs_set_chunk_bit(dev, blk, c); -+ bi->pages_in_use++; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, -+ chunk_data, -+ NULL); -+ -+ oh = (struct yaffs_obj_hdr *)chunk_data; -+ -+ in = yaffs_find_by_number(dev, tags.obj_id); -+ if (in && in->variant_type != oh->type) { -+ /* This should not happen, but somehow -+ * Wev'e ended up with an obj_id that -+ * has been reused but not yet deleted, -+ * and worse still it has changed type. -+ * Delete the old object. -+ */ -+ -+ yaffs_del_obj(in); -+ in = NULL; -+ } -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ oh->type); -+ -+ if (!in) -+ alloc_failed = 1; -+ -+ if (in && oh->shadows_obj > 0) { -+ -+ struct yaffs_shadow_fixer *fixer; -+ fixer = -+ kmalloc(sizeof -+ (struct yaffs_shadow_fixer), -+ GFP_NOFS); -+ if (fixer) { -+ fixer->next = shadow_fixers; -+ shadow_fixers = fixer; -+ fixer->obj_id = tags.obj_id; -+ fixer->shadowed_id = -+ oh->shadows_obj; -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Shadow fixer: %d shadows %d", -+ fixer->obj_id, -+ fixer->shadowed_id); -+ -+ } -+ -+ } -+ -+ if (in && in->valid) { -+ /* We have already filled this one. -+ * We have a duplicate and need to -+ * resolve it. */ -+ -+ unsigned existing_serial = in->serial; -+ unsigned new_serial = -+ tags.serial_number; -+ -+ if (((existing_serial + 1) & 3) == -+ new_serial) { -+ /* Use new one - destroy the -+ * exisiting one */ -+ yaffs_chunk_del(dev, -+ in->hdr_chunk, -+ 1, __LINE__); -+ in->valid = 0; -+ } else { -+ /* Use existing - destroy -+ * this one. */ -+ yaffs_chunk_del(dev, chunk, 1, -+ __LINE__); -+ } -+ } -+ -+ if (in && !in->valid && -+ (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == -+ YAFFS_OBJECTID_LOSTNFOUND)) { -+ /* We only load some info, don't fiddle -+ * with directory structure */ -+ in->valid = 1; -+ in->variant_type = oh->type; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->hdr_chunk = chunk; -+ in->serial = tags.serial_number; -+ -+ } else if (in && !in->valid) { -+ /* we need to load this info */ -+ -+ in->valid = 1; -+ in->variant_type = oh->type; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->hdr_chunk = chunk; -+ in->serial = tags.serial_number; -+ -+ yaffs_set_obj_name_from_oh(in, oh); -+ in->dirty = 0; -+ -+ /* directory stuff... -+ * hook up to parent -+ */ -+ -+ parent = -+ yaffs_find_or_create_by_number -+ (dev, oh->parent_obj_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ if (!parent) -+ alloc_failed = 1; -+ if (parent && parent->variant_type == -+ YAFFS_OBJECT_TYPE_UNKNOWN) { -+ /* Set up as a directory */ -+ parent->variant_type = -+ YAFFS_OBJECT_TYPE_DIRECTORY; -+ INIT_LIST_HEAD(&parent-> -+ variant.dir_variant. -+ children); -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ /* Hoosterman, a problem.... -+ * We're trying to use a -+ * non-directory as a directory -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." -+ ); -+ parent = dev->lost_n_found; -+ } -+ -+ yaffs_add_obj_to_dir(parent, in); -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Todo got a problem */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (dev->param. -+ use_header_file_size) -+ in->variant. -+ file_variant.file_size -+ = yaffs_oh_to_size(oh); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ in->variant. -+ hardlink_variant.equiv_id = -+ oh->equiv_id; -+ list_add(&in->hard_links, -+ &hard_list); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ in->variant.symlink_variant. -+ alias = -+ yaffs_clone_str(oh->alias); -+ if (!in->variant. -+ symlink_variant.alias) -+ alloc_failed = 1; -+ break; -+ } -+ } -+ } -+ } -+ -+ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* If we got this far while scanning, -+ * then the block is fully allocated. */ -+ state = YAFFS_BLOCK_STATE_FULL; -+ } -+ -+ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ /* If the block was partially allocated then -+ * treat it as fully allocated. */ -+ state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ -+ bi->block_state = state; -+ -+ /* Now let's see if it was dirty */ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state == YAFFS_BLOCK_STATE_FULL) -+ yaffs_block_became_dirty(dev, blk); -+ } -+ -+ /* Ok, we've done all the scanning. -+ * Fix up the hard link chains. -+ * We should now have scanned all the objects, now it's time to add -+ * these hardlinks. -+ */ -+ -+ yaffs_link_fixup(dev, &hard_list); -+ -+ /* -+ * Fix up any shadowed objects. -+ * There should not be more than one of these. -+ */ -+ { -+ struct yaffs_shadow_fixer *fixer; -+ struct yaffs_obj *obj; -+ -+ while (shadow_fixers) { -+ fixer = shadow_fixers; -+ shadow_fixers = fixer->next; -+ /* Complete the rename transaction by deleting the -+ * shadowed object then setting the object header -+ to unshadowed. -+ */ -+ obj = yaffs_find_by_number(dev, fixer->shadowed_id); -+ if (obj) -+ yaffs_del_obj(obj); -+ -+ obj = yaffs_find_by_number(dev, fixer->obj_id); -+ -+ if (obj) -+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); -+ -+ kfree(fixer); -+ } -+ } -+ -+ yaffs_release_temp_buffer(dev, chunk_data); -+ -+ if (alloc_failed) -+ return YAFFS_FAIL; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.12.20/fs/yaffs2/yaffs_yaffs1.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,22 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_YAFFS1_H__ -+#define __YAFFS_YAFFS1_H__ -+ -+#include "yaffs_guts.h" -+int yaffs1_scan(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.12.20/fs/yaffs2/yaffs_yaffs2.c ---- linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,1534 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yaffs_yaffs2.h" -+#include "yaffs_checkptrw.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_nand.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_verify.h" -+#include "yaffs_attribs.h" -+#include "yaffs_summary.h" -+ -+/* -+ * Checkpoints are really no benefit on very small partitions. -+ * -+ * To save space on small partitions don't bother with checkpoints unless -+ * the partition is at least this big. -+ */ -+#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 -+#define YAFFS_SMALL_HOLE_THRESHOLD 4 -+ -+/* -+ * Oldest Dirty Sequence Number handling. -+ */ -+ -+/* yaffs_calc_oldest_dirty_seq() -+ * yaffs2_find_oldest_dirty_seq() -+ * Calculate the oldest dirty sequence number if we don't know it. -+ */ -+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) -+{ -+ int i; -+ unsigned seq; -+ unsigned block_no = 0; -+ struct yaffs_block_info *b; -+ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ /* Find the oldest dirty sequence number. */ -+ seq = dev->seq_number + 1; -+ b = dev->block_info; -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ if (b->block_state == YAFFS_BLOCK_STATE_FULL && -+ (b->pages_in_use - b->soft_del_pages) < -+ dev->param.chunks_per_block && -+ b->seq_number < seq) { -+ seq = b->seq_number; -+ block_no = i; -+ } -+ b++; -+ } -+ -+ if (block_no) { -+ dev->oldest_dirty_seq = seq; -+ dev->oldest_dirty_block = block_no; -+ } -+} -+ -+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!dev->oldest_dirty_seq) -+ yaffs_calc_oldest_dirty_seq(dev); -+} -+ -+/* -+ * yaffs_clear_oldest_dirty_seq() -+ * Called when a block is erased or marked bad. (ie. when its seq_number -+ * becomes invalid). If the value matches the oldest then we clear -+ * dev->oldest_dirty_seq to force its recomputation. -+ */ -+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi) -+{ -+ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { -+ dev->oldest_dirty_seq = 0; -+ dev->oldest_dirty_block = 0; -+ } -+} -+ -+/* -+ * yaffs2_update_oldest_dirty_seq() -+ * Update the oldest dirty sequence number whenever we dirty a block. -+ * Only do this if the oldest_dirty_seq is actually being tracked. -+ */ -+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, -+ struct yaffs_block_info *bi) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (dev->oldest_dirty_seq) { -+ if (dev->oldest_dirty_seq > bi->seq_number) { -+ dev->oldest_dirty_seq = bi->seq_number; -+ dev->oldest_dirty_block = block_no; -+ } -+ } -+} -+ -+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) -+{ -+ -+ if (!dev->param.is_yaffs2) -+ return 1; /* disqualification only applies to yaffs2. */ -+ -+ if (!bi->has_shrink_hdr) -+ return 1; /* can gc */ -+ -+ yaffs2_find_oldest_dirty_seq(dev); -+ -+ /* Can't do gc of this block if there are any blocks older than this -+ * one that have discarded pages. -+ */ -+ return (bi->seq_number <= dev->oldest_dirty_seq); -+} -+ -+/* -+ * yaffs2_find_refresh_block() -+ * periodically finds the oldest full block by sequence number for refreshing. -+ * Only for yaffs2. -+ */ -+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) -+{ -+ u32 b; -+ u32 oldest = 0; -+ u32 oldest_seq = 0; -+ struct yaffs_block_info *bi; -+ -+ if (!dev->param.is_yaffs2) -+ return oldest; -+ -+ /* -+ * If refresh period < 10 then refreshing is disabled. -+ */ -+ if (dev->param.refresh_period < 10) -+ return oldest; -+ -+ /* -+ * Fix broken values. -+ */ -+ if (dev->refresh_skip > dev->param.refresh_period) -+ dev->refresh_skip = dev->param.refresh_period; -+ -+ if (dev->refresh_skip > 0) -+ return oldest; -+ -+ /* -+ * Refresh skip is now zero. -+ * We'll do a refresh this time around.... -+ * Update the refresh skip and find the oldest block. -+ */ -+ dev->refresh_skip = dev->param.refresh_period; -+ dev->refresh_count++; -+ bi = dev->block_info; -+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { -+ -+ if (oldest < 1 || bi->seq_number < oldest_seq) { -+ oldest = b; -+ oldest_seq = bi->seq_number; -+ } -+ } -+ bi++; -+ } -+ -+ if (oldest > 0) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC refresh count %d selected block %d with seq_number %d", -+ dev->refresh_count, oldest, oldest_seq); -+ } -+ -+ return oldest; -+} -+ -+int yaffs2_checkpt_required(struct yaffs_dev *dev) -+{ -+ int nblocks; -+ -+ if (!dev->param.is_yaffs2) -+ return 0; -+ -+ nblocks = dev->internal_end_block - dev->internal_start_block + 1; -+ -+ return !dev->param.skip_checkpt_wr && -+ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); -+} -+ -+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) -+{ -+ int retval; -+ int n_bytes = 0; -+ int n_blocks; -+ int dev_blocks; -+ -+ if (!dev->param.is_yaffs2) -+ return 0; -+ -+ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { -+ /* Not a valid value so recalculate */ -+ dev_blocks = dev->param.end_block - dev->param.start_block + 1; -+ n_bytes += sizeof(struct yaffs_checkpt_validity); -+ n_bytes += sizeof(struct yaffs_checkpt_dev); -+ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); -+ n_bytes += dev_blocks * dev->chunk_bit_stride; -+ n_bytes += -+ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * -+ dev->n_obj; -+ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; -+ n_bytes += sizeof(struct yaffs_checkpt_validity); -+ n_bytes += sizeof(u32); /* checksum */ -+ -+ /* Round up and add 2 blocks to allow for some bad blocks, -+ * so add 3 */ -+ -+ n_blocks = -+ (n_bytes / -+ (dev->data_bytes_per_chunk * -+ dev->param.chunks_per_block)) + 3; -+ -+ dev->checkpoint_blocks_required = n_blocks; -+ } -+ -+ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; -+ if (retval < 0) -+ retval = 0; -+ return retval; -+} -+ -+/*--------------------- Checkpointing --------------------*/ -+ -+static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) -+{ -+ struct yaffs_checkpt_validity cp; -+ -+ memset(&cp, 0, sizeof(cp)); -+ -+ cp.struct_type = sizeof(cp); -+ cp.magic = YAFFS_MAGIC; -+ cp.version = YAFFS_CHECKPOINT_VERSION; -+ cp.head = (head) ? 1 : 0; -+ -+ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) -+{ -+ struct yaffs_checkpt_validity cp; -+ int ok; -+ -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ -+ if (ok) -+ ok = (cp.struct_type == sizeof(cp)) && -+ (cp.magic == YAFFS_MAGIC) && -+ (cp.version == YAFFS_CHECKPOINT_VERSION) && -+ (cp.head == ((head) ? 1 : 0)); -+ return ok ? 1 : 0; -+} -+ -+static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, -+ struct yaffs_dev *dev) -+{ -+ cp->n_erased_blocks = dev->n_erased_blocks; -+ cp->alloc_block = dev->alloc_block; -+ cp->alloc_page = dev->alloc_page; -+ cp->n_free_chunks = dev->n_free_chunks; -+ -+ cp->n_deleted_files = dev->n_deleted_files; -+ cp->n_unlinked_files = dev->n_unlinked_files; -+ cp->n_bg_deletions = dev->n_bg_deletions; -+ cp->seq_number = dev->seq_number; -+ -+} -+ -+static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, -+ struct yaffs_checkpt_dev *cp) -+{ -+ dev->n_erased_blocks = cp->n_erased_blocks; -+ dev->alloc_block = cp->alloc_block; -+ dev->alloc_page = cp->alloc_page; -+ dev->n_free_chunks = cp->n_free_chunks; -+ -+ dev->n_deleted_files = cp->n_deleted_files; -+ dev->n_unlinked_files = cp->n_unlinked_files; -+ dev->n_bg_deletions = cp->n_bg_deletions; -+ dev->seq_number = cp->seq_number; -+} -+ -+static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_dev cp; -+ u32 n_bytes; -+ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ int ok; -+ -+ /* Write device runtime values */ -+ yaffs2_dev_to_checkpt_dev(&cp, dev); -+ cp.struct_type = sizeof(cp); -+ -+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (!ok) -+ return 0; -+ -+ /* Write block info */ -+ n_bytes = n_blocks * sizeof(struct yaffs_block_info); -+ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); -+ if (!ok) -+ return 0; -+ -+ /* Write chunk bits */ -+ n_bytes = n_blocks * dev->chunk_bit_stride; -+ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_dev cp; -+ u32 n_bytes; -+ u32 n_blocks = -+ (dev->internal_end_block - dev->internal_start_block + 1); -+ int ok; -+ -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (!ok) -+ return 0; -+ -+ if (cp.struct_type != sizeof(cp)) -+ return 0; -+ -+ yaffs_checkpt_dev_to_dev(dev, &cp); -+ -+ n_bytes = n_blocks * sizeof(struct yaffs_block_info); -+ -+ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); -+ -+ if (!ok) -+ return 0; -+ -+ n_bytes = n_blocks * dev->chunk_bit_stride; -+ -+ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); -+ -+ return ok ? 1 : 0; -+} -+ -+static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, -+ struct yaffs_obj *obj) -+{ -+ cp->obj_id = obj->obj_id; -+ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; -+ cp->hdr_chunk = obj->hdr_chunk; -+ cp->variant_type = obj->variant_type; -+ cp->deleted = obj->deleted; -+ cp->soft_del = obj->soft_del; -+ cp->unlinked = obj->unlinked; -+ cp->fake = obj->fake; -+ cp->rename_allowed = obj->rename_allowed; -+ cp->unlink_allowed = obj->unlink_allowed; -+ cp->serial = obj->serial; -+ cp->n_data_chunks = obj->n_data_chunks; -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; -+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; -+} -+ -+static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, -+ struct yaffs_checkpt_obj *cp) -+{ -+ struct yaffs_obj *parent; -+ -+ if (obj->variant_type != cp->variant_type) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", -+ cp->obj_id, cp->variant_type, cp->hdr_chunk, -+ obj->variant_type); -+ return 0; -+ } -+ -+ obj->obj_id = cp->obj_id; -+ -+ if (cp->parent_id) -+ parent = yaffs_find_or_create_by_number(obj->my_dev, -+ cp->parent_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ else -+ parent = NULL; -+ -+ if (parent) { -+ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", -+ cp->obj_id, cp->parent_id, -+ cp->variant_type, cp->hdr_chunk, -+ parent->variant_type); -+ return 0; -+ } -+ yaffs_add_obj_to_dir(parent, obj); -+ } -+ -+ obj->hdr_chunk = cp->hdr_chunk; -+ obj->variant_type = cp->variant_type; -+ obj->deleted = cp->deleted; -+ obj->soft_del = cp->soft_del; -+ obj->unlinked = cp->unlinked; -+ obj->fake = cp->fake; -+ obj->rename_allowed = cp->rename_allowed; -+ obj->unlink_allowed = cp->unlink_allowed; -+ obj->serial = cp->serial; -+ obj->n_data_chunks = cp->n_data_chunks; -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; -+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; -+ -+ if (obj->hdr_chunk > 0) -+ obj->lazy_loaded = 1; -+ return 1; -+} -+ -+static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, -+ struct yaffs_tnode *tn, u32 level, -+ int chunk_offset) -+{ -+ int i; -+ struct yaffs_dev *dev = in->my_dev; -+ int ok = 1; -+ u32 base_offset; -+ -+ if (!tn) -+ return 1; -+ -+ if (level > 0) { -+ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { -+ if (!tn->internal[i]) -+ continue; -+ ok = yaffs2_checkpt_tnode_worker(in, -+ tn->internal[i], -+ level - 1, -+ (chunk_offset << -+ YAFFS_TNODES_INTERNAL_BITS) + i); -+ } -+ return ok; -+ } -+ -+ /* Level 0 tnode */ -+ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; -+ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == -+ sizeof(base_offset)); -+ if (ok) -+ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == -+ dev->tnode_size); -+ -+ return ok; -+} -+ -+static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) -+{ -+ u32 end_marker = ~0; -+ int ok = 1; -+ -+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return ok; -+ -+ ok = yaffs2_checkpt_tnode_worker(obj, -+ obj->variant.file_variant.top, -+ obj->variant.file_variant. -+ top_level, 0); -+ if (ok) -+ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, -+ sizeof(end_marker)) == sizeof(end_marker)); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) -+{ -+ u32 base_chunk; -+ int ok = 1; -+ struct yaffs_dev *dev = obj->my_dev; -+ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; -+ struct yaffs_tnode *tn; -+ int nread = 0; -+ -+ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == -+ sizeof(base_chunk)); -+ -+ while (ok && (~base_chunk)) { -+ nread++; -+ /* Read level 0 tnode */ -+ -+ tn = yaffs_get_tnode(dev); -+ if (tn) -+ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == -+ dev->tnode_size); -+ else -+ ok = 0; -+ -+ if (tn && ok) -+ ok = yaffs_add_find_tnode_0(dev, -+ file_stuct_ptr, -+ base_chunk, tn) ? 1 : 0; -+ -+ if (ok) -+ ok = (yaffs2_checkpt_rd -+ (dev, &base_chunk, -+ sizeof(base_chunk)) == sizeof(base_chunk)); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint read tnodes %d records, last %d. ok %d", -+ nread, base_chunk, ok); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_checkpt_obj cp; -+ int i; -+ int ok = 1; -+ struct list_head *lh; -+ -+ /* Iterate through the objects in each hash entry, -+ * dumping them to the checkpointing stream. -+ */ -+ -+ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each(lh, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ if (!obj->defered_free) { -+ yaffs2_obj_checkpt_obj(&cp, obj); -+ cp.struct_type = sizeof(cp); -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", -+ cp.obj_id, cp.parent_id, -+ cp.variant_type, cp.hdr_chunk, obj); -+ -+ ok = (yaffs2_checkpt_wr(dev, &cp, -+ sizeof(cp)) == sizeof(cp)); -+ -+ if (ok && -+ obj->variant_type == -+ YAFFS_OBJECT_TYPE_FILE) -+ ok = yaffs2_wr_checkpt_tnodes(obj); -+ } -+ } -+ } -+ -+ /* Dump end of list */ -+ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); -+ cp.struct_type = sizeof(cp); -+ -+ if (ok) -+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_checkpt_obj cp; -+ int ok = 1; -+ int done = 0; -+ LIST_HEAD(hard_list); -+ -+ -+ while (ok && !done) { -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (cp.struct_type != sizeof(cp)) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "struct size %d instead of %d ok %d", -+ cp.struct_type, (int)sizeof(cp), ok); -+ ok = 0; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint read object %d parent %d type %d chunk %d ", -+ cp.obj_id, cp.parent_id, cp.variant_type, -+ cp.hdr_chunk); -+ -+ if (ok && cp.obj_id == ~0) { -+ done = 1; -+ } else if (ok) { -+ obj = -+ yaffs_find_or_create_by_number(dev, cp.obj_id, -+ cp.variant_type); -+ if (obj) { -+ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); -+ if (!ok) -+ break; -+ if (obj->variant_type == -+ YAFFS_OBJECT_TYPE_FILE) { -+ ok = yaffs2_rd_checkpt_tnodes(obj); -+ } else if (obj->variant_type == -+ YAFFS_OBJECT_TYPE_HARDLINK) { -+ list_add(&obj->hard_links, &hard_list); -+ } -+ } else { -+ ok = 0; -+ } -+ } -+ } -+ -+ if (ok) -+ yaffs_link_fixup(dev, &hard_list); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) -+{ -+ u32 checkpt_sum; -+ int ok; -+ -+ yaffs2_get_checkpt_sum(dev, &checkpt_sum); -+ -+ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == -+ sizeof(checkpt_sum)); -+ -+ if (!ok) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) -+{ -+ u32 checkpt_sum0; -+ u32 checkpt_sum1; -+ int ok; -+ -+ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); -+ -+ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == -+ sizeof(checkpt_sum1)); -+ -+ if (!ok) -+ return 0; -+ -+ if (checkpt_sum0 != checkpt_sum1) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) -+{ -+ int ok = 1; -+ -+ if (!yaffs2_checkpt_required(dev)) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "skipping checkpoint write"); -+ ok = 0; -+ } -+ -+ if (ok) -+ ok = yaffs2_checkpt_open(dev, 1); -+ -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint validity"); -+ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint device"); -+ ok = yaffs2_wr_checkpt_dev(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint objects"); -+ ok = yaffs2_wr_checkpt_objs(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint validity"); -+ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); -+ } -+ -+ if (ok) -+ ok = yaffs2_wr_checkpt_sum(dev); -+ -+ if (!yaffs_checkpt_close(dev)) -+ ok = 0; -+ -+ if (ok) -+ dev->is_checkpointed = 1; -+ else -+ dev->is_checkpointed = 0; -+ -+ return dev->is_checkpointed; -+} -+ -+static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) -+{ -+ int ok = 1; -+ -+ if (!dev->param.is_yaffs2) -+ ok = 0; -+ -+ if (ok && dev->param.skip_checkpt_rd) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "skipping checkpoint read"); -+ ok = 0; -+ } -+ -+ if (ok) -+ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ -+ -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint validity"); -+ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint device"); -+ ok = yaffs2_rd_checkpt_dev(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint objects"); -+ ok = yaffs2_rd_checkpt_objs(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint validity"); -+ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); -+ } -+ -+ if (ok) { -+ ok = yaffs2_rd_checkpt_sum(dev); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint checksum %d", ok); -+ } -+ -+ if (!yaffs_checkpt_close(dev)) -+ ok = 0; -+ -+ if (ok) -+ dev->is_checkpointed = 1; -+ else -+ dev->is_checkpointed = 0; -+ -+ return ok ? 1 : 0; -+} -+ -+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) -+{ -+ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { -+ dev->is_checkpointed = 0; -+ yaffs2_checkpt_invalidate_stream(dev); -+ } -+ if (dev->param.sb_dirty_fn) -+ dev->param.sb_dirty_fn(dev); -+} -+ -+int yaffs_checkpoint_save(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "save entry: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ yaffs_verify_objects(dev); -+ yaffs_verify_blocks(dev); -+ yaffs_verify_free_chunks(dev); -+ -+ if (!dev->is_checkpointed) { -+ yaffs2_checkpt_invalidate(dev); -+ yaffs2_wr_checkpt_data(dev); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, -+ "save exit: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ return dev->is_checkpointed; -+} -+ -+int yaffs2_checkpt_restore(struct yaffs_dev *dev) -+{ -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "restore entry: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ retval = yaffs2_rd_checkpt_data(dev); -+ -+ if (dev->is_checkpointed) { -+ yaffs_verify_objects(dev); -+ yaffs_verify_blocks(dev); -+ yaffs_verify_free_chunks(dev); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "restore exit: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ return retval; -+} -+ -+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) -+{ -+ /* if new_size > old_file_size. -+ * We're going to be writing a hole. -+ * If the hole is small then write zeros otherwise write a start -+ * of hole marker. -+ */ -+ loff_t old_file_size; -+ loff_t increase; -+ int small_hole; -+ int result = YAFFS_OK; -+ struct yaffs_dev *dev = NULL; -+ u8 *local_buffer = NULL; -+ int small_increase_ok = 0; -+ -+ if (!obj) -+ return YAFFS_FAIL; -+ -+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return YAFFS_FAIL; -+ -+ dev = obj->my_dev; -+ -+ /* Bail out if not yaffs2 mode */ -+ if (!dev->param.is_yaffs2) -+ return YAFFS_OK; -+ -+ old_file_size = obj->variant.file_variant.file_size; -+ -+ if (new_size <= old_file_size) -+ return YAFFS_OK; -+ -+ increase = new_size - old_file_size; -+ -+ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && -+ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) -+ small_hole = 1; -+ else -+ small_hole = 0; -+ -+ if (small_hole) -+ local_buffer = yaffs_get_temp_buffer(dev); -+ -+ if (local_buffer) { -+ /* fill hole with zero bytes */ -+ loff_t pos = old_file_size; -+ int this_write; -+ int written; -+ memset(local_buffer, 0, dev->data_bytes_per_chunk); -+ small_increase_ok = 1; -+ -+ while (increase > 0 && small_increase_ok) { -+ this_write = increase; -+ if (this_write > dev->data_bytes_per_chunk) -+ this_write = dev->data_bytes_per_chunk; -+ written = -+ yaffs_do_file_wr(obj, local_buffer, pos, this_write, -+ 0); -+ if (written == this_write) { -+ pos += this_write; -+ increase -= this_write; -+ } else { -+ small_increase_ok = 0; -+ } -+ } -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ -+ /* If out of space then reverse any chunks we've added */ -+ if (!small_increase_ok) -+ yaffs_resize_file_down(obj, old_file_size); -+ } -+ -+ if (!small_increase_ok && -+ obj->parent && -+ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && -+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { -+ /* Write a hole start header with the old file size */ -+ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); -+ } -+ -+ return result; -+} -+ -+struct yaffs_block_index { -+ int seq; -+ int block; -+}; -+ -+static int yaffs2_ybicmp(const void *a, const void *b) -+{ -+ int aseq = ((struct yaffs_block_index *)a)->seq; -+ int bseq = ((struct yaffs_block_index *)b)->seq; -+ int ablock = ((struct yaffs_block_index *)a)->block; -+ int bblock = ((struct yaffs_block_index *)b)->block; -+ -+ if (aseq == bseq) -+ return ablock - bblock; -+ -+ return aseq - bseq; -+} -+ -+static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, -+ int blk, int chunk_in_block, -+ int *found_chunks, -+ u8 *chunk_data, -+ struct list_head *hard_list, -+ int summary_available) -+{ -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_obj *in; -+ struct yaffs_obj *parent; -+ int equiv_id; -+ loff_t file_size; -+ int is_shrink; -+ int is_unlinked; -+ struct yaffs_ext_tags tags; -+ int result; -+ int alloc_failed = 0; -+ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; -+ struct yaffs_file_var *file_var; -+ struct yaffs_hardlink_var *hl_var; -+ struct yaffs_symlink_var *sl_var; -+ -+ if (summary_available) { -+ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); -+ tags.seq_number = bi->seq_number; -+ } -+ -+ if (!summary_available || tags.obj_id == 0) { -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); -+ dev->tags_used++; -+ } else { -+ dev->summary_used++; -+ } -+ -+ /* Let's have a good look at this chunk... */ -+ -+ if (!tags.chunk_used) { -+ /* An unassigned chunk in the block. -+ * If there are used chunks after this one, then -+ * it is a chunk that was skipped due to failing -+ * the erased check. Just skip it so that it can -+ * be deleted. -+ * But, more typically, We get here when this is -+ * an unallocated chunk and his means that -+ * either the block is empty or this is the one -+ * being allocated from -+ */ -+ -+ if (*found_chunks) { -+ /* This is a chunk that was skipped due -+ * to failing the erased check */ -+ } else if (chunk_in_block == 0) { -+ /* We're looking at the first chunk in -+ * the block so the block is unused */ -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ } else { -+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ if (dev->seq_number == bi->seq_number) { -+ /* Allocating from this block*/ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Allocating from %d %d", -+ blk, chunk_in_block); -+ -+ bi->block_state = -+ YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->alloc_block = blk; -+ dev->alloc_page = chunk_in_block; -+ dev->alloc_block_finder = blk; -+ } else { -+ /* This is a partially written block -+ * that is not the current -+ * allocation block. -+ */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Partially written block %d detected. gc will fix this.", -+ blk); -+ } -+ } -+ } -+ -+ dev->n_free_chunks++; -+ -+ } else if (tags.ecc_result == -+ YAFFS_ECC_RESULT_UNFIXED) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Unfixed ECC in chunk(%d:%d), chunk ignored", -+ blk, chunk_in_block); -+ dev->n_free_chunks++; -+ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || -+ tags.chunk_id > YAFFS_MAX_CHUNK_ID || -+ tags.obj_id == YAFFS_OBJECTID_SUMMARY || -+ (tags.chunk_id > 0 && -+ tags.n_bytes > dev->data_bytes_per_chunk) || -+ tags.seq_number != bi->seq_number) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", -+ blk, chunk_in_block, tags.obj_id, -+ tags.chunk_id, tags.n_bytes); -+ dev->n_free_chunks++; -+ } else if (tags.chunk_id > 0) { -+ /* chunk_id > 0 so it is a data chunk... */ -+ loff_t endpos; -+ loff_t chunk_base = (tags.chunk_id - 1) * -+ dev->data_bytes_per_chunk; -+ -+ *found_chunks = 1; -+ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ YAFFS_OBJECT_TYPE_FILE); -+ if (!in) -+ /* Out of memory */ -+ alloc_failed = 1; -+ -+ if (in && -+ in->variant_type == YAFFS_OBJECT_TYPE_FILE && -+ chunk_base < in->variant.file_variant.shrink_size) { -+ /* This has not been invalidated by -+ * a resize */ -+ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, -+ chunk, -1)) -+ alloc_failed = 1; -+ -+ /* File size is calculated by looking at -+ * the data chunks if we have not -+ * seen an object header yet. -+ * Stop this practice once we find an -+ * object header. -+ */ -+ endpos = chunk_base + tags.n_bytes; -+ -+ if (!in->valid && -+ in->variant.file_variant.scanned_size < endpos) { -+ in->variant.file_variant. -+ scanned_size = endpos; -+ in->variant.file_variant. -+ file_size = endpos; -+ } -+ } else if (in) { -+ /* This chunk has been invalidated by a -+ * resize, or a past file deletion -+ * so delete the chunk*/ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ } -+ } else { -+ /* chunk_id == 0, so it is an ObjectHeader. -+ * Thus, we read in the object header and make -+ * the object -+ */ -+ *found_chunks = 1; -+ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ -+ oh = NULL; -+ in = NULL; -+ -+ if (tags.extra_available) { -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ tags.extra_obj_type); -+ if (!in) -+ alloc_failed = 1; -+ } -+ -+ if (!in || -+ (!in->valid && dev->param.disable_lazy_load) || -+ tags.extra_shadows || -+ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { -+ -+ /* If we don't have valid info then we -+ * need to read the chunk -+ * TODO In future we can probably defer -+ * reading the chunk and living with -+ * invalid data until needed. -+ */ -+ -+ result = yaffs_rd_chunk_tags_nand(dev, -+ chunk, -+ chunk_data, -+ NULL); -+ -+ oh = (struct yaffs_obj_hdr *)chunk_data; -+ -+ if (dev->param.inband_tags) { -+ /* Fix up the header if they got -+ * corrupted by inband tags */ -+ oh->shadows_obj = -+ oh->inband_shadowed_obj_id; -+ oh->is_shrink = -+ oh->inband_is_shrink; -+ } -+ -+ if (!in) { -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, oh->type); -+ if (!in) -+ alloc_failed = 1; -+ } -+ } -+ -+ if (!in) { -+ /* TODO Hoosterman we have a problem! */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", -+ tags.obj_id, chunk); -+ return YAFFS_FAIL; -+ } -+ -+ if (in->valid) { -+ /* We have already filled this one. -+ * We have a duplicate that will be -+ * discarded, but we first have to suck -+ * out resize info if it is a file. -+ */ -+ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && -+ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || -+ (tags.extra_available && -+ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) -+ )) { -+ loff_t this_size = (oh) ? -+ yaffs_oh_to_size(oh) : -+ tags.extra_file_size; -+ u32 parent_obj_id = (oh) ? -+ oh->parent_obj_id : -+ tags.extra_parent_id; -+ -+ is_shrink = (oh) ? -+ oh->is_shrink : -+ tags.extra_is_shrink; -+ -+ /* If it is deleted (unlinked -+ * at start also means deleted) -+ * we treat the file size as -+ * being zeroed at this point. -+ */ -+ if (parent_obj_id == YAFFS_OBJECTID_DELETED || -+ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { -+ this_size = 0; -+ is_shrink = 1; -+ } -+ -+ if (is_shrink && -+ in->variant.file_variant.shrink_size > -+ this_size) -+ in->variant.file_variant.shrink_size = -+ this_size; -+ -+ if (is_shrink) -+ bi->has_shrink_hdr = 1; -+ } -+ /* Use existing - destroy this one. */ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ } -+ -+ if (!in->valid && in->variant_type != -+ (oh ? oh->type : tags.extra_obj_type)) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", -+ oh ? oh->type : tags.extra_obj_type, -+ in->variant_type, tags.obj_id, -+ chunk); -+ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); -+ } -+ -+ if (!in->valid && -+ (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { -+ /* We only load some info, don't fiddle -+ * with directory structure */ -+ in->valid = 1; -+ -+ if (oh) { -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->lazy_loaded = 0; -+ } else { -+ in->lazy_loaded = 1; -+ } -+ in->hdr_chunk = chunk; -+ -+ } else if (!in->valid) { -+ /* we need to load this info */ -+ in->valid = 1; -+ in->hdr_chunk = chunk; -+ if (oh) { -+ in->variant_type = oh->type; -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ -+ if (oh->shadows_obj > 0) -+ yaffs_handle_shadowed_obj(dev, -+ oh->shadows_obj, 1); -+ -+ yaffs_set_obj_name_from_oh(in, oh); -+ parent = yaffs_find_or_create_by_number(dev, -+ oh->parent_obj_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ file_size = yaffs_oh_to_size(oh); -+ is_shrink = oh->is_shrink; -+ equiv_id = oh->equiv_id; -+ } else { -+ in->variant_type = tags.extra_obj_type; -+ parent = yaffs_find_or_create_by_number(dev, -+ tags.extra_parent_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ file_size = tags.extra_file_size; -+ is_shrink = tags.extra_is_shrink; -+ equiv_id = tags.extra_equiv_id; -+ in->lazy_loaded = 1; -+ } -+ in->dirty = 0; -+ -+ if (!parent) -+ alloc_failed = 1; -+ -+ /* directory stuff... -+ * hook up to parent -+ */ -+ -+ if (parent && -+ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { -+ /* Set up as a directory */ -+ parent->variant_type = -+ YAFFS_OBJECT_TYPE_DIRECTORY; -+ INIT_LIST_HEAD(&parent-> -+ variant.dir_variant.children); -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ /* Hoosterman, another problem.... -+ * Trying to use a non-directory as a directory -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." -+ ); -+ parent = dev->lost_n_found; -+ } -+ yaffs_add_obj_to_dir(parent, in); -+ -+ is_unlinked = (parent == dev->del_dir) || -+ (parent == dev->unlinked_dir); -+ -+ if (is_shrink) -+ /* Mark the block */ -+ bi->has_shrink_hdr = 1; -+ -+ /* Note re hardlinks. -+ * Since we might scan a hardlink before its equivalent -+ * object is scanned we put them all in a list. -+ * After scanning is complete, we should have all the -+ * objects, so we run through this list and fix up all -+ * the chains. -+ */ -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Todo got a problem */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ file_var = &in->variant.file_variant; -+ if (file_var->scanned_size < file_size) { -+ /* This covers the case where the file -+ * size is greater than the data held. -+ * This will happen if the file is -+ * resized to be larger than its -+ * current data extents. -+ */ -+ file_var->file_size = file_size; -+ file_var->scanned_size = file_size; -+ } -+ -+ if (file_var->shrink_size > file_size) -+ file_var->shrink_size = file_size; -+ -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ hl_var = &in->variant.hardlink_variant; -+ if (!is_unlinked) { -+ hl_var->equiv_id = equiv_id; -+ list_add(&in->hard_links, hard_list); -+ } -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ sl_var = &in->variant.symlink_variant; -+ if (oh) { -+ sl_var->alias = -+ yaffs_clone_str(oh->alias); -+ if (!sl_var->alias) -+ alloc_failed = 1; -+ } -+ break; -+ } -+ } -+ } -+ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+int yaffs2_scan_backwards(struct yaffs_dev *dev) -+{ -+ int blk; -+ int block_iter; -+ int start_iter; -+ int end_iter; -+ int n_to_scan = 0; -+ enum yaffs_block_state state; -+ int c; -+ int deleted; -+ LIST_HEAD(hard_list); -+ struct yaffs_block_info *bi; -+ u32 seq_number; -+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ u8 *chunk_data; -+ int found_chunks; -+ int alloc_failed = 0; -+ struct yaffs_block_index *block_index = NULL; -+ int alt_block_index = 0; -+ int summary_available; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; -+ -+ block_index = -+ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); -+ -+ if (!block_index) { -+ block_index = -+ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); -+ alt_block_index = 1; -+ } -+ -+ if (!block_index) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs2_scan_backwards() could not allocate block index!" -+ ); -+ return YAFFS_FAIL; -+ } -+ -+ dev->blocks_in_checkpt = 0; -+ -+ chunk_data = yaffs_get_temp_buffer(dev); -+ -+ /* Scan all the blocks to determine their state */ -+ bi = dev->block_info; -+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; -+ blk++) { -+ yaffs_clear_chunk_bits(dev, blk); -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ -+ yaffs_query_init_block_state(dev, blk, &state, &seq_number); -+ -+ bi->block_state = state; -+ bi->seq_number = seq_number; -+ -+ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, -+ "Block scanning block %d state %d seq %d", -+ blk, bi->block_state, seq_number); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { -+ dev->blocks_in_checkpt++; -+ -+ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, -+ "block %d is bad", blk); -+ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += dev->param.chunks_per_block; -+ } else if (bi->block_state == -+ YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* Determine the highest sequence number */ -+ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && -+ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { -+ block_index[n_to_scan].seq = seq_number; -+ block_index[n_to_scan].block = blk; -+ n_to_scan++; -+ if (seq_number >= dev->seq_number) -+ dev->seq_number = seq_number; -+ } else { -+ /* TODO: Nasty sequence number! */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Block scanning block %d has bad sequence number %d", -+ blk, seq_number); -+ } -+ } -+ bi++; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); -+ -+ cond_resched(); -+ -+ /* Sort the blocks by sequence number */ -+ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), -+ yaffs2_ybicmp, NULL); -+ -+ cond_resched(); -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); -+ -+ /* Now scan the blocks looking at the data. */ -+ start_iter = 0; -+ end_iter = n_to_scan - 1; -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); -+ -+ /* For each block.... backwards */ -+ for (block_iter = end_iter; -+ !alloc_failed && block_iter >= start_iter; -+ block_iter--) { -+ /* Cooperative multitasking! This loop can run for so -+ long that watchdog timers expire. */ -+ cond_resched(); -+ -+ /* get the block to scan in the correct order */ -+ blk = block_index[block_iter].block; -+ bi = yaffs_get_block_info(dev, blk); -+ deleted = 0; -+ -+ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); -+ -+ /* For each chunk in each block that needs scanning.... */ -+ found_chunks = 0; -+ if (summary_available) -+ c = dev->chunks_per_summary - 1; -+ else -+ c = dev->param.chunks_per_block - 1; -+ -+ for (/* c is already initialised */; -+ !alloc_failed && c >= 0 && -+ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); -+ c--) { -+ /* Scan backwards... -+ * Read the tags and decide what to do -+ */ -+ if (yaffs2_scan_chunk(dev, bi, blk, c, -+ &found_chunks, chunk_data, -+ &hard_list, summary_available) == -+ YAFFS_FAIL) -+ alloc_failed = 1; -+ } -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* If we got this far while scanning, then the block -+ * is fully allocated. */ -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ } -+ -+ /* Now let's see if it was dirty */ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state == YAFFS_BLOCK_STATE_FULL) { -+ yaffs_block_became_dirty(dev, blk); -+ } -+ } -+ -+ yaffs_skip_rest_of_block(dev); -+ -+ if (alt_block_index) -+ vfree(block_index); -+ else -+ kfree(block_index); -+ -+ /* Ok, we've done all the scanning. -+ * Fix up the hard link chains. -+ * We have scanned all the objects, now it's time to add these -+ * hardlinks. -+ */ -+ yaffs_link_fixup(dev, &hard_list); -+ -+ yaffs_release_temp_buffer(dev, chunk_data); -+ -+ if (alloc_failed) -+ return YAFFS_FAIL; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.12.20/fs/yaffs2/yaffs_yaffs2.h ---- linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_YAFFS2_H__ -+#define __YAFFS_YAFFS2_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); -+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); -+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi); -+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, -+ struct yaffs_block_info *bi); -+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); -+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); -+int yaffs2_checkpt_required(struct yaffs_dev *dev); -+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); -+ -+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); -+int yaffs2_checkpt_save(struct yaffs_dev *dev); -+int yaffs2_checkpt_restore(struct yaffs_dev *dev); -+ -+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); -+int yaffs2_scan_backwards(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.12.20.orig/fs/yaffs2/yportenv.h linux-3.12.20/fs/yaffs2/yportenv.h ---- linux-3.12.20.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.12.20/fs/yaffs2/yportenv.h 2014-05-17 11:28:49.000000000 +0200 -@@ -0,0 +1,85 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YPORTENV_H__ -+#define __YPORTENV_H__ -+ -+/* -+ * Define the MTD version in terms of Linux Kernel versions -+ * This allows yaffs to be used independantly of the kernel -+ * as well as with it. -+ */ -+ -+#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) -+ -+#ifdef YAFFS_OUT_OF_TREE -+#include "moduleconfig.h" -+#endif -+ -+#include -+#define MTD_VERSION_CODE LINUX_VERSION_CODE -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* These type wrappings are used to support Unicode names in WinCE. */ -+#define YCHAR char -+#define YUCHAR unsigned char -+#define _Y(x) x -+ -+#define YAFFS_LOSTNFOUND_NAME "lost+found" -+#define YAFFS_LOSTNFOUND_PREFIX "obj" -+ -+ -+#define YAFFS_ROOT_MODE 0755 -+#define YAFFS_LOSTNFOUND_MODE 0700 -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+#define Y_CURRENT_TIME CURRENT_TIME.tv_sec -+#define Y_TIME_CONVERT(x) (x).tv_sec -+#else -+#define Y_CURRENT_TIME CURRENT_TIME -+#define Y_TIME_CONVERT(x) (x) -+#endif -+ -+#define compile_time_assertion(assertion) \ -+ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) -+ -+ -+#define yaffs_printf(msk, fmt, ...) \ -+ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) -+ -+#define yaffs_trace(msk, fmt, ...) do { \ -+ if (yaffs_trace_mask & (msk)) \ -+ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ -+} while (0) -+ -+ -+#endif diff --git a/target/linux/patches/3.12.40/zlib-inflate.patch b/target/linux/patches/3.12.40/zlib-inflate.patch deleted file mode 100644 index 58e1f6d21..000000000 --- a/target/linux/patches/3.12.40/zlib-inflate.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-2.6.37.orig/lib/Kconfig linux-2.6.37/lib/Kconfig ---- linux-2.6.37.orig/lib/Kconfig 2011-01-05 01:50:19.000000000 +0100 -+++ linux-2.6.37/lib/Kconfig 2011-03-01 20:10:29.833370667 +0100 -@@ -95,7 +95,7 @@ - # compression support is select'ed if needed - # - config ZLIB_INFLATE -- tristate -+ boolean - - config ZLIB_DEFLATE - tristate diff --git a/target/linux/patches/3.12.44/bsd-compatibility.patch b/target/linux/patches/3.12.44/bsd-compatibility.patch new file mode 100644 index 000000000..b954b658f --- /dev/null +++ b/target/linux/patches/3.12.44/bsd-compatibility.patch @@ -0,0 +1,2538 @@ +diff -Nur linux-3.11.5.orig/scripts/Makefile.lib linux-3.11.5/scripts/Makefile.lib +--- linux-3.11.5.orig/scripts/Makefile.lib 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/Makefile.lib 2013-10-16 18:09:31.000000000 +0200 +@@ -281,7 +281,12 @@ + size_append = printf $(shell \ + dec_size=0; \ + for F in $1; do \ +- fsize=$$(stat -c "%s" $$F); \ ++ if stat -qs .>/dev/null 2>&1; then \ ++ statcmd='stat -f %z'; \ ++ else \ ++ statcmd='stat -c %s'; \ ++ fi; \ ++ fsize=$$($$statcmd $$F); \ + dec_size=$$(expr $$dec_size + $$fsize); \ + done; \ + printf "%08x\n" $$dec_size | \ +diff -Nur linux-3.11.5.orig/scripts/mod/mk_elfconfig.c linux-3.11.5/scripts/mod/mk_elfconfig.c +--- linux-3.11.5.orig/scripts/mod/mk_elfconfig.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/mk_elfconfig.c 2013-10-16 18:09:31.000000000 +0200 +@@ -1,7 +1,18 @@ + #include + #include + #include +-#include ++ ++#define EI_NIDENT (16) ++#define ELFMAG "\177ELF" ++ ++#define SELFMAG 4 ++#define EI_CLASS 4 ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ + + int + main(int argc, char **argv) +diff -Nur linux-3.11.5.orig/scripts/mod/modpost.h linux-3.11.5/scripts/mod/modpost.h +--- linux-3.11.5.orig/scripts/mod/modpost.h 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/modpost.h 2013-10-16 18:09:31.000000000 +0200 +@@ -7,7 +7,2453 @@ + #include + #include + #include +-#include ++ ++ ++/* This file defines standard ELF types, structures, and macros. ++ Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++#ifndef _ELF_H ++#define _ELF_H 1 ++ ++__BEGIN_DECLS ++ ++/* Standard ELF types. */ ++ ++#include ++ ++/* Type for a 16-bit quantity. */ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++/* Types for signed and unsigned 32-bit quantities. */ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++/* Types for signed and unsigned 64-bit quantities. */ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++/* Type of addresses. */ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++/* Type of file offsets. */ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++/* Type for section indices, which are 16-bit quantities. */ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++/* Type for version symbol information. */ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++ ++/* The ELF file header. This appears at the start of every ELF file. */ ++ ++#define EI_NIDENT (16) ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf32_Half e_type; /* Object file type */ ++ Elf32_Half e_machine; /* Architecture */ ++ Elf32_Word e_version; /* Object file version */ ++ Elf32_Addr e_entry; /* Entry point virtual address */ ++ Elf32_Off e_phoff; /* Program header table file offset */ ++ Elf32_Off e_shoff; /* Section header table file offset */ ++ Elf32_Word e_flags; /* Processor-specific flags */ ++ Elf32_Half e_ehsize; /* ELF header size in bytes */ ++ Elf32_Half e_phentsize; /* Program header table entry size */ ++ Elf32_Half e_phnum; /* Program header table entry count */ ++ Elf32_Half e_shentsize; /* Section header table entry size */ ++ Elf32_Half e_shnum; /* Section header table entry count */ ++ Elf32_Half e_shstrndx; /* Section header string table index */ ++} Elf32_Ehdr; ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf64_Half e_type; /* Object file type */ ++ Elf64_Half e_machine; /* Architecture */ ++ Elf64_Word e_version; /* Object file version */ ++ Elf64_Addr e_entry; /* Entry point virtual address */ ++ Elf64_Off e_phoff; /* Program header table file offset */ ++ Elf64_Off e_shoff; /* Section header table file offset */ ++ Elf64_Word e_flags; /* Processor-specific flags */ ++ Elf64_Half e_ehsize; /* ELF header size in bytes */ ++ Elf64_Half e_phentsize; /* Program header table entry size */ ++ Elf64_Half e_phnum; /* Program header table entry count */ ++ Elf64_Half e_shentsize; /* Section header table entry size */ ++ Elf64_Half e_shnum; /* Section header table entry count */ ++ Elf64_Half e_shstrndx; /* Section header string table index */ ++} Elf64_Ehdr; ++ ++/* Fields in the e_ident array. The EI_* macros are indices into the ++ array. The macros under each EI_* macro are the values the byte ++ may have. */ ++ ++#define EI_MAG0 0 /* File identification byte 0 index */ ++#define ELFMAG0 0x7f /* Magic number byte 0 */ ++ ++#define EI_MAG1 1 /* File identification byte 1 index */ ++#define ELFMAG1 'E' /* Magic number byte 1 */ ++ ++#define EI_MAG2 2 /* File identification byte 2 index */ ++#define ELFMAG2 'L' /* Magic number byte 2 */ ++ ++#define EI_MAG3 3 /* File identification byte 3 index */ ++#define ELFMAG3 'F' /* Magic number byte 3 */ ++ ++/* Conglomeration of the identification bytes, for easy testing as a word. */ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 /* File class byte index */ ++#define ELFCLASSNONE 0 /* Invalid class */ ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATANONE 0 /* Invalid data encoding */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 /* File version byte index */ ++ /* Value must be EV_CURRENT */ ++ ++#define EI_OSABI 7 /* OS ABI identification */ ++#define ELFOSABI_NONE 0 /* UNIX System V ABI */ ++#define ELFOSABI_SYSV 0 /* Alias. */ ++#define ELFOSABI_HPUX 1 /* HP-UX */ ++#define ELFOSABI_NETBSD 2 /* NetBSD. */ ++#define ELFOSABI_LINUX 3 /* Linux. */ ++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ ++#define ELFOSABI_AIX 7 /* IBM AIX. */ ++#define ELFOSABI_IRIX 8 /* SGI Irix. */ ++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ ++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ ++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ ++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ ++#define ELFOSABI_ARM 97 /* ARM */ ++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ ++ ++#define EI_ABIVERSION 8 /* ABI version */ ++ ++#define EI_PAD 9 /* Byte index of padding bytes */ ++ ++/* Legal values for e_type (object file type). */ ++ ++#define ET_NONE 0 /* No file type */ ++#define ET_REL 1 /* Relocatable file */ ++#define ET_EXEC 2 /* Executable file */ ++#define ET_DYN 3 /* Shared object file */ ++#define ET_CORE 4 /* Core file */ ++#define ET_NUM 5 /* Number of defined types */ ++#define ET_LOOS 0xfe00 /* OS-specific range start */ ++#define ET_HIOS 0xfeff /* OS-specific range end */ ++#define ET_LOPROC 0xff00 /* Processor-specific range start */ ++#define ET_HIPROC 0xffff /* Processor-specific range end */ ++ ++/* Legal values for e_machine (architecture). */ ++ ++#define EM_NONE 0 /* No machine */ ++#define EM_M32 1 /* AT&T WE 32100 */ ++#define EM_SPARC 2 /* SUN SPARC */ ++#define EM_386 3 /* Intel 80386 */ ++#define EM_68K 4 /* Motorola m68k family */ ++#define EM_88K 5 /* Motorola m88k family */ ++#define EM_860 7 /* Intel 80860 */ ++#define EM_MIPS 8 /* MIPS R3000 big-endian */ ++#define EM_S370 9 /* IBM System/370 */ ++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ ++ ++#define EM_PARISC 15 /* HPPA */ ++#define EM_VPP500 17 /* Fujitsu VPP500 */ ++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ ++#define EM_960 19 /* Intel 80960 */ ++#define EM_PPC 20 /* PowerPC */ ++#define EM_PPC64 21 /* PowerPC 64-bit */ ++#define EM_S390 22 /* IBM S390 */ ++ ++#define EM_V800 36 /* NEC V800 series */ ++#define EM_FR20 37 /* Fujitsu FR20 */ ++#define EM_RH32 38 /* TRW RH-32 */ ++#define EM_RCE 39 /* Motorola RCE */ ++#define EM_ARM 40 /* ARM */ ++#define EM_FAKE_ALPHA 41 /* Digital Alpha */ ++#define EM_SH 42 /* Hitachi SH */ ++#define EM_SPARCV9 43 /* SPARC v9 64-bit */ ++#define EM_TRICORE 44 /* Siemens Tricore */ ++#define EM_ARC 45 /* Argonaut RISC Core */ ++#define EM_H8_300 46 /* Hitachi H8/300 */ ++#define EM_H8_300H 47 /* Hitachi H8/300H */ ++#define EM_H8S 48 /* Hitachi H8S */ ++#define EM_H8_500 49 /* Hitachi H8/500 */ ++#define EM_IA_64 50 /* Intel Merced */ ++#define EM_MIPS_X 51 /* Stanford MIPS-X */ ++#define EM_COLDFIRE 52 /* Motorola Coldfire */ ++#define EM_68HC12 53 /* Motorola M68HC12 */ ++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ ++#define EM_PCP 55 /* Siemens PCP */ ++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ ++#define EM_NDR1 57 /* Denso NDR1 microprocessor */ ++#define EM_STARCORE 58 /* Motorola Start*Core processor */ ++#define EM_ME16 59 /* Toyota ME16 processor */ ++#define EM_ST100 60 /* STMicroelectronic ST100 processor */ ++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ ++#define EM_X86_64 62 /* AMD x86-64 architecture */ ++#define EM_PDSP 63 /* Sony DSP Processor */ ++ ++#define EM_FX66 66 /* Siemens FX66 microcontroller */ ++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ ++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ ++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ ++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ ++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ ++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ ++#define EM_SVX 73 /* Silicon Graphics SVx */ ++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ ++#define EM_VAX 75 /* Digital VAX */ ++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ ++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ ++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ ++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ ++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ ++#define EM_HUANY 81 /* Harvard University machine-independent object files */ ++#define EM_PRISM 82 /* SiTera Prism */ ++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ ++#define EM_FR30 84 /* Fujitsu FR30 */ ++#define EM_D10V 85 /* Mitsubishi D10V */ ++#define EM_D30V 86 /* Mitsubishi D30V */ ++#define EM_V850 87 /* NEC v850 */ ++#define EM_M32R 88 /* Mitsubishi M32R */ ++#define EM_MN10300 89 /* Matsushita MN10300 */ ++#define EM_MN10200 90 /* Matsushita MN10200 */ ++#define EM_PJ 91 /* picoJava */ ++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ ++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ ++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ ++#define EM_NUM 95 ++ ++/* If it is necessary to assign new unofficial EM_* values, please ++ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the ++ chances of collision with official or non-GNU unofficial values. */ ++ ++#define EM_ALPHA 0x9026 ++ ++/* Legal values for e_version (version). */ ++ ++#define EV_NONE 0 /* Invalid ELF version */ ++#define EV_CURRENT 1 /* Current version */ ++#define EV_NUM 2 ++ ++/* Section header. */ ++ ++typedef struct ++{ ++ Elf32_Word sh_name; /* Section name (string tbl index) */ ++ Elf32_Word sh_type; /* Section type */ ++ Elf32_Word sh_flags; /* Section flags */ ++ Elf32_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf32_Off sh_offset; /* Section file offset */ ++ Elf32_Word sh_size; /* Section size in bytes */ ++ Elf32_Word sh_link; /* Link to another section */ ++ Elf32_Word sh_info; /* Additional section information */ ++ Elf32_Word sh_addralign; /* Section alignment */ ++ Elf32_Word sh_entsize; /* Entry size if section holds table */ ++} Elf32_Shdr; ++ ++typedef struct ++{ ++ Elf64_Word sh_name; /* Section name (string tbl index) */ ++ Elf64_Word sh_type; /* Section type */ ++ Elf64_Xword sh_flags; /* Section flags */ ++ Elf64_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf64_Off sh_offset; /* Section file offset */ ++ Elf64_Xword sh_size; /* Section size in bytes */ ++ Elf64_Word sh_link; /* Link to another section */ ++ Elf64_Word sh_info; /* Additional section information */ ++ Elf64_Xword sh_addralign; /* Section alignment */ ++ Elf64_Xword sh_entsize; /* Entry size if section holds table */ ++} Elf64_Shdr; ++ ++/* Special section indices. */ ++ ++#define SHN_UNDEF 0 /* Undefined section */ ++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ ++#define SHN_LOPROC 0xff00 /* Start of processor-specific */ ++#define SHN_HIPROC 0xff1f /* End of processor-specific */ ++#define SHN_LOOS 0xff20 /* Start of OS-specific */ ++#define SHN_HIOS 0xff3f /* End of OS-specific */ ++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ ++#define SHN_COMMON 0xfff2 /* Associated symbol is common */ ++#define SHN_XINDEX 0xffff /* Index is in extra table. */ ++#define SHN_HIRESERVE 0xffff /* End of reserved indices */ ++ ++/* Legal values for sh_type (section type). */ ++ ++#define SHT_NULL 0 /* Section header table entry unused */ ++#define SHT_PROGBITS 1 /* Program data */ ++#define SHT_SYMTAB 2 /* Symbol table */ ++#define SHT_STRTAB 3 /* String table */ ++#define SHT_RELA 4 /* Relocation entries with addends */ ++#define SHT_HASH 5 /* Symbol hash table */ ++#define SHT_DYNAMIC 6 /* Dynamic linking information */ ++#define SHT_NOTE 7 /* Notes */ ++#define SHT_NOBITS 8 /* Program space with no data (bss) */ ++#define SHT_REL 9 /* Relocation entries, no addends */ ++#define SHT_SHLIB 10 /* Reserved */ ++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ ++#define SHT_INIT_ARRAY 14 /* Array of constructors */ ++#define SHT_FINI_ARRAY 15 /* Array of destructors */ ++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ ++#define SHT_GROUP 17 /* Section group */ ++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ ++#define SHT_NUM 19 /* Number of defined types. */ ++#define SHT_LOOS 0x60000000 /* Start OS-specific */ ++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ ++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ ++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ ++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ ++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ ++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ ++#define SHT_HIOS 0x6fffffff /* End OS-specific type */ ++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define SHT_LOUSER 0x80000000 /* Start of application-specific */ ++#define SHT_HIUSER 0x8fffffff /* End of application-specific */ ++ ++/* Legal values for sh_flags (section flags). */ ++ ++#define SHF_WRITE (1 << 0) /* Writable */ ++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ ++#define SHF_EXECINSTR (1 << 2) /* Executable */ ++#define SHF_MERGE (1 << 4) /* Might be merged */ ++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ ++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ ++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ ++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling ++ required */ ++#define SHF_GROUP (1 << 9) /* Section is member of a group. */ ++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ ++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ ++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Section group handling. */ ++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ ++ ++/* Symbol table entry. */ ++ ++typedef struct ++{ ++ Elf32_Word st_name; /* Symbol name (string tbl index) */ ++ Elf32_Addr st_value; /* Symbol value */ ++ Elf32_Word st_size; /* Symbol size */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf32_Section st_shndx; /* Section index */ ++} Elf32_Sym; ++ ++typedef struct ++{ ++ Elf64_Word st_name; /* Symbol name (string tbl index) */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf64_Section st_shndx; /* Section index */ ++ Elf64_Addr st_value; /* Symbol value */ ++ Elf64_Xword st_size; /* Symbol size */ ++} Elf64_Sym; ++ ++/* The syminfo section if available contains additional information about ++ every dynamic symbol. */ ++ ++typedef struct ++{ ++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf32_Half si_flags; /* Per symbol flags */ ++} Elf32_Syminfo; ++ ++typedef struct ++{ ++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf64_Half si_flags; /* Per symbol flags */ ++} Elf64_Syminfo; ++ ++/* Possible values for si_boundto. */ ++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ ++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ ++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ ++ ++/* Possible bitmasks for si_flags. */ ++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ ++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ ++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ ++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy ++ loaded */ ++/* Syminfo version values. */ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++ ++/* How to extract and insert information held in the st_info field. */ ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++/* Legal values for ST_BIND subfield of st_info (symbol binding). */ ++ ++#define STB_LOCAL 0 /* Local symbol */ ++#define STB_GLOBAL 1 /* Global symbol */ ++#define STB_WEAK 2 /* Weak symbol */ ++#define STB_NUM 3 /* Number of defined types. */ ++#define STB_LOOS 10 /* Start of OS-specific */ ++#define STB_HIOS 12 /* End of OS-specific */ ++#define STB_LOPROC 13 /* Start of processor-specific */ ++#define STB_HIPROC 15 /* End of processor-specific */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_NOTYPE 0 /* Symbol type is unspecified */ ++#define STT_OBJECT 1 /* Symbol is a data object */ ++#define STT_FUNC 2 /* Symbol is a code object */ ++#define STT_SECTION 3 /* Symbol associated with a section */ ++#define STT_FILE 4 /* Symbol's name is file name */ ++#define STT_COMMON 5 /* Symbol is a common data object */ ++#define STT_TLS 6 /* Symbol is thread-local data object*/ ++#define STT_NUM 7 /* Number of defined types. */ ++#define STT_LOOS 10 /* Start of OS-specific */ ++#define STT_HIOS 12 /* End of OS-specific */ ++#define STT_LOPROC 13 /* Start of processor-specific */ ++#define STT_HIPROC 15 /* End of processor-specific */ ++ ++ ++/* Symbol table indices are found in the hash buckets and chain table ++ of a symbol hash table section. This special index value indicates ++ the end of a chain, meaning no further symbols are found in that bucket. */ ++ ++#define STN_UNDEF 0 /* End of a chain. */ ++ ++ ++/* How to extract and insert information held in the st_other field. */ ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++ ++/* For ELF64 the definitions are the same. */ ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++/* Symbol visibility specification encoded in the st_other field. */ ++#define STV_DEFAULT 0 /* Default symbol visibility rules */ ++#define STV_INTERNAL 1 /* Processor specific hidden class */ ++#define STV_HIDDEN 2 /* Sym unavailable in other modules */ ++#define STV_PROTECTED 3 /* Not preemptible, not exported */ ++ ++ ++/* Relocation table entry without addend (in section of type SHT_REL). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++} Elf32_Rel; ++ ++/* I have seen two different definitions of the Elf64_Rel and ++ Elf64_Rela structures, so we'll leave them out until Novell (or ++ whoever) gets their act together. */ ++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++} Elf64_Rel; ++ ++/* Relocation table entry with addend (in section of type SHT_RELA). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++ Elf32_Sword r_addend; /* Addend */ ++} Elf32_Rela; ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++ Elf64_Sxword r_addend; /* Addend */ ++} Elf64_Rela; ++ ++/* How to extract and insert information held in the r_info field. */ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++/* Program segment header. */ ++ ++typedef struct ++{ ++ Elf32_Word p_type; /* Segment type */ ++ Elf32_Off p_offset; /* Segment file offset */ ++ Elf32_Addr p_vaddr; /* Segment virtual address */ ++ Elf32_Addr p_paddr; /* Segment physical address */ ++ Elf32_Word p_filesz; /* Segment size in file */ ++ Elf32_Word p_memsz; /* Segment size in memory */ ++ Elf32_Word p_flags; /* Segment flags */ ++ Elf32_Word p_align; /* Segment alignment */ ++} Elf32_Phdr; ++ ++typedef struct ++{ ++ Elf64_Word p_type; /* Segment type */ ++ Elf64_Word p_flags; /* Segment flags */ ++ Elf64_Off p_offset; /* Segment file offset */ ++ Elf64_Addr p_vaddr; /* Segment virtual address */ ++ Elf64_Addr p_paddr; /* Segment physical address */ ++ Elf64_Xword p_filesz; /* Segment size in file */ ++ Elf64_Xword p_memsz; /* Segment size in memory */ ++ Elf64_Xword p_align; /* Segment alignment */ ++} Elf64_Phdr; ++ ++/* Legal values for p_type (segment type). */ ++ ++#define PT_NULL 0 /* Program header table entry unused */ ++#define PT_LOAD 1 /* Loadable program segment */ ++#define PT_DYNAMIC 2 /* Dynamic linking information */ ++#define PT_INTERP 3 /* Program interpreter */ ++#define PT_NOTE 4 /* Auxiliary information */ ++#define PT_SHLIB 5 /* Reserved */ ++#define PT_PHDR 6 /* Entry for header table itself */ ++#define PT_TLS 7 /* Thread-local storage segment */ ++#define PT_NUM 8 /* Number of defined types */ ++#define PT_LOOS 0x60000000 /* Start of OS-specific */ ++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ ++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ ++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff /* End of OS-specific */ ++#define PT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define PT_HIPROC 0x7fffffff /* End of processor-specific */ ++ ++/* Legal values for p_flags (segment flags). */ ++ ++#define PF_X (1 << 0) /* Segment is executable */ ++#define PF_W (1 << 1) /* Segment is writable */ ++#define PF_R (1 << 2) /* Segment is readable */ ++#define PF_MASKOS 0x0ff00000 /* OS-specific */ ++#define PF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Legal values for note segment descriptor types for core files. */ ++ ++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ ++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ ++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ ++#define NT_PRXREG 4 /* Contains copy of prxregset struct */ ++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ ++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ ++#define NT_AUXV 6 /* Contains copy of auxv array */ ++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ ++#define NT_ASRS 8 /* Contains copy of asrset struct */ ++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ ++#define NT_PSINFO 13 /* Contains copy of psinfo struct */ ++#define NT_PRCRED 14 /* Contains copy of prcred struct */ ++#define NT_UTSNAME 15 /* Contains copy of utsname struct */ ++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ ++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ ++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ ++ ++/* Legal values for the note segment descriptor types for object files. */ ++ ++#define NT_VERSION 1 /* Contains a version string. */ ++ ++ ++/* Dynamic section entry. */ ++ ++typedef struct ++{ ++ Elf32_Sword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf32_Word d_val; /* Integer value */ ++ Elf32_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct ++{ ++ Elf64_Sxword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf64_Xword d_val; /* Integer value */ ++ Elf64_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf64_Dyn; ++ ++/* Legal values for d_tag (dynamic entry type). */ ++ ++#define DT_NULL 0 /* Marks end of dynamic section */ ++#define DT_NEEDED 1 /* Name of needed library */ ++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ ++#define DT_PLTGOT 3 /* Processor defined value */ ++#define DT_HASH 4 /* Address of symbol hash table */ ++#define DT_STRTAB 5 /* Address of string table */ ++#define DT_SYMTAB 6 /* Address of symbol table */ ++#define DT_RELA 7 /* Address of Rela relocs */ ++#define DT_RELASZ 8 /* Total size of Rela relocs */ ++#define DT_RELAENT 9 /* Size of one Rela reloc */ ++#define DT_STRSZ 10 /* Size of string table */ ++#define DT_SYMENT 11 /* Size of one symbol table entry */ ++#define DT_INIT 12 /* Address of init function */ ++#define DT_FINI 13 /* Address of termination function */ ++#define DT_SONAME 14 /* Name of shared object */ ++#define DT_RPATH 15 /* Library search path (deprecated) */ ++#define DT_SYMBOLIC 16 /* Start symbol search here */ ++#define DT_REL 17 /* Address of Rel relocs */ ++#define DT_RELSZ 18 /* Total size of Rel relocs */ ++#define DT_RELENT 19 /* Size of one Rel reloc */ ++#define DT_PLTREL 20 /* Type of reloc in PLT */ ++#define DT_DEBUG 21 /* For debugging; unspecified */ ++#define DT_TEXTREL 22 /* Reloc might modify .text */ ++#define DT_JMPREL 23 /* Address of PLT relocs */ ++#define DT_BIND_NOW 24 /* Process relocations of object */ ++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ ++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ ++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ ++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ ++#define DT_RUNPATH 29 /* Library search path */ ++#define DT_FLAGS 30 /* Flags for the object being loaded */ ++#define DT_ENCODING 32 /* Start of encoded range */ ++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ ++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ ++#define DT_NUM 34 /* Number used */ ++#define DT_LOOS 0x6000000d /* Start of OS-specific */ ++#define DT_HIOS 0x6ffff000 /* End of OS-specific */ ++#define DT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define DT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ ++ ++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the ++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's ++ approach. */ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ ++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting ++ the following DT_* entry. */ ++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ ++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ ++#define DT_VALNUM 12 ++ ++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the ++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. ++ ++ If any adjustment is made to the ELF object after it has been ++ built these entries will need to be adjusted. */ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ ++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ ++#define DT_CONFIG 0x6ffffefa /* Configuration information. */ ++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ ++#define DT_AUDIT 0x6ffffefc /* Object auditing. */ ++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ ++#define DT_MOVETAB 0x6ffffefe /* Move table. */ ++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ ++#define DT_ADDRNUM 10 ++ ++/* The versioning entry types. The next are defined as part of the ++ GNU extension. */ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++/* These were chosen by Sun. */ ++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ ++#define DT_VERDEF 0x6ffffffc /* Address of version definition ++ table */ ++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ ++#define DT_VERNEED 0x6ffffffe /* Address of table with needed ++ versions */ ++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ ++#define DT_VERSIONTAGNUM 16 ++ ++/* Sun added these machine-independent extensions in the "processor-specific" ++ range. Be compatible. */ ++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ ++#define DT_FILTER 0x7fffffff /* Shared object to get values from */ ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++/* Values of `d_un.d_val' in the DT_FLAGS entry. */ ++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ ++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ ++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ ++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ ++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ ++ ++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 ++ entry in the dynamic section. */ ++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ ++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ ++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ ++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ ++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ ++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ ++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ ++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ ++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ ++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ ++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ ++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ ++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ ++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ ++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ ++ ++/* Flags for the feature selection in DT_FEATURE_1. */ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ ++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ ++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not ++ generally available. */ ++ ++/* Version definition sections. */ ++ ++typedef struct ++{ ++ Elf32_Half vd_version; /* Version revision */ ++ Elf32_Half vd_flags; /* Version information */ ++ Elf32_Half vd_ndx; /* Version Index */ ++ Elf32_Half vd_cnt; /* Number of associated aux entries */ ++ Elf32_Word vd_hash; /* Version name hash value */ ++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf32_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf32_Verdef; ++ ++typedef struct ++{ ++ Elf64_Half vd_version; /* Version revision */ ++ Elf64_Half vd_flags; /* Version information */ ++ Elf64_Half vd_ndx; /* Version Index */ ++ Elf64_Half vd_cnt; /* Number of associated aux entries */ ++ Elf64_Word vd_hash; /* Version name hash value */ ++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf64_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf64_Verdef; ++ ++ ++/* Legal values for vd_version (version revision). */ ++#define VER_DEF_NONE 0 /* No version */ ++#define VER_DEF_CURRENT 1 /* Current version */ ++#define VER_DEF_NUM 2 /* Given version number */ ++ ++/* Legal values for vd_flags (version information flags). */ ++#define VER_FLG_BASE 0x1 /* Version definition of file itself */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++/* Versym symbol index values. */ ++#define VER_NDX_LOCAL 0 /* Symbol is local. */ ++#define VER_NDX_GLOBAL 1 /* Symbol is global. */ ++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ ++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ ++ ++/* Auxialiary version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vda_name; /* Version or dependency names */ ++ Elf32_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf32_Verdaux; ++ ++typedef struct ++{ ++ Elf64_Word vda_name; /* Version or dependency names */ ++ Elf64_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf64_Verdaux; ++ ++ ++/* Version dependency section. */ ++ ++typedef struct ++{ ++ Elf32_Half vn_version; /* Version of structure */ ++ Elf32_Half vn_cnt; /* Number of associated aux entries */ ++ Elf32_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf32_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf32_Verneed; ++ ++typedef struct ++{ ++ Elf64_Half vn_version; /* Version of structure */ ++ Elf64_Half vn_cnt; /* Number of associated aux entries */ ++ Elf64_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf64_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf64_Verneed; ++ ++ ++/* Legal values for vn_version (version revision). */ ++#define VER_NEED_NONE 0 /* No version */ ++#define VER_NEED_CURRENT 1 /* Current version */ ++#define VER_NEED_NUM 2 /* Given version number */ ++ ++/* Auxiliary needed version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vna_hash; /* Hash value of dependency name */ ++ Elf32_Half vna_flags; /* Dependency specific information */ ++ Elf32_Half vna_other; /* Unused */ ++ Elf32_Word vna_name; /* Dependency name string offset */ ++ Elf32_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf32_Vernaux; ++ ++typedef struct ++{ ++ Elf64_Word vna_hash; /* Hash value of dependency name */ ++ Elf64_Half vna_flags; /* Dependency specific information */ ++ Elf64_Half vna_other; /* Unused */ ++ Elf64_Word vna_name; /* Dependency name string offset */ ++ Elf64_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf64_Vernaux; ++ ++ ++/* Legal values for vna_flags. */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++ ++/* Auxiliary vector. */ ++ ++/* This vector is normally only used by the program interpreter. The ++ usual definition in an ABI supplement uses the name auxv_t. The ++ vector is not usually defined in a standard file, but it ++ can't hurt. We rename it to avoid conflicts. The sizes of these ++ types are an arrangement between the exec server and the program ++ interpreter, so we don't fully specify them here. */ ++ ++typedef struct ++{ ++ int a_type; /* Entry type */ ++ union ++ { ++ long int a_val; /* Integer value */ ++ void *a_ptr; /* Pointer value */ ++ void (*a_fcn) (void); /* Function pointer value */ ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct ++{ ++ long int a_type; /* Entry type */ ++ union ++ { ++ long int a_val; /* Integer value */ ++ void *a_ptr; /* Pointer value */ ++ void (*a_fcn) (void); /* Function pointer value */ ++ } a_un; ++} Elf64_auxv_t; ++ ++/* Legal values for a_type (entry type). */ ++ ++#define AT_NULL 0 /* End of vector */ ++#define AT_IGNORE 1 /* Entry should be ignored */ ++#define AT_EXECFD 2 /* File descriptor of program */ ++#define AT_PHDR 3 /* Program headers for program */ ++#define AT_PHENT 4 /* Size of program header entry */ ++#define AT_PHNUM 5 /* Number of program headers */ ++#define AT_PAGESZ 6 /* System page size */ ++#define AT_BASE 7 /* Base address of interpreter */ ++#define AT_FLAGS 8 /* Flags */ ++#define AT_ENTRY 9 /* Entry point of program */ ++#define AT_NOTELF 10 /* Program is not ELF */ ++#define AT_UID 11 /* Real uid */ ++#define AT_EUID 12 /* Effective uid */ ++#define AT_GID 13 /* Real gid */ ++#define AT_EGID 14 /* Effective gid */ ++#define AT_CLKTCK 17 /* Frequency of times() */ ++ ++/* Some more special a_type values describing the hardware. */ ++#define AT_PLATFORM 15 /* String identifying platform. */ ++#define AT_HWCAP 16 /* Machine dependent hints about ++ processor capabilities. */ ++ ++/* This entry gives some information about the FPU initialization ++ performed by the kernel. */ ++#define AT_FPUCW 18 /* Used FPU control word. */ ++ ++/* Cache block sizes. */ ++#define AT_DCACHEBSIZE 19 /* Data cache block size. */ ++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ ++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ ++ ++/* A special ignored value for PPC, used by the kernel to control the ++ interpretation of the AUXV. Must be > 16. */ ++#define AT_IGNOREPPC 22 /* Entry should be ignored. */ ++ ++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ ++ ++/* Pointer to the global system page used for system calls and other ++ nice things. */ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++ ++/* Note section contents. Each entry in the note section begins with ++ a header of a fixed form. */ ++ ++typedef struct ++{ ++ Elf32_Word n_namesz; /* Length of the note's name. */ ++ Elf32_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf32_Word n_type; /* Type of the note. */ ++} Elf32_Nhdr; ++ ++typedef struct ++{ ++ Elf64_Word n_namesz; /* Length of the note's name. */ ++ Elf64_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf64_Word n_type; /* Type of the note. */ ++} Elf64_Nhdr; ++ ++/* Known names of notes. */ ++ ++/* Solaris entries in the note section have this name. */ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++/* Note entries for GNU systems have this name. */ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++/* Defined types of notes for Solaris. */ ++ ++/* Value of descriptor (one word) is desired pagesize for the binary. */ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++/* Defined note types for GNU systems. */ ++ ++/* ABI information. The descriptor consists of words: ++ word 0: OS descriptor ++ word 1: major version of the ABI ++ word 2: minor version of the ABI ++ word 3: subminor version of the ABI ++*/ ++#define ELF_NOTE_ABI 1 ++ ++/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI ++ note section entry. */ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++ ++/* Move records. */ ++typedef struct ++{ ++ Elf32_Xword m_value; /* Symbol value. */ ++ Elf32_Word m_info; /* Size and index. */ ++ Elf32_Word m_poffset; /* Symbol offset. */ ++ Elf32_Half m_repeat; /* Repeat count. */ ++ Elf32_Half m_stride; /* Stride info. */ ++} Elf32_Move; ++ ++typedef struct ++{ ++ Elf64_Xword m_value; /* Symbol value. */ ++ Elf64_Xword m_info; /* Size and index. */ ++ Elf64_Xword m_poffset; /* Symbol offset. */ ++ Elf64_Half m_repeat; /* Repeat count. */ ++ Elf64_Half m_stride; /* Stride info. */ ++} Elf64_Move; ++ ++/* Macro to construct move records. */ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++ ++/* Motorola 68k specific definitions. */ ++ ++/* Values for Elf32_Ehdr.e_flags. */ ++#define EF_CPU32 0x00810000 ++ ++/* m68k relocs. */ ++ ++#define R_68K_NONE 0 /* No reloc */ ++#define R_68K_32 1 /* Direct 32 bit */ ++#define R_68K_16 2 /* Direct 16 bit */ ++#define R_68K_8 3 /* Direct 8 bit */ ++#define R_68K_PC32 4 /* PC relative 32 bit */ ++#define R_68K_PC16 5 /* PC relative 16 bit */ ++#define R_68K_PC8 6 /* PC relative 8 bit */ ++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ ++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ ++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ ++#define R_68K_GOT32O 10 /* 32 bit GOT offset */ ++#define R_68K_GOT16O 11 /* 16 bit GOT offset */ ++#define R_68K_GOT8O 12 /* 8 bit GOT offset */ ++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ ++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ ++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ ++#define R_68K_PLT32O 16 /* 32 bit PLT offset */ ++#define R_68K_PLT16O 17 /* 16 bit PLT offset */ ++#define R_68K_PLT8O 18 /* 8 bit PLT offset */ ++#define R_68K_COPY 19 /* Copy symbol at runtime */ ++#define R_68K_GLOB_DAT 20 /* Create GOT entry */ ++#define R_68K_JMP_SLOT 21 /* Create PLT entry */ ++#define R_68K_RELATIVE 22 /* Adjust by program base */ ++/* Keep this the last entry. */ ++#define R_68K_NUM 23 ++ ++/* Intel 80386 specific definitions. */ ++ ++/* i386 relocs. */ ++ ++#define R_386_NONE 0 /* No reloc */ ++#define R_386_32 1 /* Direct 32 bit */ ++#define R_386_PC32 2 /* PC relative 32 bit */ ++#define R_386_GOT32 3 /* 32 bit GOT entry */ ++#define R_386_PLT32 4 /* 32 bit PLT address */ ++#define R_386_COPY 5 /* Copy symbol at runtime */ ++#define R_386_GLOB_DAT 6 /* Create GOT entry */ ++#define R_386_JMP_SLOT 7 /* Create PLT entry */ ++#define R_386_RELATIVE 8 /* Adjust by program base */ ++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ ++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ ++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS ++ block offset */ ++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block ++ offset */ ++#define R_386_TLS_LE 17 /* Offset relative to static TLS ++ block */ ++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of ++ general dynamic thread local data */ ++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of ++ local dynamic thread local data ++ in LE code */ ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic ++ thread local data */ ++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ ++#define R_386_TLS_GD_CALL 26 /* Relocation for call to ++ __tls_get_addr() */ ++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ ++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic ++ thread local data in LE code */ ++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ ++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to ++ __tls_get_addr() in LDM code */ ++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ ++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ ++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS ++ block offset */ ++#define R_386_TLS_LE_32 34 /* Negated offset relative to static ++ TLS block */ ++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ ++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ ++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ ++/* Keep this the last entry. */ ++#define R_386_NUM 38 ++ ++/* SUN SPARC specific definitions. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_REGISTER 13 /* Global register reserved to app. */ ++ ++/* Values for Elf64_Ehdr.e_flags. */ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 /* little endian data */ ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ ++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ ++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ ++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ ++ ++/* SPARC relocs. */ ++ ++#define R_SPARC_NONE 0 /* No reloc */ ++#define R_SPARC_8 1 /* Direct 8 bit */ ++#define R_SPARC_16 2 /* Direct 16 bit */ ++#define R_SPARC_32 3 /* Direct 32 bit */ ++#define R_SPARC_DISP8 4 /* PC relative 8 bit */ ++#define R_SPARC_DISP16 5 /* PC relative 16 bit */ ++#define R_SPARC_DISP32 6 /* PC relative 32 bit */ ++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ ++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ ++#define R_SPARC_HI22 9 /* High 22 bit */ ++#define R_SPARC_22 10 /* Direct 22 bit */ ++#define R_SPARC_13 11 /* Direct 13 bit */ ++#define R_SPARC_LO10 12 /* Truncated 10 bit */ ++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ ++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ ++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ ++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ ++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ ++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ ++#define R_SPARC_COPY 19 /* Copy symbol at runtime */ ++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ ++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ ++#define R_SPARC_RELATIVE 22 /* Adjust by program base */ ++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ ++ ++/* Additional Sparc64 relocs. */ ++ ++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ ++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ ++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ ++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ ++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ ++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ ++#define R_SPARC_10 30 /* Direct 10 bit */ ++#define R_SPARC_11 31 /* Direct 11 bit */ ++#define R_SPARC_64 32 /* Direct 64 bit */ ++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ ++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ ++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ ++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ ++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ ++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ ++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ ++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ ++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ ++#define R_SPARC_7 43 /* Direct 7 bit */ ++#define R_SPARC_5 44 /* Direct 5 bit */ ++#define R_SPARC_6 45 /* Direct 6 bit */ ++#define R_SPARC_DISP64 46 /* PC relative 64 bit */ ++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ ++#define R_SPARC_HIX22 48 /* High 22 bit complemented */ ++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ ++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ ++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ ++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ ++#define R_SPARC_REGISTER 53 /* Global register usage */ ++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ ++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++/* Keep this the last entry. */ ++#define R_SPARC_NUM 80 ++ ++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++/* Bits present in AT_HWCAP, primarily for Sparc32. */ ++ ++#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ ++#define HWCAP_SPARC_STBAR 2 ++#define HWCAP_SPARC_SWAP 4 ++#define HWCAP_SPARC_MULDIV 8 ++#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ ++#define HWCAP_SPARC_ULTRA3 32 ++ ++/* MIPS R3000 specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ ++#define EF_MIPS_PIC 2 /* Contains PIC code */ ++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ ++ ++/* Legal values for MIPS architecture level. */ ++ ++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* The following are non-official names and should not be used. */ ++ ++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* Special section indices. */ ++ ++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ ++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ ++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ ++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ ++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ ++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ ++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ ++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ ++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++/* Symbol tables. */ ++ ++/* MIPS specific values for `st_other'. */ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++/* MIPS specific values for `st_info'. */ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++/* Entries found in sections of type SHT_MIPS_GPTAB. */ ++ ++typedef union ++{ ++ struct ++ { ++ Elf32_Word gt_current_g_value; /* -G value used for compilation */ ++ Elf32_Word gt_unused; /* Not used */ ++ } gt_header; /* First entry in section */ ++ struct ++ { ++ Elf32_Word gt_g_value; /* If this value were used for -G */ ++ Elf32_Word gt_bytes; /* This many bytes would be used */ ++ } gt_entry; /* Subsequent entries in section */ ++} Elf32_gptab; ++ ++/* Entry found in sections of type SHT_MIPS_REGINFO. */ ++ ++typedef struct ++{ ++ Elf32_Word ri_gprmask; /* General registers used */ ++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ ++ Elf32_Sword ri_gp_value; /* $gp register value */ ++} Elf32_RegInfo; ++ ++/* Entries found in sections of type SHT_MIPS_OPTIONS. */ ++ ++typedef struct ++{ ++ unsigned char kind; /* Determines interpretation of the ++ variable part of descriptor. */ ++ unsigned char size; /* Size of descriptor, including header. */ ++ Elf32_Section section; /* Section header index of section affected, ++ 0 for global options. */ ++ Elf32_Word info; /* Kind-specific information. */ ++} Elf_Options; ++ ++/* Values for `kind' field in Elf_Options. */ ++ ++#define ODK_NULL 0 /* Undefined. */ ++#define ODK_REGINFO 1 /* Register usage information. */ ++#define ODK_EXCEPTIONS 2 /* Exception processing options. */ ++#define ODK_PAD 3 /* Section padding options. */ ++#define ODK_HWPATCH 4 /* Hardware workarounds performed */ ++#define ODK_FILL 5 /* record the fill value used by the linker. */ ++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ ++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ ++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ ++ ++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ ++ ++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ ++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ ++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ ++#define OEX_SMM 0x20000 /* Force sequential memory mode? */ ++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ ++ ++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ ++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ ++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ ++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++/* Entry found in `.options' section. */ ++ ++typedef struct ++{ ++ Elf32_Word hwp_flags1; /* Extra flags. */ ++ Elf32_Word hwp_flags2; /* Extra flags. */ ++} Elf_Options_Hw; ++ ++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++/* MIPS relocs. */ ++ ++#define R_MIPS_NONE 0 /* No reloc */ ++#define R_MIPS_16 1 /* Direct 16 bit */ ++#define R_MIPS_32 2 /* Direct 32 bit */ ++#define R_MIPS_REL32 3 /* PC relative 32 bit */ ++#define R_MIPS_26 4 /* Direct 26 bit shifted */ ++#define R_MIPS_HI16 5 /* High 16 bit */ ++#define R_MIPS_LO16 6 /* Low 16 bit */ ++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ ++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ ++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ ++#define R_MIPS_PC16 10 /* PC relative 16 bit */ ++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ ++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++/* Keep this the last entry. */ ++#define R_MIPS_NUM 38 ++ ++/* Legal values for p_type field of Elf32_Phdr. */ ++ ++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ ++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++/* Special program header types. */ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++/* Legal values for d_tag field of Elf32_Dyn. */ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ ++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ ++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ ++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ ++#define DT_MIPS_FLAGS 0x70000005 /* Flags */ ++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ ++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ ++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ ++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ ++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ ++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ ++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ ++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ ++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ ++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in ++ DT_MIPS_DELTA_CLASS. */ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in ++ DT_MIPS_DELTA_INSTANCE. */ ++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in ++ DT_MIPS_DELTA_RELOC. */ ++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta ++ relocations refer to. */ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in ++ DT_MIPS_DELTA_SYM. */ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the ++ class declaration. */ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in ++ DT_MIPS_DELTA_CLASSSYM. */ ++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ ++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve ++ function stored in GOT. */ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added ++ by rld on dlopen() calls. */ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ ++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ ++#define DT_MIPS_NUM 0x32 ++ ++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ ++ ++#define RHF_NONE 0 /* No flags */ ++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ ++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++/* Entries found in sections of type SHT_MIPS_LIBLIST. */ ++ ++typedef struct ++{ ++ Elf32_Word l_name; /* Name (string table index) */ ++ Elf32_Word l_time_stamp; /* Timestamp */ ++ Elf32_Word l_checksum; /* Checksum */ ++ Elf32_Word l_version; /* Interface version */ ++ Elf32_Word l_flags; /* Flags */ ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; /* Name (string table index) */ ++ Elf64_Word l_time_stamp; /* Timestamp */ ++ Elf64_Word l_checksum; /* Checksum */ ++ Elf64_Word l_version; /* Interface version */ ++ Elf64_Word l_flags; /* Flags */ ++} Elf64_Lib; ++ ++ ++/* Legal values for l_flags. */ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ ++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++/* Entries found in sections of type SHT_MIPS_CONFLICT. */ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++/* HPPA specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ ++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ ++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ ++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ ++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch ++ prediction. */ ++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ ++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ ++ ++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ ++ ++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ ++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ ++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ ++ ++/* Additional section indeces. */ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared ++ symbols in ANSI C. */ ++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ ++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ ++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ ++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ ++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++/* HPPA relocs. */ ++ ++#define R_PARISC_NONE 0 /* No reloc. */ ++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ ++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ ++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ ++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ ++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ ++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ ++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ ++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ ++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ ++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ ++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ ++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ ++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ ++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ ++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ ++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ ++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ ++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ ++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ ++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ ++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ ++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ ++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ ++#define R_PARISC_FPTR64 64 /* 64 bits function address. */ ++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ ++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ ++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ ++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ ++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ ++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ ++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ ++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ ++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ ++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ ++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ ++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ ++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ ++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ ++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ ++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 /* Copy relocation. */ ++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ ++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ ++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ ++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ ++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ ++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ ++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ ++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ ++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_HIRESERVE 255 ++ ++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++/* Alpha specific definitions. */ ++ ++/* Legal values for e_flags field of Elf64_Ehdr. */ ++ ++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ ++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ ++ ++/* Legal values for sh_type field of Elf64_Shdr. */ ++ ++/* These two are primerily concerned with ECOFF debugging info. */ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++/* Legal values for sh_flags field of Elf64_Shdr. */ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++/* Legal values for st_other field of Elf64_Sym. */ ++#define STO_ALPHA_NOPV 0x80 /* No PV required. */ ++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ ++ ++/* Alpha relocs. */ ++ ++#define R_ALPHA_NONE 0 /* No reloc */ ++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ ++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ ++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ ++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ ++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ ++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ ++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ ++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ ++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ ++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ ++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ ++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ ++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ ++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ ++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ ++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ ++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ ++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++/* Keep this the last entry. */ ++#define R_ALPHA_NUM 46 ++ ++/* Magic values of the LITUSE relocation addend. */ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++ ++/* PowerPC specific declarations */ ++ ++/* Values for Elf32/64_Ehdr.e_flags. */ ++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ ++ ++/* Cygnus local bits below */ ++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib ++ flag */ ++ ++/* PowerPC relocations defined by the ABIs */ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 /* 32bit absolute address */ ++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ ++#define R_PPC_ADDR16 3 /* 16bit absolute address */ ++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ ++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ ++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ ++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 /* PC relative 26 bit */ ++#define R_PPC_REL14 11 /* PC relative 16 bit */ ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++/* PowerPC relocations defined for the TLS access ABI. */ ++#define R_PPC_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ ++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ ++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ ++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ ++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ ++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ ++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ ++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ ++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ ++ ++/* Keep this the last entry. */ ++#define R_PPC_NUM 95 ++ ++/* The remaining relocs are from the Embedded ELF ABI, and are not ++ in the SVR4 ELF ABI. */ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ ++ ++/* Diab tool relocations. */ ++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ ++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ ++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ ++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ ++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ ++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ ++ ++/* This is a phony reloc to handle any old fashioned TOC16 references ++ that may still be in object files. */ ++#define R_PPC_TOC16 255 ++ ++ ++/* PowerPC64 relocations defined by the ABIs */ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ ++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ ++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ ++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ ++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ ++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ ++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ ++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ ++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ ++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ ++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ ++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ ++#define R_PPC64_PLT64 45 /* doubleword64 L + A */ ++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ ++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ ++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ ++#define R_PPC64_TOC 51 /* doubleword64 .TOC */ ++#define R_PPC64_PLTGOT16 52 /* half16* M + A */ ++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ ++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ ++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ ++ ++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ ++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ ++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ ++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ ++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ ++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ ++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ ++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ ++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ ++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ ++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ ++ ++/* PowerPC64 relocations defined for the TLS access ABI. */ ++#define R_PPC64_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ ++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ ++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ ++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ ++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ ++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ ++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ ++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ ++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ ++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ ++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ ++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ ++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ ++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ ++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ ++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ ++ ++/* Keep this the last entry. */ ++#define R_PPC64_NUM 107 ++ ++/* PowerPC64 specific values for the Dyn d_tag field. */ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_NUM 1 ++ ++ ++/* ARM specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++ ++/* Other constants defined in the ARM ELF spec. version B-01. */ ++/* NB. These conflict with values defined above. */ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++ ++/* Additional symbol types for Thumb */ ++#define STT_ARM_TFUNC 0xd ++ ++/* ARM-specific values for sh_flags */ ++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ ++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined ++ in the input to a link step */ ++ ++/* ARM-specific program header flags */ ++#define PF_ARM_SB 0x10000000 /* Segment contains the location ++ addressed by the static base */ ++ ++/* ARM relocs. */ ++#define R_ARM_NONE 0 /* No reloc */ ++#define R_ARM_PC24 1 /* PC relative 26 bit branch */ ++#define R_ARM_ABS32 2 /* Direct 32 bit */ ++#define R_ARM_REL32 3 /* PC relative 32 bit */ ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 /* Direct 16 bit */ ++#define R_ARM_ABS12 6 /* Direct 12 bit */ ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 /* Direct 8 bit */ ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_SWI24 13 ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_COPY 20 /* Copy symbol at runtime */ ++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ ++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ ++#define R_ARM_RELATIVE 23 /* Adjust by program base */ ++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ ++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ ++#define R_ARM_GOT32 26 /* 32 bit GOT entry */ ++#define R_ARM_PLT32 27 /* 32 bit PLT address */ ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ ++#define R_ARM_THM_PC9 103 /* thumb conditional branch */ ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++/* Keep this the last entry. */ ++#define R_ARM_NUM 256 ++ ++/* IA-64 specific declarations. */ ++ ++/* Processor specific flags for the Ehdr e_flags field. */ ++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ ++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ ++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ ++ ++/* Processor specific flags for the Phdr p_flags field. */ ++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ ++ ++/* Processor specific flags for the Shdr sh_flags field. */ ++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ ++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Dyn d_tag field. */ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++/* IA-64 relocations. */ ++#define R_IA64_NONE 0x00 /* none */ ++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ ++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ ++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ ++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ ++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ ++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ ++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ ++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ ++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ ++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ ++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ ++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ ++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ ++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ ++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ ++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ ++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ ++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ ++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ ++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ ++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ ++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ ++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ ++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ ++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ ++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ ++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ ++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ ++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ ++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ ++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ ++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ ++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ ++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ ++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ ++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ ++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ ++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ ++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ ++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ ++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ ++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ ++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ ++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ ++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ ++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ ++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ ++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ ++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ ++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ ++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ ++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ ++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ ++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ ++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ ++#define R_IA64_COPY 0x84 /* copy relocation */ ++#define R_IA64_SUB 0x85 /* Addend and symbol difference */ ++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ ++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ ++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ ++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ ++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ ++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ ++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ ++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ ++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ ++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ ++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ ++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ ++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ ++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ ++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ ++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ ++ ++/* SH specific declarations */ ++ ++/* SH relocs. */ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++/* Keep this the last entry. */ ++#define R_SH_NUM 256 ++ ++/* Additional s390 relocs */ ++ ++#define R_390_NONE 0 /* No reloc. */ ++#define R_390_8 1 /* Direct 8 bit. */ ++#define R_390_12 2 /* Direct 12 bit. */ ++#define R_390_16 3 /* Direct 16 bit. */ ++#define R_390_32 4 /* Direct 32 bit. */ ++#define R_390_PC32 5 /* PC relative 32 bit. */ ++#define R_390_GOT12 6 /* 12 bit GOT offset. */ ++#define R_390_GOT32 7 /* 32 bit GOT offset. */ ++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ ++#define R_390_COPY 9 /* Copy symbol at runtime. */ ++#define R_390_GLOB_DAT 10 /* Create GOT entry. */ ++#define R_390_JMP_SLOT 11 /* Create PLT entry. */ ++#define R_390_RELATIVE 12 /* Adjust by program base. */ ++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ ++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ ++#define R_390_GOT16 15 /* 16 bit GOT offset. */ ++#define R_390_PC16 16 /* PC relative 16 bit. */ ++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ ++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ ++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ ++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ ++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ ++#define R_390_64 22 /* Direct 64 bit. */ ++#define R_390_PC64 23 /* PC relative 64 bit. */ ++#define R_390_GOT64 24 /* 64 bit GOT offset. */ ++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ ++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ ++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ ++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ ++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ ++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ ++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ ++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ ++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ ++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ ++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ ++#define R_390_TLS_GDCALL 38 /* Tag for function call in general ++ dynamic TLS code. */ ++#define R_390_TLS_LDCALL 39 /* Tag for function call in local ++ dynamic TLS code. */ ++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ ++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ ++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS ++ block. */ ++ ++/* Keep this the last entry. */ ++#define R_390_NUM 57 ++ ++/* CRIS relocations. */ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++/* AMD x86-64 relocations. */ ++#define R_X86_64_NONE 0 /* No reloc */ ++#define R_X86_64_64 1 /* Direct 64 bit */ ++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ ++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ ++#define R_X86_64_PLT32 4 /* 32 bit PLT address */ ++#define R_X86_64_COPY 5 /* Copy symbol at runtime */ ++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ ++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ ++#define R_X86_64_RELATIVE 8 /* Adjust by program base */ ++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative ++ offset to GOT */ ++#define R_X86_64_32 10 /* Direct 32 bit zero extended */ ++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ ++#define R_X86_64_16 12 /* Direct 16 bit zero extended */ ++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ ++#define R_X86_64_8 14 /* Direct 8 bit sign extended */ ++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ ++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ ++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ ++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ ++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset ++ to two GOT entries for GD symbol */ ++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset ++ to two GOT entries for LD symbol */ ++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ ++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset ++ to GOT entry for IE symbol */ ++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ ++ ++#define R_X86_64_NUM 24 ++ ++__END_DECLS ++ ++#endif /* elf.h */ + + #include "elfconfig.h" + +@@ -185,3 +2631,4 @@ + void fatal(const char *fmt, ...); + void warn(const char *fmt, ...); + void merror(const char *fmt, ...); ++ +diff -Nur linux-3.11.5.orig/scripts/mod/sumversion.c linux-3.11.5/scripts/mod/sumversion.c +--- linux-3.11.5.orig/scripts/mod/sumversion.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/sumversion.c 2013-10-16 18:09:31.000000000 +0200 +@@ -1,4 +1,4 @@ +-#include ++/* #include */ + #ifdef __sun__ + #include + #else +diff -Nur linux-3.11.5.orig/tools/include/tools/linux_types.h linux-3.11.5/tools/include/tools/linux_types.h +--- linux-3.11.5.orig/tools/include/tools/linux_types.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.11.5/tools/include/tools/linux_types.h 2013-10-16 18:09:31.000000000 +0200 +@@ -0,0 +1,22 @@ ++#ifndef __LINUX_TYPES_H ++#define __LINUX_TYPES_H ++ ++#include ++ ++typedef uint8_t __u8; ++typedef uint8_t __be8; ++typedef uint8_t __le8; ++ ++typedef uint16_t __u16; ++typedef uint16_t __be16; ++typedef uint16_t __le16; ++ ++typedef uint32_t __u32; ++typedef uint32_t __be32; ++typedef uint32_t __le32; ++ ++typedef uint64_t __u64; ++typedef uint64_t __be64; ++typedef uint64_t __le64; ++ ++#endif diff --git a/target/linux/patches/3.12.44/cleankernel.patch b/target/linux/patches/3.12.44/cleankernel.patch new file mode 100644 index 000000000..d8c055dc3 --- /dev/null +++ b/target/linux/patches/3.12.44/cleankernel.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.5.orig/scripts/Makefile.headersinst linux-3.11.5/scripts/Makefile.headersinst +--- linux-3.11.5.orig/scripts/Makefile.headersinst 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/Makefile.headersinst 2013-10-15 16:33:10.000000000 +0200 +@@ -107,7 +107,6 @@ + + targets += $(install-file) + $(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE +- $(if $(unwanted),$(call cmd,remove),) + $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) + $(call if_changed,install) + diff --git a/target/linux/patches/3.12.44/defaults.patch b/target/linux/patches/3.12.44/defaults.patch new file mode 100644 index 000000000..6cdca084e --- /dev/null +++ b/target/linux/patches/3.12.44/defaults.patch @@ -0,0 +1,46 @@ +diff -Nur linux-3.0.4.orig/fs/Kconfig linux-3.0.4/fs/Kconfig +--- linux-3.0.4.orig/fs/Kconfig 2011-08-29 22:56:30.000000000 +0200 ++++ linux-3.0.4/fs/Kconfig 2011-10-15 22:08:44.000000000 +0200 +@@ -47,7 +47,7 @@ + def_bool n + + config EXPORTFS +- tristate ++ def_bool y + + config FILE_LOCKING + bool "Enable POSIX file locking API" if EXPERT +diff -Nur linux-3.0.4.orig/fs/notify/Kconfig linux-3.0.4/fs/notify/Kconfig +--- linux-3.0.4.orig/fs/notify/Kconfig 2011-08-29 22:56:30.000000000 +0200 ++++ linux-3.0.4/fs/notify/Kconfig 2011-10-15 22:02:00.000000000 +0200 +@@ -1,5 +1,5 @@ + config FSNOTIFY +- def_bool n ++ def_bool y + + source "fs/notify/dnotify/Kconfig" + source "fs/notify/inotify/Kconfig" +diff -Nur linux-3.11.10.orig/drivers/scsi/Kconfig linux-3.11.10/drivers/scsi/Kconfig +--- linux-3.11.10.orig/drivers/scsi/Kconfig 2013-11-29 19:42:37.000000000 +0100 ++++ linux-3.11.10/drivers/scsi/Kconfig 2013-12-27 19:13:21.000000000 +0100 +@@ -2,7 +2,7 @@ + + config SCSI_MOD + tristate +- default y if SCSI=n || SCSI=y ++ default y if SCSI=y + default m if SCSI=m + + config RAID_ATTRS +diff -Nur linux-3.11.10.orig/usr/Kconfig linux-3.11.10/usr/Kconfig +--- linux-3.11.10.orig/usr/Kconfig 2013-11-29 19:42:37.000000000 +0100 ++++ linux-3.11.10/usr/Kconfig 2013-12-27 19:15:16.000000000 +0100 +@@ -47,7 +47,7 @@ + + config RD_GZIP + bool "Support initial ramdisks compressed using gzip" if EXPERT +- default y ++ default n + depends on BLK_DEV_INITRD + select DECOMPRESS_GZIP + help diff --git a/target/linux/patches/3.12.44/disable-netfilter.patch b/target/linux/patches/3.12.44/disable-netfilter.patch new file mode 100644 index 000000000..7b1ca013a --- /dev/null +++ b/target/linux/patches/3.12.44/disable-netfilter.patch @@ -0,0 +1,160 @@ +diff -Nur linux-3.7.3.orig/net/Kconfig linux-3.7.3/net/Kconfig +--- linux-3.7.3.orig/net/Kconfig 2013-01-17 17:47:40.000000000 +0100 ++++ linux-3.7.3/net/Kconfig 2013-01-19 18:19:55.000000000 +0100 +@@ -163,7 +163,7 @@ + config NETFILTER_ADVANCED + bool "Advanced netfilter configuration" + depends on NETFILTER +- default y ++ default n + help + If you say Y here you can select between all the netfilter modules. + If you say N the more unusual ones will not be shown and the +@@ -175,7 +175,7 @@ + bool "Bridged IP/ARP packets filtering" + depends on BRIDGE && NETFILTER && INET + depends on NETFILTER_ADVANCED +- default y ++ default n + ---help--- + Enabling this option will let arptables resp. iptables see bridged + ARP resp. IP traffic. If you want a bridging firewall, you probably +diff -Nur linux-3.7.3.orig/net/netfilter/Kconfig linux-3.7.3/net/netfilter/Kconfig +--- linux-3.7.3.orig/net/netfilter/Kconfig 2013-01-17 17:47:40.000000000 +0100 ++++ linux-3.7.3/net/netfilter/Kconfig 2013-01-19 18:21:41.000000000 +0100 +@@ -22,7 +22,6 @@ + + config NETFILTER_NETLINK_LOG + tristate "Netfilter LOG over NFNETLINK interface" +- default m if NETFILTER_ADVANCED=n + select NETFILTER_NETLINK + help + If this option is enabled, the kernel will include support +@@ -34,7 +33,6 @@ + + config NF_CONNTRACK + tristate "Netfilter connection tracking support" +- default m if NETFILTER_ADVANCED=n + help + Connection tracking keeps a record of what packets have passed + through your machine, in order to figure out how they are related +@@ -60,7 +58,6 @@ + config NF_CONNTRACK_SECMARK + bool 'Connection tracking security mark support' + depends on NETWORK_SECMARK +- default m if NETFILTER_ADVANCED=n + help + This option enables security markings to be applied to + connections. Typically they are copied to connections from +@@ -177,7 +174,6 @@ + + config NF_CONNTRACK_FTP + tristate "FTP protocol support" +- default m if NETFILTER_ADVANCED=n + help + Tracking FTP connections is problematic: special helpers are + required for tracking them, and doing masquerading and other forms +@@ -211,7 +207,6 @@ + + config NF_CONNTRACK_IRC + tristate "IRC protocol support" +- default m if NETFILTER_ADVANCED=n + help + There is a commonly-used extension to IRC called + Direct Client-to-Client Protocol (DCC). This enables users to send +@@ -296,7 +291,6 @@ + + config NF_CONNTRACK_SIP + tristate "SIP protocol support" +- default m if NETFILTER_ADVANCED=n + help + SIP is an application-layer control protocol that can establish, + modify, and terminate multimedia sessions (conferences) such as +@@ -320,7 +314,6 @@ + config NF_CT_NETLINK + tristate 'Connection tracking netlink interface' + select NETFILTER_NETLINK +- default m if NETFILTER_ADVANCED=n + help + This option enables support for a netlink-based userspace interface + +@@ -424,7 +417,6 @@ + + config NETFILTER_XTABLES + tristate "Netfilter Xtables support (required for ip_tables)" +- default m if NETFILTER_ADVANCED=n + help + This is required if you intend to use any of ip_tables, + ip6_tables or arp_tables. +@@ -435,7 +427,6 @@ + + config NETFILTER_XT_MARK + tristate 'nfmark target and match support' +- default m if NETFILTER_ADVANCED=n + ---help--- + This option adds the "MARK" target and "mark" match. + +@@ -527,7 +518,6 @@ + config NETFILTER_XT_TARGET_CONNSECMARK + tristate '"CONNSECMARK" target support' + depends on NF_CONNTRACK && NF_CONNTRACK_SECMARK +- default m if NETFILTER_ADVANCED=n + help + The CONNSECMARK target copies security markings from packets + to connections, and restores security markings from connections +@@ -632,7 +622,6 @@ + + config NETFILTER_XT_TARGET_LOG + tristate "LOG target support" +- default m if NETFILTER_ADVANCED=n + help + This option adds a `LOG' target, which allows you to create rules in + any iptables table which records the packet header to the syslog. +@@ -660,7 +649,6 @@ + + config NETFILTER_XT_TARGET_NFLOG + tristate '"NFLOG" target support' +- default m if NETFILTER_ADVANCED=n + select NETFILTER_NETLINK_LOG + help + This option enables the NFLOG target, which allows to LOG +@@ -741,7 +729,6 @@ + config NETFILTER_XT_TARGET_SECMARK + tristate '"SECMARK" target support' + depends on NETWORK_SECMARK +- default m if NETFILTER_ADVANCED=n + help + The SECMARK target allows security marking of network + packets, for use with security subsystems. +@@ -751,7 +738,6 @@ + config NETFILTER_XT_TARGET_TCPMSS + tristate '"TCPMSS" target support' + depends on (IPV6 || IPV6=n) +- default m if NETFILTER_ADVANCED=n + ---help--- + This option adds a `TCPMSS' target, which allows you to alter the + MSS value of TCP SYN packets, to control the maximum size for that +@@ -856,7 +842,6 @@ + config NETFILTER_XT_MATCH_CONNTRACK + tristate '"conntrack" connection tracking match support' + depends on NF_CONNTRACK +- default m if NETFILTER_ADVANCED=n + help + This is a general conntrack match module, a superset of the state match. + +@@ -1063,7 +1048,6 @@ + config NETFILTER_XT_MATCH_POLICY + tristate 'IPsec "policy" match support' + depends on XFRM +- default m if NETFILTER_ADVANCED=n + help + Policy matching allows you to match packets based on the + IPsec policy that was used during decapsulation/will +@@ -1170,7 +1154,6 @@ + config NETFILTER_XT_MATCH_STATE + tristate '"state" match support' + depends on NF_CONNTRACK +- default m if NETFILTER_ADVANCED=n + help + Connection state matching allows you to match packets based on their + relationship to a tracked connection (ie. previous packets). This diff --git a/target/linux/patches/3.12.44/export-symbol-for-exmap.patch b/target/linux/patches/3.12.44/export-symbol-for-exmap.patch new file mode 100644 index 000000000..4f0fc8449 --- /dev/null +++ b/target/linux/patches/3.12.44/export-symbol-for-exmap.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.5.orig/kernel/pid.c linux-3.11.5/kernel/pid.c +--- linux-3.11.5.orig/kernel/pid.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/kernel/pid.c 2013-10-29 15:37:02.000000000 +0100 +@@ -450,6 +450,7 @@ + { + return find_task_by_pid_ns(vnr, task_active_pid_ns(current)); + } ++EXPORT_SYMBOL(find_task_by_vpid); + + struct pid *get_task_pid(struct task_struct *task, enum pid_type type) + { diff --git a/target/linux/patches/3.12.44/gemalto.patch b/target/linux/patches/3.12.44/gemalto.patch new file mode 100644 index 000000000..65f7af1d7 --- /dev/null +++ b/target/linux/patches/3.12.44/gemalto.patch @@ -0,0 +1,11 @@ +diff -Nur linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c linux-2.6.36/drivers/serial/8250/serial_cs.c +--- linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/drivers/tty/serial/8250/serial_cs.c 2010-12-13 23:03:40.000000000 +0100 +@@ -794,6 +794,7 @@ + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), ++ PCMCIA_DEVICE_MANF_CARD(0x0157, 0x0100), /* Gemalto SCR */ + PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ + PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ + PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), diff --git a/target/linux/patches/3.12.44/lemote-rfkill.patch b/target/linux/patches/3.12.44/lemote-rfkill.patch new file mode 100644 index 000000000..a61488434 --- /dev/null +++ b/target/linux/patches/3.12.44/lemote-rfkill.patch @@ -0,0 +1,21 @@ +diff -Nur linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c +--- linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-19 00:15:34.000000000 +0100 ++++ linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-27 23:29:46.000000000 +0200 +@@ -22,6 +22,9 @@ + + static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) + { ++#ifdef CONFIG_LEMOTE_MACH2F ++ return 1; ++#else + u8 gpio; + + gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); +@@ -29,6 +32,7 @@ + gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); + + return gpio & priv->rfkill_mask; ++#endif + } + + void rtl8187_rfkill_init(struct ieee80211_hw *hw) diff --git a/target/linux/patches/3.12.44/microblaze-ethernet.patch b/target/linux/patches/3.12.44/microblaze-ethernet.patch new file mode 100644 index 000000000..742ab477e --- /dev/null +++ b/target/linux/patches/3.12.44/microblaze-ethernet.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c +--- linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-11-29 19:42:37.000000000 +0100 ++++ linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-12-23 20:01:14.000000000 +0100 +@@ -1282,6 +1282,7 @@ + { .compatible = "xlnx,opb-ethernetlite-1.01.b", }, + { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, + { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, ++ { .compatible = "xlnx,xps-ethernetlite-2.00.b", }, + { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, + { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, + { /* end of list */ }, diff --git a/target/linux/patches/3.12.44/microblaze-setup.patch b/target/linux/patches/3.12.44/microblaze-setup.patch new file mode 100644 index 000000000..43815f274 --- /dev/null +++ b/target/linux/patches/3.12.44/microblaze-setup.patch @@ -0,0 +1,12 @@ +diff -Nur linux-3.11.10.orig/arch/microblaze/kernel/setup.c linux-3.11.10/arch/microblaze/kernel/setup.c +--- linux-3.11.10.orig/arch/microblaze/kernel/setup.c 2013-11-29 19:42:37.000000000 +0100 ++++ linux-3.11.10/arch/microblaze/kernel/setup.c 2013-12-08 13:02:37.000000000 +0100 +@@ -177,7 +177,7 @@ + #else + if (!msr) { + pr_info("!!!Your kernel not setup MSR instruction but "); +- pr_cont"CPU have it %x\n", msr); ++ pr_cont("CPU have it %x\n", msr); + } + #endif + diff --git a/target/linux/patches/3.12.44/mips-lzo-fix.patch b/target/linux/patches/3.12.44/mips-lzo-fix.patch new file mode 100644 index 000000000..0740bdc6a --- /dev/null +++ b/target/linux/patches/3.12.44/mips-lzo-fix.patch @@ -0,0 +1,12 @@ +diff -Nur linux-3.11.5.orig/arch/mips/boot/compressed/decompress.c linux-3.11.5/arch/mips/boot/compressed/decompress.c +--- linux-3.11.5.orig/arch/mips/boot/compressed/decompress.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/mips/boot/compressed/decompress.c 2013-11-08 18:19:47.000000000 +0100 +@@ -43,7 +43,7 @@ + /* activate the code for pre-boot environment */ + #define STATIC static + +-#ifdef CONFIG_KERNEL_GZIP ++#if defined(CONFIG_KERNEL_GZIP) || defined(CONFIG_KERNEL_LZO) + void *memcpy(void *dest, const void *src, size_t n) + { + int i; diff --git a/target/linux/patches/3.12.44/mkpiggy.patch b/target/linux/patches/3.12.44/mkpiggy.patch new file mode 100644 index 000000000..d4e815cd2 --- /dev/null +++ b/target/linux/patches/3.12.44/mkpiggy.patch @@ -0,0 +1,28 @@ +diff -Nur linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c +--- linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c 2012-06-22 20:37:50.000000000 +0200 ++++ linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c 2012-07-03 09:48:02.000000000 +0200 +@@ -29,7 +29,14 @@ + #include + #include + #include +-#include ++ ++static uint32_t getle32(const void *p) ++{ ++ const uint8_t *cp = p; ++ ++ return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + ++ ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); ++} + + int main(int argc, char *argv[]) + { +@@ -62,7 +69,7 @@ + } + + ilen = ftell(f); +- olen = get_unaligned_le32(&olen); ++ olen = getle32(&olen); + fclose(f); + + /* diff --git a/target/linux/patches/3.12.44/mtd-rootfs.patch b/target/linux/patches/3.12.44/mtd-rootfs.patch new file mode 100644 index 000000000..775d5fc80 --- /dev/null +++ b/target/linux/patches/3.12.44/mtd-rootfs.patch @@ -0,0 +1,26 @@ +diff -Nur linux-3.5.orig//drivers/mtd/mtdpart.c linux-3.5/drivers/mtd/mtdpart.c +--- linux-3.5.orig//drivers/mtd/mtdpart.c 2012-07-21 22:58:29.000000000 +0200 ++++ linux-3.5/drivers/mtd/mtdpart.c 2012-07-31 23:59:07.000000000 +0200 +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + + #include "mtdcore.h" + +@@ -637,6 +638,14 @@ + if (IS_ERR(slave)) + return PTR_ERR(slave); + ++ if (strcmp(parts[i].name, "rootfs") == 0) { ++ if (ROOT_DEV == 0) { ++ printk(KERN_NOTICE "mtd: partition \"rootfs\" " ++ "set to be root filesystem\n"); ++ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, i); ++ } ++ } ++ + mutex_lock(&mtd_partitions_mutex); + list_add(&slave->list, &mtd_partitions); + mutex_unlock(&mtd_partitions_mutex); diff --git a/target/linux/patches/3.12.44/non-static.patch b/target/linux/patches/3.12.44/non-static.patch new file mode 100644 index 000000000..a967703d0 --- /dev/null +++ b/target/linux/patches/3.12.44/non-static.patch @@ -0,0 +1,33 @@ +diff -Nur linux-2.6.39-rc6.orig/fs/namei.c linux-2.6.39-rc6/fs/namei.c +--- linux-2.6.39-rc6.orig/fs/namei.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6/fs/namei.c 2011-05-05 11:30:14.000000000 +0200 +@@ -1769,7 +1769,7 @@ + * needs parent already locked. Doesn't follow mounts. + * SMP-safe. + */ +-static struct dentry *lookup_hash(struct nameidata *nd) ++struct dentry *lookup_hash(struct nameidata *nd) + { + return __lookup_hash(&nd->last, nd->path.dentry, nd); + } +diff -Nur linux-2.6.39-rc6.orig/fs/splice.c linux-2.6.39-rc6/fs/splice.c +--- linux-2.6.39-rc6.orig/fs/splice.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6/fs/splice.c 2011-05-05 11:31:04.000000000 +0200 +@@ -1081,7 +1081,7 @@ + /* + * Attempt to initiate a splice from pipe to file. + */ +-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) + { + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, +@@ -1109,7 +1109,7 @@ + /* + * Attempt to initiate a splice from a file to a pipe. + */ +-static long do_splice_to(struct file *in, loff_t *ppos, ++long do_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) + { diff --git a/target/linux/patches/3.12.44/ppc64-missing-zlib.patch b/target/linux/patches/3.12.44/ppc64-missing-zlib.patch new file mode 100644 index 000000000..c6e0616be --- /dev/null +++ b/target/linux/patches/3.12.44/ppc64-missing-zlib.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig +--- linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig 2013-11-01 15:23:09.000000000 +0100 +@@ -17,6 +17,7 @@ + select PPC_NATIVE + select PPC_PCI_CHOICE if EXPERT + select ZLIB_DEFLATE ++ select ZLIB_INFLATE + select PPC_DOORBELL + select HAVE_CONTEXT_TRACKING + select HOTPLUG_CPU if SMP diff --git a/target/linux/patches/3.12.44/regmap-bool.patch b/target/linux/patches/3.12.44/regmap-bool.patch new file mode 100644 index 000000000..c3fd9a318 --- /dev/null +++ b/target/linux/patches/3.12.44/regmap-bool.patch @@ -0,0 +1,20 @@ +diff -Nur linux-3.12.33.orig/drivers/base/regmap/Kconfig linux-3.12.33/drivers/base/regmap/Kconfig +--- linux-3.12.33.orig/drivers/base/regmap/Kconfig 2014-11-15 13:28:07.000000000 +0100 ++++ linux-3.12.33/drivers/base/regmap/Kconfig 2014-12-04 11:18:59.146540398 +0100 +@@ -10,13 +10,13 @@ + bool + + config REGMAP_I2C +- tristate ++ bool + + config REGMAP_SPI +- tristate ++ bool + + config REGMAP_MMIO +- tristate ++ bool + + config REGMAP_IRQ + bool diff --git a/target/linux/patches/3.12.44/relocs.patch b/target/linux/patches/3.12.44/relocs.patch new file mode 100644 index 000000000..649b9e73e --- /dev/null +++ b/target/linux/patches/3.12.44/relocs.patch @@ -0,0 +1,2693 @@ +diff -Nur linux-3.11.5.orig/arch/x86/tools/relocs.h linux-3.11.5/arch/x86/tools/relocs.h +--- linux-3.11.5.orig/arch/x86/tools/relocs.h 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/x86/tools/relocs.h 2013-10-20 17:41:16.000000000 +0200 +@@ -9,10 +9,14 @@ + #include + #include + #include ++#ifdef __linux__ + #include + #include + #define USE_BSD + #include ++#else ++#include "elf.h" ++#endif + #include + #include + +diff -Nur linux-3.11.5.orig/tools/include/elf.h linux-3.11.5/tools/include/elf.h +--- linux-3.11.5.orig/tools/include/elf.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.11.5/tools/include/elf.h 2013-10-20 17:42:10.000000000 +0200 +@@ -0,0 +1,2671 @@ ++#ifndef _ELF_H ++#define _ELF_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include ++ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++#define EI_NIDENT (16) ++ ++typedef struct { ++ unsigned char e_ident[EI_NIDENT]; ++ Elf32_Half e_type; ++ Elf32_Half e_machine; ++ Elf32_Word e_version; ++ Elf32_Addr e_entry; ++ Elf32_Off e_phoff; ++ Elf32_Off e_shoff; ++ Elf32_Word e_flags; ++ Elf32_Half e_ehsize; ++ Elf32_Half e_phentsize; ++ Elf32_Half e_phnum; ++ Elf32_Half e_shentsize; ++ Elf32_Half e_shnum; ++ Elf32_Half e_shstrndx; ++} Elf32_Ehdr; ++ ++typedef struct { ++ unsigned char e_ident[EI_NIDENT]; ++ Elf64_Half e_type; ++ Elf64_Half e_machine; ++ Elf64_Word e_version; ++ Elf64_Addr e_entry; ++ Elf64_Off e_phoff; ++ Elf64_Off e_shoff; ++ Elf64_Word e_flags; ++ Elf64_Half e_ehsize; ++ Elf64_Half e_phentsize; ++ Elf64_Half e_phnum; ++ Elf64_Half e_shentsize; ++ Elf64_Half e_shnum; ++ Elf64_Half e_shstrndx; ++} Elf64_Ehdr; ++ ++#define EI_MAG0 0 ++#define ELFMAG0 0x7f ++ ++#define EI_MAG1 1 ++#define ELFMAG1 'E' ++ ++#define EI_MAG2 2 ++#define ELFMAG2 'L' ++ ++#define EI_MAG3 3 ++#define ELFMAG3 'F' ++ ++ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 ++#define ELFCLASSNONE 0 ++#define ELFCLASS32 1 ++#define ELFCLASS64 2 ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 ++#define ELFDATANONE 0 ++#define ELFDATA2LSB 1 ++#define ELFDATA2MSB 2 ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 ++ ++ ++#define EI_OSABI 7 ++#define ELFOSABI_NONE 0 ++#define ELFOSABI_SYSV 0 ++#define ELFOSABI_HPUX 1 ++#define ELFOSABI_NETBSD 2 ++#define ELFOSABI_LINUX 3 ++#define ELFOSABI_GNU 3 ++#define ELFOSABI_SOLARIS 6 ++#define ELFOSABI_AIX 7 ++#define ELFOSABI_IRIX 8 ++#define ELFOSABI_FREEBSD 9 ++#define ELFOSABI_TRU64 10 ++#define ELFOSABI_MODESTO 11 ++#define ELFOSABI_OPENBSD 12 ++#define ELFOSABI_ARM 97 ++#define ELFOSABI_STANDALONE 255 ++ ++#define EI_ABIVERSION 8 ++ ++#define EI_PAD 9 ++ ++ ++ ++#define ET_NONE 0 ++#define ET_REL 1 ++#define ET_EXEC 2 ++#define ET_DYN 3 ++#define ET_CORE 4 ++#define ET_NUM 5 ++#define ET_LOOS 0xfe00 ++#define ET_HIOS 0xfeff ++#define ET_LOPROC 0xff00 ++#define ET_HIPROC 0xffff ++ ++ ++ ++#define EM_NONE 0 ++#define EM_M32 1 ++#define EM_SPARC 2 ++#define EM_386 3 ++#define EM_68K 4 ++#define EM_88K 5 ++#define EM_860 7 ++#define EM_MIPS 8 ++#define EM_S370 9 ++#define EM_MIPS_RS3_LE 10 ++ ++#define EM_PARISC 15 ++#define EM_VPP500 17 ++#define EM_SPARC32PLUS 18 ++#define EM_960 19 ++#define EM_PPC 20 ++#define EM_PPC64 21 ++#define EM_S390 22 ++ ++#define EM_V800 36 ++#define EM_FR20 37 ++#define EM_RH32 38 ++#define EM_RCE 39 ++#define EM_ARM 40 ++#define EM_FAKE_ALPHA 41 ++#define EM_SH 42 ++#define EM_SPARCV9 43 ++#define EM_TRICORE 44 ++#define EM_ARC 45 ++#define EM_H8_300 46 ++#define EM_H8_300H 47 ++#define EM_H8S 48 ++#define EM_H8_500 49 ++#define EM_IA_64 50 ++#define EM_MIPS_X 51 ++#define EM_COLDFIRE 52 ++#define EM_68HC12 53 ++#define EM_MMA 54 ++#define EM_PCP 55 ++#define EM_NCPU 56 ++#define EM_NDR1 57 ++#define EM_STARCORE 58 ++#define EM_ME16 59 ++#define EM_ST100 60 ++#define EM_TINYJ 61 ++#define EM_X86_64 62 ++#define EM_PDSP 63 ++ ++#define EM_FX66 66 ++#define EM_ST9PLUS 67 ++#define EM_ST7 68 ++#define EM_68HC16 69 ++#define EM_68HC11 70 ++#define EM_68HC08 71 ++#define EM_68HC05 72 ++#define EM_SVX 73 ++#define EM_ST19 74 ++#define EM_VAX 75 ++#define EM_CRIS 76 ++#define EM_JAVELIN 77 ++#define EM_FIREPATH 78 ++#define EM_ZSP 79 ++#define EM_MMIX 80 ++#define EM_HUANY 81 ++#define EM_PRISM 82 ++#define EM_AVR 83 ++#define EM_FR30 84 ++#define EM_D10V 85 ++#define EM_D30V 86 ++#define EM_V850 87 ++#define EM_M32R 88 ++#define EM_MN10300 89 ++#define EM_MN10200 90 ++#define EM_PJ 91 ++#define EM_OPENRISC 92 ++#define EM_ARC_A5 93 ++#define EM_XTENSA 94 ++#define EM_AARCH64 183 ++#define EM_TILEPRO 188 ++#define EM_MICROBLAZE 189 ++#define EM_TILEGX 191 ++#define EM_NUM 192 ++#define EM_ALPHA 0x9026 ++ ++#define EV_NONE 0 ++#define EV_CURRENT 1 ++#define EV_NUM 2 ++ ++typedef struct { ++ Elf32_Word sh_name; ++ Elf32_Word sh_type; ++ Elf32_Word sh_flags; ++ Elf32_Addr sh_addr; ++ Elf32_Off sh_offset; ++ Elf32_Word sh_size; ++ Elf32_Word sh_link; ++ Elf32_Word sh_info; ++ Elf32_Word sh_addralign; ++ Elf32_Word sh_entsize; ++} Elf32_Shdr; ++ ++typedef struct { ++ Elf64_Word sh_name; ++ Elf64_Word sh_type; ++ Elf64_Xword sh_flags; ++ Elf64_Addr sh_addr; ++ Elf64_Off sh_offset; ++ Elf64_Xword sh_size; ++ Elf64_Word sh_link; ++ Elf64_Word sh_info; ++ Elf64_Xword sh_addralign; ++ Elf64_Xword sh_entsize; ++} Elf64_Shdr; ++ ++ ++ ++#define SHN_UNDEF 0 ++#define SHN_LORESERVE 0xff00 ++#define SHN_LOPROC 0xff00 ++#define SHN_BEFORE 0xff00 ++ ++#define SHN_AFTER 0xff01 ++ ++#define SHN_HIPROC 0xff1f ++#define SHN_LOOS 0xff20 ++#define SHN_HIOS 0xff3f ++#define SHN_ABS 0xfff1 ++#define SHN_COMMON 0xfff2 ++#define SHN_XINDEX 0xffff ++#define SHN_HIRESERVE 0xffff ++ ++ ++ ++#define SHT_NULL 0 ++#define SHT_PROGBITS 1 ++#define SHT_SYMTAB 2 ++#define SHT_STRTAB 3 ++#define SHT_RELA 4 ++#define SHT_HASH 5 ++#define SHT_DYNAMIC 6 ++#define SHT_NOTE 7 ++#define SHT_NOBITS 8 ++#define SHT_REL 9 ++#define SHT_SHLIB 10 ++#define SHT_DYNSYM 11 ++#define SHT_INIT_ARRAY 14 ++#define SHT_FINI_ARRAY 15 ++#define SHT_PREINIT_ARRAY 16 ++#define SHT_GROUP 17 ++#define SHT_SYMTAB_SHNDX 18 ++#define SHT_NUM 19 ++#define SHT_LOOS 0x60000000 ++#define SHT_GNU_ATTRIBUTES 0x6ffffff5 ++#define SHT_GNU_HASH 0x6ffffff6 ++#define SHT_GNU_LIBLIST 0x6ffffff7 ++#define SHT_CHECKSUM 0x6ffffff8 ++#define SHT_LOSUNW 0x6ffffffa ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd ++#define SHT_GNU_verneed 0x6ffffffe ++#define SHT_GNU_versym 0x6fffffff ++#define SHT_HISUNW 0x6fffffff ++#define SHT_HIOS 0x6fffffff ++#define SHT_LOPROC 0x70000000 ++#define SHT_HIPROC 0x7fffffff ++#define SHT_LOUSER 0x80000000 ++#define SHT_HIUSER 0x8fffffff ++ ++#define SHF_WRITE (1 << 0) ++#define SHF_ALLOC (1 << 1) ++#define SHF_EXECINSTR (1 << 2) ++#define SHF_MERGE (1 << 4) ++#define SHF_STRINGS (1 << 5) ++#define SHF_INFO_LINK (1 << 6) ++#define SHF_LINK_ORDER (1 << 7) ++#define SHF_OS_NONCONFORMING (1 << 8) ++ ++#define SHF_GROUP (1 << 9) ++#define SHF_TLS (1 << 10) ++#define SHF_MASKOS 0x0ff00000 ++#define SHF_MASKPROC 0xf0000000 ++#define SHF_ORDERED (1 << 30) ++#define SHF_EXCLUDE (1 << 31) ++ ++#define GRP_COMDAT 0x1 ++ ++typedef struct { ++ Elf32_Word st_name; ++ Elf32_Addr st_value; ++ Elf32_Word st_size; ++ unsigned char st_info; ++ unsigned char st_other; ++ Elf32_Section st_shndx; ++} Elf32_Sym; ++ ++typedef struct { ++ Elf64_Word st_name; ++ unsigned char st_info; ++ unsigned char st_other; ++ Elf64_Section st_shndx; ++ Elf64_Addr st_value; ++ Elf64_Xword st_size; ++} Elf64_Sym; ++ ++typedef struct { ++ Elf32_Half si_boundto; ++ Elf32_Half si_flags; ++} Elf32_Syminfo; ++ ++typedef struct { ++ Elf64_Half si_boundto; ++ Elf64_Half si_flags; ++} Elf64_Syminfo; ++ ++#define SYMINFO_BT_SELF 0xffff ++#define SYMINFO_BT_PARENT 0xfffe ++#define SYMINFO_BT_LOWRESERVE 0xff00 ++ ++#define SYMINFO_FLG_DIRECT 0x0001 ++#define SYMINFO_FLG_PASSTHRU 0x0002 ++#define SYMINFO_FLG_COPY 0x0004 ++#define SYMINFO_FLG_LAZYLOAD 0x0008 ++ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++#define STB_LOCAL 0 ++#define STB_GLOBAL 1 ++#define STB_WEAK 2 ++#define STB_NUM 3 ++#define STB_LOOS 10 ++#define STB_GNU_UNIQUE 10 ++#define STB_HIOS 12 ++#define STB_LOPROC 13 ++#define STB_HIPROC 15 ++ ++#define STT_NOTYPE 0 ++#define STT_OBJECT 1 ++#define STT_FUNC 2 ++#define STT_SECTION 3 ++#define STT_FILE 4 ++#define STT_COMMON 5 ++#define STT_TLS 6 ++#define STT_NUM 7 ++#define STT_LOOS 10 ++#define STT_GNU_IFUNC 10 ++#define STT_HIOS 12 ++#define STT_LOPROC 13 ++#define STT_HIPROC 15 ++ ++#define STN_UNDEF 0 ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++#define STV_DEFAULT 0 ++#define STV_INTERNAL 1 ++#define STV_HIDDEN 2 ++#define STV_PROTECTED 3 ++ ++ ++ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; ++ Elf32_Word r_info; ++} Elf32_Rel; ++ ++typedef struct { ++ Elf64_Addr r_offset; ++ Elf64_Xword r_info; ++} Elf64_Rel; ++ ++ ++ ++typedef struct { ++ Elf32_Addr r_offset; ++ Elf32_Word r_info; ++ Elf32_Sword r_addend; ++} Elf32_Rela; ++ ++typedef struct { ++ Elf64_Addr r_offset; ++ Elf64_Xword r_info; ++ Elf64_Sxword r_addend; ++} Elf64_Rela; ++ ++ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++ ++ ++typedef struct { ++ Elf32_Word p_type; ++ Elf32_Off p_offset; ++ Elf32_Addr p_vaddr; ++ Elf32_Addr p_paddr; ++ Elf32_Word p_filesz; ++ Elf32_Word p_memsz; ++ Elf32_Word p_flags; ++ Elf32_Word p_align; ++} Elf32_Phdr; ++ ++typedef struct { ++ Elf64_Word p_type; ++ Elf64_Word p_flags; ++ Elf64_Off p_offset; ++ Elf64_Addr p_vaddr; ++ Elf64_Addr p_paddr; ++ Elf64_Xword p_filesz; ++ Elf64_Xword p_memsz; ++ Elf64_Xword p_align; ++} Elf64_Phdr; ++ ++ ++ ++#define PT_NULL 0 ++#define PT_LOAD 1 ++#define PT_DYNAMIC 2 ++#define PT_INTERP 3 ++#define PT_NOTE 4 ++#define PT_SHLIB 5 ++#define PT_PHDR 6 ++#define PT_TLS 7 ++#define PT_NUM 8 ++#define PT_LOOS 0x60000000 ++#define PT_GNU_EH_FRAME 0x6474e550 ++#define PT_GNU_STACK 0x6474e551 ++#define PT_GNU_RELRO 0x6474e552 ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa ++#define PT_SUNWSTACK 0x6ffffffb ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff ++#define PT_LOPROC 0x70000000 ++#define PT_HIPROC 0x7fffffff ++ ++ ++#define PN_XNUM 0xffff ++ ++ ++#define PF_X (1 << 0) ++#define PF_W (1 << 1) ++#define PF_R (1 << 2) ++#define PF_MASKOS 0x0ff00000 ++#define PF_MASKPROC 0xf0000000 ++ ++ ++ ++#define NT_PRSTATUS 1 ++#define NT_FPREGSET 2 ++#define NT_PRPSINFO 3 ++#define NT_PRXREG 4 ++#define NT_TASKSTRUCT 4 ++#define NT_PLATFORM 5 ++#define NT_AUXV 6 ++#define NT_GWINDOWS 7 ++#define NT_ASRS 8 ++#define NT_PSTATUS 10 ++#define NT_PSINFO 13 ++#define NT_PRCRED 14 ++#define NT_UTSNAME 15 ++#define NT_LWPSTATUS 16 ++#define NT_LWPSINFO 17 ++#define NT_PRFPXREG 20 ++#define NT_SIGINFO 0x53494749 ++#define NT_FILE 0x46494c45 ++#define NT_PRXFPREG 0x46e62b7f ++#define NT_PPC_VMX 0x100 ++#define NT_PPC_SPE 0x101 ++#define NT_PPC_VSX 0x102 ++#define NT_386_TLS 0x200 ++#define NT_386_IOPERM 0x201 ++#define NT_X86_XSTATE 0x202 ++#define NT_S390_HIGH_GPRS 0x300 ++#define NT_S390_TIMER 0x301 ++#define NT_S390_TODCMP 0x302 ++#define NT_S390_TODPREG 0x303 ++#define NT_S390_CTRS 0x304 ++#define NT_S390_PREFIX 0x305 ++#define NT_S390_LAST_BREAK 0x306 ++#define NT_S390_SYSTEM_CALL 0x307 ++#define NT_S390_TDB 0x308 ++#define NT_ARM_VFP 0x400 ++#define NT_ARM_TLS 0x401 ++#define NT_ARM_HW_BREAK 0x402 ++#define NT_ARM_HW_WATCH 0x403 ++#define NT_METAG_CBUF 0x500 ++#define NT_METAG_RPIPE 0x501 ++#define NT_METAG_TLS 0x502 ++#define NT_VERSION 1 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Sword d_tag; ++ union { ++ Elf32_Word d_val; ++ Elf32_Addr d_ptr; ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct { ++ Elf64_Sxword d_tag; ++ union { ++ Elf64_Xword d_val; ++ Elf64_Addr d_ptr; ++ } d_un; ++} Elf64_Dyn; ++ ++ ++ ++#define DT_NULL 0 ++#define DT_NEEDED 1 ++#define DT_PLTRELSZ 2 ++#define DT_PLTGOT 3 ++#define DT_HASH 4 ++#define DT_STRTAB 5 ++#define DT_SYMTAB 6 ++#define DT_RELA 7 ++#define DT_RELASZ 8 ++#define DT_RELAENT 9 ++#define DT_STRSZ 10 ++#define DT_SYMENT 11 ++#define DT_INIT 12 ++#define DT_FINI 13 ++#define DT_SONAME 14 ++#define DT_RPATH 15 ++#define DT_SYMBOLIC 16 ++#define DT_REL 17 ++#define DT_RELSZ 18 ++#define DT_RELENT 19 ++#define DT_PLTREL 20 ++#define DT_DEBUG 21 ++#define DT_TEXTREL 22 ++#define DT_JMPREL 23 ++#define DT_BIND_NOW 24 ++#define DT_INIT_ARRAY 25 ++#define DT_FINI_ARRAY 26 ++#define DT_INIT_ARRAYSZ 27 ++#define DT_FINI_ARRAYSZ 28 ++#define DT_RUNPATH 29 ++#define DT_FLAGS 30 ++#define DT_ENCODING 32 ++#define DT_PREINIT_ARRAY 32 ++#define DT_PREINIT_ARRAYSZ 33 ++#define DT_NUM 34 ++#define DT_LOOS 0x6000000d ++#define DT_HIOS 0x6ffff000 ++#define DT_LOPROC 0x70000000 ++#define DT_HIPROC 0x7fffffff ++#define DT_PROCNUM DT_MIPS_NUM ++ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc ++#define DT_POSFLAG_1 0x6ffffdfd ++ ++#define DT_SYMINSZ 0x6ffffdfe ++#define DT_SYMINENT 0x6ffffdff ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) ++#define DT_VALNUM 12 ++ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_HASH 0x6ffffef5 ++#define DT_TLSDESC_PLT 0x6ffffef6 ++#define DT_TLSDESC_GOT 0x6ffffef7 ++#define DT_GNU_CONFLICT 0x6ffffef8 ++#define DT_GNU_LIBLIST 0x6ffffef9 ++#define DT_CONFIG 0x6ffffefa ++#define DT_DEPAUDIT 0x6ffffefb ++#define DT_AUDIT 0x6ffffefc ++#define DT_PLTPAD 0x6ffffefd ++#define DT_MOVETAB 0x6ffffefe ++#define DT_SYMINFO 0x6ffffeff ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) ++#define DT_ADDRNUM 11 ++ ++ ++ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++ ++#define DT_FLAGS_1 0x6ffffffb ++#define DT_VERDEF 0x6ffffffc ++ ++#define DT_VERDEFNUM 0x6ffffffd ++#define DT_VERNEED 0x6ffffffe ++ ++#define DT_VERNEEDNUM 0x6fffffff ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) ++#define DT_VERSIONTAGNUM 16 ++ ++ ++ ++#define DT_AUXILIARY 0x7ffffffd ++#define DT_FILTER 0x7fffffff ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++ ++#define DF_ORIGIN 0x00000001 ++#define DF_SYMBOLIC 0x00000002 ++#define DF_TEXTREL 0x00000004 ++#define DF_BIND_NOW 0x00000008 ++#define DF_STATIC_TLS 0x00000010 ++ ++ ++ ++#define DF_1_NOW 0x00000001 ++#define DF_1_GLOBAL 0x00000002 ++#define DF_1_GROUP 0x00000004 ++#define DF_1_NODELETE 0x00000008 ++#define DF_1_LOADFLTR 0x00000010 ++#define DF_1_INITFIRST 0x00000020 ++#define DF_1_NOOPEN 0x00000040 ++#define DF_1_ORIGIN 0x00000080 ++#define DF_1_DIRECT 0x00000100 ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 ++#define DF_1_NODEFLIB 0x00000800 ++#define DF_1_NODUMP 0x00001000 ++#define DF_1_CONFALT 0x00002000 ++#define DF_1_ENDFILTEE 0x00004000 ++#define DF_1_DISPRELDNE 0x00008000 ++#define DF_1_DISPRELPND 0x00010000 ++#define DF_1_NODIRECT 0x00020000 ++#define DF_1_IGNMULDEF 0x00040000 ++#define DF_1_NOKSYMS 0x00080000 ++#define DF_1_NOHDR 0x00100000 ++#define DF_1_EDITED 0x00200000 ++#define DF_1_NORELOC 0x00400000 ++#define DF_1_SYMINTPOSE 0x00800000 ++#define DF_1_GLOBAUDIT 0x01000000 ++#define DF_1_SINGLETON 0x02000000 ++ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++ ++#define DF_P1_LAZYLOAD 0x00000001 ++#define DF_P1_GROUPPERM 0x00000002 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Half vd_version; ++ Elf32_Half vd_flags; ++ Elf32_Half vd_ndx; ++ Elf32_Half vd_cnt; ++ Elf32_Word vd_hash; ++ Elf32_Word vd_aux; ++ Elf32_Word vd_next; ++} Elf32_Verdef; ++ ++typedef struct { ++ Elf64_Half vd_version; ++ Elf64_Half vd_flags; ++ Elf64_Half vd_ndx; ++ Elf64_Half vd_cnt; ++ Elf64_Word vd_hash; ++ Elf64_Word vd_aux; ++ Elf64_Word vd_next; ++} Elf64_Verdef; ++ ++ ++ ++#define VER_DEF_NONE 0 ++#define VER_DEF_CURRENT 1 ++#define VER_DEF_NUM 2 ++ ++ ++#define VER_FLG_BASE 0x1 ++#define VER_FLG_WEAK 0x2 ++ ++ ++#define VER_NDX_LOCAL 0 ++#define VER_NDX_GLOBAL 1 ++#define VER_NDX_LORESERVE 0xff00 ++#define VER_NDX_ELIMINATE 0xff01 ++ ++ ++ ++typedef struct { ++ Elf32_Word vda_name; ++ Elf32_Word vda_next; ++} Elf32_Verdaux; ++ ++typedef struct { ++ Elf64_Word vda_name; ++ Elf64_Word vda_next; ++} Elf64_Verdaux; ++ ++ ++ ++ ++typedef struct { ++ Elf32_Half vn_version; ++ Elf32_Half vn_cnt; ++ Elf32_Word vn_file; ++ Elf32_Word vn_aux; ++ Elf32_Word vn_next; ++} Elf32_Verneed; ++ ++typedef struct { ++ Elf64_Half vn_version; ++ Elf64_Half vn_cnt; ++ Elf64_Word vn_file; ++ Elf64_Word vn_aux; ++ Elf64_Word vn_next; ++} Elf64_Verneed; ++ ++ ++ ++#define VER_NEED_NONE 0 ++#define VER_NEED_CURRENT 1 ++#define VER_NEED_NUM 2 ++ ++ ++ ++typedef struct { ++ Elf32_Word vna_hash; ++ Elf32_Half vna_flags; ++ Elf32_Half vna_other; ++ Elf32_Word vna_name; ++ Elf32_Word vna_next; ++} Elf32_Vernaux; ++ ++typedef struct { ++ Elf64_Word vna_hash; ++ Elf64_Half vna_flags; ++ Elf64_Half vna_other; ++ Elf64_Word vna_name; ++ Elf64_Word vna_next; ++} Elf64_Vernaux; ++ ++ ++ ++#define VER_FLG_WEAK 0x2 ++ ++ ++ ++typedef struct { ++ uint32_t a_type; ++ union { ++ uint32_t a_val; ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct { ++ uint64_t a_type; ++ union { ++ uint64_t a_val; ++ } a_un; ++} Elf64_auxv_t; ++ ++ ++ ++#define AT_NULL 0 ++#define AT_IGNORE 1 ++#define AT_EXECFD 2 ++#define AT_PHDR 3 ++#define AT_PHENT 4 ++#define AT_PHNUM 5 ++#define AT_PAGESZ 6 ++#define AT_BASE 7 ++#define AT_FLAGS 8 ++#define AT_ENTRY 9 ++#define AT_NOTELF 10 ++#define AT_UID 11 ++#define AT_EUID 12 ++#define AT_GID 13 ++#define AT_EGID 14 ++#define AT_CLKTCK 17 ++ ++ ++#define AT_PLATFORM 15 ++#define AT_HWCAP 16 ++ ++ ++ ++ ++#define AT_FPUCW 18 ++ ++ ++#define AT_DCACHEBSIZE 19 ++#define AT_ICACHEBSIZE 20 ++#define AT_UCACHEBSIZE 21 ++ ++ ++ ++#define AT_IGNOREPPC 22 ++ ++#define AT_SECURE 23 ++ ++#define AT_BASE_PLATFORM 24 ++ ++#define AT_RANDOM 25 ++ ++#define AT_HWCAP2 26 ++ ++#define AT_EXECFN 31 ++ ++ ++ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++ ++ ++#define AT_L1I_CACHESHAPE 34 ++#define AT_L1D_CACHESHAPE 35 ++#define AT_L2_CACHESHAPE 36 ++#define AT_L3_CACHESHAPE 37 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Word n_namesz; ++ Elf32_Word n_descsz; ++ Elf32_Word n_type; ++} Elf32_Nhdr; ++ ++typedef struct { ++ Elf64_Word n_namesz; ++ Elf64_Word n_descsz; ++ Elf64_Word n_type; ++} Elf64_Nhdr; ++ ++ ++ ++ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++ ++ ++ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++#define NT_GNU_ABI_TAG 1 ++#define ELF_NOTE_ABI NT_GNU_ABI_TAG ++ ++ ++ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++#define NT_GNU_BUILD_ID 3 ++#define NT_GNU_GOLD_VERSION 4 ++ ++ ++ ++typedef struct { ++ Elf32_Xword m_value; ++ Elf32_Word m_info; ++ Elf32_Word m_poffset; ++ Elf32_Half m_repeat; ++ Elf32_Half m_stride; ++} Elf32_Move; ++ ++typedef struct { ++ Elf64_Xword m_value; ++ Elf64_Xword m_info; ++ Elf64_Xword m_poffset; ++ Elf64_Half m_repeat; ++ Elf64_Half m_stride; ++} Elf64_Move; ++ ++ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++#define EF_CPU32 0x00810000 ++ ++#define R_68K_NONE 0 ++#define R_68K_32 1 ++#define R_68K_16 2 ++#define R_68K_8 3 ++#define R_68K_PC32 4 ++#define R_68K_PC16 5 ++#define R_68K_PC8 6 ++#define R_68K_GOT32 7 ++#define R_68K_GOT16 8 ++#define R_68K_GOT8 9 ++#define R_68K_GOT32O 10 ++#define R_68K_GOT16O 11 ++#define R_68K_GOT8O 12 ++#define R_68K_PLT32 13 ++#define R_68K_PLT16 14 ++#define R_68K_PLT8 15 ++#define R_68K_PLT32O 16 ++#define R_68K_PLT16O 17 ++#define R_68K_PLT8O 18 ++#define R_68K_COPY 19 ++#define R_68K_GLOB_DAT 20 ++#define R_68K_JMP_SLOT 21 ++#define R_68K_RELATIVE 22 ++#define R_68K_NUM 23 ++ ++#define R_386_NONE 0 ++#define R_386_32 1 ++#define R_386_PC32 2 ++#define R_386_GOT32 3 ++#define R_386_PLT32 4 ++#define R_386_COPY 5 ++#define R_386_GLOB_DAT 6 ++#define R_386_JMP_SLOT 7 ++#define R_386_RELATIVE 8 ++#define R_386_GOTOFF 9 ++#define R_386_GOTPC 10 ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 ++#define R_386_TLS_IE 15 ++#define R_386_TLS_GOTIE 16 ++#define R_386_TLS_LE 17 ++#define R_386_TLS_GD 18 ++#define R_386_TLS_LDM 19 ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 ++#define R_386_TLS_GD_PUSH 25 ++#define R_386_TLS_GD_CALL 26 ++#define R_386_TLS_GD_POP 27 ++#define R_386_TLS_LDM_32 28 ++#define R_386_TLS_LDM_PUSH 29 ++#define R_386_TLS_LDM_CALL 30 ++#define R_386_TLS_LDM_POP 31 ++#define R_386_TLS_LDO_32 32 ++#define R_386_TLS_IE_32 33 ++#define R_386_TLS_LE_32 34 ++#define R_386_TLS_DTPMOD32 35 ++#define R_386_TLS_DTPOFF32 36 ++#define R_386_TLS_TPOFF32 37 ++#define R_386_SIZE32 38 ++#define R_386_TLS_GOTDESC 39 ++#define R_386_TLS_DESC_CALL 40 ++#define R_386_TLS_DESC 41 ++#define R_386_IRELATIVE 42 ++#define R_386_NUM 43 ++ ++ ++ ++ ++ ++#define STT_SPARC_REGISTER 13 ++ ++ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 ++#define EF_SPARC_SUN_US1 0x000200 ++#define EF_SPARC_HAL_R1 0x000400 ++#define EF_SPARC_SUN_US3 0x000800 ++ ++ ++ ++#define R_SPARC_NONE 0 ++#define R_SPARC_8 1 ++#define R_SPARC_16 2 ++#define R_SPARC_32 3 ++#define R_SPARC_DISP8 4 ++#define R_SPARC_DISP16 5 ++#define R_SPARC_DISP32 6 ++#define R_SPARC_WDISP30 7 ++#define R_SPARC_WDISP22 8 ++#define R_SPARC_HI22 9 ++#define R_SPARC_22 10 ++#define R_SPARC_13 11 ++#define R_SPARC_LO10 12 ++#define R_SPARC_GOT10 13 ++#define R_SPARC_GOT13 14 ++#define R_SPARC_GOT22 15 ++#define R_SPARC_PC10 16 ++#define R_SPARC_PC22 17 ++#define R_SPARC_WPLT30 18 ++#define R_SPARC_COPY 19 ++#define R_SPARC_GLOB_DAT 20 ++#define R_SPARC_JMP_SLOT 21 ++#define R_SPARC_RELATIVE 22 ++#define R_SPARC_UA32 23 ++ ++ ++ ++#define R_SPARC_PLT32 24 ++#define R_SPARC_HIPLT22 25 ++#define R_SPARC_LOPLT10 26 ++#define R_SPARC_PCPLT32 27 ++#define R_SPARC_PCPLT22 28 ++#define R_SPARC_PCPLT10 29 ++#define R_SPARC_10 30 ++#define R_SPARC_11 31 ++#define R_SPARC_64 32 ++#define R_SPARC_OLO10 33 ++#define R_SPARC_HH22 34 ++#define R_SPARC_HM10 35 ++#define R_SPARC_LM22 36 ++#define R_SPARC_PC_HH22 37 ++#define R_SPARC_PC_HM10 38 ++#define R_SPARC_PC_LM22 39 ++#define R_SPARC_WDISP16 40 ++#define R_SPARC_WDISP19 41 ++#define R_SPARC_GLOB_JMP 42 ++#define R_SPARC_7 43 ++#define R_SPARC_5 44 ++#define R_SPARC_6 45 ++#define R_SPARC_DISP64 46 ++#define R_SPARC_PLT64 47 ++#define R_SPARC_HIX22 48 ++#define R_SPARC_LOX10 49 ++#define R_SPARC_H44 50 ++#define R_SPARC_M44 51 ++#define R_SPARC_L44 52 ++#define R_SPARC_REGISTER 53 ++#define R_SPARC_UA64 54 ++#define R_SPARC_UA16 55 ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++#define R_SPARC_GOTDATA_HIX22 80 ++#define R_SPARC_GOTDATA_LOX10 81 ++#define R_SPARC_GOTDATA_OP_HIX22 82 ++#define R_SPARC_GOTDATA_OP_LOX10 83 ++#define R_SPARC_GOTDATA_OP 84 ++#define R_SPARC_H34 85 ++#define R_SPARC_SIZE32 86 ++#define R_SPARC_SIZE64 87 ++#define R_SPARC_GNU_VTINHERIT 250 ++#define R_SPARC_GNU_VTENTRY 251 ++#define R_SPARC_REV32 252 ++ ++#define R_SPARC_NUM 253 ++ ++ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++ ++#define EF_MIPS_NOREORDER 1 ++#define EF_MIPS_PIC 2 ++#define EF_MIPS_CPIC 4 ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 ++ ++ ++ ++#define EF_MIPS_ARCH_1 0x00000000 ++#define EF_MIPS_ARCH_2 0x10000000 ++#define EF_MIPS_ARCH_3 0x20000000 ++#define EF_MIPS_ARCH_4 0x30000000 ++#define EF_MIPS_ARCH_5 0x40000000 ++#define EF_MIPS_ARCH_32 0x50000000 ++#define EF_MIPS_ARCH_64 0x60000000 ++#define EF_MIPS_ARCH_32R2 0x70000000 ++#define EF_MIPS_ARCH_64R2 0x80000000 ++ ++ ++#define E_MIPS_ARCH_1 0x00000000 ++#define E_MIPS_ARCH_2 0x10000000 ++#define E_MIPS_ARCH_3 0x20000000 ++#define E_MIPS_ARCH_4 0x30000000 ++#define E_MIPS_ARCH_5 0x40000000 ++#define E_MIPS_ARCH_32 0x50000000 ++#define E_MIPS_ARCH_64 0x60000000 ++ ++ ++ ++#define SHN_MIPS_ACOMMON 0xff00 ++#define SHN_MIPS_TEXT 0xff01 ++#define SHN_MIPS_DATA 0xff02 ++#define SHN_MIPS_SCOMMON 0xff03 ++#define SHN_MIPS_SUNDEFINED 0xff04 ++ ++ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 ++#define SHT_MIPS_GPTAB 0x70000003 ++#define SHT_MIPS_UCODE 0x70000004 ++#define SHT_MIPS_DEBUG 0x70000005 ++#define SHT_MIPS_REGINFO 0x70000006 ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++ ++ ++#define SHF_MIPS_GPREL 0x10000000 ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++ ++ ++ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_PLT 0x8 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++ ++ ++typedef union { ++ struct { ++ Elf32_Word gt_current_g_value; ++ Elf32_Word gt_unused; ++ } gt_header; ++ struct { ++ Elf32_Word gt_g_value; ++ Elf32_Word gt_bytes; ++ } gt_entry; ++} Elf32_gptab; ++ ++ ++ ++typedef struct { ++ Elf32_Word ri_gprmask; ++ Elf32_Word ri_cprmask[4]; ++ Elf32_Sword ri_gp_value; ++} Elf32_RegInfo; ++ ++ ++ ++typedef struct { ++ unsigned char kind; ++ ++ unsigned char size; ++ Elf32_Section section; ++ ++ Elf32_Word info; ++} Elf_Options; ++ ++ ++ ++#define ODK_NULL 0 ++#define ODK_REGINFO 1 ++#define ODK_EXCEPTIONS 2 ++#define ODK_PAD 3 ++#define ODK_HWPATCH 4 ++#define ODK_FILL 5 ++#define ODK_TAGS 6 ++#define ODK_HWAND 7 ++#define ODK_HWOR 8 ++ ++ ++ ++#define OEX_FPU_MIN 0x1f ++#define OEX_FPU_MAX 0x1f00 ++#define OEX_PAGE0 0x10000 ++#define OEX_SMM 0x20000 ++#define OEX_FPDBUG 0x40000 ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++ ++ ++#define OHW_R4KEOP 0x1 ++#define OHW_R8KPFETCH 0x2 ++#define OHW_R5KEOP 0x4 ++#define OHW_R5KCVTL 0x8 ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++ ++ ++typedef struct { ++ Elf32_Word hwp_flags1; ++ Elf32_Word hwp_flags2; ++} Elf_Options_Hw; ++ ++ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++ ++ ++#define R_MIPS_NONE 0 ++#define R_MIPS_16 1 ++#define R_MIPS_32 2 ++#define R_MIPS_REL32 3 ++#define R_MIPS_26 4 ++#define R_MIPS_HI16 5 ++#define R_MIPS_LO16 6 ++#define R_MIPS_GPREL16 7 ++#define R_MIPS_LITERAL 8 ++#define R_MIPS_GOT16 9 ++#define R_MIPS_PC16 10 ++#define R_MIPS_CALL16 11 ++#define R_MIPS_GPREL32 12 ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++#define R_MIPS_TLS_DTPMOD32 38 ++#define R_MIPS_TLS_DTPREL32 39 ++#define R_MIPS_TLS_DTPMOD64 40 ++#define R_MIPS_TLS_DTPREL64 41 ++#define R_MIPS_TLS_GD 42 ++#define R_MIPS_TLS_LDM 43 ++#define R_MIPS_TLS_DTPREL_HI16 44 ++#define R_MIPS_TLS_DTPREL_LO16 45 ++#define R_MIPS_TLS_GOTTPREL 46 ++#define R_MIPS_TLS_TPREL32 47 ++#define R_MIPS_TLS_TPREL64 48 ++#define R_MIPS_TLS_TPREL_HI16 49 ++#define R_MIPS_TLS_TPREL_LO16 50 ++#define R_MIPS_GLOB_DAT 51 ++#define R_MIPS_COPY 126 ++#define R_MIPS_JUMP_SLOT 127 ++ ++#define R_MIPS_NUM 128 ++ ++ ++ ++#define PT_MIPS_REGINFO 0x70000000 ++#define PT_MIPS_RTPROC 0x70000001 ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 ++#define DT_MIPS_TIME_STAMP 0x70000002 ++#define DT_MIPS_ICHECKSUM 0x70000003 ++#define DT_MIPS_IVERSION 0x70000004 ++#define DT_MIPS_FLAGS 0x70000005 ++#define DT_MIPS_BASE_ADDRESS 0x70000006 ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 ++#define DT_MIPS_LIBLIST 0x70000009 ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a ++#define DT_MIPS_CONFLICTNO 0x7000000b ++#define DT_MIPS_LIBLISTNO 0x70000010 ++#define DT_MIPS_SYMTABNO 0x70000011 ++#define DT_MIPS_UNREFEXTNO 0x70000012 ++#define DT_MIPS_GOTSYM 0x70000013 ++#define DT_MIPS_HIPAGENO 0x70000014 ++#define DT_MIPS_RLD_MAP 0x70000016 ++#define DT_MIPS_DELTA_CLASS 0x70000017 ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 ++ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a ++ ++#define DT_MIPS_DELTA_RELOC 0x7000001b ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c ++ ++#define DT_MIPS_DELTA_SYM 0x7000001d ++ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e ++ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 ++ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 ++ ++#define DT_MIPS_CXX_FLAGS 0x70000022 ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 ++#define DT_MIPS_INTERFACE 0x7000002a ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d ++ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e ++ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f ++#define DT_MIPS_GP_VALUE 0x70000030 ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 ++ ++#define DT_MIPS_PLTGOT 0x70000032 ++ ++#define DT_MIPS_RWPLT 0x70000034 ++#define DT_MIPS_NUM 0x35 ++ ++ ++ ++#define RHF_NONE 0 ++#define RHF_QUICKSTART (1 << 0) ++#define RHF_NOTPOT (1 << 1) ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++ ++ ++typedef struct ++{ ++ Elf32_Word l_name; ++ Elf32_Word l_time_stamp; ++ Elf32_Word l_checksum; ++ Elf32_Word l_version; ++ Elf32_Word l_flags; ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; ++ Elf64_Word l_time_stamp; ++ Elf64_Word l_checksum; ++ Elf64_Word l_version; ++ Elf64_Word l_flags; ++} Elf64_Lib; ++ ++ ++ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) ++#define LL_IGNORE_INT_VER (1 << 1) ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++ ++ ++ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 ++#define EF_PARISC_EXT 0x00020000 ++#define EF_PARISC_LSB 0x00040000 ++#define EF_PARISC_WIDE 0x00080000 ++#define EF_PARISC_NO_KABP 0x00100000 ++ ++#define EF_PARISC_LAZYSWAP 0x00400000 ++#define EF_PARISC_ARCH 0x0000ffff ++ ++ ++ ++#define EFA_PARISC_1_0 0x020b ++#define EFA_PARISC_1_1 0x0210 ++#define EFA_PARISC_2_0 0x0214 ++ ++ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 ++ ++#define SHN_PARISC_HUGE_COMMON 0xff01 ++ ++ ++ ++#define SHT_PARISC_EXT 0x70000000 ++#define SHT_PARISC_UNWIND 0x70000001 ++#define SHT_PARISC_DOC 0x70000002 ++ ++ ++ ++#define SHF_PARISC_SHORT 0x20000000 ++#define SHF_PARISC_HUGE 0x40000000 ++#define SHF_PARISC_SBP 0x80000000 ++ ++ ++ ++#define STT_PARISC_MILLICODE 13 ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++ ++ ++#define R_PARISC_NONE 0 ++#define R_PARISC_DIR32 1 ++#define R_PARISC_DIR21L 2 ++#define R_PARISC_DIR17R 3 ++#define R_PARISC_DIR17F 4 ++#define R_PARISC_DIR14R 6 ++#define R_PARISC_PCREL32 9 ++#define R_PARISC_PCREL21L 10 ++#define R_PARISC_PCREL17R 11 ++#define R_PARISC_PCREL17F 12 ++#define R_PARISC_PCREL14R 14 ++#define R_PARISC_DPREL21L 18 ++#define R_PARISC_DPREL14R 22 ++#define R_PARISC_GPREL21L 26 ++#define R_PARISC_GPREL14R 30 ++#define R_PARISC_LTOFF21L 34 ++#define R_PARISC_LTOFF14R 38 ++#define R_PARISC_SECREL32 41 ++#define R_PARISC_SEGBASE 48 ++#define R_PARISC_SEGREL32 49 ++#define R_PARISC_PLTOFF21L 50 ++#define R_PARISC_PLTOFF14R 54 ++#define R_PARISC_LTOFF_FPTR32 57 ++#define R_PARISC_LTOFF_FPTR21L 58 ++#define R_PARISC_LTOFF_FPTR14R 62 ++#define R_PARISC_FPTR64 64 ++#define R_PARISC_PLABEL32 65 ++#define R_PARISC_PLABEL21L 66 ++#define R_PARISC_PLABEL14R 70 ++#define R_PARISC_PCREL64 72 ++#define R_PARISC_PCREL22F 74 ++#define R_PARISC_PCREL14WR 75 ++#define R_PARISC_PCREL14DR 76 ++#define R_PARISC_PCREL16F 77 ++#define R_PARISC_PCREL16WF 78 ++#define R_PARISC_PCREL16DF 79 ++#define R_PARISC_DIR64 80 ++#define R_PARISC_DIR14WR 83 ++#define R_PARISC_DIR14DR 84 ++#define R_PARISC_DIR16F 85 ++#define R_PARISC_DIR16WF 86 ++#define R_PARISC_DIR16DF 87 ++#define R_PARISC_GPREL64 88 ++#define R_PARISC_GPREL14WR 91 ++#define R_PARISC_GPREL14DR 92 ++#define R_PARISC_GPREL16F 93 ++#define R_PARISC_GPREL16WF 94 ++#define R_PARISC_GPREL16DF 95 ++#define R_PARISC_LTOFF64 96 ++#define R_PARISC_LTOFF14WR 99 ++#define R_PARISC_LTOFF14DR 100 ++#define R_PARISC_LTOFF16F 101 ++#define R_PARISC_LTOFF16WF 102 ++#define R_PARISC_LTOFF16DF 103 ++#define R_PARISC_SECREL64 104 ++#define R_PARISC_SEGREL64 112 ++#define R_PARISC_PLTOFF14WR 115 ++#define R_PARISC_PLTOFF14DR 116 ++#define R_PARISC_PLTOFF16F 117 ++#define R_PARISC_PLTOFF16WF 118 ++#define R_PARISC_PLTOFF16DF 119 ++#define R_PARISC_LTOFF_FPTR64 120 ++#define R_PARISC_LTOFF_FPTR14WR 123 ++#define R_PARISC_LTOFF_FPTR14DR 124 ++#define R_PARISC_LTOFF_FPTR16F 125 ++#define R_PARISC_LTOFF_FPTR16WF 126 ++#define R_PARISC_LTOFF_FPTR16DF 127 ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 ++#define R_PARISC_IPLT 129 ++#define R_PARISC_EPLT 130 ++#define R_PARISC_TPREL32 153 ++#define R_PARISC_TPREL21L 154 ++#define R_PARISC_TPREL14R 158 ++#define R_PARISC_LTOFF_TP21L 162 ++#define R_PARISC_LTOFF_TP14R 166 ++#define R_PARISC_LTOFF_TP14F 167 ++#define R_PARISC_TPREL64 216 ++#define R_PARISC_TPREL14WR 219 ++#define R_PARISC_TPREL14DR 220 ++#define R_PARISC_TPREL16F 221 ++#define R_PARISC_TPREL16WF 222 ++#define R_PARISC_TPREL16DF 223 ++#define R_PARISC_LTOFF_TP64 224 ++#define R_PARISC_LTOFF_TP14WR 227 ++#define R_PARISC_LTOFF_TP14DR 228 ++#define R_PARISC_LTOFF_TP16F 229 ++#define R_PARISC_LTOFF_TP16WF 230 ++#define R_PARISC_LTOFF_TP16DF 231 ++#define R_PARISC_GNU_VTENTRY 232 ++#define R_PARISC_GNU_VTINHERIT 233 ++#define R_PARISC_TLS_GD21L 234 ++#define R_PARISC_TLS_GD14R 235 ++#define R_PARISC_TLS_GDCALL 236 ++#define R_PARISC_TLS_LDM21L 237 ++#define R_PARISC_TLS_LDM14R 238 ++#define R_PARISC_TLS_LDMCALL 239 ++#define R_PARISC_TLS_LDO21L 240 ++#define R_PARISC_TLS_LDO14R 241 ++#define R_PARISC_TLS_DTPMOD32 242 ++#define R_PARISC_TLS_DTPMOD64 243 ++#define R_PARISC_TLS_DTPOFF32 244 ++#define R_PARISC_TLS_DTPOFF64 245 ++#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L ++#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R ++#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L ++#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R ++#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 ++#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 ++#define R_PARISC_HIRESERVE 255 ++ ++ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++ ++ ++ ++ ++#define EF_ALPHA_32BIT 1 ++#define EF_ALPHA_CANRELAX 2 ++ ++ ++ ++ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++ ++#define STO_ALPHA_NOPV 0x80 ++#define STO_ALPHA_STD_GPLOAD 0x88 ++ ++ ++ ++#define R_ALPHA_NONE 0 ++#define R_ALPHA_REFLONG 1 ++#define R_ALPHA_REFQUAD 2 ++#define R_ALPHA_GPREL32 3 ++#define R_ALPHA_LITERAL 4 ++#define R_ALPHA_LITUSE 5 ++#define R_ALPHA_GPDISP 6 ++#define R_ALPHA_BRADDR 7 ++#define R_ALPHA_HINT 8 ++#define R_ALPHA_SREL16 9 ++#define R_ALPHA_SREL32 10 ++#define R_ALPHA_SREL64 11 ++#define R_ALPHA_GPRELHIGH 17 ++#define R_ALPHA_GPRELLOW 18 ++#define R_ALPHA_GPREL16 19 ++#define R_ALPHA_COPY 24 ++#define R_ALPHA_GLOB_DAT 25 ++#define R_ALPHA_JMP_SLOT 26 ++#define R_ALPHA_RELATIVE 27 ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++ ++#define R_ALPHA_NUM 46 ++ ++ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++ ++#define DT_ALPHA_PLTRO (DT_LOPROC + 0) ++#define DT_ALPHA_NUM 1 ++ ++ ++ ++ ++#define EF_PPC_EMB 0x80000000 ++ ++ ++#define EF_PPC_RELOCATABLE 0x00010000 ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 ++ ++ ++ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 ++#define R_PPC_ADDR24 2 ++#define R_PPC_ADDR16 3 ++#define R_PPC_ADDR16_LO 4 ++#define R_PPC_ADDR16_HI 5 ++#define R_PPC_ADDR16_HA 6 ++#define R_PPC_ADDR14 7 ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 ++#define R_PPC_REL14 11 ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++ ++#define R_PPC_TLS 67 ++#define R_PPC_DTPMOD32 68 ++#define R_PPC_TPREL16 69 ++#define R_PPC_TPREL16_LO 70 ++#define R_PPC_TPREL16_HI 71 ++#define R_PPC_TPREL16_HA 72 ++#define R_PPC_TPREL32 73 ++#define R_PPC_DTPREL16 74 ++#define R_PPC_DTPREL16_LO 75 ++#define R_PPC_DTPREL16_HI 76 ++#define R_PPC_DTPREL16_HA 77 ++#define R_PPC_DTPREL32 78 ++#define R_PPC_GOT_TLSGD16 79 ++#define R_PPC_GOT_TLSGD16_LO 80 ++#define R_PPC_GOT_TLSGD16_HI 81 ++#define R_PPC_GOT_TLSGD16_HA 82 ++#define R_PPC_GOT_TLSLD16 83 ++#define R_PPC_GOT_TLSLD16_LO 84 ++#define R_PPC_GOT_TLSLD16_HI 85 ++#define R_PPC_GOT_TLSLD16_HA 86 ++#define R_PPC_GOT_TPREL16 87 ++#define R_PPC_GOT_TPREL16_LO 88 ++#define R_PPC_GOT_TPREL16_HI 89 ++#define R_PPC_GOT_TPREL16_HA 90 ++#define R_PPC_GOT_DTPREL16 91 ++#define R_PPC_GOT_DTPREL16_LO 92 ++#define R_PPC_GOT_DTPREL16_HI 93 ++#define R_PPC_GOT_DTPREL16_HA 94 ++ ++ ++ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 ++ ++ ++#define R_PPC_DIAB_SDA21_LO 180 ++#define R_PPC_DIAB_SDA21_HI 181 ++#define R_PPC_DIAB_SDA21_HA 182 ++#define R_PPC_DIAB_RELSDA_LO 183 ++#define R_PPC_DIAB_RELSDA_HI 184 ++#define R_PPC_DIAB_RELSDA_HA 185 ++ ++ ++#define R_PPC_IRELATIVE 248 ++ ++ ++#define R_PPC_REL16 249 ++#define R_PPC_REL16_LO 250 ++#define R_PPC_REL16_HI 251 ++#define R_PPC_REL16_HA 252 ++ ++ ++ ++#define R_PPC_TOC16 255 ++ ++ ++#define DT_PPC_GOT (DT_LOPROC + 0) ++#define DT_PPC_NUM 1 ++ ++ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 ++#define R_PPC64_ADDR24 R_PPC_ADDR24 ++#define R_PPC64_ADDR16 R_PPC_ADDR16 ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA ++#define R_PPC64_ADDR14 R_PPC_ADDR14 ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 ++#define R_PPC64_REL14 R_PPC_REL14 ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 ++#define R_PPC64_ADDR64 38 ++#define R_PPC64_ADDR16_HIGHER 39 ++#define R_PPC64_ADDR16_HIGHERA 40 ++#define R_PPC64_ADDR16_HIGHEST 41 ++#define R_PPC64_ADDR16_HIGHESTA 42 ++#define R_PPC64_UADDR64 43 ++#define R_PPC64_REL64 44 ++#define R_PPC64_PLT64 45 ++#define R_PPC64_PLTREL64 46 ++#define R_PPC64_TOC16 47 ++#define R_PPC64_TOC16_LO 48 ++#define R_PPC64_TOC16_HI 49 ++#define R_PPC64_TOC16_HA 50 ++#define R_PPC64_TOC 51 ++#define R_PPC64_PLTGOT16 52 ++#define R_PPC64_PLTGOT16_LO 53 ++#define R_PPC64_PLTGOT16_HI 54 ++#define R_PPC64_PLTGOT16_HA 55 ++ ++#define R_PPC64_ADDR16_DS 56 ++#define R_PPC64_ADDR16_LO_DS 57 ++#define R_PPC64_GOT16_DS 58 ++#define R_PPC64_GOT16_LO_DS 59 ++#define R_PPC64_PLT16_LO_DS 60 ++#define R_PPC64_SECTOFF_DS 61 ++#define R_PPC64_SECTOFF_LO_DS 62 ++#define R_PPC64_TOC16_DS 63 ++#define R_PPC64_TOC16_LO_DS 64 ++#define R_PPC64_PLTGOT16_DS 65 ++#define R_PPC64_PLTGOT16_LO_DS 66 ++ ++ ++#define R_PPC64_TLS 67 ++#define R_PPC64_DTPMOD64 68 ++#define R_PPC64_TPREL16 69 ++#define R_PPC64_TPREL16_LO 70 ++#define R_PPC64_TPREL16_HI 71 ++#define R_PPC64_TPREL16_HA 72 ++#define R_PPC64_TPREL64 73 ++#define R_PPC64_DTPREL16 74 ++#define R_PPC64_DTPREL16_LO 75 ++#define R_PPC64_DTPREL16_HI 76 ++#define R_PPC64_DTPREL16_HA 77 ++#define R_PPC64_DTPREL64 78 ++#define R_PPC64_GOT_TLSGD16 79 ++#define R_PPC64_GOT_TLSGD16_LO 80 ++#define R_PPC64_GOT_TLSGD16_HI 81 ++#define R_PPC64_GOT_TLSGD16_HA 82 ++#define R_PPC64_GOT_TLSLD16 83 ++#define R_PPC64_GOT_TLSLD16_LO 84 ++#define R_PPC64_GOT_TLSLD16_HI 85 ++#define R_PPC64_GOT_TLSLD16_HA 86 ++#define R_PPC64_GOT_TPREL16_DS 87 ++#define R_PPC64_GOT_TPREL16_LO_DS 88 ++#define R_PPC64_GOT_TPREL16_HI 89 ++#define R_PPC64_GOT_TPREL16_HA 90 ++#define R_PPC64_GOT_DTPREL16_DS 91 ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 ++#define R_PPC64_GOT_DTPREL16_HI 93 ++#define R_PPC64_GOT_DTPREL16_HA 94 ++#define R_PPC64_TPREL16_DS 95 ++#define R_PPC64_TPREL16_LO_DS 96 ++#define R_PPC64_TPREL16_HIGHER 97 ++#define R_PPC64_TPREL16_HIGHERA 98 ++#define R_PPC64_TPREL16_HIGHEST 99 ++#define R_PPC64_TPREL16_HIGHESTA 100 ++#define R_PPC64_DTPREL16_DS 101 ++#define R_PPC64_DTPREL16_LO_DS 102 ++#define R_PPC64_DTPREL16_HIGHER 103 ++#define R_PPC64_DTPREL16_HIGHERA 104 ++#define R_PPC64_DTPREL16_HIGHEST 105 ++#define R_PPC64_DTPREL16_HIGHESTA 106 ++ ++ ++#define R_PPC64_JMP_IREL 247 ++#define R_PPC64_IRELATIVE 248 ++#define R_PPC64_REL16 249 ++#define R_PPC64_REL16_LO 250 ++#define R_PPC64_REL16_HI 251 ++#define R_PPC64_REL16_HA 252 ++ ++ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_OPD (DT_LOPROC + 1) ++#define DT_PPC64_OPDSZ (DT_LOPROC + 2) ++#define DT_PPC64_NUM 3 ++ ++ ++ ++ ++ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++#define EF_ARM_SOFT_FLOAT 0x200 ++#define EF_ARM_VFP_FLOAT 0x400 ++#define EF_ARM_MAVERICK_FLOAT 0x800 ++ ++#define EF_ARM_ABI_FLOAT_SOFT 0x200 ++#define EF_ARM_ABI_FLOAT_HARD 0x400 ++ ++ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++ ++#define EF_ARM_BE8 0x00800000 ++#define EF_ARM_LE8 0x00400000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++#define EF_ARM_EABI_VER3 0x03000000 ++#define EF_ARM_EABI_VER4 0x04000000 ++#define EF_ARM_EABI_VER5 0x05000000 ++ ++ ++#define STT_ARM_TFUNC STT_LOPROC ++#define STT_ARM_16BIT STT_HIPROC ++ ++ ++#define SHF_ARM_ENTRYSECT 0x10000000 ++#define SHF_ARM_COMDEF 0x80000000 ++ ++ ++ ++#define PF_ARM_SB 0x10000000 ++ ++#define PF_ARM_PI 0x20000000 ++#define PF_ARM_ABS 0x40000000 ++ ++ ++#define PT_ARM_EXIDX (PT_LOPROC + 1) ++ ++ ++#define SHT_ARM_EXIDX (SHT_LOPROC + 1) ++#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) ++#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) ++ ++ ++#define R_AARCH64_NONE 0 ++#define R_AARCH64_ABS64 257 ++#define R_AARCH64_ABS32 258 ++#define R_AARCH64_COPY 1024 ++#define R_AARCH64_GLOB_DAT 1025 ++#define R_AARCH64_JUMP_SLOT 1026 ++#define R_AARCH64_RELATIVE 1027 ++#define R_AARCH64_TLS_DTPMOD64 1028 ++#define R_AARCH64_TLS_DTPREL64 1029 ++#define R_AARCH64_TLS_TPREL64 1030 ++#define R_AARCH64_TLSDESC 1031 ++ ++ ++#define R_ARM_NONE 0 ++#define R_ARM_PC24 1 ++#define R_ARM_ABS32 2 ++#define R_ARM_REL32 3 ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 ++#define R_ARM_ABS12 6 ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_TLS_DESC 13 ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_TLS_DTPMOD32 17 ++#define R_ARM_TLS_DTPOFF32 18 ++#define R_ARM_TLS_TPOFF32 19 ++#define R_ARM_COPY 20 ++#define R_ARM_GLOB_DAT 21 ++#define R_ARM_JUMP_SLOT 22 ++#define R_ARM_RELATIVE 23 ++#define R_ARM_GOTOFF 24 ++#define R_ARM_GOTPC 25 ++#define R_ARM_GOT32 26 ++#define R_ARM_PLT32 27 ++#define R_ARM_CALL 28 ++#define R_ARM_JUMP24 29 ++#define R_ARM_THM_JUMP24 30 ++#define R_ARM_BASE_ABS 31 ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_TARGET1 38 ++#define R_ARM_SBREL31 39 ++#define R_ARM_V4BX 40 ++#define R_ARM_TARGET2 41 ++#define R_ARM_PREL31 42 ++#define R_ARM_MOVW_ABS_NC 43 ++#define R_ARM_MOVT_ABS 44 ++#define R_ARM_MOVW_PREL_NC 45 ++#define R_ARM_MOVT_PREL 46 ++#define R_ARM_THM_MOVW_ABS_NC 47 ++#define R_ARM_THM_MOVT_ABS 48 ++#define R_ARM_THM_MOVW_PREL_NC 49 ++#define R_ARM_THM_MOVT_PREL 50 ++#define R_ARM_THM_JUMP19 51 ++#define R_ARM_THM_JUMP6 52 ++#define R_ARM_THM_ALU_PREL_11_0 53 ++#define R_ARM_THM_PC12 54 ++#define R_ARM_ABS32_NOI 55 ++#define R_ARM_REL32_NOI 56 ++#define R_ARM_ALU_PC_G0_NC 57 ++#define R_ARM_ALU_PC_G0 58 ++#define R_ARM_ALU_PC_G1_NC 59 ++#define R_ARM_ALU_PC_G1 60 ++#define R_ARM_ALU_PC_G2 61 ++#define R_ARM_LDR_PC_G1 62 ++#define R_ARM_LDR_PC_G2 63 ++#define R_ARM_LDRS_PC_G0 64 ++#define R_ARM_LDRS_PC_G1 65 ++#define R_ARM_LDRS_PC_G2 66 ++#define R_ARM_LDC_PC_G0 67 ++#define R_ARM_LDC_PC_G1 68 ++#define R_ARM_LDC_PC_G2 69 ++#define R_ARM_ALU_SB_G0_NC 70 ++#define R_ARM_ALU_SB_G0 71 ++#define R_ARM_ALU_SB_G1_NC 72 ++#define R_ARM_ALU_SB_G1 73 ++#define R_ARM_ALU_SB_G2 74 ++#define R_ARM_LDR_SB_G0 75 ++#define R_ARM_LDR_SB_G1 76 ++#define R_ARM_LDR_SB_G2 77 ++#define R_ARM_LDRS_SB_G0 78 ++#define R_ARM_LDRS_SB_G1 79 ++#define R_ARM_LDRS_SB_G2 80 ++#define R_ARM_LDC_SB_G0 81 ++#define R_ARM_LDC_SB_G1 82 ++#define R_ARM_LDC_SB_G2 83 ++#define R_ARM_MOVW_BREL_NC 84 ++#define R_ARM_MOVT_BREL 85 ++#define R_ARM_MOVW_BREL 86 ++#define R_ARM_THM_MOVW_BREL_NC 87 ++#define R_ARM_THM_MOVT_BREL 88 ++#define R_ARM_THM_MOVW_BREL 89 ++#define R_ARM_TLS_GOTDESC 90 ++#define R_ARM_TLS_CALL 91 ++#define R_ARM_TLS_DESCSEQ 92 ++#define R_ARM_THM_TLS_CALL 93 ++#define R_ARM_PLT32_ABS 94 ++#define R_ARM_GOT_ABS 95 ++#define R_ARM_GOT_PREL 96 ++#define R_ARM_GOT_BREL12 97 ++#define R_ARM_GOTOFF12 98 ++#define R_ARM_GOTRELAX 99 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 ++#define R_ARM_THM_PC9 103 ++#define R_ARM_TLS_GD32 104 ++ ++#define R_ARM_TLS_LDM32 105 ++ ++#define R_ARM_TLS_LDO32 106 ++ ++#define R_ARM_TLS_IE32 107 ++ ++#define R_ARM_TLS_LE32 108 ++#define R_ARM_TLS_LDO12 109 ++#define R_ARM_TLS_LE12 110 ++#define R_ARM_TLS_IE12GP 111 ++#define R_ARM_ME_TOO 128 ++#define R_ARM_THM_TLS_DESCSEQ 129 ++#define R_ARM_THM_TLS_DESCSEQ16 129 ++#define R_ARM_THM_TLS_DESCSEQ32 130 ++#define R_ARM_THM_GOT_BREL12 131 ++#define R_ARM_IRELATIVE 160 ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++ ++#define R_ARM_NUM 256 ++ ++ ++ ++ ++#define EF_IA_64_MASKOS 0x0000000f ++#define EF_IA_64_ABI64 0x00000010 ++#define EF_IA_64_ARCH 0xff000000 ++ ++ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) ++#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) ++#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) ++#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) ++ ++ ++#define PF_IA_64_NORECOV 0x80000000 ++ ++ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) ++ ++ ++#define SHF_IA_64_SHORT 0x10000000 ++#define SHF_IA_64_NORECOV 0x20000000 ++ ++ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++ ++#define R_IA64_NONE 0x00 ++#define R_IA64_IMM14 0x21 ++#define R_IA64_IMM22 0x22 ++#define R_IA64_IMM64 0x23 ++#define R_IA64_DIR32MSB 0x24 ++#define R_IA64_DIR32LSB 0x25 ++#define R_IA64_DIR64MSB 0x26 ++#define R_IA64_DIR64LSB 0x27 ++#define R_IA64_GPREL22 0x2a ++#define R_IA64_GPREL64I 0x2b ++#define R_IA64_GPREL32MSB 0x2c ++#define R_IA64_GPREL32LSB 0x2d ++#define R_IA64_GPREL64MSB 0x2e ++#define R_IA64_GPREL64LSB 0x2f ++#define R_IA64_LTOFF22 0x32 ++#define R_IA64_LTOFF64I 0x33 ++#define R_IA64_PLTOFF22 0x3a ++#define R_IA64_PLTOFF64I 0x3b ++#define R_IA64_PLTOFF64MSB 0x3e ++#define R_IA64_PLTOFF64LSB 0x3f ++#define R_IA64_FPTR64I 0x43 ++#define R_IA64_FPTR32MSB 0x44 ++#define R_IA64_FPTR32LSB 0x45 ++#define R_IA64_FPTR64MSB 0x46 ++#define R_IA64_FPTR64LSB 0x47 ++#define R_IA64_PCREL60B 0x48 ++#define R_IA64_PCREL21B 0x49 ++#define R_IA64_PCREL21M 0x4a ++#define R_IA64_PCREL21F 0x4b ++#define R_IA64_PCREL32MSB 0x4c ++#define R_IA64_PCREL32LSB 0x4d ++#define R_IA64_PCREL64MSB 0x4e ++#define R_IA64_PCREL64LSB 0x4f ++#define R_IA64_LTOFF_FPTR22 0x52 ++#define R_IA64_LTOFF_FPTR64I 0x53 ++#define R_IA64_LTOFF_FPTR32MSB 0x54 ++#define R_IA64_LTOFF_FPTR32LSB 0x55 ++#define R_IA64_LTOFF_FPTR64MSB 0x56 ++#define R_IA64_LTOFF_FPTR64LSB 0x57 ++#define R_IA64_SEGREL32MSB 0x5c ++#define R_IA64_SEGREL32LSB 0x5d ++#define R_IA64_SEGREL64MSB 0x5e ++#define R_IA64_SEGREL64LSB 0x5f ++#define R_IA64_SECREL32MSB 0x64 ++#define R_IA64_SECREL32LSB 0x65 ++#define R_IA64_SECREL64MSB 0x66 ++#define R_IA64_SECREL64LSB 0x67 ++#define R_IA64_REL32MSB 0x6c ++#define R_IA64_REL32LSB 0x6d ++#define R_IA64_REL64MSB 0x6e ++#define R_IA64_REL64LSB 0x6f ++#define R_IA64_LTV32MSB 0x74 ++#define R_IA64_LTV32LSB 0x75 ++#define R_IA64_LTV64MSB 0x76 ++#define R_IA64_LTV64LSB 0x77 ++#define R_IA64_PCREL21BI 0x79 ++#define R_IA64_PCREL22 0x7a ++#define R_IA64_PCREL64I 0x7b ++#define R_IA64_IPLTMSB 0x80 ++#define R_IA64_IPLTLSB 0x81 ++#define R_IA64_COPY 0x84 ++#define R_IA64_SUB 0x85 ++#define R_IA64_LTOFF22X 0x86 ++#define R_IA64_LDXMOV 0x87 ++#define R_IA64_TPREL14 0x91 ++#define R_IA64_TPREL22 0x92 ++#define R_IA64_TPREL64I 0x93 ++#define R_IA64_TPREL64MSB 0x96 ++#define R_IA64_TPREL64LSB 0x97 ++#define R_IA64_LTOFF_TPREL22 0x9a ++#define R_IA64_DTPMOD64MSB 0xa6 ++#define R_IA64_DTPMOD64LSB 0xa7 ++#define R_IA64_LTOFF_DTPMOD22 0xaa ++#define R_IA64_DTPREL14 0xb1 ++#define R_IA64_DTPREL22 0xb2 ++#define R_IA64_DTPREL64I 0xb3 ++#define R_IA64_DTPREL32MSB 0xb4 ++#define R_IA64_DTPREL32LSB 0xb5 ++#define R_IA64_DTPREL64MSB 0xb6 ++#define R_IA64_DTPREL64LSB 0xb7 ++#define R_IA64_LTOFF_DTPREL22 0xba ++ ++ ++ ++ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++ ++#define R_SH_NUM 256 ++ ++ ++ ++#define R_390_NONE 0 ++#define R_390_8 1 ++#define R_390_12 2 ++#define R_390_16 3 ++#define R_390_32 4 ++#define R_390_PC32 5 ++#define R_390_GOT12 6 ++#define R_390_GOT32 7 ++#define R_390_PLT32 8 ++#define R_390_COPY 9 ++#define R_390_GLOB_DAT 10 ++#define R_390_JMP_SLOT 11 ++#define R_390_RELATIVE 12 ++#define R_390_GOTOFF32 13 ++#define R_390_GOTPC 14 ++#define R_390_GOT16 15 ++#define R_390_PC16 16 ++#define R_390_PC16DBL 17 ++#define R_390_PLT16DBL 18 ++#define R_390_PC32DBL 19 ++#define R_390_PLT32DBL 20 ++#define R_390_GOTPCDBL 21 ++#define R_390_64 22 ++#define R_390_PC64 23 ++#define R_390_GOT64 24 ++#define R_390_PLT64 25 ++#define R_390_GOTENT 26 ++#define R_390_GOTOFF16 27 ++#define R_390_GOTOFF64 28 ++#define R_390_GOTPLT12 29 ++#define R_390_GOTPLT16 30 ++#define R_390_GOTPLT32 31 ++#define R_390_GOTPLT64 32 ++#define R_390_GOTPLTENT 33 ++#define R_390_PLTOFF16 34 ++#define R_390_PLTOFF32 35 ++#define R_390_PLTOFF64 36 ++#define R_390_TLS_LOAD 37 ++#define R_390_TLS_GDCALL 38 ++ ++#define R_390_TLS_LDCALL 39 ++ ++#define R_390_TLS_GD32 40 ++ ++#define R_390_TLS_GD64 41 ++ ++#define R_390_TLS_GOTIE12 42 ++ ++#define R_390_TLS_GOTIE32 43 ++ ++#define R_390_TLS_GOTIE64 44 ++ ++#define R_390_TLS_LDM32 45 ++ ++#define R_390_TLS_LDM64 46 ++ ++#define R_390_TLS_IE32 47 ++ ++#define R_390_TLS_IE64 48 ++ ++#define R_390_TLS_IEENT 49 ++ ++#define R_390_TLS_LE32 50 ++ ++#define R_390_TLS_LE64 51 ++ ++#define R_390_TLS_LDO32 52 ++ ++#define R_390_TLS_LDO64 53 ++ ++#define R_390_TLS_DTPMOD 54 ++#define R_390_TLS_DTPOFF 55 ++#define R_390_TLS_TPOFF 56 ++ ++#define R_390_20 57 ++#define R_390_GOT20 58 ++#define R_390_GOTPLT20 59 ++#define R_390_TLS_GOTIE20 60 ++ ++ ++#define R_390_NUM 61 ++ ++ ++ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++ ++ ++#define R_X86_64_NONE 0 ++#define R_X86_64_64 1 ++#define R_X86_64_PC32 2 ++#define R_X86_64_GOT32 3 ++#define R_X86_64_PLT32 4 ++#define R_X86_64_COPY 5 ++#define R_X86_64_GLOB_DAT 6 ++#define R_X86_64_JUMP_SLOT 7 ++#define R_X86_64_RELATIVE 8 ++#define R_X86_64_GOTPCREL 9 ++ ++#define R_X86_64_32 10 ++#define R_X86_64_32S 11 ++#define R_X86_64_16 12 ++#define R_X86_64_PC16 13 ++#define R_X86_64_8 14 ++#define R_X86_64_PC8 15 ++#define R_X86_64_DTPMOD64 16 ++#define R_X86_64_DTPOFF64 17 ++#define R_X86_64_TPOFF64 18 ++#define R_X86_64_TLSGD 19 ++ ++#define R_X86_64_TLSLD 20 ++ ++#define R_X86_64_DTPOFF32 21 ++#define R_X86_64_GOTTPOFF 22 ++ ++#define R_X86_64_TPOFF32 23 ++#define R_X86_64_PC64 24 ++#define R_X86_64_GOTOFF64 25 ++#define R_X86_64_GOTPC32 26 ++#define R_X86_64_GOT64 27 ++#define R_X86_64_GOTPCREL64 28 ++#define R_X86_64_GOTPC64 29 ++#define R_X86_64_GOTPLT64 30 ++#define R_X86_64_PLTOFF64 31 ++#define R_X86_64_SIZE32 32 ++#define R_X86_64_SIZE64 33 ++ ++#define R_X86_64_GOTPC32_TLSDESC 34 ++#define R_X86_64_TLSDESC_CALL 35 ++ ++#define R_X86_64_TLSDESC 36 ++#define R_X86_64_IRELATIVE 37 ++#define R_X86_64_RELATIVE64 38 ++#define R_X86_64_NUM 39 ++ ++ ++ ++#define R_MN10300_NONE 0 ++#define R_MN10300_32 1 ++#define R_MN10300_16 2 ++#define R_MN10300_8 3 ++#define R_MN10300_PCREL32 4 ++#define R_MN10300_PCREL16 5 ++#define R_MN10300_PCREL8 6 ++#define R_MN10300_GNU_VTINHERIT 7 ++#define R_MN10300_GNU_VTENTRY 8 ++#define R_MN10300_24 9 ++#define R_MN10300_GOTPC32 10 ++#define R_MN10300_GOTPC16 11 ++#define R_MN10300_GOTOFF32 12 ++#define R_MN10300_GOTOFF24 13 ++#define R_MN10300_GOTOFF16 14 ++#define R_MN10300_PLT32 15 ++#define R_MN10300_PLT16 16 ++#define R_MN10300_GOT32 17 ++#define R_MN10300_GOT24 18 ++#define R_MN10300_GOT16 19 ++#define R_MN10300_COPY 20 ++#define R_MN10300_GLOB_DAT 21 ++#define R_MN10300_JMP_SLOT 22 ++#define R_MN10300_RELATIVE 23 ++ ++#define R_MN10300_NUM 24 ++ ++ ++ ++#define R_M32R_NONE 0 ++#define R_M32R_16 1 ++#define R_M32R_32 2 ++#define R_M32R_24 3 ++#define R_M32R_10_PCREL 4 ++#define R_M32R_18_PCREL 5 ++#define R_M32R_26_PCREL 6 ++#define R_M32R_HI16_ULO 7 ++#define R_M32R_HI16_SLO 8 ++#define R_M32R_LO16 9 ++#define R_M32R_SDA16 10 ++#define R_M32R_GNU_VTINHERIT 11 ++#define R_M32R_GNU_VTENTRY 12 ++ ++#define R_M32R_16_RELA 33 ++#define R_M32R_32_RELA 34 ++#define R_M32R_24_RELA 35 ++#define R_M32R_10_PCREL_RELA 36 ++#define R_M32R_18_PCREL_RELA 37 ++#define R_M32R_26_PCREL_RELA 38 ++#define R_M32R_HI16_ULO_RELA 39 ++#define R_M32R_HI16_SLO_RELA 40 ++#define R_M32R_LO16_RELA 41 ++#define R_M32R_SDA16_RELA 42 ++#define R_M32R_RELA_GNU_VTINHERIT 43 ++#define R_M32R_RELA_GNU_VTENTRY 44 ++#define R_M32R_REL32 45 ++ ++#define R_M32R_GOT24 48 ++#define R_M32R_26_PLTREL 49 ++#define R_M32R_COPY 50 ++#define R_M32R_GLOB_DAT 51 ++#define R_M32R_JMP_SLOT 52 ++#define R_M32R_RELATIVE 53 ++#define R_M32R_GOTOFF 54 ++#define R_M32R_GOTPC24 55 ++#define R_M32R_GOT16_HI_ULO 56 ++ ++#define R_M32R_GOT16_HI_SLO 57 ++ ++#define R_M32R_GOT16_LO 58 ++#define R_M32R_GOTPC_HI_ULO 59 ++ ++#define R_M32R_GOTPC_HI_SLO 60 ++ ++#define R_M32R_GOTPC_LO 61 ++ ++#define R_M32R_GOTOFF_HI_ULO 62 ++ ++#define R_M32R_GOTOFF_HI_SLO 63 ++ ++#define R_M32R_GOTOFF_LO 64 ++#define R_M32R_NUM 256 ++ ++#define R_MICROBLAZE_NONE 0 ++#define R_MICROBLAZE_32 1 ++#define R_MICROBLAZE_32_PCREL 2 ++#define R_MICROBLAZE_64_PCREL 3 ++#define R_MICROBLAZE_32_PCREL_LO 4 ++#define R_MICROBLAZE_64 5 ++#define R_MICROBLAZE_32_LO 6 ++#define R_MICROBLAZE_SRO32 7 ++#define R_MICROBLAZE_SRW32 8 ++#define R_MICROBLAZE_64_NONE 9 ++#define R_MICROBLAZE_32_SYM_OP_SYM 10 ++#define R_MICROBLAZE_GNU_VTINHERIT 11 ++#define R_MICROBLAZE_GNU_VTENTRY 12 ++#define R_MICROBLAZE_GOTPC_64 13 ++#define R_MICROBLAZE_GOT_64 14 ++#define R_MICROBLAZE_PLT_64 15 ++#define R_MICROBLAZE_REL 16 ++#define R_MICROBLAZE_JUMP_SLOT 17 ++#define R_MICROBLAZE_GLOB_DAT 18 ++#define R_MICROBLAZE_GOTOFF_64 19 ++#define R_MICROBLAZE_GOTOFF_32 20 ++#define R_MICROBLAZE_COPY 21 ++#define R_MICROBLAZE_TLS 22 ++#define R_MICROBLAZE_TLSGD 23 ++#define R_MICROBLAZE_TLSLD 24 ++#define R_MICROBLAZE_TLSDTPMOD32 25 ++#define R_MICROBLAZE_TLSDTPREL32 26 ++#define R_MICROBLAZE_TLSDTPREL64 27 ++#define R_MICROBLAZE_TLSGOTTPREL32 28 ++#define R_MICROBLAZE_TLSTPREL32 29 ++ ++#ifdef __cplusplus ++} ++#endif ++ ++ ++#endif diff --git a/target/linux/patches/3.12.44/sgidefs.patch b/target/linux/patches/3.12.44/sgidefs.patch new file mode 100644 index 000000000..f00a284d9 --- /dev/null +++ b/target/linux/patches/3.12.44/sgidefs.patch @@ -0,0 +1,18 @@ +diff -Nur linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h +--- linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h 2013-11-08 22:01:28.000000000 +0100 +@@ -11,14 +11,6 @@ + #define __ASM_SGIDEFS_H + + /* +- * Using a Linux compiler for building Linux seems logic but not to +- * everybody. +- */ +-#ifndef __linux__ +-#error Use a Linux compiler or give up. +-#endif +- +-/* + * Definitions for the ISA levels + * + * With the introduction of MIPS32 / MIPS64 instruction sets definitions diff --git a/target/linux/patches/3.12.44/sortext.patch b/target/linux/patches/3.12.44/sortext.patch new file mode 100644 index 000000000..8fd4e1d6b --- /dev/null +++ b/target/linux/patches/3.12.44/sortext.patch @@ -0,0 +1,33 @@ +diff -Nur linux-3.12.6.orig/arch/arm/Kconfig linux-3.12.6/arch/arm/Kconfig +--- linux-3.12.6.orig/arch/arm/Kconfig 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/arch/arm/Kconfig 2013-12-28 19:29:33.000000000 +0100 +@@ -6,7 +6,6 @@ + select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST + select ARCH_HAVE_CUSTOM_GPIO_H + select ARCH_WANT_IPC_PARSE_VERSION +- select BUILDTIME_EXTABLE_SORT if MMU + select CLONE_BACKWARDS + select CPU_PM if (SUSPEND || CPU_IDLE) + select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && !CPU_BIG_ENDIAN && MMU +diff -Nur linux-3.12.6.orig/arch/mips/Kconfig linux-3.12.6/arch/mips/Kconfig +--- linux-3.12.6.orig/arch/mips/Kconfig 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/arch/mips/Kconfig 2013-12-28 19:30:06.000000000 +0100 +@@ -35,7 +35,6 @@ + select HAVE_MEMBLOCK_NODE_MAP + select ARCH_DISCARD_MEMBLOCK + select GENERIC_SMP_IDLE_THREAD +- select BUILDTIME_EXTABLE_SORT + select GENERIC_CLOCKEVENTS + select GENERIC_CMOS_UPDATE + select HAVE_MOD_ARCH_SPECIFIC +diff -Nur linux-3.12.6.orig/arch/x86/Kconfig linux-3.12.6/arch/x86/Kconfig +--- linux-3.12.6.orig/arch/x86/Kconfig 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/arch/x86/Kconfig 2013-12-28 19:29:50.000000000 +0100 +@@ -100,7 +100,6 @@ + select GENERIC_SMP_IDLE_THREAD + select ARCH_WANT_IPC_PARSE_VERSION if X86_32 + select HAVE_ARCH_SECCOMP_FILTER +- select BUILDTIME_EXTABLE_SORT + select GENERIC_CMOS_UPDATE + select HAVE_ARCH_SOFT_DIRTY + select CLOCKSOURCE_WATCHDOG diff --git a/target/linux/patches/3.12.44/startup.patch b/target/linux/patches/3.12.44/startup.patch new file mode 100644 index 000000000..4cd477da1 --- /dev/null +++ b/target/linux/patches/3.12.44/startup.patch @@ -0,0 +1,34 @@ +diff -Nur linux-3.12.13.orig/init/initramfs.c linux-3.12.13/init/initramfs.c +--- linux-3.12.13.orig/init/initramfs.c 2014-02-22 22:32:50.000000000 +0100 ++++ linux-3.12.13/init/initramfs.c 2014-03-15 18:09:39.040618589 +0100 +@@ -622,6 +622,9 @@ + */ + load_default_modules(); + } ++#ifdef CONFIG_DEVTMPFS_MOUNT ++ devtmpfs_mount("dev"); ++#endif + return 0; + } + rootfs_initcall(populate_rootfs); +diff -Nur linux-3.12.13.orig/init/main.c linux-3.12.13/init/main.c +--- linux-3.12.13.orig/init/main.c 2014-02-22 22:32:50.000000000 +0100 ++++ linux-3.12.13/init/main.c 2014-03-15 18:09:39.044618600 +0100 +@@ -888,6 +888,8 @@ + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + pr_err("Warning: unable to open an initial console.\n"); + ++ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); ++ + (void) sys_dup(0); + (void) sys_dup(0); + /* +@@ -896,7 +898,7 @@ + */ + + if (!ramdisk_execute_command) +- ramdisk_execute_command = "/init"; ++ ramdisk_execute_command = "/sbin/init"; + + if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { + ramdisk_execute_command = NULL; diff --git a/target/linux/patches/3.12.44/usb-defaults-off.patch b/target/linux/patches/3.12.44/usb-defaults-off.patch new file mode 100644 index 000000000..54dff2bd4 --- /dev/null +++ b/target/linux/patches/3.12.44/usb-defaults-off.patch @@ -0,0 +1,20 @@ +diff -Nur linux-3.5.orig//drivers/usb/host/Kconfig linux-3.5/drivers/usb/host/Kconfig +--- linux-3.5.orig//drivers/usb/host/Kconfig 2012-07-21 22:58:29.000000000 +0200 ++++ linux-3.5/drivers/usb/host/Kconfig 2012-07-29 11:59:56.000000000 +0200 +@@ -66,6 +66,7 @@ + config USB_EHCI_ROOT_HUB_TT + bool "Root Hub Transaction Translators" + depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST ++ default n + ---help--- + Some EHCI chips have vendor-specific extensions to integrate + transaction translators, so that no OHCI or UHCI companion +@@ -78,7 +79,7 @@ + config USB_EHCI_TT_NEWSCHED + bool "Improved Transaction Translator scheduling" + depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST +- default y ++ default n + ---help--- + This changes the periodic scheduling code to fill more of the low + and full speed bandwidth available from the Transaction Translator diff --git a/target/linux/patches/3.12.44/wlan-cf.patch b/target/linux/patches/3.12.44/wlan-cf.patch new file mode 100644 index 000000000..fc20759e2 --- /dev/null +++ b/target/linux/patches/3.12.44/wlan-cf.patch @@ -0,0 +1,11 @@ +diff -Nur linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c +--- linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c 2011-05-19 06:06:34.000000000 +0200 ++++ linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c 2011-09-12 02:46:26.987984145 +0200 +@@ -623,6 +623,7 @@ + static struct pcmcia_device_id hostap_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), ++ PCMCIA_DEVICE_MANF_CARD(0x0004, 0x2003), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), + PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), + PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), diff --git a/target/linux/patches/3.12.44/xargs.patch b/target/linux/patches/3.12.44/xargs.patch new file mode 100644 index 000000000..2c7b3df59 --- /dev/null +++ b/target/linux/patches/3.12.44/xargs.patch @@ -0,0 +1,12 @@ +diff -Nur linux-3.12.6.orig/scripts/Makefile.modpost linux-3.12.6/scripts/Makefile.modpost +--- linux-3.12.6.orig/scripts/Makefile.modpost 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/scripts/Makefile.modpost 2014-01-25 14:55:33.000000000 +0100 +@@ -60,7 +60,7 @@ + modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers + + # Step 1), find all modules listed in $(MODVERDIR)/ +-MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs -r grep -h '\.ko$$' | sort -u ++MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs grep -h '\.ko$$' | sort -u + __modules := $(shell $(MODLISTCMD)) + modules := $(patsubst %.o,%.ko, $(wildcard $(__modules:.ko=.o))) + diff --git a/target/linux/patches/3.12.44/yaffs2.patch b/target/linux/patches/3.12.44/yaffs2.patch new file mode 100644 index 000000000..306814439 --- /dev/null +++ b/target/linux/patches/3.12.44/yaffs2.patch @@ -0,0 +1,16547 @@ +diff -Nur linux-3.12.20.orig/fs/Kconfig linux-3.12.20/fs/Kconfig +--- linux-3.12.20.orig/fs/Kconfig 2014-05-15 09:56:19.000000000 +0200 ++++ linux-3.12.20/fs/Kconfig 2014-05-17 11:28:49.000000000 +0200 +@@ -193,6 +193,7 @@ + source "fs/befs/Kconfig" + source "fs/bfs/Kconfig" + source "fs/efs/Kconfig" ++source "fs/yaffs2/Kconfig" + source "fs/jffs2/Kconfig" + # UBIFS File system configuration + source "fs/ubifs/Kconfig" +diff -Nur linux-3.12.20.orig/fs/Makefile linux-3.12.20/fs/Makefile +--- linux-3.12.20.orig/fs/Makefile 2014-05-15 09:56:19.000000000 +0200 ++++ linux-3.12.20/fs/Makefile 2014-05-17 11:28:49.000000000 +0200 +@@ -126,3 +126,4 @@ + obj-$(CONFIG_CEPH_FS) += ceph/ + obj-$(CONFIG_PSTORE) += pstore/ + obj-$(CONFIG_EFIVAR_FS) += efivarfs/ ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ +diff -Nur linux-3.12.20.orig/fs/yaffs2/Kconfig linux-3.12.20/fs/yaffs2/Kconfig +--- linux-3.12.20.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/Kconfig 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,171 @@ ++# ++# yaffs file system configurations ++# ++ ++config YAFFS_FS ++ tristate "yaffs2 file system support" ++ default n ++ depends on MTD_BLOCK ++ select YAFFS_YAFFS1 ++ select YAFFS_YAFFS2 ++ help ++ yaffs2, or Yet Another Flash File System, is a file system ++ optimised for NAND Flash chips. ++ ++ To compile the yaffs2 file system support as a module, choose M ++ here: the module will be called yaffs2. ++ ++ If unsure, say N. ++ ++ Further information on yaffs2 is available at ++ . ++ ++config YAFFS_YAFFS1 ++ bool "512 byte / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs1 support -- yaffs for 512 byte / page devices ++ ++ Not needed for 2K-page devices. ++ ++ If unsure, say Y. ++ ++config YAFFS_9BYTE_TAGS ++ bool "Use older-style on-NAND data format with pageStatus byte" ++ depends on YAFFS_YAFFS1 ++ default n ++ help ++ ++ Older-style on-NAND data format has a "pageStatus" byte to record ++ chunk/page state. This byte is zero when the page is discarded. ++ Choose this option if you have existing on-NAND data using this ++ format that you need to continue to support. New data written ++ also uses the older-style format. Note: Use of this option ++ generally requires that MTD's oob layout be adjusted to use the ++ older-style format. See notes on tags formats and MTD versions ++ in yaffs_mtdif1.c. ++ ++ If unsure, say N. ++ ++config YAFFS_DOES_ECC ++ bool "Lets yaffs do its own ECC" ++ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This enables yaffs to use its own ECC functions instead of using ++ the ones from the generic MTD-NAND driver. ++ ++ If unsure, say N. ++ ++config YAFFS_ECC_WRONG_ORDER ++ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" ++ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This makes yaffs_ecc.c use the same ecc byte order as Steven ++ Hill's nand_ecc.c. If not set, then you get the same ecc byte ++ order as SmartMedia. ++ ++ If unsure, say N. ++ ++config YAFFS_YAFFS2 ++ bool "2048 byte (or larger) / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices ++ ++ If unsure, say Y. ++ ++config YAFFS_AUTO_YAFFS2 ++ bool "Autoselect yaffs2 format" ++ depends on YAFFS_YAFFS2 ++ default y ++ help ++ Without this, you need to explicitely use yaffs2 as the file ++ system type. With this, you can say "yaffs" and yaffs or yaffs2 ++ will be used depending on the device page size (yaffs on ++ 512-byte page devices, yaffs2 on 2K page devices). ++ ++ If unsure, say Y. ++ ++config YAFFS_DISABLE_TAGS_ECC ++ bool "Disable yaffs from doing ECC on tags by default" ++ depends on YAFFS_FS && YAFFS_YAFFS2 ++ default n ++ help ++ This defaults yaffs to using its own ECC calculations on tags instead of ++ just relying on the MTD. ++ This behavior can also be overridden with tags_ecc_on and ++ tags_ecc_off mount options. ++ ++ If unsure, say N. ++ ++config YAFFS_ALWAYS_CHECK_CHUNK_ERASED ++ bool "Force chunk erase check" ++ depends on YAFFS_FS ++ default n ++ help ++ Normally yaffs only checks chunks before writing until an erased ++ chunk is found. This helps to detect any partially written ++ chunks that might have happened due to power loss. ++ ++ Enabling this forces on the test that chunks are erased in flash ++ before writing to them. This takes more time but is potentially ++ a bit more secure. ++ ++ Suggest setting Y during development and ironing out driver ++ issues etc. Suggest setting to N if you want faster writing. ++ ++ If unsure, say Y. ++ ++config YAFFS_EMPTY_LOST_AND_FOUND ++ bool "Empty lost and found on boot" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is enabled then the contents of lost and found is ++ automatically dumped at mount. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BLOCK_REFRESHING ++ bool "Disable yaffs2 block refreshing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then block refreshing is disabled. ++ Block refreshing infrequently refreshes the oldest block in ++ a yaffs2 file system. This mechanism helps to refresh flash to ++ mitigate against data loss. This is particularly useful for MLC. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BACKGROUND ++ bool "Disable yaffs2 background processing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then background processing is disabled. ++ Background processing makes many foreground activities faster. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BAD_BLOCK_MARKING ++ bool "Disable yaffs2 bad block marking" ++ depends on YAFFS_FS ++ default n ++ help ++ Useful during early flash bring up to prevent problems causing ++ lots of bad block marking. ++ ++ If unsure, say N. ++ ++config YAFFS_XATTR ++ bool "Enable yaffs2 xattr support" ++ depends on YAFFS_FS ++ default y ++ help ++ If this is set then yaffs2 will provide xattr support. ++ If unsure, say Y. +diff -Nur linux-3.12.20.orig/fs/yaffs2/Makefile linux-3.12.20/fs/yaffs2/Makefile +--- linux-3.12.20.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/Makefile 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,18 @@ ++# ++# Makefile for the linux YAFFS filesystem routines. ++# ++ ++obj-$(CONFIG_YAFFS_FS) += yaffs.o ++ ++yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o ++yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o ++yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o ++yaffs-y += yaffs_mtdif.o ++yaffs-y += yaffs_nameval.o yaffs_attribs.o ++yaffs-y += yaffs_allocator.o ++yaffs-y += yaffs_yaffs1.o ++yaffs-y += yaffs_yaffs2.o ++yaffs-y += yaffs_bitmap.o ++yaffs-y += yaffs_summary.o ++yaffs-y += yaffs_verify.o ++ +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_allocator.c linux-3.12.20/fs/yaffs2/yaffs_allocator.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_allocator.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,357 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_allocator.h" ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yportenv.h" ++ ++/* ++ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks ++ * of approx 100 objects that are themn allocated singly. ++ * This is basically a simplified slab allocator. ++ * ++ * We don't use the Linux slab allocator because slab does not allow ++ * us to dump all the objects in one hit when we do a umount and tear ++ * down all the tnodes and objects. slab requires that we first free ++ * the individual objects. ++ * ++ * Once yaffs has been mainlined I shall try to motivate for a change ++ * to slab to provide the extra features we need here. ++ */ ++ ++struct yaffs_tnode_list { ++ struct yaffs_tnode_list *next; ++ struct yaffs_tnode *tnodes; ++}; ++ ++struct yaffs_obj_list { ++ struct yaffs_obj_list *next; ++ struct yaffs_obj *objects; ++}; ++ ++struct yaffs_allocator { ++ int n_tnodes_created; ++ struct yaffs_tnode *free_tnodes; ++ int n_free_tnodes; ++ struct yaffs_tnode_list *alloc_tnode_list; ++ ++ int n_obj_created; ++ struct list_head free_objs; ++ int n_free_objects; ++ ++ struct yaffs_obj_list *allocated_obj_list; ++}; ++ ++static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->alloc_tnode_list) { ++ tmp = allocator->alloc_tnode_list->next; ++ ++ kfree(allocator->alloc_tnode_list->tnodes); ++ kfree(allocator->alloc_tnode_list); ++ allocator->alloc_tnode_list = tmp; ++ } ++ ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->alloc_tnode_list = NULL; ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ int i; ++ struct yaffs_tnode *new_tnodes; ++ u8 *mem; ++ struct yaffs_tnode *curr; ++ struct yaffs_tnode *next; ++ struct yaffs_tnode_list *tnl; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_tnodes < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); ++ mem = (u8 *) new_tnodes; ++ ++ if (!new_tnodes) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: Could not allocate Tnodes"); ++ return YAFFS_FAIL; ++ } ++ ++ /* New hookup for wide tnodes */ ++ for (i = 0; i < n_tnodes - 1; i++) { ++ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; ++ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; ++ curr->internal[0] = next; ++ } ++ ++ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; ++ curr->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = (struct yaffs_tnode *)mem; ++ ++ allocator->n_free_tnodes += n_tnodes; ++ allocator->n_tnodes_created += n_tnodes; ++ ++ /* Now add this bunch of tnodes to a list for freeing up. ++ * NB If we can't add this to the management list it isn't fatal ++ * but it just means we can't free this bunch of tnodes later. ++ */ ++ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); ++ if (!tnl) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Could not add tnodes to management list"); ++ return YAFFS_FAIL; ++ } else { ++ tnl->tnodes = new_tnodes; ++ tnl->next = allocator->alloc_tnode_list; ++ allocator->alloc_tnode_list = tnl; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (!allocator) { ++ BUG(); ++ return NULL; ++ } ++ ++ /* If there are none left make more */ ++ if (!allocator->free_tnodes) ++ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); ++ ++ if (allocator->free_tnodes) { ++ tn = allocator->free_tnodes; ++ allocator->free_tnodes = allocator->free_tnodes->internal[0]; ++ allocator->n_free_tnodes--; ++ } ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ if (tn) { ++ tn->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = tn; ++ allocator->n_free_tnodes++; ++ } ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++/*--------------- yaffs_obj alloaction ------------------------ ++ * ++ * Free yaffs_objs are stored in a list using obj->siblings. ++ * The blocks of allocated objects are stored in a linked list. ++ */ ++ ++static void yaffs_init_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->allocated_obj_list = NULL; ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++} ++ ++static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ struct yaffs_obj_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->allocated_obj_list) { ++ tmp = allocator->allocated_obj_list->next; ++ kfree(allocator->allocated_obj_list->objects); ++ kfree(allocator->allocated_obj_list); ++ allocator->allocated_obj_list = tmp; ++ } ++ ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++ allocator->n_obj_created = 0; ++} ++ ++static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ int i; ++ struct yaffs_obj *new_objs; ++ struct yaffs_obj_list *list; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_obj < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); ++ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); ++ ++ if (!new_objs || !list) { ++ kfree(new_objs); ++ new_objs = NULL; ++ kfree(list); ++ list = NULL; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Could not allocate more objects"); ++ return YAFFS_FAIL; ++ } ++ ++ /* Hook them into the free list */ ++ for (i = 0; i < n_obj; i++) ++ list_add(&new_objs[i].siblings, &allocator->free_objs); ++ ++ allocator->n_free_objects += n_obj; ++ allocator->n_obj_created += n_obj; ++ ++ /* Now add this bunch of Objects to a list for freeing up. */ ++ ++ list->objects = new_objs; ++ list->next = allocator->allocated_obj_list; ++ allocator->allocated_obj_list = list; ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct list_head *lh; ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return obj; ++ } ++ ++ /* If there are none left make more */ ++ if (list_empty(&allocator->free_objs)) ++ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); ++ ++ if (!list_empty(&allocator->free_objs)) { ++ lh = allocator->free_objs.next; ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ list_del_init(lh); ++ allocator->n_free_objects--; ++ } ++ ++ return obj; ++} ++ ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ /* Link into the free list. */ ++ list_add(&obj->siblings, &allocator->free_objs); ++ allocator->n_free_objects++; ++} ++ ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ ++ if (!dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ yaffs_deinit_raw_tnodes(dev); ++ yaffs_deinit_raw_objs(dev); ++ kfree(dev->allocator); ++ dev->allocator = NULL; ++} ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator; ++ ++ if (dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); ++ if (allocator) { ++ dev->allocator = allocator; ++ yaffs_init_raw_tnodes(dev); ++ yaffs_init_raw_objs(dev); ++ } ++} ++ +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_allocator.h linux-3.12.20/fs/yaffs2/yaffs_allocator.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_allocator.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,30 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ALLOCATOR_H__ ++#define __YAFFS_ALLOCATOR_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_attribs.c linux-3.12.20/fs/yaffs2/yaffs_attribs.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_attribs.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,166 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++static inline uid_t ia_uid_read(const struct iattr *iattr) ++{ ++ return from_kuid(&init_user_ns, iattr->ia_uid); ++} ++ ++static inline gid_t ia_gid_read(const struct iattr *iattr) ++{ ++ return from_kgid(&init_user_ns, iattr->ia_gid); ++} ++ ++static inline void ia_uid_write(struct iattr *iattr, uid_t uid) ++{ ++ iattr->ia_uid = make_kuid(&init_user_ns, uid); ++} ++ ++static inline void ia_gid_write(struct iattr *iattr, gid_t gid) ++{ ++ iattr->ia_gid = make_kgid(&init_user_ns, gid); ++} ++#else ++static inline uid_t ia_uid_read(const struct iattr *iattr) ++{ ++ return iattr->ia_uid; ++} ++ ++static inline gid_t ia_gid_read(const struct iattr *inode) ++{ ++ return iattr->ia_gid; ++} ++ ++static inline void ia_uid_write(struct iattr *iattr, uid_t uid) ++{ ++ iattr->ia_uid = uid; ++} ++ ++static inline void ia_gid_write(struct iattr *iattr, gid_t gid) ++{ ++ iattr->ia_gid = gid; ++} ++#endif ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) ++{ ++ obj->yst_uid = oh->yst_uid; ++ obj->yst_gid = oh->yst_gid; ++ obj->yst_atime = oh->yst_atime; ++ obj->yst_mtime = oh->yst_mtime; ++ obj->yst_ctime = oh->yst_ctime; ++ obj->yst_rdev = oh->yst_rdev; ++} ++ ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) ++{ ++ oh->yst_uid = obj->yst_uid; ++ oh->yst_gid = obj->yst_gid; ++ oh->yst_atime = obj->yst_atime; ++ oh->yst_mtime = obj->yst_mtime; ++ oh->yst_ctime = obj->yst_ctime; ++ oh->yst_rdev = obj->yst_rdev; ++ ++} ++ ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) ++{ ++ obj->yst_mtime = Y_CURRENT_TIME; ++ if (do_a) ++ obj->yst_atime = obj->yst_mtime; ++ if (do_c) ++ obj->yst_ctime = obj->yst_mtime; ++} ++ ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) ++{ ++ yaffs_load_current_time(obj, 1, 1); ++ obj->yst_rdev = rdev; ++ obj->yst_uid = uid; ++ obj->yst_gid = gid; ++} ++ ++static loff_t yaffs_get_file_size(struct yaffs_obj *obj) ++{ ++ YCHAR *alias = NULL; ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return obj->variant.file_variant.file_size; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = obj->variant.symlink_variant.alias; ++ if (!alias) ++ return 0; ++ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); ++ default: ++ return 0; ++ } ++} ++ ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = attr->ia_valid; ++ ++ if (valid & ATTR_MODE) ++ obj->yst_mode = attr->ia_mode; ++ if (valid & ATTR_UID) ++ obj->yst_uid = ia_uid_read(attr); ++ if (valid & ATTR_GID) ++ obj->yst_gid = ia_gid_read(attr); ++ ++ if (valid & ATTR_ATIME) ++ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); ++ if (valid & ATTR_CTIME) ++ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); ++ if (valid & ATTR_MTIME) ++ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); ++ ++ if (valid & ATTR_SIZE) ++ yaffs_resize_file(obj, attr->ia_size); ++ ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ return YAFFS_OK; ++ ++} ++ ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = 0; ++ ++ attr->ia_mode = obj->yst_mode; ++ valid |= ATTR_MODE; ++ ia_uid_write(attr, obj->yst_uid); ++ valid |= ATTR_UID; ++ ia_gid_write(attr, obj->yst_gid); ++ valid |= ATTR_GID; ++ ++ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; ++ valid |= ATTR_ATIME; ++ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; ++ valid |= ATTR_CTIME; ++ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; ++ valid |= ATTR_MTIME; ++ ++ attr->ia_size = yaffs_get_file_size(obj); ++ valid |= ATTR_SIZE; ++ ++ attr->ia_valid = valid; ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_attribs.h linux-3.12.20/fs/yaffs2/yaffs_attribs.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_attribs.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ATTRIBS_H__ ++#define __YAFFS_ATTRIBS_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_bitmap.c linux-3.12.20/fs/yaffs2/yaffs_bitmap.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_bitmap.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,97 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_bitmap.h" ++#include "yaffs_trace.h" ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "BlockBits block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return dev->chunk_bits + ++ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); ++} ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block || ++ chunk < 0 || chunk >= dev->param.chunks_per_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Chunk Id (%d:%d) invalid", ++ blk, chunk); ++ BUG(); ++ } ++} ++ ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ memset(blk_bits, 0, dev->chunk_bit_stride); ++} ++ ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); ++} ++ ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] |= (1 << (chunk & 7)); ++} ++ ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; ++} ++ ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++) { ++ if (*blk_bits) ++ return 1; ++ blk_bits++; ++ } ++ return 0; ++} ++ ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ int n = 0; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) ++ n += hweight8(*blk_bits); ++ ++ return n; ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_bitmap.h linux-3.12.20/fs/yaffs2/yaffs_bitmap.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_bitmap.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++#ifndef __YAFFS_BITMAP_H__ ++#define __YAFFS_BITMAP_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.12.20/fs/yaffs2/yaffs_checkptrw.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,474 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_checkptrw.h" ++#include "yaffs_getblockinfo.h" ++ ++struct yaffs_checkpt_chunk_hdr { ++ int version; ++ int seq; ++ u32 sum; ++ u32 xor; ++} ; ++ ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++static int apply_block_offset(struct yaffs_dev *dev, int block) ++{ ++ return block - dev->block_offset; ++} ++ ++static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ hdr.version = YAFFS_CHECKPOINT_VERSION; ++ hdr.seq = dev->checkpt_page_seq; ++ hdr.sum = dev->checkpt_sum; ++ hdr.xor = dev->checkpt_xor; ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); ++} ++ ++static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ return hdr.version == YAFFS_CHECKPOINT_VERSION && ++ hdr.seq == dev->checkpt_page_seq && ++ hdr.sum == dev->checkpt_sum && ++ hdr.xor == dev->checkpt_xor; ++} ++ ++static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) ++{ ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpt blocks_avail = %d", blocks_avail); ++ ++ return (blocks_avail <= 0) ? 0 : 1; ++} ++ ++static int yaffs_checkpt_erase(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (!dev->drv.drv_erase_fn) ++ return 0; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checking blocks %d to %d", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ int offset_i = apply_block_offset(dev, i); ++ int result; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "erasing checkpt block %d", i); ++ ++ dev->n_erasures++; ++ ++ result = dev->drv.drv_erase_fn(dev, offset_i); ++ if(result) { ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += ++ dev->param.chunks_per_block; ++ } else { ++ dev->drv.drv_mark_bad_fn(dev, offset_i); ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ } ++ } ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ return 1; ++} ++ ++static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) ++{ ++ int i; ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block: erased %d reserved %d avail %d next %d ", ++ dev->n_erased_blocks, dev->param.n_reserved_blocks, ++ blocks_avail, dev->checkpt_next_block); ++ ++ if (dev->checkpt_next_block >= 0 && ++ dev->checkpt_next_block <= dev->internal_end_block && ++ blocks_avail > 0) { ++ ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev, i); ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ dev->checkpt_next_block = i + 1; ++ dev->checkpt_cur_block = i; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block %d", i); ++ return; ++ } ++ } ++ } ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_ext_tags tags; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: start: blocks %d next %d", ++ dev->blocks_in_checkpt, dev->checkpt_next_block); ++ ++ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ int chunk = i * dev->param.chunks_per_block; ++ enum yaffs_block_state state; ++ u32 seq; ++ ++ dev->tagger.read_chunk_tags_fn(dev, ++ apply_chunk_offset(dev, chunk), ++ NULL, &tags); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", ++ i, (int) state, ++ tags.obj_id, tags.seq_number, ++ tags.ecc_result); ++ ++ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ continue; ++ ++ dev->tagger.query_block_fn(dev, ++ apply_block_offset(dev, i), ++ &state, &seq); ++ if (state == YAFFS_BLOCK_STATE_DEAD) ++ continue; ++ ++ /* Right kind of block */ ++ dev->checkpt_next_block = tags.obj_id; ++ dev->checkpt_cur_block = i; ++ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; ++ dev->blocks_in_checkpt++; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "found checkpt block %d", i); ++ return; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) ++{ ++ int i; ++ ++ dev->checkpt_open_write = writing; ++ ++ /* Got the functions we need? */ ++ if (!dev->tagger.write_chunk_tags_fn || ++ !dev->tagger.read_chunk_tags_fn || ++ !dev->drv.drv_erase_fn || ++ !dev->drv.drv_mark_bad_fn) ++ return 0; ++ ++ if (writing && !yaffs2_checkpt_space_ok(dev)) ++ return 0; ++ ++ if (!dev->checkpt_buffer) ++ dev->checkpt_buffer = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ dev->checkpt_page_seq = 0; ++ dev->checkpt_byte_count = 0; ++ dev->checkpt_sum = 0; ++ dev->checkpt_xor = 0; ++ dev->checkpt_cur_block = -1; ++ dev->checkpt_cur_chunk = -1; ++ dev->checkpt_next_block = dev->internal_start_block; ++ ++ if (writing) { ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ return yaffs_checkpt_erase(dev); ++ } ++ ++ /* Opening for a read */ ++ /* Set to a value that will kick off a read */ ++ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; ++ /* A checkpoint block list of 1 checkpoint block per 16 block is ++ * (hopefully) going to be way more than we need */ ++ dev->blocks_in_checkpt = 0; ++ dev->checkpt_max_blocks = ++ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; ++ dev->checkpt_block_list = ++ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); ++ ++ if (!dev->checkpt_block_list) ++ return 0; ++ ++ for (i = 0; i < dev->checkpt_max_blocks; i++) ++ dev->checkpt_block_list[i] = -1; ++ ++ return 1; ++} ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) ++{ ++ u32 composite_sum; ++ ++ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); ++ *sum = composite_sum; ++ return 1; ++} ++ ++static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) ++{ ++ int chunk; ++ int offset_chunk; ++ struct yaffs_ext_tags tags; ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_erased_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) ++ return 0; ++ ++ tags.is_deleted = 0; ++ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ ++ tags.chunk_id = dev->checkpt_page_seq + 1; ++ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; ++ tags.n_bytes = dev->data_bytes_per_chunk; ++ if (dev->checkpt_cur_chunk == 0) { ++ /* First chunk we write for the block? Set block state to ++ checkpoint */ ++ struct yaffs_block_info *bi = ++ yaffs_get_block_info(dev, dev->checkpt_cur_block); ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ dev->blocks_in_checkpt++; ++ } ++ ++ chunk = ++ dev->checkpt_cur_block * dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", ++ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, ++ tags.obj_id, tags.chunk_id); ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ ++ dev->n_page_writes++; ++ ++ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, ++ dev->checkpt_buffer, &tags); ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { ++ dev->checkpt_cur_chunk = 0; ++ dev->checkpt_cur_block = -1; ++ } ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ ++ ++ return 1; ++} ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (!dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) ++ ok = yaffs2_checkpt_flush_buffer(dev); ++ } ++ ++ return i; ++} ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ struct yaffs_ext_tags tags; ++ int chunk; ++ int offset_chunk; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) { ++ ok = 0; ++ break; ++ } ++ ++ chunk = dev->checkpt_cur_block * ++ dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ dev->n_page_reads++; ++ ++ /* read in the next chunk */ ++ dev->tagger.read_chunk_tags_fn(dev, ++ offset_chunk, ++ dev->checkpt_buffer, ++ &tags); ++ ++ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) { ++ ok = 0; ++ break; ++ } ++ if(!yaffs2_checkpt_check_chunk_hdr(dev)) { ++ ok = 0; ++ break; ++ } ++ ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ ++ if (dev->checkpt_cur_chunk >= ++ dev->param.chunks_per_block) ++ dev->checkpt_cur_block = -1; ++ ++ } ++ ++ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ } ++ ++ return i; ++} ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->checkpt_open_write) { ++ if (dev->checkpt_byte_offs != ++ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) ++ yaffs2_checkpt_flush_buffer(dev); ++ } else if (dev->checkpt_block_list) { ++ for (i = 0; ++ i < dev->blocks_in_checkpt && ++ dev->checkpt_block_list[i] >= 0; i++) { ++ int blk = dev->checkpt_block_list[i]; ++ struct yaffs_block_info *bi = NULL; ++ ++ if (dev->internal_start_block <= blk && ++ blk <= dev->internal_end_block) ++ bi = yaffs_get_block_info(dev, blk); ++ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ } ++ kfree(dev->checkpt_block_list); ++ dev->checkpt_block_list = NULL; ++ } ++ ++ dev->n_free_chunks -= ++ dev->blocks_in_checkpt * dev->param.chunks_per_block; ++ dev->n_erased_blocks -= dev->blocks_in_checkpt; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", ++ dev->checkpt_byte_count); ++ ++ if (dev->checkpt_buffer) { ++ /* free the buffer */ ++ kfree(dev->checkpt_buffer); ++ dev->checkpt_buffer = NULL; ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) ++{ ++ /* Erase the checkpoint data */ ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint invalidate of %d blocks", ++ dev->blocks_in_checkpt); ++ ++ return yaffs_checkpt_erase(dev); ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.12.20/fs/yaffs2/yaffs_checkptrw.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_CHECKPTRW_H__ ++#define __YAFFS_CHECKPTRW_H__ ++ ++#include "yaffs_guts.h" ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev); ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_ecc.c linux-3.12.20/fs/yaffs2/yaffs_ecc.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_ecc.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,281 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two ++ * such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_ecc.h" ++ ++/* Table generated by gen-ecc.c ++ * Using a table means we do not have to calculate p1..p4 and p1'..p4' ++ * for each byte of data. These are instead provided in a table in bits7..2. ++ * Bit 0 of each entry indicates whether the entry has an odd or even parity, ++ * and therefore this bytes influence on the line parity. ++ */ ++ ++static const unsigned char column_parity_table[] = { ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++}; ++ ++ ++/* Calculate the ECC for a 256-byte block of data */ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned char line_parity = 0; ++ unsigned char line_parity_prime = 0; ++ unsigned char t; ++ unsigned char b; ++ ++ for (i = 0; i < 256; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ } ++ ++ ecc[2] = (~col_parity) | 0x03; ++ ++ t = 0; ++ if (line_parity & 0x80) ++ t |= 0x80; ++ if (line_parity_prime & 0x80) ++ t |= 0x40; ++ if (line_parity & 0x40) ++ t |= 0x20; ++ if (line_parity_prime & 0x40) ++ t |= 0x10; ++ if (line_parity & 0x20) ++ t |= 0x08; ++ if (line_parity_prime & 0x20) ++ t |= 0x04; ++ if (line_parity & 0x10) ++ t |= 0x02; ++ if (line_parity_prime & 0x10) ++ t |= 0x01; ++ ecc[1] = ~t; ++ ++ t = 0; ++ if (line_parity & 0x08) ++ t |= 0x80; ++ if (line_parity_prime & 0x08) ++ t |= 0x40; ++ if (line_parity & 0x04) ++ t |= 0x20; ++ if (line_parity_prime & 0x04) ++ t |= 0x10; ++ if (line_parity & 0x02) ++ t |= 0x08; ++ if (line_parity_prime & 0x02) ++ t |= 0x04; ++ if (line_parity & 0x01) ++ t |= 0x02; ++ if (line_parity_prime & 0x01) ++ t |= 0x01; ++ ecc[0] = ~t; ++ ++} ++ ++/* Correct the ECC on a 256 byte block of data */ ++ ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc) ++{ ++ unsigned char d0, d1, d2; /* deltas */ ++ ++ d0 = read_ecc[0] ^ test_ecc[0]; ++ d1 = read_ecc[1] ^ test_ecc[1]; ++ d2 = read_ecc[2] ^ test_ecc[2]; ++ ++ if ((d0 | d1 | d2) == 0) ++ return 0; /* no error */ ++ ++ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && ++ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && ++ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { ++ /* Single bit (recoverable) error in data */ ++ ++ unsigned byte; ++ unsigned bit; ++ ++ bit = byte = 0; ++ ++ if (d1 & 0x80) ++ byte |= 0x80; ++ if (d1 & 0x20) ++ byte |= 0x40; ++ if (d1 & 0x08) ++ byte |= 0x20; ++ if (d1 & 0x02) ++ byte |= 0x10; ++ if (d0 & 0x80) ++ byte |= 0x08; ++ if (d0 & 0x20) ++ byte |= 0x04; ++ if (d0 & 0x08) ++ byte |= 0x02; ++ if (d0 & 0x02) ++ byte |= 0x01; ++ ++ if (d2 & 0x80) ++ bit |= 0x04; ++ if (d2 & 0x20) ++ bit |= 0x02; ++ if (d2 & 0x08) ++ bit |= 0x01; ++ ++ data[byte] ^= (1 << bit); ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ read_ecc[0] = test_ecc[0]; ++ read_ecc[1] = test_ecc[1]; ++ read_ecc[2] = test_ecc[2]; ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++ ++} ++ ++/* ++ * ECCxxxOther does ECC calcs on arbitrary n bytes of data ++ */ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc_other) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned line_parity = 0; ++ unsigned line_parity_prime = 0; ++ unsigned char b; ++ ++ for (i = 0; i < n_bytes; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { ++ /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ ++ } ++ ++ ecc_other->col_parity = (col_parity >> 2) & 0x3f; ++ ecc_other->line_parity = line_parity; ++ ecc_other->line_parity_prime = line_parity_prime; ++} ++ ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc) ++{ ++ unsigned char delta_col; /* column parity delta */ ++ unsigned delta_line; /* line parity delta */ ++ unsigned delta_line_prime; /* line parity delta */ ++ unsigned bit; ++ ++ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; ++ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; ++ delta_line_prime = ++ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; ++ ++ if ((delta_col | delta_line | delta_line_prime) == 0) ++ return 0; /* no error */ ++ ++ if (delta_line == ~delta_line_prime && ++ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { ++ /* Single bit (recoverable) error in data */ ++ ++ bit = 0; ++ ++ if (delta_col & 0x20) ++ bit |= 0x04; ++ if (delta_col & 0x08) ++ bit |= 0x02; ++ if (delta_col & 0x02) ++ bit |= 0x01; ++ ++ if (delta_line >= n_bytes) ++ return -1; ++ ++ data[delta_line] ^= (1 << bit); ++ ++ return 1; /* corrected */ ++ } ++ ++ if ((hweight32(delta_line) + ++ hweight32(delta_line_prime) + ++ hweight8(delta_col)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ *read_ecc = *test_ecc; ++ return 1; /* corrected */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_ecc.h linux-3.12.20/fs/yaffs2/yaffs_ecc.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_ecc.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. ++ * Thus, two such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#ifndef __YAFFS_ECC_H__ ++#define __YAFFS_ECC_H__ ++ ++struct yaffs_ecc_other { ++ unsigned char col_parity; ++ unsigned line_parity; ++ unsigned line_parity_prime; ++}; ++ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc); ++ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc); ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc); ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.12.20/fs/yaffs2/yaffs_getblockinfo.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,35 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GETBLOCKINFO_H__ ++#define __YAFFS_GETBLOCKINFO_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++ ++/* Function to manipulate block info */ ++static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev ++ *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs: get_block_info block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return &dev->block_info[blk - dev->internal_start_block]; ++} ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_guts.c linux-3.12.20/fs/yaffs2/yaffs_guts.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_guts.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,5146 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++#include "yaffs_guts.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_tagsmarshall.h" ++#include "yaffs_nand.h" ++#include "yaffs_yaffs1.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_verify.h" ++#include "yaffs_nand.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nameval.h" ++#include "yaffs_allocator.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ ++#define YAFFS_GC_GOOD_ENOUGH 2 ++#define YAFFS_GC_PASSIVE_THRESHOLD 4 ++ ++#include "yaffs_ecc.h" ++ ++/* Forward declarations */ ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve); ++ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size); ++ ++/* Function to calculate chunk and offset */ ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out) ++{ ++ int chunk; ++ u32 offset; ++ ++ chunk = (u32) (addr >> dev->chunk_shift); ++ ++ if (dev->chunk_div == 1) { ++ /* easy power of 2 case */ ++ offset = (u32) (addr & dev->chunk_mask); ++ } else { ++ /* Non power-of-2 case */ ++ ++ loff_t chunk_base; ++ ++ chunk /= dev->chunk_div; ++ ++ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; ++ offset = (u32) (addr - chunk_base); ++ } ++ ++ *chunk_out = chunk; ++ *offset_out = offset; ++} ++ ++/* Function to return the number of shifts for a power of 2 greater than or ++ * equal to the given number ++ * Note we don't try to cater for all possible numbers and this does not have to ++ * be hellishly efficient. ++ */ ++ ++static inline u32 calc_shifts_ceiling(u32 x) ++{ ++ int extra_bits; ++ int shifts; ++ ++ shifts = extra_bits = 0; ++ ++ while (x > 1) { ++ if (x & 1) ++ extra_bits++; ++ x >>= 1; ++ shifts++; ++ } ++ ++ if (extra_bits) ++ shifts++; ++ ++ return shifts; ++} ++ ++/* Function to return the number of shifts to get a 1 in bit 0 ++ */ ++ ++static inline u32 calc_shifts(u32 x) ++{ ++ u32 shifts; ++ ++ shifts = 0; ++ ++ if (!x) ++ return 0; ++ ++ while (!(x & 1)) { ++ x >>= 1; ++ shifts++; ++ } ++ ++ return shifts; ++} ++ ++/* ++ * Temporary buffer manipulations. ++ */ ++ ++static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) ++{ ++ int i; ++ u8 *buf = (u8 *) 1; ++ ++ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); ++ ++ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { ++ dev->temp_buffer[i].in_use = 0; ++ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ dev->temp_buffer[i].buffer = buf; ++ } ++ ++ return buf ? YAFFS_OK : YAFFS_FAIL; ++} ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) ++{ ++ int i; ++ ++ dev->temp_in_use++; ++ if (dev->temp_in_use > dev->max_temp) ++ dev->max_temp = dev->temp_in_use; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].in_use == 0) { ++ dev->temp_buffer[i].in_use = 1; ++ return dev->temp_buffer[i].buffer; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); ++ /* ++ * If we got here then we have to allocate an unmanaged one ++ * This is not good. ++ */ ++ ++ dev->unmanaged_buffer_allocs++; ++ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); ++ ++} ++ ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) ++{ ++ int i; ++ ++ dev->temp_in_use--; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].buffer == buffer) { ++ dev->temp_buffer[i].in_use = 0; ++ return; ++ } ++ } ++ ++ if (buffer) { ++ /* assume it is an unmanaged one. */ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, ++ "Releasing unmanaged temp buffer"); ++ kfree(buffer); ++ dev->unmanaged_buffer_deallocs++; ++ } ++ ++} ++ ++/* ++ * Functions for robustisizing TODO ++ * ++ */ ++ ++static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) data; ++ (void) tags; ++} ++ ++static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) tags; ++} ++ ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ if (!bi->gc_prioritise) { ++ bi->gc_prioritise = 1; ++ dev->has_pending_prioritised_gc = 1; ++ bi->chunk_error_strikes++; ++ ++ if (bi->chunk_error_strikes > 3) { ++ bi->needs_retiring = 1; /* Too many stikes, so retire */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Block struck out"); ++ ++ } ++ } ++} ++ ++static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, ++ int erased_ok) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs_handle_chunk_error(dev, bi); ++ ++ if (erased_ok) { ++ /* Was an actual write failure, ++ * so mark the block for retirement.*/ ++ bi->needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d needs retiring", flash_block); ++ } ++ ++ /* Delete the chunk */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++} ++ ++/* ++ * Verification code ++ */ ++ ++/* ++ * Simple hash function. Needs to have a reasonable spread ++ */ ++ ++static inline int yaffs_hash_fn(int n) ++{ ++ if (n < 0) ++ n = -n; ++ return n % YAFFS_NOBJECT_BUCKETS; ++} ++ ++/* ++ * Access functions to useful fake objects. ++ * Note that root might have a presence in NAND if permissions are set. ++ */ ++ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) ++{ ++ return dev->root_dir; ++} ++ ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) ++{ ++ return dev->lost_n_found; ++} ++ ++/* ++ * Erased NAND checking functions ++ */ ++ ++int yaffs_check_ff(u8 *buffer, int n_bytes) ++{ ++ /* Horrible, slow implementation */ ++ while (n_bytes--) { ++ if (*buffer != 0xff) ++ return 0; ++ buffer++; ++ } ++ return 1; ++} ++ ++static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int retval = YAFFS_OK; ++ u8 *data = yaffs_get_temp_buffer(dev); ++ struct yaffs_ext_tags tags; ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); ++ ++ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) ++ retval = YAFFS_FAIL; ++ ++ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || ++ tags.chunk_used) { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not erased", nand_chunk); ++ retval = YAFFS_FAIL; ++ } ++ ++ yaffs_release_temp_buffer(dev, data); ++ ++ return retval; ++ ++} ++ ++static int yaffs_verify_chunk_written(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = YAFFS_OK; ++ struct yaffs_ext_tags temp_tags; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); ++ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || ++ temp_tags.obj_id != tags->obj_id || ++ temp_tags.chunk_id != tags->chunk_id || ++ temp_tags.n_bytes != tags->n_bytes) ++ retval = YAFFS_FAIL; ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ return retval; ++} ++ ++ ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) ++{ ++ int reserved_chunks; ++ int reserved_blocks = dev->param.n_reserved_blocks; ++ int checkpt_blocks; ++ ++ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); ++ ++ reserved_chunks = ++ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; ++ ++ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); ++} ++ ++static int yaffs_find_alloc_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ ++ if (dev->n_erased_blocks < 1) { ++ /* Hoosterman we've got a problem. ++ * Can't get space to gc ++ */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no more erased blocks"); ++ ++ return -1; ++ } ++ ++ /* Find an empty block. */ ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ dev->alloc_block_finder++; ++ if (dev->alloc_block_finder < dev->internal_start_block ++ || dev->alloc_block_finder > dev->internal_end_block) { ++ dev->alloc_block_finder = dev->internal_start_block; ++ } ++ ++ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->seq_number++; ++ bi->seq_number = dev->seq_number; ++ dev->n_erased_blocks--; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Allocated block %d, seq %d, %d left" , ++ dev->alloc_block_finder, dev->seq_number, ++ dev->n_erased_blocks); ++ return dev->alloc_block_finder; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs tragedy: no more erased blocks, but there should have been %d", ++ dev->n_erased_blocks); ++ ++ return -1; ++} ++ ++static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, ++ struct yaffs_block_info **block_ptr) ++{ ++ int ret_val; ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block < 0) { ++ /* Get next block to allocate off */ ++ dev->alloc_block = yaffs_find_alloc_block(dev); ++ dev->alloc_page = 0; ++ } ++ ++ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { ++ /* No space unless we're allowed to use the reserve. */ ++ return -1; ++ } ++ ++ if (dev->n_erased_blocks < dev->param.n_reserved_blocks ++ && dev->alloc_page == 0) ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); ++ ++ /* Next page please.... */ ++ if (dev->alloc_block >= 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ ++ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + ++ dev->alloc_page; ++ bi->pages_in_use++; ++ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); ++ ++ dev->alloc_page++; ++ ++ dev->n_free_chunks--; ++ ++ /* If the block is full set the state to full */ ++ if (dev->alloc_page >= dev->param.chunks_per_block) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ if (block_ptr) ++ *block_ptr = bi; ++ ++ return ret_val; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); ++ ++ return -1; ++} ++ ++static int yaffs_get_erased_chunks(struct yaffs_dev *dev) ++{ ++ int n; ++ ++ n = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ if (dev->alloc_block > 0) ++ n += (dev->param.chunks_per_block - dev->alloc_page); ++ ++ return n; ++ ++} ++ ++/* ++ * yaffs_skip_rest_of_block() skips over the rest of the allocation block ++ * if we don't want to write to it. ++ */ ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev) ++{ ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block > 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ } ++} ++ ++static int yaffs_write_new_chunk(struct yaffs_dev *dev, ++ const u8 *data, ++ struct yaffs_ext_tags *tags, int use_reserver) ++{ ++ int attempts = 0; ++ int write_ok = 0; ++ int chunk; ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ do { ++ struct yaffs_block_info *bi = 0; ++ int erased_ok = 0; ++ ++ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); ++ if (chunk < 0) { ++ /* no space */ ++ break; ++ } ++ ++ /* First check this chunk is erased, if it needs ++ * checking. The checking policy (unless forced ++ * always on) is as follows: ++ * ++ * Check the first page we try to write in a block. ++ * If the check passes then we don't need to check any ++ * more. If the check fails, we check again... ++ * If the block has been erased, we don't need to check. ++ * ++ * However, if the block has been prioritised for gc, ++ * then we think there might be something odd about ++ * this block and stop using it. ++ * ++ * Rationale: We should only ever see chunks that have ++ * not been erased if there was a partially written ++ * chunk due to power loss. This checking policy should ++ * catch that case with very few checks and thus save a ++ * lot of checks that are most likely not needed. ++ * ++ * Mods to the above ++ * If an erase check fails or the write fails we skip the ++ * rest of the block. ++ */ ++ ++ /* let's give it a try */ ++ attempts++; ++ ++ if (dev->param.always_check_erased) ++ bi->skip_erased_check = 0; ++ ++ if (!bi->skip_erased_check) { ++ erased_ok = yaffs_check_chunk_erased(dev, chunk); ++ if (erased_ok != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs chunk %d was not erased", ++ chunk); ++ ++ /* If not erased, delete this one, ++ * skip rest of block and ++ * try another chunk */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++ continue; ++ } ++ } ++ ++ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); ++ ++ if (!bi->skip_erased_check) ++ write_ok = ++ yaffs_verify_chunk_written(dev, chunk, data, tags); ++ ++ if (write_ok != YAFFS_OK) { ++ /* Clean up aborted write, skip to next block and ++ * try another chunk */ ++ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); ++ continue; ++ } ++ ++ bi->skip_erased_check = 1; ++ ++ /* Copy the data into the robustification buffer */ ++ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); ++ ++ } while (write_ok != YAFFS_OK && ++ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); ++ ++ if (!write_ok) ++ chunk = -1; ++ ++ if (attempts > 1) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs write required %d attempts", ++ attempts); ++ dev->n_retried_writes += (attempts - 1); ++ } ++ ++ return chunk; ++} ++ ++/* ++ * Block retiring for handling a broken block. ++ */ ++ ++static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { ++ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to mark bad and erase block %d", ++ flash_block); ++ } else { ++ struct yaffs_ext_tags tags; ++ int chunk_id = ++ flash_block * dev->param.chunks_per_block; ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ memset(&tags, 0, sizeof(tags)); ++ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; ++ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - ++ dev->chunk_offset, ++ buffer, ++ &tags) != YAFFS_OK) ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to write bad block marker to block %d", ++ flash_block); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ } ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ bi->gc_prioritise = 0; ++ bi->needs_retiring = 0; ++ ++ dev->n_retired_blocks++; ++} ++ ++/*---------------- Name handling functions ------------*/ ++ ++static u16 yaffs_calc_name_sum(const YCHAR *name) ++{ ++ u16 sum = 0; ++ u16 i = 1; ++ ++ if (!name) ++ return 0; ++ ++ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { ++ ++ /* 0x1f mask is case insensitive */ ++ sum += ((*name) & 0x1f) * i; ++ i++; ++ name++; ++ } ++ return sum; ++} ++ ++ ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ memset(obj->short_name, 0, sizeof(obj->short_name)); ++ ++ if (name && !name[0]) { ++ yaffs_fix_null_name(obj, obj->short_name, ++ YAFFS_SHORT_NAME_LENGTH); ++ name = obj->short_name; ++ } else if (name && ++ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= ++ YAFFS_SHORT_NAME_LENGTH) { ++ strcpy(obj->short_name, name); ++ } ++ ++ obj->sum = yaffs_calc_name_sum(name); ++} ++ ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ memset(tmp_name, 0, sizeof(tmp_name)); ++ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_set_obj_name(obj, tmp_name); ++#else ++ yaffs_set_obj_name(obj, oh->name); ++#endif ++} ++ ++loff_t yaffs_max_file_size(struct yaffs_dev *dev) ++{ ++ if(sizeof(loff_t) < 8) ++ return YAFFS_MAX_FILE_SIZE_32; ++ else ++ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; ++} ++ ++/*-------------------- TNODES ------------------- ++ ++ * List of spare tnodes ++ * The list is hooked together using the first pointer ++ * in the tnode. ++ */ ++ ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); ++ ++ if (tn) { ++ memset(tn, 0, dev->tnode_size); ++ dev->n_tnodes++; ++ } ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ yaffs_free_raw_tnode(dev, tn); ++ dev->n_tnodes--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ yaffs_deinit_raw_tnodes_and_objs(dev); ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++} ++ ++static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos, unsigned val) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 mask; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ val >>= dev->chunk_grp_bits; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ mask = dev->tnode_mask << bit_in_word; ++ ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val << bit_in_word)); ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ mask = ++ dev->tnode_mask >> bit_in_word; ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val >> bit_in_word)); ++ } ++} ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 val; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ val = map[word_in_map] >> bit_in_word; ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ val |= (map[word_in_map] << bit_in_word); ++ } ++ ++ val &= dev->tnode_mask; ++ val <<= dev->chunk_grp_bits; ++ ++ return val; ++} ++ ++/* ------------------- End of individual tnode manipulation -----------------*/ ++ ++/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ ++ * The look up tree is represented by the top tnode and the number of top_level ++ * in the tree. 0 means only the level 0 tnode is in the tree. ++ */ ++ ++/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id) ++{ ++ struct yaffs_tnode *tn = file_struct->top; ++ u32 i; ++ int required_depth; ++ int level = file_struct->top_level; ++ ++ (void) dev; ++ ++ /* Check sane level and chunk Id */ ++ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (i) { ++ i >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) ++ return NULL; /* Not tall enough, so we can't find it */ ++ ++ /* Traverse down to level 0 */ ++ while (level > 0 && tn) { ++ tn = tn->internal[(chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (level - 1) * ++ YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK]; ++ level--; ++ } ++ ++ return tn; ++} ++ ++/* add_find_tnode_0 finds the level 0 tnode if it exists, ++ * otherwise first expands the tree. ++ * This happens in two steps: ++ * 1. If the tree isn't tall enough, then make it taller. ++ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. ++ * ++ * Used when modifying the tree. ++ * ++ * If the tn argument is NULL, then a fresh tnode will be added otherwise the ++ * specified tn will be plugged into the ttree. ++ */ ++ ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn) ++{ ++ int required_depth; ++ int i; ++ int l; ++ struct yaffs_tnode *tn; ++ u32 x; ++ ++ /* Check sane level and page Id */ ++ if (file_struct->top_level < 0 || ++ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) { ++ /* Not tall enough, gotta make the tree taller */ ++ for (i = file_struct->top_level; i < required_depth; i++) { ++ ++ tn = yaffs_get_tnode(dev); ++ ++ if (tn) { ++ tn->internal[0] = file_struct->top; ++ file_struct->top = tn; ++ file_struct->top_level++; ++ } else { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: no more tnodes"); ++ return NULL; ++ } ++ } ++ } ++ ++ /* Traverse down to level 0, adding anything we need */ ++ ++ l = file_struct->top_level; ++ tn = file_struct->top; ++ ++ if (l > 0) { ++ while (l > 0 && tn) { ++ x = (chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK; ++ ++ if ((l > 1) && !tn->internal[x]) { ++ /* Add missing non-level-zero tnode */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } else if (l == 1) { ++ /* Looking from level 1 at level 0 */ ++ if (passed_tn) { ++ /* If we already have one, release it */ ++ if (tn->internal[x]) ++ yaffs_free_tnode(dev, ++ tn->internal[x]); ++ tn->internal[x] = passed_tn; ++ ++ } else if (!tn->internal[x]) { ++ /* Don't have one, none passed in */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } ++ } ++ ++ tn = tn->internal[x]; ++ l--; ++ } ++ } else { ++ /* top is level 0 */ ++ if (passed_tn) { ++ memcpy(tn, passed_tn, ++ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); ++ yaffs_free_tnode(dev, passed_tn); ++ } ++ } ++ ++ return tn; ++} ++ ++static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, ++ int chunk_obj) ++{ ++ return (tags->chunk_id == chunk_obj && ++ tags->obj_id == obj_id && ++ !tags->is_deleted) ? 1 : 0; ++ ++} ++ ++static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, ++ struct yaffs_ext_tags *tags, int obj_id, ++ int inode_chunk) ++{ ++ int j; ++ ++ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { ++ if (yaffs_check_chunk_bit ++ (dev, the_chunk / dev->param.chunks_per_block, ++ the_chunk % dev->param.chunks_per_block)) { ++ ++ if (dev->chunk_grp_size == 1) ++ return the_chunk; ++ else { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ tags); ++ if (yaffs_tags_match(tags, ++ obj_id, inode_chunk)) { ++ /* found it; */ ++ return the_chunk; ++ } ++ } ++ } ++ the_chunk++; ++ } ++ return -1; ++} ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /*Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ int ret_val = -1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ return ret_val; ++} ++ ++static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /* Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ int ret_val = -1; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ ++ /* Delete the entry in the filestructure (if found) */ ++ if (ret_val != -1) ++ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); ++ ++ return ret_val; ++} ++ ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan) ++{ ++ /* NB in_scan is zero unless scanning. ++ * For forward scanning, in_scan is > 0; ++ * for backward scanning in_scan is < 0 ++ * ++ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. ++ */ ++ ++ struct yaffs_tnode *tn; ++ struct yaffs_dev *dev = in->my_dev; ++ int existing_cunk; ++ struct yaffs_ext_tags existing_tags; ++ struct yaffs_ext_tags new_tags; ++ unsigned existing_serial, new_serial; ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { ++ /* Just ignore an attempt at putting a chunk into a non-file ++ * during scanning. ++ * If it is not during Scanning then something went wrong! ++ */ ++ if (!in_scan) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy:attempt to put data chunk into a non-file" ++ ); ++ BUG(); ++ } ++ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ ++ tn = yaffs_add_find_tnode_0(dev, ++ &in->variant.file_variant, ++ inode_chunk, NULL); ++ if (!tn) ++ return YAFFS_FAIL; ++ ++ if (!nand_chunk) ++ /* Dummy insert, bail now */ ++ return YAFFS_OK; ++ ++ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ if (in_scan != 0) { ++ /* If we're scanning then we need to test for duplicates ++ * NB This does not need to be efficient since it should only ++ * happen when the power fails during a write, then only one ++ * chunk should ever be affected. ++ * ++ * Correction for YAFFS2: This could happen quite a lot and we ++ * need to think about efficiency! TODO ++ * Update: For backward scanning we don't need to re-read tags ++ * so this is quite cheap. ++ */ ++ ++ if (existing_cunk > 0) { ++ /* NB Right now existing chunk will not be real ++ * chunk_id if the chunk group size > 1 ++ * thus we have to do a FindChunkInFile to get the ++ * real chunk id. ++ * ++ * We have a duplicate now we need to decide which ++ * one to use: ++ * ++ * Backwards scanning YAFFS2: The old one is what ++ * we use, dump the new one. ++ * YAFFS1: Get both sets of tags and compare serial ++ * numbers. ++ */ ++ ++ if (in_scan > 0) { ++ /* Only do this for forward scanning */ ++ yaffs_rd_chunk_tags_nand(dev, ++ nand_chunk, ++ NULL, &new_tags); ++ ++ /* Do a proper find */ ++ existing_cunk = ++ yaffs_find_chunk_in_file(in, inode_chunk, ++ &existing_tags); ++ } ++ ++ if (existing_cunk <= 0) { ++ /*Hoosterman - how did this happen? */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: existing chunk < 0 in scan" ++ ); ++ ++ } ++ ++ /* NB The deleted flags should be false, otherwise ++ * the chunks will not be loaded during a scan ++ */ ++ ++ if (in_scan > 0) { ++ new_serial = new_tags.serial_number; ++ existing_serial = existing_tags.serial_number; ++ } ++ ++ if ((in_scan > 0) && ++ (existing_cunk <= 0 || ++ ((existing_serial + 1) & 3) == new_serial)) { ++ /* Forward scanning. ++ * Use new ++ * Delete the old one and drop through to ++ * update the tnode ++ */ ++ yaffs_chunk_del(dev, existing_cunk, 1, ++ __LINE__); ++ } else { ++ /* Backward scanning or we want to use the ++ * existing one ++ * Delete the new one and return early so that ++ * the tnode isn't changed ++ */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ } ++ ++ } ++ ++ if (existing_cunk == 0) ++ in->n_data_chunks++; ++ ++ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); ++ ++ return YAFFS_OK; ++} ++ ++static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) ++{ ++ struct yaffs_block_info *the_block; ++ unsigned block_no; ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); ++ ++ block_no = chunk / dev->param.chunks_per_block; ++ the_block = yaffs_get_block_info(dev, block_no); ++ if (the_block) { ++ the_block->soft_del_pages++; ++ dev->n_free_chunks++; ++ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); ++ } ++} ++ ++/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all ++ * the chunks in the file. ++ * All soft deleting does is increment the block's softdelete count and pulls ++ * the chunk out of the tnode. ++ * Thus, essentially this is the same as DeleteWorker except that the chunks ++ * are soft deleted. ++ */ ++ ++static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, ++ u32 level, int chunk_offset) ++{ ++ int i; ++ int the_chunk; ++ int all_done = 1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = YAFFS_NTNODES_INTERNAL - 1; ++ all_done && i >= 0; ++ i--) { ++ if (tn->internal[i]) { ++ all_done = ++ yaffs_soft_del_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) ++ + i); ++ if (all_done) { ++ yaffs_free_tnode(dev, ++ tn->internal[i]); ++ tn->internal[i] = NULL; ++ } else { ++ /* Can this happen? */ ++ } ++ } ++ } ++ return (all_done) ? 1 : 0; ++ } ++ ++ /* level 0 */ ++ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk) { ++ yaffs_soft_del_chunk(dev, the_chunk); ++ yaffs_load_tnode_0(dev, tn, i, 0); ++ } ++ } ++ return 1; ++} ++ ++static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_obj *parent; ++ ++ yaffs_verify_obj_in_dir(obj); ++ parent = obj->parent; ++ ++ yaffs_verify_dir(parent); ++ ++ if (dev && dev->param.remove_obj_fn) ++ dev->param.remove_obj_fn(obj); ++ ++ list_del_init(&obj->siblings); ++ obj->parent = NULL; ++ ++ yaffs_verify_dir(parent); ++} ++ ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) ++{ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a null pointer directory" ++ ); ++ BUG(); ++ return; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a non-directory" ++ ); ++ BUG(); ++ } ++ ++ if (obj->siblings.prev == NULL) { ++ /* Not initialised */ ++ BUG(); ++ } ++ ++ yaffs_verify_dir(directory); ++ ++ yaffs_remove_obj_from_dir(obj); ++ ++ /* Now add it */ ++ list_add(&obj->siblings, &directory->variant.dir_variant.children); ++ obj->parent = directory; ++ ++ if (directory == obj->my_dev->unlinked_dir ++ || directory == obj->my_dev->del_dir) { ++ obj->unlinked = 1; ++ obj->my_dev->n_unlinked_files++; ++ obj->rename_allowed = 0; ++ } ++ ++ yaffs_verify_dir(directory); ++ yaffs_verify_obj_in_dir(obj); ++} ++ ++static int yaffs_change_obj_name(struct yaffs_obj *obj, ++ struct yaffs_obj *new_dir, ++ const YCHAR *new_name, int force, int shadows) ++{ ++ int unlink_op; ++ int del_op; ++ struct yaffs_obj *existing_target; ++ ++ if (new_dir == NULL) ++ new_dir = obj->parent; /* use the old directory */ ++ ++ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_change_obj_name: new_dir is not a directory" ++ ); ++ BUG(); ++ } ++ ++ unlink_op = (new_dir == obj->my_dev->unlinked_dir); ++ del_op = (new_dir == obj->my_dev->del_dir); ++ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ ++ /* If the object is a file going into the unlinked directory, ++ * then it is OK to just stuff it in since duplicate names are OK. ++ * else only proceed if the new name does not exist and we're putting ++ * it into a directory. ++ */ ++ if (!(unlink_op || del_op || force || ++ shadows > 0 || !existing_target) || ++ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ return YAFFS_FAIL; ++ ++ yaffs_set_obj_name(obj, new_name); ++ obj->dirty = 1; ++ yaffs_add_obj_to_dir(new_dir, obj); ++ ++ if (unlink_op) ++ obj->unlinked = 1; ++ ++ /* If it is a deletion then we mark it as a shrink for gc */ ++ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++/*------------------------ Short Operations Cache ------------------------------ ++ * In many situations where there is no high level buffering a lot of ++ * reads might be short sequential reads, and a lot of writes may be short ++ * sequential writes. eg. scanning/writing a jpeg file. ++ * In these cases, a short read/write cache can provide a huge perfomance ++ * benefit with dumb-as-a-rock code. ++ * In Linux, the page cache provides read buffering and the short op cache ++ * provides write buffering. ++ * ++ * There are a small number (~10) of cache chunks per device so that we don't ++ * need a very intelligent search. ++ */ ++ ++static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ struct yaffs_cache *cache; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ for (i = 0; i < n_caches; i++) { ++ cache = &dev->cache[i]; ++ if (cache->object == obj && cache->dirty) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void yaffs_flush_file_cache(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int lowest = -99; /* Stop compiler whining. */ ++ int i; ++ struct yaffs_cache *cache; ++ int chunk_written = 0; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ if (n_caches < 1) ++ return; ++ do { ++ cache = NULL; ++ ++ /* Find the lowest dirty chunk for this object */ ++ for (i = 0; i < n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].dirty) { ++ if (!cache || ++ dev->cache[i].chunk_id < lowest) { ++ cache = &dev->cache[i]; ++ lowest = cache->chunk_id; ++ } ++ } ++ } ++ ++ if (cache && !cache->locked) { ++ /* Write it out and free it up */ ++ chunk_written = ++ yaffs_wr_data_obj(cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ cache->object = NULL; ++ } ++ } while (cache && chunk_written > 0); ++ ++ if (cache) ++ /* Hoosterman, disk full while writing cache out. */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no space during cache write"); ++} ++ ++/*yaffs_flush_whole_cache(dev) ++ * ++ * ++ */ ++ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int n_caches = dev->param.n_caches; ++ int i; ++ ++ /* Find a dirty object in the cache and flush it... ++ * until there are no further dirty objects. ++ */ ++ do { ++ obj = NULL; ++ for (i = 0; i < n_caches && !obj; i++) { ++ if (dev->cache[i].object && dev->cache[i].dirty) ++ obj = dev->cache[i].object; ++ } ++ if (obj) ++ yaffs_flush_file_cache(obj); ++ } while (obj); ++ ++} ++ ++/* Grab us a cache chunk for use. ++ * First look for an empty one. ++ * Then look for the least recently used non-dirty one. ++ * Then look for the least recently used dirty one...., flush and look again. ++ */ ++static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->param.n_caches > 0) { ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (!dev->cache[i].object) ++ return &dev->cache[i]; ++ } ++ } ++ return NULL; ++} ++ ++static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_cache *cache; ++ struct yaffs_obj *the_obj; ++ int usage; ++ int i; ++ int pushout; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ /* Try find a non-dirty one... */ ++ ++ cache = yaffs_grab_chunk_worker(dev); ++ ++ if (!cache) { ++ /* They were all dirty, find the LRU object and flush ++ * its cache, then find again. ++ * NB what's here is not very accurate, ++ * we actually flush the object with the LRU chunk. ++ */ ++ ++ /* With locking we can't assume we can use entry zero, ++ * Set the_obj to a valid pointer for Coverity. */ ++ the_obj = dev->cache[0].object; ++ usage = -1; ++ cache = NULL; ++ pushout = -1; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object && ++ !dev->cache[i].locked && ++ (dev->cache[i].last_use < usage || ++ !cache)) { ++ usage = dev->cache[i].last_use; ++ the_obj = dev->cache[i].object; ++ cache = &dev->cache[i]; ++ pushout = i; ++ } ++ } ++ ++ if (!cache || cache->dirty) { ++ /* Flush and try again */ ++ yaffs_flush_file_cache(the_obj); ++ cache = yaffs_grab_chunk_worker(dev); ++ } ++ } ++ return cache; ++} ++ ++/* Find a cached chunk */ ++static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, ++ int chunk_id) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].chunk_id == chunk_id) { ++ dev->cache_hits++; ++ ++ return &dev->cache[i]; ++ } ++ } ++ return NULL; ++} ++ ++/* Mark the chunk for the least recently used algorithym */ ++static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, ++ int is_write) ++{ ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return; ++ ++ if (dev->cache_last_use < 0 || ++ dev->cache_last_use > 100000000) { ++ /* Reset the cache usages */ ++ for (i = 1; i < dev->param.n_caches; i++) ++ dev->cache[i].last_use = 0; ++ ++ dev->cache_last_use = 0; ++ } ++ dev->cache_last_use++; ++ cache->last_use = dev->cache_last_use; ++ ++ if (is_write) ++ cache->dirty = 1; ++} ++ ++/* Invalidate a single cache page. ++ * Do this when a whole page gets written, ++ * ie the short cache for this page is no longer valid. ++ */ ++static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) ++{ ++ struct yaffs_cache *cache; ++ ++ if (object->my_dev->param.n_caches > 0) { ++ cache = yaffs_find_chunk_cache(object, chunk_id); ++ ++ if (cache) ++ cache->object = NULL; ++ } ++} ++ ++/* Invalidate all the cache pages associated with this object ++ * Do this whenever ther file is deleted or resized. ++ */ ++static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.n_caches > 0) { ++ /* Invalidate it. */ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == in) ++ dev->cache[i].object = NULL; ++ } ++ } ++} ++ ++static void yaffs_unhash_obj(struct yaffs_obj *obj) ++{ ++ int bucket; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ /* If it is still linked into the bucket list, free from the list */ ++ if (!list_empty(&obj->hash_link)) { ++ list_del_init(&obj->hash_link); ++ bucket = yaffs_hash_fn(obj->obj_id); ++ dev->obj_bucket[bucket].count--; ++ } ++} ++ ++/* FreeObject frees up a Object and puts it back on the free list */ ++static void yaffs_free_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) { ++ BUG(); ++ return; ++ } ++ dev = obj->my_dev; ++ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", ++ obj, obj->my_inode); ++ if (obj->parent) ++ BUG(); ++ if (!list_empty(&obj->siblings)) ++ BUG(); ++ ++ if (obj->my_inode) { ++ /* We're still hooked up to a cached inode. ++ * Don't delete now, but mark for later deletion ++ */ ++ obj->defered_free = 1; ++ return; ++ } ++ ++ yaffs_unhash_obj(obj); ++ ++ yaffs_free_raw_obj(dev, obj); ++ dev->n_obj--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj) ++{ ++ if (obj->defered_free) ++ yaffs_free_obj(obj); ++} ++ ++static int yaffs_generic_obj_del(struct yaffs_obj *in) ++{ ++ /* Iinvalidate the file's data in the cache, without flushing. */ ++ yaffs_invalidate_whole_cache(in); ++ ++ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { ++ /* Move to unlinked directory so we have a deletion record */ ++ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, ++ 0); ++ } ++ ++ yaffs_remove_obj_from_dir(in); ++ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); ++ in->hdr_chunk = 0; ++ ++ yaffs_free_obj(in); ++ return YAFFS_OK; ++ ++} ++ ++static void yaffs_soft_del_file(struct yaffs_obj *obj) ++{ ++ if (!obj->deleted || ++ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || ++ obj->soft_del) ++ return; ++ ++ if (obj->n_data_chunks <= 0) { ++ /* Empty file with no duplicate object headers, ++ * just delete it immediately */ ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: Deleting empty file %d", ++ obj->obj_id); ++ yaffs_generic_obj_del(obj); ++ } else { ++ yaffs_soft_del_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant. ++ file_variant.top_level, 0); ++ obj->soft_del = 1; ++ } ++} ++ ++/* Pruning removes any part of the file structure tree that is beyond the ++ * bounds of the file (ie that does not point to chunks). ++ * ++ * A file should only get pruned when its size is reduced. ++ * ++ * Before pruning, the chunks must be pulled from the tree and the ++ * level 0 tnode entries must be zeroed out. ++ * Could also use this for file deletion, but that's probably better handled ++ * by a special case. ++ * ++ * This function is recursive. For levels > 0 the function is called again on ++ * any sub-tree. For level == 0 we just check if the sub-tree has data. ++ * If there is no data in a subtree then it is pruned. ++ */ ++ ++static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, ++ struct yaffs_tnode *tn, u32 level, ++ int del0) ++{ ++ int i; ++ int has_data; ++ ++ if (!tn) ++ return tn; ++ ++ has_data = 0; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) { ++ tn->internal[i] = ++ yaffs_prune_worker(dev, ++ tn->internal[i], ++ level - 1, ++ (i == 0) ? del0 : 1); ++ } ++ ++ if (tn->internal[i]) ++ has_data++; ++ } ++ } else { ++ int tnode_size_u32 = dev->tnode_size / sizeof(u32); ++ u32 *map = (u32 *) tn; ++ ++ for (i = 0; !has_data && i < tnode_size_u32; i++) { ++ if (map[i]) ++ has_data++; ++ } ++ } ++ ++ if (has_data == 0 && del0) { ++ /* Free and return NULL */ ++ yaffs_free_tnode(dev, tn); ++ tn = NULL; ++ } ++ return tn; ++} ++ ++static int yaffs_prune_tree(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct) ++{ ++ int i; ++ int has_data; ++ int done = 0; ++ struct yaffs_tnode *tn; ++ ++ if (file_struct->top_level < 1) ++ return YAFFS_OK; ++ ++ file_struct->top = ++ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); ++ ++ /* Now we have a tree with all the non-zero branches NULL but ++ * the height is the same as it was. ++ * Let's see if we can trim internal tnodes to shorten the tree. ++ * We can do this if only the 0th element in the tnode is in use ++ * (ie all the non-zero are NULL) ++ */ ++ ++ while (file_struct->top_level && !done) { ++ tn = file_struct->top; ++ ++ has_data = 0; ++ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) ++ has_data++; ++ } ++ ++ if (!has_data) { ++ file_struct->top = tn->internal[0]; ++ file_struct->top_level--; ++ yaffs_free_tnode(dev, tn); ++ } else { ++ done = 1; ++ } ++ } ++ ++ return YAFFS_OK; ++} ++ ++/*-------------------- End of File Structure functions.-------------------*/ ++ ++/* alloc_empty_obj gets us a clean Object.*/ ++static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); ++ ++ if (!obj) ++ return obj; ++ ++ dev->n_obj++; ++ ++ /* Now sweeten it up... */ ++ ++ memset(obj, 0, sizeof(struct yaffs_obj)); ++ obj->being_created = 1; ++ ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; ++ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; ++ INIT_LIST_HEAD(&(obj->hard_links)); ++ INIT_LIST_HEAD(&(obj->hash_link)); ++ INIT_LIST_HEAD(&obj->siblings); ++ ++ /* Now make the directory sane */ ++ if (dev->root_dir) { ++ obj->parent = dev->root_dir; ++ list_add(&(obj->siblings), ++ &dev->root_dir->variant.dir_variant.children); ++ } ++ ++ /* Add it to the lost and found directory. ++ * NB Can't put root or lost-n-found in lost-n-found so ++ * check if lost-n-found exists first ++ */ ++ if (dev->lost_n_found) ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ ++ obj->being_created = 0; ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return obj; ++} ++ ++static int yaffs_find_nice_bucket(struct yaffs_dev *dev) ++{ ++ int i; ++ int l = 999; ++ int lowest = 999999; ++ ++ /* Search for the shortest list or one that ++ * isn't too long. ++ */ ++ ++ for (i = 0; i < 10 && lowest > 4; i++) { ++ dev->bucket_finder++; ++ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; ++ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { ++ lowest = dev->obj_bucket[dev->bucket_finder].count; ++ l = dev->bucket_finder; ++ } ++ } ++ ++ return l; ++} ++ ++static int yaffs_new_obj_id(struct yaffs_dev *dev) ++{ ++ int bucket = yaffs_find_nice_bucket(dev); ++ int found = 0; ++ struct list_head *i; ++ u32 n = (u32) bucket; ++ ++ /* Now find an object value that has not already been taken ++ * by scanning the list. ++ */ ++ ++ while (!found) { ++ found = 1; ++ n += YAFFS_NOBJECT_BUCKETS; ++ if (1 || dev->obj_bucket[bucket].count > 0) { ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* If there is already one in the list */ ++ if (i && list_entry(i, struct yaffs_obj, ++ hash_link)->obj_id == n) { ++ found = 0; ++ } ++ } ++ } ++ } ++ return n; ++} ++ ++static void yaffs_hash_obj(struct yaffs_obj *in) ++{ ++ int bucket = yaffs_hash_fn(in->obj_id); ++ struct yaffs_dev *dev = in->my_dev; ++ ++ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); ++ dev->obj_bucket[bucket].count++; ++} ++ ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) ++{ ++ int bucket = yaffs_hash_fn(number); ++ struct list_head *i; ++ struct yaffs_obj *in; ++ ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* Look if it is in the list */ ++ in = list_entry(i, struct yaffs_obj, hash_link); ++ if (in->obj_id == number) { ++ /* Don't show if it is defered free */ ++ if (in->defered_free) ++ return NULL; ++ return in; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (number < 0) ++ number = yaffs_new_obj_id(dev); ++ ++ if (type == YAFFS_OBJECT_TYPE_FILE) { ++ tn = yaffs_get_tnode(dev); ++ if (!tn) ++ return NULL; ++ } ++ ++ the_obj = yaffs_alloc_empty_obj(dev); ++ if (!the_obj) { ++ if (tn) ++ yaffs_free_tnode(dev, tn); ++ return NULL; ++ } ++ ++ the_obj->fake = 0; ++ the_obj->rename_allowed = 1; ++ the_obj->unlink_allowed = 1; ++ the_obj->obj_id = number; ++ yaffs_hash_obj(the_obj); ++ the_obj->variant_type = type; ++ yaffs_load_current_time(the_obj, 1, 1); ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ the_obj->variant.file_variant.file_size = 0; ++ the_obj->variant.file_variant.scanned_size = 0; ++ the_obj->variant.file_variant.shrink_size = ++ yaffs_max_file_size(dev); ++ the_obj->variant.file_variant.top_level = 0; ++ the_obj->variant.file_variant.top = tn; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* No action required */ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* todo this should not happen */ ++ break; ++ } ++ return the_obj; ++} ++ ++static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, ++ int number, u32 mode) ++{ ++ ++ struct yaffs_obj *obj = ++ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); ++ ++ if (!obj) ++ return NULL; ++ ++ obj->fake = 1; /* it is fake so it might not use NAND */ ++ obj->rename_allowed = 0; ++ obj->unlink_allowed = 0; ++ obj->deleted = 0; ++ obj->unlinked = 0; ++ obj->yst_mode = mode; ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; /* Not a valid chunk. */ ++ return obj; ++ ++} ++ ++ ++static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++ yaffs_init_raw_tnodes_and_objs(dev); ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ INIT_LIST_HEAD(&dev->obj_bucket[i].list); ++ dev->obj_bucket[i].count = 0; ++ } ++} ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ ++ if (number > 0) ++ the_obj = yaffs_find_by_number(dev, number); ++ ++ if (!the_obj) ++ the_obj = yaffs_new_obj(dev, number, type); ++ ++ return the_obj; ++ ++} ++ ++YCHAR *yaffs_clone_str(const YCHAR *str) ++{ ++ YCHAR *new_str = NULL; ++ int len; ++ ++ if (!str) ++ str = _Y(""); ++ ++ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); ++ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); ++ if (new_str) { ++ strncpy(new_str, str, len); ++ new_str[len] = 0; ++ } ++ return new_str; ++ ++} ++/* ++ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new ++ * link (ie. name) is created or deleted in the directory. ++ * ++ * ie. ++ * create dir/a : update dir's mtime/ctime ++ * rm dir/a: update dir's mtime/ctime ++ * modify dir/a: don't update dir's mtimme/ctime ++ * ++ * This can be handled immediately or defered. Defering helps reduce the number ++ * of updates when many files in a directory are changed within a brief period. ++ * ++ * If the directory updating is defered then yaffs_update_dirty_dirs must be ++ * called periodically. ++ */ ++ ++static void yaffs_update_parent(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) ++ return; ++ dev = obj->my_dev; ++ obj->dirty = 1; ++ yaffs_load_current_time(obj, 0, 1); ++ if (dev->param.defered_dir_update) { ++ struct list_head *link = &obj->variant.dir_variant.dirty; ++ ++ if (list_empty(link)) { ++ list_add(link, &dev->dirty_dirs); ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Added object %d to dirty directories", ++ obj->obj_id); ++ } ++ ++ } else { ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev) ++{ ++ struct list_head *link; ++ struct yaffs_obj *obj; ++ struct yaffs_dir_var *d_s; ++ union yaffs_obj_var *o_v; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); ++ ++ while (!list_empty(&dev->dirty_dirs)) { ++ link = dev->dirty_dirs.next; ++ list_del_init(link); ++ ++ d_s = list_entry(link, struct yaffs_dir_var, dirty); ++ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); ++ obj = list_entry(o_v, struct yaffs_obj, variant); ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", ++ obj->obj_id); ++ ++ if (obj->dirty) ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++/* ++ * Mknod (create) a new object. ++ * equiv_obj only has meaning for a hard link; ++ * alias_str only has meaning for a symlink. ++ * rdev only has meaning for devices (a subset of special objects) ++ */ ++ ++static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, ++ struct yaffs_obj *parent, ++ const YCHAR *name, ++ u32 mode, ++ u32 uid, ++ u32 gid, ++ struct yaffs_obj *equiv_obj, ++ const YCHAR *alias_str, u32 rdev) ++{ ++ struct yaffs_obj *in; ++ YCHAR *str = NULL; ++ struct yaffs_dev *dev = parent->my_dev; ++ ++ /* Check if the entry exists. ++ * If it does then fail the call since we don't want a dup. */ ++ if (yaffs_find_by_name(parent, name)) ++ return NULL; ++ ++ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ str = yaffs_clone_str(alias_str); ++ if (!str) ++ return NULL; ++ } ++ ++ in = yaffs_new_obj(dev, -1, type); ++ ++ if (!in) { ++ kfree(str); ++ return NULL; ++ } ++ ++ in->hdr_chunk = 0; ++ in->valid = 1; ++ in->variant_type = type; ++ ++ in->yst_mode = mode; ++ ++ yaffs_attribs_init(in, gid, uid, rdev); ++ ++ in->n_data_chunks = 0; ++ ++ yaffs_set_obj_name(in, name); ++ in->dirty = 1; ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ in->my_dev = parent->my_dev; ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant.alias = str; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant.hardlink_variant.equiv_obj = equiv_obj; ++ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; ++ list_add(&in->hard_links, &equiv_obj->hard_links); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* do nothing */ ++ break; ++ } ++ ++ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { ++ /* Could not create the object header, fail */ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ if (in) ++ yaffs_update_parent(parent); ++ ++ return in; ++} ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, ++ uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, ++ mode, uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, ++ uid, gid, NULL, NULL, rdev); ++} ++ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, ++ uid, gid, NULL, alias, 0); ++} ++ ++/* yaffs_link_obj returns the object id of the equivalent object.*/ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, ++ struct yaffs_obj *equiv_obj) ++{ ++ /* Get the real object in case we were fed a hard link obj */ ++ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); ++ ++ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, ++ parent, name, 0, 0, 0, ++ equiv_obj, NULL, 0)) ++ return equiv_obj; ++ ++ return NULL; ++ ++} ++ ++ ++ ++/*---------------------- Block Management and Page Allocation -------------*/ ++ ++static void yaffs_deinit_blocks(struct yaffs_dev *dev) ++{ ++ if (dev->block_info_alt && dev->block_info) ++ vfree(dev->block_info); ++ else ++ kfree(dev->block_info); ++ ++ dev->block_info_alt = 0; ++ ++ dev->block_info = NULL; ++ ++ if (dev->chunk_bits_alt && dev->chunk_bits) ++ vfree(dev->chunk_bits); ++ else ++ kfree(dev->chunk_bits); ++ dev->chunk_bits_alt = 0; ++ dev->chunk_bits = NULL; ++} ++ ++static int yaffs_init_blocks(struct yaffs_dev *dev) ++{ ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ dev->block_info = NULL; ++ dev->chunk_bits = NULL; ++ dev->alloc_block = -1; /* force it to get a new one */ ++ ++ /* If the first allocation strategy fails, thry the alternate one */ ++ dev->block_info = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); ++ if (!dev->block_info) { ++ dev->block_info = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); ++ dev->block_info_alt = 1; ++ } else { ++ dev->block_info_alt = 0; ++ } ++ ++ if (!dev->block_info) ++ goto alloc_error; ++ ++ /* Set up dynamic blockinfo stuff. Round up bytes. */ ++ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; ++ dev->chunk_bits = ++ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); ++ if (!dev->chunk_bits) { ++ dev->chunk_bits = ++ vmalloc(dev->chunk_bit_stride * n_blocks); ++ dev->chunk_bits_alt = 1; ++ } else { ++ dev->chunk_bits_alt = 0; ++ } ++ if (!dev->chunk_bits) ++ goto alloc_error; ++ ++ ++ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); ++ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); ++ return YAFFS_OK; ++ ++alloc_error: ++ yaffs_deinit_blocks(dev); ++ return YAFFS_FAIL; ++} ++ ++ ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); ++ int erased_ok = 0; ++ int i; ++ ++ /* If the block is still healthy erase it and mark as clean. ++ * If the block has had a data failure, then retire it. ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, ++ "yaffs_block_became_dirty block %d state %d %s", ++ block_no, bi->block_state, ++ (bi->needs_retiring) ? "needs retiring" : ""); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; ++ ++ /* If this is the block being garbage collected then stop gc'ing */ ++ if (block_no == dev->gc_block) ++ dev->gc_block = 0; ++ ++ /* If this block is currently the best candidate for gc ++ * then drop as a candidate */ ++ if (block_no == dev->gc_dirtiest) { ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ } ++ ++ if (!bi->needs_retiring) { ++ yaffs2_checkpt_invalidate(dev); ++ erased_ok = yaffs_erase_block(dev, block_no); ++ if (!erased_ok) { ++ dev->n_erase_failures++; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Erasure failed %d", block_no); ++ } ++ } ++ ++ /* Verify erasure if needed */ ++ if (erased_ok && ++ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || ++ !yaffs_skip_verification(dev))) { ++ for (i = 0; i < dev->param.chunks_per_block; i++) { ++ if (!yaffs_check_chunk_erased(dev, ++ block_no * dev->param.chunks_per_block + i)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ ">>Block %d erasure supposedly OK, but chunk %d not erased", ++ block_no, i); ++ } ++ } ++ } ++ ++ if (!erased_ok) { ++ /* We lost a block of free space */ ++ dev->n_free_chunks -= dev->param.chunks_per_block; ++ yaffs_retire_block(dev, block_no); ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d retired", block_no); ++ return; ++ } ++ ++ /* Clean it up... */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ bi->seq_number = 0; ++ dev->n_erased_blocks++; ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ bi->has_shrink_hdr = 0; ++ bi->skip_erased_check = 1; /* Clean, so no need to check */ ++ bi->gc_prioritise = 0; ++ bi->has_summary = 0; ++ ++ yaffs_clear_chunk_bits(dev, block_no); ++ ++ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); ++} ++ ++static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int old_chunk, u8 *buffer) ++{ ++ int new_chunk; ++ int mark_flash = 1; ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj *object; ++ int matching_chunk; ++ int ret_val = YAFFS_OK; ++ ++ memset(&tags, 0, sizeof(tags)); ++ yaffs_rd_chunk_tags_nand(dev, old_chunk, ++ buffer, &tags); ++ object = yaffs_find_by_number(dev, tags.obj_id); ++ ++ yaffs_trace(YAFFS_TRACE_GC_DETAIL, ++ "Collecting chunk in block %d, %d %d %d ", ++ dev->gc_chunk, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ ++ if (object && !yaffs_skip_verification(dev)) { ++ if (tags.chunk_id == 0) ++ matching_chunk = ++ object->hdr_chunk; ++ else if (object->soft_del) ++ /* Defeat the test */ ++ matching_chunk = old_chunk; ++ else ++ matching_chunk = ++ yaffs_find_chunk_in_file ++ (object, tags.chunk_id, ++ NULL); ++ ++ if (old_chunk != matching_chunk) ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "gc: page in gc mismatch: %d %d %d %d", ++ old_chunk, ++ matching_chunk, ++ tags.obj_id, ++ tags.chunk_id); ++ } ++ ++ if (!object) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "page %d in gc has no object: %d %d %d ", ++ old_chunk, ++ tags.obj_id, tags.chunk_id, ++ tags.n_bytes); ++ } ++ ++ if (object && ++ object->deleted && ++ object->soft_del && tags.chunk_id != 0) { ++ /* Data chunk in a soft deleted file, ++ * throw it away. ++ * It's a soft deleted data chunk, ++ * No need to copy this, just forget ++ * about it and fix up the object. ++ */ ++ ++ /* Free chunks already includes ++ * softdeleted chunks, how ever this ++ * chunk is going to soon be really ++ * deleted which will increment free ++ * chunks. We have to decrement free ++ * chunks so this works out properly. ++ */ ++ dev->n_free_chunks--; ++ bi->soft_del_pages--; ++ ++ object->n_data_chunks--; ++ if (object->n_data_chunks <= 0) { ++ /* remeber to clean up obj */ ++ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; ++ dev->n_clean_ups++; ++ } ++ mark_flash = 0; ++ } else if (object) { ++ /* It's either a data chunk in a live ++ * file or an ObjectHeader, so we're ++ * interested in it. ++ * NB Need to keep the ObjectHeaders of ++ * deleted files until the whole file ++ * has been deleted off ++ */ ++ tags.serial_number++; ++ dev->n_gc_copies++; ++ ++ if (tags.chunk_id == 0) { ++ /* It is an object Id, ++ * We need to nuke the ++ * shrinkheader flags since its ++ * work is done. ++ * Also need to clean up ++ * shadowing. ++ */ ++ struct yaffs_obj_hdr *oh; ++ oh = (struct yaffs_obj_hdr *) buffer; ++ ++ oh->is_shrink = 0; ++ tags.extra_is_shrink = 0; ++ oh->shadows_obj = 0; ++ oh->inband_shadowed_obj_id = 0; ++ tags.extra_shadows = 0; ++ ++ /* Update file size */ ++ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { ++ yaffs_oh_size_load(oh, ++ object->variant.file_variant.file_size); ++ tags.extra_file_size = ++ object->variant.file_variant.file_size; ++ } ++ ++ yaffs_verify_oh(object, oh, &tags, 1); ++ new_chunk = ++ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); ++ } else { ++ new_chunk = ++ yaffs_write_new_chunk(dev, buffer, &tags, 1); ++ } ++ ++ if (new_chunk < 0) { ++ ret_val = YAFFS_FAIL; ++ } else { ++ ++ /* Now fix up the Tnodes etc. */ ++ ++ if (tags.chunk_id == 0) { ++ /* It's a header */ ++ object->hdr_chunk = new_chunk; ++ object->serial = tags.serial_number; ++ } else { ++ /* It's a data chunk */ ++ yaffs_put_chunk_in_file(object, tags.chunk_id, ++ new_chunk, 0); ++ } ++ } ++ } ++ if (ret_val == YAFFS_OK) ++ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); ++ return ret_val; ++} ++ ++static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) ++{ ++ int old_chunk; ++ int ret_val = YAFFS_OK; ++ int i; ++ int is_checkpt_block; ++ int max_copies; ++ int chunks_before = yaffs_get_erased_chunks(dev); ++ int chunks_after; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); ++ ++ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d, in use %d, shrink %d, whole_block %d", ++ block, bi->pages_in_use, bi->has_shrink_hdr, ++ whole_block); ++ ++ /*yaffs_verify_free_chunks(dev); */ ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; ++ ++ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ ++ ++ dev->gc_disable = 1; ++ ++ yaffs_summary_gc(dev, block); ++ ++ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d that has no chunks in use", ++ block); ++ yaffs_block_became_dirty(dev, block); ++ } else { ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_verify_blk(dev, bi, block); ++ ++ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; ++ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; ++ ++ for (/* init already done */ ; ++ ret_val == YAFFS_OK && ++ dev->gc_chunk < dev->param.chunks_per_block && ++ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && ++ max_copies > 0; ++ dev->gc_chunk++, old_chunk++) { ++ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { ++ /* Page is in use and might need to be copied */ ++ max_copies--; ++ ret_val = yaffs_gc_process_chunk(dev, bi, ++ old_chunk, buffer); ++ } ++ } ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ yaffs_verify_collected_blk(dev, bi, block); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ /* ++ * The gc did not complete. Set block state back to FULL ++ * because checkpointing does not restore gc. ++ */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } else { ++ /* The gc completed. */ ++ /* Do any required cleanups */ ++ for (i = 0; i < dev->n_clean_ups; i++) { ++ /* Time to delete the file too */ ++ struct yaffs_obj *object = ++ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); ++ if (object) { ++ yaffs_free_tnode(dev, ++ object->variant.file_variant.top); ++ object->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: About to finally delete object %d", ++ object->obj_id); ++ yaffs_generic_obj_del(object); ++ object->my_dev->n_deleted_files--; ++ } ++ ++ } ++ chunks_after = yaffs_get_erased_chunks(dev); ++ if (chunks_before >= chunks_after) ++ yaffs_trace(YAFFS_TRACE_GC, ++ "gc did not increase free chunks before %d after %d", ++ chunks_before, chunks_after); ++ dev->gc_block = 0; ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ dev->gc_disable = 0; ++ ++ return ret_val; ++} ++ ++/* ++ * find_gc_block() selects the dirtiest block (or close enough) ++ * for garbage collection. ++ */ ++ ++static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, ++ int aggressive, int background) ++{ ++ int i; ++ int iterations; ++ unsigned selected = 0; ++ int prioritised = 0; ++ int prioritised_exist = 0; ++ struct yaffs_block_info *bi; ++ int threshold; ++ ++ /* First let's see if we need to grab a prioritised block */ ++ if (dev->has_pending_prioritised_gc && !aggressive) { ++ dev->gc_dirtiest = 0; ++ bi = dev->block_info; ++ for (i = dev->internal_start_block; ++ i <= dev->internal_end_block && !selected; i++) { ++ ++ if (bi->gc_prioritise) { ++ prioritised_exist = 1; ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ selected = i; ++ prioritised = 1; ++ } ++ } ++ bi++; ++ } ++ ++ /* ++ * If there is a prioritised block and none was selected then ++ * this happened because there is at least one old dirty block ++ * gumming up the works. Let's gc the oldest dirty block. ++ */ ++ ++ if (prioritised_exist && ++ !selected && dev->oldest_dirty_block > 0) ++ selected = dev->oldest_dirty_block; ++ ++ if (!prioritised_exist) /* None found, so we can clear this */ ++ dev->has_pending_prioritised_gc = 0; ++ } ++ ++ /* If we're doing aggressive GC then we are happy to take a less-dirty ++ * block, and search harder. ++ * else (leasurely gc), then we only bother to do this if the ++ * block has only a few pages in use. ++ */ ++ ++ if (!selected) { ++ int pages_used; ++ int n_blocks = ++ dev->internal_end_block - dev->internal_start_block + 1; ++ if (aggressive) { ++ threshold = dev->param.chunks_per_block; ++ iterations = n_blocks; ++ } else { ++ int max_threshold; ++ ++ if (background) ++ max_threshold = dev->param.chunks_per_block / 2; ++ else ++ max_threshold = dev->param.chunks_per_block / 8; ++ ++ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ ++ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; ++ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ if (threshold > max_threshold) ++ threshold = max_threshold; ++ ++ iterations = n_blocks / 16 + 1; ++ if (iterations > 100) ++ iterations = 100; ++ } ++ ++ for (i = 0; ++ i < iterations && ++ (dev->gc_dirtiest < 1 || ++ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); ++ i++) { ++ dev->gc_block_finder++; ++ if (dev->gc_block_finder < dev->internal_start_block || ++ dev->gc_block_finder > dev->internal_end_block) ++ dev->gc_block_finder = ++ dev->internal_start_block; ++ ++ bi = yaffs_get_block_info(dev, dev->gc_block_finder); ++ ++ pages_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ pages_used < dev->param.chunks_per_block && ++ (dev->gc_dirtiest < 1 || ++ pages_used < dev->gc_pages_in_use) && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ dev->gc_dirtiest = dev->gc_block_finder; ++ dev->gc_pages_in_use = pages_used; ++ } ++ } ++ ++ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) ++ selected = dev->gc_dirtiest; ++ } ++ ++ /* ++ * If nothing has been selected for a while, try the oldest dirty ++ * because that's gumming up the works. ++ */ ++ ++ if (!selected && dev->param.is_yaffs2 && ++ dev->gc_not_done >= (background ? 10 : 20)) { ++ yaffs2_find_oldest_dirty_seq(dev); ++ if (dev->oldest_dirty_block > 0) { ++ selected = dev->oldest_dirty_block; ++ dev->gc_dirtiest = selected; ++ dev->oldest_dirty_gc_count++; ++ bi = yaffs_get_block_info(dev, selected); ++ dev->gc_pages_in_use = ++ bi->pages_in_use - bi->soft_del_pages; ++ } else { ++ dev->gc_not_done = 0; ++ } ++ } ++ ++ if (selected) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC Selected block %d with %d free, prioritised:%d", ++ selected, ++ dev->param.chunks_per_block - dev->gc_pages_in_use, ++ prioritised); ++ ++ dev->n_gc_blocks++; ++ if (background) ++ dev->bg_gcs++; ++ ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ dev->gc_not_done = 0; ++ if (dev->refresh_skip > 0) ++ dev->refresh_skip--; ++ } else { ++ dev->gc_not_done++; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", ++ dev->gc_block_finder, dev->gc_not_done, threshold, ++ dev->gc_dirtiest, dev->gc_pages_in_use, ++ dev->oldest_dirty_block, background ? " bg" : ""); ++ } ++ ++ return selected; ++} ++ ++/* New garbage collector ++ * If we're very low on erased blocks then we do aggressive garbage collection ++ * otherwise we do "leasurely" garbage collection. ++ * Aggressive gc looks further (whole array) and will accept less dirty blocks. ++ * Passive gc only inspects smaller areas and only accepts more dirty blocks. ++ * ++ * The idea is to help clear out space in a more spread-out manner. ++ * Dunno if it really does anything useful. ++ */ ++static int yaffs_check_gc(struct yaffs_dev *dev, int background) ++{ ++ int aggressive = 0; ++ int gc_ok = YAFFS_OK; ++ int max_tries = 0; ++ int min_erased; ++ int erased_chunks; ++ int checkpt_block_adjust; ++ ++ if (dev->param.gc_control_fn && ++ (dev->param.gc_control_fn(dev) & 1) == 0) ++ return YAFFS_OK; ++ ++ if (dev->gc_disable) ++ /* Bail out so we don't get recursive gc */ ++ return YAFFS_OK; ++ ++ /* This loop should pass the first time. ++ * Only loops here if the collection does not increase space. ++ */ ++ ++ do { ++ max_tries++; ++ ++ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); ++ ++ min_erased = ++ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; ++ erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ /* If we need a block soon then do aggressive gc. */ ++ if (dev->n_erased_blocks < min_erased) ++ aggressive = 1; ++ else { ++ if (!background ++ && erased_chunks > (dev->n_free_chunks / 4)) ++ break; ++ ++ if (dev->gc_skip > 20) ++ dev->gc_skip = 20; ++ if (erased_chunks < dev->n_free_chunks / 2 || ++ dev->gc_skip < 1 || background) ++ aggressive = 0; ++ else { ++ dev->gc_skip--; ++ break; ++ } ++ } ++ ++ dev->gc_skip = 5; ++ ++ /* If we don't already have a block being gc'd then see if we ++ * should start another */ ++ ++ if (dev->gc_block < 1 && !aggressive) { ++ dev->gc_block = yaffs2_find_refresh_block(dev); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ if (dev->gc_block < 1) { ++ dev->gc_block = ++ yaffs_find_gc_block(dev, aggressive, background); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ if (dev->gc_block > 0) { ++ dev->all_gcs++; ++ if (!aggressive) ++ dev->passive_gc_count++; ++ ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC n_erased_blocks %d aggressive %d", ++ dev->n_erased_blocks, aggressive); ++ ++ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); ++ } ++ ++ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && ++ dev->gc_block > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", ++ dev->n_erased_blocks, max_tries, ++ dev->gc_block); ++ } ++ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && ++ (dev->gc_block > 0) && (max_tries < 2)); ++ ++ return aggressive ? gc_ok : YAFFS_OK; ++} ++ ++/* ++ * yaffs_bg_gc() ++ * Garbage collects. Intended to be called from a background thread. ++ * Returns non-zero if at least half the free chunks are erased. ++ */ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) ++{ ++ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); ++ ++ yaffs_check_gc(dev, 1); ++ return erased_chunks > dev->n_free_chunks / 2; ++} ++ ++/*-------------------- Data file manipulation -----------------*/ ++ ++static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) ++{ ++ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); ++ ++ if (nand_chunk >= 0) ++ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, ++ buffer, NULL); ++ else { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not found zero instead", ++ nand_chunk); ++ /* get sane (zero) data if you read a hole */ ++ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); ++ return 0; ++ } ++ ++} ++ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn) ++{ ++ int block; ++ int page; ++ struct yaffs_ext_tags tags; ++ struct yaffs_block_info *bi; ++ ++ if (chunk_id <= 0) ++ return; ++ ++ dev->n_deletions++; ++ block = chunk_id / dev->param.chunks_per_block; ++ page = chunk_id % dev->param.chunks_per_block; ++ ++ if (!yaffs_check_chunk_bit(dev, block, page)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Deleting invalid chunk %d", chunk_id); ++ ++ bi = yaffs_get_block_info(dev, block); ++ ++ yaffs2_update_oldest_dirty_seq(dev, block, bi); ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, ++ "line %d delete of chunk %d", ++ lyn, chunk_id); ++ ++ if (!dev->param.is_yaffs2 && mark_flash && ++ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { ++ ++ memset(&tags, 0, sizeof(tags)); ++ tags.is_deleted = 1; ++ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); ++ yaffs_handle_chunk_update(dev, chunk_id, &tags); ++ } else { ++ dev->n_unmarked_deletions++; ++ } ++ ++ /* Pull out of the management area. ++ * If the whole block became dirty, this will kick off an erasure. ++ */ ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || ++ bi->block_state == YAFFS_BLOCK_STATE_FULL || ++ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ dev->n_free_chunks++; ++ yaffs_clear_chunk_bit(dev, block, page); ++ bi->pages_in_use--; ++ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && ++ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ yaffs_block_became_dirty(dev, block); ++ } ++ } ++} ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve) ++{ ++ /* Find old chunk Need to do this to get serial number ++ * Write new one and patch into tree. ++ * Invalidate old tags. ++ */ ++ ++ int prev_chunk_id; ++ struct yaffs_ext_tags prev_tags; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ yaffs_check_gc(dev, 0); ++ ++ /* Get the previous chunk at this location in the file if it exists. ++ * If it does not exist then put a zero into the tree. This creates ++ * the tnode now, rather than later when it is harder to clean up. ++ */ ++ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); ++ if (prev_chunk_id < 1 && ++ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) ++ return 0; ++ ++ /* Set up new tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ ++ new_tags.chunk_id = inode_chunk; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = ++ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; ++ new_tags.n_bytes = n_bytes; ++ ++ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Writing %d bytes to chunk!!!!!!!!!", ++ n_bytes); ++ BUG(); ++ } ++ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); ++ ++ if (new_chunk_id > 0) { ++ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ yaffs_verify_file_sane(in); ++ } ++ return new_chunk_id; ++ ++} ++ ++ ++ ++static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, ++ const YCHAR *name, const void *value, int size, ++ int flags) ++{ ++ struct yaffs_xattr_mod xmod; ++ int result; ++ ++ xmod.set = set; ++ xmod.name = name; ++ xmod.data = value; ++ xmod.size = size; ++ xmod.flags = flags; ++ xmod.result = -ENOSPC; ++ ++ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); ++ ++ if (result > 0) ++ return xmod.result; ++ else ++ return -ENOSPC; ++} ++ ++static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, ++ struct yaffs_xattr_mod *xmod) ++{ ++ int retval = 0; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer = buffer + x_offs; ++ ++ if (xmod->set) ++ retval = ++ nval_set(x_buffer, x_size, xmod->name, xmod->data, ++ xmod->size, xmod->flags); ++ else ++ retval = nval_del(x_buffer, x_size, xmod->name); ++ ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ xmod->result = retval; ++ ++ return retval; ++} ++ ++static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, ++ void *value, int size) ++{ ++ char *buffer = NULL; ++ int result; ++ struct yaffs_ext_tags tags; ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer; ++ int retval = 0; ++ ++ if (obj->hdr_chunk < 1) ++ return -ENODATA; ++ ++ /* If we know that the object has no xattribs then don't do all the ++ * reading and parsing. ++ */ ++ if (obj->xattr_known && !obj->has_xattr) { ++ if (name) ++ return -ENODATA; ++ else ++ return 0; ++ } ++ ++ buffer = (char *)yaffs_get_temp_buffer(dev); ++ if (!buffer) ++ return -ENOMEM; ++ ++ result = ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ retval = -ENOENT; ++ else { ++ x_buffer = buffer + x_offs; ++ ++ if (!obj->xattr_known) { ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ } ++ ++ if (name) ++ retval = nval_get(x_buffer, x_size, name, value, size); ++ else ++ retval = nval_list(x_buffer, x_size, value, size); ++ } ++ yaffs_release_temp_buffer(dev, (u8 *) buffer); ++ return retval; ++} ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, ++ const void *value, int size, int flags) ++{ ++ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); ++} ++ ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); ++} ++ ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, ++ int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, name, value, size); ++} ++ ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); ++} ++ ++static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) ++{ ++ u8 *buf; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ ++ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) ++ return; ++ ++ dev = in->my_dev; ++ in->lazy_loaded = 0; ++ buf = yaffs_get_temp_buffer(dev); ++ ++ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); ++ oh = (struct yaffs_obj_hdr *)buf; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ yaffs_set_obj_name_from_oh(in, oh); ++ ++ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ in->variant.symlink_variant.alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant.symlink_variant.alias) ++ alloc_failed = 1; /* Not returned */ ++ } ++ yaffs_release_temp_buffer(dev, buf); ++} ++ ++static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, ++ const YCHAR *oh_name, int buff_size) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ if (dev->param.auto_unicode) { ++ if (*oh_name) { ++ /* It is an ASCII name, do an ASCII to ++ * unicode conversion */ ++ const char *ascii_oh_name = (const char *)oh_name; ++ int n = buff_size - 1; ++ while (n > 0 && *ascii_oh_name) { ++ *name = *ascii_oh_name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ strncpy(name, oh_name + 1, buff_size - 1); ++ } ++ } else { ++#else ++ (void) dev; ++ { ++#endif ++ strncpy(name, oh_name, buff_size - 1); ++ } ++} ++ ++static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, ++ const YCHAR *name) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ ++ int is_ascii; ++ YCHAR *w; ++ ++ if (dev->param.auto_unicode) { ++ ++ is_ascii = 1; ++ w = name; ++ ++ /* Figure out if the name will fit in ascii character set */ ++ while (is_ascii && *w) { ++ if ((*w) & 0xff00) ++ is_ascii = 0; ++ w++; ++ } ++ ++ if (is_ascii) { ++ /* It is an ASCII name, so convert unicode to ascii */ ++ char *ascii_oh_name = (char *)oh_name; ++ int n = YAFFS_MAX_NAME_LENGTH - 1; ++ while (n > 0 && *name) { ++ *ascii_oh_name = *name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ /* Unicode name, so save starting at the second YCHAR */ ++ *oh_name = 0; ++ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); ++ } ++ } else { ++#else ++ dev = dev; ++ { ++#endif ++ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); ++ } ++} ++ ++/* UpdateObjectHeader updates the header on NAND for an object. ++ * If name is not NULL, then that new name is used. ++ */ ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, ++ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) ++{ ++ ++ struct yaffs_block_info *bi; ++ struct yaffs_dev *dev = in->my_dev; ++ int prev_chunk_id; ++ int ret_val = 0; ++ int result = 0; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_ext_tags old_tags; ++ const YCHAR *alias = NULL; ++ u8 *buffer = NULL; ++ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj_hdr *oh = NULL; ++ loff_t file_size = 0; ++ ++ strcpy(old_name, _Y("silly old name")); ++ ++ if (in->fake && in != dev->root_dir && !force && !xmod) ++ return ret_val; ++ ++ yaffs_check_gc(dev, 0); ++ yaffs_check_obj_details_loaded(in); ++ ++ buffer = yaffs_get_temp_buffer(in->my_dev); ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ prev_chunk_id = in->hdr_chunk; ++ ++ if (prev_chunk_id > 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, ++ buffer, &old_tags); ++ ++ yaffs_verify_oh(in, oh, &old_tags, 0); ++ memcpy(old_name, oh->name, sizeof(oh->name)); ++ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); ++ } else { ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ } ++ ++ oh->type = in->variant_type; ++ oh->yst_mode = in->yst_mode; ++ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; ++ ++ yaffs_load_attribs_oh(oh, in); ++ ++ if (in->parent) ++ oh->parent_obj_id = in->parent->obj_id; ++ else ++ oh->parent_obj_id = 0; ++ ++ if (name && *name) { ++ memset(oh->name, 0, sizeof(oh->name)); ++ yaffs_load_oh_from_name(dev, oh->name, name); ++ } else if (prev_chunk_id > 0) { ++ memcpy(oh->name, old_name, sizeof(oh->name)); ++ } else { ++ memset(oh->name, 0, sizeof(oh->name)); ++ } ++ ++ oh->is_shrink = is_shrink; ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Should not happen */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && ++ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) ++ file_size = in->variant.file_variant.file_size; ++ yaffs_oh_size_load(oh, file_size); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ oh->equiv_id = in->variant.hardlink_variant.equiv_id; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = in->variant.symlink_variant.alias; ++ if (!alias) ++ alias = _Y("no alias"); ++ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); ++ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; ++ break; ++ } ++ ++ /* process any xattrib modifications */ ++ if (xmod) ++ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); ++ ++ /* Tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ in->serial++; ++ new_tags.chunk_id = 0; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = in->serial; ++ ++ /* Add extra info for file header */ ++ new_tags.extra_available = 1; ++ new_tags.extra_parent_id = oh->parent_obj_id; ++ new_tags.extra_file_size = file_size; ++ new_tags.extra_is_shrink = oh->is_shrink; ++ new_tags.extra_equiv_id = oh->equiv_id; ++ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; ++ new_tags.extra_obj_type = in->variant_type; ++ yaffs_verify_oh(in, oh, &new_tags, 1); ++ ++ /* Create new chunk in NAND */ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, ++ (prev_chunk_id > 0) ? 1 : 0); ++ ++ if (buffer) ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (new_chunk_id < 0) ++ return new_chunk_id; ++ ++ in->hdr_chunk = new_chunk_id; ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ if (!yaffs_obj_cache_dirty(in)) ++ in->dirty = 0; ++ ++ /* If this was a shrink, then mark the block ++ * that the chunk lives on */ ++ if (is_shrink) { ++ bi = yaffs_get_block_info(in->my_dev, ++ new_chunk_id / ++ in->my_dev->param.chunks_per_block); ++ bi->has_shrink_hdr = 1; ++ } ++ ++ ++ return new_chunk_id; ++} ++ ++/*--------------------- File read/write ------------------------ ++ * Read and write have very similar structures. ++ * In general the read/write has three parts to it ++ * An incomplete chunk to start with (if the read/write is not chunk-aligned) ++ * Some complete chunks ++ * An incomplete chunk to end off with ++ * ++ * Curve-balls: the first chunk might also be the last chunk. ++ */ ++ ++int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) ++{ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ struct yaffs_cache *cache; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ chunk++; ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ if ((start + n) < dev->data_bytes_per_chunk) ++ n_copy = n; ++ else ++ n_copy = dev->data_bytes_per_chunk - start; ++ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ /* If the chunk is already in the cache or it is less than ++ * a whole chunk or we're using inband tags then use the cache ++ * (if there is caching) else bypass the cache. ++ */ ++ if (cache || n_copy != dev->data_bytes_per_chunk || ++ dev->param.inband_tags) { ++ if (dev->param.n_caches > 0) { ++ ++ /* If we can't find the data in the cache, ++ * then load it up. */ ++ ++ if (!cache) { ++ cache = ++ yaffs_grab_chunk_cache(in->my_dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ cache->n_bytes = 0; ++ } ++ ++ yaffs_use_cache(dev, cache, 0); ++ ++ cache->locked = 1; ++ ++ memcpy(buffer, &cache->data[start], n_copy); ++ ++ cache->locked = 0; ++ } else { ++ /* Read into the local buffer then copy.. */ ++ ++ u8 *local_buffer = ++ yaffs_get_temp_buffer(dev); ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ ++ memcpy(buffer, &local_buffer[start], n_copy); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Read directly into the buffer. */ ++ yaffs_rd_data_obj(in, chunk, buffer); ++ } ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ return n_done; ++} ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ int n_writeback; ++ loff_t start_write = offset; ++ int chunk_written = 0; ++ u32 n_bytes_read; ++ loff_t chunk_start; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0 && chunk_written >= 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ ++ if (((loff_t)chunk) * ++ dev->data_bytes_per_chunk + start != offset || ++ start >= dev->data_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "AddrToChunk of offset %lld gives chunk %d start %d", ++ offset, chunk, start); ++ } ++ chunk++; /* File pos to chunk in file offset */ ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ ++ if ((start + n) < dev->data_bytes_per_chunk) { ++ n_copy = n; ++ ++ /* Now calculate how many bytes to write back.... ++ * If we're overwriting and not writing to then end of ++ * file then we need to write back as much as was there ++ * before. ++ */ ++ ++ chunk_start = (((loff_t)(chunk - 1)) * ++ dev->data_bytes_per_chunk); ++ ++ if (chunk_start > in->variant.file_variant.file_size) ++ n_bytes_read = 0; /* Past end of file */ ++ else ++ n_bytes_read = ++ in->variant.file_variant.file_size - ++ chunk_start; ++ ++ if (n_bytes_read > dev->data_bytes_per_chunk) ++ n_bytes_read = dev->data_bytes_per_chunk; ++ ++ n_writeback = ++ (n_bytes_read > ++ (start + n)) ? n_bytes_read : (start + n); ++ ++ if (n_writeback < 0 || ++ n_writeback > dev->data_bytes_per_chunk) ++ BUG(); ++ ++ } else { ++ n_copy = dev->data_bytes_per_chunk - start; ++ n_writeback = dev->data_bytes_per_chunk; ++ } ++ ++ if (n_copy != dev->data_bytes_per_chunk || ++ !dev->param.cache_bypass_aligned || ++ dev->param.inband_tags) { ++ /* An incomplete start or end chunk (or maybe both ++ * start and end chunk), or we're using inband tags, ++ * or we're forcing writes through the cache, ++ * so we want to use the cache buffers. ++ */ ++ if (dev->param.n_caches > 0) { ++ struct yaffs_cache *cache; ++ ++ /* If we can't find the data in the cache, then ++ * load the cache */ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ if (!cache && ++ yaffs_check_alloc_available(dev, 1)) { ++ cache = yaffs_grab_chunk_cache(dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ } else if (cache && ++ !cache->dirty && ++ !yaffs_check_alloc_available(dev, ++ 1)) { ++ /* Drop the cache if it was a read cache ++ * item and no space check has been made ++ * for it. ++ */ ++ cache = NULL; ++ } ++ ++ if (cache) { ++ yaffs_use_cache(dev, cache, 1); ++ cache->locked = 1; ++ ++ memcpy(&cache->data[start], buffer, ++ n_copy); ++ ++ cache->locked = 0; ++ cache->n_bytes = n_writeback; ++ ++ if (write_through) { ++ chunk_written = ++ yaffs_wr_data_obj ++ (cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ } ++ } else { ++ chunk_written = -1; /* fail write */ ++ } ++ } else { ++ /* An incomplete start or end chunk (or maybe ++ * both start and end chunk). Read into the ++ * local buffer then copy over and write back. ++ */ ++ ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ memcpy(&local_buffer[start], buffer, n_copy); ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, ++ local_buffer, ++ n_writeback, 0); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Write directly from the buffer. */ ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, buffer, ++ dev->data_bytes_per_chunk, 0); ++ ++ /* Since we've overwritten the cached data, ++ * we better invalidate it. */ ++ yaffs_invalidate_chunk_cache(in, chunk); ++ } ++ ++ if (chunk_written >= 0) { ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ } ++ ++ /* Update file object */ ++ ++ if ((start_write + n_done) > in->variant.file_variant.file_size) ++ in->variant.file_variant.file_size = (start_write + n_done); ++ ++ in->dirty = 1; ++ return n_done; ++} ++ ++int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ yaffs2_handle_hole(in, offset); ++ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); ++} ++ ++/* ---------------------- File resizing stuff ------------------ */ ++ ++static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) ++{ ++ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ int i; ++ int chunk_id; ++ u32 dummy; ++ int last_del; ++ int start_del; ++ ++ if (old_size > 0) ++ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); ++ else ++ last_del = 0; ++ ++ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, ++ &start_del, &dummy); ++ last_del++; ++ start_del++; ++ ++ /* Delete backwards so that we don't end up with holes if ++ * power is lost part-way through the operation. ++ */ ++ for (i = last_del; i >= start_del; i--) { ++ /* NB this could be optimised somewhat, ++ * eg. could retrieve the tags and write them without ++ * using yaffs_chunk_del ++ */ ++ ++ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); ++ ++ if (chunk_id < 1) ++ continue; ++ ++ if (chunk_id < ++ (dev->internal_start_block * dev->param.chunks_per_block) || ++ chunk_id >= ++ ((dev->internal_end_block + 1) * ++ dev->param.chunks_per_block)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Found daft chunk_id %d for %d", ++ chunk_id, i); ++ } else { ++ in->n_data_chunks--; ++ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); ++ } ++ } ++} ++ ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) ++{ ++ int new_full; ++ u32 new_partial; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); ++ ++ yaffs_prune_chunks(obj, new_size); ++ ++ if (new_partial != 0) { ++ int last_chunk = 1 + new_full; ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ /* Rewrite the last chunk with its new size and zero pad */ ++ yaffs_rd_data_obj(obj, last_chunk, local_buffer); ++ memset(local_buffer + new_partial, 0, ++ dev->data_bytes_per_chunk - new_partial); ++ ++ yaffs_wr_data_obj(obj, last_chunk, local_buffer, ++ new_partial, 1); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ ++ obj->variant.file_variant.file_size = new_size; ++ ++ yaffs_prune_tree(dev, &obj->variant.file_variant); ++} ++ ++int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) ++{ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ ++ yaffs_flush_file_cache(in); ++ yaffs_invalidate_whole_cache(in); ++ ++ yaffs_check_gc(dev, 0); ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ if (new_size == old_size) ++ return YAFFS_OK; ++ ++ if (new_size > old_size) { ++ yaffs2_handle_hole(in, new_size); ++ in->variant.file_variant.file_size = new_size; ++ } else { ++ /* new_size < old_size */ ++ yaffs_resize_file_down(in, new_size); ++ } ++ ++ /* Write a new object header to reflect the resize. ++ * show we've shrunk the file, if need be ++ * Do this only if the file is not in the deleted directories ++ * and is not shadowed. ++ */ ++ if (in->parent && ++ !in->is_shadowed && ++ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ in->parent->obj_id != YAFFS_OBJECTID_DELETED) ++ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); ++ ++ return YAFFS_OK; ++} ++ ++int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync) ++{ ++ if (!in->dirty) ++ return YAFFS_OK; ++ ++ yaffs_flush_file_cache(in); ++ ++ if (data_sync) ++ return YAFFS_OK; ++ ++ if (update_time) ++ yaffs_load_current_time(in, 0, 0); ++ ++ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? ++ YAFFS_OK : YAFFS_FAIL; ++} ++ ++ ++/* yaffs_del_file deletes the whole file data ++ * and the inode associated with the file. ++ * It does not delete the links associated with the file. ++ */ ++static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) ++{ ++ int ret_val; ++ int del_now = 0; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!in->my_inode) ++ del_now = 1; ++ ++ if (del_now) { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->del_dir, ++ _Y("deleted"), 0, 0); ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: immediate deletion of file %d", ++ in->obj_id); ++ in->deleted = 1; ++ in->my_dev->n_deleted_files++; ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ yaffs_soft_del_file(in); ++ } else { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++ return ret_val; ++} ++ ++static int yaffs_del_file(struct yaffs_obj *in) ++{ ++ int ret_val = YAFFS_OK; ++ int deleted; /* Need to cache value on stack if in is freed */ ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ ++ if (in->n_data_chunks > 0) { ++ /* Use soft deletion if there is data in the file. ++ * That won't be the case if it has been resized to zero. ++ */ ++ if (!in->unlinked) ++ ret_val = yaffs_unlink_file_if_needed(in); ++ ++ deleted = in->deleted; ++ ++ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { ++ in->deleted = 1; ++ deleted = 1; ++ in->my_dev->n_deleted_files++; ++ yaffs_soft_del_file(in); ++ } ++ return deleted ? YAFFS_OK : YAFFS_FAIL; ++ } else { ++ /* The file has no data chunks so we toss it immediately */ ++ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); ++ in->variant.file_variant.top = NULL; ++ yaffs_generic_obj_del(in); ++ ++ return YAFFS_OK; ++ } ++} ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj) ++{ ++ return (obj && ++ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && ++ !(list_empty(&obj->variant.dir_variant.children)); ++} ++ ++static int yaffs_del_dir(struct yaffs_obj *obj) ++{ ++ /* First check that the directory is empty. */ ++ if (yaffs_is_non_empty_dir(obj)) ++ return YAFFS_FAIL; ++ ++ return yaffs_generic_obj_del(obj); ++} ++ ++static int yaffs_del_symlink(struct yaffs_obj *in) ++{ ++ kfree(in->variant.symlink_variant.alias); ++ in->variant.symlink_variant.alias = NULL; ++ ++ return yaffs_generic_obj_del(in); ++} ++ ++static int yaffs_del_link(struct yaffs_obj *in) ++{ ++ /* remove this hardlink from the list associated with the equivalent ++ * object ++ */ ++ list_del_init(&in->hard_links); ++ return yaffs_generic_obj_del(in); ++} ++ ++int yaffs_del_obj(struct yaffs_obj *obj) ++{ ++ int ret_val = -1; ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ ret_val = yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!list_empty(&obj->variant.dir_variant.dirty)) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Remove object %d from dirty directories", ++ obj->obj_id); ++ list_del_init(&obj->variant.dir_variant.dirty); ++ } ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ ret_val = yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ ret_val = yaffs_del_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ ret_val = yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ ret_val = 0; ++ break; /* should not happen. */ ++ } ++ return ret_val; ++} ++ ++ ++static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, ++ struct yaffs_obj *to_dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_add_obj_to_dir(to_dir, obj); ++ } ++} ++ ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type) ++{ ++ /* Tear down the old variant */ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ /* Nuke file data */ ++ yaffs_resize_file(obj, 0); ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Put the children in lost and found. */ ++ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); ++ if (!list_empty(&obj->variant.dir_variant.dirty)) ++ list_del_init(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ /* Nuke symplink data */ ++ kfree(obj->variant.symlink_variant.alias); ++ obj->variant.symlink_variant.alias = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ list_del_init(&obj->hard_links); ++ break; ++ default: ++ break; ++ } ++ ++ memset(&obj->variant, 0, sizeof(obj->variant)); ++ ++ /*Set up new variant if the memset is not enough. */ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ default: ++ break; ++ } ++ ++ obj->variant_type = type; ++ ++ return obj; ++ ++} ++ ++static int yaffs_unlink_worker(struct yaffs_obj *obj) ++{ ++ int del_now = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (!obj->my_inode) ++ del_now = 1; ++ ++ yaffs_update_parent(obj->parent); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ return yaffs_del_link(obj); ++ } else if (!list_empty(&obj->hard_links)) { ++ /* Curve ball: We're unlinking an object that has a hardlink. ++ * ++ * This problem arises because we are not strictly following ++ * The Linux link/inode model. ++ * ++ * We can't really delete the object. ++ * Instead, we do the following: ++ * - Select a hardlink. ++ * - Unhook it from the hard links ++ * - Move it from its parent directory so that the rename works. ++ * - Rename the object to the hardlink's name. ++ * - Delete the hardlink ++ */ ++ ++ struct yaffs_obj *hl; ++ struct yaffs_obj *parent; ++ int ret_val; ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ hl = list_entry(obj->hard_links.next, struct yaffs_obj, ++ hard_links); ++ ++ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); ++ parent = hl->parent; ++ ++ list_del_init(&hl->hard_links); ++ ++ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); ++ ++ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); ++ ++ if (ret_val == YAFFS_OK) ++ ret_val = yaffs_generic_obj_del(hl); ++ ++ return ret_val; ++ ++ } else if (del_now) { ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ list_del_init(&obj->variant.dir_variant.dirty); ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ return yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ return YAFFS_FAIL; ++ } ++ } else if (yaffs_is_non_empty_dir(obj)) { ++ return YAFFS_FAIL; ++ } else { ++ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++} ++ ++static int yaffs_unlink_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->unlink_allowed) ++ return yaffs_unlink_worker(obj); ++ ++ return YAFFS_FAIL; ++} ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) ++{ ++ struct yaffs_obj *obj; ++ ++ obj = yaffs_find_by_name(dir, name); ++ return yaffs_unlink_obj(obj); ++} ++ ++/* Note: ++ * If old_name is NULL then we take old_dir as the object to be renamed. ++ */ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, ++ struct yaffs_obj *new_dir, const YCHAR *new_name) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *existing_target = NULL; ++ int force = 0; ++ int result; ++ struct yaffs_dev *dev; ++ ++ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ dev = old_dir->my_dev; ++ ++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE ++ /* Special case for case insemsitive systems. ++ * While look-up is case insensitive, the name isn't. ++ * Therefore we might want to change x.txt to X.txt ++ */ ++ if (old_dir == new_dir && ++ old_name && new_name && ++ strcmp(old_name, new_name) == 0) ++ force = 1; ++#endif ++ ++ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ /* ENAMETOOLONG */ ++ return YAFFS_FAIL; ++ ++ if (old_name) ++ obj = yaffs_find_by_name(old_dir, old_name); ++ else{ ++ obj = old_dir; ++ old_dir = obj->parent; ++ } ++ ++ if (obj && obj->rename_allowed) { ++ /* Now handle an existing target, if there is one */ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ if (yaffs_is_non_empty_dir(existing_target)) { ++ return YAFFS_FAIL; /* ENOTEMPTY */ ++ } else if (existing_target && existing_target != obj) { ++ /* Nuke the target first, using shadowing, ++ * but only if it isn't the same object. ++ * ++ * Note we must disable gc here otherwise it can mess ++ * up the shadowing. ++ * ++ */ ++ dev->gc_disable = 1; ++ yaffs_change_obj_name(obj, new_dir, new_name, force, ++ existing_target->obj_id); ++ existing_target->is_shadowed = 1; ++ yaffs_unlink_obj(existing_target); ++ dev->gc_disable = 0; ++ } ++ ++ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); ++ ++ yaffs_update_parent(old_dir); ++ if (new_dir != old_dir) ++ yaffs_update_parent(new_dir); ++ ++ return result; ++ } ++ return YAFFS_FAIL; ++} ++ ++/*----------------------- Initialisation Scanning ---------------------- */ ++ ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning) ++{ ++ struct yaffs_obj *obj; ++ ++ if (backward_scanning) { ++ /* Handle YAFFS2 case (backward scanning) ++ * If the shadowed object exists then ignore. ++ */ ++ obj = yaffs_find_by_number(dev, obj_id); ++ if (obj) ++ return; ++ } ++ ++ /* Let's create it (if it does not exist) assuming it is a file so that ++ * it can do shrinking etc. ++ * We put it in unlinked dir to be cleaned up after the scanning ++ */ ++ obj = ++ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); ++ if (!obj) ++ return; ++ obj->is_shadowed = 1; ++ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); ++ obj->variant.file_variant.shrink_size = 0; ++ obj->valid = 1; /* So that we don't read any other info. */ ++} ++ ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) ++{ ++ struct list_head *lh; ++ struct list_head *save; ++ struct yaffs_obj *hl; ++ struct yaffs_obj *in; ++ ++ list_for_each_safe(lh, save, hard_list) { ++ hl = list_entry(lh, struct yaffs_obj, hard_links); ++ in = yaffs_find_by_number(dev, ++ hl->variant.hardlink_variant.equiv_id); ++ ++ if (in) { ++ /* Add the hardlink pointers */ ++ hl->variant.hardlink_variant.equiv_obj = in; ++ list_add(&hl->hard_links, &in->hard_links); ++ } else { ++ /* Todo Need to report/handle this better. ++ * Got a problem... hardlink to a non-existant object ++ */ ++ hl->variant.hardlink_variant.equiv_obj = NULL; ++ INIT_LIST_HEAD(&hl->hard_links); ++ } ++ } ++} ++ ++static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) ++{ ++ /* ++ * Sort out state of unlinked and deleted objects after scanning. ++ */ ++ struct list_head *i; ++ struct list_head *n; ++ struct yaffs_obj *l; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Soft delete all the unlinked files */ ++ list_for_each_safe(i, n, ++ &dev->unlinked_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++ ++ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++} ++ ++/* ++ * This code iterates through all the objects making sure that they are rooted. ++ * Any unrooted objects are re-rooted in lost+found. ++ * An object needs to be in one of: ++ * - Directly under deleted, unlinked ++ * - Directly or indirectly under root. ++ * ++ * Note: ++ * This code assumes that we don't ever change the current relationships ++ * between directories: ++ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL ++ * lost-n-found->parent == root_dir ++ * ++ * This fixes the problem where directories might have inadvertently been ++ * deleted leaving the object "hanging" without being rooted in the ++ * directory tree. ++ */ ++ ++static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ return (obj == dev->del_dir || ++ obj == dev->unlinked_dir || obj == dev->root_dir); ++} ++ ++static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_obj *parent; ++ int i; ++ struct list_head *lh; ++ struct list_head *n; ++ int depth_limit; ++ int hanging; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Iterate through the objects in each hash entry, ++ * looking at each object. ++ * Make sure it is rooted. ++ */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ parent = obj->parent; ++ ++ if (yaffs_has_null_parent(dev, obj)) { ++ /* These directories are not hanging */ ++ hanging = 0; ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ hanging = 1; ++ } else if (yaffs_has_null_parent(dev, parent)) { ++ hanging = 0; ++ } else { ++ /* ++ * Need to follow the parent chain to ++ * see if it is hanging. ++ */ ++ hanging = 0; ++ depth_limit = 100; ++ ++ while (parent != dev->root_dir && ++ parent->parent && ++ parent->parent->variant_type == ++ YAFFS_OBJECT_TYPE_DIRECTORY && ++ depth_limit > 0) { ++ parent = parent->parent; ++ depth_limit--; ++ } ++ if (parent != dev->root_dir) ++ hanging = 1; ++ } ++ if (hanging) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Hanging object %d moved to lost and found", ++ obj->obj_id); ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ } ++ } ++ } ++} ++ ++/* ++ * Delete directory contents for cleaning up lost and found. ++ */ ++static void yaffs_del_dir_contents(struct yaffs_obj *dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ BUG(); ++ ++ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) ++ yaffs_del_dir_contents(obj); ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Deleting lost_found object %d", ++ obj->obj_id); ++ yaffs_unlink_obj(obj); ++ } ++} ++ ++static void yaffs_empty_l_n_f(struct yaffs_dev *dev) ++{ ++ yaffs_del_dir_contents(dev->lost_n_found); ++} ++ ++ ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, ++ const YCHAR *name) ++{ ++ int sum; ++ struct list_head *i; ++ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj *l; ++ ++ if (!name) ++ return NULL; ++ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: null pointer directory" ++ ); ++ BUG(); ++ return NULL; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: non-directory" ++ ); ++ BUG(); ++ } ++ ++ sum = yaffs_calc_name_sum(name); ++ ++ list_for_each(i, &directory->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ ++ if (l->parent != directory) ++ BUG(); ++ ++ yaffs_check_obj_details_loaded(l); ++ ++ /* Special case for lost-n-found */ ++ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) ++ return l; ++ } else if (l->sum == sum || l->hdr_chunk <= 0) { ++ /* LostnFound chunk called Objxxx ++ * Do a real check ++ */ ++ yaffs_get_obj_name(l, buffer, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) ++ return l; ++ } ++ } ++ return NULL; ++} ++ ++/* GetEquivalentObject dereferences any hard links to get to the ++ * actual object. ++ */ ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ obj = obj->variant.hardlink_variant.equiv_obj; ++ yaffs_check_obj_details_loaded(obj); ++ } ++ return obj; ++} ++ ++/* ++ * A note or two on object names. ++ * * If the object name is missing, we then make one up in the form objnnn ++ * ++ * * ASCII names are stored in the object header's name field from byte zero ++ * * Unicode names are historically stored starting from byte zero. ++ * ++ * Then there are automatic Unicode names... ++ * The purpose of these is to save names in a way that can be read as ++ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII ++ * system to share files. ++ * ++ * These automatic unicode are stored slightly differently... ++ * - If the name can fit in the ASCII character space then they are saved as ++ * ascii names as per above. ++ * - If the name needs Unicode then the name is saved in Unicode ++ * starting at oh->name[1]. ++ ++ */ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size) ++{ ++ /* Create an object name if we could not find one. */ ++ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { ++ YCHAR local_name[20]; ++ YCHAR num_string[20]; ++ YCHAR *x = &num_string[19]; ++ unsigned v = obj->obj_id; ++ num_string[19] = 0; ++ while (v > 0) { ++ x--; ++ *x = '0' + (v % 10); ++ v /= 10; ++ } ++ /* make up a name */ ++ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); ++ strcat(local_name, x); ++ strncpy(name, local_name, buffer_size - 1); ++ } ++} ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) ++{ ++ memset(name, 0, buffer_size * sizeof(YCHAR)); ++ yaffs_check_obj_details_loaded(obj); ++ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); ++ } else if (obj->short_name[0]) { ++ strcpy(name, obj->short_name); ++ } else if (obj->hdr_chunk > 0) { ++ int result; ++ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); ++ ++ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; ++ ++ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); ++ ++ if (obj->hdr_chunk > 0) { ++ result = yaffs_rd_chunk_tags_nand(obj->my_dev, ++ obj->hdr_chunk, ++ buffer, NULL); ++ } ++ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, ++ buffer_size); ++ ++ yaffs_release_temp_buffer(obj->my_dev, buffer); ++ } ++ ++ yaffs_fix_null_name(obj, name, buffer_size); ++ ++ return strnlen(name, YAFFS_MAX_NAME_LENGTH); ++} ++ ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj) ++{ ++ /* Dereference any hard linking */ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ return obj->variant.file_variant.file_size; ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ if (!obj->variant.symlink_variant.alias) ++ return 0; ++ return strnlen(obj->variant.symlink_variant.alias, ++ YAFFS_MAX_ALIAS_LENGTH); ++ } else { ++ /* Only a directory should drop through to here */ ++ return obj->my_dev->data_bytes_per_chunk; ++ } ++} ++ ++int yaffs_get_obj_link_count(struct yaffs_obj *obj) ++{ ++ int count = 0; ++ struct list_head *i; ++ ++ if (!obj->unlinked) ++ count++; /* the object itself */ ++ ++ list_for_each(i, &obj->hard_links) ++ count++; /* add the hard links; */ ++ ++ return count; ++} ++ ++int yaffs_get_obj_inode(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ return obj->obj_id; ++} ++ ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ return DT_DIR; ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return DT_LNK; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ if (S_ISFIFO(obj->yst_mode)) ++ return DT_FIFO; ++ if (S_ISCHR(obj->yst_mode)) ++ return DT_CHR; ++ if (S_ISBLK(obj->yst_mode)) ++ return DT_BLK; ++ if (S_ISSOCK(obj->yst_mode)) ++ return DT_SOCK; ++ return DT_REG; ++ break; ++ default: ++ return DT_REG; ++ break; ++ } ++} ++ ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) ++ return yaffs_clone_str(obj->variant.symlink_variant.alias); ++ else ++ return yaffs_clone_str(_Y("")); ++} ++ ++/*--------------------------- Initialisation code -------------------------- */ ++ ++static int yaffs_check_dev_fns(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ struct yaffs_tags_handler *tagger = &dev->tagger; ++ ++ /* Common functions, gotta have */ ++ if (!drv->drv_read_chunk_fn || ++ !drv->drv_write_chunk_fn || ++ !drv->drv_erase_fn) ++ return 0; ++ ++ if (dev->param.is_yaffs2 && ++ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) ++ return 0; ++ ++ /* Install the default tags marshalling functions if needed. */ ++ yaffs_tags_compat_install(dev); ++ yaffs_tags_marshall_install(dev); ++ ++ /* Check we now have the marshalling functions required. */ ++ if (!tagger->write_chunk_tags_fn || ++ !tagger->read_chunk_tags_fn || ++ !tagger->query_block_fn || ++ !tagger->mark_bad_fn) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs_create_initial_dir(struct yaffs_dev *dev) ++{ ++ /* Initialise the unlinked, deleted, root and lost+found directories */ ++ dev->lost_n_found = dev->root_dir = NULL; ++ dev->unlinked_dir = dev->del_dir = NULL; ++ dev->unlinked_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); ++ dev->del_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); ++ dev->root_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, ++ YAFFS_ROOT_MODE | S_IFDIR); ++ dev->lost_n_found = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, ++ YAFFS_LOSTNFOUND_MODE | S_IFDIR); ++ ++ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir ++ && dev->del_dir) { ++ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++/* Low level init. ++ * Typically only used by yaffs_guts_initialise, but also used by the ++ * Low level yaffs driver tests. ++ */ ++ ++int yaffs_guts_ll_init(struct yaffs_dev *dev) ++{ ++ ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); ++ ++ if (!dev) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Need a device" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ if (dev->ll_init) ++ return YAFFS_OK; ++ ++ dev->internal_start_block = dev->param.start_block; ++ dev->internal_end_block = dev->param.end_block; ++ dev->block_offset = 0; ++ dev->chunk_offset = 0; ++ dev->n_free_chunks = 0; ++ ++ dev->gc_block = 0; ++ ++ if (dev->param.start_block == 0) { ++ dev->internal_start_block = dev->param.start_block + 1; ++ dev->internal_end_block = dev->param.end_block + 1; ++ dev->block_offset = 1; ++ dev->chunk_offset = dev->param.chunks_per_block; ++ } ++ ++ /* Check geometry parameters. */ ++ ++ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 1024) || ++ (!dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 512) || ++ (dev->param.inband_tags && !dev->param.is_yaffs2) || ++ dev->param.chunks_per_block < 2 || ++ dev->param.n_reserved_blocks < 2 || ++ dev->internal_start_block <= 0 || ++ dev->internal_end_block <= 0 || ++ dev->internal_end_block <= ++ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) ++ ) { ++ /* otherwise it is too small */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", ++ dev->param.total_bytes_per_chunk, ++ dev->param.is_yaffs2 ? "2" : "", ++ dev->param.inband_tags); ++ return YAFFS_FAIL; ++ } ++ ++ /* Sort out space for inband tags, if required */ ++ if (dev->param.inband_tags) ++ dev->data_bytes_per_chunk = ++ dev->param.total_bytes_per_chunk - ++ sizeof(struct yaffs_packed_tags2_tags_only); ++ else ++ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; ++ ++ /* Got the right mix of functions? */ ++ if (!yaffs_check_dev_fns(dev)) { ++ /* Function missing */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "device function(s) missing or wrong"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ if (yaffs_init_nand(dev) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev) ++{ ++ int i; ++ enum yaffs_block_state state; ++ u32 dummy; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if(dev->is_mounted) ++ return YAFFS_FAIL; ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ yaffs_query_init_block_state(dev, i, &state, &dummy); ++ if (state != YAFFS_BLOCK_STATE_DEAD) ++ yaffs_erase_block(dev, i); ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev) ++{ ++ int init_failed = 0; ++ unsigned x; ++ int bits; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if (dev->is_mounted) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); ++ return YAFFS_FAIL; ++ } ++ ++ dev->is_mounted = 1; ++ ++ /* OK now calculate a few things for the device */ ++ ++ /* ++ * Calculate all the chunk size manipulation numbers: ++ */ ++ x = dev->data_bytes_per_chunk; ++ /* We always use dev->chunk_shift and dev->chunk_div */ ++ dev->chunk_shift = calc_shifts(x); ++ x >>= dev->chunk_shift; ++ dev->chunk_div = x; ++ /* We only use chunk mask if chunk_div is 1 */ ++ dev->chunk_mask = (1 << dev->chunk_shift) - 1; ++ ++ /* ++ * Calculate chunk_grp_bits. ++ * We need to find the next power of 2 > than internal_end_block ++ */ ++ ++ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); ++ ++ bits = calc_shifts_ceiling(x); ++ ++ /* Set up tnode width if wide tnodes are enabled. */ ++ if (!dev->param.wide_tnodes_disabled) { ++ /* bits must be even so that we end up with 32-bit words */ ++ if (bits & 1) ++ bits++; ++ if (bits < 16) ++ dev->tnode_width = 16; ++ else ++ dev->tnode_width = bits; ++ } else { ++ dev->tnode_width = 16; ++ } ++ ++ dev->tnode_mask = (1 << dev->tnode_width) - 1; ++ ++ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), ++ * so if the bitwidth of the ++ * chunk range we're using is greater than 16 we need ++ * to figure out chunk shift and chunk_grp_size ++ */ ++ ++ if (bits <= dev->tnode_width) ++ dev->chunk_grp_bits = 0; ++ else ++ dev->chunk_grp_bits = bits - dev->tnode_width; ++ ++ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; ++ if (dev->tnode_size < sizeof(struct yaffs_tnode)) ++ dev->tnode_size = sizeof(struct yaffs_tnode); ++ ++ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; ++ ++ if (dev->param.chunks_per_block < dev->chunk_grp_size) { ++ /* We have a problem because the soft delete won't work if ++ * the chunk group size > chunks per block. ++ * This can be remedied by using larger "virtual blocks". ++ */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ /* Finished verifying the device, continue with initialisation */ ++ ++ /* More device initialisation */ ++ dev->all_gcs = 0; ++ dev->passive_gc_count = 0; ++ dev->oldest_dirty_gc_count = 0; ++ dev->bg_gcs = 0; ++ dev->gc_block_finder = 0; ++ dev->buffered_block = -1; ++ dev->doing_buffered_block_rewrite = 0; ++ dev->n_deleted_files = 0; ++ dev->n_bg_deletions = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_ecc_fixed = 0; ++ dev->n_ecc_unfixed = 0; ++ dev->n_tags_ecc_fixed = 0; ++ dev->n_tags_ecc_unfixed = 0; ++ dev->n_erase_failures = 0; ++ dev->n_erased_blocks = 0; ++ dev->gc_disable = 0; ++ dev->has_pending_prioritised_gc = 1; ++ /* Assume the worst for now, will get fixed on first GC */ ++ INIT_LIST_HEAD(&dev->dirty_dirs); ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ ++ /* Initialise temporary buffers and caches. */ ++ if (!yaffs_init_tmp_buffers(dev)) ++ init_failed = 1; ++ ++ dev->cache = NULL; ++ dev->gc_cleanup_list = NULL; ++ ++ if (!init_failed && dev->param.n_caches > 0) { ++ int i; ++ void *buf; ++ int cache_bytes = ++ dev->param.n_caches * sizeof(struct yaffs_cache); ++ ++ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) ++ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; ++ ++ dev->cache = kmalloc(cache_bytes, GFP_NOFS); ++ ++ buf = (u8 *) dev->cache; ++ ++ if (dev->cache) ++ memset(dev->cache, 0, cache_bytes); ++ ++ for (i = 0; i < dev->param.n_caches && buf; i++) { ++ dev->cache[i].object = NULL; ++ dev->cache[i].last_use = 0; ++ dev->cache[i].dirty = 0; ++ dev->cache[i].data = buf = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ } ++ if (!buf) ++ init_failed = 1; ++ ++ dev->cache_last_use = 0; ++ } ++ ++ dev->cache_hits = 0; ++ ++ if (!init_failed) { ++ dev->gc_cleanup_list = ++ kmalloc(dev->param.chunks_per_block * sizeof(u32), ++ GFP_NOFS); ++ if (!dev->gc_cleanup_list) ++ init_failed = 1; ++ } ++ ++ if (dev->param.is_yaffs2) ++ dev->param.use_header_file_size = 1; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && dev->param.is_yaffs2 && ++ !dev->param.disable_summary && ++ !yaffs_summary_init(dev)) ++ init_failed = 1; ++ ++ if (!init_failed) { ++ /* Now scan the flash. */ ++ if (dev->param.is_yaffs2) { ++ if (yaffs2_checkpt_restore(dev)) { ++ yaffs_check_obj_details_loaded(dev->root_dir); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | ++ YAFFS_TRACE_MOUNT, ++ "yaffs: restored from checkpoint" ++ ); ++ } else { ++ ++ /* Clean up the mess caused by an aborted ++ * checkpoint load then scan backwards. ++ */ ++ yaffs_deinit_blocks(dev); ++ ++ yaffs_deinit_tnodes_and_objs(dev); ++ ++ dev->n_erased_blocks = 0; ++ dev->n_free_chunks = 0; ++ dev->alloc_block = -1; ++ dev->alloc_page = -1; ++ dev->n_deleted_files = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_bg_deletions = 0; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed ++ && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && !yaffs2_scan_backwards(dev)) ++ init_failed = 1; ++ } ++ } else if (!yaffs1_scan(dev)) { ++ init_failed = 1; ++ } ++ ++ yaffs_strip_deleted_objs(dev); ++ yaffs_fix_hanging_objs(dev); ++ if (dev->param.empty_lost_n_found) ++ yaffs_empty_l_n_f(dev); ++ } ++ ++ if (init_failed) { ++ /* Clean up the mess */ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() aborted."); ++ ++ yaffs_deinitialise(dev); ++ return YAFFS_FAIL; ++ } ++ ++ /* Zero out stats */ ++ dev->n_page_reads = 0; ++ dev->n_page_writes = 0; ++ dev->n_erasures = 0; ++ dev->n_gc_copies = 0; ++ dev->n_retried_writes = 0; ++ ++ dev->n_retired_blocks = 0; ++ ++ yaffs_verify_free_chunks(dev); ++ yaffs_verify_blocks(dev); ++ ++ /* Clean up any aborted checkpoint data */ ++ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() done."); ++ return YAFFS_OK; ++} ++ ++void yaffs_deinitialise(struct yaffs_dev *dev) ++{ ++ if (dev->is_mounted) { ++ int i; ++ ++ yaffs_deinit_blocks(dev); ++ yaffs_deinit_tnodes_and_objs(dev); ++ yaffs_summary_deinit(dev); ++ ++ if (dev->param.n_caches > 0 && dev->cache) { ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ kfree(dev->cache[i].data); ++ dev->cache[i].data = NULL; ++ } ++ ++ kfree(dev->cache); ++ dev->cache = NULL; ++ } ++ ++ kfree(dev->gc_cleanup_list); ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) ++ kfree(dev->temp_buffer[i].buffer); ++ ++ dev->is_mounted = 0; ++ ++ yaffs_deinit_nand(dev); ++ } ++} ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev) ++{ ++ int n_free = 0; ++ int b; ++ struct yaffs_block_info *blk; ++ ++ blk = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ switch (blk->block_state) { ++ case YAFFS_BLOCK_STATE_EMPTY: ++ case YAFFS_BLOCK_STATE_ALLOCATING: ++ case YAFFS_BLOCK_STATE_COLLECTING: ++ case YAFFS_BLOCK_STATE_FULL: ++ n_free += ++ (dev->param.chunks_per_block - blk->pages_in_use + ++ blk->soft_del_pages); ++ break; ++ default: ++ break; ++ } ++ blk++; ++ } ++ return n_free; ++} ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev) ++{ ++ /* This is what we report to the outside world */ ++ int n_free; ++ int n_dirty_caches; ++ int blocks_for_checkpt; ++ int i; ++ ++ n_free = dev->n_free_chunks; ++ n_free += dev->n_deleted_files; ++ ++ /* Now count and subtract the number of dirty chunks in the cache. */ ++ ++ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].dirty) ++ n_dirty_caches++; ++ } ++ ++ n_free -= n_dirty_caches; ++ ++ n_free -= ++ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); ++ ++ /* Now figure checkpoint space and report that... */ ++ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); ++ ++ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); ++ ++ if (n_free < 0) ++ n_free = 0; ++ ++ return n_free; ++} ++ ++ ++ ++/* ++ * Marshalling functions to get loff_t file sizes into and out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) ++{ ++ oh->file_size_low = (fsize & 0xFFFFFFFF); ++ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); ++} ++ ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) ++{ ++ loff_t retval; ++ ++ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) ++ retval = (((loff_t) oh->file_size_high) << 32) | ++ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); ++ else ++ retval = (loff_t) oh->file_size_low; ++ ++ return retval; ++} ++ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ int s; ++ ++ for(i = 0; i < 10; i++) ++ bs[i] = 0; ++ ++ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ bi = yaffs_get_block_info(dev, i); ++ s = bi->block_state; ++ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) ++ bs[0]++; ++ else ++ bs[s]++; ++ } ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_guts.h linux-3.12.20/fs/yaffs2/yaffs_guts.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_guts.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,1007 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GUTS_H__ ++#define __YAFFS_GUTS_H__ ++ ++#include "yportenv.h" ++ ++#define YAFFS_OK 1 ++#define YAFFS_FAIL 0 ++ ++/* Give us a Y=0x59, ++ * Give us an A=0x41, ++ * Give us an FF=0xff ++ * Give us an S=0x53 ++ * And what have we got... ++ */ ++#define YAFFS_MAGIC 0x5941ff53 ++ ++/* ++ * Tnodes form a tree with the tnodes in "levels" ++ * Levels greater than 0 hold 8 slots which point to other tnodes. ++ * Those at level 0 hold 16 slots which point to chunks in NAND. ++ * ++ * A maximum level of 8 thust supports files of size up to: ++ * ++ * 2^(3*MAX_LEVEL+4) ++ * ++ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives ++ * a maximum file size of around 512Gbytees with 2k chunks. ++ */ ++#define YAFFS_NTNODES_LEVEL0 16 ++#define YAFFS_TNODES_LEVEL0_BITS 4 ++#define YAFFS_TNODES_LEVEL0_MASK 0xf ++ ++#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) ++#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) ++#define YAFFS_TNODES_INTERNAL_MASK 0x7 ++#define YAFFS_TNODES_MAX_LEVEL 8 ++#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ ++ YAFFS_TNODES_INTERNAL_BITS * \ ++ YAFFS_TNODES_MAX_LEVEL) ++#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) ++ ++#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff ++ ++/* Constants for YAFFS1 mode */ ++#define YAFFS_BYTES_PER_SPARE 16 ++#define YAFFS_BYTES_PER_CHUNK 512 ++#define YAFFS_CHUNK_SIZE_SHIFT 9 ++#define YAFFS_CHUNKS_PER_BLOCK 32 ++#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) ++ ++#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 ++#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 ++ ++ ++ ++#define YAFFS_ALLOCATION_NOBJECTS 100 ++#define YAFFS_ALLOCATION_NTNODES 100 ++#define YAFFS_ALLOCATION_NLINKS 100 ++ ++#define YAFFS_NOBJECT_BUCKETS 256 ++ ++#define YAFFS_OBJECT_SPACE 0x40000 ++#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) ++ ++/* Binary data version stamps */ ++#define YAFFS_SUMMARY_VERSION 1 ++#define YAFFS_CHECKPOINT_VERSION 7 ++ ++#ifdef CONFIG_YAFFS_UNICODE ++#define YAFFS_MAX_NAME_LENGTH 127 ++#define YAFFS_MAX_ALIAS_LENGTH 79 ++#else ++#define YAFFS_MAX_NAME_LENGTH 255 ++#define YAFFS_MAX_ALIAS_LENGTH 159 ++#endif ++ ++#define YAFFS_SHORT_NAME_LENGTH 15 ++ ++/* Some special object ids for pseudo objects */ ++#define YAFFS_OBJECTID_ROOT 1 ++#define YAFFS_OBJECTID_LOSTNFOUND 2 ++#define YAFFS_OBJECTID_UNLINKED 3 ++#define YAFFS_OBJECTID_DELETED 4 ++ ++/* Fake object Id for summary data */ ++#define YAFFS_OBJECTID_SUMMARY 0x10 ++ ++/* Pseudo object ids for checkpointing */ ++#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 ++#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 ++ ++#define YAFFS_MAX_SHORT_OP_CACHES 20 ++ ++#define YAFFS_N_TEMP_BUFFERS 6 ++ ++/* We limit the number attempts at sucessfully saving a chunk of data. ++ * Small-page devices have 32 pages per block; large-page devices have 64. ++ * Default to something in the order of 5 to 10 blocks worth of chunks. ++ */ ++#define YAFFS_WR_ATTEMPTS (5*64) ++ ++/* Sequence numbers are used in YAFFS2 to determine block allocation order. ++ * The range is limited slightly to help distinguish bad numbers from good. ++ * This also allows us to perhaps in the future use special numbers for ++ * special purposes. ++ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, ++ * and is a larger number than the lifetime of a 2GB device. ++ */ ++#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 ++#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 ++ ++/* Special sequence number for bad block that failed to be marked bad */ ++#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 ++ ++/* ChunkCache is used for short read/write operations.*/ ++struct yaffs_cache { ++ struct yaffs_obj *object; ++ int chunk_id; ++ int last_use; ++ int dirty; ++ int n_bytes; /* Only valid if the cache is dirty */ ++ int locked; /* Can't push out or flush while locked. */ ++ u8 *data; ++}; ++ ++/* yaffs1 tags structures in RAM ++ * NB This uses bitfield. Bitfields should not straddle a u32 boundary ++ * otherwise the structure size will get blown out. ++ */ ++ ++struct yaffs_tags { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes_lsb:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 n_bytes_msb:2; ++}; ++ ++union yaffs_tags_union { ++ struct yaffs_tags as_tags; ++ u8 as_bytes[8]; ++}; ++ ++ ++/* Stuff used for extended tags in YAFFS2 */ ++ ++enum yaffs_ecc_result { ++ YAFFS_ECC_RESULT_UNKNOWN, ++ YAFFS_ECC_RESULT_NO_ERROR, ++ YAFFS_ECC_RESULT_FIXED, ++ YAFFS_ECC_RESULT_UNFIXED ++}; ++ ++enum yaffs_obj_type { ++ YAFFS_OBJECT_TYPE_UNKNOWN, ++ YAFFS_OBJECT_TYPE_FILE, ++ YAFFS_OBJECT_TYPE_SYMLINK, ++ YAFFS_OBJECT_TYPE_DIRECTORY, ++ YAFFS_OBJECT_TYPE_HARDLINK, ++ YAFFS_OBJECT_TYPE_SPECIAL ++}; ++ ++#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL ++ ++struct yaffs_ext_tags { ++ unsigned chunk_used; /* Status of the chunk: used or unused */ ++ unsigned obj_id; /* If 0 this is not used */ ++ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ ++ unsigned n_bytes; /* Only valid for data chunks */ ++ ++ /* The following stuff only has meaning when we read */ ++ enum yaffs_ecc_result ecc_result; ++ unsigned block_bad; ++ ++ /* YAFFS 1 stuff */ ++ unsigned is_deleted; /* The chunk is marked deleted */ ++ unsigned serial_number; /* Yaffs1 2-bit serial number */ ++ ++ /* YAFFS2 stuff */ ++ unsigned seq_number; /* The sequence number of this block */ ++ ++ /* Extra info if this is an object header (YAFFS2 only) */ ++ ++ unsigned extra_available; /* Extra info available if not zero */ ++ unsigned extra_parent_id; /* The parent object */ ++ unsigned extra_is_shrink; /* Is it a shrink header? */ ++ unsigned extra_shadows; /* Does this shadow another object? */ ++ ++ enum yaffs_obj_type extra_obj_type; /* What object type? */ ++ ++ loff_t extra_file_size; /* Length if it is a file */ ++ unsigned extra_equiv_id; /* Equivalent object for a hard link */ ++}; ++ ++/* Spare structure for YAFFS1 */ ++struct yaffs_spare { ++ u8 tb0; ++ u8 tb1; ++ u8 tb2; ++ u8 tb3; ++ u8 page_status; /* set to 0 to delete the chunk */ ++ u8 block_status; ++ u8 tb4; ++ u8 tb5; ++ u8 ecc1[3]; ++ u8 tb6; ++ u8 tb7; ++ u8 ecc2[3]; ++}; ++ ++/*Special structure for passing through to mtd */ ++struct yaffs_nand_spare { ++ struct yaffs_spare spare; ++ int eccres1; ++ int eccres2; ++}; ++ ++/* Block data in RAM */ ++ ++enum yaffs_block_state { ++ YAFFS_BLOCK_STATE_UNKNOWN = 0, ++ ++ YAFFS_BLOCK_STATE_SCANNING, ++ /* Being scanned */ ++ ++ YAFFS_BLOCK_STATE_NEEDS_SCAN, ++ /* The block might have something on it (ie it is allocating or full, ++ * perhaps empty) but it needs to be scanned to determine its true ++ * state. ++ * This state is only valid during scanning. ++ * NB We tolerate empty because the pre-scanner might be incapable of ++ * deciding ++ * However, if this state is returned on a YAFFS2 device, ++ * then we expect a sequence number ++ */ ++ ++ YAFFS_BLOCK_STATE_EMPTY, ++ /* This block is empty */ ++ ++ YAFFS_BLOCK_STATE_ALLOCATING, ++ /* This block is partially allocated. ++ * At least one page holds valid data. ++ * This is the one currently being used for page ++ * allocation. Should never be more than one of these. ++ * If a block is only partially allocated at mount it is treated as ++ * full. ++ */ ++ ++ YAFFS_BLOCK_STATE_FULL, ++ /* All the pages in this block have been allocated. ++ * If a block was only partially allocated when mounted we treat ++ * it as fully allocated. ++ */ ++ ++ YAFFS_BLOCK_STATE_DIRTY, ++ /* The block was full and now all chunks have been deleted. ++ * Erase me, reuse me. ++ */ ++ ++ YAFFS_BLOCK_STATE_CHECKPOINT, ++ /* This block is assigned to holding checkpoint data. */ ++ ++ YAFFS_BLOCK_STATE_COLLECTING, ++ /* This block is being garbage collected */ ++ ++ YAFFS_BLOCK_STATE_DEAD ++ /* This block has failed and is not in use */ ++}; ++ ++#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) ++ ++struct yaffs_block_info { ++ ++ s32 soft_del_pages:10; /* number of soft deleted pages */ ++ s32 pages_in_use:10; /* number of pages in use */ ++ u32 block_state:4; /* One of the above block states. */ ++ /* NB use unsigned because enum is sometimes ++ * an int */ ++ u32 needs_retiring:1; /* Data has failed on this block, */ ++ /*need to get valid data off and retire*/ ++ u32 skip_erased_check:1;/* Skip the erased check on this block */ ++ u32 gc_prioritise:1; /* An ECC check or blank check has failed. ++ Block should be prioritised for GC */ ++ u32 chunk_error_strikes:3; /* How many times we've had ecc etc ++ failures on this block and tried to reuse it */ ++ u32 has_summary:1; /* The block has a summary */ ++ ++ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ ++ u32 seq_number; /* block sequence number for yaffs2 */ ++ ++}; ++ ++/* -------------------------- Object structure -------------------------------*/ ++/* This is the object structure as stored on NAND */ ++ ++struct yaffs_obj_hdr { ++ enum yaffs_obj_type type; ++ ++ /* Apply to everything */ ++ int parent_obj_id; ++ u16 sum_no_longer_used; /* checksum of name. No longer used */ ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ /* The following apply to all object types except for hard links */ ++ u32 yst_mode; /* protection */ ++ ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++ ++ /* File size applies to files only */ ++ u32 file_size_low; ++ ++ /* Equivalent object id applies to hard links only. */ ++ int equiv_id; ++ ++ /* Alias is for symlinks only. */ ++ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; ++ ++ u32 yst_rdev; /* stuff for block and char devices (major/min) */ ++ ++ u32 win_ctime[2]; ++ u32 win_atime[2]; ++ u32 win_mtime[2]; ++ ++ u32 inband_shadowed_obj_id; ++ u32 inband_is_shrink; ++ ++ u32 file_size_high; ++ u32 reserved[1]; ++ int shadows_obj; /* This object header shadows the ++ specified object if > 0 */ ++ ++ /* is_shrink applies to object headers written when wemake a hole. */ ++ u32 is_shrink; ++ ++}; ++ ++/*--------------------------- Tnode -------------------------- */ ++ ++struct yaffs_tnode { ++ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; ++}; ++ ++/*------------------------ Object -----------------------------*/ ++/* An object can be one of: ++ * - a directory (no data, has children links ++ * - a regular file (data.... not prunes :->). ++ * - a symlink [symbolic link] (the alias). ++ * - a hard link ++ */ ++ ++struct yaffs_file_var { ++ loff_t file_size; ++ loff_t scanned_size; ++ loff_t shrink_size; ++ int top_level; ++ struct yaffs_tnode *top; ++}; ++ ++struct yaffs_dir_var { ++ struct list_head children; /* list of child links */ ++ struct list_head dirty; /* Entry for list of dirty directories */ ++}; ++ ++struct yaffs_symlink_var { ++ YCHAR *alias; ++}; ++ ++struct yaffs_hardlink_var { ++ struct yaffs_obj *equiv_obj; ++ u32 equiv_id; ++}; ++ ++union yaffs_obj_var { ++ struct yaffs_file_var file_variant; ++ struct yaffs_dir_var dir_variant; ++ struct yaffs_symlink_var symlink_variant; ++ struct yaffs_hardlink_var hardlink_variant; ++}; ++ ++struct yaffs_obj { ++ u8 deleted:1; /* This should only apply to unlinked files. */ ++ u8 soft_del:1; /* it has also been soft deleted */ ++ u8 unlinked:1; /* An unlinked file.*/ ++ u8 fake:1; /* A fake object has no presence on NAND. */ ++ u8 rename_allowed:1; /* Some objects cannot be renamed. */ ++ u8 unlink_allowed:1; ++ u8 dirty:1; /* the object needs to be written to flash */ ++ u8 valid:1; /* When the file system is being loaded up, this ++ * object might be created before the data ++ * is available ++ * ie. file data chunks encountered before ++ * the header. ++ */ ++ u8 lazy_loaded:1; /* This object has been lazy loaded and ++ * is missing some detail */ ++ ++ u8 defered_free:1; /* Object is removed from NAND, but is ++ * still in the inode cache. ++ * Free of object is defered. ++ * until the inode is released. ++ */ ++ u8 being_created:1; /* This object is still being created ++ * so skip some verification checks. */ ++ u8 is_shadowed:1; /* This object is shadowed on the way ++ * to being renamed. */ ++ ++ u8 xattr_known:1; /* We know if this has object has xattribs ++ * or not. */ ++ u8 has_xattr:1; /* This object has xattribs. ++ * Only valid if xattr_known. */ ++ ++ u8 serial; /* serial number of chunk in NAND.*/ ++ u16 sum; /* sum of the name to speed searching */ ++ ++ struct yaffs_dev *my_dev; /* The device I'm on */ ++ ++ struct list_head hash_link; /* list of objects in hash bucket */ ++ ++ struct list_head hard_links; /* hard linked object chain*/ ++ ++ /* directory structure stuff */ ++ /* also used for linking up the free list */ ++ struct yaffs_obj *parent; ++ struct list_head siblings; ++ ++ /* Where's my object header in NAND? */ ++ int hdr_chunk; ++ ++ int n_data_chunks; /* Number of data chunks for this file. */ ++ ++ u32 obj_id; /* the object id value */ ++ ++ u32 yst_mode; ++ ++ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; ++ ++#ifdef CONFIG_YAFFS_WINCE ++ u32 win_ctime[2]; ++ u32 win_mtime[2]; ++ u32 win_atime[2]; ++#else ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++#endif ++ ++ u32 yst_rdev; ++ ++ void *my_inode; ++ ++ enum yaffs_obj_type variant_type; ++ ++ union yaffs_obj_var variant; ++ ++}; ++ ++struct yaffs_obj_bucket { ++ struct list_head list; ++ int count; ++}; ++ ++/* yaffs_checkpt_obj holds the definition of an object as dumped ++ * by checkpointing. ++ */ ++ ++struct yaffs_checkpt_obj { ++ int struct_type; ++ u32 obj_id; ++ u32 parent_id; ++ int hdr_chunk; ++ enum yaffs_obj_type variant_type:3; ++ u8 deleted:1; ++ u8 soft_del:1; ++ u8 unlinked:1; ++ u8 fake:1; ++ u8 rename_allowed:1; ++ u8 unlink_allowed:1; ++ u8 serial; ++ int n_data_chunks; ++ loff_t size_or_equiv_obj; ++}; ++ ++/*--------------------- Temporary buffers ---------------- ++ * ++ * These are chunk-sized working buffers. Each device has a few. ++ */ ++ ++struct yaffs_buffer { ++ u8 *buffer; ++ int in_use; ++}; ++ ++/*----------------- Device ---------------------------------*/ ++ ++struct yaffs_param { ++ const YCHAR *name; ++ ++ /* ++ * Entry parameters set up way early. Yaffs sets up the rest. ++ * The structure should be zeroed out before use so that unused ++ * and default values are zero. ++ */ ++ ++ int inband_tags; /* Use unband tags */ ++ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to ++ be a power of 2 */ ++ int chunks_per_block; /* does not need to be a power of 2 */ ++ int spare_bytes_per_chunk; /* spare area size */ ++ int start_block; /* Start block we're allowed to use */ ++ int end_block; /* End block we're allowed to use */ ++ int n_reserved_blocks; /* Tuneable so that we can reduce ++ * reserved blocks on NOR and RAM. */ ++ ++ int n_caches; /* If <= 0, then short op caching is disabled, ++ * else the number of short op caches. ++ */ ++ int cache_bypass_aligned; /* If non-zero then bypass the cache for ++ * aligned writes. ++ */ ++ ++ int use_nand_ecc; /* Flag to decide whether or not to use ++ * NAND driver ECC on data (yaffs1) */ ++ int tags_9bytes; /* Use 9 byte tags */ ++ int no_tags_ecc; /* Flag to decide whether or not to do ECC ++ * on packed tags (yaffs2) */ ++ ++ int is_yaffs2; /* Use yaffs2 mode on this device */ ++ ++ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ ++ ++ int refresh_period; /* How often to check for a block refresh */ ++ ++ /* Checkpoint control. Can be set before or after initialisation */ ++ u8 skip_checkpt_rd; ++ u8 skip_checkpt_wr; ++ ++ int enable_xattr; /* Enable xattribs */ ++ ++ int max_objects; /* ++ * Set to limit the number of objects created. ++ * 0 = no limit. ++ */ ++ ++ /* The remove_obj_fn function must be supplied by OS flavours that ++ * need it. ++ * yaffs direct uses it to implement the faster readdir. ++ * Linux uses it to protect the directory during unlocking. ++ */ ++ void (*remove_obj_fn) (struct yaffs_obj *obj); ++ ++ /* Callback to mark the superblock dirty */ ++ void (*sb_dirty_fn) (struct yaffs_dev *dev); ++ ++ /* Callback to control garbage collection. */ ++ unsigned (*gc_control_fn) (struct yaffs_dev *dev); ++ ++ /* Debug control flags. Don't use unless you know what you're doing */ ++ int use_header_file_size; /* Flag to determine if we should use ++ * file sizes from the header */ ++ int disable_lazy_load; /* Disable lazy loading on this device */ ++ int wide_tnodes_disabled; /* Set to disable wide tnodes */ ++ int disable_soft_del; /* yaffs 1 only: Set to disable the use of ++ * softdeletion. */ ++ ++ int defered_dir_update; /* Set to defer directory updates */ ++ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ int auto_unicode; ++#endif ++ int always_check_erased; /* Force chunk erased check always on */ ++ ++ int disable_summary; ++ int disable_bad_block_marking; ++ ++}; ++ ++struct yaffs_driver { ++ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len); ++ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result); ++ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_initialise_fn) (struct yaffs_dev *dev); ++ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); ++}; ++ ++struct yaffs_tags_handler { ++ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags); ++ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags); ++ ++ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++}; ++ ++struct yaffs_dev { ++ struct yaffs_param param; ++ struct yaffs_driver drv; ++ struct yaffs_tags_handler tagger; ++ ++ /* Context storage. Holds extra OS specific data for this device */ ++ ++ void *os_context; ++ void *driver_context; ++ ++ struct list_head dev_list; ++ ++ int ll_init; ++ /* Runtime parameters. Set up by YAFFS. */ ++ int data_bytes_per_chunk; ++ ++ /* Non-wide tnode stuff */ ++ u16 chunk_grp_bits; /* Number of bits that need to be resolved if ++ * the tnodes are not wide enough. ++ */ ++ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ ++ ++ /* Stuff to support wide tnodes */ ++ u32 tnode_width; ++ u32 tnode_mask; ++ u32 tnode_size; ++ ++ /* Stuff for figuring out file offset to chunk conversions */ ++ u32 chunk_shift; /* Shift value */ ++ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ ++ u32 chunk_mask; /* Mask to use for power-of-2 case */ ++ ++ int is_mounted; ++ int read_only; ++ int is_checkpointed; ++ ++ /* Stuff to support block offsetting to support start block zero */ ++ int internal_start_block; ++ int internal_end_block; ++ int block_offset; ++ int chunk_offset; ++ ++ /* Runtime checkpointing stuff */ ++ int checkpt_page_seq; /* running sequence number of checkpt pages */ ++ int checkpt_byte_count; ++ int checkpt_byte_offs; ++ u8 *checkpt_buffer; ++ int checkpt_open_write; ++ int blocks_in_checkpt; ++ int checkpt_cur_chunk; ++ int checkpt_cur_block; ++ int checkpt_next_block; ++ int *checkpt_block_list; ++ int checkpt_max_blocks; ++ u32 checkpt_sum; ++ u32 checkpt_xor; ++ ++ int checkpoint_blocks_required; /* Number of blocks needed to store ++ * current checkpoint set */ ++ ++ /* Block Info */ ++ struct yaffs_block_info *block_info; ++ u8 *chunk_bits; /* bitmap of chunks in use */ ++ u8 block_info_alt:1; /* allocated using alternative alloc */ ++ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ ++ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. ++ * Must be consistent with chunks_per_block. ++ */ ++ ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int alloc_block_finder; /* Used to search for next allocation block */ ++ ++ /* Object and Tnode memory management */ ++ void *allocator; ++ int n_obj; ++ int n_tnodes; ++ ++ int n_hardlinks; ++ ++ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; ++ u32 bucket_finder; ++ ++ int n_free_chunks; ++ ++ /* Garbage collection control */ ++ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ ++ u32 n_clean_ups; ++ ++ unsigned has_pending_prioritised_gc; /* We think this device might ++ have pending prioritised gcs */ ++ unsigned gc_disable; ++ unsigned gc_block_finder; ++ unsigned gc_dirtiest; ++ unsigned gc_pages_in_use; ++ unsigned gc_not_done; ++ unsigned gc_block; ++ unsigned gc_chunk; ++ unsigned gc_skip; ++ struct yaffs_summary_tags *gc_sum_tags; ++ ++ /* Special directories */ ++ struct yaffs_obj *root_dir; ++ struct yaffs_obj *lost_n_found; ++ ++ int buffered_block; /* Which block is buffered here? */ ++ int doing_buffered_block_rewrite; ++ ++ struct yaffs_cache *cache; ++ int cache_last_use; ++ ++ /* Stuff for background deletion and unlinked files. */ ++ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted ++ files live. */ ++ struct yaffs_obj *del_dir; /* Directory where deleted objects are ++ sent to disappear. */ ++ struct yaffs_obj *unlinked_deletion; /* Current file being ++ background deleted. */ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* Temporary buffer management */ ++ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; ++ int max_temp; ++ int temp_in_use; ++ int unmanaged_buffer_allocs; ++ int unmanaged_buffer_deallocs; ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ allocating block */ ++ unsigned oldest_dirty_seq; ++ unsigned oldest_dirty_block; ++ ++ /* Block refreshing */ ++ int refresh_skip; /* A skip down counter. ++ * Refresh happens when this gets to zero. */ ++ ++ /* Dirty directory handling */ ++ struct list_head dirty_dirs; /* List of dirty directories */ ++ ++ /* Summary */ ++ int chunks_per_summary; ++ struct yaffs_summary_tags *sum_tags; ++ ++ /* Statistics */ ++ u32 n_page_writes; ++ u32 n_page_reads; ++ u32 n_erasures; ++ u32 n_bad_queries; ++ u32 n_bad_markings; ++ u32 n_erase_failures; ++ u32 n_gc_copies; ++ u32 all_gcs; ++ u32 passive_gc_count; ++ u32 oldest_dirty_gc_count; ++ u32 n_gc_blocks; ++ u32 bg_gcs; ++ u32 n_retried_writes; ++ u32 n_retired_blocks; ++ u32 n_ecc_fixed; ++ u32 n_ecc_unfixed; ++ u32 n_tags_ecc_fixed; ++ u32 n_tags_ecc_unfixed; ++ u32 n_deletions; ++ u32 n_unmarked_deletions; ++ u32 refresh_count; ++ u32 cache_hits; ++ u32 tags_used; ++ u32 summary_used; ++ ++}; ++ ++/* The CheckpointDevice structure holds the device information that changes ++ *at runtime and must be preserved over unmount/mount cycles. ++ */ ++struct yaffs_checkpt_dev { ++ int struct_type; ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int n_free_chunks; ++ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ * allocating block */ ++ ++}; ++ ++struct yaffs_checkpt_validity { ++ int struct_type; ++ u32 magic; ++ u32 version; ++ u32 head; ++}; ++ ++struct yaffs_shadow_fixer { ++ int obj_id; ++ int shadowed_id; ++ struct yaffs_shadow_fixer *next; ++}; ++ ++/* Structure for doing xattr modifications */ ++struct yaffs_xattr_mod { ++ int set; /* If 0 then this is a deletion */ ++ const YCHAR *name; ++ const void *data; ++ int size; ++ int flags; ++ int result; ++}; ++ ++/*----------------------- YAFFS Functions -----------------------*/ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev); ++void yaffs_deinitialise(struct yaffs_dev *dev); ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, ++ struct yaffs_obj *new_dir, const YCHAR * new_name); ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); ++int yaffs_del_obj(struct yaffs_obj *obj); ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type); ++ ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj); ++int yaffs_get_obj_inode(struct yaffs_obj *obj); ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj); ++int yaffs_get_obj_link_count(struct yaffs_obj *obj); ++ ++/* File operations */ ++int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, ++ int n_bytes); ++int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid); ++ ++int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync); ++ ++/* Flushing and checkpointing */ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev); ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev); ++int yaffs_checkpoint_restore(struct yaffs_dev *dev); ++ ++/* Directory operations */ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid); ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, ++ const YCHAR *name); ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); ++ ++/* Link operations */ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, ++ struct yaffs_obj *equiv_obj); ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); ++ ++/* Symlink operations */ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias); ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); ++ ++/* Special inodes (fifos, sockets and devices) */ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev); ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, ++ const void *value, int size, int flags); ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, ++ int size); ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); ++ ++/* Special directories */ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj); ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev); ++ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); ++ ++/* Debug dump */ ++int yaffs_dump_obj(struct yaffs_obj *obj); ++ ++void yaffs_guts_test(struct yaffs_dev *dev); ++int yaffs_guts_ll_init(struct yaffs_dev *dev); ++ ++ ++/* A few useful functions to be used within the core files*/ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn); ++int yaffs_check_ff(u8 *buffer, int n_bytes); ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type); ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan); ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh); ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); ++YCHAR *yaffs_clone_str(const YCHAR *str); ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, ++ int force, int is_shrink, int shadows, ++ struct yaffs_xattr_mod *xop); ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning); ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn); ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev); ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id); ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos); ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj); ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev); ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out); ++/* ++ * Marshalling functions to get loff_t file sizes into aand out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); ++loff_t yaffs_max_file_size(struct yaffs_dev *dev); ++ ++/* ++ * Debug function to count number of blocks in each state ++ * NB Needs to be called with correct number of integers ++ */ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_linux.h linux-3.12.20/fs/yaffs2/yaffs_linux.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_linux.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,48 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_LINUX_H__ ++#define __YAFFS_LINUX_H__ ++ ++#include "yportenv.h" ++ ++struct yaffs_linux_context { ++ struct list_head context_list; /* List of these we have mounted */ ++ struct yaffs_dev *dev; ++ struct super_block *super; ++ struct task_struct *bg_thread; /* Background thread for this device */ ++ int bg_running; ++ struct mutex gross_lock; /* Gross locking mutex*/ ++ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size ++ * at compile time so we have to allocate it. ++ */ ++ struct list_head search_contexts; ++ struct task_struct *readdir_process; ++ unsigned mount_id; ++ int dirty; ++}; ++ ++#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) ++#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++#define WRITE_SIZE_STR "writesize" ++#define WRITE_SIZE(mtd) ((mtd)->writesize) ++#else ++#define WRITE_SIZE_STR "oobblock" ++#define WRITE_SIZE(mtd) ((mtd)->oobblock) ++#endif ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_mtdif.c linux-3.12.20/fs/yaffs2/yaffs_mtdif.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_mtdif.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,309 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_mtdif.h" ++ ++#include "linux/mtd/mtd.h" ++#include "linux/types.h" ++#include "linux/time.h" ++#include "linux/major.h" ++#include "linux/mtd/nand.h" ++#include "linux/kernel.h" ++#include "linux/version.h" ++#include "linux/types.h" ++ ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_linux.h" ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++#define mtd_erase(m, ei) (m)->erase(m, ei) ++#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) ++#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) ++#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) ++#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) ++#endif ++ ++ ++ ++int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ u32 addr = ++ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ struct erase_info ei; ++ int retval = 0; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++ ++static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", ++ dev, nand_chunk, data, data_len, oob, oob_len); ++ ++ if (!data || !data_len) { ++ data = NULL; ++ data_len = 0; ++ } ++ ++ if (!oob || !oob_len) { ++ oob = NULL; ++ oob_len = 0; ++ } ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = (u8 *)data; ++ ops.oobbuf = (u8 *)oob; ++ ++ retval = mtd_write_oob(mtd, addr, &ops); ++ if (retval) { ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "write_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ } ++ return retval ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = data; ++ ops.oobbuf = oob; ++ ++#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) ++ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; ++ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. ++ */ ++ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; ++#endif ++ /* Read page and oob using MTD. ++ * Check status and determine ECC result. ++ */ ++ retval = mtd_read_oob(mtd, addr, &ops); ++ if (retval) ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "read_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ ++ switch (retval) { ++ case 0: ++ /* no error */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ ++ case -EUCLEAN: ++ /* MTD's ECC fixed the data */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ break; ++ ++ case -EBADMSG: ++ default: ++ /* MTD's ECC could not fix the data */ ++ dev->n_ecc_unfixed++; ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ loff_t addr; ++ struct erase_info ei; ++ int retval = 0; ++ u32 block_size; ++ ++ block_size = dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ addr = ((loff_t) block_no) * block_size; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = block_size; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); ++ ++ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); ++ ++ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_initialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ ++ drv->drv_write_chunk_fn = yaffs_mtd_write; ++ drv->drv_read_chunk_fn = yaffs_mtd_read; ++ drv->drv_erase_fn = yaffs_mtd_erase; ++ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; ++ drv->drv_check_bad_fn = yaffs_mtd_check_bad; ++ drv->drv_initialise_fn = yaffs_mtd_initialise; ++ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; ++} ++ ++ ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev) ++{ ++ struct mtd_info *mtd; ++ ++ mtd = yaffs_get_mtd_device(sdev); ++ ++ /* Check it's an mtd device..... */ ++ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) ++ return NULL; /* This isn't an mtd device */ ++ ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device is not NAND it's type %d", ++ mtd->type); ++ return NULL; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); ++ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); ++ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); ++#else ++ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); ++#endif ++ ++ return mtd; ++} ++ ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) ++{ ++ if (yaffs_version == 2) { ++ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || ++ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && ++ !inband_tags) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not have the right page sizes" ++ ); ++ return -1; ++ } ++ } else { ++ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || ++ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support have the right page sizes" ++ ); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++void yaffs_put_mtd_device(struct mtd_info *mtd) ++{ ++ if(mtd) ++ put_mtd_device(mtd); ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_mtdif.h linux-3.12.20/fs/yaffs2/yaffs_mtdif.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_mtdif.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,25 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_MTDIF_H__ ++#define __YAFFS_MTDIF_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev); ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev); ++void yaffs_put_mtd_device(struct mtd_info *mtd); ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_nameval.c linux-3.12.20/fs/yaffs2/yaffs_nameval.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_nameval.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,208 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This simple implementation of a name-value store assumes a small number of ++* values and fits into a small finite buffer. ++ * ++ * Each attribute is stored as a record: ++ * sizeof(int) bytes record size. ++ * strnlen+1 bytes name null terminated. ++ * nbytes value. ++ * ---------- ++ * total size stored in record size ++ * ++ * This code has not been tested with unicode yet. ++ */ ++ ++#include "yaffs_nameval.h" ++ ++#include "yportenv.h" ++ ++static int nval_find(const char *xb, int xb_size, const YCHAR *name, ++ int *exist_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), ++ name, size)) { ++ if (exist_size) ++ *exist_size = size; ++ return pos; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ if (exist_size) ++ *exist_size = 0; ++ return -ENODATA; ++} ++ ++static int nval_used(const char *xb, int xb_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return pos; ++} ++ ++int nval_del(char *xb, int xb_size, const YCHAR *name) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos < 0 || pos >= xb_size) ++ return -ENODATA; ++ ++ /* Find size, shift rest over this record, ++ * then zero out the rest of buffer */ ++ memcpy(&size, xb + pos, sizeof(int)); ++ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); ++ memset(xb + (xb_size - size), 0, size); ++ return 0; ++} ++ ++int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, ++ int bsize, int flags) ++{ ++ int pos; ++ int namelen = strnlen(name, xb_size); ++ int reclen; ++ int size_exist = 0; ++ int space; ++ int start; ++ ++ pos = nval_find(xb, xb_size, name, &size_exist); ++ ++ if (flags & XATTR_CREATE && pos >= 0) ++ return -EEXIST; ++ if (flags & XATTR_REPLACE && pos < 0) ++ return -ENODATA; ++ ++ start = nval_used(xb, xb_size); ++ space = xb_size - start + size_exist; ++ ++ reclen = (sizeof(int) + namelen + 1 + bsize); ++ ++ if (reclen > space) ++ return -ENOSPC; ++ ++ if (pos >= 0) { ++ nval_del(xb, xb_size, name); ++ start = nval_used(xb, xb_size); ++ } ++ ++ pos = start; ++ ++ memcpy(xb + pos, &reclen, sizeof(int)); ++ pos += sizeof(int); ++ strncpy((YCHAR *) (xb + pos), name, reclen); ++ pos += (namelen + 1); ++ memcpy(xb + pos, buf, bsize); ++ return 0; ++} ++ ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos >= 0 && pos < xb_size) { ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ pos += sizeof(int); /* advance past record length */ ++ size -= sizeof(int); ++ ++ /* Advance over name string */ ++ while (xb[pos] && size > 0 && pos < xb_size) { ++ pos++; ++ size--; ++ } ++ /*Advance over NUL */ ++ pos++; ++ size--; ++ ++ /* If bsize is zero then this is a size query. ++ * Return the size, but don't copy. ++ */ ++ if (!bsize) ++ return size; ++ ++ if (size <= bsize) { ++ memcpy(buf, xb + pos, size); ++ return size; ++ } ++ } ++ if (pos >= 0) ++ return -ERANGE; ++ ++ return -ENODATA; ++} ++ ++int nval_list(const char *xb, int xb_size, char *buf, int bsize) ++{ ++ int pos = 0; ++ int size; ++ int name_len; ++ int ncopied = 0; ++ int filled = 0; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > sizeof(int) && ++ size <= xb_size && ++ (pos + size) < xb_size && ++ !filled) { ++ pos += sizeof(int); ++ size -= sizeof(int); ++ name_len = strnlen((YCHAR *) (xb + pos), size); ++ if (ncopied + name_len + 1 < bsize) { ++ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); ++ buf += name_len; ++ *buf = '\0'; ++ buf++; ++ if (sizeof(YCHAR) > 1) { ++ *buf = '\0'; ++ buf++; ++ } ++ ncopied += (name_len + 1); ++ } else { ++ filled = 1; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return ncopied; ++} ++ ++int nval_hasvalues(const char *xb, int xb_size) ++{ ++ return nval_used(xb, xb_size) > 0; ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_nameval.h linux-3.12.20/fs/yaffs2/yaffs_nameval.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_nameval.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __NAMEVAL_H__ ++#define __NAMEVAL_H__ ++ ++#include "yportenv.h" ++ ++int nval_del(char *xb, int xb_size, const YCHAR * name); ++int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, ++ int bsize, int flags); ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize); ++int nval_list(const char *xb, int xb_size, char *buf, int bsize); ++int nval_hasvalues(const char *xb, int xb_size); ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_nand.c linux-3.12.20/fs/yaffs2/yaffs_nand.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_nand.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,122 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_nand.h" ++#include "yaffs_tagscompat.h" ++ ++#include "yaffs_getblockinfo.h" ++#include "yaffs_summary.h" ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ struct yaffs_ext_tags local_tags; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_reads++; ++ ++ /* If there are no tags provided use local tags. */ ++ if (!tags) ++ tags = &local_tags; ++ ++ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); ++ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { ++ ++ struct yaffs_block_info *bi; ++ bi = yaffs_get_block_info(dev, ++ nand_chunk / ++ dev->param.chunks_per_block); ++ yaffs_handle_chunk_error(dev, bi); ++ } ++ return result; ++} ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_writes++; ++ ++ if (!tags) { ++ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ tags->seq_number = dev->seq_number; ++ tags->chunk_used = 1; ++ yaffs_trace(YAFFS_TRACE_WRITE, ++ "Writing chunk %d tags %d %d", ++ nand_chunk, tags->obj_id, tags->chunk_id); ++ ++ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, ++ buffer, tags); ++ ++ yaffs_summary_add(dev, tags, nand_chunk); ++ ++ return result; ++} ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ block_no -= dev->block_offset; ++ dev->n_bad_markings++; ++ ++ if (dev->param.disable_bad_block_marking) ++ return YAFFS_OK; ++ ++ return dev->tagger.mark_bad_fn(dev, block_no); ++} ++ ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ block_no -= dev->block_offset; ++ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); ++} ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ int result; ++ ++ block_no -= dev->block_offset; ++ dev->n_erasures++; ++ result = dev->drv.drv_erase_fn(dev, block_no); ++ return result; ++} ++ ++int yaffs_init_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_initialise_fn) ++ return dev->drv.drv_initialise_fn(dev); ++ return YAFFS_OK; ++} ++ ++int yaffs_deinit_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_deinitialise_fn) ++ return dev->drv.drv_deinitialise_fn(dev); ++ return YAFFS_OK; ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_nand.h linux-3.12.20/fs/yaffs2/yaffs_nand.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_nand.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_NAND_H__ ++#define __YAFFS_NAND_H__ ++#include "yaffs_guts.h" ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ unsigned *seq_number); ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); ++ ++int yaffs_init_nand(struct yaffs_dev *dev); ++int yaffs_deinit_nand(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.12.20/fs/yaffs2/yaffs_packedtags1.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,56 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags1.h" ++#include "yportenv.h" ++ ++static const u8 all_ff[20] = { ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t) ++{ ++ pt->chunk_id = t->chunk_id; ++ pt->serial_number = t->serial_number; ++ pt->n_bytes = t->n_bytes; ++ pt->obj_id = t->obj_id; ++ pt->ecc = 0; ++ pt->deleted = (t->is_deleted) ? 0 : 1; ++ pt->unused_stuff = 0; ++ pt->should_be_ff = 0xffffffff; ++} ++ ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt) ++{ ++ ++ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { ++ t->block_bad = 0; ++ if (pt->should_be_ff != 0xffffffff) ++ t->block_bad = 1; ++ t->chunk_used = 1; ++ t->obj_id = pt->obj_id; ++ t->chunk_id = pt->chunk_id; ++ t->n_bytes = pt->n_bytes; ++ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ t->is_deleted = (pt->deleted) ? 0 : 1; ++ t->serial_number = pt->serial_number; ++ } else { ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ } ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.12.20/fs/yaffs2/yaffs_packedtags1.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS1_H__ ++#define __YAFFS_PACKEDTAGS1_H__ ++ ++#include "yaffs_guts.h" ++ ++struct yaffs_packed_tags1 { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 deleted:1; ++ u32 unused_stuff:1; ++ unsigned should_be_ff; ++ ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt); ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.12.20/fs/yaffs2/yaffs_packedtags2.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,197 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags2.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++/* This code packs a set of extended tags into a binary structure for ++ * NAND storage ++ */ ++ ++/* Some of the information is "extra" struff which can be packed in to ++ * speed scanning ++ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. ++ */ ++ ++/* Extra flags applied to chunk_id */ ++ ++#define EXTRA_HEADER_INFO_FLAG 0x80000000 ++#define EXTRA_SHRINK_FLAG 0x40000000 ++#define EXTRA_SHADOWS_FLAG 0x20000000 ++#define EXTRA_SPARE_FLAGS 0x10000000 ++ ++#define ALL_EXTRA_FLAGS 0xf0000000 ++ ++/* Also, the top 4 bits of the object Id are set to the object type. */ ++#define EXTRA_OBJECT_TYPE_SHIFT (28) ++#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) ++ ++static void yaffs_dump_packed_tags2_tags_only( ++ const struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "packed tags obj %d chunk %d byte %d seq %d", ++ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); ++} ++ ++static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) ++{ ++ yaffs_dump_packed_tags2_tags_only(&pt->t); ++} ++ ++static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", ++ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, ++ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, ++ t->seq_number); ++ ++} ++ ++static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) ++{ ++ if (t->chunk_id != 0 || !t->extra_available) ++ return 0; ++ ++ /* Check if the file size is too long to store */ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && ++ (t->extra_file_size >> 31) != 0) ++ return 0; ++ return 1; ++} ++ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, ++ const struct yaffs_ext_tags *t) ++{ ++ ptt->chunk_id = t->chunk_id; ++ ptt->seq_number = t->seq_number; ++ ptt->n_bytes = t->n_bytes; ++ ptt->obj_id = t->obj_id; ++ ++ /* Only store extra tags for object headers. ++ * If it is a file then only store if the file size is short\ ++ * enough to fit. ++ */ ++ if (yaffs_check_tags_extra_packable(t)) { ++ /* Store the extra header info instead */ ++ /* We save the parent object in the chunk_id */ ++ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; ++ if (t->extra_is_shrink) ++ ptt->chunk_id |= EXTRA_SHRINK_FLAG; ++ if (t->extra_shadows) ++ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; ++ ++ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ ptt->n_bytes = t->extra_equiv_id; ++ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ ptt->n_bytes = (unsigned) t->extra_file_size; ++ else ++ ptt->n_bytes = 0; ++ } ++ ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc) ++{ ++ yaffs_pack_tags2_tags_only(&pt->t, t); ++ ++ if (tags_ecc) ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc); ++} ++ ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ ++ if (ptt->seq_number == 0xffffffff) ++ return; ++ ++ t->block_bad = 0; ++ t->chunk_used = 1; ++ t->obj_id = ptt->obj_id; ++ t->chunk_id = ptt->chunk_id; ++ t->n_bytes = ptt->n_bytes; ++ t->is_deleted = 0; ++ t->serial_number = 0; ++ t->seq_number = ptt->seq_number; ++ ++ /* Do extra header info stuff */ ++ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { ++ t->chunk_id = 0; ++ t->n_bytes = 0; ++ ++ t->extra_available = 1; ++ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); ++ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; ++ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; ++ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; ++ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ t->extra_equiv_id = ptt->n_bytes; ++ else ++ t->extra_file_size = ptt->n_bytes; ++ } ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc) ++{ ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ if (pt->t.seq_number != 0xffffffff && tags_ecc) { ++ /* Chunk is in use and we need to do ECC */ ++ ++ struct yaffs_ecc_other ecc; ++ int result; ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &ecc); ++ result = ++ yaffs_ecc_correct_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc, &ecc); ++ switch (result) { ++ case 0: ++ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ case 1: ++ ecc_result = YAFFS_ECC_RESULT_FIXED; ++ break; ++ case -1: ++ ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ break; ++ default: ++ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ } ++ } ++ yaffs_unpack_tags2_tags_only(t, &pt->t); ++ ++ t->ecc_result = ecc_result; ++ ++ yaffs_dump_packed_tags2(pt); ++ yaffs_dump_tags2(t); ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.12.20/fs/yaffs2/yaffs_packedtags2.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,47 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS2_H__ ++#define __YAFFS_PACKEDTAGS2_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_ecc.h" ++ ++struct yaffs_packed_tags2_tags_only { ++ unsigned seq_number; ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++struct yaffs_packed_tags2 { ++ struct yaffs_packed_tags2_tags_only t; ++ struct yaffs_ecc_other ecc; ++}; ++ ++/* Full packed tags with ECC, used for oob tags */ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc); ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc); ++ ++/* Only the tags part (no ECC for use with inband tags */ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *pt); ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_summary.c linux-3.12.20/fs/yaffs2/yaffs_summary.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_summary.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,312 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Summaries write the useful part of the tags for the chunks in a block into an ++ * an array which is written to the last n chunks of the block. ++ * Reading the summaries gives all the tags for the block in one read. Much ++ * faster. ++ * ++ * Chunks holding summaries are marked with tags making it look like ++ * they are part of a fake file. ++ * ++ * The summary could also be used during gc. ++ * ++ */ ++ ++#include "yaffs_summary.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_bitmap.h" ++ ++/* ++ * The summary is built up in an array of summary tags. ++ * This gets written to the last one or two (maybe more) chunks in a block. ++ * A summary header is written as the first part of each chunk of summary data. ++ * The summary header must match or the summary is rejected. ++ */ ++ ++/* Summary tags don't need the sequence number because that is redundant. */ ++struct yaffs_summary_tags { ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++/* Summary header */ ++struct yaffs_summary_header { ++ unsigned version; /* Must match current version */ ++ unsigned block; /* Must be this block */ ++ unsigned seq; /* Must be this sequence number */ ++ unsigned sum; /* Just add up all the bytes in the tags */ ++}; ++ ++ ++static void yaffs_summary_clear(struct yaffs_dev *dev) ++{ ++ if (!dev->sum_tags) ++ return; ++ memset(dev->sum_tags, 0, dev->chunks_per_summary * ++ sizeof(struct yaffs_summary_tags)); ++} ++ ++ ++void yaffs_summary_deinit(struct yaffs_dev *dev) ++{ ++ kfree(dev->sum_tags); ++ dev->sum_tags = NULL; ++ kfree(dev->gc_sum_tags); ++ dev->gc_sum_tags = NULL; ++ dev->chunks_per_summary = 0; ++} ++ ++int yaffs_summary_init(struct yaffs_dev *dev) ++{ ++ int sum_bytes; ++ int chunks_used; /* Number of chunks used by summary */ ++ int sum_tags_bytes; ++ ++ sum_bytes = dev->param.chunks_per_block * ++ sizeof(struct yaffs_summary_tags); ++ ++ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ ++ (dev->data_bytes_per_chunk - ++ sizeof(struct yaffs_summary_header)); ++ ++ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ if (!dev->sum_tags || !dev->gc_sum_tags) { ++ yaffs_summary_deinit(dev); ++ return YAFFS_FAIL; ++ } ++ ++ yaffs_summary_clear(dev); ++ ++ return YAFFS_OK; ++} ++ ++static unsigned yaffs_summary_sum(struct yaffs_dev *dev) ++{ ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int i; ++ unsigned sum = 0; ++ ++ i = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ while (i > 0) { ++ sum += *sum_buffer; ++ sum_buffer++; ++ i--; ++ } ++ ++ return sum; ++} ++ ++static int yaffs_summary_write(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int n_bytes; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); ++ tags.obj_id = YAFFS_OBJECTID_SUMMARY; ++ tags.chunk_id = 1; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ hdr.version = YAFFS_SUMMARY_VERSION; ++ hdr.block = blk; ++ hdr.seq = bi->seq_number; ++ hdr.sum = yaffs_summary_sum(dev); ++ ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ memcpy(buffer, &hdr, sizeof(hdr)); ++ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); ++ tags.n_bytes = this_tx + sizeof(hdr); ++ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ break; ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ dev->n_free_chunks--; ++ ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ tags.chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ ++ if (result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ ++ return result; ++} ++ ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)st; ++ int n_bytes; ++ int chunk_id; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ int sum_tags_bytes; ++ ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = blk * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ chunk_id = 1; ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (tags.chunk_id != chunk_id || ++ tags.obj_id != YAFFS_OBJECTID_SUMMARY || ++ tags.chunk_used == 0 || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.n_bytes != (this_tx + sizeof(hdr))) ++ result = YAFFS_FAIL; ++ if (result != YAFFS_OK) ++ break; ++ ++ if (st == dev->sum_tags) { ++ /* If we're scanning then update the block info */ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ } ++ memcpy(&hdr, buffer, sizeof(hdr)); ++ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (result == YAFFS_OK) { ++ /* Verify header */ ++ if (hdr.version != YAFFS_SUMMARY_VERSION || ++ hdr.seq != bi->seq_number || ++ hdr.sum != yaffs_summary_sum(dev)) ++ result = YAFFS_FAIL; ++ } ++ ++ if (st == dev->sum_tags && result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ return result; ++} ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_nand) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; ++ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; ++ ++ if (!dev->sum_tags) ++ return YAFFS_OK; ++ ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ yaffs_pack_tags2_tags_only(&tags_only, tags); ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ sum_tags->chunk_id = tags_only.chunk_id; ++ sum_tags->n_bytes = tags_only.n_bytes; ++ sum_tags->obj_id = tags_only.obj_id; ++ ++ if (chunk_in_block == dev->chunks_per_summary - 1) { ++ /* Time to write out the summary */ ++ yaffs_summary_write(dev, block_in_nand); ++ yaffs_summary_clear(dev); ++ yaffs_skip_rest_of_block(dev); ++ } ++ } ++ return YAFFS_OK; ++} ++ ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ tags_only.chunk_id = sum_tags->chunk_id; ++ tags_only.n_bytes = sum_tags->n_bytes; ++ tags_only.obj_id = sum_tags->obj_id; ++ yaffs_unpack_tags2_tags_only(tags, &tags_only); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int i; ++ ++ if (!bi->has_summary) ++ return; ++ ++ for (i = dev->chunks_per_summary; ++ i < dev->param.chunks_per_block; ++ i++) { ++ if (yaffs_check_chunk_bit(dev, blk, i)) { ++ yaffs_clear_chunk_bit(dev, blk, i); ++ bi->pages_in_use--; ++ dev->n_free_chunks++; ++ } ++ } ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_summary.h linux-3.12.20/fs/yaffs2/yaffs_summary.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_summary.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,37 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_SUMMARY_H__ ++#define __YAFFS_SUMMARY_H__ ++ ++#include "yaffs_packedtags2.h" ++ ++ ++int yaffs_summary_init(struct yaffs_dev *dev); ++void yaffs_summary_deinit(struct yaffs_dev *dev); ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk); ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk); ++ ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.12.20/fs/yaffs2/yaffs_tagscompat.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,381 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_ecc.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_trace.h" ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); ++ ++ ++/********** Tags ECC calculations *********/ ++ ++ ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags) ++{ ++ /* Calculate an ecc */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ unsigned i, j; ++ unsigned ecc = 0; ++ unsigned bit = 0; ++ ++ tags->ecc = 0; ++ ++ for (i = 0; i < 8; i++) { ++ for (j = 1; j & 0xff; j <<= 1) { ++ bit++; ++ if (b[i] & j) ++ ecc ^= bit; ++ } ++ } ++ tags->ecc = ecc; ++} ++ ++int yaffs_check_tags_ecc(struct yaffs_tags *tags) ++{ ++ unsigned ecc = tags->ecc; ++ ++ yaffs_calc_tags_ecc(tags); ++ ++ ecc ^= tags->ecc; ++ ++ if (ecc && ecc <= 64) { ++ /* TODO: Handle the failure better. Retire? */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ ++ ecc--; ++ ++ b[ecc / 8] ^= (1 << (ecc & 7)); ++ ++ /* Now recvalc the ecc */ ++ yaffs_calc_tags_ecc(tags); ++ ++ return 1; /* recovered error */ ++ } else if (ecc) { ++ /* Wierd ecc failure value */ ++ /* TODO Need to do somethiong here */ ++ return -1; /* unrecovered error */ ++ } ++ return 0; ++} ++ ++/********** Tags **********/ ++ ++static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ ++ yaffs_calc_tags_ecc(tags_ptr); ++ ++ spare_ptr->tb0 = tu->as_bytes[0]; ++ spare_ptr->tb1 = tu->as_bytes[1]; ++ spare_ptr->tb2 = tu->as_bytes[2]; ++ spare_ptr->tb3 = tu->as_bytes[3]; ++ spare_ptr->tb4 = tu->as_bytes[4]; ++ spare_ptr->tb5 = tu->as_bytes[5]; ++ spare_ptr->tb6 = tu->as_bytes[6]; ++ spare_ptr->tb7 = tu->as_bytes[7]; ++} ++ ++static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, ++ struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ int result; ++ ++ tu->as_bytes[0] = spare_ptr->tb0; ++ tu->as_bytes[1] = spare_ptr->tb1; ++ tu->as_bytes[2] = spare_ptr->tb2; ++ tu->as_bytes[3] = spare_ptr->tb3; ++ tu->as_bytes[4] = spare_ptr->tb4; ++ tu->as_bytes[5] = spare_ptr->tb5; ++ tu->as_bytes[6] = spare_ptr->tb6; ++ tu->as_bytes[7] = spare_ptr->tb7; ++ ++ result = yaffs_check_tags_ecc(tags_ptr); ++ if (result > 0) ++ dev->n_tags_ecc_fixed++; ++ else if (result < 0) ++ dev->n_tags_ecc_unfixed++; ++} ++ ++static void yaffs_spare_init(struct yaffs_spare *spare) ++{ ++ memset(spare, 0xff, sizeof(struct yaffs_spare)); ++} ++ ++static int yaffs_wr_nand(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ struct yaffs_spare *spare) ++{ ++ int data_size = dev->data_bytes_per_chunk; ++ ++ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, sizeof(*spare)); ++} ++ ++static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, ++ struct yaffs_spare *spare, ++ enum yaffs_ecc_result *ecc_result, ++ int correct_errors) ++{ ++ int ret_val; ++ struct yaffs_spare local_spare; ++ int data_size; ++ int spare_size; ++ int ecc_result1, ecc_result2; ++ u8 calc_ecc[3]; ++ ++ if (!spare) { ++ /* If we don't have a real spare, then we use a local one. */ ++ /* Need this for the calculation of the ecc */ ++ spare = &local_spare; ++ } ++ data_size = dev->data_bytes_per_chunk; ++ spare_size = sizeof(struct yaffs_spare); ++ ++ if (dev->param.use_nand_ecc) ++ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, spare_size, ++ ecc_result); ++ ++ ++ /* Handle the ECC at this level. */ ++ ++ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *)spare, spare_size, ++ NULL); ++ if (!data || !correct_errors) ++ return ret_val; ++ ++ /* Do ECC correction if needed. */ ++ yaffs_ecc_calc(data, calc_ecc); ++ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); ++ yaffs_ecc_calc(&data[256], calc_ecc); ++ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); ++ ++ if (ecc_result1 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result1 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result2 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result2 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result1 || ecc_result2) { ++ /* We had a data problem on this page */ ++ yaffs_handle_rd_data_error(dev, nand_chunk); ++ } ++ ++ if (ecc_result1 < 0 || ecc_result2 < 0) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ else if (ecc_result1 > 0 || ecc_result2 > 0) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ else ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ return ret_val; ++} ++ ++/* ++ * Functions for robustisizing ++ */ ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ ++ /* Mark the block for retirement */ ++ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> ++ needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>>Block %d marked for retirement", ++ flash_block); ++ ++ /* TODO: ++ * Just do a garbage collection on the affected block ++ * then retire the block ++ * NB recursion ++ */ ++} ++ ++static int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ ++ yaffs_spare_init(&spare); ++ ++ if (ext_tags->is_deleted) ++ spare.page_status = 0; ++ else { ++ tags.obj_id = ext_tags->obj_id; ++ tags.chunk_id = ext_tags->chunk_id; ++ ++ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; ++ else ++ tags.n_bytes_msb = 3; ++ ++ tags.serial_number = ext_tags->serial_number; ++ ++ if (!dev->param.use_nand_ecc && data) { ++ yaffs_ecc_calc(data, spare.ecc1); ++ yaffs_ecc_calc(&data[256], spare.ecc2); ++ } ++ ++ yaffs_load_tags_to_spare(&spare, &tags); ++ } ++ return yaffs_wr_nand(dev, nand_chunk, data, &spare); ++} ++ ++static int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ int deleted; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ if (!yaffs_rd_chunk_nand(dev, nand_chunk, ++ data, &spare, &ecc_result, 1)) ++ return YAFFS_FAIL; ++ ++ /* ext_tags may be NULL */ ++ if (!ext_tags) ++ return YAFFS_OK; ++ ++ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; ++ ++ ext_tags->is_deleted = deleted; ++ ext_tags->ecc_result = ecc_result; ++ ext_tags->block_bad = 0; /* We're reading it */ ++ /* therefore it is not a bad block */ ++ ext_tags->chunk_used = ++ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; ++ ++ if (ext_tags->chunk_used) { ++ yaffs_get_tags_from_spare(dev, &spare, &tags); ++ ext_tags->obj_id = tags.obj_id; ++ ext_tags->chunk_id = tags.chunk_id; ++ ext_tags->n_bytes = tags.n_bytes_lsb; ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ ext_tags->n_bytes |= ++ (((unsigned)tags.n_bytes_msb) << 10); ++ ++ ext_tags->serial_number = tags.serial_number; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_spare spare; ++ ++ memset(&spare, 0xff, sizeof(struct yaffs_spare)); ++ ++ spare.block_status = 'Y'; ++ ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, ++ &spare); ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, ++ NULL, &spare); ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ struct yaffs_spare spare0, spare1; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ enum yaffs_ecc_result dummy; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ *seq_number = 0; ++ ++ /* Look for bad block markers in the first two chunks */ ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, ++ NULL, &spare0, &dummy, 0); ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, ++ NULL, &spare1, &dummy, 0); ++ ++ if (hweight8(spare0.block_status & spare1.block_status) < 7) ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ else ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ ++ return YAFFS_OK; ++} ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev) ++{ ++ if(dev->param.is_yaffs2) ++ return; ++ if(!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; ++ if(!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; ++ if(!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; ++ if(!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.12.20/fs/yaffs2/yaffs_tagscompat.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSCOMPAT_H__ ++#define __YAFFS_TAGSCOMPAT_H__ ++ ++ ++#include "yaffs_guts.h" ++ ++#if 0 ++ ++ ++int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); ++int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ ++#endif ++ ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev); ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags); ++int yaffs_check_tags_ecc(struct yaffs_tags *tags); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.12.20/fs/yaffs2/yaffs_tagsmarshall.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,199 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_packedtags2.h" ++ ++static int yaffs_tags_marshall_write(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ struct yaffs_packed_tags2 pt; ++ int retval; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_write chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ /* For yaffs2 writing there must be both data and tags. ++ * If we're using inband tags, then the tags are stuffed into ++ * the end of the data buffer. ++ */ ++ if (!data || !tags) ++ BUG(); ++ else if (dev->param.inband_tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *)(data + ++ dev-> ++ data_bytes_per_chunk); ++ yaffs_pack_tags2_tags_only(pt2tp, tags); ++ } else { ++ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); ++ } ++ ++ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ (dev->param.inband_tags) ? NULL : packed_tags_ptr, ++ (dev->param.inband_tags) ? 0 : packed_tags_size); ++ ++ return retval; ++} ++ ++static int yaffs_tags_marshall_read(struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = 0; ++ int local_data = 0; ++ u8 spare_buffer[100]; ++ enum yaffs_ecc_result ecc_result; ++ ++ struct yaffs_packed_tags2 pt; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_read chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ if (dev->param.inband_tags) { ++ if (!data) { ++ local_data = 1; ++ data = yaffs_get_temp_buffer(dev); ++ } ++ } ++ ++ if (dev->param.inband_tags || (data && !tags)) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ NULL, 0, ++ &ecc_result); ++ else if (tags) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ spare_buffer, packed_tags_size, ++ &ecc_result); ++ else ++ BUG(); ++ ++ ++ if (dev->param.inband_tags) { ++ if (tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *) ++ &data[dev->data_bytes_per_chunk]; ++ yaffs_unpack_tags2_tags_only(tags, pt2tp); ++ } ++ } else if (tags) { ++ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); ++ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); ++ } ++ ++ if (local_data) ++ yaffs_release_temp_buffer(dev, data); ++ ++ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { ++ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { ++ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) ++ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ } ++ ++ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", ++ block_no); ++ ++ retval = dev->drv.drv_check_bad_fn(dev, block_no); ++ ++ if (retval== YAFFS_FAIL) { ++ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); ++ ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ *seq_number = 0; ++ } else { ++ struct yaffs_ext_tags t; ++ ++ yaffs_tags_marshall_read(dev, ++ block_no * dev->param.chunks_per_block, ++ NULL, &t); ++ ++ if (t.chunk_used) { ++ *seq_number = t.seq_number; ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ } else { ++ *seq_number = 0; ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "block query returns seq %d state %d", ++ *seq_number, *state); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ return dev->drv.drv_mark_bad_fn(dev, block_no); ++ ++} ++ ++ ++void yaffs_tags_marshall_install(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; ++ ++ if (!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; ++ ++ if (!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; ++ ++ if (!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; ++ ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.12.20/fs/yaffs2/yaffs_tagsmarshall.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSMARSHALL_H__ ++#define __YAFFS_TAGSMARSHALL_H__ ++ ++#include "yaffs_guts.h" ++void yaffs_tags_marshall_install(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_trace.h linux-3.12.20/fs/yaffs2/yaffs_trace.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_trace.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,57 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YTRACE_H__ ++#define __YTRACE_H__ ++ ++extern unsigned int yaffs_trace_mask; ++extern unsigned int yaffs_wr_attempts; ++ ++/* ++ * Tracing flags. ++ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. ++ */ ++ ++#define YAFFS_TRACE_OS 0x00000002 ++#define YAFFS_TRACE_ALLOCATE 0x00000004 ++#define YAFFS_TRACE_SCAN 0x00000008 ++#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 ++#define YAFFS_TRACE_ERASE 0x00000020 ++#define YAFFS_TRACE_GC 0x00000040 ++#define YAFFS_TRACE_WRITE 0x00000080 ++#define YAFFS_TRACE_TRACING 0x00000100 ++#define YAFFS_TRACE_DELETION 0x00000200 ++#define YAFFS_TRACE_BUFFERS 0x00000400 ++#define YAFFS_TRACE_NANDACCESS 0x00000800 ++#define YAFFS_TRACE_GC_DETAIL 0x00001000 ++#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 ++#define YAFFS_TRACE_MTD 0x00004000 ++#define YAFFS_TRACE_CHECKPOINT 0x00008000 ++ ++#define YAFFS_TRACE_VERIFY 0x00010000 ++#define YAFFS_TRACE_VERIFY_NAND 0x00020000 ++#define YAFFS_TRACE_VERIFY_FULL 0x00040000 ++#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 ++ ++#define YAFFS_TRACE_SYNC 0x00100000 ++#define YAFFS_TRACE_BACKGROUND 0x00200000 ++#define YAFFS_TRACE_LOCK 0x00400000 ++#define YAFFS_TRACE_MOUNT 0x00800000 ++ ++#define YAFFS_TRACE_ERROR 0x40000000 ++#define YAFFS_TRACE_BUG 0x80000000 ++#define YAFFS_TRACE_ALWAYS 0xf0000000 ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_verify.c linux-3.12.20/fs/yaffs2/yaffs_verify.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_verify.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,529 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_verify.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++ ++int yaffs_skip_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & ++ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_full_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_nand_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); ++} ++ ++static const char * const block_state_name[] = { ++ "Unknown", ++ "Needs scan", ++ "Scanning", ++ "Empty", ++ "Allocating", ++ "Full", ++ "Dirty", ++ "Checkpoint", ++ "Collecting", ++ "Dead" ++}; ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) ++{ ++ int actually_used; ++ int in_use; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Report illegal runtime states */ ++ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has undefined state %d", ++ n, bi->block_state); ++ ++ switch (bi->block_state) { ++ case YAFFS_BLOCK_STATE_UNKNOWN: ++ case YAFFS_BLOCK_STATE_SCANNING: ++ case YAFFS_BLOCK_STATE_NEEDS_SCAN: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has bad run-state %s", ++ n, block_state_name[bi->block_state]); ++ } ++ ++ /* Check pages in use and soft deletions are legal */ ++ ++ actually_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->pages_in_use < 0 || ++ bi->pages_in_use > dev->param.chunks_per_block || ++ bi->soft_del_pages < 0 || ++ bi->soft_del_pages > dev->param.chunks_per_block || ++ actually_used < 0 || actually_used > dev->param.chunks_per_block) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has illegal values pages_in_used %d soft_del_pages %d", ++ n, bi->pages_in_use, bi->soft_del_pages); ++ ++ /* Check chunk bitmap legal */ ++ in_use = yaffs_count_chunk_bits(dev, n); ++ if (in_use != bi->pages_in_use) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", ++ n, bi->pages_in_use, in_use); ++} ++ ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n) ++{ ++ yaffs_verify_blk(dev, bi, n); ++ ++ /* After collection the block should be in the erased state */ ++ ++ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && ++ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Block %d is in state %d after gc, should be erased", ++ n, bi->block_state); ++ } ++} ++ ++void yaffs_verify_blocks(struct yaffs_dev *dev) ++{ ++ int i; ++ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; ++ int illegal_states = 0; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ memset(state_count, 0, sizeof(state_count)); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ yaffs_verify_blk(dev, bi, i); ++ ++ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) ++ state_count[bi->block_state]++; ++ else ++ illegal_states++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%d blocks have illegal states", ++ illegal_states); ++ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many allocating blocks"); ++ ++ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%s %d blocks", ++ block_state_name[i], state_count[i]); ++ ++ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Checkpoint block count wrong dev %d count %d", ++ dev->blocks_in_checkpt, ++ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); ++ ++ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Erased block count wrong dev %d count %d", ++ dev->n_erased_blocks, ++ state_count[YAFFS_BLOCK_STATE_EMPTY]); ++ ++ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many collecting blocks %d (max is 1)", ++ state_count[YAFFS_BLOCK_STATE_COLLECTING]); ++} ++ ++/* ++ * Verify the object header. oh must be valid, but obj and tags may be NULL in ++ * which case those tests will not be performed. ++ */ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!(tags && obj && oh)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Verifying object header tags %p obj %p oh %p", ++ tags, obj, oh); ++ return; ++ } ++ ++ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || ++ oh->type > YAFFS_OBJECT_TYPE_MAX) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header type is illegal value 0x%x", ++ tags->obj_id, oh->type); ++ ++ if (tags->obj_id != obj->obj_id) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch obj_id %d", ++ tags->obj_id, obj->obj_id); ++ ++ /* ++ * Check that the object's parent ids match if parent_check requested. ++ * ++ * Tests do not apply to the root object. ++ */ ++ ++ if (parent_check && tags->obj_id > 1 && !obj->parent) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d obj->parent is NULL", ++ tags->obj_id, oh->parent_obj_id); ++ ++ if (parent_check && obj->parent && ++ oh->parent_obj_id != obj->parent->obj_id && ++ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d parent_obj_id %d", ++ tags->obj_id, oh->parent_obj_id, ++ obj->parent->obj_id); ++ ++ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is NULL", ++ obj->obj_id); ++ ++ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is 0xff", ++ obj->obj_id); ++} ++ ++void yaffs_verify_file(struct yaffs_obj *obj) ++{ ++ u32 x; ++ int required_depth; ++ int actual_depth; ++ int last_chunk; ++ u32 offset_in_chunk; ++ u32 the_chunk; ++ ++ u32 i; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ struct yaffs_tnode *tn; ++ u32 obj_id; ++ ++ if (!obj) ++ return; ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ dev = obj->my_dev; ++ obj_id = obj->obj_id; ++ ++ ++ /* Check file size is consistent with tnode depth */ ++ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, ++ &last_chunk, &offset_in_chunk); ++ last_chunk++; ++ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x > 0) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ actual_depth = obj->variant.file_variant.top_level; ++ ++ /* Check that the chunks in the tnode tree are all correct. ++ * We do this by scanning through the tnode tree and ++ * checking the tags for every chunk match. ++ */ ++ ++ if (yaffs_skip_nand_verification(dev)) ++ return; ++ ++ for (i = 1; i <= last_chunk; i++) { ++ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); ++ ++ if (!tn) ++ continue; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk > 0) { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ &tags); ++ if (tags.obj_id != obj_id || tags.chunk_id != i) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", ++ obj_id, i, the_chunk, ++ tags.obj_id, tags.chunk_id); ++ } ++ } ++} ++ ++void yaffs_verify_link(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify sane equivalent object */ ++} ++ ++void yaffs_verify_symlink(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify symlink string */ ++} ++ ++void yaffs_verify_special(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++} ++ ++void yaffs_verify_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ u32 chunk_min; ++ u32 chunk_max; ++ u32 chunk_id_ok; ++ u32 chunk_in_range; ++ u32 chunk_wrongly_deleted; ++ u32 chunk_valid; ++ ++ if (!obj) ++ return; ++ ++ if (obj->being_created) ++ return; ++ ++ dev = obj->my_dev; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Check sane object header chunk */ ++ ++ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; ++ chunk_max = ++ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; ++ ++ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && ++ ((unsigned)(obj->hdr_chunk)) <= chunk_max); ++ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); ++ chunk_valid = chunk_in_range && ++ yaffs_check_chunk_bit(dev, ++ obj->hdr_chunk / dev->param.chunks_per_block, ++ obj->hdr_chunk % dev->param.chunks_per_block); ++ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; ++ ++ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has chunk_id %d %s %s", ++ obj->obj_id, obj->hdr_chunk, ++ chunk_id_ok ? "" : ",out of range", ++ chunk_wrongly_deleted ? ",marked as deleted" : ""); ++ ++ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj_hdr *oh; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); ++ ++ yaffs_verify_oh(obj, oh, &tags, 1); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ /* Verify it has a parent */ ++ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has parent pointer %p which does not look like an object", ++ obj->obj_id, obj->parent); ++ } ++ ++ /* Verify parent is a directory */ ++ if (obj->parent && ++ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d's parent is not a directory (type %d)", ++ obj->obj_id, obj->parent->variant_type); ++ } ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ yaffs_verify_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ yaffs_verify_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ yaffs_verify_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ yaffs_verify_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ yaffs_verify_special(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has illegaltype %d", ++ obj->obj_id, obj->variant_type); ++ break; ++ } ++} ++ ++void yaffs_verify_objects(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int i; ++ struct list_head *lh; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ yaffs_verify_obj(obj); ++ } ++ } ++} ++ ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ int count = 0; ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!obj->parent) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); ++ BUG(); ++ return; ++ } ++ ++ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &obj->parent->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_verify_obj(list_obj); ++ if (obj == list_obj) ++ count++; ++ } ++ ++ if (count != 1) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory %d times", ++ count); ++ BUG(); ++ } ++} ++ ++void yaffs_verify_dir(struct yaffs_obj *directory) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ ++ if (!directory) { ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_full_verification(directory->my_dev)) ++ return; ++ ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Directory has wrong type: %d", ++ directory->variant_type); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &directory->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (list_obj->parent != directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory list has wrong parent %p", ++ list_obj->parent); ++ BUG(); ++ } ++ yaffs_verify_obj_in_dir(list_obj); ++ } ++} ++ ++static int yaffs_free_verification_failures; ++ ++void yaffs_verify_free_chunks(struct yaffs_dev *dev) ++{ ++ int counted; ++ int difference; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ counted = yaffs_count_free_chunks(dev); ++ ++ difference = dev->n_free_chunks - counted; ++ ++ if (difference) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Freechunks verification failure %d %d %d", ++ dev->n_free_chunks, counted, difference); ++ yaffs_free_verification_failures++; ++ } ++} ++ ++int yaffs_verify_file_sane(struct yaffs_obj *in) ++{ ++ (void) in; ++ return YAFFS_OK; ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_verify.h linux-3.12.20/fs/yaffs2/yaffs_verify.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_verify.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,43 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_VERIFY_H__ ++#define __YAFFS_VERIFY_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, ++ int n); ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n); ++void yaffs_verify_blocks(struct yaffs_dev *dev); ++ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check); ++void yaffs_verify_file(struct yaffs_obj *obj); ++void yaffs_verify_link(struct yaffs_obj *obj); ++void yaffs_verify_symlink(struct yaffs_obj *obj); ++void yaffs_verify_special(struct yaffs_obj *obj); ++void yaffs_verify_obj(struct yaffs_obj *obj); ++void yaffs_verify_objects(struct yaffs_dev *dev); ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); ++void yaffs_verify_dir(struct yaffs_obj *directory); ++void yaffs_verify_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_verify_file_sane(struct yaffs_obj *obj); ++ ++int yaffs_skip_verification(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_vfs.c linux-3.12.20/fs/yaffs2/yaffs_vfs.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_vfs.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,3600 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * Acknowledgements: ++ * Luc van OostenRyck for numerous patches. ++ * Nick Bane for numerous patches. ++ * Nick Bane for 2.5/2.6 integration. ++ * Andras Toth for mknod rdev issue. ++ * Michael Fischer for finding the problem with inode inconsistency. ++ * Some code bodily lifted from JFFS ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * ++ * This is the file system front-end to YAFFS that hooks it up to ++ * the VFS. ++ * ++ * Special notes: ++ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with ++ * this superblock ++ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this ++ * superblock ++ * >> inode->u.generic_ip points to the associated struct yaffs_obj. ++ */ ++ ++/* ++ * There are two variants of the VFS glue code. This variant should compile ++ * for any version of Linux. ++ */ ++#include ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) ++#define YAFFS_COMPILE_BACKGROUND ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) ++#define YAFFS_COMPILE_FREEZER ++#endif ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) ++#define YAFFS_COMPILE_EXPORTFS ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_USE_SETATTR_COPY ++#define YAFFS_USE_TRUNCATE_SETSIZE ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_HAS_EVICT_INODE ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++#define YAFFS_NEW_FOLLOW_LINK 1 ++#else ++#define YAFFS_NEW_FOLLOW_LINK 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_HAS_WRITE_SUPER ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++#include ++#endif ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++#include ++#endif ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++#include ++#include ++#endif ++#ifdef YAFFS_COMPILE_FREEZER ++#include ++#endif ++ ++#include ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++#include ++ ++#define UnlockPage(p) unlock_page(p) ++#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) ++ ++/* FIXME: use sb->s_id instead ? */ ++#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) ++ ++#else ++ ++#include ++#define BDEVNAME_SIZE 0 ++#define yaffs_devname(sb, buf) kdevname(sb->s_dev) ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) ++/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ ++#define __user ++#endif ++ ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define YPROC_ROOT (&proc_root) ++#else ++#define YPROC_ROOT NULL ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define Y_INIT_TIMER(a) init_timer(a) ++#else ++#define Y_INIT_TIMER(a) init_timer_on_stack(a) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) ++#define YAFFS_USE_WRITE_BEGIN_END 1 ++#else ++#define YAFFS_USE_WRITE_BEGIN_END 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_SUPER_HAS_DIRTY ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) ++static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) ++{ ++ uint64_t result = partition_size; ++ do_div(result, block_size); ++ return (uint32_t) result; ++} ++#else ++#define YCALCBLOCKS(s, b) ((s)/(b)) ++#endif ++ ++#include ++#include ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#include "yaffs_linux.h" ++ ++#include "yaffs_mtdif.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_getblockinfo.h" ++ ++unsigned int yaffs_trace_mask = ++ YAFFS_TRACE_BAD_BLOCKS | ++ YAFFS_TRACE_ALWAYS | ++ 0; ++ ++unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; ++unsigned int yaffs_auto_checkpoint = 1; ++unsigned int yaffs_gc_control = 1; ++unsigned int yaffs_bg_enable = 1; ++unsigned int yaffs_auto_select = 1; ++/* Module Parameters */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++module_param(yaffs_trace_mask, uint, 0644); ++module_param(yaffs_wr_attempts, uint, 0644); ++module_param(yaffs_auto_checkpoint, uint, 0644); ++module_param(yaffs_gc_control, uint, 0644); ++module_param(yaffs_bg_enable, uint, 0644); ++#else ++MODULE_PARM(yaffs_trace_mask, "i"); ++MODULE_PARM(yaffs_wr_attempts, "i"); ++MODULE_PARM(yaffs_auto_checkpoint, "i"); ++MODULE_PARM(yaffs_gc_control, "i"); ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++/* use iget and read_inode */ ++#define Y_IGET(sb, inum) iget((sb), (inum)) ++ ++#else ++/* Call local equivalent */ ++#define YAFFS_USE_OWN_IGET ++#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) ++ ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) ++#else ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) ++#endif ++ ++#define yaffs_inode_to_obj(iptr) \ ++ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) ++#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) ++#else ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++#define Y_CLEAR_INODE(i) clear_inode(i) ++#else ++#define Y_CLEAR_INODE(i) end_writeback(i) ++#endif ++ ++ ++#define update_dir_time(dir) do {\ ++ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ ++ } while (0) ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj); ++ ++ ++static void yaffs_gross_lock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); ++ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); ++} ++ ++static void yaffs_gross_unlock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); ++ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++} ++ ++ ++static int yaffs_readpage_nolock(struct file *f, struct page *pg) ++{ ++ /* Lifted from jffs2 */ ++ ++ struct yaffs_obj *obj; ++ unsigned char *pg_buf; ++ int ret; ++ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readpage_nolock at %lld, size %08x", ++ (long long)pos, ++ (unsigned)PAGE_CACHE_SIZE); ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ BUG_ON(!PageLocked(pg)); ++#else ++ if (!PageLocked(pg)) ++ PAGE_BUG(pg); ++#endif ++ ++ pg_buf = kmap(pg); ++ /* FIXME: Can kmap fail? */ ++ ++ yaffs_gross_lock(dev); ++ ++ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (ret >= 0) ++ ret = 0; ++ ++ if (ret) { ++ ClearPageUptodate(pg); ++ SetPageError(pg); ++ } else { ++ SetPageUptodate(pg); ++ ClearPageError(pg); ++ } ++ ++ flush_dcache_page(pg); ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); ++ return ret; ++} ++ ++static int yaffs_readpage_unlock(struct file *f, struct page *pg) ++{ ++ int ret = yaffs_readpage_nolock(f, pg); ++ UnlockPage(pg); ++ return ret; ++} ++ ++static int yaffs_readpage(struct file *f, struct page *pg) ++{ ++ int ret; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); ++ ret = yaffs_readpage_unlock(f, pg); ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); ++ return ret; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++#define YCRED_FSUID() from_kuid(&init_user_ns, current_fsuid()) ++#define YCRED_FSGID() from_kgid(&init_user_ns, current_fsgid()) ++#else ++#define YCRED_FSUID() YCRED(current)->fsuid ++#define YCRED_FSGID() YCRED(current)->fsgid ++ ++static inline uid_t i_uid_read(const struct inode *inode) ++{ ++ return inode->i_uid; ++} ++ ++static inline gid_t i_gid_read(const struct inode *inode) ++{ ++ return inode->i_gid; ++} ++ ++static inline void i_uid_write(struct inode *inode, uid_t uid) ++{ ++ inode->i_uid = uid; ++} ++ ++static inline void i_gid_write(struct inode *inode, gid_t gid) ++{ ++ inode->i_gid = gid; ++} ++#endif ++ ++static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc) ++ lc->dirty = val; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb) ++ sb->s_dirt = val; ++ } ++#endif ++ ++} ++ ++static void yaffs_set_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 1); ++} ++ ++static void yaffs_clear_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 0); ++} ++ ++static int yaffs_check_super_dirty(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc && lc->dirty) ++ return 1; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb && sb->s_dirt) ++ return 1; ++ } ++#endif ++ return 0; ++ ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_writepage(struct page *page, struct writeback_control *wbc) ++#else ++static int yaffs_writepage(struct page *page) ++#endif ++{ ++ struct yaffs_dev *dev; ++ struct address_space *mapping = page->mapping; ++ struct inode *inode; ++ unsigned long end_index; ++ char *buffer; ++ struct yaffs_obj *obj; ++ int n_written = 0; ++ unsigned n_bytes; ++ loff_t i_size; ++ ++ if (!mapping) ++ BUG(); ++ inode = mapping->host; ++ if (!inode) ++ BUG(); ++ i_size = i_size_read(inode); ++ ++ end_index = i_size >> PAGE_CACHE_SHIFT; ++ ++ if (page->index < end_index) ++ n_bytes = PAGE_CACHE_SIZE; ++ else { ++ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); ++ ++ if (page->index > end_index || !n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, inode size = %lld!!", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, ++ inode->i_size); ++ yaffs_trace(YAFFS_TRACE_OS, ++ " -> don't care!!"); ++ ++ zero_user_segment(page, 0, PAGE_CACHE_SIZE); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ return 0; ++ } ++ } ++ ++ if (n_bytes != PAGE_CACHE_SIZE) ++ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); ++ ++ get_page(page); ++ ++ buffer = kmap(page); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, size %08x", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag0: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ n_written = yaffs_wr_file(obj, buffer, ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag1: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ yaffs_gross_unlock(dev); ++ ++ kunmap(page); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ put_page(page); ++ ++ return (n_written == n_bytes) ? 0 : -ENOSPC; ++} ++ ++/* Space holding and freeing is done to ensure we have space available for write_begin/end */ ++/* For now we just assume few parallel writes and check against a small number. */ ++/* Todo: need to do this with a counter to handle parallel reads better */ ++ ++static ssize_t yaffs_hold_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ int n_free_chunks; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ n_free_chunks = yaffs_get_n_free_chunks(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ return (n_free_chunks > 20) ? 1 : 0; ++} ++ ++static void yaffs_release_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_gross_unlock(dev); ++} ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_begin(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ ++ struct page *pg = NULL; ++ pgoff_t index = pos >> PAGE_CACHE_SHIFT; ++ ++ int ret = 0; ++ int space_held = 0; ++ ++ /* Get a page */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ++ pg = grab_cache_page_write_begin(mapping, index, flags); ++#else ++ pg = __grab_cache_page(mapping, index); ++#endif ++ ++ *pagep = pg; ++ if (!pg) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "start yaffs_write_begin index %d(%x) uptodate %d", ++ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); ++ ++ /* Get fs space */ ++ space_held = yaffs_hold_space(filp); ++ ++ if (!space_held) { ++ ret = -ENOSPC; ++ goto out; ++ } ++ ++ /* Update page if required */ ++ ++ if (!Page_Uptodate(pg)) ++ ret = yaffs_readpage_nolock(filp, pg); ++ ++ if (ret) ++ goto out; ++ ++ /* Happy path return */ ++ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); ++ ++ return 0; ++ ++out: ++ yaffs_trace(YAFFS_TRACE_OS, ++ "end yaffs_write_begin fail returning %d", ret); ++ if (space_held) ++ yaffs_release_space(filp); ++ if (pg) { ++ unlock_page(pg); ++ page_cache_release(pg); ++ } ++ return ret; ++} ++ ++#else ++ ++static int yaffs_prepare_write(struct file *f, struct page *pg, ++ unsigned offset, unsigned to) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); ++ ++ if (!Page_Uptodate(pg)) ++ return yaffs_readpage_nolock(f, pg); ++ return 0; ++} ++#endif ++ ++ ++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, ++ loff_t * pos) ++{ ++ struct yaffs_obj *obj; ++ int n_written; ++ loff_t ipos; ++ struct inode *inode; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: hey obj is null!"); ++ return -EINVAL; ++ } ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ inode = f->f_dentry->d_inode; ++ ++ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) ++ ipos = inode->i_size; ++ else ++ ipos = *pos; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", ++ (unsigned)n, (unsigned)n, obj->obj_id, ipos); ++ ++ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: %d(%x) bytes written", ++ (unsigned)n, (unsigned)n); ++ ++ if (n_written > 0) { ++ ipos += n_written; ++ *pos = ipos; ++ if (ipos > inode->i_size) { ++ inode->i_size = ipos; ++ inode->i_blocks = (ipos + 511) >> 9; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write size updated to %lld bytes, %d blocks", ++ ipos, (int)(inode->i_blocks)); ++ } ++ ++ } ++ yaffs_gross_unlock(dev); ++ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; ++} ++ ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_end(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *pg, void *fsdadata) ++{ ++ int ret = 0; ++ void *addr, *kva; ++ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); ++ ++ kva = kmap(pg); ++ addr = kva + offset_into_page; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end addr %p pos %lld n_bytes %d", ++ addr, pos, copied); ++ ++ ret = yaffs_file_write(filp, addr, copied, &pos); ++ ++ if (ret != copied) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end not same size ret %d copied %d", ++ ret, copied); ++ SetPageError(pg); ++ } ++ ++ kunmap(pg); ++ ++ yaffs_release_space(filp); ++ unlock_page(pg); ++ page_cache_release(pg); ++ return ret; ++} ++#else ++ ++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, ++ unsigned to) ++{ ++ void *addr, *kva; ++ ++ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; ++ int n_bytes = to - offset; ++ int n_written; ++ ++ kva = kmap(pg); ++ addr = kva + offset; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write addr %p pos %lld n_bytes %d", ++ addr, pos, n_bytes); ++ ++ n_written = yaffs_file_write(f, addr, n_bytes, &pos); ++ ++ if (n_written != n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write not same size n_written %d n_bytes %d", ++ n_written, n_bytes); ++ SetPageError(pg); ++ } ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write returning %d", ++ n_written == n_bytes ? 0 : n_written); ++ ++ return n_written == n_bytes ? 0 : n_written; ++} ++#endif ++ ++static struct address_space_operations yaffs_file_address_operations = { ++ .readpage = yaffs_readpage, ++ .writepage = yaffs_writepage, ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++ .write_begin = yaffs_write_begin, ++ .write_end = yaffs_write_end, ++#else ++ .prepare_write = yaffs_prepare_write, ++ .commit_write = yaffs_commit_write, ++#endif ++}; ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_file_flush(struct file *file, fl_owner_t id) ++#else ++static int yaffs_file_flush(struct file *file) ++#endif ++{ ++ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); ++ ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_flush object %d (%s)", ++ obj->obj_id, ++ obj->dirty ? "dirty" : "clean"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_file(obj, 1, 0); ++ ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++static int yaffs_sync_object(struct file *file, int datasync) ++#else ++static int yaffs_sync_object(struct file *file, struct dentry *dentry, ++ int datasync) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++ struct dentry *dentry = file->f_path.dentry; ++#endif ++ ++ obj = yaffs_dentry_to_obj(dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_object"); ++ yaffs_gross_lock(dev); ++ yaffs_flush_file(obj, 1, datasync); ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .splice_read = generic_file_splice_read, ++ .splice_write = generic_file_splice_write, ++ .llseek = generic_file_llseek, ++}; ++ ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .sendfile = generic_file_sendfile, ++}; ++ ++#else ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = generic_file_read, ++ .write = generic_file_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ .sendfile = generic_file_sendfile, ++#endif ++}; ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++static void zero_user_segment(struct page *page, unsigned start, unsigned end) ++{ ++ void *kaddr = kmap_atomic(page, KM_USER0); ++ memset(kaddr + start, 0, end - start); ++ kunmap_atomic(kaddr, KM_USER0); ++ flush_dcache_page(page); ++} ++#endif ++ ++ ++static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) ++{ ++#ifdef YAFFS_USE_TRUNCATE_SETSIZE ++ truncate_setsize(inode, newsize); ++ return 0; ++#else ++ truncate_inode_pages(&inode->i_data, newsize); ++ return 0; ++#endif ++ ++} ++ ++ ++static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) ++{ ++#ifdef YAFFS_USE_SETATTR_COPY ++ setattr_copy(inode, attr); ++ return 0; ++#else ++ return inode_setattr(inode, attr); ++#endif ++ ++} ++ ++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_setattr of object %d", ++ yaffs_inode_to_obj(inode)->obj_id); ++#if 0 ++ /* Fail if a requested resize >= 2GB */ ++ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) ++ error = -EINVAL; ++#endif ++ ++ if (error == 0) ++ error = inode_change_ok(inode, attr); ++ if (error == 0) { ++ int result; ++ if (!error) { ++ error = yaffs_vfs_setattr(inode, attr); ++ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_vfs_setsize(inode, attr->ia_size); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ } ++ } ++ dev = yaffs_inode_to_obj(inode)->my_dev; ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "resize to %d(%x)", ++ (int)(attr->ia_size), ++ (int)(attr->ia_size)); ++ } ++ yaffs_gross_lock(dev); ++ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); ++ if (result == YAFFS_OK) { ++ error = 0; ++ } else { ++ error = -EPERM; ++ } ++ yaffs_gross_unlock(dev); ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_set_xattrib(obj, name, value, size, flags); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, ++ void *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_getxattr \"%s\" from object %d", ++ name, obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_get_xattrib(obj, name, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_remove_xattrib(obj, name); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_list_xattrib(obj, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr done returning %d", error); ++ ++ return error; ++} ++ ++ ++static const struct inode_operations yaffs_file_inode_operations = { ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++ ++static int yaffs_readlink(struct dentry *dentry, char __user * buffer, ++ int buflen) ++{ ++ unsigned char *alias; ++ int ret; ++ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) ++ return -ENOMEM; ++ ++ ret = vfs_readlink(dentry, buffer, buflen, alias); ++ kfree(alias); ++ return ret; ++} ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ void *ret; ++#else ++static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int ret ++#endif ++ unsigned char *alias; ++ int ret_int = 0; ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) { ++ ret_int = -ENOMEM; ++ goto out; ++ } ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ nd_set_link(nd, alias); ++ ret = alias; ++out: ++ if (ret_int) ++ ret = ERR_PTR(ret_int); ++ return ret; ++#else ++ ret = vfs_follow_link(nd, alias); ++ kfree(alias); ++out: ++ if (ret_int) ++ ret = ret_int; ++ return ret; ++#endif ++} ++ ++ ++#ifdef YAFFS_HAS_PUT_INODE ++ ++/* For now put inode is just for debugging ++ * Put inode is called when the inode **structure** is put. ++ */ ++static void yaffs_put_inode(struct inode *inode) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_put_inode: ino %d, count %d"), ++ (int)inode->i_ino, atomic_read(&inode->i_count); ++ ++} ++#endif ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) ++{ ++ kfree(alias); ++} ++#endif ++ ++static const struct inode_operations yaffs_symlink_inode_operations = { ++ .readlink = yaffs_readlink, ++ .follow_link = yaffs_follow_link, ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ .put_link = yaffs_put_link, ++#endif ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++#ifdef YAFFS_USE_OWN_IGET ++ ++static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) ++{ ++ struct inode *inode; ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); ++ ++ inode = iget_locked(sb, ino); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ yaffs_gross_unlock(dev); ++ ++ unlock_new_inode(inode); ++ return inode; ++} ++ ++#else ++ ++static void yaffs_read_inode(struct inode *inode) ++{ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_inode for %d", (int)inode->i_ino); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++} ++ ++#endif ++ ++ ++ ++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, ++ struct yaffs_obj *obj) ++{ ++ struct inode *inode; ++ ++ if (!sb) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL super_block!!"); ++ return NULL; ++ ++ } ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL object!!"); ++ return NULL; ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for object %d", obj->obj_id); ++ ++ inode = Y_IGET(sb, obj->obj_id); ++ if (IS_ERR(inode)) ++ return NULL; ++ ++ /* NB Side effect: iget calls back to yaffs_read_inode(). */ ++ /* iget also increments the inode's i_count */ ++ /* NB You can't be holding gross_lock or deadlock will happen! */ ++ ++ return inode; ++} ++ ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++#define YCRED(x) x ++#else ++#define YCRED(x) (x->cred) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t rdev) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t rdev) ++#else ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ int rdev) ++#endif ++{ ++ struct inode *inode; ++ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_dev *dev; ++ ++ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); ++ ++ int error = -ENOSPC; ++ uid_t uid = YCRED_FSUID(); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); ++ ++ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) ++ mode |= S_ISGID; ++ ++ if (parent) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: parent object %d type %d", ++ parent->obj_id, parent->variant_type); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: could not get parent object"); ++ return -EPERM; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: making oject for %s, mode %x dev %x", ++ dentry->d_name.name, mode, rdev); ++ ++ dev = parent->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ switch (mode & S_IFMT) { ++ default: ++ /* Special (socket, fifo, device...) */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, old_encode_dev(rdev)); ++#else ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, rdev); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); ++ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, ++ gid); ++ break; ++ case S_IFDIR: /* directory */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); ++ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, ++ uid, gid); ++ break; ++ case S_IFLNK: /* symlink */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); ++ obj = NULL; /* Do we ever get here? */ ++ break; ++ } ++ ++ /* Can not call yaffs_get_inode() with gross lock held */ ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod created object %d count = %d", ++ obj->obj_id, atomic_read(&inode->i_count)); ++ error = 0; ++ yaffs_fill_inode_from_obj(dir, parent); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); ++ error = -ENOMEM; ++ } ++ ++ return error; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++#else ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ int ret_val; ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); ++ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); ++ return ret_val; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool dummy) ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ struct nameidata *n) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *n) ++#else ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); ++ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ unsigned int dummy) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *n) ++#else ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ ++ ++ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", ++ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); ++ ++ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); ++ ++ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ ++ ++ /* Can't hold gross lock when calling yaffs_get_inode() */ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_lookup found %d", obj->obj_id); ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); ++ ++ } ++ ++/* added NCB for 2.5/6 compatability - forces add even if inode is ++ * NULL which creates dentry hash */ ++ d_add(dentry, inode); ++ ++ return NULL; ++} ++ ++/* ++ * Create a link... ++ */ ++static int yaffs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ struct inode *inode = old_dentry->d_inode; ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *link = NULL; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ ++ link = ++ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ obj); ++ ++ if (link) { ++ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); ++ d_instantiate(dentry, old_dentry->d_inode); ++ atomic_inc(&old_dentry->d_inode->i_count); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_link link count %d i_count %d", ++ old_dentry->d_inode->i_nlink, ++ atomic_read(&old_dentry->d_inode->i_count)); ++ } ++ ++ yaffs_gross_unlock(dev); ++ ++ if (link) { ++ update_dir_time(dir); ++ return 0; ++ } ++ ++ return -EPERM; ++} ++ ++static int yaffs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ uid_t uid = YCRED_FSUID(); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); ++ ++ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ return -ENAMETOOLONG; ++ ++ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > ++ YAFFS_MAX_ALIAS_LENGTH) ++ return -ENAMETOOLONG; ++ ++ dev = yaffs_inode_to_obj(dir)->my_dev; ++ yaffs_gross_lock(dev); ++ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ S_IFLNK | S_IRWXUGO, uid, gid, symname); ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ struct inode *inode; ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); ++ return 0; ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); ++ } ++ ++ return -ENOMEM; ++} ++ ++/* ++ * The VFS layer already does all the dentry stuff for rename. ++ * ++ * NB: POSIX says you can rename an object over an old object of the same name ++ */ ++static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct yaffs_dev *dev; ++ int ret_val = YAFFS_FAIL; ++ struct yaffs_obj *target; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); ++ dev = yaffs_inode_to_obj(old_dir)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ /* Check if the target is an existing directory that is not empty. */ ++ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ ++ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && ++ !list_empty(&target->variant.dir_variant.children)) { ++ ++ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); ++ ++ ret_val = YAFFS_FAIL; ++ } else { ++ /* Now does unlinking internally using shadowing mechanism */ ++ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); ++ ++ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), ++ old_dentry->d_name.name, ++ yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ } ++ yaffs_gross_unlock(dev); ++ ++ if (ret_val == YAFFS_OK) { ++ if (target) ++ inode_dec_link_count(new_dentry->d_inode); ++ ++ update_dir_time(old_dir); ++ if (old_dir != new_dir) ++ update_dir_time(new_dir); ++ return 0; ++ } else { ++ return -ENOTEMPTY; ++ } ++} ++ ++ ++ ++ ++static int yaffs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int ret_val; ++ ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", ++ (int)(dir->i_ino), dentry->d_name.name); ++ obj = yaffs_inode_to_obj(dir); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ ret_val = yaffs_unlinker(obj, dentry->d_name.name); ++ ++ if (ret_val == YAFFS_OK) { ++ inode_dec_link_count(dentry->d_inode); ++ dir->i_version++; ++ yaffs_gross_unlock(dev); ++ update_dir_time(dir); ++ return 0; ++ } ++ yaffs_gross_unlock(dev); ++ return -ENOTEMPTY; ++} ++ ++ ++ ++static const struct inode_operations yaffs_dir_inode_operations = { ++ .create = yaffs_create, ++ .lookup = yaffs_lookup, ++ .link = yaffs_link, ++ .unlink = yaffs_unlink, ++ .symlink = yaffs_symlink, ++ .mkdir = yaffs_mkdir, ++ .rmdir = yaffs_unlink, ++ .mknod = yaffs_mknod, ++ .rename = yaffs_rename, ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++/*-----------------------------------------------------------------*/ ++/* Directory search context allows us to unlock access to yaffs during ++ * filldir without causing problems with the directory being modified. ++ * This is similar to the tried and tested mechanism used in yaffs direct. ++ * ++ * A search context iterates along a doubly linked list of siblings in the ++ * directory. If the iterating object is deleted then this would corrupt ++ * the list iteration, likely causing a crash. The search context avoids ++ * this by using the remove_obj_fn to move the search context to the ++ * next object before the object is deleted. ++ * ++ * Many readdirs (and thus seach conexts) may be alive simulateously so ++ * each struct yaffs_dev has a list of these. ++ * ++ * A seach context lives for the duration of a readdir. ++ * ++ * All these functions must be called while yaffs is locked. ++ */ ++ ++struct yaffs_search_context { ++ struct yaffs_dev *dev; ++ struct yaffs_obj *dir_obj; ++ struct yaffs_obj *next_return; ++ struct list_head others; ++}; ++ ++/* ++ * yaffs_new_search() creates a new search context, initialises it and ++ * adds it to the device's search context list. ++ * ++ * Called at start of readdir. ++ */ ++static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) ++{ ++ struct yaffs_dev *dev = dir->my_dev; ++ struct yaffs_search_context *sc = ++ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); ++ if (sc) { ++ sc->dir_obj = dir; ++ sc->dev = dev; ++ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else ++ sc->next_return = ++ list_entry(dir->variant.dir_variant.children.next, ++ struct yaffs_obj, siblings); ++ INIT_LIST_HEAD(&sc->others); ++ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); ++ } ++ return sc; ++} ++ ++/* ++ * yaffs_search_end() disposes of a search context and cleans up. ++ */ ++static void yaffs_search_end(struct yaffs_search_context *sc) ++{ ++ if (sc) { ++ list_del(&sc->others); ++ kfree(sc); ++ } ++} ++ ++/* ++ * yaffs_search_advance() moves a search context to the next object. ++ * Called when the search iterates or when an object removal causes ++ * the search context to be moved to the next object. ++ */ ++static void yaffs_search_advance(struct yaffs_search_context *sc) ++{ ++ if (!sc) ++ return; ++ ++ if (sc->next_return == NULL || ++ list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else { ++ struct list_head *next = sc->next_return->siblings.next; ++ ++ if (next == &sc->dir_obj->variant.dir_variant.children) ++ sc->next_return = NULL; /* end of list */ ++ else ++ sc->next_return = ++ list_entry(next, struct yaffs_obj, siblings); ++ } ++} ++ ++/* ++ * yaffs_remove_obj_callback() is called when an object is unlinked. ++ * We check open search contexts and advance any which are currently ++ * on the object being iterated. ++ */ ++static void yaffs_remove_obj_callback(struct yaffs_obj *obj) ++{ ++ ++ struct list_head *i; ++ struct yaffs_search_context *sc; ++ struct list_head *search_contexts = ++ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); ++ ++ /* Iterate through the directory search contexts. ++ * If any are currently on the object being removed, then advance ++ * the search context to the next object to prevent a hanging pointer. ++ */ ++ list_for_each(i, search_contexts) { ++ sc = list_entry(i, struct yaffs_search_context, others); ++ if (sc->next_return == obj) ++ yaffs_search_advance(sc); ++ } ++ ++} ++ ++ ++/*-----------------------------------------------------------------*/ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++static int yaffs_readdir(struct file *file, struct dir_context *ctx) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ struct inode *inode = file->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(file->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ offset = ctx->pos; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: starting at %d", (int)offset); ++ ++ if (offset == 0) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry . ino %d", ++ (int)inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (!dir_emit_dot(file, ctx)) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ ctx->pos++; ++ } ++ if (offset == 1) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry .. ino %d", ++ (int)file->f_dentry->d_parent->d_inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (!dir_emit_dotdot(file, ctx)) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ ctx->pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ if (file->f_version != inode->i_version) { ++ offset = 2; ++ ctx->pos = offset; ++ file->f_version = inode->i_version; ++ } ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= offset) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!dir_emit(ctx, name, strlen(name), ++ this_inode, this_type) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ offset++; ++ ctx->pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++#else ++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ struct inode *inode = f->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ offset = f->f_pos; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: starting at %d", (int)offset); ++ ++ if (offset == 0) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry . ino %d", ++ (int)inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ if (offset == 1) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry .. ino %d", ++ (int)f->f_dentry->d_parent->d_inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, "..", 2, offset, ++ f->f_dentry->d_parent->d_inode->i_ino, ++ DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ if (f->f_version != inode->i_version) { ++ offset = 2; ++ f->f_pos = offset; ++ f->f_version = inode->i_version; ++ } ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= offset) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (filldir(dirent, ++ name, ++ strlen(name), ++ offset, this_inode, this_type) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ offset++; ++ f->f_pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++#endif ++ ++static const struct file_operations yaffs_dir_operations = { ++ .read = generic_read_dir, ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++ .iterate = yaffs_readdir, ++#else ++ .readdir = yaffs_readdir, ++#endif ++ .fsync = yaffs_sync_object, ++ .llseek = generic_file_llseek, ++}; ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj) ++{ ++ if (inode && obj) { ++ ++ /* Check mode against the variant type and attempt to repair if broken. */ ++ u32 mode = obj->yst_mode; ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (!S_ISREG(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFREG; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ if (!S_ISLNK(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFLNK; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!S_ISDIR(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFDIR; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ default: ++ /* TODO? */ ++ break; ++ } ++ ++ inode->i_flags |= S_NOATIME; ++ ++ inode->i_ino = obj->obj_id; ++ inode->i_mode = obj->yst_mode; ++ i_uid_write(inode, obj->yst_uid); ++ i_gid_write(inode, obj->yst_gid); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++ inode->i_blksize = inode->i_sb->s_blocksize; ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++ inode->i_rdev = old_decode_dev(obj->yst_rdev); ++ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); ++ inode->i_atime.tv_nsec = 0; ++ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; ++ inode->i_mtime.tv_nsec = 0; ++ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; ++ inode->i_ctime.tv_nsec = 0; ++#else ++ inode->i_rdev = obj->yst_rdev; ++ inode->i_atime = obj->yst_atime; ++ inode->i_mtime = obj->yst_mtime; ++ inode->i_ctime = obj->yst_ctime; ++#endif ++ inode->i_size = yaffs_get_obj_length(obj); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ ++ set_nlink(inode, yaffs_get_obj_link_count(obj)); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", ++ inode->i_mode, i_uid_read(inode), i_gid_read(inode), ++ inode->i_size, atomic_read(&inode->i_count)); ++ ++ switch (obj->yst_mode & S_IFMT) { ++ default: /* fifo, device or socket */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ init_special_inode(inode, obj->yst_mode, ++ old_decode_dev(obj->yst_rdev)); ++#else ++ init_special_inode(inode, obj->yst_mode, ++ (dev_t) (obj->yst_rdev)); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ inode->i_op = &yaffs_file_inode_operations; ++ inode->i_fop = &yaffs_file_operations; ++ inode->i_mapping->a_ops = ++ &yaffs_file_address_operations; ++ break; ++ case S_IFDIR: /* directory */ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ break; ++ case S_IFLNK: /* symlink */ ++ inode->i_op = &yaffs_symlink_inode_operations; ++ break; ++ } ++ ++ yaffs_inode_to_obj_lv(inode) = obj; ++ ++ obj->my_inode = inode; ++ ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode invalid parameters"); ++ } ++ ++} ++ ++ ++ ++/* ++ * yaffs background thread functions . ++ * yaffs_bg_thread_fn() the thread function ++ * yaffs_bg_start() launches the background thread. ++ * yaffs_bg_stop() cleans up the background thread. ++ * ++ * NB: ++ * The thread should only run after the yaffs is initialised ++ * The thread should be stopped before yaffs is unmounted. ++ * The thread should not do any writing while the fs is in read only. ++ */ ++ ++static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) ++{ ++ unsigned erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned scattered = 0; /* Free chunks not in an erased block */ ++ ++ if (erased_chunks < dev->n_free_chunks) ++ scattered = (dev->n_free_chunks - erased_chunks); ++ ++ if (!context->bg_running) ++ return 0; ++ else if (scattered < (dev->param.chunks_per_block * 2)) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 2) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 4) ++ return 1; ++ else ++ return 2; ++} ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++ ++void yaffs_background_waker(unsigned long data) ++{ ++ wake_up_process((struct task_struct *)data); ++} ++ ++static int yaffs_bg_thread_fn(void *data) ++{ ++ struct yaffs_dev *dev = (struct yaffs_dev *)data; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned long now = jiffies; ++ unsigned long next_dir_update = now; ++ unsigned long next_gc = now; ++ unsigned long expires; ++ unsigned int urgency; ++ ++ int gc_result; ++ struct timer_list timer; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "yaffs_background starting for dev %p", (void *)dev); ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ set_freezable(); ++#endif ++ while (context->bg_running) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); ++ ++ if (kthread_should_stop()) ++ break; ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ if (try_to_freeze()) ++ continue; ++#endif ++ yaffs_gross_lock(dev); ++ ++ now = jiffies; ++ ++ if (time_after(now, next_dir_update) && yaffs_bg_enable) { ++ yaffs_update_dirty_dirs(dev); ++ next_dir_update = now + HZ; ++ } ++ ++ if (time_after(now, next_gc) && yaffs_bg_enable) { ++ if (!dev->is_checkpointed) { ++ urgency = yaffs_bg_gc_urgency(dev); ++ gc_result = yaffs_bg_gc(dev, urgency); ++ if (urgency > 1) ++ next_gc = now + HZ / 20 + 1; ++ else if (urgency > 0) ++ next_gc = now + HZ / 10 + 1; ++ else ++ next_gc = now + HZ * 2; ++ } else { ++ /* ++ * gc not running so set to next_dir_update ++ * to cut down on wake ups ++ */ ++ next_gc = next_dir_update; ++ } ++ } ++ yaffs_gross_unlock(dev); ++#if 1 ++ expires = next_dir_update; ++ if (time_before(next_gc, expires)) ++ expires = next_gc; ++ if (time_before(expires, now)) ++ expires = now + HZ; ++ ++ Y_INIT_TIMER(&timer); ++ timer.expires = expires + 1; ++ timer.data = (unsigned long)current; ++ timer.function = yaffs_background_waker; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_timer(&timer); ++ schedule(); ++ del_timer_sync(&timer); ++#else ++ msleep(10); ++#endif ++ } ++ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ int retval = 0; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ ++ if (dev->read_only) ++ return -1; ++ ++ context->bg_running = 1; ++ ++ context->bg_thread = kthread_run(yaffs_bg_thread_fn, ++ (void *)dev, "yaffs-bg-%d", ++ context->mount_id); ++ ++ if (IS_ERR(context->bg_thread)) { ++ retval = PTR_ERR(context->bg_thread); ++ context->bg_thread = NULL; ++ context->bg_running = 0; ++ } ++ return retval; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); ++ ++ ctxt->bg_running = 0; ++ ++ if (ctxt->bg_thread) { ++ kthread_stop(ctxt->bg_thread); ++ ctxt->bg_thread = NULL; ++ } ++} ++#else ++static int yaffs_bg_thread_fn(void *data) ++{ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ return 0; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++} ++#endif ++ ++ ++static void yaffs_flush_inodes(struct super_block *sb) ++{ ++ struct inode *iptr; ++ struct yaffs_obj *obj; ++ ++ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { ++ obj = yaffs_inode_to_obj(iptr); ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "flushing obj %d", ++ obj->obj_id); ++ yaffs_flush_file(obj, 1, 0); ++ } ++ } ++} ++ ++static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ if (!dev) ++ return; ++ ++ yaffs_flush_inodes(sb); ++ yaffs_update_dirty_dirs(dev); ++ yaffs_flush_whole_cache(dev); ++ if (do_checkpoint) ++ yaffs_checkpoint_save(dev); ++} ++ ++static LIST_HEAD(yaffs_context_list); ++struct mutex yaffs_context_lock; ++ ++static void yaffs_put_super(struct super_block *sb) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super"); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "Shutting down yaffs background thread"); ++ yaffs_bg_stop(dev); ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "yaffs background thread shut down"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_super(sb, 1); ++ ++ yaffs_deinitialise(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ mutex_lock(&yaffs_context_lock); ++ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); ++ mutex_unlock(&yaffs_context_lock); ++ ++ if (yaffs_dev_to_lc(dev)->spare_buffer) { ++ kfree(yaffs_dev_to_lc(dev)->spare_buffer); ++ yaffs_dev_to_lc(dev)->spare_buffer = NULL; ++ } ++ ++ kfree(dev); ++ ++ yaffs_put_mtd_device(mtd); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super done"); ++} ++ ++ ++static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) ++{ ++ return yaffs_gc_control; ++} ++ ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ ++static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, ++ uint32_t generation) ++{ ++ return Y_IGET(sb, ino); ++} ++ ++static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_parent(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++struct dentry *yaffs2_get_parent(struct dentry *dentry) ++{ ++ ++ struct super_block *sb = dentry->d_inode->i_sb; ++ struct dentry *parent = ERR_PTR(-ENOENT); ++ struct inode *inode; ++ unsigned long parent_ino; ++ struct yaffs_obj *d_obj; ++ struct yaffs_obj *parent_obj; ++ ++ d_obj = yaffs_inode_to_obj(dentry->d_inode); ++ ++ if (d_obj) { ++ parent_obj = d_obj->parent; ++ if (parent_obj) { ++ parent_ino = yaffs_get_obj_inode(parent_obj); ++ inode = Y_IGET(sb, parent_ino); ++ ++ if (IS_ERR(inode)) { ++ parent = ERR_CAST(inode); ++ } else { ++ parent = d_obtain_alias(inode); ++ if (!IS_ERR(parent)) { ++ parent = ERR_PTR(-ENOMEM); ++ iput(inode); ++ } ++ } ++ } ++ } ++ ++ return parent; ++} ++ ++/* Just declare a zero structure as a NULL value implies ++ * using the default functions of exportfs. ++ */ ++ ++static struct export_operations yaffs_export_ops = { ++ .fh_to_dentry = yaffs2_fh_to_dentry, ++ .fh_to_parent = yaffs2_fh_to_parent, ++ .get_parent = yaffs2_get_parent, ++}; ++ ++#endif ++ ++static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) ++{ ++ /* Clear the association between the inode and ++ * the struct yaffs_obj. ++ */ ++ obj->my_inode = NULL; ++ yaffs_inode_to_obj_lv(inode) = NULL; ++ ++ /* If the object freeing was deferred, then the real ++ * free happens now. ++ * This should fix the inode inconsistency problem. ++ */ ++ yaffs_handle_defered_free(obj); ++} ++ ++#ifdef YAFFS_HAS_EVICT_INODE ++/* yaffs_evict_inode combines into one operation what was previously done in ++ * yaffs_clear_inode() and yaffs_delete_inode() ++ * ++ */ ++static void yaffs_evict_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ int deleteme = 0; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_evict_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (!inode->i_nlink && !is_bad_inode(inode)) ++ deleteme = 1; ++ truncate_inode_pages(&inode->i_data, 0); ++ Y_CLEAR_INODE(inode); ++ ++ if (deleteme && obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++} ++#else ++ ++/* clear is called to tell the fs to release any per-inode data it holds. ++ * The object might still exist on disk and is just being thrown out of the cache ++ * or else the object has actually been deleted and we're being called via ++ * the chain ++ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() ++ */ ++ ++static void yaffs_clear_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_clear_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++ ++} ++ ++/* delete is called when the link count is zero and the inode ++ * is put (ie. nobody wants to know about it anymore, time to ++ * delete the file). ++ * NB Must call clear_inode() ++ */ ++static void yaffs_delete_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_delete_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++ truncate_inode_pages(&inode->i_data, 0); ++#endif ++ clear_inode(inode); ++} ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ struct super_block *sb = dentry->d_sb; ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#else ++static int yaffs_statfs(struct super_block *sb, struct statfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#endif ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); ++ ++ yaffs_gross_lock(dev); ++ ++ buf->f_type = YAFFS_MAGIC; ++ buf->f_bsize = sb->s_blocksize; ++ buf->f_namelen = 255; ++ ++ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { ++ /* Do this if chunk size is not a power of 2 */ ++ ++ uint64_t bytes_in_dev; ++ uint64_t bytes_free; ++ ++ bytes_in_dev = ++ ((uint64_t) ++ ((dev->param.end_block - dev->param.start_block + ++ 1))) * ((uint64_t) (dev->param.chunks_per_block * ++ dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ ++ buf->f_blocks = bytes_in_dev; ++ ++ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * ++ ((uint64_t) (dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_free, sb->s_blocksize); ++ ++ buf->f_bfree = bytes_free; ++ ++ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { ++ ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ } else { ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ } ++ ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_bavail = buf->f_bfree; ++ ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++ ++static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) ++{ ++ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); ++ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); ++ int do_checkpoint; ++ int dirty = yaffs_check_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", ++ gc_urgent, ++ dirty ? "dirty" : "clean", ++ request_checkpoint ? "checkpoint requested" : "no checkpoint", ++ oneshot_checkpoint ? " one-shot" : ""); ++ ++ yaffs_gross_lock(dev); ++ do_checkpoint = ((request_checkpoint && !gc_urgent) || ++ oneshot_checkpoint) && !dev->is_checkpointed; ++ ++ if (dirty || do_checkpoint) { ++ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); ++ yaffs_clear_super_dirty(dev); ++ if (oneshot_checkpoint) ++ yaffs_auto_checkpoint &= ~4; ++ } ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#ifdef YAFFS_HAS_WRITE_SUPER ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static void yaffs_write_super(struct super_block *sb) ++#else ++static int yaffs_write_super(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_write_super %s", ++ request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) ++ return 0; ++#endif ++} ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_sync_fs(struct super_block *sb, int wait) ++#else ++static int yaffs_sync_fs(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++ return 0; ++} ++ ++ ++ ++static const struct super_operations yaffs_super_ops = { ++ .statfs = yaffs_statfs, ++ ++#ifndef YAFFS_USE_OWN_IGET ++ .read_inode = yaffs_read_inode, ++#endif ++#ifdef YAFFS_HAS_PUT_INODE ++ .put_inode = yaffs_put_inode, ++#endif ++ .put_super = yaffs_put_super, ++#ifdef YAFFS_HAS_EVICT_INODE ++ .evict_inode = yaffs_evict_inode, ++#else ++ .delete_inode = yaffs_delete_inode, ++ .clear_inode = yaffs_clear_inode, ++#endif ++ .sync_fs = yaffs_sync_fs, ++#ifdef YAFFS_HAS_WRITE_SUPER ++ .write_super = yaffs_write_super, ++#endif ++}; ++ ++struct yaffs_options { ++ int inband_tags; ++ int skip_checkpoint_read; ++ int skip_checkpoint_write; ++ int no_cache; ++ int tags_ecc_on; ++ int tags_ecc_overridden; ++ int lazy_loading_enabled; ++ int lazy_loading_overridden; ++ int empty_lost_and_found; ++ int empty_lost_and_found_overridden; ++ int disable_summary; ++}; ++ ++#define MAX_OPT_LEN 30 ++static int yaffs_parse_options(struct yaffs_options *options, ++ const char *options_str) ++{ ++ char cur_opt[MAX_OPT_LEN + 1]; ++ int p; ++ int error = 0; ++ ++ /* Parse through the options which is a comma seperated list */ ++ ++ while (options_str && *options_str && !error) { ++ memset(cur_opt, 0, MAX_OPT_LEN + 1); ++ p = 0; ++ ++ while (*options_str == ',') ++ options_str++; ++ ++ while (*options_str && *options_str != ',') { ++ if (p < MAX_OPT_LEN) { ++ cur_opt[p] = *options_str; ++ p++; ++ } ++ options_str++; ++ } ++ ++ if (!strcmp(cur_opt, "inband-tags")) { ++ options->inband_tags = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-off")) { ++ options->tags_ecc_on = 0; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-on")) { ++ options->tags_ecc_on = 1; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-off")) { ++ options->lazy_loading_enabled = 0; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-on")) { ++ options->lazy_loading_enabled = 1; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "disable-summary")) { ++ options->disable_summary = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { ++ options->empty_lost_and_found = 0; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { ++ options->empty_lost_and_found = 1; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "no-cache")) { ++ options->no_cache = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { ++ options->skip_checkpoint_read = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { ++ options->skip_checkpoint_write = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint")) { ++ options->skip_checkpoint_read = 1; ++ options->skip_checkpoint_write = 1; ++ } else { ++ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", ++ cur_opt); ++ error = 1; ++ } ++ } ++ ++ return error; ++} ++ ++ ++static struct dentry *yaffs_make_root(struct inode *inode) ++{ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++ struct dentry *root = d_alloc_root(inode); ++ ++ if (!root) ++ iput(inode); ++ ++ return root; ++#else ++ return d_make_root(inode); ++#endif ++} ++ ++ ++ ++ ++static struct super_block *yaffs_internal_read_super(int yaffs_version, ++ struct super_block *sb, ++ void *data, int silent) ++{ ++ int n_blocks; ++ struct inode *inode = NULL; ++ struct dentry *root; ++ struct yaffs_dev *dev = 0; ++ char devname_buf[BDEVNAME_SIZE + 1]; ++ struct mtd_info *mtd; ++ int err; ++ char *data_str = (char *)data; ++ struct yaffs_linux_context *context = NULL; ++ struct yaffs_param *param; ++ ++ int read_only = 0; ++ int inband_tags = 0; ++ ++ struct yaffs_options options; ++ ++ unsigned mount_id; ++ int found; ++ struct yaffs_linux_context *context_iterator; ++ struct list_head *l; ++ ++ if (!sb) { ++ printk(KERN_INFO "yaffs: sb is NULL\n"); ++ return NULL; ++ } ++ ++ sb->s_magic = YAFFS_MAGIC; ++ sb->s_op = &yaffs_super_ops; ++ sb->s_flags |= MS_NOATIME; ++ ++ read_only = ((sb->s_flags & MS_RDONLY) != 0); ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ sb->s_export_op = &yaffs_export_ops; ++#endif ++ ++ if (!sb->s_dev) ++ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); ++ else if (!yaffs_devname(sb, devname_buf)) ++ printk(KERN_INFO "yaffs: devname is NULL\n"); ++ else ++ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", ++ sb->s_dev, ++ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); ++ ++ if (!data_str) ++ data_str = ""; ++ ++ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); ++ ++ memset(&options, 0, sizeof(options)); ++ ++ if (yaffs_parse_options(&options, data_str)) { ++ /* Option parsing failed */ ++ return NULL; ++ } ++ ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: Using yaffs%d", yaffs_version); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", ++ MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ yaffs_devname(sb, devname_buf)); ++ ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (IS_ERR(mtd)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device %u either not valid or unavailable", ++ MINOR(sb->s_dev)); ++ return NULL; ++ } ++ ++ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); ++ yaffs_version = 2; ++ } ++ ++ /* Added NCB 26/5/2006 for completeness */ ++ if (yaffs_version == 2 && !options.inband_tags ++ && WRITE_SIZE(mtd) == 512) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); ++ yaffs_version = 1; ++ } ++ ++ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || ++ options.inband_tags) ++ inband_tags = 1; ++ ++ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) ++ return NULL; ++ ++ /* OK, so if we got here, we have an MTD that's NAND and looks ++ * like it has the right capabilities ++ * Set the struct yaffs_dev up for mtd ++ */ ++ ++ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { ++ read_only = 1; ++ printk(KERN_INFO ++ "yaffs: mtd is read only, setting superblock read only\n" ++ ); ++ sb->s_flags |= MS_RDONLY; ++ } ++ ++ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); ++ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); ++ ++ if (!dev || !context) { ++ kfree(dev); ++ kfree(context); ++ dev = NULL; ++ context = NULL; ++ ++ /* Deep shit could not allocate device structure */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." ++ ); ++ return NULL; ++ } ++ memset(dev, 0, sizeof(struct yaffs_dev)); ++ param = &(dev->param); ++ ++ memset(context, 0, sizeof(struct yaffs_linux_context)); ++ dev->os_context = context; ++ INIT_LIST_HEAD(&(context->context_list)); ++ context->dev = dev; ++ context->super = sb; ++ ++ dev->read_only = read_only; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ sb->s_fs_info = dev; ++#else ++ sb->u.generic_sbp = dev; ++#endif ++ ++ ++ dev->driver_context = mtd; ++ param->name = mtd->name; ++ ++ /* Set up the memory size parameters.... */ ++ ++ ++ param->n_reserved_blocks = 5; ++ param->n_caches = (options.no_cache) ? 0 : 10; ++ param->inband_tags = inband_tags; ++ ++ param->enable_xattr = 1; ++ if (options.lazy_loading_overridden) ++ param->disable_lazy_load = !options.lazy_loading_enabled; ++ ++ param->defered_dir_update = 1; ++ ++ if (options.tags_ecc_overridden) ++ param->no_tags_ecc = !options.tags_ecc_on; ++ ++ param->empty_lost_n_found = 1; ++ param->refresh_period = 500; ++ param->disable_summary = options.disable_summary; ++ ++ ++#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING ++ param->disable_bad_block_marking = 1; ++#endif ++ if (options.empty_lost_and_found_overridden) ++ param->empty_lost_n_found = options.empty_lost_and_found; ++ ++ /* ... and the functions. */ ++ if (yaffs_version == 2) { ++ param->is_yaffs2 = 1; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++ param->total_bytes_per_chunk = mtd->writesize; ++ param->chunks_per_block = mtd->erasesize / mtd->writesize; ++#else ++ param->total_bytes_per_chunk = mtd->oobblock; ++ param->chunks_per_block = mtd->erasesize / mtd->oobblock; ++#endif ++ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ } else { ++ param->is_yaffs2 = 0; ++ n_blocks = YCALCBLOCKS(mtd->size, ++ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); ++ ++ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; ++ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; ++ } ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ ++ yaffs_mtd_drv_install(dev); ++ ++ param->sb_dirty_fn = yaffs_set_super_dirty; ++ param->gc_control_fn = yaffs_gc_control_callback; ++ ++ yaffs_dev_to_lc(dev)->super = sb; ++ ++ param->use_nand_ecc = 1; ++ ++ param->skip_checkpt_rd = options.skip_checkpoint_read; ++ param->skip_checkpt_wr = options.skip_checkpoint_write; ++ ++ mutex_lock(&yaffs_context_lock); ++ /* Get a mount id */ ++ found = 0; ++ for (mount_id = 0; !found; mount_id++) { ++ found = 1; ++ list_for_each(l, &yaffs_context_list) { ++ context_iterator = ++ list_entry(l, struct yaffs_linux_context, ++ context_list); ++ if (context_iterator->mount_id == mount_id) ++ found = 0; ++ } ++ } ++ context->mount_id = mount_id; ++ ++ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), ++ &yaffs_context_list); ++ mutex_unlock(&yaffs_context_lock); ++ ++ /* Directory search handling... */ ++ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); ++ param->remove_obj_fn = yaffs_remove_obj_callback; ++ ++ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ ++ yaffs_gross_lock(dev); ++ ++ err = yaffs_guts_initialise(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: guts initialised %s", ++ (err == YAFFS_OK) ? "OK" : "FAILED"); ++ ++ if (err == YAFFS_OK) ++ yaffs_bg_start(dev); ++ ++ if (!context->bg_thread) ++ param->defered_dir_update = 0; ++ ++ sb->s_maxbytes = yaffs_max_file_size(dev); ++ ++ /* Release lock before yaffs_get_inode() */ ++ yaffs_gross_unlock(dev); ++ ++ /* Create root inode */ ++ if (err == YAFFS_OK) ++ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); ++ ++ if (!inode) ++ return NULL; ++ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); ++ ++ root = yaffs_make_root(inode); ++ ++ if (!root) ++ return NULL; ++ ++ sb->s_root = root; ++ if(!dev->is_checkpointed) ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); ++ return sb; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data, struct vfsmount *mnt) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs_mount, ++#else ++ .get_sb = yaffs_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs_read_super(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, void *data, ++ struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs2_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs2", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs2_mount, ++#else ++ .get_sb = yaffs2_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs2_read_super(struct super_block *sb, ++ void *data, int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) ++static struct proc_dir_entry *my_proc_entry; ++ ++static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) ++{ ++ struct yaffs_param *param = &dev->param; ++ int bs[10]; ++ ++ yaffs_count_blocks_by_state(dev,bs); ++ ++ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); ++ buf += sprintf(buf, "end_block............ %d\n", param->end_block); ++ buf += sprintf(buf, "total_bytes_per_chunk %d\n", ++ param->total_bytes_per_chunk); ++ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); ++ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); ++ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); ++ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); ++ buf += sprintf(buf, "empty_lost_n_found... %d\n", ++ param->empty_lost_n_found); ++ buf += sprintf(buf, "disable_lazy_load.... %d\n", ++ param->disable_lazy_load); ++ buf += sprintf(buf, "disable_bad_block_mrk %d\n", ++ param->disable_bad_block_marking); ++ buf += sprintf(buf, "refresh_period....... %d\n", ++ param->refresh_period); ++ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); ++ buf += sprintf(buf, "n_reserved_blocks.... %d\n", ++ param->n_reserved_blocks); ++ buf += sprintf(buf, "always_check_erased.. %d\n", ++ param->always_check_erased); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "block count by state\n"); ++ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", ++ bs[0], bs[1], bs[2], bs[3], bs[4]); ++ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", ++ bs[5], bs[6], bs[7], bs[8], bs[9]); ++ ++ return buf; ++} ++ ++static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) ++{ ++ buf += sprintf(buf, "max file size....... %lld\n", ++ (long long) yaffs_max_file_size(dev)); ++ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", ++ dev->data_bytes_per_chunk); ++ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); ++ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); ++ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); ++ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", ++ dev->blocks_in_checkpt); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); ++ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); ++ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); ++ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); ++ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); ++ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); ++ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); ++ buf += sprintf(buf, "passive_gc_count..... %u\n", ++ dev->passive_gc_count); ++ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", ++ dev->oldest_dirty_gc_count); ++ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); ++ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); ++ buf += sprintf(buf, "n_retried_writes..... %u\n", ++ dev->n_retried_writes); ++ buf += sprintf(buf, "n_retired_blocks..... %u\n", ++ dev->n_retired_blocks); ++ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); ++ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); ++ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", ++ dev->n_tags_ecc_fixed); ++ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", ++ dev->n_tags_ecc_unfixed); ++ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); ++ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); ++ buf += sprintf(buf, "n_unlinked_files..... %u\n", ++ dev->n_unlinked_files); ++ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); ++ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); ++ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); ++ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); ++ ++ return buf; ++} ++ ++static int yaffs_proc_read(char *page, ++ char **start, ++ off_t offset, int count, int *eof, void *data) ++{ ++ struct list_head *item; ++ char *buf = page; ++ int step = offset; ++ int n = 0; ++ ++ /* Get proc_file_read() to step 'offset' by one on each sucessive call. ++ * We use 'offset' (*ppos) to indicate where we are in dev_list. ++ * This also assumes the user has posted a read buffer large ++ * enough to hold the complete output; but that's life in /proc. ++ */ ++ ++ *(int *)start = 1; ++ ++ /* Print header first */ ++ if (step == 0) ++ buf += ++ sprintf(buf, ++ "Multi-version YAFFS built:" __DATE__ " " __TIME__ ++ "\n"); ++ else if (step == 1) ++ buf += sprintf(buf, "\n"); ++ else { ++ step -= 2; ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (n < (step & ~1)) { ++ n += 2; ++ continue; ++ } ++ if ((step & 1) == 0) { ++ buf += ++ sprintf(buf, "\nDevice %d \"%s\"\n", n, ++ dev->param.name); ++ buf = yaffs_dump_dev_part0(buf, dev); ++ } else { ++ buf = yaffs_dump_dev_part1(buf, dev); ++ } ++ ++ break; ++ } ++ mutex_unlock(&yaffs_context_lock); ++ } ++ ++ return buf - page < count ? buf - page : count; ++} ++ ++/** ++ * Set the verbosity of the warnings and error messages. ++ * ++ * Note that the names can only be a..z or _ with the current code. ++ */ ++ ++static struct { ++ char *mask_name; ++ unsigned mask_bitfield; ++} mask_flags[] = { ++ {"allocate", YAFFS_TRACE_ALLOCATE}, ++ {"always", YAFFS_TRACE_ALWAYS}, ++ {"background", YAFFS_TRACE_BACKGROUND}, ++ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, ++ {"buffers", YAFFS_TRACE_BUFFERS}, ++ {"bug", YAFFS_TRACE_BUG}, ++ {"checkpt", YAFFS_TRACE_CHECKPOINT}, ++ {"deletion", YAFFS_TRACE_DELETION}, ++ {"erase", YAFFS_TRACE_ERASE}, ++ {"error", YAFFS_TRACE_ERROR}, ++ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, ++ {"gc", YAFFS_TRACE_GC}, ++ {"lock", YAFFS_TRACE_LOCK}, ++ {"mtd", YAFFS_TRACE_MTD}, ++ {"nandaccess", YAFFS_TRACE_NANDACCESS}, ++ {"os", YAFFS_TRACE_OS}, ++ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, ++ {"scan", YAFFS_TRACE_SCAN}, ++ {"mount", YAFFS_TRACE_MOUNT}, ++ {"tracing", YAFFS_TRACE_TRACING}, ++ {"sync", YAFFS_TRACE_SYNC}, ++ {"write", YAFFS_TRACE_WRITE}, ++ {"verify", YAFFS_TRACE_VERIFY}, ++ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, ++ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, ++ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, ++ {"all", 0xffffffff}, ++ {"none", 0}, ++ {NULL, 0}, ++}; ++ ++#define MAX_MASK_NAME_LENGTH 40 ++static int yaffs_proc_write_trace_options(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ unsigned rg = 0, mask_bitfield; ++ char *end; ++ char *mask_name; ++ const char *x; ++ char substring[MAX_MASK_NAME_LENGTH + 1]; ++ int i; ++ int done = 0; ++ int add, len = 0; ++ int pos = 0; ++ ++ rg = yaffs_trace_mask; ++ ++ while (!done && (pos < count)) { ++ done = 1; ++ while ((pos < count) && isspace(buf[pos])) ++ pos++; ++ ++ switch (buf[pos]) { ++ case '+': ++ case '-': ++ case '=': ++ add = buf[pos]; ++ pos++; ++ break; ++ ++ default: ++ add = ' '; ++ break; ++ } ++ mask_name = NULL; ++ ++ mask_bitfield = simple_strtoul(buf + pos, &end, 0); ++ ++ if (end > buf + pos) { ++ mask_name = "numeral"; ++ len = end - (buf + pos); ++ pos += len; ++ done = 0; ++ } else { ++ for (x = buf + pos, i = 0; ++ (*x == '_' || (*x >= 'a' && *x <= 'z')) && ++ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) ++ substring[i] = *x; ++ substring[i] = '\0'; ++ ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ if (strcmp(substring, mask_flags[i].mask_name) ++ == 0) { ++ mask_name = mask_flags[i].mask_name; ++ mask_bitfield = ++ mask_flags[i].mask_bitfield; ++ done = 0; ++ break; ++ } ++ } ++ } ++ ++ if (mask_name != NULL) { ++ done = 0; ++ switch (add) { ++ case '-': ++ rg &= ~mask_bitfield; ++ break; ++ case '+': ++ rg |= mask_bitfield; ++ break; ++ case '=': ++ rg = mask_bitfield; ++ break; ++ default: ++ rg |= mask_bitfield; ++ break; ++ } ++ } ++ } ++ ++ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; ++ ++ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); ++ ++ if (rg & YAFFS_TRACE_ALWAYS) { ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ char flag; ++ flag = ((rg & mask_flags[i].mask_bitfield) == ++ mask_flags[i].mask_bitfield) ? '+' : '-'; ++ printk(KERN_DEBUG "%c%s\n", flag, ++ mask_flags[i].mask_name); ++ } ++ } ++ ++ return count; ++} ++ ++/* Debug strings are of the form: ++ * .bnnn print info on block n ++ * .cobjn,chunkn print nand chunk id for objn:chunkn ++ */ ++ ++static int yaffs_proc_debug_write(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ ++ char str[100]; ++ char *p0; ++ char *p1; ++ long p1_val; ++ long p0_val; ++ char cmd; ++ struct list_head *item; ++ ++ memset(str, 0, sizeof(str)); ++ memcpy(str, buf, min(count, sizeof(str) -1)); ++ ++ cmd = str[1]; ++ ++ p0 = str + 2; ++ ++ p1 = p0; ++ ++ while (*p1 && *p1 != ',') { ++ p1++; ++ } ++ *p1 = '\0'; ++ p1++; ++ ++ p0_val = simple_strtol(p0, NULL, 0); ++ p1_val = simple_strtol(p1, NULL, 0); ++ ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (cmd == 'b') { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev,p0_val); ++ ++ if(bi) { ++ printk("Block %d: state %d, retire %d, use %d, seq %d\n", ++ (int)p0_val, bi->block_state, ++ bi->needs_retiring, bi->pages_in_use, ++ bi->seq_number); ++ } ++ } else if (cmd == 'c') { ++ struct yaffs_obj *obj; ++ int nand_chunk; ++ ++ obj = yaffs_find_by_number(dev, p0_val); ++ if (!obj) ++ printk("No obj %d\n", (int)p0_val); ++ else { ++ if(p1_val == 0) ++ nand_chunk = obj->hdr_chunk; ++ else ++ nand_chunk = ++ yaffs_find_chunk_in_file(obj, ++ p1_val, NULL); ++ printk("Nand chunk for %d:%d is %d\n", ++ (int)p0_val, (int)p1_val, nand_chunk); ++ } ++ } ++ } ++ ++ mutex_unlock(&yaffs_context_lock); ++ ++ return count; ++} ++ ++static int yaffs_proc_write(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ if (buf[0] == '.') ++ return yaffs_proc_debug_write(file, buf, count, data); ++ return yaffs_proc_write_trace_options(file, buf, count, data); ++} ++#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) */ ++ ++/* Stuff to handle installation of file systems */ ++struct file_system_to_install { ++ struct file_system_type *fst; ++ int installed; ++}; ++ ++static struct file_system_to_install fs_to_install[] = { ++ {&yaffs_fs_type, 0}, ++ {&yaffs2_fs_type, 0}, ++ {NULL, 0} ++}; ++ ++static int __init init_yaffs_fs(void) ++{ ++ int error = 0; ++ struct file_system_to_install *fsinst; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs built " __DATE__ " " __TIME__ " Installing."); ++ ++ mutex_init(&yaffs_context_lock); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) ++ /* Install the proc_fs entries */ ++ my_proc_entry = create_proc_entry("yaffs", ++ S_IRUGO | S_IFREG, YPROC_ROOT); ++ ++ if (my_proc_entry) { ++ my_proc_entry->write_proc = yaffs_proc_write; ++ my_proc_entry->read_proc = yaffs_proc_read; ++ my_proc_entry->data = NULL; ++ } else { ++ return -ENOMEM; ++ } ++#endif ++ ++ /* Now add the file system entries */ ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst && !error) { ++ error = register_filesystem(fsinst->fst); ++ if (!error) ++ fsinst->installed = 1; ++ fsinst++; ++ } ++ ++ /* Any errors? uninstall */ ++ if (error) { ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++ } ++ ++ return error; ++} ++ ++static void __exit exit_yaffs_fs(void) ++{ ++ ++ struct file_system_to_install *fsinst; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs built " __DATE__ " " __TIME__ " removing."); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) ++ remove_proc_entry("yaffs", YPROC_ROOT); ++#endif ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++} ++ ++module_init(init_yaffs_fs) ++ module_exit(exit_yaffs_fs) ++ ++ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); ++MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.12.20/fs/yaffs2/yaffs_yaffs1.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,422 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_yaffs1.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++#include "yaffs_attribs.h" ++ ++int yaffs1_scan(struct yaffs_dev *dev) ++{ ++ struct yaffs_ext_tags tags; ++ int blk; ++ int result; ++ int chunk; ++ int c; ++ int deleted; ++ enum yaffs_block_state state; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int alloc_failed = 0; ++ struct yaffs_shadow_fixer *shadow_fixers = NULL; ++ u8 *chunk_data; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs1_scan starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, state, seq_number); ++ ++ if (state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } ++ bi++; ++ } ++ ++ /* For each block.... */ ++ for (blk = dev->internal_start_block; ++ !alloc_failed && blk <= dev->internal_end_block; blk++) { ++ ++ cond_resched(); ++ ++ bi = yaffs_get_block_info(dev, blk); ++ state = bi->block_state; ++ ++ deleted = 0; ++ ++ /* For each chunk in each block that needs scanning.... */ ++ for (c = 0; ++ !alloc_failed && c < dev->param.chunks_per_block && ++ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { ++ /* Read the tags and decide what to do */ ++ chunk = blk * dev->param.chunks_per_block + c; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, ++ &tags); ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || ++ tags.is_deleted) { ++ /* YAFFS1 only... ++ * A deleted chunk ++ */ ++ deleted++; ++ dev->n_free_chunks++; ++ } else if (!tags.chunk_used) { ++ /* An unassigned chunk in the block ++ * This means that either the block is empty or ++ * this is the one being allocated from ++ */ ++ ++ if (c == 0) { ++ /* We're looking at the first chunk in ++ *the block so the block is unused */ ++ state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ /* this is the block being allocated */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, c); ++ state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = c; ++ dev->alloc_block_finder = blk; ++ ++ } ++ ++ dev->n_free_chunks += ++ (dev->param.chunks_per_block - c); ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ unsigned int endpos; ++ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ /* PutChunkIntoFile checks for a clash ++ * (two data chunks with the same chunk_id). ++ */ ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in) { ++ if (!yaffs_put_chunk_in_file ++ (in, tags.chunk_id, chunk, 1)) ++ alloc_failed = 1; ++ } ++ ++ endpos = ++ (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk + ++ tags.n_bytes; ++ if (in && ++ in->variant_type == ++ YAFFS_OBJECT_TYPE_FILE && ++ in->variant.file_variant.scanned_size < ++ endpos) { ++ in->variant.file_variant.scanned_size = ++ endpos; ++ if (!dev->param.use_header_file_size) { ++ in->variant. ++ file_variant.file_size = ++ in->variant. ++ file_variant.scanned_size; ++ } ++ ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Make the object ++ */ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ in = yaffs_find_by_number(dev, tags.obj_id); ++ if (in && in->variant_type != oh->type) { ++ /* This should not happen, but somehow ++ * Wev'e ended up with an obj_id that ++ * has been reused but not yet deleted, ++ * and worse still it has changed type. ++ * Delete the old object. ++ */ ++ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ oh->type); ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in && oh->shadows_obj > 0) { ++ ++ struct yaffs_shadow_fixer *fixer; ++ fixer = ++ kmalloc(sizeof ++ (struct yaffs_shadow_fixer), ++ GFP_NOFS); ++ if (fixer) { ++ fixer->next = shadow_fixers; ++ shadow_fixers = fixer; ++ fixer->obj_id = tags.obj_id; ++ fixer->shadowed_id = ++ oh->shadows_obj; ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Shadow fixer: %d shadows %d", ++ fixer->obj_id, ++ fixer->shadowed_id); ++ ++ } ++ ++ } ++ ++ if (in && in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate and need to ++ * resolve it. */ ++ ++ unsigned existing_serial = in->serial; ++ unsigned new_serial = ++ tags.serial_number; ++ ++ if (((existing_serial + 1) & 3) == ++ new_serial) { ++ /* Use new one - destroy the ++ * exisiting one */ ++ yaffs_chunk_del(dev, ++ in->hdr_chunk, ++ 1, __LINE__); ++ in->valid = 0; ++ } else { ++ /* Use existing - destroy ++ * this one. */ ++ yaffs_chunk_del(dev, chunk, 1, ++ __LINE__); ++ } ++ } ++ ++ if (in && !in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == ++ YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ } else if (in && !in->valid) { ++ /* we need to load this info */ ++ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ in->dirty = 0; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ parent = ++ yaffs_find_or_create_by_number ++ (dev, oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ if (!parent) ++ alloc_failed = 1; ++ if (parent && parent->variant_type == ++ YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant. ++ children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, a problem.... ++ * We're trying to use a ++ * non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (dev->param. ++ use_header_file_size) ++ in->variant. ++ file_variant.file_size ++ = yaffs_oh_to_size(oh); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant. ++ hardlink_variant.equiv_id = ++ oh->equiv_id; ++ list_add(&in->hard_links, ++ &hard_list); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant. ++ alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant. ++ symlink_variant.alias) ++ alloc_failed = 1; ++ break; ++ } ++ } ++ } ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, ++ * then the block is fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ /* If the block was partially allocated then ++ * treat it as fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ bi->block_state = state; ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ yaffs_block_became_dirty(dev, blk); ++ } ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We should now have scanned all the objects, now it's time to add ++ * these hardlinks. ++ */ ++ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ /* ++ * Fix up any shadowed objects. ++ * There should not be more than one of these. ++ */ ++ { ++ struct yaffs_shadow_fixer *fixer; ++ struct yaffs_obj *obj; ++ ++ while (shadow_fixers) { ++ fixer = shadow_fixers; ++ shadow_fixers = fixer->next; ++ /* Complete the rename transaction by deleting the ++ * shadowed object then setting the object header ++ to unshadowed. ++ */ ++ obj = yaffs_find_by_number(dev, fixer->shadowed_id); ++ if (obj) ++ yaffs_del_obj(obj); ++ ++ obj = yaffs_find_by_number(dev, fixer->obj_id); ++ ++ if (obj) ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ kfree(fixer); ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.12.20/fs/yaffs2/yaffs_yaffs1.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS1_H__ ++#define __YAFFS_YAFFS1_H__ ++ ++#include "yaffs_guts.h" ++int yaffs1_scan(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.12.20/fs/yaffs2/yaffs_yaffs2.c +--- linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,1534 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_checkptrw.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_verify.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* ++ * Checkpoints are really no benefit on very small partitions. ++ * ++ * To save space on small partitions don't bother with checkpoints unless ++ * the partition is at least this big. ++ */ ++#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 ++#define YAFFS_SMALL_HOLE_THRESHOLD 4 ++ ++/* ++ * Oldest Dirty Sequence Number handling. ++ */ ++ ++/* yaffs_calc_oldest_dirty_seq() ++ * yaffs2_find_oldest_dirty_seq() ++ * Calculate the oldest dirty sequence number if we don't know it. ++ */ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ int i; ++ unsigned seq; ++ unsigned block_no = 0; ++ struct yaffs_block_info *b; ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ /* Find the oldest dirty sequence number. */ ++ seq = dev->seq_number + 1; ++ b = dev->block_info; ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ if (b->block_state == YAFFS_BLOCK_STATE_FULL && ++ (b->pages_in_use - b->soft_del_pages) < ++ dev->param.chunks_per_block && ++ b->seq_number < seq) { ++ seq = b->seq_number; ++ block_no = i; ++ } ++ b++; ++ } ++ ++ if (block_no) { ++ dev->oldest_dirty_seq = seq; ++ dev->oldest_dirty_block = block_no; ++ } ++} ++ ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->oldest_dirty_seq) ++ yaffs_calc_oldest_dirty_seq(dev); ++} ++ ++/* ++ * yaffs_clear_oldest_dirty_seq() ++ * Called when a block is erased or marked bad. (ie. when its seq_number ++ * becomes invalid). If the value matches the oldest then we clear ++ * dev->oldest_dirty_seq to force its recomputation. ++ */ ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ } ++} ++ ++/* ++ * yaffs2_update_oldest_dirty_seq() ++ * Update the oldest dirty sequence number whenever we dirty a block. ++ * Only do this if the oldest_dirty_seq is actually being tracked. ++ */ ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (dev->oldest_dirty_seq) { ++ if (dev->oldest_dirty_seq > bi->seq_number) { ++ dev->oldest_dirty_seq = bi->seq_number; ++ dev->oldest_dirty_block = block_no; ++ } ++ } ++} ++ ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return 1; /* disqualification only applies to yaffs2. */ ++ ++ if (!bi->has_shrink_hdr) ++ return 1; /* can gc */ ++ ++ yaffs2_find_oldest_dirty_seq(dev); ++ ++ /* Can't do gc of this block if there are any blocks older than this ++ * one that have discarded pages. ++ */ ++ return (bi->seq_number <= dev->oldest_dirty_seq); ++} ++ ++/* ++ * yaffs2_find_refresh_block() ++ * periodically finds the oldest full block by sequence number for refreshing. ++ * Only for yaffs2. ++ */ ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) ++{ ++ u32 b; ++ u32 oldest = 0; ++ u32 oldest_seq = 0; ++ struct yaffs_block_info *bi; ++ ++ if (!dev->param.is_yaffs2) ++ return oldest; ++ ++ /* ++ * If refresh period < 10 then refreshing is disabled. ++ */ ++ if (dev->param.refresh_period < 10) ++ return oldest; ++ ++ /* ++ * Fix broken values. ++ */ ++ if (dev->refresh_skip > dev->param.refresh_period) ++ dev->refresh_skip = dev->param.refresh_period; ++ ++ if (dev->refresh_skip > 0) ++ return oldest; ++ ++ /* ++ * Refresh skip is now zero. ++ * We'll do a refresh this time around.... ++ * Update the refresh skip and find the oldest block. ++ */ ++ dev->refresh_skip = dev->param.refresh_period; ++ dev->refresh_count++; ++ bi = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ ++ if (oldest < 1 || bi->seq_number < oldest_seq) { ++ oldest = b; ++ oldest_seq = bi->seq_number; ++ } ++ } ++ bi++; ++ } ++ ++ if (oldest > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC refresh count %d selected block %d with seq_number %d", ++ dev->refresh_count, oldest, oldest_seq); ++ } ++ ++ return oldest; ++} ++ ++int yaffs2_checkpt_required(struct yaffs_dev *dev) ++{ ++ int nblocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ nblocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ return !dev->param.skip_checkpt_wr && ++ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); ++} ++ ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) ++{ ++ int retval; ++ int n_bytes = 0; ++ int n_blocks; ++ int dev_blocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { ++ /* Not a valid value so recalculate */ ++ dev_blocks = dev->param.end_block - dev->param.start_block + 1; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(struct yaffs_checkpt_dev); ++ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); ++ n_bytes += dev_blocks * dev->chunk_bit_stride; ++ n_bytes += ++ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * ++ dev->n_obj; ++ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(u32); /* checksum */ ++ ++ /* Round up and add 2 blocks to allow for some bad blocks, ++ * so add 3 */ ++ ++ n_blocks = ++ (n_bytes / ++ (dev->data_bytes_per_chunk * ++ dev->param.chunks_per_block)) + 3; ++ ++ dev->checkpoint_blocks_required = n_blocks; ++ } ++ ++ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; ++ if (retval < 0) ++ retval = 0; ++ return retval; ++} ++ ++/*--------------------- Checkpointing --------------------*/ ++ ++static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ ++ memset(&cp, 0, sizeof(cp)); ++ ++ cp.struct_type = sizeof(cp); ++ cp.magic = YAFFS_MAGIC; ++ cp.version = YAFFS_CHECKPOINT_VERSION; ++ cp.head = (head) ? 1 : 0; ++ ++ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ if (ok) ++ ok = (cp.struct_type == sizeof(cp)) && ++ (cp.magic == YAFFS_MAGIC) && ++ (cp.version == YAFFS_CHECKPOINT_VERSION) && ++ (cp.head == ((head) ? 1 : 0)); ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, ++ struct yaffs_dev *dev) ++{ ++ cp->n_erased_blocks = dev->n_erased_blocks; ++ cp->alloc_block = dev->alloc_block; ++ cp->alloc_page = dev->alloc_page; ++ cp->n_free_chunks = dev->n_free_chunks; ++ ++ cp->n_deleted_files = dev->n_deleted_files; ++ cp->n_unlinked_files = dev->n_unlinked_files; ++ cp->n_bg_deletions = dev->n_bg_deletions; ++ cp->seq_number = dev->seq_number; ++ ++} ++ ++static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, ++ struct yaffs_checkpt_dev *cp) ++{ ++ dev->n_erased_blocks = cp->n_erased_blocks; ++ dev->alloc_block = cp->alloc_block; ++ dev->alloc_page = cp->alloc_page; ++ dev->n_free_chunks = cp->n_free_chunks; ++ ++ dev->n_deleted_files = cp->n_deleted_files; ++ dev->n_unlinked_files = cp->n_unlinked_files; ++ dev->n_bg_deletions = cp->n_bg_deletions; ++ dev->seq_number = cp->seq_number; ++} ++ ++static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ int ok; ++ ++ /* Write device runtime values */ ++ yaffs2_dev_to_checkpt_dev(&cp, dev); ++ cp.struct_type = sizeof(cp); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ /* Write block info */ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); ++ if (!ok) ++ return 0; ++ ++ /* Write chunk bits */ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = ++ (dev->internal_end_block - dev->internal_start_block + 1); ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ if (cp.struct_type != sizeof(cp)) ++ return 0; ++ ++ yaffs_checkpt_dev_to_dev(dev, &cp); ++ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); ++ ++ if (!ok) ++ return 0; ++ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, ++ struct yaffs_obj *obj) ++{ ++ cp->obj_id = obj->obj_id; ++ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; ++ cp->hdr_chunk = obj->hdr_chunk; ++ cp->variant_type = obj->variant_type; ++ cp->deleted = obj->deleted; ++ cp->soft_del = obj->soft_del; ++ cp->unlinked = obj->unlinked; ++ cp->fake = obj->fake; ++ cp->rename_allowed = obj->rename_allowed; ++ cp->unlink_allowed = obj->unlink_allowed; ++ cp->serial = obj->serial; ++ cp->n_data_chunks = obj->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; ++} ++ ++static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, ++ struct yaffs_checkpt_obj *cp) ++{ ++ struct yaffs_obj *parent; ++ ++ if (obj->variant_type != cp->variant_type) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", ++ cp->obj_id, cp->variant_type, cp->hdr_chunk, ++ obj->variant_type); ++ return 0; ++ } ++ ++ obj->obj_id = cp->obj_id; ++ ++ if (cp->parent_id) ++ parent = yaffs_find_or_create_by_number(obj->my_dev, ++ cp->parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ else ++ parent = NULL; ++ ++ if (parent) { ++ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", ++ cp->obj_id, cp->parent_id, ++ cp->variant_type, cp->hdr_chunk, ++ parent->variant_type); ++ return 0; ++ } ++ yaffs_add_obj_to_dir(parent, obj); ++ } ++ ++ obj->hdr_chunk = cp->hdr_chunk; ++ obj->variant_type = cp->variant_type; ++ obj->deleted = cp->deleted; ++ obj->soft_del = cp->soft_del; ++ obj->unlinked = cp->unlinked; ++ obj->fake = cp->fake; ++ obj->rename_allowed = cp->rename_allowed; ++ obj->unlink_allowed = cp->unlink_allowed; ++ obj->serial = cp->serial; ++ obj->n_data_chunks = cp->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; ++ ++ if (obj->hdr_chunk > 0) ++ obj->lazy_loaded = 1; ++ return 1; ++} ++ ++static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, ++ struct yaffs_tnode *tn, u32 level, ++ int chunk_offset) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ int ok = 1; ++ u32 base_offset; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { ++ if (!tn->internal[i]) ++ continue; ++ ok = yaffs2_checkpt_tnode_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) + i); ++ } ++ return ok; ++ } ++ ++ /* Level 0 tnode */ ++ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; ++ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == ++ sizeof(base_offset)); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ ++ return ok; ++} ++ ++static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 end_marker = ~0; ++ int ok = 1; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return ok; ++ ++ ok = yaffs2_checkpt_tnode_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant.file_variant. ++ top_level, 0); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, ++ sizeof(end_marker)) == sizeof(end_marker)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 base_chunk; ++ int ok = 1; ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; ++ struct yaffs_tnode *tn; ++ int nread = 0; ++ ++ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == ++ sizeof(base_chunk)); ++ ++ while (ok && (~base_chunk)) { ++ nread++; ++ /* Read level 0 tnode */ ++ ++ tn = yaffs_get_tnode(dev); ++ if (tn) ++ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ else ++ ok = 0; ++ ++ if (tn && ok) ++ ok = yaffs_add_find_tnode_0(dev, ++ file_stuct_ptr, ++ base_chunk, tn) ? 1 : 0; ++ ++ if (ok) ++ ok = (yaffs2_checkpt_rd ++ (dev, &base_chunk, ++ sizeof(base_chunk)) == sizeof(base_chunk)); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read tnodes %d records, last %d. ok %d", ++ nread, base_chunk, ok); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int i; ++ int ok = 1; ++ struct list_head *lh; ++ ++ /* Iterate through the objects in each hash entry, ++ * dumping them to the checkpointing stream. ++ */ ++ ++ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ if (!obj->defered_free) { ++ yaffs2_obj_checkpt_obj(&cp, obj); ++ cp.struct_type = sizeof(cp); ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", ++ cp.obj_id, cp.parent_id, ++ cp.variant_type, cp.hdr_chunk, obj); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, ++ sizeof(cp)) == sizeof(cp)); ++ ++ if (ok && ++ obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) ++ ok = yaffs2_wr_checkpt_tnodes(obj); ++ } ++ } ++ } ++ ++ /* Dump end of list */ ++ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); ++ cp.struct_type = sizeof(cp); ++ ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int ok = 1; ++ int done = 0; ++ LIST_HEAD(hard_list); ++ ++ ++ while (ok && !done) { ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (cp.struct_type != sizeof(cp)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "struct size %d instead of %d ok %d", ++ cp.struct_type, (int)sizeof(cp), ok); ++ ok = 0; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read object %d parent %d type %d chunk %d ", ++ cp.obj_id, cp.parent_id, cp.variant_type, ++ cp.hdr_chunk); ++ ++ if (ok && cp.obj_id == ~0) { ++ done = 1; ++ } else if (ok) { ++ obj = ++ yaffs_find_or_create_by_number(dev, cp.obj_id, ++ cp.variant_type); ++ if (obj) { ++ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); ++ if (!ok) ++ break; ++ if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) { ++ ok = yaffs2_rd_checkpt_tnodes(obj); ++ } else if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_HARDLINK) { ++ list_add(&obj->hard_links, &hard_list); ++ } ++ } else { ++ ok = 0; ++ } ++ } ++ } ++ ++ if (ok) ++ yaffs_link_fixup(dev, &hard_list); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum); ++ ++ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == ++ sizeof(checkpt_sum)); ++ ++ if (!ok) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum0; ++ u32 checkpt_sum1; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); ++ ++ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == ++ sizeof(checkpt_sum1)); ++ ++ if (!ok) ++ return 0; ++ ++ if (checkpt_sum0 != checkpt_sum1) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!yaffs2_checkpt_required(dev)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint write"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 1); ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint device"); ++ ok = yaffs2_wr_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint objects"); ++ ok = yaffs2_wr_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) ++ ok = yaffs2_wr_checkpt_sum(dev); ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return dev->is_checkpointed; ++} ++ ++static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!dev->param.is_yaffs2) ++ ok = 0; ++ ++ if (ok && dev->param.skip_checkpt_rd) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint read"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint device"); ++ ok = yaffs2_rd_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint objects"); ++ ok = yaffs2_rd_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) { ++ ok = yaffs2_rd_checkpt_sum(dev); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint checksum %d", ok); ++ } ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return ok ? 1 : 0; ++} ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) ++{ ++ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { ++ dev->is_checkpointed = 0; ++ yaffs2_checkpt_invalidate_stream(dev); ++ } ++ if (dev->param.sb_dirty_fn) ++ dev->param.sb_dirty_fn(dev); ++} ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "save entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ ++ if (!dev->is_checkpointed) { ++ yaffs2_checkpt_invalidate(dev); ++ yaffs2_wr_checkpt_data(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, ++ "save exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return dev->is_checkpointed; ++} ++ ++int yaffs2_checkpt_restore(struct yaffs_dev *dev) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ retval = yaffs2_rd_checkpt_data(dev); ++ ++ if (dev->is_checkpointed) { ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return retval; ++} ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) ++{ ++ /* if new_size > old_file_size. ++ * We're going to be writing a hole. ++ * If the hole is small then write zeros otherwise write a start ++ * of hole marker. ++ */ ++ loff_t old_file_size; ++ loff_t increase; ++ int small_hole; ++ int result = YAFFS_OK; ++ struct yaffs_dev *dev = NULL; ++ u8 *local_buffer = NULL; ++ int small_increase_ok = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ dev = obj->my_dev; ++ ++ /* Bail out if not yaffs2 mode */ ++ if (!dev->param.is_yaffs2) ++ return YAFFS_OK; ++ ++ old_file_size = obj->variant.file_variant.file_size; ++ ++ if (new_size <= old_file_size) ++ return YAFFS_OK; ++ ++ increase = new_size - old_file_size; ++ ++ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && ++ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) ++ small_hole = 1; ++ else ++ small_hole = 0; ++ ++ if (small_hole) ++ local_buffer = yaffs_get_temp_buffer(dev); ++ ++ if (local_buffer) { ++ /* fill hole with zero bytes */ ++ loff_t pos = old_file_size; ++ int this_write; ++ int written; ++ memset(local_buffer, 0, dev->data_bytes_per_chunk); ++ small_increase_ok = 1; ++ ++ while (increase > 0 && small_increase_ok) { ++ this_write = increase; ++ if (this_write > dev->data_bytes_per_chunk) ++ this_write = dev->data_bytes_per_chunk; ++ written = ++ yaffs_do_file_wr(obj, local_buffer, pos, this_write, ++ 0); ++ if (written == this_write) { ++ pos += this_write; ++ increase -= this_write; ++ } else { ++ small_increase_ok = 0; ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ ++ /* If out of space then reverse any chunks we've added */ ++ if (!small_increase_ok) ++ yaffs_resize_file_down(obj, old_file_size); ++ } ++ ++ if (!small_increase_ok && ++ obj->parent && ++ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { ++ /* Write a hole start header with the old file size */ ++ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); ++ } ++ ++ return result; ++} ++ ++struct yaffs_block_index { ++ int seq; ++ int block; ++}; ++ ++static int yaffs2_ybicmp(const void *a, const void *b) ++{ ++ int aseq = ((struct yaffs_block_index *)a)->seq; ++ int bseq = ((struct yaffs_block_index *)b)->seq; ++ int ablock = ((struct yaffs_block_index *)a)->block; ++ int bblock = ((struct yaffs_block_index *)b)->block; ++ ++ if (aseq == bseq) ++ return ablock - bblock; ++ ++ return aseq - bseq; ++} ++ ++static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int blk, int chunk_in_block, ++ int *found_chunks, ++ u8 *chunk_data, ++ struct list_head *hard_list, ++ int summary_available) ++{ ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int equiv_id; ++ loff_t file_size; ++ int is_shrink; ++ int is_unlinked; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; ++ struct yaffs_file_var *file_var; ++ struct yaffs_hardlink_var *hl_var; ++ struct yaffs_symlink_var *sl_var; ++ ++ if (summary_available) { ++ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); ++ tags.seq_number = bi->seq_number; ++ } ++ ++ if (!summary_available || tags.obj_id == 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); ++ dev->tags_used++; ++ } else { ++ dev->summary_used++; ++ } ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (!tags.chunk_used) { ++ /* An unassigned chunk in the block. ++ * If there are used chunks after this one, then ++ * it is a chunk that was skipped due to failing ++ * the erased check. Just skip it so that it can ++ * be deleted. ++ * But, more typically, We get here when this is ++ * an unallocated chunk and his means that ++ * either the block is empty or this is the one ++ * being allocated from ++ */ ++ ++ if (*found_chunks) { ++ /* This is a chunk that was skipped due ++ * to failing the erased check */ ++ } else if (chunk_in_block == 0) { ++ /* We're looking at the first chunk in ++ * the block so the block is unused */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ if (dev->seq_number == bi->seq_number) { ++ /* Allocating from this block*/ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, chunk_in_block); ++ ++ bi->block_state = ++ YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = chunk_in_block; ++ dev->alloc_block_finder = blk; ++ } else { ++ /* This is a partially written block ++ * that is not the current ++ * allocation block. ++ */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Partially written block %d detected. gc will fix this.", ++ blk); ++ } ++ } ++ } ++ ++ dev->n_free_chunks++; ++ ++ } else if (tags.ecc_result == ++ YAFFS_ECC_RESULT_UNFIXED) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Unfixed ECC in chunk(%d:%d), chunk ignored", ++ blk, chunk_in_block); ++ dev->n_free_chunks++; ++ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || ++ tags.chunk_id > YAFFS_MAX_CHUNK_ID || ++ tags.obj_id == YAFFS_OBJECTID_SUMMARY || ++ (tags.chunk_id > 0 && ++ tags.n_bytes > dev->data_bytes_per_chunk) || ++ tags.seq_number != bi->seq_number) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", ++ blk, chunk_in_block, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ dev->n_free_chunks++; ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ loff_t endpos; ++ loff_t chunk_base = (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk; ++ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ if (!in) ++ /* Out of memory */ ++ alloc_failed = 1; ++ ++ if (in && ++ in->variant_type == YAFFS_OBJECT_TYPE_FILE && ++ chunk_base < in->variant.file_variant.shrink_size) { ++ /* This has not been invalidated by ++ * a resize */ ++ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, ++ chunk, -1)) ++ alloc_failed = 1; ++ ++ /* File size is calculated by looking at ++ * the data chunks if we have not ++ * seen an object header yet. ++ * Stop this practice once we find an ++ * object header. ++ */ ++ endpos = chunk_base + tags.n_bytes; ++ ++ if (!in->valid && ++ in->variant.file_variant.scanned_size < endpos) { ++ in->variant.file_variant. ++ scanned_size = endpos; ++ in->variant.file_variant. ++ file_size = endpos; ++ } ++ } else if (in) { ++ /* This chunk has been invalidated by a ++ * resize, or a past file deletion ++ * so delete the chunk*/ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Thus, we read in the object header and make ++ * the object ++ */ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ oh = NULL; ++ in = NULL; ++ ++ if (tags.extra_available) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ tags.extra_obj_type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ ++ if (!in || ++ (!in->valid && dev->param.disable_lazy_load) || ++ tags.extra_shadows || ++ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { ++ ++ /* If we don't have valid info then we ++ * need to read the chunk ++ * TODO In future we can probably defer ++ * reading the chunk and living with ++ * invalid data until needed. ++ */ ++ ++ result = yaffs_rd_chunk_tags_nand(dev, ++ chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ if (dev->param.inband_tags) { ++ /* Fix up the header if they got ++ * corrupted by inband tags */ ++ oh->shadows_obj = ++ oh->inband_shadowed_obj_id; ++ oh->is_shrink = ++ oh->inband_is_shrink; ++ } ++ ++ if (!in) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, oh->type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ } ++ ++ if (!in) { ++ /* TODO Hoosterman we have a problem! */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", ++ tags.obj_id, chunk); ++ return YAFFS_FAIL; ++ } ++ ++ if (in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate that will be ++ * discarded, but we first have to suck ++ * out resize info if it is a file. ++ */ ++ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && ++ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || ++ (tags.extra_available && ++ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ )) { ++ loff_t this_size = (oh) ? ++ yaffs_oh_to_size(oh) : ++ tags.extra_file_size; ++ u32 parent_obj_id = (oh) ? ++ oh->parent_obj_id : ++ tags.extra_parent_id; ++ ++ is_shrink = (oh) ? ++ oh->is_shrink : ++ tags.extra_is_shrink; ++ ++ /* If it is deleted (unlinked ++ * at start also means deleted) ++ * we treat the file size as ++ * being zeroed at this point. ++ */ ++ if (parent_obj_id == YAFFS_OBJECTID_DELETED || ++ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { ++ this_size = 0; ++ is_shrink = 1; ++ } ++ ++ if (is_shrink && ++ in->variant.file_variant.shrink_size > ++ this_size) ++ in->variant.file_variant.shrink_size = ++ this_size; ++ ++ if (is_shrink) ++ bi->has_shrink_hdr = 1; ++ } ++ /* Use existing - destroy this one. */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ ++ if (!in->valid && in->variant_type != ++ (oh ? oh->type : tags.extra_obj_type)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", ++ oh ? oh->type : tags.extra_obj_type, ++ in->variant_type, tags.obj_id, ++ chunk); ++ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); ++ } ++ ++ if (!in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ ++ if (oh) { ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->lazy_loaded = 0; ++ } else { ++ in->lazy_loaded = 1; ++ } ++ in->hdr_chunk = chunk; ++ ++ } else if (!in->valid) { ++ /* we need to load this info */ ++ in->valid = 1; ++ in->hdr_chunk = chunk; ++ if (oh) { ++ in->variant_type = oh->type; ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ ++ if (oh->shadows_obj > 0) ++ yaffs_handle_shadowed_obj(dev, ++ oh->shadows_obj, 1); ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ parent = yaffs_find_or_create_by_number(dev, ++ oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = yaffs_oh_to_size(oh); ++ is_shrink = oh->is_shrink; ++ equiv_id = oh->equiv_id; ++ } else { ++ in->variant_type = tags.extra_obj_type; ++ parent = yaffs_find_or_create_by_number(dev, ++ tags.extra_parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = tags.extra_file_size; ++ is_shrink = tags.extra_is_shrink; ++ equiv_id = tags.extra_equiv_id; ++ in->lazy_loaded = 1; ++ } ++ in->dirty = 0; ++ ++ if (!parent) ++ alloc_failed = 1; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ if (parent && ++ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant.children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, another problem.... ++ * Trying to use a non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ yaffs_add_obj_to_dir(parent, in); ++ ++ is_unlinked = (parent == dev->del_dir) || ++ (parent == dev->unlinked_dir); ++ ++ if (is_shrink) ++ /* Mark the block */ ++ bi->has_shrink_hdr = 1; ++ ++ /* Note re hardlinks. ++ * Since we might scan a hardlink before its equivalent ++ * object is scanned we put them all in a list. ++ * After scanning is complete, we should have all the ++ * objects, so we run through this list and fix up all ++ * the chains. ++ */ ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ file_var = &in->variant.file_variant; ++ if (file_var->scanned_size < file_size) { ++ /* This covers the case where the file ++ * size is greater than the data held. ++ * This will happen if the file is ++ * resized to be larger than its ++ * current data extents. ++ */ ++ file_var->file_size = file_size; ++ file_var->scanned_size = file_size; ++ } ++ ++ if (file_var->shrink_size > file_size) ++ file_var->shrink_size = file_size; ++ ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ hl_var = &in->variant.hardlink_variant; ++ if (!is_unlinked) { ++ hl_var->equiv_id = equiv_id; ++ list_add(&in->hard_links, hard_list); ++ } ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ sl_var = &in->variant.symlink_variant; ++ if (oh) { ++ sl_var->alias = ++ yaffs_clone_str(oh->alias); ++ if (!sl_var->alias) ++ alloc_failed = 1; ++ } ++ break; ++ } ++ } ++ } ++ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++int yaffs2_scan_backwards(struct yaffs_dev *dev) ++{ ++ int blk; ++ int block_iter; ++ int start_iter; ++ int end_iter; ++ int n_to_scan = 0; ++ enum yaffs_block_state state; ++ int c; ++ int deleted; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ u8 *chunk_data; ++ int found_chunks; ++ int alloc_failed = 0; ++ struct yaffs_block_index *block_index = NULL; ++ int alt_block_index = 0; ++ int summary_available; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ block_index = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); ++ ++ if (!block_index) { ++ block_index = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); ++ alt_block_index = 1; ++ } ++ ++ if (!block_index) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards() could not allocate block index!" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, bi->block_state, seq_number); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ dev->blocks_in_checkpt++; ++ ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } else if (bi->block_state == ++ YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* Determine the highest sequence number */ ++ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && ++ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { ++ block_index[n_to_scan].seq = seq_number; ++ block_index[n_to_scan].block = blk; ++ n_to_scan++; ++ if (seq_number >= dev->seq_number) ++ dev->seq_number = seq_number; ++ } else { ++ /* TODO: Nasty sequence number! */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Block scanning block %d has bad sequence number %d", ++ blk, seq_number); ++ } ++ } ++ bi++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); ++ ++ cond_resched(); ++ ++ /* Sort the blocks by sequence number */ ++ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), ++ yaffs2_ybicmp, NULL); ++ ++ cond_resched(); ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); ++ ++ /* Now scan the blocks looking at the data. */ ++ start_iter = 0; ++ end_iter = n_to_scan - 1; ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); ++ ++ /* For each block.... backwards */ ++ for (block_iter = end_iter; ++ !alloc_failed && block_iter >= start_iter; ++ block_iter--) { ++ /* Cooperative multitasking! This loop can run for so ++ long that watchdog timers expire. */ ++ cond_resched(); ++ ++ /* get the block to scan in the correct order */ ++ blk = block_index[block_iter].block; ++ bi = yaffs_get_block_info(dev, blk); ++ deleted = 0; ++ ++ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); ++ ++ /* For each chunk in each block that needs scanning.... */ ++ found_chunks = 0; ++ if (summary_available) ++ c = dev->chunks_per_summary - 1; ++ else ++ c = dev->param.chunks_per_block - 1; ++ ++ for (/* c is already initialised */; ++ !alloc_failed && c >= 0 && ++ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); ++ c--) { ++ /* Scan backwards... ++ * Read the tags and decide what to do ++ */ ++ if (yaffs2_scan_chunk(dev, bi, blk, c, ++ &found_chunks, chunk_data, ++ &hard_list, summary_available) == ++ YAFFS_FAIL) ++ alloc_failed = 1; ++ } ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, then the block ++ * is fully allocated. */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ yaffs_block_became_dirty(dev, blk); ++ } ++ } ++ ++ yaffs_skip_rest_of_block(dev); ++ ++ if (alt_block_index) ++ vfree(block_index); ++ else ++ kfree(block_index); ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We have scanned all the objects, now it's time to add these ++ * hardlinks. ++ */ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.12.20/fs/yaffs2/yaffs_yaffs2.h +--- linux-3.12.20.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS2_H__ ++#define __YAFFS_YAFFS2_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi); ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); ++int yaffs2_checkpt_required(struct yaffs_dev *dev); ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); ++int yaffs2_checkpt_save(struct yaffs_dev *dev); ++int yaffs2_checkpt_restore(struct yaffs_dev *dev); ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); ++int yaffs2_scan_backwards(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.12.20.orig/fs/yaffs2/yportenv.h linux-3.12.20/fs/yaffs2/yportenv.h +--- linux-3.12.20.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.12.20/fs/yaffs2/yportenv.h 2014-05-17 11:28:49.000000000 +0200 +@@ -0,0 +1,85 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YPORTENV_H__ ++#define __YPORTENV_H__ ++ ++/* ++ * Define the MTD version in terms of Linux Kernel versions ++ * This allows yaffs to be used independantly of the kernel ++ * as well as with it. ++ */ ++ ++#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) ++ ++#ifdef YAFFS_OUT_OF_TREE ++#include "moduleconfig.h" ++#endif ++ ++#include ++#define MTD_VERSION_CODE LINUX_VERSION_CODE ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* These type wrappings are used to support Unicode names in WinCE. */ ++#define YCHAR char ++#define YUCHAR unsigned char ++#define _Y(x) x ++ ++#define YAFFS_LOSTNFOUND_NAME "lost+found" ++#define YAFFS_LOSTNFOUND_PREFIX "obj" ++ ++ ++#define YAFFS_ROOT_MODE 0755 ++#define YAFFS_LOSTNFOUND_MODE 0700 ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define Y_CURRENT_TIME CURRENT_TIME.tv_sec ++#define Y_TIME_CONVERT(x) (x).tv_sec ++#else ++#define Y_CURRENT_TIME CURRENT_TIME ++#define Y_TIME_CONVERT(x) (x) ++#endif ++ ++#define compile_time_assertion(assertion) \ ++ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) ++ ++ ++#define yaffs_printf(msk, fmt, ...) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) ++ ++#define yaffs_trace(msk, fmt, ...) do { \ ++ if (yaffs_trace_mask & (msk)) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ ++} while (0) ++ ++ ++#endif diff --git a/target/linux/patches/3.12.44/zlib-inflate.patch b/target/linux/patches/3.12.44/zlib-inflate.patch new file mode 100644 index 000000000..58e1f6d21 --- /dev/null +++ b/target/linux/patches/3.12.44/zlib-inflate.patch @@ -0,0 +1,12 @@ +diff -Nur linux-2.6.37.orig/lib/Kconfig linux-2.6.37/lib/Kconfig +--- linux-2.6.37.orig/lib/Kconfig 2011-01-05 01:50:19.000000000 +0100 ++++ linux-2.6.37/lib/Kconfig 2011-03-01 20:10:29.833370667 +0100 +@@ -95,7 +95,7 @@ + # compression support is select'ed if needed + # + config ZLIB_INFLATE +- tristate ++ boolean + + config ZLIB_DEFLATE + tristate diff --git a/target/linux/patches/3.14.43/bsd-compatibility.patch b/target/linux/patches/3.14.43/bsd-compatibility.patch deleted file mode 100644 index b954b658f..000000000 --- a/target/linux/patches/3.14.43/bsd-compatibility.patch +++ /dev/null @@ -1,2538 +0,0 @@ -diff -Nur linux-3.11.5.orig/scripts/Makefile.lib linux-3.11.5/scripts/Makefile.lib ---- linux-3.11.5.orig/scripts/Makefile.lib 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/Makefile.lib 2013-10-16 18:09:31.000000000 +0200 -@@ -281,7 +281,12 @@ - size_append = printf $(shell \ - dec_size=0; \ - for F in $1; do \ -- fsize=$$(stat -c "%s" $$F); \ -+ if stat -qs .>/dev/null 2>&1; then \ -+ statcmd='stat -f %z'; \ -+ else \ -+ statcmd='stat -c %s'; \ -+ fi; \ -+ fsize=$$($$statcmd $$F); \ - dec_size=$$(expr $$dec_size + $$fsize); \ - done; \ - printf "%08x\n" $$dec_size | \ -diff -Nur linux-3.11.5.orig/scripts/mod/mk_elfconfig.c linux-3.11.5/scripts/mod/mk_elfconfig.c ---- linux-3.11.5.orig/scripts/mod/mk_elfconfig.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/mk_elfconfig.c 2013-10-16 18:09:31.000000000 +0200 -@@ -1,7 +1,18 @@ - #include - #include - #include --#include -+ -+#define EI_NIDENT (16) -+#define ELFMAG "\177ELF" -+ -+#define SELFMAG 4 -+#define EI_CLASS 4 -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ - - int - main(int argc, char **argv) -diff -Nur linux-3.11.5.orig/scripts/mod/modpost.h linux-3.11.5/scripts/mod/modpost.h ---- linux-3.11.5.orig/scripts/mod/modpost.h 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/modpost.h 2013-10-16 18:09:31.000000000 +0200 -@@ -7,7 +7,2453 @@ - #include - #include - #include --#include -+ -+ -+/* This file defines standard ELF types, structures, and macros. -+ Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. -+ This file is part of the GNU C Library. -+ -+ The GNU C Library is free software; you can redistribute it and/or -+ modify it under the terms of the GNU Lesser General Public -+ License as published by the Free Software Foundation; either -+ version 2.1 of the License, or (at your option) any later version. -+ -+ The GNU C Library is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ Lesser General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public -+ License along with the GNU C Library; if not, write to the Free -+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ 02111-1307 USA. */ -+ -+#ifndef _ELF_H -+#define _ELF_H 1 -+ -+__BEGIN_DECLS -+ -+/* Standard ELF types. */ -+ -+#include -+ -+/* Type for a 16-bit quantity. */ -+typedef uint16_t Elf32_Half; -+typedef uint16_t Elf64_Half; -+ -+/* Types for signed and unsigned 32-bit quantities. */ -+typedef uint32_t Elf32_Word; -+typedef int32_t Elf32_Sword; -+typedef uint32_t Elf64_Word; -+typedef int32_t Elf64_Sword; -+ -+/* Types for signed and unsigned 64-bit quantities. */ -+typedef uint64_t Elf32_Xword; -+typedef int64_t Elf32_Sxword; -+typedef uint64_t Elf64_Xword; -+typedef int64_t Elf64_Sxword; -+ -+/* Type of addresses. */ -+typedef uint32_t Elf32_Addr; -+typedef uint64_t Elf64_Addr; -+ -+/* Type of file offsets. */ -+typedef uint32_t Elf32_Off; -+typedef uint64_t Elf64_Off; -+ -+/* Type for section indices, which are 16-bit quantities. */ -+typedef uint16_t Elf32_Section; -+typedef uint16_t Elf64_Section; -+ -+/* Type for version symbol information. */ -+typedef Elf32_Half Elf32_Versym; -+typedef Elf64_Half Elf64_Versym; -+ -+ -+/* The ELF file header. This appears at the start of every ELF file. */ -+ -+#define EI_NIDENT (16) -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf32_Half e_type; /* Object file type */ -+ Elf32_Half e_machine; /* Architecture */ -+ Elf32_Word e_version; /* Object file version */ -+ Elf32_Addr e_entry; /* Entry point virtual address */ -+ Elf32_Off e_phoff; /* Program header table file offset */ -+ Elf32_Off e_shoff; /* Section header table file offset */ -+ Elf32_Word e_flags; /* Processor-specific flags */ -+ Elf32_Half e_ehsize; /* ELF header size in bytes */ -+ Elf32_Half e_phentsize; /* Program header table entry size */ -+ Elf32_Half e_phnum; /* Program header table entry count */ -+ Elf32_Half e_shentsize; /* Section header table entry size */ -+ Elf32_Half e_shnum; /* Section header table entry count */ -+ Elf32_Half e_shstrndx; /* Section header string table index */ -+} Elf32_Ehdr; -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf64_Half e_type; /* Object file type */ -+ Elf64_Half e_machine; /* Architecture */ -+ Elf64_Word e_version; /* Object file version */ -+ Elf64_Addr e_entry; /* Entry point virtual address */ -+ Elf64_Off e_phoff; /* Program header table file offset */ -+ Elf64_Off e_shoff; /* Section header table file offset */ -+ Elf64_Word e_flags; /* Processor-specific flags */ -+ Elf64_Half e_ehsize; /* ELF header size in bytes */ -+ Elf64_Half e_phentsize; /* Program header table entry size */ -+ Elf64_Half e_phnum; /* Program header table entry count */ -+ Elf64_Half e_shentsize; /* Section header table entry size */ -+ Elf64_Half e_shnum; /* Section header table entry count */ -+ Elf64_Half e_shstrndx; /* Section header string table index */ -+} Elf64_Ehdr; -+ -+/* Fields in the e_ident array. The EI_* macros are indices into the -+ array. The macros under each EI_* macro are the values the byte -+ may have. */ -+ -+#define EI_MAG0 0 /* File identification byte 0 index */ -+#define ELFMAG0 0x7f /* Magic number byte 0 */ -+ -+#define EI_MAG1 1 /* File identification byte 1 index */ -+#define ELFMAG1 'E' /* Magic number byte 1 */ -+ -+#define EI_MAG2 2 /* File identification byte 2 index */ -+#define ELFMAG2 'L' /* Magic number byte 2 */ -+ -+#define EI_MAG3 3 /* File identification byte 3 index */ -+#define ELFMAG3 'F' /* Magic number byte 3 */ -+ -+/* Conglomeration of the identification bytes, for easy testing as a word. */ -+#define ELFMAG "\177ELF" -+#define SELFMAG 4 -+ -+#define EI_CLASS 4 /* File class byte index */ -+#define ELFCLASSNONE 0 /* Invalid class */ -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+#define ELFCLASSNUM 3 -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATANONE 0 /* Invalid data encoding */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ -+#define ELFDATANUM 3 -+ -+#define EI_VERSION 6 /* File version byte index */ -+ /* Value must be EV_CURRENT */ -+ -+#define EI_OSABI 7 /* OS ABI identification */ -+#define ELFOSABI_NONE 0 /* UNIX System V ABI */ -+#define ELFOSABI_SYSV 0 /* Alias. */ -+#define ELFOSABI_HPUX 1 /* HP-UX */ -+#define ELFOSABI_NETBSD 2 /* NetBSD. */ -+#define ELFOSABI_LINUX 3 /* Linux. */ -+#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ -+#define ELFOSABI_AIX 7 /* IBM AIX. */ -+#define ELFOSABI_IRIX 8 /* SGI Irix. */ -+#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ -+#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ -+#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ -+#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ -+#define ELFOSABI_ARM 97 /* ARM */ -+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ -+ -+#define EI_ABIVERSION 8 /* ABI version */ -+ -+#define EI_PAD 9 /* Byte index of padding bytes */ -+ -+/* Legal values for e_type (object file type). */ -+ -+#define ET_NONE 0 /* No file type */ -+#define ET_REL 1 /* Relocatable file */ -+#define ET_EXEC 2 /* Executable file */ -+#define ET_DYN 3 /* Shared object file */ -+#define ET_CORE 4 /* Core file */ -+#define ET_NUM 5 /* Number of defined types */ -+#define ET_LOOS 0xfe00 /* OS-specific range start */ -+#define ET_HIOS 0xfeff /* OS-specific range end */ -+#define ET_LOPROC 0xff00 /* Processor-specific range start */ -+#define ET_HIPROC 0xffff /* Processor-specific range end */ -+ -+/* Legal values for e_machine (architecture). */ -+ -+#define EM_NONE 0 /* No machine */ -+#define EM_M32 1 /* AT&T WE 32100 */ -+#define EM_SPARC 2 /* SUN SPARC */ -+#define EM_386 3 /* Intel 80386 */ -+#define EM_68K 4 /* Motorola m68k family */ -+#define EM_88K 5 /* Motorola m88k family */ -+#define EM_860 7 /* Intel 80860 */ -+#define EM_MIPS 8 /* MIPS R3000 big-endian */ -+#define EM_S370 9 /* IBM System/370 */ -+#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ -+ -+#define EM_PARISC 15 /* HPPA */ -+#define EM_VPP500 17 /* Fujitsu VPP500 */ -+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ -+#define EM_960 19 /* Intel 80960 */ -+#define EM_PPC 20 /* PowerPC */ -+#define EM_PPC64 21 /* PowerPC 64-bit */ -+#define EM_S390 22 /* IBM S390 */ -+ -+#define EM_V800 36 /* NEC V800 series */ -+#define EM_FR20 37 /* Fujitsu FR20 */ -+#define EM_RH32 38 /* TRW RH-32 */ -+#define EM_RCE 39 /* Motorola RCE */ -+#define EM_ARM 40 /* ARM */ -+#define EM_FAKE_ALPHA 41 /* Digital Alpha */ -+#define EM_SH 42 /* Hitachi SH */ -+#define EM_SPARCV9 43 /* SPARC v9 64-bit */ -+#define EM_TRICORE 44 /* Siemens Tricore */ -+#define EM_ARC 45 /* Argonaut RISC Core */ -+#define EM_H8_300 46 /* Hitachi H8/300 */ -+#define EM_H8_300H 47 /* Hitachi H8/300H */ -+#define EM_H8S 48 /* Hitachi H8S */ -+#define EM_H8_500 49 /* Hitachi H8/500 */ -+#define EM_IA_64 50 /* Intel Merced */ -+#define EM_MIPS_X 51 /* Stanford MIPS-X */ -+#define EM_COLDFIRE 52 /* Motorola Coldfire */ -+#define EM_68HC12 53 /* Motorola M68HC12 */ -+#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -+#define EM_PCP 55 /* Siemens PCP */ -+#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -+#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -+#define EM_STARCORE 58 /* Motorola Start*Core processor */ -+#define EM_ME16 59 /* Toyota ME16 processor */ -+#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -+#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -+#define EM_X86_64 62 /* AMD x86-64 architecture */ -+#define EM_PDSP 63 /* Sony DSP Processor */ -+ -+#define EM_FX66 66 /* Siemens FX66 microcontroller */ -+#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -+#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -+#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -+#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -+#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -+#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -+#define EM_SVX 73 /* Silicon Graphics SVx */ -+#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ -+#define EM_VAX 75 /* Digital VAX */ -+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ -+#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ -+#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -+#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -+#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ -+#define EM_HUANY 81 /* Harvard University machine-independent object files */ -+#define EM_PRISM 82 /* SiTera Prism */ -+#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -+#define EM_FR30 84 /* Fujitsu FR30 */ -+#define EM_D10V 85 /* Mitsubishi D10V */ -+#define EM_D30V 86 /* Mitsubishi D30V */ -+#define EM_V850 87 /* NEC v850 */ -+#define EM_M32R 88 /* Mitsubishi M32R */ -+#define EM_MN10300 89 /* Matsushita MN10300 */ -+#define EM_MN10200 90 /* Matsushita MN10200 */ -+#define EM_PJ 91 /* picoJava */ -+#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ -+#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ -+#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ -+#define EM_NUM 95 -+ -+/* If it is necessary to assign new unofficial EM_* values, please -+ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the -+ chances of collision with official or non-GNU unofficial values. */ -+ -+#define EM_ALPHA 0x9026 -+ -+/* Legal values for e_version (version). */ -+ -+#define EV_NONE 0 /* Invalid ELF version */ -+#define EV_CURRENT 1 /* Current version */ -+#define EV_NUM 2 -+ -+/* Section header. */ -+ -+typedef struct -+{ -+ Elf32_Word sh_name; /* Section name (string tbl index) */ -+ Elf32_Word sh_type; /* Section type */ -+ Elf32_Word sh_flags; /* Section flags */ -+ Elf32_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf32_Off sh_offset; /* Section file offset */ -+ Elf32_Word sh_size; /* Section size in bytes */ -+ Elf32_Word sh_link; /* Link to another section */ -+ Elf32_Word sh_info; /* Additional section information */ -+ Elf32_Word sh_addralign; /* Section alignment */ -+ Elf32_Word sh_entsize; /* Entry size if section holds table */ -+} Elf32_Shdr; -+ -+typedef struct -+{ -+ Elf64_Word sh_name; /* Section name (string tbl index) */ -+ Elf64_Word sh_type; /* Section type */ -+ Elf64_Xword sh_flags; /* Section flags */ -+ Elf64_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf64_Off sh_offset; /* Section file offset */ -+ Elf64_Xword sh_size; /* Section size in bytes */ -+ Elf64_Word sh_link; /* Link to another section */ -+ Elf64_Word sh_info; /* Additional section information */ -+ Elf64_Xword sh_addralign; /* Section alignment */ -+ Elf64_Xword sh_entsize; /* Entry size if section holds table */ -+} Elf64_Shdr; -+ -+/* Special section indices. */ -+ -+#define SHN_UNDEF 0 /* Undefined section */ -+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ -+#define SHN_LOPROC 0xff00 /* Start of processor-specific */ -+#define SHN_HIPROC 0xff1f /* End of processor-specific */ -+#define SHN_LOOS 0xff20 /* Start of OS-specific */ -+#define SHN_HIOS 0xff3f /* End of OS-specific */ -+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ -+#define SHN_COMMON 0xfff2 /* Associated symbol is common */ -+#define SHN_XINDEX 0xffff /* Index is in extra table. */ -+#define SHN_HIRESERVE 0xffff /* End of reserved indices */ -+ -+/* Legal values for sh_type (section type). */ -+ -+#define SHT_NULL 0 /* Section header table entry unused */ -+#define SHT_PROGBITS 1 /* Program data */ -+#define SHT_SYMTAB 2 /* Symbol table */ -+#define SHT_STRTAB 3 /* String table */ -+#define SHT_RELA 4 /* Relocation entries with addends */ -+#define SHT_HASH 5 /* Symbol hash table */ -+#define SHT_DYNAMIC 6 /* Dynamic linking information */ -+#define SHT_NOTE 7 /* Notes */ -+#define SHT_NOBITS 8 /* Program space with no data (bss) */ -+#define SHT_REL 9 /* Relocation entries, no addends */ -+#define SHT_SHLIB 10 /* Reserved */ -+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ -+#define SHT_INIT_ARRAY 14 /* Array of constructors */ -+#define SHT_FINI_ARRAY 15 /* Array of destructors */ -+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ -+#define SHT_GROUP 17 /* Section group */ -+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ -+#define SHT_NUM 19 /* Number of defined types. */ -+#define SHT_LOOS 0x60000000 /* Start OS-specific */ -+#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ -+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ -+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ -+#define SHT_SUNW_move 0x6ffffffa -+#define SHT_SUNW_COMDAT 0x6ffffffb -+#define SHT_SUNW_syminfo 0x6ffffffc -+#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ -+#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ -+#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ -+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ -+#define SHT_HIOS 0x6fffffff /* End OS-specific type */ -+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define SHT_LOUSER 0x80000000 /* Start of application-specific */ -+#define SHT_HIUSER 0x8fffffff /* End of application-specific */ -+ -+/* Legal values for sh_flags (section flags). */ -+ -+#define SHF_WRITE (1 << 0) /* Writable */ -+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ -+#define SHF_EXECINSTR (1 << 2) /* Executable */ -+#define SHF_MERGE (1 << 4) /* Might be merged */ -+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ -+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ -+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ -+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling -+ required */ -+#define SHF_GROUP (1 << 9) /* Section is member of a group. */ -+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ -+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ -+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Section group handling. */ -+#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ -+ -+/* Symbol table entry. */ -+ -+typedef struct -+{ -+ Elf32_Word st_name; /* Symbol name (string tbl index) */ -+ Elf32_Addr st_value; /* Symbol value */ -+ Elf32_Word st_size; /* Symbol size */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf32_Section st_shndx; /* Section index */ -+} Elf32_Sym; -+ -+typedef struct -+{ -+ Elf64_Word st_name; /* Symbol name (string tbl index) */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf64_Section st_shndx; /* Section index */ -+ Elf64_Addr st_value; /* Symbol value */ -+ Elf64_Xword st_size; /* Symbol size */ -+} Elf64_Sym; -+ -+/* The syminfo section if available contains additional information about -+ every dynamic symbol. */ -+ -+typedef struct -+{ -+ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf32_Half si_flags; /* Per symbol flags */ -+} Elf32_Syminfo; -+ -+typedef struct -+{ -+ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf64_Half si_flags; /* Per symbol flags */ -+} Elf64_Syminfo; -+ -+/* Possible values for si_boundto. */ -+#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ -+#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ -+#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ -+ -+/* Possible bitmasks for si_flags. */ -+#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ -+#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ -+#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ -+#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy -+ loaded */ -+/* Syminfo version values. */ -+#define SYMINFO_NONE 0 -+#define SYMINFO_CURRENT 1 -+#define SYMINFO_NUM 2 -+ -+ -+/* How to extract and insert information held in the st_info field. */ -+ -+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -+#define ELF32_ST_TYPE(val) ((val) & 0xf) -+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -+ -+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ -+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) -+ -+/* Legal values for ST_BIND subfield of st_info (symbol binding). */ -+ -+#define STB_LOCAL 0 /* Local symbol */ -+#define STB_GLOBAL 1 /* Global symbol */ -+#define STB_WEAK 2 /* Weak symbol */ -+#define STB_NUM 3 /* Number of defined types. */ -+#define STB_LOOS 10 /* Start of OS-specific */ -+#define STB_HIOS 12 /* End of OS-specific */ -+#define STB_LOPROC 13 /* Start of processor-specific */ -+#define STB_HIPROC 15 /* End of processor-specific */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_NOTYPE 0 /* Symbol type is unspecified */ -+#define STT_OBJECT 1 /* Symbol is a data object */ -+#define STT_FUNC 2 /* Symbol is a code object */ -+#define STT_SECTION 3 /* Symbol associated with a section */ -+#define STT_FILE 4 /* Symbol's name is file name */ -+#define STT_COMMON 5 /* Symbol is a common data object */ -+#define STT_TLS 6 /* Symbol is thread-local data object*/ -+#define STT_NUM 7 /* Number of defined types. */ -+#define STT_LOOS 10 /* Start of OS-specific */ -+#define STT_HIOS 12 /* End of OS-specific */ -+#define STT_LOPROC 13 /* Start of processor-specific */ -+#define STT_HIPROC 15 /* End of processor-specific */ -+ -+ -+/* Symbol table indices are found in the hash buckets and chain table -+ of a symbol hash table section. This special index value indicates -+ the end of a chain, meaning no further symbols are found in that bucket. */ -+ -+#define STN_UNDEF 0 /* End of a chain. */ -+ -+ -+/* How to extract and insert information held in the st_other field. */ -+ -+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) -+ -+/* For ELF64 the definitions are the same. */ -+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) -+ -+/* Symbol visibility specification encoded in the st_other field. */ -+#define STV_DEFAULT 0 /* Default symbol visibility rules */ -+#define STV_INTERNAL 1 /* Processor specific hidden class */ -+#define STV_HIDDEN 2 /* Sym unavailable in other modules */ -+#define STV_PROTECTED 3 /* Not preemptible, not exported */ -+ -+ -+/* Relocation table entry without addend (in section of type SHT_REL). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+} Elf32_Rel; -+ -+/* I have seen two different definitions of the Elf64_Rel and -+ Elf64_Rela structures, so we'll leave them out until Novell (or -+ whoever) gets their act together. */ -+/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+} Elf64_Rel; -+ -+/* Relocation table entry with addend (in section of type SHT_RELA). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+ Elf32_Sword r_addend; /* Addend */ -+} Elf32_Rela; -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+ Elf64_Sxword r_addend; /* Addend */ -+} Elf64_Rela; -+ -+/* How to extract and insert information held in the r_info field. */ -+ -+#define ELF32_R_SYM(val) ((val) >> 8) -+#define ELF32_R_TYPE(val) ((val) & 0xff) -+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) -+ -+#define ELF64_R_SYM(i) ((i) >> 32) -+#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) -+ -+/* Program segment header. */ -+ -+typedef struct -+{ -+ Elf32_Word p_type; /* Segment type */ -+ Elf32_Off p_offset; /* Segment file offset */ -+ Elf32_Addr p_vaddr; /* Segment virtual address */ -+ Elf32_Addr p_paddr; /* Segment physical address */ -+ Elf32_Word p_filesz; /* Segment size in file */ -+ Elf32_Word p_memsz; /* Segment size in memory */ -+ Elf32_Word p_flags; /* Segment flags */ -+ Elf32_Word p_align; /* Segment alignment */ -+} Elf32_Phdr; -+ -+typedef struct -+{ -+ Elf64_Word p_type; /* Segment type */ -+ Elf64_Word p_flags; /* Segment flags */ -+ Elf64_Off p_offset; /* Segment file offset */ -+ Elf64_Addr p_vaddr; /* Segment virtual address */ -+ Elf64_Addr p_paddr; /* Segment physical address */ -+ Elf64_Xword p_filesz; /* Segment size in file */ -+ Elf64_Xword p_memsz; /* Segment size in memory */ -+ Elf64_Xword p_align; /* Segment alignment */ -+} Elf64_Phdr; -+ -+/* Legal values for p_type (segment type). */ -+ -+#define PT_NULL 0 /* Program header table entry unused */ -+#define PT_LOAD 1 /* Loadable program segment */ -+#define PT_DYNAMIC 2 /* Dynamic linking information */ -+#define PT_INTERP 3 /* Program interpreter */ -+#define PT_NOTE 4 /* Auxiliary information */ -+#define PT_SHLIB 5 /* Reserved */ -+#define PT_PHDR 6 /* Entry for header table itself */ -+#define PT_TLS 7 /* Thread-local storage segment */ -+#define PT_NUM 8 /* Number of defined types */ -+#define PT_LOOS 0x60000000 /* Start of OS-specific */ -+#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ -+#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ -+#define PT_LOSUNW 0x6ffffffa -+#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ -+#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ -+#define PT_HISUNW 0x6fffffff -+#define PT_HIOS 0x6fffffff /* End of OS-specific */ -+#define PT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define PT_HIPROC 0x7fffffff /* End of processor-specific */ -+ -+/* Legal values for p_flags (segment flags). */ -+ -+#define PF_X (1 << 0) /* Segment is executable */ -+#define PF_W (1 << 1) /* Segment is writable */ -+#define PF_R (1 << 2) /* Segment is readable */ -+#define PF_MASKOS 0x0ff00000 /* OS-specific */ -+#define PF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Legal values for note segment descriptor types for core files. */ -+ -+#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ -+#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ -+#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ -+#define NT_PRXREG 4 /* Contains copy of prxregset struct */ -+#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ -+#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ -+#define NT_AUXV 6 /* Contains copy of auxv array */ -+#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ -+#define NT_ASRS 8 /* Contains copy of asrset struct */ -+#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ -+#define NT_PSINFO 13 /* Contains copy of psinfo struct */ -+#define NT_PRCRED 14 /* Contains copy of prcred struct */ -+#define NT_UTSNAME 15 /* Contains copy of utsname struct */ -+#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ -+#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ -+#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ -+ -+/* Legal values for the note segment descriptor types for object files. */ -+ -+#define NT_VERSION 1 /* Contains a version string. */ -+ -+ -+/* Dynamic section entry. */ -+ -+typedef struct -+{ -+ Elf32_Sword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf32_Word d_val; /* Integer value */ -+ Elf32_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf32_Dyn; -+ -+typedef struct -+{ -+ Elf64_Sxword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf64_Xword d_val; /* Integer value */ -+ Elf64_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf64_Dyn; -+ -+/* Legal values for d_tag (dynamic entry type). */ -+ -+#define DT_NULL 0 /* Marks end of dynamic section */ -+#define DT_NEEDED 1 /* Name of needed library */ -+#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ -+#define DT_PLTGOT 3 /* Processor defined value */ -+#define DT_HASH 4 /* Address of symbol hash table */ -+#define DT_STRTAB 5 /* Address of string table */ -+#define DT_SYMTAB 6 /* Address of symbol table */ -+#define DT_RELA 7 /* Address of Rela relocs */ -+#define DT_RELASZ 8 /* Total size of Rela relocs */ -+#define DT_RELAENT 9 /* Size of one Rela reloc */ -+#define DT_STRSZ 10 /* Size of string table */ -+#define DT_SYMENT 11 /* Size of one symbol table entry */ -+#define DT_INIT 12 /* Address of init function */ -+#define DT_FINI 13 /* Address of termination function */ -+#define DT_SONAME 14 /* Name of shared object */ -+#define DT_RPATH 15 /* Library search path (deprecated) */ -+#define DT_SYMBOLIC 16 /* Start symbol search here */ -+#define DT_REL 17 /* Address of Rel relocs */ -+#define DT_RELSZ 18 /* Total size of Rel relocs */ -+#define DT_RELENT 19 /* Size of one Rel reloc */ -+#define DT_PLTREL 20 /* Type of reloc in PLT */ -+#define DT_DEBUG 21 /* For debugging; unspecified */ -+#define DT_TEXTREL 22 /* Reloc might modify .text */ -+#define DT_JMPREL 23 /* Address of PLT relocs */ -+#define DT_BIND_NOW 24 /* Process relocations of object */ -+#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ -+#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ -+#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ -+#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ -+#define DT_RUNPATH 29 /* Library search path */ -+#define DT_FLAGS 30 /* Flags for the object being loaded */ -+#define DT_ENCODING 32 /* Start of encoded range */ -+#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ -+#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ -+#define DT_NUM 34 /* Number used */ -+#define DT_LOOS 0x6000000d /* Start of OS-specific */ -+#define DT_HIOS 0x6ffff000 /* End of OS-specific */ -+#define DT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define DT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ -+ -+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the -+ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's -+ approach. */ -+#define DT_VALRNGLO 0x6ffffd00 -+#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ -+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ -+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ -+#define DT_CHECKSUM 0x6ffffdf8 -+#define DT_PLTPADSZ 0x6ffffdf9 -+#define DT_MOVEENT 0x6ffffdfa -+#define DT_MOVESZ 0x6ffffdfb -+#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ -+#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting -+ the following DT_* entry. */ -+#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ -+#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ -+#define DT_VALRNGHI 0x6ffffdff -+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ -+#define DT_VALNUM 12 -+ -+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the -+ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. -+ -+ If any adjustment is made to the ELF object after it has been -+ built these entries will need to be adjusted. */ -+#define DT_ADDRRNGLO 0x6ffffe00 -+#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ -+#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ -+#define DT_CONFIG 0x6ffffefa /* Configuration information. */ -+#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ -+#define DT_AUDIT 0x6ffffefc /* Object auditing. */ -+#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ -+#define DT_MOVETAB 0x6ffffefe /* Move table. */ -+#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ -+#define DT_ADDRRNGHI 0x6ffffeff -+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ -+#define DT_ADDRNUM 10 -+ -+/* The versioning entry types. The next are defined as part of the -+ GNU extension. */ -+#define DT_VERSYM 0x6ffffff0 -+ -+#define DT_RELACOUNT 0x6ffffff9 -+#define DT_RELCOUNT 0x6ffffffa -+ -+/* These were chosen by Sun. */ -+#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ -+#define DT_VERDEF 0x6ffffffc /* Address of version definition -+ table */ -+#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ -+#define DT_VERNEED 0x6ffffffe /* Address of table with needed -+ versions */ -+#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ -+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ -+#define DT_VERSIONTAGNUM 16 -+ -+/* Sun added these machine-independent extensions in the "processor-specific" -+ range. Be compatible. */ -+#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ -+#define DT_FILTER 0x7fffffff /* Shared object to get values from */ -+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -+#define DT_EXTRANUM 3 -+ -+/* Values of `d_un.d_val' in the DT_FLAGS entry. */ -+#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ -+#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ -+#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ -+#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ -+#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ -+ -+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 -+ entry in the dynamic section. */ -+#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ -+#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ -+#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ -+#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ -+#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ -+#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ -+#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ -+#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ -+#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ -+#define DF_1_TRANS 0x00000200 -+#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ -+#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ -+#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ -+#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ -+#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ -+#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ -+#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ -+ -+/* Flags for the feature selection in DT_FEATURE_1. */ -+#define DTF_1_PARINIT 0x00000001 -+#define DTF_1_CONFEXP 0x00000002 -+ -+/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ -+#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ -+#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not -+ generally available. */ -+ -+/* Version definition sections. */ -+ -+typedef struct -+{ -+ Elf32_Half vd_version; /* Version revision */ -+ Elf32_Half vd_flags; /* Version information */ -+ Elf32_Half vd_ndx; /* Version Index */ -+ Elf32_Half vd_cnt; /* Number of associated aux entries */ -+ Elf32_Word vd_hash; /* Version name hash value */ -+ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf32_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf32_Verdef; -+ -+typedef struct -+{ -+ Elf64_Half vd_version; /* Version revision */ -+ Elf64_Half vd_flags; /* Version information */ -+ Elf64_Half vd_ndx; /* Version Index */ -+ Elf64_Half vd_cnt; /* Number of associated aux entries */ -+ Elf64_Word vd_hash; /* Version name hash value */ -+ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf64_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf64_Verdef; -+ -+ -+/* Legal values for vd_version (version revision). */ -+#define VER_DEF_NONE 0 /* No version */ -+#define VER_DEF_CURRENT 1 /* Current version */ -+#define VER_DEF_NUM 2 /* Given version number */ -+ -+/* Legal values for vd_flags (version information flags). */ -+#define VER_FLG_BASE 0x1 /* Version definition of file itself */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+/* Versym symbol index values. */ -+#define VER_NDX_LOCAL 0 /* Symbol is local. */ -+#define VER_NDX_GLOBAL 1 /* Symbol is global. */ -+#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ -+#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ -+ -+/* Auxialiary version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vda_name; /* Version or dependency names */ -+ Elf32_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf32_Verdaux; -+ -+typedef struct -+{ -+ Elf64_Word vda_name; /* Version or dependency names */ -+ Elf64_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf64_Verdaux; -+ -+ -+/* Version dependency section. */ -+ -+typedef struct -+{ -+ Elf32_Half vn_version; /* Version of structure */ -+ Elf32_Half vn_cnt; /* Number of associated aux entries */ -+ Elf32_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf32_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf32_Verneed; -+ -+typedef struct -+{ -+ Elf64_Half vn_version; /* Version of structure */ -+ Elf64_Half vn_cnt; /* Number of associated aux entries */ -+ Elf64_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf64_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf64_Verneed; -+ -+ -+/* Legal values for vn_version (version revision). */ -+#define VER_NEED_NONE 0 /* No version */ -+#define VER_NEED_CURRENT 1 /* Current version */ -+#define VER_NEED_NUM 2 /* Given version number */ -+ -+/* Auxiliary needed version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vna_hash; /* Hash value of dependency name */ -+ Elf32_Half vna_flags; /* Dependency specific information */ -+ Elf32_Half vna_other; /* Unused */ -+ Elf32_Word vna_name; /* Dependency name string offset */ -+ Elf32_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf32_Vernaux; -+ -+typedef struct -+{ -+ Elf64_Word vna_hash; /* Hash value of dependency name */ -+ Elf64_Half vna_flags; /* Dependency specific information */ -+ Elf64_Half vna_other; /* Unused */ -+ Elf64_Word vna_name; /* Dependency name string offset */ -+ Elf64_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf64_Vernaux; -+ -+ -+/* Legal values for vna_flags. */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+ -+/* Auxiliary vector. */ -+ -+/* This vector is normally only used by the program interpreter. The -+ usual definition in an ABI supplement uses the name auxv_t. The -+ vector is not usually defined in a standard file, but it -+ can't hurt. We rename it to avoid conflicts. The sizes of these -+ types are an arrangement between the exec server and the program -+ interpreter, so we don't fully specify them here. */ -+ -+typedef struct -+{ -+ int a_type; /* Entry type */ -+ union -+ { -+ long int a_val; /* Integer value */ -+ void *a_ptr; /* Pointer value */ -+ void (*a_fcn) (void); /* Function pointer value */ -+ } a_un; -+} Elf32_auxv_t; -+ -+typedef struct -+{ -+ long int a_type; /* Entry type */ -+ union -+ { -+ long int a_val; /* Integer value */ -+ void *a_ptr; /* Pointer value */ -+ void (*a_fcn) (void); /* Function pointer value */ -+ } a_un; -+} Elf64_auxv_t; -+ -+/* Legal values for a_type (entry type). */ -+ -+#define AT_NULL 0 /* End of vector */ -+#define AT_IGNORE 1 /* Entry should be ignored */ -+#define AT_EXECFD 2 /* File descriptor of program */ -+#define AT_PHDR 3 /* Program headers for program */ -+#define AT_PHENT 4 /* Size of program header entry */ -+#define AT_PHNUM 5 /* Number of program headers */ -+#define AT_PAGESZ 6 /* System page size */ -+#define AT_BASE 7 /* Base address of interpreter */ -+#define AT_FLAGS 8 /* Flags */ -+#define AT_ENTRY 9 /* Entry point of program */ -+#define AT_NOTELF 10 /* Program is not ELF */ -+#define AT_UID 11 /* Real uid */ -+#define AT_EUID 12 /* Effective uid */ -+#define AT_GID 13 /* Real gid */ -+#define AT_EGID 14 /* Effective gid */ -+#define AT_CLKTCK 17 /* Frequency of times() */ -+ -+/* Some more special a_type values describing the hardware. */ -+#define AT_PLATFORM 15 /* String identifying platform. */ -+#define AT_HWCAP 16 /* Machine dependent hints about -+ processor capabilities. */ -+ -+/* This entry gives some information about the FPU initialization -+ performed by the kernel. */ -+#define AT_FPUCW 18 /* Used FPU control word. */ -+ -+/* Cache block sizes. */ -+#define AT_DCACHEBSIZE 19 /* Data cache block size. */ -+#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ -+#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ -+ -+/* A special ignored value for PPC, used by the kernel to control the -+ interpretation of the AUXV. Must be > 16. */ -+#define AT_IGNOREPPC 22 /* Entry should be ignored. */ -+ -+#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ -+ -+/* Pointer to the global system page used for system calls and other -+ nice things. */ -+#define AT_SYSINFO 32 -+#define AT_SYSINFO_EHDR 33 -+ -+ -+/* Note section contents. Each entry in the note section begins with -+ a header of a fixed form. */ -+ -+typedef struct -+{ -+ Elf32_Word n_namesz; /* Length of the note's name. */ -+ Elf32_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf32_Word n_type; /* Type of the note. */ -+} Elf32_Nhdr; -+ -+typedef struct -+{ -+ Elf64_Word n_namesz; /* Length of the note's name. */ -+ Elf64_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf64_Word n_type; /* Type of the note. */ -+} Elf64_Nhdr; -+ -+/* Known names of notes. */ -+ -+/* Solaris entries in the note section have this name. */ -+#define ELF_NOTE_SOLARIS "SUNW Solaris" -+ -+/* Note entries for GNU systems have this name. */ -+#define ELF_NOTE_GNU "GNU" -+ -+ -+/* Defined types of notes for Solaris. */ -+ -+/* Value of descriptor (one word) is desired pagesize for the binary. */ -+#define ELF_NOTE_PAGESIZE_HINT 1 -+ -+ -+/* Defined note types for GNU systems. */ -+ -+/* ABI information. The descriptor consists of words: -+ word 0: OS descriptor -+ word 1: major version of the ABI -+ word 2: minor version of the ABI -+ word 3: subminor version of the ABI -+*/ -+#define ELF_NOTE_ABI 1 -+ -+/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI -+ note section entry. */ -+#define ELF_NOTE_OS_LINUX 0 -+#define ELF_NOTE_OS_GNU 1 -+#define ELF_NOTE_OS_SOLARIS2 2 -+#define ELF_NOTE_OS_FREEBSD 3 -+ -+ -+/* Move records. */ -+typedef struct -+{ -+ Elf32_Xword m_value; /* Symbol value. */ -+ Elf32_Word m_info; /* Size and index. */ -+ Elf32_Word m_poffset; /* Symbol offset. */ -+ Elf32_Half m_repeat; /* Repeat count. */ -+ Elf32_Half m_stride; /* Stride info. */ -+} Elf32_Move; -+ -+typedef struct -+{ -+ Elf64_Xword m_value; /* Symbol value. */ -+ Elf64_Xword m_info; /* Size and index. */ -+ Elf64_Xword m_poffset; /* Symbol offset. */ -+ Elf64_Half m_repeat; /* Repeat count. */ -+ Elf64_Half m_stride; /* Stride info. */ -+} Elf64_Move; -+ -+/* Macro to construct move records. */ -+#define ELF32_M_SYM(info) ((info) >> 8) -+#define ELF32_M_SIZE(info) ((unsigned char) (info)) -+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) -+ -+#define ELF64_M_SYM(info) ELF32_M_SYM (info) -+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) -+ -+ -+/* Motorola 68k specific definitions. */ -+ -+/* Values for Elf32_Ehdr.e_flags. */ -+#define EF_CPU32 0x00810000 -+ -+/* m68k relocs. */ -+ -+#define R_68K_NONE 0 /* No reloc */ -+#define R_68K_32 1 /* Direct 32 bit */ -+#define R_68K_16 2 /* Direct 16 bit */ -+#define R_68K_8 3 /* Direct 8 bit */ -+#define R_68K_PC32 4 /* PC relative 32 bit */ -+#define R_68K_PC16 5 /* PC relative 16 bit */ -+#define R_68K_PC8 6 /* PC relative 8 bit */ -+#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ -+#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ -+#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ -+#define R_68K_GOT32O 10 /* 32 bit GOT offset */ -+#define R_68K_GOT16O 11 /* 16 bit GOT offset */ -+#define R_68K_GOT8O 12 /* 8 bit GOT offset */ -+#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ -+#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ -+#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ -+#define R_68K_PLT32O 16 /* 32 bit PLT offset */ -+#define R_68K_PLT16O 17 /* 16 bit PLT offset */ -+#define R_68K_PLT8O 18 /* 8 bit PLT offset */ -+#define R_68K_COPY 19 /* Copy symbol at runtime */ -+#define R_68K_GLOB_DAT 20 /* Create GOT entry */ -+#define R_68K_JMP_SLOT 21 /* Create PLT entry */ -+#define R_68K_RELATIVE 22 /* Adjust by program base */ -+/* Keep this the last entry. */ -+#define R_68K_NUM 23 -+ -+/* Intel 80386 specific definitions. */ -+ -+/* i386 relocs. */ -+ -+#define R_386_NONE 0 /* No reloc */ -+#define R_386_32 1 /* Direct 32 bit */ -+#define R_386_PC32 2 /* PC relative 32 bit */ -+#define R_386_GOT32 3 /* 32 bit GOT entry */ -+#define R_386_PLT32 4 /* 32 bit PLT address */ -+#define R_386_COPY 5 /* Copy symbol at runtime */ -+#define R_386_GLOB_DAT 6 /* Create GOT entry */ -+#define R_386_JMP_SLOT 7 /* Create PLT entry */ -+#define R_386_RELATIVE 8 /* Adjust by program base */ -+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ -+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ -+#define R_386_32PLT 11 -+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ -+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS -+ block offset */ -+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block -+ offset */ -+#define R_386_TLS_LE 17 /* Offset relative to static TLS -+ block */ -+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of -+ general dynamic thread local data */ -+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of -+ local dynamic thread local data -+ in LE code */ -+#define R_386_16 20 -+#define R_386_PC16 21 -+#define R_386_8 22 -+#define R_386_PC8 23 -+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic -+ thread local data */ -+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ -+#define R_386_TLS_GD_CALL 26 /* Relocation for call to -+ __tls_get_addr() */ -+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ -+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic -+ thread local data in LE code */ -+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ -+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to -+ __tls_get_addr() in LDM code */ -+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ -+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ -+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS -+ block offset */ -+#define R_386_TLS_LE_32 34 /* Negated offset relative to static -+ TLS block */ -+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ -+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ -+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ -+/* Keep this the last entry. */ -+#define R_386_NUM 38 -+ -+/* SUN SPARC specific definitions. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_REGISTER 13 /* Global register reserved to app. */ -+ -+/* Values for Elf64_Ehdr.e_flags. */ -+ -+#define EF_SPARCV9_MM 3 -+#define EF_SPARCV9_TSO 0 -+#define EF_SPARCV9_PSO 1 -+#define EF_SPARCV9_RMO 2 -+#define EF_SPARC_LEDATA 0x800000 /* little endian data */ -+#define EF_SPARC_EXT_MASK 0xFFFF00 -+#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ -+#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ -+#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ -+#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ -+ -+/* SPARC relocs. */ -+ -+#define R_SPARC_NONE 0 /* No reloc */ -+#define R_SPARC_8 1 /* Direct 8 bit */ -+#define R_SPARC_16 2 /* Direct 16 bit */ -+#define R_SPARC_32 3 /* Direct 32 bit */ -+#define R_SPARC_DISP8 4 /* PC relative 8 bit */ -+#define R_SPARC_DISP16 5 /* PC relative 16 bit */ -+#define R_SPARC_DISP32 6 /* PC relative 32 bit */ -+#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ -+#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ -+#define R_SPARC_HI22 9 /* High 22 bit */ -+#define R_SPARC_22 10 /* Direct 22 bit */ -+#define R_SPARC_13 11 /* Direct 13 bit */ -+#define R_SPARC_LO10 12 /* Truncated 10 bit */ -+#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ -+#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ -+#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ -+#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ -+#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ -+#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ -+#define R_SPARC_COPY 19 /* Copy symbol at runtime */ -+#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ -+#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ -+#define R_SPARC_RELATIVE 22 /* Adjust by program base */ -+#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ -+ -+/* Additional Sparc64 relocs. */ -+ -+#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ -+#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ -+#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ -+#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ -+#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ -+#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ -+#define R_SPARC_10 30 /* Direct 10 bit */ -+#define R_SPARC_11 31 /* Direct 11 bit */ -+#define R_SPARC_64 32 /* Direct 64 bit */ -+#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ -+#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ -+#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ -+#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ -+#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ -+#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ -+#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ -+#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ -+#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ -+#define R_SPARC_7 43 /* Direct 7 bit */ -+#define R_SPARC_5 44 /* Direct 5 bit */ -+#define R_SPARC_6 45 /* Direct 6 bit */ -+#define R_SPARC_DISP64 46 /* PC relative 64 bit */ -+#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ -+#define R_SPARC_HIX22 48 /* High 22 bit complemented */ -+#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ -+#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ -+#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ -+#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ -+#define R_SPARC_REGISTER 53 /* Global register usage */ -+#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ -+#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ -+#define R_SPARC_TLS_GD_HI22 56 -+#define R_SPARC_TLS_GD_LO10 57 -+#define R_SPARC_TLS_GD_ADD 58 -+#define R_SPARC_TLS_GD_CALL 59 -+#define R_SPARC_TLS_LDM_HI22 60 -+#define R_SPARC_TLS_LDM_LO10 61 -+#define R_SPARC_TLS_LDM_ADD 62 -+#define R_SPARC_TLS_LDM_CALL 63 -+#define R_SPARC_TLS_LDO_HIX22 64 -+#define R_SPARC_TLS_LDO_LOX10 65 -+#define R_SPARC_TLS_LDO_ADD 66 -+#define R_SPARC_TLS_IE_HI22 67 -+#define R_SPARC_TLS_IE_LO10 68 -+#define R_SPARC_TLS_IE_LD 69 -+#define R_SPARC_TLS_IE_LDX 70 -+#define R_SPARC_TLS_IE_ADD 71 -+#define R_SPARC_TLS_LE_HIX22 72 -+#define R_SPARC_TLS_LE_LOX10 73 -+#define R_SPARC_TLS_DTPMOD32 74 -+#define R_SPARC_TLS_DTPMOD64 75 -+#define R_SPARC_TLS_DTPOFF32 76 -+#define R_SPARC_TLS_DTPOFF64 77 -+#define R_SPARC_TLS_TPOFF32 78 -+#define R_SPARC_TLS_TPOFF64 79 -+/* Keep this the last entry. */ -+#define R_SPARC_NUM 80 -+ -+/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ -+ -+#define DT_SPARC_REGISTER 0x70000001 -+#define DT_SPARC_NUM 2 -+ -+/* Bits present in AT_HWCAP, primarily for Sparc32. */ -+ -+#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ -+#define HWCAP_SPARC_STBAR 2 -+#define HWCAP_SPARC_SWAP 4 -+#define HWCAP_SPARC_MULDIV 8 -+#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ -+#define HWCAP_SPARC_ULTRA3 32 -+ -+/* MIPS R3000 specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ -+#define EF_MIPS_PIC 2 /* Contains PIC code */ -+#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ -+#define EF_MIPS_XGOT 8 -+#define EF_MIPS_64BIT_WHIRL 16 -+#define EF_MIPS_ABI2 32 -+#define EF_MIPS_ABI_ON32 64 -+#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ -+ -+/* Legal values for MIPS architecture level. */ -+ -+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* The following are non-official names and should not be used. */ -+ -+#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* Special section indices. */ -+ -+#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ -+#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ -+#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ -+#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ -+#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ -+#define SHT_MIPS_MSYM 0x70000001 -+#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ -+#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ -+#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ -+#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ -+#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ -+#define SHT_MIPS_PACKAGE 0x70000007 -+#define SHT_MIPS_PACKSYM 0x70000008 -+#define SHT_MIPS_RELD 0x70000009 -+#define SHT_MIPS_IFACE 0x7000000b -+#define SHT_MIPS_CONTENT 0x7000000c -+#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ -+#define SHT_MIPS_SHDR 0x70000010 -+#define SHT_MIPS_FDESC 0x70000011 -+#define SHT_MIPS_EXTSYM 0x70000012 -+#define SHT_MIPS_DENSE 0x70000013 -+#define SHT_MIPS_PDESC 0x70000014 -+#define SHT_MIPS_LOCSYM 0x70000015 -+#define SHT_MIPS_AUXSYM 0x70000016 -+#define SHT_MIPS_OPTSYM 0x70000017 -+#define SHT_MIPS_LOCSTR 0x70000018 -+#define SHT_MIPS_LINE 0x70000019 -+#define SHT_MIPS_RFDESC 0x7000001a -+#define SHT_MIPS_DELTASYM 0x7000001b -+#define SHT_MIPS_DELTAINST 0x7000001c -+#define SHT_MIPS_DELTACLASS 0x7000001d -+#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ -+#define SHT_MIPS_DELTADECL 0x7000001f -+#define SHT_MIPS_SYMBOL_LIB 0x70000020 -+#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ -+#define SHT_MIPS_TRANSLATE 0x70000022 -+#define SHT_MIPS_PIXIE 0x70000023 -+#define SHT_MIPS_XLATE 0x70000024 -+#define SHT_MIPS_XLATE_DEBUG 0x70000025 -+#define SHT_MIPS_WHIRL 0x70000026 -+#define SHT_MIPS_EH_REGION 0x70000027 -+#define SHT_MIPS_XLATE_OLD 0x70000028 -+#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ -+#define SHF_MIPS_MERGE 0x20000000 -+#define SHF_MIPS_ADDR 0x40000000 -+#define SHF_MIPS_STRINGS 0x80000000 -+#define SHF_MIPS_NOSTRIP 0x08000000 -+#define SHF_MIPS_LOCAL 0x04000000 -+#define SHF_MIPS_NAMES 0x02000000 -+#define SHF_MIPS_NODUPE 0x01000000 -+ -+ -+/* Symbol tables. */ -+ -+/* MIPS specific values for `st_other'. */ -+#define STO_MIPS_DEFAULT 0x0 -+#define STO_MIPS_INTERNAL 0x1 -+#define STO_MIPS_HIDDEN 0x2 -+#define STO_MIPS_PROTECTED 0x3 -+#define STO_MIPS_SC_ALIGN_UNUSED 0xff -+ -+/* MIPS specific values for `st_info'. */ -+#define STB_MIPS_SPLIT_COMMON 13 -+ -+/* Entries found in sections of type SHT_MIPS_GPTAB. */ -+ -+typedef union -+{ -+ struct -+ { -+ Elf32_Word gt_current_g_value; /* -G value used for compilation */ -+ Elf32_Word gt_unused; /* Not used */ -+ } gt_header; /* First entry in section */ -+ struct -+ { -+ Elf32_Word gt_g_value; /* If this value were used for -G */ -+ Elf32_Word gt_bytes; /* This many bytes would be used */ -+ } gt_entry; /* Subsequent entries in section */ -+} Elf32_gptab; -+ -+/* Entry found in sections of type SHT_MIPS_REGINFO. */ -+ -+typedef struct -+{ -+ Elf32_Word ri_gprmask; /* General registers used */ -+ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ -+ Elf32_Sword ri_gp_value; /* $gp register value */ -+} Elf32_RegInfo; -+ -+/* Entries found in sections of type SHT_MIPS_OPTIONS. */ -+ -+typedef struct -+{ -+ unsigned char kind; /* Determines interpretation of the -+ variable part of descriptor. */ -+ unsigned char size; /* Size of descriptor, including header. */ -+ Elf32_Section section; /* Section header index of section affected, -+ 0 for global options. */ -+ Elf32_Word info; /* Kind-specific information. */ -+} Elf_Options; -+ -+/* Values for `kind' field in Elf_Options. */ -+ -+#define ODK_NULL 0 /* Undefined. */ -+#define ODK_REGINFO 1 /* Register usage information. */ -+#define ODK_EXCEPTIONS 2 /* Exception processing options. */ -+#define ODK_PAD 3 /* Section padding options. */ -+#define ODK_HWPATCH 4 /* Hardware workarounds performed */ -+#define ODK_FILL 5 /* record the fill value used by the linker. */ -+#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ -+#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ -+#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ -+ -+/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ -+ -+#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ -+#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ -+#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ -+#define OEX_SMM 0x20000 /* Force sequential memory mode? */ -+#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ -+#define OEX_PRECISEFP OEX_FPDBUG -+#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ -+ -+#define OEX_FPU_INVAL 0x10 -+#define OEX_FPU_DIV0 0x08 -+#define OEX_FPU_OFLO 0x04 -+#define OEX_FPU_UFLO 0x02 -+#define OEX_FPU_INEX 0x01 -+ -+/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ -+ -+#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ -+#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ -+#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ -+#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ -+ -+#define OPAD_PREFIX 0x1 -+#define OPAD_POSTFIX 0x2 -+#define OPAD_SYMBOL 0x4 -+ -+/* Entry found in `.options' section. */ -+ -+typedef struct -+{ -+ Elf32_Word hwp_flags1; /* Extra flags. */ -+ Elf32_Word hwp_flags2; /* Extra flags. */ -+} Elf_Options_Hw; -+ -+/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ -+ -+#define OHWA0_R4KEOP_CHECKED 0x00000001 -+#define OHWA1_R4KEOP_CLEAN 0x00000002 -+ -+/* MIPS relocs. */ -+ -+#define R_MIPS_NONE 0 /* No reloc */ -+#define R_MIPS_16 1 /* Direct 16 bit */ -+#define R_MIPS_32 2 /* Direct 32 bit */ -+#define R_MIPS_REL32 3 /* PC relative 32 bit */ -+#define R_MIPS_26 4 /* Direct 26 bit shifted */ -+#define R_MIPS_HI16 5 /* High 16 bit */ -+#define R_MIPS_LO16 6 /* Low 16 bit */ -+#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ -+#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ -+#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ -+#define R_MIPS_PC16 10 /* PC relative 16 bit */ -+#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ -+#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ -+ -+#define R_MIPS_SHIFT5 16 -+#define R_MIPS_SHIFT6 17 -+#define R_MIPS_64 18 -+#define R_MIPS_GOT_DISP 19 -+#define R_MIPS_GOT_PAGE 20 -+#define R_MIPS_GOT_OFST 21 -+#define R_MIPS_GOT_HI16 22 -+#define R_MIPS_GOT_LO16 23 -+#define R_MIPS_SUB 24 -+#define R_MIPS_INSERT_A 25 -+#define R_MIPS_INSERT_B 26 -+#define R_MIPS_DELETE 27 -+#define R_MIPS_HIGHER 28 -+#define R_MIPS_HIGHEST 29 -+#define R_MIPS_CALL_HI16 30 -+#define R_MIPS_CALL_LO16 31 -+#define R_MIPS_SCN_DISP 32 -+#define R_MIPS_REL16 33 -+#define R_MIPS_ADD_IMMEDIATE 34 -+#define R_MIPS_PJUMP 35 -+#define R_MIPS_RELGOT 36 -+#define R_MIPS_JALR 37 -+/* Keep this the last entry. */ -+#define R_MIPS_NUM 38 -+ -+/* Legal values for p_type field of Elf32_Phdr. */ -+ -+#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ -+#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ -+#define PT_MIPS_OPTIONS 0x70000002 -+ -+/* Special program header types. */ -+ -+#define PF_MIPS_LOCAL 0x10000000 -+ -+/* Legal values for d_tag field of Elf32_Dyn. */ -+ -+#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ -+#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ -+#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ -+#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ -+#define DT_MIPS_FLAGS 0x70000005 /* Flags */ -+#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ -+#define DT_MIPS_MSYM 0x70000007 -+#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ -+#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ -+#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ -+#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ -+#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ -+#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ -+#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ -+#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ -+#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ -+#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ -+#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ -+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in -+ DT_MIPS_DELTA_CLASS. */ -+#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ -+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in -+ DT_MIPS_DELTA_INSTANCE. */ -+#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ -+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in -+ DT_MIPS_DELTA_RELOC. */ -+#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta -+ relocations refer to. */ -+#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in -+ DT_MIPS_DELTA_SYM. */ -+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the -+ class declaration. */ -+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in -+ DT_MIPS_DELTA_CLASSSYM. */ -+#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ -+#define DT_MIPS_PIXIE_INIT 0x70000023 -+#define DT_MIPS_SYMBOL_LIB 0x70000024 -+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -+#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -+#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ -+#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ -+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -+#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ -+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve -+ function stored in GOT. */ -+#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added -+ by rld on dlopen() calls. */ -+#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ -+#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ -+#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ -+#define DT_MIPS_NUM 0x32 -+ -+/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ -+ -+#define RHF_NONE 0 /* No flags */ -+#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ -+#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ -+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ -+#define RHF_NO_MOVE (1 << 3) -+#define RHF_SGI_ONLY (1 << 4) -+#define RHF_GUARANTEE_INIT (1 << 5) -+#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -+#define RHF_GUARANTEE_START_INIT (1 << 7) -+#define RHF_PIXIE (1 << 8) -+#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -+#define RHF_REQUICKSTART (1 << 10) -+#define RHF_REQUICKSTARTED (1 << 11) -+#define RHF_CORD (1 << 12) -+#define RHF_NO_UNRES_UNDEF (1 << 13) -+#define RHF_RLD_ORDER_SAFE (1 << 14) -+ -+/* Entries found in sections of type SHT_MIPS_LIBLIST. */ -+ -+typedef struct -+{ -+ Elf32_Word l_name; /* Name (string table index) */ -+ Elf32_Word l_time_stamp; /* Timestamp */ -+ Elf32_Word l_checksum; /* Checksum */ -+ Elf32_Word l_version; /* Interface version */ -+ Elf32_Word l_flags; /* Flags */ -+} Elf32_Lib; -+ -+typedef struct -+{ -+ Elf64_Word l_name; /* Name (string table index) */ -+ Elf64_Word l_time_stamp; /* Timestamp */ -+ Elf64_Word l_checksum; /* Checksum */ -+ Elf64_Word l_version; /* Interface version */ -+ Elf64_Word l_flags; /* Flags */ -+} Elf64_Lib; -+ -+ -+/* Legal values for l_flags. */ -+ -+#define LL_NONE 0 -+#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ -+#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ -+#define LL_REQUIRE_MINOR (1 << 2) -+#define LL_EXPORTS (1 << 3) -+#define LL_DELAY_LOAD (1 << 4) -+#define LL_DELTA (1 << 5) -+ -+/* Entries found in sections of type SHT_MIPS_CONFLICT. */ -+ -+typedef Elf32_Addr Elf32_Conflict; -+ -+ -+/* HPPA specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ -+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ -+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ -+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ -+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch -+ prediction. */ -+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ -+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ -+ -+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ -+ -+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ -+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ -+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ -+ -+/* Additional section indeces. */ -+ -+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared -+ symbols in ANSI C. */ -+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ -+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ -+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ -+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ -+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ -+ -+#define STT_HP_OPAQUE (STT_LOOS + 0x1) -+#define STT_HP_STUB (STT_LOOS + 0x2) -+ -+/* HPPA relocs. */ -+ -+#define R_PARISC_NONE 0 /* No reloc. */ -+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ -+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ -+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ -+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ -+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ -+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ -+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ -+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ -+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ -+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ -+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ -+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ -+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ -+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ -+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ -+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ -+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ -+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ -+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ -+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ -+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ -+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ -+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ -+#define R_PARISC_FPTR64 64 /* 64 bits function address. */ -+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ -+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ -+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ -+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ -+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ -+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ -+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ -+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ -+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ -+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ -+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ -+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ -+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ -+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ -+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ -+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LORESERVE 128 -+#define R_PARISC_COPY 128 /* Copy relocation. */ -+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ -+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ -+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ -+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ -+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ -+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ -+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ -+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ -+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_HIRESERVE 255 -+ -+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PT_HP_TLS (PT_LOOS + 0x0) -+#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -+#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -+#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -+#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -+#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -+#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -+#define PT_HP_PARALLEL (PT_LOOS + 0x10) -+#define PT_HP_FASTBIND (PT_LOOS + 0x11) -+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -+#define PT_HP_STACK (PT_LOOS + 0x14) -+ -+#define PT_PARISC_ARCHEXT 0x70000000 -+#define PT_PARISC_UNWIND 0x70000001 -+ -+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PF_PARISC_SBP 0x08000000 -+ -+#define PF_HP_PAGE_SIZE 0x00100000 -+#define PF_HP_FAR_SHARED 0x00200000 -+#define PF_HP_NEAR_SHARED 0x00400000 -+#define PF_HP_CODE 0x01000000 -+#define PF_HP_MODIFY 0x02000000 -+#define PF_HP_LAZYSWAP 0x04000000 -+#define PF_HP_SBP 0x08000000 -+ -+ -+/* Alpha specific definitions. */ -+ -+/* Legal values for e_flags field of Elf64_Ehdr. */ -+ -+#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ -+#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ -+ -+/* Legal values for sh_type field of Elf64_Shdr. */ -+ -+/* These two are primerily concerned with ECOFF debugging info. */ -+#define SHT_ALPHA_DEBUG 0x70000001 -+#define SHT_ALPHA_REGINFO 0x70000002 -+ -+/* Legal values for sh_flags field of Elf64_Shdr. */ -+ -+#define SHF_ALPHA_GPREL 0x10000000 -+ -+/* Legal values for st_other field of Elf64_Sym. */ -+#define STO_ALPHA_NOPV 0x80 /* No PV required. */ -+#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ -+ -+/* Alpha relocs. */ -+ -+#define R_ALPHA_NONE 0 /* No reloc */ -+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ -+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ -+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ -+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ -+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ -+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ -+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ -+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ -+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ -+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ -+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ -+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ -+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ -+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ -+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ -+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ -+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ -+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ -+#define R_ALPHA_TLS_GD_HI 28 -+#define R_ALPHA_TLSGD 29 -+#define R_ALPHA_TLS_LDM 30 -+#define R_ALPHA_DTPMOD64 31 -+#define R_ALPHA_GOTDTPREL 32 -+#define R_ALPHA_DTPREL64 33 -+#define R_ALPHA_DTPRELHI 34 -+#define R_ALPHA_DTPRELLO 35 -+#define R_ALPHA_DTPREL16 36 -+#define R_ALPHA_GOTTPREL 37 -+#define R_ALPHA_TPREL64 38 -+#define R_ALPHA_TPRELHI 39 -+#define R_ALPHA_TPRELLO 40 -+#define R_ALPHA_TPREL16 41 -+/* Keep this the last entry. */ -+#define R_ALPHA_NUM 46 -+ -+/* Magic values of the LITUSE relocation addend. */ -+#define LITUSE_ALPHA_ADDR 0 -+#define LITUSE_ALPHA_BASE 1 -+#define LITUSE_ALPHA_BYTOFF 2 -+#define LITUSE_ALPHA_JSR 3 -+#define LITUSE_ALPHA_TLS_GD 4 -+#define LITUSE_ALPHA_TLS_LDM 5 -+ -+ -+/* PowerPC specific declarations */ -+ -+/* Values for Elf32/64_Ehdr.e_flags. */ -+#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ -+ -+/* Cygnus local bits below */ -+#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ -+#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib -+ flag */ -+ -+/* PowerPC relocations defined by the ABIs */ -+#define R_PPC_NONE 0 -+#define R_PPC_ADDR32 1 /* 32bit absolute address */ -+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -+#define R_PPC_ADDR16 3 /* 16bit absolute address */ -+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -+#define R_PPC_ADDR14_BRTAKEN 8 -+#define R_PPC_ADDR14_BRNTAKEN 9 -+#define R_PPC_REL24 10 /* PC relative 26 bit */ -+#define R_PPC_REL14 11 /* PC relative 16 bit */ -+#define R_PPC_REL14_BRTAKEN 12 -+#define R_PPC_REL14_BRNTAKEN 13 -+#define R_PPC_GOT16 14 -+#define R_PPC_GOT16_LO 15 -+#define R_PPC_GOT16_HI 16 -+#define R_PPC_GOT16_HA 17 -+#define R_PPC_PLTREL24 18 -+#define R_PPC_COPY 19 -+#define R_PPC_GLOB_DAT 20 -+#define R_PPC_JMP_SLOT 21 -+#define R_PPC_RELATIVE 22 -+#define R_PPC_LOCAL24PC 23 -+#define R_PPC_UADDR32 24 -+#define R_PPC_UADDR16 25 -+#define R_PPC_REL32 26 -+#define R_PPC_PLT32 27 -+#define R_PPC_PLTREL32 28 -+#define R_PPC_PLT16_LO 29 -+#define R_PPC_PLT16_HI 30 -+#define R_PPC_PLT16_HA 31 -+#define R_PPC_SDAREL16 32 -+#define R_PPC_SECTOFF 33 -+#define R_PPC_SECTOFF_LO 34 -+#define R_PPC_SECTOFF_HI 35 -+#define R_PPC_SECTOFF_HA 36 -+ -+/* PowerPC relocations defined for the TLS access ABI. */ -+#define R_PPC_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ -+#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ -+#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ -+#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ -+#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ -+#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ -+#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ -+#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ -+#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ -+ -+/* Keep this the last entry. */ -+#define R_PPC_NUM 95 -+ -+/* The remaining relocs are from the Embedded ELF ABI, and are not -+ in the SVR4 ELF ABI. */ -+#define R_PPC_EMB_NADDR32 101 -+#define R_PPC_EMB_NADDR16 102 -+#define R_PPC_EMB_NADDR16_LO 103 -+#define R_PPC_EMB_NADDR16_HI 104 -+#define R_PPC_EMB_NADDR16_HA 105 -+#define R_PPC_EMB_SDAI16 106 -+#define R_PPC_EMB_SDA2I16 107 -+#define R_PPC_EMB_SDA2REL 108 -+#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ -+#define R_PPC_EMB_MRKREF 110 -+#define R_PPC_EMB_RELSEC16 111 -+#define R_PPC_EMB_RELST_LO 112 -+#define R_PPC_EMB_RELST_HI 113 -+#define R_PPC_EMB_RELST_HA 114 -+#define R_PPC_EMB_BIT_FLD 115 -+#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ -+ -+/* Diab tool relocations. */ -+#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ -+#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ -+#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ -+#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ -+#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ -+#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ -+ -+/* This is a phony reloc to handle any old fashioned TOC16 references -+ that may still be in object files. */ -+#define R_PPC_TOC16 255 -+ -+ -+/* PowerPC64 relocations defined by the ABIs */ -+#define R_PPC64_NONE R_PPC_NONE -+#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ -+#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ -+#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ -+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ -+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ -+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ -+#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ -+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -+#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ -+#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ -+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -+#define R_PPC64_GOT16 R_PPC_GOT16 -+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA -+ -+#define R_PPC64_COPY R_PPC_COPY -+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -+#define R_PPC64_RELATIVE R_PPC_RELATIVE -+ -+#define R_PPC64_UADDR32 R_PPC_UADDR32 -+#define R_PPC64_UADDR16 R_PPC_UADDR16 -+#define R_PPC64_REL32 R_PPC_REL32 -+#define R_PPC64_PLT32 R_PPC_PLT32 -+#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA -+ -+#define R_PPC64_SECTOFF R_PPC_SECTOFF -+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -+#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ -+#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ -+#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ -+#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ -+#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ -+#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ -+#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ -+#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ -+#define R_PPC64_PLT64 45 /* doubleword64 L + A */ -+#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ -+#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ -+#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ -+#define R_PPC64_TOC 51 /* doubleword64 .TOC */ -+#define R_PPC64_PLTGOT16 52 /* half16* M + A */ -+#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ -+#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ -+#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ -+ -+#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ -+#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ -+#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ -+#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ -+#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ -+#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ -+#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ -+#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ -+#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ -+#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ -+#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ -+ -+/* PowerPC64 relocations defined for the TLS access ABI. */ -+#define R_PPC64_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ -+#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ -+#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ -+#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ -+#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ -+#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ -+#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ -+#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ -+#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ -+#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ -+#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ -+#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ -+#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ -+#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ -+#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ -+#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ -+#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ -+ -+/* Keep this the last entry. */ -+#define R_PPC64_NUM 107 -+ -+/* PowerPC64 specific values for the Dyn d_tag field. */ -+#define DT_PPC64_GLINK (DT_LOPROC + 0) -+#define DT_PPC64_NUM 1 -+ -+ -+/* ARM specific declarations */ -+ -+/* Processor specific flags for the ELF header e_flags field. */ -+#define EF_ARM_RELEXEC 0x01 -+#define EF_ARM_HASENTRY 0x02 -+#define EF_ARM_INTERWORK 0x04 -+#define EF_ARM_APCS_26 0x08 -+#define EF_ARM_APCS_FLOAT 0x10 -+#define EF_ARM_PIC 0x20 -+#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ -+#define EF_ARM_NEW_ABI 0x80 -+#define EF_ARM_OLD_ABI 0x100 -+ -+/* Other constants defined in the ARM ELF spec. version B-01. */ -+/* NB. These conflict with values defined above. */ -+#define EF_ARM_SYMSARESORTED 0x04 -+#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -+#define EF_ARM_MAPSYMSFIRST 0x10 -+#define EF_ARM_EABIMASK 0XFF000000 -+ -+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -+#define EF_ARM_EABI_UNKNOWN 0x00000000 -+#define EF_ARM_EABI_VER1 0x01000000 -+#define EF_ARM_EABI_VER2 0x02000000 -+ -+/* Additional symbol types for Thumb */ -+#define STT_ARM_TFUNC 0xd -+ -+/* ARM-specific values for sh_flags */ -+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ -+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined -+ in the input to a link step */ -+ -+/* ARM-specific program header flags */ -+#define PF_ARM_SB 0x10000000 /* Segment contains the location -+ addressed by the static base */ -+ -+/* ARM relocs. */ -+#define R_ARM_NONE 0 /* No reloc */ -+#define R_ARM_PC24 1 /* PC relative 26 bit branch */ -+#define R_ARM_ABS32 2 /* Direct 32 bit */ -+#define R_ARM_REL32 3 /* PC relative 32 bit */ -+#define R_ARM_PC13 4 -+#define R_ARM_ABS16 5 /* Direct 16 bit */ -+#define R_ARM_ABS12 6 /* Direct 12 bit */ -+#define R_ARM_THM_ABS5 7 -+#define R_ARM_ABS8 8 /* Direct 8 bit */ -+#define R_ARM_SBREL32 9 -+#define R_ARM_THM_PC22 10 -+#define R_ARM_THM_PC8 11 -+#define R_ARM_AMP_VCALL9 12 -+#define R_ARM_SWI24 13 -+#define R_ARM_THM_SWI8 14 -+#define R_ARM_XPC25 15 -+#define R_ARM_THM_XPC22 16 -+#define R_ARM_COPY 20 /* Copy symbol at runtime */ -+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ -+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ -+#define R_ARM_RELATIVE 23 /* Adjust by program base */ -+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ -+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ -+#define R_ARM_GOT32 26 /* 32 bit GOT entry */ -+#define R_ARM_PLT32 27 /* 32 bit PLT address */ -+#define R_ARM_ALU_PCREL_7_0 32 -+#define R_ARM_ALU_PCREL_15_8 33 -+#define R_ARM_ALU_PCREL_23_15 34 -+#define R_ARM_LDR_SBREL_11_0 35 -+#define R_ARM_ALU_SBREL_19_12 36 -+#define R_ARM_ALU_SBREL_27_20 37 -+#define R_ARM_GNU_VTENTRY 100 -+#define R_ARM_GNU_VTINHERIT 101 -+#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ -+#define R_ARM_THM_PC9 103 /* thumb conditional branch */ -+#define R_ARM_RXPC25 249 -+#define R_ARM_RSBREL32 250 -+#define R_ARM_THM_RPC22 251 -+#define R_ARM_RREL32 252 -+#define R_ARM_RABS22 253 -+#define R_ARM_RPC24 254 -+#define R_ARM_RBASE 255 -+/* Keep this the last entry. */ -+#define R_ARM_NUM 256 -+ -+/* IA-64 specific declarations. */ -+ -+/* Processor specific flags for the Ehdr e_flags field. */ -+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ -+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ -+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ -+ -+/* Processor specific values for the Phdr p_type field. */ -+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ -+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ -+ -+/* Processor specific flags for the Phdr p_flags field. */ -+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Shdr sh_type field. */ -+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ -+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ -+ -+/* Processor specific flags for the Shdr sh_flags field. */ -+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ -+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Dyn d_tag field. */ -+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -+#define DT_IA_64_NUM 1 -+ -+/* IA-64 relocations. */ -+#define R_IA64_NONE 0x00 /* none */ -+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ -+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ -+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ -+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ -+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ -+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ -+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ -+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ -+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ -+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ -+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ -+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ -+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ -+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ -+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ -+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ -+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ -+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ -+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ -+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ -+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ -+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ -+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ -+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ -+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ -+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ -+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ -+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ -+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ -+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ -+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ -+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ -+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ -+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ -+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ -+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ -+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ -+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ -+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ -+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ -+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ -+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ -+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ -+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ -+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ -+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ -+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ -+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ -+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ -+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ -+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ -+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ -+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ -+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ -+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ -+#define R_IA64_COPY 0x84 /* copy relocation */ -+#define R_IA64_SUB 0x85 /* Addend and symbol difference */ -+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ -+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ -+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ -+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ -+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ -+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ -+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ -+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ -+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ -+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ -+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ -+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ -+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ -+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ -+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ -+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ -+ -+/* SH specific declarations */ -+ -+/* SH relocs. */ -+#define R_SH_NONE 0 -+#define R_SH_DIR32 1 -+#define R_SH_REL32 2 -+#define R_SH_DIR8WPN 3 -+#define R_SH_IND12W 4 -+#define R_SH_DIR8WPL 5 -+#define R_SH_DIR8WPZ 6 -+#define R_SH_DIR8BP 7 -+#define R_SH_DIR8W 8 -+#define R_SH_DIR8L 9 -+#define R_SH_SWITCH16 25 -+#define R_SH_SWITCH32 26 -+#define R_SH_USES 27 -+#define R_SH_COUNT 28 -+#define R_SH_ALIGN 29 -+#define R_SH_CODE 30 -+#define R_SH_DATA 31 -+#define R_SH_LABEL 32 -+#define R_SH_SWITCH8 33 -+#define R_SH_GNU_VTINHERIT 34 -+#define R_SH_GNU_VTENTRY 35 -+#define R_SH_TLS_GD_32 144 -+#define R_SH_TLS_LD_32 145 -+#define R_SH_TLS_LDO_32 146 -+#define R_SH_TLS_IE_32 147 -+#define R_SH_TLS_LE_32 148 -+#define R_SH_TLS_DTPMOD32 149 -+#define R_SH_TLS_DTPOFF32 150 -+#define R_SH_TLS_TPOFF32 151 -+#define R_SH_GOT32 160 -+#define R_SH_PLT32 161 -+#define R_SH_COPY 162 -+#define R_SH_GLOB_DAT 163 -+#define R_SH_JMP_SLOT 164 -+#define R_SH_RELATIVE 165 -+#define R_SH_GOTOFF 166 -+#define R_SH_GOTPC 167 -+/* Keep this the last entry. */ -+#define R_SH_NUM 256 -+ -+/* Additional s390 relocs */ -+ -+#define R_390_NONE 0 /* No reloc. */ -+#define R_390_8 1 /* Direct 8 bit. */ -+#define R_390_12 2 /* Direct 12 bit. */ -+#define R_390_16 3 /* Direct 16 bit. */ -+#define R_390_32 4 /* Direct 32 bit. */ -+#define R_390_PC32 5 /* PC relative 32 bit. */ -+#define R_390_GOT12 6 /* 12 bit GOT offset. */ -+#define R_390_GOT32 7 /* 32 bit GOT offset. */ -+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ -+#define R_390_COPY 9 /* Copy symbol at runtime. */ -+#define R_390_GLOB_DAT 10 /* Create GOT entry. */ -+#define R_390_JMP_SLOT 11 /* Create PLT entry. */ -+#define R_390_RELATIVE 12 /* Adjust by program base. */ -+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ -+#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ -+#define R_390_GOT16 15 /* 16 bit GOT offset. */ -+#define R_390_PC16 16 /* PC relative 16 bit. */ -+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ -+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ -+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ -+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ -+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ -+#define R_390_64 22 /* Direct 64 bit. */ -+#define R_390_PC64 23 /* PC relative 64 bit. */ -+#define R_390_GOT64 24 /* 64 bit GOT offset. */ -+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ -+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ -+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ -+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ -+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ -+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ -+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ -+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ -+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ -+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ -+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ -+#define R_390_TLS_GDCALL 38 /* Tag for function call in general -+ dynamic TLS code. */ -+#define R_390_TLS_LDCALL 39 /* Tag for function call in local -+ dynamic TLS code. */ -+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ -+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ -+#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS -+ block. */ -+ -+/* Keep this the last entry. */ -+#define R_390_NUM 57 -+ -+/* CRIS relocations. */ -+#define R_CRIS_NONE 0 -+#define R_CRIS_8 1 -+#define R_CRIS_16 2 -+#define R_CRIS_32 3 -+#define R_CRIS_8_PCREL 4 -+#define R_CRIS_16_PCREL 5 -+#define R_CRIS_32_PCREL 6 -+#define R_CRIS_GNU_VTINHERIT 7 -+#define R_CRIS_GNU_VTENTRY 8 -+#define R_CRIS_COPY 9 -+#define R_CRIS_GLOB_DAT 10 -+#define R_CRIS_JUMP_SLOT 11 -+#define R_CRIS_RELATIVE 12 -+#define R_CRIS_16_GOT 13 -+#define R_CRIS_32_GOT 14 -+#define R_CRIS_16_GOTPLT 15 -+#define R_CRIS_32_GOTPLT 16 -+#define R_CRIS_32_GOTREL 17 -+#define R_CRIS_32_PLT_GOTREL 18 -+#define R_CRIS_32_PLT_PCREL 19 -+ -+#define R_CRIS_NUM 20 -+ -+/* AMD x86-64 relocations. */ -+#define R_X86_64_NONE 0 /* No reloc */ -+#define R_X86_64_64 1 /* Direct 64 bit */ -+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ -+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ -+#define R_X86_64_PLT32 4 /* 32 bit PLT address */ -+#define R_X86_64_COPY 5 /* Copy symbol at runtime */ -+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ -+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ -+#define R_X86_64_RELATIVE 8 /* Adjust by program base */ -+#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative -+ offset to GOT */ -+#define R_X86_64_32 10 /* Direct 32 bit zero extended */ -+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ -+#define R_X86_64_16 12 /* Direct 16 bit zero extended */ -+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ -+#define R_X86_64_8 14 /* Direct 8 bit sign extended */ -+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ -+#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ -+#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ -+#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ -+#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset -+ to two GOT entries for GD symbol */ -+#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset -+ to two GOT entries for LD symbol */ -+#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ -+#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset -+ to GOT entry for IE symbol */ -+#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ -+ -+#define R_X86_64_NUM 24 -+ -+__END_DECLS -+ -+#endif /* elf.h */ - - #include "elfconfig.h" - -@@ -185,3 +2631,4 @@ - void fatal(const char *fmt, ...); - void warn(const char *fmt, ...); - void merror(const char *fmt, ...); -+ -diff -Nur linux-3.11.5.orig/scripts/mod/sumversion.c linux-3.11.5/scripts/mod/sumversion.c ---- linux-3.11.5.orig/scripts/mod/sumversion.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/sumversion.c 2013-10-16 18:09:31.000000000 +0200 -@@ -1,4 +1,4 @@ --#include -+/* #include */ - #ifdef __sun__ - #include - #else -diff -Nur linux-3.11.5.orig/tools/include/tools/linux_types.h linux-3.11.5/tools/include/tools/linux_types.h ---- linux-3.11.5.orig/tools/include/tools/linux_types.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.11.5/tools/include/tools/linux_types.h 2013-10-16 18:09:31.000000000 +0200 -@@ -0,0 +1,22 @@ -+#ifndef __LINUX_TYPES_H -+#define __LINUX_TYPES_H -+ -+#include -+ -+typedef uint8_t __u8; -+typedef uint8_t __be8; -+typedef uint8_t __le8; -+ -+typedef uint16_t __u16; -+typedef uint16_t __be16; -+typedef uint16_t __le16; -+ -+typedef uint32_t __u32; -+typedef uint32_t __be32; -+typedef uint32_t __le32; -+ -+typedef uint64_t __u64; -+typedef uint64_t __be64; -+typedef uint64_t __le64; -+ -+#endif diff --git a/target/linux/patches/3.14.43/cleankernel.patch b/target/linux/patches/3.14.43/cleankernel.patch deleted file mode 100644 index d8c055dc3..000000000 --- a/target/linux/patches/3.14.43/cleankernel.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.5.orig/scripts/Makefile.headersinst linux-3.11.5/scripts/Makefile.headersinst ---- linux-3.11.5.orig/scripts/Makefile.headersinst 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/Makefile.headersinst 2013-10-15 16:33:10.000000000 +0200 -@@ -107,7 +107,6 @@ - - targets += $(install-file) - $(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE -- $(if $(unwanted),$(call cmd,remove),) - $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) - $(call if_changed,install) - diff --git a/target/linux/patches/3.14.43/defaults.patch b/target/linux/patches/3.14.43/defaults.patch deleted file mode 100644 index f071fd1dd..000000000 --- a/target/linux/patches/3.14.43/defaults.patch +++ /dev/null @@ -1,34 +0,0 @@ -diff -Nur linux-3.0.4.orig/fs/Kconfig linux-3.0.4/fs/Kconfig ---- linux-3.0.4.orig/fs/Kconfig 2011-08-29 22:56:30.000000000 +0200 -+++ linux-3.0.4/fs/Kconfig 2011-10-15 22:08:44.000000000 +0200 -@@ -47,7 +47,7 @@ - def_bool n - - config EXPORTFS -- tristate -+ def_bool y - - config FILE_LOCKING - bool "Enable POSIX file locking API" if EXPERT -diff -Nur linux-3.0.4.orig/fs/notify/Kconfig linux-3.0.4/fs/notify/Kconfig ---- linux-3.0.4.orig/fs/notify/Kconfig 2011-08-29 22:56:30.000000000 +0200 -+++ linux-3.0.4/fs/notify/Kconfig 2011-10-15 22:02:00.000000000 +0200 -@@ -1,5 +1,5 @@ - config FSNOTIFY -- def_bool n -+ def_bool y - - source "fs/notify/dnotify/Kconfig" - source "fs/notify/inotify/Kconfig" -diff -Nur linux-3.11.10.orig/usr/Kconfig linux-3.11.10/usr/Kconfig ---- linux-3.11.10.orig/usr/Kconfig 2013-11-29 19:42:37.000000000 +0100 -+++ linux-3.11.10/usr/Kconfig 2013-12-27 19:15:16.000000000 +0100 -@@ -47,7 +47,7 @@ - - config RD_GZIP - bool "Support initial ramdisks compressed using gzip" if EXPERT -- default y -+ default n - depends on BLK_DEV_INITRD - select DECOMPRESS_GZIP - help diff --git a/target/linux/patches/3.14.43/disable-netfilter.patch b/target/linux/patches/3.14.43/disable-netfilter.patch deleted file mode 100644 index 7b1ca013a..000000000 --- a/target/linux/patches/3.14.43/disable-netfilter.patch +++ /dev/null @@ -1,160 +0,0 @@ -diff -Nur linux-3.7.3.orig/net/Kconfig linux-3.7.3/net/Kconfig ---- linux-3.7.3.orig/net/Kconfig 2013-01-17 17:47:40.000000000 +0100 -+++ linux-3.7.3/net/Kconfig 2013-01-19 18:19:55.000000000 +0100 -@@ -163,7 +163,7 @@ - config NETFILTER_ADVANCED - bool "Advanced netfilter configuration" - depends on NETFILTER -- default y -+ default n - help - If you say Y here you can select between all the netfilter modules. - If you say N the more unusual ones will not be shown and the -@@ -175,7 +175,7 @@ - bool "Bridged IP/ARP packets filtering" - depends on BRIDGE && NETFILTER && INET - depends on NETFILTER_ADVANCED -- default y -+ default n - ---help--- - Enabling this option will let arptables resp. iptables see bridged - ARP resp. IP traffic. If you want a bridging firewall, you probably -diff -Nur linux-3.7.3.orig/net/netfilter/Kconfig linux-3.7.3/net/netfilter/Kconfig ---- linux-3.7.3.orig/net/netfilter/Kconfig 2013-01-17 17:47:40.000000000 +0100 -+++ linux-3.7.3/net/netfilter/Kconfig 2013-01-19 18:21:41.000000000 +0100 -@@ -22,7 +22,6 @@ - - config NETFILTER_NETLINK_LOG - tristate "Netfilter LOG over NFNETLINK interface" -- default m if NETFILTER_ADVANCED=n - select NETFILTER_NETLINK - help - If this option is enabled, the kernel will include support -@@ -34,7 +33,6 @@ - - config NF_CONNTRACK - tristate "Netfilter connection tracking support" -- default m if NETFILTER_ADVANCED=n - help - Connection tracking keeps a record of what packets have passed - through your machine, in order to figure out how they are related -@@ -60,7 +58,6 @@ - config NF_CONNTRACK_SECMARK - bool 'Connection tracking security mark support' - depends on NETWORK_SECMARK -- default m if NETFILTER_ADVANCED=n - help - This option enables security markings to be applied to - connections. Typically they are copied to connections from -@@ -177,7 +174,6 @@ - - config NF_CONNTRACK_FTP - tristate "FTP protocol support" -- default m if NETFILTER_ADVANCED=n - help - Tracking FTP connections is problematic: special helpers are - required for tracking them, and doing masquerading and other forms -@@ -211,7 +207,6 @@ - - config NF_CONNTRACK_IRC - tristate "IRC protocol support" -- default m if NETFILTER_ADVANCED=n - help - There is a commonly-used extension to IRC called - Direct Client-to-Client Protocol (DCC). This enables users to send -@@ -296,7 +291,6 @@ - - config NF_CONNTRACK_SIP - tristate "SIP protocol support" -- default m if NETFILTER_ADVANCED=n - help - SIP is an application-layer control protocol that can establish, - modify, and terminate multimedia sessions (conferences) such as -@@ -320,7 +314,6 @@ - config NF_CT_NETLINK - tristate 'Connection tracking netlink interface' - select NETFILTER_NETLINK -- default m if NETFILTER_ADVANCED=n - help - This option enables support for a netlink-based userspace interface - -@@ -424,7 +417,6 @@ - - config NETFILTER_XTABLES - tristate "Netfilter Xtables support (required for ip_tables)" -- default m if NETFILTER_ADVANCED=n - help - This is required if you intend to use any of ip_tables, - ip6_tables or arp_tables. -@@ -435,7 +427,6 @@ - - config NETFILTER_XT_MARK - tristate 'nfmark target and match support' -- default m if NETFILTER_ADVANCED=n - ---help--- - This option adds the "MARK" target and "mark" match. - -@@ -527,7 +518,6 @@ - config NETFILTER_XT_TARGET_CONNSECMARK - tristate '"CONNSECMARK" target support' - depends on NF_CONNTRACK && NF_CONNTRACK_SECMARK -- default m if NETFILTER_ADVANCED=n - help - The CONNSECMARK target copies security markings from packets - to connections, and restores security markings from connections -@@ -632,7 +622,6 @@ - - config NETFILTER_XT_TARGET_LOG - tristate "LOG target support" -- default m if NETFILTER_ADVANCED=n - help - This option adds a `LOG' target, which allows you to create rules in - any iptables table which records the packet header to the syslog. -@@ -660,7 +649,6 @@ - - config NETFILTER_XT_TARGET_NFLOG - tristate '"NFLOG" target support' -- default m if NETFILTER_ADVANCED=n - select NETFILTER_NETLINK_LOG - help - This option enables the NFLOG target, which allows to LOG -@@ -741,7 +729,6 @@ - config NETFILTER_XT_TARGET_SECMARK - tristate '"SECMARK" target support' - depends on NETWORK_SECMARK -- default m if NETFILTER_ADVANCED=n - help - The SECMARK target allows security marking of network - packets, for use with security subsystems. -@@ -751,7 +738,6 @@ - config NETFILTER_XT_TARGET_TCPMSS - tristate '"TCPMSS" target support' - depends on (IPV6 || IPV6=n) -- default m if NETFILTER_ADVANCED=n - ---help--- - This option adds a `TCPMSS' target, which allows you to alter the - MSS value of TCP SYN packets, to control the maximum size for that -@@ -856,7 +842,6 @@ - config NETFILTER_XT_MATCH_CONNTRACK - tristate '"conntrack" connection tracking match support' - depends on NF_CONNTRACK -- default m if NETFILTER_ADVANCED=n - help - This is a general conntrack match module, a superset of the state match. - -@@ -1063,7 +1048,6 @@ - config NETFILTER_XT_MATCH_POLICY - tristate 'IPsec "policy" match support' - depends on XFRM -- default m if NETFILTER_ADVANCED=n - help - Policy matching allows you to match packets based on the - IPsec policy that was used during decapsulation/will -@@ -1170,7 +1154,6 @@ - config NETFILTER_XT_MATCH_STATE - tristate '"state" match support' - depends on NF_CONNTRACK -- default m if NETFILTER_ADVANCED=n - help - Connection state matching allows you to match packets based on their - relationship to a tracked connection (ie. previous packets). This diff --git a/target/linux/patches/3.14.43/export-symbol-for-exmap.patch b/target/linux/patches/3.14.43/export-symbol-for-exmap.patch deleted file mode 100644 index 4f0fc8449..000000000 --- a/target/linux/patches/3.14.43/export-symbol-for-exmap.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.5.orig/kernel/pid.c linux-3.11.5/kernel/pid.c ---- linux-3.11.5.orig/kernel/pid.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/kernel/pid.c 2013-10-29 15:37:02.000000000 +0100 -@@ -450,6 +450,7 @@ - { - return find_task_by_pid_ns(vnr, task_active_pid_ns(current)); - } -+EXPORT_SYMBOL(find_task_by_vpid); - - struct pid *get_task_pid(struct task_struct *task, enum pid_type type) - { diff --git a/target/linux/patches/3.14.43/fblogo.patch b/target/linux/patches/3.14.43/fblogo.patch deleted file mode 100644 index cbbb4216f..000000000 --- a/target/linux/patches/3.14.43/fblogo.patch +++ /dev/null @@ -1,2097 +0,0 @@ -diff -Nur linux-3.13.7.orig/Documentation/fb/00-INDEX linux-3.13.7/Documentation/fb/00-INDEX ---- linux-3.13.7.orig/Documentation/fb/00-INDEX 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/Documentation/fb/00-INDEX 2014-03-29 16:03:17.000000000 +0100 -@@ -21,6 +21,8 @@ - - info on the driver for EP93xx LCD controller. - fbcon.txt - - intro to and usage guide for the framebuffer console (fbcon). -+fbcondecor.txt -+ - info on the Framebuffer Console Decoration - framebuffer.txt - - introduction to frame buffer devices. - gxfb.txt -diff -Nur linux-3.13.7.orig/Documentation/fb/fbcondecor.txt linux-3.13.7/Documentation/fb/fbcondecor.txt ---- linux-3.13.7.orig/Documentation/fb/fbcondecor.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.13.7/Documentation/fb/fbcondecor.txt 2014-03-29 16:03:17.000000000 +0100 -@@ -0,0 +1,207 @@ -+What is it? -+----------- -+ -+The framebuffer decorations are a kernel feature which allows displaying a -+background picture on selected consoles. -+ -+What do I need to get it to work? -+--------------------------------- -+ -+To get fbcondecor up-and-running you will have to: -+ 1) get a copy of splashutils [1] or a similar program -+ 2) get some fbcondecor themes -+ 3) build the kernel helper program -+ 4) build your kernel with the FB_CON_DECOR option enabled. -+ -+To get fbcondecor operational right after fbcon initialization is finished, you -+will have to include a theme and the kernel helper into your initramfs image. -+Please refer to splashutils documentation for instructions on how to do that. -+ -+[1] The splashutils package can be downloaded from: -+ http://github.com/alanhaggai/fbsplash -+ -+The userspace helper -+-------------------- -+ -+The userspace fbcondecor helper (by default: /sbin/fbcondecor_helper) is called by the -+kernel whenever an important event occurs and the kernel needs some kind of -+job to be carried out. Important events include console switches and video -+mode switches (the kernel requests background images and configuration -+parameters for the current console). The fbcondecor helper must be accessible at -+all times. If it's not, fbcondecor will be switched off automatically. -+ -+It's possible to set path to the fbcondecor helper by writing it to -+/proc/sys/kernel/fbcondecor. -+ -+***************************************************************************** -+ -+The information below is mostly technical stuff. There's probably no need to -+read it unless you plan to develop a userspace helper. -+ -+The fbcondecor protocol -+----------------------- -+ -+The fbcondecor protocol defines a communication interface between the kernel and -+the userspace fbcondecor helper. -+ -+The kernel side is responsible for: -+ -+ * rendering console text, using an image as a background (instead of a -+ standard solid color fbcon uses), -+ * accepting commands from the user via ioctls on the fbcondecor device, -+ * calling the userspace helper to set things up as soon as the fb subsystem -+ is initialized. -+ -+The userspace helper is responsible for everything else, including parsing -+configuration files, decompressing the image files whenever the kernel needs -+it, and communicating with the kernel if necessary. -+ -+The fbcondecor protocol specifies how communication is done in both ways: -+kernel->userspace and userspace->helper. -+ -+Kernel -> Userspace -+------------------- -+ -+The kernel communicates with the userspace helper by calling it and specifying -+the task to be done in a series of arguments. -+ -+The arguments follow the pattern: -+ -+ -+All commands defined in fbcondecor protocol v2 have the following parameters: -+ virtual console -+ framebuffer number -+ theme -+ -+Fbcondecor protocol v1 specified an additional 'fbcondecor mode' after the -+framebuffer number. Fbcondecor protocol v1 is deprecated and should not be used. -+ -+Fbcondecor protocol v2 specifies the following commands: -+ -+getpic -+------ -+ The kernel issues this command to request image data. It's up to the -+ userspace helper to find a background image appropriate for the specified -+ theme and the current resolution. The userspace helper should respond by -+ issuing the FBIOCONDECOR_SETPIC ioctl. -+ -+init -+---- -+ The kernel issues this command after the fbcondecor device is created and -+ the fbcondecor interface is initialized. Upon receiving 'init', the userspace -+ helper should parse the kernel command line (/proc/cmdline) or otherwise -+ decide whether fbcondecor is to be activated. -+ -+ To activate fbcondecor on the first console the helper should issue the -+ FBIOCONDECOR_SETCFG, FBIOCONDECOR_SETPIC and FBIOCONDECOR_SETSTATE commands, -+ in the above-mentioned order. -+ -+ When the userspace helper is called in an early phase of the boot process -+ (right after the initialization of fbcon), no filesystems will be mounted. -+ The helper program should mount sysfs and then create the appropriate -+ framebuffer, fbcondecor and tty0 devices (if they don't already exist) to get -+ current display settings and to be able to communicate with the kernel side. -+ It should probably also mount the procfs to be able to parse the kernel -+ command line parameters. -+ -+ Note that the console sem is not held when the kernel calls fbcondecor_helper -+ with the 'init' command. The fbcondecor helper should perform all ioctls with -+ origin set to FBCON_DECOR_IO_ORIG_USER. -+ -+modechange -+---------- -+ The kernel issues this command on a mode change. The helper's response should -+ be similar to the response to the 'init' command. Note that this time the -+ console sem is held and all ioctls must be performed with origin set to -+ FBCON_DECOR_IO_ORIG_KERNEL. -+ -+ -+Userspace -> Kernel -+------------------- -+ -+Userspace programs can communicate with fbcondecor via ioctls on the -+fbcondecor device. These ioctls are to be used by both the userspace helper -+(called only by the kernel) and userspace configuration tools (run by the users). -+ -+The fbcondecor helper should set the origin field to FBCON_DECOR_IO_ORIG_KERNEL -+when doing the appropriate ioctls. All userspace configuration tools should -+use FBCON_DECOR_IO_ORIG_USER. Failure to set the appropriate value in the origin -+field when performing ioctls from the kernel helper will most likely result -+in a console deadlock. -+ -+FBCON_DECOR_IO_ORIG_KERNEL instructs fbcondecor not to try to acquire the console -+semaphore. Not surprisingly, FBCON_DECOR_IO_ORIG_USER instructs it to acquire -+the console sem. -+ -+The framebuffer console decoration provides the following ioctls (all defined in -+linux/fb.h): -+ -+FBIOCONDECOR_SETPIC -+description: loads a background picture for a virtual console -+argument: struct fbcon_decor_iowrapper*; data: struct fb_image* -+notes: -+If called for consoles other than the current foreground one, the picture data -+will be ignored. -+ -+If the current virtual console is running in a 8-bpp mode, the cmap substruct -+of fb_image has to be filled appropriately: start should be set to 16 (first -+16 colors are reserved for fbcon), len to a value <= 240 and red, green and -+blue should point to valid cmap data. The transp field is ingored. The fields -+dx, dy, bg_color, fg_color in fb_image are ignored as well. -+ -+FBIOCONDECOR_SETCFG -+description: sets the fbcondecor config for a virtual console -+argument: struct fbcon_decor_iowrapper*; data: struct vc_decor* -+notes: The structure has to be filled with valid data. -+ -+FBIOCONDECOR_GETCFG -+description: gets the fbcondecor config for a virtual console -+argument: struct fbcon_decor_iowrapper*; data: struct vc_decor* -+ -+FBIOCONDECOR_SETSTATE -+description: sets the fbcondecor state for a virtual console -+argument: struct fbcon_decor_iowrapper*; data: unsigned int* -+ values: 0 = disabled, 1 = enabled. -+ -+FBIOCONDECOR_GETSTATE -+description: gets the fbcondecor state for a virtual console -+argument: struct fbcon_decor_iowrapper*; data: unsigned int* -+ values: as in FBIOCONDECOR_SETSTATE -+ -+Info on used structures: -+ -+Definition of struct vc_decor can be found in linux/console_decor.h. It's -+heavily commented. Note that the 'theme' field should point to a string -+no longer than FBCON_DECOR_THEME_LEN. When FBIOCONDECOR_GETCFG call is -+performed, the theme field should point to a char buffer of length -+FBCON_DECOR_THEME_LEN. -+ -+Definition of struct fbcon_decor_iowrapper can be found in linux/fb.h. -+The fields in this struct have the following meaning: -+ -+vc: -+Virtual console number. -+ -+origin: -+Specifies if the ioctl is performed as a response to a kernel request. The -+fbcondecor helper should set this field to FBCON_DECOR_IO_ORIG_KERNEL, userspace -+programs should set it to FBCON_DECOR_IO_ORIG_USER. This field is necessary to -+avoid console semaphore deadlocks. -+ -+data: -+Pointer to a data structure appropriate for the performed ioctl. Type of -+the data struct is specified in the ioctls description. -+ -+***************************************************************************** -+ -+Credit -+------ -+ -+Original 'bootsplash' project & implementation by: -+ Volker Poplawski , Stefan Reinauer , -+ Steffen Winterfeldt , Michael Schroeder , -+ Ken Wimer . -+ -+Fbcondecor, fbcondecor protocol design, current implementation & docs by: -+ Michal Januszewski -+ -diff -Nur linux-3.13.7.orig/drivers/Makefile linux-3.13.7/drivers/Makefile ---- linux-3.13.7.orig/drivers/Makefile 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/drivers/Makefile 2014-03-29 16:03:17.000000000 +0100 -@@ -17,6 +17,10 @@ - obj-$(CONFIG_PCI) += pci/ - obj-$(CONFIG_PARISC) += parisc/ - obj-$(CONFIG_RAPIDIO) += rapidio/ -+# tty/ comes before char/ so that the VT console is the boot-time -+# default. -+obj-y += tty/ -+obj-y += char/ - obj-y += video/ - obj-y += idle/ - -@@ -42,11 +46,6 @@ - # reset controllers early, since gpu drivers might rely on them to initialize - obj-$(CONFIG_RESET_CONTROLLER) += reset/ - --# tty/ comes before char/ so that the VT console is the boot-time --# default. --obj-y += tty/ --obj-y += char/ -- - # gpu/ comes after char for AGP vs DRM startup - obj-y += gpu/ - -diff -Nur linux-3.13.7.orig/drivers/video/Kconfig linux-3.13.7/drivers/video/Kconfig ---- linux-3.13.7.orig/drivers/video/Kconfig 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/drivers/video/Kconfig 2014-03-29 16:03:17.000000000 +0100 -@@ -1231,7 +1231,6 @@ - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT -- select FB_TILEBLITTING - select FB_MACMODES if PPC_PMAC - ---help--- - Say Y here if you have a Matrox Millennium, Matrox Millennium II, -diff -Nur linux-3.13.7.orig/drivers/video/console/Kconfig linux-3.13.7/drivers/video/console/Kconfig ---- linux-3.13.7.orig/drivers/video/console/Kconfig 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/drivers/video/console/Kconfig 2014-03-29 16:03:17.000000000 +0100 -@@ -125,6 +125,19 @@ - such that other users of the framebuffer will remain normally - oriented. - -+config FB_CON_DECOR -+ bool "Support for the Framebuffer Console Decorations" -+ depends on FRAMEBUFFER_CONSOLE=y && !FB_TILEBLITTING -+ default n -+ ---help--- -+ This option enables support for framebuffer console decorations which -+ makes it possible to display images in the background of the system -+ consoles. Note that userspace utilities are necessary in order to take -+ advantage of these features. Refer to Documentation/fb/fbcondecor.txt -+ for more information. -+ -+ If unsure, say N. -+ - config STI_CONSOLE - bool "STI text console" - depends on PARISC -diff -Nur linux-3.13.7.orig/drivers/video/console/Makefile linux-3.13.7/drivers/video/console/Makefile ---- linux-3.13.7.orig/drivers/video/console/Makefile 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/drivers/video/console/Makefile 2014-03-29 16:03:17.000000000 +0100 -@@ -16,4 +16,5 @@ - fbcon_ccw.o - endif - -+obj-$(CONFIG_FB_CON_DECOR) += fbcondecor.o cfbcondecor.o - obj-$(CONFIG_FB_STI) += sticore.o -diff -Nur linux-3.13.7.orig/drivers/video/console/bitblit.c linux-3.13.7/drivers/video/console/bitblit.c ---- linux-3.13.7.orig/drivers/video/console/bitblit.c 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/drivers/video/console/bitblit.c 2014-03-29 16:03:17.000000000 +0100 -@@ -18,6 +18,7 @@ - #include - #include - #include "fbcon.h" -+#include "fbcondecor.h" - - /* - * Accelerated handlers. -@@ -55,6 +56,13 @@ - area.height = height * vc->vc_font.height; - area.width = width * vc->vc_font.width; - -+ if (fbcon_decor_active(info, vc)) { -+ area.sx += vc->vc_decor.tx; -+ area.sy += vc->vc_decor.ty; -+ area.dx += vc->vc_decor.tx; -+ area.dy += vc->vc_decor.ty; -+ } -+ - info->fbops->fb_copyarea(info, &area); - } - -@@ -380,11 +388,15 @@ - cursor.image.depth = 1; - cursor.rop = ROP_XOR; - -- if (info->fbops->fb_cursor) -- err = info->fbops->fb_cursor(info, &cursor); -+ if (fbcon_decor_active(info, vc)) { -+ fbcon_decor_cursor(info, &cursor); -+ } else { -+ if (info->fbops->fb_cursor) -+ err = info->fbops->fb_cursor(info, &cursor); - -- if (err) -- soft_cursor(info, &cursor); -+ if (err) -+ soft_cursor(info, &cursor); -+ } - - ops->cursor_reset = 0; - } -diff -Nur linux-3.13.7.orig/drivers/video/console/cfbcondecor.c linux-3.13.7/drivers/video/console/cfbcondecor.c ---- linux-3.13.7.orig/drivers/video/console/cfbcondecor.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.13.7/drivers/video/console/cfbcondecor.c 2014-03-29 16:03:17.000000000 +0100 -@@ -0,0 +1,471 @@ -+/* -+ * linux/drivers/video/cfbcon_decor.c -- Framebuffer decor render functions -+ * -+ * Copyright (C) 2004 Michal Januszewski -+ * -+ * Code based upon "Bootdecor" (C) 2001-2003 -+ * Volker Poplawski , -+ * Stefan Reinauer , -+ * Steffen Winterfeldt , -+ * Michael Schroeder , -+ * Ken Wimer . -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive for -+ * more details. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "fbcon.h" -+#include "fbcondecor.h" -+ -+#define parse_pixel(shift,bpp,type) \ -+ do { \ -+ if (d & (0x80 >> (shift))) \ -+ dd2[(shift)] = fgx; \ -+ else \ -+ dd2[(shift)] = transparent ? *(type *)decor_src : bgx; \ -+ decor_src += (bpp); \ -+ } while (0) \ -+ -+extern int get_color(struct vc_data *vc, struct fb_info *info, -+ u16 c, int is_fg); -+ -+void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) -+{ -+ int i, j, k; -+ int minlen = min(min(info->var.red.length, info->var.green.length), -+ info->var.blue.length); -+ u32 col; -+ -+ for (j = i = 0; i < 16; i++) { -+ k = color_table[i]; -+ -+ col = ((vc->vc_palette[j++] >> (8-minlen)) -+ << info->var.red.offset); -+ col |= ((vc->vc_palette[j++] >> (8-minlen)) -+ << info->var.green.offset); -+ col |= ((vc->vc_palette[j++] >> (8-minlen)) -+ << info->var.blue.offset); -+ ((u32 *)info->pseudo_palette)[k] = col; -+ } -+} -+ -+void fbcon_decor_renderc(struct fb_info *info, int ypos, int xpos, int height, -+ int width, u8* src, u32 fgx, u32 bgx, u8 transparent) -+{ -+ unsigned int x, y; -+ u32 dd; -+ int bytespp = ((info->var.bits_per_pixel + 7) >> 3); -+ unsigned int d = ypos * info->fix.line_length + xpos * bytespp; -+ unsigned int ds = (ypos * info->var.xres + xpos) * bytespp; -+ u16 dd2[4]; -+ -+ u8* decor_src = (u8 *)(info->bgdecor.data + ds); -+ u8* dst = (u8 *)(info->screen_base + d); -+ -+ if ((ypos + height) > info->var.yres || (xpos + width) > info->var.xres) -+ return; -+ -+ for (y = 0; y < height; y++) { -+ switch (info->var.bits_per_pixel) { -+ -+ case 32: -+ for (x = 0; x < width; x++) { -+ -+ if ((x & 7) == 0) -+ d = *src++; -+ if (d & 0x80) -+ dd = fgx; -+ else -+ dd = transparent ? -+ *(u32 *)decor_src : bgx; -+ -+ d <<= 1; -+ decor_src += 4; -+ fb_writel(dd, dst); -+ dst += 4; -+ } -+ break; -+ case 24: -+ for (x = 0; x < width; x++) { -+ -+ if ((x & 7) == 0) -+ d = *src++; -+ if (d & 0x80) -+ dd = fgx; -+ else -+ dd = transparent ? -+ (*(u32 *)decor_src & 0xffffff) : bgx; -+ -+ d <<= 1; -+ decor_src += 3; -+#ifdef __LITTLE_ENDIAN -+ fb_writew(dd & 0xffff, dst); -+ dst += 2; -+ fb_writeb((dd >> 16), dst); -+#else -+ fb_writew(dd >> 8, dst); -+ dst += 2; -+ fb_writeb(dd & 0xff, dst); -+#endif -+ dst++; -+ } -+ break; -+ case 16: -+ for (x = 0; x < width; x += 2) { -+ if ((x & 7) == 0) -+ d = *src++; -+ -+ parse_pixel(0, 2, u16); -+ parse_pixel(1, 2, u16); -+#ifdef __LITTLE_ENDIAN -+ dd = dd2[0] | (dd2[1] << 16); -+#else -+ dd = dd2[1] | (dd2[0] << 16); -+#endif -+ d <<= 2; -+ fb_writel(dd, dst); -+ dst += 4; -+ } -+ break; -+ -+ case 8: -+ for (x = 0; x < width; x += 4) { -+ if ((x & 7) == 0) -+ d = *src++; -+ -+ parse_pixel(0, 1, u8); -+ parse_pixel(1, 1, u8); -+ parse_pixel(2, 1, u8); -+ parse_pixel(3, 1, u8); -+ -+#ifdef __LITTLE_ENDIAN -+ dd = dd2[0] | (dd2[1] << 8) | (dd2[2] << 16) | (dd2[3] << 24); -+#else -+ dd = dd2[3] | (dd2[2] << 8) | (dd2[1] << 16) | (dd2[0] << 24); -+#endif -+ d <<= 4; -+ fb_writel(dd, dst); -+ dst += 4; -+ } -+ } -+ -+ dst += info->fix.line_length - width * bytespp; -+ decor_src += (info->var.xres - width) * bytespp; -+ } -+} -+ -+#define cc2cx(a) \ -+ ((info->fix.visual == FB_VISUAL_TRUECOLOR || \ -+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? \ -+ ((u32*)info->pseudo_palette)[a] : a) -+ -+void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, -+ const unsigned short *s, int count, int yy, int xx) -+{ -+ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; -+ struct fbcon_ops *ops = info->fbcon_par; -+ int fg_color, bg_color, transparent; -+ u8 *src; -+ u32 bgx, fgx; -+ u16 c = scr_readw(s); -+ -+ fg_color = get_color(vc, info, c, 1); -+ bg_color = get_color(vc, info, c, 0); -+ -+ /* Don't paint the background image if console is blanked */ -+ transparent = ops->blank_state ? 0 : -+ (vc->vc_decor.bg_color == bg_color); -+ -+ xx = xx * vc->vc_font.width + vc->vc_decor.tx; -+ yy = yy * vc->vc_font.height + vc->vc_decor.ty; -+ -+ fgx = cc2cx(fg_color); -+ bgx = cc2cx(bg_color); -+ -+ while (count--) { -+ c = scr_readw(s++); -+ src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * -+ ((vc->vc_font.width + 7) >> 3); -+ -+ fbcon_decor_renderc(info, yy, xx, vc->vc_font.height, -+ vc->vc_font.width, src, fgx, bgx, transparent); -+ xx += vc->vc_font.width; -+ } -+} -+ -+void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) -+{ -+ int i; -+ unsigned int dsize, s_pitch; -+ struct fbcon_ops *ops = info->fbcon_par; -+ struct vc_data* vc; -+ u8 *src; -+ -+ /* we really don't need any cursors while the console is blanked */ -+ if (info->state != FBINFO_STATE_RUNNING || ops->blank_state) -+ return; -+ -+ vc = vc_cons[ops->currcon].d; -+ -+ src = kmalloc(64 + sizeof(struct fb_image), GFP_ATOMIC); -+ if (!src) -+ return; -+ -+ s_pitch = (cursor->image.width + 7) >> 3; -+ dsize = s_pitch * cursor->image.height; -+ if (cursor->enable) { -+ switch (cursor->rop) { -+ case ROP_XOR: -+ for (i = 0; i < dsize; i++) -+ src[i] = cursor->image.data[i] ^ cursor->mask[i]; -+ break; -+ case ROP_COPY: -+ default: -+ for (i = 0; i < dsize; i++) -+ src[i] = cursor->image.data[i] & cursor->mask[i]; -+ break; -+ } -+ } else -+ memcpy(src, cursor->image.data, dsize); -+ -+ fbcon_decor_renderc(info, -+ cursor->image.dy + vc->vc_decor.ty, -+ cursor->image.dx + vc->vc_decor.tx, -+ cursor->image.height, -+ cursor->image.width, -+ (u8*)src, -+ cc2cx(cursor->image.fg_color), -+ cc2cx(cursor->image.bg_color), -+ cursor->image.bg_color == vc->vc_decor.bg_color); -+ -+ kfree(src); -+} -+ -+static void decorset(u8 *dst, int height, int width, int dstbytes, -+ u32 bgx, int bpp) -+{ -+ int i; -+ -+ if (bpp == 8) -+ bgx |= bgx << 8; -+ if (bpp == 16 || bpp == 8) -+ bgx |= bgx << 16; -+ -+ while (height-- > 0) { -+ u8 *p = dst; -+ -+ switch (bpp) { -+ -+ case 32: -+ for (i=0; i < width; i++) { -+ fb_writel(bgx, p); p += 4; -+ } -+ break; -+ case 24: -+ for (i=0; i < width; i++) { -+#ifdef __LITTLE_ENDIAN -+ fb_writew((bgx & 0xffff),(u16*)p); p += 2; -+ fb_writeb((bgx >> 16),p++); -+#else -+ fb_writew((bgx >> 8),(u16*)p); p += 2; -+ fb_writeb((bgx & 0xff),p++); -+#endif -+ } -+ case 16: -+ for (i=0; i < width/4; i++) { -+ fb_writel(bgx,p); p += 4; -+ fb_writel(bgx,p); p += 4; -+ } -+ if (width & 2) { -+ fb_writel(bgx,p); p += 4; -+ } -+ if (width & 1) -+ fb_writew(bgx,(u16*)p); -+ break; -+ case 8: -+ for (i=0; i < width/4; i++) { -+ fb_writel(bgx,p); p += 4; -+ } -+ -+ if (width & 2) { -+ fb_writew(bgx,p); p += 2; -+ } -+ if (width & 1) -+ fb_writeb(bgx,(u8*)p); -+ break; -+ -+ } -+ dst += dstbytes; -+ } -+} -+ -+void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes, -+ int srclinebytes, int bpp) -+{ -+ int i; -+ -+ while (height-- > 0) { -+ u32 *p = (u32 *)dst; -+ u32 *q = (u32 *)src; -+ -+ switch (bpp) { -+ -+ case 32: -+ for (i=0; i < width; i++) -+ fb_writel(*q++, p++); -+ break; -+ case 24: -+ for (i=0; i < (width*3/4); i++) -+ fb_writel(*q++, p++); -+ if ((width*3) % 4) { -+ if (width & 2) { -+ fb_writeb(*(u8*)q, (u8*)p); -+ } else if (width & 1) { -+ fb_writew(*(u16*)q, (u16*)p); -+ fb_writeb(*(u8*)((u16*)q+1),(u8*)((u16*)p+2)); -+ } -+ } -+ break; -+ case 16: -+ for (i=0; i < width/4; i++) { -+ fb_writel(*q++, p++); -+ fb_writel(*q++, p++); -+ } -+ if (width & 2) -+ fb_writel(*q++, p++); -+ if (width & 1) -+ fb_writew(*(u16*)q, (u16*)p); -+ break; -+ case 8: -+ for (i=0; i < width/4; i++) -+ fb_writel(*q++, p++); -+ -+ if (width & 2) { -+ fb_writew(*(u16*)q, (u16*)p); -+ q = (u32*) ((u16*)q + 1); -+ p = (u32*) ((u16*)p + 1); -+ } -+ if (width & 1) -+ fb_writeb(*(u8*)q, (u8*)p); -+ break; -+ } -+ -+ dst += linebytes; -+ src += srclinebytes; -+ } -+} -+ -+static void decorfill(struct fb_info *info, int sy, int sx, int height, -+ int width) -+{ -+ int bytespp = ((info->var.bits_per_pixel + 7) >> 3); -+ int d = sy * info->fix.line_length + sx * bytespp; -+ int ds = (sy * info->var.xres + sx) * bytespp; -+ -+ fbcon_decor_copy((u8 *)(info->screen_base + d), (u8 *)(info->bgdecor.data + ds), -+ height, width, info->fix.line_length, info->var.xres * bytespp, -+ info->var.bits_per_pixel); -+} -+ -+void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, -+ int height, int width) -+{ -+ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; -+ struct fbcon_ops *ops = info->fbcon_par; -+ u8 *dst; -+ int transparent, bg_color = attr_bgcol_ec(bgshift, vc, info); -+ -+ transparent = (vc->vc_decor.bg_color == bg_color); -+ sy = sy * vc->vc_font.height + vc->vc_decor.ty; -+ sx = sx * vc->vc_font.width + vc->vc_decor.tx; -+ height *= vc->vc_font.height; -+ width *= vc->vc_font.width; -+ -+ /* Don't paint the background image if console is blanked */ -+ if (transparent && !ops->blank_state) { -+ decorfill(info, sy, sx, height, width); -+ } else { -+ dst = (u8 *)(info->screen_base + sy * info->fix.line_length + -+ sx * ((info->var.bits_per_pixel + 7) >> 3)); -+ decorset(dst, height, width, info->fix.line_length, cc2cx(bg_color), -+ info->var.bits_per_pixel); -+ } -+} -+ -+void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, -+ int bottom_only) -+{ -+ unsigned int tw = vc->vc_cols*vc->vc_font.width; -+ unsigned int th = vc->vc_rows*vc->vc_font.height; -+ -+ if (!bottom_only) { -+ /* top margin */ -+ decorfill(info, 0, 0, vc->vc_decor.ty, info->var.xres); -+ /* left margin */ -+ decorfill(info, vc->vc_decor.ty, 0, th, vc->vc_decor.tx); -+ /* right margin */ -+ decorfill(info, vc->vc_decor.ty, vc->vc_decor.tx + tw, th, -+ info->var.xres - vc->vc_decor.tx - tw); -+ } -+ decorfill(info, vc->vc_decor.ty + th, 0, -+ info->var.yres - vc->vc_decor.ty - th, info->var.xres); -+} -+ -+void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, -+ int sx, int dx, int width) -+{ -+ u16 *d = (u16 *) (vc->vc_origin + vc->vc_size_row * y + dx * 2); -+ u16 *s = d + (dx - sx); -+ u16 *start = d; -+ u16 *ls = d; -+ u16 *le = d + width; -+ u16 c; -+ int x = dx; -+ u16 attr = 1; -+ -+ do { -+ c = scr_readw(d); -+ if (attr != (c & 0xff00)) { -+ attr = c & 0xff00; -+ if (d > start) { -+ fbcon_decor_putcs(vc, info, start, d - start, y, x); -+ x += d - start; -+ start = d; -+ } -+ } -+ if (s >= ls && s < le && c == scr_readw(s)) { -+ if (d > start) { -+ fbcon_decor_putcs(vc, info, start, d - start, y, x); -+ x += d - start + 1; -+ start = d + 1; -+ } else { -+ x++; -+ start++; -+ } -+ } -+ s++; -+ d++; -+ } while (d < le); -+ if (d > start) -+ fbcon_decor_putcs(vc, info, start, d - start, y, x); -+} -+ -+void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) -+{ -+ if (blank) { -+ decorset((u8 *)info->screen_base, info->var.yres, info->var.xres, -+ info->fix.line_length, 0, info->var.bits_per_pixel); -+ } else { -+ update_screen(vc); -+ fbcon_decor_clear_margins(vc, info, 0); -+ } -+} -+ -diff -Nur linux-3.13.7.orig/drivers/video/console/fbcon.c linux-3.13.7/drivers/video/console/fbcon.c ---- linux-3.13.7.orig/drivers/video/console/fbcon.c 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/drivers/video/console/fbcon.c 2014-03-29 16:03:17.000000000 +0100 -@@ -79,6 +79,7 @@ - #include - - #include "fbcon.h" -+#include "fbcondecor.h" - - #ifdef FBCONDEBUG - # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) -@@ -94,7 +95,7 @@ - - static struct display fb_display[MAX_NR_CONSOLES]; - --static signed char con2fb_map[MAX_NR_CONSOLES]; -+signed char con2fb_map[MAX_NR_CONSOLES]; - static signed char con2fb_map_boot[MAX_NR_CONSOLES]; - - static int logo_lines; -@@ -286,7 +287,7 @@ - !vt_force_oops_output(vc); - } - --static int get_color(struct vc_data *vc, struct fb_info *info, -+int get_color(struct vc_data *vc, struct fb_info *info, - u16 c, int is_fg) - { - int depth = fb_get_color_depth(&info->var, &info->fix); -@@ -551,6 +552,9 @@ - info_idx = -1; - } else { - fbcon_has_console_bind = 1; -+#ifdef CONFIG_FB_CON_DECOR -+ fbcon_decor_init(); -+#endif - } - - return err; -@@ -1007,6 +1011,12 @@ - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); - cols /= vc->vc_font.width; - rows /= vc->vc_font.height; -+ -+ if (fbcon_decor_active(info, vc)) { -+ cols = vc->vc_decor.twidth / vc->vc_font.width; -+ rows = vc->vc_decor.theight / vc->vc_font.height; -+ } -+ - vc_resize(vc, cols, rows); - - DPRINTK("mode: %s\n", info->fix.id); -@@ -1036,7 +1046,7 @@ - cap = info->flags; - - if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW || -- (info->fix.type == FB_TYPE_TEXT)) -+ (info->fix.type == FB_TYPE_TEXT) || fbcon_decor_active(info, vc)) - logo = 0; - - if (var_to_display(p, &info->var, info)) -@@ -1260,6 +1270,11 @@ - fbcon_clear_margins(vc, 0); - } - -+ if (fbcon_decor_active(info, vc)) { -+ fbcon_decor_clear(vc, info, sy, sx, height, width); -+ return; -+ } -+ - /* Split blits that cross physical y_wrap boundary */ - - y_break = p->vrows - p->yscroll; -@@ -1279,10 +1294,15 @@ - struct display *p = &fb_display[vc->vc_num]; - struct fbcon_ops *ops = info->fbcon_par; - -- if (!fbcon_is_inactive(vc, info)) -- ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, -- get_color(vc, info, scr_readw(s), 1), -- get_color(vc, info, scr_readw(s), 0)); -+ if (!fbcon_is_inactive(vc, info)) { -+ -+ if (fbcon_decor_active(info, vc)) -+ fbcon_decor_putcs(vc, info, s, count, ypos, xpos); -+ else -+ ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, -+ get_color(vc, info, scr_readw(s), 1), -+ get_color(vc, info, scr_readw(s), 0)); -+ } - } - - static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) -@@ -1298,8 +1318,13 @@ - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; - -- if (!fbcon_is_inactive(vc, info)) -- ops->clear_margins(vc, info, bottom_only); -+ if (!fbcon_is_inactive(vc, info)) { -+ if (fbcon_decor_active(info, vc)) { -+ fbcon_decor_clear_margins(vc, info, bottom_only); -+ } else { -+ ops->clear_margins(vc, info, bottom_only); -+ } -+ } - } - - static void fbcon_cursor(struct vc_data *vc, int mode) -@@ -1819,7 +1844,7 @@ - count = vc->vc_rows; - if (softback_top) - fbcon_softback_note(vc, t, count); -- if (logo_shown >= 0) -+ if (logo_shown >= 0 || fbcon_decor_active(info, vc)) - goto redraw_up; - switch (p->scrollmode) { - case SCROLL_MOVE: -@@ -1912,6 +1937,8 @@ - count = vc->vc_rows; - if (logo_shown >= 0) - goto redraw_down; -+ if (fbcon_decor_active(info, vc)) -+ goto redraw_down; - switch (p->scrollmode) { - case SCROLL_MOVE: - fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, -@@ -2060,6 +2087,13 @@ - } - return; - } -+ -+ if (fbcon_decor_active(info, vc) && sy == dy && height == 1) { -+ /* must use slower redraw bmove to keep background pic intact */ -+ fbcon_decor_bmove_redraw(vc, info, sy, sx, dx, width); -+ return; -+ } -+ - ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, - height, width); - } -@@ -2130,8 +2164,8 @@ - var.yres = virt_h * virt_fh; - x_diff = info->var.xres - var.xres; - y_diff = info->var.yres - var.yres; -- if (x_diff < 0 || x_diff > virt_fw || -- y_diff < 0 || y_diff > virt_fh) { -+ if ((x_diff < 0 || x_diff > virt_fw || -+ y_diff < 0 || y_diff > virt_fh) && !vc->vc_decor.state) { - const struct fb_videomode *mode; - - DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); -@@ -2167,6 +2201,21 @@ - - info = registered_fb[con2fb_map[vc->vc_num]]; - ops = info->fbcon_par; -+ prev_console = ops->currcon; -+ if (prev_console != -1) -+ old_info = registered_fb[con2fb_map[prev_console]]; -+ -+#ifdef CONFIG_FB_CON_DECOR -+ if (!fbcon_decor_active_vc(vc) && info->fix.visual == FB_VISUAL_DIRECTCOLOR) { -+ struct vc_data *vc_curr = vc_cons[prev_console].d; -+ if (vc_curr && fbcon_decor_active_vc(vc_curr)) { -+ /* Clear the screen to avoid displaying funky colors during -+ * palette updates. */ -+ memset((u8*)info->screen_base + info->fix.line_length * info->var.yoffset, -+ 0, info->var.yres * info->fix.line_length); -+ } -+ } -+#endif - - if (softback_top) { - if (softback_lines) -@@ -2185,9 +2234,6 @@ - logo_shown = FBCON_LOGO_CANSHOW; - } - -- prev_console = ops->currcon; -- if (prev_console != -1) -- old_info = registered_fb[con2fb_map[prev_console]]; - /* - * FIXME: If we have multiple fbdev's loaded, we need to - * update all info->currcon. Perhaps, we can place this -@@ -2231,6 +2277,18 @@ - fbcon_del_cursor_timer(old_info); - } - -+ if (fbcon_decor_active_vc(vc)) { -+ struct vc_data *vc_curr = vc_cons[prev_console].d; -+ -+ if (!vc_curr->vc_decor.theme || -+ strcmp(vc->vc_decor.theme, vc_curr->vc_decor.theme) || -+ (fbcon_decor_active_nores(info, vc_curr) && -+ !fbcon_decor_active(info, vc_curr))) { -+ fbcon_decor_disable(vc, 0); -+ fbcon_decor_call_helper("modechange", vc->vc_num); -+ } -+ } -+ - if (fbcon_is_inactive(vc, info) || - ops->blank_state != FB_BLANK_UNBLANK) - fbcon_del_cursor_timer(info); -@@ -2339,15 +2397,20 @@ - } - } - -- if (!fbcon_is_inactive(vc, info)) { -+ if (!fbcon_is_inactive(vc, info)) { - if (ops->blank_state != blank) { - ops->blank_state = blank; - fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); - ops->cursor_flash = (!blank); - -- if (!(info->flags & FBINFO_MISC_USEREVENT)) -- if (fb_blank(info, blank)) -- fbcon_generic_blank(vc, info, blank); -+ if (!(info->flags & FBINFO_MISC_USEREVENT)) { -+ if (fb_blank(info, blank)) { -+ if (fbcon_decor_active(info, vc)) -+ fbcon_decor_blank(vc, info, blank); -+ else -+ fbcon_generic_blank(vc, info, blank); -+ } -+ } - } - - if (!blank) -@@ -2522,13 +2585,22 @@ - } - - if (resize) { -+ /* reset wrap/pan */ - int cols, rows; - - cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); -+ -+ if (fbcon_decor_active(info, vc)) { -+ info->var.xoffset = info->var.yoffset = p->yscroll = 0; -+ cols = vc->vc_decor.twidth; -+ rows = vc->vc_decor.theight; -+ } - cols /= w; - rows /= h; -+ - vc_resize(vc, cols, rows); -+ - if (CON_IS_VISIBLE(vc) && softback_buf) - fbcon_update_softback(vc); - } else if (CON_IS_VISIBLE(vc) -@@ -2657,7 +2729,11 @@ - int i, j, k, depth; - u8 val; - -- if (fbcon_is_inactive(vc, info)) -+ if (fbcon_is_inactive(vc, info) -+#ifdef CONFIG_FB_CON_DECOR -+ || vc->vc_num != fg_console -+#endif -+ ) - return -EINVAL; - - if (!CON_IS_VISIBLE(vc)) -@@ -2683,14 +2759,56 @@ - } else - fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap); - -- return fb_set_cmap(&palette_cmap, info); -+ if (fbcon_decor_active(info, vc_cons[fg_console].d) && -+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) { -+ -+ u16 *red, *green, *blue; -+ int minlen = min(min(info->var.red.length, info->var.green.length), -+ info->var.blue.length); -+ int h; -+ -+ struct fb_cmap cmap = { -+ .start = 0, -+ .len = (1 << minlen), -+ .red = NULL, -+ .green = NULL, -+ .blue = NULL, -+ .transp = NULL -+ }; -+ -+ red = kmalloc(256 * sizeof(u16) * 3, GFP_KERNEL); -+ -+ if (!red) -+ goto out; -+ -+ green = red + 256; -+ blue = green + 256; -+ cmap.red = red; -+ cmap.green = green; -+ cmap.blue = blue; -+ -+ for (i = 0; i < cmap.len; i++) { -+ red[i] = green[i] = blue[i] = (0xffff * i)/(cmap.len-1); -+ } -+ -+ h = fb_set_cmap(&cmap, info); -+ fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d); -+ kfree(red); -+ -+ return h; -+ -+ } else if (fbcon_decor_active(info, vc_cons[fg_console].d) && -+ info->var.bits_per_pixel == 8 && info->bgdecor.cmap.red != NULL) -+ fb_set_cmap(&info->bgdecor.cmap, info); -+ -+out: return fb_set_cmap(&palette_cmap, info); - } - - static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) - { - unsigned long p; - int line; -- -+ - if (vc->vc_num != fg_console || !softback_lines) - return (u16 *) (vc->vc_origin + offset); - line = offset / vc->vc_size_row; -@@ -2909,7 +3027,14 @@ - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); - cols /= vc->vc_font.width; - rows /= vc->vc_font.height; -- vc_resize(vc, cols, rows); -+ -+ if (!fbcon_decor_active_nores(info, vc)) { -+ vc_resize(vc, cols, rows); -+ } else { -+ fbcon_decor_disable(vc, 0); -+ fbcon_decor_call_helper("modechange", vc->vc_num); -+ } -+ - updatescrollmode(p, info, vc); - scrollback_max = 0; - scrollback_current = 0; -@@ -2954,7 +3079,9 @@ - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); - cols /= vc->vc_font.width; - rows /= vc->vc_font.height; -- vc_resize(vc, cols, rows); -+ if (!fbcon_decor_active_nores(info, vc)) { -+ vc_resize(vc, cols, rows); -+ } - } - - if (fg != -1) -@@ -3570,6 +3697,7 @@ - } - } - -+ fbcon_decor_exit(); - fbcon_has_exited = 1; - } - -diff -Nur linux-3.13.7.orig/drivers/video/console/fbcondecor.c linux-3.13.7/drivers/video/console/fbcondecor.c ---- linux-3.13.7.orig/drivers/video/console/fbcondecor.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.13.7/drivers/video/console/fbcondecor.c 2014-03-29 16:03:17.000000000 +0100 -@@ -0,0 +1,555 @@ -+/* -+ * linux/drivers/video/console/fbcondecor.c -- Framebuffer console decorations -+ * -+ * Copyright (C) 2004-2009 Michal Januszewski -+ * -+ * Code based upon "Bootsplash" (C) 2001-2003 -+ * Volker Poplawski , -+ * Stefan Reinauer , -+ * Steffen Winterfeldt , -+ * Michael Schroeder , -+ * Ken Wimer . -+ * -+ * Compat ioctl support by Thorsten Klein . -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive for -+ * more details. -+ * -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "fbcon.h" -+#include "fbcondecor.h" -+ -+extern signed char con2fb_map[]; -+static int fbcon_decor_enable(struct vc_data *vc); -+char fbcon_decor_path[KMOD_PATH_LEN] = "/sbin/fbcondecor_helper"; -+static int initialized = 0; -+ -+int fbcon_decor_call_helper(char* cmd, unsigned short vc) -+{ -+ char *envp[] = { -+ "HOME=/", -+ "PATH=/sbin:/bin", -+ NULL -+ }; -+ -+ char tfb[5]; -+ char tcons[5]; -+ unsigned char fb = (int) con2fb_map[vc]; -+ -+ char *argv[] = { -+ fbcon_decor_path, -+ "2", -+ cmd, -+ tcons, -+ tfb, -+ vc_cons[vc].d->vc_decor.theme, -+ NULL -+ }; -+ -+ snprintf(tfb,5,"%d",fb); -+ snprintf(tcons,5,"%d",vc); -+ -+ return call_usermodehelper(fbcon_decor_path, argv, envp, UMH_WAIT_EXEC); -+} -+ -+/* Disables fbcondecor on a virtual console; called with console sem held. */ -+int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) -+{ -+ struct fb_info* info; -+ -+ if (!vc->vc_decor.state) -+ return -EINVAL; -+ -+ info = registered_fb[(int) con2fb_map[vc->vc_num]]; -+ -+ if (info == NULL) -+ return -EINVAL; -+ -+ vc->vc_decor.state = 0; -+ vc_resize(vc, info->var.xres / vc->vc_font.width, -+ info->var.yres / vc->vc_font.height); -+ -+ if (fg_console == vc->vc_num && redraw) { -+ redraw_screen(vc, 0); -+ update_region(vc, vc->vc_origin + -+ vc->vc_size_row * vc->vc_top, -+ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); -+ } -+ -+ printk(KERN_INFO "fbcondecor: switched decor state to 'off' on console %d\n", -+ vc->vc_num); -+ -+ return 0; -+} -+ -+/* Enables fbcondecor on a virtual console; called with console sem held. */ -+static int fbcon_decor_enable(struct vc_data *vc) -+{ -+ struct fb_info* info; -+ -+ info = registered_fb[(int) con2fb_map[vc->vc_num]]; -+ -+ if (vc->vc_decor.twidth == 0 || vc->vc_decor.theight == 0 || -+ info == NULL || vc->vc_decor.state || (!info->bgdecor.data && -+ vc->vc_num == fg_console)) -+ return -EINVAL; -+ -+ vc->vc_decor.state = 1; -+ vc_resize(vc, vc->vc_decor.twidth / vc->vc_font.width, -+ vc->vc_decor.theight / vc->vc_font.height); -+ -+ if (fg_console == vc->vc_num) { -+ redraw_screen(vc, 0); -+ update_region(vc, vc->vc_origin + -+ vc->vc_size_row * vc->vc_top, -+ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); -+ fbcon_decor_clear_margins(vc, info, 0); -+ } -+ -+ printk(KERN_INFO "fbcondecor: switched decor state to 'on' on console %d\n", -+ vc->vc_num); -+ -+ return 0; -+} -+ -+static inline int fbcon_decor_ioctl_dosetstate(struct vc_data *vc, unsigned int state, unsigned char origin) -+{ -+ int ret; -+ -+// if (origin == FBCON_DECOR_IO_ORIG_USER) -+ console_lock(); -+ if (!state) -+ ret = fbcon_decor_disable(vc, 1); -+ else -+ ret = fbcon_decor_enable(vc); -+// if (origin == FBCON_DECOR_IO_ORIG_USER) -+ console_unlock(); -+ -+ return ret; -+} -+ -+static inline void fbcon_decor_ioctl_dogetstate(struct vc_data *vc, unsigned int *state) -+{ -+ *state = vc->vc_decor.state; -+} -+ -+static int fbcon_decor_ioctl_dosetcfg(struct vc_data *vc, struct vc_decor *cfg, unsigned char origin) -+{ -+ struct fb_info *info; -+ int len; -+ char *tmp; -+ -+ info = registered_fb[(int) con2fb_map[vc->vc_num]]; -+ -+ if (info == NULL || !cfg->twidth || !cfg->theight || -+ cfg->tx + cfg->twidth > info->var.xres || -+ cfg->ty + cfg->theight > info->var.yres) -+ return -EINVAL; -+ -+ len = strlen_user(cfg->theme); -+ if (!len || len > FBCON_DECOR_THEME_LEN) -+ return -EINVAL; -+ tmp = kmalloc(len, GFP_KERNEL); -+ if (!tmp) -+ return -ENOMEM; -+ if (copy_from_user(tmp, (void __user *)cfg->theme, len)) -+ return -EFAULT; -+ cfg->theme = tmp; -+ cfg->state = 0; -+ -+ /* If this ioctl is a response to a request from kernel, the console sem -+ * is already held; we also don't need to disable decor because either the -+ * new config and background picture will be successfully loaded, and the -+ * decor will stay on, or in case of a failure it'll be turned off in fbcon. */ -+// if (origin == FBCON_DECOR_IO_ORIG_USER) { -+ console_lock(); -+ if (vc->vc_decor.state) -+ fbcon_decor_disable(vc, 1); -+// } -+ -+ if (vc->vc_decor.theme) -+ kfree(vc->vc_decor.theme); -+ -+ vc->vc_decor = *cfg; -+ -+// if (origin == FBCON_DECOR_IO_ORIG_USER) -+ console_unlock(); -+ -+ printk(KERN_INFO "fbcondecor: console %d using theme '%s'\n", -+ vc->vc_num, vc->vc_decor.theme); -+ return 0; -+} -+ -+static int fbcon_decor_ioctl_dogetcfg(struct vc_data *vc, struct vc_decor *decor) -+{ -+ char __user *tmp; -+ -+ tmp = decor->theme; -+ *decor = vc->vc_decor; -+ decor->theme = tmp; -+ -+ if (vc->vc_decor.theme) { -+ if (copy_to_user(tmp, vc->vc_decor.theme, strlen(vc->vc_decor.theme) + 1)) -+ return -EFAULT; -+ } else -+ if (put_user(0, tmp)) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static int fbcon_decor_ioctl_dosetpic(struct vc_data *vc, struct fb_image *img, unsigned char origin) -+{ -+ struct fb_info *info; -+ int len; -+ u8 *tmp; -+ -+ if (vc->vc_num != fg_console) -+ return -EINVAL; -+ -+ info = registered_fb[(int) con2fb_map[vc->vc_num]]; -+ -+ if (info == NULL) -+ return -EINVAL; -+ -+ if (img->width != info->var.xres || img->height != info->var.yres) { -+ printk(KERN_ERR "fbcondecor: picture dimensions mismatch\n"); -+ printk(KERN_ERR "%dx%d vs %dx%d\n", img->width, img->height, info->var.xres, info->var.yres); -+ return -EINVAL; -+ } -+ -+ if (img->depth != info->var.bits_per_pixel) { -+ printk(KERN_ERR "fbcondecor: picture depth mismatch\n"); -+ return -EINVAL; -+ } -+ -+ if (img->depth == 8) { -+ if (!img->cmap.len || !img->cmap.red || !img->cmap.green || -+ !img->cmap.blue) -+ return -EINVAL; -+ -+ tmp = vmalloc(img->cmap.len * 3 * 2); -+ if (!tmp) -+ return -ENOMEM; -+ -+ if (copy_from_user(tmp, -+ (void __user*)img->cmap.red, (img->cmap.len << 1)) || -+ copy_from_user(tmp + (img->cmap.len << 1), -+ (void __user*)img->cmap.green, (img->cmap.len << 1)) || -+ copy_from_user(tmp + (img->cmap.len << 2), -+ (void __user*)img->cmap.blue, (img->cmap.len << 1))) { -+ vfree(tmp); -+ return -EFAULT; -+ } -+ -+ img->cmap.transp = NULL; -+ img->cmap.red = (u16*)tmp; -+ img->cmap.green = img->cmap.red + img->cmap.len; -+ img->cmap.blue = img->cmap.green + img->cmap.len; -+ } else { -+ img->cmap.red = NULL; -+ } -+ -+ len = ((img->depth + 7) >> 3) * img->width * img->height; -+ -+ /* -+ * Allocate an additional byte so that we never go outside of the -+ * buffer boundaries in the rendering functions in a 24 bpp mode. -+ */ -+ tmp = vmalloc(len + 1); -+ -+ if (!tmp) -+ goto out; -+ -+ if (copy_from_user(tmp, (void __user*)img->data, len)) -+ goto out; -+ -+ img->data = tmp; -+ -+ /* If this ioctl is a response to a request from kernel, the console sem -+ * is already held. */ -+// if (origin == FBCON_DECOR_IO_ORIG_USER) -+ console_lock(); -+ -+ if (info->bgdecor.data) -+ vfree((u8*)info->bgdecor.data); -+ if (info->bgdecor.cmap.red) -+ vfree(info->bgdecor.cmap.red); -+ -+ info->bgdecor = *img; -+ -+ if (fbcon_decor_active_vc(vc) && fg_console == vc->vc_num) { -+ redraw_screen(vc, 0); -+ update_region(vc, vc->vc_origin + -+ vc->vc_size_row * vc->vc_top, -+ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); -+ fbcon_decor_clear_margins(vc, info, 0); -+ } -+ -+// if (origin == FBCON_DECOR_IO_ORIG_USER) -+ console_unlock(); -+ -+ return 0; -+ -+out: if (img->cmap.red) -+ vfree(img->cmap.red); -+ -+ if (tmp) -+ vfree(tmp); -+ return -ENOMEM; -+} -+ -+static long fbcon_decor_ioctl(struct file *filp, u_int cmd, u_long arg) -+{ -+ struct fbcon_decor_iowrapper __user *wrapper = (void __user*) arg; -+ struct vc_data *vc = NULL; -+ unsigned short vc_num = 0; -+ unsigned char origin = 0; -+ void __user *data = NULL; -+ -+ if (!access_ok(VERIFY_READ, wrapper, -+ sizeof(struct fbcon_decor_iowrapper))) -+ return -EFAULT; -+ -+ __get_user(vc_num, &wrapper->vc); -+ __get_user(origin, &wrapper->origin); -+ __get_user(data, &wrapper->data); -+ -+ if (!vc_cons_allocated(vc_num)) -+ return -EINVAL; -+ -+ vc = vc_cons[vc_num].d; -+ -+ switch (cmd) { -+ case FBIOCONDECOR_SETPIC: -+ { -+ struct fb_image img; -+ if (copy_from_user(&img, (struct fb_image __user *)data, sizeof(struct fb_image))) -+ return -EFAULT; -+ -+ return fbcon_decor_ioctl_dosetpic(vc, &img, origin); -+ } -+ case FBIOCONDECOR_SETCFG: -+ { -+ struct vc_decor cfg; -+ if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor))) -+ return -EFAULT; -+ -+ return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin); -+ } -+ case FBIOCONDECOR_GETCFG: -+ { -+ int rval; -+ struct vc_decor cfg; -+ -+ if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor))) -+ return -EFAULT; -+ -+ rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg); -+ -+ if (copy_to_user(data, &cfg, sizeof(struct vc_decor))) -+ return -EFAULT; -+ return rval; -+ } -+ case FBIOCONDECOR_SETSTATE: -+ { -+ unsigned int state = 0; -+ if (get_user(state, (unsigned int __user *)data)) -+ return -EFAULT; -+ return fbcon_decor_ioctl_dosetstate(vc, state, origin); -+ } -+ case FBIOCONDECOR_GETSTATE: -+ { -+ unsigned int state = 0; -+ fbcon_decor_ioctl_dogetstate(vc, &state); -+ return put_user(state, (unsigned int __user *)data); -+ } -+ -+ default: -+ return -ENOIOCTLCMD; -+ } -+} -+ -+#ifdef CONFIG_COMPAT -+ -+static long fbcon_decor_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { -+ -+ struct fbcon_decor_iowrapper32 __user *wrapper = (void __user *)arg; -+ struct vc_data *vc = NULL; -+ unsigned short vc_num = 0; -+ unsigned char origin = 0; -+ compat_uptr_t data_compat = 0; -+ void __user *data = NULL; -+ -+ if (!access_ok(VERIFY_READ, wrapper, -+ sizeof(struct fbcon_decor_iowrapper32))) -+ return -EFAULT; -+ -+ __get_user(vc_num, &wrapper->vc); -+ __get_user(origin, &wrapper->origin); -+ __get_user(data_compat, &wrapper->data); -+ data = compat_ptr(data_compat); -+ -+ if (!vc_cons_allocated(vc_num)) -+ return -EINVAL; -+ -+ vc = vc_cons[vc_num].d; -+ -+ switch (cmd) { -+ case FBIOCONDECOR_SETPIC32: -+ { -+ struct fb_image32 img_compat; -+ struct fb_image img; -+ -+ if (copy_from_user(&img_compat, (struct fb_image32 __user *)data, sizeof(struct fb_image32))) -+ return -EFAULT; -+ -+ fb_image_from_compat(img, img_compat); -+ -+ return fbcon_decor_ioctl_dosetpic(vc, &img, origin); -+ } -+ -+ case FBIOCONDECOR_SETCFG32: -+ { -+ struct vc_decor32 cfg_compat; -+ struct vc_decor cfg; -+ -+ if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32))) -+ return -EFAULT; -+ -+ vc_decor_from_compat(cfg, cfg_compat); -+ -+ return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin); -+ } -+ -+ case FBIOCONDECOR_GETCFG32: -+ { -+ int rval; -+ struct vc_decor32 cfg_compat; -+ struct vc_decor cfg; -+ -+ if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32))) -+ return -EFAULT; -+ cfg.theme = compat_ptr(cfg_compat.theme); -+ -+ rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg); -+ -+ vc_decor_to_compat(cfg_compat, cfg); -+ -+ if (copy_to_user((struct vc_decor32 __user *)data, &cfg_compat, sizeof(struct vc_decor32))) -+ return -EFAULT; -+ return rval; -+ } -+ -+ case FBIOCONDECOR_SETSTATE32: -+ { -+ compat_uint_t state_compat = 0; -+ unsigned int state = 0; -+ -+ if (get_user(state_compat, (compat_uint_t __user *)data)) -+ return -EFAULT; -+ -+ state = (unsigned int)state_compat; -+ -+ return fbcon_decor_ioctl_dosetstate(vc, state, origin); -+ } -+ -+ case FBIOCONDECOR_GETSTATE32: -+ { -+ compat_uint_t state_compat = 0; -+ unsigned int state = 0; -+ -+ fbcon_decor_ioctl_dogetstate(vc, &state); -+ state_compat = (compat_uint_t)state; -+ -+ return put_user(state_compat, (compat_uint_t __user *)data); -+ } -+ -+ default: -+ return -ENOIOCTLCMD; -+ } -+} -+#else -+ #define fbcon_decor_compat_ioctl NULL -+#endif -+ -+static struct file_operations fbcon_decor_ops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = fbcon_decor_ioctl, -+ .compat_ioctl = fbcon_decor_compat_ioctl -+}; -+ -+static struct miscdevice fbcon_decor_dev = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = "fbcondecor", -+ .fops = &fbcon_decor_ops -+}; -+ -+void fbcon_decor_reset() -+{ -+ int i; -+ -+ for (i = 0; i < num_registered_fb; i++) { -+ registered_fb[i]->bgdecor.data = NULL; -+ registered_fb[i]->bgdecor.cmap.red = NULL; -+ } -+ -+ for (i = 0; i < MAX_NR_CONSOLES && vc_cons[i].d; i++) { -+ vc_cons[i].d->vc_decor.state = vc_cons[i].d->vc_decor.twidth = -+ vc_cons[i].d->vc_decor.theight = 0; -+ vc_cons[i].d->vc_decor.theme = NULL; -+ } -+ -+ return; -+} -+ -+int fbcon_decor_init() -+{ -+ int i; -+ -+ fbcon_decor_reset(); -+ -+ if (initialized) -+ return 0; -+ -+ i = misc_register(&fbcon_decor_dev); -+ if (i) { -+ printk(KERN_ERR "fbcondecor: failed to register device\n"); -+ return i; -+ } -+ -+ fbcon_decor_call_helper("init", 0); -+ initialized = 1; -+ return 0; -+} -+ -+int fbcon_decor_exit(void) -+{ -+ fbcon_decor_reset(); -+ return 0; -+} -+ -+EXPORT_SYMBOL(fbcon_decor_path); -diff -Nur linux-3.13.7.orig/drivers/video/console/fbcondecor.h linux-3.13.7/drivers/video/console/fbcondecor.h ---- linux-3.13.7.orig/drivers/video/console/fbcondecor.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.13.7/drivers/video/console/fbcondecor.h 2014-03-29 21:17:27.000000000 +0100 -@@ -0,0 +1,79 @@ -+/* -+ * linux/drivers/video/console/fbcondecor.h -- Framebuffer Console Decoration headers -+ * -+ * Copyright (C) 2004 Michal Januszewski -+ * -+ */ -+ -+#ifndef __FBCON_DECOR_H -+#define __FBCON_DECOR_H -+ -+#ifndef _LINUX_FB_H -+#include -+#endif -+ -+/* This is needed for vc_cons in fbcmap.c */ -+#include -+ -+struct fb_cursor; -+struct fb_info; -+struct vc_data; -+ -+#ifdef CONFIG_FB_CON_DECOR -+/* fbcondecor.c */ -+int fbcon_decor_init(void); -+void fbcon_decor_reset(void); -+int fbcon_decor_exit(void); -+int fbcon_decor_call_helper(char* cmd, unsigned short cons); -+int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw); -+ -+/* cfbcondecor.c */ -+void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx); -+void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor); -+void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width); -+void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only); -+void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank); -+void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width); -+void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes, int srclinesbytes, int bpp); -+void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc); -+ -+/* vt.c */ -+void acquire_console_sem(void); -+void release_console_sem(void); -+void do_unblank_screen(int entering_gfx); -+ -+/* struct vc_data *y */ -+#define fbcon_decor_active_vc(y) (y->vc_decor.state && y->vc_decor.theme) -+ -+/* struct fb_info *x, struct vc_data *y */ -+#define fbcon_decor_active_nores(x,y) (x->bgdecor.data && fbcon_decor_active_vc(y)) -+ -+/* struct fb_info *x, struct vc_data *y */ -+#define fbcon_decor_active(x,y) (fbcon_decor_active_nores(x,y) && \ -+ x->bgdecor.width == x->var.xres && \ -+ x->bgdecor.height == x->var.yres && \ -+ x->bgdecor.depth == x->var.bits_per_pixel) -+ -+ -+#else /* CONFIG_FB_CON_DECOR */ -+ -+static inline void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx) {} -+static inline void fbcon_decor_putc(struct vc_data *vc, struct fb_info *info, int c, int ypos, int xpos) {} -+static inline void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) {} -+static inline void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) {} -+static inline void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only) {} -+static inline void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) {} -+static inline void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) {} -+static inline void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) {} -+static inline int fbcon_decor_call_helper(char* cmd, unsigned short cons) { return 0; } -+static inline int fbcon_decor_init(void) { return 0; } -+static inline int fbcon_decor_exit(void) { return 0; } -+static inline int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) { return 0; } -+ -+#define fbcon_decor_active_vc(y) (0) -+#define fbcon_decor_active_nores(x,y) (0) -+#define fbcon_decor_active(x,y) (0) -+ -+#endif /* CONFIG_FB_CON_DECOR */ -+ -+#endif /* __FBCON_DECOR_H */ -diff -Nur linux-3.13.7.orig/drivers/video/fbcmap.c linux-3.13.7/drivers/video/fbcmap.c ---- linux-3.13.7.orig/drivers/video/fbcmap.c 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/drivers/video/fbcmap.c 2014-03-29 16:03:17.000000000 +0100 -@@ -17,6 +17,8 @@ - #include - #include - -+#include "console/fbcondecor.h" -+ - static u16 red2[] __read_mostly = { - 0x0000, 0xaaaa - }; -@@ -249,14 +251,17 @@ - if (transp) - htransp = *transp++; - if (info->fbops->fb_setcolreg(start++, -- hred, hgreen, hblue, -+ hred, hgreen, hblue, - htransp, info)) - break; - } - } -- if (rc == 0) -+ if (rc == 0) { - fb_copy_cmap(cmap, &info->cmap); -- -+ if (fbcon_decor_active(info, vc_cons[fg_console].d) && -+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) -+ fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d); -+ } - return rc; - } - -diff -Nur linux-3.13.7.orig/drivers/video/fbmem.c linux-3.13.7/drivers/video/fbmem.c ---- linux-3.13.7.orig/drivers/video/fbmem.c 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/drivers/video/fbmem.c 2014-03-29 16:03:17.000000000 +0100 -@@ -1250,15 +1250,6 @@ - u16 reserved[3]; - }; - --struct fb_cmap32 { -- u32 start; -- u32 len; -- compat_caddr_t red; -- compat_caddr_t green; -- compat_caddr_t blue; -- compat_caddr_t transp; --}; -- - static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, - unsigned long arg) - { -diff -Nur linux-3.13.7.orig/include/linux/console_decor.h linux-3.13.7/include/linux/console_decor.h ---- linux-3.13.7.orig/include/linux/console_decor.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.13.7/include/linux/console_decor.h 2014-03-29 16:03:17.000000000 +0100 -@@ -0,0 +1,46 @@ -+#ifndef _LINUX_CONSOLE_DECOR_H_ -+#define _LINUX_CONSOLE_DECOR_H_ 1 -+ -+/* A structure used by the framebuffer console decorations (drivers/video/console/fbcondecor.c) */ -+struct vc_decor { -+ __u8 bg_color; /* The color that is to be treated as transparent */ -+ __u8 state; /* Current decor state: 0 = off, 1 = on */ -+ __u16 tx, ty; /* Top left corner coordinates of the text field */ -+ __u16 twidth, theight; /* Width and height of the text field */ -+ char* theme; -+}; -+ -+#ifdef __KERNEL__ -+#ifdef CONFIG_COMPAT -+#include -+ -+struct vc_decor32 { -+ __u8 bg_color; /* The color that is to be treated as transparent */ -+ __u8 state; /* Current decor state: 0 = off, 1 = on */ -+ __u16 tx, ty; /* Top left corner coordinates of the text field */ -+ __u16 twidth, theight; /* Width and height of the text field */ -+ compat_uptr_t theme; -+}; -+ -+#define vc_decor_from_compat(to, from) \ -+ (to).bg_color = (from).bg_color; \ -+ (to).state = (from).state; \ -+ (to).tx = (from).tx; \ -+ (to).ty = (from).ty; \ -+ (to).twidth = (from).twidth; \ -+ (to).theight = (from).theight; \ -+ (to).theme = compat_ptr((from).theme) -+ -+#define vc_decor_to_compat(to, from) \ -+ (to).bg_color = (from).bg_color; \ -+ (to).state = (from).state; \ -+ (to).tx = (from).tx; \ -+ (to).ty = (from).ty; \ -+ (to).twidth = (from).twidth; \ -+ (to).theight = (from).theight; \ -+ (to).theme = ptr_to_compat((from).theme) -+ -+#endif /* CONFIG_COMPAT */ -+#endif /* __KERNEL__ */ -+ -+#endif -diff -Nur linux-3.13.7.orig/include/linux/console_struct.h linux-3.13.7/include/linux/console_struct.h ---- linux-3.13.7.orig/include/linux/console_struct.h 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/include/linux/console_struct.h 2014-03-29 16:03:17.000000000 +0100 -@@ -19,6 +19,7 @@ - struct vt_struct; - - #define NPAR 16 -+#include - - struct vc_data { - struct tty_port port; /* Upper level data */ -@@ -107,6 +108,8 @@ - unsigned long vc_uni_pagedir; - unsigned long *vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */ - bool vc_panic_force_write; /* when oops/panic this VC can accept forced output/blanking */ -+ -+ struct vc_decor vc_decor; - /* additional information is in vt_kern.h */ - }; - -diff -Nur linux-3.13.7.orig/include/linux/fb.h linux-3.13.7/include/linux/fb.h ---- linux-3.13.7.orig/include/linux/fb.h 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/include/linux/fb.h 2014-03-29 16:03:17.000000000 +0100 -@@ -219,6 +219,34 @@ - }; - #endif - -+#ifdef __KERNEL__ -+#ifdef CONFIG_COMPAT -+struct fb_image32 { -+ __u32 dx; /* Where to place image */ -+ __u32 dy; -+ __u32 width; /* Size of image */ -+ __u32 height; -+ __u32 fg_color; /* Only used when a mono bitmap */ -+ __u32 bg_color; -+ __u8 depth; /* Depth of the image */ -+ const compat_uptr_t data; /* Pointer to image data */ -+ struct fb_cmap32 cmap; /* color map info */ -+}; -+ -+#define fb_image_from_compat(to, from) \ -+ (to).dx = (from).dx; \ -+ (to).dy = (from).dy; \ -+ (to).width = (from).width; \ -+ (to).height = (from).height; \ -+ (to).fg_color = (from).fg_color; \ -+ (to).bg_color = (from).bg_color; \ -+ (to).depth = (from).depth; \ -+ (to).data = compat_ptr((from).data); \ -+ fb_cmap_from_compat((to).cmap, (from).cmap) -+ -+#endif /* CONFIG_COMPAT */ -+#endif /* __KERNEL__ */ -+ - /* - * Frame buffer operations - * -@@ -489,6 +517,9 @@ - #define FBINFO_STATE_SUSPENDED 1 - u32 state; /* Hardware state i.e suspend */ - void *fbcon_par; /* fbcon use-only private area */ -+ -+ struct fb_image bgdecor; -+ - /* From here on everything is device dependent */ - void *par; - /* we need the PCI or similar aperture base/size not -diff -Nur linux-3.13.7.orig/include/uapi/linux/fb.h linux-3.13.7/include/uapi/linux/fb.h ---- linux-3.13.7.orig/include/uapi/linux/fb.h 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/include/uapi/linux/fb.h 2014-03-29 16:03:17.000000000 +0100 -@@ -8,6 +8,25 @@ - - #define FB_MAX 32 /* sufficient for now */ - -+struct fbcon_decor_iowrapper -+{ -+ unsigned short vc; /* Virtual console */ -+ unsigned char origin; /* Point of origin of the request */ -+ void *data; -+}; -+ -+#ifdef __KERNEL__ -+#ifdef CONFIG_COMPAT -+#include -+struct fbcon_decor_iowrapper32 -+{ -+ unsigned short vc; /* Virtual console */ -+ unsigned char origin; /* Point of origin of the request */ -+ compat_uptr_t data; -+}; -+#endif /* CONFIG_COMPAT */ -+#endif /* __KERNEL__ */ -+ - /* ioctls - 0x46 is 'F' */ - #define FBIOGET_VSCREENINFO 0x4600 -@@ -35,6 +54,25 @@ - #define FBIOGET_DISPINFO 0x4618 - #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) - -+#define FBIOCONDECOR_SETCFG _IOWR('F', 0x19, struct fbcon_decor_iowrapper) -+#define FBIOCONDECOR_GETCFG _IOR('F', 0x1A, struct fbcon_decor_iowrapper) -+#define FBIOCONDECOR_SETSTATE _IOWR('F', 0x1B, struct fbcon_decor_iowrapper) -+#define FBIOCONDECOR_GETSTATE _IOR('F', 0x1C, struct fbcon_decor_iowrapper) -+#define FBIOCONDECOR_SETPIC _IOWR('F', 0x1D, struct fbcon_decor_iowrapper) -+#ifdef __KERNEL__ -+#ifdef CONFIG_COMPAT -+#define FBIOCONDECOR_SETCFG32 _IOWR('F', 0x19, struct fbcon_decor_iowrapper32) -+#define FBIOCONDECOR_GETCFG32 _IOR('F', 0x1A, struct fbcon_decor_iowrapper32) -+#define FBIOCONDECOR_SETSTATE32 _IOWR('F', 0x1B, struct fbcon_decor_iowrapper32) -+#define FBIOCONDECOR_GETSTATE32 _IOR('F', 0x1C, struct fbcon_decor_iowrapper32) -+#define FBIOCONDECOR_SETPIC32 _IOWR('F', 0x1D, struct fbcon_decor_iowrapper32) -+#endif /* CONFIG_COMPAT */ -+#endif /* __KERNEL__ */ -+ -+#define FBCON_DECOR_THEME_LEN 128 /* Maximum lenght of a theme name */ -+#define FBCON_DECOR_IO_ORIG_KERNEL 0 /* Kernel ioctl origin */ -+#define FBCON_DECOR_IO_ORIG_USER 1 /* User ioctl origin */ -+ - #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ - #define FB_TYPE_PLANES 1 /* Non interleaved planes */ - #define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes */ -@@ -277,6 +315,29 @@ - __u32 reserved[4]; /* Reserved for future compatibility */ - }; - -+#ifdef __KERNEL__ -+#ifdef CONFIG_COMPAT -+struct fb_cmap32 { -+ __u32 start; -+ __u32 len; /* Number of entries */ -+ compat_uptr_t red; /* Red values */ -+ compat_uptr_t green; -+ compat_uptr_t blue; -+ compat_uptr_t transp; /* transparency, can be NULL */ -+}; -+ -+#define fb_cmap_from_compat(to, from) \ -+ (to).start = (from).start; \ -+ (to).len = (from).len; \ -+ (to).red = compat_ptr((from).red); \ -+ (to).green = compat_ptr((from).green); \ -+ (to).blue = compat_ptr((from).blue); \ -+ (to).transp = compat_ptr((from).transp) -+ -+#endif /* CONFIG_COMPAT */ -+#endif /* __KERNEL__ */ -+ -+ - struct fb_cmap { - __u32 start; /* First entry */ - __u32 len; /* Number of entries */ -diff -Nur linux-3.13.7.orig/kernel/sysctl.c linux-3.13.7/kernel/sysctl.c ---- linux-3.13.7.orig/kernel/sysctl.c 2014-03-24 05:45:42.000000000 +0100 -+++ linux-3.13.7/kernel/sysctl.c 2014-03-29 16:03:17.000000000 +0100 -@@ -143,6 +143,10 @@ - static int ngroups_max = NGROUPS_MAX; - static const int cap_last_cap = CAP_LAST_CAP; - -+#ifdef CONFIG_FB_CON_DECOR -+extern char fbcon_decor_path[]; -+#endif -+ - #ifdef CONFIG_INOTIFY_USER - #include - #endif -@@ -252,6 +256,15 @@ - .mode = 0555, - .child = dev_table, - }, -+#ifdef CONFIG_FB_CON_DECOR -+ { -+ .procname = "fbcondecor", -+ .data = &fbcon_decor_path, -+ .maxlen = KMOD_PATH_LEN, -+ .mode = 0644, -+ .proc_handler = &proc_dostring, -+ }, -+#endif - { } - }; - diff --git a/target/linux/patches/3.14.43/gemalto.patch b/target/linux/patches/3.14.43/gemalto.patch deleted file mode 100644 index 65f7af1d7..000000000 --- a/target/linux/patches/3.14.43/gemalto.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c linux-2.6.36/drivers/serial/8250/serial_cs.c ---- linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c 2010-10-20 22:30:22.000000000 +0200 -+++ linux-2.6.36/drivers/tty/serial/8250/serial_cs.c 2010-12-13 23:03:40.000000000 +0100 -@@ -794,6 +794,7 @@ - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), -+ PCMCIA_DEVICE_MANF_CARD(0x0157, 0x0100), /* Gemalto SCR */ - PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ - PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ - PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), diff --git a/target/linux/patches/3.14.43/initramfs-nosizelimit.patch b/target/linux/patches/3.14.43/initramfs-nosizelimit.patch deleted file mode 100644 index 40d2f6bd8..000000000 --- a/target/linux/patches/3.14.43/initramfs-nosizelimit.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 9a18df7a71bfa620b1278777d64783a359d7eb4e Mon Sep 17 00:00:00 2001 -From: Thorsten Glaser -Date: Sun, 4 May 2014 01:37:54 +0200 -Subject: [PATCH] mount tmpfs-as-rootfs (initramfs) with -o - nr_blocks=0,nr_inodes=0 - -I would have preferred to write this patch to be able to pass -rootflags=nr_blocks=0,nr_inodes=0 on the kernel command line, -and then hand these rootflags over to the initramfs (tmpfs) -mount in the same way the kernel hands them over to the block -device rootfs mount. But at least the Debian/m68k initrd also -parses $rootflags from the environment and adds it to the call -to the user-space mount for the eventual root device, which -would make the kernel command line rootflags option be used in -both places (tmpfs and e.g. ext4) which is guaranteed to error -out in at least one of them. - -This change is intended to aid people in a setup where the -initrd is the final root filesystem, i.e. not mounted over. -This is especially useful in automated tests running on qemu -for boards with constrained memory (e.g. 64 MiB on sh4). - -Considering that the initramfs is normally emptied out then -overmounted, this change is probably safe for setups where -initramfs just hosts early userspace, too, since the tmpfs -backing it is not accessible any more later on, AFAICT. - -Signed-off-by: Thorsten Glaser ---- - init/do_mounts.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/init/do_mounts.c b/init/do_mounts.c -index 82f2288..55a4cfe 100644 ---- a/init/do_mounts.c -+++ b/init/do_mounts.c -@@ -594,6 +594,7 @@ out: - } - - static bool is_tmpfs; -+static char tmpfs_rootflags[] = "nr_blocks=0,nr_inodes=0"; - static struct dentry *rootfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) - { -@@ -606,6 +607,9 @@ static struct dentry *rootfs_mount(struct file_system_type *fs_type, - if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) - fill = shmem_fill_super; - -+ if (is_tmpfs) -+ data = tmpfs_rootflags; -+ - return mount_nodev(fs_type, flags, data, fill); - } - --- -2.0.0.rc0 - diff --git a/target/linux/patches/3.14.43/lemote-rfkill.patch b/target/linux/patches/3.14.43/lemote-rfkill.patch deleted file mode 100644 index a61488434..000000000 --- a/target/linux/patches/3.14.43/lemote-rfkill.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -Nur linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c ---- linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-19 00:15:34.000000000 +0100 -+++ linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-27 23:29:46.000000000 +0200 -@@ -22,6 +22,9 @@ - - static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) - { -+#ifdef CONFIG_LEMOTE_MACH2F -+ return 1; -+#else - u8 gpio; - - gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); -@@ -29,6 +32,7 @@ - gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); - - return gpio & priv->rfkill_mask; -+#endif - } - - void rtl8187_rfkill_init(struct ieee80211_hw *hw) diff --git a/target/linux/patches/3.14.43/microblaze-axi.patch b/target/linux/patches/3.14.43/microblaze-axi.patch deleted file mode 100644 index 1a4b17d8c..000000000 --- a/target/linux/patches/3.14.43/microblaze-axi.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.13.3.orig/drivers/net/ethernet/xilinx/xilinx_axienet_main.c linux-3.13.3/drivers/net/ethernet/xilinx/xilinx_axienet_main.c ---- linux-3.13.3.orig/drivers/net/ethernet/xilinx/xilinx_axienet_main.c 2014-02-13 23:00:14.000000000 +0100 -+++ linux-3.13.3/drivers/net/ethernet/xilinx/xilinx_axienet_main.c 2014-02-24 08:03:57.000000000 +0100 -@@ -25,6 +25,7 @@ - #include - #include - #include -+#include - #include - #include - #include diff --git a/target/linux/patches/3.14.43/microblaze-ethernet.patch b/target/linux/patches/3.14.43/microblaze-ethernet.patch deleted file mode 100644 index 742ab477e..000000000 --- a/target/linux/patches/3.14.43/microblaze-ethernet.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c ---- linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-11-29 19:42:37.000000000 +0100 -+++ linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-12-23 20:01:14.000000000 +0100 -@@ -1282,6 +1282,7 @@ - { .compatible = "xlnx,opb-ethernetlite-1.01.b", }, - { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, - { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, -+ { .compatible = "xlnx,xps-ethernetlite-2.00.b", }, - { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, - { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, - { /* end of list */ }, diff --git a/target/linux/patches/3.14.43/mkpiggy.patch b/target/linux/patches/3.14.43/mkpiggy.patch deleted file mode 100644 index 751678b74..000000000 --- a/target/linux/patches/3.14.43/mkpiggy.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff -Nur linux-3.13.3.orig/arch/x86/boot/compressed/mkpiggy.c linux-3.13.3/arch/x86/boot/compressed/mkpiggy.c ---- linux-3.13.3.orig/arch/x86/boot/compressed/mkpiggy.c 2014-02-13 23:00:14.000000000 +0100 -+++ linux-3.13.3/arch/x86/boot/compressed/mkpiggy.c 2014-02-17 11:09:06.000000000 +0100 -@@ -29,7 +29,14 @@ - #include - #include - #include --#include -+ -+static uint32_t getle32(const void *p) -+{ -+ const uint8_t *cp = p; -+ -+ return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + -+ ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); -+} - - int main(int argc, char *argv[]) - { -@@ -63,7 +70,7 @@ - } - - ilen = ftell(f); -- olen = get_unaligned_le32(&olen); -+ olen = getle32(&olen); - - /* - * Now we have the input (compressed) and output (uncompressed) diff --git a/target/linux/patches/3.14.43/mptcp.patch b/target/linux/patches/3.14.43/mptcp.patch deleted file mode 100644 index 9784e0577..000000000 --- a/target/linux/patches/3.14.43/mptcp.patch +++ /dev/null @@ -1,17203 +0,0 @@ -diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c -index d286bde..34d56d7 100644 ---- a/drivers/infiniband/hw/cxgb4/cm.c -+++ b/drivers/infiniband/hw/cxgb4/cm.c -@@ -3162,7 +3162,7 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos) - */ - memset(&tmp_opt, 0, sizeof(tmp_opt)); - tcp_clear_options(&tmp_opt); -- tcp_parse_options(skb, &tmp_opt, 0, NULL); -+ tcp_parse_options(skb, &tmp_opt, NULL, 0, NULL); - - req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req)); - memset(req, 0, sizeof(*req)); -diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h -index 2faef33..9c12362 100644 ---- a/include/linux/ipv6.h -+++ b/include/linux/ipv6.h -@@ -309,12 +309,6 @@ static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk) - return NULL; - } - --static inline struct inet6_request_sock * -- inet6_rsk(const struct request_sock *rsk) --{ -- return NULL; --} -- - static inline struct raw6_sock *raw6_sk(const struct sock *sk) - { - return NULL; -diff --git a/include/linux/tcp.h b/include/linux/tcp.h -index 4ad0706..a230dd0 100644 ---- a/include/linux/tcp.h -+++ b/include/linux/tcp.h -@@ -72,6 +72,53 @@ struct tcp_sack_block { - u32 end_seq; - }; - -+struct tcp_out_options { -+ u16 options; /* bit field of OPTION_* */ -+ u8 ws; /* window scale, 0 to disable */ -+ u8 num_sack_blocks;/* number of SACK blocks to include */ -+ u8 hash_size; /* bytes in hash_location */ -+ u16 mss; /* 0 to disable */ -+ __u8 *hash_location; /* temporary pointer, overloaded */ -+ __u32 tsval, tsecr; /* need to include OPTION_TS */ -+ struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie */ -+#ifdef CONFIG_MPTCP -+ u16 mptcp_options; /* bit field of MPTCP related OPTION_* */ -+ u8 dss_csum:1, -+ add_addr_v4:1, -+ add_addr_v6:1; /* dss-checksum required? */ -+ -+ __u32 data_seq; /* data sequence number, for MPTCP */ -+ __u32 data_ack; /* data ack, for MPTCP */ -+ -+ union { -+ struct { -+ __u64 sender_key; /* sender's key for mptcp */ -+ __u64 receiver_key; /* receiver's key for mptcp */ -+ } mp_capable; -+ -+ struct { -+ __u64 sender_truncated_mac; -+ __u32 sender_nonce; -+ /* random number of the sender */ -+ __u32 token; /* token for mptcp */ -+ } mp_join_syns; -+ }; -+ -+ struct { -+ struct in_addr addr; -+ u8 addr_id; -+ } add_addr4; -+ -+ struct { -+ struct in6_addr addr; -+ u8 addr_id; -+ } add_addr6; -+ -+ u16 remove_addrs; /* list of address id */ -+ u8 addr_id; /* address id (mp_join or add_address) */ -+#endif /* CONFIG_MPTCP */ -+}; -+ - /*These are used to set the sack_ok field in struct tcp_options_received */ - #define TCP_SACK_SEEN (1 << 0) /*1 = peer is SACK capable, */ - #define TCP_FACK_ENABLED (1 << 1) /*1 = FACK is enabled locally*/ -@@ -95,6 +142,9 @@ struct tcp_options_received { - u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ - }; - -+struct mptcp_cb; -+struct mptcp_tcp_sock; -+ - static inline void tcp_clear_options(struct tcp_options_received *rx_opt) - { - rx_opt->tstamp_ok = rx_opt->sack_ok = 0; -@@ -123,6 +173,7 @@ struct tcp_request_sock { - * FastOpen it's the seq# - * after data-in-SYN. - */ -+ u8 saw_mpc:1; - }; - - static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) -@@ -130,6 +181,8 @@ static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) - return (struct tcp_request_sock *)req; - } - -+struct tcp_md5sig_key; -+ - struct tcp_sock { - /* inet_connection_sock has to be the first member of tcp_sock */ - struct inet_connection_sock inet_conn; -@@ -323,6 +376,45 @@ struct tcp_sock { - * socket. Used to retransmit SYNACKs etc. - */ - struct request_sock *fastopen_rsk; -+ -+ -+ struct mptcp_cb *mpcb; -+ struct sock *meta_sk; -+ /* We keep these flags even if CONFIG_MPTCP is not checked, because -+ * it allows checking MPTCP capability just by checking the mpc flag, -+ * rather than adding ifdefs everywhere. -+ */ -+ u16 mpc:1, /* Other end is multipath capable */ -+ inside_tk_table:1, /* Is the tcp_sock inside the token-table? */ -+ send_mp_fclose:1, -+ request_mptcp:1, /* Did we send out an MP_CAPABLE? -+ * (this speeds up mptcp_doit() in tcp_recvmsg) -+ */ -+ mptcp_enabled:1, /* Is MPTCP enabled from the application ? */ -+ pf:1, /* Potentially Failed state: when this flag is set, we -+ * stop using the subflow -+ */ -+ mp_killed:1, /* Killed with a tcp_done in mptcp? */ -+ was_meta_sk:1, /* This was a meta sk (in case of reuse) */ -+ close_it:1, /* Must close socket in mptcp_data_ready? */ -+ closing:1; -+ struct mptcp_tcp_sock *mptcp; -+#ifdef CONFIG_MPTCP -+ struct hlist_nulls_node tk_table; -+ u32 mptcp_loc_token; -+ u64 mptcp_loc_key; -+#endif /* CONFIG_MPTCP */ -+ -+ /* Functions that depend on the value of the mpc flag */ -+ u32 (*__select_window)(struct sock *sk); -+ u16 (*select_window)(struct sock *sk); -+ void (*select_initial_window)(int __space, __u32 mss, __u32 *rcv_wnd, -+ __u32 *window_clamp, int wscale_ok, -+ __u8 *rcv_wscale, __u32 init_rcv_wnd, -+ const struct sock *sk); -+ void (*init_buffer_space)(struct sock *sk); -+ void (*set_rto)(struct sock *sk); -+ bool (*should_expand_sndbuf)(const struct sock *sk); - }; - - enum tsq_flags { -@@ -334,6 +426,8 @@ enum tsq_flags { - TCP_MTU_REDUCED_DEFERRED, /* tcp_v{4|6}_err() could not call - * tcp_v{4|6}_mtu_reduced() - */ -+ MPTCP_PATH_MANAGER, /* MPTCP deferred creation of new subflows */ -+ MPTCP_SUB_DEFERRED, /* A subflow got deferred - process them */ - }; - - static inline struct tcp_sock *tcp_sk(const struct sock *sk) -@@ -352,6 +446,7 @@ struct tcp_timewait_sock { - #ifdef CONFIG_TCP_MD5SIG - struct tcp_md5sig_key *tw_md5_key; - #endif -+ struct mptcp_tw *mptcp_tw; - }; - - static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk) -diff --git a/include/net/inet6_connection_sock.h b/include/net/inet6_connection_sock.h -index f981ba7..0144c65 100644 ---- a/include/net/inet6_connection_sock.h -+++ b/include/net/inet6_connection_sock.h -@@ -27,6 +27,8 @@ int inet6_csk_bind_conflict(const struct sock *sk, - - struct dst_entry *inet6_csk_route_req(struct sock *sk, struct flowi6 *fl6, - const struct request_sock *req); -+u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, -+ const u32 rnd, const u32 synq_hsize); - - struct request_sock *inet6_csk_search_req(const struct sock *sk, - struct request_sock ***prevp, -diff --git a/include/net/inet_common.h b/include/net/inet_common.h -index fe7994c..780f229 100644 ---- a/include/net/inet_common.h -+++ b/include/net/inet_common.h -@@ -1,6 +1,8 @@ - #ifndef _INET_COMMON_H - #define _INET_COMMON_H - -+#include -+ - extern const struct proto_ops inet_stream_ops; - extern const struct proto_ops inet_dgram_ops; - -@@ -13,6 +15,8 @@ struct sock; - struct sockaddr; - struct socket; - -+int inet_create(struct net *net, struct socket *sock, int protocol, int kern); -+int inet6_create(struct net *net, struct socket *sock, int protocol, int kern); - int inet_release(struct socket *sock); - int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, - int addr_len, int flags); -diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h -index c55aeed..84d1927 100644 ---- a/include/net/inet_connection_sock.h -+++ b/include/net/inet_connection_sock.h -@@ -243,6 +243,9 @@ static inline void inet_csk_reset_xmit_timer(struct sock *sk, const int what, - - struct sock *inet_csk_accept(struct sock *sk, int flags, int *err); - -+u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd, -+ const u32 synq_hsize); -+ - struct request_sock *inet_csk_search_req(const struct sock *sk, - struct request_sock ***prevp, - const __be16 rport, -diff --git a/include/net/mptcp.h b/include/net/mptcp.h -new file mode 100644 -index 0000000..6454535 ---- /dev/null -+++ b/include/net/mptcp.h -@@ -0,0 +1,1471 @@ -+/* -+ * MPTCP implementation -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer & Author: -+ * Christoph Paasch -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MPTCP_H -+#define _MPTCP_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ #define ntohll(x) be64_to_cpu(x) -+ #define htonll(x) cpu_to_be64(x) -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ #define ntohll(x) (x) -+ #define htonll(x) (x) -+#endif -+ -+/* Max number of local or remote addresses we can store. -+ * When changing, see the bitfield below in mptcp_loc4/6. */ -+#define MPTCP_MAX_ADDR 8 -+ -+#define MPTCP_SUBFLOW_RETRY_DELAY 1000 -+ -+struct mptcp_loc4 { -+ u8 loc4_id; -+ u8 low_prio:1; -+ struct in_addr addr; -+}; -+ -+struct mptcp_rem4 { -+ u8 rem4_id; -+ u8 bitfield; -+ u8 retry_bitfield; -+ __be16 port; -+ struct in_addr addr; -+}; -+ -+struct mptcp_loc6 { -+ u8 loc6_id; -+ u8 low_prio:1; -+ struct in6_addr addr; -+}; -+ -+struct mptcp_rem6 { -+ u8 rem6_id; -+ u8 bitfield; -+ u8 retry_bitfield; -+ __be16 port; -+ struct in6_addr addr; -+}; -+ -+struct mptcp_request_sock { -+ struct tcp_request_sock req; -+ struct mptcp_cb *mpcb; -+ /* Collision list in the tuple hashtable. We need to find -+ * the req sock when receiving the third msg of the 3-way handshake, -+ * since that one does not contain the token. If this makes -+ * the request sock too long, we can use kmalloc'ed specific entries for -+ * that tuple hashtable. At the moment, though, I extend the -+ * request_sock. -+ */ -+ struct list_head collide_tuple; -+ struct hlist_nulls_node collide_tk; -+ u32 mptcp_rem_nonce; -+ u32 mptcp_loc_token; -+ u64 mptcp_loc_key; -+ u64 mptcp_rem_key; -+ u64 mptcp_hash_tmac; -+ u32 mptcp_loc_nonce; -+ u8 loc_id; -+ u8 rem_id; /* Address-id in the MP_JOIN */ -+ u8 dss_csum:1, -+ low_prio:1; -+}; -+ -+struct mptcp_options_received { -+ u16 saw_mpc:1, -+ dss_csum:1, -+ drop_me:1, -+ -+ is_mp_join:1, -+ join_ack:1, -+ -+ saw_low_prio:2, /* 0x1 - low-prio set for this subflow -+ * 0x2 - low-prio set for another subflow -+ */ -+ low_prio:1, -+ -+ saw_add_addr:2, /* Saw at least one add_addr option: -+ * 0x1: IPv4 - 0x2: IPv6 -+ */ -+ more_add_addr:1, /* Saw one more add-addr. */ -+ -+ saw_rem_addr:1, /* Saw at least one rem_addr option */ -+ more_rem_addr:1, /* Saw one more rem-addr. */ -+ -+ mp_fail:1, -+ mp_fclose:1; -+ u8 rem_id; /* Address-id in the MP_JOIN */ -+ u8 prio_addr_id; /* Address-id in the MP_PRIO */ -+ -+ const unsigned char *add_addr_ptr; /* Pointer to add-address option */ -+ const unsigned char *rem_addr_ptr; /* Pointer to rem-address option */ -+ -+ u32 data_ack; -+ u32 data_seq; -+ u16 data_len; -+ -+ u32 mptcp_rem_token;/* Remote token */ -+ -+ /* Key inside the option (from mp_capable or fast_close) */ -+ u64 mptcp_key; -+ -+ u32 mptcp_recv_nonce; -+ u64 mptcp_recv_tmac; -+ u8 mptcp_recv_mac[20]; -+}; -+ -+struct mptcp_tcp_sock { -+ struct tcp_sock *next; /* Next subflow socket */ -+ struct list_head cb_list; -+ struct mptcp_options_received rx_opt; -+ -+ /* Those three fields record the current mapping */ -+ u64 map_data_seq; -+ u32 map_subseq; -+ u16 map_data_len; -+ u16 slave_sk:1, -+ fully_established:1, -+ establish_increased:1, -+ second_packet:1, -+ attached:1, -+ send_mp_fail:1, -+ include_mpc:1, -+ mapping_present:1, -+ map_data_fin:1, -+ low_prio:1, /* use this socket as backup */ -+ rcv_low_prio:1, /* Peer sent low-prio option to us */ -+ send_mp_prio:1, /* Trigger to send mp_prio on this socket */ -+ pre_established:1; /* State between sending 3rd ACK and -+ * receiving the fourth ack of new subflows. -+ */ -+ -+ /* isn: needed to translate abs to relative subflow seqnums */ -+ u32 snt_isn; -+ u32 rcv_isn; -+ u32 last_data_seq; -+ u8 path_index; -+ u8 loc_id; -+ u8 rem_id; -+ -+ u32 last_rbuf_opti; /* Timestamp of last rbuf optimization */ -+ unsigned int sent_pkts; -+ -+ struct sk_buff *shortcut_ofoqueue; /* Shortcut to the current modified -+ * skb in the ofo-queue. -+ */ -+ -+ int init_rcv_wnd; -+ u32 infinite_cutoff_seq; -+ struct delayed_work work; -+ u32 mptcp_loc_nonce; -+ struct tcp_sock *tp; /* Where is my daddy? */ -+ u32 last_end_data_seq; -+ -+ /* MP_JOIN subflow: timer for retransmitting the 3rd ack */ -+ struct timer_list mptcp_ack_timer; -+ -+ /* HMAC of the third ack */ -+ char sender_mac[20]; -+}; -+ -+struct mptcp_tw { -+ struct list_head list; -+ u64 loc_key; -+ u64 rcv_nxt; -+ struct mptcp_cb __rcu *mpcb; -+ u8 meta_tw:1, -+ in_list:1; -+}; -+ -+#define MPTCP_PM_NAME_MAX 16 -+struct mptcp_pm_ops { -+ struct list_head list; -+ -+ /* Signal the creation of a new MPTCP-session. */ -+ void (*new_session)(struct sock *meta_sk, int index); -+ void (*release_sock)(struct sock *meta_sk); -+ void (*fully_established)(struct sock *meta_sk); -+ void (*new_remote_address)(struct sock *meta_sk); -+ int (*get_local_index)(sa_family_t family, union inet_addr *addr, -+ struct net *net); -+ int (*get_local_id)(sa_family_t family, union inet_addr *addr, -+ struct net *net); -+ void (*addr_signal)(struct sock *sk, unsigned *size, -+ struct tcp_out_options *opts, struct sk_buff *skb); -+ -+ char name[MPTCP_PM_NAME_MAX]; -+ struct module *owner; -+}; -+ -+struct mptcp_cb { -+ struct sock *meta_sk; -+ -+ /* list of sockets in this multipath connection */ -+ struct tcp_sock *connection_list; -+ /* list of sockets that need a call to release_cb */ -+ struct list_head callback_list; -+ -+ spinlock_t tw_lock; -+ struct list_head tw_list; -+ unsigned char mptw_state; -+ -+ atomic_t mpcb_refcnt; -+ -+ /* High-order bits of 64-bit sequence numbers */ -+ u32 snd_high_order[2]; -+ u32 rcv_high_order[2]; -+ -+ u16 send_infinite_mapping:1, -+ in_time_wait:1, -+ list_rcvd:1, /* XXX TO REMOVE */ -+ dss_csum:1, -+ server_side:1, -+ infinite_mapping_rcv:1, -+ infinite_mapping_snd:1, -+ dfin_combined:1, /* Was the DFIN combined with subflow-fin? */ -+ passive_close:1, -+ snd_hiseq_index:1, /* Index in snd_high_order of snd_nxt */ -+ rcv_hiseq_index:1; /* Index in rcv_high_order of rcv_nxt */ -+ -+ /* socket count in this connection */ -+ u8 cnt_subflows; -+ u8 cnt_established; -+ -+ u32 noneligible; /* Path mask of temporarily non -+ * eligible subflows by the scheduler -+ */ -+ -+ struct sk_buff_head reinject_queue; -+ -+ u8 dfin_path_index; -+ -+#define MPTCP_PM_SIZE 320 -+ u8 mptcp_pm[MPTCP_PM_SIZE] __aligned(8); -+ struct mptcp_pm_ops *pm_ops; -+ -+ /* Mutex needed, because otherwise mptcp_close will complain that the -+ * socket is owned by the user. -+ * E.g., mptcp_sub_close_wq is taking the meta-lock. -+ */ -+ struct mutex mpcb_mutex; -+ -+ /* Master socket, also part of the connection_list, this -+ * socket is the one that the application sees. -+ */ -+ struct sock *master_sk; -+ -+ u64 csum_cutoff_seq; -+ -+ __u64 mptcp_loc_key; -+ __u32 mptcp_loc_token; -+ __u64 mptcp_rem_key; -+ __u32 mptcp_rem_token; -+ -+ /* Create a new subflow - necessary because the meta-sk may be IPv4, but -+ * the new subflow can be IPv6 -+ */ -+ struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb, -+ struct request_sock *req, -+ struct dst_entry *dst); -+ -+ /* Remote addresses */ -+ struct mptcp_rem4 remaddr4[MPTCP_MAX_ADDR]; -+ u8 rem4_bits; -+ -+ struct mptcp_rem6 remaddr6[MPTCP_MAX_ADDR]; -+ u8 rem6_bits; -+ -+ u32 path_index_bits; -+ /* Next pi to pick up in case a new path becomes available */ -+ u8 next_path_index; -+ -+ /* Original snd/rcvbuf of the initial subflow. -+ * Used for the new subflows on the server-side to allow correct -+ * autotuning -+ */ -+ int orig_sk_rcvbuf; -+ int orig_sk_sndbuf; -+ u32 orig_window_clamp; -+}; -+ -+#define MPTCP_SUB_CAPABLE 0 -+#define MPTCP_SUB_LEN_CAPABLE_SYN 12 -+#define MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN 12 -+#define MPTCP_SUB_LEN_CAPABLE_ACK 20 -+#define MPTCP_SUB_LEN_CAPABLE_ACK_ALIGN 20 -+ -+#define MPTCP_SUB_JOIN 1 -+#define MPTCP_SUB_LEN_JOIN_SYN 12 -+#define MPTCP_SUB_LEN_JOIN_SYN_ALIGN 12 -+#define MPTCP_SUB_LEN_JOIN_SYNACK 16 -+#define MPTCP_SUB_LEN_JOIN_SYNACK_ALIGN 16 -+#define MPTCP_SUB_LEN_JOIN_ACK 24 -+#define MPTCP_SUB_LEN_JOIN_ACK_ALIGN 24 -+ -+#define MPTCP_SUB_DSS 2 -+#define MPTCP_SUB_LEN_DSS 4 -+#define MPTCP_SUB_LEN_DSS_ALIGN 4 -+ -+/* Lengths for seq and ack are the ones without the generic MPTCP-option header, -+ * as they are part of the DSS-option. -+ * To get the total length, just add the different options together. -+ */ -+#define MPTCP_SUB_LEN_SEQ 10 -+#define MPTCP_SUB_LEN_SEQ_CSUM 12 -+#define MPTCP_SUB_LEN_SEQ_ALIGN 12 -+ -+#define MPTCP_SUB_LEN_SEQ_64 14 -+#define MPTCP_SUB_LEN_SEQ_CSUM_64 16 -+#define MPTCP_SUB_LEN_SEQ_64_ALIGN 16 -+ -+#define MPTCP_SUB_LEN_ACK 4 -+#define MPTCP_SUB_LEN_ACK_ALIGN 4 -+ -+#define MPTCP_SUB_LEN_ACK_64 8 -+#define MPTCP_SUB_LEN_ACK_64_ALIGN 8 -+ -+/* This is the "default" option-length we will send out most often. -+ * MPTCP DSS-header -+ * 32-bit data sequence number -+ * 32-bit data ack -+ * -+ * It is necessary to calculate the effective MSS we will be using when -+ * sending data. -+ */ -+#define MPTCP_SUB_LEN_DSM_ALIGN (MPTCP_SUB_LEN_DSS_ALIGN + \ -+ MPTCP_SUB_LEN_SEQ_ALIGN + \ -+ MPTCP_SUB_LEN_ACK_ALIGN) -+ -+#define MPTCP_SUB_ADD_ADDR 3 -+#define MPTCP_SUB_LEN_ADD_ADDR4 8 -+#define MPTCP_SUB_LEN_ADD_ADDR6 20 -+#define MPTCP_SUB_LEN_ADD_ADDR4_ALIGN 8 -+#define MPTCP_SUB_LEN_ADD_ADDR6_ALIGN 20 -+ -+#define MPTCP_SUB_REMOVE_ADDR 4 -+#define MPTCP_SUB_LEN_REMOVE_ADDR 4 -+ -+#define MPTCP_SUB_PRIO 5 -+#define MPTCP_SUB_LEN_PRIO 3 -+#define MPTCP_SUB_LEN_PRIO_ADDR 4 -+#define MPTCP_SUB_LEN_PRIO_ALIGN 4 -+ -+#define MPTCP_SUB_FAIL 6 -+#define MPTCP_SUB_LEN_FAIL 12 -+#define MPTCP_SUB_LEN_FAIL_ALIGN 12 -+ -+#define MPTCP_SUB_FCLOSE 7 -+#define MPTCP_SUB_LEN_FCLOSE 12 -+#define MPTCP_SUB_LEN_FCLOSE_ALIGN 12 -+ -+ -+#define OPTION_MPTCP (1 << 5) -+ -+static inline void reset_mpc(struct tcp_sock *tp) -+{ -+ tp->mpc = 0; -+ -+ tp->__select_window = __tcp_select_window; -+ tp->select_window = tcp_select_window; -+ tp->select_initial_window = tcp_select_initial_window; -+ tp->init_buffer_space = tcp_init_buffer_space; -+ tp->set_rto = tcp_set_rto; -+ tp->should_expand_sndbuf = tcp_should_expand_sndbuf; -+} -+ -+/* Initializes MPTCP flags in tcp_sock (and other tcp_sock members that depend -+ * on those flags). -+ */ -+static inline void mptcp_init_tcp_sock(struct tcp_sock *tp) -+{ -+ reset_mpc(tp); -+} -+ -+#ifdef CONFIG_MPTCP -+ -+/* Used for checking if the mptcp initialization has been successful */ -+extern bool mptcp_init_failed; -+ -+/* MPTCP options */ -+#define OPTION_TYPE_SYN (1 << 0) -+#define OPTION_TYPE_SYNACK (1 << 1) -+#define OPTION_TYPE_ACK (1 << 2) -+#define OPTION_MP_CAPABLE (1 << 3) -+#define OPTION_DATA_ACK (1 << 4) -+#define OPTION_ADD_ADDR (1 << 5) -+#define OPTION_MP_JOIN (1 << 6) -+#define OPTION_MP_FAIL (1 << 7) -+#define OPTION_MP_FCLOSE (1 << 8) -+#define OPTION_REMOVE_ADDR (1 << 9) -+#define OPTION_MP_PRIO (1 << 10) -+ -+/* MPTCP flags */ -+#define MPTCPHDR_ACK 0x01 -+#define MPTCPHDR_SEQ 0x02 -+#define MPTCPHDR_FIN 0x04 -+#define MPTCPHDR_INF 0x08 -+#define MPTCPHDR_SEQ64_SET 0x10 /* Did we received a 64-bit seq number */ -+#define MPTCPHDR_SEQ64_OFO 0x20 /* Is it not in our circular array? */ -+#define MPTCPHDR_SEQ64_INDEX 0x40 /* Index of seq in mpcb->snd_high_order */ -+#define MPTCPHDR_DSS_CSUM 0x80 -+ -+/* It is impossible, that all 8 bits of mptcp_flags are set to 1 with the above -+ * Thus, defining MPTCPHDR_JOIN as 0xFF is safe. -+ */ -+#define MPTCPHDR_JOIN 0xFF -+ -+struct mptcp_option { -+ __u8 kind; -+ __u8 len; -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ __u8 ver:4, -+ sub:4; -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ __u8 sub:4, -+ ver:4; -+#else -+#error "Adjust your defines" -+#endif -+}; -+ -+struct mp_capable { -+ __u8 kind; -+ __u8 len; -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ __u8 ver:4, -+ sub:4; -+ __u8 h:1, -+ rsv:5, -+ b:1, -+ a:1; -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ __u8 sub:4, -+ ver:4; -+ __u8 a:1, -+ b:1, -+ rsv:5, -+ h:1; -+#else -+#error "Adjust your defines" -+#endif -+ __u64 sender_key; -+ __u64 receiver_key; -+} __attribute__((__packed__)); -+ -+struct mp_join { -+ __u8 kind; -+ __u8 len; -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ __u8 b:1, -+ rsv:3, -+ sub:4; -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ __u8 sub:4, -+ rsv:3, -+ b:1; -+#else -+#error "Adjust your defines" -+#endif -+ __u8 addr_id; -+ union { -+ struct { -+ u32 token; -+ u32 nonce; -+ } syn; -+ struct { -+ __u64 mac; -+ u32 nonce; -+ } synack; -+ struct { -+ __u8 mac[20]; -+ } ack; -+ } u; -+} __attribute__((__packed__)); -+ -+struct mp_dss { -+ __u8 kind; -+ __u8 len; -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ __u16 rsv1:4, -+ sub:4, -+ A:1, -+ a:1, -+ M:1, -+ m:1, -+ F:1, -+ rsv2:3; -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ __u16 sub:4, -+ rsv1:4, -+ rsv2:3, -+ F:1, -+ m:1, -+ M:1, -+ a:1, -+ A:1; -+#else -+#error "Adjust your defines" -+#endif -+}; -+ -+struct mp_add_addr { -+ __u8 kind; -+ __u8 len; -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ __u8 ipver:4, -+ sub:4; -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ __u8 sub:4, -+ ipver:4; -+#else -+#error "Adjust your defines" -+#endif -+ __u8 addr_id; -+ union { -+ struct { -+ struct in_addr addr; -+ __be16 port; -+ } v4; -+ struct { -+ struct in6_addr addr; -+ __be16 port; -+ } v6; -+ } u; -+} __attribute__((__packed__)); -+ -+struct mp_remove_addr { -+ __u8 kind; -+ __u8 len; -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ __u8 rsv:4, -+ sub:4; -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ __u8 sub:4, -+ rsv:4; -+#else -+#error "Adjust your defines" -+#endif -+ /* list of addr_id */ -+ __u8 addrs_id; -+}; -+ -+struct mp_fail { -+ __u8 kind; -+ __u8 len; -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ __u16 rsv1:4, -+ sub:4, -+ rsv2:8; -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ __u16 sub:4, -+ rsv1:4, -+ rsv2:8; -+#else -+#error "Adjust your defines" -+#endif -+ __be64 data_seq; -+} __attribute__((__packed__)); -+ -+struct mp_fclose { -+ __u8 kind; -+ __u8 len; -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ __u16 rsv1:4, -+ sub:4, -+ rsv2:8; -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ __u16 sub:4, -+ rsv1:4, -+ rsv2:8; -+#else -+#error "Adjust your defines" -+#endif -+ __u64 key; -+} __attribute__((__packed__)); -+ -+struct mp_prio { -+ __u8 kind; -+ __u8 len; -+#if defined(__LITTLE_ENDIAN_BITFIELD) -+ __u8 b:1, -+ rsv:3, -+ sub:4; -+#elif defined(__BIG_ENDIAN_BITFIELD) -+ __u8 sub:4, -+ rsv:3, -+ b:1; -+#else -+#error "Adjust your defines" -+#endif -+ __u8 addr_id; -+} __attribute__((__packed__)); -+ -+static inline int mptcp_sub_len_dss(struct mp_dss *m, int csum) -+{ -+ return 4 + m->A * (4 + m->a * 4) + m->M * (10 + m->m * 4 + csum * 2); -+} -+ -+#define MPTCP_APP 2 -+ -+extern int sysctl_mptcp_enabled; -+extern int sysctl_mptcp_checksum; -+extern int sysctl_mptcp_debug; -+extern int sysctl_mptcp_syn_retries; -+ -+extern struct workqueue_struct *mptcp_wq; -+ -+#define mptcp_debug(fmt, args...) \ -+ do { \ -+ if (unlikely(sysctl_mptcp_debug)) \ -+ pr_err(__FILE__ ": " fmt, ##args); \ -+ } while (0) -+ -+/* Iterates over all subflows */ -+#define mptcp_for_each_tp(mpcb, tp) \ -+ for ((tp) = (mpcb)->connection_list; (tp); (tp) = (tp)->mptcp->next) -+ -+#define mptcp_for_each_sk(mpcb, sk) \ -+ for ((sk) = (struct sock *)(mpcb)->connection_list; \ -+ sk; \ -+ sk = (struct sock *)tcp_sk(sk)->mptcp->next) -+ -+#define mptcp_for_each_sk_safe(__mpcb, __sk, __temp) \ -+ for (__sk = (struct sock *)(__mpcb)->connection_list, \ -+ __temp = __sk ? (struct sock *)tcp_sk(__sk)->mptcp->next : NULL; \ -+ __sk; \ -+ __sk = __temp, \ -+ __temp = __sk ? (struct sock *)tcp_sk(__sk)->mptcp->next : NULL) -+ -+/* Iterates over all bit set to 1 in a bitset */ -+#define mptcp_for_each_bit_set(b, i) \ -+ for (i = ffs(b) - 1; i >= 0; i = ffs(b >> (i + 1) << (i + 1)) - 1) -+ -+#define mptcp_for_each_bit_unset(b, i) \ -+ mptcp_for_each_bit_set(~b, i) -+ -+extern struct lock_class_key meta_key; -+extern struct lock_class_key meta_slock_key; -+extern u32 mptcp_secret[MD5_MESSAGE_BYTES / 4]; -+ -+/* This is needed to ensure that two subsequent key-generation result in -+ * different keys if the IPs and ports are the same. -+ */ -+extern u32 mptcp_key_seed; -+ -+#define MPTCP_HASH_SIZE 1024 -+ -+extern struct hlist_nulls_head tk_hashtable[MPTCP_HASH_SIZE]; -+ -+/* This second hashtable is needed to retrieve request socks -+ * created as a result of a join request. While the SYN contains -+ * the token, the final ack does not, so we need a separate hashtable -+ * to retrieve the mpcb. -+ */ -+extern struct list_head mptcp_reqsk_htb[MPTCP_HASH_SIZE]; -+extern spinlock_t mptcp_reqsk_hlock; /* hashtable protection */ -+ -+/* Lock, protecting the two hash-tables that hold the token. Namely, -+ * mptcp_reqsk_tk_htb and tk_hashtable -+ */ -+extern spinlock_t mptcp_tk_hashlock; /* hashtable protection */ -+ -+void mptcp_data_ready(struct sock *sk, int bytes); -+void mptcp_write_space(struct sock *sk); -+ -+void mptcp_add_meta_ofo_queue(struct sock *meta_sk, struct sk_buff *skb, -+ struct sock *sk); -+void mptcp_ofo_queue(struct sock *meta_sk); -+void mptcp_purge_ofo_queue(struct tcp_sock *meta_tp); -+void mptcp_cleanup_rbuf(struct sock *meta_sk, int copied); -+int mptcp_alloc_mpcb(struct sock *master_sk, __u64 remote_key, u32 window); -+int mptcp_add_sock(struct sock *meta_sk, struct sock *sk, u8 loc_id, u8 rem_id, -+ gfp_t flags); -+void mptcp_del_sock(struct sock *sk); -+void mptcp_update_metasocket(struct sock *sock, struct sock *meta_sk); -+void mptcp_reinject_data(struct sock *orig_sk, int clone_it); -+void mptcp_update_sndbuf(struct mptcp_cb *mpcb); -+struct sk_buff *mptcp_next_segment(struct sock *sk, int *reinject); -+void mptcp_send_fin(struct sock *meta_sk); -+void mptcp_send_active_reset(struct sock *meta_sk, gfp_t priority); -+int mptcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, -+ int push_one, gfp_t gfp); -+void mptcp_parse_options(const uint8_t *ptr, int opsize, -+ struct tcp_options_received *opt_rx, -+ struct mptcp_options_received *mopt, -+ const struct sk_buff *skb); -+void mptcp_syn_options(struct sock *sk, struct tcp_out_options *opts, -+ unsigned *remaining); -+void mptcp_synack_options(struct request_sock *req, -+ struct tcp_out_options *opts, -+ unsigned *remaining); -+void mptcp_established_options(struct sock *sk, struct sk_buff *skb, -+ struct tcp_out_options *opts, unsigned *size); -+void mptcp_options_write(__be32 *ptr, struct tcp_sock *tp, -+ struct tcp_out_options *opts, -+ struct sk_buff *skb); -+void mptcp_close(struct sock *meta_sk, long timeout); -+int mptcp_doit(struct sock *sk); -+int mptcp_create_master_sk(struct sock *meta_sk, __u64 remote_key, u32 window); -+int mptcp_check_req_master(struct sock *sk, struct sock *child, -+ struct request_sock *req, -+ struct request_sock **prev, -+ struct mptcp_options_received *mopt); -+struct sock *mptcp_check_req_child(struct sock *sk, struct sock *child, -+ struct request_sock *req, -+ struct request_sock **prev, -+ struct mptcp_options_received *mopt); -+u32 __mptcp_select_window(struct sock *sk); -+void mptcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, -+ __u32 *window_clamp, int wscale_ok, -+ __u8 *rcv_wscale, __u32 init_rcv_wnd, -+ const struct sock *sk); -+unsigned int mptcp_current_mss(struct sock *meta_sk); -+int mptcp_select_size(const struct sock *meta_sk, bool sg); -+void mptcp_key_sha1(u64 key, u32 *token, u64 *idsn); -+void mptcp_hmac_sha1(u8 *key_1, u8 *key_2, u8 *rand_1, u8 *rand_2, -+ u32 *hash_out); -+void mptcp_clean_rtx_infinite(struct sk_buff *skb, struct sock *sk); -+void mptcp_fin(struct sock *meta_sk); -+void mptcp_retransmit_timer(struct sock *meta_sk); -+int mptcp_write_wakeup(struct sock *meta_sk); -+void mptcp_sub_close_wq(struct work_struct *work); -+void mptcp_sub_close(struct sock *sk, unsigned long delay); -+struct sock *mptcp_select_ack_sock(const struct sock *meta_sk, int copied); -+void mptcp_fallback_meta_sk(struct sock *meta_sk); -+int mptcp_backlog_rcv(struct sock *meta_sk, struct sk_buff *skb); -+struct sock *mptcp_sk_clone(const struct sock *sk, int family, const gfp_t priority); -+void mptcp_ack_handler(unsigned long); -+int mptcp_check_rtt(const struct tcp_sock *tp, int time); -+int mptcp_check_snd_buf(const struct tcp_sock *tp); -+int mptcp_handle_options(struct sock *sk, const struct tcphdr *th, struct sk_buff *skb); -+void __init mptcp_init(void); -+int mptcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len); -+int mptcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, -+ unsigned int mss_now, int reinject); -+int mptso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, -+ unsigned int mss_now, gfp_t gfp, int reinject); -+void mptcp_destroy_sock(struct sock *sk); -+int mptcp_rcv_synsent_state_process(struct sock *sk, struct sock **skptr, -+ struct sk_buff *skb, -+ struct mptcp_options_received *mopt); -+unsigned int mptcp_xmit_size_goal(struct sock *meta_sk, u32 mss_now, -+ int large_allowed); -+int mptcp_time_wait(struct sock *sk, struct tcp_timewait_sock *tw); -+void mptcp_twsk_destructor(struct tcp_timewait_sock *tw); -+void mptcp_update_tw_socks(const struct tcp_sock *tp, int state); -+void mptcp_disconnect(struct sock *sk); -+bool mptcp_should_expand_sndbuf(const struct sock *sk); -+int mptcp_retransmit_skb(struct sock *meta_sk, struct sk_buff *skb); -+void mptcp_tsq_flags(struct sock *sk); -+void mptcp_tsq_sub_deferred(struct sock *meta_sk); -+struct mp_join *mptcp_find_join(struct sk_buff *skb); -+void mptcp_hash_remove_bh(struct tcp_sock *meta_tp); -+void mptcp_hash_remove(struct tcp_sock *meta_tp); -+struct sock *mptcp_hash_find(struct net *net, u32 token); -+int mptcp_lookup_join(struct sk_buff *skb, struct inet_timewait_sock *tw); -+int mptcp_do_join_short(struct sk_buff *skb, struct mptcp_options_received *mopt, -+ struct tcp_options_received *tmp_opt, struct net *net); -+void mptcp_reqsk_destructor(struct request_sock *req); -+void mptcp_reqsk_new_mptcp(struct request_sock *req, -+ const struct tcp_options_received *rx_opt, -+ const struct mptcp_options_received *mopt, -+ const struct sk_buff *skb); -+int mptcp_check_req(struct sk_buff *skb, struct net *net); -+void mptcp_connect_init(struct sock *sk); -+void mptcp_sub_force_close(struct sock *sk); -+int mptcp_sub_len_remove_addr_align(u16 bitfield); -+void mptcp_remove_shortcuts(const struct mptcp_cb *mpcb, -+ const struct sk_buff *skb); -+void mptcp_init_buffer_space(struct sock *sk); -+ -+/* MPTCP-path-manager registration/initialization functions */ -+int mptcp_register_path_manager(struct mptcp_pm_ops *pm); -+void mptcp_unregister_path_manager(struct mptcp_pm_ops *pm); -+void mptcp_init_path_manager(struct mptcp_cb *mpcb); -+void mptcp_cleanup_path_manager(struct mptcp_cb *mpcb); -+void mptcp_fallback_default(struct mptcp_cb *mpcb); -+void mptcp_get_default_path_manager(char *name); -+int mptcp_set_default_path_manager(const char *name); -+extern struct mptcp_pm_ops mptcp_pm_default; -+ -+static inline -+struct mptcp_request_sock *mptcp_rsk(const struct request_sock *req) -+{ -+ return (struct mptcp_request_sock *)req; -+} -+ -+static inline -+struct request_sock *rev_mptcp_rsk(const struct mptcp_request_sock *req) -+{ -+ return (struct request_sock *)req; -+} -+ -+static inline bool mptcp_can_sendpage(struct sock *sk) -+{ -+ struct sock *sk_it; -+ -+ if (tcp_sk(sk)->mpcb->dss_csum) -+ return false; -+ -+ mptcp_for_each_sk(tcp_sk(sk)->mpcb, sk_it) { -+ if (!(sk_it->sk_route_caps & NETIF_F_SG) || -+ !(sk_it->sk_route_caps & NETIF_F_ALL_CSUM)) -+ return false; -+ } -+ -+ return true; -+} -+ -+static inline void mptcp_push_pending_frames(struct sock *meta_sk) -+{ -+ if (mptcp_next_segment(meta_sk, NULL)) { -+ struct tcp_sock *tp = tcp_sk(meta_sk); -+ -+ /* We don't care about the MSS, because it will be set in -+ * mptcp_write_xmit. -+ */ -+ __tcp_push_pending_frames(meta_sk, 0, tp->nonagle); -+ } -+} -+ -+static inline void mptcp_send_reset(struct sock *sk) -+{ -+ tcp_send_active_reset(sk, GFP_ATOMIC); -+ mptcp_sub_force_close(sk); -+} -+ -+static inline int mptcp_is_data_seq(const struct sk_buff *skb) -+{ -+ return TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_SEQ; -+} -+ -+static inline int mptcp_is_data_fin(const struct sk_buff *skb) -+{ -+ return mptcp_is_data_seq(skb) && -+ (TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_FIN); -+} -+ -+/* Is it a data-fin while in infinite mapping mode? -+ * In infinite mode, a subflow-fin is in fact a data-fin. -+ */ -+static inline int mptcp_is_data_fin2(const struct sk_buff *skb, -+ const struct tcp_sock *tp) -+{ -+ return mptcp_is_data_fin(skb) || -+ (tp->mpcb->infinite_mapping_rcv && tcp_hdr(skb)->fin); -+} -+ -+static inline void mptcp_skb_entail_init(const struct tcp_sock *tp, -+ struct sk_buff *skb) -+{ -+ TCP_SKB_CB(skb)->mptcp_flags = MPTCPHDR_SEQ; -+} -+ -+static inline u8 mptcp_get_64_bit(u64 data_seq, struct mptcp_cb *mpcb) -+{ -+ u64 data_seq_high = (u32)(data_seq >> 32); -+ -+ if (mpcb->rcv_high_order[0] == data_seq_high) -+ return 0; -+ else if (mpcb->rcv_high_order[1] == data_seq_high) -+ return MPTCPHDR_SEQ64_INDEX; -+ else -+ return MPTCPHDR_SEQ64_OFO; -+} -+ -+/* Sets the data_seq and returns pointer to the in-skb field of the data_seq. -+ * If the packet has a 64-bit dseq, the pointer points to the last 32 bits. -+ */ -+static inline __u32 *mptcp_skb_set_data_seq(const struct sk_buff *skb, -+ u32 *data_seq, -+ struct mptcp_cb *mpcb) -+{ -+ __u32 *ptr = (__u32 *)(skb_transport_header(skb) + TCP_SKB_CB(skb)->dss_off); -+ -+ if (TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_SEQ64_SET) { -+ u64 data_seq64 = get_unaligned_be64(ptr); -+ -+ if (mpcb) -+ TCP_SKB_CB(skb)->mptcp_flags |= mptcp_get_64_bit(data_seq64, mpcb); -+ -+ *data_seq = (u32)data_seq64 ; -+ ptr++; -+ } else { -+ *data_seq = get_unaligned_be32(ptr); -+ } -+ -+ return ptr; -+} -+ -+static inline struct sock *mptcp_meta_sk(const struct sock *sk) -+{ -+ return tcp_sk(sk)->meta_sk; -+} -+ -+static inline struct tcp_sock *mptcp_meta_tp(const struct tcp_sock *tp) -+{ -+ return tcp_sk(tp->meta_sk); -+} -+ -+static inline int is_meta_tp(const struct tcp_sock *tp) -+{ -+ return tp->mpcb && mptcp_meta_tp(tp) == tp; -+} -+ -+static inline int is_meta_sk(const struct sock *sk) -+{ -+ return sk->sk_type == SOCK_STREAM && sk->sk_protocol == IPPROTO_TCP && -+ tcp_sk(sk)->mpc && mptcp_meta_sk(sk) == sk; -+} -+ -+static inline int is_master_tp(const struct tcp_sock *tp) -+{ -+ return !tp->mpc || (!tp->mptcp->slave_sk && !is_meta_tp(tp)); -+} -+ -+static inline void mptcp_hash_request_remove(struct request_sock *req) -+{ -+ int in_softirq = 0; -+ -+ if (list_empty(&mptcp_rsk(req)->collide_tuple)) -+ return; -+ -+ if (in_softirq()) { -+ spin_lock(&mptcp_reqsk_hlock); -+ in_softirq = 1; -+ } else { -+ spin_lock_bh(&mptcp_reqsk_hlock); -+ } -+ -+ list_del(&mptcp_rsk(req)->collide_tuple); -+ -+ if (in_softirq) -+ spin_unlock(&mptcp_reqsk_hlock); -+ else -+ spin_unlock_bh(&mptcp_reqsk_hlock); -+} -+ -+static inline void mptcp_init_mp_opt(struct mptcp_options_received *mopt) -+{ -+ mopt->saw_mpc = 0; -+ mopt->dss_csum = 0; -+ mopt->drop_me = 0; -+ -+ mopt->is_mp_join = 0; -+ mopt->join_ack = 0; -+ -+ mopt->saw_low_prio = 0; -+ mopt->low_prio = 0; -+ -+ mopt->saw_add_addr = 0; -+ mopt->more_add_addr = 0; -+ -+ mopt->saw_rem_addr = 0; -+ mopt->more_rem_addr = 0; -+ -+ mopt->mp_fail = 0; -+ mopt->mp_fclose = 0; -+} -+ -+static inline void mptcp_reset_mopt(struct tcp_sock *tp) -+{ -+ struct mptcp_options_received *mopt = &tp->mptcp->rx_opt; -+ -+ mopt->saw_low_prio = 0; -+ mopt->saw_add_addr = 0; -+ mopt->more_add_addr = 0; -+ mopt->saw_rem_addr = 0; -+ mopt->more_rem_addr = 0; -+ mopt->join_ack = 0; -+ mopt->mp_fail = 0; -+ mopt->mp_fclose = 0; -+} -+ -+static inline __be32 mptcp_get_highorder_sndbits(const struct sk_buff *skb, -+ const struct mptcp_cb *mpcb) -+{ -+ return htonl(mpcb->snd_high_order[(TCP_SKB_CB(skb)->mptcp_flags & -+ MPTCPHDR_SEQ64_INDEX) ? 1 : 0]); -+} -+ -+static inline u64 mptcp_get_data_seq_64(const struct mptcp_cb *mpcb, int index, -+ u32 data_seq_32) -+{ -+ return ((u64)mpcb->rcv_high_order[index] << 32) | data_seq_32; -+} -+ -+static inline u64 mptcp_get_rcv_nxt_64(const struct tcp_sock *meta_tp) -+{ -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ return mptcp_get_data_seq_64(mpcb, mpcb->rcv_hiseq_index, -+ meta_tp->rcv_nxt); -+} -+ -+static inline void mptcp_check_sndseq_wrap(struct tcp_sock *meta_tp, int inc) -+{ -+ if (unlikely(meta_tp->snd_nxt > meta_tp->snd_nxt + inc)) { -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ mpcb->snd_hiseq_index = mpcb->snd_hiseq_index ? 0 : 1; -+ mpcb->snd_high_order[mpcb->snd_hiseq_index] += 2; -+ } -+} -+ -+static inline void mptcp_check_rcvseq_wrap(struct tcp_sock *meta_tp, -+ u32 old_rcv_nxt) -+{ -+ if (unlikely(old_rcv_nxt > meta_tp->rcv_nxt)) { -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ mpcb->rcv_high_order[mpcb->rcv_hiseq_index] += 2; -+ mpcb->rcv_hiseq_index = mpcb->rcv_hiseq_index ? 0 : 1; -+ } -+} -+ -+static inline int mptcp_sk_can_send(const struct sock *sk) -+{ -+ return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT) && -+ !tcp_sk(sk)->mptcp->pre_established; -+} -+ -+static inline int mptcp_sk_can_recv(const struct sock *sk) -+{ -+ return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCP_FIN_WAIT1 | TCP_FIN_WAIT2); -+} -+ -+static inline int mptcp_sk_can_send_ack(const struct sock *sk) -+{ -+ return !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV | -+ TCPF_CLOSE | TCPF_LISTEN)) && -+ !tcp_sk(sk)->mptcp->pre_established; -+} -+ -+/* Only support GSO if all subflows supports it */ -+static inline bool mptcp_sk_can_gso(const struct sock *meta_sk) -+{ -+ struct sock *sk; -+ -+ if (tcp_sk(meta_sk)->mpcb->dss_csum) -+ return 0; -+ -+ mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { -+ if (!mptcp_sk_can_send(sk)) -+ continue; -+ if (!sk_can_gso(sk)) -+ return false; -+ } -+ return true; -+} -+ -+static inline bool mptcp_can_sg(const struct sock *meta_sk) -+{ -+ struct sock *sk; -+ -+ if (tcp_sk(meta_sk)->mpcb->dss_csum) -+ return 0; -+ -+ mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { -+ if (!mptcp_sk_can_send(sk)) -+ continue; -+ if (!(sk->sk_route_caps & NETIF_F_SG)) -+ return false; -+ } -+ return true; -+} -+ -+static inline void mptcp_set_rto(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct sock *sk_it; -+ struct inet_connection_sock *micsk = inet_csk(mptcp_meta_sk(sk)); -+ __u32 max_rto = 0; -+ -+ /* We are in recovery-phase on the MPTCP-level. Do not update the -+ * RTO, because this would kill exponential backoff. -+ */ -+ if (micsk->icsk_retransmits) -+ return; -+ -+ mptcp_for_each_sk(tp->mpcb, sk_it) { -+ if (mptcp_sk_can_send(sk_it) && -+ inet_csk(sk_it)->icsk_rto > max_rto) -+ max_rto = inet_csk(sk_it)->icsk_rto; -+ } -+ if (max_rto) { -+ micsk->icsk_rto = max_rto << 1; -+ -+ /* A successfull rto-measurement - reset backoff counter */ -+ micsk->icsk_backoff = 0; -+ } -+} -+ -+static inline int mptcp_sysctl_syn_retries(void) -+{ -+ return sysctl_mptcp_syn_retries; -+} -+ -+static inline void mptcp_sub_close_passive(struct sock *sk) -+{ -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = tcp_sk(meta_sk); -+ -+ /* Only close, if the app did a send-shutdown (passive close), and we -+ * received the data-ack of the data-fin. -+ */ -+ if (tp->mpcb->passive_close && meta_tp->snd_una == meta_tp->write_seq) -+ mptcp_sub_close(sk, 0); -+} -+ -+static inline bool mptcp_fallback_infinite(struct sock *sk, int flag) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ /* If data has been acknowleged on the meta-level, fully_established -+ * will have been set before and thus we will not fall back to infinite -+ * mapping. -+ */ -+ if (likely(tp->mptcp->fully_established)) -+ return false; -+ -+ if (!(flag & MPTCP_FLAG_DATA_ACKED)) -+ return false; -+ -+ /* Don't fallback twice ;) */ -+ if (tp->mpcb->infinite_mapping_snd) -+ return false; -+ -+ pr_err("%s %#x will fallback - pi %d, src %pI4 dst %pI4 from %pS\n", -+ __func__, tp->mpcb->mptcp_loc_token, tp->mptcp->path_index, -+ &inet_sk(sk)->inet_saddr, &inet_sk(sk)->inet_daddr, -+ __builtin_return_address(0)); -+ if (!is_master_tp(tp)) -+ return true; -+ -+ tp->mpcb->infinite_mapping_snd = 1; -+ tp->mpcb->infinite_mapping_rcv = 1; -+ tp->mptcp->fully_established = 1; -+ -+ return false; -+} -+ -+/* Find the first free index in the bitfield */ -+static inline int __mptcp_find_free_index(u8 bitfield, int j, u8 base) -+{ -+ int i; -+ mptcp_for_each_bit_unset(bitfield >> base, i) { -+ /* We wrapped at the bitfield - try from 0 on */ -+ if (i + base >= sizeof(bitfield) * 8) { -+ mptcp_for_each_bit_unset(bitfield, i) { -+ if (i >= sizeof(bitfield) * 8) -+ goto exit; -+ -+ if (i != j) -+ return i; -+ } -+ goto exit; -+ } -+ if (i + base >= sizeof(bitfield) * 8) -+ break; -+ -+ if (i + base != j) -+ return i + base; -+ } -+exit: -+ return -1; -+} -+ -+static inline int mptcp_find_free_index(u8 bitfield) -+{ -+ return __mptcp_find_free_index(bitfield, -1, 0); -+} -+ -+/* Find the first index whose bit in the bit-field == 0 */ -+static inline u8 mptcp_set_new_pathindex(struct mptcp_cb *mpcb) -+{ -+ u8 base = mpcb->next_path_index; -+ int i; -+ -+ /* Start at 1, because 0 is reserved for the meta-sk */ -+ mptcp_for_each_bit_unset(mpcb->path_index_bits >> base, i) { -+ if (i + base < 1) -+ continue; -+ if (i + base >= sizeof(mpcb->path_index_bits) * 8) -+ break; -+ i += base; -+ mpcb->path_index_bits |= (1 << i); -+ mpcb->next_path_index = i + 1; -+ return i; -+ } -+ mptcp_for_each_bit_unset(mpcb->path_index_bits, i) { -+ if (i >= sizeof(mpcb->path_index_bits) * 8) -+ break; -+ if (i < 1) -+ continue; -+ mpcb->path_index_bits |= (1 << i); -+ mpcb->next_path_index = i + 1; -+ return i; -+ } -+ -+ return 0; -+} -+ -+static inline int mptcp_v6_is_v4_mapped(struct sock *sk) -+{ -+ return sk->sk_family == AF_INET6 && -+ ipv6_addr_type(&inet6_sk(sk)->saddr) == IPV6_ADDR_MAPPED; -+} -+ -+/* TCP and MPTCP mpc flag-depending functions */ -+u16 mptcp_select_window(struct sock *sk); -+void mptcp_init_buffer_space(struct sock *sk); -+void mptcp_tcp_set_rto(struct sock *sk); -+ -+static inline void set_mpc(struct tcp_sock *tp) -+{ -+ tp->mpc = 1; -+ -+ tp->__select_window = __mptcp_select_window; -+ tp->select_window = mptcp_select_window; -+ tp->select_initial_window = mptcp_select_initial_window; -+ tp->init_buffer_space = mptcp_init_buffer_space; -+ tp->set_rto = mptcp_tcp_set_rto; -+ tp->should_expand_sndbuf = mptcp_should_expand_sndbuf; -+} -+ -+#else /* CONFIG_MPTCP */ -+#define mptcp_debug(fmt, args...) \ -+ do { \ -+ } while (0) -+ -+/* Without MPTCP, we just do one iteration -+ * over the only socket available. This assumes that -+ * the sk/tp arg is the socket in that case. -+ */ -+#define mptcp_for_each_sk(mpcb, sk) -+#define mptcp_for_each_sk_safe(__mpcb, __sk, __temp) -+ -+static inline int mptcp_is_data_fin(const struct sk_buff *skb) -+{ -+ return 0; -+} -+static inline int mptcp_is_data_seq(const struct sk_buff *skb) -+{ -+ return 0; -+} -+static inline struct sock *mptcp_meta_sk(const struct sock *sk) -+{ -+ return NULL; -+} -+static inline struct tcp_sock *mptcp_meta_tp(const struct tcp_sock *tp) -+{ -+ return NULL; -+} -+static inline int is_meta_sk(const struct sock *sk) -+{ -+ return 0; -+} -+static inline int is_master_tp(const struct tcp_sock *tp) -+{ -+ return 0; -+} -+static inline void mptcp_purge_ofo_queue(struct tcp_sock *meta_tp) {} -+static inline void mptcp_cleanup_rbuf(const struct sock *meta_sk, int copied) {} -+static inline void mptcp_del_sock(const struct sock *sk) {} -+static inline void mptcp_reinject_data(struct sock *orig_sk, int clone_it) {} -+static inline void mptcp_update_sndbuf(const struct mptcp_cb *mpcb) {} -+static inline void mptcp_skb_entail_init(const struct tcp_sock *tp, -+ const struct sk_buff *skb) {} -+static inline void mptcp_clean_rtx_infinite(const struct sk_buff *skb, -+ const struct sock *sk) {} -+static inline void mptcp_retransmit_timer(const struct sock *meta_sk) {} -+static inline int mptcp_write_wakeup(struct sock *meta_sk) -+{ -+ return 0; -+} -+static inline void mptcp_sub_close(struct sock *sk, unsigned long delay) {} -+static inline void mptcp_set_rto(const struct sock *sk) {} -+static inline void mptcp_send_fin(const struct sock *meta_sk) {} -+static inline void mptcp_parse_options(const uint8_t *ptr, const int opsize, -+ const struct tcp_options_received *opt_rx, -+ const struct mptcp_options_received *mopt, -+ const struct sk_buff *skb) {} -+static inline void mptcp_syn_options(struct sock *sk, -+ struct tcp_out_options *opts, -+ unsigned *remaining) {} -+static inline void mptcp_synack_options(struct request_sock *req, -+ struct tcp_out_options *opts, -+ unsigned *remaining) {} -+ -+static inline void mptcp_established_options(struct sock *sk, -+ struct sk_buff *skb, -+ struct tcp_out_options *opts, -+ unsigned *size) {} -+static inline void mptcp_options_write(__be32 *ptr, struct tcp_sock *tp, -+ struct tcp_out_options *opts, -+ struct sk_buff *skb) {} -+static inline void mptcp_close(struct sock *meta_sk, long timeout) {} -+static inline int mptcp_doit(struct sock *sk) -+{ -+ return 0; -+} -+static inline int mptcp_check_req_master(const struct sock *sk, -+ const struct sock *child, -+ struct request_sock *req, -+ struct request_sock **prev, -+ const struct mptcp_options_received *mopt) -+{ -+ return 1; -+} -+static inline struct sock *mptcp_check_req_child(struct sock *sk, -+ struct sock *child, -+ struct request_sock *req, -+ struct request_sock **prev, -+ struct mptcp_options_received *mopt) -+{ -+ return NULL; -+} -+static inline unsigned int mptcp_current_mss(struct sock *meta_sk) -+{ -+ return 0; -+} -+static inline int mptcp_select_size(const struct sock *meta_sk, bool sg) -+{ -+ return 0; -+} -+static inline void mptcp_sub_close_passive(struct sock *sk) {} -+static inline bool mptcp_fallback_infinite(const struct sock *sk, int flag) -+{ -+ return false; -+} -+static inline void mptcp_init_mp_opt(const struct mptcp_options_received *mopt) {} -+static inline int mptcp_check_rtt(const struct tcp_sock *tp, int time) -+{ -+ return 0; -+} -+static inline int mptcp_check_snd_buf(const struct tcp_sock *tp) -+{ -+ return 0; -+} -+static inline int mptcp_sysctl_syn_retries(void) -+{ -+ return 0; -+} -+static inline void mptcp_send_reset(const struct sock *sk) {} -+static inline void mptcp_send_active_reset(struct sock *meta_sk, -+ gfp_t priority) {} -+static inline int mptcp_write_xmit(struct sock *sk, unsigned int mss_now, -+ int nonagle, int push_one, gfp_t gfp) -+{ -+ return 0; -+} -+static inline struct sock *mptcp_sk_clone(const struct sock *sk, int family, -+ const gfp_t priority) -+{ -+ return NULL; -+} -+static inline int mptcp_handle_options(struct sock *sk, -+ const struct tcphdr *th, -+ struct sk_buff *skb) -+{ -+ return 0; -+} -+static inline void mptcp_reset_mopt(struct tcp_sock *tp) {} -+static inline void __init mptcp_init(void) {} -+static inline int mptcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) -+{ -+ return 0; -+} -+static inline int mptcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, -+ unsigned int mss_now, int reinject) -+{ -+ return 0; -+} -+static inline int mptso_fragment(struct sock *sk, struct sk_buff *skb, -+ unsigned int len, unsigned int mss_now, -+ gfp_t gfp, int reinject) -+{ -+ return 0; -+} -+static inline bool mptcp_sk_can_gso(const struct sock *sk) -+{ -+ return false; -+} -+static inline bool mptcp_can_sg(const struct sock *meta_sk) -+{ -+ return false; -+} -+static inline unsigned int mptcp_xmit_size_goal(struct sock *meta_sk, -+ u32 mss_now, int large_allowed) -+{ -+ return 0; -+} -+static inline void mptcp_destroy_sock(struct sock *sk) {} -+static inline int mptcp_rcv_synsent_state_process(struct sock *sk, -+ struct sock **skptr, -+ struct sk_buff *skb, -+ struct mptcp_options_received *mopt) -+{ -+ return 0; -+} -+static inline bool mptcp_can_sendpage(struct sock *sk) -+{ -+ return false; -+} -+static inline int mptcp_time_wait(struct sock *sk, struct tcp_timewait_sock *tw) -+{ -+ return 0; -+} -+static inline void mptcp_twsk_destructor(struct tcp_timewait_sock *tw) {} -+static inline void mptcp_update_tw_socks(const struct tcp_sock *tp, int state) {} -+static inline void mptcp_disconnect(struct sock *sk) {} -+static inline void mptcp_tsq_flags(struct sock *sk) {} -+static inline void mptcp_tsq_sub_deferred(struct sock *meta_sk) {} -+static inline void mptcp_hash_remove_bh(struct tcp_sock *meta_tp) {} -+static inline void mptcp_hash_remove(struct tcp_sock *meta_tp) {} -+static inline void mptcp_reqsk_new_mptcp(struct request_sock *req, -+ const struct tcp_options_received *rx_opt, -+ const struct mptcp_options_received *mopt, -+ const struct sk_buff *skb) {} -+static inline void mptcp_remove_shortcuts(const struct mptcp_cb *mpcb, -+ const struct sk_buff *skb) {} -+#endif /* CONFIG_MPTCP */ -+ -+#endif /* _MPTCP_H */ -diff --git a/include/net/mptcp_v4.h b/include/net/mptcp_v4.h -new file mode 100644 -index 0000000..047884c ---- /dev/null -+++ b/include/net/mptcp_v4.h -@@ -0,0 +1,69 @@ -+/* -+ * MPTCP implementation -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer & Author: -+ * Christoph Paasch -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef MPTCP_V4_H_ -+#define MPTCP_V4_H_ -+ -+ -+#include -+#include -+#include -+#include -+#include -+ -+extern struct request_sock_ops mptcp_request_sock_ops; -+ -+#ifdef CONFIG_MPTCP -+ -+int mptcp_v4_do_rcv(struct sock *meta_sk, struct sk_buff *skb); -+int mptcp_v4_rem_raddress(struct mptcp_cb *mpcb, u8 id); -+int mptcp_v4_add_raddress(struct mptcp_cb *mpcb, const struct in_addr *addr, -+ __be16 port, u8 id); -+void mptcp_v4_set_init_addr_bit(struct mptcp_cb *mpcb, __be32 daddr, int index); -+struct sock *mptcp_v4_search_req(const __be16 rport, const __be32 raddr, -+ const __be32 laddr, const struct net *net); -+int mptcp_init4_subsockets(struct sock *meta_sk, const struct mptcp_loc4 *loc, -+ struct mptcp_rem4 *rem); -+int mptcp_pm_v4_init(void); -+void mptcp_pm_v4_undo(void); -+u32 mptcp_v4_get_nonce(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport, -+ u32 seq); -+u64 mptcp_v4_get_key(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport); -+ -+#else -+ -+static inline int mptcp_v4_do_rcv(const struct sock *meta_sk, -+ const struct sk_buff *skb) -+{ -+ return 0; -+} -+ -+#endif /* CONFIG_MPTCP */ -+ -+#endif /* MPTCP_V4_H_ */ -diff --git a/include/net/mptcp_v6.h b/include/net/mptcp_v6.h -new file mode 100644 -index 0000000..c303208 ---- /dev/null -+++ b/include/net/mptcp_v6.h -@@ -0,0 +1,72 @@ -+/* -+ * MPTCP implementation -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer & Author: -+ * Jaakko Korkeaniemi -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MPTCP_V6_H -+#define _MPTCP_V6_H -+ -+#include -+#include -+ -+#include -+ -+extern struct request_sock_ops mptcp6_request_sock_ops; -+extern struct proto mptcpv6_prot; -+ -+#ifdef CONFIG_MPTCP -+ -+int mptcp_v6_do_rcv(struct sock *meta_sk, struct sk_buff *skb); -+int mptcp_v6_rem_raddress(struct mptcp_cb *mpcb, u8 id); -+int mptcp_v6_add_raddress(struct mptcp_cb *mpcb, const struct in6_addr *addr, -+ __be16 port, u8 id); -+void mptcp_v6_set_init_addr_bit(struct mptcp_cb *mpcb, -+ const struct in6_addr *daddr, int index); -+struct sock *mptcp_v6_search_req(const __be16 rport, const struct in6_addr *raddr, -+ const struct in6_addr *laddr, const struct net *net); -+int mptcp_init6_subsockets(struct sock *meta_sk, const struct mptcp_loc6 *loc, -+ struct mptcp_rem6 *rem); -+int mptcp_pm_v6_init(void); -+void mptcp_pm_v6_undo(void); -+struct sock *mptcp_v6v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, -+ struct request_sock *req, -+ struct dst_entry *dst); -+__u32 mptcp_v6_get_nonce(const __be32 *saddr, const __be32 *daddr, -+ __be16 sport, __be16 dport, u32 seq); -+u64 mptcp_v6_get_key(const __be32 *saddr, const __be32 *daddr, -+ __be16 sport, __be16 dport); -+ -+#else /* CONFIG_MPTCP */ -+ -+static inline int mptcp_v6_do_rcv(struct sock *meta_sk, struct sk_buff *skb) -+{ -+ return 0; -+} -+ -+#endif /* CONFIG_MPTCP */ -+ -+#endif /* _MPTCP_V6_H */ -diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h -index 991dcd9..6297c97 100644 ---- a/include/net/net_namespace.h -+++ b/include/net/net_namespace.h -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -90,6 +91,9 @@ struct net { - #if IS_ENABLED(CONFIG_IPV6) - struct netns_ipv6 ipv6; - #endif -+#if IS_ENABLED(CONFIG_MPTCP) -+ struct netns_mptcp mptcp; -+#endif - #if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE) - struct netns_sctp sctp; - #endif -diff --git a/include/net/netns/mptcp.h b/include/net/netns/mptcp.h -new file mode 100644 -index 0000000..bad418b ---- /dev/null -+++ b/include/net/netns/mptcp.h -@@ -0,0 +1,44 @@ -+/* -+ * MPTCP implementation - MPTCP namespace -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer: -+ * Christoph Paasch -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef __NETNS_MPTCP_H__ -+#define __NETNS_MPTCP_H__ -+ -+#include -+ -+enum { -+ MPTCP_PM_FULLMESH = 0, -+ MPTCP_PM_MAX -+}; -+ -+struct netns_mptcp { -+ void *path_managers[MPTCP_PM_MAX]; -+}; -+ -+#endif /* __NETNS_MPTCP_H__ */ -diff --git a/include/net/request_sock.h b/include/net/request_sock.h -index 7f830ff..e79e87a 100644 ---- a/include/net/request_sock.h -+++ b/include/net/request_sock.h -@@ -164,7 +164,7 @@ struct request_sock_queue { - }; - - int reqsk_queue_alloc(struct request_sock_queue *queue, -- unsigned int nr_table_entries); -+ unsigned int nr_table_entries, gfp_t flags); - - void __reqsk_queue_destroy(struct request_sock_queue *queue); - void reqsk_queue_destroy(struct request_sock_queue *queue); -diff --git a/include/net/sock.h b/include/net/sock.h -index b9586a1..09a682e 100644 ---- a/include/net/sock.h -+++ b/include/net/sock.h -@@ -899,6 +899,16 @@ void sk_clear_memalloc(struct sock *sk); - - int sk_wait_data(struct sock *sk, long *timeo); - -+/* START - needed for MPTCP */ -+extern void sock_def_error_report(struct sock *sk); -+extern struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, -+ int family); -+extern void sock_lock_init(struct sock *sk); -+ -+extern struct lock_class_key af_callback_keys[AF_MAX]; -+extern char *const af_family_clock_key_strings[AF_MAX+1]; -+/* END - needed for MPTCP */ -+ - struct request_sock_ops; - struct timewait_sock_ops; - struct inet_hashinfo; -diff --git a/include/net/tcp.h b/include/net/tcp.h -index 743acce..db0cc04 100644 ---- a/include/net/tcp.h -+++ b/include/net/tcp.h -@@ -176,6 +176,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); - #define TCPOPT_SACK 5 /* SACK Block */ - #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ - #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ -+#define TCPOPT_MPTCP 30 - #define TCPOPT_EXP 254 /* Experimental */ - /* Magic number to be after the option value for sharing TCP - * experimental options. See draft-ietf-tcpm-experimental-options-00.txt -@@ -234,6 +235,27 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); - */ - #define TFO_SERVER_ALWAYS 0x1000 - -+/* Flags from tcp_input.c for tcp_ack */ -+#define FLAG_DATA 0x01 /* Incoming frame contained data. */ -+#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ -+#define FLAG_DATA_ACKED 0x04 /* This ACK acknowledged new data. */ -+#define FLAG_RETRANS_DATA_ACKED 0x08 /* "" "" some of which was retransmitted. */ -+#define FLAG_SYN_ACKED 0x10 /* This ACK acknowledged SYN. */ -+#define FLAG_DATA_SACKED 0x20 /* New SACK. */ -+#define FLAG_ECE 0x40 /* ECE in this ACK */ -+#define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ -+#define FLAG_ORIG_SACK_ACKED 0x200 /* Never retransmitted data are (s)acked */ -+#define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ -+#define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ -+#define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */ -+#define FLAG_UPDATE_TS_RECENT 0x4000 /* tcp_replace_ts_recent() */ -+#define MPTCP_FLAG_DATA_ACKED 0x8000 -+ -+#define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED) -+#define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) -+#define FLAG_CA_ALERT (FLAG_DATA_SACKED|FLAG_ECE) -+#define FLAG_FORWARD_PROGRESS (FLAG_ACKED|FLAG_DATA_SACKED) -+ - extern struct inet_timewait_death_row tcp_death_row; - - /* sysctl variables for tcp */ -@@ -349,6 +371,112 @@ extern struct proto tcp_prot; - #define TCP_ADD_STATS_USER(net, field, val) SNMP_ADD_STATS_USER((net)->mib.tcp_statistics, field, val) - #define TCP_ADD_STATS(net, field, val) SNMP_ADD_STATS((net)->mib.tcp_statistics, field, val) - -+/**** START - Exports needed for MPTCP ****/ -+extern const struct inet_connection_sock_af_ops ipv4_specific; -+extern const struct inet_connection_sock_af_ops ipv6_specific; -+extern const struct inet_connection_sock_af_ops ipv6_mapped; -+extern const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops; -+extern const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops; -+ -+struct mptcp_options_received; -+ -+int tcp_close_state(struct sock *sk); -+void tcp_push(struct sock *sk, int flags, int mss_now, int nonagle, int -+ size_goal); -+void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now, -+ const struct sk_buff *skb); -+int tcp_xmit_probe_skb(struct sock *sk, int urgent); -+void tcp_cwnd_validate(struct sock *sk); -+void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb); -+int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, -+ gfp_t gfp_mask); -+unsigned int tcp_mss_split_point(const struct sock *sk, -+ const struct sk_buff *skb, -+ unsigned int mss_now, -+ unsigned int max_segs, -+ int nonagle); -+bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb); -+bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buff *skb, -+ unsigned int cur_mss, int nonagle); -+bool tcp_snd_wnd_test(const struct tcp_sock *tp, const struct sk_buff *skb, -+ unsigned int cur_mss); -+unsigned int tcp_cwnd_test(const struct tcp_sock *tp, const struct sk_buff *skb); -+int tcp_mtu_probe(struct sock *sk); -+int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, -+ unsigned int mss_now); -+void __pskb_trim_head(struct sk_buff *skb, int len); -+void tcp_queue_skb(struct sock *sk, struct sk_buff *skb); -+void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags); -+void tcp_reset(struct sock *sk); -+bool tcp_may_update_window(const struct tcp_sock *tp, const u32 ack, -+ const u32 ack_seq, const u32 nwin); -+bool tcp_urg_mode(const struct tcp_sock *tp); -+void tcp_ack_probe(struct sock *sk); -+void tcp_rearm_rto(struct sock *sk); -+int tcp_write_timeout(struct sock *sk); -+bool retransmits_timed_out(struct sock *sk, unsigned int boundary, -+ unsigned int timeout, bool syn_set); -+void tcp_write_err(struct sock *sk); -+void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int decr); -+void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, -+ unsigned int mss_now); -+ -+int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req); -+void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, -+ struct request_sock *req); -+__u32 tcp_v4_init_sequence(const struct sk_buff *skb); -+int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, -+ struct request_sock *req, -+ u16 queue_mapping); -+void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb); -+struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb); -+struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb); -+void tcp_v4_reqsk_destructor(struct request_sock *req); -+ -+int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req); -+void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, -+ struct request_sock *req); -+__u32 tcp_v6_init_sequence(const struct sk_buff *skb); -+int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, -+ struct flowi6 *fl6, struct request_sock *req, -+ u16 queue_mapping); -+void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb); -+int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); -+int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); -+void tcp_v6_destroy_sock(struct sock *sk); -+void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb); -+void tcp_v6_hash(struct sock *sk); -+struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb); -+struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, -+ struct request_sock *req, -+ struct dst_entry *dst); -+void tcp_v6_reqsk_destructor(struct request_sock *req); -+ -+void sock_valbool_flag(struct sock *sk, int bit, int valbool); -+unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, -+ int large_allowed); -+u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb); -+ -+void skb_clone_fraglist(struct sk_buff *skb); -+void copy_skb_header(struct sk_buff *new, const struct sk_buff *old); -+ -+void inet_twsk_free(struct inet_timewait_sock *tw); -+/* These states need RST on ABORT according to RFC793 */ -+static inline bool tcp_need_reset(int state) -+{ -+ return (1 << state) & -+ (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_FIN_WAIT1 | -+ TCPF_FIN_WAIT2 | TCPF_SYN_RECV); -+} -+ -+bool tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb, -+ int hlen); -+int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, -+ bool *fragstolen); -+bool tcp_try_coalesce(struct sock *sk, struct sk_buff *to, -+ struct sk_buff *from, bool *fragstolen); -+/**** END - Exports needed for MPTCP ****/ -+ - void tcp_tasklet_init(void); - - void tcp_v4_err(struct sk_buff *skb, u32); -@@ -445,6 +573,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int nonblock, int flags, int *addr_len); - void tcp_parse_options(const struct sk_buff *skb, - struct tcp_options_received *opt_rx, -+ struct mptcp_options_received *mopt_rx, - int estab, struct tcp_fastopen_cookie *foc); - const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); - -@@ -557,11 +686,15 @@ void tcp_send_delayed_ack(struct sock *sk); - void tcp_send_loss_probe(struct sock *sk); - bool tcp_schedule_loss_probe(struct sock *sk); - -+u16 tcp_select_window(struct sock *sk); -+ - /* tcp_input.c */ - void tcp_cwnd_application_limited(struct sock *sk); - void tcp_resume_early_retransmit(struct sock *sk); - void tcp_rearm_rto(struct sock *sk); - void tcp_reset(struct sock *sk); -+void tcp_set_rto(struct sock *sk); -+bool tcp_should_expand_sndbuf(const struct sock *sk); - - /* tcp_timer.c */ - void tcp_init_xmit_timers(struct sock *); -@@ -705,14 +838,24 @@ void tcp_send_window_probe(struct sock *sk); - */ - struct tcp_skb_cb { - union { -- struct inet_skb_parm h4; -+ union { -+ struct inet_skb_parm h4; - #if IS_ENABLED(CONFIG_IPV6) -- struct inet6_skb_parm h6; -+ struct inet6_skb_parm h6; - #endif -- } header; /* For incoming frames */ -+ } header; /* For incoming frames */ -+#ifdef CONFIG_MPTCP -+ __u32 path_mask; /* path indices that tried to send this skb */ -+#endif -+ }; - __u32 seq; /* Starting sequence number */ - __u32 end_seq; /* SEQ + FIN + SYN + datalen */ - __u32 when; /* used to compute rtt's */ -+#ifdef CONFIG_MPTCP -+ __u8 mptcp_flags; /* flags for the MPTCP layer */ -+ __u8 dss_off; /* Number of 4-byte words until -+ * seq-number */ -+#endif - __u8 tcp_flags; /* TCP header flags. (tcp[13]) */ - - __u8 sacked; /* State flags for SACK/FACK. */ -@@ -1058,7 +1201,8 @@ u32 tcp_default_init_rwnd(u32 mss); - /* Determine a window scaling and initial window to offer. */ - void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, - __u32 *window_clamp, int wscale_ok, -- __u8 *rcv_wscale, __u32 init_rcv_wnd); -+ __u8 *rcv_wscale, __u32 init_rcv_wnd, -+ const struct sock *sk); - - static inline int tcp_win_from_space(int space) - { -@@ -1070,12 +1214,18 @@ static inline int tcp_win_from_space(int space) - /* Note: caller must be prepared to deal with negative returns */ - static inline int tcp_space(const struct sock *sk) - { -+ if (tcp_sk(sk)->mpc) -+ sk = tcp_sk(sk)->meta_sk; -+ - return tcp_win_from_space(sk->sk_rcvbuf - - atomic_read(&sk->sk_rmem_alloc)); - } - - static inline int tcp_full_space(const struct sock *sk) - { -+ if (tcp_sk(sk)->mpc) -+ sk = tcp_sk(sk)->meta_sk; -+ - return tcp_win_from_space(sk->sk_rcvbuf); - } - -@@ -1090,6 +1240,7 @@ static inline void tcp_openreq_init(struct request_sock *req, - tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; - tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; - tcp_rsk(req)->snt_synack = 0; -+ tcp_rsk(req)->saw_mpc = 0; - req->mss = rx_opt->mss_clamp; - req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; - ireq->tstamp_ok = rx_opt->tstamp_ok; -diff --git a/include/uapi/linux/if.h b/include/uapi/linux/if.h -index d758163..3d81e49 100644 ---- a/include/uapi/linux/if.h -+++ b/include/uapi/linux/if.h -@@ -53,6 +53,9 @@ - - #define IFF_ECHO 0x40000 /* echo sent packets */ - -+#define IFF_NOMULTIPATH 0x80000 /* Disable for MPTCP */ -+#define IFF_MPBACKUP 0x100000 /* Use as backup path for MPTCP */ -+ - #define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ - IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) - -diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h -index 377f1e5..2ffcb03 100644 ---- a/include/uapi/linux/tcp.h -+++ b/include/uapi/linux/tcp.h -@@ -112,6 +112,7 @@ enum { - #define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ - #define TCP_TIMESTAMP 24 - #define TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes in write queue */ -+#define MPTCP_ENABLED 26 - - struct tcp_repair_opt { - __u32 opt_code; -diff --git a/net/Kconfig b/net/Kconfig -index e411046..3e4b278 100644 ---- a/net/Kconfig -+++ b/net/Kconfig -@@ -79,6 +79,7 @@ if INET - source "net/ipv4/Kconfig" - source "net/ipv6/Kconfig" - source "net/netlabel/Kconfig" -+source "net/mptcp/Kconfig" - - endif # if INET - -diff --git a/net/Makefile b/net/Makefile -index cbbbe6d..244bac1 100644 ---- a/net/Makefile -+++ b/net/Makefile -@@ -20,6 +20,7 @@ obj-$(CONFIG_INET) += ipv4/ - obj-$(CONFIG_XFRM) += xfrm/ - obj-$(CONFIG_UNIX) += unix/ - obj-$(CONFIG_NET) += ipv6/ -+obj-$(CONFIG_MPTCP) += mptcp/ - obj-$(CONFIG_PACKET) += packet/ - obj-$(CONFIG_NET_KEY) += key/ - obj-$(CONFIG_BRIDGE) += bridge/ -diff --git a/net/core/dev.c b/net/core/dev.c -index 45fa2f1..3cfdbc0 100644 ---- a/net/core/dev.c -+++ b/net/core/dev.c -@@ -5271,7 +5271,7 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags) - - dev->flags = (flags & (IFF_DEBUG | IFF_NOTRAILERS | IFF_NOARP | - IFF_DYNAMIC | IFF_MULTICAST | IFF_PORTSEL | -- IFF_AUTOMEDIA)) | -+ IFF_AUTOMEDIA | IFF_NOMULTIPATH | IFF_MPBACKUP)) | - (dev->flags & (IFF_UP | IFF_VOLATILE | IFF_PROMISC | - IFF_ALLMULTI)); - -diff --git a/net/core/request_sock.c b/net/core/request_sock.c -index 4425148..e128f08 100644 ---- a/net/core/request_sock.c -+++ b/net/core/request_sock.c -@@ -38,7 +38,8 @@ int sysctl_max_syn_backlog = 256; - EXPORT_SYMBOL(sysctl_max_syn_backlog); - - int reqsk_queue_alloc(struct request_sock_queue *queue, -- unsigned int nr_table_entries) -+ unsigned int nr_table_entries, -+ gfp_t flags) - { - size_t lopt_size = sizeof(struct listen_sock); - struct listen_sock *lopt; -@@ -48,9 +49,11 @@ int reqsk_queue_alloc(struct request_sock_queue *queue, - nr_table_entries = roundup_pow_of_two(nr_table_entries + 1); - lopt_size += nr_table_entries * sizeof(struct request_sock *); - if (lopt_size > PAGE_SIZE) -- lopt = vzalloc(lopt_size); -+ lopt = __vmalloc(lopt_size, -+ flags | __GFP_HIGHMEM | __GFP_ZERO, -+ PAGE_KERNEL); - else -- lopt = kzalloc(lopt_size, GFP_KERNEL); -+ lopt = kzalloc(lopt_size, flags); - if (lopt == NULL) - return -ENOMEM; - -diff --git a/net/core/skbuff.c b/net/core/skbuff.c -index 90b96a1..2564d89 100644 ---- a/net/core/skbuff.c -+++ b/net/core/skbuff.c -@@ -472,7 +472,7 @@ static inline void skb_drop_fraglist(struct sk_buff *skb) - skb_drop_list(&skb_shinfo(skb)->frag_list); - } - --static void skb_clone_fraglist(struct sk_buff *skb) -+void skb_clone_fraglist(struct sk_buff *skb) - { - struct sk_buff *list; - -@@ -894,7 +894,7 @@ static void skb_headers_offset_update(struct sk_buff *skb, int off) - skb->inner_mac_header += off; - } - --static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) -+void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) - { - __copy_skb_header(new, old); - -diff --git a/net/core/sock.c b/net/core/sock.c -index c0fc6bd..7314971 100644 ---- a/net/core/sock.c -+++ b/net/core/sock.c -@@ -231,7 +231,7 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = { - "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG" , - "slock-AF_NFC" , "slock-AF_VSOCK" ,"slock-AF_MAX" - }; --static const char *const af_family_clock_key_strings[AF_MAX+1] = { -+char *const af_family_clock_key_strings[AF_MAX+1] = { - "clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" , - "clock-AF_AX25" , "clock-AF_IPX" , "clock-AF_APPLETALK", - "clock-AF_NETROM", "clock-AF_BRIDGE" , "clock-AF_ATMPVC" , -@@ -252,7 +252,7 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = { - * sk_callback_lock locking rules are per-address-family, - * so split the lock classes by using a per-AF key: - */ --static struct lock_class_key af_callback_keys[AF_MAX]; -+struct lock_class_key af_callback_keys[AF_MAX]; - - /* Take into consideration the size of the struct sk_buff overhead in the - * determination of these values, since that is non-constant across -@@ -602,7 +602,7 @@ out: - return ret; - } - --static inline void sock_valbool_flag(struct sock *sk, int bit, int valbool) -+void sock_valbool_flag(struct sock *sk, int bit, int valbool) - { - if (valbool) - sock_set_flag(sk, bit); -@@ -1204,7 +1204,7 @@ lenout: - * - * (We also register the sk_lock with the lock validator.) - */ --static inline void sock_lock_init(struct sock *sk) -+void sock_lock_init(struct sock *sk) - { - sock_lock_init_class_and_name(sk, - af_family_slock_key_strings[sk->sk_family], -@@ -1252,7 +1252,7 @@ void sk_prot_clear_portaddr_nulls(struct sock *sk, int size) - } - EXPORT_SYMBOL(sk_prot_clear_portaddr_nulls); - --static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, -+struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, - int family) - { - struct sock *sk; -@@ -2184,7 +2184,7 @@ static void sock_def_wakeup(struct sock *sk) - rcu_read_unlock(); - } - --static void sock_def_error_report(struct sock *sk) -+void sock_def_error_report(struct sock *sk) - { - struct socket_wq *wq; - -diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig -index 05c57f0..630434d 100644 ---- a/net/ipv4/Kconfig -+++ b/net/ipv4/Kconfig -@@ -556,6 +556,30 @@ config TCP_CONG_ILLINOIS - For further details see: - http://www.ews.uiuc.edu/~shaoliu/tcpillinois/index.html - -+config TCP_CONG_COUPLED -+ tristate "MPTCP COUPLED CONGESTION CONTROL" -+ depends on MPTCP -+ default n -+ ---help--- -+ MultiPath TCP Coupled Congestion Control -+ To enable it, just put 'coupled' in tcp_congestion_control -+ -+config TCP_CONG_OLIA -+ tristate "MPTCP Opportunistic Linked Increase" -+ depends on MPTCP -+ default n -+ ---help--- -+ MultiPath TCP Opportunistic Linked Increase Congestion Control -+ To enable it, just put 'olia' in tcp_congestion_control -+ -+config TCP_CONG_WVEGAS -+ tristate "MPTCP WVEGAS CONGESTION CONTROL" -+ depends on MPTCP -+ default n -+ ---help--- -+ wVegas congestion control for MPTCP -+ To enable it, just put 'wvegas' in tcp_congestion_control -+ - choice - prompt "Default TCP congestion control" - default DEFAULT_CUBIC -@@ -584,6 +608,15 @@ choice - config DEFAULT_WESTWOOD - bool "Westwood" if TCP_CONG_WESTWOOD=y - -+ config DEFAULT_COUPLED -+ bool "Coupled" if TCP_CONG_COUPLED=y -+ -+ config DEFAULT_OLIA -+ bool "Olia" if TCP_CONG_OLIA=y -+ -+ config DEFAULT_WVEGAS -+ bool "Wvegas" if TCP_CONG_WVEGAS=y -+ - config DEFAULT_RENO - bool "Reno" - -@@ -605,6 +638,8 @@ config DEFAULT_TCP_CONG - default "vegas" if DEFAULT_VEGAS - default "westwood" if DEFAULT_WESTWOOD - default "veno" if DEFAULT_VENO -+ default "coupled" if DEFAULT_COUPLED -+ default "wvegas" if DEFAULT_WVEGAS - default "reno" if DEFAULT_RENO - default "cubic" - -diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c -index 19ab78a..567918a 100644 ---- a/net/ipv4/af_inet.c -+++ b/net/ipv4/af_inet.c -@@ -104,6 +104,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -246,8 +247,7 @@ EXPORT_SYMBOL(inet_listen); - * Create an inet socket. - */ - --static int inet_create(struct net *net, struct socket *sock, int protocol, -- int kern) -+int inet_create(struct net *net, struct socket *sock, int protocol, int kern) - { - struct sock *sk; - struct inet_protosw *answer; -@@ -679,6 +679,23 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags) - lock_sock(sk2); - - sock_rps_record_flow(sk2); -+ -+ if (sk2->sk_protocol == IPPROTO_TCP && tcp_sk(sk2)->mpc) { -+ struct sock *sk_it = sk2; -+ -+ mptcp_for_each_sk(tcp_sk(sk2)->mpcb, sk_it) -+ sock_rps_record_flow(sk_it); -+ -+ if (tcp_sk(sk2)->mpcb->master_sk) { -+ sk_it = tcp_sk(sk2)->mpcb->master_sk; -+ -+ write_lock_bh(&sk_it->sk_callback_lock); -+ sk_it->sk_wq = newsock->wq; -+ sk_it->sk_socket = newsock; -+ write_unlock_bh(&sk_it->sk_callback_lock); -+ } -+ } -+ - WARN_ON(!((1 << sk2->sk_state) & - (TCPF_ESTABLISHED | TCPF_SYN_RECV | - TCPF_CLOSE_WAIT | TCPF_CLOSE))); -@@ -1767,6 +1784,9 @@ static int __init inet_init(void) - - ip_init(); - -+ /* We must initialize MPTCP before TCP. */ -+ mptcp_init(); -+ - tcp_v4_init(); - - /* Setup TCP slab cache for open requests. */ -diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c -index 0d1e2cb..423dfb6 100644 ---- a/net/ipv4/inet_connection_sock.c -+++ b/net/ipv4/inet_connection_sock.c -@@ -23,6 +23,7 @@ - #include - #include - #include -+#include - - #ifdef INET_CSK_DEBUG - const char inet_csk_timer_bug_msg[] = "inet_csk BUG: unknown timer value\n"; -@@ -468,8 +469,8 @@ no_route: - } - EXPORT_SYMBOL_GPL(inet_csk_route_child_sock); - --static inline u32 inet_synq_hash(const __be32 raddr, const __be16 rport, -- const u32 rnd, const u32 synq_hsize) -+u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd, -+ const u32 synq_hsize) - { - return jhash_2words((__force u32)raddr, (__force u32)rport, rnd) & (synq_hsize - 1); - } -@@ -667,7 +668,12 @@ struct sock *inet_csk_clone_lock(const struct sock *sk, - const struct request_sock *req, - const gfp_t priority) - { -- struct sock *newsk = sk_clone_lock(sk, priority); -+ struct sock *newsk; -+ -+ if (sk->sk_protocol == IPPROTO_TCP && tcp_sk(sk)->mpc) -+ newsk = mptcp_sk_clone(sk, req->rsk_ops->family, priority); -+ else -+ newsk = sk_clone_lock(sk, priority); - - if (newsk != NULL) { - struct inet_connection_sock *newicsk = inet_csk(newsk); -@@ -744,7 +750,8 @@ int inet_csk_listen_start(struct sock *sk, const int nr_table_entries) - { - struct inet_sock *inet = inet_sk(sk); - struct inet_connection_sock *icsk = inet_csk(sk); -- int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries); -+ int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries, -+ GFP_KERNEL); - - if (rc != 0) - return rc; -@@ -802,9 +809,14 @@ void inet_csk_listen_stop(struct sock *sk) - - while ((req = acc_req) != NULL) { - struct sock *child = req->sk; -+ bool mutex_taken = false; - - acc_req = req->dl_next; - -+ if (is_meta_sk(child)) { -+ mutex_lock(&tcp_sk(child)->mpcb->mpcb_mutex); -+ mutex_taken = true; -+ } - local_bh_disable(); - bh_lock_sock(child); - WARN_ON(sock_owned_by_user(child)); -@@ -833,6 +845,8 @@ void inet_csk_listen_stop(struct sock *sk) - - bh_unlock_sock(child); - local_bh_enable(); -+ if (mutex_taken) -+ mutex_unlock(&tcp_sk(child)->mpcb->mpcb_mutex); - sock_put(child); - - sk_acceptq_removed(sk); -diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c -index f2ed13c..f08addc 100644 ---- a/net/ipv4/syncookies.c -+++ b/net/ipv4/syncookies.c -@@ -284,7 +284,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, - - /* check for timestamp cookie support */ - memset(&tcp_opt, 0, sizeof(tcp_opt)); -- tcp_parse_options(skb, &tcp_opt, 0, NULL); -+ tcp_parse_options(skb, &tcp_opt, NULL, 0, NULL); - - if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) - goto out; -@@ -354,10 +354,10 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, - /* Try to redo what tcp_v4_send_synack did. */ - req->window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW); - -- tcp_select_initial_window(tcp_full_space(sk), req->mss, -+ tp->select_initial_window(tcp_full_space(sk), req->mss, - &req->rcv_wnd, &req->window_clamp, - ireq->wscale_ok, &rcv_wscale, -- dst_metric(&rt->dst, RTAX_INITRWND)); -+ dst_metric(&rt->dst, RTAX_INITRWND), sk); - - ireq->rcv_wscale = rcv_wscale; - -diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c -index 97c8f56..be72a40 100644 ---- a/net/ipv4/tcp.c -+++ b/net/ipv4/tcp.c -@@ -271,6 +271,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -419,6 +420,9 @@ void tcp_init_sock(struct sock *sk) - sk->sk_sndbuf = sysctl_tcp_wmem[1]; - sk->sk_rcvbuf = sysctl_tcp_rmem[1]; - -+ /* Set function pointers in tcp_sock to tcp functions. */ -+ mptcp_init_tcp_sock(tp); -+ - local_bh_disable(); - sock_update_memcg(sk); - sk_sockets_allocated_inc(sk); -@@ -607,6 +611,8 @@ static inline void skb_entail(struct sock *sk, struct sk_buff *skb) - tcb->seq = tcb->end_seq = tp->write_seq; - tcb->tcp_flags = TCPHDR_ACK; - tcb->sacked = 0; -+ if (tp->mpc) -+ mptcp_skb_entail_init(tp, skb); - skb_header_release(skb); - tcp_add_write_queue_tail(sk, skb); - sk->sk_wmem_queued += skb->truesize; -@@ -640,8 +646,8 @@ static bool tcp_should_autocork(struct sock *sk, struct sk_buff *skb, - atomic_read(&sk->sk_wmem_alloc) > skb->truesize; - } - --static void tcp_push(struct sock *sk, int flags, int mss_now, -- int nonagle, int size_goal) -+void tcp_push(struct sock *sk, int flags, int mss_now, int nonagle, -+ int size_goal) - { - struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb; -@@ -726,6 +732,14 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, - int ret; - - sock_rps_record_flow(sk); -+ -+#ifdef CONFIG_MPTCP -+ if (tcp_sk(sk)->mpc) { -+ struct sock *sk_it; -+ mptcp_for_each_sk(tcp_sk(sk)->mpcb, sk_it) -+ sock_rps_record_flow(sk_it); -+ } -+#endif - /* - * We can't seek on a socket input - */ -@@ -821,8 +835,7 @@ struct sk_buff *sk_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp) - return NULL; - } - --static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, -- int large_allowed) -+unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, int large_allowed) - { - struct tcp_sock *tp = tcp_sk(sk); - u32 xmit_size_goal, old_size_goal; -@@ -872,8 +885,13 @@ static int tcp_send_mss(struct sock *sk, int *size_goal, int flags) - { - int mss_now; - -- mss_now = tcp_current_mss(sk); -- *size_goal = tcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); -+ if (tcp_sk(sk)->mpc) { -+ mss_now = mptcp_current_mss(sk); -+ *size_goal = mptcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); -+ } else { -+ mss_now = tcp_current_mss(sk); -+ *size_goal = tcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); -+ } - - return mss_now; - } -@@ -897,6 +915,26 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset, - goto out_err; - } - -+ if (tp->mpc) { -+ struct sock *sk_it = sk; -+ -+ /* We must check this with socket-lock hold because we iterate -+ * over the subflows. -+ */ -+ if (!mptcp_can_sendpage(sk)) { -+ ssize_t ret; -+ -+ release_sock(sk); -+ ret = sock_no_sendpage(sk->sk_socket, page, offset, -+ size, flags); -+ lock_sock(sk); -+ return ret; -+ } -+ -+ mptcp_for_each_sk(tp->mpcb, sk_it) -+ sock_rps_record_flow(sk_it); -+ } -+ - clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); - - mss_now = tcp_send_mss(sk, &size_goal, flags); -@@ -1001,8 +1039,9 @@ int tcp_sendpage(struct sock *sk, struct page *page, int offset, - { - ssize_t res; - -- if (!(sk->sk_route_caps & NETIF_F_SG) || -- !(sk->sk_route_caps & NETIF_F_ALL_CSUM)) -+ /* If MPTCP is enabled, we check it later after establishment */ -+ if (!tcp_sk(sk)->mpc && (!(sk->sk_route_caps & NETIF_F_SG) || -+ !(sk->sk_route_caps & NETIF_F_ALL_CSUM))) - return sock_no_sendpage(sk->sk_socket, page, offset, size, - flags); - -@@ -1018,6 +1057,9 @@ static inline int select_size(const struct sock *sk, bool sg) - const struct tcp_sock *tp = tcp_sk(sk); - int tmp = tp->mss_cache; - -+ if (tp->mpc) -+ return mptcp_select_size(sk, sg); -+ - if (sg) { - if (sk_can_gso(sk)) { - /* Small frames wont use a full page: -@@ -1105,6 +1147,12 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - goto do_error; - } - -+ if (tp->mpc) { -+ struct sock *sk_it = sk; -+ mptcp_for_each_sk(tp->mpcb, sk_it) -+ sock_rps_record_flow(sk_it); -+ } -+ - if (unlikely(tp->repair)) { - if (tp->repair_queue == TCP_RECV_QUEUE) { - copied = tcp_send_rcvq(sk, msg, size); -@@ -1132,7 +1180,10 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) - goto out_err; - -- sg = !!(sk->sk_route_caps & NETIF_F_SG); -+ if (tp->mpc) -+ sg = mptcp_can_sg(sk); -+ else -+ sg = !!(sk->sk_route_caps & NETIF_F_SG); - - while (--iovlen >= 0) { - size_t seglen = iov->iov_len; -@@ -1183,8 +1234,15 @@ new_segment: - - /* - * Check whether we can use HW checksum. -+ * -+ * If dss-csum is enabled, we do not do hw-csum. -+ * In case of non-mptcp we check the -+ * device-capabilities. -+ * In case of mptcp, hw-csum's will be handled -+ * later in mptcp_write_xmit. - */ -- if (sk->sk_route_caps & NETIF_F_ALL_CSUM) -+ if (((tp->mpc && !tp->mpcb->dss_csum) || !tp->mpc) && -+ (tp->mpc || sk->sk_route_caps & NETIF_F_ALL_CSUM)) - skb->ip_summed = CHECKSUM_PARTIAL; - - skb_entail(sk, skb); -@@ -1385,6 +1443,11 @@ void tcp_cleanup_rbuf(struct sock *sk, int copied) - - struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); - -+ if (is_meta_sk(sk)) { -+ mptcp_cleanup_rbuf(sk, copied); -+ return; -+ } -+ - WARN(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq), - "cleanup rbuf bug: copied %X seq %X rcvnxt %X\n", - tp->copied_seq, TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt); -@@ -1421,7 +1484,7 @@ void tcp_cleanup_rbuf(struct sock *sk, int copied) - - /* Optimize, __tcp_select_window() is not cheap. */ - if (2*rcv_window_now <= tp->window_clamp) { -- __u32 new_window = __tcp_select_window(sk); -+ __u32 new_window = tp->__select_window(sk); - - /* Send ACK now, if this read freed lots of space - * in our buffer. Certainly, new_window is new window. -@@ -1622,6 +1685,14 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - - lock_sock(sk); - -+#ifdef CONFIG_MPTCP -+ if (tp->mpc) { -+ struct sock *sk_it; -+ mptcp_for_each_sk(tp->mpcb, sk_it) -+ sock_rps_record_flow(sk_it); -+ } -+#endif -+ - err = -ENOTCONN; - if (sk->sk_state == TCP_LISTEN) - goto out; -@@ -2069,7 +2140,7 @@ static const unsigned char new_state[16] = { - /* TCP_CLOSING */ TCP_CLOSING, - }; - --static int tcp_close_state(struct sock *sk) -+int tcp_close_state(struct sock *sk) - { - int next = (int)new_state[sk->sk_state]; - int ns = next & TCP_STATE_MASK; -@@ -2098,8 +2169,12 @@ void tcp_shutdown(struct sock *sk, int how) - (TCPF_ESTABLISHED | TCPF_SYN_SENT | - TCPF_SYN_RECV | TCPF_CLOSE_WAIT)) { - /* Clear out any half completed packets. FIN if needed. */ -- if (tcp_close_state(sk)) -- tcp_send_fin(sk); -+ if (tcp_close_state(sk)) { -+ if (!is_meta_sk(sk)) -+ tcp_send_fin(sk); -+ else -+ mptcp_send_fin(sk); -+ } - } - } - EXPORT_SYMBOL(tcp_shutdown); -@@ -2124,6 +2199,11 @@ void tcp_close(struct sock *sk, long timeout) - int data_was_unread = 0; - int state; - -+ if (is_meta_sk(sk)) { -+ mptcp_close(sk, timeout); -+ return; -+ } -+ - lock_sock(sk); - sk->sk_shutdown = SHUTDOWN_MASK; - -@@ -2290,15 +2370,6 @@ out: - } - EXPORT_SYMBOL(tcp_close); - --/* These states need RST on ABORT according to RFC793 */ -- --static inline bool tcp_need_reset(int state) --{ -- return (1 << state) & -- (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_FIN_WAIT1 | -- TCPF_FIN_WAIT2 | TCPF_SYN_RECV); --} -- - int tcp_disconnect(struct sock *sk, int flags) - { - struct inet_sock *inet = inet_sk(sk); -@@ -2339,6 +2410,13 @@ int tcp_disconnect(struct sock *sk, int flags) - if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) - inet_reset_saddr(sk); - -+ if (is_meta_sk(sk)) { -+ mptcp_disconnect(sk); -+ } else { -+ if (tp->inside_tk_table) -+ mptcp_hash_remove_bh(tp); -+ } -+ - sk->sk_shutdown = 0; - sock_reset_flag(sk, SOCK_DONE); - tp->srtt = 0; -@@ -2698,6 +2776,18 @@ static int do_tcp_setsockopt(struct sock *sk, int level, - tp->notsent_lowat = val; - sk->sk_write_space(sk); - break; -+#ifdef CONFIG_MPTCP -+ case MPTCP_ENABLED: -+ if (sk->sk_state == TCP_CLOSE || sk->sk_state == TCP_LISTEN) { -+ if (val) -+ tp->mptcp_enabled = 1; -+ else -+ tp->mptcp_enabled = 0; -+ } else { -+ err = -EPERM; -+ } -+ break; -+#endif - default: - err = -ENOPROTOOPT; - break; -@@ -2917,6 +3007,11 @@ static int do_tcp_getsockopt(struct sock *sk, int level, - case TCP_NOTSENT_LOWAT: - val = tp->notsent_lowat; - break; -+#ifdef CONFIG_MPTCP -+ case MPTCP_ENABLED: -+ val = tp->mptcp_enabled; -+ break; -+#endif - default: - return -ENOPROTOOPT; - } -@@ -3106,8 +3201,11 @@ void tcp_done(struct sock *sk) - if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV) - TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS); - -+ WARN_ON(sk->sk_state == TCP_CLOSE); - tcp_set_state(sk, TCP_CLOSE); -+ - tcp_clear_xmit_timers(sk); -+ - if (req != NULL) - reqsk_fastopen_remove(sk, req, false); - -diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c -index eeaac39..cb06531 100644 ---- a/net/ipv4/tcp_input.c -+++ b/net/ipv4/tcp_input.c -@@ -74,6 +74,9 @@ - #include - #include - #include -+#include -+#include -+#include - - int sysctl_tcp_timestamps __read_mostly = 1; - int sysctl_tcp_window_scaling __read_mostly = 1; -@@ -99,25 +102,6 @@ int sysctl_tcp_thin_dupack __read_mostly; - int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; - int sysctl_tcp_early_retrans __read_mostly = 3; - --#define FLAG_DATA 0x01 /* Incoming frame contained data. */ --#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ --#define FLAG_DATA_ACKED 0x04 /* This ACK acknowledged new data. */ --#define FLAG_RETRANS_DATA_ACKED 0x08 /* "" "" some of which was retransmitted. */ --#define FLAG_SYN_ACKED 0x10 /* This ACK acknowledged SYN. */ --#define FLAG_DATA_SACKED 0x20 /* New SACK. */ --#define FLAG_ECE 0x40 /* ECE in this ACK */ --#define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ --#define FLAG_ORIG_SACK_ACKED 0x200 /* Never retransmitted data are (s)acked */ --#define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ --#define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ --#define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */ --#define FLAG_UPDATE_TS_RECENT 0x4000 /* tcp_replace_ts_recent() */ -- --#define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED) --#define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) --#define FLAG_CA_ALERT (FLAG_DATA_SACKED|FLAG_ECE) --#define FLAG_FORWARD_PROGRESS (FLAG_ACKED|FLAG_DATA_SACKED) -- - #define TCP_REMNANT (TCP_FLAG_FIN|TCP_FLAG_URG|TCP_FLAG_SYN|TCP_FLAG_PSH) - #define TCP_HP_BITS (~(TCP_RESERVED_BITS|TCP_FLAG_PSH)) - -@@ -283,8 +267,12 @@ static void tcp_sndbuf_expand(struct sock *sk) - per_mss = roundup_pow_of_two(per_mss) + - SKB_DATA_ALIGN(sizeof(struct sk_buff)); - -- nr_segs = max_t(u32, TCP_INIT_CWND, tp->snd_cwnd); -- nr_segs = max_t(u32, nr_segs, tp->reordering + 1); -+ if (tp->mpc) { -+ nr_segs = mptcp_check_snd_buf(tp); -+ } else { -+ nr_segs = max_t(u32, TCP_INIT_CWND, tp->snd_cwnd); -+ nr_segs = max_t(u32, nr_segs, tp->reordering + 1); -+ } - - /* Fast Recovery (RFC 5681 3.2) : - * Cubic needs 1.7 factor, rounded to 2 to include -@@ -292,8 +280,16 @@ static void tcp_sndbuf_expand(struct sock *sk) - */ - sndmem = 2 * nr_segs * per_mss; - -- if (sk->sk_sndbuf < sndmem) -+ /* MPTCP: after this sndmem is the new contribution of the -+ * current subflow to the aggregated sndbuf */ -+ if (sk->sk_sndbuf < sndmem) { -+ int old_sndbuf = sk->sk_sndbuf; - sk->sk_sndbuf = min(sndmem, sysctl_tcp_wmem[2]); -+ /* MPTCP: ok, the subflow sndbuf has grown, reflect -+ * this in the aggregate buffer.*/ -+ if (tp->mpc && old_sndbuf != sk->sk_sndbuf) -+ mptcp_update_sndbuf(tp->mpcb); -+ } - } - - /* 2. Tuning advertised window (window_clamp, rcv_ssthresh) -@@ -342,10 +338,12 @@ static int __tcp_grow_window(const struct sock *sk, const struct sk_buff *skb) - static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb) - { - struct tcp_sock *tp = tcp_sk(sk); -+ struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); - - /* Check #1 */ -- if (tp->rcv_ssthresh < tp->window_clamp && -- (int)tp->rcv_ssthresh < tcp_space(sk) && -+ if (meta_tp->rcv_ssthresh < meta_tp->window_clamp && -+ (int)meta_tp->rcv_ssthresh < tcp_space(sk) && - !sk_under_memory_pressure(sk)) { - int incr; - -@@ -353,14 +351,14 @@ static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb) - * will fit to rcvbuf in future. - */ - if (tcp_win_from_space(skb->truesize) <= skb->len) -- incr = 2 * tp->advmss; -+ incr = 2 * meta_tp->advmss; - else -- incr = __tcp_grow_window(sk, skb); -+ incr = __tcp_grow_window(meta_sk, skb); - - if (incr) { - incr = max_t(int, incr, 2 * skb->len); -- tp->rcv_ssthresh = min(tp->rcv_ssthresh + incr, -- tp->window_clamp); -+ meta_tp->rcv_ssthresh = min(meta_tp->rcv_ssthresh + incr, -+ meta_tp->window_clamp); - inet_csk(sk)->icsk_ack.quick |= 1; - } - } -@@ -543,7 +541,10 @@ void tcp_rcv_space_adjust(struct sock *sk) - int copied; - - time = tcp_time_stamp - tp->rcvq_space.time; -- if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0) -+ if (tp->mpc) { -+ if (mptcp_check_rtt(tp, time)) -+ return; -+ } else if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0) - return; - - /* Number of bytes copied to user in last RTT */ -@@ -768,7 +769,7 @@ static void tcp_update_pacing_rate(struct sock *sk) - /* Calculate rto without backoff. This is the second half of Van Jacobson's - * routine referred to above. - */ --static void tcp_set_rto(struct sock *sk) -+void tcp_set_rto(struct sock *sk) - { - const struct tcp_sock *tp = tcp_sk(sk); - /* Old crap is replaced with new one. 8) -@@ -2914,7 +2915,7 @@ static inline bool tcp_ack_update_rtt(struct sock *sk, const int flag, - return false; - - tcp_rtt_estimator(sk, seq_rtt); -- tcp_set_rto(sk); -+ tp->set_rto(sk); - - /* RFC6298: only reset backoff on valid RTT measurement. */ - inet_csk(sk)->icsk_backoff = 0; -@@ -2998,7 +2999,7 @@ void tcp_resume_early_retransmit(struct sock *sk) - } - - /* If we get here, the whole TSO packet has not been acked. */ --static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb) -+u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb) - { - struct tcp_sock *tp = tcp_sk(sk); - u32 packets_acked; -@@ -3092,6 +3093,8 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, - */ - if (!(scb->tcp_flags & TCPHDR_SYN)) { - flag |= FLAG_DATA_ACKED; -+ if (tp->mpc && mptcp_is_data_seq(skb)) -+ flag |= MPTCP_FLAG_DATA_ACKED; - } else { - flag |= FLAG_SYN_ACKED; - tp->retrans_stamp = 0; -@@ -3194,7 +3197,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, - return flag; - } - --static void tcp_ack_probe(struct sock *sk) -+void tcp_ack_probe(struct sock *sk) - { - const struct tcp_sock *tp = tcp_sk(sk); - struct inet_connection_sock *icsk = inet_csk(sk); -@@ -3241,9 +3244,8 @@ static inline bool tcp_may_raise_cwnd(const struct sock *sk, const int flag) - /* Check that window update is acceptable. - * The function assumes that snd_una<=ack<=snd_next. - */ --static inline bool tcp_may_update_window(const struct tcp_sock *tp, -- const u32 ack, const u32 ack_seq, -- const u32 nwin) -+bool tcp_may_update_window(const struct tcp_sock *tp, const u32 ack, -+ const u32 ack_seq, const u32 nwin) - { - return after(ack, tp->snd_una) || - after(ack_seq, tp->snd_wl1) || -@@ -3362,7 +3364,7 @@ static void tcp_process_tlp_ack(struct sock *sk, u32 ack, int flag) - } - - /* This routine deals with incoming acks, but not outgoing ones. */ --static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) -+static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) - { - struct inet_connection_sock *icsk = inet_csk(sk); - struct tcp_sock *tp = tcp_sk(sk); -@@ -3455,6 +3457,16 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) - flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una, sack_rtt); - acked -= tp->packets_out; - -+ if (tp->mpc) { -+ if (mptcp_fallback_infinite(sk, flag)) { -+ pr_err("%s resetting flow\n", __func__); -+ mptcp_send_reset(sk); -+ goto invalid_ack; -+ } -+ -+ mptcp_clean_rtx_infinite(skb, sk); -+ } -+ - /* Advance cwnd if state allows */ - if (tcp_may_raise_cwnd(sk, flag)) - tcp_cong_avoid(sk, ack, acked, prior_in_flight); -@@ -3519,8 +3531,9 @@ old_ack: - * the fast version below fails. - */ - void tcp_parse_options(const struct sk_buff *skb, -- struct tcp_options_received *opt_rx, int estab, -- struct tcp_fastopen_cookie *foc) -+ struct tcp_options_received *opt_rx, -+ struct mptcp_options_received *mopt, -+ int estab, struct tcp_fastopen_cookie *foc) - { - const unsigned char *ptr; - const struct tcphdr *th = tcp_hdr(skb); -@@ -3603,6 +3616,10 @@ void tcp_parse_options(const struct sk_buff *skb, - */ - break; - #endif -+ case TCPOPT_MPTCP: -+ mptcp_parse_options(ptr - 2, opsize, opt_rx, -+ mopt, skb); -+ break; - case TCPOPT_EXP: - /* Fast Open option shares code 254 using a - * 16 bits magic number. It's valid only in -@@ -3664,8 +3681,8 @@ static bool tcp_fast_parse_options(const struct sk_buff *skb, - if (tcp_parse_aligned_timestamp(tp, th)) - return true; - } -- -- tcp_parse_options(skb, &tp->rx_opt, 1, NULL); -+ tcp_parse_options(skb, &tp->rx_opt, tp->mpc ? &tp->mptcp->rx_opt : NULL, -+ 1, NULL); - if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) - tp->rx_opt.rcv_tsecr -= tp->tsoffset; - -@@ -3838,6 +3855,8 @@ static void tcp_fin(struct sock *sk) - dst = __sk_dst_get(sk); - if (!dst || !dst_metric(dst, RTAX_QUICKACK)) - inet_csk(sk)->icsk_ack.pingpong = 1; -+ if (tp->mpc) -+ mptcp_sub_close_passive(sk); - break; - - case TCP_CLOSE_WAIT: -@@ -3859,6 +3878,13 @@ static void tcp_fin(struct sock *sk) - tcp_set_state(sk, TCP_CLOSING); - break; - case TCP_FIN_WAIT2: -+ if (tp->mpc) { -+ /* The socket will get closed by mptcp_data_ready. -+ * We first have to process all data-sequences. -+ */ -+ tp->close_it = 1; -+ break; -+ } - /* Received a FIN -- send ACK and enter TIME_WAIT. */ - tcp_send_ack(sk); - tcp_time_wait(sk, TCP_TIME_WAIT, 0); -@@ -3883,6 +3909,10 @@ static void tcp_fin(struct sock *sk) - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - -+ /* Don't wake up MPTCP-subflows */ -+ if (tp->mpc) -+ return; -+ - /* Do not send POLL_HUP for half duplex close. */ - if (sk->sk_shutdown == SHUTDOWN_MASK || - sk->sk_state == TCP_CLOSE) -@@ -4080,7 +4110,11 @@ static void tcp_ofo_queue(struct sock *sk) - tcp_dsack_extend(sk, TCP_SKB_CB(skb)->seq, dsack); - } - -- if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) { -+ /* In case of MPTCP, the segment may be empty if it's a -+ * non-data DATA_FIN. (see beginning of tcp_data_queue) -+ */ -+ if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt) && -+ !(tp->mpc && TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq)) { - SOCK_DEBUG(sk, "ofo packet was already received\n"); - __skb_unlink(skb, &tp->out_of_order_queue); - __kfree_skb(skb); -@@ -4104,6 +4138,9 @@ static int tcp_prune_queue(struct sock *sk); - static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb, - unsigned int size) - { -+ if (tcp_sk(sk)->mpc) -+ sk = mptcp_meta_sk(sk); -+ - if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || - !sk_rmem_schedule(sk, skb, size)) { - -@@ -4134,15 +4171,16 @@ static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb, - * Better try to coalesce them right now to avoid future collapses. - * Returns true if caller should free @from instead of queueing it - */ --static bool tcp_try_coalesce(struct sock *sk, -- struct sk_buff *to, -- struct sk_buff *from, -- bool *fragstolen) -+bool tcp_try_coalesce(struct sock *sk, struct sk_buff *to, struct sk_buff *from, -+ bool *fragstolen) - { - int delta; - - *fragstolen = false; - -+ if (tcp_sk(sk)->mpc && !is_meta_sk(sk)) -+ return false; -+ - if (tcp_hdr(from)->fin) - return false; - -@@ -4232,7 +4270,9 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) - - /* Do skb overlap to previous one? */ - if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { -- if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { -+ /* MPTCP allows non-data data-fin to be in the ofo-queue */ -+ if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq) && -+ !(tp->mpc && end_seq == seq)) { - /* All the bits are present. Drop. */ - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFOMERGE); - __kfree_skb(skb); -@@ -4270,6 +4310,9 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) - end_seq); - break; - } -+ /* MPTCP allows non-data data-fin to be in the ofo-queue */ -+ if (tp->mpc && TCP_SKB_CB(skb1)->seq == TCP_SKB_CB(skb1)->end_seq) -+ continue; - __skb_unlink(skb1, &tp->out_of_order_queue); - tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq, - TCP_SKB_CB(skb1)->end_seq); -@@ -4287,8 +4330,8 @@ end: - } - } - --static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, -- bool *fragstolen) -+int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, -+ bool *fragstolen) - { - int eaten; - struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue); -@@ -4350,7 +4393,10 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) - int eaten = -1; - bool fragstolen = false; - -- if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) -+ /* If no data is present, but a data_fin is in the options, we still -+ * have to call mptcp_queue_skb later on. */ -+ if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq && -+ !(tp->mpc && mptcp_is_data_fin(skb))) - goto drop; - - skb_dst_drop(skb); -@@ -4396,7 +4442,7 @@ queue_and_out: - eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen); - } - tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; -- if (skb->len) -+ if (skb->len || mptcp_is_data_fin(skb)) - tcp_event_data_recv(sk, skb); - if (th->fin) - tcp_fin(sk); -@@ -4418,7 +4464,11 @@ queue_and_out: - - if (eaten > 0) - kfree_skb_partial(skb, fragstolen); -- if (!sock_flag(sk, SOCK_DEAD)) -+ if (!sock_flag(sk, SOCK_DEAD) || tp->mpc) -+ /* MPTCP: we always have to call data_ready, because -+ * we may be about to receive a data-fin, which still -+ * must get queued. -+ */ - sk->sk_data_ready(sk, 0); - return; - } -@@ -4470,6 +4520,8 @@ static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb, - next = skb_queue_next(list, skb); - - __skb_unlink(skb, list); -+ if (tcp_sk(sk)->mpc) -+ mptcp_remove_shortcuts(tcp_sk(sk)->mpcb, skb); - __kfree_skb(skb); - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRCVCOLLAPSED); - -@@ -4642,6 +4694,18 @@ static bool tcp_prune_ofo_queue(struct sock *sk) - struct tcp_sock *tp = tcp_sk(sk); - bool res = false; - -+ if (is_meta_sk(sk)) { -+ if (!skb_queue_empty(&tp->out_of_order_queue)) { -+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_OFOPRUNED); -+ mptcp_purge_ofo_queue(tp); -+ -+ /* No sack at the mptcp-level */ -+ sk_mem_reclaim(sk); -+ res = true; -+ } -+ return res; -+ } -+ - if (!skb_queue_empty(&tp->out_of_order_queue)) { - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_OFOPRUNED); - __skb_queue_purge(&tp->out_of_order_queue); -@@ -4731,7 +4795,7 @@ void tcp_cwnd_application_limited(struct sock *sk) - tp->snd_cwnd_stamp = tcp_time_stamp; - } - --static bool tcp_should_expand_sndbuf(const struct sock *sk) -+bool tcp_should_expand_sndbuf(const struct sock *sk) - { - const struct tcp_sock *tp = tcp_sk(sk); - -@@ -4766,7 +4830,7 @@ static void tcp_new_space(struct sock *sk) - { - struct tcp_sock *tp = tcp_sk(sk); - -- if (tcp_should_expand_sndbuf(sk)) { -+ if (tp->should_expand_sndbuf(sk)) { - tcp_sndbuf_expand(sk); - tp->snd_cwnd_stamp = tcp_time_stamp; - } -@@ -4778,8 +4842,9 @@ static void tcp_check_space(struct sock *sk) - { - if (sock_flag(sk, SOCK_QUEUE_SHRUNK)) { - sock_reset_flag(sk, SOCK_QUEUE_SHRUNK); -- if (sk->sk_socket && -- test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) -+ if (tcp_sk(sk)->mpc || -+ (sk->sk_socket && -+ test_bit(SOCK_NOSPACE, &sk->sk_socket->flags))) - tcp_new_space(sk); - } - } -@@ -4802,7 +4867,7 @@ static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible) - /* ... and right edge of window advances far enough. - * (tcp_recvmsg() will send ACK otherwise). Or... - */ -- __tcp_select_window(sk) >= tp->rcv_wnd) || -+ tp->__select_window(sk) >= tp->rcv_wnd) || - /* We ACK each frame or... */ - tcp_in_quickack_mode(sk) || - /* We have out of order data. */ -@@ -4904,6 +4969,10 @@ static void tcp_urg(struct sock *sk, struct sk_buff *skb, const struct tcphdr *t - { - struct tcp_sock *tp = tcp_sk(sk); - -+ /* MPTCP urgent data is not yet supported */ -+ if (tp->mpc) -+ return; -+ - /* Check if we get a new urgent pointer - normally not. */ - if (th->urg) - tcp_check_urg(sk, th); -@@ -4971,8 +5040,7 @@ static inline bool tcp_checksum_complete_user(struct sock *sk, - } - - #ifdef CONFIG_NET_DMA --static bool tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb, -- int hlen) -+bool tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb, int hlen) - { - struct tcp_sock *tp = tcp_sk(sk); - int chunk = skb->len - hlen; -@@ -5081,9 +5149,15 @@ syn_challenge: - goto discard; - } - -+ /* If valid: post process the received MPTCP options. */ -+ if (tp->mpc && mptcp_handle_options(sk, th, skb)) -+ goto discard; -+ - return true; - - discard: -+ if (tp->mpc) -+ mptcp_reset_mopt(tp); - __kfree_skb(skb); - return false; - } -@@ -5135,6 +5209,10 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, - - tp->rx_opt.saw_tstamp = 0; - -+ /* MPTCP: force slowpath. */ -+ if (tp->mpc) -+ goto slow_path; -+ - /* pred_flags is 0xS?10 << 16 + snd_wnd - * if header_prediction is to be made - * 'S' will always be tp->tcp_header_len >> 2 -@@ -5349,7 +5427,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) - */ - tp->lsndtime = tcp_time_stamp; - -- tcp_init_buffer_space(sk); -+ tp->init_buffer_space(sk); - - if (sock_flag(sk, SOCK_KEEPOPEN)) - inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tp)); -@@ -5379,7 +5457,7 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, - /* Get original SYNACK MSS value if user MSS sets mss_clamp */ - tcp_clear_options(&opt); - opt.user_mss = opt.mss_clamp = 0; -- tcp_parse_options(synack, &opt, 0, NULL); -+ tcp_parse_options(synack, &opt, NULL, 0, NULL); - mss = opt.mss_clamp; - } - -@@ -5414,8 +5492,11 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, - struct tcp_sock *tp = tcp_sk(sk); - struct tcp_fastopen_cookie foc = { .len = -1 }; - int saved_clamp = tp->rx_opt.mss_clamp; -+ struct mptcp_options_received mopt; -+ mptcp_init_mp_opt(&mopt); - -- tcp_parse_options(skb, &tp->rx_opt, 0, &foc); -+ tcp_parse_options(skb, &tp->rx_opt, -+ tp->mpc ? &tp->mptcp->rx_opt : &mopt, 0, &foc); - if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) - tp->rx_opt.rcv_tsecr -= tp->tsoffset; - -@@ -5462,6 +5543,21 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, - if (!th->syn) - goto discard_and_undo; - -+ if (tp->request_mptcp || tp->mpc) { -+ int ret; -+ ret = mptcp_rcv_synsent_state_process(sk, &sk, -+ skb, &mopt); -+ -+ /* May have changed if we support MPTCP */ -+ tp = tcp_sk(sk); -+ icsk = inet_csk(sk); -+ -+ if (ret == 1) -+ goto reset_and_undo; -+ if (ret == 2) -+ goto discard; -+ } -+ - /* rfc793: - * "If the SYN bit is on ... - * are acceptable then ... -@@ -5474,6 +5570,15 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, - tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); - tcp_ack(sk, skb, FLAG_SLOWPATH); - -+ if (tp->mpc && !is_master_tp(tp)) { -+ /* Timer for repeating the ACK until an answer -+ * arrives. Used only when establishing an additional -+ * subflow inside of an MPTCP connection. -+ */ -+ sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, -+ jiffies + icsk->icsk_rto); -+ } -+ - /* Ok.. it's good. Set up sequence numbers and - * move to established. - */ -@@ -5500,6 +5605,11 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, - tp->tcp_header_len = sizeof(struct tcphdr); - } - -+ if (tp->mpc) { -+ tp->tcp_header_len += MPTCP_SUB_LEN_DSM_ALIGN; -+ tp->advmss -= MPTCP_SUB_LEN_DSM_ALIGN; -+ } -+ - if (tcp_is_sack(tp) && sysctl_tcp_fack) - tcp_enable_fack(tp); - -@@ -5520,7 +5630,9 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, - tcp_rcv_fastopen_synack(sk, skb, &foc)) - return -1; - -- if (sk->sk_write_pending || -+ /* With MPTCP we cannot send data on the third ack due to the -+ * lack of option-space */ -+ if ((sk->sk_write_pending && !tp->mpc) || - icsk->icsk_accept_queue.rskq_defer_accept || - icsk->icsk_ack.pingpong) { - /* Save one ACK. Data will be ready after -@@ -5562,6 +5674,7 @@ discard: - tcp_paws_reject(&tp->rx_opt, 0)) - goto discard_and_undo; - -+ /* TODO - check this here for MPTCP */ - if (th->syn) { - /* We see SYN without ACK. It is attempt of - * simultaneous connect with crossed SYNs. -@@ -5578,6 +5691,11 @@ discard: - tp->tcp_header_len = sizeof(struct tcphdr); - } - -+ if (tp->mpc) { -+ tp->tcp_header_len += MPTCP_SUB_LEN_DSM_ALIGN; -+ tp->advmss -= MPTCP_SUB_LEN_DSM_ALIGN; -+ } -+ - tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; - tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1; - -@@ -5636,6 +5754,7 @@ reset_and_undo: - - int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - const struct tcphdr *th, unsigned int len) -+ __releases(&sk->sk_lock.slock) - { - struct tcp_sock *tp = tcp_sk(sk); - struct inet_connection_sock *icsk = inet_csk(sk); -@@ -5687,6 +5806,10 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - - case TCP_SYN_SENT: - queued = tcp_rcv_synsent_state_process(sk, skb, th, len); -+ if (is_meta_sk(sk)) { -+ sk = tcp_sk(sk)->mpcb->master_sk; -+ tp = tcp_sk(sk); -+ } - if (queued >= 0) - return queued; - -@@ -5694,6 +5817,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - tcp_urg(sk, skb, th); - __kfree_skb(skb); - tcp_data_snd_check(sk); -+ if (tp->mpc && is_master_tp(tp)) -+ bh_unlock_sock(sk); - return 0; - } - -@@ -5736,7 +5861,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - - tcp_mtup_init(sk); - tp->copied_seq = tp->rcv_nxt; -- tcp_init_buffer_space(sk); -+ tp->init_buffer_space(sk); - } - smp_mb(); - tcp_set_state(sk, TCP_ESTABLISHED); -@@ -5756,6 +5881,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - - if (tp->rx_opt.tstamp_ok) - tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; -+ if (tp->mpc) -+ tp->advmss -= MPTCP_SUB_LEN_DSM_ALIGN; - - if (req) { - /* Re-arm the timer because data may have been sent out. -@@ -5777,6 +5904,12 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - - tcp_initialize_rcv_mss(sk); - tcp_fast_path_on(tp); -+ /* Send an ACK when establishing a new -+ * MPTCP subflow, i.e. using an MP_JOIN -+ * subtype. -+ */ -+ if (tp->mpc && !is_master_tp(tp)) -+ tcp_send_ack(sk); - break; - - case TCP_FIN_WAIT1: { -@@ -5828,7 +5961,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - tmo = tcp_fin_time(sk); - if (tmo > TCP_TIMEWAIT_LEN) { - inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN); -- } else if (th->fin || sock_owned_by_user(sk)) { -+ } else if (th->fin || mptcp_is_data_fin(skb) || -+ sock_owned_by_user(sk)) { - /* Bad case. We could lose such FIN otherwise. - * It is not a big problem, but it looks confusing - * and not so rare event. We still can lose it now, -@@ -5857,6 +5991,9 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - goto discard; - } - break; -+ case TCP_CLOSE: -+ if (tp->mp_killed) -+ goto discard; - } - - /* step 6: check the URG bit */ -@@ -5877,7 +6014,11 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - */ - if (sk->sk_shutdown & RCV_SHUTDOWN) { - if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && -- after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) { -+ after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt) && -+ !tp->mpc) { -+ /* In case of mptcp, the reset is handled by -+ * mptcp_rcv_state_process -+ */ - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA); - tcp_reset(sk); - return 1; -diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c -index 1e4eac7..5891fa6 100644 ---- a/net/ipv4/tcp_ipv4.c -+++ b/net/ipv4/tcp_ipv4.c -@@ -67,6 +67,8 @@ - #include - #include - #include -+#include -+#include - #include - #include - #include -@@ -99,7 +101,7 @@ static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, - struct inet_hashinfo tcp_hashinfo; - EXPORT_SYMBOL(tcp_hashinfo); - --static inline __u32 tcp_v4_init_sequence(const struct sk_buff *skb) -+__u32 tcp_v4_init_sequence(const struct sk_buff *skb) - { - return secure_tcp_sequence_number(ip_hdr(skb)->daddr, - ip_hdr(skb)->saddr, -@@ -334,7 +336,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) - struct inet_sock *inet; - const int type = icmp_hdr(icmp_skb)->type; - const int code = icmp_hdr(icmp_skb)->code; -- struct sock *sk; -+ struct sock *sk, *meta_sk; - struct sk_buff *skb; - struct request_sock *req; - __u32 seq; -@@ -358,13 +360,19 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) - return; - } - -- bh_lock_sock(sk); -+ tp = tcp_sk(sk); -+ if (tp->mpc) -+ meta_sk = mptcp_meta_sk(sk); -+ else -+ meta_sk = sk; -+ -+ bh_lock_sock(meta_sk); - /* If too many ICMPs get dropped on busy - * servers this needs to be solved differently. - * We do take care of PMTU discovery (RFC1191) special case : - * we can receive locally generated ICMP messages while socket is held. - */ -- if (sock_owned_by_user(sk)) { -+ if (sock_owned_by_user(meta_sk)) { - if (!(type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)) - NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS); - } -@@ -377,7 +385,6 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) - } - - icsk = inet_csk(sk); -- tp = tcp_sk(sk); - req = tp->fastopen_rsk; - seq = ntohl(th->seq); - if (sk->sk_state != TCP_LISTEN && -@@ -411,11 +418,13 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) - goto out; - - tp->mtu_info = info; -- if (!sock_owned_by_user(sk)) { -+ if (!sock_owned_by_user(meta_sk)) { - tcp_v4_mtu_reduced(sk); - } else { - if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags)) - sock_hold(sk); -+ if (tp->mpc) -+ mptcp_tsq_flags(sk); - } - goto out; - } -@@ -431,7 +440,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) - - /* XXX (TFO) - revisit the following logic for TFO */ - -- if (sock_owned_by_user(sk)) -+ if (sock_owned_by_user(meta_sk)) - break; - - icsk->icsk_backoff--; -@@ -473,7 +482,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) - switch (sk->sk_state) { - struct request_sock *req, **prev; - case TCP_LISTEN: -- if (sock_owned_by_user(sk)) -+ if (sock_owned_by_user(meta_sk)) - goto out; - - req = inet_csk_search_req(sk, &prev, th->dest, -@@ -506,7 +515,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) - It can f.e. if SYNs crossed, - or Fast Open. - */ -- if (!sock_owned_by_user(sk)) { -+ if (!sock_owned_by_user(meta_sk)) { - sk->sk_err = err; - - sk->sk_error_report(sk); -@@ -535,7 +544,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) - */ - - inet = inet_sk(sk); -- if (!sock_owned_by_user(sk) && inet->recverr) { -+ if (!sock_owned_by_user(meta_sk) && inet->recverr) { - sk->sk_err = err; - sk->sk_error_report(sk); - } else { /* Only an error on timeout */ -@@ -543,7 +552,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) - } - - out: -- bh_unlock_sock(sk); -+ bh_unlock_sock(meta_sk); - sock_put(sk); - } - -@@ -585,7 +594,7 @@ EXPORT_SYMBOL(tcp_v4_send_check); - * Exception: precedence violation. We do not implement it in any case. - */ - --static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) -+void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) - { - const struct tcphdr *th = tcp_hdr(skb); - struct { -@@ -709,10 +718,10 @@ release_sk1: - outside socket context is ugly, certainly. What can I do? - */ - --static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, -+static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 data_ack, - u32 win, u32 tsval, u32 tsecr, int oif, - struct tcp_md5sig_key *key, -- int reply_flags, u8 tos) -+ int reply_flags, u8 tos, int mptcp) - { - const struct tcphdr *th = tcp_hdr(skb); - struct { -@@ -721,6 +730,10 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, - #ifdef CONFIG_TCP_MD5SIG - + (TCPOLEN_MD5SIG_ALIGNED >> 2) - #endif -+#ifdef CONFIG_MPTCP -+ + ((MPTCP_SUB_LEN_DSS >> 2) + -+ (MPTCP_SUB_LEN_ACK >> 2)) -+#endif - ]; - } rep; - struct ip_reply_arg arg; -@@ -765,6 +778,21 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, - ip_hdr(skb)->daddr, &rep.th); - } - #endif -+#ifdef CONFIG_MPTCP -+ if (mptcp) { -+ int offset = (tsecr) ? 3 : 0; -+ /* Construction of 32-bit data_ack */ -+ rep.opt[offset++] = htonl((TCPOPT_MPTCP << 24) | -+ ((MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK) << 16) | -+ (0x20 << 8) | -+ (0x01)); -+ rep.opt[offset] = htonl(data_ack); -+ -+ arg.iov[0].iov_len += MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK; -+ rep.th.doff = arg.iov[0].iov_len / 4; -+ } -+#endif /* CONFIG_MPTCP */ -+ - arg.flags = reply_flags; - arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, - ip_hdr(skb)->saddr, /* XXX */ -@@ -783,36 +811,44 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb) - { - struct inet_timewait_sock *tw = inet_twsk(sk); - struct tcp_timewait_sock *tcptw = tcp_twsk(sk); -+ u32 data_ack = 0; -+ int mptcp = 0; -+ -+ if (tcptw->mptcp_tw && tcptw->mptcp_tw->meta_tw) { -+ data_ack = (u32)tcptw->mptcp_tw->rcv_nxt; -+ mptcp = 1; -+ } - - tcp_v4_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, -+ data_ack, - tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, - tcp_time_stamp + tcptw->tw_ts_offset, - tcptw->tw_ts_recent, - tw->tw_bound_dev_if, - tcp_twsk_md5_key(tcptw), - tw->tw_transparent ? IP_REPLY_ARG_NOSRCCHECK : 0, -- tw->tw_tos -+ tw->tw_tos, mptcp - ); - - inet_twsk_put(tw); - } - --static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, -- struct request_sock *req) -+void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, -+ struct request_sock *req) - { - /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV - * sk->sk_state == TCP_SYN_RECV -> for Fast Open. - */ - tcp_v4_send_ack(skb, (sk->sk_state == TCP_LISTEN) ? - tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt, -- tcp_rsk(req)->rcv_nxt, req->rcv_wnd, -+ tcp_rsk(req)->rcv_nxt, 0, req->rcv_wnd, - tcp_time_stamp, - req->ts_recent, - 0, - tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr, - AF_INET), - inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, -- ip_hdr(skb)->tos); -+ ip_hdr(skb)->tos, 0); - } - - /* -@@ -820,9 +856,9 @@ static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, - * This still operates on a request_sock only, not on a big - * socket. - */ --static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, -- struct request_sock *req, -- u16 queue_mapping) -+int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, -+ struct request_sock *req, -+ u16 queue_mapping) - { - const struct inet_request_sock *ireq = inet_rsk(req); - struct flowi4 fl4; -@@ -850,7 +886,7 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, - return err; - } - --static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) -+int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) - { - int res = tcp_v4_send_synack(sk, NULL, req, 0); - -@@ -862,7 +898,7 @@ static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) - /* - * IPv4 request_sock destructor. - */ --static void tcp_v4_reqsk_destructor(struct request_sock *req) -+void tcp_v4_reqsk_destructor(struct request_sock *req) - { - kfree(inet_rsk(req)->opt); - } -@@ -902,7 +938,7 @@ EXPORT_SYMBOL(tcp_syn_flood_action); - /* - * Save and compile IPv4 options into the request_sock if needed. - */ --static struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb) -+struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb) - { - const struct ip_options *opt = &(IPCB(skb)->opt); - struct ip_options_rcu *dopt = NULL; -@@ -1254,7 +1290,7 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = { - }; - - #ifdef CONFIG_TCP_MD5SIG --static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { -+const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { - .md5_lookup = tcp_v4_reqsk_md5_lookup, - .calc_md5_hash = tcp_v4_md5_hash_skb, - }; -@@ -1412,7 +1448,7 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk, - tcp_init_congestion_control(child); - tcp_mtup_init(child); - tcp_init_metrics(child); -- tcp_init_buffer_space(child); -+ tp->init_buffer_space(child); - - /* Queue the data carried in the SYN packet. We need to first - * bump skb's refcnt because the caller will attempt to free it. -@@ -1444,6 +1480,7 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk, - int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) - { - struct tcp_options_received tmp_opt; -+ struct mptcp_options_received mopt; - struct request_sock *req; - struct inet_request_sock *ireq; - struct tcp_sock *tp = tcp_sk(sk); -@@ -1458,6 +1495,22 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) - struct sk_buff *skb_synack; - int do_fastopen; - -+ tcp_clear_options(&tmp_opt); -+ tmp_opt.mss_clamp = TCP_MSS_DEFAULT; -+ tmp_opt.user_mss = tp->rx_opt.user_mss; -+ mptcp_init_mp_opt(&mopt); -+ tcp_parse_options(skb, &tmp_opt, &mopt, 0, want_cookie ? NULL : &foc); -+ -+#ifdef CONFIG_MPTCP -+ /* MPTCP structures not initialized, so clear MPTCP fields */ -+ if (mptcp_init_failed) -+ mptcp_init_mp_opt(&mopt); -+ -+ if (mopt.is_mp_join) -+ return mptcp_do_join_short(skb, &mopt, &tmp_opt, sock_net(sk)); -+ if (mopt.drop_me) -+ goto drop; -+#endif - /* Never answer to SYNs send to broadcast or multicast */ - if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) - goto drop; -@@ -1483,7 +1536,22 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) - goto drop; - } - -- req = inet_reqsk_alloc(&tcp_request_sock_ops); -+#ifdef CONFIG_MPTCP -+ if (sysctl_mptcp_enabled == MPTCP_APP && !tp->mptcp_enabled) -+ mopt.saw_mpc = 0; -+ if (mopt.saw_mpc && !want_cookie) { -+ req = inet_reqsk_alloc(&mptcp_request_sock_ops); -+ -+ if (!req) -+ goto drop; -+ -+ mptcp_rsk(req)->mpcb = NULL; -+ mptcp_rsk(req)->dss_csum = mopt.dss_csum; -+ mptcp_rsk(req)->collide_tk.pprev = NULL; -+ } else -+#endif -+ req = inet_reqsk_alloc(&tcp_request_sock_ops); -+ - if (!req) - goto drop; - -@@ -1491,17 +1559,15 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) - tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops; - #endif - -- tcp_clear_options(&tmp_opt); -- tmp_opt.mss_clamp = TCP_MSS_DEFAULT; -- tmp_opt.user_mss = tp->rx_opt.user_mss; -- tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); -- - if (want_cookie && !tmp_opt.saw_tstamp) - tcp_clear_options(&tmp_opt); - - tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; - tcp_openreq_init(req, &tmp_opt, skb); - -+ if (mopt.saw_mpc && !want_cookie) -+ mptcp_reqsk_new_mptcp(req, &tmp_opt, &mopt, skb); -+ - ireq = inet_rsk(req); - ireq->ir_loc_addr = daddr; - ireq->ir_rmt_addr = saddr; -@@ -1713,7 +1779,7 @@ put_and_exit: - } - EXPORT_SYMBOL(tcp_v4_syn_recv_sock); - --static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) -+struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) - { - struct tcphdr *th = tcp_hdr(skb); - const struct iphdr *iph = ip_hdr(skb); -@@ -1730,8 +1796,15 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) - - if (nsk) { - if (nsk->sk_state != TCP_TIME_WAIT) { -+ /* Don't lock again the meta-sk. It has been locked -+ * before mptcp_v4_do_rcv. -+ */ -+ if (tcp_sk(nsk)->mpc && !is_meta_sk(sk)) -+ bh_lock_sock(mptcp_meta_sk(nsk)); - bh_lock_sock(nsk); -+ - return nsk; -+ - } - inet_twsk_put(inet_twsk(nsk)); - return NULL; -@@ -1788,6 +1861,9 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) - goto discard; - #endif - -+ if (is_meta_sk(sk)) -+ return mptcp_v4_do_rcv(sk, skb); -+ - if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ - struct dst_entry *dst = sk->sk_rx_dst; - -@@ -1919,7 +1995,7 @@ bool tcp_prequeue(struct sock *sk, struct sk_buff *skb) - } else if (skb_queue_len(&tp->ucopy.prequeue) == 1) { - wake_up_interruptible_sync_poll(sk_sleep(sk), - POLLIN | POLLRDNORM | POLLRDBAND); -- if (!inet_csk_ack_scheduled(sk)) -+ if (!inet_csk_ack_scheduled(sk) && !tp->mpc) - inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, - (3 * tcp_rto_min(sk)) / 4, - TCP_RTO_MAX); -@@ -1936,7 +2012,7 @@ int tcp_v4_rcv(struct sk_buff *skb) - { - const struct iphdr *iph; - const struct tcphdr *th; -- struct sock *sk; -+ struct sock *sk, *meta_sk = NULL; - int ret; - struct net *net = dev_net(skb->dev); - -@@ -1969,18 +2045,42 @@ int tcp_v4_rcv(struct sk_buff *skb) - TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + - skb->len - th->doff * 4); - TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); -+#ifdef CONFIG_MPTCP -+ TCP_SKB_CB(skb)->mptcp_flags = 0; -+ TCP_SKB_CB(skb)->dss_off = 0; -+#endif - TCP_SKB_CB(skb)->when = 0; - TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph); - TCP_SKB_CB(skb)->sacked = 0; - - sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest); -- if (!sk) -- goto no_tcp_socket; - - process: -- if (sk->sk_state == TCP_TIME_WAIT) -+ if (sk && sk->sk_state == TCP_TIME_WAIT) - goto do_time_wait; - -+#ifdef CONFIG_MPTCP -+ if (!sk && th->syn && !th->ack) { -+ int ret = mptcp_lookup_join(skb, NULL); -+ -+ if (ret < 0) { -+ tcp_v4_send_reset(NULL, skb); -+ goto discard_it; -+ } else if (ret > 0) { -+ return 0; -+ } -+ } -+ -+ /* Is there a pending request sock for this segment ? */ -+ if ((!sk || sk->sk_state == TCP_LISTEN) && mptcp_check_req(skb, net)) { -+ if (sk) -+ sock_put(sk); -+ return 0; -+ } -+#endif -+ if (!sk) -+ goto no_tcp_socket; -+ - if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) { - NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP); - goto discard_and_relse; -@@ -1996,11 +2096,21 @@ process: - sk_mark_napi_id(sk, skb); - skb->dev = NULL; - -- bh_lock_sock_nested(sk); -+ if (tcp_sk(sk)->mpc) { -+ meta_sk = mptcp_meta_sk(sk); -+ -+ bh_lock_sock_nested(meta_sk); -+ if (sock_owned_by_user(meta_sk)) -+ skb->sk = sk; -+ } else { -+ meta_sk = sk; -+ bh_lock_sock_nested(sk); -+ } -+ - ret = 0; -- if (!sock_owned_by_user(sk)) { -+ if (!sock_owned_by_user(meta_sk)) { - #ifdef CONFIG_NET_DMA -- struct tcp_sock *tp = tcp_sk(sk); -+ struct tcp_sock *tp = tcp_sk(meta_sk); - if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan = net_dma_find_channel(); - if (tp->ucopy.dma_chan) -@@ -2008,16 +2118,16 @@ process: - else - #endif - { -- if (!tcp_prequeue(sk, skb)) -+ if (!tcp_prequeue(meta_sk, skb)) - ret = tcp_v4_do_rcv(sk, skb); - } -- } else if (unlikely(sk_add_backlog(sk, skb, -- sk->sk_rcvbuf + sk->sk_sndbuf))) { -- bh_unlock_sock(sk); -+ } else if (unlikely(sk_add_backlog(meta_sk, skb, -+ meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { -+ bh_unlock_sock(meta_sk); - NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); - goto discard_and_relse; - } -- bh_unlock_sock(sk); -+ bh_unlock_sock(meta_sk); - - sock_put(sk); - -@@ -2072,6 +2182,18 @@ do_time_wait: - sk = sk2; - goto process; - } -+#ifdef CONFIG_MPTCP -+ if (th->syn && !th->ack) { -+ int ret = mptcp_lookup_join(skb, inet_twsk(sk)); -+ -+ if (ret < 0) { -+ tcp_v4_send_reset(NULL, skb); -+ goto discard_it; -+ } else if (ret > 0) { -+ return 0; -+ } -+ } -+#endif - /* Fall through to ACK */ - } - case TCP_TW_ACK: -@@ -2154,6 +2276,11 @@ void tcp_v4_destroy_sock(struct sock *sk) - - tcp_cleanup_congestion_control(sk); - -+ if (tp->mpc) -+ mptcp_destroy_sock(sk); -+ if (tp->inside_tk_table) -+ mptcp_hash_remove(tp); -+ - /* Cleanup up the write buffer. */ - tcp_write_queue_purge(sk); - -diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c -index 7a436c5..72f9b8e 100644 ---- a/net/ipv4/tcp_minisocks.c -+++ b/net/ipv4/tcp_minisocks.c -@@ -18,11 +18,13 @@ - * Jorge Cwik, - */ - -+#include - #include - #include - #include - #include - #include -+#include - #include - #include - #include -@@ -95,10 +97,13 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, - struct tcp_options_received tmp_opt; - struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); - bool paws_reject = false; -+ struct mptcp_options_received mopt; - - tmp_opt.saw_tstamp = 0; - if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) { -- tcp_parse_options(skb, &tmp_opt, 0, NULL); -+ mptcp_init_mp_opt(&mopt); -+ -+ tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL); - - if (tmp_opt.saw_tstamp) { - tmp_opt.rcv_tsecr -= tcptw->tw_ts_offset; -@@ -106,6 +111,11 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, - tmp_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp; - paws_reject = tcp_paws_reject(&tmp_opt, th->rst); - } -+ -+ if (unlikely(mopt.mp_fclose) && tcptw->mptcp_tw) { -+ if (mopt.mptcp_key == tcptw->mptcp_tw->loc_key) -+ goto kill_with_rst; -+ } - } - - if (tw->tw_substate == TCP_FIN_WAIT2) { -@@ -128,6 +138,16 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, - if (!th->ack || - !after(TCP_SKB_CB(skb)->end_seq, tcptw->tw_rcv_nxt) || - TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq) { -+ /* If mptcp_is_data_fin() returns true, we are sure that -+ * mopt has been initialized - otherwise it would not -+ * be a DATA_FIN. -+ */ -+ if (tcptw->mptcp_tw && tcptw->mptcp_tw->meta_tw && -+ mptcp_is_data_fin(skb) && -+ TCP_SKB_CB(skb)->seq == tcptw->tw_rcv_nxt && -+ mopt.data_seq + 1 == (u32)tcptw->mptcp_tw->rcv_nxt) -+ return TCP_TW_ACK; -+ - inet_twsk_put(tw); - return TCP_TW_SUCCESS; - } -@@ -270,6 +290,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) - const struct tcp_sock *tp = tcp_sk(sk); - bool recycle_ok = false; - -+ if (is_meta_sk(sk)) { -+ mptcp_update_tw_socks(tp, state); -+ goto tcp_done; -+ } -+ - if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp) - recycle_ok = tcp_remember_stamp(sk); - -@@ -290,6 +315,15 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) - tcptw->tw_ts_recent_stamp = tp->rx_opt.ts_recent_stamp; - tcptw->tw_ts_offset = tp->tsoffset; - -+ if (tp->mpc) { -+ if (mptcp_time_wait(sk, tcptw)) { -+ inet_twsk_free(tw); -+ goto exit; -+ } -+ } else { -+ tcptw->mptcp_tw = NULL; -+ } -+ - #if IS_ENABLED(CONFIG_IPV6) - if (tw->tw_family == PF_INET6) { - struct ipv6_pinfo *np = inet6_sk(sk); -@@ -347,15 +381,19 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPTIMEWAITOVERFLOW); - } - -+exit: - tcp_update_metrics(sk); -+tcp_done: - tcp_done(sk); - } - - void tcp_twsk_destructor(struct sock *sk) - { --#ifdef CONFIG_TCP_MD5SIG - struct tcp_timewait_sock *twsk = tcp_twsk(sk); - -+ if (twsk->mptcp_tw) -+ mptcp_twsk_destructor(twsk); -+#ifdef CONFIG_TCP_MD5SIG - if (twsk->tw_md5_key) - kfree_rcu(twsk->tw_md5_key, rcu); - #endif -@@ -392,6 +430,9 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, - - newtp->snd_sml = newtp->snd_una = - newtp->snd_nxt = newtp->snd_up = treq->snt_isn + 1; -+#ifdef CONFIG_MPTCP -+ memset(&newtp->rcvq_space, 0, sizeof(newtp->rcvq_space)); -+#endif - - tcp_prequeue_init(newtp); - INIT_LIST_HEAD(&newtp->tsq_node); -@@ -436,7 +477,11 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, - - newtp->urg_data = 0; - -- if (sock_flag(newsk, SOCK_KEEPOPEN)) -+ /* MPTCP: If we are creating a subflow, KEEPOPEN might have been -+ * set on the meta. But, keepalive is entirely handled at the -+ * meta-socket, so let's keep it there. -+ */ -+ if (sock_flag(newsk, SOCK_KEEPOPEN) && is_meta_sk(sk)) - inet_csk_reset_keepalive_timer(newsk, - keepalive_time_when(newtp)); - -@@ -468,6 +513,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, - newtp->rx_opt.ts_recent_stamp = 0; - newtp->tcp_header_len = sizeof(struct tcphdr); - } -+ if (treq->saw_mpc) -+ newtp->tcp_header_len += MPTCP_SUB_LEN_DSM_ALIGN; - newtp->tsoffset = 0; - #ifdef CONFIG_TCP_MD5SIG - newtp->md5sig_info = NULL; /*XXX*/ -@@ -504,16 +551,20 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, - bool fastopen) - { - struct tcp_options_received tmp_opt; -+ struct mptcp_options_received mopt; - struct sock *child; - const struct tcphdr *th = tcp_hdr(skb); - __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); - bool paws_reject = false; - -- BUG_ON(fastopen == (sk->sk_state == TCP_LISTEN)); -+ BUG_ON(!tcp_sk(sk)->mpc && fastopen == (sk->sk_state == TCP_LISTEN)); - - tmp_opt.saw_tstamp = 0; -+ -+ mptcp_init_mp_opt(&mopt); -+ - if (th->doff > (sizeof(struct tcphdr)>>2)) { -- tcp_parse_options(skb, &tmp_opt, 0, NULL); -+ tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL); - - if (tmp_opt.saw_tstamp) { - tmp_opt.ts_recent = req->ts_recent; -@@ -552,7 +603,14 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, - * - * Reset timer after retransmitting SYNACK, similar to - * the idea of fast retransmit in recovery. -+ * -+ * Fall back to TCP if MP_CAPABLE is not set. - */ -+ -+ if (tcp_rsk(req)->saw_mpc && !mopt.saw_mpc) -+ tcp_rsk(req)->saw_mpc = false; -+ -+ - if (!inet_rtx_syn_ack(sk, req)) - req->expires = min(TCP_TIMEOUT_INIT << req->num_timeout, - TCP_RTO_MAX) + jiffies; -@@ -674,7 +732,20 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, - - /* While TCP_DEFER_ACCEPT is active, drop bare ACK. */ - if (req->num_timeout < inet_csk(sk)->icsk_accept_queue.rskq_defer_accept && -- TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1) { -+ TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1 && -+ /* TODO MPTCP: -+ * We do this here, because otherwise options sent in the third ack, -+ * or duplicate fourth ack will get lost. Options like MP_PRIO, ADD_ADDR,... -+ * -+ * We could store them in request_sock, but this would mean that we -+ * have to put tcp_options_received and mptcp_options_received in there, -+ * increasing considerably the size of the request-sock. -+ * -+ * As soon as we have reworked the request-sock MPTCP-fields and -+ * created a mptcp_request_sock structure, we can handle options -+ * correclty there without increasing request_sock. -+ */ -+ !tcp_rsk(req)->saw_mpc) { - inet_rsk(req)->acked = 1; - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDEFERACCEPTDROP); - return NULL; -@@ -686,10 +757,29 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, - * ESTABLISHED STATE. If it will be dropped after - * socket is created, wait for troubles. - */ -- child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); -+#ifdef CONFIG_MPTCP -+ if (tcp_sk(sk)->mpc) -+ /* MPTCP: We call the mptcp-specific syn_recv_sock */ -+ child = tcp_sk(sk)->mpcb->syn_recv_sock(sk, skb, req, NULL); -+ else -+#endif -+ child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, -+ req, NULL); -+ - if (child == NULL) - goto listen_overflow; - -+ if (!is_meta_sk(sk)) { -+ int ret = mptcp_check_req_master(sk, child, req, prev, &mopt); -+ if (ret < 0) -+ goto listen_overflow; -+ -+ /* MPTCP-supported */ -+ if (!ret) -+ return tcp_sk(child)->mpcb->master_sk; -+ } else { -+ return mptcp_check_req_child(sk, child, req, prev, &mopt); -+ } - inet_csk_reqsk_queue_unlink(sk, req, prev); - inet_csk_reqsk_queue_removed(sk, req); - -@@ -739,8 +829,9 @@ int tcp_child_process(struct sock *parent, struct sock *child, - { - int ret = 0; - int state = child->sk_state; -+ struct sock *meta_sk = tcp_sk(child)->mpc ? mptcp_meta_sk(child) : child; - -- if (!sock_owned_by_user(child)) { -+ if (!sock_owned_by_user(meta_sk)) { - ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), - skb->len); - /* Wakeup parent, send SIGIO */ -@@ -751,10 +842,14 @@ int tcp_child_process(struct sock *parent, struct sock *child, - * in main socket hash table and lock on listening - * socket does not protect us more. - */ -- __sk_add_backlog(child, skb); -+ if (tcp_sk(child)->mpc) -+ skb->sk = child; -+ __sk_add_backlog(meta_sk, skb); - } - -- bh_unlock_sock(child); -+ if (tcp_sk(child)->mpc) -+ bh_unlock_sock(child); -+ bh_unlock_sock(meta_sk); - sock_put(child); - return ret; - } -diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c -index 17a11e6..6b45057 100644 ---- a/net/ipv4/tcp_output.c -+++ b/net/ipv4/tcp_output.c -@@ -36,6 +36,12 @@ - - #define pr_fmt(fmt) "TCP: " fmt - -+#include -+#include -+#if IS_ENABLED(CONFIG_IPV6) -+#include -+#endif -+#include - #include - - #include -@@ -72,7 +78,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, - int push_one, gfp_t gfp); - - /* Account for new data that has been sent to the network. */ --static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) -+void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) - { - struct inet_connection_sock *icsk = inet_csk(sk); - struct tcp_sock *tp = tcp_sk(sk); -@@ -211,7 +217,7 @@ u32 tcp_default_init_rwnd(u32 mss) - void tcp_select_initial_window(int __space, __u32 mss, - __u32 *rcv_wnd, __u32 *window_clamp, - int wscale_ok, __u8 *rcv_wscale, -- __u32 init_rcv_wnd) -+ __u32 init_rcv_wnd, const struct sock *sk) - { - unsigned int space = (__space < 0 ? 0 : __space); - -@@ -266,11 +272,15 @@ EXPORT_SYMBOL(tcp_select_initial_window); - * value can be stuffed directly into th->window for an outgoing - * frame. - */ --static u16 tcp_select_window(struct sock *sk) -+u16 tcp_select_window(struct sock *sk) - { - struct tcp_sock *tp = tcp_sk(sk); -- u32 cur_win = tcp_receive_window(tp); -- u32 new_win = __tcp_select_window(sk); -+ /* The window must never shrink at the meta-level. At the subflow we -+ * have to allow this. Otherwise we may announce a window too large -+ * for the current meta-level sk_rcvbuf. -+ */ -+ u32 cur_win = tcp_receive_window(tp->mpc ? tcp_sk(mptcp_meta_sk(sk)) : tp); -+ u32 new_win = tp->__select_window(sk); - - /* Never shrink the offered window */ - if (new_win < cur_win) { -@@ -283,6 +293,7 @@ static u16 tcp_select_window(struct sock *sk) - */ - new_win = ALIGN(cur_win, 1 << tp->rx_opt.rcv_wscale); - } -+ - tp->rcv_wnd = new_win; - tp->rcv_wup = tp->rcv_nxt; - -@@ -361,7 +372,7 @@ static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb, - /* Constructs common control bits of non-data skb. If SYN/FIN is present, - * auto increment end seqno. - */ --static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags) -+void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags) - { - struct skb_shared_info *shinfo = skb_shinfo(skb); - -@@ -381,7 +392,7 @@ static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags) - TCP_SKB_CB(skb)->end_seq = seq; - } - --static inline bool tcp_urg_mode(const struct tcp_sock *tp) -+bool tcp_urg_mode(const struct tcp_sock *tp) - { - return tp->snd_una != tp->snd_up; - } -@@ -391,17 +402,7 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp) - #define OPTION_MD5 (1 << 2) - #define OPTION_WSCALE (1 << 3) - #define OPTION_FAST_OPEN_COOKIE (1 << 8) -- --struct tcp_out_options { -- u16 options; /* bit field of OPTION_* */ -- u16 mss; /* 0 to disable */ -- u8 ws; /* window scale, 0 to disable */ -- u8 num_sack_blocks; /* number of SACK blocks to include */ -- u8 hash_size; /* bytes in hash_location */ -- __u8 *hash_location; /* temporary pointer, overloaded */ -- __u32 tsval, tsecr; /* need to include OPTION_TS */ -- struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie */ --}; -+/* Before adding here - take a look at OPTION_MPTCP in include/net/mptcp.h */ - - /* Write previously computed TCP options to the packet. - * -@@ -417,7 +418,7 @@ struct tcp_out_options { - * (but it may well be that other scenarios fail similarly). - */ - static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, -- struct tcp_out_options *opts) -+ struct tcp_out_options *opts, struct sk_buff *skb) - { - u16 options = opts->options; /* mungable copy */ - -@@ -500,6 +501,9 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, - } - ptr += (foc->len + 3) >> 2; - } -+ -+ if (unlikely(OPTION_MPTCP & opts->options)) -+ mptcp_options_write(ptr, tp, opts, skb); - } - - /* Compute TCP options for SYN packets. This is not the final -@@ -551,6 +555,8 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, - if (unlikely(!(OPTION_TS & opts->options))) - remaining -= TCPOLEN_SACKPERM_ALIGNED; - } -+ if (tp->request_mptcp || tp->mpc) -+ mptcp_syn_options(sk, opts, &remaining); - - if (fastopen && fastopen->cookie.len >= 0) { - u32 need = TCPOLEN_EXP_FASTOPEN_BASE + fastopen->cookie.len; -@@ -624,6 +630,9 @@ static unsigned int tcp_synack_options(struct sock *sk, - } - } - -+ if (tcp_rsk(req)->saw_mpc) -+ mptcp_synack_options(req, opts, &remaining); -+ - return MAX_TCP_OPTION_SPACE - remaining; - } - -@@ -657,16 +666,22 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb - opts->tsecr = tp->rx_opt.ts_recent; - size += TCPOLEN_TSTAMP_ALIGNED; - } -+ if (tp->mpc) -+ mptcp_established_options(sk, skb, opts, &size); - - eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; - if (unlikely(eff_sacks)) { -- const unsigned int remaining = MAX_TCP_OPTION_SPACE - size; -- opts->num_sack_blocks = -- min_t(unsigned int, eff_sacks, -- (remaining - TCPOLEN_SACK_BASE_ALIGNED) / -- TCPOLEN_SACK_PERBLOCK); -- size += TCPOLEN_SACK_BASE_ALIGNED + -- opts->num_sack_blocks * TCPOLEN_SACK_PERBLOCK; -+ const unsigned remaining = MAX_TCP_OPTION_SPACE - size; -+ if (remaining < TCPOLEN_SACK_BASE_ALIGNED) -+ opts->num_sack_blocks = 0; -+ else -+ opts->num_sack_blocks = -+ min_t(unsigned int, eff_sacks, -+ (remaining - TCPOLEN_SACK_BASE_ALIGNED) / -+ TCPOLEN_SACK_PERBLOCK); -+ if (opts->num_sack_blocks) -+ size += TCPOLEN_SACK_BASE_ALIGNED + -+ opts->num_sack_blocks * TCPOLEN_SACK_PERBLOCK; - } - - return size; -@@ -714,7 +729,7 @@ static void tcp_tasklet_func(unsigned long data) - unsigned long flags; - struct list_head *q, *n; - struct tcp_sock *tp; -- struct sock *sk; -+ struct sock *sk, *meta_sk; - - local_irq_save(flags); - list_splice_init(&tsq->head, &list); -@@ -725,15 +740,27 @@ static void tcp_tasklet_func(unsigned long data) - list_del(&tp->tsq_node); - - sk = (struct sock *)tp; -- bh_lock_sock(sk); -+ meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; -+ bh_lock_sock(meta_sk); - -- if (!sock_owned_by_user(sk)) { -+ if (!sock_owned_by_user(meta_sk)) { - tcp_tsq_handler(sk); -+ if (tp->mpc) -+ tcp_tsq_handler(meta_sk); - } else { - /* defer the work to tcp_release_cb() */ - set_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); -+ -+ /* For MPTCP, we set the tsq-bit on the meta, and the -+ * subflow as we don't know if the limitation happened -+ * while inside mptcp_write_xmit or during tcp_write_xmit. -+ */ -+ if (tp->mpc) { -+ set_bit(TCP_TSQ_DEFERRED, &tcp_sk(meta_sk)->tsq_flags); -+ mptcp_tsq_flags(sk); -+ } - } -- bh_unlock_sock(sk); -+ bh_unlock_sock(meta_sk); - - clear_bit(TSQ_QUEUED, &tp->tsq_flags); - sk_free(sk); -@@ -743,7 +770,10 @@ static void tcp_tasklet_func(unsigned long data) - #define TCP_DEFERRED_ALL ((1UL << TCP_TSQ_DEFERRED) | \ - (1UL << TCP_WRITE_TIMER_DEFERRED) | \ - (1UL << TCP_DELACK_TIMER_DEFERRED) | \ -- (1UL << TCP_MTU_REDUCED_DEFERRED)) -+ (1UL << TCP_MTU_REDUCED_DEFERRED) | \ -+ (1UL << MPTCP_PATH_MANAGER) | \ -+ (1UL << MPTCP_SUB_DEFERRED)) -+ - /** - * tcp_release_cb - tcp release_sock() callback - * @sk: socket -@@ -790,6 +820,13 @@ void tcp_release_cb(struct sock *sk) - sk->sk_prot->mtu_reduced(sk); - __sock_put(sk); - } -+ if (flags & (1UL << MPTCP_PATH_MANAGER)) { -+ if (tcp_sk(sk)->mpcb->pm_ops->release_sock) -+ tcp_sk(sk)->mpcb->pm_ops->release_sock(sk); -+ __sock_put(sk); -+ } -+ if (flags & (1UL << MPTCP_SUB_DEFERRED)) -+ mptcp_tsq_sub_deferred(sk); - } - EXPORT_SYMBOL(tcp_release_cb); - -@@ -849,8 +886,8 @@ void tcp_wfree(struct sk_buff *skb) - * We are working here with either a clone of the original - * SKB, or a fresh unique copy made by the retransmit engine. - */ --static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, -- gfp_t gfp_mask) -+int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, -+ gfp_t gfp_mask) - { - const struct inet_connection_sock *icsk = inet_csk(sk); - struct inet_sock *inet; -@@ -878,10 +915,28 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, - NET_INC_STATS(sock_net(sk), - LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES); - -- if (unlikely(skb_cloned(skb))) -- skb = pskb_copy(skb, gfp_mask); -- else -+ if (unlikely(skb_cloned(skb))) { -+ struct sk_buff *newskb; -+ if (mptcp_is_data_seq(skb)) -+ skb_push(skb, MPTCP_SUB_LEN_DSS_ALIGN + -+ MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN); -+ -+ newskb = pskb_copy(skb, gfp_mask); -+ -+ if (mptcp_is_data_seq(skb)) { -+ skb_pull(skb, MPTCP_SUB_LEN_DSS_ALIGN + -+ MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN); -+ if (newskb) -+ skb_pull(newskb, MPTCP_SUB_LEN_DSS_ALIGN + -+ MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN); -+ } -+ skb = newskb; -+ } else { - skb = skb_clone(skb, gfp_mask); -+ } - if (unlikely(!skb)) - return -ENOBUFS; - } -@@ -929,7 +984,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, - */ - th->window = htons(min(tp->rcv_wnd, 65535U)); - } else { -- th->window = htons(tcp_select_window(sk)); -+ th->window = htons(tp->select_window(sk)); - } - th->check = 0; - th->urg_ptr = 0; -@@ -945,7 +1000,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, - } - } - -- tcp_options_write((__be32 *)(th + 1), tp, &opts); -+ tcp_options_write((__be32 *)(th + 1), tp, &opts, skb); - if (likely((tcb->tcp_flags & TCPHDR_SYN) == 0)) - TCP_ECN_send(sk, skb, tcp_header_size); - -@@ -984,7 +1039,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, - * NOTE: probe0 timer is not checked, do not forget tcp_push_pending_frames, - * otherwise socket can stall. - */ --static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) -+void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) - { - struct tcp_sock *tp = tcp_sk(sk); - -@@ -997,15 +1052,16 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) - } - - /* Initialize TSO segments for a packet. */ --static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, -- unsigned int mss_now) -+void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, -+ unsigned int mss_now) - { - struct skb_shared_info *shinfo = skb_shinfo(skb); - - /* Make sure we own this skb before messing gso_size/gso_segs */ - WARN_ON_ONCE(skb_cloned(skb)); - -- if (skb->len <= mss_now || skb->ip_summed == CHECKSUM_NONE) { -+ if (skb->len <= mss_now || (is_meta_sk(sk) && !mptcp_sk_can_gso(sk)) || -+ (!is_meta_sk(sk) && !sk_can_gso(sk)) || skb->ip_summed == CHECKSUM_NONE) { - /* Avoid the costly divide in the normal - * non-TSO case. - */ -@@ -1037,7 +1093,7 @@ static void tcp_adjust_fackets_out(struct sock *sk, const struct sk_buff *skb, - /* Pcount in the middle of the write queue got changed, we need to do various - * tweaks to fix counters - */ --static void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int decr) -+void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int decr) - { - struct tcp_sock *tp = tcp_sk(sk); - -@@ -1078,6 +1134,9 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, - int nlen; - u8 flags; - -+ if (tcp_sk(sk)->mpc && mptcp_is_data_seq(skb)) -+ mptcp_fragment(sk, skb, len, mss_now, 0); -+ - if (WARN_ON(len > skb->len)) - return -EINVAL; - -@@ -1160,7 +1219,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, - * eventually). The difference is that pulled data not copied, but - * immediately discarded. - */ --static void __pskb_trim_head(struct sk_buff *skb, int len) -+void __pskb_trim_head(struct sk_buff *skb, int len) - { - struct skb_shared_info *shinfo; - int i, k, eat; -@@ -1201,6 +1260,9 @@ static void __pskb_trim_head(struct sk_buff *skb, int len) - /* Remove acked data from a packet in the transmit queue. */ - int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) - { -+ if (tcp_sk(sk)->mpc && !is_meta_sk(sk) && mptcp_is_data_seq(skb)) -+ return mptcp_trim_head(sk, skb, len); -+ - if (skb_unclone(skb, GFP_ATOMIC)) - return -ENOMEM; - -@@ -1218,6 +1280,15 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) - if (tcp_skb_pcount(skb) > 1) - tcp_set_skb_tso_segs(sk, skb, tcp_skb_mss(skb)); - -+#ifdef CONFIG_MPTCP -+ /* Some data got acked - we assume that the seq-number reached the dest. -+ * Anyway, our MPTCP-option has been trimmed above - we lost it here. -+ * Only remove the SEQ if the call does not come from a meta retransmit. -+ */ -+ if (tcp_sk(sk)->mpc && !is_meta_sk(sk)) -+ TCP_SKB_CB(skb)->mptcp_flags &= ~MPTCPHDR_SEQ; -+#endif -+ - return 0; - } - -@@ -1377,7 +1448,7 @@ unsigned int tcp_current_mss(struct sock *sk) - } - - /* Congestion window validation. (RFC2861) */ --static void tcp_cwnd_validate(struct sock *sk) -+void tcp_cwnd_validate(struct sock *sk) - { - struct tcp_sock *tp = tcp_sk(sk); - -@@ -1411,8 +1482,8 @@ static bool tcp_minshall_check(const struct tcp_sock *tp) - * But we can avoid doing the divide again given we already have - * skb_pcount = skb->len / mss_now - */ --static void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now, -- const struct sk_buff *skb) -+void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now, -+ const struct sk_buff *skb) - { - if (skb->len < tcp_skb_pcount(skb) * mss_now) - tp->snd_sml = TCP_SKB_CB(skb)->end_seq; -@@ -1433,19 +1504,28 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp, - (!nonagle && tp->packets_out && tcp_minshall_check(tp))); - } - /* Returns the portion of skb which can be sent right away */ --static unsigned int tcp_mss_split_point(const struct sock *sk, -- const struct sk_buff *skb, -- unsigned int mss_now, -- unsigned int max_segs, -- int nonagle) -+unsigned int tcp_mss_split_point(const struct sock *sk, -+ const struct sk_buff *skb, -+ unsigned int mss_now, -+ unsigned int max_segs, -+ int nonagle) - { - const struct tcp_sock *tp = tcp_sk(sk); -+ const struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; - u32 partial, needed, window, max_len; - -- window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; -+ if (!tp->mpc) -+ window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; -+ else -+ /* We need to evaluate the available space in the sending window -+ * at the subflow level. However, the subflow seq has not yet -+ * been set. Nevertheless we know that the caller will set it to -+ * write_seq. -+ */ -+ window = tcp_wnd_end(tp) - tp->write_seq; - max_len = mss_now * max_segs; - -- if (likely(max_len <= window && skb != tcp_write_queue_tail(sk))) -+ if (likely(max_len <= window && skb != tcp_write_queue_tail(meta_sk))) - return max_len; - - needed = min(skb->len, window); -@@ -1467,13 +1547,14 @@ static unsigned int tcp_mss_split_point(const struct sock *sk, - /* Can at least one segment of SKB be sent right now, according to the - * congestion window rules? If so, return how many segments are allowed. - */ --static inline unsigned int tcp_cwnd_test(const struct tcp_sock *tp, -- const struct sk_buff *skb) -+unsigned int tcp_cwnd_test(const struct tcp_sock *tp, -+ const struct sk_buff *skb) - { - u32 in_flight, cwnd; - - /* Don't be strict about the congestion window for the final FIN. */ -- if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) && -+ if (skb && -+ ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) || mptcp_is_data_fin(skb)) && - tcp_skb_pcount(skb) == 1) - return 1; - -@@ -1489,8 +1570,8 @@ static inline unsigned int tcp_cwnd_test(const struct tcp_sock *tp, - * This must be invoked the first time we consider transmitting - * SKB onto the wire. - */ --static int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, -- unsigned int mss_now) -+int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, -+ unsigned int mss_now) - { - int tso_segs = tcp_skb_pcount(skb); - -@@ -1505,8 +1586,8 @@ static int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, - /* Return true if the Nagle test allows this packet to be - * sent now. - */ --static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buff *skb, -- unsigned int cur_mss, int nonagle) -+bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buff *skb, -+ unsigned int cur_mss, int nonagle) - { - /* Nagle rule does not apply to frames, which sit in the middle of the - * write_queue (they have no chances to get new data). -@@ -1518,7 +1599,8 @@ static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buf - return true; - - /* Don't use the nagle rule for urgent data (or for the final FIN). */ -- if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)) -+ if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) || -+ mptcp_is_data_fin(skb)) - return true; - - if (!tcp_nagle_check(skb->len < cur_mss, tp, cur_mss, nonagle)) -@@ -1528,9 +1610,8 @@ static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buf - } - - /* Does at least the first segment of SKB fit into the send window? */ --static bool tcp_snd_wnd_test(const struct tcp_sock *tp, -- const struct sk_buff *skb, -- unsigned int cur_mss) -+bool tcp_snd_wnd_test(const struct tcp_sock *tp, const struct sk_buff *skb, -+ unsigned int cur_mss) - { - u32 end_seq = TCP_SKB_CB(skb)->end_seq; - -@@ -1549,14 +1630,16 @@ static unsigned int tcp_snd_test(const struct sock *sk, struct sk_buff *skb, - { - const struct tcp_sock *tp = tcp_sk(sk); - unsigned int cwnd_quota; -+ const struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; -+ const struct tcp_sock *meta_tp = tcp_sk(meta_sk); - -- tcp_init_tso_segs(sk, skb, cur_mss); -+ tcp_init_tso_segs(meta_sk, skb, cur_mss); - -- if (!tcp_nagle_test(tp, skb, cur_mss, nonagle)) -+ if (!tcp_nagle_test(meta_tp, skb, cur_mss, nonagle)) - return 0; - - cwnd_quota = tcp_cwnd_test(tp, skb); -- if (cwnd_quota && !tcp_snd_wnd_test(tp, skb, cur_mss)) -+ if (cwnd_quota && !tcp_snd_wnd_test(meta_tp, skb, cur_mss)) - cwnd_quota = 0; - - return cwnd_quota; -@@ -1566,12 +1649,16 @@ static unsigned int tcp_snd_test(const struct sock *sk, struct sk_buff *skb, - bool tcp_may_send_now(struct sock *sk) - { - const struct tcp_sock *tp = tcp_sk(sk); -- struct sk_buff *skb = tcp_send_head(sk); -+ struct sk_buff *skb; -+ const struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; -+ const struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ -+ skb = tcp_send_head(meta_sk); - - return skb && - tcp_snd_test(sk, skb, tcp_current_mss(sk), -- (tcp_skb_is_last(sk, skb) ? -- tp->nonagle : TCP_NAGLE_PUSH)); -+ (tcp_skb_is_last(meta_sk, skb) ? -+ meta_tp->nonagle : TCP_NAGLE_PUSH)); - } - - /* Trim TSO SKB to LEN bytes, put the remaining data into a new packet -@@ -1588,6 +1675,9 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, - int nlen = skb->len - len; - u8 flags; - -+ if (tcp_sk(sk)->mpc && mptcp_is_data_seq(skb)) -+ mptso_fragment(sk, skb, len, mss_now, gfp, 0); -+ - /* All of a TSO frame must be composed of paged data. */ - if (skb->len != skb->data_len) - return tcp_fragment(sk, skb, len, mss_now); -@@ -1633,29 +1723,39 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, - * - * This algorithm is from John Heffner. - */ --static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) -+bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) - { - struct tcp_sock *tp = tcp_sk(sk); -+ struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); - const struct inet_connection_sock *icsk = inet_csk(sk); - u32 send_win, cong_win, limit, in_flight; - int win_divisor; - -- if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) -+ if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) || mptcp_is_data_fin(skb)) - goto send_now; - - if (icsk->icsk_ca_state != TCP_CA_Open) - goto send_now; - - /* Defer for less than two clock ticks. */ -- if (tp->tso_deferred && -- (((u32)jiffies << 1) >> 1) - (tp->tso_deferred >> 1) > 1) -+ if (meta_tp->tso_deferred && -+ (((u32)jiffies << 1) >> 1) - (meta_tp->tso_deferred >> 1) > 1) - goto send_now; - - in_flight = tcp_packets_in_flight(tp); - - BUG_ON(tcp_skb_pcount(skb) <= 1 || (tp->snd_cwnd <= in_flight)); - -- send_win = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; -+ if (!tp->mpc) -+ send_win = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; -+ else -+ /* We need to evaluate the available space in the sending window -+ * at the subflow level. However, the subflow seq has not yet -+ * been set. Nevertheless we know that the caller will set it to -+ * write_seq. -+ */ -+ send_win = tcp_wnd_end(tp) - tp->write_seq; - - /* From in_flight test above, we know that cwnd > in_flight. */ - cong_win = (tp->snd_cwnd - in_flight) * tp->mss_cache; -@@ -1668,7 +1768,7 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) - goto send_now; - - /* Middle in queue won't get any more data, full sendable already? */ -- if ((skb != tcp_write_queue_tail(sk)) && (limit >= skb->len)) -+ if ((skb != tcp_write_queue_tail(meta_sk)) && (limit >= skb->len)) - goto send_now; - - win_divisor = ACCESS_ONCE(sysctl_tcp_tso_win_divisor); -@@ -1694,13 +1794,13 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) - /* Ok, it looks like it is advisable to defer. - * Do not rearm the timer if already set to not break TCP ACK clocking. - */ -- if (!tp->tso_deferred) -- tp->tso_deferred = 1 | (jiffies << 1); -+ if (!meta_tp->tso_deferred) -+ meta_tp->tso_deferred = 1 | (jiffies << 1); - - return true; - - send_now: -- tp->tso_deferred = 0; -+ meta_tp->tso_deferred = 0; - return false; - } - -@@ -1713,7 +1813,7 @@ send_now: - * 1 if a probe was sent, - * -1 otherwise - */ --static int tcp_mtu_probe(struct sock *sk) -+int tcp_mtu_probe(struct sock *sk) - { - struct tcp_sock *tp = tcp_sk(sk); - struct inet_connection_sock *icsk = inet_csk(sk); -@@ -1858,6 +1958,9 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, - int cwnd_quota; - int result; - -+ if (is_meta_sk(sk)) -+ return mptcp_write_xmit(sk, mss_now, nonagle, push_one, gfp); -+ - sent_pkts = 0; - - if (!push_one) { -@@ -2313,6 +2416,10 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to, - if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN) - return; - -+ /* Currently not supported for MPTCP - but it should be possible */ -+ if (tp->mpc) -+ return; -+ - tcp_for_write_queue_from_safe(skb, tmp, sk) { - if (!tcp_can_collapse(sk, skb)) - break; -@@ -2410,10 +2517,26 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) - */ - if (unlikely((NET_IP_ALIGN && ((unsigned long)skb->data & 3)) || - skb_headroom(skb) >= 0xFFFF)) { -- struct sk_buff *nskb = __pskb_copy(skb, MAX_TCP_HEADER, -- GFP_ATOMIC); -+ struct sk_buff *nskb; -+ -+ if (mptcp_is_data_seq(skb)) -+ skb_push(skb, MPTCP_SUB_LEN_DSS_ALIGN + -+ MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN); -+ -+ nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC); -+ -+ if (mptcp_is_data_seq(skb)) { -+ skb_pull(skb, MPTCP_SUB_LEN_DSS_ALIGN + -+ MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN); -+ if (nskb) -+ skb_pull(nskb, MPTCP_SUB_LEN_DSS_ALIGN + -+ MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN); -+ } - err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) : -- -ENOBUFS; -+ -ENOBUFS; - } else { - err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); - } -@@ -2640,6 +2763,11 @@ void tcp_send_active_reset(struct sock *sk, gfp_t priority) - { - struct sk_buff *skb; - -+ if (is_meta_sk(sk)) { -+ mptcp_send_active_reset(sk, priority); -+ return; -+ } -+ - /* NOTE: No TCP options attached and we never retransmit this. */ - skb = alloc_skb(MAX_TCP_HEADER, priority); - if (!skb) { -@@ -2742,14 +2870,14 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, - (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0)) - req->window_clamp = tcp_full_space(sk); - -- /* tcp_full_space because it is guaranteed to be the first packet */ -- tcp_select_initial_window(tcp_full_space(sk), -- mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0), -+ tp->select_initial_window(tcp_full_space(sk), -+ mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0) - -+ (tcp_rsk(req)->saw_mpc ? MPTCP_SUB_LEN_DSM_ALIGN : 0), - &req->rcv_wnd, - &req->window_clamp, - ireq->wscale_ok, - &rcv_wscale, -- dst_metric(dst, RTAX_INITRWND)); -+ dst_metric(dst, RTAX_INITRWND), sk); - ireq->rcv_wscale = rcv_wscale; - } - -@@ -2785,7 +2913,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, - - /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ - th->window = htons(min(req->rcv_wnd, 65535U)); -- tcp_options_write((__be32 *)(th + 1), tp, &opts); -+ tcp_options_write((__be32 *)(th + 1), tp, &opts, skb); - th->doff = (tcp_header_size >> 2); - TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS, tcp_skb_pcount(skb)); - -@@ -2839,13 +2967,13 @@ static void tcp_connect_init(struct sock *sk) - (tp->window_clamp > tcp_full_space(sk) || tp->window_clamp == 0)) - tp->window_clamp = tcp_full_space(sk); - -- tcp_select_initial_window(tcp_full_space(sk), -+ tp->select_initial_window(tcp_full_space(sk), - tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0), - &tp->rcv_wnd, - &tp->window_clamp, - sysctl_tcp_window_scaling, - &rcv_wscale, -- dst_metric(dst, RTAX_INITRWND)); -+ dst_metric(dst, RTAX_INITRWND), sk); - - tp->rx_opt.rcv_wscale = rcv_wscale; - tp->rcv_ssthresh = tp->rcv_wnd; -@@ -2869,6 +2997,38 @@ static void tcp_connect_init(struct sock *sk) - inet_csk(sk)->icsk_rto = TCP_TIMEOUT_INIT; - inet_csk(sk)->icsk_retransmits = 0; - tcp_clear_retrans(tp); -+ -+#ifdef CONFIG_MPTCP -+ if (sysctl_mptcp_enabled && mptcp_doit(sk)) { -+ if (is_master_tp(tp)) { -+ tp->request_mptcp = 1; -+ mptcp_connect_init(sk); -+ } else if (tp->mptcp) { -+ struct inet_sock *inet = inet_sk(sk); -+ -+ tp->mptcp->snt_isn = tp->write_seq; -+ tp->mptcp->init_rcv_wnd = tp->rcv_wnd; -+ -+ /* Set nonce for new subflows */ -+ if (sk->sk_family == AF_INET) -+ tp->mptcp->mptcp_loc_nonce = mptcp_v4_get_nonce( -+ inet->inet_saddr, -+ inet->inet_daddr, -+ inet->inet_sport, -+ inet->inet_dport, -+ tp->write_seq); -+#if IS_ENABLED(CONFIG_IPV6) -+ else -+ tp->mptcp->mptcp_loc_nonce = mptcp_v6_get_nonce( -+ inet6_sk(sk)->saddr.s6_addr32, -+ sk->sk_v6_daddr.s6_addr32, -+ inet->inet_sport, -+ inet->inet_dport, -+ tp->write_seq); -+#endif -+ } -+ } -+#endif - } - - static void tcp_connect_queue_skb(struct sock *sk, struct sk_buff *skb) -@@ -3111,6 +3271,7 @@ void tcp_send_ack(struct sock *sk) - TCP_SKB_CB(buff)->when = tcp_time_stamp; - tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC)); - } -+EXPORT_SYMBOL(tcp_send_ack); - - /* This routine sends a packet with an out of date sequence - * number. It assumes the other end will try to ack it. -@@ -3123,7 +3284,7 @@ void tcp_send_ack(struct sock *sk) - * one is with SEG.SEQ=SND.UNA to deliver urgent pointer, another is - * out-of-date with SND.UNA-1 to probe window. - */ --static int tcp_xmit_probe_skb(struct sock *sk, int urgent) -+int tcp_xmit_probe_skb(struct sock *sk, int urgent) - { - struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb; -@@ -3161,6 +3322,9 @@ int tcp_write_wakeup(struct sock *sk) - if (sk->sk_state == TCP_CLOSE) - return -1; - -+ if (is_meta_sk(sk)) -+ return mptcp_write_wakeup(sk); -+ - if ((skb = tcp_send_head(sk)) != NULL && - before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp))) { - int err; -diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c -index 64f0354..7b55b9a 100644 ---- a/net/ipv4/tcp_timer.c -+++ b/net/ipv4/tcp_timer.c -@@ -20,6 +20,7 @@ - - #include - #include -+#include - #include - - int sysctl_tcp_syn_retries __read_mostly = TCP_SYN_RETRIES; -@@ -32,7 +33,7 @@ int sysctl_tcp_retries2 __read_mostly = TCP_RETR2; - int sysctl_tcp_orphan_retries __read_mostly; - int sysctl_tcp_thin_linear_timeouts __read_mostly; - --static void tcp_write_err(struct sock *sk) -+void tcp_write_err(struct sock *sk) - { - sk->sk_err = sk->sk_err_soft ? : ETIMEDOUT; - sk->sk_error_report(sk); -@@ -124,10 +125,8 @@ static void tcp_mtu_probing(struct inet_connection_sock *icsk, struct sock *sk) - * retransmissions with an initial RTO of TCP_RTO_MIN or TCP_TIMEOUT_INIT if - * syn_set flag is set. - */ --static bool retransmits_timed_out(struct sock *sk, -- unsigned int boundary, -- unsigned int timeout, -- bool syn_set) -+bool retransmits_timed_out(struct sock *sk, unsigned int boundary, -+ unsigned int timeout, bool syn_set) - { - unsigned int linear_backoff_thresh, start_ts; - unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN; -@@ -153,7 +152,7 @@ static bool retransmits_timed_out(struct sock *sk, - } - - /* A write timeout has occurred. Process the after effects. */ --static int tcp_write_timeout(struct sock *sk) -+int tcp_write_timeout(struct sock *sk) - { - struct inet_connection_sock *icsk = inet_csk(sk); - struct tcp_sock *tp = tcp_sk(sk); -@@ -168,6 +167,10 @@ static int tcp_write_timeout(struct sock *sk) - } - retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries; - syn_set = true; -+ /* Stop retransmitting MP_CAPABLE options in SYN if timed out. */ -+ if (tcp_sk(sk)->request_mptcp && -+ icsk->icsk_retransmits >= mptcp_sysctl_syn_retries()) -+ tcp_sk(sk)->request_mptcp = 0; - } else { - if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) { - /* Black hole detection */ -@@ -248,18 +251,22 @@ out: - static void tcp_delack_timer(unsigned long data) - { - struct sock *sk = (struct sock *)data; -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; - -- bh_lock_sock(sk); -- if (!sock_owned_by_user(sk)) { -+ bh_lock_sock(meta_sk); -+ if (!sock_owned_by_user(meta_sk)) { - tcp_delack_timer_handler(sk); - } else { - inet_csk(sk)->icsk_ack.blocked = 1; -- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED); -+ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_DELAYEDACKLOCKED); - /* deleguate our work to tcp_release_cb() */ - if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags)) - sock_hold(sk); -+ if (tp->mpc) -+ mptcp_tsq_flags(sk); - } -- bh_unlock_sock(sk); -+ bh_unlock_sock(meta_sk); - sock_put(sk); - } - -@@ -421,6 +428,9 @@ void tcp_retransmit_timer(struct sock *sk) - - tcp_enter_loss(sk, 0); - -+ if (tp->mpc) -+ mptcp_reinject_data(sk, 1); -+ - if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk)) > 0) { - /* Retransmission failed because of local congestion, - * do not backoff. -@@ -471,6 +481,8 @@ out_reset_timer: - /* Use normal (exponential) backoff */ - icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX); - } -+ if (tp->mpc) -+ mptcp_set_rto(sk); - inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX); - if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1, 0, 0)) - __sk_dst_reset(sk); -@@ -502,7 +514,10 @@ void tcp_write_timer_handler(struct sock *sk) - break; - case ICSK_TIME_RETRANS: - icsk->icsk_pending = 0; -- tcp_retransmit_timer(sk); -+ if (is_meta_sk(sk)) -+ mptcp_retransmit_timer(sk); -+ else -+ tcp_retransmit_timer(sk); - break; - case ICSK_TIME_PROBE0: - icsk->icsk_pending = 0; -@@ -517,16 +532,19 @@ out: - static void tcp_write_timer(unsigned long data) - { - struct sock *sk = (struct sock *)data; -+ struct sock *meta_sk = tcp_sk(sk)->mpc ? mptcp_meta_sk(sk) : sk; - -- bh_lock_sock(sk); -- if (!sock_owned_by_user(sk)) { -+ bh_lock_sock(meta_sk); -+ if (!sock_owned_by_user(meta_sk)) { - tcp_write_timer_handler(sk); - } else { - /* deleguate our work to tcp_release_cb() */ - if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags)) - sock_hold(sk); -+ if (tcp_sk(sk)->mpc) -+ mptcp_tsq_flags(sk); - } -- bh_unlock_sock(sk); -+ bh_unlock_sock(meta_sk); - sock_put(sk); - } - -@@ -563,11 +581,12 @@ static void tcp_keepalive_timer (unsigned long data) - struct sock *sk = (struct sock *) data; - struct inet_connection_sock *icsk = inet_csk(sk); - struct tcp_sock *tp = tcp_sk(sk); -+ struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; - u32 elapsed; - - /* Only process if socket is not in use. */ -- bh_lock_sock(sk); -- if (sock_owned_by_user(sk)) { -+ bh_lock_sock(meta_sk); -+ if (sock_owned_by_user(meta_sk)) { - /* Try again later. */ - inet_csk_reset_keepalive_timer (sk, HZ/20); - goto out; -@@ -578,6 +597,29 @@ static void tcp_keepalive_timer (unsigned long data) - goto out; - } - -+ if (tp->send_mp_fclose) { -+ /* MUST do this before tcp_write_timeout, because retrans_stamp -+ * may have been set to 0 in another part while we are -+ * retransmitting MP_FASTCLOSE. Then, we would crash, because -+ * retransmits_timed_out accesses the meta-write-queue. -+ * -+ * We make sure that the timestamp is != 0. -+ */ -+ if (!tp->retrans_stamp) -+ tp->retrans_stamp = tcp_time_stamp ? : 1; -+ -+ if (tcp_write_timeout(sk)) -+ goto out; -+ -+ tcp_send_ack(sk); -+ icsk->icsk_backoff++; -+ icsk->icsk_retransmits++; -+ -+ icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX); -+ elapsed = icsk->icsk_rto; -+ goto resched; -+ } -+ - if (sk->sk_state == TCP_FIN_WAIT2 && sock_flag(sk, SOCK_DEAD)) { - if (tp->linger2 >= 0) { - const int tmo = tcp_fin_time(sk) - TCP_TIMEWAIT_LEN; -@@ -639,7 +681,7 @@ death: - tcp_done(sk); - - out: -- bh_unlock_sock(sk); -+ bh_unlock_sock(meta_sk); - sock_put(sk); - } - -diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c -index 6c7fa08..733d602 100644 ---- a/net/ipv6/addrconf.c -+++ b/net/ipv6/addrconf.c -@@ -765,6 +765,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) - - kfree_rcu(ifp, rcu); - } -+EXPORT_SYMBOL(inet6_ifa_finish_destroy); - - static void - ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) -diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c -index d935889..9f0fd80 100644 ---- a/net/ipv6/af_inet6.c -+++ b/net/ipv6/af_inet6.c -@@ -97,8 +97,7 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) - return (struct ipv6_pinfo *)(((u8 *)sk) + offset); - } - --static int inet6_create(struct net *net, struct socket *sock, int protocol, -- int kern) -+int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) - { - struct inet_sock *inet; - struct ipv6_pinfo *np; -diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c -index c913818..2f5b4c5 100644 ---- a/net/ipv6/inet6_connection_sock.c -+++ b/net/ipv6/inet6_connection_sock.c -@@ -96,8 +96,8 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, - /* - * request_sock (formerly open request) hash tables. - */ --static u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, -- const u32 rnd, const u32 synq_hsize) -+u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, -+ const u32 rnd, const u32 synq_hsize) - { - u32 c; - -diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c -index bb53a5e7..0d29995 100644 ---- a/net/ipv6/syncookies.c -+++ b/net/ipv6/syncookies.c -@@ -181,7 +181,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) - - /* check for timestamp cookie support */ - memset(&tcp_opt, 0, sizeof(tcp_opt)); -- tcp_parse_options(skb, &tcp_opt, 0, NULL); -+ tcp_parse_options(skb, &tcp_opt, NULL, 0, NULL); - - if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) - goto out; -@@ -253,10 +253,10 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) - } - - req->window_clamp = tp->window_clamp ? :dst_metric(dst, RTAX_WINDOW); -- tcp_select_initial_window(tcp_full_space(sk), req->mss, -+ tp->select_initial_window(tcp_full_space(sk), req->mss, - &req->rcv_wnd, &req->window_clamp, - ireq->wscale_ok, &rcv_wscale, -- dst_metric(dst, RTAX_INITRWND)); -+ dst_metric(dst, RTAX_INITRWND), sk); - - ireq->rcv_wscale = rcv_wscale; - -diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c -index 889079b..d7f8b5f 100644 ---- a/net/ipv6/tcp_ipv6.c -+++ b/net/ipv6/tcp_ipv6.c -@@ -63,6 +63,8 @@ - #include - #include - #include -+#include -+#include - #include - - #include -@@ -73,14 +75,6 @@ - #include - #include - --static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb); --static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, -- struct request_sock *req); -- --static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); -- --static const struct inet_connection_sock_af_ops ipv6_mapped; --static const struct inet_connection_sock_af_ops ipv6_specific; - #ifdef CONFIG_TCP_MD5SIG - static const struct tcp_sock_af_ops tcp_sock_ipv6_specific; - static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific; -@@ -92,7 +86,7 @@ static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, - } - #endif - --static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) -+void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) - { - struct dst_entry *dst = skb_dst(skb); - const struct rt6_info *rt = (const struct rt6_info *)dst; -@@ -104,7 +98,7 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) - inet6_sk(sk)->rx_dst_cookie = rt->rt6i_node->fn_sernum; - } - --static void tcp_v6_hash(struct sock *sk) -+void tcp_v6_hash(struct sock *sk) - { - if (sk->sk_state != TCP_CLOSE) { - if (inet_csk(sk)->icsk_af_ops == &ipv6_mapped) { -@@ -117,7 +111,7 @@ static void tcp_v6_hash(struct sock *sk) - } - } - --static __u32 tcp_v6_init_sequence(const struct sk_buff *skb) -+__u32 tcp_v6_init_sequence(const struct sk_buff *skb) - { - return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32, - ipv6_hdr(skb)->saddr.s6_addr32, -@@ -125,7 +119,7 @@ static __u32 tcp_v6_init_sequence(const struct sk_buff *skb) - tcp_hdr(skb)->source); - } - --static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, -+int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, - int addr_len) - { - struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; -@@ -339,7 +333,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data; - const struct tcphdr *th = (struct tcphdr *)(skb->data+offset); - struct ipv6_pinfo *np; -- struct sock *sk; -+ struct sock *sk, *meta_sk; - int err; - struct tcp_sock *tp; - __u32 seq; -@@ -359,8 +353,14 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - return; - } - -- bh_lock_sock(sk); -- if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG) -+ tp = tcp_sk(sk); -+ if (tp->mpc) -+ meta_sk = mptcp_meta_sk(sk); -+ else -+ meta_sk = sk; -+ -+ bh_lock_sock(meta_sk); -+ if (sock_owned_by_user(meta_sk) && type != ICMPV6_PKT_TOOBIG) - NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS); - - if (sk->sk_state == TCP_CLOSE) -@@ -371,7 +371,6 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - goto out; - } - -- tp = tcp_sk(sk); - seq = ntohl(th->seq); - if (sk->sk_state != TCP_LISTEN && - !between(seq, tp->snd_una, tp->snd_nxt)) { -@@ -401,11 +400,15 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - goto out; - - tp->mtu_info = ntohl(info); -- if (!sock_owned_by_user(sk)) -+ if (!sock_owned_by_user(meta_sk)) - tcp_v6_mtu_reduced(sk); -- else if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, -+ else { -+ if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, - &tp->tsq_flags)) -- sock_hold(sk); -+ sock_hold(sk); -+ if (tp->mpc) -+ mptcp_tsq_flags(sk); -+ } - goto out; - } - -@@ -415,7 +418,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - switch (sk->sk_state) { - struct request_sock *req, **prev; - case TCP_LISTEN: -- if (sock_owned_by_user(sk)) -+ if (sock_owned_by_user(meta_sk)) - goto out; - - req = inet6_csk_search_req(sk, &prev, th->dest, &hdr->daddr, -@@ -440,7 +443,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - case TCP_SYN_SENT: - case TCP_SYN_RECV: /* Cannot happen. - It can, it SYNs are crossed. --ANK */ -- if (!sock_owned_by_user(sk)) { -+ if (!sock_owned_by_user(meta_sk)) { - sk->sk_err = err; - sk->sk_error_report(sk); /* Wake people up to see the error (see connect in sock.c) */ - -@@ -450,22 +453,22 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - goto out; - } - -- if (!sock_owned_by_user(sk) && np->recverr) { -+ if (!sock_owned_by_user(meta_sk) && np->recverr) { - sk->sk_err = err; - sk->sk_error_report(sk); - } else - sk->sk_err_soft = err; - - out: -- bh_unlock_sock(sk); -+ bh_unlock_sock(meta_sk); - sock_put(sk); - } - - --static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, -- struct flowi6 *fl6, -- struct request_sock *req, -- u16 queue_mapping) -+int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, -+ struct flowi6 *fl6, -+ struct request_sock *req, -+ u16 queue_mapping) - { - struct inet_request_sock *ireq = inet_rsk(req); - struct ipv6_pinfo *np = inet6_sk(sk); -@@ -495,7 +498,7 @@ done: - return err; - } - --static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) -+int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) - { - struct flowi6 fl6; - int res; -@@ -506,7 +509,7 @@ static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) - return res; - } - --static void tcp_v6_reqsk_destructor(struct request_sock *req) -+void tcp_v6_reqsk_destructor(struct request_sock *req) - { - kfree_skb(inet_rsk(req)->pktopts); - } -@@ -719,16 +722,16 @@ struct request_sock_ops tcp6_request_sock_ops __read_mostly = { - }; - - #ifdef CONFIG_TCP_MD5SIG --static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { -+const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { - .md5_lookup = tcp_v6_reqsk_md5_lookup, - .calc_md5_hash = tcp_v6_md5_hash_skb, - }; - #endif - --static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, -- u32 tsval, u32 tsecr, -+static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, -+ u32 data_ack, u32 win, u32 tsval, u32 tsecr, - struct tcp_md5sig_key *key, int rst, u8 tclass, -- u32 label) -+ u32 label, int mptcp) - { - const struct tcphdr *th = tcp_hdr(skb); - struct tcphdr *t1; -@@ -746,7 +749,10 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, - if (key) - tot_len += TCPOLEN_MD5SIG_ALIGNED; - #endif -- -+#ifdef CONFIG_MPTCP -+ if (mptcp) -+ tot_len += MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK; -+#endif - buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len, - GFP_ATOMIC); - if (buff == NULL) -@@ -784,6 +790,17 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, - tcp_v6_md5_hash_hdr((__u8 *)topt, key, - &ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, t1); -+ topt += 4; -+ } -+#endif -+#ifdef CONFIG_MPTCP -+ if (mptcp) { -+ /* Construction of 32-bit data_ack */ -+ *topt++ = htonl((TCPOPT_MPTCP << 24) | -+ ((MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK) << 16) | -+ (0x20 << 8) | -+ (0x01)); -+ *topt++ = htonl(data_ack); - } - #endif - -@@ -821,7 +838,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, - kfree_skb(buff); - } - --static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) -+void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) - { - const struct tcphdr *th = tcp_hdr(skb); - u32 seq = 0, ack_seq = 0; -@@ -876,7 +893,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) - ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len - - (th->doff << 2); - -- tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, key, 1, 0, 0); -+ tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, 0, key, 1, 0, 0, 0); - - #ifdef CONFIG_TCP_MD5SIG - release_sk1: -@@ -887,40 +904,47 @@ release_sk1: - #endif - } - --static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, -+static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 data_ack, - u32 win, u32 tsval, u32 tsecr, -- struct tcp_md5sig_key *key, u8 tclass, -- u32 label) -+ struct tcp_md5sig_key *key, u8 tclass, u32 label, -+ int mptcp) - { -- tcp_v6_send_response(skb, seq, ack, win, tsval, tsecr, key, 0, tclass, -- label); -+ tcp_v6_send_response(skb, seq, ack, data_ack, win, tsval, tsecr, key, 0, -+ tclass, label, mptcp); - } - - static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) - { - struct inet_timewait_sock *tw = inet_twsk(sk); - struct tcp_timewait_sock *tcptw = tcp_twsk(sk); -+ u32 data_ack = 0; -+ int mptcp = 0; - -+ if (tcptw->mptcp_tw && tcptw->mptcp_tw->meta_tw) { -+ data_ack = (u32)tcptw->mptcp_tw->rcv_nxt; -+ mptcp = 1; -+ } - tcp_v6_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, -+ data_ack, - tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, - tcp_time_stamp + tcptw->tw_ts_offset, - tcptw->tw_ts_recent, tcp_twsk_md5_key(tcptw), -- tw->tw_tclass, (tw->tw_flowlabel << 12)); -+ tw->tw_tclass, (tw->tw_flowlabel << 12), mptcp); - - inet_twsk_put(tw); - } - --static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, -- struct request_sock *req) -+void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, -+ struct request_sock *req) - { - tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1, -- req->rcv_wnd, tcp_time_stamp, req->ts_recent, -+ 0, req->rcv_wnd, tcp_time_stamp, req->ts_recent, - tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), -- 0, 0); -+ 0, 0, 0); - } - - --static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) -+struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) - { - struct request_sock *req, **prev; - const struct tcphdr *th = tcp_hdr(skb); -@@ -939,7 +963,13 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) - - if (nsk) { - if (nsk->sk_state != TCP_TIME_WAIT) { -+ /* Don't lock again the meta-sk. It has been locked -+ * before mptcp_v6_do_rcv. -+ */ -+ if (tcp_sk(nsk)->mpc && !is_meta_sk(sk)) -+ bh_lock_sock(mptcp_meta_sk(nsk)); - bh_lock_sock(nsk); -+ - return nsk; - } - inet_twsk_put(inet_twsk(nsk)); -@@ -959,6 +989,7 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) - static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) - { - struct tcp_options_received tmp_opt; -+ struct mptcp_options_received mopt; - struct request_sock *req; - struct inet_request_sock *ireq; - struct ipv6_pinfo *np = inet6_sk(sk); -@@ -971,6 +1002,23 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) - if (skb->protocol == htons(ETH_P_IP)) - return tcp_v4_conn_request(sk, skb); - -+ tcp_clear_options(&tmp_opt); -+ tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); -+ tmp_opt.user_mss = tp->rx_opt.user_mss; -+ mptcp_init_mp_opt(&mopt); -+ tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL); -+ -+#ifdef CONFIG_MPTCP -+ /*MPTCP structures not initialized, so return error */ -+ if (mptcp_init_failed) -+ mptcp_init_mp_opt(&mopt); -+ -+ if (mopt.is_mp_join) -+ return mptcp_do_join_short(skb, &mopt, &tmp_opt, sock_net(sk)); -+ if (mopt.drop_me) -+ goto drop; -+#endif -+ - if (!ipv6_unicast_destination(skb)) - goto drop; - -@@ -986,7 +1034,22 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) - goto drop; - } - -- req = inet6_reqsk_alloc(&tcp6_request_sock_ops); -+#ifdef CONFIG_MPTCP -+ if (sysctl_mptcp_enabled == MPTCP_APP && !tp->mptcp_enabled) -+ mopt.saw_mpc = 0; -+ if (mopt.saw_mpc && !want_cookie) { -+ req = inet6_reqsk_alloc(&mptcp6_request_sock_ops); -+ -+ if (req == NULL) -+ goto drop; -+ -+ mptcp_rsk(req)->mpcb = NULL; -+ mptcp_rsk(req)->dss_csum = mopt.dss_csum; -+ mptcp_rsk(req)->collide_tk.pprev = NULL; -+ } else -+#endif -+ req = inet6_reqsk_alloc(&tcp6_request_sock_ops); -+ - if (req == NULL) - goto drop; - -@@ -994,17 +1057,15 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) - tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops; - #endif - -- tcp_clear_options(&tmp_opt); -- tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); -- tmp_opt.user_mss = tp->rx_opt.user_mss; -- tcp_parse_options(skb, &tmp_opt, 0, NULL); -- - if (want_cookie && !tmp_opt.saw_tstamp) - tcp_clear_options(&tmp_opt); - - tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; - tcp_openreq_init(req, &tmp_opt, skb); - -+ if (mopt.saw_mpc && !want_cookie) -+ mptcp_reqsk_new_mptcp(req, &tmp_opt, &mopt, skb); -+ - ireq = inet_rsk(req); - ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; - ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; -@@ -1094,9 +1155,9 @@ drop: - return 0; /* don't send reset */ - } - --static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, -- struct request_sock *req, -- struct dst_entry *dst) -+struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, -+ struct request_sock *req, -+ struct dst_entry *dst) - { - struct inet_request_sock *ireq; - struct ipv6_pinfo *newnp, *np = inet6_sk(sk); -@@ -1317,7 +1378,7 @@ static __sum16 tcp_v6_checksum_init(struct sk_buff *skb) - * This is because we cannot sleep with the original spinlock - * held. - */ --static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) -+int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) - { - struct ipv6_pinfo *np = inet6_sk(sk); - struct tcp_sock *tp; -@@ -1339,6 +1400,9 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) - goto discard; - #endif - -+ if (is_meta_sk(sk)) -+ return mptcp_v6_do_rcv(sk, skb); -+ - if (sk_filter(sk, skb)) - goto discard; - -@@ -1460,7 +1524,7 @@ static int tcp_v6_rcv(struct sk_buff *skb) - { - const struct tcphdr *th; - const struct ipv6hdr *hdr; -- struct sock *sk; -+ struct sock *sk, *meta_sk = NULL; - int ret; - struct net *net = dev_net(skb->dev); - -@@ -1491,18 +1555,43 @@ static int tcp_v6_rcv(struct sk_buff *skb) - TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + - skb->len - th->doff*4); - TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); -+#ifdef CONFIG_MPTCP -+ TCP_SKB_CB(skb)->mptcp_flags = 0; -+ TCP_SKB_CB(skb)->dss_off = 0; -+#endif - TCP_SKB_CB(skb)->when = 0; - TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr); - TCP_SKB_CB(skb)->sacked = 0; - - sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest); -- if (!sk) -- goto no_tcp_socket; - - process: -- if (sk->sk_state == TCP_TIME_WAIT) -+ if (sk && sk->sk_state == TCP_TIME_WAIT) - goto do_time_wait; - -+#ifdef CONFIG_MPTCP -+ if (!sk && th->syn && !th->ack) { -+ int ret = mptcp_lookup_join(skb, NULL); -+ -+ if (ret < 0) { -+ tcp_v6_send_reset(NULL, skb); -+ goto discard_it; -+ } else if (ret > 0) { -+ return 0; -+ } -+ } -+ -+ /* Is there a pending request sock for this segment ? */ -+ if ((!sk || sk->sk_state == TCP_LISTEN) && mptcp_check_req(skb, net)) { -+ if (sk) -+ sock_put(sk); -+ return 0; -+ } -+#endif -+ -+ if (!sk) -+ goto no_tcp_socket; -+ - if (hdr->hop_limit < inet6_sk(sk)->min_hopcount) { - NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP); - goto discard_and_relse; -@@ -1517,11 +1606,21 @@ process: - sk_mark_napi_id(sk, skb); - skb->dev = NULL; - -- bh_lock_sock_nested(sk); -+ if (tcp_sk(sk)->mpc) { -+ meta_sk = mptcp_meta_sk(sk); -+ -+ bh_lock_sock_nested(meta_sk); -+ if (sock_owned_by_user(meta_sk)) -+ skb->sk = sk; -+ } else { -+ meta_sk = sk; -+ bh_lock_sock_nested(sk); -+ } -+ - ret = 0; -- if (!sock_owned_by_user(sk)) { -+ if (!sock_owned_by_user(meta_sk)) { - #ifdef CONFIG_NET_DMA -- struct tcp_sock *tp = tcp_sk(sk); -+ struct tcp_sock *tp = tcp_sk(meta_sk); - if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) - tp->ucopy.dma_chan = net_dma_find_channel(); - if (tp->ucopy.dma_chan) -@@ -1529,16 +1628,17 @@ process: - else - #endif - { -- if (!tcp_prequeue(sk, skb)) -+ if (!tcp_prequeue(meta_sk, skb)) - ret = tcp_v6_do_rcv(sk, skb); - } -- } else if (unlikely(sk_add_backlog(sk, skb, -- sk->sk_rcvbuf + sk->sk_sndbuf))) { -- bh_unlock_sock(sk); -+ } else if (unlikely(sk_add_backlog(meta_sk, skb, -+ meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { -+ bh_unlock_sock(meta_sk); - NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); - goto discard_and_relse; - } -- bh_unlock_sock(sk); -+ -+ bh_unlock_sock(meta_sk); - - sock_put(sk); - return ret ? -1 : 0; -@@ -1595,6 +1695,18 @@ do_time_wait: - sk = sk2; - goto process; - } -+#ifdef CONFIG_MPTCP -+ if (th->syn && !th->ack) { -+ int ret = mptcp_lookup_join(skb, inet_twsk(sk)); -+ -+ if (ret < 0) { -+ tcp_v6_send_reset(NULL, skb); -+ goto discard_it; -+ } else if (ret > 0) { -+ return 0; -+ } -+ } -+#endif - /* Fall through to ACK */ - } - case TCP_TW_ACK: -@@ -1644,13 +1756,13 @@ static void tcp_v6_early_demux(struct sk_buff *skb) - } - } - --static struct timewait_sock_ops tcp6_timewait_sock_ops = { -+struct timewait_sock_ops tcp6_timewait_sock_ops = { - .twsk_obj_size = sizeof(struct tcp6_timewait_sock), - .twsk_unique = tcp_twsk_unique, - .twsk_destructor= tcp_twsk_destructor, - }; - --static const struct inet_connection_sock_af_ops ipv6_specific = { -+const struct inet_connection_sock_af_ops ipv6_specific = { - .queue_xmit = inet6_csk_xmit, - .send_check = tcp_v6_send_check, - .rebuild_header = inet6_sk_rebuild_header, -@@ -1682,7 +1794,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = { - * TCP over IPv4 via INET6 API - */ - --static const struct inet_connection_sock_af_ops ipv6_mapped = { -+const struct inet_connection_sock_af_ops ipv6_mapped = { - .queue_xmit = ip_queue_xmit, - .send_check = tcp_v4_send_check, - .rebuild_header = inet_sk_rebuild_header, -@@ -1727,7 +1839,7 @@ static int tcp_v6_init_sock(struct sock *sk) - return 0; - } - --static void tcp_v6_destroy_sock(struct sock *sk) -+void tcp_v6_destroy_sock(struct sock *sk) - { - tcp_v4_destroy_sock(sk); - inet6_destroy_sock(sk); -diff --git a/net/mptcp/Kconfig b/net/mptcp/Kconfig -new file mode 100644 -index 0000000..88a05b1 ---- /dev/null -+++ b/net/mptcp/Kconfig -@@ -0,0 +1,58 @@ -+# -+# MPTCP configuration -+# -+config MPTCP -+ bool "MPTCP protocol" -+ depends on (IPV6=y || IPV6=n) -+ ---help--- -+ This replaces the normal TCP stack with a Multipath TCP stack, -+ able to use several paths at once. -+ -+menuconfig MPTCP_PM_ADVANCED -+ bool "MPTCP: advanced path-manager control" -+ depends on MPTCP=y -+ ---help--- -+ Support for selection of different path-managers. You should choose 'Y' here, -+ because otherwise you will not actively create new MPTCP-subflows. -+ -+if MPTCP_PM_ADVANCED -+ -+config MPTCP_FULLMESH -+ tristate "MPTCP Full-Mesh Path-Manager" -+ depends on MPTCP=y -+ ---help--- -+ This path-management module will create a full-mesh among all IP-addresses. -+ -+config MPTCP_NDIFFPORTS -+ tristate "MPTCP ndiff-ports" -+ depends on MPTCP=y -+ ---help--- -+ This path-management module will create multiple subflows between the same -+ pair of IP-addresses, modifying the source-port. You can set the number -+ of subflows via the mptcp_ndiffports-sysctl. -+ -+choice -+ prompt "Default MPTCP Path-Manager" -+ default DEFAULT -+ help -+ Select the Path-Manager of your choice -+ -+ config DEFAULT_FULLMESH -+ bool "Full mesh" if MPTCP_FULLMESH=y -+ -+ config DEFAULT_NDIFFPORTS -+ bool "ndiff-ports" if MPTCP_NDIFFPORTS=y -+ -+ config DEFAULT_DUMMY -+ bool "Default" -+ -+endchoice -+ -+endif -+ -+config DEFAULT_MPTCP_PM -+ string -+ default "default" if DEFAULT_DUMMY -+ default "fullmesh" if DEFAULT_FULLMESH -+ default "ndiffports" if DEFAULT_NDIFFPORTS -+ default "default" -diff --git a/net/mptcp/Makefile b/net/mptcp/Makefile -new file mode 100644 -index 0000000..e7238f5 ---- /dev/null -+++ b/net/mptcp/Makefile -@@ -0,0 +1,18 @@ -+# -+## Makefile for MultiPath TCP support code. -+# -+# -+ -+obj-$(CONFIG_MPTCP) += mptcp.o -+ -+mptcp-y := mptcp_ctrl.o mptcp_ipv4.o mptcp_ofo_queue.o mptcp_pm.o \ -+ mptcp_output.o mptcp_input.o -+ -+obj-$(CONFIG_TCP_CONG_COUPLED) += mptcp_coupled.o -+obj-$(CONFIG_TCP_CONG_OLIA) += mptcp_olia.o -+obj-$(CONFIG_TCP_CONG_WVEGAS) += mptcp_wvegas.o -+obj-$(CONFIG_MPTCP_FULLMESH) += mptcp_fullmesh.o -+obj-$(CONFIG_MPTCP_NDIFFPORTS) += mptcp_ndiffports.o -+ -+mptcp-$(subst m,y,$(CONFIG_IPV6)) += mptcp_ipv6.o -+ -diff --git a/net/mptcp/mptcp_coupled.c b/net/mptcp/mptcp_coupled.c -new file mode 100644 -index 0000000..d71f96e ---- /dev/null -+++ b/net/mptcp/mptcp_coupled.c -@@ -0,0 +1,273 @@ -+/* -+ * MPTCP implementation - Coupled Congestion Control -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer & Author: -+ * Christoph Paasch -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+#include -+#include -+ -+#include -+ -+/* Scaling is done in the numerator with alpha_scale_num and in the denominator -+ * with alpha_scale_den. -+ * -+ * To downscale, we just need to use alpha_scale. -+ * -+ * We have: alpha_scale = alpha_scale_num / (alpha_scale_den ^ 2) -+ */ -+static int alpha_scale_den = 10; -+static int alpha_scale_num = 32; -+static int alpha_scale = 12; -+ -+struct mptcp_ccc { -+ u64 alpha; -+ bool forced_update; -+}; -+ -+static inline int mptcp_ccc_sk_can_send(const struct sock *sk) -+{ -+ return mptcp_sk_can_send(sk) && tcp_sk(sk)->srtt; -+} -+ -+static inline u64 mptcp_get_alpha(struct sock *meta_sk) -+{ -+ struct mptcp_ccc *mptcp_ccc = inet_csk_ca(meta_sk); -+ return mptcp_ccc->alpha; -+} -+ -+static inline void mptcp_set_alpha(struct sock *meta_sk, u64 alpha) -+{ -+ struct mptcp_ccc *mptcp_ccc = inet_csk_ca(meta_sk); -+ mptcp_ccc->alpha = alpha; -+} -+ -+static inline u64 mptcp_ccc_scale(u32 val, int scale) -+{ -+ return (u64) val << scale; -+} -+ -+static inline bool mptcp_get_forced(struct sock *meta_sk) -+{ -+ struct mptcp_ccc *mptcp_ccc = inet_csk_ca(meta_sk); -+ return mptcp_ccc->forced_update; -+} -+ -+static inline void mptcp_set_forced(struct sock *meta_sk, bool force) -+{ -+ struct mptcp_ccc *mptcp_ccc = inet_csk_ca(meta_sk); -+ mptcp_ccc->forced_update = force; -+} -+ -+static void mptcp_ccc_recalc_alpha(struct sock *sk) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; -+ struct sock *sub_sk; -+ int best_cwnd = 0, best_rtt = 0, can_send = 0; -+ u64 max_numerator = 0, sum_denominator = 0, alpha = 1; -+ -+ if (!mpcb) -+ return; -+ -+ /* Only one subflow left - fall back to normal reno-behavior -+ * (set alpha to 1) */ -+ if (mpcb->cnt_established <= 1) -+ goto exit; -+ -+ /* Do regular alpha-calculation for multiple subflows */ -+ -+ /* Find the max numerator of the alpha-calculation */ -+ mptcp_for_each_sk(mpcb, sub_sk) { -+ struct tcp_sock *sub_tp = tcp_sk(sub_sk); -+ u64 tmp; -+ -+ if (!mptcp_ccc_sk_can_send(sub_sk)) -+ continue; -+ -+ can_send++; -+ -+ /* We need to look for the path, that provides the max-value. -+ * Integer-overflow is not possible here, because -+ * tmp will be in u64. -+ */ -+ tmp = div64_u64(mptcp_ccc_scale(sub_tp->snd_cwnd, -+ alpha_scale_num), (u64)sub_tp->srtt * sub_tp->srtt); -+ -+ if (tmp >= max_numerator) { -+ max_numerator = tmp; -+ best_cwnd = sub_tp->snd_cwnd; -+ best_rtt = sub_tp->srtt; -+ } -+ } -+ -+ /* No subflow is able to send - we don't care anymore */ -+ if (unlikely(!can_send)) -+ goto exit; -+ -+ /* Calculate the denominator */ -+ mptcp_for_each_sk(mpcb, sub_sk) { -+ struct tcp_sock *sub_tp = tcp_sk(sub_sk); -+ -+ if (!mptcp_ccc_sk_can_send(sub_sk)) -+ continue; -+ -+ sum_denominator += div_u64( -+ mptcp_ccc_scale(sub_tp->snd_cwnd, -+ alpha_scale_den) * best_rtt, -+ sub_tp->srtt); -+ } -+ sum_denominator *= sum_denominator; -+ if (unlikely(!sum_denominator)) { -+ pr_err("%s: sum_denominator == 0, cnt_established:%d\n", -+ __func__, mpcb->cnt_established); -+ mptcp_for_each_sk(mpcb, sub_sk) { -+ struct tcp_sock *sub_tp = tcp_sk(sub_sk); -+ pr_err("%s: pi:%d, state:%d\n, rtt:%u, cwnd: %u", -+ __func__, sub_tp->mptcp->path_index, -+ sub_sk->sk_state, sub_tp->srtt, -+ sub_tp->snd_cwnd); -+ } -+ } -+ -+ alpha = div64_u64(mptcp_ccc_scale(best_cwnd, alpha_scale_num), sum_denominator); -+ -+ if (unlikely(!alpha)) -+ alpha = 1; -+ -+exit: -+ mptcp_set_alpha(mptcp_meta_sk(sk), alpha); -+} -+ -+static void mptcp_ccc_init(struct sock *sk) -+{ -+ if (tcp_sk(sk)->mpc) { -+ mptcp_set_forced(mptcp_meta_sk(sk), 0); -+ mptcp_set_alpha(mptcp_meta_sk(sk), 1); -+ } -+ /* If we do not mptcp, behave like reno: return */ -+} -+ -+static void mptcp_ccc_cwnd_event(struct sock *sk, enum tcp_ca_event event) -+{ -+ if (event == CA_EVENT_LOSS) -+ mptcp_ccc_recalc_alpha(sk); -+} -+ -+static void mptcp_ccc_set_state(struct sock *sk, u8 ca_state) -+{ -+ if (!tcp_sk(sk)->mpc) -+ return; -+ -+ mptcp_set_forced(mptcp_meta_sk(sk), 1); -+} -+ -+static void mptcp_ccc_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct mptcp_cb *mpcb = tp->mpcb; -+ int snd_cwnd; -+ -+ if (!tp->mpc) { -+ tcp_reno_cong_avoid(sk, ack, acked, in_flight); -+ return; -+ } -+ -+ if (!tcp_is_cwnd_limited(sk, in_flight)) -+ return; -+ -+ if (tp->snd_cwnd <= tp->snd_ssthresh) { -+ /* In "safe" area, increase. */ -+ tcp_slow_start(tp, acked); -+ mptcp_ccc_recalc_alpha(sk); -+ return; -+ } -+ -+ if (mptcp_get_forced(mptcp_meta_sk(sk))) { -+ mptcp_ccc_recalc_alpha(sk); -+ mptcp_set_forced(mptcp_meta_sk(sk), 0); -+ } -+ -+ if (mpcb->cnt_established > 1) { -+ u64 alpha = mptcp_get_alpha(mptcp_meta_sk(sk)); -+ -+ /* This may happen, if at the initialization, the mpcb -+ * was not yet attached to the sock, and thus -+ * initializing alpha failed. -+ */ -+ if (unlikely(!alpha)) -+ alpha = 1; -+ -+ snd_cwnd = (int) div_u64 ((u64) mptcp_ccc_scale(1, alpha_scale), -+ alpha); -+ -+ /* snd_cwnd_cnt >= max (scale * tot_cwnd / alpha, cwnd) -+ * Thus, we select here the max value. */ -+ if (snd_cwnd < tp->snd_cwnd) -+ snd_cwnd = tp->snd_cwnd; -+ } else { -+ snd_cwnd = tp->snd_cwnd; -+ } -+ -+ if (tp->snd_cwnd_cnt >= snd_cwnd) { -+ if (tp->snd_cwnd < tp->snd_cwnd_clamp) { -+ tp->snd_cwnd++; -+ mptcp_ccc_recalc_alpha(sk); -+ } -+ -+ tp->snd_cwnd_cnt = 0; -+ } else { -+ tp->snd_cwnd_cnt++; -+ } -+} -+ -+static struct tcp_congestion_ops mptcp_ccc = { -+ .init = mptcp_ccc_init, -+ .ssthresh = tcp_reno_ssthresh, -+ .cong_avoid = mptcp_ccc_cong_avoid, -+ .cwnd_event = mptcp_ccc_cwnd_event, -+ .set_state = mptcp_ccc_set_state, -+ .min_cwnd = tcp_reno_min_cwnd, -+ .owner = THIS_MODULE, -+ .name = "coupled", -+}; -+ -+static int __init mptcp_ccc_register(void) -+{ -+ BUILD_BUG_ON(sizeof(struct mptcp_ccc) > ICSK_CA_PRIV_SIZE); -+ return tcp_register_congestion_control(&mptcp_ccc); -+} -+ -+static void __exit mptcp_ccc_unregister(void) -+{ -+ tcp_unregister_congestion_control(&mptcp_ccc); -+} -+ -+module_init(mptcp_ccc_register); -+module_exit(mptcp_ccc_unregister); -+ -+MODULE_AUTHOR("Christoph Paasch, Sébastien Barré"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("MPTCP COUPLED CONGESTION CONTROL"); -+MODULE_VERSION("0.1"); -diff --git a/net/mptcp/mptcp_ctrl.c b/net/mptcp/mptcp_ctrl.c -new file mode 100644 -index 0000000..6a7654d ---- /dev/null -+++ b/net/mptcp/mptcp_ctrl.c -@@ -0,0 +1,2270 @@ -+/* -+ * MPTCP implementation - MPTCP-control -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer & Author: -+ * Christoph Paasch -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#if IS_ENABLED(CONFIG_IPV6) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static struct kmem_cache *mptcp_sock_cache __read_mostly; -+static struct kmem_cache *mptcp_cb_cache __read_mostly; -+static struct kmem_cache *mptcp_tw_cache __read_mostly; -+ -+int sysctl_mptcp_enabled __read_mostly = 1; -+int sysctl_mptcp_checksum __read_mostly = 1; -+int sysctl_mptcp_debug __read_mostly; -+EXPORT_SYMBOL(sysctl_mptcp_debug); -+int sysctl_mptcp_syn_retries __read_mostly = 3; -+ -+bool mptcp_init_failed __read_mostly; -+ -+static int proc_mptcp_path_manager(ctl_table *ctl, int write, -+ void __user *buffer, size_t *lenp, -+ loff_t *ppos) -+{ -+ char val[MPTCP_PM_NAME_MAX]; -+ ctl_table tbl = { -+ .data = val, -+ .maxlen = MPTCP_PM_NAME_MAX, -+ }; -+ int ret; -+ -+ mptcp_get_default_path_manager(val); -+ -+ ret = proc_dostring(&tbl, write, buffer, lenp, ppos); -+ if (write && ret == 0) -+ ret = mptcp_set_default_path_manager(val); -+ return ret; -+} -+ -+static struct ctl_table mptcp_table[] = { -+ { -+ .procname = "mptcp_enabled", -+ .data = &sysctl_mptcp_enabled, -+ .maxlen = sizeof(int), -+ .mode = 0644, -+ .proc_handler = &proc_dointvec -+ }, -+ { -+ .procname = "mptcp_checksum", -+ .data = &sysctl_mptcp_checksum, -+ .maxlen = sizeof(int), -+ .mode = 0644, -+ .proc_handler = &proc_dointvec -+ }, -+ { -+ .procname = "mptcp_debug", -+ .data = &sysctl_mptcp_debug, -+ .maxlen = sizeof(int), -+ .mode = 0644, -+ .proc_handler = &proc_dointvec -+ }, -+ { -+ .procname = "mptcp_syn_retries", -+ .data = &sysctl_mptcp_syn_retries, -+ .maxlen = sizeof(int), -+ .mode = 0644, -+ .proc_handler = &proc_dointvec -+ }, -+ { -+ .procname = "mptcp_path_manager", -+ .mode = 0644, -+ .maxlen = MPTCP_PM_NAME_MAX, -+ .proc_handler = proc_mptcp_path_manager, -+ }, -+ { } -+}; -+ -+static inline u32 mptcp_hash_tk(u32 token) -+{ -+ return token % MPTCP_HASH_SIZE; -+} -+ -+struct hlist_nulls_head tk_hashtable[MPTCP_HASH_SIZE]; -+EXPORT_SYMBOL(tk_hashtable); -+ -+/* This second hashtable is needed to retrieve request socks -+ * created as a result of a join request. While the SYN contains -+ * the token, the final ack does not, so we need a separate hashtable -+ * to retrieve the mpcb. -+ */ -+struct list_head mptcp_reqsk_htb[MPTCP_HASH_SIZE]; -+spinlock_t mptcp_reqsk_hlock; /* hashtable protection */ -+ -+/* The following hash table is used to avoid collision of token */ -+static struct hlist_nulls_head mptcp_reqsk_tk_htb[MPTCP_HASH_SIZE]; -+spinlock_t mptcp_tk_hashlock; /* hashtable protection */ -+ -+static int mptcp_reqsk_find_tk(u32 token) -+{ -+ u32 hash = mptcp_hash_tk(token); -+ struct mptcp_request_sock *mtreqsk; -+ const struct hlist_nulls_node *node; -+ -+ hlist_nulls_for_each_entry_rcu(mtreqsk, node, -+ &mptcp_reqsk_tk_htb[hash], collide_tk) { -+ if (token == mtreqsk->mptcp_loc_token) -+ return 1; -+ } -+ return 0; -+} -+ -+static void mptcp_reqsk_insert_tk(struct request_sock *reqsk, u32 token) -+{ -+ u32 hash = mptcp_hash_tk(token); -+ -+ hlist_nulls_add_head_rcu(&mptcp_rsk(reqsk)->collide_tk, -+ &mptcp_reqsk_tk_htb[hash]); -+} -+ -+static void mptcp_reqsk_remove_tk(struct request_sock *reqsk) -+{ -+ rcu_read_lock(); -+ spin_lock(&mptcp_tk_hashlock); -+ hlist_nulls_del_init_rcu(&mptcp_rsk(reqsk)->collide_tk); -+ spin_unlock(&mptcp_tk_hashlock); -+ rcu_read_unlock(); -+} -+ -+void mptcp_reqsk_destructor(struct request_sock *req) -+{ -+ if (!mptcp_rsk(req)->mpcb) { -+ if (in_softirq()) { -+ mptcp_reqsk_remove_tk(req); -+ } else { -+ rcu_read_lock_bh(); -+ spin_lock(&mptcp_tk_hashlock); -+ hlist_nulls_del_init_rcu(&mptcp_rsk(req)->collide_tk); -+ spin_unlock(&mptcp_tk_hashlock); -+ rcu_read_unlock_bh(); -+ } -+ } else { -+ mptcp_hash_request_remove(req); -+ } -+} -+ -+static void __mptcp_hash_insert(struct tcp_sock *meta_tp, u32 token) -+{ -+ u32 hash = mptcp_hash_tk(token); -+ hlist_nulls_add_head_rcu(&meta_tp->tk_table, &tk_hashtable[hash]); -+ meta_tp->inside_tk_table = 1; -+} -+ -+static int mptcp_find_token(u32 token) -+{ -+ u32 hash = mptcp_hash_tk(token); -+ struct tcp_sock *meta_tp; -+ const struct hlist_nulls_node *node; -+ -+ hlist_nulls_for_each_entry_rcu(meta_tp, node, &tk_hashtable[hash], tk_table) { -+ if (token == meta_tp->mptcp_loc_token) -+ return 1; -+ } -+ return 0; -+} -+ -+static void mptcp_set_key_reqsk(struct request_sock *req, -+ const struct sk_buff *skb) -+{ -+ struct inet_request_sock *ireq = inet_rsk(req); -+ struct mptcp_request_sock *mtreq = mptcp_rsk(req); -+ -+ if (skb->protocol == htons(ETH_P_IP)) { -+ mtreq->mptcp_loc_key = mptcp_v4_get_key(ip_hdr(skb)->saddr, -+ ip_hdr(skb)->daddr, -+ htons(ireq->ir_num), -+ ireq->ir_rmt_port); -+#if IS_ENABLED(CONFIG_IPV6) -+ } else { -+ mtreq->mptcp_loc_key = mptcp_v6_get_key(ipv6_hdr(skb)->saddr.s6_addr32, -+ ipv6_hdr(skb)->daddr.s6_addr32, -+ htons(ireq->ir_num), -+ ireq->ir_rmt_port); -+#endif -+ } -+ -+ mptcp_key_sha1(mtreq->mptcp_loc_key, &mtreq->mptcp_loc_token, NULL); -+} -+ -+/* New MPTCP-connection request, prepare a new token for the meta-socket that -+ * will be created in mptcp_check_req_master(), and store the received token. -+ */ -+void mptcp_reqsk_new_mptcp(struct request_sock *req, -+ const struct tcp_options_received *rx_opt, -+ const struct mptcp_options_received *mopt, -+ const struct sk_buff *skb) -+{ -+ struct mptcp_request_sock *mtreq = mptcp_rsk(req); -+ -+ tcp_rsk(req)->saw_mpc = 1; -+ -+ rcu_read_lock(); -+ spin_lock(&mptcp_tk_hashlock); -+ do { -+ mptcp_set_key_reqsk(req, skb); -+ } while (mptcp_reqsk_find_tk(mtreq->mptcp_loc_token) || -+ mptcp_find_token(mtreq->mptcp_loc_token)); -+ -+ mptcp_reqsk_insert_tk(req, mtreq->mptcp_loc_token); -+ spin_unlock(&mptcp_tk_hashlock); -+ rcu_read_unlock(); -+ mtreq->mptcp_rem_key = mopt->mptcp_key; -+} -+ -+static void mptcp_set_key_sk(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct inet_sock *isk = inet_sk(sk); -+ -+ if (sk->sk_family == AF_INET) -+ tp->mptcp_loc_key = mptcp_v4_get_key(isk->inet_saddr, -+ isk->inet_daddr, -+ isk->inet_sport, -+ isk->inet_dport); -+#if IS_ENABLED(CONFIG_IPV6) -+ else -+ tp->mptcp_loc_key = mptcp_v6_get_key(inet6_sk(sk)->saddr.s6_addr32, -+ sk->sk_v6_daddr.s6_addr32, -+ isk->inet_sport, -+ isk->inet_dport); -+#endif -+ -+ mptcp_key_sha1(tp->mptcp_loc_key, -+ &tp->mptcp_loc_token, NULL); -+} -+ -+void mptcp_connect_init(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ rcu_read_lock_bh(); -+ spin_lock(&mptcp_tk_hashlock); -+ do { -+ mptcp_set_key_sk(sk); -+ } while (mptcp_reqsk_find_tk(tp->mptcp_loc_token) || -+ mptcp_find_token(tp->mptcp_loc_token)); -+ -+ __mptcp_hash_insert(tp, tp->mptcp_loc_token); -+ spin_unlock(&mptcp_tk_hashlock); -+ rcu_read_unlock_bh(); -+} -+ -+/** -+ * This function increments the refcount of the mpcb struct. -+ * It is the responsibility of the caller to decrement when releasing -+ * the structure. -+ */ -+struct sock *mptcp_hash_find(struct net *net, u32 token) -+{ -+ u32 hash = mptcp_hash_tk(token); -+ struct tcp_sock *meta_tp; -+ struct sock *meta_sk = NULL; -+ struct hlist_nulls_node *node; -+ -+ rcu_read_lock(); -+ hlist_nulls_for_each_entry_rcu(meta_tp, node, &tk_hashtable[hash], -+ tk_table) { -+ meta_sk = (struct sock *)meta_tp; -+ if (token == meta_tp->mptcp_loc_token && -+ net_eq(net, sock_net(meta_sk)) && -+ atomic_inc_not_zero(&meta_sk->sk_refcnt)) -+ break; -+ meta_sk = NULL; -+ } -+ rcu_read_unlock(); -+ return meta_sk; -+} -+ -+void mptcp_hash_remove_bh(struct tcp_sock *meta_tp) -+{ -+ /* remove from the token hashtable */ -+ rcu_read_lock_bh(); -+ spin_lock(&mptcp_tk_hashlock); -+ hlist_nulls_del_init_rcu(&meta_tp->tk_table); -+ meta_tp->inside_tk_table = 0; -+ spin_unlock(&mptcp_tk_hashlock); -+ rcu_read_unlock_bh(); -+} -+ -+void mptcp_hash_remove(struct tcp_sock *meta_tp) -+{ -+ rcu_read_lock(); -+ spin_lock(&mptcp_tk_hashlock); -+ hlist_nulls_del_init_rcu(&meta_tp->tk_table); -+ meta_tp->inside_tk_table = 0; -+ spin_unlock(&mptcp_tk_hashlock); -+ rcu_read_unlock(); -+} -+ -+static struct sock *mptcp_syn_recv_sock(struct sock *sk, struct sk_buff *skb, -+ struct request_sock *req, -+ struct dst_entry *dst) -+{ -+#if IS_ENABLED(CONFIG_IPV6) -+ if (sk->sk_family == AF_INET6) -+ return tcp_v6_syn_recv_sock(sk, skb, req, dst); -+ -+ /* sk->sk_family == AF_INET */ -+ if (req->rsk_ops->family == AF_INET6) -+ return mptcp_v6v4_syn_recv_sock(sk, skb, req, dst); -+#endif -+ -+ /* sk->sk_family == AF_INET && req->rsk_ops->family == AF_INET */ -+ return tcp_v4_syn_recv_sock(sk, skb, req, dst); -+} -+ -+struct sock *mptcp_select_ack_sock(const struct sock *meta_sk, int copied) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct sock *sk, *subsk = NULL; -+ u32 max_data_seq = 0; -+ /* max_data_seq initialized to correct compiler-warning. -+ * But the initialization is handled by max_data_seq_set -+ */ -+ short max_data_seq_set = 0; -+ u32 min_time = 0xffffffff; -+ -+ /* How do we select the subflow to send the window-update on? -+ * -+ * 1. He has to be in a state where he can send an ack and is -+ * operational (pf = 0). -+ * 2. He has to be one of those subflow who recently -+ * contributed to the received stream -+ * (this guarantees a working subflow) -+ * a) its latest data_seq received is after the original -+ * copied_seq. -+ * We select the one with the lowest rtt, so that the -+ * window-update reaches our peer the fastest. -+ * b) if no subflow has this kind of data_seq (e.g., very -+ * strange meta-level retransmissions going on), we take -+ * the subflow who last sent the highest data_seq. -+ */ -+ mptcp_for_each_sk(meta_tp->mpcb, sk) { -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ if (!mptcp_sk_can_send_ack(sk) || tp->pf) -+ continue; -+ -+ /* Select among those who contributed to the -+ * current receive-queue. -+ */ -+ if (copied && after(tp->mptcp->last_data_seq, meta_tp->copied_seq - copied)) { -+ if (tp->srtt < min_time) { -+ min_time = tp->srtt; -+ subsk = sk; -+ max_data_seq_set = 0; -+ } -+ continue; -+ } -+ -+ if (!subsk && !max_data_seq_set) { -+ max_data_seq = tp->mptcp->last_data_seq; -+ max_data_seq_set = 1; -+ subsk = sk; -+ } -+ -+ /* Otherwise, take the one with the highest data_seq */ -+ if ((!subsk || max_data_seq_set) && -+ after(tp->mptcp->last_data_seq, max_data_seq)) { -+ max_data_seq = tp->mptcp->last_data_seq; -+ subsk = sk; -+ } -+ } -+ -+ if (!subsk) { -+ mptcp_debug("%s subsk is null, copied %d, cseq %u\n", __func__, -+ copied, meta_tp->copied_seq); -+ mptcp_for_each_sk(meta_tp->mpcb, sk) { -+ struct tcp_sock *tp = tcp_sk(sk); -+ mptcp_debug("%s pi %d state %u last_dseq %u\n", -+ __func__, tp->mptcp->path_index, sk->sk_state, -+ tp->mptcp->last_data_seq); -+ } -+ } -+ -+ return subsk; -+} -+EXPORT_SYMBOL(mptcp_select_ack_sock); -+ -+static void mptcp_sock_def_error_report(struct sock *sk) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; -+ -+ if (!sock_flag(sk, SOCK_DEAD)) -+ mptcp_sub_close(sk, 0); -+ -+ if (mpcb->infinite_mapping_rcv || mpcb->infinite_mapping_snd || -+ mpcb->send_infinite_mapping) { -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ -+ meta_sk->sk_err = sk->sk_err; -+ meta_sk->sk_err_soft = sk->sk_err_soft; -+ -+ if (!sock_flag(meta_sk, SOCK_DEAD)) -+ meta_sk->sk_error_report(meta_sk); -+ -+ tcp_done(meta_sk); -+ } -+ -+ sk->sk_err = 0; -+ return; -+} -+ -+static void mptcp_mpcb_put(struct mptcp_cb *mpcb) -+{ -+ if (atomic_dec_and_test(&mpcb->mpcb_refcnt)) { -+ mptcp_cleanup_path_manager(mpcb); -+ kmem_cache_free(mptcp_cb_cache, mpcb); -+ } -+} -+ -+static void mptcp_sock_destruct(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ inet_sock_destruct(sk); -+ -+ BUG_ON(!list_empty(&tp->mptcp->cb_list)); -+ -+ kmem_cache_free(mptcp_sock_cache, tp->mptcp); -+ tp->mptcp = NULL; -+ -+ if (!is_meta_sk(sk) && !tp->was_meta_sk) { -+ /* Taken when mpcb pointer was set */ -+ sock_put(mptcp_meta_sk(sk)); -+ mptcp_mpcb_put(tp->mpcb); -+ } else { -+ struct mptcp_cb *mpcb = tp->mpcb; -+ struct mptcp_tw *mptw; -+ -+ /* The mpcb is disappearing - we can make the final -+ * update to the rcv_nxt of the time-wait-sock and remove -+ * its reference to the mpcb. -+ */ -+ spin_lock_bh(&mpcb->tw_lock); -+ list_for_each_entry_rcu(mptw, &mpcb->tw_list, list) { -+ list_del_rcu(&mptw->list); -+ mptw->in_list = 0; -+ mptcp_mpcb_put(mpcb); -+ rcu_assign_pointer(mptw->mpcb, NULL); -+ } -+ spin_unlock_bh(&mpcb->tw_lock); -+ -+ mptcp_mpcb_put(mpcb); -+ -+ mptcp_debug("%s destroying meta-sk\n", __func__); -+ } -+} -+ -+void mptcp_destroy_sock(struct sock *sk) -+{ -+ if (is_meta_sk(sk)) { -+ struct sock *sk_it, *tmpsk; -+ -+ __skb_queue_purge(&tcp_sk(sk)->mpcb->reinject_queue); -+ mptcp_purge_ofo_queue(tcp_sk(sk)); -+ -+ /* We have to close all remaining subflows. Normally, they -+ * should all be about to get closed. But, if the kernel is -+ * forcing a closure (e.g., tcp_write_err), the subflows might -+ * not have been closed properly (as we are waiting for the -+ * DATA_ACK of the DATA_FIN). -+ */ -+ mptcp_for_each_sk_safe(tcp_sk(sk)->mpcb, sk_it, tmpsk) { -+ /* Already did call tcp_close - waiting for graceful -+ * closure, or if we are retransmitting fast-close on -+ * the subflow. The reset (or timeout) will kill the -+ * subflow.. -+ */ -+ if (tcp_sk(sk_it)->closing || -+ tcp_sk(sk_it)->send_mp_fclose) -+ continue; -+ -+ /* Allow the delayed work first to prevent time-wait state */ -+ if (delayed_work_pending(&tcp_sk(sk_it)->mptcp->work)) -+ continue; -+ -+ mptcp_sub_close(sk_it, 0); -+ } -+ } else { -+ mptcp_del_sock(sk); -+ } -+} -+ -+static void mptcp_set_state(struct sock *sk) -+{ -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ -+ /* Meta is not yet established - wake up the application */ -+ if ((1 << meta_sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV) && -+ sk->sk_state == TCP_ESTABLISHED) { -+ tcp_set_state(meta_sk, TCP_ESTABLISHED); -+ -+ if (!sock_flag(meta_sk, SOCK_DEAD)) { -+ meta_sk->sk_state_change(meta_sk); -+ sk_wake_async(meta_sk, SOCK_WAKE_IO, POLL_OUT); -+ } -+ } -+ -+ if (sk->sk_state == TCP_ESTABLISHED) { -+ tcp_sk(sk)->mptcp->establish_increased = 1; -+ tcp_sk(sk)->mpcb->cnt_established++; -+ } -+} -+ -+u32 mptcp_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned; -+u32 mptcp_key_seed = 0; -+ -+void mptcp_key_sha1(u64 key, u32 *token, u64 *idsn) -+{ -+ u32 workspace[SHA_WORKSPACE_WORDS]; -+ u32 mptcp_hashed_key[SHA_DIGEST_WORDS]; -+ u8 input[64]; -+ int i; -+ -+ memset(workspace, 0, sizeof(workspace)); -+ -+ /* Initialize input with appropriate padding */ -+ memset(&input[9], 0, sizeof(input) - 10); /* -10, because the last byte -+ * is explicitly set too */ -+ memcpy(input, &key, sizeof(key)); /* Copy key to the msg beginning */ -+ input[8] = 0x80; /* Padding: First bit after message = 1 */ -+ input[63] = 0x40; /* Padding: Length of the message = 64 bits */ -+ -+ sha_init(mptcp_hashed_key); -+ sha_transform(mptcp_hashed_key, input, workspace); -+ -+ for (i = 0; i < 5; i++) -+ mptcp_hashed_key[i] = cpu_to_be32(mptcp_hashed_key[i]); -+ -+ if (token) -+ *token = mptcp_hashed_key[0]; -+ if (idsn) -+ *idsn = *((u64 *)&mptcp_hashed_key[3]); -+} -+ -+void mptcp_hmac_sha1(u8 *key_1, u8 *key_2, u8 *rand_1, u8 *rand_2, -+ u32 *hash_out) -+{ -+ u32 workspace[SHA_WORKSPACE_WORDS]; -+ u8 input[128]; /* 2 512-bit blocks */ -+ int i; -+ -+ memset(workspace, 0, sizeof(workspace)); -+ -+ /* Generate key xored with ipad */ -+ memset(input, 0x36, 64); -+ for (i = 0; i < 8; i++) -+ input[i] ^= key_1[i]; -+ for (i = 0; i < 8; i++) -+ input[i + 8] ^= key_2[i]; -+ -+ memcpy(&input[64], rand_1, 4); -+ memcpy(&input[68], rand_2, 4); -+ input[72] = 0x80; /* Padding: First bit after message = 1 */ -+ memset(&input[73], 0, 53); -+ -+ /* Padding: Length of the message = 512 + 64 bits */ -+ input[126] = 0x02; -+ input[127] = 0x40; -+ -+ sha_init(hash_out); -+ sha_transform(hash_out, input, workspace); -+ memset(workspace, 0, sizeof(workspace)); -+ -+ sha_transform(hash_out, &input[64], workspace); -+ memset(workspace, 0, sizeof(workspace)); -+ -+ for (i = 0; i < 5; i++) -+ hash_out[i] = cpu_to_be32(hash_out[i]); -+ -+ /* Prepare second part of hmac */ -+ memset(input, 0x5C, 64); -+ for (i = 0; i < 8; i++) -+ input[i] ^= key_1[i]; -+ for (i = 0; i < 8; i++) -+ input[i + 8] ^= key_2[i]; -+ -+ memcpy(&input[64], hash_out, 20); -+ input[84] = 0x80; -+ memset(&input[85], 0, 41); -+ -+ /* Padding: Length of the message = 512 + 160 bits */ -+ input[126] = 0x02; -+ input[127] = 0xA0; -+ -+ sha_init(hash_out); -+ sha_transform(hash_out, input, workspace); -+ memset(workspace, 0, sizeof(workspace)); -+ -+ sha_transform(hash_out, &input[64], workspace); -+ -+ for (i = 0; i < 5; i++) -+ hash_out[i] = cpu_to_be32(hash_out[i]); -+} -+ -+static void mptcp_mpcb_inherit_sockopts(struct sock *meta_sk, struct sock *master_sk) -+{ -+ /* Socket-options handled by mptcp_inherit_sk while creating the meta-sk. -+ * ====== -+ * SO_SNDBUF, SO_SNDBUFFORCE, SO_RCVBUF, SO_RCVBUFFORCE, SO_RCVLOWAT, -+ * SO_RCVTIMEO, SO_SNDTIMEO, SO_ATTACH_FILTER, SO_DETACH_FILTER, -+ * TCP_NODELAY, TCP_CORK -+ * -+ * Socket-options handled in this function here -+ * ====== -+ * TCP_DEFER_ACCEPT -+ * -+ * Socket-options on the todo-list -+ * ====== -+ * SO_BINDTODEVICE - should probably prevent creation of new subsocks -+ * across other devices. - what about the api-draft? -+ * SO_DEBUG -+ * SO_REUSEADDR - probably we don't care about this -+ * SO_DONTROUTE, SO_BROADCAST -+ * SO_OOBINLINE -+ * SO_LINGER -+ * SO_TIMESTAMP* - I don't think this is of concern for a SOCK_STREAM -+ * SO_PASSSEC - I don't think this is of concern for a SOCK_STREAM -+ * SO_RXQ_OVFL -+ * TCP_COOKIE_TRANSACTIONS -+ * TCP_MAXSEG -+ * TCP_THIN_* - Handled by mptcp_inherit_sk, but we need to support this -+ * in mptcp_retransmit_timer. AND we need to check what is -+ * about the subsockets. -+ * TCP_LINGER2 -+ * TCP_WINDOW_CLAMP -+ * TCP_USER_TIMEOUT -+ * TCP_MD5SIG -+ * -+ * Socket-options of no concern for the meta-socket (but for the subsocket) -+ * ====== -+ * SO_PRIORITY -+ * SO_MARK -+ * TCP_CONGESTION -+ * TCP_SYNCNT -+ * TCP_QUICKACK -+ * SO_KEEPALIVE -+ */ -+ -+ /****** DEFER_ACCEPT-handler ******/ -+ -+ /* DEFER_ACCEPT is not of concern for new subflows - we always accept -+ * them -+ */ -+ inet_csk(meta_sk)->icsk_accept_queue.rskq_defer_accept = 0; -+} -+ -+static void mptcp_sub_inherit_sockopts(struct sock *meta_sk, struct sock *sub_sk) -+{ -+ /* IP_TOS also goes to the subflow. */ -+ if (inet_sk(sub_sk)->tos != inet_sk(meta_sk)->tos) { -+ inet_sk(sub_sk)->tos = inet_sk(meta_sk)->tos; -+ sub_sk->sk_priority = meta_sk->sk_priority; -+ sk_dst_reset(sub_sk); -+ } -+ -+ /* Inherit SO_REUSEADDR */ -+ sub_sk->sk_reuse = meta_sk->sk_reuse; -+ -+ /* Inherit snd/rcv-buffer locks */ -+ sub_sk->sk_userlocks = meta_sk->sk_userlocks & ~SOCK_BINDPORT_LOCK; -+} -+ -+int mptcp_backlog_rcv(struct sock *meta_sk, struct sk_buff *skb) -+{ -+ /* skb-sk may be NULL if we receive a packet immediatly after the -+ * SYN/ACK + MP_CAPABLE. -+ */ -+ struct sock *sk = skb->sk ? skb->sk : meta_sk; -+ int ret = 0; -+ -+ skb->sk = NULL; -+ -+ if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) { -+ kfree_skb(skb); -+ return 0; -+ } -+ -+ if (sk->sk_family == AF_INET) -+ ret = tcp_v4_do_rcv(sk, skb); -+#if IS_ENABLED(CONFIG_IPV6) -+ else -+ ret = tcp_v6_do_rcv(sk, skb); -+#endif -+ -+ sock_put(sk); -+ return ret; -+} -+ -+struct lock_class_key meta_key; -+struct lock_class_key meta_slock_key; -+ -+/* Code heavily inspired from sk_clone() */ -+static int mptcp_inherit_sk(const struct sock *sk, struct sock *newsk, -+ int family, const gfp_t flags) -+{ -+ struct sk_filter *filter; -+ struct proto *prot = newsk->sk_prot; -+ const struct inet_connection_sock_af_ops *af_ops = inet_csk(newsk)->icsk_af_ops; -+#ifdef CONFIG_SECURITY_NETWORK -+ void *sptr = newsk->sk_security; -+#endif -+ -+ if (sk->sk_family == AF_INET) { -+ memcpy(newsk, sk, offsetof(struct sock, sk_dontcopy_begin)); -+ memcpy(&newsk->sk_dontcopy_end, &sk->sk_dontcopy_end, -+ sizeof(struct tcp_sock) - offsetof(struct sock, sk_dontcopy_end)); -+ } else { -+ memcpy(newsk, sk, offsetof(struct sock, sk_dontcopy_begin)); -+ memcpy(&newsk->sk_dontcopy_end, &sk->sk_dontcopy_end, -+ sizeof(struct tcp6_sock) - offsetof(struct sock, sk_dontcopy_end)); -+ } -+ -+#ifdef CONFIG_SECURITY_NETWORK -+ newsk->sk_security = sptr; -+ security_sk_clone(sk, newsk); -+#endif -+ -+ /* Has been changed by sock_copy above - we may need an IPv6-socket */ -+ newsk->sk_family = family; -+ newsk->sk_prot = prot; -+ newsk->sk_prot_creator = prot; -+ inet_csk(newsk)->icsk_af_ops = af_ops; -+ -+ /* We don't yet have the mptcp-point. Thus we still need inet_sock_destruct */ -+ newsk->sk_destruct = inet_sock_destruct; -+ -+ /* SANITY */ -+ get_net(sock_net(newsk)); -+ sk_node_init(&newsk->sk_node); -+ sock_lock_init_class_and_name(newsk, "slock-AF_INET-MPTCP", -+ &meta_slock_key, "sk_lock-AF_INET-MPTCP", -+ &meta_key); -+ -+ /* Unlocks are in: -+ * -+ * 1. If we are creating the master-sk -+ * * on client-side in tcp_rcv_state_process, "case TCP_SYN_SENT" -+ * * on server-side in tcp_child_process -+ * 2. If we are creating another subsock -+ * * Also in tcp_child_process -+ */ -+ bh_lock_sock(newsk); -+ newsk->sk_backlog.head = NULL; -+ newsk->sk_backlog.tail = NULL; -+ newsk->sk_backlog.len = 0; -+ -+ atomic_set(&newsk->sk_rmem_alloc, 0); -+ atomic_set(&newsk->sk_wmem_alloc, 1); -+ atomic_set(&newsk->sk_omem_alloc, 0); -+ -+ skb_queue_head_init(&newsk->sk_receive_queue); -+ skb_queue_head_init(&newsk->sk_write_queue); -+#ifdef CONFIG_NET_DMA -+ skb_queue_head_init(&newsk->sk_async_wait_queue); -+#endif -+ -+ spin_lock_init(&newsk->sk_dst_lock); -+ rwlock_init(&newsk->sk_callback_lock); -+ lockdep_set_class_and_name(&newsk->sk_callback_lock, -+ af_callback_keys + newsk->sk_family, -+ af_family_clock_key_strings[newsk->sk_family]); -+ newsk->sk_dst_cache = NULL; -+ newsk->sk_rx_dst = NULL; -+ newsk->sk_wmem_queued = 0; -+ newsk->sk_forward_alloc = 0; -+ newsk->sk_send_head = NULL; -+ newsk->sk_userlocks = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK; -+ -+ tcp_sk(newsk)->mptcp = NULL; -+ -+ sock_reset_flag(newsk, SOCK_DONE); -+ skb_queue_head_init(&newsk->sk_error_queue); -+ -+ filter = rcu_dereference_protected(newsk->sk_filter, 1); -+ if (filter != NULL) -+ sk_filter_charge(newsk, filter); -+ -+ if (unlikely(xfrm_sk_clone_policy(newsk))) { -+ /* It is still raw copy of parent, so invalidate -+ * destructor and make plain sk_free() -+ */ -+ newsk->sk_destruct = NULL; -+ bh_unlock_sock(newsk); -+ sk_free(newsk); -+ newsk = NULL; -+ return -ENOMEM; -+ } -+ -+ newsk->sk_err = 0; -+ newsk->sk_priority = 0; -+ /* Before updating sk_refcnt, we must commit prior changes to memory -+ * (Documentation/RCU/rculist_nulls.txt for details) -+ */ -+ smp_wmb(); -+ atomic_set(&newsk->sk_refcnt, 2); -+ -+ /* Increment the counter in the same struct proto as the master -+ * sock (sk_refcnt_debug_inc uses newsk->sk_prot->socks, that -+ * is the same as sk->sk_prot->socks, as this field was copied -+ * with memcpy). -+ * -+ * This _changes_ the previous behaviour, where -+ * tcp_create_openreq_child always was incrementing the -+ * equivalent to tcp_prot->socks (inet_sock_nr), so this have -+ * to be taken into account in all callers. -acme -+ */ -+ sk_refcnt_debug_inc(newsk); -+ sk_set_socket(newsk, NULL); -+ newsk->sk_wq = NULL; -+ -+ if (newsk->sk_prot->sockets_allocated) -+ percpu_counter_inc(newsk->sk_prot->sockets_allocated); -+ -+ if (sock_flag(newsk, SOCK_TIMESTAMP) || -+ sock_flag(newsk, SOCK_TIMESTAMPING_RX_SOFTWARE)) -+ net_enable_timestamp(); -+ -+ return 0; -+} -+ -+int mptcp_alloc_mpcb(struct sock *meta_sk, __u64 remote_key, u32 window) -+{ -+ struct mptcp_cb *mpcb; -+ struct sock *master_sk; -+ struct inet_connection_sock *master_icsk, *meta_icsk = inet_csk(meta_sk); -+ struct tcp_sock *master_tp, *meta_tp = tcp_sk(meta_sk); -+ struct sk_buff *skb, *tmp; -+ u64 idsn; -+ -+ master_sk = sk_prot_alloc(meta_sk->sk_prot, GFP_ATOMIC | __GFP_ZERO, -+ meta_sk->sk_family); -+ if (!master_sk) -+ return -ENOBUFS; -+ -+ master_tp = tcp_sk(master_sk); -+ master_icsk = inet_csk(master_sk); -+ -+ /* Need to set this here - it is needed by mptcp_inherit_sk */ -+ master_sk->sk_prot = meta_sk->sk_prot; -+ master_sk->sk_prot_creator = meta_sk->sk_prot; -+ master_icsk->icsk_af_ops = meta_icsk->icsk_af_ops; -+ -+ mpcb = kmem_cache_zalloc(mptcp_cb_cache, GFP_ATOMIC); -+ if (!mpcb) { -+ sk_free(master_sk); -+ return -ENOBUFS; -+ } -+ -+ /* master_sk inherits from meta_sk */ -+ if (mptcp_inherit_sk(meta_sk, master_sk, meta_sk->sk_family, GFP_ATOMIC)) { -+ kmem_cache_free(mptcp_cb_cache, mpcb); -+ return -ENOBUFS; -+ } -+ -+#if IS_ENABLED(CONFIG_IPV6) -+ if (meta_icsk->icsk_af_ops == &ipv6_mapped) { -+ struct ipv6_pinfo *newnp, *np = inet6_sk(meta_sk); -+ -+ inet_sk(master_sk)->pinet6 = &((struct tcp6_sock *)master_sk)->inet6; -+ -+ newnp = inet6_sk(master_sk); -+ memcpy(newnp, np, sizeof(struct ipv6_pinfo)); -+ -+ newnp->ipv6_mc_list = NULL; -+ newnp->ipv6_ac_list = NULL; -+ newnp->ipv6_fl_list = NULL; -+ newnp->opt = NULL; -+ newnp->pktoptions = NULL; -+ (void)xchg(&newnp->rxpmtu, NULL); -+ } else if (meta_sk->sk_family == AF_INET6) { -+ struct ipv6_pinfo *newnp, *np = inet6_sk(meta_sk); -+ -+ inet_sk(master_sk)->pinet6 = &((struct tcp6_sock *)master_sk)->inet6; -+ -+ newnp = inet6_sk(master_sk); -+ memcpy(newnp, np, sizeof(struct ipv6_pinfo)); -+ -+ newnp->hop_limit = -1; -+ newnp->mcast_hops = IPV6_DEFAULT_MCASTHOPS; -+ newnp->mc_loop = 1; -+ newnp->pmtudisc = IPV6_PMTUDISC_WANT; -+ newnp->ipv6only = sock_net(master_sk)->ipv6.sysctl.bindv6only; -+ } -+#endif -+ -+ meta_tp->mptcp = kmem_cache_zalloc(mptcp_sock_cache, GFP_ATOMIC); -+ if (!meta_tp->mptcp) { -+ kmem_cache_free(mptcp_cb_cache, mpcb); -+ sk_free(master_sk); -+ return -ENOBUFS; -+ } -+ -+ INIT_LIST_HEAD(&meta_tp->mptcp->cb_list); -+ -+ /* Store the keys and generate the peer's token */ -+ mpcb->mptcp_loc_key = meta_tp->mptcp_loc_key; -+ mpcb->mptcp_loc_token = meta_tp->mptcp_loc_token; -+ -+ /* Generate Initial data-sequence-numbers */ -+ mptcp_key_sha1(mpcb->mptcp_loc_key, NULL, &idsn); -+ idsn = ntohll(idsn) + 1; -+ mpcb->snd_high_order[0] = idsn >> 32; -+ mpcb->snd_high_order[1] = mpcb->snd_high_order[0] - 1; -+ -+ meta_tp->write_seq = (u32)idsn; -+ meta_tp->snd_sml = meta_tp->write_seq; -+ meta_tp->snd_una = meta_tp->write_seq; -+ meta_tp->snd_nxt = meta_tp->write_seq; -+ meta_tp->pushed_seq = meta_tp->write_seq; -+ meta_tp->snd_up = meta_tp->write_seq; -+ -+ mpcb->mptcp_rem_key = remote_key; -+ mptcp_key_sha1(mpcb->mptcp_rem_key, &mpcb->mptcp_rem_token, &idsn); -+ idsn = ntohll(idsn) + 1; -+ mpcb->rcv_high_order[0] = idsn >> 32; -+ mpcb->rcv_high_order[1] = mpcb->rcv_high_order[0] + 1; -+ meta_tp->copied_seq = (u32) idsn; -+ meta_tp->rcv_nxt = (u32) idsn; -+ meta_tp->rcv_wup = (u32) idsn; -+ -+ meta_tp->snd_wl1 = meta_tp->rcv_nxt - 1; -+ meta_tp->snd_wnd = window; -+ meta_tp->retrans_stamp = 0; /* Set in tcp_connect() */ -+ -+ meta_tp->packets_out = 0; -+ meta_tp->mptcp->snt_isn = meta_tp->write_seq; /* Initial data-sequence-number */ -+ meta_icsk->icsk_probes_out = 0; -+ -+ /* Set mptcp-pointers */ -+ master_tp->mpcb = mpcb; -+ master_tp->meta_sk = meta_sk; -+ meta_tp->mpcb = mpcb; -+ meta_tp->meta_sk = meta_sk; -+ mpcb->meta_sk = meta_sk; -+ mpcb->master_sk = master_sk; -+ -+ set_mpc(meta_tp); -+ meta_tp->mptcp->attached = 0; -+ meta_tp->was_meta_sk = 0; -+ -+ /* Initialize the queues */ -+ skb_queue_head_init(&mpcb->reinject_queue); -+ skb_queue_head_init(&master_tp->out_of_order_queue); -+ tcp_prequeue_init(master_tp); -+ INIT_LIST_HEAD(&master_tp->tsq_node); -+ -+ master_tp->tsq_flags = 0; -+ -+ /* Copy the write-queue from the meta down to the master. -+ * This is necessary to get the SYN to the master-write-queue. -+ * No other data can be queued, before tcp_sendmsg waits for the -+ * connection to finish. -+ */ -+ skb_queue_walk_safe(&meta_sk->sk_write_queue, skb, tmp) { -+ skb_unlink(skb, &meta_sk->sk_write_queue); -+ skb_queue_tail(&master_sk->sk_write_queue, skb); -+ -+ master_sk->sk_wmem_queued += skb->truesize; -+ sk_mem_charge(master_sk, skb->truesize); -+ } -+ -+ meta_sk->sk_wmem_queued = 0; -+ meta_sk->sk_forward_alloc = 0; -+ -+ mutex_init(&mpcb->mpcb_mutex); -+ -+ /* Init the accept_queue structure, we support a queue of 32 pending -+ * connections, it does not need to be huge, since we only store here -+ * pending subflow creations. -+ */ -+ if (reqsk_queue_alloc(&meta_icsk->icsk_accept_queue, 32, GFP_ATOMIC)) { -+ inet_put_port(master_sk); -+ kmem_cache_free(mptcp_sock_cache, meta_tp->mptcp); -+ kmem_cache_free(mptcp_cb_cache, mpcb); -+ sk_free(master_sk); -+ reset_mpc(meta_tp); -+ return -ENOMEM; -+ } -+ -+ /* Redefine function-pointers as the meta-sk is now fully ready */ -+ meta_sk->sk_backlog_rcv = mptcp_backlog_rcv; -+ meta_sk->sk_destruct = mptcp_sock_destruct; -+ mpcb->syn_recv_sock = mptcp_syn_recv_sock; -+ -+ /* Meta-level retransmit timer */ -+ meta_icsk->icsk_rto *= 2; /* Double of initial - rto */ -+ -+ tcp_init_xmit_timers(master_sk); -+ /* Has been set for sending out the SYN */ -+ inet_csk_clear_xmit_timer(meta_sk, ICSK_TIME_RETRANS); -+ -+ if (!meta_tp->inside_tk_table) { -+ /* Adding the meta_tp in the token hashtable - coming from server-side */ -+ rcu_read_lock(); -+ spin_lock(&mptcp_tk_hashlock); -+ -+ __mptcp_hash_insert(meta_tp, mpcb->mptcp_loc_token); -+ -+ spin_unlock(&mptcp_tk_hashlock); -+ rcu_read_unlock(); -+ } -+ master_tp->inside_tk_table = 0; -+ -+ /* Init time-wait stuff */ -+ INIT_LIST_HEAD(&mpcb->tw_list); -+ spin_lock_init(&mpcb->tw_lock); -+ -+ INIT_LIST_HEAD(&mpcb->callback_list); -+ -+ mptcp_mpcb_inherit_sockopts(meta_sk, master_sk); -+ -+ mpcb->orig_sk_rcvbuf = meta_sk->sk_rcvbuf; -+ mpcb->orig_sk_sndbuf = meta_sk->sk_sndbuf; -+ mpcb->orig_window_clamp = meta_tp->window_clamp; -+ -+ /* The meta is directly linked - set refcnt to 1 */ -+ atomic_set(&mpcb->mpcb_refcnt, 1); -+ -+ mptcp_init_path_manager(mpcb); -+ -+ mptcp_debug("%s: created mpcb with token %#x\n", -+ __func__, mpcb->mptcp_loc_token); -+ -+ return 0; -+} -+ -+struct sock *mptcp_sk_clone(const struct sock *sk, int family, -+ const gfp_t priority) -+{ -+ struct sock *newsk = NULL; -+ -+ if (family == AF_INET && sk->sk_family == AF_INET) { -+ newsk = sk_prot_alloc(&tcp_prot, priority, family); -+ if (!newsk) -+ return NULL; -+ -+ /* Set these pointers - they are needed by mptcp_inherit_sk */ -+ newsk->sk_prot = &tcp_prot; -+ newsk->sk_prot_creator = &tcp_prot; -+ inet_csk(newsk)->icsk_af_ops = &ipv4_specific; -+ newsk->sk_family = AF_INET; -+ } -+#if IS_ENABLED(CONFIG_IPV6) -+ else { -+ newsk = sk_prot_alloc(&tcpv6_prot, priority, family); -+ if (!newsk) -+ return NULL; -+ -+ newsk->sk_prot = &tcpv6_prot; -+ newsk->sk_prot_creator = &tcpv6_prot; -+ if (family == AF_INET) -+ inet_csk(newsk)->icsk_af_ops = &ipv6_mapped; -+ else -+ inet_csk(newsk)->icsk_af_ops = &ipv6_specific; -+ newsk->sk_family = AF_INET6; -+ } -+#endif -+ -+ if (mptcp_inherit_sk(sk, newsk, family, priority)) -+ return NULL; -+ -+ return newsk; -+} -+ -+void mptcp_fallback_meta_sk(struct sock *meta_sk) -+{ -+ kfree(inet_csk(meta_sk)->icsk_accept_queue.listen_opt); -+ kmem_cache_free(mptcp_sock_cache, tcp_sk(meta_sk)->mptcp); -+ kmem_cache_free(mptcp_cb_cache, tcp_sk(meta_sk)->mpcb); -+} -+ -+int mptcp_add_sock(struct sock *meta_sk, struct sock *sk, u8 loc_id, u8 rem_id, -+ gfp_t flags) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ tp->mptcp = kmem_cache_zalloc(mptcp_sock_cache, flags); -+ if (!tp->mptcp) -+ return -ENOMEM; -+ -+ tp->mptcp->path_index = mptcp_set_new_pathindex(mpcb); -+ /* No more space for more subflows? */ -+ if (!tp->mptcp->path_index) { -+ kmem_cache_free(mptcp_sock_cache, tp->mptcp); -+ return -EPERM; -+ } -+ -+ INIT_LIST_HEAD(&tp->mptcp->cb_list); -+ -+ tp->mptcp->tp = tp; -+ tp->mpcb = mpcb; -+ tp->meta_sk = meta_sk; -+ set_mpc(tp); -+ tp->mptcp->loc_id = loc_id; -+ tp->mptcp->rem_id = rem_id; -+ tp->mptcp->last_rbuf_opti = tcp_time_stamp; -+ -+ /* The corresponding sock_put is in mptcp_sock_destruct(). It cannot be -+ * included in mptcp_del_sock(), because the mpcb must remain alive -+ * until the last subsocket is completely destroyed. -+ */ -+ sock_hold(meta_sk); -+ atomic_inc(&mpcb->mpcb_refcnt); -+ -+ tp->mptcp->next = mpcb->connection_list; -+ mpcb->connection_list = tp; -+ tp->mptcp->attached = 1; -+ -+ mpcb->cnt_subflows++; -+ atomic_add(atomic_read(&((struct sock *)tp)->sk_rmem_alloc), -+ &meta_sk->sk_rmem_alloc); -+ -+ mptcp_sub_inherit_sockopts(meta_sk, sk); -+ INIT_DELAYED_WORK(&tp->mptcp->work, mptcp_sub_close_wq); -+ -+ /* As we successfully allocated the mptcp_tcp_sock, we have to -+ * change the function-pointers here (for sk_destruct to work correctly) -+ */ -+ sk->sk_error_report = mptcp_sock_def_error_report; -+ sk->sk_data_ready = mptcp_data_ready; -+ sk->sk_write_space = mptcp_write_space; -+ sk->sk_state_change = mptcp_set_state; -+ sk->sk_destruct = mptcp_sock_destruct; -+ -+ if (sk->sk_family == AF_INET) -+ mptcp_debug("%s: token %#x pi %d, src_addr:%pI4:%d dst_addr:%pI4:%d, cnt_subflows now %d\n", -+ __func__ , mpcb->mptcp_loc_token, -+ tp->mptcp->path_index, -+ &((struct inet_sock *)tp)->inet_saddr, -+ ntohs(((struct inet_sock *)tp)->inet_sport), -+ &((struct inet_sock *)tp)->inet_daddr, -+ ntohs(((struct inet_sock *)tp)->inet_dport), -+ mpcb->cnt_subflows); -+#if IS_ENABLED(CONFIG_IPV6) -+ else -+ mptcp_debug("%s: token %#x pi %d, src_addr:%pI6:%d dst_addr:%pI6:%d, cnt_subflows now %d\n", -+ __func__ , mpcb->mptcp_loc_token, -+ tp->mptcp->path_index, &inet6_sk(sk)->saddr, -+ ntohs(((struct inet_sock *)tp)->inet_sport), -+ &sk->sk_v6_daddr, -+ ntohs(((struct inet_sock *)tp)->inet_dport), -+ mpcb->cnt_subflows); -+#endif -+ -+ return 0; -+} -+ -+void mptcp_del_sock(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk), *tp_prev; -+ struct mptcp_cb *mpcb; -+ -+ if (!tp->mptcp || !tp->mptcp->attached) -+ return; -+ -+ mpcb = tp->mpcb; -+ tp_prev = mpcb->connection_list; -+ -+ mptcp_debug("%s: Removing subsock tok %#x pi:%d state %d is_meta? %d\n", -+ __func__, mpcb->mptcp_loc_token, tp->mptcp->path_index, -+ sk->sk_state, is_meta_sk(sk)); -+ -+ if (tp_prev == tp) { -+ mpcb->connection_list = tp->mptcp->next; -+ } else { -+ for (; tp_prev && tp_prev->mptcp->next; tp_prev = tp_prev->mptcp->next) { -+ if (tp_prev->mptcp->next == tp) { -+ tp_prev->mptcp->next = tp->mptcp->next; -+ break; -+ } -+ } -+ } -+ mpcb->cnt_subflows--; -+ if (tp->mptcp->establish_increased) -+ mpcb->cnt_established--; -+ -+ tp->mptcp->next = NULL; -+ tp->mptcp->attached = 0; -+ mpcb->path_index_bits &= ~(1 << tp->mptcp->path_index); -+ -+ if (!skb_queue_empty(&sk->sk_write_queue)) -+ mptcp_reinject_data(sk, 0); -+ -+ if (is_master_tp(tp)) -+ mpcb->master_sk = NULL; -+ else if (tp->mptcp->pre_established) -+ sk_stop_timer(sk, &tp->mptcp->mptcp_ack_timer); -+ -+ rcu_assign_pointer(inet_sk(sk)->inet_opt, NULL); -+} -+ -+/* Updates the metasocket ULID/port data, based on the given sock. -+ * The argument sock must be the sock accessible to the application. -+ * In this function, we update the meta socket info, based on the changes -+ * in the application socket (bind, address allocation, ...) -+ */ -+void mptcp_update_metasocket(struct sock *sk, struct sock *meta_sk) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ union inet_addr addr; -+ int index; -+ -+ /* Get the index of the local address */ -+ if (sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(sk)) { -+ addr.ip = inet_sk(sk)->inet_saddr; -+ index = mpcb->pm_ops->get_local_index(AF_INET, &addr, sock_net(meta_sk)); -+ } else { -+ addr.in6 = inet6_sk(sk)->saddr; -+ index = mpcb->pm_ops->get_local_index(AF_INET6, &addr, sock_net(meta_sk)); -+ } -+ -+ if (sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(sk)) { -+ mptcp_v4_add_raddress(mpcb, -+ (struct in_addr *)&inet_sk(sk)->inet_daddr, -+ 0, 0); -+ if (index >= 0) -+ mptcp_v4_set_init_addr_bit(mpcb, inet_sk(sk)->inet_daddr, index); -+ } else { -+#if IS_ENABLED(CONFIG_IPV6) -+ mptcp_v6_add_raddress(mpcb, &sk->sk_v6_daddr, 0, 0); -+ if (index >= 0) -+ mptcp_v6_set_init_addr_bit(mpcb, &sk->sk_v6_daddr, index); -+#endif -+ } -+ -+ if (mpcb->pm_ops->new_session) -+ mpcb->pm_ops->new_session(meta_sk, index); -+ -+ tcp_sk(sk)->mptcp->send_mp_prio = tcp_sk(sk)->mptcp->low_prio; -+} -+ -+/* Clean up the receive buffer for full frames taken by the user, -+ * then send an ACK if necessary. COPIED is the number of bytes -+ * tcp_recvmsg has given to the user so far, it speeds up the -+ * calculation of whether or not we must ACK for the sake of -+ * a window update. -+ */ -+void mptcp_cleanup_rbuf(struct sock *meta_sk, int copied) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct sock *sk; -+ __u32 rcv_window_now = 0; -+ -+ if (copied > 0 && !(meta_sk->sk_shutdown & RCV_SHUTDOWN)) { -+ rcv_window_now = tcp_receive_window(meta_tp); -+ -+ if (2 * rcv_window_now > meta_tp->window_clamp) -+ rcv_window_now = 0; -+ } -+ -+ mptcp_for_each_sk(meta_tp->mpcb, sk) { -+ struct tcp_sock *tp = tcp_sk(sk); -+ const struct inet_connection_sock *icsk = inet_csk(sk); -+ -+ if (!mptcp_sk_can_send_ack(sk)) -+ continue; -+ -+ if (!inet_csk_ack_scheduled(sk)) -+ goto second_part; -+ /* Delayed ACKs frequently hit locked sockets during bulk -+ * receive. -+ */ -+ if (icsk->icsk_ack.blocked || -+ /* Once-per-two-segments ACK was not sent by tcp_input.c */ -+ tp->rcv_nxt - tp->rcv_wup > icsk->icsk_ack.rcv_mss || -+ /* If this read emptied read buffer, we send ACK, if -+ * connection is not bidirectional, user drained -+ * receive buffer and there was a small segment -+ * in queue. -+ */ -+ (copied > 0 && -+ ((icsk->icsk_ack.pending & ICSK_ACK_PUSHED2) || -+ ((icsk->icsk_ack.pending & ICSK_ACK_PUSHED) && -+ !icsk->icsk_ack.pingpong)) && -+ !atomic_read(&meta_sk->sk_rmem_alloc))) { -+ tcp_send_ack(sk); -+ continue; -+ } -+ -+second_part: -+ /* This here is the second part of tcp_cleanup_rbuf */ -+ if (rcv_window_now) { -+ __u32 new_window = tp->__select_window(sk); -+ -+ /* Send ACK now, if this read freed lots of space -+ * in our buffer. Certainly, new_window is new window. -+ * We can advertise it now, if it is not less than -+ * current one. -+ * "Lots" means "at least twice" here. -+ */ -+ if (new_window && new_window >= 2 * rcv_window_now) -+ tcp_send_ack(sk); -+ } -+ } -+} -+ -+static int mptcp_sub_send_fin(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct sk_buff *skb = tcp_write_queue_tail(sk); -+ int mss_now; -+ -+ /* Optimization, tack on the FIN if we have a queue of -+ * unsent frames. But be careful about outgoing SACKS -+ * and IP options. -+ */ -+ mss_now = tcp_current_mss(sk); -+ -+ if (tcp_send_head(sk) != NULL) { -+ TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN; -+ TCP_SKB_CB(skb)->end_seq++; -+ tp->write_seq++; -+ } else { -+ skb = alloc_skb_fclone(MAX_TCP_HEADER, GFP_ATOMIC); -+ if (!skb) -+ return 1; -+ -+ /* Reserve space for headers and prepare control bits. */ -+ skb_reserve(skb, MAX_TCP_HEADER); -+ /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */ -+ tcp_init_nondata_skb(skb, tp->write_seq, -+ TCPHDR_ACK | TCPHDR_FIN); -+ tcp_queue_skb(sk, skb); -+ } -+ __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF); -+ -+ return 0; -+} -+ -+void mptcp_sub_close_wq(struct work_struct *work) -+{ -+ struct mptcp_tcp_sock *mptcp = container_of(work, struct mptcp_tcp_sock, work.work); -+ struct tcp_sock *tp = mptcp->tp; -+ struct sock *sk = (struct sock *)tp; -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ -+ mutex_lock(&tp->mpcb->mpcb_mutex); -+ lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); -+ -+ if (sock_flag(sk, SOCK_DEAD)) -+ goto exit; -+ -+ /* We come from tcp_disconnect. We are sure that meta_sk is set */ -+ if (!tp->mpc) { -+ tp->closing = 1; -+ sock_rps_reset_flow(sk); -+ tcp_close(sk, 0); -+ goto exit; -+ } -+ -+ if (meta_sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == TCP_CLOSE) { -+ tp->closing = 1; -+ sock_rps_reset_flow(sk); -+ tcp_close(sk, 0); -+ } else if (tcp_close_state(sk)) { -+ sk->sk_shutdown |= SEND_SHUTDOWN; -+ tcp_send_fin(sk); -+ } -+ -+exit: -+ release_sock(meta_sk); -+ mutex_unlock(&tp->mpcb->mpcb_mutex); -+ sock_put(sk); -+} -+ -+void mptcp_sub_close(struct sock *sk, unsigned long delay) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct delayed_work *work = &tcp_sk(sk)->mptcp->work; -+ -+ /* We are already closing - e.g., call from sock_def_error_report upon -+ * tcp_disconnect in tcp_close. -+ */ -+ if (tp->closing) -+ return; -+ -+ /* Work already scheduled ? */ -+ if (work_pending(&work->work)) { -+ /* Work present - who will be first ? */ -+ if (jiffies + delay > work->timer.expires) -+ return; -+ -+ /* Try canceling - if it fails, work will be executed soon */ -+ if (!cancel_delayed_work(work)) -+ return; -+ sock_put(sk); -+ } -+ -+ if (!delay) { -+ unsigned char old_state = sk->sk_state; -+ -+ /* If we are in user-context we can directly do the closing -+ * procedure. No need to schedule a work-queue. -+ */ -+ if (!in_softirq()) { -+ if (sock_flag(sk, SOCK_DEAD)) -+ return; -+ -+ if (!tp->mpc) { -+ tp->closing = 1; -+ sock_rps_reset_flow(sk); -+ tcp_close(sk, 0); -+ return; -+ } -+ -+ if (mptcp_meta_sk(sk)->sk_shutdown == SHUTDOWN_MASK || -+ sk->sk_state == TCP_CLOSE) { -+ tp->closing = 1; -+ sock_rps_reset_flow(sk); -+ tcp_close(sk, 0); -+ } else if (tcp_close_state(sk)) { -+ sk->sk_shutdown |= SEND_SHUTDOWN; -+ tcp_send_fin(sk); -+ } -+ -+ return; -+ } -+ -+ /* We directly send the FIN. Because it may take so a long time, -+ * untile the work-queue will get scheduled... -+ * -+ * If mptcp_sub_send_fin returns 1, it failed and thus we reset -+ * the old state so that tcp_close will finally send the fin -+ * in user-context. -+ */ -+ if (!sk->sk_err && old_state != TCP_CLOSE && -+ tcp_close_state(sk) && mptcp_sub_send_fin(sk)) { -+ if (old_state == TCP_ESTABLISHED) -+ TCP_INC_STATS(sock_net(sk), TCP_MIB_CURRESTAB); -+ sk->sk_state = old_state; -+ } -+ } -+ -+ sock_hold(sk); -+ queue_delayed_work(mptcp_wq, work, delay); -+} -+ -+void mptcp_sub_force_close(struct sock *sk) -+{ -+ /* The below tcp_done may have freed the socket, if he is already dead. -+ * Thus, we are not allowed to access it afterwards. That's why -+ * we have to store the dead-state in this local variable. -+ */ -+ int sock_is_dead = sock_flag(sk, SOCK_DEAD); -+ -+ tcp_sk(sk)->mp_killed = 1; -+ -+ if (sk->sk_state != TCP_CLOSE) -+ tcp_done(sk); -+ -+ if (!sock_is_dead) -+ mptcp_sub_close(sk, 0); -+} -+EXPORT_SYMBOL(mptcp_sub_force_close); -+ -+/* Update the mpcb send window, based on the contributions -+ * of each subflow -+ */ -+void mptcp_update_sndbuf(struct mptcp_cb *mpcb) -+{ -+ struct sock *meta_sk = mpcb->meta_sk, *sk; -+ int new_sndbuf = 0, old_sndbuf = meta_sk->sk_sndbuf; -+ mptcp_for_each_sk(mpcb, sk) { -+ if (!mptcp_sk_can_send(sk)) -+ continue; -+ -+ new_sndbuf += sk->sk_sndbuf; -+ -+ if (new_sndbuf > sysctl_tcp_wmem[2] || new_sndbuf < 0) { -+ new_sndbuf = sysctl_tcp_wmem[2]; -+ break; -+ } -+ } -+ meta_sk->sk_sndbuf = max(min(new_sndbuf, sysctl_tcp_wmem[2]), meta_sk->sk_sndbuf); -+ -+ /* The subflow's call to sk_write_space in tcp_new_space ends up in -+ * mptcp_write_space. -+ * It has nothing to do with waking up the application. -+ * So, we do it here. -+ */ -+ if (old_sndbuf != meta_sk->sk_sndbuf) -+ meta_sk->sk_write_space(meta_sk); -+} -+ -+void mptcp_close(struct sock *meta_sk, long timeout) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct sock *sk_it, *tmpsk; -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ struct sk_buff *skb; -+ int data_was_unread = 0; -+ int state; -+ -+ mptcp_debug("%s: Close of meta_sk with tok %#x\n", -+ __func__, mpcb->mptcp_loc_token); -+ -+ mutex_lock(&mpcb->mpcb_mutex); -+ lock_sock(meta_sk); -+ -+ if (meta_tp->inside_tk_table) { -+ /* Detach the mpcb from the token hashtable */ -+ mptcp_hash_remove_bh(meta_tp); -+ reqsk_queue_destroy(&inet_csk(meta_sk)->icsk_accept_queue); -+ } -+ -+ meta_sk->sk_shutdown = SHUTDOWN_MASK; -+ /* We need to flush the recv. buffs. We do this only on the -+ * descriptor close, not protocol-sourced closes, because the -+ * reader process may not have drained the data yet! -+ */ -+ while ((skb = __skb_dequeue(&meta_sk->sk_receive_queue)) != NULL) { -+ u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq - -+ tcp_hdr(skb)->fin; -+ data_was_unread += len; -+ __kfree_skb(skb); -+ } -+ -+ sk_mem_reclaim(meta_sk); -+ -+ /* If socket has been already reset (e.g. in tcp_reset()) - kill it. */ -+ if (meta_sk->sk_state == TCP_CLOSE) { -+ mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { -+ if (tcp_sk(sk_it)->send_mp_fclose) -+ continue; -+ mptcp_sub_close(sk_it, 0); -+ } -+ goto adjudge_to_death; -+ } -+ -+ if (data_was_unread) { -+ /* Unread data was tossed, zap the connection. */ -+ NET_INC_STATS_USER(sock_net(meta_sk), LINUX_MIB_TCPABORTONCLOSE); -+ tcp_set_state(meta_sk, TCP_CLOSE); -+ tcp_send_active_reset(meta_sk, meta_sk->sk_allocation); -+ } else if (sock_flag(meta_sk, SOCK_LINGER) && !meta_sk->sk_lingertime) { -+ /* Check zero linger _after_ checking for unread data. */ -+ meta_sk->sk_prot->disconnect(meta_sk, 0); -+ NET_INC_STATS_USER(sock_net(meta_sk), LINUX_MIB_TCPABORTONDATA); -+ } else if (tcp_close_state(meta_sk)) { -+ mptcp_send_fin(meta_sk); -+ } else if (meta_tp->snd_una == meta_tp->write_seq) { -+ /* The DATA_FIN has been sent and acknowledged -+ * (e.g., by sk_shutdown). Close all the other subflows -+ */ -+ mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { -+ unsigned long delay = 0; -+ /* If we are the passive closer, don't trigger -+ * subflow-fin until the subflow has been finned -+ * by the peer. - thus we add a delay -+ */ -+ if (mpcb->passive_close && -+ sk_it->sk_state == TCP_ESTABLISHED) -+ delay = inet_csk(sk_it)->icsk_rto << 3; -+ -+ mptcp_sub_close(sk_it, delay); -+ } -+ } -+ -+ sk_stream_wait_close(meta_sk, timeout); -+ -+adjudge_to_death: -+ state = meta_sk->sk_state; -+ sock_hold(meta_sk); -+ sock_orphan(meta_sk); -+ -+ /* socket will be freed after mptcp_close - we have to prevent -+ * access from the subflows. -+ */ -+ mptcp_for_each_sk(mpcb, sk_it) { -+ /* Similar to sock_orphan, but we don't set it DEAD, because -+ * the callbacks are still set and must be called. -+ */ -+ write_lock_bh(&sk_it->sk_callback_lock); -+ sk_set_socket(sk_it, NULL); -+ sk_it->sk_wq = NULL; -+ write_unlock_bh(&sk_it->sk_callback_lock); -+ } -+ -+ /* It is the last release_sock in its life. It will remove backlog. */ -+ release_sock(meta_sk); -+ -+ /* Now socket is owned by kernel and we acquire BH lock -+ * to finish close. No need to check for user refs. -+ */ -+ local_bh_disable(); -+ bh_lock_sock(meta_sk); -+ WARN_ON(sock_owned_by_user(meta_sk)); -+ -+ percpu_counter_inc(meta_sk->sk_prot->orphan_count); -+ -+ /* Have we already been destroyed by a softirq or backlog? */ -+ if (state != TCP_CLOSE && meta_sk->sk_state == TCP_CLOSE) -+ goto out; -+ -+ /* This is a (useful) BSD violating of the RFC. There is a -+ * problem with TCP as specified in that the other end could -+ * keep a socket open forever with no application left this end. -+ * We use a 3 minute timeout (about the same as BSD) then kill -+ * our end. If they send after that then tough - BUT: long enough -+ * that we won't make the old 4*rto = almost no time - whoops -+ * reset mistake. -+ * -+ * Nope, it was not mistake. It is really desired behaviour -+ * f.e. on http servers, when such sockets are useless, but -+ * consume significant resources. Let's do it with special -+ * linger2 option. --ANK -+ */ -+ -+ if (meta_sk->sk_state == TCP_FIN_WAIT2) { -+ if (meta_tp->linger2 < 0) { -+ tcp_set_state(meta_sk, TCP_CLOSE); -+ tcp_send_active_reset(meta_sk, GFP_ATOMIC); -+ NET_INC_STATS_BH(sock_net(meta_sk), -+ LINUX_MIB_TCPABORTONLINGER); -+ } else { -+ const int tmo = tcp_fin_time(meta_sk); -+ -+ if (tmo > TCP_TIMEWAIT_LEN) { -+ inet_csk_reset_keepalive_timer(meta_sk, -+ tmo - TCP_TIMEWAIT_LEN); -+ } else { -+ tcp_time_wait(meta_sk, TCP_FIN_WAIT2, tmo); -+ goto out; -+ } -+ } -+ } -+ if (meta_sk->sk_state != TCP_CLOSE) { -+ sk_mem_reclaim(meta_sk); -+ if (tcp_too_many_orphans(meta_sk, 0)) { -+ if (net_ratelimit()) -+ pr_info("MPTCP: too many of orphaned sockets\n"); -+ tcp_set_state(meta_sk, TCP_CLOSE); -+ tcp_send_active_reset(meta_sk, GFP_ATOMIC); -+ NET_INC_STATS_BH(sock_net(meta_sk), -+ LINUX_MIB_TCPABORTONMEMORY); -+ } -+ } -+ -+ -+ if (meta_sk->sk_state == TCP_CLOSE) -+ inet_csk_destroy_sock(meta_sk); -+ /* Otherwise, socket is reprieved until protocol close. */ -+ -+out: -+ bh_unlock_sock(meta_sk); -+ local_bh_enable(); -+ mutex_unlock(&mpcb->mpcb_mutex); -+ sock_put(meta_sk); /* Taken by sock_hold */ -+} -+ -+void mptcp_disconnect(struct sock *sk) -+{ -+ struct sock *subsk, *tmpsk; -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ __skb_queue_purge(&tp->mpcb->reinject_queue); -+ -+ if (tp->inside_tk_table) { -+ mptcp_hash_remove_bh(tp); -+ reqsk_queue_destroy(&inet_csk(tp->meta_sk)->icsk_accept_queue); -+ } -+ -+ local_bh_disable(); -+ mptcp_for_each_sk_safe(tp->mpcb, subsk, tmpsk) { -+ /* The socket will get removed from the subsocket-list -+ * and made non-mptcp by setting mpc to 0. -+ * -+ * This is necessary, because tcp_disconnect assumes -+ * that the connection is completly dead afterwards. -+ * Thus we need to do a mptcp_del_sock. Due to this call -+ * we have to make it non-mptcp. -+ * -+ * We have to lock the socket, because we set mpc to 0. -+ * An incoming packet would take the subsocket's lock -+ * and go on into the receive-path. -+ * This would be a race. -+ */ -+ -+ bh_lock_sock(subsk); -+ mptcp_del_sock(subsk); -+ reset_mpc(tcp_sk(subsk)); -+ mptcp_sub_force_close(subsk); -+ bh_unlock_sock(subsk); -+ } -+ local_bh_enable(); -+ -+ tp->was_meta_sk = 1; -+ reset_mpc(tp); -+} -+ -+ -+/* Returns 1 if we should enable MPTCP for that socket. */ -+int mptcp_doit(struct sock *sk) -+{ -+ /* Do not allow MPTCP enabling if the MPTCP initialization failed */ -+ if (mptcp_init_failed) -+ return 0; -+ -+ if (sysctl_mptcp_enabled == MPTCP_APP && !tcp_sk(sk)->mptcp_enabled) -+ return 0; -+ -+ /* Socket may already be established (e.g., called from tcp_recvmsg) */ -+ if (tcp_sk(sk)->mpc || tcp_sk(sk)->request_mptcp) -+ return 1; -+ -+ /* Don't do mptcp over loopback */ -+ if (sk->sk_family == AF_INET && -+ (ipv4_is_loopback(inet_sk(sk)->inet_daddr) || -+ ipv4_is_loopback(inet_sk(sk)->inet_saddr))) -+ return 0; -+#if IS_ENABLED(CONFIG_IPV6) -+ if (sk->sk_family == AF_INET6 && -+ (ipv6_addr_loopback(&sk->sk_v6_daddr) || -+ ipv6_addr_loopback(&inet6_sk(sk)->saddr))) -+ return 0; -+#endif -+ if (mptcp_v6_is_v4_mapped(sk) && -+ ipv4_is_loopback(inet_sk(sk)->inet_saddr)) -+ return 0; -+ -+#ifdef CONFIG_TCP_MD5SIG -+ /* If TCP_MD5SIG is enabled, do not do MPTCP - there is no Option-Space */ -+ if (tcp_sk(sk)->af_specific->md5_lookup(sk, sk)) -+ return 0; -+#endif -+ -+ return 1; -+} -+ -+int mptcp_create_master_sk(struct sock *meta_sk, __u64 remote_key, u32 window) -+{ -+ struct tcp_sock *master_tp; -+ struct sock *master_sk; -+ -+ if (mptcp_alloc_mpcb(meta_sk, remote_key, window)) -+ goto err_alloc_mpcb; -+ -+ master_sk = tcp_sk(meta_sk)->mpcb->master_sk; -+ master_tp = tcp_sk(master_sk); -+ -+ if (mptcp_add_sock(meta_sk, master_sk, 0, 0, GFP_ATOMIC)) -+ goto err_add_sock; -+ -+ if (__inet_inherit_port(meta_sk, master_sk) < 0) -+ goto err_add_sock; -+ -+ meta_sk->sk_prot->unhash(meta_sk); -+ -+ if (master_sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(master_sk)) -+ __inet_hash_nolisten(master_sk, NULL); -+#if IS_ENABLED(CONFIG_IPV6) -+ else -+ __inet6_hash(master_sk, NULL); -+#endif -+ -+ master_tp->mptcp->init_rcv_wnd = master_tp->rcv_wnd; -+ -+ return 0; -+ -+err_add_sock: -+ mptcp_fallback_meta_sk(meta_sk); -+ -+ inet_csk_prepare_forced_close(master_sk); -+ tcp_done(master_sk); -+ inet_csk_prepare_forced_close(meta_sk); -+ tcp_done(meta_sk); -+ -+err_alloc_mpcb: -+ return -ENOBUFS; -+} -+ -+int mptcp_check_req_master(struct sock *sk, struct sock *child, -+ struct request_sock *req, -+ struct request_sock **prev, -+ struct mptcp_options_received *mopt) -+{ -+ struct tcp_sock *child_tp = tcp_sk(child); -+ struct sock *meta_sk = child; -+ struct mptcp_cb *mpcb; -+ struct mptcp_request_sock *mtreq; -+ -+ if (!tcp_rsk(req)->saw_mpc) -+ return 1; -+ -+ /* Just set this values to pass them to mptcp_alloc_mpcb */ -+ mtreq = mptcp_rsk(req); -+ child_tp->mptcp_loc_key = mtreq->mptcp_loc_key; -+ child_tp->mptcp_loc_token = mtreq->mptcp_loc_token; -+ -+ if (mptcp_create_master_sk(meta_sk, mtreq->mptcp_rem_key, -+ child_tp->snd_wnd)) -+ return -ENOBUFS; -+ -+ child = tcp_sk(child)->mpcb->master_sk; -+ child_tp = tcp_sk(child); -+ mpcb = child_tp->mpcb; -+ -+ child_tp->mptcp->snt_isn = tcp_rsk(req)->snt_isn; -+ child_tp->mptcp->rcv_isn = tcp_rsk(req)->rcv_isn; -+ -+ mpcb->dss_csum = mtreq->dss_csum; -+ mpcb->server_side = 1; -+ -+ /* Will be moved to ESTABLISHED by tcp_rcv_state_process() */ -+ mptcp_update_metasocket(child, meta_sk); -+ -+ /* Needs to be done here additionally, because when accepting a -+ * new connection we pass by __reqsk_free and not reqsk_free. -+ */ -+ mptcp_reqsk_remove_tk(req); -+ -+ /* Hold when creating the meta-sk in tcp_vX_syn_recv_sock. */ -+ sock_put(meta_sk); -+ -+ inet_csk_reqsk_queue_unlink(sk, req, prev); -+ inet_csk_reqsk_queue_removed(sk, req); -+ inet_csk_reqsk_queue_add(sk, req, meta_sk); -+ -+ return 0; -+} -+ -+struct sock *mptcp_check_req_child(struct sock *meta_sk, struct sock *child, -+ struct request_sock *req, -+ struct request_sock **prev, -+ struct mptcp_options_received *mopt) -+{ -+ struct tcp_sock *child_tp = tcp_sk(child); -+ struct mptcp_request_sock *mtreq = mptcp_rsk(req); -+ struct mptcp_cb *mpcb = mtreq->mpcb; -+ u8 hash_mac_check[20]; -+ -+ child_tp->inside_tk_table = 0; -+ -+ if (!mopt->join_ack) -+ goto teardown; -+ -+ mptcp_hmac_sha1((u8 *)&mpcb->mptcp_rem_key, -+ (u8 *)&mpcb->mptcp_loc_key, -+ (u8 *)&mtreq->mptcp_rem_nonce, -+ (u8 *)&mtreq->mptcp_loc_nonce, -+ (u32 *)hash_mac_check); -+ -+ if (memcmp(hash_mac_check, (char *)&mopt->mptcp_recv_mac, 20)) -+ goto teardown; -+ -+ /* Point it to the same struct socket and wq as the meta_sk */ -+ sk_set_socket(child, meta_sk->sk_socket); -+ child->sk_wq = meta_sk->sk_wq; -+ -+ if (mptcp_add_sock(meta_sk, child, mtreq->loc_id, mtreq->rem_id, GFP_ATOMIC)) { -+ reset_mpc(child_tp); /* Has been inherited, but now -+ * child_tp->mptcp is NULL -+ */ -+ /* TODO when we support acking the third ack for new subflows, -+ * we should silently discard this third ack, by returning NULL. -+ * -+ * Maybe, at the retransmission we will have enough memory to -+ * fully add the socket to the meta-sk. -+ */ -+ goto teardown; -+ } -+ -+ /* The child is a clone of the meta socket, we must now reset -+ * some of the fields -+ */ -+ child_tp->mptcp->rcv_low_prio = mtreq->low_prio; -+ -+ /* We should allow proper increase of the snd/rcv-buffers. Thus, we -+ * use the original values instead of the bloated up ones from the -+ * clone. -+ */ -+ child->sk_sndbuf = mpcb->orig_sk_sndbuf; -+ child->sk_rcvbuf = mpcb->orig_sk_rcvbuf; -+ -+ child_tp->mptcp->slave_sk = 1; -+ child_tp->mptcp->snt_isn = tcp_rsk(req)->snt_isn; -+ child_tp->mptcp->rcv_isn = tcp_rsk(req)->rcv_isn; -+ child_tp->mptcp->init_rcv_wnd = req->rcv_wnd; -+ -+ child_tp->tsq_flags = 0; -+ -+ /* Subflows do not use the accept queue, as they -+ * are attached immediately to the mpcb. -+ */ -+ inet_csk_reqsk_queue_drop(meta_sk, req, prev); -+ return child; -+ -+teardown: -+ /* Drop this request - sock creation failed. */ -+ inet_csk_reqsk_queue_drop(meta_sk, req, prev); -+ inet_csk_prepare_forced_close(child); -+ tcp_done(child); -+ return meta_sk; -+} -+ -+int mptcp_time_wait(struct sock *sk, struct tcp_timewait_sock *tw) -+{ -+ struct mptcp_tw *mptw; -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct mptcp_cb *mpcb = tp->mpcb; -+ -+ /* Alloc MPTCP-tw-sock */ -+ mptw = kmem_cache_alloc(mptcp_tw_cache, GFP_ATOMIC); -+ if (!mptw) -+ return -ENOBUFS; -+ -+ atomic_inc(&mpcb->mpcb_refcnt); -+ -+ tw->mptcp_tw = mptw; -+ mptw->loc_key = mpcb->mptcp_loc_key; -+ mptw->meta_tw = mpcb->in_time_wait; -+ if (mptw->meta_tw) { -+ mptw->rcv_nxt = mptcp_get_rcv_nxt_64(mptcp_meta_tp(tp)); -+ if (mpcb->mptw_state != TCP_TIME_WAIT) -+ mptw->rcv_nxt++; -+ } -+ rcu_assign_pointer(mptw->mpcb, mpcb); -+ -+ spin_lock(&mpcb->tw_lock); -+ list_add_rcu(&mptw->list, &tp->mpcb->tw_list); -+ mptw->in_list = 1; -+ spin_unlock(&mpcb->tw_lock); -+ -+ return 0; -+} -+ -+void mptcp_twsk_destructor(struct tcp_timewait_sock *tw) -+{ -+ struct mptcp_cb *mpcb; -+ -+ rcu_read_lock(); -+ mpcb = rcu_dereference(tw->mptcp_tw->mpcb); -+ -+ /* If we are still holding a ref to the mpcb, we have to remove ourself -+ * from the list and drop the ref properly. -+ */ -+ if (mpcb && atomic_inc_not_zero(&mpcb->mpcb_refcnt)) { -+ spin_lock(&mpcb->tw_lock); -+ if (tw->mptcp_tw->in_list) { -+ list_del_rcu(&tw->mptcp_tw->list); -+ tw->mptcp_tw->in_list = 0; -+ } -+ spin_unlock(&mpcb->tw_lock); -+ -+ /* Twice, because we increased it above */ -+ mptcp_mpcb_put(mpcb); -+ mptcp_mpcb_put(mpcb); -+ } -+ -+ rcu_read_unlock(); -+ -+ kmem_cache_free(mptcp_tw_cache, tw->mptcp_tw); -+} -+ -+/* Updates the rcv_nxt of the time-wait-socks and allows them to ack a -+ * data-fin. -+ */ -+void mptcp_update_tw_socks(const struct tcp_sock *tp, int state) -+{ -+ struct mptcp_tw *mptw; -+ -+ /* Used for sockets that go into tw after the meta -+ * (see mptcp_time_wait()) -+ */ -+ tp->mpcb->in_time_wait = 1; -+ tp->mpcb->mptw_state = state; -+ -+ /* Update the time-wait-sock's information */ -+ rcu_read_lock_bh(); -+ list_for_each_entry_rcu(mptw, &tp->mpcb->tw_list, list) { -+ mptw->meta_tw = 1; -+ mptw->rcv_nxt = mptcp_get_rcv_nxt_64(tp); -+ -+ /* We want to ack a DATA_FIN, but are yet in FIN_WAIT_2 - -+ * pretend as if the DATA_FIN has already reached us, that way -+ * the checks in tcp_timewait_state_process will be good as the -+ * DATA_FIN comes in. -+ */ -+ if (state != TCP_TIME_WAIT) -+ mptw->rcv_nxt++; -+ } -+ rcu_read_unlock_bh(); -+} -+ -+void mptcp_tsq_flags(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ -+ /* It will be handled as a regular deferred-call */ -+ if (is_meta_sk(sk)) -+ return; -+ -+ if (list_empty(&tp->mptcp->cb_list)) { -+ list_add(&tp->mptcp->cb_list, &tp->mpcb->callback_list); -+ /* We need to hold it here, as the sock_hold is not assured -+ * by the release_sock as it is done in regular TCP. -+ * -+ * The subsocket may get inet_csk_destroy'd while it is inside -+ * the callback_list. -+ */ -+ sock_hold(sk); -+ } -+ -+ if (!test_and_set_bit(MPTCP_SUB_DEFERRED, &tcp_sk(meta_sk)->tsq_flags)) -+ sock_hold(meta_sk); -+} -+ -+void mptcp_tsq_sub_deferred(struct sock *meta_sk) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct mptcp_tcp_sock *mptcp, *tmp; -+ -+ BUG_ON(!is_meta_sk(meta_sk) && !meta_tp->was_meta_sk); -+ -+ __sock_put(meta_sk); -+ list_for_each_entry_safe(mptcp, tmp, &meta_tp->mpcb->callback_list, cb_list) { -+ struct tcp_sock *tp = mptcp->tp; -+ struct sock *sk = (struct sock *)tp; -+ -+ list_del_init(&mptcp->cb_list); -+ sk->sk_prot->release_cb(sk); -+ /* Final sock_put (cfr. mptcp_tsq_flags */ -+ sock_put(sk); -+ } -+} -+ -+struct workqueue_struct *mptcp_wq; -+EXPORT_SYMBOL(mptcp_wq); -+ -+/* Output /proc/net/mptcp */ -+static int mptcp_pm_seq_show(struct seq_file *seq, void *v) -+{ -+ struct tcp_sock *meta_tp; -+ struct net *net = seq->private; -+ int i, n = 0; -+ -+ seq_printf(seq, " sl loc_tok rem_tok v6 " -+ "local_address " -+ "remote_address " -+ "st ns tx_queue rx_queue inode"); -+ seq_putc(seq, '\n'); -+ -+ for (i = 0; i < MPTCP_HASH_SIZE; i++) { -+ struct hlist_nulls_node *node; -+ rcu_read_lock_bh(); -+ hlist_nulls_for_each_entry_rcu(meta_tp, node, -+ &tk_hashtable[i], tk_table) { -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ struct sock *meta_sk = (struct sock *)meta_tp; -+ struct inet_sock *isk = inet_sk(meta_sk); -+ -+ if (!meta_tp->mpc || !net_eq(net, sock_net(meta_sk))) -+ continue; -+ -+ seq_printf(seq, "%4d: %04X %04X ", n++, -+ mpcb->mptcp_loc_token, -+ mpcb->mptcp_rem_token); -+ if (meta_sk->sk_family == AF_INET || -+ mptcp_v6_is_v4_mapped(meta_sk)) { -+ seq_printf(seq, " 0 %08X:%04X %08X:%04X ", -+ isk->inet_saddr, -+ ntohs(isk->inet_sport), -+ isk->inet_daddr, -+ ntohs(isk->inet_dport)); -+#if IS_ENABLED(CONFIG_IPV6) -+ } else if (meta_sk->sk_family == AF_INET6) { -+ struct in6_addr *src = &isk->pinet6->saddr; -+ struct in6_addr *dst = &meta_sk->sk_v6_daddr; -+ seq_printf(seq, " 1 %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X", -+ src->s6_addr32[0], src->s6_addr32[1], -+ src->s6_addr32[2], src->s6_addr32[3], -+ ntohs(isk->inet_sport), -+ dst->s6_addr32[0], dst->s6_addr32[1], -+ dst->s6_addr32[2], dst->s6_addr32[3], -+ ntohs(isk->inet_dport)); -+#endif -+ } -+ seq_printf(seq, " %02X %02X %08X:%08X %lu", -+ meta_sk->sk_state, mpcb->cnt_subflows, -+ meta_tp->write_seq - meta_tp->snd_una, -+ max_t(int, meta_tp->rcv_nxt - -+ meta_tp->copied_seq, 0), -+ sock_i_ino(meta_sk)); -+ seq_putc(seq, '\n'); -+ } -+ rcu_read_unlock_bh(); -+ } -+ -+ return 0; -+} -+ -+static int mptcp_pm_seq_open(struct inode *inode, struct file *file) -+{ -+ return single_open_net(inode, file, mptcp_pm_seq_show); -+} -+ -+static const struct file_operations mptcp_pm_seq_fops = { -+ .owner = THIS_MODULE, -+ .open = mptcp_pm_seq_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release_net, -+}; -+ -+static int mptcp_pm_init_net(struct net *net) -+{ -+ if (!proc_create("mptcp", S_IRUGO, net->proc_net, &mptcp_pm_seq_fops)) -+ return -ENOMEM; -+ -+ return 0; -+} -+ -+static void mptcp_pm_exit_net(struct net *net) -+{ -+ remove_proc_entry("mptcp", net->proc_net); -+} -+ -+static struct pernet_operations mptcp_pm_proc_ops = { -+ .init = mptcp_pm_init_net, -+ .exit = mptcp_pm_exit_net, -+}; -+ -+/* General initialization of mptcp */ -+void __init mptcp_init(void) -+{ -+ int i; -+ struct ctl_table_header *mptcp_sysctl; -+ -+ mptcp_sock_cache = kmem_cache_create("mptcp_sock", -+ sizeof(struct mptcp_tcp_sock), -+ 0, SLAB_HWCACHE_ALIGN, -+ NULL); -+ if (!mptcp_sock_cache) -+ goto mptcp_sock_cache_failed; -+ -+ mptcp_cb_cache = kmem_cache_create("mptcp_cb", sizeof(struct mptcp_cb), -+ 0, SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, -+ NULL); -+ if (!mptcp_cb_cache) -+ goto mptcp_cb_cache_failed; -+ -+ mptcp_tw_cache = kmem_cache_create("mptcp_tw", sizeof(struct mptcp_tw), -+ 0, SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, -+ NULL); -+ if (!mptcp_tw_cache) -+ goto mptcp_tw_cache_failed; -+ -+ get_random_bytes(mptcp_secret, sizeof(mptcp_secret)); -+ -+ mptcp_wq = alloc_workqueue("mptcp_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 8); -+ if (!mptcp_wq) -+ goto alloc_workqueue_failed; -+ -+ for (i = 0; i < MPTCP_HASH_SIZE; i++) { -+ INIT_HLIST_NULLS_HEAD(&tk_hashtable[i], i); -+ INIT_LIST_HEAD(&mptcp_reqsk_htb[i]); -+ INIT_HLIST_NULLS_HEAD(&mptcp_reqsk_tk_htb[i], i); -+ } -+ -+ spin_lock_init(&mptcp_reqsk_hlock); -+ spin_lock_init(&mptcp_tk_hashlock); -+ -+ if (register_pernet_subsys(&mptcp_pm_proc_ops)) -+ goto pernet_failed; -+ -+#if IS_ENABLED(CONFIG_IPV6) -+ if (mptcp_pm_v6_init()) -+ goto mptcp_pm_v6_failed; -+#endif -+ if (mptcp_pm_v4_init()) -+ goto mptcp_pm_v4_failed; -+ -+ mptcp_sysctl = register_net_sysctl(&init_net, "net/mptcp", mptcp_table); -+ if (!mptcp_sysctl) -+ goto register_sysctl_failed; -+ -+ if (mptcp_register_path_manager(&mptcp_pm_default)) -+ goto register_pm_failed; -+ -+ pr_info("MPTCP: Stable release v0.89.0-rc"); -+ -+ mptcp_init_failed = false; -+ -+ return; -+ -+register_pm_failed: -+ unregister_net_sysctl_table(mptcp_sysctl); -+register_sysctl_failed: -+ mptcp_pm_v4_undo(); -+mptcp_pm_v4_failed: -+#if IS_ENABLED(CONFIG_IPV6) -+ mptcp_pm_v6_undo(); -+mptcp_pm_v6_failed: -+#endif -+ unregister_pernet_subsys(&mptcp_pm_proc_ops); -+pernet_failed: -+ destroy_workqueue(mptcp_wq); -+alloc_workqueue_failed: -+ kmem_cache_destroy(mptcp_tw_cache); -+mptcp_tw_cache_failed: -+ kmem_cache_destroy(mptcp_cb_cache); -+mptcp_cb_cache_failed: -+ kmem_cache_destroy(mptcp_sock_cache); -+mptcp_sock_cache_failed: -+ mptcp_init_failed = true; -+} -diff --git a/net/mptcp/mptcp_fullmesh.c b/net/mptcp/mptcp_fullmesh.c -new file mode 100644 -index 0000000..49bddf3 ---- /dev/null -+++ b/net/mptcp/mptcp_fullmesh.c -@@ -0,0 +1,1313 @@ -+#include -+ -+#include -+#include -+ -+#if IS_ENABLED(CONFIG_IPV6) -+#include -+#include -+#endif -+ -+enum { -+ MPTCP_EVENT_ADD = 1, -+ MPTCP_EVENT_DEL, -+ MPTCP_EVENT_MOD, -+}; -+ -+struct mptcp_loc_addr { -+ struct mptcp_loc4 locaddr4[MPTCP_MAX_ADDR]; -+ u8 loc4_bits; -+ u8 next_v4_index; -+ -+ struct mptcp_loc6 locaddr6[MPTCP_MAX_ADDR]; -+ u8 loc6_bits; -+ u8 next_v6_index; -+}; -+ -+struct mptcp_addr_event { -+ struct list_head list; -+ unsigned short family; -+ u8 code:7, -+ low_prio:1; -+ union inet_addr addr; -+}; -+ -+struct fullmesh_priv { -+ /* Worker struct for subflow establishment */ -+ struct work_struct subflow_work; -+ /* Delayed worker, when the routing-tables are not yet ready. */ -+ struct delayed_work subflow_retry_work; -+ -+ struct mptcp_cb *mpcb; -+ -+ u16 remove_addrs; /* Addresses to remove */ -+ u8 announced_addrs_v4; /* IPv4 Addresses we did announce */ -+ u8 announced_addrs_v6; /* IPv4 Addresses we did announce */ -+ -+ u8 add_addr; /* Are we sending an add_addr? */ -+}; -+ -+struct mptcp_fm_ns { -+ struct mptcp_loc_addr __rcu *local; -+ spinlock_t local_lock; /* Protecting the above pointer */ -+ struct list_head events; -+ struct delayed_work address_worker; -+ -+ struct net *net; -+}; -+ -+static struct mptcp_pm_ops full_mesh __read_mostly; -+ -+static struct mptcp_fm_ns *fm_get_ns(struct net *net) -+{ -+ return (struct mptcp_fm_ns *)net->mptcp.path_managers[MPTCP_PM_FULLMESH]; -+} -+ -+static void full_mesh_create_subflows(struct sock *meta_sk); -+ -+static void retry_subflow_worker(struct work_struct *work) -+{ -+ struct delayed_work *delayed_work = container_of(work, -+ struct delayed_work, -+ work); -+ struct fullmesh_priv *pm_priv = container_of(delayed_work, -+ struct fullmesh_priv, -+ subflow_retry_work); -+ struct mptcp_cb *mpcb = pm_priv->mpcb; -+ struct sock *meta_sk = mpcb->meta_sk; -+ struct mptcp_loc_addr *mptcp_local; -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(meta_sk)); -+ int iter = 0, i; -+ -+ /* We need a local (stable) copy of the address-list. Really, it is not -+ * such a big deal, if the address-list is not 100% up-to-date. -+ */ -+ rcu_read_lock_bh(); -+ mptcp_local = rcu_dereference_bh(fm_ns->local); -+ mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), GFP_ATOMIC); -+ rcu_read_unlock_bh(); -+ -+ if (!mptcp_local) -+ return; -+ -+next_subflow: -+ if (iter) { -+ release_sock(meta_sk); -+ mutex_unlock(&mpcb->mpcb_mutex); -+ -+ yield(); -+ } -+ mutex_lock(&mpcb->mpcb_mutex); -+ lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); -+ -+ iter++; -+ -+ if (sock_flag(meta_sk, SOCK_DEAD)) -+ goto exit; -+ -+ mptcp_for_each_bit_set(mpcb->rem4_bits, i) { -+ struct mptcp_rem4 *rem = &mpcb->remaddr4[i]; -+ /* Do we need to retry establishing a subflow ? */ -+ if (rem->retry_bitfield) { -+ int i = mptcp_find_free_index(~rem->retry_bitfield); -+ -+ rem->bitfield |= (1 << i); -+ rem->retry_bitfield &= ~(1 << i); -+ -+ mptcp_init4_subsockets(meta_sk, &mptcp_local->locaddr4[i], rem); -+ goto next_subflow; -+ } -+ } -+ -+#if IS_ENABLED(CONFIG_IPV6) -+ mptcp_for_each_bit_set(mpcb->rem6_bits, i) { -+ struct mptcp_rem6 *rem = &mpcb->remaddr6[i]; -+ -+ /* Do we need to retry establishing a subflow ? */ -+ if (rem->retry_bitfield) { -+ int i = mptcp_find_free_index(~rem->retry_bitfield); -+ -+ rem->bitfield |= (1 << i); -+ rem->retry_bitfield &= ~(1 << i); -+ -+ mptcp_init6_subsockets(meta_sk, &mptcp_local->locaddr6[i], rem); -+ goto next_subflow; -+ } -+ } -+#endif -+ -+exit: -+ kfree(mptcp_local); -+ release_sock(meta_sk); -+ mutex_unlock(&mpcb->mpcb_mutex); -+ sock_put(meta_sk); -+} -+ -+/** -+ * Create all new subflows, by doing calls to mptcp_initX_subsockets -+ * -+ * This function uses a goto next_subflow, to allow releasing the lock between -+ * new subflows and giving other processes a chance to do some work on the -+ * socket and potentially finishing the communication. -+ **/ -+static void create_subflow_worker(struct work_struct *work) -+{ -+ struct fullmesh_priv *pm_priv = container_of(work, -+ struct fullmesh_priv, -+ subflow_work); -+ struct mptcp_cb *mpcb = pm_priv->mpcb; -+ struct sock *meta_sk = mpcb->meta_sk; -+ struct mptcp_loc_addr *mptcp_local; -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(meta_sk)); -+ int iter = 0, retry = 0; -+ int i; -+ -+ /* We need a local (stable) copy of the address-list. Really, it is not -+ * such a big deal, if the address-list is not 100% up-to-date. -+ */ -+ rcu_read_lock_bh(); -+ mptcp_local = rcu_dereference_bh(fm_ns->local); -+ mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), GFP_ATOMIC); -+ rcu_read_unlock_bh(); -+ -+ if (!mptcp_local) -+ return; -+ -+next_subflow: -+ if (iter) { -+ release_sock(meta_sk); -+ mutex_unlock(&mpcb->mpcb_mutex); -+ -+ yield(); -+ } -+ mutex_lock(&mpcb->mpcb_mutex); -+ lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); -+ -+ iter++; -+ -+ if (sock_flag(meta_sk, SOCK_DEAD)) -+ goto exit; -+ -+ if (mpcb->master_sk && -+ !tcp_sk(mpcb->master_sk)->mptcp->fully_established) -+ goto exit; -+ -+ mptcp_for_each_bit_set(mpcb->rem4_bits, i) { -+ struct mptcp_rem4 *rem; -+ u8 remaining_bits; -+ -+ rem = &mpcb->remaddr4[i]; -+ remaining_bits = ~(rem->bitfield) & mptcp_local->loc4_bits; -+ -+ /* Are there still combinations to handle? */ -+ if (remaining_bits) { -+ int i = mptcp_find_free_index(~remaining_bits); -+ -+ rem->bitfield |= (1 << i); -+ -+ /* If a route is not yet available then retry once */ -+ if (mptcp_init4_subsockets(meta_sk, &mptcp_local->locaddr4[i], -+ rem) == -ENETUNREACH) -+ retry = rem->retry_bitfield |= (1 << i); -+ goto next_subflow; -+ } -+ } -+ -+#if IS_ENABLED(CONFIG_IPV6) -+ mptcp_for_each_bit_set(mpcb->rem6_bits, i) { -+ struct mptcp_rem6 *rem; -+ u8 remaining_bits; -+ -+ rem = &mpcb->remaddr6[i]; -+ remaining_bits = ~(rem->bitfield) & mptcp_local->loc6_bits; -+ -+ /* Are there still combinations to handle? */ -+ if (remaining_bits) { -+ int i = mptcp_find_free_index(~remaining_bits); -+ -+ rem->bitfield |= (1 << i); -+ -+ /* If a route is not yet available then retry once */ -+ if (mptcp_init6_subsockets(meta_sk, &mptcp_local->locaddr6[i], -+ rem) == -ENETUNREACH) -+ retry = rem->retry_bitfield |= (1 << i); -+ goto next_subflow; -+ } -+ } -+#endif -+ -+ if (retry && !delayed_work_pending(&pm_priv->subflow_retry_work)) { -+ sock_hold(meta_sk); -+ queue_delayed_work(mptcp_wq, &pm_priv->subflow_retry_work, -+ msecs_to_jiffies(MPTCP_SUBFLOW_RETRY_DELAY)); -+ } -+ -+exit: -+ kfree(mptcp_local); -+ release_sock(meta_sk); -+ mutex_unlock(&mpcb->mpcb_mutex); -+ sock_put(meta_sk); -+} -+ -+static void update_remove_addrs(u8 addr_id, struct sock *meta_sk, -+ struct mptcp_loc_addr *mptcp_local) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct fullmesh_priv *fmp = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; -+ struct sock *sk; -+ int i; -+ -+ fmp->remove_addrs |= (1 << addr_id); -+ /* v4 goes from 0 to MPTCP_MAX_ADDR, v6 beyond */ -+ if (addr_id < MPTCP_MAX_ADDR) { -+ fmp->announced_addrs_v4 &= ~(1 << addr_id); -+ -+ mptcp_for_each_bit_set(mpcb->rem4_bits, i) { -+ mpcb->remaddr4[i].bitfield &= mptcp_local->loc4_bits; -+ mpcb->remaddr4[i].retry_bitfield &= mptcp_local->loc4_bits; -+ } -+ } else { -+ fmp->announced_addrs_v6 &= ~(1 << (addr_id - MPTCP_MAX_ADDR)); -+ -+ mptcp_for_each_bit_set(mpcb->rem6_bits, i) { -+ mpcb->remaddr6[i].bitfield &= mptcp_local->loc6_bits; -+ mpcb->remaddr6[i].retry_bitfield &= mptcp_local->loc6_bits; -+ } -+ } -+ -+ sk = mptcp_select_ack_sock(meta_sk, 0); -+ if (sk) -+ tcp_send_ack(sk); -+} -+ -+static int mptcp_find_address(struct mptcp_loc_addr *mptcp_local, -+ sa_family_t family, union inet_addr *addr) -+{ -+ int i; -+ u8 loc_bits; -+ bool found = false; -+ -+ if (family == AF_INET) -+ loc_bits = mptcp_local->loc4_bits; -+ else -+ loc_bits = mptcp_local->loc6_bits; -+ -+ mptcp_for_each_bit_set(loc_bits, i) { -+ if (family == AF_INET && -+ mptcp_local->locaddr4[i].addr.s_addr == addr->in.s_addr) { -+ found = true; -+ break; -+ } -+ if (family == AF_INET6 && -+ ipv6_addr_equal(&mptcp_local->locaddr6[i].addr, -+ &addr->in6)) { -+ found = true; -+ break; -+ } -+ } -+ -+ if (!found) -+ return -1; -+ -+ return i; -+} -+ -+static void mptcp_address_worker(struct work_struct *work) -+{ -+ struct delayed_work *delayed_work = container_of(work, -+ struct delayed_work, -+ work); -+ struct mptcp_fm_ns *fm_ns = container_of(delayed_work, -+ struct mptcp_fm_ns, -+ address_worker); -+ struct net *net = fm_ns->net; -+ struct mptcp_addr_event *event = NULL; -+ struct mptcp_loc_addr *mptcp_local, *old; -+ int i, id = -1; /* id is used in the socket-code on a delete-event */ -+ bool success; /* Used to indicate if we succeeded handling the event */ -+ -+next_event: -+ success = false; -+ kfree(event); -+ -+ /* First, let's dequeue an event from our event-list */ -+ rcu_read_lock_bh(); -+ spin_lock(&fm_ns->local_lock); -+ -+ event = list_first_entry_or_null(&fm_ns->events, -+ struct mptcp_addr_event, list); -+ if (!event) { -+ spin_unlock(&fm_ns->local_lock); -+ rcu_read_unlock_bh(); -+ return; -+ } -+ -+ list_del(&event->list); -+ -+ mptcp_local = rcu_dereference_bh(fm_ns->local); -+ -+ if (event->code == MPTCP_EVENT_DEL) { -+ id = mptcp_find_address(mptcp_local, event->family, &event->addr); -+ -+ /* Not in the list - so we don't care */ -+ if (id < 0) -+ goto duno; -+ -+ old = mptcp_local; -+ mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), -+ GFP_ATOMIC); -+ if (!mptcp_local) -+ goto duno; -+ -+ if (event->family == AF_INET) -+ mptcp_local->loc4_bits &= ~(1 << id); -+ else -+ mptcp_local->loc6_bits &= ~(1 << id); -+ -+ rcu_assign_pointer(fm_ns->local, mptcp_local); -+ kfree(old); -+ } else { -+ int i = mptcp_find_address(mptcp_local, event->family, &event->addr); -+ int j = i; -+ -+ if (j < 0) { -+ /* Not in the list, so we have to find an empty slot */ -+ if (event->family == AF_INET) -+ i = __mptcp_find_free_index(mptcp_local->loc4_bits, -1, -+ mptcp_local->next_v4_index); -+ if (event->family == AF_INET6) -+ i = __mptcp_find_free_index(mptcp_local->loc6_bits, -1, -+ mptcp_local->next_v6_index); -+ -+ if (i < 0) { -+ mptcp_debug("%s no more space\n", __func__); -+ goto duno; -+ } -+ -+ /* It might have been a MOD-event. */ -+ event->code = MPTCP_EVENT_ADD; -+ } else { -+ /* Let's check if anything changes */ -+ if (event->family == AF_INET && -+ event->low_prio == mptcp_local->locaddr4[i].low_prio) -+ goto duno; -+ -+ if (event->family == AF_INET6 && -+ event->low_prio == mptcp_local->locaddr6[i].low_prio) -+ goto duno; -+ } -+ -+ old = mptcp_local; -+ mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), -+ GFP_ATOMIC); -+ if (!mptcp_local) -+ goto duno; -+ -+ if (event->family == AF_INET) { -+ mptcp_local->locaddr4[i].addr.s_addr = event->addr.in.s_addr; -+ mptcp_local->locaddr4[i].loc4_id = i + 1; -+ mptcp_local->locaddr4[i].low_prio = event->low_prio; -+ } else { -+ mptcp_local->locaddr6[i].addr = event->addr.in6; -+ mptcp_local->locaddr6[i].loc6_id = i + MPTCP_MAX_ADDR; -+ mptcp_local->locaddr6[i].low_prio = event->low_prio; -+ } -+ -+ if (j < 0) { -+ if (event->family == AF_INET) { -+ mptcp_local->loc4_bits |= (1 << i); -+ mptcp_local->next_v4_index = i + 1; -+ } else { -+ mptcp_local->loc6_bits |= (1 << i); -+ mptcp_local->next_v6_index = i + 1; -+ } -+ } -+ -+ rcu_assign_pointer(fm_ns->local, mptcp_local); -+ kfree(old); -+ } -+ success = true; -+ -+duno: -+ spin_unlock(&fm_ns->local_lock); -+ rcu_read_unlock_bh(); -+ -+ if (!success) -+ goto next_event; -+ -+ /* Now we iterate over the MPTCP-sockets and apply the event. */ -+ for (i = 0; i < MPTCP_HASH_SIZE; i++) { -+ struct hlist_nulls_node *node; -+ struct tcp_sock *meta_tp; -+ -+ rcu_read_lock_bh(); -+ hlist_nulls_for_each_entry_rcu(meta_tp, node, &tk_hashtable[i], -+ tk_table) { -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ struct sock *meta_sk = (struct sock *)meta_tp, *sk; -+ struct fullmesh_priv *fmp = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; -+ -+ if (sock_net(meta_sk) != net) -+ continue; -+ -+ if (unlikely(!atomic_inc_not_zero(&meta_sk->sk_refcnt))) -+ continue; -+ -+ bh_lock_sock(meta_sk); -+ -+ if (!meta_tp->mpc || !is_meta_sk(meta_sk) || -+ mpcb->infinite_mapping_snd || -+ mpcb->infinite_mapping_rcv || -+ mpcb->send_infinite_mapping) -+ goto next; -+ -+ /* May be that the pm has changed in-between */ -+ if (mpcb->pm_ops != &full_mesh) -+ goto next; -+ -+ if (sock_owned_by_user(meta_sk)) { -+ if (!test_and_set_bit(MPTCP_PATH_MANAGER, -+ &meta_tp->tsq_flags)) -+ sock_hold(meta_sk); -+ -+ goto next; -+ } -+ -+ if (event->code == MPTCP_EVENT_ADD) { -+ if (event->family == AF_INET) -+ fmp->add_addr++; -+#if IS_ENABLED(CONFIG_IPV6) -+ if (event->family == AF_INET6) -+ fmp->add_addr++; -+#endif -+ -+ sk = mptcp_select_ack_sock(meta_sk, 0); -+ if (sk) -+ tcp_send_ack(sk); -+ -+ full_mesh_create_subflows(meta_sk); -+ } -+ -+ if (event->code == MPTCP_EVENT_DEL) { -+ struct sock *sk, *tmpsk; -+ struct mptcp_loc_addr *mptcp_local; -+ bool found = false; -+ -+ mptcp_local = rcu_dereference_bh(fm_ns->local); -+ -+ /* Look for the socket and remove him */ -+ mptcp_for_each_sk_safe(mpcb, sk, tmpsk) { -+ if ((event->family == AF_INET6 && -+ (sk->sk_family == AF_INET || -+ mptcp_v6_is_v4_mapped(sk))) || -+ (event->family == AF_INET && -+ (sk->sk_family == AF_INET6 && -+ !mptcp_v6_is_v4_mapped(sk)))) -+ continue; -+ -+ if (event->family == AF_INET && -+ (sk->sk_family == AF_INET || -+ mptcp_v6_is_v4_mapped(sk)) && -+ inet_sk(sk)->inet_saddr != event->addr.in.s_addr) -+ continue; -+ -+ if (event->family == AF_INET6 && -+ sk->sk_family == AF_INET6 && -+ !ipv6_addr_equal(&inet6_sk(sk)->saddr, &event->addr.in6)) -+ continue; -+ -+ /* Reinject, so that pf = 1 and so we -+ * won't select this one as the -+ * ack-sock. -+ */ -+ mptcp_reinject_data(sk, 0); -+ -+ /* A master is special, it has -+ * address-id 0 -+ */ -+ if (!tcp_sk(sk)->mptcp->loc_id) -+ update_remove_addrs(0, meta_sk, mptcp_local); -+ else if (tcp_sk(sk)->mptcp->loc_id != id) -+ update_remove_addrs(tcp_sk(sk)->mptcp->loc_id, meta_sk, mptcp_local); -+ -+ mptcp_sub_force_close(sk); -+ found = true; -+ } -+ -+ if (!found) -+ goto next; -+ -+ /* The id may have been given by the event, -+ * matching on a local address. And it may not -+ * have matched on one of the above sockets, -+ * because the client never created a subflow. -+ * So, we have to finally remove it here. -+ */ -+ if (id > 0) -+ update_remove_addrs(id, meta_sk, mptcp_local); -+ } -+ -+ if (event->code == MPTCP_EVENT_MOD) { -+ struct sock *sk; -+ -+ mptcp_for_each_sk(mpcb, sk) { -+ struct tcp_sock *tp = tcp_sk(sk); -+ if (event->family == AF_INET && -+ (sk->sk_family == AF_INET || -+ mptcp_v6_is_v4_mapped(sk)) && -+ inet_sk(sk)->inet_saddr == event->addr.in.s_addr) { -+ if (event->low_prio != tp->mptcp->low_prio) { -+ tp->mptcp->send_mp_prio = 1; -+ tp->mptcp->low_prio = event->low_prio; -+ -+ tcp_send_ack(sk); -+ } -+ } -+ -+ if (event->family == AF_INET6 && -+ sk->sk_family == AF_INET6 && -+ !ipv6_addr_equal(&inet6_sk(sk)->saddr, &event->addr.in6)) { -+ if (event->low_prio != tp->mptcp->low_prio) { -+ tp->mptcp->send_mp_prio = 1; -+ tp->mptcp->low_prio = event->low_prio; -+ -+ tcp_send_ack(sk); -+ } -+ } -+ } -+ } -+next: -+ bh_unlock_sock(meta_sk); -+ sock_put(meta_sk); -+ } -+ rcu_read_unlock_bh(); -+ } -+ goto next_event; -+} -+ -+static struct mptcp_addr_event *lookup_similar_event(struct net *net, -+ struct mptcp_addr_event *event) -+{ -+ struct mptcp_addr_event *eventq; -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); -+ -+ list_for_each_entry(eventq, &fm_ns->events, list) { -+ if (eventq->family != event->family) -+ continue; -+ if (event->family == AF_INET) { -+ if (eventq->addr.in.s_addr == event->addr.in.s_addr) -+ return eventq; -+ } else { -+ if (ipv6_addr_equal(&eventq->addr.in6, &event->addr.in6)) -+ return eventq; -+ } -+ } -+ return NULL; -+} -+ -+/* We already hold the net-namespace MPTCP-lock */ -+static void add_pm_event(struct net *net, struct mptcp_addr_event *event) -+{ -+ struct mptcp_addr_event *eventq = lookup_similar_event(net, event); -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); -+ -+ if (eventq) { -+ switch (event->code) { -+ case MPTCP_EVENT_DEL: -+ list_del(&eventq->list); -+ kfree(eventq); -+ break; -+ case MPTCP_EVENT_ADD: -+ eventq->low_prio = event->low_prio; -+ eventq->code = MPTCP_EVENT_ADD; -+ return; -+ case MPTCP_EVENT_MOD: -+ eventq->low_prio = event->low_prio; -+ return; -+ } -+ } -+ -+ /* OK, we have to add the new address to the wait queue */ -+ eventq = kmemdup(event, sizeof(struct mptcp_addr_event), GFP_ATOMIC); -+ if (!eventq) -+ return; -+ -+ list_add_tail(&eventq->list, &fm_ns->events); -+ -+ /* Create work-queue */ -+ if (!delayed_work_pending(&fm_ns->address_worker)) -+ queue_delayed_work(mptcp_wq, &fm_ns->address_worker, -+ msecs_to_jiffies(500)); -+} -+ -+static void addr4_event_handler(struct in_ifaddr *ifa, unsigned long event, -+ struct net *net) -+{ -+ struct net_device *netdev = ifa->ifa_dev->dev; -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); -+ struct mptcp_addr_event mpevent; -+ -+ if (ifa->ifa_scope > RT_SCOPE_LINK || -+ ipv4_is_loopback(ifa->ifa_local)) -+ return; -+ -+ spin_lock_bh(&fm_ns->local_lock); -+ -+ mpevent.family = AF_INET; -+ mpevent.addr.in.s_addr = ifa->ifa_local; -+ mpevent.low_prio = (netdev->flags & IFF_MPBACKUP) ? 1 : 0; -+ -+ if (event == NETDEV_DOWN || !netif_running(netdev) || -+ (netdev->flags & IFF_NOMULTIPATH)) -+ mpevent.code = MPTCP_EVENT_DEL; -+ else if (event == NETDEV_UP) -+ mpevent.code = MPTCP_EVENT_ADD; -+ else if (event == NETDEV_CHANGE) -+ mpevent.code = MPTCP_EVENT_MOD; -+ -+ add_pm_event(net, &mpevent); -+ -+ spin_unlock_bh(&fm_ns->local_lock); -+ return; -+} -+ -+/* React on IPv4-addr add/rem-events */ -+static int mptcp_pm_inetaddr_event(struct notifier_block *this, -+ unsigned long event, void *ptr) -+{ -+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; -+ struct net *net = dev_net(ifa->ifa_dev->dev); -+ -+ addr4_event_handler(ifa, event, net); -+ -+ return NOTIFY_DONE; -+} -+ -+static struct notifier_block mptcp_pm_inetaddr_notifier = { -+ .notifier_call = mptcp_pm_inetaddr_event, -+}; -+ -+#if IS_ENABLED(CONFIG_IPV6) -+ -+/* IPV6-related address/interface watchers */ -+struct mptcp_dad_data { -+ struct timer_list timer; -+ struct inet6_ifaddr *ifa; -+}; -+ -+static void dad_callback(unsigned long arg); -+static int inet6_addr_event(struct notifier_block *this, -+ unsigned long event, void *ptr); -+ -+static int ipv6_is_in_dad_state(struct inet6_ifaddr *ifa) -+{ -+ return ((ifa->flags & IFA_F_TENTATIVE) && -+ ifa->state == INET6_IFADDR_STATE_DAD); -+} -+ -+static void dad_init_timer(struct mptcp_dad_data *data, -+ struct inet6_ifaddr *ifa) -+{ -+ data->ifa = ifa; -+ data->timer.data = (unsigned long)data; -+ data->timer.function = dad_callback; -+ if (ifa->idev->cnf.rtr_solicit_delay) -+ data->timer.expires = jiffies + ifa->idev->cnf.rtr_solicit_delay; -+ else -+ data->timer.expires = jiffies + (HZ/10); -+} -+ -+static void dad_callback(unsigned long arg) -+{ -+ struct mptcp_dad_data *data = (struct mptcp_dad_data *)arg; -+ -+ if (ipv6_is_in_dad_state(data->ifa)) { -+ dad_init_timer(data, data->ifa); -+ add_timer(&data->timer); -+ } else { -+ inet6_addr_event(NULL, NETDEV_UP, data->ifa); -+ in6_ifa_put(data->ifa); -+ kfree(data); -+ } -+} -+ -+static inline void dad_setup_timer(struct inet6_ifaddr *ifa) -+{ -+ struct mptcp_dad_data *data; -+ -+ data = kmalloc(sizeof(*data), GFP_ATOMIC); -+ -+ if (!data) -+ return; -+ -+ init_timer(&data->timer); -+ dad_init_timer(data, ifa); -+ add_timer(&data->timer); -+ in6_ifa_hold(ifa); -+} -+ -+static void addr6_event_handler(struct inet6_ifaddr *ifa, unsigned long event, -+ struct net *net) -+{ -+ struct net_device *netdev = ifa->idev->dev; -+ int addr_type = ipv6_addr_type(&ifa->addr); -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); -+ struct mptcp_addr_event mpevent; -+ -+ if (ifa->scope > RT_SCOPE_LINK || -+ addr_type == IPV6_ADDR_ANY || -+ (addr_type & IPV6_ADDR_LOOPBACK) || -+ (addr_type & IPV6_ADDR_LINKLOCAL)) -+ return; -+ -+ spin_lock_bh(&fm_ns->local_lock); -+ -+ mpevent.family = AF_INET6; -+ mpevent.addr.in6 = ifa->addr; -+ mpevent.low_prio = (netdev->flags & IFF_MPBACKUP) ? 1 : 0; -+ -+ if (event == NETDEV_DOWN ||!netif_running(netdev) || -+ (netdev->flags & IFF_NOMULTIPATH)) -+ mpevent.code = MPTCP_EVENT_DEL; -+ else if (event == NETDEV_UP) -+ mpevent.code = MPTCP_EVENT_ADD; -+ else if (event == NETDEV_CHANGE) -+ mpevent.code = MPTCP_EVENT_MOD; -+ -+ add_pm_event(net, &mpevent); -+ -+ spin_unlock_bh(&fm_ns->local_lock); -+ return; -+} -+ -+/* React on IPv6-addr add/rem-events */ -+static int inet6_addr_event(struct notifier_block *this, unsigned long event, -+ void *ptr) -+{ -+ struct inet6_ifaddr *ifa6 = (struct inet6_ifaddr *)ptr; -+ struct net *net = dev_net(ifa6->idev->dev); -+ -+ if (ipv6_is_in_dad_state(ifa6)) -+ dad_setup_timer(ifa6); -+ else -+ addr6_event_handler(ifa6, event, net); -+ -+ return NOTIFY_DONE; -+} -+ -+static struct notifier_block inet6_addr_notifier = { -+ .notifier_call = inet6_addr_event, -+}; -+ -+#endif -+ -+/* React on ifup/down-events */ -+static int netdev_event(struct notifier_block *this, unsigned long event, -+ void *ptr) -+{ -+ struct net_device *dev = netdev_notifier_info_to_dev(ptr); -+ struct in_device *in_dev; -+#if IS_ENABLED(CONFIG_IPV6) -+ struct inet6_dev *in6_dev; -+#endif -+ -+ if (!(event == NETDEV_UP || event == NETDEV_DOWN || -+ event == NETDEV_CHANGE)) -+ return NOTIFY_DONE; -+ -+ rcu_read_lock(); -+ in_dev = __in_dev_get_rtnl(dev); -+ -+ if (in_dev) { -+ for_ifa(in_dev) { -+ mptcp_pm_inetaddr_event(NULL, event, ifa); -+ } endfor_ifa(in_dev); -+ } -+ -+#if IS_ENABLED(CONFIG_IPV6) -+ in6_dev = __in6_dev_get(dev); -+ -+ if (in6_dev) { -+ struct inet6_ifaddr *ifa6; -+ list_for_each_entry(ifa6, &in6_dev->addr_list, if_list) -+ inet6_addr_event(NULL, event, ifa6); -+ } -+#endif -+ -+ rcu_read_unlock(); -+ return NOTIFY_DONE; -+} -+ -+static struct notifier_block mptcp_pm_netdev_notifier = { -+ .notifier_call = netdev_event, -+}; -+ -+static void full_mesh_new_session(struct sock *meta_sk, int index) -+{ -+ struct mptcp_loc_addr *mptcp_local; -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct fullmesh_priv *fmp = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; -+ struct net *net = sock_net(meta_sk); -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); -+ struct sock *sk; -+ int i; -+ -+ if (index == -1) { -+ mptcp_fallback_default(mpcb); -+ return; -+ } -+ -+ /* Initialize workqueue-struct */ -+ INIT_WORK(&fmp->subflow_work, create_subflow_worker); -+ INIT_DELAYED_WORK(&fmp->subflow_retry_work, retry_subflow_worker); -+ fmp->mpcb = mpcb; -+ -+ sk = mptcp_select_ack_sock(meta_sk, 0); -+ -+ rcu_read_lock(); -+ mptcp_local = rcu_dereference(fm_ns->local); -+ -+ /* Look for the address among the local addresses */ -+ mptcp_for_each_bit_set(mptcp_local->loc4_bits, i) { -+ __be32 ifa_address = mptcp_local->locaddr4[i].addr.s_addr; -+ -+ /* We do not need to announce the initial subflow's address again */ -+ if ((meta_sk->sk_family == AF_INET || -+ mptcp_v6_is_v4_mapped(meta_sk)) && -+ inet_sk(meta_sk)->inet_saddr == ifa_address) -+ continue; -+ -+ fmp->add_addr++; -+ -+ if (sk) -+ tcp_send_ack(sk); -+ } -+ -+#if IS_ENABLED(CONFIG_IPV6) -+ mptcp_for_each_bit_set(mptcp_local->loc6_bits, i) { -+ struct in6_addr *ifa6 = &mptcp_local->locaddr6[i].addr; -+ -+ /* We do not need to announce the initial subflow's address again */ -+ if (meta_sk->sk_family == AF_INET6 && -+ ipv6_addr_equal(&inet6_sk(meta_sk)->saddr, ifa6)) -+ continue; -+ -+ fmp->add_addr++; -+ -+ if (sk) -+ tcp_send_ack(sk); -+ } -+#endif -+ -+ rcu_read_unlock(); -+ -+ if (meta_sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(meta_sk)) -+ fmp->announced_addrs_v4 |= (1 << index); -+ else -+ fmp->announced_addrs_v6 |= (1 << index); -+} -+ -+static void full_mesh_create_subflows(struct sock *meta_sk) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct fullmesh_priv *pm_priv = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; -+ -+ if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv || -+ mpcb->send_infinite_mapping || -+ mpcb->server_side || sock_flag(meta_sk, SOCK_DEAD)) -+ return; -+ -+ /* The master may not yet be fully established (address added through -+ * mptcp_update_metasocket). Then, we should not attempt to create new -+ * subflows. -+ */ -+ if (mpcb->master_sk && -+ !tcp_sk(mpcb->master_sk)->mptcp->fully_established) -+ return; -+ -+ if (!work_pending(&pm_priv->subflow_work)) { -+ sock_hold(meta_sk); -+ queue_work(mptcp_wq, &pm_priv->subflow_work); -+ } -+} -+ -+/* Called upon release_sock, if the socket was owned by the user during -+ * a path-management event. -+ */ -+static void full_mesh_release_sock(struct sock *meta_sk) -+{ -+ struct mptcp_loc_addr *mptcp_local; -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct fullmesh_priv *fmp = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(meta_sk)); -+ struct sock *sk, *tmpsk; -+ int i; -+ -+ rcu_read_lock(); -+ mptcp_local = rcu_dereference(fm_ns->local); -+ -+ /* First, detect modifications or additions */ -+ mptcp_for_each_bit_set(mptcp_local->loc4_bits, i) { -+ struct in_addr ifa = mptcp_local->locaddr4[i].addr; -+ bool found = false; -+ -+ mptcp_for_each_sk(mpcb, sk) { -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ if (sk->sk_family == AF_INET6 && -+ !mptcp_v6_is_v4_mapped(sk)) -+ continue; -+ -+ if (inet_sk(sk)->inet_saddr != ifa.s_addr) -+ continue; -+ -+ found = true; -+ -+ if (mptcp_local->locaddr4[i].low_prio != tp->mptcp->low_prio) { -+ tp->mptcp->send_mp_prio = 1; -+ tp->mptcp->low_prio = mptcp_local->locaddr4[i].low_prio; -+ -+ tcp_send_ack(sk); -+ } -+ } -+ -+ if (!found) { -+ fmp->add_addr++; -+ -+ sk = mptcp_select_ack_sock(meta_sk, 0); -+ if (sk) -+ tcp_send_ack(sk); -+ full_mesh_create_subflows(meta_sk); -+ } -+ } -+ -+#if IS_ENABLED(CONFIG_IPV6) -+ mptcp_for_each_bit_set(mptcp_local->loc6_bits, i) { -+ struct in6_addr ifa = mptcp_local->locaddr6[i].addr; -+ bool found = false; -+ -+ mptcp_for_each_sk(mpcb, sk) { -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ if (sk->sk_family == AF_INET || -+ mptcp_v6_is_v4_mapped(sk)) -+ continue; -+ -+ if (!ipv6_addr_equal(&inet6_sk(sk)->saddr, &ifa)) -+ continue; -+ -+ found = true; -+ -+ if (mptcp_local->locaddr6[i].low_prio != tp->mptcp->low_prio) { -+ tp->mptcp->send_mp_prio = 1; -+ tp->mptcp->low_prio = mptcp_local->locaddr6[i].low_prio; -+ -+ tcp_send_ack(sk); -+ } -+ } -+ -+ if (!found) { -+ fmp->add_addr++; -+ -+ sk = mptcp_select_ack_sock(meta_sk, 0); -+ if (sk) -+ tcp_send_ack(sk); -+ full_mesh_create_subflows(meta_sk); -+ } -+ } -+#endif -+ -+ /* Now, detect address-removals */ -+ mptcp_for_each_sk_safe(mpcb, sk, tmpsk) { -+ bool shall_remove = true; -+ -+ if (sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(sk)) { -+ mptcp_for_each_bit_set(mptcp_local->loc4_bits, i) { -+ if (inet_sk(sk)->inet_saddr == mptcp_local->locaddr4[i].addr.s_addr) { -+ shall_remove = false; -+ break; -+ } -+ } -+ } else { -+ mptcp_for_each_bit_set(mptcp_local->loc6_bits, i) { -+ if (ipv6_addr_equal(&inet6_sk(sk)->saddr, &mptcp_local->locaddr6[i].addr)) { -+ shall_remove = false; -+ break; -+ } -+ } -+ } -+ -+ if (shall_remove) { -+ /* Reinject, so that pf = 1 and so we -+ * won't select this one as the -+ * ack-sock. -+ */ -+ mptcp_reinject_data(sk, 0); -+ -+ update_remove_addrs(tcp_sk(sk)->mptcp->loc_id, meta_sk, -+ mptcp_local); -+ -+ if (mpcb->master_sk == sk) -+ update_remove_addrs(0, meta_sk, mptcp_local); -+ -+ mptcp_sub_force_close(sk); -+ } -+ } -+ rcu_read_unlock(); -+} -+ -+static int full_mesh_get_local_index(sa_family_t family, union inet_addr *addr, -+ struct net *net) -+{ -+ struct mptcp_loc_addr *mptcp_local; -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); -+ int index; -+ -+ /* Handle the backup-flows */ -+ rcu_read_lock(); -+ mptcp_local = rcu_dereference(fm_ns->local); -+ -+ index = mptcp_find_address(mptcp_local, family, addr); -+ -+ rcu_read_unlock(); -+ -+ return index; -+} -+ -+static int full_mesh_get_local_id(sa_family_t family, union inet_addr *addr, -+ struct net *net) -+{ -+ struct mptcp_loc_addr *mptcp_local; -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); -+ int index, id = -1; -+ -+ /* Handle the backup-flows */ -+ rcu_read_lock(); -+ mptcp_local = rcu_dereference(fm_ns->local); -+ -+ index = mptcp_find_address(mptcp_local, family, addr); -+ -+ if (index != -1) { -+ if (family == AF_INET) -+ id = mptcp_local->locaddr4[index].loc4_id; -+ else -+ id = mptcp_local->locaddr6[index].loc6_id; -+ } -+ -+ -+ rcu_read_unlock(); -+ -+ return id; -+} -+ -+static void full_mesh_addr_signal(struct sock *sk, unsigned *size, -+ struct tcp_out_options *opts, -+ struct sk_buff *skb) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct mptcp_cb *mpcb = tp->mpcb; -+ struct fullmesh_priv *fmp = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; -+ struct mptcp_loc_addr *mptcp_local; -+ struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(sk)); -+ int remove_addr_len; -+ u8 unannouncedv4, unannouncedv6; -+ -+ if (likely(!fmp->add_addr)) -+ goto remove_addr; -+ -+ rcu_read_lock(); -+ mptcp_local = rcu_dereference(fm_ns->local); -+ -+ /* IPv4 */ -+ unannouncedv4 = (~fmp->announced_addrs_v4) & mptcp_local->loc4_bits; -+ if (unannouncedv4 && -+ MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_ADD_ADDR4_ALIGN) { -+ int ind = mptcp_find_free_index(~unannouncedv4); -+ -+ opts->options |= OPTION_MPTCP; -+ opts->mptcp_options |= OPTION_ADD_ADDR; -+ opts->add_addr4.addr_id = mptcp_local->locaddr4[ind].loc4_id; -+ opts->add_addr4.addr = mptcp_local->locaddr4[ind].addr; -+ opts->add_addr_v4 = 1; -+ -+ if (skb) { -+ fmp->announced_addrs_v4 |= (1 << ind); -+ fmp->add_addr--; -+ } -+ *size += MPTCP_SUB_LEN_ADD_ADDR4_ALIGN; -+ } -+ -+ /* IPv6 */ -+ unannouncedv6 = (~fmp->announced_addrs_v6) & mptcp_local->loc6_bits; -+ if (unannouncedv6 && -+ MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_ADD_ADDR6_ALIGN) { -+ int ind = mptcp_find_free_index(~unannouncedv6); -+ -+ opts->options |= OPTION_MPTCP; -+ opts->mptcp_options |= OPTION_ADD_ADDR; -+ opts->add_addr6.addr_id = mptcp_local->locaddr6[ind].loc6_id; -+ opts->add_addr6.addr = mptcp_local->locaddr6[ind].addr; -+ opts->add_addr_v6 = 1; -+ -+ if (skb) { -+ fmp->announced_addrs_v6 |= (1 << ind); -+ fmp->add_addr--; -+ } -+ *size += MPTCP_SUB_LEN_ADD_ADDR6_ALIGN; -+ } -+ -+ rcu_read_unlock(); -+ -+ if (!unannouncedv4 && !unannouncedv6 && skb) { -+ fmp->add_addr--; -+ } -+ -+remove_addr: -+ if (likely(!fmp->remove_addrs)) -+ return; -+ -+ remove_addr_len = mptcp_sub_len_remove_addr_align(fmp->remove_addrs); -+ if (MAX_TCP_OPTION_SPACE - *size < remove_addr_len) -+ return; -+ -+ opts->options |= OPTION_MPTCP; -+ opts->mptcp_options |= OPTION_REMOVE_ADDR; -+ opts->remove_addrs = fmp->remove_addrs; -+ *size += remove_addr_len; -+ if (skb) -+ fmp->remove_addrs = 0; -+} -+ -+static int mptcp_fm_init_net(struct net *net) -+{ -+ struct mptcp_loc_addr *mptcp_local; -+ struct mptcp_fm_ns *fm_ns; -+ -+ fm_ns = kzalloc(sizeof(*fm_ns), GFP_KERNEL); -+ if (!fm_ns) -+ return -ENOBUFS; -+ -+ mptcp_local = kzalloc(sizeof(*mptcp_local), GFP_KERNEL); -+ if (!mptcp_local) { -+ kfree(fm_ns); -+ return -ENOBUFS; -+ } -+ -+ mptcp_local->next_v4_index = 1; -+ -+ rcu_assign_pointer(fm_ns->local, mptcp_local); -+ INIT_DELAYED_WORK(&fm_ns->address_worker, mptcp_address_worker); -+ INIT_LIST_HEAD(&fm_ns->events); -+ spin_lock_init(&fm_ns->local_lock); -+ fm_ns->net = net; -+ net->mptcp.path_managers[MPTCP_PM_FULLMESH] = fm_ns; -+ -+ return 0; -+} -+ -+static void mptcp_fm_exit_net(struct net *net) -+{ -+ struct mptcp_addr_event *eventq, *tmp; -+ struct mptcp_fm_ns *fm_ns; -+ struct mptcp_loc_addr *mptcp_local; -+ -+ fm_ns = fm_get_ns(net); -+ cancel_delayed_work_sync(&fm_ns->address_worker); -+ -+ rcu_read_lock_bh(); -+ -+ mptcp_local = rcu_dereference_bh(fm_ns->local); -+ kfree(mptcp_local); -+ -+ spin_lock(&fm_ns->local_lock); -+ list_for_each_entry_safe(eventq, tmp, &fm_ns->events, list) { -+ list_del(&eventq->list); -+ kfree(eventq); -+ } -+ spin_unlock(&fm_ns->local_lock); -+ -+ rcu_read_unlock_bh(); -+ -+ kfree(fm_ns); -+} -+ -+static struct pernet_operations full_mesh_net_ops = { -+ .init = mptcp_fm_init_net, -+ .exit = mptcp_fm_exit_net, -+}; -+ -+static struct mptcp_pm_ops full_mesh __read_mostly = { -+ .new_session = full_mesh_new_session, -+ .release_sock = full_mesh_release_sock, -+ .fully_established = full_mesh_create_subflows, -+ .new_remote_address = full_mesh_create_subflows, -+ .get_local_index = full_mesh_get_local_index, -+ .get_local_id = full_mesh_get_local_id, -+ .addr_signal = full_mesh_addr_signal, -+ .name = "fullmesh", -+ .owner = THIS_MODULE, -+}; -+ -+/* General initialization of MPTCP_PM */ -+static int __init full_mesh_register(void) -+{ -+ int ret; -+ -+ BUILD_BUG_ON(sizeof(struct fullmesh_priv) > MPTCP_PM_SIZE); -+ -+ ret = register_pernet_subsys(&full_mesh_net_ops); -+ if (ret) -+ goto out; -+ -+ ret = register_inetaddr_notifier(&mptcp_pm_inetaddr_notifier); -+ if (ret) -+ goto err_reg_inetaddr; -+ ret = register_netdevice_notifier(&mptcp_pm_netdev_notifier); -+ if (ret) -+ goto err_reg_netdev; -+ -+#if IS_ENABLED(CONFIG_IPV6) -+ ret = register_inet6addr_notifier(&inet6_addr_notifier); -+ if (ret) -+ goto err_reg_inet6addr; -+#endif -+ -+ ret = mptcp_register_path_manager(&full_mesh); -+ if (ret) -+ goto err_reg_pm; -+ -+out: -+ return ret; -+ -+ -+err_reg_pm: -+#if IS_ENABLED(CONFIG_IPV6) -+ unregister_inet6addr_notifier(&inet6_addr_notifier); -+err_reg_inet6addr: -+#endif -+ unregister_netdevice_notifier(&mptcp_pm_netdev_notifier); -+err_reg_netdev: -+ unregister_inetaddr_notifier(&mptcp_pm_inetaddr_notifier); -+err_reg_inetaddr: -+ unregister_pernet_subsys(&full_mesh_net_ops); -+ goto out; -+} -+ -+static void full_mesh_unregister(void) -+{ -+#if IS_ENABLED(CONFIG_IPV6) -+ unregister_inet6addr_notifier(&inet6_addr_notifier); -+#endif -+ unregister_netdevice_notifier(&mptcp_pm_netdev_notifier); -+ unregister_inetaddr_notifier(&mptcp_pm_inetaddr_notifier); -+ unregister_pernet_subsys(&full_mesh_net_ops); -+ mptcp_unregister_path_manager(&full_mesh); -+} -+ -+module_init(full_mesh_register); -+module_exit(full_mesh_unregister); -+ -+MODULE_AUTHOR("Christoph Paasch"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Full-Mesh MPTCP"); -+MODULE_VERSION("0.88"); -diff --git a/net/mptcp/mptcp_input.c b/net/mptcp/mptcp_input.c -new file mode 100644 -index 0000000..f3c9057 ---- /dev/null -+++ b/net/mptcp/mptcp_input.c -@@ -0,0 +1,2254 @@ -+/* -+ * MPTCP implementation - Sending side -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer & Author: -+ * Christoph Paasch -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include -+ -+#include -+#include -+#include -+ -+#include -+ -+/* is seq1 < seq2 ? */ -+static inline int before64(const u64 seq1, const u64 seq2) -+{ -+ return (s64)(seq1 - seq2) < 0; -+} -+ -+/* is seq1 > seq2 ? */ -+#define after64(seq1, seq2) before64(seq2, seq1) -+ -+static inline void mptcp_become_fully_estab(struct sock *sk) -+{ -+ tcp_sk(sk)->mptcp->fully_established = 1; -+ -+ if (is_master_tp(tcp_sk(sk)) && -+ tcp_sk(sk)->mpcb->pm_ops->fully_established) -+ tcp_sk(sk)->mpcb->pm_ops->fully_established(mptcp_meta_sk(sk)); -+} -+ -+/* Similar to tcp_tso_acked without any memory accounting */ -+static inline int mptcp_tso_acked_reinject(struct sock *sk, struct sk_buff *skb) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ u32 packets_acked, len; -+ -+ BUG_ON(!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una)); -+ -+ packets_acked = tcp_skb_pcount(skb); -+ -+ if (skb_unclone(skb, GFP_ATOMIC)) -+ return 0; -+ -+ len = tp->snd_una - TCP_SKB_CB(skb)->seq; -+ __pskb_trim_head(skb, len); -+ -+ TCP_SKB_CB(skb)->seq += len; -+ skb->ip_summed = CHECKSUM_PARTIAL; -+ skb->truesize -= len; -+ -+ /* Any change of skb->len requires recalculation of tso factor. */ -+ if (tcp_skb_pcount(skb) > 1) -+ tcp_set_skb_tso_segs(sk, skb, tcp_skb_mss(skb)); -+ packets_acked -= tcp_skb_pcount(skb); -+ -+ if (packets_acked) { -+ BUG_ON(tcp_skb_pcount(skb) == 0); -+ BUG_ON(!before(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)); -+ } -+ -+ return packets_acked; -+} -+ -+/** -+ * Cleans the meta-socket retransmission queue and the reinject-queue. -+ * @sk must be the metasocket. -+ */ -+static void mptcp_clean_rtx_queue(struct sock *meta_sk, u32 prior_snd_una) -+{ -+ struct sk_buff *skb, *tmp; -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ bool acked = false; -+ u32 acked_pcount; -+ -+ while ((skb = tcp_write_queue_head(meta_sk)) && -+ skb != tcp_send_head(meta_sk)) { -+ bool fully_acked = true; -+ -+ if (before(meta_tp->snd_una, TCP_SKB_CB(skb)->end_seq)) { -+ if (tcp_skb_pcount(skb) == 1 || -+ !after(meta_tp->snd_una, TCP_SKB_CB(skb)->seq)) -+ break; -+ -+ acked_pcount = tcp_tso_acked(meta_sk, skb); -+ if (!acked_pcount) -+ break; -+ -+ fully_acked = false; -+ } else { -+ acked_pcount = tcp_skb_pcount(skb); -+ } -+ -+ acked = true; -+ meta_tp->packets_out -= acked_pcount; -+ meta_tp->retrans_stamp = 0; -+ -+ if (!fully_acked) -+ break; -+ -+ tcp_unlink_write_queue(skb, meta_sk); -+ -+ if (mptcp_is_data_fin(skb)) { -+ struct sock *sk_it; -+ -+ /* DATA_FIN has been acknowledged - now we can close -+ * the subflows -+ */ -+ mptcp_for_each_sk(mpcb, sk_it) { -+ unsigned long delay = 0; -+ -+ /* If we are the passive closer, don't trigger -+ * subflow-fin until the subflow has been finned -+ * by the peer - thus we add a delay. -+ */ -+ if (mpcb->passive_close && -+ sk_it->sk_state == TCP_ESTABLISHED) -+ delay = inet_csk(sk_it)->icsk_rto << 3; -+ -+ mptcp_sub_close(sk_it, delay); -+ } -+ } -+ sk_wmem_free_skb(meta_sk, skb); -+ } -+ /* Remove acknowledged data from the reinject queue */ -+ skb_queue_walk_safe(&mpcb->reinject_queue, skb, tmp) { -+ if (before(meta_tp->snd_una, TCP_SKB_CB(skb)->end_seq)) { -+ if (tcp_skb_pcount(skb) == 1 || -+ !after(meta_tp->snd_una, TCP_SKB_CB(skb)->seq)) -+ break; -+ -+ mptcp_tso_acked_reinject(meta_sk, skb); -+ break; -+ } -+ -+ __skb_unlink(skb, &mpcb->reinject_queue); -+ __kfree_skb(skb); -+ } -+ -+ if (likely(between(meta_tp->snd_up, prior_snd_una, meta_tp->snd_una))) -+ meta_tp->snd_up = meta_tp->snd_una; -+ -+ if (acked) { -+ tcp_rearm_rto(meta_sk); -+ /* Normally this is done in tcp_try_undo_loss - but MPTCP -+ * does not call this function. -+ */ -+ inet_csk(meta_sk)->icsk_retransmits = 0; -+ } -+} -+ -+/* Inspired by tcp_rcv_state_process */ -+static int mptcp_rcv_state_process(struct sock *meta_sk, struct sock *sk, -+ const struct sk_buff *skb, u32 data_seq, -+ u16 data_len) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk), *tp = tcp_sk(sk); -+ struct tcphdr *th = tcp_hdr(skb); -+ -+ /* State-machine handling if FIN has been enqueued and he has -+ * been acked (snd_una == write_seq) - it's important that this -+ * here is after sk_wmem_free_skb because otherwise -+ * sk_forward_alloc is wrong upon inet_csk_destroy_sock() -+ */ -+ switch (meta_sk->sk_state) { -+ case TCP_FIN_WAIT1: -+ if (meta_tp->snd_una == meta_tp->write_seq) { -+ struct dst_entry *dst = __sk_dst_get(meta_sk); -+ -+ tcp_set_state(meta_sk, TCP_FIN_WAIT2); -+ meta_sk->sk_shutdown |= SEND_SHUTDOWN; -+ -+ dst = __sk_dst_get(sk); -+ if (dst) -+ dst_confirm(dst); -+ -+ if (!sock_flag(meta_sk, SOCK_DEAD)) { -+ /* Wake up lingering close() */ -+ meta_sk->sk_state_change(meta_sk); -+ } else { -+ int tmo; -+ -+ if (meta_tp->linger2 < 0 || -+ (data_len && -+ after(data_seq + data_len - (mptcp_is_data_fin2(skb, tp) ? 1 : 0), -+ meta_tp->rcv_nxt))) { -+ mptcp_send_active_reset(meta_sk, GFP_ATOMIC); -+ tcp_done(meta_sk); -+ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_TCPABORTONDATA); -+ return 1; -+ } -+ -+ tmo = tcp_fin_time(meta_sk); -+ if (tmo > TCP_TIMEWAIT_LEN) { -+ inet_csk_reset_keepalive_timer(meta_sk, tmo - TCP_TIMEWAIT_LEN); -+ } else if (mptcp_is_data_fin2(skb, tp) || -+ sock_owned_by_user(meta_sk)) { -+ /* Bad case. We could lose such FIN otherwise. -+ * It is not a big problem, but it looks confusing -+ * and not so rare event. We still can lose it now, -+ * if it spins in bh_lock_sock(), but it is really -+ * marginal case. -+ */ -+ inet_csk_reset_keepalive_timer(meta_sk, tmo); -+ } else { -+ tcp_time_wait(meta_sk, TCP_FIN_WAIT2, tmo); -+ } -+ } -+ } -+ break; -+ case TCP_CLOSING: -+ case TCP_LAST_ACK: -+ if (meta_tp->snd_una == meta_tp->write_seq) { -+ tcp_done(meta_sk); -+ return 1; -+ } -+ break; -+ } -+ -+ /* step 7: process the segment text */ -+ switch (meta_sk->sk_state) { -+ case TCP_FIN_WAIT1: -+ case TCP_FIN_WAIT2: -+ /* RFC 793 says to queue data in these states, -+ * RFC 1122 says we MUST send a reset. -+ * BSD 4.4 also does reset. -+ */ -+ if (meta_sk->sk_shutdown & RCV_SHUTDOWN) { -+ if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && -+ after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt) && -+ !mptcp_is_data_fin2(skb, tp)) { -+ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_TCPABORTONDATA); -+ mptcp_send_active_reset(meta_sk, GFP_ATOMIC); -+ tcp_reset(meta_sk); -+ return 1; -+ } -+ } -+ break; -+ } -+ -+ return 0; -+} -+ -+/** -+ * @return: -+ * i) 1: Everything's fine. -+ * ii) -1: A reset has been sent on the subflow - csum-failure -+ * iii) 0: csum-failure but no reset sent, because it's the last subflow. -+ * Last packet should not be destroyed by the caller because it has -+ * been done here. -+ */ -+static int mptcp_verif_dss_csum(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct sk_buff *tmp, *tmp1, *last = NULL; -+ __wsum csum_tcp = 0; /* cumulative checksum of pld + mptcp-header */ -+ int ans = 1, overflowed = 0, offset = 0, dss_csum_added = 0; -+ int iter = 0; -+ -+ skb_queue_walk_safe(&sk->sk_receive_queue, tmp, tmp1) { -+ unsigned int csum_len; -+ -+ if (before(tp->mptcp->map_subseq + tp->mptcp->map_data_len, TCP_SKB_CB(tmp)->end_seq)) -+ /* Mapping ends in the middle of the packet - -+ * csum only these bytes -+ */ -+ csum_len = tp->mptcp->map_subseq + tp->mptcp->map_data_len - TCP_SKB_CB(tmp)->seq; -+ else -+ csum_len = tmp->len; -+ -+ offset = 0; -+ if (overflowed) { -+ char first_word[4]; -+ first_word[0] = 0; -+ first_word[1] = 0; -+ first_word[2] = 0; -+ first_word[3] = *(tmp->data); -+ csum_tcp = csum_partial(first_word, 4, csum_tcp); -+ offset = 1; -+ csum_len--; -+ overflowed = 0; -+ } -+ -+ csum_tcp = skb_checksum(tmp, offset, csum_len, csum_tcp); -+ -+ /* Was it on an odd-length? Then we have to merge the next byte -+ * correctly (see above) -+ */ -+ if (csum_len != (csum_len & (~1))) -+ overflowed = 1; -+ -+ if (mptcp_is_data_seq(tmp) && !dss_csum_added) { -+ __be32 data_seq = htonl((u32)(tp->mptcp->map_data_seq >> 32)); -+ -+ /* If a 64-bit dss is present, we increase the offset -+ * by 4 bytes, as the high-order 64-bits will be added -+ * in the final csum_partial-call. -+ */ -+ u32 offset = skb_transport_offset(tmp) + -+ TCP_SKB_CB(tmp)->dss_off; -+ if (TCP_SKB_CB(tmp)->mptcp_flags & MPTCPHDR_SEQ64_SET) -+ offset += 4; -+ -+ csum_tcp = skb_checksum(tmp, offset, -+ MPTCP_SUB_LEN_SEQ_CSUM, -+ csum_tcp); -+ -+ csum_tcp = csum_partial(&data_seq, -+ sizeof(data_seq), csum_tcp); -+ -+ dss_csum_added = 1; /* Just do it once */ -+ } -+ last = tmp; -+ iter++; -+ -+ if (!skb_queue_is_last(&sk->sk_receive_queue, tmp) && -+ !before(TCP_SKB_CB(tmp1)->seq, -+ tp->mptcp->map_subseq + tp->mptcp->map_data_len)) -+ break; -+ } -+ -+ /* Now, checksum must be 0 */ -+ if (unlikely(csum_fold(csum_tcp))) { -+ pr_err("%s csum is wrong: %#x data_seq %u dss_csum_added %d overflowed %d iterations %d\n", -+ __func__, csum_fold(csum_tcp), -+ TCP_SKB_CB(last)->seq, dss_csum_added, overflowed, -+ iter); -+ -+ tp->mptcp->send_mp_fail = 1; -+ -+ /* map_data_seq is the data-seq number of the -+ * mapping we are currently checking -+ */ -+ tp->mpcb->csum_cutoff_seq = tp->mptcp->map_data_seq; -+ -+ if (tp->mpcb->cnt_subflows > 1) { -+ mptcp_send_reset(sk); -+ ans = -1; -+ } else { -+ tp->mpcb->send_infinite_mapping = 1; -+ -+ /* Need to purge the rcv-queue as it's no more valid */ -+ while ((tmp = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { -+ tp->copied_seq = TCP_SKB_CB(tmp)->end_seq; -+ kfree_skb(tmp); -+ } -+ -+ ans = 0; -+ } -+ } -+ -+ return ans; -+} -+ -+static inline void mptcp_prepare_skb(struct sk_buff *skb, struct sk_buff *next, -+ struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); -+ /* Adapt data-seq's to the packet itself. We kinda transform the -+ * dss-mapping to a per-packet granularity. This is necessary to -+ * correctly handle overlapping mappings coming from different -+ * subflows. Otherwise it would be a complete mess. -+ */ -+ tcb->seq = ((u32)tp->mptcp->map_data_seq) + tcb->seq - tp->mptcp->map_subseq; -+ tcb->end_seq = tcb->seq + skb->len; -+ -+ /* If cur is the last one in the rcv-queue (or the last one for this -+ * mapping), and data_fin is enqueued, the end_data_seq is +1. -+ */ -+ if (skb_queue_is_last(&sk->sk_receive_queue, skb) || -+ after(TCP_SKB_CB(next)->end_seq, tp->mptcp->map_subseq + tp->mptcp->map_data_len)) { -+ tcb->end_seq += tp->mptcp->map_data_fin; -+ -+ /* We manually set the fin-flag if it is a data-fin. For easy -+ * processing in tcp_recvmsg. -+ */ -+ if (mptcp_is_data_fin2(skb, tp)) -+ tcp_hdr(skb)->fin = 1; -+ else -+ tcp_hdr(skb)->fin = 0; -+ } else { -+ /* We may have a subflow-fin with data but without data-fin */ -+ tcp_hdr(skb)->fin = 0; -+ } -+} -+ -+/** -+ * @return: 1 if the segment has been eaten and can be suppressed, -+ * otherwise 0. -+ */ -+static inline int mptcp_direct_copy(struct sk_buff *skb, struct sock *meta_sk) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ int chunk = min_t(unsigned int, skb->len, meta_tp->ucopy.len); -+ int eaten = 0; -+ -+ __set_current_state(TASK_RUNNING); -+ -+ local_bh_enable(); -+ if (!skb_copy_datagram_iovec(skb, 0, meta_tp->ucopy.iov, chunk)) { -+ meta_tp->ucopy.len -= chunk; -+ meta_tp->copied_seq += chunk; -+ eaten = (chunk == skb->len); -+ tcp_rcv_space_adjust(meta_sk); -+ } -+ local_bh_disable(); -+ return eaten; -+} -+ -+static inline void mptcp_reset_mapping(struct tcp_sock *tp) -+{ -+ tp->mptcp->map_data_len = 0; -+ tp->mptcp->map_data_seq = 0; -+ tp->mptcp->map_subseq = 0; -+ tp->mptcp->map_data_fin = 0; -+ tp->mptcp->mapping_present = 0; -+} -+ -+/* The DSS-mapping received on the sk only covers the second half of the skb -+ * (cut at seq). We trim the head from the skb. -+ * Data will be freed upon kfree(). -+ * -+ * Inspired by tcp_trim_head(). -+ */ -+static void mptcp_skb_trim_head(struct sk_buff *skb, struct sock *sk, u32 seq) -+{ -+ int len = seq - TCP_SKB_CB(skb)->seq; -+ u32 new_seq = TCP_SKB_CB(skb)->seq + len; -+ -+ if (len < skb_headlen(skb)) -+ __skb_pull(skb, len); -+ else -+ __pskb_trim_head(skb, len - skb_headlen(skb)); -+ -+ TCP_SKB_CB(skb)->seq = new_seq; -+ -+ skb->truesize -= len; -+ atomic_sub(len, &sk->sk_rmem_alloc); -+ sk_mem_uncharge(sk, len); -+} -+ -+/* The DSS-mapping received on the sk only covers the first half of the skb -+ * (cut at seq). We create a second skb (@return), and queue it in the rcv-queue -+ * as further packets may resolve the mapping of the second half of data. -+ * -+ * Inspired by tcp_fragment(). -+ */ -+static int mptcp_skb_split_tail(struct sk_buff *skb, struct sock *sk, u32 seq) -+{ -+ struct sk_buff *buff; -+ int nsize; -+ int nlen, len; -+ -+ len = seq - TCP_SKB_CB(skb)->seq; -+ nsize = skb_headlen(skb) - len + tcp_sk(sk)->tcp_header_len; -+ if (nsize < 0) -+ nsize = 0; -+ -+ /* Get a new skb... force flag on. */ -+ buff = alloc_skb(nsize, GFP_ATOMIC); -+ if (buff == NULL) -+ return -ENOMEM; -+ -+ skb_reserve(buff, tcp_sk(sk)->tcp_header_len); -+ skb_reset_transport_header(buff); -+ -+ tcp_hdr(buff)->fin = tcp_hdr(skb)->fin; -+ tcp_hdr(skb)->fin = 0; -+ -+ /* We absolutly need to call skb_set_owner_r before refreshing the -+ * truesize of buff, otherwise the moved data will account twice. -+ */ -+ skb_set_owner_r(buff, sk); -+ nlen = skb->len - len - nsize; -+ buff->truesize += nlen; -+ skb->truesize -= nlen; -+ -+ /* Correct the sequence numbers. */ -+ TCP_SKB_CB(buff)->seq = TCP_SKB_CB(skb)->seq + len; -+ TCP_SKB_CB(buff)->end_seq = TCP_SKB_CB(skb)->end_seq; -+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq; -+ -+ skb_split(skb, buff, len); -+ -+ __skb_queue_after(&sk->sk_receive_queue, skb, buff); -+ -+ return 0; -+} -+ -+/* @return: 0 everything is fine. Just continue processing -+ * 1 subflow is broken stop everything -+ * -1 this packet was broken - continue with the next one. -+ */ -+static int mptcp_prevalidate_skb(struct sock *sk, struct sk_buff *skb) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ /* If we are in infinite mode, the subflow-fin is in fact a data-fin. */ -+ if (!skb->len && tcp_hdr(skb)->fin && !mptcp_is_data_fin(skb) && -+ !tp->mpcb->infinite_mapping_rcv) { -+ /* Remove a pure subflow-fin from the queue and increase -+ * copied_seq. -+ */ -+ tp->copied_seq = TCP_SKB_CB(skb)->end_seq; -+ __skb_unlink(skb, &sk->sk_receive_queue); -+ __kfree_skb(skb); -+ return -1; -+ } -+ -+ /* If we are not yet fully established and do not know the mapping for -+ * this segment, this path has to fallback to infinite or be torn down. -+ */ -+ if (!tp->mptcp->fully_established && !mptcp_is_data_seq(skb) && -+ !tp->mptcp->mapping_present && !tp->mpcb->infinite_mapping_rcv) { -+ pr_err("%s %#x will fallback - pi %d from %pS, seq %u\n", -+ __func__, tp->mpcb->mptcp_loc_token, -+ tp->mptcp->path_index, __builtin_return_address(0), -+ TCP_SKB_CB(skb)->seq); -+ -+ if (!is_master_tp(tp)) { -+ mptcp_send_reset(sk); -+ return 1; -+ } -+ -+ tp->mpcb->infinite_mapping_snd = 1; -+ tp->mpcb->infinite_mapping_rcv = 1; -+ tp->mptcp->fully_established = 1; -+ } -+ -+ /* Receiver-side becomes fully established when a whole rcv-window has -+ * been received without the need to fallback due to the previous -+ * condition. */ -+ if (!tp->mptcp->fully_established) { -+ tp->mptcp->init_rcv_wnd -= skb->len; -+ if (tp->mptcp->init_rcv_wnd < 0) -+ mptcp_become_fully_estab(sk); -+ } -+ -+ return 0; -+} -+ -+/* @return: 0 everything is fine. Just continue processing -+ * 1 subflow is broken stop everything -+ * -1 this packet was broken - continue with the next one. -+ */ -+static int mptcp_detect_mapping(struct sock *sk, struct sk_buff *skb) -+{ -+ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); -+ struct mptcp_cb *mpcb = tp->mpcb; -+ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); -+ u32 *ptr; -+ u32 data_seq, sub_seq, data_len, tcp_end_seq; -+ -+ /* If we are in infinite-mapping-mode, the subflow is guaranteed to be -+ * in-order at the data-level. Thus data-seq-numbers can be inferred -+ * from what is expected at the data-level. -+ */ -+ if (mpcb->infinite_mapping_rcv) { -+ tp->mptcp->map_data_seq = mptcp_get_rcv_nxt_64(meta_tp); -+ tp->mptcp->map_subseq = tcb->seq; -+ tp->mptcp->map_data_len = skb->len; -+ tp->mptcp->map_data_fin = tcp_hdr(skb)->fin; -+ tp->mptcp->mapping_present = 1; -+ return 0; -+ } -+ -+ /* No mapping here? Exit - it is either already set or still on its way */ -+ if (!mptcp_is_data_seq(skb)) { -+ /* Too many packets without a mapping - this subflow is broken */ -+ if (!tp->mptcp->mapping_present && -+ tp->rcv_nxt - tp->copied_seq > 65536) { -+ mptcp_send_reset(sk); -+ return 1; -+ } -+ -+ return 0; -+ } -+ -+ ptr = mptcp_skb_set_data_seq(skb, &data_seq, mpcb); -+ ptr++; -+ sub_seq = get_unaligned_be32(ptr) + tp->mptcp->rcv_isn; -+ ptr++; -+ data_len = get_unaligned_be16(ptr); -+ -+ /* If it's an empty skb with DATA_FIN, sub_seq must get fixed. -+ * The draft sets it to 0, but we really would like to have the -+ * real value, to have an easy handling afterwards here in this -+ * function. -+ */ -+ if (mptcp_is_data_fin(skb) && skb->len == 0) -+ sub_seq = TCP_SKB_CB(skb)->seq; -+ -+ /* If there is already a mapping - we check if it maps with the current -+ * one. If not - we reset. -+ */ -+ if (tp->mptcp->mapping_present && -+ (data_seq != (u32)tp->mptcp->map_data_seq || -+ sub_seq != tp->mptcp->map_subseq || -+ data_len != tp->mptcp->map_data_len + tp->mptcp->map_data_fin || -+ mptcp_is_data_fin(skb) != tp->mptcp->map_data_fin)) { -+ /* Mapping in packet is different from what we want */ -+ pr_err("%s Mappings do not match!\n", __func__); -+ pr_err("%s dseq %u mdseq %u, sseq %u msseq %u dlen %u mdlen %u dfin %d mdfin %d\n", -+ __func__, data_seq, (u32)tp->mptcp->map_data_seq, -+ sub_seq, tp->mptcp->map_subseq, data_len, -+ tp->mptcp->map_data_len, mptcp_is_data_fin(skb), -+ tp->mptcp->map_data_fin); -+ mptcp_send_reset(sk); -+ return 1; -+ } -+ -+ /* If the previous check was good, the current mapping is valid and we exit. */ -+ if (tp->mptcp->mapping_present) -+ return 0; -+ -+ /* Mapping not yet set on this subflow - we set it here! */ -+ -+ if (!data_len) { -+ mpcb->infinite_mapping_rcv = 1; -+ tp->mptcp->fully_established = 1; -+ /* We need to repeat mp_fail's until the sender felt -+ * back to infinite-mapping - here we stop repeating it. -+ */ -+ tp->mptcp->send_mp_fail = 0; -+ -+ /* We have to fixup data_len - it must be the same as skb->len */ -+ data_len = skb->len + (mptcp_is_data_fin(skb) ? 1 : 0); -+ sub_seq = tcb->seq; -+ -+ /* TODO kill all other subflows than this one */ -+ /* data_seq and so on are set correctly */ -+ -+ /* At this point, the meta-ofo-queue has to be emptied, -+ * as the following data is guaranteed to be in-order at -+ * the data and subflow-level -+ */ -+ mptcp_purge_ofo_queue(meta_tp); -+ } -+ -+ /* We are sending mp-fail's and thus are in fallback mode. -+ * Ignore packets which do not announce the fallback and still -+ * want to provide a mapping. -+ */ -+ if (tp->mptcp->send_mp_fail) { -+ tp->copied_seq = TCP_SKB_CB(skb)->end_seq; -+ __skb_unlink(skb, &sk->sk_receive_queue); -+ __kfree_skb(skb); -+ return -1; -+ } -+ -+ /* FIN increased the mapping-length by 1 */ -+ if (mptcp_is_data_fin(skb)) -+ data_len--; -+ -+ /* Subflow-sequences of packet must be -+ * (at least partially) be part of the DSS-mapping's -+ * subflow-sequence-space. -+ * -+ * Basically the mapping is not valid, if either of the -+ * following conditions is true: -+ * -+ * 1. It's not a data_fin and -+ * MPTCP-sub_seq >= TCP-end_seq -+ * -+ * 2. It's a data_fin and TCP-end_seq > TCP-seq and -+ * MPTCP-sub_seq >= TCP-end_seq -+ * -+ * The previous two can be merged into: -+ * TCP-end_seq > TCP-seq and MPTCP-sub_seq >= TCP-end_seq -+ * Because if it's not a data-fin, TCP-end_seq > TCP-seq -+ * -+ * 3. It's a data_fin and skb->len == 0 and -+ * MPTCP-sub_seq > TCP-end_seq -+ * -+ * 4. It's not a data_fin and TCP-end_seq > TCP-seq and -+ * MPTCP-sub_seq + MPTCP-data_len <= TCP-seq -+ * -+ * 5. MPTCP-sub_seq is prior to what we already copied (copied_seq) -+ */ -+ -+ /* subflow-fin is not part of the mapping - ignore it here ! */ -+ tcp_end_seq = tcb->end_seq - tcp_hdr(skb)->fin; -+ if ((!before(sub_seq, tcb->end_seq) && after(tcp_end_seq, tcb->seq)) || -+ (mptcp_is_data_fin(skb) && skb->len == 0 && after(sub_seq, tcb->end_seq)) || -+ (!after(sub_seq + data_len, tcb->seq) && after(tcp_end_seq, tcb->seq)) || -+ before(sub_seq, tp->copied_seq)) { -+ /* Subflow-sequences of packet is different from what is in the -+ * packet's dss-mapping. The peer is misbehaving - reset -+ */ -+ pr_err("%s Packet's mapping does not map to the DSS sub_seq %u " -+ "end_seq %u, tcp_end_seq %u seq %u dfin %u len %u data_len %u" -+ "copied_seq %u\n", __func__, sub_seq, tcb->end_seq, tcp_end_seq, tcb->seq, mptcp_is_data_fin(skb), -+ skb->len, data_len, tp->copied_seq); -+ mptcp_send_reset(sk); -+ return 1; -+ } -+ -+ /* Does the DSS had 64-bit seqnum's ? */ -+ if (!(tcb->mptcp_flags & MPTCPHDR_SEQ64_SET)) { -+ /* Wrapped around? */ -+ if (unlikely(after(data_seq, meta_tp->rcv_nxt) && data_seq < meta_tp->rcv_nxt)) { -+ tp->mptcp->map_data_seq = mptcp_get_data_seq_64(mpcb, !mpcb->rcv_hiseq_index, data_seq); -+ } else { -+ /* Else, access the default high-order bits */ -+ tp->mptcp->map_data_seq = mptcp_get_data_seq_64(mpcb, mpcb->rcv_hiseq_index, data_seq); -+ } -+ } else { -+ tp->mptcp->map_data_seq = mptcp_get_data_seq_64(mpcb, (tcb->mptcp_flags & MPTCPHDR_SEQ64_INDEX) ? 1 : 0, data_seq); -+ -+ if (unlikely(tcb->mptcp_flags & MPTCPHDR_SEQ64_OFO)) { -+ /* We make sure that the data_seq is invalid. -+ * It will be dropped later. -+ */ -+ tp->mptcp->map_data_seq += 0xFFFFFFFF; -+ tp->mptcp->map_data_seq += 0xFFFFFFFF; -+ } -+ } -+ -+ tp->mptcp->map_data_len = data_len; -+ tp->mptcp->map_subseq = sub_seq; -+ tp->mptcp->map_data_fin = mptcp_is_data_fin(skb) ? 1 : 0; -+ tp->mptcp->mapping_present = 1; -+ -+ return 0; -+} -+ -+/* Similar to tcp_sequence(...) */ -+static inline int mptcp_sequence(const struct tcp_sock *meta_tp, -+ u64 data_seq, u64 end_data_seq) -+{ -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ u64 rcv_wup64; -+ -+ /* Wrap-around? */ -+ if (meta_tp->rcv_wup > meta_tp->rcv_nxt) { -+ rcv_wup64 = ((u64)(mpcb->rcv_high_order[mpcb->rcv_hiseq_index] - 1) << 32) | -+ meta_tp->rcv_wup; -+ } else { -+ rcv_wup64 = mptcp_get_data_seq_64(mpcb, mpcb->rcv_hiseq_index, -+ meta_tp->rcv_wup); -+ } -+ -+ return !before64(end_data_seq, rcv_wup64) && -+ !after64(data_seq, mptcp_get_rcv_nxt_64(meta_tp) + tcp_receive_window(meta_tp)); -+} -+ -+/* @return: 0 everything is fine. Just continue processing -+ * -1 this packet was broken - continue with the next one. -+ */ -+static int mptcp_validate_mapping(struct sock *sk, struct sk_buff *skb) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct sk_buff *tmp, *tmp1; -+ u32 tcp_end_seq; -+ -+ if (!tp->mptcp->mapping_present) -+ return 0; -+ -+ /* either, the new skb gave us the mapping and the first segment -+ * in the sub-rcv-queue has to be trimmed ... -+ */ -+ tmp = skb_peek(&sk->sk_receive_queue); -+ if (before(TCP_SKB_CB(tmp)->seq, tp->mptcp->map_subseq) && -+ after(TCP_SKB_CB(tmp)->end_seq, tp->mptcp->map_subseq)) -+ mptcp_skb_trim_head(tmp, sk, tp->mptcp->map_subseq); -+ -+ /* ... or the new skb (tail) has to be split at the end. */ -+ tcp_end_seq = TCP_SKB_CB(skb)->end_seq - (tcp_hdr(skb)->fin ? 1 : 0); -+ if (after(tcp_end_seq, tp->mptcp->map_subseq + tp->mptcp->map_data_len)) { -+ u32 seq = tp->mptcp->map_subseq + tp->mptcp->map_data_len; -+ if (mptcp_skb_split_tail(skb, sk, seq)) { /* Allocation failed */ -+ /* TODO : maybe handle this here better. -+ * We now just force meta-retransmission. -+ */ -+ tp->copied_seq = TCP_SKB_CB(skb)->end_seq; -+ __skb_unlink(skb, &sk->sk_receive_queue); -+ __kfree_skb(skb); -+ return -1; -+ } -+ } -+ -+ /* Now, remove old sk_buff's from the receive-queue. -+ * This may happen if the mapping has been lost for these segments and -+ * the next mapping has already been received. -+ */ -+ if (tp->mptcp->mapping_present && -+ before(TCP_SKB_CB(skb_peek(&sk->sk_receive_queue))->seq, tp->mptcp->map_subseq)) { -+ skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { -+ if (!before(TCP_SKB_CB(tmp1)->seq, tp->mptcp->map_subseq)) -+ break; -+ -+ tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; -+ __skb_unlink(tmp1, &sk->sk_receive_queue); -+ -+ /* Impossible that we could free skb here, because his -+ * mapping is known to be valid from previous checks -+ */ -+ __kfree_skb(tmp1); -+ } -+ } -+ -+ return 0; -+} -+ -+/* @return: 0 everything is fine. Just continue processing -+ * 1 subflow is broken stop everything -+ * -1 this mapping has been put in the meta-receive-queue -+ * -2 this mapping has been eaten by the application -+ */ -+static int mptcp_queue_skb(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ struct mptcp_cb *mpcb = tp->mpcb; -+ struct sk_buff *tmp, *tmp1; -+ u64 rcv_nxt64 = mptcp_get_rcv_nxt_64(meta_tp); -+ bool data_queued = false; -+ -+ /* Have we not yet received the full mapping? */ -+ if (!tp->mptcp->mapping_present || -+ before(tp->rcv_nxt, tp->mptcp->map_subseq + tp->mptcp->map_data_len)) -+ return 0; -+ -+ /* Is this an overlapping mapping? rcv_nxt >= end_data_seq -+ * OR -+ * This mapping is out of window -+ */ -+ if (!before64(rcv_nxt64, tp->mptcp->map_data_seq + tp->mptcp->map_data_len + tp->mptcp->map_data_fin) || -+ !mptcp_sequence(meta_tp, tp->mptcp->map_data_seq, -+ tp->mptcp->map_data_seq + tp->mptcp->map_data_len + tp->mptcp->map_data_fin)) { -+ skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { -+ __skb_unlink(tmp1, &sk->sk_receive_queue); -+ tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; -+ __kfree_skb(tmp1); -+ -+ if (!skb_queue_empty(&sk->sk_receive_queue) && -+ !before(TCP_SKB_CB(tmp)->seq, -+ tp->mptcp->map_subseq + tp->mptcp->map_data_len)) -+ break; -+ } -+ -+ mptcp_reset_mapping(tp); -+ -+ return -1; -+ } -+ -+ /* Record it, because we want to send our data_fin on the same path */ -+ if (tp->mptcp->map_data_fin) { -+ mpcb->dfin_path_index = tp->mptcp->path_index; -+ mpcb->dfin_combined = !!(sk->sk_shutdown & RCV_SHUTDOWN); -+ } -+ -+ /* Verify the checksum */ -+ if (mpcb->dss_csum && !mpcb->infinite_mapping_rcv) { -+ int ret = mptcp_verif_dss_csum(sk); -+ -+ if (ret <= 0) { -+ mptcp_reset_mapping(tp); -+ return 1; -+ } -+ } -+ -+ if (before64(rcv_nxt64, tp->mptcp->map_data_seq)) { -+ /* Seg's have to go to the meta-ofo-queue */ -+ skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { -+ tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; -+ mptcp_prepare_skb(tmp1, tmp, sk); -+ __skb_unlink(tmp1, &sk->sk_receive_queue); -+ /* MUST be done here, because fragstolen may be true later. -+ * Then, kfree_skb_partial will not account the memory. -+ */ -+ skb_orphan(tmp1); -+ -+ if (!mpcb->in_time_wait) /* In time-wait, do not receive data */ -+ mptcp_add_meta_ofo_queue(meta_sk, tmp1, sk); -+ else -+ __kfree_skb(tmp1); -+ -+ if (!skb_queue_empty(&sk->sk_receive_queue) && -+ !before(TCP_SKB_CB(tmp)->seq, -+ tp->mptcp->map_subseq + tp->mptcp->map_data_len)) -+ break; -+ -+ } -+ } else { -+ /* Ready for the meta-rcv-queue */ -+ skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { -+ int eaten = 0; -+ int copied_early = 0; -+ bool fragstolen = false; -+ u32 old_rcv_nxt = meta_tp->rcv_nxt; -+ -+ tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; -+ mptcp_prepare_skb(tmp1, tmp, sk); -+ __skb_unlink(tmp1, &sk->sk_receive_queue); -+ /* MUST be done here, because fragstolen may be true. -+ * Then, kfree_skb_partial will not account the memory. -+ */ -+ skb_orphan(tmp1); -+ -+ /* This segment has already been received */ -+ if (!after(TCP_SKB_CB(tmp1)->end_seq, meta_tp->rcv_nxt)) { -+ __kfree_skb(tmp1); -+ goto next; -+ } -+ -+#ifdef CONFIG_NET_DMA -+ if (TCP_SKB_CB(tmp1)->seq == meta_tp->rcv_nxt && -+ meta_tp->ucopy.task == current && -+ meta_tp->copied_seq == meta_tp->rcv_nxt && -+ tmp1->len <= meta_tp->ucopy.len && -+ sock_owned_by_user(meta_sk) && -+ tcp_dma_try_early_copy(meta_sk, tmp1, 0)) { -+ copied_early = 1; -+ eaten = 1; -+ } -+#endif -+ -+ /* Is direct copy possible ? */ -+ if (TCP_SKB_CB(tmp1)->seq == meta_tp->rcv_nxt && -+ meta_tp->ucopy.task == current && -+ meta_tp->copied_seq == meta_tp->rcv_nxt && -+ meta_tp->ucopy.len && sock_owned_by_user(meta_sk) && -+ !copied_early) -+ eaten = mptcp_direct_copy(tmp1, meta_sk); -+ -+ if (mpcb->in_time_wait) /* In time-wait, do not receive data */ -+ eaten = 1; -+ -+ if (!eaten) -+ eaten = tcp_queue_rcv(meta_sk, tmp1, 0, &fragstolen); -+ -+ meta_tp->rcv_nxt = TCP_SKB_CB(tmp1)->end_seq; -+ mptcp_check_rcvseq_wrap(meta_tp, old_rcv_nxt); -+ -+ if (copied_early) -+ tcp_cleanup_rbuf(meta_sk, tmp1->len); -+ -+ if (tcp_hdr(tmp1)->fin && !mpcb->in_time_wait) -+ mptcp_fin(meta_sk); -+ -+ /* Check if this fills a gap in the ofo queue */ -+ if (!skb_queue_empty(&meta_tp->out_of_order_queue)) -+ mptcp_ofo_queue(meta_sk); -+ -+#ifdef CONFIG_NET_DMA -+ if (copied_early) -+ __skb_queue_tail(&meta_sk->sk_async_wait_queue, -+ tmp1); -+ else -+#endif -+ if (eaten) -+ kfree_skb_partial(tmp1, fragstolen); -+ -+ data_queued = true; -+next: -+ if (!skb_queue_empty(&sk->sk_receive_queue) && -+ !before(TCP_SKB_CB(tmp)->seq, -+ tp->mptcp->map_subseq + tp->mptcp->map_data_len)) -+ break; -+ } -+ } -+ -+ inet_csk(meta_sk)->icsk_ack.lrcvtime = tcp_time_stamp; -+ tp->mptcp->last_data_seq = tp->mptcp->map_data_seq; -+ mptcp_reset_mapping(tp); -+ -+ return data_queued ? -1 : -2; -+} -+ -+void mptcp_data_ready(struct sock *sk, int bytes) -+{ -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ struct sk_buff *skb, *tmp; -+ int queued = 0; -+ -+ /* If the meta is already closed, there is no point in pushing data */ -+ if (meta_sk->sk_state == TCP_CLOSE && !tcp_sk(sk)->mpcb->in_time_wait) { -+ skb_queue_purge(&sk->sk_receive_queue); -+ tcp_sk(sk)->copied_seq = tcp_sk(sk)->rcv_nxt; -+ goto exit; -+ } -+ -+restart: -+ /* Iterate over all segments, detect their mapping (if we don't have -+ * one yet), validate them and push everything one level higher. -+ */ -+ skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) { -+ int ret; -+ /* Pre-validation - e.g., early fallback */ -+ ret = mptcp_prevalidate_skb(sk, skb); -+ if (ret < 0) -+ goto restart; -+ else if (ret > 0) -+ break; -+ -+ /* Set the current mapping */ -+ ret = mptcp_detect_mapping(sk, skb); -+ if (ret < 0) -+ goto restart; -+ else if (ret > 0) -+ break; -+ -+ /* Validation */ -+ if (mptcp_validate_mapping(sk, skb) < 0) -+ goto restart; -+ -+ /* Push a level higher */ -+ ret = mptcp_queue_skb(sk); -+ if (ret < 0) { -+ if (ret == -1) -+ queued = ret; -+ goto restart; -+ } else if (ret == 0) { -+ continue; -+ } else { /* ret == 1 */ -+ break; -+ } -+ } -+ -+exit: -+ if (tcp_sk(sk)->close_it) { -+ tcp_send_ack(sk); -+ tcp_time_wait(sk, TCP_TIME_WAIT, 0); -+ } -+ -+ if (queued == -1 && !sock_flag(meta_sk, SOCK_DEAD)) -+ meta_sk->sk_data_ready(meta_sk, 0); -+} -+ -+ -+int mptcp_check_req(struct sk_buff *skb, struct net *net) -+{ -+ struct tcphdr *th = tcp_hdr(skb); -+ struct sock *meta_sk = NULL; -+ -+ /* MPTCP structures not initialized */ -+ if (mptcp_init_failed) -+ return 0; -+ -+ if (skb->protocol == htons(ETH_P_IP)) -+ meta_sk = mptcp_v4_search_req(th->source, ip_hdr(skb)->saddr, -+ ip_hdr(skb)->daddr, net); -+#if IS_ENABLED(CONFIG_IPV6) -+ else /* IPv6 */ -+ meta_sk = mptcp_v6_search_req(th->source, &ipv6_hdr(skb)->saddr, -+ &ipv6_hdr(skb)->daddr, net); -+#endif /* CONFIG_IPV6 */ -+ -+ if (!meta_sk) -+ return 0; -+ -+ TCP_SKB_CB(skb)->mptcp_flags = MPTCPHDR_JOIN; -+ -+ bh_lock_sock_nested(meta_sk); -+ if (sock_owned_by_user(meta_sk)) { -+ skb->sk = meta_sk; -+ if (unlikely(sk_add_backlog(meta_sk, skb, -+ meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { -+ bh_unlock_sock(meta_sk); -+ NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); -+ sock_put(meta_sk); /* Taken by mptcp_search_req */ -+ kfree_skb(skb); -+ return 1; -+ } -+ } else if (skb->protocol == htons(ETH_P_IP)) { -+ tcp_v4_do_rcv(meta_sk, skb); -+#if IS_ENABLED(CONFIG_IPV6) -+ } else { /* IPv6 */ -+ tcp_v6_do_rcv(meta_sk, skb); -+#endif /* CONFIG_IPV6 */ -+ } -+ bh_unlock_sock(meta_sk); -+ sock_put(meta_sk); /* Taken by mptcp_vX_search_req */ -+ return 1; -+} -+ -+struct mp_join *mptcp_find_join(struct sk_buff *skb) -+{ -+ struct tcphdr *th = tcp_hdr(skb); -+ unsigned char *ptr; -+ int length = (th->doff * 4) - sizeof(struct tcphdr); -+ -+ /* Jump through the options to check whether JOIN is there */ -+ ptr = (unsigned char *)(th + 1); -+ while (length > 0) { -+ int opcode = *ptr++; -+ int opsize; -+ -+ switch (opcode) { -+ case TCPOPT_EOL: -+ return NULL; -+ case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ -+ length--; -+ continue; -+ default: -+ opsize = *ptr++; -+ if (opsize < 2) /* "silly options" */ -+ return NULL; -+ if (opsize > length) -+ return NULL; /* don't parse partial options */ -+ if (opcode == TCPOPT_MPTCP && -+ ((struct mptcp_option *)(ptr - 2))->sub == MPTCP_SUB_JOIN) { -+ return (struct mp_join *)(ptr - 2); -+ } -+ ptr += opsize - 2; -+ length -= opsize; -+ } -+ } -+ return NULL; -+} -+ -+int mptcp_lookup_join(struct sk_buff *skb, struct inet_timewait_sock *tw) -+{ -+ struct mptcp_cb *mpcb; -+ struct sock *meta_sk; -+ u32 token; -+ struct mp_join *join_opt = mptcp_find_join(skb); -+ if (!join_opt) -+ return 0; -+ -+ /* MPTCP structures were not initialized, so return error */ -+ if (mptcp_init_failed) -+ return -1; -+ -+ token = join_opt->u.syn.token; -+ meta_sk = mptcp_hash_find(dev_net(skb_dst(skb)->dev), token); -+ if (!meta_sk) { -+ mptcp_debug("%s:mpcb not found:%x\n", __func__, token); -+ return -1; -+ } -+ -+ mpcb = tcp_sk(meta_sk)->mpcb; -+ if (mpcb->infinite_mapping_rcv || mpcb->send_infinite_mapping) { -+ /* We are in fallback-mode on the reception-side - -+ * no new subflows! -+ */ -+ sock_put(meta_sk); /* Taken by mptcp_hash_find */ -+ return -1; -+ } -+ -+ /* Coming from time-wait-sock processing in tcp_v4_rcv. -+ * We have to deschedule it before continuing, because otherwise -+ * mptcp_v4_do_rcv will hit again on it inside tcp_v4_hnd_req. -+ */ -+ if (tw) { -+ inet_twsk_deschedule(tw, &tcp_death_row); -+ inet_twsk_put(tw); -+ } -+ -+ TCP_SKB_CB(skb)->mptcp_flags = MPTCPHDR_JOIN; -+ /* OK, this is a new syn/join, let's create a new open request and -+ * send syn+ack -+ */ -+ bh_lock_sock_nested(meta_sk); -+ if (sock_owned_by_user(meta_sk)) { -+ skb->sk = meta_sk; -+ if (unlikely(sk_add_backlog(meta_sk, skb, -+ meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { -+ bh_unlock_sock(meta_sk); -+ NET_INC_STATS_BH(sock_net(meta_sk), -+ LINUX_MIB_TCPBACKLOGDROP); -+ sock_put(meta_sk); /* Taken by mptcp_hash_find */ -+ kfree_skb(skb); -+ return 1; -+ } -+ } else if (skb->protocol == htons(ETH_P_IP)) { -+ tcp_v4_do_rcv(meta_sk, skb); -+#if IS_ENABLED(CONFIG_IPV6) -+ } else { -+ tcp_v6_do_rcv(meta_sk, skb); -+#endif /* CONFIG_IPV6 */ -+ } -+ bh_unlock_sock(meta_sk); -+ sock_put(meta_sk); /* Taken by mptcp_hash_find */ -+ return 1; -+} -+ -+int mptcp_do_join_short(struct sk_buff *skb, struct mptcp_options_received *mopt, -+ struct tcp_options_received *tmp_opt, struct net *net) -+{ -+ struct sock *meta_sk; -+ u32 token; -+ -+ token = mopt->mptcp_rem_token; -+ meta_sk = mptcp_hash_find(net, token); -+ if (!meta_sk) { -+ mptcp_debug("%s:mpcb not found:%x\n", __func__, token); -+ return -1; -+ } -+ -+ TCP_SKB_CB(skb)->mptcp_flags = MPTCPHDR_JOIN; -+ -+ /* OK, this is a new syn/join, let's create a new open request and -+ * send syn+ack -+ */ -+ bh_lock_sock(meta_sk); -+ -+ /* This check is also done in mptcp_vX_do_rcv. But, there we cannot -+ * call tcp_vX_send_reset, because we hold already two socket-locks. -+ * (the listener and the meta from above) -+ * -+ * And the send-reset will try to take yet another one (ip_send_reply). -+ * Thus, we propagate the reset up to tcp_rcv_state_process. -+ */ -+ if (tcp_sk(meta_sk)->mpcb->infinite_mapping_rcv || -+ tcp_sk(meta_sk)->mpcb->send_infinite_mapping || -+ meta_sk->sk_state == TCP_CLOSE || !tcp_sk(meta_sk)->inside_tk_table) { -+ bh_unlock_sock(meta_sk); -+ sock_put(meta_sk); /* Taken by mptcp_hash_find */ -+ return -1; -+ } -+ -+ if (sock_owned_by_user(meta_sk)) { -+ skb->sk = meta_sk; -+ if (unlikely(sk_add_backlog(meta_sk, skb, -+ meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) -+ NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); -+ else -+ /* Must make sure that upper layers won't free the -+ * skb if it is added to the backlog-queue. -+ */ -+ skb_get(skb); -+ } else { -+ /* mptcp_v4_do_rcv tries to free the skb - we prevent this, as -+ * the skb will finally be freed by tcp_v4_do_rcv (where we are -+ * coming from) -+ */ -+ skb_get(skb); -+ if (skb->protocol == htons(ETH_P_IP)) { -+ tcp_v4_do_rcv(meta_sk, skb); -+#if IS_ENABLED(CONFIG_IPV6) -+ } else { /* IPv6 */ -+ tcp_v6_do_rcv(meta_sk, skb); -+#endif /* CONFIG_IPV6 */ -+ } -+ } -+ -+ bh_unlock_sock(meta_sk); -+ sock_put(meta_sk); /* Taken by mptcp_hash_find */ -+ return 0; -+} -+ -+/** -+ * Equivalent of tcp_fin() for MPTCP -+ * Can be called only when the FIN is validly part -+ * of the data seqnum space. Not before when we get holes. -+ */ -+void mptcp_fin(struct sock *meta_sk) -+{ -+ struct sock *sk = NULL, *sk_it; -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ -+ mptcp_for_each_sk(mpcb, sk_it) { -+ if (tcp_sk(sk_it)->mptcp->path_index == mpcb->dfin_path_index) { -+ sk = sk_it; -+ break; -+ } -+ } -+ -+ if (!sk || sk->sk_state == TCP_CLOSE) -+ sk = mptcp_select_ack_sock(meta_sk, 0); -+ -+ inet_csk_schedule_ack(sk); -+ -+ meta_sk->sk_shutdown |= RCV_SHUTDOWN; -+ sock_set_flag(meta_sk, SOCK_DONE); -+ -+ switch (meta_sk->sk_state) { -+ case TCP_SYN_RECV: -+ case TCP_ESTABLISHED: -+ /* Move to CLOSE_WAIT */ -+ tcp_set_state(meta_sk, TCP_CLOSE_WAIT); -+ inet_csk(sk)->icsk_ack.pingpong = 1; -+ break; -+ -+ case TCP_CLOSE_WAIT: -+ case TCP_CLOSING: -+ /* Received a retransmission of the FIN, do -+ * nothing. -+ */ -+ break; -+ case TCP_LAST_ACK: -+ /* RFC793: Remain in the LAST-ACK state. */ -+ break; -+ -+ case TCP_FIN_WAIT1: -+ /* This case occurs when a simultaneous close -+ * happens, we must ack the received FIN and -+ * enter the CLOSING state. -+ */ -+ tcp_send_ack(sk); -+ tcp_set_state(meta_sk, TCP_CLOSING); -+ break; -+ case TCP_FIN_WAIT2: -+ /* Received a FIN -- send ACK and enter TIME_WAIT. */ -+ tcp_send_ack(sk); -+ tcp_time_wait(meta_sk, TCP_TIME_WAIT, 0); -+ break; -+ default: -+ /* Only TCP_LISTEN and TCP_CLOSE are left, in these -+ * cases we should never reach this piece of code. -+ */ -+ pr_err("%s: Impossible, meta_sk->sk_state=%d\n", __func__, -+ meta_sk->sk_state); -+ break; -+ } -+ -+ /* It _is_ possible, that we have something out-of-order _after_ FIN. -+ * Probably, we should reset in this case. For now drop them. -+ */ -+ mptcp_purge_ofo_queue(meta_tp); -+ sk_mem_reclaim(meta_sk); -+ -+ if (!sock_flag(meta_sk, SOCK_DEAD)) { -+ meta_sk->sk_state_change(meta_sk); -+ -+ /* Do not send POLL_HUP for half duplex close. */ -+ if (meta_sk->sk_shutdown == SHUTDOWN_MASK || -+ meta_sk->sk_state == TCP_CLOSE) -+ sk_wake_async(meta_sk, SOCK_WAKE_WAITD, POLL_HUP); -+ else -+ sk_wake_async(meta_sk, SOCK_WAKE_WAITD, POLL_IN); -+ } -+ -+ return; -+} -+ -+static void mptcp_xmit_retransmit_queue(struct sock *meta_sk) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct sk_buff *skb; -+ -+ if (!meta_tp->packets_out) -+ return; -+ -+ tcp_for_write_queue(skb, meta_sk) { -+ if (skb == tcp_send_head(meta_sk)) -+ break; -+ -+ if (mptcp_retransmit_skb(meta_sk, skb)) -+ return; -+ -+ if (skb == tcp_write_queue_head(meta_sk)) -+ inet_csk_reset_xmit_timer(meta_sk, ICSK_TIME_RETRANS, -+ inet_csk(meta_sk)->icsk_rto, -+ TCP_RTO_MAX); -+ } -+} -+ -+/* Handle the DATA_ACK */ -+static void mptcp_data_ack(struct sock *sk, const struct sk_buff *skb) -+{ -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk), *tp = tcp_sk(sk); -+ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); -+ u32 prior_snd_una = meta_tp->snd_una; -+ int prior_packets; -+ u32 nwin, data_ack, data_seq; -+ u16 data_len = 0; -+ -+ /* A valid packet came in - subflow is operational again */ -+ tp->pf = 0; -+ -+ /* Even if there is no data-ack, we stop retransmitting. -+ * Except if this is a SYN/ACK. Then it is just a retransmission -+ */ -+ if (tp->mptcp->pre_established && !tcp_hdr(skb)->syn) { -+ tp->mptcp->pre_established = 0; -+ sk_stop_timer(sk, &tp->mptcp->mptcp_ack_timer); -+ } -+ -+ /* If we are in infinite mapping mode, rx_opt.data_ack has been -+ * set by mptcp_clean_rtx_infinite. -+ */ -+ if (!(tcb->mptcp_flags & MPTCPHDR_ACK) && !tp->mpcb->infinite_mapping_snd) -+ goto exit; -+ -+ data_ack = tp->mptcp->rx_opt.data_ack; -+ -+ if (unlikely(!tp->mptcp->fully_established) && -+ (data_ack != meta_tp->mptcp->snt_isn || -+ tp->mptcp->snt_isn + 1 != TCP_SKB_CB(skb)->ack_seq)) -+ /* As soon as data has been data-acked, -+ * or a subflow-data-ack (not acking syn - thus snt_isn + 1) -+ * includes a data-ack, we are fully established -+ */ -+ mptcp_become_fully_estab(sk); -+ -+ /* Get the data_seq */ -+ if (mptcp_is_data_seq(skb)) { -+ data_seq = tp->mptcp->rx_opt.data_seq; -+ data_len = tp->mptcp->rx_opt.data_len; -+ } else { -+ data_seq = meta_tp->snd_wl1; -+ } -+ -+ /* If the ack is older than previous acks -+ * then we can probably ignore it. -+ */ -+ if (before(data_ack, prior_snd_una)) -+ goto exit; -+ -+ /* If the ack includes data we haven't sent yet, discard -+ * this segment (RFC793 Section 3.9). -+ */ -+ if (after(data_ack, meta_tp->snd_nxt)) -+ goto exit; -+ -+ /*** Now, update the window - inspired by tcp_ack_update_window ***/ -+ nwin = ntohs(tcp_hdr(skb)->window); -+ -+ if (likely(!tcp_hdr(skb)->syn)) -+ nwin <<= tp->rx_opt.snd_wscale; -+ -+ if (tcp_may_update_window(meta_tp, data_ack, data_seq, nwin)) { -+ tcp_update_wl(meta_tp, data_seq); -+ -+ /* Draft v09, Section 3.3.5: -+ * [...] It should only update its local receive window values -+ * when the largest sequence number allowed (i.e. DATA_ACK + -+ * receive window) increases. [...] -+ */ -+ if (meta_tp->snd_wnd != nwin && -+ !before(data_ack + nwin, tcp_wnd_end(meta_tp))) { -+ meta_tp->snd_wnd = nwin; -+ -+ if (nwin > meta_tp->max_window) -+ meta_tp->max_window = nwin; -+ } -+ } -+ /*** Done, update the window ***/ -+ -+ /* We passed data and got it acked, remove any soft error -+ * log. Something worked... -+ */ -+ sk->sk_err_soft = 0; -+ inet_csk(meta_sk)->icsk_probes_out = 0; -+ meta_tp->rcv_tstamp = tcp_time_stamp; -+ prior_packets = meta_tp->packets_out; -+ if (!prior_packets) -+ goto no_queue; -+ -+ meta_tp->snd_una = data_ack; -+ -+ mptcp_clean_rtx_queue(meta_sk, prior_snd_una); -+ -+ /* We are in loss-state, and something got acked, retransmit the whole -+ * queue now! -+ */ -+ if (inet_csk(meta_sk)->icsk_ca_state == TCP_CA_Loss && -+ after(data_ack, prior_snd_una)) { -+ mptcp_xmit_retransmit_queue(meta_sk); -+ inet_csk(meta_sk)->icsk_ca_state = TCP_CA_Open; -+ } -+ -+ /* Simplified version of tcp_new_space, because the snd-buffer -+ * is handled by all the subflows. -+ */ -+ if (sock_flag(meta_sk, SOCK_QUEUE_SHRUNK)) { -+ sock_reset_flag(meta_sk, SOCK_QUEUE_SHRUNK); -+ if (meta_sk->sk_socket && -+ test_bit(SOCK_NOSPACE, &meta_sk->sk_socket->flags)) -+ meta_sk->sk_write_space(meta_sk); -+ } -+ -+ if (meta_sk->sk_state != TCP_ESTABLISHED && -+ mptcp_rcv_state_process(meta_sk, sk, skb, data_seq, data_len)) -+ return; -+ -+exit: -+ mptcp_push_pending_frames(meta_sk); -+ -+ return; -+ -+no_queue: -+ if (tcp_send_head(meta_sk)) -+ tcp_ack_probe(meta_sk); -+ -+ mptcp_push_pending_frames(meta_sk); -+ -+ return; -+} -+ -+void mptcp_clean_rtx_infinite(struct sk_buff *skb, struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = tcp_sk(mptcp_meta_sk(sk)); -+ -+ if (!tp->mpcb->infinite_mapping_snd) -+ return; -+ -+ /* The difference between both write_seq's represents the offset between -+ * data-sequence and subflow-sequence. As we are infinite, this must -+ * match. -+ * -+ * Thus, from this difference we can infer the meta snd_una. -+ */ -+ tp->mptcp->rx_opt.data_ack = meta_tp->snd_nxt - tp->snd_nxt + -+ tp->snd_una; -+ -+ mptcp_data_ack(sk, skb); -+} -+ -+/**** static functions used by mptcp_parse_options */ -+ -+static inline int mptcp_rem_raddress(struct mptcp_cb *mpcb, u8 rem_id) -+{ -+ if (mptcp_v4_rem_raddress(mpcb, rem_id) < 0) { -+#if IS_ENABLED(CONFIG_IPV6) -+ if (mptcp_v6_rem_raddress(mpcb, rem_id) < 0) -+ return -1; -+#else -+ return -1; -+#endif /* CONFIG_IPV6 */ -+ } -+ return 0; -+} -+ -+static void mptcp_send_reset_rem_id(const struct mptcp_cb *mpcb, u8 rem_id) -+{ -+ struct sock *sk_it, *tmpsk; -+ -+ mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { -+ if (tcp_sk(sk_it)->mptcp->rem_id == rem_id) { -+ mptcp_reinject_data(sk_it, 0); -+ sk_it->sk_err = ECONNRESET; -+ if (tcp_need_reset(sk_it->sk_state)) -+ tcp_send_active_reset(sk_it, GFP_ATOMIC); -+ mptcp_sub_force_close(sk_it); -+ } -+ } -+} -+ -+void mptcp_parse_options(const uint8_t *ptr, int opsize, -+ struct tcp_options_received *opt_rx, -+ struct mptcp_options_received *mopt, -+ const struct sk_buff *skb) -+{ -+ struct mptcp_option *mp_opt = (struct mptcp_option *)ptr; -+ -+ /* If the socket is mp-capable we would have a mopt. */ -+ if (!mopt) -+ return; -+ -+ switch (mp_opt->sub) { -+ case MPTCP_SUB_CAPABLE: -+ { -+ struct mp_capable *mpcapable = (struct mp_capable *)ptr; -+ -+ if (opsize != MPTCP_SUB_LEN_CAPABLE_SYN && -+ opsize != MPTCP_SUB_LEN_CAPABLE_ACK) { -+ mptcp_debug("%s: mp_capable: bad option size %d\n", -+ __func__, opsize); -+ break; -+ } -+ -+ if (!sysctl_mptcp_enabled) -+ break; -+ -+ /* We only support MPTCP version 0 */ -+ if (mpcapable->ver != 0) -+ break; -+ -+ /* MPTCP-RFC 6824: -+ * "If receiving a message with the 'B' flag set to 1, and this -+ * is not understood, then this SYN MUST be silently ignored; -+ */ -+ if (mpcapable->b) { -+ mopt->drop_me = 1; -+ break; -+ } -+ -+ /* MPTCP-RFC 6824: -+ * "An implementation that only supports this method MUST set -+ * bit "H" to 1, and bits "C" through "G" to 0." -+ */ -+ if (!mpcapable->h) -+ break; -+ -+ mopt->saw_mpc = 1; -+ mopt->dss_csum = sysctl_mptcp_checksum || mpcapable->a; -+ -+ if (opsize >= MPTCP_SUB_LEN_CAPABLE_SYN) -+ mopt->mptcp_key = mpcapable->sender_key; -+ -+ break; -+ } -+ case MPTCP_SUB_JOIN: -+ { -+ struct mp_join *mpjoin = (struct mp_join *)ptr; -+ -+ if (opsize != MPTCP_SUB_LEN_JOIN_SYN && -+ opsize != MPTCP_SUB_LEN_JOIN_SYNACK && -+ opsize != MPTCP_SUB_LEN_JOIN_ACK) { -+ mptcp_debug("%s: mp_join: bad option size %d\n", -+ __func__, opsize); -+ break; -+ } -+ -+ /* saw_mpc must be set, because in tcp_check_req we assume that -+ * it is set to support falling back to reg. TCP if a rexmitted -+ * SYN has no MP_CAPABLE or MP_JOIN -+ */ -+ switch (opsize) { -+ case MPTCP_SUB_LEN_JOIN_SYN: -+ mopt->is_mp_join = 1; -+ mopt->saw_mpc = 1; -+ mopt->low_prio = mpjoin->b; -+ mopt->rem_id = mpjoin->addr_id; -+ mopt->mptcp_rem_token = mpjoin->u.syn.token; -+ mopt->mptcp_recv_nonce = mpjoin->u.syn.nonce; -+ break; -+ case MPTCP_SUB_LEN_JOIN_SYNACK: -+ mopt->saw_mpc = 1; -+ mopt->low_prio = mpjoin->b; -+ mopt->rem_id = mpjoin->addr_id; -+ mopt->mptcp_recv_tmac = mpjoin->u.synack.mac; -+ mopt->mptcp_recv_nonce = mpjoin->u.synack.nonce; -+ break; -+ case MPTCP_SUB_LEN_JOIN_ACK: -+ mopt->saw_mpc = 1; -+ mopt->join_ack = 1; -+ memcpy(mopt->mptcp_recv_mac, mpjoin->u.ack.mac, 20); -+ break; -+ } -+ break; -+ } -+ case MPTCP_SUB_DSS: -+ { -+ struct mp_dss *mdss = (struct mp_dss *)ptr; -+ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); -+ -+ /* We check opsize for the csum and non-csum case. We do this, -+ * because the draft says that the csum SHOULD be ignored if -+ * it has not been negotiated in the MP_CAPABLE but still is -+ * present in the data. -+ * -+ * It will get ignored later in mptcp_queue_skb. -+ */ -+ if (opsize != mptcp_sub_len_dss(mdss, 0) && -+ opsize != mptcp_sub_len_dss(mdss, 1)) { -+ mptcp_debug("%s: mp_dss: bad option size %d\n", -+ __func__, opsize); -+ break; -+ } -+ -+ ptr += 4; -+ -+ if (mdss->A) { -+ tcb->mptcp_flags |= MPTCPHDR_ACK; -+ -+ if (mdss->a) { -+ mopt->data_ack = (u32) get_unaligned_be64(ptr); -+ ptr += MPTCP_SUB_LEN_ACK_64; -+ } else { -+ mopt->data_ack = get_unaligned_be32(ptr); -+ ptr += MPTCP_SUB_LEN_ACK; -+ } -+ } -+ -+ tcb->dss_off = (ptr - skb_transport_header(skb)); -+ -+ if (mdss->M) { -+ if (mdss->m) { -+ u64 data_seq64 = get_unaligned_be64(ptr); -+ -+ tcb->mptcp_flags |= MPTCPHDR_SEQ64_SET; -+ mopt->data_seq = (u32) data_seq64; -+ -+ ptr += 12; /* 64-bit dseq + subseq */ -+ } else { -+ mopt->data_seq = get_unaligned_be32(ptr); -+ ptr += 8; /* 32-bit dseq + subseq */ -+ } -+ mopt->data_len = get_unaligned_be16(ptr); -+ -+ tcb->mptcp_flags |= MPTCPHDR_SEQ; -+ -+ /* Is a check-sum present? */ -+ if (opsize == mptcp_sub_len_dss(mdss, 1)) -+ tcb->mptcp_flags |= MPTCPHDR_DSS_CSUM; -+ -+ /* DATA_FIN only possible with DSS-mapping */ -+ if (mdss->F) -+ tcb->mptcp_flags |= MPTCPHDR_FIN; -+ } -+ -+ break; -+ } -+ case MPTCP_SUB_ADD_ADDR: -+ { -+#if IS_ENABLED(CONFIG_IPV6) -+ struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; -+ -+ if ((mpadd->ipver == 4 && opsize != MPTCP_SUB_LEN_ADD_ADDR4 && -+ opsize != MPTCP_SUB_LEN_ADD_ADDR4 + 2) || -+ (mpadd->ipver == 6 && opsize != MPTCP_SUB_LEN_ADD_ADDR6 && -+ opsize != MPTCP_SUB_LEN_ADD_ADDR6 + 2)) { -+#else -+ if (opsize != MPTCP_SUB_LEN_ADD_ADDR4 && -+ opsize != MPTCP_SUB_LEN_ADD_ADDR4 + 2) { -+#endif /* CONFIG_IPV6 */ -+ mptcp_debug("%s: mp_add_addr: bad option size %d\n", -+ __func__, opsize); -+ break; -+ } -+ -+ /* We have to manually parse the options if we got two of them. */ -+ if (mopt->saw_add_addr) { -+ mopt->more_add_addr = 1; -+ break; -+ } -+ mopt->saw_add_addr = 1; -+ mopt->add_addr_ptr = ptr; -+ break; -+ } -+ case MPTCP_SUB_REMOVE_ADDR: -+ if ((opsize - MPTCP_SUB_LEN_REMOVE_ADDR) < 0) { -+ mptcp_debug("%s: mp_remove_addr: bad option size %d\n", -+ __func__, opsize); -+ break; -+ } -+ -+ if (mopt->saw_rem_addr) { -+ mopt->more_rem_addr = 1; -+ break; -+ } -+ mopt->saw_rem_addr = 1; -+ mopt->rem_addr_ptr = ptr; -+ break; -+ case MPTCP_SUB_PRIO: -+ { -+ struct mp_prio *mpprio = (struct mp_prio *)ptr; -+ -+ if (opsize != MPTCP_SUB_LEN_PRIO && -+ opsize != MPTCP_SUB_LEN_PRIO_ADDR) { -+ mptcp_debug("%s: mp_prio: bad option size %d\n", -+ __func__, opsize); -+ break; -+ } -+ -+ mopt->saw_low_prio = 1; -+ mopt->low_prio = mpprio->b; -+ -+ if (opsize == MPTCP_SUB_LEN_PRIO_ADDR) { -+ mopt->saw_low_prio = 2; -+ mopt->prio_addr_id = mpprio->addr_id; -+ } -+ break; -+ } -+ case MPTCP_SUB_FAIL: -+ if (opsize != MPTCP_SUB_LEN_FAIL) { -+ mptcp_debug("%s: mp_fail: bad option size %d\n", -+ __func__, opsize); -+ break; -+ } -+ mopt->mp_fail = 1; -+ break; -+ case MPTCP_SUB_FCLOSE: -+ if (opsize != MPTCP_SUB_LEN_FCLOSE) { -+ mptcp_debug("%s: mp_fclose: bad option size %d\n", -+ __func__, opsize); -+ break; -+ } -+ -+ mopt->mp_fclose = 1; -+ mopt->mptcp_key = ((struct mp_fclose *)ptr)->key; -+ -+ break; -+ default: -+ mptcp_debug("%s: Received unkown subtype: %d\n", -+ __func__, mp_opt->sub); -+ break; -+ } -+} -+ -+int mptcp_check_rtt(const struct tcp_sock *tp, int time) -+{ -+ struct mptcp_cb *mpcb = tp->mpcb; -+ struct sock *sk; -+ u32 rtt_max = 0; -+ -+ /* In MPTCP, we take the max delay across all flows, -+ * in order to take into account meta-reordering buffers. -+ */ -+ mptcp_for_each_sk(mpcb, sk) { -+ if (!mptcp_sk_can_recv(sk)) -+ continue; -+ -+ if (rtt_max < tcp_sk(sk)->rcv_rtt_est.rtt) -+ rtt_max = tcp_sk(sk)->rcv_rtt_est.rtt; -+ } -+ if (time < (rtt_max >> 3) || !rtt_max) -+ return 1; -+ -+ return 0; -+} -+ -+static void mptcp_handle_add_addr(const unsigned char *ptr, struct sock *sk) -+{ -+ struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; -+ -+ if (mpadd->ipver == 4) { -+ __be16 port = 0; -+ if (mpadd->len == MPTCP_SUB_LEN_ADD_ADDR4 + 2) -+ port = mpadd->u.v4.port; -+ -+ mptcp_v4_add_raddress(tcp_sk(sk)->mpcb, &mpadd->u.v4.addr, port, -+ mpadd->addr_id); -+#if IS_ENABLED(CONFIG_IPV6) -+ } else if (mpadd->ipver == 6) { -+ __be16 port = 0; -+ if (mpadd->len == MPTCP_SUB_LEN_ADD_ADDR6 + 2) -+ port = mpadd->u.v6.port; -+ -+ mptcp_v6_add_raddress(tcp_sk(sk)->mpcb, &mpadd->u.v6.addr, port, -+ mpadd->addr_id); -+#endif /* CONFIG_IPV6 */ -+ } -+} -+ -+static void mptcp_handle_rem_addr(const unsigned char *ptr, struct sock *sk) -+{ -+ struct mp_remove_addr *mprem = (struct mp_remove_addr *)ptr; -+ int i; -+ u8 rem_id; -+ -+ for (i = 0; i <= mprem->len - MPTCP_SUB_LEN_REMOVE_ADDR; i++) { -+ rem_id = (&mprem->addrs_id)[i]; -+ if (!mptcp_rem_raddress(tcp_sk(sk)->mpcb, rem_id)) -+ mptcp_send_reset_rem_id(tcp_sk(sk)->mpcb, rem_id); -+ } -+} -+ -+static void mptcp_parse_addropt(const struct sk_buff *skb, struct sock *sk) -+{ -+ struct tcphdr *th = tcp_hdr(skb); -+ unsigned char *ptr; -+ int length = (th->doff * 4) - sizeof(struct tcphdr); -+ -+ /* Jump through the options to check whether ADD_ADDR is there */ -+ ptr = (unsigned char *)(th + 1); -+ while (length > 0) { -+ int opcode = *ptr++; -+ int opsize; -+ -+ switch (opcode) { -+ case TCPOPT_EOL: -+ return; -+ case TCPOPT_NOP: -+ length--; -+ continue; -+ default: -+ opsize = *ptr++; -+ if (opsize < 2) -+ return; -+ if (opsize > length) -+ return; /* don't parse partial options */ -+ if (opcode == TCPOPT_MPTCP && -+ ((struct mptcp_option *)ptr)->sub == MPTCP_SUB_ADD_ADDR) { -+#if IS_ENABLED(CONFIG_IPV6) -+ struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; -+ if ((mpadd->ipver == 4 && opsize != MPTCP_SUB_LEN_ADD_ADDR4 && -+ opsize != MPTCP_SUB_LEN_ADD_ADDR4 + 2) || -+ (mpadd->ipver == 6 && opsize != MPTCP_SUB_LEN_ADD_ADDR6 && -+ opsize != MPTCP_SUB_LEN_ADD_ADDR6 + 2)) -+#else -+ if (opsize != MPTCP_SUB_LEN_ADD_ADDR4 && -+ opsize != MPTCP_SUB_LEN_ADD_ADDR4 + 2) -+#endif /* CONFIG_IPV6 */ -+ goto cont; -+ -+ mptcp_handle_add_addr(ptr, sk); -+ } -+ if (opcode == TCPOPT_MPTCP && -+ ((struct mptcp_option *)ptr)->sub == MPTCP_SUB_REMOVE_ADDR) { -+ if ((opsize - MPTCP_SUB_LEN_REMOVE_ADDR) < 0) -+ goto cont; -+ -+ mptcp_handle_rem_addr(ptr, sk); -+ } -+cont: -+ ptr += opsize - 2; -+ length -= opsize; -+ } -+ } -+ return; -+} -+ -+static inline int mptcp_mp_fail_rcvd(struct sock *sk, const struct tcphdr *th) -+{ -+ struct mptcp_tcp_sock *mptcp = tcp_sk(sk)->mptcp; -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; -+ -+ if (unlikely(mptcp->rx_opt.mp_fail)) { -+ mptcp->rx_opt.mp_fail = 0; -+ -+ if (!th->rst && !mpcb->infinite_mapping_snd) { -+ struct sock *sk_it; -+ -+ mpcb->send_infinite_mapping = 1; -+ /* We resend everything that has not been acknowledged */ -+ meta_sk->sk_send_head = tcp_write_queue_head(meta_sk); -+ -+ /* We artificially restart the whole send-queue. Thus, -+ * it is as if no packets are in flight -+ */ -+ tcp_sk(meta_sk)->packets_out = 0; -+ -+ /* If the snd_nxt already wrapped around, we have to -+ * undo the wrapping, as we are restarting from snd_una -+ * on. -+ */ -+ if (tcp_sk(meta_sk)->snd_nxt < tcp_sk(meta_sk)->snd_una) { -+ mpcb->snd_high_order[mpcb->snd_hiseq_index] -= 2; -+ mpcb->snd_hiseq_index = mpcb->snd_hiseq_index ? 0 : 1; -+ } -+ tcp_sk(meta_sk)->snd_nxt = tcp_sk(meta_sk)->snd_una; -+ -+ /* Trigger a sending on the meta. */ -+ mptcp_push_pending_frames(meta_sk); -+ -+ mptcp_for_each_sk(mpcb, sk_it) { -+ if (sk != sk_it) -+ mptcp_sub_force_close(sk_it); -+ } -+ } -+ -+ return 0; -+ } -+ -+ if (unlikely(mptcp->rx_opt.mp_fclose)) { -+ struct sock *sk_it, *tmpsk; -+ -+ mptcp->rx_opt.mp_fclose = 0; -+ if (mptcp->rx_opt.mptcp_key != mpcb->mptcp_loc_key) -+ return 0; -+ -+ if (tcp_need_reset(sk->sk_state)) -+ tcp_send_active_reset(sk, GFP_ATOMIC); -+ -+ mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) -+ mptcp_sub_force_close(sk_it); -+ -+ tcp_reset(meta_sk); -+ -+ return 1; -+ } -+ -+ return 0; -+} -+ -+static inline void mptcp_path_array_check(struct sock *meta_sk) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ -+ if (unlikely(mpcb->list_rcvd)) { -+ mpcb->list_rcvd = 0; -+ if (mpcb->pm_ops->new_remote_address) -+ mpcb->pm_ops->new_remote_address(meta_sk); -+ } -+} -+ -+int mptcp_handle_options(struct sock *sk, const struct tcphdr *th, struct sk_buff *skb) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct mptcp_options_received *mopt = &tp->mptcp->rx_opt; -+ -+ if (tp->mpcb->infinite_mapping_rcv || tp->mpcb->infinite_mapping_snd) -+ return 0; -+ -+ if (mptcp_mp_fail_rcvd(sk, th)) -+ return 1; -+ -+ /* RFC 6824, Section 3.3: -+ * If a checksum is not present when its use has been negotiated, the -+ * receiver MUST close the subflow with a RST as it is considered broken. -+ */ -+ if (mptcp_is_data_seq(skb) && tp->mpcb->dss_csum && -+ !(TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_DSS_CSUM)) { -+ if (tcp_need_reset(sk->sk_state)) -+ tcp_send_active_reset(sk, GFP_ATOMIC); -+ -+ mptcp_sub_force_close(sk); -+ return 1; -+ } -+ -+ /* We have to acknowledge retransmissions of the third -+ * ack. -+ */ -+ if (mopt->join_ack) { -+ tcp_send_delayed_ack(sk); -+ mopt->join_ack = 0; -+ } -+ -+ if (mopt->saw_add_addr || mopt->saw_rem_addr) { -+ if (mopt->more_add_addr || mopt->more_rem_addr) { -+ mptcp_parse_addropt(skb, sk); -+ } else { -+ if (mopt->saw_add_addr) -+ mptcp_handle_add_addr(mopt->add_addr_ptr, sk); -+ if (mopt->saw_rem_addr) -+ mptcp_handle_rem_addr(mopt->rem_addr_ptr, sk); -+ } -+ -+ mopt->more_add_addr = 0; -+ mopt->saw_add_addr = 0; -+ mopt->more_rem_addr = 0; -+ mopt->saw_rem_addr = 0; -+ } -+ if (mopt->saw_low_prio) { -+ if (mopt->saw_low_prio == 1) { -+ tp->mptcp->rcv_low_prio = mopt->low_prio; -+ } else { -+ struct sock *sk_it; -+ mptcp_for_each_sk(tp->mpcb, sk_it) { -+ struct mptcp_tcp_sock *mptcp = tcp_sk(sk_it)->mptcp; -+ if (mptcp->rem_id == mopt->prio_addr_id) -+ mptcp->rcv_low_prio = mopt->low_prio; -+ } -+ } -+ mopt->saw_low_prio = 0; -+ } -+ -+ mptcp_data_ack(sk, skb); -+ -+ mptcp_path_array_check(mptcp_meta_sk(sk)); -+ /* Socket may have been mp_killed by a REMOVE_ADDR */ -+ if (tp->mp_killed) -+ return 1; -+ -+ return 0; -+} -+ -+/* The skptr is needed, because if we become MPTCP-capable, we have to switch -+ * from meta-socket to master-socket. -+ * -+ * @return: 1 - we want to reset this connection -+ * 2 - we want to discard the received syn/ack -+ * 0 - everything is fine - continue -+ */ -+int mptcp_rcv_synsent_state_process(struct sock *sk, struct sock **skptr, -+ struct sk_buff *skb, -+ struct mptcp_options_received *mopt) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ if (tp->mpc) { -+ u8 hash_mac_check[20]; -+ struct mptcp_cb *mpcb = tp->mpcb; -+ -+ mptcp_hmac_sha1((u8 *)&mpcb->mptcp_rem_key, -+ (u8 *)&mpcb->mptcp_loc_key, -+ (u8 *)&tp->mptcp->rx_opt.mptcp_recv_nonce, -+ (u8 *)&tp->mptcp->mptcp_loc_nonce, -+ (u32 *)hash_mac_check); -+ if (memcmp(hash_mac_check, -+ (char *)&tp->mptcp->rx_opt.mptcp_recv_tmac, 8)) { -+ mptcp_sub_force_close(sk); -+ return 1; -+ } -+ -+ /* Set this flag in order to postpone data sending -+ * until the 4th ack arrives. -+ */ -+ tp->mptcp->pre_established = 1; -+ tp->mptcp->rcv_low_prio = tp->mptcp->rx_opt.low_prio; -+ -+ mptcp_hmac_sha1((u8 *)&mpcb->mptcp_loc_key, -+ (u8 *)&mpcb->mptcp_rem_key, -+ (u8 *)&tp->mptcp->mptcp_loc_nonce, -+ (u8 *)&tp->mptcp->rx_opt.mptcp_recv_nonce, -+ (u32 *)&tp->mptcp->sender_mac[0]); -+ -+ } else if (mopt->saw_mpc) { -+ if (mptcp_create_master_sk(sk, mopt->mptcp_key, -+ ntohs(tcp_hdr(skb)->window))) -+ return 2; -+ -+ sk = tcp_sk(sk)->mpcb->master_sk; -+ *skptr = sk; -+ tp = tcp_sk(sk); -+ -+ /* snd_nxt - 1, because it has been incremented -+ * by tcp_connect for the SYN -+ */ -+ tp->mptcp->snt_isn = tp->snd_nxt - 1; -+ tp->mpcb->dss_csum = mopt->dss_csum; -+ tp->mptcp->include_mpc = 1; -+ -+ sk_set_socket(sk, mptcp_meta_sk(sk)->sk_socket); -+ sk->sk_wq = mptcp_meta_sk(sk)->sk_wq; -+ -+ mptcp_update_metasocket(sk, mptcp_meta_sk(sk)); -+ -+ /* hold in mptcp_inherit_sk due to initialization to 2 */ -+ sock_put(sk); -+ } else { -+ tp->request_mptcp = 0; -+ -+ if (tp->inside_tk_table) -+ mptcp_hash_remove(tp); -+ } -+ -+ if (tp->mpc) -+ tp->mptcp->rcv_isn = TCP_SKB_CB(skb)->seq; -+ -+ return 0; -+} -+ -+bool mptcp_should_expand_sndbuf(const struct sock *sk) -+{ -+ struct sock *sk_it; -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ int cnt_backups = 0; -+ int backup_available = 0; -+ -+ /* We circumvent this check in tcp_check_space, because we want to -+ * always call sk_write_space. So, we reproduce the check here. -+ */ -+ if (!meta_sk->sk_socket || -+ !test_bit(SOCK_NOSPACE, &meta_sk->sk_socket->flags)) -+ return false; -+ -+ /* If the user specified a specific send buffer setting, do -+ * not modify it. -+ */ -+ if (meta_sk->sk_userlocks & SOCK_SNDBUF_LOCK) -+ return false; -+ -+ /* If we are under global TCP memory pressure, do not expand. */ -+ if (sk_under_memory_pressure(meta_sk)) -+ return false; -+ -+ /* If we are under soft global TCP memory pressure, do not expand. */ -+ if (sk_memory_allocated(meta_sk) >= sk_prot_mem_limits(meta_sk, 0)) -+ return false; -+ -+ -+ /* For MPTCP we look for a subsocket that could send data. -+ * If we found one, then we update the send-buffer. -+ */ -+ mptcp_for_each_sk(meta_tp->mpcb, sk_it) { -+ struct tcp_sock *tp_it = tcp_sk(sk_it); -+ -+ if (!mptcp_sk_can_send(sk_it)) -+ continue; -+ -+ /* Backup-flows have to be counted - if there is no other -+ * subflow we take the backup-flow into account. */ -+ if (tp_it->mptcp->rcv_low_prio || tp_it->mptcp->low_prio) { -+ cnt_backups++; -+ } -+ -+ if (tp_it->packets_out < tp_it->snd_cwnd) { -+ if (tp_it->mptcp->rcv_low_prio || tp_it->mptcp->low_prio) { -+ backup_available = 1; -+ continue; -+ } -+ return true; -+ } -+ } -+ -+ /* Backup-flow is available for sending - update send-buffer */ -+ if (meta_tp->mpcb->cnt_established == cnt_backups && backup_available) -+ return true; -+ return false; -+} -+ -+void mptcp_init_buffer_space(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ int space; -+ -+ tcp_init_buffer_space(sk); -+ -+ if (is_master_tp(tp)) { -+ /* If there is only one subflow, we just use regular TCP -+ * autotuning. User-locks are handled already by -+ * tcp_init_buffer_space -+ */ -+ meta_tp->window_clamp = tp->window_clamp; -+ meta_tp->rcv_ssthresh = tp->rcv_ssthresh; -+ meta_sk->sk_rcvbuf = sk->sk_rcvbuf; -+ meta_sk->sk_sndbuf = sk->sk_sndbuf; -+ -+ return; -+ } -+ -+ if (meta_sk->sk_userlocks & SOCK_RCVBUF_LOCK) -+ goto snd_buf; -+ -+ /* Adding a new subflow to the rcv-buffer space. We make a simple -+ * addition, to give some space to allow traffic on the new subflow. -+ * Autotuning will increase it further later on. -+ */ -+ space = min(meta_sk->sk_rcvbuf + sk->sk_rcvbuf, sysctl_tcp_rmem[2]); -+ if (space > meta_sk->sk_rcvbuf) { -+ meta_tp->window_clamp += tp->window_clamp; -+ meta_tp->rcv_ssthresh += tp->rcv_ssthresh; -+ meta_sk->sk_rcvbuf = space; -+ } -+ -+snd_buf: -+ if (meta_sk->sk_userlocks & SOCK_SNDBUF_LOCK) -+ return; -+ -+ /* Adding a new subflow to the send-buffer space. We make a simple -+ * addition, to give some space to allow traffic on the new subflow. -+ * Autotuning will increase it further later on. -+ */ -+ space = min(meta_sk->sk_sndbuf + sk->sk_sndbuf, sysctl_tcp_wmem[2]); -+ if (space > meta_sk->sk_sndbuf) { -+ meta_sk->sk_sndbuf = space; -+ meta_sk->sk_write_space(meta_sk); -+ } -+} -+ -+void mptcp_tcp_set_rto(struct sock *sk) -+{ -+ tcp_set_rto(sk); -+ mptcp_set_rto(sk); -+} -diff --git a/net/mptcp/mptcp_ipv4.c b/net/mptcp/mptcp_ipv4.c -new file mode 100644 -index 0000000..b6053f1 ---- /dev/null -+++ b/net/mptcp/mptcp_ipv4.c -@@ -0,0 +1,603 @@ -+/* -+ * MPTCP implementation - IPv4-specific functions -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer: -+ * Christoph Paasch -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+u32 mptcp_v4_get_nonce(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport, -+ u32 seq) -+{ -+ u32 hash[MD5_DIGEST_WORDS]; -+ -+ hash[0] = (__force u32)saddr; -+ hash[1] = (__force u32)daddr; -+ hash[2] = ((__force u16)sport << 16) + (__force u16)dport; -+ hash[3] = seq; -+ -+ md5_transform(hash, mptcp_secret); -+ -+ return hash[0]; -+} -+ -+u64 mptcp_v4_get_key(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport) -+{ -+ u32 hash[MD5_DIGEST_WORDS]; -+ -+ hash[0] = (__force u32)saddr; -+ hash[1] = (__force u32)daddr; -+ hash[2] = ((__force u16)sport << 16) + (__force u16)dport; -+ hash[3] = mptcp_key_seed++; -+ -+ md5_transform(hash, mptcp_secret); -+ -+ return *((u64 *)hash); -+} -+ -+ -+static void mptcp_v4_reqsk_destructor(struct request_sock *req) -+{ -+ mptcp_reqsk_destructor(req); -+ -+ tcp_v4_reqsk_destructor(req); -+} -+ -+/* Similar to tcp_request_sock_ops */ -+struct request_sock_ops mptcp_request_sock_ops __read_mostly = { -+ .family = PF_INET, -+ .obj_size = sizeof(struct mptcp_request_sock), -+ .rtx_syn_ack = tcp_v4_rtx_synack, -+ .send_ack = tcp_v4_reqsk_send_ack, -+ .destructor = mptcp_v4_reqsk_destructor, -+ .send_reset = tcp_v4_send_reset, -+ .syn_ack_timeout = tcp_syn_ack_timeout, -+}; -+ -+static void mptcp_v4_reqsk_queue_hash_add(struct sock *meta_sk, -+ struct request_sock *req, -+ unsigned long timeout) -+{ -+ const u32 h1 = inet_synq_hash(inet_rsk(req)->ir_rmt_addr, -+ inet_rsk(req)->ir_rmt_port, -+ 0, MPTCP_HASH_SIZE); -+ /* We cannot call inet_csk_reqsk_queue_hash_add(), because we do not -+ * want to reset the keepalive-timer (responsible for retransmitting -+ * SYN/ACKs). We do not retransmit SYN/ACKs+MP_JOINs, because we cannot -+ * overload the keepalive timer. Also, it's not a big deal, because the -+ * third ACK of the MP_JOIN-handshake is sent in a reliable manner. So, -+ * if the third ACK gets lost, the client will handle the retransmission -+ * anyways. If our SYN/ACK gets lost, the client will retransmit the -+ * SYN. -+ */ -+ struct inet_connection_sock *meta_icsk = inet_csk(meta_sk); -+ struct listen_sock *lopt = meta_icsk->icsk_accept_queue.listen_opt; -+ const u32 h2 = inet_synq_hash(inet_rsk(req)->ir_rmt_addr, -+ inet_rsk(req)->ir_rmt_port, -+ lopt->hash_rnd, lopt->nr_table_entries); -+ -+ reqsk_queue_hash_req(&meta_icsk->icsk_accept_queue, h2, req, timeout); -+ reqsk_queue_added(&meta_icsk->icsk_accept_queue); -+ -+ spin_lock(&mptcp_reqsk_hlock); -+ list_add(&mptcp_rsk(req)->collide_tuple, &mptcp_reqsk_htb[h1]); -+ spin_unlock(&mptcp_reqsk_hlock); -+} -+ -+/* Similar to tcp_v4_conn_request */ -+static void mptcp_v4_join_request(struct sock *meta_sk, struct sk_buff *skb) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct tcp_options_received tmp_opt; -+ struct mptcp_options_received mopt; -+ struct request_sock *req; -+ struct inet_request_sock *ireq; -+ struct mptcp_request_sock *mtreq; -+ struct dst_entry *dst = NULL; -+ u8 mptcp_hash_mac[20]; -+ __be32 saddr = ip_hdr(skb)->saddr; -+ __be32 daddr = ip_hdr(skb)->daddr; -+ __u32 isn = TCP_SKB_CB(skb)->when; -+ int want_cookie = 0; -+ union inet_addr addr; -+ -+ tcp_clear_options(&tmp_opt); -+ mptcp_init_mp_opt(&mopt); -+ tmp_opt.mss_clamp = TCP_MSS_DEFAULT; -+ tmp_opt.user_mss = tcp_sk(meta_sk)->rx_opt.user_mss; -+ tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL); -+ -+ req = inet_reqsk_alloc(&mptcp_request_sock_ops); -+ if (!req) -+ return; -+ -+#ifdef CONFIG_TCP_MD5SIG -+ tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops; -+#endif -+ -+ tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; -+ tcp_openreq_init(req, &tmp_opt, skb); -+ -+ ireq = inet_rsk(req); -+ ireq->ir_loc_addr = daddr; -+ ireq->ir_rmt_addr = saddr; -+ ireq->no_srccheck = inet_sk(meta_sk)->transparent; -+ ireq->opt = tcp_v4_save_options(skb); -+ -+ if (security_inet_conn_request(meta_sk, skb, req)) -+ goto drop_and_free; -+ -+ if (!want_cookie || tmp_opt.tstamp_ok) -+ TCP_ECN_create_request(req, skb, sock_net(meta_sk)); -+ -+ if (!isn) { -+ struct flowi4 fl4; -+ -+ /* VJ's idea. We save last timestamp seen -+ * from the destination in peer table, when entering -+ * state TIME-WAIT, and check against it before -+ * accepting new connection request. -+ * -+ * If "isn" is not zero, this request hit alive -+ * timewait bucket, so that all the necessary checks -+ * are made in the function processing timewait state. -+ */ -+ if (tmp_opt.saw_tstamp && -+ tcp_death_row.sysctl_tw_recycle && -+ (dst = inet_csk_route_req(meta_sk, &fl4, req)) != NULL && -+ fl4.daddr == saddr) { -+ if (!tcp_peer_is_proven(req, dst, true)) { -+ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_PAWSPASSIVEREJECTED); -+ goto drop_and_release; -+ } -+ } -+ /* Kill the following clause, if you dislike this way. */ -+ else if (!sysctl_tcp_syncookies && -+ (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(meta_sk) < -+ (sysctl_max_syn_backlog >> 2)) && -+ !tcp_peer_is_proven(req, dst, false)) { -+ /* Without syncookies last quarter of -+ * backlog is filled with destinations, -+ * proven to be alive. -+ * It means that we continue to communicate -+ * to destinations, already remembered -+ * to the moment of synflood. -+ */ -+ LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"), -+ &saddr, ntohs(tcp_hdr(skb)->source)); -+ goto drop_and_release; -+ } -+ -+ isn = tcp_v4_init_sequence(skb); -+ } -+ tcp_rsk(req)->snt_isn = isn; -+ tcp_rsk(req)->snt_synack = tcp_time_stamp; -+ tcp_rsk(req)->listener = NULL; -+ -+ mtreq = mptcp_rsk(req); -+ mtreq->mpcb = mpcb; -+ INIT_LIST_HEAD(&mtreq->collide_tuple); -+ mtreq->mptcp_rem_nonce = mopt.mptcp_recv_nonce; -+ mtreq->mptcp_rem_key = mpcb->mptcp_rem_key; -+ mtreq->mptcp_loc_key = mpcb->mptcp_loc_key; -+ mtreq->mptcp_loc_nonce = mptcp_v4_get_nonce(saddr, daddr, -+ tcp_hdr(skb)->source, -+ tcp_hdr(skb)->dest, isn); -+ mptcp_hmac_sha1((u8 *)&mtreq->mptcp_loc_key, -+ (u8 *)&mtreq->mptcp_rem_key, -+ (u8 *)&mtreq->mptcp_loc_nonce, -+ (u8 *)&mtreq->mptcp_rem_nonce, (u32 *)mptcp_hash_mac); -+ mtreq->mptcp_hash_tmac = *(u64 *)mptcp_hash_mac; -+ -+ addr.ip = ireq->ir_loc_addr; -+ mtreq->loc_id = mpcb->pm_ops->get_local_id(AF_INET, &addr, sock_net(meta_sk)); -+ if (mtreq->loc_id == -1) /* Address not part of the allowed ones */ -+ goto drop_and_release; -+ mtreq->rem_id = mopt.rem_id; -+ mtreq->low_prio = mopt.low_prio; -+ tcp_rsk(req)->saw_mpc = 1; -+ -+ if (tcp_v4_send_synack(meta_sk, dst, req, skb_get_queue_mapping(skb))) -+ goto drop_and_free; -+ -+ /* Adding to request queue in metasocket */ -+ mptcp_v4_reqsk_queue_hash_add(meta_sk, req, TCP_TIMEOUT_INIT); -+ -+ return; -+ -+drop_and_release: -+ dst_release(dst); -+drop_and_free: -+ reqsk_free(req); -+ return; -+} -+ -+int mptcp_v4_rem_raddress(struct mptcp_cb *mpcb, u8 id) -+{ -+ int i; -+ -+ for (i = 0; i < MPTCP_MAX_ADDR; i++) { -+ if (!((1 << i) & mpcb->rem4_bits)) -+ continue; -+ -+ if (mpcb->remaddr4[i].rem4_id == id) { -+ /* remove address from bitfield */ -+ mpcb->rem4_bits &= ~(1 << i); -+ -+ return 0; -+ } -+ } -+ -+ return -1; -+} -+ -+/* Based on function tcp_v4_conn_request (tcp_ipv4.c) -+ * Returns -1 if there is no space anymore to store an additional -+ * address -+ */ -+int mptcp_v4_add_raddress(struct mptcp_cb *mpcb, const struct in_addr *addr, -+ __be16 port, u8 id) -+{ -+ int i; -+ struct mptcp_rem4 *rem4; -+ -+ mptcp_for_each_bit_set(mpcb->rem4_bits, i) { -+ rem4 = &mpcb->remaddr4[i]; -+ -+ /* Address is already in the list --- continue */ -+ if (rem4->rem4_id == id && -+ rem4->addr.s_addr == addr->s_addr && rem4->port == port) -+ return 0; -+ -+ /* This may be the case, when the peer is behind a NAT. He is -+ * trying to JOIN, thus sending the JOIN with a certain ID. -+ * However the src_addr of the IP-packet has been changed. We -+ * update the addr in the list, because this is the address as -+ * OUR BOX sees it. -+ */ -+ if (rem4->rem4_id == id && rem4->addr.s_addr != addr->s_addr) { -+ /* update the address */ -+ mptcp_debug("%s: updating old addr:%pI4 to addr %pI4 with id:%d\n", -+ __func__, &rem4->addr.s_addr, -+ &addr->s_addr, id); -+ rem4->addr.s_addr = addr->s_addr; -+ rem4->port = port; -+ mpcb->list_rcvd = 1; -+ return 0; -+ } -+ } -+ -+ i = mptcp_find_free_index(mpcb->rem4_bits); -+ /* Do we have already the maximum number of local/remote addresses? */ -+ if (i < 0) { -+ mptcp_debug("%s: At max num of remote addresses: %d --- not adding address: %pI4\n", -+ __func__, MPTCP_MAX_ADDR, &addr->s_addr); -+ return -1; -+ } -+ -+ rem4 = &mpcb->remaddr4[i]; -+ -+ /* Address is not known yet, store it */ -+ rem4->addr.s_addr = addr->s_addr; -+ rem4->port = port; -+ rem4->bitfield = 0; -+ rem4->retry_bitfield = 0; -+ rem4->rem4_id = id; -+ mpcb->list_rcvd = 1; -+ mpcb->rem4_bits |= (1 << i); -+ -+ return 0; -+} -+ -+/* Sets the bitfield of the remote-address field -+ * local address is not set as it will disappear with the global address-list -+ */ -+void mptcp_v4_set_init_addr_bit(struct mptcp_cb *mpcb, __be32 daddr, int index) -+{ -+ int i; -+ -+ mptcp_for_each_bit_set(mpcb->rem4_bits, i) { -+ if (mpcb->remaddr4[i].addr.s_addr == daddr) { -+ mpcb->remaddr4[i].bitfield |= (1 << index); -+ return; -+ } -+ } -+} -+ -+/* We only process join requests here. (either the SYN or the final ACK) */ -+int mptcp_v4_do_rcv(struct sock *meta_sk, struct sk_buff *skb) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct sock *child, *rsk = NULL; -+ int ret; -+ -+ if (!(TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_JOIN)) { -+ struct tcphdr *th = tcp_hdr(skb); -+ const struct iphdr *iph = ip_hdr(skb); -+ struct sock *sk; -+ -+ sk = inet_lookup_established(sock_net(meta_sk), &tcp_hashinfo, -+ iph->saddr, th->source, iph->daddr, -+ th->dest, inet_iif(skb)); -+ -+ if (!sk) { -+ kfree_skb(skb); -+ return 0; -+ } -+ if (is_meta_sk(sk)) { -+ WARN("%s Did not find a sub-sk - did found the meta!\n", __func__); -+ kfree_skb(skb); -+ sock_put(sk); -+ return 0; -+ } -+ -+ if (sk->sk_state == TCP_TIME_WAIT) { -+ inet_twsk_put(inet_twsk(sk)); -+ kfree_skb(skb); -+ return 0; -+ } -+ -+ ret = tcp_v4_do_rcv(sk, skb); -+ sock_put(sk); -+ -+ return ret; -+ } -+ TCP_SKB_CB(skb)->mptcp_flags = 0; -+ -+ /* Has been removed from the tk-table. Thus, no new subflows. -+ * -+ * Check for close-state is necessary, because we may have been closed -+ * without passing by mptcp_close(). -+ * -+ * When falling back, no new subflows are allowed either. -+ */ -+ if (meta_sk->sk_state == TCP_CLOSE || !tcp_sk(meta_sk)->inside_tk_table || -+ mpcb->infinite_mapping_rcv || mpcb->send_infinite_mapping) -+ goto reset_and_discard; -+ -+ child = tcp_v4_hnd_req(meta_sk, skb); -+ -+ if (!child) -+ goto discard; -+ -+ if (child != meta_sk) { -+ sock_rps_save_rxhash(child, skb); -+ /* We don't call tcp_child_process here, because we hold -+ * already the meta-sk-lock and are sure that it is not owned -+ * by the user. -+ */ -+ ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), skb->len); -+ bh_unlock_sock(child); -+ sock_put(child); -+ if (ret) { -+ rsk = child; -+ goto reset_and_discard; -+ } -+ } else { -+ if (tcp_hdr(skb)->syn) { -+ struct mp_join *join_opt = mptcp_find_join(skb); -+ /* Currently we make two calls to mptcp_find_join(). This -+ * can probably be optimized. -+ */ -+ if (mptcp_v4_add_raddress(mpcb, -+ (struct in_addr *)&ip_hdr(skb)->saddr, -+ 0, -+ join_opt->addr_id) < 0) -+ goto reset_and_discard; -+ mpcb->list_rcvd = 0; -+ -+ mptcp_v4_join_request(meta_sk, skb); -+ goto discard; -+ } -+ goto reset_and_discard; -+ } -+ return 0; -+ -+reset_and_discard: -+ tcp_v4_send_reset(rsk, skb); -+discard: -+ kfree_skb(skb); -+ return 0; -+} -+ -+/* After this, the ref count of the meta_sk associated with the request_sock -+ * is incremented. Thus it is the responsibility of the caller -+ * to call sock_put() when the reference is not needed anymore. -+ */ -+struct sock *mptcp_v4_search_req(const __be16 rport, const __be32 raddr, -+ const __be32 laddr, const struct net *net) -+{ -+ struct mptcp_request_sock *mtreq; -+ struct sock *meta_sk = NULL; -+ -+ spin_lock(&mptcp_reqsk_hlock); -+ list_for_each_entry(mtreq, -+ &mptcp_reqsk_htb[inet_synq_hash(raddr, rport, 0, -+ MPTCP_HASH_SIZE)], -+ collide_tuple) { -+ struct inet_request_sock *ireq = inet_rsk(rev_mptcp_rsk(mtreq)); -+ meta_sk = mtreq->mpcb->meta_sk; -+ -+ if (ireq->ir_rmt_port == rport && -+ ireq->ir_rmt_addr == raddr && -+ ireq->ir_loc_addr == laddr && -+ rev_mptcp_rsk(mtreq)->rsk_ops->family == AF_INET && -+ net_eq(net, sock_net(meta_sk))) -+ break; -+ meta_sk = NULL; -+ } -+ -+ if (meta_sk && unlikely(!atomic_inc_not_zero(&meta_sk->sk_refcnt))) -+ meta_sk = NULL; -+ spin_unlock(&mptcp_reqsk_hlock); -+ -+ return meta_sk; -+} -+ -+/* Create a new IPv4 subflow. -+ * -+ * We are in user-context and meta-sock-lock is hold. -+ */ -+int mptcp_init4_subsockets(struct sock *meta_sk, const struct mptcp_loc4 *loc, -+ struct mptcp_rem4 *rem) -+{ -+ struct tcp_sock *tp; -+ struct sock *sk; -+ struct sockaddr_in loc_in, rem_in; -+ struct socket sock; -+ int ulid_size = 0, ret; -+ -+ /** First, create and prepare the new socket */ -+ -+ sock.type = meta_sk->sk_socket->type; -+ sock.state = SS_UNCONNECTED; -+ sock.wq = meta_sk->sk_socket->wq; -+ sock.file = meta_sk->sk_socket->file; -+ sock.ops = NULL; -+ -+ ret = inet_create(sock_net(meta_sk), &sock, IPPROTO_TCP, 1); -+ if (unlikely(ret < 0)) { -+ mptcp_debug("%s inet_create failed ret: %d\n", __func__, ret); -+ return ret; -+ } -+ -+ sk = sock.sk; -+ tp = tcp_sk(sk); -+ -+ /* All subsockets need the MPTCP-lock-class */ -+ lockdep_set_class_and_name(&(sk)->sk_lock.slock, &meta_slock_key, "slock-AF_INET-MPTCP"); -+ lockdep_init_map(&(sk)->sk_lock.dep_map, "sk_lock-AF_INET-MPTCP", &meta_key, 0); -+ -+ if (mptcp_add_sock(meta_sk, sk, loc->loc4_id, rem->rem4_id, GFP_KERNEL)) -+ goto error; -+ -+ tp->mptcp->slave_sk = 1; -+ tp->mptcp->low_prio = loc->low_prio; -+ -+ /* Initializing the timer for an MPTCP subflow */ -+ setup_timer(&tp->mptcp->mptcp_ack_timer, mptcp_ack_handler, (unsigned long)sk); -+ -+ /** Then, connect the socket to the peer */ -+ -+ ulid_size = sizeof(struct sockaddr_in); -+ loc_in.sin_family = AF_INET; -+ rem_in.sin_family = AF_INET; -+ loc_in.sin_port = 0; -+ if (rem->port) -+ rem_in.sin_port = rem->port; -+ else -+ rem_in.sin_port = inet_sk(meta_sk)->inet_dport; -+ loc_in.sin_addr = loc->addr; -+ rem_in.sin_addr = rem->addr; -+ -+ ret = sock.ops->bind(&sock, (struct sockaddr *)&loc_in, ulid_size); -+ if (ret < 0) { -+ mptcp_debug("%s: MPTCP subsocket bind() failed, error %d\n", -+ __func__, ret); -+ goto error; -+ } -+ -+ mptcp_debug("%s: token %#x pi %d src_addr:%pI4:%d dst_addr:%pI4:%d\n", -+ __func__, tcp_sk(meta_sk)->mpcb->mptcp_loc_token, -+ tp->mptcp->path_index, &loc_in.sin_addr, -+ ntohs(loc_in.sin_port), &rem_in.sin_addr, -+ ntohs(rem_in.sin_port)); -+ -+ ret = sock.ops->connect(&sock, (struct sockaddr *)&rem_in, -+ ulid_size, O_NONBLOCK); -+ if (ret < 0 && ret != -EINPROGRESS) { -+ mptcp_debug("%s: MPTCP subsocket connect() failed, error %d\n", -+ __func__, ret); -+ goto error; -+ } -+ -+ sk_set_socket(sk, meta_sk->sk_socket); -+ sk->sk_wq = meta_sk->sk_wq; -+ -+ return 0; -+ -+error: -+ /* May happen if mptcp_add_sock fails first */ -+ if (!tp->mpc) { -+ tcp_close(sk, 0); -+ } else { -+ local_bh_disable(); -+ mptcp_sub_force_close(sk); -+ local_bh_enable(); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(mptcp_init4_subsockets); -+ -+/* General initialization of IPv4 for MPTCP */ -+int mptcp_pm_v4_init(void) -+{ -+ int ret = 0; -+ struct request_sock_ops *ops = &mptcp_request_sock_ops; -+ -+ ops->slab_name = kasprintf(GFP_KERNEL, "request_sock_%s", "MPTCP"); -+ if (ops->slab_name == NULL) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ ops->slab = kmem_cache_create(ops->slab_name, ops->obj_size, 0, -+ SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, -+ NULL); -+ -+ if (ops->slab == NULL) { -+ ret = -ENOMEM; -+ goto err_reqsk_create; -+ } -+ -+out: -+ return ret; -+ -+err_reqsk_create: -+ kfree(ops->slab_name); -+ ops->slab_name = NULL; -+ goto out; -+} -+ -+void mptcp_pm_v4_undo(void) -+{ -+ kmem_cache_destroy(mptcp_request_sock_ops.slab); -+ kfree(mptcp_request_sock_ops.slab_name); -+} -+ -+ -diff --git a/net/mptcp/mptcp_ipv6.c b/net/mptcp/mptcp_ipv6.c -new file mode 100644 -index 0000000..b6b444d ---- /dev/null -+++ b/net/mptcp/mptcp_ipv6.c -@@ -0,0 +1,822 @@ -+/* -+ * MPTCP implementation - IPv6-specific functions -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer: -+ * Jaakko Korkeaniemi -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+static int mptcp_v6v4_send_synack(struct sock *meta_sk, struct request_sock *req, -+ u16 queue_mapping); -+ -+__u32 mptcp_v6_get_nonce(const __be32 *saddr, const __be32 *daddr, -+ __be16 sport, __be16 dport, u32 seq) -+{ -+ u32 secret[MD5_MESSAGE_BYTES / 4]; -+ u32 hash[MD5_DIGEST_WORDS]; -+ u32 i; -+ -+ memcpy(hash, saddr, 16); -+ for (i = 0; i < 4; i++) -+ secret[i] = mptcp_secret[i] + (__force u32)daddr[i]; -+ secret[4] = mptcp_secret[4] + -+ (((__force u16)sport << 16) + (__force u16)dport); -+ secret[5] = seq; -+ for (i = 6; i < MD5_MESSAGE_BYTES / 4; i++) -+ secret[i] = mptcp_secret[i]; -+ -+ md5_transform(hash, secret); -+ -+ return hash[0]; -+} -+ -+u64 mptcp_v6_get_key(const __be32 *saddr, const __be32 *daddr, -+ __be16 sport, __be16 dport) -+{ -+ u32 secret[MD5_MESSAGE_BYTES / 4]; -+ u32 hash[MD5_DIGEST_WORDS]; -+ u32 i; -+ -+ memcpy(hash, saddr, 16); -+ for (i = 0; i < 4; i++) -+ secret[i] = mptcp_secret[i] + (__force u32)daddr[i]; -+ secret[4] = mptcp_secret[4] + -+ (((__force u16)sport << 16) + (__force u16)dport); -+ secret[5] = mptcp_key_seed++; -+ for (i = 5; i < MD5_MESSAGE_BYTES / 4; i++) -+ secret[i] = mptcp_secret[i]; -+ -+ md5_transform(hash, secret); -+ -+ return *((u64 *)hash); -+} -+ -+static void mptcp_v6_reqsk_destructor(struct request_sock *req) -+{ -+ mptcp_reqsk_destructor(req); -+ -+ tcp_v6_reqsk_destructor(req); -+} -+ -+/* Similar to tcp_v6_rtx_synack */ -+static int mptcp_v6_rtx_synack(struct sock *meta_sk, struct request_sock *req) -+{ -+ if (meta_sk->sk_family == AF_INET6) -+ return tcp_v6_rtx_synack(meta_sk, req); -+ -+ TCP_INC_STATS_BH(sock_net(meta_sk), TCP_MIB_RETRANSSEGS); -+ return mptcp_v6v4_send_synack(meta_sk, req, 0); -+} -+ -+/* Similar to tcp6_request_sock_ops */ -+struct request_sock_ops mptcp6_request_sock_ops __read_mostly = { -+ .family = AF_INET6, -+ .obj_size = sizeof(struct mptcp_request_sock), -+ .rtx_syn_ack = mptcp_v6_rtx_synack, -+ .send_ack = tcp_v6_reqsk_send_ack, -+ .destructor = mptcp_v6_reqsk_destructor, -+ .send_reset = tcp_v6_send_reset, -+ .syn_ack_timeout = tcp_syn_ack_timeout, -+}; -+ -+static void mptcp_v6_reqsk_queue_hash_add(struct sock *meta_sk, -+ struct request_sock *req, -+ unsigned long timeout) -+{ -+ const u32 h1 = inet6_synq_hash(&inet_rsk(req)->ir_v6_rmt_addr, -+ inet_rsk(req)->ir_rmt_port, -+ 0, MPTCP_HASH_SIZE); -+ /* We cannot call inet6_csk_reqsk_queue_hash_add(), because we do not -+ * want to reset the keepalive-timer (responsible for retransmitting -+ * SYN/ACKs). We do not retransmit SYN/ACKs+MP_JOINs, because we cannot -+ * overload the keepalive timer. Also, it's not a big deal, because the -+ * third ACK of the MP_JOIN-handshake is sent in a reliable manner. So, -+ * if the third ACK gets lost, the client will handle the retransmission -+ * anyways. If our SYN/ACK gets lost, the client will retransmit the -+ * SYN. -+ */ -+ struct inet_connection_sock *meta_icsk = inet_csk(meta_sk); -+ struct listen_sock *lopt = meta_icsk->icsk_accept_queue.listen_opt; -+ const u32 h2 = inet6_synq_hash(&inet_rsk(req)->ir_v6_rmt_addr, -+ inet_rsk(req)->ir_rmt_port, -+ lopt->hash_rnd, lopt->nr_table_entries); -+ -+ reqsk_queue_hash_req(&meta_icsk->icsk_accept_queue, h2, req, timeout); -+ reqsk_queue_added(&meta_icsk->icsk_accept_queue); -+ -+ spin_lock(&mptcp_reqsk_hlock); -+ list_add(&mptcp_rsk(req)->collide_tuple, &mptcp_reqsk_htb[h1]); -+ spin_unlock(&mptcp_reqsk_hlock); -+} -+ -+/* Similar to tcp_v6_send_synack -+ * -+ * The meta-socket is IPv4, but a new subsocket is IPv6 -+ */ -+static int mptcp_v6v4_send_synack(struct sock *meta_sk, struct request_sock *req, -+ u16 queue_mapping) -+{ -+ struct inet_request_sock *treq = inet_rsk(req); -+ struct sk_buff *skb; -+ struct flowi6 fl6; -+ struct dst_entry *dst; -+ int err = -ENOMEM; -+ -+ memset(&fl6, 0, sizeof(fl6)); -+ fl6.flowi6_proto = IPPROTO_TCP; -+ fl6.daddr = treq->ir_v6_rmt_addr; -+ fl6.saddr = treq->ir_v6_loc_addr; -+ fl6.flowlabel = 0; -+ fl6.flowi6_oif = treq->ir_iif; -+ fl6.flowi6_mark = meta_sk->sk_mark; -+ fl6.fl6_dport = inet_rsk(req)->ir_rmt_port; -+ fl6.fl6_sport = htons(inet_rsk(req)->ir_num); -+ security_req_classify_flow(req, flowi6_to_flowi(&fl6)); -+ -+ dst = ip6_dst_lookup_flow(meta_sk, &fl6, NULL); -+ if (IS_ERR(dst)) { -+ err = PTR_ERR(dst); -+ return err; -+ } -+ skb = tcp_make_synack(meta_sk, dst, req, NULL); -+ -+ if (skb) { -+ __tcp_v6_send_check(skb, &treq->ir_v6_loc_addr, -+ &treq->ir_v6_rmt_addr); -+ -+ fl6.daddr = treq->ir_v6_rmt_addr; -+ skb_set_queue_mapping(skb, queue_mapping); -+ err = ip6_xmit(meta_sk, skb, &fl6, NULL, 0); -+ err = net_xmit_eval(err); -+ } -+ -+ return err; -+} -+ -+/* Similar to tcp_v6_syn_recv_sock -+ * -+ * The meta-socket is IPv4, but a new subsocket is IPv6 -+ */ -+struct sock *mptcp_v6v4_syn_recv_sock(struct sock *meta_sk, struct sk_buff *skb, -+ struct request_sock *req, -+ struct dst_entry *dst) -+{ -+ struct inet_request_sock *treq; -+ struct ipv6_pinfo *newnp; -+ struct tcp6_sock *newtcp6sk; -+ struct inet_sock *newinet; -+ struct tcp_sock *newtp; -+ struct sock *newsk; -+ -+ treq = inet_rsk(req); -+ -+ if (sk_acceptq_is_full(meta_sk)) -+ goto out_overflow; -+ -+ if (!dst) { -+ /* This code is similar to inet6_csk_route_req, but as we -+ * don't have a np-pointer in the meta, we have to do it -+ * manually. -+ */ -+ struct flowi6 fl6; -+ -+ memset(&fl6, 0, sizeof(fl6)); -+ fl6.flowi6_proto = IPPROTO_TCP; -+ fl6.daddr = treq->ir_v6_rmt_addr; -+ fl6.saddr = treq->ir_v6_loc_addr; -+ fl6.flowi6_oif = treq->ir_iif; -+ fl6.flowi6_mark = meta_sk->sk_mark; -+ fl6.fl6_dport = inet_rsk(req)->ir_rmt_port; -+ fl6.fl6_sport = htons(inet_rsk(req)->ir_num); -+ security_req_classify_flow(req, flowi6_to_flowi(&fl6)); -+ -+ dst = ip6_dst_lookup_flow(meta_sk, &fl6, NULL); -+ if (IS_ERR(dst)) -+ goto out; -+ } -+ -+ newsk = tcp_create_openreq_child(meta_sk, req, skb); -+ if (newsk == NULL) -+ goto out_nonewsk; -+ -+ /* Diff to tcp_v6_syn_recv_sock: Must do this prior to __ip6_dst_store, -+ * as it tries to access the pinet6-pointer. -+ */ -+ newtcp6sk = (struct tcp6_sock *)newsk; -+ inet_sk(newsk)->pinet6 = &newtcp6sk->inet6; -+ -+ /* -+ * No need to charge this sock to the relevant IPv6 refcnt debug socks -+ * count here, tcp_create_openreq_child now does this for us, see the -+ * comment in that function for the gory details. -acme -+ */ -+ -+ newsk->sk_gso_type = SKB_GSO_TCPV6; -+ __ip6_dst_store(newsk, dst, NULL, NULL); -+ inet6_sk_rx_dst_set(newsk, skb); -+ -+ newtp = tcp_sk(newsk); -+ newinet = inet_sk(newsk); -+ newnp = inet6_sk(newsk); -+ -+ newsk->sk_v6_daddr = treq->ir_v6_rmt_addr; -+ newnp->saddr = treq->ir_v6_loc_addr; -+ newsk->sk_v6_rcv_saddr = treq->ir_v6_loc_addr; -+ newsk->sk_bound_dev_if = treq->ir_iif; -+ -+ /* Now IPv6 options... -+ -+ First: no IPv4 options. -+ */ -+ newinet->inet_opt = NULL; -+ newnp->ipv6_ac_list = NULL; -+ newnp->ipv6_fl_list = NULL; -+ newnp->rxopt.all = 0; -+ -+ /* Clone pktoptions received with SYN */ -+ newnp->pktoptions = NULL; -+ if (treq->pktopts != NULL) { -+ newnp->pktoptions = skb_clone(treq->pktopts, -+ sk_gfp_atomic(meta_sk, GFP_ATOMIC)); -+ consume_skb(treq->pktopts); -+ treq->pktopts = NULL; -+ if (newnp->pktoptions) -+ skb_set_owner_r(newnp->pktoptions, newsk); -+ } -+ newnp->opt = NULL; -+ newnp->mcast_oif = inet6_iif(skb); -+ newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; -+ newnp->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(skb)); -+ -+ /* Initialization copied from inet6_create - normally this should have -+ * been handled by the memcpy as in tcp_v6_syn_recv_sock -+ */ -+ newnp->hop_limit = -1; -+ newnp->mc_loop = 1; -+ newnp->pmtudisc = IPV6_PMTUDISC_WANT; -+ (void)xchg(&newnp->rxpmtu, NULL); -+ -+ inet_csk(newsk)->icsk_ext_hdr_len = 0; -+ -+ tcp_mtup_init(newsk); -+ tcp_sync_mss(newsk, dst_mtu(dst)); -+ newtp->advmss = dst_metric_advmss(dst); -+ if (tcp_sk(meta_sk)->rx_opt.user_mss && -+ tcp_sk(meta_sk)->rx_opt.user_mss < newtp->advmss) -+ newtp->advmss = tcp_sk(meta_sk)->rx_opt.user_mss; -+ -+ tcp_initialize_rcv_mss(newsk); -+ -+ newinet->inet_daddr = LOOPBACK4_IPV6; -+ newinet->inet_saddr = LOOPBACK4_IPV6; -+ newinet->inet_rcv_saddr = LOOPBACK4_IPV6; -+ -+ if (__inet_inherit_port(meta_sk, newsk) < 0) { -+ inet_csk_prepare_forced_close(newsk); -+ tcp_done(newsk); -+ goto out; -+ } -+ __inet6_hash(newsk, NULL); -+ -+ return newsk; -+ -+out_overflow: -+ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_LISTENOVERFLOWS); -+out_nonewsk: -+ dst_release(dst); -+out: -+ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_LISTENDROPS); -+ return NULL; -+} -+ -+/* Similar to tcp_v6_conn_request */ -+static void mptcp_v6_join_request(struct sock *meta_sk, struct sk_buff *skb) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct tcp_options_received tmp_opt; -+ struct mptcp_options_received mopt; -+ struct ipv6_pinfo *np = inet6_sk(meta_sk); -+ struct request_sock *req; -+ struct inet_request_sock *treq; -+ struct mptcp_request_sock *mtreq; -+ u8 mptcp_hash_mac[20]; -+ __u32 isn = TCP_SKB_CB(skb)->when; -+ struct dst_entry *dst = NULL; -+ struct flowi6 fl6; -+ int want_cookie = 0; -+ union inet_addr addr; -+ -+ tcp_clear_options(&tmp_opt); -+ mptcp_init_mp_opt(&mopt); -+ tmp_opt.mss_clamp = TCP_MSS_DEFAULT; -+ tmp_opt.user_mss = tcp_sk(meta_sk)->rx_opt.user_mss; -+ tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL); -+ -+ req = inet6_reqsk_alloc(&mptcp6_request_sock_ops); -+ if (!req) -+ return; -+ -+#ifdef CONFIG_TCP_MD5SIG -+ tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops; -+#endif -+ -+ tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; -+ tcp_openreq_init(req, &tmp_opt, skb); -+ -+ treq = inet_rsk(req); -+ treq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; -+ treq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; -+ -+ if (!want_cookie || tmp_opt.tstamp_ok) -+ TCP_ECN_create_request(req, skb, sock_net(meta_sk)); -+ -+ treq->ir_iif = meta_sk->sk_bound_dev_if; -+ -+ /* So that link locals have meaning */ -+ if (!meta_sk->sk_bound_dev_if && -+ ipv6_addr_type(&treq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) -+ treq->ir_iif = inet6_iif(skb); -+ -+ if (!isn) { -+ if (meta_sk->sk_family == AF_INET6 && -+ (ipv6_opt_accepted(meta_sk, skb) || -+ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || -+ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)) { -+ atomic_inc(&skb->users); -+ treq->pktopts = skb; -+ } -+ -+ /* VJ's idea. We save last timestamp seen -+ * from the destination in peer table, when entering -+ * state TIME-WAIT, and check against it before -+ * accepting new connection request. -+ * -+ * If "isn" is not zero, this request hit alive -+ * timewait bucket, so that all the necessary checks -+ * are made in the function processing timewait state. -+ */ -+ if (tmp_opt.saw_tstamp && -+ tcp_death_row.sysctl_tw_recycle && -+ (dst = inet6_csk_route_req(meta_sk, &fl6, req)) != NULL) { -+ if (!tcp_peer_is_proven(req, dst, true)) { -+ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_PAWSPASSIVEREJECTED); -+ goto drop_and_release; -+ } -+ } -+ /* Kill the following clause, if you dislike this way. */ -+ else if (!sysctl_tcp_syncookies && -+ (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(meta_sk) < -+ (sysctl_max_syn_backlog >> 2)) && -+ !tcp_peer_is_proven(req, dst, false)) { -+ /* Without syncookies last quarter of -+ * backlog is filled with destinations, -+ * proven to be alive. -+ * It means that we continue to communicate -+ * to destinations, already remembered -+ * to the moment of synflood. -+ */ -+ LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open request from %pI6/%u\n", -+ &treq->ir_v6_rmt_addr, -+ ntohs(tcp_hdr(skb)->source)); -+ goto drop_and_release; -+ } -+ -+ isn = tcp_v6_init_sequence(skb); -+ } -+ -+ tcp_rsk(req)->snt_isn = isn; -+ tcp_rsk(req)->snt_synack = tcp_time_stamp; -+ tcp_rsk(req)->listener = NULL; -+ -+ mtreq = mptcp_rsk(req); -+ mtreq->mpcb = mpcb; -+ INIT_LIST_HEAD(&mtreq->collide_tuple); -+ mtreq->mptcp_rem_nonce = mopt.mptcp_recv_nonce; -+ mtreq->mptcp_rem_key = mpcb->mptcp_rem_key; -+ mtreq->mptcp_loc_key = mpcb->mptcp_loc_key; -+ mtreq->mptcp_loc_nonce = mptcp_v6_get_nonce(ipv6_hdr(skb)->daddr.s6_addr32, -+ ipv6_hdr(skb)->saddr.s6_addr32, -+ tcp_hdr(skb)->dest, -+ tcp_hdr(skb)->source, isn); -+ mptcp_hmac_sha1((u8 *)&mtreq->mptcp_loc_key, -+ (u8 *)&mtreq->mptcp_rem_key, -+ (u8 *)&mtreq->mptcp_loc_nonce, -+ (u8 *)&mtreq->mptcp_rem_nonce, (u32 *)mptcp_hash_mac); -+ mtreq->mptcp_hash_tmac = *(u64 *)mptcp_hash_mac; -+ -+ addr.in6 = treq->ir_v6_loc_addr; -+ mtreq->loc_id = mpcb->pm_ops->get_local_id(AF_INET6, &addr, sock_net(meta_sk)); -+ if (mtreq->loc_id == -1) /* Address not part of the allowed ones */ -+ goto drop_and_release; -+ mtreq->rem_id = mopt.rem_id; -+ mtreq->low_prio = mopt.low_prio; -+ tcp_rsk(req)->saw_mpc = 1; -+ -+ if (meta_sk->sk_family == AF_INET6) { -+ if (tcp_v6_send_synack(meta_sk, dst, &fl6, req, -+ skb_get_queue_mapping(skb))) -+ goto drop_and_free; -+ } else { -+ if (mptcp_v6v4_send_synack(meta_sk, req, skb_get_queue_mapping(skb))) -+ goto drop_and_free; -+ } -+ -+ /* Adding to request queue in metasocket */ -+ mptcp_v6_reqsk_queue_hash_add(meta_sk, req, TCP_TIMEOUT_INIT); -+ -+ return; -+ -+drop_and_release: -+ dst_release(dst); -+drop_and_free: -+ reqsk_free(req); -+ return; -+} -+ -+int mptcp_v6_rem_raddress(struct mptcp_cb *mpcb, u8 id) -+{ -+ int i; -+ -+ for (i = 0; i < MPTCP_MAX_ADDR; i++) { -+ if (!((1 << i) & mpcb->rem6_bits)) -+ continue; -+ -+ if (mpcb->remaddr6[i].rem6_id == id) { -+ /* remove address from bitfield */ -+ mpcb->rem6_bits &= ~(1 << i); -+ -+ return 0; -+ } -+ } -+ -+ return -1; -+} -+ -+/* Returns -1 if there is no space anymore to store an additional -+ * address -+ */ -+int mptcp_v6_add_raddress(struct mptcp_cb *mpcb, const struct in6_addr *addr, -+ __be16 port, u8 id) -+{ -+ int i; -+ struct mptcp_rem6 *rem6; -+ -+ mptcp_for_each_bit_set(mpcb->rem6_bits, i) { -+ rem6 = &mpcb->remaddr6[i]; -+ -+ /* Address is already in the list --- continue */ -+ if (rem6->rem6_id == id && -+ ipv6_addr_equal(&rem6->addr, addr) && rem6->port == port) -+ return 0; -+ -+ /* This may be the case, when the peer is behind a NAT. He is -+ * trying to JOIN, thus sending the JOIN with a certain ID. -+ * However the src_addr of the IP-packet has been changed. We -+ * update the addr in the list, because this is the address as -+ * OUR BOX sees it. -+ */ -+ if (rem6->rem6_id == id) { -+ /* update the address */ -+ mptcp_debug("%s: updating old addr: %pI6 to addr %pI6 with id:%d\n", -+ __func__, &rem6->addr, addr, id); -+ rem6->addr = *addr; -+ rem6->port = port; -+ mpcb->list_rcvd = 1; -+ return 0; -+ } -+ } -+ -+ i = mptcp_find_free_index(mpcb->rem6_bits); -+ /* Do we have already the maximum number of local/remote addresses? */ -+ if (i < 0) { -+ mptcp_debug("%s: At max num of remote addresses: %d --- not adding address: %pI6\n", -+ __func__, MPTCP_MAX_ADDR, addr); -+ return -1; -+ } -+ -+ rem6 = &mpcb->remaddr6[i]; -+ -+ /* Address is not known yet, store it */ -+ rem6->addr = *addr; -+ rem6->port = port; -+ rem6->bitfield = 0; -+ rem6->retry_bitfield = 0; -+ rem6->rem6_id = id; -+ mpcb->list_rcvd = 1; -+ mpcb->rem6_bits |= (1 << i); -+ -+ return 0; -+} -+ -+/* Sets the bitfield of the remote-address field -+ * local address is not set as it will disappear with the global address-list -+ */ -+void mptcp_v6_set_init_addr_bit(struct mptcp_cb *mpcb, -+ const struct in6_addr *daddr, int index) -+{ -+ int i; -+ mptcp_for_each_bit_set(mpcb->rem6_bits, i) { -+ if (ipv6_addr_equal(&mpcb->remaddr6[i].addr, daddr)) { -+ mpcb->remaddr6[i].bitfield |= (1 << index); -+ return; -+ } -+ } -+} -+ -+int mptcp_v6_do_rcv(struct sock *meta_sk, struct sk_buff *skb) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct sock *child, *rsk = NULL; -+ int ret; -+ -+ if (!(TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_JOIN)) { -+ struct tcphdr *th = tcp_hdr(skb); -+ const struct ipv6hdr *ip6h = ipv6_hdr(skb); -+ struct sock *sk; -+ -+ sk = __inet6_lookup_established(sock_net(meta_sk), -+ &tcp_hashinfo, -+ &ip6h->saddr, th->source, -+ &ip6h->daddr, ntohs(th->dest), -+ inet6_iif(skb)); -+ -+ if (!sk) { -+ kfree_skb(skb); -+ return 0; -+ } -+ if (is_meta_sk(sk)) { -+ WARN("%s Did not find a sub-sk!\n", __func__); -+ kfree_skb(skb); -+ sock_put(sk); -+ return 0; -+ } -+ -+ if (sk->sk_state == TCP_TIME_WAIT) { -+ inet_twsk_put(inet_twsk(sk)); -+ kfree_skb(skb); -+ return 0; -+ } -+ -+ ret = tcp_v6_do_rcv(sk, skb); -+ sock_put(sk); -+ -+ return ret; -+ } -+ TCP_SKB_CB(skb)->mptcp_flags = 0; -+ -+ /* Has been removed from the tk-table. Thus, no new subflows. -+ * -+ * Check for close-state is necessary, because we may have been closed -+ * without passing by mptcp_close(). -+ * -+ * When falling back, no new subflows are allowed either. -+ */ -+ if (meta_sk->sk_state == TCP_CLOSE || !tcp_sk(meta_sk)->inside_tk_table || -+ mpcb->infinite_mapping_rcv || mpcb->send_infinite_mapping) -+ goto reset_and_discard; -+ -+ child = tcp_v6_hnd_req(meta_sk, skb); -+ -+ if (!child) -+ goto discard; -+ -+ if (child != meta_sk) { -+ sock_rps_save_rxhash(child, skb); -+ /* We don't call tcp_child_process here, because we hold -+ * already the meta-sk-lock and are sure that it is not owned -+ * by the user. -+ */ -+ ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), skb->len); -+ bh_unlock_sock(child); -+ sock_put(child); -+ if (ret) { -+ rsk = child; -+ goto reset_and_discard; -+ } -+ } else { -+ if (tcp_hdr(skb)->syn) { -+ struct mp_join *join_opt = mptcp_find_join(skb); -+ /* Currently we make two calls to mptcp_find_join(). This -+ * can probably be optimized. */ -+ if (mptcp_v6_add_raddress(mpcb, -+ (struct in6_addr *)&ipv6_hdr(skb)->saddr, -+ 0, -+ join_opt->addr_id) < 0) -+ goto reset_and_discard; -+ mpcb->list_rcvd = 0; -+ -+ mptcp_v6_join_request(meta_sk, skb); -+ goto discard; -+ } -+ goto reset_and_discard; -+ } -+ return 0; -+ -+reset_and_discard: -+ tcp_v6_send_reset(rsk, skb); -+discard: -+ kfree_skb(skb); -+ return 0; -+} -+ -+/* After this, the ref count of the meta_sk associated with the request_sock -+ * is incremented. Thus it is the responsibility of the caller -+ * to call sock_put() when the reference is not needed anymore. -+ */ -+struct sock *mptcp_v6_search_req(const __be16 rport, const struct in6_addr *raddr, -+ const struct in6_addr *laddr, const struct net *net) -+{ -+ struct mptcp_request_sock *mtreq; -+ struct sock *meta_sk = NULL; -+ -+ spin_lock(&mptcp_reqsk_hlock); -+ list_for_each_entry(mtreq, -+ &mptcp_reqsk_htb[inet6_synq_hash(raddr, rport, 0, -+ MPTCP_HASH_SIZE)], -+ collide_tuple) { -+ struct inet_request_sock *treq = inet_rsk(rev_mptcp_rsk(mtreq)); -+ meta_sk = mtreq->mpcb->meta_sk; -+ -+ if (inet_rsk(rev_mptcp_rsk(mtreq))->ir_rmt_port == rport && -+ rev_mptcp_rsk(mtreq)->rsk_ops->family == AF_INET6 && -+ ipv6_addr_equal(&treq->ir_v6_rmt_addr, raddr) && -+ ipv6_addr_equal(&treq->ir_v6_loc_addr, laddr) && -+ net_eq(net, sock_net(meta_sk))) -+ break; -+ meta_sk = NULL; -+ } -+ -+ if (meta_sk && unlikely(!atomic_inc_not_zero(&meta_sk->sk_refcnt))) -+ meta_sk = NULL; -+ spin_unlock(&mptcp_reqsk_hlock); -+ -+ return meta_sk; -+} -+ -+/* Create a new IPv6 subflow. -+ * -+ * We are in user-context and meta-sock-lock is hold. -+ */ -+int mptcp_init6_subsockets(struct sock *meta_sk, const struct mptcp_loc6 *loc, -+ struct mptcp_rem6 *rem) -+{ -+ struct tcp_sock *tp; -+ struct sock *sk; -+ struct sockaddr_in6 loc_in, rem_in; -+ struct socket sock; -+ int ulid_size = 0, ret; -+ -+ /** First, create and prepare the new socket */ -+ -+ sock.type = meta_sk->sk_socket->type; -+ sock.state = SS_UNCONNECTED; -+ sock.wq = meta_sk->sk_socket->wq; -+ sock.file = meta_sk->sk_socket->file; -+ sock.ops = NULL; -+ -+ ret = inet6_create(sock_net(meta_sk), &sock, IPPROTO_TCP, 1); -+ if (unlikely(ret < 0)) { -+ mptcp_debug("%s inet6_create failed ret: %d\n", __func__, ret); -+ return ret; -+ } -+ -+ sk = sock.sk; -+ tp = tcp_sk(sk); -+ -+ /* All subsockets need the MPTCP-lock-class */ -+ lockdep_set_class_and_name(&(sk)->sk_lock.slock, &meta_slock_key, "slock-AF_INET-MPTCP"); -+ lockdep_init_map(&(sk)->sk_lock.dep_map, "sk_lock-AF_INET-MPTCP", &meta_key, 0); -+ -+ if (mptcp_add_sock(meta_sk, sk, loc->loc6_id, rem->rem6_id, GFP_KERNEL)) -+ goto error; -+ -+ tp->mptcp->slave_sk = 1; -+ tp->mptcp->low_prio = loc->low_prio; -+ -+ /* Initializing the timer for an MPTCP subflow */ -+ setup_timer(&tp->mptcp->mptcp_ack_timer, mptcp_ack_handler, (unsigned long)sk); -+ -+ /** Then, connect the socket to the peer */ -+ -+ ulid_size = sizeof(struct sockaddr_in6); -+ loc_in.sin6_family = AF_INET6; -+ rem_in.sin6_family = AF_INET6; -+ loc_in.sin6_port = 0; -+ if (rem->port) -+ rem_in.sin6_port = rem->port; -+ else -+ rem_in.sin6_port = inet_sk(meta_sk)->inet_dport; -+ loc_in.sin6_addr = loc->addr; -+ rem_in.sin6_addr = rem->addr; -+ -+ ret = sock.ops->bind(&sock, (struct sockaddr *)&loc_in, ulid_size); -+ if (ret < 0) { -+ mptcp_debug("%s: MPTCP subsocket bind()failed, error %d\n", -+ __func__, ret); -+ goto error; -+ } -+ -+ mptcp_debug("%s: token %#x pi %d src_addr:%pI6:%d dst_addr:%pI6:%d\n", -+ __func__, tcp_sk(meta_sk)->mpcb->mptcp_loc_token, -+ tp->mptcp->path_index, &loc_in.sin6_addr, -+ ntohs(loc_in.sin6_port), &rem_in.sin6_addr, -+ ntohs(rem_in.sin6_port)); -+ -+ ret = sock.ops->connect(&sock, (struct sockaddr *)&rem_in, -+ ulid_size, O_NONBLOCK); -+ if (ret < 0 && ret != -EINPROGRESS) { -+ mptcp_debug("%s: MPTCP subsocket connect() failed, error %d\n", -+ __func__, ret); -+ goto error; -+ } -+ -+ sk_set_socket(sk, meta_sk->sk_socket); -+ sk->sk_wq = meta_sk->sk_wq; -+ -+ return 0; -+ -+error: -+ /* May happen if mptcp_add_sock fails first */ -+ if (!tp->mpc) { -+ tcp_close(sk, 0); -+ } else { -+ local_bh_disable(); -+ mptcp_sub_force_close(sk); -+ local_bh_enable(); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(mptcp_init6_subsockets); -+ -+int mptcp_pm_v6_init(void) -+{ -+ int ret = 0; -+ struct request_sock_ops *ops = &mptcp6_request_sock_ops; -+ -+ ops->slab_name = kasprintf(GFP_KERNEL, "request_sock_%s", "MPTCP6"); -+ if (ops->slab_name == NULL) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ ops->slab = kmem_cache_create(ops->slab_name, ops->obj_size, 0, -+ SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, -+ NULL); -+ -+ if (ops->slab == NULL) { -+ ret = -ENOMEM; -+ goto err_reqsk_create; -+ } -+ -+out: -+ return ret; -+ -+err_reqsk_create: -+ kfree(ops->slab_name); -+ ops->slab_name = NULL; -+ goto out; -+} -+ -+void mptcp_pm_v6_undo(void) -+{ -+ kmem_cache_destroy(mptcp6_request_sock_ops.slab); -+ kfree(mptcp6_request_sock_ops.slab_name); -+} -diff --git a/net/mptcp/mptcp_ndiffports.c b/net/mptcp/mptcp_ndiffports.c -new file mode 100644 -index 0000000..a126325 ---- /dev/null -+++ b/net/mptcp/mptcp_ndiffports.c -@@ -0,0 +1,171 @@ -+#include -+ -+#include -+#include -+ -+#if IS_ENABLED(CONFIG_IPV6) -+#include -+#endif -+ -+struct ndiffports_priv { -+ /* Worker struct for subflow establishment */ -+ struct work_struct subflow_work; -+ -+ struct mptcp_cb *mpcb; -+}; -+ -+static int sysctl_mptcp_ndiffports __read_mostly = 2; -+ -+/** -+ * Create all new subflows, by doing calls to mptcp_initX_subsockets -+ * -+ * This function uses a goto next_subflow, to allow releasing the lock between -+ * new subflows and giving other processes a chance to do some work on the -+ * socket and potentially finishing the communication. -+ **/ -+static void create_subflow_worker(struct work_struct *work) -+{ -+ struct ndiffports_priv *pm_priv = container_of(work, -+ struct ndiffports_priv, -+ subflow_work); -+ struct mptcp_cb *mpcb = pm_priv->mpcb; -+ struct sock *meta_sk = mpcb->meta_sk; -+ int iter = 0; -+ -+next_subflow: -+ if (iter) { -+ release_sock(meta_sk); -+ mutex_unlock(&mpcb->mpcb_mutex); -+ -+ yield(); -+ } -+ mutex_lock(&mpcb->mpcb_mutex); -+ lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); -+ -+ iter++; -+ -+ if (sock_flag(meta_sk, SOCK_DEAD)) -+ goto exit; -+ -+ if (mpcb->master_sk && -+ !tcp_sk(mpcb->master_sk)->mptcp->fully_established) -+ goto exit; -+ -+ if (sysctl_mptcp_ndiffports > iter && -+ sysctl_mptcp_ndiffports > mpcb->cnt_subflows) { -+ if (meta_sk->sk_family == AF_INET || -+ mptcp_v6_is_v4_mapped(meta_sk)) { -+ struct mptcp_loc4 loc; -+ -+ loc.addr.s_addr = inet_sk(meta_sk)->inet_saddr; -+ loc.loc4_id = 0; -+ loc.low_prio = 0; -+ -+ mptcp_init4_subsockets(meta_sk, &loc, &mpcb->remaddr4[0]); -+ } else { -+#if IS_ENABLED(CONFIG_IPV6) -+ struct mptcp_loc6 loc; -+ -+ loc.addr = inet6_sk(meta_sk)->saddr; -+ loc.loc6_id = 0; -+ loc.low_prio = 0; -+ -+ mptcp_init6_subsockets(meta_sk, &loc, &mpcb->remaddr6[0]); -+#endif -+ } -+ goto next_subflow; -+ } -+ -+exit: -+ release_sock(meta_sk); -+ mutex_unlock(&mpcb->mpcb_mutex); -+ sock_put(meta_sk); -+} -+ -+static void ndiffports_new_session(struct sock *meta_sk, int index) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct ndiffports_priv *fmp = (struct ndiffports_priv *)&mpcb->mptcp_pm[0]; -+ -+ /* Initialize workqueue-struct */ -+ INIT_WORK(&fmp->subflow_work, create_subflow_worker); -+ fmp->mpcb = mpcb; -+} -+ -+static void ndiffports_create_subflows(struct sock *meta_sk) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct ndiffports_priv *pm_priv = (struct ndiffports_priv *)&mpcb->mptcp_pm[0]; -+ -+ if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv || -+ mpcb->send_infinite_mapping || -+ mpcb->server_side || sock_flag(meta_sk, SOCK_DEAD)) -+ return; -+ -+ if (!work_pending(&pm_priv->subflow_work)) { -+ sock_hold(meta_sk); -+ queue_work(mptcp_wq, &pm_priv->subflow_work); -+ } -+} -+ -+static int ndiffports_get_local_index(sa_family_t family, union inet_addr *addr, -+ struct net *net) -+{ -+ return 0; -+} -+ -+static struct mptcp_pm_ops ndiffports __read_mostly = { -+ .new_session = ndiffports_new_session, -+ .fully_established = ndiffports_create_subflows, -+ .get_local_index = ndiffports_get_local_index, -+ .get_local_id = ndiffports_get_local_index, -+ .name = "ndiffports", -+ .owner = THIS_MODULE, -+}; -+ -+static struct ctl_table ndiff_table[] = { -+ { -+ .procname = "mptcp_ndiffports", -+ .data = &sysctl_mptcp_ndiffports, -+ .maxlen = sizeof(int), -+ .mode = 0644, -+ .proc_handler = &proc_dointvec -+ }, -+ { } -+}; -+ -+struct ctl_table_header *mptcp_sysctl; -+ -+/* General initialization of MPTCP_PM */ -+static int __init ndiffports_register(void) -+{ -+ BUILD_BUG_ON(sizeof(struct ndiffports_priv) > MPTCP_PM_SIZE); -+ -+ mptcp_sysctl = register_net_sysctl(&init_net, "net/mptcp", ndiff_table); -+ if (!mptcp_sysctl) -+ goto exit; -+ -+ if (mptcp_register_path_manager(&ndiffports)) -+ goto pm_failed; -+ -+ return 0; -+ -+pm_failed: -+ unregister_net_sysctl_table(mptcp_sysctl); -+exit: -+ return -1; -+} -+ -+static void ndiffports_unregister(void) -+{ -+ mptcp_unregister_path_manager(&ndiffports); -+ unregister_net_sysctl_table(mptcp_sysctl); -+} -+ -+module_init(ndiffports_register); -+module_exit(ndiffports_unregister); -+ -+MODULE_AUTHOR("Christoph Paasch"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("NDIFF-PORTS MPTCP"); -+MODULE_VERSION("0.88"); -diff --git a/net/mptcp/mptcp_ofo_queue.c b/net/mptcp/mptcp_ofo_queue.c -new file mode 100644 -index 0000000..e182855 ---- /dev/null -+++ b/net/mptcp/mptcp_ofo_queue.c -@@ -0,0 +1,278 @@ -+/* -+ * MPTCP implementation - Fast algorithm for MPTCP meta-reordering -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer & Author: -+ * Christoph Paasch -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include -+#include -+#include -+#include -+ -+void mptcp_remove_shortcuts(const struct mptcp_cb *mpcb, -+ const struct sk_buff *skb) -+{ -+ struct tcp_sock *tp; -+ -+ mptcp_for_each_tp(mpcb, tp) { -+ if (tp->mptcp->shortcut_ofoqueue == skb) { -+ tp->mptcp->shortcut_ofoqueue = NULL; -+ return; -+ } -+ } -+} -+ -+/* Does 'skb' fits after 'here' in the queue 'head' ? -+ * If yes, we queue it and return 1 -+ */ -+static int mptcp_ofo_queue_after(struct sk_buff_head *head, -+ struct sk_buff *skb, struct sk_buff *here, -+ struct tcp_sock *tp) -+{ -+ struct sock *meta_sk = tp->meta_sk; -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ u32 seq = TCP_SKB_CB(skb)->seq; -+ u32 end_seq = TCP_SKB_CB(skb)->end_seq; -+ -+ /* We want to queue skb after here, thus seq >= end_seq */ -+ if (before(seq, TCP_SKB_CB(here)->end_seq)) -+ return 0; -+ -+ if (seq == TCP_SKB_CB(here)->end_seq) { -+ bool fragstolen = false; -+ -+ if (!tcp_try_coalesce(meta_sk, here, skb, &fragstolen)) { -+ __skb_queue_after(&meta_tp->out_of_order_queue, here, skb); -+ return 1; -+ } else { -+ kfree_skb_partial(skb, fragstolen); -+ return -1; -+ } -+ } -+ -+ /* If here is the last one, we can always queue it */ -+ if (skb_queue_is_last(head, here)) { -+ __skb_queue_after(head, here, skb); -+ return 1; -+ } else { -+ struct sk_buff *skb1 = skb_queue_next(head, here); -+ /* It's not the last one, but does it fits between 'here' and -+ * the one after 'here' ? Thus, does end_seq <= after_here->seq -+ */ -+ if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) { -+ __skb_queue_after(head, here, skb); -+ return 1; -+ } -+ } -+ -+ return 0; -+} -+ -+static void try_shortcut(struct sk_buff *shortcut, struct sk_buff *skb, -+ struct sk_buff_head *head, struct tcp_sock *tp) -+{ -+ struct sock *meta_sk = tp->meta_sk; -+ struct tcp_sock *tp_it, *meta_tp = tcp_sk(meta_sk); -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ struct sk_buff *skb1, *best_shortcut = NULL; -+ u32 seq = TCP_SKB_CB(skb)->seq; -+ u32 end_seq = TCP_SKB_CB(skb)->end_seq; -+ u32 distance = 0xffffffff; -+ -+ /* First, check the tp's shortcut */ -+ if (!shortcut) { -+ if (skb_queue_empty(head)) { -+ __skb_queue_head(head, skb); -+ goto end; -+ } -+ } else { -+ int ret = mptcp_ofo_queue_after(head, skb, shortcut, tp); -+ /* Does the tp's shortcut is a hit? If yes, we insert. */ -+ -+ if (ret) { -+ skb = (ret > 0) ? skb : NULL; -+ goto end; -+ } -+ } -+ -+ /* Check the shortcuts of the other subsockets. */ -+ mptcp_for_each_tp(mpcb, tp_it) { -+ shortcut = tp_it->mptcp->shortcut_ofoqueue; -+ /* Can we queue it here? If yes, do so! */ -+ if (shortcut) { -+ int ret = mptcp_ofo_queue_after(head, skb, shortcut, tp); -+ -+ if (ret) { -+ skb = (ret > 0) ? skb : NULL; -+ goto end; -+ } -+ } -+ -+ /* Could not queue it, check if we are close. -+ * We are looking for a shortcut, close enough to seq to -+ * set skb1 prematurely and thus improve the subsequent lookup, -+ * which tries to find a skb1 so that skb1->seq <= seq. -+ * -+ * So, here we only take shortcuts, whose shortcut->seq > seq, -+ * and minimize the distance between shortcut->seq and seq and -+ * set best_shortcut to this one with the minimal distance. -+ * -+ * That way, the subsequent while-loop is shortest. -+ */ -+ if (shortcut && after(TCP_SKB_CB(shortcut)->seq, seq)) { -+ /* Are we closer than the current best shortcut? */ -+ if ((u32)(TCP_SKB_CB(shortcut)->seq - seq) < distance) { -+ distance = (u32)(TCP_SKB_CB(shortcut)->seq - seq); -+ best_shortcut = shortcut; -+ } -+ } -+ } -+ -+ if (best_shortcut) -+ skb1 = best_shortcut; -+ else -+ skb1 = skb_peek_tail(head); -+ -+ if (seq == TCP_SKB_CB(skb1)->end_seq) { -+ bool fragstolen = false; -+ -+ if (!tcp_try_coalesce(meta_sk, skb1, skb, &fragstolen)) { -+ __skb_queue_after(&meta_tp->out_of_order_queue, skb1, skb); -+ } else { -+ kfree_skb_partial(skb, fragstolen); -+ skb = NULL; -+ } -+ -+ goto end; -+ } -+ -+ /* Find the insertion point, starting from best_shortcut if available. -+ * -+ * Inspired from tcp_data_queue_ofo. -+ */ -+ while (1) { -+ /* skb1->seq <= seq */ -+ if (!after(TCP_SKB_CB(skb1)->seq, seq)) -+ break; -+ if (skb_queue_is_first(head, skb1)) { -+ skb1 = NULL; -+ break; -+ } -+ skb1 = skb_queue_prev(head, skb1); -+ } -+ -+ /* Do skb overlap to previous one? */ -+ if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { -+ if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { -+ /* All the bits are present. */ -+ __kfree_skb(skb); -+ skb = NULL; -+ goto end; -+ } -+ if (seq == TCP_SKB_CB(skb1)->seq) { -+ if (skb_queue_is_first(head, skb1)) -+ skb1 = NULL; -+ else -+ skb1 = skb_queue_prev(head, skb1); -+ } -+ } -+ if (!skb1) -+ __skb_queue_head(head, skb); -+ else -+ __skb_queue_after(head, skb1, skb); -+ -+ /* And clean segments covered by new one as whole. */ -+ while (!skb_queue_is_last(head, skb)) { -+ skb1 = skb_queue_next(head, skb); -+ -+ if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) -+ break; -+ -+ __skb_unlink(skb1, head); -+ mptcp_remove_shortcuts(mpcb, skb1); -+ __kfree_skb(skb1); -+ } -+ -+end: -+ if (skb) { -+ skb_set_owner_r(skb, meta_sk); -+ tp->mptcp->shortcut_ofoqueue = skb; -+ } -+ -+ return; -+} -+ -+/** -+ * @sk: the subflow that received this skb. -+ */ -+void mptcp_add_meta_ofo_queue(struct sock *meta_sk, struct sk_buff *skb, -+ struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ try_shortcut(tp->mptcp->shortcut_ofoqueue, skb, -+ &tcp_sk(meta_sk)->out_of_order_queue, tp); -+} -+ -+void mptcp_ofo_queue(struct sock *meta_sk) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct sk_buff *skb; -+ -+ while ((skb = skb_peek(&meta_tp->out_of_order_queue)) != NULL) { -+ u32 old_rcv_nxt = meta_tp->rcv_nxt; -+ if (after(TCP_SKB_CB(skb)->seq, meta_tp->rcv_nxt)) -+ break; -+ -+ if (!after(TCP_SKB_CB(skb)->end_seq, meta_tp->rcv_nxt)) { -+ __skb_unlink(skb, &meta_tp->out_of_order_queue); -+ mptcp_remove_shortcuts(meta_tp->mpcb, skb); -+ __kfree_skb(skb); -+ continue; -+ } -+ -+ __skb_unlink(skb, &meta_tp->out_of_order_queue); -+ mptcp_remove_shortcuts(meta_tp->mpcb, skb); -+ -+ __skb_queue_tail(&meta_sk->sk_receive_queue, skb); -+ meta_tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; -+ mptcp_check_rcvseq_wrap(meta_tp, old_rcv_nxt); -+ -+ if (tcp_hdr(skb)->fin) -+ mptcp_fin(meta_sk); -+ } -+} -+ -+void mptcp_purge_ofo_queue(struct tcp_sock *meta_tp) -+{ -+ struct sk_buff_head *head = &meta_tp->out_of_order_queue; -+ struct sk_buff *skb, *tmp; -+ -+ skb_queue_walk_safe(head, skb, tmp) { -+ __skb_unlink(skb, head); -+ mptcp_remove_shortcuts(meta_tp->mpcb, skb); -+ kfree_skb(skb); -+ } -+} -diff --git a/net/mptcp/mptcp_olia.c b/net/mptcp/mptcp_olia.c -new file mode 100644 -index 0000000..43d821e ---- /dev/null -+++ b/net/mptcp/mptcp_olia.c -@@ -0,0 +1,314 @@ -+/* -+ * MPTCP implementation - OPPORTUNISTIC LINKED INCREASES CONGESTION CONTROL: -+ * -+ * Algorithm design: -+ * Ramin Khalili -+ * Nicolas Gast -+ * Jean-Yves Le Boudec -+ * -+ * Implementation: -+ * Ramin Khalili -+ * -+ * Ported to the official MPTCP-kernel: -+ * Christoph Paasch -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+ -+#include -+#include -+ -+#include -+ -+static int scale = 10; -+ -+struct mptcp_olia { -+ u32 mptcp_loss1; -+ u32 mptcp_loss2; -+ u32 mptcp_loss3; -+ int epsilon_num; -+ u32 epsilon_den; -+ int mptcp_snd_cwnd_cnt; -+}; -+ -+static inline int mptcp_olia_sk_can_send(const struct sock *sk) -+{ -+ return mptcp_sk_can_send(sk) && tcp_sk(sk)->srtt; -+} -+ -+static inline u64 mptcp_olia_scale(u64 val, int scale) -+{ -+ return (u64) val << scale; -+} -+ -+/* take care of artificially inflate (see RFC5681) -+ * of cwnd during fast-retransmit phase -+ */ -+static u32 mptcp_get_crt_cwnd(struct sock *sk) -+{ -+ struct inet_connection_sock *icsk = inet_csk(sk); -+ -+ if (icsk->icsk_ca_state == TCP_CA_Recovery) -+ return tcp_sk(sk)->snd_ssthresh; -+ else -+ return tcp_sk(sk)->snd_cwnd; -+} -+ -+/* return the dominator of the first term of the increasing term */ -+static u64 mptcp_get_rate(struct mptcp_cb *mpcb , u32 path_rtt) -+{ -+ struct sock *sk; -+ u64 rate = 1; /* We have to avoid a zero-rate because it is used as a divisor */ -+ -+ mptcp_for_each_sk(mpcb, sk) { -+ struct tcp_sock *tp = tcp_sk(sk); -+ u64 scaled_num; -+ u32 tmp_cwnd; -+ -+ if (!mptcp_olia_sk_can_send(sk)) -+ continue; -+ -+ tmp_cwnd = mptcp_get_crt_cwnd(sk); -+ scaled_num = mptcp_olia_scale(tmp_cwnd, scale) * path_rtt; -+ rate += div_u64(scaled_num , tp->srtt); -+ } -+ rate *= rate; -+ return rate; -+} -+ -+/* find the maximum cwnd, used to find set M */ -+static u32 mptcp_get_max_cwnd(struct mptcp_cb *mpcb) -+{ -+ struct sock *sk; -+ u32 best_cwnd = 0; -+ -+ mptcp_for_each_sk(mpcb, sk) { -+ u32 tmp_cwnd; -+ -+ if (!mptcp_olia_sk_can_send(sk)) -+ continue; -+ -+ tmp_cwnd = mptcp_get_crt_cwnd(sk); -+ if (tmp_cwnd > best_cwnd) -+ best_cwnd = tmp_cwnd; -+ } -+ return best_cwnd; -+} -+ -+static void mptcp_get_epsilon(struct mptcp_cb *mpcb) -+{ -+ struct mptcp_olia *ca; -+ struct tcp_sock *tp; -+ struct sock *sk; -+ u64 tmp_int, tmp_rtt, best_int = 0, best_rtt = 1; -+ u32 max_cwnd = 1, best_cwnd = 1, tmp_cwnd; -+ u8 M = 0, B_not_M = 0; -+ -+ /* TODO - integrate this in the following loop - we just want to iterate once */ -+ -+ max_cwnd = mptcp_get_max_cwnd(mpcb); -+ -+ /* find the best path */ -+ mptcp_for_each_sk(mpcb, sk) { -+ tp = tcp_sk(sk); -+ ca = inet_csk_ca(sk); -+ -+ if (!mptcp_olia_sk_can_send(sk)) -+ continue; -+ -+ tmp_rtt = tp->srtt * tp->srtt; -+ /* TODO - check here and rename variables */ -+ tmp_int = max(ca->mptcp_loss3 - ca->mptcp_loss2, -+ ca->mptcp_loss2 - ca->mptcp_loss1); -+ -+ tmp_cwnd = mptcp_get_crt_cwnd(sk); -+ if (tmp_int * best_rtt >= best_int * tmp_rtt) { -+ best_rtt = tmp_rtt; -+ best_int = tmp_int; -+ best_cwnd = tmp_cwnd; -+ } -+ } -+ -+ /* TODO - integrate this here in mptcp_get_max_cwnd and in the previous loop */ -+ /* find the size of M and B_not_M */ -+ mptcp_for_each_sk(mpcb, sk) { -+ tp = tcp_sk(sk); -+ ca = inet_csk_ca(sk); -+ -+ if (!mptcp_olia_sk_can_send(sk)) -+ continue; -+ -+ tmp_cwnd = mptcp_get_crt_cwnd(sk); -+ if (tmp_cwnd == max_cwnd) { -+ M++; -+ } else { -+ tmp_rtt = tp->srtt * tp->srtt; -+ tmp_int = max(ca->mptcp_loss3 - ca->mptcp_loss2, -+ ca->mptcp_loss2 - ca->mptcp_loss1); -+ -+ if (tmp_int * best_rtt == best_int * tmp_rtt) -+ B_not_M++; -+ } -+ } -+ -+ /* check if the path is in M or B_not_M and set the value of epsilon accordingly */ -+ mptcp_for_each_sk(mpcb, sk) { -+ tp = tcp_sk(sk); -+ ca = inet_csk_ca(sk); -+ -+ if (!mptcp_olia_sk_can_send(sk)) -+ continue; -+ -+ if (B_not_M == 0) { -+ ca->epsilon_num = 0; -+ ca->epsilon_den = 1; -+ } else { -+ tmp_rtt = tp->srtt * tp->srtt; -+ tmp_int = max(ca->mptcp_loss3 - ca->mptcp_loss2, -+ ca->mptcp_loss2 - ca->mptcp_loss1); -+ tmp_cwnd = mptcp_get_crt_cwnd(sk); -+ -+ if (tmp_cwnd < max_cwnd && -+ tmp_int * best_rtt == best_int * tmp_rtt){ -+ ca->epsilon_num = 1; -+ ca->epsilon_den = mpcb->cnt_established * B_not_M; -+ } else if (tmp_cwnd == max_cwnd) { -+ ca->epsilon_num = -1; -+ ca->epsilon_den = mpcb->cnt_established * M; -+ } else { -+ ca->epsilon_num = 0; -+ ca->epsilon_den = 1; -+ } -+ } -+ } -+ -+} -+ -+/* setting the initial values */ -+static void mptcp_olia_init(struct sock *sk) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct mptcp_olia *ca = inet_csk_ca(sk); -+ -+ if (tp->mpc) { -+ ca->mptcp_loss1 = tp->snd_una; -+ ca->mptcp_loss2 = tp->snd_una; -+ ca->mptcp_loss3 = tp->snd_una; -+ ca->mptcp_snd_cwnd_cnt = 0; -+ ca->epsilon_num = 0; -+ ca->epsilon_den = 1; -+ } -+} -+ -+/* updating inter-loss distance and ssthresh */ -+static void mptcp_olia_set_state(struct sock *sk, u8 new_state) -+{ -+ if (!tcp_sk(sk)->mpc) -+ return; -+ -+ if (new_state == TCP_CA_Loss || -+ new_state == TCP_CA_Recovery || new_state == TCP_CA_CWR) { -+ struct mptcp_olia *ca = inet_csk_ca(sk); -+ -+ if (ca->mptcp_loss3 != ca->mptcp_loss2 && -+ !inet_csk(sk)->icsk_retransmits) { -+ ca->mptcp_loss1 = ca->mptcp_loss2; -+ ca->mptcp_loss2 = ca->mptcp_loss3; -+ } -+ } -+ -+} -+ -+/* main algorithm */ -+static void mptcp_olia_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct mptcp_olia *ca = inet_csk_ca(sk); -+ struct mptcp_cb *mpcb = tp->mpcb; -+ -+ u64 inc_num, inc_den, rate, cwnd_scaled; -+ -+ if (!tp->mpc) { -+ tcp_reno_cong_avoid(sk, ack, acked, in_flight); -+ return; -+ } -+ -+ ca->mptcp_loss3 = tp->snd_una; -+ -+ if (!tcp_is_cwnd_limited(sk, in_flight)) -+ return; -+ -+ /* slow start if it is in the safe area */ -+ if (tp->snd_cwnd <= tp->snd_ssthresh) { -+ tcp_slow_start(tp, acked); -+ return; -+ } -+ -+ mptcp_get_epsilon(mpcb); -+ rate = mptcp_get_rate(mpcb, tp->srtt); -+ cwnd_scaled = mptcp_olia_scale(tp->snd_cwnd, scale); -+ inc_den = ca->epsilon_den * tp->snd_cwnd * rate ? : 1; -+ -+ /* calculate the increasing term, scaling is used to reduce the rounding effect */ -+ if (ca->epsilon_num == -1) { -+ if (ca->epsilon_den * cwnd_scaled * cwnd_scaled < rate) { -+ inc_num = rate - ca->epsilon_den * -+ cwnd_scaled * cwnd_scaled; -+ ca->mptcp_snd_cwnd_cnt -= div64_u64( -+ mptcp_olia_scale(inc_num , scale) , inc_den); -+ } else { -+ inc_num = ca->epsilon_den * -+ cwnd_scaled * cwnd_scaled - rate; -+ ca->mptcp_snd_cwnd_cnt += div64_u64( -+ mptcp_olia_scale(inc_num , scale) , inc_den); -+ } -+ } else { -+ inc_num = ca->epsilon_num * rate + -+ ca->epsilon_den * cwnd_scaled * cwnd_scaled; -+ ca->mptcp_snd_cwnd_cnt += div64_u64( -+ mptcp_olia_scale(inc_num , scale) , inc_den); -+ } -+ -+ -+ if (ca->mptcp_snd_cwnd_cnt >= (1 << scale) - 1) { -+ if (tp->snd_cwnd < tp->snd_cwnd_clamp) -+ tp->snd_cwnd++; -+ ca->mptcp_snd_cwnd_cnt = 0; -+ } else if (ca->mptcp_snd_cwnd_cnt <= 0 - (1 << scale) + 1) { -+ tp->snd_cwnd = max((int) 1 , (int) tp->snd_cwnd - 1); -+ ca->mptcp_snd_cwnd_cnt = 0; -+ } -+} -+ -+static struct tcp_congestion_ops mptcp_olia = { -+ .init = mptcp_olia_init, -+ .ssthresh = tcp_reno_ssthresh, -+ .cong_avoid = mptcp_olia_cong_avoid, -+ .set_state = mptcp_olia_set_state, -+ .min_cwnd = tcp_reno_min_cwnd, -+ .owner = THIS_MODULE, -+ .name = "olia", -+}; -+ -+static int __init mptcp_olia_register(void) -+{ -+ BUILD_BUG_ON(sizeof(struct mptcp_olia) > ICSK_CA_PRIV_SIZE); -+ return tcp_register_congestion_control(&mptcp_olia); -+} -+ -+static void __exit mptcp_olia_unregister(void) -+{ -+ tcp_unregister_congestion_control(&mptcp_olia); -+} -+ -+module_init(mptcp_olia_register); -+module_exit(mptcp_olia_unregister); -+ -+MODULE_AUTHOR("Ramin Khalili, Nicolas Gast, Jean-Yves Le Boudec"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("MPTCP COUPLED CONGESTION CONTROL"); -+MODULE_VERSION("0.1"); -diff --git a/net/mptcp/mptcp_output.c b/net/mptcp/mptcp_output.c -new file mode 100644 -index 0000000..807b79e ---- /dev/null -+++ b/net/mptcp/mptcp_output.c -@@ -0,0 +1,2255 @@ -+/* -+ * MPTCP implementation - Sending side -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer & Author: -+ * Christoph Paasch -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+static inline int mptcp_pi_to_flag(int pi) -+{ -+ return 1 << (pi - 1); -+} -+ -+static inline int mptcp_sub_len_remove_addr(u16 bitfield) -+{ -+ unsigned int c; -+ for (c = 0; bitfield; c++) -+ bitfield &= bitfield - 1; -+ return MPTCP_SUB_LEN_REMOVE_ADDR + c - 1; -+} -+ -+int mptcp_sub_len_remove_addr_align(u16 bitfield) -+{ -+ return ALIGN(mptcp_sub_len_remove_addr(bitfield), 4); -+} -+EXPORT_SYMBOL(mptcp_sub_len_remove_addr_align); -+ -+/* If the sub-socket sk available to send the skb? */ -+static int mptcp_is_available(struct sock *sk, struct sk_buff *skb, -+ unsigned int *mss) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ unsigned int mss_now; -+ -+ /* Set of states for which we are allowed to send data */ -+ if (!mptcp_sk_can_send(sk)) -+ return 0; -+ -+ /* We do not send data on this subflow unless it is -+ * fully established, i.e. the 4th ack has been received. -+ */ -+ if (tp->mptcp->pre_established) -+ return 0; -+ -+ if (tp->pf || -+ (tp->mpcb->noneligible & mptcp_pi_to_flag(tp->mptcp->path_index))) -+ return 0; -+ -+ if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss) { -+ /* If SACK is disabled, and we got a loss, TCP does not exit -+ * the loss-state until something above high_seq has been acked. -+ * (see tcp_try_undo_recovery) -+ * -+ * high_seq is the snd_nxt at the moment of the RTO. As soon -+ * as we have an RTO, we won't push data on the subflow. -+ * Thus, snd_una can never go beyond high_seq. -+ */ -+ if (!tcp_is_reno(tp)) -+ return 0; -+ else if (tp->snd_una != tp->high_seq) -+ return 0; -+ } -+ -+ if (!tp->mptcp->fully_established) { -+ /* Make sure that we send in-order data */ -+ if (skb && tp->mptcp->second_packet && -+ tp->mptcp->last_end_data_seq != TCP_SKB_CB(skb)->seq) -+ return 0; -+ } -+ -+ if (!tcp_cwnd_test(tp, skb)) -+ return 0; -+ -+ mss_now = tcp_current_mss(sk); -+ /* Don't send on this subflow if we bypass the allowed send-window at -+ * the per-subflow level. Similar to tcp_snd_wnd_test, but manually -+ * calculated end_seq (because here at this point end_seq is still at -+ * the meta-level). -+ */ -+ if (skb && after(tp->write_seq + min(skb->len, mss_now), tcp_wnd_end(tp))) -+ return 0; -+ -+ if (mss) -+ *mss = mss_now; -+ -+ return 1; -+} -+ -+/* Are we not allowed to reinject this skb on tp? */ -+static int mptcp_dont_reinject_skb(struct tcp_sock *tp, struct sk_buff *skb) -+{ -+ /* If the skb has already been enqueued in this sk, try to find -+ * another one. -+ */ -+ return skb && -+ /* Has the skb already been enqueued into this subsocket? */ -+ mptcp_pi_to_flag(tp->mptcp->path_index) & TCP_SKB_CB(skb)->path_mask; -+} -+ -+/* This is the scheduler. This function decides on which flow to send -+ * a given MSS. If all subflows are found to be busy, NULL is returned -+ * The flow is selected based on the shortest RTT. -+ * If all paths have full cong windows, we simply return NULL. -+ * -+ * Additionally, this function is aware of the backup-subflows. -+ */ -+static struct sock *get_available_subflow(struct sock *meta_sk, -+ struct sk_buff *skb, -+ unsigned int *mss_now) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct sock *sk, *bestsk = NULL, *lowpriosk = NULL, *backupsk = NULL; -+ unsigned int mss = 0, mss_lowprio = 0, mss_backup = 0; -+ u32 min_time_to_peer = 0xffffffff, lowprio_min_time_to_peer = 0xffffffff; -+ int cnt_backups = 0; -+ -+ /* if there is only one subflow, bypass the scheduling function */ -+ if (mpcb->cnt_subflows == 1) { -+ bestsk = (struct sock *)mpcb->connection_list; -+ if (!mptcp_is_available(bestsk, skb, mss_now)) -+ bestsk = NULL; -+ return bestsk; -+ } -+ -+ /* Answer data_fin on same subflow!!! */ -+ if (meta_sk->sk_shutdown & RCV_SHUTDOWN && -+ skb && mptcp_is_data_fin(skb)) { -+ mptcp_for_each_sk(mpcb, sk) { -+ if (tcp_sk(sk)->mptcp->path_index == mpcb->dfin_path_index && -+ mptcp_is_available(sk, skb, mss_now)) -+ return sk; -+ } -+ } -+ -+ /* First, find the best subflow */ -+ mptcp_for_each_sk(mpcb, sk) { -+ struct tcp_sock *tp = tcp_sk(sk); -+ int this_mss; -+ -+ if (tp->mptcp->rcv_low_prio || tp->mptcp->low_prio) -+ cnt_backups++; -+ -+ if ((tp->mptcp->rcv_low_prio || tp->mptcp->low_prio) && -+ tp->srtt < lowprio_min_time_to_peer) { -+ -+ if (!mptcp_is_available(sk, skb, &this_mss)) -+ continue; -+ -+ if (mptcp_dont_reinject_skb(tp, skb)) { -+ mss_backup = this_mss; -+ backupsk = sk; -+ continue; -+ } -+ -+ lowprio_min_time_to_peer = tp->srtt; -+ lowpriosk = sk; -+ mss_lowprio = this_mss; -+ } else if (!(tp->mptcp->rcv_low_prio || tp->mptcp->low_prio) && -+ tp->srtt < min_time_to_peer) { -+ if (!mptcp_is_available(sk, skb, &this_mss)) -+ continue; -+ -+ if (mptcp_dont_reinject_skb(tp, skb)) { -+ mss_backup = this_mss; -+ backupsk = sk; -+ continue; -+ } -+ -+ min_time_to_peer = tp->srtt; -+ bestsk = sk; -+ mss = this_mss; -+ } -+ } -+ -+ if (mpcb->cnt_established == cnt_backups && lowpriosk) { -+ mss = mss_lowprio; -+ sk = lowpriosk; -+ } else if (bestsk) { -+ sk = bestsk; -+ } else if (backupsk){ -+ /* It has been sent on all subflows once - let's give it a -+ * chance again by restarting its pathmask. -+ */ -+ if (skb) -+ TCP_SKB_CB(skb)->path_mask = 0; -+ mss = mss_backup; -+ sk = backupsk; -+ } -+ -+ if (mss_now) -+ *mss_now = mss; -+ -+ return sk; -+} -+ -+static struct mp_dss *mptcp_skb_find_dss(const struct sk_buff *skb) -+{ -+ if (!mptcp_is_data_seq(skb)) -+ return NULL; -+ -+ return (struct mp_dss *)(skb->data - (MPTCP_SUB_LEN_DSS_ALIGN + -+ MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN)); -+} -+ -+/* get the data-seq and end-data-seq and store them again in the -+ * tcp_skb_cb -+ */ -+static int mptcp_reconstruct_mapping(struct sk_buff *skb, struct sk_buff *orig_skb) -+{ -+ struct mp_dss *mpdss = mptcp_skb_find_dss(orig_skb); -+ u32 *p32; -+ u16 *p16; -+ -+ if (!mpdss || !mpdss->M) -+ return 1; -+ -+ /* Move the pointer to the data-seq */ -+ p32 = (u32 *)mpdss; -+ p32++; -+ if (mpdss->A) { -+ p32++; -+ if (mpdss->a) -+ p32++; -+ } -+ -+ TCP_SKB_CB(skb)->seq = ntohl(*p32); -+ -+ /* Get the data_len to calculate the end_data_seq */ -+ p32++; -+ p32++; -+ p16 = (u16 *)p32; -+ TCP_SKB_CB(skb)->end_seq = ntohs(*p16) + TCP_SKB_CB(skb)->seq; -+ -+ return 0; -+} -+ -+/* Similar to __pskb_copy and sk_stream_alloc_skb. */ -+static struct sk_buff *mptcp_pskb_copy(struct sk_buff *skb) -+{ -+ struct sk_buff *n; -+ /* The TCP header must be at least 32-bit aligned. */ -+ int size = ALIGN(skb_headlen(skb), 4); -+ -+ n = alloc_skb_fclone(size + MAX_TCP_HEADER, GFP_ATOMIC); -+ if (!n) -+ return NULL; -+ -+ /* Set the data pointer */ -+ skb_reserve(n, MAX_TCP_HEADER); -+ /* Set the tail pointer and length */ -+ skb_put(n, skb_headlen(skb)); -+ /* Copy the bytes */ -+ skb_copy_from_linear_data(skb, n->data, n->len); -+ -+ n->truesize += skb->data_len; -+ n->data_len = skb->data_len; -+ n->len = skb->len; -+ -+ if (skb_shinfo(skb)->nr_frags) { -+ int i; -+ -+ if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) { -+ if (skb_copy_ubufs(skb, GFP_ATOMIC)) { -+ kfree_skb(n); -+ n = NULL; -+ goto out; -+ } -+ } -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i]; -+ skb_frag_ref(skb, i); -+ } -+ skb_shinfo(n)->nr_frags = i; -+ } -+ -+ if (skb_has_frag_list(skb)) { -+ skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list; -+ skb_clone_fraglist(n); -+ } -+ -+ copy_skb_header(n, skb); -+out: -+ return n; -+} -+ -+/* Reinject data from one TCP subflow to the meta_sk. If sk == NULL, we are -+ * coming from the meta-retransmit-timer -+ */ -+static void __mptcp_reinject_data(struct sk_buff *orig_skb, struct sock *meta_sk, -+ struct sock *sk, int clone_it) -+{ -+ struct sk_buff *skb, *skb1; -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ u32 seq, end_seq; -+ -+ if (clone_it) { -+ /* pskb_copy is necessary here, because the TCP/IP-headers -+ * will be changed when it's going to be reinjected on another -+ * subflow. -+ */ -+ skb = mptcp_pskb_copy(orig_skb); -+ } else { -+ __skb_unlink(orig_skb, &sk->sk_write_queue); -+ sock_set_flag(sk, SOCK_QUEUE_SHRUNK); -+ sk->sk_wmem_queued -= orig_skb->truesize; -+ sk_mem_uncharge(sk, orig_skb->truesize); -+ skb = orig_skb; -+ } -+ if (unlikely(!skb)) -+ return; -+ -+ if (sk && mptcp_reconstruct_mapping(skb, orig_skb)) { -+ __kfree_skb(skb); -+ return; -+ } -+ -+ skb->sk = meta_sk; -+ -+ /* If it reached already the destination, we don't have to reinject it */ -+ if (!after(TCP_SKB_CB(skb)->end_seq, meta_tp->snd_una)) { -+ __kfree_skb(skb); -+ return; -+ } -+ -+ /* Only reinject segments that are fully covered by the mapping */ -+ if (skb->len + (mptcp_is_data_fin(skb) ? 1 : 0) != -+ TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq) { -+ u32 seq = TCP_SKB_CB(skb)->seq; -+ u32 end_seq = TCP_SKB_CB(skb)->end_seq; -+ -+ __kfree_skb(skb); -+ -+ /* Ok, now we have to look for the full mapping in the meta -+ * send-queue :S -+ */ -+ tcp_for_write_queue(skb, meta_sk) { -+ /* Not yet at the mapping? */ -+ if (before(TCP_SKB_CB(skb)->seq, seq)) -+ continue; -+ /* We have passed by the mapping */ -+ if (after(TCP_SKB_CB(skb)->end_seq, end_seq)) -+ return; -+ -+ __mptcp_reinject_data(skb, meta_sk, NULL, 1); -+ } -+ return; -+ } -+ -+ /* If it's empty, just add */ -+ if (skb_queue_empty(&mpcb->reinject_queue)) { -+ skb_queue_head(&mpcb->reinject_queue, skb); -+ return; -+ } -+ -+ /* Find place to insert skb - or even we can 'drop' it, as the -+ * data is already covered by other skb's in the reinject-queue. -+ * -+ * This is inspired by code from tcp_data_queue. -+ */ -+ -+ skb1 = skb_peek_tail(&mpcb->reinject_queue); -+ seq = TCP_SKB_CB(skb)->seq; -+ while (1) { -+ if (!after(TCP_SKB_CB(skb1)->seq, seq)) -+ break; -+ if (skb_queue_is_first(&mpcb->reinject_queue, skb1)) { -+ skb1 = NULL; -+ break; -+ } -+ skb1 = skb_queue_prev(&mpcb->reinject_queue, skb1); -+ } -+ -+ /* Do skb overlap to previous one? */ -+ end_seq = TCP_SKB_CB(skb)->end_seq; -+ if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { -+ if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { -+ /* All the bits are present. Don't reinject */ -+ __kfree_skb(skb); -+ return; -+ } -+ if (seq == TCP_SKB_CB(skb1)->seq) { -+ if (skb_queue_is_first(&mpcb->reinject_queue, skb1)) -+ skb1 = NULL; -+ else -+ skb1 = skb_queue_prev(&mpcb->reinject_queue, skb1); -+ } -+ } -+ if (!skb1) -+ __skb_queue_head(&mpcb->reinject_queue, skb); -+ else -+ __skb_queue_after(&mpcb->reinject_queue, skb1, skb); -+ -+ /* And clean segments covered by new one as whole. */ -+ while (!skb_queue_is_last(&mpcb->reinject_queue, skb)) { -+ skb1 = skb_queue_next(&mpcb->reinject_queue, skb); -+ -+ if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) -+ break; -+ -+ __skb_unlink(skb1, &mpcb->reinject_queue); -+ __kfree_skb(skb1); -+ } -+ return; -+} -+ -+/* Inserts data into the reinject queue */ -+void mptcp_reinject_data(struct sock *sk, int clone_it) -+{ -+ struct sk_buff *skb_it, *tmp; -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct sock *meta_sk = tp->meta_sk; -+ -+ /* It has already been closed - there is really no point in reinjecting */ -+ if (meta_sk->sk_state == TCP_CLOSE) -+ return; -+ -+ skb_queue_walk_safe(&sk->sk_write_queue, skb_it, tmp) { -+ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb_it); -+ /* Subflow syn's and fin's are not reinjected. -+ * -+ * As well as empty subflow-fins with a data-fin. -+ * They are reinjected below (without the subflow-fin-flag) -+ */ -+ if (tcb->tcp_flags & TCPHDR_SYN || -+ (tcb->tcp_flags & TCPHDR_FIN && !mptcp_is_data_fin(skb_it)) || -+ (tcb->tcp_flags & TCPHDR_FIN && mptcp_is_data_fin(skb_it) && !skb_it->len)) -+ continue; -+ -+ __mptcp_reinject_data(skb_it, meta_sk, sk, clone_it); -+ } -+ -+ skb_it = tcp_write_queue_tail(meta_sk); -+ /* If sk has sent the empty data-fin, we have to reinject it too. */ -+ if (skb_it && mptcp_is_data_fin(skb_it) && skb_it->len == 0 && -+ TCP_SKB_CB(skb_it)->path_mask & mptcp_pi_to_flag(tp->mptcp->path_index)) { -+ __mptcp_reinject_data(skb_it, meta_sk, NULL, 1); -+ } -+ -+ mptcp_push_pending_frames(meta_sk); -+ -+ tp->pf = 1; -+} -+EXPORT_SYMBOL(mptcp_reinject_data); -+ -+static void mptcp_combine_dfin(struct sk_buff *skb, struct sock *meta_sk, -+ struct sock *subsk) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ struct sock *sk_it; -+ int all_empty = 1, all_acked; -+ -+ /* In infinite mapping we always try to combine */ -+ if (mpcb->infinite_mapping_snd && tcp_close_state(subsk)) { -+ subsk->sk_shutdown |= SEND_SHUTDOWN; -+ TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN; -+ return; -+ } -+ -+ /* Don't combine, if they didn't combine - otherwise we end up in -+ * TIME_WAIT, even if our app is smart enough to avoid it -+ */ -+ if (meta_sk->sk_shutdown & RCV_SHUTDOWN) { -+ if (!mpcb->dfin_combined) -+ return; -+ } -+ -+ /* If no other subflow has data to send, we can combine */ -+ mptcp_for_each_sk(mpcb, sk_it) { -+ if (!mptcp_sk_can_send(sk_it)) -+ continue; -+ -+ if (!tcp_write_queue_empty(sk_it)) -+ all_empty = 0; -+ } -+ -+ /* If all data has been DATA_ACKed, we can combine. -+ * -1, because the data_fin consumed one byte -+ */ -+ all_acked = (meta_tp->snd_una == (meta_tp->write_seq - 1)); -+ -+ if ((all_empty || all_acked) && tcp_close_state(subsk)) { -+ subsk->sk_shutdown |= SEND_SHUTDOWN; -+ TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN; -+ } -+} -+ -+static struct sk_buff *mptcp_skb_entail(struct sock *sk, struct sk_buff *skb, -+ int reinject) -+{ -+ __be32 *ptr; -+ __u16 data_len; -+ struct mp_dss *mdss; -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ struct mptcp_cb *mpcb = tp->mpcb; -+ struct tcp_skb_cb *tcb; -+ struct sk_buff *subskb = NULL; -+ -+ if (!reinject) -+ TCP_SKB_CB(skb)->mptcp_flags |= (mpcb->snd_hiseq_index ? -+ MPTCPHDR_SEQ64_INDEX : 0); -+ -+ subskb = mptcp_pskb_copy(skb); -+ if (!subskb) -+ return NULL; -+ -+ TCP_SKB_CB(skb)->path_mask |= mptcp_pi_to_flag(tp->mptcp->path_index); -+ -+ if (!(sk->sk_route_caps & NETIF_F_ALL_CSUM) && -+ skb->ip_summed == CHECKSUM_PARTIAL) { -+ subskb->csum = skb->csum = skb_checksum(skb, 0, skb->len, 0); -+ subskb->ip_summed = skb->ip_summed = CHECKSUM_NONE; -+ } -+ -+ /* The subskb is going in the subflow send-queue. Its path-mask -+ * is not needed anymore and MUST be set to 0, as the path-mask -+ * is a union with inet_skb_param. -+ */ -+ tcb = TCP_SKB_CB(subskb); -+ tcb->path_mask = 0; -+ -+ if (mptcp_is_data_fin(subskb)) -+ mptcp_combine_dfin(subskb, meta_sk, sk); -+ -+ if (tp->mpcb->infinite_mapping_snd) -+ goto no_data_seq; -+ -+ if (tp->mpcb->send_infinite_mapping && -+ !before(tcb->seq, mptcp_meta_tp(tp)->snd_nxt)) { -+ tp->mptcp->fully_established = 1; -+ tp->mpcb->infinite_mapping_snd = 1; -+ tp->mptcp->infinite_cutoff_seq = tp->write_seq; -+ tcb->mptcp_flags |= MPTCPHDR_INF; -+ data_len = 0; -+ } else { -+ data_len = tcb->end_seq - tcb->seq; -+ } -+ -+ /**** Write MPTCP DSS-option to the packet. ****/ -+ ptr = (__be32 *)(subskb->data - (MPTCP_SUB_LEN_DSS_ALIGN + -+ MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN)); -+ -+ /* Then we start writing it from the start */ -+ mdss = (struct mp_dss *)ptr; -+ -+ mdss->kind = TCPOPT_MPTCP; -+ mdss->sub = MPTCP_SUB_DSS; -+ mdss->rsv1 = 0; -+ mdss->rsv2 = 0; -+ mdss->F = (mptcp_is_data_fin(subskb) ? 1 : 0); -+ mdss->m = 0; -+ mdss->M = 1; -+ mdss->a = 0; -+ mdss->A = 1; -+ mdss->len = mptcp_sub_len_dss(mdss, tp->mpcb->dss_csum); -+ -+ ptr++; -+ ptr++; /* data_ack will be set in mptcp_options_write */ -+ *ptr++ = htonl(tcb->seq); /* data_seq */ -+ -+ /* If it's a non-data DATA_FIN, we set subseq to 0 (draft v7) */ -+ if (mptcp_is_data_fin(subskb) && subskb->len == 0) -+ *ptr++ = 0; /* subseq */ -+ else -+ *ptr++ = htonl(tp->write_seq - tp->mptcp->snt_isn); /* subseq */ -+ -+ if (tp->mpcb->dss_csum && data_len) { -+ __be16 *p16 = (__be16 *)ptr; -+ __be32 hdseq = mptcp_get_highorder_sndbits(subskb, tp->mpcb); -+ __wsum csum; -+ *ptr = htonl(((data_len) << 16) | -+ (TCPOPT_EOL << 8) | -+ (TCPOPT_EOL)); -+ -+ csum = csum_partial(ptr - 2, 12, subskb->csum); -+ p16++; -+ *p16++ = csum_fold(csum_partial(&hdseq, sizeof(hdseq), csum)); -+ } else { -+ *ptr++ = htonl(((data_len) << 16) | -+ (TCPOPT_NOP << 8) | -+ (TCPOPT_NOP)); -+ } -+ -+no_data_seq: -+ tcb->seq = tp->write_seq; -+ tcb->sacked = 0; /* reset the sacked field: from the point of view -+ * of this subflow, we are sending a brand new -+ * segment */ -+ /* Take into account seg len */ -+ tp->write_seq += subskb->len + ((tcb->tcp_flags & TCPHDR_FIN) ? 1 : 0); -+ tcb->end_seq = tp->write_seq; -+ -+ /* If it's a non-payload DATA_FIN (also no subflow-fin), the -+ * segment is not part of the subflow but on a meta-only-level -+ */ -+ if (!mptcp_is_data_fin(subskb) || tcb->end_seq != tcb->seq) { -+ tcp_add_write_queue_tail(sk, subskb); -+ sk->sk_wmem_queued += subskb->truesize; -+ sk_mem_charge(sk, subskb->truesize); -+ } -+ -+ return subskb; -+} -+ -+static void mptcp_sub_event_new_data_sent(struct sock *sk, -+ struct sk_buff *subskb, -+ struct sk_buff *skb) -+{ -+ /* If it's a non-payload DATA_FIN (also no subflow-fin), the -+ * segment is not part of the subflow but on a meta-only-level -+ * -+ * We free it, because it has been queued nowhere. -+ */ -+ if (!mptcp_is_data_fin(subskb) || -+ (TCP_SKB_CB(subskb)->end_seq != TCP_SKB_CB(subskb)->seq)) { -+ tcp_event_new_data_sent(sk, subskb); -+ tcp_sk(sk)->mptcp->second_packet = 1; -+ tcp_sk(sk)->mptcp->last_end_data_seq = TCP_SKB_CB(skb)->end_seq; -+ } else { -+ kfree_skb(subskb); -+ } -+} -+ -+/* Handle the packets and sockets after a tcp_transmit_skb failed */ -+static void mptcp_transmit_skb_failed(struct sock *sk, struct sk_buff *skb, -+ struct sk_buff *subskb) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct mptcp_cb *mpcb = tp->mpcb; -+ -+ /* No work to do if we are in infinite mapping mode -+ * There is only one subflow left and we cannot send this segment on -+ * another subflow. -+ */ -+ if (mpcb->infinite_mapping_snd) -+ return; -+ -+ TCP_SKB_CB(skb)->path_mask &= ~mptcp_pi_to_flag(tp->mptcp->path_index); -+ -+ if (TCP_SKB_CB(subskb)->tcp_flags & TCPHDR_FIN) { -+ /* If it is a subflow-fin we must leave it on the -+ * subflow-send-queue, so that the probe-timer -+ * can retransmit it. -+ */ -+ if (!tp->packets_out && !inet_csk(sk)->icsk_pending) -+ inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0, -+ inet_csk(sk)->icsk_rto, TCP_RTO_MAX); -+ } else if (mptcp_is_data_fin(subskb) && -+ TCP_SKB_CB(subskb)->end_seq == TCP_SKB_CB(subskb)->seq) { -+ /* An empty data-fin has not been enqueued on the subflow -+ * and thus we free it. -+ */ -+ -+ kfree_skb(subskb); -+ } else { -+ /* In all other cases we remove it from the sub-queue. -+ * Other subflows may send it, or the probe-timer will -+ * handle it. -+ */ -+ tcp_advance_send_head(sk, subskb); -+ -+ /* tcp_add_write_queue_tail initialized highest_sack. We have -+ * to reset it, if necessary. -+ */ -+ if (tp->highest_sack == subskb) -+ tp->highest_sack = NULL; -+ -+ tcp_unlink_write_queue(subskb, sk); -+ tp->write_seq -= subskb->len; -+ sk_wmem_free_skb(sk, subskb); -+ } -+} -+ -+/* Function to create two new TCP segments. Shrinks the given segment -+ * to the specified size and appends a new segment with the rest of the -+ * packet to the list. This won't be called frequently, I hope. -+ * Remember, these are still headerless SKBs at this point. -+ */ -+int mptcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, -+ unsigned int mss_now, int reinject) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct sk_buff *buff; -+ int nsize, old_factor; -+ int nlen; -+ u8 flags; -+ int dsslen = MPTCP_SUB_LEN_DSS_ALIGN + MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN; -+ char dss[MPTCP_SUB_LEN_DSS_ALIGN + MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN]; -+ -+ if (WARN_ON(len > skb->len)) -+ return -EINVAL; -+ -+ /* DSS-option must be recovered afterwards. */ -+ if (!is_meta_sk(sk)) -+ memcpy(dss, skb->data - dsslen, dsslen); -+ -+ nsize = skb_headlen(skb) - len; -+ if (nsize < 0) -+ nsize = 0; -+ -+ if (skb_cloned(skb)) { -+ if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) -+ return -ENOMEM; -+ /* Recover dss-option */ -+ if (!is_meta_sk(sk)) -+ memcpy(skb->data - dsslen, dss, dsslen); -+ } -+ -+ /* Get a new skb... force flag on. */ -+ buff = sk_stream_alloc_skb(sk, nsize, GFP_ATOMIC); -+ if (buff == NULL) -+ return -ENOMEM; /* We'll just try again later. */ -+ -+ /* See below - if reinject == 1, the buff will be added to the reinject- -+ * queue, which is currently not part of the memory-accounting. -+ */ -+ if (reinject != 1) { -+ sk->sk_wmem_queued += buff->truesize; -+ sk_mem_charge(sk, buff->truesize); -+ } -+ nlen = skb->len - len - nsize; -+ buff->truesize += nlen; -+ skb->truesize -= nlen; -+ -+ /* Correct the sequence numbers. */ -+ TCP_SKB_CB(buff)->seq = TCP_SKB_CB(skb)->seq + len; -+ TCP_SKB_CB(buff)->end_seq = TCP_SKB_CB(skb)->end_seq; -+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq; -+ -+ /* PSH and FIN should only be set in the second packet. */ -+ flags = TCP_SKB_CB(skb)->tcp_flags; -+ TCP_SKB_CB(skb)->tcp_flags = flags & ~(TCPHDR_FIN | TCPHDR_PSH); -+ TCP_SKB_CB(buff)->tcp_flags = flags; -+ TCP_SKB_CB(buff)->sacked = TCP_SKB_CB(skb)->sacked; -+ -+ flags = TCP_SKB_CB(skb)->mptcp_flags; -+ TCP_SKB_CB(skb)->mptcp_flags = flags & ~(MPTCPHDR_FIN); -+ TCP_SKB_CB(buff)->mptcp_flags = flags; -+ -+ if (!skb_shinfo(skb)->nr_frags && skb->ip_summed != CHECKSUM_PARTIAL) { -+ /* Copy and checksum data tail into the new buffer. */ -+ buff->csum = csum_partial_copy_nocheck(skb->data + len, -+ skb_put(buff, nsize), -+ nsize, 0); -+ -+ skb_trim(skb, len); -+ -+ skb->csum = csum_block_sub(skb->csum, buff->csum, len); -+ } else { -+ skb->ip_summed = CHECKSUM_PARTIAL; -+ skb_split(skb, buff, len); -+ } -+ -+ /* We lost the dss-option when creating buff - put it back! */ -+ if (!is_meta_sk(sk)) -+ memcpy(buff->data - dsslen, dss, dsslen); -+ -+ buff->ip_summed = skb->ip_summed; -+ -+ /* Looks stupid, but our code really uses when of -+ * skbs, which it never sent before. --ANK -+ */ -+ TCP_SKB_CB(buff)->when = TCP_SKB_CB(skb)->when; -+ buff->tstamp = skb->tstamp; -+ -+ old_factor = tcp_skb_pcount(skb); -+ -+ /* Fix up tso_factor for both original and new SKB. */ -+ tcp_set_skb_tso_segs(sk, skb, mss_now); -+ tcp_set_skb_tso_segs(sk, buff, mss_now); -+ -+ /* If this packet has been sent out already, we must -+ * adjust the various packet counters. -+ */ -+ if (!before(tp->snd_nxt, TCP_SKB_CB(buff)->end_seq) && reinject != 1) { -+ int diff = old_factor - tcp_skb_pcount(skb) - -+ tcp_skb_pcount(buff); -+ -+ if (diff) -+ tcp_adjust_pcount(sk, skb, diff); -+ } -+ -+ /* Link BUFF into the send queue. */ -+ skb_header_release(buff); -+ if (reinject == 1) -+ __skb_queue_after(&tcp_sk(sk)->mpcb->reinject_queue, skb, buff); -+ else -+ tcp_insert_write_queue_after(skb, buff, sk); -+ -+ return 0; -+} -+ -+int mptso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, -+ unsigned int mss_now, gfp_t gfp, int reinject) -+{ -+ struct sk_buff *buff; -+ int nlen = skb->len - len, old_factor; -+ u8 flags; -+ int dsslen = MPTCP_SUB_LEN_DSS_ALIGN + MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN; -+ -+ /* All of a TSO frame must be composed of paged data. */ -+ if (skb->len != skb->data_len) -+ return mptcp_fragment(sk, skb, len, mss_now, reinject); -+ -+ buff = sk_stream_alloc_skb(sk, 0, gfp); -+ if (unlikely(buff == NULL)) -+ return -ENOMEM; -+ -+ /* See below - if reinject == 1, the buff will be added to the reinject- -+ * queue, which is currently not part of the memory-accounting. -+ */ -+ if (reinject != 1) { -+ sk->sk_wmem_queued += buff->truesize; -+ sk_mem_charge(sk, buff->truesize); -+ } -+ buff->truesize += nlen; -+ skb->truesize -= nlen; -+ -+ /* Correct the sequence numbers. */ -+ TCP_SKB_CB(buff)->seq = TCP_SKB_CB(skb)->seq + len; -+ TCP_SKB_CB(buff)->end_seq = TCP_SKB_CB(skb)->end_seq; -+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq; -+ -+ /* PSH and FIN should only be set in the second packet. */ -+ flags = TCP_SKB_CB(skb)->tcp_flags; -+ TCP_SKB_CB(skb)->tcp_flags = flags & ~(TCPHDR_FIN | TCPHDR_PSH); -+ TCP_SKB_CB(buff)->tcp_flags = flags; -+ -+ flags = TCP_SKB_CB(skb)->mptcp_flags; -+ TCP_SKB_CB(skb)->mptcp_flags = flags & ~(MPTCPHDR_FIN); -+ TCP_SKB_CB(buff)->mptcp_flags = flags; -+ -+ /* This packet was never sent out yet, so no SACK bits. */ -+ TCP_SKB_CB(buff)->sacked = 0; -+ -+ buff->ip_summed = CHECKSUM_PARTIAL; -+ skb->ip_summed = CHECKSUM_PARTIAL; -+ skb_split(skb, buff, len); -+ -+ /* We lost the dss-option when creating buff - put it back! */ -+ if (!is_meta_sk(sk)) -+ memcpy(buff->data - dsslen, skb->data - dsslen, dsslen); -+ -+ old_factor = tcp_skb_pcount(skb); -+ -+ /* Fix up tso_factor for both original and new SKB. */ -+ tcp_set_skb_tso_segs(sk, skb, mss_now); -+ tcp_set_skb_tso_segs(sk, buff, mss_now); -+ -+ /* If this packet has been sent out already, we must -+ * adjust the various packet counters. -+ */ -+ if (!before(tcp_sk(sk)->snd_nxt, TCP_SKB_CB(buff)->end_seq) && reinject != 1) { -+ int diff = old_factor - tcp_skb_pcount(skb) - -+ tcp_skb_pcount(buff); -+ -+ if (diff) -+ tcp_adjust_pcount(sk, skb, diff); -+ } -+ -+ /* Link BUFF into the send queue. */ -+ skb_header_release(buff); -+ if (reinject == 1) -+ __skb_queue_after(&tcp_sk(sk)->mpcb->reinject_queue, skb, buff); -+ else -+ tcp_insert_write_queue_after(skb, buff, sk); -+ -+ return 0; -+} -+ -+/* Inspired by tcp_write_wakeup */ -+int mptcp_write_wakeup(struct sock *meta_sk) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct sk_buff *skb, *subskb; -+ -+ skb = tcp_send_head(meta_sk); -+ if (skb && -+ before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(meta_tp))) { -+ int err; -+ unsigned int mss; -+ unsigned int seg_size = tcp_wnd_end(meta_tp) - TCP_SKB_CB(skb)->seq; -+ struct sock *subsk = get_available_subflow(meta_sk, skb, &mss); -+ if (!subsk) -+ return -1; -+ -+ if (before(meta_tp->pushed_seq, TCP_SKB_CB(skb)->end_seq)) -+ meta_tp->pushed_seq = TCP_SKB_CB(skb)->end_seq; -+ -+ /* We are probing the opening of a window -+ * but the window size is != 0 -+ * must have been a result SWS avoidance ( sender ) -+ */ -+ if (seg_size < TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq || -+ skb->len > mss) { -+ seg_size = min(seg_size, mss); -+ TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_PSH; -+ if (mptcp_fragment(meta_sk, skb, seg_size, mss, 0)) -+ return -1; -+ } else if (!tcp_skb_pcount(skb)) { -+ tcp_set_skb_tso_segs(meta_sk, skb, mss); -+ } -+ -+ subskb = mptcp_skb_entail(subsk, skb, 0); -+ if (!subskb) -+ return -1; -+ -+ TCP_SKB_CB(subskb)->tcp_flags |= TCPHDR_PSH; -+ TCP_SKB_CB(skb)->when = tcp_time_stamp; -+ TCP_SKB_CB(subskb)->when = tcp_time_stamp; -+ err = tcp_transmit_skb(subsk, subskb, 1, GFP_ATOMIC); -+ if (unlikely(err)) { -+ mptcp_transmit_skb_failed(subsk, skb, subskb); -+ return err; -+ } -+ -+ mptcp_check_sndseq_wrap(meta_tp, TCP_SKB_CB(skb)->end_seq - -+ TCP_SKB_CB(skb)->seq); -+ tcp_event_new_data_sent(meta_sk, skb); -+ mptcp_sub_event_new_data_sent(subsk, subskb, skb); -+ -+ return 0; -+ } else { -+ struct sock *sk_it; -+ int ans = 0; -+ -+ if (between(meta_tp->snd_up, meta_tp->snd_una + 1, -+ meta_tp->snd_una + 0xFFFF)) { -+ mptcp_for_each_sk(meta_tp->mpcb, sk_it) { -+ if (mptcp_sk_can_send_ack(sk_it)) -+ tcp_xmit_probe_skb(sk_it, 1); -+ } -+ } -+ -+ /* At least one of the tcp_xmit_probe_skb's has to succeed */ -+ mptcp_for_each_sk(meta_tp->mpcb, sk_it) { -+ int ret; -+ -+ if (!mptcp_sk_can_send_ack(sk_it)) -+ continue; -+ -+ ret = tcp_xmit_probe_skb(sk_it, 0); -+ if (unlikely(ret > 0)) -+ ans = ret; -+ } -+ return ans; -+ } -+} -+ -+static void mptcp_find_and_set_pathmask(struct sock *meta_sk, struct sk_buff *skb) -+{ -+ struct sk_buff *skb_it; -+ -+ skb_it = tcp_write_queue_head(meta_sk); -+ -+ tcp_for_write_queue_from(skb_it, meta_sk) { -+ if (skb_it == tcp_send_head(meta_sk)) -+ break; -+ -+ if (TCP_SKB_CB(skb_it)->seq == TCP_SKB_CB(skb)->seq) { -+ TCP_SKB_CB(skb)->path_mask = TCP_SKB_CB(skb_it)->path_mask; -+ break; -+ } -+ } -+} -+ -+static struct sk_buff *mptcp_rcv_buf_optimization(struct sock *sk, int penal) -+{ -+ struct sock *meta_sk; -+ struct tcp_sock *tp = tcp_sk(sk), *tp_it; -+ struct sk_buff *skb_head; -+ -+ if (tp->mpcb->cnt_subflows == 1) -+ return NULL; -+ -+ meta_sk = mptcp_meta_sk(sk); -+ skb_head = tcp_write_queue_head(meta_sk); -+ -+ if (!skb_head || skb_head == tcp_send_head(meta_sk)) -+ return NULL; -+ -+ /* If penalization is optional (coming from mptcp_next_segment() and -+ * We are not send-buffer-limited we do not penalize. The retransmission -+ * is just an optimization to fix the idle-time due to the delay before -+ * we wake up the application. -+ */ -+ if (!penal && sk_stream_memory_free(meta_sk)) -+ goto retrans; -+ -+ /* Only penalize again after an RTT has elapsed */ -+ if (tcp_time_stamp - tp->mptcp->last_rbuf_opti < tp->srtt >> 3) -+ goto retrans; -+ -+ /* Half the cwnd of the slow flow */ -+ mptcp_for_each_tp(tp->mpcb, tp_it) { -+ if (tp_it != tp && -+ TCP_SKB_CB(skb_head)->path_mask & mptcp_pi_to_flag(tp_it->mptcp->path_index)) { -+ if (tp->srtt < tp_it->srtt && inet_csk((struct sock *)tp_it)->icsk_ca_state == TCP_CA_Open) { -+ tp_it->snd_cwnd = max(tp_it->snd_cwnd >> 1U, 1U); -+ if (tp_it->snd_ssthresh != TCP_INFINITE_SSTHRESH) -+ tp_it->snd_ssthresh = max(tp_it->snd_ssthresh >> 1U, 2U); -+ -+ tp->mptcp->last_rbuf_opti = tcp_time_stamp; -+ } -+ break; -+ } -+ } -+ -+retrans: -+ -+ /* Segment not yet injected into this path? Take it!!! */ -+ if (!(TCP_SKB_CB(skb_head)->path_mask & mptcp_pi_to_flag(tp->mptcp->path_index))) { -+ bool do_retrans = false; -+ mptcp_for_each_tp(tp->mpcb, tp_it) { -+ if (tp_it != tp && -+ TCP_SKB_CB(skb_head)->path_mask & mptcp_pi_to_flag(tp_it->mptcp->path_index)) { -+ if (tp_it->snd_cwnd <= 4) { -+ do_retrans = true; -+ break; -+ } -+ -+ if (4 * tp->srtt >= tp_it->srtt) { -+ do_retrans = false; -+ break; -+ } else { -+ do_retrans = true; -+ } -+ } -+ } -+ -+ if (do_retrans) -+ return skb_head; -+ } -+ return NULL; -+} -+ -+int mptcp_write_xmit(struct sock *meta_sk, unsigned int mss_now, int nonagle, -+ int push_one, gfp_t gfp) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk), *subtp; -+ struct sock *subsk; -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ struct sk_buff *skb; -+ unsigned int tso_segs, old_factor, sent_pkts; -+ int cwnd_quota; -+ int result; -+ int reinject = 0; -+ -+ sent_pkts = 0; -+ -+ /* Currently mtu-probing is not done in MPTCP */ -+ if (!push_one && 0) { -+ /* Do MTU probing. */ -+ result = tcp_mtu_probe(meta_sk); -+ if (!result) -+ return 0; -+ else if (result > 0) -+ sent_pkts = 1; -+ } -+ -+ while ((skb = mptcp_next_segment(meta_sk, &reinject))) { -+ unsigned int limit; -+ struct sk_buff *subskb = NULL; -+ u32 noneligible = mpcb->noneligible; -+ -+ if (reinject == 1) { -+ if (!after(TCP_SKB_CB(skb)->end_seq, meta_tp->snd_una)) { -+ /* Segment already reached the peer, take the next one */ -+ __skb_unlink(skb, &mpcb->reinject_queue); -+ __kfree_skb(skb); -+ continue; -+ } -+ -+ /* Reinjection and it is coming from a subflow? We need -+ * to find out the path-mask from the meta-write-queue -+ * to properly select a subflow. -+ */ -+ if (!TCP_SKB_CB(skb)->path_mask) -+ mptcp_find_and_set_pathmask(meta_sk, skb); -+ } -+ -+subflow: -+ subsk = get_available_subflow(meta_sk, skb, &mss_now); -+ if (!subsk) -+ break; -+ subtp = tcp_sk(subsk); -+ -+ /* Since all subsocks are locked before calling the scheduler, -+ * the tcp_send_head should not change. -+ */ -+ BUG_ON(!reinject && tcp_send_head(meta_sk) != skb); -+retry: -+ /* If the segment was cloned (e.g. a meta retransmission), -+ * the header must be expanded/copied so that there is no -+ * corruption of TSO information. -+ */ -+ if (skb_unclone(skb, GFP_ATOMIC)) -+ break; -+ -+ old_factor = tcp_skb_pcount(skb); -+ tcp_set_skb_tso_segs(meta_sk, skb, mss_now); -+ tso_segs = tcp_skb_pcount(skb); -+ -+ if (reinject == -1) { -+ /* The packet has already once been sent, so if we -+ * change the pcount here we have to adjust packets_out -+ * in the meta-sk -+ */ -+ int diff = old_factor - tso_segs; -+ -+ if (diff) -+ tcp_adjust_pcount(meta_sk, skb, diff); -+ } -+ -+ cwnd_quota = tcp_cwnd_test(subtp, skb); -+ if (!cwnd_quota) { -+ /* May happen due to two cases: -+ * -+ * - if at the first selection we circumvented -+ * the test due to a DATA_FIN (and got rejected at -+ * tcp_snd_wnd_test), but the reinjected segment is not -+ * a DATA_FIN. -+ * - if we take a DATA_FIN with data, but -+ * tcp_set_skb_tso_segs() increases the number of -+ * tso_segs to something > 1. Then, cwnd_test might -+ * reject it. -+ */ -+ mpcb->noneligible |= mptcp_pi_to_flag(subtp->mptcp->path_index); -+ continue; -+ } -+ -+ if (!reinject && unlikely(!tcp_snd_wnd_test(meta_tp, skb, mss_now))) { -+ skb = mptcp_rcv_buf_optimization(subsk, 1); -+ if (skb) { -+ reinject = -1; -+ goto retry; -+ } -+ break; -+ } -+ -+ if (tso_segs == 1) { -+ if (unlikely(!tcp_nagle_test(meta_tp, skb, mss_now, -+ (tcp_skb_is_last(meta_sk, skb) ? -+ nonagle : TCP_NAGLE_PUSH)))) -+ break; -+ } else { -+ /* Do not try to defer the transmission of a reinjected -+ * segment. Send it directly. -+ * If it is not possible to send the TSO segment on the -+ * best subflow right now try to look for another subflow. -+ * If there is no subflow available defer the segment to avoid -+ * the call to mptso_fragment. -+ */ -+ if (!push_one && !reinject && tcp_tso_should_defer(subsk, skb)) { -+ mpcb->noneligible |= mptcp_pi_to_flag(subtp->mptcp->path_index); -+ goto subflow; -+ } -+ } -+ -+ limit = mss_now; -+ if (tso_segs > 1 && !tcp_urg_mode(meta_tp)) -+ limit = tcp_mss_split_point(subsk, skb, mss_now, -+ min_t(unsigned int, -+ cwnd_quota, -+ subsk->sk_gso_max_segs), -+ nonagle); -+ -+ if (skb->len > limit && -+ unlikely(mptso_fragment(meta_sk, skb, limit, mss_now, gfp, reinject))) -+ break; -+ -+ subskb = mptcp_skb_entail(subsk, skb, reinject); -+ if (!subskb) -+ break; -+ -+ mpcb->noneligible = noneligible; -+ TCP_SKB_CB(skb)->when = tcp_time_stamp; -+ TCP_SKB_CB(subskb)->when = tcp_time_stamp; -+ if (unlikely(tcp_transmit_skb(subsk, subskb, 1, gfp))) { -+ mptcp_transmit_skb_failed(subsk, skb, subskb); -+ mpcb->noneligible |= mptcp_pi_to_flag(subtp->mptcp->path_index); -+ continue; -+ } -+ -+ if (!reinject) { -+ mptcp_check_sndseq_wrap(meta_tp, -+ TCP_SKB_CB(skb)->end_seq - -+ TCP_SKB_CB(skb)->seq); -+ tcp_event_new_data_sent(meta_sk, skb); -+ } -+ -+ tcp_minshall_update(meta_tp, mss_now, skb); -+ sent_pkts += tcp_skb_pcount(skb); -+ tcp_sk(subsk)->mptcp->sent_pkts += tcp_skb_pcount(skb); -+ -+ mptcp_sub_event_new_data_sent(subsk, subskb, skb); -+ -+ if (reinject > 0) { -+ __skb_unlink(skb, &mpcb->reinject_queue); -+ kfree_skb(skb); -+ } -+ -+ if (push_one) -+ break; -+ } -+ -+ mpcb->noneligible = 0; -+ -+ if (likely(sent_pkts)) { -+ mptcp_for_each_sk(mpcb, subsk) { -+ subtp = tcp_sk(subsk); -+ if (subtp->mptcp->sent_pkts) { -+ if (tcp_in_cwnd_reduction(subsk)) -+ subtp->prr_out += subtp->mptcp->sent_pkts; -+ tcp_cwnd_validate(subsk); -+ subtp->mptcp->sent_pkts = 0; -+ } -+ } -+ return 0; -+ } -+ -+ return !meta_tp->packets_out && tcp_send_head(meta_sk); -+} -+ -+void mptcp_write_space(struct sock *sk) -+{ -+ mptcp_push_pending_frames(mptcp_meta_sk(sk)); -+} -+ -+u32 __mptcp_select_window(struct sock *sk) -+{ -+ struct inet_connection_sock *icsk = inet_csk(sk); -+ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); -+ int mss, free_space, full_space, window; -+ -+ /* MSS for the peer's data. Previous versions used mss_clamp -+ * here. I don't know if the value based on our guesses -+ * of peer's MSS is better for the performance. It's more correct -+ * but may be worse for the performance because of rcv_mss -+ * fluctuations. --SAW 1998/11/1 -+ */ -+ mss = icsk->icsk_ack.rcv_mss; -+ free_space = tcp_space(sk); -+ full_space = min_t(int, meta_tp->window_clamp, -+ tcp_full_space(sk)); -+ -+ if (mss > full_space) -+ mss = full_space; -+ -+ if (free_space < (full_space >> 1)) { -+ icsk->icsk_ack.quick = 0; -+ -+ if (tcp_memory_pressure) -+ /* TODO this has to be adapted when we support different -+ * MSS's among the subflows. -+ */ -+ meta_tp->rcv_ssthresh = min(meta_tp->rcv_ssthresh, -+ 4U * meta_tp->advmss); -+ -+ if (free_space < mss) -+ return 0; -+ } -+ -+ if (free_space > meta_tp->rcv_ssthresh) -+ free_space = meta_tp->rcv_ssthresh; -+ -+ /* Don't do rounding if we are using window scaling, since the -+ * scaled window will not line up with the MSS boundary anyway. -+ */ -+ window = meta_tp->rcv_wnd; -+ if (tp->rx_opt.rcv_wscale) { -+ window = free_space; -+ -+ /* Advertise enough space so that it won't get scaled away. -+ * Import case: prevent zero window announcement if -+ * 1< mss. -+ */ -+ if (((window >> tp->rx_opt.rcv_wscale) << tp-> -+ rx_opt.rcv_wscale) != window) -+ window = (((window >> tp->rx_opt.rcv_wscale) + 1) -+ << tp->rx_opt.rcv_wscale); -+ } else { -+ /* Get the largest window that is a nice multiple of mss. -+ * Window clamp already applied above. -+ * If our current window offering is within 1 mss of the -+ * free space we just keep it. This prevents the divide -+ * and multiply from happening most of the time. -+ * We also don't do any window rounding when the free space -+ * is too small. -+ */ -+ if (window <= free_space - mss || window > free_space) -+ window = (free_space / mss) * mss; -+ else if (mss == full_space && -+ free_space > window + (full_space >> 1)) -+ window = free_space; -+ } -+ -+ return window; -+} -+ -+void mptcp_syn_options(struct sock *sk, struct tcp_out_options *opts, -+ unsigned *remaining) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ -+ opts->options |= OPTION_MPTCP; -+ if (is_master_tp(tp)) { -+ opts->mptcp_options |= OPTION_MP_CAPABLE | OPTION_TYPE_SYN; -+ *remaining -= MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN; -+ opts->mp_capable.sender_key = tp->mptcp_loc_key; -+ opts->dss_csum = !!sysctl_mptcp_checksum; -+ } else { -+ struct mptcp_cb *mpcb = tp->mpcb; -+ -+ opts->mptcp_options |= OPTION_MP_JOIN | OPTION_TYPE_SYN; -+ *remaining -= MPTCP_SUB_LEN_JOIN_SYN_ALIGN; -+ opts->mp_join_syns.token = mpcb->mptcp_rem_token; -+ opts->addr_id = tp->mptcp->loc_id; -+ opts->mp_join_syns.sender_nonce = tp->mptcp->mptcp_loc_nonce; -+ } -+} -+ -+void mptcp_synack_options(struct request_sock *req, -+ struct tcp_out_options *opts, unsigned *remaining) -+{ -+ struct mptcp_request_sock *mtreq; -+ mtreq = mptcp_rsk(req); -+ -+ opts->options |= OPTION_MPTCP; -+ /* MPCB not yet set - thus it's a new MPTCP-session */ -+ if (!mtreq->mpcb) { -+ opts->mptcp_options |= OPTION_MP_CAPABLE | OPTION_TYPE_SYNACK; -+ opts->mp_capable.sender_key = mtreq->mptcp_loc_key; -+ opts->dss_csum = !!sysctl_mptcp_checksum || mtreq->dss_csum; -+ *remaining -= MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN; -+ } else { -+ opts->mptcp_options |= OPTION_MP_JOIN | OPTION_TYPE_SYNACK; -+ opts->mp_join_syns.sender_truncated_mac = -+ mtreq->mptcp_hash_tmac; -+ opts->mp_join_syns.sender_nonce = mtreq->mptcp_loc_nonce; -+ opts->addr_id = mtreq->loc_id; -+ *remaining -= MPTCP_SUB_LEN_JOIN_SYNACK_ALIGN; -+ } -+} -+ -+void mptcp_established_options(struct sock *sk, struct sk_buff *skb, -+ struct tcp_out_options *opts, unsigned *size) -+{ -+ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); -+ struct mptcp_cb *mpcb = tp->mpcb; -+ struct tcp_skb_cb *tcb = skb ? TCP_SKB_CB(skb) : NULL; -+ -+ /* In fallback mp_fail-mode, we have to repeat it until the fallback -+ * has been done by the sender -+ */ -+ if (unlikely(tp->mptcp->send_mp_fail)) { -+ opts->options |= OPTION_MPTCP; -+ opts->mptcp_options |= OPTION_MP_FAIL; -+ opts->data_ack = (__u32)(mpcb->csum_cutoff_seq >> 32); -+ opts->data_seq = (__u32)mpcb->csum_cutoff_seq; -+ *size += MPTCP_SUB_LEN_FAIL; -+ return; -+ } -+ -+ if (unlikely(tp->send_mp_fclose)) { -+ opts->options |= OPTION_MPTCP; -+ opts->mptcp_options |= OPTION_MP_FCLOSE; -+ opts->mp_capable.receiver_key = mpcb->mptcp_rem_key; -+ *size += MPTCP_SUB_LEN_FCLOSE_ALIGN; -+ return; -+ } -+ -+ /* 1. If we are the sender of the infinite-mapping, we need the -+ * MPTCPHDR_INF-flag, because a retransmission of the -+ * infinite-announcment still needs the mptcp-option. -+ * -+ * We need infinite_cutoff_seq, because retransmissions from before -+ * the infinite-cutoff-moment still need the MPTCP-signalling to stay -+ * consistent. -+ * -+ * 2. If we are the receiver of the infinite-mapping, we always skip -+ * mptcp-options, because acknowledgments from before the -+ * infinite-mapping point have already been sent out. -+ * -+ * I know, the whole infinite-mapping stuff is ugly... -+ * -+ * TODO: Handle wrapped data-sequence numbers -+ * (even if it's very unlikely) -+ */ -+ if (unlikely(mpcb->infinite_mapping_snd) && -+ tp->mptcp->fully_established && -+ ((mpcb->send_infinite_mapping && tcb && -+ !(tcb->mptcp_flags & MPTCPHDR_INF) && -+ !before(tcb->seq, tp->mptcp->infinite_cutoff_seq)) || -+ !mpcb->send_infinite_mapping)) -+ return; -+ -+ if (unlikely(tp->mptcp->include_mpc)) { -+ opts->options |= OPTION_MPTCP; -+ opts->mptcp_options |= OPTION_MP_CAPABLE | -+ OPTION_TYPE_ACK; -+ *size += MPTCP_SUB_LEN_CAPABLE_ACK_ALIGN; -+ opts->mp_capable.sender_key = mpcb->mptcp_loc_key; -+ opts->mp_capable.receiver_key = mpcb->mptcp_rem_key; -+ opts->dss_csum = mpcb->dss_csum; -+ -+ if (skb) -+ tp->mptcp->include_mpc = 0; -+ } -+ if (unlikely(tp->mptcp->pre_established)) { -+ opts->options |= OPTION_MPTCP; -+ opts->mptcp_options |= OPTION_MP_JOIN | OPTION_TYPE_ACK; -+ *size += MPTCP_SUB_LEN_JOIN_ACK_ALIGN; -+ } -+ -+ if (!tp->mptcp->include_mpc && !tp->mptcp->pre_established) { -+ opts->options |= OPTION_MPTCP; -+ opts->mptcp_options |= OPTION_DATA_ACK; -+ /* If !skb, we come from tcp_current_mss and thus we always -+ * assume that the DSS-option will be set for the data-packet. -+ */ -+ if (skb && !mptcp_is_data_seq(skb)) { -+ opts->data_ack = meta_tp->rcv_nxt; -+ -+ *size += MPTCP_SUB_LEN_ACK_ALIGN; -+ } else { -+ opts->data_ack = meta_tp->rcv_nxt; -+ -+ /* Doesn't matter, if csum included or not. It will be -+ * either 10 or 12, and thus aligned = 12 -+ */ -+ *size += MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN; -+ } -+ -+ *size += MPTCP_SUB_LEN_DSS_ALIGN; -+ } -+ -+ if (mpcb->pm_ops->addr_signal) -+ mpcb->pm_ops->addr_signal(sk, size, opts, skb); -+ -+ if (unlikely(tp->mptcp->send_mp_prio) && -+ MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_PRIO_ALIGN) { -+ opts->options |= OPTION_MPTCP; -+ opts->mptcp_options |= OPTION_MP_PRIO; -+ if (skb) -+ tp->mptcp->send_mp_prio = 0; -+ *size += MPTCP_SUB_LEN_PRIO_ALIGN; -+ } -+ -+ return; -+} -+ -+u16 mptcp_select_window(struct sock *sk) -+{ -+ u16 new_win = tcp_select_window(sk); -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct tcp_sock *meta_tp = mptcp_meta_tp(tp); -+ -+ meta_tp->rcv_wnd = tp->rcv_wnd; -+ meta_tp->rcv_wup = meta_tp->rcv_nxt; -+ -+ return new_win; -+} -+ -+void mptcp_options_write(__be32 *ptr, struct tcp_sock *tp, -+ struct tcp_out_options *opts, -+ struct sk_buff *skb) -+{ -+ if (unlikely(OPTION_MP_CAPABLE & opts->mptcp_options)) { -+ struct mp_capable *mpc = (struct mp_capable *)ptr; -+ -+ mpc->kind = TCPOPT_MPTCP; -+ -+ if ((OPTION_TYPE_SYN & opts->mptcp_options) || -+ (OPTION_TYPE_SYNACK & opts->mptcp_options)) { -+ mpc->sender_key = opts->mp_capable.sender_key; -+ mpc->len = MPTCP_SUB_LEN_CAPABLE_SYN; -+ ptr += MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN >> 2; -+ } else if (OPTION_TYPE_ACK & opts->mptcp_options) { -+ mpc->sender_key = opts->mp_capable.sender_key; -+ mpc->receiver_key = opts->mp_capable.receiver_key; -+ mpc->len = MPTCP_SUB_LEN_CAPABLE_ACK; -+ ptr += MPTCP_SUB_LEN_CAPABLE_ACK_ALIGN >> 2; -+ } -+ -+ mpc->sub = MPTCP_SUB_CAPABLE; -+ mpc->ver = 0; -+ mpc->a = opts->dss_csum; -+ mpc->b = 0; -+ mpc->rsv = 0; -+ mpc->h = 1; -+ } -+ -+ if (unlikely(OPTION_MP_JOIN & opts->mptcp_options)) { -+ struct mp_join *mpj = (struct mp_join *)ptr; -+ -+ mpj->kind = TCPOPT_MPTCP; -+ mpj->sub = MPTCP_SUB_JOIN; -+ mpj->rsv = 0; -+ mpj->addr_id = opts->addr_id; -+ -+ if (OPTION_TYPE_SYN & opts->mptcp_options) { -+ mpj->len = MPTCP_SUB_LEN_JOIN_SYN; -+ mpj->u.syn.token = opts->mp_join_syns.token; -+ mpj->u.syn.nonce = opts->mp_join_syns.sender_nonce; -+ mpj->b = tp->mptcp->low_prio; -+ ptr += MPTCP_SUB_LEN_JOIN_SYN_ALIGN >> 2; -+ } else if (OPTION_TYPE_SYNACK & opts->mptcp_options) { -+ mpj->len = MPTCP_SUB_LEN_JOIN_SYNACK; -+ mpj->u.synack.mac = -+ opts->mp_join_syns.sender_truncated_mac; -+ mpj->u.synack.nonce = opts->mp_join_syns.sender_nonce; -+ mpj->b = tp->mptcp->low_prio; -+ ptr += MPTCP_SUB_LEN_JOIN_SYNACK_ALIGN >> 2; -+ } else if (OPTION_TYPE_ACK & opts->mptcp_options) { -+ mpj->len = MPTCP_SUB_LEN_JOIN_ACK; -+ memcpy(mpj->u.ack.mac, &tp->mptcp->sender_mac[0], 20); -+ ptr += MPTCP_SUB_LEN_JOIN_ACK_ALIGN >> 2; -+ } -+ } -+ if (unlikely(OPTION_ADD_ADDR & opts->mptcp_options)) { -+ struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; -+ -+ mpadd->kind = TCPOPT_MPTCP; -+ if (opts->add_addr_v4) { -+ mpadd->len = MPTCP_SUB_LEN_ADD_ADDR4; -+ mpadd->sub = MPTCP_SUB_ADD_ADDR; -+ mpadd->ipver = 4; -+ mpadd->addr_id = opts->add_addr4.addr_id; -+ mpadd->u.v4.addr = opts->add_addr4.addr; -+ ptr += MPTCP_SUB_LEN_ADD_ADDR4_ALIGN >> 2; -+ } else if (opts->add_addr_v6) { -+ mpadd->len = MPTCP_SUB_LEN_ADD_ADDR6; -+ mpadd->sub = MPTCP_SUB_ADD_ADDR; -+ mpadd->ipver = 6; -+ mpadd->addr_id = opts->add_addr6.addr_id; -+ memcpy(&mpadd->u.v6.addr, &opts->add_addr6.addr, -+ sizeof(mpadd->u.v6.addr)); -+ ptr += MPTCP_SUB_LEN_ADD_ADDR6_ALIGN >> 2; -+ } -+ } -+ if (unlikely(OPTION_REMOVE_ADDR & opts->mptcp_options)) { -+ struct mp_remove_addr *mprem = (struct mp_remove_addr *)ptr; -+ u8 *addrs_id; -+ int id, len, len_align; -+ -+ len = mptcp_sub_len_remove_addr(opts->remove_addrs); -+ len_align = mptcp_sub_len_remove_addr_align(opts->remove_addrs); -+ -+ mprem->kind = TCPOPT_MPTCP; -+ mprem->len = len; -+ mprem->sub = MPTCP_SUB_REMOVE_ADDR; -+ mprem->rsv = 0; -+ addrs_id = &mprem->addrs_id; -+ -+ mptcp_for_each_bit_set(opts->remove_addrs, id) -+ *(addrs_id++) = id; -+ -+ /* Fill the rest with NOP's */ -+ if (len_align > len) { -+ int i; -+ for (i = 0; i < len_align - len; i++) -+ *(addrs_id++) = TCPOPT_NOP; -+ } -+ -+ ptr += len_align >> 2; -+ } -+ if (unlikely(OPTION_MP_FAIL & opts->mptcp_options)) { -+ struct mp_fail *mpfail = (struct mp_fail *)ptr; -+ -+ mpfail->kind = TCPOPT_MPTCP; -+ mpfail->len = MPTCP_SUB_LEN_FAIL; -+ mpfail->sub = MPTCP_SUB_FAIL; -+ mpfail->rsv1 = 0; -+ mpfail->rsv2 = 0; -+ mpfail->data_seq = htonll(((u64)opts->data_ack << 32) | opts->data_seq); -+ -+ ptr += MPTCP_SUB_LEN_FAIL_ALIGN >> 2; -+ } -+ if (unlikely(OPTION_MP_FCLOSE & opts->mptcp_options)) { -+ struct mp_fclose *mpfclose = (struct mp_fclose *)ptr; -+ -+ mpfclose->kind = TCPOPT_MPTCP; -+ mpfclose->len = MPTCP_SUB_LEN_FCLOSE; -+ mpfclose->sub = MPTCP_SUB_FCLOSE; -+ mpfclose->rsv1 = 0; -+ mpfclose->rsv2 = 0; -+ mpfclose->key = opts->mp_capable.receiver_key; -+ -+ ptr += MPTCP_SUB_LEN_FCLOSE_ALIGN >> 2; -+ } -+ -+ if (OPTION_DATA_ACK & opts->mptcp_options) { -+ if (!mptcp_is_data_seq(skb)) { -+ struct mp_dss *mdss = (struct mp_dss *)ptr; -+ -+ mdss->kind = TCPOPT_MPTCP; -+ mdss->sub = MPTCP_SUB_DSS; -+ mdss->rsv1 = 0; -+ mdss->rsv2 = 0; -+ mdss->F = 0; -+ mdss->m = 0; -+ mdss->M = 0; -+ mdss->a = 0; -+ mdss->A = 1; -+ mdss->len = mptcp_sub_len_dss(mdss, tp->mpcb->dss_csum); -+ -+ ptr++; -+ *ptr++ = htonl(opts->data_ack); -+ } else { -+ /**** Just update the data_ack ****/ -+ -+ /* Get pointer to data_ack-field. MPTCP is always at -+ * the end of the TCP-options. -+ */ -+ /* TODO if we allow sending 64-bit dseq's we have to change "16" */ -+ __be32 *dack = (__be32 *)(skb->data + (tcp_hdr(skb)->doff << 2) - 16); -+ -+ *dack = htonl(opts->data_ack); -+ } -+ } -+ if (unlikely(OPTION_MP_PRIO & opts->mptcp_options)) { -+ struct mp_prio *mpprio = (struct mp_prio *)ptr; -+ -+ mpprio->kind = TCPOPT_MPTCP; -+ mpprio->len = MPTCP_SUB_LEN_PRIO; -+ mpprio->sub = MPTCP_SUB_PRIO; -+ mpprio->rsv = 0; -+ mpprio->b = tp->mptcp->low_prio; -+ mpprio->addr_id = TCPOPT_NOP; -+ -+ ptr += MPTCP_SUB_LEN_PRIO_ALIGN >> 2; -+ } -+} -+ -+/* Returns the next segment to be sent from the mptcp meta-queue. -+ * (chooses the reinject queue if any segment is waiting in it, otherwise, -+ * chooses the normal write queue). -+ * Sets *@reinject to 1 if the returned segment comes from the -+ * reinject queue. Sets it to 0 if it is the regular send-head of the meta-sk, -+ * and sets it to -1 if it is a meta-level retransmission to optimize the -+ * receive-buffer. -+ */ -+struct sk_buff *mptcp_next_segment(struct sock *meta_sk, int *reinject) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; -+ struct sk_buff *skb = NULL; -+ if (reinject) -+ *reinject = 0; -+ -+ /* If we are in fallback-mode, just take from the meta-send-queue */ -+ if (mpcb->infinite_mapping_snd || mpcb->send_infinite_mapping) -+ return tcp_send_head(meta_sk); -+ -+ skb = skb_peek(&mpcb->reinject_queue); -+ -+ if (skb) { -+ if (reinject) -+ *reinject = 1; -+ } else { -+ skb = tcp_send_head(meta_sk); -+ -+ if (!skb && meta_sk->sk_socket && -+ test_bit(SOCK_NOSPACE, &meta_sk->sk_socket->flags) && -+ sk_stream_wspace(meta_sk) < sk_stream_min_wspace(meta_sk)) { -+ struct sock *subsk = get_available_subflow(meta_sk, NULL, NULL); -+ if (!subsk) -+ return NULL; -+ -+ skb = mptcp_rcv_buf_optimization(subsk, 0); -+ if (skb && reinject) -+ *reinject = -1; -+ } -+ } -+ return skb; -+} -+ -+/* Sends the datafin */ -+void mptcp_send_fin(struct sock *meta_sk) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct sk_buff *skb = tcp_write_queue_tail(meta_sk); -+ int mss_now; -+ -+ if ((1 << meta_sk->sk_state) & (TCPF_CLOSE_WAIT | TCPF_LAST_ACK)) -+ meta_tp->mpcb->passive_close = 1; -+ -+ /* Optimization, tack on the FIN if we have a queue of -+ * unsent frames. But be careful about outgoing SACKS -+ * and IP options. -+ */ -+ mss_now = mptcp_current_mss(meta_sk); -+ -+ if (tcp_send_head(meta_sk) != NULL) { -+ TCP_SKB_CB(skb)->mptcp_flags |= MPTCPHDR_FIN; -+ TCP_SKB_CB(skb)->end_seq++; -+ meta_tp->write_seq++; -+ } else { -+ /* Socket is locked, keep trying until memory is available. */ -+ for (;;) { -+ skb = alloc_skb_fclone(MAX_TCP_HEADER, -+ meta_sk->sk_allocation); -+ if (skb) -+ break; -+ yield(); -+ } -+ /* Reserve space for headers and prepare control bits. */ -+ skb_reserve(skb, MAX_TCP_HEADER); -+ -+ tcp_init_nondata_skb(skb, meta_tp->write_seq, TCPHDR_ACK); -+ TCP_SKB_CB(skb)->end_seq++; -+ TCP_SKB_CB(skb)->mptcp_flags |= MPTCPHDR_FIN | MPTCPHDR_SEQ; -+ tcp_queue_skb(meta_sk, skb); -+ } -+ __tcp_push_pending_frames(meta_sk, mss_now, TCP_NAGLE_OFF); -+} -+ -+void mptcp_send_active_reset(struct sock *meta_sk, gfp_t priority) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ struct sock *sk = NULL, *sk_it = NULL, *tmpsk; -+ -+ if (!mpcb->cnt_subflows) -+ return; -+ -+ WARN_ON(meta_tp->send_mp_fclose); -+ -+ /* First - select a socket */ -+ sk = mptcp_select_ack_sock(meta_sk, 0); -+ -+ /* May happen if no subflow is in an appropriate state */ -+ if (!sk) -+ return; -+ -+ /* We are in infinite mode - just send a reset */ -+ if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv) { -+ sk->sk_err = ECONNRESET; -+ if (tcp_need_reset(sk->sk_state)) -+ tcp_send_active_reset(sk, priority); -+ mptcp_sub_force_close(sk); -+ return; -+ } -+ -+ -+ tcp_sk(sk)->send_mp_fclose = 1; -+ /** Reset all other subflows */ -+ -+ /* tcp_done must be handled with bh disabled */ -+ if (!in_serving_softirq()) -+ local_bh_disable(); -+ -+ mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { -+ if (tcp_sk(sk_it)->send_mp_fclose) -+ continue; -+ -+ sk_it->sk_err = ECONNRESET; -+ if (tcp_need_reset(sk_it->sk_state)) -+ tcp_send_active_reset(sk_it, GFP_ATOMIC); -+ mptcp_sub_force_close(sk_it); -+ } -+ -+ if (!in_serving_softirq()) -+ local_bh_enable(); -+ -+ tcp_send_ack(sk); -+ inet_csk_reset_keepalive_timer(sk, inet_csk(sk)->icsk_rto); -+ -+ meta_tp->send_mp_fclose = 1; -+} -+ -+static void mptcp_ack_retransmit_timer(struct sock *sk) -+{ -+ struct sk_buff *skb; -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct inet_connection_sock *icsk = inet_csk(sk); -+ -+ if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk)) -+ goto out; /* Routing failure or similar */ -+ -+ if (!tp->retrans_stamp) -+ tp->retrans_stamp = tcp_time_stamp ? : 1; -+ -+ if (tcp_write_timeout(sk)) { -+ tp->mptcp->pre_established = 0; -+ sk_stop_timer(sk, &tp->mptcp->mptcp_ack_timer); -+ tcp_send_active_reset(sk, GFP_ATOMIC); -+ goto out; -+ } -+ -+ skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); -+ if (skb == NULL) { -+ sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, -+ jiffies + icsk->icsk_rto); -+ return; -+ } -+ -+ /* Reserve space for headers and prepare control bits */ -+ skb_reserve(skb, MAX_TCP_HEADER); -+ tcp_init_nondata_skb(skb, tp->snd_una, TCPHDR_ACK); -+ -+ TCP_SKB_CB(skb)->when = tcp_time_stamp; -+ if (tcp_transmit_skb(sk, skb, 0, GFP_ATOMIC) > 0) { -+ /* Retransmission failed because of local congestion, -+ * do not backoff. -+ */ -+ if (!icsk->icsk_retransmits) -+ icsk->icsk_retransmits = 1; -+ sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, -+ jiffies + icsk->icsk_rto); -+ return; -+ } -+ -+ -+ icsk->icsk_retransmits++; -+ icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX); -+ sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, -+ jiffies + icsk->icsk_rto); -+ if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1, 0, 0)) { -+ __sk_dst_reset(sk); -+ } -+ -+out:; -+} -+ -+void mptcp_ack_handler(unsigned long data) -+{ -+ struct sock *sk = (struct sock *)data; -+ struct sock *meta_sk = mptcp_meta_sk(sk); -+ -+ bh_lock_sock(meta_sk); -+ if (sock_owned_by_user(meta_sk)) { -+ /* Try again later */ -+ sk_reset_timer(sk, &tcp_sk(sk)->mptcp->mptcp_ack_timer, -+ jiffies + (HZ / 20)); -+ goto out_unlock; -+ } -+ -+ if (sk->sk_state == TCP_CLOSE) -+ goto out_unlock; -+ -+ mptcp_ack_retransmit_timer(sk); -+ -+ sk_mem_reclaim(sk); -+ -+out_unlock: -+ bh_unlock_sock(meta_sk); -+ sock_put(sk); -+} -+ -+/* Similar to tcp_retransmit_skb -+ * -+ * The diff is that we handle the retransmission-stats (retrans_stamp) at the -+ * meta-level. -+ */ -+int mptcp_retransmit_skb(struct sock *meta_sk, struct sk_buff *skb) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct sock *subsk; -+ struct sk_buff *subskb; -+ unsigned int limit, tso_segs, mss_now; -+ int err = -1, oldpcount; -+ -+ /* Do not sent more than we queued. 1/4 is reserved for possible -+ * copying overhead: fragmentation, tunneling, mangling etc. -+ * -+ * This is a meta-retransmission thus we check on the meta-socket. -+ */ -+ if (atomic_read(&meta_sk->sk_wmem_alloc) > -+ min(meta_sk->sk_wmem_queued + (meta_sk->sk_wmem_queued >> 2), meta_sk->sk_sndbuf)) { -+ return -EAGAIN; -+ } -+ -+ /* We need to make sure that the retransmitted segment can be sent on a -+ * subflow right now. If it is too big, it needs to be fragmented. -+ */ -+ subsk = get_available_subflow(meta_sk, skb, &mss_now); -+ if (!subsk) { -+ /* We want to increase icsk_retransmits, thus return 0, so that -+ * mptcp_retransmit_timer enters the desired branch. -+ */ -+ err = 0; -+ goto failed; -+ } -+ -+ /* If the segment was cloned (e.g. a meta retransmission), the header -+ * must be expanded/copied so that there is no corruption of TSO -+ * information. -+ */ -+ if (skb_unclone(skb, GFP_ATOMIC)) { -+ err = ENOMEM; -+ goto failed; -+ } -+ -+ oldpcount = tcp_skb_pcount(skb); -+ tcp_set_skb_tso_segs(meta_sk, skb, mss_now); -+ tso_segs = tcp_skb_pcount(skb); -+ BUG_ON(!tso_segs); -+ -+ /* The MSS might have changed and so the number of segments. We -+ * need to account for this change. -+ */ -+ if (unlikely(oldpcount != tso_segs)) -+ tcp_adjust_pcount(meta_sk, skb, oldpcount - tso_segs); -+ -+ limit = mss_now; -+ if (tso_segs > 1 && !tcp_urg_mode(meta_tp)) -+ limit = tcp_mss_split_point(subsk, skb, mss_now, -+ min_t(unsigned int, -+ tcp_cwnd_test(tcp_sk(subsk), skb), -+ subsk->sk_gso_max_segs), -+ TCP_NAGLE_OFF); -+ -+ if (skb->len > limit && -+ unlikely(mptso_fragment(meta_sk, skb, limit, mss_now, -+ GFP_ATOMIC, 0))) -+ goto failed; -+ -+ subskb = mptcp_skb_entail(subsk, skb, -1); -+ if (!subskb) -+ goto failed; -+ -+ TCP_SKB_CB(skb)->when = tcp_time_stamp; -+ TCP_SKB_CB(subskb)->when = tcp_time_stamp; -+ err = tcp_transmit_skb(subsk, subskb, 1, GFP_ATOMIC); -+ if (!err) { -+ /* Update global TCP statistics. */ -+ TCP_INC_STATS(sock_net(meta_sk), TCP_MIB_RETRANSSEGS); -+ -+ /* Diff to tcp_retransmit_skb */ -+ -+ /* Save stamp of the first retransmit. */ -+ if (!meta_tp->retrans_stamp) -+ meta_tp->retrans_stamp = TCP_SKB_CB(subskb)->when; -+ mptcp_sub_event_new_data_sent(subsk, subskb, skb); -+ } else { -+ mptcp_transmit_skb_failed(subsk, skb, subskb); -+ } -+ -+failed: -+ return err; -+} -+ -+/* Similar to tcp_retransmit_timer -+ * -+ * The diff is that we have to handle retransmissions of the FAST_CLOSE-message -+ * and that we don't have an srtt estimation at the meta-level. -+ */ -+void mptcp_retransmit_timer(struct sock *meta_sk) -+{ -+ struct tcp_sock *meta_tp = tcp_sk(meta_sk); -+ struct mptcp_cb *mpcb = meta_tp->mpcb; -+ struct inet_connection_sock *meta_icsk = inet_csk(meta_sk); -+ int err; -+ -+ /* In fallback, retransmission is handled at the subflow-level */ -+ if (!meta_tp->packets_out || mpcb->infinite_mapping_snd || -+ mpcb->send_infinite_mapping) -+ return; -+ -+ WARN_ON(tcp_write_queue_empty(meta_sk)); -+ -+ if (!meta_tp->snd_wnd && !sock_flag(meta_sk, SOCK_DEAD) && -+ !((1 << meta_sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) { -+ /* Receiver dastardly shrinks window. Our retransmits -+ * become zero probes, but we should not timeout this -+ * connection. If the socket is an orphan, time it out, -+ * we cannot allow such beasts to hang infinitely. -+ */ -+ struct inet_sock *meta_inet = inet_sk(meta_sk); -+ if (meta_sk->sk_family == AF_INET) { -+ LIMIT_NETDEBUG(KERN_DEBUG "MPTCP: Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n", -+ &meta_inet->inet_daddr, -+ ntohs(meta_inet->inet_dport), -+ meta_inet->inet_num, meta_tp->snd_una, -+ meta_tp->snd_nxt); -+ } -+#if IS_ENABLED(CONFIG_IPV6) -+ else if (meta_sk->sk_family == AF_INET6) { -+ LIMIT_NETDEBUG(KERN_DEBUG "MPTCP: Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n", -+ &meta_sk->sk_v6_daddr, -+ ntohs(meta_inet->inet_dport), -+ meta_inet->inet_num, meta_tp->snd_una, -+ meta_tp->snd_nxt); -+ } -+#endif -+ if (tcp_time_stamp - meta_tp->rcv_tstamp > TCP_RTO_MAX) { -+ tcp_write_err(meta_sk); -+ return; -+ } -+ -+ mptcp_retransmit_skb(meta_sk, tcp_write_queue_head(meta_sk)); -+ goto out_reset_timer; -+ } -+ -+ if (tcp_write_timeout(meta_sk)) -+ return; -+ -+ if (meta_icsk->icsk_retransmits == 0) -+ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_TCPTIMEOUTS); -+ -+ meta_icsk->icsk_ca_state = TCP_CA_Loss; -+ -+ err = mptcp_retransmit_skb(meta_sk, tcp_write_queue_head(meta_sk)); -+ if (err > 0) { -+ /* Retransmission failed because of local congestion, -+ * do not backoff. -+ */ -+ if (!meta_icsk->icsk_retransmits) -+ meta_icsk->icsk_retransmits = 1; -+ inet_csk_reset_xmit_timer(meta_sk, ICSK_TIME_RETRANS, -+ min(meta_icsk->icsk_rto, TCP_RESOURCE_PROBE_INTERVAL), -+ TCP_RTO_MAX); -+ return; -+ } -+ -+ /* Increase the timeout each time we retransmit. Note that -+ * we do not increase the rtt estimate. rto is initialized -+ * from rtt, but increases here. Jacobson (SIGCOMM 88) suggests -+ * that doubling rto each time is the least we can get away with. -+ * In KA9Q, Karn uses this for the first few times, and then -+ * goes to quadratic. netBSD doubles, but only goes up to *64, -+ * and clamps at 1 to 64 sec afterwards. Note that 120 sec is -+ * defined in the protocol as the maximum possible RTT. I guess -+ * we'll have to use something other than TCP to talk to the -+ * University of Mars. -+ * -+ * PAWS allows us longer timeouts and large windows, so once -+ * implemented ftp to mars will work nicely. We will have to fix -+ * the 120 second clamps though! -+ */ -+ meta_icsk->icsk_backoff++; -+ meta_icsk->icsk_retransmits++; -+ -+out_reset_timer: -+ /* If stream is thin, use linear timeouts. Since 'icsk_backoff' is -+ * used to reset timer, set to 0. Recalculate 'icsk_rto' as this -+ * might be increased if the stream oscillates between thin and thick, -+ * thus the old value might already be too high compared to the value -+ * set by 'tcp_set_rto' in tcp_input.c which resets the rto without -+ * backoff. Limit to TCP_THIN_LINEAR_RETRIES before initiating -+ * exponential backoff behaviour to avoid continue hammering -+ * linear-timeout retransmissions into a black hole -+ */ -+ if (meta_sk->sk_state == TCP_ESTABLISHED && -+ (meta_tp->thin_lto || sysctl_tcp_thin_linear_timeouts) && -+ tcp_stream_is_thin(meta_tp) && -+ meta_icsk->icsk_retransmits <= TCP_THIN_LINEAR_RETRIES) { -+ meta_icsk->icsk_backoff = 0; -+ /* We cannot do the same as in tcp_write_timer because the -+ * srtt is not set here. -+ */ -+ mptcp_set_rto(meta_sk); -+ } else { -+ /* Use normal (exponential) backoff */ -+ meta_icsk->icsk_rto = min(meta_icsk->icsk_rto << 1, TCP_RTO_MAX); -+ } -+ inet_csk_reset_xmit_timer(meta_sk, ICSK_TIME_RETRANS, meta_icsk->icsk_rto, TCP_RTO_MAX); -+ -+ return; -+} -+ -+/* Modify values to an mptcp-level for the initial window of new subflows */ -+void mptcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, -+ __u32 *window_clamp, int wscale_ok, -+ __u8 *rcv_wscale, __u32 init_rcv_wnd, -+ const struct sock *sk) -+{ -+ struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; -+ -+ *window_clamp = mpcb->orig_window_clamp; -+ __space = tcp_win_from_space(mpcb->orig_sk_rcvbuf); -+ -+ tcp_select_initial_window(__space, mss, rcv_wnd, window_clamp, -+ wscale_ok, rcv_wscale, init_rcv_wnd, sk); -+} -+ -+unsigned int mptcp_current_mss(struct sock *meta_sk) -+{ -+ unsigned int mss = 0; -+ struct sock *sk; -+ -+ mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { -+ int this_mss; -+ -+ if (!mptcp_sk_can_send(sk)) -+ continue; -+ -+ this_mss = tcp_current_mss(sk); -+ if (this_mss > mss) -+ mss = this_mss; -+ } -+ -+ /* If no subflow is available, we take a default-mss from the -+ * meta-socket. -+ */ -+ return !mss ? tcp_current_mss(meta_sk) : mss; -+} -+ -+int mptcp_select_size(const struct sock *meta_sk, bool sg) -+{ -+ int mss = 0; /* We look for the smallest MSS */ -+ struct sock *sk; -+ -+ mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { -+ int this_mss; -+ -+ if (!mptcp_sk_can_send(sk)) -+ continue; -+ -+ this_mss = tcp_sk(sk)->mss_cache; -+ if (this_mss > mss) -+ mss = this_mss; -+ } -+ -+ if (sg) { -+ if (mptcp_sk_can_gso(meta_sk)) { -+ mss = SKB_WITH_OVERHEAD(2048 - MAX_TCP_HEADER); -+ } else { -+ int pgbreak = SKB_MAX_HEAD(MAX_TCP_HEADER); -+ -+ if (mss >= pgbreak && -+ mss <= pgbreak + (MAX_SKB_FRAGS - 1) * PAGE_SIZE) -+ mss = pgbreak; -+ } -+ } -+ -+ return !mss ? tcp_sk(meta_sk)->mss_cache : mss; -+} -+ -+int mptcp_check_snd_buf(const struct tcp_sock *tp) -+{ -+ struct sock *sk; -+ u32 rtt_max = tp->srtt; -+ u64 bw_est; -+ -+ if (!tp->srtt) -+ return tp->reordering + 1; -+ -+ mptcp_for_each_sk(tp->mpcb, sk) { -+ if (!mptcp_sk_can_send(sk)) -+ continue; -+ -+ if (rtt_max < tcp_sk(sk)->srtt) -+ rtt_max = tcp_sk(sk)->srtt; -+ } -+ -+ bw_est = div64_u64(((u64)tp->snd_cwnd * rtt_max) << 16, -+ (u64)tp->srtt); -+ -+ return max_t(unsigned int, (u32)(bw_est >> 16), -+ tp->reordering + 1); -+ -+} -+ -+unsigned int mptcp_xmit_size_goal(struct sock *meta_sk, u32 mss_now, -+ int large_allowed) -+{ -+ struct sock *sk; -+ u32 xmit_size_goal = 0; -+ -+ if (large_allowed && mptcp_sk_can_gso(meta_sk)) { -+ mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { -+ int this_size_goal; -+ -+ if (!mptcp_sk_can_send(sk)) -+ continue; -+ -+ this_size_goal = tcp_xmit_size_goal(sk, mss_now, 1); -+ if (this_size_goal > xmit_size_goal) -+ xmit_size_goal = this_size_goal; -+ } -+ } -+ -+ return max(xmit_size_goal, mss_now); -+} -+ -+/* Similar to tcp_trim_head - but we correctly copy the DSS-option */ -+int mptcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) -+{ -+ int dsslen = MPTCP_SUB_LEN_DSS_ALIGN + MPTCP_SUB_LEN_ACK_ALIGN + -+ MPTCP_SUB_LEN_SEQ_ALIGN; -+ char dss[dsslen]; -+ -+ /* DSS-option must be recovered afterwards. */ -+ memcpy(dss, skb->data - dsslen, dsslen); -+ -+ if (skb_cloned(skb)) { -+ /* pskb_expand_head will delete our DSS-option. We have to copy -+ * it back if pskb_expand_head succeeds. -+ */ -+ -+ if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) -+ return -ENOMEM; -+ -+ memcpy(skb->data - dsslen, dss, dsslen); -+ } -+ -+ __pskb_trim_head(skb, len); -+ -+ /* Put the DSS-option back in our header */ -+ memcpy(skb->data - dsslen, dss, dsslen); -+ -+ TCP_SKB_CB(skb)->seq += len; -+ skb->ip_summed = CHECKSUM_PARTIAL; -+ -+ skb->truesize -= len; -+ sk->sk_wmem_queued -= len; -+ sk_mem_uncharge(sk, len); -+ sock_set_flag(sk, SOCK_QUEUE_SHRUNK); -+ -+ /* Any change of skb->len requires recalculation of tso factor. */ -+ if (tcp_skb_pcount(skb) > 1) -+ tcp_set_skb_tso_segs(sk, skb, tcp_skb_mss(skb)); -+ -+ return 0; -+} -diff --git a/net/mptcp/mptcp_pm.c b/net/mptcp/mptcp_pm.c -new file mode 100644 -index 0000000..1f78ae1 ---- /dev/null -+++ b/net/mptcp/mptcp_pm.c -@@ -0,0 +1,170 @@ -+/* -+ * MPTCP implementation - MPTCP-subflow-management -+ * -+ * Initial Design & Implementation: -+ * Sébastien Barré -+ * -+ * Current Maintainer & Author: -+ * Christoph Paasch -+ * -+ * Additional authors: -+ * Jaakko Korkeaniemi -+ * Gregory Detal -+ * Fabien Duchêne -+ * Andreas Seelinger -+ * Lavkesh Lahngir -+ * Andreas Ripke -+ * Vlad Dogaru -+ * Octavian Purdila -+ * John Ronan -+ * Catalin Nicutar -+ * Brandon Heller -+ * -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+ -+#include -+#include -+ -+static DEFINE_SPINLOCK(mptcp_pm_list_lock); -+static LIST_HEAD(mptcp_pm_list); -+ -+static int mptcp_default_index(sa_family_t family, union inet_addr *addr, -+ struct net *net) -+{ -+ return 0; -+} -+ -+struct mptcp_pm_ops mptcp_pm_default = { -+ .get_local_index = mptcp_default_index, -+ .get_local_id = mptcp_default_index, /* We do not care */ -+ .name = "default", -+ .owner = THIS_MODULE, -+}; -+ -+static struct mptcp_pm_ops *mptcp_pm_find(const char *name) -+{ -+ struct mptcp_pm_ops *e; -+ -+ list_for_each_entry_rcu(e, &mptcp_pm_list, list) { -+ if (strcmp(e->name, name) == 0) -+ return e; -+ } -+ -+ return NULL; -+} -+ -+int mptcp_register_path_manager(struct mptcp_pm_ops *pm) -+{ -+ int ret = 0; -+ -+ if (!pm->get_local_index || !pm->get_local_id) -+ return -EINVAL; -+ -+ spin_lock(&mptcp_pm_list_lock); -+ if (mptcp_pm_find(pm->name)) { -+ pr_notice("%s already registered\n", pm->name); -+ ret = -EEXIST; -+ } else { -+ list_add_tail_rcu(&pm->list, &mptcp_pm_list); -+ pr_info("%s registered\n", pm->name); -+ } -+ spin_unlock(&mptcp_pm_list_lock); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(mptcp_register_path_manager); -+ -+void mptcp_unregister_path_manager(struct mptcp_pm_ops *pm) -+{ -+ spin_lock(&mptcp_pm_list_lock); -+ list_del_rcu(&pm->list); -+ spin_unlock(&mptcp_pm_list_lock); -+} -+EXPORT_SYMBOL_GPL(mptcp_unregister_path_manager); -+ -+void mptcp_get_default_path_manager(char *name) -+{ -+ struct mptcp_pm_ops *pm; -+ -+ BUG_ON(list_empty(&mptcp_pm_list)); -+ -+ rcu_read_lock(); -+ pm = list_entry(mptcp_pm_list.next, struct mptcp_pm_ops, list); -+ strncpy(name, pm->name, MPTCP_PM_NAME_MAX); -+ rcu_read_unlock(); -+} -+ -+int mptcp_set_default_path_manager(const char *name) -+{ -+ struct mptcp_pm_ops *pm; -+ int ret = -ENOENT; -+ -+ spin_lock(&mptcp_pm_list_lock); -+ pm = mptcp_pm_find(name); -+#ifdef CONFIG_MODULES -+ if (!pm && capable(CAP_NET_ADMIN)) { -+ spin_unlock(&mptcp_pm_list_lock); -+ -+ request_module("mptcp_%s", name); -+ spin_lock(&mptcp_pm_list_lock); -+ pm = mptcp_pm_find(name); -+ } -+#endif -+ -+ if (pm) { -+ list_move(&pm->list, &mptcp_pm_list); -+ ret = 0; -+ } else { -+ pr_info("%s is not available\n", name); -+ } -+ spin_unlock(&mptcp_pm_list_lock); -+ -+ return ret; -+} -+ -+void mptcp_init_path_manager(struct mptcp_cb *mpcb) -+{ -+ struct mptcp_pm_ops *pm; -+ -+ rcu_read_lock(); -+ list_for_each_entry_rcu(pm, &mptcp_pm_list, list) { -+ if (try_module_get(pm->owner)) { -+ mpcb->pm_ops = pm; -+ break; -+ } -+ } -+ rcu_read_unlock(); -+} -+ -+/* Manage refcounts on socket close. */ -+void mptcp_cleanup_path_manager(struct mptcp_cb *mpcb) -+{ -+ module_put(mpcb->pm_ops->owner); -+} -+ -+/* Fallback to the default path-manager. */ -+void mptcp_fallback_default(struct mptcp_cb *mpcb) -+{ -+ struct mptcp_pm_ops *pm; -+ -+ mptcp_cleanup_path_manager(mpcb); -+ pm = mptcp_pm_find("default"); -+ -+ /* Cannot fail - it's the default module */ -+ try_module_get(pm->owner); -+ mpcb->pm_ops = pm; -+} -+EXPORT_SYMBOL_GPL(mptcp_fallback_default); -+ -+/* Set default value from kernel configuration at bootup */ -+static int __init mptcp_path_manager_default(void) -+{ -+ return mptcp_set_default_path_manager(CONFIG_DEFAULT_MPTCP_PM); -+} -+late_initcall(mptcp_path_manager_default); -diff --git a/net/mptcp/mptcp_wvegas.c b/net/mptcp/mptcp_wvegas.c -new file mode 100644 -index 0000000..8e1fd50 ---- /dev/null -+++ b/net/mptcp/mptcp_wvegas.c -@@ -0,0 +1,270 @@ -+/* -+ * MPTCP implementation - WEIGHTED VEGAS -+ * -+ * Algorithm design: -+ * Yu Cao -+ * Mingwei Xu -+ * Xiaoming Fu -+ * -+ * Implementation: -+ * Yu Cao -+ * Enhuan Dong -+ * -+ * Ported to the official MPTCP-kernel: -+ * Christoph Paasch -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+static int initial_alpha = 2; -+static int total_alpha = 10; -+static int gamma = 1; -+ -+module_param(initial_alpha, int, 0644); -+MODULE_PARM_DESC(initial_alpha, "initial alpha for all subflows"); -+module_param(total_alpha, int, 0644); -+MODULE_PARM_DESC(total_alpha, "total alpha for all subflows"); -+module_param(gamma, int, 0644); -+MODULE_PARM_DESC(gamma, "limit on increase (scale by 2)"); -+ -+#define MPTCP_WVEGAS_SCALE 16 -+ -+/* wVegas variables */ -+struct wvegas { -+ u32 beg_snd_nxt; /* right edge during last RTT */ -+ u8 doing_wvegas_now;/* if true, do wvegas for this RTT */ -+ -+ u16 cnt_rtt; /* # of RTTs measured within last RTT */ -+ u32 sampled_rtt; /* cumulative RTTs measured within last RTT (in usec) */ -+ u32 base_rtt; /* the min of all wVegas RTT measurements seen (in usec) */ -+ -+ u64 instant_rate; /* cwnd / srtt_us, unit: pkts/us * 2^16 */ -+ u64 weight; /* the ratio of subflow's rate to the total rate, * 2^16 */ -+ int alpha; /* alpha for each subflows */ -+ -+ u32 queue_delay; /* queue delay*/ -+}; -+ -+ -+static inline u64 mptcp_wvegas_scale(u32 val, int scale) -+{ -+ return (u64) val << scale; -+} -+ -+static void wvegas_enable(struct sock *sk) -+{ -+ const struct tcp_sock *tp = tcp_sk(sk); -+ struct wvegas *wvegas = inet_csk_ca(sk); -+ -+ wvegas->doing_wvegas_now = 1; -+ -+ wvegas->beg_snd_nxt = tp->snd_nxt; -+ -+ wvegas->cnt_rtt = 0; -+ wvegas->sampled_rtt = 0; -+ -+ wvegas->instant_rate = 0; -+ wvegas->alpha = initial_alpha; -+ wvegas->weight = mptcp_wvegas_scale(1, MPTCP_WVEGAS_SCALE); -+ -+ wvegas->queue_delay = 0; -+} -+ -+static inline void wvegas_disable(struct sock *sk) -+{ -+ struct wvegas *wvegas = inet_csk_ca(sk); -+ -+ wvegas->doing_wvegas_now = 0; -+} -+ -+static void mptcp_wvegas_init(struct sock *sk) -+{ -+ struct wvegas *wvegas = inet_csk_ca(sk); -+ -+ wvegas->base_rtt = 0x7fffffff; -+ wvegas_enable(sk); -+} -+ -+static inline u64 mptcp_wvegas_rate(u32 cwnd, u32 rtt_us) -+{ -+ return div_u64(mptcp_wvegas_scale(cwnd, MPTCP_WVEGAS_SCALE), rtt_us); -+} -+ -+static void mptcp_wvegas_pkts_acked(struct sock *sk, u32 cnt, s32 rtt_us) -+{ -+ struct wvegas *wvegas = inet_csk_ca(sk); -+ u32 vrtt; -+ -+ if (rtt_us < 0) -+ return; -+ -+ vrtt = rtt_us + 1; -+ -+ if (vrtt < wvegas->base_rtt) -+ wvegas->base_rtt = vrtt; -+ -+ wvegas->sampled_rtt += vrtt; -+ wvegas->cnt_rtt++; -+} -+ -+static void mptcp_wvegas_state(struct sock *sk, u8 ca_state) -+{ -+ if (ca_state == TCP_CA_Open) -+ wvegas_enable(sk); -+ else -+ wvegas_disable(sk); -+} -+ -+static void mptcp_wvegas_cwnd_event(struct sock *sk, enum tcp_ca_event event) -+{ -+ if (event == CA_EVENT_CWND_RESTART) { -+ mptcp_wvegas_init(sk); -+ } else if (event == CA_EVENT_LOSS) { -+ struct wvegas *wvegas = inet_csk_ca(sk); -+ wvegas->instant_rate = 0; -+ } -+} -+ -+static inline u32 mptcp_wvegas_ssthresh(struct tcp_sock *tp) -+{ -+ return min(tp->snd_ssthresh, tp->snd_cwnd - 1); -+} -+ -+static u64 mptcp_wvegas_weight(struct mptcp_cb *mpcb, struct sock *sk) -+{ -+ u64 total_rate = 0; -+ struct sock *sub_sk; -+ struct wvegas *wvegas = inet_csk_ca(sk); -+ -+ if (!mpcb) -+ return wvegas->weight; -+ -+ -+ mptcp_for_each_sk(mpcb, sub_sk) { -+ struct wvegas *sub_wvegas = inet_csk_ca(sub_sk); -+ -+ /* sampled_rtt is initialized by 0 */ -+ if (mptcp_sk_can_send(sub_sk) && (sub_wvegas->sampled_rtt > 0)) -+ total_rate += sub_wvegas->instant_rate; -+ } -+ -+ if (total_rate && wvegas->instant_rate) -+ return div64_u64(mptcp_wvegas_scale(wvegas->instant_rate, MPTCP_WVEGAS_SCALE), total_rate); -+ else -+ return wvegas->weight; -+} -+ -+static void mptcp_wvegas_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) -+{ -+ struct tcp_sock *tp = tcp_sk(sk); -+ struct wvegas *wvegas = inet_csk_ca(sk); -+ -+ if (!wvegas->doing_wvegas_now) { -+ tcp_reno_cong_avoid(sk, ack, acked, in_flight); -+ return; -+ } -+ -+ if (after(ack, wvegas->beg_snd_nxt)) { -+ wvegas->beg_snd_nxt = tp->snd_nxt; -+ -+ if (wvegas->cnt_rtt <= 2) { -+ tcp_reno_cong_avoid(sk, ack, acked, in_flight); -+ } else { -+ u32 rtt, diff, q_delay; -+ u64 target_cwnd; -+ -+ rtt = wvegas->sampled_rtt / wvegas->cnt_rtt; -+ target_cwnd = div_u64(((u64)tp->snd_cwnd * wvegas->base_rtt), rtt); -+ -+ diff = div_u64((u64)tp->snd_cwnd * (rtt - wvegas->base_rtt), rtt); -+ -+ if (diff > gamma && tp->snd_cwnd <= tp->snd_ssthresh) { -+ tp->snd_cwnd = min(tp->snd_cwnd, (u32)target_cwnd+1); -+ tp->snd_ssthresh = mptcp_wvegas_ssthresh(tp); -+ -+ } else if (tp->snd_cwnd <= tp->snd_ssthresh) { -+ tcp_slow_start(tp, acked); -+ } else { -+ if (diff >= wvegas->alpha) { -+ wvegas->instant_rate = mptcp_wvegas_rate(tp->snd_cwnd, rtt); -+ wvegas->weight = mptcp_wvegas_weight(tp->mpcb, sk); -+ wvegas->alpha = max(2U, (u32)((wvegas->weight * total_alpha) >> MPTCP_WVEGAS_SCALE)); -+ } -+ if (diff > wvegas->alpha) { -+ tp->snd_cwnd--; -+ tp->snd_ssthresh = mptcp_wvegas_ssthresh(tp); -+ } else if (diff < wvegas->alpha) { -+ tp->snd_cwnd++; -+ } -+ -+ /* Try to drain link queue if needed*/ -+ q_delay = rtt - wvegas->base_rtt; -+ if ((wvegas->queue_delay == 0) || (wvegas->queue_delay > q_delay)) -+ wvegas->queue_delay = q_delay; -+ -+ if (q_delay >= 2 * wvegas->queue_delay) { -+ u32 backoff_factor = div_u64(mptcp_wvegas_scale(wvegas->base_rtt, MPTCP_WVEGAS_SCALE), 2 * rtt); -+ tp->snd_cwnd = ((u64)tp->snd_cwnd * backoff_factor) >> MPTCP_WVEGAS_SCALE; -+ wvegas->queue_delay = 0; -+ } -+ } -+ -+ if (tp->snd_cwnd < 2) -+ tp->snd_cwnd = 2; -+ else if (tp->snd_cwnd > tp->snd_cwnd_clamp) -+ tp->snd_cwnd = tp->snd_cwnd_clamp; -+ -+ tp->snd_ssthresh = tcp_current_ssthresh(sk); -+ } -+ -+ wvegas->cnt_rtt = 0; -+ wvegas->sampled_rtt = 0; -+ } -+ /* Use normal slow start */ -+ else if (tp->snd_cwnd <= tp->snd_ssthresh) -+ tcp_slow_start(tp, acked); -+} -+ -+ -+static struct tcp_congestion_ops mptcp_wvegas __read_mostly = { -+ .flags = TCP_CONG_RTT_STAMP, -+ .init = mptcp_wvegas_init, -+ .ssthresh = tcp_reno_ssthresh, -+ .cong_avoid = mptcp_wvegas_cong_avoid, -+ .min_cwnd = tcp_reno_min_cwnd, -+ .pkts_acked = mptcp_wvegas_pkts_acked, -+ .set_state = mptcp_wvegas_state, -+ .cwnd_event = mptcp_wvegas_cwnd_event, -+ -+ .owner = THIS_MODULE, -+ .name = "wvegas", -+}; -+ -+static int __init mptcp_wvegas_register(void) -+{ -+ BUILD_BUG_ON(sizeof(struct wvegas) > ICSK_CA_PRIV_SIZE); -+ tcp_register_congestion_control(&mptcp_wvegas); -+ return 0; -+} -+ -+static void __exit mptcp_wvegas_unregister(void) -+{ -+ tcp_unregister_congestion_control(&mptcp_wvegas); -+} -+ -+module_init(mptcp_wvegas_register); -+module_exit(mptcp_wvegas_unregister); -+ -+MODULE_AUTHOR("Yu Cao, Enhuan Dong"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("MPTCP wVegas"); -+MODULE_VERSION("0.1"); diff --git a/target/linux/patches/3.14.43/mtd-rootfs.patch b/target/linux/patches/3.14.43/mtd-rootfs.patch deleted file mode 100644 index 775d5fc80..000000000 --- a/target/linux/patches/3.14.43/mtd-rootfs.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff -Nur linux-3.5.orig//drivers/mtd/mtdpart.c linux-3.5/drivers/mtd/mtdpart.c ---- linux-3.5.orig//drivers/mtd/mtdpart.c 2012-07-21 22:58:29.000000000 +0200 -+++ linux-3.5/drivers/mtd/mtdpart.c 2012-07-31 23:59:07.000000000 +0200 -@@ -30,6 +30,7 @@ - #include - #include - #include -+#include - - #include "mtdcore.h" - -@@ -637,6 +638,14 @@ - if (IS_ERR(slave)) - return PTR_ERR(slave); - -+ if (strcmp(parts[i].name, "rootfs") == 0) { -+ if (ROOT_DEV == 0) { -+ printk(KERN_NOTICE "mtd: partition \"rootfs\" " -+ "set to be root filesystem\n"); -+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, i); -+ } -+ } -+ - mutex_lock(&mtd_partitions_mutex); - list_add(&slave->list, &mtd_partitions); - mutex_unlock(&mtd_partitions_mutex); diff --git a/target/linux/patches/3.14.43/nfsv3-tcp.patch b/target/linux/patches/3.14.43/nfsv3-tcp.patch deleted file mode 100644 index 68ed95b08..000000000 --- a/target/linux/patches/3.14.43/nfsv3-tcp.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-3.13.11.orig/fs/nfs/nfsroot.c linux-3.13.11/fs/nfs/nfsroot.c ---- linux-3.13.11.orig/fs/nfs/nfsroot.c 2014-04-23 01:49:33.000000000 +0200 -+++ linux-3.13.11/fs/nfs/nfsroot.c 2014-05-13 16:09:25.000000000 +0200 -@@ -87,7 +87,7 @@ - #define NFS_ROOT "/tftpboot/%s" - - /* Default NFSROOT mount options. */ --#define NFS_DEF_OPTIONS "vers=2,udp,rsize=4096,wsize=4096" -+#define NFS_DEF_OPTIONS "nfsvers=3,proto=tcp,rsize=4096,wsize=4096" - - /* Parameters passed from the kernel command line */ - static char nfs_root_parms[256] __initdata = ""; diff --git a/target/linux/patches/3.14.43/non-static.patch b/target/linux/patches/3.14.43/non-static.patch deleted file mode 100644 index a967703d0..000000000 --- a/target/linux/patches/3.14.43/non-static.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -Nur linux-2.6.39-rc6.orig/fs/namei.c linux-2.6.39-rc6/fs/namei.c ---- linux-2.6.39-rc6.orig/fs/namei.c 2011-05-04 04:59:13.000000000 +0200 -+++ linux-2.6.39-rc6/fs/namei.c 2011-05-05 11:30:14.000000000 +0200 -@@ -1769,7 +1769,7 @@ - * needs parent already locked. Doesn't follow mounts. - * SMP-safe. - */ --static struct dentry *lookup_hash(struct nameidata *nd) -+struct dentry *lookup_hash(struct nameidata *nd) - { - return __lookup_hash(&nd->last, nd->path.dentry, nd); - } -diff -Nur linux-2.6.39-rc6.orig/fs/splice.c linux-2.6.39-rc6/fs/splice.c ---- linux-2.6.39-rc6.orig/fs/splice.c 2011-05-04 04:59:13.000000000 +0200 -+++ linux-2.6.39-rc6/fs/splice.c 2011-05-05 11:31:04.000000000 +0200 -@@ -1081,7 +1081,7 @@ - /* - * Attempt to initiate a splice from pipe to file. - */ --static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags) - { - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, -@@ -1109,7 +1109,7 @@ - /* - * Attempt to initiate a splice from a file to a pipe. - */ --static long do_splice_to(struct file *in, loff_t *ppos, -+long do_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) - { diff --git a/target/linux/patches/3.14.43/ppc64-missing-zlib.patch b/target/linux/patches/3.14.43/ppc64-missing-zlib.patch deleted file mode 100644 index c6e0616be..000000000 --- a/target/linux/patches/3.14.43/ppc64-missing-zlib.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig ---- linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig 2013-11-01 15:23:09.000000000 +0100 -@@ -17,6 +17,7 @@ - select PPC_NATIVE - select PPC_PCI_CHOICE if EXPERT - select ZLIB_DEFLATE -+ select ZLIB_INFLATE - select PPC_DOORBELL - select HAVE_CONTEXT_TRACKING - select HOTPLUG_CPU if SMP diff --git a/target/linux/patches/3.14.43/regmap-boolean.patch b/target/linux/patches/3.14.43/regmap-boolean.patch deleted file mode 100644 index d73620e4d..000000000 --- a/target/linux/patches/3.14.43/regmap-boolean.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff -Nur linux-3.14.4.orig/drivers/base/regmap/Kconfig linux-3.14.4/drivers/base/regmap/Kconfig ---- linux-3.14.4.orig/drivers/base/regmap/Kconfig 2014-05-13 13:33:14.000000000 +0200 -+++ linux-3.14.4/drivers/base/regmap/Kconfig 2014-05-21 13:46:48.928985115 +0200 -@@ -10,16 +10,16 @@ - bool - - config REGMAP_I2C -- tristate -+ boolean - - config REGMAP_SPI -- tristate -+ boolean - - config REGMAP_SPMI -- tristate -+ boolean - - config REGMAP_MMIO -- tristate -+ boolean - - config REGMAP_IRQ - bool diff --git a/target/linux/patches/3.14.43/relocs.patch b/target/linux/patches/3.14.43/relocs.patch deleted file mode 100644 index 69a7c88a9..000000000 --- a/target/linux/patches/3.14.43/relocs.patch +++ /dev/null @@ -1,2709 +0,0 @@ -diff -Nur linux-3.13.6.orig/arch/x86/tools/relocs.c linux-3.13.6/arch/x86/tools/relocs.c ---- linux-3.13.6.orig/arch/x86/tools/relocs.c 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/arch/x86/tools/relocs.c 2014-03-15 19:39:45.000000000 +0100 -@@ -126,6 +126,7 @@ - - if (err) { - regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); -+ printf("foo: %s\n", sym_regex[i]); - die("%s", errbuf); - } - } -diff -Nur linux-3.13.6.orig/arch/x86/tools/relocs.h linux-3.13.6/arch/x86/tools/relocs.h ---- linux-3.13.6.orig/arch/x86/tools/relocs.h 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/arch/x86/tools/relocs.h 2014-03-15 18:48:40.000000000 +0100 -@@ -9,11 +9,19 @@ - #include - #include - #include -+#ifdef __linux__ - #include - #include - #define USE_BSD - #include -+#else -+#include "elf.h" -+#endif -+#ifdef __APPLE__ -+#include -+#else - #include -+#endif - #include - - void die(char *fmt, ...); -diff -Nur linux-3.13.6.orig/tools/include/elf.h linux-3.13.6/tools/include/elf.h ---- linux-3.13.6.orig/tools/include/elf.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.13.6/tools/include/elf.h 2014-03-15 18:47:36.000000000 +0100 -@@ -0,0 +1,2671 @@ -+#ifndef _ELF_H -+#define _ELF_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#include -+ -+typedef uint16_t Elf32_Half; -+typedef uint16_t Elf64_Half; -+ -+typedef uint32_t Elf32_Word; -+typedef int32_t Elf32_Sword; -+typedef uint32_t Elf64_Word; -+typedef int32_t Elf64_Sword; -+ -+typedef uint64_t Elf32_Xword; -+typedef int64_t Elf32_Sxword; -+typedef uint64_t Elf64_Xword; -+typedef int64_t Elf64_Sxword; -+ -+typedef uint32_t Elf32_Addr; -+typedef uint64_t Elf64_Addr; -+ -+typedef uint32_t Elf32_Off; -+typedef uint64_t Elf64_Off; -+ -+typedef uint16_t Elf32_Section; -+typedef uint16_t Elf64_Section; -+ -+typedef Elf32_Half Elf32_Versym; -+typedef Elf64_Half Elf64_Versym; -+ -+#define EI_NIDENT (16) -+ -+typedef struct { -+ unsigned char e_ident[EI_NIDENT]; -+ Elf32_Half e_type; -+ Elf32_Half e_machine; -+ Elf32_Word e_version; -+ Elf32_Addr e_entry; -+ Elf32_Off e_phoff; -+ Elf32_Off e_shoff; -+ Elf32_Word e_flags; -+ Elf32_Half e_ehsize; -+ Elf32_Half e_phentsize; -+ Elf32_Half e_phnum; -+ Elf32_Half e_shentsize; -+ Elf32_Half e_shnum; -+ Elf32_Half e_shstrndx; -+} Elf32_Ehdr; -+ -+typedef struct { -+ unsigned char e_ident[EI_NIDENT]; -+ Elf64_Half e_type; -+ Elf64_Half e_machine; -+ Elf64_Word e_version; -+ Elf64_Addr e_entry; -+ Elf64_Off e_phoff; -+ Elf64_Off e_shoff; -+ Elf64_Word e_flags; -+ Elf64_Half e_ehsize; -+ Elf64_Half e_phentsize; -+ Elf64_Half e_phnum; -+ Elf64_Half e_shentsize; -+ Elf64_Half e_shnum; -+ Elf64_Half e_shstrndx; -+} Elf64_Ehdr; -+ -+#define EI_MAG0 0 -+#define ELFMAG0 0x7f -+ -+#define EI_MAG1 1 -+#define ELFMAG1 'E' -+ -+#define EI_MAG2 2 -+#define ELFMAG2 'L' -+ -+#define EI_MAG3 3 -+#define ELFMAG3 'F' -+ -+ -+#define ELFMAG "\177ELF" -+#define SELFMAG 4 -+ -+#define EI_CLASS 4 -+#define ELFCLASSNONE 0 -+#define ELFCLASS32 1 -+#define ELFCLASS64 2 -+#define ELFCLASSNUM 3 -+ -+#define EI_DATA 5 -+#define ELFDATANONE 0 -+#define ELFDATA2LSB 1 -+#define ELFDATA2MSB 2 -+#define ELFDATANUM 3 -+ -+#define EI_VERSION 6 -+ -+ -+#define EI_OSABI 7 -+#define ELFOSABI_NONE 0 -+#define ELFOSABI_SYSV 0 -+#define ELFOSABI_HPUX 1 -+#define ELFOSABI_NETBSD 2 -+#define ELFOSABI_LINUX 3 -+#define ELFOSABI_GNU 3 -+#define ELFOSABI_SOLARIS 6 -+#define ELFOSABI_AIX 7 -+#define ELFOSABI_IRIX 8 -+#define ELFOSABI_FREEBSD 9 -+#define ELFOSABI_TRU64 10 -+#define ELFOSABI_MODESTO 11 -+#define ELFOSABI_OPENBSD 12 -+#define ELFOSABI_ARM 97 -+#define ELFOSABI_STANDALONE 255 -+ -+#define EI_ABIVERSION 8 -+ -+#define EI_PAD 9 -+ -+ -+ -+#define ET_NONE 0 -+#define ET_REL 1 -+#define ET_EXEC 2 -+#define ET_DYN 3 -+#define ET_CORE 4 -+#define ET_NUM 5 -+#define ET_LOOS 0xfe00 -+#define ET_HIOS 0xfeff -+#define ET_LOPROC 0xff00 -+#define ET_HIPROC 0xffff -+ -+ -+ -+#define EM_NONE 0 -+#define EM_M32 1 -+#define EM_SPARC 2 -+#define EM_386 3 -+#define EM_68K 4 -+#define EM_88K 5 -+#define EM_860 7 -+#define EM_MIPS 8 -+#define EM_S370 9 -+#define EM_MIPS_RS3_LE 10 -+ -+#define EM_PARISC 15 -+#define EM_VPP500 17 -+#define EM_SPARC32PLUS 18 -+#define EM_960 19 -+#define EM_PPC 20 -+#define EM_PPC64 21 -+#define EM_S390 22 -+ -+#define EM_V800 36 -+#define EM_FR20 37 -+#define EM_RH32 38 -+#define EM_RCE 39 -+#define EM_ARM 40 -+#define EM_FAKE_ALPHA 41 -+#define EM_SH 42 -+#define EM_SPARCV9 43 -+#define EM_TRICORE 44 -+#define EM_ARC 45 -+#define EM_H8_300 46 -+#define EM_H8_300H 47 -+#define EM_H8S 48 -+#define EM_H8_500 49 -+#define EM_IA_64 50 -+#define EM_MIPS_X 51 -+#define EM_COLDFIRE 52 -+#define EM_68HC12 53 -+#define EM_MMA 54 -+#define EM_PCP 55 -+#define EM_NCPU 56 -+#define EM_NDR1 57 -+#define EM_STARCORE 58 -+#define EM_ME16 59 -+#define EM_ST100 60 -+#define EM_TINYJ 61 -+#define EM_X86_64 62 -+#define EM_PDSP 63 -+ -+#define EM_FX66 66 -+#define EM_ST9PLUS 67 -+#define EM_ST7 68 -+#define EM_68HC16 69 -+#define EM_68HC11 70 -+#define EM_68HC08 71 -+#define EM_68HC05 72 -+#define EM_SVX 73 -+#define EM_ST19 74 -+#define EM_VAX 75 -+#define EM_CRIS 76 -+#define EM_JAVELIN 77 -+#define EM_FIREPATH 78 -+#define EM_ZSP 79 -+#define EM_MMIX 80 -+#define EM_HUANY 81 -+#define EM_PRISM 82 -+#define EM_AVR 83 -+#define EM_FR30 84 -+#define EM_D10V 85 -+#define EM_D30V 86 -+#define EM_V850 87 -+#define EM_M32R 88 -+#define EM_MN10300 89 -+#define EM_MN10200 90 -+#define EM_PJ 91 -+#define EM_OPENRISC 92 -+#define EM_ARC_A5 93 -+#define EM_XTENSA 94 -+#define EM_AARCH64 183 -+#define EM_TILEPRO 188 -+#define EM_MICROBLAZE 189 -+#define EM_TILEGX 191 -+#define EM_NUM 192 -+#define EM_ALPHA 0x9026 -+ -+#define EV_NONE 0 -+#define EV_CURRENT 1 -+#define EV_NUM 2 -+ -+typedef struct { -+ Elf32_Word sh_name; -+ Elf32_Word sh_type; -+ Elf32_Word sh_flags; -+ Elf32_Addr sh_addr; -+ Elf32_Off sh_offset; -+ Elf32_Word sh_size; -+ Elf32_Word sh_link; -+ Elf32_Word sh_info; -+ Elf32_Word sh_addralign; -+ Elf32_Word sh_entsize; -+} Elf32_Shdr; -+ -+typedef struct { -+ Elf64_Word sh_name; -+ Elf64_Word sh_type; -+ Elf64_Xword sh_flags; -+ Elf64_Addr sh_addr; -+ Elf64_Off sh_offset; -+ Elf64_Xword sh_size; -+ Elf64_Word sh_link; -+ Elf64_Word sh_info; -+ Elf64_Xword sh_addralign; -+ Elf64_Xword sh_entsize; -+} Elf64_Shdr; -+ -+ -+ -+#define SHN_UNDEF 0 -+#define SHN_LORESERVE 0xff00 -+#define SHN_LOPROC 0xff00 -+#define SHN_BEFORE 0xff00 -+ -+#define SHN_AFTER 0xff01 -+ -+#define SHN_HIPROC 0xff1f -+#define SHN_LOOS 0xff20 -+#define SHN_HIOS 0xff3f -+#define SHN_ABS 0xfff1 -+#define SHN_COMMON 0xfff2 -+#define SHN_XINDEX 0xffff -+#define SHN_HIRESERVE 0xffff -+ -+ -+ -+#define SHT_NULL 0 -+#define SHT_PROGBITS 1 -+#define SHT_SYMTAB 2 -+#define SHT_STRTAB 3 -+#define SHT_RELA 4 -+#define SHT_HASH 5 -+#define SHT_DYNAMIC 6 -+#define SHT_NOTE 7 -+#define SHT_NOBITS 8 -+#define SHT_REL 9 -+#define SHT_SHLIB 10 -+#define SHT_DYNSYM 11 -+#define SHT_INIT_ARRAY 14 -+#define SHT_FINI_ARRAY 15 -+#define SHT_PREINIT_ARRAY 16 -+#define SHT_GROUP 17 -+#define SHT_SYMTAB_SHNDX 18 -+#define SHT_NUM 19 -+#define SHT_LOOS 0x60000000 -+#define SHT_GNU_ATTRIBUTES 0x6ffffff5 -+#define SHT_GNU_HASH 0x6ffffff6 -+#define SHT_GNU_LIBLIST 0x6ffffff7 -+#define SHT_CHECKSUM 0x6ffffff8 -+#define SHT_LOSUNW 0x6ffffffa -+#define SHT_SUNW_move 0x6ffffffa -+#define SHT_SUNW_COMDAT 0x6ffffffb -+#define SHT_SUNW_syminfo 0x6ffffffc -+#define SHT_GNU_verdef 0x6ffffffd -+#define SHT_GNU_verneed 0x6ffffffe -+#define SHT_GNU_versym 0x6fffffff -+#define SHT_HISUNW 0x6fffffff -+#define SHT_HIOS 0x6fffffff -+#define SHT_LOPROC 0x70000000 -+#define SHT_HIPROC 0x7fffffff -+#define SHT_LOUSER 0x80000000 -+#define SHT_HIUSER 0x8fffffff -+ -+#define SHF_WRITE (1 << 0) -+#define SHF_ALLOC (1 << 1) -+#define SHF_EXECINSTR (1 << 2) -+#define SHF_MERGE (1 << 4) -+#define SHF_STRINGS (1 << 5) -+#define SHF_INFO_LINK (1 << 6) -+#define SHF_LINK_ORDER (1 << 7) -+#define SHF_OS_NONCONFORMING (1 << 8) -+ -+#define SHF_GROUP (1 << 9) -+#define SHF_TLS (1 << 10) -+#define SHF_MASKOS 0x0ff00000 -+#define SHF_MASKPROC 0xf0000000 -+#define SHF_ORDERED (1 << 30) -+#define SHF_EXCLUDE (1 << 31) -+ -+#define GRP_COMDAT 0x1 -+ -+typedef struct { -+ Elf32_Word st_name; -+ Elf32_Addr st_value; -+ Elf32_Word st_size; -+ unsigned char st_info; -+ unsigned char st_other; -+ Elf32_Section st_shndx; -+} Elf32_Sym; -+ -+typedef struct { -+ Elf64_Word st_name; -+ unsigned char st_info; -+ unsigned char st_other; -+ Elf64_Section st_shndx; -+ Elf64_Addr st_value; -+ Elf64_Xword st_size; -+} Elf64_Sym; -+ -+typedef struct { -+ Elf32_Half si_boundto; -+ Elf32_Half si_flags; -+} Elf32_Syminfo; -+ -+typedef struct { -+ Elf64_Half si_boundto; -+ Elf64_Half si_flags; -+} Elf64_Syminfo; -+ -+#define SYMINFO_BT_SELF 0xffff -+#define SYMINFO_BT_PARENT 0xfffe -+#define SYMINFO_BT_LOWRESERVE 0xff00 -+ -+#define SYMINFO_FLG_DIRECT 0x0001 -+#define SYMINFO_FLG_PASSTHRU 0x0002 -+#define SYMINFO_FLG_COPY 0x0004 -+#define SYMINFO_FLG_LAZYLOAD 0x0008 -+ -+#define SYMINFO_NONE 0 -+#define SYMINFO_CURRENT 1 -+#define SYMINFO_NUM 2 -+ -+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -+#define ELF32_ST_TYPE(val) ((val) & 0xf) -+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -+ -+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) -+ -+#define STB_LOCAL 0 -+#define STB_GLOBAL 1 -+#define STB_WEAK 2 -+#define STB_NUM 3 -+#define STB_LOOS 10 -+#define STB_GNU_UNIQUE 10 -+#define STB_HIOS 12 -+#define STB_LOPROC 13 -+#define STB_HIPROC 15 -+ -+#define STT_NOTYPE 0 -+#define STT_OBJECT 1 -+#define STT_FUNC 2 -+#define STT_SECTION 3 -+#define STT_FILE 4 -+#define STT_COMMON 5 -+#define STT_TLS 6 -+#define STT_NUM 7 -+#define STT_LOOS 10 -+#define STT_GNU_IFUNC 10 -+#define STT_HIOS 12 -+#define STT_LOPROC 13 -+#define STT_HIPROC 15 -+ -+#define STN_UNDEF 0 -+ -+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) -+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) -+ -+#define STV_DEFAULT 0 -+#define STV_INTERNAL 1 -+#define STV_HIDDEN 2 -+#define STV_PROTECTED 3 -+ -+ -+ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; -+ Elf32_Word r_info; -+} Elf32_Rel; -+ -+typedef struct { -+ Elf64_Addr r_offset; -+ Elf64_Xword r_info; -+} Elf64_Rel; -+ -+ -+ -+typedef struct { -+ Elf32_Addr r_offset; -+ Elf32_Word r_info; -+ Elf32_Sword r_addend; -+} Elf32_Rela; -+ -+typedef struct { -+ Elf64_Addr r_offset; -+ Elf64_Xword r_info; -+ Elf64_Sxword r_addend; -+} Elf64_Rela; -+ -+ -+ -+#define ELF32_R_SYM(val) ((val) >> 8) -+#define ELF32_R_TYPE(val) ((val) & 0xff) -+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) -+ -+#define ELF64_R_SYM(i) ((i) >> 32) -+#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) -+ -+ -+ -+typedef struct { -+ Elf32_Word p_type; -+ Elf32_Off p_offset; -+ Elf32_Addr p_vaddr; -+ Elf32_Addr p_paddr; -+ Elf32_Word p_filesz; -+ Elf32_Word p_memsz; -+ Elf32_Word p_flags; -+ Elf32_Word p_align; -+} Elf32_Phdr; -+ -+typedef struct { -+ Elf64_Word p_type; -+ Elf64_Word p_flags; -+ Elf64_Off p_offset; -+ Elf64_Addr p_vaddr; -+ Elf64_Addr p_paddr; -+ Elf64_Xword p_filesz; -+ Elf64_Xword p_memsz; -+ Elf64_Xword p_align; -+} Elf64_Phdr; -+ -+ -+ -+#define PT_NULL 0 -+#define PT_LOAD 1 -+#define PT_DYNAMIC 2 -+#define PT_INTERP 3 -+#define PT_NOTE 4 -+#define PT_SHLIB 5 -+#define PT_PHDR 6 -+#define PT_TLS 7 -+#define PT_NUM 8 -+#define PT_LOOS 0x60000000 -+#define PT_GNU_EH_FRAME 0x6474e550 -+#define PT_GNU_STACK 0x6474e551 -+#define PT_GNU_RELRO 0x6474e552 -+#define PT_LOSUNW 0x6ffffffa -+#define PT_SUNWBSS 0x6ffffffa -+#define PT_SUNWSTACK 0x6ffffffb -+#define PT_HISUNW 0x6fffffff -+#define PT_HIOS 0x6fffffff -+#define PT_LOPROC 0x70000000 -+#define PT_HIPROC 0x7fffffff -+ -+ -+#define PN_XNUM 0xffff -+ -+ -+#define PF_X (1 << 0) -+#define PF_W (1 << 1) -+#define PF_R (1 << 2) -+#define PF_MASKOS 0x0ff00000 -+#define PF_MASKPROC 0xf0000000 -+ -+ -+ -+#define NT_PRSTATUS 1 -+#define NT_FPREGSET 2 -+#define NT_PRPSINFO 3 -+#define NT_PRXREG 4 -+#define NT_TASKSTRUCT 4 -+#define NT_PLATFORM 5 -+#define NT_AUXV 6 -+#define NT_GWINDOWS 7 -+#define NT_ASRS 8 -+#define NT_PSTATUS 10 -+#define NT_PSINFO 13 -+#define NT_PRCRED 14 -+#define NT_UTSNAME 15 -+#define NT_LWPSTATUS 16 -+#define NT_LWPSINFO 17 -+#define NT_PRFPXREG 20 -+#define NT_SIGINFO 0x53494749 -+#define NT_FILE 0x46494c45 -+#define NT_PRXFPREG 0x46e62b7f -+#define NT_PPC_VMX 0x100 -+#define NT_PPC_SPE 0x101 -+#define NT_PPC_VSX 0x102 -+#define NT_386_TLS 0x200 -+#define NT_386_IOPERM 0x201 -+#define NT_X86_XSTATE 0x202 -+#define NT_S390_HIGH_GPRS 0x300 -+#define NT_S390_TIMER 0x301 -+#define NT_S390_TODCMP 0x302 -+#define NT_S390_TODPREG 0x303 -+#define NT_S390_CTRS 0x304 -+#define NT_S390_PREFIX 0x305 -+#define NT_S390_LAST_BREAK 0x306 -+#define NT_S390_SYSTEM_CALL 0x307 -+#define NT_S390_TDB 0x308 -+#define NT_ARM_VFP 0x400 -+#define NT_ARM_TLS 0x401 -+#define NT_ARM_HW_BREAK 0x402 -+#define NT_ARM_HW_WATCH 0x403 -+#define NT_METAG_CBUF 0x500 -+#define NT_METAG_RPIPE 0x501 -+#define NT_METAG_TLS 0x502 -+#define NT_VERSION 1 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Sword d_tag; -+ union { -+ Elf32_Word d_val; -+ Elf32_Addr d_ptr; -+ } d_un; -+} Elf32_Dyn; -+ -+typedef struct { -+ Elf64_Sxword d_tag; -+ union { -+ Elf64_Xword d_val; -+ Elf64_Addr d_ptr; -+ } d_un; -+} Elf64_Dyn; -+ -+ -+ -+#define DT_NULL 0 -+#define DT_NEEDED 1 -+#define DT_PLTRELSZ 2 -+#define DT_PLTGOT 3 -+#define DT_HASH 4 -+#define DT_STRTAB 5 -+#define DT_SYMTAB 6 -+#define DT_RELA 7 -+#define DT_RELASZ 8 -+#define DT_RELAENT 9 -+#define DT_STRSZ 10 -+#define DT_SYMENT 11 -+#define DT_INIT 12 -+#define DT_FINI 13 -+#define DT_SONAME 14 -+#define DT_RPATH 15 -+#define DT_SYMBOLIC 16 -+#define DT_REL 17 -+#define DT_RELSZ 18 -+#define DT_RELENT 19 -+#define DT_PLTREL 20 -+#define DT_DEBUG 21 -+#define DT_TEXTREL 22 -+#define DT_JMPREL 23 -+#define DT_BIND_NOW 24 -+#define DT_INIT_ARRAY 25 -+#define DT_FINI_ARRAY 26 -+#define DT_INIT_ARRAYSZ 27 -+#define DT_FINI_ARRAYSZ 28 -+#define DT_RUNPATH 29 -+#define DT_FLAGS 30 -+#define DT_ENCODING 32 -+#define DT_PREINIT_ARRAY 32 -+#define DT_PREINIT_ARRAYSZ 33 -+#define DT_NUM 34 -+#define DT_LOOS 0x6000000d -+#define DT_HIOS 0x6ffff000 -+#define DT_LOPROC 0x70000000 -+#define DT_HIPROC 0x7fffffff -+#define DT_PROCNUM DT_MIPS_NUM -+ -+#define DT_VALRNGLO 0x6ffffd00 -+#define DT_GNU_PRELINKED 0x6ffffdf5 -+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 -+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 -+#define DT_CHECKSUM 0x6ffffdf8 -+#define DT_PLTPADSZ 0x6ffffdf9 -+#define DT_MOVEENT 0x6ffffdfa -+#define DT_MOVESZ 0x6ffffdfb -+#define DT_FEATURE_1 0x6ffffdfc -+#define DT_POSFLAG_1 0x6ffffdfd -+ -+#define DT_SYMINSZ 0x6ffffdfe -+#define DT_SYMINENT 0x6ffffdff -+#define DT_VALRNGHI 0x6ffffdff -+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) -+#define DT_VALNUM 12 -+ -+#define DT_ADDRRNGLO 0x6ffffe00 -+#define DT_GNU_HASH 0x6ffffef5 -+#define DT_TLSDESC_PLT 0x6ffffef6 -+#define DT_TLSDESC_GOT 0x6ffffef7 -+#define DT_GNU_CONFLICT 0x6ffffef8 -+#define DT_GNU_LIBLIST 0x6ffffef9 -+#define DT_CONFIG 0x6ffffefa -+#define DT_DEPAUDIT 0x6ffffefb -+#define DT_AUDIT 0x6ffffefc -+#define DT_PLTPAD 0x6ffffefd -+#define DT_MOVETAB 0x6ffffefe -+#define DT_SYMINFO 0x6ffffeff -+#define DT_ADDRRNGHI 0x6ffffeff -+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) -+#define DT_ADDRNUM 11 -+ -+ -+ -+#define DT_VERSYM 0x6ffffff0 -+ -+#define DT_RELACOUNT 0x6ffffff9 -+#define DT_RELCOUNT 0x6ffffffa -+ -+ -+#define DT_FLAGS_1 0x6ffffffb -+#define DT_VERDEF 0x6ffffffc -+ -+#define DT_VERDEFNUM 0x6ffffffd -+#define DT_VERNEED 0x6ffffffe -+ -+#define DT_VERNEEDNUM 0x6fffffff -+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) -+#define DT_VERSIONTAGNUM 16 -+ -+ -+ -+#define DT_AUXILIARY 0x7ffffffd -+#define DT_FILTER 0x7fffffff -+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -+#define DT_EXTRANUM 3 -+ -+ -+#define DF_ORIGIN 0x00000001 -+#define DF_SYMBOLIC 0x00000002 -+#define DF_TEXTREL 0x00000004 -+#define DF_BIND_NOW 0x00000008 -+#define DF_STATIC_TLS 0x00000010 -+ -+ -+ -+#define DF_1_NOW 0x00000001 -+#define DF_1_GLOBAL 0x00000002 -+#define DF_1_GROUP 0x00000004 -+#define DF_1_NODELETE 0x00000008 -+#define DF_1_LOADFLTR 0x00000010 -+#define DF_1_INITFIRST 0x00000020 -+#define DF_1_NOOPEN 0x00000040 -+#define DF_1_ORIGIN 0x00000080 -+#define DF_1_DIRECT 0x00000100 -+#define DF_1_TRANS 0x00000200 -+#define DF_1_INTERPOSE 0x00000400 -+#define DF_1_NODEFLIB 0x00000800 -+#define DF_1_NODUMP 0x00001000 -+#define DF_1_CONFALT 0x00002000 -+#define DF_1_ENDFILTEE 0x00004000 -+#define DF_1_DISPRELDNE 0x00008000 -+#define DF_1_DISPRELPND 0x00010000 -+#define DF_1_NODIRECT 0x00020000 -+#define DF_1_IGNMULDEF 0x00040000 -+#define DF_1_NOKSYMS 0x00080000 -+#define DF_1_NOHDR 0x00100000 -+#define DF_1_EDITED 0x00200000 -+#define DF_1_NORELOC 0x00400000 -+#define DF_1_SYMINTPOSE 0x00800000 -+#define DF_1_GLOBAUDIT 0x01000000 -+#define DF_1_SINGLETON 0x02000000 -+ -+#define DTF_1_PARINIT 0x00000001 -+#define DTF_1_CONFEXP 0x00000002 -+ -+ -+#define DF_P1_LAZYLOAD 0x00000001 -+#define DF_P1_GROUPPERM 0x00000002 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Half vd_version; -+ Elf32_Half vd_flags; -+ Elf32_Half vd_ndx; -+ Elf32_Half vd_cnt; -+ Elf32_Word vd_hash; -+ Elf32_Word vd_aux; -+ Elf32_Word vd_next; -+} Elf32_Verdef; -+ -+typedef struct { -+ Elf64_Half vd_version; -+ Elf64_Half vd_flags; -+ Elf64_Half vd_ndx; -+ Elf64_Half vd_cnt; -+ Elf64_Word vd_hash; -+ Elf64_Word vd_aux; -+ Elf64_Word vd_next; -+} Elf64_Verdef; -+ -+ -+ -+#define VER_DEF_NONE 0 -+#define VER_DEF_CURRENT 1 -+#define VER_DEF_NUM 2 -+ -+ -+#define VER_FLG_BASE 0x1 -+#define VER_FLG_WEAK 0x2 -+ -+ -+#define VER_NDX_LOCAL 0 -+#define VER_NDX_GLOBAL 1 -+#define VER_NDX_LORESERVE 0xff00 -+#define VER_NDX_ELIMINATE 0xff01 -+ -+ -+ -+typedef struct { -+ Elf32_Word vda_name; -+ Elf32_Word vda_next; -+} Elf32_Verdaux; -+ -+typedef struct { -+ Elf64_Word vda_name; -+ Elf64_Word vda_next; -+} Elf64_Verdaux; -+ -+ -+ -+ -+typedef struct { -+ Elf32_Half vn_version; -+ Elf32_Half vn_cnt; -+ Elf32_Word vn_file; -+ Elf32_Word vn_aux; -+ Elf32_Word vn_next; -+} Elf32_Verneed; -+ -+typedef struct { -+ Elf64_Half vn_version; -+ Elf64_Half vn_cnt; -+ Elf64_Word vn_file; -+ Elf64_Word vn_aux; -+ Elf64_Word vn_next; -+} Elf64_Verneed; -+ -+ -+ -+#define VER_NEED_NONE 0 -+#define VER_NEED_CURRENT 1 -+#define VER_NEED_NUM 2 -+ -+ -+ -+typedef struct { -+ Elf32_Word vna_hash; -+ Elf32_Half vna_flags; -+ Elf32_Half vna_other; -+ Elf32_Word vna_name; -+ Elf32_Word vna_next; -+} Elf32_Vernaux; -+ -+typedef struct { -+ Elf64_Word vna_hash; -+ Elf64_Half vna_flags; -+ Elf64_Half vna_other; -+ Elf64_Word vna_name; -+ Elf64_Word vna_next; -+} Elf64_Vernaux; -+ -+ -+ -+#define VER_FLG_WEAK 0x2 -+ -+ -+ -+typedef struct { -+ uint32_t a_type; -+ union { -+ uint32_t a_val; -+ } a_un; -+} Elf32_auxv_t; -+ -+typedef struct { -+ uint64_t a_type; -+ union { -+ uint64_t a_val; -+ } a_un; -+} Elf64_auxv_t; -+ -+ -+ -+#define AT_NULL 0 -+#define AT_IGNORE 1 -+#define AT_EXECFD 2 -+#define AT_PHDR 3 -+#define AT_PHENT 4 -+#define AT_PHNUM 5 -+#define AT_PAGESZ 6 -+#define AT_BASE 7 -+#define AT_FLAGS 8 -+#define AT_ENTRY 9 -+#define AT_NOTELF 10 -+#define AT_UID 11 -+#define AT_EUID 12 -+#define AT_GID 13 -+#define AT_EGID 14 -+#define AT_CLKTCK 17 -+ -+ -+#define AT_PLATFORM 15 -+#define AT_HWCAP 16 -+ -+ -+ -+ -+#define AT_FPUCW 18 -+ -+ -+#define AT_DCACHEBSIZE 19 -+#define AT_ICACHEBSIZE 20 -+#define AT_UCACHEBSIZE 21 -+ -+ -+ -+#define AT_IGNOREPPC 22 -+ -+#define AT_SECURE 23 -+ -+#define AT_BASE_PLATFORM 24 -+ -+#define AT_RANDOM 25 -+ -+#define AT_HWCAP2 26 -+ -+#define AT_EXECFN 31 -+ -+ -+ -+#define AT_SYSINFO 32 -+#define AT_SYSINFO_EHDR 33 -+ -+ -+ -+#define AT_L1I_CACHESHAPE 34 -+#define AT_L1D_CACHESHAPE 35 -+#define AT_L2_CACHESHAPE 36 -+#define AT_L3_CACHESHAPE 37 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Word n_namesz; -+ Elf32_Word n_descsz; -+ Elf32_Word n_type; -+} Elf32_Nhdr; -+ -+typedef struct { -+ Elf64_Word n_namesz; -+ Elf64_Word n_descsz; -+ Elf64_Word n_type; -+} Elf64_Nhdr; -+ -+ -+ -+ -+#define ELF_NOTE_SOLARIS "SUNW Solaris" -+ -+ -+#define ELF_NOTE_GNU "GNU" -+ -+ -+ -+ -+ -+#define ELF_NOTE_PAGESIZE_HINT 1 -+ -+ -+#define NT_GNU_ABI_TAG 1 -+#define ELF_NOTE_ABI NT_GNU_ABI_TAG -+ -+ -+ -+#define ELF_NOTE_OS_LINUX 0 -+#define ELF_NOTE_OS_GNU 1 -+#define ELF_NOTE_OS_SOLARIS2 2 -+#define ELF_NOTE_OS_FREEBSD 3 -+ -+#define NT_GNU_BUILD_ID 3 -+#define NT_GNU_GOLD_VERSION 4 -+ -+ -+ -+typedef struct { -+ Elf32_Xword m_value; -+ Elf32_Word m_info; -+ Elf32_Word m_poffset; -+ Elf32_Half m_repeat; -+ Elf32_Half m_stride; -+} Elf32_Move; -+ -+typedef struct { -+ Elf64_Xword m_value; -+ Elf64_Xword m_info; -+ Elf64_Xword m_poffset; -+ Elf64_Half m_repeat; -+ Elf64_Half m_stride; -+} Elf64_Move; -+ -+ -+#define ELF32_M_SYM(info) ((info) >> 8) -+#define ELF32_M_SIZE(info) ((unsigned char) (info)) -+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) -+ -+#define ELF64_M_SYM(info) ELF32_M_SYM (info) -+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) -+ -+#define EF_CPU32 0x00810000 -+ -+#define R_68K_NONE 0 -+#define R_68K_32 1 -+#define R_68K_16 2 -+#define R_68K_8 3 -+#define R_68K_PC32 4 -+#define R_68K_PC16 5 -+#define R_68K_PC8 6 -+#define R_68K_GOT32 7 -+#define R_68K_GOT16 8 -+#define R_68K_GOT8 9 -+#define R_68K_GOT32O 10 -+#define R_68K_GOT16O 11 -+#define R_68K_GOT8O 12 -+#define R_68K_PLT32 13 -+#define R_68K_PLT16 14 -+#define R_68K_PLT8 15 -+#define R_68K_PLT32O 16 -+#define R_68K_PLT16O 17 -+#define R_68K_PLT8O 18 -+#define R_68K_COPY 19 -+#define R_68K_GLOB_DAT 20 -+#define R_68K_JMP_SLOT 21 -+#define R_68K_RELATIVE 22 -+#define R_68K_NUM 23 -+ -+#define R_386_NONE 0 -+#define R_386_32 1 -+#define R_386_PC32 2 -+#define R_386_GOT32 3 -+#define R_386_PLT32 4 -+#define R_386_COPY 5 -+#define R_386_GLOB_DAT 6 -+#define R_386_JMP_SLOT 7 -+#define R_386_RELATIVE 8 -+#define R_386_GOTOFF 9 -+#define R_386_GOTPC 10 -+#define R_386_32PLT 11 -+#define R_386_TLS_TPOFF 14 -+#define R_386_TLS_IE 15 -+#define R_386_TLS_GOTIE 16 -+#define R_386_TLS_LE 17 -+#define R_386_TLS_GD 18 -+#define R_386_TLS_LDM 19 -+#define R_386_16 20 -+#define R_386_PC16 21 -+#define R_386_8 22 -+#define R_386_PC8 23 -+#define R_386_TLS_GD_32 24 -+#define R_386_TLS_GD_PUSH 25 -+#define R_386_TLS_GD_CALL 26 -+#define R_386_TLS_GD_POP 27 -+#define R_386_TLS_LDM_32 28 -+#define R_386_TLS_LDM_PUSH 29 -+#define R_386_TLS_LDM_CALL 30 -+#define R_386_TLS_LDM_POP 31 -+#define R_386_TLS_LDO_32 32 -+#define R_386_TLS_IE_32 33 -+#define R_386_TLS_LE_32 34 -+#define R_386_TLS_DTPMOD32 35 -+#define R_386_TLS_DTPOFF32 36 -+#define R_386_TLS_TPOFF32 37 -+#define R_386_SIZE32 38 -+#define R_386_TLS_GOTDESC 39 -+#define R_386_TLS_DESC_CALL 40 -+#define R_386_TLS_DESC 41 -+#define R_386_IRELATIVE 42 -+#define R_386_NUM 43 -+ -+ -+ -+ -+ -+#define STT_SPARC_REGISTER 13 -+ -+ -+ -+#define EF_SPARCV9_MM 3 -+#define EF_SPARCV9_TSO 0 -+#define EF_SPARCV9_PSO 1 -+#define EF_SPARCV9_RMO 2 -+#define EF_SPARC_LEDATA 0x800000 -+#define EF_SPARC_EXT_MASK 0xFFFF00 -+#define EF_SPARC_32PLUS 0x000100 -+#define EF_SPARC_SUN_US1 0x000200 -+#define EF_SPARC_HAL_R1 0x000400 -+#define EF_SPARC_SUN_US3 0x000800 -+ -+ -+ -+#define R_SPARC_NONE 0 -+#define R_SPARC_8 1 -+#define R_SPARC_16 2 -+#define R_SPARC_32 3 -+#define R_SPARC_DISP8 4 -+#define R_SPARC_DISP16 5 -+#define R_SPARC_DISP32 6 -+#define R_SPARC_WDISP30 7 -+#define R_SPARC_WDISP22 8 -+#define R_SPARC_HI22 9 -+#define R_SPARC_22 10 -+#define R_SPARC_13 11 -+#define R_SPARC_LO10 12 -+#define R_SPARC_GOT10 13 -+#define R_SPARC_GOT13 14 -+#define R_SPARC_GOT22 15 -+#define R_SPARC_PC10 16 -+#define R_SPARC_PC22 17 -+#define R_SPARC_WPLT30 18 -+#define R_SPARC_COPY 19 -+#define R_SPARC_GLOB_DAT 20 -+#define R_SPARC_JMP_SLOT 21 -+#define R_SPARC_RELATIVE 22 -+#define R_SPARC_UA32 23 -+ -+ -+ -+#define R_SPARC_PLT32 24 -+#define R_SPARC_HIPLT22 25 -+#define R_SPARC_LOPLT10 26 -+#define R_SPARC_PCPLT32 27 -+#define R_SPARC_PCPLT22 28 -+#define R_SPARC_PCPLT10 29 -+#define R_SPARC_10 30 -+#define R_SPARC_11 31 -+#define R_SPARC_64 32 -+#define R_SPARC_OLO10 33 -+#define R_SPARC_HH22 34 -+#define R_SPARC_HM10 35 -+#define R_SPARC_LM22 36 -+#define R_SPARC_PC_HH22 37 -+#define R_SPARC_PC_HM10 38 -+#define R_SPARC_PC_LM22 39 -+#define R_SPARC_WDISP16 40 -+#define R_SPARC_WDISP19 41 -+#define R_SPARC_GLOB_JMP 42 -+#define R_SPARC_7 43 -+#define R_SPARC_5 44 -+#define R_SPARC_6 45 -+#define R_SPARC_DISP64 46 -+#define R_SPARC_PLT64 47 -+#define R_SPARC_HIX22 48 -+#define R_SPARC_LOX10 49 -+#define R_SPARC_H44 50 -+#define R_SPARC_M44 51 -+#define R_SPARC_L44 52 -+#define R_SPARC_REGISTER 53 -+#define R_SPARC_UA64 54 -+#define R_SPARC_UA16 55 -+#define R_SPARC_TLS_GD_HI22 56 -+#define R_SPARC_TLS_GD_LO10 57 -+#define R_SPARC_TLS_GD_ADD 58 -+#define R_SPARC_TLS_GD_CALL 59 -+#define R_SPARC_TLS_LDM_HI22 60 -+#define R_SPARC_TLS_LDM_LO10 61 -+#define R_SPARC_TLS_LDM_ADD 62 -+#define R_SPARC_TLS_LDM_CALL 63 -+#define R_SPARC_TLS_LDO_HIX22 64 -+#define R_SPARC_TLS_LDO_LOX10 65 -+#define R_SPARC_TLS_LDO_ADD 66 -+#define R_SPARC_TLS_IE_HI22 67 -+#define R_SPARC_TLS_IE_LO10 68 -+#define R_SPARC_TLS_IE_LD 69 -+#define R_SPARC_TLS_IE_LDX 70 -+#define R_SPARC_TLS_IE_ADD 71 -+#define R_SPARC_TLS_LE_HIX22 72 -+#define R_SPARC_TLS_LE_LOX10 73 -+#define R_SPARC_TLS_DTPMOD32 74 -+#define R_SPARC_TLS_DTPMOD64 75 -+#define R_SPARC_TLS_DTPOFF32 76 -+#define R_SPARC_TLS_DTPOFF64 77 -+#define R_SPARC_TLS_TPOFF32 78 -+#define R_SPARC_TLS_TPOFF64 79 -+#define R_SPARC_GOTDATA_HIX22 80 -+#define R_SPARC_GOTDATA_LOX10 81 -+#define R_SPARC_GOTDATA_OP_HIX22 82 -+#define R_SPARC_GOTDATA_OP_LOX10 83 -+#define R_SPARC_GOTDATA_OP 84 -+#define R_SPARC_H34 85 -+#define R_SPARC_SIZE32 86 -+#define R_SPARC_SIZE64 87 -+#define R_SPARC_GNU_VTINHERIT 250 -+#define R_SPARC_GNU_VTENTRY 251 -+#define R_SPARC_REV32 252 -+ -+#define R_SPARC_NUM 253 -+ -+ -+ -+#define DT_SPARC_REGISTER 0x70000001 -+#define DT_SPARC_NUM 2 -+ -+ -+#define EF_MIPS_NOREORDER 1 -+#define EF_MIPS_PIC 2 -+#define EF_MIPS_CPIC 4 -+#define EF_MIPS_XGOT 8 -+#define EF_MIPS_64BIT_WHIRL 16 -+#define EF_MIPS_ABI2 32 -+#define EF_MIPS_ABI_ON32 64 -+#define EF_MIPS_ARCH 0xf0000000 -+ -+ -+ -+#define EF_MIPS_ARCH_1 0x00000000 -+#define EF_MIPS_ARCH_2 0x10000000 -+#define EF_MIPS_ARCH_3 0x20000000 -+#define EF_MIPS_ARCH_4 0x30000000 -+#define EF_MIPS_ARCH_5 0x40000000 -+#define EF_MIPS_ARCH_32 0x50000000 -+#define EF_MIPS_ARCH_64 0x60000000 -+#define EF_MIPS_ARCH_32R2 0x70000000 -+#define EF_MIPS_ARCH_64R2 0x80000000 -+ -+ -+#define E_MIPS_ARCH_1 0x00000000 -+#define E_MIPS_ARCH_2 0x10000000 -+#define E_MIPS_ARCH_3 0x20000000 -+#define E_MIPS_ARCH_4 0x30000000 -+#define E_MIPS_ARCH_5 0x40000000 -+#define E_MIPS_ARCH_32 0x50000000 -+#define E_MIPS_ARCH_64 0x60000000 -+ -+ -+ -+#define SHN_MIPS_ACOMMON 0xff00 -+#define SHN_MIPS_TEXT 0xff01 -+#define SHN_MIPS_DATA 0xff02 -+#define SHN_MIPS_SCOMMON 0xff03 -+#define SHN_MIPS_SUNDEFINED 0xff04 -+ -+ -+ -+#define SHT_MIPS_LIBLIST 0x70000000 -+#define SHT_MIPS_MSYM 0x70000001 -+#define SHT_MIPS_CONFLICT 0x70000002 -+#define SHT_MIPS_GPTAB 0x70000003 -+#define SHT_MIPS_UCODE 0x70000004 -+#define SHT_MIPS_DEBUG 0x70000005 -+#define SHT_MIPS_REGINFO 0x70000006 -+#define SHT_MIPS_PACKAGE 0x70000007 -+#define SHT_MIPS_PACKSYM 0x70000008 -+#define SHT_MIPS_RELD 0x70000009 -+#define SHT_MIPS_IFACE 0x7000000b -+#define SHT_MIPS_CONTENT 0x7000000c -+#define SHT_MIPS_OPTIONS 0x7000000d -+#define SHT_MIPS_SHDR 0x70000010 -+#define SHT_MIPS_FDESC 0x70000011 -+#define SHT_MIPS_EXTSYM 0x70000012 -+#define SHT_MIPS_DENSE 0x70000013 -+#define SHT_MIPS_PDESC 0x70000014 -+#define SHT_MIPS_LOCSYM 0x70000015 -+#define SHT_MIPS_AUXSYM 0x70000016 -+#define SHT_MIPS_OPTSYM 0x70000017 -+#define SHT_MIPS_LOCSTR 0x70000018 -+#define SHT_MIPS_LINE 0x70000019 -+#define SHT_MIPS_RFDESC 0x7000001a -+#define SHT_MIPS_DELTASYM 0x7000001b -+#define SHT_MIPS_DELTAINST 0x7000001c -+#define SHT_MIPS_DELTACLASS 0x7000001d -+#define SHT_MIPS_DWARF 0x7000001e -+#define SHT_MIPS_DELTADECL 0x7000001f -+#define SHT_MIPS_SYMBOL_LIB 0x70000020 -+#define SHT_MIPS_EVENTS 0x70000021 -+#define SHT_MIPS_TRANSLATE 0x70000022 -+#define SHT_MIPS_PIXIE 0x70000023 -+#define SHT_MIPS_XLATE 0x70000024 -+#define SHT_MIPS_XLATE_DEBUG 0x70000025 -+#define SHT_MIPS_WHIRL 0x70000026 -+#define SHT_MIPS_EH_REGION 0x70000027 -+#define SHT_MIPS_XLATE_OLD 0x70000028 -+#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -+ -+ -+ -+#define SHF_MIPS_GPREL 0x10000000 -+#define SHF_MIPS_MERGE 0x20000000 -+#define SHF_MIPS_ADDR 0x40000000 -+#define SHF_MIPS_STRINGS 0x80000000 -+#define SHF_MIPS_NOSTRIP 0x08000000 -+#define SHF_MIPS_LOCAL 0x04000000 -+#define SHF_MIPS_NAMES 0x02000000 -+#define SHF_MIPS_NODUPE 0x01000000 -+ -+ -+ -+ -+ -+#define STO_MIPS_DEFAULT 0x0 -+#define STO_MIPS_INTERNAL 0x1 -+#define STO_MIPS_HIDDEN 0x2 -+#define STO_MIPS_PROTECTED 0x3 -+#define STO_MIPS_PLT 0x8 -+#define STO_MIPS_SC_ALIGN_UNUSED 0xff -+ -+ -+#define STB_MIPS_SPLIT_COMMON 13 -+ -+ -+ -+typedef union { -+ struct { -+ Elf32_Word gt_current_g_value; -+ Elf32_Word gt_unused; -+ } gt_header; -+ struct { -+ Elf32_Word gt_g_value; -+ Elf32_Word gt_bytes; -+ } gt_entry; -+} Elf32_gptab; -+ -+ -+ -+typedef struct { -+ Elf32_Word ri_gprmask; -+ Elf32_Word ri_cprmask[4]; -+ Elf32_Sword ri_gp_value; -+} Elf32_RegInfo; -+ -+ -+ -+typedef struct { -+ unsigned char kind; -+ -+ unsigned char size; -+ Elf32_Section section; -+ -+ Elf32_Word info; -+} Elf_Options; -+ -+ -+ -+#define ODK_NULL 0 -+#define ODK_REGINFO 1 -+#define ODK_EXCEPTIONS 2 -+#define ODK_PAD 3 -+#define ODK_HWPATCH 4 -+#define ODK_FILL 5 -+#define ODK_TAGS 6 -+#define ODK_HWAND 7 -+#define ODK_HWOR 8 -+ -+ -+ -+#define OEX_FPU_MIN 0x1f -+#define OEX_FPU_MAX 0x1f00 -+#define OEX_PAGE0 0x10000 -+#define OEX_SMM 0x20000 -+#define OEX_FPDBUG 0x40000 -+#define OEX_PRECISEFP OEX_FPDBUG -+#define OEX_DISMISS 0x80000 -+ -+#define OEX_FPU_INVAL 0x10 -+#define OEX_FPU_DIV0 0x08 -+#define OEX_FPU_OFLO 0x04 -+#define OEX_FPU_UFLO 0x02 -+#define OEX_FPU_INEX 0x01 -+ -+ -+ -+#define OHW_R4KEOP 0x1 -+#define OHW_R8KPFETCH 0x2 -+#define OHW_R5KEOP 0x4 -+#define OHW_R5KCVTL 0x8 -+ -+#define OPAD_PREFIX 0x1 -+#define OPAD_POSTFIX 0x2 -+#define OPAD_SYMBOL 0x4 -+ -+ -+ -+typedef struct { -+ Elf32_Word hwp_flags1; -+ Elf32_Word hwp_flags2; -+} Elf_Options_Hw; -+ -+ -+ -+#define OHWA0_R4KEOP_CHECKED 0x00000001 -+#define OHWA1_R4KEOP_CLEAN 0x00000002 -+ -+ -+ -+#define R_MIPS_NONE 0 -+#define R_MIPS_16 1 -+#define R_MIPS_32 2 -+#define R_MIPS_REL32 3 -+#define R_MIPS_26 4 -+#define R_MIPS_HI16 5 -+#define R_MIPS_LO16 6 -+#define R_MIPS_GPREL16 7 -+#define R_MIPS_LITERAL 8 -+#define R_MIPS_GOT16 9 -+#define R_MIPS_PC16 10 -+#define R_MIPS_CALL16 11 -+#define R_MIPS_GPREL32 12 -+ -+#define R_MIPS_SHIFT5 16 -+#define R_MIPS_SHIFT6 17 -+#define R_MIPS_64 18 -+#define R_MIPS_GOT_DISP 19 -+#define R_MIPS_GOT_PAGE 20 -+#define R_MIPS_GOT_OFST 21 -+#define R_MIPS_GOT_HI16 22 -+#define R_MIPS_GOT_LO16 23 -+#define R_MIPS_SUB 24 -+#define R_MIPS_INSERT_A 25 -+#define R_MIPS_INSERT_B 26 -+#define R_MIPS_DELETE 27 -+#define R_MIPS_HIGHER 28 -+#define R_MIPS_HIGHEST 29 -+#define R_MIPS_CALL_HI16 30 -+#define R_MIPS_CALL_LO16 31 -+#define R_MIPS_SCN_DISP 32 -+#define R_MIPS_REL16 33 -+#define R_MIPS_ADD_IMMEDIATE 34 -+#define R_MIPS_PJUMP 35 -+#define R_MIPS_RELGOT 36 -+#define R_MIPS_JALR 37 -+#define R_MIPS_TLS_DTPMOD32 38 -+#define R_MIPS_TLS_DTPREL32 39 -+#define R_MIPS_TLS_DTPMOD64 40 -+#define R_MIPS_TLS_DTPREL64 41 -+#define R_MIPS_TLS_GD 42 -+#define R_MIPS_TLS_LDM 43 -+#define R_MIPS_TLS_DTPREL_HI16 44 -+#define R_MIPS_TLS_DTPREL_LO16 45 -+#define R_MIPS_TLS_GOTTPREL 46 -+#define R_MIPS_TLS_TPREL32 47 -+#define R_MIPS_TLS_TPREL64 48 -+#define R_MIPS_TLS_TPREL_HI16 49 -+#define R_MIPS_TLS_TPREL_LO16 50 -+#define R_MIPS_GLOB_DAT 51 -+#define R_MIPS_COPY 126 -+#define R_MIPS_JUMP_SLOT 127 -+ -+#define R_MIPS_NUM 128 -+ -+ -+ -+#define PT_MIPS_REGINFO 0x70000000 -+#define PT_MIPS_RTPROC 0x70000001 -+#define PT_MIPS_OPTIONS 0x70000002 -+ -+ -+ -+#define PF_MIPS_LOCAL 0x10000000 -+ -+ -+ -+#define DT_MIPS_RLD_VERSION 0x70000001 -+#define DT_MIPS_TIME_STAMP 0x70000002 -+#define DT_MIPS_ICHECKSUM 0x70000003 -+#define DT_MIPS_IVERSION 0x70000004 -+#define DT_MIPS_FLAGS 0x70000005 -+#define DT_MIPS_BASE_ADDRESS 0x70000006 -+#define DT_MIPS_MSYM 0x70000007 -+#define DT_MIPS_CONFLICT 0x70000008 -+#define DT_MIPS_LIBLIST 0x70000009 -+#define DT_MIPS_LOCAL_GOTNO 0x7000000a -+#define DT_MIPS_CONFLICTNO 0x7000000b -+#define DT_MIPS_LIBLISTNO 0x70000010 -+#define DT_MIPS_SYMTABNO 0x70000011 -+#define DT_MIPS_UNREFEXTNO 0x70000012 -+#define DT_MIPS_GOTSYM 0x70000013 -+#define DT_MIPS_HIPAGENO 0x70000014 -+#define DT_MIPS_RLD_MAP 0x70000016 -+#define DT_MIPS_DELTA_CLASS 0x70000017 -+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 -+ -+#define DT_MIPS_DELTA_INSTANCE 0x70000019 -+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a -+ -+#define DT_MIPS_DELTA_RELOC 0x7000001b -+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c -+ -+#define DT_MIPS_DELTA_SYM 0x7000001d -+ -+#define DT_MIPS_DELTA_SYM_NO 0x7000001e -+ -+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 -+ -+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 -+ -+#define DT_MIPS_CXX_FLAGS 0x70000022 -+#define DT_MIPS_PIXIE_INIT 0x70000023 -+#define DT_MIPS_SYMBOL_LIB 0x70000024 -+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -+#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -+#define DT_MIPS_OPTIONS 0x70000029 -+#define DT_MIPS_INTERFACE 0x7000002a -+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -+#define DT_MIPS_INTERFACE_SIZE 0x7000002c -+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d -+ -+#define DT_MIPS_PERF_SUFFIX 0x7000002e -+ -+#define DT_MIPS_COMPACT_SIZE 0x7000002f -+#define DT_MIPS_GP_VALUE 0x70000030 -+#define DT_MIPS_AUX_DYNAMIC 0x70000031 -+ -+#define DT_MIPS_PLTGOT 0x70000032 -+ -+#define DT_MIPS_RWPLT 0x70000034 -+#define DT_MIPS_NUM 0x35 -+ -+ -+ -+#define RHF_NONE 0 -+#define RHF_QUICKSTART (1 << 0) -+#define RHF_NOTPOT (1 << 1) -+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) -+#define RHF_NO_MOVE (1 << 3) -+#define RHF_SGI_ONLY (1 << 4) -+#define RHF_GUARANTEE_INIT (1 << 5) -+#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -+#define RHF_GUARANTEE_START_INIT (1 << 7) -+#define RHF_PIXIE (1 << 8) -+#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -+#define RHF_REQUICKSTART (1 << 10) -+#define RHF_REQUICKSTARTED (1 << 11) -+#define RHF_CORD (1 << 12) -+#define RHF_NO_UNRES_UNDEF (1 << 13) -+#define RHF_RLD_ORDER_SAFE (1 << 14) -+ -+ -+ -+typedef struct -+{ -+ Elf32_Word l_name; -+ Elf32_Word l_time_stamp; -+ Elf32_Word l_checksum; -+ Elf32_Word l_version; -+ Elf32_Word l_flags; -+} Elf32_Lib; -+ -+typedef struct -+{ -+ Elf64_Word l_name; -+ Elf64_Word l_time_stamp; -+ Elf64_Word l_checksum; -+ Elf64_Word l_version; -+ Elf64_Word l_flags; -+} Elf64_Lib; -+ -+ -+ -+ -+#define LL_NONE 0 -+#define LL_EXACT_MATCH (1 << 0) -+#define LL_IGNORE_INT_VER (1 << 1) -+#define LL_REQUIRE_MINOR (1 << 2) -+#define LL_EXPORTS (1 << 3) -+#define LL_DELAY_LOAD (1 << 4) -+#define LL_DELTA (1 << 5) -+ -+ -+ -+typedef Elf32_Addr Elf32_Conflict; -+ -+ -+ -+ -+ -+ -+#define EF_PARISC_TRAPNIL 0x00010000 -+#define EF_PARISC_EXT 0x00020000 -+#define EF_PARISC_LSB 0x00040000 -+#define EF_PARISC_WIDE 0x00080000 -+#define EF_PARISC_NO_KABP 0x00100000 -+ -+#define EF_PARISC_LAZYSWAP 0x00400000 -+#define EF_PARISC_ARCH 0x0000ffff -+ -+ -+ -+#define EFA_PARISC_1_0 0x020b -+#define EFA_PARISC_1_1 0x0210 -+#define EFA_PARISC_2_0 0x0214 -+ -+ -+ -+#define SHN_PARISC_ANSI_COMMON 0xff00 -+ -+#define SHN_PARISC_HUGE_COMMON 0xff01 -+ -+ -+ -+#define SHT_PARISC_EXT 0x70000000 -+#define SHT_PARISC_UNWIND 0x70000001 -+#define SHT_PARISC_DOC 0x70000002 -+ -+ -+ -+#define SHF_PARISC_SHORT 0x20000000 -+#define SHF_PARISC_HUGE 0x40000000 -+#define SHF_PARISC_SBP 0x80000000 -+ -+ -+ -+#define STT_PARISC_MILLICODE 13 -+ -+#define STT_HP_OPAQUE (STT_LOOS + 0x1) -+#define STT_HP_STUB (STT_LOOS + 0x2) -+ -+ -+ -+#define R_PARISC_NONE 0 -+#define R_PARISC_DIR32 1 -+#define R_PARISC_DIR21L 2 -+#define R_PARISC_DIR17R 3 -+#define R_PARISC_DIR17F 4 -+#define R_PARISC_DIR14R 6 -+#define R_PARISC_PCREL32 9 -+#define R_PARISC_PCREL21L 10 -+#define R_PARISC_PCREL17R 11 -+#define R_PARISC_PCREL17F 12 -+#define R_PARISC_PCREL14R 14 -+#define R_PARISC_DPREL21L 18 -+#define R_PARISC_DPREL14R 22 -+#define R_PARISC_GPREL21L 26 -+#define R_PARISC_GPREL14R 30 -+#define R_PARISC_LTOFF21L 34 -+#define R_PARISC_LTOFF14R 38 -+#define R_PARISC_SECREL32 41 -+#define R_PARISC_SEGBASE 48 -+#define R_PARISC_SEGREL32 49 -+#define R_PARISC_PLTOFF21L 50 -+#define R_PARISC_PLTOFF14R 54 -+#define R_PARISC_LTOFF_FPTR32 57 -+#define R_PARISC_LTOFF_FPTR21L 58 -+#define R_PARISC_LTOFF_FPTR14R 62 -+#define R_PARISC_FPTR64 64 -+#define R_PARISC_PLABEL32 65 -+#define R_PARISC_PLABEL21L 66 -+#define R_PARISC_PLABEL14R 70 -+#define R_PARISC_PCREL64 72 -+#define R_PARISC_PCREL22F 74 -+#define R_PARISC_PCREL14WR 75 -+#define R_PARISC_PCREL14DR 76 -+#define R_PARISC_PCREL16F 77 -+#define R_PARISC_PCREL16WF 78 -+#define R_PARISC_PCREL16DF 79 -+#define R_PARISC_DIR64 80 -+#define R_PARISC_DIR14WR 83 -+#define R_PARISC_DIR14DR 84 -+#define R_PARISC_DIR16F 85 -+#define R_PARISC_DIR16WF 86 -+#define R_PARISC_DIR16DF 87 -+#define R_PARISC_GPREL64 88 -+#define R_PARISC_GPREL14WR 91 -+#define R_PARISC_GPREL14DR 92 -+#define R_PARISC_GPREL16F 93 -+#define R_PARISC_GPREL16WF 94 -+#define R_PARISC_GPREL16DF 95 -+#define R_PARISC_LTOFF64 96 -+#define R_PARISC_LTOFF14WR 99 -+#define R_PARISC_LTOFF14DR 100 -+#define R_PARISC_LTOFF16F 101 -+#define R_PARISC_LTOFF16WF 102 -+#define R_PARISC_LTOFF16DF 103 -+#define R_PARISC_SECREL64 104 -+#define R_PARISC_SEGREL64 112 -+#define R_PARISC_PLTOFF14WR 115 -+#define R_PARISC_PLTOFF14DR 116 -+#define R_PARISC_PLTOFF16F 117 -+#define R_PARISC_PLTOFF16WF 118 -+#define R_PARISC_PLTOFF16DF 119 -+#define R_PARISC_LTOFF_FPTR64 120 -+#define R_PARISC_LTOFF_FPTR14WR 123 -+#define R_PARISC_LTOFF_FPTR14DR 124 -+#define R_PARISC_LTOFF_FPTR16F 125 -+#define R_PARISC_LTOFF_FPTR16WF 126 -+#define R_PARISC_LTOFF_FPTR16DF 127 -+#define R_PARISC_LORESERVE 128 -+#define R_PARISC_COPY 128 -+#define R_PARISC_IPLT 129 -+#define R_PARISC_EPLT 130 -+#define R_PARISC_TPREL32 153 -+#define R_PARISC_TPREL21L 154 -+#define R_PARISC_TPREL14R 158 -+#define R_PARISC_LTOFF_TP21L 162 -+#define R_PARISC_LTOFF_TP14R 166 -+#define R_PARISC_LTOFF_TP14F 167 -+#define R_PARISC_TPREL64 216 -+#define R_PARISC_TPREL14WR 219 -+#define R_PARISC_TPREL14DR 220 -+#define R_PARISC_TPREL16F 221 -+#define R_PARISC_TPREL16WF 222 -+#define R_PARISC_TPREL16DF 223 -+#define R_PARISC_LTOFF_TP64 224 -+#define R_PARISC_LTOFF_TP14WR 227 -+#define R_PARISC_LTOFF_TP14DR 228 -+#define R_PARISC_LTOFF_TP16F 229 -+#define R_PARISC_LTOFF_TP16WF 230 -+#define R_PARISC_LTOFF_TP16DF 231 -+#define R_PARISC_GNU_VTENTRY 232 -+#define R_PARISC_GNU_VTINHERIT 233 -+#define R_PARISC_TLS_GD21L 234 -+#define R_PARISC_TLS_GD14R 235 -+#define R_PARISC_TLS_GDCALL 236 -+#define R_PARISC_TLS_LDM21L 237 -+#define R_PARISC_TLS_LDM14R 238 -+#define R_PARISC_TLS_LDMCALL 239 -+#define R_PARISC_TLS_LDO21L 240 -+#define R_PARISC_TLS_LDO14R 241 -+#define R_PARISC_TLS_DTPMOD32 242 -+#define R_PARISC_TLS_DTPMOD64 243 -+#define R_PARISC_TLS_DTPOFF32 244 -+#define R_PARISC_TLS_DTPOFF64 245 -+#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L -+#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R -+#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L -+#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R -+#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 -+#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 -+#define R_PARISC_HIRESERVE 255 -+ -+ -+ -+#define PT_HP_TLS (PT_LOOS + 0x0) -+#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -+#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -+#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -+#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -+#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -+#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -+#define PT_HP_PARALLEL (PT_LOOS + 0x10) -+#define PT_HP_FASTBIND (PT_LOOS + 0x11) -+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -+#define PT_HP_STACK (PT_LOOS + 0x14) -+ -+#define PT_PARISC_ARCHEXT 0x70000000 -+#define PT_PARISC_UNWIND 0x70000001 -+ -+ -+ -+#define PF_PARISC_SBP 0x08000000 -+ -+#define PF_HP_PAGE_SIZE 0x00100000 -+#define PF_HP_FAR_SHARED 0x00200000 -+#define PF_HP_NEAR_SHARED 0x00400000 -+#define PF_HP_CODE 0x01000000 -+#define PF_HP_MODIFY 0x02000000 -+#define PF_HP_LAZYSWAP 0x04000000 -+#define PF_HP_SBP 0x08000000 -+ -+ -+ -+ -+ -+ -+#define EF_ALPHA_32BIT 1 -+#define EF_ALPHA_CANRELAX 2 -+ -+ -+ -+ -+#define SHT_ALPHA_DEBUG 0x70000001 -+#define SHT_ALPHA_REGINFO 0x70000002 -+ -+ -+ -+#define SHF_ALPHA_GPREL 0x10000000 -+ -+ -+#define STO_ALPHA_NOPV 0x80 -+#define STO_ALPHA_STD_GPLOAD 0x88 -+ -+ -+ -+#define R_ALPHA_NONE 0 -+#define R_ALPHA_REFLONG 1 -+#define R_ALPHA_REFQUAD 2 -+#define R_ALPHA_GPREL32 3 -+#define R_ALPHA_LITERAL 4 -+#define R_ALPHA_LITUSE 5 -+#define R_ALPHA_GPDISP 6 -+#define R_ALPHA_BRADDR 7 -+#define R_ALPHA_HINT 8 -+#define R_ALPHA_SREL16 9 -+#define R_ALPHA_SREL32 10 -+#define R_ALPHA_SREL64 11 -+#define R_ALPHA_GPRELHIGH 17 -+#define R_ALPHA_GPRELLOW 18 -+#define R_ALPHA_GPREL16 19 -+#define R_ALPHA_COPY 24 -+#define R_ALPHA_GLOB_DAT 25 -+#define R_ALPHA_JMP_SLOT 26 -+#define R_ALPHA_RELATIVE 27 -+#define R_ALPHA_TLS_GD_HI 28 -+#define R_ALPHA_TLSGD 29 -+#define R_ALPHA_TLS_LDM 30 -+#define R_ALPHA_DTPMOD64 31 -+#define R_ALPHA_GOTDTPREL 32 -+#define R_ALPHA_DTPREL64 33 -+#define R_ALPHA_DTPRELHI 34 -+#define R_ALPHA_DTPRELLO 35 -+#define R_ALPHA_DTPREL16 36 -+#define R_ALPHA_GOTTPREL 37 -+#define R_ALPHA_TPREL64 38 -+#define R_ALPHA_TPRELHI 39 -+#define R_ALPHA_TPRELLO 40 -+#define R_ALPHA_TPREL16 41 -+ -+#define R_ALPHA_NUM 46 -+ -+ -+#define LITUSE_ALPHA_ADDR 0 -+#define LITUSE_ALPHA_BASE 1 -+#define LITUSE_ALPHA_BYTOFF 2 -+#define LITUSE_ALPHA_JSR 3 -+#define LITUSE_ALPHA_TLS_GD 4 -+#define LITUSE_ALPHA_TLS_LDM 5 -+ -+ -+#define DT_ALPHA_PLTRO (DT_LOPROC + 0) -+#define DT_ALPHA_NUM 1 -+ -+ -+ -+ -+#define EF_PPC_EMB 0x80000000 -+ -+ -+#define EF_PPC_RELOCATABLE 0x00010000 -+#define EF_PPC_RELOCATABLE_LIB 0x00008000 -+ -+ -+ -+#define R_PPC_NONE 0 -+#define R_PPC_ADDR32 1 -+#define R_PPC_ADDR24 2 -+#define R_PPC_ADDR16 3 -+#define R_PPC_ADDR16_LO 4 -+#define R_PPC_ADDR16_HI 5 -+#define R_PPC_ADDR16_HA 6 -+#define R_PPC_ADDR14 7 -+#define R_PPC_ADDR14_BRTAKEN 8 -+#define R_PPC_ADDR14_BRNTAKEN 9 -+#define R_PPC_REL24 10 -+#define R_PPC_REL14 11 -+#define R_PPC_REL14_BRTAKEN 12 -+#define R_PPC_REL14_BRNTAKEN 13 -+#define R_PPC_GOT16 14 -+#define R_PPC_GOT16_LO 15 -+#define R_PPC_GOT16_HI 16 -+#define R_PPC_GOT16_HA 17 -+#define R_PPC_PLTREL24 18 -+#define R_PPC_COPY 19 -+#define R_PPC_GLOB_DAT 20 -+#define R_PPC_JMP_SLOT 21 -+#define R_PPC_RELATIVE 22 -+#define R_PPC_LOCAL24PC 23 -+#define R_PPC_UADDR32 24 -+#define R_PPC_UADDR16 25 -+#define R_PPC_REL32 26 -+#define R_PPC_PLT32 27 -+#define R_PPC_PLTREL32 28 -+#define R_PPC_PLT16_LO 29 -+#define R_PPC_PLT16_HI 30 -+#define R_PPC_PLT16_HA 31 -+#define R_PPC_SDAREL16 32 -+#define R_PPC_SECTOFF 33 -+#define R_PPC_SECTOFF_LO 34 -+#define R_PPC_SECTOFF_HI 35 -+#define R_PPC_SECTOFF_HA 36 -+ -+ -+#define R_PPC_TLS 67 -+#define R_PPC_DTPMOD32 68 -+#define R_PPC_TPREL16 69 -+#define R_PPC_TPREL16_LO 70 -+#define R_PPC_TPREL16_HI 71 -+#define R_PPC_TPREL16_HA 72 -+#define R_PPC_TPREL32 73 -+#define R_PPC_DTPREL16 74 -+#define R_PPC_DTPREL16_LO 75 -+#define R_PPC_DTPREL16_HI 76 -+#define R_PPC_DTPREL16_HA 77 -+#define R_PPC_DTPREL32 78 -+#define R_PPC_GOT_TLSGD16 79 -+#define R_PPC_GOT_TLSGD16_LO 80 -+#define R_PPC_GOT_TLSGD16_HI 81 -+#define R_PPC_GOT_TLSGD16_HA 82 -+#define R_PPC_GOT_TLSLD16 83 -+#define R_PPC_GOT_TLSLD16_LO 84 -+#define R_PPC_GOT_TLSLD16_HI 85 -+#define R_PPC_GOT_TLSLD16_HA 86 -+#define R_PPC_GOT_TPREL16 87 -+#define R_PPC_GOT_TPREL16_LO 88 -+#define R_PPC_GOT_TPREL16_HI 89 -+#define R_PPC_GOT_TPREL16_HA 90 -+#define R_PPC_GOT_DTPREL16 91 -+#define R_PPC_GOT_DTPREL16_LO 92 -+#define R_PPC_GOT_DTPREL16_HI 93 -+#define R_PPC_GOT_DTPREL16_HA 94 -+ -+ -+ -+#define R_PPC_EMB_NADDR32 101 -+#define R_PPC_EMB_NADDR16 102 -+#define R_PPC_EMB_NADDR16_LO 103 -+#define R_PPC_EMB_NADDR16_HI 104 -+#define R_PPC_EMB_NADDR16_HA 105 -+#define R_PPC_EMB_SDAI16 106 -+#define R_PPC_EMB_SDA2I16 107 -+#define R_PPC_EMB_SDA2REL 108 -+#define R_PPC_EMB_SDA21 109 -+#define R_PPC_EMB_MRKREF 110 -+#define R_PPC_EMB_RELSEC16 111 -+#define R_PPC_EMB_RELST_LO 112 -+#define R_PPC_EMB_RELST_HI 113 -+#define R_PPC_EMB_RELST_HA 114 -+#define R_PPC_EMB_BIT_FLD 115 -+#define R_PPC_EMB_RELSDA 116 -+ -+ -+#define R_PPC_DIAB_SDA21_LO 180 -+#define R_PPC_DIAB_SDA21_HI 181 -+#define R_PPC_DIAB_SDA21_HA 182 -+#define R_PPC_DIAB_RELSDA_LO 183 -+#define R_PPC_DIAB_RELSDA_HI 184 -+#define R_PPC_DIAB_RELSDA_HA 185 -+ -+ -+#define R_PPC_IRELATIVE 248 -+ -+ -+#define R_PPC_REL16 249 -+#define R_PPC_REL16_LO 250 -+#define R_PPC_REL16_HI 251 -+#define R_PPC_REL16_HA 252 -+ -+ -+ -+#define R_PPC_TOC16 255 -+ -+ -+#define DT_PPC_GOT (DT_LOPROC + 0) -+#define DT_PPC_NUM 1 -+ -+ -+#define R_PPC64_NONE R_PPC_NONE -+#define R_PPC64_ADDR32 R_PPC_ADDR32 -+#define R_PPC64_ADDR24 R_PPC_ADDR24 -+#define R_PPC64_ADDR16 R_PPC_ADDR16 -+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO -+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI -+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA -+#define R_PPC64_ADDR14 R_PPC_ADDR14 -+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -+#define R_PPC64_REL24 R_PPC_REL24 -+#define R_PPC64_REL14 R_PPC_REL14 -+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -+#define R_PPC64_GOT16 R_PPC_GOT16 -+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA -+ -+#define R_PPC64_COPY R_PPC_COPY -+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -+#define R_PPC64_RELATIVE R_PPC_RELATIVE -+ -+#define R_PPC64_UADDR32 R_PPC_UADDR32 -+#define R_PPC64_UADDR16 R_PPC_UADDR16 -+#define R_PPC64_REL32 R_PPC_REL32 -+#define R_PPC64_PLT32 R_PPC_PLT32 -+#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA -+ -+#define R_PPC64_SECTOFF R_PPC_SECTOFF -+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -+#define R_PPC64_ADDR30 37 -+#define R_PPC64_ADDR64 38 -+#define R_PPC64_ADDR16_HIGHER 39 -+#define R_PPC64_ADDR16_HIGHERA 40 -+#define R_PPC64_ADDR16_HIGHEST 41 -+#define R_PPC64_ADDR16_HIGHESTA 42 -+#define R_PPC64_UADDR64 43 -+#define R_PPC64_REL64 44 -+#define R_PPC64_PLT64 45 -+#define R_PPC64_PLTREL64 46 -+#define R_PPC64_TOC16 47 -+#define R_PPC64_TOC16_LO 48 -+#define R_PPC64_TOC16_HI 49 -+#define R_PPC64_TOC16_HA 50 -+#define R_PPC64_TOC 51 -+#define R_PPC64_PLTGOT16 52 -+#define R_PPC64_PLTGOT16_LO 53 -+#define R_PPC64_PLTGOT16_HI 54 -+#define R_PPC64_PLTGOT16_HA 55 -+ -+#define R_PPC64_ADDR16_DS 56 -+#define R_PPC64_ADDR16_LO_DS 57 -+#define R_PPC64_GOT16_DS 58 -+#define R_PPC64_GOT16_LO_DS 59 -+#define R_PPC64_PLT16_LO_DS 60 -+#define R_PPC64_SECTOFF_DS 61 -+#define R_PPC64_SECTOFF_LO_DS 62 -+#define R_PPC64_TOC16_DS 63 -+#define R_PPC64_TOC16_LO_DS 64 -+#define R_PPC64_PLTGOT16_DS 65 -+#define R_PPC64_PLTGOT16_LO_DS 66 -+ -+ -+#define R_PPC64_TLS 67 -+#define R_PPC64_DTPMOD64 68 -+#define R_PPC64_TPREL16 69 -+#define R_PPC64_TPREL16_LO 70 -+#define R_PPC64_TPREL16_HI 71 -+#define R_PPC64_TPREL16_HA 72 -+#define R_PPC64_TPREL64 73 -+#define R_PPC64_DTPREL16 74 -+#define R_PPC64_DTPREL16_LO 75 -+#define R_PPC64_DTPREL16_HI 76 -+#define R_PPC64_DTPREL16_HA 77 -+#define R_PPC64_DTPREL64 78 -+#define R_PPC64_GOT_TLSGD16 79 -+#define R_PPC64_GOT_TLSGD16_LO 80 -+#define R_PPC64_GOT_TLSGD16_HI 81 -+#define R_PPC64_GOT_TLSGD16_HA 82 -+#define R_PPC64_GOT_TLSLD16 83 -+#define R_PPC64_GOT_TLSLD16_LO 84 -+#define R_PPC64_GOT_TLSLD16_HI 85 -+#define R_PPC64_GOT_TLSLD16_HA 86 -+#define R_PPC64_GOT_TPREL16_DS 87 -+#define R_PPC64_GOT_TPREL16_LO_DS 88 -+#define R_PPC64_GOT_TPREL16_HI 89 -+#define R_PPC64_GOT_TPREL16_HA 90 -+#define R_PPC64_GOT_DTPREL16_DS 91 -+#define R_PPC64_GOT_DTPREL16_LO_DS 92 -+#define R_PPC64_GOT_DTPREL16_HI 93 -+#define R_PPC64_GOT_DTPREL16_HA 94 -+#define R_PPC64_TPREL16_DS 95 -+#define R_PPC64_TPREL16_LO_DS 96 -+#define R_PPC64_TPREL16_HIGHER 97 -+#define R_PPC64_TPREL16_HIGHERA 98 -+#define R_PPC64_TPREL16_HIGHEST 99 -+#define R_PPC64_TPREL16_HIGHESTA 100 -+#define R_PPC64_DTPREL16_DS 101 -+#define R_PPC64_DTPREL16_LO_DS 102 -+#define R_PPC64_DTPREL16_HIGHER 103 -+#define R_PPC64_DTPREL16_HIGHERA 104 -+#define R_PPC64_DTPREL16_HIGHEST 105 -+#define R_PPC64_DTPREL16_HIGHESTA 106 -+ -+ -+#define R_PPC64_JMP_IREL 247 -+#define R_PPC64_IRELATIVE 248 -+#define R_PPC64_REL16 249 -+#define R_PPC64_REL16_LO 250 -+#define R_PPC64_REL16_HI 251 -+#define R_PPC64_REL16_HA 252 -+ -+ -+#define DT_PPC64_GLINK (DT_LOPROC + 0) -+#define DT_PPC64_OPD (DT_LOPROC + 1) -+#define DT_PPC64_OPDSZ (DT_LOPROC + 2) -+#define DT_PPC64_NUM 3 -+ -+ -+ -+ -+ -+#define EF_ARM_RELEXEC 0x01 -+#define EF_ARM_HASENTRY 0x02 -+#define EF_ARM_INTERWORK 0x04 -+#define EF_ARM_APCS_26 0x08 -+#define EF_ARM_APCS_FLOAT 0x10 -+#define EF_ARM_PIC 0x20 -+#define EF_ARM_ALIGN8 0x40 -+#define EF_ARM_NEW_ABI 0x80 -+#define EF_ARM_OLD_ABI 0x100 -+#define EF_ARM_SOFT_FLOAT 0x200 -+#define EF_ARM_VFP_FLOAT 0x400 -+#define EF_ARM_MAVERICK_FLOAT 0x800 -+ -+#define EF_ARM_ABI_FLOAT_SOFT 0x200 -+#define EF_ARM_ABI_FLOAT_HARD 0x400 -+ -+ -+#define EF_ARM_SYMSARESORTED 0x04 -+#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -+#define EF_ARM_MAPSYMSFIRST 0x10 -+#define EF_ARM_EABIMASK 0XFF000000 -+ -+ -+#define EF_ARM_BE8 0x00800000 -+#define EF_ARM_LE8 0x00400000 -+ -+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -+#define EF_ARM_EABI_UNKNOWN 0x00000000 -+#define EF_ARM_EABI_VER1 0x01000000 -+#define EF_ARM_EABI_VER2 0x02000000 -+#define EF_ARM_EABI_VER3 0x03000000 -+#define EF_ARM_EABI_VER4 0x04000000 -+#define EF_ARM_EABI_VER5 0x05000000 -+ -+ -+#define STT_ARM_TFUNC STT_LOPROC -+#define STT_ARM_16BIT STT_HIPROC -+ -+ -+#define SHF_ARM_ENTRYSECT 0x10000000 -+#define SHF_ARM_COMDEF 0x80000000 -+ -+ -+ -+#define PF_ARM_SB 0x10000000 -+ -+#define PF_ARM_PI 0x20000000 -+#define PF_ARM_ABS 0x40000000 -+ -+ -+#define PT_ARM_EXIDX (PT_LOPROC + 1) -+ -+ -+#define SHT_ARM_EXIDX (SHT_LOPROC + 1) -+#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) -+#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) -+ -+ -+#define R_AARCH64_NONE 0 -+#define R_AARCH64_ABS64 257 -+#define R_AARCH64_ABS32 258 -+#define R_AARCH64_COPY 1024 -+#define R_AARCH64_GLOB_DAT 1025 -+#define R_AARCH64_JUMP_SLOT 1026 -+#define R_AARCH64_RELATIVE 1027 -+#define R_AARCH64_TLS_DTPMOD64 1028 -+#define R_AARCH64_TLS_DTPREL64 1029 -+#define R_AARCH64_TLS_TPREL64 1030 -+#define R_AARCH64_TLSDESC 1031 -+ -+ -+#define R_ARM_NONE 0 -+#define R_ARM_PC24 1 -+#define R_ARM_ABS32 2 -+#define R_ARM_REL32 3 -+#define R_ARM_PC13 4 -+#define R_ARM_ABS16 5 -+#define R_ARM_ABS12 6 -+#define R_ARM_THM_ABS5 7 -+#define R_ARM_ABS8 8 -+#define R_ARM_SBREL32 9 -+#define R_ARM_THM_PC22 10 -+#define R_ARM_THM_PC8 11 -+#define R_ARM_AMP_VCALL9 12 -+#define R_ARM_TLS_DESC 13 -+#define R_ARM_THM_SWI8 14 -+#define R_ARM_XPC25 15 -+#define R_ARM_THM_XPC22 16 -+#define R_ARM_TLS_DTPMOD32 17 -+#define R_ARM_TLS_DTPOFF32 18 -+#define R_ARM_TLS_TPOFF32 19 -+#define R_ARM_COPY 20 -+#define R_ARM_GLOB_DAT 21 -+#define R_ARM_JUMP_SLOT 22 -+#define R_ARM_RELATIVE 23 -+#define R_ARM_GOTOFF 24 -+#define R_ARM_GOTPC 25 -+#define R_ARM_GOT32 26 -+#define R_ARM_PLT32 27 -+#define R_ARM_CALL 28 -+#define R_ARM_JUMP24 29 -+#define R_ARM_THM_JUMP24 30 -+#define R_ARM_BASE_ABS 31 -+#define R_ARM_ALU_PCREL_7_0 32 -+#define R_ARM_ALU_PCREL_15_8 33 -+#define R_ARM_ALU_PCREL_23_15 34 -+#define R_ARM_LDR_SBREL_11_0 35 -+#define R_ARM_ALU_SBREL_19_12 36 -+#define R_ARM_ALU_SBREL_27_20 37 -+#define R_ARM_TARGET1 38 -+#define R_ARM_SBREL31 39 -+#define R_ARM_V4BX 40 -+#define R_ARM_TARGET2 41 -+#define R_ARM_PREL31 42 -+#define R_ARM_MOVW_ABS_NC 43 -+#define R_ARM_MOVT_ABS 44 -+#define R_ARM_MOVW_PREL_NC 45 -+#define R_ARM_MOVT_PREL 46 -+#define R_ARM_THM_MOVW_ABS_NC 47 -+#define R_ARM_THM_MOVT_ABS 48 -+#define R_ARM_THM_MOVW_PREL_NC 49 -+#define R_ARM_THM_MOVT_PREL 50 -+#define R_ARM_THM_JUMP19 51 -+#define R_ARM_THM_JUMP6 52 -+#define R_ARM_THM_ALU_PREL_11_0 53 -+#define R_ARM_THM_PC12 54 -+#define R_ARM_ABS32_NOI 55 -+#define R_ARM_REL32_NOI 56 -+#define R_ARM_ALU_PC_G0_NC 57 -+#define R_ARM_ALU_PC_G0 58 -+#define R_ARM_ALU_PC_G1_NC 59 -+#define R_ARM_ALU_PC_G1 60 -+#define R_ARM_ALU_PC_G2 61 -+#define R_ARM_LDR_PC_G1 62 -+#define R_ARM_LDR_PC_G2 63 -+#define R_ARM_LDRS_PC_G0 64 -+#define R_ARM_LDRS_PC_G1 65 -+#define R_ARM_LDRS_PC_G2 66 -+#define R_ARM_LDC_PC_G0 67 -+#define R_ARM_LDC_PC_G1 68 -+#define R_ARM_LDC_PC_G2 69 -+#define R_ARM_ALU_SB_G0_NC 70 -+#define R_ARM_ALU_SB_G0 71 -+#define R_ARM_ALU_SB_G1_NC 72 -+#define R_ARM_ALU_SB_G1 73 -+#define R_ARM_ALU_SB_G2 74 -+#define R_ARM_LDR_SB_G0 75 -+#define R_ARM_LDR_SB_G1 76 -+#define R_ARM_LDR_SB_G2 77 -+#define R_ARM_LDRS_SB_G0 78 -+#define R_ARM_LDRS_SB_G1 79 -+#define R_ARM_LDRS_SB_G2 80 -+#define R_ARM_LDC_SB_G0 81 -+#define R_ARM_LDC_SB_G1 82 -+#define R_ARM_LDC_SB_G2 83 -+#define R_ARM_MOVW_BREL_NC 84 -+#define R_ARM_MOVT_BREL 85 -+#define R_ARM_MOVW_BREL 86 -+#define R_ARM_THM_MOVW_BREL_NC 87 -+#define R_ARM_THM_MOVT_BREL 88 -+#define R_ARM_THM_MOVW_BREL 89 -+#define R_ARM_TLS_GOTDESC 90 -+#define R_ARM_TLS_CALL 91 -+#define R_ARM_TLS_DESCSEQ 92 -+#define R_ARM_THM_TLS_CALL 93 -+#define R_ARM_PLT32_ABS 94 -+#define R_ARM_GOT_ABS 95 -+#define R_ARM_GOT_PREL 96 -+#define R_ARM_GOT_BREL12 97 -+#define R_ARM_GOTOFF12 98 -+#define R_ARM_GOTRELAX 99 -+#define R_ARM_GNU_VTENTRY 100 -+#define R_ARM_GNU_VTINHERIT 101 -+#define R_ARM_THM_PC11 102 -+#define R_ARM_THM_PC9 103 -+#define R_ARM_TLS_GD32 104 -+ -+#define R_ARM_TLS_LDM32 105 -+ -+#define R_ARM_TLS_LDO32 106 -+ -+#define R_ARM_TLS_IE32 107 -+ -+#define R_ARM_TLS_LE32 108 -+#define R_ARM_TLS_LDO12 109 -+#define R_ARM_TLS_LE12 110 -+#define R_ARM_TLS_IE12GP 111 -+#define R_ARM_ME_TOO 128 -+#define R_ARM_THM_TLS_DESCSEQ 129 -+#define R_ARM_THM_TLS_DESCSEQ16 129 -+#define R_ARM_THM_TLS_DESCSEQ32 130 -+#define R_ARM_THM_GOT_BREL12 131 -+#define R_ARM_IRELATIVE 160 -+#define R_ARM_RXPC25 249 -+#define R_ARM_RSBREL32 250 -+#define R_ARM_THM_RPC22 251 -+#define R_ARM_RREL32 252 -+#define R_ARM_RABS22 253 -+#define R_ARM_RPC24 254 -+#define R_ARM_RBASE 255 -+ -+#define R_ARM_NUM 256 -+ -+ -+ -+ -+#define EF_IA_64_MASKOS 0x0000000f -+#define EF_IA_64_ABI64 0x00000010 -+#define EF_IA_64_ARCH 0xff000000 -+ -+ -+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) -+#define PT_IA_64_UNWIND (PT_LOPROC + 1) -+#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) -+#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) -+#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) -+ -+ -+#define PF_IA_64_NORECOV 0x80000000 -+ -+ -+#define SHT_IA_64_EXT (SHT_LOPROC + 0) -+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) -+ -+ -+#define SHF_IA_64_SHORT 0x10000000 -+#define SHF_IA_64_NORECOV 0x20000000 -+ -+ -+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -+#define DT_IA_64_NUM 1 -+ -+ -+#define R_IA64_NONE 0x00 -+#define R_IA64_IMM14 0x21 -+#define R_IA64_IMM22 0x22 -+#define R_IA64_IMM64 0x23 -+#define R_IA64_DIR32MSB 0x24 -+#define R_IA64_DIR32LSB 0x25 -+#define R_IA64_DIR64MSB 0x26 -+#define R_IA64_DIR64LSB 0x27 -+#define R_IA64_GPREL22 0x2a -+#define R_IA64_GPREL64I 0x2b -+#define R_IA64_GPREL32MSB 0x2c -+#define R_IA64_GPREL32LSB 0x2d -+#define R_IA64_GPREL64MSB 0x2e -+#define R_IA64_GPREL64LSB 0x2f -+#define R_IA64_LTOFF22 0x32 -+#define R_IA64_LTOFF64I 0x33 -+#define R_IA64_PLTOFF22 0x3a -+#define R_IA64_PLTOFF64I 0x3b -+#define R_IA64_PLTOFF64MSB 0x3e -+#define R_IA64_PLTOFF64LSB 0x3f -+#define R_IA64_FPTR64I 0x43 -+#define R_IA64_FPTR32MSB 0x44 -+#define R_IA64_FPTR32LSB 0x45 -+#define R_IA64_FPTR64MSB 0x46 -+#define R_IA64_FPTR64LSB 0x47 -+#define R_IA64_PCREL60B 0x48 -+#define R_IA64_PCREL21B 0x49 -+#define R_IA64_PCREL21M 0x4a -+#define R_IA64_PCREL21F 0x4b -+#define R_IA64_PCREL32MSB 0x4c -+#define R_IA64_PCREL32LSB 0x4d -+#define R_IA64_PCREL64MSB 0x4e -+#define R_IA64_PCREL64LSB 0x4f -+#define R_IA64_LTOFF_FPTR22 0x52 -+#define R_IA64_LTOFF_FPTR64I 0x53 -+#define R_IA64_LTOFF_FPTR32MSB 0x54 -+#define R_IA64_LTOFF_FPTR32LSB 0x55 -+#define R_IA64_LTOFF_FPTR64MSB 0x56 -+#define R_IA64_LTOFF_FPTR64LSB 0x57 -+#define R_IA64_SEGREL32MSB 0x5c -+#define R_IA64_SEGREL32LSB 0x5d -+#define R_IA64_SEGREL64MSB 0x5e -+#define R_IA64_SEGREL64LSB 0x5f -+#define R_IA64_SECREL32MSB 0x64 -+#define R_IA64_SECREL32LSB 0x65 -+#define R_IA64_SECREL64MSB 0x66 -+#define R_IA64_SECREL64LSB 0x67 -+#define R_IA64_REL32MSB 0x6c -+#define R_IA64_REL32LSB 0x6d -+#define R_IA64_REL64MSB 0x6e -+#define R_IA64_REL64LSB 0x6f -+#define R_IA64_LTV32MSB 0x74 -+#define R_IA64_LTV32LSB 0x75 -+#define R_IA64_LTV64MSB 0x76 -+#define R_IA64_LTV64LSB 0x77 -+#define R_IA64_PCREL21BI 0x79 -+#define R_IA64_PCREL22 0x7a -+#define R_IA64_PCREL64I 0x7b -+#define R_IA64_IPLTMSB 0x80 -+#define R_IA64_IPLTLSB 0x81 -+#define R_IA64_COPY 0x84 -+#define R_IA64_SUB 0x85 -+#define R_IA64_LTOFF22X 0x86 -+#define R_IA64_LDXMOV 0x87 -+#define R_IA64_TPREL14 0x91 -+#define R_IA64_TPREL22 0x92 -+#define R_IA64_TPREL64I 0x93 -+#define R_IA64_TPREL64MSB 0x96 -+#define R_IA64_TPREL64LSB 0x97 -+#define R_IA64_LTOFF_TPREL22 0x9a -+#define R_IA64_DTPMOD64MSB 0xa6 -+#define R_IA64_DTPMOD64LSB 0xa7 -+#define R_IA64_LTOFF_DTPMOD22 0xaa -+#define R_IA64_DTPREL14 0xb1 -+#define R_IA64_DTPREL22 0xb2 -+#define R_IA64_DTPREL64I 0xb3 -+#define R_IA64_DTPREL32MSB 0xb4 -+#define R_IA64_DTPREL32LSB 0xb5 -+#define R_IA64_DTPREL64MSB 0xb6 -+#define R_IA64_DTPREL64LSB 0xb7 -+#define R_IA64_LTOFF_DTPREL22 0xba -+ -+ -+ -+ -+#define R_SH_NONE 0 -+#define R_SH_DIR32 1 -+#define R_SH_REL32 2 -+#define R_SH_DIR8WPN 3 -+#define R_SH_IND12W 4 -+#define R_SH_DIR8WPL 5 -+#define R_SH_DIR8WPZ 6 -+#define R_SH_DIR8BP 7 -+#define R_SH_DIR8W 8 -+#define R_SH_DIR8L 9 -+#define R_SH_SWITCH16 25 -+#define R_SH_SWITCH32 26 -+#define R_SH_USES 27 -+#define R_SH_COUNT 28 -+#define R_SH_ALIGN 29 -+#define R_SH_CODE 30 -+#define R_SH_DATA 31 -+#define R_SH_LABEL 32 -+#define R_SH_SWITCH8 33 -+#define R_SH_GNU_VTINHERIT 34 -+#define R_SH_GNU_VTENTRY 35 -+#define R_SH_TLS_GD_32 144 -+#define R_SH_TLS_LD_32 145 -+#define R_SH_TLS_LDO_32 146 -+#define R_SH_TLS_IE_32 147 -+#define R_SH_TLS_LE_32 148 -+#define R_SH_TLS_DTPMOD32 149 -+#define R_SH_TLS_DTPOFF32 150 -+#define R_SH_TLS_TPOFF32 151 -+#define R_SH_GOT32 160 -+#define R_SH_PLT32 161 -+#define R_SH_COPY 162 -+#define R_SH_GLOB_DAT 163 -+#define R_SH_JMP_SLOT 164 -+#define R_SH_RELATIVE 165 -+#define R_SH_GOTOFF 166 -+#define R_SH_GOTPC 167 -+ -+#define R_SH_NUM 256 -+ -+ -+ -+#define R_390_NONE 0 -+#define R_390_8 1 -+#define R_390_12 2 -+#define R_390_16 3 -+#define R_390_32 4 -+#define R_390_PC32 5 -+#define R_390_GOT12 6 -+#define R_390_GOT32 7 -+#define R_390_PLT32 8 -+#define R_390_COPY 9 -+#define R_390_GLOB_DAT 10 -+#define R_390_JMP_SLOT 11 -+#define R_390_RELATIVE 12 -+#define R_390_GOTOFF32 13 -+#define R_390_GOTPC 14 -+#define R_390_GOT16 15 -+#define R_390_PC16 16 -+#define R_390_PC16DBL 17 -+#define R_390_PLT16DBL 18 -+#define R_390_PC32DBL 19 -+#define R_390_PLT32DBL 20 -+#define R_390_GOTPCDBL 21 -+#define R_390_64 22 -+#define R_390_PC64 23 -+#define R_390_GOT64 24 -+#define R_390_PLT64 25 -+#define R_390_GOTENT 26 -+#define R_390_GOTOFF16 27 -+#define R_390_GOTOFF64 28 -+#define R_390_GOTPLT12 29 -+#define R_390_GOTPLT16 30 -+#define R_390_GOTPLT32 31 -+#define R_390_GOTPLT64 32 -+#define R_390_GOTPLTENT 33 -+#define R_390_PLTOFF16 34 -+#define R_390_PLTOFF32 35 -+#define R_390_PLTOFF64 36 -+#define R_390_TLS_LOAD 37 -+#define R_390_TLS_GDCALL 38 -+ -+#define R_390_TLS_LDCALL 39 -+ -+#define R_390_TLS_GD32 40 -+ -+#define R_390_TLS_GD64 41 -+ -+#define R_390_TLS_GOTIE12 42 -+ -+#define R_390_TLS_GOTIE32 43 -+ -+#define R_390_TLS_GOTIE64 44 -+ -+#define R_390_TLS_LDM32 45 -+ -+#define R_390_TLS_LDM64 46 -+ -+#define R_390_TLS_IE32 47 -+ -+#define R_390_TLS_IE64 48 -+ -+#define R_390_TLS_IEENT 49 -+ -+#define R_390_TLS_LE32 50 -+ -+#define R_390_TLS_LE64 51 -+ -+#define R_390_TLS_LDO32 52 -+ -+#define R_390_TLS_LDO64 53 -+ -+#define R_390_TLS_DTPMOD 54 -+#define R_390_TLS_DTPOFF 55 -+#define R_390_TLS_TPOFF 56 -+ -+#define R_390_20 57 -+#define R_390_GOT20 58 -+#define R_390_GOTPLT20 59 -+#define R_390_TLS_GOTIE20 60 -+ -+ -+#define R_390_NUM 61 -+ -+ -+ -+#define R_CRIS_NONE 0 -+#define R_CRIS_8 1 -+#define R_CRIS_16 2 -+#define R_CRIS_32 3 -+#define R_CRIS_8_PCREL 4 -+#define R_CRIS_16_PCREL 5 -+#define R_CRIS_32_PCREL 6 -+#define R_CRIS_GNU_VTINHERIT 7 -+#define R_CRIS_GNU_VTENTRY 8 -+#define R_CRIS_COPY 9 -+#define R_CRIS_GLOB_DAT 10 -+#define R_CRIS_JUMP_SLOT 11 -+#define R_CRIS_RELATIVE 12 -+#define R_CRIS_16_GOT 13 -+#define R_CRIS_32_GOT 14 -+#define R_CRIS_16_GOTPLT 15 -+#define R_CRIS_32_GOTPLT 16 -+#define R_CRIS_32_GOTREL 17 -+#define R_CRIS_32_PLT_GOTREL 18 -+#define R_CRIS_32_PLT_PCREL 19 -+ -+#define R_CRIS_NUM 20 -+ -+ -+ -+#define R_X86_64_NONE 0 -+#define R_X86_64_64 1 -+#define R_X86_64_PC32 2 -+#define R_X86_64_GOT32 3 -+#define R_X86_64_PLT32 4 -+#define R_X86_64_COPY 5 -+#define R_X86_64_GLOB_DAT 6 -+#define R_X86_64_JUMP_SLOT 7 -+#define R_X86_64_RELATIVE 8 -+#define R_X86_64_GOTPCREL 9 -+ -+#define R_X86_64_32 10 -+#define R_X86_64_32S 11 -+#define R_X86_64_16 12 -+#define R_X86_64_PC16 13 -+#define R_X86_64_8 14 -+#define R_X86_64_PC8 15 -+#define R_X86_64_DTPMOD64 16 -+#define R_X86_64_DTPOFF64 17 -+#define R_X86_64_TPOFF64 18 -+#define R_X86_64_TLSGD 19 -+ -+#define R_X86_64_TLSLD 20 -+ -+#define R_X86_64_DTPOFF32 21 -+#define R_X86_64_GOTTPOFF 22 -+ -+#define R_X86_64_TPOFF32 23 -+#define R_X86_64_PC64 24 -+#define R_X86_64_GOTOFF64 25 -+#define R_X86_64_GOTPC32 26 -+#define R_X86_64_GOT64 27 -+#define R_X86_64_GOTPCREL64 28 -+#define R_X86_64_GOTPC64 29 -+#define R_X86_64_GOTPLT64 30 -+#define R_X86_64_PLTOFF64 31 -+#define R_X86_64_SIZE32 32 -+#define R_X86_64_SIZE64 33 -+ -+#define R_X86_64_GOTPC32_TLSDESC 34 -+#define R_X86_64_TLSDESC_CALL 35 -+ -+#define R_X86_64_TLSDESC 36 -+#define R_X86_64_IRELATIVE 37 -+#define R_X86_64_RELATIVE64 38 -+#define R_X86_64_NUM 39 -+ -+ -+ -+#define R_MN10300_NONE 0 -+#define R_MN10300_32 1 -+#define R_MN10300_16 2 -+#define R_MN10300_8 3 -+#define R_MN10300_PCREL32 4 -+#define R_MN10300_PCREL16 5 -+#define R_MN10300_PCREL8 6 -+#define R_MN10300_GNU_VTINHERIT 7 -+#define R_MN10300_GNU_VTENTRY 8 -+#define R_MN10300_24 9 -+#define R_MN10300_GOTPC32 10 -+#define R_MN10300_GOTPC16 11 -+#define R_MN10300_GOTOFF32 12 -+#define R_MN10300_GOTOFF24 13 -+#define R_MN10300_GOTOFF16 14 -+#define R_MN10300_PLT32 15 -+#define R_MN10300_PLT16 16 -+#define R_MN10300_GOT32 17 -+#define R_MN10300_GOT24 18 -+#define R_MN10300_GOT16 19 -+#define R_MN10300_COPY 20 -+#define R_MN10300_GLOB_DAT 21 -+#define R_MN10300_JMP_SLOT 22 -+#define R_MN10300_RELATIVE 23 -+ -+#define R_MN10300_NUM 24 -+ -+ -+ -+#define R_M32R_NONE 0 -+#define R_M32R_16 1 -+#define R_M32R_32 2 -+#define R_M32R_24 3 -+#define R_M32R_10_PCREL 4 -+#define R_M32R_18_PCREL 5 -+#define R_M32R_26_PCREL 6 -+#define R_M32R_HI16_ULO 7 -+#define R_M32R_HI16_SLO 8 -+#define R_M32R_LO16 9 -+#define R_M32R_SDA16 10 -+#define R_M32R_GNU_VTINHERIT 11 -+#define R_M32R_GNU_VTENTRY 12 -+ -+#define R_M32R_16_RELA 33 -+#define R_M32R_32_RELA 34 -+#define R_M32R_24_RELA 35 -+#define R_M32R_10_PCREL_RELA 36 -+#define R_M32R_18_PCREL_RELA 37 -+#define R_M32R_26_PCREL_RELA 38 -+#define R_M32R_HI16_ULO_RELA 39 -+#define R_M32R_HI16_SLO_RELA 40 -+#define R_M32R_LO16_RELA 41 -+#define R_M32R_SDA16_RELA 42 -+#define R_M32R_RELA_GNU_VTINHERIT 43 -+#define R_M32R_RELA_GNU_VTENTRY 44 -+#define R_M32R_REL32 45 -+ -+#define R_M32R_GOT24 48 -+#define R_M32R_26_PLTREL 49 -+#define R_M32R_COPY 50 -+#define R_M32R_GLOB_DAT 51 -+#define R_M32R_JMP_SLOT 52 -+#define R_M32R_RELATIVE 53 -+#define R_M32R_GOTOFF 54 -+#define R_M32R_GOTPC24 55 -+#define R_M32R_GOT16_HI_ULO 56 -+ -+#define R_M32R_GOT16_HI_SLO 57 -+ -+#define R_M32R_GOT16_LO 58 -+#define R_M32R_GOTPC_HI_ULO 59 -+ -+#define R_M32R_GOTPC_HI_SLO 60 -+ -+#define R_M32R_GOTPC_LO 61 -+ -+#define R_M32R_GOTOFF_HI_ULO 62 -+ -+#define R_M32R_GOTOFF_HI_SLO 63 -+ -+#define R_M32R_GOTOFF_LO 64 -+#define R_M32R_NUM 256 -+ -+#define R_MICROBLAZE_NONE 0 -+#define R_MICROBLAZE_32 1 -+#define R_MICROBLAZE_32_PCREL 2 -+#define R_MICROBLAZE_64_PCREL 3 -+#define R_MICROBLAZE_32_PCREL_LO 4 -+#define R_MICROBLAZE_64 5 -+#define R_MICROBLAZE_32_LO 6 -+#define R_MICROBLAZE_SRO32 7 -+#define R_MICROBLAZE_SRW32 8 -+#define R_MICROBLAZE_64_NONE 9 -+#define R_MICROBLAZE_32_SYM_OP_SYM 10 -+#define R_MICROBLAZE_GNU_VTINHERIT 11 -+#define R_MICROBLAZE_GNU_VTENTRY 12 -+#define R_MICROBLAZE_GOTPC_64 13 -+#define R_MICROBLAZE_GOT_64 14 -+#define R_MICROBLAZE_PLT_64 15 -+#define R_MICROBLAZE_REL 16 -+#define R_MICROBLAZE_JUMP_SLOT 17 -+#define R_MICROBLAZE_GLOB_DAT 18 -+#define R_MICROBLAZE_GOTOFF_64 19 -+#define R_MICROBLAZE_GOTOFF_32 20 -+#define R_MICROBLAZE_COPY 21 -+#define R_MICROBLAZE_TLS 22 -+#define R_MICROBLAZE_TLSGD 23 -+#define R_MICROBLAZE_TLSLD 24 -+#define R_MICROBLAZE_TLSDTPMOD32 25 -+#define R_MICROBLAZE_TLSDTPREL32 26 -+#define R_MICROBLAZE_TLSDTPREL64 27 -+#define R_MICROBLAZE_TLSGOTTPREL32 28 -+#define R_MICROBLAZE_TLSTPREL32 29 -+ -+#ifdef __cplusplus -+} -+#endif -+ -+ -+#endif diff --git a/target/linux/patches/3.14.43/sgidefs.patch b/target/linux/patches/3.14.43/sgidefs.patch deleted file mode 100644 index f00a284d9..000000000 --- a/target/linux/patches/3.14.43/sgidefs.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h ---- linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h 2013-11-08 22:01:28.000000000 +0100 -@@ -11,14 +11,6 @@ - #define __ASM_SGIDEFS_H - - /* -- * Using a Linux compiler for building Linux seems logic but not to -- * everybody. -- */ --#ifndef __linux__ --#error Use a Linux compiler or give up. --#endif -- --/* - * Definitions for the ISA levels - * - * With the introduction of MIPS32 / MIPS64 instruction sets definitions diff --git a/target/linux/patches/3.14.43/sortext.patch b/target/linux/patches/3.14.43/sortext.patch deleted file mode 100644 index 8fd4e1d6b..000000000 --- a/target/linux/patches/3.14.43/sortext.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -Nur linux-3.12.6.orig/arch/arm/Kconfig linux-3.12.6/arch/arm/Kconfig ---- linux-3.12.6.orig/arch/arm/Kconfig 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/arch/arm/Kconfig 2013-12-28 19:29:33.000000000 +0100 -@@ -6,7 +6,6 @@ - select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST - select ARCH_HAVE_CUSTOM_GPIO_H - select ARCH_WANT_IPC_PARSE_VERSION -- select BUILDTIME_EXTABLE_SORT if MMU - select CLONE_BACKWARDS - select CPU_PM if (SUSPEND || CPU_IDLE) - select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && !CPU_BIG_ENDIAN && MMU -diff -Nur linux-3.12.6.orig/arch/mips/Kconfig linux-3.12.6/arch/mips/Kconfig ---- linux-3.12.6.orig/arch/mips/Kconfig 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/arch/mips/Kconfig 2013-12-28 19:30:06.000000000 +0100 -@@ -35,7 +35,6 @@ - select HAVE_MEMBLOCK_NODE_MAP - select ARCH_DISCARD_MEMBLOCK - select GENERIC_SMP_IDLE_THREAD -- select BUILDTIME_EXTABLE_SORT - select GENERIC_CLOCKEVENTS - select GENERIC_CMOS_UPDATE - select HAVE_MOD_ARCH_SPECIFIC -diff -Nur linux-3.12.6.orig/arch/x86/Kconfig linux-3.12.6/arch/x86/Kconfig ---- linux-3.12.6.orig/arch/x86/Kconfig 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/arch/x86/Kconfig 2013-12-28 19:29:50.000000000 +0100 -@@ -100,7 +100,6 @@ - select GENERIC_SMP_IDLE_THREAD - select ARCH_WANT_IPC_PARSE_VERSION if X86_32 - select HAVE_ARCH_SECCOMP_FILTER -- select BUILDTIME_EXTABLE_SORT - select GENERIC_CMOS_UPDATE - select HAVE_ARCH_SOFT_DIRTY - select CLOCKSOURCE_WATCHDOG diff --git a/target/linux/patches/3.14.43/startup.patch b/target/linux/patches/3.14.43/startup.patch deleted file mode 100644 index d396b75e4..000000000 --- a/target/linux/patches/3.14.43/startup.patch +++ /dev/null @@ -1,37 +0,0 @@ -diff -Nur linux-3.13.3.orig/init/main.c linux-3.13.3/init/main.c ---- linux-3.13.3.orig/init/main.c 2014-02-13 23:00:14.000000000 +0100 -+++ linux-3.13.3/init/main.c 2014-02-17 11:35:14.000000000 +0100 -@@ -916,6 +917,8 @@ - if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - pr_err("Warning: unable to open an initial console.\n"); - -+ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); -+ - (void) sys_dup(0); - (void) sys_dup(0); - /* -diff -Nur linux-3.13.6.orig/init/initramfs.c linux-3.13.6/init/initramfs.c ---- linux-3.13.6.orig/init/initramfs.c 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/init/initramfs.c 2014-03-15 12:11:31.882731916 +0100 -@@ -622,6 +622,9 @@ - */ - load_default_modules(); - } -+#ifdef CONFIG_DEVTMPFS_MOUNT -+ devtmpfs_mount("dev"); -+#endif - return 0; - } - rootfs_initcall(populate_rootfs); -diff -Nur linux-3.13.6.orig/init/main.c linux-3.13.6/init/main.c ---- linux-3.13.6.orig/init/main.c 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/init/main.c 2014-03-15 12:13:16.459024452 +0100 -@@ -924,7 +924,7 @@ - */ - - if (!ramdisk_execute_command) -- ramdisk_execute_command = "/init"; -+ ramdisk_execute_command = "/sbin/init"; - - if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { - ramdisk_execute_command = NULL; diff --git a/target/linux/patches/3.14.43/wlan-cf.patch b/target/linux/patches/3.14.43/wlan-cf.patch deleted file mode 100644 index fc20759e2..000000000 --- a/target/linux/patches/3.14.43/wlan-cf.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c ---- linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c 2011-05-19 06:06:34.000000000 +0200 -+++ linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c 2011-09-12 02:46:26.987984145 +0200 -@@ -623,6 +623,7 @@ - static struct pcmcia_device_id hostap_cs_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), -+ PCMCIA_DEVICE_MANF_CARD(0x0004, 0x2003), - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), - PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), - PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), diff --git a/target/linux/patches/3.14.43/xargs.patch b/target/linux/patches/3.14.43/xargs.patch deleted file mode 100644 index 2c7b3df59..000000000 --- a/target/linux/patches/3.14.43/xargs.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-3.12.6.orig/scripts/Makefile.modpost linux-3.12.6/scripts/Makefile.modpost ---- linux-3.12.6.orig/scripts/Makefile.modpost 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/scripts/Makefile.modpost 2014-01-25 14:55:33.000000000 +0100 -@@ -60,7 +60,7 @@ - modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers - - # Step 1), find all modules listed in $(MODVERDIR)/ --MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs -r grep -h '\.ko$$' | sort -u -+MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs grep -h '\.ko$$' | sort -u - __modules := $(shell $(MODLISTCMD)) - modules := $(patsubst %.o,%.ko, $(wildcard $(__modules:.ko=.o))) - diff --git a/target/linux/patches/3.14.43/yaffs2.patch b/target/linux/patches/3.14.43/yaffs2.patch deleted file mode 100644 index 30e4a790e..000000000 --- a/target/linux/patches/3.14.43/yaffs2.patch +++ /dev/null @@ -1,16559 +0,0 @@ -diff -Nur linux-3.14.43.orig/fs/Kconfig linux-3.14.43/fs/Kconfig ---- linux-3.14.43.orig/fs/Kconfig 2015-05-17 18:54:01.000000000 +0200 -+++ linux-3.14.43/fs/Kconfig 2015-06-15 21:20:48.000000000 +0200 -@@ -189,6 +189,7 @@ - source "fs/befs/Kconfig" - source "fs/bfs/Kconfig" - source "fs/efs/Kconfig" -+source "fs/yaffs2/Kconfig" - source "fs/jffs2/Kconfig" - # UBIFS File system configuration - source "fs/ubifs/Kconfig" -diff -Nur linux-3.14.43.orig/fs/Makefile linux-3.14.43/fs/Makefile ---- linux-3.14.43.orig/fs/Makefile 2015-05-17 18:54:01.000000000 +0200 -+++ linux-3.14.43/fs/Makefile 2015-06-15 21:20:48.000000000 +0200 -@@ -125,3 +125,4 @@ - obj-$(CONFIG_CEPH_FS) += ceph/ - obj-$(CONFIG_PSTORE) += pstore/ - obj-$(CONFIG_EFIVAR_FS) += efivarfs/ -+obj-$(CONFIG_YAFFS_FS) += yaffs2/ -diff -Nur linux-3.14.43.orig/fs/yaffs2/Kconfig linux-3.14.43/fs/yaffs2/Kconfig ---- linux-3.14.43.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/Kconfig 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,171 @@ -+# -+# yaffs file system configurations -+# -+ -+config YAFFS_FS -+ tristate "yaffs2 file system support" -+ default n -+ depends on MTD_BLOCK -+ select YAFFS_YAFFS1 -+ select YAFFS_YAFFS2 -+ help -+ yaffs2, or Yet Another Flash File System, is a file system -+ optimised for NAND Flash chips. -+ -+ To compile the yaffs2 file system support as a module, choose M -+ here: the module will be called yaffs2. -+ -+ If unsure, say N. -+ -+ Further information on yaffs2 is available at -+ . -+ -+config YAFFS_YAFFS1 -+ bool "512 byte / page devices" -+ depends on YAFFS_FS -+ default y -+ help -+ Enable yaffs1 support -- yaffs for 512 byte / page devices -+ -+ Not needed for 2K-page devices. -+ -+ If unsure, say Y. -+ -+config YAFFS_9BYTE_TAGS -+ bool "Use older-style on-NAND data format with pageStatus byte" -+ depends on YAFFS_YAFFS1 -+ default n -+ help -+ -+ Older-style on-NAND data format has a "pageStatus" byte to record -+ chunk/page state. This byte is zero when the page is discarded. -+ Choose this option if you have existing on-NAND data using this -+ format that you need to continue to support. New data written -+ also uses the older-style format. Note: Use of this option -+ generally requires that MTD's oob layout be adjusted to use the -+ older-style format. See notes on tags formats and MTD versions -+ in yaffs_mtdif1.c. -+ -+ If unsure, say N. -+ -+config YAFFS_DOES_ECC -+ bool "Lets yaffs do its own ECC" -+ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS -+ default n -+ help -+ This enables yaffs to use its own ECC functions instead of using -+ the ones from the generic MTD-NAND driver. -+ -+ If unsure, say N. -+ -+config YAFFS_ECC_WRONG_ORDER -+ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" -+ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS -+ default n -+ help -+ This makes yaffs_ecc.c use the same ecc byte order as Steven -+ Hill's nand_ecc.c. If not set, then you get the same ecc byte -+ order as SmartMedia. -+ -+ If unsure, say N. -+ -+config YAFFS_YAFFS2 -+ bool "2048 byte (or larger) / page devices" -+ depends on YAFFS_FS -+ default y -+ help -+ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices -+ -+ If unsure, say Y. -+ -+config YAFFS_AUTO_YAFFS2 -+ bool "Autoselect yaffs2 format" -+ depends on YAFFS_YAFFS2 -+ default y -+ help -+ Without this, you need to explicitely use yaffs2 as the file -+ system type. With this, you can say "yaffs" and yaffs or yaffs2 -+ will be used depending on the device page size (yaffs on -+ 512-byte page devices, yaffs2 on 2K page devices). -+ -+ If unsure, say Y. -+ -+config YAFFS_DISABLE_TAGS_ECC -+ bool "Disable yaffs from doing ECC on tags by default" -+ depends on YAFFS_FS && YAFFS_YAFFS2 -+ default n -+ help -+ This defaults yaffs to using its own ECC calculations on tags instead of -+ just relying on the MTD. -+ This behavior can also be overridden with tags_ecc_on and -+ tags_ecc_off mount options. -+ -+ If unsure, say N. -+ -+config YAFFS_ALWAYS_CHECK_CHUNK_ERASED -+ bool "Force chunk erase check" -+ depends on YAFFS_FS -+ default n -+ help -+ Normally yaffs only checks chunks before writing until an erased -+ chunk is found. This helps to detect any partially written -+ chunks that might have happened due to power loss. -+ -+ Enabling this forces on the test that chunks are erased in flash -+ before writing to them. This takes more time but is potentially -+ a bit more secure. -+ -+ Suggest setting Y during development and ironing out driver -+ issues etc. Suggest setting to N if you want faster writing. -+ -+ If unsure, say Y. -+ -+config YAFFS_EMPTY_LOST_AND_FOUND -+ bool "Empty lost and found on boot" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is enabled then the contents of lost and found is -+ automatically dumped at mount. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BLOCK_REFRESHING -+ bool "Disable yaffs2 block refreshing" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is set, then block refreshing is disabled. -+ Block refreshing infrequently refreshes the oldest block in -+ a yaffs2 file system. This mechanism helps to refresh flash to -+ mitigate against data loss. This is particularly useful for MLC. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BACKGROUND -+ bool "Disable yaffs2 background processing" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is set, then background processing is disabled. -+ Background processing makes many foreground activities faster. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BAD_BLOCK_MARKING -+ bool "Disable yaffs2 bad block marking" -+ depends on YAFFS_FS -+ default n -+ help -+ Useful during early flash bring up to prevent problems causing -+ lots of bad block marking. -+ -+ If unsure, say N. -+ -+config YAFFS_XATTR -+ bool "Enable yaffs2 xattr support" -+ depends on YAFFS_FS -+ default y -+ help -+ If this is set then yaffs2 will provide xattr support. -+ If unsure, say Y. -diff -Nur linux-3.14.43.orig/fs/yaffs2/Makefile linux-3.14.43/fs/yaffs2/Makefile ---- linux-3.14.43.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/Makefile 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,18 @@ -+# -+# Makefile for the linux YAFFS filesystem routines. -+# -+ -+obj-$(CONFIG_YAFFS_FS) += yaffs.o -+ -+yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o -+yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o -+yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o -+yaffs-y += yaffs_mtdif.o -+yaffs-y += yaffs_nameval.o yaffs_attribs.o -+yaffs-y += yaffs_allocator.o -+yaffs-y += yaffs_yaffs1.o -+yaffs-y += yaffs_yaffs2.o -+yaffs-y += yaffs_bitmap.o -+yaffs-y += yaffs_summary.o -+yaffs-y += yaffs_verify.o -+ -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_allocator.c linux-3.14.43/fs/yaffs2/yaffs_allocator.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_allocator.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,357 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_allocator.h" -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yportenv.h" -+ -+/* -+ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks -+ * of approx 100 objects that are themn allocated singly. -+ * This is basically a simplified slab allocator. -+ * -+ * We don't use the Linux slab allocator because slab does not allow -+ * us to dump all the objects in one hit when we do a umount and tear -+ * down all the tnodes and objects. slab requires that we first free -+ * the individual objects. -+ * -+ * Once yaffs has been mainlined I shall try to motivate for a change -+ * to slab to provide the extra features we need here. -+ */ -+ -+struct yaffs_tnode_list { -+ struct yaffs_tnode_list *next; -+ struct yaffs_tnode *tnodes; -+}; -+ -+struct yaffs_obj_list { -+ struct yaffs_obj_list *next; -+ struct yaffs_obj *objects; -+}; -+ -+struct yaffs_allocator { -+ int n_tnodes_created; -+ struct yaffs_tnode *free_tnodes; -+ int n_free_tnodes; -+ struct yaffs_tnode_list *alloc_tnode_list; -+ -+ int n_obj_created; -+ struct list_head free_objs; -+ int n_free_objects; -+ -+ struct yaffs_obj_list *allocated_obj_list; -+}; -+ -+static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ struct yaffs_tnode_list *tmp; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ while (allocator->alloc_tnode_list) { -+ tmp = allocator->alloc_tnode_list->next; -+ -+ kfree(allocator->alloc_tnode_list->tnodes); -+ kfree(allocator->alloc_tnode_list); -+ allocator->alloc_tnode_list = tmp; -+ } -+ -+ allocator->free_tnodes = NULL; -+ allocator->n_free_tnodes = 0; -+ allocator->n_tnodes_created = 0; -+} -+ -+static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator->alloc_tnode_list = NULL; -+ allocator->free_tnodes = NULL; -+ allocator->n_free_tnodes = 0; -+ allocator->n_tnodes_created = 0; -+} -+ -+static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ int i; -+ struct yaffs_tnode *new_tnodes; -+ u8 *mem; -+ struct yaffs_tnode *curr; -+ struct yaffs_tnode *next; -+ struct yaffs_tnode_list *tnl; -+ -+ if (!allocator) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ if (n_tnodes < 1) -+ return YAFFS_OK; -+ -+ /* make these things */ -+ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); -+ mem = (u8 *) new_tnodes; -+ -+ if (!new_tnodes) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs: Could not allocate Tnodes"); -+ return YAFFS_FAIL; -+ } -+ -+ /* New hookup for wide tnodes */ -+ for (i = 0; i < n_tnodes - 1; i++) { -+ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; -+ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; -+ curr->internal[0] = next; -+ } -+ -+ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; -+ curr->internal[0] = allocator->free_tnodes; -+ allocator->free_tnodes = (struct yaffs_tnode *)mem; -+ -+ allocator->n_free_tnodes += n_tnodes; -+ allocator->n_tnodes_created += n_tnodes; -+ -+ /* Now add this bunch of tnodes to a list for freeing up. -+ * NB If we can't add this to the management list it isn't fatal -+ * but it just means we can't free this bunch of tnodes later. -+ */ -+ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); -+ if (!tnl) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Could not add tnodes to management list"); -+ return YAFFS_FAIL; -+ } else { -+ tnl->tnodes = new_tnodes; -+ tnl->next = allocator->alloc_tnode_list; -+ allocator->alloc_tnode_list = tnl; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); -+ -+ return YAFFS_OK; -+} -+ -+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ struct yaffs_tnode *tn = NULL; -+ -+ if (!allocator) { -+ BUG(); -+ return NULL; -+ } -+ -+ /* If there are none left make more */ -+ if (!allocator->free_tnodes) -+ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); -+ -+ if (allocator->free_tnodes) { -+ tn = allocator->free_tnodes; -+ allocator->free_tnodes = allocator->free_tnodes->internal[0]; -+ allocator->n_free_tnodes--; -+ } -+ -+ return tn; -+} -+ -+/* FreeTnode frees up a tnode and puts it back on the free list */ -+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ if (tn) { -+ tn->internal[0] = allocator->free_tnodes; -+ allocator->free_tnodes = tn; -+ allocator->n_free_tnodes++; -+ } -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+/*--------------- yaffs_obj alloaction ------------------------ -+ * -+ * Free yaffs_objs are stored in a list using obj->siblings. -+ * The blocks of allocated objects are stored in a linked list. -+ */ -+ -+static void yaffs_init_raw_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator->allocated_obj_list = NULL; -+ INIT_LIST_HEAD(&allocator->free_objs); -+ allocator->n_free_objects = 0; -+} -+ -+static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ struct yaffs_obj_list *tmp; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ while (allocator->allocated_obj_list) { -+ tmp = allocator->allocated_obj_list->next; -+ kfree(allocator->allocated_obj_list->objects); -+ kfree(allocator->allocated_obj_list); -+ allocator->allocated_obj_list = tmp; -+ } -+ -+ INIT_LIST_HEAD(&allocator->free_objs); -+ allocator->n_free_objects = 0; -+ allocator->n_obj_created = 0; -+} -+ -+static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ int i; -+ struct yaffs_obj *new_objs; -+ struct yaffs_obj_list *list; -+ -+ if (!allocator) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ if (n_obj < 1) -+ return YAFFS_OK; -+ -+ /* make these things */ -+ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); -+ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); -+ -+ if (!new_objs || !list) { -+ kfree(new_objs); -+ new_objs = NULL; -+ kfree(list); -+ list = NULL; -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, -+ "Could not allocate more objects"); -+ return YAFFS_FAIL; -+ } -+ -+ /* Hook them into the free list */ -+ for (i = 0; i < n_obj; i++) -+ list_add(&new_objs[i].siblings, &allocator->free_objs); -+ -+ allocator->n_free_objects += n_obj; -+ allocator->n_obj_created += n_obj; -+ -+ /* Now add this bunch of Objects to a list for freeing up. */ -+ -+ list->objects = new_objs; -+ list->next = allocator->allocated_obj_list; -+ allocator->allocated_obj_list = list; -+ -+ return YAFFS_OK; -+} -+ -+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj = NULL; -+ struct list_head *lh; -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return obj; -+ } -+ -+ /* If there are none left make more */ -+ if (list_empty(&allocator->free_objs)) -+ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); -+ -+ if (!list_empty(&allocator->free_objs)) { -+ lh = allocator->free_objs.next; -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ list_del_init(lh); -+ allocator->n_free_objects--; -+ } -+ -+ return obj; -+} -+ -+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) -+{ -+ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ /* Link into the free list. */ -+ list_add(&obj->siblings, &allocator->free_objs); -+ allocator->n_free_objects++; -+} -+ -+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ -+ if (!dev->allocator) { -+ BUG(); -+ return; -+ } -+ -+ yaffs_deinit_raw_tnodes(dev); -+ yaffs_deinit_raw_objs(dev); -+ kfree(dev->allocator); -+ dev->allocator = NULL; -+} -+ -+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator; -+ -+ if (dev->allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); -+ if (allocator) { -+ dev->allocator = allocator; -+ yaffs_init_raw_tnodes(dev); -+ yaffs_init_raw_objs(dev); -+ } -+} -+ -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_allocator.h linux-3.14.43/fs/yaffs2/yaffs_allocator.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_allocator.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,30 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_ALLOCATOR_H__ -+#define __YAFFS_ALLOCATOR_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); -+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); -+ -+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); -+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); -+ -+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); -+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_attribs.c linux-3.14.43/fs/yaffs2/yaffs_attribs.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_attribs.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,132 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_attribs.h" -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) -+#define IATTR_UID ia_uid -+#define IATTR_GID ia_gid -+#else -+#define IATTR_UID ia_uid.val -+#define IATTR_GID ia_gid.val -+#endif -+ -+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) -+{ -+ obj->yst_uid = oh->yst_uid; -+ obj->yst_gid = oh->yst_gid; -+ obj->yst_atime = oh->yst_atime; -+ obj->yst_mtime = oh->yst_mtime; -+ obj->yst_ctime = oh->yst_ctime; -+ obj->yst_rdev = oh->yst_rdev; -+} -+ -+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) -+{ -+ oh->yst_uid = obj->yst_uid; -+ oh->yst_gid = obj->yst_gid; -+ oh->yst_atime = obj->yst_atime; -+ oh->yst_mtime = obj->yst_mtime; -+ oh->yst_ctime = obj->yst_ctime; -+ oh->yst_rdev = obj->yst_rdev; -+ -+} -+ -+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) -+{ -+ obj->yst_mtime = Y_CURRENT_TIME; -+ if (do_a) -+ obj->yst_atime = obj->yst_mtime; -+ if (do_c) -+ obj->yst_ctime = obj->yst_mtime; -+} -+ -+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) -+{ -+ yaffs_load_current_time(obj, 1, 1); -+ obj->yst_rdev = rdev; -+ obj->yst_uid = uid; -+ obj->yst_gid = gid; -+} -+ -+static loff_t yaffs_get_file_size(struct yaffs_obj *obj) -+{ -+ YCHAR *alias = NULL; -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return obj->variant.file_variant.file_size; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ alias = obj->variant.symlink_variant.alias; -+ if (!alias) -+ return 0; -+ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); -+ default: -+ return 0; -+ } -+} -+ -+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) -+{ -+ unsigned int valid = attr->ia_valid; -+ -+ if (valid & ATTR_MODE) -+ obj->yst_mode = attr->ia_mode; -+ if (valid & ATTR_UID) -+ obj->yst_uid = attr->IATTR_UID; -+ if (valid & ATTR_GID) -+ obj->yst_gid = attr->IATTR_GID; -+ -+ if (valid & ATTR_ATIME) -+ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); -+ if (valid & ATTR_CTIME) -+ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); -+ if (valid & ATTR_MTIME) -+ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); -+ -+ if (valid & ATTR_SIZE) -+ yaffs_resize_file(obj, attr->ia_size); -+ -+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); -+ -+ return YAFFS_OK; -+ -+} -+ -+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) -+{ -+ unsigned int valid = 0; -+ -+ attr->ia_mode = obj->yst_mode; -+ valid |= ATTR_MODE; -+ attr->IATTR_UID = obj->yst_uid; -+ valid |= ATTR_UID; -+ attr->IATTR_GID = obj->yst_gid; -+ valid |= ATTR_GID; -+ -+ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; -+ valid |= ATTR_ATIME; -+ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; -+ valid |= ATTR_CTIME; -+ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; -+ valid |= ATTR_MTIME; -+ -+ attr->ia_size = yaffs_get_file_size(obj); -+ valid |= ATTR_SIZE; -+ -+ attr->ia_valid = valid; -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_attribs.h linux-3.14.43/fs/yaffs2/yaffs_attribs.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_attribs.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,28 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_ATTRIBS_H__ -+#define __YAFFS_ATTRIBS_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); -+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); -+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); -+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); -+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); -+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_bitmap.c linux-3.14.43/fs/yaffs2/yaffs_bitmap.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_bitmap.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,97 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_bitmap.h" -+#include "yaffs_trace.h" -+/* -+ * Chunk bitmap manipulations -+ */ -+ -+static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "BlockBits block %d is not valid", -+ blk); -+ BUG(); -+ } -+ return dev->chunk_bits + -+ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); -+} -+ -+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block || -+ chunk < 0 || chunk >= dev->param.chunks_per_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Chunk Id (%d:%d) invalid", -+ blk, chunk); -+ BUG(); -+ } -+} -+ -+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ memset(blk_bits, 0, dev->chunk_bit_stride); -+} -+ -+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); -+} -+ -+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ blk_bits[chunk / 8] |= (1 << (chunk & 7)); -+} -+ -+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; -+} -+ -+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ int i; -+ -+ for (i = 0; i < dev->chunk_bit_stride; i++) { -+ if (*blk_bits) -+ return 1; -+ blk_bits++; -+ } -+ return 0; -+} -+ -+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ int i; -+ int n = 0; -+ -+ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) -+ n += hweight8(*blk_bits); -+ -+ return n; -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_bitmap.h linux-3.14.43/fs/yaffs2/yaffs_bitmap.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_bitmap.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,33 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* -+ * Chunk bitmap manipulations -+ */ -+ -+#ifndef __YAFFS_BITMAP_H__ -+#define __YAFFS_BITMAP_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); -+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); -+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); -+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.14.43/fs/yaffs2/yaffs_checkptrw.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_checkptrw.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,466 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_checkptrw.h" -+#include "yaffs_getblockinfo.h" -+ -+struct yaffs_checkpt_chunk_hdr { -+ int version; -+ int seq; -+ u32 sum; -+ u32 xor; -+} ; -+ -+ -+static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) -+{ -+ return chunk - dev->chunk_offset; -+} -+ -+static int apply_block_offset(struct yaffs_dev *dev, int block) -+{ -+ return block - dev->block_offset; -+} -+ -+static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_chunk_hdr hdr; -+ -+ hdr.version = YAFFS_CHECKPOINT_VERSION; -+ hdr.seq = dev->checkpt_page_seq; -+ hdr.sum = dev->checkpt_sum; -+ hdr.xor = dev->checkpt_xor; -+ -+ dev->checkpt_byte_offs = sizeof(hdr); -+ -+ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); -+} -+ -+static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_chunk_hdr hdr; -+ -+ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); -+ -+ dev->checkpt_byte_offs = sizeof(hdr); -+ -+ return hdr.version == YAFFS_CHECKPOINT_VERSION && -+ hdr.seq == dev->checkpt_page_seq && -+ hdr.sum == dev->checkpt_sum && -+ hdr.xor == dev->checkpt_xor; -+} -+ -+static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) -+{ -+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpt blocks_avail = %d", blocks_avail); -+ -+ return (blocks_avail <= 0) ? 0 : 1; -+} -+ -+static int yaffs_checkpt_erase(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (!dev->drv.drv_erase_fn) -+ return 0; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checking blocks %d to %d", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); -+ int offset_i = apply_block_offset(dev, i); -+ int result; -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "erasing checkpt block %d", i); -+ -+ dev->n_erasures++; -+ -+ result = dev->drv.drv_erase_fn(dev, offset_i); -+ if(result) { -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += -+ dev->param.chunks_per_block; -+ } else { -+ dev->drv.drv_mark_bad_fn(dev, offset_i); -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ } -+ } -+ } -+ -+ dev->blocks_in_checkpt = 0; -+ -+ return 1; -+} -+ -+static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) -+{ -+ int i; -+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "allocating checkpt block: erased %d reserved %d avail %d next %d ", -+ dev->n_erased_blocks, dev->param.n_reserved_blocks, -+ blocks_avail, dev->checkpt_next_block); -+ -+ if (dev->checkpt_next_block >= 0 && -+ dev->checkpt_next_block <= dev->internal_end_block && -+ blocks_avail > 0) { -+ -+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; -+ i++) { -+ struct yaffs_block_info *bi; -+ -+ bi = yaffs_get_block_info(dev, i); -+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ dev->checkpt_next_block = i + 1; -+ dev->checkpt_cur_block = i; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "allocating checkpt block %d", i); -+ return; -+ } -+ } -+ } -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); -+ -+ dev->checkpt_next_block = -1; -+ dev->checkpt_cur_block = -1; -+} -+ -+static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) -+{ -+ int i; -+ struct yaffs_ext_tags tags; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "find next checkpt block: start: blocks %d next %d", -+ dev->blocks_in_checkpt, dev->checkpt_next_block); -+ -+ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) -+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; -+ i++) { -+ int chunk = i * dev->param.chunks_per_block; -+ enum yaffs_block_state state; -+ u32 seq; -+ -+ dev->tagger.read_chunk_tags_fn(dev, -+ apply_chunk_offset(dev, chunk), -+ NULL, &tags); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", -+ i, (int) state, -+ tags.obj_id, tags.seq_number, -+ tags.ecc_result); -+ -+ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ continue; -+ -+ dev->tagger.query_block_fn(dev, -+ apply_block_offset(dev, i), -+ &state, &seq); -+ if (state == YAFFS_BLOCK_STATE_DEAD) -+ continue; -+ -+ /* Right kind of block */ -+ dev->checkpt_next_block = tags.obj_id; -+ dev->checkpt_cur_block = i; -+ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; -+ dev->blocks_in_checkpt++; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "found checkpt block %d", i); -+ return; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); -+ -+ dev->checkpt_next_block = -1; -+ dev->checkpt_cur_block = -1; -+} -+ -+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) -+{ -+ int i; -+ -+ dev->checkpt_open_write = writing; -+ -+ /* Got the functions we need? */ -+ if (!dev->tagger.write_chunk_tags_fn || -+ !dev->tagger.read_chunk_tags_fn || -+ !dev->drv.drv_erase_fn || -+ !dev->drv.drv_mark_bad_fn) -+ return 0; -+ -+ if (writing && !yaffs2_checkpt_space_ok(dev)) -+ return 0; -+ -+ if (!dev->checkpt_buffer) -+ dev->checkpt_buffer = -+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ dev->checkpt_page_seq = 0; -+ dev->checkpt_byte_count = 0; -+ dev->checkpt_sum = 0; -+ dev->checkpt_xor = 0; -+ dev->checkpt_cur_block = -1; -+ dev->checkpt_cur_chunk = -1; -+ dev->checkpt_next_block = dev->internal_start_block; -+ -+ if (writing) { -+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); -+ yaffs2_checkpt_init_chunk_hdr(dev); -+ return yaffs_checkpt_erase(dev); -+ } -+ -+ /* Opening for a read */ -+ /* Set to a value that will kick off a read */ -+ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; -+ /* A checkpoint block list of 1 checkpoint block per 16 block is -+ * (hopefully) going to be way more than we need */ -+ dev->blocks_in_checkpt = 0; -+ dev->checkpt_max_blocks = -+ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; -+ if (!dev->checkpt_block_list) -+ dev->checkpt_block_list = -+ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); -+ -+ if (!dev->checkpt_block_list) -+ return 0; -+ -+ for (i = 0; i < dev->checkpt_max_blocks; i++) -+ dev->checkpt_block_list[i] = -1; -+ -+ return 1; -+} -+ -+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) -+{ -+ u32 composite_sum; -+ -+ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); -+ *sum = composite_sum; -+ return 1; -+} -+ -+static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) -+{ -+ int chunk; -+ int offset_chunk; -+ struct yaffs_ext_tags tags; -+ -+ if (dev->checkpt_cur_block < 0) { -+ yaffs2_checkpt_find_erased_block(dev); -+ dev->checkpt_cur_chunk = 0; -+ } -+ -+ if (dev->checkpt_cur_block < 0) -+ return 0; -+ -+ tags.is_deleted = 0; -+ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ -+ tags.chunk_id = dev->checkpt_page_seq + 1; -+ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; -+ tags.n_bytes = dev->data_bytes_per_chunk; -+ if (dev->checkpt_cur_chunk == 0) { -+ /* First chunk we write for the block? Set block state to -+ checkpoint */ -+ struct yaffs_block_info *bi = -+ yaffs_get_block_info(dev, dev->checkpt_cur_block); -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ dev->blocks_in_checkpt++; -+ } -+ -+ chunk = -+ dev->checkpt_cur_block * dev->param.chunks_per_block + -+ dev->checkpt_cur_chunk; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", -+ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, -+ tags.obj_id, tags.chunk_id); -+ -+ offset_chunk = apply_chunk_offset(dev, chunk); -+ -+ dev->n_page_writes++; -+ -+ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, -+ dev->checkpt_buffer, &tags); -+ dev->checkpt_page_seq++; -+ dev->checkpt_cur_chunk++; -+ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { -+ dev->checkpt_cur_chunk = 0; -+ dev->checkpt_cur_block = -1; -+ } -+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); -+ -+ yaffs2_checkpt_init_chunk_hdr(dev); -+ -+ -+ return 1; -+} -+ -+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) -+{ -+ int i = 0; -+ int ok = 1; -+ u8 *data_bytes = (u8 *) data; -+ -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ if (!dev->checkpt_open_write) -+ return -1; -+ -+ while (i < n_bytes && ok) { -+ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; -+ dev->checkpt_sum += *data_bytes; -+ dev->checkpt_xor ^= *data_bytes; -+ -+ dev->checkpt_byte_offs++; -+ i++; -+ data_bytes++; -+ dev->checkpt_byte_count++; -+ -+ if (dev->checkpt_byte_offs < 0 || -+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) -+ ok = yaffs2_checkpt_flush_buffer(dev); -+ } -+ -+ return i; -+} -+ -+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) -+{ -+ int i = 0; -+ struct yaffs_ext_tags tags; -+ int chunk; -+ int offset_chunk; -+ u8 *data_bytes = (u8 *) data; -+ -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ if (dev->checkpt_open_write) -+ return -1; -+ -+ while (i < n_bytes) { -+ -+ if (dev->checkpt_byte_offs < 0 || -+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { -+ -+ if (dev->checkpt_cur_block < 0) { -+ yaffs2_checkpt_find_block(dev); -+ dev->checkpt_cur_chunk = 0; -+ } -+ -+ /* Bail out if we can't find a checpoint block */ -+ if (dev->checkpt_cur_block < 0) -+ break; -+ -+ chunk = dev->checkpt_cur_block * -+ dev->param.chunks_per_block + -+ dev->checkpt_cur_chunk; -+ -+ offset_chunk = apply_chunk_offset(dev, chunk); -+ dev->n_page_reads++; -+ -+ /* Read in the next chunk */ -+ dev->tagger.read_chunk_tags_fn(dev, -+ offset_chunk, -+ dev->checkpt_buffer, -+ &tags); -+ -+ /* Bail out if the chunk is corrupted. */ -+ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || -+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || -+ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ break; -+ -+ /* Bail out if it is not a checkpoint chunk. */ -+ if(!yaffs2_checkpt_check_chunk_hdr(dev)) -+ break; -+ -+ dev->checkpt_page_seq++; -+ dev->checkpt_cur_chunk++; -+ -+ if (dev->checkpt_cur_chunk >= -+ dev->param.chunks_per_block) -+ dev->checkpt_cur_block = -1; -+ -+ } -+ -+ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; -+ dev->checkpt_sum += *data_bytes; -+ dev->checkpt_xor ^= *data_bytes; -+ dev->checkpt_byte_offs++; -+ i++; -+ data_bytes++; -+ dev->checkpt_byte_count++; -+ } -+ -+ return i; /* Number of bytes read */ -+} -+ -+int yaffs_checkpt_close(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (dev->checkpt_open_write) { -+ if (dev->checkpt_byte_offs != -+ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) -+ yaffs2_checkpt_flush_buffer(dev); -+ } else if (dev->checkpt_block_list) { -+ for (i = 0; -+ i < dev->blocks_in_checkpt && -+ dev->checkpt_block_list[i] >= 0; i++) { -+ int blk = dev->checkpt_block_list[i]; -+ struct yaffs_block_info *bi = NULL; -+ -+ if (dev->internal_start_block <= blk && -+ blk <= dev->internal_end_block) -+ bi = yaffs_get_block_info(dev, blk); -+ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ } -+ } -+ -+ dev->n_free_chunks -= -+ dev->blocks_in_checkpt * dev->param.chunks_per_block; -+ dev->n_erased_blocks -= dev->blocks_in_checkpt; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", -+ dev->checkpt_byte_count); -+ -+ if (dev->checkpt_buffer) -+ return 1; -+ else -+ return 0; -+} -+ -+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) -+{ -+ /* Erase the checkpoint data */ -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpoint invalidate of %d blocks", -+ dev->blocks_in_checkpt); -+ -+ return yaffs_checkpt_erase(dev); -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.14.43/fs/yaffs2/yaffs_checkptrw.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_checkptrw.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,33 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_CHECKPTRW_H__ -+#define __YAFFS_CHECKPTRW_H__ -+ -+#include "yaffs_guts.h" -+ -+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); -+ -+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); -+ -+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); -+ -+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); -+ -+int yaffs_checkpt_close(struct yaffs_dev *dev); -+ -+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_ecc.c linux-3.14.43/fs/yaffs2/yaffs_ecc.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_ecc.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,281 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * This code implements the ECC algorithm used in SmartMedia. -+ * -+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. -+ * The two unused bit are set to 1. -+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two -+ * such ECC blocks are used on a 512-byte NAND page. -+ * -+ */ -+ -+#include "yportenv.h" -+ -+#include "yaffs_ecc.h" -+ -+/* Table generated by gen-ecc.c -+ * Using a table means we do not have to calculate p1..p4 and p1'..p4' -+ * for each byte of data. These are instead provided in a table in bits7..2. -+ * Bit 0 of each entry indicates whether the entry has an odd or even parity, -+ * and therefore this bytes influence on the line parity. -+ */ -+ -+static const unsigned char column_parity_table[] = { -+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, -+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, -+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, -+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, -+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, -+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, -+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, -+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, -+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, -+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, -+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, -+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, -+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, -+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, -+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, -+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, -+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, -+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, -+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, -+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, -+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, -+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, -+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, -+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, -+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, -+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, -+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, -+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, -+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, -+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, -+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, -+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, -+}; -+ -+ -+/* Calculate the ECC for a 256-byte block of data */ -+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) -+{ -+ unsigned int i; -+ unsigned char col_parity = 0; -+ unsigned char line_parity = 0; -+ unsigned char line_parity_prime = 0; -+ unsigned char t; -+ unsigned char b; -+ -+ for (i = 0; i < 256; i++) { -+ b = column_parity_table[*data++]; -+ col_parity ^= b; -+ -+ if (b & 0x01) { /* odd number of bits in the byte */ -+ line_parity ^= i; -+ line_parity_prime ^= ~i; -+ } -+ } -+ -+ ecc[2] = (~col_parity) | 0x03; -+ -+ t = 0; -+ if (line_parity & 0x80) -+ t |= 0x80; -+ if (line_parity_prime & 0x80) -+ t |= 0x40; -+ if (line_parity & 0x40) -+ t |= 0x20; -+ if (line_parity_prime & 0x40) -+ t |= 0x10; -+ if (line_parity & 0x20) -+ t |= 0x08; -+ if (line_parity_prime & 0x20) -+ t |= 0x04; -+ if (line_parity & 0x10) -+ t |= 0x02; -+ if (line_parity_prime & 0x10) -+ t |= 0x01; -+ ecc[1] = ~t; -+ -+ t = 0; -+ if (line_parity & 0x08) -+ t |= 0x80; -+ if (line_parity_prime & 0x08) -+ t |= 0x40; -+ if (line_parity & 0x04) -+ t |= 0x20; -+ if (line_parity_prime & 0x04) -+ t |= 0x10; -+ if (line_parity & 0x02) -+ t |= 0x08; -+ if (line_parity_prime & 0x02) -+ t |= 0x04; -+ if (line_parity & 0x01) -+ t |= 0x02; -+ if (line_parity_prime & 0x01) -+ t |= 0x01; -+ ecc[0] = ~t; -+ -+} -+ -+/* Correct the ECC on a 256 byte block of data */ -+ -+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, -+ const unsigned char *test_ecc) -+{ -+ unsigned char d0, d1, d2; /* deltas */ -+ -+ d0 = read_ecc[0] ^ test_ecc[0]; -+ d1 = read_ecc[1] ^ test_ecc[1]; -+ d2 = read_ecc[2] ^ test_ecc[2]; -+ -+ if ((d0 | d1 | d2) == 0) -+ return 0; /* no error */ -+ -+ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && -+ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && -+ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { -+ /* Single bit (recoverable) error in data */ -+ -+ unsigned byte; -+ unsigned bit; -+ -+ bit = byte = 0; -+ -+ if (d1 & 0x80) -+ byte |= 0x80; -+ if (d1 & 0x20) -+ byte |= 0x40; -+ if (d1 & 0x08) -+ byte |= 0x20; -+ if (d1 & 0x02) -+ byte |= 0x10; -+ if (d0 & 0x80) -+ byte |= 0x08; -+ if (d0 & 0x20) -+ byte |= 0x04; -+ if (d0 & 0x08) -+ byte |= 0x02; -+ if (d0 & 0x02) -+ byte |= 0x01; -+ -+ if (d2 & 0x80) -+ bit |= 0x04; -+ if (d2 & 0x20) -+ bit |= 0x02; -+ if (d2 & 0x08) -+ bit |= 0x01; -+ -+ data[byte] ^= (1 << bit); -+ -+ return 1; /* Corrected the error */ -+ } -+ -+ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { -+ /* Reccoverable error in ecc */ -+ -+ read_ecc[0] = test_ecc[0]; -+ read_ecc[1] = test_ecc[1]; -+ read_ecc[2] = test_ecc[2]; -+ -+ return 1; /* Corrected the error */ -+ } -+ -+ /* Unrecoverable error */ -+ -+ return -1; -+ -+} -+ -+/* -+ * ECCxxxOther does ECC calcs on arbitrary n bytes of data -+ */ -+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *ecc_other) -+{ -+ unsigned int i; -+ unsigned char col_parity = 0; -+ unsigned line_parity = 0; -+ unsigned line_parity_prime = 0; -+ unsigned char b; -+ -+ for (i = 0; i < n_bytes; i++) { -+ b = column_parity_table[*data++]; -+ col_parity ^= b; -+ -+ if (b & 0x01) { -+ /* odd number of bits in the byte */ -+ line_parity ^= i; -+ line_parity_prime ^= ~i; -+ } -+ -+ } -+ -+ ecc_other->col_parity = (col_parity >> 2) & 0x3f; -+ ecc_other->line_parity = line_parity; -+ ecc_other->line_parity_prime = line_parity_prime; -+} -+ -+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *read_ecc, -+ const struct yaffs_ecc_other *test_ecc) -+{ -+ unsigned char delta_col; /* column parity delta */ -+ unsigned delta_line; /* line parity delta */ -+ unsigned delta_line_prime; /* line parity delta */ -+ unsigned bit; -+ -+ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; -+ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; -+ delta_line_prime = -+ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; -+ -+ if ((delta_col | delta_line | delta_line_prime) == 0) -+ return 0; /* no error */ -+ -+ if (delta_line == ~delta_line_prime && -+ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { -+ /* Single bit (recoverable) error in data */ -+ -+ bit = 0; -+ -+ if (delta_col & 0x20) -+ bit |= 0x04; -+ if (delta_col & 0x08) -+ bit |= 0x02; -+ if (delta_col & 0x02) -+ bit |= 0x01; -+ -+ if (delta_line >= n_bytes) -+ return -1; -+ -+ data[delta_line] ^= (1 << bit); -+ -+ return 1; /* corrected */ -+ } -+ -+ if ((hweight32(delta_line) + -+ hweight32(delta_line_prime) + -+ hweight8(delta_col)) == 1) { -+ /* Reccoverable error in ecc */ -+ -+ *read_ecc = *test_ecc; -+ return 1; /* corrected */ -+ } -+ -+ /* Unrecoverable error */ -+ -+ return -1; -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_ecc.h linux-3.14.43/fs/yaffs2/yaffs_ecc.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_ecc.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,44 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* -+ * This code implements the ECC algorithm used in SmartMedia. -+ * -+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. -+ * The two unused bit are set to 1. -+ * The ECC can correct single bit errors in a 256-byte page of data. -+ * Thus, two such ECC blocks are used on a 512-byte NAND page. -+ * -+ */ -+ -+#ifndef __YAFFS_ECC_H__ -+#define __YAFFS_ECC_H__ -+ -+struct yaffs_ecc_other { -+ unsigned char col_parity; -+ unsigned line_parity; -+ unsigned line_parity_prime; -+}; -+ -+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); -+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, -+ const unsigned char *test_ecc); -+ -+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *ecc); -+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *read_ecc, -+ const struct yaffs_ecc_other *test_ecc); -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.14.43/fs/yaffs2/yaffs_getblockinfo.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_getblockinfo.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,35 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_GETBLOCKINFO_H__ -+#define __YAFFS_GETBLOCKINFO_H__ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+ -+/* Function to manipulate block info */ -+static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev -+ *dev, int blk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs: get_block_info block %d is not valid", -+ blk); -+ BUG(); -+ } -+ return &dev->block_info[blk - dev->internal_start_block]; -+} -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_guts.c linux-3.14.43/fs/yaffs2/yaffs_guts.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_guts.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,5140 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yportenv.h" -+#include "yaffs_trace.h" -+ -+#include "yaffs_guts.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_tagscompat.h" -+#include "yaffs_tagsmarshall.h" -+#include "yaffs_nand.h" -+#include "yaffs_yaffs1.h" -+#include "yaffs_yaffs2.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_verify.h" -+#include "yaffs_nand.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_nameval.h" -+#include "yaffs_allocator.h" -+#include "yaffs_attribs.h" -+#include "yaffs_summary.h" -+ -+/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ -+#define YAFFS_GC_GOOD_ENOUGH 2 -+#define YAFFS_GC_PASSIVE_THRESHOLD 4 -+ -+#include "yaffs_ecc.h" -+ -+/* Forward declarations */ -+ -+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, -+ const u8 *buffer, int n_bytes, int use_reserve); -+ -+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, -+ int buffer_size); -+ -+/* Function to calculate chunk and offset */ -+ -+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, -+ int *chunk_out, u32 *offset_out) -+{ -+ int chunk; -+ u32 offset; -+ -+ chunk = (u32) (addr >> dev->chunk_shift); -+ -+ if (dev->chunk_div == 1) { -+ /* easy power of 2 case */ -+ offset = (u32) (addr & dev->chunk_mask); -+ } else { -+ /* Non power-of-2 case */ -+ -+ loff_t chunk_base; -+ -+ chunk /= dev->chunk_div; -+ -+ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; -+ offset = (u32) (addr - chunk_base); -+ } -+ -+ *chunk_out = chunk; -+ *offset_out = offset; -+} -+ -+/* Function to return the number of shifts for a power of 2 greater than or -+ * equal to the given number -+ * Note we don't try to cater for all possible numbers and this does not have to -+ * be hellishly efficient. -+ */ -+ -+static inline u32 calc_shifts_ceiling(u32 x) -+{ -+ int extra_bits; -+ int shifts; -+ -+ shifts = extra_bits = 0; -+ -+ while (x > 1) { -+ if (x & 1) -+ extra_bits++; -+ x >>= 1; -+ shifts++; -+ } -+ -+ if (extra_bits) -+ shifts++; -+ -+ return shifts; -+} -+ -+/* Function to return the number of shifts to get a 1 in bit 0 -+ */ -+ -+static inline u32 calc_shifts(u32 x) -+{ -+ u32 shifts; -+ -+ shifts = 0; -+ -+ if (!x) -+ return 0; -+ -+ while (!(x & 1)) { -+ x >>= 1; -+ shifts++; -+ } -+ -+ return shifts; -+} -+ -+/* -+ * Temporary buffer manipulations. -+ */ -+ -+static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) -+{ -+ int i; -+ u8 *buf = (u8 *) 1; -+ -+ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); -+ -+ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { -+ dev->temp_buffer[i].in_use = 0; -+ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ dev->temp_buffer[i].buffer = buf; -+ } -+ -+ return buf ? YAFFS_OK : YAFFS_FAIL; -+} -+ -+u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) -+{ -+ int i; -+ -+ dev->temp_in_use++; -+ if (dev->temp_in_use > dev->max_temp) -+ dev->max_temp = dev->temp_in_use; -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ if (dev->temp_buffer[i].in_use == 0) { -+ dev->temp_buffer[i].in_use = 1; -+ return dev->temp_buffer[i].buffer; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); -+ /* -+ * If we got here then we have to allocate an unmanaged one -+ * This is not good. -+ */ -+ -+ dev->unmanaged_buffer_allocs++; -+ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); -+ -+} -+ -+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) -+{ -+ int i; -+ -+ dev->temp_in_use--; -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ if (dev->temp_buffer[i].buffer == buffer) { -+ dev->temp_buffer[i].in_use = 0; -+ return; -+ } -+ } -+ -+ if (buffer) { -+ /* assume it is an unmanaged one. */ -+ yaffs_trace(YAFFS_TRACE_BUFFERS, -+ "Releasing unmanaged temp buffer"); -+ kfree(buffer); -+ dev->unmanaged_buffer_deallocs++; -+ } -+ -+} -+ -+/* -+ * Functions for robustisizing TODO -+ * -+ */ -+ -+static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, -+ const struct yaffs_ext_tags *tags) -+{ -+ (void) dev; -+ (void) nand_chunk; -+ (void) data; -+ (void) tags; -+} -+ -+static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, -+ const struct yaffs_ext_tags *tags) -+{ -+ (void) dev; -+ (void) nand_chunk; -+ (void) tags; -+} -+ -+void yaffs_handle_chunk_error(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi) -+{ -+ if (!bi->gc_prioritise) { -+ bi->gc_prioritise = 1; -+ dev->has_pending_prioritised_gc = 1; -+ bi->chunk_error_strikes++; -+ -+ if (bi->chunk_error_strikes > 3) { -+ bi->needs_retiring = 1; /* Too many stikes, so retire */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Block struck out"); -+ -+ } -+ } -+} -+ -+static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, -+ int erased_ok) -+{ -+ int flash_block = nand_chunk / dev->param.chunks_per_block; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -+ -+ yaffs_handle_chunk_error(dev, bi); -+ -+ if (erased_ok) { -+ /* Was an actual write failure, -+ * so mark the block for retirement.*/ -+ bi->needs_retiring = 1; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Block %d needs retiring", flash_block); -+ } -+ -+ /* Delete the chunk */ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ yaffs_skip_rest_of_block(dev); -+} -+ -+/* -+ * Verification code -+ */ -+ -+/* -+ * Simple hash function. Needs to have a reasonable spread -+ */ -+ -+static inline int yaffs_hash_fn(int n) -+{ -+ if (n < 0) -+ n = -n; -+ return n % YAFFS_NOBJECT_BUCKETS; -+} -+ -+/* -+ * Access functions to useful fake objects. -+ * Note that root might have a presence in NAND if permissions are set. -+ */ -+ -+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) -+{ -+ return dev->root_dir; -+} -+ -+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) -+{ -+ return dev->lost_n_found; -+} -+ -+/* -+ * Erased NAND checking functions -+ */ -+ -+int yaffs_check_ff(u8 *buffer, int n_bytes) -+{ -+ /* Horrible, slow implementation */ -+ while (n_bytes--) { -+ if (*buffer != 0xff) -+ return 0; -+ buffer++; -+ } -+ return 1; -+} -+ -+static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) -+{ -+ int retval = YAFFS_OK; -+ u8 *data = yaffs_get_temp_buffer(dev); -+ struct yaffs_ext_tags tags; -+ int result; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); -+ -+ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) -+ retval = YAFFS_FAIL; -+ -+ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || -+ tags.chunk_used) { -+ yaffs_trace(YAFFS_TRACE_NANDACCESS, -+ "Chunk %d not erased", nand_chunk); -+ retval = YAFFS_FAIL; -+ } -+ -+ yaffs_release_temp_buffer(dev, data); -+ -+ return retval; -+ -+} -+ -+static int yaffs_verify_chunk_written(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, -+ struct yaffs_ext_tags *tags) -+{ -+ int retval = YAFFS_OK; -+ struct yaffs_ext_tags temp_tags; -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ int result; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); -+ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || -+ temp_tags.obj_id != tags->obj_id || -+ temp_tags.chunk_id != tags->chunk_id || -+ temp_tags.n_bytes != tags->n_bytes) -+ retval = YAFFS_FAIL; -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ return retval; -+} -+ -+ -+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) -+{ -+ int reserved_chunks; -+ int reserved_blocks = dev->param.n_reserved_blocks; -+ int checkpt_blocks; -+ -+ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); -+ -+ reserved_chunks = -+ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; -+ -+ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); -+} -+ -+static int yaffs_find_alloc_block(struct yaffs_dev *dev) -+{ -+ int i; -+ struct yaffs_block_info *bi; -+ -+ if (dev->n_erased_blocks < 1) { -+ /* Hoosterman we've got a problem. -+ * Can't get space to gc -+ */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: no more erased blocks"); -+ -+ return -1; -+ } -+ -+ /* Find an empty block. */ -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ dev->alloc_block_finder++; -+ if (dev->alloc_block_finder < dev->internal_start_block -+ || dev->alloc_block_finder > dev->internal_end_block) { -+ dev->alloc_block_finder = dev->internal_start_block; -+ } -+ -+ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->seq_number++; -+ bi->seq_number = dev->seq_number; -+ dev->n_erased_blocks--; -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, -+ "Allocated block %d, seq %d, %d left" , -+ dev->alloc_block_finder, dev->seq_number, -+ dev->n_erased_blocks); -+ return dev->alloc_block_finder; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs tragedy: no more erased blocks, but there should have been %d", -+ dev->n_erased_blocks); -+ -+ return -1; -+} -+ -+static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, -+ struct yaffs_block_info **block_ptr) -+{ -+ int ret_val; -+ struct yaffs_block_info *bi; -+ -+ if (dev->alloc_block < 0) { -+ /* Get next block to allocate off */ -+ dev->alloc_block = yaffs_find_alloc_block(dev); -+ dev->alloc_page = 0; -+ } -+ -+ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { -+ /* No space unless we're allowed to use the reserve. */ -+ return -1; -+ } -+ -+ if (dev->n_erased_blocks < dev->param.n_reserved_blocks -+ && dev->alloc_page == 0) -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); -+ -+ /* Next page please.... */ -+ if (dev->alloc_block >= 0) { -+ bi = yaffs_get_block_info(dev, dev->alloc_block); -+ -+ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + -+ dev->alloc_page; -+ bi->pages_in_use++; -+ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); -+ -+ dev->alloc_page++; -+ -+ dev->n_free_chunks--; -+ -+ /* If the block is full set the state to full */ -+ if (dev->alloc_page >= dev->param.chunks_per_block) { -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ -+ if (block_ptr) -+ *block_ptr = bi; -+ -+ return ret_val; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); -+ -+ return -1; -+} -+ -+static int yaffs_get_erased_chunks(struct yaffs_dev *dev) -+{ -+ int n; -+ -+ n = dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ if (dev->alloc_block > 0) -+ n += (dev->param.chunks_per_block - dev->alloc_page); -+ -+ return n; -+ -+} -+ -+/* -+ * yaffs_skip_rest_of_block() skips over the rest of the allocation block -+ * if we don't want to write to it. -+ */ -+void yaffs_skip_rest_of_block(struct yaffs_dev *dev) -+{ -+ struct yaffs_block_info *bi; -+ -+ if (dev->alloc_block > 0) { -+ bi = yaffs_get_block_info(dev, dev->alloc_block); -+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ } -+} -+ -+static int yaffs_write_new_chunk(struct yaffs_dev *dev, -+ const u8 *data, -+ struct yaffs_ext_tags *tags, int use_reserver) -+{ -+ int attempts = 0; -+ int write_ok = 0; -+ int chunk; -+ -+ yaffs2_checkpt_invalidate(dev); -+ -+ do { -+ struct yaffs_block_info *bi = 0; -+ int erased_ok = 0; -+ -+ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); -+ if (chunk < 0) { -+ /* no space */ -+ break; -+ } -+ -+ /* First check this chunk is erased, if it needs -+ * checking. The checking policy (unless forced -+ * always on) is as follows: -+ * -+ * Check the first page we try to write in a block. -+ * If the check passes then we don't need to check any -+ * more. If the check fails, we check again... -+ * If the block has been erased, we don't need to check. -+ * -+ * However, if the block has been prioritised for gc, -+ * then we think there might be something odd about -+ * this block and stop using it. -+ * -+ * Rationale: We should only ever see chunks that have -+ * not been erased if there was a partially written -+ * chunk due to power loss. This checking policy should -+ * catch that case with very few checks and thus save a -+ * lot of checks that are most likely not needed. -+ * -+ * Mods to the above -+ * If an erase check fails or the write fails we skip the -+ * rest of the block. -+ */ -+ -+ /* let's give it a try */ -+ attempts++; -+ -+ if (dev->param.always_check_erased) -+ bi->skip_erased_check = 0; -+ -+ if (!bi->skip_erased_check) { -+ erased_ok = yaffs_check_chunk_erased(dev, chunk); -+ if (erased_ok != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs chunk %d was not erased", -+ chunk); -+ -+ /* If not erased, delete this one, -+ * skip rest of block and -+ * try another chunk */ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ yaffs_skip_rest_of_block(dev); -+ continue; -+ } -+ } -+ -+ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); -+ -+ if (!bi->skip_erased_check) -+ write_ok = -+ yaffs_verify_chunk_written(dev, chunk, data, tags); -+ -+ if (write_ok != YAFFS_OK) { -+ /* Clean up aborted write, skip to next block and -+ * try another chunk */ -+ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); -+ continue; -+ } -+ -+ bi->skip_erased_check = 1; -+ -+ /* Copy the data into the robustification buffer */ -+ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); -+ -+ } while (write_ok != YAFFS_OK && -+ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); -+ -+ if (!write_ok) -+ chunk = -1; -+ -+ if (attempts > 1) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs write required %d attempts", -+ attempts); -+ dev->n_retried_writes += (attempts - 1); -+ } -+ -+ return chunk; -+} -+ -+/* -+ * Block retiring for handling a broken block. -+ */ -+ -+static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -+ -+ yaffs2_checkpt_invalidate(dev); -+ -+ yaffs2_clear_oldest_dirty_seq(dev, bi); -+ -+ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { -+ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Failed to mark bad and erase block %d", -+ flash_block); -+ } else { -+ struct yaffs_ext_tags tags; -+ int chunk_id = -+ flash_block * dev->param.chunks_per_block; -+ -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ memset(buffer, 0xff, dev->data_bytes_per_chunk); -+ memset(&tags, 0, sizeof(tags)); -+ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; -+ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - -+ dev->chunk_offset, -+ buffer, -+ &tags) != YAFFS_OK) -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Failed to write bad block marker to block %d", -+ flash_block); -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ } -+ -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ bi->gc_prioritise = 0; -+ bi->needs_retiring = 0; -+ -+ dev->n_retired_blocks++; -+} -+ -+/*---------------- Name handling functions ------------*/ -+ -+static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, -+ const YCHAR *oh_name, int buff_size) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ if (dev->param.auto_unicode) { -+ if (*oh_name) { -+ /* It is an ASCII name, do an ASCII to -+ * unicode conversion */ -+ const char *ascii_oh_name = (const char *)oh_name; -+ int n = buff_size - 1; -+ while (n > 0 && *ascii_oh_name) { -+ *name = *ascii_oh_name; -+ name++; -+ ascii_oh_name++; -+ n--; -+ } -+ } else { -+ strncpy(name, oh_name + 1, buff_size - 1); -+ } -+ } else { -+#else -+ (void) dev; -+ { -+#endif -+ strncpy(name, oh_name, buff_size - 1); -+ } -+} -+ -+static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, -+ const YCHAR *name) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ -+ int is_ascii; -+ const YCHAR *w; -+ -+ if (dev->param.auto_unicode) { -+ -+ is_ascii = 1; -+ w = name; -+ -+ /* Figure out if the name will fit in ascii character set */ -+ while (is_ascii && *w) { -+ if ((*w) & 0xff00) -+ is_ascii = 0; -+ w++; -+ } -+ -+ if (is_ascii) { -+ /* It is an ASCII name, so convert unicode to ascii */ -+ char *ascii_oh_name = (char *)oh_name; -+ int n = YAFFS_MAX_NAME_LENGTH - 1; -+ while (n > 0 && *name) { -+ *ascii_oh_name = *name; -+ name++; -+ ascii_oh_name++; -+ n--; -+ } -+ } else { -+ /* Unicode name, so save starting at the second YCHAR */ -+ *oh_name = 0; -+ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); -+ } -+ } else { -+#else -+ dev = dev; -+ { -+#endif -+ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); -+ } -+} -+ -+static u16 yaffs_calc_name_sum(const YCHAR *name) -+{ -+ u16 sum = 0; -+ u16 i = 1; -+ -+ if (!name) -+ return 0; -+ -+ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { -+ -+ /* 0x1f mask is case insensitive */ -+ sum += ((*name) & 0x1f) * i; -+ i++; -+ name++; -+ } -+ return sum; -+} -+ -+ -+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) -+{ -+ memset(obj->short_name, 0, sizeof(obj->short_name)); -+ -+ if (name && !name[0]) { -+ yaffs_fix_null_name(obj, obj->short_name, -+ YAFFS_SHORT_NAME_LENGTH); -+ name = obj->short_name; -+ } else if (name && -+ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= -+ YAFFS_SHORT_NAME_LENGTH) { -+ strcpy(obj->short_name, name); -+ } -+ -+ obj->sum = yaffs_calc_name_sum(name); -+} -+ -+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, -+ const struct yaffs_obj_hdr *oh) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; -+ memset(tmp_name, 0, sizeof(tmp_name)); -+ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, -+ YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_set_obj_name(obj, tmp_name); -+#else -+ yaffs_set_obj_name(obj, oh->name); -+#endif -+} -+ -+loff_t yaffs_max_file_size(struct yaffs_dev *dev) -+{ -+ if(sizeof(loff_t) < 8) -+ return YAFFS_MAX_FILE_SIZE_32; -+ else -+ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; -+} -+ -+/*-------------------- TNODES ------------------- -+ -+ * List of spare tnodes -+ * The list is hooked together using the first pointer -+ * in the tnode. -+ */ -+ -+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) -+{ -+ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); -+ -+ if (tn) { -+ memset(tn, 0, dev->tnode_size); -+ dev->n_tnodes++; -+ } -+ -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+ -+ return tn; -+} -+ -+/* FreeTnode frees up a tnode and puts it back on the free list */ -+static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) -+{ -+ yaffs_free_raw_tnode(dev, tn); -+ dev->n_tnodes--; -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ yaffs_deinit_raw_tnodes_and_objs(dev); -+ dev->n_obj = 0; -+ dev->n_tnodes = 0; -+} -+ -+static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos, unsigned val) -+{ -+ u32 *map = (u32 *) tn; -+ u32 bit_in_map; -+ u32 bit_in_word; -+ u32 word_in_map; -+ u32 mask; -+ -+ pos &= YAFFS_TNODES_LEVEL0_MASK; -+ val >>= dev->chunk_grp_bits; -+ -+ bit_in_map = pos * dev->tnode_width; -+ word_in_map = bit_in_map / 32; -+ bit_in_word = bit_in_map & (32 - 1); -+ -+ mask = dev->tnode_mask << bit_in_word; -+ -+ map[word_in_map] &= ~mask; -+ map[word_in_map] |= (mask & (val << bit_in_word)); -+ -+ if (dev->tnode_width > (32 - bit_in_word)) { -+ bit_in_word = (32 - bit_in_word); -+ word_in_map++; -+ mask = -+ dev->tnode_mask >> bit_in_word; -+ map[word_in_map] &= ~mask; -+ map[word_in_map] |= (mask & (val >> bit_in_word)); -+ } -+} -+ -+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos) -+{ -+ u32 *map = (u32 *) tn; -+ u32 bit_in_map; -+ u32 bit_in_word; -+ u32 word_in_map; -+ u32 val; -+ -+ pos &= YAFFS_TNODES_LEVEL0_MASK; -+ -+ bit_in_map = pos * dev->tnode_width; -+ word_in_map = bit_in_map / 32; -+ bit_in_word = bit_in_map & (32 - 1); -+ -+ val = map[word_in_map] >> bit_in_word; -+ -+ if (dev->tnode_width > (32 - bit_in_word)) { -+ bit_in_word = (32 - bit_in_word); -+ word_in_map++; -+ val |= (map[word_in_map] << bit_in_word); -+ } -+ -+ val &= dev->tnode_mask; -+ val <<= dev->chunk_grp_bits; -+ -+ return val; -+} -+ -+/* ------------------- End of individual tnode manipulation -----------------*/ -+ -+/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ -+ * The look up tree is represented by the top tnode and the number of top_level -+ * in the tree. 0 means only the level 0 tnode is in the tree. -+ */ -+ -+/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ -+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id) -+{ -+ struct yaffs_tnode *tn = file_struct->top; -+ u32 i; -+ int required_depth; -+ int level = file_struct->top_level; -+ -+ (void) dev; -+ -+ /* Check sane level and chunk Id */ -+ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) -+ return NULL; -+ -+ if (chunk_id > YAFFS_MAX_CHUNK_ID) -+ return NULL; -+ -+ /* First check we're tall enough (ie enough top_level) */ -+ -+ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (i) { -+ i >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ if (required_depth > file_struct->top_level) -+ return NULL; /* Not tall enough, so we can't find it */ -+ -+ /* Traverse down to level 0 */ -+ while (level > 0 && tn) { -+ tn = tn->internal[(chunk_id >> -+ (YAFFS_TNODES_LEVEL0_BITS + -+ (level - 1) * -+ YAFFS_TNODES_INTERNAL_BITS)) & -+ YAFFS_TNODES_INTERNAL_MASK]; -+ level--; -+ } -+ -+ return tn; -+} -+ -+/* add_find_tnode_0 finds the level 0 tnode if it exists, -+ * otherwise first expands the tree. -+ * This happens in two steps: -+ * 1. If the tree isn't tall enough, then make it taller. -+ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. -+ * -+ * Used when modifying the tree. -+ * -+ * If the tn argument is NULL, then a fresh tnode will be added otherwise the -+ * specified tn will be plugged into the ttree. -+ */ -+ -+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id, -+ struct yaffs_tnode *passed_tn) -+{ -+ int required_depth; -+ int i; -+ int l; -+ struct yaffs_tnode *tn; -+ u32 x; -+ -+ /* Check sane level and page Id */ -+ if (file_struct->top_level < 0 || -+ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) -+ return NULL; -+ -+ if (chunk_id > YAFFS_MAX_CHUNK_ID) -+ return NULL; -+ -+ /* First check we're tall enough (ie enough top_level) */ -+ -+ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (x) { -+ x >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ if (required_depth > file_struct->top_level) { -+ /* Not tall enough, gotta make the tree taller */ -+ for (i = file_struct->top_level; i < required_depth; i++) { -+ -+ tn = yaffs_get_tnode(dev); -+ -+ if (tn) { -+ tn->internal[0] = file_struct->top; -+ file_struct->top = tn; -+ file_struct->top_level++; -+ } else { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs: no more tnodes"); -+ return NULL; -+ } -+ } -+ } -+ -+ /* Traverse down to level 0, adding anything we need */ -+ -+ l = file_struct->top_level; -+ tn = file_struct->top; -+ -+ if (l > 0) { -+ while (l > 0 && tn) { -+ x = (chunk_id >> -+ (YAFFS_TNODES_LEVEL0_BITS + -+ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & -+ YAFFS_TNODES_INTERNAL_MASK; -+ -+ if ((l > 1) && !tn->internal[x]) { -+ /* Add missing non-level-zero tnode */ -+ tn->internal[x] = yaffs_get_tnode(dev); -+ if (!tn->internal[x]) -+ return NULL; -+ } else if (l == 1) { -+ /* Looking from level 1 at level 0 */ -+ if (passed_tn) { -+ /* If we already have one, release it */ -+ if (tn->internal[x]) -+ yaffs_free_tnode(dev, -+ tn->internal[x]); -+ tn->internal[x] = passed_tn; -+ -+ } else if (!tn->internal[x]) { -+ /* Don't have one, none passed in */ -+ tn->internal[x] = yaffs_get_tnode(dev); -+ if (!tn->internal[x]) -+ return NULL; -+ } -+ } -+ -+ tn = tn->internal[x]; -+ l--; -+ } -+ } else { -+ /* top is level 0 */ -+ if (passed_tn) { -+ memcpy(tn, passed_tn, -+ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); -+ yaffs_free_tnode(dev, passed_tn); -+ } -+ } -+ -+ return tn; -+} -+ -+static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, -+ int chunk_obj) -+{ -+ return (tags->chunk_id == chunk_obj && -+ tags->obj_id == obj_id && -+ !tags->is_deleted) ? 1 : 0; -+ -+} -+ -+static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, -+ struct yaffs_ext_tags *tags, int obj_id, -+ int inode_chunk) -+{ -+ int j; -+ -+ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { -+ if (yaffs_check_chunk_bit -+ (dev, the_chunk / dev->param.chunks_per_block, -+ the_chunk % dev->param.chunks_per_block)) { -+ -+ if (dev->chunk_grp_size == 1) -+ return the_chunk; -+ else { -+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, -+ tags); -+ if (yaffs_tags_match(tags, -+ obj_id, inode_chunk)) { -+ /* found it; */ -+ return the_chunk; -+ } -+ } -+ } -+ the_chunk++; -+ } -+ return -1; -+} -+ -+int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags) -+{ -+ /*Get the Tnode, then get the level 0 offset chunk offset */ -+ struct yaffs_tnode *tn; -+ int the_chunk = -1; -+ struct yaffs_ext_tags local_tags; -+ int ret_val = -1; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!tags) { -+ /* Passed a NULL, so use our own tags space */ -+ tags = &local_tags; -+ } -+ -+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -+ -+ if (!tn) -+ return ret_val; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, -+ inode_chunk); -+ return ret_val; -+} -+ -+static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags) -+{ -+ /* Get the Tnode, then get the level 0 offset chunk offset */ -+ struct yaffs_tnode *tn; -+ int the_chunk = -1; -+ struct yaffs_ext_tags local_tags; -+ struct yaffs_dev *dev = in->my_dev; -+ int ret_val = -1; -+ -+ if (!tags) { -+ /* Passed a NULL, so use our own tags space */ -+ tags = &local_tags; -+ } -+ -+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -+ -+ if (!tn) -+ return ret_val; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, -+ inode_chunk); -+ -+ /* Delete the entry in the filestructure (if found) */ -+ if (ret_val != -1) -+ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); -+ -+ return ret_val; -+} -+ -+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ int nand_chunk, int in_scan) -+{ -+ /* NB in_scan is zero unless scanning. -+ * For forward scanning, in_scan is > 0; -+ * for backward scanning in_scan is < 0 -+ * -+ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. -+ */ -+ -+ struct yaffs_tnode *tn; -+ struct yaffs_dev *dev = in->my_dev; -+ int existing_cunk; -+ struct yaffs_ext_tags existing_tags; -+ struct yaffs_ext_tags new_tags; -+ unsigned existing_serial, new_serial; -+ -+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { -+ /* Just ignore an attempt at putting a chunk into a non-file -+ * during scanning. -+ * If it is not during Scanning then something went wrong! -+ */ -+ if (!in_scan) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy:attempt to put data chunk into a non-file" -+ ); -+ BUG(); -+ } -+ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ return YAFFS_OK; -+ } -+ -+ tn = yaffs_add_find_tnode_0(dev, -+ &in->variant.file_variant, -+ inode_chunk, NULL); -+ if (!tn) -+ return YAFFS_FAIL; -+ -+ if (!nand_chunk) -+ /* Dummy insert, bail now */ -+ return YAFFS_OK; -+ -+ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ if (in_scan != 0) { -+ /* If we're scanning then we need to test for duplicates -+ * NB This does not need to be efficient since it should only -+ * happen when the power fails during a write, then only one -+ * chunk should ever be affected. -+ * -+ * Correction for YAFFS2: This could happen quite a lot and we -+ * need to think about efficiency! TODO -+ * Update: For backward scanning we don't need to re-read tags -+ * so this is quite cheap. -+ */ -+ -+ if (existing_cunk > 0) { -+ /* NB Right now existing chunk will not be real -+ * chunk_id if the chunk group size > 1 -+ * thus we have to do a FindChunkInFile to get the -+ * real chunk id. -+ * -+ * We have a duplicate now we need to decide which -+ * one to use: -+ * -+ * Backwards scanning YAFFS2: The old one is what -+ * we use, dump the new one. -+ * YAFFS1: Get both sets of tags and compare serial -+ * numbers. -+ */ -+ -+ if (in_scan > 0) { -+ /* Only do this for forward scanning */ -+ yaffs_rd_chunk_tags_nand(dev, -+ nand_chunk, -+ NULL, &new_tags); -+ -+ /* Do a proper find */ -+ existing_cunk = -+ yaffs_find_chunk_in_file(in, inode_chunk, -+ &existing_tags); -+ } -+ -+ if (existing_cunk <= 0) { -+ /*Hoosterman - how did this happen? */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: existing chunk < 0 in scan" -+ ); -+ -+ } -+ -+ /* NB The deleted flags should be false, otherwise -+ * the chunks will not be loaded during a scan -+ */ -+ -+ if (in_scan > 0) { -+ new_serial = new_tags.serial_number; -+ existing_serial = existing_tags.serial_number; -+ } -+ -+ if ((in_scan > 0) && -+ (existing_cunk <= 0 || -+ ((existing_serial + 1) & 3) == new_serial)) { -+ /* Forward scanning. -+ * Use new -+ * Delete the old one and drop through to -+ * update the tnode -+ */ -+ yaffs_chunk_del(dev, existing_cunk, 1, -+ __LINE__); -+ } else { -+ /* Backward scanning or we want to use the -+ * existing one -+ * Delete the new one and return early so that -+ * the tnode isn't changed -+ */ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ return YAFFS_OK; -+ } -+ } -+ -+ } -+ -+ if (existing_cunk == 0) -+ in->n_data_chunks++; -+ -+ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); -+ -+ return YAFFS_OK; -+} -+ -+static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) -+{ -+ struct yaffs_block_info *the_block; -+ unsigned block_no; -+ -+ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); -+ -+ block_no = chunk / dev->param.chunks_per_block; -+ the_block = yaffs_get_block_info(dev, block_no); -+ if (the_block) { -+ the_block->soft_del_pages++; -+ dev->n_free_chunks++; -+ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); -+ } -+} -+ -+/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all -+ * the chunks in the file. -+ * All soft deleting does is increment the block's softdelete count and pulls -+ * the chunk out of the tnode. -+ * Thus, essentially this is the same as DeleteWorker except that the chunks -+ * are soft deleted. -+ */ -+ -+static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, -+ u32 level, int chunk_offset) -+{ -+ int i; -+ int the_chunk; -+ int all_done = 1; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!tn) -+ return 1; -+ -+ if (level > 0) { -+ for (i = YAFFS_NTNODES_INTERNAL - 1; -+ all_done && i >= 0; -+ i--) { -+ if (tn->internal[i]) { -+ all_done = -+ yaffs_soft_del_worker(in, -+ tn->internal[i], -+ level - 1, -+ (chunk_offset << -+ YAFFS_TNODES_INTERNAL_BITS) -+ + i); -+ if (all_done) { -+ yaffs_free_tnode(dev, -+ tn->internal[i]); -+ tn->internal[i] = NULL; -+ } else { -+ /* Can this happen? */ -+ } -+ } -+ } -+ return (all_done) ? 1 : 0; -+ } -+ -+ /* level 0 */ -+ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { -+ the_chunk = yaffs_get_group_base(dev, tn, i); -+ if (the_chunk) { -+ yaffs_soft_del_chunk(dev, the_chunk); -+ yaffs_load_tnode_0(dev, tn, i, 0); -+ } -+ } -+ return 1; -+} -+ -+static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ struct yaffs_obj *parent; -+ -+ yaffs_verify_obj_in_dir(obj); -+ parent = obj->parent; -+ -+ yaffs_verify_dir(parent); -+ -+ if (dev && dev->param.remove_obj_fn) -+ dev->param.remove_obj_fn(obj); -+ -+ list_del_init(&obj->siblings); -+ obj->parent = NULL; -+ -+ yaffs_verify_dir(parent); -+} -+ -+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) -+{ -+ if (!directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: Trying to add an object to a null pointer directory" -+ ); -+ BUG(); -+ return; -+ } -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: Trying to add an object to a non-directory" -+ ); -+ BUG(); -+ } -+ -+ if (obj->siblings.prev == NULL) { -+ /* Not initialised */ -+ BUG(); -+ } -+ -+ yaffs_verify_dir(directory); -+ -+ yaffs_remove_obj_from_dir(obj); -+ -+ /* Now add it */ -+ list_add(&obj->siblings, &directory->variant.dir_variant.children); -+ obj->parent = directory; -+ -+ if (directory == obj->my_dev->unlinked_dir -+ || directory == obj->my_dev->del_dir) { -+ obj->unlinked = 1; -+ obj->my_dev->n_unlinked_files++; -+ obj->rename_allowed = 0; -+ } -+ -+ yaffs_verify_dir(directory); -+ yaffs_verify_obj_in_dir(obj); -+} -+ -+static int yaffs_change_obj_name(struct yaffs_obj *obj, -+ struct yaffs_obj *new_dir, -+ const YCHAR *new_name, int force, int shadows) -+{ -+ int unlink_op; -+ int del_op; -+ struct yaffs_obj *existing_target; -+ -+ if (new_dir == NULL) -+ new_dir = obj->parent; /* use the old directory */ -+ -+ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_change_obj_name: new_dir is not a directory" -+ ); -+ BUG(); -+ } -+ -+ unlink_op = (new_dir == obj->my_dev->unlinked_dir); -+ del_op = (new_dir == obj->my_dev->del_dir); -+ -+ existing_target = yaffs_find_by_name(new_dir, new_name); -+ -+ /* If the object is a file going into the unlinked directory, -+ * then it is OK to just stuff it in since duplicate names are OK. -+ * else only proceed if the new name does not exist and we're putting -+ * it into a directory. -+ */ -+ if (!(unlink_op || del_op || force || -+ shadows > 0 || !existing_target) || -+ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) -+ return YAFFS_FAIL; -+ -+ yaffs_set_obj_name(obj, new_name); -+ obj->dirty = 1; -+ yaffs_add_obj_to_dir(new_dir, obj); -+ -+ if (unlink_op) -+ obj->unlinked = 1; -+ -+ /* If it is a deletion then we mark it as a shrink for gc */ -+ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+/*------------------------ Short Operations Cache ------------------------------ -+ * In many situations where there is no high level buffering a lot of -+ * reads might be short sequential reads, and a lot of writes may be short -+ * sequential writes. eg. scanning/writing a jpeg file. -+ * In these cases, a short read/write cache can provide a huge perfomance -+ * benefit with dumb-as-a-rock code. -+ * In Linux, the page cache provides read buffering and the short op cache -+ * provides write buffering. -+ * -+ * There are a small number (~10) of cache chunks per device so that we don't -+ * need a very intelligent search. -+ */ -+ -+static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ struct yaffs_cache *cache; -+ int n_caches = obj->my_dev->param.n_caches; -+ -+ for (i = 0; i < n_caches; i++) { -+ cache = &dev->cache[i]; -+ if (cache->object == obj && cache->dirty) -+ return 1; -+ } -+ -+ return 0; -+} -+ -+static void yaffs_flush_single_cache(struct yaffs_cache *cache, int discard) -+{ -+ -+ if (!cache || cache->locked) -+ return; -+ -+ /* Write it out and free it up if need be.*/ -+ if (cache->dirty) { -+ yaffs_wr_data_obj(cache->object, -+ cache->chunk_id, -+ cache->data, -+ cache->n_bytes, -+ 1); -+ -+ cache->dirty = 0; -+ } -+ -+ if (discard) -+ cache->object = NULL; -+} -+ -+static void yaffs_flush_file_cache(struct yaffs_obj *obj, int discard) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ struct yaffs_cache *cache; -+ int n_caches = obj->my_dev->param.n_caches; -+ -+ if (n_caches < 1) -+ return; -+ -+ -+ /* Find the chunks for this object and flush them. */ -+ for (i = 0; i < n_caches; i++) { -+ cache = &dev->cache[i]; -+ if (cache->object == obj) -+ yaffs_flush_single_cache(cache, discard); -+ } -+ -+} -+ -+ -+void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard) -+{ -+ struct yaffs_obj *obj; -+ int n_caches = dev->param.n_caches; -+ int i; -+ -+ /* Find a dirty object in the cache and flush it... -+ * until there are no further dirty objects. -+ */ -+ do { -+ obj = NULL; -+ for (i = 0; i < n_caches && !obj; i++) { -+ if (dev->cache[i].object && dev->cache[i].dirty) -+ obj = dev->cache[i].object; -+ } -+ if (obj) -+ yaffs_flush_file_cache(obj, discard); -+ } while (obj); -+ -+} -+ -+/* Grab us an unused cache chunk for use. -+ * First look for an empty one. -+ * Then look for the least recently used non-dirty one. -+ * Then look for the least recently used dirty one...., flush and look again. -+ */ -+static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (dev->param.n_caches > 0) { -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (!dev->cache[i].object) -+ return &dev->cache[i]; -+ } -+ } -+ -+ return NULL; -+} -+ -+static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) -+{ -+ struct yaffs_cache *cache; -+ int usage; -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return NULL; -+ -+ /* First look for an unused cache */ -+ -+ cache = yaffs_grab_chunk_worker(dev); -+ -+ if (cache) -+ return cache; -+ -+ /* -+ * Thery were all in use. -+ * Find the LRU cache and flush it if it is dirty. -+ */ -+ -+ usage = -1; -+ cache = NULL; -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object && -+ !dev->cache[i].locked && -+ (dev->cache[i].last_use < usage || !cache)) { -+ usage = dev->cache[i].last_use; -+ cache = &dev->cache[i]; -+ } -+ } -+ -+#if 1 -+ yaffs_flush_single_cache(cache, 1); -+#else -+ yaffs_flush_file_cache(cache->object, 1); -+ cache = yaffs_grab_chunk_worker(dev); -+#endif -+ -+ return cache; -+} -+ -+/* Find a cached chunk */ -+static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, -+ int chunk_id) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return NULL; -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object == obj && -+ dev->cache[i].chunk_id == chunk_id) { -+ dev->cache_hits++; -+ -+ return &dev->cache[i]; -+ } -+ } -+ return NULL; -+} -+ -+/* Mark the chunk for the least recently used algorithym */ -+static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, -+ int is_write) -+{ -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return; -+ -+ if (dev->cache_last_use < 0 || -+ dev->cache_last_use > 100000000) { -+ /* Reset the cache usages */ -+ for (i = 1; i < dev->param.n_caches; i++) -+ dev->cache[i].last_use = 0; -+ -+ dev->cache_last_use = 0; -+ } -+ dev->cache_last_use++; -+ cache->last_use = dev->cache_last_use; -+ -+ if (is_write) -+ cache->dirty = 1; -+} -+ -+/* Invalidate a single cache page. -+ * Do this when a whole page gets written, -+ * ie the short cache for this page is no longer valid. -+ */ -+static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) -+{ -+ struct yaffs_cache *cache; -+ -+ if (object->my_dev->param.n_caches > 0) { -+ cache = yaffs_find_chunk_cache(object, chunk_id); -+ -+ if (cache) -+ cache->object = NULL; -+ } -+} -+ -+/* Invalidate all the cache pages associated with this object -+ * Do this whenever ther file is deleted or resized. -+ */ -+static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) -+{ -+ int i; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (dev->param.n_caches > 0) { -+ /* Invalidate it. */ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object == in) -+ dev->cache[i].object = NULL; -+ } -+ } -+} -+ -+static void yaffs_unhash_obj(struct yaffs_obj *obj) -+{ -+ int bucket; -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ /* If it is still linked into the bucket list, free from the list */ -+ if (!list_empty(&obj->hash_link)) { -+ list_del_init(&obj->hash_link); -+ bucket = yaffs_hash_fn(obj->obj_id); -+ dev->obj_bucket[bucket].count--; -+ } -+} -+ -+/* FreeObject frees up a Object and puts it back on the free list */ -+static void yaffs_free_obj(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ -+ if (!obj) { -+ BUG(); -+ return; -+ } -+ dev = obj->my_dev; -+ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", -+ obj, obj->my_inode); -+ if (obj->parent) -+ BUG(); -+ if (!list_empty(&obj->siblings)) -+ BUG(); -+ -+ if (obj->my_inode) { -+ /* We're still hooked up to a cached inode. -+ * Don't delete now, but mark for later deletion -+ */ -+ obj->defered_free = 1; -+ return; -+ } -+ -+ yaffs_unhash_obj(obj); -+ -+ yaffs_free_raw_obj(dev, obj); -+ dev->n_obj--; -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+void yaffs_handle_defered_free(struct yaffs_obj *obj) -+{ -+ if (obj->defered_free) -+ yaffs_free_obj(obj); -+} -+ -+static int yaffs_generic_obj_del(struct yaffs_obj *in) -+{ -+ /* Iinvalidate the file's data in the cache, without flushing. */ -+ yaffs_invalidate_whole_cache(in); -+ -+ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { -+ /* Move to unlinked directory so we have a deletion record */ -+ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, -+ 0); -+ } -+ -+ yaffs_remove_obj_from_dir(in); -+ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); -+ in->hdr_chunk = 0; -+ -+ yaffs_free_obj(in); -+ return YAFFS_OK; -+ -+} -+ -+static void yaffs_soft_del_file(struct yaffs_obj *obj) -+{ -+ if (!obj->deleted || -+ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || -+ obj->soft_del) -+ return; -+ -+ if (obj->n_data_chunks <= 0) { -+ /* Empty file with no duplicate object headers, -+ * just delete it immediately */ -+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); -+ obj->variant.file_variant.top = NULL; -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: Deleting empty file %d", -+ obj->obj_id); -+ yaffs_generic_obj_del(obj); -+ } else { -+ yaffs_soft_del_worker(obj, -+ obj->variant.file_variant.top, -+ obj->variant. -+ file_variant.top_level, 0); -+ obj->soft_del = 1; -+ } -+} -+ -+/* Pruning removes any part of the file structure tree that is beyond the -+ * bounds of the file (ie that does not point to chunks). -+ * -+ * A file should only get pruned when its size is reduced. -+ * -+ * Before pruning, the chunks must be pulled from the tree and the -+ * level 0 tnode entries must be zeroed out. -+ * Could also use this for file deletion, but that's probably better handled -+ * by a special case. -+ * -+ * This function is recursive. For levels > 0 the function is called again on -+ * any sub-tree. For level == 0 we just check if the sub-tree has data. -+ * If there is no data in a subtree then it is pruned. -+ */ -+ -+static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, -+ struct yaffs_tnode *tn, u32 level, -+ int del0) -+{ -+ int i; -+ int has_data; -+ -+ if (!tn) -+ return tn; -+ -+ has_data = 0; -+ -+ if (level > 0) { -+ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { -+ if (tn->internal[i]) { -+ tn->internal[i] = -+ yaffs_prune_worker(dev, -+ tn->internal[i], -+ level - 1, -+ (i == 0) ? del0 : 1); -+ } -+ -+ if (tn->internal[i]) -+ has_data++; -+ } -+ } else { -+ int tnode_size_u32 = dev->tnode_size / sizeof(u32); -+ u32 *map = (u32 *) tn; -+ -+ for (i = 0; !has_data && i < tnode_size_u32; i++) { -+ if (map[i]) -+ has_data++; -+ } -+ } -+ -+ if (has_data == 0 && del0) { -+ /* Free and return NULL */ -+ yaffs_free_tnode(dev, tn); -+ tn = NULL; -+ } -+ return tn; -+} -+ -+static int yaffs_prune_tree(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct) -+{ -+ int i; -+ int has_data; -+ int done = 0; -+ struct yaffs_tnode *tn; -+ -+ if (file_struct->top_level < 1) -+ return YAFFS_OK; -+ -+ file_struct->top = -+ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); -+ -+ /* Now we have a tree with all the non-zero branches NULL but -+ * the height is the same as it was. -+ * Let's see if we can trim internal tnodes to shorten the tree. -+ * We can do this if only the 0th element in the tnode is in use -+ * (ie all the non-zero are NULL) -+ */ -+ -+ while (file_struct->top_level && !done) { -+ tn = file_struct->top; -+ -+ has_data = 0; -+ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { -+ if (tn->internal[i]) -+ has_data++; -+ } -+ -+ if (!has_data) { -+ file_struct->top = tn->internal[0]; -+ file_struct->top_level--; -+ yaffs_free_tnode(dev, tn); -+ } else { -+ done = 1; -+ } -+ } -+ -+ return YAFFS_OK; -+} -+ -+/*-------------------- End of File Structure functions.-------------------*/ -+ -+/* alloc_empty_obj gets us a clean Object.*/ -+static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); -+ -+ if (!obj) -+ return obj; -+ -+ dev->n_obj++; -+ -+ /* Now sweeten it up... */ -+ -+ memset(obj, 0, sizeof(struct yaffs_obj)); -+ obj->being_created = 1; -+ -+ obj->my_dev = dev; -+ obj->hdr_chunk = 0; -+ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; -+ INIT_LIST_HEAD(&(obj->hard_links)); -+ INIT_LIST_HEAD(&(obj->hash_link)); -+ INIT_LIST_HEAD(&obj->siblings); -+ -+ /* Now make the directory sane */ -+ if (dev->root_dir) { -+ obj->parent = dev->root_dir; -+ list_add(&(obj->siblings), -+ &dev->root_dir->variant.dir_variant.children); -+ } -+ -+ /* Add it to the lost and found directory. -+ * NB Can't put root or lost-n-found in lost-n-found so -+ * check if lost-n-found exists first -+ */ -+ if (dev->lost_n_found) -+ yaffs_add_obj_to_dir(dev->lost_n_found, obj); -+ -+ obj->being_created = 0; -+ -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+ -+ return obj; -+} -+ -+static int yaffs_find_nice_bucket(struct yaffs_dev *dev) -+{ -+ int i; -+ int l = 999; -+ int lowest = 999999; -+ -+ /* Search for the shortest list or one that -+ * isn't too long. -+ */ -+ -+ for (i = 0; i < 10 && lowest > 4; i++) { -+ dev->bucket_finder++; -+ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; -+ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { -+ lowest = dev->obj_bucket[dev->bucket_finder].count; -+ l = dev->bucket_finder; -+ } -+ } -+ -+ return l; -+} -+ -+static int yaffs_new_obj_id(struct yaffs_dev *dev) -+{ -+ int bucket = yaffs_find_nice_bucket(dev); -+ int found = 0; -+ struct list_head *i; -+ u32 n = (u32) bucket; -+ -+ /* -+ * Now find an object value that has not already been taken -+ * by scanning the list, incrementing each time by number of buckets. -+ */ -+ while (!found) { -+ found = 1; -+ n += YAFFS_NOBJECT_BUCKETS; -+ list_for_each(i, &dev->obj_bucket[bucket].list) { -+ /* Check if this value is already taken. */ -+ if (i && list_entry(i, struct yaffs_obj, -+ hash_link)->obj_id == n) -+ found = 0; -+ } -+ } -+ return n; -+} -+ -+static void yaffs_hash_obj(struct yaffs_obj *in) -+{ -+ int bucket = yaffs_hash_fn(in->obj_id); -+ struct yaffs_dev *dev = in->my_dev; -+ -+ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); -+ dev->obj_bucket[bucket].count++; -+} -+ -+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) -+{ -+ int bucket = yaffs_hash_fn(number); -+ struct list_head *i; -+ struct yaffs_obj *in; -+ -+ list_for_each(i, &dev->obj_bucket[bucket].list) { -+ /* Look if it is in the list */ -+ in = list_entry(i, struct yaffs_obj, hash_link); -+ if (in->obj_id == number) { -+ /* Don't show if it is defered free */ -+ if (in->defered_free) -+ return NULL; -+ return in; -+ } -+ } -+ -+ return NULL; -+} -+ -+static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, -+ enum yaffs_obj_type type) -+{ -+ struct yaffs_obj *the_obj = NULL; -+ struct yaffs_tnode *tn = NULL; -+ -+ if (number < 0) -+ number = yaffs_new_obj_id(dev); -+ -+ if (type == YAFFS_OBJECT_TYPE_FILE) { -+ tn = yaffs_get_tnode(dev); -+ if (!tn) -+ return NULL; -+ } -+ -+ the_obj = yaffs_alloc_empty_obj(dev); -+ if (!the_obj) { -+ if (tn) -+ yaffs_free_tnode(dev, tn); -+ return NULL; -+ } -+ -+ the_obj->fake = 0; -+ the_obj->rename_allowed = 1; -+ the_obj->unlink_allowed = 1; -+ the_obj->obj_id = number; -+ yaffs_hash_obj(the_obj); -+ the_obj->variant_type = type; -+ yaffs_load_current_time(the_obj, 1, 1); -+ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ the_obj->variant.file_variant.file_size = 0; -+ the_obj->variant.file_variant.scanned_size = 0; -+ the_obj->variant.file_variant.shrink_size = -+ yaffs_max_file_size(dev); -+ the_obj->variant.file_variant.top_level = 0; -+ the_obj->variant.file_variant.top = tn; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); -+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* No action required */ -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* todo this should not happen */ -+ break; -+ } -+ return the_obj; -+} -+ -+static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, -+ int number, u32 mode) -+{ -+ -+ struct yaffs_obj *obj = -+ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); -+ -+ if (!obj) -+ return NULL; -+ -+ obj->fake = 1; /* it is fake so it might not use NAND */ -+ obj->rename_allowed = 0; -+ obj->unlink_allowed = 0; -+ obj->deleted = 0; -+ obj->unlinked = 0; -+ obj->yst_mode = mode; -+ obj->my_dev = dev; -+ obj->hdr_chunk = 0; /* Not a valid chunk. */ -+ return obj; -+ -+} -+ -+ -+static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ dev->n_obj = 0; -+ dev->n_tnodes = 0; -+ yaffs_init_raw_tnodes_and_objs(dev); -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ INIT_LIST_HEAD(&dev->obj_bucket[i].list); -+ dev->obj_bucket[i].count = 0; -+ } -+} -+ -+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, -+ int number, -+ enum yaffs_obj_type type) -+{ -+ struct yaffs_obj *the_obj = NULL; -+ -+ if (number > 0) -+ the_obj = yaffs_find_by_number(dev, number); -+ -+ if (!the_obj) -+ the_obj = yaffs_new_obj(dev, number, type); -+ -+ return the_obj; -+ -+} -+ -+YCHAR *yaffs_clone_str(const YCHAR *str) -+{ -+ YCHAR *new_str = NULL; -+ int len; -+ -+ if (!str) -+ str = _Y(""); -+ -+ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); -+ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); -+ if (new_str) { -+ strncpy(new_str, str, len); -+ new_str[len] = 0; -+ } -+ return new_str; -+ -+} -+/* -+ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new -+ * link (ie. name) is created or deleted in the directory. -+ * -+ * ie. -+ * create dir/a : update dir's mtime/ctime -+ * rm dir/a: update dir's mtime/ctime -+ * modify dir/a: don't update dir's mtimme/ctime -+ * -+ * This can be handled immediately or defered. Defering helps reduce the number -+ * of updates when many files in a directory are changed within a brief period. -+ * -+ * If the directory updating is defered then yaffs_update_dirty_dirs must be -+ * called periodically. -+ */ -+ -+static void yaffs_update_parent(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ -+ if (!obj) -+ return; -+ dev = obj->my_dev; -+ obj->dirty = 1; -+ yaffs_load_current_time(obj, 0, 1); -+ if (dev->param.defered_dir_update) { -+ struct list_head *link = &obj->variant.dir_variant.dirty; -+ -+ if (list_empty(link)) { -+ list_add(link, &dev->dirty_dirs); -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "Added object %d to dirty directories", -+ obj->obj_id); -+ } -+ -+ } else { -+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); -+ } -+} -+ -+void yaffs_update_dirty_dirs(struct yaffs_dev *dev) -+{ -+ struct list_head *link; -+ struct yaffs_obj *obj; -+ struct yaffs_dir_var *d_s; -+ union yaffs_obj_var *o_v; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); -+ -+ while (!list_empty(&dev->dirty_dirs)) { -+ link = dev->dirty_dirs.next; -+ list_del_init(link); -+ -+ d_s = list_entry(link, struct yaffs_dir_var, dirty); -+ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); -+ obj = list_entry(o_v, struct yaffs_obj, variant); -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", -+ obj->obj_id); -+ -+ if (obj->dirty) -+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); -+ } -+} -+ -+/* -+ * Mknod (create) a new object. -+ * equiv_obj only has meaning for a hard link; -+ * alias_str only has meaning for a symlink. -+ * rdev only has meaning for devices (a subset of special objects) -+ */ -+ -+static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, -+ struct yaffs_obj *parent, -+ const YCHAR *name, -+ u32 mode, -+ u32 uid, -+ u32 gid, -+ struct yaffs_obj *equiv_obj, -+ const YCHAR *alias_str, u32 rdev) -+{ -+ struct yaffs_obj *in; -+ YCHAR *str = NULL; -+ struct yaffs_dev *dev = parent->my_dev; -+ -+ /* Check if the entry exists. -+ * If it does then fail the call since we don't want a dup. */ -+ if (yaffs_find_by_name(parent, name)) -+ return NULL; -+ -+ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ str = yaffs_clone_str(alias_str); -+ if (!str) -+ return NULL; -+ } -+ -+ in = yaffs_new_obj(dev, -1, type); -+ -+ if (!in) { -+ kfree(str); -+ return NULL; -+ } -+ -+ in->hdr_chunk = 0; -+ in->valid = 1; -+ in->variant_type = type; -+ -+ in->yst_mode = mode; -+ -+ yaffs_attribs_init(in, gid, uid, rdev); -+ -+ in->n_data_chunks = 0; -+ -+ yaffs_set_obj_name(in, name); -+ in->dirty = 1; -+ -+ yaffs_add_obj_to_dir(parent, in); -+ -+ in->my_dev = parent->my_dev; -+ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ in->variant.symlink_variant.alias = str; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ in->variant.hardlink_variant.equiv_obj = equiv_obj; -+ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; -+ list_add(&in->hard_links, &equiv_obj->hard_links); -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* do nothing */ -+ break; -+ } -+ -+ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { -+ /* Could not create the object header, fail */ -+ yaffs_del_obj(in); -+ in = NULL; -+ } -+ -+ if (in) -+ yaffs_update_parent(parent); -+ -+ return in; -+} -+ -+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, -+ uid, gid, NULL, NULL, 0); -+} -+ -+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, -+ u32 mode, u32 uid, u32 gid) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, -+ mode, uid, gid, NULL, NULL, 0); -+} -+ -+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, u32 rdev) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, -+ uid, gid, NULL, NULL, rdev); -+} -+ -+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, const YCHAR *alias) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, -+ uid, gid, NULL, alias, 0); -+} -+ -+/* yaffs_link_obj returns the object id of the equivalent object.*/ -+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, -+ struct yaffs_obj *equiv_obj) -+{ -+ /* Get the real object in case we were fed a hard link obj */ -+ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); -+ -+ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, -+ parent, name, 0, 0, 0, -+ equiv_obj, NULL, 0)) -+ return equiv_obj; -+ -+ return NULL; -+ -+} -+ -+ -+ -+/*---------------------- Block Management and Page Allocation -------------*/ -+ -+static void yaffs_deinit_blocks(struct yaffs_dev *dev) -+{ -+ if (dev->block_info_alt && dev->block_info) -+ vfree(dev->block_info); -+ else -+ kfree(dev->block_info); -+ -+ dev->block_info_alt = 0; -+ -+ dev->block_info = NULL; -+ -+ if (dev->chunk_bits_alt && dev->chunk_bits) -+ vfree(dev->chunk_bits); -+ else -+ kfree(dev->chunk_bits); -+ dev->chunk_bits_alt = 0; -+ dev->chunk_bits = NULL; -+} -+ -+static int yaffs_init_blocks(struct yaffs_dev *dev) -+{ -+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ -+ dev->block_info = NULL; -+ dev->chunk_bits = NULL; -+ dev->alloc_block = -1; /* force it to get a new one */ -+ -+ /* If the first allocation strategy fails, thry the alternate one */ -+ dev->block_info = -+ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); -+ if (!dev->block_info) { -+ dev->block_info = -+ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); -+ dev->block_info_alt = 1; -+ } else { -+ dev->block_info_alt = 0; -+ } -+ -+ if (!dev->block_info) -+ goto alloc_error; -+ -+ /* Set up dynamic blockinfo stuff. Round up bytes. */ -+ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; -+ dev->chunk_bits = -+ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); -+ if (!dev->chunk_bits) { -+ dev->chunk_bits = -+ vmalloc(dev->chunk_bit_stride * n_blocks); -+ dev->chunk_bits_alt = 1; -+ } else { -+ dev->chunk_bits_alt = 0; -+ } -+ if (!dev->chunk_bits) -+ goto alloc_error; -+ -+ -+ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); -+ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); -+ return YAFFS_OK; -+ -+alloc_error: -+ yaffs_deinit_blocks(dev); -+ return YAFFS_FAIL; -+} -+ -+ -+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); -+ int erased_ok = 0; -+ int i; -+ -+ /* If the block is still healthy erase it and mark as clean. -+ * If the block has had a data failure, then retire it. -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, -+ "yaffs_block_became_dirty block %d state %d %s", -+ block_no, bi->block_state, -+ (bi->needs_retiring) ? "needs retiring" : ""); -+ -+ yaffs2_clear_oldest_dirty_seq(dev, bi); -+ -+ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; -+ -+ /* If this is the block being garbage collected then stop gc'ing */ -+ if (block_no == dev->gc_block) -+ dev->gc_block = 0; -+ -+ /* If this block is currently the best candidate for gc -+ * then drop as a candidate */ -+ if (block_no == dev->gc_dirtiest) { -+ dev->gc_dirtiest = 0; -+ dev->gc_pages_in_use = 0; -+ } -+ -+ if (!bi->needs_retiring) { -+ yaffs2_checkpt_invalidate(dev); -+ erased_ok = yaffs_erase_block(dev, block_no); -+ if (!erased_ok) { -+ dev->n_erase_failures++; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Erasure failed %d", block_no); -+ } -+ } -+ -+ /* Verify erasure if needed */ -+ if (erased_ok && -+ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || -+ !yaffs_skip_verification(dev))) { -+ for (i = 0; i < dev->param.chunks_per_block; i++) { -+ if (!yaffs_check_chunk_erased(dev, -+ block_no * dev->param.chunks_per_block + i)) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ ">>Block %d erasure supposedly OK, but chunk %d not erased", -+ block_no, i); -+ } -+ } -+ } -+ -+ if (!erased_ok) { -+ /* We lost a block of free space */ -+ dev->n_free_chunks -= dev->param.chunks_per_block; -+ yaffs_retire_block(dev, block_no); -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Block %d retired", block_no); -+ return; -+ } -+ -+ /* Clean it up... */ -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ bi->seq_number = 0; -+ dev->n_erased_blocks++; -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ bi->has_shrink_hdr = 0; -+ bi->skip_erased_check = 1; /* Clean, so no need to check */ -+ bi->gc_prioritise = 0; -+ bi->has_summary = 0; -+ -+ yaffs_clear_chunk_bits(dev, block_no); -+ -+ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); -+} -+ -+static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, -+ int old_chunk, u8 *buffer) -+{ -+ int new_chunk; -+ int mark_flash = 1; -+ struct yaffs_ext_tags tags; -+ struct yaffs_obj *object; -+ int matching_chunk; -+ int ret_val = YAFFS_OK; -+ -+ memset(&tags, 0, sizeof(tags)); -+ yaffs_rd_chunk_tags_nand(dev, old_chunk, -+ buffer, &tags); -+ object = yaffs_find_by_number(dev, tags.obj_id); -+ -+ yaffs_trace(YAFFS_TRACE_GC_DETAIL, -+ "Collecting chunk in block %d, %d %d %d ", -+ dev->gc_chunk, tags.obj_id, -+ tags.chunk_id, tags.n_bytes); -+ -+ if (object && !yaffs_skip_verification(dev)) { -+ if (tags.chunk_id == 0) -+ matching_chunk = -+ object->hdr_chunk; -+ else if (object->soft_del) -+ /* Defeat the test */ -+ matching_chunk = old_chunk; -+ else -+ matching_chunk = -+ yaffs_find_chunk_in_file -+ (object, tags.chunk_id, -+ NULL); -+ -+ if (old_chunk != matching_chunk) -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "gc: page in gc mismatch: %d %d %d %d", -+ old_chunk, -+ matching_chunk, -+ tags.obj_id, -+ tags.chunk_id); -+ } -+ -+ if (!object) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "page %d in gc has no object: %d %d %d ", -+ old_chunk, -+ tags.obj_id, tags.chunk_id, -+ tags.n_bytes); -+ } -+ -+ if (object && -+ object->deleted && -+ object->soft_del && tags.chunk_id != 0) { -+ /* Data chunk in a soft deleted file, -+ * throw it away. -+ * It's a soft deleted data chunk, -+ * No need to copy this, just forget -+ * about it and fix up the object. -+ */ -+ -+ /* Free chunks already includes -+ * softdeleted chunks, how ever this -+ * chunk is going to soon be really -+ * deleted which will increment free -+ * chunks. We have to decrement free -+ * chunks so this works out properly. -+ */ -+ dev->n_free_chunks--; -+ bi->soft_del_pages--; -+ -+ object->n_data_chunks--; -+ if (object->n_data_chunks <= 0) { -+ /* remeber to clean up obj */ -+ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; -+ dev->n_clean_ups++; -+ } -+ mark_flash = 0; -+ } else if (object) { -+ /* It's either a data chunk in a live -+ * file or an ObjectHeader, so we're -+ * interested in it. -+ * NB Need to keep the ObjectHeaders of -+ * deleted files until the whole file -+ * has been deleted off -+ */ -+ tags.serial_number++; -+ dev->n_gc_copies++; -+ -+ if (tags.chunk_id == 0) { -+ /* It is an object Id, -+ * We need to nuke the -+ * shrinkheader flags since its -+ * work is done. -+ * Also need to clean up -+ * shadowing. -+ */ -+ struct yaffs_obj_hdr *oh; -+ oh = (struct yaffs_obj_hdr *) buffer; -+ -+ oh->is_shrink = 0; -+ tags.extra_is_shrink = 0; -+ oh->shadows_obj = 0; -+ oh->inband_shadowed_obj_id = 0; -+ tags.extra_shadows = 0; -+ -+ /* Update file size */ -+ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { -+ yaffs_oh_size_load(oh, -+ object->variant.file_variant.file_size); -+ tags.extra_file_size = -+ object->variant.file_variant.file_size; -+ } -+ -+ yaffs_verify_oh(object, oh, &tags, 1); -+ new_chunk = -+ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); -+ } else { -+ new_chunk = -+ yaffs_write_new_chunk(dev, buffer, &tags, 1); -+ } -+ -+ if (new_chunk < 0) { -+ ret_val = YAFFS_FAIL; -+ } else { -+ -+ /* Now fix up the Tnodes etc. */ -+ -+ if (tags.chunk_id == 0) { -+ /* It's a header */ -+ object->hdr_chunk = new_chunk; -+ object->serial = tags.serial_number; -+ } else { -+ /* It's a data chunk */ -+ yaffs_put_chunk_in_file(object, tags.chunk_id, -+ new_chunk, 0); -+ } -+ } -+ } -+ if (ret_val == YAFFS_OK) -+ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); -+ return ret_val; -+} -+ -+static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) -+{ -+ int old_chunk; -+ int ret_val = YAFFS_OK; -+ int i; -+ int is_checkpt_block; -+ int max_copies; -+ int chunks_before = yaffs_get_erased_chunks(dev); -+ int chunks_after; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); -+ -+ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "Collecting block %d, in use %d, shrink %d, whole_block %d", -+ block, bi->pages_in_use, bi->has_shrink_hdr, -+ whole_block); -+ -+ /*yaffs_verify_free_chunks(dev); */ -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) -+ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; -+ -+ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ -+ -+ dev->gc_disable = 1; -+ -+ yaffs_summary_gc(dev, block); -+ -+ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "Collecting block %d that has no chunks in use", -+ block); -+ yaffs_block_became_dirty(dev, block); -+ } else { -+ -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ yaffs_verify_blk(dev, bi, block); -+ -+ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; -+ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; -+ -+ for (/* init already done */ ; -+ ret_val == YAFFS_OK && -+ dev->gc_chunk < dev->param.chunks_per_block && -+ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && -+ max_copies > 0; -+ dev->gc_chunk++, old_chunk++) { -+ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { -+ /* Page is in use and might need to be copied */ -+ max_copies--; -+ ret_val = yaffs_gc_process_chunk(dev, bi, -+ old_chunk, buffer); -+ } -+ } -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ -+ yaffs_verify_collected_blk(dev, bi, block); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { -+ /* -+ * The gc did not complete. Set block state back to FULL -+ * because checkpointing does not restore gc. -+ */ -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ } else { -+ /* The gc completed. */ -+ /* Do any required cleanups */ -+ for (i = 0; i < dev->n_clean_ups; i++) { -+ /* Time to delete the file too */ -+ struct yaffs_obj *object = -+ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); -+ if (object) { -+ yaffs_free_tnode(dev, -+ object->variant.file_variant.top); -+ object->variant.file_variant.top = NULL; -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: About to finally delete object %d", -+ object->obj_id); -+ yaffs_generic_obj_del(object); -+ object->my_dev->n_deleted_files--; -+ } -+ -+ } -+ chunks_after = yaffs_get_erased_chunks(dev); -+ if (chunks_before >= chunks_after) -+ yaffs_trace(YAFFS_TRACE_GC, -+ "gc did not increase free chunks before %d after %d", -+ chunks_before, chunks_after); -+ dev->gc_block = 0; -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ -+ dev->gc_disable = 0; -+ -+ return ret_val; -+} -+ -+/* -+ * find_gc_block() selects the dirtiest block (or close enough) -+ * for garbage collection. -+ */ -+ -+static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, -+ int aggressive, int background) -+{ -+ int i; -+ int iterations; -+ unsigned selected = 0; -+ int prioritised = 0; -+ int prioritised_exist = 0; -+ struct yaffs_block_info *bi; -+ int threshold; -+ -+ /* First let's see if we need to grab a prioritised block */ -+ if (dev->has_pending_prioritised_gc && !aggressive) { -+ dev->gc_dirtiest = 0; -+ bi = dev->block_info; -+ for (i = dev->internal_start_block; -+ i <= dev->internal_end_block && !selected; i++) { -+ -+ if (bi->gc_prioritise) { -+ prioritised_exist = 1; -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && -+ yaffs_block_ok_for_gc(dev, bi)) { -+ selected = i; -+ prioritised = 1; -+ } -+ } -+ bi++; -+ } -+ -+ /* -+ * If there is a prioritised block and none was selected then -+ * this happened because there is at least one old dirty block -+ * gumming up the works. Let's gc the oldest dirty block. -+ */ -+ -+ if (prioritised_exist && -+ !selected && dev->oldest_dirty_block > 0) -+ selected = dev->oldest_dirty_block; -+ -+ if (!prioritised_exist) /* None found, so we can clear this */ -+ dev->has_pending_prioritised_gc = 0; -+ } -+ -+ /* If we're doing aggressive GC then we are happy to take a less-dirty -+ * block, and search harder. -+ * else (leasurely gc), then we only bother to do this if the -+ * block has only a few pages in use. -+ */ -+ -+ if (!selected) { -+ int pages_used; -+ int n_blocks = -+ dev->internal_end_block - dev->internal_start_block + 1; -+ if (aggressive) { -+ threshold = dev->param.chunks_per_block; -+ iterations = n_blocks; -+ } else { -+ int max_threshold; -+ -+ if (background) -+ max_threshold = dev->param.chunks_per_block / 2; -+ else -+ max_threshold = dev->param.chunks_per_block / 8; -+ -+ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) -+ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; -+ -+ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; -+ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) -+ threshold = YAFFS_GC_PASSIVE_THRESHOLD; -+ if (threshold > max_threshold) -+ threshold = max_threshold; -+ -+ iterations = n_blocks / 16 + 1; -+ if (iterations > 100) -+ iterations = 100; -+ } -+ -+ for (i = 0; -+ i < iterations && -+ (dev->gc_dirtiest < 1 || -+ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); -+ i++) { -+ dev->gc_block_finder++; -+ if (dev->gc_block_finder < dev->internal_start_block || -+ dev->gc_block_finder > dev->internal_end_block) -+ dev->gc_block_finder = -+ dev->internal_start_block; -+ -+ bi = yaffs_get_block_info(dev, dev->gc_block_finder); -+ -+ pages_used = bi->pages_in_use - bi->soft_del_pages; -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && -+ pages_used < dev->param.chunks_per_block && -+ (dev->gc_dirtiest < 1 || -+ pages_used < dev->gc_pages_in_use) && -+ yaffs_block_ok_for_gc(dev, bi)) { -+ dev->gc_dirtiest = dev->gc_block_finder; -+ dev->gc_pages_in_use = pages_used; -+ } -+ } -+ -+ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) -+ selected = dev->gc_dirtiest; -+ } -+ -+ /* -+ * If nothing has been selected for a while, try the oldest dirty -+ * because that's gumming up the works. -+ */ -+ -+ if (!selected && dev->param.is_yaffs2 && -+ dev->gc_not_done >= (background ? 10 : 20)) { -+ yaffs2_find_oldest_dirty_seq(dev); -+ if (dev->oldest_dirty_block > 0) { -+ selected = dev->oldest_dirty_block; -+ dev->gc_dirtiest = selected; -+ dev->oldest_dirty_gc_count++; -+ bi = yaffs_get_block_info(dev, selected); -+ dev->gc_pages_in_use = -+ bi->pages_in_use - bi->soft_del_pages; -+ } else { -+ dev->gc_not_done = 0; -+ } -+ } -+ -+ if (selected) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC Selected block %d with %d free, prioritised:%d", -+ selected, -+ dev->param.chunks_per_block - dev->gc_pages_in_use, -+ prioritised); -+ -+ dev->n_gc_blocks++; -+ if (background) -+ dev->bg_gcs++; -+ -+ dev->gc_dirtiest = 0; -+ dev->gc_pages_in_use = 0; -+ dev->gc_not_done = 0; -+ if (dev->refresh_skip > 0) -+ dev->refresh_skip--; -+ } else { -+ dev->gc_not_done++; -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", -+ dev->gc_block_finder, dev->gc_not_done, threshold, -+ dev->gc_dirtiest, dev->gc_pages_in_use, -+ dev->oldest_dirty_block, background ? " bg" : ""); -+ } -+ -+ return selected; -+} -+ -+/* New garbage collector -+ * If we're very low on erased blocks then we do aggressive garbage collection -+ * otherwise we do "leasurely" garbage collection. -+ * Aggressive gc looks further (whole array) and will accept less dirty blocks. -+ * Passive gc only inspects smaller areas and only accepts more dirty blocks. -+ * -+ * The idea is to help clear out space in a more spread-out manner. -+ * Dunno if it really does anything useful. -+ */ -+static int yaffs_check_gc(struct yaffs_dev *dev, int background) -+{ -+ int aggressive = 0; -+ int gc_ok = YAFFS_OK; -+ int max_tries = 0; -+ int min_erased; -+ int erased_chunks; -+ int checkpt_block_adjust; -+ -+ if (dev->param.gc_control_fn && -+ (dev->param.gc_control_fn(dev) & 1) == 0) -+ return YAFFS_OK; -+ -+ if (dev->gc_disable) -+ /* Bail out so we don't get recursive gc */ -+ return YAFFS_OK; -+ -+ /* This loop should pass the first time. -+ * Only loops here if the collection does not increase space. -+ */ -+ -+ do { -+ max_tries++; -+ -+ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); -+ -+ min_erased = -+ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; -+ erased_chunks = -+ dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ /* If we need a block soon then do aggressive gc. */ -+ if (dev->n_erased_blocks < min_erased) -+ aggressive = 1; -+ else { -+ if (!background -+ && erased_chunks > (dev->n_free_chunks / 4)) -+ break; -+ -+ if (dev->gc_skip > 20) -+ dev->gc_skip = 20; -+ if (erased_chunks < dev->n_free_chunks / 2 || -+ dev->gc_skip < 1 || background) -+ aggressive = 0; -+ else { -+ dev->gc_skip--; -+ break; -+ } -+ } -+ -+ dev->gc_skip = 5; -+ -+ /* If we don't already have a block being gc'd then see if we -+ * should start another */ -+ -+ if (dev->gc_block < 1 && !aggressive) { -+ dev->gc_block = yaffs2_find_refresh_block(dev); -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ if (dev->gc_block < 1) { -+ dev->gc_block = -+ yaffs_find_gc_block(dev, aggressive, background); -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ -+ if (dev->gc_block > 0) { -+ dev->all_gcs++; -+ if (!aggressive) -+ dev->passive_gc_count++; -+ -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: GC n_erased_blocks %d aggressive %d", -+ dev->n_erased_blocks, aggressive); -+ -+ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); -+ } -+ -+ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && -+ dev->gc_block > 0) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", -+ dev->n_erased_blocks, max_tries, -+ dev->gc_block); -+ } -+ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && -+ (dev->gc_block > 0) && (max_tries < 2)); -+ -+ return aggressive ? gc_ok : YAFFS_OK; -+} -+ -+/* -+ * yaffs_bg_gc() -+ * Garbage collects. Intended to be called from a background thread. -+ * Returns non-zero if at least half the free chunks are erased. -+ */ -+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) -+{ -+ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); -+ -+ yaffs_check_gc(dev, 1); -+ return erased_chunks > dev->n_free_chunks / 2; -+} -+ -+/*-------------------- Data file manipulation -----------------*/ -+ -+static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) -+{ -+ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); -+ -+ if (nand_chunk >= 0) -+ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, -+ buffer, NULL); -+ else { -+ yaffs_trace(YAFFS_TRACE_NANDACCESS, -+ "Chunk %d not found zero instead", -+ nand_chunk); -+ /* get sane (zero) data if you read a hole */ -+ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); -+ return 0; -+ } -+ -+} -+ -+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, -+ int lyn) -+{ -+ int block; -+ int page; -+ struct yaffs_ext_tags tags; -+ struct yaffs_block_info *bi; -+ -+ if (chunk_id <= 0) -+ return; -+ -+ dev->n_deletions++; -+ block = chunk_id / dev->param.chunks_per_block; -+ page = chunk_id % dev->param.chunks_per_block; -+ -+ if (!yaffs_check_chunk_bit(dev, block, page)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Deleting invalid chunk %d", chunk_id); -+ -+ bi = yaffs_get_block_info(dev, block); -+ -+ yaffs2_update_oldest_dirty_seq(dev, block, bi); -+ -+ yaffs_trace(YAFFS_TRACE_DELETION, -+ "line %d delete of chunk %d", -+ lyn, chunk_id); -+ -+ if (!dev->param.is_yaffs2 && mark_flash && -+ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { -+ -+ memset(&tags, 0, sizeof(tags)); -+ tags.is_deleted = 1; -+ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); -+ yaffs_handle_chunk_update(dev, chunk_id, &tags); -+ } else { -+ dev->n_unmarked_deletions++; -+ } -+ -+ /* Pull out of the management area. -+ * If the whole block became dirty, this will kick off an erasure. -+ */ -+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || -+ bi->block_state == YAFFS_BLOCK_STATE_FULL || -+ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { -+ dev->n_free_chunks++; -+ yaffs_clear_chunk_bit(dev, block, page); -+ bi->pages_in_use--; -+ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && -+ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ yaffs_block_became_dirty(dev, block); -+ } -+ } -+} -+ -+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, -+ const u8 *buffer, int n_bytes, int use_reserve) -+{ -+ /* Find old chunk Need to do this to get serial number -+ * Write new one and patch into tree. -+ * Invalidate old tags. -+ */ -+ -+ int prev_chunk_id; -+ struct yaffs_ext_tags prev_tags; -+ int new_chunk_id; -+ struct yaffs_ext_tags new_tags; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ yaffs_check_gc(dev, 0); -+ -+ /* Get the previous chunk at this location in the file if it exists. -+ * If it does not exist then put a zero into the tree. This creates -+ * the tnode now, rather than later when it is harder to clean up. -+ */ -+ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); -+ if (prev_chunk_id < 1 && -+ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) -+ return 0; -+ -+ /* Set up new tags */ -+ memset(&new_tags, 0, sizeof(new_tags)); -+ -+ new_tags.chunk_id = inode_chunk; -+ new_tags.obj_id = in->obj_id; -+ new_tags.serial_number = -+ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; -+ new_tags.n_bytes = n_bytes; -+ -+ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Writing %d bytes to chunk!!!!!!!!!", -+ n_bytes); -+ BUG(); -+ } -+ -+ new_chunk_id = -+ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); -+ -+ if (new_chunk_id > 0) { -+ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); -+ -+ if (prev_chunk_id > 0) -+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -+ -+ yaffs_verify_file_sane(in); -+ } -+ return new_chunk_id; -+ -+} -+ -+ -+ -+static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, -+ const YCHAR *name, const void *value, int size, -+ int flags) -+{ -+ struct yaffs_xattr_mod xmod; -+ int result; -+ -+ xmod.set = set; -+ xmod.name = name; -+ xmod.data = value; -+ xmod.size = size; -+ xmod.flags = flags; -+ xmod.result = -ENOSPC; -+ -+ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); -+ -+ if (result > 0) -+ return xmod.result; -+ else -+ return -ENOSPC; -+} -+ -+static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, -+ struct yaffs_xattr_mod *xmod) -+{ -+ int retval = 0; -+ int x_offs = sizeof(struct yaffs_obj_hdr); -+ struct yaffs_dev *dev = obj->my_dev; -+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); -+ char *x_buffer = buffer + x_offs; -+ -+ if (xmod->set) -+ retval = -+ nval_set(x_buffer, x_size, xmod->name, xmod->data, -+ xmod->size, xmod->flags); -+ else -+ retval = nval_del(x_buffer, x_size, xmod->name); -+ -+ obj->has_xattr = nval_hasvalues(x_buffer, x_size); -+ obj->xattr_known = 1; -+ xmod->result = retval; -+ -+ return retval; -+} -+ -+static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, -+ void *value, int size) -+{ -+ char *buffer = NULL; -+ int result; -+ struct yaffs_ext_tags tags; -+ struct yaffs_dev *dev = obj->my_dev; -+ int x_offs = sizeof(struct yaffs_obj_hdr); -+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); -+ char *x_buffer; -+ int retval = 0; -+ -+ if (obj->hdr_chunk < 1) -+ return -ENODATA; -+ -+ /* If we know that the object has no xattribs then don't do all the -+ * reading and parsing. -+ */ -+ if (obj->xattr_known && !obj->has_xattr) { -+ if (name) -+ return -ENODATA; -+ else -+ return 0; -+ } -+ -+ buffer = (char *)yaffs_get_temp_buffer(dev); -+ if (!buffer) -+ return -ENOMEM; -+ -+ result = -+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); -+ -+ if (result != YAFFS_OK) -+ retval = -ENOENT; -+ else { -+ x_buffer = buffer + x_offs; -+ -+ if (!obj->xattr_known) { -+ obj->has_xattr = nval_hasvalues(x_buffer, x_size); -+ obj->xattr_known = 1; -+ } -+ -+ if (name) -+ retval = nval_get(x_buffer, x_size, name, value, size); -+ else -+ retval = nval_list(x_buffer, x_size, value, size); -+ } -+ yaffs_release_temp_buffer(dev, (u8 *) buffer); -+ return retval; -+} -+ -+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, -+ const void *value, int size, int flags) -+{ -+ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); -+} -+ -+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) -+{ -+ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); -+} -+ -+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, -+ int size) -+{ -+ return yaffs_do_xattrib_fetch(obj, name, value, size); -+} -+ -+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) -+{ -+ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); -+} -+ -+static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) -+{ -+ u8 *buf; -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_dev *dev; -+ struct yaffs_ext_tags tags; -+ int result; -+ int alloc_failed = 0; -+ -+ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) -+ return; -+ -+ dev = in->my_dev; -+ in->lazy_loaded = 0; -+ buf = yaffs_get_temp_buffer(dev); -+ -+ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); -+ oh = (struct yaffs_obj_hdr *)buf; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ yaffs_set_obj_name_from_oh(in, oh); -+ -+ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ in->variant.symlink_variant.alias = -+ yaffs_clone_str(oh->alias); -+ if (!in->variant.symlink_variant.alias) -+ alloc_failed = 1; /* Not returned */ -+ } -+ yaffs_release_temp_buffer(dev, buf); -+} -+ -+/* UpdateObjectHeader updates the header on NAND for an object. -+ * If name is not NULL, then that new name is used. -+ */ -+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, -+ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) -+{ -+ -+ struct yaffs_block_info *bi; -+ struct yaffs_dev *dev = in->my_dev; -+ int prev_chunk_id; -+ int ret_val = 0; -+ int result = 0; -+ int new_chunk_id; -+ struct yaffs_ext_tags new_tags; -+ struct yaffs_ext_tags old_tags; -+ const YCHAR *alias = NULL; -+ u8 *buffer = NULL; -+ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; -+ struct yaffs_obj_hdr *oh = NULL; -+ loff_t file_size = 0; -+ -+ strcpy(old_name, _Y("silly old name")); -+ -+ if (in->fake && in != dev->root_dir && !force && !xmod) -+ return ret_val; -+ -+ yaffs_check_gc(dev, 0); -+ yaffs_check_obj_details_loaded(in); -+ -+ buffer = yaffs_get_temp_buffer(in->my_dev); -+ oh = (struct yaffs_obj_hdr *)buffer; -+ -+ prev_chunk_id = in->hdr_chunk; -+ -+ if (prev_chunk_id > 0) { -+ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, -+ buffer, &old_tags); -+ -+ yaffs_verify_oh(in, oh, &old_tags, 0); -+ memcpy(old_name, oh->name, sizeof(oh->name)); -+ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); -+ } else { -+ memset(buffer, 0xff, dev->data_bytes_per_chunk); -+ } -+ -+ oh->type = in->variant_type; -+ oh->yst_mode = in->yst_mode; -+ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; -+ -+ yaffs_load_attribs_oh(oh, in); -+ -+ if (in->parent) -+ oh->parent_obj_id = in->parent->obj_id; -+ else -+ oh->parent_obj_id = 0; -+ -+ if (name && *name) { -+ memset(oh->name, 0, sizeof(oh->name)); -+ yaffs_load_oh_from_name(dev, oh->name, name); -+ } else if (prev_chunk_id > 0) { -+ memcpy(oh->name, old_name, sizeof(oh->name)); -+ } else { -+ memset(oh->name, 0, sizeof(oh->name)); -+ } -+ -+ oh->is_shrink = is_shrink; -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Should not happen */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && -+ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) -+ file_size = in->variant.file_variant.file_size; -+ yaffs_oh_size_load(oh, file_size); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ oh->equiv_id = in->variant.hardlink_variant.equiv_id; -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ alias = in->variant.symlink_variant.alias; -+ if (!alias) -+ alias = _Y("no alias"); -+ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); -+ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; -+ break; -+ } -+ -+ /* process any xattrib modifications */ -+ if (xmod) -+ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); -+ -+ /* Tags */ -+ memset(&new_tags, 0, sizeof(new_tags)); -+ in->serial++; -+ new_tags.chunk_id = 0; -+ new_tags.obj_id = in->obj_id; -+ new_tags.serial_number = in->serial; -+ -+ /* Add extra info for file header */ -+ new_tags.extra_available = 1; -+ new_tags.extra_parent_id = oh->parent_obj_id; -+ new_tags.extra_file_size = file_size; -+ new_tags.extra_is_shrink = oh->is_shrink; -+ new_tags.extra_equiv_id = oh->equiv_id; -+ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; -+ new_tags.extra_obj_type = in->variant_type; -+ yaffs_verify_oh(in, oh, &new_tags, 1); -+ -+ /* Create new chunk in NAND */ -+ new_chunk_id = -+ yaffs_write_new_chunk(dev, buffer, &new_tags, -+ (prev_chunk_id > 0) ? 1 : 0); -+ -+ if (buffer) -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ if (new_chunk_id < 0) -+ return new_chunk_id; -+ -+ in->hdr_chunk = new_chunk_id; -+ -+ if (prev_chunk_id > 0) -+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -+ -+ if (!yaffs_obj_cache_dirty(in)) -+ in->dirty = 0; -+ -+ /* If this was a shrink, then mark the block -+ * that the chunk lives on */ -+ if (is_shrink) { -+ bi = yaffs_get_block_info(in->my_dev, -+ new_chunk_id / -+ in->my_dev->param.chunks_per_block); -+ bi->has_shrink_hdr = 1; -+ } -+ -+ -+ return new_chunk_id; -+} -+ -+/*--------------------- File read/write ------------------------ -+ * Read and write have very similar structures. -+ * In general the read/write has three parts to it -+ * An incomplete chunk to start with (if the read/write is not chunk-aligned) -+ * Some complete chunks -+ * An incomplete chunk to end off with -+ * -+ * Curve-balls: the first chunk might also be the last chunk. -+ */ -+ -+int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) -+{ -+ int chunk; -+ u32 start; -+ int n_copy; -+ int n = n_bytes; -+ int n_done = 0; -+ struct yaffs_cache *cache; -+ struct yaffs_dev *dev; -+ -+ dev = in->my_dev; -+ -+ while (n > 0) { -+ yaffs_addr_to_chunk(dev, offset, &chunk, &start); -+ chunk++; -+ -+ /* OK now check for the curveball where the start and end are in -+ * the same chunk. -+ */ -+ if ((start + n) < dev->data_bytes_per_chunk) -+ n_copy = n; -+ else -+ n_copy = dev->data_bytes_per_chunk - start; -+ -+ cache = yaffs_find_chunk_cache(in, chunk); -+ -+ /* If the chunk is already in the cache or it is less than -+ * a whole chunk or we're using inband tags then use the cache -+ * (if there is caching) else bypass the cache. -+ */ -+ if (cache || n_copy != dev->data_bytes_per_chunk || -+ dev->param.inband_tags) { -+ if (dev->param.n_caches > 0) { -+ -+ /* If we can't find the data in the cache, -+ * then load it up. */ -+ -+ if (!cache) { -+ cache = -+ yaffs_grab_chunk_cache(in->my_dev); -+ cache->object = in; -+ cache->chunk_id = chunk; -+ cache->dirty = 0; -+ cache->locked = 0; -+ yaffs_rd_data_obj(in, chunk, -+ cache->data); -+ cache->n_bytes = 0; -+ } -+ -+ yaffs_use_cache(dev, cache, 0); -+ -+ cache->locked = 1; -+ -+ memcpy(buffer, &cache->data[start], n_copy); -+ -+ cache->locked = 0; -+ } else { -+ /* Read into the local buffer then copy.. */ -+ -+ u8 *local_buffer = -+ yaffs_get_temp_buffer(dev); -+ yaffs_rd_data_obj(in, chunk, local_buffer); -+ -+ memcpy(buffer, &local_buffer[start], n_copy); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ } else { -+ /* A full chunk. Read directly into the buffer. */ -+ yaffs_rd_data_obj(in, chunk, buffer); -+ } -+ n -= n_copy; -+ offset += n_copy; -+ buffer += n_copy; -+ n_done += n_copy; -+ } -+ return n_done; -+} -+ -+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_through) -+{ -+ -+ int chunk; -+ u32 start; -+ int n_copy; -+ int n = n_bytes; -+ int n_done = 0; -+ int n_writeback; -+ loff_t start_write = offset; -+ int chunk_written = 0; -+ u32 n_bytes_read; -+ loff_t chunk_start; -+ struct yaffs_dev *dev; -+ -+ dev = in->my_dev; -+ -+ while (n > 0 && chunk_written >= 0) { -+ yaffs_addr_to_chunk(dev, offset, &chunk, &start); -+ -+ if (((loff_t)chunk) * -+ dev->data_bytes_per_chunk + start != offset || -+ start >= dev->data_bytes_per_chunk) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "AddrToChunk of offset %lld gives chunk %d start %d", -+ offset, chunk, start); -+ } -+ chunk++; /* File pos to chunk in file offset */ -+ -+ /* OK now check for the curveball where the start and end are in -+ * the same chunk. -+ */ -+ -+ if ((start + n) < dev->data_bytes_per_chunk) { -+ n_copy = n; -+ -+ /* Now calculate how many bytes to write back.... -+ * If we're overwriting and not writing to then end of -+ * file then we need to write back as much as was there -+ * before. -+ */ -+ -+ chunk_start = (((loff_t)(chunk - 1)) * -+ dev->data_bytes_per_chunk); -+ -+ if (chunk_start > in->variant.file_variant.file_size) -+ n_bytes_read = 0; /* Past end of file */ -+ else -+ n_bytes_read = -+ in->variant.file_variant.file_size - -+ chunk_start; -+ -+ if (n_bytes_read > dev->data_bytes_per_chunk) -+ n_bytes_read = dev->data_bytes_per_chunk; -+ -+ n_writeback = -+ (n_bytes_read > -+ (start + n)) ? n_bytes_read : (start + n); -+ -+ if (n_writeback < 0 || -+ n_writeback > dev->data_bytes_per_chunk) -+ BUG(); -+ -+ } else { -+ n_copy = dev->data_bytes_per_chunk - start; -+ n_writeback = dev->data_bytes_per_chunk; -+ } -+ -+ if (n_copy != dev->data_bytes_per_chunk || -+ !dev->param.cache_bypass_aligned || -+ dev->param.inband_tags) { -+ /* An incomplete start or end chunk (or maybe both -+ * start and end chunk), or we're using inband tags, -+ * or we're forcing writes through the cache, -+ * so we want to use the cache buffers. -+ */ -+ if (dev->param.n_caches > 0) { -+ struct yaffs_cache *cache; -+ -+ /* If we can't find the data in the cache, then -+ * load the cache */ -+ cache = yaffs_find_chunk_cache(in, chunk); -+ -+ if (!cache && -+ yaffs_check_alloc_available(dev, 1)) { -+ cache = yaffs_grab_chunk_cache(dev); -+ cache->object = in; -+ cache->chunk_id = chunk; -+ cache->dirty = 0; -+ cache->locked = 0; -+ yaffs_rd_data_obj(in, chunk, -+ cache->data); -+ } else if (cache && -+ !cache->dirty && -+ !yaffs_check_alloc_available(dev, -+ 1)) { -+ /* Drop the cache if it was a read cache -+ * item and no space check has been made -+ * for it. -+ */ -+ cache = NULL; -+ } -+ -+ if (cache) { -+ yaffs_use_cache(dev, cache, 1); -+ cache->locked = 1; -+ -+ memcpy(&cache->data[start], buffer, -+ n_copy); -+ -+ cache->locked = 0; -+ cache->n_bytes = n_writeback; -+ -+ if (write_through) { -+ chunk_written = -+ yaffs_wr_data_obj -+ (cache->object, -+ cache->chunk_id, -+ cache->data, -+ cache->n_bytes, 1); -+ cache->dirty = 0; -+ } -+ } else { -+ chunk_written = -1; /* fail write */ -+ } -+ } else { -+ /* An incomplete start or end chunk (or maybe -+ * both start and end chunk). Read into the -+ * local buffer then copy over and write back. -+ */ -+ -+ u8 *local_buffer = yaffs_get_temp_buffer(dev); -+ -+ yaffs_rd_data_obj(in, chunk, local_buffer); -+ memcpy(&local_buffer[start], buffer, n_copy); -+ -+ chunk_written = -+ yaffs_wr_data_obj(in, chunk, -+ local_buffer, -+ n_writeback, 0); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ } else { -+ /* A full chunk. Write directly from the buffer. */ -+ -+ chunk_written = -+ yaffs_wr_data_obj(in, chunk, buffer, -+ dev->data_bytes_per_chunk, 0); -+ -+ /* Since we've overwritten the cached data, -+ * we better invalidate it. */ -+ yaffs_invalidate_chunk_cache(in, chunk); -+ } -+ -+ if (chunk_written >= 0) { -+ n -= n_copy; -+ offset += n_copy; -+ buffer += n_copy; -+ n_done += n_copy; -+ } -+ } -+ -+ /* Update file object */ -+ -+ if ((start_write + n_done) > in->variant.file_variant.file_size) -+ in->variant.file_variant.file_size = (start_write + n_done); -+ -+ in->dirty = 1; -+ return n_done; -+} -+ -+int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_through) -+{ -+ yaffs2_handle_hole(in, offset); -+ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); -+} -+ -+/* ---------------------- File resizing stuff ------------------ */ -+ -+static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) -+{ -+ -+ struct yaffs_dev *dev = in->my_dev; -+ loff_t old_size = in->variant.file_variant.file_size; -+ int i; -+ int chunk_id; -+ u32 dummy; -+ int last_del; -+ int start_del; -+ -+ if (old_size > 0) -+ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); -+ else -+ last_del = 0; -+ -+ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, -+ &start_del, &dummy); -+ last_del++; -+ start_del++; -+ -+ /* Delete backwards so that we don't end up with holes if -+ * power is lost part-way through the operation. -+ */ -+ for (i = last_del; i >= start_del; i--) { -+ /* NB this could be optimised somewhat, -+ * eg. could retrieve the tags and write them without -+ * using yaffs_chunk_del -+ */ -+ -+ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); -+ -+ if (chunk_id < 1) -+ continue; -+ -+ if (chunk_id < -+ (dev->internal_start_block * dev->param.chunks_per_block) || -+ chunk_id >= -+ ((dev->internal_end_block + 1) * -+ dev->param.chunks_per_block)) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Found daft chunk_id %d for %d", -+ chunk_id, i); -+ } else { -+ in->n_data_chunks--; -+ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); -+ } -+ } -+} -+ -+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) -+{ -+ int new_full; -+ u32 new_partial; -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); -+ -+ yaffs_prune_chunks(obj, new_size); -+ -+ if (new_partial != 0) { -+ int last_chunk = 1 + new_full; -+ u8 *local_buffer = yaffs_get_temp_buffer(dev); -+ -+ /* Rewrite the last chunk with its new size and zero pad */ -+ yaffs_rd_data_obj(obj, last_chunk, local_buffer); -+ memset(local_buffer + new_partial, 0, -+ dev->data_bytes_per_chunk - new_partial); -+ -+ yaffs_wr_data_obj(obj, last_chunk, local_buffer, -+ new_partial, 1); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ -+ obj->variant.file_variant.file_size = new_size; -+ -+ yaffs_prune_tree(dev, &obj->variant.file_variant); -+} -+ -+int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) -+{ -+ struct yaffs_dev *dev = in->my_dev; -+ loff_t old_size = in->variant.file_variant.file_size; -+ -+ yaffs_flush_file_cache(in, 1); -+ yaffs_invalidate_whole_cache(in); -+ -+ yaffs_check_gc(dev, 0); -+ -+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return YAFFS_FAIL; -+ -+ if (new_size == old_size) -+ return YAFFS_OK; -+ -+ if (new_size > old_size) { -+ yaffs2_handle_hole(in, new_size); -+ in->variant.file_variant.file_size = new_size; -+ } else { -+ /* new_size < old_size */ -+ yaffs_resize_file_down(in, new_size); -+ } -+ -+ /* Write a new object header to reflect the resize. -+ * show we've shrunk the file, if need be -+ * Do this only if the file is not in the deleted directories -+ * and is not shadowed. -+ */ -+ if (in->parent && -+ !in->is_shadowed && -+ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && -+ in->parent->obj_id != YAFFS_OBJECTID_DELETED) -+ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); -+ -+ return YAFFS_OK; -+} -+ -+int yaffs_flush_file(struct yaffs_obj *in, -+ int update_time, -+ int data_sync, -+ int discard_cache) -+{ -+ if (!in->dirty) -+ return YAFFS_OK; -+ -+ yaffs_flush_file_cache(in, discard_cache); -+ -+ if (data_sync) -+ return YAFFS_OK; -+ -+ if (update_time) -+ yaffs_load_current_time(in, 0, 0); -+ -+ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? -+ YAFFS_OK : YAFFS_FAIL; -+} -+ -+ -+/* yaffs_del_file deletes the whole file data -+ * and the inode associated with the file. -+ * It does not delete the links associated with the file. -+ */ -+static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) -+{ -+ int ret_val; -+ int del_now = 0; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!in->my_inode) -+ del_now = 1; -+ -+ if (del_now) { -+ ret_val = -+ yaffs_change_obj_name(in, in->my_dev->del_dir, -+ _Y("deleted"), 0, 0); -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: immediate deletion of file %d", -+ in->obj_id); -+ in->deleted = 1; -+ in->my_dev->n_deleted_files++; -+ if (dev->param.disable_soft_del || dev->param.is_yaffs2) -+ yaffs_resize_file(in, 0); -+ yaffs_soft_del_file(in); -+ } else { -+ ret_val = -+ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, -+ _Y("unlinked"), 0, 0); -+ } -+ return ret_val; -+} -+ -+static int yaffs_del_file(struct yaffs_obj *in) -+{ -+ int ret_val = YAFFS_OK; -+ int deleted; /* Need to cache value on stack if in is freed */ -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (dev->param.disable_soft_del || dev->param.is_yaffs2) -+ yaffs_resize_file(in, 0); -+ -+ if (in->n_data_chunks > 0) { -+ /* Use soft deletion if there is data in the file. -+ * That won't be the case if it has been resized to zero. -+ */ -+ if (!in->unlinked) -+ ret_val = yaffs_unlink_file_if_needed(in); -+ -+ deleted = in->deleted; -+ -+ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { -+ in->deleted = 1; -+ deleted = 1; -+ in->my_dev->n_deleted_files++; -+ yaffs_soft_del_file(in); -+ } -+ return deleted ? YAFFS_OK : YAFFS_FAIL; -+ } else { -+ /* The file has no data chunks so we toss it immediately */ -+ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); -+ in->variant.file_variant.top = NULL; -+ yaffs_generic_obj_del(in); -+ -+ return YAFFS_OK; -+ } -+} -+ -+int yaffs_is_non_empty_dir(struct yaffs_obj *obj) -+{ -+ return (obj && -+ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && -+ !(list_empty(&obj->variant.dir_variant.children)); -+} -+ -+static int yaffs_del_dir(struct yaffs_obj *obj) -+{ -+ /* First check that the directory is empty. */ -+ if (yaffs_is_non_empty_dir(obj)) -+ return YAFFS_FAIL; -+ -+ return yaffs_generic_obj_del(obj); -+} -+ -+static int yaffs_del_symlink(struct yaffs_obj *in) -+{ -+ kfree(in->variant.symlink_variant.alias); -+ in->variant.symlink_variant.alias = NULL; -+ -+ return yaffs_generic_obj_del(in); -+} -+ -+static int yaffs_del_link(struct yaffs_obj *in) -+{ -+ /* remove this hardlink from the list associated with the equivalent -+ * object -+ */ -+ list_del_init(&in->hard_links); -+ return yaffs_generic_obj_del(in); -+} -+ -+int yaffs_del_obj(struct yaffs_obj *obj) -+{ -+ int ret_val = -1; -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ ret_val = yaffs_del_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ if (!list_empty(&obj->variant.dir_variant.dirty)) { -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "Remove object %d from dirty directories", -+ obj->obj_id); -+ list_del_init(&obj->variant.dir_variant.dirty); -+ } -+ return yaffs_del_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ ret_val = yaffs_del_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ ret_val = yaffs_del_link(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ ret_val = yaffs_generic_obj_del(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ ret_val = 0; -+ break; /* should not happen. */ -+ } -+ return ret_val; -+} -+ -+ -+static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, -+ struct yaffs_obj *to_dir) -+{ -+ struct yaffs_obj *obj; -+ struct list_head *lh; -+ struct list_head *n; -+ -+ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ yaffs_add_obj_to_dir(to_dir, obj); -+ } -+} -+ -+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, -+ enum yaffs_obj_type type) -+{ -+ /* Tear down the old variant */ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ /* Nuke file data */ -+ yaffs_resize_file(obj, 0); -+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); -+ obj->variant.file_variant.top = NULL; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Put the children in lost and found. */ -+ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); -+ if (!list_empty(&obj->variant.dir_variant.dirty)) -+ list_del_init(&obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ /* Nuke symplink data */ -+ kfree(obj->variant.symlink_variant.alias); -+ obj->variant.symlink_variant.alias = NULL; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ list_del_init(&obj->hard_links); -+ break; -+ default: -+ break; -+ } -+ -+ memset(&obj->variant, 0, sizeof(obj->variant)); -+ -+ /*Set up new variant if the memset is not enough. */ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ INIT_LIST_HEAD(&obj->variant.dir_variant.children); -+ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ default: -+ break; -+ } -+ -+ obj->variant_type = type; -+ -+ return obj; -+ -+} -+ -+static int yaffs_unlink_worker(struct yaffs_obj *obj) -+{ -+ int del_now = 0; -+ -+ if (!obj) -+ return YAFFS_FAIL; -+ -+ if (!obj->my_inode) -+ del_now = 1; -+ -+ yaffs_update_parent(obj->parent); -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { -+ return yaffs_del_link(obj); -+ } else if (!list_empty(&obj->hard_links)) { -+ /* Curve ball: We're unlinking an object that has a hardlink. -+ * -+ * This problem arises because we are not strictly following -+ * The Linux link/inode model. -+ * -+ * We can't really delete the object. -+ * Instead, we do the following: -+ * - Select a hardlink. -+ * - Unhook it from the hard links -+ * - Move it from its parent directory so that the rename works. -+ * - Rename the object to the hardlink's name. -+ * - Delete the hardlink -+ */ -+ -+ struct yaffs_obj *hl; -+ struct yaffs_obj *parent; -+ int ret_val; -+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ hl = list_entry(obj->hard_links.next, struct yaffs_obj, -+ hard_links); -+ -+ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); -+ parent = hl->parent; -+ -+ list_del_init(&hl->hard_links); -+ -+ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); -+ -+ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); -+ -+ if (ret_val == YAFFS_OK) -+ ret_val = yaffs_generic_obj_del(hl); -+ -+ return ret_val; -+ -+ } else if (del_now) { -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return yaffs_del_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ list_del_init(&obj->variant.dir_variant.dirty); -+ return yaffs_del_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ return yaffs_del_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ return yaffs_generic_obj_del(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ default: -+ return YAFFS_FAIL; -+ } -+ } else if (yaffs_is_non_empty_dir(obj)) { -+ return YAFFS_FAIL; -+ } else { -+ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, -+ _Y("unlinked"), 0, 0); -+ } -+} -+ -+static int yaffs_unlink_obj(struct yaffs_obj *obj) -+{ -+ if (obj && obj->unlink_allowed) -+ return yaffs_unlink_worker(obj); -+ -+ return YAFFS_FAIL; -+} -+ -+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) -+{ -+ struct yaffs_obj *obj; -+ -+ obj = yaffs_find_by_name(dir, name); -+ return yaffs_unlink_obj(obj); -+} -+ -+/* Note: -+ * If old_name is NULL then we take old_dir as the object to be renamed. -+ */ -+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, -+ struct yaffs_obj *new_dir, const YCHAR *new_name) -+{ -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_obj *existing_target = NULL; -+ int force = 0; -+ int result; -+ struct yaffs_dev *dev; -+ -+ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ dev = old_dir->my_dev; -+ -+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE -+ /* Special case for case insemsitive systems. -+ * While look-up is case insensitive, the name isn't. -+ * Therefore we might want to change x.txt to X.txt -+ */ -+ if (old_dir == new_dir && -+ old_name && new_name && -+ strcmp(old_name, new_name) == 0) -+ force = 1; -+#endif -+ -+ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > -+ YAFFS_MAX_NAME_LENGTH) -+ /* ENAMETOOLONG */ -+ return YAFFS_FAIL; -+ -+ if (old_name) -+ obj = yaffs_find_by_name(old_dir, old_name); -+ else{ -+ obj = old_dir; -+ old_dir = obj->parent; -+ } -+ -+ if (obj && obj->rename_allowed) { -+ /* Now handle an existing target, if there is one */ -+ existing_target = yaffs_find_by_name(new_dir, new_name); -+ if (yaffs_is_non_empty_dir(existing_target)) { -+ return YAFFS_FAIL; /* ENOTEMPTY */ -+ } else if (existing_target && existing_target != obj) { -+ /* Nuke the target first, using shadowing, -+ * but only if it isn't the same object. -+ * -+ * Note we must disable gc here otherwise it can mess -+ * up the shadowing. -+ * -+ */ -+ dev->gc_disable = 1; -+ yaffs_change_obj_name(obj, new_dir, new_name, force, -+ existing_target->obj_id); -+ existing_target->is_shadowed = 1; -+ yaffs_unlink_obj(existing_target); -+ dev->gc_disable = 0; -+ } -+ -+ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); -+ -+ yaffs_update_parent(old_dir); -+ if (new_dir != old_dir) -+ yaffs_update_parent(new_dir); -+ -+ return result; -+ } -+ return YAFFS_FAIL; -+} -+ -+/*----------------------- Initialisation Scanning ---------------------- */ -+ -+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, -+ int backward_scanning) -+{ -+ struct yaffs_obj *obj; -+ -+ if (backward_scanning) { -+ /* Handle YAFFS2 case (backward scanning) -+ * If the shadowed object exists then ignore. -+ */ -+ obj = yaffs_find_by_number(dev, obj_id); -+ if (obj) -+ return; -+ } -+ -+ /* Let's create it (if it does not exist) assuming it is a file so that -+ * it can do shrinking etc. -+ * We put it in unlinked dir to be cleaned up after the scanning -+ */ -+ obj = -+ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); -+ if (!obj) -+ return; -+ obj->is_shadowed = 1; -+ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); -+ obj->variant.file_variant.shrink_size = 0; -+ obj->valid = 1; /* So that we don't read any other info. */ -+} -+ -+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) -+{ -+ struct list_head *lh; -+ struct list_head *save; -+ struct yaffs_obj *hl; -+ struct yaffs_obj *in; -+ -+ list_for_each_safe(lh, save, hard_list) { -+ hl = list_entry(lh, struct yaffs_obj, hard_links); -+ in = yaffs_find_by_number(dev, -+ hl->variant.hardlink_variant.equiv_id); -+ -+ if (in) { -+ /* Add the hardlink pointers */ -+ hl->variant.hardlink_variant.equiv_obj = in; -+ list_add(&hl->hard_links, &in->hard_links); -+ } else { -+ /* Todo Need to report/handle this better. -+ * Got a problem... hardlink to a non-existant object -+ */ -+ hl->variant.hardlink_variant.equiv_obj = NULL; -+ INIT_LIST_HEAD(&hl->hard_links); -+ } -+ } -+} -+ -+static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) -+{ -+ /* -+ * Sort out state of unlinked and deleted objects after scanning. -+ */ -+ struct list_head *i; -+ struct list_head *n; -+ struct yaffs_obj *l; -+ -+ if (dev->read_only) -+ return; -+ -+ /* Soft delete all the unlinked files */ -+ list_for_each_safe(i, n, -+ &dev->unlinked_dir->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ yaffs_del_obj(l); -+ } -+ -+ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ yaffs_del_obj(l); -+ } -+} -+ -+/* -+ * This code iterates through all the objects making sure that they are rooted. -+ * Any unrooted objects are re-rooted in lost+found. -+ * An object needs to be in one of: -+ * - Directly under deleted, unlinked -+ * - Directly or indirectly under root. -+ * -+ * Note: -+ * This code assumes that we don't ever change the current relationships -+ * between directories: -+ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL -+ * lost-n-found->parent == root_dir -+ * -+ * This fixes the problem where directories might have inadvertently been -+ * deleted leaving the object "hanging" without being rooted in the -+ * directory tree. -+ */ -+ -+static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) -+{ -+ return (obj == dev->del_dir || -+ obj == dev->unlinked_dir || obj == dev->root_dir); -+} -+ -+static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_obj *parent; -+ int i; -+ struct list_head *lh; -+ struct list_head *n; -+ int depth_limit; -+ int hanging; -+ -+ if (dev->read_only) -+ return; -+ -+ /* Iterate through the objects in each hash entry, -+ * looking at each object. -+ * Make sure it is rooted. -+ */ -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ parent = obj->parent; -+ -+ if (yaffs_has_null_parent(dev, obj)) { -+ /* These directories are not hanging */ -+ hanging = 0; -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ hanging = 1; -+ } else if (yaffs_has_null_parent(dev, parent)) { -+ hanging = 0; -+ } else { -+ /* -+ * Need to follow the parent chain to -+ * see if it is hanging. -+ */ -+ hanging = 0; -+ depth_limit = 100; -+ -+ while (parent != dev->root_dir && -+ parent->parent && -+ parent->parent->variant_type == -+ YAFFS_OBJECT_TYPE_DIRECTORY && -+ depth_limit > 0) { -+ parent = parent->parent; -+ depth_limit--; -+ } -+ if (parent != dev->root_dir) -+ hanging = 1; -+ } -+ if (hanging) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Hanging object %d moved to lost and found", -+ obj->obj_id); -+ yaffs_add_obj_to_dir(dev->lost_n_found, obj); -+ } -+ } -+ } -+} -+ -+/* -+ * Delete directory contents for cleaning up lost and found. -+ */ -+static void yaffs_del_dir_contents(struct yaffs_obj *dir) -+{ -+ struct yaffs_obj *obj; -+ struct list_head *lh; -+ struct list_head *n; -+ -+ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) -+ BUG(); -+ -+ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) -+ yaffs_del_dir_contents(obj); -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Deleting lost_found object %d", -+ obj->obj_id); -+ yaffs_unlink_obj(obj); -+ } -+} -+ -+static void yaffs_empty_l_n_f(struct yaffs_dev *dev) -+{ -+ yaffs_del_dir_contents(dev->lost_n_found); -+} -+ -+ -+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, -+ const YCHAR *name) -+{ -+ int sum; -+ struct list_head *i; -+ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; -+ struct yaffs_obj *l; -+ -+ if (!name) -+ return NULL; -+ -+ if (!directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_find_by_name: null pointer directory" -+ ); -+ BUG(); -+ return NULL; -+ } -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_find_by_name: non-directory" -+ ); -+ BUG(); -+ } -+ -+ sum = yaffs_calc_name_sum(name); -+ -+ list_for_each(i, &directory->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ -+ if (l->parent != directory) -+ BUG(); -+ -+ yaffs_check_obj_details_loaded(l); -+ -+ /* Special case for lost-n-found */ -+ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { -+ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) -+ return l; -+ } else if (l->sum == sum || l->hdr_chunk <= 0) { -+ /* LostnFound chunk called Objxxx -+ * Do a real check -+ */ -+ yaffs_get_obj_name(l, buffer, -+ YAFFS_MAX_NAME_LENGTH + 1); -+ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) -+ return l; -+ } -+ } -+ return NULL; -+} -+ -+/* GetEquivalentObject dereferences any hard links to get to the -+ * actual object. -+ */ -+ -+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) -+{ -+ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { -+ obj = obj->variant.hardlink_variant.equiv_obj; -+ yaffs_check_obj_details_loaded(obj); -+ } -+ return obj; -+} -+ -+/* -+ * A note or two on object names. -+ * * If the object name is missing, we then make one up in the form objnnn -+ * -+ * * ASCII names are stored in the object header's name field from byte zero -+ * * Unicode names are historically stored starting from byte zero. -+ * -+ * Then there are automatic Unicode names... -+ * The purpose of these is to save names in a way that can be read as -+ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII -+ * system to share files. -+ * -+ * These automatic unicode are stored slightly differently... -+ * - If the name can fit in the ASCII character space then they are saved as -+ * ascii names as per above. -+ * - If the name needs Unicode then the name is saved in Unicode -+ * starting at oh->name[1]. -+ -+ */ -+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, -+ int buffer_size) -+{ -+ /* Create an object name if we could not find one. */ -+ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { -+ YCHAR local_name[20]; -+ YCHAR num_string[20]; -+ YCHAR *x = &num_string[19]; -+ unsigned v = obj->obj_id; -+ num_string[19] = 0; -+ while (v > 0) { -+ x--; -+ *x = '0' + (v % 10); -+ v /= 10; -+ } -+ /* make up a name */ -+ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); -+ strcat(local_name, x); -+ strncpy(name, local_name, buffer_size - 1); -+ } -+} -+ -+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) -+{ -+ memset(name, 0, buffer_size * sizeof(YCHAR)); -+ yaffs_check_obj_details_loaded(obj); -+ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { -+ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); -+ } else if (obj->short_name[0]) { -+ strcpy(name, obj->short_name); -+ } else if (obj->hdr_chunk > 0) { -+ int result; -+ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); -+ -+ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; -+ -+ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); -+ -+ if (obj->hdr_chunk > 0) { -+ result = yaffs_rd_chunk_tags_nand(obj->my_dev, -+ obj->hdr_chunk, -+ buffer, NULL); -+ } -+ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, -+ buffer_size); -+ -+ yaffs_release_temp_buffer(obj->my_dev, buffer); -+ } -+ -+ yaffs_fix_null_name(obj, name, buffer_size); -+ -+ return strnlen(name, YAFFS_MAX_NAME_LENGTH); -+} -+ -+loff_t yaffs_get_obj_length(struct yaffs_obj *obj) -+{ -+ /* Dereference any hard linking */ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ return obj->variant.file_variant.file_size; -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ if (!obj->variant.symlink_variant.alias) -+ return 0; -+ return strnlen(obj->variant.symlink_variant.alias, -+ YAFFS_MAX_ALIAS_LENGTH); -+ } else { -+ /* Only a directory should drop through to here */ -+ return obj->my_dev->data_bytes_per_chunk; -+ } -+} -+ -+int yaffs_get_obj_link_count(struct yaffs_obj *obj) -+{ -+ int count = 0; -+ struct list_head *i; -+ -+ if (!obj->unlinked) -+ count++; /* the object itself */ -+ -+ list_for_each(i, &obj->hard_links) -+ count++; /* add the hard links; */ -+ -+ return count; -+} -+ -+int yaffs_get_obj_inode(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ return obj->obj_id; -+} -+ -+unsigned yaffs_get_obj_type(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return DT_REG; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ return DT_DIR; -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ return DT_LNK; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ return DT_REG; -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ if (S_ISFIFO(obj->yst_mode)) -+ return DT_FIFO; -+ if (S_ISCHR(obj->yst_mode)) -+ return DT_CHR; -+ if (S_ISBLK(obj->yst_mode)) -+ return DT_BLK; -+ if (S_ISSOCK(obj->yst_mode)) -+ return DT_SOCK; -+ return DT_REG; -+ break; -+ default: -+ return DT_REG; -+ break; -+ } -+} -+ -+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) -+ return yaffs_clone_str(obj->variant.symlink_variant.alias); -+ else -+ return yaffs_clone_str(_Y("")); -+} -+ -+/*--------------------------- Initialisation code -------------------------- */ -+ -+static int yaffs_check_dev_fns(struct yaffs_dev *dev) -+{ -+ struct yaffs_driver *drv = &dev->drv; -+ struct yaffs_tags_handler *tagger = &dev->tagger; -+ -+ /* Common functions, gotta have */ -+ if (!drv->drv_read_chunk_fn || -+ !drv->drv_write_chunk_fn || -+ !drv->drv_erase_fn) -+ return 0; -+ -+ if (dev->param.is_yaffs2 && -+ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) -+ return 0; -+ -+ /* Install the default tags marshalling functions if needed. */ -+ yaffs_tags_compat_install(dev); -+ yaffs_tags_marshall_install(dev); -+ -+ /* Check we now have the marshalling functions required. */ -+ if (!tagger->write_chunk_tags_fn || -+ !tagger->read_chunk_tags_fn || -+ !tagger->query_block_fn || -+ !tagger->mark_bad_fn) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs_create_initial_dir(struct yaffs_dev *dev) -+{ -+ /* Initialise the unlinked, deleted, root and lost+found directories */ -+ dev->lost_n_found = dev->root_dir = NULL; -+ dev->unlinked_dir = dev->del_dir = NULL; -+ dev->unlinked_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); -+ dev->del_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); -+ dev->root_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, -+ YAFFS_ROOT_MODE | S_IFDIR); -+ dev->lost_n_found = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, -+ YAFFS_LOSTNFOUND_MODE | S_IFDIR); -+ -+ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir -+ && dev->del_dir) { -+ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); -+ return YAFFS_OK; -+ } -+ return YAFFS_FAIL; -+} -+ -+/* Low level init. -+ * Typically only used by yaffs_guts_initialise, but also used by the -+ * Low level yaffs driver tests. -+ */ -+ -+int yaffs_guts_ll_init(struct yaffs_dev *dev) -+{ -+ -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); -+ -+ if (!dev) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Need a device" -+ ); -+ return YAFFS_FAIL; -+ } -+ -+ if (dev->ll_init) -+ return YAFFS_OK; -+ -+ dev->internal_start_block = dev->param.start_block; -+ dev->internal_end_block = dev->param.end_block; -+ dev->block_offset = 0; -+ dev->chunk_offset = 0; -+ dev->n_free_chunks = 0; -+ -+ dev->gc_block = 0; -+ -+ if (dev->param.start_block == 0) { -+ dev->internal_start_block = dev->param.start_block + 1; -+ dev->internal_end_block = dev->param.end_block + 1; -+ dev->block_offset = 1; -+ dev->chunk_offset = dev->param.chunks_per_block; -+ } -+ -+ /* Check geometry parameters. */ -+ -+ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && -+ dev->param.total_bytes_per_chunk < 1024) || -+ (!dev->param.is_yaffs2 && -+ dev->param.total_bytes_per_chunk < 512) || -+ (dev->param.inband_tags && !dev->param.is_yaffs2) || -+ dev->param.chunks_per_block < 2 || -+ dev->param.n_reserved_blocks < 2 || -+ dev->internal_start_block <= 0 || -+ dev->internal_end_block <= 0 || -+ dev->internal_end_block <= -+ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) -+ ) { -+ /* otherwise it is too small */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", -+ dev->param.total_bytes_per_chunk, -+ dev->param.is_yaffs2 ? "2" : "", -+ dev->param.inband_tags); -+ return YAFFS_FAIL; -+ } -+ -+ /* Sort out space for inband tags, if required */ -+ if (dev->param.inband_tags) -+ dev->data_bytes_per_chunk = -+ dev->param.total_bytes_per_chunk - -+ sizeof(struct yaffs_packed_tags2_tags_only); -+ else -+ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; -+ -+ /* Got the right mix of functions? */ -+ if (!yaffs_check_dev_fns(dev)) { -+ /* Function missing */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "device function(s) missing or wrong"); -+ -+ return YAFFS_FAIL; -+ } -+ -+ if (yaffs_init_nand(dev) != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); -+ return YAFFS_FAIL; -+ } -+ -+ return YAFFS_OK; -+} -+ -+ -+int yaffs_guts_format_dev(struct yaffs_dev *dev) -+{ -+ int i; -+ enum yaffs_block_state state; -+ u32 dummy; -+ -+ if(yaffs_guts_ll_init(dev) != YAFFS_OK) -+ return YAFFS_FAIL; -+ -+ if(dev->is_mounted) -+ return YAFFS_FAIL; -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ yaffs_query_init_block_state(dev, i, &state, &dummy); -+ if (state != YAFFS_BLOCK_STATE_DEAD) -+ yaffs_erase_block(dev, i); -+ } -+ -+ return YAFFS_OK; -+} -+ -+ -+int yaffs_guts_initialise(struct yaffs_dev *dev) -+{ -+ int init_failed = 0; -+ unsigned x; -+ int bits; -+ -+ if(yaffs_guts_ll_init(dev) != YAFFS_OK) -+ return YAFFS_FAIL; -+ -+ if (dev->is_mounted) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); -+ return YAFFS_FAIL; -+ } -+ -+ dev->is_mounted = 1; -+ -+ /* OK now calculate a few things for the device */ -+ -+ /* -+ * Calculate all the chunk size manipulation numbers: -+ */ -+ x = dev->data_bytes_per_chunk; -+ /* We always use dev->chunk_shift and dev->chunk_div */ -+ dev->chunk_shift = calc_shifts(x); -+ x >>= dev->chunk_shift; -+ dev->chunk_div = x; -+ /* We only use chunk mask if chunk_div is 1 */ -+ dev->chunk_mask = (1 << dev->chunk_shift) - 1; -+ -+ /* -+ * Calculate chunk_grp_bits. -+ * We need to find the next power of 2 > than internal_end_block -+ */ -+ -+ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); -+ -+ bits = calc_shifts_ceiling(x); -+ -+ /* Set up tnode width if wide tnodes are enabled. */ -+ if (!dev->param.wide_tnodes_disabled) { -+ /* bits must be even so that we end up with 32-bit words */ -+ if (bits & 1) -+ bits++; -+ if (bits < 16) -+ dev->tnode_width = 16; -+ else -+ dev->tnode_width = bits; -+ } else { -+ dev->tnode_width = 16; -+ } -+ -+ dev->tnode_mask = (1 << dev->tnode_width) - 1; -+ -+ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), -+ * so if the bitwidth of the -+ * chunk range we're using is greater than 16 we need -+ * to figure out chunk shift and chunk_grp_size -+ */ -+ -+ if (bits <= dev->tnode_width) -+ dev->chunk_grp_bits = 0; -+ else -+ dev->chunk_grp_bits = bits - dev->tnode_width; -+ -+ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; -+ if (dev->tnode_size < sizeof(struct yaffs_tnode)) -+ dev->tnode_size = sizeof(struct yaffs_tnode); -+ -+ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; -+ -+ if (dev->param.chunks_per_block < dev->chunk_grp_size) { -+ /* We have a problem because the soft delete won't work if -+ * the chunk group size > chunks per block. -+ * This can be remedied by using larger "virtual blocks". -+ */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); -+ -+ return YAFFS_FAIL; -+ } -+ -+ /* Finished verifying the device, continue with initialisation */ -+ -+ /* More device initialisation */ -+ dev->all_gcs = 0; -+ dev->passive_gc_count = 0; -+ dev->oldest_dirty_gc_count = 0; -+ dev->bg_gcs = 0; -+ dev->gc_block_finder = 0; -+ dev->buffered_block = -1; -+ dev->doing_buffered_block_rewrite = 0; -+ dev->n_deleted_files = 0; -+ dev->n_bg_deletions = 0; -+ dev->n_unlinked_files = 0; -+ dev->n_ecc_fixed = 0; -+ dev->n_ecc_unfixed = 0; -+ dev->n_tags_ecc_fixed = 0; -+ dev->n_tags_ecc_unfixed = 0; -+ dev->n_erase_failures = 0; -+ dev->n_erased_blocks = 0; -+ dev->gc_disable = 0; -+ dev->has_pending_prioritised_gc = 1; -+ /* Assume the worst for now, will get fixed on first GC */ -+ INIT_LIST_HEAD(&dev->dirty_dirs); -+ dev->oldest_dirty_seq = 0; -+ dev->oldest_dirty_block = 0; -+ -+ /* Initialise temporary buffers and caches. */ -+ if (!yaffs_init_tmp_buffers(dev)) -+ init_failed = 1; -+ -+ dev->cache = NULL; -+ dev->gc_cleanup_list = NULL; -+ -+ if (!init_failed && dev->param.n_caches > 0) { -+ int i; -+ void *buf; -+ int cache_bytes = -+ dev->param.n_caches * sizeof(struct yaffs_cache); -+ -+ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) -+ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; -+ -+ dev->cache = kmalloc(cache_bytes, GFP_NOFS); -+ -+ buf = (u8 *) dev->cache; -+ -+ if (dev->cache) -+ memset(dev->cache, 0, cache_bytes); -+ -+ for (i = 0; i < dev->param.n_caches && buf; i++) { -+ dev->cache[i].object = NULL; -+ dev->cache[i].last_use = 0; -+ dev->cache[i].dirty = 0; -+ dev->cache[i].data = buf = -+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ } -+ if (!buf) -+ init_failed = 1; -+ -+ dev->cache_last_use = 0; -+ } -+ -+ dev->cache_hits = 0; -+ -+ if (!init_failed) { -+ dev->gc_cleanup_list = -+ kmalloc(dev->param.chunks_per_block * sizeof(u32), -+ GFP_NOFS); -+ if (!dev->gc_cleanup_list) -+ init_failed = 1; -+ } -+ -+ if (dev->param.is_yaffs2) -+ dev->param.use_header_file_size = 1; -+ -+ if (!init_failed && !yaffs_init_blocks(dev)) -+ init_failed = 1; -+ -+ yaffs_init_tnodes_and_objs(dev); -+ -+ if (!init_failed && !yaffs_create_initial_dir(dev)) -+ init_failed = 1; -+ -+ if (!init_failed && dev->param.is_yaffs2 && -+ !dev->param.disable_summary && -+ !yaffs_summary_init(dev)) -+ init_failed = 1; -+ -+ if (!init_failed) { -+ /* Now scan the flash. */ -+ if (dev->param.is_yaffs2) { -+ if (yaffs2_checkpt_restore(dev)) { -+ yaffs_check_obj_details_loaded(dev->root_dir); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | -+ YAFFS_TRACE_MOUNT, -+ "yaffs: restored from checkpoint" -+ ); -+ } else { -+ -+ /* Clean up the mess caused by an aborted -+ * checkpoint load then scan backwards. -+ */ -+ yaffs_deinit_blocks(dev); -+ -+ yaffs_deinit_tnodes_and_objs(dev); -+ -+ dev->n_erased_blocks = 0; -+ dev->n_free_chunks = 0; -+ dev->alloc_block = -1; -+ dev->alloc_page = -1; -+ dev->n_deleted_files = 0; -+ dev->n_unlinked_files = 0; -+ dev->n_bg_deletions = 0; -+ -+ if (!init_failed && !yaffs_init_blocks(dev)) -+ init_failed = 1; -+ -+ yaffs_init_tnodes_and_objs(dev); -+ -+ if (!init_failed -+ && !yaffs_create_initial_dir(dev)) -+ init_failed = 1; -+ -+ if (!init_failed && !yaffs2_scan_backwards(dev)) -+ init_failed = 1; -+ } -+ } else if (!yaffs1_scan(dev)) { -+ init_failed = 1; -+ } -+ -+ yaffs_strip_deleted_objs(dev); -+ yaffs_fix_hanging_objs(dev); -+ if (dev->param.empty_lost_n_found) -+ yaffs_empty_l_n_f(dev); -+ } -+ -+ if (init_failed) { -+ /* Clean up the mess */ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: yaffs_guts_initialise() aborted."); -+ -+ yaffs_deinitialise(dev); -+ return YAFFS_FAIL; -+ } -+ -+ /* Zero out stats */ -+ dev->n_page_reads = 0; -+ dev->n_page_writes = 0; -+ dev->n_erasures = 0; -+ dev->n_gc_copies = 0; -+ dev->n_retried_writes = 0; -+ -+ dev->n_retired_blocks = 0; -+ -+ yaffs_verify_free_chunks(dev); -+ yaffs_verify_blocks(dev); -+ -+ /* Clean up any aborted checkpoint data */ -+ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) -+ yaffs2_checkpt_invalidate(dev); -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: yaffs_guts_initialise() done."); -+ return YAFFS_OK; -+} -+ -+void yaffs_deinitialise(struct yaffs_dev *dev) -+{ -+ if (dev->is_mounted) { -+ int i; -+ -+ yaffs_deinit_blocks(dev); -+ yaffs_deinit_tnodes_and_objs(dev); -+ yaffs_summary_deinit(dev); -+ -+ if (dev->param.n_caches > 0 && dev->cache) { -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ kfree(dev->cache[i].data); -+ dev->cache[i].data = NULL; -+ } -+ -+ kfree(dev->cache); -+ dev->cache = NULL; -+ } -+ -+ kfree(dev->gc_cleanup_list); -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ kfree(dev->temp_buffer[i].buffer); -+ dev->temp_buffer[i].buffer = NULL; -+ } -+ -+ kfree(dev->checkpt_buffer); -+ dev->checkpt_buffer = NULL; -+ kfree(dev->checkpt_block_list); -+ dev->checkpt_block_list = NULL; -+ -+ dev->is_mounted = 0; -+ -+ yaffs_deinit_nand(dev); -+ } -+} -+ -+int yaffs_count_free_chunks(struct yaffs_dev *dev) -+{ -+ int n_free = 0; -+ int b; -+ struct yaffs_block_info *blk; -+ -+ blk = dev->block_info; -+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { -+ switch (blk->block_state) { -+ case YAFFS_BLOCK_STATE_EMPTY: -+ case YAFFS_BLOCK_STATE_ALLOCATING: -+ case YAFFS_BLOCK_STATE_COLLECTING: -+ case YAFFS_BLOCK_STATE_FULL: -+ n_free += -+ (dev->param.chunks_per_block - blk->pages_in_use + -+ blk->soft_del_pages); -+ break; -+ default: -+ break; -+ } -+ blk++; -+ } -+ return n_free; -+} -+ -+int yaffs_get_n_free_chunks(struct yaffs_dev *dev) -+{ -+ /* This is what we report to the outside world */ -+ int n_free; -+ int n_dirty_caches; -+ int blocks_for_checkpt; -+ int i; -+ -+ n_free = dev->n_free_chunks; -+ n_free += dev->n_deleted_files; -+ -+ /* Now count and subtract the number of dirty chunks in the cache. */ -+ -+ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].dirty) -+ n_dirty_caches++; -+ } -+ -+ n_free -= n_dirty_caches; -+ -+ n_free -= -+ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); -+ -+ /* Now figure checkpoint space and report that... */ -+ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); -+ -+ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); -+ -+ if (n_free < 0) -+ n_free = 0; -+ -+ return n_free; -+} -+ -+ -+ -+/* -+ * Marshalling functions to get loff_t file sizes into and out of -+ * object headers. -+ */ -+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) -+{ -+ oh->file_size_low = (fsize & 0xFFFFFFFF); -+ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); -+} -+ -+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) -+{ -+ loff_t retval; -+ -+ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) -+ retval = (((loff_t) oh->file_size_high) << 32) | -+ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); -+ else -+ retval = (loff_t) oh->file_size_low; -+ -+ return retval; -+} -+ -+ -+void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) -+{ -+ int i; -+ struct yaffs_block_info *bi; -+ int s; -+ -+ for(i = 0; i < 10; i++) -+ bs[i] = 0; -+ -+ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ bi = yaffs_get_block_info(dev, i); -+ s = bi->block_state; -+ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) -+ bs[0]++; -+ else -+ bs[s]++; -+ } -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_guts.h linux-3.14.43/fs/yaffs2/yaffs_guts.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_guts.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,1010 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_GUTS_H__ -+#define __YAFFS_GUTS_H__ -+ -+#include "yportenv.h" -+ -+#define YAFFS_OK 1 -+#define YAFFS_FAIL 0 -+ -+/* Give us a Y=0x59, -+ * Give us an A=0x41, -+ * Give us an FF=0xff -+ * Give us an S=0x53 -+ * And what have we got... -+ */ -+#define YAFFS_MAGIC 0x5941ff53 -+ -+/* -+ * Tnodes form a tree with the tnodes in "levels" -+ * Levels greater than 0 hold 8 slots which point to other tnodes. -+ * Those at level 0 hold 16 slots which point to chunks in NAND. -+ * -+ * A maximum level of 8 thust supports files of size up to: -+ * -+ * 2^(3*MAX_LEVEL+4) -+ * -+ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives -+ * a maximum file size of around 512Gbytees with 2k chunks. -+ */ -+#define YAFFS_NTNODES_LEVEL0 16 -+#define YAFFS_TNODES_LEVEL0_BITS 4 -+#define YAFFS_TNODES_LEVEL0_MASK 0xf -+ -+#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) -+#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) -+#define YAFFS_TNODES_INTERNAL_MASK 0x7 -+#define YAFFS_TNODES_MAX_LEVEL 8 -+#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ -+ YAFFS_TNODES_INTERNAL_BITS * \ -+ YAFFS_TNODES_MAX_LEVEL) -+#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) -+ -+#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff -+ -+/* Constants for YAFFS1 mode */ -+#define YAFFS_BYTES_PER_SPARE 16 -+#define YAFFS_BYTES_PER_CHUNK 512 -+#define YAFFS_CHUNK_SIZE_SHIFT 9 -+#define YAFFS_CHUNKS_PER_BLOCK 32 -+#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) -+ -+#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 -+#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 -+ -+ -+ -+#define YAFFS_ALLOCATION_NOBJECTS 100 -+#define YAFFS_ALLOCATION_NTNODES 100 -+#define YAFFS_ALLOCATION_NLINKS 100 -+ -+#define YAFFS_NOBJECT_BUCKETS 256 -+ -+#define YAFFS_OBJECT_SPACE 0x40000 -+#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) -+ -+/* Binary data version stamps */ -+#define YAFFS_SUMMARY_VERSION 1 -+#define YAFFS_CHECKPOINT_VERSION 7 -+ -+#ifdef CONFIG_YAFFS_UNICODE -+#define YAFFS_MAX_NAME_LENGTH 127 -+#define YAFFS_MAX_ALIAS_LENGTH 79 -+#else -+#define YAFFS_MAX_NAME_LENGTH 255 -+#define YAFFS_MAX_ALIAS_LENGTH 159 -+#endif -+ -+#define YAFFS_SHORT_NAME_LENGTH 15 -+ -+/* Some special object ids for pseudo objects */ -+#define YAFFS_OBJECTID_ROOT 1 -+#define YAFFS_OBJECTID_LOSTNFOUND 2 -+#define YAFFS_OBJECTID_UNLINKED 3 -+#define YAFFS_OBJECTID_DELETED 4 -+ -+/* Fake object Id for summary data */ -+#define YAFFS_OBJECTID_SUMMARY 0x10 -+ -+/* Pseudo object ids for checkpointing */ -+#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 -+#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 -+ -+#define YAFFS_MAX_SHORT_OP_CACHES 20 -+ -+#define YAFFS_N_TEMP_BUFFERS 6 -+ -+/* We limit the number attempts at sucessfully saving a chunk of data. -+ * Small-page devices have 32 pages per block; large-page devices have 64. -+ * Default to something in the order of 5 to 10 blocks worth of chunks. -+ */ -+#define YAFFS_WR_ATTEMPTS (5*64) -+ -+/* Sequence numbers are used in YAFFS2 to determine block allocation order. -+ * The range is limited slightly to help distinguish bad numbers from good. -+ * This also allows us to perhaps in the future use special numbers for -+ * special purposes. -+ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, -+ * and is a larger number than the lifetime of a 2GB device. -+ */ -+#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 -+#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 -+ -+/* Special sequence number for bad block that failed to be marked bad */ -+#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 -+ -+/* ChunkCache is used for short read/write operations.*/ -+struct yaffs_cache { -+ struct yaffs_obj *object; -+ int chunk_id; -+ int last_use; -+ int dirty; -+ int n_bytes; /* Only valid if the cache is dirty */ -+ int locked; /* Can't push out or flush while locked. */ -+ u8 *data; -+}; -+ -+/* yaffs1 tags structures in RAM -+ * NB This uses bitfield. Bitfields should not straddle a u32 boundary -+ * otherwise the structure size will get blown out. -+ */ -+ -+struct yaffs_tags { -+ u32 chunk_id:20; -+ u32 serial_number:2; -+ u32 n_bytes_lsb:10; -+ u32 obj_id:18; -+ u32 ecc:12; -+ u32 n_bytes_msb:2; -+}; -+ -+union yaffs_tags_union { -+ struct yaffs_tags as_tags; -+ u8 as_bytes[8]; -+}; -+ -+ -+/* Stuff used for extended tags in YAFFS2 */ -+ -+enum yaffs_ecc_result { -+ YAFFS_ECC_RESULT_UNKNOWN, -+ YAFFS_ECC_RESULT_NO_ERROR, -+ YAFFS_ECC_RESULT_FIXED, -+ YAFFS_ECC_RESULT_UNFIXED -+}; -+ -+enum yaffs_obj_type { -+ YAFFS_OBJECT_TYPE_UNKNOWN, -+ YAFFS_OBJECT_TYPE_FILE, -+ YAFFS_OBJECT_TYPE_SYMLINK, -+ YAFFS_OBJECT_TYPE_DIRECTORY, -+ YAFFS_OBJECT_TYPE_HARDLINK, -+ YAFFS_OBJECT_TYPE_SPECIAL -+}; -+ -+#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL -+ -+struct yaffs_ext_tags { -+ unsigned chunk_used; /* Status of the chunk: used or unused */ -+ unsigned obj_id; /* If 0 this is not used */ -+ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ -+ unsigned n_bytes; /* Only valid for data chunks */ -+ -+ /* The following stuff only has meaning when we read */ -+ enum yaffs_ecc_result ecc_result; -+ unsigned block_bad; -+ -+ /* YAFFS 1 stuff */ -+ unsigned is_deleted; /* The chunk is marked deleted */ -+ unsigned serial_number; /* Yaffs1 2-bit serial number */ -+ -+ /* YAFFS2 stuff */ -+ unsigned seq_number; /* The sequence number of this block */ -+ -+ /* Extra info if this is an object header (YAFFS2 only) */ -+ -+ unsigned extra_available; /* Extra info available if not zero */ -+ unsigned extra_parent_id; /* The parent object */ -+ unsigned extra_is_shrink; /* Is it a shrink header? */ -+ unsigned extra_shadows; /* Does this shadow another object? */ -+ -+ enum yaffs_obj_type extra_obj_type; /* What object type? */ -+ -+ loff_t extra_file_size; /* Length if it is a file */ -+ unsigned extra_equiv_id; /* Equivalent object for a hard link */ -+}; -+ -+/* Spare structure for YAFFS1 */ -+struct yaffs_spare { -+ u8 tb0; -+ u8 tb1; -+ u8 tb2; -+ u8 tb3; -+ u8 page_status; /* set to 0 to delete the chunk */ -+ u8 block_status; -+ u8 tb4; -+ u8 tb5; -+ u8 ecc1[3]; -+ u8 tb6; -+ u8 tb7; -+ u8 ecc2[3]; -+}; -+ -+/*Special structure for passing through to mtd */ -+struct yaffs_nand_spare { -+ struct yaffs_spare spare; -+ int eccres1; -+ int eccres2; -+}; -+ -+/* Block data in RAM */ -+ -+enum yaffs_block_state { -+ YAFFS_BLOCK_STATE_UNKNOWN = 0, -+ -+ YAFFS_BLOCK_STATE_SCANNING, -+ /* Being scanned */ -+ -+ YAFFS_BLOCK_STATE_NEEDS_SCAN, -+ /* The block might have something on it (ie it is allocating or full, -+ * perhaps empty) but it needs to be scanned to determine its true -+ * state. -+ * This state is only valid during scanning. -+ * NB We tolerate empty because the pre-scanner might be incapable of -+ * deciding -+ * However, if this state is returned on a YAFFS2 device, -+ * then we expect a sequence number -+ */ -+ -+ YAFFS_BLOCK_STATE_EMPTY, -+ /* This block is empty */ -+ -+ YAFFS_BLOCK_STATE_ALLOCATING, -+ /* This block is partially allocated. -+ * At least one page holds valid data. -+ * This is the one currently being used for page -+ * allocation. Should never be more than one of these. -+ * If a block is only partially allocated at mount it is treated as -+ * full. -+ */ -+ -+ YAFFS_BLOCK_STATE_FULL, -+ /* All the pages in this block have been allocated. -+ * If a block was only partially allocated when mounted we treat -+ * it as fully allocated. -+ */ -+ -+ YAFFS_BLOCK_STATE_DIRTY, -+ /* The block was full and now all chunks have been deleted. -+ * Erase me, reuse me. -+ */ -+ -+ YAFFS_BLOCK_STATE_CHECKPOINT, -+ /* This block is assigned to holding checkpoint data. */ -+ -+ YAFFS_BLOCK_STATE_COLLECTING, -+ /* This block is being garbage collected */ -+ -+ YAFFS_BLOCK_STATE_DEAD -+ /* This block has failed and is not in use */ -+}; -+ -+#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) -+ -+struct yaffs_block_info { -+ -+ s32 soft_del_pages:10; /* number of soft deleted pages */ -+ s32 pages_in_use:10; /* number of pages in use */ -+ u32 block_state:4; /* One of the above block states. */ -+ /* NB use unsigned because enum is sometimes -+ * an int */ -+ u32 needs_retiring:1; /* Data has failed on this block, */ -+ /*need to get valid data off and retire*/ -+ u32 skip_erased_check:1;/* Skip the erased check on this block */ -+ u32 gc_prioritise:1; /* An ECC check or blank check has failed. -+ Block should be prioritised for GC */ -+ u32 chunk_error_strikes:3; /* How many times we've had ecc etc -+ failures on this block and tried to reuse it */ -+ u32 has_summary:1; /* The block has a summary */ -+ -+ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ -+ u32 seq_number; /* block sequence number for yaffs2 */ -+ -+}; -+ -+/* -------------------------- Object structure -------------------------------*/ -+/* This is the object structure as stored on NAND */ -+ -+struct yaffs_obj_hdr { -+ enum yaffs_obj_type type; -+ -+ /* Apply to everything */ -+ int parent_obj_id; -+ u16 sum_no_longer_used; /* checksum of name. No longer used */ -+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ /* The following apply to all object types except for hard links */ -+ u32 yst_mode; /* protection */ -+ -+ u32 yst_uid; -+ u32 yst_gid; -+ u32 yst_atime; -+ u32 yst_mtime; -+ u32 yst_ctime; -+ -+ /* File size applies to files only */ -+ u32 file_size_low; -+ -+ /* Equivalent object id applies to hard links only. */ -+ int equiv_id; -+ -+ /* Alias is for symlinks only. */ -+ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; -+ -+ u32 yst_rdev; /* stuff for block and char devices (major/min) */ -+ -+ u32 win_ctime[2]; -+ u32 win_atime[2]; -+ u32 win_mtime[2]; -+ -+ u32 inband_shadowed_obj_id; -+ u32 inband_is_shrink; -+ -+ u32 file_size_high; -+ u32 reserved[1]; -+ int shadows_obj; /* This object header shadows the -+ specified object if > 0 */ -+ -+ /* is_shrink applies to object headers written when wemake a hole. */ -+ u32 is_shrink; -+ -+}; -+ -+/*--------------------------- Tnode -------------------------- */ -+ -+struct yaffs_tnode { -+ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; -+}; -+ -+/*------------------------ Object -----------------------------*/ -+/* An object can be one of: -+ * - a directory (no data, has children links -+ * - a regular file (data.... not prunes :->). -+ * - a symlink [symbolic link] (the alias). -+ * - a hard link -+ */ -+ -+struct yaffs_file_var { -+ loff_t file_size; -+ loff_t scanned_size; -+ loff_t shrink_size; -+ int top_level; -+ struct yaffs_tnode *top; -+}; -+ -+struct yaffs_dir_var { -+ struct list_head children; /* list of child links */ -+ struct list_head dirty; /* Entry for list of dirty directories */ -+}; -+ -+struct yaffs_symlink_var { -+ YCHAR *alias; -+}; -+ -+struct yaffs_hardlink_var { -+ struct yaffs_obj *equiv_obj; -+ u32 equiv_id; -+}; -+ -+union yaffs_obj_var { -+ struct yaffs_file_var file_variant; -+ struct yaffs_dir_var dir_variant; -+ struct yaffs_symlink_var symlink_variant; -+ struct yaffs_hardlink_var hardlink_variant; -+}; -+ -+struct yaffs_obj { -+ u8 deleted:1; /* This should only apply to unlinked files. */ -+ u8 soft_del:1; /* it has also been soft deleted */ -+ u8 unlinked:1; /* An unlinked file.*/ -+ u8 fake:1; /* A fake object has no presence on NAND. */ -+ u8 rename_allowed:1; /* Some objects cannot be renamed. */ -+ u8 unlink_allowed:1; -+ u8 dirty:1; /* the object needs to be written to flash */ -+ u8 valid:1; /* When the file system is being loaded up, this -+ * object might be created before the data -+ * is available -+ * ie. file data chunks encountered before -+ * the header. -+ */ -+ u8 lazy_loaded:1; /* This object has been lazy loaded and -+ * is missing some detail */ -+ -+ u8 defered_free:1; /* Object is removed from NAND, but is -+ * still in the inode cache. -+ * Free of object is defered. -+ * until the inode is released. -+ */ -+ u8 being_created:1; /* This object is still being created -+ * so skip some verification checks. */ -+ u8 is_shadowed:1; /* This object is shadowed on the way -+ * to being renamed. */ -+ -+ u8 xattr_known:1; /* We know if this has object has xattribs -+ * or not. */ -+ u8 has_xattr:1; /* This object has xattribs. -+ * Only valid if xattr_known. */ -+ -+ u8 serial; /* serial number of chunk in NAND.*/ -+ u16 sum; /* sum of the name to speed searching */ -+ -+ struct yaffs_dev *my_dev; /* The device I'm on */ -+ -+ struct list_head hash_link; /* list of objects in hash bucket */ -+ -+ struct list_head hard_links; /* hard linked object chain*/ -+ -+ /* directory structure stuff */ -+ /* also used for linking up the free list */ -+ struct yaffs_obj *parent; -+ struct list_head siblings; -+ -+ /* Where's my object header in NAND? */ -+ int hdr_chunk; -+ -+ int n_data_chunks; /* Number of data chunks for this file. */ -+ -+ u32 obj_id; /* the object id value */ -+ -+ u32 yst_mode; -+ -+ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; -+ -+#ifdef CONFIG_YAFFS_WINCE -+ u32 win_ctime[2]; -+ u32 win_mtime[2]; -+ u32 win_atime[2]; -+#else -+ u32 yst_uid; -+ u32 yst_gid; -+ u32 yst_atime; -+ u32 yst_mtime; -+ u32 yst_ctime; -+#endif -+ -+ u32 yst_rdev; -+ -+ void *my_inode; -+ -+ enum yaffs_obj_type variant_type; -+ -+ union yaffs_obj_var variant; -+ -+}; -+ -+struct yaffs_obj_bucket { -+ struct list_head list; -+ int count; -+}; -+ -+/* yaffs_checkpt_obj holds the definition of an object as dumped -+ * by checkpointing. -+ */ -+ -+struct yaffs_checkpt_obj { -+ int struct_type; -+ u32 obj_id; -+ u32 parent_id; -+ int hdr_chunk; -+ enum yaffs_obj_type variant_type:3; -+ u8 deleted:1; -+ u8 soft_del:1; -+ u8 unlinked:1; -+ u8 fake:1; -+ u8 rename_allowed:1; -+ u8 unlink_allowed:1; -+ u8 serial; -+ int n_data_chunks; -+ loff_t size_or_equiv_obj; -+}; -+ -+/*--------------------- Temporary buffers ---------------- -+ * -+ * These are chunk-sized working buffers. Each device has a few. -+ */ -+ -+struct yaffs_buffer { -+ u8 *buffer; -+ int in_use; -+}; -+ -+/*----------------- Device ---------------------------------*/ -+ -+struct yaffs_param { -+ const YCHAR *name; -+ -+ /* -+ * Entry parameters set up way early. Yaffs sets up the rest. -+ * The structure should be zeroed out before use so that unused -+ * and default values are zero. -+ */ -+ -+ int inband_tags; /* Use unband tags */ -+ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to -+ be a power of 2 */ -+ int chunks_per_block; /* does not need to be a power of 2 */ -+ int spare_bytes_per_chunk; /* spare area size */ -+ int start_block; /* Start block we're allowed to use */ -+ int end_block; /* End block we're allowed to use */ -+ int n_reserved_blocks; /* Tuneable so that we can reduce -+ * reserved blocks on NOR and RAM. */ -+ -+ int n_caches; /* If <= 0, then short op caching is disabled, -+ * else the number of short op caches. -+ */ -+ int cache_bypass_aligned; /* If non-zero then bypass the cache for -+ * aligned writes. -+ */ -+ -+ int use_nand_ecc; /* Flag to decide whether or not to use -+ * NAND driver ECC on data (yaffs1) */ -+ int tags_9bytes; /* Use 9 byte tags */ -+ int no_tags_ecc; /* Flag to decide whether or not to do ECC -+ * on packed tags (yaffs2) */ -+ -+ int is_yaffs2; /* Use yaffs2 mode on this device */ -+ -+ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ -+ -+ int refresh_period; /* How often to check for a block refresh */ -+ -+ /* Checkpoint control. Can be set before or after initialisation */ -+ u8 skip_checkpt_rd; -+ u8 skip_checkpt_wr; -+ -+ int enable_xattr; /* Enable xattribs */ -+ -+ int max_objects; /* -+ * Set to limit the number of objects created. -+ * 0 = no limit. -+ */ -+ -+ /* The remove_obj_fn function must be supplied by OS flavours that -+ * need it. -+ * yaffs direct uses it to implement the faster readdir. -+ * Linux uses it to protect the directory during unlocking. -+ */ -+ void (*remove_obj_fn) (struct yaffs_obj *obj); -+ -+ /* Callback to mark the superblock dirty */ -+ void (*sb_dirty_fn) (struct yaffs_dev *dev); -+ -+ /* Callback to control garbage collection. */ -+ unsigned (*gc_control_fn) (struct yaffs_dev *dev); -+ -+ /* Debug control flags. Don't use unless you know what you're doing */ -+ int use_header_file_size; /* Flag to determine if we should use -+ * file sizes from the header */ -+ int disable_lazy_load; /* Disable lazy loading on this device */ -+ int wide_tnodes_disabled; /* Set to disable wide tnodes */ -+ int disable_soft_del; /* yaffs 1 only: Set to disable the use of -+ * softdeletion. */ -+ -+ int defered_dir_update; /* Set to defer directory updates */ -+ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ int auto_unicode; -+#endif -+ int always_check_erased; /* Force chunk erased check always on */ -+ -+ int disable_summary; -+ int disable_bad_block_marking; -+ -+}; -+ -+struct yaffs_driver { -+ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, int data_len, -+ const u8 *oob, int oob_len); -+ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, -+ u8 *data, int data_len, -+ u8 *oob, int oob_len, -+ enum yaffs_ecc_result *ecc_result); -+ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_initialise_fn) (struct yaffs_dev *dev); -+ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); -+}; -+ -+struct yaffs_tags_handler { -+ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ const struct yaffs_ext_tags *tags); -+ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, -+ int nand_chunk, u8 *data, -+ struct yaffs_ext_tags *tags); -+ -+ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number); -+ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); -+}; -+ -+struct yaffs_dev { -+ struct yaffs_param param; -+ struct yaffs_driver drv; -+ struct yaffs_tags_handler tagger; -+ -+ /* Context storage. Holds extra OS specific data for this device */ -+ -+ void *os_context; -+ void *driver_context; -+ -+ struct list_head dev_list; -+ -+ int ll_init; -+ /* Runtime parameters. Set up by YAFFS. */ -+ int data_bytes_per_chunk; -+ -+ /* Non-wide tnode stuff */ -+ u16 chunk_grp_bits; /* Number of bits that need to be resolved if -+ * the tnodes are not wide enough. -+ */ -+ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ -+ -+ /* Stuff to support wide tnodes */ -+ u32 tnode_width; -+ u32 tnode_mask; -+ u32 tnode_size; -+ -+ /* Stuff for figuring out file offset to chunk conversions */ -+ u32 chunk_shift; /* Shift value */ -+ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ -+ u32 chunk_mask; /* Mask to use for power-of-2 case */ -+ -+ int is_mounted; -+ int read_only; -+ int is_checkpointed; -+ -+ /* Stuff to support block offsetting to support start block zero */ -+ int internal_start_block; -+ int internal_end_block; -+ int block_offset; -+ int chunk_offset; -+ -+ /* Runtime checkpointing stuff */ -+ int checkpt_page_seq; /* running sequence number of checkpt pages */ -+ int checkpt_byte_count; -+ int checkpt_byte_offs; -+ u8 *checkpt_buffer; -+ int checkpt_open_write; -+ int blocks_in_checkpt; -+ int checkpt_cur_chunk; -+ int checkpt_cur_block; -+ int checkpt_next_block; -+ int *checkpt_block_list; -+ int checkpt_max_blocks; -+ u32 checkpt_sum; -+ u32 checkpt_xor; -+ -+ int checkpoint_blocks_required; /* Number of blocks needed to store -+ * current checkpoint set */ -+ -+ /* Block Info */ -+ struct yaffs_block_info *block_info; -+ u8 *chunk_bits; /* bitmap of chunks in use */ -+ u8 block_info_alt:1; /* allocated using alternative alloc */ -+ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ -+ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. -+ * Must be consistent with chunks_per_block. -+ */ -+ -+ int n_erased_blocks; -+ int alloc_block; /* Current block being allocated off */ -+ u32 alloc_page; -+ int alloc_block_finder; /* Used to search for next allocation block */ -+ -+ /* Object and Tnode memory management */ -+ void *allocator; -+ int n_obj; -+ int n_tnodes; -+ -+ int n_hardlinks; -+ -+ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; -+ u32 bucket_finder; -+ -+ int n_free_chunks; -+ -+ /* Garbage collection control */ -+ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ -+ u32 n_clean_ups; -+ -+ unsigned has_pending_prioritised_gc; /* We think this device might -+ have pending prioritised gcs */ -+ unsigned gc_disable; -+ unsigned gc_block_finder; -+ unsigned gc_dirtiest; -+ unsigned gc_pages_in_use; -+ unsigned gc_not_done; -+ unsigned gc_block; -+ unsigned gc_chunk; -+ unsigned gc_skip; -+ struct yaffs_summary_tags *gc_sum_tags; -+ -+ /* Special directories */ -+ struct yaffs_obj *root_dir; -+ struct yaffs_obj *lost_n_found; -+ -+ int buffered_block; /* Which block is buffered here? */ -+ int doing_buffered_block_rewrite; -+ -+ struct yaffs_cache *cache; -+ int cache_last_use; -+ -+ /* Stuff for background deletion and unlinked files. */ -+ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted -+ files live. */ -+ struct yaffs_obj *del_dir; /* Directory where deleted objects are -+ sent to disappear. */ -+ struct yaffs_obj *unlinked_deletion; /* Current file being -+ background deleted. */ -+ int n_deleted_files; /* Count of files awaiting deletion; */ -+ int n_unlinked_files; /* Count of unlinked files. */ -+ int n_bg_deletions; /* Count of background deletions. */ -+ -+ /* Temporary buffer management */ -+ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; -+ int max_temp; -+ int temp_in_use; -+ int unmanaged_buffer_allocs; -+ int unmanaged_buffer_deallocs; -+ -+ /* yaffs2 runtime stuff */ -+ unsigned seq_number; /* Sequence number of currently -+ allocating block */ -+ unsigned oldest_dirty_seq; -+ unsigned oldest_dirty_block; -+ -+ /* Block refreshing */ -+ int refresh_skip; /* A skip down counter. -+ * Refresh happens when this gets to zero. */ -+ -+ /* Dirty directory handling */ -+ struct list_head dirty_dirs; /* List of dirty directories */ -+ -+ /* Summary */ -+ int chunks_per_summary; -+ struct yaffs_summary_tags *sum_tags; -+ -+ /* Statistics */ -+ u32 n_page_writes; -+ u32 n_page_reads; -+ u32 n_erasures; -+ u32 n_bad_queries; -+ u32 n_bad_markings; -+ u32 n_erase_failures; -+ u32 n_gc_copies; -+ u32 all_gcs; -+ u32 passive_gc_count; -+ u32 oldest_dirty_gc_count; -+ u32 n_gc_blocks; -+ u32 bg_gcs; -+ u32 n_retried_writes; -+ u32 n_retired_blocks; -+ u32 n_ecc_fixed; -+ u32 n_ecc_unfixed; -+ u32 n_tags_ecc_fixed; -+ u32 n_tags_ecc_unfixed; -+ u32 n_deletions; -+ u32 n_unmarked_deletions; -+ u32 refresh_count; -+ u32 cache_hits; -+ u32 tags_used; -+ u32 summary_used; -+ -+}; -+ -+/* The CheckpointDevice structure holds the device information that changes -+ *at runtime and must be preserved over unmount/mount cycles. -+ */ -+struct yaffs_checkpt_dev { -+ int struct_type; -+ int n_erased_blocks; -+ int alloc_block; /* Current block being allocated off */ -+ u32 alloc_page; -+ int n_free_chunks; -+ -+ int n_deleted_files; /* Count of files awaiting deletion; */ -+ int n_unlinked_files; /* Count of unlinked files. */ -+ int n_bg_deletions; /* Count of background deletions. */ -+ -+ /* yaffs2 runtime stuff */ -+ unsigned seq_number; /* Sequence number of currently -+ * allocating block */ -+ -+}; -+ -+struct yaffs_checkpt_validity { -+ int struct_type; -+ u32 magic; -+ u32 version; -+ u32 head; -+}; -+ -+struct yaffs_shadow_fixer { -+ int obj_id; -+ int shadowed_id; -+ struct yaffs_shadow_fixer *next; -+}; -+ -+/* Structure for doing xattr modifications */ -+struct yaffs_xattr_mod { -+ int set; /* If 0 then this is a deletion */ -+ const YCHAR *name; -+ const void *data; -+ int size; -+ int flags; -+ int result; -+}; -+ -+/*----------------------- YAFFS Functions -----------------------*/ -+ -+int yaffs_guts_initialise(struct yaffs_dev *dev); -+void yaffs_deinitialise(struct yaffs_dev *dev); -+ -+int yaffs_get_n_free_chunks(struct yaffs_dev *dev); -+ -+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, -+ struct yaffs_obj *new_dir, const YCHAR * new_name); -+ -+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); -+int yaffs_del_obj(struct yaffs_obj *obj); -+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, -+ enum yaffs_obj_type type); -+ -+ -+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); -+loff_t yaffs_get_obj_length(struct yaffs_obj *obj); -+int yaffs_get_obj_inode(struct yaffs_obj *obj); -+unsigned yaffs_get_obj_type(struct yaffs_obj *obj); -+int yaffs_get_obj_link_count(struct yaffs_obj *obj); -+ -+/* File operations */ -+int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, -+ int n_bytes); -+int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, -+ int n_bytes, int write_trhrough); -+int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); -+ -+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid); -+ -+int yaffs_flush_file(struct yaffs_obj *in, -+ int update_time, -+ int data_sync, -+ int discard_cache); -+ -+/* Flushing and checkpointing */ -+void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard); -+ -+int yaffs_checkpoint_save(struct yaffs_dev *dev); -+int yaffs_checkpoint_restore(struct yaffs_dev *dev); -+ -+/* Directory operations */ -+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, -+ u32 mode, u32 uid, u32 gid); -+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, -+ const YCHAR *name); -+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); -+ -+/* Link operations */ -+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, -+ struct yaffs_obj *equiv_obj); -+ -+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); -+ -+/* Symlink operations */ -+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, const YCHAR *alias); -+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); -+ -+/* Special inodes (fifos, sockets and devices) */ -+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, u32 rdev); -+ -+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, -+ const void *value, int size, int flags); -+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, -+ int size); -+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); -+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); -+ -+/* Special directories */ -+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); -+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); -+ -+void yaffs_handle_defered_free(struct yaffs_obj *obj); -+ -+void yaffs_update_dirty_dirs(struct yaffs_dev *dev); -+ -+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); -+ -+/* Debug dump */ -+int yaffs_dump_obj(struct yaffs_obj *obj); -+ -+void yaffs_guts_test(struct yaffs_dev *dev); -+int yaffs_guts_ll_init(struct yaffs_dev *dev); -+ -+ -+/* A few useful functions to be used within the core files*/ -+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, -+ int lyn); -+int yaffs_check_ff(u8 *buffer, int n_bytes); -+void yaffs_handle_chunk_error(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi); -+ -+u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); -+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); -+ -+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, -+ int number, -+ enum yaffs_obj_type type); -+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ int nand_chunk, int in_scan); -+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); -+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, -+ const struct yaffs_obj_hdr *oh); -+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); -+YCHAR *yaffs_clone_str(const YCHAR *str); -+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); -+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); -+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, -+ int force, int is_shrink, int shadows, -+ struct yaffs_xattr_mod *xop); -+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, -+ int backward_scanning); -+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); -+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); -+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id, -+ struct yaffs_tnode *passed_tn); -+ -+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_trhrough); -+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); -+void yaffs_skip_rest_of_block(struct yaffs_dev *dev); -+ -+int yaffs_count_free_chunks(struct yaffs_dev *dev); -+ -+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id); -+ -+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos); -+ -+int yaffs_is_non_empty_dir(struct yaffs_obj *obj); -+ -+int yaffs_guts_format_dev(struct yaffs_dev *dev); -+ -+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, -+ int *chunk_out, u32 *offset_out); -+/* -+ * Marshalling functions to get loff_t file sizes into aand out of -+ * object headers. -+ */ -+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); -+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); -+loff_t yaffs_max_file_size(struct yaffs_dev *dev); -+ -+/* -+ * Debug function to count number of blocks in each state -+ * NB Needs to be called with correct number of integers -+ */ -+ -+void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); -+ -+int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_linux.h linux-3.14.43/fs/yaffs2/yaffs_linux.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_linux.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,48 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_LINUX_H__ -+#define __YAFFS_LINUX_H__ -+ -+#include "yportenv.h" -+ -+struct yaffs_linux_context { -+ struct list_head context_list; /* List of these we have mounted */ -+ struct yaffs_dev *dev; -+ struct super_block *super; -+ struct task_struct *bg_thread; /* Background thread for this device */ -+ int bg_running; -+ struct mutex gross_lock; /* Gross locking mutex*/ -+ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size -+ * at compile time so we have to allocate it. -+ */ -+ struct list_head search_contexts; -+ struct task_struct *readdir_process; -+ unsigned mount_id; -+ int dirty; -+}; -+ -+#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) -+#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+#define WRITE_SIZE_STR "writesize" -+#define WRITE_SIZE(mtd) ((mtd)->writesize) -+#else -+#define WRITE_SIZE_STR "oobblock" -+#define WRITE_SIZE(mtd) ((mtd)->oobblock) -+#endif -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_mtdif.c linux-3.14.43/fs/yaffs2/yaffs_mtdif.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_mtdif.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,310 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yportenv.h" -+ -+#include "yaffs_mtdif.h" -+ -+#include "linux/mtd/mtd.h" -+#include "linux/types.h" -+#include "linux/time.h" -+#include "linux/mtd/nand.h" -+#include "linux/kernel.h" -+#include "linux/version.h" -+#include "linux/types.h" -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) -+#include "uapi/linux/major.h" -+#endif -+ -+#include "yaffs_trace.h" -+#include "yaffs_guts.h" -+#include "yaffs_linux.h" -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) -+#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) -+#define mtd_erase(m, ei) (m)->erase(m, ei) -+#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) -+#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) -+#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) -+#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) -+#endif -+ -+ -+ -+int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ u32 addr = -+ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * -+ dev->param.chunks_per_block; -+ struct erase_info ei; -+ int retval = 0; -+ -+ ei.mtd = mtd; -+ ei.addr = addr; -+ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; -+ ei.time = 1000; -+ ei.retries = 2; -+ ei.callback = NULL; -+ ei.priv = (u_long) dev; -+ -+ retval = mtd_erase(mtd, &ei); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+ -+static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, int data_len, -+ const u8 *oob, int oob_len) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ loff_t addr; -+ struct mtd_oob_ops ops; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", -+ dev, nand_chunk, data, data_len, oob, oob_len); -+ -+ if (!data || !data_len) { -+ data = NULL; -+ data_len = 0; -+ } -+ -+ if (!oob || !oob_len) { -+ oob = NULL; -+ oob_len = 0; -+ } -+ -+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; -+ memset(&ops, 0, sizeof(ops)); -+ ops.mode = MTD_OPS_AUTO_OOB; -+ ops.len = (data) ? data_len : 0; -+ ops.ooblen = oob_len; -+ ops.datbuf = (u8 *)data; -+ ops.oobbuf = (u8 *)oob; -+ -+ retval = mtd_write_oob(mtd, addr, &ops); -+ if (retval) { -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "write_oob failed, chunk %d, mtd error %d", -+ nand_chunk, retval); -+ } -+ return retval ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, -+ u8 *data, int data_len, -+ u8 *oob, int oob_len, -+ enum yaffs_ecc_result *ecc_result) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ loff_t addr; -+ struct mtd_oob_ops ops; -+ int retval; -+ -+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; -+ memset(&ops, 0, sizeof(ops)); -+ ops.mode = MTD_OPS_AUTO_OOB; -+ ops.len = (data) ? data_len : 0; -+ ops.ooblen = oob_len; -+ ops.datbuf = data; -+ ops.oobbuf = oob; -+ -+#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) -+ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; -+ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. -+ */ -+ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; -+#endif -+ /* Read page and oob using MTD. -+ * Check status and determine ECC result. -+ */ -+ retval = mtd_read_oob(mtd, addr, &ops); -+ if (retval) -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "read_oob failed, chunk %d, mtd error %d", -+ nand_chunk, retval); -+ -+ switch (retval) { -+ case 0: -+ /* no error */ -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ break; -+ -+ case -EUCLEAN: -+ /* MTD's ECC fixed the data */ -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_FIXED; -+ dev->n_ecc_fixed++; -+ break; -+ -+ case -EBADMSG: -+ default: -+ /* MTD's ECC could not fix the data */ -+ dev->n_ecc_unfixed++; -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ return YAFFS_FAIL; -+ } -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ -+ loff_t addr; -+ struct erase_info ei; -+ int retval = 0; -+ u32 block_size; -+ -+ block_size = dev->param.total_bytes_per_chunk * -+ dev->param.chunks_per_block; -+ addr = ((loff_t) block_no) * block_size; -+ -+ ei.mtd = mtd; -+ ei.addr = addr; -+ ei.len = block_size; -+ ei.time = 1000; -+ ei.retries = 2; -+ ei.callback = NULL; -+ ei.priv = (u_long) dev; -+ -+ retval = mtd_erase(mtd, &ei); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); -+ -+ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); -+ return (retval) ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); -+ -+ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); -+ return (retval) ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_initialise(struct yaffs_dev *dev) -+{ -+ return YAFFS_OK; -+} -+ -+static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) -+{ -+ return YAFFS_OK; -+} -+ -+ -+void yaffs_mtd_drv_install(struct yaffs_dev *dev) -+{ -+ struct yaffs_driver *drv = &dev->drv; -+ -+ drv->drv_write_chunk_fn = yaffs_mtd_write; -+ drv->drv_read_chunk_fn = yaffs_mtd_read; -+ drv->drv_erase_fn = yaffs_mtd_erase; -+ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; -+ drv->drv_check_bad_fn = yaffs_mtd_check_bad; -+ drv->drv_initialise_fn = yaffs_mtd_initialise; -+ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; -+} -+ -+ -+struct mtd_info * yaffs_get_mtd_device(dev_t sdev) -+{ -+ struct mtd_info *mtd; -+ -+ mtd = yaffs_get_mtd_device(sdev); -+ -+ /* Check it's an mtd device..... */ -+ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) -+ return NULL; /* This isn't an mtd device */ -+ -+ /* Check it's NAND */ -+ if (mtd->type != MTD_NANDFLASH) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: MTD device is not NAND it's type %d", -+ mtd->type); -+ return NULL; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); -+ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); -+ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -+ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); -+#else -+ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); -+#endif -+ -+ return mtd; -+} -+ -+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) -+{ -+ if (yaffs_version == 2) { -+ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || -+ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && -+ !inband_tags) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device does not have the right page sizes" -+ ); -+ return -1; -+ } -+ } else { -+ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || -+ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device does not support have the right page sizes" -+ ); -+ return -1; -+ } -+ } -+ -+ return 0; -+} -+ -+ -+void yaffs_put_mtd_device(struct mtd_info *mtd) -+{ -+ if(mtd) -+ put_mtd_device(mtd); -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_mtdif.h linux-3.14.43/fs/yaffs2/yaffs_mtdif.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_mtdif.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,25 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_MTDIF_H__ -+#define __YAFFS_MTDIF_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_mtd_drv_install(struct yaffs_dev *dev); -+struct mtd_info * yaffs_get_mtd_device(dev_t sdev); -+void yaffs_put_mtd_device(struct mtd_info *mtd); -+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_nameval.c linux-3.14.43/fs/yaffs2/yaffs_nameval.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_nameval.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,208 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * This simple implementation of a name-value store assumes a small number of -+* values and fits into a small finite buffer. -+ * -+ * Each attribute is stored as a record: -+ * sizeof(int) bytes record size. -+ * strnlen+1 bytes name null terminated. -+ * nbytes value. -+ * ---------- -+ * total size stored in record size -+ * -+ * This code has not been tested with unicode yet. -+ */ -+ -+#include "yaffs_nameval.h" -+ -+#include "yportenv.h" -+ -+static int nval_find(const char *xb, int xb_size, const YCHAR *name, -+ int *exist_size) -+{ -+ int pos = 0; -+ int size; -+ -+ memcpy(&size, xb, sizeof(int)); -+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { -+ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), -+ name, size)) { -+ if (exist_size) -+ *exist_size = size; -+ return pos; -+ } -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ if (exist_size) -+ *exist_size = 0; -+ return -ENODATA; -+} -+ -+static int nval_used(const char *xb, int xb_size) -+{ -+ int pos = 0; -+ int size; -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ return pos; -+} -+ -+int nval_del(char *xb, int xb_size, const YCHAR *name) -+{ -+ int pos = nval_find(xb, xb_size, name, NULL); -+ int size; -+ -+ if (pos < 0 || pos >= xb_size) -+ return -ENODATA; -+ -+ /* Find size, shift rest over this record, -+ * then zero out the rest of buffer */ -+ memcpy(&size, xb + pos, sizeof(int)); -+ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); -+ memset(xb + (xb_size - size), 0, size); -+ return 0; -+} -+ -+int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, -+ int bsize, int flags) -+{ -+ int pos; -+ int namelen = strnlen(name, xb_size); -+ int reclen; -+ int size_exist = 0; -+ int space; -+ int start; -+ -+ pos = nval_find(xb, xb_size, name, &size_exist); -+ -+ if (flags & XATTR_CREATE && pos >= 0) -+ return -EEXIST; -+ if (flags & XATTR_REPLACE && pos < 0) -+ return -ENODATA; -+ -+ start = nval_used(xb, xb_size); -+ space = xb_size - start + size_exist; -+ -+ reclen = (sizeof(int) + namelen + 1 + bsize); -+ -+ if (reclen > space) -+ return -ENOSPC; -+ -+ if (pos >= 0) { -+ nval_del(xb, xb_size, name); -+ start = nval_used(xb, xb_size); -+ } -+ -+ pos = start; -+ -+ memcpy(xb + pos, &reclen, sizeof(int)); -+ pos += sizeof(int); -+ strncpy((YCHAR *) (xb + pos), name, reclen); -+ pos += (namelen + 1); -+ memcpy(xb + pos, buf, bsize); -+ return 0; -+} -+ -+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, -+ int bsize) -+{ -+ int pos = nval_find(xb, xb_size, name, NULL); -+ int size; -+ -+ if (pos >= 0 && pos < xb_size) { -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ pos += sizeof(int); /* advance past record length */ -+ size -= sizeof(int); -+ -+ /* Advance over name string */ -+ while (xb[pos] && size > 0 && pos < xb_size) { -+ pos++; -+ size--; -+ } -+ /*Advance over NUL */ -+ pos++; -+ size--; -+ -+ /* If bsize is zero then this is a size query. -+ * Return the size, but don't copy. -+ */ -+ if (!bsize) -+ return size; -+ -+ if (size <= bsize) { -+ memcpy(buf, xb + pos, size); -+ return size; -+ } -+ } -+ if (pos >= 0) -+ return -ERANGE; -+ -+ return -ENODATA; -+} -+ -+int nval_list(const char *xb, int xb_size, char *buf, int bsize) -+{ -+ int pos = 0; -+ int size; -+ int name_len; -+ int ncopied = 0; -+ int filled = 0; -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ while (size > sizeof(int) && -+ size <= xb_size && -+ (pos + size) < xb_size && -+ !filled) { -+ pos += sizeof(int); -+ size -= sizeof(int); -+ name_len = strnlen((YCHAR *) (xb + pos), size); -+ if (ncopied + name_len + 1 < bsize) { -+ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); -+ buf += name_len; -+ *buf = '\0'; -+ buf++; -+ if (sizeof(YCHAR) > 1) { -+ *buf = '\0'; -+ buf++; -+ } -+ ncopied += (name_len + 1); -+ } else { -+ filled = 1; -+ } -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ return ncopied; -+} -+ -+int nval_hasvalues(const char *xb, int xb_size) -+{ -+ return nval_used(xb, xb_size) > 0; -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_nameval.h linux-3.14.43/fs/yaffs2/yaffs_nameval.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_nameval.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,28 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __NAMEVAL_H__ -+#define __NAMEVAL_H__ -+ -+#include "yportenv.h" -+ -+int nval_del(char *xb, int xb_size, const YCHAR * name); -+int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, -+ int bsize, int flags); -+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, -+ int bsize); -+int nval_list(const char *xb, int xb_size, char *buf, int bsize); -+int nval_hasvalues(const char *xb, int xb_size); -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_nand.c linux-3.14.43/fs/yaffs2/yaffs_nand.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_nand.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,122 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_nand.h" -+#include "yaffs_tagscompat.h" -+ -+#include "yaffs_getblockinfo.h" -+#include "yaffs_summary.h" -+ -+static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) -+{ -+ return chunk - dev->chunk_offset; -+} -+ -+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, -+ u8 *buffer, struct yaffs_ext_tags *tags) -+{ -+ int result; -+ struct yaffs_ext_tags local_tags; -+ int flash_chunk = apply_chunk_offset(dev, nand_chunk); -+ -+ dev->n_page_reads++; -+ -+ /* If there are no tags provided use local tags. */ -+ if (!tags) -+ tags = &local_tags; -+ -+ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); -+ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { -+ -+ struct yaffs_block_info *bi; -+ bi = yaffs_get_block_info(dev, -+ nand_chunk / -+ dev->param.chunks_per_block); -+ yaffs_handle_chunk_error(dev, bi); -+ } -+ return result; -+} -+ -+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *buffer, struct yaffs_ext_tags *tags) -+{ -+ int result; -+ int flash_chunk = apply_chunk_offset(dev, nand_chunk); -+ -+ dev->n_page_writes++; -+ -+ if (!tags) { -+ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ tags->seq_number = dev->seq_number; -+ tags->chunk_used = 1; -+ yaffs_trace(YAFFS_TRACE_WRITE, -+ "Writing chunk %d tags %d %d", -+ nand_chunk, tags->obj_id, tags->chunk_id); -+ -+ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, -+ buffer, tags); -+ -+ yaffs_summary_add(dev, tags, nand_chunk); -+ -+ return result; -+} -+ -+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ block_no -= dev->block_offset; -+ dev->n_bad_markings++; -+ -+ if (dev->param.disable_bad_block_marking) -+ return YAFFS_OK; -+ -+ return dev->tagger.mark_bad_fn(dev, block_no); -+} -+ -+ -+int yaffs_query_init_block_state(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ block_no -= dev->block_offset; -+ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); -+} -+ -+int yaffs_erase_block(struct yaffs_dev *dev, int block_no) -+{ -+ int result; -+ -+ block_no -= dev->block_offset; -+ dev->n_erasures++; -+ result = dev->drv.drv_erase_fn(dev, block_no); -+ return result; -+} -+ -+int yaffs_init_nand(struct yaffs_dev *dev) -+{ -+ if (dev->drv.drv_initialise_fn) -+ return dev->drv.drv_initialise_fn(dev); -+ return YAFFS_OK; -+} -+ -+int yaffs_deinit_nand(struct yaffs_dev *dev) -+{ -+ if (dev->drv.drv_deinitialise_fn) -+ return dev->drv.drv_deinitialise_fn(dev); -+ return YAFFS_OK; -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_nand.h linux-3.14.43/fs/yaffs2/yaffs_nand.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_nand.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_NAND_H__ -+#define __YAFFS_NAND_H__ -+#include "yaffs_guts.h" -+ -+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, -+ u8 *buffer, struct yaffs_ext_tags *tags); -+ -+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *buffer, struct yaffs_ext_tags *tags); -+ -+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); -+ -+int yaffs_query_init_block_state(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ unsigned *seq_number); -+ -+int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); -+ -+int yaffs_init_nand(struct yaffs_dev *dev); -+int yaffs_deinit_nand(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.14.43/fs/yaffs2/yaffs_packedtags1.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_packedtags1.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,56 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_packedtags1.h" -+#include "yportenv.h" -+ -+static const u8 all_ff[20] = { -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff -+}; -+ -+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, -+ const struct yaffs_ext_tags *t) -+{ -+ pt->chunk_id = t->chunk_id; -+ pt->serial_number = t->serial_number; -+ pt->n_bytes = t->n_bytes; -+ pt->obj_id = t->obj_id; -+ pt->ecc = 0; -+ pt->deleted = (t->is_deleted) ? 0 : 1; -+ pt->unused_stuff = 0; -+ pt->should_be_ff = 0xffffffff; -+} -+ -+void yaffs_unpack_tags1(struct yaffs_ext_tags *t, -+ const struct yaffs_packed_tags1 *pt) -+{ -+ -+ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { -+ t->block_bad = 0; -+ if (pt->should_be_ff != 0xffffffff) -+ t->block_bad = 1; -+ t->chunk_used = 1; -+ t->obj_id = pt->obj_id; -+ t->chunk_id = pt->chunk_id; -+ t->n_bytes = pt->n_bytes; -+ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ t->is_deleted = (pt->deleted) ? 0 : 1; -+ t->serial_number = pt->serial_number; -+ } else { -+ memset(t, 0, sizeof(struct yaffs_ext_tags)); -+ } -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.14.43/fs/yaffs2/yaffs_packedtags1.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_packedtags1.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ -+ -+#ifndef __YAFFS_PACKEDTAGS1_H__ -+#define __YAFFS_PACKEDTAGS1_H__ -+ -+#include "yaffs_guts.h" -+ -+struct yaffs_packed_tags1 { -+ u32 chunk_id:20; -+ u32 serial_number:2; -+ u32 n_bytes:10; -+ u32 obj_id:18; -+ u32 ecc:12; -+ u32 deleted:1; -+ u32 unused_stuff:1; -+ unsigned should_be_ff; -+ -+}; -+ -+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, -+ const struct yaffs_ext_tags *t); -+void yaffs_unpack_tags1(struct yaffs_ext_tags *t, -+ const struct yaffs_packed_tags1 *pt); -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.14.43/fs/yaffs2/yaffs_packedtags2.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_packedtags2.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,197 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_packedtags2.h" -+#include "yportenv.h" -+#include "yaffs_trace.h" -+ -+/* This code packs a set of extended tags into a binary structure for -+ * NAND storage -+ */ -+ -+/* Some of the information is "extra" struff which can be packed in to -+ * speed scanning -+ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. -+ */ -+ -+/* Extra flags applied to chunk_id */ -+ -+#define EXTRA_HEADER_INFO_FLAG 0x80000000 -+#define EXTRA_SHRINK_FLAG 0x40000000 -+#define EXTRA_SHADOWS_FLAG 0x20000000 -+#define EXTRA_SPARE_FLAGS 0x10000000 -+ -+#define ALL_EXTRA_FLAGS 0xf0000000 -+ -+/* Also, the top 4 bits of the object Id are set to the object type. */ -+#define EXTRA_OBJECT_TYPE_SHIFT (28) -+#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) -+ -+static void yaffs_dump_packed_tags2_tags_only( -+ const struct yaffs_packed_tags2_tags_only *ptt) -+{ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "packed tags obj %d chunk %d byte %d seq %d", -+ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); -+} -+ -+static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) -+{ -+ yaffs_dump_packed_tags2_tags_only(&pt->t); -+} -+ -+static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) -+{ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", -+ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, -+ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, -+ t->seq_number); -+ -+} -+ -+static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) -+{ -+ if (t->chunk_id != 0 || !t->extra_available) -+ return 0; -+ -+ /* Check if the file size is too long to store */ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && -+ (t->extra_file_size >> 31) != 0) -+ return 0; -+ return 1; -+} -+ -+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, -+ const struct yaffs_ext_tags *t) -+{ -+ ptt->chunk_id = t->chunk_id; -+ ptt->seq_number = t->seq_number; -+ ptt->n_bytes = t->n_bytes; -+ ptt->obj_id = t->obj_id; -+ -+ /* Only store extra tags for object headers. -+ * If it is a file then only store if the file size is short\ -+ * enough to fit. -+ */ -+ if (yaffs_check_tags_extra_packable(t)) { -+ /* Store the extra header info instead */ -+ /* We save the parent object in the chunk_id */ -+ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; -+ if (t->extra_is_shrink) -+ ptt->chunk_id |= EXTRA_SHRINK_FLAG; -+ if (t->extra_shadows) -+ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; -+ -+ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; -+ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); -+ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ ptt->n_bytes = t->extra_equiv_id; -+ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) -+ ptt->n_bytes = (unsigned) t->extra_file_size; -+ else -+ ptt->n_bytes = 0; -+ } -+ -+ yaffs_dump_packed_tags2_tags_only(ptt); -+ yaffs_dump_tags2(t); -+} -+ -+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, -+ const struct yaffs_ext_tags *t, int tags_ecc) -+{ -+ yaffs_pack_tags2_tags_only(&pt->t, t); -+ -+ if (tags_ecc) -+ yaffs_ecc_calc_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &pt->ecc); -+} -+ -+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, -+ struct yaffs_packed_tags2_tags_only *ptt) -+{ -+ memset(t, 0, sizeof(struct yaffs_ext_tags)); -+ -+ if (ptt->seq_number == 0xffffffff) -+ return; -+ -+ t->block_bad = 0; -+ t->chunk_used = 1; -+ t->obj_id = ptt->obj_id; -+ t->chunk_id = ptt->chunk_id; -+ t->n_bytes = ptt->n_bytes; -+ t->is_deleted = 0; -+ t->serial_number = 0; -+ t->seq_number = ptt->seq_number; -+ -+ /* Do extra header info stuff */ -+ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { -+ t->chunk_id = 0; -+ t->n_bytes = 0; -+ -+ t->extra_available = 1; -+ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); -+ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; -+ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; -+ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; -+ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; -+ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ t->extra_equiv_id = ptt->n_bytes; -+ else -+ t->extra_file_size = ptt->n_bytes; -+ } -+ yaffs_dump_packed_tags2_tags_only(ptt); -+ yaffs_dump_tags2(t); -+} -+ -+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, -+ int tags_ecc) -+{ -+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ -+ if (pt->t.seq_number != 0xffffffff && tags_ecc) { -+ /* Chunk is in use and we need to do ECC */ -+ -+ struct yaffs_ecc_other ecc; -+ int result; -+ yaffs_ecc_calc_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &ecc); -+ result = -+ yaffs_ecc_correct_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &pt->ecc, &ecc); -+ switch (result) { -+ case 0: -+ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ break; -+ case 1: -+ ecc_result = YAFFS_ECC_RESULT_FIXED; -+ break; -+ case -1: -+ ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ break; -+ default: -+ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; -+ } -+ } -+ yaffs_unpack_tags2_tags_only(t, &pt->t); -+ -+ t->ecc_result = ecc_result; -+ -+ yaffs_dump_packed_tags2(pt); -+ yaffs_dump_tags2(t); -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.14.43/fs/yaffs2/yaffs_packedtags2.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_packedtags2.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,47 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ -+ -+#ifndef __YAFFS_PACKEDTAGS2_H__ -+#define __YAFFS_PACKEDTAGS2_H__ -+ -+#include "yaffs_guts.h" -+#include "yaffs_ecc.h" -+ -+struct yaffs_packed_tags2_tags_only { -+ unsigned seq_number; -+ unsigned obj_id; -+ unsigned chunk_id; -+ unsigned n_bytes; -+}; -+ -+struct yaffs_packed_tags2 { -+ struct yaffs_packed_tags2_tags_only t; -+ struct yaffs_ecc_other ecc; -+}; -+ -+/* Full packed tags with ECC, used for oob tags */ -+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, -+ const struct yaffs_ext_tags *t, int tags_ecc); -+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, -+ int tags_ecc); -+ -+/* Only the tags part (no ECC for use with inband tags */ -+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, -+ const struct yaffs_ext_tags *t); -+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, -+ struct yaffs_packed_tags2_tags_only *pt); -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_summary.c linux-3.14.43/fs/yaffs2/yaffs_summary.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_summary.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,312 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Summaries write the useful part of the tags for the chunks in a block into an -+ * an array which is written to the last n chunks of the block. -+ * Reading the summaries gives all the tags for the block in one read. Much -+ * faster. -+ * -+ * Chunks holding summaries are marked with tags making it look like -+ * they are part of a fake file. -+ * -+ * The summary could also be used during gc. -+ * -+ */ -+ -+#include "yaffs_summary.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_nand.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_bitmap.h" -+ -+/* -+ * The summary is built up in an array of summary tags. -+ * This gets written to the last one or two (maybe more) chunks in a block. -+ * A summary header is written as the first part of each chunk of summary data. -+ * The summary header must match or the summary is rejected. -+ */ -+ -+/* Summary tags don't need the sequence number because that is redundant. */ -+struct yaffs_summary_tags { -+ unsigned obj_id; -+ unsigned chunk_id; -+ unsigned n_bytes; -+}; -+ -+/* Summary header */ -+struct yaffs_summary_header { -+ unsigned version; /* Must match current version */ -+ unsigned block; /* Must be this block */ -+ unsigned seq; /* Must be this sequence number */ -+ unsigned sum; /* Just add up all the bytes in the tags */ -+}; -+ -+ -+static void yaffs_summary_clear(struct yaffs_dev *dev) -+{ -+ if (!dev->sum_tags) -+ return; -+ memset(dev->sum_tags, 0, dev->chunks_per_summary * -+ sizeof(struct yaffs_summary_tags)); -+} -+ -+ -+void yaffs_summary_deinit(struct yaffs_dev *dev) -+{ -+ kfree(dev->sum_tags); -+ dev->sum_tags = NULL; -+ kfree(dev->gc_sum_tags); -+ dev->gc_sum_tags = NULL; -+ dev->chunks_per_summary = 0; -+} -+ -+int yaffs_summary_init(struct yaffs_dev *dev) -+{ -+ int sum_bytes; -+ int chunks_used; /* Number of chunks used by summary */ -+ int sum_tags_bytes; -+ -+ sum_bytes = dev->param.chunks_per_block * -+ sizeof(struct yaffs_summary_tags); -+ -+ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ -+ (dev->data_bytes_per_chunk - -+ sizeof(struct yaffs_summary_header)); -+ -+ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; -+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); -+ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); -+ if (!dev->sum_tags || !dev->gc_sum_tags) { -+ yaffs_summary_deinit(dev); -+ return YAFFS_FAIL; -+ } -+ -+ yaffs_summary_clear(dev); -+ -+ return YAFFS_OK; -+} -+ -+static unsigned yaffs_summary_sum(struct yaffs_dev *dev) -+{ -+ u8 *sum_buffer = (u8 *)dev->sum_tags; -+ int i; -+ unsigned sum = 0; -+ -+ i = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ while (i > 0) { -+ sum += *sum_buffer; -+ sum_buffer++; -+ i--; -+ } -+ -+ return sum; -+} -+ -+static int yaffs_summary_write(struct yaffs_dev *dev, int blk) -+{ -+ struct yaffs_ext_tags tags; -+ u8 *buffer; -+ u8 *sum_buffer = (u8 *)dev->sum_tags; -+ int n_bytes; -+ int chunk_in_nand; -+ int chunk_in_block; -+ int result; -+ int this_tx; -+ struct yaffs_summary_header hdr; -+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ -+ buffer = yaffs_get_temp_buffer(dev); -+ n_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); -+ tags.obj_id = YAFFS_OBJECTID_SUMMARY; -+ tags.chunk_id = 1; -+ chunk_in_block = dev->chunks_per_summary; -+ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + -+ dev->chunks_per_summary; -+ hdr.version = YAFFS_SUMMARY_VERSION; -+ hdr.block = blk; -+ hdr.seq = bi->seq_number; -+ hdr.sum = yaffs_summary_sum(dev); -+ -+ do { -+ this_tx = n_bytes; -+ if (this_tx > sum_bytes_per_chunk) -+ this_tx = sum_bytes_per_chunk; -+ memcpy(buffer, &hdr, sizeof(hdr)); -+ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); -+ tags.n_bytes = this_tx + sizeof(hdr); -+ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, -+ buffer, &tags); -+ -+ if (result != YAFFS_OK) -+ break; -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ dev->n_free_chunks--; -+ -+ n_bytes -= this_tx; -+ sum_buffer += this_tx; -+ chunk_in_nand++; -+ chunk_in_block++; -+ tags.chunk_id++; -+ } while (result == YAFFS_OK && n_bytes > 0); -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ -+ if (result == YAFFS_OK) -+ bi->has_summary = 1; -+ -+ -+ return result; -+} -+ -+int yaffs_summary_read(struct yaffs_dev *dev, -+ struct yaffs_summary_tags *st, -+ int blk) -+{ -+ struct yaffs_ext_tags tags; -+ u8 *buffer; -+ u8 *sum_buffer = (u8 *)st; -+ int n_bytes; -+ int chunk_id; -+ int chunk_in_nand; -+ int chunk_in_block; -+ int result; -+ int this_tx; -+ struct yaffs_summary_header hdr; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); -+ int sum_tags_bytes; -+ -+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ buffer = yaffs_get_temp_buffer(dev); -+ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; -+ chunk_in_block = dev->chunks_per_summary; -+ chunk_in_nand = blk * dev->param.chunks_per_block + -+ dev->chunks_per_summary; -+ chunk_id = 1; -+ do { -+ this_tx = n_bytes; -+ if (this_tx > sum_bytes_per_chunk) -+ this_tx = sum_bytes_per_chunk; -+ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, -+ buffer, &tags); -+ -+ if (tags.chunk_id != chunk_id || -+ tags.obj_id != YAFFS_OBJECTID_SUMMARY || -+ tags.chunk_used == 0 || -+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || -+ tags.n_bytes != (this_tx + sizeof(hdr))) -+ result = YAFFS_FAIL; -+ if (result != YAFFS_OK) -+ break; -+ -+ if (st == dev->sum_tags) { -+ /* If we're scanning then update the block info */ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ } -+ memcpy(&hdr, buffer, sizeof(hdr)); -+ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); -+ n_bytes -= this_tx; -+ sum_buffer += this_tx; -+ chunk_in_nand++; -+ chunk_in_block++; -+ chunk_id++; -+ } while (result == YAFFS_OK && n_bytes > 0); -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ if (result == YAFFS_OK) { -+ /* Verify header */ -+ if (hdr.version != YAFFS_SUMMARY_VERSION || -+ hdr.seq != bi->seq_number || -+ hdr.sum != yaffs_summary_sum(dev)) -+ result = YAFFS_FAIL; -+ } -+ -+ if (st == dev->sum_tags && result == YAFFS_OK) -+ bi->has_summary = 1; -+ -+ return result; -+} -+ -+int yaffs_summary_add(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_nand) -+{ -+ struct yaffs_packed_tags2_tags_only tags_only; -+ struct yaffs_summary_tags *sum_tags; -+ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; -+ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; -+ -+ if (!dev->sum_tags) -+ return YAFFS_OK; -+ -+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { -+ yaffs_pack_tags2_tags_only(&tags_only, tags); -+ sum_tags = &dev->sum_tags[chunk_in_block]; -+ sum_tags->chunk_id = tags_only.chunk_id; -+ sum_tags->n_bytes = tags_only.n_bytes; -+ sum_tags->obj_id = tags_only.obj_id; -+ -+ if (chunk_in_block == dev->chunks_per_summary - 1) { -+ /* Time to write out the summary */ -+ yaffs_summary_write(dev, block_in_nand); -+ yaffs_summary_clear(dev); -+ yaffs_skip_rest_of_block(dev); -+ } -+ } -+ return YAFFS_OK; -+} -+ -+int yaffs_summary_fetch(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block) -+{ -+ struct yaffs_packed_tags2_tags_only tags_only; -+ struct yaffs_summary_tags *sum_tags; -+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { -+ sum_tags = &dev->sum_tags[chunk_in_block]; -+ tags_only.chunk_id = sum_tags->chunk_id; -+ tags_only.n_bytes = sum_tags->n_bytes; -+ tags_only.obj_id = sum_tags->obj_id; -+ yaffs_unpack_tags2_tags_only(tags, &tags_only); -+ return YAFFS_OK; -+ } -+ return YAFFS_FAIL; -+} -+ -+void yaffs_summary_gc(struct yaffs_dev *dev, int blk) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ int i; -+ -+ if (!bi->has_summary) -+ return; -+ -+ for (i = dev->chunks_per_summary; -+ i < dev->param.chunks_per_block; -+ i++) { -+ if (yaffs_check_chunk_bit(dev, blk, i)) { -+ yaffs_clear_chunk_bit(dev, blk, i); -+ bi->pages_in_use--; -+ dev->n_free_chunks++; -+ } -+ } -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_summary.h linux-3.14.43/fs/yaffs2/yaffs_summary.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_summary.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,37 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_SUMMARY_H__ -+#define __YAFFS_SUMMARY_H__ -+ -+#include "yaffs_packedtags2.h" -+ -+ -+int yaffs_summary_init(struct yaffs_dev *dev); -+void yaffs_summary_deinit(struct yaffs_dev *dev); -+ -+int yaffs_summary_add(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block); -+int yaffs_summary_fetch(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block); -+int yaffs_summary_read(struct yaffs_dev *dev, -+ struct yaffs_summary_tags *st, -+ int blk); -+void yaffs_summary_gc(struct yaffs_dev *dev, int blk); -+ -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.14.43/fs/yaffs2/yaffs_tagscompat.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_tagscompat.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,381 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_tagscompat.h" -+#include "yaffs_ecc.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_trace.h" -+ -+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); -+ -+ -+/********** Tags ECC calculations *********/ -+ -+ -+void yaffs_calc_tags_ecc(struct yaffs_tags *tags) -+{ -+ /* Calculate an ecc */ -+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; -+ unsigned i, j; -+ unsigned ecc = 0; -+ unsigned bit = 0; -+ -+ tags->ecc = 0; -+ -+ for (i = 0; i < 8; i++) { -+ for (j = 1; j & 0xff; j <<= 1) { -+ bit++; -+ if (b[i] & j) -+ ecc ^= bit; -+ } -+ } -+ tags->ecc = ecc; -+} -+ -+int yaffs_check_tags_ecc(struct yaffs_tags *tags) -+{ -+ unsigned ecc = tags->ecc; -+ -+ yaffs_calc_tags_ecc(tags); -+ -+ ecc ^= tags->ecc; -+ -+ if (ecc && ecc <= 64) { -+ /* TODO: Handle the failure better. Retire? */ -+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; -+ -+ ecc--; -+ -+ b[ecc / 8] ^= (1 << (ecc & 7)); -+ -+ /* Now recvalc the ecc */ -+ yaffs_calc_tags_ecc(tags); -+ -+ return 1; /* recovered error */ -+ } else if (ecc) { -+ /* Wierd ecc failure value */ -+ /* TODO Need to do somethiong here */ -+ return -1; /* unrecovered error */ -+ } -+ return 0; -+} -+ -+/********** Tags **********/ -+ -+static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, -+ struct yaffs_tags *tags_ptr) -+{ -+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; -+ -+ yaffs_calc_tags_ecc(tags_ptr); -+ -+ spare_ptr->tb0 = tu->as_bytes[0]; -+ spare_ptr->tb1 = tu->as_bytes[1]; -+ spare_ptr->tb2 = tu->as_bytes[2]; -+ spare_ptr->tb3 = tu->as_bytes[3]; -+ spare_ptr->tb4 = tu->as_bytes[4]; -+ spare_ptr->tb5 = tu->as_bytes[5]; -+ spare_ptr->tb6 = tu->as_bytes[6]; -+ spare_ptr->tb7 = tu->as_bytes[7]; -+} -+ -+static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, -+ struct yaffs_spare *spare_ptr, -+ struct yaffs_tags *tags_ptr) -+{ -+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; -+ int result; -+ -+ tu->as_bytes[0] = spare_ptr->tb0; -+ tu->as_bytes[1] = spare_ptr->tb1; -+ tu->as_bytes[2] = spare_ptr->tb2; -+ tu->as_bytes[3] = spare_ptr->tb3; -+ tu->as_bytes[4] = spare_ptr->tb4; -+ tu->as_bytes[5] = spare_ptr->tb5; -+ tu->as_bytes[6] = spare_ptr->tb6; -+ tu->as_bytes[7] = spare_ptr->tb7; -+ -+ result = yaffs_check_tags_ecc(tags_ptr); -+ if (result > 0) -+ dev->n_tags_ecc_fixed++; -+ else if (result < 0) -+ dev->n_tags_ecc_unfixed++; -+} -+ -+static void yaffs_spare_init(struct yaffs_spare *spare) -+{ -+ memset(spare, 0xff, sizeof(struct yaffs_spare)); -+} -+ -+static int yaffs_wr_nand(struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ struct yaffs_spare *spare) -+{ -+ int data_size = dev->data_bytes_per_chunk; -+ -+ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *) spare, sizeof(*spare)); -+} -+ -+static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, -+ struct yaffs_spare *spare, -+ enum yaffs_ecc_result *ecc_result, -+ int correct_errors) -+{ -+ int ret_val; -+ struct yaffs_spare local_spare; -+ int data_size; -+ int spare_size; -+ int ecc_result1, ecc_result2; -+ u8 calc_ecc[3]; -+ -+ if (!spare) { -+ /* If we don't have a real spare, then we use a local one. */ -+ /* Need this for the calculation of the ecc */ -+ spare = &local_spare; -+ } -+ data_size = dev->data_bytes_per_chunk; -+ spare_size = sizeof(struct yaffs_spare); -+ -+ if (dev->param.use_nand_ecc) -+ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *) spare, spare_size, -+ ecc_result); -+ -+ -+ /* Handle the ECC at this level. */ -+ -+ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *)spare, spare_size, -+ NULL); -+ if (!data || !correct_errors) -+ return ret_val; -+ -+ /* Do ECC correction if needed. */ -+ yaffs_ecc_calc(data, calc_ecc); -+ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); -+ yaffs_ecc_calc(&data[256], calc_ecc); -+ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); -+ -+ if (ecc_result1 > 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error fix performed on chunk %d:0", -+ nand_chunk); -+ dev->n_ecc_fixed++; -+ } else if (ecc_result1 < 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error unfixed on chunk %d:0", -+ nand_chunk); -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (ecc_result2 > 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error fix performed on chunk %d:1", -+ nand_chunk); -+ dev->n_ecc_fixed++; -+ } else if (ecc_result2 < 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error unfixed on chunk %d:1", -+ nand_chunk); -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (ecc_result1 || ecc_result2) { -+ /* We had a data problem on this page */ -+ yaffs_handle_rd_data_error(dev, nand_chunk); -+ } -+ -+ if (ecc_result1 < 0 || ecc_result2 < 0) -+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ else if (ecc_result1 > 0 || ecc_result2 > 0) -+ *ecc_result = YAFFS_ECC_RESULT_FIXED; -+ else -+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ -+ return ret_val; -+} -+ -+/* -+ * Functions for robustisizing -+ */ -+ -+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) -+{ -+ int flash_block = nand_chunk / dev->param.chunks_per_block; -+ -+ /* Mark the block for retirement */ -+ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> -+ needs_retiring = 1; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>>Block %d marked for retirement", -+ flash_block); -+ -+ /* TODO: -+ * Just do a garbage collection on the affected block -+ * then retire the block -+ * NB recursion -+ */ -+} -+ -+static int yaffs_tags_compat_wr(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, const struct yaffs_ext_tags *ext_tags) -+{ -+ struct yaffs_spare spare; -+ struct yaffs_tags tags; -+ -+ yaffs_spare_init(&spare); -+ -+ if (ext_tags->is_deleted) -+ spare.page_status = 0; -+ else { -+ tags.obj_id = ext_tags->obj_id; -+ tags.chunk_id = ext_tags->chunk_id; -+ -+ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); -+ -+ if (dev->data_bytes_per_chunk >= 1024) -+ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; -+ else -+ tags.n_bytes_msb = 3; -+ -+ tags.serial_number = ext_tags->serial_number; -+ -+ if (!dev->param.use_nand_ecc && data) { -+ yaffs_ecc_calc(data, spare.ecc1); -+ yaffs_ecc_calc(&data[256], spare.ecc2); -+ } -+ -+ yaffs_load_tags_to_spare(&spare, &tags); -+ } -+ return yaffs_wr_nand(dev, nand_chunk, data, &spare); -+} -+ -+static int yaffs_tags_compat_rd(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, struct yaffs_ext_tags *ext_tags) -+{ -+ struct yaffs_spare spare; -+ struct yaffs_tags tags; -+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; -+ static struct yaffs_spare spare_ff; -+ static int init; -+ int deleted; -+ -+ if (!init) { -+ memset(&spare_ff, 0xff, sizeof(spare_ff)); -+ init = 1; -+ } -+ -+ if (!yaffs_rd_chunk_nand(dev, nand_chunk, -+ data, &spare, &ecc_result, 1)) -+ return YAFFS_FAIL; -+ -+ /* ext_tags may be NULL */ -+ if (!ext_tags) -+ return YAFFS_OK; -+ -+ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; -+ -+ ext_tags->is_deleted = deleted; -+ ext_tags->ecc_result = ecc_result; -+ ext_tags->block_bad = 0; /* We're reading it */ -+ /* therefore it is not a bad block */ -+ ext_tags->chunk_used = -+ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; -+ -+ if (ext_tags->chunk_used) { -+ yaffs_get_tags_from_spare(dev, &spare, &tags); -+ ext_tags->obj_id = tags.obj_id; -+ ext_tags->chunk_id = tags.chunk_id; -+ ext_tags->n_bytes = tags.n_bytes_lsb; -+ -+ if (dev->data_bytes_per_chunk >= 1024) -+ ext_tags->n_bytes |= -+ (((unsigned)tags.n_bytes_msb) << 10); -+ -+ ext_tags->serial_number = tags.serial_number; -+ } -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) -+{ -+ struct yaffs_spare spare; -+ -+ memset(&spare, 0xff, sizeof(struct yaffs_spare)); -+ -+ spare.block_status = 'Y'; -+ -+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, -+ &spare); -+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, -+ NULL, &spare); -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ struct yaffs_spare spare0, spare1; -+ static struct yaffs_spare spare_ff; -+ static int init; -+ enum yaffs_ecc_result dummy; -+ -+ if (!init) { -+ memset(&spare_ff, 0xff, sizeof(spare_ff)); -+ init = 1; -+ } -+ -+ *seq_number = 0; -+ -+ /* Look for bad block markers in the first two chunks */ -+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, -+ NULL, &spare0, &dummy, 0); -+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, -+ NULL, &spare1, &dummy, 0); -+ -+ if (hweight8(spare0.block_status & spare1.block_status) < 7) -+ *state = YAFFS_BLOCK_STATE_DEAD; -+ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) -+ *state = YAFFS_BLOCK_STATE_EMPTY; -+ else -+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; -+ -+ return YAFFS_OK; -+} -+ -+void yaffs_tags_compat_install(struct yaffs_dev *dev) -+{ -+ if(dev->param.is_yaffs2) -+ return; -+ if(!dev->tagger.write_chunk_tags_fn) -+ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; -+ if(!dev->tagger.read_chunk_tags_fn) -+ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; -+ if(!dev->tagger.query_block_fn) -+ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; -+ if(!dev->tagger.mark_bad_fn) -+ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.14.43/fs/yaffs2/yaffs_tagscompat.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_tagscompat.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,44 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_TAGSCOMPAT_H__ -+#define __YAFFS_TAGSCOMPAT_H__ -+ -+ -+#include "yaffs_guts.h" -+ -+#if 0 -+ -+ -+int yaffs_tags_compat_wr(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, const struct yaffs_ext_tags *tags); -+int yaffs_tags_compat_rd(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, struct yaffs_ext_tags *tags); -+int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); -+int yaffs_tags_compat_query_block(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number); -+ -+#endif -+ -+ -+void yaffs_tags_compat_install(struct yaffs_dev *dev); -+void yaffs_calc_tags_ecc(struct yaffs_tags *tags); -+int yaffs_check_tags_ecc(struct yaffs_tags *tags); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.14.43/fs/yaffs2/yaffs_tagsmarshall.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_tagsmarshall.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,199 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yaffs_packedtags2.h" -+ -+static int yaffs_tags_marshall_write(struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ const struct yaffs_ext_tags *tags) -+{ -+ struct yaffs_packed_tags2 pt; -+ int retval; -+ -+ int packed_tags_size = -+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); -+ void *packed_tags_ptr = -+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_tags_marshall_write chunk %d data %p tags %p", -+ nand_chunk, data, tags); -+ -+ /* For yaffs2 writing there must be both data and tags. -+ * If we're using inband tags, then the tags are stuffed into -+ * the end of the data buffer. -+ */ -+ if (!data || !tags) -+ BUG(); -+ else if (dev->param.inband_tags) { -+ struct yaffs_packed_tags2_tags_only *pt2tp; -+ pt2tp = -+ (struct yaffs_packed_tags2_tags_only *)(data + -+ dev-> -+ data_bytes_per_chunk); -+ yaffs_pack_tags2_tags_only(pt2tp, tags); -+ } else { -+ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); -+ } -+ -+ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ (dev->param.inband_tags) ? NULL : packed_tags_ptr, -+ (dev->param.inband_tags) ? 0 : packed_tags_size); -+ -+ return retval; -+} -+ -+static int yaffs_tags_marshall_read(struct yaffs_dev *dev, -+ int nand_chunk, u8 *data, -+ struct yaffs_ext_tags *tags) -+{ -+ int retval = 0; -+ int local_data = 0; -+ u8 spare_buffer[100]; -+ enum yaffs_ecc_result ecc_result; -+ -+ struct yaffs_packed_tags2 pt; -+ -+ int packed_tags_size = -+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); -+ void *packed_tags_ptr = -+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_tags_marshall_read chunk %d data %p tags %p", -+ nand_chunk, data, tags); -+ -+ if (dev->param.inband_tags) { -+ if (!data) { -+ local_data = 1; -+ data = yaffs_get_temp_buffer(dev); -+ } -+ } -+ -+ if (dev->param.inband_tags || (data && !tags)) -+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ NULL, 0, -+ &ecc_result); -+ else if (tags) -+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ spare_buffer, packed_tags_size, -+ &ecc_result); -+ else -+ BUG(); -+ -+ -+ if (dev->param.inband_tags) { -+ if (tags) { -+ struct yaffs_packed_tags2_tags_only *pt2tp; -+ pt2tp = -+ (struct yaffs_packed_tags2_tags_only *) -+ &data[dev->data_bytes_per_chunk]; -+ yaffs_unpack_tags2_tags_only(tags, pt2tp); -+ } -+ } else if (tags) { -+ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); -+ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); -+ } -+ -+ if (local_data) -+ yaffs_release_temp_buffer(dev, data); -+ -+ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { -+ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { -+ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) -+ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; -+ dev->n_ecc_fixed++; -+ } -+ -+ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) -+ return YAFFS_OK; -+ else -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", -+ block_no); -+ -+ retval = dev->drv.drv_check_bad_fn(dev, block_no); -+ -+ if (retval== YAFFS_FAIL) { -+ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); -+ -+ *state = YAFFS_BLOCK_STATE_DEAD; -+ *seq_number = 0; -+ } else { -+ struct yaffs_ext_tags t; -+ -+ yaffs_tags_marshall_read(dev, -+ block_no * dev->param.chunks_per_block, -+ NULL, &t); -+ -+ if (t.chunk_used) { -+ *seq_number = t.seq_number; -+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; -+ } else { -+ *seq_number = 0; -+ *state = YAFFS_BLOCK_STATE_EMPTY; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "block query returns seq %d state %d", -+ *seq_number, *state); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ else -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ return dev->drv.drv_mark_bad_fn(dev, block_no); -+ -+} -+ -+ -+void yaffs_tags_marshall_install(struct yaffs_dev *dev) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!dev->tagger.write_chunk_tags_fn) -+ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; -+ -+ if (!dev->tagger.read_chunk_tags_fn) -+ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; -+ -+ if (!dev->tagger.query_block_fn) -+ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; -+ -+ if (!dev->tagger.mark_bad_fn) -+ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; -+ -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.14.43/fs/yaffs2/yaffs_tagsmarshall.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_tagsmarshall.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,22 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_TAGSMARSHALL_H__ -+#define __YAFFS_TAGSMARSHALL_H__ -+ -+#include "yaffs_guts.h" -+void yaffs_tags_marshall_install(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_trace.h linux-3.14.43/fs/yaffs2/yaffs_trace.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_trace.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,57 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YTRACE_H__ -+#define __YTRACE_H__ -+ -+extern unsigned int yaffs_trace_mask; -+extern unsigned int yaffs_wr_attempts; -+ -+/* -+ * Tracing flags. -+ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. -+ */ -+ -+#define YAFFS_TRACE_OS 0x00000002 -+#define YAFFS_TRACE_ALLOCATE 0x00000004 -+#define YAFFS_TRACE_SCAN 0x00000008 -+#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 -+#define YAFFS_TRACE_ERASE 0x00000020 -+#define YAFFS_TRACE_GC 0x00000040 -+#define YAFFS_TRACE_WRITE 0x00000080 -+#define YAFFS_TRACE_TRACING 0x00000100 -+#define YAFFS_TRACE_DELETION 0x00000200 -+#define YAFFS_TRACE_BUFFERS 0x00000400 -+#define YAFFS_TRACE_NANDACCESS 0x00000800 -+#define YAFFS_TRACE_GC_DETAIL 0x00001000 -+#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 -+#define YAFFS_TRACE_MTD 0x00004000 -+#define YAFFS_TRACE_CHECKPOINT 0x00008000 -+ -+#define YAFFS_TRACE_VERIFY 0x00010000 -+#define YAFFS_TRACE_VERIFY_NAND 0x00020000 -+#define YAFFS_TRACE_VERIFY_FULL 0x00040000 -+#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 -+ -+#define YAFFS_TRACE_SYNC 0x00100000 -+#define YAFFS_TRACE_BACKGROUND 0x00200000 -+#define YAFFS_TRACE_LOCK 0x00400000 -+#define YAFFS_TRACE_MOUNT 0x00800000 -+ -+#define YAFFS_TRACE_ERROR 0x40000000 -+#define YAFFS_TRACE_BUG 0x80000000 -+#define YAFFS_TRACE_ALWAYS 0xf0000000 -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_verify.c linux-3.14.43/fs/yaffs2/yaffs_verify.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_verify.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,529 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_verify.h" -+#include "yaffs_trace.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_nand.h" -+ -+int yaffs_skip_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & -+ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); -+} -+ -+static int yaffs_skip_full_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); -+} -+ -+static int yaffs_skip_nand_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); -+} -+ -+static const char * const block_state_name[] = { -+ "Unknown", -+ "Needs scan", -+ "Scanning", -+ "Empty", -+ "Allocating", -+ "Full", -+ "Dirty", -+ "Checkpoint", -+ "Collecting", -+ "Dead" -+}; -+ -+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) -+{ -+ int actually_used; -+ int in_use; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Report illegal runtime states */ -+ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has undefined state %d", -+ n, bi->block_state); -+ -+ switch (bi->block_state) { -+ case YAFFS_BLOCK_STATE_UNKNOWN: -+ case YAFFS_BLOCK_STATE_SCANNING: -+ case YAFFS_BLOCK_STATE_NEEDS_SCAN: -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has bad run-state %s", -+ n, block_state_name[bi->block_state]); -+ } -+ -+ /* Check pages in use and soft deletions are legal */ -+ -+ actually_used = bi->pages_in_use - bi->soft_del_pages; -+ -+ if (bi->pages_in_use < 0 || -+ bi->pages_in_use > dev->param.chunks_per_block || -+ bi->soft_del_pages < 0 || -+ bi->soft_del_pages > dev->param.chunks_per_block || -+ actually_used < 0 || actually_used > dev->param.chunks_per_block) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has illegal values pages_in_used %d soft_del_pages %d", -+ n, bi->pages_in_use, bi->soft_del_pages); -+ -+ /* Check chunk bitmap legal */ -+ in_use = yaffs_count_chunk_bits(dev, n); -+ if (in_use != bi->pages_in_use) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", -+ n, bi->pages_in_use, in_use); -+} -+ -+void yaffs_verify_collected_blk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, int n) -+{ -+ yaffs_verify_blk(dev, bi, n); -+ -+ /* After collection the block should be in the erased state */ -+ -+ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && -+ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Block %d is in state %d after gc, should be erased", -+ n, bi->block_state); -+ } -+} -+ -+void yaffs_verify_blocks(struct yaffs_dev *dev) -+{ -+ int i; -+ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; -+ int illegal_states = 0; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ memset(state_count, 0, sizeof(state_count)); -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); -+ yaffs_verify_blk(dev, bi, i); -+ -+ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) -+ state_count[bi->block_state]++; -+ else -+ illegal_states++; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); -+ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "%d blocks have illegal states", -+ illegal_states); -+ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Too many allocating blocks"); -+ -+ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "%s %d blocks", -+ block_state_name[i], state_count[i]); -+ -+ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Checkpoint block count wrong dev %d count %d", -+ dev->blocks_in_checkpt, -+ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); -+ -+ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Erased block count wrong dev %d count %d", -+ dev->n_erased_blocks, -+ state_count[YAFFS_BLOCK_STATE_EMPTY]); -+ -+ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Too many collecting blocks %d (max is 1)", -+ state_count[YAFFS_BLOCK_STATE_COLLECTING]); -+} -+ -+/* -+ * Verify the object header. oh must be valid, but obj and tags may be NULL in -+ * which case those tests will not be performed. -+ */ -+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, -+ struct yaffs_ext_tags *tags, int parent_check) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ if (!(tags && obj && oh)) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Verifying object header tags %p obj %p oh %p", -+ tags, obj, oh); -+ return; -+ } -+ -+ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || -+ oh->type > YAFFS_OBJECT_TYPE_MAX) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header type is illegal value 0x%x", -+ tags->obj_id, oh->type); -+ -+ if (tags->obj_id != obj->obj_id) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch obj_id %d", -+ tags->obj_id, obj->obj_id); -+ -+ /* -+ * Check that the object's parent ids match if parent_check requested. -+ * -+ * Tests do not apply to the root object. -+ */ -+ -+ if (parent_check && tags->obj_id > 1 && !obj->parent) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch parent_id %d obj->parent is NULL", -+ tags->obj_id, oh->parent_obj_id); -+ -+ if (parent_check && obj->parent && -+ oh->parent_obj_id != obj->parent->obj_id && -+ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || -+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch parent_id %d parent_obj_id %d", -+ tags->obj_id, oh->parent_obj_id, -+ obj->parent->obj_id); -+ -+ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header name is NULL", -+ obj->obj_id); -+ -+ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header name is 0xff", -+ obj->obj_id); -+} -+ -+void yaffs_verify_file(struct yaffs_obj *obj) -+{ -+ u32 x; -+ int required_depth; -+ int actual_depth; -+ int last_chunk; -+ u32 offset_in_chunk; -+ u32 the_chunk; -+ -+ u32 i; -+ struct yaffs_dev *dev; -+ struct yaffs_ext_tags tags; -+ struct yaffs_tnode *tn; -+ u32 obj_id; -+ -+ if (!obj) -+ return; -+ -+ if (yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ dev = obj->my_dev; -+ obj_id = obj->obj_id; -+ -+ -+ /* Check file size is consistent with tnode depth */ -+ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, -+ &last_chunk, &offset_in_chunk); -+ last_chunk++; -+ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (x > 0) { -+ x >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ actual_depth = obj->variant.file_variant.top_level; -+ -+ /* Check that the chunks in the tnode tree are all correct. -+ * We do this by scanning through the tnode tree and -+ * checking the tags for every chunk match. -+ */ -+ -+ if (yaffs_skip_nand_verification(dev)) -+ return; -+ -+ for (i = 1; i <= last_chunk; i++) { -+ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); -+ -+ if (!tn) -+ continue; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, i); -+ if (the_chunk > 0) { -+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, -+ &tags); -+ if (tags.obj_id != obj_id || tags.chunk_id != i) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", -+ obj_id, i, the_chunk, -+ tags.obj_id, tags.chunk_id); -+ } -+ } -+} -+ -+void yaffs_verify_link(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ /* Verify sane equivalent object */ -+} -+ -+void yaffs_verify_symlink(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ /* Verify symlink string */ -+} -+ -+void yaffs_verify_special(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+} -+ -+void yaffs_verify_obj(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ u32 chunk_min; -+ u32 chunk_max; -+ u32 chunk_id_ok; -+ u32 chunk_in_range; -+ u32 chunk_wrongly_deleted; -+ u32 chunk_valid; -+ -+ if (!obj) -+ return; -+ -+ if (obj->being_created) -+ return; -+ -+ dev = obj->my_dev; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Check sane object header chunk */ -+ -+ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; -+ chunk_max = -+ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; -+ -+ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && -+ ((unsigned)(obj->hdr_chunk)) <= chunk_max); -+ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); -+ chunk_valid = chunk_in_range && -+ yaffs_check_chunk_bit(dev, -+ obj->hdr_chunk / dev->param.chunks_per_block, -+ obj->hdr_chunk % dev->param.chunks_per_block); -+ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; -+ -+ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has chunk_id %d %s %s", -+ obj->obj_id, obj->hdr_chunk, -+ chunk_id_ok ? "" : ",out of range", -+ chunk_wrongly_deleted ? ",marked as deleted" : ""); -+ -+ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { -+ struct yaffs_ext_tags tags; -+ struct yaffs_obj_hdr *oh; -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ oh = (struct yaffs_obj_hdr *)buffer; -+ -+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); -+ -+ yaffs_verify_oh(obj, oh, &tags, 1); -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ -+ /* Verify it has a parent */ -+ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has parent pointer %p which does not look like an object", -+ obj->obj_id, obj->parent); -+ } -+ -+ /* Verify parent is a directory */ -+ if (obj->parent && -+ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d's parent is not a directory (type %d)", -+ obj->obj_id, obj->parent->variant_type); -+ } -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ yaffs_verify_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ yaffs_verify_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ yaffs_verify_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ yaffs_verify_link(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ yaffs_verify_special(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ default: -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has illegaltype %d", -+ obj->obj_id, obj->variant_type); -+ break; -+ } -+} -+ -+void yaffs_verify_objects(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ int i; -+ struct list_head *lh; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each(lh, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ yaffs_verify_obj(obj); -+ } -+ } -+} -+ -+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) -+{ -+ struct list_head *lh; -+ struct yaffs_obj *list_obj; -+ int count = 0; -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); -+ BUG(); -+ return; -+ } -+ -+ if (yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ if (!obj->parent) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); -+ BUG(); -+ return; -+ } -+ -+ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); -+ BUG(); -+ } -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ list_for_each(lh, &obj->parent->variant.dir_variant.children) { -+ list_obj = list_entry(lh, struct yaffs_obj, siblings); -+ yaffs_verify_obj(list_obj); -+ if (obj == list_obj) -+ count++; -+ } -+ -+ if (count != 1) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Object in directory %d times", -+ count); -+ BUG(); -+ } -+} -+ -+void yaffs_verify_dir(struct yaffs_obj *directory) -+{ -+ struct list_head *lh; -+ struct yaffs_obj *list_obj; -+ -+ if (!directory) { -+ BUG(); -+ return; -+ } -+ -+ if (yaffs_skip_full_verification(directory->my_dev)) -+ return; -+ -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Directory has wrong type: %d", -+ directory->variant_type); -+ BUG(); -+ } -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ list_for_each(lh, &directory->variant.dir_variant.children) { -+ list_obj = list_entry(lh, struct yaffs_obj, siblings); -+ if (list_obj->parent != directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Object in directory list has wrong parent %p", -+ list_obj->parent); -+ BUG(); -+ } -+ yaffs_verify_obj_in_dir(list_obj); -+ } -+} -+ -+static int yaffs_free_verification_failures; -+ -+void yaffs_verify_free_chunks(struct yaffs_dev *dev) -+{ -+ int counted; -+ int difference; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ counted = yaffs_count_free_chunks(dev); -+ -+ difference = dev->n_free_chunks - counted; -+ -+ if (difference) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Freechunks verification failure %d %d %d", -+ dev->n_free_chunks, counted, difference); -+ yaffs_free_verification_failures++; -+ } -+} -+ -+int yaffs_verify_file_sane(struct yaffs_obj *in) -+{ -+ (void) in; -+ return YAFFS_OK; -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_verify.h linux-3.14.43/fs/yaffs2/yaffs_verify.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_verify.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,43 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_VERIFY_H__ -+#define __YAFFS_VERIFY_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, -+ int n); -+void yaffs_verify_collected_blk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, int n); -+void yaffs_verify_blocks(struct yaffs_dev *dev); -+ -+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, -+ struct yaffs_ext_tags *tags, int parent_check); -+void yaffs_verify_file(struct yaffs_obj *obj); -+void yaffs_verify_link(struct yaffs_obj *obj); -+void yaffs_verify_symlink(struct yaffs_obj *obj); -+void yaffs_verify_special(struct yaffs_obj *obj); -+void yaffs_verify_obj(struct yaffs_obj *obj); -+void yaffs_verify_objects(struct yaffs_dev *dev); -+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); -+void yaffs_verify_dir(struct yaffs_obj *directory); -+void yaffs_verify_free_chunks(struct yaffs_dev *dev); -+ -+int yaffs_verify_file_sane(struct yaffs_obj *obj); -+ -+int yaffs_skip_verification(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_vfs.c linux-3.14.43/fs/yaffs2/yaffs_vfs.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_vfs.c 2015-06-15 21:21:52.000000000 +0200 -@@ -0,0 +1,3658 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * Acknowledgements: -+ * Luc van OostenRyck for numerous patches. -+ * Nick Bane for numerous patches. -+ * Nick Bane for 2.5/2.6 integration. -+ * Andras Toth for mknod rdev issue. -+ * Michael Fischer for finding the problem with inode inconsistency. -+ * Some code bodily lifted from JFFS -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * -+ * This is the file system front-end to YAFFS that hooks it up to -+ * the VFS. -+ * -+ * Special notes: -+ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with -+ * this superblock -+ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this -+ * superblock -+ * >> inode->u.generic_ip points to the associated struct yaffs_obj. -+ */ -+ -+/* -+ * There are two variants of the VFS glue code. This variant should compile -+ * for any version of Linux. -+ */ -+#include -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) -+#define YAFFS_COMPILE_BACKGROUND -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) -+#define YAFFS_COMPILE_FREEZER -+#endif -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) -+#define YAFFS_COMPILE_EXPORTFS -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) -+#define YAFFS_USE_SETATTR_COPY -+#define YAFFS_USE_TRUNCATE_SETSIZE -+#endif -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) -+#define YAFFS_HAS_EVICT_INODE -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) -+#define YAFFS_NEW_FOLLOW_LINK 1 -+#else -+#define YAFFS_NEW_FOLLOW_LINK 0 -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) -+#define YAFFS_HAS_WRITE_SUPER -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+#include -+#endif -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+#include -+#endif -+ -+#ifdef YAFFS_COMPILE_BACKGROUND -+#include -+#include -+#endif -+#ifdef YAFFS_COMPILE_FREEZER -+#include -+#endif -+ -+#include -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ -+#include -+ -+#define UnlockPage(p) unlock_page(p) -+#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) -+ -+/* FIXME: use sb->s_id instead ? */ -+#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) -+ -+#else -+ -+#include -+#define BDEVNAME_SIZE 0 -+#define yaffs_devname(sb, buf) kdevname(sb->s_dev) -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) -+/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ -+#define __user -+#endif -+ -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) -+#define YPROC_ROOT (&proc_root) -+#else -+#define YPROC_ROOT NULL -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) -+#define Y_INIT_TIMER(a) init_timer(a) -+#else -+#define Y_INIT_TIMER(a) init_timer_on_stack(a) -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) -+#define YAFFS_USE_WRITE_BEGIN_END 1 -+#else -+#define YAFFS_USE_WRITE_BEGIN_END 0 -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) -+#define YAFFS_SUPER_HAS_DIRTY -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) -+#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) -+static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) -+{ -+ uint64_t result = partition_size; -+ do_div(result, block_size); -+ return (uint32_t) result; -+} -+#else -+#define YCALCBLOCKS(s, b) ((s)/(b)) -+#endif -+ -+#include -+#include -+ -+#include "yportenv.h" -+#include "yaffs_trace.h" -+#include "yaffs_guts.h" -+#include "yaffs_attribs.h" -+ -+#include "yaffs_linux.h" -+ -+#include "yaffs_mtdif.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_getblockinfo.h" -+ -+unsigned int yaffs_trace_mask = -+ YAFFS_TRACE_BAD_BLOCKS | -+ YAFFS_TRACE_ALWAYS | -+ 0; -+ -+unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; -+unsigned int yaffs_auto_checkpoint = 1; -+unsigned int yaffs_gc_control = 1; -+unsigned int yaffs_bg_enable = 1; -+unsigned int yaffs_auto_select = 1; -+/* Module Parameters */ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+module_param(yaffs_trace_mask, uint, 0644); -+module_param(yaffs_wr_attempts, uint, 0644); -+module_param(yaffs_auto_checkpoint, uint, 0644); -+module_param(yaffs_gc_control, uint, 0644); -+module_param(yaffs_bg_enable, uint, 0644); -+#else -+MODULE_PARM(yaffs_trace_mask, "i"); -+MODULE_PARM(yaffs_wr_attempts, "i"); -+MODULE_PARM(yaffs_auto_checkpoint, "i"); -+MODULE_PARM(yaffs_gc_control, "i"); -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) -+/* use iget and read_inode */ -+#define Y_IGET(sb, inum) iget((sb), (inum)) -+ -+#else -+/* Call local equivalent */ -+#define YAFFS_USE_OWN_IGET -+#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) -+ -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) -+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) -+#else -+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) -+#endif -+ -+#define yaffs_inode_to_obj(iptr) \ -+ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) -+#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) -+#else -+#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+#define Y_CLEAR_INODE(i) clear_inode(i) -+#else -+#define Y_CLEAR_INODE(i) end_writeback(i) -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) -+#define YAFFS_USE_DIR_ITERATE -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0)) -+#define YAFFS_NEW_PROCFS -+#include -+#endif -+ -+ -+#define update_dir_time(dir) do {\ -+ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ -+ } while (0) -+ -+static void yaffs_fill_inode_from_obj(struct inode *inode, -+ struct yaffs_obj *obj); -+ -+ -+static void yaffs_gross_lock(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); -+ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); -+} -+ -+static void yaffs_gross_unlock(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); -+ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); -+} -+ -+ -+static int yaffs_readpage_nolock(struct file *f, struct page *pg) -+{ -+ /* Lifted from jffs2 */ -+ -+ struct yaffs_obj *obj; -+ unsigned char *pg_buf; -+ int ret; -+ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readpage_nolock at %lld, size %08x", -+ (long long)pos, -+ (unsigned)PAGE_CACHE_SIZE); -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ BUG_ON(!PageLocked(pg)); -+#else -+ if (!PageLocked(pg)) -+ PAGE_BUG(pg); -+#endif -+ -+ pg_buf = kmap(pg); -+ /* FIXME: Can kmap fail? */ -+ -+ yaffs_gross_lock(dev); -+ -+ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (ret >= 0) -+ ret = 0; -+ -+ if (ret) { -+ ClearPageUptodate(pg); -+ SetPageError(pg); -+ } else { -+ SetPageUptodate(pg); -+ ClearPageError(pg); -+ } -+ -+ flush_dcache_page(pg); -+ kunmap(pg); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); -+ return ret; -+} -+ -+static int yaffs_readpage_unlock(struct file *f, struct page *pg) -+{ -+ int ret = yaffs_readpage_nolock(f, pg); -+ UnlockPage(pg); -+ return ret; -+} -+ -+static int yaffs_readpage(struct file *f, struct page *pg) -+{ -+ int ret; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); -+ ret = yaffs_readpage_unlock(f, pg); -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); -+ return ret; -+} -+ -+ -+static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) -+{ -+ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); -+ -+ if (lc) -+ lc->dirty = val; -+ -+# ifdef YAFFS_SUPER_HAS_DIRTY -+ { -+ struct super_block *sb = lc->super; -+ -+ if (sb) -+ sb->s_dirt = val; -+ } -+#endif -+ -+} -+ -+static void yaffs_set_super_dirty(struct yaffs_dev *dev) -+{ -+ yaffs_set_super_dirty_val(dev, 1); -+} -+ -+static void yaffs_clear_super_dirty(struct yaffs_dev *dev) -+{ -+ yaffs_set_super_dirty_val(dev, 0); -+} -+ -+static int yaffs_check_super_dirty(struct yaffs_dev *dev) -+{ -+ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); -+ -+ if (lc && lc->dirty) -+ return 1; -+ -+# ifdef YAFFS_SUPER_HAS_DIRTY -+ { -+ struct super_block *sb = lc->super; -+ -+ if (sb && sb->s_dirt) -+ return 1; -+ } -+#endif -+ return 0; -+ -+} -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_writepage(struct page *page, struct writeback_control *wbc) -+#else -+static int yaffs_writepage(struct page *page) -+#endif -+{ -+ struct yaffs_dev *dev; -+ struct address_space *mapping = page->mapping; -+ struct inode *inode; -+ unsigned long end_index; -+ char *buffer; -+ struct yaffs_obj *obj; -+ int n_written = 0; -+ unsigned n_bytes; -+ loff_t i_size; -+ -+ if (!mapping) -+ BUG(); -+ inode = mapping->host; -+ if (!inode) -+ BUG(); -+ i_size = i_size_read(inode); -+ -+ end_index = i_size >> PAGE_CACHE_SHIFT; -+ -+ if (page->index < end_index) -+ n_bytes = PAGE_CACHE_SIZE; -+ else { -+ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); -+ -+ if (page->index > end_index || !n_bytes) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_writepage at %lld, inode size = %lld!!", -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, -+ inode->i_size); -+ yaffs_trace(YAFFS_TRACE_OS, -+ " -> don't care!!"); -+ -+ zero_user_segment(page, 0, PAGE_CACHE_SIZE); -+ set_page_writeback(page); -+ unlock_page(page); -+ end_page_writeback(page); -+ return 0; -+ } -+ } -+ -+ if (n_bytes != PAGE_CACHE_SIZE) -+ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); -+ -+ get_page(page); -+ -+ buffer = kmap(page); -+ -+ obj = yaffs_inode_to_obj(inode); -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_writepage at %lld, size %08x", -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "writepag0: obj = %lld, ino = %lld", -+ obj->variant.file_variant.file_size, inode->i_size); -+ -+ n_written = yaffs_wr_file(obj, buffer, -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); -+ -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "writepag1: obj = %lld, ino = %lld", -+ obj->variant.file_variant.file_size, inode->i_size); -+ -+ yaffs_gross_unlock(dev); -+ -+ kunmap(page); -+ set_page_writeback(page); -+ unlock_page(page); -+ end_page_writeback(page); -+ put_page(page); -+ -+ return (n_written == n_bytes) ? 0 : -ENOSPC; -+} -+ -+/* Space holding and freeing is done to ensure we have space available for write_begin/end */ -+/* For now we just assume few parallel writes and check against a small number. */ -+/* Todo: need to do this with a counter to handle parallel reads better */ -+ -+static ssize_t yaffs_hold_space(struct file *f) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ int n_free_chunks; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ n_free_chunks = yaffs_get_n_free_chunks(dev); -+ -+ yaffs_gross_unlock(dev); -+ -+ return (n_free_chunks > 20) ? 1 : 0; -+} -+ -+static void yaffs_release_space(struct file *f) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_gross_unlock(dev); -+} -+ -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+static int yaffs_write_begin(struct file *filp, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned flags, -+ struct page **pagep, void **fsdata) -+{ -+ struct page *pg = NULL; -+ pgoff_t index = pos >> PAGE_CACHE_SHIFT; -+ -+ int ret = 0; -+ int space_held = 0; -+ -+ /* Get a page */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) -+ pg = grab_cache_page_write_begin(mapping, index, flags); -+#else -+ pg = __grab_cache_page(mapping, index); -+#endif -+ -+ *pagep = pg; -+ if (!pg) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "start yaffs_write_begin index %d(%x) uptodate %d", -+ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); -+ -+ /* Get fs space */ -+ space_held = yaffs_hold_space(filp); -+ -+ if (!space_held) { -+ ret = -ENOSPC; -+ goto out; -+ } -+ -+ /* Update page if required */ -+ -+ if (!Page_Uptodate(pg)) -+ ret = yaffs_readpage_nolock(filp, pg); -+ -+ if (ret) -+ goto out; -+ -+ /* Happy path return */ -+ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); -+ -+ return 0; -+ -+out: -+ yaffs_trace(YAFFS_TRACE_OS, -+ "end yaffs_write_begin fail returning %d", ret); -+ if (space_held) -+ yaffs_release_space(filp); -+ if (pg) { -+ unlock_page(pg); -+ page_cache_release(pg); -+ } -+ return ret; -+} -+ -+#else -+ -+static int yaffs_prepare_write(struct file *f, struct page *pg, -+ unsigned offset, unsigned to) -+{ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); -+ -+ if (!Page_Uptodate(pg)) -+ return yaffs_readpage_nolock(f, pg); -+ return 0; -+} -+#endif -+ -+ -+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, -+ loff_t * pos) -+{ -+ struct yaffs_obj *obj; -+ int n_written; -+ loff_t ipos; -+ struct inode *inode; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write: hey obj is null!"); -+ return -EINVAL; -+ } -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ inode = f->f_dentry->d_inode; -+ -+ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) -+ ipos = inode->i_size; -+ else -+ ipos = *pos; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", -+ (unsigned)n, (unsigned)n, obj->obj_id, ipos); -+ -+ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); -+ -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write: %d(%x) bytes written", -+ (unsigned)n, (unsigned)n); -+ -+ if (n_written > 0) { -+ ipos += n_written; -+ *pos = ipos; -+ if (ipos > inode->i_size) { -+ inode->i_size = ipos; -+ inode->i_blocks = (ipos + 511) >> 9; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write size updated to %lld bytes, %d blocks", -+ ipos, (int)(inode->i_blocks)); -+ } -+ -+ } -+ yaffs_gross_unlock(dev); -+ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; -+} -+ -+ -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+static int yaffs_write_end(struct file *filp, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned copied, -+ struct page *pg, void *fsdadata) -+{ -+ int ret = 0; -+ void *addr, *kva; -+ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); -+ -+ kva = kmap(pg); -+ addr = kva + offset_into_page; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_write_end addr %p pos %lld n_bytes %d", -+ addr, pos, copied); -+ -+ ret = yaffs_file_write(filp, addr, copied, &pos); -+ -+ if (ret != copied) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_write_end not same size ret %d copied %d", -+ ret, copied); -+ SetPageError(pg); -+ } -+ -+ kunmap(pg); -+ -+ yaffs_release_space(filp); -+ unlock_page(pg); -+ page_cache_release(pg); -+ return ret; -+} -+#else -+ -+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, -+ unsigned to) -+{ -+ void *addr, *kva; -+ -+ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; -+ int n_bytes = to - offset; -+ int n_written; -+ -+ kva = kmap(pg); -+ addr = kva + offset; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write addr %p pos %lld n_bytes %d", -+ addr, pos, n_bytes); -+ -+ n_written = yaffs_file_write(f, addr, n_bytes, &pos); -+ -+ if (n_written != n_bytes) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write not same size n_written %d n_bytes %d", -+ n_written, n_bytes); -+ SetPageError(pg); -+ } -+ kunmap(pg); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write returning %d", -+ n_written == n_bytes ? 0 : n_written); -+ -+ return n_written == n_bytes ? 0 : n_written; -+} -+#endif -+ -+static struct address_space_operations yaffs_file_address_operations = { -+ .readpage = yaffs_readpage, -+ .writepage = yaffs_writepage, -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+ .write_begin = yaffs_write_begin, -+ .write_end = yaffs_write_end, -+#else -+ .prepare_write = yaffs_prepare_write, -+ .commit_write = yaffs_commit_write, -+#endif -+}; -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_file_flush(struct file *file, fl_owner_t id) -+#else -+static int yaffs_file_flush(struct file *file) -+#endif -+{ -+ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); -+ -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_flush object %d (%s)", -+ obj->obj_id, -+ obj->dirty ? "dirty" : "clean"); -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_flush_file(obj, 1, 0, 0); -+ -+ yaffs_gross_unlock(dev); -+ -+ return 0; -+} -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) -+static int yaffs_sync_object(struct file *file, int datasync) -+#else -+static int yaffs_sync_object(struct file *file, struct dentry *dentry, -+ int datasync) -+#endif -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) -+ struct dentry *dentry = file->f_path.dentry; -+#endif -+ -+ obj = yaffs_dentry_to_obj(dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, -+ "yaffs_sync_object"); -+ yaffs_gross_lock(dev); -+ yaffs_flush_file(obj, 1, datasync, 0); -+ yaffs_gross_unlock(dev); -+ return 0; -+} -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) -+static const struct file_operations yaffs_file_operations = { -+ .read = do_sync_read, -+ .write = do_sync_write, -+ .aio_read = generic_file_aio_read, -+ .aio_write = generic_file_aio_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+ .splice_read = generic_file_splice_read, -+ .splice_write = generic_file_splice_write, -+ .llseek = generic_file_llseek, -+}; -+ -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) -+ -+static const struct file_operations yaffs_file_operations = { -+ .read = do_sync_read, -+ .write = do_sync_write, -+ .aio_read = generic_file_aio_read, -+ .aio_write = generic_file_aio_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+ .sendfile = generic_file_sendfile, -+}; -+ -+#else -+ -+static const struct file_operations yaffs_file_operations = { -+ .read = generic_file_read, -+ .write = generic_file_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ .sendfile = generic_file_sendfile, -+#endif -+}; -+#endif -+ -+ -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) -+static void zero_user_segment(struct page *page, unsigned start, unsigned end) -+{ -+ void *kaddr = kmap_atomic(page, KM_USER0); -+ memset(kaddr + start, 0, end - start); -+ kunmap_atomic(kaddr, KM_USER0); -+ flush_dcache_page(page); -+} -+#endif -+ -+ -+static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) -+{ -+#ifdef YAFFS_USE_TRUNCATE_SETSIZE -+ truncate_setsize(inode, newsize); -+ return 0; -+#else -+ truncate_inode_pages(&inode->i_data, newsize); -+ return 0; -+#endif -+ -+} -+ -+ -+static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) -+{ -+#ifdef YAFFS_USE_SETATTR_COPY -+ setattr_copy(inode, attr); -+ return 0; -+#else -+ return inode_setattr(inode, attr); -+#endif -+ -+} -+ -+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_setattr of object %d", -+ yaffs_inode_to_obj(inode)->obj_id); -+#if 0 -+ /* Fail if a requested resize >= 2GB */ -+ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) -+ error = -EINVAL; -+#endif -+ -+ if (error == 0) -+ error = inode_change_ok(inode, attr); -+ if (error == 0) { -+ int result; -+ if (!error) { -+ error = yaffs_vfs_setattr(inode, attr); -+ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); -+ if (attr->ia_valid & ATTR_SIZE) { -+ yaffs_vfs_setsize(inode, attr->ia_size); -+ inode->i_blocks = (inode->i_size + 511) >> 9; -+ } -+ } -+ dev = yaffs_inode_to_obj(inode)->my_dev; -+ if (attr->ia_valid & ATTR_SIZE) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "resize to %d(%x)", -+ (int)(attr->ia_size), -+ (int)(attr->ia_size)); -+ } -+ yaffs_gross_lock(dev); -+ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); -+ if (result == YAFFS_OK) { -+ error = 0; -+ } else { -+ error = -EPERM; -+ } -+ yaffs_gross_unlock(dev); -+ -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); -+ -+ return error; -+} -+ -+static int yaffs_setxattr(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ int result; -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ result = yaffs_set_xattrib(obj, name, value, size, flags); -+ if (result == YAFFS_OK) -+ error = 0; -+ else if (result < 0) -+ error = result; -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); -+ -+ return error; -+} -+ -+static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, -+ void *buff, size_t size) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_getxattr \"%s\" from object %d", -+ name, obj->obj_id); -+ -+ if (error == 0) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ error = yaffs_get_xattrib(obj, name, buff, size); -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); -+ -+ return error; -+} -+ -+static int yaffs_removexattr(struct dentry *dentry, const char *name) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_removexattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ int result; -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ result = yaffs_remove_xattrib(obj, name); -+ if (result == YAFFS_OK) -+ error = 0; -+ else if (result < 0) -+ error = result; -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_removexattr done returning %d", error); -+ -+ return error; -+} -+ -+static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_listxattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ error = yaffs_list_xattrib(obj, buff, size); -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_listxattr done returning %d", error); -+ -+ return error; -+} -+ -+ -+static const struct inode_operations yaffs_file_inode_operations = { -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+ -+static int yaffs_readlink(struct dentry *dentry, char __user * buffer, -+ int buflen) -+{ -+ unsigned char *alias; -+ int ret; -+ -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (!alias) -+ return -ENOMEM; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) -+ ret = vfs_readlink(dentry, buffer, buflen, alias); -+#else -+ ret = readlink_copy(buffer, buflen, alias); -+#endif -+ kfree(alias); -+ return ret; -+} -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ void *ret; -+#else -+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ int ret -+#endif -+ unsigned char *alias; -+ int ret_int = 0; -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); -+ yaffs_gross_unlock(dev); -+ -+ if (!alias) { -+ ret_int = -ENOMEM; -+ goto out; -+ } -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+ nd_set_link(nd, alias); -+ ret = alias; -+out: -+ if (ret_int) -+ ret = ERR_PTR(ret_int); -+ return ret; -+#else -+ ret = vfs_follow_link(nd, alias); -+ kfree(alias); -+out: -+ if (ret_int) -+ ret = ret_int; -+ return ret; -+#endif -+} -+ -+ -+#ifdef YAFFS_HAS_PUT_INODE -+ -+/* For now put inode is just for debugging -+ * Put inode is called when the inode **structure** is put. -+ */ -+static void yaffs_put_inode(struct inode *inode) -+{ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_put_inode: ino %d, count %d"), -+ (int)inode->i_ino, atomic_read(&inode->i_count); -+ -+} -+#endif -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) -+{ -+ kfree(alias); -+} -+#endif -+ -+static const struct inode_operations yaffs_symlink_inode_operations = { -+ .readlink = yaffs_readlink, -+ .follow_link = yaffs_follow_link, -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+ .put_link = yaffs_put_link, -+#endif -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+#ifdef YAFFS_USE_OWN_IGET -+ -+static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) -+{ -+ struct inode *inode; -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); -+ -+ inode = iget_locked(sb, ino); -+ if (!inode) -+ return ERR_PTR(-ENOMEM); -+ if (!(inode->i_state & I_NEW)) -+ return inode; -+ -+ /* NB This is called as a side effect of other functions, but -+ * we had to release the lock to prevent deadlocks, so -+ * need to lock again. -+ */ -+ -+ yaffs_gross_lock(dev); -+ -+ obj = yaffs_find_by_number(dev, inode->i_ino); -+ -+ yaffs_fill_inode_from_obj(inode, obj); -+ -+ yaffs_gross_unlock(dev); -+ -+ unlock_new_inode(inode); -+ return inode; -+} -+ -+#else -+ -+static void yaffs_read_inode(struct inode *inode) -+{ -+ /* NB This is called as a side effect of other functions, but -+ * we had to release the lock to prevent deadlocks, so -+ * need to lock again. -+ */ -+ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_inode for %d", (int)inode->i_ino); -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_lock(dev); -+ -+ obj = yaffs_find_by_number(dev, inode->i_ino); -+ -+ yaffs_fill_inode_from_obj(inode, obj); -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_unlock(dev); -+} -+ -+#endif -+ -+ -+ -+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, -+ struct yaffs_obj *obj) -+{ -+ struct inode *inode; -+ -+ if (!sb) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for NULL super_block!!"); -+ return NULL; -+ -+ } -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for NULL object!!"); -+ return NULL; -+ -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for object %d", obj->obj_id); -+ -+ inode = Y_IGET(sb, obj->obj_id); -+ if (IS_ERR(inode)) -+ return NULL; -+ -+ /* NB Side effect: iget calls back to yaffs_read_inode(). */ -+ /* iget also increments the inode's i_count */ -+ /* NB You can't be holding gross_lock or deadlock will happen! */ -+ -+ return inode; -+} -+ -+ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -+#define YCRED(x) x -+#else -+#define YCRED(x) (x->cred) -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) -+#define YPROC_uid(p) (YCRED(p)->fsuid) -+#define YPROC_gid(p) (YCRED(p)->fsgid) -+#define EXTRACT_gid(x) x -+#define EXTRACT_uid(x) x -+#define MAKE_gid(x) x -+#define MAKE_uid(x) x -+#else -+#define YPROC_uid(p) from_kuid(&init_user_ns, YCRED(p)->fsuid) -+#define YPROC_gid(p) from_kgid(&init_user_ns, YCRED(p)->fsgid) -+#define EXTRACT_gid(x) from_kgid(&init_user_ns, x) -+#define EXTRACT_uid(x) from_kuid(&init_user_ns, x) -+#define MAKE_gid(x) make_kgid(&init_user_ns, x) -+#define MAKE_uid(x) make_kuid(&init_user_ns, x) -+#endif -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t rdev) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, -+ dev_t rdev) -+#else -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, -+ int rdev) -+#endif -+{ -+ struct inode *inode; -+ -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_dev *dev; -+ -+ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); -+ -+ int error = -ENOSPC; -+ uid_t uid = YPROC_uid(current); -+ gid_t gid = -+ (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); -+ -+ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) -+ mode |= S_ISGID; -+ -+ if (parent) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: parent object %d type %d", -+ parent->obj_id, parent->variant_type); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: could not get parent object"); -+ return -EPERM; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: making oject for %s, mode %x dev %x", -+ dentry->d_name.name, mode, rdev); -+ -+ dev = parent->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ switch (mode & S_IFMT) { -+ default: -+ /* Special (socket, fifo, device...) */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ obj = -+ yaffs_create_special(parent, dentry->d_name.name, mode, uid, -+ gid, old_encode_dev(rdev)); -+#else -+ obj = -+ yaffs_create_special(parent, dentry->d_name.name, mode, uid, -+ gid, rdev); -+#endif -+ break; -+ case S_IFREG: /* file */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); -+ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, -+ gid); -+ break; -+ case S_IFDIR: /* directory */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); -+ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, -+ uid, gid); -+ break; -+ case S_IFLNK: /* symlink */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); -+ obj = NULL; /* Do we ever get here? */ -+ break; -+ } -+ -+ /* Can not call yaffs_get_inode() with gross lock held */ -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); -+ d_instantiate(dentry, inode); -+ update_dir_time(dir); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod created object %d count = %d", -+ obj->obj_id, atomic_read(&inode->i_count)); -+ error = 0; -+ yaffs_fill_inode_from_obj(dir, parent); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); -+ error = -ENOMEM; -+ } -+ -+ return error; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -+#else -+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) -+#endif -+{ -+ int ret_val; -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); -+ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); -+ return ret_val; -+} -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool dummy) -+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ struct nameidata *n) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, -+ struct nameidata *n) -+#else -+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) -+#endif -+{ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); -+ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, -+ unsigned int dummy) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, -+ struct nameidata *n) -+#else -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) -+#endif -+{ -+ struct yaffs_obj *obj; -+ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ -+ -+ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_lock(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", -+ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); -+ -+ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); -+ -+ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ -+ -+ /* Can't hold gross lock when calling yaffs_get_inode() */ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_lookup found %d", obj->obj_id); -+ -+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); -+ -+ } -+ -+/* added NCB for 2.5/6 compatability - forces add even if inode is -+ * NULL which creates dentry hash */ -+ d_add(dentry, inode); -+ -+ return NULL; -+} -+ -+/* -+ * Create a link... -+ */ -+static int yaffs_link(struct dentry *old_dentry, struct inode *dir, -+ struct dentry *dentry) -+{ -+ struct inode *inode = old_dentry->d_inode; -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_obj *link = NULL; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); -+ -+ obj = yaffs_inode_to_obj(inode); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ -+ link = -+ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, -+ obj); -+ -+ if (link) { -+ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); -+ d_instantiate(dentry, old_dentry->d_inode); -+ atomic_inc(&old_dentry->d_inode->i_count); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_link link count %d i_count %d", -+ old_dentry->d_inode->i_nlink, -+ atomic_read(&old_dentry->d_inode->i_count)); -+ } -+ -+ yaffs_gross_unlock(dev); -+ -+ if (link) { -+ update_dir_time(dir); -+ return 0; -+ } -+ -+ return -EPERM; -+} -+ -+static int yaffs_symlink(struct inode *dir, struct dentry *dentry, -+ const char *symname) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ uid_t uid = YPROC_uid(current); -+ gid_t gid = -+ (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); -+ -+ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > -+ YAFFS_MAX_NAME_LENGTH) -+ return -ENAMETOOLONG; -+ -+ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > -+ YAFFS_MAX_ALIAS_LENGTH) -+ return -ENAMETOOLONG; -+ -+ dev = yaffs_inode_to_obj(dir)->my_dev; -+ yaffs_gross_lock(dev); -+ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, -+ S_IFLNK | S_IRWXUGO, uid, gid, symname); -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ struct inode *inode; -+ -+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); -+ d_instantiate(dentry, inode); -+ update_dir_time(dir); -+ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); -+ return 0; -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); -+ } -+ -+ return -ENOMEM; -+} -+ -+/* -+ * The VFS layer already does all the dentry stuff for rename. -+ * -+ * NB: POSIX says you can rename an object over an old object of the same name -+ */ -+static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, -+ struct inode *new_dir, struct dentry *new_dentry) -+{ -+ struct yaffs_dev *dev; -+ int ret_val = YAFFS_FAIL; -+ struct yaffs_obj *target; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); -+ dev = yaffs_inode_to_obj(old_dir)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ /* Check if the target is an existing directory that is not empty. */ -+ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), -+ new_dentry->d_name.name); -+ -+ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && -+ !list_empty(&target->variant.dir_variant.children)) { -+ -+ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); -+ -+ ret_val = YAFFS_FAIL; -+ } else { -+ /* Now does unlinking internally using shadowing mechanism */ -+ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); -+ -+ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), -+ old_dentry->d_name.name, -+ yaffs_inode_to_obj(new_dir), -+ new_dentry->d_name.name); -+ } -+ yaffs_gross_unlock(dev); -+ -+ if (ret_val == YAFFS_OK) { -+ if (target) -+ inode_dec_link_count(new_dentry->d_inode); -+ -+ update_dir_time(old_dir); -+ if (old_dir != new_dir) -+ update_dir_time(new_dir); -+ return 0; -+ } else { -+ return -ENOTEMPTY; -+ } -+} -+ -+ -+ -+ -+static int yaffs_unlink(struct inode *dir, struct dentry *dentry) -+{ -+ int ret_val; -+ -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", -+ (int)(dir->i_ino), dentry->d_name.name); -+ obj = yaffs_inode_to_obj(dir); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ ret_val = yaffs_unlinker(obj, dentry->d_name.name); -+ -+ if (ret_val == YAFFS_OK) { -+ inode_dec_link_count(dentry->d_inode); -+ dir->i_version++; -+ yaffs_gross_unlock(dev); -+ update_dir_time(dir); -+ return 0; -+ } -+ yaffs_gross_unlock(dev); -+ return -ENOTEMPTY; -+} -+ -+ -+ -+static const struct inode_operations yaffs_dir_inode_operations = { -+ .create = yaffs_create, -+ .lookup = yaffs_lookup, -+ .link = yaffs_link, -+ .unlink = yaffs_unlink, -+ .symlink = yaffs_symlink, -+ .mkdir = yaffs_mkdir, -+ .rmdir = yaffs_unlink, -+ .mknod = yaffs_mknod, -+ .rename = yaffs_rename, -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+/*-----------------------------------------------------------------*/ -+/* Directory search context allows us to unlock access to yaffs during -+ * filldir without causing problems with the directory being modified. -+ * This is similar to the tried and tested mechanism used in yaffs direct. -+ * -+ * A search context iterates along a doubly linked list of siblings in the -+ * directory. If the iterating object is deleted then this would corrupt -+ * the list iteration, likely causing a crash. The search context avoids -+ * this by using the remove_obj_fn to move the search context to the -+ * next object before the object is deleted. -+ * -+ * Many readdirs (and thus seach conexts) may be alive simulateously so -+ * each struct yaffs_dev has a list of these. -+ * -+ * A seach context lives for the duration of a readdir. -+ * -+ * All these functions must be called while yaffs is locked. -+ */ -+ -+struct yaffs_search_context { -+ struct yaffs_dev *dev; -+ struct yaffs_obj *dir_obj; -+ struct yaffs_obj *next_return; -+ struct list_head others; -+}; -+ -+/* -+ * yaffs_new_search() creates a new search context, initialises it and -+ * adds it to the device's search context list. -+ * -+ * Called at start of readdir. -+ */ -+static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) -+{ -+ struct yaffs_dev *dev = dir->my_dev; -+ struct yaffs_search_context *sc = -+ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); -+ if (sc) { -+ sc->dir_obj = dir; -+ sc->dev = dev; -+ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) -+ sc->next_return = NULL; -+ else -+ sc->next_return = -+ list_entry(dir->variant.dir_variant.children.next, -+ struct yaffs_obj, siblings); -+ INIT_LIST_HEAD(&sc->others); -+ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); -+ } -+ return sc; -+} -+ -+/* -+ * yaffs_search_end() disposes of a search context and cleans up. -+ */ -+static void yaffs_search_end(struct yaffs_search_context *sc) -+{ -+ if (sc) { -+ list_del(&sc->others); -+ kfree(sc); -+ } -+} -+ -+/* -+ * yaffs_search_advance() moves a search context to the next object. -+ * Called when the search iterates or when an object removal causes -+ * the search context to be moved to the next object. -+ */ -+static void yaffs_search_advance(struct yaffs_search_context *sc) -+{ -+ if (!sc) -+ return; -+ -+ if (sc->next_return == NULL || -+ list_empty(&sc->dir_obj->variant.dir_variant.children)) -+ sc->next_return = NULL; -+ else { -+ struct list_head *next = sc->next_return->siblings.next; -+ -+ if (next == &sc->dir_obj->variant.dir_variant.children) -+ sc->next_return = NULL; /* end of list */ -+ else -+ sc->next_return = -+ list_entry(next, struct yaffs_obj, siblings); -+ } -+} -+ -+/* -+ * yaffs_remove_obj_callback() is called when an object is unlinked. -+ * We check open search contexts and advance any which are currently -+ * on the object being iterated. -+ */ -+static void yaffs_remove_obj_callback(struct yaffs_obj *obj) -+{ -+ -+ struct list_head *i; -+ struct yaffs_search_context *sc; -+ struct list_head *search_contexts = -+ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); -+ -+ /* Iterate through the directory search contexts. -+ * If any are currently on the object being removed, then advance -+ * the search context to the next object to prevent a hanging pointer. -+ */ -+ list_for_each(i, search_contexts) { -+ sc = list_entry(i, struct yaffs_search_context, others); -+ if (sc->next_return == obj) -+ yaffs_search_advance(sc); -+ } -+ -+} -+ -+ -+/*-----------------------------------------------------------------*/ -+ -+#ifdef YAFFS_USE_DIR_ITERATE -+static int yaffs_iterate(struct file *f, struct dir_context *dc) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ struct yaffs_search_context *sc; -+ unsigned long curoffs; -+ struct yaffs_obj *l; -+ int ret_val = 0; -+ -+ char name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_dev_to_lc(dev)->readdir_process = current; -+ -+ sc = yaffs_new_search(obj); -+ if (!sc) { -+ ret_val = -ENOMEM; -+ goto out; -+ } -+ -+ if (!dir_emit_dots(f, dc)) -+ return 0; -+ -+ curoffs = 1; -+ -+ while (sc->next_return) { -+ curoffs++; -+ l = sc->next_return; -+ if (curoffs >= dc->pos) { -+ int this_inode = yaffs_get_obj_inode(l); -+ int this_type = yaffs_get_obj_type(l); -+ -+ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: %s inode %d", -+ name, yaffs_get_obj_inode(l)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (!dir_emit(dc, -+ name, -+ strlen(name), -+ this_inode, -+ this_type)) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ -+ yaffs_gross_lock(dev); -+ -+ dc->pos++; -+ f->f_pos++; -+ } -+ yaffs_search_advance(sc); -+ } -+ -+out: -+ yaffs_search_end(sc); -+ yaffs_dev_to_lc(dev)->readdir_process = NULL; -+ yaffs_gross_unlock(dev); -+ -+ return ret_val; -+} -+ -+#else -+ -+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ struct yaffs_search_context *sc; -+ struct inode *inode = f->f_dentry->d_inode; -+ unsigned long offset, curoffs; -+ struct yaffs_obj *l; -+ int ret_val = 0; -+ -+ char name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_dev_to_lc(dev)->readdir_process = current; -+ -+ offset = f->f_pos; -+ -+ sc = yaffs_new_search(obj); -+ if (!sc) { -+ ret_val = -ENOMEM; -+ goto out; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: starting at %d", (int)offset); -+ -+ if (offset == 0) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry . ino %d", -+ (int)inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ f->f_pos++; -+ } -+ if (offset == 1) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry .. ino %d", -+ (int)f->f_dentry->d_parent->d_inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (filldir(dirent, "..", 2, offset, -+ f->f_dentry->d_parent->d_inode->i_ino, -+ DT_DIR) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ f->f_pos++; -+ } -+ -+ curoffs = 1; -+ -+ /* If the directory has changed since the open or last call to -+ readdir, rewind to after the 2 canned entries. */ -+ if (f->f_version != inode->i_version) { -+ offset = 2; -+ f->f_pos = offset; -+ f->f_version = inode->i_version; -+ } -+ -+ while (sc->next_return) { -+ curoffs++; -+ l = sc->next_return; -+ if (curoffs >= offset) { -+ int this_inode = yaffs_get_obj_inode(l); -+ int this_type = yaffs_get_obj_type(l); -+ -+ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: %s inode %d", -+ name, yaffs_get_obj_inode(l)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (filldir(dirent, -+ name, -+ strlen(name), -+ offset, this_inode, this_type) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ -+ yaffs_gross_lock(dev); -+ -+ offset++; -+ f->f_pos++; -+ } -+ yaffs_search_advance(sc); -+ } -+ -+out: -+ yaffs_search_end(sc); -+ yaffs_dev_to_lc(dev)->readdir_process = NULL; -+ yaffs_gross_unlock(dev); -+ -+ return ret_val; -+} -+ -+#endif -+ -+static const struct file_operations yaffs_dir_operations = { -+ .read = generic_read_dir, -+#ifdef YAFFS_USE_DIR_ITERATE -+ .iterate = yaffs_iterate, -+#else -+ .readdir = yaffs_readdir, -+#endif -+ .fsync = yaffs_sync_object, -+ .llseek = generic_file_llseek, -+}; -+ -+static void yaffs_fill_inode_from_obj(struct inode *inode, -+ struct yaffs_obj *obj) -+{ -+ if (inode && obj) { -+ -+ /* Check mode against the variant type and attempt to repair if broken. */ -+ u32 mode = obj->yst_mode; -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (!S_ISREG(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFREG; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ if (!S_ISLNK(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFLNK; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ if (!S_ISDIR(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFDIR; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ default: -+ /* TODO? */ -+ break; -+ } -+ -+ inode->i_flags |= S_NOATIME; -+ -+ inode->i_ino = obj->obj_id; -+ inode->i_mode = obj->yst_mode; -+ inode->i_uid = MAKE_uid(obj->yst_uid); -+ inode->i_gid = MAKE_gid(obj->yst_gid); -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+ inode->i_blksize = inode->i_sb->s_blocksize; -+#endif -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ -+ inode->i_rdev = old_decode_dev(obj->yst_rdev); -+ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); -+ inode->i_atime.tv_nsec = 0; -+ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; -+ inode->i_mtime.tv_nsec = 0; -+ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; -+ inode->i_ctime.tv_nsec = 0; -+#else -+ inode->i_rdev = obj->yst_rdev; -+ inode->i_atime = obj->yst_atime; -+ inode->i_mtime = obj->yst_mtime; -+ inode->i_ctime = obj->yst_ctime; -+#endif -+ inode->i_size = yaffs_get_obj_length(obj); -+ inode->i_blocks = (inode->i_size + 511) >> 9; -+ -+ set_nlink(inode, yaffs_get_obj_link_count(obj)); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", -+ inode->i_mode, obj->yst_uid, obj->yst_gid, -+ inode->i_size, atomic_read(&inode->i_count)); -+ -+ switch (obj->yst_mode & S_IFMT) { -+ default: /* fifo, device or socket */ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ init_special_inode(inode, obj->yst_mode, -+ old_decode_dev(obj->yst_rdev)); -+#else -+ init_special_inode(inode, obj->yst_mode, -+ (dev_t) (obj->yst_rdev)); -+#endif -+ break; -+ case S_IFREG: /* file */ -+ inode->i_op = &yaffs_file_inode_operations; -+ inode->i_fop = &yaffs_file_operations; -+ inode->i_mapping->a_ops = -+ &yaffs_file_address_operations; -+ break; -+ case S_IFDIR: /* directory */ -+ inode->i_op = &yaffs_dir_inode_operations; -+ inode->i_fop = &yaffs_dir_operations; -+ break; -+ case S_IFLNK: /* symlink */ -+ inode->i_op = &yaffs_symlink_inode_operations; -+ break; -+ } -+ -+ yaffs_inode_to_obj_lv(inode) = obj; -+ -+ obj->my_inode = inode; -+ -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_fill_inode invalid parameters"); -+ } -+ -+} -+ -+ -+ -+/* -+ * yaffs background thread functions . -+ * yaffs_bg_thread_fn() the thread function -+ * yaffs_bg_start() launches the background thread. -+ * yaffs_bg_stop() cleans up the background thread. -+ * -+ * NB: -+ * The thread should only run after the yaffs is initialised -+ * The thread should be stopped before yaffs is unmounted. -+ * The thread should not do any writing while the fs is in read only. -+ */ -+ -+static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) -+{ -+ unsigned erased_chunks = -+ dev->n_erased_blocks * dev->param.chunks_per_block; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ unsigned scattered = 0; /* Free chunks not in an erased block */ -+ -+ if (erased_chunks < dev->n_free_chunks) -+ scattered = (dev->n_free_chunks - erased_chunks); -+ -+ if (!context->bg_running) -+ return 0; -+ else if (scattered < (dev->param.chunks_per_block * 2)) -+ return 0; -+ else if (erased_chunks > dev->n_free_chunks / 2) -+ return 0; -+ else if (erased_chunks > dev->n_free_chunks / 4) -+ return 1; -+ else -+ return 2; -+} -+ -+#ifdef YAFFS_COMPILE_BACKGROUND -+ -+void yaffs_background_waker(unsigned long data) -+{ -+ wake_up_process((struct task_struct *)data); -+} -+ -+static int yaffs_bg_thread_fn(void *data) -+{ -+ struct yaffs_dev *dev = (struct yaffs_dev *)data; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ unsigned long now = jiffies; -+ unsigned long next_dir_update = now; -+ unsigned long next_gc = now; -+ unsigned long expires; -+ unsigned int urgency; -+ -+ int gc_result; -+ struct timer_list timer; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "yaffs_background starting for dev %p", (void *)dev); -+ -+#ifdef YAFFS_COMPILE_FREEZER -+ set_freezable(); -+#endif -+ while (context->bg_running) { -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); -+ -+ if (kthread_should_stop()) -+ break; -+ -+#ifdef YAFFS_COMPILE_FREEZER -+ if (try_to_freeze()) -+ continue; -+#endif -+ yaffs_gross_lock(dev); -+ -+ now = jiffies; -+ -+ if (time_after(now, next_dir_update) && yaffs_bg_enable) { -+ yaffs_update_dirty_dirs(dev); -+ next_dir_update = now + HZ; -+ } -+ -+ if (time_after(now, next_gc) && yaffs_bg_enable) { -+ if (!dev->is_checkpointed) { -+ urgency = yaffs_bg_gc_urgency(dev); -+ gc_result = yaffs_bg_gc(dev, urgency); -+ if (urgency > 1) -+ next_gc = now + HZ / 20 + 1; -+ else if (urgency > 0) -+ next_gc = now + HZ / 10 + 1; -+ else -+ next_gc = now + HZ * 2; -+ } else { -+ /* -+ * gc not running so set to next_dir_update -+ * to cut down on wake ups -+ */ -+ next_gc = next_dir_update; -+ } -+ } -+ yaffs_gross_unlock(dev); -+#if 1 -+ expires = next_dir_update; -+ if (time_before(next_gc, expires)) -+ expires = next_gc; -+ if (time_before(expires, now)) -+ expires = now + HZ; -+ -+ Y_INIT_TIMER(&timer); -+ timer.expires = expires + 1; -+ timer.data = (unsigned long)current; -+ timer.function = yaffs_background_waker; -+ -+ set_current_state(TASK_INTERRUPTIBLE); -+ add_timer(&timer); -+ schedule(); -+ del_timer_sync(&timer); -+#else -+ msleep(10); -+#endif -+ } -+ -+ return 0; -+} -+ -+static int yaffs_bg_start(struct yaffs_dev *dev) -+{ -+ int retval = 0; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ -+ if (dev->read_only) -+ return -1; -+ -+ context->bg_running = 1; -+ -+ context->bg_thread = kthread_run(yaffs_bg_thread_fn, -+ (void *)dev, "yaffs-bg-%d", -+ context->mount_id); -+ -+ if (IS_ERR(context->bg_thread)) { -+ retval = PTR_ERR(context->bg_thread); -+ context->bg_thread = NULL; -+ context->bg_running = 0; -+ } -+ return retval; -+} -+ -+static void yaffs_bg_stop(struct yaffs_dev *dev) -+{ -+ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); -+ -+ ctxt->bg_running = 0; -+ -+ if (ctxt->bg_thread) { -+ kthread_stop(ctxt->bg_thread); -+ ctxt->bg_thread = NULL; -+ } -+} -+#else -+static int yaffs_bg_thread_fn(void *data) -+{ -+ return 0; -+} -+ -+static int yaffs_bg_start(struct yaffs_dev *dev) -+{ -+ return 0; -+} -+ -+static void yaffs_bg_stop(struct yaffs_dev *dev) -+{ -+} -+#endif -+ -+ -+static void yaffs_flush_inodes(struct super_block *sb) -+{ -+ struct inode *iptr; -+ struct yaffs_obj *obj; -+ -+ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { -+ obj = yaffs_inode_to_obj(iptr); -+ if (obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "flushing obj %d", -+ obj->obj_id); -+ yaffs_flush_file(obj, 1, 0, 0); -+ } -+ } -+} -+ -+static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ if (!dev) -+ return; -+ -+ yaffs_flush_inodes(sb); -+ yaffs_update_dirty_dirs(dev); -+ yaffs_flush_whole_cache(dev, 1); -+ if (do_checkpoint) -+ yaffs_checkpoint_save(dev); -+} -+ -+static LIST_HEAD(yaffs_context_list); -+struct mutex yaffs_context_lock; -+ -+static void yaffs_put_super(struct super_block *sb) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, -+ "yaffs_put_super"); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, -+ "Shutting down yaffs background thread"); -+ yaffs_bg_stop(dev); -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, -+ "yaffs background thread shut down"); -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_flush_super(sb, 1); -+ -+ yaffs_deinitialise(dev); -+ -+ yaffs_gross_unlock(dev); -+ -+ mutex_lock(&yaffs_context_lock); -+ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); -+ mutex_unlock(&yaffs_context_lock); -+ -+ if (yaffs_dev_to_lc(dev)->spare_buffer) { -+ kfree(yaffs_dev_to_lc(dev)->spare_buffer); -+ yaffs_dev_to_lc(dev)->spare_buffer = NULL; -+ } -+ -+ kfree(dev); -+ -+ yaffs_put_mtd_device(mtd); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, -+ "yaffs_put_super done"); -+} -+ -+ -+static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) -+{ -+ return yaffs_gc_control; -+} -+ -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+ -+static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, -+ uint32_t generation) -+{ -+ return Y_IGET(sb, ino); -+} -+ -+static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, -+ struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, -+ yaffs2_nfs_get_inode); -+} -+ -+static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, -+ struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ return generic_fh_to_parent(sb, fid, fh_len, fh_type, -+ yaffs2_nfs_get_inode); -+} -+ -+struct dentry *yaffs2_get_parent(struct dentry *dentry) -+{ -+ -+ struct super_block *sb = dentry->d_inode->i_sb; -+ struct dentry *parent = ERR_PTR(-ENOENT); -+ struct inode *inode; -+ unsigned long parent_ino; -+ struct yaffs_obj *d_obj; -+ struct yaffs_obj *parent_obj; -+ -+ d_obj = yaffs_inode_to_obj(dentry->d_inode); -+ -+ if (d_obj) { -+ parent_obj = d_obj->parent; -+ if (parent_obj) { -+ parent_ino = yaffs_get_obj_inode(parent_obj); -+ inode = Y_IGET(sb, parent_ino); -+ -+ if (IS_ERR(inode)) { -+ parent = ERR_CAST(inode); -+ } else { -+ parent = d_obtain_alias(inode); -+ if (!IS_ERR(parent)) { -+ parent = ERR_PTR(-ENOMEM); -+ iput(inode); -+ } -+ } -+ } -+ } -+ -+ return parent; -+} -+ -+/* Just declare a zero structure as a NULL value implies -+ * using the default functions of exportfs. -+ */ -+ -+static struct export_operations yaffs_export_ops = { -+ .fh_to_dentry = yaffs2_fh_to_dentry, -+ .fh_to_parent = yaffs2_fh_to_parent, -+ .get_parent = yaffs2_get_parent, -+}; -+ -+#endif -+ -+static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) -+{ -+ /* Clear the association between the inode and -+ * the struct yaffs_obj. -+ */ -+ obj->my_inode = NULL; -+ yaffs_inode_to_obj_lv(inode) = NULL; -+ -+ /* If the object freeing was deferred, then the real -+ * free happens now. -+ * This should fix the inode inconsistency problem. -+ */ -+ yaffs_handle_defered_free(obj); -+} -+ -+#ifdef YAFFS_HAS_EVICT_INODE -+/* yaffs_evict_inode combines into one operation what was previously done in -+ * yaffs_clear_inode() and yaffs_delete_inode() -+ * -+ */ -+static void yaffs_evict_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ int deleteme = 0; -+ -+ obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_evict_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (!inode->i_nlink && !is_bad_inode(inode)) -+ deleteme = 1; -+ truncate_inode_pages(&inode->i_data, 0); -+ Y_CLEAR_INODE(inode); -+ -+ if (deleteme && obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_del_obj(obj); -+ yaffs_gross_unlock(dev); -+ } -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_unstitch_obj(inode, obj); -+ yaffs_gross_unlock(dev); -+ } -+} -+#else -+ -+/* clear is called to tell the fs to release any per-inode data it holds. -+ * The object might still exist on disk and is just being thrown out of the cache -+ * or else the object has actually been deleted and we're being called via -+ * the chain -+ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() -+ */ -+ -+static void yaffs_clear_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_clear_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_unstitch_obj(inode, obj); -+ yaffs_gross_unlock(dev); -+ } -+ -+} -+ -+/* delete is called when the link count is zero and the inode -+ * is put (ie. nobody wants to know about it anymore, time to -+ * delete the file). -+ * NB Must call clear_inode() -+ */ -+static void yaffs_delete_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_delete_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_del_obj(obj); -+ yaffs_gross_unlock(dev); -+ } -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) -+ truncate_inode_pages(&inode->i_data, 0); -+#endif -+ clear_inode(inode); -+} -+#endif -+ -+ -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ struct super_block *sb = dentry->d_sb; -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+#else -+static int yaffs_statfs(struct super_block *sb, struct statfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+#endif -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); -+ -+ yaffs_gross_lock(dev); -+ -+ buf->f_type = YAFFS_MAGIC; -+ buf->f_bsize = sb->s_blocksize; -+ buf->f_namelen = 255; -+ -+ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { -+ /* Do this if chunk size is not a power of 2 */ -+ -+ uint64_t bytes_in_dev; -+ uint64_t bytes_free; -+ -+ bytes_in_dev = -+ ((uint64_t) -+ ((dev->param.end_block - dev->param.start_block + -+ 1))) * ((uint64_t) (dev->param.chunks_per_block * -+ dev->data_bytes_per_chunk)); -+ -+ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ -+ buf->f_blocks = bytes_in_dev; -+ -+ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * -+ ((uint64_t) (dev->data_bytes_per_chunk)); -+ -+ do_div(bytes_free, sb->s_blocksize); -+ -+ buf->f_bfree = bytes_free; -+ -+ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { -+ -+ buf->f_blocks = -+ (dev->param.end_block - dev->param.start_block + 1) * -+ dev->param.chunks_per_block / -+ (sb->s_blocksize / dev->data_bytes_per_chunk); -+ buf->f_bfree = -+ yaffs_get_n_free_chunks(dev) / -+ (sb->s_blocksize / dev->data_bytes_per_chunk); -+ } else { -+ buf->f_blocks = -+ (dev->param.end_block - dev->param.start_block + 1) * -+ dev->param.chunks_per_block * -+ (dev->data_bytes_per_chunk / sb->s_blocksize); -+ -+ buf->f_bfree = -+ yaffs_get_n_free_chunks(dev) * -+ (dev->data_bytes_per_chunk / sb->s_blocksize); -+ } -+ -+ buf->f_files = 0; -+ buf->f_ffree = 0; -+ buf->f_bavail = buf->f_bfree; -+ -+ yaffs_gross_unlock(dev); -+ return 0; -+} -+ -+ -+ -+static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) -+{ -+ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); -+ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); -+ int do_checkpoint; -+ int dirty = yaffs_check_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, -+ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", -+ gc_urgent, -+ dirty ? "dirty" : "clean", -+ request_checkpoint ? "checkpoint requested" : "no checkpoint", -+ oneshot_checkpoint ? " one-shot" : ""); -+ -+ yaffs_gross_lock(dev); -+ do_checkpoint = ((request_checkpoint && !gc_urgent) || -+ oneshot_checkpoint) && !dev->is_checkpointed; -+ -+ if (dirty || do_checkpoint) { -+ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); -+ yaffs_clear_super_dirty(dev); -+ if (oneshot_checkpoint) -+ yaffs_auto_checkpoint &= ~4; -+ } -+ yaffs_gross_unlock(dev); -+ -+ return 0; -+} -+ -+ -+#ifdef YAFFS_HAS_WRITE_SUPER -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static void yaffs_write_super(struct super_block *sb) -+#else -+static int yaffs_write_super(struct super_block *sb) -+#endif -+{ -+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, -+ "yaffs_write_super %s", -+ request_checkpoint ? " checkpt" : ""); -+ -+ yaffs_do_sync_fs(sb, request_checkpoint); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) -+ return 0; -+#endif -+} -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_sync_fs(struct super_block *sb, int wait) -+#else -+static int yaffs_sync_fs(struct super_block *sb) -+#endif -+{ -+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, -+ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); -+ -+ yaffs_do_sync_fs(sb, request_checkpoint); -+ -+ return 0; -+} -+ -+/* the function only is used to change dev->read_only when this file system -+ * is remounted. -+ */ -+static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) -+{ -+ int read_only = 0; -+ struct mtd_info *mtd; -+ struct yaffs_dev *dev = 0; -+ -+ /* Get the device */ -+ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); -+ if (!mtd) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device #%u doesn't appear to exist", -+ MINOR(sb->s_dev)); -+ return 1; -+ } -+ -+ /* Check it's NAND */ -+ if (mtd->type != MTD_NANDFLASH) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device is not NAND it's type %d", -+ mtd->type); -+ return 1; -+ } -+ -+ read_only = ((*flags & MS_RDONLY) != 0); -+ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { -+ read_only = 1; -+ printk(KERN_INFO -+ "yaffs: mtd is read only, setting superblock read only"); -+ *flags |= MS_RDONLY; -+ } -+ -+ dev = sb->s_fs_info; -+ dev->read_only = read_only; -+ -+ return 0; -+} -+ -+static const struct super_operations yaffs_super_ops = { -+ .statfs = yaffs_statfs, -+ -+#ifndef YAFFS_USE_OWN_IGET -+ .read_inode = yaffs_read_inode, -+#endif -+#ifdef YAFFS_HAS_PUT_INODE -+ .put_inode = yaffs_put_inode, -+#endif -+ .put_super = yaffs_put_super, -+#ifdef YAFFS_HAS_EVICT_INODE -+ .evict_inode = yaffs_evict_inode, -+#else -+ .delete_inode = yaffs_delete_inode, -+ .clear_inode = yaffs_clear_inode, -+#endif -+ .sync_fs = yaffs_sync_fs, -+#ifdef YAFFS_HAS_WRITE_SUPER -+ .write_super = yaffs_write_super, -+#endif -+ .remount_fs = yaffs_remount_fs, -+}; -+ -+struct yaffs_options { -+ int inband_tags; -+ int skip_checkpoint_read; -+ int skip_checkpoint_write; -+ int no_cache; -+ int tags_ecc_on; -+ int tags_ecc_overridden; -+ int lazy_loading_enabled; -+ int lazy_loading_overridden; -+ int empty_lost_and_found; -+ int empty_lost_and_found_overridden; -+ int disable_summary; -+}; -+ -+#define MAX_OPT_LEN 30 -+static int yaffs_parse_options(struct yaffs_options *options, -+ const char *options_str) -+{ -+ char cur_opt[MAX_OPT_LEN + 1]; -+ int p; -+ int error = 0; -+ -+ /* Parse through the options which is a comma seperated list */ -+ -+ while (options_str && *options_str && !error) { -+ memset(cur_opt, 0, MAX_OPT_LEN + 1); -+ p = 0; -+ -+ while (*options_str == ',') -+ options_str++; -+ -+ while (*options_str && *options_str != ',') { -+ if (p < MAX_OPT_LEN) { -+ cur_opt[p] = *options_str; -+ p++; -+ } -+ options_str++; -+ } -+ -+ if (!strcmp(cur_opt, "inband-tags")) { -+ options->inband_tags = 1; -+ } else if (!strcmp(cur_opt, "tags-ecc-off")) { -+ options->tags_ecc_on = 0; -+ options->tags_ecc_overridden = 1; -+ } else if (!strcmp(cur_opt, "tags-ecc-on")) { -+ options->tags_ecc_on = 1; -+ options->tags_ecc_overridden = 1; -+ } else if (!strcmp(cur_opt, "lazy-loading-off")) { -+ options->lazy_loading_enabled = 0; -+ options->lazy_loading_overridden = 1; -+ } else if (!strcmp(cur_opt, "lazy-loading-on")) { -+ options->lazy_loading_enabled = 1; -+ options->lazy_loading_overridden = 1; -+ } else if (!strcmp(cur_opt, "disable-summary")) { -+ options->disable_summary = 1; -+ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { -+ options->empty_lost_and_found = 0; -+ options->empty_lost_and_found_overridden = 1; -+ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { -+ options->empty_lost_and_found = 1; -+ options->empty_lost_and_found_overridden = 1; -+ } else if (!strcmp(cur_opt, "no-cache")) { -+ options->no_cache = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { -+ options->skip_checkpoint_read = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { -+ options->skip_checkpoint_write = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint")) { -+ options->skip_checkpoint_read = 1; -+ options->skip_checkpoint_write = 1; -+ } else { -+ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", -+ cur_opt); -+ error = 1; -+ } -+ } -+ -+ return error; -+} -+ -+ -+static struct dentry *yaffs_make_root(struct inode *inode) -+{ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) -+ struct dentry *root = d_alloc_root(inode); -+ -+ if (!root) -+ iput(inode); -+ -+ return root; -+#else -+ return d_make_root(inode); -+#endif -+} -+ -+ -+ -+ -+static struct super_block *yaffs_internal_read_super(int yaffs_version, -+ struct super_block *sb, -+ void *data, int silent) -+{ -+ int n_blocks; -+ struct inode *inode = NULL; -+ struct dentry *root; -+ struct yaffs_dev *dev = 0; -+ char devname_buf[BDEVNAME_SIZE + 1]; -+ struct mtd_info *mtd; -+ int err; -+ char *data_str = (char *)data; -+ struct yaffs_linux_context *context = NULL; -+ struct yaffs_param *param; -+ -+ int read_only = 0; -+ int inband_tags = 0; -+ -+ struct yaffs_options options; -+ -+ unsigned mount_id; -+ int found; -+ struct yaffs_linux_context *context_iterator; -+ struct list_head *l; -+ -+ if (!sb) { -+ printk(KERN_INFO "yaffs: sb is NULL\n"); -+ return NULL; -+ } -+ -+ sb->s_magic = YAFFS_MAGIC; -+ sb->s_op = &yaffs_super_ops; -+ sb->s_flags |= MS_NOATIME; -+ -+ read_only = ((sb->s_flags & MS_RDONLY) != 0); -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+ sb->s_export_op = &yaffs_export_ops; -+#endif -+ -+ if (!sb->s_dev) -+ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); -+ else if (!yaffs_devname(sb, devname_buf)) -+ printk(KERN_INFO "yaffs: devname is NULL\n"); -+ else -+ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", -+ sb->s_dev, -+ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); -+ -+ if (!data_str) -+ data_str = ""; -+ -+ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); -+ -+ memset(&options, 0, sizeof(options)); -+ -+ if (yaffs_parse_options(&options, data_str)) { -+ /* Option parsing failed */ -+ return NULL; -+ } -+ -+ sb->s_blocksize = PAGE_CACHE_SIZE; -+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: Using yaffs%d", yaffs_version); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", -+ MAJOR(sb->s_dev), MINOR(sb->s_dev), -+ yaffs_devname(sb, devname_buf)); -+ -+ /* Get the device */ -+ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); -+ if (IS_ERR(mtd)) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: MTD device %u either not valid or unavailable", -+ MINOR(sb->s_dev)); -+ return NULL; -+ } -+ -+ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); -+ yaffs_version = 2; -+ } -+ -+ /* Added NCB 26/5/2006 for completeness */ -+ if (yaffs_version == 2 && !options.inband_tags -+ && WRITE_SIZE(mtd) == 512) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); -+ yaffs_version = 1; -+ } -+ -+ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || -+ options.inband_tags) -+ inband_tags = 1; -+ -+ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) -+ return NULL; -+ -+ /* OK, so if we got here, we have an MTD that's NAND and looks -+ * like it has the right capabilities -+ * Set the struct yaffs_dev up for mtd -+ */ -+ -+ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { -+ read_only = 1; -+ printk(KERN_INFO -+ "yaffs: mtd is read only, setting superblock read only\n" -+ ); -+ sb->s_flags |= MS_RDONLY; -+ } -+ -+ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); -+ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); -+ -+ if (!dev || !context) { -+ kfree(dev); -+ kfree(context); -+ dev = NULL; -+ context = NULL; -+ -+ /* Deep shit could not allocate device structure */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." -+ ); -+ return NULL; -+ } -+ memset(dev, 0, sizeof(struct yaffs_dev)); -+ param = &(dev->param); -+ -+ memset(context, 0, sizeof(struct yaffs_linux_context)); -+ dev->os_context = context; -+ INIT_LIST_HEAD(&(context->context_list)); -+ context->dev = dev; -+ context->super = sb; -+ -+ dev->read_only = read_only; -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ sb->s_fs_info = dev; -+#else -+ sb->u.generic_sbp = dev; -+#endif -+ -+ -+ dev->driver_context = mtd; -+ param->name = mtd->name; -+ -+ /* Set up the memory size parameters.... */ -+ -+ -+ param->n_reserved_blocks = 5; -+ param->n_caches = (options.no_cache) ? 0 : 10; -+ param->inband_tags = inband_tags; -+ -+ param->enable_xattr = 1; -+ if (options.lazy_loading_overridden) -+ param->disable_lazy_load = !options.lazy_loading_enabled; -+ -+ param->defered_dir_update = 1; -+ -+ if (options.tags_ecc_overridden) -+ param->no_tags_ecc = !options.tags_ecc_on; -+ -+ param->empty_lost_n_found = 1; -+ param->refresh_period = 500; -+ param->disable_summary = options.disable_summary; -+ -+ -+#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING -+ param->disable_bad_block_marking = 1; -+#endif -+ if (options.empty_lost_and_found_overridden) -+ param->empty_lost_n_found = options.empty_lost_and_found; -+ -+ /* ... and the functions. */ -+ if (yaffs_version == 2) { -+ param->is_yaffs2 = 1; -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+ param->total_bytes_per_chunk = mtd->writesize; -+ param->chunks_per_block = mtd->erasesize / mtd->writesize; -+#else -+ param->total_bytes_per_chunk = mtd->oobblock; -+ param->chunks_per_block = mtd->erasesize / mtd->oobblock; -+#endif -+ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); -+ -+ param->start_block = 0; -+ param->end_block = n_blocks - 1; -+ } else { -+ param->is_yaffs2 = 0; -+ n_blocks = YCALCBLOCKS(mtd->size, -+ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); -+ -+ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; -+ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; -+ } -+ -+ param->start_block = 0; -+ param->end_block = n_blocks - 1; -+ -+ yaffs_mtd_drv_install(dev); -+ -+ param->sb_dirty_fn = yaffs_set_super_dirty; -+ param->gc_control_fn = yaffs_gc_control_callback; -+ -+ yaffs_dev_to_lc(dev)->super = sb; -+ -+ param->use_nand_ecc = 1; -+ -+ param->skip_checkpt_rd = options.skip_checkpoint_read; -+ param->skip_checkpt_wr = options.skip_checkpoint_write; -+ -+ mutex_lock(&yaffs_context_lock); -+ /* Get a mount id */ -+ found = 0; -+ for (mount_id = 0; !found; mount_id++) { -+ found = 1; -+ list_for_each(l, &yaffs_context_list) { -+ context_iterator = -+ list_entry(l, struct yaffs_linux_context, -+ context_list); -+ if (context_iterator->mount_id == mount_id) -+ found = 0; -+ } -+ } -+ context->mount_id = mount_id; -+ -+ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), -+ &yaffs_context_list); -+ mutex_unlock(&yaffs_context_lock); -+ -+ /* Directory search handling... */ -+ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); -+ param->remove_obj_fn = yaffs_remove_obj_callback; -+ -+ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); -+ -+ yaffs_gross_lock(dev); -+ -+ err = yaffs_guts_initialise(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: guts initialised %s", -+ (err == YAFFS_OK) ? "OK" : "FAILED"); -+ -+ if (err == YAFFS_OK) -+ yaffs_bg_start(dev); -+ -+ if (!context->bg_thread) -+ param->defered_dir_update = 0; -+ -+ sb->s_maxbytes = yaffs_max_file_size(dev); -+ -+ /* Release lock before yaffs_get_inode() */ -+ yaffs_gross_unlock(dev); -+ -+ /* Create root inode */ -+ if (err == YAFFS_OK) -+ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); -+ -+ if (!inode) -+ return NULL; -+ -+ inode->i_op = &yaffs_dir_inode_operations; -+ inode->i_fop = &yaffs_dir_operations; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); -+ -+ root = yaffs_make_root(inode); -+ -+ if (!root) -+ return NULL; -+ -+ sb->s_root = root; -+ if(!dev->is_checkpointed) -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs_read_super: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); -+ return sb; -+} -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data) -+{ -+ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); -+} -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data, struct vfsmount *mnt) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs_internal_read_super_mtd, mnt); -+} -+#else -+static struct super_block *yaffs_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs_internal_read_super_mtd); -+} -+#endif -+ -+static struct file_system_type yaffs_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "yaffs", -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+ .mount = yaffs_mount, -+#else -+ .get_sb = yaffs_read_super, -+#endif -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV, -+}; -+#else -+static struct super_block *yaffs_read_super(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(1, sb, data, silent); -+} -+ -+static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, -+ FS_REQUIRES_DEV); -+#endif -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data) -+{ -+ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); -+} -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs2_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, void *data, -+ struct vfsmount *mnt) -+{ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs2_internal_read_super_mtd, mnt); -+} -+#else -+static struct super_block *yaffs2_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs2_internal_read_super_mtd); -+} -+#endif -+ -+static struct file_system_type yaffs2_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "yaffs2", -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+ .mount = yaffs2_mount, -+#else -+ .get_sb = yaffs2_read_super, -+#endif -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV, -+}; -+#else -+static struct super_block *yaffs2_read_super(struct super_block *sb, -+ void *data, int silent) -+{ -+ return yaffs_internal_read_super(2, sb, data, silent); -+} -+ -+static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, -+ FS_REQUIRES_DEV); -+#endif -+ -+ -+static struct proc_dir_entry *my_proc_entry; -+ -+static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) -+{ -+ struct yaffs_param *param = &dev->param; -+ int bs[10]; -+ -+ yaffs_count_blocks_by_state(dev,bs); -+ -+ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); -+ buf += sprintf(buf, "end_block............ %d\n", param->end_block); -+ buf += sprintf(buf, "total_bytes_per_chunk %d\n", -+ param->total_bytes_per_chunk); -+ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); -+ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); -+ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); -+ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); -+ buf += sprintf(buf, "empty_lost_n_found... %d\n", -+ param->empty_lost_n_found); -+ buf += sprintf(buf, "disable_lazy_load.... %d\n", -+ param->disable_lazy_load); -+ buf += sprintf(buf, "disable_bad_block_mrk %d\n", -+ param->disable_bad_block_marking); -+ buf += sprintf(buf, "refresh_period....... %d\n", -+ param->refresh_period); -+ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); -+ buf += sprintf(buf, "n_reserved_blocks.... %d\n", -+ param->n_reserved_blocks); -+ buf += sprintf(buf, "always_check_erased.. %d\n", -+ param->always_check_erased); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "block count by state\n"); -+ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", -+ bs[0], bs[1], bs[2], bs[3], bs[4]); -+ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", -+ bs[5], bs[6], bs[7], bs[8], bs[9]); -+ -+ return buf; -+} -+ -+static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) -+{ -+ buf += sprintf(buf, "max file size....... %lld\n", -+ (long long) yaffs_max_file_size(dev)); -+ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", -+ dev->data_bytes_per_chunk); -+ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); -+ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); -+ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); -+ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", -+ dev->blocks_in_checkpt); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); -+ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); -+ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); -+ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); -+ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); -+ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); -+ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); -+ buf += sprintf(buf, "passive_gc_count..... %u\n", -+ dev->passive_gc_count); -+ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", -+ dev->oldest_dirty_gc_count); -+ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); -+ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); -+ buf += sprintf(buf, "n_retried_writes..... %u\n", -+ dev->n_retried_writes); -+ buf += sprintf(buf, "n_retired_blocks..... %u\n", -+ dev->n_retired_blocks); -+ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); -+ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); -+ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", -+ dev->n_tags_ecc_fixed); -+ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", -+ dev->n_tags_ecc_unfixed); -+ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); -+ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); -+ buf += sprintf(buf, "n_unlinked_files..... %u\n", -+ dev->n_unlinked_files); -+ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); -+ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); -+ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); -+ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); -+ -+ return buf; -+} -+ -+static int yaffs_proc_read(char *page, -+ char **start, -+ off_t offset, int count, int *eof, void *data) -+{ -+ struct list_head *item; -+ char *buf = page; -+ int step = offset; -+ int n = 0; -+ -+ /* Get proc_file_read() to step 'offset' by one on each sucessive call. -+ * We use 'offset' (*ppos) to indicate where we are in dev_list. -+ * This also assumes the user has posted a read buffer large -+ * enough to hold the complete output; but that's life in /proc. -+ */ -+ -+ *(int *)start = 1; -+ -+ /* Print header first */ -+ if (step == 0) -+ buf += -+ sprintf(buf, -+ "Multi-version YAFFS built\n"); -+ else if (step == 1) -+ buf += sprintf(buf, "\n"); -+ else { -+ step -= 2; -+ -+ mutex_lock(&yaffs_context_lock); -+ -+ /* Locate and print the Nth entry. Order N-squared but N is small. */ -+ list_for_each(item, &yaffs_context_list) { -+ struct yaffs_linux_context *dc = -+ list_entry(item, struct yaffs_linux_context, -+ context_list); -+ struct yaffs_dev *dev = dc->dev; -+ -+ if (n < (step & ~1)) { -+ n += 2; -+ continue; -+ } -+ if ((step & 1) == 0) { -+ buf += -+ sprintf(buf, "\nDevice %d \"%s\"\n", n, -+ dev->param.name); -+ buf = yaffs_dump_dev_part0(buf, dev); -+ } else { -+ buf = yaffs_dump_dev_part1(buf, dev); -+ } -+ -+ break; -+ } -+ mutex_unlock(&yaffs_context_lock); -+ } -+ -+ return buf - page < count ? buf - page : count; -+} -+ -+/** -+ * Set the verbosity of the warnings and error messages. -+ * -+ * Note that the names can only be a..z or _ with the current code. -+ */ -+ -+static struct { -+ char *mask_name; -+ unsigned mask_bitfield; -+} mask_flags[] = { -+ {"allocate", YAFFS_TRACE_ALLOCATE}, -+ {"always", YAFFS_TRACE_ALWAYS}, -+ {"background", YAFFS_TRACE_BACKGROUND}, -+ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, -+ {"buffers", YAFFS_TRACE_BUFFERS}, -+ {"bug", YAFFS_TRACE_BUG}, -+ {"checkpt", YAFFS_TRACE_CHECKPOINT}, -+ {"deletion", YAFFS_TRACE_DELETION}, -+ {"erase", YAFFS_TRACE_ERASE}, -+ {"error", YAFFS_TRACE_ERROR}, -+ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, -+ {"gc", YAFFS_TRACE_GC}, -+ {"lock", YAFFS_TRACE_LOCK}, -+ {"mtd", YAFFS_TRACE_MTD}, -+ {"nandaccess", YAFFS_TRACE_NANDACCESS}, -+ {"os", YAFFS_TRACE_OS}, -+ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, -+ {"scan", YAFFS_TRACE_SCAN}, -+ {"mount", YAFFS_TRACE_MOUNT}, -+ {"tracing", YAFFS_TRACE_TRACING}, -+ {"sync", YAFFS_TRACE_SYNC}, -+ {"write", YAFFS_TRACE_WRITE}, -+ {"verify", YAFFS_TRACE_VERIFY}, -+ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, -+ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, -+ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, -+ {"all", 0xffffffff}, -+ {"none", 0}, -+ {NULL, 0}, -+}; -+ -+#define MAX_MASK_NAME_LENGTH 40 -+static int yaffs_proc_write_trace_options(struct file *file, const char *buf, -+ unsigned long count) -+{ -+ unsigned rg = 0, mask_bitfield; -+ char *end; -+ char *mask_name; -+ const char *x; -+ char substring[MAX_MASK_NAME_LENGTH + 1]; -+ int i; -+ int done = 0; -+ int add, len = 0; -+ int pos = 0; -+ -+ rg = yaffs_trace_mask; -+ -+ while (!done && (pos < count)) { -+ done = 1; -+ while ((pos < count) && isspace(buf[pos])) -+ pos++; -+ -+ switch (buf[pos]) { -+ case '+': -+ case '-': -+ case '=': -+ add = buf[pos]; -+ pos++; -+ break; -+ -+ default: -+ add = ' '; -+ break; -+ } -+ mask_name = NULL; -+ -+ mask_bitfield = simple_strtoul(buf + pos, &end, 0); -+ -+ if (end > buf + pos) { -+ mask_name = "numeral"; -+ len = end - (buf + pos); -+ pos += len; -+ done = 0; -+ } else { -+ for (x = buf + pos, i = 0; -+ (*x == '_' || (*x >= 'a' && *x <= 'z')) && -+ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) -+ substring[i] = *x; -+ substring[i] = '\0'; -+ -+ for (i = 0; mask_flags[i].mask_name != NULL; i++) { -+ if (strcmp(substring, mask_flags[i].mask_name) -+ == 0) { -+ mask_name = mask_flags[i].mask_name; -+ mask_bitfield = -+ mask_flags[i].mask_bitfield; -+ done = 0; -+ break; -+ } -+ } -+ } -+ -+ if (mask_name != NULL) { -+ done = 0; -+ switch (add) { -+ case '-': -+ rg &= ~mask_bitfield; -+ break; -+ case '+': -+ rg |= mask_bitfield; -+ break; -+ case '=': -+ rg = mask_bitfield; -+ break; -+ default: -+ rg |= mask_bitfield; -+ break; -+ } -+ } -+ } -+ -+ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; -+ -+ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); -+ -+ if (rg & YAFFS_TRACE_ALWAYS) { -+ for (i = 0; mask_flags[i].mask_name != NULL; i++) { -+ char flag; -+ flag = ((rg & mask_flags[i].mask_bitfield) == -+ mask_flags[i].mask_bitfield) ? '+' : '-'; -+ printk(KERN_DEBUG "%c%s\n", flag, -+ mask_flags[i].mask_name); -+ } -+ } -+ -+ return count; -+} -+ -+/* Debug strings are of the form: -+ * .bnnn print info on block n -+ * .cobjn,chunkn print nand chunk id for objn:chunkn -+ */ -+ -+static int yaffs_proc_debug_write(struct file *file, const char *buf, -+ unsigned long count) -+{ -+ -+ char str[100]; -+ char *p0; -+ char *p1; -+ long p1_val; -+ long p0_val; -+ char cmd; -+ struct list_head *item; -+ -+ memset(str, 0, sizeof(str)); -+ memcpy(str, buf, min((size_t)count, sizeof(str) -1)); -+ -+ cmd = str[1]; -+ -+ p0 = str + 2; -+ -+ p1 = p0; -+ -+ while (*p1 && *p1 != ',') { -+ p1++; -+ } -+ *p1 = '\0'; -+ p1++; -+ -+ p0_val = simple_strtol(p0, NULL, 0); -+ p1_val = simple_strtol(p1, NULL, 0); -+ -+ -+ mutex_lock(&yaffs_context_lock); -+ -+ /* Locate and print the Nth entry. Order N-squared but N is small. */ -+ list_for_each(item, &yaffs_context_list) { -+ struct yaffs_linux_context *dc = -+ list_entry(item, struct yaffs_linux_context, -+ context_list); -+ struct yaffs_dev *dev = dc->dev; -+ -+ if (cmd == 'b') { -+ struct yaffs_block_info *bi; -+ -+ bi = yaffs_get_block_info(dev,p0_val); -+ -+ if(bi) { -+ printk("Block %d: state %d, retire %d, use %d, seq %d\n", -+ (int)p0_val, bi->block_state, -+ bi->needs_retiring, bi->pages_in_use, -+ bi->seq_number); -+ } -+ } else if (cmd == 'c') { -+ struct yaffs_obj *obj; -+ int nand_chunk; -+ -+ obj = yaffs_find_by_number(dev, p0_val); -+ if (!obj) -+ printk("No obj %d\n", (int)p0_val); -+ else { -+ if(p1_val == 0) -+ nand_chunk = obj->hdr_chunk; -+ else -+ nand_chunk = -+ yaffs_find_chunk_in_file(obj, -+ p1_val, NULL); -+ printk("Nand chunk for %d:%d is %d\n", -+ (int)p0_val, (int)p1_val, nand_chunk); -+ } -+ } -+ } -+ -+ mutex_unlock(&yaffs_context_lock); -+ -+ return count; -+} -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) -+static int yaffs_proc_write(struct file *file, const char *buf, -+ unsigned long count, void *ppos) -+#else -+static ssize_t yaffs_proc_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) -+#endif -+{ -+ if (buf[0] == '.') -+ return yaffs_proc_debug_write(file, buf, count); -+ return yaffs_proc_write_trace_options(file, buf, count); -+} -+ -+/* Stuff to handle installation of file systems */ -+struct file_system_to_install { -+ struct file_system_type *fst; -+ int installed; -+}; -+ -+static struct file_system_to_install fs_to_install[] = { -+ {&yaffs_fs_type, 0}, -+ {&yaffs2_fs_type, 0}, -+ {NULL, 0} -+}; -+ -+ -+#ifdef YAFFS_NEW_PROCFS -+static int yaffs_proc_show(struct seq_file *m, void *v) -+{ -+ /* FIXME: Unify in a better way? */ -+ char buffer[512]; -+ char *start; -+ int len; -+ -+ len = yaffs_proc_read(buffer, &start, 0, sizeof(buffer), NULL, NULL); -+ seq_puts(m, buffer); -+ return 0; -+} -+ -+static int yaffs_proc_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, yaffs_proc_show, NULL); -+} -+ -+static struct file_operations procfs_ops = { -+ .owner = THIS_MODULE, -+ .open = yaffs_proc_open, -+ .read = seq_read, -+ .write = yaffs_proc_write, -+}; -+ -+static int yaffs_procfs_init(void) -+{ -+ /* Install the proc_fs entries */ -+ my_proc_entry = proc_create("yaffs", -+ S_IRUGO | S_IFREG, -+ YPROC_ROOT, -+ &procfs_ops); -+ -+ if (my_proc_entry) { -+ return 0; -+ } else { -+ return -ENOMEM; -+ } -+} -+ -+#else -+ -+ -+static int yaffs_procfs_init(void) -+{ -+ /* Install the proc_fs entries */ -+ my_proc_entry = create_proc_entry("yaffs", -+ S_IRUGO | S_IFREG, YPROC_ROOT); -+ -+ if (my_proc_entry) { -+ my_proc_entry->write_proc = yaffs_proc_write; -+ my_proc_entry->read_proc = yaffs_proc_read; -+ my_proc_entry->data = NULL; -+ return 0; -+ } else { -+ return -ENOMEM; -+ } -+} -+ -+#endif -+ -+ -+static int __init init_yaffs_fs(void) -+{ -+ int error = 0; -+ struct file_system_to_install *fsinst; -+ -+ mutex_init(&yaffs_context_lock); -+ -+ error = yaffs_procfs_init(); -+ if (error) -+ return error; -+ -+ /* Now add the file system entries */ -+ -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst && !error) { -+ error = register_filesystem(fsinst->fst); -+ if (!error) -+ fsinst->installed = 1; -+ fsinst++; -+ } -+ -+ /* Any errors? uninstall */ -+ if (error) { -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst) { -+ if (fsinst->installed) { -+ unregister_filesystem(fsinst->fst); -+ fsinst->installed = 0; -+ } -+ fsinst++; -+ } -+ } -+ -+ return error; -+} -+ -+static void __exit exit_yaffs_fs(void) -+{ -+ -+ struct file_system_to_install *fsinst; -+ -+ remove_proc_entry("yaffs", YPROC_ROOT); -+ -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst) { -+ if (fsinst->installed) { -+ unregister_filesystem(fsinst->fst); -+ fsinst->installed = 0; -+ } -+ fsinst++; -+ } -+} -+ -+module_init(init_yaffs_fs) -+ module_exit(exit_yaffs_fs) -+ -+ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); -+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); -+MODULE_LICENSE("GPL"); -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.14.43/fs/yaffs2/yaffs_yaffs1.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_yaffs1.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,422 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_yaffs1.h" -+#include "yportenv.h" -+#include "yaffs_trace.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_nand.h" -+#include "yaffs_attribs.h" -+ -+int yaffs1_scan(struct yaffs_dev *dev) -+{ -+ struct yaffs_ext_tags tags; -+ int blk; -+ int result; -+ int chunk; -+ int c; -+ int deleted; -+ enum yaffs_block_state state; -+ LIST_HEAD(hard_list); -+ struct yaffs_block_info *bi; -+ u32 seq_number; -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_obj *in; -+ struct yaffs_obj *parent; -+ int alloc_failed = 0; -+ struct yaffs_shadow_fixer *shadow_fixers = NULL; -+ u8 *chunk_data; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs1_scan starts intstartblk %d intendblk %d...", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ chunk_data = yaffs_get_temp_buffer(dev); -+ -+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; -+ -+ /* Scan all the blocks to determine their state */ -+ bi = dev->block_info; -+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; -+ blk++) { -+ yaffs_clear_chunk_bits(dev, blk); -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ -+ yaffs_query_init_block_state(dev, blk, &state, &seq_number); -+ -+ bi->block_state = state; -+ bi->seq_number = seq_number; -+ -+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) -+ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, -+ "Block scanning block %d state %d seq %d", -+ blk, state, seq_number); -+ -+ if (state == YAFFS_BLOCK_STATE_DEAD) { -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, -+ "block %d is bad", blk); -+ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += dev->param.chunks_per_block; -+ } -+ bi++; -+ } -+ -+ /* For each block.... */ -+ for (blk = dev->internal_start_block; -+ !alloc_failed && blk <= dev->internal_end_block; blk++) { -+ -+ cond_resched(); -+ -+ bi = yaffs_get_block_info(dev, blk); -+ state = bi->block_state; -+ -+ deleted = 0; -+ -+ /* For each chunk in each block that needs scanning.... */ -+ for (c = 0; -+ !alloc_failed && c < dev->param.chunks_per_block && -+ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { -+ /* Read the tags and decide what to do */ -+ chunk = blk * dev->param.chunks_per_block + c; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, -+ &tags); -+ -+ /* Let's have a good look at this chunk... */ -+ -+ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || -+ tags.is_deleted) { -+ /* YAFFS1 only... -+ * A deleted chunk -+ */ -+ deleted++; -+ dev->n_free_chunks++; -+ } else if (!tags.chunk_used) { -+ /* An unassigned chunk in the block -+ * This means that either the block is empty or -+ * this is the one being allocated from -+ */ -+ -+ if (c == 0) { -+ /* We're looking at the first chunk in -+ *the block so the block is unused */ -+ state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ } else { -+ /* this is the block being allocated */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Allocating from %d %d", -+ blk, c); -+ state = YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->alloc_block = blk; -+ dev->alloc_page = c; -+ dev->alloc_block_finder = blk; -+ -+ } -+ -+ dev->n_free_chunks += -+ (dev->param.chunks_per_block - c); -+ } else if (tags.chunk_id > 0) { -+ /* chunk_id > 0 so it is a data chunk... */ -+ unsigned int endpos; -+ -+ yaffs_set_chunk_bit(dev, blk, c); -+ bi->pages_in_use++; -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ YAFFS_OBJECT_TYPE_FILE); -+ /* PutChunkIntoFile checks for a clash -+ * (two data chunks with the same chunk_id). -+ */ -+ -+ if (!in) -+ alloc_failed = 1; -+ -+ if (in) { -+ if (!yaffs_put_chunk_in_file -+ (in, tags.chunk_id, chunk, 1)) -+ alloc_failed = 1; -+ } -+ -+ endpos = -+ (tags.chunk_id - 1) * -+ dev->data_bytes_per_chunk + -+ tags.n_bytes; -+ if (in && -+ in->variant_type == -+ YAFFS_OBJECT_TYPE_FILE && -+ in->variant.file_variant.scanned_size < -+ endpos) { -+ in->variant.file_variant.scanned_size = -+ endpos; -+ if (!dev->param.use_header_file_size) { -+ in->variant. -+ file_variant.file_size = -+ in->variant. -+ file_variant.scanned_size; -+ } -+ -+ } -+ } else { -+ /* chunk_id == 0, so it is an ObjectHeader. -+ * Make the object -+ */ -+ yaffs_set_chunk_bit(dev, blk, c); -+ bi->pages_in_use++; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, -+ chunk_data, -+ NULL); -+ -+ oh = (struct yaffs_obj_hdr *)chunk_data; -+ -+ in = yaffs_find_by_number(dev, tags.obj_id); -+ if (in && in->variant_type != oh->type) { -+ /* This should not happen, but somehow -+ * Wev'e ended up with an obj_id that -+ * has been reused but not yet deleted, -+ * and worse still it has changed type. -+ * Delete the old object. -+ */ -+ -+ yaffs_del_obj(in); -+ in = NULL; -+ } -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ oh->type); -+ -+ if (!in) -+ alloc_failed = 1; -+ -+ if (in && oh->shadows_obj > 0) { -+ -+ struct yaffs_shadow_fixer *fixer; -+ fixer = -+ kmalloc(sizeof -+ (struct yaffs_shadow_fixer), -+ GFP_NOFS); -+ if (fixer) { -+ fixer->next = shadow_fixers; -+ shadow_fixers = fixer; -+ fixer->obj_id = tags.obj_id; -+ fixer->shadowed_id = -+ oh->shadows_obj; -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Shadow fixer: %d shadows %d", -+ fixer->obj_id, -+ fixer->shadowed_id); -+ -+ } -+ -+ } -+ -+ if (in && in->valid) { -+ /* We have already filled this one. -+ * We have a duplicate and need to -+ * resolve it. */ -+ -+ unsigned existing_serial = in->serial; -+ unsigned new_serial = -+ tags.serial_number; -+ -+ if (((existing_serial + 1) & 3) == -+ new_serial) { -+ /* Use new one - destroy the -+ * exisiting one */ -+ yaffs_chunk_del(dev, -+ in->hdr_chunk, -+ 1, __LINE__); -+ in->valid = 0; -+ } else { -+ /* Use existing - destroy -+ * this one. */ -+ yaffs_chunk_del(dev, chunk, 1, -+ __LINE__); -+ } -+ } -+ -+ if (in && !in->valid && -+ (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == -+ YAFFS_OBJECTID_LOSTNFOUND)) { -+ /* We only load some info, don't fiddle -+ * with directory structure */ -+ in->valid = 1; -+ in->variant_type = oh->type; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->hdr_chunk = chunk; -+ in->serial = tags.serial_number; -+ -+ } else if (in && !in->valid) { -+ /* we need to load this info */ -+ -+ in->valid = 1; -+ in->variant_type = oh->type; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->hdr_chunk = chunk; -+ in->serial = tags.serial_number; -+ -+ yaffs_set_obj_name_from_oh(in, oh); -+ in->dirty = 0; -+ -+ /* directory stuff... -+ * hook up to parent -+ */ -+ -+ parent = -+ yaffs_find_or_create_by_number -+ (dev, oh->parent_obj_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ if (!parent) -+ alloc_failed = 1; -+ if (parent && parent->variant_type == -+ YAFFS_OBJECT_TYPE_UNKNOWN) { -+ /* Set up as a directory */ -+ parent->variant_type = -+ YAFFS_OBJECT_TYPE_DIRECTORY; -+ INIT_LIST_HEAD(&parent-> -+ variant.dir_variant. -+ children); -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ /* Hoosterman, a problem.... -+ * We're trying to use a -+ * non-directory as a directory -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." -+ ); -+ parent = dev->lost_n_found; -+ } -+ -+ yaffs_add_obj_to_dir(parent, in); -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Todo got a problem */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (dev->param. -+ use_header_file_size) -+ in->variant. -+ file_variant.file_size -+ = yaffs_oh_to_size(oh); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ in->variant. -+ hardlink_variant.equiv_id = -+ oh->equiv_id; -+ list_add(&in->hard_links, -+ &hard_list); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ in->variant.symlink_variant. -+ alias = -+ yaffs_clone_str(oh->alias); -+ if (!in->variant. -+ symlink_variant.alias) -+ alloc_failed = 1; -+ break; -+ } -+ } -+ } -+ } -+ -+ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* If we got this far while scanning, -+ * then the block is fully allocated. */ -+ state = YAFFS_BLOCK_STATE_FULL; -+ } -+ -+ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ /* If the block was partially allocated then -+ * treat it as fully allocated. */ -+ state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ -+ bi->block_state = state; -+ -+ /* Now let's see if it was dirty */ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state == YAFFS_BLOCK_STATE_FULL) -+ yaffs_block_became_dirty(dev, blk); -+ } -+ -+ /* Ok, we've done all the scanning. -+ * Fix up the hard link chains. -+ * We should now have scanned all the objects, now it's time to add -+ * these hardlinks. -+ */ -+ -+ yaffs_link_fixup(dev, &hard_list); -+ -+ /* -+ * Fix up any shadowed objects. -+ * There should not be more than one of these. -+ */ -+ { -+ struct yaffs_shadow_fixer *fixer; -+ struct yaffs_obj *obj; -+ -+ while (shadow_fixers) { -+ fixer = shadow_fixers; -+ shadow_fixers = fixer->next; -+ /* Complete the rename transaction by deleting the -+ * shadowed object then setting the object header -+ to unshadowed. -+ */ -+ obj = yaffs_find_by_number(dev, fixer->shadowed_id); -+ if (obj) -+ yaffs_del_obj(obj); -+ -+ obj = yaffs_find_by_number(dev, fixer->obj_id); -+ -+ if (obj) -+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); -+ -+ kfree(fixer); -+ } -+ } -+ -+ yaffs_release_temp_buffer(dev, chunk_data); -+ -+ if (alloc_failed) -+ return YAFFS_FAIL; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.14.43/fs/yaffs2/yaffs_yaffs1.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_yaffs1.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,22 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_YAFFS1_H__ -+#define __YAFFS_YAFFS1_H__ -+ -+#include "yaffs_guts.h" -+int yaffs1_scan(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.14.43/fs/yaffs2/yaffs_yaffs2.c ---- linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_yaffs2.c 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,1532 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yaffs_yaffs2.h" -+#include "yaffs_checkptrw.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_nand.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_verify.h" -+#include "yaffs_attribs.h" -+#include "yaffs_summary.h" -+ -+/* -+ * Checkpoints are really no benefit on very small partitions. -+ * -+ * To save space on small partitions don't bother with checkpoints unless -+ * the partition is at least this big. -+ */ -+#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 -+#define YAFFS_SMALL_HOLE_THRESHOLD 4 -+ -+/* -+ * Oldest Dirty Sequence Number handling. -+ */ -+ -+/* yaffs_calc_oldest_dirty_seq() -+ * yaffs2_find_oldest_dirty_seq() -+ * Calculate the oldest dirty sequence number if we don't know it. -+ */ -+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) -+{ -+ int i; -+ unsigned seq; -+ unsigned block_no = 0; -+ struct yaffs_block_info *b; -+ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ /* Find the oldest dirty sequence number. */ -+ seq = dev->seq_number + 1; -+ b = dev->block_info; -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ if (b->block_state == YAFFS_BLOCK_STATE_FULL && -+ (b->pages_in_use - b->soft_del_pages) < -+ dev->param.chunks_per_block && -+ b->seq_number < seq) { -+ seq = b->seq_number; -+ block_no = i; -+ } -+ b++; -+ } -+ -+ if (block_no) { -+ dev->oldest_dirty_seq = seq; -+ dev->oldest_dirty_block = block_no; -+ } -+} -+ -+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!dev->oldest_dirty_seq) -+ yaffs_calc_oldest_dirty_seq(dev); -+} -+ -+/* -+ * yaffs_clear_oldest_dirty_seq() -+ * Called when a block is erased or marked bad. (ie. when its seq_number -+ * becomes invalid). If the value matches the oldest then we clear -+ * dev->oldest_dirty_seq to force its recomputation. -+ */ -+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi) -+{ -+ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { -+ dev->oldest_dirty_seq = 0; -+ dev->oldest_dirty_block = 0; -+ } -+} -+ -+/* -+ * yaffs2_update_oldest_dirty_seq() -+ * Update the oldest dirty sequence number whenever we dirty a block. -+ * Only do this if the oldest_dirty_seq is actually being tracked. -+ */ -+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, -+ struct yaffs_block_info *bi) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (dev->oldest_dirty_seq) { -+ if (dev->oldest_dirty_seq > bi->seq_number) { -+ dev->oldest_dirty_seq = bi->seq_number; -+ dev->oldest_dirty_block = block_no; -+ } -+ } -+} -+ -+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) -+{ -+ -+ if (!dev->param.is_yaffs2) -+ return 1; /* disqualification only applies to yaffs2. */ -+ -+ if (!bi->has_shrink_hdr) -+ return 1; /* can gc */ -+ -+ yaffs2_find_oldest_dirty_seq(dev); -+ -+ /* Can't do gc of this block if there are any blocks older than this -+ * one that have discarded pages. -+ */ -+ return (bi->seq_number <= dev->oldest_dirty_seq); -+} -+ -+/* -+ * yaffs2_find_refresh_block() -+ * periodically finds the oldest full block by sequence number for refreshing. -+ * Only for yaffs2. -+ */ -+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) -+{ -+ u32 b; -+ u32 oldest = 0; -+ u32 oldest_seq = 0; -+ struct yaffs_block_info *bi; -+ -+ if (!dev->param.is_yaffs2) -+ return oldest; -+ -+ /* -+ * If refresh period < 10 then refreshing is disabled. -+ */ -+ if (dev->param.refresh_period < 10) -+ return oldest; -+ -+ /* -+ * Fix broken values. -+ */ -+ if (dev->refresh_skip > dev->param.refresh_period) -+ dev->refresh_skip = dev->param.refresh_period; -+ -+ if (dev->refresh_skip > 0) -+ return oldest; -+ -+ /* -+ * Refresh skip is now zero. -+ * We'll do a refresh this time around.... -+ * Update the refresh skip and find the oldest block. -+ */ -+ dev->refresh_skip = dev->param.refresh_period; -+ dev->refresh_count++; -+ bi = dev->block_info; -+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { -+ -+ if (oldest < 1 || bi->seq_number < oldest_seq) { -+ oldest = b; -+ oldest_seq = bi->seq_number; -+ } -+ } -+ bi++; -+ } -+ -+ if (oldest > 0) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC refresh count %d selected block %d with seq_number %d", -+ dev->refresh_count, oldest, oldest_seq); -+ } -+ -+ return oldest; -+} -+ -+int yaffs2_checkpt_required(struct yaffs_dev *dev) -+{ -+ int nblocks; -+ -+ if (!dev->param.is_yaffs2) -+ return 0; -+ -+ nblocks = dev->internal_end_block - dev->internal_start_block + 1; -+ -+ return !dev->param.skip_checkpt_wr && -+ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); -+} -+ -+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) -+{ -+ int retval; -+ int n_bytes = 0; -+ int n_blocks; -+ int dev_blocks; -+ -+ if (!dev->param.is_yaffs2) -+ return 0; -+ -+ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { -+ /* Not a valid value so recalculate */ -+ dev_blocks = dev->param.end_block - dev->param.start_block + 1; -+ n_bytes += sizeof(struct yaffs_checkpt_validity); -+ n_bytes += sizeof(struct yaffs_checkpt_dev); -+ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); -+ n_bytes += dev_blocks * dev->chunk_bit_stride; -+ n_bytes += -+ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * -+ dev->n_obj; -+ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; -+ n_bytes += sizeof(struct yaffs_checkpt_validity); -+ n_bytes += sizeof(u32); /* checksum */ -+ -+ /* Round up and add 2 blocks to allow for some bad blocks, -+ * so add 3 */ -+ -+ n_blocks = -+ (n_bytes / -+ (dev->data_bytes_per_chunk * -+ dev->param.chunks_per_block)) + 3; -+ -+ dev->checkpoint_blocks_required = n_blocks; -+ } -+ -+ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; -+ if (retval < 0) -+ retval = 0; -+ return retval; -+} -+ -+/*--------------------- Checkpointing --------------------*/ -+ -+static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) -+{ -+ struct yaffs_checkpt_validity cp; -+ -+ memset(&cp, 0, sizeof(cp)); -+ -+ cp.struct_type = sizeof(cp); -+ cp.magic = YAFFS_MAGIC; -+ cp.version = YAFFS_CHECKPOINT_VERSION; -+ cp.head = (head) ? 1 : 0; -+ -+ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) -+{ -+ struct yaffs_checkpt_validity cp; -+ int ok; -+ -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ -+ if (ok) -+ ok = (cp.struct_type == sizeof(cp)) && -+ (cp.magic == YAFFS_MAGIC) && -+ (cp.version == YAFFS_CHECKPOINT_VERSION) && -+ (cp.head == ((head) ? 1 : 0)); -+ return ok ? 1 : 0; -+} -+ -+static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, -+ struct yaffs_dev *dev) -+{ -+ cp->n_erased_blocks = dev->n_erased_blocks; -+ cp->alloc_block = dev->alloc_block; -+ cp->alloc_page = dev->alloc_page; -+ cp->n_free_chunks = dev->n_free_chunks; -+ -+ cp->n_deleted_files = dev->n_deleted_files; -+ cp->n_unlinked_files = dev->n_unlinked_files; -+ cp->n_bg_deletions = dev->n_bg_deletions; -+ cp->seq_number = dev->seq_number; -+ -+} -+ -+static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, -+ struct yaffs_checkpt_dev *cp) -+{ -+ dev->n_erased_blocks = cp->n_erased_blocks; -+ dev->alloc_block = cp->alloc_block; -+ dev->alloc_page = cp->alloc_page; -+ dev->n_free_chunks = cp->n_free_chunks; -+ -+ dev->n_deleted_files = cp->n_deleted_files; -+ dev->n_unlinked_files = cp->n_unlinked_files; -+ dev->n_bg_deletions = cp->n_bg_deletions; -+ dev->seq_number = cp->seq_number; -+} -+ -+static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_dev cp; -+ u32 n_bytes; -+ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ int ok; -+ -+ /* Write device runtime values */ -+ yaffs2_dev_to_checkpt_dev(&cp, dev); -+ cp.struct_type = sizeof(cp); -+ -+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (!ok) -+ return 0; -+ -+ /* Write block info */ -+ n_bytes = n_blocks * sizeof(struct yaffs_block_info); -+ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); -+ if (!ok) -+ return 0; -+ -+ /* Write chunk bits */ -+ n_bytes = n_blocks * dev->chunk_bit_stride; -+ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_dev cp; -+ u32 n_bytes; -+ u32 n_blocks = -+ (dev->internal_end_block - dev->internal_start_block + 1); -+ int ok; -+ -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (!ok) -+ return 0; -+ -+ if (cp.struct_type != sizeof(cp)) -+ return 0; -+ -+ yaffs_checkpt_dev_to_dev(dev, &cp); -+ -+ n_bytes = n_blocks * sizeof(struct yaffs_block_info); -+ -+ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); -+ -+ if (!ok) -+ return 0; -+ -+ n_bytes = n_blocks * dev->chunk_bit_stride; -+ -+ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); -+ -+ return ok ? 1 : 0; -+} -+ -+static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, -+ struct yaffs_obj *obj) -+{ -+ cp->obj_id = obj->obj_id; -+ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; -+ cp->hdr_chunk = obj->hdr_chunk; -+ cp->variant_type = obj->variant_type; -+ cp->deleted = obj->deleted; -+ cp->soft_del = obj->soft_del; -+ cp->unlinked = obj->unlinked; -+ cp->fake = obj->fake; -+ cp->rename_allowed = obj->rename_allowed; -+ cp->unlink_allowed = obj->unlink_allowed; -+ cp->serial = obj->serial; -+ cp->n_data_chunks = obj->n_data_chunks; -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; -+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; -+} -+ -+static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, -+ struct yaffs_checkpt_obj *cp) -+{ -+ struct yaffs_obj *parent; -+ -+ if (obj->variant_type != cp->variant_type) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", -+ cp->obj_id, cp->variant_type, cp->hdr_chunk, -+ obj->variant_type); -+ return 0; -+ } -+ -+ obj->obj_id = cp->obj_id; -+ -+ if (cp->parent_id) -+ parent = yaffs_find_or_create_by_number(obj->my_dev, -+ cp->parent_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ else -+ parent = NULL; -+ -+ if (parent) { -+ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", -+ cp->obj_id, cp->parent_id, -+ cp->variant_type, cp->hdr_chunk, -+ parent->variant_type); -+ return 0; -+ } -+ yaffs_add_obj_to_dir(parent, obj); -+ } -+ -+ obj->hdr_chunk = cp->hdr_chunk; -+ obj->variant_type = cp->variant_type; -+ obj->deleted = cp->deleted; -+ obj->soft_del = cp->soft_del; -+ obj->unlinked = cp->unlinked; -+ obj->fake = cp->fake; -+ obj->rename_allowed = cp->rename_allowed; -+ obj->unlink_allowed = cp->unlink_allowed; -+ obj->serial = cp->serial; -+ obj->n_data_chunks = cp->n_data_chunks; -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; -+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; -+ -+ if (obj->hdr_chunk > 0) -+ obj->lazy_loaded = 1; -+ return 1; -+} -+ -+static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, -+ struct yaffs_tnode *tn, u32 level, -+ int chunk_offset) -+{ -+ int i; -+ struct yaffs_dev *dev = in->my_dev; -+ int ok = 1; -+ u32 base_offset; -+ -+ if (!tn) -+ return 1; -+ -+ if (level > 0) { -+ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { -+ if (!tn->internal[i]) -+ continue; -+ ok = yaffs2_checkpt_tnode_worker(in, -+ tn->internal[i], -+ level - 1, -+ (chunk_offset << -+ YAFFS_TNODES_INTERNAL_BITS) + i); -+ } -+ return ok; -+ } -+ -+ /* Level 0 tnode */ -+ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; -+ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == -+ sizeof(base_offset)); -+ if (ok) -+ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == -+ dev->tnode_size); -+ -+ return ok; -+} -+ -+static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) -+{ -+ u32 end_marker = ~0; -+ int ok = 1; -+ -+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return ok; -+ -+ ok = yaffs2_checkpt_tnode_worker(obj, -+ obj->variant.file_variant.top, -+ obj->variant.file_variant. -+ top_level, 0); -+ if (ok) -+ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, -+ sizeof(end_marker)) == sizeof(end_marker)); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) -+{ -+ u32 base_chunk; -+ int ok = 1; -+ struct yaffs_dev *dev = obj->my_dev; -+ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; -+ struct yaffs_tnode *tn; -+ int nread = 0; -+ -+ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == -+ sizeof(base_chunk)); -+ -+ while (ok && (~base_chunk)) { -+ nread++; -+ /* Read level 0 tnode */ -+ -+ tn = yaffs_get_tnode(dev); -+ if (tn) -+ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == -+ dev->tnode_size); -+ else -+ ok = 0; -+ -+ if (tn && ok) -+ ok = yaffs_add_find_tnode_0(dev, -+ file_stuct_ptr, -+ base_chunk, tn) ? 1 : 0; -+ -+ if (ok) -+ ok = (yaffs2_checkpt_rd -+ (dev, &base_chunk, -+ sizeof(base_chunk)) == sizeof(base_chunk)); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint read tnodes %d records, last %d. ok %d", -+ nread, base_chunk, ok); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_checkpt_obj cp; -+ int i; -+ int ok = 1; -+ struct list_head *lh; -+ -+ /* Iterate through the objects in each hash entry, -+ * dumping them to the checkpointing stream. -+ */ -+ -+ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each(lh, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ if (!obj->defered_free) { -+ yaffs2_obj_checkpt_obj(&cp, obj); -+ cp.struct_type = sizeof(cp); -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", -+ cp.obj_id, cp.parent_id, -+ cp.variant_type, cp.hdr_chunk, obj); -+ -+ ok = (yaffs2_checkpt_wr(dev, &cp, -+ sizeof(cp)) == sizeof(cp)); -+ -+ if (ok && -+ obj->variant_type == -+ YAFFS_OBJECT_TYPE_FILE) -+ ok = yaffs2_wr_checkpt_tnodes(obj); -+ } -+ } -+ } -+ -+ /* Dump end of list */ -+ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); -+ cp.struct_type = sizeof(cp); -+ -+ if (ok) -+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_checkpt_obj cp; -+ int ok = 1; -+ int done = 0; -+ LIST_HEAD(hard_list); -+ -+ -+ while (ok && !done) { -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (cp.struct_type != sizeof(cp)) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "struct size %d instead of %d ok %d", -+ cp.struct_type, (int)sizeof(cp), ok); -+ ok = 0; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint read object %d parent %d type %d chunk %d ", -+ cp.obj_id, cp.parent_id, cp.variant_type, -+ cp.hdr_chunk); -+ -+ if (ok && cp.obj_id == ~0) { -+ done = 1; -+ } else if (ok) { -+ obj = -+ yaffs_find_or_create_by_number(dev, cp.obj_id, -+ cp.variant_type); -+ if (obj) { -+ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); -+ if (!ok) -+ break; -+ if (obj->variant_type == -+ YAFFS_OBJECT_TYPE_FILE) { -+ ok = yaffs2_rd_checkpt_tnodes(obj); -+ } else if (obj->variant_type == -+ YAFFS_OBJECT_TYPE_HARDLINK) { -+ list_add(&obj->hard_links, &hard_list); -+ } -+ } else { -+ ok = 0; -+ } -+ } -+ } -+ -+ if (ok) -+ yaffs_link_fixup(dev, &hard_list); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) -+{ -+ u32 checkpt_sum; -+ int ok; -+ -+ yaffs2_get_checkpt_sum(dev, &checkpt_sum); -+ -+ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == -+ sizeof(checkpt_sum)); -+ -+ if (!ok) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) -+{ -+ u32 checkpt_sum0; -+ u32 checkpt_sum1; -+ int ok; -+ -+ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); -+ -+ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == -+ sizeof(checkpt_sum1)); -+ -+ if (!ok) -+ return 0; -+ -+ if (checkpt_sum0 != checkpt_sum1) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) -+{ -+ int ok = 1; -+ -+ if (!yaffs2_checkpt_required(dev)) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "skipping checkpoint write"); -+ ok = 0; -+ } -+ -+ if (ok) -+ ok = yaffs2_checkpt_open(dev, 1); -+ -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint validity"); -+ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint device"); -+ ok = yaffs2_wr_checkpt_dev(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint objects"); -+ ok = yaffs2_wr_checkpt_objs(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint validity"); -+ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); -+ } -+ -+ if (ok) -+ ok = yaffs2_wr_checkpt_sum(dev); -+ -+ if (!yaffs_checkpt_close(dev)) -+ ok = 0; -+ -+ if (ok) -+ dev->is_checkpointed = 1; -+ else -+ dev->is_checkpointed = 0; -+ -+ return dev->is_checkpointed; -+} -+ -+static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) -+{ -+ int ok = 1; -+ -+ if (!dev->param.is_yaffs2) -+ ok = 0; -+ -+ if (ok && dev->param.skip_checkpt_rd) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "skipping checkpoint read"); -+ ok = 0; -+ } -+ -+ if (ok) -+ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ -+ -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint validity"); -+ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint device"); -+ ok = yaffs2_rd_checkpt_dev(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint objects"); -+ ok = yaffs2_rd_checkpt_objs(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint validity"); -+ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); -+ } -+ -+ if (ok) { -+ ok = yaffs2_rd_checkpt_sum(dev); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint checksum %d", ok); -+ } -+ -+ if (!yaffs_checkpt_close(dev)) -+ ok = 0; -+ -+ if (ok) -+ dev->is_checkpointed = 1; -+ else -+ dev->is_checkpointed = 0; -+ -+ return ok ? 1 : 0; -+} -+ -+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) -+{ -+ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { -+ dev->is_checkpointed = 0; -+ yaffs2_checkpt_invalidate_stream(dev); -+ } -+ if (dev->param.sb_dirty_fn) -+ dev->param.sb_dirty_fn(dev); -+} -+ -+int yaffs_checkpoint_save(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "save entry: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ yaffs_verify_objects(dev); -+ yaffs_verify_blocks(dev); -+ yaffs_verify_free_chunks(dev); -+ -+ if (!dev->is_checkpointed) { -+ yaffs2_checkpt_invalidate(dev); -+ yaffs2_wr_checkpt_data(dev); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, -+ "save exit: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ return dev->is_checkpointed; -+} -+ -+int yaffs2_checkpt_restore(struct yaffs_dev *dev) -+{ -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "restore entry: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ retval = yaffs2_rd_checkpt_data(dev); -+ -+ if (dev->is_checkpointed) { -+ yaffs_verify_objects(dev); -+ yaffs_verify_blocks(dev); -+ yaffs_verify_free_chunks(dev); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "restore exit: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ return retval; -+} -+ -+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) -+{ -+ /* if new_size > old_file_size. -+ * We're going to be writing a hole. -+ * If the hole is small then write zeros otherwise write a start -+ * of hole marker. -+ */ -+ loff_t old_file_size; -+ loff_t increase; -+ int small_hole; -+ int result = YAFFS_OK; -+ struct yaffs_dev *dev = NULL; -+ u8 *local_buffer = NULL; -+ int small_increase_ok = 0; -+ -+ if (!obj) -+ return YAFFS_FAIL; -+ -+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return YAFFS_FAIL; -+ -+ dev = obj->my_dev; -+ -+ /* Bail out if not yaffs2 mode */ -+ if (!dev->param.is_yaffs2) -+ return YAFFS_OK; -+ -+ old_file_size = obj->variant.file_variant.file_size; -+ -+ if (new_size <= old_file_size) -+ return YAFFS_OK; -+ -+ increase = new_size - old_file_size; -+ -+ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && -+ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) -+ small_hole = 1; -+ else -+ small_hole = 0; -+ -+ if (small_hole) -+ local_buffer = yaffs_get_temp_buffer(dev); -+ -+ if (local_buffer) { -+ /* fill hole with zero bytes */ -+ loff_t pos = old_file_size; -+ int this_write; -+ int written; -+ memset(local_buffer, 0, dev->data_bytes_per_chunk); -+ small_increase_ok = 1; -+ -+ while (increase > 0 && small_increase_ok) { -+ this_write = increase; -+ if (this_write > dev->data_bytes_per_chunk) -+ this_write = dev->data_bytes_per_chunk; -+ written = -+ yaffs_do_file_wr(obj, local_buffer, pos, this_write, -+ 0); -+ if (written == this_write) { -+ pos += this_write; -+ increase -= this_write; -+ } else { -+ small_increase_ok = 0; -+ } -+ } -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ -+ /* If out of space then reverse any chunks we've added */ -+ if (!small_increase_ok) -+ yaffs_resize_file_down(obj, old_file_size); -+ } -+ -+ if (!small_increase_ok && -+ obj->parent && -+ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && -+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { -+ /* Write a hole start header with the old file size */ -+ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); -+ } -+ -+ return result; -+} -+ -+struct yaffs_block_index { -+ int seq; -+ int block; -+}; -+ -+static int yaffs2_ybicmp(const void *a, const void *b) -+{ -+ int aseq = ((struct yaffs_block_index *)a)->seq; -+ int bseq = ((struct yaffs_block_index *)b)->seq; -+ int ablock = ((struct yaffs_block_index *)a)->block; -+ int bblock = ((struct yaffs_block_index *)b)->block; -+ -+ if (aseq == bseq) -+ return ablock - bblock; -+ -+ return aseq - bseq; -+} -+ -+static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, -+ int blk, int chunk_in_block, -+ int *found_chunks, -+ u8 *chunk_data, -+ struct list_head *hard_list, -+ int summary_available) -+{ -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_obj *in; -+ struct yaffs_obj *parent; -+ int equiv_id; -+ loff_t file_size; -+ int is_shrink; -+ int is_unlinked; -+ struct yaffs_ext_tags tags; -+ int result; -+ int alloc_failed = 0; -+ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; -+ struct yaffs_file_var *file_var; -+ struct yaffs_hardlink_var *hl_var; -+ struct yaffs_symlink_var *sl_var; -+ -+ if (summary_available) { -+ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); -+ tags.seq_number = bi->seq_number; -+ } -+ -+ if (!summary_available || tags.obj_id == 0) { -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); -+ dev->tags_used++; -+ } else { -+ dev->summary_used++; -+ } -+ -+ /* Let's have a good look at this chunk... */ -+ -+ if (!tags.chunk_used) { -+ /* An unassigned chunk in the block. -+ * If there are used chunks after this one, then -+ * it is a chunk that was skipped due to failing -+ * the erased check. Just skip it so that it can -+ * be deleted. -+ * But, more typically, We get here when this is -+ * an unallocated chunk and his means that -+ * either the block is empty or this is the one -+ * being allocated from -+ */ -+ -+ if (*found_chunks) { -+ /* This is a chunk that was skipped due -+ * to failing the erased check */ -+ } else if (chunk_in_block == 0) { -+ /* We're looking at the first chunk in -+ * the block so the block is unused */ -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ } else { -+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ if (dev->seq_number == bi->seq_number) { -+ /* Allocating from this block*/ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Allocating from %d %d", -+ blk, chunk_in_block); -+ -+ bi->block_state = -+ YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->alloc_block = blk; -+ dev->alloc_page = chunk_in_block; -+ dev->alloc_block_finder = blk; -+ } else { -+ /* This is a partially written block -+ * that is not the current -+ * allocation block. -+ */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Partially written block %d detected. gc will fix this.", -+ blk); -+ } -+ } -+ } -+ -+ dev->n_free_chunks++; -+ -+ } else if (tags.ecc_result == -+ YAFFS_ECC_RESULT_UNFIXED) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Unfixed ECC in chunk(%d:%d), chunk ignored", -+ blk, chunk_in_block); -+ dev->n_free_chunks++; -+ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || -+ tags.chunk_id > YAFFS_MAX_CHUNK_ID || -+ tags.obj_id == YAFFS_OBJECTID_SUMMARY || -+ (tags.chunk_id > 0 && -+ tags.n_bytes > dev->data_bytes_per_chunk) || -+ tags.seq_number != bi->seq_number) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", -+ blk, chunk_in_block, tags.obj_id, -+ tags.chunk_id, tags.n_bytes); -+ dev->n_free_chunks++; -+ } else if (tags.chunk_id > 0) { -+ /* chunk_id > 0 so it is a data chunk... */ -+ loff_t endpos; -+ loff_t chunk_base = (tags.chunk_id - 1) * -+ dev->data_bytes_per_chunk; -+ -+ *found_chunks = 1; -+ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ YAFFS_OBJECT_TYPE_FILE); -+ if (!in) -+ /* Out of memory */ -+ alloc_failed = 1; -+ -+ if (in && -+ in->variant_type == YAFFS_OBJECT_TYPE_FILE && -+ chunk_base < in->variant.file_variant.shrink_size) { -+ /* This has not been invalidated by -+ * a resize */ -+ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, -+ chunk, -1)) -+ alloc_failed = 1; -+ -+ /* File size is calculated by looking at -+ * the data chunks if we have not -+ * seen an object header yet. -+ * Stop this practice once we find an -+ * object header. -+ */ -+ endpos = chunk_base + tags.n_bytes; -+ -+ if (!in->valid && -+ in->variant.file_variant.scanned_size < endpos) { -+ in->variant.file_variant. -+ scanned_size = endpos; -+ in->variant.file_variant. -+ file_size = endpos; -+ } -+ } else if (in) { -+ /* This chunk has been invalidated by a -+ * resize, or a past file deletion -+ * so delete the chunk*/ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ } -+ } else { -+ /* chunk_id == 0, so it is an ObjectHeader. -+ * Thus, we read in the object header and make -+ * the object -+ */ -+ *found_chunks = 1; -+ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ -+ oh = NULL; -+ in = NULL; -+ -+ if (tags.extra_available) { -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ tags.extra_obj_type); -+ if (!in) -+ alloc_failed = 1; -+ } -+ -+ if (!in || -+ (!in->valid && dev->param.disable_lazy_load) || -+ tags.extra_shadows || -+ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { -+ -+ /* If we don't have valid info then we -+ * need to read the chunk -+ * TODO In future we can probably defer -+ * reading the chunk and living with -+ * invalid data until needed. -+ */ -+ -+ result = yaffs_rd_chunk_tags_nand(dev, -+ chunk, -+ chunk_data, -+ NULL); -+ -+ oh = (struct yaffs_obj_hdr *)chunk_data; -+ -+ if (dev->param.inband_tags) { -+ /* Fix up the header if they got -+ * corrupted by inband tags */ -+ oh->shadows_obj = -+ oh->inband_shadowed_obj_id; -+ oh->is_shrink = -+ oh->inband_is_shrink; -+ } -+ -+ if (!in) { -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, oh->type); -+ if (!in) -+ alloc_failed = 1; -+ } -+ } -+ -+ if (!in) { -+ /* TODO Hoosterman we have a problem! */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", -+ tags.obj_id, chunk); -+ return YAFFS_FAIL; -+ } -+ -+ if (in->valid) { -+ /* We have already filled this one. -+ * We have a duplicate that will be -+ * discarded, but we first have to suck -+ * out resize info if it is a file. -+ */ -+ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && -+ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || -+ (tags.extra_available && -+ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) -+ )) { -+ loff_t this_size = (oh) ? -+ yaffs_oh_to_size(oh) : -+ tags.extra_file_size; -+ u32 parent_obj_id = (oh) ? -+ oh->parent_obj_id : -+ tags.extra_parent_id; -+ -+ is_shrink = (oh) ? -+ oh->is_shrink : -+ tags.extra_is_shrink; -+ -+ /* If it is deleted (unlinked -+ * at start also means deleted) -+ * we treat the file size as -+ * being zeroed at this point. -+ */ -+ if (parent_obj_id == YAFFS_OBJECTID_DELETED || -+ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { -+ this_size = 0; -+ is_shrink = 1; -+ } -+ -+ if (is_shrink && -+ in->variant.file_variant.shrink_size > -+ this_size) -+ in->variant.file_variant.shrink_size = -+ this_size; -+ -+ if (is_shrink) -+ bi->has_shrink_hdr = 1; -+ } -+ /* Use existing - destroy this one. */ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ } -+ -+ if (!in->valid && in->variant_type != -+ (oh ? oh->type : tags.extra_obj_type)) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", -+ oh ? oh->type : tags.extra_obj_type, -+ in->variant_type, tags.obj_id, -+ chunk); -+ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); -+ } -+ -+ if (!in->valid && -+ (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { -+ /* We only load some info, don't fiddle -+ * with directory structure */ -+ in->valid = 1; -+ -+ if (oh) { -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->lazy_loaded = 0; -+ } else { -+ in->lazy_loaded = 1; -+ } -+ in->hdr_chunk = chunk; -+ -+ } else if (!in->valid) { -+ /* we need to load this info */ -+ in->valid = 1; -+ in->hdr_chunk = chunk; -+ if (oh) { -+ in->variant_type = oh->type; -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ -+ if (oh->shadows_obj > 0) -+ yaffs_handle_shadowed_obj(dev, -+ oh->shadows_obj, 1); -+ -+ yaffs_set_obj_name_from_oh(in, oh); -+ parent = yaffs_find_or_create_by_number(dev, -+ oh->parent_obj_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ file_size = yaffs_oh_to_size(oh); -+ is_shrink = oh->is_shrink; -+ equiv_id = oh->equiv_id; -+ } else { -+ in->variant_type = tags.extra_obj_type; -+ parent = yaffs_find_or_create_by_number(dev, -+ tags.extra_parent_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ file_size = tags.extra_file_size; -+ is_shrink = tags.extra_is_shrink; -+ equiv_id = tags.extra_equiv_id; -+ in->lazy_loaded = 1; -+ } -+ in->dirty = 0; -+ -+ if (!parent) -+ alloc_failed = 1; -+ -+ /* directory stuff... -+ * hook up to parent -+ */ -+ -+ if (parent && -+ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { -+ /* Set up as a directory */ -+ parent->variant_type = -+ YAFFS_OBJECT_TYPE_DIRECTORY; -+ INIT_LIST_HEAD(&parent-> -+ variant.dir_variant.children); -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ /* Hoosterman, another problem.... -+ * Trying to use a non-directory as a directory -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." -+ ); -+ parent = dev->lost_n_found; -+ } -+ yaffs_add_obj_to_dir(parent, in); -+ -+ is_unlinked = (parent == dev->del_dir) || -+ (parent == dev->unlinked_dir); -+ -+ if (is_shrink) -+ /* Mark the block */ -+ bi->has_shrink_hdr = 1; -+ -+ /* Note re hardlinks. -+ * Since we might scan a hardlink before its equivalent -+ * object is scanned we put them all in a list. -+ * After scanning is complete, we should have all the -+ * objects, so we run through this list and fix up all -+ * the chains. -+ */ -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Todo got a problem */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ file_var = &in->variant.file_variant; -+ if (file_var->scanned_size < file_size) { -+ /* This covers the case where the file -+ * size is greater than the data held. -+ * This will happen if the file is -+ * resized to be larger than its -+ * current data extents. -+ */ -+ file_var->file_size = file_size; -+ file_var->scanned_size = file_size; -+ } -+ -+ if (file_var->shrink_size > file_size) -+ file_var->shrink_size = file_size; -+ -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ hl_var = &in->variant.hardlink_variant; -+ if (!is_unlinked) { -+ hl_var->equiv_id = equiv_id; -+ list_add(&in->hard_links, hard_list); -+ } -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ sl_var = &in->variant.symlink_variant; -+ if (oh) { -+ sl_var->alias = -+ yaffs_clone_str(oh->alias); -+ if (!sl_var->alias) -+ alloc_failed = 1; -+ } -+ break; -+ } -+ } -+ } -+ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+int yaffs2_scan_backwards(struct yaffs_dev *dev) -+{ -+ int blk; -+ int block_iter; -+ int start_iter; -+ int end_iter; -+ int n_to_scan = 0; -+ enum yaffs_block_state state; -+ int c; -+ LIST_HEAD(hard_list); -+ struct yaffs_block_info *bi; -+ u32 seq_number; -+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ u8 *chunk_data; -+ int found_chunks; -+ int alloc_failed = 0; -+ struct yaffs_block_index *block_index = NULL; -+ int alt_block_index = 0; -+ int summary_available; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; -+ -+ block_index = -+ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); -+ -+ if (!block_index) { -+ block_index = -+ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); -+ alt_block_index = 1; -+ } -+ -+ if (!block_index) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs2_scan_backwards() could not allocate block index!" -+ ); -+ return YAFFS_FAIL; -+ } -+ -+ dev->blocks_in_checkpt = 0; -+ -+ chunk_data = yaffs_get_temp_buffer(dev); -+ -+ /* Scan all the blocks to determine their state */ -+ bi = dev->block_info; -+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; -+ blk++) { -+ yaffs_clear_chunk_bits(dev, blk); -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ -+ yaffs_query_init_block_state(dev, blk, &state, &seq_number); -+ -+ bi->block_state = state; -+ bi->seq_number = seq_number; -+ -+ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, -+ "Block scanning block %d state %d seq %d", -+ blk, bi->block_state, seq_number); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { -+ dev->blocks_in_checkpt++; -+ -+ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, -+ "block %d is bad", blk); -+ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += dev->param.chunks_per_block; -+ } else if (bi->block_state == -+ YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* Determine the highest sequence number */ -+ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && -+ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { -+ block_index[n_to_scan].seq = seq_number; -+ block_index[n_to_scan].block = blk; -+ n_to_scan++; -+ if (seq_number >= dev->seq_number) -+ dev->seq_number = seq_number; -+ } else { -+ /* TODO: Nasty sequence number! */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Block scanning block %d has bad sequence number %d", -+ blk, seq_number); -+ } -+ } -+ bi++; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); -+ -+ cond_resched(); -+ -+ /* Sort the blocks by sequence number */ -+ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), -+ yaffs2_ybicmp, NULL); -+ -+ cond_resched(); -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); -+ -+ /* Now scan the blocks looking at the data. */ -+ start_iter = 0; -+ end_iter = n_to_scan - 1; -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); -+ -+ /* For each block.... backwards */ -+ for (block_iter = end_iter; -+ !alloc_failed && block_iter >= start_iter; -+ block_iter--) { -+ /* Cooperative multitasking! This loop can run for so -+ long that watchdog timers expire. */ -+ cond_resched(); -+ -+ /* get the block to scan in the correct order */ -+ blk = block_index[block_iter].block; -+ bi = yaffs_get_block_info(dev, blk); -+ -+ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); -+ -+ /* For each chunk in each block that needs scanning.... */ -+ found_chunks = 0; -+ if (summary_available) -+ c = dev->chunks_per_summary - 1; -+ else -+ c = dev->param.chunks_per_block - 1; -+ -+ for (/* c is already initialised */; -+ !alloc_failed && c >= 0 && -+ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); -+ c--) { -+ /* Scan backwards... -+ * Read the tags and decide what to do -+ */ -+ if (yaffs2_scan_chunk(dev, bi, blk, c, -+ &found_chunks, chunk_data, -+ &hard_list, summary_available) == -+ YAFFS_FAIL) -+ alloc_failed = 1; -+ } -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* If we got this far while scanning, then the block -+ * is fully allocated. */ -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ } -+ -+ /* Now let's see if it was dirty */ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state == YAFFS_BLOCK_STATE_FULL) { -+ yaffs_block_became_dirty(dev, blk); -+ } -+ } -+ -+ yaffs_skip_rest_of_block(dev); -+ -+ if (alt_block_index) -+ vfree(block_index); -+ else -+ kfree(block_index); -+ -+ /* Ok, we've done all the scanning. -+ * Fix up the hard link chains. -+ * We have scanned all the objects, now it's time to add these -+ * hardlinks. -+ */ -+ yaffs_link_fixup(dev, &hard_list); -+ -+ yaffs_release_temp_buffer(dev, chunk_data); -+ -+ if (alloc_failed) -+ return YAFFS_FAIL; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.14.43/fs/yaffs2/yaffs_yaffs2.h ---- linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yaffs_yaffs2.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_YAFFS2_H__ -+#define __YAFFS_YAFFS2_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); -+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); -+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi); -+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, -+ struct yaffs_block_info *bi); -+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); -+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); -+int yaffs2_checkpt_required(struct yaffs_dev *dev); -+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); -+ -+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); -+int yaffs2_checkpt_save(struct yaffs_dev *dev); -+int yaffs2_checkpt_restore(struct yaffs_dev *dev); -+ -+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); -+int yaffs2_scan_backwards(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.14.43.orig/fs/yaffs2/yportenv.h linux-3.14.43/fs/yaffs2/yportenv.h ---- linux-3.14.43.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.14.43/fs/yaffs2/yportenv.h 2015-06-15 21:20:48.000000000 +0200 -@@ -0,0 +1,85 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YPORTENV_H__ -+#define __YPORTENV_H__ -+ -+/* -+ * Define the MTD version in terms of Linux Kernel versions -+ * This allows yaffs to be used independantly of the kernel -+ * as well as with it. -+ */ -+ -+#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) -+ -+#ifdef YAFFS_OUT_OF_TREE -+#include "moduleconfig.h" -+#endif -+ -+#include -+#define MTD_VERSION_CODE LINUX_VERSION_CODE -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* These type wrappings are used to support Unicode names in WinCE. */ -+#define YCHAR char -+#define YUCHAR unsigned char -+#define _Y(x) x -+ -+#define YAFFS_LOSTNFOUND_NAME "lost+found" -+#define YAFFS_LOSTNFOUND_PREFIX "obj" -+ -+ -+#define YAFFS_ROOT_MODE 0755 -+#define YAFFS_LOSTNFOUND_MODE 0700 -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+#define Y_CURRENT_TIME CURRENT_TIME.tv_sec -+#define Y_TIME_CONVERT(x) (x).tv_sec -+#else -+#define Y_CURRENT_TIME CURRENT_TIME -+#define Y_TIME_CONVERT(x) (x) -+#endif -+ -+#define compile_time_assertion(assertion) \ -+ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) -+ -+ -+#define yaffs_printf(msk, fmt, ...) \ -+ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) -+ -+#define yaffs_trace(msk, fmt, ...) do { \ -+ if (yaffs_trace_mask & (msk)) \ -+ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ -+} while (0) -+ -+ -+#endif diff --git a/target/linux/patches/3.14.43/zlib-inflate.patch b/target/linux/patches/3.14.43/zlib-inflate.patch deleted file mode 100644 index 58e1f6d21..000000000 --- a/target/linux/patches/3.14.43/zlib-inflate.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-2.6.37.orig/lib/Kconfig linux-2.6.37/lib/Kconfig ---- linux-2.6.37.orig/lib/Kconfig 2011-01-05 01:50:19.000000000 +0100 -+++ linux-2.6.37/lib/Kconfig 2011-03-01 20:10:29.833370667 +0100 -@@ -95,7 +95,7 @@ - # compression support is select'ed if needed - # - config ZLIB_INFLATE -- tristate -+ boolean - - config ZLIB_DEFLATE - tristate diff --git a/target/linux/patches/3.14.45/bsd-compatibility.patch b/target/linux/patches/3.14.45/bsd-compatibility.patch new file mode 100644 index 000000000..b954b658f --- /dev/null +++ b/target/linux/patches/3.14.45/bsd-compatibility.patch @@ -0,0 +1,2538 @@ +diff -Nur linux-3.11.5.orig/scripts/Makefile.lib linux-3.11.5/scripts/Makefile.lib +--- linux-3.11.5.orig/scripts/Makefile.lib 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/Makefile.lib 2013-10-16 18:09:31.000000000 +0200 +@@ -281,7 +281,12 @@ + size_append = printf $(shell \ + dec_size=0; \ + for F in $1; do \ +- fsize=$$(stat -c "%s" $$F); \ ++ if stat -qs .>/dev/null 2>&1; then \ ++ statcmd='stat -f %z'; \ ++ else \ ++ statcmd='stat -c %s'; \ ++ fi; \ ++ fsize=$$($$statcmd $$F); \ + dec_size=$$(expr $$dec_size + $$fsize); \ + done; \ + printf "%08x\n" $$dec_size | \ +diff -Nur linux-3.11.5.orig/scripts/mod/mk_elfconfig.c linux-3.11.5/scripts/mod/mk_elfconfig.c +--- linux-3.11.5.orig/scripts/mod/mk_elfconfig.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/mk_elfconfig.c 2013-10-16 18:09:31.000000000 +0200 +@@ -1,7 +1,18 @@ + #include + #include + #include +-#include ++ ++#define EI_NIDENT (16) ++#define ELFMAG "\177ELF" ++ ++#define SELFMAG 4 ++#define EI_CLASS 4 ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ + + int + main(int argc, char **argv) +diff -Nur linux-3.11.5.orig/scripts/mod/modpost.h linux-3.11.5/scripts/mod/modpost.h +--- linux-3.11.5.orig/scripts/mod/modpost.h 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/modpost.h 2013-10-16 18:09:31.000000000 +0200 +@@ -7,7 +7,2453 @@ + #include + #include + #include +-#include ++ ++ ++/* This file defines standard ELF types, structures, and macros. ++ Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++#ifndef _ELF_H ++#define _ELF_H 1 ++ ++__BEGIN_DECLS ++ ++/* Standard ELF types. */ ++ ++#include ++ ++/* Type for a 16-bit quantity. */ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++/* Types for signed and unsigned 32-bit quantities. */ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++/* Types for signed and unsigned 64-bit quantities. */ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++/* Type of addresses. */ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++/* Type of file offsets. */ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++/* Type for section indices, which are 16-bit quantities. */ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++/* Type for version symbol information. */ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++ ++/* The ELF file header. This appears at the start of every ELF file. */ ++ ++#define EI_NIDENT (16) ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf32_Half e_type; /* Object file type */ ++ Elf32_Half e_machine; /* Architecture */ ++ Elf32_Word e_version; /* Object file version */ ++ Elf32_Addr e_entry; /* Entry point virtual address */ ++ Elf32_Off e_phoff; /* Program header table file offset */ ++ Elf32_Off e_shoff; /* Section header table file offset */ ++ Elf32_Word e_flags; /* Processor-specific flags */ ++ Elf32_Half e_ehsize; /* ELF header size in bytes */ ++ Elf32_Half e_phentsize; /* Program header table entry size */ ++ Elf32_Half e_phnum; /* Program header table entry count */ ++ Elf32_Half e_shentsize; /* Section header table entry size */ ++ Elf32_Half e_shnum; /* Section header table entry count */ ++ Elf32_Half e_shstrndx; /* Section header string table index */ ++} Elf32_Ehdr; ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf64_Half e_type; /* Object file type */ ++ Elf64_Half e_machine; /* Architecture */ ++ Elf64_Word e_version; /* Object file version */ ++ Elf64_Addr e_entry; /* Entry point virtual address */ ++ Elf64_Off e_phoff; /* Program header table file offset */ ++ Elf64_Off e_shoff; /* Section header table file offset */ ++ Elf64_Word e_flags; /* Processor-specific flags */ ++ Elf64_Half e_ehsize; /* ELF header size in bytes */ ++ Elf64_Half e_phentsize; /* Program header table entry size */ ++ Elf64_Half e_phnum; /* Program header table entry count */ ++ Elf64_Half e_shentsize; /* Section header table entry size */ ++ Elf64_Half e_shnum; /* Section header table entry count */ ++ Elf64_Half e_shstrndx; /* Section header string table index */ ++} Elf64_Ehdr; ++ ++/* Fields in the e_ident array. The EI_* macros are indices into the ++ array. The macros under each EI_* macro are the values the byte ++ may have. */ ++ ++#define EI_MAG0 0 /* File identification byte 0 index */ ++#define ELFMAG0 0x7f /* Magic number byte 0 */ ++ ++#define EI_MAG1 1 /* File identification byte 1 index */ ++#define ELFMAG1 'E' /* Magic number byte 1 */ ++ ++#define EI_MAG2 2 /* File identification byte 2 index */ ++#define ELFMAG2 'L' /* Magic number byte 2 */ ++ ++#define EI_MAG3 3 /* File identification byte 3 index */ ++#define ELFMAG3 'F' /* Magic number byte 3 */ ++ ++/* Conglomeration of the identification bytes, for easy testing as a word. */ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 /* File class byte index */ ++#define ELFCLASSNONE 0 /* Invalid class */ ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATANONE 0 /* Invalid data encoding */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 /* File version byte index */ ++ /* Value must be EV_CURRENT */ ++ ++#define EI_OSABI 7 /* OS ABI identification */ ++#define ELFOSABI_NONE 0 /* UNIX System V ABI */ ++#define ELFOSABI_SYSV 0 /* Alias. */ ++#define ELFOSABI_HPUX 1 /* HP-UX */ ++#define ELFOSABI_NETBSD 2 /* NetBSD. */ ++#define ELFOSABI_LINUX 3 /* Linux. */ ++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ ++#define ELFOSABI_AIX 7 /* IBM AIX. */ ++#define ELFOSABI_IRIX 8 /* SGI Irix. */ ++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ ++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ ++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ ++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ ++#define ELFOSABI_ARM 97 /* ARM */ ++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ ++ ++#define EI_ABIVERSION 8 /* ABI version */ ++ ++#define EI_PAD 9 /* Byte index of padding bytes */ ++ ++/* Legal values for e_type (object file type). */ ++ ++#define ET_NONE 0 /* No file type */ ++#define ET_REL 1 /* Relocatable file */ ++#define ET_EXEC 2 /* Executable file */ ++#define ET_DYN 3 /* Shared object file */ ++#define ET_CORE 4 /* Core file */ ++#define ET_NUM 5 /* Number of defined types */ ++#define ET_LOOS 0xfe00 /* OS-specific range start */ ++#define ET_HIOS 0xfeff /* OS-specific range end */ ++#define ET_LOPROC 0xff00 /* Processor-specific range start */ ++#define ET_HIPROC 0xffff /* Processor-specific range end */ ++ ++/* Legal values for e_machine (architecture). */ ++ ++#define EM_NONE 0 /* No machine */ ++#define EM_M32 1 /* AT&T WE 32100 */ ++#define EM_SPARC 2 /* SUN SPARC */ ++#define EM_386 3 /* Intel 80386 */ ++#define EM_68K 4 /* Motorola m68k family */ ++#define EM_88K 5 /* Motorola m88k family */ ++#define EM_860 7 /* Intel 80860 */ ++#define EM_MIPS 8 /* MIPS R3000 big-endian */ ++#define EM_S370 9 /* IBM System/370 */ ++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ ++ ++#define EM_PARISC 15 /* HPPA */ ++#define EM_VPP500 17 /* Fujitsu VPP500 */ ++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ ++#define EM_960 19 /* Intel 80960 */ ++#define EM_PPC 20 /* PowerPC */ ++#define EM_PPC64 21 /* PowerPC 64-bit */ ++#define EM_S390 22 /* IBM S390 */ ++ ++#define EM_V800 36 /* NEC V800 series */ ++#define EM_FR20 37 /* Fujitsu FR20 */ ++#define EM_RH32 38 /* TRW RH-32 */ ++#define EM_RCE 39 /* Motorola RCE */ ++#define EM_ARM 40 /* ARM */ ++#define EM_FAKE_ALPHA 41 /* Digital Alpha */ ++#define EM_SH 42 /* Hitachi SH */ ++#define EM_SPARCV9 43 /* SPARC v9 64-bit */ ++#define EM_TRICORE 44 /* Siemens Tricore */ ++#define EM_ARC 45 /* Argonaut RISC Core */ ++#define EM_H8_300 46 /* Hitachi H8/300 */ ++#define EM_H8_300H 47 /* Hitachi H8/300H */ ++#define EM_H8S 48 /* Hitachi H8S */ ++#define EM_H8_500 49 /* Hitachi H8/500 */ ++#define EM_IA_64 50 /* Intel Merced */ ++#define EM_MIPS_X 51 /* Stanford MIPS-X */ ++#define EM_COLDFIRE 52 /* Motorola Coldfire */ ++#define EM_68HC12 53 /* Motorola M68HC12 */ ++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ ++#define EM_PCP 55 /* Siemens PCP */ ++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ ++#define EM_NDR1 57 /* Denso NDR1 microprocessor */ ++#define EM_STARCORE 58 /* Motorola Start*Core processor */ ++#define EM_ME16 59 /* Toyota ME16 processor */ ++#define EM_ST100 60 /* STMicroelectronic ST100 processor */ ++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ ++#define EM_X86_64 62 /* AMD x86-64 architecture */ ++#define EM_PDSP 63 /* Sony DSP Processor */ ++ ++#define EM_FX66 66 /* Siemens FX66 microcontroller */ ++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ ++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ ++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ ++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ ++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ ++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ ++#define EM_SVX 73 /* Silicon Graphics SVx */ ++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ ++#define EM_VAX 75 /* Digital VAX */ ++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ ++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ ++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ ++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ ++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ ++#define EM_HUANY 81 /* Harvard University machine-independent object files */ ++#define EM_PRISM 82 /* SiTera Prism */ ++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ ++#define EM_FR30 84 /* Fujitsu FR30 */ ++#define EM_D10V 85 /* Mitsubishi D10V */ ++#define EM_D30V 86 /* Mitsubishi D30V */ ++#define EM_V850 87 /* NEC v850 */ ++#define EM_M32R 88 /* Mitsubishi M32R */ ++#define EM_MN10300 89 /* Matsushita MN10300 */ ++#define EM_MN10200 90 /* Matsushita MN10200 */ ++#define EM_PJ 91 /* picoJava */ ++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ ++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ ++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ ++#define EM_NUM 95 ++ ++/* If it is necessary to assign new unofficial EM_* values, please ++ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the ++ chances of collision with official or non-GNU unofficial values. */ ++ ++#define EM_ALPHA 0x9026 ++ ++/* Legal values for e_version (version). */ ++ ++#define EV_NONE 0 /* Invalid ELF version */ ++#define EV_CURRENT 1 /* Current version */ ++#define EV_NUM 2 ++ ++/* Section header. */ ++ ++typedef struct ++{ ++ Elf32_Word sh_name; /* Section name (string tbl index) */ ++ Elf32_Word sh_type; /* Section type */ ++ Elf32_Word sh_flags; /* Section flags */ ++ Elf32_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf32_Off sh_offset; /* Section file offset */ ++ Elf32_Word sh_size; /* Section size in bytes */ ++ Elf32_Word sh_link; /* Link to another section */ ++ Elf32_Word sh_info; /* Additional section information */ ++ Elf32_Word sh_addralign; /* Section alignment */ ++ Elf32_Word sh_entsize; /* Entry size if section holds table */ ++} Elf32_Shdr; ++ ++typedef struct ++{ ++ Elf64_Word sh_name; /* Section name (string tbl index) */ ++ Elf64_Word sh_type; /* Section type */ ++ Elf64_Xword sh_flags; /* Section flags */ ++ Elf64_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf64_Off sh_offset; /* Section file offset */ ++ Elf64_Xword sh_size; /* Section size in bytes */ ++ Elf64_Word sh_link; /* Link to another section */ ++ Elf64_Word sh_info; /* Additional section information */ ++ Elf64_Xword sh_addralign; /* Section alignment */ ++ Elf64_Xword sh_entsize; /* Entry size if section holds table */ ++} Elf64_Shdr; ++ ++/* Special section indices. */ ++ ++#define SHN_UNDEF 0 /* Undefined section */ ++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ ++#define SHN_LOPROC 0xff00 /* Start of processor-specific */ ++#define SHN_HIPROC 0xff1f /* End of processor-specific */ ++#define SHN_LOOS 0xff20 /* Start of OS-specific */ ++#define SHN_HIOS 0xff3f /* End of OS-specific */ ++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ ++#define SHN_COMMON 0xfff2 /* Associated symbol is common */ ++#define SHN_XINDEX 0xffff /* Index is in extra table. */ ++#define SHN_HIRESERVE 0xffff /* End of reserved indices */ ++ ++/* Legal values for sh_type (section type). */ ++ ++#define SHT_NULL 0 /* Section header table entry unused */ ++#define SHT_PROGBITS 1 /* Program data */ ++#define SHT_SYMTAB 2 /* Symbol table */ ++#define SHT_STRTAB 3 /* String table */ ++#define SHT_RELA 4 /* Relocation entries with addends */ ++#define SHT_HASH 5 /* Symbol hash table */ ++#define SHT_DYNAMIC 6 /* Dynamic linking information */ ++#define SHT_NOTE 7 /* Notes */ ++#define SHT_NOBITS 8 /* Program space with no data (bss) */ ++#define SHT_REL 9 /* Relocation entries, no addends */ ++#define SHT_SHLIB 10 /* Reserved */ ++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ ++#define SHT_INIT_ARRAY 14 /* Array of constructors */ ++#define SHT_FINI_ARRAY 15 /* Array of destructors */ ++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ ++#define SHT_GROUP 17 /* Section group */ ++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ ++#define SHT_NUM 19 /* Number of defined types. */ ++#define SHT_LOOS 0x60000000 /* Start OS-specific */ ++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ ++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ ++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ ++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ ++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ ++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ ++#define SHT_HIOS 0x6fffffff /* End OS-specific type */ ++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define SHT_LOUSER 0x80000000 /* Start of application-specific */ ++#define SHT_HIUSER 0x8fffffff /* End of application-specific */ ++ ++/* Legal values for sh_flags (section flags). */ ++ ++#define SHF_WRITE (1 << 0) /* Writable */ ++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ ++#define SHF_EXECINSTR (1 << 2) /* Executable */ ++#define SHF_MERGE (1 << 4) /* Might be merged */ ++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ ++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ ++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ ++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling ++ required */ ++#define SHF_GROUP (1 << 9) /* Section is member of a group. */ ++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ ++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ ++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Section group handling. */ ++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ ++ ++/* Symbol table entry. */ ++ ++typedef struct ++{ ++ Elf32_Word st_name; /* Symbol name (string tbl index) */ ++ Elf32_Addr st_value; /* Symbol value */ ++ Elf32_Word st_size; /* Symbol size */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf32_Section st_shndx; /* Section index */ ++} Elf32_Sym; ++ ++typedef struct ++{ ++ Elf64_Word st_name; /* Symbol name (string tbl index) */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf64_Section st_shndx; /* Section index */ ++ Elf64_Addr st_value; /* Symbol value */ ++ Elf64_Xword st_size; /* Symbol size */ ++} Elf64_Sym; ++ ++/* The syminfo section if available contains additional information about ++ every dynamic symbol. */ ++ ++typedef struct ++{ ++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf32_Half si_flags; /* Per symbol flags */ ++} Elf32_Syminfo; ++ ++typedef struct ++{ ++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf64_Half si_flags; /* Per symbol flags */ ++} Elf64_Syminfo; ++ ++/* Possible values for si_boundto. */ ++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ ++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ ++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ ++ ++/* Possible bitmasks for si_flags. */ ++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ ++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ ++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ ++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy ++ loaded */ ++/* Syminfo version values. */ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++ ++/* How to extract and insert information held in the st_info field. */ ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++/* Legal values for ST_BIND subfield of st_info (symbol binding). */ ++ ++#define STB_LOCAL 0 /* Local symbol */ ++#define STB_GLOBAL 1 /* Global symbol */ ++#define STB_WEAK 2 /* Weak symbol */ ++#define STB_NUM 3 /* Number of defined types. */ ++#define STB_LOOS 10 /* Start of OS-specific */ ++#define STB_HIOS 12 /* End of OS-specific */ ++#define STB_LOPROC 13 /* Start of processor-specific */ ++#define STB_HIPROC 15 /* End of processor-specific */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_NOTYPE 0 /* Symbol type is unspecified */ ++#define STT_OBJECT 1 /* Symbol is a data object */ ++#define STT_FUNC 2 /* Symbol is a code object */ ++#define STT_SECTION 3 /* Symbol associated with a section */ ++#define STT_FILE 4 /* Symbol's name is file name */ ++#define STT_COMMON 5 /* Symbol is a common data object */ ++#define STT_TLS 6 /* Symbol is thread-local data object*/ ++#define STT_NUM 7 /* Number of defined types. */ ++#define STT_LOOS 10 /* Start of OS-specific */ ++#define STT_HIOS 12 /* End of OS-specific */ ++#define STT_LOPROC 13 /* Start of processor-specific */ ++#define STT_HIPROC 15 /* End of processor-specific */ ++ ++ ++/* Symbol table indices are found in the hash buckets and chain table ++ of a symbol hash table section. This special index value indicates ++ the end of a chain, meaning no further symbols are found in that bucket. */ ++ ++#define STN_UNDEF 0 /* End of a chain. */ ++ ++ ++/* How to extract and insert information held in the st_other field. */ ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++ ++/* For ELF64 the definitions are the same. */ ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++/* Symbol visibility specification encoded in the st_other field. */ ++#define STV_DEFAULT 0 /* Default symbol visibility rules */ ++#define STV_INTERNAL 1 /* Processor specific hidden class */ ++#define STV_HIDDEN 2 /* Sym unavailable in other modules */ ++#define STV_PROTECTED 3 /* Not preemptible, not exported */ ++ ++ ++/* Relocation table entry without addend (in section of type SHT_REL). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++} Elf32_Rel; ++ ++/* I have seen two different definitions of the Elf64_Rel and ++ Elf64_Rela structures, so we'll leave them out until Novell (or ++ whoever) gets their act together. */ ++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++} Elf64_Rel; ++ ++/* Relocation table entry with addend (in section of type SHT_RELA). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++ Elf32_Sword r_addend; /* Addend */ ++} Elf32_Rela; ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++ Elf64_Sxword r_addend; /* Addend */ ++} Elf64_Rela; ++ ++/* How to extract and insert information held in the r_info field. */ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++/* Program segment header. */ ++ ++typedef struct ++{ ++ Elf32_Word p_type; /* Segment type */ ++ Elf32_Off p_offset; /* Segment file offset */ ++ Elf32_Addr p_vaddr; /* Segment virtual address */ ++ Elf32_Addr p_paddr; /* Segment physical address */ ++ Elf32_Word p_filesz; /* Segment size in file */ ++ Elf32_Word p_memsz; /* Segment size in memory */ ++ Elf32_Word p_flags; /* Segment flags */ ++ Elf32_Word p_align; /* Segment alignment */ ++} Elf32_Phdr; ++ ++typedef struct ++{ ++ Elf64_Word p_type; /* Segment type */ ++ Elf64_Word p_flags; /* Segment flags */ ++ Elf64_Off p_offset; /* Segment file offset */ ++ Elf64_Addr p_vaddr; /* Segment virtual address */ ++ Elf64_Addr p_paddr; /* Segment physical address */ ++ Elf64_Xword p_filesz; /* Segment size in file */ ++ Elf64_Xword p_memsz; /* Segment size in memory */ ++ Elf64_Xword p_align; /* Segment alignment */ ++} Elf64_Phdr; ++ ++/* Legal values for p_type (segment type). */ ++ ++#define PT_NULL 0 /* Program header table entry unused */ ++#define PT_LOAD 1 /* Loadable program segment */ ++#define PT_DYNAMIC 2 /* Dynamic linking information */ ++#define PT_INTERP 3 /* Program interpreter */ ++#define PT_NOTE 4 /* Auxiliary information */ ++#define PT_SHLIB 5 /* Reserved */ ++#define PT_PHDR 6 /* Entry for header table itself */ ++#define PT_TLS 7 /* Thread-local storage segment */ ++#define PT_NUM 8 /* Number of defined types */ ++#define PT_LOOS 0x60000000 /* Start of OS-specific */ ++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ ++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ ++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff /* End of OS-specific */ ++#define PT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define PT_HIPROC 0x7fffffff /* End of processor-specific */ ++ ++/* Legal values for p_flags (segment flags). */ ++ ++#define PF_X (1 << 0) /* Segment is executable */ ++#define PF_W (1 << 1) /* Segment is writable */ ++#define PF_R (1 << 2) /* Segment is readable */ ++#define PF_MASKOS 0x0ff00000 /* OS-specific */ ++#define PF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Legal values for note segment descriptor types for core files. */ ++ ++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ ++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ ++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ ++#define NT_PRXREG 4 /* Contains copy of prxregset struct */ ++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ ++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ ++#define NT_AUXV 6 /* Contains copy of auxv array */ ++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ ++#define NT_ASRS 8 /* Contains copy of asrset struct */ ++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ ++#define NT_PSINFO 13 /* Contains copy of psinfo struct */ ++#define NT_PRCRED 14 /* Contains copy of prcred struct */ ++#define NT_UTSNAME 15 /* Contains copy of utsname struct */ ++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ ++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ ++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ ++ ++/* Legal values for the note segment descriptor types for object files. */ ++ ++#define NT_VERSION 1 /* Contains a version string. */ ++ ++ ++/* Dynamic section entry. */ ++ ++typedef struct ++{ ++ Elf32_Sword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf32_Word d_val; /* Integer value */ ++ Elf32_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct ++{ ++ Elf64_Sxword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf64_Xword d_val; /* Integer value */ ++ Elf64_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf64_Dyn; ++ ++/* Legal values for d_tag (dynamic entry type). */ ++ ++#define DT_NULL 0 /* Marks end of dynamic section */ ++#define DT_NEEDED 1 /* Name of needed library */ ++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ ++#define DT_PLTGOT 3 /* Processor defined value */ ++#define DT_HASH 4 /* Address of symbol hash table */ ++#define DT_STRTAB 5 /* Address of string table */ ++#define DT_SYMTAB 6 /* Address of symbol table */ ++#define DT_RELA 7 /* Address of Rela relocs */ ++#define DT_RELASZ 8 /* Total size of Rela relocs */ ++#define DT_RELAENT 9 /* Size of one Rela reloc */ ++#define DT_STRSZ 10 /* Size of string table */ ++#define DT_SYMENT 11 /* Size of one symbol table entry */ ++#define DT_INIT 12 /* Address of init function */ ++#define DT_FINI 13 /* Address of termination function */ ++#define DT_SONAME 14 /* Name of shared object */ ++#define DT_RPATH 15 /* Library search path (deprecated) */ ++#define DT_SYMBOLIC 16 /* Start symbol search here */ ++#define DT_REL 17 /* Address of Rel relocs */ ++#define DT_RELSZ 18 /* Total size of Rel relocs */ ++#define DT_RELENT 19 /* Size of one Rel reloc */ ++#define DT_PLTREL 20 /* Type of reloc in PLT */ ++#define DT_DEBUG 21 /* For debugging; unspecified */ ++#define DT_TEXTREL 22 /* Reloc might modify .text */ ++#define DT_JMPREL 23 /* Address of PLT relocs */ ++#define DT_BIND_NOW 24 /* Process relocations of object */ ++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ ++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ ++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ ++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ ++#define DT_RUNPATH 29 /* Library search path */ ++#define DT_FLAGS 30 /* Flags for the object being loaded */ ++#define DT_ENCODING 32 /* Start of encoded range */ ++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ ++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ ++#define DT_NUM 34 /* Number used */ ++#define DT_LOOS 0x6000000d /* Start of OS-specific */ ++#define DT_HIOS 0x6ffff000 /* End of OS-specific */ ++#define DT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define DT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ ++ ++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the ++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's ++ approach. */ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ ++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting ++ the following DT_* entry. */ ++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ ++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ ++#define DT_VALNUM 12 ++ ++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the ++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. ++ ++ If any adjustment is made to the ELF object after it has been ++ built these entries will need to be adjusted. */ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ ++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ ++#define DT_CONFIG 0x6ffffefa /* Configuration information. */ ++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ ++#define DT_AUDIT 0x6ffffefc /* Object auditing. */ ++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ ++#define DT_MOVETAB 0x6ffffefe /* Move table. */ ++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ ++#define DT_ADDRNUM 10 ++ ++/* The versioning entry types. The next are defined as part of the ++ GNU extension. */ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++/* These were chosen by Sun. */ ++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ ++#define DT_VERDEF 0x6ffffffc /* Address of version definition ++ table */ ++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ ++#define DT_VERNEED 0x6ffffffe /* Address of table with needed ++ versions */ ++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ ++#define DT_VERSIONTAGNUM 16 ++ ++/* Sun added these machine-independent extensions in the "processor-specific" ++ range. Be compatible. */ ++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ ++#define DT_FILTER 0x7fffffff /* Shared object to get values from */ ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++/* Values of `d_un.d_val' in the DT_FLAGS entry. */ ++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ ++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ ++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ ++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ ++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ ++ ++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 ++ entry in the dynamic section. */ ++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ ++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ ++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ ++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ ++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ ++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ ++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ ++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ ++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ ++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ ++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ ++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ ++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ ++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ ++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ ++ ++/* Flags for the feature selection in DT_FEATURE_1. */ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ ++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ ++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not ++ generally available. */ ++ ++/* Version definition sections. */ ++ ++typedef struct ++{ ++ Elf32_Half vd_version; /* Version revision */ ++ Elf32_Half vd_flags; /* Version information */ ++ Elf32_Half vd_ndx; /* Version Index */ ++ Elf32_Half vd_cnt; /* Number of associated aux entries */ ++ Elf32_Word vd_hash; /* Version name hash value */ ++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf32_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf32_Verdef; ++ ++typedef struct ++{ ++ Elf64_Half vd_version; /* Version revision */ ++ Elf64_Half vd_flags; /* Version information */ ++ Elf64_Half vd_ndx; /* Version Index */ ++ Elf64_Half vd_cnt; /* Number of associated aux entries */ ++ Elf64_Word vd_hash; /* Version name hash value */ ++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf64_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf64_Verdef; ++ ++ ++/* Legal values for vd_version (version revision). */ ++#define VER_DEF_NONE 0 /* No version */ ++#define VER_DEF_CURRENT 1 /* Current version */ ++#define VER_DEF_NUM 2 /* Given version number */ ++ ++/* Legal values for vd_flags (version information flags). */ ++#define VER_FLG_BASE 0x1 /* Version definition of file itself */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++/* Versym symbol index values. */ ++#define VER_NDX_LOCAL 0 /* Symbol is local. */ ++#define VER_NDX_GLOBAL 1 /* Symbol is global. */ ++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ ++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ ++ ++/* Auxialiary version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vda_name; /* Version or dependency names */ ++ Elf32_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf32_Verdaux; ++ ++typedef struct ++{ ++ Elf64_Word vda_name; /* Version or dependency names */ ++ Elf64_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf64_Verdaux; ++ ++ ++/* Version dependency section. */ ++ ++typedef struct ++{ ++ Elf32_Half vn_version; /* Version of structure */ ++ Elf32_Half vn_cnt; /* Number of associated aux entries */ ++ Elf32_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf32_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf32_Verneed; ++ ++typedef struct ++{ ++ Elf64_Half vn_version; /* Version of structure */ ++ Elf64_Half vn_cnt; /* Number of associated aux entries */ ++ Elf64_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf64_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf64_Verneed; ++ ++ ++/* Legal values for vn_version (version revision). */ ++#define VER_NEED_NONE 0 /* No version */ ++#define VER_NEED_CURRENT 1 /* Current version */ ++#define VER_NEED_NUM 2 /* Given version number */ ++ ++/* Auxiliary needed version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vna_hash; /* Hash value of dependency name */ ++ Elf32_Half vna_flags; /* Dependency specific information */ ++ Elf32_Half vna_other; /* Unused */ ++ Elf32_Word vna_name; /* Dependency name string offset */ ++ Elf32_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf32_Vernaux; ++ ++typedef struct ++{ ++ Elf64_Word vna_hash; /* Hash value of dependency name */ ++ Elf64_Half vna_flags; /* Dependency specific information */ ++ Elf64_Half vna_other; /* Unused */ ++ Elf64_Word vna_name; /* Dependency name string offset */ ++ Elf64_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf64_Vernaux; ++ ++ ++/* Legal values for vna_flags. */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++ ++/* Auxiliary vector. */ ++ ++/* This vector is normally only used by the program interpreter. The ++ usual definition in an ABI supplement uses the name auxv_t. The ++ vector is not usually defined in a standard file, but it ++ can't hurt. We rename it to avoid conflicts. The sizes of these ++ types are an arrangement between the exec server and the program ++ interpreter, so we don't fully specify them here. */ ++ ++typedef struct ++{ ++ int a_type; /* Entry type */ ++ union ++ { ++ long int a_val; /* Integer value */ ++ void *a_ptr; /* Pointer value */ ++ void (*a_fcn) (void); /* Function pointer value */ ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct ++{ ++ long int a_type; /* Entry type */ ++ union ++ { ++ long int a_val; /* Integer value */ ++ void *a_ptr; /* Pointer value */ ++ void (*a_fcn) (void); /* Function pointer value */ ++ } a_un; ++} Elf64_auxv_t; ++ ++/* Legal values for a_type (entry type). */ ++ ++#define AT_NULL 0 /* End of vector */ ++#define AT_IGNORE 1 /* Entry should be ignored */ ++#define AT_EXECFD 2 /* File descriptor of program */ ++#define AT_PHDR 3 /* Program headers for program */ ++#define AT_PHENT 4 /* Size of program header entry */ ++#define AT_PHNUM 5 /* Number of program headers */ ++#define AT_PAGESZ 6 /* System page size */ ++#define AT_BASE 7 /* Base address of interpreter */ ++#define AT_FLAGS 8 /* Flags */ ++#define AT_ENTRY 9 /* Entry point of program */ ++#define AT_NOTELF 10 /* Program is not ELF */ ++#define AT_UID 11 /* Real uid */ ++#define AT_EUID 12 /* Effective uid */ ++#define AT_GID 13 /* Real gid */ ++#define AT_EGID 14 /* Effective gid */ ++#define AT_CLKTCK 17 /* Frequency of times() */ ++ ++/* Some more special a_type values describing the hardware. */ ++#define AT_PLATFORM 15 /* String identifying platform. */ ++#define AT_HWCAP 16 /* Machine dependent hints about ++ processor capabilities. */ ++ ++/* This entry gives some information about the FPU initialization ++ performed by the kernel. */ ++#define AT_FPUCW 18 /* Used FPU control word. */ ++ ++/* Cache block sizes. */ ++#define AT_DCACHEBSIZE 19 /* Data cache block size. */ ++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ ++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ ++ ++/* A special ignored value for PPC, used by the kernel to control the ++ interpretation of the AUXV. Must be > 16. */ ++#define AT_IGNOREPPC 22 /* Entry should be ignored. */ ++ ++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ ++ ++/* Pointer to the global system page used for system calls and other ++ nice things. */ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++ ++/* Note section contents. Each entry in the note section begins with ++ a header of a fixed form. */ ++ ++typedef struct ++{ ++ Elf32_Word n_namesz; /* Length of the note's name. */ ++ Elf32_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf32_Word n_type; /* Type of the note. */ ++} Elf32_Nhdr; ++ ++typedef struct ++{ ++ Elf64_Word n_namesz; /* Length of the note's name. */ ++ Elf64_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf64_Word n_type; /* Type of the note. */ ++} Elf64_Nhdr; ++ ++/* Known names of notes. */ ++ ++/* Solaris entries in the note section have this name. */ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++/* Note entries for GNU systems have this name. */ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++/* Defined types of notes for Solaris. */ ++ ++/* Value of descriptor (one word) is desired pagesize for the binary. */ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++/* Defined note types for GNU systems. */ ++ ++/* ABI information. The descriptor consists of words: ++ word 0: OS descriptor ++ word 1: major version of the ABI ++ word 2: minor version of the ABI ++ word 3: subminor version of the ABI ++*/ ++#define ELF_NOTE_ABI 1 ++ ++/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI ++ note section entry. */ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++ ++/* Move records. */ ++typedef struct ++{ ++ Elf32_Xword m_value; /* Symbol value. */ ++ Elf32_Word m_info; /* Size and index. */ ++ Elf32_Word m_poffset; /* Symbol offset. */ ++ Elf32_Half m_repeat; /* Repeat count. */ ++ Elf32_Half m_stride; /* Stride info. */ ++} Elf32_Move; ++ ++typedef struct ++{ ++ Elf64_Xword m_value; /* Symbol value. */ ++ Elf64_Xword m_info; /* Size and index. */ ++ Elf64_Xword m_poffset; /* Symbol offset. */ ++ Elf64_Half m_repeat; /* Repeat count. */ ++ Elf64_Half m_stride; /* Stride info. */ ++} Elf64_Move; ++ ++/* Macro to construct move records. */ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++ ++/* Motorola 68k specific definitions. */ ++ ++/* Values for Elf32_Ehdr.e_flags. */ ++#define EF_CPU32 0x00810000 ++ ++/* m68k relocs. */ ++ ++#define R_68K_NONE 0 /* No reloc */ ++#define R_68K_32 1 /* Direct 32 bit */ ++#define R_68K_16 2 /* Direct 16 bit */ ++#define R_68K_8 3 /* Direct 8 bit */ ++#define R_68K_PC32 4 /* PC relative 32 bit */ ++#define R_68K_PC16 5 /* PC relative 16 bit */ ++#define R_68K_PC8 6 /* PC relative 8 bit */ ++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ ++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ ++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ ++#define R_68K_GOT32O 10 /* 32 bit GOT offset */ ++#define R_68K_GOT16O 11 /* 16 bit GOT offset */ ++#define R_68K_GOT8O 12 /* 8 bit GOT offset */ ++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ ++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ ++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ ++#define R_68K_PLT32O 16 /* 32 bit PLT offset */ ++#define R_68K_PLT16O 17 /* 16 bit PLT offset */ ++#define R_68K_PLT8O 18 /* 8 bit PLT offset */ ++#define R_68K_COPY 19 /* Copy symbol at runtime */ ++#define R_68K_GLOB_DAT 20 /* Create GOT entry */ ++#define R_68K_JMP_SLOT 21 /* Create PLT entry */ ++#define R_68K_RELATIVE 22 /* Adjust by program base */ ++/* Keep this the last entry. */ ++#define R_68K_NUM 23 ++ ++/* Intel 80386 specific definitions. */ ++ ++/* i386 relocs. */ ++ ++#define R_386_NONE 0 /* No reloc */ ++#define R_386_32 1 /* Direct 32 bit */ ++#define R_386_PC32 2 /* PC relative 32 bit */ ++#define R_386_GOT32 3 /* 32 bit GOT entry */ ++#define R_386_PLT32 4 /* 32 bit PLT address */ ++#define R_386_COPY 5 /* Copy symbol at runtime */ ++#define R_386_GLOB_DAT 6 /* Create GOT entry */ ++#define R_386_JMP_SLOT 7 /* Create PLT entry */ ++#define R_386_RELATIVE 8 /* Adjust by program base */ ++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ ++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ ++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS ++ block offset */ ++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block ++ offset */ ++#define R_386_TLS_LE 17 /* Offset relative to static TLS ++ block */ ++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of ++ general dynamic thread local data */ ++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of ++ local dynamic thread local data ++ in LE code */ ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic ++ thread local data */ ++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ ++#define R_386_TLS_GD_CALL 26 /* Relocation for call to ++ __tls_get_addr() */ ++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ ++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic ++ thread local data in LE code */ ++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ ++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to ++ __tls_get_addr() in LDM code */ ++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ ++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ ++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS ++ block offset */ ++#define R_386_TLS_LE_32 34 /* Negated offset relative to static ++ TLS block */ ++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ ++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ ++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ ++/* Keep this the last entry. */ ++#define R_386_NUM 38 ++ ++/* SUN SPARC specific definitions. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_REGISTER 13 /* Global register reserved to app. */ ++ ++/* Values for Elf64_Ehdr.e_flags. */ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 /* little endian data */ ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ ++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ ++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ ++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ ++ ++/* SPARC relocs. */ ++ ++#define R_SPARC_NONE 0 /* No reloc */ ++#define R_SPARC_8 1 /* Direct 8 bit */ ++#define R_SPARC_16 2 /* Direct 16 bit */ ++#define R_SPARC_32 3 /* Direct 32 bit */ ++#define R_SPARC_DISP8 4 /* PC relative 8 bit */ ++#define R_SPARC_DISP16 5 /* PC relative 16 bit */ ++#define R_SPARC_DISP32 6 /* PC relative 32 bit */ ++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ ++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ ++#define R_SPARC_HI22 9 /* High 22 bit */ ++#define R_SPARC_22 10 /* Direct 22 bit */ ++#define R_SPARC_13 11 /* Direct 13 bit */ ++#define R_SPARC_LO10 12 /* Truncated 10 bit */ ++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ ++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ ++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ ++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ ++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ ++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ ++#define R_SPARC_COPY 19 /* Copy symbol at runtime */ ++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ ++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ ++#define R_SPARC_RELATIVE 22 /* Adjust by program base */ ++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ ++ ++/* Additional Sparc64 relocs. */ ++ ++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ ++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ ++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ ++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ ++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ ++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ ++#define R_SPARC_10 30 /* Direct 10 bit */ ++#define R_SPARC_11 31 /* Direct 11 bit */ ++#define R_SPARC_64 32 /* Direct 64 bit */ ++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ ++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ ++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ ++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ ++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ ++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ ++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ ++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ ++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ ++#define R_SPARC_7 43 /* Direct 7 bit */ ++#define R_SPARC_5 44 /* Direct 5 bit */ ++#define R_SPARC_6 45 /* Direct 6 bit */ ++#define R_SPARC_DISP64 46 /* PC relative 64 bit */ ++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ ++#define R_SPARC_HIX22 48 /* High 22 bit complemented */ ++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ ++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ ++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ ++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ ++#define R_SPARC_REGISTER 53 /* Global register usage */ ++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ ++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++/* Keep this the last entry. */ ++#define R_SPARC_NUM 80 ++ ++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++/* Bits present in AT_HWCAP, primarily for Sparc32. */ ++ ++#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ ++#define HWCAP_SPARC_STBAR 2 ++#define HWCAP_SPARC_SWAP 4 ++#define HWCAP_SPARC_MULDIV 8 ++#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ ++#define HWCAP_SPARC_ULTRA3 32 ++ ++/* MIPS R3000 specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ ++#define EF_MIPS_PIC 2 /* Contains PIC code */ ++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ ++ ++/* Legal values for MIPS architecture level. */ ++ ++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* The following are non-official names and should not be used. */ ++ ++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* Special section indices. */ ++ ++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ ++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ ++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ ++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ ++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ ++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ ++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ ++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ ++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++/* Symbol tables. */ ++ ++/* MIPS specific values for `st_other'. */ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++/* MIPS specific values for `st_info'. */ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++/* Entries found in sections of type SHT_MIPS_GPTAB. */ ++ ++typedef union ++{ ++ struct ++ { ++ Elf32_Word gt_current_g_value; /* -G value used for compilation */ ++ Elf32_Word gt_unused; /* Not used */ ++ } gt_header; /* First entry in section */ ++ struct ++ { ++ Elf32_Word gt_g_value; /* If this value were used for -G */ ++ Elf32_Word gt_bytes; /* This many bytes would be used */ ++ } gt_entry; /* Subsequent entries in section */ ++} Elf32_gptab; ++ ++/* Entry found in sections of type SHT_MIPS_REGINFO. */ ++ ++typedef struct ++{ ++ Elf32_Word ri_gprmask; /* General registers used */ ++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ ++ Elf32_Sword ri_gp_value; /* $gp register value */ ++} Elf32_RegInfo; ++ ++/* Entries found in sections of type SHT_MIPS_OPTIONS. */ ++ ++typedef struct ++{ ++ unsigned char kind; /* Determines interpretation of the ++ variable part of descriptor. */ ++ unsigned char size; /* Size of descriptor, including header. */ ++ Elf32_Section section; /* Section header index of section affected, ++ 0 for global options. */ ++ Elf32_Word info; /* Kind-specific information. */ ++} Elf_Options; ++ ++/* Values for `kind' field in Elf_Options. */ ++ ++#define ODK_NULL 0 /* Undefined. */ ++#define ODK_REGINFO 1 /* Register usage information. */ ++#define ODK_EXCEPTIONS 2 /* Exception processing options. */ ++#define ODK_PAD 3 /* Section padding options. */ ++#define ODK_HWPATCH 4 /* Hardware workarounds performed */ ++#define ODK_FILL 5 /* record the fill value used by the linker. */ ++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ ++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ ++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ ++ ++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ ++ ++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ ++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ ++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ ++#define OEX_SMM 0x20000 /* Force sequential memory mode? */ ++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ ++ ++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ ++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ ++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ ++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++/* Entry found in `.options' section. */ ++ ++typedef struct ++{ ++ Elf32_Word hwp_flags1; /* Extra flags. */ ++ Elf32_Word hwp_flags2; /* Extra flags. */ ++} Elf_Options_Hw; ++ ++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++/* MIPS relocs. */ ++ ++#define R_MIPS_NONE 0 /* No reloc */ ++#define R_MIPS_16 1 /* Direct 16 bit */ ++#define R_MIPS_32 2 /* Direct 32 bit */ ++#define R_MIPS_REL32 3 /* PC relative 32 bit */ ++#define R_MIPS_26 4 /* Direct 26 bit shifted */ ++#define R_MIPS_HI16 5 /* High 16 bit */ ++#define R_MIPS_LO16 6 /* Low 16 bit */ ++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ ++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ ++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ ++#define R_MIPS_PC16 10 /* PC relative 16 bit */ ++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ ++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++/* Keep this the last entry. */ ++#define R_MIPS_NUM 38 ++ ++/* Legal values for p_type field of Elf32_Phdr. */ ++ ++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ ++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++/* Special program header types. */ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++/* Legal values for d_tag field of Elf32_Dyn. */ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ ++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ ++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ ++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ ++#define DT_MIPS_FLAGS 0x70000005 /* Flags */ ++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ ++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ ++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ ++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ ++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ ++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ ++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ ++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ ++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ ++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in ++ DT_MIPS_DELTA_CLASS. */ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in ++ DT_MIPS_DELTA_INSTANCE. */ ++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in ++ DT_MIPS_DELTA_RELOC. */ ++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta ++ relocations refer to. */ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in ++ DT_MIPS_DELTA_SYM. */ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the ++ class declaration. */ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in ++ DT_MIPS_DELTA_CLASSSYM. */ ++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ ++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve ++ function stored in GOT. */ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added ++ by rld on dlopen() calls. */ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ ++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ ++#define DT_MIPS_NUM 0x32 ++ ++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ ++ ++#define RHF_NONE 0 /* No flags */ ++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ ++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++/* Entries found in sections of type SHT_MIPS_LIBLIST. */ ++ ++typedef struct ++{ ++ Elf32_Word l_name; /* Name (string table index) */ ++ Elf32_Word l_time_stamp; /* Timestamp */ ++ Elf32_Word l_checksum; /* Checksum */ ++ Elf32_Word l_version; /* Interface version */ ++ Elf32_Word l_flags; /* Flags */ ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; /* Name (string table index) */ ++ Elf64_Word l_time_stamp; /* Timestamp */ ++ Elf64_Word l_checksum; /* Checksum */ ++ Elf64_Word l_version; /* Interface version */ ++ Elf64_Word l_flags; /* Flags */ ++} Elf64_Lib; ++ ++ ++/* Legal values for l_flags. */ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ ++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++/* Entries found in sections of type SHT_MIPS_CONFLICT. */ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++/* HPPA specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ ++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ ++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ ++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ ++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch ++ prediction. */ ++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ ++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ ++ ++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ ++ ++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ ++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ ++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ ++ ++/* Additional section indeces. */ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared ++ symbols in ANSI C. */ ++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ ++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ ++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ ++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ ++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++/* HPPA relocs. */ ++ ++#define R_PARISC_NONE 0 /* No reloc. */ ++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ ++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ ++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ ++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ ++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ ++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ ++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ ++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ ++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ ++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ ++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ ++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ ++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ ++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ ++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ ++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ ++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ ++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ ++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ ++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ ++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ ++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ ++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ ++#define R_PARISC_FPTR64 64 /* 64 bits function address. */ ++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ ++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ ++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ ++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ ++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ ++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ ++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ ++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ ++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ ++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ ++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ ++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ ++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ ++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ ++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ ++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 /* Copy relocation. */ ++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ ++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ ++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ ++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ ++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ ++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ ++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ ++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ ++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_HIRESERVE 255 ++ ++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++/* Alpha specific definitions. */ ++ ++/* Legal values for e_flags field of Elf64_Ehdr. */ ++ ++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ ++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ ++ ++/* Legal values for sh_type field of Elf64_Shdr. */ ++ ++/* These two are primerily concerned with ECOFF debugging info. */ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++/* Legal values for sh_flags field of Elf64_Shdr. */ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++/* Legal values for st_other field of Elf64_Sym. */ ++#define STO_ALPHA_NOPV 0x80 /* No PV required. */ ++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ ++ ++/* Alpha relocs. */ ++ ++#define R_ALPHA_NONE 0 /* No reloc */ ++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ ++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ ++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ ++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ ++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ ++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ ++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ ++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ ++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ ++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ ++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ ++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ ++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ ++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ ++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ ++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ ++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ ++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++/* Keep this the last entry. */ ++#define R_ALPHA_NUM 46 ++ ++/* Magic values of the LITUSE relocation addend. */ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++ ++/* PowerPC specific declarations */ ++ ++/* Values for Elf32/64_Ehdr.e_flags. */ ++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ ++ ++/* Cygnus local bits below */ ++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib ++ flag */ ++ ++/* PowerPC relocations defined by the ABIs */ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 /* 32bit absolute address */ ++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ ++#define R_PPC_ADDR16 3 /* 16bit absolute address */ ++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ ++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ ++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ ++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 /* PC relative 26 bit */ ++#define R_PPC_REL14 11 /* PC relative 16 bit */ ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++/* PowerPC relocations defined for the TLS access ABI. */ ++#define R_PPC_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ ++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ ++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ ++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ ++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ ++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ ++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ ++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ ++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ ++ ++/* Keep this the last entry. */ ++#define R_PPC_NUM 95 ++ ++/* The remaining relocs are from the Embedded ELF ABI, and are not ++ in the SVR4 ELF ABI. */ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ ++ ++/* Diab tool relocations. */ ++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ ++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ ++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ ++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ ++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ ++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ ++ ++/* This is a phony reloc to handle any old fashioned TOC16 references ++ that may still be in object files. */ ++#define R_PPC_TOC16 255 ++ ++ ++/* PowerPC64 relocations defined by the ABIs */ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ ++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ ++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ ++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ ++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ ++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ ++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ ++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ ++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ ++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ ++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ ++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ ++#define R_PPC64_PLT64 45 /* doubleword64 L + A */ ++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ ++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ ++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ ++#define R_PPC64_TOC 51 /* doubleword64 .TOC */ ++#define R_PPC64_PLTGOT16 52 /* half16* M + A */ ++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ ++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ ++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ ++ ++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ ++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ ++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ ++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ ++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ ++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ ++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ ++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ ++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ ++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ ++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ ++ ++/* PowerPC64 relocations defined for the TLS access ABI. */ ++#define R_PPC64_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ ++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ ++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ ++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ ++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ ++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ ++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ ++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ ++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ ++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ ++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ ++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ ++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ ++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ ++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ ++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ ++ ++/* Keep this the last entry. */ ++#define R_PPC64_NUM 107 ++ ++/* PowerPC64 specific values for the Dyn d_tag field. */ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_NUM 1 ++ ++ ++/* ARM specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++ ++/* Other constants defined in the ARM ELF spec. version B-01. */ ++/* NB. These conflict with values defined above. */ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++ ++/* Additional symbol types for Thumb */ ++#define STT_ARM_TFUNC 0xd ++ ++/* ARM-specific values for sh_flags */ ++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ ++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined ++ in the input to a link step */ ++ ++/* ARM-specific program header flags */ ++#define PF_ARM_SB 0x10000000 /* Segment contains the location ++ addressed by the static base */ ++ ++/* ARM relocs. */ ++#define R_ARM_NONE 0 /* No reloc */ ++#define R_ARM_PC24 1 /* PC relative 26 bit branch */ ++#define R_ARM_ABS32 2 /* Direct 32 bit */ ++#define R_ARM_REL32 3 /* PC relative 32 bit */ ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 /* Direct 16 bit */ ++#define R_ARM_ABS12 6 /* Direct 12 bit */ ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 /* Direct 8 bit */ ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_SWI24 13 ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_COPY 20 /* Copy symbol at runtime */ ++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ ++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ ++#define R_ARM_RELATIVE 23 /* Adjust by program base */ ++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ ++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ ++#define R_ARM_GOT32 26 /* 32 bit GOT entry */ ++#define R_ARM_PLT32 27 /* 32 bit PLT address */ ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ ++#define R_ARM_THM_PC9 103 /* thumb conditional branch */ ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++/* Keep this the last entry. */ ++#define R_ARM_NUM 256 ++ ++/* IA-64 specific declarations. */ ++ ++/* Processor specific flags for the Ehdr e_flags field. */ ++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ ++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ ++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ ++ ++/* Processor specific flags for the Phdr p_flags field. */ ++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ ++ ++/* Processor specific flags for the Shdr sh_flags field. */ ++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ ++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Dyn d_tag field. */ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++/* IA-64 relocations. */ ++#define R_IA64_NONE 0x00 /* none */ ++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ ++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ ++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ ++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ ++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ ++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ ++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ ++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ ++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ ++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ ++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ ++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ ++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ ++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ ++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ ++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ ++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ ++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ ++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ ++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ ++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ ++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ ++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ ++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ ++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ ++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ ++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ ++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ ++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ ++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ ++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ ++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ ++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ ++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ ++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ ++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ ++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ ++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ ++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ ++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ ++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ ++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ ++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ ++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ ++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ ++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ ++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ ++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ ++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ ++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ ++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ ++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ ++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ ++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ ++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ ++#define R_IA64_COPY 0x84 /* copy relocation */ ++#define R_IA64_SUB 0x85 /* Addend and symbol difference */ ++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ ++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ ++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ ++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ ++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ ++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ ++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ ++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ ++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ ++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ ++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ ++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ ++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ ++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ ++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ ++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ ++ ++/* SH specific declarations */ ++ ++/* SH relocs. */ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++/* Keep this the last entry. */ ++#define R_SH_NUM 256 ++ ++/* Additional s390 relocs */ ++ ++#define R_390_NONE 0 /* No reloc. */ ++#define R_390_8 1 /* Direct 8 bit. */ ++#define R_390_12 2 /* Direct 12 bit. */ ++#define R_390_16 3 /* Direct 16 bit. */ ++#define R_390_32 4 /* Direct 32 bit. */ ++#define R_390_PC32 5 /* PC relative 32 bit. */ ++#define R_390_GOT12 6 /* 12 bit GOT offset. */ ++#define R_390_GOT32 7 /* 32 bit GOT offset. */ ++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ ++#define R_390_COPY 9 /* Copy symbol at runtime. */ ++#define R_390_GLOB_DAT 10 /* Create GOT entry. */ ++#define R_390_JMP_SLOT 11 /* Create PLT entry. */ ++#define R_390_RELATIVE 12 /* Adjust by program base. */ ++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ ++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ ++#define R_390_GOT16 15 /* 16 bit GOT offset. */ ++#define R_390_PC16 16 /* PC relative 16 bit. */ ++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ ++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ ++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ ++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ ++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ ++#define R_390_64 22 /* Direct 64 bit. */ ++#define R_390_PC64 23 /* PC relative 64 bit. */ ++#define R_390_GOT64 24 /* 64 bit GOT offset. */ ++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ ++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ ++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ ++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ ++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ ++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ ++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ ++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ ++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ ++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ ++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ ++#define R_390_TLS_GDCALL 38 /* Tag for function call in general ++ dynamic TLS code. */ ++#define R_390_TLS_LDCALL 39 /* Tag for function call in local ++ dynamic TLS code. */ ++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ ++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ ++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS ++ block. */ ++ ++/* Keep this the last entry. */ ++#define R_390_NUM 57 ++ ++/* CRIS relocations. */ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++/* AMD x86-64 relocations. */ ++#define R_X86_64_NONE 0 /* No reloc */ ++#define R_X86_64_64 1 /* Direct 64 bit */ ++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ ++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ ++#define R_X86_64_PLT32 4 /* 32 bit PLT address */ ++#define R_X86_64_COPY 5 /* Copy symbol at runtime */ ++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ ++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ ++#define R_X86_64_RELATIVE 8 /* Adjust by program base */ ++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative ++ offset to GOT */ ++#define R_X86_64_32 10 /* Direct 32 bit zero extended */ ++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ ++#define R_X86_64_16 12 /* Direct 16 bit zero extended */ ++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ ++#define R_X86_64_8 14 /* Direct 8 bit sign extended */ ++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ ++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ ++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ ++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ ++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset ++ to two GOT entries for GD symbol */ ++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset ++ to two GOT entries for LD symbol */ ++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ ++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset ++ to GOT entry for IE symbol */ ++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ ++ ++#define R_X86_64_NUM 24 ++ ++__END_DECLS ++ ++#endif /* elf.h */ + + #include "elfconfig.h" + +@@ -185,3 +2631,4 @@ + void fatal(const char *fmt, ...); + void warn(const char *fmt, ...); + void merror(const char *fmt, ...); ++ +diff -Nur linux-3.11.5.orig/scripts/mod/sumversion.c linux-3.11.5/scripts/mod/sumversion.c +--- linux-3.11.5.orig/scripts/mod/sumversion.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/sumversion.c 2013-10-16 18:09:31.000000000 +0200 +@@ -1,4 +1,4 @@ +-#include ++/* #include */ + #ifdef __sun__ + #include + #else +diff -Nur linux-3.11.5.orig/tools/include/tools/linux_types.h linux-3.11.5/tools/include/tools/linux_types.h +--- linux-3.11.5.orig/tools/include/tools/linux_types.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.11.5/tools/include/tools/linux_types.h 2013-10-16 18:09:31.000000000 +0200 +@@ -0,0 +1,22 @@ ++#ifndef __LINUX_TYPES_H ++#define __LINUX_TYPES_H ++ ++#include ++ ++typedef uint8_t __u8; ++typedef uint8_t __be8; ++typedef uint8_t __le8; ++ ++typedef uint16_t __u16; ++typedef uint16_t __be16; ++typedef uint16_t __le16; ++ ++typedef uint32_t __u32; ++typedef uint32_t __be32; ++typedef uint32_t __le32; ++ ++typedef uint64_t __u64; ++typedef uint64_t __be64; ++typedef uint64_t __le64; ++ ++#endif diff --git a/target/linux/patches/3.14.45/cleankernel.patch b/target/linux/patches/3.14.45/cleankernel.patch new file mode 100644 index 000000000..d8c055dc3 --- /dev/null +++ b/target/linux/patches/3.14.45/cleankernel.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.5.orig/scripts/Makefile.headersinst linux-3.11.5/scripts/Makefile.headersinst +--- linux-3.11.5.orig/scripts/Makefile.headersinst 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/Makefile.headersinst 2013-10-15 16:33:10.000000000 +0200 +@@ -107,7 +107,6 @@ + + targets += $(install-file) + $(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE +- $(if $(unwanted),$(call cmd,remove),) + $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) + $(call if_changed,install) + diff --git a/target/linux/patches/3.14.45/defaults.patch b/target/linux/patches/3.14.45/defaults.patch new file mode 100644 index 000000000..f071fd1dd --- /dev/null +++ b/target/linux/patches/3.14.45/defaults.patch @@ -0,0 +1,34 @@ +diff -Nur linux-3.0.4.orig/fs/Kconfig linux-3.0.4/fs/Kconfig +--- linux-3.0.4.orig/fs/Kconfig 2011-08-29 22:56:30.000000000 +0200 ++++ linux-3.0.4/fs/Kconfig 2011-10-15 22:08:44.000000000 +0200 +@@ -47,7 +47,7 @@ + def_bool n + + config EXPORTFS +- tristate ++ def_bool y + + config FILE_LOCKING + bool "Enable POSIX file locking API" if EXPERT +diff -Nur linux-3.0.4.orig/fs/notify/Kconfig linux-3.0.4/fs/notify/Kconfig +--- linux-3.0.4.orig/fs/notify/Kconfig 2011-08-29 22:56:30.000000000 +0200 ++++ linux-3.0.4/fs/notify/Kconfig 2011-10-15 22:02:00.000000000 +0200 +@@ -1,5 +1,5 @@ + config FSNOTIFY +- def_bool n ++ def_bool y + + source "fs/notify/dnotify/Kconfig" + source "fs/notify/inotify/Kconfig" +diff -Nur linux-3.11.10.orig/usr/Kconfig linux-3.11.10/usr/Kconfig +--- linux-3.11.10.orig/usr/Kconfig 2013-11-29 19:42:37.000000000 +0100 ++++ linux-3.11.10/usr/Kconfig 2013-12-27 19:15:16.000000000 +0100 +@@ -47,7 +47,7 @@ + + config RD_GZIP + bool "Support initial ramdisks compressed using gzip" if EXPERT +- default y ++ default n + depends on BLK_DEV_INITRD + select DECOMPRESS_GZIP + help diff --git a/target/linux/patches/3.14.45/disable-netfilter.patch b/target/linux/patches/3.14.45/disable-netfilter.patch new file mode 100644 index 000000000..7b1ca013a --- /dev/null +++ b/target/linux/patches/3.14.45/disable-netfilter.patch @@ -0,0 +1,160 @@ +diff -Nur linux-3.7.3.orig/net/Kconfig linux-3.7.3/net/Kconfig +--- linux-3.7.3.orig/net/Kconfig 2013-01-17 17:47:40.000000000 +0100 ++++ linux-3.7.3/net/Kconfig 2013-01-19 18:19:55.000000000 +0100 +@@ -163,7 +163,7 @@ + config NETFILTER_ADVANCED + bool "Advanced netfilter configuration" + depends on NETFILTER +- default y ++ default n + help + If you say Y here you can select between all the netfilter modules. + If you say N the more unusual ones will not be shown and the +@@ -175,7 +175,7 @@ + bool "Bridged IP/ARP packets filtering" + depends on BRIDGE && NETFILTER && INET + depends on NETFILTER_ADVANCED +- default y ++ default n + ---help--- + Enabling this option will let arptables resp. iptables see bridged + ARP resp. IP traffic. If you want a bridging firewall, you probably +diff -Nur linux-3.7.3.orig/net/netfilter/Kconfig linux-3.7.3/net/netfilter/Kconfig +--- linux-3.7.3.orig/net/netfilter/Kconfig 2013-01-17 17:47:40.000000000 +0100 ++++ linux-3.7.3/net/netfilter/Kconfig 2013-01-19 18:21:41.000000000 +0100 +@@ -22,7 +22,6 @@ + + config NETFILTER_NETLINK_LOG + tristate "Netfilter LOG over NFNETLINK interface" +- default m if NETFILTER_ADVANCED=n + select NETFILTER_NETLINK + help + If this option is enabled, the kernel will include support +@@ -34,7 +33,6 @@ + + config NF_CONNTRACK + tristate "Netfilter connection tracking support" +- default m if NETFILTER_ADVANCED=n + help + Connection tracking keeps a record of what packets have passed + through your machine, in order to figure out how they are related +@@ -60,7 +58,6 @@ + config NF_CONNTRACK_SECMARK + bool 'Connection tracking security mark support' + depends on NETWORK_SECMARK +- default m if NETFILTER_ADVANCED=n + help + This option enables security markings to be applied to + connections. Typically they are copied to connections from +@@ -177,7 +174,6 @@ + + config NF_CONNTRACK_FTP + tristate "FTP protocol support" +- default m if NETFILTER_ADVANCED=n + help + Tracking FTP connections is problematic: special helpers are + required for tracking them, and doing masquerading and other forms +@@ -211,7 +207,6 @@ + + config NF_CONNTRACK_IRC + tristate "IRC protocol support" +- default m if NETFILTER_ADVANCED=n + help + There is a commonly-used extension to IRC called + Direct Client-to-Client Protocol (DCC). This enables users to send +@@ -296,7 +291,6 @@ + + config NF_CONNTRACK_SIP + tristate "SIP protocol support" +- default m if NETFILTER_ADVANCED=n + help + SIP is an application-layer control protocol that can establish, + modify, and terminate multimedia sessions (conferences) such as +@@ -320,7 +314,6 @@ + config NF_CT_NETLINK + tristate 'Connection tracking netlink interface' + select NETFILTER_NETLINK +- default m if NETFILTER_ADVANCED=n + help + This option enables support for a netlink-based userspace interface + +@@ -424,7 +417,6 @@ + + config NETFILTER_XTABLES + tristate "Netfilter Xtables support (required for ip_tables)" +- default m if NETFILTER_ADVANCED=n + help + This is required if you intend to use any of ip_tables, + ip6_tables or arp_tables. +@@ -435,7 +427,6 @@ + + config NETFILTER_XT_MARK + tristate 'nfmark target and match support' +- default m if NETFILTER_ADVANCED=n + ---help--- + This option adds the "MARK" target and "mark" match. + +@@ -527,7 +518,6 @@ + config NETFILTER_XT_TARGET_CONNSECMARK + tristate '"CONNSECMARK" target support' + depends on NF_CONNTRACK && NF_CONNTRACK_SECMARK +- default m if NETFILTER_ADVANCED=n + help + The CONNSECMARK target copies security markings from packets + to connections, and restores security markings from connections +@@ -632,7 +622,6 @@ + + config NETFILTER_XT_TARGET_LOG + tristate "LOG target support" +- default m if NETFILTER_ADVANCED=n + help + This option adds a `LOG' target, which allows you to create rules in + any iptables table which records the packet header to the syslog. +@@ -660,7 +649,6 @@ + + config NETFILTER_XT_TARGET_NFLOG + tristate '"NFLOG" target support' +- default m if NETFILTER_ADVANCED=n + select NETFILTER_NETLINK_LOG + help + This option enables the NFLOG target, which allows to LOG +@@ -741,7 +729,6 @@ + config NETFILTER_XT_TARGET_SECMARK + tristate '"SECMARK" target support' + depends on NETWORK_SECMARK +- default m if NETFILTER_ADVANCED=n + help + The SECMARK target allows security marking of network + packets, for use with security subsystems. +@@ -751,7 +738,6 @@ + config NETFILTER_XT_TARGET_TCPMSS + tristate '"TCPMSS" target support' + depends on (IPV6 || IPV6=n) +- default m if NETFILTER_ADVANCED=n + ---help--- + This option adds a `TCPMSS' target, which allows you to alter the + MSS value of TCP SYN packets, to control the maximum size for that +@@ -856,7 +842,6 @@ + config NETFILTER_XT_MATCH_CONNTRACK + tristate '"conntrack" connection tracking match support' + depends on NF_CONNTRACK +- default m if NETFILTER_ADVANCED=n + help + This is a general conntrack match module, a superset of the state match. + +@@ -1063,7 +1048,6 @@ + config NETFILTER_XT_MATCH_POLICY + tristate 'IPsec "policy" match support' + depends on XFRM +- default m if NETFILTER_ADVANCED=n + help + Policy matching allows you to match packets based on the + IPsec policy that was used during decapsulation/will +@@ -1170,7 +1154,6 @@ + config NETFILTER_XT_MATCH_STATE + tristate '"state" match support' + depends on NF_CONNTRACK +- default m if NETFILTER_ADVANCED=n + help + Connection state matching allows you to match packets based on their + relationship to a tracked connection (ie. previous packets). This diff --git a/target/linux/patches/3.14.45/export-symbol-for-exmap.patch b/target/linux/patches/3.14.45/export-symbol-for-exmap.patch new file mode 100644 index 000000000..4f0fc8449 --- /dev/null +++ b/target/linux/patches/3.14.45/export-symbol-for-exmap.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.5.orig/kernel/pid.c linux-3.11.5/kernel/pid.c +--- linux-3.11.5.orig/kernel/pid.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/kernel/pid.c 2013-10-29 15:37:02.000000000 +0100 +@@ -450,6 +450,7 @@ + { + return find_task_by_pid_ns(vnr, task_active_pid_ns(current)); + } ++EXPORT_SYMBOL(find_task_by_vpid); + + struct pid *get_task_pid(struct task_struct *task, enum pid_type type) + { diff --git a/target/linux/patches/3.14.45/fblogo.patch b/target/linux/patches/3.14.45/fblogo.patch new file mode 100644 index 000000000..cbbb4216f --- /dev/null +++ b/target/linux/patches/3.14.45/fblogo.patch @@ -0,0 +1,2097 @@ +diff -Nur linux-3.13.7.orig/Documentation/fb/00-INDEX linux-3.13.7/Documentation/fb/00-INDEX +--- linux-3.13.7.orig/Documentation/fb/00-INDEX 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/Documentation/fb/00-INDEX 2014-03-29 16:03:17.000000000 +0100 +@@ -21,6 +21,8 @@ + - info on the driver for EP93xx LCD controller. + fbcon.txt + - intro to and usage guide for the framebuffer console (fbcon). ++fbcondecor.txt ++ - info on the Framebuffer Console Decoration + framebuffer.txt + - introduction to frame buffer devices. + gxfb.txt +diff -Nur linux-3.13.7.orig/Documentation/fb/fbcondecor.txt linux-3.13.7/Documentation/fb/fbcondecor.txt +--- linux-3.13.7.orig/Documentation/fb/fbcondecor.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.13.7/Documentation/fb/fbcondecor.txt 2014-03-29 16:03:17.000000000 +0100 +@@ -0,0 +1,207 @@ ++What is it? ++----------- ++ ++The framebuffer decorations are a kernel feature which allows displaying a ++background picture on selected consoles. ++ ++What do I need to get it to work? ++--------------------------------- ++ ++To get fbcondecor up-and-running you will have to: ++ 1) get a copy of splashutils [1] or a similar program ++ 2) get some fbcondecor themes ++ 3) build the kernel helper program ++ 4) build your kernel with the FB_CON_DECOR option enabled. ++ ++To get fbcondecor operational right after fbcon initialization is finished, you ++will have to include a theme and the kernel helper into your initramfs image. ++Please refer to splashutils documentation for instructions on how to do that. ++ ++[1] The splashutils package can be downloaded from: ++ http://github.com/alanhaggai/fbsplash ++ ++The userspace helper ++-------------------- ++ ++The userspace fbcondecor helper (by default: /sbin/fbcondecor_helper) is called by the ++kernel whenever an important event occurs and the kernel needs some kind of ++job to be carried out. Important events include console switches and video ++mode switches (the kernel requests background images and configuration ++parameters for the current console). The fbcondecor helper must be accessible at ++all times. If it's not, fbcondecor will be switched off automatically. ++ ++It's possible to set path to the fbcondecor helper by writing it to ++/proc/sys/kernel/fbcondecor. ++ ++***************************************************************************** ++ ++The information below is mostly technical stuff. There's probably no need to ++read it unless you plan to develop a userspace helper. ++ ++The fbcondecor protocol ++----------------------- ++ ++The fbcondecor protocol defines a communication interface between the kernel and ++the userspace fbcondecor helper. ++ ++The kernel side is responsible for: ++ ++ * rendering console text, using an image as a background (instead of a ++ standard solid color fbcon uses), ++ * accepting commands from the user via ioctls on the fbcondecor device, ++ * calling the userspace helper to set things up as soon as the fb subsystem ++ is initialized. ++ ++The userspace helper is responsible for everything else, including parsing ++configuration files, decompressing the image files whenever the kernel needs ++it, and communicating with the kernel if necessary. ++ ++The fbcondecor protocol specifies how communication is done in both ways: ++kernel->userspace and userspace->helper. ++ ++Kernel -> Userspace ++------------------- ++ ++The kernel communicates with the userspace helper by calling it and specifying ++the task to be done in a series of arguments. ++ ++The arguments follow the pattern: ++ ++ ++All commands defined in fbcondecor protocol v2 have the following parameters: ++ virtual console ++ framebuffer number ++ theme ++ ++Fbcondecor protocol v1 specified an additional 'fbcondecor mode' after the ++framebuffer number. Fbcondecor protocol v1 is deprecated and should not be used. ++ ++Fbcondecor protocol v2 specifies the following commands: ++ ++getpic ++------ ++ The kernel issues this command to request image data. It's up to the ++ userspace helper to find a background image appropriate for the specified ++ theme and the current resolution. The userspace helper should respond by ++ issuing the FBIOCONDECOR_SETPIC ioctl. ++ ++init ++---- ++ The kernel issues this command after the fbcondecor device is created and ++ the fbcondecor interface is initialized. Upon receiving 'init', the userspace ++ helper should parse the kernel command line (/proc/cmdline) or otherwise ++ decide whether fbcondecor is to be activated. ++ ++ To activate fbcondecor on the first console the helper should issue the ++ FBIOCONDECOR_SETCFG, FBIOCONDECOR_SETPIC and FBIOCONDECOR_SETSTATE commands, ++ in the above-mentioned order. ++ ++ When the userspace helper is called in an early phase of the boot process ++ (right after the initialization of fbcon), no filesystems will be mounted. ++ The helper program should mount sysfs and then create the appropriate ++ framebuffer, fbcondecor and tty0 devices (if they don't already exist) to get ++ current display settings and to be able to communicate with the kernel side. ++ It should probably also mount the procfs to be able to parse the kernel ++ command line parameters. ++ ++ Note that the console sem is not held when the kernel calls fbcondecor_helper ++ with the 'init' command. The fbcondecor helper should perform all ioctls with ++ origin set to FBCON_DECOR_IO_ORIG_USER. ++ ++modechange ++---------- ++ The kernel issues this command on a mode change. The helper's response should ++ be similar to the response to the 'init' command. Note that this time the ++ console sem is held and all ioctls must be performed with origin set to ++ FBCON_DECOR_IO_ORIG_KERNEL. ++ ++ ++Userspace -> Kernel ++------------------- ++ ++Userspace programs can communicate with fbcondecor via ioctls on the ++fbcondecor device. These ioctls are to be used by both the userspace helper ++(called only by the kernel) and userspace configuration tools (run by the users). ++ ++The fbcondecor helper should set the origin field to FBCON_DECOR_IO_ORIG_KERNEL ++when doing the appropriate ioctls. All userspace configuration tools should ++use FBCON_DECOR_IO_ORIG_USER. Failure to set the appropriate value in the origin ++field when performing ioctls from the kernel helper will most likely result ++in a console deadlock. ++ ++FBCON_DECOR_IO_ORIG_KERNEL instructs fbcondecor not to try to acquire the console ++semaphore. Not surprisingly, FBCON_DECOR_IO_ORIG_USER instructs it to acquire ++the console sem. ++ ++The framebuffer console decoration provides the following ioctls (all defined in ++linux/fb.h): ++ ++FBIOCONDECOR_SETPIC ++description: loads a background picture for a virtual console ++argument: struct fbcon_decor_iowrapper*; data: struct fb_image* ++notes: ++If called for consoles other than the current foreground one, the picture data ++will be ignored. ++ ++If the current virtual console is running in a 8-bpp mode, the cmap substruct ++of fb_image has to be filled appropriately: start should be set to 16 (first ++16 colors are reserved for fbcon), len to a value <= 240 and red, green and ++blue should point to valid cmap data. The transp field is ingored. The fields ++dx, dy, bg_color, fg_color in fb_image are ignored as well. ++ ++FBIOCONDECOR_SETCFG ++description: sets the fbcondecor config for a virtual console ++argument: struct fbcon_decor_iowrapper*; data: struct vc_decor* ++notes: The structure has to be filled with valid data. ++ ++FBIOCONDECOR_GETCFG ++description: gets the fbcondecor config for a virtual console ++argument: struct fbcon_decor_iowrapper*; data: struct vc_decor* ++ ++FBIOCONDECOR_SETSTATE ++description: sets the fbcondecor state for a virtual console ++argument: struct fbcon_decor_iowrapper*; data: unsigned int* ++ values: 0 = disabled, 1 = enabled. ++ ++FBIOCONDECOR_GETSTATE ++description: gets the fbcondecor state for a virtual console ++argument: struct fbcon_decor_iowrapper*; data: unsigned int* ++ values: as in FBIOCONDECOR_SETSTATE ++ ++Info on used structures: ++ ++Definition of struct vc_decor can be found in linux/console_decor.h. It's ++heavily commented. Note that the 'theme' field should point to a string ++no longer than FBCON_DECOR_THEME_LEN. When FBIOCONDECOR_GETCFG call is ++performed, the theme field should point to a char buffer of length ++FBCON_DECOR_THEME_LEN. ++ ++Definition of struct fbcon_decor_iowrapper can be found in linux/fb.h. ++The fields in this struct have the following meaning: ++ ++vc: ++Virtual console number. ++ ++origin: ++Specifies if the ioctl is performed as a response to a kernel request. The ++fbcondecor helper should set this field to FBCON_DECOR_IO_ORIG_KERNEL, userspace ++programs should set it to FBCON_DECOR_IO_ORIG_USER. This field is necessary to ++avoid console semaphore deadlocks. ++ ++data: ++Pointer to a data structure appropriate for the performed ioctl. Type of ++the data struct is specified in the ioctls description. ++ ++***************************************************************************** ++ ++Credit ++------ ++ ++Original 'bootsplash' project & implementation by: ++ Volker Poplawski , Stefan Reinauer , ++ Steffen Winterfeldt , Michael Schroeder , ++ Ken Wimer . ++ ++Fbcondecor, fbcondecor protocol design, current implementation & docs by: ++ Michal Januszewski ++ +diff -Nur linux-3.13.7.orig/drivers/Makefile linux-3.13.7/drivers/Makefile +--- linux-3.13.7.orig/drivers/Makefile 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/drivers/Makefile 2014-03-29 16:03:17.000000000 +0100 +@@ -17,6 +17,10 @@ + obj-$(CONFIG_PCI) += pci/ + obj-$(CONFIG_PARISC) += parisc/ + obj-$(CONFIG_RAPIDIO) += rapidio/ ++# tty/ comes before char/ so that the VT console is the boot-time ++# default. ++obj-y += tty/ ++obj-y += char/ + obj-y += video/ + obj-y += idle/ + +@@ -42,11 +46,6 @@ + # reset controllers early, since gpu drivers might rely on them to initialize + obj-$(CONFIG_RESET_CONTROLLER) += reset/ + +-# tty/ comes before char/ so that the VT console is the boot-time +-# default. +-obj-y += tty/ +-obj-y += char/ +- + # gpu/ comes after char for AGP vs DRM startup + obj-y += gpu/ + +diff -Nur linux-3.13.7.orig/drivers/video/Kconfig linux-3.13.7/drivers/video/Kconfig +--- linux-3.13.7.orig/drivers/video/Kconfig 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/drivers/video/Kconfig 2014-03-29 16:03:17.000000000 +0100 +@@ -1231,7 +1231,6 @@ + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT +- select FB_TILEBLITTING + select FB_MACMODES if PPC_PMAC + ---help--- + Say Y here if you have a Matrox Millennium, Matrox Millennium II, +diff -Nur linux-3.13.7.orig/drivers/video/console/Kconfig linux-3.13.7/drivers/video/console/Kconfig +--- linux-3.13.7.orig/drivers/video/console/Kconfig 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/drivers/video/console/Kconfig 2014-03-29 16:03:17.000000000 +0100 +@@ -125,6 +125,19 @@ + such that other users of the framebuffer will remain normally + oriented. + ++config FB_CON_DECOR ++ bool "Support for the Framebuffer Console Decorations" ++ depends on FRAMEBUFFER_CONSOLE=y && !FB_TILEBLITTING ++ default n ++ ---help--- ++ This option enables support for framebuffer console decorations which ++ makes it possible to display images in the background of the system ++ consoles. Note that userspace utilities are necessary in order to take ++ advantage of these features. Refer to Documentation/fb/fbcondecor.txt ++ for more information. ++ ++ If unsure, say N. ++ + config STI_CONSOLE + bool "STI text console" + depends on PARISC +diff -Nur linux-3.13.7.orig/drivers/video/console/Makefile linux-3.13.7/drivers/video/console/Makefile +--- linux-3.13.7.orig/drivers/video/console/Makefile 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/drivers/video/console/Makefile 2014-03-29 16:03:17.000000000 +0100 +@@ -16,4 +16,5 @@ + fbcon_ccw.o + endif + ++obj-$(CONFIG_FB_CON_DECOR) += fbcondecor.o cfbcondecor.o + obj-$(CONFIG_FB_STI) += sticore.o +diff -Nur linux-3.13.7.orig/drivers/video/console/bitblit.c linux-3.13.7/drivers/video/console/bitblit.c +--- linux-3.13.7.orig/drivers/video/console/bitblit.c 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/drivers/video/console/bitblit.c 2014-03-29 16:03:17.000000000 +0100 +@@ -18,6 +18,7 @@ + #include + #include + #include "fbcon.h" ++#include "fbcondecor.h" + + /* + * Accelerated handlers. +@@ -55,6 +56,13 @@ + area.height = height * vc->vc_font.height; + area.width = width * vc->vc_font.width; + ++ if (fbcon_decor_active(info, vc)) { ++ area.sx += vc->vc_decor.tx; ++ area.sy += vc->vc_decor.ty; ++ area.dx += vc->vc_decor.tx; ++ area.dy += vc->vc_decor.ty; ++ } ++ + info->fbops->fb_copyarea(info, &area); + } + +@@ -380,11 +388,15 @@ + cursor.image.depth = 1; + cursor.rop = ROP_XOR; + +- if (info->fbops->fb_cursor) +- err = info->fbops->fb_cursor(info, &cursor); ++ if (fbcon_decor_active(info, vc)) { ++ fbcon_decor_cursor(info, &cursor); ++ } else { ++ if (info->fbops->fb_cursor) ++ err = info->fbops->fb_cursor(info, &cursor); + +- if (err) +- soft_cursor(info, &cursor); ++ if (err) ++ soft_cursor(info, &cursor); ++ } + + ops->cursor_reset = 0; + } +diff -Nur linux-3.13.7.orig/drivers/video/console/cfbcondecor.c linux-3.13.7/drivers/video/console/cfbcondecor.c +--- linux-3.13.7.orig/drivers/video/console/cfbcondecor.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.13.7/drivers/video/console/cfbcondecor.c 2014-03-29 16:03:17.000000000 +0100 +@@ -0,0 +1,471 @@ ++/* ++ * linux/drivers/video/cfbcon_decor.c -- Framebuffer decor render functions ++ * ++ * Copyright (C) 2004 Michal Januszewski ++ * ++ * Code based upon "Bootdecor" (C) 2001-2003 ++ * Volker Poplawski , ++ * Stefan Reinauer , ++ * Steffen Winterfeldt , ++ * Michael Schroeder , ++ * Ken Wimer . ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fbcon.h" ++#include "fbcondecor.h" ++ ++#define parse_pixel(shift,bpp,type) \ ++ do { \ ++ if (d & (0x80 >> (shift))) \ ++ dd2[(shift)] = fgx; \ ++ else \ ++ dd2[(shift)] = transparent ? *(type *)decor_src : bgx; \ ++ decor_src += (bpp); \ ++ } while (0) \ ++ ++extern int get_color(struct vc_data *vc, struct fb_info *info, ++ u16 c, int is_fg); ++ ++void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) ++{ ++ int i, j, k; ++ int minlen = min(min(info->var.red.length, info->var.green.length), ++ info->var.blue.length); ++ u32 col; ++ ++ for (j = i = 0; i < 16; i++) { ++ k = color_table[i]; ++ ++ col = ((vc->vc_palette[j++] >> (8-minlen)) ++ << info->var.red.offset); ++ col |= ((vc->vc_palette[j++] >> (8-minlen)) ++ << info->var.green.offset); ++ col |= ((vc->vc_palette[j++] >> (8-minlen)) ++ << info->var.blue.offset); ++ ((u32 *)info->pseudo_palette)[k] = col; ++ } ++} ++ ++void fbcon_decor_renderc(struct fb_info *info, int ypos, int xpos, int height, ++ int width, u8* src, u32 fgx, u32 bgx, u8 transparent) ++{ ++ unsigned int x, y; ++ u32 dd; ++ int bytespp = ((info->var.bits_per_pixel + 7) >> 3); ++ unsigned int d = ypos * info->fix.line_length + xpos * bytespp; ++ unsigned int ds = (ypos * info->var.xres + xpos) * bytespp; ++ u16 dd2[4]; ++ ++ u8* decor_src = (u8 *)(info->bgdecor.data + ds); ++ u8* dst = (u8 *)(info->screen_base + d); ++ ++ if ((ypos + height) > info->var.yres || (xpos + width) > info->var.xres) ++ return; ++ ++ for (y = 0; y < height; y++) { ++ switch (info->var.bits_per_pixel) { ++ ++ case 32: ++ for (x = 0; x < width; x++) { ++ ++ if ((x & 7) == 0) ++ d = *src++; ++ if (d & 0x80) ++ dd = fgx; ++ else ++ dd = transparent ? ++ *(u32 *)decor_src : bgx; ++ ++ d <<= 1; ++ decor_src += 4; ++ fb_writel(dd, dst); ++ dst += 4; ++ } ++ break; ++ case 24: ++ for (x = 0; x < width; x++) { ++ ++ if ((x & 7) == 0) ++ d = *src++; ++ if (d & 0x80) ++ dd = fgx; ++ else ++ dd = transparent ? ++ (*(u32 *)decor_src & 0xffffff) : bgx; ++ ++ d <<= 1; ++ decor_src += 3; ++#ifdef __LITTLE_ENDIAN ++ fb_writew(dd & 0xffff, dst); ++ dst += 2; ++ fb_writeb((dd >> 16), dst); ++#else ++ fb_writew(dd >> 8, dst); ++ dst += 2; ++ fb_writeb(dd & 0xff, dst); ++#endif ++ dst++; ++ } ++ break; ++ case 16: ++ for (x = 0; x < width; x += 2) { ++ if ((x & 7) == 0) ++ d = *src++; ++ ++ parse_pixel(0, 2, u16); ++ parse_pixel(1, 2, u16); ++#ifdef __LITTLE_ENDIAN ++ dd = dd2[0] | (dd2[1] << 16); ++#else ++ dd = dd2[1] | (dd2[0] << 16); ++#endif ++ d <<= 2; ++ fb_writel(dd, dst); ++ dst += 4; ++ } ++ break; ++ ++ case 8: ++ for (x = 0; x < width; x += 4) { ++ if ((x & 7) == 0) ++ d = *src++; ++ ++ parse_pixel(0, 1, u8); ++ parse_pixel(1, 1, u8); ++ parse_pixel(2, 1, u8); ++ parse_pixel(3, 1, u8); ++ ++#ifdef __LITTLE_ENDIAN ++ dd = dd2[0] | (dd2[1] << 8) | (dd2[2] << 16) | (dd2[3] << 24); ++#else ++ dd = dd2[3] | (dd2[2] << 8) | (dd2[1] << 16) | (dd2[0] << 24); ++#endif ++ d <<= 4; ++ fb_writel(dd, dst); ++ dst += 4; ++ } ++ } ++ ++ dst += info->fix.line_length - width * bytespp; ++ decor_src += (info->var.xres - width) * bytespp; ++ } ++} ++ ++#define cc2cx(a) \ ++ ((info->fix.visual == FB_VISUAL_TRUECOLOR || \ ++ info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? \ ++ ((u32*)info->pseudo_palette)[a] : a) ++ ++void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, ++ const unsigned short *s, int count, int yy, int xx) ++{ ++ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; ++ struct fbcon_ops *ops = info->fbcon_par; ++ int fg_color, bg_color, transparent; ++ u8 *src; ++ u32 bgx, fgx; ++ u16 c = scr_readw(s); ++ ++ fg_color = get_color(vc, info, c, 1); ++ bg_color = get_color(vc, info, c, 0); ++ ++ /* Don't paint the background image if console is blanked */ ++ transparent = ops->blank_state ? 0 : ++ (vc->vc_decor.bg_color == bg_color); ++ ++ xx = xx * vc->vc_font.width + vc->vc_decor.tx; ++ yy = yy * vc->vc_font.height + vc->vc_decor.ty; ++ ++ fgx = cc2cx(fg_color); ++ bgx = cc2cx(bg_color); ++ ++ while (count--) { ++ c = scr_readw(s++); ++ src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * ++ ((vc->vc_font.width + 7) >> 3); ++ ++ fbcon_decor_renderc(info, yy, xx, vc->vc_font.height, ++ vc->vc_font.width, src, fgx, bgx, transparent); ++ xx += vc->vc_font.width; ++ } ++} ++ ++void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) ++{ ++ int i; ++ unsigned int dsize, s_pitch; ++ struct fbcon_ops *ops = info->fbcon_par; ++ struct vc_data* vc; ++ u8 *src; ++ ++ /* we really don't need any cursors while the console is blanked */ ++ if (info->state != FBINFO_STATE_RUNNING || ops->blank_state) ++ return; ++ ++ vc = vc_cons[ops->currcon].d; ++ ++ src = kmalloc(64 + sizeof(struct fb_image), GFP_ATOMIC); ++ if (!src) ++ return; ++ ++ s_pitch = (cursor->image.width + 7) >> 3; ++ dsize = s_pitch * cursor->image.height; ++ if (cursor->enable) { ++ switch (cursor->rop) { ++ case ROP_XOR: ++ for (i = 0; i < dsize; i++) ++ src[i] = cursor->image.data[i] ^ cursor->mask[i]; ++ break; ++ case ROP_COPY: ++ default: ++ for (i = 0; i < dsize; i++) ++ src[i] = cursor->image.data[i] & cursor->mask[i]; ++ break; ++ } ++ } else ++ memcpy(src, cursor->image.data, dsize); ++ ++ fbcon_decor_renderc(info, ++ cursor->image.dy + vc->vc_decor.ty, ++ cursor->image.dx + vc->vc_decor.tx, ++ cursor->image.height, ++ cursor->image.width, ++ (u8*)src, ++ cc2cx(cursor->image.fg_color), ++ cc2cx(cursor->image.bg_color), ++ cursor->image.bg_color == vc->vc_decor.bg_color); ++ ++ kfree(src); ++} ++ ++static void decorset(u8 *dst, int height, int width, int dstbytes, ++ u32 bgx, int bpp) ++{ ++ int i; ++ ++ if (bpp == 8) ++ bgx |= bgx << 8; ++ if (bpp == 16 || bpp == 8) ++ bgx |= bgx << 16; ++ ++ while (height-- > 0) { ++ u8 *p = dst; ++ ++ switch (bpp) { ++ ++ case 32: ++ for (i=0; i < width; i++) { ++ fb_writel(bgx, p); p += 4; ++ } ++ break; ++ case 24: ++ for (i=0; i < width; i++) { ++#ifdef __LITTLE_ENDIAN ++ fb_writew((bgx & 0xffff),(u16*)p); p += 2; ++ fb_writeb((bgx >> 16),p++); ++#else ++ fb_writew((bgx >> 8),(u16*)p); p += 2; ++ fb_writeb((bgx & 0xff),p++); ++#endif ++ } ++ case 16: ++ for (i=0; i < width/4; i++) { ++ fb_writel(bgx,p); p += 4; ++ fb_writel(bgx,p); p += 4; ++ } ++ if (width & 2) { ++ fb_writel(bgx,p); p += 4; ++ } ++ if (width & 1) ++ fb_writew(bgx,(u16*)p); ++ break; ++ case 8: ++ for (i=0; i < width/4; i++) { ++ fb_writel(bgx,p); p += 4; ++ } ++ ++ if (width & 2) { ++ fb_writew(bgx,p); p += 2; ++ } ++ if (width & 1) ++ fb_writeb(bgx,(u8*)p); ++ break; ++ ++ } ++ dst += dstbytes; ++ } ++} ++ ++void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes, ++ int srclinebytes, int bpp) ++{ ++ int i; ++ ++ while (height-- > 0) { ++ u32 *p = (u32 *)dst; ++ u32 *q = (u32 *)src; ++ ++ switch (bpp) { ++ ++ case 32: ++ for (i=0; i < width; i++) ++ fb_writel(*q++, p++); ++ break; ++ case 24: ++ for (i=0; i < (width*3/4); i++) ++ fb_writel(*q++, p++); ++ if ((width*3) % 4) { ++ if (width & 2) { ++ fb_writeb(*(u8*)q, (u8*)p); ++ } else if (width & 1) { ++ fb_writew(*(u16*)q, (u16*)p); ++ fb_writeb(*(u8*)((u16*)q+1),(u8*)((u16*)p+2)); ++ } ++ } ++ break; ++ case 16: ++ for (i=0; i < width/4; i++) { ++ fb_writel(*q++, p++); ++ fb_writel(*q++, p++); ++ } ++ if (width & 2) ++ fb_writel(*q++, p++); ++ if (width & 1) ++ fb_writew(*(u16*)q, (u16*)p); ++ break; ++ case 8: ++ for (i=0; i < width/4; i++) ++ fb_writel(*q++, p++); ++ ++ if (width & 2) { ++ fb_writew(*(u16*)q, (u16*)p); ++ q = (u32*) ((u16*)q + 1); ++ p = (u32*) ((u16*)p + 1); ++ } ++ if (width & 1) ++ fb_writeb(*(u8*)q, (u8*)p); ++ break; ++ } ++ ++ dst += linebytes; ++ src += srclinebytes; ++ } ++} ++ ++static void decorfill(struct fb_info *info, int sy, int sx, int height, ++ int width) ++{ ++ int bytespp = ((info->var.bits_per_pixel + 7) >> 3); ++ int d = sy * info->fix.line_length + sx * bytespp; ++ int ds = (sy * info->var.xres + sx) * bytespp; ++ ++ fbcon_decor_copy((u8 *)(info->screen_base + d), (u8 *)(info->bgdecor.data + ds), ++ height, width, info->fix.line_length, info->var.xres * bytespp, ++ info->var.bits_per_pixel); ++} ++ ++void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, ++ int height, int width) ++{ ++ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; ++ struct fbcon_ops *ops = info->fbcon_par; ++ u8 *dst; ++ int transparent, bg_color = attr_bgcol_ec(bgshift, vc, info); ++ ++ transparent = (vc->vc_decor.bg_color == bg_color); ++ sy = sy * vc->vc_font.height + vc->vc_decor.ty; ++ sx = sx * vc->vc_font.width + vc->vc_decor.tx; ++ height *= vc->vc_font.height; ++ width *= vc->vc_font.width; ++ ++ /* Don't paint the background image if console is blanked */ ++ if (transparent && !ops->blank_state) { ++ decorfill(info, sy, sx, height, width); ++ } else { ++ dst = (u8 *)(info->screen_base + sy * info->fix.line_length + ++ sx * ((info->var.bits_per_pixel + 7) >> 3)); ++ decorset(dst, height, width, info->fix.line_length, cc2cx(bg_color), ++ info->var.bits_per_pixel); ++ } ++} ++ ++void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, ++ int bottom_only) ++{ ++ unsigned int tw = vc->vc_cols*vc->vc_font.width; ++ unsigned int th = vc->vc_rows*vc->vc_font.height; ++ ++ if (!bottom_only) { ++ /* top margin */ ++ decorfill(info, 0, 0, vc->vc_decor.ty, info->var.xres); ++ /* left margin */ ++ decorfill(info, vc->vc_decor.ty, 0, th, vc->vc_decor.tx); ++ /* right margin */ ++ decorfill(info, vc->vc_decor.ty, vc->vc_decor.tx + tw, th, ++ info->var.xres - vc->vc_decor.tx - tw); ++ } ++ decorfill(info, vc->vc_decor.ty + th, 0, ++ info->var.yres - vc->vc_decor.ty - th, info->var.xres); ++} ++ ++void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, ++ int sx, int dx, int width) ++{ ++ u16 *d = (u16 *) (vc->vc_origin + vc->vc_size_row * y + dx * 2); ++ u16 *s = d + (dx - sx); ++ u16 *start = d; ++ u16 *ls = d; ++ u16 *le = d + width; ++ u16 c; ++ int x = dx; ++ u16 attr = 1; ++ ++ do { ++ c = scr_readw(d); ++ if (attr != (c & 0xff00)) { ++ attr = c & 0xff00; ++ if (d > start) { ++ fbcon_decor_putcs(vc, info, start, d - start, y, x); ++ x += d - start; ++ start = d; ++ } ++ } ++ if (s >= ls && s < le && c == scr_readw(s)) { ++ if (d > start) { ++ fbcon_decor_putcs(vc, info, start, d - start, y, x); ++ x += d - start + 1; ++ start = d + 1; ++ } else { ++ x++; ++ start++; ++ } ++ } ++ s++; ++ d++; ++ } while (d < le); ++ if (d > start) ++ fbcon_decor_putcs(vc, info, start, d - start, y, x); ++} ++ ++void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) ++{ ++ if (blank) { ++ decorset((u8 *)info->screen_base, info->var.yres, info->var.xres, ++ info->fix.line_length, 0, info->var.bits_per_pixel); ++ } else { ++ update_screen(vc); ++ fbcon_decor_clear_margins(vc, info, 0); ++ } ++} ++ +diff -Nur linux-3.13.7.orig/drivers/video/console/fbcon.c linux-3.13.7/drivers/video/console/fbcon.c +--- linux-3.13.7.orig/drivers/video/console/fbcon.c 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/drivers/video/console/fbcon.c 2014-03-29 16:03:17.000000000 +0100 +@@ -79,6 +79,7 @@ + #include + + #include "fbcon.h" ++#include "fbcondecor.h" + + #ifdef FBCONDEBUG + # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) +@@ -94,7 +95,7 @@ + + static struct display fb_display[MAX_NR_CONSOLES]; + +-static signed char con2fb_map[MAX_NR_CONSOLES]; ++signed char con2fb_map[MAX_NR_CONSOLES]; + static signed char con2fb_map_boot[MAX_NR_CONSOLES]; + + static int logo_lines; +@@ -286,7 +287,7 @@ + !vt_force_oops_output(vc); + } + +-static int get_color(struct vc_data *vc, struct fb_info *info, ++int get_color(struct vc_data *vc, struct fb_info *info, + u16 c, int is_fg) + { + int depth = fb_get_color_depth(&info->var, &info->fix); +@@ -551,6 +552,9 @@ + info_idx = -1; + } else { + fbcon_has_console_bind = 1; ++#ifdef CONFIG_FB_CON_DECOR ++ fbcon_decor_init(); ++#endif + } + + return err; +@@ -1007,6 +1011,12 @@ + rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols /= vc->vc_font.width; + rows /= vc->vc_font.height; ++ ++ if (fbcon_decor_active(info, vc)) { ++ cols = vc->vc_decor.twidth / vc->vc_font.width; ++ rows = vc->vc_decor.theight / vc->vc_font.height; ++ } ++ + vc_resize(vc, cols, rows); + + DPRINTK("mode: %s\n", info->fix.id); +@@ -1036,7 +1046,7 @@ + cap = info->flags; + + if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW || +- (info->fix.type == FB_TYPE_TEXT)) ++ (info->fix.type == FB_TYPE_TEXT) || fbcon_decor_active(info, vc)) + logo = 0; + + if (var_to_display(p, &info->var, info)) +@@ -1260,6 +1270,11 @@ + fbcon_clear_margins(vc, 0); + } + ++ if (fbcon_decor_active(info, vc)) { ++ fbcon_decor_clear(vc, info, sy, sx, height, width); ++ return; ++ } ++ + /* Split blits that cross physical y_wrap boundary */ + + y_break = p->vrows - p->yscroll; +@@ -1279,10 +1294,15 @@ + struct display *p = &fb_display[vc->vc_num]; + struct fbcon_ops *ops = info->fbcon_par; + +- if (!fbcon_is_inactive(vc, info)) +- ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, +- get_color(vc, info, scr_readw(s), 1), +- get_color(vc, info, scr_readw(s), 0)); ++ if (!fbcon_is_inactive(vc, info)) { ++ ++ if (fbcon_decor_active(info, vc)) ++ fbcon_decor_putcs(vc, info, s, count, ypos, xpos); ++ else ++ ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, ++ get_color(vc, info, scr_readw(s), 1), ++ get_color(vc, info, scr_readw(s), 0)); ++ } + } + + static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) +@@ -1298,8 +1318,13 @@ + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; + +- if (!fbcon_is_inactive(vc, info)) +- ops->clear_margins(vc, info, bottom_only); ++ if (!fbcon_is_inactive(vc, info)) { ++ if (fbcon_decor_active(info, vc)) { ++ fbcon_decor_clear_margins(vc, info, bottom_only); ++ } else { ++ ops->clear_margins(vc, info, bottom_only); ++ } ++ } + } + + static void fbcon_cursor(struct vc_data *vc, int mode) +@@ -1819,7 +1844,7 @@ + count = vc->vc_rows; + if (softback_top) + fbcon_softback_note(vc, t, count); +- if (logo_shown >= 0) ++ if (logo_shown >= 0 || fbcon_decor_active(info, vc)) + goto redraw_up; + switch (p->scrollmode) { + case SCROLL_MOVE: +@@ -1912,6 +1937,8 @@ + count = vc->vc_rows; + if (logo_shown >= 0) + goto redraw_down; ++ if (fbcon_decor_active(info, vc)) ++ goto redraw_down; + switch (p->scrollmode) { + case SCROLL_MOVE: + fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, +@@ -2060,6 +2087,13 @@ + } + return; + } ++ ++ if (fbcon_decor_active(info, vc) && sy == dy && height == 1) { ++ /* must use slower redraw bmove to keep background pic intact */ ++ fbcon_decor_bmove_redraw(vc, info, sy, sx, dx, width); ++ return; ++ } ++ + ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, + height, width); + } +@@ -2130,8 +2164,8 @@ + var.yres = virt_h * virt_fh; + x_diff = info->var.xres - var.xres; + y_diff = info->var.yres - var.yres; +- if (x_diff < 0 || x_diff > virt_fw || +- y_diff < 0 || y_diff > virt_fh) { ++ if ((x_diff < 0 || x_diff > virt_fw || ++ y_diff < 0 || y_diff > virt_fh) && !vc->vc_decor.state) { + const struct fb_videomode *mode; + + DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); +@@ -2167,6 +2201,21 @@ + + info = registered_fb[con2fb_map[vc->vc_num]]; + ops = info->fbcon_par; ++ prev_console = ops->currcon; ++ if (prev_console != -1) ++ old_info = registered_fb[con2fb_map[prev_console]]; ++ ++#ifdef CONFIG_FB_CON_DECOR ++ if (!fbcon_decor_active_vc(vc) && info->fix.visual == FB_VISUAL_DIRECTCOLOR) { ++ struct vc_data *vc_curr = vc_cons[prev_console].d; ++ if (vc_curr && fbcon_decor_active_vc(vc_curr)) { ++ /* Clear the screen to avoid displaying funky colors during ++ * palette updates. */ ++ memset((u8*)info->screen_base + info->fix.line_length * info->var.yoffset, ++ 0, info->var.yres * info->fix.line_length); ++ } ++ } ++#endif + + if (softback_top) { + if (softback_lines) +@@ -2185,9 +2234,6 @@ + logo_shown = FBCON_LOGO_CANSHOW; + } + +- prev_console = ops->currcon; +- if (prev_console != -1) +- old_info = registered_fb[con2fb_map[prev_console]]; + /* + * FIXME: If we have multiple fbdev's loaded, we need to + * update all info->currcon. Perhaps, we can place this +@@ -2231,6 +2277,18 @@ + fbcon_del_cursor_timer(old_info); + } + ++ if (fbcon_decor_active_vc(vc)) { ++ struct vc_data *vc_curr = vc_cons[prev_console].d; ++ ++ if (!vc_curr->vc_decor.theme || ++ strcmp(vc->vc_decor.theme, vc_curr->vc_decor.theme) || ++ (fbcon_decor_active_nores(info, vc_curr) && ++ !fbcon_decor_active(info, vc_curr))) { ++ fbcon_decor_disable(vc, 0); ++ fbcon_decor_call_helper("modechange", vc->vc_num); ++ } ++ } ++ + if (fbcon_is_inactive(vc, info) || + ops->blank_state != FB_BLANK_UNBLANK) + fbcon_del_cursor_timer(info); +@@ -2339,15 +2397,20 @@ + } + } + +- if (!fbcon_is_inactive(vc, info)) { ++ if (!fbcon_is_inactive(vc, info)) { + if (ops->blank_state != blank) { + ops->blank_state = blank; + fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); + ops->cursor_flash = (!blank); + +- if (!(info->flags & FBINFO_MISC_USEREVENT)) +- if (fb_blank(info, blank)) +- fbcon_generic_blank(vc, info, blank); ++ if (!(info->flags & FBINFO_MISC_USEREVENT)) { ++ if (fb_blank(info, blank)) { ++ if (fbcon_decor_active(info, vc)) ++ fbcon_decor_blank(vc, info, blank); ++ else ++ fbcon_generic_blank(vc, info, blank); ++ } ++ } + } + + if (!blank) +@@ -2522,13 +2585,22 @@ + } + + if (resize) { ++ /* reset wrap/pan */ + int cols, rows; + + cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); + rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); ++ ++ if (fbcon_decor_active(info, vc)) { ++ info->var.xoffset = info->var.yoffset = p->yscroll = 0; ++ cols = vc->vc_decor.twidth; ++ rows = vc->vc_decor.theight; ++ } + cols /= w; + rows /= h; ++ + vc_resize(vc, cols, rows); ++ + if (CON_IS_VISIBLE(vc) && softback_buf) + fbcon_update_softback(vc); + } else if (CON_IS_VISIBLE(vc) +@@ -2657,7 +2729,11 @@ + int i, j, k, depth; + u8 val; + +- if (fbcon_is_inactive(vc, info)) ++ if (fbcon_is_inactive(vc, info) ++#ifdef CONFIG_FB_CON_DECOR ++ || vc->vc_num != fg_console ++#endif ++ ) + return -EINVAL; + + if (!CON_IS_VISIBLE(vc)) +@@ -2683,14 +2759,56 @@ + } else + fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap); + +- return fb_set_cmap(&palette_cmap, info); ++ if (fbcon_decor_active(info, vc_cons[fg_console].d) && ++ info->fix.visual == FB_VISUAL_DIRECTCOLOR) { ++ ++ u16 *red, *green, *blue; ++ int minlen = min(min(info->var.red.length, info->var.green.length), ++ info->var.blue.length); ++ int h; ++ ++ struct fb_cmap cmap = { ++ .start = 0, ++ .len = (1 << minlen), ++ .red = NULL, ++ .green = NULL, ++ .blue = NULL, ++ .transp = NULL ++ }; ++ ++ red = kmalloc(256 * sizeof(u16) * 3, GFP_KERNEL); ++ ++ if (!red) ++ goto out; ++ ++ green = red + 256; ++ blue = green + 256; ++ cmap.red = red; ++ cmap.green = green; ++ cmap.blue = blue; ++ ++ for (i = 0; i < cmap.len; i++) { ++ red[i] = green[i] = blue[i] = (0xffff * i)/(cmap.len-1); ++ } ++ ++ h = fb_set_cmap(&cmap, info); ++ fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d); ++ kfree(red); ++ ++ return h; ++ ++ } else if (fbcon_decor_active(info, vc_cons[fg_console].d) && ++ info->var.bits_per_pixel == 8 && info->bgdecor.cmap.red != NULL) ++ fb_set_cmap(&info->bgdecor.cmap, info); ++ ++out: return fb_set_cmap(&palette_cmap, info); + } + + static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) + { + unsigned long p; + int line; +- ++ + if (vc->vc_num != fg_console || !softback_lines) + return (u16 *) (vc->vc_origin + offset); + line = offset / vc->vc_size_row; +@@ -2909,7 +3027,14 @@ + rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols /= vc->vc_font.width; + rows /= vc->vc_font.height; +- vc_resize(vc, cols, rows); ++ ++ if (!fbcon_decor_active_nores(info, vc)) { ++ vc_resize(vc, cols, rows); ++ } else { ++ fbcon_decor_disable(vc, 0); ++ fbcon_decor_call_helper("modechange", vc->vc_num); ++ } ++ + updatescrollmode(p, info, vc); + scrollback_max = 0; + scrollback_current = 0; +@@ -2954,7 +3079,9 @@ + rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols /= vc->vc_font.width; + rows /= vc->vc_font.height; +- vc_resize(vc, cols, rows); ++ if (!fbcon_decor_active_nores(info, vc)) { ++ vc_resize(vc, cols, rows); ++ } + } + + if (fg != -1) +@@ -3570,6 +3697,7 @@ + } + } + ++ fbcon_decor_exit(); + fbcon_has_exited = 1; + } + +diff -Nur linux-3.13.7.orig/drivers/video/console/fbcondecor.c linux-3.13.7/drivers/video/console/fbcondecor.c +--- linux-3.13.7.orig/drivers/video/console/fbcondecor.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.13.7/drivers/video/console/fbcondecor.c 2014-03-29 16:03:17.000000000 +0100 +@@ -0,0 +1,555 @@ ++/* ++ * linux/drivers/video/console/fbcondecor.c -- Framebuffer console decorations ++ * ++ * Copyright (C) 2004-2009 Michal Januszewski ++ * ++ * Code based upon "Bootsplash" (C) 2001-2003 ++ * Volker Poplawski , ++ * Stefan Reinauer , ++ * Steffen Winterfeldt , ++ * Michael Schroeder , ++ * Ken Wimer . ++ * ++ * Compat ioctl support by Thorsten Klein . ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "fbcon.h" ++#include "fbcondecor.h" ++ ++extern signed char con2fb_map[]; ++static int fbcon_decor_enable(struct vc_data *vc); ++char fbcon_decor_path[KMOD_PATH_LEN] = "/sbin/fbcondecor_helper"; ++static int initialized = 0; ++ ++int fbcon_decor_call_helper(char* cmd, unsigned short vc) ++{ ++ char *envp[] = { ++ "HOME=/", ++ "PATH=/sbin:/bin", ++ NULL ++ }; ++ ++ char tfb[5]; ++ char tcons[5]; ++ unsigned char fb = (int) con2fb_map[vc]; ++ ++ char *argv[] = { ++ fbcon_decor_path, ++ "2", ++ cmd, ++ tcons, ++ tfb, ++ vc_cons[vc].d->vc_decor.theme, ++ NULL ++ }; ++ ++ snprintf(tfb,5,"%d",fb); ++ snprintf(tcons,5,"%d",vc); ++ ++ return call_usermodehelper(fbcon_decor_path, argv, envp, UMH_WAIT_EXEC); ++} ++ ++/* Disables fbcondecor on a virtual console; called with console sem held. */ ++int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) ++{ ++ struct fb_info* info; ++ ++ if (!vc->vc_decor.state) ++ return -EINVAL; ++ ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ ++ if (info == NULL) ++ return -EINVAL; ++ ++ vc->vc_decor.state = 0; ++ vc_resize(vc, info->var.xres / vc->vc_font.width, ++ info->var.yres / vc->vc_font.height); ++ ++ if (fg_console == vc->vc_num && redraw) { ++ redraw_screen(vc, 0); ++ update_region(vc, vc->vc_origin + ++ vc->vc_size_row * vc->vc_top, ++ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); ++ } ++ ++ printk(KERN_INFO "fbcondecor: switched decor state to 'off' on console %d\n", ++ vc->vc_num); ++ ++ return 0; ++} ++ ++/* Enables fbcondecor on a virtual console; called with console sem held. */ ++static int fbcon_decor_enable(struct vc_data *vc) ++{ ++ struct fb_info* info; ++ ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ ++ if (vc->vc_decor.twidth == 0 || vc->vc_decor.theight == 0 || ++ info == NULL || vc->vc_decor.state || (!info->bgdecor.data && ++ vc->vc_num == fg_console)) ++ return -EINVAL; ++ ++ vc->vc_decor.state = 1; ++ vc_resize(vc, vc->vc_decor.twidth / vc->vc_font.width, ++ vc->vc_decor.theight / vc->vc_font.height); ++ ++ if (fg_console == vc->vc_num) { ++ redraw_screen(vc, 0); ++ update_region(vc, vc->vc_origin + ++ vc->vc_size_row * vc->vc_top, ++ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); ++ fbcon_decor_clear_margins(vc, info, 0); ++ } ++ ++ printk(KERN_INFO "fbcondecor: switched decor state to 'on' on console %d\n", ++ vc->vc_num); ++ ++ return 0; ++} ++ ++static inline int fbcon_decor_ioctl_dosetstate(struct vc_data *vc, unsigned int state, unsigned char origin) ++{ ++ int ret; ++ ++// if (origin == FBCON_DECOR_IO_ORIG_USER) ++ console_lock(); ++ if (!state) ++ ret = fbcon_decor_disable(vc, 1); ++ else ++ ret = fbcon_decor_enable(vc); ++// if (origin == FBCON_DECOR_IO_ORIG_USER) ++ console_unlock(); ++ ++ return ret; ++} ++ ++static inline void fbcon_decor_ioctl_dogetstate(struct vc_data *vc, unsigned int *state) ++{ ++ *state = vc->vc_decor.state; ++} ++ ++static int fbcon_decor_ioctl_dosetcfg(struct vc_data *vc, struct vc_decor *cfg, unsigned char origin) ++{ ++ struct fb_info *info; ++ int len; ++ char *tmp; ++ ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ ++ if (info == NULL || !cfg->twidth || !cfg->theight || ++ cfg->tx + cfg->twidth > info->var.xres || ++ cfg->ty + cfg->theight > info->var.yres) ++ return -EINVAL; ++ ++ len = strlen_user(cfg->theme); ++ if (!len || len > FBCON_DECOR_THEME_LEN) ++ return -EINVAL; ++ tmp = kmalloc(len, GFP_KERNEL); ++ if (!tmp) ++ return -ENOMEM; ++ if (copy_from_user(tmp, (void __user *)cfg->theme, len)) ++ return -EFAULT; ++ cfg->theme = tmp; ++ cfg->state = 0; ++ ++ /* If this ioctl is a response to a request from kernel, the console sem ++ * is already held; we also don't need to disable decor because either the ++ * new config and background picture will be successfully loaded, and the ++ * decor will stay on, or in case of a failure it'll be turned off in fbcon. */ ++// if (origin == FBCON_DECOR_IO_ORIG_USER) { ++ console_lock(); ++ if (vc->vc_decor.state) ++ fbcon_decor_disable(vc, 1); ++// } ++ ++ if (vc->vc_decor.theme) ++ kfree(vc->vc_decor.theme); ++ ++ vc->vc_decor = *cfg; ++ ++// if (origin == FBCON_DECOR_IO_ORIG_USER) ++ console_unlock(); ++ ++ printk(KERN_INFO "fbcondecor: console %d using theme '%s'\n", ++ vc->vc_num, vc->vc_decor.theme); ++ return 0; ++} ++ ++static int fbcon_decor_ioctl_dogetcfg(struct vc_data *vc, struct vc_decor *decor) ++{ ++ char __user *tmp; ++ ++ tmp = decor->theme; ++ *decor = vc->vc_decor; ++ decor->theme = tmp; ++ ++ if (vc->vc_decor.theme) { ++ if (copy_to_user(tmp, vc->vc_decor.theme, strlen(vc->vc_decor.theme) + 1)) ++ return -EFAULT; ++ } else ++ if (put_user(0, tmp)) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static int fbcon_decor_ioctl_dosetpic(struct vc_data *vc, struct fb_image *img, unsigned char origin) ++{ ++ struct fb_info *info; ++ int len; ++ u8 *tmp; ++ ++ if (vc->vc_num != fg_console) ++ return -EINVAL; ++ ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ ++ if (info == NULL) ++ return -EINVAL; ++ ++ if (img->width != info->var.xres || img->height != info->var.yres) { ++ printk(KERN_ERR "fbcondecor: picture dimensions mismatch\n"); ++ printk(KERN_ERR "%dx%d vs %dx%d\n", img->width, img->height, info->var.xres, info->var.yres); ++ return -EINVAL; ++ } ++ ++ if (img->depth != info->var.bits_per_pixel) { ++ printk(KERN_ERR "fbcondecor: picture depth mismatch\n"); ++ return -EINVAL; ++ } ++ ++ if (img->depth == 8) { ++ if (!img->cmap.len || !img->cmap.red || !img->cmap.green || ++ !img->cmap.blue) ++ return -EINVAL; ++ ++ tmp = vmalloc(img->cmap.len * 3 * 2); ++ if (!tmp) ++ return -ENOMEM; ++ ++ if (copy_from_user(tmp, ++ (void __user*)img->cmap.red, (img->cmap.len << 1)) || ++ copy_from_user(tmp + (img->cmap.len << 1), ++ (void __user*)img->cmap.green, (img->cmap.len << 1)) || ++ copy_from_user(tmp + (img->cmap.len << 2), ++ (void __user*)img->cmap.blue, (img->cmap.len << 1))) { ++ vfree(tmp); ++ return -EFAULT; ++ } ++ ++ img->cmap.transp = NULL; ++ img->cmap.red = (u16*)tmp; ++ img->cmap.green = img->cmap.red + img->cmap.len; ++ img->cmap.blue = img->cmap.green + img->cmap.len; ++ } else { ++ img->cmap.red = NULL; ++ } ++ ++ len = ((img->depth + 7) >> 3) * img->width * img->height; ++ ++ /* ++ * Allocate an additional byte so that we never go outside of the ++ * buffer boundaries in the rendering functions in a 24 bpp mode. ++ */ ++ tmp = vmalloc(len + 1); ++ ++ if (!tmp) ++ goto out; ++ ++ if (copy_from_user(tmp, (void __user*)img->data, len)) ++ goto out; ++ ++ img->data = tmp; ++ ++ /* If this ioctl is a response to a request from kernel, the console sem ++ * is already held. */ ++// if (origin == FBCON_DECOR_IO_ORIG_USER) ++ console_lock(); ++ ++ if (info->bgdecor.data) ++ vfree((u8*)info->bgdecor.data); ++ if (info->bgdecor.cmap.red) ++ vfree(info->bgdecor.cmap.red); ++ ++ info->bgdecor = *img; ++ ++ if (fbcon_decor_active_vc(vc) && fg_console == vc->vc_num) { ++ redraw_screen(vc, 0); ++ update_region(vc, vc->vc_origin + ++ vc->vc_size_row * vc->vc_top, ++ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); ++ fbcon_decor_clear_margins(vc, info, 0); ++ } ++ ++// if (origin == FBCON_DECOR_IO_ORIG_USER) ++ console_unlock(); ++ ++ return 0; ++ ++out: if (img->cmap.red) ++ vfree(img->cmap.red); ++ ++ if (tmp) ++ vfree(tmp); ++ return -ENOMEM; ++} ++ ++static long fbcon_decor_ioctl(struct file *filp, u_int cmd, u_long arg) ++{ ++ struct fbcon_decor_iowrapper __user *wrapper = (void __user*) arg; ++ struct vc_data *vc = NULL; ++ unsigned short vc_num = 0; ++ unsigned char origin = 0; ++ void __user *data = NULL; ++ ++ if (!access_ok(VERIFY_READ, wrapper, ++ sizeof(struct fbcon_decor_iowrapper))) ++ return -EFAULT; ++ ++ __get_user(vc_num, &wrapper->vc); ++ __get_user(origin, &wrapper->origin); ++ __get_user(data, &wrapper->data); ++ ++ if (!vc_cons_allocated(vc_num)) ++ return -EINVAL; ++ ++ vc = vc_cons[vc_num].d; ++ ++ switch (cmd) { ++ case FBIOCONDECOR_SETPIC: ++ { ++ struct fb_image img; ++ if (copy_from_user(&img, (struct fb_image __user *)data, sizeof(struct fb_image))) ++ return -EFAULT; ++ ++ return fbcon_decor_ioctl_dosetpic(vc, &img, origin); ++ } ++ case FBIOCONDECOR_SETCFG: ++ { ++ struct vc_decor cfg; ++ if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor))) ++ return -EFAULT; ++ ++ return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin); ++ } ++ case FBIOCONDECOR_GETCFG: ++ { ++ int rval; ++ struct vc_decor cfg; ++ ++ if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor))) ++ return -EFAULT; ++ ++ rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg); ++ ++ if (copy_to_user(data, &cfg, sizeof(struct vc_decor))) ++ return -EFAULT; ++ return rval; ++ } ++ case FBIOCONDECOR_SETSTATE: ++ { ++ unsigned int state = 0; ++ if (get_user(state, (unsigned int __user *)data)) ++ return -EFAULT; ++ return fbcon_decor_ioctl_dosetstate(vc, state, origin); ++ } ++ case FBIOCONDECOR_GETSTATE: ++ { ++ unsigned int state = 0; ++ fbcon_decor_ioctl_dogetstate(vc, &state); ++ return put_user(state, (unsigned int __user *)data); ++ } ++ ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++ ++#ifdef CONFIG_COMPAT ++ ++static long fbcon_decor_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { ++ ++ struct fbcon_decor_iowrapper32 __user *wrapper = (void __user *)arg; ++ struct vc_data *vc = NULL; ++ unsigned short vc_num = 0; ++ unsigned char origin = 0; ++ compat_uptr_t data_compat = 0; ++ void __user *data = NULL; ++ ++ if (!access_ok(VERIFY_READ, wrapper, ++ sizeof(struct fbcon_decor_iowrapper32))) ++ return -EFAULT; ++ ++ __get_user(vc_num, &wrapper->vc); ++ __get_user(origin, &wrapper->origin); ++ __get_user(data_compat, &wrapper->data); ++ data = compat_ptr(data_compat); ++ ++ if (!vc_cons_allocated(vc_num)) ++ return -EINVAL; ++ ++ vc = vc_cons[vc_num].d; ++ ++ switch (cmd) { ++ case FBIOCONDECOR_SETPIC32: ++ { ++ struct fb_image32 img_compat; ++ struct fb_image img; ++ ++ if (copy_from_user(&img_compat, (struct fb_image32 __user *)data, sizeof(struct fb_image32))) ++ return -EFAULT; ++ ++ fb_image_from_compat(img, img_compat); ++ ++ return fbcon_decor_ioctl_dosetpic(vc, &img, origin); ++ } ++ ++ case FBIOCONDECOR_SETCFG32: ++ { ++ struct vc_decor32 cfg_compat; ++ struct vc_decor cfg; ++ ++ if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32))) ++ return -EFAULT; ++ ++ vc_decor_from_compat(cfg, cfg_compat); ++ ++ return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin); ++ } ++ ++ case FBIOCONDECOR_GETCFG32: ++ { ++ int rval; ++ struct vc_decor32 cfg_compat; ++ struct vc_decor cfg; ++ ++ if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32))) ++ return -EFAULT; ++ cfg.theme = compat_ptr(cfg_compat.theme); ++ ++ rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg); ++ ++ vc_decor_to_compat(cfg_compat, cfg); ++ ++ if (copy_to_user((struct vc_decor32 __user *)data, &cfg_compat, sizeof(struct vc_decor32))) ++ return -EFAULT; ++ return rval; ++ } ++ ++ case FBIOCONDECOR_SETSTATE32: ++ { ++ compat_uint_t state_compat = 0; ++ unsigned int state = 0; ++ ++ if (get_user(state_compat, (compat_uint_t __user *)data)) ++ return -EFAULT; ++ ++ state = (unsigned int)state_compat; ++ ++ return fbcon_decor_ioctl_dosetstate(vc, state, origin); ++ } ++ ++ case FBIOCONDECOR_GETSTATE32: ++ { ++ compat_uint_t state_compat = 0; ++ unsigned int state = 0; ++ ++ fbcon_decor_ioctl_dogetstate(vc, &state); ++ state_compat = (compat_uint_t)state; ++ ++ return put_user(state_compat, (compat_uint_t __user *)data); ++ } ++ ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++#else ++ #define fbcon_decor_compat_ioctl NULL ++#endif ++ ++static struct file_operations fbcon_decor_ops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = fbcon_decor_ioctl, ++ .compat_ioctl = fbcon_decor_compat_ioctl ++}; ++ ++static struct miscdevice fbcon_decor_dev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "fbcondecor", ++ .fops = &fbcon_decor_ops ++}; ++ ++void fbcon_decor_reset() ++{ ++ int i; ++ ++ for (i = 0; i < num_registered_fb; i++) { ++ registered_fb[i]->bgdecor.data = NULL; ++ registered_fb[i]->bgdecor.cmap.red = NULL; ++ } ++ ++ for (i = 0; i < MAX_NR_CONSOLES && vc_cons[i].d; i++) { ++ vc_cons[i].d->vc_decor.state = vc_cons[i].d->vc_decor.twidth = ++ vc_cons[i].d->vc_decor.theight = 0; ++ vc_cons[i].d->vc_decor.theme = NULL; ++ } ++ ++ return; ++} ++ ++int fbcon_decor_init() ++{ ++ int i; ++ ++ fbcon_decor_reset(); ++ ++ if (initialized) ++ return 0; ++ ++ i = misc_register(&fbcon_decor_dev); ++ if (i) { ++ printk(KERN_ERR "fbcondecor: failed to register device\n"); ++ return i; ++ } ++ ++ fbcon_decor_call_helper("init", 0); ++ initialized = 1; ++ return 0; ++} ++ ++int fbcon_decor_exit(void) ++{ ++ fbcon_decor_reset(); ++ return 0; ++} ++ ++EXPORT_SYMBOL(fbcon_decor_path); +diff -Nur linux-3.13.7.orig/drivers/video/console/fbcondecor.h linux-3.13.7/drivers/video/console/fbcondecor.h +--- linux-3.13.7.orig/drivers/video/console/fbcondecor.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.13.7/drivers/video/console/fbcondecor.h 2014-03-29 21:17:27.000000000 +0100 +@@ -0,0 +1,79 @@ ++/* ++ * linux/drivers/video/console/fbcondecor.h -- Framebuffer Console Decoration headers ++ * ++ * Copyright (C) 2004 Michal Januszewski ++ * ++ */ ++ ++#ifndef __FBCON_DECOR_H ++#define __FBCON_DECOR_H ++ ++#ifndef _LINUX_FB_H ++#include ++#endif ++ ++/* This is needed for vc_cons in fbcmap.c */ ++#include ++ ++struct fb_cursor; ++struct fb_info; ++struct vc_data; ++ ++#ifdef CONFIG_FB_CON_DECOR ++/* fbcondecor.c */ ++int fbcon_decor_init(void); ++void fbcon_decor_reset(void); ++int fbcon_decor_exit(void); ++int fbcon_decor_call_helper(char* cmd, unsigned short cons); ++int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw); ++ ++/* cfbcondecor.c */ ++void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx); ++void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor); ++void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width); ++void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only); ++void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank); ++void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width); ++void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes, int srclinesbytes, int bpp); ++void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc); ++ ++/* vt.c */ ++void acquire_console_sem(void); ++void release_console_sem(void); ++void do_unblank_screen(int entering_gfx); ++ ++/* struct vc_data *y */ ++#define fbcon_decor_active_vc(y) (y->vc_decor.state && y->vc_decor.theme) ++ ++/* struct fb_info *x, struct vc_data *y */ ++#define fbcon_decor_active_nores(x,y) (x->bgdecor.data && fbcon_decor_active_vc(y)) ++ ++/* struct fb_info *x, struct vc_data *y */ ++#define fbcon_decor_active(x,y) (fbcon_decor_active_nores(x,y) && \ ++ x->bgdecor.width == x->var.xres && \ ++ x->bgdecor.height == x->var.yres && \ ++ x->bgdecor.depth == x->var.bits_per_pixel) ++ ++ ++#else /* CONFIG_FB_CON_DECOR */ ++ ++static inline void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx) {} ++static inline void fbcon_decor_putc(struct vc_data *vc, struct fb_info *info, int c, int ypos, int xpos) {} ++static inline void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) {} ++static inline void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) {} ++static inline void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only) {} ++static inline void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) {} ++static inline void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) {} ++static inline void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) {} ++static inline int fbcon_decor_call_helper(char* cmd, unsigned short cons) { return 0; } ++static inline int fbcon_decor_init(void) { return 0; } ++static inline int fbcon_decor_exit(void) { return 0; } ++static inline int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) { return 0; } ++ ++#define fbcon_decor_active_vc(y) (0) ++#define fbcon_decor_active_nores(x,y) (0) ++#define fbcon_decor_active(x,y) (0) ++ ++#endif /* CONFIG_FB_CON_DECOR */ ++ ++#endif /* __FBCON_DECOR_H */ +diff -Nur linux-3.13.7.orig/drivers/video/fbcmap.c linux-3.13.7/drivers/video/fbcmap.c +--- linux-3.13.7.orig/drivers/video/fbcmap.c 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/drivers/video/fbcmap.c 2014-03-29 16:03:17.000000000 +0100 +@@ -17,6 +17,8 @@ + #include + #include + ++#include "console/fbcondecor.h" ++ + static u16 red2[] __read_mostly = { + 0x0000, 0xaaaa + }; +@@ -249,14 +251,17 @@ + if (transp) + htransp = *transp++; + if (info->fbops->fb_setcolreg(start++, +- hred, hgreen, hblue, ++ hred, hgreen, hblue, + htransp, info)) + break; + } + } +- if (rc == 0) ++ if (rc == 0) { + fb_copy_cmap(cmap, &info->cmap); +- ++ if (fbcon_decor_active(info, vc_cons[fg_console].d) && ++ info->fix.visual == FB_VISUAL_DIRECTCOLOR) ++ fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d); ++ } + return rc; + } + +diff -Nur linux-3.13.7.orig/drivers/video/fbmem.c linux-3.13.7/drivers/video/fbmem.c +--- linux-3.13.7.orig/drivers/video/fbmem.c 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/drivers/video/fbmem.c 2014-03-29 16:03:17.000000000 +0100 +@@ -1250,15 +1250,6 @@ + u16 reserved[3]; + }; + +-struct fb_cmap32 { +- u32 start; +- u32 len; +- compat_caddr_t red; +- compat_caddr_t green; +- compat_caddr_t blue; +- compat_caddr_t transp; +-}; +- + static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, + unsigned long arg) + { +diff -Nur linux-3.13.7.orig/include/linux/console_decor.h linux-3.13.7/include/linux/console_decor.h +--- linux-3.13.7.orig/include/linux/console_decor.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.13.7/include/linux/console_decor.h 2014-03-29 16:03:17.000000000 +0100 +@@ -0,0 +1,46 @@ ++#ifndef _LINUX_CONSOLE_DECOR_H_ ++#define _LINUX_CONSOLE_DECOR_H_ 1 ++ ++/* A structure used by the framebuffer console decorations (drivers/video/console/fbcondecor.c) */ ++struct vc_decor { ++ __u8 bg_color; /* The color that is to be treated as transparent */ ++ __u8 state; /* Current decor state: 0 = off, 1 = on */ ++ __u16 tx, ty; /* Top left corner coordinates of the text field */ ++ __u16 twidth, theight; /* Width and height of the text field */ ++ char* theme; ++}; ++ ++#ifdef __KERNEL__ ++#ifdef CONFIG_COMPAT ++#include ++ ++struct vc_decor32 { ++ __u8 bg_color; /* The color that is to be treated as transparent */ ++ __u8 state; /* Current decor state: 0 = off, 1 = on */ ++ __u16 tx, ty; /* Top left corner coordinates of the text field */ ++ __u16 twidth, theight; /* Width and height of the text field */ ++ compat_uptr_t theme; ++}; ++ ++#define vc_decor_from_compat(to, from) \ ++ (to).bg_color = (from).bg_color; \ ++ (to).state = (from).state; \ ++ (to).tx = (from).tx; \ ++ (to).ty = (from).ty; \ ++ (to).twidth = (from).twidth; \ ++ (to).theight = (from).theight; \ ++ (to).theme = compat_ptr((from).theme) ++ ++#define vc_decor_to_compat(to, from) \ ++ (to).bg_color = (from).bg_color; \ ++ (to).state = (from).state; \ ++ (to).tx = (from).tx; \ ++ (to).ty = (from).ty; \ ++ (to).twidth = (from).twidth; \ ++ (to).theight = (from).theight; \ ++ (to).theme = ptr_to_compat((from).theme) ++ ++#endif /* CONFIG_COMPAT */ ++#endif /* __KERNEL__ */ ++ ++#endif +diff -Nur linux-3.13.7.orig/include/linux/console_struct.h linux-3.13.7/include/linux/console_struct.h +--- linux-3.13.7.orig/include/linux/console_struct.h 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/include/linux/console_struct.h 2014-03-29 16:03:17.000000000 +0100 +@@ -19,6 +19,7 @@ + struct vt_struct; + + #define NPAR 16 ++#include + + struct vc_data { + struct tty_port port; /* Upper level data */ +@@ -107,6 +108,8 @@ + unsigned long vc_uni_pagedir; + unsigned long *vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */ + bool vc_panic_force_write; /* when oops/panic this VC can accept forced output/blanking */ ++ ++ struct vc_decor vc_decor; + /* additional information is in vt_kern.h */ + }; + +diff -Nur linux-3.13.7.orig/include/linux/fb.h linux-3.13.7/include/linux/fb.h +--- linux-3.13.7.orig/include/linux/fb.h 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/include/linux/fb.h 2014-03-29 16:03:17.000000000 +0100 +@@ -219,6 +219,34 @@ + }; + #endif + ++#ifdef __KERNEL__ ++#ifdef CONFIG_COMPAT ++struct fb_image32 { ++ __u32 dx; /* Where to place image */ ++ __u32 dy; ++ __u32 width; /* Size of image */ ++ __u32 height; ++ __u32 fg_color; /* Only used when a mono bitmap */ ++ __u32 bg_color; ++ __u8 depth; /* Depth of the image */ ++ const compat_uptr_t data; /* Pointer to image data */ ++ struct fb_cmap32 cmap; /* color map info */ ++}; ++ ++#define fb_image_from_compat(to, from) \ ++ (to).dx = (from).dx; \ ++ (to).dy = (from).dy; \ ++ (to).width = (from).width; \ ++ (to).height = (from).height; \ ++ (to).fg_color = (from).fg_color; \ ++ (to).bg_color = (from).bg_color; \ ++ (to).depth = (from).depth; \ ++ (to).data = compat_ptr((from).data); \ ++ fb_cmap_from_compat((to).cmap, (from).cmap) ++ ++#endif /* CONFIG_COMPAT */ ++#endif /* __KERNEL__ */ ++ + /* + * Frame buffer operations + * +@@ -489,6 +517,9 @@ + #define FBINFO_STATE_SUSPENDED 1 + u32 state; /* Hardware state i.e suspend */ + void *fbcon_par; /* fbcon use-only private area */ ++ ++ struct fb_image bgdecor; ++ + /* From here on everything is device dependent */ + void *par; + /* we need the PCI or similar aperture base/size not +diff -Nur linux-3.13.7.orig/include/uapi/linux/fb.h linux-3.13.7/include/uapi/linux/fb.h +--- linux-3.13.7.orig/include/uapi/linux/fb.h 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/include/uapi/linux/fb.h 2014-03-29 16:03:17.000000000 +0100 +@@ -8,6 +8,25 @@ + + #define FB_MAX 32 /* sufficient for now */ + ++struct fbcon_decor_iowrapper ++{ ++ unsigned short vc; /* Virtual console */ ++ unsigned char origin; /* Point of origin of the request */ ++ void *data; ++}; ++ ++#ifdef __KERNEL__ ++#ifdef CONFIG_COMPAT ++#include ++struct fbcon_decor_iowrapper32 ++{ ++ unsigned short vc; /* Virtual console */ ++ unsigned char origin; /* Point of origin of the request */ ++ compat_uptr_t data; ++}; ++#endif /* CONFIG_COMPAT */ ++#endif /* __KERNEL__ */ ++ + /* ioctls + 0x46 is 'F' */ + #define FBIOGET_VSCREENINFO 0x4600 +@@ -35,6 +54,25 @@ + #define FBIOGET_DISPINFO 0x4618 + #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) + ++#define FBIOCONDECOR_SETCFG _IOWR('F', 0x19, struct fbcon_decor_iowrapper) ++#define FBIOCONDECOR_GETCFG _IOR('F', 0x1A, struct fbcon_decor_iowrapper) ++#define FBIOCONDECOR_SETSTATE _IOWR('F', 0x1B, struct fbcon_decor_iowrapper) ++#define FBIOCONDECOR_GETSTATE _IOR('F', 0x1C, struct fbcon_decor_iowrapper) ++#define FBIOCONDECOR_SETPIC _IOWR('F', 0x1D, struct fbcon_decor_iowrapper) ++#ifdef __KERNEL__ ++#ifdef CONFIG_COMPAT ++#define FBIOCONDECOR_SETCFG32 _IOWR('F', 0x19, struct fbcon_decor_iowrapper32) ++#define FBIOCONDECOR_GETCFG32 _IOR('F', 0x1A, struct fbcon_decor_iowrapper32) ++#define FBIOCONDECOR_SETSTATE32 _IOWR('F', 0x1B, struct fbcon_decor_iowrapper32) ++#define FBIOCONDECOR_GETSTATE32 _IOR('F', 0x1C, struct fbcon_decor_iowrapper32) ++#define FBIOCONDECOR_SETPIC32 _IOWR('F', 0x1D, struct fbcon_decor_iowrapper32) ++#endif /* CONFIG_COMPAT */ ++#endif /* __KERNEL__ */ ++ ++#define FBCON_DECOR_THEME_LEN 128 /* Maximum lenght of a theme name */ ++#define FBCON_DECOR_IO_ORIG_KERNEL 0 /* Kernel ioctl origin */ ++#define FBCON_DECOR_IO_ORIG_USER 1 /* User ioctl origin */ ++ + #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ + #define FB_TYPE_PLANES 1 /* Non interleaved planes */ + #define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes */ +@@ -277,6 +315,29 @@ + __u32 reserved[4]; /* Reserved for future compatibility */ + }; + ++#ifdef __KERNEL__ ++#ifdef CONFIG_COMPAT ++struct fb_cmap32 { ++ __u32 start; ++ __u32 len; /* Number of entries */ ++ compat_uptr_t red; /* Red values */ ++ compat_uptr_t green; ++ compat_uptr_t blue; ++ compat_uptr_t transp; /* transparency, can be NULL */ ++}; ++ ++#define fb_cmap_from_compat(to, from) \ ++ (to).start = (from).start; \ ++ (to).len = (from).len; \ ++ (to).red = compat_ptr((from).red); \ ++ (to).green = compat_ptr((from).green); \ ++ (to).blue = compat_ptr((from).blue); \ ++ (to).transp = compat_ptr((from).transp) ++ ++#endif /* CONFIG_COMPAT */ ++#endif /* __KERNEL__ */ ++ ++ + struct fb_cmap { + __u32 start; /* First entry */ + __u32 len; /* Number of entries */ +diff -Nur linux-3.13.7.orig/kernel/sysctl.c linux-3.13.7/kernel/sysctl.c +--- linux-3.13.7.orig/kernel/sysctl.c 2014-03-24 05:45:42.000000000 +0100 ++++ linux-3.13.7/kernel/sysctl.c 2014-03-29 16:03:17.000000000 +0100 +@@ -143,6 +143,10 @@ + static int ngroups_max = NGROUPS_MAX; + static const int cap_last_cap = CAP_LAST_CAP; + ++#ifdef CONFIG_FB_CON_DECOR ++extern char fbcon_decor_path[]; ++#endif ++ + #ifdef CONFIG_INOTIFY_USER + #include + #endif +@@ -252,6 +256,15 @@ + .mode = 0555, + .child = dev_table, + }, ++#ifdef CONFIG_FB_CON_DECOR ++ { ++ .procname = "fbcondecor", ++ .data = &fbcon_decor_path, ++ .maxlen = KMOD_PATH_LEN, ++ .mode = 0644, ++ .proc_handler = &proc_dostring, ++ }, ++#endif + { } + }; + diff --git a/target/linux/patches/3.14.45/gemalto.patch b/target/linux/patches/3.14.45/gemalto.patch new file mode 100644 index 000000000..65f7af1d7 --- /dev/null +++ b/target/linux/patches/3.14.45/gemalto.patch @@ -0,0 +1,11 @@ +diff -Nur linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c linux-2.6.36/drivers/serial/8250/serial_cs.c +--- linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/drivers/tty/serial/8250/serial_cs.c 2010-12-13 23:03:40.000000000 +0100 +@@ -794,6 +794,7 @@ + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), ++ PCMCIA_DEVICE_MANF_CARD(0x0157, 0x0100), /* Gemalto SCR */ + PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ + PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ + PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), diff --git a/target/linux/patches/3.14.45/initramfs-nosizelimit.patch b/target/linux/patches/3.14.45/initramfs-nosizelimit.patch new file mode 100644 index 000000000..40d2f6bd8 --- /dev/null +++ b/target/linux/patches/3.14.45/initramfs-nosizelimit.patch @@ -0,0 +1,57 @@ +From 9a18df7a71bfa620b1278777d64783a359d7eb4e Mon Sep 17 00:00:00 2001 +From: Thorsten Glaser +Date: Sun, 4 May 2014 01:37:54 +0200 +Subject: [PATCH] mount tmpfs-as-rootfs (initramfs) with -o + nr_blocks=0,nr_inodes=0 + +I would have preferred to write this patch to be able to pass +rootflags=nr_blocks=0,nr_inodes=0 on the kernel command line, +and then hand these rootflags over to the initramfs (tmpfs) +mount in the same way the kernel hands them over to the block +device rootfs mount. But at least the Debian/m68k initrd also +parses $rootflags from the environment and adds it to the call +to the user-space mount for the eventual root device, which +would make the kernel command line rootflags option be used in +both places (tmpfs and e.g. ext4) which is guaranteed to error +out in at least one of them. + +This change is intended to aid people in a setup where the +initrd is the final root filesystem, i.e. not mounted over. +This is especially useful in automated tests running on qemu +for boards with constrained memory (e.g. 64 MiB on sh4). + +Considering that the initramfs is normally emptied out then +overmounted, this change is probably safe for setups where +initramfs just hosts early userspace, too, since the tmpfs +backing it is not accessible any more later on, AFAICT. + +Signed-off-by: Thorsten Glaser +--- + init/do_mounts.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/init/do_mounts.c b/init/do_mounts.c +index 82f2288..55a4cfe 100644 +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -594,6 +594,7 @@ out: + } + + static bool is_tmpfs; ++static char tmpfs_rootflags[] = "nr_blocks=0,nr_inodes=0"; + static struct dentry *rootfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) + { +@@ -606,6 +607,9 @@ static struct dentry *rootfs_mount(struct file_system_type *fs_type, + if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) + fill = shmem_fill_super; + ++ if (is_tmpfs) ++ data = tmpfs_rootflags; ++ + return mount_nodev(fs_type, flags, data, fill); + } + +-- +2.0.0.rc0 + diff --git a/target/linux/patches/3.14.45/lemote-rfkill.patch b/target/linux/patches/3.14.45/lemote-rfkill.patch new file mode 100644 index 000000000..a61488434 --- /dev/null +++ b/target/linux/patches/3.14.45/lemote-rfkill.patch @@ -0,0 +1,21 @@ +diff -Nur linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c +--- linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-19 00:15:34.000000000 +0100 ++++ linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-27 23:29:46.000000000 +0200 +@@ -22,6 +22,9 @@ + + static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) + { ++#ifdef CONFIG_LEMOTE_MACH2F ++ return 1; ++#else + u8 gpio; + + gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); +@@ -29,6 +32,7 @@ + gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); + + return gpio & priv->rfkill_mask; ++#endif + } + + void rtl8187_rfkill_init(struct ieee80211_hw *hw) diff --git a/target/linux/patches/3.14.45/microblaze-axi.patch b/target/linux/patches/3.14.45/microblaze-axi.patch new file mode 100644 index 000000000..1a4b17d8c --- /dev/null +++ b/target/linux/patches/3.14.45/microblaze-axi.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.13.3.orig/drivers/net/ethernet/xilinx/xilinx_axienet_main.c linux-3.13.3/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +--- linux-3.13.3.orig/drivers/net/ethernet/xilinx/xilinx_axienet_main.c 2014-02-13 23:00:14.000000000 +0100 ++++ linux-3.13.3/drivers/net/ethernet/xilinx/xilinx_axienet_main.c 2014-02-24 08:03:57.000000000 +0100 +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + #include diff --git a/target/linux/patches/3.14.45/microblaze-ethernet.patch b/target/linux/patches/3.14.45/microblaze-ethernet.patch new file mode 100644 index 000000000..742ab477e --- /dev/null +++ b/target/linux/patches/3.14.45/microblaze-ethernet.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c +--- linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-11-29 19:42:37.000000000 +0100 ++++ linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-12-23 20:01:14.000000000 +0100 +@@ -1282,6 +1282,7 @@ + { .compatible = "xlnx,opb-ethernetlite-1.01.b", }, + { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, + { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, ++ { .compatible = "xlnx,xps-ethernetlite-2.00.b", }, + { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, + { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, + { /* end of list */ }, diff --git a/target/linux/patches/3.14.45/mkpiggy.patch b/target/linux/patches/3.14.45/mkpiggy.patch new file mode 100644 index 000000000..751678b74 --- /dev/null +++ b/target/linux/patches/3.14.45/mkpiggy.patch @@ -0,0 +1,28 @@ +diff -Nur linux-3.13.3.orig/arch/x86/boot/compressed/mkpiggy.c linux-3.13.3/arch/x86/boot/compressed/mkpiggy.c +--- linux-3.13.3.orig/arch/x86/boot/compressed/mkpiggy.c 2014-02-13 23:00:14.000000000 +0100 ++++ linux-3.13.3/arch/x86/boot/compressed/mkpiggy.c 2014-02-17 11:09:06.000000000 +0100 +@@ -29,7 +29,14 @@ + #include + #include + #include +-#include ++ ++static uint32_t getle32(const void *p) ++{ ++ const uint8_t *cp = p; ++ ++ return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + ++ ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); ++} + + int main(int argc, char *argv[]) + { +@@ -63,7 +70,7 @@ + } + + ilen = ftell(f); +- olen = get_unaligned_le32(&olen); ++ olen = getle32(&olen); + + /* + * Now we have the input (compressed) and output (uncompressed) diff --git a/target/linux/patches/3.14.45/mptcp.patch b/target/linux/patches/3.14.45/mptcp.patch new file mode 100644 index 000000000..af2dc7837 --- /dev/null +++ b/target/linux/patches/3.14.45/mptcp.patch @@ -0,0 +1,17133 @@ +diff -Nur linux-3.14.45.orig/drivers/infiniband/hw/cxgb4/cm.c linux-3.14.45/drivers/infiniband/hw/cxgb4/cm.c +--- linux-3.14.45.orig/drivers/infiniband/hw/cxgb4/cm.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/drivers/infiniband/hw/cxgb4/cm.c 2015-06-24 14:15:48.871862463 +0200 +@@ -3162,7 +3162,7 @@ + */ + memset(&tmp_opt, 0, sizeof(tmp_opt)); + tcp_clear_options(&tmp_opt); +- tcp_parse_options(skb, &tmp_opt, 0, NULL); ++ tcp_parse_options(skb, &tmp_opt, NULL, 0, NULL); + + req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req)); + memset(req, 0, sizeof(*req)); +diff -Nur linux-3.14.45.orig/include/linux/ipv6.h linux-3.14.45/include/linux/ipv6.h +--- linux-3.14.45.orig/include/linux/ipv6.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/linux/ipv6.h 2015-06-24 14:15:48.871862463 +0200 +@@ -309,12 +309,6 @@ + return NULL; + } + +-static inline struct inet6_request_sock * +- inet6_rsk(const struct request_sock *rsk) +-{ +- return NULL; +-} +- + static inline struct raw6_sock *raw6_sk(const struct sock *sk) + { + return NULL; +diff -Nur linux-3.14.45.orig/include/linux/tcp.h linux-3.14.45/include/linux/tcp.h +--- linux-3.14.45.orig/include/linux/tcp.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/linux/tcp.h 2015-06-24 14:15:48.871862463 +0200 +@@ -72,6 +72,53 @@ + u32 end_seq; + }; + ++struct tcp_out_options { ++ u16 options; /* bit field of OPTION_* */ ++ u8 ws; /* window scale, 0 to disable */ ++ u8 num_sack_blocks;/* number of SACK blocks to include */ ++ u8 hash_size; /* bytes in hash_location */ ++ u16 mss; /* 0 to disable */ ++ __u8 *hash_location; /* temporary pointer, overloaded */ ++ __u32 tsval, tsecr; /* need to include OPTION_TS */ ++ struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie */ ++#ifdef CONFIG_MPTCP ++ u16 mptcp_options; /* bit field of MPTCP related OPTION_* */ ++ u8 dss_csum:1, ++ add_addr_v4:1, ++ add_addr_v6:1; /* dss-checksum required? */ ++ ++ __u32 data_seq; /* data sequence number, for MPTCP */ ++ __u32 data_ack; /* data ack, for MPTCP */ ++ ++ union { ++ struct { ++ __u64 sender_key; /* sender's key for mptcp */ ++ __u64 receiver_key; /* receiver's key for mptcp */ ++ } mp_capable; ++ ++ struct { ++ __u64 sender_truncated_mac; ++ __u32 sender_nonce; ++ /* random number of the sender */ ++ __u32 token; /* token for mptcp */ ++ } mp_join_syns; ++ }; ++ ++ struct { ++ struct in_addr addr; ++ u8 addr_id; ++ } add_addr4; ++ ++ struct { ++ struct in6_addr addr; ++ u8 addr_id; ++ } add_addr6; ++ ++ u16 remove_addrs; /* list of address id */ ++ u8 addr_id; /* address id (mp_join or add_address) */ ++#endif /* CONFIG_MPTCP */ ++}; ++ + /*These are used to set the sack_ok field in struct tcp_options_received */ + #define TCP_SACK_SEEN (1 << 0) /*1 = peer is SACK capable, */ + #define TCP_FACK_ENABLED (1 << 1) /*1 = FACK is enabled locally*/ +@@ -95,6 +142,9 @@ + u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ + }; + ++struct mptcp_cb; ++struct mptcp_tcp_sock; ++ + static inline void tcp_clear_options(struct tcp_options_received *rx_opt) + { + rx_opt->tstamp_ok = rx_opt->sack_ok = 0; +@@ -123,6 +173,7 @@ + * FastOpen it's the seq# + * after data-in-SYN. + */ ++ u8 saw_mpc:1; + }; + + static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req) +@@ -130,6 +181,8 @@ + return (struct tcp_request_sock *)req; + } + ++struct tcp_md5sig_key; ++ + struct tcp_sock { + /* inet_connection_sock has to be the first member of tcp_sock */ + struct inet_connection_sock inet_conn; +@@ -323,6 +376,45 @@ + * socket. Used to retransmit SYNACKs etc. + */ + struct request_sock *fastopen_rsk; ++ ++ ++ struct mptcp_cb *mpcb; ++ struct sock *meta_sk; ++ /* We keep these flags even if CONFIG_MPTCP is not checked, because ++ * it allows checking MPTCP capability just by checking the mpc flag, ++ * rather than adding ifdefs everywhere. ++ */ ++ u16 mpc:1, /* Other end is multipath capable */ ++ inside_tk_table:1, /* Is the tcp_sock inside the token-table? */ ++ send_mp_fclose:1, ++ request_mptcp:1, /* Did we send out an MP_CAPABLE? ++ * (this speeds up mptcp_doit() in tcp_recvmsg) ++ */ ++ mptcp_enabled:1, /* Is MPTCP enabled from the application ? */ ++ pf:1, /* Potentially Failed state: when this flag is set, we ++ * stop using the subflow ++ */ ++ mp_killed:1, /* Killed with a tcp_done in mptcp? */ ++ was_meta_sk:1, /* This was a meta sk (in case of reuse) */ ++ close_it:1, /* Must close socket in mptcp_data_ready? */ ++ closing:1; ++ struct mptcp_tcp_sock *mptcp; ++#ifdef CONFIG_MPTCP ++ struct hlist_nulls_node tk_table; ++ u32 mptcp_loc_token; ++ u64 mptcp_loc_key; ++#endif /* CONFIG_MPTCP */ ++ ++ /* Functions that depend on the value of the mpc flag */ ++ u32 (*__select_window)(struct sock *sk); ++ u16 (*select_window)(struct sock *sk); ++ void (*select_initial_window)(int __space, __u32 mss, __u32 *rcv_wnd, ++ __u32 *window_clamp, int wscale_ok, ++ __u8 *rcv_wscale, __u32 init_rcv_wnd, ++ const struct sock *sk); ++ void (*init_buffer_space)(struct sock *sk); ++ void (*set_rto)(struct sock *sk); ++ bool (*should_expand_sndbuf)(const struct sock *sk); + }; + + enum tsq_flags { +@@ -334,6 +426,8 @@ + TCP_MTU_REDUCED_DEFERRED, /* tcp_v{4|6}_err() could not call + * tcp_v{4|6}_mtu_reduced() + */ ++ MPTCP_PATH_MANAGER, /* MPTCP deferred creation of new subflows */ ++ MPTCP_SUB_DEFERRED, /* A subflow got deferred - process them */ + }; + + static inline struct tcp_sock *tcp_sk(const struct sock *sk) +@@ -352,6 +446,7 @@ + #ifdef CONFIG_TCP_MD5SIG + struct tcp_md5sig_key *tw_md5_key; + #endif ++ struct mptcp_tw *mptcp_tw; + }; + + static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk) +diff -Nur linux-3.14.45.orig/include/net/inet6_connection_sock.h linux-3.14.45/include/net/inet6_connection_sock.h +--- linux-3.14.45.orig/include/net/inet6_connection_sock.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/net/inet6_connection_sock.h 2015-06-24 14:15:48.871862463 +0200 +@@ -27,6 +27,8 @@ + + struct dst_entry *inet6_csk_route_req(struct sock *sk, struct flowi6 *fl6, + const struct request_sock *req); ++u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, ++ const u32 rnd, const u32 synq_hsize); + + struct request_sock *inet6_csk_search_req(const struct sock *sk, + struct request_sock ***prevp, +diff -Nur linux-3.14.45.orig/include/net/inet_common.h linux-3.14.45/include/net/inet_common.h +--- linux-3.14.45.orig/include/net/inet_common.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/net/inet_common.h 2015-06-24 14:15:48.871862463 +0200 +@@ -1,6 +1,8 @@ + #ifndef _INET_COMMON_H + #define _INET_COMMON_H + ++#include ++ + extern const struct proto_ops inet_stream_ops; + extern const struct proto_ops inet_dgram_ops; + +@@ -13,6 +15,8 @@ + struct sockaddr; + struct socket; + ++int inet_create(struct net *net, struct socket *sock, int protocol, int kern); ++int inet6_create(struct net *net, struct socket *sock, int protocol, int kern); + int inet_release(struct socket *sock); + int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags); +diff -Nur linux-3.14.45.orig/include/net/inet_connection_sock.h linux-3.14.45/include/net/inet_connection_sock.h +--- linux-3.14.45.orig/include/net/inet_connection_sock.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/net/inet_connection_sock.h 2015-06-24 14:15:48.871862463 +0200 +@@ -244,6 +244,9 @@ + + struct sock *inet_csk_accept(struct sock *sk, int flags, int *err); + ++u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd, ++ const u32 synq_hsize); ++ + struct request_sock *inet_csk_search_req(const struct sock *sk, + struct request_sock ***prevp, + const __be16 rport, +diff -Nur linux-3.14.45.orig/include/net/mptcp.h linux-3.14.45/include/net/mptcp.h +--- linux-3.14.45.orig/include/net/mptcp.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/include/net/mptcp.h 2015-06-24 14:15:48.871862463 +0200 +@@ -0,0 +1,1471 @@ ++/* ++ * MPTCP implementation ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer & Author: ++ * Christoph Paasch ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef _MPTCP_H ++#define _MPTCP_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ #define ntohll(x) be64_to_cpu(x) ++ #define htonll(x) cpu_to_be64(x) ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ #define ntohll(x) (x) ++ #define htonll(x) (x) ++#endif ++ ++/* Max number of local or remote addresses we can store. ++ * When changing, see the bitfield below in mptcp_loc4/6. */ ++#define MPTCP_MAX_ADDR 8 ++ ++#define MPTCP_SUBFLOW_RETRY_DELAY 1000 ++ ++struct mptcp_loc4 { ++ u8 loc4_id; ++ u8 low_prio:1; ++ struct in_addr addr; ++}; ++ ++struct mptcp_rem4 { ++ u8 rem4_id; ++ u8 bitfield; ++ u8 retry_bitfield; ++ __be16 port; ++ struct in_addr addr; ++}; ++ ++struct mptcp_loc6 { ++ u8 loc6_id; ++ u8 low_prio:1; ++ struct in6_addr addr; ++}; ++ ++struct mptcp_rem6 { ++ u8 rem6_id; ++ u8 bitfield; ++ u8 retry_bitfield; ++ __be16 port; ++ struct in6_addr addr; ++}; ++ ++struct mptcp_request_sock { ++ struct tcp_request_sock req; ++ struct mptcp_cb *mpcb; ++ /* Collision list in the tuple hashtable. We need to find ++ * the req sock when receiving the third msg of the 3-way handshake, ++ * since that one does not contain the token. If this makes ++ * the request sock too long, we can use kmalloc'ed specific entries for ++ * that tuple hashtable. At the moment, though, I extend the ++ * request_sock. ++ */ ++ struct list_head collide_tuple; ++ struct hlist_nulls_node collide_tk; ++ u32 mptcp_rem_nonce; ++ u32 mptcp_loc_token; ++ u64 mptcp_loc_key; ++ u64 mptcp_rem_key; ++ u64 mptcp_hash_tmac; ++ u32 mptcp_loc_nonce; ++ u8 loc_id; ++ u8 rem_id; /* Address-id in the MP_JOIN */ ++ u8 dss_csum:1, ++ low_prio:1; ++}; ++ ++struct mptcp_options_received { ++ u16 saw_mpc:1, ++ dss_csum:1, ++ drop_me:1, ++ ++ is_mp_join:1, ++ join_ack:1, ++ ++ saw_low_prio:2, /* 0x1 - low-prio set for this subflow ++ * 0x2 - low-prio set for another subflow ++ */ ++ low_prio:1, ++ ++ saw_add_addr:2, /* Saw at least one add_addr option: ++ * 0x1: IPv4 - 0x2: IPv6 ++ */ ++ more_add_addr:1, /* Saw one more add-addr. */ ++ ++ saw_rem_addr:1, /* Saw at least one rem_addr option */ ++ more_rem_addr:1, /* Saw one more rem-addr. */ ++ ++ mp_fail:1, ++ mp_fclose:1; ++ u8 rem_id; /* Address-id in the MP_JOIN */ ++ u8 prio_addr_id; /* Address-id in the MP_PRIO */ ++ ++ const unsigned char *add_addr_ptr; /* Pointer to add-address option */ ++ const unsigned char *rem_addr_ptr; /* Pointer to rem-address option */ ++ ++ u32 data_ack; ++ u32 data_seq; ++ u16 data_len; ++ ++ u32 mptcp_rem_token;/* Remote token */ ++ ++ /* Key inside the option (from mp_capable or fast_close) */ ++ u64 mptcp_key; ++ ++ u32 mptcp_recv_nonce; ++ u64 mptcp_recv_tmac; ++ u8 mptcp_recv_mac[20]; ++}; ++ ++struct mptcp_tcp_sock { ++ struct tcp_sock *next; /* Next subflow socket */ ++ struct list_head cb_list; ++ struct mptcp_options_received rx_opt; ++ ++ /* Those three fields record the current mapping */ ++ u64 map_data_seq; ++ u32 map_subseq; ++ u16 map_data_len; ++ u16 slave_sk:1, ++ fully_established:1, ++ establish_increased:1, ++ second_packet:1, ++ attached:1, ++ send_mp_fail:1, ++ include_mpc:1, ++ mapping_present:1, ++ map_data_fin:1, ++ low_prio:1, /* use this socket as backup */ ++ rcv_low_prio:1, /* Peer sent low-prio option to us */ ++ send_mp_prio:1, /* Trigger to send mp_prio on this socket */ ++ pre_established:1; /* State between sending 3rd ACK and ++ * receiving the fourth ack of new subflows. ++ */ ++ ++ /* isn: needed to translate abs to relative subflow seqnums */ ++ u32 snt_isn; ++ u32 rcv_isn; ++ u32 last_data_seq; ++ u8 path_index; ++ u8 loc_id; ++ u8 rem_id; ++ ++ u32 last_rbuf_opti; /* Timestamp of last rbuf optimization */ ++ unsigned int sent_pkts; ++ ++ struct sk_buff *shortcut_ofoqueue; /* Shortcut to the current modified ++ * skb in the ofo-queue. ++ */ ++ ++ int init_rcv_wnd; ++ u32 infinite_cutoff_seq; ++ struct delayed_work work; ++ u32 mptcp_loc_nonce; ++ struct tcp_sock *tp; /* Where is my daddy? */ ++ u32 last_end_data_seq; ++ ++ /* MP_JOIN subflow: timer for retransmitting the 3rd ack */ ++ struct timer_list mptcp_ack_timer; ++ ++ /* HMAC of the third ack */ ++ char sender_mac[20]; ++}; ++ ++struct mptcp_tw { ++ struct list_head list; ++ u64 loc_key; ++ u64 rcv_nxt; ++ struct mptcp_cb __rcu *mpcb; ++ u8 meta_tw:1, ++ in_list:1; ++}; ++ ++#define MPTCP_PM_NAME_MAX 16 ++struct mptcp_pm_ops { ++ struct list_head list; ++ ++ /* Signal the creation of a new MPTCP-session. */ ++ void (*new_session)(struct sock *meta_sk, int index); ++ void (*release_sock)(struct sock *meta_sk); ++ void (*fully_established)(struct sock *meta_sk); ++ void (*new_remote_address)(struct sock *meta_sk); ++ int (*get_local_index)(sa_family_t family, union inet_addr *addr, ++ struct net *net); ++ int (*get_local_id)(sa_family_t family, union inet_addr *addr, ++ struct net *net); ++ void (*addr_signal)(struct sock *sk, unsigned *size, ++ struct tcp_out_options *opts, struct sk_buff *skb); ++ ++ char name[MPTCP_PM_NAME_MAX]; ++ struct module *owner; ++}; ++ ++struct mptcp_cb { ++ struct sock *meta_sk; ++ ++ /* list of sockets in this multipath connection */ ++ struct tcp_sock *connection_list; ++ /* list of sockets that need a call to release_cb */ ++ struct list_head callback_list; ++ ++ spinlock_t tw_lock; ++ struct list_head tw_list; ++ unsigned char mptw_state; ++ ++ atomic_t mpcb_refcnt; ++ ++ /* High-order bits of 64-bit sequence numbers */ ++ u32 snd_high_order[2]; ++ u32 rcv_high_order[2]; ++ ++ u16 send_infinite_mapping:1, ++ in_time_wait:1, ++ list_rcvd:1, /* XXX TO REMOVE */ ++ dss_csum:1, ++ server_side:1, ++ infinite_mapping_rcv:1, ++ infinite_mapping_snd:1, ++ dfin_combined:1, /* Was the DFIN combined with subflow-fin? */ ++ passive_close:1, ++ snd_hiseq_index:1, /* Index in snd_high_order of snd_nxt */ ++ rcv_hiseq_index:1; /* Index in rcv_high_order of rcv_nxt */ ++ ++ /* socket count in this connection */ ++ u8 cnt_subflows; ++ u8 cnt_established; ++ ++ u32 noneligible; /* Path mask of temporarily non ++ * eligible subflows by the scheduler ++ */ ++ ++ struct sk_buff_head reinject_queue; ++ ++ u8 dfin_path_index; ++ ++#define MPTCP_PM_SIZE 320 ++ u8 mptcp_pm[MPTCP_PM_SIZE] __aligned(8); ++ struct mptcp_pm_ops *pm_ops; ++ ++ /* Mutex needed, because otherwise mptcp_close will complain that the ++ * socket is owned by the user. ++ * E.g., mptcp_sub_close_wq is taking the meta-lock. ++ */ ++ struct mutex mpcb_mutex; ++ ++ /* Master socket, also part of the connection_list, this ++ * socket is the one that the application sees. ++ */ ++ struct sock *master_sk; ++ ++ u64 csum_cutoff_seq; ++ ++ __u64 mptcp_loc_key; ++ __u32 mptcp_loc_token; ++ __u64 mptcp_rem_key; ++ __u32 mptcp_rem_token; ++ ++ /* Create a new subflow - necessary because the meta-sk may be IPv4, but ++ * the new subflow can be IPv6 ++ */ ++ struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb, ++ struct request_sock *req, ++ struct dst_entry *dst); ++ ++ /* Remote addresses */ ++ struct mptcp_rem4 remaddr4[MPTCP_MAX_ADDR]; ++ u8 rem4_bits; ++ ++ struct mptcp_rem6 remaddr6[MPTCP_MAX_ADDR]; ++ u8 rem6_bits; ++ ++ u32 path_index_bits; ++ /* Next pi to pick up in case a new path becomes available */ ++ u8 next_path_index; ++ ++ /* Original snd/rcvbuf of the initial subflow. ++ * Used for the new subflows on the server-side to allow correct ++ * autotuning ++ */ ++ int orig_sk_rcvbuf; ++ int orig_sk_sndbuf; ++ u32 orig_window_clamp; ++}; ++ ++#define MPTCP_SUB_CAPABLE 0 ++#define MPTCP_SUB_LEN_CAPABLE_SYN 12 ++#define MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN 12 ++#define MPTCP_SUB_LEN_CAPABLE_ACK 20 ++#define MPTCP_SUB_LEN_CAPABLE_ACK_ALIGN 20 ++ ++#define MPTCP_SUB_JOIN 1 ++#define MPTCP_SUB_LEN_JOIN_SYN 12 ++#define MPTCP_SUB_LEN_JOIN_SYN_ALIGN 12 ++#define MPTCP_SUB_LEN_JOIN_SYNACK 16 ++#define MPTCP_SUB_LEN_JOIN_SYNACK_ALIGN 16 ++#define MPTCP_SUB_LEN_JOIN_ACK 24 ++#define MPTCP_SUB_LEN_JOIN_ACK_ALIGN 24 ++ ++#define MPTCP_SUB_DSS 2 ++#define MPTCP_SUB_LEN_DSS 4 ++#define MPTCP_SUB_LEN_DSS_ALIGN 4 ++ ++/* Lengths for seq and ack are the ones without the generic MPTCP-option header, ++ * as they are part of the DSS-option. ++ * To get the total length, just add the different options together. ++ */ ++#define MPTCP_SUB_LEN_SEQ 10 ++#define MPTCP_SUB_LEN_SEQ_CSUM 12 ++#define MPTCP_SUB_LEN_SEQ_ALIGN 12 ++ ++#define MPTCP_SUB_LEN_SEQ_64 14 ++#define MPTCP_SUB_LEN_SEQ_CSUM_64 16 ++#define MPTCP_SUB_LEN_SEQ_64_ALIGN 16 ++ ++#define MPTCP_SUB_LEN_ACK 4 ++#define MPTCP_SUB_LEN_ACK_ALIGN 4 ++ ++#define MPTCP_SUB_LEN_ACK_64 8 ++#define MPTCP_SUB_LEN_ACK_64_ALIGN 8 ++ ++/* This is the "default" option-length we will send out most often. ++ * MPTCP DSS-header ++ * 32-bit data sequence number ++ * 32-bit data ack ++ * ++ * It is necessary to calculate the effective MSS we will be using when ++ * sending data. ++ */ ++#define MPTCP_SUB_LEN_DSM_ALIGN (MPTCP_SUB_LEN_DSS_ALIGN + \ ++ MPTCP_SUB_LEN_SEQ_ALIGN + \ ++ MPTCP_SUB_LEN_ACK_ALIGN) ++ ++#define MPTCP_SUB_ADD_ADDR 3 ++#define MPTCP_SUB_LEN_ADD_ADDR4 8 ++#define MPTCP_SUB_LEN_ADD_ADDR6 20 ++#define MPTCP_SUB_LEN_ADD_ADDR4_ALIGN 8 ++#define MPTCP_SUB_LEN_ADD_ADDR6_ALIGN 20 ++ ++#define MPTCP_SUB_REMOVE_ADDR 4 ++#define MPTCP_SUB_LEN_REMOVE_ADDR 4 ++ ++#define MPTCP_SUB_PRIO 5 ++#define MPTCP_SUB_LEN_PRIO 3 ++#define MPTCP_SUB_LEN_PRIO_ADDR 4 ++#define MPTCP_SUB_LEN_PRIO_ALIGN 4 ++ ++#define MPTCP_SUB_FAIL 6 ++#define MPTCP_SUB_LEN_FAIL 12 ++#define MPTCP_SUB_LEN_FAIL_ALIGN 12 ++ ++#define MPTCP_SUB_FCLOSE 7 ++#define MPTCP_SUB_LEN_FCLOSE 12 ++#define MPTCP_SUB_LEN_FCLOSE_ALIGN 12 ++ ++ ++#define OPTION_MPTCP (1 << 5) ++ ++static inline void reset_mpc(struct tcp_sock *tp) ++{ ++ tp->mpc = 0; ++ ++ tp->__select_window = __tcp_select_window; ++ tp->select_window = tcp_select_window; ++ tp->select_initial_window = tcp_select_initial_window; ++ tp->init_buffer_space = tcp_init_buffer_space; ++ tp->set_rto = tcp_set_rto; ++ tp->should_expand_sndbuf = tcp_should_expand_sndbuf; ++} ++ ++/* Initializes MPTCP flags in tcp_sock (and other tcp_sock members that depend ++ * on those flags). ++ */ ++static inline void mptcp_init_tcp_sock(struct tcp_sock *tp) ++{ ++ reset_mpc(tp); ++} ++ ++#ifdef CONFIG_MPTCP ++ ++/* Used for checking if the mptcp initialization has been successful */ ++extern bool mptcp_init_failed; ++ ++/* MPTCP options */ ++#define OPTION_TYPE_SYN (1 << 0) ++#define OPTION_TYPE_SYNACK (1 << 1) ++#define OPTION_TYPE_ACK (1 << 2) ++#define OPTION_MP_CAPABLE (1 << 3) ++#define OPTION_DATA_ACK (1 << 4) ++#define OPTION_ADD_ADDR (1 << 5) ++#define OPTION_MP_JOIN (1 << 6) ++#define OPTION_MP_FAIL (1 << 7) ++#define OPTION_MP_FCLOSE (1 << 8) ++#define OPTION_REMOVE_ADDR (1 << 9) ++#define OPTION_MP_PRIO (1 << 10) ++ ++/* MPTCP flags */ ++#define MPTCPHDR_ACK 0x01 ++#define MPTCPHDR_SEQ 0x02 ++#define MPTCPHDR_FIN 0x04 ++#define MPTCPHDR_INF 0x08 ++#define MPTCPHDR_SEQ64_SET 0x10 /* Did we received a 64-bit seq number */ ++#define MPTCPHDR_SEQ64_OFO 0x20 /* Is it not in our circular array? */ ++#define MPTCPHDR_SEQ64_INDEX 0x40 /* Index of seq in mpcb->snd_high_order */ ++#define MPTCPHDR_DSS_CSUM 0x80 ++ ++/* It is impossible, that all 8 bits of mptcp_flags are set to 1 with the above ++ * Thus, defining MPTCPHDR_JOIN as 0xFF is safe. ++ */ ++#define MPTCPHDR_JOIN 0xFF ++ ++struct mptcp_option { ++ __u8 kind; ++ __u8 len; ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u8 ver:4, ++ sub:4; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u8 sub:4, ++ ver:4; ++#else ++#error "Adjust your defines" ++#endif ++}; ++ ++struct mp_capable { ++ __u8 kind; ++ __u8 len; ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u8 ver:4, ++ sub:4; ++ __u8 h:1, ++ rsv:5, ++ b:1, ++ a:1; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u8 sub:4, ++ ver:4; ++ __u8 a:1, ++ b:1, ++ rsv:5, ++ h:1; ++#else ++#error "Adjust your defines" ++#endif ++ __u64 sender_key; ++ __u64 receiver_key; ++} __attribute__((__packed__)); ++ ++struct mp_join { ++ __u8 kind; ++ __u8 len; ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u8 b:1, ++ rsv:3, ++ sub:4; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u8 sub:4, ++ rsv:3, ++ b:1; ++#else ++#error "Adjust your defines" ++#endif ++ __u8 addr_id; ++ union { ++ struct { ++ u32 token; ++ u32 nonce; ++ } syn; ++ struct { ++ __u64 mac; ++ u32 nonce; ++ } synack; ++ struct { ++ __u8 mac[20]; ++ } ack; ++ } u; ++} __attribute__((__packed__)); ++ ++struct mp_dss { ++ __u8 kind; ++ __u8 len; ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u16 rsv1:4, ++ sub:4, ++ A:1, ++ a:1, ++ M:1, ++ m:1, ++ F:1, ++ rsv2:3; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u16 sub:4, ++ rsv1:4, ++ rsv2:3, ++ F:1, ++ m:1, ++ M:1, ++ a:1, ++ A:1; ++#else ++#error "Adjust your defines" ++#endif ++}; ++ ++struct mp_add_addr { ++ __u8 kind; ++ __u8 len; ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u8 ipver:4, ++ sub:4; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u8 sub:4, ++ ipver:4; ++#else ++#error "Adjust your defines" ++#endif ++ __u8 addr_id; ++ union { ++ struct { ++ struct in_addr addr; ++ __be16 port; ++ } v4; ++ struct { ++ struct in6_addr addr; ++ __be16 port; ++ } v6; ++ } u; ++} __attribute__((__packed__)); ++ ++struct mp_remove_addr { ++ __u8 kind; ++ __u8 len; ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u8 rsv:4, ++ sub:4; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u8 sub:4, ++ rsv:4; ++#else ++#error "Adjust your defines" ++#endif ++ /* list of addr_id */ ++ __u8 addrs_id; ++}; ++ ++struct mp_fail { ++ __u8 kind; ++ __u8 len; ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u16 rsv1:4, ++ sub:4, ++ rsv2:8; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u16 sub:4, ++ rsv1:4, ++ rsv2:8; ++#else ++#error "Adjust your defines" ++#endif ++ __be64 data_seq; ++} __attribute__((__packed__)); ++ ++struct mp_fclose { ++ __u8 kind; ++ __u8 len; ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u16 rsv1:4, ++ sub:4, ++ rsv2:8; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u16 sub:4, ++ rsv1:4, ++ rsv2:8; ++#else ++#error "Adjust your defines" ++#endif ++ __u64 key; ++} __attribute__((__packed__)); ++ ++struct mp_prio { ++ __u8 kind; ++ __u8 len; ++#if defined(__LITTLE_ENDIAN_BITFIELD) ++ __u8 b:1, ++ rsv:3, ++ sub:4; ++#elif defined(__BIG_ENDIAN_BITFIELD) ++ __u8 sub:4, ++ rsv:3, ++ b:1; ++#else ++#error "Adjust your defines" ++#endif ++ __u8 addr_id; ++} __attribute__((__packed__)); ++ ++static inline int mptcp_sub_len_dss(struct mp_dss *m, int csum) ++{ ++ return 4 + m->A * (4 + m->a * 4) + m->M * (10 + m->m * 4 + csum * 2); ++} ++ ++#define MPTCP_APP 2 ++ ++extern int sysctl_mptcp_enabled; ++extern int sysctl_mptcp_checksum; ++extern int sysctl_mptcp_debug; ++extern int sysctl_mptcp_syn_retries; ++ ++extern struct workqueue_struct *mptcp_wq; ++ ++#define mptcp_debug(fmt, args...) \ ++ do { \ ++ if (unlikely(sysctl_mptcp_debug)) \ ++ pr_err(__FILE__ ": " fmt, ##args); \ ++ } while (0) ++ ++/* Iterates over all subflows */ ++#define mptcp_for_each_tp(mpcb, tp) \ ++ for ((tp) = (mpcb)->connection_list; (tp); (tp) = (tp)->mptcp->next) ++ ++#define mptcp_for_each_sk(mpcb, sk) \ ++ for ((sk) = (struct sock *)(mpcb)->connection_list; \ ++ sk; \ ++ sk = (struct sock *)tcp_sk(sk)->mptcp->next) ++ ++#define mptcp_for_each_sk_safe(__mpcb, __sk, __temp) \ ++ for (__sk = (struct sock *)(__mpcb)->connection_list, \ ++ __temp = __sk ? (struct sock *)tcp_sk(__sk)->mptcp->next : NULL; \ ++ __sk; \ ++ __sk = __temp, \ ++ __temp = __sk ? (struct sock *)tcp_sk(__sk)->mptcp->next : NULL) ++ ++/* Iterates over all bit set to 1 in a bitset */ ++#define mptcp_for_each_bit_set(b, i) \ ++ for (i = ffs(b) - 1; i >= 0; i = ffs(b >> (i + 1) << (i + 1)) - 1) ++ ++#define mptcp_for_each_bit_unset(b, i) \ ++ mptcp_for_each_bit_set(~b, i) ++ ++extern struct lock_class_key meta_key; ++extern struct lock_class_key meta_slock_key; ++extern u32 mptcp_secret[MD5_MESSAGE_BYTES / 4]; ++ ++/* This is needed to ensure that two subsequent key-generation result in ++ * different keys if the IPs and ports are the same. ++ */ ++extern u32 mptcp_key_seed; ++ ++#define MPTCP_HASH_SIZE 1024 ++ ++extern struct hlist_nulls_head tk_hashtable[MPTCP_HASH_SIZE]; ++ ++/* This second hashtable is needed to retrieve request socks ++ * created as a result of a join request. While the SYN contains ++ * the token, the final ack does not, so we need a separate hashtable ++ * to retrieve the mpcb. ++ */ ++extern struct list_head mptcp_reqsk_htb[MPTCP_HASH_SIZE]; ++extern spinlock_t mptcp_reqsk_hlock; /* hashtable protection */ ++ ++/* Lock, protecting the two hash-tables that hold the token. Namely, ++ * mptcp_reqsk_tk_htb and tk_hashtable ++ */ ++extern spinlock_t mptcp_tk_hashlock; /* hashtable protection */ ++ ++void mptcp_data_ready(struct sock *sk, int bytes); ++void mptcp_write_space(struct sock *sk); ++ ++void mptcp_add_meta_ofo_queue(struct sock *meta_sk, struct sk_buff *skb, ++ struct sock *sk); ++void mptcp_ofo_queue(struct sock *meta_sk); ++void mptcp_purge_ofo_queue(struct tcp_sock *meta_tp); ++void mptcp_cleanup_rbuf(struct sock *meta_sk, int copied); ++int mptcp_alloc_mpcb(struct sock *master_sk, __u64 remote_key, u32 window); ++int mptcp_add_sock(struct sock *meta_sk, struct sock *sk, u8 loc_id, u8 rem_id, ++ gfp_t flags); ++void mptcp_del_sock(struct sock *sk); ++void mptcp_update_metasocket(struct sock *sock, struct sock *meta_sk); ++void mptcp_reinject_data(struct sock *orig_sk, int clone_it); ++void mptcp_update_sndbuf(struct mptcp_cb *mpcb); ++struct sk_buff *mptcp_next_segment(struct sock *sk, int *reinject); ++void mptcp_send_fin(struct sock *meta_sk); ++void mptcp_send_active_reset(struct sock *meta_sk, gfp_t priority); ++int mptcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, ++ int push_one, gfp_t gfp); ++void mptcp_parse_options(const uint8_t *ptr, int opsize, ++ struct tcp_options_received *opt_rx, ++ struct mptcp_options_received *mopt, ++ const struct sk_buff *skb); ++void mptcp_syn_options(struct sock *sk, struct tcp_out_options *opts, ++ unsigned *remaining); ++void mptcp_synack_options(struct request_sock *req, ++ struct tcp_out_options *opts, ++ unsigned *remaining); ++void mptcp_established_options(struct sock *sk, struct sk_buff *skb, ++ struct tcp_out_options *opts, unsigned *size); ++void mptcp_options_write(__be32 *ptr, struct tcp_sock *tp, ++ struct tcp_out_options *opts, ++ struct sk_buff *skb); ++void mptcp_close(struct sock *meta_sk, long timeout); ++int mptcp_doit(struct sock *sk); ++int mptcp_create_master_sk(struct sock *meta_sk, __u64 remote_key, u32 window); ++int mptcp_check_req_master(struct sock *sk, struct sock *child, ++ struct request_sock *req, ++ struct request_sock **prev, ++ struct mptcp_options_received *mopt); ++struct sock *mptcp_check_req_child(struct sock *sk, struct sock *child, ++ struct request_sock *req, ++ struct request_sock **prev, ++ struct mptcp_options_received *mopt); ++u32 __mptcp_select_window(struct sock *sk); ++void mptcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, ++ __u32 *window_clamp, int wscale_ok, ++ __u8 *rcv_wscale, __u32 init_rcv_wnd, ++ const struct sock *sk); ++unsigned int mptcp_current_mss(struct sock *meta_sk); ++int mptcp_select_size(const struct sock *meta_sk, bool sg); ++void mptcp_key_sha1(u64 key, u32 *token, u64 *idsn); ++void mptcp_hmac_sha1(u8 *key_1, u8 *key_2, u8 *rand_1, u8 *rand_2, ++ u32 *hash_out); ++void mptcp_clean_rtx_infinite(struct sk_buff *skb, struct sock *sk); ++void mptcp_fin(struct sock *meta_sk); ++void mptcp_retransmit_timer(struct sock *meta_sk); ++int mptcp_write_wakeup(struct sock *meta_sk); ++void mptcp_sub_close_wq(struct work_struct *work); ++void mptcp_sub_close(struct sock *sk, unsigned long delay); ++struct sock *mptcp_select_ack_sock(const struct sock *meta_sk, int copied); ++void mptcp_fallback_meta_sk(struct sock *meta_sk); ++int mptcp_backlog_rcv(struct sock *meta_sk, struct sk_buff *skb); ++struct sock *mptcp_sk_clone(const struct sock *sk, int family, const gfp_t priority); ++void mptcp_ack_handler(unsigned long); ++int mptcp_check_rtt(const struct tcp_sock *tp, int time); ++int mptcp_check_snd_buf(const struct tcp_sock *tp); ++int mptcp_handle_options(struct sock *sk, const struct tcphdr *th, struct sk_buff *skb); ++void __init mptcp_init(void); ++int mptcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len); ++int mptcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, ++ unsigned int mss_now, int reinject); ++int mptso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, ++ unsigned int mss_now, gfp_t gfp, int reinject); ++void mptcp_destroy_sock(struct sock *sk); ++int mptcp_rcv_synsent_state_process(struct sock *sk, struct sock **skptr, ++ struct sk_buff *skb, ++ struct mptcp_options_received *mopt); ++unsigned int mptcp_xmit_size_goal(struct sock *meta_sk, u32 mss_now, ++ int large_allowed); ++int mptcp_time_wait(struct sock *sk, struct tcp_timewait_sock *tw); ++void mptcp_twsk_destructor(struct tcp_timewait_sock *tw); ++void mptcp_update_tw_socks(const struct tcp_sock *tp, int state); ++void mptcp_disconnect(struct sock *sk); ++bool mptcp_should_expand_sndbuf(const struct sock *sk); ++int mptcp_retransmit_skb(struct sock *meta_sk, struct sk_buff *skb); ++void mptcp_tsq_flags(struct sock *sk); ++void mptcp_tsq_sub_deferred(struct sock *meta_sk); ++struct mp_join *mptcp_find_join(struct sk_buff *skb); ++void mptcp_hash_remove_bh(struct tcp_sock *meta_tp); ++void mptcp_hash_remove(struct tcp_sock *meta_tp); ++struct sock *mptcp_hash_find(struct net *net, u32 token); ++int mptcp_lookup_join(struct sk_buff *skb, struct inet_timewait_sock *tw); ++int mptcp_do_join_short(struct sk_buff *skb, struct mptcp_options_received *mopt, ++ struct tcp_options_received *tmp_opt, struct net *net); ++void mptcp_reqsk_destructor(struct request_sock *req); ++void mptcp_reqsk_new_mptcp(struct request_sock *req, ++ const struct tcp_options_received *rx_opt, ++ const struct mptcp_options_received *mopt, ++ const struct sk_buff *skb); ++int mptcp_check_req(struct sk_buff *skb, struct net *net); ++void mptcp_connect_init(struct sock *sk); ++void mptcp_sub_force_close(struct sock *sk); ++int mptcp_sub_len_remove_addr_align(u16 bitfield); ++void mptcp_remove_shortcuts(const struct mptcp_cb *mpcb, ++ const struct sk_buff *skb); ++void mptcp_init_buffer_space(struct sock *sk); ++ ++/* MPTCP-path-manager registration/initialization functions */ ++int mptcp_register_path_manager(struct mptcp_pm_ops *pm); ++void mptcp_unregister_path_manager(struct mptcp_pm_ops *pm); ++void mptcp_init_path_manager(struct mptcp_cb *mpcb); ++void mptcp_cleanup_path_manager(struct mptcp_cb *mpcb); ++void mptcp_fallback_default(struct mptcp_cb *mpcb); ++void mptcp_get_default_path_manager(char *name); ++int mptcp_set_default_path_manager(const char *name); ++extern struct mptcp_pm_ops mptcp_pm_default; ++ ++static inline ++struct mptcp_request_sock *mptcp_rsk(const struct request_sock *req) ++{ ++ return (struct mptcp_request_sock *)req; ++} ++ ++static inline ++struct request_sock *rev_mptcp_rsk(const struct mptcp_request_sock *req) ++{ ++ return (struct request_sock *)req; ++} ++ ++static inline bool mptcp_can_sendpage(struct sock *sk) ++{ ++ struct sock *sk_it; ++ ++ if (tcp_sk(sk)->mpcb->dss_csum) ++ return false; ++ ++ mptcp_for_each_sk(tcp_sk(sk)->mpcb, sk_it) { ++ if (!(sk_it->sk_route_caps & NETIF_F_SG) || ++ !(sk_it->sk_route_caps & NETIF_F_ALL_CSUM)) ++ return false; ++ } ++ ++ return true; ++} ++ ++static inline void mptcp_push_pending_frames(struct sock *meta_sk) ++{ ++ if (mptcp_next_segment(meta_sk, NULL)) { ++ struct tcp_sock *tp = tcp_sk(meta_sk); ++ ++ /* We don't care about the MSS, because it will be set in ++ * mptcp_write_xmit. ++ */ ++ __tcp_push_pending_frames(meta_sk, 0, tp->nonagle); ++ } ++} ++ ++static inline void mptcp_send_reset(struct sock *sk) ++{ ++ tcp_send_active_reset(sk, GFP_ATOMIC); ++ mptcp_sub_force_close(sk); ++} ++ ++static inline int mptcp_is_data_seq(const struct sk_buff *skb) ++{ ++ return TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_SEQ; ++} ++ ++static inline int mptcp_is_data_fin(const struct sk_buff *skb) ++{ ++ return mptcp_is_data_seq(skb) && ++ (TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_FIN); ++} ++ ++/* Is it a data-fin while in infinite mapping mode? ++ * In infinite mode, a subflow-fin is in fact a data-fin. ++ */ ++static inline int mptcp_is_data_fin2(const struct sk_buff *skb, ++ const struct tcp_sock *tp) ++{ ++ return mptcp_is_data_fin(skb) || ++ (tp->mpcb->infinite_mapping_rcv && tcp_hdr(skb)->fin); ++} ++ ++static inline void mptcp_skb_entail_init(const struct tcp_sock *tp, ++ struct sk_buff *skb) ++{ ++ TCP_SKB_CB(skb)->mptcp_flags = MPTCPHDR_SEQ; ++} ++ ++static inline u8 mptcp_get_64_bit(u64 data_seq, struct mptcp_cb *mpcb) ++{ ++ u64 data_seq_high = (u32)(data_seq >> 32); ++ ++ if (mpcb->rcv_high_order[0] == data_seq_high) ++ return 0; ++ else if (mpcb->rcv_high_order[1] == data_seq_high) ++ return MPTCPHDR_SEQ64_INDEX; ++ else ++ return MPTCPHDR_SEQ64_OFO; ++} ++ ++/* Sets the data_seq and returns pointer to the in-skb field of the data_seq. ++ * If the packet has a 64-bit dseq, the pointer points to the last 32 bits. ++ */ ++static inline __u32 *mptcp_skb_set_data_seq(const struct sk_buff *skb, ++ u32 *data_seq, ++ struct mptcp_cb *mpcb) ++{ ++ __u32 *ptr = (__u32 *)(skb_transport_header(skb) + TCP_SKB_CB(skb)->dss_off); ++ ++ if (TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_SEQ64_SET) { ++ u64 data_seq64 = get_unaligned_be64(ptr); ++ ++ if (mpcb) ++ TCP_SKB_CB(skb)->mptcp_flags |= mptcp_get_64_bit(data_seq64, mpcb); ++ ++ *data_seq = (u32)data_seq64 ; ++ ptr++; ++ } else { ++ *data_seq = get_unaligned_be32(ptr); ++ } ++ ++ return ptr; ++} ++ ++static inline struct sock *mptcp_meta_sk(const struct sock *sk) ++{ ++ return tcp_sk(sk)->meta_sk; ++} ++ ++static inline struct tcp_sock *mptcp_meta_tp(const struct tcp_sock *tp) ++{ ++ return tcp_sk(tp->meta_sk); ++} ++ ++static inline int is_meta_tp(const struct tcp_sock *tp) ++{ ++ return tp->mpcb && mptcp_meta_tp(tp) == tp; ++} ++ ++static inline int is_meta_sk(const struct sock *sk) ++{ ++ return sk->sk_type == SOCK_STREAM && sk->sk_protocol == IPPROTO_TCP && ++ tcp_sk(sk)->mpc && mptcp_meta_sk(sk) == sk; ++} ++ ++static inline int is_master_tp(const struct tcp_sock *tp) ++{ ++ return !tp->mpc || (!tp->mptcp->slave_sk && !is_meta_tp(tp)); ++} ++ ++static inline void mptcp_hash_request_remove(struct request_sock *req) ++{ ++ int in_softirq = 0; ++ ++ if (list_empty(&mptcp_rsk(req)->collide_tuple)) ++ return; ++ ++ if (in_softirq()) { ++ spin_lock(&mptcp_reqsk_hlock); ++ in_softirq = 1; ++ } else { ++ spin_lock_bh(&mptcp_reqsk_hlock); ++ } ++ ++ list_del(&mptcp_rsk(req)->collide_tuple); ++ ++ if (in_softirq) ++ spin_unlock(&mptcp_reqsk_hlock); ++ else ++ spin_unlock_bh(&mptcp_reqsk_hlock); ++} ++ ++static inline void mptcp_init_mp_opt(struct mptcp_options_received *mopt) ++{ ++ mopt->saw_mpc = 0; ++ mopt->dss_csum = 0; ++ mopt->drop_me = 0; ++ ++ mopt->is_mp_join = 0; ++ mopt->join_ack = 0; ++ ++ mopt->saw_low_prio = 0; ++ mopt->low_prio = 0; ++ ++ mopt->saw_add_addr = 0; ++ mopt->more_add_addr = 0; ++ ++ mopt->saw_rem_addr = 0; ++ mopt->more_rem_addr = 0; ++ ++ mopt->mp_fail = 0; ++ mopt->mp_fclose = 0; ++} ++ ++static inline void mptcp_reset_mopt(struct tcp_sock *tp) ++{ ++ struct mptcp_options_received *mopt = &tp->mptcp->rx_opt; ++ ++ mopt->saw_low_prio = 0; ++ mopt->saw_add_addr = 0; ++ mopt->more_add_addr = 0; ++ mopt->saw_rem_addr = 0; ++ mopt->more_rem_addr = 0; ++ mopt->join_ack = 0; ++ mopt->mp_fail = 0; ++ mopt->mp_fclose = 0; ++} ++ ++static inline __be32 mptcp_get_highorder_sndbits(const struct sk_buff *skb, ++ const struct mptcp_cb *mpcb) ++{ ++ return htonl(mpcb->snd_high_order[(TCP_SKB_CB(skb)->mptcp_flags & ++ MPTCPHDR_SEQ64_INDEX) ? 1 : 0]); ++} ++ ++static inline u64 mptcp_get_data_seq_64(const struct mptcp_cb *mpcb, int index, ++ u32 data_seq_32) ++{ ++ return ((u64)mpcb->rcv_high_order[index] << 32) | data_seq_32; ++} ++ ++static inline u64 mptcp_get_rcv_nxt_64(const struct tcp_sock *meta_tp) ++{ ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ return mptcp_get_data_seq_64(mpcb, mpcb->rcv_hiseq_index, ++ meta_tp->rcv_nxt); ++} ++ ++static inline void mptcp_check_sndseq_wrap(struct tcp_sock *meta_tp, int inc) ++{ ++ if (unlikely(meta_tp->snd_nxt > meta_tp->snd_nxt + inc)) { ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ mpcb->snd_hiseq_index = mpcb->snd_hiseq_index ? 0 : 1; ++ mpcb->snd_high_order[mpcb->snd_hiseq_index] += 2; ++ } ++} ++ ++static inline void mptcp_check_rcvseq_wrap(struct tcp_sock *meta_tp, ++ u32 old_rcv_nxt) ++{ ++ if (unlikely(old_rcv_nxt > meta_tp->rcv_nxt)) { ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ mpcb->rcv_high_order[mpcb->rcv_hiseq_index] += 2; ++ mpcb->rcv_hiseq_index = mpcb->rcv_hiseq_index ? 0 : 1; ++ } ++} ++ ++static inline int mptcp_sk_can_send(const struct sock *sk) ++{ ++ return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT) && ++ !tcp_sk(sk)->mptcp->pre_established; ++} ++ ++static inline int mptcp_sk_can_recv(const struct sock *sk) ++{ ++ return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCP_FIN_WAIT1 | TCP_FIN_WAIT2); ++} ++ ++static inline int mptcp_sk_can_send_ack(const struct sock *sk) ++{ ++ return !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV | ++ TCPF_CLOSE | TCPF_LISTEN)) && ++ !tcp_sk(sk)->mptcp->pre_established; ++} ++ ++/* Only support GSO if all subflows supports it */ ++static inline bool mptcp_sk_can_gso(const struct sock *meta_sk) ++{ ++ struct sock *sk; ++ ++ if (tcp_sk(meta_sk)->mpcb->dss_csum) ++ return 0; ++ ++ mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { ++ if (!mptcp_sk_can_send(sk)) ++ continue; ++ if (!sk_can_gso(sk)) ++ return false; ++ } ++ return true; ++} ++ ++static inline bool mptcp_can_sg(const struct sock *meta_sk) ++{ ++ struct sock *sk; ++ ++ if (tcp_sk(meta_sk)->mpcb->dss_csum) ++ return 0; ++ ++ mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { ++ if (!mptcp_sk_can_send(sk)) ++ continue; ++ if (!(sk->sk_route_caps & NETIF_F_SG)) ++ return false; ++ } ++ return true; ++} ++ ++static inline void mptcp_set_rto(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct sock *sk_it; ++ struct inet_connection_sock *micsk = inet_csk(mptcp_meta_sk(sk)); ++ __u32 max_rto = 0; ++ ++ /* We are in recovery-phase on the MPTCP-level. Do not update the ++ * RTO, because this would kill exponential backoff. ++ */ ++ if (micsk->icsk_retransmits) ++ return; ++ ++ mptcp_for_each_sk(tp->mpcb, sk_it) { ++ if (mptcp_sk_can_send(sk_it) && ++ inet_csk(sk_it)->icsk_rto > max_rto) ++ max_rto = inet_csk(sk_it)->icsk_rto; ++ } ++ if (max_rto) { ++ micsk->icsk_rto = max_rto << 1; ++ ++ /* A successfull rto-measurement - reset backoff counter */ ++ micsk->icsk_backoff = 0; ++ } ++} ++ ++static inline int mptcp_sysctl_syn_retries(void) ++{ ++ return sysctl_mptcp_syn_retries; ++} ++ ++static inline void mptcp_sub_close_passive(struct sock *sk) ++{ ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = tcp_sk(meta_sk); ++ ++ /* Only close, if the app did a send-shutdown (passive close), and we ++ * received the data-ack of the data-fin. ++ */ ++ if (tp->mpcb->passive_close && meta_tp->snd_una == meta_tp->write_seq) ++ mptcp_sub_close(sk, 0); ++} ++ ++static inline bool mptcp_fallback_infinite(struct sock *sk, int flag) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ /* If data has been acknowleged on the meta-level, fully_established ++ * will have been set before and thus we will not fall back to infinite ++ * mapping. ++ */ ++ if (likely(tp->mptcp->fully_established)) ++ return false; ++ ++ if (!(flag & MPTCP_FLAG_DATA_ACKED)) ++ return false; ++ ++ /* Don't fallback twice ;) */ ++ if (tp->mpcb->infinite_mapping_snd) ++ return false; ++ ++ pr_err("%s %#x will fallback - pi %d, src %pI4 dst %pI4 from %pS\n", ++ __func__, tp->mpcb->mptcp_loc_token, tp->mptcp->path_index, ++ &inet_sk(sk)->inet_saddr, &inet_sk(sk)->inet_daddr, ++ __builtin_return_address(0)); ++ if (!is_master_tp(tp)) ++ return true; ++ ++ tp->mpcb->infinite_mapping_snd = 1; ++ tp->mpcb->infinite_mapping_rcv = 1; ++ tp->mptcp->fully_established = 1; ++ ++ return false; ++} ++ ++/* Find the first free index in the bitfield */ ++static inline int __mptcp_find_free_index(u8 bitfield, int j, u8 base) ++{ ++ int i; ++ mptcp_for_each_bit_unset(bitfield >> base, i) { ++ /* We wrapped at the bitfield - try from 0 on */ ++ if (i + base >= sizeof(bitfield) * 8) { ++ mptcp_for_each_bit_unset(bitfield, i) { ++ if (i >= sizeof(bitfield) * 8) ++ goto exit; ++ ++ if (i != j) ++ return i; ++ } ++ goto exit; ++ } ++ if (i + base >= sizeof(bitfield) * 8) ++ break; ++ ++ if (i + base != j) ++ return i + base; ++ } ++exit: ++ return -1; ++} ++ ++static inline int mptcp_find_free_index(u8 bitfield) ++{ ++ return __mptcp_find_free_index(bitfield, -1, 0); ++} ++ ++/* Find the first index whose bit in the bit-field == 0 */ ++static inline u8 mptcp_set_new_pathindex(struct mptcp_cb *mpcb) ++{ ++ u8 base = mpcb->next_path_index; ++ int i; ++ ++ /* Start at 1, because 0 is reserved for the meta-sk */ ++ mptcp_for_each_bit_unset(mpcb->path_index_bits >> base, i) { ++ if (i + base < 1) ++ continue; ++ if (i + base >= sizeof(mpcb->path_index_bits) * 8) ++ break; ++ i += base; ++ mpcb->path_index_bits |= (1 << i); ++ mpcb->next_path_index = i + 1; ++ return i; ++ } ++ mptcp_for_each_bit_unset(mpcb->path_index_bits, i) { ++ if (i >= sizeof(mpcb->path_index_bits) * 8) ++ break; ++ if (i < 1) ++ continue; ++ mpcb->path_index_bits |= (1 << i); ++ mpcb->next_path_index = i + 1; ++ return i; ++ } ++ ++ return 0; ++} ++ ++static inline int mptcp_v6_is_v4_mapped(struct sock *sk) ++{ ++ return sk->sk_family == AF_INET6 && ++ ipv6_addr_type(&inet6_sk(sk)->saddr) == IPV6_ADDR_MAPPED; ++} ++ ++/* TCP and MPTCP mpc flag-depending functions */ ++u16 mptcp_select_window(struct sock *sk); ++void mptcp_init_buffer_space(struct sock *sk); ++void mptcp_tcp_set_rto(struct sock *sk); ++ ++static inline void set_mpc(struct tcp_sock *tp) ++{ ++ tp->mpc = 1; ++ ++ tp->__select_window = __mptcp_select_window; ++ tp->select_window = mptcp_select_window; ++ tp->select_initial_window = mptcp_select_initial_window; ++ tp->init_buffer_space = mptcp_init_buffer_space; ++ tp->set_rto = mptcp_tcp_set_rto; ++ tp->should_expand_sndbuf = mptcp_should_expand_sndbuf; ++} ++ ++#else /* CONFIG_MPTCP */ ++#define mptcp_debug(fmt, args...) \ ++ do { \ ++ } while (0) ++ ++/* Without MPTCP, we just do one iteration ++ * over the only socket available. This assumes that ++ * the sk/tp arg is the socket in that case. ++ */ ++#define mptcp_for_each_sk(mpcb, sk) ++#define mptcp_for_each_sk_safe(__mpcb, __sk, __temp) ++ ++static inline int mptcp_is_data_fin(const struct sk_buff *skb) ++{ ++ return 0; ++} ++static inline int mptcp_is_data_seq(const struct sk_buff *skb) ++{ ++ return 0; ++} ++static inline struct sock *mptcp_meta_sk(const struct sock *sk) ++{ ++ return NULL; ++} ++static inline struct tcp_sock *mptcp_meta_tp(const struct tcp_sock *tp) ++{ ++ return NULL; ++} ++static inline int is_meta_sk(const struct sock *sk) ++{ ++ return 0; ++} ++static inline int is_master_tp(const struct tcp_sock *tp) ++{ ++ return 0; ++} ++static inline void mptcp_purge_ofo_queue(struct tcp_sock *meta_tp) {} ++static inline void mptcp_cleanup_rbuf(const struct sock *meta_sk, int copied) {} ++static inline void mptcp_del_sock(const struct sock *sk) {} ++static inline void mptcp_reinject_data(struct sock *orig_sk, int clone_it) {} ++static inline void mptcp_update_sndbuf(const struct mptcp_cb *mpcb) {} ++static inline void mptcp_skb_entail_init(const struct tcp_sock *tp, ++ const struct sk_buff *skb) {} ++static inline void mptcp_clean_rtx_infinite(const struct sk_buff *skb, ++ const struct sock *sk) {} ++static inline void mptcp_retransmit_timer(const struct sock *meta_sk) {} ++static inline int mptcp_write_wakeup(struct sock *meta_sk) ++{ ++ return 0; ++} ++static inline void mptcp_sub_close(struct sock *sk, unsigned long delay) {} ++static inline void mptcp_set_rto(const struct sock *sk) {} ++static inline void mptcp_send_fin(const struct sock *meta_sk) {} ++static inline void mptcp_parse_options(const uint8_t *ptr, const int opsize, ++ const struct tcp_options_received *opt_rx, ++ const struct mptcp_options_received *mopt, ++ const struct sk_buff *skb) {} ++static inline void mptcp_syn_options(struct sock *sk, ++ struct tcp_out_options *opts, ++ unsigned *remaining) {} ++static inline void mptcp_synack_options(struct request_sock *req, ++ struct tcp_out_options *opts, ++ unsigned *remaining) {} ++ ++static inline void mptcp_established_options(struct sock *sk, ++ struct sk_buff *skb, ++ struct tcp_out_options *opts, ++ unsigned *size) {} ++static inline void mptcp_options_write(__be32 *ptr, struct tcp_sock *tp, ++ struct tcp_out_options *opts, ++ struct sk_buff *skb) {} ++static inline void mptcp_close(struct sock *meta_sk, long timeout) {} ++static inline int mptcp_doit(struct sock *sk) ++{ ++ return 0; ++} ++static inline int mptcp_check_req_master(const struct sock *sk, ++ const struct sock *child, ++ struct request_sock *req, ++ struct request_sock **prev, ++ const struct mptcp_options_received *mopt) ++{ ++ return 1; ++} ++static inline struct sock *mptcp_check_req_child(struct sock *sk, ++ struct sock *child, ++ struct request_sock *req, ++ struct request_sock **prev, ++ struct mptcp_options_received *mopt) ++{ ++ return NULL; ++} ++static inline unsigned int mptcp_current_mss(struct sock *meta_sk) ++{ ++ return 0; ++} ++static inline int mptcp_select_size(const struct sock *meta_sk, bool sg) ++{ ++ return 0; ++} ++static inline void mptcp_sub_close_passive(struct sock *sk) {} ++static inline bool mptcp_fallback_infinite(const struct sock *sk, int flag) ++{ ++ return false; ++} ++static inline void mptcp_init_mp_opt(const struct mptcp_options_received *mopt) {} ++static inline int mptcp_check_rtt(const struct tcp_sock *tp, int time) ++{ ++ return 0; ++} ++static inline int mptcp_check_snd_buf(const struct tcp_sock *tp) ++{ ++ return 0; ++} ++static inline int mptcp_sysctl_syn_retries(void) ++{ ++ return 0; ++} ++static inline void mptcp_send_reset(const struct sock *sk) {} ++static inline void mptcp_send_active_reset(struct sock *meta_sk, ++ gfp_t priority) {} ++static inline int mptcp_write_xmit(struct sock *sk, unsigned int mss_now, ++ int nonagle, int push_one, gfp_t gfp) ++{ ++ return 0; ++} ++static inline struct sock *mptcp_sk_clone(const struct sock *sk, int family, ++ const gfp_t priority) ++{ ++ return NULL; ++} ++static inline int mptcp_handle_options(struct sock *sk, ++ const struct tcphdr *th, ++ struct sk_buff *skb) ++{ ++ return 0; ++} ++static inline void mptcp_reset_mopt(struct tcp_sock *tp) {} ++static inline void __init mptcp_init(void) {} ++static inline int mptcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) ++{ ++ return 0; ++} ++static inline int mptcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, ++ unsigned int mss_now, int reinject) ++{ ++ return 0; ++} ++static inline int mptso_fragment(struct sock *sk, struct sk_buff *skb, ++ unsigned int len, unsigned int mss_now, ++ gfp_t gfp, int reinject) ++{ ++ return 0; ++} ++static inline bool mptcp_sk_can_gso(const struct sock *sk) ++{ ++ return false; ++} ++static inline bool mptcp_can_sg(const struct sock *meta_sk) ++{ ++ return false; ++} ++static inline unsigned int mptcp_xmit_size_goal(struct sock *meta_sk, ++ u32 mss_now, int large_allowed) ++{ ++ return 0; ++} ++static inline void mptcp_destroy_sock(struct sock *sk) {} ++static inline int mptcp_rcv_synsent_state_process(struct sock *sk, ++ struct sock **skptr, ++ struct sk_buff *skb, ++ struct mptcp_options_received *mopt) ++{ ++ return 0; ++} ++static inline bool mptcp_can_sendpage(struct sock *sk) ++{ ++ return false; ++} ++static inline int mptcp_time_wait(struct sock *sk, struct tcp_timewait_sock *tw) ++{ ++ return 0; ++} ++static inline void mptcp_twsk_destructor(struct tcp_timewait_sock *tw) {} ++static inline void mptcp_update_tw_socks(const struct tcp_sock *tp, int state) {} ++static inline void mptcp_disconnect(struct sock *sk) {} ++static inline void mptcp_tsq_flags(struct sock *sk) {} ++static inline void mptcp_tsq_sub_deferred(struct sock *meta_sk) {} ++static inline void mptcp_hash_remove_bh(struct tcp_sock *meta_tp) {} ++static inline void mptcp_hash_remove(struct tcp_sock *meta_tp) {} ++static inline void mptcp_reqsk_new_mptcp(struct request_sock *req, ++ const struct tcp_options_received *rx_opt, ++ const struct mptcp_options_received *mopt, ++ const struct sk_buff *skb) {} ++static inline void mptcp_remove_shortcuts(const struct mptcp_cb *mpcb, ++ const struct sk_buff *skb) {} ++#endif /* CONFIG_MPTCP */ ++ ++#endif /* _MPTCP_H */ +diff -Nur linux-3.14.45.orig/include/net/mptcp_v4.h linux-3.14.45/include/net/mptcp_v4.h +--- linux-3.14.45.orig/include/net/mptcp_v4.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/include/net/mptcp_v4.h 2015-06-24 14:15:48.871862463 +0200 +@@ -0,0 +1,69 @@ ++/* ++ * MPTCP implementation ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer & Author: ++ * Christoph Paasch ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef MPTCP_V4_H_ ++#define MPTCP_V4_H_ ++ ++ ++#include ++#include ++#include ++#include ++#include ++ ++extern struct request_sock_ops mptcp_request_sock_ops; ++ ++#ifdef CONFIG_MPTCP ++ ++int mptcp_v4_do_rcv(struct sock *meta_sk, struct sk_buff *skb); ++int mptcp_v4_rem_raddress(struct mptcp_cb *mpcb, u8 id); ++int mptcp_v4_add_raddress(struct mptcp_cb *mpcb, const struct in_addr *addr, ++ __be16 port, u8 id); ++void mptcp_v4_set_init_addr_bit(struct mptcp_cb *mpcb, __be32 daddr, int index); ++struct sock *mptcp_v4_search_req(const __be16 rport, const __be32 raddr, ++ const __be32 laddr, const struct net *net); ++int mptcp_init4_subsockets(struct sock *meta_sk, const struct mptcp_loc4 *loc, ++ struct mptcp_rem4 *rem); ++int mptcp_pm_v4_init(void); ++void mptcp_pm_v4_undo(void); ++u32 mptcp_v4_get_nonce(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport, ++ u32 seq); ++u64 mptcp_v4_get_key(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport); ++ ++#else ++ ++static inline int mptcp_v4_do_rcv(const struct sock *meta_sk, ++ const struct sk_buff *skb) ++{ ++ return 0; ++} ++ ++#endif /* CONFIG_MPTCP */ ++ ++#endif /* MPTCP_V4_H_ */ +diff -Nur linux-3.14.45.orig/include/net/mptcp_v6.h linux-3.14.45/include/net/mptcp_v6.h +--- linux-3.14.45.orig/include/net/mptcp_v6.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/include/net/mptcp_v6.h 2015-06-24 14:15:48.871862463 +0200 +@@ -0,0 +1,72 @@ ++/* ++ * MPTCP implementation ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer & Author: ++ * Jaakko Korkeaniemi ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef _MPTCP_V6_H ++#define _MPTCP_V6_H ++ ++#include ++#include ++ ++#include ++ ++extern struct request_sock_ops mptcp6_request_sock_ops; ++extern struct proto mptcpv6_prot; ++ ++#ifdef CONFIG_MPTCP ++ ++int mptcp_v6_do_rcv(struct sock *meta_sk, struct sk_buff *skb); ++int mptcp_v6_rem_raddress(struct mptcp_cb *mpcb, u8 id); ++int mptcp_v6_add_raddress(struct mptcp_cb *mpcb, const struct in6_addr *addr, ++ __be16 port, u8 id); ++void mptcp_v6_set_init_addr_bit(struct mptcp_cb *mpcb, ++ const struct in6_addr *daddr, int index); ++struct sock *mptcp_v6_search_req(const __be16 rport, const struct in6_addr *raddr, ++ const struct in6_addr *laddr, const struct net *net); ++int mptcp_init6_subsockets(struct sock *meta_sk, const struct mptcp_loc6 *loc, ++ struct mptcp_rem6 *rem); ++int mptcp_pm_v6_init(void); ++void mptcp_pm_v6_undo(void); ++struct sock *mptcp_v6v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ++ struct request_sock *req, ++ struct dst_entry *dst); ++__u32 mptcp_v6_get_nonce(const __be32 *saddr, const __be32 *daddr, ++ __be16 sport, __be16 dport, u32 seq); ++u64 mptcp_v6_get_key(const __be32 *saddr, const __be32 *daddr, ++ __be16 sport, __be16 dport); ++ ++#else /* CONFIG_MPTCP */ ++ ++static inline int mptcp_v6_do_rcv(struct sock *meta_sk, struct sk_buff *skb) ++{ ++ return 0; ++} ++ ++#endif /* CONFIG_MPTCP */ ++ ++#endif /* _MPTCP_V6_H */ +diff -Nur linux-3.14.45.orig/include/net/net_namespace.h linux-3.14.45/include/net/net_namespace.h +--- linux-3.14.45.orig/include/net/net_namespace.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/net/net_namespace.h 2015-06-24 14:15:48.871862463 +0200 +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -90,6 +91,9 @@ + #if IS_ENABLED(CONFIG_IPV6) + struct netns_ipv6 ipv6; + #endif ++#if IS_ENABLED(CONFIG_MPTCP) ++ struct netns_mptcp mptcp; ++#endif + #if defined(CONFIG_IP_SCTP) || defined(CONFIG_IP_SCTP_MODULE) + struct netns_sctp sctp; + #endif +diff -Nur linux-3.14.45.orig/include/net/netns/mptcp.h linux-3.14.45/include/net/netns/mptcp.h +--- linux-3.14.45.orig/include/net/netns/mptcp.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/include/net/netns/mptcp.h 2015-06-24 14:15:48.871862463 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * MPTCP implementation - MPTCP namespace ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer: ++ * Christoph Paasch ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef __NETNS_MPTCP_H__ ++#define __NETNS_MPTCP_H__ ++ ++#include ++ ++enum { ++ MPTCP_PM_FULLMESH = 0, ++ MPTCP_PM_MAX ++}; ++ ++struct netns_mptcp { ++ void *path_managers[MPTCP_PM_MAX]; ++}; ++ ++#endif /* __NETNS_MPTCP_H__ */ +diff -Nur linux-3.14.45.orig/include/net/request_sock.h linux-3.14.45/include/net/request_sock.h +--- linux-3.14.45.orig/include/net/request_sock.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/net/request_sock.h 2015-06-24 14:15:48.871862463 +0200 +@@ -164,7 +164,7 @@ + }; + + int reqsk_queue_alloc(struct request_sock_queue *queue, +- unsigned int nr_table_entries); ++ unsigned int nr_table_entries, gfp_t flags); + + void __reqsk_queue_destroy(struct request_sock_queue *queue); + void reqsk_queue_destroy(struct request_sock_queue *queue); +diff -Nur linux-3.14.45.orig/include/net/sock.h linux-3.14.45/include/net/sock.h +--- linux-3.14.45.orig/include/net/sock.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/net/sock.h 2015-06-24 14:15:48.871862463 +0200 +@@ -899,6 +899,16 @@ + + int sk_wait_data(struct sock *sk, long *timeo); + ++/* START - needed for MPTCP */ ++extern void sock_def_error_report(struct sock *sk); ++extern struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, ++ int family); ++extern void sock_lock_init(struct sock *sk); ++ ++extern struct lock_class_key af_callback_keys[AF_MAX]; ++extern char *const af_family_clock_key_strings[AF_MAX+1]; ++/* END - needed for MPTCP */ ++ + struct request_sock_ops; + struct timewait_sock_ops; + struct inet_hashinfo; +diff -Nur linux-3.14.45.orig/include/net/tcp.h linux-3.14.45/include/net/tcp.h +--- linux-3.14.45.orig/include/net/tcp.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/net/tcp.h 2015-06-24 14:15:48.875862469 +0200 +@@ -176,6 +176,7 @@ + #define TCPOPT_SACK 5 /* SACK Block */ + #define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */ + #define TCPOPT_MD5SIG 19 /* MD5 Signature (RFC2385) */ ++#define TCPOPT_MPTCP 30 + #define TCPOPT_EXP 254 /* Experimental */ + /* Magic number to be after the option value for sharing TCP + * experimental options. See draft-ietf-tcpm-experimental-options-00.txt +@@ -234,6 +235,27 @@ + */ + #define TFO_SERVER_ALWAYS 0x1000 + ++/* Flags from tcp_input.c for tcp_ack */ ++#define FLAG_DATA 0x01 /* Incoming frame contained data. */ ++#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ ++#define FLAG_DATA_ACKED 0x04 /* This ACK acknowledged new data. */ ++#define FLAG_RETRANS_DATA_ACKED 0x08 /* "" "" some of which was retransmitted. */ ++#define FLAG_SYN_ACKED 0x10 /* This ACK acknowledged SYN. */ ++#define FLAG_DATA_SACKED 0x20 /* New SACK. */ ++#define FLAG_ECE 0x40 /* ECE in this ACK */ ++#define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ ++#define FLAG_ORIG_SACK_ACKED 0x200 /* Never retransmitted data are (s)acked */ ++#define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ ++#define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ ++#define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */ ++#define FLAG_UPDATE_TS_RECENT 0x4000 /* tcp_replace_ts_recent() */ ++#define MPTCP_FLAG_DATA_ACKED 0x8000 ++ ++#define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED) ++#define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) ++#define FLAG_CA_ALERT (FLAG_DATA_SACKED|FLAG_ECE) ++#define FLAG_FORWARD_PROGRESS (FLAG_ACKED|FLAG_DATA_SACKED) ++ + extern struct inet_timewait_death_row tcp_death_row; + + /* sysctl variables for tcp */ +@@ -349,6 +371,112 @@ + #define TCP_ADD_STATS_USER(net, field, val) SNMP_ADD_STATS_USER((net)->mib.tcp_statistics, field, val) + #define TCP_ADD_STATS(net, field, val) SNMP_ADD_STATS((net)->mib.tcp_statistics, field, val) + ++/**** START - Exports needed for MPTCP ****/ ++extern const struct inet_connection_sock_af_ops ipv4_specific; ++extern const struct inet_connection_sock_af_ops ipv6_specific; ++extern const struct inet_connection_sock_af_ops ipv6_mapped; ++extern const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops; ++extern const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops; ++ ++struct mptcp_options_received; ++ ++int tcp_close_state(struct sock *sk); ++void tcp_push(struct sock *sk, int flags, int mss_now, int nonagle, int ++ size_goal); ++void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now, ++ const struct sk_buff *skb); ++int tcp_xmit_probe_skb(struct sock *sk, int urgent); ++void tcp_cwnd_validate(struct sock *sk); ++void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb); ++int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, ++ gfp_t gfp_mask); ++unsigned int tcp_mss_split_point(const struct sock *sk, ++ const struct sk_buff *skb, ++ unsigned int mss_now, ++ unsigned int max_segs, ++ int nonagle); ++bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb); ++bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buff *skb, ++ unsigned int cur_mss, int nonagle); ++bool tcp_snd_wnd_test(const struct tcp_sock *tp, const struct sk_buff *skb, ++ unsigned int cur_mss); ++unsigned int tcp_cwnd_test(const struct tcp_sock *tp, const struct sk_buff *skb); ++int tcp_mtu_probe(struct sock *sk); ++int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, ++ unsigned int mss_now); ++void __pskb_trim_head(struct sk_buff *skb, int len); ++void tcp_queue_skb(struct sock *sk, struct sk_buff *skb); ++void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags); ++void tcp_reset(struct sock *sk); ++bool tcp_may_update_window(const struct tcp_sock *tp, const u32 ack, ++ const u32 ack_seq, const u32 nwin); ++bool tcp_urg_mode(const struct tcp_sock *tp); ++void tcp_ack_probe(struct sock *sk); ++void tcp_rearm_rto(struct sock *sk); ++int tcp_write_timeout(struct sock *sk); ++bool retransmits_timed_out(struct sock *sk, unsigned int boundary, ++ unsigned int timeout, bool syn_set); ++void tcp_write_err(struct sock *sk); ++void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int decr); ++void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, ++ unsigned int mss_now); ++ ++int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req); ++void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, ++ struct request_sock *req); ++__u32 tcp_v4_init_sequence(const struct sk_buff *skb); ++int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, ++ struct request_sock *req, ++ u16 queue_mapping); ++void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb); ++struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb); ++struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb); ++void tcp_v4_reqsk_destructor(struct request_sock *req); ++ ++int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req); ++void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, ++ struct request_sock *req); ++__u32 tcp_v6_init_sequence(const struct sk_buff *skb); ++int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, ++ struct flowi6 *fl6, struct request_sock *req, ++ u16 queue_mapping); ++void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb); ++int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); ++int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); ++void tcp_v6_destroy_sock(struct sock *sk); ++void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb); ++void tcp_v6_hash(struct sock *sk); ++struct sock *tcp_v6_hnd_req(struct sock *sk,struct sk_buff *skb); ++struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ++ struct request_sock *req, ++ struct dst_entry *dst); ++void tcp_v6_reqsk_destructor(struct request_sock *req); ++ ++void sock_valbool_flag(struct sock *sk, int bit, int valbool); ++unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, ++ int large_allowed); ++u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb); ++ ++void skb_clone_fraglist(struct sk_buff *skb); ++void copy_skb_header(struct sk_buff *new, const struct sk_buff *old); ++ ++void inet_twsk_free(struct inet_timewait_sock *tw); ++/* These states need RST on ABORT according to RFC793 */ ++static inline bool tcp_need_reset(int state) ++{ ++ return (1 << state) & ++ (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_FIN_WAIT1 | ++ TCPF_FIN_WAIT2 | TCPF_SYN_RECV); ++} ++ ++bool tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb, ++ int hlen); ++int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, ++ bool *fragstolen); ++bool tcp_try_coalesce(struct sock *sk, struct sk_buff *to, ++ struct sk_buff *from, bool *fragstolen); ++/**** END - Exports needed for MPTCP ****/ ++ + void tcp_tasklet_init(void); + + void tcp_v4_err(struct sk_buff *skb, u32); +@@ -445,6 +573,7 @@ + size_t len, int nonblock, int flags, int *addr_len); + void tcp_parse_options(const struct sk_buff *skb, + struct tcp_options_received *opt_rx, ++ struct mptcp_options_received *mopt_rx, + int estab, struct tcp_fastopen_cookie *foc); + const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); + +@@ -558,11 +687,15 @@ + void tcp_send_loss_probe(struct sock *sk); + bool tcp_schedule_loss_probe(struct sock *sk); + ++u16 tcp_select_window(struct sock *sk); ++ + /* tcp_input.c */ + void tcp_cwnd_application_limited(struct sock *sk); + void tcp_resume_early_retransmit(struct sock *sk); + void tcp_rearm_rto(struct sock *sk); + void tcp_reset(struct sock *sk); ++void tcp_set_rto(struct sock *sk); ++bool tcp_should_expand_sndbuf(const struct sock *sk); + + /* tcp_timer.c */ + void tcp_init_xmit_timers(struct sock *); +@@ -706,14 +839,24 @@ + */ + struct tcp_skb_cb { + union { +- struct inet_skb_parm h4; ++ union { ++ struct inet_skb_parm h4; + #if IS_ENABLED(CONFIG_IPV6) +- struct inet6_skb_parm h6; ++ struct inet6_skb_parm h6; ++#endif ++ } header; /* For incoming frames */ ++#ifdef CONFIG_MPTCP ++ __u32 path_mask; /* path indices that tried to send this skb */ + #endif +- } header; /* For incoming frames */ ++ }; + __u32 seq; /* Starting sequence number */ + __u32 end_seq; /* SEQ + FIN + SYN + datalen */ + __u32 when; /* used to compute rtt's */ ++#ifdef CONFIG_MPTCP ++ __u8 mptcp_flags; /* flags for the MPTCP layer */ ++ __u8 dss_off; /* Number of 4-byte words until ++ * seq-number */ ++#endif + __u8 tcp_flags; /* TCP header flags. (tcp[13]) */ + + __u8 sacked; /* State flags for SACK/FACK. */ +@@ -1061,7 +1204,8 @@ + /* Determine a window scaling and initial window to offer. */ + void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, + __u32 *window_clamp, int wscale_ok, +- __u8 *rcv_wscale, __u32 init_rcv_wnd); ++ __u8 *rcv_wscale, __u32 init_rcv_wnd, ++ const struct sock *sk); + + static inline int tcp_win_from_space(int space) + { +@@ -1073,12 +1217,18 @@ + /* Note: caller must be prepared to deal with negative returns */ + static inline int tcp_space(const struct sock *sk) + { ++ if (tcp_sk(sk)->mpc) ++ sk = tcp_sk(sk)->meta_sk; ++ + return tcp_win_from_space(sk->sk_rcvbuf - + atomic_read(&sk->sk_rmem_alloc)); + } + + static inline int tcp_full_space(const struct sock *sk) + { ++ if (tcp_sk(sk)->mpc) ++ sk = tcp_sk(sk)->meta_sk; ++ + return tcp_win_from_space(sk->sk_rcvbuf); + } + +@@ -1093,6 +1243,7 @@ + tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; + tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; + tcp_rsk(req)->snt_synack = 0; ++ tcp_rsk(req)->saw_mpc = 0; + req->mss = rx_opt->mss_clamp; + req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; + ireq->tstamp_ok = rx_opt->tstamp_ok; +diff -Nur linux-3.14.45.orig/include/uapi/linux/if.h linux-3.14.45/include/uapi/linux/if.h +--- linux-3.14.45.orig/include/uapi/linux/if.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/uapi/linux/if.h 2015-06-24 14:15:48.875862469 +0200 +@@ -53,6 +53,9 @@ + + #define IFF_ECHO 0x40000 /* echo sent packets */ + ++#define IFF_NOMULTIPATH 0x80000 /* Disable for MPTCP */ ++#define IFF_MPBACKUP 0x100000 /* Use as backup path for MPTCP */ ++ + #define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\ + IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT) + +diff -Nur linux-3.14.45.orig/include/uapi/linux/tcp.h linux-3.14.45/include/uapi/linux/tcp.h +--- linux-3.14.45.orig/include/uapi/linux/tcp.h 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/include/uapi/linux/tcp.h 2015-06-24 14:15:48.875862469 +0200 +@@ -112,6 +112,7 @@ + #define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ + #define TCP_TIMESTAMP 24 + #define TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes in write queue */ ++#define MPTCP_ENABLED 26 + + struct tcp_repair_opt { + __u32 opt_code; +diff -Nur linux-3.14.45.orig/net/Kconfig linux-3.14.45/net/Kconfig +--- linux-3.14.45.orig/net/Kconfig 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/Kconfig 2015-06-24 14:15:48.875862469 +0200 +@@ -79,6 +79,7 @@ + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" + source "net/netlabel/Kconfig" ++source "net/mptcp/Kconfig" + + endif # if INET + +diff -Nur linux-3.14.45.orig/net/Makefile linux-3.14.45/net/Makefile +--- linux-3.14.45.orig/net/Makefile 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/Makefile 2015-06-24 14:15:48.875862469 +0200 +@@ -20,6 +20,7 @@ + obj-$(CONFIG_XFRM) += xfrm/ + obj-$(CONFIG_UNIX) += unix/ + obj-$(CONFIG_NET) += ipv6/ ++obj-$(CONFIG_MPTCP) += mptcp/ + obj-$(CONFIG_PACKET) += packet/ + obj-$(CONFIG_NET_KEY) += key/ + obj-$(CONFIG_BRIDGE) += bridge/ +diff -Nur linux-3.14.45.orig/net/core/dev.c linux-3.14.45/net/core/dev.c +--- linux-3.14.45.orig/net/core/dev.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/core/dev.c 2015-06-24 14:15:48.875862469 +0200 +@@ -5399,7 +5399,7 @@ + + dev->flags = (flags & (IFF_DEBUG | IFF_NOTRAILERS | IFF_NOARP | + IFF_DYNAMIC | IFF_MULTICAST | IFF_PORTSEL | +- IFF_AUTOMEDIA)) | ++ IFF_AUTOMEDIA | IFF_NOMULTIPATH | IFF_MPBACKUP)) | + (dev->flags & (IFF_UP | IFF_VOLATILE | IFF_PROMISC | + IFF_ALLMULTI)); + +diff -Nur linux-3.14.45.orig/net/core/request_sock.c linux-3.14.45/net/core/request_sock.c +--- linux-3.14.45.orig/net/core/request_sock.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/core/request_sock.c 2015-06-24 14:15:48.875862469 +0200 +@@ -38,7 +38,8 @@ + EXPORT_SYMBOL(sysctl_max_syn_backlog); + + int reqsk_queue_alloc(struct request_sock_queue *queue, +- unsigned int nr_table_entries) ++ unsigned int nr_table_entries, ++ gfp_t flags) + { + size_t lopt_size = sizeof(struct listen_sock); + struct listen_sock *lopt; +@@ -48,9 +49,11 @@ + nr_table_entries = roundup_pow_of_two(nr_table_entries + 1); + lopt_size += nr_table_entries * sizeof(struct request_sock *); + if (lopt_size > PAGE_SIZE) +- lopt = vzalloc(lopt_size); ++ lopt = __vmalloc(lopt_size, ++ flags | __GFP_HIGHMEM | __GFP_ZERO, ++ PAGE_KERNEL); + else +- lopt = kzalloc(lopt_size, GFP_KERNEL); ++ lopt = kzalloc(lopt_size, flags); + if (lopt == NULL) + return -ENOMEM; + +diff -Nur linux-3.14.45.orig/net/core/skbuff.c linux-3.14.45/net/core/skbuff.c +--- linux-3.14.45.orig/net/core/skbuff.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/core/skbuff.c 2015-06-24 14:15:48.875862469 +0200 +@@ -491,7 +491,7 @@ + skb_drop_list(&skb_shinfo(skb)->frag_list); + } + +-static void skb_clone_fraglist(struct sk_buff *skb) ++void skb_clone_fraglist(struct sk_buff *skb) + { + struct sk_buff *list; + +@@ -913,7 +913,7 @@ + skb->inner_mac_header += off; + } + +-static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) ++void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) + { + __copy_skb_header(new, old); + +diff -Nur linux-3.14.45.orig/net/core/sock.c linux-3.14.45/net/core/sock.c +--- linux-3.14.45.orig/net/core/sock.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/core/sock.c 2015-06-24 14:15:48.875862469 +0200 +@@ -280,7 +280,7 @@ + "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG" , + "slock-AF_NFC" , "slock-AF_VSOCK" ,"slock-AF_MAX" + }; +-static const char *const af_family_clock_key_strings[AF_MAX+1] = { ++char *const af_family_clock_key_strings[AF_MAX+1] = { + "clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" , + "clock-AF_AX25" , "clock-AF_IPX" , "clock-AF_APPLETALK", + "clock-AF_NETROM", "clock-AF_BRIDGE" , "clock-AF_ATMPVC" , +@@ -301,7 +301,7 @@ + * sk_callback_lock locking rules are per-address-family, + * so split the lock classes by using a per-AF key: + */ +-static struct lock_class_key af_callback_keys[AF_MAX]; ++struct lock_class_key af_callback_keys[AF_MAX]; + + /* Take into consideration the size of the struct sk_buff overhead in the + * determination of these values, since that is non-constant across +@@ -651,7 +651,7 @@ + return ret; + } + +-static inline void sock_valbool_flag(struct sock *sk, int bit, int valbool) ++void sock_valbool_flag(struct sock *sk, int bit, int valbool) + { + if (valbool) + sock_set_flag(sk, bit); +@@ -1272,7 +1272,7 @@ + * + * (We also register the sk_lock with the lock validator.) + */ +-static inline void sock_lock_init(struct sock *sk) ++void sock_lock_init(struct sock *sk) + { + sock_lock_init_class_and_name(sk, + af_family_slock_key_strings[sk->sk_family], +@@ -1320,7 +1320,7 @@ + } + EXPORT_SYMBOL(sk_prot_clear_portaddr_nulls); + +-static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, ++struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, + int family) + { + struct sock *sk; +@@ -2252,7 +2252,7 @@ + rcu_read_unlock(); + } + +-static void sock_def_error_report(struct sock *sk) ++void sock_def_error_report(struct sock *sk) + { + struct socket_wq *wq; + +diff -Nur linux-3.14.45.orig/net/ipv4/Kconfig linux-3.14.45/net/ipv4/Kconfig +--- linux-3.14.45.orig/net/ipv4/Kconfig 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv4/Kconfig 2015-06-24 14:15:48.875862469 +0200 +@@ -556,6 +556,30 @@ + For further details see: + http://www.ews.uiuc.edu/~shaoliu/tcpillinois/index.html + ++config TCP_CONG_COUPLED ++ tristate "MPTCP COUPLED CONGESTION CONTROL" ++ depends on MPTCP ++ default n ++ ---help--- ++ MultiPath TCP Coupled Congestion Control ++ To enable it, just put 'coupled' in tcp_congestion_control ++ ++config TCP_CONG_OLIA ++ tristate "MPTCP Opportunistic Linked Increase" ++ depends on MPTCP ++ default n ++ ---help--- ++ MultiPath TCP Opportunistic Linked Increase Congestion Control ++ To enable it, just put 'olia' in tcp_congestion_control ++ ++config TCP_CONG_WVEGAS ++ tristate "MPTCP WVEGAS CONGESTION CONTROL" ++ depends on MPTCP ++ default n ++ ---help--- ++ wVegas congestion control for MPTCP ++ To enable it, just put 'wvegas' in tcp_congestion_control ++ + choice + prompt "Default TCP congestion control" + default DEFAULT_CUBIC +@@ -584,6 +608,15 @@ + config DEFAULT_WESTWOOD + bool "Westwood" if TCP_CONG_WESTWOOD=y + ++ config DEFAULT_COUPLED ++ bool "Coupled" if TCP_CONG_COUPLED=y ++ ++ config DEFAULT_OLIA ++ bool "Olia" if TCP_CONG_OLIA=y ++ ++ config DEFAULT_WVEGAS ++ bool "Wvegas" if TCP_CONG_WVEGAS=y ++ + config DEFAULT_RENO + bool "Reno" + +@@ -605,6 +638,8 @@ + default "vegas" if DEFAULT_VEGAS + default "westwood" if DEFAULT_WESTWOOD + default "veno" if DEFAULT_VENO ++ default "coupled" if DEFAULT_COUPLED ++ default "wvegas" if DEFAULT_WVEGAS + default "reno" if DEFAULT_RENO + default "cubic" + +diff -Nur linux-3.14.45.orig/net/ipv4/af_inet.c linux-3.14.45/net/ipv4/af_inet.c +--- linux-3.14.45.orig/net/ipv4/af_inet.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv4/af_inet.c 2015-06-24 14:15:48.875862469 +0200 +@@ -104,6 +104,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -246,8 +247,7 @@ + * Create an inet socket. + */ + +-static int inet_create(struct net *net, struct socket *sock, int protocol, +- int kern) ++int inet_create(struct net *net, struct socket *sock, int protocol, int kern) + { + struct sock *sk; + struct inet_protosw *answer; +@@ -679,6 +679,23 @@ + lock_sock(sk2); + + sock_rps_record_flow(sk2); ++ ++ if (sk2->sk_protocol == IPPROTO_TCP && tcp_sk(sk2)->mpc) { ++ struct sock *sk_it = sk2; ++ ++ mptcp_for_each_sk(tcp_sk(sk2)->mpcb, sk_it) ++ sock_rps_record_flow(sk_it); ++ ++ if (tcp_sk(sk2)->mpcb->master_sk) { ++ sk_it = tcp_sk(sk2)->mpcb->master_sk; ++ ++ write_lock_bh(&sk_it->sk_callback_lock); ++ sk_it->sk_wq = newsock->wq; ++ sk_it->sk_socket = newsock; ++ write_unlock_bh(&sk_it->sk_callback_lock); ++ } ++ } ++ + WARN_ON(!((1 << sk2->sk_state) & + (TCPF_ESTABLISHED | TCPF_SYN_RECV | + TCPF_CLOSE_WAIT | TCPF_CLOSE))); +@@ -1770,6 +1787,9 @@ + + ip_init(); + ++ /* We must initialize MPTCP before TCP. */ ++ mptcp_init(); ++ + tcp_v4_init(); + + /* Setup TCP slab cache for open requests. */ +diff -Nur linux-3.14.45.orig/net/ipv4/inet_connection_sock.c linux-3.14.45/net/ipv4/inet_connection_sock.c +--- linux-3.14.45.orig/net/ipv4/inet_connection_sock.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv4/inet_connection_sock.c 2015-06-24 14:15:48.875862469 +0200 +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #ifdef INET_CSK_DEBUG + const char inet_csk_timer_bug_msg[] = "inet_csk BUG: unknown timer value\n"; +@@ -468,8 +469,8 @@ + } + EXPORT_SYMBOL_GPL(inet_csk_route_child_sock); + +-static inline u32 inet_synq_hash(const __be32 raddr, const __be16 rport, +- const u32 rnd, const u32 synq_hsize) ++u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd, ++ const u32 synq_hsize) + { + return jhash_2words((__force u32)raddr, (__force u32)rport, rnd) & (synq_hsize - 1); + } +@@ -667,7 +668,12 @@ + const struct request_sock *req, + const gfp_t priority) + { +- struct sock *newsk = sk_clone_lock(sk, priority); ++ struct sock *newsk; ++ ++ if (sk->sk_protocol == IPPROTO_TCP && tcp_sk(sk)->mpc) ++ newsk = mptcp_sk_clone(sk, req->rsk_ops->family, priority); ++ else ++ newsk = sk_clone_lock(sk, priority); + + if (newsk != NULL) { + struct inet_connection_sock *newicsk = inet_csk(newsk); +@@ -744,7 +750,8 @@ + { + struct inet_sock *inet = inet_sk(sk); + struct inet_connection_sock *icsk = inet_csk(sk); +- int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries); ++ int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries, ++ GFP_KERNEL); + + if (rc != 0) + return rc; +@@ -802,9 +809,14 @@ + + while ((req = acc_req) != NULL) { + struct sock *child = req->sk; ++ bool mutex_taken = false; + + acc_req = req->dl_next; + ++ if (is_meta_sk(child)) { ++ mutex_lock(&tcp_sk(child)->mpcb->mpcb_mutex); ++ mutex_taken = true; ++ } + local_bh_disable(); + bh_lock_sock(child); + WARN_ON(sock_owned_by_user(child)); +@@ -833,6 +845,8 @@ + + bh_unlock_sock(child); + local_bh_enable(); ++ if (mutex_taken) ++ mutex_unlock(&tcp_sk(child)->mpcb->mpcb_mutex); + sock_put(child); + + sk_acceptq_removed(sk); +diff -Nur linux-3.14.45.orig/net/ipv4/syncookies.c linux-3.14.45/net/ipv4/syncookies.c +--- linux-3.14.45.orig/net/ipv4/syncookies.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv4/syncookies.c 2015-06-24 14:15:48.875862469 +0200 +@@ -284,7 +284,7 @@ + + /* check for timestamp cookie support */ + memset(&tcp_opt, 0, sizeof(tcp_opt)); +- tcp_parse_options(skb, &tcp_opt, 0, NULL); ++ tcp_parse_options(skb, &tcp_opt, NULL, 0, NULL); + + if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) + goto out; +@@ -354,10 +354,10 @@ + /* Try to redo what tcp_v4_send_synack did. */ + req->window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW); + +- tcp_select_initial_window(tcp_full_space(sk), req->mss, ++ tp->select_initial_window(tcp_full_space(sk), req->mss, + &req->rcv_wnd, &req->window_clamp, + ireq->wscale_ok, &rcv_wscale, +- dst_metric(&rt->dst, RTAX_INITRWND)); ++ dst_metric(&rt->dst, RTAX_INITRWND), sk); + + ireq->rcv_wscale = rcv_wscale; + +diff -Nur linux-3.14.45.orig/net/ipv4/tcp.c linux-3.14.45/net/ipv4/tcp.c +--- linux-3.14.45.orig/net/ipv4/tcp.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv4/tcp.c 2015-06-24 14:15:48.879862472 +0200 +@@ -271,6 +271,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -419,6 +420,9 @@ + sk->sk_sndbuf = sysctl_tcp_wmem[1]; + sk->sk_rcvbuf = sysctl_tcp_rmem[1]; + ++ /* Set function pointers in tcp_sock to tcp functions. */ ++ mptcp_init_tcp_sock(tp); ++ + local_bh_disable(); + sock_update_memcg(sk); + sk_sockets_allocated_inc(sk); +@@ -607,6 +611,8 @@ + tcb->seq = tcb->end_seq = tp->write_seq; + tcb->tcp_flags = TCPHDR_ACK; + tcb->sacked = 0; ++ if (tp->mpc) ++ mptcp_skb_entail_init(tp, skb); + skb_header_release(skb); + tcp_add_write_queue_tail(sk, skb); + sk->sk_wmem_queued += skb->truesize; +@@ -640,8 +646,8 @@ + atomic_read(&sk->sk_wmem_alloc) > skb->truesize; + } + +-static void tcp_push(struct sock *sk, int flags, int mss_now, +- int nonagle, int size_goal) ++void tcp_push(struct sock *sk, int flags, int mss_now, int nonagle, ++ int size_goal) + { + struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *skb; +@@ -726,6 +732,14 @@ + int ret; + + sock_rps_record_flow(sk); ++ ++#ifdef CONFIG_MPTCP ++ if (tcp_sk(sk)->mpc) { ++ struct sock *sk_it; ++ mptcp_for_each_sk(tcp_sk(sk)->mpcb, sk_it) ++ sock_rps_record_flow(sk_it); ++ } ++#endif + /* + * We can't seek on a socket input + */ +@@ -821,8 +835,7 @@ + return NULL; + } + +-static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, +- int large_allowed) ++unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now, int large_allowed) + { + struct tcp_sock *tp = tcp_sk(sk); + u32 xmit_size_goal, old_size_goal; +@@ -872,8 +885,13 @@ + { + int mss_now; + +- mss_now = tcp_current_mss(sk); +- *size_goal = tcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); ++ if (tcp_sk(sk)->mpc) { ++ mss_now = mptcp_current_mss(sk); ++ *size_goal = mptcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); ++ } else { ++ mss_now = tcp_current_mss(sk); ++ *size_goal = tcp_xmit_size_goal(sk, mss_now, !(flags & MSG_OOB)); ++ } + + return mss_now; + } +@@ -897,6 +915,26 @@ + goto out_err; + } + ++ if (tp->mpc) { ++ struct sock *sk_it = sk; ++ ++ /* We must check this with socket-lock hold because we iterate ++ * over the subflows. ++ */ ++ if (!mptcp_can_sendpage(sk)) { ++ ssize_t ret; ++ ++ release_sock(sk); ++ ret = sock_no_sendpage(sk->sk_socket, page, offset, ++ size, flags); ++ lock_sock(sk); ++ return ret; ++ } ++ ++ mptcp_for_each_sk(tp->mpcb, sk_it) ++ sock_rps_record_flow(sk_it); ++ } ++ + clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); + + mss_now = tcp_send_mss(sk, &size_goal, flags); +@@ -1001,8 +1039,9 @@ + { + ssize_t res; + +- if (!(sk->sk_route_caps & NETIF_F_SG) || +- !(sk->sk_route_caps & NETIF_F_ALL_CSUM)) ++ /* If MPTCP is enabled, we check it later after establishment */ ++ if (!tcp_sk(sk)->mpc && (!(sk->sk_route_caps & NETIF_F_SG) || ++ !(sk->sk_route_caps & NETIF_F_ALL_CSUM))) + return sock_no_sendpage(sk->sk_socket, page, offset, size, + flags); + +@@ -1018,6 +1057,9 @@ + const struct tcp_sock *tp = tcp_sk(sk); + int tmp = tp->mss_cache; + ++ if (tp->mpc) ++ return mptcp_select_size(sk, sg); ++ + if (sg) { + if (sk_can_gso(sk)) { + /* Small frames wont use a full page: +@@ -1105,6 +1147,12 @@ + goto do_error; + } + ++ if (tp->mpc) { ++ struct sock *sk_it = sk; ++ mptcp_for_each_sk(tp->mpcb, sk_it) ++ sock_rps_record_flow(sk_it); ++ } ++ + if (unlikely(tp->repair)) { + if (tp->repair_queue == TCP_RECV_QUEUE) { + copied = tcp_send_rcvq(sk, msg, size); +@@ -1132,7 +1180,10 @@ + if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) + goto out_err; + +- sg = !!(sk->sk_route_caps & NETIF_F_SG); ++ if (tp->mpc) ++ sg = mptcp_can_sg(sk); ++ else ++ sg = !!(sk->sk_route_caps & NETIF_F_SG); + + while (--iovlen >= 0) { + size_t seglen = iov->iov_len; +@@ -1176,8 +1227,15 @@ + + /* + * Check whether we can use HW checksum. ++ * ++ * If dss-csum is enabled, we do not do hw-csum. ++ * In case of non-mptcp we check the ++ * device-capabilities. ++ * In case of mptcp, hw-csum's will be handled ++ * later in mptcp_write_xmit. + */ +- if (sk->sk_route_caps & NETIF_F_ALL_CSUM) ++ if (((tp->mpc && !tp->mpcb->dss_csum) || !tp->mpc) && ++ (tp->mpc || sk->sk_route_caps & NETIF_F_ALL_CSUM)) + skb->ip_summed = CHECKSUM_PARTIAL; + + skb_entail(sk, skb); +@@ -1386,6 +1444,11 @@ + + struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); + ++ if (is_meta_sk(sk)) { ++ mptcp_cleanup_rbuf(sk, copied); ++ return; ++ } ++ + WARN(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq), + "cleanup rbuf bug: copied %X seq %X rcvnxt %X\n", + tp->copied_seq, TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt); +@@ -1422,7 +1485,7 @@ + + /* Optimize, __tcp_select_window() is not cheap. */ + if (2*rcv_window_now <= tp->window_clamp) { +- __u32 new_window = __tcp_select_window(sk); ++ __u32 new_window = tp->__select_window(sk); + + /* Send ACK now, if this read freed lots of space + * in our buffer. Certainly, new_window is new window. +@@ -1623,6 +1686,14 @@ + + lock_sock(sk); + ++#ifdef CONFIG_MPTCP ++ if (tp->mpc) { ++ struct sock *sk_it; ++ mptcp_for_each_sk(tp->mpcb, sk_it) ++ sock_rps_record_flow(sk_it); ++ } ++#endif ++ + err = -ENOTCONN; + if (sk->sk_state == TCP_LISTEN) + goto out; +@@ -2070,7 +2141,7 @@ + /* TCP_CLOSING */ TCP_CLOSING, + }; + +-static int tcp_close_state(struct sock *sk) ++int tcp_close_state(struct sock *sk) + { + int next = (int)new_state[sk->sk_state]; + int ns = next & TCP_STATE_MASK; +@@ -2099,8 +2170,12 @@ + (TCPF_ESTABLISHED | TCPF_SYN_SENT | + TCPF_SYN_RECV | TCPF_CLOSE_WAIT)) { + /* Clear out any half completed packets. FIN if needed. */ +- if (tcp_close_state(sk)) +- tcp_send_fin(sk); ++ if (tcp_close_state(sk)) { ++ if (!is_meta_sk(sk)) ++ tcp_send_fin(sk); ++ else ++ mptcp_send_fin(sk); ++ } + } + } + EXPORT_SYMBOL(tcp_shutdown); +@@ -2125,6 +2200,11 @@ + int data_was_unread = 0; + int state; + ++ if (is_meta_sk(sk)) { ++ mptcp_close(sk, timeout); ++ return; ++ } ++ + lock_sock(sk); + sk->sk_shutdown = SHUTDOWN_MASK; + +@@ -2291,15 +2371,6 @@ + } + EXPORT_SYMBOL(tcp_close); + +-/* These states need RST on ABORT according to RFC793 */ +- +-static inline bool tcp_need_reset(int state) +-{ +- return (1 << state) & +- (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_FIN_WAIT1 | +- TCPF_FIN_WAIT2 | TCPF_SYN_RECV); +-} +- + int tcp_disconnect(struct sock *sk, int flags) + { + struct inet_sock *inet = inet_sk(sk); +@@ -2340,6 +2411,13 @@ + if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) + inet_reset_saddr(sk); + ++ if (is_meta_sk(sk)) { ++ mptcp_disconnect(sk); ++ } else { ++ if (tp->inside_tk_table) ++ mptcp_hash_remove_bh(tp); ++ } ++ + sk->sk_shutdown = 0; + sock_reset_flag(sk, SOCK_DONE); + tp->srtt = 0; +@@ -2699,6 +2777,18 @@ + tp->notsent_lowat = val; + sk->sk_write_space(sk); + break; ++#ifdef CONFIG_MPTCP ++ case MPTCP_ENABLED: ++ if (sk->sk_state == TCP_CLOSE || sk->sk_state == TCP_LISTEN) { ++ if (val) ++ tp->mptcp_enabled = 1; ++ else ++ tp->mptcp_enabled = 0; ++ } else { ++ err = -EPERM; ++ } ++ break; ++#endif + default: + err = -ENOPROTOOPT; + break; +@@ -2918,6 +3008,11 @@ + case TCP_NOTSENT_LOWAT: + val = tp->notsent_lowat; + break; ++#ifdef CONFIG_MPTCP ++ case MPTCP_ENABLED: ++ val = tp->mptcp_enabled; ++ break; ++#endif + default: + return -ENOPROTOOPT; + } +@@ -3088,8 +3183,11 @@ + if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV) + TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS); + ++ WARN_ON(sk->sk_state == TCP_CLOSE); + tcp_set_state(sk, TCP_CLOSE); ++ + tcp_clear_xmit_timers(sk); ++ + if (req != NULL) + reqsk_fastopen_remove(sk, req, false); + +diff -Nur linux-3.14.45.orig/net/ipv4/tcp_input.c linux-3.14.45/net/ipv4/tcp_input.c +--- linux-3.14.45.orig/net/ipv4/tcp_input.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv4/tcp_input.c 2015-06-24 14:15:48.883862476 +0200 +@@ -74,6 +74,9 @@ + #include + #include + #include ++#include ++#include ++#include + + int sysctl_tcp_timestamps __read_mostly = 1; + int sysctl_tcp_window_scaling __read_mostly = 1; +@@ -99,25 +102,6 @@ + int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; + int sysctl_tcp_early_retrans __read_mostly = 3; + +-#define FLAG_DATA 0x01 /* Incoming frame contained data. */ +-#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ +-#define FLAG_DATA_ACKED 0x04 /* This ACK acknowledged new data. */ +-#define FLAG_RETRANS_DATA_ACKED 0x08 /* "" "" some of which was retransmitted. */ +-#define FLAG_SYN_ACKED 0x10 /* This ACK acknowledged SYN. */ +-#define FLAG_DATA_SACKED 0x20 /* New SACK. */ +-#define FLAG_ECE 0x40 /* ECE in this ACK */ +-#define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ +-#define FLAG_ORIG_SACK_ACKED 0x200 /* Never retransmitted data are (s)acked */ +-#define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ +-#define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ +-#define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */ +-#define FLAG_UPDATE_TS_RECENT 0x4000 /* tcp_replace_ts_recent() */ +- +-#define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED) +-#define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) +-#define FLAG_CA_ALERT (FLAG_DATA_SACKED|FLAG_ECE) +-#define FLAG_FORWARD_PROGRESS (FLAG_ACKED|FLAG_DATA_SACKED) +- + #define TCP_REMNANT (TCP_FLAG_FIN|TCP_FLAG_URG|TCP_FLAG_SYN|TCP_FLAG_PSH) + #define TCP_HP_BITS (~(TCP_RESERVED_BITS|TCP_FLAG_PSH)) + +@@ -283,8 +267,12 @@ + per_mss = roundup_pow_of_two(per_mss) + + SKB_DATA_ALIGN(sizeof(struct sk_buff)); + +- nr_segs = max_t(u32, TCP_INIT_CWND, tp->snd_cwnd); +- nr_segs = max_t(u32, nr_segs, tp->reordering + 1); ++ if (tp->mpc) { ++ nr_segs = mptcp_check_snd_buf(tp); ++ } else { ++ nr_segs = max_t(u32, TCP_INIT_CWND, tp->snd_cwnd); ++ nr_segs = max_t(u32, nr_segs, tp->reordering + 1); ++ } + + /* Fast Recovery (RFC 5681 3.2) : + * Cubic needs 1.7 factor, rounded to 2 to include +@@ -292,8 +280,16 @@ + */ + sndmem = 2 * nr_segs * per_mss; + +- if (sk->sk_sndbuf < sndmem) ++ /* MPTCP: after this sndmem is the new contribution of the ++ * current subflow to the aggregated sndbuf */ ++ if (sk->sk_sndbuf < sndmem) { ++ int old_sndbuf = sk->sk_sndbuf; + sk->sk_sndbuf = min(sndmem, sysctl_tcp_wmem[2]); ++ /* MPTCP: ok, the subflow sndbuf has grown, reflect ++ * this in the aggregate buffer.*/ ++ if (tp->mpc && old_sndbuf != sk->sk_sndbuf) ++ mptcp_update_sndbuf(tp->mpcb); ++ } + } + + /* 2. Tuning advertised window (window_clamp, rcv_ssthresh) +@@ -342,10 +338,12 @@ + static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb) + { + struct tcp_sock *tp = tcp_sk(sk); ++ struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); + + /* Check #1 */ +- if (tp->rcv_ssthresh < tp->window_clamp && +- (int)tp->rcv_ssthresh < tcp_space(sk) && ++ if (meta_tp->rcv_ssthresh < meta_tp->window_clamp && ++ (int)meta_tp->rcv_ssthresh < tcp_space(sk) && + !sk_under_memory_pressure(sk)) { + int incr; + +@@ -353,14 +351,14 @@ + * will fit to rcvbuf in future. + */ + if (tcp_win_from_space(skb->truesize) <= skb->len) +- incr = 2 * tp->advmss; ++ incr = 2 * meta_tp->advmss; + else +- incr = __tcp_grow_window(sk, skb); ++ incr = __tcp_grow_window(meta_sk, skb); + + if (incr) { + incr = max_t(int, incr, 2 * skb->len); +- tp->rcv_ssthresh = min(tp->rcv_ssthresh + incr, +- tp->window_clamp); ++ meta_tp->rcv_ssthresh = min(meta_tp->rcv_ssthresh + incr, ++ meta_tp->window_clamp); + inet_csk(sk)->icsk_ack.quick |= 1; + } + } +@@ -543,7 +541,10 @@ + int copied; + + time = tcp_time_stamp - tp->rcvq_space.time; +- if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0) ++ if (tp->mpc) { ++ if (mptcp_check_rtt(tp, time)) ++ return; ++ } else if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0) + return; + + /* Number of bytes copied to user in last RTT */ +@@ -768,7 +769,7 @@ + /* Calculate rto without backoff. This is the second half of Van Jacobson's + * routine referred to above. + */ +-static void tcp_set_rto(struct sock *sk) ++void tcp_set_rto(struct sock *sk) + { + const struct tcp_sock *tp = tcp_sk(sk); + /* Old crap is replaced with new one. 8) +@@ -2909,7 +2910,7 @@ + return false; + + tcp_rtt_estimator(sk, seq_rtt); +- tcp_set_rto(sk); ++ tp->set_rto(sk); + + /* RFC6298: only reset backoff on valid RTT measurement. */ + inet_csk(sk)->icsk_backoff = 0; +@@ -2993,7 +2994,7 @@ + } + + /* If we get here, the whole TSO packet has not been acked. */ +-static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb) ++u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb) + { + struct tcp_sock *tp = tcp_sk(sk); + u32 packets_acked; +@@ -3088,6 +3089,8 @@ + */ + if (!(scb->tcp_flags & TCPHDR_SYN)) { + flag |= FLAG_DATA_ACKED; ++ if (tp->mpc && mptcp_is_data_seq(skb)) ++ flag |= MPTCP_FLAG_DATA_ACKED; + } else { + flag |= FLAG_SYN_ACKED; + tp->retrans_stamp = 0; +@@ -3190,7 +3193,7 @@ + return flag; + } + +-static void tcp_ack_probe(struct sock *sk) ++void tcp_ack_probe(struct sock *sk) + { + const struct tcp_sock *tp = tcp_sk(sk); + struct inet_connection_sock *icsk = inet_csk(sk); +@@ -3237,9 +3240,8 @@ + /* Check that window update is acceptable. + * The function assumes that snd_una<=ack<=snd_next. + */ +-static inline bool tcp_may_update_window(const struct tcp_sock *tp, +- const u32 ack, const u32 ack_seq, +- const u32 nwin) ++bool tcp_may_update_window(const struct tcp_sock *tp, const u32 ack, ++ const u32 ack_seq, const u32 nwin) + { + return after(ack, tp->snd_una) || + after(ack_seq, tp->snd_wl1) || +@@ -3358,7 +3360,7 @@ + } + + /* This routine deals with incoming acks, but not outgoing ones. */ +-static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) ++static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) + { + struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); +@@ -3453,6 +3455,16 @@ + flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una, sack_rtt); + acked -= tp->packets_out; + ++ if (tp->mpc) { ++ if (mptcp_fallback_infinite(sk, flag)) { ++ pr_err("%s resetting flow\n", __func__); ++ mptcp_send_reset(sk); ++ goto invalid_ack; ++ } ++ ++ mptcp_clean_rtx_infinite(skb, sk); ++ } ++ + /* Advance cwnd if state allows */ + if (tcp_may_raise_cwnd(sk, flag)) + tcp_cong_avoid(sk, ack, acked, prior_in_flight); +@@ -3517,8 +3529,9 @@ + * the fast version below fails. + */ + void tcp_parse_options(const struct sk_buff *skb, +- struct tcp_options_received *opt_rx, int estab, +- struct tcp_fastopen_cookie *foc) ++ struct tcp_options_received *opt_rx, ++ struct mptcp_options_received *mopt, ++ int estab, struct tcp_fastopen_cookie *foc) + { + const unsigned char *ptr; + const struct tcphdr *th = tcp_hdr(skb); +@@ -3601,6 +3614,10 @@ + */ + break; + #endif ++ case TCPOPT_MPTCP: ++ mptcp_parse_options(ptr - 2, opsize, opt_rx, ++ mopt, skb); ++ break; + case TCPOPT_EXP: + /* Fast Open option shares code 254 using a + * 16 bits magic number. It's valid only in +@@ -3662,8 +3679,8 @@ + if (tcp_parse_aligned_timestamp(tp, th)) + return true; + } +- +- tcp_parse_options(skb, &tp->rx_opt, 1, NULL); ++ tcp_parse_options(skb, &tp->rx_opt, tp->mpc ? &tp->mptcp->rx_opt : NULL, ++ 1, NULL); + if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) + tp->rx_opt.rcv_tsecr -= tp->tsoffset; + +@@ -3836,6 +3853,8 @@ + dst = __sk_dst_get(sk); + if (!dst || !dst_metric(dst, RTAX_QUICKACK)) + inet_csk(sk)->icsk_ack.pingpong = 1; ++ if (tp->mpc) ++ mptcp_sub_close_passive(sk); + break; + + case TCP_CLOSE_WAIT: +@@ -3857,6 +3876,13 @@ + tcp_set_state(sk, TCP_CLOSING); + break; + case TCP_FIN_WAIT2: ++ if (tp->mpc) { ++ /* The socket will get closed by mptcp_data_ready. ++ * We first have to process all data-sequences. ++ */ ++ tp->close_it = 1; ++ break; ++ } + /* Received a FIN -- send ACK and enter TIME_WAIT. */ + tcp_send_ack(sk); + tcp_time_wait(sk, TCP_TIME_WAIT, 0); +@@ -3881,6 +3907,10 @@ + if (!sock_flag(sk, SOCK_DEAD)) { + sk->sk_state_change(sk); + ++ /* Don't wake up MPTCP-subflows */ ++ if (tp->mpc) ++ return; ++ + /* Do not send POLL_HUP for half duplex close. */ + if (sk->sk_shutdown == SHUTDOWN_MASK || + sk->sk_state == TCP_CLOSE) +@@ -4078,7 +4108,11 @@ + tcp_dsack_extend(sk, TCP_SKB_CB(skb)->seq, dsack); + } + +- if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) { ++ /* In case of MPTCP, the segment may be empty if it's a ++ * non-data DATA_FIN. (see beginning of tcp_data_queue) ++ */ ++ if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt) && ++ !(tp->mpc && TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq)) { + SOCK_DEBUG(sk, "ofo packet was already received\n"); + __skb_unlink(skb, &tp->out_of_order_queue); + __kfree_skb(skb); +@@ -4102,6 +4136,9 @@ + static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb, + unsigned int size) + { ++ if (tcp_sk(sk)->mpc) ++ sk = mptcp_meta_sk(sk); ++ + if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || + !sk_rmem_schedule(sk, skb, size)) { + +@@ -4132,15 +4169,16 @@ + * Better try to coalesce them right now to avoid future collapses. + * Returns true if caller should free @from instead of queueing it + */ +-static bool tcp_try_coalesce(struct sock *sk, +- struct sk_buff *to, +- struct sk_buff *from, +- bool *fragstolen) ++bool tcp_try_coalesce(struct sock *sk, struct sk_buff *to, struct sk_buff *from, ++ bool *fragstolen) + { + int delta; + + *fragstolen = false; + ++ if (tcp_sk(sk)->mpc && !is_meta_sk(sk)) ++ return false; ++ + if (tcp_hdr(from)->fin) + return false; + +@@ -4230,7 +4268,9 @@ + + /* Do skb overlap to previous one? */ + if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { +- if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { ++ /* MPTCP allows non-data data-fin to be in the ofo-queue */ ++ if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq) && ++ !(tp->mpc && end_seq == seq)) { + /* All the bits are present. Drop. */ + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFOMERGE); + __kfree_skb(skb); +@@ -4268,6 +4308,9 @@ + end_seq); + break; + } ++ /* MPTCP allows non-data data-fin to be in the ofo-queue */ ++ if (tp->mpc && TCP_SKB_CB(skb1)->seq == TCP_SKB_CB(skb1)->end_seq) ++ continue; + __skb_unlink(skb1, &tp->out_of_order_queue); + tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq, + TCP_SKB_CB(skb1)->end_seq); +@@ -4285,8 +4328,8 @@ + } + } + +-static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, +- bool *fragstolen) ++int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen, ++ bool *fragstolen) + { + int eaten; + struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue); +@@ -4348,7 +4391,10 @@ + int eaten = -1; + bool fragstolen = false; + +- if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) ++ /* If no data is present, but a data_fin is in the options, we still ++ * have to call mptcp_queue_skb later on. */ ++ if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq && ++ !(tp->mpc && mptcp_is_data_fin(skb))) + goto drop; + + skb_dst_drop(skb); +@@ -4394,7 +4440,7 @@ + eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen); + } + tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; +- if (skb->len) ++ if (skb->len || mptcp_is_data_fin(skb)) + tcp_event_data_recv(sk, skb); + if (th->fin) + tcp_fin(sk); +@@ -4416,7 +4462,11 @@ + + if (eaten > 0) + kfree_skb_partial(skb, fragstolen); +- if (!sock_flag(sk, SOCK_DEAD)) ++ if (!sock_flag(sk, SOCK_DEAD) || tp->mpc) ++ /* MPTCP: we always have to call data_ready, because ++ * we may be about to receive a data-fin, which still ++ * must get queued. ++ */ + sk->sk_data_ready(sk, 0); + return; + } +@@ -4468,6 +4518,8 @@ + next = skb_queue_next(list, skb); + + __skb_unlink(skb, list); ++ if (tcp_sk(sk)->mpc) ++ mptcp_remove_shortcuts(tcp_sk(sk)->mpcb, skb); + __kfree_skb(skb); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRCVCOLLAPSED); + +@@ -4640,6 +4692,18 @@ + struct tcp_sock *tp = tcp_sk(sk); + bool res = false; + ++ if (is_meta_sk(sk)) { ++ if (!skb_queue_empty(&tp->out_of_order_queue)) { ++ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_OFOPRUNED); ++ mptcp_purge_ofo_queue(tp); ++ ++ /* No sack at the mptcp-level */ ++ sk_mem_reclaim(sk); ++ res = true; ++ } ++ return res; ++ } ++ + if (!skb_queue_empty(&tp->out_of_order_queue)) { + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_OFOPRUNED); + __skb_queue_purge(&tp->out_of_order_queue); +@@ -4729,7 +4793,7 @@ + tp->snd_cwnd_stamp = tcp_time_stamp; + } + +-static bool tcp_should_expand_sndbuf(const struct sock *sk) ++bool tcp_should_expand_sndbuf(const struct sock *sk) + { + const struct tcp_sock *tp = tcp_sk(sk); + +@@ -4764,7 +4828,7 @@ + { + struct tcp_sock *tp = tcp_sk(sk); + +- if (tcp_should_expand_sndbuf(sk)) { ++ if (tp->should_expand_sndbuf(sk)) { + tcp_sndbuf_expand(sk); + tp->snd_cwnd_stamp = tcp_time_stamp; + } +@@ -4776,8 +4840,9 @@ + { + if (sock_flag(sk, SOCK_QUEUE_SHRUNK)) { + sock_reset_flag(sk, SOCK_QUEUE_SHRUNK); +- if (sk->sk_socket && +- test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) ++ if (tcp_sk(sk)->mpc || ++ (sk->sk_socket && ++ test_bit(SOCK_NOSPACE, &sk->sk_socket->flags))) + tcp_new_space(sk); + } + } +@@ -4800,7 +4865,7 @@ + /* ... and right edge of window advances far enough. + * (tcp_recvmsg() will send ACK otherwise). Or... + */ +- __tcp_select_window(sk) >= tp->rcv_wnd) || ++ tp->__select_window(sk) >= tp->rcv_wnd) || + /* We ACK each frame or... */ + tcp_in_quickack_mode(sk) || + /* We have out of order data. */ +@@ -4902,6 +4967,10 @@ + { + struct tcp_sock *tp = tcp_sk(sk); + ++ /* MPTCP urgent data is not yet supported */ ++ if (tp->mpc) ++ return; ++ + /* Check if we get a new urgent pointer - normally not. */ + if (th->urg) + tcp_check_urg(sk, th); +@@ -4969,8 +5038,7 @@ + } + + #ifdef CONFIG_NET_DMA +-static bool tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb, +- int hlen) ++bool tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb, int hlen) + { + struct tcp_sock *tp = tcp_sk(sk); + int chunk = skb->len - hlen; +@@ -5079,9 +5147,15 @@ + goto discard; + } + ++ /* If valid: post process the received MPTCP options. */ ++ if (tp->mpc && mptcp_handle_options(sk, th, skb)) ++ goto discard; ++ + return true; + + discard: ++ if (tp->mpc) ++ mptcp_reset_mopt(tp); + __kfree_skb(skb); + return false; + } +@@ -5133,6 +5207,10 @@ + + tp->rx_opt.saw_tstamp = 0; + ++ /* MPTCP: force slowpath. */ ++ if (tp->mpc) ++ goto slow_path; ++ + /* pred_flags is 0xS?10 << 16 + snd_wnd + * if header_prediction is to be made + * 'S' will always be tp->tcp_header_len >> 2 +@@ -5347,7 +5425,7 @@ + */ + tp->lsndtime = tcp_time_stamp; + +- tcp_init_buffer_space(sk); ++ tp->init_buffer_space(sk); + + if (sock_flag(sk, SOCK_KEEPOPEN)) + inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tp)); +@@ -5377,7 +5455,7 @@ + /* Get original SYNACK MSS value if user MSS sets mss_clamp */ + tcp_clear_options(&opt); + opt.user_mss = opt.mss_clamp = 0; +- tcp_parse_options(synack, &opt, 0, NULL); ++ tcp_parse_options(synack, &opt, NULL, 0, NULL); + mss = opt.mss_clamp; + } + +@@ -5412,8 +5490,11 @@ + struct tcp_sock *tp = tcp_sk(sk); + struct tcp_fastopen_cookie foc = { .len = -1 }; + int saved_clamp = tp->rx_opt.mss_clamp; ++ struct mptcp_options_received mopt; ++ mptcp_init_mp_opt(&mopt); + +- tcp_parse_options(skb, &tp->rx_opt, 0, &foc); ++ tcp_parse_options(skb, &tp->rx_opt, ++ tp->mpc ? &tp->mptcp->rx_opt : &mopt, 0, &foc); + if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) + tp->rx_opt.rcv_tsecr -= tp->tsoffset; + +@@ -5460,6 +5541,21 @@ + if (!th->syn) + goto discard_and_undo; + ++ if (tp->request_mptcp || tp->mpc) { ++ int ret; ++ ret = mptcp_rcv_synsent_state_process(sk, &sk, ++ skb, &mopt); ++ ++ /* May have changed if we support MPTCP */ ++ tp = tcp_sk(sk); ++ icsk = inet_csk(sk); ++ ++ if (ret == 1) ++ goto reset_and_undo; ++ if (ret == 2) ++ goto discard; ++ } ++ + /* rfc793: + * "If the SYN bit is on ... + * are acceptable then ... +@@ -5472,6 +5568,15 @@ + tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); + tcp_ack(sk, skb, FLAG_SLOWPATH); + ++ if (tp->mpc && !is_master_tp(tp)) { ++ /* Timer for repeating the ACK until an answer ++ * arrives. Used only when establishing an additional ++ * subflow inside of an MPTCP connection. ++ */ ++ sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, ++ jiffies + icsk->icsk_rto); ++ } ++ + /* Ok.. it's good. Set up sequence numbers and + * move to established. + */ +@@ -5498,6 +5603,11 @@ + tp->tcp_header_len = sizeof(struct tcphdr); + } + ++ if (tp->mpc) { ++ tp->tcp_header_len += MPTCP_SUB_LEN_DSM_ALIGN; ++ tp->advmss -= MPTCP_SUB_LEN_DSM_ALIGN; ++ } ++ + if (tcp_is_sack(tp) && sysctl_tcp_fack) + tcp_enable_fack(tp); + +@@ -5518,7 +5628,9 @@ + tcp_rcv_fastopen_synack(sk, skb, &foc)) + return -1; + +- if (sk->sk_write_pending || ++ /* With MPTCP we cannot send data on the third ack due to the ++ * lack of option-space */ ++ if ((sk->sk_write_pending && !tp->mpc) || + icsk->icsk_accept_queue.rskq_defer_accept || + icsk->icsk_ack.pingpong) { + /* Save one ACK. Data will be ready after +@@ -5560,6 +5672,7 @@ + tcp_paws_reject(&tp->rx_opt, 0)) + goto discard_and_undo; + ++ /* TODO - check this here for MPTCP */ + if (th->syn) { + /* We see SYN without ACK. It is attempt of + * simultaneous connect with crossed SYNs. +@@ -5576,6 +5689,11 @@ + tp->tcp_header_len = sizeof(struct tcphdr); + } + ++ if (tp->mpc) { ++ tp->tcp_header_len += MPTCP_SUB_LEN_DSM_ALIGN; ++ tp->advmss -= MPTCP_SUB_LEN_DSM_ALIGN; ++ } ++ + tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; + tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1; + +@@ -5634,6 +5752,7 @@ + + int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, + const struct tcphdr *th, unsigned int len) ++ __releases(&sk->sk_lock.slock) + { + struct tcp_sock *tp = tcp_sk(sk); + struct inet_connection_sock *icsk = inet_csk(sk); +@@ -5685,6 +5804,10 @@ + + case TCP_SYN_SENT: + queued = tcp_rcv_synsent_state_process(sk, skb, th, len); ++ if (is_meta_sk(sk)) { ++ sk = tcp_sk(sk)->mpcb->master_sk; ++ tp = tcp_sk(sk); ++ } + if (queued >= 0) + return queued; + +@@ -5692,6 +5815,8 @@ + tcp_urg(sk, skb, th); + __kfree_skb(skb); + tcp_data_snd_check(sk); ++ if (tp->mpc && is_master_tp(tp)) ++ bh_unlock_sock(sk); + return 0; + } + +@@ -5734,7 +5859,7 @@ + + tcp_mtup_init(sk); + tp->copied_seq = tp->rcv_nxt; +- tcp_init_buffer_space(sk); ++ tp->init_buffer_space(sk); + } + smp_mb(); + tcp_set_state(sk, TCP_ESTABLISHED); +@@ -5754,6 +5879,8 @@ + + if (tp->rx_opt.tstamp_ok) + tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; ++ if (tp->mpc) ++ tp->advmss -= MPTCP_SUB_LEN_DSM_ALIGN; + + if (req) { + /* Re-arm the timer because data may have been sent out. +@@ -5775,6 +5902,12 @@ + + tcp_initialize_rcv_mss(sk); + tcp_fast_path_on(tp); ++ /* Send an ACK when establishing a new ++ * MPTCP subflow, i.e. using an MP_JOIN ++ * subtype. ++ */ ++ if (tp->mpc && !is_master_tp(tp)) ++ tcp_send_ack(sk); + break; + + case TCP_FIN_WAIT1: { +@@ -5826,7 +5959,8 @@ + tmo = tcp_fin_time(sk); + if (tmo > TCP_TIMEWAIT_LEN) { + inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN); +- } else if (th->fin || sock_owned_by_user(sk)) { ++ } else if (th->fin || mptcp_is_data_fin(skb) || ++ sock_owned_by_user(sk)) { + /* Bad case. We could lose such FIN otherwise. + * It is not a big problem, but it looks confusing + * and not so rare event. We still can lose it now, +@@ -5855,6 +5989,9 @@ + goto discard; + } + break; ++ case TCP_CLOSE: ++ if (tp->mp_killed) ++ goto discard; + } + + /* step 6: check the URG bit */ +@@ -5875,7 +6012,11 @@ + */ + if (sk->sk_shutdown & RCV_SHUTDOWN) { + if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && +- after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) { ++ after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt) && ++ !tp->mpc) { ++ /* In case of mptcp, the reset is handled by ++ * mptcp_rcv_state_process ++ */ + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA); + tcp_reset(sk); + return 1; +diff -Nur linux-3.14.45.orig/net/ipv4/tcp_ipv4.c linux-3.14.45/net/ipv4/tcp_ipv4.c +--- linux-3.14.45.orig/net/ipv4/tcp_ipv4.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv4/tcp_ipv4.c 2015-06-24 14:15:48.883862476 +0200 +@@ -67,6 +67,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -99,7 +101,7 @@ + struct inet_hashinfo tcp_hashinfo; + EXPORT_SYMBOL(tcp_hashinfo); + +-static inline __u32 tcp_v4_init_sequence(const struct sk_buff *skb) ++__u32 tcp_v4_init_sequence(const struct sk_buff *skb) + { + return secure_tcp_sequence_number(ip_hdr(skb)->daddr, + ip_hdr(skb)->saddr, +@@ -335,7 +337,7 @@ + struct inet_sock *inet; + const int type = icmp_hdr(icmp_skb)->type; + const int code = icmp_hdr(icmp_skb)->code; +- struct sock *sk; ++ struct sock *sk, *meta_sk; + struct sk_buff *skb; + struct request_sock *req; + __u32 seq; +@@ -359,13 +361,19 @@ + return; + } + +- bh_lock_sock(sk); ++ tp = tcp_sk(sk); ++ if (tp->mpc) ++ meta_sk = mptcp_meta_sk(sk); ++ else ++ meta_sk = sk; ++ ++ bh_lock_sock(meta_sk); + /* If too many ICMPs get dropped on busy + * servers this needs to be solved differently. + * We do take care of PMTU discovery (RFC1191) special case : + * we can receive locally generated ICMP messages while socket is held. + */ +- if (sock_owned_by_user(sk)) { ++ if (sock_owned_by_user(meta_sk)) { + if (!(type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)) + NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS); + } +@@ -378,7 +386,6 @@ + } + + icsk = inet_csk(sk); +- tp = tcp_sk(sk); + req = tp->fastopen_rsk; + seq = ntohl(th->seq); + if (sk->sk_state != TCP_LISTEN && +@@ -412,11 +419,13 @@ + goto out; + + tp->mtu_info = info; +- if (!sock_owned_by_user(sk)) { ++ if (!sock_owned_by_user(meta_sk)) { + tcp_v4_mtu_reduced(sk); + } else { + if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags)) + sock_hold(sk); ++ if (tp->mpc) ++ mptcp_tsq_flags(sk); + } + goto out; + } +@@ -432,7 +441,7 @@ + + /* XXX (TFO) - revisit the following logic for TFO */ + +- if (sock_owned_by_user(sk)) ++ if (sock_owned_by_user(meta_sk)) + break; + + icsk->icsk_backoff--; +@@ -474,7 +483,7 @@ + switch (sk->sk_state) { + struct request_sock *req, **prev; + case TCP_LISTEN: +- if (sock_owned_by_user(sk)) ++ if (sock_owned_by_user(meta_sk)) + goto out; + + req = inet_csk_search_req(sk, &prev, th->dest, +@@ -507,7 +516,7 @@ + It can f.e. if SYNs crossed, + or Fast Open. + */ +- if (!sock_owned_by_user(sk)) { ++ if (!sock_owned_by_user(meta_sk)) { + sk->sk_err = err; + + sk->sk_error_report(sk); +@@ -536,7 +545,7 @@ + */ + + inet = inet_sk(sk); +- if (!sock_owned_by_user(sk) && inet->recverr) { ++ if (!sock_owned_by_user(meta_sk) && inet->recverr) { + sk->sk_err = err; + sk->sk_error_report(sk); + } else { /* Only an error on timeout */ +@@ -544,7 +553,7 @@ + } + + out: +- bh_unlock_sock(sk); ++ bh_unlock_sock(meta_sk); + sock_put(sk); + } + +@@ -586,7 +595,7 @@ + * Exception: precedence violation. We do not implement it in any case. + */ + +-static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) ++void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb) + { + const struct tcphdr *th = tcp_hdr(skb); + struct { +@@ -711,10 +720,10 @@ + outside socket context is ugly, certainly. What can I do? + */ + +-static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, ++static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 data_ack, + u32 win, u32 tsval, u32 tsecr, int oif, + struct tcp_md5sig_key *key, +- int reply_flags, u8 tos) ++ int reply_flags, u8 tos, int mptcp) + { + const struct tcphdr *th = tcp_hdr(skb); + struct { +@@ -723,6 +732,10 @@ + #ifdef CONFIG_TCP_MD5SIG + + (TCPOLEN_MD5SIG_ALIGNED >> 2) + #endif ++#ifdef CONFIG_MPTCP ++ + ((MPTCP_SUB_LEN_DSS >> 2) + ++ (MPTCP_SUB_LEN_ACK >> 2)) ++#endif + ]; + } rep; + struct ip_reply_arg arg; +@@ -767,6 +780,21 @@ + ip_hdr(skb)->daddr, &rep.th); + } + #endif ++#ifdef CONFIG_MPTCP ++ if (mptcp) { ++ int offset = (tsecr) ? 3 : 0; ++ /* Construction of 32-bit data_ack */ ++ rep.opt[offset++] = htonl((TCPOPT_MPTCP << 24) | ++ ((MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK) << 16) | ++ (0x20 << 8) | ++ (0x01)); ++ rep.opt[offset] = htonl(data_ack); ++ ++ arg.iov[0].iov_len += MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK; ++ rep.th.doff = arg.iov[0].iov_len / 4; ++ } ++#endif /* CONFIG_MPTCP */ ++ + arg.flags = reply_flags; + arg.csum = csum_tcpudp_nofold(ip_hdr(skb)->daddr, + ip_hdr(skb)->saddr, /* XXX */ +@@ -786,36 +814,44 @@ + { + struct inet_timewait_sock *tw = inet_twsk(sk); + struct tcp_timewait_sock *tcptw = tcp_twsk(sk); ++ u32 data_ack = 0; ++ int mptcp = 0; ++ ++ if (tcptw->mptcp_tw && tcptw->mptcp_tw->meta_tw) { ++ data_ack = (u32)tcptw->mptcp_tw->rcv_nxt; ++ mptcp = 1; ++ } + + tcp_v4_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, ++ data_ack, + tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, + tcp_time_stamp + tcptw->tw_ts_offset, + tcptw->tw_ts_recent, + tw->tw_bound_dev_if, + tcp_twsk_md5_key(tcptw), + tw->tw_transparent ? IP_REPLY_ARG_NOSRCCHECK : 0, +- tw->tw_tos ++ tw->tw_tos, mptcp + ); + + inet_twsk_put(tw); + } + +-static void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, +- struct request_sock *req) ++void tcp_v4_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, ++ struct request_sock *req) + { + /* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV + * sk->sk_state == TCP_SYN_RECV -> for Fast Open. + */ + tcp_v4_send_ack(skb, (sk->sk_state == TCP_LISTEN) ? + tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt, +- tcp_rsk(req)->rcv_nxt, req->rcv_wnd, ++ tcp_rsk(req)->rcv_nxt, 0, req->rcv_wnd, + tcp_time_stamp, + req->ts_recent, + 0, + tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr, + AF_INET), + inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, +- ip_hdr(skb)->tos); ++ ip_hdr(skb)->tos, 0); + } + + /* +@@ -823,9 +859,9 @@ + * This still operates on a request_sock only, not on a big + * socket. + */ +-static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, +- struct request_sock *req, +- u16 queue_mapping) ++int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, ++ struct request_sock *req, ++ u16 queue_mapping) + { + const struct inet_request_sock *ireq = inet_rsk(req); + struct flowi4 fl4; +@@ -853,7 +889,7 @@ + return err; + } + +-static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) ++int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req) + { + int res = tcp_v4_send_synack(sk, NULL, req, 0); + +@@ -865,7 +901,7 @@ + /* + * IPv4 request_sock destructor. + */ +-static void tcp_v4_reqsk_destructor(struct request_sock *req) ++void tcp_v4_reqsk_destructor(struct request_sock *req) + { + kfree(inet_rsk(req)->opt); + } +@@ -905,7 +941,7 @@ + /* + * Save and compile IPv4 options into the request_sock if needed. + */ +-static struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb) ++struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb) + { + const struct ip_options *opt = &(IPCB(skb)->opt); + struct ip_options_rcu *dopt = NULL; +@@ -1257,7 +1293,7 @@ + }; + + #ifdef CONFIG_TCP_MD5SIG +-static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { ++const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { + .md5_lookup = tcp_v4_reqsk_md5_lookup, + .calc_md5_hash = tcp_v4_md5_hash_skb, + }; +@@ -1415,7 +1451,7 @@ + tcp_init_congestion_control(child); + tcp_mtup_init(child); + tcp_init_metrics(child); +- tcp_init_buffer_space(child); ++ tp->init_buffer_space(child); + + /* Queue the data carried in the SYN packet. We need to first + * bump skb's refcnt because the caller will attempt to free it. +@@ -1447,6 +1483,7 @@ + int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) + { + struct tcp_options_received tmp_opt; ++ struct mptcp_options_received mopt; + struct request_sock *req; + struct inet_request_sock *ireq; + struct tcp_sock *tp = tcp_sk(sk); +@@ -1461,6 +1498,22 @@ + struct sk_buff *skb_synack; + int do_fastopen; + ++ tcp_clear_options(&tmp_opt); ++ tmp_opt.mss_clamp = TCP_MSS_DEFAULT; ++ tmp_opt.user_mss = tp->rx_opt.user_mss; ++ mptcp_init_mp_opt(&mopt); ++ tcp_parse_options(skb, &tmp_opt, &mopt, 0, want_cookie ? NULL : &foc); ++ ++#ifdef CONFIG_MPTCP ++ /* MPTCP structures not initialized, so clear MPTCP fields */ ++ if (mptcp_init_failed) ++ mptcp_init_mp_opt(&mopt); ++ ++ if (mopt.is_mp_join) ++ return mptcp_do_join_short(skb, &mopt, &tmp_opt, sock_net(sk)); ++ if (mopt.drop_me) ++ goto drop; ++#endif + /* Never answer to SYNs send to broadcast or multicast */ + if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) + goto drop; +@@ -1486,7 +1539,22 @@ + goto drop; + } + +- req = inet_reqsk_alloc(&tcp_request_sock_ops); ++#ifdef CONFIG_MPTCP ++ if (sysctl_mptcp_enabled == MPTCP_APP && !tp->mptcp_enabled) ++ mopt.saw_mpc = 0; ++ if (mopt.saw_mpc && !want_cookie) { ++ req = inet_reqsk_alloc(&mptcp_request_sock_ops); ++ ++ if (!req) ++ goto drop; ++ ++ mptcp_rsk(req)->mpcb = NULL; ++ mptcp_rsk(req)->dss_csum = mopt.dss_csum; ++ mptcp_rsk(req)->collide_tk.pprev = NULL; ++ } else ++#endif ++ req = inet_reqsk_alloc(&tcp_request_sock_ops); ++ + if (!req) + goto drop; + +@@ -1494,17 +1562,15 @@ + tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops; + #endif + +- tcp_clear_options(&tmp_opt); +- tmp_opt.mss_clamp = TCP_MSS_DEFAULT; +- tmp_opt.user_mss = tp->rx_opt.user_mss; +- tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc); +- + if (want_cookie && !tmp_opt.saw_tstamp) + tcp_clear_options(&tmp_opt); + + tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; + tcp_openreq_init(req, &tmp_opt, skb); + ++ if (mopt.saw_mpc && !want_cookie) ++ mptcp_reqsk_new_mptcp(req, &tmp_opt, &mopt, skb); ++ + ireq = inet_rsk(req); + ireq->ir_loc_addr = daddr; + ireq->ir_rmt_addr = saddr; +@@ -1716,7 +1782,7 @@ + } + EXPORT_SYMBOL(tcp_v4_syn_recv_sock); + +-static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) ++struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) + { + struct tcphdr *th = tcp_hdr(skb); + const struct iphdr *iph = ip_hdr(skb); +@@ -1733,8 +1799,15 @@ + + if (nsk) { + if (nsk->sk_state != TCP_TIME_WAIT) { ++ /* Don't lock again the meta-sk. It has been locked ++ * before mptcp_v4_do_rcv. ++ */ ++ if (tcp_sk(nsk)->mpc && !is_meta_sk(sk)) ++ bh_lock_sock(mptcp_meta_sk(nsk)); + bh_lock_sock(nsk); ++ + return nsk; ++ + } + inet_twsk_put(inet_twsk(nsk)); + return NULL; +@@ -1791,6 +1864,9 @@ + goto discard; + #endif + ++ if (is_meta_sk(sk)) ++ return mptcp_v4_do_rcv(sk, skb); ++ + if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */ + struct dst_entry *dst = sk->sk_rx_dst; + +@@ -1922,7 +1998,7 @@ + } else if (skb_queue_len(&tp->ucopy.prequeue) == 1) { + wake_up_interruptible_sync_poll(sk_sleep(sk), + POLLIN | POLLRDNORM | POLLRDBAND); +- if (!inet_csk_ack_scheduled(sk)) ++ if (!inet_csk_ack_scheduled(sk) && !tp->mpc) + inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, + (3 * tcp_rto_min(sk)) / 4, + TCP_RTO_MAX); +@@ -1939,7 +2015,7 @@ + { + const struct iphdr *iph; + const struct tcphdr *th; +- struct sock *sk; ++ struct sock *sk, *meta_sk = NULL; + int ret; + struct net *net = dev_net(skb->dev); + +@@ -1972,18 +2048,42 @@ + TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + + skb->len - th->doff * 4); + TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); ++#ifdef CONFIG_MPTCP ++ TCP_SKB_CB(skb)->mptcp_flags = 0; ++ TCP_SKB_CB(skb)->dss_off = 0; ++#endif + TCP_SKB_CB(skb)->when = 0; + TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph); + TCP_SKB_CB(skb)->sacked = 0; + + sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest); +- if (!sk) +- goto no_tcp_socket; + + process: +- if (sk->sk_state == TCP_TIME_WAIT) ++ if (sk && sk->sk_state == TCP_TIME_WAIT) + goto do_time_wait; + ++#ifdef CONFIG_MPTCP ++ if (!sk && th->syn && !th->ack) { ++ int ret = mptcp_lookup_join(skb, NULL); ++ ++ if (ret < 0) { ++ tcp_v4_send_reset(NULL, skb); ++ goto discard_it; ++ } else if (ret > 0) { ++ return 0; ++ } ++ } ++ ++ /* Is there a pending request sock for this segment ? */ ++ if ((!sk || sk->sk_state == TCP_LISTEN) && mptcp_check_req(skb, net)) { ++ if (sk) ++ sock_put(sk); ++ return 0; ++ } ++#endif ++ if (!sk) ++ goto no_tcp_socket; ++ + if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) { + NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP); + goto discard_and_relse; +@@ -1999,11 +2099,21 @@ + sk_mark_napi_id(sk, skb); + skb->dev = NULL; + +- bh_lock_sock_nested(sk); ++ if (tcp_sk(sk)->mpc) { ++ meta_sk = mptcp_meta_sk(sk); ++ ++ bh_lock_sock_nested(meta_sk); ++ if (sock_owned_by_user(meta_sk)) ++ skb->sk = sk; ++ } else { ++ meta_sk = sk; ++ bh_lock_sock_nested(sk); ++ } ++ + ret = 0; +- if (!sock_owned_by_user(sk)) { ++ if (!sock_owned_by_user(meta_sk)) { + #ifdef CONFIG_NET_DMA +- struct tcp_sock *tp = tcp_sk(sk); ++ struct tcp_sock *tp = tcp_sk(meta_sk); + if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) + tp->ucopy.dma_chan = net_dma_find_channel(); + if (tp->ucopy.dma_chan) +@@ -2011,16 +2121,16 @@ + else + #endif + { +- if (!tcp_prequeue(sk, skb)) ++ if (!tcp_prequeue(meta_sk, skb)) + ret = tcp_v4_do_rcv(sk, skb); + } +- } else if (unlikely(sk_add_backlog(sk, skb, +- sk->sk_rcvbuf + sk->sk_sndbuf))) { +- bh_unlock_sock(sk); ++ } else if (unlikely(sk_add_backlog(meta_sk, skb, ++ meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { ++ bh_unlock_sock(meta_sk); + NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); + goto discard_and_relse; + } +- bh_unlock_sock(sk); ++ bh_unlock_sock(meta_sk); + + sock_put(sk); + +@@ -2075,6 +2185,18 @@ + sk = sk2; + goto process; + } ++#ifdef CONFIG_MPTCP ++ if (th->syn && !th->ack) { ++ int ret = mptcp_lookup_join(skb, inet_twsk(sk)); ++ ++ if (ret < 0) { ++ tcp_v4_send_reset(NULL, skb); ++ goto discard_it; ++ } else if (ret > 0) { ++ return 0; ++ } ++ } ++#endif + /* Fall through to ACK */ + } + case TCP_TW_ACK: +@@ -2158,6 +2280,11 @@ + + tcp_cleanup_congestion_control(sk); + ++ if (tp->mpc) ++ mptcp_destroy_sock(sk); ++ if (tp->inside_tk_table) ++ mptcp_hash_remove(tp); ++ + /* Cleanup up the write buffer. */ + tcp_write_queue_purge(sk); + +diff -Nur linux-3.14.45.orig/net/ipv4/tcp_minisocks.c linux-3.14.45/net/ipv4/tcp_minisocks.c +--- linux-3.14.45.orig/net/ipv4/tcp_minisocks.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv4/tcp_minisocks.c 2015-06-24 14:15:48.887862480 +0200 +@@ -18,11 +18,13 @@ + * Jorge Cwik, + */ + ++#include + #include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -95,10 +97,13 @@ + struct tcp_options_received tmp_opt; + struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); + bool paws_reject = false; ++ struct mptcp_options_received mopt; + + tmp_opt.saw_tstamp = 0; + if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) { +- tcp_parse_options(skb, &tmp_opt, 0, NULL); ++ mptcp_init_mp_opt(&mopt); ++ ++ tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL); + + if (tmp_opt.saw_tstamp) { + tmp_opt.rcv_tsecr -= tcptw->tw_ts_offset; +@@ -106,6 +111,11 @@ + tmp_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp; + paws_reject = tcp_paws_reject(&tmp_opt, th->rst); + } ++ ++ if (unlikely(mopt.mp_fclose) && tcptw->mptcp_tw) { ++ if (mopt.mptcp_key == tcptw->mptcp_tw->loc_key) ++ goto kill_with_rst; ++ } + } + + if (tw->tw_substate == TCP_FIN_WAIT2) { +@@ -128,6 +138,16 @@ + if (!th->ack || + !after(TCP_SKB_CB(skb)->end_seq, tcptw->tw_rcv_nxt) || + TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq) { ++ /* If mptcp_is_data_fin() returns true, we are sure that ++ * mopt has been initialized - otherwise it would not ++ * be a DATA_FIN. ++ */ ++ if (tcptw->mptcp_tw && tcptw->mptcp_tw->meta_tw && ++ mptcp_is_data_fin(skb) && ++ TCP_SKB_CB(skb)->seq == tcptw->tw_rcv_nxt && ++ mopt.data_seq + 1 == (u32)tcptw->mptcp_tw->rcv_nxt) ++ return TCP_TW_ACK; ++ + inet_twsk_put(tw); + return TCP_TW_SUCCESS; + } +@@ -270,6 +290,11 @@ + const struct tcp_sock *tp = tcp_sk(sk); + bool recycle_ok = false; + ++ if (is_meta_sk(sk)) { ++ mptcp_update_tw_socks(tp, state); ++ goto tcp_done; ++ } ++ + if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp) + recycle_ok = tcp_remember_stamp(sk); + +@@ -290,6 +315,15 @@ + tcptw->tw_ts_recent_stamp = tp->rx_opt.ts_recent_stamp; + tcptw->tw_ts_offset = tp->tsoffset; + ++ if (tp->mpc) { ++ if (mptcp_time_wait(sk, tcptw)) { ++ inet_twsk_free(tw); ++ goto exit; ++ } ++ } else { ++ tcptw->mptcp_tw = NULL; ++ } ++ + #if IS_ENABLED(CONFIG_IPV6) + if (tw->tw_family == PF_INET6) { + struct ipv6_pinfo *np = inet6_sk(sk); +@@ -347,15 +381,19 @@ + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPTIMEWAITOVERFLOW); + } + ++exit: + tcp_update_metrics(sk); ++tcp_done: + tcp_done(sk); + } + + void tcp_twsk_destructor(struct sock *sk) + { +-#ifdef CONFIG_TCP_MD5SIG + struct tcp_timewait_sock *twsk = tcp_twsk(sk); + ++ if (twsk->mptcp_tw) ++ mptcp_twsk_destructor(twsk); ++#ifdef CONFIG_TCP_MD5SIG + if (twsk->tw_md5_key) + kfree_rcu(twsk->tw_md5_key, rcu); + #endif +@@ -392,6 +430,9 @@ + + newtp->snd_sml = newtp->snd_una = + newtp->snd_nxt = newtp->snd_up = treq->snt_isn + 1; ++#ifdef CONFIG_MPTCP ++ memset(&newtp->rcvq_space, 0, sizeof(newtp->rcvq_space)); ++#endif + + tcp_prequeue_init(newtp); + INIT_LIST_HEAD(&newtp->tsq_node); +@@ -436,7 +477,11 @@ + + newtp->urg_data = 0; + +- if (sock_flag(newsk, SOCK_KEEPOPEN)) ++ /* MPTCP: If we are creating a subflow, KEEPOPEN might have been ++ * set on the meta. But, keepalive is entirely handled at the ++ * meta-socket, so let's keep it there. ++ */ ++ if (sock_flag(newsk, SOCK_KEEPOPEN) && is_meta_sk(sk)) + inet_csk_reset_keepalive_timer(newsk, + keepalive_time_when(newtp)); + +@@ -468,6 +513,8 @@ + newtp->rx_opt.ts_recent_stamp = 0; + newtp->tcp_header_len = sizeof(struct tcphdr); + } ++ if (treq->saw_mpc) ++ newtp->tcp_header_len += MPTCP_SUB_LEN_DSM_ALIGN; + newtp->tsoffset = 0; + #ifdef CONFIG_TCP_MD5SIG + newtp->md5sig_info = NULL; /*XXX*/ +@@ -504,16 +551,20 @@ + bool fastopen) + { + struct tcp_options_received tmp_opt; ++ struct mptcp_options_received mopt; + struct sock *child; + const struct tcphdr *th = tcp_hdr(skb); + __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK); + bool paws_reject = false; + +- BUG_ON(fastopen == (sk->sk_state == TCP_LISTEN)); ++ BUG_ON(!tcp_sk(sk)->mpc && fastopen == (sk->sk_state == TCP_LISTEN)); + + tmp_opt.saw_tstamp = 0; ++ ++ mptcp_init_mp_opt(&mopt); ++ + if (th->doff > (sizeof(struct tcphdr)>>2)) { +- tcp_parse_options(skb, &tmp_opt, 0, NULL); ++ tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL); + + if (tmp_opt.saw_tstamp) { + tmp_opt.ts_recent = req->ts_recent; +@@ -552,7 +603,14 @@ + * + * Reset timer after retransmitting SYNACK, similar to + * the idea of fast retransmit in recovery. ++ * ++ * Fall back to TCP if MP_CAPABLE is not set. + */ ++ ++ if (tcp_rsk(req)->saw_mpc && !mopt.saw_mpc) ++ tcp_rsk(req)->saw_mpc = false; ++ ++ + if (!inet_rtx_syn_ack(sk, req)) + req->expires = min(TCP_TIMEOUT_INIT << req->num_timeout, + TCP_RTO_MAX) + jiffies; +@@ -674,7 +732,20 @@ + + /* While TCP_DEFER_ACCEPT is active, drop bare ACK. */ + if (req->num_timeout < inet_csk(sk)->icsk_accept_queue.rskq_defer_accept && +- TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1) { ++ TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1 && ++ /* TODO MPTCP: ++ * We do this here, because otherwise options sent in the third ack, ++ * or duplicate fourth ack will get lost. Options like MP_PRIO, ADD_ADDR,... ++ * ++ * We could store them in request_sock, but this would mean that we ++ * have to put tcp_options_received and mptcp_options_received in there, ++ * increasing considerably the size of the request-sock. ++ * ++ * As soon as we have reworked the request-sock MPTCP-fields and ++ * created a mptcp_request_sock structure, we can handle options ++ * correclty there without increasing request_sock. ++ */ ++ !tcp_rsk(req)->saw_mpc) { + inet_rsk(req)->acked = 1; + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDEFERACCEPTDROP); + return NULL; +@@ -686,10 +757,29 @@ + * ESTABLISHED STATE. If it will be dropped after + * socket is created, wait for troubles. + */ +- child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL); ++#ifdef CONFIG_MPTCP ++ if (tcp_sk(sk)->mpc) ++ /* MPTCP: We call the mptcp-specific syn_recv_sock */ ++ child = tcp_sk(sk)->mpcb->syn_recv_sock(sk, skb, req, NULL); ++ else ++#endif ++ child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, ++ req, NULL); ++ + if (child == NULL) + goto listen_overflow; + ++ if (!is_meta_sk(sk)) { ++ int ret = mptcp_check_req_master(sk, child, req, prev, &mopt); ++ if (ret < 0) ++ goto listen_overflow; ++ ++ /* MPTCP-supported */ ++ if (!ret) ++ return tcp_sk(child)->mpcb->master_sk; ++ } else { ++ return mptcp_check_req_child(sk, child, req, prev, &mopt); ++ } + inet_csk_reqsk_queue_unlink(sk, req, prev); + inet_csk_reqsk_queue_removed(sk, req); + +@@ -739,8 +829,9 @@ + { + int ret = 0; + int state = child->sk_state; ++ struct sock *meta_sk = tcp_sk(child)->mpc ? mptcp_meta_sk(child) : child; + +- if (!sock_owned_by_user(child)) { ++ if (!sock_owned_by_user(meta_sk)) { + ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), + skb->len); + /* Wakeup parent, send SIGIO */ +@@ -751,10 +842,14 @@ + * in main socket hash table and lock on listening + * socket does not protect us more. + */ +- __sk_add_backlog(child, skb); ++ if (tcp_sk(child)->mpc) ++ skb->sk = child; ++ __sk_add_backlog(meta_sk, skb); + } + +- bh_unlock_sock(child); ++ if (tcp_sk(child)->mpc) ++ bh_unlock_sock(child); ++ bh_unlock_sock(meta_sk); + sock_put(child); + return ret; + } +diff -Nur linux-3.14.45.orig/net/ipv4/tcp_output.c linux-3.14.45/net/ipv4/tcp_output.c +--- linux-3.14.45.orig/net/ipv4/tcp_output.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv4/tcp_output.c 2015-06-24 14:15:48.887862480 +0200 +@@ -36,6 +36,12 @@ + + #define pr_fmt(fmt) "TCP: " fmt + ++#include ++#include ++#if IS_ENABLED(CONFIG_IPV6) ++#include ++#endif ++#include + #include + + #include +@@ -72,7 +78,7 @@ + int push_one, gfp_t gfp); + + /* Account for new data that has been sent to the network. */ +-static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) ++void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) + { + struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); +@@ -211,7 +217,7 @@ + void tcp_select_initial_window(int __space, __u32 mss, + __u32 *rcv_wnd, __u32 *window_clamp, + int wscale_ok, __u8 *rcv_wscale, +- __u32 init_rcv_wnd) ++ __u32 init_rcv_wnd, const struct sock *sk) + { + unsigned int space = (__space < 0 ? 0 : __space); + +@@ -266,11 +272,15 @@ + * value can be stuffed directly into th->window for an outgoing + * frame. + */ +-static u16 tcp_select_window(struct sock *sk) ++u16 tcp_select_window(struct sock *sk) + { + struct tcp_sock *tp = tcp_sk(sk); +- u32 cur_win = tcp_receive_window(tp); +- u32 new_win = __tcp_select_window(sk); ++ /* The window must never shrink at the meta-level. At the subflow we ++ * have to allow this. Otherwise we may announce a window too large ++ * for the current meta-level sk_rcvbuf. ++ */ ++ u32 cur_win = tcp_receive_window(tp->mpc ? tcp_sk(mptcp_meta_sk(sk)) : tp); ++ u32 new_win = tp->__select_window(sk); + + /* Never shrink the offered window */ + if (new_win < cur_win) { +@@ -283,6 +293,7 @@ + */ + new_win = ALIGN(cur_win, 1 << tp->rx_opt.rcv_wscale); + } ++ + tp->rcv_wnd = new_win; + tp->rcv_wup = tp->rcv_nxt; + +@@ -361,7 +372,7 @@ + /* Constructs common control bits of non-data skb. If SYN/FIN is present, + * auto increment end seqno. + */ +-static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags) ++void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags) + { + struct skb_shared_info *shinfo = skb_shinfo(skb); + +@@ -381,7 +392,7 @@ + TCP_SKB_CB(skb)->end_seq = seq; + } + +-static inline bool tcp_urg_mode(const struct tcp_sock *tp) ++bool tcp_urg_mode(const struct tcp_sock *tp) + { + return tp->snd_una != tp->snd_up; + } +@@ -391,17 +402,7 @@ + #define OPTION_MD5 (1 << 2) + #define OPTION_WSCALE (1 << 3) + #define OPTION_FAST_OPEN_COOKIE (1 << 8) +- +-struct tcp_out_options { +- u16 options; /* bit field of OPTION_* */ +- u16 mss; /* 0 to disable */ +- u8 ws; /* window scale, 0 to disable */ +- u8 num_sack_blocks; /* number of SACK blocks to include */ +- u8 hash_size; /* bytes in hash_location */ +- __u8 *hash_location; /* temporary pointer, overloaded */ +- __u32 tsval, tsecr; /* need to include OPTION_TS */ +- struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie */ +-}; ++/* Before adding here - take a look at OPTION_MPTCP in include/net/mptcp.h */ + + /* Write previously computed TCP options to the packet. + * +@@ -417,7 +418,7 @@ + * (but it may well be that other scenarios fail similarly). + */ + static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, +- struct tcp_out_options *opts) ++ struct tcp_out_options *opts, struct sk_buff *skb) + { + u16 options = opts->options; /* mungable copy */ + +@@ -500,6 +501,9 @@ + } + ptr += (foc->len + 3) >> 2; + } ++ ++ if (unlikely(OPTION_MPTCP & opts->options)) ++ mptcp_options_write(ptr, tp, opts, skb); + } + + /* Compute TCP options for SYN packets. This is not the final +@@ -551,6 +555,8 @@ + if (unlikely(!(OPTION_TS & opts->options))) + remaining -= TCPOLEN_SACKPERM_ALIGNED; + } ++ if (tp->request_mptcp || tp->mpc) ++ mptcp_syn_options(sk, opts, &remaining); + + if (fastopen && fastopen->cookie.len >= 0) { + u32 need = TCPOLEN_EXP_FASTOPEN_BASE + fastopen->cookie.len; +@@ -624,6 +630,9 @@ + } + } + ++ if (tcp_rsk(req)->saw_mpc) ++ mptcp_synack_options(req, opts, &remaining); ++ + return MAX_TCP_OPTION_SPACE - remaining; + } + +@@ -657,16 +666,22 @@ + opts->tsecr = tp->rx_opt.ts_recent; + size += TCPOLEN_TSTAMP_ALIGNED; + } ++ if (tp->mpc) ++ mptcp_established_options(sk, skb, opts, &size); + + eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; + if (unlikely(eff_sacks)) { +- const unsigned int remaining = MAX_TCP_OPTION_SPACE - size; +- opts->num_sack_blocks = +- min_t(unsigned int, eff_sacks, +- (remaining - TCPOLEN_SACK_BASE_ALIGNED) / +- TCPOLEN_SACK_PERBLOCK); +- size += TCPOLEN_SACK_BASE_ALIGNED + +- opts->num_sack_blocks * TCPOLEN_SACK_PERBLOCK; ++ const unsigned remaining = MAX_TCP_OPTION_SPACE - size; ++ if (remaining < TCPOLEN_SACK_BASE_ALIGNED) ++ opts->num_sack_blocks = 0; ++ else ++ opts->num_sack_blocks = ++ min_t(unsigned int, eff_sacks, ++ (remaining - TCPOLEN_SACK_BASE_ALIGNED) / ++ TCPOLEN_SACK_PERBLOCK); ++ if (opts->num_sack_blocks) ++ size += TCPOLEN_SACK_BASE_ALIGNED + ++ opts->num_sack_blocks * TCPOLEN_SACK_PERBLOCK; + } + + return size; +@@ -714,7 +729,7 @@ + unsigned long flags; + struct list_head *q, *n; + struct tcp_sock *tp; +- struct sock *sk; ++ struct sock *sk, *meta_sk; + + local_irq_save(flags); + list_splice_init(&tsq->head, &list); +@@ -725,15 +740,27 @@ + list_del(&tp->tsq_node); + + sk = (struct sock *)tp; +- bh_lock_sock(sk); ++ meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; ++ bh_lock_sock(meta_sk); + +- if (!sock_owned_by_user(sk)) { ++ if (!sock_owned_by_user(meta_sk)) { + tcp_tsq_handler(sk); ++ if (tp->mpc) ++ tcp_tsq_handler(meta_sk); + } else { + /* defer the work to tcp_release_cb() */ + set_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); ++ ++ /* For MPTCP, we set the tsq-bit on the meta, and the ++ * subflow as we don't know if the limitation happened ++ * while inside mptcp_write_xmit or during tcp_write_xmit. ++ */ ++ if (tp->mpc) { ++ set_bit(TCP_TSQ_DEFERRED, &tcp_sk(meta_sk)->tsq_flags); ++ mptcp_tsq_flags(sk); ++ } + } +- bh_unlock_sock(sk); ++ bh_unlock_sock(meta_sk); + + clear_bit(TSQ_QUEUED, &tp->tsq_flags); + sk_free(sk); +@@ -743,7 +770,10 @@ + #define TCP_DEFERRED_ALL ((1UL << TCP_TSQ_DEFERRED) | \ + (1UL << TCP_WRITE_TIMER_DEFERRED) | \ + (1UL << TCP_DELACK_TIMER_DEFERRED) | \ +- (1UL << TCP_MTU_REDUCED_DEFERRED)) ++ (1UL << TCP_MTU_REDUCED_DEFERRED) | \ ++ (1UL << MPTCP_PATH_MANAGER) | \ ++ (1UL << MPTCP_SUB_DEFERRED)) ++ + /** + * tcp_release_cb - tcp release_sock() callback + * @sk: socket +@@ -790,6 +820,13 @@ + inet_csk(sk)->icsk_af_ops->mtu_reduced(sk); + __sock_put(sk); + } ++ if (flags & (1UL << MPTCP_PATH_MANAGER)) { ++ if (tcp_sk(sk)->mpcb->pm_ops->release_sock) ++ tcp_sk(sk)->mpcb->pm_ops->release_sock(sk); ++ __sock_put(sk); ++ } ++ if (flags & (1UL << MPTCP_SUB_DEFERRED)) ++ mptcp_tsq_sub_deferred(sk); + } + EXPORT_SYMBOL(tcp_release_cb); + +@@ -849,8 +886,8 @@ + * We are working here with either a clone of the original + * SKB, or a fresh unique copy made by the retransmit engine. + */ +-static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, +- gfp_t gfp_mask) ++int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, ++ gfp_t gfp_mask) + { + const struct inet_connection_sock *icsk = inet_csk(sk); + struct inet_sock *inet; +@@ -878,10 +915,28 @@ + NET_INC_STATS(sock_net(sk), + LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES); + +- if (unlikely(skb_cloned(skb))) +- skb = pskb_copy(skb, gfp_mask); +- else ++ if (unlikely(skb_cloned(skb))) { ++ struct sk_buff *newskb; ++ if (mptcp_is_data_seq(skb)) ++ skb_push(skb, MPTCP_SUB_LEN_DSS_ALIGN + ++ MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN); ++ ++ newskb = pskb_copy(skb, gfp_mask); ++ ++ if (mptcp_is_data_seq(skb)) { ++ skb_pull(skb, MPTCP_SUB_LEN_DSS_ALIGN + ++ MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN); ++ if (newskb) ++ skb_pull(newskb, MPTCP_SUB_LEN_DSS_ALIGN + ++ MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN); ++ } ++ skb = newskb; ++ } else { + skb = skb_clone(skb, gfp_mask); ++ } + if (unlikely(!skb)) + return -ENOBUFS; + } +@@ -929,7 +984,7 @@ + */ + th->window = htons(min(tp->rcv_wnd, 65535U)); + } else { +- th->window = htons(tcp_select_window(sk)); ++ th->window = htons(tp->select_window(sk)); + } + th->check = 0; + th->urg_ptr = 0; +@@ -945,7 +1000,7 @@ + } + } + +- tcp_options_write((__be32 *)(th + 1), tp, &opts); ++ tcp_options_write((__be32 *)(th + 1), tp, &opts, skb); + if (likely((tcb->tcp_flags & TCPHDR_SYN) == 0)) + TCP_ECN_send(sk, skb, tcp_header_size); + +@@ -984,7 +1039,7 @@ + * NOTE: probe0 timer is not checked, do not forget tcp_push_pending_frames, + * otherwise socket can stall. + */ +-static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) ++void tcp_queue_skb(struct sock *sk, struct sk_buff *skb) + { + struct tcp_sock *tp = tcp_sk(sk); + +@@ -997,15 +1052,16 @@ + } + + /* Initialize TSO segments for a packet. */ +-static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, +- unsigned int mss_now) ++void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, ++ unsigned int mss_now) + { + struct skb_shared_info *shinfo = skb_shinfo(skb); + + /* Make sure we own this skb before messing gso_size/gso_segs */ + WARN_ON_ONCE(skb_cloned(skb)); + +- if (skb->len <= mss_now || skb->ip_summed == CHECKSUM_NONE) { ++ if (skb->len <= mss_now || (is_meta_sk(sk) && !mptcp_sk_can_gso(sk)) || ++ (!is_meta_sk(sk) && !sk_can_gso(sk)) || skb->ip_summed == CHECKSUM_NONE) { + /* Avoid the costly divide in the normal + * non-TSO case. + */ +@@ -1037,7 +1093,7 @@ + /* Pcount in the middle of the write queue got changed, we need to do various + * tweaks to fix counters + */ +-static void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int decr) ++void tcp_adjust_pcount(struct sock *sk, const struct sk_buff *skb, int decr) + { + struct tcp_sock *tp = tcp_sk(sk); + +@@ -1078,6 +1134,9 @@ + int nlen; + u8 flags; + ++ if (tcp_sk(sk)->mpc && mptcp_is_data_seq(skb)) ++ mptcp_fragment(sk, skb, len, mss_now, 0); ++ + if (WARN_ON(len > skb->len)) + return -EINVAL; + +@@ -1160,7 +1219,7 @@ + * eventually). The difference is that pulled data not copied, but + * immediately discarded. + */ +-static void __pskb_trim_head(struct sk_buff *skb, int len) ++void __pskb_trim_head(struct sk_buff *skb, int len) + { + struct skb_shared_info *shinfo; + int i, k, eat; +@@ -1201,6 +1260,9 @@ + /* Remove acked data from a packet in the transmit queue. */ + int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) + { ++ if (tcp_sk(sk)->mpc && !is_meta_sk(sk) && mptcp_is_data_seq(skb)) ++ return mptcp_trim_head(sk, skb, len); ++ + if (skb_unclone(skb, GFP_ATOMIC)) + return -ENOMEM; + +@@ -1218,6 +1280,15 @@ + if (tcp_skb_pcount(skb) > 1) + tcp_set_skb_tso_segs(sk, skb, tcp_skb_mss(skb)); + ++#ifdef CONFIG_MPTCP ++ /* Some data got acked - we assume that the seq-number reached the dest. ++ * Anyway, our MPTCP-option has been trimmed above - we lost it here. ++ * Only remove the SEQ if the call does not come from a meta retransmit. ++ */ ++ if (tcp_sk(sk)->mpc && !is_meta_sk(sk)) ++ TCP_SKB_CB(skb)->mptcp_flags &= ~MPTCPHDR_SEQ; ++#endif ++ + return 0; + } + +@@ -1377,7 +1448,7 @@ + } + + /* Congestion window validation. (RFC2861) */ +-static void tcp_cwnd_validate(struct sock *sk) ++void tcp_cwnd_validate(struct sock *sk) + { + struct tcp_sock *tp = tcp_sk(sk); + +@@ -1411,8 +1482,8 @@ + * But we can avoid doing the divide again given we already have + * skb_pcount = skb->len / mss_now + */ +-static void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now, +- const struct sk_buff *skb) ++void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now, ++ const struct sk_buff *skb) + { + if (skb->len < tcp_skb_pcount(skb) * mss_now) + tp->snd_sml = TCP_SKB_CB(skb)->end_seq; +@@ -1433,19 +1504,28 @@ + (!nonagle && tp->packets_out && tcp_minshall_check(tp))); + } + /* Returns the portion of skb which can be sent right away */ +-static unsigned int tcp_mss_split_point(const struct sock *sk, +- const struct sk_buff *skb, +- unsigned int mss_now, +- unsigned int max_segs, +- int nonagle) ++unsigned int tcp_mss_split_point(const struct sock *sk, ++ const struct sk_buff *skb, ++ unsigned int mss_now, ++ unsigned int max_segs, ++ int nonagle) + { + const struct tcp_sock *tp = tcp_sk(sk); ++ const struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; + u32 partial, needed, window, max_len; + +- window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; ++ if (!tp->mpc) ++ window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; ++ else ++ /* We need to evaluate the available space in the sending window ++ * at the subflow level. However, the subflow seq has not yet ++ * been set. Nevertheless we know that the caller will set it to ++ * write_seq. ++ */ ++ window = tcp_wnd_end(tp) - tp->write_seq; + max_len = mss_now * max_segs; + +- if (likely(max_len <= window && skb != tcp_write_queue_tail(sk))) ++ if (likely(max_len <= window && skb != tcp_write_queue_tail(meta_sk))) + return max_len; + + needed = min(skb->len, window); +@@ -1467,13 +1547,14 @@ + /* Can at least one segment of SKB be sent right now, according to the + * congestion window rules? If so, return how many segments are allowed. + */ +-static inline unsigned int tcp_cwnd_test(const struct tcp_sock *tp, +- const struct sk_buff *skb) ++unsigned int tcp_cwnd_test(const struct tcp_sock *tp, ++ const struct sk_buff *skb) + { + u32 in_flight, cwnd; + + /* Don't be strict about the congestion window for the final FIN. */ +- if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) && ++ if (skb && ++ ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) || mptcp_is_data_fin(skb)) && + tcp_skb_pcount(skb) == 1) + return 1; + +@@ -1489,8 +1570,8 @@ + * This must be invoked the first time we consider transmitting + * SKB onto the wire. + */ +-static int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, +- unsigned int mss_now) ++int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb, ++ unsigned int mss_now) + { + int tso_segs = tcp_skb_pcount(skb); + +@@ -1505,8 +1586,8 @@ + /* Return true if the Nagle test allows this packet to be + * sent now. + */ +-static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buff *skb, +- unsigned int cur_mss, int nonagle) ++bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buff *skb, ++ unsigned int cur_mss, int nonagle) + { + /* Nagle rule does not apply to frames, which sit in the middle of the + * write_queue (they have no chances to get new data). +@@ -1518,7 +1599,8 @@ + return true; + + /* Don't use the nagle rule for urgent data (or for the final FIN). */ +- if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)) ++ if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) || ++ mptcp_is_data_fin(skb)) + return true; + + if (!tcp_nagle_check(skb->len < cur_mss, tp, cur_mss, nonagle)) +@@ -1528,9 +1610,8 @@ + } + + /* Does at least the first segment of SKB fit into the send window? */ +-static bool tcp_snd_wnd_test(const struct tcp_sock *tp, +- const struct sk_buff *skb, +- unsigned int cur_mss) ++bool tcp_snd_wnd_test(const struct tcp_sock *tp, const struct sk_buff *skb, ++ unsigned int cur_mss) + { + u32 end_seq = TCP_SKB_CB(skb)->end_seq; + +@@ -1549,14 +1630,16 @@ + { + const struct tcp_sock *tp = tcp_sk(sk); + unsigned int cwnd_quota; ++ const struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; ++ const struct tcp_sock *meta_tp = tcp_sk(meta_sk); + +- tcp_init_tso_segs(sk, skb, cur_mss); ++ tcp_init_tso_segs(meta_sk, skb, cur_mss); + +- if (!tcp_nagle_test(tp, skb, cur_mss, nonagle)) ++ if (!tcp_nagle_test(meta_tp, skb, cur_mss, nonagle)) + return 0; + + cwnd_quota = tcp_cwnd_test(tp, skb); +- if (cwnd_quota && !tcp_snd_wnd_test(tp, skb, cur_mss)) ++ if (cwnd_quota && !tcp_snd_wnd_test(meta_tp, skb, cur_mss)) + cwnd_quota = 0; + + return cwnd_quota; +@@ -1566,12 +1649,16 @@ + bool tcp_may_send_now(struct sock *sk) + { + const struct tcp_sock *tp = tcp_sk(sk); +- struct sk_buff *skb = tcp_send_head(sk); ++ struct sk_buff *skb; ++ const struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; ++ const struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ ++ skb = tcp_send_head(meta_sk); + + return skb && + tcp_snd_test(sk, skb, tcp_current_mss(sk), +- (tcp_skb_is_last(sk, skb) ? +- tp->nonagle : TCP_NAGLE_PUSH)); ++ (tcp_skb_is_last(meta_sk, skb) ? ++ meta_tp->nonagle : TCP_NAGLE_PUSH)); + } + + /* Trim TSO SKB to LEN bytes, put the remaining data into a new packet +@@ -1588,6 +1675,9 @@ + int nlen = skb->len - len; + u8 flags; + ++ if (tcp_sk(sk)->mpc && mptcp_is_data_seq(skb)) ++ mptso_fragment(sk, skb, len, mss_now, gfp, 0); ++ + /* All of a TSO frame must be composed of paged data. */ + if (skb->len != skb->data_len) + return tcp_fragment(sk, skb, len, mss_now); +@@ -1633,29 +1723,39 @@ + * + * This algorithm is from John Heffner. + */ +-static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) ++bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb) + { + struct tcp_sock *tp = tcp_sk(sk); ++ struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); + const struct inet_connection_sock *icsk = inet_csk(sk); + u32 send_win, cong_win, limit, in_flight; + int win_divisor; + +- if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) ++ if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) || mptcp_is_data_fin(skb)) + goto send_now; + + if (icsk->icsk_ca_state != TCP_CA_Open) + goto send_now; + + /* Defer for less than two clock ticks. */ +- if (tp->tso_deferred && +- (((u32)jiffies << 1) >> 1) - (tp->tso_deferred >> 1) > 1) ++ if (meta_tp->tso_deferred && ++ (((u32)jiffies << 1) >> 1) - (meta_tp->tso_deferred >> 1) > 1) + goto send_now; + + in_flight = tcp_packets_in_flight(tp); + + BUG_ON(tcp_skb_pcount(skb) <= 1 || (tp->snd_cwnd <= in_flight)); + +- send_win = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; ++ if (!tp->mpc) ++ send_win = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq; ++ else ++ /* We need to evaluate the available space in the sending window ++ * at the subflow level. However, the subflow seq has not yet ++ * been set. Nevertheless we know that the caller will set it to ++ * write_seq. ++ */ ++ send_win = tcp_wnd_end(tp) - tp->write_seq; + + /* From in_flight test above, we know that cwnd > in_flight. */ + cong_win = (tp->snd_cwnd - in_flight) * tp->mss_cache; +@@ -1668,7 +1768,7 @@ + goto send_now; + + /* Middle in queue won't get any more data, full sendable already? */ +- if ((skb != tcp_write_queue_tail(sk)) && (limit >= skb->len)) ++ if ((skb != tcp_write_queue_tail(meta_sk)) && (limit >= skb->len)) + goto send_now; + + win_divisor = ACCESS_ONCE(sysctl_tcp_tso_win_divisor); +@@ -1694,13 +1794,13 @@ + /* Ok, it looks like it is advisable to defer. + * Do not rearm the timer if already set to not break TCP ACK clocking. + */ +- if (!tp->tso_deferred) +- tp->tso_deferred = 1 | (jiffies << 1); ++ if (!meta_tp->tso_deferred) ++ meta_tp->tso_deferred = 1 | (jiffies << 1); + + return true; + + send_now: +- tp->tso_deferred = 0; ++ meta_tp->tso_deferred = 0; + return false; + } + +@@ -1713,7 +1813,7 @@ + * 1 if a probe was sent, + * -1 otherwise + */ +-static int tcp_mtu_probe(struct sock *sk) ++int tcp_mtu_probe(struct sock *sk) + { + struct tcp_sock *tp = tcp_sk(sk); + struct inet_connection_sock *icsk = inet_csk(sk); +@@ -1858,6 +1958,9 @@ + int cwnd_quota; + int result; + ++ if (is_meta_sk(sk)) ++ return mptcp_write_xmit(sk, mss_now, nonagle, push_one, gfp); ++ + sent_pkts = 0; + + if (!push_one) { +@@ -2314,6 +2417,10 @@ + if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN) + return; + ++ /* Currently not supported for MPTCP - but it should be possible */ ++ if (tp->mpc) ++ return; ++ + tcp_for_write_queue_from_safe(skb, tmp, sk) { + if (!tcp_can_collapse(sk, skb)) + break; +@@ -2411,10 +2518,26 @@ + */ + if (unlikely((NET_IP_ALIGN && ((unsigned long)skb->data & 3)) || + skb_headroom(skb) >= 0xFFFF)) { +- struct sk_buff *nskb = __pskb_copy(skb, MAX_TCP_HEADER, +- GFP_ATOMIC); ++ struct sk_buff *nskb; ++ ++ if (mptcp_is_data_seq(skb)) ++ skb_push(skb, MPTCP_SUB_LEN_DSS_ALIGN + ++ MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN); ++ ++ nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC); ++ ++ if (mptcp_is_data_seq(skb)) { ++ skb_pull(skb, MPTCP_SUB_LEN_DSS_ALIGN + ++ MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN); ++ if (nskb) ++ skb_pull(nskb, MPTCP_SUB_LEN_DSS_ALIGN + ++ MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN); ++ } + err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) : +- -ENOBUFS; ++ -ENOBUFS; + } else { + err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); + } +@@ -2665,6 +2788,11 @@ + { + struct sk_buff *skb; + ++ if (is_meta_sk(sk)) { ++ mptcp_send_active_reset(sk, priority); ++ return; ++ } ++ + /* NOTE: No TCP options attached and we never retransmit this. */ + skb = alloc_skb(MAX_TCP_HEADER, priority); + if (!skb) { +@@ -2767,14 +2895,14 @@ + (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0)) + req->window_clamp = tcp_full_space(sk); + +- /* tcp_full_space because it is guaranteed to be the first packet */ +- tcp_select_initial_window(tcp_full_space(sk), +- mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0), ++ tp->select_initial_window(tcp_full_space(sk), ++ mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0) - ++ (tcp_rsk(req)->saw_mpc ? MPTCP_SUB_LEN_DSM_ALIGN : 0), + &req->rcv_wnd, + &req->window_clamp, + ireq->wscale_ok, + &rcv_wscale, +- dst_metric(dst, RTAX_INITRWND)); ++ dst_metric(dst, RTAX_INITRWND), sk); + ireq->rcv_wscale = rcv_wscale; + } + +@@ -2810,7 +2938,7 @@ + + /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ + th->window = htons(min(req->rcv_wnd, 65535U)); +- tcp_options_write((__be32 *)(th + 1), tp, &opts); ++ tcp_options_write((__be32 *)(th + 1), tp, &opts, skb); + th->doff = (tcp_header_size >> 2); + TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS, tcp_skb_pcount(skb)); + +@@ -2866,13 +2994,13 @@ + (tp->window_clamp > tcp_full_space(sk) || tp->window_clamp == 0)) + tp->window_clamp = tcp_full_space(sk); + +- tcp_select_initial_window(tcp_full_space(sk), ++ tp->select_initial_window(tcp_full_space(sk), + tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0), + &tp->rcv_wnd, + &tp->window_clamp, + sysctl_tcp_window_scaling, + &rcv_wscale, +- dst_metric(dst, RTAX_INITRWND)); ++ dst_metric(dst, RTAX_INITRWND), sk); + + tp->rx_opt.rcv_wscale = rcv_wscale; + tp->rcv_ssthresh = tp->rcv_wnd; +@@ -2896,6 +3024,38 @@ + inet_csk(sk)->icsk_rto = TCP_TIMEOUT_INIT; + inet_csk(sk)->icsk_retransmits = 0; + tcp_clear_retrans(tp); ++ ++#ifdef CONFIG_MPTCP ++ if (sysctl_mptcp_enabled && mptcp_doit(sk)) { ++ if (is_master_tp(tp)) { ++ tp->request_mptcp = 1; ++ mptcp_connect_init(sk); ++ } else if (tp->mptcp) { ++ struct inet_sock *inet = inet_sk(sk); ++ ++ tp->mptcp->snt_isn = tp->write_seq; ++ tp->mptcp->init_rcv_wnd = tp->rcv_wnd; ++ ++ /* Set nonce for new subflows */ ++ if (sk->sk_family == AF_INET) ++ tp->mptcp->mptcp_loc_nonce = mptcp_v4_get_nonce( ++ inet->inet_saddr, ++ inet->inet_daddr, ++ inet->inet_sport, ++ inet->inet_dport, ++ tp->write_seq); ++#if IS_ENABLED(CONFIG_IPV6) ++ else ++ tp->mptcp->mptcp_loc_nonce = mptcp_v6_get_nonce( ++ inet6_sk(sk)->saddr.s6_addr32, ++ sk->sk_v6_daddr.s6_addr32, ++ inet->inet_sport, ++ inet->inet_dport, ++ tp->write_seq); ++#endif ++ } ++ } ++#endif + } + + static void tcp_connect_queue_skb(struct sock *sk, struct sk_buff *skb) +@@ -3131,6 +3291,7 @@ + TCP_SKB_CB(buff)->when = tcp_time_stamp; + tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC)); + } ++EXPORT_SYMBOL(tcp_send_ack); + + /* This routine sends a packet with an out of date sequence + * number. It assumes the other end will try to ack it. +@@ -3143,7 +3304,7 @@ + * one is with SEG.SEQ=SND.UNA to deliver urgent pointer, another is + * out-of-date with SND.UNA-1 to probe window. + */ +-static int tcp_xmit_probe_skb(struct sock *sk, int urgent) ++int tcp_xmit_probe_skb(struct sock *sk, int urgent) + { + struct tcp_sock *tp = tcp_sk(sk); + struct sk_buff *skb; +@@ -3181,6 +3342,9 @@ + if (sk->sk_state == TCP_CLOSE) + return -1; + ++ if (is_meta_sk(sk)) ++ return mptcp_write_wakeup(sk); ++ + if ((skb = tcp_send_head(sk)) != NULL && + before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp))) { + int err; +diff -Nur linux-3.14.45.orig/net/ipv4/tcp_timer.c linux-3.14.45/net/ipv4/tcp_timer.c +--- linux-3.14.45.orig/net/ipv4/tcp_timer.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv4/tcp_timer.c 2015-06-24 14:15:48.891862483 +0200 +@@ -20,6 +20,7 @@ + + #include + #include ++#include + #include + + int sysctl_tcp_syn_retries __read_mostly = TCP_SYN_RETRIES; +@@ -32,7 +33,7 @@ + int sysctl_tcp_orphan_retries __read_mostly; + int sysctl_tcp_thin_linear_timeouts __read_mostly; + +-static void tcp_write_err(struct sock *sk) ++void tcp_write_err(struct sock *sk) + { + sk->sk_err = sk->sk_err_soft ? : ETIMEDOUT; + sk->sk_error_report(sk); +@@ -124,10 +125,8 @@ + * retransmissions with an initial RTO of TCP_RTO_MIN or TCP_TIMEOUT_INIT if + * syn_set flag is set. + */ +-static bool retransmits_timed_out(struct sock *sk, +- unsigned int boundary, +- unsigned int timeout, +- bool syn_set) ++bool retransmits_timed_out(struct sock *sk, unsigned int boundary, ++ unsigned int timeout, bool syn_set) + { + unsigned int linear_backoff_thresh, start_ts; + unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN; +@@ -153,7 +152,7 @@ + } + + /* A write timeout has occurred. Process the after effects. */ +-static int tcp_write_timeout(struct sock *sk) ++int tcp_write_timeout(struct sock *sk) + { + struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); +@@ -168,6 +167,10 @@ + } + retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries; + syn_set = true; ++ /* Stop retransmitting MP_CAPABLE options in SYN if timed out. */ ++ if (tcp_sk(sk)->request_mptcp && ++ icsk->icsk_retransmits >= mptcp_sysctl_syn_retries()) ++ tcp_sk(sk)->request_mptcp = 0; + } else { + if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) { + /* Black hole detection */ +@@ -248,18 +251,22 @@ + static void tcp_delack_timer(unsigned long data) + { + struct sock *sk = (struct sock *)data; ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; + +- bh_lock_sock(sk); +- if (!sock_owned_by_user(sk)) { ++ bh_lock_sock(meta_sk); ++ if (!sock_owned_by_user(meta_sk)) { + tcp_delack_timer_handler(sk); + } else { + inet_csk(sk)->icsk_ack.blocked = 1; +- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED); ++ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_DELAYEDACKLOCKED); + /* deleguate our work to tcp_release_cb() */ + if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags)) + sock_hold(sk); ++ if (tp->mpc) ++ mptcp_tsq_flags(sk); + } +- bh_unlock_sock(sk); ++ bh_unlock_sock(meta_sk); + sock_put(sk); + } + +@@ -421,6 +428,9 @@ + + tcp_enter_loss(sk, 0); + ++ if (tp->mpc) ++ mptcp_reinject_data(sk, 1); ++ + if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk)) > 0) { + /* Retransmission failed because of local congestion, + * do not backoff. +@@ -471,6 +481,8 @@ + /* Use normal (exponential) backoff */ + icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX); + } ++ if (tp->mpc) ++ mptcp_set_rto(sk); + inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX); + if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1, 0, 0)) + __sk_dst_reset(sk); +@@ -502,7 +514,10 @@ + break; + case ICSK_TIME_RETRANS: + icsk->icsk_pending = 0; +- tcp_retransmit_timer(sk); ++ if (is_meta_sk(sk)) ++ mptcp_retransmit_timer(sk); ++ else ++ tcp_retransmit_timer(sk); + break; + case ICSK_TIME_PROBE0: + icsk->icsk_pending = 0; +@@ -517,16 +532,19 @@ + static void tcp_write_timer(unsigned long data) + { + struct sock *sk = (struct sock *)data; ++ struct sock *meta_sk = tcp_sk(sk)->mpc ? mptcp_meta_sk(sk) : sk; + +- bh_lock_sock(sk); +- if (!sock_owned_by_user(sk)) { ++ bh_lock_sock(meta_sk); ++ if (!sock_owned_by_user(meta_sk)) { + tcp_write_timer_handler(sk); + } else { + /* deleguate our work to tcp_release_cb() */ + if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags)) + sock_hold(sk); ++ if (tcp_sk(sk)->mpc) ++ mptcp_tsq_flags(sk); + } +- bh_unlock_sock(sk); ++ bh_unlock_sock(meta_sk); + sock_put(sk); + } + +@@ -563,11 +581,12 @@ + struct sock *sk = (struct sock *) data; + struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); ++ struct sock *meta_sk = tp->mpc ? mptcp_meta_sk(sk) : sk; + u32 elapsed; + + /* Only process if socket is not in use. */ +- bh_lock_sock(sk); +- if (sock_owned_by_user(sk)) { ++ bh_lock_sock(meta_sk); ++ if (sock_owned_by_user(meta_sk)) { + /* Try again later. */ + inet_csk_reset_keepalive_timer (sk, HZ/20); + goto out; +@@ -578,6 +597,29 @@ + goto out; + } + ++ if (tp->send_mp_fclose) { ++ /* MUST do this before tcp_write_timeout, because retrans_stamp ++ * may have been set to 0 in another part while we are ++ * retransmitting MP_FASTCLOSE. Then, we would crash, because ++ * retransmits_timed_out accesses the meta-write-queue. ++ * ++ * We make sure that the timestamp is != 0. ++ */ ++ if (!tp->retrans_stamp) ++ tp->retrans_stamp = tcp_time_stamp ? : 1; ++ ++ if (tcp_write_timeout(sk)) ++ goto out; ++ ++ tcp_send_ack(sk); ++ icsk->icsk_backoff++; ++ icsk->icsk_retransmits++; ++ ++ icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX); ++ elapsed = icsk->icsk_rto; ++ goto resched; ++ } ++ + if (sk->sk_state == TCP_FIN_WAIT2 && sock_flag(sk, SOCK_DEAD)) { + if (tp->linger2 >= 0) { + const int tmo = tcp_fin_time(sk) - TCP_TIMEWAIT_LEN; +@@ -639,7 +681,7 @@ + tcp_done(sk); + + out: +- bh_unlock_sock(sk); ++ bh_unlock_sock(meta_sk); + sock_put(sk); + } + +diff -Nur linux-3.14.45.orig/net/ipv6/addrconf.c linux-3.14.45/net/ipv6/addrconf.c +--- linux-3.14.45.orig/net/ipv6/addrconf.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv6/addrconf.c 2015-06-24 14:15:48.891862483 +0200 +@@ -765,6 +765,7 @@ + + kfree_rcu(ifp, rcu); + } ++EXPORT_SYMBOL(inet6_ifa_finish_destroy); + + static void + ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) +diff -Nur linux-3.14.45.orig/net/ipv6/af_inet6.c linux-3.14.45/net/ipv6/af_inet6.c +--- linux-3.14.45.orig/net/ipv6/af_inet6.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv6/af_inet6.c 2015-06-24 14:15:48.891862483 +0200 +@@ -97,8 +97,7 @@ + return (struct ipv6_pinfo *)(((u8 *)sk) + offset); + } + +-static int inet6_create(struct net *net, struct socket *sock, int protocol, +- int kern) ++int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) + { + struct inet_sock *inet; + struct ipv6_pinfo *np; +diff -Nur linux-3.14.45.orig/net/ipv6/inet6_connection_sock.c linux-3.14.45/net/ipv6/inet6_connection_sock.c +--- linux-3.14.45.orig/net/ipv6/inet6_connection_sock.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv6/inet6_connection_sock.c 2015-06-24 14:15:48.891862483 +0200 +@@ -96,8 +96,8 @@ + /* + * request_sock (formerly open request) hash tables. + */ +-static u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, +- const u32 rnd, const u32 synq_hsize) ++u32 inet6_synq_hash(const struct in6_addr *raddr, const __be16 rport, ++ const u32 rnd, const u32 synq_hsize) + { + u32 c; + +diff -Nur linux-3.14.45.orig/net/ipv6/syncookies.c linux-3.14.45/net/ipv6/syncookies.c +--- linux-3.14.45.orig/net/ipv6/syncookies.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv6/syncookies.c 2015-06-24 14:15:48.891862483 +0200 +@@ -181,7 +181,7 @@ + + /* check for timestamp cookie support */ + memset(&tcp_opt, 0, sizeof(tcp_opt)); +- tcp_parse_options(skb, &tcp_opt, 0, NULL); ++ tcp_parse_options(skb, &tcp_opt, NULL, 0, NULL); + + if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok)) + goto out; +@@ -253,10 +253,10 @@ + } + + req->window_clamp = tp->window_clamp ? :dst_metric(dst, RTAX_WINDOW); +- tcp_select_initial_window(tcp_full_space(sk), req->mss, ++ tp->select_initial_window(tcp_full_space(sk), req->mss, + &req->rcv_wnd, &req->window_clamp, + ireq->wscale_ok, &rcv_wscale, +- dst_metric(dst, RTAX_INITRWND)); ++ dst_metric(dst, RTAX_INITRWND), sk); + + ireq->rcv_wscale = rcv_wscale; + +diff -Nur linux-3.14.45.orig/net/ipv6/tcp_ipv6.c linux-3.14.45/net/ipv6/tcp_ipv6.c +--- linux-3.14.45.orig/net/ipv6/tcp_ipv6.c 2015-06-23 02:01:36.000000000 +0200 ++++ linux-3.14.45/net/ipv6/tcp_ipv6.c 2015-06-24 14:44:57.517799806 +0200 +@@ -63,6 +63,8 @@ + #include + #include + #include ++#include ++#include + #include + + #include +@@ -73,14 +75,6 @@ + #include + #include + +-static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb); +-static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, +- struct request_sock *req); +- +-static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb); +- +-static const struct inet_connection_sock_af_ops ipv6_mapped; +-static const struct inet_connection_sock_af_ops ipv6_specific; + #ifdef CONFIG_TCP_MD5SIG + static const struct tcp_sock_af_ops tcp_sock_ipv6_specific; + static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific; +@@ -92,7 +86,7 @@ + } + #endif + +-static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) ++void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) + { + struct dst_entry *dst = skb_dst(skb); + const struct rt6_info *rt = (const struct rt6_info *)dst; +@@ -104,7 +98,7 @@ + inet6_sk(sk)->rx_dst_cookie = rt->rt6i_node->fn_sernum; + } + +-static void tcp_v6_hash(struct sock *sk) ++void tcp_v6_hash(struct sock *sk) + { + if (sk->sk_state != TCP_CLOSE) { + if (inet_csk(sk)->icsk_af_ops == &ipv6_mapped) { +@@ -117,7 +111,7 @@ + } + } + +-static __u32 tcp_v6_init_sequence(const struct sk_buff *skb) ++__u32 tcp_v6_init_sequence(const struct sk_buff *skb) + { + return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32, + ipv6_hdr(skb)->saddr.s6_addr32, +@@ -125,7 +119,7 @@ + tcp_hdr(skb)->source); + } + +-static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, ++int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, + int addr_len) + { + struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; +@@ -339,7 +333,7 @@ + const struct ipv6hdr *hdr = (const struct ipv6hdr *)skb->data; + const struct tcphdr *th = (struct tcphdr *)(skb->data+offset); + struct ipv6_pinfo *np; +- struct sock *sk; ++ struct sock *sk, *meta_sk; + int err; + struct tcp_sock *tp; + __u32 seq; +@@ -359,8 +353,14 @@ + return; + } + +- bh_lock_sock(sk); +- if (sock_owned_by_user(sk) && type != ICMPV6_PKT_TOOBIG) ++ tp = tcp_sk(sk); ++ if (tp->mpc) ++ meta_sk = mptcp_meta_sk(sk); ++ else ++ meta_sk = sk; ++ ++ bh_lock_sock(meta_sk); ++ if (sock_owned_by_user(meta_sk) && type != ICMPV6_PKT_TOOBIG) + NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS); + + if (sk->sk_state == TCP_CLOSE) +@@ -371,7 +371,6 @@ + goto out; + } + +- tp = tcp_sk(sk); + seq = ntohl(th->seq); + if (sk->sk_state != TCP_LISTEN && + !between(seq, tp->snd_una, tp->snd_nxt)) { +@@ -401,11 +400,15 @@ + goto out; + + tp->mtu_info = ntohl(info); +- if (!sock_owned_by_user(sk)) ++ if (!sock_owned_by_user(meta_sk)) + tcp_v6_mtu_reduced(sk); +- else if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, ++ else { ++ if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, + &tp->tsq_flags)) +- sock_hold(sk); ++ sock_hold(sk); ++ if (tp->mpc) ++ mptcp_tsq_flags(sk); ++ } + goto out; + } + +@@ -415,7 +418,7 @@ + switch (sk->sk_state) { + struct request_sock *req, **prev; + case TCP_LISTEN: +- if (sock_owned_by_user(sk)) ++ if (sock_owned_by_user(meta_sk)) + goto out; + + req = inet6_csk_search_req(sk, &prev, th->dest, &hdr->daddr, +@@ -440,7 +443,7 @@ + case TCP_SYN_SENT: + case TCP_SYN_RECV: /* Cannot happen. + It can, it SYNs are crossed. --ANK */ +- if (!sock_owned_by_user(sk)) { ++ if (!sock_owned_by_user(meta_sk)) { + sk->sk_err = err; + sk->sk_error_report(sk); /* Wake people up to see the error (see connect in sock.c) */ + +@@ -450,22 +453,22 @@ + goto out; + } + +- if (!sock_owned_by_user(sk) && np->recverr) { ++ if (!sock_owned_by_user(meta_sk) && np->recverr) { + sk->sk_err = err; + sk->sk_error_report(sk); + } else + sk->sk_err_soft = err; + + out: +- bh_unlock_sock(sk); ++ bh_unlock_sock(meta_sk); + sock_put(sk); + } + + +-static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, +- struct flowi6 *fl6, +- struct request_sock *req, +- u16 queue_mapping) ++int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, ++ struct flowi6 *fl6, ++ struct request_sock *req, ++ u16 queue_mapping) + { + struct inet_request_sock *ireq = inet_rsk(req); + struct ipv6_pinfo *np = inet6_sk(sk); +@@ -495,7 +498,7 @@ + return err; + } + +-static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) ++int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) + { + struct flowi6 fl6; + int res; +@@ -506,7 +509,7 @@ + return res; + } + +-static void tcp_v6_reqsk_destructor(struct request_sock *req) ++void tcp_v6_reqsk_destructor(struct request_sock *req) + { + kfree_skb(inet_rsk(req)->pktopts); + } +@@ -719,16 +722,16 @@ + }; + + #ifdef CONFIG_TCP_MD5SIG +-static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { ++const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { + .md5_lookup = tcp_v6_reqsk_md5_lookup, + .calc_md5_hash = tcp_v6_md5_hash_skb, + }; + #endif + +-static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, +- u32 tsval, u32 tsecr, ++static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, ++ u32 data_ack, u32 win, u32 tsval, u32 tsecr, + struct tcp_md5sig_key *key, int rst, u8 tclass, +- u32 label) ++ u32 label, int mptcp) + { + const struct tcphdr *th = tcp_hdr(skb); + struct tcphdr *t1; +@@ -746,7 +749,10 @@ + if (key) + tot_len += TCPOLEN_MD5SIG_ALIGNED; + #endif +- ++#ifdef CONFIG_MPTCP ++ if (mptcp) ++ tot_len += MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK; ++#endif + buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len, + GFP_ATOMIC); + if (buff == NULL) +@@ -784,6 +790,17 @@ + tcp_v6_md5_hash_hdr((__u8 *)topt, key, + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, t1); ++ topt += 4; ++ } ++#endif ++#ifdef CONFIG_MPTCP ++ if (mptcp) { ++ /* Construction of 32-bit data_ack */ ++ *topt++ = htonl((TCPOPT_MPTCP << 24) | ++ ((MPTCP_SUB_LEN_DSS + MPTCP_SUB_LEN_ACK) << 16) | ++ (0x20 << 8) | ++ (0x01)); ++ *topt++ = htonl(data_ack); + } + #endif + +@@ -821,7 +838,7 @@ + kfree_skb(buff); + } + +-static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) ++void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb) + { + const struct tcphdr *th = tcp_hdr(skb); + u32 seq = 0, ack_seq = 0; +@@ -876,7 +893,7 @@ + ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len - + (th->doff << 2); + +- tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, key, 1, 0, 0); ++ tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, 0, key, 1, 0, 0, 0); + + #ifdef CONFIG_TCP_MD5SIG + release_sk1: +@@ -887,40 +904,48 @@ + #endif + } + +-static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, ++static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 data_ack, + u32 win, u32 tsval, u32 tsecr, +- struct tcp_md5sig_key *key, u8 tclass, +- u32 label) ++ struct tcp_md5sig_key *key, u8 tclass, u32 label, ++ int mptcp) + { + tcp_v6_send_response(skb, seq, ack, win, tsval, tsecr, key, 0, tclass, +- label); ++ label, mptcp); + } + + static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb) + { + struct inet_timewait_sock *tw = inet_twsk(sk); + struct tcp_timewait_sock *tcptw = tcp_twsk(sk); ++ u32 data_ack = 0; ++ int mptcp = 0; ++ ++ if (tcptw->mptcp_tw && tcptw->mptcp_tw->meta_tw) { ++ data_ack = (u32)tcptw->mptcp_tw->rcv_nxt; ++ mptcp = 1; ++ } + + tcp_v6_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt, ++ data_ack, + tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale, + tcp_time_stamp + tcptw->tw_ts_offset, + tcptw->tw_ts_recent, tcp_twsk_md5_key(tcptw), +- tw->tw_tclass, cpu_to_be32(tw->tw_flowlabel)); ++ tw->tw_tclass, cpu_to_be32(tw->tw_flowlabel), mptcp); + + inet_twsk_put(tw); + } + +-static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, ++void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *req) + { + tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1, +- req->rcv_wnd, tcp_time_stamp, req->ts_recent, ++ 0, req->rcv_wnd, tcp_time_stamp, req->ts_recent, + tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), +- 0, 0); ++ 0, 0, 0); + } + + +-static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) ++struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) + { + struct request_sock *req, **prev; + const struct tcphdr *th = tcp_hdr(skb); +@@ -939,7 +964,13 @@ + + if (nsk) { + if (nsk->sk_state != TCP_TIME_WAIT) { ++ /* Don't lock again the meta-sk. It has been locked ++ * before mptcp_v6_do_rcv. ++ */ ++ if (tcp_sk(nsk)->mpc && !is_meta_sk(sk)) ++ bh_lock_sock(mptcp_meta_sk(nsk)); + bh_lock_sock(nsk); ++ + return nsk; + } + inet_twsk_put(inet_twsk(nsk)); +@@ -959,6 +990,7 @@ + static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) + { + struct tcp_options_received tmp_opt; ++ struct mptcp_options_received mopt; + struct request_sock *req; + struct inet_request_sock *ireq; + struct ipv6_pinfo *np = inet6_sk(sk); +@@ -971,6 +1003,23 @@ + if (skb->protocol == htons(ETH_P_IP)) + return tcp_v4_conn_request(sk, skb); + ++ tcp_clear_options(&tmp_opt); ++ tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); ++ tmp_opt.user_mss = tp->rx_opt.user_mss; ++ mptcp_init_mp_opt(&mopt); ++ tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL); ++ ++#ifdef CONFIG_MPTCP ++ /*MPTCP structures not initialized, so return error */ ++ if (mptcp_init_failed) ++ mptcp_init_mp_opt(&mopt); ++ ++ if (mopt.is_mp_join) ++ return mptcp_do_join_short(skb, &mopt, &tmp_opt, sock_net(sk)); ++ if (mopt.drop_me) ++ goto drop; ++#endif ++ + if (!ipv6_unicast_destination(skb)) + goto drop; + +@@ -986,7 +1035,22 @@ + goto drop; + } + +- req = inet6_reqsk_alloc(&tcp6_request_sock_ops); ++#ifdef CONFIG_MPTCP ++ if (sysctl_mptcp_enabled == MPTCP_APP && !tp->mptcp_enabled) ++ mopt.saw_mpc = 0; ++ if (mopt.saw_mpc && !want_cookie) { ++ req = inet6_reqsk_alloc(&mptcp6_request_sock_ops); ++ ++ if (req == NULL) ++ goto drop; ++ ++ mptcp_rsk(req)->mpcb = NULL; ++ mptcp_rsk(req)->dss_csum = mopt.dss_csum; ++ mptcp_rsk(req)->collide_tk.pprev = NULL; ++ } else ++#endif ++ req = inet6_reqsk_alloc(&tcp6_request_sock_ops); ++ + if (req == NULL) + goto drop; + +@@ -994,17 +1058,15 @@ + tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops; + #endif + +- tcp_clear_options(&tmp_opt); +- tmp_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); +- tmp_opt.user_mss = tp->rx_opt.user_mss; +- tcp_parse_options(skb, &tmp_opt, 0, NULL); +- + if (want_cookie && !tmp_opt.saw_tstamp) + tcp_clear_options(&tmp_opt); + + tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; + tcp_openreq_init(req, &tmp_opt, skb); + ++ if (mopt.saw_mpc && !want_cookie) ++ mptcp_reqsk_new_mptcp(req, &tmp_opt, &mopt, skb); ++ + ireq = inet_rsk(req); + ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; + ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; +@@ -1094,9 +1156,9 @@ + return 0; /* don't send reset */ + } + +-static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, +- struct request_sock *req, +- struct dst_entry *dst) ++struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ++ struct request_sock *req, ++ struct dst_entry *dst) + { + struct inet_request_sock *ireq; + struct ipv6_pinfo *newnp, *np = inet6_sk(sk); +@@ -1317,7 +1379,7 @@ + * This is because we cannot sleep with the original spinlock + * held. + */ +-static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) ++int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) + { + struct ipv6_pinfo *np = inet6_sk(sk); + struct tcp_sock *tp; +@@ -1339,6 +1401,9 @@ + goto discard; + #endif + ++ if (is_meta_sk(sk)) ++ return mptcp_v6_do_rcv(sk, skb); ++ + if (sk_filter(sk, skb)) + goto discard; + +@@ -1460,7 +1525,7 @@ + { + const struct tcphdr *th; + const struct ipv6hdr *hdr; +- struct sock *sk; ++ struct sock *sk, *meta_sk = NULL; + int ret; + struct net *net = dev_net(skb->dev); + +@@ -1491,18 +1556,43 @@ + TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin + + skb->len - th->doff*4); + TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq); ++#ifdef CONFIG_MPTCP ++ TCP_SKB_CB(skb)->mptcp_flags = 0; ++ TCP_SKB_CB(skb)->dss_off = 0; ++#endif + TCP_SKB_CB(skb)->when = 0; + TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr); + TCP_SKB_CB(skb)->sacked = 0; + + sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest); +- if (!sk) +- goto no_tcp_socket; + + process: +- if (sk->sk_state == TCP_TIME_WAIT) ++ if (sk && sk->sk_state == TCP_TIME_WAIT) + goto do_time_wait; + ++#ifdef CONFIG_MPTCP ++ if (!sk && th->syn && !th->ack) { ++ int ret = mptcp_lookup_join(skb, NULL); ++ ++ if (ret < 0) { ++ tcp_v6_send_reset(NULL, skb); ++ goto discard_it; ++ } else if (ret > 0) { ++ return 0; ++ } ++ } ++ ++ /* Is there a pending request sock for this segment ? */ ++ if ((!sk || sk->sk_state == TCP_LISTEN) && mptcp_check_req(skb, net)) { ++ if (sk) ++ sock_put(sk); ++ return 0; ++ } ++#endif ++ ++ if (!sk) ++ goto no_tcp_socket; ++ + if (hdr->hop_limit < inet6_sk(sk)->min_hopcount) { + NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP); + goto discard_and_relse; +@@ -1517,11 +1607,21 @@ + sk_mark_napi_id(sk, skb); + skb->dev = NULL; + +- bh_lock_sock_nested(sk); ++ if (tcp_sk(sk)->mpc) { ++ meta_sk = mptcp_meta_sk(sk); ++ ++ bh_lock_sock_nested(meta_sk); ++ if (sock_owned_by_user(meta_sk)) ++ skb->sk = sk; ++ } else { ++ meta_sk = sk; ++ bh_lock_sock_nested(sk); ++ } ++ + ret = 0; +- if (!sock_owned_by_user(sk)) { ++ if (!sock_owned_by_user(meta_sk)) { + #ifdef CONFIG_NET_DMA +- struct tcp_sock *tp = tcp_sk(sk); ++ struct tcp_sock *tp = tcp_sk(meta_sk); + if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) + tp->ucopy.dma_chan = net_dma_find_channel(); + if (tp->ucopy.dma_chan) +@@ -1529,16 +1629,17 @@ + else + #endif + { +- if (!tcp_prequeue(sk, skb)) ++ if (!tcp_prequeue(meta_sk, skb)) + ret = tcp_v6_do_rcv(sk, skb); + } +- } else if (unlikely(sk_add_backlog(sk, skb, +- sk->sk_rcvbuf + sk->sk_sndbuf))) { +- bh_unlock_sock(sk); ++ } else if (unlikely(sk_add_backlog(meta_sk, skb, ++ meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { ++ bh_unlock_sock(meta_sk); + NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); + goto discard_and_relse; + } +- bh_unlock_sock(sk); ++ ++ bh_unlock_sock(meta_sk); + + sock_put(sk); + return ret ? -1 : 0; +@@ -1595,6 +1696,18 @@ + sk = sk2; + goto process; + } ++#ifdef CONFIG_MPTCP ++ if (th->syn && !th->ack) { ++ int ret = mptcp_lookup_join(skb, inet_twsk(sk)); ++ ++ if (ret < 0) { ++ tcp_v6_send_reset(NULL, skb); ++ goto discard_it; ++ } else if (ret > 0) { ++ return 0; ++ } ++ } ++#endif + /* Fall through to ACK */ + } + case TCP_TW_ACK: +@@ -1644,13 +1757,13 @@ + } + } + +-static struct timewait_sock_ops tcp6_timewait_sock_ops = { ++struct timewait_sock_ops tcp6_timewait_sock_ops = { + .twsk_obj_size = sizeof(struct tcp6_timewait_sock), + .twsk_unique = tcp_twsk_unique, + .twsk_destructor= tcp_twsk_destructor, + }; + +-static const struct inet_connection_sock_af_ops ipv6_specific = { ++const struct inet_connection_sock_af_ops ipv6_specific = { + .queue_xmit = inet6_csk_xmit, + .send_check = tcp_v6_send_check, + .rebuild_header = inet6_sk_rebuild_header, +@@ -1683,7 +1796,7 @@ + * TCP over IPv4 via INET6 API + */ + +-static const struct inet_connection_sock_af_ops ipv6_mapped = { ++const struct inet_connection_sock_af_ops ipv6_mapped = { + .queue_xmit = ip_queue_xmit, + .send_check = tcp_v4_send_check, + .rebuild_header = inet_sk_rebuild_header, +@@ -1729,7 +1842,7 @@ + return 0; + } + +-static void tcp_v6_destroy_sock(struct sock *sk) ++void tcp_v6_destroy_sock(struct sock *sk) + { + tcp_v4_destroy_sock(sk); + inet6_destroy_sock(sk); +diff -Nur linux-3.14.45.orig/net/mptcp/Kconfig linux-3.14.45/net/mptcp/Kconfig +--- linux-3.14.45.orig/net/mptcp/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/Kconfig 2015-06-24 14:15:48.891862483 +0200 +@@ -0,0 +1,58 @@ ++# ++# MPTCP configuration ++# ++config MPTCP ++ bool "MPTCP protocol" ++ depends on (IPV6=y || IPV6=n) ++ ---help--- ++ This replaces the normal TCP stack with a Multipath TCP stack, ++ able to use several paths at once. ++ ++menuconfig MPTCP_PM_ADVANCED ++ bool "MPTCP: advanced path-manager control" ++ depends on MPTCP=y ++ ---help--- ++ Support for selection of different path-managers. You should choose 'Y' here, ++ because otherwise you will not actively create new MPTCP-subflows. ++ ++if MPTCP_PM_ADVANCED ++ ++config MPTCP_FULLMESH ++ tristate "MPTCP Full-Mesh Path-Manager" ++ depends on MPTCP=y ++ ---help--- ++ This path-management module will create a full-mesh among all IP-addresses. ++ ++config MPTCP_NDIFFPORTS ++ tristate "MPTCP ndiff-ports" ++ depends on MPTCP=y ++ ---help--- ++ This path-management module will create multiple subflows between the same ++ pair of IP-addresses, modifying the source-port. You can set the number ++ of subflows via the mptcp_ndiffports-sysctl. ++ ++choice ++ prompt "Default MPTCP Path-Manager" ++ default DEFAULT ++ help ++ Select the Path-Manager of your choice ++ ++ config DEFAULT_FULLMESH ++ bool "Full mesh" if MPTCP_FULLMESH=y ++ ++ config DEFAULT_NDIFFPORTS ++ bool "ndiff-ports" if MPTCP_NDIFFPORTS=y ++ ++ config DEFAULT_DUMMY ++ bool "Default" ++ ++endchoice ++ ++endif ++ ++config DEFAULT_MPTCP_PM ++ string ++ default "default" if DEFAULT_DUMMY ++ default "fullmesh" if DEFAULT_FULLMESH ++ default "ndiffports" if DEFAULT_NDIFFPORTS ++ default "default" +diff -Nur linux-3.14.45.orig/net/mptcp/Makefile linux-3.14.45/net/mptcp/Makefile +--- linux-3.14.45.orig/net/mptcp/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/Makefile 2015-06-24 14:15:48.891862483 +0200 +@@ -0,0 +1,18 @@ ++# ++## Makefile for MultiPath TCP support code. ++# ++# ++ ++obj-$(CONFIG_MPTCP) += mptcp.o ++ ++mptcp-y := mptcp_ctrl.o mptcp_ipv4.o mptcp_ofo_queue.o mptcp_pm.o \ ++ mptcp_output.o mptcp_input.o ++ ++obj-$(CONFIG_TCP_CONG_COUPLED) += mptcp_coupled.o ++obj-$(CONFIG_TCP_CONG_OLIA) += mptcp_olia.o ++obj-$(CONFIG_TCP_CONG_WVEGAS) += mptcp_wvegas.o ++obj-$(CONFIG_MPTCP_FULLMESH) += mptcp_fullmesh.o ++obj-$(CONFIG_MPTCP_NDIFFPORTS) += mptcp_ndiffports.o ++ ++mptcp-$(subst m,y,$(CONFIG_IPV6)) += mptcp_ipv6.o ++ +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_coupled.c linux-3.14.45/net/mptcp/mptcp_coupled.c +--- linux-3.14.45.orig/net/mptcp/mptcp_coupled.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_coupled.c 2015-06-24 14:15:48.891862483 +0200 +@@ -0,0 +1,273 @@ ++/* ++ * MPTCP implementation - Coupled Congestion Control ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer & Author: ++ * Christoph Paasch ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++#include ++#include ++ ++#include ++ ++/* Scaling is done in the numerator with alpha_scale_num and in the denominator ++ * with alpha_scale_den. ++ * ++ * To downscale, we just need to use alpha_scale. ++ * ++ * We have: alpha_scale = alpha_scale_num / (alpha_scale_den ^ 2) ++ */ ++static int alpha_scale_den = 10; ++static int alpha_scale_num = 32; ++static int alpha_scale = 12; ++ ++struct mptcp_ccc { ++ u64 alpha; ++ bool forced_update; ++}; ++ ++static inline int mptcp_ccc_sk_can_send(const struct sock *sk) ++{ ++ return mptcp_sk_can_send(sk) && tcp_sk(sk)->srtt; ++} ++ ++static inline u64 mptcp_get_alpha(struct sock *meta_sk) ++{ ++ struct mptcp_ccc *mptcp_ccc = inet_csk_ca(meta_sk); ++ return mptcp_ccc->alpha; ++} ++ ++static inline void mptcp_set_alpha(struct sock *meta_sk, u64 alpha) ++{ ++ struct mptcp_ccc *mptcp_ccc = inet_csk_ca(meta_sk); ++ mptcp_ccc->alpha = alpha; ++} ++ ++static inline u64 mptcp_ccc_scale(u32 val, int scale) ++{ ++ return (u64) val << scale; ++} ++ ++static inline bool mptcp_get_forced(struct sock *meta_sk) ++{ ++ struct mptcp_ccc *mptcp_ccc = inet_csk_ca(meta_sk); ++ return mptcp_ccc->forced_update; ++} ++ ++static inline void mptcp_set_forced(struct sock *meta_sk, bool force) ++{ ++ struct mptcp_ccc *mptcp_ccc = inet_csk_ca(meta_sk); ++ mptcp_ccc->forced_update = force; ++} ++ ++static void mptcp_ccc_recalc_alpha(struct sock *sk) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; ++ struct sock *sub_sk; ++ int best_cwnd = 0, best_rtt = 0, can_send = 0; ++ u64 max_numerator = 0, sum_denominator = 0, alpha = 1; ++ ++ if (!mpcb) ++ return; ++ ++ /* Only one subflow left - fall back to normal reno-behavior ++ * (set alpha to 1) */ ++ if (mpcb->cnt_established <= 1) ++ goto exit; ++ ++ /* Do regular alpha-calculation for multiple subflows */ ++ ++ /* Find the max numerator of the alpha-calculation */ ++ mptcp_for_each_sk(mpcb, sub_sk) { ++ struct tcp_sock *sub_tp = tcp_sk(sub_sk); ++ u64 tmp; ++ ++ if (!mptcp_ccc_sk_can_send(sub_sk)) ++ continue; ++ ++ can_send++; ++ ++ /* We need to look for the path, that provides the max-value. ++ * Integer-overflow is not possible here, because ++ * tmp will be in u64. ++ */ ++ tmp = div64_u64(mptcp_ccc_scale(sub_tp->snd_cwnd, ++ alpha_scale_num), (u64)sub_tp->srtt * sub_tp->srtt); ++ ++ if (tmp >= max_numerator) { ++ max_numerator = tmp; ++ best_cwnd = sub_tp->snd_cwnd; ++ best_rtt = sub_tp->srtt; ++ } ++ } ++ ++ /* No subflow is able to send - we don't care anymore */ ++ if (unlikely(!can_send)) ++ goto exit; ++ ++ /* Calculate the denominator */ ++ mptcp_for_each_sk(mpcb, sub_sk) { ++ struct tcp_sock *sub_tp = tcp_sk(sub_sk); ++ ++ if (!mptcp_ccc_sk_can_send(sub_sk)) ++ continue; ++ ++ sum_denominator += div_u64( ++ mptcp_ccc_scale(sub_tp->snd_cwnd, ++ alpha_scale_den) * best_rtt, ++ sub_tp->srtt); ++ } ++ sum_denominator *= sum_denominator; ++ if (unlikely(!sum_denominator)) { ++ pr_err("%s: sum_denominator == 0, cnt_established:%d\n", ++ __func__, mpcb->cnt_established); ++ mptcp_for_each_sk(mpcb, sub_sk) { ++ struct tcp_sock *sub_tp = tcp_sk(sub_sk); ++ pr_err("%s: pi:%d, state:%d\n, rtt:%u, cwnd: %u", ++ __func__, sub_tp->mptcp->path_index, ++ sub_sk->sk_state, sub_tp->srtt, ++ sub_tp->snd_cwnd); ++ } ++ } ++ ++ alpha = div64_u64(mptcp_ccc_scale(best_cwnd, alpha_scale_num), sum_denominator); ++ ++ if (unlikely(!alpha)) ++ alpha = 1; ++ ++exit: ++ mptcp_set_alpha(mptcp_meta_sk(sk), alpha); ++} ++ ++static void mptcp_ccc_init(struct sock *sk) ++{ ++ if (tcp_sk(sk)->mpc) { ++ mptcp_set_forced(mptcp_meta_sk(sk), 0); ++ mptcp_set_alpha(mptcp_meta_sk(sk), 1); ++ } ++ /* If we do not mptcp, behave like reno: return */ ++} ++ ++static void mptcp_ccc_cwnd_event(struct sock *sk, enum tcp_ca_event event) ++{ ++ if (event == CA_EVENT_LOSS) ++ mptcp_ccc_recalc_alpha(sk); ++} ++ ++static void mptcp_ccc_set_state(struct sock *sk, u8 ca_state) ++{ ++ if (!tcp_sk(sk)->mpc) ++ return; ++ ++ mptcp_set_forced(mptcp_meta_sk(sk), 1); ++} ++ ++static void mptcp_ccc_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct mptcp_cb *mpcb = tp->mpcb; ++ int snd_cwnd; ++ ++ if (!tp->mpc) { ++ tcp_reno_cong_avoid(sk, ack, acked, in_flight); ++ return; ++ } ++ ++ if (!tcp_is_cwnd_limited(sk, in_flight)) ++ return; ++ ++ if (tp->snd_cwnd <= tp->snd_ssthresh) { ++ /* In "safe" area, increase. */ ++ tcp_slow_start(tp, acked); ++ mptcp_ccc_recalc_alpha(sk); ++ return; ++ } ++ ++ if (mptcp_get_forced(mptcp_meta_sk(sk))) { ++ mptcp_ccc_recalc_alpha(sk); ++ mptcp_set_forced(mptcp_meta_sk(sk), 0); ++ } ++ ++ if (mpcb->cnt_established > 1) { ++ u64 alpha = mptcp_get_alpha(mptcp_meta_sk(sk)); ++ ++ /* This may happen, if at the initialization, the mpcb ++ * was not yet attached to the sock, and thus ++ * initializing alpha failed. ++ */ ++ if (unlikely(!alpha)) ++ alpha = 1; ++ ++ snd_cwnd = (int) div_u64 ((u64) mptcp_ccc_scale(1, alpha_scale), ++ alpha); ++ ++ /* snd_cwnd_cnt >= max (scale * tot_cwnd / alpha, cwnd) ++ * Thus, we select here the max value. */ ++ if (snd_cwnd < tp->snd_cwnd) ++ snd_cwnd = tp->snd_cwnd; ++ } else { ++ snd_cwnd = tp->snd_cwnd; ++ } ++ ++ if (tp->snd_cwnd_cnt >= snd_cwnd) { ++ if (tp->snd_cwnd < tp->snd_cwnd_clamp) { ++ tp->snd_cwnd++; ++ mptcp_ccc_recalc_alpha(sk); ++ } ++ ++ tp->snd_cwnd_cnt = 0; ++ } else { ++ tp->snd_cwnd_cnt++; ++ } ++} ++ ++static struct tcp_congestion_ops mptcp_ccc = { ++ .init = mptcp_ccc_init, ++ .ssthresh = tcp_reno_ssthresh, ++ .cong_avoid = mptcp_ccc_cong_avoid, ++ .cwnd_event = mptcp_ccc_cwnd_event, ++ .set_state = mptcp_ccc_set_state, ++ .min_cwnd = tcp_reno_min_cwnd, ++ .owner = THIS_MODULE, ++ .name = "coupled", ++}; ++ ++static int __init mptcp_ccc_register(void) ++{ ++ BUILD_BUG_ON(sizeof(struct mptcp_ccc) > ICSK_CA_PRIV_SIZE); ++ return tcp_register_congestion_control(&mptcp_ccc); ++} ++ ++static void __exit mptcp_ccc_unregister(void) ++{ ++ tcp_unregister_congestion_control(&mptcp_ccc); ++} ++ ++module_init(mptcp_ccc_register); ++module_exit(mptcp_ccc_unregister); ++ ++MODULE_AUTHOR("Christoph Paasch, Sébastien Barré"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MPTCP COUPLED CONGESTION CONTROL"); ++MODULE_VERSION("0.1"); +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_ctrl.c linux-3.14.45/net/mptcp/mptcp_ctrl.c +--- linux-3.14.45.orig/net/mptcp/mptcp_ctrl.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_ctrl.c 2015-06-24 14:15:48.891862483 +0200 +@@ -0,0 +1,2270 @@ ++/* ++ * MPTCP implementation - MPTCP-control ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer & Author: ++ * Christoph Paasch ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#if IS_ENABLED(CONFIG_IPV6) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct kmem_cache *mptcp_sock_cache __read_mostly; ++static struct kmem_cache *mptcp_cb_cache __read_mostly; ++static struct kmem_cache *mptcp_tw_cache __read_mostly; ++ ++int sysctl_mptcp_enabled __read_mostly = 1; ++int sysctl_mptcp_checksum __read_mostly = 1; ++int sysctl_mptcp_debug __read_mostly; ++EXPORT_SYMBOL(sysctl_mptcp_debug); ++int sysctl_mptcp_syn_retries __read_mostly = 3; ++ ++bool mptcp_init_failed __read_mostly; ++ ++static int proc_mptcp_path_manager(ctl_table *ctl, int write, ++ void __user *buffer, size_t *lenp, ++ loff_t *ppos) ++{ ++ char val[MPTCP_PM_NAME_MAX]; ++ ctl_table tbl = { ++ .data = val, ++ .maxlen = MPTCP_PM_NAME_MAX, ++ }; ++ int ret; ++ ++ mptcp_get_default_path_manager(val); ++ ++ ret = proc_dostring(&tbl, write, buffer, lenp, ppos); ++ if (write && ret == 0) ++ ret = mptcp_set_default_path_manager(val); ++ return ret; ++} ++ ++static struct ctl_table mptcp_table[] = { ++ { ++ .procname = "mptcp_enabled", ++ .data = &sysctl_mptcp_enabled, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = &proc_dointvec ++ }, ++ { ++ .procname = "mptcp_checksum", ++ .data = &sysctl_mptcp_checksum, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = &proc_dointvec ++ }, ++ { ++ .procname = "mptcp_debug", ++ .data = &sysctl_mptcp_debug, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = &proc_dointvec ++ }, ++ { ++ .procname = "mptcp_syn_retries", ++ .data = &sysctl_mptcp_syn_retries, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = &proc_dointvec ++ }, ++ { ++ .procname = "mptcp_path_manager", ++ .mode = 0644, ++ .maxlen = MPTCP_PM_NAME_MAX, ++ .proc_handler = proc_mptcp_path_manager, ++ }, ++ { } ++}; ++ ++static inline u32 mptcp_hash_tk(u32 token) ++{ ++ return token % MPTCP_HASH_SIZE; ++} ++ ++struct hlist_nulls_head tk_hashtable[MPTCP_HASH_SIZE]; ++EXPORT_SYMBOL(tk_hashtable); ++ ++/* This second hashtable is needed to retrieve request socks ++ * created as a result of a join request. While the SYN contains ++ * the token, the final ack does not, so we need a separate hashtable ++ * to retrieve the mpcb. ++ */ ++struct list_head mptcp_reqsk_htb[MPTCP_HASH_SIZE]; ++spinlock_t mptcp_reqsk_hlock; /* hashtable protection */ ++ ++/* The following hash table is used to avoid collision of token */ ++static struct hlist_nulls_head mptcp_reqsk_tk_htb[MPTCP_HASH_SIZE]; ++spinlock_t mptcp_tk_hashlock; /* hashtable protection */ ++ ++static int mptcp_reqsk_find_tk(u32 token) ++{ ++ u32 hash = mptcp_hash_tk(token); ++ struct mptcp_request_sock *mtreqsk; ++ const struct hlist_nulls_node *node; ++ ++ hlist_nulls_for_each_entry_rcu(mtreqsk, node, ++ &mptcp_reqsk_tk_htb[hash], collide_tk) { ++ if (token == mtreqsk->mptcp_loc_token) ++ return 1; ++ } ++ return 0; ++} ++ ++static void mptcp_reqsk_insert_tk(struct request_sock *reqsk, u32 token) ++{ ++ u32 hash = mptcp_hash_tk(token); ++ ++ hlist_nulls_add_head_rcu(&mptcp_rsk(reqsk)->collide_tk, ++ &mptcp_reqsk_tk_htb[hash]); ++} ++ ++static void mptcp_reqsk_remove_tk(struct request_sock *reqsk) ++{ ++ rcu_read_lock(); ++ spin_lock(&mptcp_tk_hashlock); ++ hlist_nulls_del_init_rcu(&mptcp_rsk(reqsk)->collide_tk); ++ spin_unlock(&mptcp_tk_hashlock); ++ rcu_read_unlock(); ++} ++ ++void mptcp_reqsk_destructor(struct request_sock *req) ++{ ++ if (!mptcp_rsk(req)->mpcb) { ++ if (in_softirq()) { ++ mptcp_reqsk_remove_tk(req); ++ } else { ++ rcu_read_lock_bh(); ++ spin_lock(&mptcp_tk_hashlock); ++ hlist_nulls_del_init_rcu(&mptcp_rsk(req)->collide_tk); ++ spin_unlock(&mptcp_tk_hashlock); ++ rcu_read_unlock_bh(); ++ } ++ } else { ++ mptcp_hash_request_remove(req); ++ } ++} ++ ++static void __mptcp_hash_insert(struct tcp_sock *meta_tp, u32 token) ++{ ++ u32 hash = mptcp_hash_tk(token); ++ hlist_nulls_add_head_rcu(&meta_tp->tk_table, &tk_hashtable[hash]); ++ meta_tp->inside_tk_table = 1; ++} ++ ++static int mptcp_find_token(u32 token) ++{ ++ u32 hash = mptcp_hash_tk(token); ++ struct tcp_sock *meta_tp; ++ const struct hlist_nulls_node *node; ++ ++ hlist_nulls_for_each_entry_rcu(meta_tp, node, &tk_hashtable[hash], tk_table) { ++ if (token == meta_tp->mptcp_loc_token) ++ return 1; ++ } ++ return 0; ++} ++ ++static void mptcp_set_key_reqsk(struct request_sock *req, ++ const struct sk_buff *skb) ++{ ++ struct inet_request_sock *ireq = inet_rsk(req); ++ struct mptcp_request_sock *mtreq = mptcp_rsk(req); ++ ++ if (skb->protocol == htons(ETH_P_IP)) { ++ mtreq->mptcp_loc_key = mptcp_v4_get_key(ip_hdr(skb)->saddr, ++ ip_hdr(skb)->daddr, ++ htons(ireq->ir_num), ++ ireq->ir_rmt_port); ++#if IS_ENABLED(CONFIG_IPV6) ++ } else { ++ mtreq->mptcp_loc_key = mptcp_v6_get_key(ipv6_hdr(skb)->saddr.s6_addr32, ++ ipv6_hdr(skb)->daddr.s6_addr32, ++ htons(ireq->ir_num), ++ ireq->ir_rmt_port); ++#endif ++ } ++ ++ mptcp_key_sha1(mtreq->mptcp_loc_key, &mtreq->mptcp_loc_token, NULL); ++} ++ ++/* New MPTCP-connection request, prepare a new token for the meta-socket that ++ * will be created in mptcp_check_req_master(), and store the received token. ++ */ ++void mptcp_reqsk_new_mptcp(struct request_sock *req, ++ const struct tcp_options_received *rx_opt, ++ const struct mptcp_options_received *mopt, ++ const struct sk_buff *skb) ++{ ++ struct mptcp_request_sock *mtreq = mptcp_rsk(req); ++ ++ tcp_rsk(req)->saw_mpc = 1; ++ ++ rcu_read_lock(); ++ spin_lock(&mptcp_tk_hashlock); ++ do { ++ mptcp_set_key_reqsk(req, skb); ++ } while (mptcp_reqsk_find_tk(mtreq->mptcp_loc_token) || ++ mptcp_find_token(mtreq->mptcp_loc_token)); ++ ++ mptcp_reqsk_insert_tk(req, mtreq->mptcp_loc_token); ++ spin_unlock(&mptcp_tk_hashlock); ++ rcu_read_unlock(); ++ mtreq->mptcp_rem_key = mopt->mptcp_key; ++} ++ ++static void mptcp_set_key_sk(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct inet_sock *isk = inet_sk(sk); ++ ++ if (sk->sk_family == AF_INET) ++ tp->mptcp_loc_key = mptcp_v4_get_key(isk->inet_saddr, ++ isk->inet_daddr, ++ isk->inet_sport, ++ isk->inet_dport); ++#if IS_ENABLED(CONFIG_IPV6) ++ else ++ tp->mptcp_loc_key = mptcp_v6_get_key(inet6_sk(sk)->saddr.s6_addr32, ++ sk->sk_v6_daddr.s6_addr32, ++ isk->inet_sport, ++ isk->inet_dport); ++#endif ++ ++ mptcp_key_sha1(tp->mptcp_loc_key, ++ &tp->mptcp_loc_token, NULL); ++} ++ ++void mptcp_connect_init(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ rcu_read_lock_bh(); ++ spin_lock(&mptcp_tk_hashlock); ++ do { ++ mptcp_set_key_sk(sk); ++ } while (mptcp_reqsk_find_tk(tp->mptcp_loc_token) || ++ mptcp_find_token(tp->mptcp_loc_token)); ++ ++ __mptcp_hash_insert(tp, tp->mptcp_loc_token); ++ spin_unlock(&mptcp_tk_hashlock); ++ rcu_read_unlock_bh(); ++} ++ ++/** ++ * This function increments the refcount of the mpcb struct. ++ * It is the responsibility of the caller to decrement when releasing ++ * the structure. ++ */ ++struct sock *mptcp_hash_find(struct net *net, u32 token) ++{ ++ u32 hash = mptcp_hash_tk(token); ++ struct tcp_sock *meta_tp; ++ struct sock *meta_sk = NULL; ++ struct hlist_nulls_node *node; ++ ++ rcu_read_lock(); ++ hlist_nulls_for_each_entry_rcu(meta_tp, node, &tk_hashtable[hash], ++ tk_table) { ++ meta_sk = (struct sock *)meta_tp; ++ if (token == meta_tp->mptcp_loc_token && ++ net_eq(net, sock_net(meta_sk)) && ++ atomic_inc_not_zero(&meta_sk->sk_refcnt)) ++ break; ++ meta_sk = NULL; ++ } ++ rcu_read_unlock(); ++ return meta_sk; ++} ++ ++void mptcp_hash_remove_bh(struct tcp_sock *meta_tp) ++{ ++ /* remove from the token hashtable */ ++ rcu_read_lock_bh(); ++ spin_lock(&mptcp_tk_hashlock); ++ hlist_nulls_del_init_rcu(&meta_tp->tk_table); ++ meta_tp->inside_tk_table = 0; ++ spin_unlock(&mptcp_tk_hashlock); ++ rcu_read_unlock_bh(); ++} ++ ++void mptcp_hash_remove(struct tcp_sock *meta_tp) ++{ ++ rcu_read_lock(); ++ spin_lock(&mptcp_tk_hashlock); ++ hlist_nulls_del_init_rcu(&meta_tp->tk_table); ++ meta_tp->inside_tk_table = 0; ++ spin_unlock(&mptcp_tk_hashlock); ++ rcu_read_unlock(); ++} ++ ++static struct sock *mptcp_syn_recv_sock(struct sock *sk, struct sk_buff *skb, ++ struct request_sock *req, ++ struct dst_entry *dst) ++{ ++#if IS_ENABLED(CONFIG_IPV6) ++ if (sk->sk_family == AF_INET6) ++ return tcp_v6_syn_recv_sock(sk, skb, req, dst); ++ ++ /* sk->sk_family == AF_INET */ ++ if (req->rsk_ops->family == AF_INET6) ++ return mptcp_v6v4_syn_recv_sock(sk, skb, req, dst); ++#endif ++ ++ /* sk->sk_family == AF_INET && req->rsk_ops->family == AF_INET */ ++ return tcp_v4_syn_recv_sock(sk, skb, req, dst); ++} ++ ++struct sock *mptcp_select_ack_sock(const struct sock *meta_sk, int copied) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct sock *sk, *subsk = NULL; ++ u32 max_data_seq = 0; ++ /* max_data_seq initialized to correct compiler-warning. ++ * But the initialization is handled by max_data_seq_set ++ */ ++ short max_data_seq_set = 0; ++ u32 min_time = 0xffffffff; ++ ++ /* How do we select the subflow to send the window-update on? ++ * ++ * 1. He has to be in a state where he can send an ack and is ++ * operational (pf = 0). ++ * 2. He has to be one of those subflow who recently ++ * contributed to the received stream ++ * (this guarantees a working subflow) ++ * a) its latest data_seq received is after the original ++ * copied_seq. ++ * We select the one with the lowest rtt, so that the ++ * window-update reaches our peer the fastest. ++ * b) if no subflow has this kind of data_seq (e.g., very ++ * strange meta-level retransmissions going on), we take ++ * the subflow who last sent the highest data_seq. ++ */ ++ mptcp_for_each_sk(meta_tp->mpcb, sk) { ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ if (!mptcp_sk_can_send_ack(sk) || tp->pf) ++ continue; ++ ++ /* Select among those who contributed to the ++ * current receive-queue. ++ */ ++ if (copied && after(tp->mptcp->last_data_seq, meta_tp->copied_seq - copied)) { ++ if (tp->srtt < min_time) { ++ min_time = tp->srtt; ++ subsk = sk; ++ max_data_seq_set = 0; ++ } ++ continue; ++ } ++ ++ if (!subsk && !max_data_seq_set) { ++ max_data_seq = tp->mptcp->last_data_seq; ++ max_data_seq_set = 1; ++ subsk = sk; ++ } ++ ++ /* Otherwise, take the one with the highest data_seq */ ++ if ((!subsk || max_data_seq_set) && ++ after(tp->mptcp->last_data_seq, max_data_seq)) { ++ max_data_seq = tp->mptcp->last_data_seq; ++ subsk = sk; ++ } ++ } ++ ++ if (!subsk) { ++ mptcp_debug("%s subsk is null, copied %d, cseq %u\n", __func__, ++ copied, meta_tp->copied_seq); ++ mptcp_for_each_sk(meta_tp->mpcb, sk) { ++ struct tcp_sock *tp = tcp_sk(sk); ++ mptcp_debug("%s pi %d state %u last_dseq %u\n", ++ __func__, tp->mptcp->path_index, sk->sk_state, ++ tp->mptcp->last_data_seq); ++ } ++ } ++ ++ return subsk; ++} ++EXPORT_SYMBOL(mptcp_select_ack_sock); ++ ++static void mptcp_sock_def_error_report(struct sock *sk) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; ++ ++ if (!sock_flag(sk, SOCK_DEAD)) ++ mptcp_sub_close(sk, 0); ++ ++ if (mpcb->infinite_mapping_rcv || mpcb->infinite_mapping_snd || ++ mpcb->send_infinite_mapping) { ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ ++ meta_sk->sk_err = sk->sk_err; ++ meta_sk->sk_err_soft = sk->sk_err_soft; ++ ++ if (!sock_flag(meta_sk, SOCK_DEAD)) ++ meta_sk->sk_error_report(meta_sk); ++ ++ tcp_done(meta_sk); ++ } ++ ++ sk->sk_err = 0; ++ return; ++} ++ ++static void mptcp_mpcb_put(struct mptcp_cb *mpcb) ++{ ++ if (atomic_dec_and_test(&mpcb->mpcb_refcnt)) { ++ mptcp_cleanup_path_manager(mpcb); ++ kmem_cache_free(mptcp_cb_cache, mpcb); ++ } ++} ++ ++static void mptcp_sock_destruct(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ inet_sock_destruct(sk); ++ ++ BUG_ON(!list_empty(&tp->mptcp->cb_list)); ++ ++ kmem_cache_free(mptcp_sock_cache, tp->mptcp); ++ tp->mptcp = NULL; ++ ++ if (!is_meta_sk(sk) && !tp->was_meta_sk) { ++ /* Taken when mpcb pointer was set */ ++ sock_put(mptcp_meta_sk(sk)); ++ mptcp_mpcb_put(tp->mpcb); ++ } else { ++ struct mptcp_cb *mpcb = tp->mpcb; ++ struct mptcp_tw *mptw; ++ ++ /* The mpcb is disappearing - we can make the final ++ * update to the rcv_nxt of the time-wait-sock and remove ++ * its reference to the mpcb. ++ */ ++ spin_lock_bh(&mpcb->tw_lock); ++ list_for_each_entry_rcu(mptw, &mpcb->tw_list, list) { ++ list_del_rcu(&mptw->list); ++ mptw->in_list = 0; ++ mptcp_mpcb_put(mpcb); ++ rcu_assign_pointer(mptw->mpcb, NULL); ++ } ++ spin_unlock_bh(&mpcb->tw_lock); ++ ++ mptcp_mpcb_put(mpcb); ++ ++ mptcp_debug("%s destroying meta-sk\n", __func__); ++ } ++} ++ ++void mptcp_destroy_sock(struct sock *sk) ++{ ++ if (is_meta_sk(sk)) { ++ struct sock *sk_it, *tmpsk; ++ ++ __skb_queue_purge(&tcp_sk(sk)->mpcb->reinject_queue); ++ mptcp_purge_ofo_queue(tcp_sk(sk)); ++ ++ /* We have to close all remaining subflows. Normally, they ++ * should all be about to get closed. But, if the kernel is ++ * forcing a closure (e.g., tcp_write_err), the subflows might ++ * not have been closed properly (as we are waiting for the ++ * DATA_ACK of the DATA_FIN). ++ */ ++ mptcp_for_each_sk_safe(tcp_sk(sk)->mpcb, sk_it, tmpsk) { ++ /* Already did call tcp_close - waiting for graceful ++ * closure, or if we are retransmitting fast-close on ++ * the subflow. The reset (or timeout) will kill the ++ * subflow.. ++ */ ++ if (tcp_sk(sk_it)->closing || ++ tcp_sk(sk_it)->send_mp_fclose) ++ continue; ++ ++ /* Allow the delayed work first to prevent time-wait state */ ++ if (delayed_work_pending(&tcp_sk(sk_it)->mptcp->work)) ++ continue; ++ ++ mptcp_sub_close(sk_it, 0); ++ } ++ } else { ++ mptcp_del_sock(sk); ++ } ++} ++ ++static void mptcp_set_state(struct sock *sk) ++{ ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ ++ /* Meta is not yet established - wake up the application */ ++ if ((1 << meta_sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV) && ++ sk->sk_state == TCP_ESTABLISHED) { ++ tcp_set_state(meta_sk, TCP_ESTABLISHED); ++ ++ if (!sock_flag(meta_sk, SOCK_DEAD)) { ++ meta_sk->sk_state_change(meta_sk); ++ sk_wake_async(meta_sk, SOCK_WAKE_IO, POLL_OUT); ++ } ++ } ++ ++ if (sk->sk_state == TCP_ESTABLISHED) { ++ tcp_sk(sk)->mptcp->establish_increased = 1; ++ tcp_sk(sk)->mpcb->cnt_established++; ++ } ++} ++ ++u32 mptcp_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned; ++u32 mptcp_key_seed = 0; ++ ++void mptcp_key_sha1(u64 key, u32 *token, u64 *idsn) ++{ ++ u32 workspace[SHA_WORKSPACE_WORDS]; ++ u32 mptcp_hashed_key[SHA_DIGEST_WORDS]; ++ u8 input[64]; ++ int i; ++ ++ memset(workspace, 0, sizeof(workspace)); ++ ++ /* Initialize input with appropriate padding */ ++ memset(&input[9], 0, sizeof(input) - 10); /* -10, because the last byte ++ * is explicitly set too */ ++ memcpy(input, &key, sizeof(key)); /* Copy key to the msg beginning */ ++ input[8] = 0x80; /* Padding: First bit after message = 1 */ ++ input[63] = 0x40; /* Padding: Length of the message = 64 bits */ ++ ++ sha_init(mptcp_hashed_key); ++ sha_transform(mptcp_hashed_key, input, workspace); ++ ++ for (i = 0; i < 5; i++) ++ mptcp_hashed_key[i] = cpu_to_be32(mptcp_hashed_key[i]); ++ ++ if (token) ++ *token = mptcp_hashed_key[0]; ++ if (idsn) ++ *idsn = *((u64 *)&mptcp_hashed_key[3]); ++} ++ ++void mptcp_hmac_sha1(u8 *key_1, u8 *key_2, u8 *rand_1, u8 *rand_2, ++ u32 *hash_out) ++{ ++ u32 workspace[SHA_WORKSPACE_WORDS]; ++ u8 input[128]; /* 2 512-bit blocks */ ++ int i; ++ ++ memset(workspace, 0, sizeof(workspace)); ++ ++ /* Generate key xored with ipad */ ++ memset(input, 0x36, 64); ++ for (i = 0; i < 8; i++) ++ input[i] ^= key_1[i]; ++ for (i = 0; i < 8; i++) ++ input[i + 8] ^= key_2[i]; ++ ++ memcpy(&input[64], rand_1, 4); ++ memcpy(&input[68], rand_2, 4); ++ input[72] = 0x80; /* Padding: First bit after message = 1 */ ++ memset(&input[73], 0, 53); ++ ++ /* Padding: Length of the message = 512 + 64 bits */ ++ input[126] = 0x02; ++ input[127] = 0x40; ++ ++ sha_init(hash_out); ++ sha_transform(hash_out, input, workspace); ++ memset(workspace, 0, sizeof(workspace)); ++ ++ sha_transform(hash_out, &input[64], workspace); ++ memset(workspace, 0, sizeof(workspace)); ++ ++ for (i = 0; i < 5; i++) ++ hash_out[i] = cpu_to_be32(hash_out[i]); ++ ++ /* Prepare second part of hmac */ ++ memset(input, 0x5C, 64); ++ for (i = 0; i < 8; i++) ++ input[i] ^= key_1[i]; ++ for (i = 0; i < 8; i++) ++ input[i + 8] ^= key_2[i]; ++ ++ memcpy(&input[64], hash_out, 20); ++ input[84] = 0x80; ++ memset(&input[85], 0, 41); ++ ++ /* Padding: Length of the message = 512 + 160 bits */ ++ input[126] = 0x02; ++ input[127] = 0xA0; ++ ++ sha_init(hash_out); ++ sha_transform(hash_out, input, workspace); ++ memset(workspace, 0, sizeof(workspace)); ++ ++ sha_transform(hash_out, &input[64], workspace); ++ ++ for (i = 0; i < 5; i++) ++ hash_out[i] = cpu_to_be32(hash_out[i]); ++} ++ ++static void mptcp_mpcb_inherit_sockopts(struct sock *meta_sk, struct sock *master_sk) ++{ ++ /* Socket-options handled by mptcp_inherit_sk while creating the meta-sk. ++ * ====== ++ * SO_SNDBUF, SO_SNDBUFFORCE, SO_RCVBUF, SO_RCVBUFFORCE, SO_RCVLOWAT, ++ * SO_RCVTIMEO, SO_SNDTIMEO, SO_ATTACH_FILTER, SO_DETACH_FILTER, ++ * TCP_NODELAY, TCP_CORK ++ * ++ * Socket-options handled in this function here ++ * ====== ++ * TCP_DEFER_ACCEPT ++ * ++ * Socket-options on the todo-list ++ * ====== ++ * SO_BINDTODEVICE - should probably prevent creation of new subsocks ++ * across other devices. - what about the api-draft? ++ * SO_DEBUG ++ * SO_REUSEADDR - probably we don't care about this ++ * SO_DONTROUTE, SO_BROADCAST ++ * SO_OOBINLINE ++ * SO_LINGER ++ * SO_TIMESTAMP* - I don't think this is of concern for a SOCK_STREAM ++ * SO_PASSSEC - I don't think this is of concern for a SOCK_STREAM ++ * SO_RXQ_OVFL ++ * TCP_COOKIE_TRANSACTIONS ++ * TCP_MAXSEG ++ * TCP_THIN_* - Handled by mptcp_inherit_sk, but we need to support this ++ * in mptcp_retransmit_timer. AND we need to check what is ++ * about the subsockets. ++ * TCP_LINGER2 ++ * TCP_WINDOW_CLAMP ++ * TCP_USER_TIMEOUT ++ * TCP_MD5SIG ++ * ++ * Socket-options of no concern for the meta-socket (but for the subsocket) ++ * ====== ++ * SO_PRIORITY ++ * SO_MARK ++ * TCP_CONGESTION ++ * TCP_SYNCNT ++ * TCP_QUICKACK ++ * SO_KEEPALIVE ++ */ ++ ++ /****** DEFER_ACCEPT-handler ******/ ++ ++ /* DEFER_ACCEPT is not of concern for new subflows - we always accept ++ * them ++ */ ++ inet_csk(meta_sk)->icsk_accept_queue.rskq_defer_accept = 0; ++} ++ ++static void mptcp_sub_inherit_sockopts(struct sock *meta_sk, struct sock *sub_sk) ++{ ++ /* IP_TOS also goes to the subflow. */ ++ if (inet_sk(sub_sk)->tos != inet_sk(meta_sk)->tos) { ++ inet_sk(sub_sk)->tos = inet_sk(meta_sk)->tos; ++ sub_sk->sk_priority = meta_sk->sk_priority; ++ sk_dst_reset(sub_sk); ++ } ++ ++ /* Inherit SO_REUSEADDR */ ++ sub_sk->sk_reuse = meta_sk->sk_reuse; ++ ++ /* Inherit snd/rcv-buffer locks */ ++ sub_sk->sk_userlocks = meta_sk->sk_userlocks & ~SOCK_BINDPORT_LOCK; ++} ++ ++int mptcp_backlog_rcv(struct sock *meta_sk, struct sk_buff *skb) ++{ ++ /* skb-sk may be NULL if we receive a packet immediatly after the ++ * SYN/ACK + MP_CAPABLE. ++ */ ++ struct sock *sk = skb->sk ? skb->sk : meta_sk; ++ int ret = 0; ++ ++ skb->sk = NULL; ++ ++ if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) { ++ kfree_skb(skb); ++ return 0; ++ } ++ ++ if (sk->sk_family == AF_INET) ++ ret = tcp_v4_do_rcv(sk, skb); ++#if IS_ENABLED(CONFIG_IPV6) ++ else ++ ret = tcp_v6_do_rcv(sk, skb); ++#endif ++ ++ sock_put(sk); ++ return ret; ++} ++ ++struct lock_class_key meta_key; ++struct lock_class_key meta_slock_key; ++ ++/* Code heavily inspired from sk_clone() */ ++static int mptcp_inherit_sk(const struct sock *sk, struct sock *newsk, ++ int family, const gfp_t flags) ++{ ++ struct sk_filter *filter; ++ struct proto *prot = newsk->sk_prot; ++ const struct inet_connection_sock_af_ops *af_ops = inet_csk(newsk)->icsk_af_ops; ++#ifdef CONFIG_SECURITY_NETWORK ++ void *sptr = newsk->sk_security; ++#endif ++ ++ if (sk->sk_family == AF_INET) { ++ memcpy(newsk, sk, offsetof(struct sock, sk_dontcopy_begin)); ++ memcpy(&newsk->sk_dontcopy_end, &sk->sk_dontcopy_end, ++ sizeof(struct tcp_sock) - offsetof(struct sock, sk_dontcopy_end)); ++ } else { ++ memcpy(newsk, sk, offsetof(struct sock, sk_dontcopy_begin)); ++ memcpy(&newsk->sk_dontcopy_end, &sk->sk_dontcopy_end, ++ sizeof(struct tcp6_sock) - offsetof(struct sock, sk_dontcopy_end)); ++ } ++ ++#ifdef CONFIG_SECURITY_NETWORK ++ newsk->sk_security = sptr; ++ security_sk_clone(sk, newsk); ++#endif ++ ++ /* Has been changed by sock_copy above - we may need an IPv6-socket */ ++ newsk->sk_family = family; ++ newsk->sk_prot = prot; ++ newsk->sk_prot_creator = prot; ++ inet_csk(newsk)->icsk_af_ops = af_ops; ++ ++ /* We don't yet have the mptcp-point. Thus we still need inet_sock_destruct */ ++ newsk->sk_destruct = inet_sock_destruct; ++ ++ /* SANITY */ ++ get_net(sock_net(newsk)); ++ sk_node_init(&newsk->sk_node); ++ sock_lock_init_class_and_name(newsk, "slock-AF_INET-MPTCP", ++ &meta_slock_key, "sk_lock-AF_INET-MPTCP", ++ &meta_key); ++ ++ /* Unlocks are in: ++ * ++ * 1. If we are creating the master-sk ++ * * on client-side in tcp_rcv_state_process, "case TCP_SYN_SENT" ++ * * on server-side in tcp_child_process ++ * 2. If we are creating another subsock ++ * * Also in tcp_child_process ++ */ ++ bh_lock_sock(newsk); ++ newsk->sk_backlog.head = NULL; ++ newsk->sk_backlog.tail = NULL; ++ newsk->sk_backlog.len = 0; ++ ++ atomic_set(&newsk->sk_rmem_alloc, 0); ++ atomic_set(&newsk->sk_wmem_alloc, 1); ++ atomic_set(&newsk->sk_omem_alloc, 0); ++ ++ skb_queue_head_init(&newsk->sk_receive_queue); ++ skb_queue_head_init(&newsk->sk_write_queue); ++#ifdef CONFIG_NET_DMA ++ skb_queue_head_init(&newsk->sk_async_wait_queue); ++#endif ++ ++ spin_lock_init(&newsk->sk_dst_lock); ++ rwlock_init(&newsk->sk_callback_lock); ++ lockdep_set_class_and_name(&newsk->sk_callback_lock, ++ af_callback_keys + newsk->sk_family, ++ af_family_clock_key_strings[newsk->sk_family]); ++ newsk->sk_dst_cache = NULL; ++ newsk->sk_rx_dst = NULL; ++ newsk->sk_wmem_queued = 0; ++ newsk->sk_forward_alloc = 0; ++ newsk->sk_send_head = NULL; ++ newsk->sk_userlocks = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK; ++ ++ tcp_sk(newsk)->mptcp = NULL; ++ ++ sock_reset_flag(newsk, SOCK_DONE); ++ skb_queue_head_init(&newsk->sk_error_queue); ++ ++ filter = rcu_dereference_protected(newsk->sk_filter, 1); ++ if (filter != NULL) ++ sk_filter_charge(newsk, filter); ++ ++ if (unlikely(xfrm_sk_clone_policy(newsk))) { ++ /* It is still raw copy of parent, so invalidate ++ * destructor and make plain sk_free() ++ */ ++ newsk->sk_destruct = NULL; ++ bh_unlock_sock(newsk); ++ sk_free(newsk); ++ newsk = NULL; ++ return -ENOMEM; ++ } ++ ++ newsk->sk_err = 0; ++ newsk->sk_priority = 0; ++ /* Before updating sk_refcnt, we must commit prior changes to memory ++ * (Documentation/RCU/rculist_nulls.txt for details) ++ */ ++ smp_wmb(); ++ atomic_set(&newsk->sk_refcnt, 2); ++ ++ /* Increment the counter in the same struct proto as the master ++ * sock (sk_refcnt_debug_inc uses newsk->sk_prot->socks, that ++ * is the same as sk->sk_prot->socks, as this field was copied ++ * with memcpy). ++ * ++ * This _changes_ the previous behaviour, where ++ * tcp_create_openreq_child always was incrementing the ++ * equivalent to tcp_prot->socks (inet_sock_nr), so this have ++ * to be taken into account in all callers. -acme ++ */ ++ sk_refcnt_debug_inc(newsk); ++ sk_set_socket(newsk, NULL); ++ newsk->sk_wq = NULL; ++ ++ if (newsk->sk_prot->sockets_allocated) ++ percpu_counter_inc(newsk->sk_prot->sockets_allocated); ++ ++ if (sock_flag(newsk, SOCK_TIMESTAMP) || ++ sock_flag(newsk, SOCK_TIMESTAMPING_RX_SOFTWARE)) ++ net_enable_timestamp(); ++ ++ return 0; ++} ++ ++int mptcp_alloc_mpcb(struct sock *meta_sk, __u64 remote_key, u32 window) ++{ ++ struct mptcp_cb *mpcb; ++ struct sock *master_sk; ++ struct inet_connection_sock *master_icsk, *meta_icsk = inet_csk(meta_sk); ++ struct tcp_sock *master_tp, *meta_tp = tcp_sk(meta_sk); ++ struct sk_buff *skb, *tmp; ++ u64 idsn; ++ ++ master_sk = sk_prot_alloc(meta_sk->sk_prot, GFP_ATOMIC | __GFP_ZERO, ++ meta_sk->sk_family); ++ if (!master_sk) ++ return -ENOBUFS; ++ ++ master_tp = tcp_sk(master_sk); ++ master_icsk = inet_csk(master_sk); ++ ++ /* Need to set this here - it is needed by mptcp_inherit_sk */ ++ master_sk->sk_prot = meta_sk->sk_prot; ++ master_sk->sk_prot_creator = meta_sk->sk_prot; ++ master_icsk->icsk_af_ops = meta_icsk->icsk_af_ops; ++ ++ mpcb = kmem_cache_zalloc(mptcp_cb_cache, GFP_ATOMIC); ++ if (!mpcb) { ++ sk_free(master_sk); ++ return -ENOBUFS; ++ } ++ ++ /* master_sk inherits from meta_sk */ ++ if (mptcp_inherit_sk(meta_sk, master_sk, meta_sk->sk_family, GFP_ATOMIC)) { ++ kmem_cache_free(mptcp_cb_cache, mpcb); ++ return -ENOBUFS; ++ } ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ if (meta_icsk->icsk_af_ops == &ipv6_mapped) { ++ struct ipv6_pinfo *newnp, *np = inet6_sk(meta_sk); ++ ++ inet_sk(master_sk)->pinet6 = &((struct tcp6_sock *)master_sk)->inet6; ++ ++ newnp = inet6_sk(master_sk); ++ memcpy(newnp, np, sizeof(struct ipv6_pinfo)); ++ ++ newnp->ipv6_mc_list = NULL; ++ newnp->ipv6_ac_list = NULL; ++ newnp->ipv6_fl_list = NULL; ++ newnp->opt = NULL; ++ newnp->pktoptions = NULL; ++ (void)xchg(&newnp->rxpmtu, NULL); ++ } else if (meta_sk->sk_family == AF_INET6) { ++ struct ipv6_pinfo *newnp, *np = inet6_sk(meta_sk); ++ ++ inet_sk(master_sk)->pinet6 = &((struct tcp6_sock *)master_sk)->inet6; ++ ++ newnp = inet6_sk(master_sk); ++ memcpy(newnp, np, sizeof(struct ipv6_pinfo)); ++ ++ newnp->hop_limit = -1; ++ newnp->mcast_hops = IPV6_DEFAULT_MCASTHOPS; ++ newnp->mc_loop = 1; ++ newnp->pmtudisc = IPV6_PMTUDISC_WANT; ++ newnp->ipv6only = sock_net(master_sk)->ipv6.sysctl.bindv6only; ++ } ++#endif ++ ++ meta_tp->mptcp = kmem_cache_zalloc(mptcp_sock_cache, GFP_ATOMIC); ++ if (!meta_tp->mptcp) { ++ kmem_cache_free(mptcp_cb_cache, mpcb); ++ sk_free(master_sk); ++ return -ENOBUFS; ++ } ++ ++ INIT_LIST_HEAD(&meta_tp->mptcp->cb_list); ++ ++ /* Store the keys and generate the peer's token */ ++ mpcb->mptcp_loc_key = meta_tp->mptcp_loc_key; ++ mpcb->mptcp_loc_token = meta_tp->mptcp_loc_token; ++ ++ /* Generate Initial data-sequence-numbers */ ++ mptcp_key_sha1(mpcb->mptcp_loc_key, NULL, &idsn); ++ idsn = ntohll(idsn) + 1; ++ mpcb->snd_high_order[0] = idsn >> 32; ++ mpcb->snd_high_order[1] = mpcb->snd_high_order[0] - 1; ++ ++ meta_tp->write_seq = (u32)idsn; ++ meta_tp->snd_sml = meta_tp->write_seq; ++ meta_tp->snd_una = meta_tp->write_seq; ++ meta_tp->snd_nxt = meta_tp->write_seq; ++ meta_tp->pushed_seq = meta_tp->write_seq; ++ meta_tp->snd_up = meta_tp->write_seq; ++ ++ mpcb->mptcp_rem_key = remote_key; ++ mptcp_key_sha1(mpcb->mptcp_rem_key, &mpcb->mptcp_rem_token, &idsn); ++ idsn = ntohll(idsn) + 1; ++ mpcb->rcv_high_order[0] = idsn >> 32; ++ mpcb->rcv_high_order[1] = mpcb->rcv_high_order[0] + 1; ++ meta_tp->copied_seq = (u32) idsn; ++ meta_tp->rcv_nxt = (u32) idsn; ++ meta_tp->rcv_wup = (u32) idsn; ++ ++ meta_tp->snd_wl1 = meta_tp->rcv_nxt - 1; ++ meta_tp->snd_wnd = window; ++ meta_tp->retrans_stamp = 0; /* Set in tcp_connect() */ ++ ++ meta_tp->packets_out = 0; ++ meta_tp->mptcp->snt_isn = meta_tp->write_seq; /* Initial data-sequence-number */ ++ meta_icsk->icsk_probes_out = 0; ++ ++ /* Set mptcp-pointers */ ++ master_tp->mpcb = mpcb; ++ master_tp->meta_sk = meta_sk; ++ meta_tp->mpcb = mpcb; ++ meta_tp->meta_sk = meta_sk; ++ mpcb->meta_sk = meta_sk; ++ mpcb->master_sk = master_sk; ++ ++ set_mpc(meta_tp); ++ meta_tp->mptcp->attached = 0; ++ meta_tp->was_meta_sk = 0; ++ ++ /* Initialize the queues */ ++ skb_queue_head_init(&mpcb->reinject_queue); ++ skb_queue_head_init(&master_tp->out_of_order_queue); ++ tcp_prequeue_init(master_tp); ++ INIT_LIST_HEAD(&master_tp->tsq_node); ++ ++ master_tp->tsq_flags = 0; ++ ++ /* Copy the write-queue from the meta down to the master. ++ * This is necessary to get the SYN to the master-write-queue. ++ * No other data can be queued, before tcp_sendmsg waits for the ++ * connection to finish. ++ */ ++ skb_queue_walk_safe(&meta_sk->sk_write_queue, skb, tmp) { ++ skb_unlink(skb, &meta_sk->sk_write_queue); ++ skb_queue_tail(&master_sk->sk_write_queue, skb); ++ ++ master_sk->sk_wmem_queued += skb->truesize; ++ sk_mem_charge(master_sk, skb->truesize); ++ } ++ ++ meta_sk->sk_wmem_queued = 0; ++ meta_sk->sk_forward_alloc = 0; ++ ++ mutex_init(&mpcb->mpcb_mutex); ++ ++ /* Init the accept_queue structure, we support a queue of 32 pending ++ * connections, it does not need to be huge, since we only store here ++ * pending subflow creations. ++ */ ++ if (reqsk_queue_alloc(&meta_icsk->icsk_accept_queue, 32, GFP_ATOMIC)) { ++ inet_put_port(master_sk); ++ kmem_cache_free(mptcp_sock_cache, meta_tp->mptcp); ++ kmem_cache_free(mptcp_cb_cache, mpcb); ++ sk_free(master_sk); ++ reset_mpc(meta_tp); ++ return -ENOMEM; ++ } ++ ++ /* Redefine function-pointers as the meta-sk is now fully ready */ ++ meta_sk->sk_backlog_rcv = mptcp_backlog_rcv; ++ meta_sk->sk_destruct = mptcp_sock_destruct; ++ mpcb->syn_recv_sock = mptcp_syn_recv_sock; ++ ++ /* Meta-level retransmit timer */ ++ meta_icsk->icsk_rto *= 2; /* Double of initial - rto */ ++ ++ tcp_init_xmit_timers(master_sk); ++ /* Has been set for sending out the SYN */ ++ inet_csk_clear_xmit_timer(meta_sk, ICSK_TIME_RETRANS); ++ ++ if (!meta_tp->inside_tk_table) { ++ /* Adding the meta_tp in the token hashtable - coming from server-side */ ++ rcu_read_lock(); ++ spin_lock(&mptcp_tk_hashlock); ++ ++ __mptcp_hash_insert(meta_tp, mpcb->mptcp_loc_token); ++ ++ spin_unlock(&mptcp_tk_hashlock); ++ rcu_read_unlock(); ++ } ++ master_tp->inside_tk_table = 0; ++ ++ /* Init time-wait stuff */ ++ INIT_LIST_HEAD(&mpcb->tw_list); ++ spin_lock_init(&mpcb->tw_lock); ++ ++ INIT_LIST_HEAD(&mpcb->callback_list); ++ ++ mptcp_mpcb_inherit_sockopts(meta_sk, master_sk); ++ ++ mpcb->orig_sk_rcvbuf = meta_sk->sk_rcvbuf; ++ mpcb->orig_sk_sndbuf = meta_sk->sk_sndbuf; ++ mpcb->orig_window_clamp = meta_tp->window_clamp; ++ ++ /* The meta is directly linked - set refcnt to 1 */ ++ atomic_set(&mpcb->mpcb_refcnt, 1); ++ ++ mptcp_init_path_manager(mpcb); ++ ++ mptcp_debug("%s: created mpcb with token %#x\n", ++ __func__, mpcb->mptcp_loc_token); ++ ++ return 0; ++} ++ ++struct sock *mptcp_sk_clone(const struct sock *sk, int family, ++ const gfp_t priority) ++{ ++ struct sock *newsk = NULL; ++ ++ if (family == AF_INET && sk->sk_family == AF_INET) { ++ newsk = sk_prot_alloc(&tcp_prot, priority, family); ++ if (!newsk) ++ return NULL; ++ ++ /* Set these pointers - they are needed by mptcp_inherit_sk */ ++ newsk->sk_prot = &tcp_prot; ++ newsk->sk_prot_creator = &tcp_prot; ++ inet_csk(newsk)->icsk_af_ops = &ipv4_specific; ++ newsk->sk_family = AF_INET; ++ } ++#if IS_ENABLED(CONFIG_IPV6) ++ else { ++ newsk = sk_prot_alloc(&tcpv6_prot, priority, family); ++ if (!newsk) ++ return NULL; ++ ++ newsk->sk_prot = &tcpv6_prot; ++ newsk->sk_prot_creator = &tcpv6_prot; ++ if (family == AF_INET) ++ inet_csk(newsk)->icsk_af_ops = &ipv6_mapped; ++ else ++ inet_csk(newsk)->icsk_af_ops = &ipv6_specific; ++ newsk->sk_family = AF_INET6; ++ } ++#endif ++ ++ if (mptcp_inherit_sk(sk, newsk, family, priority)) ++ return NULL; ++ ++ return newsk; ++} ++ ++void mptcp_fallback_meta_sk(struct sock *meta_sk) ++{ ++ kfree(inet_csk(meta_sk)->icsk_accept_queue.listen_opt); ++ kmem_cache_free(mptcp_sock_cache, tcp_sk(meta_sk)->mptcp); ++ kmem_cache_free(mptcp_cb_cache, tcp_sk(meta_sk)->mpcb); ++} ++ ++int mptcp_add_sock(struct sock *meta_sk, struct sock *sk, u8 loc_id, u8 rem_id, ++ gfp_t flags) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ tp->mptcp = kmem_cache_zalloc(mptcp_sock_cache, flags); ++ if (!tp->mptcp) ++ return -ENOMEM; ++ ++ tp->mptcp->path_index = mptcp_set_new_pathindex(mpcb); ++ /* No more space for more subflows? */ ++ if (!tp->mptcp->path_index) { ++ kmem_cache_free(mptcp_sock_cache, tp->mptcp); ++ return -EPERM; ++ } ++ ++ INIT_LIST_HEAD(&tp->mptcp->cb_list); ++ ++ tp->mptcp->tp = tp; ++ tp->mpcb = mpcb; ++ tp->meta_sk = meta_sk; ++ set_mpc(tp); ++ tp->mptcp->loc_id = loc_id; ++ tp->mptcp->rem_id = rem_id; ++ tp->mptcp->last_rbuf_opti = tcp_time_stamp; ++ ++ /* The corresponding sock_put is in mptcp_sock_destruct(). It cannot be ++ * included in mptcp_del_sock(), because the mpcb must remain alive ++ * until the last subsocket is completely destroyed. ++ */ ++ sock_hold(meta_sk); ++ atomic_inc(&mpcb->mpcb_refcnt); ++ ++ tp->mptcp->next = mpcb->connection_list; ++ mpcb->connection_list = tp; ++ tp->mptcp->attached = 1; ++ ++ mpcb->cnt_subflows++; ++ atomic_add(atomic_read(&((struct sock *)tp)->sk_rmem_alloc), ++ &meta_sk->sk_rmem_alloc); ++ ++ mptcp_sub_inherit_sockopts(meta_sk, sk); ++ INIT_DELAYED_WORK(&tp->mptcp->work, mptcp_sub_close_wq); ++ ++ /* As we successfully allocated the mptcp_tcp_sock, we have to ++ * change the function-pointers here (for sk_destruct to work correctly) ++ */ ++ sk->sk_error_report = mptcp_sock_def_error_report; ++ sk->sk_data_ready = mptcp_data_ready; ++ sk->sk_write_space = mptcp_write_space; ++ sk->sk_state_change = mptcp_set_state; ++ sk->sk_destruct = mptcp_sock_destruct; ++ ++ if (sk->sk_family == AF_INET) ++ mptcp_debug("%s: token %#x pi %d, src_addr:%pI4:%d dst_addr:%pI4:%d, cnt_subflows now %d\n", ++ __func__ , mpcb->mptcp_loc_token, ++ tp->mptcp->path_index, ++ &((struct inet_sock *)tp)->inet_saddr, ++ ntohs(((struct inet_sock *)tp)->inet_sport), ++ &((struct inet_sock *)tp)->inet_daddr, ++ ntohs(((struct inet_sock *)tp)->inet_dport), ++ mpcb->cnt_subflows); ++#if IS_ENABLED(CONFIG_IPV6) ++ else ++ mptcp_debug("%s: token %#x pi %d, src_addr:%pI6:%d dst_addr:%pI6:%d, cnt_subflows now %d\n", ++ __func__ , mpcb->mptcp_loc_token, ++ tp->mptcp->path_index, &inet6_sk(sk)->saddr, ++ ntohs(((struct inet_sock *)tp)->inet_sport), ++ &sk->sk_v6_daddr, ++ ntohs(((struct inet_sock *)tp)->inet_dport), ++ mpcb->cnt_subflows); ++#endif ++ ++ return 0; ++} ++ ++void mptcp_del_sock(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk), *tp_prev; ++ struct mptcp_cb *mpcb; ++ ++ if (!tp->mptcp || !tp->mptcp->attached) ++ return; ++ ++ mpcb = tp->mpcb; ++ tp_prev = mpcb->connection_list; ++ ++ mptcp_debug("%s: Removing subsock tok %#x pi:%d state %d is_meta? %d\n", ++ __func__, mpcb->mptcp_loc_token, tp->mptcp->path_index, ++ sk->sk_state, is_meta_sk(sk)); ++ ++ if (tp_prev == tp) { ++ mpcb->connection_list = tp->mptcp->next; ++ } else { ++ for (; tp_prev && tp_prev->mptcp->next; tp_prev = tp_prev->mptcp->next) { ++ if (tp_prev->mptcp->next == tp) { ++ tp_prev->mptcp->next = tp->mptcp->next; ++ break; ++ } ++ } ++ } ++ mpcb->cnt_subflows--; ++ if (tp->mptcp->establish_increased) ++ mpcb->cnt_established--; ++ ++ tp->mptcp->next = NULL; ++ tp->mptcp->attached = 0; ++ mpcb->path_index_bits &= ~(1 << tp->mptcp->path_index); ++ ++ if (!skb_queue_empty(&sk->sk_write_queue)) ++ mptcp_reinject_data(sk, 0); ++ ++ if (is_master_tp(tp)) ++ mpcb->master_sk = NULL; ++ else if (tp->mptcp->pre_established) ++ sk_stop_timer(sk, &tp->mptcp->mptcp_ack_timer); ++ ++ rcu_assign_pointer(inet_sk(sk)->inet_opt, NULL); ++} ++ ++/* Updates the metasocket ULID/port data, based on the given sock. ++ * The argument sock must be the sock accessible to the application. ++ * In this function, we update the meta socket info, based on the changes ++ * in the application socket (bind, address allocation, ...) ++ */ ++void mptcp_update_metasocket(struct sock *sk, struct sock *meta_sk) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ union inet_addr addr; ++ int index; ++ ++ /* Get the index of the local address */ ++ if (sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(sk)) { ++ addr.ip = inet_sk(sk)->inet_saddr; ++ index = mpcb->pm_ops->get_local_index(AF_INET, &addr, sock_net(meta_sk)); ++ } else { ++ addr.in6 = inet6_sk(sk)->saddr; ++ index = mpcb->pm_ops->get_local_index(AF_INET6, &addr, sock_net(meta_sk)); ++ } ++ ++ if (sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(sk)) { ++ mptcp_v4_add_raddress(mpcb, ++ (struct in_addr *)&inet_sk(sk)->inet_daddr, ++ 0, 0); ++ if (index >= 0) ++ mptcp_v4_set_init_addr_bit(mpcb, inet_sk(sk)->inet_daddr, index); ++ } else { ++#if IS_ENABLED(CONFIG_IPV6) ++ mptcp_v6_add_raddress(mpcb, &sk->sk_v6_daddr, 0, 0); ++ if (index >= 0) ++ mptcp_v6_set_init_addr_bit(mpcb, &sk->sk_v6_daddr, index); ++#endif ++ } ++ ++ if (mpcb->pm_ops->new_session) ++ mpcb->pm_ops->new_session(meta_sk, index); ++ ++ tcp_sk(sk)->mptcp->send_mp_prio = tcp_sk(sk)->mptcp->low_prio; ++} ++ ++/* Clean up the receive buffer for full frames taken by the user, ++ * then send an ACK if necessary. COPIED is the number of bytes ++ * tcp_recvmsg has given to the user so far, it speeds up the ++ * calculation of whether or not we must ACK for the sake of ++ * a window update. ++ */ ++void mptcp_cleanup_rbuf(struct sock *meta_sk, int copied) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct sock *sk; ++ __u32 rcv_window_now = 0; ++ ++ if (copied > 0 && !(meta_sk->sk_shutdown & RCV_SHUTDOWN)) { ++ rcv_window_now = tcp_receive_window(meta_tp); ++ ++ if (2 * rcv_window_now > meta_tp->window_clamp) ++ rcv_window_now = 0; ++ } ++ ++ mptcp_for_each_sk(meta_tp->mpcb, sk) { ++ struct tcp_sock *tp = tcp_sk(sk); ++ const struct inet_connection_sock *icsk = inet_csk(sk); ++ ++ if (!mptcp_sk_can_send_ack(sk)) ++ continue; ++ ++ if (!inet_csk_ack_scheduled(sk)) ++ goto second_part; ++ /* Delayed ACKs frequently hit locked sockets during bulk ++ * receive. ++ */ ++ if (icsk->icsk_ack.blocked || ++ /* Once-per-two-segments ACK was not sent by tcp_input.c */ ++ tp->rcv_nxt - tp->rcv_wup > icsk->icsk_ack.rcv_mss || ++ /* If this read emptied read buffer, we send ACK, if ++ * connection is not bidirectional, user drained ++ * receive buffer and there was a small segment ++ * in queue. ++ */ ++ (copied > 0 && ++ ((icsk->icsk_ack.pending & ICSK_ACK_PUSHED2) || ++ ((icsk->icsk_ack.pending & ICSK_ACK_PUSHED) && ++ !icsk->icsk_ack.pingpong)) && ++ !atomic_read(&meta_sk->sk_rmem_alloc))) { ++ tcp_send_ack(sk); ++ continue; ++ } ++ ++second_part: ++ /* This here is the second part of tcp_cleanup_rbuf */ ++ if (rcv_window_now) { ++ __u32 new_window = tp->__select_window(sk); ++ ++ /* Send ACK now, if this read freed lots of space ++ * in our buffer. Certainly, new_window is new window. ++ * We can advertise it now, if it is not less than ++ * current one. ++ * "Lots" means "at least twice" here. ++ */ ++ if (new_window && new_window >= 2 * rcv_window_now) ++ tcp_send_ack(sk); ++ } ++ } ++} ++ ++static int mptcp_sub_send_fin(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct sk_buff *skb = tcp_write_queue_tail(sk); ++ int mss_now; ++ ++ /* Optimization, tack on the FIN if we have a queue of ++ * unsent frames. But be careful about outgoing SACKS ++ * and IP options. ++ */ ++ mss_now = tcp_current_mss(sk); ++ ++ if (tcp_send_head(sk) != NULL) { ++ TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN; ++ TCP_SKB_CB(skb)->end_seq++; ++ tp->write_seq++; ++ } else { ++ skb = alloc_skb_fclone(MAX_TCP_HEADER, GFP_ATOMIC); ++ if (!skb) ++ return 1; ++ ++ /* Reserve space for headers and prepare control bits. */ ++ skb_reserve(skb, MAX_TCP_HEADER); ++ /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */ ++ tcp_init_nondata_skb(skb, tp->write_seq, ++ TCPHDR_ACK | TCPHDR_FIN); ++ tcp_queue_skb(sk, skb); ++ } ++ __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF); ++ ++ return 0; ++} ++ ++void mptcp_sub_close_wq(struct work_struct *work) ++{ ++ struct mptcp_tcp_sock *mptcp = container_of(work, struct mptcp_tcp_sock, work.work); ++ struct tcp_sock *tp = mptcp->tp; ++ struct sock *sk = (struct sock *)tp; ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ ++ mutex_lock(&tp->mpcb->mpcb_mutex); ++ lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); ++ ++ if (sock_flag(sk, SOCK_DEAD)) ++ goto exit; ++ ++ /* We come from tcp_disconnect. We are sure that meta_sk is set */ ++ if (!tp->mpc) { ++ tp->closing = 1; ++ sock_rps_reset_flow(sk); ++ tcp_close(sk, 0); ++ goto exit; ++ } ++ ++ if (meta_sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == TCP_CLOSE) { ++ tp->closing = 1; ++ sock_rps_reset_flow(sk); ++ tcp_close(sk, 0); ++ } else if (tcp_close_state(sk)) { ++ sk->sk_shutdown |= SEND_SHUTDOWN; ++ tcp_send_fin(sk); ++ } ++ ++exit: ++ release_sock(meta_sk); ++ mutex_unlock(&tp->mpcb->mpcb_mutex); ++ sock_put(sk); ++} ++ ++void mptcp_sub_close(struct sock *sk, unsigned long delay) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct delayed_work *work = &tcp_sk(sk)->mptcp->work; ++ ++ /* We are already closing - e.g., call from sock_def_error_report upon ++ * tcp_disconnect in tcp_close. ++ */ ++ if (tp->closing) ++ return; ++ ++ /* Work already scheduled ? */ ++ if (work_pending(&work->work)) { ++ /* Work present - who will be first ? */ ++ if (jiffies + delay > work->timer.expires) ++ return; ++ ++ /* Try canceling - if it fails, work will be executed soon */ ++ if (!cancel_delayed_work(work)) ++ return; ++ sock_put(sk); ++ } ++ ++ if (!delay) { ++ unsigned char old_state = sk->sk_state; ++ ++ /* If we are in user-context we can directly do the closing ++ * procedure. No need to schedule a work-queue. ++ */ ++ if (!in_softirq()) { ++ if (sock_flag(sk, SOCK_DEAD)) ++ return; ++ ++ if (!tp->mpc) { ++ tp->closing = 1; ++ sock_rps_reset_flow(sk); ++ tcp_close(sk, 0); ++ return; ++ } ++ ++ if (mptcp_meta_sk(sk)->sk_shutdown == SHUTDOWN_MASK || ++ sk->sk_state == TCP_CLOSE) { ++ tp->closing = 1; ++ sock_rps_reset_flow(sk); ++ tcp_close(sk, 0); ++ } else if (tcp_close_state(sk)) { ++ sk->sk_shutdown |= SEND_SHUTDOWN; ++ tcp_send_fin(sk); ++ } ++ ++ return; ++ } ++ ++ /* We directly send the FIN. Because it may take so a long time, ++ * untile the work-queue will get scheduled... ++ * ++ * If mptcp_sub_send_fin returns 1, it failed and thus we reset ++ * the old state so that tcp_close will finally send the fin ++ * in user-context. ++ */ ++ if (!sk->sk_err && old_state != TCP_CLOSE && ++ tcp_close_state(sk) && mptcp_sub_send_fin(sk)) { ++ if (old_state == TCP_ESTABLISHED) ++ TCP_INC_STATS(sock_net(sk), TCP_MIB_CURRESTAB); ++ sk->sk_state = old_state; ++ } ++ } ++ ++ sock_hold(sk); ++ queue_delayed_work(mptcp_wq, work, delay); ++} ++ ++void mptcp_sub_force_close(struct sock *sk) ++{ ++ /* The below tcp_done may have freed the socket, if he is already dead. ++ * Thus, we are not allowed to access it afterwards. That's why ++ * we have to store the dead-state in this local variable. ++ */ ++ int sock_is_dead = sock_flag(sk, SOCK_DEAD); ++ ++ tcp_sk(sk)->mp_killed = 1; ++ ++ if (sk->sk_state != TCP_CLOSE) ++ tcp_done(sk); ++ ++ if (!sock_is_dead) ++ mptcp_sub_close(sk, 0); ++} ++EXPORT_SYMBOL(mptcp_sub_force_close); ++ ++/* Update the mpcb send window, based on the contributions ++ * of each subflow ++ */ ++void mptcp_update_sndbuf(struct mptcp_cb *mpcb) ++{ ++ struct sock *meta_sk = mpcb->meta_sk, *sk; ++ int new_sndbuf = 0, old_sndbuf = meta_sk->sk_sndbuf; ++ mptcp_for_each_sk(mpcb, sk) { ++ if (!mptcp_sk_can_send(sk)) ++ continue; ++ ++ new_sndbuf += sk->sk_sndbuf; ++ ++ if (new_sndbuf > sysctl_tcp_wmem[2] || new_sndbuf < 0) { ++ new_sndbuf = sysctl_tcp_wmem[2]; ++ break; ++ } ++ } ++ meta_sk->sk_sndbuf = max(min(new_sndbuf, sysctl_tcp_wmem[2]), meta_sk->sk_sndbuf); ++ ++ /* The subflow's call to sk_write_space in tcp_new_space ends up in ++ * mptcp_write_space. ++ * It has nothing to do with waking up the application. ++ * So, we do it here. ++ */ ++ if (old_sndbuf != meta_sk->sk_sndbuf) ++ meta_sk->sk_write_space(meta_sk); ++} ++ ++void mptcp_close(struct sock *meta_sk, long timeout) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct sock *sk_it, *tmpsk; ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ struct sk_buff *skb; ++ int data_was_unread = 0; ++ int state; ++ ++ mptcp_debug("%s: Close of meta_sk with tok %#x\n", ++ __func__, mpcb->mptcp_loc_token); ++ ++ mutex_lock(&mpcb->mpcb_mutex); ++ lock_sock(meta_sk); ++ ++ if (meta_tp->inside_tk_table) { ++ /* Detach the mpcb from the token hashtable */ ++ mptcp_hash_remove_bh(meta_tp); ++ reqsk_queue_destroy(&inet_csk(meta_sk)->icsk_accept_queue); ++ } ++ ++ meta_sk->sk_shutdown = SHUTDOWN_MASK; ++ /* We need to flush the recv. buffs. We do this only on the ++ * descriptor close, not protocol-sourced closes, because the ++ * reader process may not have drained the data yet! ++ */ ++ while ((skb = __skb_dequeue(&meta_sk->sk_receive_queue)) != NULL) { ++ u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq - ++ tcp_hdr(skb)->fin; ++ data_was_unread += len; ++ __kfree_skb(skb); ++ } ++ ++ sk_mem_reclaim(meta_sk); ++ ++ /* If socket has been already reset (e.g. in tcp_reset()) - kill it. */ ++ if (meta_sk->sk_state == TCP_CLOSE) { ++ mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { ++ if (tcp_sk(sk_it)->send_mp_fclose) ++ continue; ++ mptcp_sub_close(sk_it, 0); ++ } ++ goto adjudge_to_death; ++ } ++ ++ if (data_was_unread) { ++ /* Unread data was tossed, zap the connection. */ ++ NET_INC_STATS_USER(sock_net(meta_sk), LINUX_MIB_TCPABORTONCLOSE); ++ tcp_set_state(meta_sk, TCP_CLOSE); ++ tcp_send_active_reset(meta_sk, meta_sk->sk_allocation); ++ } else if (sock_flag(meta_sk, SOCK_LINGER) && !meta_sk->sk_lingertime) { ++ /* Check zero linger _after_ checking for unread data. */ ++ meta_sk->sk_prot->disconnect(meta_sk, 0); ++ NET_INC_STATS_USER(sock_net(meta_sk), LINUX_MIB_TCPABORTONDATA); ++ } else if (tcp_close_state(meta_sk)) { ++ mptcp_send_fin(meta_sk); ++ } else if (meta_tp->snd_una == meta_tp->write_seq) { ++ /* The DATA_FIN has been sent and acknowledged ++ * (e.g., by sk_shutdown). Close all the other subflows ++ */ ++ mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { ++ unsigned long delay = 0; ++ /* If we are the passive closer, don't trigger ++ * subflow-fin until the subflow has been finned ++ * by the peer. - thus we add a delay ++ */ ++ if (mpcb->passive_close && ++ sk_it->sk_state == TCP_ESTABLISHED) ++ delay = inet_csk(sk_it)->icsk_rto << 3; ++ ++ mptcp_sub_close(sk_it, delay); ++ } ++ } ++ ++ sk_stream_wait_close(meta_sk, timeout); ++ ++adjudge_to_death: ++ state = meta_sk->sk_state; ++ sock_hold(meta_sk); ++ sock_orphan(meta_sk); ++ ++ /* socket will be freed after mptcp_close - we have to prevent ++ * access from the subflows. ++ */ ++ mptcp_for_each_sk(mpcb, sk_it) { ++ /* Similar to sock_orphan, but we don't set it DEAD, because ++ * the callbacks are still set and must be called. ++ */ ++ write_lock_bh(&sk_it->sk_callback_lock); ++ sk_set_socket(sk_it, NULL); ++ sk_it->sk_wq = NULL; ++ write_unlock_bh(&sk_it->sk_callback_lock); ++ } ++ ++ /* It is the last release_sock in its life. It will remove backlog. */ ++ release_sock(meta_sk); ++ ++ /* Now socket is owned by kernel and we acquire BH lock ++ * to finish close. No need to check for user refs. ++ */ ++ local_bh_disable(); ++ bh_lock_sock(meta_sk); ++ WARN_ON(sock_owned_by_user(meta_sk)); ++ ++ percpu_counter_inc(meta_sk->sk_prot->orphan_count); ++ ++ /* Have we already been destroyed by a softirq or backlog? */ ++ if (state != TCP_CLOSE && meta_sk->sk_state == TCP_CLOSE) ++ goto out; ++ ++ /* This is a (useful) BSD violating of the RFC. There is a ++ * problem with TCP as specified in that the other end could ++ * keep a socket open forever with no application left this end. ++ * We use a 3 minute timeout (about the same as BSD) then kill ++ * our end. If they send after that then tough - BUT: long enough ++ * that we won't make the old 4*rto = almost no time - whoops ++ * reset mistake. ++ * ++ * Nope, it was not mistake. It is really desired behaviour ++ * f.e. on http servers, when such sockets are useless, but ++ * consume significant resources. Let's do it with special ++ * linger2 option. --ANK ++ */ ++ ++ if (meta_sk->sk_state == TCP_FIN_WAIT2) { ++ if (meta_tp->linger2 < 0) { ++ tcp_set_state(meta_sk, TCP_CLOSE); ++ tcp_send_active_reset(meta_sk, GFP_ATOMIC); ++ NET_INC_STATS_BH(sock_net(meta_sk), ++ LINUX_MIB_TCPABORTONLINGER); ++ } else { ++ const int tmo = tcp_fin_time(meta_sk); ++ ++ if (tmo > TCP_TIMEWAIT_LEN) { ++ inet_csk_reset_keepalive_timer(meta_sk, ++ tmo - TCP_TIMEWAIT_LEN); ++ } else { ++ tcp_time_wait(meta_sk, TCP_FIN_WAIT2, tmo); ++ goto out; ++ } ++ } ++ } ++ if (meta_sk->sk_state != TCP_CLOSE) { ++ sk_mem_reclaim(meta_sk); ++ if (tcp_too_many_orphans(meta_sk, 0)) { ++ if (net_ratelimit()) ++ pr_info("MPTCP: too many of orphaned sockets\n"); ++ tcp_set_state(meta_sk, TCP_CLOSE); ++ tcp_send_active_reset(meta_sk, GFP_ATOMIC); ++ NET_INC_STATS_BH(sock_net(meta_sk), ++ LINUX_MIB_TCPABORTONMEMORY); ++ } ++ } ++ ++ ++ if (meta_sk->sk_state == TCP_CLOSE) ++ inet_csk_destroy_sock(meta_sk); ++ /* Otherwise, socket is reprieved until protocol close. */ ++ ++out: ++ bh_unlock_sock(meta_sk); ++ local_bh_enable(); ++ mutex_unlock(&mpcb->mpcb_mutex); ++ sock_put(meta_sk); /* Taken by sock_hold */ ++} ++ ++void mptcp_disconnect(struct sock *sk) ++{ ++ struct sock *subsk, *tmpsk; ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ __skb_queue_purge(&tp->mpcb->reinject_queue); ++ ++ if (tp->inside_tk_table) { ++ mptcp_hash_remove_bh(tp); ++ reqsk_queue_destroy(&inet_csk(tp->meta_sk)->icsk_accept_queue); ++ } ++ ++ local_bh_disable(); ++ mptcp_for_each_sk_safe(tp->mpcb, subsk, tmpsk) { ++ /* The socket will get removed from the subsocket-list ++ * and made non-mptcp by setting mpc to 0. ++ * ++ * This is necessary, because tcp_disconnect assumes ++ * that the connection is completly dead afterwards. ++ * Thus we need to do a mptcp_del_sock. Due to this call ++ * we have to make it non-mptcp. ++ * ++ * We have to lock the socket, because we set mpc to 0. ++ * An incoming packet would take the subsocket's lock ++ * and go on into the receive-path. ++ * This would be a race. ++ */ ++ ++ bh_lock_sock(subsk); ++ mptcp_del_sock(subsk); ++ reset_mpc(tcp_sk(subsk)); ++ mptcp_sub_force_close(subsk); ++ bh_unlock_sock(subsk); ++ } ++ local_bh_enable(); ++ ++ tp->was_meta_sk = 1; ++ reset_mpc(tp); ++} ++ ++ ++/* Returns 1 if we should enable MPTCP for that socket. */ ++int mptcp_doit(struct sock *sk) ++{ ++ /* Do not allow MPTCP enabling if the MPTCP initialization failed */ ++ if (mptcp_init_failed) ++ return 0; ++ ++ if (sysctl_mptcp_enabled == MPTCP_APP && !tcp_sk(sk)->mptcp_enabled) ++ return 0; ++ ++ /* Socket may already be established (e.g., called from tcp_recvmsg) */ ++ if (tcp_sk(sk)->mpc || tcp_sk(sk)->request_mptcp) ++ return 1; ++ ++ /* Don't do mptcp over loopback */ ++ if (sk->sk_family == AF_INET && ++ (ipv4_is_loopback(inet_sk(sk)->inet_daddr) || ++ ipv4_is_loopback(inet_sk(sk)->inet_saddr))) ++ return 0; ++#if IS_ENABLED(CONFIG_IPV6) ++ if (sk->sk_family == AF_INET6 && ++ (ipv6_addr_loopback(&sk->sk_v6_daddr) || ++ ipv6_addr_loopback(&inet6_sk(sk)->saddr))) ++ return 0; ++#endif ++ if (mptcp_v6_is_v4_mapped(sk) && ++ ipv4_is_loopback(inet_sk(sk)->inet_saddr)) ++ return 0; ++ ++#ifdef CONFIG_TCP_MD5SIG ++ /* If TCP_MD5SIG is enabled, do not do MPTCP - there is no Option-Space */ ++ if (tcp_sk(sk)->af_specific->md5_lookup(sk, sk)) ++ return 0; ++#endif ++ ++ return 1; ++} ++ ++int mptcp_create_master_sk(struct sock *meta_sk, __u64 remote_key, u32 window) ++{ ++ struct tcp_sock *master_tp; ++ struct sock *master_sk; ++ ++ if (mptcp_alloc_mpcb(meta_sk, remote_key, window)) ++ goto err_alloc_mpcb; ++ ++ master_sk = tcp_sk(meta_sk)->mpcb->master_sk; ++ master_tp = tcp_sk(master_sk); ++ ++ if (mptcp_add_sock(meta_sk, master_sk, 0, 0, GFP_ATOMIC)) ++ goto err_add_sock; ++ ++ if (__inet_inherit_port(meta_sk, master_sk) < 0) ++ goto err_add_sock; ++ ++ meta_sk->sk_prot->unhash(meta_sk); ++ ++ if (master_sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(master_sk)) ++ __inet_hash_nolisten(master_sk, NULL); ++#if IS_ENABLED(CONFIG_IPV6) ++ else ++ __inet6_hash(master_sk, NULL); ++#endif ++ ++ master_tp->mptcp->init_rcv_wnd = master_tp->rcv_wnd; ++ ++ return 0; ++ ++err_add_sock: ++ mptcp_fallback_meta_sk(meta_sk); ++ ++ inet_csk_prepare_forced_close(master_sk); ++ tcp_done(master_sk); ++ inet_csk_prepare_forced_close(meta_sk); ++ tcp_done(meta_sk); ++ ++err_alloc_mpcb: ++ return -ENOBUFS; ++} ++ ++int mptcp_check_req_master(struct sock *sk, struct sock *child, ++ struct request_sock *req, ++ struct request_sock **prev, ++ struct mptcp_options_received *mopt) ++{ ++ struct tcp_sock *child_tp = tcp_sk(child); ++ struct sock *meta_sk = child; ++ struct mptcp_cb *mpcb; ++ struct mptcp_request_sock *mtreq; ++ ++ if (!tcp_rsk(req)->saw_mpc) ++ return 1; ++ ++ /* Just set this values to pass them to mptcp_alloc_mpcb */ ++ mtreq = mptcp_rsk(req); ++ child_tp->mptcp_loc_key = mtreq->mptcp_loc_key; ++ child_tp->mptcp_loc_token = mtreq->mptcp_loc_token; ++ ++ if (mptcp_create_master_sk(meta_sk, mtreq->mptcp_rem_key, ++ child_tp->snd_wnd)) ++ return -ENOBUFS; ++ ++ child = tcp_sk(child)->mpcb->master_sk; ++ child_tp = tcp_sk(child); ++ mpcb = child_tp->mpcb; ++ ++ child_tp->mptcp->snt_isn = tcp_rsk(req)->snt_isn; ++ child_tp->mptcp->rcv_isn = tcp_rsk(req)->rcv_isn; ++ ++ mpcb->dss_csum = mtreq->dss_csum; ++ mpcb->server_side = 1; ++ ++ /* Will be moved to ESTABLISHED by tcp_rcv_state_process() */ ++ mptcp_update_metasocket(child, meta_sk); ++ ++ /* Needs to be done here additionally, because when accepting a ++ * new connection we pass by __reqsk_free and not reqsk_free. ++ */ ++ mptcp_reqsk_remove_tk(req); ++ ++ /* Hold when creating the meta-sk in tcp_vX_syn_recv_sock. */ ++ sock_put(meta_sk); ++ ++ inet_csk_reqsk_queue_unlink(sk, req, prev); ++ inet_csk_reqsk_queue_removed(sk, req); ++ inet_csk_reqsk_queue_add(sk, req, meta_sk); ++ ++ return 0; ++} ++ ++struct sock *mptcp_check_req_child(struct sock *meta_sk, struct sock *child, ++ struct request_sock *req, ++ struct request_sock **prev, ++ struct mptcp_options_received *mopt) ++{ ++ struct tcp_sock *child_tp = tcp_sk(child); ++ struct mptcp_request_sock *mtreq = mptcp_rsk(req); ++ struct mptcp_cb *mpcb = mtreq->mpcb; ++ u8 hash_mac_check[20]; ++ ++ child_tp->inside_tk_table = 0; ++ ++ if (!mopt->join_ack) ++ goto teardown; ++ ++ mptcp_hmac_sha1((u8 *)&mpcb->mptcp_rem_key, ++ (u8 *)&mpcb->mptcp_loc_key, ++ (u8 *)&mtreq->mptcp_rem_nonce, ++ (u8 *)&mtreq->mptcp_loc_nonce, ++ (u32 *)hash_mac_check); ++ ++ if (memcmp(hash_mac_check, (char *)&mopt->mptcp_recv_mac, 20)) ++ goto teardown; ++ ++ /* Point it to the same struct socket and wq as the meta_sk */ ++ sk_set_socket(child, meta_sk->sk_socket); ++ child->sk_wq = meta_sk->sk_wq; ++ ++ if (mptcp_add_sock(meta_sk, child, mtreq->loc_id, mtreq->rem_id, GFP_ATOMIC)) { ++ reset_mpc(child_tp); /* Has been inherited, but now ++ * child_tp->mptcp is NULL ++ */ ++ /* TODO when we support acking the third ack for new subflows, ++ * we should silently discard this third ack, by returning NULL. ++ * ++ * Maybe, at the retransmission we will have enough memory to ++ * fully add the socket to the meta-sk. ++ */ ++ goto teardown; ++ } ++ ++ /* The child is a clone of the meta socket, we must now reset ++ * some of the fields ++ */ ++ child_tp->mptcp->rcv_low_prio = mtreq->low_prio; ++ ++ /* We should allow proper increase of the snd/rcv-buffers. Thus, we ++ * use the original values instead of the bloated up ones from the ++ * clone. ++ */ ++ child->sk_sndbuf = mpcb->orig_sk_sndbuf; ++ child->sk_rcvbuf = mpcb->orig_sk_rcvbuf; ++ ++ child_tp->mptcp->slave_sk = 1; ++ child_tp->mptcp->snt_isn = tcp_rsk(req)->snt_isn; ++ child_tp->mptcp->rcv_isn = tcp_rsk(req)->rcv_isn; ++ child_tp->mptcp->init_rcv_wnd = req->rcv_wnd; ++ ++ child_tp->tsq_flags = 0; ++ ++ /* Subflows do not use the accept queue, as they ++ * are attached immediately to the mpcb. ++ */ ++ inet_csk_reqsk_queue_drop(meta_sk, req, prev); ++ return child; ++ ++teardown: ++ /* Drop this request - sock creation failed. */ ++ inet_csk_reqsk_queue_drop(meta_sk, req, prev); ++ inet_csk_prepare_forced_close(child); ++ tcp_done(child); ++ return meta_sk; ++} ++ ++int mptcp_time_wait(struct sock *sk, struct tcp_timewait_sock *tw) ++{ ++ struct mptcp_tw *mptw; ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct mptcp_cb *mpcb = tp->mpcb; ++ ++ /* Alloc MPTCP-tw-sock */ ++ mptw = kmem_cache_alloc(mptcp_tw_cache, GFP_ATOMIC); ++ if (!mptw) ++ return -ENOBUFS; ++ ++ atomic_inc(&mpcb->mpcb_refcnt); ++ ++ tw->mptcp_tw = mptw; ++ mptw->loc_key = mpcb->mptcp_loc_key; ++ mptw->meta_tw = mpcb->in_time_wait; ++ if (mptw->meta_tw) { ++ mptw->rcv_nxt = mptcp_get_rcv_nxt_64(mptcp_meta_tp(tp)); ++ if (mpcb->mptw_state != TCP_TIME_WAIT) ++ mptw->rcv_nxt++; ++ } ++ rcu_assign_pointer(mptw->mpcb, mpcb); ++ ++ spin_lock(&mpcb->tw_lock); ++ list_add_rcu(&mptw->list, &tp->mpcb->tw_list); ++ mptw->in_list = 1; ++ spin_unlock(&mpcb->tw_lock); ++ ++ return 0; ++} ++ ++void mptcp_twsk_destructor(struct tcp_timewait_sock *tw) ++{ ++ struct mptcp_cb *mpcb; ++ ++ rcu_read_lock(); ++ mpcb = rcu_dereference(tw->mptcp_tw->mpcb); ++ ++ /* If we are still holding a ref to the mpcb, we have to remove ourself ++ * from the list and drop the ref properly. ++ */ ++ if (mpcb && atomic_inc_not_zero(&mpcb->mpcb_refcnt)) { ++ spin_lock(&mpcb->tw_lock); ++ if (tw->mptcp_tw->in_list) { ++ list_del_rcu(&tw->mptcp_tw->list); ++ tw->mptcp_tw->in_list = 0; ++ } ++ spin_unlock(&mpcb->tw_lock); ++ ++ /* Twice, because we increased it above */ ++ mptcp_mpcb_put(mpcb); ++ mptcp_mpcb_put(mpcb); ++ } ++ ++ rcu_read_unlock(); ++ ++ kmem_cache_free(mptcp_tw_cache, tw->mptcp_tw); ++} ++ ++/* Updates the rcv_nxt of the time-wait-socks and allows them to ack a ++ * data-fin. ++ */ ++void mptcp_update_tw_socks(const struct tcp_sock *tp, int state) ++{ ++ struct mptcp_tw *mptw; ++ ++ /* Used for sockets that go into tw after the meta ++ * (see mptcp_time_wait()) ++ */ ++ tp->mpcb->in_time_wait = 1; ++ tp->mpcb->mptw_state = state; ++ ++ /* Update the time-wait-sock's information */ ++ rcu_read_lock_bh(); ++ list_for_each_entry_rcu(mptw, &tp->mpcb->tw_list, list) { ++ mptw->meta_tw = 1; ++ mptw->rcv_nxt = mptcp_get_rcv_nxt_64(tp); ++ ++ /* We want to ack a DATA_FIN, but are yet in FIN_WAIT_2 - ++ * pretend as if the DATA_FIN has already reached us, that way ++ * the checks in tcp_timewait_state_process will be good as the ++ * DATA_FIN comes in. ++ */ ++ if (state != TCP_TIME_WAIT) ++ mptw->rcv_nxt++; ++ } ++ rcu_read_unlock_bh(); ++} ++ ++void mptcp_tsq_flags(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ ++ /* It will be handled as a regular deferred-call */ ++ if (is_meta_sk(sk)) ++ return; ++ ++ if (list_empty(&tp->mptcp->cb_list)) { ++ list_add(&tp->mptcp->cb_list, &tp->mpcb->callback_list); ++ /* We need to hold it here, as the sock_hold is not assured ++ * by the release_sock as it is done in regular TCP. ++ * ++ * The subsocket may get inet_csk_destroy'd while it is inside ++ * the callback_list. ++ */ ++ sock_hold(sk); ++ } ++ ++ if (!test_and_set_bit(MPTCP_SUB_DEFERRED, &tcp_sk(meta_sk)->tsq_flags)) ++ sock_hold(meta_sk); ++} ++ ++void mptcp_tsq_sub_deferred(struct sock *meta_sk) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct mptcp_tcp_sock *mptcp, *tmp; ++ ++ BUG_ON(!is_meta_sk(meta_sk) && !meta_tp->was_meta_sk); ++ ++ __sock_put(meta_sk); ++ list_for_each_entry_safe(mptcp, tmp, &meta_tp->mpcb->callback_list, cb_list) { ++ struct tcp_sock *tp = mptcp->tp; ++ struct sock *sk = (struct sock *)tp; ++ ++ list_del_init(&mptcp->cb_list); ++ sk->sk_prot->release_cb(sk); ++ /* Final sock_put (cfr. mptcp_tsq_flags */ ++ sock_put(sk); ++ } ++} ++ ++struct workqueue_struct *mptcp_wq; ++EXPORT_SYMBOL(mptcp_wq); ++ ++/* Output /proc/net/mptcp */ ++static int mptcp_pm_seq_show(struct seq_file *seq, void *v) ++{ ++ struct tcp_sock *meta_tp; ++ struct net *net = seq->private; ++ int i, n = 0; ++ ++ seq_printf(seq, " sl loc_tok rem_tok v6 " ++ "local_address " ++ "remote_address " ++ "st ns tx_queue rx_queue inode"); ++ seq_putc(seq, '\n'); ++ ++ for (i = 0; i < MPTCP_HASH_SIZE; i++) { ++ struct hlist_nulls_node *node; ++ rcu_read_lock_bh(); ++ hlist_nulls_for_each_entry_rcu(meta_tp, node, ++ &tk_hashtable[i], tk_table) { ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ struct sock *meta_sk = (struct sock *)meta_tp; ++ struct inet_sock *isk = inet_sk(meta_sk); ++ ++ if (!meta_tp->mpc || !net_eq(net, sock_net(meta_sk))) ++ continue; ++ ++ seq_printf(seq, "%4d: %04X %04X ", n++, ++ mpcb->mptcp_loc_token, ++ mpcb->mptcp_rem_token); ++ if (meta_sk->sk_family == AF_INET || ++ mptcp_v6_is_v4_mapped(meta_sk)) { ++ seq_printf(seq, " 0 %08X:%04X %08X:%04X ", ++ isk->inet_saddr, ++ ntohs(isk->inet_sport), ++ isk->inet_daddr, ++ ntohs(isk->inet_dport)); ++#if IS_ENABLED(CONFIG_IPV6) ++ } else if (meta_sk->sk_family == AF_INET6) { ++ struct in6_addr *src = &isk->pinet6->saddr; ++ struct in6_addr *dst = &meta_sk->sk_v6_daddr; ++ seq_printf(seq, " 1 %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X", ++ src->s6_addr32[0], src->s6_addr32[1], ++ src->s6_addr32[2], src->s6_addr32[3], ++ ntohs(isk->inet_sport), ++ dst->s6_addr32[0], dst->s6_addr32[1], ++ dst->s6_addr32[2], dst->s6_addr32[3], ++ ntohs(isk->inet_dport)); ++#endif ++ } ++ seq_printf(seq, " %02X %02X %08X:%08X %lu", ++ meta_sk->sk_state, mpcb->cnt_subflows, ++ meta_tp->write_seq - meta_tp->snd_una, ++ max_t(int, meta_tp->rcv_nxt - ++ meta_tp->copied_seq, 0), ++ sock_i_ino(meta_sk)); ++ seq_putc(seq, '\n'); ++ } ++ rcu_read_unlock_bh(); ++ } ++ ++ return 0; ++} ++ ++static int mptcp_pm_seq_open(struct inode *inode, struct file *file) ++{ ++ return single_open_net(inode, file, mptcp_pm_seq_show); ++} ++ ++static const struct file_operations mptcp_pm_seq_fops = { ++ .owner = THIS_MODULE, ++ .open = mptcp_pm_seq_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release_net, ++}; ++ ++static int mptcp_pm_init_net(struct net *net) ++{ ++ if (!proc_create("mptcp", S_IRUGO, net->proc_net, &mptcp_pm_seq_fops)) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void mptcp_pm_exit_net(struct net *net) ++{ ++ remove_proc_entry("mptcp", net->proc_net); ++} ++ ++static struct pernet_operations mptcp_pm_proc_ops = { ++ .init = mptcp_pm_init_net, ++ .exit = mptcp_pm_exit_net, ++}; ++ ++/* General initialization of mptcp */ ++void __init mptcp_init(void) ++{ ++ int i; ++ struct ctl_table_header *mptcp_sysctl; ++ ++ mptcp_sock_cache = kmem_cache_create("mptcp_sock", ++ sizeof(struct mptcp_tcp_sock), ++ 0, SLAB_HWCACHE_ALIGN, ++ NULL); ++ if (!mptcp_sock_cache) ++ goto mptcp_sock_cache_failed; ++ ++ mptcp_cb_cache = kmem_cache_create("mptcp_cb", sizeof(struct mptcp_cb), ++ 0, SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, ++ NULL); ++ if (!mptcp_cb_cache) ++ goto mptcp_cb_cache_failed; ++ ++ mptcp_tw_cache = kmem_cache_create("mptcp_tw", sizeof(struct mptcp_tw), ++ 0, SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, ++ NULL); ++ if (!mptcp_tw_cache) ++ goto mptcp_tw_cache_failed; ++ ++ get_random_bytes(mptcp_secret, sizeof(mptcp_secret)); ++ ++ mptcp_wq = alloc_workqueue("mptcp_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 8); ++ if (!mptcp_wq) ++ goto alloc_workqueue_failed; ++ ++ for (i = 0; i < MPTCP_HASH_SIZE; i++) { ++ INIT_HLIST_NULLS_HEAD(&tk_hashtable[i], i); ++ INIT_LIST_HEAD(&mptcp_reqsk_htb[i]); ++ INIT_HLIST_NULLS_HEAD(&mptcp_reqsk_tk_htb[i], i); ++ } ++ ++ spin_lock_init(&mptcp_reqsk_hlock); ++ spin_lock_init(&mptcp_tk_hashlock); ++ ++ if (register_pernet_subsys(&mptcp_pm_proc_ops)) ++ goto pernet_failed; ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ if (mptcp_pm_v6_init()) ++ goto mptcp_pm_v6_failed; ++#endif ++ if (mptcp_pm_v4_init()) ++ goto mptcp_pm_v4_failed; ++ ++ mptcp_sysctl = register_net_sysctl(&init_net, "net/mptcp", mptcp_table); ++ if (!mptcp_sysctl) ++ goto register_sysctl_failed; ++ ++ if (mptcp_register_path_manager(&mptcp_pm_default)) ++ goto register_pm_failed; ++ ++ pr_info("MPTCP: Stable release v0.89.0-rc"); ++ ++ mptcp_init_failed = false; ++ ++ return; ++ ++register_pm_failed: ++ unregister_net_sysctl_table(mptcp_sysctl); ++register_sysctl_failed: ++ mptcp_pm_v4_undo(); ++mptcp_pm_v4_failed: ++#if IS_ENABLED(CONFIG_IPV6) ++ mptcp_pm_v6_undo(); ++mptcp_pm_v6_failed: ++#endif ++ unregister_pernet_subsys(&mptcp_pm_proc_ops); ++pernet_failed: ++ destroy_workqueue(mptcp_wq); ++alloc_workqueue_failed: ++ kmem_cache_destroy(mptcp_tw_cache); ++mptcp_tw_cache_failed: ++ kmem_cache_destroy(mptcp_cb_cache); ++mptcp_cb_cache_failed: ++ kmem_cache_destroy(mptcp_sock_cache); ++mptcp_sock_cache_failed: ++ mptcp_init_failed = true; ++} +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_fullmesh.c linux-3.14.45/net/mptcp/mptcp_fullmesh.c +--- linux-3.14.45.orig/net/mptcp/mptcp_fullmesh.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_fullmesh.c 2015-06-24 14:15:48.891862483 +0200 +@@ -0,0 +1,1313 @@ ++#include ++ ++#include ++#include ++ ++#if IS_ENABLED(CONFIG_IPV6) ++#include ++#include ++#endif ++ ++enum { ++ MPTCP_EVENT_ADD = 1, ++ MPTCP_EVENT_DEL, ++ MPTCP_EVENT_MOD, ++}; ++ ++struct mptcp_loc_addr { ++ struct mptcp_loc4 locaddr4[MPTCP_MAX_ADDR]; ++ u8 loc4_bits; ++ u8 next_v4_index; ++ ++ struct mptcp_loc6 locaddr6[MPTCP_MAX_ADDR]; ++ u8 loc6_bits; ++ u8 next_v6_index; ++}; ++ ++struct mptcp_addr_event { ++ struct list_head list; ++ unsigned short family; ++ u8 code:7, ++ low_prio:1; ++ union inet_addr addr; ++}; ++ ++struct fullmesh_priv { ++ /* Worker struct for subflow establishment */ ++ struct work_struct subflow_work; ++ /* Delayed worker, when the routing-tables are not yet ready. */ ++ struct delayed_work subflow_retry_work; ++ ++ struct mptcp_cb *mpcb; ++ ++ u16 remove_addrs; /* Addresses to remove */ ++ u8 announced_addrs_v4; /* IPv4 Addresses we did announce */ ++ u8 announced_addrs_v6; /* IPv4 Addresses we did announce */ ++ ++ u8 add_addr; /* Are we sending an add_addr? */ ++}; ++ ++struct mptcp_fm_ns { ++ struct mptcp_loc_addr __rcu *local; ++ spinlock_t local_lock; /* Protecting the above pointer */ ++ struct list_head events; ++ struct delayed_work address_worker; ++ ++ struct net *net; ++}; ++ ++static struct mptcp_pm_ops full_mesh __read_mostly; ++ ++static struct mptcp_fm_ns *fm_get_ns(struct net *net) ++{ ++ return (struct mptcp_fm_ns *)net->mptcp.path_managers[MPTCP_PM_FULLMESH]; ++} ++ ++static void full_mesh_create_subflows(struct sock *meta_sk); ++ ++static void retry_subflow_worker(struct work_struct *work) ++{ ++ struct delayed_work *delayed_work = container_of(work, ++ struct delayed_work, ++ work); ++ struct fullmesh_priv *pm_priv = container_of(delayed_work, ++ struct fullmesh_priv, ++ subflow_retry_work); ++ struct mptcp_cb *mpcb = pm_priv->mpcb; ++ struct sock *meta_sk = mpcb->meta_sk; ++ struct mptcp_loc_addr *mptcp_local; ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(meta_sk)); ++ int iter = 0, i; ++ ++ /* We need a local (stable) copy of the address-list. Really, it is not ++ * such a big deal, if the address-list is not 100% up-to-date. ++ */ ++ rcu_read_lock_bh(); ++ mptcp_local = rcu_dereference_bh(fm_ns->local); ++ mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), GFP_ATOMIC); ++ rcu_read_unlock_bh(); ++ ++ if (!mptcp_local) ++ return; ++ ++next_subflow: ++ if (iter) { ++ release_sock(meta_sk); ++ mutex_unlock(&mpcb->mpcb_mutex); ++ ++ yield(); ++ } ++ mutex_lock(&mpcb->mpcb_mutex); ++ lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); ++ ++ iter++; ++ ++ if (sock_flag(meta_sk, SOCK_DEAD)) ++ goto exit; ++ ++ mptcp_for_each_bit_set(mpcb->rem4_bits, i) { ++ struct mptcp_rem4 *rem = &mpcb->remaddr4[i]; ++ /* Do we need to retry establishing a subflow ? */ ++ if (rem->retry_bitfield) { ++ int i = mptcp_find_free_index(~rem->retry_bitfield); ++ ++ rem->bitfield |= (1 << i); ++ rem->retry_bitfield &= ~(1 << i); ++ ++ mptcp_init4_subsockets(meta_sk, &mptcp_local->locaddr4[i], rem); ++ goto next_subflow; ++ } ++ } ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ mptcp_for_each_bit_set(mpcb->rem6_bits, i) { ++ struct mptcp_rem6 *rem = &mpcb->remaddr6[i]; ++ ++ /* Do we need to retry establishing a subflow ? */ ++ if (rem->retry_bitfield) { ++ int i = mptcp_find_free_index(~rem->retry_bitfield); ++ ++ rem->bitfield |= (1 << i); ++ rem->retry_bitfield &= ~(1 << i); ++ ++ mptcp_init6_subsockets(meta_sk, &mptcp_local->locaddr6[i], rem); ++ goto next_subflow; ++ } ++ } ++#endif ++ ++exit: ++ kfree(mptcp_local); ++ release_sock(meta_sk); ++ mutex_unlock(&mpcb->mpcb_mutex); ++ sock_put(meta_sk); ++} ++ ++/** ++ * Create all new subflows, by doing calls to mptcp_initX_subsockets ++ * ++ * This function uses a goto next_subflow, to allow releasing the lock between ++ * new subflows and giving other processes a chance to do some work on the ++ * socket and potentially finishing the communication. ++ **/ ++static void create_subflow_worker(struct work_struct *work) ++{ ++ struct fullmesh_priv *pm_priv = container_of(work, ++ struct fullmesh_priv, ++ subflow_work); ++ struct mptcp_cb *mpcb = pm_priv->mpcb; ++ struct sock *meta_sk = mpcb->meta_sk; ++ struct mptcp_loc_addr *mptcp_local; ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(meta_sk)); ++ int iter = 0, retry = 0; ++ int i; ++ ++ /* We need a local (stable) copy of the address-list. Really, it is not ++ * such a big deal, if the address-list is not 100% up-to-date. ++ */ ++ rcu_read_lock_bh(); ++ mptcp_local = rcu_dereference_bh(fm_ns->local); ++ mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), GFP_ATOMIC); ++ rcu_read_unlock_bh(); ++ ++ if (!mptcp_local) ++ return; ++ ++next_subflow: ++ if (iter) { ++ release_sock(meta_sk); ++ mutex_unlock(&mpcb->mpcb_mutex); ++ ++ yield(); ++ } ++ mutex_lock(&mpcb->mpcb_mutex); ++ lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); ++ ++ iter++; ++ ++ if (sock_flag(meta_sk, SOCK_DEAD)) ++ goto exit; ++ ++ if (mpcb->master_sk && ++ !tcp_sk(mpcb->master_sk)->mptcp->fully_established) ++ goto exit; ++ ++ mptcp_for_each_bit_set(mpcb->rem4_bits, i) { ++ struct mptcp_rem4 *rem; ++ u8 remaining_bits; ++ ++ rem = &mpcb->remaddr4[i]; ++ remaining_bits = ~(rem->bitfield) & mptcp_local->loc4_bits; ++ ++ /* Are there still combinations to handle? */ ++ if (remaining_bits) { ++ int i = mptcp_find_free_index(~remaining_bits); ++ ++ rem->bitfield |= (1 << i); ++ ++ /* If a route is not yet available then retry once */ ++ if (mptcp_init4_subsockets(meta_sk, &mptcp_local->locaddr4[i], ++ rem) == -ENETUNREACH) ++ retry = rem->retry_bitfield |= (1 << i); ++ goto next_subflow; ++ } ++ } ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ mptcp_for_each_bit_set(mpcb->rem6_bits, i) { ++ struct mptcp_rem6 *rem; ++ u8 remaining_bits; ++ ++ rem = &mpcb->remaddr6[i]; ++ remaining_bits = ~(rem->bitfield) & mptcp_local->loc6_bits; ++ ++ /* Are there still combinations to handle? */ ++ if (remaining_bits) { ++ int i = mptcp_find_free_index(~remaining_bits); ++ ++ rem->bitfield |= (1 << i); ++ ++ /* If a route is not yet available then retry once */ ++ if (mptcp_init6_subsockets(meta_sk, &mptcp_local->locaddr6[i], ++ rem) == -ENETUNREACH) ++ retry = rem->retry_bitfield |= (1 << i); ++ goto next_subflow; ++ } ++ } ++#endif ++ ++ if (retry && !delayed_work_pending(&pm_priv->subflow_retry_work)) { ++ sock_hold(meta_sk); ++ queue_delayed_work(mptcp_wq, &pm_priv->subflow_retry_work, ++ msecs_to_jiffies(MPTCP_SUBFLOW_RETRY_DELAY)); ++ } ++ ++exit: ++ kfree(mptcp_local); ++ release_sock(meta_sk); ++ mutex_unlock(&mpcb->mpcb_mutex); ++ sock_put(meta_sk); ++} ++ ++static void update_remove_addrs(u8 addr_id, struct sock *meta_sk, ++ struct mptcp_loc_addr *mptcp_local) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct fullmesh_priv *fmp = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; ++ struct sock *sk; ++ int i; ++ ++ fmp->remove_addrs |= (1 << addr_id); ++ /* v4 goes from 0 to MPTCP_MAX_ADDR, v6 beyond */ ++ if (addr_id < MPTCP_MAX_ADDR) { ++ fmp->announced_addrs_v4 &= ~(1 << addr_id); ++ ++ mptcp_for_each_bit_set(mpcb->rem4_bits, i) { ++ mpcb->remaddr4[i].bitfield &= mptcp_local->loc4_bits; ++ mpcb->remaddr4[i].retry_bitfield &= mptcp_local->loc4_bits; ++ } ++ } else { ++ fmp->announced_addrs_v6 &= ~(1 << (addr_id - MPTCP_MAX_ADDR)); ++ ++ mptcp_for_each_bit_set(mpcb->rem6_bits, i) { ++ mpcb->remaddr6[i].bitfield &= mptcp_local->loc6_bits; ++ mpcb->remaddr6[i].retry_bitfield &= mptcp_local->loc6_bits; ++ } ++ } ++ ++ sk = mptcp_select_ack_sock(meta_sk, 0); ++ if (sk) ++ tcp_send_ack(sk); ++} ++ ++static int mptcp_find_address(struct mptcp_loc_addr *mptcp_local, ++ sa_family_t family, union inet_addr *addr) ++{ ++ int i; ++ u8 loc_bits; ++ bool found = false; ++ ++ if (family == AF_INET) ++ loc_bits = mptcp_local->loc4_bits; ++ else ++ loc_bits = mptcp_local->loc6_bits; ++ ++ mptcp_for_each_bit_set(loc_bits, i) { ++ if (family == AF_INET && ++ mptcp_local->locaddr4[i].addr.s_addr == addr->in.s_addr) { ++ found = true; ++ break; ++ } ++ if (family == AF_INET6 && ++ ipv6_addr_equal(&mptcp_local->locaddr6[i].addr, ++ &addr->in6)) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) ++ return -1; ++ ++ return i; ++} ++ ++static void mptcp_address_worker(struct work_struct *work) ++{ ++ struct delayed_work *delayed_work = container_of(work, ++ struct delayed_work, ++ work); ++ struct mptcp_fm_ns *fm_ns = container_of(delayed_work, ++ struct mptcp_fm_ns, ++ address_worker); ++ struct net *net = fm_ns->net; ++ struct mptcp_addr_event *event = NULL; ++ struct mptcp_loc_addr *mptcp_local, *old; ++ int i, id = -1; /* id is used in the socket-code on a delete-event */ ++ bool success; /* Used to indicate if we succeeded handling the event */ ++ ++next_event: ++ success = false; ++ kfree(event); ++ ++ /* First, let's dequeue an event from our event-list */ ++ rcu_read_lock_bh(); ++ spin_lock(&fm_ns->local_lock); ++ ++ event = list_first_entry_or_null(&fm_ns->events, ++ struct mptcp_addr_event, list); ++ if (!event) { ++ spin_unlock(&fm_ns->local_lock); ++ rcu_read_unlock_bh(); ++ return; ++ } ++ ++ list_del(&event->list); ++ ++ mptcp_local = rcu_dereference_bh(fm_ns->local); ++ ++ if (event->code == MPTCP_EVENT_DEL) { ++ id = mptcp_find_address(mptcp_local, event->family, &event->addr); ++ ++ /* Not in the list - so we don't care */ ++ if (id < 0) ++ goto duno; ++ ++ old = mptcp_local; ++ mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), ++ GFP_ATOMIC); ++ if (!mptcp_local) ++ goto duno; ++ ++ if (event->family == AF_INET) ++ mptcp_local->loc4_bits &= ~(1 << id); ++ else ++ mptcp_local->loc6_bits &= ~(1 << id); ++ ++ rcu_assign_pointer(fm_ns->local, mptcp_local); ++ kfree(old); ++ } else { ++ int i = mptcp_find_address(mptcp_local, event->family, &event->addr); ++ int j = i; ++ ++ if (j < 0) { ++ /* Not in the list, so we have to find an empty slot */ ++ if (event->family == AF_INET) ++ i = __mptcp_find_free_index(mptcp_local->loc4_bits, -1, ++ mptcp_local->next_v4_index); ++ if (event->family == AF_INET6) ++ i = __mptcp_find_free_index(mptcp_local->loc6_bits, -1, ++ mptcp_local->next_v6_index); ++ ++ if (i < 0) { ++ mptcp_debug("%s no more space\n", __func__); ++ goto duno; ++ } ++ ++ /* It might have been a MOD-event. */ ++ event->code = MPTCP_EVENT_ADD; ++ } else { ++ /* Let's check if anything changes */ ++ if (event->family == AF_INET && ++ event->low_prio == mptcp_local->locaddr4[i].low_prio) ++ goto duno; ++ ++ if (event->family == AF_INET6 && ++ event->low_prio == mptcp_local->locaddr6[i].low_prio) ++ goto duno; ++ } ++ ++ old = mptcp_local; ++ mptcp_local = kmemdup(mptcp_local, sizeof(*mptcp_local), ++ GFP_ATOMIC); ++ if (!mptcp_local) ++ goto duno; ++ ++ if (event->family == AF_INET) { ++ mptcp_local->locaddr4[i].addr.s_addr = event->addr.in.s_addr; ++ mptcp_local->locaddr4[i].loc4_id = i + 1; ++ mptcp_local->locaddr4[i].low_prio = event->low_prio; ++ } else { ++ mptcp_local->locaddr6[i].addr = event->addr.in6; ++ mptcp_local->locaddr6[i].loc6_id = i + MPTCP_MAX_ADDR; ++ mptcp_local->locaddr6[i].low_prio = event->low_prio; ++ } ++ ++ if (j < 0) { ++ if (event->family == AF_INET) { ++ mptcp_local->loc4_bits |= (1 << i); ++ mptcp_local->next_v4_index = i + 1; ++ } else { ++ mptcp_local->loc6_bits |= (1 << i); ++ mptcp_local->next_v6_index = i + 1; ++ } ++ } ++ ++ rcu_assign_pointer(fm_ns->local, mptcp_local); ++ kfree(old); ++ } ++ success = true; ++ ++duno: ++ spin_unlock(&fm_ns->local_lock); ++ rcu_read_unlock_bh(); ++ ++ if (!success) ++ goto next_event; ++ ++ /* Now we iterate over the MPTCP-sockets and apply the event. */ ++ for (i = 0; i < MPTCP_HASH_SIZE; i++) { ++ struct hlist_nulls_node *node; ++ struct tcp_sock *meta_tp; ++ ++ rcu_read_lock_bh(); ++ hlist_nulls_for_each_entry_rcu(meta_tp, node, &tk_hashtable[i], ++ tk_table) { ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ struct sock *meta_sk = (struct sock *)meta_tp, *sk; ++ struct fullmesh_priv *fmp = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; ++ ++ if (sock_net(meta_sk) != net) ++ continue; ++ ++ if (unlikely(!atomic_inc_not_zero(&meta_sk->sk_refcnt))) ++ continue; ++ ++ bh_lock_sock(meta_sk); ++ ++ if (!meta_tp->mpc || !is_meta_sk(meta_sk) || ++ mpcb->infinite_mapping_snd || ++ mpcb->infinite_mapping_rcv || ++ mpcb->send_infinite_mapping) ++ goto next; ++ ++ /* May be that the pm has changed in-between */ ++ if (mpcb->pm_ops != &full_mesh) ++ goto next; ++ ++ if (sock_owned_by_user(meta_sk)) { ++ if (!test_and_set_bit(MPTCP_PATH_MANAGER, ++ &meta_tp->tsq_flags)) ++ sock_hold(meta_sk); ++ ++ goto next; ++ } ++ ++ if (event->code == MPTCP_EVENT_ADD) { ++ if (event->family == AF_INET) ++ fmp->add_addr++; ++#if IS_ENABLED(CONFIG_IPV6) ++ if (event->family == AF_INET6) ++ fmp->add_addr++; ++#endif ++ ++ sk = mptcp_select_ack_sock(meta_sk, 0); ++ if (sk) ++ tcp_send_ack(sk); ++ ++ full_mesh_create_subflows(meta_sk); ++ } ++ ++ if (event->code == MPTCP_EVENT_DEL) { ++ struct sock *sk, *tmpsk; ++ struct mptcp_loc_addr *mptcp_local; ++ bool found = false; ++ ++ mptcp_local = rcu_dereference_bh(fm_ns->local); ++ ++ /* Look for the socket and remove him */ ++ mptcp_for_each_sk_safe(mpcb, sk, tmpsk) { ++ if ((event->family == AF_INET6 && ++ (sk->sk_family == AF_INET || ++ mptcp_v6_is_v4_mapped(sk))) || ++ (event->family == AF_INET && ++ (sk->sk_family == AF_INET6 && ++ !mptcp_v6_is_v4_mapped(sk)))) ++ continue; ++ ++ if (event->family == AF_INET && ++ (sk->sk_family == AF_INET || ++ mptcp_v6_is_v4_mapped(sk)) && ++ inet_sk(sk)->inet_saddr != event->addr.in.s_addr) ++ continue; ++ ++ if (event->family == AF_INET6 && ++ sk->sk_family == AF_INET6 && ++ !ipv6_addr_equal(&inet6_sk(sk)->saddr, &event->addr.in6)) ++ continue; ++ ++ /* Reinject, so that pf = 1 and so we ++ * won't select this one as the ++ * ack-sock. ++ */ ++ mptcp_reinject_data(sk, 0); ++ ++ /* A master is special, it has ++ * address-id 0 ++ */ ++ if (!tcp_sk(sk)->mptcp->loc_id) ++ update_remove_addrs(0, meta_sk, mptcp_local); ++ else if (tcp_sk(sk)->mptcp->loc_id != id) ++ update_remove_addrs(tcp_sk(sk)->mptcp->loc_id, meta_sk, mptcp_local); ++ ++ mptcp_sub_force_close(sk); ++ found = true; ++ } ++ ++ if (!found) ++ goto next; ++ ++ /* The id may have been given by the event, ++ * matching on a local address. And it may not ++ * have matched on one of the above sockets, ++ * because the client never created a subflow. ++ * So, we have to finally remove it here. ++ */ ++ if (id > 0) ++ update_remove_addrs(id, meta_sk, mptcp_local); ++ } ++ ++ if (event->code == MPTCP_EVENT_MOD) { ++ struct sock *sk; ++ ++ mptcp_for_each_sk(mpcb, sk) { ++ struct tcp_sock *tp = tcp_sk(sk); ++ if (event->family == AF_INET && ++ (sk->sk_family == AF_INET || ++ mptcp_v6_is_v4_mapped(sk)) && ++ inet_sk(sk)->inet_saddr == event->addr.in.s_addr) { ++ if (event->low_prio != tp->mptcp->low_prio) { ++ tp->mptcp->send_mp_prio = 1; ++ tp->mptcp->low_prio = event->low_prio; ++ ++ tcp_send_ack(sk); ++ } ++ } ++ ++ if (event->family == AF_INET6 && ++ sk->sk_family == AF_INET6 && ++ !ipv6_addr_equal(&inet6_sk(sk)->saddr, &event->addr.in6)) { ++ if (event->low_prio != tp->mptcp->low_prio) { ++ tp->mptcp->send_mp_prio = 1; ++ tp->mptcp->low_prio = event->low_prio; ++ ++ tcp_send_ack(sk); ++ } ++ } ++ } ++ } ++next: ++ bh_unlock_sock(meta_sk); ++ sock_put(meta_sk); ++ } ++ rcu_read_unlock_bh(); ++ } ++ goto next_event; ++} ++ ++static struct mptcp_addr_event *lookup_similar_event(struct net *net, ++ struct mptcp_addr_event *event) ++{ ++ struct mptcp_addr_event *eventq; ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); ++ ++ list_for_each_entry(eventq, &fm_ns->events, list) { ++ if (eventq->family != event->family) ++ continue; ++ if (event->family == AF_INET) { ++ if (eventq->addr.in.s_addr == event->addr.in.s_addr) ++ return eventq; ++ } else { ++ if (ipv6_addr_equal(&eventq->addr.in6, &event->addr.in6)) ++ return eventq; ++ } ++ } ++ return NULL; ++} ++ ++/* We already hold the net-namespace MPTCP-lock */ ++static void add_pm_event(struct net *net, struct mptcp_addr_event *event) ++{ ++ struct mptcp_addr_event *eventq = lookup_similar_event(net, event); ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); ++ ++ if (eventq) { ++ switch (event->code) { ++ case MPTCP_EVENT_DEL: ++ list_del(&eventq->list); ++ kfree(eventq); ++ break; ++ case MPTCP_EVENT_ADD: ++ eventq->low_prio = event->low_prio; ++ eventq->code = MPTCP_EVENT_ADD; ++ return; ++ case MPTCP_EVENT_MOD: ++ eventq->low_prio = event->low_prio; ++ return; ++ } ++ } ++ ++ /* OK, we have to add the new address to the wait queue */ ++ eventq = kmemdup(event, sizeof(struct mptcp_addr_event), GFP_ATOMIC); ++ if (!eventq) ++ return; ++ ++ list_add_tail(&eventq->list, &fm_ns->events); ++ ++ /* Create work-queue */ ++ if (!delayed_work_pending(&fm_ns->address_worker)) ++ queue_delayed_work(mptcp_wq, &fm_ns->address_worker, ++ msecs_to_jiffies(500)); ++} ++ ++static void addr4_event_handler(struct in_ifaddr *ifa, unsigned long event, ++ struct net *net) ++{ ++ struct net_device *netdev = ifa->ifa_dev->dev; ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); ++ struct mptcp_addr_event mpevent; ++ ++ if (ifa->ifa_scope > RT_SCOPE_LINK || ++ ipv4_is_loopback(ifa->ifa_local)) ++ return; ++ ++ spin_lock_bh(&fm_ns->local_lock); ++ ++ mpevent.family = AF_INET; ++ mpevent.addr.in.s_addr = ifa->ifa_local; ++ mpevent.low_prio = (netdev->flags & IFF_MPBACKUP) ? 1 : 0; ++ ++ if (event == NETDEV_DOWN || !netif_running(netdev) || ++ (netdev->flags & IFF_NOMULTIPATH)) ++ mpevent.code = MPTCP_EVENT_DEL; ++ else if (event == NETDEV_UP) ++ mpevent.code = MPTCP_EVENT_ADD; ++ else if (event == NETDEV_CHANGE) ++ mpevent.code = MPTCP_EVENT_MOD; ++ ++ add_pm_event(net, &mpevent); ++ ++ spin_unlock_bh(&fm_ns->local_lock); ++ return; ++} ++ ++/* React on IPv4-addr add/rem-events */ ++static int mptcp_pm_inetaddr_event(struct notifier_block *this, ++ unsigned long event, void *ptr) ++{ ++ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; ++ struct net *net = dev_net(ifa->ifa_dev->dev); ++ ++ addr4_event_handler(ifa, event, net); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block mptcp_pm_inetaddr_notifier = { ++ .notifier_call = mptcp_pm_inetaddr_event, ++}; ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ ++/* IPV6-related address/interface watchers */ ++struct mptcp_dad_data { ++ struct timer_list timer; ++ struct inet6_ifaddr *ifa; ++}; ++ ++static void dad_callback(unsigned long arg); ++static int inet6_addr_event(struct notifier_block *this, ++ unsigned long event, void *ptr); ++ ++static int ipv6_is_in_dad_state(struct inet6_ifaddr *ifa) ++{ ++ return ((ifa->flags & IFA_F_TENTATIVE) && ++ ifa->state == INET6_IFADDR_STATE_DAD); ++} ++ ++static void dad_init_timer(struct mptcp_dad_data *data, ++ struct inet6_ifaddr *ifa) ++{ ++ data->ifa = ifa; ++ data->timer.data = (unsigned long)data; ++ data->timer.function = dad_callback; ++ if (ifa->idev->cnf.rtr_solicit_delay) ++ data->timer.expires = jiffies + ifa->idev->cnf.rtr_solicit_delay; ++ else ++ data->timer.expires = jiffies + (HZ/10); ++} ++ ++static void dad_callback(unsigned long arg) ++{ ++ struct mptcp_dad_data *data = (struct mptcp_dad_data *)arg; ++ ++ if (ipv6_is_in_dad_state(data->ifa)) { ++ dad_init_timer(data, data->ifa); ++ add_timer(&data->timer); ++ } else { ++ inet6_addr_event(NULL, NETDEV_UP, data->ifa); ++ in6_ifa_put(data->ifa); ++ kfree(data); ++ } ++} ++ ++static inline void dad_setup_timer(struct inet6_ifaddr *ifa) ++{ ++ struct mptcp_dad_data *data; ++ ++ data = kmalloc(sizeof(*data), GFP_ATOMIC); ++ ++ if (!data) ++ return; ++ ++ init_timer(&data->timer); ++ dad_init_timer(data, ifa); ++ add_timer(&data->timer); ++ in6_ifa_hold(ifa); ++} ++ ++static void addr6_event_handler(struct inet6_ifaddr *ifa, unsigned long event, ++ struct net *net) ++{ ++ struct net_device *netdev = ifa->idev->dev; ++ int addr_type = ipv6_addr_type(&ifa->addr); ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); ++ struct mptcp_addr_event mpevent; ++ ++ if (ifa->scope > RT_SCOPE_LINK || ++ addr_type == IPV6_ADDR_ANY || ++ (addr_type & IPV6_ADDR_LOOPBACK) || ++ (addr_type & IPV6_ADDR_LINKLOCAL)) ++ return; ++ ++ spin_lock_bh(&fm_ns->local_lock); ++ ++ mpevent.family = AF_INET6; ++ mpevent.addr.in6 = ifa->addr; ++ mpevent.low_prio = (netdev->flags & IFF_MPBACKUP) ? 1 : 0; ++ ++ if (event == NETDEV_DOWN ||!netif_running(netdev) || ++ (netdev->flags & IFF_NOMULTIPATH)) ++ mpevent.code = MPTCP_EVENT_DEL; ++ else if (event == NETDEV_UP) ++ mpevent.code = MPTCP_EVENT_ADD; ++ else if (event == NETDEV_CHANGE) ++ mpevent.code = MPTCP_EVENT_MOD; ++ ++ add_pm_event(net, &mpevent); ++ ++ spin_unlock_bh(&fm_ns->local_lock); ++ return; ++} ++ ++/* React on IPv6-addr add/rem-events */ ++static int inet6_addr_event(struct notifier_block *this, unsigned long event, ++ void *ptr) ++{ ++ struct inet6_ifaddr *ifa6 = (struct inet6_ifaddr *)ptr; ++ struct net *net = dev_net(ifa6->idev->dev); ++ ++ if (ipv6_is_in_dad_state(ifa6)) ++ dad_setup_timer(ifa6); ++ else ++ addr6_event_handler(ifa6, event, net); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block inet6_addr_notifier = { ++ .notifier_call = inet6_addr_event, ++}; ++ ++#endif ++ ++/* React on ifup/down-events */ ++static int netdev_event(struct notifier_block *this, unsigned long event, ++ void *ptr) ++{ ++ struct net_device *dev = netdev_notifier_info_to_dev(ptr); ++ struct in_device *in_dev; ++#if IS_ENABLED(CONFIG_IPV6) ++ struct inet6_dev *in6_dev; ++#endif ++ ++ if (!(event == NETDEV_UP || event == NETDEV_DOWN || ++ event == NETDEV_CHANGE)) ++ return NOTIFY_DONE; ++ ++ rcu_read_lock(); ++ in_dev = __in_dev_get_rtnl(dev); ++ ++ if (in_dev) { ++ for_ifa(in_dev) { ++ mptcp_pm_inetaddr_event(NULL, event, ifa); ++ } endfor_ifa(in_dev); ++ } ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ in6_dev = __in6_dev_get(dev); ++ ++ if (in6_dev) { ++ struct inet6_ifaddr *ifa6; ++ list_for_each_entry(ifa6, &in6_dev->addr_list, if_list) ++ inet6_addr_event(NULL, event, ifa6); ++ } ++#endif ++ ++ rcu_read_unlock(); ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block mptcp_pm_netdev_notifier = { ++ .notifier_call = netdev_event, ++}; ++ ++static void full_mesh_new_session(struct sock *meta_sk, int index) ++{ ++ struct mptcp_loc_addr *mptcp_local; ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct fullmesh_priv *fmp = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; ++ struct net *net = sock_net(meta_sk); ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); ++ struct sock *sk; ++ int i; ++ ++ if (index == -1) { ++ mptcp_fallback_default(mpcb); ++ return; ++ } ++ ++ /* Initialize workqueue-struct */ ++ INIT_WORK(&fmp->subflow_work, create_subflow_worker); ++ INIT_DELAYED_WORK(&fmp->subflow_retry_work, retry_subflow_worker); ++ fmp->mpcb = mpcb; ++ ++ sk = mptcp_select_ack_sock(meta_sk, 0); ++ ++ rcu_read_lock(); ++ mptcp_local = rcu_dereference(fm_ns->local); ++ ++ /* Look for the address among the local addresses */ ++ mptcp_for_each_bit_set(mptcp_local->loc4_bits, i) { ++ __be32 ifa_address = mptcp_local->locaddr4[i].addr.s_addr; ++ ++ /* We do not need to announce the initial subflow's address again */ ++ if ((meta_sk->sk_family == AF_INET || ++ mptcp_v6_is_v4_mapped(meta_sk)) && ++ inet_sk(meta_sk)->inet_saddr == ifa_address) ++ continue; ++ ++ fmp->add_addr++; ++ ++ if (sk) ++ tcp_send_ack(sk); ++ } ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ mptcp_for_each_bit_set(mptcp_local->loc6_bits, i) { ++ struct in6_addr *ifa6 = &mptcp_local->locaddr6[i].addr; ++ ++ /* We do not need to announce the initial subflow's address again */ ++ if (meta_sk->sk_family == AF_INET6 && ++ ipv6_addr_equal(&inet6_sk(meta_sk)->saddr, ifa6)) ++ continue; ++ ++ fmp->add_addr++; ++ ++ if (sk) ++ tcp_send_ack(sk); ++ } ++#endif ++ ++ rcu_read_unlock(); ++ ++ if (meta_sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(meta_sk)) ++ fmp->announced_addrs_v4 |= (1 << index); ++ else ++ fmp->announced_addrs_v6 |= (1 << index); ++} ++ ++static void full_mesh_create_subflows(struct sock *meta_sk) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct fullmesh_priv *pm_priv = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; ++ ++ if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv || ++ mpcb->send_infinite_mapping || ++ mpcb->server_side || sock_flag(meta_sk, SOCK_DEAD)) ++ return; ++ ++ /* The master may not yet be fully established (address added through ++ * mptcp_update_metasocket). Then, we should not attempt to create new ++ * subflows. ++ */ ++ if (mpcb->master_sk && ++ !tcp_sk(mpcb->master_sk)->mptcp->fully_established) ++ return; ++ ++ if (!work_pending(&pm_priv->subflow_work)) { ++ sock_hold(meta_sk); ++ queue_work(mptcp_wq, &pm_priv->subflow_work); ++ } ++} ++ ++/* Called upon release_sock, if the socket was owned by the user during ++ * a path-management event. ++ */ ++static void full_mesh_release_sock(struct sock *meta_sk) ++{ ++ struct mptcp_loc_addr *mptcp_local; ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct fullmesh_priv *fmp = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(meta_sk)); ++ struct sock *sk, *tmpsk; ++ int i; ++ ++ rcu_read_lock(); ++ mptcp_local = rcu_dereference(fm_ns->local); ++ ++ /* First, detect modifications or additions */ ++ mptcp_for_each_bit_set(mptcp_local->loc4_bits, i) { ++ struct in_addr ifa = mptcp_local->locaddr4[i].addr; ++ bool found = false; ++ ++ mptcp_for_each_sk(mpcb, sk) { ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ if (sk->sk_family == AF_INET6 && ++ !mptcp_v6_is_v4_mapped(sk)) ++ continue; ++ ++ if (inet_sk(sk)->inet_saddr != ifa.s_addr) ++ continue; ++ ++ found = true; ++ ++ if (mptcp_local->locaddr4[i].low_prio != tp->mptcp->low_prio) { ++ tp->mptcp->send_mp_prio = 1; ++ tp->mptcp->low_prio = mptcp_local->locaddr4[i].low_prio; ++ ++ tcp_send_ack(sk); ++ } ++ } ++ ++ if (!found) { ++ fmp->add_addr++; ++ ++ sk = mptcp_select_ack_sock(meta_sk, 0); ++ if (sk) ++ tcp_send_ack(sk); ++ full_mesh_create_subflows(meta_sk); ++ } ++ } ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ mptcp_for_each_bit_set(mptcp_local->loc6_bits, i) { ++ struct in6_addr ifa = mptcp_local->locaddr6[i].addr; ++ bool found = false; ++ ++ mptcp_for_each_sk(mpcb, sk) { ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ if (sk->sk_family == AF_INET || ++ mptcp_v6_is_v4_mapped(sk)) ++ continue; ++ ++ if (!ipv6_addr_equal(&inet6_sk(sk)->saddr, &ifa)) ++ continue; ++ ++ found = true; ++ ++ if (mptcp_local->locaddr6[i].low_prio != tp->mptcp->low_prio) { ++ tp->mptcp->send_mp_prio = 1; ++ tp->mptcp->low_prio = mptcp_local->locaddr6[i].low_prio; ++ ++ tcp_send_ack(sk); ++ } ++ } ++ ++ if (!found) { ++ fmp->add_addr++; ++ ++ sk = mptcp_select_ack_sock(meta_sk, 0); ++ if (sk) ++ tcp_send_ack(sk); ++ full_mesh_create_subflows(meta_sk); ++ } ++ } ++#endif ++ ++ /* Now, detect address-removals */ ++ mptcp_for_each_sk_safe(mpcb, sk, tmpsk) { ++ bool shall_remove = true; ++ ++ if (sk->sk_family == AF_INET || mptcp_v6_is_v4_mapped(sk)) { ++ mptcp_for_each_bit_set(mptcp_local->loc4_bits, i) { ++ if (inet_sk(sk)->inet_saddr == mptcp_local->locaddr4[i].addr.s_addr) { ++ shall_remove = false; ++ break; ++ } ++ } ++ } else { ++ mptcp_for_each_bit_set(mptcp_local->loc6_bits, i) { ++ if (ipv6_addr_equal(&inet6_sk(sk)->saddr, &mptcp_local->locaddr6[i].addr)) { ++ shall_remove = false; ++ break; ++ } ++ } ++ } ++ ++ if (shall_remove) { ++ /* Reinject, so that pf = 1 and so we ++ * won't select this one as the ++ * ack-sock. ++ */ ++ mptcp_reinject_data(sk, 0); ++ ++ update_remove_addrs(tcp_sk(sk)->mptcp->loc_id, meta_sk, ++ mptcp_local); ++ ++ if (mpcb->master_sk == sk) ++ update_remove_addrs(0, meta_sk, mptcp_local); ++ ++ mptcp_sub_force_close(sk); ++ } ++ } ++ rcu_read_unlock(); ++} ++ ++static int full_mesh_get_local_index(sa_family_t family, union inet_addr *addr, ++ struct net *net) ++{ ++ struct mptcp_loc_addr *mptcp_local; ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); ++ int index; ++ ++ /* Handle the backup-flows */ ++ rcu_read_lock(); ++ mptcp_local = rcu_dereference(fm_ns->local); ++ ++ index = mptcp_find_address(mptcp_local, family, addr); ++ ++ rcu_read_unlock(); ++ ++ return index; ++} ++ ++static int full_mesh_get_local_id(sa_family_t family, union inet_addr *addr, ++ struct net *net) ++{ ++ struct mptcp_loc_addr *mptcp_local; ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(net); ++ int index, id = -1; ++ ++ /* Handle the backup-flows */ ++ rcu_read_lock(); ++ mptcp_local = rcu_dereference(fm_ns->local); ++ ++ index = mptcp_find_address(mptcp_local, family, addr); ++ ++ if (index != -1) { ++ if (family == AF_INET) ++ id = mptcp_local->locaddr4[index].loc4_id; ++ else ++ id = mptcp_local->locaddr6[index].loc6_id; ++ } ++ ++ ++ rcu_read_unlock(); ++ ++ return id; ++} ++ ++static void full_mesh_addr_signal(struct sock *sk, unsigned *size, ++ struct tcp_out_options *opts, ++ struct sk_buff *skb) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct mptcp_cb *mpcb = tp->mpcb; ++ struct fullmesh_priv *fmp = (struct fullmesh_priv *)&mpcb->mptcp_pm[0]; ++ struct mptcp_loc_addr *mptcp_local; ++ struct mptcp_fm_ns *fm_ns = fm_get_ns(sock_net(sk)); ++ int remove_addr_len; ++ u8 unannouncedv4, unannouncedv6; ++ ++ if (likely(!fmp->add_addr)) ++ goto remove_addr; ++ ++ rcu_read_lock(); ++ mptcp_local = rcu_dereference(fm_ns->local); ++ ++ /* IPv4 */ ++ unannouncedv4 = (~fmp->announced_addrs_v4) & mptcp_local->loc4_bits; ++ if (unannouncedv4 && ++ MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_ADD_ADDR4_ALIGN) { ++ int ind = mptcp_find_free_index(~unannouncedv4); ++ ++ opts->options |= OPTION_MPTCP; ++ opts->mptcp_options |= OPTION_ADD_ADDR; ++ opts->add_addr4.addr_id = mptcp_local->locaddr4[ind].loc4_id; ++ opts->add_addr4.addr = mptcp_local->locaddr4[ind].addr; ++ opts->add_addr_v4 = 1; ++ ++ if (skb) { ++ fmp->announced_addrs_v4 |= (1 << ind); ++ fmp->add_addr--; ++ } ++ *size += MPTCP_SUB_LEN_ADD_ADDR4_ALIGN; ++ } ++ ++ /* IPv6 */ ++ unannouncedv6 = (~fmp->announced_addrs_v6) & mptcp_local->loc6_bits; ++ if (unannouncedv6 && ++ MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_ADD_ADDR6_ALIGN) { ++ int ind = mptcp_find_free_index(~unannouncedv6); ++ ++ opts->options |= OPTION_MPTCP; ++ opts->mptcp_options |= OPTION_ADD_ADDR; ++ opts->add_addr6.addr_id = mptcp_local->locaddr6[ind].loc6_id; ++ opts->add_addr6.addr = mptcp_local->locaddr6[ind].addr; ++ opts->add_addr_v6 = 1; ++ ++ if (skb) { ++ fmp->announced_addrs_v6 |= (1 << ind); ++ fmp->add_addr--; ++ } ++ *size += MPTCP_SUB_LEN_ADD_ADDR6_ALIGN; ++ } ++ ++ rcu_read_unlock(); ++ ++ if (!unannouncedv4 && !unannouncedv6 && skb) { ++ fmp->add_addr--; ++ } ++ ++remove_addr: ++ if (likely(!fmp->remove_addrs)) ++ return; ++ ++ remove_addr_len = mptcp_sub_len_remove_addr_align(fmp->remove_addrs); ++ if (MAX_TCP_OPTION_SPACE - *size < remove_addr_len) ++ return; ++ ++ opts->options |= OPTION_MPTCP; ++ opts->mptcp_options |= OPTION_REMOVE_ADDR; ++ opts->remove_addrs = fmp->remove_addrs; ++ *size += remove_addr_len; ++ if (skb) ++ fmp->remove_addrs = 0; ++} ++ ++static int mptcp_fm_init_net(struct net *net) ++{ ++ struct mptcp_loc_addr *mptcp_local; ++ struct mptcp_fm_ns *fm_ns; ++ ++ fm_ns = kzalloc(sizeof(*fm_ns), GFP_KERNEL); ++ if (!fm_ns) ++ return -ENOBUFS; ++ ++ mptcp_local = kzalloc(sizeof(*mptcp_local), GFP_KERNEL); ++ if (!mptcp_local) { ++ kfree(fm_ns); ++ return -ENOBUFS; ++ } ++ ++ mptcp_local->next_v4_index = 1; ++ ++ rcu_assign_pointer(fm_ns->local, mptcp_local); ++ INIT_DELAYED_WORK(&fm_ns->address_worker, mptcp_address_worker); ++ INIT_LIST_HEAD(&fm_ns->events); ++ spin_lock_init(&fm_ns->local_lock); ++ fm_ns->net = net; ++ net->mptcp.path_managers[MPTCP_PM_FULLMESH] = fm_ns; ++ ++ return 0; ++} ++ ++static void mptcp_fm_exit_net(struct net *net) ++{ ++ struct mptcp_addr_event *eventq, *tmp; ++ struct mptcp_fm_ns *fm_ns; ++ struct mptcp_loc_addr *mptcp_local; ++ ++ fm_ns = fm_get_ns(net); ++ cancel_delayed_work_sync(&fm_ns->address_worker); ++ ++ rcu_read_lock_bh(); ++ ++ mptcp_local = rcu_dereference_bh(fm_ns->local); ++ kfree(mptcp_local); ++ ++ spin_lock(&fm_ns->local_lock); ++ list_for_each_entry_safe(eventq, tmp, &fm_ns->events, list) { ++ list_del(&eventq->list); ++ kfree(eventq); ++ } ++ spin_unlock(&fm_ns->local_lock); ++ ++ rcu_read_unlock_bh(); ++ ++ kfree(fm_ns); ++} ++ ++static struct pernet_operations full_mesh_net_ops = { ++ .init = mptcp_fm_init_net, ++ .exit = mptcp_fm_exit_net, ++}; ++ ++static struct mptcp_pm_ops full_mesh __read_mostly = { ++ .new_session = full_mesh_new_session, ++ .release_sock = full_mesh_release_sock, ++ .fully_established = full_mesh_create_subflows, ++ .new_remote_address = full_mesh_create_subflows, ++ .get_local_index = full_mesh_get_local_index, ++ .get_local_id = full_mesh_get_local_id, ++ .addr_signal = full_mesh_addr_signal, ++ .name = "fullmesh", ++ .owner = THIS_MODULE, ++}; ++ ++/* General initialization of MPTCP_PM */ ++static int __init full_mesh_register(void) ++{ ++ int ret; ++ ++ BUILD_BUG_ON(sizeof(struct fullmesh_priv) > MPTCP_PM_SIZE); ++ ++ ret = register_pernet_subsys(&full_mesh_net_ops); ++ if (ret) ++ goto out; ++ ++ ret = register_inetaddr_notifier(&mptcp_pm_inetaddr_notifier); ++ if (ret) ++ goto err_reg_inetaddr; ++ ret = register_netdevice_notifier(&mptcp_pm_netdev_notifier); ++ if (ret) ++ goto err_reg_netdev; ++ ++#if IS_ENABLED(CONFIG_IPV6) ++ ret = register_inet6addr_notifier(&inet6_addr_notifier); ++ if (ret) ++ goto err_reg_inet6addr; ++#endif ++ ++ ret = mptcp_register_path_manager(&full_mesh); ++ if (ret) ++ goto err_reg_pm; ++ ++out: ++ return ret; ++ ++ ++err_reg_pm: ++#if IS_ENABLED(CONFIG_IPV6) ++ unregister_inet6addr_notifier(&inet6_addr_notifier); ++err_reg_inet6addr: ++#endif ++ unregister_netdevice_notifier(&mptcp_pm_netdev_notifier); ++err_reg_netdev: ++ unregister_inetaddr_notifier(&mptcp_pm_inetaddr_notifier); ++err_reg_inetaddr: ++ unregister_pernet_subsys(&full_mesh_net_ops); ++ goto out; ++} ++ ++static void full_mesh_unregister(void) ++{ ++#if IS_ENABLED(CONFIG_IPV6) ++ unregister_inet6addr_notifier(&inet6_addr_notifier); ++#endif ++ unregister_netdevice_notifier(&mptcp_pm_netdev_notifier); ++ unregister_inetaddr_notifier(&mptcp_pm_inetaddr_notifier); ++ unregister_pernet_subsys(&full_mesh_net_ops); ++ mptcp_unregister_path_manager(&full_mesh); ++} ++ ++module_init(full_mesh_register); ++module_exit(full_mesh_unregister); ++ ++MODULE_AUTHOR("Christoph Paasch"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Full-Mesh MPTCP"); ++MODULE_VERSION("0.88"); +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_input.c linux-3.14.45/net/mptcp/mptcp_input.c +--- linux-3.14.45.orig/net/mptcp/mptcp_input.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_input.c 2015-06-24 14:15:48.895862487 +0200 +@@ -0,0 +1,2254 @@ ++/* ++ * MPTCP implementation - Sending side ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer & Author: ++ * Christoph Paasch ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++/* is seq1 < seq2 ? */ ++static inline int before64(const u64 seq1, const u64 seq2) ++{ ++ return (s64)(seq1 - seq2) < 0; ++} ++ ++/* is seq1 > seq2 ? */ ++#define after64(seq1, seq2) before64(seq2, seq1) ++ ++static inline void mptcp_become_fully_estab(struct sock *sk) ++{ ++ tcp_sk(sk)->mptcp->fully_established = 1; ++ ++ if (is_master_tp(tcp_sk(sk)) && ++ tcp_sk(sk)->mpcb->pm_ops->fully_established) ++ tcp_sk(sk)->mpcb->pm_ops->fully_established(mptcp_meta_sk(sk)); ++} ++ ++/* Similar to tcp_tso_acked without any memory accounting */ ++static inline int mptcp_tso_acked_reinject(struct sock *sk, struct sk_buff *skb) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ u32 packets_acked, len; ++ ++ BUG_ON(!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una)); ++ ++ packets_acked = tcp_skb_pcount(skb); ++ ++ if (skb_unclone(skb, GFP_ATOMIC)) ++ return 0; ++ ++ len = tp->snd_una - TCP_SKB_CB(skb)->seq; ++ __pskb_trim_head(skb, len); ++ ++ TCP_SKB_CB(skb)->seq += len; ++ skb->ip_summed = CHECKSUM_PARTIAL; ++ skb->truesize -= len; ++ ++ /* Any change of skb->len requires recalculation of tso factor. */ ++ if (tcp_skb_pcount(skb) > 1) ++ tcp_set_skb_tso_segs(sk, skb, tcp_skb_mss(skb)); ++ packets_acked -= tcp_skb_pcount(skb); ++ ++ if (packets_acked) { ++ BUG_ON(tcp_skb_pcount(skb) == 0); ++ BUG_ON(!before(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)); ++ } ++ ++ return packets_acked; ++} ++ ++/** ++ * Cleans the meta-socket retransmission queue and the reinject-queue. ++ * @sk must be the metasocket. ++ */ ++static void mptcp_clean_rtx_queue(struct sock *meta_sk, u32 prior_snd_una) ++{ ++ struct sk_buff *skb, *tmp; ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ bool acked = false; ++ u32 acked_pcount; ++ ++ while ((skb = tcp_write_queue_head(meta_sk)) && ++ skb != tcp_send_head(meta_sk)) { ++ bool fully_acked = true; ++ ++ if (before(meta_tp->snd_una, TCP_SKB_CB(skb)->end_seq)) { ++ if (tcp_skb_pcount(skb) == 1 || ++ !after(meta_tp->snd_una, TCP_SKB_CB(skb)->seq)) ++ break; ++ ++ acked_pcount = tcp_tso_acked(meta_sk, skb); ++ if (!acked_pcount) ++ break; ++ ++ fully_acked = false; ++ } else { ++ acked_pcount = tcp_skb_pcount(skb); ++ } ++ ++ acked = true; ++ meta_tp->packets_out -= acked_pcount; ++ meta_tp->retrans_stamp = 0; ++ ++ if (!fully_acked) ++ break; ++ ++ tcp_unlink_write_queue(skb, meta_sk); ++ ++ if (mptcp_is_data_fin(skb)) { ++ struct sock *sk_it; ++ ++ /* DATA_FIN has been acknowledged - now we can close ++ * the subflows ++ */ ++ mptcp_for_each_sk(mpcb, sk_it) { ++ unsigned long delay = 0; ++ ++ /* If we are the passive closer, don't trigger ++ * subflow-fin until the subflow has been finned ++ * by the peer - thus we add a delay. ++ */ ++ if (mpcb->passive_close && ++ sk_it->sk_state == TCP_ESTABLISHED) ++ delay = inet_csk(sk_it)->icsk_rto << 3; ++ ++ mptcp_sub_close(sk_it, delay); ++ } ++ } ++ sk_wmem_free_skb(meta_sk, skb); ++ } ++ /* Remove acknowledged data from the reinject queue */ ++ skb_queue_walk_safe(&mpcb->reinject_queue, skb, tmp) { ++ if (before(meta_tp->snd_una, TCP_SKB_CB(skb)->end_seq)) { ++ if (tcp_skb_pcount(skb) == 1 || ++ !after(meta_tp->snd_una, TCP_SKB_CB(skb)->seq)) ++ break; ++ ++ mptcp_tso_acked_reinject(meta_sk, skb); ++ break; ++ } ++ ++ __skb_unlink(skb, &mpcb->reinject_queue); ++ __kfree_skb(skb); ++ } ++ ++ if (likely(between(meta_tp->snd_up, prior_snd_una, meta_tp->snd_una))) ++ meta_tp->snd_up = meta_tp->snd_una; ++ ++ if (acked) { ++ tcp_rearm_rto(meta_sk); ++ /* Normally this is done in tcp_try_undo_loss - but MPTCP ++ * does not call this function. ++ */ ++ inet_csk(meta_sk)->icsk_retransmits = 0; ++ } ++} ++ ++/* Inspired by tcp_rcv_state_process */ ++static int mptcp_rcv_state_process(struct sock *meta_sk, struct sock *sk, ++ const struct sk_buff *skb, u32 data_seq, ++ u16 data_len) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk), *tp = tcp_sk(sk); ++ struct tcphdr *th = tcp_hdr(skb); ++ ++ /* State-machine handling if FIN has been enqueued and he has ++ * been acked (snd_una == write_seq) - it's important that this ++ * here is after sk_wmem_free_skb because otherwise ++ * sk_forward_alloc is wrong upon inet_csk_destroy_sock() ++ */ ++ switch (meta_sk->sk_state) { ++ case TCP_FIN_WAIT1: ++ if (meta_tp->snd_una == meta_tp->write_seq) { ++ struct dst_entry *dst = __sk_dst_get(meta_sk); ++ ++ tcp_set_state(meta_sk, TCP_FIN_WAIT2); ++ meta_sk->sk_shutdown |= SEND_SHUTDOWN; ++ ++ dst = __sk_dst_get(sk); ++ if (dst) ++ dst_confirm(dst); ++ ++ if (!sock_flag(meta_sk, SOCK_DEAD)) { ++ /* Wake up lingering close() */ ++ meta_sk->sk_state_change(meta_sk); ++ } else { ++ int tmo; ++ ++ if (meta_tp->linger2 < 0 || ++ (data_len && ++ after(data_seq + data_len - (mptcp_is_data_fin2(skb, tp) ? 1 : 0), ++ meta_tp->rcv_nxt))) { ++ mptcp_send_active_reset(meta_sk, GFP_ATOMIC); ++ tcp_done(meta_sk); ++ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_TCPABORTONDATA); ++ return 1; ++ } ++ ++ tmo = tcp_fin_time(meta_sk); ++ if (tmo > TCP_TIMEWAIT_LEN) { ++ inet_csk_reset_keepalive_timer(meta_sk, tmo - TCP_TIMEWAIT_LEN); ++ } else if (mptcp_is_data_fin2(skb, tp) || ++ sock_owned_by_user(meta_sk)) { ++ /* Bad case. We could lose such FIN otherwise. ++ * It is not a big problem, but it looks confusing ++ * and not so rare event. We still can lose it now, ++ * if it spins in bh_lock_sock(), but it is really ++ * marginal case. ++ */ ++ inet_csk_reset_keepalive_timer(meta_sk, tmo); ++ } else { ++ tcp_time_wait(meta_sk, TCP_FIN_WAIT2, tmo); ++ } ++ } ++ } ++ break; ++ case TCP_CLOSING: ++ case TCP_LAST_ACK: ++ if (meta_tp->snd_una == meta_tp->write_seq) { ++ tcp_done(meta_sk); ++ return 1; ++ } ++ break; ++ } ++ ++ /* step 7: process the segment text */ ++ switch (meta_sk->sk_state) { ++ case TCP_FIN_WAIT1: ++ case TCP_FIN_WAIT2: ++ /* RFC 793 says to queue data in these states, ++ * RFC 1122 says we MUST send a reset. ++ * BSD 4.4 also does reset. ++ */ ++ if (meta_sk->sk_shutdown & RCV_SHUTDOWN) { ++ if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && ++ after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt) && ++ !mptcp_is_data_fin2(skb, tp)) { ++ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_TCPABORTONDATA); ++ mptcp_send_active_reset(meta_sk, GFP_ATOMIC); ++ tcp_reset(meta_sk); ++ return 1; ++ } ++ } ++ break; ++ } ++ ++ return 0; ++} ++ ++/** ++ * @return: ++ * i) 1: Everything's fine. ++ * ii) -1: A reset has been sent on the subflow - csum-failure ++ * iii) 0: csum-failure but no reset sent, because it's the last subflow. ++ * Last packet should not be destroyed by the caller because it has ++ * been done here. ++ */ ++static int mptcp_verif_dss_csum(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct sk_buff *tmp, *tmp1, *last = NULL; ++ __wsum csum_tcp = 0; /* cumulative checksum of pld + mptcp-header */ ++ int ans = 1, overflowed = 0, offset = 0, dss_csum_added = 0; ++ int iter = 0; ++ ++ skb_queue_walk_safe(&sk->sk_receive_queue, tmp, tmp1) { ++ unsigned int csum_len; ++ ++ if (before(tp->mptcp->map_subseq + tp->mptcp->map_data_len, TCP_SKB_CB(tmp)->end_seq)) ++ /* Mapping ends in the middle of the packet - ++ * csum only these bytes ++ */ ++ csum_len = tp->mptcp->map_subseq + tp->mptcp->map_data_len - TCP_SKB_CB(tmp)->seq; ++ else ++ csum_len = tmp->len; ++ ++ offset = 0; ++ if (overflowed) { ++ char first_word[4]; ++ first_word[0] = 0; ++ first_word[1] = 0; ++ first_word[2] = 0; ++ first_word[3] = *(tmp->data); ++ csum_tcp = csum_partial(first_word, 4, csum_tcp); ++ offset = 1; ++ csum_len--; ++ overflowed = 0; ++ } ++ ++ csum_tcp = skb_checksum(tmp, offset, csum_len, csum_tcp); ++ ++ /* Was it on an odd-length? Then we have to merge the next byte ++ * correctly (see above) ++ */ ++ if (csum_len != (csum_len & (~1))) ++ overflowed = 1; ++ ++ if (mptcp_is_data_seq(tmp) && !dss_csum_added) { ++ __be32 data_seq = htonl((u32)(tp->mptcp->map_data_seq >> 32)); ++ ++ /* If a 64-bit dss is present, we increase the offset ++ * by 4 bytes, as the high-order 64-bits will be added ++ * in the final csum_partial-call. ++ */ ++ u32 offset = skb_transport_offset(tmp) + ++ TCP_SKB_CB(tmp)->dss_off; ++ if (TCP_SKB_CB(tmp)->mptcp_flags & MPTCPHDR_SEQ64_SET) ++ offset += 4; ++ ++ csum_tcp = skb_checksum(tmp, offset, ++ MPTCP_SUB_LEN_SEQ_CSUM, ++ csum_tcp); ++ ++ csum_tcp = csum_partial(&data_seq, ++ sizeof(data_seq), csum_tcp); ++ ++ dss_csum_added = 1; /* Just do it once */ ++ } ++ last = tmp; ++ iter++; ++ ++ if (!skb_queue_is_last(&sk->sk_receive_queue, tmp) && ++ !before(TCP_SKB_CB(tmp1)->seq, ++ tp->mptcp->map_subseq + tp->mptcp->map_data_len)) ++ break; ++ } ++ ++ /* Now, checksum must be 0 */ ++ if (unlikely(csum_fold(csum_tcp))) { ++ pr_err("%s csum is wrong: %#x data_seq %u dss_csum_added %d overflowed %d iterations %d\n", ++ __func__, csum_fold(csum_tcp), ++ TCP_SKB_CB(last)->seq, dss_csum_added, overflowed, ++ iter); ++ ++ tp->mptcp->send_mp_fail = 1; ++ ++ /* map_data_seq is the data-seq number of the ++ * mapping we are currently checking ++ */ ++ tp->mpcb->csum_cutoff_seq = tp->mptcp->map_data_seq; ++ ++ if (tp->mpcb->cnt_subflows > 1) { ++ mptcp_send_reset(sk); ++ ans = -1; ++ } else { ++ tp->mpcb->send_infinite_mapping = 1; ++ ++ /* Need to purge the rcv-queue as it's no more valid */ ++ while ((tmp = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { ++ tp->copied_seq = TCP_SKB_CB(tmp)->end_seq; ++ kfree_skb(tmp); ++ } ++ ++ ans = 0; ++ } ++ } ++ ++ return ans; ++} ++ ++static inline void mptcp_prepare_skb(struct sk_buff *skb, struct sk_buff *next, ++ struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); ++ /* Adapt data-seq's to the packet itself. We kinda transform the ++ * dss-mapping to a per-packet granularity. This is necessary to ++ * correctly handle overlapping mappings coming from different ++ * subflows. Otherwise it would be a complete mess. ++ */ ++ tcb->seq = ((u32)tp->mptcp->map_data_seq) + tcb->seq - tp->mptcp->map_subseq; ++ tcb->end_seq = tcb->seq + skb->len; ++ ++ /* If cur is the last one in the rcv-queue (or the last one for this ++ * mapping), and data_fin is enqueued, the end_data_seq is +1. ++ */ ++ if (skb_queue_is_last(&sk->sk_receive_queue, skb) || ++ after(TCP_SKB_CB(next)->end_seq, tp->mptcp->map_subseq + tp->mptcp->map_data_len)) { ++ tcb->end_seq += tp->mptcp->map_data_fin; ++ ++ /* We manually set the fin-flag if it is a data-fin. For easy ++ * processing in tcp_recvmsg. ++ */ ++ if (mptcp_is_data_fin2(skb, tp)) ++ tcp_hdr(skb)->fin = 1; ++ else ++ tcp_hdr(skb)->fin = 0; ++ } else { ++ /* We may have a subflow-fin with data but without data-fin */ ++ tcp_hdr(skb)->fin = 0; ++ } ++} ++ ++/** ++ * @return: 1 if the segment has been eaten and can be suppressed, ++ * otherwise 0. ++ */ ++static inline int mptcp_direct_copy(struct sk_buff *skb, struct sock *meta_sk) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ int chunk = min_t(unsigned int, skb->len, meta_tp->ucopy.len); ++ int eaten = 0; ++ ++ __set_current_state(TASK_RUNNING); ++ ++ local_bh_enable(); ++ if (!skb_copy_datagram_iovec(skb, 0, meta_tp->ucopy.iov, chunk)) { ++ meta_tp->ucopy.len -= chunk; ++ meta_tp->copied_seq += chunk; ++ eaten = (chunk == skb->len); ++ tcp_rcv_space_adjust(meta_sk); ++ } ++ local_bh_disable(); ++ return eaten; ++} ++ ++static inline void mptcp_reset_mapping(struct tcp_sock *tp) ++{ ++ tp->mptcp->map_data_len = 0; ++ tp->mptcp->map_data_seq = 0; ++ tp->mptcp->map_subseq = 0; ++ tp->mptcp->map_data_fin = 0; ++ tp->mptcp->mapping_present = 0; ++} ++ ++/* The DSS-mapping received on the sk only covers the second half of the skb ++ * (cut at seq). We trim the head from the skb. ++ * Data will be freed upon kfree(). ++ * ++ * Inspired by tcp_trim_head(). ++ */ ++static void mptcp_skb_trim_head(struct sk_buff *skb, struct sock *sk, u32 seq) ++{ ++ int len = seq - TCP_SKB_CB(skb)->seq; ++ u32 new_seq = TCP_SKB_CB(skb)->seq + len; ++ ++ if (len < skb_headlen(skb)) ++ __skb_pull(skb, len); ++ else ++ __pskb_trim_head(skb, len - skb_headlen(skb)); ++ ++ TCP_SKB_CB(skb)->seq = new_seq; ++ ++ skb->truesize -= len; ++ atomic_sub(len, &sk->sk_rmem_alloc); ++ sk_mem_uncharge(sk, len); ++} ++ ++/* The DSS-mapping received on the sk only covers the first half of the skb ++ * (cut at seq). We create a second skb (@return), and queue it in the rcv-queue ++ * as further packets may resolve the mapping of the second half of data. ++ * ++ * Inspired by tcp_fragment(). ++ */ ++static int mptcp_skb_split_tail(struct sk_buff *skb, struct sock *sk, u32 seq) ++{ ++ struct sk_buff *buff; ++ int nsize; ++ int nlen, len; ++ ++ len = seq - TCP_SKB_CB(skb)->seq; ++ nsize = skb_headlen(skb) - len + tcp_sk(sk)->tcp_header_len; ++ if (nsize < 0) ++ nsize = 0; ++ ++ /* Get a new skb... force flag on. */ ++ buff = alloc_skb(nsize, GFP_ATOMIC); ++ if (buff == NULL) ++ return -ENOMEM; ++ ++ skb_reserve(buff, tcp_sk(sk)->tcp_header_len); ++ skb_reset_transport_header(buff); ++ ++ tcp_hdr(buff)->fin = tcp_hdr(skb)->fin; ++ tcp_hdr(skb)->fin = 0; ++ ++ /* We absolutly need to call skb_set_owner_r before refreshing the ++ * truesize of buff, otherwise the moved data will account twice. ++ */ ++ skb_set_owner_r(buff, sk); ++ nlen = skb->len - len - nsize; ++ buff->truesize += nlen; ++ skb->truesize -= nlen; ++ ++ /* Correct the sequence numbers. */ ++ TCP_SKB_CB(buff)->seq = TCP_SKB_CB(skb)->seq + len; ++ TCP_SKB_CB(buff)->end_seq = TCP_SKB_CB(skb)->end_seq; ++ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq; ++ ++ skb_split(skb, buff, len); ++ ++ __skb_queue_after(&sk->sk_receive_queue, skb, buff); ++ ++ return 0; ++} ++ ++/* @return: 0 everything is fine. Just continue processing ++ * 1 subflow is broken stop everything ++ * -1 this packet was broken - continue with the next one. ++ */ ++static int mptcp_prevalidate_skb(struct sock *sk, struct sk_buff *skb) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ /* If we are in infinite mode, the subflow-fin is in fact a data-fin. */ ++ if (!skb->len && tcp_hdr(skb)->fin && !mptcp_is_data_fin(skb) && ++ !tp->mpcb->infinite_mapping_rcv) { ++ /* Remove a pure subflow-fin from the queue and increase ++ * copied_seq. ++ */ ++ tp->copied_seq = TCP_SKB_CB(skb)->end_seq; ++ __skb_unlink(skb, &sk->sk_receive_queue); ++ __kfree_skb(skb); ++ return -1; ++ } ++ ++ /* If we are not yet fully established and do not know the mapping for ++ * this segment, this path has to fallback to infinite or be torn down. ++ */ ++ if (!tp->mptcp->fully_established && !mptcp_is_data_seq(skb) && ++ !tp->mptcp->mapping_present && !tp->mpcb->infinite_mapping_rcv) { ++ pr_err("%s %#x will fallback - pi %d from %pS, seq %u\n", ++ __func__, tp->mpcb->mptcp_loc_token, ++ tp->mptcp->path_index, __builtin_return_address(0), ++ TCP_SKB_CB(skb)->seq); ++ ++ if (!is_master_tp(tp)) { ++ mptcp_send_reset(sk); ++ return 1; ++ } ++ ++ tp->mpcb->infinite_mapping_snd = 1; ++ tp->mpcb->infinite_mapping_rcv = 1; ++ tp->mptcp->fully_established = 1; ++ } ++ ++ /* Receiver-side becomes fully established when a whole rcv-window has ++ * been received without the need to fallback due to the previous ++ * condition. */ ++ if (!tp->mptcp->fully_established) { ++ tp->mptcp->init_rcv_wnd -= skb->len; ++ if (tp->mptcp->init_rcv_wnd < 0) ++ mptcp_become_fully_estab(sk); ++ } ++ ++ return 0; ++} ++ ++/* @return: 0 everything is fine. Just continue processing ++ * 1 subflow is broken stop everything ++ * -1 this packet was broken - continue with the next one. ++ */ ++static int mptcp_detect_mapping(struct sock *sk, struct sk_buff *skb) ++{ ++ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); ++ struct mptcp_cb *mpcb = tp->mpcb; ++ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); ++ u32 *ptr; ++ u32 data_seq, sub_seq, data_len, tcp_end_seq; ++ ++ /* If we are in infinite-mapping-mode, the subflow is guaranteed to be ++ * in-order at the data-level. Thus data-seq-numbers can be inferred ++ * from what is expected at the data-level. ++ */ ++ if (mpcb->infinite_mapping_rcv) { ++ tp->mptcp->map_data_seq = mptcp_get_rcv_nxt_64(meta_tp); ++ tp->mptcp->map_subseq = tcb->seq; ++ tp->mptcp->map_data_len = skb->len; ++ tp->mptcp->map_data_fin = tcp_hdr(skb)->fin; ++ tp->mptcp->mapping_present = 1; ++ return 0; ++ } ++ ++ /* No mapping here? Exit - it is either already set or still on its way */ ++ if (!mptcp_is_data_seq(skb)) { ++ /* Too many packets without a mapping - this subflow is broken */ ++ if (!tp->mptcp->mapping_present && ++ tp->rcv_nxt - tp->copied_seq > 65536) { ++ mptcp_send_reset(sk); ++ return 1; ++ } ++ ++ return 0; ++ } ++ ++ ptr = mptcp_skb_set_data_seq(skb, &data_seq, mpcb); ++ ptr++; ++ sub_seq = get_unaligned_be32(ptr) + tp->mptcp->rcv_isn; ++ ptr++; ++ data_len = get_unaligned_be16(ptr); ++ ++ /* If it's an empty skb with DATA_FIN, sub_seq must get fixed. ++ * The draft sets it to 0, but we really would like to have the ++ * real value, to have an easy handling afterwards here in this ++ * function. ++ */ ++ if (mptcp_is_data_fin(skb) && skb->len == 0) ++ sub_seq = TCP_SKB_CB(skb)->seq; ++ ++ /* If there is already a mapping - we check if it maps with the current ++ * one. If not - we reset. ++ */ ++ if (tp->mptcp->mapping_present && ++ (data_seq != (u32)tp->mptcp->map_data_seq || ++ sub_seq != tp->mptcp->map_subseq || ++ data_len != tp->mptcp->map_data_len + tp->mptcp->map_data_fin || ++ mptcp_is_data_fin(skb) != tp->mptcp->map_data_fin)) { ++ /* Mapping in packet is different from what we want */ ++ pr_err("%s Mappings do not match!\n", __func__); ++ pr_err("%s dseq %u mdseq %u, sseq %u msseq %u dlen %u mdlen %u dfin %d mdfin %d\n", ++ __func__, data_seq, (u32)tp->mptcp->map_data_seq, ++ sub_seq, tp->mptcp->map_subseq, data_len, ++ tp->mptcp->map_data_len, mptcp_is_data_fin(skb), ++ tp->mptcp->map_data_fin); ++ mptcp_send_reset(sk); ++ return 1; ++ } ++ ++ /* If the previous check was good, the current mapping is valid and we exit. */ ++ if (tp->mptcp->mapping_present) ++ return 0; ++ ++ /* Mapping not yet set on this subflow - we set it here! */ ++ ++ if (!data_len) { ++ mpcb->infinite_mapping_rcv = 1; ++ tp->mptcp->fully_established = 1; ++ /* We need to repeat mp_fail's until the sender felt ++ * back to infinite-mapping - here we stop repeating it. ++ */ ++ tp->mptcp->send_mp_fail = 0; ++ ++ /* We have to fixup data_len - it must be the same as skb->len */ ++ data_len = skb->len + (mptcp_is_data_fin(skb) ? 1 : 0); ++ sub_seq = tcb->seq; ++ ++ /* TODO kill all other subflows than this one */ ++ /* data_seq and so on are set correctly */ ++ ++ /* At this point, the meta-ofo-queue has to be emptied, ++ * as the following data is guaranteed to be in-order at ++ * the data and subflow-level ++ */ ++ mptcp_purge_ofo_queue(meta_tp); ++ } ++ ++ /* We are sending mp-fail's and thus are in fallback mode. ++ * Ignore packets which do not announce the fallback and still ++ * want to provide a mapping. ++ */ ++ if (tp->mptcp->send_mp_fail) { ++ tp->copied_seq = TCP_SKB_CB(skb)->end_seq; ++ __skb_unlink(skb, &sk->sk_receive_queue); ++ __kfree_skb(skb); ++ return -1; ++ } ++ ++ /* FIN increased the mapping-length by 1 */ ++ if (mptcp_is_data_fin(skb)) ++ data_len--; ++ ++ /* Subflow-sequences of packet must be ++ * (at least partially) be part of the DSS-mapping's ++ * subflow-sequence-space. ++ * ++ * Basically the mapping is not valid, if either of the ++ * following conditions is true: ++ * ++ * 1. It's not a data_fin and ++ * MPTCP-sub_seq >= TCP-end_seq ++ * ++ * 2. It's a data_fin and TCP-end_seq > TCP-seq and ++ * MPTCP-sub_seq >= TCP-end_seq ++ * ++ * The previous two can be merged into: ++ * TCP-end_seq > TCP-seq and MPTCP-sub_seq >= TCP-end_seq ++ * Because if it's not a data-fin, TCP-end_seq > TCP-seq ++ * ++ * 3. It's a data_fin and skb->len == 0 and ++ * MPTCP-sub_seq > TCP-end_seq ++ * ++ * 4. It's not a data_fin and TCP-end_seq > TCP-seq and ++ * MPTCP-sub_seq + MPTCP-data_len <= TCP-seq ++ * ++ * 5. MPTCP-sub_seq is prior to what we already copied (copied_seq) ++ */ ++ ++ /* subflow-fin is not part of the mapping - ignore it here ! */ ++ tcp_end_seq = tcb->end_seq - tcp_hdr(skb)->fin; ++ if ((!before(sub_seq, tcb->end_seq) && after(tcp_end_seq, tcb->seq)) || ++ (mptcp_is_data_fin(skb) && skb->len == 0 && after(sub_seq, tcb->end_seq)) || ++ (!after(sub_seq + data_len, tcb->seq) && after(tcp_end_seq, tcb->seq)) || ++ before(sub_seq, tp->copied_seq)) { ++ /* Subflow-sequences of packet is different from what is in the ++ * packet's dss-mapping. The peer is misbehaving - reset ++ */ ++ pr_err("%s Packet's mapping does not map to the DSS sub_seq %u " ++ "end_seq %u, tcp_end_seq %u seq %u dfin %u len %u data_len %u" ++ "copied_seq %u\n", __func__, sub_seq, tcb->end_seq, tcp_end_seq, tcb->seq, mptcp_is_data_fin(skb), ++ skb->len, data_len, tp->copied_seq); ++ mptcp_send_reset(sk); ++ return 1; ++ } ++ ++ /* Does the DSS had 64-bit seqnum's ? */ ++ if (!(tcb->mptcp_flags & MPTCPHDR_SEQ64_SET)) { ++ /* Wrapped around? */ ++ if (unlikely(after(data_seq, meta_tp->rcv_nxt) && data_seq < meta_tp->rcv_nxt)) { ++ tp->mptcp->map_data_seq = mptcp_get_data_seq_64(mpcb, !mpcb->rcv_hiseq_index, data_seq); ++ } else { ++ /* Else, access the default high-order bits */ ++ tp->mptcp->map_data_seq = mptcp_get_data_seq_64(mpcb, mpcb->rcv_hiseq_index, data_seq); ++ } ++ } else { ++ tp->mptcp->map_data_seq = mptcp_get_data_seq_64(mpcb, (tcb->mptcp_flags & MPTCPHDR_SEQ64_INDEX) ? 1 : 0, data_seq); ++ ++ if (unlikely(tcb->mptcp_flags & MPTCPHDR_SEQ64_OFO)) { ++ /* We make sure that the data_seq is invalid. ++ * It will be dropped later. ++ */ ++ tp->mptcp->map_data_seq += 0xFFFFFFFF; ++ tp->mptcp->map_data_seq += 0xFFFFFFFF; ++ } ++ } ++ ++ tp->mptcp->map_data_len = data_len; ++ tp->mptcp->map_subseq = sub_seq; ++ tp->mptcp->map_data_fin = mptcp_is_data_fin(skb) ? 1 : 0; ++ tp->mptcp->mapping_present = 1; ++ ++ return 0; ++} ++ ++/* Similar to tcp_sequence(...) */ ++static inline int mptcp_sequence(const struct tcp_sock *meta_tp, ++ u64 data_seq, u64 end_data_seq) ++{ ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ u64 rcv_wup64; ++ ++ /* Wrap-around? */ ++ if (meta_tp->rcv_wup > meta_tp->rcv_nxt) { ++ rcv_wup64 = ((u64)(mpcb->rcv_high_order[mpcb->rcv_hiseq_index] - 1) << 32) | ++ meta_tp->rcv_wup; ++ } else { ++ rcv_wup64 = mptcp_get_data_seq_64(mpcb, mpcb->rcv_hiseq_index, ++ meta_tp->rcv_wup); ++ } ++ ++ return !before64(end_data_seq, rcv_wup64) && ++ !after64(data_seq, mptcp_get_rcv_nxt_64(meta_tp) + tcp_receive_window(meta_tp)); ++} ++ ++/* @return: 0 everything is fine. Just continue processing ++ * -1 this packet was broken - continue with the next one. ++ */ ++static int mptcp_validate_mapping(struct sock *sk, struct sk_buff *skb) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct sk_buff *tmp, *tmp1; ++ u32 tcp_end_seq; ++ ++ if (!tp->mptcp->mapping_present) ++ return 0; ++ ++ /* either, the new skb gave us the mapping and the first segment ++ * in the sub-rcv-queue has to be trimmed ... ++ */ ++ tmp = skb_peek(&sk->sk_receive_queue); ++ if (before(TCP_SKB_CB(tmp)->seq, tp->mptcp->map_subseq) && ++ after(TCP_SKB_CB(tmp)->end_seq, tp->mptcp->map_subseq)) ++ mptcp_skb_trim_head(tmp, sk, tp->mptcp->map_subseq); ++ ++ /* ... or the new skb (tail) has to be split at the end. */ ++ tcp_end_seq = TCP_SKB_CB(skb)->end_seq - (tcp_hdr(skb)->fin ? 1 : 0); ++ if (after(tcp_end_seq, tp->mptcp->map_subseq + tp->mptcp->map_data_len)) { ++ u32 seq = tp->mptcp->map_subseq + tp->mptcp->map_data_len; ++ if (mptcp_skb_split_tail(skb, sk, seq)) { /* Allocation failed */ ++ /* TODO : maybe handle this here better. ++ * We now just force meta-retransmission. ++ */ ++ tp->copied_seq = TCP_SKB_CB(skb)->end_seq; ++ __skb_unlink(skb, &sk->sk_receive_queue); ++ __kfree_skb(skb); ++ return -1; ++ } ++ } ++ ++ /* Now, remove old sk_buff's from the receive-queue. ++ * This may happen if the mapping has been lost for these segments and ++ * the next mapping has already been received. ++ */ ++ if (tp->mptcp->mapping_present && ++ before(TCP_SKB_CB(skb_peek(&sk->sk_receive_queue))->seq, tp->mptcp->map_subseq)) { ++ skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { ++ if (!before(TCP_SKB_CB(tmp1)->seq, tp->mptcp->map_subseq)) ++ break; ++ ++ tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; ++ __skb_unlink(tmp1, &sk->sk_receive_queue); ++ ++ /* Impossible that we could free skb here, because his ++ * mapping is known to be valid from previous checks ++ */ ++ __kfree_skb(tmp1); ++ } ++ } ++ ++ return 0; ++} ++ ++/* @return: 0 everything is fine. Just continue processing ++ * 1 subflow is broken stop everything ++ * -1 this mapping has been put in the meta-receive-queue ++ * -2 this mapping has been eaten by the application ++ */ ++static int mptcp_queue_skb(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ struct mptcp_cb *mpcb = tp->mpcb; ++ struct sk_buff *tmp, *tmp1; ++ u64 rcv_nxt64 = mptcp_get_rcv_nxt_64(meta_tp); ++ bool data_queued = false; ++ ++ /* Have we not yet received the full mapping? */ ++ if (!tp->mptcp->mapping_present || ++ before(tp->rcv_nxt, tp->mptcp->map_subseq + tp->mptcp->map_data_len)) ++ return 0; ++ ++ /* Is this an overlapping mapping? rcv_nxt >= end_data_seq ++ * OR ++ * This mapping is out of window ++ */ ++ if (!before64(rcv_nxt64, tp->mptcp->map_data_seq + tp->mptcp->map_data_len + tp->mptcp->map_data_fin) || ++ !mptcp_sequence(meta_tp, tp->mptcp->map_data_seq, ++ tp->mptcp->map_data_seq + tp->mptcp->map_data_len + tp->mptcp->map_data_fin)) { ++ skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { ++ __skb_unlink(tmp1, &sk->sk_receive_queue); ++ tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; ++ __kfree_skb(tmp1); ++ ++ if (!skb_queue_empty(&sk->sk_receive_queue) && ++ !before(TCP_SKB_CB(tmp)->seq, ++ tp->mptcp->map_subseq + tp->mptcp->map_data_len)) ++ break; ++ } ++ ++ mptcp_reset_mapping(tp); ++ ++ return -1; ++ } ++ ++ /* Record it, because we want to send our data_fin on the same path */ ++ if (tp->mptcp->map_data_fin) { ++ mpcb->dfin_path_index = tp->mptcp->path_index; ++ mpcb->dfin_combined = !!(sk->sk_shutdown & RCV_SHUTDOWN); ++ } ++ ++ /* Verify the checksum */ ++ if (mpcb->dss_csum && !mpcb->infinite_mapping_rcv) { ++ int ret = mptcp_verif_dss_csum(sk); ++ ++ if (ret <= 0) { ++ mptcp_reset_mapping(tp); ++ return 1; ++ } ++ } ++ ++ if (before64(rcv_nxt64, tp->mptcp->map_data_seq)) { ++ /* Seg's have to go to the meta-ofo-queue */ ++ skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { ++ tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; ++ mptcp_prepare_skb(tmp1, tmp, sk); ++ __skb_unlink(tmp1, &sk->sk_receive_queue); ++ /* MUST be done here, because fragstolen may be true later. ++ * Then, kfree_skb_partial will not account the memory. ++ */ ++ skb_orphan(tmp1); ++ ++ if (!mpcb->in_time_wait) /* In time-wait, do not receive data */ ++ mptcp_add_meta_ofo_queue(meta_sk, tmp1, sk); ++ else ++ __kfree_skb(tmp1); ++ ++ if (!skb_queue_empty(&sk->sk_receive_queue) && ++ !before(TCP_SKB_CB(tmp)->seq, ++ tp->mptcp->map_subseq + tp->mptcp->map_data_len)) ++ break; ++ ++ } ++ } else { ++ /* Ready for the meta-rcv-queue */ ++ skb_queue_walk_safe(&sk->sk_receive_queue, tmp1, tmp) { ++ int eaten = 0; ++ int copied_early = 0; ++ bool fragstolen = false; ++ u32 old_rcv_nxt = meta_tp->rcv_nxt; ++ ++ tp->copied_seq = TCP_SKB_CB(tmp1)->end_seq; ++ mptcp_prepare_skb(tmp1, tmp, sk); ++ __skb_unlink(tmp1, &sk->sk_receive_queue); ++ /* MUST be done here, because fragstolen may be true. ++ * Then, kfree_skb_partial will not account the memory. ++ */ ++ skb_orphan(tmp1); ++ ++ /* This segment has already been received */ ++ if (!after(TCP_SKB_CB(tmp1)->end_seq, meta_tp->rcv_nxt)) { ++ __kfree_skb(tmp1); ++ goto next; ++ } ++ ++#ifdef CONFIG_NET_DMA ++ if (TCP_SKB_CB(tmp1)->seq == meta_tp->rcv_nxt && ++ meta_tp->ucopy.task == current && ++ meta_tp->copied_seq == meta_tp->rcv_nxt && ++ tmp1->len <= meta_tp->ucopy.len && ++ sock_owned_by_user(meta_sk) && ++ tcp_dma_try_early_copy(meta_sk, tmp1, 0)) { ++ copied_early = 1; ++ eaten = 1; ++ } ++#endif ++ ++ /* Is direct copy possible ? */ ++ if (TCP_SKB_CB(tmp1)->seq == meta_tp->rcv_nxt && ++ meta_tp->ucopy.task == current && ++ meta_tp->copied_seq == meta_tp->rcv_nxt && ++ meta_tp->ucopy.len && sock_owned_by_user(meta_sk) && ++ !copied_early) ++ eaten = mptcp_direct_copy(tmp1, meta_sk); ++ ++ if (mpcb->in_time_wait) /* In time-wait, do not receive data */ ++ eaten = 1; ++ ++ if (!eaten) ++ eaten = tcp_queue_rcv(meta_sk, tmp1, 0, &fragstolen); ++ ++ meta_tp->rcv_nxt = TCP_SKB_CB(tmp1)->end_seq; ++ mptcp_check_rcvseq_wrap(meta_tp, old_rcv_nxt); ++ ++ if (copied_early) ++ tcp_cleanup_rbuf(meta_sk, tmp1->len); ++ ++ if (tcp_hdr(tmp1)->fin && !mpcb->in_time_wait) ++ mptcp_fin(meta_sk); ++ ++ /* Check if this fills a gap in the ofo queue */ ++ if (!skb_queue_empty(&meta_tp->out_of_order_queue)) ++ mptcp_ofo_queue(meta_sk); ++ ++#ifdef CONFIG_NET_DMA ++ if (copied_early) ++ __skb_queue_tail(&meta_sk->sk_async_wait_queue, ++ tmp1); ++ else ++#endif ++ if (eaten) ++ kfree_skb_partial(tmp1, fragstolen); ++ ++ data_queued = true; ++next: ++ if (!skb_queue_empty(&sk->sk_receive_queue) && ++ !before(TCP_SKB_CB(tmp)->seq, ++ tp->mptcp->map_subseq + tp->mptcp->map_data_len)) ++ break; ++ } ++ } ++ ++ inet_csk(meta_sk)->icsk_ack.lrcvtime = tcp_time_stamp; ++ tp->mptcp->last_data_seq = tp->mptcp->map_data_seq; ++ mptcp_reset_mapping(tp); ++ ++ return data_queued ? -1 : -2; ++} ++ ++void mptcp_data_ready(struct sock *sk, int bytes) ++{ ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ struct sk_buff *skb, *tmp; ++ int queued = 0; ++ ++ /* If the meta is already closed, there is no point in pushing data */ ++ if (meta_sk->sk_state == TCP_CLOSE && !tcp_sk(sk)->mpcb->in_time_wait) { ++ skb_queue_purge(&sk->sk_receive_queue); ++ tcp_sk(sk)->copied_seq = tcp_sk(sk)->rcv_nxt; ++ goto exit; ++ } ++ ++restart: ++ /* Iterate over all segments, detect their mapping (if we don't have ++ * one yet), validate them and push everything one level higher. ++ */ ++ skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) { ++ int ret; ++ /* Pre-validation - e.g., early fallback */ ++ ret = mptcp_prevalidate_skb(sk, skb); ++ if (ret < 0) ++ goto restart; ++ else if (ret > 0) ++ break; ++ ++ /* Set the current mapping */ ++ ret = mptcp_detect_mapping(sk, skb); ++ if (ret < 0) ++ goto restart; ++ else if (ret > 0) ++ break; ++ ++ /* Validation */ ++ if (mptcp_validate_mapping(sk, skb) < 0) ++ goto restart; ++ ++ /* Push a level higher */ ++ ret = mptcp_queue_skb(sk); ++ if (ret < 0) { ++ if (ret == -1) ++ queued = ret; ++ goto restart; ++ } else if (ret == 0) { ++ continue; ++ } else { /* ret == 1 */ ++ break; ++ } ++ } ++ ++exit: ++ if (tcp_sk(sk)->close_it) { ++ tcp_send_ack(sk); ++ tcp_time_wait(sk, TCP_TIME_WAIT, 0); ++ } ++ ++ if (queued == -1 && !sock_flag(meta_sk, SOCK_DEAD)) ++ meta_sk->sk_data_ready(meta_sk, 0); ++} ++ ++ ++int mptcp_check_req(struct sk_buff *skb, struct net *net) ++{ ++ struct tcphdr *th = tcp_hdr(skb); ++ struct sock *meta_sk = NULL; ++ ++ /* MPTCP structures not initialized */ ++ if (mptcp_init_failed) ++ return 0; ++ ++ if (skb->protocol == htons(ETH_P_IP)) ++ meta_sk = mptcp_v4_search_req(th->source, ip_hdr(skb)->saddr, ++ ip_hdr(skb)->daddr, net); ++#if IS_ENABLED(CONFIG_IPV6) ++ else /* IPv6 */ ++ meta_sk = mptcp_v6_search_req(th->source, &ipv6_hdr(skb)->saddr, ++ &ipv6_hdr(skb)->daddr, net); ++#endif /* CONFIG_IPV6 */ ++ ++ if (!meta_sk) ++ return 0; ++ ++ TCP_SKB_CB(skb)->mptcp_flags = MPTCPHDR_JOIN; ++ ++ bh_lock_sock_nested(meta_sk); ++ if (sock_owned_by_user(meta_sk)) { ++ skb->sk = meta_sk; ++ if (unlikely(sk_add_backlog(meta_sk, skb, ++ meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { ++ bh_unlock_sock(meta_sk); ++ NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); ++ sock_put(meta_sk); /* Taken by mptcp_search_req */ ++ kfree_skb(skb); ++ return 1; ++ } ++ } else if (skb->protocol == htons(ETH_P_IP)) { ++ tcp_v4_do_rcv(meta_sk, skb); ++#if IS_ENABLED(CONFIG_IPV6) ++ } else { /* IPv6 */ ++ tcp_v6_do_rcv(meta_sk, skb); ++#endif /* CONFIG_IPV6 */ ++ } ++ bh_unlock_sock(meta_sk); ++ sock_put(meta_sk); /* Taken by mptcp_vX_search_req */ ++ return 1; ++} ++ ++struct mp_join *mptcp_find_join(struct sk_buff *skb) ++{ ++ struct tcphdr *th = tcp_hdr(skb); ++ unsigned char *ptr; ++ int length = (th->doff * 4) - sizeof(struct tcphdr); ++ ++ /* Jump through the options to check whether JOIN is there */ ++ ptr = (unsigned char *)(th + 1); ++ while (length > 0) { ++ int opcode = *ptr++; ++ int opsize; ++ ++ switch (opcode) { ++ case TCPOPT_EOL: ++ return NULL; ++ case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ ++ length--; ++ continue; ++ default: ++ opsize = *ptr++; ++ if (opsize < 2) /* "silly options" */ ++ return NULL; ++ if (opsize > length) ++ return NULL; /* don't parse partial options */ ++ if (opcode == TCPOPT_MPTCP && ++ ((struct mptcp_option *)(ptr - 2))->sub == MPTCP_SUB_JOIN) { ++ return (struct mp_join *)(ptr - 2); ++ } ++ ptr += opsize - 2; ++ length -= opsize; ++ } ++ } ++ return NULL; ++} ++ ++int mptcp_lookup_join(struct sk_buff *skb, struct inet_timewait_sock *tw) ++{ ++ struct mptcp_cb *mpcb; ++ struct sock *meta_sk; ++ u32 token; ++ struct mp_join *join_opt = mptcp_find_join(skb); ++ if (!join_opt) ++ return 0; ++ ++ /* MPTCP structures were not initialized, so return error */ ++ if (mptcp_init_failed) ++ return -1; ++ ++ token = join_opt->u.syn.token; ++ meta_sk = mptcp_hash_find(dev_net(skb_dst(skb)->dev), token); ++ if (!meta_sk) { ++ mptcp_debug("%s:mpcb not found:%x\n", __func__, token); ++ return -1; ++ } ++ ++ mpcb = tcp_sk(meta_sk)->mpcb; ++ if (mpcb->infinite_mapping_rcv || mpcb->send_infinite_mapping) { ++ /* We are in fallback-mode on the reception-side - ++ * no new subflows! ++ */ ++ sock_put(meta_sk); /* Taken by mptcp_hash_find */ ++ return -1; ++ } ++ ++ /* Coming from time-wait-sock processing in tcp_v4_rcv. ++ * We have to deschedule it before continuing, because otherwise ++ * mptcp_v4_do_rcv will hit again on it inside tcp_v4_hnd_req. ++ */ ++ if (tw) { ++ inet_twsk_deschedule(tw, &tcp_death_row); ++ inet_twsk_put(tw); ++ } ++ ++ TCP_SKB_CB(skb)->mptcp_flags = MPTCPHDR_JOIN; ++ /* OK, this is a new syn/join, let's create a new open request and ++ * send syn+ack ++ */ ++ bh_lock_sock_nested(meta_sk); ++ if (sock_owned_by_user(meta_sk)) { ++ skb->sk = meta_sk; ++ if (unlikely(sk_add_backlog(meta_sk, skb, ++ meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) { ++ bh_unlock_sock(meta_sk); ++ NET_INC_STATS_BH(sock_net(meta_sk), ++ LINUX_MIB_TCPBACKLOGDROP); ++ sock_put(meta_sk); /* Taken by mptcp_hash_find */ ++ kfree_skb(skb); ++ return 1; ++ } ++ } else if (skb->protocol == htons(ETH_P_IP)) { ++ tcp_v4_do_rcv(meta_sk, skb); ++#if IS_ENABLED(CONFIG_IPV6) ++ } else { ++ tcp_v6_do_rcv(meta_sk, skb); ++#endif /* CONFIG_IPV6 */ ++ } ++ bh_unlock_sock(meta_sk); ++ sock_put(meta_sk); /* Taken by mptcp_hash_find */ ++ return 1; ++} ++ ++int mptcp_do_join_short(struct sk_buff *skb, struct mptcp_options_received *mopt, ++ struct tcp_options_received *tmp_opt, struct net *net) ++{ ++ struct sock *meta_sk; ++ u32 token; ++ ++ token = mopt->mptcp_rem_token; ++ meta_sk = mptcp_hash_find(net, token); ++ if (!meta_sk) { ++ mptcp_debug("%s:mpcb not found:%x\n", __func__, token); ++ return -1; ++ } ++ ++ TCP_SKB_CB(skb)->mptcp_flags = MPTCPHDR_JOIN; ++ ++ /* OK, this is a new syn/join, let's create a new open request and ++ * send syn+ack ++ */ ++ bh_lock_sock(meta_sk); ++ ++ /* This check is also done in mptcp_vX_do_rcv. But, there we cannot ++ * call tcp_vX_send_reset, because we hold already two socket-locks. ++ * (the listener and the meta from above) ++ * ++ * And the send-reset will try to take yet another one (ip_send_reply). ++ * Thus, we propagate the reset up to tcp_rcv_state_process. ++ */ ++ if (tcp_sk(meta_sk)->mpcb->infinite_mapping_rcv || ++ tcp_sk(meta_sk)->mpcb->send_infinite_mapping || ++ meta_sk->sk_state == TCP_CLOSE || !tcp_sk(meta_sk)->inside_tk_table) { ++ bh_unlock_sock(meta_sk); ++ sock_put(meta_sk); /* Taken by mptcp_hash_find */ ++ return -1; ++ } ++ ++ if (sock_owned_by_user(meta_sk)) { ++ skb->sk = meta_sk; ++ if (unlikely(sk_add_backlog(meta_sk, skb, ++ meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) ++ NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP); ++ else ++ /* Must make sure that upper layers won't free the ++ * skb if it is added to the backlog-queue. ++ */ ++ skb_get(skb); ++ } else { ++ /* mptcp_v4_do_rcv tries to free the skb - we prevent this, as ++ * the skb will finally be freed by tcp_v4_do_rcv (where we are ++ * coming from) ++ */ ++ skb_get(skb); ++ if (skb->protocol == htons(ETH_P_IP)) { ++ tcp_v4_do_rcv(meta_sk, skb); ++#if IS_ENABLED(CONFIG_IPV6) ++ } else { /* IPv6 */ ++ tcp_v6_do_rcv(meta_sk, skb); ++#endif /* CONFIG_IPV6 */ ++ } ++ } ++ ++ bh_unlock_sock(meta_sk); ++ sock_put(meta_sk); /* Taken by mptcp_hash_find */ ++ return 0; ++} ++ ++/** ++ * Equivalent of tcp_fin() for MPTCP ++ * Can be called only when the FIN is validly part ++ * of the data seqnum space. Not before when we get holes. ++ */ ++void mptcp_fin(struct sock *meta_sk) ++{ ++ struct sock *sk = NULL, *sk_it; ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ ++ mptcp_for_each_sk(mpcb, sk_it) { ++ if (tcp_sk(sk_it)->mptcp->path_index == mpcb->dfin_path_index) { ++ sk = sk_it; ++ break; ++ } ++ } ++ ++ if (!sk || sk->sk_state == TCP_CLOSE) ++ sk = mptcp_select_ack_sock(meta_sk, 0); ++ ++ inet_csk_schedule_ack(sk); ++ ++ meta_sk->sk_shutdown |= RCV_SHUTDOWN; ++ sock_set_flag(meta_sk, SOCK_DONE); ++ ++ switch (meta_sk->sk_state) { ++ case TCP_SYN_RECV: ++ case TCP_ESTABLISHED: ++ /* Move to CLOSE_WAIT */ ++ tcp_set_state(meta_sk, TCP_CLOSE_WAIT); ++ inet_csk(sk)->icsk_ack.pingpong = 1; ++ break; ++ ++ case TCP_CLOSE_WAIT: ++ case TCP_CLOSING: ++ /* Received a retransmission of the FIN, do ++ * nothing. ++ */ ++ break; ++ case TCP_LAST_ACK: ++ /* RFC793: Remain in the LAST-ACK state. */ ++ break; ++ ++ case TCP_FIN_WAIT1: ++ /* This case occurs when a simultaneous close ++ * happens, we must ack the received FIN and ++ * enter the CLOSING state. ++ */ ++ tcp_send_ack(sk); ++ tcp_set_state(meta_sk, TCP_CLOSING); ++ break; ++ case TCP_FIN_WAIT2: ++ /* Received a FIN -- send ACK and enter TIME_WAIT. */ ++ tcp_send_ack(sk); ++ tcp_time_wait(meta_sk, TCP_TIME_WAIT, 0); ++ break; ++ default: ++ /* Only TCP_LISTEN and TCP_CLOSE are left, in these ++ * cases we should never reach this piece of code. ++ */ ++ pr_err("%s: Impossible, meta_sk->sk_state=%d\n", __func__, ++ meta_sk->sk_state); ++ break; ++ } ++ ++ /* It _is_ possible, that we have something out-of-order _after_ FIN. ++ * Probably, we should reset in this case. For now drop them. ++ */ ++ mptcp_purge_ofo_queue(meta_tp); ++ sk_mem_reclaim(meta_sk); ++ ++ if (!sock_flag(meta_sk, SOCK_DEAD)) { ++ meta_sk->sk_state_change(meta_sk); ++ ++ /* Do not send POLL_HUP for half duplex close. */ ++ if (meta_sk->sk_shutdown == SHUTDOWN_MASK || ++ meta_sk->sk_state == TCP_CLOSE) ++ sk_wake_async(meta_sk, SOCK_WAKE_WAITD, POLL_HUP); ++ else ++ sk_wake_async(meta_sk, SOCK_WAKE_WAITD, POLL_IN); ++ } ++ ++ return; ++} ++ ++static void mptcp_xmit_retransmit_queue(struct sock *meta_sk) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct sk_buff *skb; ++ ++ if (!meta_tp->packets_out) ++ return; ++ ++ tcp_for_write_queue(skb, meta_sk) { ++ if (skb == tcp_send_head(meta_sk)) ++ break; ++ ++ if (mptcp_retransmit_skb(meta_sk, skb)) ++ return; ++ ++ if (skb == tcp_write_queue_head(meta_sk)) ++ inet_csk_reset_xmit_timer(meta_sk, ICSK_TIME_RETRANS, ++ inet_csk(meta_sk)->icsk_rto, ++ TCP_RTO_MAX); ++ } ++} ++ ++/* Handle the DATA_ACK */ ++static void mptcp_data_ack(struct sock *sk, const struct sk_buff *skb) ++{ ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk), *tp = tcp_sk(sk); ++ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); ++ u32 prior_snd_una = meta_tp->snd_una; ++ int prior_packets; ++ u32 nwin, data_ack, data_seq; ++ u16 data_len = 0; ++ ++ /* A valid packet came in - subflow is operational again */ ++ tp->pf = 0; ++ ++ /* Even if there is no data-ack, we stop retransmitting. ++ * Except if this is a SYN/ACK. Then it is just a retransmission ++ */ ++ if (tp->mptcp->pre_established && !tcp_hdr(skb)->syn) { ++ tp->mptcp->pre_established = 0; ++ sk_stop_timer(sk, &tp->mptcp->mptcp_ack_timer); ++ } ++ ++ /* If we are in infinite mapping mode, rx_opt.data_ack has been ++ * set by mptcp_clean_rtx_infinite. ++ */ ++ if (!(tcb->mptcp_flags & MPTCPHDR_ACK) && !tp->mpcb->infinite_mapping_snd) ++ goto exit; ++ ++ data_ack = tp->mptcp->rx_opt.data_ack; ++ ++ if (unlikely(!tp->mptcp->fully_established) && ++ (data_ack != meta_tp->mptcp->snt_isn || ++ tp->mptcp->snt_isn + 1 != TCP_SKB_CB(skb)->ack_seq)) ++ /* As soon as data has been data-acked, ++ * or a subflow-data-ack (not acking syn - thus snt_isn + 1) ++ * includes a data-ack, we are fully established ++ */ ++ mptcp_become_fully_estab(sk); ++ ++ /* Get the data_seq */ ++ if (mptcp_is_data_seq(skb)) { ++ data_seq = tp->mptcp->rx_opt.data_seq; ++ data_len = tp->mptcp->rx_opt.data_len; ++ } else { ++ data_seq = meta_tp->snd_wl1; ++ } ++ ++ /* If the ack is older than previous acks ++ * then we can probably ignore it. ++ */ ++ if (before(data_ack, prior_snd_una)) ++ goto exit; ++ ++ /* If the ack includes data we haven't sent yet, discard ++ * this segment (RFC793 Section 3.9). ++ */ ++ if (after(data_ack, meta_tp->snd_nxt)) ++ goto exit; ++ ++ /*** Now, update the window - inspired by tcp_ack_update_window ***/ ++ nwin = ntohs(tcp_hdr(skb)->window); ++ ++ if (likely(!tcp_hdr(skb)->syn)) ++ nwin <<= tp->rx_opt.snd_wscale; ++ ++ if (tcp_may_update_window(meta_tp, data_ack, data_seq, nwin)) { ++ tcp_update_wl(meta_tp, data_seq); ++ ++ /* Draft v09, Section 3.3.5: ++ * [...] It should only update its local receive window values ++ * when the largest sequence number allowed (i.e. DATA_ACK + ++ * receive window) increases. [...] ++ */ ++ if (meta_tp->snd_wnd != nwin && ++ !before(data_ack + nwin, tcp_wnd_end(meta_tp))) { ++ meta_tp->snd_wnd = nwin; ++ ++ if (nwin > meta_tp->max_window) ++ meta_tp->max_window = nwin; ++ } ++ } ++ /*** Done, update the window ***/ ++ ++ /* We passed data and got it acked, remove any soft error ++ * log. Something worked... ++ */ ++ sk->sk_err_soft = 0; ++ inet_csk(meta_sk)->icsk_probes_out = 0; ++ meta_tp->rcv_tstamp = tcp_time_stamp; ++ prior_packets = meta_tp->packets_out; ++ if (!prior_packets) ++ goto no_queue; ++ ++ meta_tp->snd_una = data_ack; ++ ++ mptcp_clean_rtx_queue(meta_sk, prior_snd_una); ++ ++ /* We are in loss-state, and something got acked, retransmit the whole ++ * queue now! ++ */ ++ if (inet_csk(meta_sk)->icsk_ca_state == TCP_CA_Loss && ++ after(data_ack, prior_snd_una)) { ++ mptcp_xmit_retransmit_queue(meta_sk); ++ inet_csk(meta_sk)->icsk_ca_state = TCP_CA_Open; ++ } ++ ++ /* Simplified version of tcp_new_space, because the snd-buffer ++ * is handled by all the subflows. ++ */ ++ if (sock_flag(meta_sk, SOCK_QUEUE_SHRUNK)) { ++ sock_reset_flag(meta_sk, SOCK_QUEUE_SHRUNK); ++ if (meta_sk->sk_socket && ++ test_bit(SOCK_NOSPACE, &meta_sk->sk_socket->flags)) ++ meta_sk->sk_write_space(meta_sk); ++ } ++ ++ if (meta_sk->sk_state != TCP_ESTABLISHED && ++ mptcp_rcv_state_process(meta_sk, sk, skb, data_seq, data_len)) ++ return; ++ ++exit: ++ mptcp_push_pending_frames(meta_sk); ++ ++ return; ++ ++no_queue: ++ if (tcp_send_head(meta_sk)) ++ tcp_ack_probe(meta_sk); ++ ++ mptcp_push_pending_frames(meta_sk); ++ ++ return; ++} ++ ++void mptcp_clean_rtx_infinite(struct sk_buff *skb, struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = tcp_sk(mptcp_meta_sk(sk)); ++ ++ if (!tp->mpcb->infinite_mapping_snd) ++ return; ++ ++ /* The difference between both write_seq's represents the offset between ++ * data-sequence and subflow-sequence. As we are infinite, this must ++ * match. ++ * ++ * Thus, from this difference we can infer the meta snd_una. ++ */ ++ tp->mptcp->rx_opt.data_ack = meta_tp->snd_nxt - tp->snd_nxt + ++ tp->snd_una; ++ ++ mptcp_data_ack(sk, skb); ++} ++ ++/**** static functions used by mptcp_parse_options */ ++ ++static inline int mptcp_rem_raddress(struct mptcp_cb *mpcb, u8 rem_id) ++{ ++ if (mptcp_v4_rem_raddress(mpcb, rem_id) < 0) { ++#if IS_ENABLED(CONFIG_IPV6) ++ if (mptcp_v6_rem_raddress(mpcb, rem_id) < 0) ++ return -1; ++#else ++ return -1; ++#endif /* CONFIG_IPV6 */ ++ } ++ return 0; ++} ++ ++static void mptcp_send_reset_rem_id(const struct mptcp_cb *mpcb, u8 rem_id) ++{ ++ struct sock *sk_it, *tmpsk; ++ ++ mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { ++ if (tcp_sk(sk_it)->mptcp->rem_id == rem_id) { ++ mptcp_reinject_data(sk_it, 0); ++ sk_it->sk_err = ECONNRESET; ++ if (tcp_need_reset(sk_it->sk_state)) ++ tcp_send_active_reset(sk_it, GFP_ATOMIC); ++ mptcp_sub_force_close(sk_it); ++ } ++ } ++} ++ ++void mptcp_parse_options(const uint8_t *ptr, int opsize, ++ struct tcp_options_received *opt_rx, ++ struct mptcp_options_received *mopt, ++ const struct sk_buff *skb) ++{ ++ struct mptcp_option *mp_opt = (struct mptcp_option *)ptr; ++ ++ /* If the socket is mp-capable we would have a mopt. */ ++ if (!mopt) ++ return; ++ ++ switch (mp_opt->sub) { ++ case MPTCP_SUB_CAPABLE: ++ { ++ struct mp_capable *mpcapable = (struct mp_capable *)ptr; ++ ++ if (opsize != MPTCP_SUB_LEN_CAPABLE_SYN && ++ opsize != MPTCP_SUB_LEN_CAPABLE_ACK) { ++ mptcp_debug("%s: mp_capable: bad option size %d\n", ++ __func__, opsize); ++ break; ++ } ++ ++ if (!sysctl_mptcp_enabled) ++ break; ++ ++ /* We only support MPTCP version 0 */ ++ if (mpcapable->ver != 0) ++ break; ++ ++ /* MPTCP-RFC 6824: ++ * "If receiving a message with the 'B' flag set to 1, and this ++ * is not understood, then this SYN MUST be silently ignored; ++ */ ++ if (mpcapable->b) { ++ mopt->drop_me = 1; ++ break; ++ } ++ ++ /* MPTCP-RFC 6824: ++ * "An implementation that only supports this method MUST set ++ * bit "H" to 1, and bits "C" through "G" to 0." ++ */ ++ if (!mpcapable->h) ++ break; ++ ++ mopt->saw_mpc = 1; ++ mopt->dss_csum = sysctl_mptcp_checksum || mpcapable->a; ++ ++ if (opsize >= MPTCP_SUB_LEN_CAPABLE_SYN) ++ mopt->mptcp_key = mpcapable->sender_key; ++ ++ break; ++ } ++ case MPTCP_SUB_JOIN: ++ { ++ struct mp_join *mpjoin = (struct mp_join *)ptr; ++ ++ if (opsize != MPTCP_SUB_LEN_JOIN_SYN && ++ opsize != MPTCP_SUB_LEN_JOIN_SYNACK && ++ opsize != MPTCP_SUB_LEN_JOIN_ACK) { ++ mptcp_debug("%s: mp_join: bad option size %d\n", ++ __func__, opsize); ++ break; ++ } ++ ++ /* saw_mpc must be set, because in tcp_check_req we assume that ++ * it is set to support falling back to reg. TCP if a rexmitted ++ * SYN has no MP_CAPABLE or MP_JOIN ++ */ ++ switch (opsize) { ++ case MPTCP_SUB_LEN_JOIN_SYN: ++ mopt->is_mp_join = 1; ++ mopt->saw_mpc = 1; ++ mopt->low_prio = mpjoin->b; ++ mopt->rem_id = mpjoin->addr_id; ++ mopt->mptcp_rem_token = mpjoin->u.syn.token; ++ mopt->mptcp_recv_nonce = mpjoin->u.syn.nonce; ++ break; ++ case MPTCP_SUB_LEN_JOIN_SYNACK: ++ mopt->saw_mpc = 1; ++ mopt->low_prio = mpjoin->b; ++ mopt->rem_id = mpjoin->addr_id; ++ mopt->mptcp_recv_tmac = mpjoin->u.synack.mac; ++ mopt->mptcp_recv_nonce = mpjoin->u.synack.nonce; ++ break; ++ case MPTCP_SUB_LEN_JOIN_ACK: ++ mopt->saw_mpc = 1; ++ mopt->join_ack = 1; ++ memcpy(mopt->mptcp_recv_mac, mpjoin->u.ack.mac, 20); ++ break; ++ } ++ break; ++ } ++ case MPTCP_SUB_DSS: ++ { ++ struct mp_dss *mdss = (struct mp_dss *)ptr; ++ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); ++ ++ /* We check opsize for the csum and non-csum case. We do this, ++ * because the draft says that the csum SHOULD be ignored if ++ * it has not been negotiated in the MP_CAPABLE but still is ++ * present in the data. ++ * ++ * It will get ignored later in mptcp_queue_skb. ++ */ ++ if (opsize != mptcp_sub_len_dss(mdss, 0) && ++ opsize != mptcp_sub_len_dss(mdss, 1)) { ++ mptcp_debug("%s: mp_dss: bad option size %d\n", ++ __func__, opsize); ++ break; ++ } ++ ++ ptr += 4; ++ ++ if (mdss->A) { ++ tcb->mptcp_flags |= MPTCPHDR_ACK; ++ ++ if (mdss->a) { ++ mopt->data_ack = (u32) get_unaligned_be64(ptr); ++ ptr += MPTCP_SUB_LEN_ACK_64; ++ } else { ++ mopt->data_ack = get_unaligned_be32(ptr); ++ ptr += MPTCP_SUB_LEN_ACK; ++ } ++ } ++ ++ tcb->dss_off = (ptr - skb_transport_header(skb)); ++ ++ if (mdss->M) { ++ if (mdss->m) { ++ u64 data_seq64 = get_unaligned_be64(ptr); ++ ++ tcb->mptcp_flags |= MPTCPHDR_SEQ64_SET; ++ mopt->data_seq = (u32) data_seq64; ++ ++ ptr += 12; /* 64-bit dseq + subseq */ ++ } else { ++ mopt->data_seq = get_unaligned_be32(ptr); ++ ptr += 8; /* 32-bit dseq + subseq */ ++ } ++ mopt->data_len = get_unaligned_be16(ptr); ++ ++ tcb->mptcp_flags |= MPTCPHDR_SEQ; ++ ++ /* Is a check-sum present? */ ++ if (opsize == mptcp_sub_len_dss(mdss, 1)) ++ tcb->mptcp_flags |= MPTCPHDR_DSS_CSUM; ++ ++ /* DATA_FIN only possible with DSS-mapping */ ++ if (mdss->F) ++ tcb->mptcp_flags |= MPTCPHDR_FIN; ++ } ++ ++ break; ++ } ++ case MPTCP_SUB_ADD_ADDR: ++ { ++#if IS_ENABLED(CONFIG_IPV6) ++ struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; ++ ++ if ((mpadd->ipver == 4 && opsize != MPTCP_SUB_LEN_ADD_ADDR4 && ++ opsize != MPTCP_SUB_LEN_ADD_ADDR4 + 2) || ++ (mpadd->ipver == 6 && opsize != MPTCP_SUB_LEN_ADD_ADDR6 && ++ opsize != MPTCP_SUB_LEN_ADD_ADDR6 + 2)) { ++#else ++ if (opsize != MPTCP_SUB_LEN_ADD_ADDR4 && ++ opsize != MPTCP_SUB_LEN_ADD_ADDR4 + 2) { ++#endif /* CONFIG_IPV6 */ ++ mptcp_debug("%s: mp_add_addr: bad option size %d\n", ++ __func__, opsize); ++ break; ++ } ++ ++ /* We have to manually parse the options if we got two of them. */ ++ if (mopt->saw_add_addr) { ++ mopt->more_add_addr = 1; ++ break; ++ } ++ mopt->saw_add_addr = 1; ++ mopt->add_addr_ptr = ptr; ++ break; ++ } ++ case MPTCP_SUB_REMOVE_ADDR: ++ if ((opsize - MPTCP_SUB_LEN_REMOVE_ADDR) < 0) { ++ mptcp_debug("%s: mp_remove_addr: bad option size %d\n", ++ __func__, opsize); ++ break; ++ } ++ ++ if (mopt->saw_rem_addr) { ++ mopt->more_rem_addr = 1; ++ break; ++ } ++ mopt->saw_rem_addr = 1; ++ mopt->rem_addr_ptr = ptr; ++ break; ++ case MPTCP_SUB_PRIO: ++ { ++ struct mp_prio *mpprio = (struct mp_prio *)ptr; ++ ++ if (opsize != MPTCP_SUB_LEN_PRIO && ++ opsize != MPTCP_SUB_LEN_PRIO_ADDR) { ++ mptcp_debug("%s: mp_prio: bad option size %d\n", ++ __func__, opsize); ++ break; ++ } ++ ++ mopt->saw_low_prio = 1; ++ mopt->low_prio = mpprio->b; ++ ++ if (opsize == MPTCP_SUB_LEN_PRIO_ADDR) { ++ mopt->saw_low_prio = 2; ++ mopt->prio_addr_id = mpprio->addr_id; ++ } ++ break; ++ } ++ case MPTCP_SUB_FAIL: ++ if (opsize != MPTCP_SUB_LEN_FAIL) { ++ mptcp_debug("%s: mp_fail: bad option size %d\n", ++ __func__, opsize); ++ break; ++ } ++ mopt->mp_fail = 1; ++ break; ++ case MPTCP_SUB_FCLOSE: ++ if (opsize != MPTCP_SUB_LEN_FCLOSE) { ++ mptcp_debug("%s: mp_fclose: bad option size %d\n", ++ __func__, opsize); ++ break; ++ } ++ ++ mopt->mp_fclose = 1; ++ mopt->mptcp_key = ((struct mp_fclose *)ptr)->key; ++ ++ break; ++ default: ++ mptcp_debug("%s: Received unkown subtype: %d\n", ++ __func__, mp_opt->sub); ++ break; ++ } ++} ++ ++int mptcp_check_rtt(const struct tcp_sock *tp, int time) ++{ ++ struct mptcp_cb *mpcb = tp->mpcb; ++ struct sock *sk; ++ u32 rtt_max = 0; ++ ++ /* In MPTCP, we take the max delay across all flows, ++ * in order to take into account meta-reordering buffers. ++ */ ++ mptcp_for_each_sk(mpcb, sk) { ++ if (!mptcp_sk_can_recv(sk)) ++ continue; ++ ++ if (rtt_max < tcp_sk(sk)->rcv_rtt_est.rtt) ++ rtt_max = tcp_sk(sk)->rcv_rtt_est.rtt; ++ } ++ if (time < (rtt_max >> 3) || !rtt_max) ++ return 1; ++ ++ return 0; ++} ++ ++static void mptcp_handle_add_addr(const unsigned char *ptr, struct sock *sk) ++{ ++ struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; ++ ++ if (mpadd->ipver == 4) { ++ __be16 port = 0; ++ if (mpadd->len == MPTCP_SUB_LEN_ADD_ADDR4 + 2) ++ port = mpadd->u.v4.port; ++ ++ mptcp_v4_add_raddress(tcp_sk(sk)->mpcb, &mpadd->u.v4.addr, port, ++ mpadd->addr_id); ++#if IS_ENABLED(CONFIG_IPV6) ++ } else if (mpadd->ipver == 6) { ++ __be16 port = 0; ++ if (mpadd->len == MPTCP_SUB_LEN_ADD_ADDR6 + 2) ++ port = mpadd->u.v6.port; ++ ++ mptcp_v6_add_raddress(tcp_sk(sk)->mpcb, &mpadd->u.v6.addr, port, ++ mpadd->addr_id); ++#endif /* CONFIG_IPV6 */ ++ } ++} ++ ++static void mptcp_handle_rem_addr(const unsigned char *ptr, struct sock *sk) ++{ ++ struct mp_remove_addr *mprem = (struct mp_remove_addr *)ptr; ++ int i; ++ u8 rem_id; ++ ++ for (i = 0; i <= mprem->len - MPTCP_SUB_LEN_REMOVE_ADDR; i++) { ++ rem_id = (&mprem->addrs_id)[i]; ++ if (!mptcp_rem_raddress(tcp_sk(sk)->mpcb, rem_id)) ++ mptcp_send_reset_rem_id(tcp_sk(sk)->mpcb, rem_id); ++ } ++} ++ ++static void mptcp_parse_addropt(const struct sk_buff *skb, struct sock *sk) ++{ ++ struct tcphdr *th = tcp_hdr(skb); ++ unsigned char *ptr; ++ int length = (th->doff * 4) - sizeof(struct tcphdr); ++ ++ /* Jump through the options to check whether ADD_ADDR is there */ ++ ptr = (unsigned char *)(th + 1); ++ while (length > 0) { ++ int opcode = *ptr++; ++ int opsize; ++ ++ switch (opcode) { ++ case TCPOPT_EOL: ++ return; ++ case TCPOPT_NOP: ++ length--; ++ continue; ++ default: ++ opsize = *ptr++; ++ if (opsize < 2) ++ return; ++ if (opsize > length) ++ return; /* don't parse partial options */ ++ if (opcode == TCPOPT_MPTCP && ++ ((struct mptcp_option *)ptr)->sub == MPTCP_SUB_ADD_ADDR) { ++#if IS_ENABLED(CONFIG_IPV6) ++ struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; ++ if ((mpadd->ipver == 4 && opsize != MPTCP_SUB_LEN_ADD_ADDR4 && ++ opsize != MPTCP_SUB_LEN_ADD_ADDR4 + 2) || ++ (mpadd->ipver == 6 && opsize != MPTCP_SUB_LEN_ADD_ADDR6 && ++ opsize != MPTCP_SUB_LEN_ADD_ADDR6 + 2)) ++#else ++ if (opsize != MPTCP_SUB_LEN_ADD_ADDR4 && ++ opsize != MPTCP_SUB_LEN_ADD_ADDR4 + 2) ++#endif /* CONFIG_IPV6 */ ++ goto cont; ++ ++ mptcp_handle_add_addr(ptr, sk); ++ } ++ if (opcode == TCPOPT_MPTCP && ++ ((struct mptcp_option *)ptr)->sub == MPTCP_SUB_REMOVE_ADDR) { ++ if ((opsize - MPTCP_SUB_LEN_REMOVE_ADDR) < 0) ++ goto cont; ++ ++ mptcp_handle_rem_addr(ptr, sk); ++ } ++cont: ++ ptr += opsize - 2; ++ length -= opsize; ++ } ++ } ++ return; ++} ++ ++static inline int mptcp_mp_fail_rcvd(struct sock *sk, const struct tcphdr *th) ++{ ++ struct mptcp_tcp_sock *mptcp = tcp_sk(sk)->mptcp; ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; ++ ++ if (unlikely(mptcp->rx_opt.mp_fail)) { ++ mptcp->rx_opt.mp_fail = 0; ++ ++ if (!th->rst && !mpcb->infinite_mapping_snd) { ++ struct sock *sk_it; ++ ++ mpcb->send_infinite_mapping = 1; ++ /* We resend everything that has not been acknowledged */ ++ meta_sk->sk_send_head = tcp_write_queue_head(meta_sk); ++ ++ /* We artificially restart the whole send-queue. Thus, ++ * it is as if no packets are in flight ++ */ ++ tcp_sk(meta_sk)->packets_out = 0; ++ ++ /* If the snd_nxt already wrapped around, we have to ++ * undo the wrapping, as we are restarting from snd_una ++ * on. ++ */ ++ if (tcp_sk(meta_sk)->snd_nxt < tcp_sk(meta_sk)->snd_una) { ++ mpcb->snd_high_order[mpcb->snd_hiseq_index] -= 2; ++ mpcb->snd_hiseq_index = mpcb->snd_hiseq_index ? 0 : 1; ++ } ++ tcp_sk(meta_sk)->snd_nxt = tcp_sk(meta_sk)->snd_una; ++ ++ /* Trigger a sending on the meta. */ ++ mptcp_push_pending_frames(meta_sk); ++ ++ mptcp_for_each_sk(mpcb, sk_it) { ++ if (sk != sk_it) ++ mptcp_sub_force_close(sk_it); ++ } ++ } ++ ++ return 0; ++ } ++ ++ if (unlikely(mptcp->rx_opt.mp_fclose)) { ++ struct sock *sk_it, *tmpsk; ++ ++ mptcp->rx_opt.mp_fclose = 0; ++ if (mptcp->rx_opt.mptcp_key != mpcb->mptcp_loc_key) ++ return 0; ++ ++ if (tcp_need_reset(sk->sk_state)) ++ tcp_send_active_reset(sk, GFP_ATOMIC); ++ ++ mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) ++ mptcp_sub_force_close(sk_it); ++ ++ tcp_reset(meta_sk); ++ ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static inline void mptcp_path_array_check(struct sock *meta_sk) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ ++ if (unlikely(mpcb->list_rcvd)) { ++ mpcb->list_rcvd = 0; ++ if (mpcb->pm_ops->new_remote_address) ++ mpcb->pm_ops->new_remote_address(meta_sk); ++ } ++} ++ ++int mptcp_handle_options(struct sock *sk, const struct tcphdr *th, struct sk_buff *skb) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct mptcp_options_received *mopt = &tp->mptcp->rx_opt; ++ ++ if (tp->mpcb->infinite_mapping_rcv || tp->mpcb->infinite_mapping_snd) ++ return 0; ++ ++ if (mptcp_mp_fail_rcvd(sk, th)) ++ return 1; ++ ++ /* RFC 6824, Section 3.3: ++ * If a checksum is not present when its use has been negotiated, the ++ * receiver MUST close the subflow with a RST as it is considered broken. ++ */ ++ if (mptcp_is_data_seq(skb) && tp->mpcb->dss_csum && ++ !(TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_DSS_CSUM)) { ++ if (tcp_need_reset(sk->sk_state)) ++ tcp_send_active_reset(sk, GFP_ATOMIC); ++ ++ mptcp_sub_force_close(sk); ++ return 1; ++ } ++ ++ /* We have to acknowledge retransmissions of the third ++ * ack. ++ */ ++ if (mopt->join_ack) { ++ tcp_send_delayed_ack(sk); ++ mopt->join_ack = 0; ++ } ++ ++ if (mopt->saw_add_addr || mopt->saw_rem_addr) { ++ if (mopt->more_add_addr || mopt->more_rem_addr) { ++ mptcp_parse_addropt(skb, sk); ++ } else { ++ if (mopt->saw_add_addr) ++ mptcp_handle_add_addr(mopt->add_addr_ptr, sk); ++ if (mopt->saw_rem_addr) ++ mptcp_handle_rem_addr(mopt->rem_addr_ptr, sk); ++ } ++ ++ mopt->more_add_addr = 0; ++ mopt->saw_add_addr = 0; ++ mopt->more_rem_addr = 0; ++ mopt->saw_rem_addr = 0; ++ } ++ if (mopt->saw_low_prio) { ++ if (mopt->saw_low_prio == 1) { ++ tp->mptcp->rcv_low_prio = mopt->low_prio; ++ } else { ++ struct sock *sk_it; ++ mptcp_for_each_sk(tp->mpcb, sk_it) { ++ struct mptcp_tcp_sock *mptcp = tcp_sk(sk_it)->mptcp; ++ if (mptcp->rem_id == mopt->prio_addr_id) ++ mptcp->rcv_low_prio = mopt->low_prio; ++ } ++ } ++ mopt->saw_low_prio = 0; ++ } ++ ++ mptcp_data_ack(sk, skb); ++ ++ mptcp_path_array_check(mptcp_meta_sk(sk)); ++ /* Socket may have been mp_killed by a REMOVE_ADDR */ ++ if (tp->mp_killed) ++ return 1; ++ ++ return 0; ++} ++ ++/* The skptr is needed, because if we become MPTCP-capable, we have to switch ++ * from meta-socket to master-socket. ++ * ++ * @return: 1 - we want to reset this connection ++ * 2 - we want to discard the received syn/ack ++ * 0 - everything is fine - continue ++ */ ++int mptcp_rcv_synsent_state_process(struct sock *sk, struct sock **skptr, ++ struct sk_buff *skb, ++ struct mptcp_options_received *mopt) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ if (tp->mpc) { ++ u8 hash_mac_check[20]; ++ struct mptcp_cb *mpcb = tp->mpcb; ++ ++ mptcp_hmac_sha1((u8 *)&mpcb->mptcp_rem_key, ++ (u8 *)&mpcb->mptcp_loc_key, ++ (u8 *)&tp->mptcp->rx_opt.mptcp_recv_nonce, ++ (u8 *)&tp->mptcp->mptcp_loc_nonce, ++ (u32 *)hash_mac_check); ++ if (memcmp(hash_mac_check, ++ (char *)&tp->mptcp->rx_opt.mptcp_recv_tmac, 8)) { ++ mptcp_sub_force_close(sk); ++ return 1; ++ } ++ ++ /* Set this flag in order to postpone data sending ++ * until the 4th ack arrives. ++ */ ++ tp->mptcp->pre_established = 1; ++ tp->mptcp->rcv_low_prio = tp->mptcp->rx_opt.low_prio; ++ ++ mptcp_hmac_sha1((u8 *)&mpcb->mptcp_loc_key, ++ (u8 *)&mpcb->mptcp_rem_key, ++ (u8 *)&tp->mptcp->mptcp_loc_nonce, ++ (u8 *)&tp->mptcp->rx_opt.mptcp_recv_nonce, ++ (u32 *)&tp->mptcp->sender_mac[0]); ++ ++ } else if (mopt->saw_mpc) { ++ if (mptcp_create_master_sk(sk, mopt->mptcp_key, ++ ntohs(tcp_hdr(skb)->window))) ++ return 2; ++ ++ sk = tcp_sk(sk)->mpcb->master_sk; ++ *skptr = sk; ++ tp = tcp_sk(sk); ++ ++ /* snd_nxt - 1, because it has been incremented ++ * by tcp_connect for the SYN ++ */ ++ tp->mptcp->snt_isn = tp->snd_nxt - 1; ++ tp->mpcb->dss_csum = mopt->dss_csum; ++ tp->mptcp->include_mpc = 1; ++ ++ sk_set_socket(sk, mptcp_meta_sk(sk)->sk_socket); ++ sk->sk_wq = mptcp_meta_sk(sk)->sk_wq; ++ ++ mptcp_update_metasocket(sk, mptcp_meta_sk(sk)); ++ ++ /* hold in mptcp_inherit_sk due to initialization to 2 */ ++ sock_put(sk); ++ } else { ++ tp->request_mptcp = 0; ++ ++ if (tp->inside_tk_table) ++ mptcp_hash_remove(tp); ++ } ++ ++ if (tp->mpc) ++ tp->mptcp->rcv_isn = TCP_SKB_CB(skb)->seq; ++ ++ return 0; ++} ++ ++bool mptcp_should_expand_sndbuf(const struct sock *sk) ++{ ++ struct sock *sk_it; ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ int cnt_backups = 0; ++ int backup_available = 0; ++ ++ /* We circumvent this check in tcp_check_space, because we want to ++ * always call sk_write_space. So, we reproduce the check here. ++ */ ++ if (!meta_sk->sk_socket || ++ !test_bit(SOCK_NOSPACE, &meta_sk->sk_socket->flags)) ++ return false; ++ ++ /* If the user specified a specific send buffer setting, do ++ * not modify it. ++ */ ++ if (meta_sk->sk_userlocks & SOCK_SNDBUF_LOCK) ++ return false; ++ ++ /* If we are under global TCP memory pressure, do not expand. */ ++ if (sk_under_memory_pressure(meta_sk)) ++ return false; ++ ++ /* If we are under soft global TCP memory pressure, do not expand. */ ++ if (sk_memory_allocated(meta_sk) >= sk_prot_mem_limits(meta_sk, 0)) ++ return false; ++ ++ ++ /* For MPTCP we look for a subsocket that could send data. ++ * If we found one, then we update the send-buffer. ++ */ ++ mptcp_for_each_sk(meta_tp->mpcb, sk_it) { ++ struct tcp_sock *tp_it = tcp_sk(sk_it); ++ ++ if (!mptcp_sk_can_send(sk_it)) ++ continue; ++ ++ /* Backup-flows have to be counted - if there is no other ++ * subflow we take the backup-flow into account. */ ++ if (tp_it->mptcp->rcv_low_prio || tp_it->mptcp->low_prio) { ++ cnt_backups++; ++ } ++ ++ if (tp_it->packets_out < tp_it->snd_cwnd) { ++ if (tp_it->mptcp->rcv_low_prio || tp_it->mptcp->low_prio) { ++ backup_available = 1; ++ continue; ++ } ++ return true; ++ } ++ } ++ ++ /* Backup-flow is available for sending - update send-buffer */ ++ if (meta_tp->mpcb->cnt_established == cnt_backups && backup_available) ++ return true; ++ return false; ++} ++ ++void mptcp_init_buffer_space(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ int space; ++ ++ tcp_init_buffer_space(sk); ++ ++ if (is_master_tp(tp)) { ++ /* If there is only one subflow, we just use regular TCP ++ * autotuning. User-locks are handled already by ++ * tcp_init_buffer_space ++ */ ++ meta_tp->window_clamp = tp->window_clamp; ++ meta_tp->rcv_ssthresh = tp->rcv_ssthresh; ++ meta_sk->sk_rcvbuf = sk->sk_rcvbuf; ++ meta_sk->sk_sndbuf = sk->sk_sndbuf; ++ ++ return; ++ } ++ ++ if (meta_sk->sk_userlocks & SOCK_RCVBUF_LOCK) ++ goto snd_buf; ++ ++ /* Adding a new subflow to the rcv-buffer space. We make a simple ++ * addition, to give some space to allow traffic on the new subflow. ++ * Autotuning will increase it further later on. ++ */ ++ space = min(meta_sk->sk_rcvbuf + sk->sk_rcvbuf, sysctl_tcp_rmem[2]); ++ if (space > meta_sk->sk_rcvbuf) { ++ meta_tp->window_clamp += tp->window_clamp; ++ meta_tp->rcv_ssthresh += tp->rcv_ssthresh; ++ meta_sk->sk_rcvbuf = space; ++ } ++ ++snd_buf: ++ if (meta_sk->sk_userlocks & SOCK_SNDBUF_LOCK) ++ return; ++ ++ /* Adding a new subflow to the send-buffer space. We make a simple ++ * addition, to give some space to allow traffic on the new subflow. ++ * Autotuning will increase it further later on. ++ */ ++ space = min(meta_sk->sk_sndbuf + sk->sk_sndbuf, sysctl_tcp_wmem[2]); ++ if (space > meta_sk->sk_sndbuf) { ++ meta_sk->sk_sndbuf = space; ++ meta_sk->sk_write_space(meta_sk); ++ } ++} ++ ++void mptcp_tcp_set_rto(struct sock *sk) ++{ ++ tcp_set_rto(sk); ++ mptcp_set_rto(sk); ++} +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_ipv4.c linux-3.14.45/net/mptcp/mptcp_ipv4.c +--- linux-3.14.45.orig/net/mptcp/mptcp_ipv4.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_ipv4.c 2015-06-24 14:15:48.895862487 +0200 +@@ -0,0 +1,603 @@ ++/* ++ * MPTCP implementation - IPv4-specific functions ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer: ++ * Christoph Paasch ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++u32 mptcp_v4_get_nonce(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport, ++ u32 seq) ++{ ++ u32 hash[MD5_DIGEST_WORDS]; ++ ++ hash[0] = (__force u32)saddr; ++ hash[1] = (__force u32)daddr; ++ hash[2] = ((__force u16)sport << 16) + (__force u16)dport; ++ hash[3] = seq; ++ ++ md5_transform(hash, mptcp_secret); ++ ++ return hash[0]; ++} ++ ++u64 mptcp_v4_get_key(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport) ++{ ++ u32 hash[MD5_DIGEST_WORDS]; ++ ++ hash[0] = (__force u32)saddr; ++ hash[1] = (__force u32)daddr; ++ hash[2] = ((__force u16)sport << 16) + (__force u16)dport; ++ hash[3] = mptcp_key_seed++; ++ ++ md5_transform(hash, mptcp_secret); ++ ++ return *((u64 *)hash); ++} ++ ++ ++static void mptcp_v4_reqsk_destructor(struct request_sock *req) ++{ ++ mptcp_reqsk_destructor(req); ++ ++ tcp_v4_reqsk_destructor(req); ++} ++ ++/* Similar to tcp_request_sock_ops */ ++struct request_sock_ops mptcp_request_sock_ops __read_mostly = { ++ .family = PF_INET, ++ .obj_size = sizeof(struct mptcp_request_sock), ++ .rtx_syn_ack = tcp_v4_rtx_synack, ++ .send_ack = tcp_v4_reqsk_send_ack, ++ .destructor = mptcp_v4_reqsk_destructor, ++ .send_reset = tcp_v4_send_reset, ++ .syn_ack_timeout = tcp_syn_ack_timeout, ++}; ++ ++static void mptcp_v4_reqsk_queue_hash_add(struct sock *meta_sk, ++ struct request_sock *req, ++ unsigned long timeout) ++{ ++ const u32 h1 = inet_synq_hash(inet_rsk(req)->ir_rmt_addr, ++ inet_rsk(req)->ir_rmt_port, ++ 0, MPTCP_HASH_SIZE); ++ /* We cannot call inet_csk_reqsk_queue_hash_add(), because we do not ++ * want to reset the keepalive-timer (responsible for retransmitting ++ * SYN/ACKs). We do not retransmit SYN/ACKs+MP_JOINs, because we cannot ++ * overload the keepalive timer. Also, it's not a big deal, because the ++ * third ACK of the MP_JOIN-handshake is sent in a reliable manner. So, ++ * if the third ACK gets lost, the client will handle the retransmission ++ * anyways. If our SYN/ACK gets lost, the client will retransmit the ++ * SYN. ++ */ ++ struct inet_connection_sock *meta_icsk = inet_csk(meta_sk); ++ struct listen_sock *lopt = meta_icsk->icsk_accept_queue.listen_opt; ++ const u32 h2 = inet_synq_hash(inet_rsk(req)->ir_rmt_addr, ++ inet_rsk(req)->ir_rmt_port, ++ lopt->hash_rnd, lopt->nr_table_entries); ++ ++ reqsk_queue_hash_req(&meta_icsk->icsk_accept_queue, h2, req, timeout); ++ reqsk_queue_added(&meta_icsk->icsk_accept_queue); ++ ++ spin_lock(&mptcp_reqsk_hlock); ++ list_add(&mptcp_rsk(req)->collide_tuple, &mptcp_reqsk_htb[h1]); ++ spin_unlock(&mptcp_reqsk_hlock); ++} ++ ++/* Similar to tcp_v4_conn_request */ ++static void mptcp_v4_join_request(struct sock *meta_sk, struct sk_buff *skb) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct tcp_options_received tmp_opt; ++ struct mptcp_options_received mopt; ++ struct request_sock *req; ++ struct inet_request_sock *ireq; ++ struct mptcp_request_sock *mtreq; ++ struct dst_entry *dst = NULL; ++ u8 mptcp_hash_mac[20]; ++ __be32 saddr = ip_hdr(skb)->saddr; ++ __be32 daddr = ip_hdr(skb)->daddr; ++ __u32 isn = TCP_SKB_CB(skb)->when; ++ int want_cookie = 0; ++ union inet_addr addr; ++ ++ tcp_clear_options(&tmp_opt); ++ mptcp_init_mp_opt(&mopt); ++ tmp_opt.mss_clamp = TCP_MSS_DEFAULT; ++ tmp_opt.user_mss = tcp_sk(meta_sk)->rx_opt.user_mss; ++ tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL); ++ ++ req = inet_reqsk_alloc(&mptcp_request_sock_ops); ++ if (!req) ++ return; ++ ++#ifdef CONFIG_TCP_MD5SIG ++ tcp_rsk(req)->af_specific = &tcp_request_sock_ipv4_ops; ++#endif ++ ++ tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; ++ tcp_openreq_init(req, &tmp_opt, skb); ++ ++ ireq = inet_rsk(req); ++ ireq->ir_loc_addr = daddr; ++ ireq->ir_rmt_addr = saddr; ++ ireq->no_srccheck = inet_sk(meta_sk)->transparent; ++ ireq->opt = tcp_v4_save_options(skb); ++ ++ if (security_inet_conn_request(meta_sk, skb, req)) ++ goto drop_and_free; ++ ++ if (!want_cookie || tmp_opt.tstamp_ok) ++ TCP_ECN_create_request(req, skb, sock_net(meta_sk)); ++ ++ if (!isn) { ++ struct flowi4 fl4; ++ ++ /* VJ's idea. We save last timestamp seen ++ * from the destination in peer table, when entering ++ * state TIME-WAIT, and check against it before ++ * accepting new connection request. ++ * ++ * If "isn" is not zero, this request hit alive ++ * timewait bucket, so that all the necessary checks ++ * are made in the function processing timewait state. ++ */ ++ if (tmp_opt.saw_tstamp && ++ tcp_death_row.sysctl_tw_recycle && ++ (dst = inet_csk_route_req(meta_sk, &fl4, req)) != NULL && ++ fl4.daddr == saddr) { ++ if (!tcp_peer_is_proven(req, dst, true)) { ++ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_PAWSPASSIVEREJECTED); ++ goto drop_and_release; ++ } ++ } ++ /* Kill the following clause, if you dislike this way. */ ++ else if (!sysctl_tcp_syncookies && ++ (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(meta_sk) < ++ (sysctl_max_syn_backlog >> 2)) && ++ !tcp_peer_is_proven(req, dst, false)) { ++ /* Without syncookies last quarter of ++ * backlog is filled with destinations, ++ * proven to be alive. ++ * It means that we continue to communicate ++ * to destinations, already remembered ++ * to the moment of synflood. ++ */ ++ LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"), ++ &saddr, ntohs(tcp_hdr(skb)->source)); ++ goto drop_and_release; ++ } ++ ++ isn = tcp_v4_init_sequence(skb); ++ } ++ tcp_rsk(req)->snt_isn = isn; ++ tcp_rsk(req)->snt_synack = tcp_time_stamp; ++ tcp_rsk(req)->listener = NULL; ++ ++ mtreq = mptcp_rsk(req); ++ mtreq->mpcb = mpcb; ++ INIT_LIST_HEAD(&mtreq->collide_tuple); ++ mtreq->mptcp_rem_nonce = mopt.mptcp_recv_nonce; ++ mtreq->mptcp_rem_key = mpcb->mptcp_rem_key; ++ mtreq->mptcp_loc_key = mpcb->mptcp_loc_key; ++ mtreq->mptcp_loc_nonce = mptcp_v4_get_nonce(saddr, daddr, ++ tcp_hdr(skb)->source, ++ tcp_hdr(skb)->dest, isn); ++ mptcp_hmac_sha1((u8 *)&mtreq->mptcp_loc_key, ++ (u8 *)&mtreq->mptcp_rem_key, ++ (u8 *)&mtreq->mptcp_loc_nonce, ++ (u8 *)&mtreq->mptcp_rem_nonce, (u32 *)mptcp_hash_mac); ++ mtreq->mptcp_hash_tmac = *(u64 *)mptcp_hash_mac; ++ ++ addr.ip = ireq->ir_loc_addr; ++ mtreq->loc_id = mpcb->pm_ops->get_local_id(AF_INET, &addr, sock_net(meta_sk)); ++ if (mtreq->loc_id == -1) /* Address not part of the allowed ones */ ++ goto drop_and_release; ++ mtreq->rem_id = mopt.rem_id; ++ mtreq->low_prio = mopt.low_prio; ++ tcp_rsk(req)->saw_mpc = 1; ++ ++ if (tcp_v4_send_synack(meta_sk, dst, req, skb_get_queue_mapping(skb))) ++ goto drop_and_free; ++ ++ /* Adding to request queue in metasocket */ ++ mptcp_v4_reqsk_queue_hash_add(meta_sk, req, TCP_TIMEOUT_INIT); ++ ++ return; ++ ++drop_and_release: ++ dst_release(dst); ++drop_and_free: ++ reqsk_free(req); ++ return; ++} ++ ++int mptcp_v4_rem_raddress(struct mptcp_cb *mpcb, u8 id) ++{ ++ int i; ++ ++ for (i = 0; i < MPTCP_MAX_ADDR; i++) { ++ if (!((1 << i) & mpcb->rem4_bits)) ++ continue; ++ ++ if (mpcb->remaddr4[i].rem4_id == id) { ++ /* remove address from bitfield */ ++ mpcb->rem4_bits &= ~(1 << i); ++ ++ return 0; ++ } ++ } ++ ++ return -1; ++} ++ ++/* Based on function tcp_v4_conn_request (tcp_ipv4.c) ++ * Returns -1 if there is no space anymore to store an additional ++ * address ++ */ ++int mptcp_v4_add_raddress(struct mptcp_cb *mpcb, const struct in_addr *addr, ++ __be16 port, u8 id) ++{ ++ int i; ++ struct mptcp_rem4 *rem4; ++ ++ mptcp_for_each_bit_set(mpcb->rem4_bits, i) { ++ rem4 = &mpcb->remaddr4[i]; ++ ++ /* Address is already in the list --- continue */ ++ if (rem4->rem4_id == id && ++ rem4->addr.s_addr == addr->s_addr && rem4->port == port) ++ return 0; ++ ++ /* This may be the case, when the peer is behind a NAT. He is ++ * trying to JOIN, thus sending the JOIN with a certain ID. ++ * However the src_addr of the IP-packet has been changed. We ++ * update the addr in the list, because this is the address as ++ * OUR BOX sees it. ++ */ ++ if (rem4->rem4_id == id && rem4->addr.s_addr != addr->s_addr) { ++ /* update the address */ ++ mptcp_debug("%s: updating old addr:%pI4 to addr %pI4 with id:%d\n", ++ __func__, &rem4->addr.s_addr, ++ &addr->s_addr, id); ++ rem4->addr.s_addr = addr->s_addr; ++ rem4->port = port; ++ mpcb->list_rcvd = 1; ++ return 0; ++ } ++ } ++ ++ i = mptcp_find_free_index(mpcb->rem4_bits); ++ /* Do we have already the maximum number of local/remote addresses? */ ++ if (i < 0) { ++ mptcp_debug("%s: At max num of remote addresses: %d --- not adding address: %pI4\n", ++ __func__, MPTCP_MAX_ADDR, &addr->s_addr); ++ return -1; ++ } ++ ++ rem4 = &mpcb->remaddr4[i]; ++ ++ /* Address is not known yet, store it */ ++ rem4->addr.s_addr = addr->s_addr; ++ rem4->port = port; ++ rem4->bitfield = 0; ++ rem4->retry_bitfield = 0; ++ rem4->rem4_id = id; ++ mpcb->list_rcvd = 1; ++ mpcb->rem4_bits |= (1 << i); ++ ++ return 0; ++} ++ ++/* Sets the bitfield of the remote-address field ++ * local address is not set as it will disappear with the global address-list ++ */ ++void mptcp_v4_set_init_addr_bit(struct mptcp_cb *mpcb, __be32 daddr, int index) ++{ ++ int i; ++ ++ mptcp_for_each_bit_set(mpcb->rem4_bits, i) { ++ if (mpcb->remaddr4[i].addr.s_addr == daddr) { ++ mpcb->remaddr4[i].bitfield |= (1 << index); ++ return; ++ } ++ } ++} ++ ++/* We only process join requests here. (either the SYN or the final ACK) */ ++int mptcp_v4_do_rcv(struct sock *meta_sk, struct sk_buff *skb) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct sock *child, *rsk = NULL; ++ int ret; ++ ++ if (!(TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_JOIN)) { ++ struct tcphdr *th = tcp_hdr(skb); ++ const struct iphdr *iph = ip_hdr(skb); ++ struct sock *sk; ++ ++ sk = inet_lookup_established(sock_net(meta_sk), &tcp_hashinfo, ++ iph->saddr, th->source, iph->daddr, ++ th->dest, inet_iif(skb)); ++ ++ if (!sk) { ++ kfree_skb(skb); ++ return 0; ++ } ++ if (is_meta_sk(sk)) { ++ WARN("%s Did not find a sub-sk - did found the meta!\n", __func__); ++ kfree_skb(skb); ++ sock_put(sk); ++ return 0; ++ } ++ ++ if (sk->sk_state == TCP_TIME_WAIT) { ++ inet_twsk_put(inet_twsk(sk)); ++ kfree_skb(skb); ++ return 0; ++ } ++ ++ ret = tcp_v4_do_rcv(sk, skb); ++ sock_put(sk); ++ ++ return ret; ++ } ++ TCP_SKB_CB(skb)->mptcp_flags = 0; ++ ++ /* Has been removed from the tk-table. Thus, no new subflows. ++ * ++ * Check for close-state is necessary, because we may have been closed ++ * without passing by mptcp_close(). ++ * ++ * When falling back, no new subflows are allowed either. ++ */ ++ if (meta_sk->sk_state == TCP_CLOSE || !tcp_sk(meta_sk)->inside_tk_table || ++ mpcb->infinite_mapping_rcv || mpcb->send_infinite_mapping) ++ goto reset_and_discard; ++ ++ child = tcp_v4_hnd_req(meta_sk, skb); ++ ++ if (!child) ++ goto discard; ++ ++ if (child != meta_sk) { ++ sock_rps_save_rxhash(child, skb); ++ /* We don't call tcp_child_process here, because we hold ++ * already the meta-sk-lock and are sure that it is not owned ++ * by the user. ++ */ ++ ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), skb->len); ++ bh_unlock_sock(child); ++ sock_put(child); ++ if (ret) { ++ rsk = child; ++ goto reset_and_discard; ++ } ++ } else { ++ if (tcp_hdr(skb)->syn) { ++ struct mp_join *join_opt = mptcp_find_join(skb); ++ /* Currently we make two calls to mptcp_find_join(). This ++ * can probably be optimized. ++ */ ++ if (mptcp_v4_add_raddress(mpcb, ++ (struct in_addr *)&ip_hdr(skb)->saddr, ++ 0, ++ join_opt->addr_id) < 0) ++ goto reset_and_discard; ++ mpcb->list_rcvd = 0; ++ ++ mptcp_v4_join_request(meta_sk, skb); ++ goto discard; ++ } ++ goto reset_and_discard; ++ } ++ return 0; ++ ++reset_and_discard: ++ tcp_v4_send_reset(rsk, skb); ++discard: ++ kfree_skb(skb); ++ return 0; ++} ++ ++/* After this, the ref count of the meta_sk associated with the request_sock ++ * is incremented. Thus it is the responsibility of the caller ++ * to call sock_put() when the reference is not needed anymore. ++ */ ++struct sock *mptcp_v4_search_req(const __be16 rport, const __be32 raddr, ++ const __be32 laddr, const struct net *net) ++{ ++ struct mptcp_request_sock *mtreq; ++ struct sock *meta_sk = NULL; ++ ++ spin_lock(&mptcp_reqsk_hlock); ++ list_for_each_entry(mtreq, ++ &mptcp_reqsk_htb[inet_synq_hash(raddr, rport, 0, ++ MPTCP_HASH_SIZE)], ++ collide_tuple) { ++ struct inet_request_sock *ireq = inet_rsk(rev_mptcp_rsk(mtreq)); ++ meta_sk = mtreq->mpcb->meta_sk; ++ ++ if (ireq->ir_rmt_port == rport && ++ ireq->ir_rmt_addr == raddr && ++ ireq->ir_loc_addr == laddr && ++ rev_mptcp_rsk(mtreq)->rsk_ops->family == AF_INET && ++ net_eq(net, sock_net(meta_sk))) ++ break; ++ meta_sk = NULL; ++ } ++ ++ if (meta_sk && unlikely(!atomic_inc_not_zero(&meta_sk->sk_refcnt))) ++ meta_sk = NULL; ++ spin_unlock(&mptcp_reqsk_hlock); ++ ++ return meta_sk; ++} ++ ++/* Create a new IPv4 subflow. ++ * ++ * We are in user-context and meta-sock-lock is hold. ++ */ ++int mptcp_init4_subsockets(struct sock *meta_sk, const struct mptcp_loc4 *loc, ++ struct mptcp_rem4 *rem) ++{ ++ struct tcp_sock *tp; ++ struct sock *sk; ++ struct sockaddr_in loc_in, rem_in; ++ struct socket sock; ++ int ulid_size = 0, ret; ++ ++ /** First, create and prepare the new socket */ ++ ++ sock.type = meta_sk->sk_socket->type; ++ sock.state = SS_UNCONNECTED; ++ sock.wq = meta_sk->sk_socket->wq; ++ sock.file = meta_sk->sk_socket->file; ++ sock.ops = NULL; ++ ++ ret = inet_create(sock_net(meta_sk), &sock, IPPROTO_TCP, 1); ++ if (unlikely(ret < 0)) { ++ mptcp_debug("%s inet_create failed ret: %d\n", __func__, ret); ++ return ret; ++ } ++ ++ sk = sock.sk; ++ tp = tcp_sk(sk); ++ ++ /* All subsockets need the MPTCP-lock-class */ ++ lockdep_set_class_and_name(&(sk)->sk_lock.slock, &meta_slock_key, "slock-AF_INET-MPTCP"); ++ lockdep_init_map(&(sk)->sk_lock.dep_map, "sk_lock-AF_INET-MPTCP", &meta_key, 0); ++ ++ if (mptcp_add_sock(meta_sk, sk, loc->loc4_id, rem->rem4_id, GFP_KERNEL)) ++ goto error; ++ ++ tp->mptcp->slave_sk = 1; ++ tp->mptcp->low_prio = loc->low_prio; ++ ++ /* Initializing the timer for an MPTCP subflow */ ++ setup_timer(&tp->mptcp->mptcp_ack_timer, mptcp_ack_handler, (unsigned long)sk); ++ ++ /** Then, connect the socket to the peer */ ++ ++ ulid_size = sizeof(struct sockaddr_in); ++ loc_in.sin_family = AF_INET; ++ rem_in.sin_family = AF_INET; ++ loc_in.sin_port = 0; ++ if (rem->port) ++ rem_in.sin_port = rem->port; ++ else ++ rem_in.sin_port = inet_sk(meta_sk)->inet_dport; ++ loc_in.sin_addr = loc->addr; ++ rem_in.sin_addr = rem->addr; ++ ++ ret = sock.ops->bind(&sock, (struct sockaddr *)&loc_in, ulid_size); ++ if (ret < 0) { ++ mptcp_debug("%s: MPTCP subsocket bind() failed, error %d\n", ++ __func__, ret); ++ goto error; ++ } ++ ++ mptcp_debug("%s: token %#x pi %d src_addr:%pI4:%d dst_addr:%pI4:%d\n", ++ __func__, tcp_sk(meta_sk)->mpcb->mptcp_loc_token, ++ tp->mptcp->path_index, &loc_in.sin_addr, ++ ntohs(loc_in.sin_port), &rem_in.sin_addr, ++ ntohs(rem_in.sin_port)); ++ ++ ret = sock.ops->connect(&sock, (struct sockaddr *)&rem_in, ++ ulid_size, O_NONBLOCK); ++ if (ret < 0 && ret != -EINPROGRESS) { ++ mptcp_debug("%s: MPTCP subsocket connect() failed, error %d\n", ++ __func__, ret); ++ goto error; ++ } ++ ++ sk_set_socket(sk, meta_sk->sk_socket); ++ sk->sk_wq = meta_sk->sk_wq; ++ ++ return 0; ++ ++error: ++ /* May happen if mptcp_add_sock fails first */ ++ if (!tp->mpc) { ++ tcp_close(sk, 0); ++ } else { ++ local_bh_disable(); ++ mptcp_sub_force_close(sk); ++ local_bh_enable(); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(mptcp_init4_subsockets); ++ ++/* General initialization of IPv4 for MPTCP */ ++int mptcp_pm_v4_init(void) ++{ ++ int ret = 0; ++ struct request_sock_ops *ops = &mptcp_request_sock_ops; ++ ++ ops->slab_name = kasprintf(GFP_KERNEL, "request_sock_%s", "MPTCP"); ++ if (ops->slab_name == NULL) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ops->slab = kmem_cache_create(ops->slab_name, ops->obj_size, 0, ++ SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, ++ NULL); ++ ++ if (ops->slab == NULL) { ++ ret = -ENOMEM; ++ goto err_reqsk_create; ++ } ++ ++out: ++ return ret; ++ ++err_reqsk_create: ++ kfree(ops->slab_name); ++ ops->slab_name = NULL; ++ goto out; ++} ++ ++void mptcp_pm_v4_undo(void) ++{ ++ kmem_cache_destroy(mptcp_request_sock_ops.slab); ++ kfree(mptcp_request_sock_ops.slab_name); ++} ++ ++ +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_ipv6.c linux-3.14.45/net/mptcp/mptcp_ipv6.c +--- linux-3.14.45.orig/net/mptcp/mptcp_ipv6.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_ipv6.c 2015-06-24 14:15:48.931862523 +0200 +@@ -0,0 +1,822 @@ ++/* ++ * MPTCP implementation - IPv6-specific functions ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer: ++ * Jaakko Korkeaniemi ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int mptcp_v6v4_send_synack(struct sock *meta_sk, struct request_sock *req, ++ u16 queue_mapping); ++ ++__u32 mptcp_v6_get_nonce(const __be32 *saddr, const __be32 *daddr, ++ __be16 sport, __be16 dport, u32 seq) ++{ ++ u32 secret[MD5_MESSAGE_BYTES / 4]; ++ u32 hash[MD5_DIGEST_WORDS]; ++ u32 i; ++ ++ memcpy(hash, saddr, 16); ++ for (i = 0; i < 4; i++) ++ secret[i] = mptcp_secret[i] + (__force u32)daddr[i]; ++ secret[4] = mptcp_secret[4] + ++ (((__force u16)sport << 16) + (__force u16)dport); ++ secret[5] = seq; ++ for (i = 6; i < MD5_MESSAGE_BYTES / 4; i++) ++ secret[i] = mptcp_secret[i]; ++ ++ md5_transform(hash, secret); ++ ++ return hash[0]; ++} ++ ++u64 mptcp_v6_get_key(const __be32 *saddr, const __be32 *daddr, ++ __be16 sport, __be16 dport) ++{ ++ u32 secret[MD5_MESSAGE_BYTES / 4]; ++ u32 hash[MD5_DIGEST_WORDS]; ++ u32 i; ++ ++ memcpy(hash, saddr, 16); ++ for (i = 0; i < 4; i++) ++ secret[i] = mptcp_secret[i] + (__force u32)daddr[i]; ++ secret[4] = mptcp_secret[4] + ++ (((__force u16)sport << 16) + (__force u16)dport); ++ secret[5] = mptcp_key_seed++; ++ for (i = 5; i < MD5_MESSAGE_BYTES / 4; i++) ++ secret[i] = mptcp_secret[i]; ++ ++ md5_transform(hash, secret); ++ ++ return *((u64 *)hash); ++} ++ ++static void mptcp_v6_reqsk_destructor(struct request_sock *req) ++{ ++ mptcp_reqsk_destructor(req); ++ ++ tcp_v6_reqsk_destructor(req); ++} ++ ++/* Similar to tcp_v6_rtx_synack */ ++static int mptcp_v6_rtx_synack(struct sock *meta_sk, struct request_sock *req) ++{ ++ if (meta_sk->sk_family == AF_INET6) ++ return tcp_v6_rtx_synack(meta_sk, req); ++ ++ TCP_INC_STATS_BH(sock_net(meta_sk), TCP_MIB_RETRANSSEGS); ++ return mptcp_v6v4_send_synack(meta_sk, req, 0); ++} ++ ++/* Similar to tcp6_request_sock_ops */ ++struct request_sock_ops mptcp6_request_sock_ops __read_mostly = { ++ .family = AF_INET6, ++ .obj_size = sizeof(struct mptcp_request_sock), ++ .rtx_syn_ack = mptcp_v6_rtx_synack, ++ .send_ack = tcp_v6_reqsk_send_ack, ++ .destructor = mptcp_v6_reqsk_destructor, ++ .send_reset = tcp_v6_send_reset, ++ .syn_ack_timeout = tcp_syn_ack_timeout, ++}; ++ ++static void mptcp_v6_reqsk_queue_hash_add(struct sock *meta_sk, ++ struct request_sock *req, ++ unsigned long timeout) ++{ ++ const u32 h1 = inet6_synq_hash(&inet_rsk(req)->ir_v6_rmt_addr, ++ inet_rsk(req)->ir_rmt_port, ++ 0, MPTCP_HASH_SIZE); ++ /* We cannot call inet6_csk_reqsk_queue_hash_add(), because we do not ++ * want to reset the keepalive-timer (responsible for retransmitting ++ * SYN/ACKs). We do not retransmit SYN/ACKs+MP_JOINs, because we cannot ++ * overload the keepalive timer. Also, it's not a big deal, because the ++ * third ACK of the MP_JOIN-handshake is sent in a reliable manner. So, ++ * if the third ACK gets lost, the client will handle the retransmission ++ * anyways. If our SYN/ACK gets lost, the client will retransmit the ++ * SYN. ++ */ ++ struct inet_connection_sock *meta_icsk = inet_csk(meta_sk); ++ struct listen_sock *lopt = meta_icsk->icsk_accept_queue.listen_opt; ++ const u32 h2 = inet6_synq_hash(&inet_rsk(req)->ir_v6_rmt_addr, ++ inet_rsk(req)->ir_rmt_port, ++ lopt->hash_rnd, lopt->nr_table_entries); ++ ++ reqsk_queue_hash_req(&meta_icsk->icsk_accept_queue, h2, req, timeout); ++ reqsk_queue_added(&meta_icsk->icsk_accept_queue); ++ ++ spin_lock(&mptcp_reqsk_hlock); ++ list_add(&mptcp_rsk(req)->collide_tuple, &mptcp_reqsk_htb[h1]); ++ spin_unlock(&mptcp_reqsk_hlock); ++} ++ ++/* Similar to tcp_v6_send_synack ++ * ++ * The meta-socket is IPv4, but a new subsocket is IPv6 ++ */ ++static int mptcp_v6v4_send_synack(struct sock *meta_sk, struct request_sock *req, ++ u16 queue_mapping) ++{ ++ struct inet_request_sock *treq = inet_rsk(req); ++ struct sk_buff *skb; ++ struct flowi6 fl6; ++ struct dst_entry *dst; ++ int err = -ENOMEM; ++ ++ memset(&fl6, 0, sizeof(fl6)); ++ fl6.flowi6_proto = IPPROTO_TCP; ++ fl6.daddr = treq->ir_v6_rmt_addr; ++ fl6.saddr = treq->ir_v6_loc_addr; ++ fl6.flowlabel = 0; ++ fl6.flowi6_oif = treq->ir_iif; ++ fl6.flowi6_mark = meta_sk->sk_mark; ++ fl6.fl6_dport = inet_rsk(req)->ir_rmt_port; ++ fl6.fl6_sport = htons(inet_rsk(req)->ir_num); ++ security_req_classify_flow(req, flowi6_to_flowi(&fl6)); ++ ++ dst = ip6_dst_lookup_flow(meta_sk, &fl6, NULL); ++ if (IS_ERR(dst)) { ++ err = PTR_ERR(dst); ++ return err; ++ } ++ skb = tcp_make_synack(meta_sk, dst, req, NULL); ++ ++ if (skb) { ++ __tcp_v6_send_check(skb, &treq->ir_v6_loc_addr, ++ &treq->ir_v6_rmt_addr); ++ ++ fl6.daddr = treq->ir_v6_rmt_addr; ++ skb_set_queue_mapping(skb, queue_mapping); ++ err = ip6_xmit(meta_sk, skb, &fl6, NULL, 0); ++ err = net_xmit_eval(err); ++ } ++ ++ return err; ++} ++ ++/* Similar to tcp_v6_syn_recv_sock ++ * ++ * The meta-socket is IPv4, but a new subsocket is IPv6 ++ */ ++struct sock *mptcp_v6v4_syn_recv_sock(struct sock *meta_sk, struct sk_buff *skb, ++ struct request_sock *req, ++ struct dst_entry *dst) ++{ ++ struct inet_request_sock *treq; ++ struct ipv6_pinfo *newnp; ++ struct tcp6_sock *newtcp6sk; ++ struct inet_sock *newinet; ++ struct tcp_sock *newtp; ++ struct sock *newsk; ++ ++ treq = inet_rsk(req); ++ ++ if (sk_acceptq_is_full(meta_sk)) ++ goto out_overflow; ++ ++ if (!dst) { ++ /* This code is similar to inet6_csk_route_req, but as we ++ * don't have a np-pointer in the meta, we have to do it ++ * manually. ++ */ ++ struct flowi6 fl6; ++ ++ memset(&fl6, 0, sizeof(fl6)); ++ fl6.flowi6_proto = IPPROTO_TCP; ++ fl6.daddr = treq->ir_v6_rmt_addr; ++ fl6.saddr = treq->ir_v6_loc_addr; ++ fl6.flowi6_oif = treq->ir_iif; ++ fl6.flowi6_mark = meta_sk->sk_mark; ++ fl6.fl6_dport = inet_rsk(req)->ir_rmt_port; ++ fl6.fl6_sport = htons(inet_rsk(req)->ir_num); ++ security_req_classify_flow(req, flowi6_to_flowi(&fl6)); ++ ++ dst = ip6_dst_lookup_flow(meta_sk, &fl6, NULL); ++ if (IS_ERR(dst)) ++ goto out; ++ } ++ ++ newsk = tcp_create_openreq_child(meta_sk, req, skb); ++ if (newsk == NULL) ++ goto out_nonewsk; ++ ++ /* Diff to tcp_v6_syn_recv_sock: Must do this prior to __ip6_dst_store, ++ * as it tries to access the pinet6-pointer. ++ */ ++ newtcp6sk = (struct tcp6_sock *)newsk; ++ inet_sk(newsk)->pinet6 = &newtcp6sk->inet6; ++ ++ /* ++ * No need to charge this sock to the relevant IPv6 refcnt debug socks ++ * count here, tcp_create_openreq_child now does this for us, see the ++ * comment in that function for the gory details. -acme ++ */ ++ ++ newsk->sk_gso_type = SKB_GSO_TCPV6; ++ __ip6_dst_store(newsk, dst, NULL, NULL); ++ inet6_sk_rx_dst_set(newsk, skb); ++ ++ newtp = tcp_sk(newsk); ++ newinet = inet_sk(newsk); ++ newnp = inet6_sk(newsk); ++ ++ newsk->sk_v6_daddr = treq->ir_v6_rmt_addr; ++ newnp->saddr = treq->ir_v6_loc_addr; ++ newsk->sk_v6_rcv_saddr = treq->ir_v6_loc_addr; ++ newsk->sk_bound_dev_if = treq->ir_iif; ++ ++ /* Now IPv6 options... ++ ++ First: no IPv4 options. ++ */ ++ newinet->inet_opt = NULL; ++ newnp->ipv6_ac_list = NULL; ++ newnp->ipv6_fl_list = NULL; ++ newnp->rxopt.all = 0; ++ ++ /* Clone pktoptions received with SYN */ ++ newnp->pktoptions = NULL; ++ if (treq->pktopts != NULL) { ++ newnp->pktoptions = skb_clone(treq->pktopts, ++ sk_gfp_atomic(meta_sk, GFP_ATOMIC)); ++ consume_skb(treq->pktopts); ++ treq->pktopts = NULL; ++ if (newnp->pktoptions) ++ skb_set_owner_r(newnp->pktoptions, newsk); ++ } ++ newnp->opt = NULL; ++ newnp->mcast_oif = inet6_iif(skb); ++ newnp->mcast_hops = ipv6_hdr(skb)->hop_limit; ++ newnp->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(skb)); ++ ++ /* Initialization copied from inet6_create - normally this should have ++ * been handled by the memcpy as in tcp_v6_syn_recv_sock ++ */ ++ newnp->hop_limit = -1; ++ newnp->mc_loop = 1; ++ newnp->pmtudisc = IPV6_PMTUDISC_WANT; ++ (void)xchg(&newnp->rxpmtu, NULL); ++ ++ inet_csk(newsk)->icsk_ext_hdr_len = 0; ++ ++ tcp_mtup_init(newsk); ++ tcp_sync_mss(newsk, dst_mtu(dst)); ++ newtp->advmss = dst_metric_advmss(dst); ++ if (tcp_sk(meta_sk)->rx_opt.user_mss && ++ tcp_sk(meta_sk)->rx_opt.user_mss < newtp->advmss) ++ newtp->advmss = tcp_sk(meta_sk)->rx_opt.user_mss; ++ ++ tcp_initialize_rcv_mss(newsk); ++ ++ newinet->inet_daddr = LOOPBACK4_IPV6; ++ newinet->inet_saddr = LOOPBACK4_IPV6; ++ newinet->inet_rcv_saddr = LOOPBACK4_IPV6; ++ ++ if (__inet_inherit_port(meta_sk, newsk) < 0) { ++ inet_csk_prepare_forced_close(newsk); ++ tcp_done(newsk); ++ goto out; ++ } ++ __inet6_hash(newsk, NULL); ++ ++ return newsk; ++ ++out_overflow: ++ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_LISTENOVERFLOWS); ++out_nonewsk: ++ dst_release(dst); ++out: ++ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_LISTENDROPS); ++ return NULL; ++} ++ ++/* Similar to tcp_v6_conn_request */ ++static void mptcp_v6_join_request(struct sock *meta_sk, struct sk_buff *skb) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct tcp_options_received tmp_opt; ++ struct mptcp_options_received mopt; ++ struct ipv6_pinfo *np = inet6_sk(meta_sk); ++ struct request_sock *req; ++ struct inet_request_sock *treq; ++ struct mptcp_request_sock *mtreq; ++ u8 mptcp_hash_mac[20]; ++ __u32 isn = TCP_SKB_CB(skb)->when; ++ struct dst_entry *dst = NULL; ++ struct flowi6 fl6; ++ int want_cookie = 0; ++ union inet_addr addr; ++ ++ tcp_clear_options(&tmp_opt); ++ mptcp_init_mp_opt(&mopt); ++ tmp_opt.mss_clamp = TCP_MSS_DEFAULT; ++ tmp_opt.user_mss = tcp_sk(meta_sk)->rx_opt.user_mss; ++ tcp_parse_options(skb, &tmp_opt, &mopt, 0, NULL); ++ ++ req = inet6_reqsk_alloc(&mptcp6_request_sock_ops); ++ if (!req) ++ return; ++ ++#ifdef CONFIG_TCP_MD5SIG ++ tcp_rsk(req)->af_specific = &tcp_request_sock_ipv6_ops; ++#endif ++ ++ tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; ++ tcp_openreq_init(req, &tmp_opt, skb); ++ ++ treq = inet_rsk(req); ++ treq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; ++ treq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; ++ ++ if (!want_cookie || tmp_opt.tstamp_ok) ++ TCP_ECN_create_request(req, skb, sock_net(meta_sk)); ++ ++ treq->ir_iif = meta_sk->sk_bound_dev_if; ++ ++ /* So that link locals have meaning */ ++ if (!meta_sk->sk_bound_dev_if && ++ ipv6_addr_type(&treq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) ++ treq->ir_iif = inet6_iif(skb); ++ ++ if (!isn) { ++ if (meta_sk->sk_family == AF_INET6 && ++ (ipv6_opt_accepted(meta_sk, skb) || ++ np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || ++ np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)) { ++ atomic_inc(&skb->users); ++ treq->pktopts = skb; ++ } ++ ++ /* VJ's idea. We save last timestamp seen ++ * from the destination in peer table, when entering ++ * state TIME-WAIT, and check against it before ++ * accepting new connection request. ++ * ++ * If "isn" is not zero, this request hit alive ++ * timewait bucket, so that all the necessary checks ++ * are made in the function processing timewait state. ++ */ ++ if (tmp_opt.saw_tstamp && ++ tcp_death_row.sysctl_tw_recycle && ++ (dst = inet6_csk_route_req(meta_sk, &fl6, req)) != NULL) { ++ if (!tcp_peer_is_proven(req, dst, true)) { ++ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_PAWSPASSIVEREJECTED); ++ goto drop_and_release; ++ } ++ } ++ /* Kill the following clause, if you dislike this way. */ ++ else if (!sysctl_tcp_syncookies && ++ (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(meta_sk) < ++ (sysctl_max_syn_backlog >> 2)) && ++ !tcp_peer_is_proven(req, dst, false)) { ++ /* Without syncookies last quarter of ++ * backlog is filled with destinations, ++ * proven to be alive. ++ * It means that we continue to communicate ++ * to destinations, already remembered ++ * to the moment of synflood. ++ */ ++ LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open request from %pI6/%u\n", ++ &treq->ir_v6_rmt_addr, ++ ntohs(tcp_hdr(skb)->source)); ++ goto drop_and_release; ++ } ++ ++ isn = tcp_v6_init_sequence(skb); ++ } ++ ++ tcp_rsk(req)->snt_isn = isn; ++ tcp_rsk(req)->snt_synack = tcp_time_stamp; ++ tcp_rsk(req)->listener = NULL; ++ ++ mtreq = mptcp_rsk(req); ++ mtreq->mpcb = mpcb; ++ INIT_LIST_HEAD(&mtreq->collide_tuple); ++ mtreq->mptcp_rem_nonce = mopt.mptcp_recv_nonce; ++ mtreq->mptcp_rem_key = mpcb->mptcp_rem_key; ++ mtreq->mptcp_loc_key = mpcb->mptcp_loc_key; ++ mtreq->mptcp_loc_nonce = mptcp_v6_get_nonce(ipv6_hdr(skb)->daddr.s6_addr32, ++ ipv6_hdr(skb)->saddr.s6_addr32, ++ tcp_hdr(skb)->dest, ++ tcp_hdr(skb)->source, isn); ++ mptcp_hmac_sha1((u8 *)&mtreq->mptcp_loc_key, ++ (u8 *)&mtreq->mptcp_rem_key, ++ (u8 *)&mtreq->mptcp_loc_nonce, ++ (u8 *)&mtreq->mptcp_rem_nonce, (u32 *)mptcp_hash_mac); ++ mtreq->mptcp_hash_tmac = *(u64 *)mptcp_hash_mac; ++ ++ addr.in6 = treq->ir_v6_loc_addr; ++ mtreq->loc_id = mpcb->pm_ops->get_local_id(AF_INET6, &addr, sock_net(meta_sk)); ++ if (mtreq->loc_id == -1) /* Address not part of the allowed ones */ ++ goto drop_and_release; ++ mtreq->rem_id = mopt.rem_id; ++ mtreq->low_prio = mopt.low_prio; ++ tcp_rsk(req)->saw_mpc = 1; ++ ++ if (meta_sk->sk_family == AF_INET6) { ++ if (tcp_v6_send_synack(meta_sk, dst, &fl6, req, ++ skb_get_queue_mapping(skb))) ++ goto drop_and_free; ++ } else { ++ if (mptcp_v6v4_send_synack(meta_sk, req, skb_get_queue_mapping(skb))) ++ goto drop_and_free; ++ } ++ ++ /* Adding to request queue in metasocket */ ++ mptcp_v6_reqsk_queue_hash_add(meta_sk, req, TCP_TIMEOUT_INIT); ++ ++ return; ++ ++drop_and_release: ++ dst_release(dst); ++drop_and_free: ++ reqsk_free(req); ++ return; ++} ++ ++int mptcp_v6_rem_raddress(struct mptcp_cb *mpcb, u8 id) ++{ ++ int i; ++ ++ for (i = 0; i < MPTCP_MAX_ADDR; i++) { ++ if (!((1 << i) & mpcb->rem6_bits)) ++ continue; ++ ++ if (mpcb->remaddr6[i].rem6_id == id) { ++ /* remove address from bitfield */ ++ mpcb->rem6_bits &= ~(1 << i); ++ ++ return 0; ++ } ++ } ++ ++ return -1; ++} ++ ++/* Returns -1 if there is no space anymore to store an additional ++ * address ++ */ ++int mptcp_v6_add_raddress(struct mptcp_cb *mpcb, const struct in6_addr *addr, ++ __be16 port, u8 id) ++{ ++ int i; ++ struct mptcp_rem6 *rem6; ++ ++ mptcp_for_each_bit_set(mpcb->rem6_bits, i) { ++ rem6 = &mpcb->remaddr6[i]; ++ ++ /* Address is already in the list --- continue */ ++ if (rem6->rem6_id == id && ++ ipv6_addr_equal(&rem6->addr, addr) && rem6->port == port) ++ return 0; ++ ++ /* This may be the case, when the peer is behind a NAT. He is ++ * trying to JOIN, thus sending the JOIN with a certain ID. ++ * However the src_addr of the IP-packet has been changed. We ++ * update the addr in the list, because this is the address as ++ * OUR BOX sees it. ++ */ ++ if (rem6->rem6_id == id) { ++ /* update the address */ ++ mptcp_debug("%s: updating old addr: %pI6 to addr %pI6 with id:%d\n", ++ __func__, &rem6->addr, addr, id); ++ rem6->addr = *addr; ++ rem6->port = port; ++ mpcb->list_rcvd = 1; ++ return 0; ++ } ++ } ++ ++ i = mptcp_find_free_index(mpcb->rem6_bits); ++ /* Do we have already the maximum number of local/remote addresses? */ ++ if (i < 0) { ++ mptcp_debug("%s: At max num of remote addresses: %d --- not adding address: %pI6\n", ++ __func__, MPTCP_MAX_ADDR, addr); ++ return -1; ++ } ++ ++ rem6 = &mpcb->remaddr6[i]; ++ ++ /* Address is not known yet, store it */ ++ rem6->addr = *addr; ++ rem6->port = port; ++ rem6->bitfield = 0; ++ rem6->retry_bitfield = 0; ++ rem6->rem6_id = id; ++ mpcb->list_rcvd = 1; ++ mpcb->rem6_bits |= (1 << i); ++ ++ return 0; ++} ++ ++/* Sets the bitfield of the remote-address field ++ * local address is not set as it will disappear with the global address-list ++ */ ++void mptcp_v6_set_init_addr_bit(struct mptcp_cb *mpcb, ++ const struct in6_addr *daddr, int index) ++{ ++ int i; ++ mptcp_for_each_bit_set(mpcb->rem6_bits, i) { ++ if (ipv6_addr_equal(&mpcb->remaddr6[i].addr, daddr)) { ++ mpcb->remaddr6[i].bitfield |= (1 << index); ++ return; ++ } ++ } ++} ++ ++int mptcp_v6_do_rcv(struct sock *meta_sk, struct sk_buff *skb) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct sock *child, *rsk = NULL; ++ int ret; ++ ++ if (!(TCP_SKB_CB(skb)->mptcp_flags & MPTCPHDR_JOIN)) { ++ struct tcphdr *th = tcp_hdr(skb); ++ const struct ipv6hdr *ip6h = ipv6_hdr(skb); ++ struct sock *sk; ++ ++ sk = __inet6_lookup_established(sock_net(meta_sk), ++ &tcp_hashinfo, ++ &ip6h->saddr, th->source, ++ &ip6h->daddr, ntohs(th->dest), ++ inet6_iif(skb)); ++ ++ if (!sk) { ++ kfree_skb(skb); ++ return 0; ++ } ++ if (is_meta_sk(sk)) { ++ WARN("%s Did not find a sub-sk!\n", __func__); ++ kfree_skb(skb); ++ sock_put(sk); ++ return 0; ++ } ++ ++ if (sk->sk_state == TCP_TIME_WAIT) { ++ inet_twsk_put(inet_twsk(sk)); ++ kfree_skb(skb); ++ return 0; ++ } ++ ++ ret = tcp_v6_do_rcv(sk, skb); ++ sock_put(sk); ++ ++ return ret; ++ } ++ TCP_SKB_CB(skb)->mptcp_flags = 0; ++ ++ /* Has been removed from the tk-table. Thus, no new subflows. ++ * ++ * Check for close-state is necessary, because we may have been closed ++ * without passing by mptcp_close(). ++ * ++ * When falling back, no new subflows are allowed either. ++ */ ++ if (meta_sk->sk_state == TCP_CLOSE || !tcp_sk(meta_sk)->inside_tk_table || ++ mpcb->infinite_mapping_rcv || mpcb->send_infinite_mapping) ++ goto reset_and_discard; ++ ++ child = tcp_v6_hnd_req(meta_sk, skb); ++ ++ if (!child) ++ goto discard; ++ ++ if (child != meta_sk) { ++ sock_rps_save_rxhash(child, skb); ++ /* We don't call tcp_child_process here, because we hold ++ * already the meta-sk-lock and are sure that it is not owned ++ * by the user. ++ */ ++ ret = tcp_rcv_state_process(child, skb, tcp_hdr(skb), skb->len); ++ bh_unlock_sock(child); ++ sock_put(child); ++ if (ret) { ++ rsk = child; ++ goto reset_and_discard; ++ } ++ } else { ++ if (tcp_hdr(skb)->syn) { ++ struct mp_join *join_opt = mptcp_find_join(skb); ++ /* Currently we make two calls to mptcp_find_join(). This ++ * can probably be optimized. */ ++ if (mptcp_v6_add_raddress(mpcb, ++ (struct in6_addr *)&ipv6_hdr(skb)->saddr, ++ 0, ++ join_opt->addr_id) < 0) ++ goto reset_and_discard; ++ mpcb->list_rcvd = 0; ++ ++ mptcp_v6_join_request(meta_sk, skb); ++ goto discard; ++ } ++ goto reset_and_discard; ++ } ++ return 0; ++ ++reset_and_discard: ++ tcp_v6_send_reset(rsk, skb); ++discard: ++ kfree_skb(skb); ++ return 0; ++} ++ ++/* After this, the ref count of the meta_sk associated with the request_sock ++ * is incremented. Thus it is the responsibility of the caller ++ * to call sock_put() when the reference is not needed anymore. ++ */ ++struct sock *mptcp_v6_search_req(const __be16 rport, const struct in6_addr *raddr, ++ const struct in6_addr *laddr, const struct net *net) ++{ ++ struct mptcp_request_sock *mtreq; ++ struct sock *meta_sk = NULL; ++ ++ spin_lock(&mptcp_reqsk_hlock); ++ list_for_each_entry(mtreq, ++ &mptcp_reqsk_htb[inet6_synq_hash(raddr, rport, 0, ++ MPTCP_HASH_SIZE)], ++ collide_tuple) { ++ struct inet_request_sock *treq = inet_rsk(rev_mptcp_rsk(mtreq)); ++ meta_sk = mtreq->mpcb->meta_sk; ++ ++ if (inet_rsk(rev_mptcp_rsk(mtreq))->ir_rmt_port == rport && ++ rev_mptcp_rsk(mtreq)->rsk_ops->family == AF_INET6 && ++ ipv6_addr_equal(&treq->ir_v6_rmt_addr, raddr) && ++ ipv6_addr_equal(&treq->ir_v6_loc_addr, laddr) && ++ net_eq(net, sock_net(meta_sk))) ++ break; ++ meta_sk = NULL; ++ } ++ ++ if (meta_sk && unlikely(!atomic_inc_not_zero(&meta_sk->sk_refcnt))) ++ meta_sk = NULL; ++ spin_unlock(&mptcp_reqsk_hlock); ++ ++ return meta_sk; ++} ++ ++/* Create a new IPv6 subflow. ++ * ++ * We are in user-context and meta-sock-lock is hold. ++ */ ++int mptcp_init6_subsockets(struct sock *meta_sk, const struct mptcp_loc6 *loc, ++ struct mptcp_rem6 *rem) ++{ ++ struct tcp_sock *tp; ++ struct sock *sk; ++ struct sockaddr_in6 loc_in, rem_in; ++ struct socket sock; ++ int ulid_size = 0, ret; ++ ++ /** First, create and prepare the new socket */ ++ ++ sock.type = meta_sk->sk_socket->type; ++ sock.state = SS_UNCONNECTED; ++ sock.wq = meta_sk->sk_socket->wq; ++ sock.file = meta_sk->sk_socket->file; ++ sock.ops = NULL; ++ ++ ret = inet6_create(sock_net(meta_sk), &sock, IPPROTO_TCP, 1); ++ if (unlikely(ret < 0)) { ++ mptcp_debug("%s inet6_create failed ret: %d\n", __func__, ret); ++ return ret; ++ } ++ ++ sk = sock.sk; ++ tp = tcp_sk(sk); ++ ++ /* All subsockets need the MPTCP-lock-class */ ++ lockdep_set_class_and_name(&(sk)->sk_lock.slock, &meta_slock_key, "slock-AF_INET-MPTCP"); ++ lockdep_init_map(&(sk)->sk_lock.dep_map, "sk_lock-AF_INET-MPTCP", &meta_key, 0); ++ ++ if (mptcp_add_sock(meta_sk, sk, loc->loc6_id, rem->rem6_id, GFP_KERNEL)) ++ goto error; ++ ++ tp->mptcp->slave_sk = 1; ++ tp->mptcp->low_prio = loc->low_prio; ++ ++ /* Initializing the timer for an MPTCP subflow */ ++ setup_timer(&tp->mptcp->mptcp_ack_timer, mptcp_ack_handler, (unsigned long)sk); ++ ++ /** Then, connect the socket to the peer */ ++ ++ ulid_size = sizeof(struct sockaddr_in6); ++ loc_in.sin6_family = AF_INET6; ++ rem_in.sin6_family = AF_INET6; ++ loc_in.sin6_port = 0; ++ if (rem->port) ++ rem_in.sin6_port = rem->port; ++ else ++ rem_in.sin6_port = inet_sk(meta_sk)->inet_dport; ++ loc_in.sin6_addr = loc->addr; ++ rem_in.sin6_addr = rem->addr; ++ ++ ret = sock.ops->bind(&sock, (struct sockaddr *)&loc_in, ulid_size); ++ if (ret < 0) { ++ mptcp_debug("%s: MPTCP subsocket bind()failed, error %d\n", ++ __func__, ret); ++ goto error; ++ } ++ ++ mptcp_debug("%s: token %#x pi %d src_addr:%pI6:%d dst_addr:%pI6:%d\n", ++ __func__, tcp_sk(meta_sk)->mpcb->mptcp_loc_token, ++ tp->mptcp->path_index, &loc_in.sin6_addr, ++ ntohs(loc_in.sin6_port), &rem_in.sin6_addr, ++ ntohs(rem_in.sin6_port)); ++ ++ ret = sock.ops->connect(&sock, (struct sockaddr *)&rem_in, ++ ulid_size, O_NONBLOCK); ++ if (ret < 0 && ret != -EINPROGRESS) { ++ mptcp_debug("%s: MPTCP subsocket connect() failed, error %d\n", ++ __func__, ret); ++ goto error; ++ } ++ ++ sk_set_socket(sk, meta_sk->sk_socket); ++ sk->sk_wq = meta_sk->sk_wq; ++ ++ return 0; ++ ++error: ++ /* May happen if mptcp_add_sock fails first */ ++ if (!tp->mpc) { ++ tcp_close(sk, 0); ++ } else { ++ local_bh_disable(); ++ mptcp_sub_force_close(sk); ++ local_bh_enable(); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(mptcp_init6_subsockets); ++ ++int mptcp_pm_v6_init(void) ++{ ++ int ret = 0; ++ struct request_sock_ops *ops = &mptcp6_request_sock_ops; ++ ++ ops->slab_name = kasprintf(GFP_KERNEL, "request_sock_%s", "MPTCP6"); ++ if (ops->slab_name == NULL) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ops->slab = kmem_cache_create(ops->slab_name, ops->obj_size, 0, ++ SLAB_DESTROY_BY_RCU|SLAB_HWCACHE_ALIGN, ++ NULL); ++ ++ if (ops->slab == NULL) { ++ ret = -ENOMEM; ++ goto err_reqsk_create; ++ } ++ ++out: ++ return ret; ++ ++err_reqsk_create: ++ kfree(ops->slab_name); ++ ops->slab_name = NULL; ++ goto out; ++} ++ ++void mptcp_pm_v6_undo(void) ++{ ++ kmem_cache_destroy(mptcp6_request_sock_ops.slab); ++ kfree(mptcp6_request_sock_ops.slab_name); ++} +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_ndiffports.c linux-3.14.45/net/mptcp/mptcp_ndiffports.c +--- linux-3.14.45.orig/net/mptcp/mptcp_ndiffports.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_ndiffports.c 2015-06-24 14:15:48.931862523 +0200 +@@ -0,0 +1,171 @@ ++#include ++ ++#include ++#include ++ ++#if IS_ENABLED(CONFIG_IPV6) ++#include ++#endif ++ ++struct ndiffports_priv { ++ /* Worker struct for subflow establishment */ ++ struct work_struct subflow_work; ++ ++ struct mptcp_cb *mpcb; ++}; ++ ++static int sysctl_mptcp_ndiffports __read_mostly = 2; ++ ++/** ++ * Create all new subflows, by doing calls to mptcp_initX_subsockets ++ * ++ * This function uses a goto next_subflow, to allow releasing the lock between ++ * new subflows and giving other processes a chance to do some work on the ++ * socket and potentially finishing the communication. ++ **/ ++static void create_subflow_worker(struct work_struct *work) ++{ ++ struct ndiffports_priv *pm_priv = container_of(work, ++ struct ndiffports_priv, ++ subflow_work); ++ struct mptcp_cb *mpcb = pm_priv->mpcb; ++ struct sock *meta_sk = mpcb->meta_sk; ++ int iter = 0; ++ ++next_subflow: ++ if (iter) { ++ release_sock(meta_sk); ++ mutex_unlock(&mpcb->mpcb_mutex); ++ ++ yield(); ++ } ++ mutex_lock(&mpcb->mpcb_mutex); ++ lock_sock_nested(meta_sk, SINGLE_DEPTH_NESTING); ++ ++ iter++; ++ ++ if (sock_flag(meta_sk, SOCK_DEAD)) ++ goto exit; ++ ++ if (mpcb->master_sk && ++ !tcp_sk(mpcb->master_sk)->mptcp->fully_established) ++ goto exit; ++ ++ if (sysctl_mptcp_ndiffports > iter && ++ sysctl_mptcp_ndiffports > mpcb->cnt_subflows) { ++ if (meta_sk->sk_family == AF_INET || ++ mptcp_v6_is_v4_mapped(meta_sk)) { ++ struct mptcp_loc4 loc; ++ ++ loc.addr.s_addr = inet_sk(meta_sk)->inet_saddr; ++ loc.loc4_id = 0; ++ loc.low_prio = 0; ++ ++ mptcp_init4_subsockets(meta_sk, &loc, &mpcb->remaddr4[0]); ++ } else { ++#if IS_ENABLED(CONFIG_IPV6) ++ struct mptcp_loc6 loc; ++ ++ loc.addr = inet6_sk(meta_sk)->saddr; ++ loc.loc6_id = 0; ++ loc.low_prio = 0; ++ ++ mptcp_init6_subsockets(meta_sk, &loc, &mpcb->remaddr6[0]); ++#endif ++ } ++ goto next_subflow; ++ } ++ ++exit: ++ release_sock(meta_sk); ++ mutex_unlock(&mpcb->mpcb_mutex); ++ sock_put(meta_sk); ++} ++ ++static void ndiffports_new_session(struct sock *meta_sk, int index) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct ndiffports_priv *fmp = (struct ndiffports_priv *)&mpcb->mptcp_pm[0]; ++ ++ /* Initialize workqueue-struct */ ++ INIT_WORK(&fmp->subflow_work, create_subflow_worker); ++ fmp->mpcb = mpcb; ++} ++ ++static void ndiffports_create_subflows(struct sock *meta_sk) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct ndiffports_priv *pm_priv = (struct ndiffports_priv *)&mpcb->mptcp_pm[0]; ++ ++ if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv || ++ mpcb->send_infinite_mapping || ++ mpcb->server_side || sock_flag(meta_sk, SOCK_DEAD)) ++ return; ++ ++ if (!work_pending(&pm_priv->subflow_work)) { ++ sock_hold(meta_sk); ++ queue_work(mptcp_wq, &pm_priv->subflow_work); ++ } ++} ++ ++static int ndiffports_get_local_index(sa_family_t family, union inet_addr *addr, ++ struct net *net) ++{ ++ return 0; ++} ++ ++static struct mptcp_pm_ops ndiffports __read_mostly = { ++ .new_session = ndiffports_new_session, ++ .fully_established = ndiffports_create_subflows, ++ .get_local_index = ndiffports_get_local_index, ++ .get_local_id = ndiffports_get_local_index, ++ .name = "ndiffports", ++ .owner = THIS_MODULE, ++}; ++ ++static struct ctl_table ndiff_table[] = { ++ { ++ .procname = "mptcp_ndiffports", ++ .data = &sysctl_mptcp_ndiffports, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = &proc_dointvec ++ }, ++ { } ++}; ++ ++struct ctl_table_header *mptcp_sysctl; ++ ++/* General initialization of MPTCP_PM */ ++static int __init ndiffports_register(void) ++{ ++ BUILD_BUG_ON(sizeof(struct ndiffports_priv) > MPTCP_PM_SIZE); ++ ++ mptcp_sysctl = register_net_sysctl(&init_net, "net/mptcp", ndiff_table); ++ if (!mptcp_sysctl) ++ goto exit; ++ ++ if (mptcp_register_path_manager(&ndiffports)) ++ goto pm_failed; ++ ++ return 0; ++ ++pm_failed: ++ unregister_net_sysctl_table(mptcp_sysctl); ++exit: ++ return -1; ++} ++ ++static void ndiffports_unregister(void) ++{ ++ mptcp_unregister_path_manager(&ndiffports); ++ unregister_net_sysctl_table(mptcp_sysctl); ++} ++ ++module_init(ndiffports_register); ++module_exit(ndiffports_unregister); ++ ++MODULE_AUTHOR("Christoph Paasch"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("NDIFF-PORTS MPTCP"); ++MODULE_VERSION("0.88"); +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_ofo_queue.c linux-3.14.45/net/mptcp/mptcp_ofo_queue.c +--- linux-3.14.45.orig/net/mptcp/mptcp_ofo_queue.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_ofo_queue.c 2015-06-24 14:15:48.931862523 +0200 +@@ -0,0 +1,278 @@ ++/* ++ * MPTCP implementation - Fast algorithm for MPTCP meta-reordering ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer & Author: ++ * Christoph Paasch ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++void mptcp_remove_shortcuts(const struct mptcp_cb *mpcb, ++ const struct sk_buff *skb) ++{ ++ struct tcp_sock *tp; ++ ++ mptcp_for_each_tp(mpcb, tp) { ++ if (tp->mptcp->shortcut_ofoqueue == skb) { ++ tp->mptcp->shortcut_ofoqueue = NULL; ++ return; ++ } ++ } ++} ++ ++/* Does 'skb' fits after 'here' in the queue 'head' ? ++ * If yes, we queue it and return 1 ++ */ ++static int mptcp_ofo_queue_after(struct sk_buff_head *head, ++ struct sk_buff *skb, struct sk_buff *here, ++ struct tcp_sock *tp) ++{ ++ struct sock *meta_sk = tp->meta_sk; ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ u32 seq = TCP_SKB_CB(skb)->seq; ++ u32 end_seq = TCP_SKB_CB(skb)->end_seq; ++ ++ /* We want to queue skb after here, thus seq >= end_seq */ ++ if (before(seq, TCP_SKB_CB(here)->end_seq)) ++ return 0; ++ ++ if (seq == TCP_SKB_CB(here)->end_seq) { ++ bool fragstolen = false; ++ ++ if (!tcp_try_coalesce(meta_sk, here, skb, &fragstolen)) { ++ __skb_queue_after(&meta_tp->out_of_order_queue, here, skb); ++ return 1; ++ } else { ++ kfree_skb_partial(skb, fragstolen); ++ return -1; ++ } ++ } ++ ++ /* If here is the last one, we can always queue it */ ++ if (skb_queue_is_last(head, here)) { ++ __skb_queue_after(head, here, skb); ++ return 1; ++ } else { ++ struct sk_buff *skb1 = skb_queue_next(head, here); ++ /* It's not the last one, but does it fits between 'here' and ++ * the one after 'here' ? Thus, does end_seq <= after_here->seq ++ */ ++ if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) { ++ __skb_queue_after(head, here, skb); ++ return 1; ++ } ++ } ++ ++ return 0; ++} ++ ++static void try_shortcut(struct sk_buff *shortcut, struct sk_buff *skb, ++ struct sk_buff_head *head, struct tcp_sock *tp) ++{ ++ struct sock *meta_sk = tp->meta_sk; ++ struct tcp_sock *tp_it, *meta_tp = tcp_sk(meta_sk); ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ struct sk_buff *skb1, *best_shortcut = NULL; ++ u32 seq = TCP_SKB_CB(skb)->seq; ++ u32 end_seq = TCP_SKB_CB(skb)->end_seq; ++ u32 distance = 0xffffffff; ++ ++ /* First, check the tp's shortcut */ ++ if (!shortcut) { ++ if (skb_queue_empty(head)) { ++ __skb_queue_head(head, skb); ++ goto end; ++ } ++ } else { ++ int ret = mptcp_ofo_queue_after(head, skb, shortcut, tp); ++ /* Does the tp's shortcut is a hit? If yes, we insert. */ ++ ++ if (ret) { ++ skb = (ret > 0) ? skb : NULL; ++ goto end; ++ } ++ } ++ ++ /* Check the shortcuts of the other subsockets. */ ++ mptcp_for_each_tp(mpcb, tp_it) { ++ shortcut = tp_it->mptcp->shortcut_ofoqueue; ++ /* Can we queue it here? If yes, do so! */ ++ if (shortcut) { ++ int ret = mptcp_ofo_queue_after(head, skb, shortcut, tp); ++ ++ if (ret) { ++ skb = (ret > 0) ? skb : NULL; ++ goto end; ++ } ++ } ++ ++ /* Could not queue it, check if we are close. ++ * We are looking for a shortcut, close enough to seq to ++ * set skb1 prematurely and thus improve the subsequent lookup, ++ * which tries to find a skb1 so that skb1->seq <= seq. ++ * ++ * So, here we only take shortcuts, whose shortcut->seq > seq, ++ * and minimize the distance between shortcut->seq and seq and ++ * set best_shortcut to this one with the minimal distance. ++ * ++ * That way, the subsequent while-loop is shortest. ++ */ ++ if (shortcut && after(TCP_SKB_CB(shortcut)->seq, seq)) { ++ /* Are we closer than the current best shortcut? */ ++ if ((u32)(TCP_SKB_CB(shortcut)->seq - seq) < distance) { ++ distance = (u32)(TCP_SKB_CB(shortcut)->seq - seq); ++ best_shortcut = shortcut; ++ } ++ } ++ } ++ ++ if (best_shortcut) ++ skb1 = best_shortcut; ++ else ++ skb1 = skb_peek_tail(head); ++ ++ if (seq == TCP_SKB_CB(skb1)->end_seq) { ++ bool fragstolen = false; ++ ++ if (!tcp_try_coalesce(meta_sk, skb1, skb, &fragstolen)) { ++ __skb_queue_after(&meta_tp->out_of_order_queue, skb1, skb); ++ } else { ++ kfree_skb_partial(skb, fragstolen); ++ skb = NULL; ++ } ++ ++ goto end; ++ } ++ ++ /* Find the insertion point, starting from best_shortcut if available. ++ * ++ * Inspired from tcp_data_queue_ofo. ++ */ ++ while (1) { ++ /* skb1->seq <= seq */ ++ if (!after(TCP_SKB_CB(skb1)->seq, seq)) ++ break; ++ if (skb_queue_is_first(head, skb1)) { ++ skb1 = NULL; ++ break; ++ } ++ skb1 = skb_queue_prev(head, skb1); ++ } ++ ++ /* Do skb overlap to previous one? */ ++ if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { ++ if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { ++ /* All the bits are present. */ ++ __kfree_skb(skb); ++ skb = NULL; ++ goto end; ++ } ++ if (seq == TCP_SKB_CB(skb1)->seq) { ++ if (skb_queue_is_first(head, skb1)) ++ skb1 = NULL; ++ else ++ skb1 = skb_queue_prev(head, skb1); ++ } ++ } ++ if (!skb1) ++ __skb_queue_head(head, skb); ++ else ++ __skb_queue_after(head, skb1, skb); ++ ++ /* And clean segments covered by new one as whole. */ ++ while (!skb_queue_is_last(head, skb)) { ++ skb1 = skb_queue_next(head, skb); ++ ++ if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) ++ break; ++ ++ __skb_unlink(skb1, head); ++ mptcp_remove_shortcuts(mpcb, skb1); ++ __kfree_skb(skb1); ++ } ++ ++end: ++ if (skb) { ++ skb_set_owner_r(skb, meta_sk); ++ tp->mptcp->shortcut_ofoqueue = skb; ++ } ++ ++ return; ++} ++ ++/** ++ * @sk: the subflow that received this skb. ++ */ ++void mptcp_add_meta_ofo_queue(struct sock *meta_sk, struct sk_buff *skb, ++ struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ try_shortcut(tp->mptcp->shortcut_ofoqueue, skb, ++ &tcp_sk(meta_sk)->out_of_order_queue, tp); ++} ++ ++void mptcp_ofo_queue(struct sock *meta_sk) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct sk_buff *skb; ++ ++ while ((skb = skb_peek(&meta_tp->out_of_order_queue)) != NULL) { ++ u32 old_rcv_nxt = meta_tp->rcv_nxt; ++ if (after(TCP_SKB_CB(skb)->seq, meta_tp->rcv_nxt)) ++ break; ++ ++ if (!after(TCP_SKB_CB(skb)->end_seq, meta_tp->rcv_nxt)) { ++ __skb_unlink(skb, &meta_tp->out_of_order_queue); ++ mptcp_remove_shortcuts(meta_tp->mpcb, skb); ++ __kfree_skb(skb); ++ continue; ++ } ++ ++ __skb_unlink(skb, &meta_tp->out_of_order_queue); ++ mptcp_remove_shortcuts(meta_tp->mpcb, skb); ++ ++ __skb_queue_tail(&meta_sk->sk_receive_queue, skb); ++ meta_tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; ++ mptcp_check_rcvseq_wrap(meta_tp, old_rcv_nxt); ++ ++ if (tcp_hdr(skb)->fin) ++ mptcp_fin(meta_sk); ++ } ++} ++ ++void mptcp_purge_ofo_queue(struct tcp_sock *meta_tp) ++{ ++ struct sk_buff_head *head = &meta_tp->out_of_order_queue; ++ struct sk_buff *skb, *tmp; ++ ++ skb_queue_walk_safe(head, skb, tmp) { ++ __skb_unlink(skb, head); ++ mptcp_remove_shortcuts(meta_tp->mpcb, skb); ++ kfree_skb(skb); ++ } ++} +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_olia.c linux-3.14.45/net/mptcp/mptcp_olia.c +--- linux-3.14.45.orig/net/mptcp/mptcp_olia.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_olia.c 2015-06-24 14:15:48.931862523 +0200 +@@ -0,0 +1,314 @@ ++/* ++ * MPTCP implementation - OPPORTUNISTIC LINKED INCREASES CONGESTION CONTROL: ++ * ++ * Algorithm design: ++ * Ramin Khalili ++ * Nicolas Gast ++ * Jean-Yves Le Boudec ++ * ++ * Implementation: ++ * Ramin Khalili ++ * ++ * Ported to the official MPTCP-kernel: ++ * Christoph Paasch ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++ ++#include ++#include ++ ++#include ++ ++static int scale = 10; ++ ++struct mptcp_olia { ++ u32 mptcp_loss1; ++ u32 mptcp_loss2; ++ u32 mptcp_loss3; ++ int epsilon_num; ++ u32 epsilon_den; ++ int mptcp_snd_cwnd_cnt; ++}; ++ ++static inline int mptcp_olia_sk_can_send(const struct sock *sk) ++{ ++ return mptcp_sk_can_send(sk) && tcp_sk(sk)->srtt; ++} ++ ++static inline u64 mptcp_olia_scale(u64 val, int scale) ++{ ++ return (u64) val << scale; ++} ++ ++/* take care of artificially inflate (see RFC5681) ++ * of cwnd during fast-retransmit phase ++ */ ++static u32 mptcp_get_crt_cwnd(struct sock *sk) ++{ ++ struct inet_connection_sock *icsk = inet_csk(sk); ++ ++ if (icsk->icsk_ca_state == TCP_CA_Recovery) ++ return tcp_sk(sk)->snd_ssthresh; ++ else ++ return tcp_sk(sk)->snd_cwnd; ++} ++ ++/* return the dominator of the first term of the increasing term */ ++static u64 mptcp_get_rate(struct mptcp_cb *mpcb , u32 path_rtt) ++{ ++ struct sock *sk; ++ u64 rate = 1; /* We have to avoid a zero-rate because it is used as a divisor */ ++ ++ mptcp_for_each_sk(mpcb, sk) { ++ struct tcp_sock *tp = tcp_sk(sk); ++ u64 scaled_num; ++ u32 tmp_cwnd; ++ ++ if (!mptcp_olia_sk_can_send(sk)) ++ continue; ++ ++ tmp_cwnd = mptcp_get_crt_cwnd(sk); ++ scaled_num = mptcp_olia_scale(tmp_cwnd, scale) * path_rtt; ++ rate += div_u64(scaled_num , tp->srtt); ++ } ++ rate *= rate; ++ return rate; ++} ++ ++/* find the maximum cwnd, used to find set M */ ++static u32 mptcp_get_max_cwnd(struct mptcp_cb *mpcb) ++{ ++ struct sock *sk; ++ u32 best_cwnd = 0; ++ ++ mptcp_for_each_sk(mpcb, sk) { ++ u32 tmp_cwnd; ++ ++ if (!mptcp_olia_sk_can_send(sk)) ++ continue; ++ ++ tmp_cwnd = mptcp_get_crt_cwnd(sk); ++ if (tmp_cwnd > best_cwnd) ++ best_cwnd = tmp_cwnd; ++ } ++ return best_cwnd; ++} ++ ++static void mptcp_get_epsilon(struct mptcp_cb *mpcb) ++{ ++ struct mptcp_olia *ca; ++ struct tcp_sock *tp; ++ struct sock *sk; ++ u64 tmp_int, tmp_rtt, best_int = 0, best_rtt = 1; ++ u32 max_cwnd = 1, best_cwnd = 1, tmp_cwnd; ++ u8 M = 0, B_not_M = 0; ++ ++ /* TODO - integrate this in the following loop - we just want to iterate once */ ++ ++ max_cwnd = mptcp_get_max_cwnd(mpcb); ++ ++ /* find the best path */ ++ mptcp_for_each_sk(mpcb, sk) { ++ tp = tcp_sk(sk); ++ ca = inet_csk_ca(sk); ++ ++ if (!mptcp_olia_sk_can_send(sk)) ++ continue; ++ ++ tmp_rtt = tp->srtt * tp->srtt; ++ /* TODO - check here and rename variables */ ++ tmp_int = max(ca->mptcp_loss3 - ca->mptcp_loss2, ++ ca->mptcp_loss2 - ca->mptcp_loss1); ++ ++ tmp_cwnd = mptcp_get_crt_cwnd(sk); ++ if (tmp_int * best_rtt >= best_int * tmp_rtt) { ++ best_rtt = tmp_rtt; ++ best_int = tmp_int; ++ best_cwnd = tmp_cwnd; ++ } ++ } ++ ++ /* TODO - integrate this here in mptcp_get_max_cwnd and in the previous loop */ ++ /* find the size of M and B_not_M */ ++ mptcp_for_each_sk(mpcb, sk) { ++ tp = tcp_sk(sk); ++ ca = inet_csk_ca(sk); ++ ++ if (!mptcp_olia_sk_can_send(sk)) ++ continue; ++ ++ tmp_cwnd = mptcp_get_crt_cwnd(sk); ++ if (tmp_cwnd == max_cwnd) { ++ M++; ++ } else { ++ tmp_rtt = tp->srtt * tp->srtt; ++ tmp_int = max(ca->mptcp_loss3 - ca->mptcp_loss2, ++ ca->mptcp_loss2 - ca->mptcp_loss1); ++ ++ if (tmp_int * best_rtt == best_int * tmp_rtt) ++ B_not_M++; ++ } ++ } ++ ++ /* check if the path is in M or B_not_M and set the value of epsilon accordingly */ ++ mptcp_for_each_sk(mpcb, sk) { ++ tp = tcp_sk(sk); ++ ca = inet_csk_ca(sk); ++ ++ if (!mptcp_olia_sk_can_send(sk)) ++ continue; ++ ++ if (B_not_M == 0) { ++ ca->epsilon_num = 0; ++ ca->epsilon_den = 1; ++ } else { ++ tmp_rtt = tp->srtt * tp->srtt; ++ tmp_int = max(ca->mptcp_loss3 - ca->mptcp_loss2, ++ ca->mptcp_loss2 - ca->mptcp_loss1); ++ tmp_cwnd = mptcp_get_crt_cwnd(sk); ++ ++ if (tmp_cwnd < max_cwnd && ++ tmp_int * best_rtt == best_int * tmp_rtt){ ++ ca->epsilon_num = 1; ++ ca->epsilon_den = mpcb->cnt_established * B_not_M; ++ } else if (tmp_cwnd == max_cwnd) { ++ ca->epsilon_num = -1; ++ ca->epsilon_den = mpcb->cnt_established * M; ++ } else { ++ ca->epsilon_num = 0; ++ ca->epsilon_den = 1; ++ } ++ } ++ } ++ ++} ++ ++/* setting the initial values */ ++static void mptcp_olia_init(struct sock *sk) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct mptcp_olia *ca = inet_csk_ca(sk); ++ ++ if (tp->mpc) { ++ ca->mptcp_loss1 = tp->snd_una; ++ ca->mptcp_loss2 = tp->snd_una; ++ ca->mptcp_loss3 = tp->snd_una; ++ ca->mptcp_snd_cwnd_cnt = 0; ++ ca->epsilon_num = 0; ++ ca->epsilon_den = 1; ++ } ++} ++ ++/* updating inter-loss distance and ssthresh */ ++static void mptcp_olia_set_state(struct sock *sk, u8 new_state) ++{ ++ if (!tcp_sk(sk)->mpc) ++ return; ++ ++ if (new_state == TCP_CA_Loss || ++ new_state == TCP_CA_Recovery || new_state == TCP_CA_CWR) { ++ struct mptcp_olia *ca = inet_csk_ca(sk); ++ ++ if (ca->mptcp_loss3 != ca->mptcp_loss2 && ++ !inet_csk(sk)->icsk_retransmits) { ++ ca->mptcp_loss1 = ca->mptcp_loss2; ++ ca->mptcp_loss2 = ca->mptcp_loss3; ++ } ++ } ++ ++} ++ ++/* main algorithm */ ++static void mptcp_olia_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct mptcp_olia *ca = inet_csk_ca(sk); ++ struct mptcp_cb *mpcb = tp->mpcb; ++ ++ u64 inc_num, inc_den, rate, cwnd_scaled; ++ ++ if (!tp->mpc) { ++ tcp_reno_cong_avoid(sk, ack, acked, in_flight); ++ return; ++ } ++ ++ ca->mptcp_loss3 = tp->snd_una; ++ ++ if (!tcp_is_cwnd_limited(sk, in_flight)) ++ return; ++ ++ /* slow start if it is in the safe area */ ++ if (tp->snd_cwnd <= tp->snd_ssthresh) { ++ tcp_slow_start(tp, acked); ++ return; ++ } ++ ++ mptcp_get_epsilon(mpcb); ++ rate = mptcp_get_rate(mpcb, tp->srtt); ++ cwnd_scaled = mptcp_olia_scale(tp->snd_cwnd, scale); ++ inc_den = ca->epsilon_den * tp->snd_cwnd * rate ? : 1; ++ ++ /* calculate the increasing term, scaling is used to reduce the rounding effect */ ++ if (ca->epsilon_num == -1) { ++ if (ca->epsilon_den * cwnd_scaled * cwnd_scaled < rate) { ++ inc_num = rate - ca->epsilon_den * ++ cwnd_scaled * cwnd_scaled; ++ ca->mptcp_snd_cwnd_cnt -= div64_u64( ++ mptcp_olia_scale(inc_num , scale) , inc_den); ++ } else { ++ inc_num = ca->epsilon_den * ++ cwnd_scaled * cwnd_scaled - rate; ++ ca->mptcp_snd_cwnd_cnt += div64_u64( ++ mptcp_olia_scale(inc_num , scale) , inc_den); ++ } ++ } else { ++ inc_num = ca->epsilon_num * rate + ++ ca->epsilon_den * cwnd_scaled * cwnd_scaled; ++ ca->mptcp_snd_cwnd_cnt += div64_u64( ++ mptcp_olia_scale(inc_num , scale) , inc_den); ++ } ++ ++ ++ if (ca->mptcp_snd_cwnd_cnt >= (1 << scale) - 1) { ++ if (tp->snd_cwnd < tp->snd_cwnd_clamp) ++ tp->snd_cwnd++; ++ ca->mptcp_snd_cwnd_cnt = 0; ++ } else if (ca->mptcp_snd_cwnd_cnt <= 0 - (1 << scale) + 1) { ++ tp->snd_cwnd = max((int) 1 , (int) tp->snd_cwnd - 1); ++ ca->mptcp_snd_cwnd_cnt = 0; ++ } ++} ++ ++static struct tcp_congestion_ops mptcp_olia = { ++ .init = mptcp_olia_init, ++ .ssthresh = tcp_reno_ssthresh, ++ .cong_avoid = mptcp_olia_cong_avoid, ++ .set_state = mptcp_olia_set_state, ++ .min_cwnd = tcp_reno_min_cwnd, ++ .owner = THIS_MODULE, ++ .name = "olia", ++}; ++ ++static int __init mptcp_olia_register(void) ++{ ++ BUILD_BUG_ON(sizeof(struct mptcp_olia) > ICSK_CA_PRIV_SIZE); ++ return tcp_register_congestion_control(&mptcp_olia); ++} ++ ++static void __exit mptcp_olia_unregister(void) ++{ ++ tcp_unregister_congestion_control(&mptcp_olia); ++} ++ ++module_init(mptcp_olia_register); ++module_exit(mptcp_olia_unregister); ++ ++MODULE_AUTHOR("Ramin Khalili, Nicolas Gast, Jean-Yves Le Boudec"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MPTCP COUPLED CONGESTION CONTROL"); ++MODULE_VERSION("0.1"); +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_output.c linux-3.14.45/net/mptcp/mptcp_output.c +--- linux-3.14.45.orig/net/mptcp/mptcp_output.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_output.c 2015-06-24 14:15:48.931862523 +0200 +@@ -0,0 +1,2255 @@ ++/* ++ * MPTCP implementation - Sending side ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer & Author: ++ * Christoph Paasch ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++static inline int mptcp_pi_to_flag(int pi) ++{ ++ return 1 << (pi - 1); ++} ++ ++static inline int mptcp_sub_len_remove_addr(u16 bitfield) ++{ ++ unsigned int c; ++ for (c = 0; bitfield; c++) ++ bitfield &= bitfield - 1; ++ return MPTCP_SUB_LEN_REMOVE_ADDR + c - 1; ++} ++ ++int mptcp_sub_len_remove_addr_align(u16 bitfield) ++{ ++ return ALIGN(mptcp_sub_len_remove_addr(bitfield), 4); ++} ++EXPORT_SYMBOL(mptcp_sub_len_remove_addr_align); ++ ++/* If the sub-socket sk available to send the skb? */ ++static int mptcp_is_available(struct sock *sk, struct sk_buff *skb, ++ unsigned int *mss) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ unsigned int mss_now; ++ ++ /* Set of states for which we are allowed to send data */ ++ if (!mptcp_sk_can_send(sk)) ++ return 0; ++ ++ /* We do not send data on this subflow unless it is ++ * fully established, i.e. the 4th ack has been received. ++ */ ++ if (tp->mptcp->pre_established) ++ return 0; ++ ++ if (tp->pf || ++ (tp->mpcb->noneligible & mptcp_pi_to_flag(tp->mptcp->path_index))) ++ return 0; ++ ++ if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss) { ++ /* If SACK is disabled, and we got a loss, TCP does not exit ++ * the loss-state until something above high_seq has been acked. ++ * (see tcp_try_undo_recovery) ++ * ++ * high_seq is the snd_nxt at the moment of the RTO. As soon ++ * as we have an RTO, we won't push data on the subflow. ++ * Thus, snd_una can never go beyond high_seq. ++ */ ++ if (!tcp_is_reno(tp)) ++ return 0; ++ else if (tp->snd_una != tp->high_seq) ++ return 0; ++ } ++ ++ if (!tp->mptcp->fully_established) { ++ /* Make sure that we send in-order data */ ++ if (skb && tp->mptcp->second_packet && ++ tp->mptcp->last_end_data_seq != TCP_SKB_CB(skb)->seq) ++ return 0; ++ } ++ ++ if (!tcp_cwnd_test(tp, skb)) ++ return 0; ++ ++ mss_now = tcp_current_mss(sk); ++ /* Don't send on this subflow if we bypass the allowed send-window at ++ * the per-subflow level. Similar to tcp_snd_wnd_test, but manually ++ * calculated end_seq (because here at this point end_seq is still at ++ * the meta-level). ++ */ ++ if (skb && after(tp->write_seq + min(skb->len, mss_now), tcp_wnd_end(tp))) ++ return 0; ++ ++ if (mss) ++ *mss = mss_now; ++ ++ return 1; ++} ++ ++/* Are we not allowed to reinject this skb on tp? */ ++static int mptcp_dont_reinject_skb(struct tcp_sock *tp, struct sk_buff *skb) ++{ ++ /* If the skb has already been enqueued in this sk, try to find ++ * another one. ++ */ ++ return skb && ++ /* Has the skb already been enqueued into this subsocket? */ ++ mptcp_pi_to_flag(tp->mptcp->path_index) & TCP_SKB_CB(skb)->path_mask; ++} ++ ++/* This is the scheduler. This function decides on which flow to send ++ * a given MSS. If all subflows are found to be busy, NULL is returned ++ * The flow is selected based on the shortest RTT. ++ * If all paths have full cong windows, we simply return NULL. ++ * ++ * Additionally, this function is aware of the backup-subflows. ++ */ ++static struct sock *get_available_subflow(struct sock *meta_sk, ++ struct sk_buff *skb, ++ unsigned int *mss_now) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct sock *sk, *bestsk = NULL, *lowpriosk = NULL, *backupsk = NULL; ++ unsigned int mss = 0, mss_lowprio = 0, mss_backup = 0; ++ u32 min_time_to_peer = 0xffffffff, lowprio_min_time_to_peer = 0xffffffff; ++ int cnt_backups = 0; ++ ++ /* if there is only one subflow, bypass the scheduling function */ ++ if (mpcb->cnt_subflows == 1) { ++ bestsk = (struct sock *)mpcb->connection_list; ++ if (!mptcp_is_available(bestsk, skb, mss_now)) ++ bestsk = NULL; ++ return bestsk; ++ } ++ ++ /* Answer data_fin on same subflow!!! */ ++ if (meta_sk->sk_shutdown & RCV_SHUTDOWN && ++ skb && mptcp_is_data_fin(skb)) { ++ mptcp_for_each_sk(mpcb, sk) { ++ if (tcp_sk(sk)->mptcp->path_index == mpcb->dfin_path_index && ++ mptcp_is_available(sk, skb, mss_now)) ++ return sk; ++ } ++ } ++ ++ /* First, find the best subflow */ ++ mptcp_for_each_sk(mpcb, sk) { ++ struct tcp_sock *tp = tcp_sk(sk); ++ int this_mss; ++ ++ if (tp->mptcp->rcv_low_prio || tp->mptcp->low_prio) ++ cnt_backups++; ++ ++ if ((tp->mptcp->rcv_low_prio || tp->mptcp->low_prio) && ++ tp->srtt < lowprio_min_time_to_peer) { ++ ++ if (!mptcp_is_available(sk, skb, &this_mss)) ++ continue; ++ ++ if (mptcp_dont_reinject_skb(tp, skb)) { ++ mss_backup = this_mss; ++ backupsk = sk; ++ continue; ++ } ++ ++ lowprio_min_time_to_peer = tp->srtt; ++ lowpriosk = sk; ++ mss_lowprio = this_mss; ++ } else if (!(tp->mptcp->rcv_low_prio || tp->mptcp->low_prio) && ++ tp->srtt < min_time_to_peer) { ++ if (!mptcp_is_available(sk, skb, &this_mss)) ++ continue; ++ ++ if (mptcp_dont_reinject_skb(tp, skb)) { ++ mss_backup = this_mss; ++ backupsk = sk; ++ continue; ++ } ++ ++ min_time_to_peer = tp->srtt; ++ bestsk = sk; ++ mss = this_mss; ++ } ++ } ++ ++ if (mpcb->cnt_established == cnt_backups && lowpriosk) { ++ mss = mss_lowprio; ++ sk = lowpriosk; ++ } else if (bestsk) { ++ sk = bestsk; ++ } else if (backupsk){ ++ /* It has been sent on all subflows once - let's give it a ++ * chance again by restarting its pathmask. ++ */ ++ if (skb) ++ TCP_SKB_CB(skb)->path_mask = 0; ++ mss = mss_backup; ++ sk = backupsk; ++ } ++ ++ if (mss_now) ++ *mss_now = mss; ++ ++ return sk; ++} ++ ++static struct mp_dss *mptcp_skb_find_dss(const struct sk_buff *skb) ++{ ++ if (!mptcp_is_data_seq(skb)) ++ return NULL; ++ ++ return (struct mp_dss *)(skb->data - (MPTCP_SUB_LEN_DSS_ALIGN + ++ MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN)); ++} ++ ++/* get the data-seq and end-data-seq and store them again in the ++ * tcp_skb_cb ++ */ ++static int mptcp_reconstruct_mapping(struct sk_buff *skb, struct sk_buff *orig_skb) ++{ ++ struct mp_dss *mpdss = mptcp_skb_find_dss(orig_skb); ++ u32 *p32; ++ u16 *p16; ++ ++ if (!mpdss || !mpdss->M) ++ return 1; ++ ++ /* Move the pointer to the data-seq */ ++ p32 = (u32 *)mpdss; ++ p32++; ++ if (mpdss->A) { ++ p32++; ++ if (mpdss->a) ++ p32++; ++ } ++ ++ TCP_SKB_CB(skb)->seq = ntohl(*p32); ++ ++ /* Get the data_len to calculate the end_data_seq */ ++ p32++; ++ p32++; ++ p16 = (u16 *)p32; ++ TCP_SKB_CB(skb)->end_seq = ntohs(*p16) + TCP_SKB_CB(skb)->seq; ++ ++ return 0; ++} ++ ++/* Similar to __pskb_copy and sk_stream_alloc_skb. */ ++static struct sk_buff *mptcp_pskb_copy(struct sk_buff *skb) ++{ ++ struct sk_buff *n; ++ /* The TCP header must be at least 32-bit aligned. */ ++ int size = ALIGN(skb_headlen(skb), 4); ++ ++ n = alloc_skb_fclone(size + MAX_TCP_HEADER, GFP_ATOMIC); ++ if (!n) ++ return NULL; ++ ++ /* Set the data pointer */ ++ skb_reserve(n, MAX_TCP_HEADER); ++ /* Set the tail pointer and length */ ++ skb_put(n, skb_headlen(skb)); ++ /* Copy the bytes */ ++ skb_copy_from_linear_data(skb, n->data, n->len); ++ ++ n->truesize += skb->data_len; ++ n->data_len = skb->data_len; ++ n->len = skb->len; ++ ++ if (skb_shinfo(skb)->nr_frags) { ++ int i; ++ ++ if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) { ++ if (skb_copy_ubufs(skb, GFP_ATOMIC)) { ++ kfree_skb(n); ++ n = NULL; ++ goto out; ++ } ++ } ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i]; ++ skb_frag_ref(skb, i); ++ } ++ skb_shinfo(n)->nr_frags = i; ++ } ++ ++ if (skb_has_frag_list(skb)) { ++ skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list; ++ skb_clone_fraglist(n); ++ } ++ ++ copy_skb_header(n, skb); ++out: ++ return n; ++} ++ ++/* Reinject data from one TCP subflow to the meta_sk. If sk == NULL, we are ++ * coming from the meta-retransmit-timer ++ */ ++static void __mptcp_reinject_data(struct sk_buff *orig_skb, struct sock *meta_sk, ++ struct sock *sk, int clone_it) ++{ ++ struct sk_buff *skb, *skb1; ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ u32 seq, end_seq; ++ ++ if (clone_it) { ++ /* pskb_copy is necessary here, because the TCP/IP-headers ++ * will be changed when it's going to be reinjected on another ++ * subflow. ++ */ ++ skb = mptcp_pskb_copy(orig_skb); ++ } else { ++ __skb_unlink(orig_skb, &sk->sk_write_queue); ++ sock_set_flag(sk, SOCK_QUEUE_SHRUNK); ++ sk->sk_wmem_queued -= orig_skb->truesize; ++ sk_mem_uncharge(sk, orig_skb->truesize); ++ skb = orig_skb; ++ } ++ if (unlikely(!skb)) ++ return; ++ ++ if (sk && mptcp_reconstruct_mapping(skb, orig_skb)) { ++ __kfree_skb(skb); ++ return; ++ } ++ ++ skb->sk = meta_sk; ++ ++ /* If it reached already the destination, we don't have to reinject it */ ++ if (!after(TCP_SKB_CB(skb)->end_seq, meta_tp->snd_una)) { ++ __kfree_skb(skb); ++ return; ++ } ++ ++ /* Only reinject segments that are fully covered by the mapping */ ++ if (skb->len + (mptcp_is_data_fin(skb) ? 1 : 0) != ++ TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq) { ++ u32 seq = TCP_SKB_CB(skb)->seq; ++ u32 end_seq = TCP_SKB_CB(skb)->end_seq; ++ ++ __kfree_skb(skb); ++ ++ /* Ok, now we have to look for the full mapping in the meta ++ * send-queue :S ++ */ ++ tcp_for_write_queue(skb, meta_sk) { ++ /* Not yet at the mapping? */ ++ if (before(TCP_SKB_CB(skb)->seq, seq)) ++ continue; ++ /* We have passed by the mapping */ ++ if (after(TCP_SKB_CB(skb)->end_seq, end_seq)) ++ return; ++ ++ __mptcp_reinject_data(skb, meta_sk, NULL, 1); ++ } ++ return; ++ } ++ ++ /* If it's empty, just add */ ++ if (skb_queue_empty(&mpcb->reinject_queue)) { ++ skb_queue_head(&mpcb->reinject_queue, skb); ++ return; ++ } ++ ++ /* Find place to insert skb - or even we can 'drop' it, as the ++ * data is already covered by other skb's in the reinject-queue. ++ * ++ * This is inspired by code from tcp_data_queue. ++ */ ++ ++ skb1 = skb_peek_tail(&mpcb->reinject_queue); ++ seq = TCP_SKB_CB(skb)->seq; ++ while (1) { ++ if (!after(TCP_SKB_CB(skb1)->seq, seq)) ++ break; ++ if (skb_queue_is_first(&mpcb->reinject_queue, skb1)) { ++ skb1 = NULL; ++ break; ++ } ++ skb1 = skb_queue_prev(&mpcb->reinject_queue, skb1); ++ } ++ ++ /* Do skb overlap to previous one? */ ++ end_seq = TCP_SKB_CB(skb)->end_seq; ++ if (skb1 && before(seq, TCP_SKB_CB(skb1)->end_seq)) { ++ if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) { ++ /* All the bits are present. Don't reinject */ ++ __kfree_skb(skb); ++ return; ++ } ++ if (seq == TCP_SKB_CB(skb1)->seq) { ++ if (skb_queue_is_first(&mpcb->reinject_queue, skb1)) ++ skb1 = NULL; ++ else ++ skb1 = skb_queue_prev(&mpcb->reinject_queue, skb1); ++ } ++ } ++ if (!skb1) ++ __skb_queue_head(&mpcb->reinject_queue, skb); ++ else ++ __skb_queue_after(&mpcb->reinject_queue, skb1, skb); ++ ++ /* And clean segments covered by new one as whole. */ ++ while (!skb_queue_is_last(&mpcb->reinject_queue, skb)) { ++ skb1 = skb_queue_next(&mpcb->reinject_queue, skb); ++ ++ if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) ++ break; ++ ++ __skb_unlink(skb1, &mpcb->reinject_queue); ++ __kfree_skb(skb1); ++ } ++ return; ++} ++ ++/* Inserts data into the reinject queue */ ++void mptcp_reinject_data(struct sock *sk, int clone_it) ++{ ++ struct sk_buff *skb_it, *tmp; ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct sock *meta_sk = tp->meta_sk; ++ ++ /* It has already been closed - there is really no point in reinjecting */ ++ if (meta_sk->sk_state == TCP_CLOSE) ++ return; ++ ++ skb_queue_walk_safe(&sk->sk_write_queue, skb_it, tmp) { ++ struct tcp_skb_cb *tcb = TCP_SKB_CB(skb_it); ++ /* Subflow syn's and fin's are not reinjected. ++ * ++ * As well as empty subflow-fins with a data-fin. ++ * They are reinjected below (without the subflow-fin-flag) ++ */ ++ if (tcb->tcp_flags & TCPHDR_SYN || ++ (tcb->tcp_flags & TCPHDR_FIN && !mptcp_is_data_fin(skb_it)) || ++ (tcb->tcp_flags & TCPHDR_FIN && mptcp_is_data_fin(skb_it) && !skb_it->len)) ++ continue; ++ ++ __mptcp_reinject_data(skb_it, meta_sk, sk, clone_it); ++ } ++ ++ skb_it = tcp_write_queue_tail(meta_sk); ++ /* If sk has sent the empty data-fin, we have to reinject it too. */ ++ if (skb_it && mptcp_is_data_fin(skb_it) && skb_it->len == 0 && ++ TCP_SKB_CB(skb_it)->path_mask & mptcp_pi_to_flag(tp->mptcp->path_index)) { ++ __mptcp_reinject_data(skb_it, meta_sk, NULL, 1); ++ } ++ ++ mptcp_push_pending_frames(meta_sk); ++ ++ tp->pf = 1; ++} ++EXPORT_SYMBOL(mptcp_reinject_data); ++ ++static void mptcp_combine_dfin(struct sk_buff *skb, struct sock *meta_sk, ++ struct sock *subsk) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ struct sock *sk_it; ++ int all_empty = 1, all_acked; ++ ++ /* In infinite mapping we always try to combine */ ++ if (mpcb->infinite_mapping_snd && tcp_close_state(subsk)) { ++ subsk->sk_shutdown |= SEND_SHUTDOWN; ++ TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN; ++ return; ++ } ++ ++ /* Don't combine, if they didn't combine - otherwise we end up in ++ * TIME_WAIT, even if our app is smart enough to avoid it ++ */ ++ if (meta_sk->sk_shutdown & RCV_SHUTDOWN) { ++ if (!mpcb->dfin_combined) ++ return; ++ } ++ ++ /* If no other subflow has data to send, we can combine */ ++ mptcp_for_each_sk(mpcb, sk_it) { ++ if (!mptcp_sk_can_send(sk_it)) ++ continue; ++ ++ if (!tcp_write_queue_empty(sk_it)) ++ all_empty = 0; ++ } ++ ++ /* If all data has been DATA_ACKed, we can combine. ++ * -1, because the data_fin consumed one byte ++ */ ++ all_acked = (meta_tp->snd_una == (meta_tp->write_seq - 1)); ++ ++ if ((all_empty || all_acked) && tcp_close_state(subsk)) { ++ subsk->sk_shutdown |= SEND_SHUTDOWN; ++ TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN; ++ } ++} ++ ++static struct sk_buff *mptcp_skb_entail(struct sock *sk, struct sk_buff *skb, ++ int reinject) ++{ ++ __be32 *ptr; ++ __u16 data_len; ++ struct mp_dss *mdss; ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ struct mptcp_cb *mpcb = tp->mpcb; ++ struct tcp_skb_cb *tcb; ++ struct sk_buff *subskb = NULL; ++ ++ if (!reinject) ++ TCP_SKB_CB(skb)->mptcp_flags |= (mpcb->snd_hiseq_index ? ++ MPTCPHDR_SEQ64_INDEX : 0); ++ ++ subskb = mptcp_pskb_copy(skb); ++ if (!subskb) ++ return NULL; ++ ++ TCP_SKB_CB(skb)->path_mask |= mptcp_pi_to_flag(tp->mptcp->path_index); ++ ++ if (!(sk->sk_route_caps & NETIF_F_ALL_CSUM) && ++ skb->ip_summed == CHECKSUM_PARTIAL) { ++ subskb->csum = skb->csum = skb_checksum(skb, 0, skb->len, 0); ++ subskb->ip_summed = skb->ip_summed = CHECKSUM_NONE; ++ } ++ ++ /* The subskb is going in the subflow send-queue. Its path-mask ++ * is not needed anymore and MUST be set to 0, as the path-mask ++ * is a union with inet_skb_param. ++ */ ++ tcb = TCP_SKB_CB(subskb); ++ tcb->path_mask = 0; ++ ++ if (mptcp_is_data_fin(subskb)) ++ mptcp_combine_dfin(subskb, meta_sk, sk); ++ ++ if (tp->mpcb->infinite_mapping_snd) ++ goto no_data_seq; ++ ++ if (tp->mpcb->send_infinite_mapping && ++ !before(tcb->seq, mptcp_meta_tp(tp)->snd_nxt)) { ++ tp->mptcp->fully_established = 1; ++ tp->mpcb->infinite_mapping_snd = 1; ++ tp->mptcp->infinite_cutoff_seq = tp->write_seq; ++ tcb->mptcp_flags |= MPTCPHDR_INF; ++ data_len = 0; ++ } else { ++ data_len = tcb->end_seq - tcb->seq; ++ } ++ ++ /**** Write MPTCP DSS-option to the packet. ****/ ++ ptr = (__be32 *)(subskb->data - (MPTCP_SUB_LEN_DSS_ALIGN + ++ MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN)); ++ ++ /* Then we start writing it from the start */ ++ mdss = (struct mp_dss *)ptr; ++ ++ mdss->kind = TCPOPT_MPTCP; ++ mdss->sub = MPTCP_SUB_DSS; ++ mdss->rsv1 = 0; ++ mdss->rsv2 = 0; ++ mdss->F = (mptcp_is_data_fin(subskb) ? 1 : 0); ++ mdss->m = 0; ++ mdss->M = 1; ++ mdss->a = 0; ++ mdss->A = 1; ++ mdss->len = mptcp_sub_len_dss(mdss, tp->mpcb->dss_csum); ++ ++ ptr++; ++ ptr++; /* data_ack will be set in mptcp_options_write */ ++ *ptr++ = htonl(tcb->seq); /* data_seq */ ++ ++ /* If it's a non-data DATA_FIN, we set subseq to 0 (draft v7) */ ++ if (mptcp_is_data_fin(subskb) && subskb->len == 0) ++ *ptr++ = 0; /* subseq */ ++ else ++ *ptr++ = htonl(tp->write_seq - tp->mptcp->snt_isn); /* subseq */ ++ ++ if (tp->mpcb->dss_csum && data_len) { ++ __be16 *p16 = (__be16 *)ptr; ++ __be32 hdseq = mptcp_get_highorder_sndbits(subskb, tp->mpcb); ++ __wsum csum; ++ *ptr = htonl(((data_len) << 16) | ++ (TCPOPT_EOL << 8) | ++ (TCPOPT_EOL)); ++ ++ csum = csum_partial(ptr - 2, 12, subskb->csum); ++ p16++; ++ *p16++ = csum_fold(csum_partial(&hdseq, sizeof(hdseq), csum)); ++ } else { ++ *ptr++ = htonl(((data_len) << 16) | ++ (TCPOPT_NOP << 8) | ++ (TCPOPT_NOP)); ++ } ++ ++no_data_seq: ++ tcb->seq = tp->write_seq; ++ tcb->sacked = 0; /* reset the sacked field: from the point of view ++ * of this subflow, we are sending a brand new ++ * segment */ ++ /* Take into account seg len */ ++ tp->write_seq += subskb->len + ((tcb->tcp_flags & TCPHDR_FIN) ? 1 : 0); ++ tcb->end_seq = tp->write_seq; ++ ++ /* If it's a non-payload DATA_FIN (also no subflow-fin), the ++ * segment is not part of the subflow but on a meta-only-level ++ */ ++ if (!mptcp_is_data_fin(subskb) || tcb->end_seq != tcb->seq) { ++ tcp_add_write_queue_tail(sk, subskb); ++ sk->sk_wmem_queued += subskb->truesize; ++ sk_mem_charge(sk, subskb->truesize); ++ } ++ ++ return subskb; ++} ++ ++static void mptcp_sub_event_new_data_sent(struct sock *sk, ++ struct sk_buff *subskb, ++ struct sk_buff *skb) ++{ ++ /* If it's a non-payload DATA_FIN (also no subflow-fin), the ++ * segment is not part of the subflow but on a meta-only-level ++ * ++ * We free it, because it has been queued nowhere. ++ */ ++ if (!mptcp_is_data_fin(subskb) || ++ (TCP_SKB_CB(subskb)->end_seq != TCP_SKB_CB(subskb)->seq)) { ++ tcp_event_new_data_sent(sk, subskb); ++ tcp_sk(sk)->mptcp->second_packet = 1; ++ tcp_sk(sk)->mptcp->last_end_data_seq = TCP_SKB_CB(skb)->end_seq; ++ } else { ++ kfree_skb(subskb); ++ } ++} ++ ++/* Handle the packets and sockets after a tcp_transmit_skb failed */ ++static void mptcp_transmit_skb_failed(struct sock *sk, struct sk_buff *skb, ++ struct sk_buff *subskb) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct mptcp_cb *mpcb = tp->mpcb; ++ ++ /* No work to do if we are in infinite mapping mode ++ * There is only one subflow left and we cannot send this segment on ++ * another subflow. ++ */ ++ if (mpcb->infinite_mapping_snd) ++ return; ++ ++ TCP_SKB_CB(skb)->path_mask &= ~mptcp_pi_to_flag(tp->mptcp->path_index); ++ ++ if (TCP_SKB_CB(subskb)->tcp_flags & TCPHDR_FIN) { ++ /* If it is a subflow-fin we must leave it on the ++ * subflow-send-queue, so that the probe-timer ++ * can retransmit it. ++ */ ++ if (!tp->packets_out && !inet_csk(sk)->icsk_pending) ++ inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0, ++ inet_csk(sk)->icsk_rto, TCP_RTO_MAX); ++ } else if (mptcp_is_data_fin(subskb) && ++ TCP_SKB_CB(subskb)->end_seq == TCP_SKB_CB(subskb)->seq) { ++ /* An empty data-fin has not been enqueued on the subflow ++ * and thus we free it. ++ */ ++ ++ kfree_skb(subskb); ++ } else { ++ /* In all other cases we remove it from the sub-queue. ++ * Other subflows may send it, or the probe-timer will ++ * handle it. ++ */ ++ tcp_advance_send_head(sk, subskb); ++ ++ /* tcp_add_write_queue_tail initialized highest_sack. We have ++ * to reset it, if necessary. ++ */ ++ if (tp->highest_sack == subskb) ++ tp->highest_sack = NULL; ++ ++ tcp_unlink_write_queue(subskb, sk); ++ tp->write_seq -= subskb->len; ++ sk_wmem_free_skb(sk, subskb); ++ } ++} ++ ++/* Function to create two new TCP segments. Shrinks the given segment ++ * to the specified size and appends a new segment with the rest of the ++ * packet to the list. This won't be called frequently, I hope. ++ * Remember, these are still headerless SKBs at this point. ++ */ ++int mptcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, ++ unsigned int mss_now, int reinject) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct sk_buff *buff; ++ int nsize, old_factor; ++ int nlen; ++ u8 flags; ++ int dsslen = MPTCP_SUB_LEN_DSS_ALIGN + MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN; ++ char dss[MPTCP_SUB_LEN_DSS_ALIGN + MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN]; ++ ++ if (WARN_ON(len > skb->len)) ++ return -EINVAL; ++ ++ /* DSS-option must be recovered afterwards. */ ++ if (!is_meta_sk(sk)) ++ memcpy(dss, skb->data - dsslen, dsslen); ++ ++ nsize = skb_headlen(skb) - len; ++ if (nsize < 0) ++ nsize = 0; ++ ++ if (skb_cloned(skb)) { ++ if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) ++ return -ENOMEM; ++ /* Recover dss-option */ ++ if (!is_meta_sk(sk)) ++ memcpy(skb->data - dsslen, dss, dsslen); ++ } ++ ++ /* Get a new skb... force flag on. */ ++ buff = sk_stream_alloc_skb(sk, nsize, GFP_ATOMIC); ++ if (buff == NULL) ++ return -ENOMEM; /* We'll just try again later. */ ++ ++ /* See below - if reinject == 1, the buff will be added to the reinject- ++ * queue, which is currently not part of the memory-accounting. ++ */ ++ if (reinject != 1) { ++ sk->sk_wmem_queued += buff->truesize; ++ sk_mem_charge(sk, buff->truesize); ++ } ++ nlen = skb->len - len - nsize; ++ buff->truesize += nlen; ++ skb->truesize -= nlen; ++ ++ /* Correct the sequence numbers. */ ++ TCP_SKB_CB(buff)->seq = TCP_SKB_CB(skb)->seq + len; ++ TCP_SKB_CB(buff)->end_seq = TCP_SKB_CB(skb)->end_seq; ++ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq; ++ ++ /* PSH and FIN should only be set in the second packet. */ ++ flags = TCP_SKB_CB(skb)->tcp_flags; ++ TCP_SKB_CB(skb)->tcp_flags = flags & ~(TCPHDR_FIN | TCPHDR_PSH); ++ TCP_SKB_CB(buff)->tcp_flags = flags; ++ TCP_SKB_CB(buff)->sacked = TCP_SKB_CB(skb)->sacked; ++ ++ flags = TCP_SKB_CB(skb)->mptcp_flags; ++ TCP_SKB_CB(skb)->mptcp_flags = flags & ~(MPTCPHDR_FIN); ++ TCP_SKB_CB(buff)->mptcp_flags = flags; ++ ++ if (!skb_shinfo(skb)->nr_frags && skb->ip_summed != CHECKSUM_PARTIAL) { ++ /* Copy and checksum data tail into the new buffer. */ ++ buff->csum = csum_partial_copy_nocheck(skb->data + len, ++ skb_put(buff, nsize), ++ nsize, 0); ++ ++ skb_trim(skb, len); ++ ++ skb->csum = csum_block_sub(skb->csum, buff->csum, len); ++ } else { ++ skb->ip_summed = CHECKSUM_PARTIAL; ++ skb_split(skb, buff, len); ++ } ++ ++ /* We lost the dss-option when creating buff - put it back! */ ++ if (!is_meta_sk(sk)) ++ memcpy(buff->data - dsslen, dss, dsslen); ++ ++ buff->ip_summed = skb->ip_summed; ++ ++ /* Looks stupid, but our code really uses when of ++ * skbs, which it never sent before. --ANK ++ */ ++ TCP_SKB_CB(buff)->when = TCP_SKB_CB(skb)->when; ++ buff->tstamp = skb->tstamp; ++ ++ old_factor = tcp_skb_pcount(skb); ++ ++ /* Fix up tso_factor for both original and new SKB. */ ++ tcp_set_skb_tso_segs(sk, skb, mss_now); ++ tcp_set_skb_tso_segs(sk, buff, mss_now); ++ ++ /* If this packet has been sent out already, we must ++ * adjust the various packet counters. ++ */ ++ if (!before(tp->snd_nxt, TCP_SKB_CB(buff)->end_seq) && reinject != 1) { ++ int diff = old_factor - tcp_skb_pcount(skb) - ++ tcp_skb_pcount(buff); ++ ++ if (diff) ++ tcp_adjust_pcount(sk, skb, diff); ++ } ++ ++ /* Link BUFF into the send queue. */ ++ skb_header_release(buff); ++ if (reinject == 1) ++ __skb_queue_after(&tcp_sk(sk)->mpcb->reinject_queue, skb, buff); ++ else ++ tcp_insert_write_queue_after(skb, buff, sk); ++ ++ return 0; ++} ++ ++int mptso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, ++ unsigned int mss_now, gfp_t gfp, int reinject) ++{ ++ struct sk_buff *buff; ++ int nlen = skb->len - len, old_factor; ++ u8 flags; ++ int dsslen = MPTCP_SUB_LEN_DSS_ALIGN + MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN; ++ ++ /* All of a TSO frame must be composed of paged data. */ ++ if (skb->len != skb->data_len) ++ return mptcp_fragment(sk, skb, len, mss_now, reinject); ++ ++ buff = sk_stream_alloc_skb(sk, 0, gfp); ++ if (unlikely(buff == NULL)) ++ return -ENOMEM; ++ ++ /* See below - if reinject == 1, the buff will be added to the reinject- ++ * queue, which is currently not part of the memory-accounting. ++ */ ++ if (reinject != 1) { ++ sk->sk_wmem_queued += buff->truesize; ++ sk_mem_charge(sk, buff->truesize); ++ } ++ buff->truesize += nlen; ++ skb->truesize -= nlen; ++ ++ /* Correct the sequence numbers. */ ++ TCP_SKB_CB(buff)->seq = TCP_SKB_CB(skb)->seq + len; ++ TCP_SKB_CB(buff)->end_seq = TCP_SKB_CB(skb)->end_seq; ++ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(buff)->seq; ++ ++ /* PSH and FIN should only be set in the second packet. */ ++ flags = TCP_SKB_CB(skb)->tcp_flags; ++ TCP_SKB_CB(skb)->tcp_flags = flags & ~(TCPHDR_FIN | TCPHDR_PSH); ++ TCP_SKB_CB(buff)->tcp_flags = flags; ++ ++ flags = TCP_SKB_CB(skb)->mptcp_flags; ++ TCP_SKB_CB(skb)->mptcp_flags = flags & ~(MPTCPHDR_FIN); ++ TCP_SKB_CB(buff)->mptcp_flags = flags; ++ ++ /* This packet was never sent out yet, so no SACK bits. */ ++ TCP_SKB_CB(buff)->sacked = 0; ++ ++ buff->ip_summed = CHECKSUM_PARTIAL; ++ skb->ip_summed = CHECKSUM_PARTIAL; ++ skb_split(skb, buff, len); ++ ++ /* We lost the dss-option when creating buff - put it back! */ ++ if (!is_meta_sk(sk)) ++ memcpy(buff->data - dsslen, skb->data - dsslen, dsslen); ++ ++ old_factor = tcp_skb_pcount(skb); ++ ++ /* Fix up tso_factor for both original and new SKB. */ ++ tcp_set_skb_tso_segs(sk, skb, mss_now); ++ tcp_set_skb_tso_segs(sk, buff, mss_now); ++ ++ /* If this packet has been sent out already, we must ++ * adjust the various packet counters. ++ */ ++ if (!before(tcp_sk(sk)->snd_nxt, TCP_SKB_CB(buff)->end_seq) && reinject != 1) { ++ int diff = old_factor - tcp_skb_pcount(skb) - ++ tcp_skb_pcount(buff); ++ ++ if (diff) ++ tcp_adjust_pcount(sk, skb, diff); ++ } ++ ++ /* Link BUFF into the send queue. */ ++ skb_header_release(buff); ++ if (reinject == 1) ++ __skb_queue_after(&tcp_sk(sk)->mpcb->reinject_queue, skb, buff); ++ else ++ tcp_insert_write_queue_after(skb, buff, sk); ++ ++ return 0; ++} ++ ++/* Inspired by tcp_write_wakeup */ ++int mptcp_write_wakeup(struct sock *meta_sk) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct sk_buff *skb, *subskb; ++ ++ skb = tcp_send_head(meta_sk); ++ if (skb && ++ before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(meta_tp))) { ++ int err; ++ unsigned int mss; ++ unsigned int seg_size = tcp_wnd_end(meta_tp) - TCP_SKB_CB(skb)->seq; ++ struct sock *subsk = get_available_subflow(meta_sk, skb, &mss); ++ if (!subsk) ++ return -1; ++ ++ if (before(meta_tp->pushed_seq, TCP_SKB_CB(skb)->end_seq)) ++ meta_tp->pushed_seq = TCP_SKB_CB(skb)->end_seq; ++ ++ /* We are probing the opening of a window ++ * but the window size is != 0 ++ * must have been a result SWS avoidance ( sender ) ++ */ ++ if (seg_size < TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq || ++ skb->len > mss) { ++ seg_size = min(seg_size, mss); ++ TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_PSH; ++ if (mptcp_fragment(meta_sk, skb, seg_size, mss, 0)) ++ return -1; ++ } else if (!tcp_skb_pcount(skb)) { ++ tcp_set_skb_tso_segs(meta_sk, skb, mss); ++ } ++ ++ subskb = mptcp_skb_entail(subsk, skb, 0); ++ if (!subskb) ++ return -1; ++ ++ TCP_SKB_CB(subskb)->tcp_flags |= TCPHDR_PSH; ++ TCP_SKB_CB(skb)->when = tcp_time_stamp; ++ TCP_SKB_CB(subskb)->when = tcp_time_stamp; ++ err = tcp_transmit_skb(subsk, subskb, 1, GFP_ATOMIC); ++ if (unlikely(err)) { ++ mptcp_transmit_skb_failed(subsk, skb, subskb); ++ return err; ++ } ++ ++ mptcp_check_sndseq_wrap(meta_tp, TCP_SKB_CB(skb)->end_seq - ++ TCP_SKB_CB(skb)->seq); ++ tcp_event_new_data_sent(meta_sk, skb); ++ mptcp_sub_event_new_data_sent(subsk, subskb, skb); ++ ++ return 0; ++ } else { ++ struct sock *sk_it; ++ int ans = 0; ++ ++ if (between(meta_tp->snd_up, meta_tp->snd_una + 1, ++ meta_tp->snd_una + 0xFFFF)) { ++ mptcp_for_each_sk(meta_tp->mpcb, sk_it) { ++ if (mptcp_sk_can_send_ack(sk_it)) ++ tcp_xmit_probe_skb(sk_it, 1); ++ } ++ } ++ ++ /* At least one of the tcp_xmit_probe_skb's has to succeed */ ++ mptcp_for_each_sk(meta_tp->mpcb, sk_it) { ++ int ret; ++ ++ if (!mptcp_sk_can_send_ack(sk_it)) ++ continue; ++ ++ ret = tcp_xmit_probe_skb(sk_it, 0); ++ if (unlikely(ret > 0)) ++ ans = ret; ++ } ++ return ans; ++ } ++} ++ ++static void mptcp_find_and_set_pathmask(struct sock *meta_sk, struct sk_buff *skb) ++{ ++ struct sk_buff *skb_it; ++ ++ skb_it = tcp_write_queue_head(meta_sk); ++ ++ tcp_for_write_queue_from(skb_it, meta_sk) { ++ if (skb_it == tcp_send_head(meta_sk)) ++ break; ++ ++ if (TCP_SKB_CB(skb_it)->seq == TCP_SKB_CB(skb)->seq) { ++ TCP_SKB_CB(skb)->path_mask = TCP_SKB_CB(skb_it)->path_mask; ++ break; ++ } ++ } ++} ++ ++static struct sk_buff *mptcp_rcv_buf_optimization(struct sock *sk, int penal) ++{ ++ struct sock *meta_sk; ++ struct tcp_sock *tp = tcp_sk(sk), *tp_it; ++ struct sk_buff *skb_head; ++ ++ if (tp->mpcb->cnt_subflows == 1) ++ return NULL; ++ ++ meta_sk = mptcp_meta_sk(sk); ++ skb_head = tcp_write_queue_head(meta_sk); ++ ++ if (!skb_head || skb_head == tcp_send_head(meta_sk)) ++ return NULL; ++ ++ /* If penalization is optional (coming from mptcp_next_segment() and ++ * We are not send-buffer-limited we do not penalize. The retransmission ++ * is just an optimization to fix the idle-time due to the delay before ++ * we wake up the application. ++ */ ++ if (!penal && sk_stream_memory_free(meta_sk)) ++ goto retrans; ++ ++ /* Only penalize again after an RTT has elapsed */ ++ if (tcp_time_stamp - tp->mptcp->last_rbuf_opti < tp->srtt >> 3) ++ goto retrans; ++ ++ /* Half the cwnd of the slow flow */ ++ mptcp_for_each_tp(tp->mpcb, tp_it) { ++ if (tp_it != tp && ++ TCP_SKB_CB(skb_head)->path_mask & mptcp_pi_to_flag(tp_it->mptcp->path_index)) { ++ if (tp->srtt < tp_it->srtt && inet_csk((struct sock *)tp_it)->icsk_ca_state == TCP_CA_Open) { ++ tp_it->snd_cwnd = max(tp_it->snd_cwnd >> 1U, 1U); ++ if (tp_it->snd_ssthresh != TCP_INFINITE_SSTHRESH) ++ tp_it->snd_ssthresh = max(tp_it->snd_ssthresh >> 1U, 2U); ++ ++ tp->mptcp->last_rbuf_opti = tcp_time_stamp; ++ } ++ break; ++ } ++ } ++ ++retrans: ++ ++ /* Segment not yet injected into this path? Take it!!! */ ++ if (!(TCP_SKB_CB(skb_head)->path_mask & mptcp_pi_to_flag(tp->mptcp->path_index))) { ++ bool do_retrans = false; ++ mptcp_for_each_tp(tp->mpcb, tp_it) { ++ if (tp_it != tp && ++ TCP_SKB_CB(skb_head)->path_mask & mptcp_pi_to_flag(tp_it->mptcp->path_index)) { ++ if (tp_it->snd_cwnd <= 4) { ++ do_retrans = true; ++ break; ++ } ++ ++ if (4 * tp->srtt >= tp_it->srtt) { ++ do_retrans = false; ++ break; ++ } else { ++ do_retrans = true; ++ } ++ } ++ } ++ ++ if (do_retrans) ++ return skb_head; ++ } ++ return NULL; ++} ++ ++int mptcp_write_xmit(struct sock *meta_sk, unsigned int mss_now, int nonagle, ++ int push_one, gfp_t gfp) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk), *subtp; ++ struct sock *subsk; ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ struct sk_buff *skb; ++ unsigned int tso_segs, old_factor, sent_pkts; ++ int cwnd_quota; ++ int result; ++ int reinject = 0; ++ ++ sent_pkts = 0; ++ ++ /* Currently mtu-probing is not done in MPTCP */ ++ if (!push_one && 0) { ++ /* Do MTU probing. */ ++ result = tcp_mtu_probe(meta_sk); ++ if (!result) ++ return 0; ++ else if (result > 0) ++ sent_pkts = 1; ++ } ++ ++ while ((skb = mptcp_next_segment(meta_sk, &reinject))) { ++ unsigned int limit; ++ struct sk_buff *subskb = NULL; ++ u32 noneligible = mpcb->noneligible; ++ ++ if (reinject == 1) { ++ if (!after(TCP_SKB_CB(skb)->end_seq, meta_tp->snd_una)) { ++ /* Segment already reached the peer, take the next one */ ++ __skb_unlink(skb, &mpcb->reinject_queue); ++ __kfree_skb(skb); ++ continue; ++ } ++ ++ /* Reinjection and it is coming from a subflow? We need ++ * to find out the path-mask from the meta-write-queue ++ * to properly select a subflow. ++ */ ++ if (!TCP_SKB_CB(skb)->path_mask) ++ mptcp_find_and_set_pathmask(meta_sk, skb); ++ } ++ ++subflow: ++ subsk = get_available_subflow(meta_sk, skb, &mss_now); ++ if (!subsk) ++ break; ++ subtp = tcp_sk(subsk); ++ ++ /* Since all subsocks are locked before calling the scheduler, ++ * the tcp_send_head should not change. ++ */ ++ BUG_ON(!reinject && tcp_send_head(meta_sk) != skb); ++retry: ++ /* If the segment was cloned (e.g. a meta retransmission), ++ * the header must be expanded/copied so that there is no ++ * corruption of TSO information. ++ */ ++ if (skb_unclone(skb, GFP_ATOMIC)) ++ break; ++ ++ old_factor = tcp_skb_pcount(skb); ++ tcp_set_skb_tso_segs(meta_sk, skb, mss_now); ++ tso_segs = tcp_skb_pcount(skb); ++ ++ if (reinject == -1) { ++ /* The packet has already once been sent, so if we ++ * change the pcount here we have to adjust packets_out ++ * in the meta-sk ++ */ ++ int diff = old_factor - tso_segs; ++ ++ if (diff) ++ tcp_adjust_pcount(meta_sk, skb, diff); ++ } ++ ++ cwnd_quota = tcp_cwnd_test(subtp, skb); ++ if (!cwnd_quota) { ++ /* May happen due to two cases: ++ * ++ * - if at the first selection we circumvented ++ * the test due to a DATA_FIN (and got rejected at ++ * tcp_snd_wnd_test), but the reinjected segment is not ++ * a DATA_FIN. ++ * - if we take a DATA_FIN with data, but ++ * tcp_set_skb_tso_segs() increases the number of ++ * tso_segs to something > 1. Then, cwnd_test might ++ * reject it. ++ */ ++ mpcb->noneligible |= mptcp_pi_to_flag(subtp->mptcp->path_index); ++ continue; ++ } ++ ++ if (!reinject && unlikely(!tcp_snd_wnd_test(meta_tp, skb, mss_now))) { ++ skb = mptcp_rcv_buf_optimization(subsk, 1); ++ if (skb) { ++ reinject = -1; ++ goto retry; ++ } ++ break; ++ } ++ ++ if (tso_segs == 1) { ++ if (unlikely(!tcp_nagle_test(meta_tp, skb, mss_now, ++ (tcp_skb_is_last(meta_sk, skb) ? ++ nonagle : TCP_NAGLE_PUSH)))) ++ break; ++ } else { ++ /* Do not try to defer the transmission of a reinjected ++ * segment. Send it directly. ++ * If it is not possible to send the TSO segment on the ++ * best subflow right now try to look for another subflow. ++ * If there is no subflow available defer the segment to avoid ++ * the call to mptso_fragment. ++ */ ++ if (!push_one && !reinject && tcp_tso_should_defer(subsk, skb)) { ++ mpcb->noneligible |= mptcp_pi_to_flag(subtp->mptcp->path_index); ++ goto subflow; ++ } ++ } ++ ++ limit = mss_now; ++ if (tso_segs > 1 && !tcp_urg_mode(meta_tp)) ++ limit = tcp_mss_split_point(subsk, skb, mss_now, ++ min_t(unsigned int, ++ cwnd_quota, ++ subsk->sk_gso_max_segs), ++ nonagle); ++ ++ if (skb->len > limit && ++ unlikely(mptso_fragment(meta_sk, skb, limit, mss_now, gfp, reinject))) ++ break; ++ ++ subskb = mptcp_skb_entail(subsk, skb, reinject); ++ if (!subskb) ++ break; ++ ++ mpcb->noneligible = noneligible; ++ TCP_SKB_CB(skb)->when = tcp_time_stamp; ++ TCP_SKB_CB(subskb)->when = tcp_time_stamp; ++ if (unlikely(tcp_transmit_skb(subsk, subskb, 1, gfp))) { ++ mptcp_transmit_skb_failed(subsk, skb, subskb); ++ mpcb->noneligible |= mptcp_pi_to_flag(subtp->mptcp->path_index); ++ continue; ++ } ++ ++ if (!reinject) { ++ mptcp_check_sndseq_wrap(meta_tp, ++ TCP_SKB_CB(skb)->end_seq - ++ TCP_SKB_CB(skb)->seq); ++ tcp_event_new_data_sent(meta_sk, skb); ++ } ++ ++ tcp_minshall_update(meta_tp, mss_now, skb); ++ sent_pkts += tcp_skb_pcount(skb); ++ tcp_sk(subsk)->mptcp->sent_pkts += tcp_skb_pcount(skb); ++ ++ mptcp_sub_event_new_data_sent(subsk, subskb, skb); ++ ++ if (reinject > 0) { ++ __skb_unlink(skb, &mpcb->reinject_queue); ++ kfree_skb(skb); ++ } ++ ++ if (push_one) ++ break; ++ } ++ ++ mpcb->noneligible = 0; ++ ++ if (likely(sent_pkts)) { ++ mptcp_for_each_sk(mpcb, subsk) { ++ subtp = tcp_sk(subsk); ++ if (subtp->mptcp->sent_pkts) { ++ if (tcp_in_cwnd_reduction(subsk)) ++ subtp->prr_out += subtp->mptcp->sent_pkts; ++ tcp_cwnd_validate(subsk); ++ subtp->mptcp->sent_pkts = 0; ++ } ++ } ++ return 0; ++ } ++ ++ return !meta_tp->packets_out && tcp_send_head(meta_sk); ++} ++ ++void mptcp_write_space(struct sock *sk) ++{ ++ mptcp_push_pending_frames(mptcp_meta_sk(sk)); ++} ++ ++u32 __mptcp_select_window(struct sock *sk) ++{ ++ struct inet_connection_sock *icsk = inet_csk(sk); ++ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); ++ int mss, free_space, full_space, window; ++ ++ /* MSS for the peer's data. Previous versions used mss_clamp ++ * here. I don't know if the value based on our guesses ++ * of peer's MSS is better for the performance. It's more correct ++ * but may be worse for the performance because of rcv_mss ++ * fluctuations. --SAW 1998/11/1 ++ */ ++ mss = icsk->icsk_ack.rcv_mss; ++ free_space = tcp_space(sk); ++ full_space = min_t(int, meta_tp->window_clamp, ++ tcp_full_space(sk)); ++ ++ if (mss > full_space) ++ mss = full_space; ++ ++ if (free_space < (full_space >> 1)) { ++ icsk->icsk_ack.quick = 0; ++ ++ if (tcp_memory_pressure) ++ /* TODO this has to be adapted when we support different ++ * MSS's among the subflows. ++ */ ++ meta_tp->rcv_ssthresh = min(meta_tp->rcv_ssthresh, ++ 4U * meta_tp->advmss); ++ ++ if (free_space < mss) ++ return 0; ++ } ++ ++ if (free_space > meta_tp->rcv_ssthresh) ++ free_space = meta_tp->rcv_ssthresh; ++ ++ /* Don't do rounding if we are using window scaling, since the ++ * scaled window will not line up with the MSS boundary anyway. ++ */ ++ window = meta_tp->rcv_wnd; ++ if (tp->rx_opt.rcv_wscale) { ++ window = free_space; ++ ++ /* Advertise enough space so that it won't get scaled away. ++ * Import case: prevent zero window announcement if ++ * 1< mss. ++ */ ++ if (((window >> tp->rx_opt.rcv_wscale) << tp-> ++ rx_opt.rcv_wscale) != window) ++ window = (((window >> tp->rx_opt.rcv_wscale) + 1) ++ << tp->rx_opt.rcv_wscale); ++ } else { ++ /* Get the largest window that is a nice multiple of mss. ++ * Window clamp already applied above. ++ * If our current window offering is within 1 mss of the ++ * free space we just keep it. This prevents the divide ++ * and multiply from happening most of the time. ++ * We also don't do any window rounding when the free space ++ * is too small. ++ */ ++ if (window <= free_space - mss || window > free_space) ++ window = (free_space / mss) * mss; ++ else if (mss == full_space && ++ free_space > window + (full_space >> 1)) ++ window = free_space; ++ } ++ ++ return window; ++} ++ ++void mptcp_syn_options(struct sock *sk, struct tcp_out_options *opts, ++ unsigned *remaining) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ ++ opts->options |= OPTION_MPTCP; ++ if (is_master_tp(tp)) { ++ opts->mptcp_options |= OPTION_MP_CAPABLE | OPTION_TYPE_SYN; ++ *remaining -= MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN; ++ opts->mp_capable.sender_key = tp->mptcp_loc_key; ++ opts->dss_csum = !!sysctl_mptcp_checksum; ++ } else { ++ struct mptcp_cb *mpcb = tp->mpcb; ++ ++ opts->mptcp_options |= OPTION_MP_JOIN | OPTION_TYPE_SYN; ++ *remaining -= MPTCP_SUB_LEN_JOIN_SYN_ALIGN; ++ opts->mp_join_syns.token = mpcb->mptcp_rem_token; ++ opts->addr_id = tp->mptcp->loc_id; ++ opts->mp_join_syns.sender_nonce = tp->mptcp->mptcp_loc_nonce; ++ } ++} ++ ++void mptcp_synack_options(struct request_sock *req, ++ struct tcp_out_options *opts, unsigned *remaining) ++{ ++ struct mptcp_request_sock *mtreq; ++ mtreq = mptcp_rsk(req); ++ ++ opts->options |= OPTION_MPTCP; ++ /* MPCB not yet set - thus it's a new MPTCP-session */ ++ if (!mtreq->mpcb) { ++ opts->mptcp_options |= OPTION_MP_CAPABLE | OPTION_TYPE_SYNACK; ++ opts->mp_capable.sender_key = mtreq->mptcp_loc_key; ++ opts->dss_csum = !!sysctl_mptcp_checksum || mtreq->dss_csum; ++ *remaining -= MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN; ++ } else { ++ opts->mptcp_options |= OPTION_MP_JOIN | OPTION_TYPE_SYNACK; ++ opts->mp_join_syns.sender_truncated_mac = ++ mtreq->mptcp_hash_tmac; ++ opts->mp_join_syns.sender_nonce = mtreq->mptcp_loc_nonce; ++ opts->addr_id = mtreq->loc_id; ++ *remaining -= MPTCP_SUB_LEN_JOIN_SYNACK_ALIGN; ++ } ++} ++ ++void mptcp_established_options(struct sock *sk, struct sk_buff *skb, ++ struct tcp_out_options *opts, unsigned *size) ++{ ++ struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp); ++ struct mptcp_cb *mpcb = tp->mpcb; ++ struct tcp_skb_cb *tcb = skb ? TCP_SKB_CB(skb) : NULL; ++ ++ /* In fallback mp_fail-mode, we have to repeat it until the fallback ++ * has been done by the sender ++ */ ++ if (unlikely(tp->mptcp->send_mp_fail)) { ++ opts->options |= OPTION_MPTCP; ++ opts->mptcp_options |= OPTION_MP_FAIL; ++ opts->data_ack = (__u32)(mpcb->csum_cutoff_seq >> 32); ++ opts->data_seq = (__u32)mpcb->csum_cutoff_seq; ++ *size += MPTCP_SUB_LEN_FAIL; ++ return; ++ } ++ ++ if (unlikely(tp->send_mp_fclose)) { ++ opts->options |= OPTION_MPTCP; ++ opts->mptcp_options |= OPTION_MP_FCLOSE; ++ opts->mp_capable.receiver_key = mpcb->mptcp_rem_key; ++ *size += MPTCP_SUB_LEN_FCLOSE_ALIGN; ++ return; ++ } ++ ++ /* 1. If we are the sender of the infinite-mapping, we need the ++ * MPTCPHDR_INF-flag, because a retransmission of the ++ * infinite-announcment still needs the mptcp-option. ++ * ++ * We need infinite_cutoff_seq, because retransmissions from before ++ * the infinite-cutoff-moment still need the MPTCP-signalling to stay ++ * consistent. ++ * ++ * 2. If we are the receiver of the infinite-mapping, we always skip ++ * mptcp-options, because acknowledgments from before the ++ * infinite-mapping point have already been sent out. ++ * ++ * I know, the whole infinite-mapping stuff is ugly... ++ * ++ * TODO: Handle wrapped data-sequence numbers ++ * (even if it's very unlikely) ++ */ ++ if (unlikely(mpcb->infinite_mapping_snd) && ++ tp->mptcp->fully_established && ++ ((mpcb->send_infinite_mapping && tcb && ++ !(tcb->mptcp_flags & MPTCPHDR_INF) && ++ !before(tcb->seq, tp->mptcp->infinite_cutoff_seq)) || ++ !mpcb->send_infinite_mapping)) ++ return; ++ ++ if (unlikely(tp->mptcp->include_mpc)) { ++ opts->options |= OPTION_MPTCP; ++ opts->mptcp_options |= OPTION_MP_CAPABLE | ++ OPTION_TYPE_ACK; ++ *size += MPTCP_SUB_LEN_CAPABLE_ACK_ALIGN; ++ opts->mp_capable.sender_key = mpcb->mptcp_loc_key; ++ opts->mp_capable.receiver_key = mpcb->mptcp_rem_key; ++ opts->dss_csum = mpcb->dss_csum; ++ ++ if (skb) ++ tp->mptcp->include_mpc = 0; ++ } ++ if (unlikely(tp->mptcp->pre_established)) { ++ opts->options |= OPTION_MPTCP; ++ opts->mptcp_options |= OPTION_MP_JOIN | OPTION_TYPE_ACK; ++ *size += MPTCP_SUB_LEN_JOIN_ACK_ALIGN; ++ } ++ ++ if (!tp->mptcp->include_mpc && !tp->mptcp->pre_established) { ++ opts->options |= OPTION_MPTCP; ++ opts->mptcp_options |= OPTION_DATA_ACK; ++ /* If !skb, we come from tcp_current_mss and thus we always ++ * assume that the DSS-option will be set for the data-packet. ++ */ ++ if (skb && !mptcp_is_data_seq(skb)) { ++ opts->data_ack = meta_tp->rcv_nxt; ++ ++ *size += MPTCP_SUB_LEN_ACK_ALIGN; ++ } else { ++ opts->data_ack = meta_tp->rcv_nxt; ++ ++ /* Doesn't matter, if csum included or not. It will be ++ * either 10 or 12, and thus aligned = 12 ++ */ ++ *size += MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN; ++ } ++ ++ *size += MPTCP_SUB_LEN_DSS_ALIGN; ++ } ++ ++ if (mpcb->pm_ops->addr_signal) ++ mpcb->pm_ops->addr_signal(sk, size, opts, skb); ++ ++ if (unlikely(tp->mptcp->send_mp_prio) && ++ MAX_TCP_OPTION_SPACE - *size >= MPTCP_SUB_LEN_PRIO_ALIGN) { ++ opts->options |= OPTION_MPTCP; ++ opts->mptcp_options |= OPTION_MP_PRIO; ++ if (skb) ++ tp->mptcp->send_mp_prio = 0; ++ *size += MPTCP_SUB_LEN_PRIO_ALIGN; ++ } ++ ++ return; ++} ++ ++u16 mptcp_select_window(struct sock *sk) ++{ ++ u16 new_win = tcp_select_window(sk); ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct tcp_sock *meta_tp = mptcp_meta_tp(tp); ++ ++ meta_tp->rcv_wnd = tp->rcv_wnd; ++ meta_tp->rcv_wup = meta_tp->rcv_nxt; ++ ++ return new_win; ++} ++ ++void mptcp_options_write(__be32 *ptr, struct tcp_sock *tp, ++ struct tcp_out_options *opts, ++ struct sk_buff *skb) ++{ ++ if (unlikely(OPTION_MP_CAPABLE & opts->mptcp_options)) { ++ struct mp_capable *mpc = (struct mp_capable *)ptr; ++ ++ mpc->kind = TCPOPT_MPTCP; ++ ++ if ((OPTION_TYPE_SYN & opts->mptcp_options) || ++ (OPTION_TYPE_SYNACK & opts->mptcp_options)) { ++ mpc->sender_key = opts->mp_capable.sender_key; ++ mpc->len = MPTCP_SUB_LEN_CAPABLE_SYN; ++ ptr += MPTCP_SUB_LEN_CAPABLE_SYN_ALIGN >> 2; ++ } else if (OPTION_TYPE_ACK & opts->mptcp_options) { ++ mpc->sender_key = opts->mp_capable.sender_key; ++ mpc->receiver_key = opts->mp_capable.receiver_key; ++ mpc->len = MPTCP_SUB_LEN_CAPABLE_ACK; ++ ptr += MPTCP_SUB_LEN_CAPABLE_ACK_ALIGN >> 2; ++ } ++ ++ mpc->sub = MPTCP_SUB_CAPABLE; ++ mpc->ver = 0; ++ mpc->a = opts->dss_csum; ++ mpc->b = 0; ++ mpc->rsv = 0; ++ mpc->h = 1; ++ } ++ ++ if (unlikely(OPTION_MP_JOIN & opts->mptcp_options)) { ++ struct mp_join *mpj = (struct mp_join *)ptr; ++ ++ mpj->kind = TCPOPT_MPTCP; ++ mpj->sub = MPTCP_SUB_JOIN; ++ mpj->rsv = 0; ++ mpj->addr_id = opts->addr_id; ++ ++ if (OPTION_TYPE_SYN & opts->mptcp_options) { ++ mpj->len = MPTCP_SUB_LEN_JOIN_SYN; ++ mpj->u.syn.token = opts->mp_join_syns.token; ++ mpj->u.syn.nonce = opts->mp_join_syns.sender_nonce; ++ mpj->b = tp->mptcp->low_prio; ++ ptr += MPTCP_SUB_LEN_JOIN_SYN_ALIGN >> 2; ++ } else if (OPTION_TYPE_SYNACK & opts->mptcp_options) { ++ mpj->len = MPTCP_SUB_LEN_JOIN_SYNACK; ++ mpj->u.synack.mac = ++ opts->mp_join_syns.sender_truncated_mac; ++ mpj->u.synack.nonce = opts->mp_join_syns.sender_nonce; ++ mpj->b = tp->mptcp->low_prio; ++ ptr += MPTCP_SUB_LEN_JOIN_SYNACK_ALIGN >> 2; ++ } else if (OPTION_TYPE_ACK & opts->mptcp_options) { ++ mpj->len = MPTCP_SUB_LEN_JOIN_ACK; ++ memcpy(mpj->u.ack.mac, &tp->mptcp->sender_mac[0], 20); ++ ptr += MPTCP_SUB_LEN_JOIN_ACK_ALIGN >> 2; ++ } ++ } ++ if (unlikely(OPTION_ADD_ADDR & opts->mptcp_options)) { ++ struct mp_add_addr *mpadd = (struct mp_add_addr *)ptr; ++ ++ mpadd->kind = TCPOPT_MPTCP; ++ if (opts->add_addr_v4) { ++ mpadd->len = MPTCP_SUB_LEN_ADD_ADDR4; ++ mpadd->sub = MPTCP_SUB_ADD_ADDR; ++ mpadd->ipver = 4; ++ mpadd->addr_id = opts->add_addr4.addr_id; ++ mpadd->u.v4.addr = opts->add_addr4.addr; ++ ptr += MPTCP_SUB_LEN_ADD_ADDR4_ALIGN >> 2; ++ } else if (opts->add_addr_v6) { ++ mpadd->len = MPTCP_SUB_LEN_ADD_ADDR6; ++ mpadd->sub = MPTCP_SUB_ADD_ADDR; ++ mpadd->ipver = 6; ++ mpadd->addr_id = opts->add_addr6.addr_id; ++ memcpy(&mpadd->u.v6.addr, &opts->add_addr6.addr, ++ sizeof(mpadd->u.v6.addr)); ++ ptr += MPTCP_SUB_LEN_ADD_ADDR6_ALIGN >> 2; ++ } ++ } ++ if (unlikely(OPTION_REMOVE_ADDR & opts->mptcp_options)) { ++ struct mp_remove_addr *mprem = (struct mp_remove_addr *)ptr; ++ u8 *addrs_id; ++ int id, len, len_align; ++ ++ len = mptcp_sub_len_remove_addr(opts->remove_addrs); ++ len_align = mptcp_sub_len_remove_addr_align(opts->remove_addrs); ++ ++ mprem->kind = TCPOPT_MPTCP; ++ mprem->len = len; ++ mprem->sub = MPTCP_SUB_REMOVE_ADDR; ++ mprem->rsv = 0; ++ addrs_id = &mprem->addrs_id; ++ ++ mptcp_for_each_bit_set(opts->remove_addrs, id) ++ *(addrs_id++) = id; ++ ++ /* Fill the rest with NOP's */ ++ if (len_align > len) { ++ int i; ++ for (i = 0; i < len_align - len; i++) ++ *(addrs_id++) = TCPOPT_NOP; ++ } ++ ++ ptr += len_align >> 2; ++ } ++ if (unlikely(OPTION_MP_FAIL & opts->mptcp_options)) { ++ struct mp_fail *mpfail = (struct mp_fail *)ptr; ++ ++ mpfail->kind = TCPOPT_MPTCP; ++ mpfail->len = MPTCP_SUB_LEN_FAIL; ++ mpfail->sub = MPTCP_SUB_FAIL; ++ mpfail->rsv1 = 0; ++ mpfail->rsv2 = 0; ++ mpfail->data_seq = htonll(((u64)opts->data_ack << 32) | opts->data_seq); ++ ++ ptr += MPTCP_SUB_LEN_FAIL_ALIGN >> 2; ++ } ++ if (unlikely(OPTION_MP_FCLOSE & opts->mptcp_options)) { ++ struct mp_fclose *mpfclose = (struct mp_fclose *)ptr; ++ ++ mpfclose->kind = TCPOPT_MPTCP; ++ mpfclose->len = MPTCP_SUB_LEN_FCLOSE; ++ mpfclose->sub = MPTCP_SUB_FCLOSE; ++ mpfclose->rsv1 = 0; ++ mpfclose->rsv2 = 0; ++ mpfclose->key = opts->mp_capable.receiver_key; ++ ++ ptr += MPTCP_SUB_LEN_FCLOSE_ALIGN >> 2; ++ } ++ ++ if (OPTION_DATA_ACK & opts->mptcp_options) { ++ if (!mptcp_is_data_seq(skb)) { ++ struct mp_dss *mdss = (struct mp_dss *)ptr; ++ ++ mdss->kind = TCPOPT_MPTCP; ++ mdss->sub = MPTCP_SUB_DSS; ++ mdss->rsv1 = 0; ++ mdss->rsv2 = 0; ++ mdss->F = 0; ++ mdss->m = 0; ++ mdss->M = 0; ++ mdss->a = 0; ++ mdss->A = 1; ++ mdss->len = mptcp_sub_len_dss(mdss, tp->mpcb->dss_csum); ++ ++ ptr++; ++ *ptr++ = htonl(opts->data_ack); ++ } else { ++ /**** Just update the data_ack ****/ ++ ++ /* Get pointer to data_ack-field. MPTCP is always at ++ * the end of the TCP-options. ++ */ ++ /* TODO if we allow sending 64-bit dseq's we have to change "16" */ ++ __be32 *dack = (__be32 *)(skb->data + (tcp_hdr(skb)->doff << 2) - 16); ++ ++ *dack = htonl(opts->data_ack); ++ } ++ } ++ if (unlikely(OPTION_MP_PRIO & opts->mptcp_options)) { ++ struct mp_prio *mpprio = (struct mp_prio *)ptr; ++ ++ mpprio->kind = TCPOPT_MPTCP; ++ mpprio->len = MPTCP_SUB_LEN_PRIO; ++ mpprio->sub = MPTCP_SUB_PRIO; ++ mpprio->rsv = 0; ++ mpprio->b = tp->mptcp->low_prio; ++ mpprio->addr_id = TCPOPT_NOP; ++ ++ ptr += MPTCP_SUB_LEN_PRIO_ALIGN >> 2; ++ } ++} ++ ++/* Returns the next segment to be sent from the mptcp meta-queue. ++ * (chooses the reinject queue if any segment is waiting in it, otherwise, ++ * chooses the normal write queue). ++ * Sets *@reinject to 1 if the returned segment comes from the ++ * reinject queue. Sets it to 0 if it is the regular send-head of the meta-sk, ++ * and sets it to -1 if it is a meta-level retransmission to optimize the ++ * receive-buffer. ++ */ ++struct sk_buff *mptcp_next_segment(struct sock *meta_sk, int *reinject) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(meta_sk)->mpcb; ++ struct sk_buff *skb = NULL; ++ if (reinject) ++ *reinject = 0; ++ ++ /* If we are in fallback-mode, just take from the meta-send-queue */ ++ if (mpcb->infinite_mapping_snd || mpcb->send_infinite_mapping) ++ return tcp_send_head(meta_sk); ++ ++ skb = skb_peek(&mpcb->reinject_queue); ++ ++ if (skb) { ++ if (reinject) ++ *reinject = 1; ++ } else { ++ skb = tcp_send_head(meta_sk); ++ ++ if (!skb && meta_sk->sk_socket && ++ test_bit(SOCK_NOSPACE, &meta_sk->sk_socket->flags) && ++ sk_stream_wspace(meta_sk) < sk_stream_min_wspace(meta_sk)) { ++ struct sock *subsk = get_available_subflow(meta_sk, NULL, NULL); ++ if (!subsk) ++ return NULL; ++ ++ skb = mptcp_rcv_buf_optimization(subsk, 0); ++ if (skb && reinject) ++ *reinject = -1; ++ } ++ } ++ return skb; ++} ++ ++/* Sends the datafin */ ++void mptcp_send_fin(struct sock *meta_sk) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct sk_buff *skb = tcp_write_queue_tail(meta_sk); ++ int mss_now; ++ ++ if ((1 << meta_sk->sk_state) & (TCPF_CLOSE_WAIT | TCPF_LAST_ACK)) ++ meta_tp->mpcb->passive_close = 1; ++ ++ /* Optimization, tack on the FIN if we have a queue of ++ * unsent frames. But be careful about outgoing SACKS ++ * and IP options. ++ */ ++ mss_now = mptcp_current_mss(meta_sk); ++ ++ if (tcp_send_head(meta_sk) != NULL) { ++ TCP_SKB_CB(skb)->mptcp_flags |= MPTCPHDR_FIN; ++ TCP_SKB_CB(skb)->end_seq++; ++ meta_tp->write_seq++; ++ } else { ++ /* Socket is locked, keep trying until memory is available. */ ++ for (;;) { ++ skb = alloc_skb_fclone(MAX_TCP_HEADER, ++ meta_sk->sk_allocation); ++ if (skb) ++ break; ++ yield(); ++ } ++ /* Reserve space for headers and prepare control bits. */ ++ skb_reserve(skb, MAX_TCP_HEADER); ++ ++ tcp_init_nondata_skb(skb, meta_tp->write_seq, TCPHDR_ACK); ++ TCP_SKB_CB(skb)->end_seq++; ++ TCP_SKB_CB(skb)->mptcp_flags |= MPTCPHDR_FIN | MPTCPHDR_SEQ; ++ tcp_queue_skb(meta_sk, skb); ++ } ++ __tcp_push_pending_frames(meta_sk, mss_now, TCP_NAGLE_OFF); ++} ++ ++void mptcp_send_active_reset(struct sock *meta_sk, gfp_t priority) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ struct sock *sk = NULL, *sk_it = NULL, *tmpsk; ++ ++ if (!mpcb->cnt_subflows) ++ return; ++ ++ WARN_ON(meta_tp->send_mp_fclose); ++ ++ /* First - select a socket */ ++ sk = mptcp_select_ack_sock(meta_sk, 0); ++ ++ /* May happen if no subflow is in an appropriate state */ ++ if (!sk) ++ return; ++ ++ /* We are in infinite mode - just send a reset */ ++ if (mpcb->infinite_mapping_snd || mpcb->infinite_mapping_rcv) { ++ sk->sk_err = ECONNRESET; ++ if (tcp_need_reset(sk->sk_state)) ++ tcp_send_active_reset(sk, priority); ++ mptcp_sub_force_close(sk); ++ return; ++ } ++ ++ ++ tcp_sk(sk)->send_mp_fclose = 1; ++ /** Reset all other subflows */ ++ ++ /* tcp_done must be handled with bh disabled */ ++ if (!in_serving_softirq()) ++ local_bh_disable(); ++ ++ mptcp_for_each_sk_safe(mpcb, sk_it, tmpsk) { ++ if (tcp_sk(sk_it)->send_mp_fclose) ++ continue; ++ ++ sk_it->sk_err = ECONNRESET; ++ if (tcp_need_reset(sk_it->sk_state)) ++ tcp_send_active_reset(sk_it, GFP_ATOMIC); ++ mptcp_sub_force_close(sk_it); ++ } ++ ++ if (!in_serving_softirq()) ++ local_bh_enable(); ++ ++ tcp_send_ack(sk); ++ inet_csk_reset_keepalive_timer(sk, inet_csk(sk)->icsk_rto); ++ ++ meta_tp->send_mp_fclose = 1; ++} ++ ++static void mptcp_ack_retransmit_timer(struct sock *sk) ++{ ++ struct sk_buff *skb; ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct inet_connection_sock *icsk = inet_csk(sk); ++ ++ if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk)) ++ goto out; /* Routing failure or similar */ ++ ++ if (!tp->retrans_stamp) ++ tp->retrans_stamp = tcp_time_stamp ? : 1; ++ ++ if (tcp_write_timeout(sk)) { ++ tp->mptcp->pre_established = 0; ++ sk_stop_timer(sk, &tp->mptcp->mptcp_ack_timer); ++ tcp_send_active_reset(sk, GFP_ATOMIC); ++ goto out; ++ } ++ ++ skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); ++ if (skb == NULL) { ++ sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, ++ jiffies + icsk->icsk_rto); ++ return; ++ } ++ ++ /* Reserve space for headers and prepare control bits */ ++ skb_reserve(skb, MAX_TCP_HEADER); ++ tcp_init_nondata_skb(skb, tp->snd_una, TCPHDR_ACK); ++ ++ TCP_SKB_CB(skb)->when = tcp_time_stamp; ++ if (tcp_transmit_skb(sk, skb, 0, GFP_ATOMIC) > 0) { ++ /* Retransmission failed because of local congestion, ++ * do not backoff. ++ */ ++ if (!icsk->icsk_retransmits) ++ icsk->icsk_retransmits = 1; ++ sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, ++ jiffies + icsk->icsk_rto); ++ return; ++ } ++ ++ ++ icsk->icsk_retransmits++; ++ icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX); ++ sk_reset_timer(sk, &tp->mptcp->mptcp_ack_timer, ++ jiffies + icsk->icsk_rto); ++ if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1, 0, 0)) { ++ __sk_dst_reset(sk); ++ } ++ ++out:; ++} ++ ++void mptcp_ack_handler(unsigned long data) ++{ ++ struct sock *sk = (struct sock *)data; ++ struct sock *meta_sk = mptcp_meta_sk(sk); ++ ++ bh_lock_sock(meta_sk); ++ if (sock_owned_by_user(meta_sk)) { ++ /* Try again later */ ++ sk_reset_timer(sk, &tcp_sk(sk)->mptcp->mptcp_ack_timer, ++ jiffies + (HZ / 20)); ++ goto out_unlock; ++ } ++ ++ if (sk->sk_state == TCP_CLOSE) ++ goto out_unlock; ++ ++ mptcp_ack_retransmit_timer(sk); ++ ++ sk_mem_reclaim(sk); ++ ++out_unlock: ++ bh_unlock_sock(meta_sk); ++ sock_put(sk); ++} ++ ++/* Similar to tcp_retransmit_skb ++ * ++ * The diff is that we handle the retransmission-stats (retrans_stamp) at the ++ * meta-level. ++ */ ++int mptcp_retransmit_skb(struct sock *meta_sk, struct sk_buff *skb) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct sock *subsk; ++ struct sk_buff *subskb; ++ unsigned int limit, tso_segs, mss_now; ++ int err = -1, oldpcount; ++ ++ /* Do not sent more than we queued. 1/4 is reserved for possible ++ * copying overhead: fragmentation, tunneling, mangling etc. ++ * ++ * This is a meta-retransmission thus we check on the meta-socket. ++ */ ++ if (atomic_read(&meta_sk->sk_wmem_alloc) > ++ min(meta_sk->sk_wmem_queued + (meta_sk->sk_wmem_queued >> 2), meta_sk->sk_sndbuf)) { ++ return -EAGAIN; ++ } ++ ++ /* We need to make sure that the retransmitted segment can be sent on a ++ * subflow right now. If it is too big, it needs to be fragmented. ++ */ ++ subsk = get_available_subflow(meta_sk, skb, &mss_now); ++ if (!subsk) { ++ /* We want to increase icsk_retransmits, thus return 0, so that ++ * mptcp_retransmit_timer enters the desired branch. ++ */ ++ err = 0; ++ goto failed; ++ } ++ ++ /* If the segment was cloned (e.g. a meta retransmission), the header ++ * must be expanded/copied so that there is no corruption of TSO ++ * information. ++ */ ++ if (skb_unclone(skb, GFP_ATOMIC)) { ++ err = ENOMEM; ++ goto failed; ++ } ++ ++ oldpcount = tcp_skb_pcount(skb); ++ tcp_set_skb_tso_segs(meta_sk, skb, mss_now); ++ tso_segs = tcp_skb_pcount(skb); ++ BUG_ON(!tso_segs); ++ ++ /* The MSS might have changed and so the number of segments. We ++ * need to account for this change. ++ */ ++ if (unlikely(oldpcount != tso_segs)) ++ tcp_adjust_pcount(meta_sk, skb, oldpcount - tso_segs); ++ ++ limit = mss_now; ++ if (tso_segs > 1 && !tcp_urg_mode(meta_tp)) ++ limit = tcp_mss_split_point(subsk, skb, mss_now, ++ min_t(unsigned int, ++ tcp_cwnd_test(tcp_sk(subsk), skb), ++ subsk->sk_gso_max_segs), ++ TCP_NAGLE_OFF); ++ ++ if (skb->len > limit && ++ unlikely(mptso_fragment(meta_sk, skb, limit, mss_now, ++ GFP_ATOMIC, 0))) ++ goto failed; ++ ++ subskb = mptcp_skb_entail(subsk, skb, -1); ++ if (!subskb) ++ goto failed; ++ ++ TCP_SKB_CB(skb)->when = tcp_time_stamp; ++ TCP_SKB_CB(subskb)->when = tcp_time_stamp; ++ err = tcp_transmit_skb(subsk, subskb, 1, GFP_ATOMIC); ++ if (!err) { ++ /* Update global TCP statistics. */ ++ TCP_INC_STATS(sock_net(meta_sk), TCP_MIB_RETRANSSEGS); ++ ++ /* Diff to tcp_retransmit_skb */ ++ ++ /* Save stamp of the first retransmit. */ ++ if (!meta_tp->retrans_stamp) ++ meta_tp->retrans_stamp = TCP_SKB_CB(subskb)->when; ++ mptcp_sub_event_new_data_sent(subsk, subskb, skb); ++ } else { ++ mptcp_transmit_skb_failed(subsk, skb, subskb); ++ } ++ ++failed: ++ return err; ++} ++ ++/* Similar to tcp_retransmit_timer ++ * ++ * The diff is that we have to handle retransmissions of the FAST_CLOSE-message ++ * and that we don't have an srtt estimation at the meta-level. ++ */ ++void mptcp_retransmit_timer(struct sock *meta_sk) ++{ ++ struct tcp_sock *meta_tp = tcp_sk(meta_sk); ++ struct mptcp_cb *mpcb = meta_tp->mpcb; ++ struct inet_connection_sock *meta_icsk = inet_csk(meta_sk); ++ int err; ++ ++ /* In fallback, retransmission is handled at the subflow-level */ ++ if (!meta_tp->packets_out || mpcb->infinite_mapping_snd || ++ mpcb->send_infinite_mapping) ++ return; ++ ++ WARN_ON(tcp_write_queue_empty(meta_sk)); ++ ++ if (!meta_tp->snd_wnd && !sock_flag(meta_sk, SOCK_DEAD) && ++ !((1 << meta_sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) { ++ /* Receiver dastardly shrinks window. Our retransmits ++ * become zero probes, but we should not timeout this ++ * connection. If the socket is an orphan, time it out, ++ * we cannot allow such beasts to hang infinitely. ++ */ ++ struct inet_sock *meta_inet = inet_sk(meta_sk); ++ if (meta_sk->sk_family == AF_INET) { ++ LIMIT_NETDEBUG(KERN_DEBUG "MPTCP: Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n", ++ &meta_inet->inet_daddr, ++ ntohs(meta_inet->inet_dport), ++ meta_inet->inet_num, meta_tp->snd_una, ++ meta_tp->snd_nxt); ++ } ++#if IS_ENABLED(CONFIG_IPV6) ++ else if (meta_sk->sk_family == AF_INET6) { ++ LIMIT_NETDEBUG(KERN_DEBUG "MPTCP: Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n", ++ &meta_sk->sk_v6_daddr, ++ ntohs(meta_inet->inet_dport), ++ meta_inet->inet_num, meta_tp->snd_una, ++ meta_tp->snd_nxt); ++ } ++#endif ++ if (tcp_time_stamp - meta_tp->rcv_tstamp > TCP_RTO_MAX) { ++ tcp_write_err(meta_sk); ++ return; ++ } ++ ++ mptcp_retransmit_skb(meta_sk, tcp_write_queue_head(meta_sk)); ++ goto out_reset_timer; ++ } ++ ++ if (tcp_write_timeout(meta_sk)) ++ return; ++ ++ if (meta_icsk->icsk_retransmits == 0) ++ NET_INC_STATS_BH(sock_net(meta_sk), LINUX_MIB_TCPTIMEOUTS); ++ ++ meta_icsk->icsk_ca_state = TCP_CA_Loss; ++ ++ err = mptcp_retransmit_skb(meta_sk, tcp_write_queue_head(meta_sk)); ++ if (err > 0) { ++ /* Retransmission failed because of local congestion, ++ * do not backoff. ++ */ ++ if (!meta_icsk->icsk_retransmits) ++ meta_icsk->icsk_retransmits = 1; ++ inet_csk_reset_xmit_timer(meta_sk, ICSK_TIME_RETRANS, ++ min(meta_icsk->icsk_rto, TCP_RESOURCE_PROBE_INTERVAL), ++ TCP_RTO_MAX); ++ return; ++ } ++ ++ /* Increase the timeout each time we retransmit. Note that ++ * we do not increase the rtt estimate. rto is initialized ++ * from rtt, but increases here. Jacobson (SIGCOMM 88) suggests ++ * that doubling rto each time is the least we can get away with. ++ * In KA9Q, Karn uses this for the first few times, and then ++ * goes to quadratic. netBSD doubles, but only goes up to *64, ++ * and clamps at 1 to 64 sec afterwards. Note that 120 sec is ++ * defined in the protocol as the maximum possible RTT. I guess ++ * we'll have to use something other than TCP to talk to the ++ * University of Mars. ++ * ++ * PAWS allows us longer timeouts and large windows, so once ++ * implemented ftp to mars will work nicely. We will have to fix ++ * the 120 second clamps though! ++ */ ++ meta_icsk->icsk_backoff++; ++ meta_icsk->icsk_retransmits++; ++ ++out_reset_timer: ++ /* If stream is thin, use linear timeouts. Since 'icsk_backoff' is ++ * used to reset timer, set to 0. Recalculate 'icsk_rto' as this ++ * might be increased if the stream oscillates between thin and thick, ++ * thus the old value might already be too high compared to the value ++ * set by 'tcp_set_rto' in tcp_input.c which resets the rto without ++ * backoff. Limit to TCP_THIN_LINEAR_RETRIES before initiating ++ * exponential backoff behaviour to avoid continue hammering ++ * linear-timeout retransmissions into a black hole ++ */ ++ if (meta_sk->sk_state == TCP_ESTABLISHED && ++ (meta_tp->thin_lto || sysctl_tcp_thin_linear_timeouts) && ++ tcp_stream_is_thin(meta_tp) && ++ meta_icsk->icsk_retransmits <= TCP_THIN_LINEAR_RETRIES) { ++ meta_icsk->icsk_backoff = 0; ++ /* We cannot do the same as in tcp_write_timer because the ++ * srtt is not set here. ++ */ ++ mptcp_set_rto(meta_sk); ++ } else { ++ /* Use normal (exponential) backoff */ ++ meta_icsk->icsk_rto = min(meta_icsk->icsk_rto << 1, TCP_RTO_MAX); ++ } ++ inet_csk_reset_xmit_timer(meta_sk, ICSK_TIME_RETRANS, meta_icsk->icsk_rto, TCP_RTO_MAX); ++ ++ return; ++} ++ ++/* Modify values to an mptcp-level for the initial window of new subflows */ ++void mptcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, ++ __u32 *window_clamp, int wscale_ok, ++ __u8 *rcv_wscale, __u32 init_rcv_wnd, ++ const struct sock *sk) ++{ ++ struct mptcp_cb *mpcb = tcp_sk(sk)->mpcb; ++ ++ *window_clamp = mpcb->orig_window_clamp; ++ __space = tcp_win_from_space(mpcb->orig_sk_rcvbuf); ++ ++ tcp_select_initial_window(__space, mss, rcv_wnd, window_clamp, ++ wscale_ok, rcv_wscale, init_rcv_wnd, sk); ++} ++ ++unsigned int mptcp_current_mss(struct sock *meta_sk) ++{ ++ unsigned int mss = 0; ++ struct sock *sk; ++ ++ mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { ++ int this_mss; ++ ++ if (!mptcp_sk_can_send(sk)) ++ continue; ++ ++ this_mss = tcp_current_mss(sk); ++ if (this_mss > mss) ++ mss = this_mss; ++ } ++ ++ /* If no subflow is available, we take a default-mss from the ++ * meta-socket. ++ */ ++ return !mss ? tcp_current_mss(meta_sk) : mss; ++} ++ ++int mptcp_select_size(const struct sock *meta_sk, bool sg) ++{ ++ int mss = 0; /* We look for the smallest MSS */ ++ struct sock *sk; ++ ++ mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { ++ int this_mss; ++ ++ if (!mptcp_sk_can_send(sk)) ++ continue; ++ ++ this_mss = tcp_sk(sk)->mss_cache; ++ if (this_mss > mss) ++ mss = this_mss; ++ } ++ ++ if (sg) { ++ if (mptcp_sk_can_gso(meta_sk)) { ++ mss = SKB_WITH_OVERHEAD(2048 - MAX_TCP_HEADER); ++ } else { ++ int pgbreak = SKB_MAX_HEAD(MAX_TCP_HEADER); ++ ++ if (mss >= pgbreak && ++ mss <= pgbreak + (MAX_SKB_FRAGS - 1) * PAGE_SIZE) ++ mss = pgbreak; ++ } ++ } ++ ++ return !mss ? tcp_sk(meta_sk)->mss_cache : mss; ++} ++ ++int mptcp_check_snd_buf(const struct tcp_sock *tp) ++{ ++ struct sock *sk; ++ u32 rtt_max = tp->srtt; ++ u64 bw_est; ++ ++ if (!tp->srtt) ++ return tp->reordering + 1; ++ ++ mptcp_for_each_sk(tp->mpcb, sk) { ++ if (!mptcp_sk_can_send(sk)) ++ continue; ++ ++ if (rtt_max < tcp_sk(sk)->srtt) ++ rtt_max = tcp_sk(sk)->srtt; ++ } ++ ++ bw_est = div64_u64(((u64)tp->snd_cwnd * rtt_max) << 16, ++ (u64)tp->srtt); ++ ++ return max_t(unsigned int, (u32)(bw_est >> 16), ++ tp->reordering + 1); ++ ++} ++ ++unsigned int mptcp_xmit_size_goal(struct sock *meta_sk, u32 mss_now, ++ int large_allowed) ++{ ++ struct sock *sk; ++ u32 xmit_size_goal = 0; ++ ++ if (large_allowed && mptcp_sk_can_gso(meta_sk)) { ++ mptcp_for_each_sk(tcp_sk(meta_sk)->mpcb, sk) { ++ int this_size_goal; ++ ++ if (!mptcp_sk_can_send(sk)) ++ continue; ++ ++ this_size_goal = tcp_xmit_size_goal(sk, mss_now, 1); ++ if (this_size_goal > xmit_size_goal) ++ xmit_size_goal = this_size_goal; ++ } ++ } ++ ++ return max(xmit_size_goal, mss_now); ++} ++ ++/* Similar to tcp_trim_head - but we correctly copy the DSS-option */ ++int mptcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len) ++{ ++ int dsslen = MPTCP_SUB_LEN_DSS_ALIGN + MPTCP_SUB_LEN_ACK_ALIGN + ++ MPTCP_SUB_LEN_SEQ_ALIGN; ++ char dss[dsslen]; ++ ++ /* DSS-option must be recovered afterwards. */ ++ memcpy(dss, skb->data - dsslen, dsslen); ++ ++ if (skb_cloned(skb)) { ++ /* pskb_expand_head will delete our DSS-option. We have to copy ++ * it back if pskb_expand_head succeeds. ++ */ ++ ++ if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) ++ return -ENOMEM; ++ ++ memcpy(skb->data - dsslen, dss, dsslen); ++ } ++ ++ __pskb_trim_head(skb, len); ++ ++ /* Put the DSS-option back in our header */ ++ memcpy(skb->data - dsslen, dss, dsslen); ++ ++ TCP_SKB_CB(skb)->seq += len; ++ skb->ip_summed = CHECKSUM_PARTIAL; ++ ++ skb->truesize -= len; ++ sk->sk_wmem_queued -= len; ++ sk_mem_uncharge(sk, len); ++ sock_set_flag(sk, SOCK_QUEUE_SHRUNK); ++ ++ /* Any change of skb->len requires recalculation of tso factor. */ ++ if (tcp_skb_pcount(skb) > 1) ++ tcp_set_skb_tso_segs(sk, skb, tcp_skb_mss(skb)); ++ ++ return 0; ++} +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_pm.c linux-3.14.45/net/mptcp/mptcp_pm.c +--- linux-3.14.45.orig/net/mptcp/mptcp_pm.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_pm.c 2015-06-24 14:15:48.931862523 +0200 +@@ -0,0 +1,170 @@ ++/* ++ * MPTCP implementation - MPTCP-subflow-management ++ * ++ * Initial Design & Implementation: ++ * Sébastien Barré ++ * ++ * Current Maintainer & Author: ++ * Christoph Paasch ++ * ++ * Additional authors: ++ * Jaakko Korkeaniemi ++ * Gregory Detal ++ * Fabien Duchêne ++ * Andreas Seelinger ++ * Lavkesh Lahngir ++ * Andreas Ripke ++ * Vlad Dogaru ++ * Octavian Purdila ++ * John Ronan ++ * Catalin Nicutar ++ * Brandon Heller ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++ ++#include ++#include ++ ++static DEFINE_SPINLOCK(mptcp_pm_list_lock); ++static LIST_HEAD(mptcp_pm_list); ++ ++static int mptcp_default_index(sa_family_t family, union inet_addr *addr, ++ struct net *net) ++{ ++ return 0; ++} ++ ++struct mptcp_pm_ops mptcp_pm_default = { ++ .get_local_index = mptcp_default_index, ++ .get_local_id = mptcp_default_index, /* We do not care */ ++ .name = "default", ++ .owner = THIS_MODULE, ++}; ++ ++static struct mptcp_pm_ops *mptcp_pm_find(const char *name) ++{ ++ struct mptcp_pm_ops *e; ++ ++ list_for_each_entry_rcu(e, &mptcp_pm_list, list) { ++ if (strcmp(e->name, name) == 0) ++ return e; ++ } ++ ++ return NULL; ++} ++ ++int mptcp_register_path_manager(struct mptcp_pm_ops *pm) ++{ ++ int ret = 0; ++ ++ if (!pm->get_local_index || !pm->get_local_id) ++ return -EINVAL; ++ ++ spin_lock(&mptcp_pm_list_lock); ++ if (mptcp_pm_find(pm->name)) { ++ pr_notice("%s already registered\n", pm->name); ++ ret = -EEXIST; ++ } else { ++ list_add_tail_rcu(&pm->list, &mptcp_pm_list); ++ pr_info("%s registered\n", pm->name); ++ } ++ spin_unlock(&mptcp_pm_list_lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(mptcp_register_path_manager); ++ ++void mptcp_unregister_path_manager(struct mptcp_pm_ops *pm) ++{ ++ spin_lock(&mptcp_pm_list_lock); ++ list_del_rcu(&pm->list); ++ spin_unlock(&mptcp_pm_list_lock); ++} ++EXPORT_SYMBOL_GPL(mptcp_unregister_path_manager); ++ ++void mptcp_get_default_path_manager(char *name) ++{ ++ struct mptcp_pm_ops *pm; ++ ++ BUG_ON(list_empty(&mptcp_pm_list)); ++ ++ rcu_read_lock(); ++ pm = list_entry(mptcp_pm_list.next, struct mptcp_pm_ops, list); ++ strncpy(name, pm->name, MPTCP_PM_NAME_MAX); ++ rcu_read_unlock(); ++} ++ ++int mptcp_set_default_path_manager(const char *name) ++{ ++ struct mptcp_pm_ops *pm; ++ int ret = -ENOENT; ++ ++ spin_lock(&mptcp_pm_list_lock); ++ pm = mptcp_pm_find(name); ++#ifdef CONFIG_MODULES ++ if (!pm && capable(CAP_NET_ADMIN)) { ++ spin_unlock(&mptcp_pm_list_lock); ++ ++ request_module("mptcp_%s", name); ++ spin_lock(&mptcp_pm_list_lock); ++ pm = mptcp_pm_find(name); ++ } ++#endif ++ ++ if (pm) { ++ list_move(&pm->list, &mptcp_pm_list); ++ ret = 0; ++ } else { ++ pr_info("%s is not available\n", name); ++ } ++ spin_unlock(&mptcp_pm_list_lock); ++ ++ return ret; ++} ++ ++void mptcp_init_path_manager(struct mptcp_cb *mpcb) ++{ ++ struct mptcp_pm_ops *pm; ++ ++ rcu_read_lock(); ++ list_for_each_entry_rcu(pm, &mptcp_pm_list, list) { ++ if (try_module_get(pm->owner)) { ++ mpcb->pm_ops = pm; ++ break; ++ } ++ } ++ rcu_read_unlock(); ++} ++ ++/* Manage refcounts on socket close. */ ++void mptcp_cleanup_path_manager(struct mptcp_cb *mpcb) ++{ ++ module_put(mpcb->pm_ops->owner); ++} ++ ++/* Fallback to the default path-manager. */ ++void mptcp_fallback_default(struct mptcp_cb *mpcb) ++{ ++ struct mptcp_pm_ops *pm; ++ ++ mptcp_cleanup_path_manager(mpcb); ++ pm = mptcp_pm_find("default"); ++ ++ /* Cannot fail - it's the default module */ ++ try_module_get(pm->owner); ++ mpcb->pm_ops = pm; ++} ++EXPORT_SYMBOL_GPL(mptcp_fallback_default); ++ ++/* Set default value from kernel configuration at bootup */ ++static int __init mptcp_path_manager_default(void) ++{ ++ return mptcp_set_default_path_manager(CONFIG_DEFAULT_MPTCP_PM); ++} ++late_initcall(mptcp_path_manager_default); +diff -Nur linux-3.14.45.orig/net/mptcp/mptcp_wvegas.c linux-3.14.45/net/mptcp/mptcp_wvegas.c +--- linux-3.14.45.orig/net/mptcp/mptcp_wvegas.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.45/net/mptcp/mptcp_wvegas.c 2015-06-24 14:15:48.931862523 +0200 +@@ -0,0 +1,270 @@ ++/* ++ * MPTCP implementation - WEIGHTED VEGAS ++ * ++ * Algorithm design: ++ * Yu Cao ++ * Mingwei Xu ++ * Xiaoming Fu ++ * ++ * Implementation: ++ * Yu Cao ++ * Enhuan Dong ++ * ++ * Ported to the official MPTCP-kernel: ++ * Christoph Paasch ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static int initial_alpha = 2; ++static int total_alpha = 10; ++static int gamma = 1; ++ ++module_param(initial_alpha, int, 0644); ++MODULE_PARM_DESC(initial_alpha, "initial alpha for all subflows"); ++module_param(total_alpha, int, 0644); ++MODULE_PARM_DESC(total_alpha, "total alpha for all subflows"); ++module_param(gamma, int, 0644); ++MODULE_PARM_DESC(gamma, "limit on increase (scale by 2)"); ++ ++#define MPTCP_WVEGAS_SCALE 16 ++ ++/* wVegas variables */ ++struct wvegas { ++ u32 beg_snd_nxt; /* right edge during last RTT */ ++ u8 doing_wvegas_now;/* if true, do wvegas for this RTT */ ++ ++ u16 cnt_rtt; /* # of RTTs measured within last RTT */ ++ u32 sampled_rtt; /* cumulative RTTs measured within last RTT (in usec) */ ++ u32 base_rtt; /* the min of all wVegas RTT measurements seen (in usec) */ ++ ++ u64 instant_rate; /* cwnd / srtt_us, unit: pkts/us * 2^16 */ ++ u64 weight; /* the ratio of subflow's rate to the total rate, * 2^16 */ ++ int alpha; /* alpha for each subflows */ ++ ++ u32 queue_delay; /* queue delay*/ ++}; ++ ++ ++static inline u64 mptcp_wvegas_scale(u32 val, int scale) ++{ ++ return (u64) val << scale; ++} ++ ++static void wvegas_enable(struct sock *sk) ++{ ++ const struct tcp_sock *tp = tcp_sk(sk); ++ struct wvegas *wvegas = inet_csk_ca(sk); ++ ++ wvegas->doing_wvegas_now = 1; ++ ++ wvegas->beg_snd_nxt = tp->snd_nxt; ++ ++ wvegas->cnt_rtt = 0; ++ wvegas->sampled_rtt = 0; ++ ++ wvegas->instant_rate = 0; ++ wvegas->alpha = initial_alpha; ++ wvegas->weight = mptcp_wvegas_scale(1, MPTCP_WVEGAS_SCALE); ++ ++ wvegas->queue_delay = 0; ++} ++ ++static inline void wvegas_disable(struct sock *sk) ++{ ++ struct wvegas *wvegas = inet_csk_ca(sk); ++ ++ wvegas->doing_wvegas_now = 0; ++} ++ ++static void mptcp_wvegas_init(struct sock *sk) ++{ ++ struct wvegas *wvegas = inet_csk_ca(sk); ++ ++ wvegas->base_rtt = 0x7fffffff; ++ wvegas_enable(sk); ++} ++ ++static inline u64 mptcp_wvegas_rate(u32 cwnd, u32 rtt_us) ++{ ++ return div_u64(mptcp_wvegas_scale(cwnd, MPTCP_WVEGAS_SCALE), rtt_us); ++} ++ ++static void mptcp_wvegas_pkts_acked(struct sock *sk, u32 cnt, s32 rtt_us) ++{ ++ struct wvegas *wvegas = inet_csk_ca(sk); ++ u32 vrtt; ++ ++ if (rtt_us < 0) ++ return; ++ ++ vrtt = rtt_us + 1; ++ ++ if (vrtt < wvegas->base_rtt) ++ wvegas->base_rtt = vrtt; ++ ++ wvegas->sampled_rtt += vrtt; ++ wvegas->cnt_rtt++; ++} ++ ++static void mptcp_wvegas_state(struct sock *sk, u8 ca_state) ++{ ++ if (ca_state == TCP_CA_Open) ++ wvegas_enable(sk); ++ else ++ wvegas_disable(sk); ++} ++ ++static void mptcp_wvegas_cwnd_event(struct sock *sk, enum tcp_ca_event event) ++{ ++ if (event == CA_EVENT_CWND_RESTART) { ++ mptcp_wvegas_init(sk); ++ } else if (event == CA_EVENT_LOSS) { ++ struct wvegas *wvegas = inet_csk_ca(sk); ++ wvegas->instant_rate = 0; ++ } ++} ++ ++static inline u32 mptcp_wvegas_ssthresh(struct tcp_sock *tp) ++{ ++ return min(tp->snd_ssthresh, tp->snd_cwnd - 1); ++} ++ ++static u64 mptcp_wvegas_weight(struct mptcp_cb *mpcb, struct sock *sk) ++{ ++ u64 total_rate = 0; ++ struct sock *sub_sk; ++ struct wvegas *wvegas = inet_csk_ca(sk); ++ ++ if (!mpcb) ++ return wvegas->weight; ++ ++ ++ mptcp_for_each_sk(mpcb, sub_sk) { ++ struct wvegas *sub_wvegas = inet_csk_ca(sub_sk); ++ ++ /* sampled_rtt is initialized by 0 */ ++ if (mptcp_sk_can_send(sub_sk) && (sub_wvegas->sampled_rtt > 0)) ++ total_rate += sub_wvegas->instant_rate; ++ } ++ ++ if (total_rate && wvegas->instant_rate) ++ return div64_u64(mptcp_wvegas_scale(wvegas->instant_rate, MPTCP_WVEGAS_SCALE), total_rate); ++ else ++ return wvegas->weight; ++} ++ ++static void mptcp_wvegas_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) ++{ ++ struct tcp_sock *tp = tcp_sk(sk); ++ struct wvegas *wvegas = inet_csk_ca(sk); ++ ++ if (!wvegas->doing_wvegas_now) { ++ tcp_reno_cong_avoid(sk, ack, acked, in_flight); ++ return; ++ } ++ ++ if (after(ack, wvegas->beg_snd_nxt)) { ++ wvegas->beg_snd_nxt = tp->snd_nxt; ++ ++ if (wvegas->cnt_rtt <= 2) { ++ tcp_reno_cong_avoid(sk, ack, acked, in_flight); ++ } else { ++ u32 rtt, diff, q_delay; ++ u64 target_cwnd; ++ ++ rtt = wvegas->sampled_rtt / wvegas->cnt_rtt; ++ target_cwnd = div_u64(((u64)tp->snd_cwnd * wvegas->base_rtt), rtt); ++ ++ diff = div_u64((u64)tp->snd_cwnd * (rtt - wvegas->base_rtt), rtt); ++ ++ if (diff > gamma && tp->snd_cwnd <= tp->snd_ssthresh) { ++ tp->snd_cwnd = min(tp->snd_cwnd, (u32)target_cwnd+1); ++ tp->snd_ssthresh = mptcp_wvegas_ssthresh(tp); ++ ++ } else if (tp->snd_cwnd <= tp->snd_ssthresh) { ++ tcp_slow_start(tp, acked); ++ } else { ++ if (diff >= wvegas->alpha) { ++ wvegas->instant_rate = mptcp_wvegas_rate(tp->snd_cwnd, rtt); ++ wvegas->weight = mptcp_wvegas_weight(tp->mpcb, sk); ++ wvegas->alpha = max(2U, (u32)((wvegas->weight * total_alpha) >> MPTCP_WVEGAS_SCALE)); ++ } ++ if (diff > wvegas->alpha) { ++ tp->snd_cwnd--; ++ tp->snd_ssthresh = mptcp_wvegas_ssthresh(tp); ++ } else if (diff < wvegas->alpha) { ++ tp->snd_cwnd++; ++ } ++ ++ /* Try to drain link queue if needed*/ ++ q_delay = rtt - wvegas->base_rtt; ++ if ((wvegas->queue_delay == 0) || (wvegas->queue_delay > q_delay)) ++ wvegas->queue_delay = q_delay; ++ ++ if (q_delay >= 2 * wvegas->queue_delay) { ++ u32 backoff_factor = div_u64(mptcp_wvegas_scale(wvegas->base_rtt, MPTCP_WVEGAS_SCALE), 2 * rtt); ++ tp->snd_cwnd = ((u64)tp->snd_cwnd * backoff_factor) >> MPTCP_WVEGAS_SCALE; ++ wvegas->queue_delay = 0; ++ } ++ } ++ ++ if (tp->snd_cwnd < 2) ++ tp->snd_cwnd = 2; ++ else if (tp->snd_cwnd > tp->snd_cwnd_clamp) ++ tp->snd_cwnd = tp->snd_cwnd_clamp; ++ ++ tp->snd_ssthresh = tcp_current_ssthresh(sk); ++ } ++ ++ wvegas->cnt_rtt = 0; ++ wvegas->sampled_rtt = 0; ++ } ++ /* Use normal slow start */ ++ else if (tp->snd_cwnd <= tp->snd_ssthresh) ++ tcp_slow_start(tp, acked); ++} ++ ++ ++static struct tcp_congestion_ops mptcp_wvegas __read_mostly = { ++ .flags = TCP_CONG_RTT_STAMP, ++ .init = mptcp_wvegas_init, ++ .ssthresh = tcp_reno_ssthresh, ++ .cong_avoid = mptcp_wvegas_cong_avoid, ++ .min_cwnd = tcp_reno_min_cwnd, ++ .pkts_acked = mptcp_wvegas_pkts_acked, ++ .set_state = mptcp_wvegas_state, ++ .cwnd_event = mptcp_wvegas_cwnd_event, ++ ++ .owner = THIS_MODULE, ++ .name = "wvegas", ++}; ++ ++static int __init mptcp_wvegas_register(void) ++{ ++ BUILD_BUG_ON(sizeof(struct wvegas) > ICSK_CA_PRIV_SIZE); ++ tcp_register_congestion_control(&mptcp_wvegas); ++ return 0; ++} ++ ++static void __exit mptcp_wvegas_unregister(void) ++{ ++ tcp_unregister_congestion_control(&mptcp_wvegas); ++} ++ ++module_init(mptcp_wvegas_register); ++module_exit(mptcp_wvegas_unregister); ++ ++MODULE_AUTHOR("Yu Cao, Enhuan Dong"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MPTCP wVegas"); ++MODULE_VERSION("0.1"); diff --git a/target/linux/patches/3.14.45/mtd-rootfs.patch b/target/linux/patches/3.14.45/mtd-rootfs.patch new file mode 100644 index 000000000..775d5fc80 --- /dev/null +++ b/target/linux/patches/3.14.45/mtd-rootfs.patch @@ -0,0 +1,26 @@ +diff -Nur linux-3.5.orig//drivers/mtd/mtdpart.c linux-3.5/drivers/mtd/mtdpart.c +--- linux-3.5.orig//drivers/mtd/mtdpart.c 2012-07-21 22:58:29.000000000 +0200 ++++ linux-3.5/drivers/mtd/mtdpart.c 2012-07-31 23:59:07.000000000 +0200 +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + + #include "mtdcore.h" + +@@ -637,6 +638,14 @@ + if (IS_ERR(slave)) + return PTR_ERR(slave); + ++ if (strcmp(parts[i].name, "rootfs") == 0) { ++ if (ROOT_DEV == 0) { ++ printk(KERN_NOTICE "mtd: partition \"rootfs\" " ++ "set to be root filesystem\n"); ++ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, i); ++ } ++ } ++ + mutex_lock(&mtd_partitions_mutex); + list_add(&slave->list, &mtd_partitions); + mutex_unlock(&mtd_partitions_mutex); diff --git a/target/linux/patches/3.14.45/nfsv3-tcp.patch b/target/linux/patches/3.14.45/nfsv3-tcp.patch new file mode 100644 index 000000000..68ed95b08 --- /dev/null +++ b/target/linux/patches/3.14.45/nfsv3-tcp.patch @@ -0,0 +1,12 @@ +diff -Nur linux-3.13.11.orig/fs/nfs/nfsroot.c linux-3.13.11/fs/nfs/nfsroot.c +--- linux-3.13.11.orig/fs/nfs/nfsroot.c 2014-04-23 01:49:33.000000000 +0200 ++++ linux-3.13.11/fs/nfs/nfsroot.c 2014-05-13 16:09:25.000000000 +0200 +@@ -87,7 +87,7 @@ + #define NFS_ROOT "/tftpboot/%s" + + /* Default NFSROOT mount options. */ +-#define NFS_DEF_OPTIONS "vers=2,udp,rsize=4096,wsize=4096" ++#define NFS_DEF_OPTIONS "nfsvers=3,proto=tcp,rsize=4096,wsize=4096" + + /* Parameters passed from the kernel command line */ + static char nfs_root_parms[256] __initdata = ""; diff --git a/target/linux/patches/3.14.45/non-static.patch b/target/linux/patches/3.14.45/non-static.patch new file mode 100644 index 000000000..a967703d0 --- /dev/null +++ b/target/linux/patches/3.14.45/non-static.patch @@ -0,0 +1,33 @@ +diff -Nur linux-2.6.39-rc6.orig/fs/namei.c linux-2.6.39-rc6/fs/namei.c +--- linux-2.6.39-rc6.orig/fs/namei.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6/fs/namei.c 2011-05-05 11:30:14.000000000 +0200 +@@ -1769,7 +1769,7 @@ + * needs parent already locked. Doesn't follow mounts. + * SMP-safe. + */ +-static struct dentry *lookup_hash(struct nameidata *nd) ++struct dentry *lookup_hash(struct nameidata *nd) + { + return __lookup_hash(&nd->last, nd->path.dentry, nd); + } +diff -Nur linux-2.6.39-rc6.orig/fs/splice.c linux-2.6.39-rc6/fs/splice.c +--- linux-2.6.39-rc6.orig/fs/splice.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6/fs/splice.c 2011-05-05 11:31:04.000000000 +0200 +@@ -1081,7 +1081,7 @@ + /* + * Attempt to initiate a splice from pipe to file. + */ +-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) + { + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, +@@ -1109,7 +1109,7 @@ + /* + * Attempt to initiate a splice from a file to a pipe. + */ +-static long do_splice_to(struct file *in, loff_t *ppos, ++long do_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) + { diff --git a/target/linux/patches/3.14.45/ppc64-missing-zlib.patch b/target/linux/patches/3.14.45/ppc64-missing-zlib.patch new file mode 100644 index 000000000..c6e0616be --- /dev/null +++ b/target/linux/patches/3.14.45/ppc64-missing-zlib.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig +--- linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig 2013-11-01 15:23:09.000000000 +0100 +@@ -17,6 +17,7 @@ + select PPC_NATIVE + select PPC_PCI_CHOICE if EXPERT + select ZLIB_DEFLATE ++ select ZLIB_INFLATE + select PPC_DOORBELL + select HAVE_CONTEXT_TRACKING + select HOTPLUG_CPU if SMP diff --git a/target/linux/patches/3.14.45/regmap-boolean.patch b/target/linux/patches/3.14.45/regmap-boolean.patch new file mode 100644 index 000000000..d73620e4d --- /dev/null +++ b/target/linux/patches/3.14.45/regmap-boolean.patch @@ -0,0 +1,24 @@ +diff -Nur linux-3.14.4.orig/drivers/base/regmap/Kconfig linux-3.14.4/drivers/base/regmap/Kconfig +--- linux-3.14.4.orig/drivers/base/regmap/Kconfig 2014-05-13 13:33:14.000000000 +0200 ++++ linux-3.14.4/drivers/base/regmap/Kconfig 2014-05-21 13:46:48.928985115 +0200 +@@ -10,16 +10,16 @@ + bool + + config REGMAP_I2C +- tristate ++ boolean + + config REGMAP_SPI +- tristate ++ boolean + + config REGMAP_SPMI +- tristate ++ boolean + + config REGMAP_MMIO +- tristate ++ boolean + + config REGMAP_IRQ + bool diff --git a/target/linux/patches/3.14.45/relocs.patch b/target/linux/patches/3.14.45/relocs.patch new file mode 100644 index 000000000..69a7c88a9 --- /dev/null +++ b/target/linux/patches/3.14.45/relocs.patch @@ -0,0 +1,2709 @@ +diff -Nur linux-3.13.6.orig/arch/x86/tools/relocs.c linux-3.13.6/arch/x86/tools/relocs.c +--- linux-3.13.6.orig/arch/x86/tools/relocs.c 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/arch/x86/tools/relocs.c 2014-03-15 19:39:45.000000000 +0100 +@@ -126,6 +126,7 @@ + + if (err) { + regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); ++ printf("foo: %s\n", sym_regex[i]); + die("%s", errbuf); + } + } +diff -Nur linux-3.13.6.orig/arch/x86/tools/relocs.h linux-3.13.6/arch/x86/tools/relocs.h +--- linux-3.13.6.orig/arch/x86/tools/relocs.h 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/arch/x86/tools/relocs.h 2014-03-15 18:48:40.000000000 +0100 +@@ -9,11 +9,19 @@ + #include + #include + #include ++#ifdef __linux__ + #include + #include + #define USE_BSD + #include ++#else ++#include "elf.h" ++#endif ++#ifdef __APPLE__ ++#include ++#else + #include ++#endif + #include + + void die(char *fmt, ...); +diff -Nur linux-3.13.6.orig/tools/include/elf.h linux-3.13.6/tools/include/elf.h +--- linux-3.13.6.orig/tools/include/elf.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.13.6/tools/include/elf.h 2014-03-15 18:47:36.000000000 +0100 +@@ -0,0 +1,2671 @@ ++#ifndef _ELF_H ++#define _ELF_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include ++ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++#define EI_NIDENT (16) ++ ++typedef struct { ++ unsigned char e_ident[EI_NIDENT]; ++ Elf32_Half e_type; ++ Elf32_Half e_machine; ++ Elf32_Word e_version; ++ Elf32_Addr e_entry; ++ Elf32_Off e_phoff; ++ Elf32_Off e_shoff; ++ Elf32_Word e_flags; ++ Elf32_Half e_ehsize; ++ Elf32_Half e_phentsize; ++ Elf32_Half e_phnum; ++ Elf32_Half e_shentsize; ++ Elf32_Half e_shnum; ++ Elf32_Half e_shstrndx; ++} Elf32_Ehdr; ++ ++typedef struct { ++ unsigned char e_ident[EI_NIDENT]; ++ Elf64_Half e_type; ++ Elf64_Half e_machine; ++ Elf64_Word e_version; ++ Elf64_Addr e_entry; ++ Elf64_Off e_phoff; ++ Elf64_Off e_shoff; ++ Elf64_Word e_flags; ++ Elf64_Half e_ehsize; ++ Elf64_Half e_phentsize; ++ Elf64_Half e_phnum; ++ Elf64_Half e_shentsize; ++ Elf64_Half e_shnum; ++ Elf64_Half e_shstrndx; ++} Elf64_Ehdr; ++ ++#define EI_MAG0 0 ++#define ELFMAG0 0x7f ++ ++#define EI_MAG1 1 ++#define ELFMAG1 'E' ++ ++#define EI_MAG2 2 ++#define ELFMAG2 'L' ++ ++#define EI_MAG3 3 ++#define ELFMAG3 'F' ++ ++ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 ++#define ELFCLASSNONE 0 ++#define ELFCLASS32 1 ++#define ELFCLASS64 2 ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 ++#define ELFDATANONE 0 ++#define ELFDATA2LSB 1 ++#define ELFDATA2MSB 2 ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 ++ ++ ++#define EI_OSABI 7 ++#define ELFOSABI_NONE 0 ++#define ELFOSABI_SYSV 0 ++#define ELFOSABI_HPUX 1 ++#define ELFOSABI_NETBSD 2 ++#define ELFOSABI_LINUX 3 ++#define ELFOSABI_GNU 3 ++#define ELFOSABI_SOLARIS 6 ++#define ELFOSABI_AIX 7 ++#define ELFOSABI_IRIX 8 ++#define ELFOSABI_FREEBSD 9 ++#define ELFOSABI_TRU64 10 ++#define ELFOSABI_MODESTO 11 ++#define ELFOSABI_OPENBSD 12 ++#define ELFOSABI_ARM 97 ++#define ELFOSABI_STANDALONE 255 ++ ++#define EI_ABIVERSION 8 ++ ++#define EI_PAD 9 ++ ++ ++ ++#define ET_NONE 0 ++#define ET_REL 1 ++#define ET_EXEC 2 ++#define ET_DYN 3 ++#define ET_CORE 4 ++#define ET_NUM 5 ++#define ET_LOOS 0xfe00 ++#define ET_HIOS 0xfeff ++#define ET_LOPROC 0xff00 ++#define ET_HIPROC 0xffff ++ ++ ++ ++#define EM_NONE 0 ++#define EM_M32 1 ++#define EM_SPARC 2 ++#define EM_386 3 ++#define EM_68K 4 ++#define EM_88K 5 ++#define EM_860 7 ++#define EM_MIPS 8 ++#define EM_S370 9 ++#define EM_MIPS_RS3_LE 10 ++ ++#define EM_PARISC 15 ++#define EM_VPP500 17 ++#define EM_SPARC32PLUS 18 ++#define EM_960 19 ++#define EM_PPC 20 ++#define EM_PPC64 21 ++#define EM_S390 22 ++ ++#define EM_V800 36 ++#define EM_FR20 37 ++#define EM_RH32 38 ++#define EM_RCE 39 ++#define EM_ARM 40 ++#define EM_FAKE_ALPHA 41 ++#define EM_SH 42 ++#define EM_SPARCV9 43 ++#define EM_TRICORE 44 ++#define EM_ARC 45 ++#define EM_H8_300 46 ++#define EM_H8_300H 47 ++#define EM_H8S 48 ++#define EM_H8_500 49 ++#define EM_IA_64 50 ++#define EM_MIPS_X 51 ++#define EM_COLDFIRE 52 ++#define EM_68HC12 53 ++#define EM_MMA 54 ++#define EM_PCP 55 ++#define EM_NCPU 56 ++#define EM_NDR1 57 ++#define EM_STARCORE 58 ++#define EM_ME16 59 ++#define EM_ST100 60 ++#define EM_TINYJ 61 ++#define EM_X86_64 62 ++#define EM_PDSP 63 ++ ++#define EM_FX66 66 ++#define EM_ST9PLUS 67 ++#define EM_ST7 68 ++#define EM_68HC16 69 ++#define EM_68HC11 70 ++#define EM_68HC08 71 ++#define EM_68HC05 72 ++#define EM_SVX 73 ++#define EM_ST19 74 ++#define EM_VAX 75 ++#define EM_CRIS 76 ++#define EM_JAVELIN 77 ++#define EM_FIREPATH 78 ++#define EM_ZSP 79 ++#define EM_MMIX 80 ++#define EM_HUANY 81 ++#define EM_PRISM 82 ++#define EM_AVR 83 ++#define EM_FR30 84 ++#define EM_D10V 85 ++#define EM_D30V 86 ++#define EM_V850 87 ++#define EM_M32R 88 ++#define EM_MN10300 89 ++#define EM_MN10200 90 ++#define EM_PJ 91 ++#define EM_OPENRISC 92 ++#define EM_ARC_A5 93 ++#define EM_XTENSA 94 ++#define EM_AARCH64 183 ++#define EM_TILEPRO 188 ++#define EM_MICROBLAZE 189 ++#define EM_TILEGX 191 ++#define EM_NUM 192 ++#define EM_ALPHA 0x9026 ++ ++#define EV_NONE 0 ++#define EV_CURRENT 1 ++#define EV_NUM 2 ++ ++typedef struct { ++ Elf32_Word sh_name; ++ Elf32_Word sh_type; ++ Elf32_Word sh_flags; ++ Elf32_Addr sh_addr; ++ Elf32_Off sh_offset; ++ Elf32_Word sh_size; ++ Elf32_Word sh_link; ++ Elf32_Word sh_info; ++ Elf32_Word sh_addralign; ++ Elf32_Word sh_entsize; ++} Elf32_Shdr; ++ ++typedef struct { ++ Elf64_Word sh_name; ++ Elf64_Word sh_type; ++ Elf64_Xword sh_flags; ++ Elf64_Addr sh_addr; ++ Elf64_Off sh_offset; ++ Elf64_Xword sh_size; ++ Elf64_Word sh_link; ++ Elf64_Word sh_info; ++ Elf64_Xword sh_addralign; ++ Elf64_Xword sh_entsize; ++} Elf64_Shdr; ++ ++ ++ ++#define SHN_UNDEF 0 ++#define SHN_LORESERVE 0xff00 ++#define SHN_LOPROC 0xff00 ++#define SHN_BEFORE 0xff00 ++ ++#define SHN_AFTER 0xff01 ++ ++#define SHN_HIPROC 0xff1f ++#define SHN_LOOS 0xff20 ++#define SHN_HIOS 0xff3f ++#define SHN_ABS 0xfff1 ++#define SHN_COMMON 0xfff2 ++#define SHN_XINDEX 0xffff ++#define SHN_HIRESERVE 0xffff ++ ++ ++ ++#define SHT_NULL 0 ++#define SHT_PROGBITS 1 ++#define SHT_SYMTAB 2 ++#define SHT_STRTAB 3 ++#define SHT_RELA 4 ++#define SHT_HASH 5 ++#define SHT_DYNAMIC 6 ++#define SHT_NOTE 7 ++#define SHT_NOBITS 8 ++#define SHT_REL 9 ++#define SHT_SHLIB 10 ++#define SHT_DYNSYM 11 ++#define SHT_INIT_ARRAY 14 ++#define SHT_FINI_ARRAY 15 ++#define SHT_PREINIT_ARRAY 16 ++#define SHT_GROUP 17 ++#define SHT_SYMTAB_SHNDX 18 ++#define SHT_NUM 19 ++#define SHT_LOOS 0x60000000 ++#define SHT_GNU_ATTRIBUTES 0x6ffffff5 ++#define SHT_GNU_HASH 0x6ffffff6 ++#define SHT_GNU_LIBLIST 0x6ffffff7 ++#define SHT_CHECKSUM 0x6ffffff8 ++#define SHT_LOSUNW 0x6ffffffa ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd ++#define SHT_GNU_verneed 0x6ffffffe ++#define SHT_GNU_versym 0x6fffffff ++#define SHT_HISUNW 0x6fffffff ++#define SHT_HIOS 0x6fffffff ++#define SHT_LOPROC 0x70000000 ++#define SHT_HIPROC 0x7fffffff ++#define SHT_LOUSER 0x80000000 ++#define SHT_HIUSER 0x8fffffff ++ ++#define SHF_WRITE (1 << 0) ++#define SHF_ALLOC (1 << 1) ++#define SHF_EXECINSTR (1 << 2) ++#define SHF_MERGE (1 << 4) ++#define SHF_STRINGS (1 << 5) ++#define SHF_INFO_LINK (1 << 6) ++#define SHF_LINK_ORDER (1 << 7) ++#define SHF_OS_NONCONFORMING (1 << 8) ++ ++#define SHF_GROUP (1 << 9) ++#define SHF_TLS (1 << 10) ++#define SHF_MASKOS 0x0ff00000 ++#define SHF_MASKPROC 0xf0000000 ++#define SHF_ORDERED (1 << 30) ++#define SHF_EXCLUDE (1 << 31) ++ ++#define GRP_COMDAT 0x1 ++ ++typedef struct { ++ Elf32_Word st_name; ++ Elf32_Addr st_value; ++ Elf32_Word st_size; ++ unsigned char st_info; ++ unsigned char st_other; ++ Elf32_Section st_shndx; ++} Elf32_Sym; ++ ++typedef struct { ++ Elf64_Word st_name; ++ unsigned char st_info; ++ unsigned char st_other; ++ Elf64_Section st_shndx; ++ Elf64_Addr st_value; ++ Elf64_Xword st_size; ++} Elf64_Sym; ++ ++typedef struct { ++ Elf32_Half si_boundto; ++ Elf32_Half si_flags; ++} Elf32_Syminfo; ++ ++typedef struct { ++ Elf64_Half si_boundto; ++ Elf64_Half si_flags; ++} Elf64_Syminfo; ++ ++#define SYMINFO_BT_SELF 0xffff ++#define SYMINFO_BT_PARENT 0xfffe ++#define SYMINFO_BT_LOWRESERVE 0xff00 ++ ++#define SYMINFO_FLG_DIRECT 0x0001 ++#define SYMINFO_FLG_PASSTHRU 0x0002 ++#define SYMINFO_FLG_COPY 0x0004 ++#define SYMINFO_FLG_LAZYLOAD 0x0008 ++ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++#define STB_LOCAL 0 ++#define STB_GLOBAL 1 ++#define STB_WEAK 2 ++#define STB_NUM 3 ++#define STB_LOOS 10 ++#define STB_GNU_UNIQUE 10 ++#define STB_HIOS 12 ++#define STB_LOPROC 13 ++#define STB_HIPROC 15 ++ ++#define STT_NOTYPE 0 ++#define STT_OBJECT 1 ++#define STT_FUNC 2 ++#define STT_SECTION 3 ++#define STT_FILE 4 ++#define STT_COMMON 5 ++#define STT_TLS 6 ++#define STT_NUM 7 ++#define STT_LOOS 10 ++#define STT_GNU_IFUNC 10 ++#define STT_HIOS 12 ++#define STT_LOPROC 13 ++#define STT_HIPROC 15 ++ ++#define STN_UNDEF 0 ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++#define STV_DEFAULT 0 ++#define STV_INTERNAL 1 ++#define STV_HIDDEN 2 ++#define STV_PROTECTED 3 ++ ++ ++ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; ++ Elf32_Word r_info; ++} Elf32_Rel; ++ ++typedef struct { ++ Elf64_Addr r_offset; ++ Elf64_Xword r_info; ++} Elf64_Rel; ++ ++ ++ ++typedef struct { ++ Elf32_Addr r_offset; ++ Elf32_Word r_info; ++ Elf32_Sword r_addend; ++} Elf32_Rela; ++ ++typedef struct { ++ Elf64_Addr r_offset; ++ Elf64_Xword r_info; ++ Elf64_Sxword r_addend; ++} Elf64_Rela; ++ ++ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++ ++ ++typedef struct { ++ Elf32_Word p_type; ++ Elf32_Off p_offset; ++ Elf32_Addr p_vaddr; ++ Elf32_Addr p_paddr; ++ Elf32_Word p_filesz; ++ Elf32_Word p_memsz; ++ Elf32_Word p_flags; ++ Elf32_Word p_align; ++} Elf32_Phdr; ++ ++typedef struct { ++ Elf64_Word p_type; ++ Elf64_Word p_flags; ++ Elf64_Off p_offset; ++ Elf64_Addr p_vaddr; ++ Elf64_Addr p_paddr; ++ Elf64_Xword p_filesz; ++ Elf64_Xword p_memsz; ++ Elf64_Xword p_align; ++} Elf64_Phdr; ++ ++ ++ ++#define PT_NULL 0 ++#define PT_LOAD 1 ++#define PT_DYNAMIC 2 ++#define PT_INTERP 3 ++#define PT_NOTE 4 ++#define PT_SHLIB 5 ++#define PT_PHDR 6 ++#define PT_TLS 7 ++#define PT_NUM 8 ++#define PT_LOOS 0x60000000 ++#define PT_GNU_EH_FRAME 0x6474e550 ++#define PT_GNU_STACK 0x6474e551 ++#define PT_GNU_RELRO 0x6474e552 ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa ++#define PT_SUNWSTACK 0x6ffffffb ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff ++#define PT_LOPROC 0x70000000 ++#define PT_HIPROC 0x7fffffff ++ ++ ++#define PN_XNUM 0xffff ++ ++ ++#define PF_X (1 << 0) ++#define PF_W (1 << 1) ++#define PF_R (1 << 2) ++#define PF_MASKOS 0x0ff00000 ++#define PF_MASKPROC 0xf0000000 ++ ++ ++ ++#define NT_PRSTATUS 1 ++#define NT_FPREGSET 2 ++#define NT_PRPSINFO 3 ++#define NT_PRXREG 4 ++#define NT_TASKSTRUCT 4 ++#define NT_PLATFORM 5 ++#define NT_AUXV 6 ++#define NT_GWINDOWS 7 ++#define NT_ASRS 8 ++#define NT_PSTATUS 10 ++#define NT_PSINFO 13 ++#define NT_PRCRED 14 ++#define NT_UTSNAME 15 ++#define NT_LWPSTATUS 16 ++#define NT_LWPSINFO 17 ++#define NT_PRFPXREG 20 ++#define NT_SIGINFO 0x53494749 ++#define NT_FILE 0x46494c45 ++#define NT_PRXFPREG 0x46e62b7f ++#define NT_PPC_VMX 0x100 ++#define NT_PPC_SPE 0x101 ++#define NT_PPC_VSX 0x102 ++#define NT_386_TLS 0x200 ++#define NT_386_IOPERM 0x201 ++#define NT_X86_XSTATE 0x202 ++#define NT_S390_HIGH_GPRS 0x300 ++#define NT_S390_TIMER 0x301 ++#define NT_S390_TODCMP 0x302 ++#define NT_S390_TODPREG 0x303 ++#define NT_S390_CTRS 0x304 ++#define NT_S390_PREFIX 0x305 ++#define NT_S390_LAST_BREAK 0x306 ++#define NT_S390_SYSTEM_CALL 0x307 ++#define NT_S390_TDB 0x308 ++#define NT_ARM_VFP 0x400 ++#define NT_ARM_TLS 0x401 ++#define NT_ARM_HW_BREAK 0x402 ++#define NT_ARM_HW_WATCH 0x403 ++#define NT_METAG_CBUF 0x500 ++#define NT_METAG_RPIPE 0x501 ++#define NT_METAG_TLS 0x502 ++#define NT_VERSION 1 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Sword d_tag; ++ union { ++ Elf32_Word d_val; ++ Elf32_Addr d_ptr; ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct { ++ Elf64_Sxword d_tag; ++ union { ++ Elf64_Xword d_val; ++ Elf64_Addr d_ptr; ++ } d_un; ++} Elf64_Dyn; ++ ++ ++ ++#define DT_NULL 0 ++#define DT_NEEDED 1 ++#define DT_PLTRELSZ 2 ++#define DT_PLTGOT 3 ++#define DT_HASH 4 ++#define DT_STRTAB 5 ++#define DT_SYMTAB 6 ++#define DT_RELA 7 ++#define DT_RELASZ 8 ++#define DT_RELAENT 9 ++#define DT_STRSZ 10 ++#define DT_SYMENT 11 ++#define DT_INIT 12 ++#define DT_FINI 13 ++#define DT_SONAME 14 ++#define DT_RPATH 15 ++#define DT_SYMBOLIC 16 ++#define DT_REL 17 ++#define DT_RELSZ 18 ++#define DT_RELENT 19 ++#define DT_PLTREL 20 ++#define DT_DEBUG 21 ++#define DT_TEXTREL 22 ++#define DT_JMPREL 23 ++#define DT_BIND_NOW 24 ++#define DT_INIT_ARRAY 25 ++#define DT_FINI_ARRAY 26 ++#define DT_INIT_ARRAYSZ 27 ++#define DT_FINI_ARRAYSZ 28 ++#define DT_RUNPATH 29 ++#define DT_FLAGS 30 ++#define DT_ENCODING 32 ++#define DT_PREINIT_ARRAY 32 ++#define DT_PREINIT_ARRAYSZ 33 ++#define DT_NUM 34 ++#define DT_LOOS 0x6000000d ++#define DT_HIOS 0x6ffff000 ++#define DT_LOPROC 0x70000000 ++#define DT_HIPROC 0x7fffffff ++#define DT_PROCNUM DT_MIPS_NUM ++ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc ++#define DT_POSFLAG_1 0x6ffffdfd ++ ++#define DT_SYMINSZ 0x6ffffdfe ++#define DT_SYMINENT 0x6ffffdff ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) ++#define DT_VALNUM 12 ++ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_HASH 0x6ffffef5 ++#define DT_TLSDESC_PLT 0x6ffffef6 ++#define DT_TLSDESC_GOT 0x6ffffef7 ++#define DT_GNU_CONFLICT 0x6ffffef8 ++#define DT_GNU_LIBLIST 0x6ffffef9 ++#define DT_CONFIG 0x6ffffefa ++#define DT_DEPAUDIT 0x6ffffefb ++#define DT_AUDIT 0x6ffffefc ++#define DT_PLTPAD 0x6ffffefd ++#define DT_MOVETAB 0x6ffffefe ++#define DT_SYMINFO 0x6ffffeff ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) ++#define DT_ADDRNUM 11 ++ ++ ++ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++ ++#define DT_FLAGS_1 0x6ffffffb ++#define DT_VERDEF 0x6ffffffc ++ ++#define DT_VERDEFNUM 0x6ffffffd ++#define DT_VERNEED 0x6ffffffe ++ ++#define DT_VERNEEDNUM 0x6fffffff ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) ++#define DT_VERSIONTAGNUM 16 ++ ++ ++ ++#define DT_AUXILIARY 0x7ffffffd ++#define DT_FILTER 0x7fffffff ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++ ++#define DF_ORIGIN 0x00000001 ++#define DF_SYMBOLIC 0x00000002 ++#define DF_TEXTREL 0x00000004 ++#define DF_BIND_NOW 0x00000008 ++#define DF_STATIC_TLS 0x00000010 ++ ++ ++ ++#define DF_1_NOW 0x00000001 ++#define DF_1_GLOBAL 0x00000002 ++#define DF_1_GROUP 0x00000004 ++#define DF_1_NODELETE 0x00000008 ++#define DF_1_LOADFLTR 0x00000010 ++#define DF_1_INITFIRST 0x00000020 ++#define DF_1_NOOPEN 0x00000040 ++#define DF_1_ORIGIN 0x00000080 ++#define DF_1_DIRECT 0x00000100 ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 ++#define DF_1_NODEFLIB 0x00000800 ++#define DF_1_NODUMP 0x00001000 ++#define DF_1_CONFALT 0x00002000 ++#define DF_1_ENDFILTEE 0x00004000 ++#define DF_1_DISPRELDNE 0x00008000 ++#define DF_1_DISPRELPND 0x00010000 ++#define DF_1_NODIRECT 0x00020000 ++#define DF_1_IGNMULDEF 0x00040000 ++#define DF_1_NOKSYMS 0x00080000 ++#define DF_1_NOHDR 0x00100000 ++#define DF_1_EDITED 0x00200000 ++#define DF_1_NORELOC 0x00400000 ++#define DF_1_SYMINTPOSE 0x00800000 ++#define DF_1_GLOBAUDIT 0x01000000 ++#define DF_1_SINGLETON 0x02000000 ++ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++ ++#define DF_P1_LAZYLOAD 0x00000001 ++#define DF_P1_GROUPPERM 0x00000002 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Half vd_version; ++ Elf32_Half vd_flags; ++ Elf32_Half vd_ndx; ++ Elf32_Half vd_cnt; ++ Elf32_Word vd_hash; ++ Elf32_Word vd_aux; ++ Elf32_Word vd_next; ++} Elf32_Verdef; ++ ++typedef struct { ++ Elf64_Half vd_version; ++ Elf64_Half vd_flags; ++ Elf64_Half vd_ndx; ++ Elf64_Half vd_cnt; ++ Elf64_Word vd_hash; ++ Elf64_Word vd_aux; ++ Elf64_Word vd_next; ++} Elf64_Verdef; ++ ++ ++ ++#define VER_DEF_NONE 0 ++#define VER_DEF_CURRENT 1 ++#define VER_DEF_NUM 2 ++ ++ ++#define VER_FLG_BASE 0x1 ++#define VER_FLG_WEAK 0x2 ++ ++ ++#define VER_NDX_LOCAL 0 ++#define VER_NDX_GLOBAL 1 ++#define VER_NDX_LORESERVE 0xff00 ++#define VER_NDX_ELIMINATE 0xff01 ++ ++ ++ ++typedef struct { ++ Elf32_Word vda_name; ++ Elf32_Word vda_next; ++} Elf32_Verdaux; ++ ++typedef struct { ++ Elf64_Word vda_name; ++ Elf64_Word vda_next; ++} Elf64_Verdaux; ++ ++ ++ ++ ++typedef struct { ++ Elf32_Half vn_version; ++ Elf32_Half vn_cnt; ++ Elf32_Word vn_file; ++ Elf32_Word vn_aux; ++ Elf32_Word vn_next; ++} Elf32_Verneed; ++ ++typedef struct { ++ Elf64_Half vn_version; ++ Elf64_Half vn_cnt; ++ Elf64_Word vn_file; ++ Elf64_Word vn_aux; ++ Elf64_Word vn_next; ++} Elf64_Verneed; ++ ++ ++ ++#define VER_NEED_NONE 0 ++#define VER_NEED_CURRENT 1 ++#define VER_NEED_NUM 2 ++ ++ ++ ++typedef struct { ++ Elf32_Word vna_hash; ++ Elf32_Half vna_flags; ++ Elf32_Half vna_other; ++ Elf32_Word vna_name; ++ Elf32_Word vna_next; ++} Elf32_Vernaux; ++ ++typedef struct { ++ Elf64_Word vna_hash; ++ Elf64_Half vna_flags; ++ Elf64_Half vna_other; ++ Elf64_Word vna_name; ++ Elf64_Word vna_next; ++} Elf64_Vernaux; ++ ++ ++ ++#define VER_FLG_WEAK 0x2 ++ ++ ++ ++typedef struct { ++ uint32_t a_type; ++ union { ++ uint32_t a_val; ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct { ++ uint64_t a_type; ++ union { ++ uint64_t a_val; ++ } a_un; ++} Elf64_auxv_t; ++ ++ ++ ++#define AT_NULL 0 ++#define AT_IGNORE 1 ++#define AT_EXECFD 2 ++#define AT_PHDR 3 ++#define AT_PHENT 4 ++#define AT_PHNUM 5 ++#define AT_PAGESZ 6 ++#define AT_BASE 7 ++#define AT_FLAGS 8 ++#define AT_ENTRY 9 ++#define AT_NOTELF 10 ++#define AT_UID 11 ++#define AT_EUID 12 ++#define AT_GID 13 ++#define AT_EGID 14 ++#define AT_CLKTCK 17 ++ ++ ++#define AT_PLATFORM 15 ++#define AT_HWCAP 16 ++ ++ ++ ++ ++#define AT_FPUCW 18 ++ ++ ++#define AT_DCACHEBSIZE 19 ++#define AT_ICACHEBSIZE 20 ++#define AT_UCACHEBSIZE 21 ++ ++ ++ ++#define AT_IGNOREPPC 22 ++ ++#define AT_SECURE 23 ++ ++#define AT_BASE_PLATFORM 24 ++ ++#define AT_RANDOM 25 ++ ++#define AT_HWCAP2 26 ++ ++#define AT_EXECFN 31 ++ ++ ++ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++ ++ ++#define AT_L1I_CACHESHAPE 34 ++#define AT_L1D_CACHESHAPE 35 ++#define AT_L2_CACHESHAPE 36 ++#define AT_L3_CACHESHAPE 37 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Word n_namesz; ++ Elf32_Word n_descsz; ++ Elf32_Word n_type; ++} Elf32_Nhdr; ++ ++typedef struct { ++ Elf64_Word n_namesz; ++ Elf64_Word n_descsz; ++ Elf64_Word n_type; ++} Elf64_Nhdr; ++ ++ ++ ++ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++ ++ ++ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++#define NT_GNU_ABI_TAG 1 ++#define ELF_NOTE_ABI NT_GNU_ABI_TAG ++ ++ ++ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++#define NT_GNU_BUILD_ID 3 ++#define NT_GNU_GOLD_VERSION 4 ++ ++ ++ ++typedef struct { ++ Elf32_Xword m_value; ++ Elf32_Word m_info; ++ Elf32_Word m_poffset; ++ Elf32_Half m_repeat; ++ Elf32_Half m_stride; ++} Elf32_Move; ++ ++typedef struct { ++ Elf64_Xword m_value; ++ Elf64_Xword m_info; ++ Elf64_Xword m_poffset; ++ Elf64_Half m_repeat; ++ Elf64_Half m_stride; ++} Elf64_Move; ++ ++ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++#define EF_CPU32 0x00810000 ++ ++#define R_68K_NONE 0 ++#define R_68K_32 1 ++#define R_68K_16 2 ++#define R_68K_8 3 ++#define R_68K_PC32 4 ++#define R_68K_PC16 5 ++#define R_68K_PC8 6 ++#define R_68K_GOT32 7 ++#define R_68K_GOT16 8 ++#define R_68K_GOT8 9 ++#define R_68K_GOT32O 10 ++#define R_68K_GOT16O 11 ++#define R_68K_GOT8O 12 ++#define R_68K_PLT32 13 ++#define R_68K_PLT16 14 ++#define R_68K_PLT8 15 ++#define R_68K_PLT32O 16 ++#define R_68K_PLT16O 17 ++#define R_68K_PLT8O 18 ++#define R_68K_COPY 19 ++#define R_68K_GLOB_DAT 20 ++#define R_68K_JMP_SLOT 21 ++#define R_68K_RELATIVE 22 ++#define R_68K_NUM 23 ++ ++#define R_386_NONE 0 ++#define R_386_32 1 ++#define R_386_PC32 2 ++#define R_386_GOT32 3 ++#define R_386_PLT32 4 ++#define R_386_COPY 5 ++#define R_386_GLOB_DAT 6 ++#define R_386_JMP_SLOT 7 ++#define R_386_RELATIVE 8 ++#define R_386_GOTOFF 9 ++#define R_386_GOTPC 10 ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 ++#define R_386_TLS_IE 15 ++#define R_386_TLS_GOTIE 16 ++#define R_386_TLS_LE 17 ++#define R_386_TLS_GD 18 ++#define R_386_TLS_LDM 19 ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 ++#define R_386_TLS_GD_PUSH 25 ++#define R_386_TLS_GD_CALL 26 ++#define R_386_TLS_GD_POP 27 ++#define R_386_TLS_LDM_32 28 ++#define R_386_TLS_LDM_PUSH 29 ++#define R_386_TLS_LDM_CALL 30 ++#define R_386_TLS_LDM_POP 31 ++#define R_386_TLS_LDO_32 32 ++#define R_386_TLS_IE_32 33 ++#define R_386_TLS_LE_32 34 ++#define R_386_TLS_DTPMOD32 35 ++#define R_386_TLS_DTPOFF32 36 ++#define R_386_TLS_TPOFF32 37 ++#define R_386_SIZE32 38 ++#define R_386_TLS_GOTDESC 39 ++#define R_386_TLS_DESC_CALL 40 ++#define R_386_TLS_DESC 41 ++#define R_386_IRELATIVE 42 ++#define R_386_NUM 43 ++ ++ ++ ++ ++ ++#define STT_SPARC_REGISTER 13 ++ ++ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 ++#define EF_SPARC_SUN_US1 0x000200 ++#define EF_SPARC_HAL_R1 0x000400 ++#define EF_SPARC_SUN_US3 0x000800 ++ ++ ++ ++#define R_SPARC_NONE 0 ++#define R_SPARC_8 1 ++#define R_SPARC_16 2 ++#define R_SPARC_32 3 ++#define R_SPARC_DISP8 4 ++#define R_SPARC_DISP16 5 ++#define R_SPARC_DISP32 6 ++#define R_SPARC_WDISP30 7 ++#define R_SPARC_WDISP22 8 ++#define R_SPARC_HI22 9 ++#define R_SPARC_22 10 ++#define R_SPARC_13 11 ++#define R_SPARC_LO10 12 ++#define R_SPARC_GOT10 13 ++#define R_SPARC_GOT13 14 ++#define R_SPARC_GOT22 15 ++#define R_SPARC_PC10 16 ++#define R_SPARC_PC22 17 ++#define R_SPARC_WPLT30 18 ++#define R_SPARC_COPY 19 ++#define R_SPARC_GLOB_DAT 20 ++#define R_SPARC_JMP_SLOT 21 ++#define R_SPARC_RELATIVE 22 ++#define R_SPARC_UA32 23 ++ ++ ++ ++#define R_SPARC_PLT32 24 ++#define R_SPARC_HIPLT22 25 ++#define R_SPARC_LOPLT10 26 ++#define R_SPARC_PCPLT32 27 ++#define R_SPARC_PCPLT22 28 ++#define R_SPARC_PCPLT10 29 ++#define R_SPARC_10 30 ++#define R_SPARC_11 31 ++#define R_SPARC_64 32 ++#define R_SPARC_OLO10 33 ++#define R_SPARC_HH22 34 ++#define R_SPARC_HM10 35 ++#define R_SPARC_LM22 36 ++#define R_SPARC_PC_HH22 37 ++#define R_SPARC_PC_HM10 38 ++#define R_SPARC_PC_LM22 39 ++#define R_SPARC_WDISP16 40 ++#define R_SPARC_WDISP19 41 ++#define R_SPARC_GLOB_JMP 42 ++#define R_SPARC_7 43 ++#define R_SPARC_5 44 ++#define R_SPARC_6 45 ++#define R_SPARC_DISP64 46 ++#define R_SPARC_PLT64 47 ++#define R_SPARC_HIX22 48 ++#define R_SPARC_LOX10 49 ++#define R_SPARC_H44 50 ++#define R_SPARC_M44 51 ++#define R_SPARC_L44 52 ++#define R_SPARC_REGISTER 53 ++#define R_SPARC_UA64 54 ++#define R_SPARC_UA16 55 ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++#define R_SPARC_GOTDATA_HIX22 80 ++#define R_SPARC_GOTDATA_LOX10 81 ++#define R_SPARC_GOTDATA_OP_HIX22 82 ++#define R_SPARC_GOTDATA_OP_LOX10 83 ++#define R_SPARC_GOTDATA_OP 84 ++#define R_SPARC_H34 85 ++#define R_SPARC_SIZE32 86 ++#define R_SPARC_SIZE64 87 ++#define R_SPARC_GNU_VTINHERIT 250 ++#define R_SPARC_GNU_VTENTRY 251 ++#define R_SPARC_REV32 252 ++ ++#define R_SPARC_NUM 253 ++ ++ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++ ++#define EF_MIPS_NOREORDER 1 ++#define EF_MIPS_PIC 2 ++#define EF_MIPS_CPIC 4 ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 ++ ++ ++ ++#define EF_MIPS_ARCH_1 0x00000000 ++#define EF_MIPS_ARCH_2 0x10000000 ++#define EF_MIPS_ARCH_3 0x20000000 ++#define EF_MIPS_ARCH_4 0x30000000 ++#define EF_MIPS_ARCH_5 0x40000000 ++#define EF_MIPS_ARCH_32 0x50000000 ++#define EF_MIPS_ARCH_64 0x60000000 ++#define EF_MIPS_ARCH_32R2 0x70000000 ++#define EF_MIPS_ARCH_64R2 0x80000000 ++ ++ ++#define E_MIPS_ARCH_1 0x00000000 ++#define E_MIPS_ARCH_2 0x10000000 ++#define E_MIPS_ARCH_3 0x20000000 ++#define E_MIPS_ARCH_4 0x30000000 ++#define E_MIPS_ARCH_5 0x40000000 ++#define E_MIPS_ARCH_32 0x50000000 ++#define E_MIPS_ARCH_64 0x60000000 ++ ++ ++ ++#define SHN_MIPS_ACOMMON 0xff00 ++#define SHN_MIPS_TEXT 0xff01 ++#define SHN_MIPS_DATA 0xff02 ++#define SHN_MIPS_SCOMMON 0xff03 ++#define SHN_MIPS_SUNDEFINED 0xff04 ++ ++ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 ++#define SHT_MIPS_GPTAB 0x70000003 ++#define SHT_MIPS_UCODE 0x70000004 ++#define SHT_MIPS_DEBUG 0x70000005 ++#define SHT_MIPS_REGINFO 0x70000006 ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++ ++ ++#define SHF_MIPS_GPREL 0x10000000 ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++ ++ ++ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_PLT 0x8 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++ ++ ++typedef union { ++ struct { ++ Elf32_Word gt_current_g_value; ++ Elf32_Word gt_unused; ++ } gt_header; ++ struct { ++ Elf32_Word gt_g_value; ++ Elf32_Word gt_bytes; ++ } gt_entry; ++} Elf32_gptab; ++ ++ ++ ++typedef struct { ++ Elf32_Word ri_gprmask; ++ Elf32_Word ri_cprmask[4]; ++ Elf32_Sword ri_gp_value; ++} Elf32_RegInfo; ++ ++ ++ ++typedef struct { ++ unsigned char kind; ++ ++ unsigned char size; ++ Elf32_Section section; ++ ++ Elf32_Word info; ++} Elf_Options; ++ ++ ++ ++#define ODK_NULL 0 ++#define ODK_REGINFO 1 ++#define ODK_EXCEPTIONS 2 ++#define ODK_PAD 3 ++#define ODK_HWPATCH 4 ++#define ODK_FILL 5 ++#define ODK_TAGS 6 ++#define ODK_HWAND 7 ++#define ODK_HWOR 8 ++ ++ ++ ++#define OEX_FPU_MIN 0x1f ++#define OEX_FPU_MAX 0x1f00 ++#define OEX_PAGE0 0x10000 ++#define OEX_SMM 0x20000 ++#define OEX_FPDBUG 0x40000 ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++ ++ ++#define OHW_R4KEOP 0x1 ++#define OHW_R8KPFETCH 0x2 ++#define OHW_R5KEOP 0x4 ++#define OHW_R5KCVTL 0x8 ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++ ++ ++typedef struct { ++ Elf32_Word hwp_flags1; ++ Elf32_Word hwp_flags2; ++} Elf_Options_Hw; ++ ++ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++ ++ ++#define R_MIPS_NONE 0 ++#define R_MIPS_16 1 ++#define R_MIPS_32 2 ++#define R_MIPS_REL32 3 ++#define R_MIPS_26 4 ++#define R_MIPS_HI16 5 ++#define R_MIPS_LO16 6 ++#define R_MIPS_GPREL16 7 ++#define R_MIPS_LITERAL 8 ++#define R_MIPS_GOT16 9 ++#define R_MIPS_PC16 10 ++#define R_MIPS_CALL16 11 ++#define R_MIPS_GPREL32 12 ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++#define R_MIPS_TLS_DTPMOD32 38 ++#define R_MIPS_TLS_DTPREL32 39 ++#define R_MIPS_TLS_DTPMOD64 40 ++#define R_MIPS_TLS_DTPREL64 41 ++#define R_MIPS_TLS_GD 42 ++#define R_MIPS_TLS_LDM 43 ++#define R_MIPS_TLS_DTPREL_HI16 44 ++#define R_MIPS_TLS_DTPREL_LO16 45 ++#define R_MIPS_TLS_GOTTPREL 46 ++#define R_MIPS_TLS_TPREL32 47 ++#define R_MIPS_TLS_TPREL64 48 ++#define R_MIPS_TLS_TPREL_HI16 49 ++#define R_MIPS_TLS_TPREL_LO16 50 ++#define R_MIPS_GLOB_DAT 51 ++#define R_MIPS_COPY 126 ++#define R_MIPS_JUMP_SLOT 127 ++ ++#define R_MIPS_NUM 128 ++ ++ ++ ++#define PT_MIPS_REGINFO 0x70000000 ++#define PT_MIPS_RTPROC 0x70000001 ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 ++#define DT_MIPS_TIME_STAMP 0x70000002 ++#define DT_MIPS_ICHECKSUM 0x70000003 ++#define DT_MIPS_IVERSION 0x70000004 ++#define DT_MIPS_FLAGS 0x70000005 ++#define DT_MIPS_BASE_ADDRESS 0x70000006 ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 ++#define DT_MIPS_LIBLIST 0x70000009 ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a ++#define DT_MIPS_CONFLICTNO 0x7000000b ++#define DT_MIPS_LIBLISTNO 0x70000010 ++#define DT_MIPS_SYMTABNO 0x70000011 ++#define DT_MIPS_UNREFEXTNO 0x70000012 ++#define DT_MIPS_GOTSYM 0x70000013 ++#define DT_MIPS_HIPAGENO 0x70000014 ++#define DT_MIPS_RLD_MAP 0x70000016 ++#define DT_MIPS_DELTA_CLASS 0x70000017 ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 ++ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a ++ ++#define DT_MIPS_DELTA_RELOC 0x7000001b ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c ++ ++#define DT_MIPS_DELTA_SYM 0x7000001d ++ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e ++ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 ++ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 ++ ++#define DT_MIPS_CXX_FLAGS 0x70000022 ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 ++#define DT_MIPS_INTERFACE 0x7000002a ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d ++ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e ++ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f ++#define DT_MIPS_GP_VALUE 0x70000030 ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 ++ ++#define DT_MIPS_PLTGOT 0x70000032 ++ ++#define DT_MIPS_RWPLT 0x70000034 ++#define DT_MIPS_NUM 0x35 ++ ++ ++ ++#define RHF_NONE 0 ++#define RHF_QUICKSTART (1 << 0) ++#define RHF_NOTPOT (1 << 1) ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++ ++ ++typedef struct ++{ ++ Elf32_Word l_name; ++ Elf32_Word l_time_stamp; ++ Elf32_Word l_checksum; ++ Elf32_Word l_version; ++ Elf32_Word l_flags; ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; ++ Elf64_Word l_time_stamp; ++ Elf64_Word l_checksum; ++ Elf64_Word l_version; ++ Elf64_Word l_flags; ++} Elf64_Lib; ++ ++ ++ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) ++#define LL_IGNORE_INT_VER (1 << 1) ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++ ++ ++ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 ++#define EF_PARISC_EXT 0x00020000 ++#define EF_PARISC_LSB 0x00040000 ++#define EF_PARISC_WIDE 0x00080000 ++#define EF_PARISC_NO_KABP 0x00100000 ++ ++#define EF_PARISC_LAZYSWAP 0x00400000 ++#define EF_PARISC_ARCH 0x0000ffff ++ ++ ++ ++#define EFA_PARISC_1_0 0x020b ++#define EFA_PARISC_1_1 0x0210 ++#define EFA_PARISC_2_0 0x0214 ++ ++ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 ++ ++#define SHN_PARISC_HUGE_COMMON 0xff01 ++ ++ ++ ++#define SHT_PARISC_EXT 0x70000000 ++#define SHT_PARISC_UNWIND 0x70000001 ++#define SHT_PARISC_DOC 0x70000002 ++ ++ ++ ++#define SHF_PARISC_SHORT 0x20000000 ++#define SHF_PARISC_HUGE 0x40000000 ++#define SHF_PARISC_SBP 0x80000000 ++ ++ ++ ++#define STT_PARISC_MILLICODE 13 ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++ ++ ++#define R_PARISC_NONE 0 ++#define R_PARISC_DIR32 1 ++#define R_PARISC_DIR21L 2 ++#define R_PARISC_DIR17R 3 ++#define R_PARISC_DIR17F 4 ++#define R_PARISC_DIR14R 6 ++#define R_PARISC_PCREL32 9 ++#define R_PARISC_PCREL21L 10 ++#define R_PARISC_PCREL17R 11 ++#define R_PARISC_PCREL17F 12 ++#define R_PARISC_PCREL14R 14 ++#define R_PARISC_DPREL21L 18 ++#define R_PARISC_DPREL14R 22 ++#define R_PARISC_GPREL21L 26 ++#define R_PARISC_GPREL14R 30 ++#define R_PARISC_LTOFF21L 34 ++#define R_PARISC_LTOFF14R 38 ++#define R_PARISC_SECREL32 41 ++#define R_PARISC_SEGBASE 48 ++#define R_PARISC_SEGREL32 49 ++#define R_PARISC_PLTOFF21L 50 ++#define R_PARISC_PLTOFF14R 54 ++#define R_PARISC_LTOFF_FPTR32 57 ++#define R_PARISC_LTOFF_FPTR21L 58 ++#define R_PARISC_LTOFF_FPTR14R 62 ++#define R_PARISC_FPTR64 64 ++#define R_PARISC_PLABEL32 65 ++#define R_PARISC_PLABEL21L 66 ++#define R_PARISC_PLABEL14R 70 ++#define R_PARISC_PCREL64 72 ++#define R_PARISC_PCREL22F 74 ++#define R_PARISC_PCREL14WR 75 ++#define R_PARISC_PCREL14DR 76 ++#define R_PARISC_PCREL16F 77 ++#define R_PARISC_PCREL16WF 78 ++#define R_PARISC_PCREL16DF 79 ++#define R_PARISC_DIR64 80 ++#define R_PARISC_DIR14WR 83 ++#define R_PARISC_DIR14DR 84 ++#define R_PARISC_DIR16F 85 ++#define R_PARISC_DIR16WF 86 ++#define R_PARISC_DIR16DF 87 ++#define R_PARISC_GPREL64 88 ++#define R_PARISC_GPREL14WR 91 ++#define R_PARISC_GPREL14DR 92 ++#define R_PARISC_GPREL16F 93 ++#define R_PARISC_GPREL16WF 94 ++#define R_PARISC_GPREL16DF 95 ++#define R_PARISC_LTOFF64 96 ++#define R_PARISC_LTOFF14WR 99 ++#define R_PARISC_LTOFF14DR 100 ++#define R_PARISC_LTOFF16F 101 ++#define R_PARISC_LTOFF16WF 102 ++#define R_PARISC_LTOFF16DF 103 ++#define R_PARISC_SECREL64 104 ++#define R_PARISC_SEGREL64 112 ++#define R_PARISC_PLTOFF14WR 115 ++#define R_PARISC_PLTOFF14DR 116 ++#define R_PARISC_PLTOFF16F 117 ++#define R_PARISC_PLTOFF16WF 118 ++#define R_PARISC_PLTOFF16DF 119 ++#define R_PARISC_LTOFF_FPTR64 120 ++#define R_PARISC_LTOFF_FPTR14WR 123 ++#define R_PARISC_LTOFF_FPTR14DR 124 ++#define R_PARISC_LTOFF_FPTR16F 125 ++#define R_PARISC_LTOFF_FPTR16WF 126 ++#define R_PARISC_LTOFF_FPTR16DF 127 ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 ++#define R_PARISC_IPLT 129 ++#define R_PARISC_EPLT 130 ++#define R_PARISC_TPREL32 153 ++#define R_PARISC_TPREL21L 154 ++#define R_PARISC_TPREL14R 158 ++#define R_PARISC_LTOFF_TP21L 162 ++#define R_PARISC_LTOFF_TP14R 166 ++#define R_PARISC_LTOFF_TP14F 167 ++#define R_PARISC_TPREL64 216 ++#define R_PARISC_TPREL14WR 219 ++#define R_PARISC_TPREL14DR 220 ++#define R_PARISC_TPREL16F 221 ++#define R_PARISC_TPREL16WF 222 ++#define R_PARISC_TPREL16DF 223 ++#define R_PARISC_LTOFF_TP64 224 ++#define R_PARISC_LTOFF_TP14WR 227 ++#define R_PARISC_LTOFF_TP14DR 228 ++#define R_PARISC_LTOFF_TP16F 229 ++#define R_PARISC_LTOFF_TP16WF 230 ++#define R_PARISC_LTOFF_TP16DF 231 ++#define R_PARISC_GNU_VTENTRY 232 ++#define R_PARISC_GNU_VTINHERIT 233 ++#define R_PARISC_TLS_GD21L 234 ++#define R_PARISC_TLS_GD14R 235 ++#define R_PARISC_TLS_GDCALL 236 ++#define R_PARISC_TLS_LDM21L 237 ++#define R_PARISC_TLS_LDM14R 238 ++#define R_PARISC_TLS_LDMCALL 239 ++#define R_PARISC_TLS_LDO21L 240 ++#define R_PARISC_TLS_LDO14R 241 ++#define R_PARISC_TLS_DTPMOD32 242 ++#define R_PARISC_TLS_DTPMOD64 243 ++#define R_PARISC_TLS_DTPOFF32 244 ++#define R_PARISC_TLS_DTPOFF64 245 ++#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L ++#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R ++#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L ++#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R ++#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 ++#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 ++#define R_PARISC_HIRESERVE 255 ++ ++ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++ ++ ++ ++ ++#define EF_ALPHA_32BIT 1 ++#define EF_ALPHA_CANRELAX 2 ++ ++ ++ ++ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++ ++#define STO_ALPHA_NOPV 0x80 ++#define STO_ALPHA_STD_GPLOAD 0x88 ++ ++ ++ ++#define R_ALPHA_NONE 0 ++#define R_ALPHA_REFLONG 1 ++#define R_ALPHA_REFQUAD 2 ++#define R_ALPHA_GPREL32 3 ++#define R_ALPHA_LITERAL 4 ++#define R_ALPHA_LITUSE 5 ++#define R_ALPHA_GPDISP 6 ++#define R_ALPHA_BRADDR 7 ++#define R_ALPHA_HINT 8 ++#define R_ALPHA_SREL16 9 ++#define R_ALPHA_SREL32 10 ++#define R_ALPHA_SREL64 11 ++#define R_ALPHA_GPRELHIGH 17 ++#define R_ALPHA_GPRELLOW 18 ++#define R_ALPHA_GPREL16 19 ++#define R_ALPHA_COPY 24 ++#define R_ALPHA_GLOB_DAT 25 ++#define R_ALPHA_JMP_SLOT 26 ++#define R_ALPHA_RELATIVE 27 ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++ ++#define R_ALPHA_NUM 46 ++ ++ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++ ++#define DT_ALPHA_PLTRO (DT_LOPROC + 0) ++#define DT_ALPHA_NUM 1 ++ ++ ++ ++ ++#define EF_PPC_EMB 0x80000000 ++ ++ ++#define EF_PPC_RELOCATABLE 0x00010000 ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 ++ ++ ++ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 ++#define R_PPC_ADDR24 2 ++#define R_PPC_ADDR16 3 ++#define R_PPC_ADDR16_LO 4 ++#define R_PPC_ADDR16_HI 5 ++#define R_PPC_ADDR16_HA 6 ++#define R_PPC_ADDR14 7 ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 ++#define R_PPC_REL14 11 ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++ ++#define R_PPC_TLS 67 ++#define R_PPC_DTPMOD32 68 ++#define R_PPC_TPREL16 69 ++#define R_PPC_TPREL16_LO 70 ++#define R_PPC_TPREL16_HI 71 ++#define R_PPC_TPREL16_HA 72 ++#define R_PPC_TPREL32 73 ++#define R_PPC_DTPREL16 74 ++#define R_PPC_DTPREL16_LO 75 ++#define R_PPC_DTPREL16_HI 76 ++#define R_PPC_DTPREL16_HA 77 ++#define R_PPC_DTPREL32 78 ++#define R_PPC_GOT_TLSGD16 79 ++#define R_PPC_GOT_TLSGD16_LO 80 ++#define R_PPC_GOT_TLSGD16_HI 81 ++#define R_PPC_GOT_TLSGD16_HA 82 ++#define R_PPC_GOT_TLSLD16 83 ++#define R_PPC_GOT_TLSLD16_LO 84 ++#define R_PPC_GOT_TLSLD16_HI 85 ++#define R_PPC_GOT_TLSLD16_HA 86 ++#define R_PPC_GOT_TPREL16 87 ++#define R_PPC_GOT_TPREL16_LO 88 ++#define R_PPC_GOT_TPREL16_HI 89 ++#define R_PPC_GOT_TPREL16_HA 90 ++#define R_PPC_GOT_DTPREL16 91 ++#define R_PPC_GOT_DTPREL16_LO 92 ++#define R_PPC_GOT_DTPREL16_HI 93 ++#define R_PPC_GOT_DTPREL16_HA 94 ++ ++ ++ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 ++ ++ ++#define R_PPC_DIAB_SDA21_LO 180 ++#define R_PPC_DIAB_SDA21_HI 181 ++#define R_PPC_DIAB_SDA21_HA 182 ++#define R_PPC_DIAB_RELSDA_LO 183 ++#define R_PPC_DIAB_RELSDA_HI 184 ++#define R_PPC_DIAB_RELSDA_HA 185 ++ ++ ++#define R_PPC_IRELATIVE 248 ++ ++ ++#define R_PPC_REL16 249 ++#define R_PPC_REL16_LO 250 ++#define R_PPC_REL16_HI 251 ++#define R_PPC_REL16_HA 252 ++ ++ ++ ++#define R_PPC_TOC16 255 ++ ++ ++#define DT_PPC_GOT (DT_LOPROC + 0) ++#define DT_PPC_NUM 1 ++ ++ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 ++#define R_PPC64_ADDR24 R_PPC_ADDR24 ++#define R_PPC64_ADDR16 R_PPC_ADDR16 ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA ++#define R_PPC64_ADDR14 R_PPC_ADDR14 ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 ++#define R_PPC64_REL14 R_PPC_REL14 ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 ++#define R_PPC64_ADDR64 38 ++#define R_PPC64_ADDR16_HIGHER 39 ++#define R_PPC64_ADDR16_HIGHERA 40 ++#define R_PPC64_ADDR16_HIGHEST 41 ++#define R_PPC64_ADDR16_HIGHESTA 42 ++#define R_PPC64_UADDR64 43 ++#define R_PPC64_REL64 44 ++#define R_PPC64_PLT64 45 ++#define R_PPC64_PLTREL64 46 ++#define R_PPC64_TOC16 47 ++#define R_PPC64_TOC16_LO 48 ++#define R_PPC64_TOC16_HI 49 ++#define R_PPC64_TOC16_HA 50 ++#define R_PPC64_TOC 51 ++#define R_PPC64_PLTGOT16 52 ++#define R_PPC64_PLTGOT16_LO 53 ++#define R_PPC64_PLTGOT16_HI 54 ++#define R_PPC64_PLTGOT16_HA 55 ++ ++#define R_PPC64_ADDR16_DS 56 ++#define R_PPC64_ADDR16_LO_DS 57 ++#define R_PPC64_GOT16_DS 58 ++#define R_PPC64_GOT16_LO_DS 59 ++#define R_PPC64_PLT16_LO_DS 60 ++#define R_PPC64_SECTOFF_DS 61 ++#define R_PPC64_SECTOFF_LO_DS 62 ++#define R_PPC64_TOC16_DS 63 ++#define R_PPC64_TOC16_LO_DS 64 ++#define R_PPC64_PLTGOT16_DS 65 ++#define R_PPC64_PLTGOT16_LO_DS 66 ++ ++ ++#define R_PPC64_TLS 67 ++#define R_PPC64_DTPMOD64 68 ++#define R_PPC64_TPREL16 69 ++#define R_PPC64_TPREL16_LO 70 ++#define R_PPC64_TPREL16_HI 71 ++#define R_PPC64_TPREL16_HA 72 ++#define R_PPC64_TPREL64 73 ++#define R_PPC64_DTPREL16 74 ++#define R_PPC64_DTPREL16_LO 75 ++#define R_PPC64_DTPREL16_HI 76 ++#define R_PPC64_DTPREL16_HA 77 ++#define R_PPC64_DTPREL64 78 ++#define R_PPC64_GOT_TLSGD16 79 ++#define R_PPC64_GOT_TLSGD16_LO 80 ++#define R_PPC64_GOT_TLSGD16_HI 81 ++#define R_PPC64_GOT_TLSGD16_HA 82 ++#define R_PPC64_GOT_TLSLD16 83 ++#define R_PPC64_GOT_TLSLD16_LO 84 ++#define R_PPC64_GOT_TLSLD16_HI 85 ++#define R_PPC64_GOT_TLSLD16_HA 86 ++#define R_PPC64_GOT_TPREL16_DS 87 ++#define R_PPC64_GOT_TPREL16_LO_DS 88 ++#define R_PPC64_GOT_TPREL16_HI 89 ++#define R_PPC64_GOT_TPREL16_HA 90 ++#define R_PPC64_GOT_DTPREL16_DS 91 ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 ++#define R_PPC64_GOT_DTPREL16_HI 93 ++#define R_PPC64_GOT_DTPREL16_HA 94 ++#define R_PPC64_TPREL16_DS 95 ++#define R_PPC64_TPREL16_LO_DS 96 ++#define R_PPC64_TPREL16_HIGHER 97 ++#define R_PPC64_TPREL16_HIGHERA 98 ++#define R_PPC64_TPREL16_HIGHEST 99 ++#define R_PPC64_TPREL16_HIGHESTA 100 ++#define R_PPC64_DTPREL16_DS 101 ++#define R_PPC64_DTPREL16_LO_DS 102 ++#define R_PPC64_DTPREL16_HIGHER 103 ++#define R_PPC64_DTPREL16_HIGHERA 104 ++#define R_PPC64_DTPREL16_HIGHEST 105 ++#define R_PPC64_DTPREL16_HIGHESTA 106 ++ ++ ++#define R_PPC64_JMP_IREL 247 ++#define R_PPC64_IRELATIVE 248 ++#define R_PPC64_REL16 249 ++#define R_PPC64_REL16_LO 250 ++#define R_PPC64_REL16_HI 251 ++#define R_PPC64_REL16_HA 252 ++ ++ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_OPD (DT_LOPROC + 1) ++#define DT_PPC64_OPDSZ (DT_LOPROC + 2) ++#define DT_PPC64_NUM 3 ++ ++ ++ ++ ++ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++#define EF_ARM_SOFT_FLOAT 0x200 ++#define EF_ARM_VFP_FLOAT 0x400 ++#define EF_ARM_MAVERICK_FLOAT 0x800 ++ ++#define EF_ARM_ABI_FLOAT_SOFT 0x200 ++#define EF_ARM_ABI_FLOAT_HARD 0x400 ++ ++ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++ ++#define EF_ARM_BE8 0x00800000 ++#define EF_ARM_LE8 0x00400000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++#define EF_ARM_EABI_VER3 0x03000000 ++#define EF_ARM_EABI_VER4 0x04000000 ++#define EF_ARM_EABI_VER5 0x05000000 ++ ++ ++#define STT_ARM_TFUNC STT_LOPROC ++#define STT_ARM_16BIT STT_HIPROC ++ ++ ++#define SHF_ARM_ENTRYSECT 0x10000000 ++#define SHF_ARM_COMDEF 0x80000000 ++ ++ ++ ++#define PF_ARM_SB 0x10000000 ++ ++#define PF_ARM_PI 0x20000000 ++#define PF_ARM_ABS 0x40000000 ++ ++ ++#define PT_ARM_EXIDX (PT_LOPROC + 1) ++ ++ ++#define SHT_ARM_EXIDX (SHT_LOPROC + 1) ++#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) ++#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) ++ ++ ++#define R_AARCH64_NONE 0 ++#define R_AARCH64_ABS64 257 ++#define R_AARCH64_ABS32 258 ++#define R_AARCH64_COPY 1024 ++#define R_AARCH64_GLOB_DAT 1025 ++#define R_AARCH64_JUMP_SLOT 1026 ++#define R_AARCH64_RELATIVE 1027 ++#define R_AARCH64_TLS_DTPMOD64 1028 ++#define R_AARCH64_TLS_DTPREL64 1029 ++#define R_AARCH64_TLS_TPREL64 1030 ++#define R_AARCH64_TLSDESC 1031 ++ ++ ++#define R_ARM_NONE 0 ++#define R_ARM_PC24 1 ++#define R_ARM_ABS32 2 ++#define R_ARM_REL32 3 ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 ++#define R_ARM_ABS12 6 ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_TLS_DESC 13 ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_TLS_DTPMOD32 17 ++#define R_ARM_TLS_DTPOFF32 18 ++#define R_ARM_TLS_TPOFF32 19 ++#define R_ARM_COPY 20 ++#define R_ARM_GLOB_DAT 21 ++#define R_ARM_JUMP_SLOT 22 ++#define R_ARM_RELATIVE 23 ++#define R_ARM_GOTOFF 24 ++#define R_ARM_GOTPC 25 ++#define R_ARM_GOT32 26 ++#define R_ARM_PLT32 27 ++#define R_ARM_CALL 28 ++#define R_ARM_JUMP24 29 ++#define R_ARM_THM_JUMP24 30 ++#define R_ARM_BASE_ABS 31 ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_TARGET1 38 ++#define R_ARM_SBREL31 39 ++#define R_ARM_V4BX 40 ++#define R_ARM_TARGET2 41 ++#define R_ARM_PREL31 42 ++#define R_ARM_MOVW_ABS_NC 43 ++#define R_ARM_MOVT_ABS 44 ++#define R_ARM_MOVW_PREL_NC 45 ++#define R_ARM_MOVT_PREL 46 ++#define R_ARM_THM_MOVW_ABS_NC 47 ++#define R_ARM_THM_MOVT_ABS 48 ++#define R_ARM_THM_MOVW_PREL_NC 49 ++#define R_ARM_THM_MOVT_PREL 50 ++#define R_ARM_THM_JUMP19 51 ++#define R_ARM_THM_JUMP6 52 ++#define R_ARM_THM_ALU_PREL_11_0 53 ++#define R_ARM_THM_PC12 54 ++#define R_ARM_ABS32_NOI 55 ++#define R_ARM_REL32_NOI 56 ++#define R_ARM_ALU_PC_G0_NC 57 ++#define R_ARM_ALU_PC_G0 58 ++#define R_ARM_ALU_PC_G1_NC 59 ++#define R_ARM_ALU_PC_G1 60 ++#define R_ARM_ALU_PC_G2 61 ++#define R_ARM_LDR_PC_G1 62 ++#define R_ARM_LDR_PC_G2 63 ++#define R_ARM_LDRS_PC_G0 64 ++#define R_ARM_LDRS_PC_G1 65 ++#define R_ARM_LDRS_PC_G2 66 ++#define R_ARM_LDC_PC_G0 67 ++#define R_ARM_LDC_PC_G1 68 ++#define R_ARM_LDC_PC_G2 69 ++#define R_ARM_ALU_SB_G0_NC 70 ++#define R_ARM_ALU_SB_G0 71 ++#define R_ARM_ALU_SB_G1_NC 72 ++#define R_ARM_ALU_SB_G1 73 ++#define R_ARM_ALU_SB_G2 74 ++#define R_ARM_LDR_SB_G0 75 ++#define R_ARM_LDR_SB_G1 76 ++#define R_ARM_LDR_SB_G2 77 ++#define R_ARM_LDRS_SB_G0 78 ++#define R_ARM_LDRS_SB_G1 79 ++#define R_ARM_LDRS_SB_G2 80 ++#define R_ARM_LDC_SB_G0 81 ++#define R_ARM_LDC_SB_G1 82 ++#define R_ARM_LDC_SB_G2 83 ++#define R_ARM_MOVW_BREL_NC 84 ++#define R_ARM_MOVT_BREL 85 ++#define R_ARM_MOVW_BREL 86 ++#define R_ARM_THM_MOVW_BREL_NC 87 ++#define R_ARM_THM_MOVT_BREL 88 ++#define R_ARM_THM_MOVW_BREL 89 ++#define R_ARM_TLS_GOTDESC 90 ++#define R_ARM_TLS_CALL 91 ++#define R_ARM_TLS_DESCSEQ 92 ++#define R_ARM_THM_TLS_CALL 93 ++#define R_ARM_PLT32_ABS 94 ++#define R_ARM_GOT_ABS 95 ++#define R_ARM_GOT_PREL 96 ++#define R_ARM_GOT_BREL12 97 ++#define R_ARM_GOTOFF12 98 ++#define R_ARM_GOTRELAX 99 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 ++#define R_ARM_THM_PC9 103 ++#define R_ARM_TLS_GD32 104 ++ ++#define R_ARM_TLS_LDM32 105 ++ ++#define R_ARM_TLS_LDO32 106 ++ ++#define R_ARM_TLS_IE32 107 ++ ++#define R_ARM_TLS_LE32 108 ++#define R_ARM_TLS_LDO12 109 ++#define R_ARM_TLS_LE12 110 ++#define R_ARM_TLS_IE12GP 111 ++#define R_ARM_ME_TOO 128 ++#define R_ARM_THM_TLS_DESCSEQ 129 ++#define R_ARM_THM_TLS_DESCSEQ16 129 ++#define R_ARM_THM_TLS_DESCSEQ32 130 ++#define R_ARM_THM_GOT_BREL12 131 ++#define R_ARM_IRELATIVE 160 ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++ ++#define R_ARM_NUM 256 ++ ++ ++ ++ ++#define EF_IA_64_MASKOS 0x0000000f ++#define EF_IA_64_ABI64 0x00000010 ++#define EF_IA_64_ARCH 0xff000000 ++ ++ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) ++#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) ++#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) ++#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) ++ ++ ++#define PF_IA_64_NORECOV 0x80000000 ++ ++ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) ++ ++ ++#define SHF_IA_64_SHORT 0x10000000 ++#define SHF_IA_64_NORECOV 0x20000000 ++ ++ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++ ++#define R_IA64_NONE 0x00 ++#define R_IA64_IMM14 0x21 ++#define R_IA64_IMM22 0x22 ++#define R_IA64_IMM64 0x23 ++#define R_IA64_DIR32MSB 0x24 ++#define R_IA64_DIR32LSB 0x25 ++#define R_IA64_DIR64MSB 0x26 ++#define R_IA64_DIR64LSB 0x27 ++#define R_IA64_GPREL22 0x2a ++#define R_IA64_GPREL64I 0x2b ++#define R_IA64_GPREL32MSB 0x2c ++#define R_IA64_GPREL32LSB 0x2d ++#define R_IA64_GPREL64MSB 0x2e ++#define R_IA64_GPREL64LSB 0x2f ++#define R_IA64_LTOFF22 0x32 ++#define R_IA64_LTOFF64I 0x33 ++#define R_IA64_PLTOFF22 0x3a ++#define R_IA64_PLTOFF64I 0x3b ++#define R_IA64_PLTOFF64MSB 0x3e ++#define R_IA64_PLTOFF64LSB 0x3f ++#define R_IA64_FPTR64I 0x43 ++#define R_IA64_FPTR32MSB 0x44 ++#define R_IA64_FPTR32LSB 0x45 ++#define R_IA64_FPTR64MSB 0x46 ++#define R_IA64_FPTR64LSB 0x47 ++#define R_IA64_PCREL60B 0x48 ++#define R_IA64_PCREL21B 0x49 ++#define R_IA64_PCREL21M 0x4a ++#define R_IA64_PCREL21F 0x4b ++#define R_IA64_PCREL32MSB 0x4c ++#define R_IA64_PCREL32LSB 0x4d ++#define R_IA64_PCREL64MSB 0x4e ++#define R_IA64_PCREL64LSB 0x4f ++#define R_IA64_LTOFF_FPTR22 0x52 ++#define R_IA64_LTOFF_FPTR64I 0x53 ++#define R_IA64_LTOFF_FPTR32MSB 0x54 ++#define R_IA64_LTOFF_FPTR32LSB 0x55 ++#define R_IA64_LTOFF_FPTR64MSB 0x56 ++#define R_IA64_LTOFF_FPTR64LSB 0x57 ++#define R_IA64_SEGREL32MSB 0x5c ++#define R_IA64_SEGREL32LSB 0x5d ++#define R_IA64_SEGREL64MSB 0x5e ++#define R_IA64_SEGREL64LSB 0x5f ++#define R_IA64_SECREL32MSB 0x64 ++#define R_IA64_SECREL32LSB 0x65 ++#define R_IA64_SECREL64MSB 0x66 ++#define R_IA64_SECREL64LSB 0x67 ++#define R_IA64_REL32MSB 0x6c ++#define R_IA64_REL32LSB 0x6d ++#define R_IA64_REL64MSB 0x6e ++#define R_IA64_REL64LSB 0x6f ++#define R_IA64_LTV32MSB 0x74 ++#define R_IA64_LTV32LSB 0x75 ++#define R_IA64_LTV64MSB 0x76 ++#define R_IA64_LTV64LSB 0x77 ++#define R_IA64_PCREL21BI 0x79 ++#define R_IA64_PCREL22 0x7a ++#define R_IA64_PCREL64I 0x7b ++#define R_IA64_IPLTMSB 0x80 ++#define R_IA64_IPLTLSB 0x81 ++#define R_IA64_COPY 0x84 ++#define R_IA64_SUB 0x85 ++#define R_IA64_LTOFF22X 0x86 ++#define R_IA64_LDXMOV 0x87 ++#define R_IA64_TPREL14 0x91 ++#define R_IA64_TPREL22 0x92 ++#define R_IA64_TPREL64I 0x93 ++#define R_IA64_TPREL64MSB 0x96 ++#define R_IA64_TPREL64LSB 0x97 ++#define R_IA64_LTOFF_TPREL22 0x9a ++#define R_IA64_DTPMOD64MSB 0xa6 ++#define R_IA64_DTPMOD64LSB 0xa7 ++#define R_IA64_LTOFF_DTPMOD22 0xaa ++#define R_IA64_DTPREL14 0xb1 ++#define R_IA64_DTPREL22 0xb2 ++#define R_IA64_DTPREL64I 0xb3 ++#define R_IA64_DTPREL32MSB 0xb4 ++#define R_IA64_DTPREL32LSB 0xb5 ++#define R_IA64_DTPREL64MSB 0xb6 ++#define R_IA64_DTPREL64LSB 0xb7 ++#define R_IA64_LTOFF_DTPREL22 0xba ++ ++ ++ ++ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++ ++#define R_SH_NUM 256 ++ ++ ++ ++#define R_390_NONE 0 ++#define R_390_8 1 ++#define R_390_12 2 ++#define R_390_16 3 ++#define R_390_32 4 ++#define R_390_PC32 5 ++#define R_390_GOT12 6 ++#define R_390_GOT32 7 ++#define R_390_PLT32 8 ++#define R_390_COPY 9 ++#define R_390_GLOB_DAT 10 ++#define R_390_JMP_SLOT 11 ++#define R_390_RELATIVE 12 ++#define R_390_GOTOFF32 13 ++#define R_390_GOTPC 14 ++#define R_390_GOT16 15 ++#define R_390_PC16 16 ++#define R_390_PC16DBL 17 ++#define R_390_PLT16DBL 18 ++#define R_390_PC32DBL 19 ++#define R_390_PLT32DBL 20 ++#define R_390_GOTPCDBL 21 ++#define R_390_64 22 ++#define R_390_PC64 23 ++#define R_390_GOT64 24 ++#define R_390_PLT64 25 ++#define R_390_GOTENT 26 ++#define R_390_GOTOFF16 27 ++#define R_390_GOTOFF64 28 ++#define R_390_GOTPLT12 29 ++#define R_390_GOTPLT16 30 ++#define R_390_GOTPLT32 31 ++#define R_390_GOTPLT64 32 ++#define R_390_GOTPLTENT 33 ++#define R_390_PLTOFF16 34 ++#define R_390_PLTOFF32 35 ++#define R_390_PLTOFF64 36 ++#define R_390_TLS_LOAD 37 ++#define R_390_TLS_GDCALL 38 ++ ++#define R_390_TLS_LDCALL 39 ++ ++#define R_390_TLS_GD32 40 ++ ++#define R_390_TLS_GD64 41 ++ ++#define R_390_TLS_GOTIE12 42 ++ ++#define R_390_TLS_GOTIE32 43 ++ ++#define R_390_TLS_GOTIE64 44 ++ ++#define R_390_TLS_LDM32 45 ++ ++#define R_390_TLS_LDM64 46 ++ ++#define R_390_TLS_IE32 47 ++ ++#define R_390_TLS_IE64 48 ++ ++#define R_390_TLS_IEENT 49 ++ ++#define R_390_TLS_LE32 50 ++ ++#define R_390_TLS_LE64 51 ++ ++#define R_390_TLS_LDO32 52 ++ ++#define R_390_TLS_LDO64 53 ++ ++#define R_390_TLS_DTPMOD 54 ++#define R_390_TLS_DTPOFF 55 ++#define R_390_TLS_TPOFF 56 ++ ++#define R_390_20 57 ++#define R_390_GOT20 58 ++#define R_390_GOTPLT20 59 ++#define R_390_TLS_GOTIE20 60 ++ ++ ++#define R_390_NUM 61 ++ ++ ++ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++ ++ ++#define R_X86_64_NONE 0 ++#define R_X86_64_64 1 ++#define R_X86_64_PC32 2 ++#define R_X86_64_GOT32 3 ++#define R_X86_64_PLT32 4 ++#define R_X86_64_COPY 5 ++#define R_X86_64_GLOB_DAT 6 ++#define R_X86_64_JUMP_SLOT 7 ++#define R_X86_64_RELATIVE 8 ++#define R_X86_64_GOTPCREL 9 ++ ++#define R_X86_64_32 10 ++#define R_X86_64_32S 11 ++#define R_X86_64_16 12 ++#define R_X86_64_PC16 13 ++#define R_X86_64_8 14 ++#define R_X86_64_PC8 15 ++#define R_X86_64_DTPMOD64 16 ++#define R_X86_64_DTPOFF64 17 ++#define R_X86_64_TPOFF64 18 ++#define R_X86_64_TLSGD 19 ++ ++#define R_X86_64_TLSLD 20 ++ ++#define R_X86_64_DTPOFF32 21 ++#define R_X86_64_GOTTPOFF 22 ++ ++#define R_X86_64_TPOFF32 23 ++#define R_X86_64_PC64 24 ++#define R_X86_64_GOTOFF64 25 ++#define R_X86_64_GOTPC32 26 ++#define R_X86_64_GOT64 27 ++#define R_X86_64_GOTPCREL64 28 ++#define R_X86_64_GOTPC64 29 ++#define R_X86_64_GOTPLT64 30 ++#define R_X86_64_PLTOFF64 31 ++#define R_X86_64_SIZE32 32 ++#define R_X86_64_SIZE64 33 ++ ++#define R_X86_64_GOTPC32_TLSDESC 34 ++#define R_X86_64_TLSDESC_CALL 35 ++ ++#define R_X86_64_TLSDESC 36 ++#define R_X86_64_IRELATIVE 37 ++#define R_X86_64_RELATIVE64 38 ++#define R_X86_64_NUM 39 ++ ++ ++ ++#define R_MN10300_NONE 0 ++#define R_MN10300_32 1 ++#define R_MN10300_16 2 ++#define R_MN10300_8 3 ++#define R_MN10300_PCREL32 4 ++#define R_MN10300_PCREL16 5 ++#define R_MN10300_PCREL8 6 ++#define R_MN10300_GNU_VTINHERIT 7 ++#define R_MN10300_GNU_VTENTRY 8 ++#define R_MN10300_24 9 ++#define R_MN10300_GOTPC32 10 ++#define R_MN10300_GOTPC16 11 ++#define R_MN10300_GOTOFF32 12 ++#define R_MN10300_GOTOFF24 13 ++#define R_MN10300_GOTOFF16 14 ++#define R_MN10300_PLT32 15 ++#define R_MN10300_PLT16 16 ++#define R_MN10300_GOT32 17 ++#define R_MN10300_GOT24 18 ++#define R_MN10300_GOT16 19 ++#define R_MN10300_COPY 20 ++#define R_MN10300_GLOB_DAT 21 ++#define R_MN10300_JMP_SLOT 22 ++#define R_MN10300_RELATIVE 23 ++ ++#define R_MN10300_NUM 24 ++ ++ ++ ++#define R_M32R_NONE 0 ++#define R_M32R_16 1 ++#define R_M32R_32 2 ++#define R_M32R_24 3 ++#define R_M32R_10_PCREL 4 ++#define R_M32R_18_PCREL 5 ++#define R_M32R_26_PCREL 6 ++#define R_M32R_HI16_ULO 7 ++#define R_M32R_HI16_SLO 8 ++#define R_M32R_LO16 9 ++#define R_M32R_SDA16 10 ++#define R_M32R_GNU_VTINHERIT 11 ++#define R_M32R_GNU_VTENTRY 12 ++ ++#define R_M32R_16_RELA 33 ++#define R_M32R_32_RELA 34 ++#define R_M32R_24_RELA 35 ++#define R_M32R_10_PCREL_RELA 36 ++#define R_M32R_18_PCREL_RELA 37 ++#define R_M32R_26_PCREL_RELA 38 ++#define R_M32R_HI16_ULO_RELA 39 ++#define R_M32R_HI16_SLO_RELA 40 ++#define R_M32R_LO16_RELA 41 ++#define R_M32R_SDA16_RELA 42 ++#define R_M32R_RELA_GNU_VTINHERIT 43 ++#define R_M32R_RELA_GNU_VTENTRY 44 ++#define R_M32R_REL32 45 ++ ++#define R_M32R_GOT24 48 ++#define R_M32R_26_PLTREL 49 ++#define R_M32R_COPY 50 ++#define R_M32R_GLOB_DAT 51 ++#define R_M32R_JMP_SLOT 52 ++#define R_M32R_RELATIVE 53 ++#define R_M32R_GOTOFF 54 ++#define R_M32R_GOTPC24 55 ++#define R_M32R_GOT16_HI_ULO 56 ++ ++#define R_M32R_GOT16_HI_SLO 57 ++ ++#define R_M32R_GOT16_LO 58 ++#define R_M32R_GOTPC_HI_ULO 59 ++ ++#define R_M32R_GOTPC_HI_SLO 60 ++ ++#define R_M32R_GOTPC_LO 61 ++ ++#define R_M32R_GOTOFF_HI_ULO 62 ++ ++#define R_M32R_GOTOFF_HI_SLO 63 ++ ++#define R_M32R_GOTOFF_LO 64 ++#define R_M32R_NUM 256 ++ ++#define R_MICROBLAZE_NONE 0 ++#define R_MICROBLAZE_32 1 ++#define R_MICROBLAZE_32_PCREL 2 ++#define R_MICROBLAZE_64_PCREL 3 ++#define R_MICROBLAZE_32_PCREL_LO 4 ++#define R_MICROBLAZE_64 5 ++#define R_MICROBLAZE_32_LO 6 ++#define R_MICROBLAZE_SRO32 7 ++#define R_MICROBLAZE_SRW32 8 ++#define R_MICROBLAZE_64_NONE 9 ++#define R_MICROBLAZE_32_SYM_OP_SYM 10 ++#define R_MICROBLAZE_GNU_VTINHERIT 11 ++#define R_MICROBLAZE_GNU_VTENTRY 12 ++#define R_MICROBLAZE_GOTPC_64 13 ++#define R_MICROBLAZE_GOT_64 14 ++#define R_MICROBLAZE_PLT_64 15 ++#define R_MICROBLAZE_REL 16 ++#define R_MICROBLAZE_JUMP_SLOT 17 ++#define R_MICROBLAZE_GLOB_DAT 18 ++#define R_MICROBLAZE_GOTOFF_64 19 ++#define R_MICROBLAZE_GOTOFF_32 20 ++#define R_MICROBLAZE_COPY 21 ++#define R_MICROBLAZE_TLS 22 ++#define R_MICROBLAZE_TLSGD 23 ++#define R_MICROBLAZE_TLSLD 24 ++#define R_MICROBLAZE_TLSDTPMOD32 25 ++#define R_MICROBLAZE_TLSDTPREL32 26 ++#define R_MICROBLAZE_TLSDTPREL64 27 ++#define R_MICROBLAZE_TLSGOTTPREL32 28 ++#define R_MICROBLAZE_TLSTPREL32 29 ++ ++#ifdef __cplusplus ++} ++#endif ++ ++ ++#endif diff --git a/target/linux/patches/3.14.45/sgidefs.patch b/target/linux/patches/3.14.45/sgidefs.patch new file mode 100644 index 000000000..f00a284d9 --- /dev/null +++ b/target/linux/patches/3.14.45/sgidefs.patch @@ -0,0 +1,18 @@ +diff -Nur linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h +--- linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h 2013-11-08 22:01:28.000000000 +0100 +@@ -11,14 +11,6 @@ + #define __ASM_SGIDEFS_H + + /* +- * Using a Linux compiler for building Linux seems logic but not to +- * everybody. +- */ +-#ifndef __linux__ +-#error Use a Linux compiler or give up. +-#endif +- +-/* + * Definitions for the ISA levels + * + * With the introduction of MIPS32 / MIPS64 instruction sets definitions diff --git a/target/linux/patches/3.14.45/sortext.patch b/target/linux/patches/3.14.45/sortext.patch new file mode 100644 index 000000000..8fd4e1d6b --- /dev/null +++ b/target/linux/patches/3.14.45/sortext.patch @@ -0,0 +1,33 @@ +diff -Nur linux-3.12.6.orig/arch/arm/Kconfig linux-3.12.6/arch/arm/Kconfig +--- linux-3.12.6.orig/arch/arm/Kconfig 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/arch/arm/Kconfig 2013-12-28 19:29:33.000000000 +0100 +@@ -6,7 +6,6 @@ + select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST + select ARCH_HAVE_CUSTOM_GPIO_H + select ARCH_WANT_IPC_PARSE_VERSION +- select BUILDTIME_EXTABLE_SORT if MMU + select CLONE_BACKWARDS + select CPU_PM if (SUSPEND || CPU_IDLE) + select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && !CPU_BIG_ENDIAN && MMU +diff -Nur linux-3.12.6.orig/arch/mips/Kconfig linux-3.12.6/arch/mips/Kconfig +--- linux-3.12.6.orig/arch/mips/Kconfig 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/arch/mips/Kconfig 2013-12-28 19:30:06.000000000 +0100 +@@ -35,7 +35,6 @@ + select HAVE_MEMBLOCK_NODE_MAP + select ARCH_DISCARD_MEMBLOCK + select GENERIC_SMP_IDLE_THREAD +- select BUILDTIME_EXTABLE_SORT + select GENERIC_CLOCKEVENTS + select GENERIC_CMOS_UPDATE + select HAVE_MOD_ARCH_SPECIFIC +diff -Nur linux-3.12.6.orig/arch/x86/Kconfig linux-3.12.6/arch/x86/Kconfig +--- linux-3.12.6.orig/arch/x86/Kconfig 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/arch/x86/Kconfig 2013-12-28 19:29:50.000000000 +0100 +@@ -100,7 +100,6 @@ + select GENERIC_SMP_IDLE_THREAD + select ARCH_WANT_IPC_PARSE_VERSION if X86_32 + select HAVE_ARCH_SECCOMP_FILTER +- select BUILDTIME_EXTABLE_SORT + select GENERIC_CMOS_UPDATE + select HAVE_ARCH_SOFT_DIRTY + select CLOCKSOURCE_WATCHDOG diff --git a/target/linux/patches/3.14.45/startup.patch b/target/linux/patches/3.14.45/startup.patch new file mode 100644 index 000000000..d396b75e4 --- /dev/null +++ b/target/linux/patches/3.14.45/startup.patch @@ -0,0 +1,37 @@ +diff -Nur linux-3.13.3.orig/init/main.c linux-3.13.3/init/main.c +--- linux-3.13.3.orig/init/main.c 2014-02-13 23:00:14.000000000 +0100 ++++ linux-3.13.3/init/main.c 2014-02-17 11:35:14.000000000 +0100 +@@ -916,6 +917,8 @@ + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + pr_err("Warning: unable to open an initial console.\n"); + ++ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); ++ + (void) sys_dup(0); + (void) sys_dup(0); + /* +diff -Nur linux-3.13.6.orig/init/initramfs.c linux-3.13.6/init/initramfs.c +--- linux-3.13.6.orig/init/initramfs.c 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/init/initramfs.c 2014-03-15 12:11:31.882731916 +0100 +@@ -622,6 +622,9 @@ + */ + load_default_modules(); + } ++#ifdef CONFIG_DEVTMPFS_MOUNT ++ devtmpfs_mount("dev"); ++#endif + return 0; + } + rootfs_initcall(populate_rootfs); +diff -Nur linux-3.13.6.orig/init/main.c linux-3.13.6/init/main.c +--- linux-3.13.6.orig/init/main.c 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/init/main.c 2014-03-15 12:13:16.459024452 +0100 +@@ -924,7 +924,7 @@ + */ + + if (!ramdisk_execute_command) +- ramdisk_execute_command = "/init"; ++ ramdisk_execute_command = "/sbin/init"; + + if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { + ramdisk_execute_command = NULL; diff --git a/target/linux/patches/3.14.45/wlan-cf.patch b/target/linux/patches/3.14.45/wlan-cf.patch new file mode 100644 index 000000000..fc20759e2 --- /dev/null +++ b/target/linux/patches/3.14.45/wlan-cf.patch @@ -0,0 +1,11 @@ +diff -Nur linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c +--- linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c 2011-05-19 06:06:34.000000000 +0200 ++++ linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c 2011-09-12 02:46:26.987984145 +0200 +@@ -623,6 +623,7 @@ + static struct pcmcia_device_id hostap_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), ++ PCMCIA_DEVICE_MANF_CARD(0x0004, 0x2003), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), + PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), + PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), diff --git a/target/linux/patches/3.14.45/xargs.patch b/target/linux/patches/3.14.45/xargs.patch new file mode 100644 index 000000000..2c7b3df59 --- /dev/null +++ b/target/linux/patches/3.14.45/xargs.patch @@ -0,0 +1,12 @@ +diff -Nur linux-3.12.6.orig/scripts/Makefile.modpost linux-3.12.6/scripts/Makefile.modpost +--- linux-3.12.6.orig/scripts/Makefile.modpost 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/scripts/Makefile.modpost 2014-01-25 14:55:33.000000000 +0100 +@@ -60,7 +60,7 @@ + modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers + + # Step 1), find all modules listed in $(MODVERDIR)/ +-MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs -r grep -h '\.ko$$' | sort -u ++MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs grep -h '\.ko$$' | sort -u + __modules := $(shell $(MODLISTCMD)) + modules := $(patsubst %.o,%.ko, $(wildcard $(__modules:.ko=.o))) + diff --git a/target/linux/patches/3.14.45/yaffs2.patch b/target/linux/patches/3.14.45/yaffs2.patch new file mode 100644 index 000000000..30e4a790e --- /dev/null +++ b/target/linux/patches/3.14.45/yaffs2.patch @@ -0,0 +1,16559 @@ +diff -Nur linux-3.14.43.orig/fs/Kconfig linux-3.14.43/fs/Kconfig +--- linux-3.14.43.orig/fs/Kconfig 2015-05-17 18:54:01.000000000 +0200 ++++ linux-3.14.43/fs/Kconfig 2015-06-15 21:20:48.000000000 +0200 +@@ -189,6 +189,7 @@ + source "fs/befs/Kconfig" + source "fs/bfs/Kconfig" + source "fs/efs/Kconfig" ++source "fs/yaffs2/Kconfig" + source "fs/jffs2/Kconfig" + # UBIFS File system configuration + source "fs/ubifs/Kconfig" +diff -Nur linux-3.14.43.orig/fs/Makefile linux-3.14.43/fs/Makefile +--- linux-3.14.43.orig/fs/Makefile 2015-05-17 18:54:01.000000000 +0200 ++++ linux-3.14.43/fs/Makefile 2015-06-15 21:20:48.000000000 +0200 +@@ -125,3 +125,4 @@ + obj-$(CONFIG_CEPH_FS) += ceph/ + obj-$(CONFIG_PSTORE) += pstore/ + obj-$(CONFIG_EFIVAR_FS) += efivarfs/ ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ +diff -Nur linux-3.14.43.orig/fs/yaffs2/Kconfig linux-3.14.43/fs/yaffs2/Kconfig +--- linux-3.14.43.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/Kconfig 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,171 @@ ++# ++# yaffs file system configurations ++# ++ ++config YAFFS_FS ++ tristate "yaffs2 file system support" ++ default n ++ depends on MTD_BLOCK ++ select YAFFS_YAFFS1 ++ select YAFFS_YAFFS2 ++ help ++ yaffs2, or Yet Another Flash File System, is a file system ++ optimised for NAND Flash chips. ++ ++ To compile the yaffs2 file system support as a module, choose M ++ here: the module will be called yaffs2. ++ ++ If unsure, say N. ++ ++ Further information on yaffs2 is available at ++ . ++ ++config YAFFS_YAFFS1 ++ bool "512 byte / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs1 support -- yaffs for 512 byte / page devices ++ ++ Not needed for 2K-page devices. ++ ++ If unsure, say Y. ++ ++config YAFFS_9BYTE_TAGS ++ bool "Use older-style on-NAND data format with pageStatus byte" ++ depends on YAFFS_YAFFS1 ++ default n ++ help ++ ++ Older-style on-NAND data format has a "pageStatus" byte to record ++ chunk/page state. This byte is zero when the page is discarded. ++ Choose this option if you have existing on-NAND data using this ++ format that you need to continue to support. New data written ++ also uses the older-style format. Note: Use of this option ++ generally requires that MTD's oob layout be adjusted to use the ++ older-style format. See notes on tags formats and MTD versions ++ in yaffs_mtdif1.c. ++ ++ If unsure, say N. ++ ++config YAFFS_DOES_ECC ++ bool "Lets yaffs do its own ECC" ++ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This enables yaffs to use its own ECC functions instead of using ++ the ones from the generic MTD-NAND driver. ++ ++ If unsure, say N. ++ ++config YAFFS_ECC_WRONG_ORDER ++ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" ++ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This makes yaffs_ecc.c use the same ecc byte order as Steven ++ Hill's nand_ecc.c. If not set, then you get the same ecc byte ++ order as SmartMedia. ++ ++ If unsure, say N. ++ ++config YAFFS_YAFFS2 ++ bool "2048 byte (or larger) / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices ++ ++ If unsure, say Y. ++ ++config YAFFS_AUTO_YAFFS2 ++ bool "Autoselect yaffs2 format" ++ depends on YAFFS_YAFFS2 ++ default y ++ help ++ Without this, you need to explicitely use yaffs2 as the file ++ system type. With this, you can say "yaffs" and yaffs or yaffs2 ++ will be used depending on the device page size (yaffs on ++ 512-byte page devices, yaffs2 on 2K page devices). ++ ++ If unsure, say Y. ++ ++config YAFFS_DISABLE_TAGS_ECC ++ bool "Disable yaffs from doing ECC on tags by default" ++ depends on YAFFS_FS && YAFFS_YAFFS2 ++ default n ++ help ++ This defaults yaffs to using its own ECC calculations on tags instead of ++ just relying on the MTD. ++ This behavior can also be overridden with tags_ecc_on and ++ tags_ecc_off mount options. ++ ++ If unsure, say N. ++ ++config YAFFS_ALWAYS_CHECK_CHUNK_ERASED ++ bool "Force chunk erase check" ++ depends on YAFFS_FS ++ default n ++ help ++ Normally yaffs only checks chunks before writing until an erased ++ chunk is found. This helps to detect any partially written ++ chunks that might have happened due to power loss. ++ ++ Enabling this forces on the test that chunks are erased in flash ++ before writing to them. This takes more time but is potentially ++ a bit more secure. ++ ++ Suggest setting Y during development and ironing out driver ++ issues etc. Suggest setting to N if you want faster writing. ++ ++ If unsure, say Y. ++ ++config YAFFS_EMPTY_LOST_AND_FOUND ++ bool "Empty lost and found on boot" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is enabled then the contents of lost and found is ++ automatically dumped at mount. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BLOCK_REFRESHING ++ bool "Disable yaffs2 block refreshing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then block refreshing is disabled. ++ Block refreshing infrequently refreshes the oldest block in ++ a yaffs2 file system. This mechanism helps to refresh flash to ++ mitigate against data loss. This is particularly useful for MLC. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BACKGROUND ++ bool "Disable yaffs2 background processing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then background processing is disabled. ++ Background processing makes many foreground activities faster. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BAD_BLOCK_MARKING ++ bool "Disable yaffs2 bad block marking" ++ depends on YAFFS_FS ++ default n ++ help ++ Useful during early flash bring up to prevent problems causing ++ lots of bad block marking. ++ ++ If unsure, say N. ++ ++config YAFFS_XATTR ++ bool "Enable yaffs2 xattr support" ++ depends on YAFFS_FS ++ default y ++ help ++ If this is set then yaffs2 will provide xattr support. ++ If unsure, say Y. +diff -Nur linux-3.14.43.orig/fs/yaffs2/Makefile linux-3.14.43/fs/yaffs2/Makefile +--- linux-3.14.43.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/Makefile 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,18 @@ ++# ++# Makefile for the linux YAFFS filesystem routines. ++# ++ ++obj-$(CONFIG_YAFFS_FS) += yaffs.o ++ ++yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o ++yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o ++yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o ++yaffs-y += yaffs_mtdif.o ++yaffs-y += yaffs_nameval.o yaffs_attribs.o ++yaffs-y += yaffs_allocator.o ++yaffs-y += yaffs_yaffs1.o ++yaffs-y += yaffs_yaffs2.o ++yaffs-y += yaffs_bitmap.o ++yaffs-y += yaffs_summary.o ++yaffs-y += yaffs_verify.o ++ +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_allocator.c linux-3.14.43/fs/yaffs2/yaffs_allocator.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_allocator.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,357 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_allocator.h" ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yportenv.h" ++ ++/* ++ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks ++ * of approx 100 objects that are themn allocated singly. ++ * This is basically a simplified slab allocator. ++ * ++ * We don't use the Linux slab allocator because slab does not allow ++ * us to dump all the objects in one hit when we do a umount and tear ++ * down all the tnodes and objects. slab requires that we first free ++ * the individual objects. ++ * ++ * Once yaffs has been mainlined I shall try to motivate for a change ++ * to slab to provide the extra features we need here. ++ */ ++ ++struct yaffs_tnode_list { ++ struct yaffs_tnode_list *next; ++ struct yaffs_tnode *tnodes; ++}; ++ ++struct yaffs_obj_list { ++ struct yaffs_obj_list *next; ++ struct yaffs_obj *objects; ++}; ++ ++struct yaffs_allocator { ++ int n_tnodes_created; ++ struct yaffs_tnode *free_tnodes; ++ int n_free_tnodes; ++ struct yaffs_tnode_list *alloc_tnode_list; ++ ++ int n_obj_created; ++ struct list_head free_objs; ++ int n_free_objects; ++ ++ struct yaffs_obj_list *allocated_obj_list; ++}; ++ ++static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->alloc_tnode_list) { ++ tmp = allocator->alloc_tnode_list->next; ++ ++ kfree(allocator->alloc_tnode_list->tnodes); ++ kfree(allocator->alloc_tnode_list); ++ allocator->alloc_tnode_list = tmp; ++ } ++ ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->alloc_tnode_list = NULL; ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ int i; ++ struct yaffs_tnode *new_tnodes; ++ u8 *mem; ++ struct yaffs_tnode *curr; ++ struct yaffs_tnode *next; ++ struct yaffs_tnode_list *tnl; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_tnodes < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); ++ mem = (u8 *) new_tnodes; ++ ++ if (!new_tnodes) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: Could not allocate Tnodes"); ++ return YAFFS_FAIL; ++ } ++ ++ /* New hookup for wide tnodes */ ++ for (i = 0; i < n_tnodes - 1; i++) { ++ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; ++ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; ++ curr->internal[0] = next; ++ } ++ ++ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; ++ curr->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = (struct yaffs_tnode *)mem; ++ ++ allocator->n_free_tnodes += n_tnodes; ++ allocator->n_tnodes_created += n_tnodes; ++ ++ /* Now add this bunch of tnodes to a list for freeing up. ++ * NB If we can't add this to the management list it isn't fatal ++ * but it just means we can't free this bunch of tnodes later. ++ */ ++ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); ++ if (!tnl) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Could not add tnodes to management list"); ++ return YAFFS_FAIL; ++ } else { ++ tnl->tnodes = new_tnodes; ++ tnl->next = allocator->alloc_tnode_list; ++ allocator->alloc_tnode_list = tnl; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (!allocator) { ++ BUG(); ++ return NULL; ++ } ++ ++ /* If there are none left make more */ ++ if (!allocator->free_tnodes) ++ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); ++ ++ if (allocator->free_tnodes) { ++ tn = allocator->free_tnodes; ++ allocator->free_tnodes = allocator->free_tnodes->internal[0]; ++ allocator->n_free_tnodes--; ++ } ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ if (tn) { ++ tn->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = tn; ++ allocator->n_free_tnodes++; ++ } ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++/*--------------- yaffs_obj alloaction ------------------------ ++ * ++ * Free yaffs_objs are stored in a list using obj->siblings. ++ * The blocks of allocated objects are stored in a linked list. ++ */ ++ ++static void yaffs_init_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->allocated_obj_list = NULL; ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++} ++ ++static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ struct yaffs_obj_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->allocated_obj_list) { ++ tmp = allocator->allocated_obj_list->next; ++ kfree(allocator->allocated_obj_list->objects); ++ kfree(allocator->allocated_obj_list); ++ allocator->allocated_obj_list = tmp; ++ } ++ ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++ allocator->n_obj_created = 0; ++} ++ ++static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ int i; ++ struct yaffs_obj *new_objs; ++ struct yaffs_obj_list *list; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_obj < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); ++ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); ++ ++ if (!new_objs || !list) { ++ kfree(new_objs); ++ new_objs = NULL; ++ kfree(list); ++ list = NULL; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Could not allocate more objects"); ++ return YAFFS_FAIL; ++ } ++ ++ /* Hook them into the free list */ ++ for (i = 0; i < n_obj; i++) ++ list_add(&new_objs[i].siblings, &allocator->free_objs); ++ ++ allocator->n_free_objects += n_obj; ++ allocator->n_obj_created += n_obj; ++ ++ /* Now add this bunch of Objects to a list for freeing up. */ ++ ++ list->objects = new_objs; ++ list->next = allocator->allocated_obj_list; ++ allocator->allocated_obj_list = list; ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct list_head *lh; ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return obj; ++ } ++ ++ /* If there are none left make more */ ++ if (list_empty(&allocator->free_objs)) ++ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); ++ ++ if (!list_empty(&allocator->free_objs)) { ++ lh = allocator->free_objs.next; ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ list_del_init(lh); ++ allocator->n_free_objects--; ++ } ++ ++ return obj; ++} ++ ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ /* Link into the free list. */ ++ list_add(&obj->siblings, &allocator->free_objs); ++ allocator->n_free_objects++; ++} ++ ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ ++ if (!dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ yaffs_deinit_raw_tnodes(dev); ++ yaffs_deinit_raw_objs(dev); ++ kfree(dev->allocator); ++ dev->allocator = NULL; ++} ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator; ++ ++ if (dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); ++ if (allocator) { ++ dev->allocator = allocator; ++ yaffs_init_raw_tnodes(dev); ++ yaffs_init_raw_objs(dev); ++ } ++} ++ +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_allocator.h linux-3.14.43/fs/yaffs2/yaffs_allocator.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_allocator.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,30 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ALLOCATOR_H__ ++#define __YAFFS_ALLOCATOR_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_attribs.c linux-3.14.43/fs/yaffs2/yaffs_attribs.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_attribs.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,132 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) ++#define IATTR_UID ia_uid ++#define IATTR_GID ia_gid ++#else ++#define IATTR_UID ia_uid.val ++#define IATTR_GID ia_gid.val ++#endif ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) ++{ ++ obj->yst_uid = oh->yst_uid; ++ obj->yst_gid = oh->yst_gid; ++ obj->yst_atime = oh->yst_atime; ++ obj->yst_mtime = oh->yst_mtime; ++ obj->yst_ctime = oh->yst_ctime; ++ obj->yst_rdev = oh->yst_rdev; ++} ++ ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) ++{ ++ oh->yst_uid = obj->yst_uid; ++ oh->yst_gid = obj->yst_gid; ++ oh->yst_atime = obj->yst_atime; ++ oh->yst_mtime = obj->yst_mtime; ++ oh->yst_ctime = obj->yst_ctime; ++ oh->yst_rdev = obj->yst_rdev; ++ ++} ++ ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) ++{ ++ obj->yst_mtime = Y_CURRENT_TIME; ++ if (do_a) ++ obj->yst_atime = obj->yst_mtime; ++ if (do_c) ++ obj->yst_ctime = obj->yst_mtime; ++} ++ ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) ++{ ++ yaffs_load_current_time(obj, 1, 1); ++ obj->yst_rdev = rdev; ++ obj->yst_uid = uid; ++ obj->yst_gid = gid; ++} ++ ++static loff_t yaffs_get_file_size(struct yaffs_obj *obj) ++{ ++ YCHAR *alias = NULL; ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return obj->variant.file_variant.file_size; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = obj->variant.symlink_variant.alias; ++ if (!alias) ++ return 0; ++ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); ++ default: ++ return 0; ++ } ++} ++ ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = attr->ia_valid; ++ ++ if (valid & ATTR_MODE) ++ obj->yst_mode = attr->ia_mode; ++ if (valid & ATTR_UID) ++ obj->yst_uid = attr->IATTR_UID; ++ if (valid & ATTR_GID) ++ obj->yst_gid = attr->IATTR_GID; ++ ++ if (valid & ATTR_ATIME) ++ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); ++ if (valid & ATTR_CTIME) ++ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); ++ if (valid & ATTR_MTIME) ++ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); ++ ++ if (valid & ATTR_SIZE) ++ yaffs_resize_file(obj, attr->ia_size); ++ ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ return YAFFS_OK; ++ ++} ++ ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = 0; ++ ++ attr->ia_mode = obj->yst_mode; ++ valid |= ATTR_MODE; ++ attr->IATTR_UID = obj->yst_uid; ++ valid |= ATTR_UID; ++ attr->IATTR_GID = obj->yst_gid; ++ valid |= ATTR_GID; ++ ++ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; ++ valid |= ATTR_ATIME; ++ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; ++ valid |= ATTR_CTIME; ++ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; ++ valid |= ATTR_MTIME; ++ ++ attr->ia_size = yaffs_get_file_size(obj); ++ valid |= ATTR_SIZE; ++ ++ attr->ia_valid = valid; ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_attribs.h linux-3.14.43/fs/yaffs2/yaffs_attribs.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_attribs.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ATTRIBS_H__ ++#define __YAFFS_ATTRIBS_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_bitmap.c linux-3.14.43/fs/yaffs2/yaffs_bitmap.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_bitmap.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,97 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_bitmap.h" ++#include "yaffs_trace.h" ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "BlockBits block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return dev->chunk_bits + ++ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); ++} ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block || ++ chunk < 0 || chunk >= dev->param.chunks_per_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Chunk Id (%d:%d) invalid", ++ blk, chunk); ++ BUG(); ++ } ++} ++ ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ memset(blk_bits, 0, dev->chunk_bit_stride); ++} ++ ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); ++} ++ ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] |= (1 << (chunk & 7)); ++} ++ ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; ++} ++ ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++) { ++ if (*blk_bits) ++ return 1; ++ blk_bits++; ++ } ++ return 0; ++} ++ ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ int n = 0; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) ++ n += hweight8(*blk_bits); ++ ++ return n; ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_bitmap.h linux-3.14.43/fs/yaffs2/yaffs_bitmap.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_bitmap.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++#ifndef __YAFFS_BITMAP_H__ ++#define __YAFFS_BITMAP_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.14.43/fs/yaffs2/yaffs_checkptrw.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_checkptrw.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,466 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_checkptrw.h" ++#include "yaffs_getblockinfo.h" ++ ++struct yaffs_checkpt_chunk_hdr { ++ int version; ++ int seq; ++ u32 sum; ++ u32 xor; ++} ; ++ ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++static int apply_block_offset(struct yaffs_dev *dev, int block) ++{ ++ return block - dev->block_offset; ++} ++ ++static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ hdr.version = YAFFS_CHECKPOINT_VERSION; ++ hdr.seq = dev->checkpt_page_seq; ++ hdr.sum = dev->checkpt_sum; ++ hdr.xor = dev->checkpt_xor; ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); ++} ++ ++static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ return hdr.version == YAFFS_CHECKPOINT_VERSION && ++ hdr.seq == dev->checkpt_page_seq && ++ hdr.sum == dev->checkpt_sum && ++ hdr.xor == dev->checkpt_xor; ++} ++ ++static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) ++{ ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpt blocks_avail = %d", blocks_avail); ++ ++ return (blocks_avail <= 0) ? 0 : 1; ++} ++ ++static int yaffs_checkpt_erase(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (!dev->drv.drv_erase_fn) ++ return 0; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checking blocks %d to %d", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ int offset_i = apply_block_offset(dev, i); ++ int result; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "erasing checkpt block %d", i); ++ ++ dev->n_erasures++; ++ ++ result = dev->drv.drv_erase_fn(dev, offset_i); ++ if(result) { ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += ++ dev->param.chunks_per_block; ++ } else { ++ dev->drv.drv_mark_bad_fn(dev, offset_i); ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ } ++ } ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ return 1; ++} ++ ++static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) ++{ ++ int i; ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block: erased %d reserved %d avail %d next %d ", ++ dev->n_erased_blocks, dev->param.n_reserved_blocks, ++ blocks_avail, dev->checkpt_next_block); ++ ++ if (dev->checkpt_next_block >= 0 && ++ dev->checkpt_next_block <= dev->internal_end_block && ++ blocks_avail > 0) { ++ ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev, i); ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ dev->checkpt_next_block = i + 1; ++ dev->checkpt_cur_block = i; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block %d", i); ++ return; ++ } ++ } ++ } ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_ext_tags tags; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: start: blocks %d next %d", ++ dev->blocks_in_checkpt, dev->checkpt_next_block); ++ ++ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ int chunk = i * dev->param.chunks_per_block; ++ enum yaffs_block_state state; ++ u32 seq; ++ ++ dev->tagger.read_chunk_tags_fn(dev, ++ apply_chunk_offset(dev, chunk), ++ NULL, &tags); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", ++ i, (int) state, ++ tags.obj_id, tags.seq_number, ++ tags.ecc_result); ++ ++ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ continue; ++ ++ dev->tagger.query_block_fn(dev, ++ apply_block_offset(dev, i), ++ &state, &seq); ++ if (state == YAFFS_BLOCK_STATE_DEAD) ++ continue; ++ ++ /* Right kind of block */ ++ dev->checkpt_next_block = tags.obj_id; ++ dev->checkpt_cur_block = i; ++ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; ++ dev->blocks_in_checkpt++; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "found checkpt block %d", i); ++ return; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) ++{ ++ int i; ++ ++ dev->checkpt_open_write = writing; ++ ++ /* Got the functions we need? */ ++ if (!dev->tagger.write_chunk_tags_fn || ++ !dev->tagger.read_chunk_tags_fn || ++ !dev->drv.drv_erase_fn || ++ !dev->drv.drv_mark_bad_fn) ++ return 0; ++ ++ if (writing && !yaffs2_checkpt_space_ok(dev)) ++ return 0; ++ ++ if (!dev->checkpt_buffer) ++ dev->checkpt_buffer = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ dev->checkpt_page_seq = 0; ++ dev->checkpt_byte_count = 0; ++ dev->checkpt_sum = 0; ++ dev->checkpt_xor = 0; ++ dev->checkpt_cur_block = -1; ++ dev->checkpt_cur_chunk = -1; ++ dev->checkpt_next_block = dev->internal_start_block; ++ ++ if (writing) { ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ return yaffs_checkpt_erase(dev); ++ } ++ ++ /* Opening for a read */ ++ /* Set to a value that will kick off a read */ ++ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; ++ /* A checkpoint block list of 1 checkpoint block per 16 block is ++ * (hopefully) going to be way more than we need */ ++ dev->blocks_in_checkpt = 0; ++ dev->checkpt_max_blocks = ++ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; ++ if (!dev->checkpt_block_list) ++ dev->checkpt_block_list = ++ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); ++ ++ if (!dev->checkpt_block_list) ++ return 0; ++ ++ for (i = 0; i < dev->checkpt_max_blocks; i++) ++ dev->checkpt_block_list[i] = -1; ++ ++ return 1; ++} ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) ++{ ++ u32 composite_sum; ++ ++ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); ++ *sum = composite_sum; ++ return 1; ++} ++ ++static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) ++{ ++ int chunk; ++ int offset_chunk; ++ struct yaffs_ext_tags tags; ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_erased_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) ++ return 0; ++ ++ tags.is_deleted = 0; ++ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ ++ tags.chunk_id = dev->checkpt_page_seq + 1; ++ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; ++ tags.n_bytes = dev->data_bytes_per_chunk; ++ if (dev->checkpt_cur_chunk == 0) { ++ /* First chunk we write for the block? Set block state to ++ checkpoint */ ++ struct yaffs_block_info *bi = ++ yaffs_get_block_info(dev, dev->checkpt_cur_block); ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ dev->blocks_in_checkpt++; ++ } ++ ++ chunk = ++ dev->checkpt_cur_block * dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", ++ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, ++ tags.obj_id, tags.chunk_id); ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ ++ dev->n_page_writes++; ++ ++ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, ++ dev->checkpt_buffer, &tags); ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { ++ dev->checkpt_cur_chunk = 0; ++ dev->checkpt_cur_block = -1; ++ } ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ ++ ++ return 1; ++} ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (!dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) ++ ok = yaffs2_checkpt_flush_buffer(dev); ++ } ++ ++ return i; ++} ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) ++{ ++ int i = 0; ++ struct yaffs_ext_tags tags; ++ int chunk; ++ int offset_chunk; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes) { ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ /* Bail out if we can't find a checpoint block */ ++ if (dev->checkpt_cur_block < 0) ++ break; ++ ++ chunk = dev->checkpt_cur_block * ++ dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ dev->n_page_reads++; ++ ++ /* Read in the next chunk */ ++ dev->tagger.read_chunk_tags_fn(dev, ++ offset_chunk, ++ dev->checkpt_buffer, ++ &tags); ++ ++ /* Bail out if the chunk is corrupted. */ ++ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ break; ++ ++ /* Bail out if it is not a checkpoint chunk. */ ++ if(!yaffs2_checkpt_check_chunk_hdr(dev)) ++ break; ++ ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ ++ if (dev->checkpt_cur_chunk >= ++ dev->param.chunks_per_block) ++ dev->checkpt_cur_block = -1; ++ ++ } ++ ++ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ } ++ ++ return i; /* Number of bytes read */ ++} ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->checkpt_open_write) { ++ if (dev->checkpt_byte_offs != ++ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) ++ yaffs2_checkpt_flush_buffer(dev); ++ } else if (dev->checkpt_block_list) { ++ for (i = 0; ++ i < dev->blocks_in_checkpt && ++ dev->checkpt_block_list[i] >= 0; i++) { ++ int blk = dev->checkpt_block_list[i]; ++ struct yaffs_block_info *bi = NULL; ++ ++ if (dev->internal_start_block <= blk && ++ blk <= dev->internal_end_block) ++ bi = yaffs_get_block_info(dev, blk); ++ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ } ++ } ++ ++ dev->n_free_chunks -= ++ dev->blocks_in_checkpt * dev->param.chunks_per_block; ++ dev->n_erased_blocks -= dev->blocks_in_checkpt; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", ++ dev->checkpt_byte_count); ++ ++ if (dev->checkpt_buffer) ++ return 1; ++ else ++ return 0; ++} ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) ++{ ++ /* Erase the checkpoint data */ ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint invalidate of %d blocks", ++ dev->blocks_in_checkpt); ++ ++ return yaffs_checkpt_erase(dev); ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.14.43/fs/yaffs2/yaffs_checkptrw.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_checkptrw.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_CHECKPTRW_H__ ++#define __YAFFS_CHECKPTRW_H__ ++ ++#include "yaffs_guts.h" ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev); ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_ecc.c linux-3.14.43/fs/yaffs2/yaffs_ecc.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_ecc.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,281 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two ++ * such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_ecc.h" ++ ++/* Table generated by gen-ecc.c ++ * Using a table means we do not have to calculate p1..p4 and p1'..p4' ++ * for each byte of data. These are instead provided in a table in bits7..2. ++ * Bit 0 of each entry indicates whether the entry has an odd or even parity, ++ * and therefore this bytes influence on the line parity. ++ */ ++ ++static const unsigned char column_parity_table[] = { ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++}; ++ ++ ++/* Calculate the ECC for a 256-byte block of data */ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned char line_parity = 0; ++ unsigned char line_parity_prime = 0; ++ unsigned char t; ++ unsigned char b; ++ ++ for (i = 0; i < 256; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ } ++ ++ ecc[2] = (~col_parity) | 0x03; ++ ++ t = 0; ++ if (line_parity & 0x80) ++ t |= 0x80; ++ if (line_parity_prime & 0x80) ++ t |= 0x40; ++ if (line_parity & 0x40) ++ t |= 0x20; ++ if (line_parity_prime & 0x40) ++ t |= 0x10; ++ if (line_parity & 0x20) ++ t |= 0x08; ++ if (line_parity_prime & 0x20) ++ t |= 0x04; ++ if (line_parity & 0x10) ++ t |= 0x02; ++ if (line_parity_prime & 0x10) ++ t |= 0x01; ++ ecc[1] = ~t; ++ ++ t = 0; ++ if (line_parity & 0x08) ++ t |= 0x80; ++ if (line_parity_prime & 0x08) ++ t |= 0x40; ++ if (line_parity & 0x04) ++ t |= 0x20; ++ if (line_parity_prime & 0x04) ++ t |= 0x10; ++ if (line_parity & 0x02) ++ t |= 0x08; ++ if (line_parity_prime & 0x02) ++ t |= 0x04; ++ if (line_parity & 0x01) ++ t |= 0x02; ++ if (line_parity_prime & 0x01) ++ t |= 0x01; ++ ecc[0] = ~t; ++ ++} ++ ++/* Correct the ECC on a 256 byte block of data */ ++ ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc) ++{ ++ unsigned char d0, d1, d2; /* deltas */ ++ ++ d0 = read_ecc[0] ^ test_ecc[0]; ++ d1 = read_ecc[1] ^ test_ecc[1]; ++ d2 = read_ecc[2] ^ test_ecc[2]; ++ ++ if ((d0 | d1 | d2) == 0) ++ return 0; /* no error */ ++ ++ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && ++ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && ++ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { ++ /* Single bit (recoverable) error in data */ ++ ++ unsigned byte; ++ unsigned bit; ++ ++ bit = byte = 0; ++ ++ if (d1 & 0x80) ++ byte |= 0x80; ++ if (d1 & 0x20) ++ byte |= 0x40; ++ if (d1 & 0x08) ++ byte |= 0x20; ++ if (d1 & 0x02) ++ byte |= 0x10; ++ if (d0 & 0x80) ++ byte |= 0x08; ++ if (d0 & 0x20) ++ byte |= 0x04; ++ if (d0 & 0x08) ++ byte |= 0x02; ++ if (d0 & 0x02) ++ byte |= 0x01; ++ ++ if (d2 & 0x80) ++ bit |= 0x04; ++ if (d2 & 0x20) ++ bit |= 0x02; ++ if (d2 & 0x08) ++ bit |= 0x01; ++ ++ data[byte] ^= (1 << bit); ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ read_ecc[0] = test_ecc[0]; ++ read_ecc[1] = test_ecc[1]; ++ read_ecc[2] = test_ecc[2]; ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++ ++} ++ ++/* ++ * ECCxxxOther does ECC calcs on arbitrary n bytes of data ++ */ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc_other) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned line_parity = 0; ++ unsigned line_parity_prime = 0; ++ unsigned char b; ++ ++ for (i = 0; i < n_bytes; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { ++ /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ ++ } ++ ++ ecc_other->col_parity = (col_parity >> 2) & 0x3f; ++ ecc_other->line_parity = line_parity; ++ ecc_other->line_parity_prime = line_parity_prime; ++} ++ ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc) ++{ ++ unsigned char delta_col; /* column parity delta */ ++ unsigned delta_line; /* line parity delta */ ++ unsigned delta_line_prime; /* line parity delta */ ++ unsigned bit; ++ ++ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; ++ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; ++ delta_line_prime = ++ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; ++ ++ if ((delta_col | delta_line | delta_line_prime) == 0) ++ return 0; /* no error */ ++ ++ if (delta_line == ~delta_line_prime && ++ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { ++ /* Single bit (recoverable) error in data */ ++ ++ bit = 0; ++ ++ if (delta_col & 0x20) ++ bit |= 0x04; ++ if (delta_col & 0x08) ++ bit |= 0x02; ++ if (delta_col & 0x02) ++ bit |= 0x01; ++ ++ if (delta_line >= n_bytes) ++ return -1; ++ ++ data[delta_line] ^= (1 << bit); ++ ++ return 1; /* corrected */ ++ } ++ ++ if ((hweight32(delta_line) + ++ hweight32(delta_line_prime) + ++ hweight8(delta_col)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ *read_ecc = *test_ecc; ++ return 1; /* corrected */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_ecc.h linux-3.14.43/fs/yaffs2/yaffs_ecc.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_ecc.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. ++ * Thus, two such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#ifndef __YAFFS_ECC_H__ ++#define __YAFFS_ECC_H__ ++ ++struct yaffs_ecc_other { ++ unsigned char col_parity; ++ unsigned line_parity; ++ unsigned line_parity_prime; ++}; ++ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc); ++ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc); ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc); ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.14.43/fs/yaffs2/yaffs_getblockinfo.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_getblockinfo.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,35 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GETBLOCKINFO_H__ ++#define __YAFFS_GETBLOCKINFO_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++ ++/* Function to manipulate block info */ ++static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev ++ *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs: get_block_info block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return &dev->block_info[blk - dev->internal_start_block]; ++} ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_guts.c linux-3.14.43/fs/yaffs2/yaffs_guts.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_guts.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,5140 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++#include "yaffs_guts.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_tagsmarshall.h" ++#include "yaffs_nand.h" ++#include "yaffs_yaffs1.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_verify.h" ++#include "yaffs_nand.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nameval.h" ++#include "yaffs_allocator.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ ++#define YAFFS_GC_GOOD_ENOUGH 2 ++#define YAFFS_GC_PASSIVE_THRESHOLD 4 ++ ++#include "yaffs_ecc.h" ++ ++/* Forward declarations */ ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve); ++ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size); ++ ++/* Function to calculate chunk and offset */ ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out) ++{ ++ int chunk; ++ u32 offset; ++ ++ chunk = (u32) (addr >> dev->chunk_shift); ++ ++ if (dev->chunk_div == 1) { ++ /* easy power of 2 case */ ++ offset = (u32) (addr & dev->chunk_mask); ++ } else { ++ /* Non power-of-2 case */ ++ ++ loff_t chunk_base; ++ ++ chunk /= dev->chunk_div; ++ ++ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; ++ offset = (u32) (addr - chunk_base); ++ } ++ ++ *chunk_out = chunk; ++ *offset_out = offset; ++} ++ ++/* Function to return the number of shifts for a power of 2 greater than or ++ * equal to the given number ++ * Note we don't try to cater for all possible numbers and this does not have to ++ * be hellishly efficient. ++ */ ++ ++static inline u32 calc_shifts_ceiling(u32 x) ++{ ++ int extra_bits; ++ int shifts; ++ ++ shifts = extra_bits = 0; ++ ++ while (x > 1) { ++ if (x & 1) ++ extra_bits++; ++ x >>= 1; ++ shifts++; ++ } ++ ++ if (extra_bits) ++ shifts++; ++ ++ return shifts; ++} ++ ++/* Function to return the number of shifts to get a 1 in bit 0 ++ */ ++ ++static inline u32 calc_shifts(u32 x) ++{ ++ u32 shifts; ++ ++ shifts = 0; ++ ++ if (!x) ++ return 0; ++ ++ while (!(x & 1)) { ++ x >>= 1; ++ shifts++; ++ } ++ ++ return shifts; ++} ++ ++/* ++ * Temporary buffer manipulations. ++ */ ++ ++static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) ++{ ++ int i; ++ u8 *buf = (u8 *) 1; ++ ++ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); ++ ++ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { ++ dev->temp_buffer[i].in_use = 0; ++ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ dev->temp_buffer[i].buffer = buf; ++ } ++ ++ return buf ? YAFFS_OK : YAFFS_FAIL; ++} ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) ++{ ++ int i; ++ ++ dev->temp_in_use++; ++ if (dev->temp_in_use > dev->max_temp) ++ dev->max_temp = dev->temp_in_use; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].in_use == 0) { ++ dev->temp_buffer[i].in_use = 1; ++ return dev->temp_buffer[i].buffer; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); ++ /* ++ * If we got here then we have to allocate an unmanaged one ++ * This is not good. ++ */ ++ ++ dev->unmanaged_buffer_allocs++; ++ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); ++ ++} ++ ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) ++{ ++ int i; ++ ++ dev->temp_in_use--; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].buffer == buffer) { ++ dev->temp_buffer[i].in_use = 0; ++ return; ++ } ++ } ++ ++ if (buffer) { ++ /* assume it is an unmanaged one. */ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, ++ "Releasing unmanaged temp buffer"); ++ kfree(buffer); ++ dev->unmanaged_buffer_deallocs++; ++ } ++ ++} ++ ++/* ++ * Functions for robustisizing TODO ++ * ++ */ ++ ++static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) data; ++ (void) tags; ++} ++ ++static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) tags; ++} ++ ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ if (!bi->gc_prioritise) { ++ bi->gc_prioritise = 1; ++ dev->has_pending_prioritised_gc = 1; ++ bi->chunk_error_strikes++; ++ ++ if (bi->chunk_error_strikes > 3) { ++ bi->needs_retiring = 1; /* Too many stikes, so retire */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Block struck out"); ++ ++ } ++ } ++} ++ ++static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, ++ int erased_ok) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs_handle_chunk_error(dev, bi); ++ ++ if (erased_ok) { ++ /* Was an actual write failure, ++ * so mark the block for retirement.*/ ++ bi->needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d needs retiring", flash_block); ++ } ++ ++ /* Delete the chunk */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++} ++ ++/* ++ * Verification code ++ */ ++ ++/* ++ * Simple hash function. Needs to have a reasonable spread ++ */ ++ ++static inline int yaffs_hash_fn(int n) ++{ ++ if (n < 0) ++ n = -n; ++ return n % YAFFS_NOBJECT_BUCKETS; ++} ++ ++/* ++ * Access functions to useful fake objects. ++ * Note that root might have a presence in NAND if permissions are set. ++ */ ++ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) ++{ ++ return dev->root_dir; ++} ++ ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) ++{ ++ return dev->lost_n_found; ++} ++ ++/* ++ * Erased NAND checking functions ++ */ ++ ++int yaffs_check_ff(u8 *buffer, int n_bytes) ++{ ++ /* Horrible, slow implementation */ ++ while (n_bytes--) { ++ if (*buffer != 0xff) ++ return 0; ++ buffer++; ++ } ++ return 1; ++} ++ ++static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int retval = YAFFS_OK; ++ u8 *data = yaffs_get_temp_buffer(dev); ++ struct yaffs_ext_tags tags; ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); ++ ++ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) ++ retval = YAFFS_FAIL; ++ ++ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || ++ tags.chunk_used) { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not erased", nand_chunk); ++ retval = YAFFS_FAIL; ++ } ++ ++ yaffs_release_temp_buffer(dev, data); ++ ++ return retval; ++ ++} ++ ++static int yaffs_verify_chunk_written(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = YAFFS_OK; ++ struct yaffs_ext_tags temp_tags; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); ++ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || ++ temp_tags.obj_id != tags->obj_id || ++ temp_tags.chunk_id != tags->chunk_id || ++ temp_tags.n_bytes != tags->n_bytes) ++ retval = YAFFS_FAIL; ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ return retval; ++} ++ ++ ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) ++{ ++ int reserved_chunks; ++ int reserved_blocks = dev->param.n_reserved_blocks; ++ int checkpt_blocks; ++ ++ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); ++ ++ reserved_chunks = ++ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; ++ ++ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); ++} ++ ++static int yaffs_find_alloc_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ ++ if (dev->n_erased_blocks < 1) { ++ /* Hoosterman we've got a problem. ++ * Can't get space to gc ++ */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no more erased blocks"); ++ ++ return -1; ++ } ++ ++ /* Find an empty block. */ ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ dev->alloc_block_finder++; ++ if (dev->alloc_block_finder < dev->internal_start_block ++ || dev->alloc_block_finder > dev->internal_end_block) { ++ dev->alloc_block_finder = dev->internal_start_block; ++ } ++ ++ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->seq_number++; ++ bi->seq_number = dev->seq_number; ++ dev->n_erased_blocks--; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Allocated block %d, seq %d, %d left" , ++ dev->alloc_block_finder, dev->seq_number, ++ dev->n_erased_blocks); ++ return dev->alloc_block_finder; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs tragedy: no more erased blocks, but there should have been %d", ++ dev->n_erased_blocks); ++ ++ return -1; ++} ++ ++static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, ++ struct yaffs_block_info **block_ptr) ++{ ++ int ret_val; ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block < 0) { ++ /* Get next block to allocate off */ ++ dev->alloc_block = yaffs_find_alloc_block(dev); ++ dev->alloc_page = 0; ++ } ++ ++ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { ++ /* No space unless we're allowed to use the reserve. */ ++ return -1; ++ } ++ ++ if (dev->n_erased_blocks < dev->param.n_reserved_blocks ++ && dev->alloc_page == 0) ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); ++ ++ /* Next page please.... */ ++ if (dev->alloc_block >= 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ ++ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + ++ dev->alloc_page; ++ bi->pages_in_use++; ++ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); ++ ++ dev->alloc_page++; ++ ++ dev->n_free_chunks--; ++ ++ /* If the block is full set the state to full */ ++ if (dev->alloc_page >= dev->param.chunks_per_block) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ if (block_ptr) ++ *block_ptr = bi; ++ ++ return ret_val; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); ++ ++ return -1; ++} ++ ++static int yaffs_get_erased_chunks(struct yaffs_dev *dev) ++{ ++ int n; ++ ++ n = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ if (dev->alloc_block > 0) ++ n += (dev->param.chunks_per_block - dev->alloc_page); ++ ++ return n; ++ ++} ++ ++/* ++ * yaffs_skip_rest_of_block() skips over the rest of the allocation block ++ * if we don't want to write to it. ++ */ ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev) ++{ ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block > 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ } ++} ++ ++static int yaffs_write_new_chunk(struct yaffs_dev *dev, ++ const u8 *data, ++ struct yaffs_ext_tags *tags, int use_reserver) ++{ ++ int attempts = 0; ++ int write_ok = 0; ++ int chunk; ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ do { ++ struct yaffs_block_info *bi = 0; ++ int erased_ok = 0; ++ ++ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); ++ if (chunk < 0) { ++ /* no space */ ++ break; ++ } ++ ++ /* First check this chunk is erased, if it needs ++ * checking. The checking policy (unless forced ++ * always on) is as follows: ++ * ++ * Check the first page we try to write in a block. ++ * If the check passes then we don't need to check any ++ * more. If the check fails, we check again... ++ * If the block has been erased, we don't need to check. ++ * ++ * However, if the block has been prioritised for gc, ++ * then we think there might be something odd about ++ * this block and stop using it. ++ * ++ * Rationale: We should only ever see chunks that have ++ * not been erased if there was a partially written ++ * chunk due to power loss. This checking policy should ++ * catch that case with very few checks and thus save a ++ * lot of checks that are most likely not needed. ++ * ++ * Mods to the above ++ * If an erase check fails or the write fails we skip the ++ * rest of the block. ++ */ ++ ++ /* let's give it a try */ ++ attempts++; ++ ++ if (dev->param.always_check_erased) ++ bi->skip_erased_check = 0; ++ ++ if (!bi->skip_erased_check) { ++ erased_ok = yaffs_check_chunk_erased(dev, chunk); ++ if (erased_ok != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs chunk %d was not erased", ++ chunk); ++ ++ /* If not erased, delete this one, ++ * skip rest of block and ++ * try another chunk */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++ continue; ++ } ++ } ++ ++ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); ++ ++ if (!bi->skip_erased_check) ++ write_ok = ++ yaffs_verify_chunk_written(dev, chunk, data, tags); ++ ++ if (write_ok != YAFFS_OK) { ++ /* Clean up aborted write, skip to next block and ++ * try another chunk */ ++ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); ++ continue; ++ } ++ ++ bi->skip_erased_check = 1; ++ ++ /* Copy the data into the robustification buffer */ ++ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); ++ ++ } while (write_ok != YAFFS_OK && ++ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); ++ ++ if (!write_ok) ++ chunk = -1; ++ ++ if (attempts > 1) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs write required %d attempts", ++ attempts); ++ dev->n_retried_writes += (attempts - 1); ++ } ++ ++ return chunk; ++} ++ ++/* ++ * Block retiring for handling a broken block. ++ */ ++ ++static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { ++ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to mark bad and erase block %d", ++ flash_block); ++ } else { ++ struct yaffs_ext_tags tags; ++ int chunk_id = ++ flash_block * dev->param.chunks_per_block; ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ memset(&tags, 0, sizeof(tags)); ++ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; ++ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - ++ dev->chunk_offset, ++ buffer, ++ &tags) != YAFFS_OK) ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to write bad block marker to block %d", ++ flash_block); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ } ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ bi->gc_prioritise = 0; ++ bi->needs_retiring = 0; ++ ++ dev->n_retired_blocks++; ++} ++ ++/*---------------- Name handling functions ------------*/ ++ ++static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, ++ const YCHAR *oh_name, int buff_size) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ if (dev->param.auto_unicode) { ++ if (*oh_name) { ++ /* It is an ASCII name, do an ASCII to ++ * unicode conversion */ ++ const char *ascii_oh_name = (const char *)oh_name; ++ int n = buff_size - 1; ++ while (n > 0 && *ascii_oh_name) { ++ *name = *ascii_oh_name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ strncpy(name, oh_name + 1, buff_size - 1); ++ } ++ } else { ++#else ++ (void) dev; ++ { ++#endif ++ strncpy(name, oh_name, buff_size - 1); ++ } ++} ++ ++static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, ++ const YCHAR *name) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ ++ int is_ascii; ++ const YCHAR *w; ++ ++ if (dev->param.auto_unicode) { ++ ++ is_ascii = 1; ++ w = name; ++ ++ /* Figure out if the name will fit in ascii character set */ ++ while (is_ascii && *w) { ++ if ((*w) & 0xff00) ++ is_ascii = 0; ++ w++; ++ } ++ ++ if (is_ascii) { ++ /* It is an ASCII name, so convert unicode to ascii */ ++ char *ascii_oh_name = (char *)oh_name; ++ int n = YAFFS_MAX_NAME_LENGTH - 1; ++ while (n > 0 && *name) { ++ *ascii_oh_name = *name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ /* Unicode name, so save starting at the second YCHAR */ ++ *oh_name = 0; ++ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); ++ } ++ } else { ++#else ++ dev = dev; ++ { ++#endif ++ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); ++ } ++} ++ ++static u16 yaffs_calc_name_sum(const YCHAR *name) ++{ ++ u16 sum = 0; ++ u16 i = 1; ++ ++ if (!name) ++ return 0; ++ ++ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { ++ ++ /* 0x1f mask is case insensitive */ ++ sum += ((*name) & 0x1f) * i; ++ i++; ++ name++; ++ } ++ return sum; ++} ++ ++ ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ memset(obj->short_name, 0, sizeof(obj->short_name)); ++ ++ if (name && !name[0]) { ++ yaffs_fix_null_name(obj, obj->short_name, ++ YAFFS_SHORT_NAME_LENGTH); ++ name = obj->short_name; ++ } else if (name && ++ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= ++ YAFFS_SHORT_NAME_LENGTH) { ++ strcpy(obj->short_name, name); ++ } ++ ++ obj->sum = yaffs_calc_name_sum(name); ++} ++ ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ memset(tmp_name, 0, sizeof(tmp_name)); ++ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_set_obj_name(obj, tmp_name); ++#else ++ yaffs_set_obj_name(obj, oh->name); ++#endif ++} ++ ++loff_t yaffs_max_file_size(struct yaffs_dev *dev) ++{ ++ if(sizeof(loff_t) < 8) ++ return YAFFS_MAX_FILE_SIZE_32; ++ else ++ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; ++} ++ ++/*-------------------- TNODES ------------------- ++ ++ * List of spare tnodes ++ * The list is hooked together using the first pointer ++ * in the tnode. ++ */ ++ ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); ++ ++ if (tn) { ++ memset(tn, 0, dev->tnode_size); ++ dev->n_tnodes++; ++ } ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ yaffs_free_raw_tnode(dev, tn); ++ dev->n_tnodes--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ yaffs_deinit_raw_tnodes_and_objs(dev); ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++} ++ ++static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos, unsigned val) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 mask; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ val >>= dev->chunk_grp_bits; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ mask = dev->tnode_mask << bit_in_word; ++ ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val << bit_in_word)); ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ mask = ++ dev->tnode_mask >> bit_in_word; ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val >> bit_in_word)); ++ } ++} ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 val; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ val = map[word_in_map] >> bit_in_word; ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ val |= (map[word_in_map] << bit_in_word); ++ } ++ ++ val &= dev->tnode_mask; ++ val <<= dev->chunk_grp_bits; ++ ++ return val; ++} ++ ++/* ------------------- End of individual tnode manipulation -----------------*/ ++ ++/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ ++ * The look up tree is represented by the top tnode and the number of top_level ++ * in the tree. 0 means only the level 0 tnode is in the tree. ++ */ ++ ++/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id) ++{ ++ struct yaffs_tnode *tn = file_struct->top; ++ u32 i; ++ int required_depth; ++ int level = file_struct->top_level; ++ ++ (void) dev; ++ ++ /* Check sane level and chunk Id */ ++ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (i) { ++ i >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) ++ return NULL; /* Not tall enough, so we can't find it */ ++ ++ /* Traverse down to level 0 */ ++ while (level > 0 && tn) { ++ tn = tn->internal[(chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (level - 1) * ++ YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK]; ++ level--; ++ } ++ ++ return tn; ++} ++ ++/* add_find_tnode_0 finds the level 0 tnode if it exists, ++ * otherwise first expands the tree. ++ * This happens in two steps: ++ * 1. If the tree isn't tall enough, then make it taller. ++ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. ++ * ++ * Used when modifying the tree. ++ * ++ * If the tn argument is NULL, then a fresh tnode will be added otherwise the ++ * specified tn will be plugged into the ttree. ++ */ ++ ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn) ++{ ++ int required_depth; ++ int i; ++ int l; ++ struct yaffs_tnode *tn; ++ u32 x; ++ ++ /* Check sane level and page Id */ ++ if (file_struct->top_level < 0 || ++ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) { ++ /* Not tall enough, gotta make the tree taller */ ++ for (i = file_struct->top_level; i < required_depth; i++) { ++ ++ tn = yaffs_get_tnode(dev); ++ ++ if (tn) { ++ tn->internal[0] = file_struct->top; ++ file_struct->top = tn; ++ file_struct->top_level++; ++ } else { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: no more tnodes"); ++ return NULL; ++ } ++ } ++ } ++ ++ /* Traverse down to level 0, adding anything we need */ ++ ++ l = file_struct->top_level; ++ tn = file_struct->top; ++ ++ if (l > 0) { ++ while (l > 0 && tn) { ++ x = (chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK; ++ ++ if ((l > 1) && !tn->internal[x]) { ++ /* Add missing non-level-zero tnode */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } else if (l == 1) { ++ /* Looking from level 1 at level 0 */ ++ if (passed_tn) { ++ /* If we already have one, release it */ ++ if (tn->internal[x]) ++ yaffs_free_tnode(dev, ++ tn->internal[x]); ++ tn->internal[x] = passed_tn; ++ ++ } else if (!tn->internal[x]) { ++ /* Don't have one, none passed in */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } ++ } ++ ++ tn = tn->internal[x]; ++ l--; ++ } ++ } else { ++ /* top is level 0 */ ++ if (passed_tn) { ++ memcpy(tn, passed_tn, ++ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); ++ yaffs_free_tnode(dev, passed_tn); ++ } ++ } ++ ++ return tn; ++} ++ ++static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, ++ int chunk_obj) ++{ ++ return (tags->chunk_id == chunk_obj && ++ tags->obj_id == obj_id && ++ !tags->is_deleted) ? 1 : 0; ++ ++} ++ ++static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, ++ struct yaffs_ext_tags *tags, int obj_id, ++ int inode_chunk) ++{ ++ int j; ++ ++ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { ++ if (yaffs_check_chunk_bit ++ (dev, the_chunk / dev->param.chunks_per_block, ++ the_chunk % dev->param.chunks_per_block)) { ++ ++ if (dev->chunk_grp_size == 1) ++ return the_chunk; ++ else { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ tags); ++ if (yaffs_tags_match(tags, ++ obj_id, inode_chunk)) { ++ /* found it; */ ++ return the_chunk; ++ } ++ } ++ } ++ the_chunk++; ++ } ++ return -1; ++} ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /*Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ int ret_val = -1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ return ret_val; ++} ++ ++static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /* Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ int ret_val = -1; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ ++ /* Delete the entry in the filestructure (if found) */ ++ if (ret_val != -1) ++ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); ++ ++ return ret_val; ++} ++ ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan) ++{ ++ /* NB in_scan is zero unless scanning. ++ * For forward scanning, in_scan is > 0; ++ * for backward scanning in_scan is < 0 ++ * ++ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. ++ */ ++ ++ struct yaffs_tnode *tn; ++ struct yaffs_dev *dev = in->my_dev; ++ int existing_cunk; ++ struct yaffs_ext_tags existing_tags; ++ struct yaffs_ext_tags new_tags; ++ unsigned existing_serial, new_serial; ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { ++ /* Just ignore an attempt at putting a chunk into a non-file ++ * during scanning. ++ * If it is not during Scanning then something went wrong! ++ */ ++ if (!in_scan) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy:attempt to put data chunk into a non-file" ++ ); ++ BUG(); ++ } ++ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ ++ tn = yaffs_add_find_tnode_0(dev, ++ &in->variant.file_variant, ++ inode_chunk, NULL); ++ if (!tn) ++ return YAFFS_FAIL; ++ ++ if (!nand_chunk) ++ /* Dummy insert, bail now */ ++ return YAFFS_OK; ++ ++ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ if (in_scan != 0) { ++ /* If we're scanning then we need to test for duplicates ++ * NB This does not need to be efficient since it should only ++ * happen when the power fails during a write, then only one ++ * chunk should ever be affected. ++ * ++ * Correction for YAFFS2: This could happen quite a lot and we ++ * need to think about efficiency! TODO ++ * Update: For backward scanning we don't need to re-read tags ++ * so this is quite cheap. ++ */ ++ ++ if (existing_cunk > 0) { ++ /* NB Right now existing chunk will not be real ++ * chunk_id if the chunk group size > 1 ++ * thus we have to do a FindChunkInFile to get the ++ * real chunk id. ++ * ++ * We have a duplicate now we need to decide which ++ * one to use: ++ * ++ * Backwards scanning YAFFS2: The old one is what ++ * we use, dump the new one. ++ * YAFFS1: Get both sets of tags and compare serial ++ * numbers. ++ */ ++ ++ if (in_scan > 0) { ++ /* Only do this for forward scanning */ ++ yaffs_rd_chunk_tags_nand(dev, ++ nand_chunk, ++ NULL, &new_tags); ++ ++ /* Do a proper find */ ++ existing_cunk = ++ yaffs_find_chunk_in_file(in, inode_chunk, ++ &existing_tags); ++ } ++ ++ if (existing_cunk <= 0) { ++ /*Hoosterman - how did this happen? */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: existing chunk < 0 in scan" ++ ); ++ ++ } ++ ++ /* NB The deleted flags should be false, otherwise ++ * the chunks will not be loaded during a scan ++ */ ++ ++ if (in_scan > 0) { ++ new_serial = new_tags.serial_number; ++ existing_serial = existing_tags.serial_number; ++ } ++ ++ if ((in_scan > 0) && ++ (existing_cunk <= 0 || ++ ((existing_serial + 1) & 3) == new_serial)) { ++ /* Forward scanning. ++ * Use new ++ * Delete the old one and drop through to ++ * update the tnode ++ */ ++ yaffs_chunk_del(dev, existing_cunk, 1, ++ __LINE__); ++ } else { ++ /* Backward scanning or we want to use the ++ * existing one ++ * Delete the new one and return early so that ++ * the tnode isn't changed ++ */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ } ++ ++ } ++ ++ if (existing_cunk == 0) ++ in->n_data_chunks++; ++ ++ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); ++ ++ return YAFFS_OK; ++} ++ ++static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) ++{ ++ struct yaffs_block_info *the_block; ++ unsigned block_no; ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); ++ ++ block_no = chunk / dev->param.chunks_per_block; ++ the_block = yaffs_get_block_info(dev, block_no); ++ if (the_block) { ++ the_block->soft_del_pages++; ++ dev->n_free_chunks++; ++ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); ++ } ++} ++ ++/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all ++ * the chunks in the file. ++ * All soft deleting does is increment the block's softdelete count and pulls ++ * the chunk out of the tnode. ++ * Thus, essentially this is the same as DeleteWorker except that the chunks ++ * are soft deleted. ++ */ ++ ++static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, ++ u32 level, int chunk_offset) ++{ ++ int i; ++ int the_chunk; ++ int all_done = 1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = YAFFS_NTNODES_INTERNAL - 1; ++ all_done && i >= 0; ++ i--) { ++ if (tn->internal[i]) { ++ all_done = ++ yaffs_soft_del_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) ++ + i); ++ if (all_done) { ++ yaffs_free_tnode(dev, ++ tn->internal[i]); ++ tn->internal[i] = NULL; ++ } else { ++ /* Can this happen? */ ++ } ++ } ++ } ++ return (all_done) ? 1 : 0; ++ } ++ ++ /* level 0 */ ++ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk) { ++ yaffs_soft_del_chunk(dev, the_chunk); ++ yaffs_load_tnode_0(dev, tn, i, 0); ++ } ++ } ++ return 1; ++} ++ ++static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_obj *parent; ++ ++ yaffs_verify_obj_in_dir(obj); ++ parent = obj->parent; ++ ++ yaffs_verify_dir(parent); ++ ++ if (dev && dev->param.remove_obj_fn) ++ dev->param.remove_obj_fn(obj); ++ ++ list_del_init(&obj->siblings); ++ obj->parent = NULL; ++ ++ yaffs_verify_dir(parent); ++} ++ ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) ++{ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a null pointer directory" ++ ); ++ BUG(); ++ return; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a non-directory" ++ ); ++ BUG(); ++ } ++ ++ if (obj->siblings.prev == NULL) { ++ /* Not initialised */ ++ BUG(); ++ } ++ ++ yaffs_verify_dir(directory); ++ ++ yaffs_remove_obj_from_dir(obj); ++ ++ /* Now add it */ ++ list_add(&obj->siblings, &directory->variant.dir_variant.children); ++ obj->parent = directory; ++ ++ if (directory == obj->my_dev->unlinked_dir ++ || directory == obj->my_dev->del_dir) { ++ obj->unlinked = 1; ++ obj->my_dev->n_unlinked_files++; ++ obj->rename_allowed = 0; ++ } ++ ++ yaffs_verify_dir(directory); ++ yaffs_verify_obj_in_dir(obj); ++} ++ ++static int yaffs_change_obj_name(struct yaffs_obj *obj, ++ struct yaffs_obj *new_dir, ++ const YCHAR *new_name, int force, int shadows) ++{ ++ int unlink_op; ++ int del_op; ++ struct yaffs_obj *existing_target; ++ ++ if (new_dir == NULL) ++ new_dir = obj->parent; /* use the old directory */ ++ ++ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_change_obj_name: new_dir is not a directory" ++ ); ++ BUG(); ++ } ++ ++ unlink_op = (new_dir == obj->my_dev->unlinked_dir); ++ del_op = (new_dir == obj->my_dev->del_dir); ++ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ ++ /* If the object is a file going into the unlinked directory, ++ * then it is OK to just stuff it in since duplicate names are OK. ++ * else only proceed if the new name does not exist and we're putting ++ * it into a directory. ++ */ ++ if (!(unlink_op || del_op || force || ++ shadows > 0 || !existing_target) || ++ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ return YAFFS_FAIL; ++ ++ yaffs_set_obj_name(obj, new_name); ++ obj->dirty = 1; ++ yaffs_add_obj_to_dir(new_dir, obj); ++ ++ if (unlink_op) ++ obj->unlinked = 1; ++ ++ /* If it is a deletion then we mark it as a shrink for gc */ ++ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++/*------------------------ Short Operations Cache ------------------------------ ++ * In many situations where there is no high level buffering a lot of ++ * reads might be short sequential reads, and a lot of writes may be short ++ * sequential writes. eg. scanning/writing a jpeg file. ++ * In these cases, a short read/write cache can provide a huge perfomance ++ * benefit with dumb-as-a-rock code. ++ * In Linux, the page cache provides read buffering and the short op cache ++ * provides write buffering. ++ * ++ * There are a small number (~10) of cache chunks per device so that we don't ++ * need a very intelligent search. ++ */ ++ ++static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ struct yaffs_cache *cache; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ for (i = 0; i < n_caches; i++) { ++ cache = &dev->cache[i]; ++ if (cache->object == obj && cache->dirty) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void yaffs_flush_single_cache(struct yaffs_cache *cache, int discard) ++{ ++ ++ if (!cache || cache->locked) ++ return; ++ ++ /* Write it out and free it up if need be.*/ ++ if (cache->dirty) { ++ yaffs_wr_data_obj(cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, ++ 1); ++ ++ cache->dirty = 0; ++ } ++ ++ if (discard) ++ cache->object = NULL; ++} ++ ++static void yaffs_flush_file_cache(struct yaffs_obj *obj, int discard) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ struct yaffs_cache *cache; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ if (n_caches < 1) ++ return; ++ ++ ++ /* Find the chunks for this object and flush them. */ ++ for (i = 0; i < n_caches; i++) { ++ cache = &dev->cache[i]; ++ if (cache->object == obj) ++ yaffs_flush_single_cache(cache, discard); ++ } ++ ++} ++ ++ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard) ++{ ++ struct yaffs_obj *obj; ++ int n_caches = dev->param.n_caches; ++ int i; ++ ++ /* Find a dirty object in the cache and flush it... ++ * until there are no further dirty objects. ++ */ ++ do { ++ obj = NULL; ++ for (i = 0; i < n_caches && !obj; i++) { ++ if (dev->cache[i].object && dev->cache[i].dirty) ++ obj = dev->cache[i].object; ++ } ++ if (obj) ++ yaffs_flush_file_cache(obj, discard); ++ } while (obj); ++ ++} ++ ++/* Grab us an unused cache chunk for use. ++ * First look for an empty one. ++ * Then look for the least recently used non-dirty one. ++ * Then look for the least recently used dirty one...., flush and look again. ++ */ ++static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->param.n_caches > 0) { ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (!dev->cache[i].object) ++ return &dev->cache[i]; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_cache *cache; ++ int usage; ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ /* First look for an unused cache */ ++ ++ cache = yaffs_grab_chunk_worker(dev); ++ ++ if (cache) ++ return cache; ++ ++ /* ++ * Thery were all in use. ++ * Find the LRU cache and flush it if it is dirty. ++ */ ++ ++ usage = -1; ++ cache = NULL; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object && ++ !dev->cache[i].locked && ++ (dev->cache[i].last_use < usage || !cache)) { ++ usage = dev->cache[i].last_use; ++ cache = &dev->cache[i]; ++ } ++ } ++ ++#if 1 ++ yaffs_flush_single_cache(cache, 1); ++#else ++ yaffs_flush_file_cache(cache->object, 1); ++ cache = yaffs_grab_chunk_worker(dev); ++#endif ++ ++ return cache; ++} ++ ++/* Find a cached chunk */ ++static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, ++ int chunk_id) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].chunk_id == chunk_id) { ++ dev->cache_hits++; ++ ++ return &dev->cache[i]; ++ } ++ } ++ return NULL; ++} ++ ++/* Mark the chunk for the least recently used algorithym */ ++static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, ++ int is_write) ++{ ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return; ++ ++ if (dev->cache_last_use < 0 || ++ dev->cache_last_use > 100000000) { ++ /* Reset the cache usages */ ++ for (i = 1; i < dev->param.n_caches; i++) ++ dev->cache[i].last_use = 0; ++ ++ dev->cache_last_use = 0; ++ } ++ dev->cache_last_use++; ++ cache->last_use = dev->cache_last_use; ++ ++ if (is_write) ++ cache->dirty = 1; ++} ++ ++/* Invalidate a single cache page. ++ * Do this when a whole page gets written, ++ * ie the short cache for this page is no longer valid. ++ */ ++static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) ++{ ++ struct yaffs_cache *cache; ++ ++ if (object->my_dev->param.n_caches > 0) { ++ cache = yaffs_find_chunk_cache(object, chunk_id); ++ ++ if (cache) ++ cache->object = NULL; ++ } ++} ++ ++/* Invalidate all the cache pages associated with this object ++ * Do this whenever ther file is deleted or resized. ++ */ ++static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.n_caches > 0) { ++ /* Invalidate it. */ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == in) ++ dev->cache[i].object = NULL; ++ } ++ } ++} ++ ++static void yaffs_unhash_obj(struct yaffs_obj *obj) ++{ ++ int bucket; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ /* If it is still linked into the bucket list, free from the list */ ++ if (!list_empty(&obj->hash_link)) { ++ list_del_init(&obj->hash_link); ++ bucket = yaffs_hash_fn(obj->obj_id); ++ dev->obj_bucket[bucket].count--; ++ } ++} ++ ++/* FreeObject frees up a Object and puts it back on the free list */ ++static void yaffs_free_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) { ++ BUG(); ++ return; ++ } ++ dev = obj->my_dev; ++ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", ++ obj, obj->my_inode); ++ if (obj->parent) ++ BUG(); ++ if (!list_empty(&obj->siblings)) ++ BUG(); ++ ++ if (obj->my_inode) { ++ /* We're still hooked up to a cached inode. ++ * Don't delete now, but mark for later deletion ++ */ ++ obj->defered_free = 1; ++ return; ++ } ++ ++ yaffs_unhash_obj(obj); ++ ++ yaffs_free_raw_obj(dev, obj); ++ dev->n_obj--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj) ++{ ++ if (obj->defered_free) ++ yaffs_free_obj(obj); ++} ++ ++static int yaffs_generic_obj_del(struct yaffs_obj *in) ++{ ++ /* Iinvalidate the file's data in the cache, without flushing. */ ++ yaffs_invalidate_whole_cache(in); ++ ++ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { ++ /* Move to unlinked directory so we have a deletion record */ ++ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, ++ 0); ++ } ++ ++ yaffs_remove_obj_from_dir(in); ++ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); ++ in->hdr_chunk = 0; ++ ++ yaffs_free_obj(in); ++ return YAFFS_OK; ++ ++} ++ ++static void yaffs_soft_del_file(struct yaffs_obj *obj) ++{ ++ if (!obj->deleted || ++ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || ++ obj->soft_del) ++ return; ++ ++ if (obj->n_data_chunks <= 0) { ++ /* Empty file with no duplicate object headers, ++ * just delete it immediately */ ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: Deleting empty file %d", ++ obj->obj_id); ++ yaffs_generic_obj_del(obj); ++ } else { ++ yaffs_soft_del_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant. ++ file_variant.top_level, 0); ++ obj->soft_del = 1; ++ } ++} ++ ++/* Pruning removes any part of the file structure tree that is beyond the ++ * bounds of the file (ie that does not point to chunks). ++ * ++ * A file should only get pruned when its size is reduced. ++ * ++ * Before pruning, the chunks must be pulled from the tree and the ++ * level 0 tnode entries must be zeroed out. ++ * Could also use this for file deletion, but that's probably better handled ++ * by a special case. ++ * ++ * This function is recursive. For levels > 0 the function is called again on ++ * any sub-tree. For level == 0 we just check if the sub-tree has data. ++ * If there is no data in a subtree then it is pruned. ++ */ ++ ++static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, ++ struct yaffs_tnode *tn, u32 level, ++ int del0) ++{ ++ int i; ++ int has_data; ++ ++ if (!tn) ++ return tn; ++ ++ has_data = 0; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) { ++ tn->internal[i] = ++ yaffs_prune_worker(dev, ++ tn->internal[i], ++ level - 1, ++ (i == 0) ? del0 : 1); ++ } ++ ++ if (tn->internal[i]) ++ has_data++; ++ } ++ } else { ++ int tnode_size_u32 = dev->tnode_size / sizeof(u32); ++ u32 *map = (u32 *) tn; ++ ++ for (i = 0; !has_data && i < tnode_size_u32; i++) { ++ if (map[i]) ++ has_data++; ++ } ++ } ++ ++ if (has_data == 0 && del0) { ++ /* Free and return NULL */ ++ yaffs_free_tnode(dev, tn); ++ tn = NULL; ++ } ++ return tn; ++} ++ ++static int yaffs_prune_tree(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct) ++{ ++ int i; ++ int has_data; ++ int done = 0; ++ struct yaffs_tnode *tn; ++ ++ if (file_struct->top_level < 1) ++ return YAFFS_OK; ++ ++ file_struct->top = ++ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); ++ ++ /* Now we have a tree with all the non-zero branches NULL but ++ * the height is the same as it was. ++ * Let's see if we can trim internal tnodes to shorten the tree. ++ * We can do this if only the 0th element in the tnode is in use ++ * (ie all the non-zero are NULL) ++ */ ++ ++ while (file_struct->top_level && !done) { ++ tn = file_struct->top; ++ ++ has_data = 0; ++ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) ++ has_data++; ++ } ++ ++ if (!has_data) { ++ file_struct->top = tn->internal[0]; ++ file_struct->top_level--; ++ yaffs_free_tnode(dev, tn); ++ } else { ++ done = 1; ++ } ++ } ++ ++ return YAFFS_OK; ++} ++ ++/*-------------------- End of File Structure functions.-------------------*/ ++ ++/* alloc_empty_obj gets us a clean Object.*/ ++static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); ++ ++ if (!obj) ++ return obj; ++ ++ dev->n_obj++; ++ ++ /* Now sweeten it up... */ ++ ++ memset(obj, 0, sizeof(struct yaffs_obj)); ++ obj->being_created = 1; ++ ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; ++ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; ++ INIT_LIST_HEAD(&(obj->hard_links)); ++ INIT_LIST_HEAD(&(obj->hash_link)); ++ INIT_LIST_HEAD(&obj->siblings); ++ ++ /* Now make the directory sane */ ++ if (dev->root_dir) { ++ obj->parent = dev->root_dir; ++ list_add(&(obj->siblings), ++ &dev->root_dir->variant.dir_variant.children); ++ } ++ ++ /* Add it to the lost and found directory. ++ * NB Can't put root or lost-n-found in lost-n-found so ++ * check if lost-n-found exists first ++ */ ++ if (dev->lost_n_found) ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ ++ obj->being_created = 0; ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return obj; ++} ++ ++static int yaffs_find_nice_bucket(struct yaffs_dev *dev) ++{ ++ int i; ++ int l = 999; ++ int lowest = 999999; ++ ++ /* Search for the shortest list or one that ++ * isn't too long. ++ */ ++ ++ for (i = 0; i < 10 && lowest > 4; i++) { ++ dev->bucket_finder++; ++ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; ++ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { ++ lowest = dev->obj_bucket[dev->bucket_finder].count; ++ l = dev->bucket_finder; ++ } ++ } ++ ++ return l; ++} ++ ++static int yaffs_new_obj_id(struct yaffs_dev *dev) ++{ ++ int bucket = yaffs_find_nice_bucket(dev); ++ int found = 0; ++ struct list_head *i; ++ u32 n = (u32) bucket; ++ ++ /* ++ * Now find an object value that has not already been taken ++ * by scanning the list, incrementing each time by number of buckets. ++ */ ++ while (!found) { ++ found = 1; ++ n += YAFFS_NOBJECT_BUCKETS; ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* Check if this value is already taken. */ ++ if (i && list_entry(i, struct yaffs_obj, ++ hash_link)->obj_id == n) ++ found = 0; ++ } ++ } ++ return n; ++} ++ ++static void yaffs_hash_obj(struct yaffs_obj *in) ++{ ++ int bucket = yaffs_hash_fn(in->obj_id); ++ struct yaffs_dev *dev = in->my_dev; ++ ++ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); ++ dev->obj_bucket[bucket].count++; ++} ++ ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) ++{ ++ int bucket = yaffs_hash_fn(number); ++ struct list_head *i; ++ struct yaffs_obj *in; ++ ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* Look if it is in the list */ ++ in = list_entry(i, struct yaffs_obj, hash_link); ++ if (in->obj_id == number) { ++ /* Don't show if it is defered free */ ++ if (in->defered_free) ++ return NULL; ++ return in; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (number < 0) ++ number = yaffs_new_obj_id(dev); ++ ++ if (type == YAFFS_OBJECT_TYPE_FILE) { ++ tn = yaffs_get_tnode(dev); ++ if (!tn) ++ return NULL; ++ } ++ ++ the_obj = yaffs_alloc_empty_obj(dev); ++ if (!the_obj) { ++ if (tn) ++ yaffs_free_tnode(dev, tn); ++ return NULL; ++ } ++ ++ the_obj->fake = 0; ++ the_obj->rename_allowed = 1; ++ the_obj->unlink_allowed = 1; ++ the_obj->obj_id = number; ++ yaffs_hash_obj(the_obj); ++ the_obj->variant_type = type; ++ yaffs_load_current_time(the_obj, 1, 1); ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ the_obj->variant.file_variant.file_size = 0; ++ the_obj->variant.file_variant.scanned_size = 0; ++ the_obj->variant.file_variant.shrink_size = ++ yaffs_max_file_size(dev); ++ the_obj->variant.file_variant.top_level = 0; ++ the_obj->variant.file_variant.top = tn; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* No action required */ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* todo this should not happen */ ++ break; ++ } ++ return the_obj; ++} ++ ++static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, ++ int number, u32 mode) ++{ ++ ++ struct yaffs_obj *obj = ++ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); ++ ++ if (!obj) ++ return NULL; ++ ++ obj->fake = 1; /* it is fake so it might not use NAND */ ++ obj->rename_allowed = 0; ++ obj->unlink_allowed = 0; ++ obj->deleted = 0; ++ obj->unlinked = 0; ++ obj->yst_mode = mode; ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; /* Not a valid chunk. */ ++ return obj; ++ ++} ++ ++ ++static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++ yaffs_init_raw_tnodes_and_objs(dev); ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ INIT_LIST_HEAD(&dev->obj_bucket[i].list); ++ dev->obj_bucket[i].count = 0; ++ } ++} ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ ++ if (number > 0) ++ the_obj = yaffs_find_by_number(dev, number); ++ ++ if (!the_obj) ++ the_obj = yaffs_new_obj(dev, number, type); ++ ++ return the_obj; ++ ++} ++ ++YCHAR *yaffs_clone_str(const YCHAR *str) ++{ ++ YCHAR *new_str = NULL; ++ int len; ++ ++ if (!str) ++ str = _Y(""); ++ ++ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); ++ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); ++ if (new_str) { ++ strncpy(new_str, str, len); ++ new_str[len] = 0; ++ } ++ return new_str; ++ ++} ++/* ++ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new ++ * link (ie. name) is created or deleted in the directory. ++ * ++ * ie. ++ * create dir/a : update dir's mtime/ctime ++ * rm dir/a: update dir's mtime/ctime ++ * modify dir/a: don't update dir's mtimme/ctime ++ * ++ * This can be handled immediately or defered. Defering helps reduce the number ++ * of updates when many files in a directory are changed within a brief period. ++ * ++ * If the directory updating is defered then yaffs_update_dirty_dirs must be ++ * called periodically. ++ */ ++ ++static void yaffs_update_parent(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) ++ return; ++ dev = obj->my_dev; ++ obj->dirty = 1; ++ yaffs_load_current_time(obj, 0, 1); ++ if (dev->param.defered_dir_update) { ++ struct list_head *link = &obj->variant.dir_variant.dirty; ++ ++ if (list_empty(link)) { ++ list_add(link, &dev->dirty_dirs); ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Added object %d to dirty directories", ++ obj->obj_id); ++ } ++ ++ } else { ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev) ++{ ++ struct list_head *link; ++ struct yaffs_obj *obj; ++ struct yaffs_dir_var *d_s; ++ union yaffs_obj_var *o_v; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); ++ ++ while (!list_empty(&dev->dirty_dirs)) { ++ link = dev->dirty_dirs.next; ++ list_del_init(link); ++ ++ d_s = list_entry(link, struct yaffs_dir_var, dirty); ++ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); ++ obj = list_entry(o_v, struct yaffs_obj, variant); ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", ++ obj->obj_id); ++ ++ if (obj->dirty) ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++/* ++ * Mknod (create) a new object. ++ * equiv_obj only has meaning for a hard link; ++ * alias_str only has meaning for a symlink. ++ * rdev only has meaning for devices (a subset of special objects) ++ */ ++ ++static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, ++ struct yaffs_obj *parent, ++ const YCHAR *name, ++ u32 mode, ++ u32 uid, ++ u32 gid, ++ struct yaffs_obj *equiv_obj, ++ const YCHAR *alias_str, u32 rdev) ++{ ++ struct yaffs_obj *in; ++ YCHAR *str = NULL; ++ struct yaffs_dev *dev = parent->my_dev; ++ ++ /* Check if the entry exists. ++ * If it does then fail the call since we don't want a dup. */ ++ if (yaffs_find_by_name(parent, name)) ++ return NULL; ++ ++ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ str = yaffs_clone_str(alias_str); ++ if (!str) ++ return NULL; ++ } ++ ++ in = yaffs_new_obj(dev, -1, type); ++ ++ if (!in) { ++ kfree(str); ++ return NULL; ++ } ++ ++ in->hdr_chunk = 0; ++ in->valid = 1; ++ in->variant_type = type; ++ ++ in->yst_mode = mode; ++ ++ yaffs_attribs_init(in, gid, uid, rdev); ++ ++ in->n_data_chunks = 0; ++ ++ yaffs_set_obj_name(in, name); ++ in->dirty = 1; ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ in->my_dev = parent->my_dev; ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant.alias = str; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant.hardlink_variant.equiv_obj = equiv_obj; ++ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; ++ list_add(&in->hard_links, &equiv_obj->hard_links); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* do nothing */ ++ break; ++ } ++ ++ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { ++ /* Could not create the object header, fail */ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ if (in) ++ yaffs_update_parent(parent); ++ ++ return in; ++} ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, ++ uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, ++ mode, uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, ++ uid, gid, NULL, NULL, rdev); ++} ++ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, ++ uid, gid, NULL, alias, 0); ++} ++ ++/* yaffs_link_obj returns the object id of the equivalent object.*/ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, ++ struct yaffs_obj *equiv_obj) ++{ ++ /* Get the real object in case we were fed a hard link obj */ ++ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); ++ ++ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, ++ parent, name, 0, 0, 0, ++ equiv_obj, NULL, 0)) ++ return equiv_obj; ++ ++ return NULL; ++ ++} ++ ++ ++ ++/*---------------------- Block Management and Page Allocation -------------*/ ++ ++static void yaffs_deinit_blocks(struct yaffs_dev *dev) ++{ ++ if (dev->block_info_alt && dev->block_info) ++ vfree(dev->block_info); ++ else ++ kfree(dev->block_info); ++ ++ dev->block_info_alt = 0; ++ ++ dev->block_info = NULL; ++ ++ if (dev->chunk_bits_alt && dev->chunk_bits) ++ vfree(dev->chunk_bits); ++ else ++ kfree(dev->chunk_bits); ++ dev->chunk_bits_alt = 0; ++ dev->chunk_bits = NULL; ++} ++ ++static int yaffs_init_blocks(struct yaffs_dev *dev) ++{ ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ dev->block_info = NULL; ++ dev->chunk_bits = NULL; ++ dev->alloc_block = -1; /* force it to get a new one */ ++ ++ /* If the first allocation strategy fails, thry the alternate one */ ++ dev->block_info = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); ++ if (!dev->block_info) { ++ dev->block_info = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); ++ dev->block_info_alt = 1; ++ } else { ++ dev->block_info_alt = 0; ++ } ++ ++ if (!dev->block_info) ++ goto alloc_error; ++ ++ /* Set up dynamic blockinfo stuff. Round up bytes. */ ++ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; ++ dev->chunk_bits = ++ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); ++ if (!dev->chunk_bits) { ++ dev->chunk_bits = ++ vmalloc(dev->chunk_bit_stride * n_blocks); ++ dev->chunk_bits_alt = 1; ++ } else { ++ dev->chunk_bits_alt = 0; ++ } ++ if (!dev->chunk_bits) ++ goto alloc_error; ++ ++ ++ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); ++ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); ++ return YAFFS_OK; ++ ++alloc_error: ++ yaffs_deinit_blocks(dev); ++ return YAFFS_FAIL; ++} ++ ++ ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); ++ int erased_ok = 0; ++ int i; ++ ++ /* If the block is still healthy erase it and mark as clean. ++ * If the block has had a data failure, then retire it. ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, ++ "yaffs_block_became_dirty block %d state %d %s", ++ block_no, bi->block_state, ++ (bi->needs_retiring) ? "needs retiring" : ""); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; ++ ++ /* If this is the block being garbage collected then stop gc'ing */ ++ if (block_no == dev->gc_block) ++ dev->gc_block = 0; ++ ++ /* If this block is currently the best candidate for gc ++ * then drop as a candidate */ ++ if (block_no == dev->gc_dirtiest) { ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ } ++ ++ if (!bi->needs_retiring) { ++ yaffs2_checkpt_invalidate(dev); ++ erased_ok = yaffs_erase_block(dev, block_no); ++ if (!erased_ok) { ++ dev->n_erase_failures++; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Erasure failed %d", block_no); ++ } ++ } ++ ++ /* Verify erasure if needed */ ++ if (erased_ok && ++ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || ++ !yaffs_skip_verification(dev))) { ++ for (i = 0; i < dev->param.chunks_per_block; i++) { ++ if (!yaffs_check_chunk_erased(dev, ++ block_no * dev->param.chunks_per_block + i)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ ">>Block %d erasure supposedly OK, but chunk %d not erased", ++ block_no, i); ++ } ++ } ++ } ++ ++ if (!erased_ok) { ++ /* We lost a block of free space */ ++ dev->n_free_chunks -= dev->param.chunks_per_block; ++ yaffs_retire_block(dev, block_no); ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d retired", block_no); ++ return; ++ } ++ ++ /* Clean it up... */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ bi->seq_number = 0; ++ dev->n_erased_blocks++; ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ bi->has_shrink_hdr = 0; ++ bi->skip_erased_check = 1; /* Clean, so no need to check */ ++ bi->gc_prioritise = 0; ++ bi->has_summary = 0; ++ ++ yaffs_clear_chunk_bits(dev, block_no); ++ ++ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); ++} ++ ++static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int old_chunk, u8 *buffer) ++{ ++ int new_chunk; ++ int mark_flash = 1; ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj *object; ++ int matching_chunk; ++ int ret_val = YAFFS_OK; ++ ++ memset(&tags, 0, sizeof(tags)); ++ yaffs_rd_chunk_tags_nand(dev, old_chunk, ++ buffer, &tags); ++ object = yaffs_find_by_number(dev, tags.obj_id); ++ ++ yaffs_trace(YAFFS_TRACE_GC_DETAIL, ++ "Collecting chunk in block %d, %d %d %d ", ++ dev->gc_chunk, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ ++ if (object && !yaffs_skip_verification(dev)) { ++ if (tags.chunk_id == 0) ++ matching_chunk = ++ object->hdr_chunk; ++ else if (object->soft_del) ++ /* Defeat the test */ ++ matching_chunk = old_chunk; ++ else ++ matching_chunk = ++ yaffs_find_chunk_in_file ++ (object, tags.chunk_id, ++ NULL); ++ ++ if (old_chunk != matching_chunk) ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "gc: page in gc mismatch: %d %d %d %d", ++ old_chunk, ++ matching_chunk, ++ tags.obj_id, ++ tags.chunk_id); ++ } ++ ++ if (!object) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "page %d in gc has no object: %d %d %d ", ++ old_chunk, ++ tags.obj_id, tags.chunk_id, ++ tags.n_bytes); ++ } ++ ++ if (object && ++ object->deleted && ++ object->soft_del && tags.chunk_id != 0) { ++ /* Data chunk in a soft deleted file, ++ * throw it away. ++ * It's a soft deleted data chunk, ++ * No need to copy this, just forget ++ * about it and fix up the object. ++ */ ++ ++ /* Free chunks already includes ++ * softdeleted chunks, how ever this ++ * chunk is going to soon be really ++ * deleted which will increment free ++ * chunks. We have to decrement free ++ * chunks so this works out properly. ++ */ ++ dev->n_free_chunks--; ++ bi->soft_del_pages--; ++ ++ object->n_data_chunks--; ++ if (object->n_data_chunks <= 0) { ++ /* remeber to clean up obj */ ++ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; ++ dev->n_clean_ups++; ++ } ++ mark_flash = 0; ++ } else if (object) { ++ /* It's either a data chunk in a live ++ * file or an ObjectHeader, so we're ++ * interested in it. ++ * NB Need to keep the ObjectHeaders of ++ * deleted files until the whole file ++ * has been deleted off ++ */ ++ tags.serial_number++; ++ dev->n_gc_copies++; ++ ++ if (tags.chunk_id == 0) { ++ /* It is an object Id, ++ * We need to nuke the ++ * shrinkheader flags since its ++ * work is done. ++ * Also need to clean up ++ * shadowing. ++ */ ++ struct yaffs_obj_hdr *oh; ++ oh = (struct yaffs_obj_hdr *) buffer; ++ ++ oh->is_shrink = 0; ++ tags.extra_is_shrink = 0; ++ oh->shadows_obj = 0; ++ oh->inband_shadowed_obj_id = 0; ++ tags.extra_shadows = 0; ++ ++ /* Update file size */ ++ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { ++ yaffs_oh_size_load(oh, ++ object->variant.file_variant.file_size); ++ tags.extra_file_size = ++ object->variant.file_variant.file_size; ++ } ++ ++ yaffs_verify_oh(object, oh, &tags, 1); ++ new_chunk = ++ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); ++ } else { ++ new_chunk = ++ yaffs_write_new_chunk(dev, buffer, &tags, 1); ++ } ++ ++ if (new_chunk < 0) { ++ ret_val = YAFFS_FAIL; ++ } else { ++ ++ /* Now fix up the Tnodes etc. */ ++ ++ if (tags.chunk_id == 0) { ++ /* It's a header */ ++ object->hdr_chunk = new_chunk; ++ object->serial = tags.serial_number; ++ } else { ++ /* It's a data chunk */ ++ yaffs_put_chunk_in_file(object, tags.chunk_id, ++ new_chunk, 0); ++ } ++ } ++ } ++ if (ret_val == YAFFS_OK) ++ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); ++ return ret_val; ++} ++ ++static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) ++{ ++ int old_chunk; ++ int ret_val = YAFFS_OK; ++ int i; ++ int is_checkpt_block; ++ int max_copies; ++ int chunks_before = yaffs_get_erased_chunks(dev); ++ int chunks_after; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); ++ ++ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d, in use %d, shrink %d, whole_block %d", ++ block, bi->pages_in_use, bi->has_shrink_hdr, ++ whole_block); ++ ++ /*yaffs_verify_free_chunks(dev); */ ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; ++ ++ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ ++ ++ dev->gc_disable = 1; ++ ++ yaffs_summary_gc(dev, block); ++ ++ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d that has no chunks in use", ++ block); ++ yaffs_block_became_dirty(dev, block); ++ } else { ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_verify_blk(dev, bi, block); ++ ++ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; ++ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; ++ ++ for (/* init already done */ ; ++ ret_val == YAFFS_OK && ++ dev->gc_chunk < dev->param.chunks_per_block && ++ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && ++ max_copies > 0; ++ dev->gc_chunk++, old_chunk++) { ++ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { ++ /* Page is in use and might need to be copied */ ++ max_copies--; ++ ret_val = yaffs_gc_process_chunk(dev, bi, ++ old_chunk, buffer); ++ } ++ } ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ yaffs_verify_collected_blk(dev, bi, block); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ /* ++ * The gc did not complete. Set block state back to FULL ++ * because checkpointing does not restore gc. ++ */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } else { ++ /* The gc completed. */ ++ /* Do any required cleanups */ ++ for (i = 0; i < dev->n_clean_ups; i++) { ++ /* Time to delete the file too */ ++ struct yaffs_obj *object = ++ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); ++ if (object) { ++ yaffs_free_tnode(dev, ++ object->variant.file_variant.top); ++ object->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: About to finally delete object %d", ++ object->obj_id); ++ yaffs_generic_obj_del(object); ++ object->my_dev->n_deleted_files--; ++ } ++ ++ } ++ chunks_after = yaffs_get_erased_chunks(dev); ++ if (chunks_before >= chunks_after) ++ yaffs_trace(YAFFS_TRACE_GC, ++ "gc did not increase free chunks before %d after %d", ++ chunks_before, chunks_after); ++ dev->gc_block = 0; ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ dev->gc_disable = 0; ++ ++ return ret_val; ++} ++ ++/* ++ * find_gc_block() selects the dirtiest block (or close enough) ++ * for garbage collection. ++ */ ++ ++static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, ++ int aggressive, int background) ++{ ++ int i; ++ int iterations; ++ unsigned selected = 0; ++ int prioritised = 0; ++ int prioritised_exist = 0; ++ struct yaffs_block_info *bi; ++ int threshold; ++ ++ /* First let's see if we need to grab a prioritised block */ ++ if (dev->has_pending_prioritised_gc && !aggressive) { ++ dev->gc_dirtiest = 0; ++ bi = dev->block_info; ++ for (i = dev->internal_start_block; ++ i <= dev->internal_end_block && !selected; i++) { ++ ++ if (bi->gc_prioritise) { ++ prioritised_exist = 1; ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ selected = i; ++ prioritised = 1; ++ } ++ } ++ bi++; ++ } ++ ++ /* ++ * If there is a prioritised block and none was selected then ++ * this happened because there is at least one old dirty block ++ * gumming up the works. Let's gc the oldest dirty block. ++ */ ++ ++ if (prioritised_exist && ++ !selected && dev->oldest_dirty_block > 0) ++ selected = dev->oldest_dirty_block; ++ ++ if (!prioritised_exist) /* None found, so we can clear this */ ++ dev->has_pending_prioritised_gc = 0; ++ } ++ ++ /* If we're doing aggressive GC then we are happy to take a less-dirty ++ * block, and search harder. ++ * else (leasurely gc), then we only bother to do this if the ++ * block has only a few pages in use. ++ */ ++ ++ if (!selected) { ++ int pages_used; ++ int n_blocks = ++ dev->internal_end_block - dev->internal_start_block + 1; ++ if (aggressive) { ++ threshold = dev->param.chunks_per_block; ++ iterations = n_blocks; ++ } else { ++ int max_threshold; ++ ++ if (background) ++ max_threshold = dev->param.chunks_per_block / 2; ++ else ++ max_threshold = dev->param.chunks_per_block / 8; ++ ++ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ ++ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; ++ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ if (threshold > max_threshold) ++ threshold = max_threshold; ++ ++ iterations = n_blocks / 16 + 1; ++ if (iterations > 100) ++ iterations = 100; ++ } ++ ++ for (i = 0; ++ i < iterations && ++ (dev->gc_dirtiest < 1 || ++ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); ++ i++) { ++ dev->gc_block_finder++; ++ if (dev->gc_block_finder < dev->internal_start_block || ++ dev->gc_block_finder > dev->internal_end_block) ++ dev->gc_block_finder = ++ dev->internal_start_block; ++ ++ bi = yaffs_get_block_info(dev, dev->gc_block_finder); ++ ++ pages_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ pages_used < dev->param.chunks_per_block && ++ (dev->gc_dirtiest < 1 || ++ pages_used < dev->gc_pages_in_use) && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ dev->gc_dirtiest = dev->gc_block_finder; ++ dev->gc_pages_in_use = pages_used; ++ } ++ } ++ ++ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) ++ selected = dev->gc_dirtiest; ++ } ++ ++ /* ++ * If nothing has been selected for a while, try the oldest dirty ++ * because that's gumming up the works. ++ */ ++ ++ if (!selected && dev->param.is_yaffs2 && ++ dev->gc_not_done >= (background ? 10 : 20)) { ++ yaffs2_find_oldest_dirty_seq(dev); ++ if (dev->oldest_dirty_block > 0) { ++ selected = dev->oldest_dirty_block; ++ dev->gc_dirtiest = selected; ++ dev->oldest_dirty_gc_count++; ++ bi = yaffs_get_block_info(dev, selected); ++ dev->gc_pages_in_use = ++ bi->pages_in_use - bi->soft_del_pages; ++ } else { ++ dev->gc_not_done = 0; ++ } ++ } ++ ++ if (selected) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC Selected block %d with %d free, prioritised:%d", ++ selected, ++ dev->param.chunks_per_block - dev->gc_pages_in_use, ++ prioritised); ++ ++ dev->n_gc_blocks++; ++ if (background) ++ dev->bg_gcs++; ++ ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ dev->gc_not_done = 0; ++ if (dev->refresh_skip > 0) ++ dev->refresh_skip--; ++ } else { ++ dev->gc_not_done++; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", ++ dev->gc_block_finder, dev->gc_not_done, threshold, ++ dev->gc_dirtiest, dev->gc_pages_in_use, ++ dev->oldest_dirty_block, background ? " bg" : ""); ++ } ++ ++ return selected; ++} ++ ++/* New garbage collector ++ * If we're very low on erased blocks then we do aggressive garbage collection ++ * otherwise we do "leasurely" garbage collection. ++ * Aggressive gc looks further (whole array) and will accept less dirty blocks. ++ * Passive gc only inspects smaller areas and only accepts more dirty blocks. ++ * ++ * The idea is to help clear out space in a more spread-out manner. ++ * Dunno if it really does anything useful. ++ */ ++static int yaffs_check_gc(struct yaffs_dev *dev, int background) ++{ ++ int aggressive = 0; ++ int gc_ok = YAFFS_OK; ++ int max_tries = 0; ++ int min_erased; ++ int erased_chunks; ++ int checkpt_block_adjust; ++ ++ if (dev->param.gc_control_fn && ++ (dev->param.gc_control_fn(dev) & 1) == 0) ++ return YAFFS_OK; ++ ++ if (dev->gc_disable) ++ /* Bail out so we don't get recursive gc */ ++ return YAFFS_OK; ++ ++ /* This loop should pass the first time. ++ * Only loops here if the collection does not increase space. ++ */ ++ ++ do { ++ max_tries++; ++ ++ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); ++ ++ min_erased = ++ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; ++ erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ /* If we need a block soon then do aggressive gc. */ ++ if (dev->n_erased_blocks < min_erased) ++ aggressive = 1; ++ else { ++ if (!background ++ && erased_chunks > (dev->n_free_chunks / 4)) ++ break; ++ ++ if (dev->gc_skip > 20) ++ dev->gc_skip = 20; ++ if (erased_chunks < dev->n_free_chunks / 2 || ++ dev->gc_skip < 1 || background) ++ aggressive = 0; ++ else { ++ dev->gc_skip--; ++ break; ++ } ++ } ++ ++ dev->gc_skip = 5; ++ ++ /* If we don't already have a block being gc'd then see if we ++ * should start another */ ++ ++ if (dev->gc_block < 1 && !aggressive) { ++ dev->gc_block = yaffs2_find_refresh_block(dev); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ if (dev->gc_block < 1) { ++ dev->gc_block = ++ yaffs_find_gc_block(dev, aggressive, background); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ if (dev->gc_block > 0) { ++ dev->all_gcs++; ++ if (!aggressive) ++ dev->passive_gc_count++; ++ ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC n_erased_blocks %d aggressive %d", ++ dev->n_erased_blocks, aggressive); ++ ++ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); ++ } ++ ++ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && ++ dev->gc_block > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", ++ dev->n_erased_blocks, max_tries, ++ dev->gc_block); ++ } ++ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && ++ (dev->gc_block > 0) && (max_tries < 2)); ++ ++ return aggressive ? gc_ok : YAFFS_OK; ++} ++ ++/* ++ * yaffs_bg_gc() ++ * Garbage collects. Intended to be called from a background thread. ++ * Returns non-zero if at least half the free chunks are erased. ++ */ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) ++{ ++ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); ++ ++ yaffs_check_gc(dev, 1); ++ return erased_chunks > dev->n_free_chunks / 2; ++} ++ ++/*-------------------- Data file manipulation -----------------*/ ++ ++static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) ++{ ++ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); ++ ++ if (nand_chunk >= 0) ++ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, ++ buffer, NULL); ++ else { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not found zero instead", ++ nand_chunk); ++ /* get sane (zero) data if you read a hole */ ++ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); ++ return 0; ++ } ++ ++} ++ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn) ++{ ++ int block; ++ int page; ++ struct yaffs_ext_tags tags; ++ struct yaffs_block_info *bi; ++ ++ if (chunk_id <= 0) ++ return; ++ ++ dev->n_deletions++; ++ block = chunk_id / dev->param.chunks_per_block; ++ page = chunk_id % dev->param.chunks_per_block; ++ ++ if (!yaffs_check_chunk_bit(dev, block, page)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Deleting invalid chunk %d", chunk_id); ++ ++ bi = yaffs_get_block_info(dev, block); ++ ++ yaffs2_update_oldest_dirty_seq(dev, block, bi); ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, ++ "line %d delete of chunk %d", ++ lyn, chunk_id); ++ ++ if (!dev->param.is_yaffs2 && mark_flash && ++ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { ++ ++ memset(&tags, 0, sizeof(tags)); ++ tags.is_deleted = 1; ++ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); ++ yaffs_handle_chunk_update(dev, chunk_id, &tags); ++ } else { ++ dev->n_unmarked_deletions++; ++ } ++ ++ /* Pull out of the management area. ++ * If the whole block became dirty, this will kick off an erasure. ++ */ ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || ++ bi->block_state == YAFFS_BLOCK_STATE_FULL || ++ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ dev->n_free_chunks++; ++ yaffs_clear_chunk_bit(dev, block, page); ++ bi->pages_in_use--; ++ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && ++ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ yaffs_block_became_dirty(dev, block); ++ } ++ } ++} ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve) ++{ ++ /* Find old chunk Need to do this to get serial number ++ * Write new one and patch into tree. ++ * Invalidate old tags. ++ */ ++ ++ int prev_chunk_id; ++ struct yaffs_ext_tags prev_tags; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ yaffs_check_gc(dev, 0); ++ ++ /* Get the previous chunk at this location in the file if it exists. ++ * If it does not exist then put a zero into the tree. This creates ++ * the tnode now, rather than later when it is harder to clean up. ++ */ ++ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); ++ if (prev_chunk_id < 1 && ++ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) ++ return 0; ++ ++ /* Set up new tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ ++ new_tags.chunk_id = inode_chunk; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = ++ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; ++ new_tags.n_bytes = n_bytes; ++ ++ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Writing %d bytes to chunk!!!!!!!!!", ++ n_bytes); ++ BUG(); ++ } ++ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); ++ ++ if (new_chunk_id > 0) { ++ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ yaffs_verify_file_sane(in); ++ } ++ return new_chunk_id; ++ ++} ++ ++ ++ ++static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, ++ const YCHAR *name, const void *value, int size, ++ int flags) ++{ ++ struct yaffs_xattr_mod xmod; ++ int result; ++ ++ xmod.set = set; ++ xmod.name = name; ++ xmod.data = value; ++ xmod.size = size; ++ xmod.flags = flags; ++ xmod.result = -ENOSPC; ++ ++ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); ++ ++ if (result > 0) ++ return xmod.result; ++ else ++ return -ENOSPC; ++} ++ ++static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, ++ struct yaffs_xattr_mod *xmod) ++{ ++ int retval = 0; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer = buffer + x_offs; ++ ++ if (xmod->set) ++ retval = ++ nval_set(x_buffer, x_size, xmod->name, xmod->data, ++ xmod->size, xmod->flags); ++ else ++ retval = nval_del(x_buffer, x_size, xmod->name); ++ ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ xmod->result = retval; ++ ++ return retval; ++} ++ ++static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, ++ void *value, int size) ++{ ++ char *buffer = NULL; ++ int result; ++ struct yaffs_ext_tags tags; ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer; ++ int retval = 0; ++ ++ if (obj->hdr_chunk < 1) ++ return -ENODATA; ++ ++ /* If we know that the object has no xattribs then don't do all the ++ * reading and parsing. ++ */ ++ if (obj->xattr_known && !obj->has_xattr) { ++ if (name) ++ return -ENODATA; ++ else ++ return 0; ++ } ++ ++ buffer = (char *)yaffs_get_temp_buffer(dev); ++ if (!buffer) ++ return -ENOMEM; ++ ++ result = ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ retval = -ENOENT; ++ else { ++ x_buffer = buffer + x_offs; ++ ++ if (!obj->xattr_known) { ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ } ++ ++ if (name) ++ retval = nval_get(x_buffer, x_size, name, value, size); ++ else ++ retval = nval_list(x_buffer, x_size, value, size); ++ } ++ yaffs_release_temp_buffer(dev, (u8 *) buffer); ++ return retval; ++} ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, ++ const void *value, int size, int flags) ++{ ++ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); ++} ++ ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); ++} ++ ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, ++ int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, name, value, size); ++} ++ ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); ++} ++ ++static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) ++{ ++ u8 *buf; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ ++ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) ++ return; ++ ++ dev = in->my_dev; ++ in->lazy_loaded = 0; ++ buf = yaffs_get_temp_buffer(dev); ++ ++ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); ++ oh = (struct yaffs_obj_hdr *)buf; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ yaffs_set_obj_name_from_oh(in, oh); ++ ++ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ in->variant.symlink_variant.alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant.symlink_variant.alias) ++ alloc_failed = 1; /* Not returned */ ++ } ++ yaffs_release_temp_buffer(dev, buf); ++} ++ ++/* UpdateObjectHeader updates the header on NAND for an object. ++ * If name is not NULL, then that new name is used. ++ */ ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, ++ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) ++{ ++ ++ struct yaffs_block_info *bi; ++ struct yaffs_dev *dev = in->my_dev; ++ int prev_chunk_id; ++ int ret_val = 0; ++ int result = 0; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_ext_tags old_tags; ++ const YCHAR *alias = NULL; ++ u8 *buffer = NULL; ++ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj_hdr *oh = NULL; ++ loff_t file_size = 0; ++ ++ strcpy(old_name, _Y("silly old name")); ++ ++ if (in->fake && in != dev->root_dir && !force && !xmod) ++ return ret_val; ++ ++ yaffs_check_gc(dev, 0); ++ yaffs_check_obj_details_loaded(in); ++ ++ buffer = yaffs_get_temp_buffer(in->my_dev); ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ prev_chunk_id = in->hdr_chunk; ++ ++ if (prev_chunk_id > 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, ++ buffer, &old_tags); ++ ++ yaffs_verify_oh(in, oh, &old_tags, 0); ++ memcpy(old_name, oh->name, sizeof(oh->name)); ++ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); ++ } else { ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ } ++ ++ oh->type = in->variant_type; ++ oh->yst_mode = in->yst_mode; ++ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; ++ ++ yaffs_load_attribs_oh(oh, in); ++ ++ if (in->parent) ++ oh->parent_obj_id = in->parent->obj_id; ++ else ++ oh->parent_obj_id = 0; ++ ++ if (name && *name) { ++ memset(oh->name, 0, sizeof(oh->name)); ++ yaffs_load_oh_from_name(dev, oh->name, name); ++ } else if (prev_chunk_id > 0) { ++ memcpy(oh->name, old_name, sizeof(oh->name)); ++ } else { ++ memset(oh->name, 0, sizeof(oh->name)); ++ } ++ ++ oh->is_shrink = is_shrink; ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Should not happen */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && ++ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) ++ file_size = in->variant.file_variant.file_size; ++ yaffs_oh_size_load(oh, file_size); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ oh->equiv_id = in->variant.hardlink_variant.equiv_id; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = in->variant.symlink_variant.alias; ++ if (!alias) ++ alias = _Y("no alias"); ++ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); ++ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; ++ break; ++ } ++ ++ /* process any xattrib modifications */ ++ if (xmod) ++ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); ++ ++ /* Tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ in->serial++; ++ new_tags.chunk_id = 0; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = in->serial; ++ ++ /* Add extra info for file header */ ++ new_tags.extra_available = 1; ++ new_tags.extra_parent_id = oh->parent_obj_id; ++ new_tags.extra_file_size = file_size; ++ new_tags.extra_is_shrink = oh->is_shrink; ++ new_tags.extra_equiv_id = oh->equiv_id; ++ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; ++ new_tags.extra_obj_type = in->variant_type; ++ yaffs_verify_oh(in, oh, &new_tags, 1); ++ ++ /* Create new chunk in NAND */ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, ++ (prev_chunk_id > 0) ? 1 : 0); ++ ++ if (buffer) ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (new_chunk_id < 0) ++ return new_chunk_id; ++ ++ in->hdr_chunk = new_chunk_id; ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ if (!yaffs_obj_cache_dirty(in)) ++ in->dirty = 0; ++ ++ /* If this was a shrink, then mark the block ++ * that the chunk lives on */ ++ if (is_shrink) { ++ bi = yaffs_get_block_info(in->my_dev, ++ new_chunk_id / ++ in->my_dev->param.chunks_per_block); ++ bi->has_shrink_hdr = 1; ++ } ++ ++ ++ return new_chunk_id; ++} ++ ++/*--------------------- File read/write ------------------------ ++ * Read and write have very similar structures. ++ * In general the read/write has three parts to it ++ * An incomplete chunk to start with (if the read/write is not chunk-aligned) ++ * Some complete chunks ++ * An incomplete chunk to end off with ++ * ++ * Curve-balls: the first chunk might also be the last chunk. ++ */ ++ ++int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) ++{ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ struct yaffs_cache *cache; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ chunk++; ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ if ((start + n) < dev->data_bytes_per_chunk) ++ n_copy = n; ++ else ++ n_copy = dev->data_bytes_per_chunk - start; ++ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ /* If the chunk is already in the cache or it is less than ++ * a whole chunk or we're using inband tags then use the cache ++ * (if there is caching) else bypass the cache. ++ */ ++ if (cache || n_copy != dev->data_bytes_per_chunk || ++ dev->param.inband_tags) { ++ if (dev->param.n_caches > 0) { ++ ++ /* If we can't find the data in the cache, ++ * then load it up. */ ++ ++ if (!cache) { ++ cache = ++ yaffs_grab_chunk_cache(in->my_dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ cache->n_bytes = 0; ++ } ++ ++ yaffs_use_cache(dev, cache, 0); ++ ++ cache->locked = 1; ++ ++ memcpy(buffer, &cache->data[start], n_copy); ++ ++ cache->locked = 0; ++ } else { ++ /* Read into the local buffer then copy.. */ ++ ++ u8 *local_buffer = ++ yaffs_get_temp_buffer(dev); ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ ++ memcpy(buffer, &local_buffer[start], n_copy); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Read directly into the buffer. */ ++ yaffs_rd_data_obj(in, chunk, buffer); ++ } ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ return n_done; ++} ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ int n_writeback; ++ loff_t start_write = offset; ++ int chunk_written = 0; ++ u32 n_bytes_read; ++ loff_t chunk_start; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0 && chunk_written >= 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ ++ if (((loff_t)chunk) * ++ dev->data_bytes_per_chunk + start != offset || ++ start >= dev->data_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "AddrToChunk of offset %lld gives chunk %d start %d", ++ offset, chunk, start); ++ } ++ chunk++; /* File pos to chunk in file offset */ ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ ++ if ((start + n) < dev->data_bytes_per_chunk) { ++ n_copy = n; ++ ++ /* Now calculate how many bytes to write back.... ++ * If we're overwriting and not writing to then end of ++ * file then we need to write back as much as was there ++ * before. ++ */ ++ ++ chunk_start = (((loff_t)(chunk - 1)) * ++ dev->data_bytes_per_chunk); ++ ++ if (chunk_start > in->variant.file_variant.file_size) ++ n_bytes_read = 0; /* Past end of file */ ++ else ++ n_bytes_read = ++ in->variant.file_variant.file_size - ++ chunk_start; ++ ++ if (n_bytes_read > dev->data_bytes_per_chunk) ++ n_bytes_read = dev->data_bytes_per_chunk; ++ ++ n_writeback = ++ (n_bytes_read > ++ (start + n)) ? n_bytes_read : (start + n); ++ ++ if (n_writeback < 0 || ++ n_writeback > dev->data_bytes_per_chunk) ++ BUG(); ++ ++ } else { ++ n_copy = dev->data_bytes_per_chunk - start; ++ n_writeback = dev->data_bytes_per_chunk; ++ } ++ ++ if (n_copy != dev->data_bytes_per_chunk || ++ !dev->param.cache_bypass_aligned || ++ dev->param.inband_tags) { ++ /* An incomplete start or end chunk (or maybe both ++ * start and end chunk), or we're using inband tags, ++ * or we're forcing writes through the cache, ++ * so we want to use the cache buffers. ++ */ ++ if (dev->param.n_caches > 0) { ++ struct yaffs_cache *cache; ++ ++ /* If we can't find the data in the cache, then ++ * load the cache */ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ if (!cache && ++ yaffs_check_alloc_available(dev, 1)) { ++ cache = yaffs_grab_chunk_cache(dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ } else if (cache && ++ !cache->dirty && ++ !yaffs_check_alloc_available(dev, ++ 1)) { ++ /* Drop the cache if it was a read cache ++ * item and no space check has been made ++ * for it. ++ */ ++ cache = NULL; ++ } ++ ++ if (cache) { ++ yaffs_use_cache(dev, cache, 1); ++ cache->locked = 1; ++ ++ memcpy(&cache->data[start], buffer, ++ n_copy); ++ ++ cache->locked = 0; ++ cache->n_bytes = n_writeback; ++ ++ if (write_through) { ++ chunk_written = ++ yaffs_wr_data_obj ++ (cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ } ++ } else { ++ chunk_written = -1; /* fail write */ ++ } ++ } else { ++ /* An incomplete start or end chunk (or maybe ++ * both start and end chunk). Read into the ++ * local buffer then copy over and write back. ++ */ ++ ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ memcpy(&local_buffer[start], buffer, n_copy); ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, ++ local_buffer, ++ n_writeback, 0); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Write directly from the buffer. */ ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, buffer, ++ dev->data_bytes_per_chunk, 0); ++ ++ /* Since we've overwritten the cached data, ++ * we better invalidate it. */ ++ yaffs_invalidate_chunk_cache(in, chunk); ++ } ++ ++ if (chunk_written >= 0) { ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ } ++ ++ /* Update file object */ ++ ++ if ((start_write + n_done) > in->variant.file_variant.file_size) ++ in->variant.file_variant.file_size = (start_write + n_done); ++ ++ in->dirty = 1; ++ return n_done; ++} ++ ++int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ yaffs2_handle_hole(in, offset); ++ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); ++} ++ ++/* ---------------------- File resizing stuff ------------------ */ ++ ++static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) ++{ ++ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ int i; ++ int chunk_id; ++ u32 dummy; ++ int last_del; ++ int start_del; ++ ++ if (old_size > 0) ++ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); ++ else ++ last_del = 0; ++ ++ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, ++ &start_del, &dummy); ++ last_del++; ++ start_del++; ++ ++ /* Delete backwards so that we don't end up with holes if ++ * power is lost part-way through the operation. ++ */ ++ for (i = last_del; i >= start_del; i--) { ++ /* NB this could be optimised somewhat, ++ * eg. could retrieve the tags and write them without ++ * using yaffs_chunk_del ++ */ ++ ++ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); ++ ++ if (chunk_id < 1) ++ continue; ++ ++ if (chunk_id < ++ (dev->internal_start_block * dev->param.chunks_per_block) || ++ chunk_id >= ++ ((dev->internal_end_block + 1) * ++ dev->param.chunks_per_block)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Found daft chunk_id %d for %d", ++ chunk_id, i); ++ } else { ++ in->n_data_chunks--; ++ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); ++ } ++ } ++} ++ ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) ++{ ++ int new_full; ++ u32 new_partial; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); ++ ++ yaffs_prune_chunks(obj, new_size); ++ ++ if (new_partial != 0) { ++ int last_chunk = 1 + new_full; ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ /* Rewrite the last chunk with its new size and zero pad */ ++ yaffs_rd_data_obj(obj, last_chunk, local_buffer); ++ memset(local_buffer + new_partial, 0, ++ dev->data_bytes_per_chunk - new_partial); ++ ++ yaffs_wr_data_obj(obj, last_chunk, local_buffer, ++ new_partial, 1); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ ++ obj->variant.file_variant.file_size = new_size; ++ ++ yaffs_prune_tree(dev, &obj->variant.file_variant); ++} ++ ++int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) ++{ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ ++ yaffs_flush_file_cache(in, 1); ++ yaffs_invalidate_whole_cache(in); ++ ++ yaffs_check_gc(dev, 0); ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ if (new_size == old_size) ++ return YAFFS_OK; ++ ++ if (new_size > old_size) { ++ yaffs2_handle_hole(in, new_size); ++ in->variant.file_variant.file_size = new_size; ++ } else { ++ /* new_size < old_size */ ++ yaffs_resize_file_down(in, new_size); ++ } ++ ++ /* Write a new object header to reflect the resize. ++ * show we've shrunk the file, if need be ++ * Do this only if the file is not in the deleted directories ++ * and is not shadowed. ++ */ ++ if (in->parent && ++ !in->is_shadowed && ++ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ in->parent->obj_id != YAFFS_OBJECTID_DELETED) ++ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); ++ ++ return YAFFS_OK; ++} ++ ++int yaffs_flush_file(struct yaffs_obj *in, ++ int update_time, ++ int data_sync, ++ int discard_cache) ++{ ++ if (!in->dirty) ++ return YAFFS_OK; ++ ++ yaffs_flush_file_cache(in, discard_cache); ++ ++ if (data_sync) ++ return YAFFS_OK; ++ ++ if (update_time) ++ yaffs_load_current_time(in, 0, 0); ++ ++ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? ++ YAFFS_OK : YAFFS_FAIL; ++} ++ ++ ++/* yaffs_del_file deletes the whole file data ++ * and the inode associated with the file. ++ * It does not delete the links associated with the file. ++ */ ++static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) ++{ ++ int ret_val; ++ int del_now = 0; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!in->my_inode) ++ del_now = 1; ++ ++ if (del_now) { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->del_dir, ++ _Y("deleted"), 0, 0); ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: immediate deletion of file %d", ++ in->obj_id); ++ in->deleted = 1; ++ in->my_dev->n_deleted_files++; ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ yaffs_soft_del_file(in); ++ } else { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++ return ret_val; ++} ++ ++static int yaffs_del_file(struct yaffs_obj *in) ++{ ++ int ret_val = YAFFS_OK; ++ int deleted; /* Need to cache value on stack if in is freed */ ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ ++ if (in->n_data_chunks > 0) { ++ /* Use soft deletion if there is data in the file. ++ * That won't be the case if it has been resized to zero. ++ */ ++ if (!in->unlinked) ++ ret_val = yaffs_unlink_file_if_needed(in); ++ ++ deleted = in->deleted; ++ ++ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { ++ in->deleted = 1; ++ deleted = 1; ++ in->my_dev->n_deleted_files++; ++ yaffs_soft_del_file(in); ++ } ++ return deleted ? YAFFS_OK : YAFFS_FAIL; ++ } else { ++ /* The file has no data chunks so we toss it immediately */ ++ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); ++ in->variant.file_variant.top = NULL; ++ yaffs_generic_obj_del(in); ++ ++ return YAFFS_OK; ++ } ++} ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj) ++{ ++ return (obj && ++ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && ++ !(list_empty(&obj->variant.dir_variant.children)); ++} ++ ++static int yaffs_del_dir(struct yaffs_obj *obj) ++{ ++ /* First check that the directory is empty. */ ++ if (yaffs_is_non_empty_dir(obj)) ++ return YAFFS_FAIL; ++ ++ return yaffs_generic_obj_del(obj); ++} ++ ++static int yaffs_del_symlink(struct yaffs_obj *in) ++{ ++ kfree(in->variant.symlink_variant.alias); ++ in->variant.symlink_variant.alias = NULL; ++ ++ return yaffs_generic_obj_del(in); ++} ++ ++static int yaffs_del_link(struct yaffs_obj *in) ++{ ++ /* remove this hardlink from the list associated with the equivalent ++ * object ++ */ ++ list_del_init(&in->hard_links); ++ return yaffs_generic_obj_del(in); ++} ++ ++int yaffs_del_obj(struct yaffs_obj *obj) ++{ ++ int ret_val = -1; ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ ret_val = yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!list_empty(&obj->variant.dir_variant.dirty)) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Remove object %d from dirty directories", ++ obj->obj_id); ++ list_del_init(&obj->variant.dir_variant.dirty); ++ } ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ ret_val = yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ ret_val = yaffs_del_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ ret_val = yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ ret_val = 0; ++ break; /* should not happen. */ ++ } ++ return ret_val; ++} ++ ++ ++static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, ++ struct yaffs_obj *to_dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_add_obj_to_dir(to_dir, obj); ++ } ++} ++ ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type) ++{ ++ /* Tear down the old variant */ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ /* Nuke file data */ ++ yaffs_resize_file(obj, 0); ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Put the children in lost and found. */ ++ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); ++ if (!list_empty(&obj->variant.dir_variant.dirty)) ++ list_del_init(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ /* Nuke symplink data */ ++ kfree(obj->variant.symlink_variant.alias); ++ obj->variant.symlink_variant.alias = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ list_del_init(&obj->hard_links); ++ break; ++ default: ++ break; ++ } ++ ++ memset(&obj->variant, 0, sizeof(obj->variant)); ++ ++ /*Set up new variant if the memset is not enough. */ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ default: ++ break; ++ } ++ ++ obj->variant_type = type; ++ ++ return obj; ++ ++} ++ ++static int yaffs_unlink_worker(struct yaffs_obj *obj) ++{ ++ int del_now = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (!obj->my_inode) ++ del_now = 1; ++ ++ yaffs_update_parent(obj->parent); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ return yaffs_del_link(obj); ++ } else if (!list_empty(&obj->hard_links)) { ++ /* Curve ball: We're unlinking an object that has a hardlink. ++ * ++ * This problem arises because we are not strictly following ++ * The Linux link/inode model. ++ * ++ * We can't really delete the object. ++ * Instead, we do the following: ++ * - Select a hardlink. ++ * - Unhook it from the hard links ++ * - Move it from its parent directory so that the rename works. ++ * - Rename the object to the hardlink's name. ++ * - Delete the hardlink ++ */ ++ ++ struct yaffs_obj *hl; ++ struct yaffs_obj *parent; ++ int ret_val; ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ hl = list_entry(obj->hard_links.next, struct yaffs_obj, ++ hard_links); ++ ++ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); ++ parent = hl->parent; ++ ++ list_del_init(&hl->hard_links); ++ ++ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); ++ ++ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); ++ ++ if (ret_val == YAFFS_OK) ++ ret_val = yaffs_generic_obj_del(hl); ++ ++ return ret_val; ++ ++ } else if (del_now) { ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ list_del_init(&obj->variant.dir_variant.dirty); ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ return yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ return YAFFS_FAIL; ++ } ++ } else if (yaffs_is_non_empty_dir(obj)) { ++ return YAFFS_FAIL; ++ } else { ++ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++} ++ ++static int yaffs_unlink_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->unlink_allowed) ++ return yaffs_unlink_worker(obj); ++ ++ return YAFFS_FAIL; ++} ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) ++{ ++ struct yaffs_obj *obj; ++ ++ obj = yaffs_find_by_name(dir, name); ++ return yaffs_unlink_obj(obj); ++} ++ ++/* Note: ++ * If old_name is NULL then we take old_dir as the object to be renamed. ++ */ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, ++ struct yaffs_obj *new_dir, const YCHAR *new_name) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *existing_target = NULL; ++ int force = 0; ++ int result; ++ struct yaffs_dev *dev; ++ ++ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ dev = old_dir->my_dev; ++ ++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE ++ /* Special case for case insemsitive systems. ++ * While look-up is case insensitive, the name isn't. ++ * Therefore we might want to change x.txt to X.txt ++ */ ++ if (old_dir == new_dir && ++ old_name && new_name && ++ strcmp(old_name, new_name) == 0) ++ force = 1; ++#endif ++ ++ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ /* ENAMETOOLONG */ ++ return YAFFS_FAIL; ++ ++ if (old_name) ++ obj = yaffs_find_by_name(old_dir, old_name); ++ else{ ++ obj = old_dir; ++ old_dir = obj->parent; ++ } ++ ++ if (obj && obj->rename_allowed) { ++ /* Now handle an existing target, if there is one */ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ if (yaffs_is_non_empty_dir(existing_target)) { ++ return YAFFS_FAIL; /* ENOTEMPTY */ ++ } else if (existing_target && existing_target != obj) { ++ /* Nuke the target first, using shadowing, ++ * but only if it isn't the same object. ++ * ++ * Note we must disable gc here otherwise it can mess ++ * up the shadowing. ++ * ++ */ ++ dev->gc_disable = 1; ++ yaffs_change_obj_name(obj, new_dir, new_name, force, ++ existing_target->obj_id); ++ existing_target->is_shadowed = 1; ++ yaffs_unlink_obj(existing_target); ++ dev->gc_disable = 0; ++ } ++ ++ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); ++ ++ yaffs_update_parent(old_dir); ++ if (new_dir != old_dir) ++ yaffs_update_parent(new_dir); ++ ++ return result; ++ } ++ return YAFFS_FAIL; ++} ++ ++/*----------------------- Initialisation Scanning ---------------------- */ ++ ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning) ++{ ++ struct yaffs_obj *obj; ++ ++ if (backward_scanning) { ++ /* Handle YAFFS2 case (backward scanning) ++ * If the shadowed object exists then ignore. ++ */ ++ obj = yaffs_find_by_number(dev, obj_id); ++ if (obj) ++ return; ++ } ++ ++ /* Let's create it (if it does not exist) assuming it is a file so that ++ * it can do shrinking etc. ++ * We put it in unlinked dir to be cleaned up after the scanning ++ */ ++ obj = ++ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); ++ if (!obj) ++ return; ++ obj->is_shadowed = 1; ++ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); ++ obj->variant.file_variant.shrink_size = 0; ++ obj->valid = 1; /* So that we don't read any other info. */ ++} ++ ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) ++{ ++ struct list_head *lh; ++ struct list_head *save; ++ struct yaffs_obj *hl; ++ struct yaffs_obj *in; ++ ++ list_for_each_safe(lh, save, hard_list) { ++ hl = list_entry(lh, struct yaffs_obj, hard_links); ++ in = yaffs_find_by_number(dev, ++ hl->variant.hardlink_variant.equiv_id); ++ ++ if (in) { ++ /* Add the hardlink pointers */ ++ hl->variant.hardlink_variant.equiv_obj = in; ++ list_add(&hl->hard_links, &in->hard_links); ++ } else { ++ /* Todo Need to report/handle this better. ++ * Got a problem... hardlink to a non-existant object ++ */ ++ hl->variant.hardlink_variant.equiv_obj = NULL; ++ INIT_LIST_HEAD(&hl->hard_links); ++ } ++ } ++} ++ ++static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) ++{ ++ /* ++ * Sort out state of unlinked and deleted objects after scanning. ++ */ ++ struct list_head *i; ++ struct list_head *n; ++ struct yaffs_obj *l; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Soft delete all the unlinked files */ ++ list_for_each_safe(i, n, ++ &dev->unlinked_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++ ++ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++} ++ ++/* ++ * This code iterates through all the objects making sure that they are rooted. ++ * Any unrooted objects are re-rooted in lost+found. ++ * An object needs to be in one of: ++ * - Directly under deleted, unlinked ++ * - Directly or indirectly under root. ++ * ++ * Note: ++ * This code assumes that we don't ever change the current relationships ++ * between directories: ++ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL ++ * lost-n-found->parent == root_dir ++ * ++ * This fixes the problem where directories might have inadvertently been ++ * deleted leaving the object "hanging" without being rooted in the ++ * directory tree. ++ */ ++ ++static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ return (obj == dev->del_dir || ++ obj == dev->unlinked_dir || obj == dev->root_dir); ++} ++ ++static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_obj *parent; ++ int i; ++ struct list_head *lh; ++ struct list_head *n; ++ int depth_limit; ++ int hanging; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Iterate through the objects in each hash entry, ++ * looking at each object. ++ * Make sure it is rooted. ++ */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ parent = obj->parent; ++ ++ if (yaffs_has_null_parent(dev, obj)) { ++ /* These directories are not hanging */ ++ hanging = 0; ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ hanging = 1; ++ } else if (yaffs_has_null_parent(dev, parent)) { ++ hanging = 0; ++ } else { ++ /* ++ * Need to follow the parent chain to ++ * see if it is hanging. ++ */ ++ hanging = 0; ++ depth_limit = 100; ++ ++ while (parent != dev->root_dir && ++ parent->parent && ++ parent->parent->variant_type == ++ YAFFS_OBJECT_TYPE_DIRECTORY && ++ depth_limit > 0) { ++ parent = parent->parent; ++ depth_limit--; ++ } ++ if (parent != dev->root_dir) ++ hanging = 1; ++ } ++ if (hanging) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Hanging object %d moved to lost and found", ++ obj->obj_id); ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ } ++ } ++ } ++} ++ ++/* ++ * Delete directory contents for cleaning up lost and found. ++ */ ++static void yaffs_del_dir_contents(struct yaffs_obj *dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ BUG(); ++ ++ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) ++ yaffs_del_dir_contents(obj); ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Deleting lost_found object %d", ++ obj->obj_id); ++ yaffs_unlink_obj(obj); ++ } ++} ++ ++static void yaffs_empty_l_n_f(struct yaffs_dev *dev) ++{ ++ yaffs_del_dir_contents(dev->lost_n_found); ++} ++ ++ ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, ++ const YCHAR *name) ++{ ++ int sum; ++ struct list_head *i; ++ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj *l; ++ ++ if (!name) ++ return NULL; ++ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: null pointer directory" ++ ); ++ BUG(); ++ return NULL; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: non-directory" ++ ); ++ BUG(); ++ } ++ ++ sum = yaffs_calc_name_sum(name); ++ ++ list_for_each(i, &directory->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ ++ if (l->parent != directory) ++ BUG(); ++ ++ yaffs_check_obj_details_loaded(l); ++ ++ /* Special case for lost-n-found */ ++ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) ++ return l; ++ } else if (l->sum == sum || l->hdr_chunk <= 0) { ++ /* LostnFound chunk called Objxxx ++ * Do a real check ++ */ ++ yaffs_get_obj_name(l, buffer, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) ++ return l; ++ } ++ } ++ return NULL; ++} ++ ++/* GetEquivalentObject dereferences any hard links to get to the ++ * actual object. ++ */ ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ obj = obj->variant.hardlink_variant.equiv_obj; ++ yaffs_check_obj_details_loaded(obj); ++ } ++ return obj; ++} ++ ++/* ++ * A note or two on object names. ++ * * If the object name is missing, we then make one up in the form objnnn ++ * ++ * * ASCII names are stored in the object header's name field from byte zero ++ * * Unicode names are historically stored starting from byte zero. ++ * ++ * Then there are automatic Unicode names... ++ * The purpose of these is to save names in a way that can be read as ++ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII ++ * system to share files. ++ * ++ * These automatic unicode are stored slightly differently... ++ * - If the name can fit in the ASCII character space then they are saved as ++ * ascii names as per above. ++ * - If the name needs Unicode then the name is saved in Unicode ++ * starting at oh->name[1]. ++ ++ */ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size) ++{ ++ /* Create an object name if we could not find one. */ ++ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { ++ YCHAR local_name[20]; ++ YCHAR num_string[20]; ++ YCHAR *x = &num_string[19]; ++ unsigned v = obj->obj_id; ++ num_string[19] = 0; ++ while (v > 0) { ++ x--; ++ *x = '0' + (v % 10); ++ v /= 10; ++ } ++ /* make up a name */ ++ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); ++ strcat(local_name, x); ++ strncpy(name, local_name, buffer_size - 1); ++ } ++} ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) ++{ ++ memset(name, 0, buffer_size * sizeof(YCHAR)); ++ yaffs_check_obj_details_loaded(obj); ++ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); ++ } else if (obj->short_name[0]) { ++ strcpy(name, obj->short_name); ++ } else if (obj->hdr_chunk > 0) { ++ int result; ++ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); ++ ++ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; ++ ++ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); ++ ++ if (obj->hdr_chunk > 0) { ++ result = yaffs_rd_chunk_tags_nand(obj->my_dev, ++ obj->hdr_chunk, ++ buffer, NULL); ++ } ++ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, ++ buffer_size); ++ ++ yaffs_release_temp_buffer(obj->my_dev, buffer); ++ } ++ ++ yaffs_fix_null_name(obj, name, buffer_size); ++ ++ return strnlen(name, YAFFS_MAX_NAME_LENGTH); ++} ++ ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj) ++{ ++ /* Dereference any hard linking */ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ return obj->variant.file_variant.file_size; ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ if (!obj->variant.symlink_variant.alias) ++ return 0; ++ return strnlen(obj->variant.symlink_variant.alias, ++ YAFFS_MAX_ALIAS_LENGTH); ++ } else { ++ /* Only a directory should drop through to here */ ++ return obj->my_dev->data_bytes_per_chunk; ++ } ++} ++ ++int yaffs_get_obj_link_count(struct yaffs_obj *obj) ++{ ++ int count = 0; ++ struct list_head *i; ++ ++ if (!obj->unlinked) ++ count++; /* the object itself */ ++ ++ list_for_each(i, &obj->hard_links) ++ count++; /* add the hard links; */ ++ ++ return count; ++} ++ ++int yaffs_get_obj_inode(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ return obj->obj_id; ++} ++ ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ return DT_DIR; ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return DT_LNK; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ if (S_ISFIFO(obj->yst_mode)) ++ return DT_FIFO; ++ if (S_ISCHR(obj->yst_mode)) ++ return DT_CHR; ++ if (S_ISBLK(obj->yst_mode)) ++ return DT_BLK; ++ if (S_ISSOCK(obj->yst_mode)) ++ return DT_SOCK; ++ return DT_REG; ++ break; ++ default: ++ return DT_REG; ++ break; ++ } ++} ++ ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) ++ return yaffs_clone_str(obj->variant.symlink_variant.alias); ++ else ++ return yaffs_clone_str(_Y("")); ++} ++ ++/*--------------------------- Initialisation code -------------------------- */ ++ ++static int yaffs_check_dev_fns(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ struct yaffs_tags_handler *tagger = &dev->tagger; ++ ++ /* Common functions, gotta have */ ++ if (!drv->drv_read_chunk_fn || ++ !drv->drv_write_chunk_fn || ++ !drv->drv_erase_fn) ++ return 0; ++ ++ if (dev->param.is_yaffs2 && ++ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) ++ return 0; ++ ++ /* Install the default tags marshalling functions if needed. */ ++ yaffs_tags_compat_install(dev); ++ yaffs_tags_marshall_install(dev); ++ ++ /* Check we now have the marshalling functions required. */ ++ if (!tagger->write_chunk_tags_fn || ++ !tagger->read_chunk_tags_fn || ++ !tagger->query_block_fn || ++ !tagger->mark_bad_fn) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs_create_initial_dir(struct yaffs_dev *dev) ++{ ++ /* Initialise the unlinked, deleted, root and lost+found directories */ ++ dev->lost_n_found = dev->root_dir = NULL; ++ dev->unlinked_dir = dev->del_dir = NULL; ++ dev->unlinked_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); ++ dev->del_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); ++ dev->root_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, ++ YAFFS_ROOT_MODE | S_IFDIR); ++ dev->lost_n_found = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, ++ YAFFS_LOSTNFOUND_MODE | S_IFDIR); ++ ++ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir ++ && dev->del_dir) { ++ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++/* Low level init. ++ * Typically only used by yaffs_guts_initialise, but also used by the ++ * Low level yaffs driver tests. ++ */ ++ ++int yaffs_guts_ll_init(struct yaffs_dev *dev) ++{ ++ ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); ++ ++ if (!dev) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Need a device" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ if (dev->ll_init) ++ return YAFFS_OK; ++ ++ dev->internal_start_block = dev->param.start_block; ++ dev->internal_end_block = dev->param.end_block; ++ dev->block_offset = 0; ++ dev->chunk_offset = 0; ++ dev->n_free_chunks = 0; ++ ++ dev->gc_block = 0; ++ ++ if (dev->param.start_block == 0) { ++ dev->internal_start_block = dev->param.start_block + 1; ++ dev->internal_end_block = dev->param.end_block + 1; ++ dev->block_offset = 1; ++ dev->chunk_offset = dev->param.chunks_per_block; ++ } ++ ++ /* Check geometry parameters. */ ++ ++ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 1024) || ++ (!dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 512) || ++ (dev->param.inband_tags && !dev->param.is_yaffs2) || ++ dev->param.chunks_per_block < 2 || ++ dev->param.n_reserved_blocks < 2 || ++ dev->internal_start_block <= 0 || ++ dev->internal_end_block <= 0 || ++ dev->internal_end_block <= ++ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) ++ ) { ++ /* otherwise it is too small */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", ++ dev->param.total_bytes_per_chunk, ++ dev->param.is_yaffs2 ? "2" : "", ++ dev->param.inband_tags); ++ return YAFFS_FAIL; ++ } ++ ++ /* Sort out space for inband tags, if required */ ++ if (dev->param.inband_tags) ++ dev->data_bytes_per_chunk = ++ dev->param.total_bytes_per_chunk - ++ sizeof(struct yaffs_packed_tags2_tags_only); ++ else ++ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; ++ ++ /* Got the right mix of functions? */ ++ if (!yaffs_check_dev_fns(dev)) { ++ /* Function missing */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "device function(s) missing or wrong"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ if (yaffs_init_nand(dev) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev) ++{ ++ int i; ++ enum yaffs_block_state state; ++ u32 dummy; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if(dev->is_mounted) ++ return YAFFS_FAIL; ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ yaffs_query_init_block_state(dev, i, &state, &dummy); ++ if (state != YAFFS_BLOCK_STATE_DEAD) ++ yaffs_erase_block(dev, i); ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev) ++{ ++ int init_failed = 0; ++ unsigned x; ++ int bits; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if (dev->is_mounted) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); ++ return YAFFS_FAIL; ++ } ++ ++ dev->is_mounted = 1; ++ ++ /* OK now calculate a few things for the device */ ++ ++ /* ++ * Calculate all the chunk size manipulation numbers: ++ */ ++ x = dev->data_bytes_per_chunk; ++ /* We always use dev->chunk_shift and dev->chunk_div */ ++ dev->chunk_shift = calc_shifts(x); ++ x >>= dev->chunk_shift; ++ dev->chunk_div = x; ++ /* We only use chunk mask if chunk_div is 1 */ ++ dev->chunk_mask = (1 << dev->chunk_shift) - 1; ++ ++ /* ++ * Calculate chunk_grp_bits. ++ * We need to find the next power of 2 > than internal_end_block ++ */ ++ ++ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); ++ ++ bits = calc_shifts_ceiling(x); ++ ++ /* Set up tnode width if wide tnodes are enabled. */ ++ if (!dev->param.wide_tnodes_disabled) { ++ /* bits must be even so that we end up with 32-bit words */ ++ if (bits & 1) ++ bits++; ++ if (bits < 16) ++ dev->tnode_width = 16; ++ else ++ dev->tnode_width = bits; ++ } else { ++ dev->tnode_width = 16; ++ } ++ ++ dev->tnode_mask = (1 << dev->tnode_width) - 1; ++ ++ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), ++ * so if the bitwidth of the ++ * chunk range we're using is greater than 16 we need ++ * to figure out chunk shift and chunk_grp_size ++ */ ++ ++ if (bits <= dev->tnode_width) ++ dev->chunk_grp_bits = 0; ++ else ++ dev->chunk_grp_bits = bits - dev->tnode_width; ++ ++ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; ++ if (dev->tnode_size < sizeof(struct yaffs_tnode)) ++ dev->tnode_size = sizeof(struct yaffs_tnode); ++ ++ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; ++ ++ if (dev->param.chunks_per_block < dev->chunk_grp_size) { ++ /* We have a problem because the soft delete won't work if ++ * the chunk group size > chunks per block. ++ * This can be remedied by using larger "virtual blocks". ++ */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ /* Finished verifying the device, continue with initialisation */ ++ ++ /* More device initialisation */ ++ dev->all_gcs = 0; ++ dev->passive_gc_count = 0; ++ dev->oldest_dirty_gc_count = 0; ++ dev->bg_gcs = 0; ++ dev->gc_block_finder = 0; ++ dev->buffered_block = -1; ++ dev->doing_buffered_block_rewrite = 0; ++ dev->n_deleted_files = 0; ++ dev->n_bg_deletions = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_ecc_fixed = 0; ++ dev->n_ecc_unfixed = 0; ++ dev->n_tags_ecc_fixed = 0; ++ dev->n_tags_ecc_unfixed = 0; ++ dev->n_erase_failures = 0; ++ dev->n_erased_blocks = 0; ++ dev->gc_disable = 0; ++ dev->has_pending_prioritised_gc = 1; ++ /* Assume the worst for now, will get fixed on first GC */ ++ INIT_LIST_HEAD(&dev->dirty_dirs); ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ ++ /* Initialise temporary buffers and caches. */ ++ if (!yaffs_init_tmp_buffers(dev)) ++ init_failed = 1; ++ ++ dev->cache = NULL; ++ dev->gc_cleanup_list = NULL; ++ ++ if (!init_failed && dev->param.n_caches > 0) { ++ int i; ++ void *buf; ++ int cache_bytes = ++ dev->param.n_caches * sizeof(struct yaffs_cache); ++ ++ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) ++ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; ++ ++ dev->cache = kmalloc(cache_bytes, GFP_NOFS); ++ ++ buf = (u8 *) dev->cache; ++ ++ if (dev->cache) ++ memset(dev->cache, 0, cache_bytes); ++ ++ for (i = 0; i < dev->param.n_caches && buf; i++) { ++ dev->cache[i].object = NULL; ++ dev->cache[i].last_use = 0; ++ dev->cache[i].dirty = 0; ++ dev->cache[i].data = buf = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ } ++ if (!buf) ++ init_failed = 1; ++ ++ dev->cache_last_use = 0; ++ } ++ ++ dev->cache_hits = 0; ++ ++ if (!init_failed) { ++ dev->gc_cleanup_list = ++ kmalloc(dev->param.chunks_per_block * sizeof(u32), ++ GFP_NOFS); ++ if (!dev->gc_cleanup_list) ++ init_failed = 1; ++ } ++ ++ if (dev->param.is_yaffs2) ++ dev->param.use_header_file_size = 1; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && dev->param.is_yaffs2 && ++ !dev->param.disable_summary && ++ !yaffs_summary_init(dev)) ++ init_failed = 1; ++ ++ if (!init_failed) { ++ /* Now scan the flash. */ ++ if (dev->param.is_yaffs2) { ++ if (yaffs2_checkpt_restore(dev)) { ++ yaffs_check_obj_details_loaded(dev->root_dir); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | ++ YAFFS_TRACE_MOUNT, ++ "yaffs: restored from checkpoint" ++ ); ++ } else { ++ ++ /* Clean up the mess caused by an aborted ++ * checkpoint load then scan backwards. ++ */ ++ yaffs_deinit_blocks(dev); ++ ++ yaffs_deinit_tnodes_and_objs(dev); ++ ++ dev->n_erased_blocks = 0; ++ dev->n_free_chunks = 0; ++ dev->alloc_block = -1; ++ dev->alloc_page = -1; ++ dev->n_deleted_files = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_bg_deletions = 0; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed ++ && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && !yaffs2_scan_backwards(dev)) ++ init_failed = 1; ++ } ++ } else if (!yaffs1_scan(dev)) { ++ init_failed = 1; ++ } ++ ++ yaffs_strip_deleted_objs(dev); ++ yaffs_fix_hanging_objs(dev); ++ if (dev->param.empty_lost_n_found) ++ yaffs_empty_l_n_f(dev); ++ } ++ ++ if (init_failed) { ++ /* Clean up the mess */ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() aborted."); ++ ++ yaffs_deinitialise(dev); ++ return YAFFS_FAIL; ++ } ++ ++ /* Zero out stats */ ++ dev->n_page_reads = 0; ++ dev->n_page_writes = 0; ++ dev->n_erasures = 0; ++ dev->n_gc_copies = 0; ++ dev->n_retried_writes = 0; ++ ++ dev->n_retired_blocks = 0; ++ ++ yaffs_verify_free_chunks(dev); ++ yaffs_verify_blocks(dev); ++ ++ /* Clean up any aborted checkpoint data */ ++ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() done."); ++ return YAFFS_OK; ++} ++ ++void yaffs_deinitialise(struct yaffs_dev *dev) ++{ ++ if (dev->is_mounted) { ++ int i; ++ ++ yaffs_deinit_blocks(dev); ++ yaffs_deinit_tnodes_and_objs(dev); ++ yaffs_summary_deinit(dev); ++ ++ if (dev->param.n_caches > 0 && dev->cache) { ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ kfree(dev->cache[i].data); ++ dev->cache[i].data = NULL; ++ } ++ ++ kfree(dev->cache); ++ dev->cache = NULL; ++ } ++ ++ kfree(dev->gc_cleanup_list); ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ kfree(dev->temp_buffer[i].buffer); ++ dev->temp_buffer[i].buffer = NULL; ++ } ++ ++ kfree(dev->checkpt_buffer); ++ dev->checkpt_buffer = NULL; ++ kfree(dev->checkpt_block_list); ++ dev->checkpt_block_list = NULL; ++ ++ dev->is_mounted = 0; ++ ++ yaffs_deinit_nand(dev); ++ } ++} ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev) ++{ ++ int n_free = 0; ++ int b; ++ struct yaffs_block_info *blk; ++ ++ blk = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ switch (blk->block_state) { ++ case YAFFS_BLOCK_STATE_EMPTY: ++ case YAFFS_BLOCK_STATE_ALLOCATING: ++ case YAFFS_BLOCK_STATE_COLLECTING: ++ case YAFFS_BLOCK_STATE_FULL: ++ n_free += ++ (dev->param.chunks_per_block - blk->pages_in_use + ++ blk->soft_del_pages); ++ break; ++ default: ++ break; ++ } ++ blk++; ++ } ++ return n_free; ++} ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev) ++{ ++ /* This is what we report to the outside world */ ++ int n_free; ++ int n_dirty_caches; ++ int blocks_for_checkpt; ++ int i; ++ ++ n_free = dev->n_free_chunks; ++ n_free += dev->n_deleted_files; ++ ++ /* Now count and subtract the number of dirty chunks in the cache. */ ++ ++ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].dirty) ++ n_dirty_caches++; ++ } ++ ++ n_free -= n_dirty_caches; ++ ++ n_free -= ++ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); ++ ++ /* Now figure checkpoint space and report that... */ ++ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); ++ ++ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); ++ ++ if (n_free < 0) ++ n_free = 0; ++ ++ return n_free; ++} ++ ++ ++ ++/* ++ * Marshalling functions to get loff_t file sizes into and out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) ++{ ++ oh->file_size_low = (fsize & 0xFFFFFFFF); ++ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); ++} ++ ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) ++{ ++ loff_t retval; ++ ++ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) ++ retval = (((loff_t) oh->file_size_high) << 32) | ++ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); ++ else ++ retval = (loff_t) oh->file_size_low; ++ ++ return retval; ++} ++ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ int s; ++ ++ for(i = 0; i < 10; i++) ++ bs[i] = 0; ++ ++ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ bi = yaffs_get_block_info(dev, i); ++ s = bi->block_state; ++ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) ++ bs[0]++; ++ else ++ bs[s]++; ++ } ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_guts.h linux-3.14.43/fs/yaffs2/yaffs_guts.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_guts.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,1010 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GUTS_H__ ++#define __YAFFS_GUTS_H__ ++ ++#include "yportenv.h" ++ ++#define YAFFS_OK 1 ++#define YAFFS_FAIL 0 ++ ++/* Give us a Y=0x59, ++ * Give us an A=0x41, ++ * Give us an FF=0xff ++ * Give us an S=0x53 ++ * And what have we got... ++ */ ++#define YAFFS_MAGIC 0x5941ff53 ++ ++/* ++ * Tnodes form a tree with the tnodes in "levels" ++ * Levels greater than 0 hold 8 slots which point to other tnodes. ++ * Those at level 0 hold 16 slots which point to chunks in NAND. ++ * ++ * A maximum level of 8 thust supports files of size up to: ++ * ++ * 2^(3*MAX_LEVEL+4) ++ * ++ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives ++ * a maximum file size of around 512Gbytees with 2k chunks. ++ */ ++#define YAFFS_NTNODES_LEVEL0 16 ++#define YAFFS_TNODES_LEVEL0_BITS 4 ++#define YAFFS_TNODES_LEVEL0_MASK 0xf ++ ++#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) ++#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) ++#define YAFFS_TNODES_INTERNAL_MASK 0x7 ++#define YAFFS_TNODES_MAX_LEVEL 8 ++#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ ++ YAFFS_TNODES_INTERNAL_BITS * \ ++ YAFFS_TNODES_MAX_LEVEL) ++#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) ++ ++#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff ++ ++/* Constants for YAFFS1 mode */ ++#define YAFFS_BYTES_PER_SPARE 16 ++#define YAFFS_BYTES_PER_CHUNK 512 ++#define YAFFS_CHUNK_SIZE_SHIFT 9 ++#define YAFFS_CHUNKS_PER_BLOCK 32 ++#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) ++ ++#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 ++#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 ++ ++ ++ ++#define YAFFS_ALLOCATION_NOBJECTS 100 ++#define YAFFS_ALLOCATION_NTNODES 100 ++#define YAFFS_ALLOCATION_NLINKS 100 ++ ++#define YAFFS_NOBJECT_BUCKETS 256 ++ ++#define YAFFS_OBJECT_SPACE 0x40000 ++#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) ++ ++/* Binary data version stamps */ ++#define YAFFS_SUMMARY_VERSION 1 ++#define YAFFS_CHECKPOINT_VERSION 7 ++ ++#ifdef CONFIG_YAFFS_UNICODE ++#define YAFFS_MAX_NAME_LENGTH 127 ++#define YAFFS_MAX_ALIAS_LENGTH 79 ++#else ++#define YAFFS_MAX_NAME_LENGTH 255 ++#define YAFFS_MAX_ALIAS_LENGTH 159 ++#endif ++ ++#define YAFFS_SHORT_NAME_LENGTH 15 ++ ++/* Some special object ids for pseudo objects */ ++#define YAFFS_OBJECTID_ROOT 1 ++#define YAFFS_OBJECTID_LOSTNFOUND 2 ++#define YAFFS_OBJECTID_UNLINKED 3 ++#define YAFFS_OBJECTID_DELETED 4 ++ ++/* Fake object Id for summary data */ ++#define YAFFS_OBJECTID_SUMMARY 0x10 ++ ++/* Pseudo object ids for checkpointing */ ++#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 ++#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 ++ ++#define YAFFS_MAX_SHORT_OP_CACHES 20 ++ ++#define YAFFS_N_TEMP_BUFFERS 6 ++ ++/* We limit the number attempts at sucessfully saving a chunk of data. ++ * Small-page devices have 32 pages per block; large-page devices have 64. ++ * Default to something in the order of 5 to 10 blocks worth of chunks. ++ */ ++#define YAFFS_WR_ATTEMPTS (5*64) ++ ++/* Sequence numbers are used in YAFFS2 to determine block allocation order. ++ * The range is limited slightly to help distinguish bad numbers from good. ++ * This also allows us to perhaps in the future use special numbers for ++ * special purposes. ++ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, ++ * and is a larger number than the lifetime of a 2GB device. ++ */ ++#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 ++#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 ++ ++/* Special sequence number for bad block that failed to be marked bad */ ++#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 ++ ++/* ChunkCache is used for short read/write operations.*/ ++struct yaffs_cache { ++ struct yaffs_obj *object; ++ int chunk_id; ++ int last_use; ++ int dirty; ++ int n_bytes; /* Only valid if the cache is dirty */ ++ int locked; /* Can't push out or flush while locked. */ ++ u8 *data; ++}; ++ ++/* yaffs1 tags structures in RAM ++ * NB This uses bitfield. Bitfields should not straddle a u32 boundary ++ * otherwise the structure size will get blown out. ++ */ ++ ++struct yaffs_tags { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes_lsb:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 n_bytes_msb:2; ++}; ++ ++union yaffs_tags_union { ++ struct yaffs_tags as_tags; ++ u8 as_bytes[8]; ++}; ++ ++ ++/* Stuff used for extended tags in YAFFS2 */ ++ ++enum yaffs_ecc_result { ++ YAFFS_ECC_RESULT_UNKNOWN, ++ YAFFS_ECC_RESULT_NO_ERROR, ++ YAFFS_ECC_RESULT_FIXED, ++ YAFFS_ECC_RESULT_UNFIXED ++}; ++ ++enum yaffs_obj_type { ++ YAFFS_OBJECT_TYPE_UNKNOWN, ++ YAFFS_OBJECT_TYPE_FILE, ++ YAFFS_OBJECT_TYPE_SYMLINK, ++ YAFFS_OBJECT_TYPE_DIRECTORY, ++ YAFFS_OBJECT_TYPE_HARDLINK, ++ YAFFS_OBJECT_TYPE_SPECIAL ++}; ++ ++#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL ++ ++struct yaffs_ext_tags { ++ unsigned chunk_used; /* Status of the chunk: used or unused */ ++ unsigned obj_id; /* If 0 this is not used */ ++ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ ++ unsigned n_bytes; /* Only valid for data chunks */ ++ ++ /* The following stuff only has meaning when we read */ ++ enum yaffs_ecc_result ecc_result; ++ unsigned block_bad; ++ ++ /* YAFFS 1 stuff */ ++ unsigned is_deleted; /* The chunk is marked deleted */ ++ unsigned serial_number; /* Yaffs1 2-bit serial number */ ++ ++ /* YAFFS2 stuff */ ++ unsigned seq_number; /* The sequence number of this block */ ++ ++ /* Extra info if this is an object header (YAFFS2 only) */ ++ ++ unsigned extra_available; /* Extra info available if not zero */ ++ unsigned extra_parent_id; /* The parent object */ ++ unsigned extra_is_shrink; /* Is it a shrink header? */ ++ unsigned extra_shadows; /* Does this shadow another object? */ ++ ++ enum yaffs_obj_type extra_obj_type; /* What object type? */ ++ ++ loff_t extra_file_size; /* Length if it is a file */ ++ unsigned extra_equiv_id; /* Equivalent object for a hard link */ ++}; ++ ++/* Spare structure for YAFFS1 */ ++struct yaffs_spare { ++ u8 tb0; ++ u8 tb1; ++ u8 tb2; ++ u8 tb3; ++ u8 page_status; /* set to 0 to delete the chunk */ ++ u8 block_status; ++ u8 tb4; ++ u8 tb5; ++ u8 ecc1[3]; ++ u8 tb6; ++ u8 tb7; ++ u8 ecc2[3]; ++}; ++ ++/*Special structure for passing through to mtd */ ++struct yaffs_nand_spare { ++ struct yaffs_spare spare; ++ int eccres1; ++ int eccres2; ++}; ++ ++/* Block data in RAM */ ++ ++enum yaffs_block_state { ++ YAFFS_BLOCK_STATE_UNKNOWN = 0, ++ ++ YAFFS_BLOCK_STATE_SCANNING, ++ /* Being scanned */ ++ ++ YAFFS_BLOCK_STATE_NEEDS_SCAN, ++ /* The block might have something on it (ie it is allocating or full, ++ * perhaps empty) but it needs to be scanned to determine its true ++ * state. ++ * This state is only valid during scanning. ++ * NB We tolerate empty because the pre-scanner might be incapable of ++ * deciding ++ * However, if this state is returned on a YAFFS2 device, ++ * then we expect a sequence number ++ */ ++ ++ YAFFS_BLOCK_STATE_EMPTY, ++ /* This block is empty */ ++ ++ YAFFS_BLOCK_STATE_ALLOCATING, ++ /* This block is partially allocated. ++ * At least one page holds valid data. ++ * This is the one currently being used for page ++ * allocation. Should never be more than one of these. ++ * If a block is only partially allocated at mount it is treated as ++ * full. ++ */ ++ ++ YAFFS_BLOCK_STATE_FULL, ++ /* All the pages in this block have been allocated. ++ * If a block was only partially allocated when mounted we treat ++ * it as fully allocated. ++ */ ++ ++ YAFFS_BLOCK_STATE_DIRTY, ++ /* The block was full and now all chunks have been deleted. ++ * Erase me, reuse me. ++ */ ++ ++ YAFFS_BLOCK_STATE_CHECKPOINT, ++ /* This block is assigned to holding checkpoint data. */ ++ ++ YAFFS_BLOCK_STATE_COLLECTING, ++ /* This block is being garbage collected */ ++ ++ YAFFS_BLOCK_STATE_DEAD ++ /* This block has failed and is not in use */ ++}; ++ ++#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) ++ ++struct yaffs_block_info { ++ ++ s32 soft_del_pages:10; /* number of soft deleted pages */ ++ s32 pages_in_use:10; /* number of pages in use */ ++ u32 block_state:4; /* One of the above block states. */ ++ /* NB use unsigned because enum is sometimes ++ * an int */ ++ u32 needs_retiring:1; /* Data has failed on this block, */ ++ /*need to get valid data off and retire*/ ++ u32 skip_erased_check:1;/* Skip the erased check on this block */ ++ u32 gc_prioritise:1; /* An ECC check or blank check has failed. ++ Block should be prioritised for GC */ ++ u32 chunk_error_strikes:3; /* How many times we've had ecc etc ++ failures on this block and tried to reuse it */ ++ u32 has_summary:1; /* The block has a summary */ ++ ++ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ ++ u32 seq_number; /* block sequence number for yaffs2 */ ++ ++}; ++ ++/* -------------------------- Object structure -------------------------------*/ ++/* This is the object structure as stored on NAND */ ++ ++struct yaffs_obj_hdr { ++ enum yaffs_obj_type type; ++ ++ /* Apply to everything */ ++ int parent_obj_id; ++ u16 sum_no_longer_used; /* checksum of name. No longer used */ ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ /* The following apply to all object types except for hard links */ ++ u32 yst_mode; /* protection */ ++ ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++ ++ /* File size applies to files only */ ++ u32 file_size_low; ++ ++ /* Equivalent object id applies to hard links only. */ ++ int equiv_id; ++ ++ /* Alias is for symlinks only. */ ++ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; ++ ++ u32 yst_rdev; /* stuff for block and char devices (major/min) */ ++ ++ u32 win_ctime[2]; ++ u32 win_atime[2]; ++ u32 win_mtime[2]; ++ ++ u32 inband_shadowed_obj_id; ++ u32 inband_is_shrink; ++ ++ u32 file_size_high; ++ u32 reserved[1]; ++ int shadows_obj; /* This object header shadows the ++ specified object if > 0 */ ++ ++ /* is_shrink applies to object headers written when wemake a hole. */ ++ u32 is_shrink; ++ ++}; ++ ++/*--------------------------- Tnode -------------------------- */ ++ ++struct yaffs_tnode { ++ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; ++}; ++ ++/*------------------------ Object -----------------------------*/ ++/* An object can be one of: ++ * - a directory (no data, has children links ++ * - a regular file (data.... not prunes :->). ++ * - a symlink [symbolic link] (the alias). ++ * - a hard link ++ */ ++ ++struct yaffs_file_var { ++ loff_t file_size; ++ loff_t scanned_size; ++ loff_t shrink_size; ++ int top_level; ++ struct yaffs_tnode *top; ++}; ++ ++struct yaffs_dir_var { ++ struct list_head children; /* list of child links */ ++ struct list_head dirty; /* Entry for list of dirty directories */ ++}; ++ ++struct yaffs_symlink_var { ++ YCHAR *alias; ++}; ++ ++struct yaffs_hardlink_var { ++ struct yaffs_obj *equiv_obj; ++ u32 equiv_id; ++}; ++ ++union yaffs_obj_var { ++ struct yaffs_file_var file_variant; ++ struct yaffs_dir_var dir_variant; ++ struct yaffs_symlink_var symlink_variant; ++ struct yaffs_hardlink_var hardlink_variant; ++}; ++ ++struct yaffs_obj { ++ u8 deleted:1; /* This should only apply to unlinked files. */ ++ u8 soft_del:1; /* it has also been soft deleted */ ++ u8 unlinked:1; /* An unlinked file.*/ ++ u8 fake:1; /* A fake object has no presence on NAND. */ ++ u8 rename_allowed:1; /* Some objects cannot be renamed. */ ++ u8 unlink_allowed:1; ++ u8 dirty:1; /* the object needs to be written to flash */ ++ u8 valid:1; /* When the file system is being loaded up, this ++ * object might be created before the data ++ * is available ++ * ie. file data chunks encountered before ++ * the header. ++ */ ++ u8 lazy_loaded:1; /* This object has been lazy loaded and ++ * is missing some detail */ ++ ++ u8 defered_free:1; /* Object is removed from NAND, but is ++ * still in the inode cache. ++ * Free of object is defered. ++ * until the inode is released. ++ */ ++ u8 being_created:1; /* This object is still being created ++ * so skip some verification checks. */ ++ u8 is_shadowed:1; /* This object is shadowed on the way ++ * to being renamed. */ ++ ++ u8 xattr_known:1; /* We know if this has object has xattribs ++ * or not. */ ++ u8 has_xattr:1; /* This object has xattribs. ++ * Only valid if xattr_known. */ ++ ++ u8 serial; /* serial number of chunk in NAND.*/ ++ u16 sum; /* sum of the name to speed searching */ ++ ++ struct yaffs_dev *my_dev; /* The device I'm on */ ++ ++ struct list_head hash_link; /* list of objects in hash bucket */ ++ ++ struct list_head hard_links; /* hard linked object chain*/ ++ ++ /* directory structure stuff */ ++ /* also used for linking up the free list */ ++ struct yaffs_obj *parent; ++ struct list_head siblings; ++ ++ /* Where's my object header in NAND? */ ++ int hdr_chunk; ++ ++ int n_data_chunks; /* Number of data chunks for this file. */ ++ ++ u32 obj_id; /* the object id value */ ++ ++ u32 yst_mode; ++ ++ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; ++ ++#ifdef CONFIG_YAFFS_WINCE ++ u32 win_ctime[2]; ++ u32 win_mtime[2]; ++ u32 win_atime[2]; ++#else ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++#endif ++ ++ u32 yst_rdev; ++ ++ void *my_inode; ++ ++ enum yaffs_obj_type variant_type; ++ ++ union yaffs_obj_var variant; ++ ++}; ++ ++struct yaffs_obj_bucket { ++ struct list_head list; ++ int count; ++}; ++ ++/* yaffs_checkpt_obj holds the definition of an object as dumped ++ * by checkpointing. ++ */ ++ ++struct yaffs_checkpt_obj { ++ int struct_type; ++ u32 obj_id; ++ u32 parent_id; ++ int hdr_chunk; ++ enum yaffs_obj_type variant_type:3; ++ u8 deleted:1; ++ u8 soft_del:1; ++ u8 unlinked:1; ++ u8 fake:1; ++ u8 rename_allowed:1; ++ u8 unlink_allowed:1; ++ u8 serial; ++ int n_data_chunks; ++ loff_t size_or_equiv_obj; ++}; ++ ++/*--------------------- Temporary buffers ---------------- ++ * ++ * These are chunk-sized working buffers. Each device has a few. ++ */ ++ ++struct yaffs_buffer { ++ u8 *buffer; ++ int in_use; ++}; ++ ++/*----------------- Device ---------------------------------*/ ++ ++struct yaffs_param { ++ const YCHAR *name; ++ ++ /* ++ * Entry parameters set up way early. Yaffs sets up the rest. ++ * The structure should be zeroed out before use so that unused ++ * and default values are zero. ++ */ ++ ++ int inband_tags; /* Use unband tags */ ++ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to ++ be a power of 2 */ ++ int chunks_per_block; /* does not need to be a power of 2 */ ++ int spare_bytes_per_chunk; /* spare area size */ ++ int start_block; /* Start block we're allowed to use */ ++ int end_block; /* End block we're allowed to use */ ++ int n_reserved_blocks; /* Tuneable so that we can reduce ++ * reserved blocks on NOR and RAM. */ ++ ++ int n_caches; /* If <= 0, then short op caching is disabled, ++ * else the number of short op caches. ++ */ ++ int cache_bypass_aligned; /* If non-zero then bypass the cache for ++ * aligned writes. ++ */ ++ ++ int use_nand_ecc; /* Flag to decide whether or not to use ++ * NAND driver ECC on data (yaffs1) */ ++ int tags_9bytes; /* Use 9 byte tags */ ++ int no_tags_ecc; /* Flag to decide whether or not to do ECC ++ * on packed tags (yaffs2) */ ++ ++ int is_yaffs2; /* Use yaffs2 mode on this device */ ++ ++ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ ++ ++ int refresh_period; /* How often to check for a block refresh */ ++ ++ /* Checkpoint control. Can be set before or after initialisation */ ++ u8 skip_checkpt_rd; ++ u8 skip_checkpt_wr; ++ ++ int enable_xattr; /* Enable xattribs */ ++ ++ int max_objects; /* ++ * Set to limit the number of objects created. ++ * 0 = no limit. ++ */ ++ ++ /* The remove_obj_fn function must be supplied by OS flavours that ++ * need it. ++ * yaffs direct uses it to implement the faster readdir. ++ * Linux uses it to protect the directory during unlocking. ++ */ ++ void (*remove_obj_fn) (struct yaffs_obj *obj); ++ ++ /* Callback to mark the superblock dirty */ ++ void (*sb_dirty_fn) (struct yaffs_dev *dev); ++ ++ /* Callback to control garbage collection. */ ++ unsigned (*gc_control_fn) (struct yaffs_dev *dev); ++ ++ /* Debug control flags. Don't use unless you know what you're doing */ ++ int use_header_file_size; /* Flag to determine if we should use ++ * file sizes from the header */ ++ int disable_lazy_load; /* Disable lazy loading on this device */ ++ int wide_tnodes_disabled; /* Set to disable wide tnodes */ ++ int disable_soft_del; /* yaffs 1 only: Set to disable the use of ++ * softdeletion. */ ++ ++ int defered_dir_update; /* Set to defer directory updates */ ++ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ int auto_unicode; ++#endif ++ int always_check_erased; /* Force chunk erased check always on */ ++ ++ int disable_summary; ++ int disable_bad_block_marking; ++ ++}; ++ ++struct yaffs_driver { ++ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len); ++ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result); ++ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_initialise_fn) (struct yaffs_dev *dev); ++ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); ++}; ++ ++struct yaffs_tags_handler { ++ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags); ++ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags); ++ ++ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++}; ++ ++struct yaffs_dev { ++ struct yaffs_param param; ++ struct yaffs_driver drv; ++ struct yaffs_tags_handler tagger; ++ ++ /* Context storage. Holds extra OS specific data for this device */ ++ ++ void *os_context; ++ void *driver_context; ++ ++ struct list_head dev_list; ++ ++ int ll_init; ++ /* Runtime parameters. Set up by YAFFS. */ ++ int data_bytes_per_chunk; ++ ++ /* Non-wide tnode stuff */ ++ u16 chunk_grp_bits; /* Number of bits that need to be resolved if ++ * the tnodes are not wide enough. ++ */ ++ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ ++ ++ /* Stuff to support wide tnodes */ ++ u32 tnode_width; ++ u32 tnode_mask; ++ u32 tnode_size; ++ ++ /* Stuff for figuring out file offset to chunk conversions */ ++ u32 chunk_shift; /* Shift value */ ++ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ ++ u32 chunk_mask; /* Mask to use for power-of-2 case */ ++ ++ int is_mounted; ++ int read_only; ++ int is_checkpointed; ++ ++ /* Stuff to support block offsetting to support start block zero */ ++ int internal_start_block; ++ int internal_end_block; ++ int block_offset; ++ int chunk_offset; ++ ++ /* Runtime checkpointing stuff */ ++ int checkpt_page_seq; /* running sequence number of checkpt pages */ ++ int checkpt_byte_count; ++ int checkpt_byte_offs; ++ u8 *checkpt_buffer; ++ int checkpt_open_write; ++ int blocks_in_checkpt; ++ int checkpt_cur_chunk; ++ int checkpt_cur_block; ++ int checkpt_next_block; ++ int *checkpt_block_list; ++ int checkpt_max_blocks; ++ u32 checkpt_sum; ++ u32 checkpt_xor; ++ ++ int checkpoint_blocks_required; /* Number of blocks needed to store ++ * current checkpoint set */ ++ ++ /* Block Info */ ++ struct yaffs_block_info *block_info; ++ u8 *chunk_bits; /* bitmap of chunks in use */ ++ u8 block_info_alt:1; /* allocated using alternative alloc */ ++ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ ++ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. ++ * Must be consistent with chunks_per_block. ++ */ ++ ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int alloc_block_finder; /* Used to search for next allocation block */ ++ ++ /* Object and Tnode memory management */ ++ void *allocator; ++ int n_obj; ++ int n_tnodes; ++ ++ int n_hardlinks; ++ ++ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; ++ u32 bucket_finder; ++ ++ int n_free_chunks; ++ ++ /* Garbage collection control */ ++ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ ++ u32 n_clean_ups; ++ ++ unsigned has_pending_prioritised_gc; /* We think this device might ++ have pending prioritised gcs */ ++ unsigned gc_disable; ++ unsigned gc_block_finder; ++ unsigned gc_dirtiest; ++ unsigned gc_pages_in_use; ++ unsigned gc_not_done; ++ unsigned gc_block; ++ unsigned gc_chunk; ++ unsigned gc_skip; ++ struct yaffs_summary_tags *gc_sum_tags; ++ ++ /* Special directories */ ++ struct yaffs_obj *root_dir; ++ struct yaffs_obj *lost_n_found; ++ ++ int buffered_block; /* Which block is buffered here? */ ++ int doing_buffered_block_rewrite; ++ ++ struct yaffs_cache *cache; ++ int cache_last_use; ++ ++ /* Stuff for background deletion and unlinked files. */ ++ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted ++ files live. */ ++ struct yaffs_obj *del_dir; /* Directory where deleted objects are ++ sent to disappear. */ ++ struct yaffs_obj *unlinked_deletion; /* Current file being ++ background deleted. */ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* Temporary buffer management */ ++ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; ++ int max_temp; ++ int temp_in_use; ++ int unmanaged_buffer_allocs; ++ int unmanaged_buffer_deallocs; ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ allocating block */ ++ unsigned oldest_dirty_seq; ++ unsigned oldest_dirty_block; ++ ++ /* Block refreshing */ ++ int refresh_skip; /* A skip down counter. ++ * Refresh happens when this gets to zero. */ ++ ++ /* Dirty directory handling */ ++ struct list_head dirty_dirs; /* List of dirty directories */ ++ ++ /* Summary */ ++ int chunks_per_summary; ++ struct yaffs_summary_tags *sum_tags; ++ ++ /* Statistics */ ++ u32 n_page_writes; ++ u32 n_page_reads; ++ u32 n_erasures; ++ u32 n_bad_queries; ++ u32 n_bad_markings; ++ u32 n_erase_failures; ++ u32 n_gc_copies; ++ u32 all_gcs; ++ u32 passive_gc_count; ++ u32 oldest_dirty_gc_count; ++ u32 n_gc_blocks; ++ u32 bg_gcs; ++ u32 n_retried_writes; ++ u32 n_retired_blocks; ++ u32 n_ecc_fixed; ++ u32 n_ecc_unfixed; ++ u32 n_tags_ecc_fixed; ++ u32 n_tags_ecc_unfixed; ++ u32 n_deletions; ++ u32 n_unmarked_deletions; ++ u32 refresh_count; ++ u32 cache_hits; ++ u32 tags_used; ++ u32 summary_used; ++ ++}; ++ ++/* The CheckpointDevice structure holds the device information that changes ++ *at runtime and must be preserved over unmount/mount cycles. ++ */ ++struct yaffs_checkpt_dev { ++ int struct_type; ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int n_free_chunks; ++ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ * allocating block */ ++ ++}; ++ ++struct yaffs_checkpt_validity { ++ int struct_type; ++ u32 magic; ++ u32 version; ++ u32 head; ++}; ++ ++struct yaffs_shadow_fixer { ++ int obj_id; ++ int shadowed_id; ++ struct yaffs_shadow_fixer *next; ++}; ++ ++/* Structure for doing xattr modifications */ ++struct yaffs_xattr_mod { ++ int set; /* If 0 then this is a deletion */ ++ const YCHAR *name; ++ const void *data; ++ int size; ++ int flags; ++ int result; ++}; ++ ++/*----------------------- YAFFS Functions -----------------------*/ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev); ++void yaffs_deinitialise(struct yaffs_dev *dev); ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, ++ struct yaffs_obj *new_dir, const YCHAR * new_name); ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); ++int yaffs_del_obj(struct yaffs_obj *obj); ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type); ++ ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj); ++int yaffs_get_obj_inode(struct yaffs_obj *obj); ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj); ++int yaffs_get_obj_link_count(struct yaffs_obj *obj); ++ ++/* File operations */ ++int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, ++ int n_bytes); ++int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid); ++ ++int yaffs_flush_file(struct yaffs_obj *in, ++ int update_time, ++ int data_sync, ++ int discard_cache); ++ ++/* Flushing and checkpointing */ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard); ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev); ++int yaffs_checkpoint_restore(struct yaffs_dev *dev); ++ ++/* Directory operations */ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid); ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, ++ const YCHAR *name); ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); ++ ++/* Link operations */ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, ++ struct yaffs_obj *equiv_obj); ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); ++ ++/* Symlink operations */ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias); ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); ++ ++/* Special inodes (fifos, sockets and devices) */ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev); ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, ++ const void *value, int size, int flags); ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, ++ int size); ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); ++ ++/* Special directories */ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj); ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev); ++ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); ++ ++/* Debug dump */ ++int yaffs_dump_obj(struct yaffs_obj *obj); ++ ++void yaffs_guts_test(struct yaffs_dev *dev); ++int yaffs_guts_ll_init(struct yaffs_dev *dev); ++ ++ ++/* A few useful functions to be used within the core files*/ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn); ++int yaffs_check_ff(u8 *buffer, int n_bytes); ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type); ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan); ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh); ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); ++YCHAR *yaffs_clone_str(const YCHAR *str); ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, ++ int force, int is_shrink, int shadows, ++ struct yaffs_xattr_mod *xop); ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning); ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn); ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev); ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id); ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos); ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj); ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev); ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out); ++/* ++ * Marshalling functions to get loff_t file sizes into aand out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); ++loff_t yaffs_max_file_size(struct yaffs_dev *dev); ++ ++/* ++ * Debug function to count number of blocks in each state ++ * NB Needs to be called with correct number of integers ++ */ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_linux.h linux-3.14.43/fs/yaffs2/yaffs_linux.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_linux.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,48 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_LINUX_H__ ++#define __YAFFS_LINUX_H__ ++ ++#include "yportenv.h" ++ ++struct yaffs_linux_context { ++ struct list_head context_list; /* List of these we have mounted */ ++ struct yaffs_dev *dev; ++ struct super_block *super; ++ struct task_struct *bg_thread; /* Background thread for this device */ ++ int bg_running; ++ struct mutex gross_lock; /* Gross locking mutex*/ ++ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size ++ * at compile time so we have to allocate it. ++ */ ++ struct list_head search_contexts; ++ struct task_struct *readdir_process; ++ unsigned mount_id; ++ int dirty; ++}; ++ ++#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) ++#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++#define WRITE_SIZE_STR "writesize" ++#define WRITE_SIZE(mtd) ((mtd)->writesize) ++#else ++#define WRITE_SIZE_STR "oobblock" ++#define WRITE_SIZE(mtd) ((mtd)->oobblock) ++#endif ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_mtdif.c linux-3.14.43/fs/yaffs2/yaffs_mtdif.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_mtdif.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,310 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_mtdif.h" ++ ++#include "linux/mtd/mtd.h" ++#include "linux/types.h" ++#include "linux/time.h" ++#include "linux/mtd/nand.h" ++#include "linux/kernel.h" ++#include "linux/version.h" ++#include "linux/types.h" ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) ++#include "uapi/linux/major.h" ++#endif ++ ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_linux.h" ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++#define mtd_erase(m, ei) (m)->erase(m, ei) ++#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) ++#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) ++#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) ++#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) ++#endif ++ ++ ++ ++int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ u32 addr = ++ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ struct erase_info ei; ++ int retval = 0; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++ ++static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", ++ dev, nand_chunk, data, data_len, oob, oob_len); ++ ++ if (!data || !data_len) { ++ data = NULL; ++ data_len = 0; ++ } ++ ++ if (!oob || !oob_len) { ++ oob = NULL; ++ oob_len = 0; ++ } ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = (u8 *)data; ++ ops.oobbuf = (u8 *)oob; ++ ++ retval = mtd_write_oob(mtd, addr, &ops); ++ if (retval) { ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "write_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ } ++ return retval ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = data; ++ ops.oobbuf = oob; ++ ++#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) ++ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; ++ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. ++ */ ++ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; ++#endif ++ /* Read page and oob using MTD. ++ * Check status and determine ECC result. ++ */ ++ retval = mtd_read_oob(mtd, addr, &ops); ++ if (retval) ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "read_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ ++ switch (retval) { ++ case 0: ++ /* no error */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ ++ case -EUCLEAN: ++ /* MTD's ECC fixed the data */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ break; ++ ++ case -EBADMSG: ++ default: ++ /* MTD's ECC could not fix the data */ ++ dev->n_ecc_unfixed++; ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ loff_t addr; ++ struct erase_info ei; ++ int retval = 0; ++ u32 block_size; ++ ++ block_size = dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ addr = ((loff_t) block_no) * block_size; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = block_size; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); ++ ++ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); ++ ++ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_initialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ ++ drv->drv_write_chunk_fn = yaffs_mtd_write; ++ drv->drv_read_chunk_fn = yaffs_mtd_read; ++ drv->drv_erase_fn = yaffs_mtd_erase; ++ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; ++ drv->drv_check_bad_fn = yaffs_mtd_check_bad; ++ drv->drv_initialise_fn = yaffs_mtd_initialise; ++ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; ++} ++ ++ ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev) ++{ ++ struct mtd_info *mtd; ++ ++ mtd = yaffs_get_mtd_device(sdev); ++ ++ /* Check it's an mtd device..... */ ++ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) ++ return NULL; /* This isn't an mtd device */ ++ ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device is not NAND it's type %d", ++ mtd->type); ++ return NULL; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); ++ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); ++ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); ++#else ++ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); ++#endif ++ ++ return mtd; ++} ++ ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) ++{ ++ if (yaffs_version == 2) { ++ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || ++ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && ++ !inband_tags) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not have the right page sizes" ++ ); ++ return -1; ++ } ++ } else { ++ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || ++ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support have the right page sizes" ++ ); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++void yaffs_put_mtd_device(struct mtd_info *mtd) ++{ ++ if(mtd) ++ put_mtd_device(mtd); ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_mtdif.h linux-3.14.43/fs/yaffs2/yaffs_mtdif.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_mtdif.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,25 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_MTDIF_H__ ++#define __YAFFS_MTDIF_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev); ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev); ++void yaffs_put_mtd_device(struct mtd_info *mtd); ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_nameval.c linux-3.14.43/fs/yaffs2/yaffs_nameval.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_nameval.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,208 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This simple implementation of a name-value store assumes a small number of ++* values and fits into a small finite buffer. ++ * ++ * Each attribute is stored as a record: ++ * sizeof(int) bytes record size. ++ * strnlen+1 bytes name null terminated. ++ * nbytes value. ++ * ---------- ++ * total size stored in record size ++ * ++ * This code has not been tested with unicode yet. ++ */ ++ ++#include "yaffs_nameval.h" ++ ++#include "yportenv.h" ++ ++static int nval_find(const char *xb, int xb_size, const YCHAR *name, ++ int *exist_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), ++ name, size)) { ++ if (exist_size) ++ *exist_size = size; ++ return pos; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ if (exist_size) ++ *exist_size = 0; ++ return -ENODATA; ++} ++ ++static int nval_used(const char *xb, int xb_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return pos; ++} ++ ++int nval_del(char *xb, int xb_size, const YCHAR *name) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos < 0 || pos >= xb_size) ++ return -ENODATA; ++ ++ /* Find size, shift rest over this record, ++ * then zero out the rest of buffer */ ++ memcpy(&size, xb + pos, sizeof(int)); ++ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); ++ memset(xb + (xb_size - size), 0, size); ++ return 0; ++} ++ ++int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, ++ int bsize, int flags) ++{ ++ int pos; ++ int namelen = strnlen(name, xb_size); ++ int reclen; ++ int size_exist = 0; ++ int space; ++ int start; ++ ++ pos = nval_find(xb, xb_size, name, &size_exist); ++ ++ if (flags & XATTR_CREATE && pos >= 0) ++ return -EEXIST; ++ if (flags & XATTR_REPLACE && pos < 0) ++ return -ENODATA; ++ ++ start = nval_used(xb, xb_size); ++ space = xb_size - start + size_exist; ++ ++ reclen = (sizeof(int) + namelen + 1 + bsize); ++ ++ if (reclen > space) ++ return -ENOSPC; ++ ++ if (pos >= 0) { ++ nval_del(xb, xb_size, name); ++ start = nval_used(xb, xb_size); ++ } ++ ++ pos = start; ++ ++ memcpy(xb + pos, &reclen, sizeof(int)); ++ pos += sizeof(int); ++ strncpy((YCHAR *) (xb + pos), name, reclen); ++ pos += (namelen + 1); ++ memcpy(xb + pos, buf, bsize); ++ return 0; ++} ++ ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos >= 0 && pos < xb_size) { ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ pos += sizeof(int); /* advance past record length */ ++ size -= sizeof(int); ++ ++ /* Advance over name string */ ++ while (xb[pos] && size > 0 && pos < xb_size) { ++ pos++; ++ size--; ++ } ++ /*Advance over NUL */ ++ pos++; ++ size--; ++ ++ /* If bsize is zero then this is a size query. ++ * Return the size, but don't copy. ++ */ ++ if (!bsize) ++ return size; ++ ++ if (size <= bsize) { ++ memcpy(buf, xb + pos, size); ++ return size; ++ } ++ } ++ if (pos >= 0) ++ return -ERANGE; ++ ++ return -ENODATA; ++} ++ ++int nval_list(const char *xb, int xb_size, char *buf, int bsize) ++{ ++ int pos = 0; ++ int size; ++ int name_len; ++ int ncopied = 0; ++ int filled = 0; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > sizeof(int) && ++ size <= xb_size && ++ (pos + size) < xb_size && ++ !filled) { ++ pos += sizeof(int); ++ size -= sizeof(int); ++ name_len = strnlen((YCHAR *) (xb + pos), size); ++ if (ncopied + name_len + 1 < bsize) { ++ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); ++ buf += name_len; ++ *buf = '\0'; ++ buf++; ++ if (sizeof(YCHAR) > 1) { ++ *buf = '\0'; ++ buf++; ++ } ++ ncopied += (name_len + 1); ++ } else { ++ filled = 1; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return ncopied; ++} ++ ++int nval_hasvalues(const char *xb, int xb_size) ++{ ++ return nval_used(xb, xb_size) > 0; ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_nameval.h linux-3.14.43/fs/yaffs2/yaffs_nameval.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_nameval.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __NAMEVAL_H__ ++#define __NAMEVAL_H__ ++ ++#include "yportenv.h" ++ ++int nval_del(char *xb, int xb_size, const YCHAR * name); ++int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, ++ int bsize, int flags); ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize); ++int nval_list(const char *xb, int xb_size, char *buf, int bsize); ++int nval_hasvalues(const char *xb, int xb_size); ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_nand.c linux-3.14.43/fs/yaffs2/yaffs_nand.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_nand.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,122 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_nand.h" ++#include "yaffs_tagscompat.h" ++ ++#include "yaffs_getblockinfo.h" ++#include "yaffs_summary.h" ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ struct yaffs_ext_tags local_tags; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_reads++; ++ ++ /* If there are no tags provided use local tags. */ ++ if (!tags) ++ tags = &local_tags; ++ ++ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); ++ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { ++ ++ struct yaffs_block_info *bi; ++ bi = yaffs_get_block_info(dev, ++ nand_chunk / ++ dev->param.chunks_per_block); ++ yaffs_handle_chunk_error(dev, bi); ++ } ++ return result; ++} ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_writes++; ++ ++ if (!tags) { ++ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ tags->seq_number = dev->seq_number; ++ tags->chunk_used = 1; ++ yaffs_trace(YAFFS_TRACE_WRITE, ++ "Writing chunk %d tags %d %d", ++ nand_chunk, tags->obj_id, tags->chunk_id); ++ ++ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, ++ buffer, tags); ++ ++ yaffs_summary_add(dev, tags, nand_chunk); ++ ++ return result; ++} ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ block_no -= dev->block_offset; ++ dev->n_bad_markings++; ++ ++ if (dev->param.disable_bad_block_marking) ++ return YAFFS_OK; ++ ++ return dev->tagger.mark_bad_fn(dev, block_no); ++} ++ ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ block_no -= dev->block_offset; ++ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); ++} ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ int result; ++ ++ block_no -= dev->block_offset; ++ dev->n_erasures++; ++ result = dev->drv.drv_erase_fn(dev, block_no); ++ return result; ++} ++ ++int yaffs_init_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_initialise_fn) ++ return dev->drv.drv_initialise_fn(dev); ++ return YAFFS_OK; ++} ++ ++int yaffs_deinit_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_deinitialise_fn) ++ return dev->drv.drv_deinitialise_fn(dev); ++ return YAFFS_OK; ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_nand.h linux-3.14.43/fs/yaffs2/yaffs_nand.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_nand.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_NAND_H__ ++#define __YAFFS_NAND_H__ ++#include "yaffs_guts.h" ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ unsigned *seq_number); ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); ++ ++int yaffs_init_nand(struct yaffs_dev *dev); ++int yaffs_deinit_nand(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.14.43/fs/yaffs2/yaffs_packedtags1.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_packedtags1.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,56 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags1.h" ++#include "yportenv.h" ++ ++static const u8 all_ff[20] = { ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t) ++{ ++ pt->chunk_id = t->chunk_id; ++ pt->serial_number = t->serial_number; ++ pt->n_bytes = t->n_bytes; ++ pt->obj_id = t->obj_id; ++ pt->ecc = 0; ++ pt->deleted = (t->is_deleted) ? 0 : 1; ++ pt->unused_stuff = 0; ++ pt->should_be_ff = 0xffffffff; ++} ++ ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt) ++{ ++ ++ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { ++ t->block_bad = 0; ++ if (pt->should_be_ff != 0xffffffff) ++ t->block_bad = 1; ++ t->chunk_used = 1; ++ t->obj_id = pt->obj_id; ++ t->chunk_id = pt->chunk_id; ++ t->n_bytes = pt->n_bytes; ++ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ t->is_deleted = (pt->deleted) ? 0 : 1; ++ t->serial_number = pt->serial_number; ++ } else { ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ } ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.14.43/fs/yaffs2/yaffs_packedtags1.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_packedtags1.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS1_H__ ++#define __YAFFS_PACKEDTAGS1_H__ ++ ++#include "yaffs_guts.h" ++ ++struct yaffs_packed_tags1 { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 deleted:1; ++ u32 unused_stuff:1; ++ unsigned should_be_ff; ++ ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt); ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.14.43/fs/yaffs2/yaffs_packedtags2.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_packedtags2.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,197 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags2.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++/* This code packs a set of extended tags into a binary structure for ++ * NAND storage ++ */ ++ ++/* Some of the information is "extra" struff which can be packed in to ++ * speed scanning ++ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. ++ */ ++ ++/* Extra flags applied to chunk_id */ ++ ++#define EXTRA_HEADER_INFO_FLAG 0x80000000 ++#define EXTRA_SHRINK_FLAG 0x40000000 ++#define EXTRA_SHADOWS_FLAG 0x20000000 ++#define EXTRA_SPARE_FLAGS 0x10000000 ++ ++#define ALL_EXTRA_FLAGS 0xf0000000 ++ ++/* Also, the top 4 bits of the object Id are set to the object type. */ ++#define EXTRA_OBJECT_TYPE_SHIFT (28) ++#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) ++ ++static void yaffs_dump_packed_tags2_tags_only( ++ const struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "packed tags obj %d chunk %d byte %d seq %d", ++ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); ++} ++ ++static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) ++{ ++ yaffs_dump_packed_tags2_tags_only(&pt->t); ++} ++ ++static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", ++ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, ++ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, ++ t->seq_number); ++ ++} ++ ++static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) ++{ ++ if (t->chunk_id != 0 || !t->extra_available) ++ return 0; ++ ++ /* Check if the file size is too long to store */ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && ++ (t->extra_file_size >> 31) != 0) ++ return 0; ++ return 1; ++} ++ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, ++ const struct yaffs_ext_tags *t) ++{ ++ ptt->chunk_id = t->chunk_id; ++ ptt->seq_number = t->seq_number; ++ ptt->n_bytes = t->n_bytes; ++ ptt->obj_id = t->obj_id; ++ ++ /* Only store extra tags for object headers. ++ * If it is a file then only store if the file size is short\ ++ * enough to fit. ++ */ ++ if (yaffs_check_tags_extra_packable(t)) { ++ /* Store the extra header info instead */ ++ /* We save the parent object in the chunk_id */ ++ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; ++ if (t->extra_is_shrink) ++ ptt->chunk_id |= EXTRA_SHRINK_FLAG; ++ if (t->extra_shadows) ++ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; ++ ++ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ ptt->n_bytes = t->extra_equiv_id; ++ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ ptt->n_bytes = (unsigned) t->extra_file_size; ++ else ++ ptt->n_bytes = 0; ++ } ++ ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc) ++{ ++ yaffs_pack_tags2_tags_only(&pt->t, t); ++ ++ if (tags_ecc) ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc); ++} ++ ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ ++ if (ptt->seq_number == 0xffffffff) ++ return; ++ ++ t->block_bad = 0; ++ t->chunk_used = 1; ++ t->obj_id = ptt->obj_id; ++ t->chunk_id = ptt->chunk_id; ++ t->n_bytes = ptt->n_bytes; ++ t->is_deleted = 0; ++ t->serial_number = 0; ++ t->seq_number = ptt->seq_number; ++ ++ /* Do extra header info stuff */ ++ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { ++ t->chunk_id = 0; ++ t->n_bytes = 0; ++ ++ t->extra_available = 1; ++ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); ++ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; ++ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; ++ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; ++ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ t->extra_equiv_id = ptt->n_bytes; ++ else ++ t->extra_file_size = ptt->n_bytes; ++ } ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc) ++{ ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ if (pt->t.seq_number != 0xffffffff && tags_ecc) { ++ /* Chunk is in use and we need to do ECC */ ++ ++ struct yaffs_ecc_other ecc; ++ int result; ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &ecc); ++ result = ++ yaffs_ecc_correct_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc, &ecc); ++ switch (result) { ++ case 0: ++ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ case 1: ++ ecc_result = YAFFS_ECC_RESULT_FIXED; ++ break; ++ case -1: ++ ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ break; ++ default: ++ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ } ++ } ++ yaffs_unpack_tags2_tags_only(t, &pt->t); ++ ++ t->ecc_result = ecc_result; ++ ++ yaffs_dump_packed_tags2(pt); ++ yaffs_dump_tags2(t); ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.14.43/fs/yaffs2/yaffs_packedtags2.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_packedtags2.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,47 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS2_H__ ++#define __YAFFS_PACKEDTAGS2_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_ecc.h" ++ ++struct yaffs_packed_tags2_tags_only { ++ unsigned seq_number; ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++struct yaffs_packed_tags2 { ++ struct yaffs_packed_tags2_tags_only t; ++ struct yaffs_ecc_other ecc; ++}; ++ ++/* Full packed tags with ECC, used for oob tags */ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc); ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc); ++ ++/* Only the tags part (no ECC for use with inband tags */ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *pt); ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_summary.c linux-3.14.43/fs/yaffs2/yaffs_summary.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_summary.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,312 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Summaries write the useful part of the tags for the chunks in a block into an ++ * an array which is written to the last n chunks of the block. ++ * Reading the summaries gives all the tags for the block in one read. Much ++ * faster. ++ * ++ * Chunks holding summaries are marked with tags making it look like ++ * they are part of a fake file. ++ * ++ * The summary could also be used during gc. ++ * ++ */ ++ ++#include "yaffs_summary.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_bitmap.h" ++ ++/* ++ * The summary is built up in an array of summary tags. ++ * This gets written to the last one or two (maybe more) chunks in a block. ++ * A summary header is written as the first part of each chunk of summary data. ++ * The summary header must match or the summary is rejected. ++ */ ++ ++/* Summary tags don't need the sequence number because that is redundant. */ ++struct yaffs_summary_tags { ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++/* Summary header */ ++struct yaffs_summary_header { ++ unsigned version; /* Must match current version */ ++ unsigned block; /* Must be this block */ ++ unsigned seq; /* Must be this sequence number */ ++ unsigned sum; /* Just add up all the bytes in the tags */ ++}; ++ ++ ++static void yaffs_summary_clear(struct yaffs_dev *dev) ++{ ++ if (!dev->sum_tags) ++ return; ++ memset(dev->sum_tags, 0, dev->chunks_per_summary * ++ sizeof(struct yaffs_summary_tags)); ++} ++ ++ ++void yaffs_summary_deinit(struct yaffs_dev *dev) ++{ ++ kfree(dev->sum_tags); ++ dev->sum_tags = NULL; ++ kfree(dev->gc_sum_tags); ++ dev->gc_sum_tags = NULL; ++ dev->chunks_per_summary = 0; ++} ++ ++int yaffs_summary_init(struct yaffs_dev *dev) ++{ ++ int sum_bytes; ++ int chunks_used; /* Number of chunks used by summary */ ++ int sum_tags_bytes; ++ ++ sum_bytes = dev->param.chunks_per_block * ++ sizeof(struct yaffs_summary_tags); ++ ++ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ ++ (dev->data_bytes_per_chunk - ++ sizeof(struct yaffs_summary_header)); ++ ++ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ if (!dev->sum_tags || !dev->gc_sum_tags) { ++ yaffs_summary_deinit(dev); ++ return YAFFS_FAIL; ++ } ++ ++ yaffs_summary_clear(dev); ++ ++ return YAFFS_OK; ++} ++ ++static unsigned yaffs_summary_sum(struct yaffs_dev *dev) ++{ ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int i; ++ unsigned sum = 0; ++ ++ i = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ while (i > 0) { ++ sum += *sum_buffer; ++ sum_buffer++; ++ i--; ++ } ++ ++ return sum; ++} ++ ++static int yaffs_summary_write(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int n_bytes; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); ++ tags.obj_id = YAFFS_OBJECTID_SUMMARY; ++ tags.chunk_id = 1; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ hdr.version = YAFFS_SUMMARY_VERSION; ++ hdr.block = blk; ++ hdr.seq = bi->seq_number; ++ hdr.sum = yaffs_summary_sum(dev); ++ ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ memcpy(buffer, &hdr, sizeof(hdr)); ++ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); ++ tags.n_bytes = this_tx + sizeof(hdr); ++ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ break; ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ dev->n_free_chunks--; ++ ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ tags.chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ ++ if (result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ ++ return result; ++} ++ ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)st; ++ int n_bytes; ++ int chunk_id; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ int sum_tags_bytes; ++ ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = blk * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ chunk_id = 1; ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (tags.chunk_id != chunk_id || ++ tags.obj_id != YAFFS_OBJECTID_SUMMARY || ++ tags.chunk_used == 0 || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.n_bytes != (this_tx + sizeof(hdr))) ++ result = YAFFS_FAIL; ++ if (result != YAFFS_OK) ++ break; ++ ++ if (st == dev->sum_tags) { ++ /* If we're scanning then update the block info */ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ } ++ memcpy(&hdr, buffer, sizeof(hdr)); ++ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (result == YAFFS_OK) { ++ /* Verify header */ ++ if (hdr.version != YAFFS_SUMMARY_VERSION || ++ hdr.seq != bi->seq_number || ++ hdr.sum != yaffs_summary_sum(dev)) ++ result = YAFFS_FAIL; ++ } ++ ++ if (st == dev->sum_tags && result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ return result; ++} ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_nand) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; ++ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; ++ ++ if (!dev->sum_tags) ++ return YAFFS_OK; ++ ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ yaffs_pack_tags2_tags_only(&tags_only, tags); ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ sum_tags->chunk_id = tags_only.chunk_id; ++ sum_tags->n_bytes = tags_only.n_bytes; ++ sum_tags->obj_id = tags_only.obj_id; ++ ++ if (chunk_in_block == dev->chunks_per_summary - 1) { ++ /* Time to write out the summary */ ++ yaffs_summary_write(dev, block_in_nand); ++ yaffs_summary_clear(dev); ++ yaffs_skip_rest_of_block(dev); ++ } ++ } ++ return YAFFS_OK; ++} ++ ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ tags_only.chunk_id = sum_tags->chunk_id; ++ tags_only.n_bytes = sum_tags->n_bytes; ++ tags_only.obj_id = sum_tags->obj_id; ++ yaffs_unpack_tags2_tags_only(tags, &tags_only); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int i; ++ ++ if (!bi->has_summary) ++ return; ++ ++ for (i = dev->chunks_per_summary; ++ i < dev->param.chunks_per_block; ++ i++) { ++ if (yaffs_check_chunk_bit(dev, blk, i)) { ++ yaffs_clear_chunk_bit(dev, blk, i); ++ bi->pages_in_use--; ++ dev->n_free_chunks++; ++ } ++ } ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_summary.h linux-3.14.43/fs/yaffs2/yaffs_summary.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_summary.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,37 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_SUMMARY_H__ ++#define __YAFFS_SUMMARY_H__ ++ ++#include "yaffs_packedtags2.h" ++ ++ ++int yaffs_summary_init(struct yaffs_dev *dev); ++void yaffs_summary_deinit(struct yaffs_dev *dev); ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk); ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk); ++ ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.14.43/fs/yaffs2/yaffs_tagscompat.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_tagscompat.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,381 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_ecc.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_trace.h" ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); ++ ++ ++/********** Tags ECC calculations *********/ ++ ++ ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags) ++{ ++ /* Calculate an ecc */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ unsigned i, j; ++ unsigned ecc = 0; ++ unsigned bit = 0; ++ ++ tags->ecc = 0; ++ ++ for (i = 0; i < 8; i++) { ++ for (j = 1; j & 0xff; j <<= 1) { ++ bit++; ++ if (b[i] & j) ++ ecc ^= bit; ++ } ++ } ++ tags->ecc = ecc; ++} ++ ++int yaffs_check_tags_ecc(struct yaffs_tags *tags) ++{ ++ unsigned ecc = tags->ecc; ++ ++ yaffs_calc_tags_ecc(tags); ++ ++ ecc ^= tags->ecc; ++ ++ if (ecc && ecc <= 64) { ++ /* TODO: Handle the failure better. Retire? */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ ++ ecc--; ++ ++ b[ecc / 8] ^= (1 << (ecc & 7)); ++ ++ /* Now recvalc the ecc */ ++ yaffs_calc_tags_ecc(tags); ++ ++ return 1; /* recovered error */ ++ } else if (ecc) { ++ /* Wierd ecc failure value */ ++ /* TODO Need to do somethiong here */ ++ return -1; /* unrecovered error */ ++ } ++ return 0; ++} ++ ++/********** Tags **********/ ++ ++static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ ++ yaffs_calc_tags_ecc(tags_ptr); ++ ++ spare_ptr->tb0 = tu->as_bytes[0]; ++ spare_ptr->tb1 = tu->as_bytes[1]; ++ spare_ptr->tb2 = tu->as_bytes[2]; ++ spare_ptr->tb3 = tu->as_bytes[3]; ++ spare_ptr->tb4 = tu->as_bytes[4]; ++ spare_ptr->tb5 = tu->as_bytes[5]; ++ spare_ptr->tb6 = tu->as_bytes[6]; ++ spare_ptr->tb7 = tu->as_bytes[7]; ++} ++ ++static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, ++ struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ int result; ++ ++ tu->as_bytes[0] = spare_ptr->tb0; ++ tu->as_bytes[1] = spare_ptr->tb1; ++ tu->as_bytes[2] = spare_ptr->tb2; ++ tu->as_bytes[3] = spare_ptr->tb3; ++ tu->as_bytes[4] = spare_ptr->tb4; ++ tu->as_bytes[5] = spare_ptr->tb5; ++ tu->as_bytes[6] = spare_ptr->tb6; ++ tu->as_bytes[7] = spare_ptr->tb7; ++ ++ result = yaffs_check_tags_ecc(tags_ptr); ++ if (result > 0) ++ dev->n_tags_ecc_fixed++; ++ else if (result < 0) ++ dev->n_tags_ecc_unfixed++; ++} ++ ++static void yaffs_spare_init(struct yaffs_spare *spare) ++{ ++ memset(spare, 0xff, sizeof(struct yaffs_spare)); ++} ++ ++static int yaffs_wr_nand(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ struct yaffs_spare *spare) ++{ ++ int data_size = dev->data_bytes_per_chunk; ++ ++ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, sizeof(*spare)); ++} ++ ++static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, ++ struct yaffs_spare *spare, ++ enum yaffs_ecc_result *ecc_result, ++ int correct_errors) ++{ ++ int ret_val; ++ struct yaffs_spare local_spare; ++ int data_size; ++ int spare_size; ++ int ecc_result1, ecc_result2; ++ u8 calc_ecc[3]; ++ ++ if (!spare) { ++ /* If we don't have a real spare, then we use a local one. */ ++ /* Need this for the calculation of the ecc */ ++ spare = &local_spare; ++ } ++ data_size = dev->data_bytes_per_chunk; ++ spare_size = sizeof(struct yaffs_spare); ++ ++ if (dev->param.use_nand_ecc) ++ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, spare_size, ++ ecc_result); ++ ++ ++ /* Handle the ECC at this level. */ ++ ++ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *)spare, spare_size, ++ NULL); ++ if (!data || !correct_errors) ++ return ret_val; ++ ++ /* Do ECC correction if needed. */ ++ yaffs_ecc_calc(data, calc_ecc); ++ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); ++ yaffs_ecc_calc(&data[256], calc_ecc); ++ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); ++ ++ if (ecc_result1 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result1 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result2 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result2 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result1 || ecc_result2) { ++ /* We had a data problem on this page */ ++ yaffs_handle_rd_data_error(dev, nand_chunk); ++ } ++ ++ if (ecc_result1 < 0 || ecc_result2 < 0) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ else if (ecc_result1 > 0 || ecc_result2 > 0) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ else ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ return ret_val; ++} ++ ++/* ++ * Functions for robustisizing ++ */ ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ ++ /* Mark the block for retirement */ ++ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> ++ needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>>Block %d marked for retirement", ++ flash_block); ++ ++ /* TODO: ++ * Just do a garbage collection on the affected block ++ * then retire the block ++ * NB recursion ++ */ ++} ++ ++static int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ ++ yaffs_spare_init(&spare); ++ ++ if (ext_tags->is_deleted) ++ spare.page_status = 0; ++ else { ++ tags.obj_id = ext_tags->obj_id; ++ tags.chunk_id = ext_tags->chunk_id; ++ ++ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; ++ else ++ tags.n_bytes_msb = 3; ++ ++ tags.serial_number = ext_tags->serial_number; ++ ++ if (!dev->param.use_nand_ecc && data) { ++ yaffs_ecc_calc(data, spare.ecc1); ++ yaffs_ecc_calc(&data[256], spare.ecc2); ++ } ++ ++ yaffs_load_tags_to_spare(&spare, &tags); ++ } ++ return yaffs_wr_nand(dev, nand_chunk, data, &spare); ++} ++ ++static int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ int deleted; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ if (!yaffs_rd_chunk_nand(dev, nand_chunk, ++ data, &spare, &ecc_result, 1)) ++ return YAFFS_FAIL; ++ ++ /* ext_tags may be NULL */ ++ if (!ext_tags) ++ return YAFFS_OK; ++ ++ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; ++ ++ ext_tags->is_deleted = deleted; ++ ext_tags->ecc_result = ecc_result; ++ ext_tags->block_bad = 0; /* We're reading it */ ++ /* therefore it is not a bad block */ ++ ext_tags->chunk_used = ++ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; ++ ++ if (ext_tags->chunk_used) { ++ yaffs_get_tags_from_spare(dev, &spare, &tags); ++ ext_tags->obj_id = tags.obj_id; ++ ext_tags->chunk_id = tags.chunk_id; ++ ext_tags->n_bytes = tags.n_bytes_lsb; ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ ext_tags->n_bytes |= ++ (((unsigned)tags.n_bytes_msb) << 10); ++ ++ ext_tags->serial_number = tags.serial_number; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_spare spare; ++ ++ memset(&spare, 0xff, sizeof(struct yaffs_spare)); ++ ++ spare.block_status = 'Y'; ++ ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, ++ &spare); ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, ++ NULL, &spare); ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ struct yaffs_spare spare0, spare1; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ enum yaffs_ecc_result dummy; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ *seq_number = 0; ++ ++ /* Look for bad block markers in the first two chunks */ ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, ++ NULL, &spare0, &dummy, 0); ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, ++ NULL, &spare1, &dummy, 0); ++ ++ if (hweight8(spare0.block_status & spare1.block_status) < 7) ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ else ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ ++ return YAFFS_OK; ++} ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev) ++{ ++ if(dev->param.is_yaffs2) ++ return; ++ if(!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; ++ if(!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; ++ if(!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; ++ if(!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.14.43/fs/yaffs2/yaffs_tagscompat.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_tagscompat.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSCOMPAT_H__ ++#define __YAFFS_TAGSCOMPAT_H__ ++ ++ ++#include "yaffs_guts.h" ++ ++#if 0 ++ ++ ++int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); ++int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ ++#endif ++ ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev); ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags); ++int yaffs_check_tags_ecc(struct yaffs_tags *tags); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.14.43/fs/yaffs2/yaffs_tagsmarshall.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_tagsmarshall.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,199 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_packedtags2.h" ++ ++static int yaffs_tags_marshall_write(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ struct yaffs_packed_tags2 pt; ++ int retval; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_write chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ /* For yaffs2 writing there must be both data and tags. ++ * If we're using inband tags, then the tags are stuffed into ++ * the end of the data buffer. ++ */ ++ if (!data || !tags) ++ BUG(); ++ else if (dev->param.inband_tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *)(data + ++ dev-> ++ data_bytes_per_chunk); ++ yaffs_pack_tags2_tags_only(pt2tp, tags); ++ } else { ++ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); ++ } ++ ++ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ (dev->param.inband_tags) ? NULL : packed_tags_ptr, ++ (dev->param.inband_tags) ? 0 : packed_tags_size); ++ ++ return retval; ++} ++ ++static int yaffs_tags_marshall_read(struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = 0; ++ int local_data = 0; ++ u8 spare_buffer[100]; ++ enum yaffs_ecc_result ecc_result; ++ ++ struct yaffs_packed_tags2 pt; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_read chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ if (dev->param.inband_tags) { ++ if (!data) { ++ local_data = 1; ++ data = yaffs_get_temp_buffer(dev); ++ } ++ } ++ ++ if (dev->param.inband_tags || (data && !tags)) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ NULL, 0, ++ &ecc_result); ++ else if (tags) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ spare_buffer, packed_tags_size, ++ &ecc_result); ++ else ++ BUG(); ++ ++ ++ if (dev->param.inband_tags) { ++ if (tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *) ++ &data[dev->data_bytes_per_chunk]; ++ yaffs_unpack_tags2_tags_only(tags, pt2tp); ++ } ++ } else if (tags) { ++ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); ++ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); ++ } ++ ++ if (local_data) ++ yaffs_release_temp_buffer(dev, data); ++ ++ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { ++ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { ++ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) ++ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ } ++ ++ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", ++ block_no); ++ ++ retval = dev->drv.drv_check_bad_fn(dev, block_no); ++ ++ if (retval== YAFFS_FAIL) { ++ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); ++ ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ *seq_number = 0; ++ } else { ++ struct yaffs_ext_tags t; ++ ++ yaffs_tags_marshall_read(dev, ++ block_no * dev->param.chunks_per_block, ++ NULL, &t); ++ ++ if (t.chunk_used) { ++ *seq_number = t.seq_number; ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ } else { ++ *seq_number = 0; ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "block query returns seq %d state %d", ++ *seq_number, *state); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ return dev->drv.drv_mark_bad_fn(dev, block_no); ++ ++} ++ ++ ++void yaffs_tags_marshall_install(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; ++ ++ if (!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; ++ ++ if (!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; ++ ++ if (!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; ++ ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.14.43/fs/yaffs2/yaffs_tagsmarshall.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_tagsmarshall.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSMARSHALL_H__ ++#define __YAFFS_TAGSMARSHALL_H__ ++ ++#include "yaffs_guts.h" ++void yaffs_tags_marshall_install(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_trace.h linux-3.14.43/fs/yaffs2/yaffs_trace.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_trace.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,57 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YTRACE_H__ ++#define __YTRACE_H__ ++ ++extern unsigned int yaffs_trace_mask; ++extern unsigned int yaffs_wr_attempts; ++ ++/* ++ * Tracing flags. ++ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. ++ */ ++ ++#define YAFFS_TRACE_OS 0x00000002 ++#define YAFFS_TRACE_ALLOCATE 0x00000004 ++#define YAFFS_TRACE_SCAN 0x00000008 ++#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 ++#define YAFFS_TRACE_ERASE 0x00000020 ++#define YAFFS_TRACE_GC 0x00000040 ++#define YAFFS_TRACE_WRITE 0x00000080 ++#define YAFFS_TRACE_TRACING 0x00000100 ++#define YAFFS_TRACE_DELETION 0x00000200 ++#define YAFFS_TRACE_BUFFERS 0x00000400 ++#define YAFFS_TRACE_NANDACCESS 0x00000800 ++#define YAFFS_TRACE_GC_DETAIL 0x00001000 ++#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 ++#define YAFFS_TRACE_MTD 0x00004000 ++#define YAFFS_TRACE_CHECKPOINT 0x00008000 ++ ++#define YAFFS_TRACE_VERIFY 0x00010000 ++#define YAFFS_TRACE_VERIFY_NAND 0x00020000 ++#define YAFFS_TRACE_VERIFY_FULL 0x00040000 ++#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 ++ ++#define YAFFS_TRACE_SYNC 0x00100000 ++#define YAFFS_TRACE_BACKGROUND 0x00200000 ++#define YAFFS_TRACE_LOCK 0x00400000 ++#define YAFFS_TRACE_MOUNT 0x00800000 ++ ++#define YAFFS_TRACE_ERROR 0x40000000 ++#define YAFFS_TRACE_BUG 0x80000000 ++#define YAFFS_TRACE_ALWAYS 0xf0000000 ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_verify.c linux-3.14.43/fs/yaffs2/yaffs_verify.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_verify.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,529 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_verify.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++ ++int yaffs_skip_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & ++ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_full_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_nand_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); ++} ++ ++static const char * const block_state_name[] = { ++ "Unknown", ++ "Needs scan", ++ "Scanning", ++ "Empty", ++ "Allocating", ++ "Full", ++ "Dirty", ++ "Checkpoint", ++ "Collecting", ++ "Dead" ++}; ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) ++{ ++ int actually_used; ++ int in_use; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Report illegal runtime states */ ++ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has undefined state %d", ++ n, bi->block_state); ++ ++ switch (bi->block_state) { ++ case YAFFS_BLOCK_STATE_UNKNOWN: ++ case YAFFS_BLOCK_STATE_SCANNING: ++ case YAFFS_BLOCK_STATE_NEEDS_SCAN: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has bad run-state %s", ++ n, block_state_name[bi->block_state]); ++ } ++ ++ /* Check pages in use and soft deletions are legal */ ++ ++ actually_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->pages_in_use < 0 || ++ bi->pages_in_use > dev->param.chunks_per_block || ++ bi->soft_del_pages < 0 || ++ bi->soft_del_pages > dev->param.chunks_per_block || ++ actually_used < 0 || actually_used > dev->param.chunks_per_block) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has illegal values pages_in_used %d soft_del_pages %d", ++ n, bi->pages_in_use, bi->soft_del_pages); ++ ++ /* Check chunk bitmap legal */ ++ in_use = yaffs_count_chunk_bits(dev, n); ++ if (in_use != bi->pages_in_use) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", ++ n, bi->pages_in_use, in_use); ++} ++ ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n) ++{ ++ yaffs_verify_blk(dev, bi, n); ++ ++ /* After collection the block should be in the erased state */ ++ ++ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && ++ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Block %d is in state %d after gc, should be erased", ++ n, bi->block_state); ++ } ++} ++ ++void yaffs_verify_blocks(struct yaffs_dev *dev) ++{ ++ int i; ++ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; ++ int illegal_states = 0; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ memset(state_count, 0, sizeof(state_count)); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ yaffs_verify_blk(dev, bi, i); ++ ++ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) ++ state_count[bi->block_state]++; ++ else ++ illegal_states++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%d blocks have illegal states", ++ illegal_states); ++ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many allocating blocks"); ++ ++ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%s %d blocks", ++ block_state_name[i], state_count[i]); ++ ++ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Checkpoint block count wrong dev %d count %d", ++ dev->blocks_in_checkpt, ++ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); ++ ++ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Erased block count wrong dev %d count %d", ++ dev->n_erased_blocks, ++ state_count[YAFFS_BLOCK_STATE_EMPTY]); ++ ++ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many collecting blocks %d (max is 1)", ++ state_count[YAFFS_BLOCK_STATE_COLLECTING]); ++} ++ ++/* ++ * Verify the object header. oh must be valid, but obj and tags may be NULL in ++ * which case those tests will not be performed. ++ */ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!(tags && obj && oh)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Verifying object header tags %p obj %p oh %p", ++ tags, obj, oh); ++ return; ++ } ++ ++ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || ++ oh->type > YAFFS_OBJECT_TYPE_MAX) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header type is illegal value 0x%x", ++ tags->obj_id, oh->type); ++ ++ if (tags->obj_id != obj->obj_id) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch obj_id %d", ++ tags->obj_id, obj->obj_id); ++ ++ /* ++ * Check that the object's parent ids match if parent_check requested. ++ * ++ * Tests do not apply to the root object. ++ */ ++ ++ if (parent_check && tags->obj_id > 1 && !obj->parent) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d obj->parent is NULL", ++ tags->obj_id, oh->parent_obj_id); ++ ++ if (parent_check && obj->parent && ++ oh->parent_obj_id != obj->parent->obj_id && ++ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d parent_obj_id %d", ++ tags->obj_id, oh->parent_obj_id, ++ obj->parent->obj_id); ++ ++ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is NULL", ++ obj->obj_id); ++ ++ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is 0xff", ++ obj->obj_id); ++} ++ ++void yaffs_verify_file(struct yaffs_obj *obj) ++{ ++ u32 x; ++ int required_depth; ++ int actual_depth; ++ int last_chunk; ++ u32 offset_in_chunk; ++ u32 the_chunk; ++ ++ u32 i; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ struct yaffs_tnode *tn; ++ u32 obj_id; ++ ++ if (!obj) ++ return; ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ dev = obj->my_dev; ++ obj_id = obj->obj_id; ++ ++ ++ /* Check file size is consistent with tnode depth */ ++ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, ++ &last_chunk, &offset_in_chunk); ++ last_chunk++; ++ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x > 0) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ actual_depth = obj->variant.file_variant.top_level; ++ ++ /* Check that the chunks in the tnode tree are all correct. ++ * We do this by scanning through the tnode tree and ++ * checking the tags for every chunk match. ++ */ ++ ++ if (yaffs_skip_nand_verification(dev)) ++ return; ++ ++ for (i = 1; i <= last_chunk; i++) { ++ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); ++ ++ if (!tn) ++ continue; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk > 0) { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ &tags); ++ if (tags.obj_id != obj_id || tags.chunk_id != i) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", ++ obj_id, i, the_chunk, ++ tags.obj_id, tags.chunk_id); ++ } ++ } ++} ++ ++void yaffs_verify_link(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify sane equivalent object */ ++} ++ ++void yaffs_verify_symlink(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify symlink string */ ++} ++ ++void yaffs_verify_special(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++} ++ ++void yaffs_verify_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ u32 chunk_min; ++ u32 chunk_max; ++ u32 chunk_id_ok; ++ u32 chunk_in_range; ++ u32 chunk_wrongly_deleted; ++ u32 chunk_valid; ++ ++ if (!obj) ++ return; ++ ++ if (obj->being_created) ++ return; ++ ++ dev = obj->my_dev; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Check sane object header chunk */ ++ ++ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; ++ chunk_max = ++ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; ++ ++ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && ++ ((unsigned)(obj->hdr_chunk)) <= chunk_max); ++ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); ++ chunk_valid = chunk_in_range && ++ yaffs_check_chunk_bit(dev, ++ obj->hdr_chunk / dev->param.chunks_per_block, ++ obj->hdr_chunk % dev->param.chunks_per_block); ++ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; ++ ++ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has chunk_id %d %s %s", ++ obj->obj_id, obj->hdr_chunk, ++ chunk_id_ok ? "" : ",out of range", ++ chunk_wrongly_deleted ? ",marked as deleted" : ""); ++ ++ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj_hdr *oh; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); ++ ++ yaffs_verify_oh(obj, oh, &tags, 1); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ /* Verify it has a parent */ ++ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has parent pointer %p which does not look like an object", ++ obj->obj_id, obj->parent); ++ } ++ ++ /* Verify parent is a directory */ ++ if (obj->parent && ++ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d's parent is not a directory (type %d)", ++ obj->obj_id, obj->parent->variant_type); ++ } ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ yaffs_verify_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ yaffs_verify_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ yaffs_verify_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ yaffs_verify_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ yaffs_verify_special(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has illegaltype %d", ++ obj->obj_id, obj->variant_type); ++ break; ++ } ++} ++ ++void yaffs_verify_objects(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int i; ++ struct list_head *lh; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ yaffs_verify_obj(obj); ++ } ++ } ++} ++ ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ int count = 0; ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!obj->parent) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); ++ BUG(); ++ return; ++ } ++ ++ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &obj->parent->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_verify_obj(list_obj); ++ if (obj == list_obj) ++ count++; ++ } ++ ++ if (count != 1) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory %d times", ++ count); ++ BUG(); ++ } ++} ++ ++void yaffs_verify_dir(struct yaffs_obj *directory) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ ++ if (!directory) { ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_full_verification(directory->my_dev)) ++ return; ++ ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Directory has wrong type: %d", ++ directory->variant_type); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &directory->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (list_obj->parent != directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory list has wrong parent %p", ++ list_obj->parent); ++ BUG(); ++ } ++ yaffs_verify_obj_in_dir(list_obj); ++ } ++} ++ ++static int yaffs_free_verification_failures; ++ ++void yaffs_verify_free_chunks(struct yaffs_dev *dev) ++{ ++ int counted; ++ int difference; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ counted = yaffs_count_free_chunks(dev); ++ ++ difference = dev->n_free_chunks - counted; ++ ++ if (difference) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Freechunks verification failure %d %d %d", ++ dev->n_free_chunks, counted, difference); ++ yaffs_free_verification_failures++; ++ } ++} ++ ++int yaffs_verify_file_sane(struct yaffs_obj *in) ++{ ++ (void) in; ++ return YAFFS_OK; ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_verify.h linux-3.14.43/fs/yaffs2/yaffs_verify.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_verify.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,43 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_VERIFY_H__ ++#define __YAFFS_VERIFY_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, ++ int n); ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n); ++void yaffs_verify_blocks(struct yaffs_dev *dev); ++ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check); ++void yaffs_verify_file(struct yaffs_obj *obj); ++void yaffs_verify_link(struct yaffs_obj *obj); ++void yaffs_verify_symlink(struct yaffs_obj *obj); ++void yaffs_verify_special(struct yaffs_obj *obj); ++void yaffs_verify_obj(struct yaffs_obj *obj); ++void yaffs_verify_objects(struct yaffs_dev *dev); ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); ++void yaffs_verify_dir(struct yaffs_obj *directory); ++void yaffs_verify_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_verify_file_sane(struct yaffs_obj *obj); ++ ++int yaffs_skip_verification(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_vfs.c linux-3.14.43/fs/yaffs2/yaffs_vfs.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_vfs.c 2015-06-15 21:21:52.000000000 +0200 +@@ -0,0 +1,3658 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * Acknowledgements: ++ * Luc van OostenRyck for numerous patches. ++ * Nick Bane for numerous patches. ++ * Nick Bane for 2.5/2.6 integration. ++ * Andras Toth for mknod rdev issue. ++ * Michael Fischer for finding the problem with inode inconsistency. ++ * Some code bodily lifted from JFFS ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * ++ * This is the file system front-end to YAFFS that hooks it up to ++ * the VFS. ++ * ++ * Special notes: ++ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with ++ * this superblock ++ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this ++ * superblock ++ * >> inode->u.generic_ip points to the associated struct yaffs_obj. ++ */ ++ ++/* ++ * There are two variants of the VFS glue code. This variant should compile ++ * for any version of Linux. ++ */ ++#include ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) ++#define YAFFS_COMPILE_BACKGROUND ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) ++#define YAFFS_COMPILE_FREEZER ++#endif ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) ++#define YAFFS_COMPILE_EXPORTFS ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_USE_SETATTR_COPY ++#define YAFFS_USE_TRUNCATE_SETSIZE ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_HAS_EVICT_INODE ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++#define YAFFS_NEW_FOLLOW_LINK 1 ++#else ++#define YAFFS_NEW_FOLLOW_LINK 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_HAS_WRITE_SUPER ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++#include ++#endif ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++#include ++#endif ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++#include ++#include ++#endif ++#ifdef YAFFS_COMPILE_FREEZER ++#include ++#endif ++ ++#include ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++#include ++ ++#define UnlockPage(p) unlock_page(p) ++#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) ++ ++/* FIXME: use sb->s_id instead ? */ ++#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) ++ ++#else ++ ++#include ++#define BDEVNAME_SIZE 0 ++#define yaffs_devname(sb, buf) kdevname(sb->s_dev) ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) ++/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ ++#define __user ++#endif ++ ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define YPROC_ROOT (&proc_root) ++#else ++#define YPROC_ROOT NULL ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define Y_INIT_TIMER(a) init_timer(a) ++#else ++#define Y_INIT_TIMER(a) init_timer_on_stack(a) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) ++#define YAFFS_USE_WRITE_BEGIN_END 1 ++#else ++#define YAFFS_USE_WRITE_BEGIN_END 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_SUPER_HAS_DIRTY ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) ++static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) ++{ ++ uint64_t result = partition_size; ++ do_div(result, block_size); ++ return (uint32_t) result; ++} ++#else ++#define YCALCBLOCKS(s, b) ((s)/(b)) ++#endif ++ ++#include ++#include ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#include "yaffs_linux.h" ++ ++#include "yaffs_mtdif.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_getblockinfo.h" ++ ++unsigned int yaffs_trace_mask = ++ YAFFS_TRACE_BAD_BLOCKS | ++ YAFFS_TRACE_ALWAYS | ++ 0; ++ ++unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; ++unsigned int yaffs_auto_checkpoint = 1; ++unsigned int yaffs_gc_control = 1; ++unsigned int yaffs_bg_enable = 1; ++unsigned int yaffs_auto_select = 1; ++/* Module Parameters */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++module_param(yaffs_trace_mask, uint, 0644); ++module_param(yaffs_wr_attempts, uint, 0644); ++module_param(yaffs_auto_checkpoint, uint, 0644); ++module_param(yaffs_gc_control, uint, 0644); ++module_param(yaffs_bg_enable, uint, 0644); ++#else ++MODULE_PARM(yaffs_trace_mask, "i"); ++MODULE_PARM(yaffs_wr_attempts, "i"); ++MODULE_PARM(yaffs_auto_checkpoint, "i"); ++MODULE_PARM(yaffs_gc_control, "i"); ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++/* use iget and read_inode */ ++#define Y_IGET(sb, inum) iget((sb), (inum)) ++ ++#else ++/* Call local equivalent */ ++#define YAFFS_USE_OWN_IGET ++#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) ++ ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) ++#else ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) ++#endif ++ ++#define yaffs_inode_to_obj(iptr) \ ++ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) ++#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) ++#else ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++#define Y_CLEAR_INODE(i) clear_inode(i) ++#else ++#define Y_CLEAR_INODE(i) end_writeback(i) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++#define YAFFS_USE_DIR_ITERATE ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0)) ++#define YAFFS_NEW_PROCFS ++#include ++#endif ++ ++ ++#define update_dir_time(dir) do {\ ++ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ ++ } while (0) ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj); ++ ++ ++static void yaffs_gross_lock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); ++ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); ++} ++ ++static void yaffs_gross_unlock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); ++ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++} ++ ++ ++static int yaffs_readpage_nolock(struct file *f, struct page *pg) ++{ ++ /* Lifted from jffs2 */ ++ ++ struct yaffs_obj *obj; ++ unsigned char *pg_buf; ++ int ret; ++ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readpage_nolock at %lld, size %08x", ++ (long long)pos, ++ (unsigned)PAGE_CACHE_SIZE); ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ BUG_ON(!PageLocked(pg)); ++#else ++ if (!PageLocked(pg)) ++ PAGE_BUG(pg); ++#endif ++ ++ pg_buf = kmap(pg); ++ /* FIXME: Can kmap fail? */ ++ ++ yaffs_gross_lock(dev); ++ ++ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (ret >= 0) ++ ret = 0; ++ ++ if (ret) { ++ ClearPageUptodate(pg); ++ SetPageError(pg); ++ } else { ++ SetPageUptodate(pg); ++ ClearPageError(pg); ++ } ++ ++ flush_dcache_page(pg); ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); ++ return ret; ++} ++ ++static int yaffs_readpage_unlock(struct file *f, struct page *pg) ++{ ++ int ret = yaffs_readpage_nolock(f, pg); ++ UnlockPage(pg); ++ return ret; ++} ++ ++static int yaffs_readpage(struct file *f, struct page *pg) ++{ ++ int ret; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); ++ ret = yaffs_readpage_unlock(f, pg); ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); ++ return ret; ++} ++ ++ ++static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc) ++ lc->dirty = val; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb) ++ sb->s_dirt = val; ++ } ++#endif ++ ++} ++ ++static void yaffs_set_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 1); ++} ++ ++static void yaffs_clear_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 0); ++} ++ ++static int yaffs_check_super_dirty(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc && lc->dirty) ++ return 1; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb && sb->s_dirt) ++ return 1; ++ } ++#endif ++ return 0; ++ ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_writepage(struct page *page, struct writeback_control *wbc) ++#else ++static int yaffs_writepage(struct page *page) ++#endif ++{ ++ struct yaffs_dev *dev; ++ struct address_space *mapping = page->mapping; ++ struct inode *inode; ++ unsigned long end_index; ++ char *buffer; ++ struct yaffs_obj *obj; ++ int n_written = 0; ++ unsigned n_bytes; ++ loff_t i_size; ++ ++ if (!mapping) ++ BUG(); ++ inode = mapping->host; ++ if (!inode) ++ BUG(); ++ i_size = i_size_read(inode); ++ ++ end_index = i_size >> PAGE_CACHE_SHIFT; ++ ++ if (page->index < end_index) ++ n_bytes = PAGE_CACHE_SIZE; ++ else { ++ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); ++ ++ if (page->index > end_index || !n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, inode size = %lld!!", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, ++ inode->i_size); ++ yaffs_trace(YAFFS_TRACE_OS, ++ " -> don't care!!"); ++ ++ zero_user_segment(page, 0, PAGE_CACHE_SIZE); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ return 0; ++ } ++ } ++ ++ if (n_bytes != PAGE_CACHE_SIZE) ++ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); ++ ++ get_page(page); ++ ++ buffer = kmap(page); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, size %08x", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag0: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ n_written = yaffs_wr_file(obj, buffer, ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag1: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ yaffs_gross_unlock(dev); ++ ++ kunmap(page); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ put_page(page); ++ ++ return (n_written == n_bytes) ? 0 : -ENOSPC; ++} ++ ++/* Space holding and freeing is done to ensure we have space available for write_begin/end */ ++/* For now we just assume few parallel writes and check against a small number. */ ++/* Todo: need to do this with a counter to handle parallel reads better */ ++ ++static ssize_t yaffs_hold_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ int n_free_chunks; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ n_free_chunks = yaffs_get_n_free_chunks(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ return (n_free_chunks > 20) ? 1 : 0; ++} ++ ++static void yaffs_release_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_gross_unlock(dev); ++} ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_begin(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ ++ struct page *pg = NULL; ++ pgoff_t index = pos >> PAGE_CACHE_SHIFT; ++ ++ int ret = 0; ++ int space_held = 0; ++ ++ /* Get a page */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ++ pg = grab_cache_page_write_begin(mapping, index, flags); ++#else ++ pg = __grab_cache_page(mapping, index); ++#endif ++ ++ *pagep = pg; ++ if (!pg) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "start yaffs_write_begin index %d(%x) uptodate %d", ++ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); ++ ++ /* Get fs space */ ++ space_held = yaffs_hold_space(filp); ++ ++ if (!space_held) { ++ ret = -ENOSPC; ++ goto out; ++ } ++ ++ /* Update page if required */ ++ ++ if (!Page_Uptodate(pg)) ++ ret = yaffs_readpage_nolock(filp, pg); ++ ++ if (ret) ++ goto out; ++ ++ /* Happy path return */ ++ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); ++ ++ return 0; ++ ++out: ++ yaffs_trace(YAFFS_TRACE_OS, ++ "end yaffs_write_begin fail returning %d", ret); ++ if (space_held) ++ yaffs_release_space(filp); ++ if (pg) { ++ unlock_page(pg); ++ page_cache_release(pg); ++ } ++ return ret; ++} ++ ++#else ++ ++static int yaffs_prepare_write(struct file *f, struct page *pg, ++ unsigned offset, unsigned to) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); ++ ++ if (!Page_Uptodate(pg)) ++ return yaffs_readpage_nolock(f, pg); ++ return 0; ++} ++#endif ++ ++ ++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, ++ loff_t * pos) ++{ ++ struct yaffs_obj *obj; ++ int n_written; ++ loff_t ipos; ++ struct inode *inode; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: hey obj is null!"); ++ return -EINVAL; ++ } ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ inode = f->f_dentry->d_inode; ++ ++ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) ++ ipos = inode->i_size; ++ else ++ ipos = *pos; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", ++ (unsigned)n, (unsigned)n, obj->obj_id, ipos); ++ ++ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: %d(%x) bytes written", ++ (unsigned)n, (unsigned)n); ++ ++ if (n_written > 0) { ++ ipos += n_written; ++ *pos = ipos; ++ if (ipos > inode->i_size) { ++ inode->i_size = ipos; ++ inode->i_blocks = (ipos + 511) >> 9; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write size updated to %lld bytes, %d blocks", ++ ipos, (int)(inode->i_blocks)); ++ } ++ ++ } ++ yaffs_gross_unlock(dev); ++ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; ++} ++ ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_end(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *pg, void *fsdadata) ++{ ++ int ret = 0; ++ void *addr, *kva; ++ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); ++ ++ kva = kmap(pg); ++ addr = kva + offset_into_page; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end addr %p pos %lld n_bytes %d", ++ addr, pos, copied); ++ ++ ret = yaffs_file_write(filp, addr, copied, &pos); ++ ++ if (ret != copied) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end not same size ret %d copied %d", ++ ret, copied); ++ SetPageError(pg); ++ } ++ ++ kunmap(pg); ++ ++ yaffs_release_space(filp); ++ unlock_page(pg); ++ page_cache_release(pg); ++ return ret; ++} ++#else ++ ++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, ++ unsigned to) ++{ ++ void *addr, *kva; ++ ++ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; ++ int n_bytes = to - offset; ++ int n_written; ++ ++ kva = kmap(pg); ++ addr = kva + offset; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write addr %p pos %lld n_bytes %d", ++ addr, pos, n_bytes); ++ ++ n_written = yaffs_file_write(f, addr, n_bytes, &pos); ++ ++ if (n_written != n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write not same size n_written %d n_bytes %d", ++ n_written, n_bytes); ++ SetPageError(pg); ++ } ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write returning %d", ++ n_written == n_bytes ? 0 : n_written); ++ ++ return n_written == n_bytes ? 0 : n_written; ++} ++#endif ++ ++static struct address_space_operations yaffs_file_address_operations = { ++ .readpage = yaffs_readpage, ++ .writepage = yaffs_writepage, ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++ .write_begin = yaffs_write_begin, ++ .write_end = yaffs_write_end, ++#else ++ .prepare_write = yaffs_prepare_write, ++ .commit_write = yaffs_commit_write, ++#endif ++}; ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_file_flush(struct file *file, fl_owner_t id) ++#else ++static int yaffs_file_flush(struct file *file) ++#endif ++{ ++ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); ++ ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_flush object %d (%s)", ++ obj->obj_id, ++ obj->dirty ? "dirty" : "clean"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_file(obj, 1, 0, 0); ++ ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++static int yaffs_sync_object(struct file *file, int datasync) ++#else ++static int yaffs_sync_object(struct file *file, struct dentry *dentry, ++ int datasync) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++ struct dentry *dentry = file->f_path.dentry; ++#endif ++ ++ obj = yaffs_dentry_to_obj(dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_object"); ++ yaffs_gross_lock(dev); ++ yaffs_flush_file(obj, 1, datasync, 0); ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .splice_read = generic_file_splice_read, ++ .splice_write = generic_file_splice_write, ++ .llseek = generic_file_llseek, ++}; ++ ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .sendfile = generic_file_sendfile, ++}; ++ ++#else ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = generic_file_read, ++ .write = generic_file_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ .sendfile = generic_file_sendfile, ++#endif ++}; ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++static void zero_user_segment(struct page *page, unsigned start, unsigned end) ++{ ++ void *kaddr = kmap_atomic(page, KM_USER0); ++ memset(kaddr + start, 0, end - start); ++ kunmap_atomic(kaddr, KM_USER0); ++ flush_dcache_page(page); ++} ++#endif ++ ++ ++static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) ++{ ++#ifdef YAFFS_USE_TRUNCATE_SETSIZE ++ truncate_setsize(inode, newsize); ++ return 0; ++#else ++ truncate_inode_pages(&inode->i_data, newsize); ++ return 0; ++#endif ++ ++} ++ ++ ++static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) ++{ ++#ifdef YAFFS_USE_SETATTR_COPY ++ setattr_copy(inode, attr); ++ return 0; ++#else ++ return inode_setattr(inode, attr); ++#endif ++ ++} ++ ++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_setattr of object %d", ++ yaffs_inode_to_obj(inode)->obj_id); ++#if 0 ++ /* Fail if a requested resize >= 2GB */ ++ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) ++ error = -EINVAL; ++#endif ++ ++ if (error == 0) ++ error = inode_change_ok(inode, attr); ++ if (error == 0) { ++ int result; ++ if (!error) { ++ error = yaffs_vfs_setattr(inode, attr); ++ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_vfs_setsize(inode, attr->ia_size); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ } ++ } ++ dev = yaffs_inode_to_obj(inode)->my_dev; ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "resize to %d(%x)", ++ (int)(attr->ia_size), ++ (int)(attr->ia_size)); ++ } ++ yaffs_gross_lock(dev); ++ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); ++ if (result == YAFFS_OK) { ++ error = 0; ++ } else { ++ error = -EPERM; ++ } ++ yaffs_gross_unlock(dev); ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_set_xattrib(obj, name, value, size, flags); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, ++ void *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_getxattr \"%s\" from object %d", ++ name, obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_get_xattrib(obj, name, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_remove_xattrib(obj, name); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_list_xattrib(obj, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr done returning %d", error); ++ ++ return error; ++} ++ ++ ++static const struct inode_operations yaffs_file_inode_operations = { ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++ ++static int yaffs_readlink(struct dentry *dentry, char __user * buffer, ++ int buflen) ++{ ++ unsigned char *alias; ++ int ret; ++ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) ++ return -ENOMEM; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) ++ ret = vfs_readlink(dentry, buffer, buflen, alias); ++#else ++ ret = readlink_copy(buffer, buflen, alias); ++#endif ++ kfree(alias); ++ return ret; ++} ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ void *ret; ++#else ++static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int ret ++#endif ++ unsigned char *alias; ++ int ret_int = 0; ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) { ++ ret_int = -ENOMEM; ++ goto out; ++ } ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ nd_set_link(nd, alias); ++ ret = alias; ++out: ++ if (ret_int) ++ ret = ERR_PTR(ret_int); ++ return ret; ++#else ++ ret = vfs_follow_link(nd, alias); ++ kfree(alias); ++out: ++ if (ret_int) ++ ret = ret_int; ++ return ret; ++#endif ++} ++ ++ ++#ifdef YAFFS_HAS_PUT_INODE ++ ++/* For now put inode is just for debugging ++ * Put inode is called when the inode **structure** is put. ++ */ ++static void yaffs_put_inode(struct inode *inode) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_put_inode: ino %d, count %d"), ++ (int)inode->i_ino, atomic_read(&inode->i_count); ++ ++} ++#endif ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) ++{ ++ kfree(alias); ++} ++#endif ++ ++static const struct inode_operations yaffs_symlink_inode_operations = { ++ .readlink = yaffs_readlink, ++ .follow_link = yaffs_follow_link, ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ .put_link = yaffs_put_link, ++#endif ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++#ifdef YAFFS_USE_OWN_IGET ++ ++static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) ++{ ++ struct inode *inode; ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); ++ ++ inode = iget_locked(sb, ino); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ yaffs_gross_unlock(dev); ++ ++ unlock_new_inode(inode); ++ return inode; ++} ++ ++#else ++ ++static void yaffs_read_inode(struct inode *inode) ++{ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_inode for %d", (int)inode->i_ino); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++} ++ ++#endif ++ ++ ++ ++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, ++ struct yaffs_obj *obj) ++{ ++ struct inode *inode; ++ ++ if (!sb) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL super_block!!"); ++ return NULL; ++ ++ } ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL object!!"); ++ return NULL; ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for object %d", obj->obj_id); ++ ++ inode = Y_IGET(sb, obj->obj_id); ++ if (IS_ERR(inode)) ++ return NULL; ++ ++ /* NB Side effect: iget calls back to yaffs_read_inode(). */ ++ /* iget also increments the inode's i_count */ ++ /* NB You can't be holding gross_lock or deadlock will happen! */ ++ ++ return inode; ++} ++ ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++#define YCRED(x) x ++#else ++#define YCRED(x) (x->cred) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) ++#define YPROC_uid(p) (YCRED(p)->fsuid) ++#define YPROC_gid(p) (YCRED(p)->fsgid) ++#define EXTRACT_gid(x) x ++#define EXTRACT_uid(x) x ++#define MAKE_gid(x) x ++#define MAKE_uid(x) x ++#else ++#define YPROC_uid(p) from_kuid(&init_user_ns, YCRED(p)->fsuid) ++#define YPROC_gid(p) from_kgid(&init_user_ns, YCRED(p)->fsgid) ++#define EXTRACT_gid(x) from_kgid(&init_user_ns, x) ++#define EXTRACT_uid(x) from_kuid(&init_user_ns, x) ++#define MAKE_gid(x) make_kgid(&init_user_ns, x) ++#define MAKE_uid(x) make_kuid(&init_user_ns, x) ++#endif ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t rdev) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t rdev) ++#else ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ int rdev) ++#endif ++{ ++ struct inode *inode; ++ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_dev *dev; ++ ++ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); ++ ++ int error = -ENOSPC; ++ uid_t uid = YPROC_uid(current); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); ++ ++ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) ++ mode |= S_ISGID; ++ ++ if (parent) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: parent object %d type %d", ++ parent->obj_id, parent->variant_type); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: could not get parent object"); ++ return -EPERM; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: making oject for %s, mode %x dev %x", ++ dentry->d_name.name, mode, rdev); ++ ++ dev = parent->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ switch (mode & S_IFMT) { ++ default: ++ /* Special (socket, fifo, device...) */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, old_encode_dev(rdev)); ++#else ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, rdev); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); ++ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, ++ gid); ++ break; ++ case S_IFDIR: /* directory */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); ++ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, ++ uid, gid); ++ break; ++ case S_IFLNK: /* symlink */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); ++ obj = NULL; /* Do we ever get here? */ ++ break; ++ } ++ ++ /* Can not call yaffs_get_inode() with gross lock held */ ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod created object %d count = %d", ++ obj->obj_id, atomic_read(&inode->i_count)); ++ error = 0; ++ yaffs_fill_inode_from_obj(dir, parent); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); ++ error = -ENOMEM; ++ } ++ ++ return error; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++#else ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ int ret_val; ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); ++ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); ++ return ret_val; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool dummy) ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ struct nameidata *n) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *n) ++#else ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); ++ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ unsigned int dummy) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *n) ++#else ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ ++ ++ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", ++ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); ++ ++ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); ++ ++ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ ++ ++ /* Can't hold gross lock when calling yaffs_get_inode() */ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_lookup found %d", obj->obj_id); ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); ++ ++ } ++ ++/* added NCB for 2.5/6 compatability - forces add even if inode is ++ * NULL which creates dentry hash */ ++ d_add(dentry, inode); ++ ++ return NULL; ++} ++ ++/* ++ * Create a link... ++ */ ++static int yaffs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ struct inode *inode = old_dentry->d_inode; ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *link = NULL; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ ++ link = ++ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ obj); ++ ++ if (link) { ++ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); ++ d_instantiate(dentry, old_dentry->d_inode); ++ atomic_inc(&old_dentry->d_inode->i_count); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_link link count %d i_count %d", ++ old_dentry->d_inode->i_nlink, ++ atomic_read(&old_dentry->d_inode->i_count)); ++ } ++ ++ yaffs_gross_unlock(dev); ++ ++ if (link) { ++ update_dir_time(dir); ++ return 0; ++ } ++ ++ return -EPERM; ++} ++ ++static int yaffs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ uid_t uid = YPROC_uid(current); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); ++ ++ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ return -ENAMETOOLONG; ++ ++ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > ++ YAFFS_MAX_ALIAS_LENGTH) ++ return -ENAMETOOLONG; ++ ++ dev = yaffs_inode_to_obj(dir)->my_dev; ++ yaffs_gross_lock(dev); ++ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ S_IFLNK | S_IRWXUGO, uid, gid, symname); ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ struct inode *inode; ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); ++ return 0; ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); ++ } ++ ++ return -ENOMEM; ++} ++ ++/* ++ * The VFS layer already does all the dentry stuff for rename. ++ * ++ * NB: POSIX says you can rename an object over an old object of the same name ++ */ ++static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct yaffs_dev *dev; ++ int ret_val = YAFFS_FAIL; ++ struct yaffs_obj *target; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); ++ dev = yaffs_inode_to_obj(old_dir)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ /* Check if the target is an existing directory that is not empty. */ ++ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ ++ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && ++ !list_empty(&target->variant.dir_variant.children)) { ++ ++ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); ++ ++ ret_val = YAFFS_FAIL; ++ } else { ++ /* Now does unlinking internally using shadowing mechanism */ ++ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); ++ ++ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), ++ old_dentry->d_name.name, ++ yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ } ++ yaffs_gross_unlock(dev); ++ ++ if (ret_val == YAFFS_OK) { ++ if (target) ++ inode_dec_link_count(new_dentry->d_inode); ++ ++ update_dir_time(old_dir); ++ if (old_dir != new_dir) ++ update_dir_time(new_dir); ++ return 0; ++ } else { ++ return -ENOTEMPTY; ++ } ++} ++ ++ ++ ++ ++static int yaffs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int ret_val; ++ ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", ++ (int)(dir->i_ino), dentry->d_name.name); ++ obj = yaffs_inode_to_obj(dir); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ ret_val = yaffs_unlinker(obj, dentry->d_name.name); ++ ++ if (ret_val == YAFFS_OK) { ++ inode_dec_link_count(dentry->d_inode); ++ dir->i_version++; ++ yaffs_gross_unlock(dev); ++ update_dir_time(dir); ++ return 0; ++ } ++ yaffs_gross_unlock(dev); ++ return -ENOTEMPTY; ++} ++ ++ ++ ++static const struct inode_operations yaffs_dir_inode_operations = { ++ .create = yaffs_create, ++ .lookup = yaffs_lookup, ++ .link = yaffs_link, ++ .unlink = yaffs_unlink, ++ .symlink = yaffs_symlink, ++ .mkdir = yaffs_mkdir, ++ .rmdir = yaffs_unlink, ++ .mknod = yaffs_mknod, ++ .rename = yaffs_rename, ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++/*-----------------------------------------------------------------*/ ++/* Directory search context allows us to unlock access to yaffs during ++ * filldir without causing problems with the directory being modified. ++ * This is similar to the tried and tested mechanism used in yaffs direct. ++ * ++ * A search context iterates along a doubly linked list of siblings in the ++ * directory. If the iterating object is deleted then this would corrupt ++ * the list iteration, likely causing a crash. The search context avoids ++ * this by using the remove_obj_fn to move the search context to the ++ * next object before the object is deleted. ++ * ++ * Many readdirs (and thus seach conexts) may be alive simulateously so ++ * each struct yaffs_dev has a list of these. ++ * ++ * A seach context lives for the duration of a readdir. ++ * ++ * All these functions must be called while yaffs is locked. ++ */ ++ ++struct yaffs_search_context { ++ struct yaffs_dev *dev; ++ struct yaffs_obj *dir_obj; ++ struct yaffs_obj *next_return; ++ struct list_head others; ++}; ++ ++/* ++ * yaffs_new_search() creates a new search context, initialises it and ++ * adds it to the device's search context list. ++ * ++ * Called at start of readdir. ++ */ ++static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) ++{ ++ struct yaffs_dev *dev = dir->my_dev; ++ struct yaffs_search_context *sc = ++ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); ++ if (sc) { ++ sc->dir_obj = dir; ++ sc->dev = dev; ++ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else ++ sc->next_return = ++ list_entry(dir->variant.dir_variant.children.next, ++ struct yaffs_obj, siblings); ++ INIT_LIST_HEAD(&sc->others); ++ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); ++ } ++ return sc; ++} ++ ++/* ++ * yaffs_search_end() disposes of a search context and cleans up. ++ */ ++static void yaffs_search_end(struct yaffs_search_context *sc) ++{ ++ if (sc) { ++ list_del(&sc->others); ++ kfree(sc); ++ } ++} ++ ++/* ++ * yaffs_search_advance() moves a search context to the next object. ++ * Called when the search iterates or when an object removal causes ++ * the search context to be moved to the next object. ++ */ ++static void yaffs_search_advance(struct yaffs_search_context *sc) ++{ ++ if (!sc) ++ return; ++ ++ if (sc->next_return == NULL || ++ list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else { ++ struct list_head *next = sc->next_return->siblings.next; ++ ++ if (next == &sc->dir_obj->variant.dir_variant.children) ++ sc->next_return = NULL; /* end of list */ ++ else ++ sc->next_return = ++ list_entry(next, struct yaffs_obj, siblings); ++ } ++} ++ ++/* ++ * yaffs_remove_obj_callback() is called when an object is unlinked. ++ * We check open search contexts and advance any which are currently ++ * on the object being iterated. ++ */ ++static void yaffs_remove_obj_callback(struct yaffs_obj *obj) ++{ ++ ++ struct list_head *i; ++ struct yaffs_search_context *sc; ++ struct list_head *search_contexts = ++ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); ++ ++ /* Iterate through the directory search contexts. ++ * If any are currently on the object being removed, then advance ++ * the search context to the next object to prevent a hanging pointer. ++ */ ++ list_for_each(i, search_contexts) { ++ sc = list_entry(i, struct yaffs_search_context, others); ++ if (sc->next_return == obj) ++ yaffs_search_advance(sc); ++ } ++ ++} ++ ++ ++/*-----------------------------------------------------------------*/ ++ ++#ifdef YAFFS_USE_DIR_ITERATE ++static int yaffs_iterate(struct file *f, struct dir_context *dc) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ unsigned long curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ if (!dir_emit_dots(f, dc)) ++ return 0; ++ ++ curoffs = 1; ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= dc->pos) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!dir_emit(dc, ++ name, ++ strlen(name), ++ this_inode, ++ this_type)) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ dc->pos++; ++ f->f_pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++ ++#else ++ ++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ struct inode *inode = f->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ offset = f->f_pos; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: starting at %d", (int)offset); ++ ++ if (offset == 0) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry . ino %d", ++ (int)inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ if (offset == 1) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry .. ino %d", ++ (int)f->f_dentry->d_parent->d_inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, "..", 2, offset, ++ f->f_dentry->d_parent->d_inode->i_ino, ++ DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ if (f->f_version != inode->i_version) { ++ offset = 2; ++ f->f_pos = offset; ++ f->f_version = inode->i_version; ++ } ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= offset) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (filldir(dirent, ++ name, ++ strlen(name), ++ offset, this_inode, this_type) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ offset++; ++ f->f_pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++ ++#endif ++ ++static const struct file_operations yaffs_dir_operations = { ++ .read = generic_read_dir, ++#ifdef YAFFS_USE_DIR_ITERATE ++ .iterate = yaffs_iterate, ++#else ++ .readdir = yaffs_readdir, ++#endif ++ .fsync = yaffs_sync_object, ++ .llseek = generic_file_llseek, ++}; ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj) ++{ ++ if (inode && obj) { ++ ++ /* Check mode against the variant type and attempt to repair if broken. */ ++ u32 mode = obj->yst_mode; ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (!S_ISREG(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFREG; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ if (!S_ISLNK(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFLNK; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!S_ISDIR(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFDIR; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ default: ++ /* TODO? */ ++ break; ++ } ++ ++ inode->i_flags |= S_NOATIME; ++ ++ inode->i_ino = obj->obj_id; ++ inode->i_mode = obj->yst_mode; ++ inode->i_uid = MAKE_uid(obj->yst_uid); ++ inode->i_gid = MAKE_gid(obj->yst_gid); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++ inode->i_blksize = inode->i_sb->s_blocksize; ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++ inode->i_rdev = old_decode_dev(obj->yst_rdev); ++ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); ++ inode->i_atime.tv_nsec = 0; ++ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; ++ inode->i_mtime.tv_nsec = 0; ++ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; ++ inode->i_ctime.tv_nsec = 0; ++#else ++ inode->i_rdev = obj->yst_rdev; ++ inode->i_atime = obj->yst_atime; ++ inode->i_mtime = obj->yst_mtime; ++ inode->i_ctime = obj->yst_ctime; ++#endif ++ inode->i_size = yaffs_get_obj_length(obj); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ ++ set_nlink(inode, yaffs_get_obj_link_count(obj)); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", ++ inode->i_mode, obj->yst_uid, obj->yst_gid, ++ inode->i_size, atomic_read(&inode->i_count)); ++ ++ switch (obj->yst_mode & S_IFMT) { ++ default: /* fifo, device or socket */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ init_special_inode(inode, obj->yst_mode, ++ old_decode_dev(obj->yst_rdev)); ++#else ++ init_special_inode(inode, obj->yst_mode, ++ (dev_t) (obj->yst_rdev)); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ inode->i_op = &yaffs_file_inode_operations; ++ inode->i_fop = &yaffs_file_operations; ++ inode->i_mapping->a_ops = ++ &yaffs_file_address_operations; ++ break; ++ case S_IFDIR: /* directory */ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ break; ++ case S_IFLNK: /* symlink */ ++ inode->i_op = &yaffs_symlink_inode_operations; ++ break; ++ } ++ ++ yaffs_inode_to_obj_lv(inode) = obj; ++ ++ obj->my_inode = inode; ++ ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode invalid parameters"); ++ } ++ ++} ++ ++ ++ ++/* ++ * yaffs background thread functions . ++ * yaffs_bg_thread_fn() the thread function ++ * yaffs_bg_start() launches the background thread. ++ * yaffs_bg_stop() cleans up the background thread. ++ * ++ * NB: ++ * The thread should only run after the yaffs is initialised ++ * The thread should be stopped before yaffs is unmounted. ++ * The thread should not do any writing while the fs is in read only. ++ */ ++ ++static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) ++{ ++ unsigned erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned scattered = 0; /* Free chunks not in an erased block */ ++ ++ if (erased_chunks < dev->n_free_chunks) ++ scattered = (dev->n_free_chunks - erased_chunks); ++ ++ if (!context->bg_running) ++ return 0; ++ else if (scattered < (dev->param.chunks_per_block * 2)) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 2) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 4) ++ return 1; ++ else ++ return 2; ++} ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++ ++void yaffs_background_waker(unsigned long data) ++{ ++ wake_up_process((struct task_struct *)data); ++} ++ ++static int yaffs_bg_thread_fn(void *data) ++{ ++ struct yaffs_dev *dev = (struct yaffs_dev *)data; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned long now = jiffies; ++ unsigned long next_dir_update = now; ++ unsigned long next_gc = now; ++ unsigned long expires; ++ unsigned int urgency; ++ ++ int gc_result; ++ struct timer_list timer; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "yaffs_background starting for dev %p", (void *)dev); ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ set_freezable(); ++#endif ++ while (context->bg_running) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); ++ ++ if (kthread_should_stop()) ++ break; ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ if (try_to_freeze()) ++ continue; ++#endif ++ yaffs_gross_lock(dev); ++ ++ now = jiffies; ++ ++ if (time_after(now, next_dir_update) && yaffs_bg_enable) { ++ yaffs_update_dirty_dirs(dev); ++ next_dir_update = now + HZ; ++ } ++ ++ if (time_after(now, next_gc) && yaffs_bg_enable) { ++ if (!dev->is_checkpointed) { ++ urgency = yaffs_bg_gc_urgency(dev); ++ gc_result = yaffs_bg_gc(dev, urgency); ++ if (urgency > 1) ++ next_gc = now + HZ / 20 + 1; ++ else if (urgency > 0) ++ next_gc = now + HZ / 10 + 1; ++ else ++ next_gc = now + HZ * 2; ++ } else { ++ /* ++ * gc not running so set to next_dir_update ++ * to cut down on wake ups ++ */ ++ next_gc = next_dir_update; ++ } ++ } ++ yaffs_gross_unlock(dev); ++#if 1 ++ expires = next_dir_update; ++ if (time_before(next_gc, expires)) ++ expires = next_gc; ++ if (time_before(expires, now)) ++ expires = now + HZ; ++ ++ Y_INIT_TIMER(&timer); ++ timer.expires = expires + 1; ++ timer.data = (unsigned long)current; ++ timer.function = yaffs_background_waker; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_timer(&timer); ++ schedule(); ++ del_timer_sync(&timer); ++#else ++ msleep(10); ++#endif ++ } ++ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ int retval = 0; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ ++ if (dev->read_only) ++ return -1; ++ ++ context->bg_running = 1; ++ ++ context->bg_thread = kthread_run(yaffs_bg_thread_fn, ++ (void *)dev, "yaffs-bg-%d", ++ context->mount_id); ++ ++ if (IS_ERR(context->bg_thread)) { ++ retval = PTR_ERR(context->bg_thread); ++ context->bg_thread = NULL; ++ context->bg_running = 0; ++ } ++ return retval; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); ++ ++ ctxt->bg_running = 0; ++ ++ if (ctxt->bg_thread) { ++ kthread_stop(ctxt->bg_thread); ++ ctxt->bg_thread = NULL; ++ } ++} ++#else ++static int yaffs_bg_thread_fn(void *data) ++{ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ return 0; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++} ++#endif ++ ++ ++static void yaffs_flush_inodes(struct super_block *sb) ++{ ++ struct inode *iptr; ++ struct yaffs_obj *obj; ++ ++ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { ++ obj = yaffs_inode_to_obj(iptr); ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "flushing obj %d", ++ obj->obj_id); ++ yaffs_flush_file(obj, 1, 0, 0); ++ } ++ } ++} ++ ++static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ if (!dev) ++ return; ++ ++ yaffs_flush_inodes(sb); ++ yaffs_update_dirty_dirs(dev); ++ yaffs_flush_whole_cache(dev, 1); ++ if (do_checkpoint) ++ yaffs_checkpoint_save(dev); ++} ++ ++static LIST_HEAD(yaffs_context_list); ++struct mutex yaffs_context_lock; ++ ++static void yaffs_put_super(struct super_block *sb) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super"); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "Shutting down yaffs background thread"); ++ yaffs_bg_stop(dev); ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "yaffs background thread shut down"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_super(sb, 1); ++ ++ yaffs_deinitialise(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ mutex_lock(&yaffs_context_lock); ++ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); ++ mutex_unlock(&yaffs_context_lock); ++ ++ if (yaffs_dev_to_lc(dev)->spare_buffer) { ++ kfree(yaffs_dev_to_lc(dev)->spare_buffer); ++ yaffs_dev_to_lc(dev)->spare_buffer = NULL; ++ } ++ ++ kfree(dev); ++ ++ yaffs_put_mtd_device(mtd); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super done"); ++} ++ ++ ++static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) ++{ ++ return yaffs_gc_control; ++} ++ ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ ++static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, ++ uint32_t generation) ++{ ++ return Y_IGET(sb, ino); ++} ++ ++static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_parent(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++struct dentry *yaffs2_get_parent(struct dentry *dentry) ++{ ++ ++ struct super_block *sb = dentry->d_inode->i_sb; ++ struct dentry *parent = ERR_PTR(-ENOENT); ++ struct inode *inode; ++ unsigned long parent_ino; ++ struct yaffs_obj *d_obj; ++ struct yaffs_obj *parent_obj; ++ ++ d_obj = yaffs_inode_to_obj(dentry->d_inode); ++ ++ if (d_obj) { ++ parent_obj = d_obj->parent; ++ if (parent_obj) { ++ parent_ino = yaffs_get_obj_inode(parent_obj); ++ inode = Y_IGET(sb, parent_ino); ++ ++ if (IS_ERR(inode)) { ++ parent = ERR_CAST(inode); ++ } else { ++ parent = d_obtain_alias(inode); ++ if (!IS_ERR(parent)) { ++ parent = ERR_PTR(-ENOMEM); ++ iput(inode); ++ } ++ } ++ } ++ } ++ ++ return parent; ++} ++ ++/* Just declare a zero structure as a NULL value implies ++ * using the default functions of exportfs. ++ */ ++ ++static struct export_operations yaffs_export_ops = { ++ .fh_to_dentry = yaffs2_fh_to_dentry, ++ .fh_to_parent = yaffs2_fh_to_parent, ++ .get_parent = yaffs2_get_parent, ++}; ++ ++#endif ++ ++static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) ++{ ++ /* Clear the association between the inode and ++ * the struct yaffs_obj. ++ */ ++ obj->my_inode = NULL; ++ yaffs_inode_to_obj_lv(inode) = NULL; ++ ++ /* If the object freeing was deferred, then the real ++ * free happens now. ++ * This should fix the inode inconsistency problem. ++ */ ++ yaffs_handle_defered_free(obj); ++} ++ ++#ifdef YAFFS_HAS_EVICT_INODE ++/* yaffs_evict_inode combines into one operation what was previously done in ++ * yaffs_clear_inode() and yaffs_delete_inode() ++ * ++ */ ++static void yaffs_evict_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ int deleteme = 0; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_evict_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (!inode->i_nlink && !is_bad_inode(inode)) ++ deleteme = 1; ++ truncate_inode_pages(&inode->i_data, 0); ++ Y_CLEAR_INODE(inode); ++ ++ if (deleteme && obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++} ++#else ++ ++/* clear is called to tell the fs to release any per-inode data it holds. ++ * The object might still exist on disk and is just being thrown out of the cache ++ * or else the object has actually been deleted and we're being called via ++ * the chain ++ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() ++ */ ++ ++static void yaffs_clear_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_clear_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++ ++} ++ ++/* delete is called when the link count is zero and the inode ++ * is put (ie. nobody wants to know about it anymore, time to ++ * delete the file). ++ * NB Must call clear_inode() ++ */ ++static void yaffs_delete_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_delete_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++ truncate_inode_pages(&inode->i_data, 0); ++#endif ++ clear_inode(inode); ++} ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ struct super_block *sb = dentry->d_sb; ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#else ++static int yaffs_statfs(struct super_block *sb, struct statfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#endif ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); ++ ++ yaffs_gross_lock(dev); ++ ++ buf->f_type = YAFFS_MAGIC; ++ buf->f_bsize = sb->s_blocksize; ++ buf->f_namelen = 255; ++ ++ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { ++ /* Do this if chunk size is not a power of 2 */ ++ ++ uint64_t bytes_in_dev; ++ uint64_t bytes_free; ++ ++ bytes_in_dev = ++ ((uint64_t) ++ ((dev->param.end_block - dev->param.start_block + ++ 1))) * ((uint64_t) (dev->param.chunks_per_block * ++ dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ ++ buf->f_blocks = bytes_in_dev; ++ ++ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * ++ ((uint64_t) (dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_free, sb->s_blocksize); ++ ++ buf->f_bfree = bytes_free; ++ ++ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { ++ ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ } else { ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ } ++ ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_bavail = buf->f_bfree; ++ ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++ ++static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) ++{ ++ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); ++ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); ++ int do_checkpoint; ++ int dirty = yaffs_check_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", ++ gc_urgent, ++ dirty ? "dirty" : "clean", ++ request_checkpoint ? "checkpoint requested" : "no checkpoint", ++ oneshot_checkpoint ? " one-shot" : ""); ++ ++ yaffs_gross_lock(dev); ++ do_checkpoint = ((request_checkpoint && !gc_urgent) || ++ oneshot_checkpoint) && !dev->is_checkpointed; ++ ++ if (dirty || do_checkpoint) { ++ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); ++ yaffs_clear_super_dirty(dev); ++ if (oneshot_checkpoint) ++ yaffs_auto_checkpoint &= ~4; ++ } ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#ifdef YAFFS_HAS_WRITE_SUPER ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static void yaffs_write_super(struct super_block *sb) ++#else ++static int yaffs_write_super(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_write_super %s", ++ request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) ++ return 0; ++#endif ++} ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_sync_fs(struct super_block *sb, int wait) ++#else ++static int yaffs_sync_fs(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++ return 0; ++} ++ ++/* the function only is used to change dev->read_only when this file system ++ * is remounted. ++ */ ++static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) ++{ ++ int read_only = 0; ++ struct mtd_info *mtd; ++ struct yaffs_dev *dev = 0; ++ ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (!mtd) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device #%u doesn't appear to exist", ++ MINOR(sb->s_dev)); ++ return 1; ++ } ++ ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device is not NAND it's type %d", ++ mtd->type); ++ return 1; ++ } ++ ++ read_only = ((*flags & MS_RDONLY) != 0); ++ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { ++ read_only = 1; ++ printk(KERN_INFO ++ "yaffs: mtd is read only, setting superblock read only"); ++ *flags |= MS_RDONLY; ++ } ++ ++ dev = sb->s_fs_info; ++ dev->read_only = read_only; ++ ++ return 0; ++} ++ ++static const struct super_operations yaffs_super_ops = { ++ .statfs = yaffs_statfs, ++ ++#ifndef YAFFS_USE_OWN_IGET ++ .read_inode = yaffs_read_inode, ++#endif ++#ifdef YAFFS_HAS_PUT_INODE ++ .put_inode = yaffs_put_inode, ++#endif ++ .put_super = yaffs_put_super, ++#ifdef YAFFS_HAS_EVICT_INODE ++ .evict_inode = yaffs_evict_inode, ++#else ++ .delete_inode = yaffs_delete_inode, ++ .clear_inode = yaffs_clear_inode, ++#endif ++ .sync_fs = yaffs_sync_fs, ++#ifdef YAFFS_HAS_WRITE_SUPER ++ .write_super = yaffs_write_super, ++#endif ++ .remount_fs = yaffs_remount_fs, ++}; ++ ++struct yaffs_options { ++ int inband_tags; ++ int skip_checkpoint_read; ++ int skip_checkpoint_write; ++ int no_cache; ++ int tags_ecc_on; ++ int tags_ecc_overridden; ++ int lazy_loading_enabled; ++ int lazy_loading_overridden; ++ int empty_lost_and_found; ++ int empty_lost_and_found_overridden; ++ int disable_summary; ++}; ++ ++#define MAX_OPT_LEN 30 ++static int yaffs_parse_options(struct yaffs_options *options, ++ const char *options_str) ++{ ++ char cur_opt[MAX_OPT_LEN + 1]; ++ int p; ++ int error = 0; ++ ++ /* Parse through the options which is a comma seperated list */ ++ ++ while (options_str && *options_str && !error) { ++ memset(cur_opt, 0, MAX_OPT_LEN + 1); ++ p = 0; ++ ++ while (*options_str == ',') ++ options_str++; ++ ++ while (*options_str && *options_str != ',') { ++ if (p < MAX_OPT_LEN) { ++ cur_opt[p] = *options_str; ++ p++; ++ } ++ options_str++; ++ } ++ ++ if (!strcmp(cur_opt, "inband-tags")) { ++ options->inband_tags = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-off")) { ++ options->tags_ecc_on = 0; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-on")) { ++ options->tags_ecc_on = 1; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-off")) { ++ options->lazy_loading_enabled = 0; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-on")) { ++ options->lazy_loading_enabled = 1; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "disable-summary")) { ++ options->disable_summary = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { ++ options->empty_lost_and_found = 0; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { ++ options->empty_lost_and_found = 1; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "no-cache")) { ++ options->no_cache = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { ++ options->skip_checkpoint_read = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { ++ options->skip_checkpoint_write = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint")) { ++ options->skip_checkpoint_read = 1; ++ options->skip_checkpoint_write = 1; ++ } else { ++ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", ++ cur_opt); ++ error = 1; ++ } ++ } ++ ++ return error; ++} ++ ++ ++static struct dentry *yaffs_make_root(struct inode *inode) ++{ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++ struct dentry *root = d_alloc_root(inode); ++ ++ if (!root) ++ iput(inode); ++ ++ return root; ++#else ++ return d_make_root(inode); ++#endif ++} ++ ++ ++ ++ ++static struct super_block *yaffs_internal_read_super(int yaffs_version, ++ struct super_block *sb, ++ void *data, int silent) ++{ ++ int n_blocks; ++ struct inode *inode = NULL; ++ struct dentry *root; ++ struct yaffs_dev *dev = 0; ++ char devname_buf[BDEVNAME_SIZE + 1]; ++ struct mtd_info *mtd; ++ int err; ++ char *data_str = (char *)data; ++ struct yaffs_linux_context *context = NULL; ++ struct yaffs_param *param; ++ ++ int read_only = 0; ++ int inband_tags = 0; ++ ++ struct yaffs_options options; ++ ++ unsigned mount_id; ++ int found; ++ struct yaffs_linux_context *context_iterator; ++ struct list_head *l; ++ ++ if (!sb) { ++ printk(KERN_INFO "yaffs: sb is NULL\n"); ++ return NULL; ++ } ++ ++ sb->s_magic = YAFFS_MAGIC; ++ sb->s_op = &yaffs_super_ops; ++ sb->s_flags |= MS_NOATIME; ++ ++ read_only = ((sb->s_flags & MS_RDONLY) != 0); ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ sb->s_export_op = &yaffs_export_ops; ++#endif ++ ++ if (!sb->s_dev) ++ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); ++ else if (!yaffs_devname(sb, devname_buf)) ++ printk(KERN_INFO "yaffs: devname is NULL\n"); ++ else ++ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", ++ sb->s_dev, ++ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); ++ ++ if (!data_str) ++ data_str = ""; ++ ++ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); ++ ++ memset(&options, 0, sizeof(options)); ++ ++ if (yaffs_parse_options(&options, data_str)) { ++ /* Option parsing failed */ ++ return NULL; ++ } ++ ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: Using yaffs%d", yaffs_version); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", ++ MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ yaffs_devname(sb, devname_buf)); ++ ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (IS_ERR(mtd)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device %u either not valid or unavailable", ++ MINOR(sb->s_dev)); ++ return NULL; ++ } ++ ++ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); ++ yaffs_version = 2; ++ } ++ ++ /* Added NCB 26/5/2006 for completeness */ ++ if (yaffs_version == 2 && !options.inband_tags ++ && WRITE_SIZE(mtd) == 512) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); ++ yaffs_version = 1; ++ } ++ ++ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || ++ options.inband_tags) ++ inband_tags = 1; ++ ++ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) ++ return NULL; ++ ++ /* OK, so if we got here, we have an MTD that's NAND and looks ++ * like it has the right capabilities ++ * Set the struct yaffs_dev up for mtd ++ */ ++ ++ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { ++ read_only = 1; ++ printk(KERN_INFO ++ "yaffs: mtd is read only, setting superblock read only\n" ++ ); ++ sb->s_flags |= MS_RDONLY; ++ } ++ ++ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); ++ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); ++ ++ if (!dev || !context) { ++ kfree(dev); ++ kfree(context); ++ dev = NULL; ++ context = NULL; ++ ++ /* Deep shit could not allocate device structure */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." ++ ); ++ return NULL; ++ } ++ memset(dev, 0, sizeof(struct yaffs_dev)); ++ param = &(dev->param); ++ ++ memset(context, 0, sizeof(struct yaffs_linux_context)); ++ dev->os_context = context; ++ INIT_LIST_HEAD(&(context->context_list)); ++ context->dev = dev; ++ context->super = sb; ++ ++ dev->read_only = read_only; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ sb->s_fs_info = dev; ++#else ++ sb->u.generic_sbp = dev; ++#endif ++ ++ ++ dev->driver_context = mtd; ++ param->name = mtd->name; ++ ++ /* Set up the memory size parameters.... */ ++ ++ ++ param->n_reserved_blocks = 5; ++ param->n_caches = (options.no_cache) ? 0 : 10; ++ param->inband_tags = inband_tags; ++ ++ param->enable_xattr = 1; ++ if (options.lazy_loading_overridden) ++ param->disable_lazy_load = !options.lazy_loading_enabled; ++ ++ param->defered_dir_update = 1; ++ ++ if (options.tags_ecc_overridden) ++ param->no_tags_ecc = !options.tags_ecc_on; ++ ++ param->empty_lost_n_found = 1; ++ param->refresh_period = 500; ++ param->disable_summary = options.disable_summary; ++ ++ ++#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING ++ param->disable_bad_block_marking = 1; ++#endif ++ if (options.empty_lost_and_found_overridden) ++ param->empty_lost_n_found = options.empty_lost_and_found; ++ ++ /* ... and the functions. */ ++ if (yaffs_version == 2) { ++ param->is_yaffs2 = 1; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++ param->total_bytes_per_chunk = mtd->writesize; ++ param->chunks_per_block = mtd->erasesize / mtd->writesize; ++#else ++ param->total_bytes_per_chunk = mtd->oobblock; ++ param->chunks_per_block = mtd->erasesize / mtd->oobblock; ++#endif ++ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ } else { ++ param->is_yaffs2 = 0; ++ n_blocks = YCALCBLOCKS(mtd->size, ++ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); ++ ++ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; ++ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; ++ } ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ ++ yaffs_mtd_drv_install(dev); ++ ++ param->sb_dirty_fn = yaffs_set_super_dirty; ++ param->gc_control_fn = yaffs_gc_control_callback; ++ ++ yaffs_dev_to_lc(dev)->super = sb; ++ ++ param->use_nand_ecc = 1; ++ ++ param->skip_checkpt_rd = options.skip_checkpoint_read; ++ param->skip_checkpt_wr = options.skip_checkpoint_write; ++ ++ mutex_lock(&yaffs_context_lock); ++ /* Get a mount id */ ++ found = 0; ++ for (mount_id = 0; !found; mount_id++) { ++ found = 1; ++ list_for_each(l, &yaffs_context_list) { ++ context_iterator = ++ list_entry(l, struct yaffs_linux_context, ++ context_list); ++ if (context_iterator->mount_id == mount_id) ++ found = 0; ++ } ++ } ++ context->mount_id = mount_id; ++ ++ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), ++ &yaffs_context_list); ++ mutex_unlock(&yaffs_context_lock); ++ ++ /* Directory search handling... */ ++ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); ++ param->remove_obj_fn = yaffs_remove_obj_callback; ++ ++ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ ++ yaffs_gross_lock(dev); ++ ++ err = yaffs_guts_initialise(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: guts initialised %s", ++ (err == YAFFS_OK) ? "OK" : "FAILED"); ++ ++ if (err == YAFFS_OK) ++ yaffs_bg_start(dev); ++ ++ if (!context->bg_thread) ++ param->defered_dir_update = 0; ++ ++ sb->s_maxbytes = yaffs_max_file_size(dev); ++ ++ /* Release lock before yaffs_get_inode() */ ++ yaffs_gross_unlock(dev); ++ ++ /* Create root inode */ ++ if (err == YAFFS_OK) ++ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); ++ ++ if (!inode) ++ return NULL; ++ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); ++ ++ root = yaffs_make_root(inode); ++ ++ if (!root) ++ return NULL; ++ ++ sb->s_root = root; ++ if(!dev->is_checkpointed) ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); ++ return sb; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data, struct vfsmount *mnt) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs_mount, ++#else ++ .get_sb = yaffs_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs_read_super(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, void *data, ++ struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs2_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs2", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs2_mount, ++#else ++ .get_sb = yaffs2_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs2_read_super(struct super_block *sb, ++ void *data, int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++static struct proc_dir_entry *my_proc_entry; ++ ++static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) ++{ ++ struct yaffs_param *param = &dev->param; ++ int bs[10]; ++ ++ yaffs_count_blocks_by_state(dev,bs); ++ ++ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); ++ buf += sprintf(buf, "end_block............ %d\n", param->end_block); ++ buf += sprintf(buf, "total_bytes_per_chunk %d\n", ++ param->total_bytes_per_chunk); ++ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); ++ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); ++ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); ++ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); ++ buf += sprintf(buf, "empty_lost_n_found... %d\n", ++ param->empty_lost_n_found); ++ buf += sprintf(buf, "disable_lazy_load.... %d\n", ++ param->disable_lazy_load); ++ buf += sprintf(buf, "disable_bad_block_mrk %d\n", ++ param->disable_bad_block_marking); ++ buf += sprintf(buf, "refresh_period....... %d\n", ++ param->refresh_period); ++ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); ++ buf += sprintf(buf, "n_reserved_blocks.... %d\n", ++ param->n_reserved_blocks); ++ buf += sprintf(buf, "always_check_erased.. %d\n", ++ param->always_check_erased); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "block count by state\n"); ++ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", ++ bs[0], bs[1], bs[2], bs[3], bs[4]); ++ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", ++ bs[5], bs[6], bs[7], bs[8], bs[9]); ++ ++ return buf; ++} ++ ++static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) ++{ ++ buf += sprintf(buf, "max file size....... %lld\n", ++ (long long) yaffs_max_file_size(dev)); ++ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", ++ dev->data_bytes_per_chunk); ++ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); ++ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); ++ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); ++ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", ++ dev->blocks_in_checkpt); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); ++ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); ++ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); ++ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); ++ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); ++ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); ++ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); ++ buf += sprintf(buf, "passive_gc_count..... %u\n", ++ dev->passive_gc_count); ++ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", ++ dev->oldest_dirty_gc_count); ++ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); ++ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); ++ buf += sprintf(buf, "n_retried_writes..... %u\n", ++ dev->n_retried_writes); ++ buf += sprintf(buf, "n_retired_blocks..... %u\n", ++ dev->n_retired_blocks); ++ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); ++ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); ++ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", ++ dev->n_tags_ecc_fixed); ++ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", ++ dev->n_tags_ecc_unfixed); ++ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); ++ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); ++ buf += sprintf(buf, "n_unlinked_files..... %u\n", ++ dev->n_unlinked_files); ++ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); ++ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); ++ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); ++ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); ++ ++ return buf; ++} ++ ++static int yaffs_proc_read(char *page, ++ char **start, ++ off_t offset, int count, int *eof, void *data) ++{ ++ struct list_head *item; ++ char *buf = page; ++ int step = offset; ++ int n = 0; ++ ++ /* Get proc_file_read() to step 'offset' by one on each sucessive call. ++ * We use 'offset' (*ppos) to indicate where we are in dev_list. ++ * This also assumes the user has posted a read buffer large ++ * enough to hold the complete output; but that's life in /proc. ++ */ ++ ++ *(int *)start = 1; ++ ++ /* Print header first */ ++ if (step == 0) ++ buf += ++ sprintf(buf, ++ "Multi-version YAFFS built\n"); ++ else if (step == 1) ++ buf += sprintf(buf, "\n"); ++ else { ++ step -= 2; ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (n < (step & ~1)) { ++ n += 2; ++ continue; ++ } ++ if ((step & 1) == 0) { ++ buf += ++ sprintf(buf, "\nDevice %d \"%s\"\n", n, ++ dev->param.name); ++ buf = yaffs_dump_dev_part0(buf, dev); ++ } else { ++ buf = yaffs_dump_dev_part1(buf, dev); ++ } ++ ++ break; ++ } ++ mutex_unlock(&yaffs_context_lock); ++ } ++ ++ return buf - page < count ? buf - page : count; ++} ++ ++/** ++ * Set the verbosity of the warnings and error messages. ++ * ++ * Note that the names can only be a..z or _ with the current code. ++ */ ++ ++static struct { ++ char *mask_name; ++ unsigned mask_bitfield; ++} mask_flags[] = { ++ {"allocate", YAFFS_TRACE_ALLOCATE}, ++ {"always", YAFFS_TRACE_ALWAYS}, ++ {"background", YAFFS_TRACE_BACKGROUND}, ++ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, ++ {"buffers", YAFFS_TRACE_BUFFERS}, ++ {"bug", YAFFS_TRACE_BUG}, ++ {"checkpt", YAFFS_TRACE_CHECKPOINT}, ++ {"deletion", YAFFS_TRACE_DELETION}, ++ {"erase", YAFFS_TRACE_ERASE}, ++ {"error", YAFFS_TRACE_ERROR}, ++ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, ++ {"gc", YAFFS_TRACE_GC}, ++ {"lock", YAFFS_TRACE_LOCK}, ++ {"mtd", YAFFS_TRACE_MTD}, ++ {"nandaccess", YAFFS_TRACE_NANDACCESS}, ++ {"os", YAFFS_TRACE_OS}, ++ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, ++ {"scan", YAFFS_TRACE_SCAN}, ++ {"mount", YAFFS_TRACE_MOUNT}, ++ {"tracing", YAFFS_TRACE_TRACING}, ++ {"sync", YAFFS_TRACE_SYNC}, ++ {"write", YAFFS_TRACE_WRITE}, ++ {"verify", YAFFS_TRACE_VERIFY}, ++ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, ++ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, ++ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, ++ {"all", 0xffffffff}, ++ {"none", 0}, ++ {NULL, 0}, ++}; ++ ++#define MAX_MASK_NAME_LENGTH 40 ++static int yaffs_proc_write_trace_options(struct file *file, const char *buf, ++ unsigned long count) ++{ ++ unsigned rg = 0, mask_bitfield; ++ char *end; ++ char *mask_name; ++ const char *x; ++ char substring[MAX_MASK_NAME_LENGTH + 1]; ++ int i; ++ int done = 0; ++ int add, len = 0; ++ int pos = 0; ++ ++ rg = yaffs_trace_mask; ++ ++ while (!done && (pos < count)) { ++ done = 1; ++ while ((pos < count) && isspace(buf[pos])) ++ pos++; ++ ++ switch (buf[pos]) { ++ case '+': ++ case '-': ++ case '=': ++ add = buf[pos]; ++ pos++; ++ break; ++ ++ default: ++ add = ' '; ++ break; ++ } ++ mask_name = NULL; ++ ++ mask_bitfield = simple_strtoul(buf + pos, &end, 0); ++ ++ if (end > buf + pos) { ++ mask_name = "numeral"; ++ len = end - (buf + pos); ++ pos += len; ++ done = 0; ++ } else { ++ for (x = buf + pos, i = 0; ++ (*x == '_' || (*x >= 'a' && *x <= 'z')) && ++ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) ++ substring[i] = *x; ++ substring[i] = '\0'; ++ ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ if (strcmp(substring, mask_flags[i].mask_name) ++ == 0) { ++ mask_name = mask_flags[i].mask_name; ++ mask_bitfield = ++ mask_flags[i].mask_bitfield; ++ done = 0; ++ break; ++ } ++ } ++ } ++ ++ if (mask_name != NULL) { ++ done = 0; ++ switch (add) { ++ case '-': ++ rg &= ~mask_bitfield; ++ break; ++ case '+': ++ rg |= mask_bitfield; ++ break; ++ case '=': ++ rg = mask_bitfield; ++ break; ++ default: ++ rg |= mask_bitfield; ++ break; ++ } ++ } ++ } ++ ++ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; ++ ++ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); ++ ++ if (rg & YAFFS_TRACE_ALWAYS) { ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ char flag; ++ flag = ((rg & mask_flags[i].mask_bitfield) == ++ mask_flags[i].mask_bitfield) ? '+' : '-'; ++ printk(KERN_DEBUG "%c%s\n", flag, ++ mask_flags[i].mask_name); ++ } ++ } ++ ++ return count; ++} ++ ++/* Debug strings are of the form: ++ * .bnnn print info on block n ++ * .cobjn,chunkn print nand chunk id for objn:chunkn ++ */ ++ ++static int yaffs_proc_debug_write(struct file *file, const char *buf, ++ unsigned long count) ++{ ++ ++ char str[100]; ++ char *p0; ++ char *p1; ++ long p1_val; ++ long p0_val; ++ char cmd; ++ struct list_head *item; ++ ++ memset(str, 0, sizeof(str)); ++ memcpy(str, buf, min((size_t)count, sizeof(str) -1)); ++ ++ cmd = str[1]; ++ ++ p0 = str + 2; ++ ++ p1 = p0; ++ ++ while (*p1 && *p1 != ',') { ++ p1++; ++ } ++ *p1 = '\0'; ++ p1++; ++ ++ p0_val = simple_strtol(p0, NULL, 0); ++ p1_val = simple_strtol(p1, NULL, 0); ++ ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (cmd == 'b') { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev,p0_val); ++ ++ if(bi) { ++ printk("Block %d: state %d, retire %d, use %d, seq %d\n", ++ (int)p0_val, bi->block_state, ++ bi->needs_retiring, bi->pages_in_use, ++ bi->seq_number); ++ } ++ } else if (cmd == 'c') { ++ struct yaffs_obj *obj; ++ int nand_chunk; ++ ++ obj = yaffs_find_by_number(dev, p0_val); ++ if (!obj) ++ printk("No obj %d\n", (int)p0_val); ++ else { ++ if(p1_val == 0) ++ nand_chunk = obj->hdr_chunk; ++ else ++ nand_chunk = ++ yaffs_find_chunk_in_file(obj, ++ p1_val, NULL); ++ printk("Nand chunk for %d:%d is %d\n", ++ (int)p0_val, (int)p1_val, nand_chunk); ++ } ++ } ++ } ++ ++ mutex_unlock(&yaffs_context_lock); ++ ++ return count; ++} ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) ++static int yaffs_proc_write(struct file *file, const char *buf, ++ unsigned long count, void *ppos) ++#else ++static ssize_t yaffs_proc_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++#endif ++{ ++ if (buf[0] == '.') ++ return yaffs_proc_debug_write(file, buf, count); ++ return yaffs_proc_write_trace_options(file, buf, count); ++} ++ ++/* Stuff to handle installation of file systems */ ++struct file_system_to_install { ++ struct file_system_type *fst; ++ int installed; ++}; ++ ++static struct file_system_to_install fs_to_install[] = { ++ {&yaffs_fs_type, 0}, ++ {&yaffs2_fs_type, 0}, ++ {NULL, 0} ++}; ++ ++ ++#ifdef YAFFS_NEW_PROCFS ++static int yaffs_proc_show(struct seq_file *m, void *v) ++{ ++ /* FIXME: Unify in a better way? */ ++ char buffer[512]; ++ char *start; ++ int len; ++ ++ len = yaffs_proc_read(buffer, &start, 0, sizeof(buffer), NULL, NULL); ++ seq_puts(m, buffer); ++ return 0; ++} ++ ++static int yaffs_proc_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, yaffs_proc_show, NULL); ++} ++ ++static struct file_operations procfs_ops = { ++ .owner = THIS_MODULE, ++ .open = yaffs_proc_open, ++ .read = seq_read, ++ .write = yaffs_proc_write, ++}; ++ ++static int yaffs_procfs_init(void) ++{ ++ /* Install the proc_fs entries */ ++ my_proc_entry = proc_create("yaffs", ++ S_IRUGO | S_IFREG, ++ YPROC_ROOT, ++ &procfs_ops); ++ ++ if (my_proc_entry) { ++ return 0; ++ } else { ++ return -ENOMEM; ++ } ++} ++ ++#else ++ ++ ++static int yaffs_procfs_init(void) ++{ ++ /* Install the proc_fs entries */ ++ my_proc_entry = create_proc_entry("yaffs", ++ S_IRUGO | S_IFREG, YPROC_ROOT); ++ ++ if (my_proc_entry) { ++ my_proc_entry->write_proc = yaffs_proc_write; ++ my_proc_entry->read_proc = yaffs_proc_read; ++ my_proc_entry->data = NULL; ++ return 0; ++ } else { ++ return -ENOMEM; ++ } ++} ++ ++#endif ++ ++ ++static int __init init_yaffs_fs(void) ++{ ++ int error = 0; ++ struct file_system_to_install *fsinst; ++ ++ mutex_init(&yaffs_context_lock); ++ ++ error = yaffs_procfs_init(); ++ if (error) ++ return error; ++ ++ /* Now add the file system entries */ ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst && !error) { ++ error = register_filesystem(fsinst->fst); ++ if (!error) ++ fsinst->installed = 1; ++ fsinst++; ++ } ++ ++ /* Any errors? uninstall */ ++ if (error) { ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++ } ++ ++ return error; ++} ++ ++static void __exit exit_yaffs_fs(void) ++{ ++ ++ struct file_system_to_install *fsinst; ++ ++ remove_proc_entry("yaffs", YPROC_ROOT); ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++} ++ ++module_init(init_yaffs_fs) ++ module_exit(exit_yaffs_fs) ++ ++ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); ++MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.14.43/fs/yaffs2/yaffs_yaffs1.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_yaffs1.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,422 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_yaffs1.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++#include "yaffs_attribs.h" ++ ++int yaffs1_scan(struct yaffs_dev *dev) ++{ ++ struct yaffs_ext_tags tags; ++ int blk; ++ int result; ++ int chunk; ++ int c; ++ int deleted; ++ enum yaffs_block_state state; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int alloc_failed = 0; ++ struct yaffs_shadow_fixer *shadow_fixers = NULL; ++ u8 *chunk_data; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs1_scan starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, state, seq_number); ++ ++ if (state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } ++ bi++; ++ } ++ ++ /* For each block.... */ ++ for (blk = dev->internal_start_block; ++ !alloc_failed && blk <= dev->internal_end_block; blk++) { ++ ++ cond_resched(); ++ ++ bi = yaffs_get_block_info(dev, blk); ++ state = bi->block_state; ++ ++ deleted = 0; ++ ++ /* For each chunk in each block that needs scanning.... */ ++ for (c = 0; ++ !alloc_failed && c < dev->param.chunks_per_block && ++ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { ++ /* Read the tags and decide what to do */ ++ chunk = blk * dev->param.chunks_per_block + c; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, ++ &tags); ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || ++ tags.is_deleted) { ++ /* YAFFS1 only... ++ * A deleted chunk ++ */ ++ deleted++; ++ dev->n_free_chunks++; ++ } else if (!tags.chunk_used) { ++ /* An unassigned chunk in the block ++ * This means that either the block is empty or ++ * this is the one being allocated from ++ */ ++ ++ if (c == 0) { ++ /* We're looking at the first chunk in ++ *the block so the block is unused */ ++ state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ /* this is the block being allocated */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, c); ++ state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = c; ++ dev->alloc_block_finder = blk; ++ ++ } ++ ++ dev->n_free_chunks += ++ (dev->param.chunks_per_block - c); ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ unsigned int endpos; ++ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ /* PutChunkIntoFile checks for a clash ++ * (two data chunks with the same chunk_id). ++ */ ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in) { ++ if (!yaffs_put_chunk_in_file ++ (in, tags.chunk_id, chunk, 1)) ++ alloc_failed = 1; ++ } ++ ++ endpos = ++ (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk + ++ tags.n_bytes; ++ if (in && ++ in->variant_type == ++ YAFFS_OBJECT_TYPE_FILE && ++ in->variant.file_variant.scanned_size < ++ endpos) { ++ in->variant.file_variant.scanned_size = ++ endpos; ++ if (!dev->param.use_header_file_size) { ++ in->variant. ++ file_variant.file_size = ++ in->variant. ++ file_variant.scanned_size; ++ } ++ ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Make the object ++ */ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ in = yaffs_find_by_number(dev, tags.obj_id); ++ if (in && in->variant_type != oh->type) { ++ /* This should not happen, but somehow ++ * Wev'e ended up with an obj_id that ++ * has been reused but not yet deleted, ++ * and worse still it has changed type. ++ * Delete the old object. ++ */ ++ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ oh->type); ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in && oh->shadows_obj > 0) { ++ ++ struct yaffs_shadow_fixer *fixer; ++ fixer = ++ kmalloc(sizeof ++ (struct yaffs_shadow_fixer), ++ GFP_NOFS); ++ if (fixer) { ++ fixer->next = shadow_fixers; ++ shadow_fixers = fixer; ++ fixer->obj_id = tags.obj_id; ++ fixer->shadowed_id = ++ oh->shadows_obj; ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Shadow fixer: %d shadows %d", ++ fixer->obj_id, ++ fixer->shadowed_id); ++ ++ } ++ ++ } ++ ++ if (in && in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate and need to ++ * resolve it. */ ++ ++ unsigned existing_serial = in->serial; ++ unsigned new_serial = ++ tags.serial_number; ++ ++ if (((existing_serial + 1) & 3) == ++ new_serial) { ++ /* Use new one - destroy the ++ * exisiting one */ ++ yaffs_chunk_del(dev, ++ in->hdr_chunk, ++ 1, __LINE__); ++ in->valid = 0; ++ } else { ++ /* Use existing - destroy ++ * this one. */ ++ yaffs_chunk_del(dev, chunk, 1, ++ __LINE__); ++ } ++ } ++ ++ if (in && !in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == ++ YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ } else if (in && !in->valid) { ++ /* we need to load this info */ ++ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ in->dirty = 0; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ parent = ++ yaffs_find_or_create_by_number ++ (dev, oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ if (!parent) ++ alloc_failed = 1; ++ if (parent && parent->variant_type == ++ YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant. ++ children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, a problem.... ++ * We're trying to use a ++ * non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (dev->param. ++ use_header_file_size) ++ in->variant. ++ file_variant.file_size ++ = yaffs_oh_to_size(oh); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant. ++ hardlink_variant.equiv_id = ++ oh->equiv_id; ++ list_add(&in->hard_links, ++ &hard_list); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant. ++ alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant. ++ symlink_variant.alias) ++ alloc_failed = 1; ++ break; ++ } ++ } ++ } ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, ++ * then the block is fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ /* If the block was partially allocated then ++ * treat it as fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ bi->block_state = state; ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ yaffs_block_became_dirty(dev, blk); ++ } ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We should now have scanned all the objects, now it's time to add ++ * these hardlinks. ++ */ ++ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ /* ++ * Fix up any shadowed objects. ++ * There should not be more than one of these. ++ */ ++ { ++ struct yaffs_shadow_fixer *fixer; ++ struct yaffs_obj *obj; ++ ++ while (shadow_fixers) { ++ fixer = shadow_fixers; ++ shadow_fixers = fixer->next; ++ /* Complete the rename transaction by deleting the ++ * shadowed object then setting the object header ++ to unshadowed. ++ */ ++ obj = yaffs_find_by_number(dev, fixer->shadowed_id); ++ if (obj) ++ yaffs_del_obj(obj); ++ ++ obj = yaffs_find_by_number(dev, fixer->obj_id); ++ ++ if (obj) ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ kfree(fixer); ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.14.43/fs/yaffs2/yaffs_yaffs1.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_yaffs1.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS1_H__ ++#define __YAFFS_YAFFS1_H__ ++ ++#include "yaffs_guts.h" ++int yaffs1_scan(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.14.43/fs/yaffs2/yaffs_yaffs2.c +--- linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_yaffs2.c 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,1532 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_checkptrw.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_verify.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* ++ * Checkpoints are really no benefit on very small partitions. ++ * ++ * To save space on small partitions don't bother with checkpoints unless ++ * the partition is at least this big. ++ */ ++#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 ++#define YAFFS_SMALL_HOLE_THRESHOLD 4 ++ ++/* ++ * Oldest Dirty Sequence Number handling. ++ */ ++ ++/* yaffs_calc_oldest_dirty_seq() ++ * yaffs2_find_oldest_dirty_seq() ++ * Calculate the oldest dirty sequence number if we don't know it. ++ */ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ int i; ++ unsigned seq; ++ unsigned block_no = 0; ++ struct yaffs_block_info *b; ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ /* Find the oldest dirty sequence number. */ ++ seq = dev->seq_number + 1; ++ b = dev->block_info; ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ if (b->block_state == YAFFS_BLOCK_STATE_FULL && ++ (b->pages_in_use - b->soft_del_pages) < ++ dev->param.chunks_per_block && ++ b->seq_number < seq) { ++ seq = b->seq_number; ++ block_no = i; ++ } ++ b++; ++ } ++ ++ if (block_no) { ++ dev->oldest_dirty_seq = seq; ++ dev->oldest_dirty_block = block_no; ++ } ++} ++ ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->oldest_dirty_seq) ++ yaffs_calc_oldest_dirty_seq(dev); ++} ++ ++/* ++ * yaffs_clear_oldest_dirty_seq() ++ * Called when a block is erased or marked bad. (ie. when its seq_number ++ * becomes invalid). If the value matches the oldest then we clear ++ * dev->oldest_dirty_seq to force its recomputation. ++ */ ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ } ++} ++ ++/* ++ * yaffs2_update_oldest_dirty_seq() ++ * Update the oldest dirty sequence number whenever we dirty a block. ++ * Only do this if the oldest_dirty_seq is actually being tracked. ++ */ ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (dev->oldest_dirty_seq) { ++ if (dev->oldest_dirty_seq > bi->seq_number) { ++ dev->oldest_dirty_seq = bi->seq_number; ++ dev->oldest_dirty_block = block_no; ++ } ++ } ++} ++ ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return 1; /* disqualification only applies to yaffs2. */ ++ ++ if (!bi->has_shrink_hdr) ++ return 1; /* can gc */ ++ ++ yaffs2_find_oldest_dirty_seq(dev); ++ ++ /* Can't do gc of this block if there are any blocks older than this ++ * one that have discarded pages. ++ */ ++ return (bi->seq_number <= dev->oldest_dirty_seq); ++} ++ ++/* ++ * yaffs2_find_refresh_block() ++ * periodically finds the oldest full block by sequence number for refreshing. ++ * Only for yaffs2. ++ */ ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) ++{ ++ u32 b; ++ u32 oldest = 0; ++ u32 oldest_seq = 0; ++ struct yaffs_block_info *bi; ++ ++ if (!dev->param.is_yaffs2) ++ return oldest; ++ ++ /* ++ * If refresh period < 10 then refreshing is disabled. ++ */ ++ if (dev->param.refresh_period < 10) ++ return oldest; ++ ++ /* ++ * Fix broken values. ++ */ ++ if (dev->refresh_skip > dev->param.refresh_period) ++ dev->refresh_skip = dev->param.refresh_period; ++ ++ if (dev->refresh_skip > 0) ++ return oldest; ++ ++ /* ++ * Refresh skip is now zero. ++ * We'll do a refresh this time around.... ++ * Update the refresh skip and find the oldest block. ++ */ ++ dev->refresh_skip = dev->param.refresh_period; ++ dev->refresh_count++; ++ bi = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ ++ if (oldest < 1 || bi->seq_number < oldest_seq) { ++ oldest = b; ++ oldest_seq = bi->seq_number; ++ } ++ } ++ bi++; ++ } ++ ++ if (oldest > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC refresh count %d selected block %d with seq_number %d", ++ dev->refresh_count, oldest, oldest_seq); ++ } ++ ++ return oldest; ++} ++ ++int yaffs2_checkpt_required(struct yaffs_dev *dev) ++{ ++ int nblocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ nblocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ return !dev->param.skip_checkpt_wr && ++ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); ++} ++ ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) ++{ ++ int retval; ++ int n_bytes = 0; ++ int n_blocks; ++ int dev_blocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { ++ /* Not a valid value so recalculate */ ++ dev_blocks = dev->param.end_block - dev->param.start_block + 1; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(struct yaffs_checkpt_dev); ++ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); ++ n_bytes += dev_blocks * dev->chunk_bit_stride; ++ n_bytes += ++ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * ++ dev->n_obj; ++ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(u32); /* checksum */ ++ ++ /* Round up and add 2 blocks to allow for some bad blocks, ++ * so add 3 */ ++ ++ n_blocks = ++ (n_bytes / ++ (dev->data_bytes_per_chunk * ++ dev->param.chunks_per_block)) + 3; ++ ++ dev->checkpoint_blocks_required = n_blocks; ++ } ++ ++ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; ++ if (retval < 0) ++ retval = 0; ++ return retval; ++} ++ ++/*--------------------- Checkpointing --------------------*/ ++ ++static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ ++ memset(&cp, 0, sizeof(cp)); ++ ++ cp.struct_type = sizeof(cp); ++ cp.magic = YAFFS_MAGIC; ++ cp.version = YAFFS_CHECKPOINT_VERSION; ++ cp.head = (head) ? 1 : 0; ++ ++ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ if (ok) ++ ok = (cp.struct_type == sizeof(cp)) && ++ (cp.magic == YAFFS_MAGIC) && ++ (cp.version == YAFFS_CHECKPOINT_VERSION) && ++ (cp.head == ((head) ? 1 : 0)); ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, ++ struct yaffs_dev *dev) ++{ ++ cp->n_erased_blocks = dev->n_erased_blocks; ++ cp->alloc_block = dev->alloc_block; ++ cp->alloc_page = dev->alloc_page; ++ cp->n_free_chunks = dev->n_free_chunks; ++ ++ cp->n_deleted_files = dev->n_deleted_files; ++ cp->n_unlinked_files = dev->n_unlinked_files; ++ cp->n_bg_deletions = dev->n_bg_deletions; ++ cp->seq_number = dev->seq_number; ++ ++} ++ ++static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, ++ struct yaffs_checkpt_dev *cp) ++{ ++ dev->n_erased_blocks = cp->n_erased_blocks; ++ dev->alloc_block = cp->alloc_block; ++ dev->alloc_page = cp->alloc_page; ++ dev->n_free_chunks = cp->n_free_chunks; ++ ++ dev->n_deleted_files = cp->n_deleted_files; ++ dev->n_unlinked_files = cp->n_unlinked_files; ++ dev->n_bg_deletions = cp->n_bg_deletions; ++ dev->seq_number = cp->seq_number; ++} ++ ++static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ int ok; ++ ++ /* Write device runtime values */ ++ yaffs2_dev_to_checkpt_dev(&cp, dev); ++ cp.struct_type = sizeof(cp); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ /* Write block info */ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); ++ if (!ok) ++ return 0; ++ ++ /* Write chunk bits */ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = ++ (dev->internal_end_block - dev->internal_start_block + 1); ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ if (cp.struct_type != sizeof(cp)) ++ return 0; ++ ++ yaffs_checkpt_dev_to_dev(dev, &cp); ++ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); ++ ++ if (!ok) ++ return 0; ++ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, ++ struct yaffs_obj *obj) ++{ ++ cp->obj_id = obj->obj_id; ++ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; ++ cp->hdr_chunk = obj->hdr_chunk; ++ cp->variant_type = obj->variant_type; ++ cp->deleted = obj->deleted; ++ cp->soft_del = obj->soft_del; ++ cp->unlinked = obj->unlinked; ++ cp->fake = obj->fake; ++ cp->rename_allowed = obj->rename_allowed; ++ cp->unlink_allowed = obj->unlink_allowed; ++ cp->serial = obj->serial; ++ cp->n_data_chunks = obj->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; ++} ++ ++static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, ++ struct yaffs_checkpt_obj *cp) ++{ ++ struct yaffs_obj *parent; ++ ++ if (obj->variant_type != cp->variant_type) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", ++ cp->obj_id, cp->variant_type, cp->hdr_chunk, ++ obj->variant_type); ++ return 0; ++ } ++ ++ obj->obj_id = cp->obj_id; ++ ++ if (cp->parent_id) ++ parent = yaffs_find_or_create_by_number(obj->my_dev, ++ cp->parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ else ++ parent = NULL; ++ ++ if (parent) { ++ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", ++ cp->obj_id, cp->parent_id, ++ cp->variant_type, cp->hdr_chunk, ++ parent->variant_type); ++ return 0; ++ } ++ yaffs_add_obj_to_dir(parent, obj); ++ } ++ ++ obj->hdr_chunk = cp->hdr_chunk; ++ obj->variant_type = cp->variant_type; ++ obj->deleted = cp->deleted; ++ obj->soft_del = cp->soft_del; ++ obj->unlinked = cp->unlinked; ++ obj->fake = cp->fake; ++ obj->rename_allowed = cp->rename_allowed; ++ obj->unlink_allowed = cp->unlink_allowed; ++ obj->serial = cp->serial; ++ obj->n_data_chunks = cp->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; ++ ++ if (obj->hdr_chunk > 0) ++ obj->lazy_loaded = 1; ++ return 1; ++} ++ ++static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, ++ struct yaffs_tnode *tn, u32 level, ++ int chunk_offset) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ int ok = 1; ++ u32 base_offset; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { ++ if (!tn->internal[i]) ++ continue; ++ ok = yaffs2_checkpt_tnode_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) + i); ++ } ++ return ok; ++ } ++ ++ /* Level 0 tnode */ ++ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; ++ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == ++ sizeof(base_offset)); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ ++ return ok; ++} ++ ++static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 end_marker = ~0; ++ int ok = 1; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return ok; ++ ++ ok = yaffs2_checkpt_tnode_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant.file_variant. ++ top_level, 0); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, ++ sizeof(end_marker)) == sizeof(end_marker)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 base_chunk; ++ int ok = 1; ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; ++ struct yaffs_tnode *tn; ++ int nread = 0; ++ ++ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == ++ sizeof(base_chunk)); ++ ++ while (ok && (~base_chunk)) { ++ nread++; ++ /* Read level 0 tnode */ ++ ++ tn = yaffs_get_tnode(dev); ++ if (tn) ++ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ else ++ ok = 0; ++ ++ if (tn && ok) ++ ok = yaffs_add_find_tnode_0(dev, ++ file_stuct_ptr, ++ base_chunk, tn) ? 1 : 0; ++ ++ if (ok) ++ ok = (yaffs2_checkpt_rd ++ (dev, &base_chunk, ++ sizeof(base_chunk)) == sizeof(base_chunk)); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read tnodes %d records, last %d. ok %d", ++ nread, base_chunk, ok); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int i; ++ int ok = 1; ++ struct list_head *lh; ++ ++ /* Iterate through the objects in each hash entry, ++ * dumping them to the checkpointing stream. ++ */ ++ ++ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ if (!obj->defered_free) { ++ yaffs2_obj_checkpt_obj(&cp, obj); ++ cp.struct_type = sizeof(cp); ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", ++ cp.obj_id, cp.parent_id, ++ cp.variant_type, cp.hdr_chunk, obj); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, ++ sizeof(cp)) == sizeof(cp)); ++ ++ if (ok && ++ obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) ++ ok = yaffs2_wr_checkpt_tnodes(obj); ++ } ++ } ++ } ++ ++ /* Dump end of list */ ++ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); ++ cp.struct_type = sizeof(cp); ++ ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int ok = 1; ++ int done = 0; ++ LIST_HEAD(hard_list); ++ ++ ++ while (ok && !done) { ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (cp.struct_type != sizeof(cp)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "struct size %d instead of %d ok %d", ++ cp.struct_type, (int)sizeof(cp), ok); ++ ok = 0; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read object %d parent %d type %d chunk %d ", ++ cp.obj_id, cp.parent_id, cp.variant_type, ++ cp.hdr_chunk); ++ ++ if (ok && cp.obj_id == ~0) { ++ done = 1; ++ } else if (ok) { ++ obj = ++ yaffs_find_or_create_by_number(dev, cp.obj_id, ++ cp.variant_type); ++ if (obj) { ++ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); ++ if (!ok) ++ break; ++ if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) { ++ ok = yaffs2_rd_checkpt_tnodes(obj); ++ } else if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_HARDLINK) { ++ list_add(&obj->hard_links, &hard_list); ++ } ++ } else { ++ ok = 0; ++ } ++ } ++ } ++ ++ if (ok) ++ yaffs_link_fixup(dev, &hard_list); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum); ++ ++ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == ++ sizeof(checkpt_sum)); ++ ++ if (!ok) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum0; ++ u32 checkpt_sum1; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); ++ ++ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == ++ sizeof(checkpt_sum1)); ++ ++ if (!ok) ++ return 0; ++ ++ if (checkpt_sum0 != checkpt_sum1) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!yaffs2_checkpt_required(dev)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint write"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 1); ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint device"); ++ ok = yaffs2_wr_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint objects"); ++ ok = yaffs2_wr_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) ++ ok = yaffs2_wr_checkpt_sum(dev); ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return dev->is_checkpointed; ++} ++ ++static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!dev->param.is_yaffs2) ++ ok = 0; ++ ++ if (ok && dev->param.skip_checkpt_rd) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint read"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint device"); ++ ok = yaffs2_rd_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint objects"); ++ ok = yaffs2_rd_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) { ++ ok = yaffs2_rd_checkpt_sum(dev); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint checksum %d", ok); ++ } ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return ok ? 1 : 0; ++} ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) ++{ ++ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { ++ dev->is_checkpointed = 0; ++ yaffs2_checkpt_invalidate_stream(dev); ++ } ++ if (dev->param.sb_dirty_fn) ++ dev->param.sb_dirty_fn(dev); ++} ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "save entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ ++ if (!dev->is_checkpointed) { ++ yaffs2_checkpt_invalidate(dev); ++ yaffs2_wr_checkpt_data(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, ++ "save exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return dev->is_checkpointed; ++} ++ ++int yaffs2_checkpt_restore(struct yaffs_dev *dev) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ retval = yaffs2_rd_checkpt_data(dev); ++ ++ if (dev->is_checkpointed) { ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return retval; ++} ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) ++{ ++ /* if new_size > old_file_size. ++ * We're going to be writing a hole. ++ * If the hole is small then write zeros otherwise write a start ++ * of hole marker. ++ */ ++ loff_t old_file_size; ++ loff_t increase; ++ int small_hole; ++ int result = YAFFS_OK; ++ struct yaffs_dev *dev = NULL; ++ u8 *local_buffer = NULL; ++ int small_increase_ok = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ dev = obj->my_dev; ++ ++ /* Bail out if not yaffs2 mode */ ++ if (!dev->param.is_yaffs2) ++ return YAFFS_OK; ++ ++ old_file_size = obj->variant.file_variant.file_size; ++ ++ if (new_size <= old_file_size) ++ return YAFFS_OK; ++ ++ increase = new_size - old_file_size; ++ ++ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && ++ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) ++ small_hole = 1; ++ else ++ small_hole = 0; ++ ++ if (small_hole) ++ local_buffer = yaffs_get_temp_buffer(dev); ++ ++ if (local_buffer) { ++ /* fill hole with zero bytes */ ++ loff_t pos = old_file_size; ++ int this_write; ++ int written; ++ memset(local_buffer, 0, dev->data_bytes_per_chunk); ++ small_increase_ok = 1; ++ ++ while (increase > 0 && small_increase_ok) { ++ this_write = increase; ++ if (this_write > dev->data_bytes_per_chunk) ++ this_write = dev->data_bytes_per_chunk; ++ written = ++ yaffs_do_file_wr(obj, local_buffer, pos, this_write, ++ 0); ++ if (written == this_write) { ++ pos += this_write; ++ increase -= this_write; ++ } else { ++ small_increase_ok = 0; ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ ++ /* If out of space then reverse any chunks we've added */ ++ if (!small_increase_ok) ++ yaffs_resize_file_down(obj, old_file_size); ++ } ++ ++ if (!small_increase_ok && ++ obj->parent && ++ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { ++ /* Write a hole start header with the old file size */ ++ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); ++ } ++ ++ return result; ++} ++ ++struct yaffs_block_index { ++ int seq; ++ int block; ++}; ++ ++static int yaffs2_ybicmp(const void *a, const void *b) ++{ ++ int aseq = ((struct yaffs_block_index *)a)->seq; ++ int bseq = ((struct yaffs_block_index *)b)->seq; ++ int ablock = ((struct yaffs_block_index *)a)->block; ++ int bblock = ((struct yaffs_block_index *)b)->block; ++ ++ if (aseq == bseq) ++ return ablock - bblock; ++ ++ return aseq - bseq; ++} ++ ++static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int blk, int chunk_in_block, ++ int *found_chunks, ++ u8 *chunk_data, ++ struct list_head *hard_list, ++ int summary_available) ++{ ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int equiv_id; ++ loff_t file_size; ++ int is_shrink; ++ int is_unlinked; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; ++ struct yaffs_file_var *file_var; ++ struct yaffs_hardlink_var *hl_var; ++ struct yaffs_symlink_var *sl_var; ++ ++ if (summary_available) { ++ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); ++ tags.seq_number = bi->seq_number; ++ } ++ ++ if (!summary_available || tags.obj_id == 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); ++ dev->tags_used++; ++ } else { ++ dev->summary_used++; ++ } ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (!tags.chunk_used) { ++ /* An unassigned chunk in the block. ++ * If there are used chunks after this one, then ++ * it is a chunk that was skipped due to failing ++ * the erased check. Just skip it so that it can ++ * be deleted. ++ * But, more typically, We get here when this is ++ * an unallocated chunk and his means that ++ * either the block is empty or this is the one ++ * being allocated from ++ */ ++ ++ if (*found_chunks) { ++ /* This is a chunk that was skipped due ++ * to failing the erased check */ ++ } else if (chunk_in_block == 0) { ++ /* We're looking at the first chunk in ++ * the block so the block is unused */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ if (dev->seq_number == bi->seq_number) { ++ /* Allocating from this block*/ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, chunk_in_block); ++ ++ bi->block_state = ++ YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = chunk_in_block; ++ dev->alloc_block_finder = blk; ++ } else { ++ /* This is a partially written block ++ * that is not the current ++ * allocation block. ++ */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Partially written block %d detected. gc will fix this.", ++ blk); ++ } ++ } ++ } ++ ++ dev->n_free_chunks++; ++ ++ } else if (tags.ecc_result == ++ YAFFS_ECC_RESULT_UNFIXED) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Unfixed ECC in chunk(%d:%d), chunk ignored", ++ blk, chunk_in_block); ++ dev->n_free_chunks++; ++ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || ++ tags.chunk_id > YAFFS_MAX_CHUNK_ID || ++ tags.obj_id == YAFFS_OBJECTID_SUMMARY || ++ (tags.chunk_id > 0 && ++ tags.n_bytes > dev->data_bytes_per_chunk) || ++ tags.seq_number != bi->seq_number) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", ++ blk, chunk_in_block, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ dev->n_free_chunks++; ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ loff_t endpos; ++ loff_t chunk_base = (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk; ++ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ if (!in) ++ /* Out of memory */ ++ alloc_failed = 1; ++ ++ if (in && ++ in->variant_type == YAFFS_OBJECT_TYPE_FILE && ++ chunk_base < in->variant.file_variant.shrink_size) { ++ /* This has not been invalidated by ++ * a resize */ ++ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, ++ chunk, -1)) ++ alloc_failed = 1; ++ ++ /* File size is calculated by looking at ++ * the data chunks if we have not ++ * seen an object header yet. ++ * Stop this practice once we find an ++ * object header. ++ */ ++ endpos = chunk_base + tags.n_bytes; ++ ++ if (!in->valid && ++ in->variant.file_variant.scanned_size < endpos) { ++ in->variant.file_variant. ++ scanned_size = endpos; ++ in->variant.file_variant. ++ file_size = endpos; ++ } ++ } else if (in) { ++ /* This chunk has been invalidated by a ++ * resize, or a past file deletion ++ * so delete the chunk*/ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Thus, we read in the object header and make ++ * the object ++ */ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ oh = NULL; ++ in = NULL; ++ ++ if (tags.extra_available) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ tags.extra_obj_type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ ++ if (!in || ++ (!in->valid && dev->param.disable_lazy_load) || ++ tags.extra_shadows || ++ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { ++ ++ /* If we don't have valid info then we ++ * need to read the chunk ++ * TODO In future we can probably defer ++ * reading the chunk and living with ++ * invalid data until needed. ++ */ ++ ++ result = yaffs_rd_chunk_tags_nand(dev, ++ chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ if (dev->param.inband_tags) { ++ /* Fix up the header if they got ++ * corrupted by inband tags */ ++ oh->shadows_obj = ++ oh->inband_shadowed_obj_id; ++ oh->is_shrink = ++ oh->inband_is_shrink; ++ } ++ ++ if (!in) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, oh->type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ } ++ ++ if (!in) { ++ /* TODO Hoosterman we have a problem! */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", ++ tags.obj_id, chunk); ++ return YAFFS_FAIL; ++ } ++ ++ if (in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate that will be ++ * discarded, but we first have to suck ++ * out resize info if it is a file. ++ */ ++ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && ++ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || ++ (tags.extra_available && ++ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ )) { ++ loff_t this_size = (oh) ? ++ yaffs_oh_to_size(oh) : ++ tags.extra_file_size; ++ u32 parent_obj_id = (oh) ? ++ oh->parent_obj_id : ++ tags.extra_parent_id; ++ ++ is_shrink = (oh) ? ++ oh->is_shrink : ++ tags.extra_is_shrink; ++ ++ /* If it is deleted (unlinked ++ * at start also means deleted) ++ * we treat the file size as ++ * being zeroed at this point. ++ */ ++ if (parent_obj_id == YAFFS_OBJECTID_DELETED || ++ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { ++ this_size = 0; ++ is_shrink = 1; ++ } ++ ++ if (is_shrink && ++ in->variant.file_variant.shrink_size > ++ this_size) ++ in->variant.file_variant.shrink_size = ++ this_size; ++ ++ if (is_shrink) ++ bi->has_shrink_hdr = 1; ++ } ++ /* Use existing - destroy this one. */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ ++ if (!in->valid && in->variant_type != ++ (oh ? oh->type : tags.extra_obj_type)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", ++ oh ? oh->type : tags.extra_obj_type, ++ in->variant_type, tags.obj_id, ++ chunk); ++ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); ++ } ++ ++ if (!in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ ++ if (oh) { ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->lazy_loaded = 0; ++ } else { ++ in->lazy_loaded = 1; ++ } ++ in->hdr_chunk = chunk; ++ ++ } else if (!in->valid) { ++ /* we need to load this info */ ++ in->valid = 1; ++ in->hdr_chunk = chunk; ++ if (oh) { ++ in->variant_type = oh->type; ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ ++ if (oh->shadows_obj > 0) ++ yaffs_handle_shadowed_obj(dev, ++ oh->shadows_obj, 1); ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ parent = yaffs_find_or_create_by_number(dev, ++ oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = yaffs_oh_to_size(oh); ++ is_shrink = oh->is_shrink; ++ equiv_id = oh->equiv_id; ++ } else { ++ in->variant_type = tags.extra_obj_type; ++ parent = yaffs_find_or_create_by_number(dev, ++ tags.extra_parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = tags.extra_file_size; ++ is_shrink = tags.extra_is_shrink; ++ equiv_id = tags.extra_equiv_id; ++ in->lazy_loaded = 1; ++ } ++ in->dirty = 0; ++ ++ if (!parent) ++ alloc_failed = 1; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ if (parent && ++ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant.children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, another problem.... ++ * Trying to use a non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ yaffs_add_obj_to_dir(parent, in); ++ ++ is_unlinked = (parent == dev->del_dir) || ++ (parent == dev->unlinked_dir); ++ ++ if (is_shrink) ++ /* Mark the block */ ++ bi->has_shrink_hdr = 1; ++ ++ /* Note re hardlinks. ++ * Since we might scan a hardlink before its equivalent ++ * object is scanned we put them all in a list. ++ * After scanning is complete, we should have all the ++ * objects, so we run through this list and fix up all ++ * the chains. ++ */ ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ file_var = &in->variant.file_variant; ++ if (file_var->scanned_size < file_size) { ++ /* This covers the case where the file ++ * size is greater than the data held. ++ * This will happen if the file is ++ * resized to be larger than its ++ * current data extents. ++ */ ++ file_var->file_size = file_size; ++ file_var->scanned_size = file_size; ++ } ++ ++ if (file_var->shrink_size > file_size) ++ file_var->shrink_size = file_size; ++ ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ hl_var = &in->variant.hardlink_variant; ++ if (!is_unlinked) { ++ hl_var->equiv_id = equiv_id; ++ list_add(&in->hard_links, hard_list); ++ } ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ sl_var = &in->variant.symlink_variant; ++ if (oh) { ++ sl_var->alias = ++ yaffs_clone_str(oh->alias); ++ if (!sl_var->alias) ++ alloc_failed = 1; ++ } ++ break; ++ } ++ } ++ } ++ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++int yaffs2_scan_backwards(struct yaffs_dev *dev) ++{ ++ int blk; ++ int block_iter; ++ int start_iter; ++ int end_iter; ++ int n_to_scan = 0; ++ enum yaffs_block_state state; ++ int c; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ u8 *chunk_data; ++ int found_chunks; ++ int alloc_failed = 0; ++ struct yaffs_block_index *block_index = NULL; ++ int alt_block_index = 0; ++ int summary_available; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ block_index = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); ++ ++ if (!block_index) { ++ block_index = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); ++ alt_block_index = 1; ++ } ++ ++ if (!block_index) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards() could not allocate block index!" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, bi->block_state, seq_number); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ dev->blocks_in_checkpt++; ++ ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } else if (bi->block_state == ++ YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* Determine the highest sequence number */ ++ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && ++ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { ++ block_index[n_to_scan].seq = seq_number; ++ block_index[n_to_scan].block = blk; ++ n_to_scan++; ++ if (seq_number >= dev->seq_number) ++ dev->seq_number = seq_number; ++ } else { ++ /* TODO: Nasty sequence number! */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Block scanning block %d has bad sequence number %d", ++ blk, seq_number); ++ } ++ } ++ bi++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); ++ ++ cond_resched(); ++ ++ /* Sort the blocks by sequence number */ ++ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), ++ yaffs2_ybicmp, NULL); ++ ++ cond_resched(); ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); ++ ++ /* Now scan the blocks looking at the data. */ ++ start_iter = 0; ++ end_iter = n_to_scan - 1; ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); ++ ++ /* For each block.... backwards */ ++ for (block_iter = end_iter; ++ !alloc_failed && block_iter >= start_iter; ++ block_iter--) { ++ /* Cooperative multitasking! This loop can run for so ++ long that watchdog timers expire. */ ++ cond_resched(); ++ ++ /* get the block to scan in the correct order */ ++ blk = block_index[block_iter].block; ++ bi = yaffs_get_block_info(dev, blk); ++ ++ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); ++ ++ /* For each chunk in each block that needs scanning.... */ ++ found_chunks = 0; ++ if (summary_available) ++ c = dev->chunks_per_summary - 1; ++ else ++ c = dev->param.chunks_per_block - 1; ++ ++ for (/* c is already initialised */; ++ !alloc_failed && c >= 0 && ++ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); ++ c--) { ++ /* Scan backwards... ++ * Read the tags and decide what to do ++ */ ++ if (yaffs2_scan_chunk(dev, bi, blk, c, ++ &found_chunks, chunk_data, ++ &hard_list, summary_available) == ++ YAFFS_FAIL) ++ alloc_failed = 1; ++ } ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, then the block ++ * is fully allocated. */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ yaffs_block_became_dirty(dev, blk); ++ } ++ } ++ ++ yaffs_skip_rest_of_block(dev); ++ ++ if (alt_block_index) ++ vfree(block_index); ++ else ++ kfree(block_index); ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We have scanned all the objects, now it's time to add these ++ * hardlinks. ++ */ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.14.43/fs/yaffs2/yaffs_yaffs2.h +--- linux-3.14.43.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yaffs_yaffs2.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS2_H__ ++#define __YAFFS_YAFFS2_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi); ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); ++int yaffs2_checkpt_required(struct yaffs_dev *dev); ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); ++int yaffs2_checkpt_save(struct yaffs_dev *dev); ++int yaffs2_checkpt_restore(struct yaffs_dev *dev); ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); ++int yaffs2_scan_backwards(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.14.43.orig/fs/yaffs2/yportenv.h linux-3.14.43/fs/yaffs2/yportenv.h +--- linux-3.14.43.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.14.43/fs/yaffs2/yportenv.h 2015-06-15 21:20:48.000000000 +0200 +@@ -0,0 +1,85 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YPORTENV_H__ ++#define __YPORTENV_H__ ++ ++/* ++ * Define the MTD version in terms of Linux Kernel versions ++ * This allows yaffs to be used independantly of the kernel ++ * as well as with it. ++ */ ++ ++#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) ++ ++#ifdef YAFFS_OUT_OF_TREE ++#include "moduleconfig.h" ++#endif ++ ++#include ++#define MTD_VERSION_CODE LINUX_VERSION_CODE ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* These type wrappings are used to support Unicode names in WinCE. */ ++#define YCHAR char ++#define YUCHAR unsigned char ++#define _Y(x) x ++ ++#define YAFFS_LOSTNFOUND_NAME "lost+found" ++#define YAFFS_LOSTNFOUND_PREFIX "obj" ++ ++ ++#define YAFFS_ROOT_MODE 0755 ++#define YAFFS_LOSTNFOUND_MODE 0700 ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define Y_CURRENT_TIME CURRENT_TIME.tv_sec ++#define Y_TIME_CONVERT(x) (x).tv_sec ++#else ++#define Y_CURRENT_TIME CURRENT_TIME ++#define Y_TIME_CONVERT(x) (x) ++#endif ++ ++#define compile_time_assertion(assertion) \ ++ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) ++ ++ ++#define yaffs_printf(msk, fmt, ...) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) ++ ++#define yaffs_trace(msk, fmt, ...) do { \ ++ if (yaffs_trace_mask & (msk)) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ ++} while (0) ++ ++ ++#endif diff --git a/target/linux/patches/3.14.45/zlib-inflate.patch b/target/linux/patches/3.14.45/zlib-inflate.patch new file mode 100644 index 000000000..58e1f6d21 --- /dev/null +++ b/target/linux/patches/3.14.45/zlib-inflate.patch @@ -0,0 +1,12 @@ +diff -Nur linux-2.6.37.orig/lib/Kconfig linux-2.6.37/lib/Kconfig +--- linux-2.6.37.orig/lib/Kconfig 2011-01-05 01:50:19.000000000 +0100 ++++ linux-2.6.37/lib/Kconfig 2011-03-01 20:10:29.833370667 +0100 +@@ -95,7 +95,7 @@ + # compression support is select'ed if needed + # + config ZLIB_INFLATE +- tristate ++ boolean + + config ZLIB_DEFLATE + tristate diff --git a/target/linux/patches/3.18.14/bsd-compatibility.patch b/target/linux/patches/3.18.14/bsd-compatibility.patch deleted file mode 100644 index b954b658f..000000000 --- a/target/linux/patches/3.18.14/bsd-compatibility.patch +++ /dev/null @@ -1,2538 +0,0 @@ -diff -Nur linux-3.11.5.orig/scripts/Makefile.lib linux-3.11.5/scripts/Makefile.lib ---- linux-3.11.5.orig/scripts/Makefile.lib 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/Makefile.lib 2013-10-16 18:09:31.000000000 +0200 -@@ -281,7 +281,12 @@ - size_append = printf $(shell \ - dec_size=0; \ - for F in $1; do \ -- fsize=$$(stat -c "%s" $$F); \ -+ if stat -qs .>/dev/null 2>&1; then \ -+ statcmd='stat -f %z'; \ -+ else \ -+ statcmd='stat -c %s'; \ -+ fi; \ -+ fsize=$$($$statcmd $$F); \ - dec_size=$$(expr $$dec_size + $$fsize); \ - done; \ - printf "%08x\n" $$dec_size | \ -diff -Nur linux-3.11.5.orig/scripts/mod/mk_elfconfig.c linux-3.11.5/scripts/mod/mk_elfconfig.c ---- linux-3.11.5.orig/scripts/mod/mk_elfconfig.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/mk_elfconfig.c 2013-10-16 18:09:31.000000000 +0200 -@@ -1,7 +1,18 @@ - #include - #include - #include --#include -+ -+#define EI_NIDENT (16) -+#define ELFMAG "\177ELF" -+ -+#define SELFMAG 4 -+#define EI_CLASS 4 -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ - - int - main(int argc, char **argv) -diff -Nur linux-3.11.5.orig/scripts/mod/modpost.h linux-3.11.5/scripts/mod/modpost.h ---- linux-3.11.5.orig/scripts/mod/modpost.h 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/modpost.h 2013-10-16 18:09:31.000000000 +0200 -@@ -7,7 +7,2453 @@ - #include - #include - #include --#include -+ -+ -+/* This file defines standard ELF types, structures, and macros. -+ Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. -+ This file is part of the GNU C Library. -+ -+ The GNU C Library is free software; you can redistribute it and/or -+ modify it under the terms of the GNU Lesser General Public -+ License as published by the Free Software Foundation; either -+ version 2.1 of the License, or (at your option) any later version. -+ -+ The GNU C Library is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ Lesser General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public -+ License along with the GNU C Library; if not, write to the Free -+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ 02111-1307 USA. */ -+ -+#ifndef _ELF_H -+#define _ELF_H 1 -+ -+__BEGIN_DECLS -+ -+/* Standard ELF types. */ -+ -+#include -+ -+/* Type for a 16-bit quantity. */ -+typedef uint16_t Elf32_Half; -+typedef uint16_t Elf64_Half; -+ -+/* Types for signed and unsigned 32-bit quantities. */ -+typedef uint32_t Elf32_Word; -+typedef int32_t Elf32_Sword; -+typedef uint32_t Elf64_Word; -+typedef int32_t Elf64_Sword; -+ -+/* Types for signed and unsigned 64-bit quantities. */ -+typedef uint64_t Elf32_Xword; -+typedef int64_t Elf32_Sxword; -+typedef uint64_t Elf64_Xword; -+typedef int64_t Elf64_Sxword; -+ -+/* Type of addresses. */ -+typedef uint32_t Elf32_Addr; -+typedef uint64_t Elf64_Addr; -+ -+/* Type of file offsets. */ -+typedef uint32_t Elf32_Off; -+typedef uint64_t Elf64_Off; -+ -+/* Type for section indices, which are 16-bit quantities. */ -+typedef uint16_t Elf32_Section; -+typedef uint16_t Elf64_Section; -+ -+/* Type for version symbol information. */ -+typedef Elf32_Half Elf32_Versym; -+typedef Elf64_Half Elf64_Versym; -+ -+ -+/* The ELF file header. This appears at the start of every ELF file. */ -+ -+#define EI_NIDENT (16) -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf32_Half e_type; /* Object file type */ -+ Elf32_Half e_machine; /* Architecture */ -+ Elf32_Word e_version; /* Object file version */ -+ Elf32_Addr e_entry; /* Entry point virtual address */ -+ Elf32_Off e_phoff; /* Program header table file offset */ -+ Elf32_Off e_shoff; /* Section header table file offset */ -+ Elf32_Word e_flags; /* Processor-specific flags */ -+ Elf32_Half e_ehsize; /* ELF header size in bytes */ -+ Elf32_Half e_phentsize; /* Program header table entry size */ -+ Elf32_Half e_phnum; /* Program header table entry count */ -+ Elf32_Half e_shentsize; /* Section header table entry size */ -+ Elf32_Half e_shnum; /* Section header table entry count */ -+ Elf32_Half e_shstrndx; /* Section header string table index */ -+} Elf32_Ehdr; -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf64_Half e_type; /* Object file type */ -+ Elf64_Half e_machine; /* Architecture */ -+ Elf64_Word e_version; /* Object file version */ -+ Elf64_Addr e_entry; /* Entry point virtual address */ -+ Elf64_Off e_phoff; /* Program header table file offset */ -+ Elf64_Off e_shoff; /* Section header table file offset */ -+ Elf64_Word e_flags; /* Processor-specific flags */ -+ Elf64_Half e_ehsize; /* ELF header size in bytes */ -+ Elf64_Half e_phentsize; /* Program header table entry size */ -+ Elf64_Half e_phnum; /* Program header table entry count */ -+ Elf64_Half e_shentsize; /* Section header table entry size */ -+ Elf64_Half e_shnum; /* Section header table entry count */ -+ Elf64_Half e_shstrndx; /* Section header string table index */ -+} Elf64_Ehdr; -+ -+/* Fields in the e_ident array. The EI_* macros are indices into the -+ array. The macros under each EI_* macro are the values the byte -+ may have. */ -+ -+#define EI_MAG0 0 /* File identification byte 0 index */ -+#define ELFMAG0 0x7f /* Magic number byte 0 */ -+ -+#define EI_MAG1 1 /* File identification byte 1 index */ -+#define ELFMAG1 'E' /* Magic number byte 1 */ -+ -+#define EI_MAG2 2 /* File identification byte 2 index */ -+#define ELFMAG2 'L' /* Magic number byte 2 */ -+ -+#define EI_MAG3 3 /* File identification byte 3 index */ -+#define ELFMAG3 'F' /* Magic number byte 3 */ -+ -+/* Conglomeration of the identification bytes, for easy testing as a word. */ -+#define ELFMAG "\177ELF" -+#define SELFMAG 4 -+ -+#define EI_CLASS 4 /* File class byte index */ -+#define ELFCLASSNONE 0 /* Invalid class */ -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+#define ELFCLASSNUM 3 -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATANONE 0 /* Invalid data encoding */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ -+#define ELFDATANUM 3 -+ -+#define EI_VERSION 6 /* File version byte index */ -+ /* Value must be EV_CURRENT */ -+ -+#define EI_OSABI 7 /* OS ABI identification */ -+#define ELFOSABI_NONE 0 /* UNIX System V ABI */ -+#define ELFOSABI_SYSV 0 /* Alias. */ -+#define ELFOSABI_HPUX 1 /* HP-UX */ -+#define ELFOSABI_NETBSD 2 /* NetBSD. */ -+#define ELFOSABI_LINUX 3 /* Linux. */ -+#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ -+#define ELFOSABI_AIX 7 /* IBM AIX. */ -+#define ELFOSABI_IRIX 8 /* SGI Irix. */ -+#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ -+#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ -+#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ -+#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ -+#define ELFOSABI_ARM 97 /* ARM */ -+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ -+ -+#define EI_ABIVERSION 8 /* ABI version */ -+ -+#define EI_PAD 9 /* Byte index of padding bytes */ -+ -+/* Legal values for e_type (object file type). */ -+ -+#define ET_NONE 0 /* No file type */ -+#define ET_REL 1 /* Relocatable file */ -+#define ET_EXEC 2 /* Executable file */ -+#define ET_DYN 3 /* Shared object file */ -+#define ET_CORE 4 /* Core file */ -+#define ET_NUM 5 /* Number of defined types */ -+#define ET_LOOS 0xfe00 /* OS-specific range start */ -+#define ET_HIOS 0xfeff /* OS-specific range end */ -+#define ET_LOPROC 0xff00 /* Processor-specific range start */ -+#define ET_HIPROC 0xffff /* Processor-specific range end */ -+ -+/* Legal values for e_machine (architecture). */ -+ -+#define EM_NONE 0 /* No machine */ -+#define EM_M32 1 /* AT&T WE 32100 */ -+#define EM_SPARC 2 /* SUN SPARC */ -+#define EM_386 3 /* Intel 80386 */ -+#define EM_68K 4 /* Motorola m68k family */ -+#define EM_88K 5 /* Motorola m88k family */ -+#define EM_860 7 /* Intel 80860 */ -+#define EM_MIPS 8 /* MIPS R3000 big-endian */ -+#define EM_S370 9 /* IBM System/370 */ -+#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ -+ -+#define EM_PARISC 15 /* HPPA */ -+#define EM_VPP500 17 /* Fujitsu VPP500 */ -+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ -+#define EM_960 19 /* Intel 80960 */ -+#define EM_PPC 20 /* PowerPC */ -+#define EM_PPC64 21 /* PowerPC 64-bit */ -+#define EM_S390 22 /* IBM S390 */ -+ -+#define EM_V800 36 /* NEC V800 series */ -+#define EM_FR20 37 /* Fujitsu FR20 */ -+#define EM_RH32 38 /* TRW RH-32 */ -+#define EM_RCE 39 /* Motorola RCE */ -+#define EM_ARM 40 /* ARM */ -+#define EM_FAKE_ALPHA 41 /* Digital Alpha */ -+#define EM_SH 42 /* Hitachi SH */ -+#define EM_SPARCV9 43 /* SPARC v9 64-bit */ -+#define EM_TRICORE 44 /* Siemens Tricore */ -+#define EM_ARC 45 /* Argonaut RISC Core */ -+#define EM_H8_300 46 /* Hitachi H8/300 */ -+#define EM_H8_300H 47 /* Hitachi H8/300H */ -+#define EM_H8S 48 /* Hitachi H8S */ -+#define EM_H8_500 49 /* Hitachi H8/500 */ -+#define EM_IA_64 50 /* Intel Merced */ -+#define EM_MIPS_X 51 /* Stanford MIPS-X */ -+#define EM_COLDFIRE 52 /* Motorola Coldfire */ -+#define EM_68HC12 53 /* Motorola M68HC12 */ -+#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -+#define EM_PCP 55 /* Siemens PCP */ -+#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -+#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -+#define EM_STARCORE 58 /* Motorola Start*Core processor */ -+#define EM_ME16 59 /* Toyota ME16 processor */ -+#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -+#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -+#define EM_X86_64 62 /* AMD x86-64 architecture */ -+#define EM_PDSP 63 /* Sony DSP Processor */ -+ -+#define EM_FX66 66 /* Siemens FX66 microcontroller */ -+#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -+#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -+#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -+#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -+#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -+#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -+#define EM_SVX 73 /* Silicon Graphics SVx */ -+#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ -+#define EM_VAX 75 /* Digital VAX */ -+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ -+#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ -+#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -+#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -+#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ -+#define EM_HUANY 81 /* Harvard University machine-independent object files */ -+#define EM_PRISM 82 /* SiTera Prism */ -+#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -+#define EM_FR30 84 /* Fujitsu FR30 */ -+#define EM_D10V 85 /* Mitsubishi D10V */ -+#define EM_D30V 86 /* Mitsubishi D30V */ -+#define EM_V850 87 /* NEC v850 */ -+#define EM_M32R 88 /* Mitsubishi M32R */ -+#define EM_MN10300 89 /* Matsushita MN10300 */ -+#define EM_MN10200 90 /* Matsushita MN10200 */ -+#define EM_PJ 91 /* picoJava */ -+#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ -+#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ -+#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ -+#define EM_NUM 95 -+ -+/* If it is necessary to assign new unofficial EM_* values, please -+ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the -+ chances of collision with official or non-GNU unofficial values. */ -+ -+#define EM_ALPHA 0x9026 -+ -+/* Legal values for e_version (version). */ -+ -+#define EV_NONE 0 /* Invalid ELF version */ -+#define EV_CURRENT 1 /* Current version */ -+#define EV_NUM 2 -+ -+/* Section header. */ -+ -+typedef struct -+{ -+ Elf32_Word sh_name; /* Section name (string tbl index) */ -+ Elf32_Word sh_type; /* Section type */ -+ Elf32_Word sh_flags; /* Section flags */ -+ Elf32_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf32_Off sh_offset; /* Section file offset */ -+ Elf32_Word sh_size; /* Section size in bytes */ -+ Elf32_Word sh_link; /* Link to another section */ -+ Elf32_Word sh_info; /* Additional section information */ -+ Elf32_Word sh_addralign; /* Section alignment */ -+ Elf32_Word sh_entsize; /* Entry size if section holds table */ -+} Elf32_Shdr; -+ -+typedef struct -+{ -+ Elf64_Word sh_name; /* Section name (string tbl index) */ -+ Elf64_Word sh_type; /* Section type */ -+ Elf64_Xword sh_flags; /* Section flags */ -+ Elf64_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf64_Off sh_offset; /* Section file offset */ -+ Elf64_Xword sh_size; /* Section size in bytes */ -+ Elf64_Word sh_link; /* Link to another section */ -+ Elf64_Word sh_info; /* Additional section information */ -+ Elf64_Xword sh_addralign; /* Section alignment */ -+ Elf64_Xword sh_entsize; /* Entry size if section holds table */ -+} Elf64_Shdr; -+ -+/* Special section indices. */ -+ -+#define SHN_UNDEF 0 /* Undefined section */ -+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ -+#define SHN_LOPROC 0xff00 /* Start of processor-specific */ -+#define SHN_HIPROC 0xff1f /* End of processor-specific */ -+#define SHN_LOOS 0xff20 /* Start of OS-specific */ -+#define SHN_HIOS 0xff3f /* End of OS-specific */ -+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ -+#define SHN_COMMON 0xfff2 /* Associated symbol is common */ -+#define SHN_XINDEX 0xffff /* Index is in extra table. */ -+#define SHN_HIRESERVE 0xffff /* End of reserved indices */ -+ -+/* Legal values for sh_type (section type). */ -+ -+#define SHT_NULL 0 /* Section header table entry unused */ -+#define SHT_PROGBITS 1 /* Program data */ -+#define SHT_SYMTAB 2 /* Symbol table */ -+#define SHT_STRTAB 3 /* String table */ -+#define SHT_RELA 4 /* Relocation entries with addends */ -+#define SHT_HASH 5 /* Symbol hash table */ -+#define SHT_DYNAMIC 6 /* Dynamic linking information */ -+#define SHT_NOTE 7 /* Notes */ -+#define SHT_NOBITS 8 /* Program space with no data (bss) */ -+#define SHT_REL 9 /* Relocation entries, no addends */ -+#define SHT_SHLIB 10 /* Reserved */ -+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ -+#define SHT_INIT_ARRAY 14 /* Array of constructors */ -+#define SHT_FINI_ARRAY 15 /* Array of destructors */ -+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ -+#define SHT_GROUP 17 /* Section group */ -+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ -+#define SHT_NUM 19 /* Number of defined types. */ -+#define SHT_LOOS 0x60000000 /* Start OS-specific */ -+#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ -+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ -+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ -+#define SHT_SUNW_move 0x6ffffffa -+#define SHT_SUNW_COMDAT 0x6ffffffb -+#define SHT_SUNW_syminfo 0x6ffffffc -+#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ -+#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ -+#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ -+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ -+#define SHT_HIOS 0x6fffffff /* End OS-specific type */ -+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define SHT_LOUSER 0x80000000 /* Start of application-specific */ -+#define SHT_HIUSER 0x8fffffff /* End of application-specific */ -+ -+/* Legal values for sh_flags (section flags). */ -+ -+#define SHF_WRITE (1 << 0) /* Writable */ -+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ -+#define SHF_EXECINSTR (1 << 2) /* Executable */ -+#define SHF_MERGE (1 << 4) /* Might be merged */ -+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ -+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ -+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ -+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling -+ required */ -+#define SHF_GROUP (1 << 9) /* Section is member of a group. */ -+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ -+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ -+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Section group handling. */ -+#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ -+ -+/* Symbol table entry. */ -+ -+typedef struct -+{ -+ Elf32_Word st_name; /* Symbol name (string tbl index) */ -+ Elf32_Addr st_value; /* Symbol value */ -+ Elf32_Word st_size; /* Symbol size */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf32_Section st_shndx; /* Section index */ -+} Elf32_Sym; -+ -+typedef struct -+{ -+ Elf64_Word st_name; /* Symbol name (string tbl index) */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf64_Section st_shndx; /* Section index */ -+ Elf64_Addr st_value; /* Symbol value */ -+ Elf64_Xword st_size; /* Symbol size */ -+} Elf64_Sym; -+ -+/* The syminfo section if available contains additional information about -+ every dynamic symbol. */ -+ -+typedef struct -+{ -+ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf32_Half si_flags; /* Per symbol flags */ -+} Elf32_Syminfo; -+ -+typedef struct -+{ -+ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf64_Half si_flags; /* Per symbol flags */ -+} Elf64_Syminfo; -+ -+/* Possible values for si_boundto. */ -+#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ -+#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ -+#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ -+ -+/* Possible bitmasks for si_flags. */ -+#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ -+#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ -+#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ -+#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy -+ loaded */ -+/* Syminfo version values. */ -+#define SYMINFO_NONE 0 -+#define SYMINFO_CURRENT 1 -+#define SYMINFO_NUM 2 -+ -+ -+/* How to extract and insert information held in the st_info field. */ -+ -+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -+#define ELF32_ST_TYPE(val) ((val) & 0xf) -+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -+ -+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ -+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) -+ -+/* Legal values for ST_BIND subfield of st_info (symbol binding). */ -+ -+#define STB_LOCAL 0 /* Local symbol */ -+#define STB_GLOBAL 1 /* Global symbol */ -+#define STB_WEAK 2 /* Weak symbol */ -+#define STB_NUM 3 /* Number of defined types. */ -+#define STB_LOOS 10 /* Start of OS-specific */ -+#define STB_HIOS 12 /* End of OS-specific */ -+#define STB_LOPROC 13 /* Start of processor-specific */ -+#define STB_HIPROC 15 /* End of processor-specific */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_NOTYPE 0 /* Symbol type is unspecified */ -+#define STT_OBJECT 1 /* Symbol is a data object */ -+#define STT_FUNC 2 /* Symbol is a code object */ -+#define STT_SECTION 3 /* Symbol associated with a section */ -+#define STT_FILE 4 /* Symbol's name is file name */ -+#define STT_COMMON 5 /* Symbol is a common data object */ -+#define STT_TLS 6 /* Symbol is thread-local data object*/ -+#define STT_NUM 7 /* Number of defined types. */ -+#define STT_LOOS 10 /* Start of OS-specific */ -+#define STT_HIOS 12 /* End of OS-specific */ -+#define STT_LOPROC 13 /* Start of processor-specific */ -+#define STT_HIPROC 15 /* End of processor-specific */ -+ -+ -+/* Symbol table indices are found in the hash buckets and chain table -+ of a symbol hash table section. This special index value indicates -+ the end of a chain, meaning no further symbols are found in that bucket. */ -+ -+#define STN_UNDEF 0 /* End of a chain. */ -+ -+ -+/* How to extract and insert information held in the st_other field. */ -+ -+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) -+ -+/* For ELF64 the definitions are the same. */ -+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) -+ -+/* Symbol visibility specification encoded in the st_other field. */ -+#define STV_DEFAULT 0 /* Default symbol visibility rules */ -+#define STV_INTERNAL 1 /* Processor specific hidden class */ -+#define STV_HIDDEN 2 /* Sym unavailable in other modules */ -+#define STV_PROTECTED 3 /* Not preemptible, not exported */ -+ -+ -+/* Relocation table entry without addend (in section of type SHT_REL). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+} Elf32_Rel; -+ -+/* I have seen two different definitions of the Elf64_Rel and -+ Elf64_Rela structures, so we'll leave them out until Novell (or -+ whoever) gets their act together. */ -+/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+} Elf64_Rel; -+ -+/* Relocation table entry with addend (in section of type SHT_RELA). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+ Elf32_Sword r_addend; /* Addend */ -+} Elf32_Rela; -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+ Elf64_Sxword r_addend; /* Addend */ -+} Elf64_Rela; -+ -+/* How to extract and insert information held in the r_info field. */ -+ -+#define ELF32_R_SYM(val) ((val) >> 8) -+#define ELF32_R_TYPE(val) ((val) & 0xff) -+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) -+ -+#define ELF64_R_SYM(i) ((i) >> 32) -+#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) -+ -+/* Program segment header. */ -+ -+typedef struct -+{ -+ Elf32_Word p_type; /* Segment type */ -+ Elf32_Off p_offset; /* Segment file offset */ -+ Elf32_Addr p_vaddr; /* Segment virtual address */ -+ Elf32_Addr p_paddr; /* Segment physical address */ -+ Elf32_Word p_filesz; /* Segment size in file */ -+ Elf32_Word p_memsz; /* Segment size in memory */ -+ Elf32_Word p_flags; /* Segment flags */ -+ Elf32_Word p_align; /* Segment alignment */ -+} Elf32_Phdr; -+ -+typedef struct -+{ -+ Elf64_Word p_type; /* Segment type */ -+ Elf64_Word p_flags; /* Segment flags */ -+ Elf64_Off p_offset; /* Segment file offset */ -+ Elf64_Addr p_vaddr; /* Segment virtual address */ -+ Elf64_Addr p_paddr; /* Segment physical address */ -+ Elf64_Xword p_filesz; /* Segment size in file */ -+ Elf64_Xword p_memsz; /* Segment size in memory */ -+ Elf64_Xword p_align; /* Segment alignment */ -+} Elf64_Phdr; -+ -+/* Legal values for p_type (segment type). */ -+ -+#define PT_NULL 0 /* Program header table entry unused */ -+#define PT_LOAD 1 /* Loadable program segment */ -+#define PT_DYNAMIC 2 /* Dynamic linking information */ -+#define PT_INTERP 3 /* Program interpreter */ -+#define PT_NOTE 4 /* Auxiliary information */ -+#define PT_SHLIB 5 /* Reserved */ -+#define PT_PHDR 6 /* Entry for header table itself */ -+#define PT_TLS 7 /* Thread-local storage segment */ -+#define PT_NUM 8 /* Number of defined types */ -+#define PT_LOOS 0x60000000 /* Start of OS-specific */ -+#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ -+#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ -+#define PT_LOSUNW 0x6ffffffa -+#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ -+#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ -+#define PT_HISUNW 0x6fffffff -+#define PT_HIOS 0x6fffffff /* End of OS-specific */ -+#define PT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define PT_HIPROC 0x7fffffff /* End of processor-specific */ -+ -+/* Legal values for p_flags (segment flags). */ -+ -+#define PF_X (1 << 0) /* Segment is executable */ -+#define PF_W (1 << 1) /* Segment is writable */ -+#define PF_R (1 << 2) /* Segment is readable */ -+#define PF_MASKOS 0x0ff00000 /* OS-specific */ -+#define PF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Legal values for note segment descriptor types for core files. */ -+ -+#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ -+#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ -+#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ -+#define NT_PRXREG 4 /* Contains copy of prxregset struct */ -+#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ -+#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ -+#define NT_AUXV 6 /* Contains copy of auxv array */ -+#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ -+#define NT_ASRS 8 /* Contains copy of asrset struct */ -+#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ -+#define NT_PSINFO 13 /* Contains copy of psinfo struct */ -+#define NT_PRCRED 14 /* Contains copy of prcred struct */ -+#define NT_UTSNAME 15 /* Contains copy of utsname struct */ -+#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ -+#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ -+#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ -+ -+/* Legal values for the note segment descriptor types for object files. */ -+ -+#define NT_VERSION 1 /* Contains a version string. */ -+ -+ -+/* Dynamic section entry. */ -+ -+typedef struct -+{ -+ Elf32_Sword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf32_Word d_val; /* Integer value */ -+ Elf32_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf32_Dyn; -+ -+typedef struct -+{ -+ Elf64_Sxword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf64_Xword d_val; /* Integer value */ -+ Elf64_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf64_Dyn; -+ -+/* Legal values for d_tag (dynamic entry type). */ -+ -+#define DT_NULL 0 /* Marks end of dynamic section */ -+#define DT_NEEDED 1 /* Name of needed library */ -+#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ -+#define DT_PLTGOT 3 /* Processor defined value */ -+#define DT_HASH 4 /* Address of symbol hash table */ -+#define DT_STRTAB 5 /* Address of string table */ -+#define DT_SYMTAB 6 /* Address of symbol table */ -+#define DT_RELA 7 /* Address of Rela relocs */ -+#define DT_RELASZ 8 /* Total size of Rela relocs */ -+#define DT_RELAENT 9 /* Size of one Rela reloc */ -+#define DT_STRSZ 10 /* Size of string table */ -+#define DT_SYMENT 11 /* Size of one symbol table entry */ -+#define DT_INIT 12 /* Address of init function */ -+#define DT_FINI 13 /* Address of termination function */ -+#define DT_SONAME 14 /* Name of shared object */ -+#define DT_RPATH 15 /* Library search path (deprecated) */ -+#define DT_SYMBOLIC 16 /* Start symbol search here */ -+#define DT_REL 17 /* Address of Rel relocs */ -+#define DT_RELSZ 18 /* Total size of Rel relocs */ -+#define DT_RELENT 19 /* Size of one Rel reloc */ -+#define DT_PLTREL 20 /* Type of reloc in PLT */ -+#define DT_DEBUG 21 /* For debugging; unspecified */ -+#define DT_TEXTREL 22 /* Reloc might modify .text */ -+#define DT_JMPREL 23 /* Address of PLT relocs */ -+#define DT_BIND_NOW 24 /* Process relocations of object */ -+#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ -+#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ -+#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ -+#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ -+#define DT_RUNPATH 29 /* Library search path */ -+#define DT_FLAGS 30 /* Flags for the object being loaded */ -+#define DT_ENCODING 32 /* Start of encoded range */ -+#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ -+#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ -+#define DT_NUM 34 /* Number used */ -+#define DT_LOOS 0x6000000d /* Start of OS-specific */ -+#define DT_HIOS 0x6ffff000 /* End of OS-specific */ -+#define DT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define DT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ -+ -+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the -+ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's -+ approach. */ -+#define DT_VALRNGLO 0x6ffffd00 -+#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ -+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ -+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ -+#define DT_CHECKSUM 0x6ffffdf8 -+#define DT_PLTPADSZ 0x6ffffdf9 -+#define DT_MOVEENT 0x6ffffdfa -+#define DT_MOVESZ 0x6ffffdfb -+#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ -+#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting -+ the following DT_* entry. */ -+#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ -+#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ -+#define DT_VALRNGHI 0x6ffffdff -+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ -+#define DT_VALNUM 12 -+ -+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the -+ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. -+ -+ If any adjustment is made to the ELF object after it has been -+ built these entries will need to be adjusted. */ -+#define DT_ADDRRNGLO 0x6ffffe00 -+#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ -+#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ -+#define DT_CONFIG 0x6ffffefa /* Configuration information. */ -+#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ -+#define DT_AUDIT 0x6ffffefc /* Object auditing. */ -+#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ -+#define DT_MOVETAB 0x6ffffefe /* Move table. */ -+#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ -+#define DT_ADDRRNGHI 0x6ffffeff -+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ -+#define DT_ADDRNUM 10 -+ -+/* The versioning entry types. The next are defined as part of the -+ GNU extension. */ -+#define DT_VERSYM 0x6ffffff0 -+ -+#define DT_RELACOUNT 0x6ffffff9 -+#define DT_RELCOUNT 0x6ffffffa -+ -+/* These were chosen by Sun. */ -+#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ -+#define DT_VERDEF 0x6ffffffc /* Address of version definition -+ table */ -+#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ -+#define DT_VERNEED 0x6ffffffe /* Address of table with needed -+ versions */ -+#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ -+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ -+#define DT_VERSIONTAGNUM 16 -+ -+/* Sun added these machine-independent extensions in the "processor-specific" -+ range. Be compatible. */ -+#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ -+#define DT_FILTER 0x7fffffff /* Shared object to get values from */ -+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -+#define DT_EXTRANUM 3 -+ -+/* Values of `d_un.d_val' in the DT_FLAGS entry. */ -+#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ -+#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ -+#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ -+#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ -+#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ -+ -+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 -+ entry in the dynamic section. */ -+#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ -+#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ -+#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ -+#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ -+#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ -+#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ -+#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ -+#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ -+#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ -+#define DF_1_TRANS 0x00000200 -+#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ -+#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ -+#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ -+#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ -+#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ -+#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ -+#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ -+ -+/* Flags for the feature selection in DT_FEATURE_1. */ -+#define DTF_1_PARINIT 0x00000001 -+#define DTF_1_CONFEXP 0x00000002 -+ -+/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ -+#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ -+#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not -+ generally available. */ -+ -+/* Version definition sections. */ -+ -+typedef struct -+{ -+ Elf32_Half vd_version; /* Version revision */ -+ Elf32_Half vd_flags; /* Version information */ -+ Elf32_Half vd_ndx; /* Version Index */ -+ Elf32_Half vd_cnt; /* Number of associated aux entries */ -+ Elf32_Word vd_hash; /* Version name hash value */ -+ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf32_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf32_Verdef; -+ -+typedef struct -+{ -+ Elf64_Half vd_version; /* Version revision */ -+ Elf64_Half vd_flags; /* Version information */ -+ Elf64_Half vd_ndx; /* Version Index */ -+ Elf64_Half vd_cnt; /* Number of associated aux entries */ -+ Elf64_Word vd_hash; /* Version name hash value */ -+ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf64_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf64_Verdef; -+ -+ -+/* Legal values for vd_version (version revision). */ -+#define VER_DEF_NONE 0 /* No version */ -+#define VER_DEF_CURRENT 1 /* Current version */ -+#define VER_DEF_NUM 2 /* Given version number */ -+ -+/* Legal values for vd_flags (version information flags). */ -+#define VER_FLG_BASE 0x1 /* Version definition of file itself */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+/* Versym symbol index values. */ -+#define VER_NDX_LOCAL 0 /* Symbol is local. */ -+#define VER_NDX_GLOBAL 1 /* Symbol is global. */ -+#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ -+#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ -+ -+/* Auxialiary version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vda_name; /* Version or dependency names */ -+ Elf32_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf32_Verdaux; -+ -+typedef struct -+{ -+ Elf64_Word vda_name; /* Version or dependency names */ -+ Elf64_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf64_Verdaux; -+ -+ -+/* Version dependency section. */ -+ -+typedef struct -+{ -+ Elf32_Half vn_version; /* Version of structure */ -+ Elf32_Half vn_cnt; /* Number of associated aux entries */ -+ Elf32_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf32_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf32_Verneed; -+ -+typedef struct -+{ -+ Elf64_Half vn_version; /* Version of structure */ -+ Elf64_Half vn_cnt; /* Number of associated aux entries */ -+ Elf64_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf64_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf64_Verneed; -+ -+ -+/* Legal values for vn_version (version revision). */ -+#define VER_NEED_NONE 0 /* No version */ -+#define VER_NEED_CURRENT 1 /* Current version */ -+#define VER_NEED_NUM 2 /* Given version number */ -+ -+/* Auxiliary needed version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vna_hash; /* Hash value of dependency name */ -+ Elf32_Half vna_flags; /* Dependency specific information */ -+ Elf32_Half vna_other; /* Unused */ -+ Elf32_Word vna_name; /* Dependency name string offset */ -+ Elf32_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf32_Vernaux; -+ -+typedef struct -+{ -+ Elf64_Word vna_hash; /* Hash value of dependency name */ -+ Elf64_Half vna_flags; /* Dependency specific information */ -+ Elf64_Half vna_other; /* Unused */ -+ Elf64_Word vna_name; /* Dependency name string offset */ -+ Elf64_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf64_Vernaux; -+ -+ -+/* Legal values for vna_flags. */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+ -+/* Auxiliary vector. */ -+ -+/* This vector is normally only used by the program interpreter. The -+ usual definition in an ABI supplement uses the name auxv_t. The -+ vector is not usually defined in a standard file, but it -+ can't hurt. We rename it to avoid conflicts. The sizes of these -+ types are an arrangement between the exec server and the program -+ interpreter, so we don't fully specify them here. */ -+ -+typedef struct -+{ -+ int a_type; /* Entry type */ -+ union -+ { -+ long int a_val; /* Integer value */ -+ void *a_ptr; /* Pointer value */ -+ void (*a_fcn) (void); /* Function pointer value */ -+ } a_un; -+} Elf32_auxv_t; -+ -+typedef struct -+{ -+ long int a_type; /* Entry type */ -+ union -+ { -+ long int a_val; /* Integer value */ -+ void *a_ptr; /* Pointer value */ -+ void (*a_fcn) (void); /* Function pointer value */ -+ } a_un; -+} Elf64_auxv_t; -+ -+/* Legal values for a_type (entry type). */ -+ -+#define AT_NULL 0 /* End of vector */ -+#define AT_IGNORE 1 /* Entry should be ignored */ -+#define AT_EXECFD 2 /* File descriptor of program */ -+#define AT_PHDR 3 /* Program headers for program */ -+#define AT_PHENT 4 /* Size of program header entry */ -+#define AT_PHNUM 5 /* Number of program headers */ -+#define AT_PAGESZ 6 /* System page size */ -+#define AT_BASE 7 /* Base address of interpreter */ -+#define AT_FLAGS 8 /* Flags */ -+#define AT_ENTRY 9 /* Entry point of program */ -+#define AT_NOTELF 10 /* Program is not ELF */ -+#define AT_UID 11 /* Real uid */ -+#define AT_EUID 12 /* Effective uid */ -+#define AT_GID 13 /* Real gid */ -+#define AT_EGID 14 /* Effective gid */ -+#define AT_CLKTCK 17 /* Frequency of times() */ -+ -+/* Some more special a_type values describing the hardware. */ -+#define AT_PLATFORM 15 /* String identifying platform. */ -+#define AT_HWCAP 16 /* Machine dependent hints about -+ processor capabilities. */ -+ -+/* This entry gives some information about the FPU initialization -+ performed by the kernel. */ -+#define AT_FPUCW 18 /* Used FPU control word. */ -+ -+/* Cache block sizes. */ -+#define AT_DCACHEBSIZE 19 /* Data cache block size. */ -+#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ -+#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ -+ -+/* A special ignored value for PPC, used by the kernel to control the -+ interpretation of the AUXV. Must be > 16. */ -+#define AT_IGNOREPPC 22 /* Entry should be ignored. */ -+ -+#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ -+ -+/* Pointer to the global system page used for system calls and other -+ nice things. */ -+#define AT_SYSINFO 32 -+#define AT_SYSINFO_EHDR 33 -+ -+ -+/* Note section contents. Each entry in the note section begins with -+ a header of a fixed form. */ -+ -+typedef struct -+{ -+ Elf32_Word n_namesz; /* Length of the note's name. */ -+ Elf32_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf32_Word n_type; /* Type of the note. */ -+} Elf32_Nhdr; -+ -+typedef struct -+{ -+ Elf64_Word n_namesz; /* Length of the note's name. */ -+ Elf64_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf64_Word n_type; /* Type of the note. */ -+} Elf64_Nhdr; -+ -+/* Known names of notes. */ -+ -+/* Solaris entries in the note section have this name. */ -+#define ELF_NOTE_SOLARIS "SUNW Solaris" -+ -+/* Note entries for GNU systems have this name. */ -+#define ELF_NOTE_GNU "GNU" -+ -+ -+/* Defined types of notes for Solaris. */ -+ -+/* Value of descriptor (one word) is desired pagesize for the binary. */ -+#define ELF_NOTE_PAGESIZE_HINT 1 -+ -+ -+/* Defined note types for GNU systems. */ -+ -+/* ABI information. The descriptor consists of words: -+ word 0: OS descriptor -+ word 1: major version of the ABI -+ word 2: minor version of the ABI -+ word 3: subminor version of the ABI -+*/ -+#define ELF_NOTE_ABI 1 -+ -+/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI -+ note section entry. */ -+#define ELF_NOTE_OS_LINUX 0 -+#define ELF_NOTE_OS_GNU 1 -+#define ELF_NOTE_OS_SOLARIS2 2 -+#define ELF_NOTE_OS_FREEBSD 3 -+ -+ -+/* Move records. */ -+typedef struct -+{ -+ Elf32_Xword m_value; /* Symbol value. */ -+ Elf32_Word m_info; /* Size and index. */ -+ Elf32_Word m_poffset; /* Symbol offset. */ -+ Elf32_Half m_repeat; /* Repeat count. */ -+ Elf32_Half m_stride; /* Stride info. */ -+} Elf32_Move; -+ -+typedef struct -+{ -+ Elf64_Xword m_value; /* Symbol value. */ -+ Elf64_Xword m_info; /* Size and index. */ -+ Elf64_Xword m_poffset; /* Symbol offset. */ -+ Elf64_Half m_repeat; /* Repeat count. */ -+ Elf64_Half m_stride; /* Stride info. */ -+} Elf64_Move; -+ -+/* Macro to construct move records. */ -+#define ELF32_M_SYM(info) ((info) >> 8) -+#define ELF32_M_SIZE(info) ((unsigned char) (info)) -+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) -+ -+#define ELF64_M_SYM(info) ELF32_M_SYM (info) -+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) -+ -+ -+/* Motorola 68k specific definitions. */ -+ -+/* Values for Elf32_Ehdr.e_flags. */ -+#define EF_CPU32 0x00810000 -+ -+/* m68k relocs. */ -+ -+#define R_68K_NONE 0 /* No reloc */ -+#define R_68K_32 1 /* Direct 32 bit */ -+#define R_68K_16 2 /* Direct 16 bit */ -+#define R_68K_8 3 /* Direct 8 bit */ -+#define R_68K_PC32 4 /* PC relative 32 bit */ -+#define R_68K_PC16 5 /* PC relative 16 bit */ -+#define R_68K_PC8 6 /* PC relative 8 bit */ -+#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ -+#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ -+#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ -+#define R_68K_GOT32O 10 /* 32 bit GOT offset */ -+#define R_68K_GOT16O 11 /* 16 bit GOT offset */ -+#define R_68K_GOT8O 12 /* 8 bit GOT offset */ -+#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ -+#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ -+#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ -+#define R_68K_PLT32O 16 /* 32 bit PLT offset */ -+#define R_68K_PLT16O 17 /* 16 bit PLT offset */ -+#define R_68K_PLT8O 18 /* 8 bit PLT offset */ -+#define R_68K_COPY 19 /* Copy symbol at runtime */ -+#define R_68K_GLOB_DAT 20 /* Create GOT entry */ -+#define R_68K_JMP_SLOT 21 /* Create PLT entry */ -+#define R_68K_RELATIVE 22 /* Adjust by program base */ -+/* Keep this the last entry. */ -+#define R_68K_NUM 23 -+ -+/* Intel 80386 specific definitions. */ -+ -+/* i386 relocs. */ -+ -+#define R_386_NONE 0 /* No reloc */ -+#define R_386_32 1 /* Direct 32 bit */ -+#define R_386_PC32 2 /* PC relative 32 bit */ -+#define R_386_GOT32 3 /* 32 bit GOT entry */ -+#define R_386_PLT32 4 /* 32 bit PLT address */ -+#define R_386_COPY 5 /* Copy symbol at runtime */ -+#define R_386_GLOB_DAT 6 /* Create GOT entry */ -+#define R_386_JMP_SLOT 7 /* Create PLT entry */ -+#define R_386_RELATIVE 8 /* Adjust by program base */ -+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ -+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ -+#define R_386_32PLT 11 -+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ -+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS -+ block offset */ -+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block -+ offset */ -+#define R_386_TLS_LE 17 /* Offset relative to static TLS -+ block */ -+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of -+ general dynamic thread local data */ -+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of -+ local dynamic thread local data -+ in LE code */ -+#define R_386_16 20 -+#define R_386_PC16 21 -+#define R_386_8 22 -+#define R_386_PC8 23 -+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic -+ thread local data */ -+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ -+#define R_386_TLS_GD_CALL 26 /* Relocation for call to -+ __tls_get_addr() */ -+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ -+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic -+ thread local data in LE code */ -+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ -+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to -+ __tls_get_addr() in LDM code */ -+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ -+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ -+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS -+ block offset */ -+#define R_386_TLS_LE_32 34 /* Negated offset relative to static -+ TLS block */ -+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ -+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ -+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ -+/* Keep this the last entry. */ -+#define R_386_NUM 38 -+ -+/* SUN SPARC specific definitions. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_REGISTER 13 /* Global register reserved to app. */ -+ -+/* Values for Elf64_Ehdr.e_flags. */ -+ -+#define EF_SPARCV9_MM 3 -+#define EF_SPARCV9_TSO 0 -+#define EF_SPARCV9_PSO 1 -+#define EF_SPARCV9_RMO 2 -+#define EF_SPARC_LEDATA 0x800000 /* little endian data */ -+#define EF_SPARC_EXT_MASK 0xFFFF00 -+#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ -+#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ -+#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ -+#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ -+ -+/* SPARC relocs. */ -+ -+#define R_SPARC_NONE 0 /* No reloc */ -+#define R_SPARC_8 1 /* Direct 8 bit */ -+#define R_SPARC_16 2 /* Direct 16 bit */ -+#define R_SPARC_32 3 /* Direct 32 bit */ -+#define R_SPARC_DISP8 4 /* PC relative 8 bit */ -+#define R_SPARC_DISP16 5 /* PC relative 16 bit */ -+#define R_SPARC_DISP32 6 /* PC relative 32 bit */ -+#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ -+#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ -+#define R_SPARC_HI22 9 /* High 22 bit */ -+#define R_SPARC_22 10 /* Direct 22 bit */ -+#define R_SPARC_13 11 /* Direct 13 bit */ -+#define R_SPARC_LO10 12 /* Truncated 10 bit */ -+#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ -+#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ -+#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ -+#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ -+#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ -+#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ -+#define R_SPARC_COPY 19 /* Copy symbol at runtime */ -+#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ -+#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ -+#define R_SPARC_RELATIVE 22 /* Adjust by program base */ -+#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ -+ -+/* Additional Sparc64 relocs. */ -+ -+#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ -+#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ -+#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ -+#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ -+#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ -+#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ -+#define R_SPARC_10 30 /* Direct 10 bit */ -+#define R_SPARC_11 31 /* Direct 11 bit */ -+#define R_SPARC_64 32 /* Direct 64 bit */ -+#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ -+#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ -+#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ -+#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ -+#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ -+#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ -+#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ -+#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ -+#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ -+#define R_SPARC_7 43 /* Direct 7 bit */ -+#define R_SPARC_5 44 /* Direct 5 bit */ -+#define R_SPARC_6 45 /* Direct 6 bit */ -+#define R_SPARC_DISP64 46 /* PC relative 64 bit */ -+#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ -+#define R_SPARC_HIX22 48 /* High 22 bit complemented */ -+#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ -+#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ -+#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ -+#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ -+#define R_SPARC_REGISTER 53 /* Global register usage */ -+#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ -+#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ -+#define R_SPARC_TLS_GD_HI22 56 -+#define R_SPARC_TLS_GD_LO10 57 -+#define R_SPARC_TLS_GD_ADD 58 -+#define R_SPARC_TLS_GD_CALL 59 -+#define R_SPARC_TLS_LDM_HI22 60 -+#define R_SPARC_TLS_LDM_LO10 61 -+#define R_SPARC_TLS_LDM_ADD 62 -+#define R_SPARC_TLS_LDM_CALL 63 -+#define R_SPARC_TLS_LDO_HIX22 64 -+#define R_SPARC_TLS_LDO_LOX10 65 -+#define R_SPARC_TLS_LDO_ADD 66 -+#define R_SPARC_TLS_IE_HI22 67 -+#define R_SPARC_TLS_IE_LO10 68 -+#define R_SPARC_TLS_IE_LD 69 -+#define R_SPARC_TLS_IE_LDX 70 -+#define R_SPARC_TLS_IE_ADD 71 -+#define R_SPARC_TLS_LE_HIX22 72 -+#define R_SPARC_TLS_LE_LOX10 73 -+#define R_SPARC_TLS_DTPMOD32 74 -+#define R_SPARC_TLS_DTPMOD64 75 -+#define R_SPARC_TLS_DTPOFF32 76 -+#define R_SPARC_TLS_DTPOFF64 77 -+#define R_SPARC_TLS_TPOFF32 78 -+#define R_SPARC_TLS_TPOFF64 79 -+/* Keep this the last entry. */ -+#define R_SPARC_NUM 80 -+ -+/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ -+ -+#define DT_SPARC_REGISTER 0x70000001 -+#define DT_SPARC_NUM 2 -+ -+/* Bits present in AT_HWCAP, primarily for Sparc32. */ -+ -+#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ -+#define HWCAP_SPARC_STBAR 2 -+#define HWCAP_SPARC_SWAP 4 -+#define HWCAP_SPARC_MULDIV 8 -+#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ -+#define HWCAP_SPARC_ULTRA3 32 -+ -+/* MIPS R3000 specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ -+#define EF_MIPS_PIC 2 /* Contains PIC code */ -+#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ -+#define EF_MIPS_XGOT 8 -+#define EF_MIPS_64BIT_WHIRL 16 -+#define EF_MIPS_ABI2 32 -+#define EF_MIPS_ABI_ON32 64 -+#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ -+ -+/* Legal values for MIPS architecture level. */ -+ -+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* The following are non-official names and should not be used. */ -+ -+#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* Special section indices. */ -+ -+#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ -+#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ -+#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ -+#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ -+#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ -+#define SHT_MIPS_MSYM 0x70000001 -+#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ -+#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ -+#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ -+#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ -+#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ -+#define SHT_MIPS_PACKAGE 0x70000007 -+#define SHT_MIPS_PACKSYM 0x70000008 -+#define SHT_MIPS_RELD 0x70000009 -+#define SHT_MIPS_IFACE 0x7000000b -+#define SHT_MIPS_CONTENT 0x7000000c -+#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ -+#define SHT_MIPS_SHDR 0x70000010 -+#define SHT_MIPS_FDESC 0x70000011 -+#define SHT_MIPS_EXTSYM 0x70000012 -+#define SHT_MIPS_DENSE 0x70000013 -+#define SHT_MIPS_PDESC 0x70000014 -+#define SHT_MIPS_LOCSYM 0x70000015 -+#define SHT_MIPS_AUXSYM 0x70000016 -+#define SHT_MIPS_OPTSYM 0x70000017 -+#define SHT_MIPS_LOCSTR 0x70000018 -+#define SHT_MIPS_LINE 0x70000019 -+#define SHT_MIPS_RFDESC 0x7000001a -+#define SHT_MIPS_DELTASYM 0x7000001b -+#define SHT_MIPS_DELTAINST 0x7000001c -+#define SHT_MIPS_DELTACLASS 0x7000001d -+#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ -+#define SHT_MIPS_DELTADECL 0x7000001f -+#define SHT_MIPS_SYMBOL_LIB 0x70000020 -+#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ -+#define SHT_MIPS_TRANSLATE 0x70000022 -+#define SHT_MIPS_PIXIE 0x70000023 -+#define SHT_MIPS_XLATE 0x70000024 -+#define SHT_MIPS_XLATE_DEBUG 0x70000025 -+#define SHT_MIPS_WHIRL 0x70000026 -+#define SHT_MIPS_EH_REGION 0x70000027 -+#define SHT_MIPS_XLATE_OLD 0x70000028 -+#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ -+#define SHF_MIPS_MERGE 0x20000000 -+#define SHF_MIPS_ADDR 0x40000000 -+#define SHF_MIPS_STRINGS 0x80000000 -+#define SHF_MIPS_NOSTRIP 0x08000000 -+#define SHF_MIPS_LOCAL 0x04000000 -+#define SHF_MIPS_NAMES 0x02000000 -+#define SHF_MIPS_NODUPE 0x01000000 -+ -+ -+/* Symbol tables. */ -+ -+/* MIPS specific values for `st_other'. */ -+#define STO_MIPS_DEFAULT 0x0 -+#define STO_MIPS_INTERNAL 0x1 -+#define STO_MIPS_HIDDEN 0x2 -+#define STO_MIPS_PROTECTED 0x3 -+#define STO_MIPS_SC_ALIGN_UNUSED 0xff -+ -+/* MIPS specific values for `st_info'. */ -+#define STB_MIPS_SPLIT_COMMON 13 -+ -+/* Entries found in sections of type SHT_MIPS_GPTAB. */ -+ -+typedef union -+{ -+ struct -+ { -+ Elf32_Word gt_current_g_value; /* -G value used for compilation */ -+ Elf32_Word gt_unused; /* Not used */ -+ } gt_header; /* First entry in section */ -+ struct -+ { -+ Elf32_Word gt_g_value; /* If this value were used for -G */ -+ Elf32_Word gt_bytes; /* This many bytes would be used */ -+ } gt_entry; /* Subsequent entries in section */ -+} Elf32_gptab; -+ -+/* Entry found in sections of type SHT_MIPS_REGINFO. */ -+ -+typedef struct -+{ -+ Elf32_Word ri_gprmask; /* General registers used */ -+ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ -+ Elf32_Sword ri_gp_value; /* $gp register value */ -+} Elf32_RegInfo; -+ -+/* Entries found in sections of type SHT_MIPS_OPTIONS. */ -+ -+typedef struct -+{ -+ unsigned char kind; /* Determines interpretation of the -+ variable part of descriptor. */ -+ unsigned char size; /* Size of descriptor, including header. */ -+ Elf32_Section section; /* Section header index of section affected, -+ 0 for global options. */ -+ Elf32_Word info; /* Kind-specific information. */ -+} Elf_Options; -+ -+/* Values for `kind' field in Elf_Options. */ -+ -+#define ODK_NULL 0 /* Undefined. */ -+#define ODK_REGINFO 1 /* Register usage information. */ -+#define ODK_EXCEPTIONS 2 /* Exception processing options. */ -+#define ODK_PAD 3 /* Section padding options. */ -+#define ODK_HWPATCH 4 /* Hardware workarounds performed */ -+#define ODK_FILL 5 /* record the fill value used by the linker. */ -+#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ -+#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ -+#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ -+ -+/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ -+ -+#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ -+#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ -+#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ -+#define OEX_SMM 0x20000 /* Force sequential memory mode? */ -+#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ -+#define OEX_PRECISEFP OEX_FPDBUG -+#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ -+ -+#define OEX_FPU_INVAL 0x10 -+#define OEX_FPU_DIV0 0x08 -+#define OEX_FPU_OFLO 0x04 -+#define OEX_FPU_UFLO 0x02 -+#define OEX_FPU_INEX 0x01 -+ -+/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ -+ -+#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ -+#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ -+#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ -+#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ -+ -+#define OPAD_PREFIX 0x1 -+#define OPAD_POSTFIX 0x2 -+#define OPAD_SYMBOL 0x4 -+ -+/* Entry found in `.options' section. */ -+ -+typedef struct -+{ -+ Elf32_Word hwp_flags1; /* Extra flags. */ -+ Elf32_Word hwp_flags2; /* Extra flags. */ -+} Elf_Options_Hw; -+ -+/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ -+ -+#define OHWA0_R4KEOP_CHECKED 0x00000001 -+#define OHWA1_R4KEOP_CLEAN 0x00000002 -+ -+/* MIPS relocs. */ -+ -+#define R_MIPS_NONE 0 /* No reloc */ -+#define R_MIPS_16 1 /* Direct 16 bit */ -+#define R_MIPS_32 2 /* Direct 32 bit */ -+#define R_MIPS_REL32 3 /* PC relative 32 bit */ -+#define R_MIPS_26 4 /* Direct 26 bit shifted */ -+#define R_MIPS_HI16 5 /* High 16 bit */ -+#define R_MIPS_LO16 6 /* Low 16 bit */ -+#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ -+#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ -+#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ -+#define R_MIPS_PC16 10 /* PC relative 16 bit */ -+#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ -+#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ -+ -+#define R_MIPS_SHIFT5 16 -+#define R_MIPS_SHIFT6 17 -+#define R_MIPS_64 18 -+#define R_MIPS_GOT_DISP 19 -+#define R_MIPS_GOT_PAGE 20 -+#define R_MIPS_GOT_OFST 21 -+#define R_MIPS_GOT_HI16 22 -+#define R_MIPS_GOT_LO16 23 -+#define R_MIPS_SUB 24 -+#define R_MIPS_INSERT_A 25 -+#define R_MIPS_INSERT_B 26 -+#define R_MIPS_DELETE 27 -+#define R_MIPS_HIGHER 28 -+#define R_MIPS_HIGHEST 29 -+#define R_MIPS_CALL_HI16 30 -+#define R_MIPS_CALL_LO16 31 -+#define R_MIPS_SCN_DISP 32 -+#define R_MIPS_REL16 33 -+#define R_MIPS_ADD_IMMEDIATE 34 -+#define R_MIPS_PJUMP 35 -+#define R_MIPS_RELGOT 36 -+#define R_MIPS_JALR 37 -+/* Keep this the last entry. */ -+#define R_MIPS_NUM 38 -+ -+/* Legal values for p_type field of Elf32_Phdr. */ -+ -+#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ -+#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ -+#define PT_MIPS_OPTIONS 0x70000002 -+ -+/* Special program header types. */ -+ -+#define PF_MIPS_LOCAL 0x10000000 -+ -+/* Legal values for d_tag field of Elf32_Dyn. */ -+ -+#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ -+#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ -+#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ -+#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ -+#define DT_MIPS_FLAGS 0x70000005 /* Flags */ -+#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ -+#define DT_MIPS_MSYM 0x70000007 -+#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ -+#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ -+#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ -+#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ -+#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ -+#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ -+#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ -+#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ -+#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ -+#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ -+#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ -+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in -+ DT_MIPS_DELTA_CLASS. */ -+#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ -+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in -+ DT_MIPS_DELTA_INSTANCE. */ -+#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ -+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in -+ DT_MIPS_DELTA_RELOC. */ -+#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta -+ relocations refer to. */ -+#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in -+ DT_MIPS_DELTA_SYM. */ -+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the -+ class declaration. */ -+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in -+ DT_MIPS_DELTA_CLASSSYM. */ -+#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ -+#define DT_MIPS_PIXIE_INIT 0x70000023 -+#define DT_MIPS_SYMBOL_LIB 0x70000024 -+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -+#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -+#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ -+#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ -+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -+#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ -+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve -+ function stored in GOT. */ -+#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added -+ by rld on dlopen() calls. */ -+#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ -+#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ -+#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ -+#define DT_MIPS_NUM 0x32 -+ -+/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ -+ -+#define RHF_NONE 0 /* No flags */ -+#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ -+#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ -+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ -+#define RHF_NO_MOVE (1 << 3) -+#define RHF_SGI_ONLY (1 << 4) -+#define RHF_GUARANTEE_INIT (1 << 5) -+#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -+#define RHF_GUARANTEE_START_INIT (1 << 7) -+#define RHF_PIXIE (1 << 8) -+#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -+#define RHF_REQUICKSTART (1 << 10) -+#define RHF_REQUICKSTARTED (1 << 11) -+#define RHF_CORD (1 << 12) -+#define RHF_NO_UNRES_UNDEF (1 << 13) -+#define RHF_RLD_ORDER_SAFE (1 << 14) -+ -+/* Entries found in sections of type SHT_MIPS_LIBLIST. */ -+ -+typedef struct -+{ -+ Elf32_Word l_name; /* Name (string table index) */ -+ Elf32_Word l_time_stamp; /* Timestamp */ -+ Elf32_Word l_checksum; /* Checksum */ -+ Elf32_Word l_version; /* Interface version */ -+ Elf32_Word l_flags; /* Flags */ -+} Elf32_Lib; -+ -+typedef struct -+{ -+ Elf64_Word l_name; /* Name (string table index) */ -+ Elf64_Word l_time_stamp; /* Timestamp */ -+ Elf64_Word l_checksum; /* Checksum */ -+ Elf64_Word l_version; /* Interface version */ -+ Elf64_Word l_flags; /* Flags */ -+} Elf64_Lib; -+ -+ -+/* Legal values for l_flags. */ -+ -+#define LL_NONE 0 -+#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ -+#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ -+#define LL_REQUIRE_MINOR (1 << 2) -+#define LL_EXPORTS (1 << 3) -+#define LL_DELAY_LOAD (1 << 4) -+#define LL_DELTA (1 << 5) -+ -+/* Entries found in sections of type SHT_MIPS_CONFLICT. */ -+ -+typedef Elf32_Addr Elf32_Conflict; -+ -+ -+/* HPPA specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ -+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ -+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ -+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ -+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch -+ prediction. */ -+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ -+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ -+ -+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ -+ -+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ -+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ -+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ -+ -+/* Additional section indeces. */ -+ -+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared -+ symbols in ANSI C. */ -+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ -+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ -+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ -+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ -+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ -+ -+#define STT_HP_OPAQUE (STT_LOOS + 0x1) -+#define STT_HP_STUB (STT_LOOS + 0x2) -+ -+/* HPPA relocs. */ -+ -+#define R_PARISC_NONE 0 /* No reloc. */ -+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ -+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ -+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ -+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ -+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ -+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ -+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ -+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ -+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ -+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ -+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ -+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ -+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ -+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ -+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ -+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ -+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ -+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ -+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ -+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ -+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ -+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ -+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ -+#define R_PARISC_FPTR64 64 /* 64 bits function address. */ -+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ -+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ -+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ -+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ -+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ -+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ -+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ -+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ -+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ -+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ -+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ -+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ -+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ -+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ -+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ -+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LORESERVE 128 -+#define R_PARISC_COPY 128 /* Copy relocation. */ -+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ -+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ -+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ -+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ -+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ -+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ -+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ -+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ -+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_HIRESERVE 255 -+ -+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PT_HP_TLS (PT_LOOS + 0x0) -+#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -+#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -+#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -+#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -+#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -+#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -+#define PT_HP_PARALLEL (PT_LOOS + 0x10) -+#define PT_HP_FASTBIND (PT_LOOS + 0x11) -+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -+#define PT_HP_STACK (PT_LOOS + 0x14) -+ -+#define PT_PARISC_ARCHEXT 0x70000000 -+#define PT_PARISC_UNWIND 0x70000001 -+ -+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PF_PARISC_SBP 0x08000000 -+ -+#define PF_HP_PAGE_SIZE 0x00100000 -+#define PF_HP_FAR_SHARED 0x00200000 -+#define PF_HP_NEAR_SHARED 0x00400000 -+#define PF_HP_CODE 0x01000000 -+#define PF_HP_MODIFY 0x02000000 -+#define PF_HP_LAZYSWAP 0x04000000 -+#define PF_HP_SBP 0x08000000 -+ -+ -+/* Alpha specific definitions. */ -+ -+/* Legal values for e_flags field of Elf64_Ehdr. */ -+ -+#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ -+#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ -+ -+/* Legal values for sh_type field of Elf64_Shdr. */ -+ -+/* These two are primerily concerned with ECOFF debugging info. */ -+#define SHT_ALPHA_DEBUG 0x70000001 -+#define SHT_ALPHA_REGINFO 0x70000002 -+ -+/* Legal values for sh_flags field of Elf64_Shdr. */ -+ -+#define SHF_ALPHA_GPREL 0x10000000 -+ -+/* Legal values for st_other field of Elf64_Sym. */ -+#define STO_ALPHA_NOPV 0x80 /* No PV required. */ -+#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ -+ -+/* Alpha relocs. */ -+ -+#define R_ALPHA_NONE 0 /* No reloc */ -+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ -+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ -+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ -+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ -+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ -+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ -+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ -+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ -+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ -+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ -+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ -+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ -+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ -+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ -+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ -+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ -+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ -+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ -+#define R_ALPHA_TLS_GD_HI 28 -+#define R_ALPHA_TLSGD 29 -+#define R_ALPHA_TLS_LDM 30 -+#define R_ALPHA_DTPMOD64 31 -+#define R_ALPHA_GOTDTPREL 32 -+#define R_ALPHA_DTPREL64 33 -+#define R_ALPHA_DTPRELHI 34 -+#define R_ALPHA_DTPRELLO 35 -+#define R_ALPHA_DTPREL16 36 -+#define R_ALPHA_GOTTPREL 37 -+#define R_ALPHA_TPREL64 38 -+#define R_ALPHA_TPRELHI 39 -+#define R_ALPHA_TPRELLO 40 -+#define R_ALPHA_TPREL16 41 -+/* Keep this the last entry. */ -+#define R_ALPHA_NUM 46 -+ -+/* Magic values of the LITUSE relocation addend. */ -+#define LITUSE_ALPHA_ADDR 0 -+#define LITUSE_ALPHA_BASE 1 -+#define LITUSE_ALPHA_BYTOFF 2 -+#define LITUSE_ALPHA_JSR 3 -+#define LITUSE_ALPHA_TLS_GD 4 -+#define LITUSE_ALPHA_TLS_LDM 5 -+ -+ -+/* PowerPC specific declarations */ -+ -+/* Values for Elf32/64_Ehdr.e_flags. */ -+#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ -+ -+/* Cygnus local bits below */ -+#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ -+#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib -+ flag */ -+ -+/* PowerPC relocations defined by the ABIs */ -+#define R_PPC_NONE 0 -+#define R_PPC_ADDR32 1 /* 32bit absolute address */ -+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -+#define R_PPC_ADDR16 3 /* 16bit absolute address */ -+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -+#define R_PPC_ADDR14_BRTAKEN 8 -+#define R_PPC_ADDR14_BRNTAKEN 9 -+#define R_PPC_REL24 10 /* PC relative 26 bit */ -+#define R_PPC_REL14 11 /* PC relative 16 bit */ -+#define R_PPC_REL14_BRTAKEN 12 -+#define R_PPC_REL14_BRNTAKEN 13 -+#define R_PPC_GOT16 14 -+#define R_PPC_GOT16_LO 15 -+#define R_PPC_GOT16_HI 16 -+#define R_PPC_GOT16_HA 17 -+#define R_PPC_PLTREL24 18 -+#define R_PPC_COPY 19 -+#define R_PPC_GLOB_DAT 20 -+#define R_PPC_JMP_SLOT 21 -+#define R_PPC_RELATIVE 22 -+#define R_PPC_LOCAL24PC 23 -+#define R_PPC_UADDR32 24 -+#define R_PPC_UADDR16 25 -+#define R_PPC_REL32 26 -+#define R_PPC_PLT32 27 -+#define R_PPC_PLTREL32 28 -+#define R_PPC_PLT16_LO 29 -+#define R_PPC_PLT16_HI 30 -+#define R_PPC_PLT16_HA 31 -+#define R_PPC_SDAREL16 32 -+#define R_PPC_SECTOFF 33 -+#define R_PPC_SECTOFF_LO 34 -+#define R_PPC_SECTOFF_HI 35 -+#define R_PPC_SECTOFF_HA 36 -+ -+/* PowerPC relocations defined for the TLS access ABI. */ -+#define R_PPC_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ -+#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ -+#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ -+#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ -+#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ -+#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ -+#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ -+#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ -+#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ -+ -+/* Keep this the last entry. */ -+#define R_PPC_NUM 95 -+ -+/* The remaining relocs are from the Embedded ELF ABI, and are not -+ in the SVR4 ELF ABI. */ -+#define R_PPC_EMB_NADDR32 101 -+#define R_PPC_EMB_NADDR16 102 -+#define R_PPC_EMB_NADDR16_LO 103 -+#define R_PPC_EMB_NADDR16_HI 104 -+#define R_PPC_EMB_NADDR16_HA 105 -+#define R_PPC_EMB_SDAI16 106 -+#define R_PPC_EMB_SDA2I16 107 -+#define R_PPC_EMB_SDA2REL 108 -+#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ -+#define R_PPC_EMB_MRKREF 110 -+#define R_PPC_EMB_RELSEC16 111 -+#define R_PPC_EMB_RELST_LO 112 -+#define R_PPC_EMB_RELST_HI 113 -+#define R_PPC_EMB_RELST_HA 114 -+#define R_PPC_EMB_BIT_FLD 115 -+#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ -+ -+/* Diab tool relocations. */ -+#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ -+#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ -+#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ -+#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ -+#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ -+#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ -+ -+/* This is a phony reloc to handle any old fashioned TOC16 references -+ that may still be in object files. */ -+#define R_PPC_TOC16 255 -+ -+ -+/* PowerPC64 relocations defined by the ABIs */ -+#define R_PPC64_NONE R_PPC_NONE -+#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ -+#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ -+#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ -+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ -+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ -+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ -+#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ -+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -+#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ -+#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ -+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -+#define R_PPC64_GOT16 R_PPC_GOT16 -+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA -+ -+#define R_PPC64_COPY R_PPC_COPY -+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -+#define R_PPC64_RELATIVE R_PPC_RELATIVE -+ -+#define R_PPC64_UADDR32 R_PPC_UADDR32 -+#define R_PPC64_UADDR16 R_PPC_UADDR16 -+#define R_PPC64_REL32 R_PPC_REL32 -+#define R_PPC64_PLT32 R_PPC_PLT32 -+#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA -+ -+#define R_PPC64_SECTOFF R_PPC_SECTOFF -+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -+#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ -+#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ -+#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ -+#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ -+#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ -+#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ -+#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ -+#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ -+#define R_PPC64_PLT64 45 /* doubleword64 L + A */ -+#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ -+#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ -+#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ -+#define R_PPC64_TOC 51 /* doubleword64 .TOC */ -+#define R_PPC64_PLTGOT16 52 /* half16* M + A */ -+#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ -+#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ -+#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ -+ -+#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ -+#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ -+#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ -+#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ -+#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ -+#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ -+#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ -+#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ -+#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ -+#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ -+#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ -+ -+/* PowerPC64 relocations defined for the TLS access ABI. */ -+#define R_PPC64_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ -+#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ -+#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ -+#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ -+#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ -+#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ -+#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ -+#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ -+#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ -+#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ -+#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ -+#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ -+#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ -+#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ -+#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ -+#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ -+#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ -+ -+/* Keep this the last entry. */ -+#define R_PPC64_NUM 107 -+ -+/* PowerPC64 specific values for the Dyn d_tag field. */ -+#define DT_PPC64_GLINK (DT_LOPROC + 0) -+#define DT_PPC64_NUM 1 -+ -+ -+/* ARM specific declarations */ -+ -+/* Processor specific flags for the ELF header e_flags field. */ -+#define EF_ARM_RELEXEC 0x01 -+#define EF_ARM_HASENTRY 0x02 -+#define EF_ARM_INTERWORK 0x04 -+#define EF_ARM_APCS_26 0x08 -+#define EF_ARM_APCS_FLOAT 0x10 -+#define EF_ARM_PIC 0x20 -+#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ -+#define EF_ARM_NEW_ABI 0x80 -+#define EF_ARM_OLD_ABI 0x100 -+ -+/* Other constants defined in the ARM ELF spec. version B-01. */ -+/* NB. These conflict with values defined above. */ -+#define EF_ARM_SYMSARESORTED 0x04 -+#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -+#define EF_ARM_MAPSYMSFIRST 0x10 -+#define EF_ARM_EABIMASK 0XFF000000 -+ -+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -+#define EF_ARM_EABI_UNKNOWN 0x00000000 -+#define EF_ARM_EABI_VER1 0x01000000 -+#define EF_ARM_EABI_VER2 0x02000000 -+ -+/* Additional symbol types for Thumb */ -+#define STT_ARM_TFUNC 0xd -+ -+/* ARM-specific values for sh_flags */ -+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ -+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined -+ in the input to a link step */ -+ -+/* ARM-specific program header flags */ -+#define PF_ARM_SB 0x10000000 /* Segment contains the location -+ addressed by the static base */ -+ -+/* ARM relocs. */ -+#define R_ARM_NONE 0 /* No reloc */ -+#define R_ARM_PC24 1 /* PC relative 26 bit branch */ -+#define R_ARM_ABS32 2 /* Direct 32 bit */ -+#define R_ARM_REL32 3 /* PC relative 32 bit */ -+#define R_ARM_PC13 4 -+#define R_ARM_ABS16 5 /* Direct 16 bit */ -+#define R_ARM_ABS12 6 /* Direct 12 bit */ -+#define R_ARM_THM_ABS5 7 -+#define R_ARM_ABS8 8 /* Direct 8 bit */ -+#define R_ARM_SBREL32 9 -+#define R_ARM_THM_PC22 10 -+#define R_ARM_THM_PC8 11 -+#define R_ARM_AMP_VCALL9 12 -+#define R_ARM_SWI24 13 -+#define R_ARM_THM_SWI8 14 -+#define R_ARM_XPC25 15 -+#define R_ARM_THM_XPC22 16 -+#define R_ARM_COPY 20 /* Copy symbol at runtime */ -+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ -+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ -+#define R_ARM_RELATIVE 23 /* Adjust by program base */ -+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ -+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ -+#define R_ARM_GOT32 26 /* 32 bit GOT entry */ -+#define R_ARM_PLT32 27 /* 32 bit PLT address */ -+#define R_ARM_ALU_PCREL_7_0 32 -+#define R_ARM_ALU_PCREL_15_8 33 -+#define R_ARM_ALU_PCREL_23_15 34 -+#define R_ARM_LDR_SBREL_11_0 35 -+#define R_ARM_ALU_SBREL_19_12 36 -+#define R_ARM_ALU_SBREL_27_20 37 -+#define R_ARM_GNU_VTENTRY 100 -+#define R_ARM_GNU_VTINHERIT 101 -+#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ -+#define R_ARM_THM_PC9 103 /* thumb conditional branch */ -+#define R_ARM_RXPC25 249 -+#define R_ARM_RSBREL32 250 -+#define R_ARM_THM_RPC22 251 -+#define R_ARM_RREL32 252 -+#define R_ARM_RABS22 253 -+#define R_ARM_RPC24 254 -+#define R_ARM_RBASE 255 -+/* Keep this the last entry. */ -+#define R_ARM_NUM 256 -+ -+/* IA-64 specific declarations. */ -+ -+/* Processor specific flags for the Ehdr e_flags field. */ -+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ -+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ -+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ -+ -+/* Processor specific values for the Phdr p_type field. */ -+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ -+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ -+ -+/* Processor specific flags for the Phdr p_flags field. */ -+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Shdr sh_type field. */ -+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ -+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ -+ -+/* Processor specific flags for the Shdr sh_flags field. */ -+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ -+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Dyn d_tag field. */ -+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -+#define DT_IA_64_NUM 1 -+ -+/* IA-64 relocations. */ -+#define R_IA64_NONE 0x00 /* none */ -+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ -+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ -+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ -+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ -+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ -+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ -+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ -+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ -+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ -+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ -+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ -+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ -+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ -+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ -+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ -+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ -+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ -+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ -+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ -+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ -+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ -+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ -+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ -+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ -+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ -+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ -+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ -+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ -+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ -+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ -+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ -+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ -+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ -+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ -+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ -+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ -+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ -+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ -+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ -+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ -+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ -+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ -+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ -+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ -+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ -+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ -+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ -+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ -+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ -+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ -+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ -+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ -+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ -+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ -+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ -+#define R_IA64_COPY 0x84 /* copy relocation */ -+#define R_IA64_SUB 0x85 /* Addend and symbol difference */ -+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ -+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ -+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ -+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ -+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ -+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ -+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ -+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ -+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ -+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ -+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ -+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ -+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ -+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ -+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ -+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ -+ -+/* SH specific declarations */ -+ -+/* SH relocs. */ -+#define R_SH_NONE 0 -+#define R_SH_DIR32 1 -+#define R_SH_REL32 2 -+#define R_SH_DIR8WPN 3 -+#define R_SH_IND12W 4 -+#define R_SH_DIR8WPL 5 -+#define R_SH_DIR8WPZ 6 -+#define R_SH_DIR8BP 7 -+#define R_SH_DIR8W 8 -+#define R_SH_DIR8L 9 -+#define R_SH_SWITCH16 25 -+#define R_SH_SWITCH32 26 -+#define R_SH_USES 27 -+#define R_SH_COUNT 28 -+#define R_SH_ALIGN 29 -+#define R_SH_CODE 30 -+#define R_SH_DATA 31 -+#define R_SH_LABEL 32 -+#define R_SH_SWITCH8 33 -+#define R_SH_GNU_VTINHERIT 34 -+#define R_SH_GNU_VTENTRY 35 -+#define R_SH_TLS_GD_32 144 -+#define R_SH_TLS_LD_32 145 -+#define R_SH_TLS_LDO_32 146 -+#define R_SH_TLS_IE_32 147 -+#define R_SH_TLS_LE_32 148 -+#define R_SH_TLS_DTPMOD32 149 -+#define R_SH_TLS_DTPOFF32 150 -+#define R_SH_TLS_TPOFF32 151 -+#define R_SH_GOT32 160 -+#define R_SH_PLT32 161 -+#define R_SH_COPY 162 -+#define R_SH_GLOB_DAT 163 -+#define R_SH_JMP_SLOT 164 -+#define R_SH_RELATIVE 165 -+#define R_SH_GOTOFF 166 -+#define R_SH_GOTPC 167 -+/* Keep this the last entry. */ -+#define R_SH_NUM 256 -+ -+/* Additional s390 relocs */ -+ -+#define R_390_NONE 0 /* No reloc. */ -+#define R_390_8 1 /* Direct 8 bit. */ -+#define R_390_12 2 /* Direct 12 bit. */ -+#define R_390_16 3 /* Direct 16 bit. */ -+#define R_390_32 4 /* Direct 32 bit. */ -+#define R_390_PC32 5 /* PC relative 32 bit. */ -+#define R_390_GOT12 6 /* 12 bit GOT offset. */ -+#define R_390_GOT32 7 /* 32 bit GOT offset. */ -+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ -+#define R_390_COPY 9 /* Copy symbol at runtime. */ -+#define R_390_GLOB_DAT 10 /* Create GOT entry. */ -+#define R_390_JMP_SLOT 11 /* Create PLT entry. */ -+#define R_390_RELATIVE 12 /* Adjust by program base. */ -+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ -+#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ -+#define R_390_GOT16 15 /* 16 bit GOT offset. */ -+#define R_390_PC16 16 /* PC relative 16 bit. */ -+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ -+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ -+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ -+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ -+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ -+#define R_390_64 22 /* Direct 64 bit. */ -+#define R_390_PC64 23 /* PC relative 64 bit. */ -+#define R_390_GOT64 24 /* 64 bit GOT offset. */ -+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ -+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ -+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ -+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ -+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ -+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ -+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ -+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ -+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ -+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ -+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ -+#define R_390_TLS_GDCALL 38 /* Tag for function call in general -+ dynamic TLS code. */ -+#define R_390_TLS_LDCALL 39 /* Tag for function call in local -+ dynamic TLS code. */ -+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ -+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ -+#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS -+ block. */ -+ -+/* Keep this the last entry. */ -+#define R_390_NUM 57 -+ -+/* CRIS relocations. */ -+#define R_CRIS_NONE 0 -+#define R_CRIS_8 1 -+#define R_CRIS_16 2 -+#define R_CRIS_32 3 -+#define R_CRIS_8_PCREL 4 -+#define R_CRIS_16_PCREL 5 -+#define R_CRIS_32_PCREL 6 -+#define R_CRIS_GNU_VTINHERIT 7 -+#define R_CRIS_GNU_VTENTRY 8 -+#define R_CRIS_COPY 9 -+#define R_CRIS_GLOB_DAT 10 -+#define R_CRIS_JUMP_SLOT 11 -+#define R_CRIS_RELATIVE 12 -+#define R_CRIS_16_GOT 13 -+#define R_CRIS_32_GOT 14 -+#define R_CRIS_16_GOTPLT 15 -+#define R_CRIS_32_GOTPLT 16 -+#define R_CRIS_32_GOTREL 17 -+#define R_CRIS_32_PLT_GOTREL 18 -+#define R_CRIS_32_PLT_PCREL 19 -+ -+#define R_CRIS_NUM 20 -+ -+/* AMD x86-64 relocations. */ -+#define R_X86_64_NONE 0 /* No reloc */ -+#define R_X86_64_64 1 /* Direct 64 bit */ -+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ -+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ -+#define R_X86_64_PLT32 4 /* 32 bit PLT address */ -+#define R_X86_64_COPY 5 /* Copy symbol at runtime */ -+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ -+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ -+#define R_X86_64_RELATIVE 8 /* Adjust by program base */ -+#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative -+ offset to GOT */ -+#define R_X86_64_32 10 /* Direct 32 bit zero extended */ -+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ -+#define R_X86_64_16 12 /* Direct 16 bit zero extended */ -+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ -+#define R_X86_64_8 14 /* Direct 8 bit sign extended */ -+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ -+#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ -+#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ -+#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ -+#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset -+ to two GOT entries for GD symbol */ -+#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset -+ to two GOT entries for LD symbol */ -+#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ -+#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset -+ to GOT entry for IE symbol */ -+#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ -+ -+#define R_X86_64_NUM 24 -+ -+__END_DECLS -+ -+#endif /* elf.h */ - - #include "elfconfig.h" - -@@ -185,3 +2631,4 @@ - void fatal(const char *fmt, ...); - void warn(const char *fmt, ...); - void merror(const char *fmt, ...); -+ -diff -Nur linux-3.11.5.orig/scripts/mod/sumversion.c linux-3.11.5/scripts/mod/sumversion.c ---- linux-3.11.5.orig/scripts/mod/sumversion.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/mod/sumversion.c 2013-10-16 18:09:31.000000000 +0200 -@@ -1,4 +1,4 @@ --#include -+/* #include */ - #ifdef __sun__ - #include - #else -diff -Nur linux-3.11.5.orig/tools/include/tools/linux_types.h linux-3.11.5/tools/include/tools/linux_types.h ---- linux-3.11.5.orig/tools/include/tools/linux_types.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.11.5/tools/include/tools/linux_types.h 2013-10-16 18:09:31.000000000 +0200 -@@ -0,0 +1,22 @@ -+#ifndef __LINUX_TYPES_H -+#define __LINUX_TYPES_H -+ -+#include -+ -+typedef uint8_t __u8; -+typedef uint8_t __be8; -+typedef uint8_t __le8; -+ -+typedef uint16_t __u16; -+typedef uint16_t __be16; -+typedef uint16_t __le16; -+ -+typedef uint32_t __u32; -+typedef uint32_t __be32; -+typedef uint32_t __le32; -+ -+typedef uint64_t __u64; -+typedef uint64_t __be64; -+typedef uint64_t __le64; -+ -+#endif diff --git a/target/linux/patches/3.18.14/cleankernel.patch b/target/linux/patches/3.18.14/cleankernel.patch deleted file mode 100644 index d8c055dc3..000000000 --- a/target/linux/patches/3.18.14/cleankernel.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.5.orig/scripts/Makefile.headersinst linux-3.11.5/scripts/Makefile.headersinst ---- linux-3.11.5.orig/scripts/Makefile.headersinst 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/Makefile.headersinst 2013-10-15 16:33:10.000000000 +0200 -@@ -107,7 +107,6 @@ - - targets += $(install-file) - $(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE -- $(if $(unwanted),$(call cmd,remove),) - $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) - $(call if_changed,install) - diff --git a/target/linux/patches/3.18.14/cris-header.patch b/target/linux/patches/3.18.14/cris-header.patch deleted file mode 100644 index 3db07e530..000000000 --- a/target/linux/patches/3.18.14/cris-header.patch +++ /dev/null @@ -1,50 +0,0 @@ -diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild ---- linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 -+++ linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild 2014-09-26 19:24:50.000000000 +0200 -@@ -1 +1,2 @@ - # CRISv10 arch -+header-y += ptrace.h -diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild ---- linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 -+++ linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild 2014-09-26 19:24:31.000000000 +0200 -@@ -1 +1,2 @@ - # CRISv32 arch -+header-y += ptrace.h -diff -Nur linux-3.16.2.orig/arch/cris/include/asm/Kbuild linux-3.16.2/arch/cris/include/asm/Kbuild ---- linux-3.16.2.orig/arch/cris/include/asm/Kbuild 2014-09-06 01:37:11.000000000 +0200 -+++ linux-3.16.2/arch/cris/include/asm/Kbuild 2014-09-26 19:24:31.000000000 +0200 -@@ -1,8 +1,3 @@ -- --header-y += arch-v10/ --header-y += arch-v32/ -- -- - generic-y += barrier.h - generic-y += clkdev.h - generic-y += cputime.h -diff -Nur linux-3.16.2.orig/arch/cris/include/uapi/asm/Kbuild linux-3.16.2/arch/cris/include/uapi/asm/Kbuild ---- linux-3.16.2.orig/arch/cris/include/uapi/asm/Kbuild 2014-09-06 01:37:11.000000000 +0200 -+++ linux-3.16.2/arch/cris/include/uapi/asm/Kbuild 2014-09-26 19:24:31.000000000 +0200 -@@ -1,8 +1,8 @@ - # UAPI Header export list - include include/uapi/asm-generic/Kbuild.asm - --header-y += arch-v10/ --header-y += arch-v32/ -+header-y += ../arch-v10/arch/ -+header-y += ../arch-v32/arch/ - header-y += auxvec.h - header-y += bitsperlong.h - header-y += byteorder.h -diff -Nur linux-3.16.2.orig/scripts/headers.sh linux-3.16.2/scripts/headers.sh ---- linux-3.16.2.orig/scripts/headers.sh 2014-09-06 01:37:11.000000000 +0200 -+++ linux-3.16.2/scripts/headers.sh 2014-09-26 19:24:31.000000000 +0200 -@@ -19,8 +19,6 @@ - case ${arch} in - um) # no userspace export - ;; -- cris) # headers export are known broken -- ;; - *) - if [ -d ${srctree}/arch/${arch} ]; then - do_command $1 ${arch} diff --git a/target/linux/patches/3.18.14/cris-initramfs.patch b/target/linux/patches/3.18.14/cris-initramfs.patch deleted file mode 100644 index b709e705e..000000000 --- a/target/linux/patches/3.18.14/cris-initramfs.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff -Nur linux-3.18.12.orig/arch/cris/arch-v10/mm/init.c linux-3.18.12/arch/cris/arch-v10/mm/init.c ---- linux-3.18.12.orig/arch/cris/arch-v10/mm/init.c 2015-04-20 21:48:02.000000000 +0200 -+++ linux-3.18.12/arch/cris/arch-v10/mm/init.c 2015-05-16 01:46:37.000000000 +0200 -@@ -261,3 +261,7 @@ - { - flush_etrax_cacherange(0, 8192); - } -+ -+void free_initrd_mem(unsigned long start, unsigned long end) -+{ -+} -diff -Nur linux-3.18.12.orig/arch/cris/arch-v32/mm/init.c linux-3.18.12/arch/cris/arch-v32/mm/init.c ---- linux-3.18.12.orig/arch/cris/arch-v32/mm/init.c 2015-04-20 21:48:02.000000000 +0200 -+++ linux-3.18.12/arch/cris/arch-v32/mm/init.c 2015-05-16 01:46:54.000000000 +0200 -@@ -171,3 +171,7 @@ - - mem_map = contig_page_data.node_mem_map; - } -+ -+void free_initrd_mem(unsigned long start, unsigned long end) -+{ -+} diff --git a/target/linux/patches/3.18.14/defaults.patch b/target/linux/patches/3.18.14/defaults.patch deleted file mode 100644 index 6cdca084e..000000000 --- a/target/linux/patches/3.18.14/defaults.patch +++ /dev/null @@ -1,46 +0,0 @@ -diff -Nur linux-3.0.4.orig/fs/Kconfig linux-3.0.4/fs/Kconfig ---- linux-3.0.4.orig/fs/Kconfig 2011-08-29 22:56:30.000000000 +0200 -+++ linux-3.0.4/fs/Kconfig 2011-10-15 22:08:44.000000000 +0200 -@@ -47,7 +47,7 @@ - def_bool n - - config EXPORTFS -- tristate -+ def_bool y - - config FILE_LOCKING - bool "Enable POSIX file locking API" if EXPERT -diff -Nur linux-3.0.4.orig/fs/notify/Kconfig linux-3.0.4/fs/notify/Kconfig ---- linux-3.0.4.orig/fs/notify/Kconfig 2011-08-29 22:56:30.000000000 +0200 -+++ linux-3.0.4/fs/notify/Kconfig 2011-10-15 22:02:00.000000000 +0200 -@@ -1,5 +1,5 @@ - config FSNOTIFY -- def_bool n -+ def_bool y - - source "fs/notify/dnotify/Kconfig" - source "fs/notify/inotify/Kconfig" -diff -Nur linux-3.11.10.orig/drivers/scsi/Kconfig linux-3.11.10/drivers/scsi/Kconfig ---- linux-3.11.10.orig/drivers/scsi/Kconfig 2013-11-29 19:42:37.000000000 +0100 -+++ linux-3.11.10/drivers/scsi/Kconfig 2013-12-27 19:13:21.000000000 +0100 -@@ -2,7 +2,7 @@ - - config SCSI_MOD - tristate -- default y if SCSI=n || SCSI=y -+ default y if SCSI=y - default m if SCSI=m - - config RAID_ATTRS -diff -Nur linux-3.11.10.orig/usr/Kconfig linux-3.11.10/usr/Kconfig ---- linux-3.11.10.orig/usr/Kconfig 2013-11-29 19:42:37.000000000 +0100 -+++ linux-3.11.10/usr/Kconfig 2013-12-27 19:15:16.000000000 +0100 -@@ -47,7 +47,7 @@ - - config RD_GZIP - bool "Support initial ramdisks compressed using gzip" if EXPERT -- default y -+ default n - depends on BLK_DEV_INITRD - select DECOMPRESS_GZIP - help diff --git a/target/linux/patches/3.18.14/export-symbol-for-exmap.patch b/target/linux/patches/3.18.14/export-symbol-for-exmap.patch deleted file mode 100644 index 4f0fc8449..000000000 --- a/target/linux/patches/3.18.14/export-symbol-for-exmap.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.5.orig/kernel/pid.c linux-3.11.5/kernel/pid.c ---- linux-3.11.5.orig/kernel/pid.c 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/kernel/pid.c 2013-10-29 15:37:02.000000000 +0100 -@@ -450,6 +450,7 @@ - { - return find_task_by_pid_ns(vnr, task_active_pid_ns(current)); - } -+EXPORT_SYMBOL(find_task_by_vpid); - - struct pid *get_task_pid(struct task_struct *task, enum pid_type type) - { diff --git a/target/linux/patches/3.18.14/fblogo.patch b/target/linux/patches/3.18.14/fblogo.patch deleted file mode 100644 index 5b9070242..000000000 --- a/target/linux/patches/3.18.14/fblogo.patch +++ /dev/null @@ -1,2057 +0,0 @@ -diff -Nur linux-3.18.9.orig/Documentation/fb/00-INDEX linux-3.18.9/Documentation/fb/00-INDEX ---- linux-3.18.9.orig/Documentation/fb/00-INDEX 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/Documentation/fb/00-INDEX 2015-03-15 14:34:13.068143682 -0500 -@@ -23,6 +23,8 @@ - - info on the driver for EP93xx LCD controller. - fbcon.txt - - intro to and usage guide for the framebuffer console (fbcon). -+fbcondecor.txt -+ - info on the Framebuffer Console Decoration - framebuffer.txt - - introduction to frame buffer devices. - gxfb.txt -diff -Nur linux-3.18.9.orig/Documentation/fb/fbcondecor.txt linux-3.18.9/Documentation/fb/fbcondecor.txt ---- linux-3.18.9.orig/Documentation/fb/fbcondecor.txt 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.9/Documentation/fb/fbcondecor.txt 2015-03-15 14:34:13.068143682 -0500 -@@ -0,0 +1,207 @@ -+What is it? -+----------- -+ -+The framebuffer decorations are a kernel feature which allows displaying a -+background picture on selected consoles. -+ -+What do I need to get it to work? -+--------------------------------- -+ -+To get fbcondecor up-and-running you will have to: -+ 1) get a copy of splashutils [1] or a similar program -+ 2) get some fbcondecor themes -+ 3) build the kernel helper program -+ 4) build your kernel with the FB_CON_DECOR option enabled. -+ -+To get fbcondecor operational right after fbcon initialization is finished, you -+will have to include a theme and the kernel helper into your initramfs image. -+Please refer to splashutils documentation for instructions on how to do that. -+ -+[1] The splashutils package can be downloaded from: -+ http://github.com/alanhaggai/fbsplash -+ -+The userspace helper -+-------------------- -+ -+The userspace fbcondecor helper (by default: /sbin/fbcondecor_helper) is called by the -+kernel whenever an important event occurs and the kernel needs some kind of -+job to be carried out. Important events include console switches and video -+mode switches (the kernel requests background images and configuration -+parameters for the current console). The fbcondecor helper must be accessible at -+all times. If it's not, fbcondecor will be switched off automatically. -+ -+It's possible to set path to the fbcondecor helper by writing it to -+/proc/sys/kernel/fbcondecor. -+ -+***************************************************************************** -+ -+The information below is mostly technical stuff. There's probably no need to -+read it unless you plan to develop a userspace helper. -+ -+The fbcondecor protocol -+----------------------- -+ -+The fbcondecor protocol defines a communication interface between the kernel and -+the userspace fbcondecor helper. -+ -+The kernel side is responsible for: -+ -+ * rendering console text, using an image as a background (instead of a -+ standard solid color fbcon uses), -+ * accepting commands from the user via ioctls on the fbcondecor device, -+ * calling the userspace helper to set things up as soon as the fb subsystem -+ is initialized. -+ -+The userspace helper is responsible for everything else, including parsing -+configuration files, decompressing the image files whenever the kernel needs -+it, and communicating with the kernel if necessary. -+ -+The fbcondecor protocol specifies how communication is done in both ways: -+kernel->userspace and userspace->helper. -+ -+Kernel -> Userspace -+------------------- -+ -+The kernel communicates with the userspace helper by calling it and specifying -+the task to be done in a series of arguments. -+ -+The arguments follow the pattern: -+ -+ -+All commands defined in fbcondecor protocol v2 have the following parameters: -+ virtual console -+ framebuffer number -+ theme -+ -+Fbcondecor protocol v1 specified an additional 'fbcondecor mode' after the -+framebuffer number. Fbcondecor protocol v1 is deprecated and should not be used. -+ -+Fbcondecor protocol v2 specifies the following commands: -+ -+getpic -+------ -+ The kernel issues this command to request image data. It's up to the -+ userspace helper to find a background image appropriate for the specified -+ theme and the current resolution. The userspace helper should respond by -+ issuing the FBIOCONDECOR_SETPIC ioctl. -+ -+init -+---- -+ The kernel issues this command after the fbcondecor device is created and -+ the fbcondecor interface is initialized. Upon receiving 'init', the userspace -+ helper should parse the kernel command line (/proc/cmdline) or otherwise -+ decide whether fbcondecor is to be activated. -+ -+ To activate fbcondecor on the first console the helper should issue the -+ FBIOCONDECOR_SETCFG, FBIOCONDECOR_SETPIC and FBIOCONDECOR_SETSTATE commands, -+ in the above-mentioned order. -+ -+ When the userspace helper is called in an early phase of the boot process -+ (right after the initialization of fbcon), no filesystems will be mounted. -+ The helper program should mount sysfs and then create the appropriate -+ framebuffer, fbcondecor and tty0 devices (if they don't already exist) to get -+ current display settings and to be able to communicate with the kernel side. -+ It should probably also mount the procfs to be able to parse the kernel -+ command line parameters. -+ -+ Note that the console sem is not held when the kernel calls fbcondecor_helper -+ with the 'init' command. The fbcondecor helper should perform all ioctls with -+ origin set to FBCON_DECOR_IO_ORIG_USER. -+ -+modechange -+---------- -+ The kernel issues this command on a mode change. The helper's response should -+ be similar to the response to the 'init' command. Note that this time the -+ console sem is held and all ioctls must be performed with origin set to -+ FBCON_DECOR_IO_ORIG_KERNEL. -+ -+ -+Userspace -> Kernel -+------------------- -+ -+Userspace programs can communicate with fbcondecor via ioctls on the -+fbcondecor device. These ioctls are to be used by both the userspace helper -+(called only by the kernel) and userspace configuration tools (run by the users). -+ -+The fbcondecor helper should set the origin field to FBCON_DECOR_IO_ORIG_KERNEL -+when doing the appropriate ioctls. All userspace configuration tools should -+use FBCON_DECOR_IO_ORIG_USER. Failure to set the appropriate value in the origin -+field when performing ioctls from the kernel helper will most likely result -+in a console deadlock. -+ -+FBCON_DECOR_IO_ORIG_KERNEL instructs fbcondecor not to try to acquire the console -+semaphore. Not surprisingly, FBCON_DECOR_IO_ORIG_USER instructs it to acquire -+the console sem. -+ -+The framebuffer console decoration provides the following ioctls (all defined in -+linux/fb.h): -+ -+FBIOCONDECOR_SETPIC -+description: loads a background picture for a virtual console -+argument: struct fbcon_decor_iowrapper*; data: struct fb_image* -+notes: -+If called for consoles other than the current foreground one, the picture data -+will be ignored. -+ -+If the current virtual console is running in a 8-bpp mode, the cmap substruct -+of fb_image has to be filled appropriately: start should be set to 16 (first -+16 colors are reserved for fbcon), len to a value <= 240 and red, green and -+blue should point to valid cmap data. The transp field is ingored. The fields -+dx, dy, bg_color, fg_color in fb_image are ignored as well. -+ -+FBIOCONDECOR_SETCFG -+description: sets the fbcondecor config for a virtual console -+argument: struct fbcon_decor_iowrapper*; data: struct vc_decor* -+notes: The structure has to be filled with valid data. -+ -+FBIOCONDECOR_GETCFG -+description: gets the fbcondecor config for a virtual console -+argument: struct fbcon_decor_iowrapper*; data: struct vc_decor* -+ -+FBIOCONDECOR_SETSTATE -+description: sets the fbcondecor state for a virtual console -+argument: struct fbcon_decor_iowrapper*; data: unsigned int* -+ values: 0 = disabled, 1 = enabled. -+ -+FBIOCONDECOR_GETSTATE -+description: gets the fbcondecor state for a virtual console -+argument: struct fbcon_decor_iowrapper*; data: unsigned int* -+ values: as in FBIOCONDECOR_SETSTATE -+ -+Info on used structures: -+ -+Definition of struct vc_decor can be found in linux/console_decor.h. It's -+heavily commented. Note that the 'theme' field should point to a string -+no longer than FBCON_DECOR_THEME_LEN. When FBIOCONDECOR_GETCFG call is -+performed, the theme field should point to a char buffer of length -+FBCON_DECOR_THEME_LEN. -+ -+Definition of struct fbcon_decor_iowrapper can be found in linux/fb.h. -+The fields in this struct have the following meaning: -+ -+vc: -+Virtual console number. -+ -+origin: -+Specifies if the ioctl is performed as a response to a kernel request. The -+fbcondecor helper should set this field to FBCON_DECOR_IO_ORIG_KERNEL, userspace -+programs should set it to FBCON_DECOR_IO_ORIG_USER. This field is necessary to -+avoid console semaphore deadlocks. -+ -+data: -+Pointer to a data structure appropriate for the performed ioctl. Type of -+the data struct is specified in the ioctls description. -+ -+***************************************************************************** -+ -+Credit -+------ -+ -+Original 'bootsplash' project & implementation by: -+ Volker Poplawski , Stefan Reinauer , -+ Steffen Winterfeldt , Michael Schroeder , -+ Ken Wimer . -+ -+Fbcondecor, fbcondecor protocol design, current implementation & docs by: -+ Michal Januszewski -+ -diff -Nur linux-3.18.9.orig/drivers/Makefile linux-3.18.9/drivers/Makefile ---- linux-3.18.9.orig/drivers/Makefile 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/drivers/Makefile 2015-03-15 14:34:13.068143682 -0500 -@@ -17,6 +17,10 @@ - obj-$(CONFIG_PCI) += pci/ - obj-$(CONFIG_PARISC) += parisc/ - obj-$(CONFIG_RAPIDIO) += rapidio/ -+# tty/ comes before char/ so that the VT console is the boot-time -+# default. -+obj-y += tty/ -+obj-y += char/ - obj-y += video/ - obj-y += idle/ - -@@ -45,11 +49,6 @@ - # reset controllers early, since gpu drivers might rely on them to initialize - obj-$(CONFIG_RESET_CONTROLLER) += reset/ - --# tty/ comes before char/ so that the VT console is the boot-time --# default. --obj-y += tty/ --obj-y += char/ -- - # gpu/ comes after char for AGP vs DRM startup - obj-y += gpu/ - -diff -Nur linux-3.18.9.orig/drivers/video/console/bitblit.c linux-3.18.9/drivers/video/console/bitblit.c ---- linux-3.18.9.orig/drivers/video/console/bitblit.c 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/drivers/video/console/bitblit.c 2015-03-15 14:34:13.068143682 -0500 -@@ -18,6 +18,7 @@ - #include - #include - #include "fbcon.h" -+#include "fbcondecor.h" - - /* - * Accelerated handlers. -@@ -55,6 +56,13 @@ - area.height = height * vc->vc_font.height; - area.width = width * vc->vc_font.width; - -+ if (fbcon_decor_active(info, vc)) { -+ area.sx += vc->vc_decor.tx; -+ area.sy += vc->vc_decor.ty; -+ area.dx += vc->vc_decor.tx; -+ area.dy += vc->vc_decor.ty; -+ } -+ - info->fbops->fb_copyarea(info, &area); - } - -@@ -379,11 +387,15 @@ - cursor.image.depth = 1; - cursor.rop = ROP_XOR; - -- if (info->fbops->fb_cursor) -- err = info->fbops->fb_cursor(info, &cursor); -+ if (fbcon_decor_active(info, vc)) { -+ fbcon_decor_cursor(info, &cursor); -+ } else { -+ if (info->fbops->fb_cursor) -+ err = info->fbops->fb_cursor(info, &cursor); - -- if (err) -- soft_cursor(info, &cursor); -+ if (err) -+ soft_cursor(info, &cursor); -+ } - - ops->cursor_reset = 0; - } -diff -Nur linux-3.18.9.orig/drivers/video/console/cfbcondecor.c linux-3.18.9/drivers/video/console/cfbcondecor.c ---- linux-3.18.9.orig/drivers/video/console/cfbcondecor.c 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.9/drivers/video/console/cfbcondecor.c 2015-03-15 14:34:13.072143681 -0500 -@@ -0,0 +1,471 @@ -+/* -+ * linux/drivers/video/cfbcon_decor.c -- Framebuffer decor render functions -+ * -+ * Copyright (C) 2004 Michal Januszewski -+ * -+ * Code based upon "Bootdecor" (C) 2001-2003 -+ * Volker Poplawski , -+ * Stefan Reinauer , -+ * Steffen Winterfeldt , -+ * Michael Schroeder , -+ * Ken Wimer . -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive for -+ * more details. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "fbcon.h" -+#include "fbcondecor.h" -+ -+#define parse_pixel(shift,bpp,type) \ -+ do { \ -+ if (d & (0x80 >> (shift))) \ -+ dd2[(shift)] = fgx; \ -+ else \ -+ dd2[(shift)] = transparent ? *(type *)decor_src : bgx; \ -+ decor_src += (bpp); \ -+ } while (0) \ -+ -+extern int get_color(struct vc_data *vc, struct fb_info *info, -+ u16 c, int is_fg); -+ -+void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) -+{ -+ int i, j, k; -+ int minlen = min(min(info->var.red.length, info->var.green.length), -+ info->var.blue.length); -+ u32 col; -+ -+ for (j = i = 0; i < 16; i++) { -+ k = color_table[i]; -+ -+ col = ((vc->vc_palette[j++] >> (8-minlen)) -+ << info->var.red.offset); -+ col |= ((vc->vc_palette[j++] >> (8-minlen)) -+ << info->var.green.offset); -+ col |= ((vc->vc_palette[j++] >> (8-minlen)) -+ << info->var.blue.offset); -+ ((u32 *)info->pseudo_palette)[k] = col; -+ } -+} -+ -+void fbcon_decor_renderc(struct fb_info *info, int ypos, int xpos, int height, -+ int width, u8* src, u32 fgx, u32 bgx, u8 transparent) -+{ -+ unsigned int x, y; -+ u32 dd; -+ int bytespp = ((info->var.bits_per_pixel + 7) >> 3); -+ unsigned int d = ypos * info->fix.line_length + xpos * bytespp; -+ unsigned int ds = (ypos * info->var.xres + xpos) * bytespp; -+ u16 dd2[4]; -+ -+ u8* decor_src = (u8 *)(info->bgdecor.data + ds); -+ u8* dst = (u8 *)(info->screen_base + d); -+ -+ if ((ypos + height) > info->var.yres || (xpos + width) > info->var.xres) -+ return; -+ -+ for (y = 0; y < height; y++) { -+ switch (info->var.bits_per_pixel) { -+ -+ case 32: -+ for (x = 0; x < width; x++) { -+ -+ if ((x & 7) == 0) -+ d = *src++; -+ if (d & 0x80) -+ dd = fgx; -+ else -+ dd = transparent ? -+ *(u32 *)decor_src : bgx; -+ -+ d <<= 1; -+ decor_src += 4; -+ fb_writel(dd, dst); -+ dst += 4; -+ } -+ break; -+ case 24: -+ for (x = 0; x < width; x++) { -+ -+ if ((x & 7) == 0) -+ d = *src++; -+ if (d & 0x80) -+ dd = fgx; -+ else -+ dd = transparent ? -+ (*(u32 *)decor_src & 0xffffff) : bgx; -+ -+ d <<= 1; -+ decor_src += 3; -+#ifdef __LITTLE_ENDIAN -+ fb_writew(dd & 0xffff, dst); -+ dst += 2; -+ fb_writeb((dd >> 16), dst); -+#else -+ fb_writew(dd >> 8, dst); -+ dst += 2; -+ fb_writeb(dd & 0xff, dst); -+#endif -+ dst++; -+ } -+ break; -+ case 16: -+ for (x = 0; x < width; x += 2) { -+ if ((x & 7) == 0) -+ d = *src++; -+ -+ parse_pixel(0, 2, u16); -+ parse_pixel(1, 2, u16); -+#ifdef __LITTLE_ENDIAN -+ dd = dd2[0] | (dd2[1] << 16); -+#else -+ dd = dd2[1] | (dd2[0] << 16); -+#endif -+ d <<= 2; -+ fb_writel(dd, dst); -+ dst += 4; -+ } -+ break; -+ -+ case 8: -+ for (x = 0; x < width; x += 4) { -+ if ((x & 7) == 0) -+ d = *src++; -+ -+ parse_pixel(0, 1, u8); -+ parse_pixel(1, 1, u8); -+ parse_pixel(2, 1, u8); -+ parse_pixel(3, 1, u8); -+ -+#ifdef __LITTLE_ENDIAN -+ dd = dd2[0] | (dd2[1] << 8) | (dd2[2] << 16) | (dd2[3] << 24); -+#else -+ dd = dd2[3] | (dd2[2] << 8) | (dd2[1] << 16) | (dd2[0] << 24); -+#endif -+ d <<= 4; -+ fb_writel(dd, dst); -+ dst += 4; -+ } -+ } -+ -+ dst += info->fix.line_length - width * bytespp; -+ decor_src += (info->var.xres - width) * bytespp; -+ } -+} -+ -+#define cc2cx(a) \ -+ ((info->fix.visual == FB_VISUAL_TRUECOLOR || \ -+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? \ -+ ((u32*)info->pseudo_palette)[a] : a) -+ -+void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, -+ const unsigned short *s, int count, int yy, int xx) -+{ -+ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; -+ struct fbcon_ops *ops = info->fbcon_par; -+ int fg_color, bg_color, transparent; -+ u8 *src; -+ u32 bgx, fgx; -+ u16 c = scr_readw(s); -+ -+ fg_color = get_color(vc, info, c, 1); -+ bg_color = get_color(vc, info, c, 0); -+ -+ /* Don't paint the background image if console is blanked */ -+ transparent = ops->blank_state ? 0 : -+ (vc->vc_decor.bg_color == bg_color); -+ -+ xx = xx * vc->vc_font.width + vc->vc_decor.tx; -+ yy = yy * vc->vc_font.height + vc->vc_decor.ty; -+ -+ fgx = cc2cx(fg_color); -+ bgx = cc2cx(bg_color); -+ -+ while (count--) { -+ c = scr_readw(s++); -+ src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * -+ ((vc->vc_font.width + 7) >> 3); -+ -+ fbcon_decor_renderc(info, yy, xx, vc->vc_font.height, -+ vc->vc_font.width, src, fgx, bgx, transparent); -+ xx += vc->vc_font.width; -+ } -+} -+ -+void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) -+{ -+ int i; -+ unsigned int dsize, s_pitch; -+ struct fbcon_ops *ops = info->fbcon_par; -+ struct vc_data* vc; -+ u8 *src; -+ -+ /* we really don't need any cursors while the console is blanked */ -+ if (info->state != FBINFO_STATE_RUNNING || ops->blank_state) -+ return; -+ -+ vc = vc_cons[ops->currcon].d; -+ -+ src = kmalloc(64 + sizeof(struct fb_image), GFP_ATOMIC); -+ if (!src) -+ return; -+ -+ s_pitch = (cursor->image.width + 7) >> 3; -+ dsize = s_pitch * cursor->image.height; -+ if (cursor->enable) { -+ switch (cursor->rop) { -+ case ROP_XOR: -+ for (i = 0; i < dsize; i++) -+ src[i] = cursor->image.data[i] ^ cursor->mask[i]; -+ break; -+ case ROP_COPY: -+ default: -+ for (i = 0; i < dsize; i++) -+ src[i] = cursor->image.data[i] & cursor->mask[i]; -+ break; -+ } -+ } else -+ memcpy(src, cursor->image.data, dsize); -+ -+ fbcon_decor_renderc(info, -+ cursor->image.dy + vc->vc_decor.ty, -+ cursor->image.dx + vc->vc_decor.tx, -+ cursor->image.height, -+ cursor->image.width, -+ (u8*)src, -+ cc2cx(cursor->image.fg_color), -+ cc2cx(cursor->image.bg_color), -+ cursor->image.bg_color == vc->vc_decor.bg_color); -+ -+ kfree(src); -+} -+ -+static void decorset(u8 *dst, int height, int width, int dstbytes, -+ u32 bgx, int bpp) -+{ -+ int i; -+ -+ if (bpp == 8) -+ bgx |= bgx << 8; -+ if (bpp == 16 || bpp == 8) -+ bgx |= bgx << 16; -+ -+ while (height-- > 0) { -+ u8 *p = dst; -+ -+ switch (bpp) { -+ -+ case 32: -+ for (i=0; i < width; i++) { -+ fb_writel(bgx, p); p += 4; -+ } -+ break; -+ case 24: -+ for (i=0; i < width; i++) { -+#ifdef __LITTLE_ENDIAN -+ fb_writew((bgx & 0xffff),(u16*)p); p += 2; -+ fb_writeb((bgx >> 16),p++); -+#else -+ fb_writew((bgx >> 8),(u16*)p); p += 2; -+ fb_writeb((bgx & 0xff),p++); -+#endif -+ } -+ case 16: -+ for (i=0; i < width/4; i++) { -+ fb_writel(bgx,p); p += 4; -+ fb_writel(bgx,p); p += 4; -+ } -+ if (width & 2) { -+ fb_writel(bgx,p); p += 4; -+ } -+ if (width & 1) -+ fb_writew(bgx,(u16*)p); -+ break; -+ case 8: -+ for (i=0; i < width/4; i++) { -+ fb_writel(bgx,p); p += 4; -+ } -+ -+ if (width & 2) { -+ fb_writew(bgx,p); p += 2; -+ } -+ if (width & 1) -+ fb_writeb(bgx,(u8*)p); -+ break; -+ -+ } -+ dst += dstbytes; -+ } -+} -+ -+void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes, -+ int srclinebytes, int bpp) -+{ -+ int i; -+ -+ while (height-- > 0) { -+ u32 *p = (u32 *)dst; -+ u32 *q = (u32 *)src; -+ -+ switch (bpp) { -+ -+ case 32: -+ for (i=0; i < width; i++) -+ fb_writel(*q++, p++); -+ break; -+ case 24: -+ for (i=0; i < (width*3/4); i++) -+ fb_writel(*q++, p++); -+ if ((width*3) % 4) { -+ if (width & 2) { -+ fb_writeb(*(u8*)q, (u8*)p); -+ } else if (width & 1) { -+ fb_writew(*(u16*)q, (u16*)p); -+ fb_writeb(*(u8*)((u16*)q+1),(u8*)((u16*)p+2)); -+ } -+ } -+ break; -+ case 16: -+ for (i=0; i < width/4; i++) { -+ fb_writel(*q++, p++); -+ fb_writel(*q++, p++); -+ } -+ if (width & 2) -+ fb_writel(*q++, p++); -+ if (width & 1) -+ fb_writew(*(u16*)q, (u16*)p); -+ break; -+ case 8: -+ for (i=0; i < width/4; i++) -+ fb_writel(*q++, p++); -+ -+ if (width & 2) { -+ fb_writew(*(u16*)q, (u16*)p); -+ q = (u32*) ((u16*)q + 1); -+ p = (u32*) ((u16*)p + 1); -+ } -+ if (width & 1) -+ fb_writeb(*(u8*)q, (u8*)p); -+ break; -+ } -+ -+ dst += linebytes; -+ src += srclinebytes; -+ } -+} -+ -+static void decorfill(struct fb_info *info, int sy, int sx, int height, -+ int width) -+{ -+ int bytespp = ((info->var.bits_per_pixel + 7) >> 3); -+ int d = sy * info->fix.line_length + sx * bytespp; -+ int ds = (sy * info->var.xres + sx) * bytespp; -+ -+ fbcon_decor_copy((u8 *)(info->screen_base + d), (u8 *)(info->bgdecor.data + ds), -+ height, width, info->fix.line_length, info->var.xres * bytespp, -+ info->var.bits_per_pixel); -+} -+ -+void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, -+ int height, int width) -+{ -+ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; -+ struct fbcon_ops *ops = info->fbcon_par; -+ u8 *dst; -+ int transparent, bg_color = attr_bgcol_ec(bgshift, vc, info); -+ -+ transparent = (vc->vc_decor.bg_color == bg_color); -+ sy = sy * vc->vc_font.height + vc->vc_decor.ty; -+ sx = sx * vc->vc_font.width + vc->vc_decor.tx; -+ height *= vc->vc_font.height; -+ width *= vc->vc_font.width; -+ -+ /* Don't paint the background image if console is blanked */ -+ if (transparent && !ops->blank_state) { -+ decorfill(info, sy, sx, height, width); -+ } else { -+ dst = (u8 *)(info->screen_base + sy * info->fix.line_length + -+ sx * ((info->var.bits_per_pixel + 7) >> 3)); -+ decorset(dst, height, width, info->fix.line_length, cc2cx(bg_color), -+ info->var.bits_per_pixel); -+ } -+} -+ -+void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, -+ int bottom_only) -+{ -+ unsigned int tw = vc->vc_cols*vc->vc_font.width; -+ unsigned int th = vc->vc_rows*vc->vc_font.height; -+ -+ if (!bottom_only) { -+ /* top margin */ -+ decorfill(info, 0, 0, vc->vc_decor.ty, info->var.xres); -+ /* left margin */ -+ decorfill(info, vc->vc_decor.ty, 0, th, vc->vc_decor.tx); -+ /* right margin */ -+ decorfill(info, vc->vc_decor.ty, vc->vc_decor.tx + tw, th, -+ info->var.xres - vc->vc_decor.tx - tw); -+ } -+ decorfill(info, vc->vc_decor.ty + th, 0, -+ info->var.yres - vc->vc_decor.ty - th, info->var.xres); -+} -+ -+void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, -+ int sx, int dx, int width) -+{ -+ u16 *d = (u16 *) (vc->vc_origin + vc->vc_size_row * y + dx * 2); -+ u16 *s = d + (dx - sx); -+ u16 *start = d; -+ u16 *ls = d; -+ u16 *le = d + width; -+ u16 c; -+ int x = dx; -+ u16 attr = 1; -+ -+ do { -+ c = scr_readw(d); -+ if (attr != (c & 0xff00)) { -+ attr = c & 0xff00; -+ if (d > start) { -+ fbcon_decor_putcs(vc, info, start, d - start, y, x); -+ x += d - start; -+ start = d; -+ } -+ } -+ if (s >= ls && s < le && c == scr_readw(s)) { -+ if (d > start) { -+ fbcon_decor_putcs(vc, info, start, d - start, y, x); -+ x += d - start + 1; -+ start = d + 1; -+ } else { -+ x++; -+ start++; -+ } -+ } -+ s++; -+ d++; -+ } while (d < le); -+ if (d > start) -+ fbcon_decor_putcs(vc, info, start, d - start, y, x); -+} -+ -+void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) -+{ -+ if (blank) { -+ decorset((u8 *)info->screen_base, info->var.yres, info->var.xres, -+ info->fix.line_length, 0, info->var.bits_per_pixel); -+ } else { -+ update_screen(vc); -+ fbcon_decor_clear_margins(vc, info, 0); -+ } -+} -+ -diff -Nur linux-3.18.9.orig/drivers/video/console/fbcon.c linux-3.18.9/drivers/video/console/fbcon.c ---- linux-3.18.9.orig/drivers/video/console/fbcon.c 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/drivers/video/console/fbcon.c 2015-03-15 14:34:13.072143681 -0500 -@@ -79,6 +79,7 @@ - #include - - #include "fbcon.h" -+#include "fbcondecor.h" - - #ifdef FBCONDEBUG - # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) -@@ -94,7 +95,7 @@ - - static struct display fb_display[MAX_NR_CONSOLES]; - --static signed char con2fb_map[MAX_NR_CONSOLES]; -+signed char con2fb_map[MAX_NR_CONSOLES]; - static signed char con2fb_map_boot[MAX_NR_CONSOLES]; - - static int logo_lines; -@@ -286,7 +287,7 @@ - !vt_force_oops_output(vc); - } - --static int get_color(struct vc_data *vc, struct fb_info *info, -+int get_color(struct vc_data *vc, struct fb_info *info, - u16 c, int is_fg) - { - int depth = fb_get_color_depth(&info->var, &info->fix); -@@ -550,6 +551,9 @@ - info_idx = -1; - } else { - fbcon_has_console_bind = 1; -+#ifdef CONFIG_FB_CON_DECOR -+ fbcon_decor_init(); -+#endif - } - - return err; -@@ -1007,6 +1011,12 @@ - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); - cols /= vc->vc_font.width; - rows /= vc->vc_font.height; -+ -+ if (fbcon_decor_active(info, vc)) { -+ cols = vc->vc_decor.twidth / vc->vc_font.width; -+ rows = vc->vc_decor.theight / vc->vc_font.height; -+ } -+ - vc_resize(vc, cols, rows); - - DPRINTK("mode: %s\n", info->fix.id); -@@ -1036,7 +1046,7 @@ - cap = info->flags; - - if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW || -- (info->fix.type == FB_TYPE_TEXT)) -+ (info->fix.type == FB_TYPE_TEXT) || fbcon_decor_active(info, vc)) - logo = 0; - - if (var_to_display(p, &info->var, info)) -@@ -1260,6 +1270,11 @@ - fbcon_clear_margins(vc, 0); - } - -+ if (fbcon_decor_active(info, vc)) { -+ fbcon_decor_clear(vc, info, sy, sx, height, width); -+ return; -+ } -+ - /* Split blits that cross physical y_wrap boundary */ - - y_break = p->vrows - p->yscroll; -@@ -1279,10 +1294,15 @@ - struct display *p = &fb_display[vc->vc_num]; - struct fbcon_ops *ops = info->fbcon_par; - -- if (!fbcon_is_inactive(vc, info)) -- ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, -- get_color(vc, info, scr_readw(s), 1), -- get_color(vc, info, scr_readw(s), 0)); -+ if (!fbcon_is_inactive(vc, info)) { -+ -+ if (fbcon_decor_active(info, vc)) -+ fbcon_decor_putcs(vc, info, s, count, ypos, xpos); -+ else -+ ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, -+ get_color(vc, info, scr_readw(s), 1), -+ get_color(vc, info, scr_readw(s), 0)); -+ } - } - - static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) -@@ -1298,8 +1318,13 @@ - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - struct fbcon_ops *ops = info->fbcon_par; - -- if (!fbcon_is_inactive(vc, info)) -- ops->clear_margins(vc, info, bottom_only); -+ if (!fbcon_is_inactive(vc, info)) { -+ if (fbcon_decor_active(info, vc)) { -+ fbcon_decor_clear_margins(vc, info, bottom_only); -+ } else { -+ ops->clear_margins(vc, info, bottom_only); -+ } -+ } - } - - static void fbcon_cursor(struct vc_data *vc, int mode) -@@ -1819,7 +1844,7 @@ - count = vc->vc_rows; - if (softback_top) - fbcon_softback_note(vc, t, count); -- if (logo_shown >= 0) -+ if (logo_shown >= 0 || fbcon_decor_active(info, vc)) - goto redraw_up; - switch (p->scrollmode) { - case SCROLL_MOVE: -@@ -1912,6 +1937,8 @@ - count = vc->vc_rows; - if (logo_shown >= 0) - goto redraw_down; -+ if (fbcon_decor_active(info, vc)) -+ goto redraw_down; - switch (p->scrollmode) { - case SCROLL_MOVE: - fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, -@@ -2060,6 +2087,13 @@ - } - return; - } -+ -+ if (fbcon_decor_active(info, vc) && sy == dy && height == 1) { -+ /* must use slower redraw bmove to keep background pic intact */ -+ fbcon_decor_bmove_redraw(vc, info, sy, sx, dx, width); -+ return; -+ } -+ - ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, - height, width); - } -@@ -2130,8 +2164,8 @@ - var.yres = virt_h * virt_fh; - x_diff = info->var.xres - var.xres; - y_diff = info->var.yres - var.yres; -- if (x_diff < 0 || x_diff > virt_fw || -- y_diff < 0 || y_diff > virt_fh) { -+ if ((x_diff < 0 || x_diff > virt_fw || -+ y_diff < 0 || y_diff > virt_fh) && !vc->vc_decor.state) { - const struct fb_videomode *mode; - - DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); -@@ -2167,6 +2201,21 @@ - - info = registered_fb[con2fb_map[vc->vc_num]]; - ops = info->fbcon_par; -+ prev_console = ops->currcon; -+ if (prev_console != -1) -+ old_info = registered_fb[con2fb_map[prev_console]]; -+ -+#ifdef CONFIG_FB_CON_DECOR -+ if (!fbcon_decor_active_vc(vc) && info->fix.visual == FB_VISUAL_DIRECTCOLOR) { -+ struct vc_data *vc_curr = vc_cons[prev_console].d; -+ if (vc_curr && fbcon_decor_active_vc(vc_curr)) { -+ /* Clear the screen to avoid displaying funky colors during -+ * palette updates. */ -+ memset((u8*)info->screen_base + info->fix.line_length * info->var.yoffset, -+ 0, info->var.yres * info->fix.line_length); -+ } -+ } -+#endif - - if (softback_top) { - if (softback_lines) -@@ -2185,9 +2234,6 @@ - logo_shown = FBCON_LOGO_CANSHOW; - } - -- prev_console = ops->currcon; -- if (prev_console != -1) -- old_info = registered_fb[con2fb_map[prev_console]]; - /* - * FIXME: If we have multiple fbdev's loaded, we need to - * update all info->currcon. Perhaps, we can place this -@@ -2231,6 +2277,18 @@ - fbcon_del_cursor_timer(old_info); - } - -+ if (fbcon_decor_active_vc(vc)) { -+ struct vc_data *vc_curr = vc_cons[prev_console].d; -+ -+ if (!vc_curr->vc_decor.theme || -+ strcmp(vc->vc_decor.theme, vc_curr->vc_decor.theme) || -+ (fbcon_decor_active_nores(info, vc_curr) && -+ !fbcon_decor_active(info, vc_curr))) { -+ fbcon_decor_disable(vc, 0); -+ fbcon_decor_call_helper("modechange", vc->vc_num); -+ } -+ } -+ - if (fbcon_is_inactive(vc, info) || - ops->blank_state != FB_BLANK_UNBLANK) - fbcon_del_cursor_timer(info); -@@ -2339,15 +2397,20 @@ - } - } - -- if (!fbcon_is_inactive(vc, info)) { -+ if (!fbcon_is_inactive(vc, info)) { - if (ops->blank_state != blank) { - ops->blank_state = blank; - fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); - ops->cursor_flash = (!blank); - -- if (!(info->flags & FBINFO_MISC_USEREVENT)) -- if (fb_blank(info, blank)) -- fbcon_generic_blank(vc, info, blank); -+ if (!(info->flags & FBINFO_MISC_USEREVENT)) { -+ if (fb_blank(info, blank)) { -+ if (fbcon_decor_active(info, vc)) -+ fbcon_decor_blank(vc, info, blank); -+ else -+ fbcon_generic_blank(vc, info, blank); -+ } -+ } - } - - if (!blank) -@@ -2522,13 +2585,22 @@ - } - - if (resize) { -+ /* reset wrap/pan */ - int cols, rows; - - cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); -+ -+ if (fbcon_decor_active(info, vc)) { -+ info->var.xoffset = info->var.yoffset = p->yscroll = 0; -+ cols = vc->vc_decor.twidth; -+ rows = vc->vc_decor.theight; -+ } - cols /= w; - rows /= h; -+ - vc_resize(vc, cols, rows); -+ - if (CON_IS_VISIBLE(vc) && softback_buf) - fbcon_update_softback(vc); - } else if (CON_IS_VISIBLE(vc) -@@ -2657,7 +2729,11 @@ - int i, j, k, depth; - u8 val; - -- if (fbcon_is_inactive(vc, info)) -+ if (fbcon_is_inactive(vc, info) -+#ifdef CONFIG_FB_CON_DECOR -+ || vc->vc_num != fg_console -+#endif -+ ) - return -EINVAL; - - if (!CON_IS_VISIBLE(vc)) -@@ -2683,14 +2759,56 @@ - } else - fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap); - -- return fb_set_cmap(&palette_cmap, info); -+ if (fbcon_decor_active(info, vc_cons[fg_console].d) && -+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) { -+ -+ u16 *red, *green, *blue; -+ int minlen = min(min(info->var.red.length, info->var.green.length), -+ info->var.blue.length); -+ int h; -+ -+ struct fb_cmap cmap = { -+ .start = 0, -+ .len = (1 << minlen), -+ .red = NULL, -+ .green = NULL, -+ .blue = NULL, -+ .transp = NULL -+ }; -+ -+ red = kmalloc(256 * sizeof(u16) * 3, GFP_KERNEL); -+ -+ if (!red) -+ goto out; -+ -+ green = red + 256; -+ blue = green + 256; -+ cmap.red = red; -+ cmap.green = green; -+ cmap.blue = blue; -+ -+ for (i = 0; i < cmap.len; i++) { -+ red[i] = green[i] = blue[i] = (0xffff * i)/(cmap.len-1); -+ } -+ -+ h = fb_set_cmap(&cmap, info); -+ fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d); -+ kfree(red); -+ -+ return h; -+ -+ } else if (fbcon_decor_active(info, vc_cons[fg_console].d) && -+ info->var.bits_per_pixel == 8 && info->bgdecor.cmap.red != NULL) -+ fb_set_cmap(&info->bgdecor.cmap, info); -+ -+out: return fb_set_cmap(&palette_cmap, info); - } - - static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) - { - unsigned long p; - int line; -- -+ - if (vc->vc_num != fg_console || !softback_lines) - return (u16 *) (vc->vc_origin + offset); - line = offset / vc->vc_size_row; -@@ -2909,7 +3027,14 @@ - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); - cols /= vc->vc_font.width; - rows /= vc->vc_font.height; -- vc_resize(vc, cols, rows); -+ -+ if (!fbcon_decor_active_nores(info, vc)) { -+ vc_resize(vc, cols, rows); -+ } else { -+ fbcon_decor_disable(vc, 0); -+ fbcon_decor_call_helper("modechange", vc->vc_num); -+ } -+ - updatescrollmode(p, info, vc); - scrollback_max = 0; - scrollback_current = 0; -@@ -2954,7 +3079,9 @@ - rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); - cols /= vc->vc_font.width; - rows /= vc->vc_font.height; -- vc_resize(vc, cols, rows); -+ if (!fbcon_decor_active_nores(info, vc)) { -+ vc_resize(vc, cols, rows); -+ } - } - - if (fg != -1) -@@ -3596,6 +3723,7 @@ - } - } - -+ fbcon_decor_exit(); - fbcon_has_exited = 1; - } - -diff -Nur linux-3.18.9.orig/drivers/video/console/fbcondecor.c linux-3.18.9/drivers/video/console/fbcondecor.c ---- linux-3.18.9.orig/drivers/video/console/fbcondecor.c 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.9/drivers/video/console/fbcondecor.c 2015-03-15 14:34:13.076143680 -0500 -@@ -0,0 +1,555 @@ -+/* -+ * linux/drivers/video/console/fbcondecor.c -- Framebuffer console decorations -+ * -+ * Copyright (C) 2004-2009 Michal Januszewski -+ * -+ * Code based upon "Bootsplash" (C) 2001-2003 -+ * Volker Poplawski , -+ * Stefan Reinauer , -+ * Steffen Winterfeldt , -+ * Michael Schroeder , -+ * Ken Wimer . -+ * -+ * Compat ioctl support by Thorsten Klein . -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive for -+ * more details. -+ * -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "fbcon.h" -+#include "fbcondecor.h" -+ -+extern signed char con2fb_map[]; -+static int fbcon_decor_enable(struct vc_data *vc); -+char fbcon_decor_path[KMOD_PATH_LEN] = "/sbin/fbcondecor_helper"; -+static int initialized = 0; -+ -+int fbcon_decor_call_helper(char* cmd, unsigned short vc) -+{ -+ char *envp[] = { -+ "HOME=/", -+ "PATH=/sbin:/bin", -+ NULL -+ }; -+ -+ char tfb[5]; -+ char tcons[5]; -+ unsigned char fb = (int) con2fb_map[vc]; -+ -+ char *argv[] = { -+ fbcon_decor_path, -+ "2", -+ cmd, -+ tcons, -+ tfb, -+ vc_cons[vc].d->vc_decor.theme, -+ NULL -+ }; -+ -+ snprintf(tfb,5,"%d",fb); -+ snprintf(tcons,5,"%d",vc); -+ -+ return call_usermodehelper(fbcon_decor_path, argv, envp, UMH_WAIT_EXEC); -+} -+ -+/* Disables fbcondecor on a virtual console; called with console sem held. */ -+int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) -+{ -+ struct fb_info* info; -+ -+ if (!vc->vc_decor.state) -+ return -EINVAL; -+ -+ info = registered_fb[(int) con2fb_map[vc->vc_num]]; -+ -+ if (info == NULL) -+ return -EINVAL; -+ -+ vc->vc_decor.state = 0; -+ vc_resize(vc, info->var.xres / vc->vc_font.width, -+ info->var.yres / vc->vc_font.height); -+ -+ if (fg_console == vc->vc_num && redraw) { -+ redraw_screen(vc, 0); -+ update_region(vc, vc->vc_origin + -+ vc->vc_size_row * vc->vc_top, -+ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); -+ } -+ -+ printk(KERN_INFO "fbcondecor: switched decor state to 'off' on console %d\n", -+ vc->vc_num); -+ -+ return 0; -+} -+ -+/* Enables fbcondecor on a virtual console; called with console sem held. */ -+static int fbcon_decor_enable(struct vc_data *vc) -+{ -+ struct fb_info* info; -+ -+ info = registered_fb[(int) con2fb_map[vc->vc_num]]; -+ -+ if (vc->vc_decor.twidth == 0 || vc->vc_decor.theight == 0 || -+ info == NULL || vc->vc_decor.state || (!info->bgdecor.data && -+ vc->vc_num == fg_console)) -+ return -EINVAL; -+ -+ vc->vc_decor.state = 1; -+ vc_resize(vc, vc->vc_decor.twidth / vc->vc_font.width, -+ vc->vc_decor.theight / vc->vc_font.height); -+ -+ if (fg_console == vc->vc_num) { -+ redraw_screen(vc, 0); -+ update_region(vc, vc->vc_origin + -+ vc->vc_size_row * vc->vc_top, -+ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); -+ fbcon_decor_clear_margins(vc, info, 0); -+ } -+ -+ printk(KERN_INFO "fbcondecor: switched decor state to 'on' on console %d\n", -+ vc->vc_num); -+ -+ return 0; -+} -+ -+static inline int fbcon_decor_ioctl_dosetstate(struct vc_data *vc, unsigned int state, unsigned char origin) -+{ -+ int ret; -+ -+// if (origin == FBCON_DECOR_IO_ORIG_USER) -+ console_lock(); -+ if (!state) -+ ret = fbcon_decor_disable(vc, 1); -+ else -+ ret = fbcon_decor_enable(vc); -+// if (origin == FBCON_DECOR_IO_ORIG_USER) -+ console_unlock(); -+ -+ return ret; -+} -+ -+static inline void fbcon_decor_ioctl_dogetstate(struct vc_data *vc, unsigned int *state) -+{ -+ *state = vc->vc_decor.state; -+} -+ -+static int fbcon_decor_ioctl_dosetcfg(struct vc_data *vc, struct vc_decor *cfg, unsigned char origin) -+{ -+ struct fb_info *info; -+ int len; -+ char *tmp; -+ -+ info = registered_fb[(int) con2fb_map[vc->vc_num]]; -+ -+ if (info == NULL || !cfg->twidth || !cfg->theight || -+ cfg->tx + cfg->twidth > info->var.xres || -+ cfg->ty + cfg->theight > info->var.yres) -+ return -EINVAL; -+ -+ len = strlen_user(cfg->theme); -+ if (!len || len > FBCON_DECOR_THEME_LEN) -+ return -EINVAL; -+ tmp = kmalloc(len, GFP_KERNEL); -+ if (!tmp) -+ return -ENOMEM; -+ if (copy_from_user(tmp, (void __user *)cfg->theme, len)) -+ return -EFAULT; -+ cfg->theme = tmp; -+ cfg->state = 0; -+ -+ /* If this ioctl is a response to a request from kernel, the console sem -+ * is already held; we also don't need to disable decor because either the -+ * new config and background picture will be successfully loaded, and the -+ * decor will stay on, or in case of a failure it'll be turned off in fbcon. */ -+// if (origin == FBCON_DECOR_IO_ORIG_USER) { -+ console_lock(); -+ if (vc->vc_decor.state) -+ fbcon_decor_disable(vc, 1); -+// } -+ -+ if (vc->vc_decor.theme) -+ kfree(vc->vc_decor.theme); -+ -+ vc->vc_decor = *cfg; -+ -+// if (origin == FBCON_DECOR_IO_ORIG_USER) -+ console_unlock(); -+ -+ printk(KERN_INFO "fbcondecor: console %d using theme '%s'\n", -+ vc->vc_num, vc->vc_decor.theme); -+ return 0; -+} -+ -+static int fbcon_decor_ioctl_dogetcfg(struct vc_data *vc, struct vc_decor *decor) -+{ -+ char __user *tmp; -+ -+ tmp = decor->theme; -+ *decor = vc->vc_decor; -+ decor->theme = tmp; -+ -+ if (vc->vc_decor.theme) { -+ if (copy_to_user(tmp, vc->vc_decor.theme, strlen(vc->vc_decor.theme) + 1)) -+ return -EFAULT; -+ } else -+ if (put_user(0, tmp)) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static int fbcon_decor_ioctl_dosetpic(struct vc_data *vc, struct fb_image *img, unsigned char origin) -+{ -+ struct fb_info *info; -+ int len; -+ u8 *tmp; -+ -+ if (vc->vc_num != fg_console) -+ return -EINVAL; -+ -+ info = registered_fb[(int) con2fb_map[vc->vc_num]]; -+ -+ if (info == NULL) -+ return -EINVAL; -+ -+ if (img->width != info->var.xres || img->height != info->var.yres) { -+ printk(KERN_ERR "fbcondecor: picture dimensions mismatch\n"); -+ printk(KERN_ERR "%dx%d vs %dx%d\n", img->width, img->height, info->var.xres, info->var.yres); -+ return -EINVAL; -+ } -+ -+ if (img->depth != info->var.bits_per_pixel) { -+ printk(KERN_ERR "fbcondecor: picture depth mismatch\n"); -+ return -EINVAL; -+ } -+ -+ if (img->depth == 8) { -+ if (!img->cmap.len || !img->cmap.red || !img->cmap.green || -+ !img->cmap.blue) -+ return -EINVAL; -+ -+ tmp = vmalloc(img->cmap.len * 3 * 2); -+ if (!tmp) -+ return -ENOMEM; -+ -+ if (copy_from_user(tmp, -+ (void __user*)img->cmap.red, (img->cmap.len << 1)) || -+ copy_from_user(tmp + (img->cmap.len << 1), -+ (void __user*)img->cmap.green, (img->cmap.len << 1)) || -+ copy_from_user(tmp + (img->cmap.len << 2), -+ (void __user*)img->cmap.blue, (img->cmap.len << 1))) { -+ vfree(tmp); -+ return -EFAULT; -+ } -+ -+ img->cmap.transp = NULL; -+ img->cmap.red = (u16*)tmp; -+ img->cmap.green = img->cmap.red + img->cmap.len; -+ img->cmap.blue = img->cmap.green + img->cmap.len; -+ } else { -+ img->cmap.red = NULL; -+ } -+ -+ len = ((img->depth + 7) >> 3) * img->width * img->height; -+ -+ /* -+ * Allocate an additional byte so that we never go outside of the -+ * buffer boundaries in the rendering functions in a 24 bpp mode. -+ */ -+ tmp = vmalloc(len + 1); -+ -+ if (!tmp) -+ goto out; -+ -+ if (copy_from_user(tmp, (void __user*)img->data, len)) -+ goto out; -+ -+ img->data = tmp; -+ -+ /* If this ioctl is a response to a request from kernel, the console sem -+ * is already held. */ -+// if (origin == FBCON_DECOR_IO_ORIG_USER) -+ console_lock(); -+ -+ if (info->bgdecor.data) -+ vfree((u8*)info->bgdecor.data); -+ if (info->bgdecor.cmap.red) -+ vfree(info->bgdecor.cmap.red); -+ -+ info->bgdecor = *img; -+ -+ if (fbcon_decor_active_vc(vc) && fg_console == vc->vc_num) { -+ redraw_screen(vc, 0); -+ update_region(vc, vc->vc_origin + -+ vc->vc_size_row * vc->vc_top, -+ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); -+ fbcon_decor_clear_margins(vc, info, 0); -+ } -+ -+// if (origin == FBCON_DECOR_IO_ORIG_USER) -+ console_unlock(); -+ -+ return 0; -+ -+out: if (img->cmap.red) -+ vfree(img->cmap.red); -+ -+ if (tmp) -+ vfree(tmp); -+ return -ENOMEM; -+} -+ -+static long fbcon_decor_ioctl(struct file *filp, u_int cmd, u_long arg) -+{ -+ struct fbcon_decor_iowrapper __user *wrapper = (void __user*) arg; -+ struct vc_data *vc = NULL; -+ unsigned short vc_num = 0; -+ unsigned char origin = 0; -+ void __user *data = NULL; -+ -+ if (!access_ok(VERIFY_READ, wrapper, -+ sizeof(struct fbcon_decor_iowrapper))) -+ return -EFAULT; -+ -+ __get_user(vc_num, &wrapper->vc); -+ __get_user(origin, &wrapper->origin); -+ __get_user(data, &wrapper->data); -+ -+ if (!vc_cons_allocated(vc_num)) -+ return -EINVAL; -+ -+ vc = vc_cons[vc_num].d; -+ -+ switch (cmd) { -+ case FBIOCONDECOR_SETPIC: -+ { -+ struct fb_image img; -+ if (copy_from_user(&img, (struct fb_image __user *)data, sizeof(struct fb_image))) -+ return -EFAULT; -+ -+ return fbcon_decor_ioctl_dosetpic(vc, &img, origin); -+ } -+ case FBIOCONDECOR_SETCFG: -+ { -+ struct vc_decor cfg; -+ if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor))) -+ return -EFAULT; -+ -+ return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin); -+ } -+ case FBIOCONDECOR_GETCFG: -+ { -+ int rval; -+ struct vc_decor cfg; -+ -+ if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor))) -+ return -EFAULT; -+ -+ rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg); -+ -+ if (copy_to_user(data, &cfg, sizeof(struct vc_decor))) -+ return -EFAULT; -+ return rval; -+ } -+ case FBIOCONDECOR_SETSTATE: -+ { -+ unsigned int state = 0; -+ if (get_user(state, (unsigned int __user *)data)) -+ return -EFAULT; -+ return fbcon_decor_ioctl_dosetstate(vc, state, origin); -+ } -+ case FBIOCONDECOR_GETSTATE: -+ { -+ unsigned int state = 0; -+ fbcon_decor_ioctl_dogetstate(vc, &state); -+ return put_user(state, (unsigned int __user *)data); -+ } -+ -+ default: -+ return -ENOIOCTLCMD; -+ } -+} -+ -+#ifdef CONFIG_COMPAT -+ -+static long fbcon_decor_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { -+ -+ struct fbcon_decor_iowrapper32 __user *wrapper = (void __user *)arg; -+ struct vc_data *vc = NULL; -+ unsigned short vc_num = 0; -+ unsigned char origin = 0; -+ compat_uptr_t data_compat = 0; -+ void __user *data = NULL; -+ -+ if (!access_ok(VERIFY_READ, wrapper, -+ sizeof(struct fbcon_decor_iowrapper32))) -+ return -EFAULT; -+ -+ __get_user(vc_num, &wrapper->vc); -+ __get_user(origin, &wrapper->origin); -+ __get_user(data_compat, &wrapper->data); -+ data = compat_ptr(data_compat); -+ -+ if (!vc_cons_allocated(vc_num)) -+ return -EINVAL; -+ -+ vc = vc_cons[vc_num].d; -+ -+ switch (cmd) { -+ case FBIOCONDECOR_SETPIC32: -+ { -+ struct fb_image32 img_compat; -+ struct fb_image img; -+ -+ if (copy_from_user(&img_compat, (struct fb_image32 __user *)data, sizeof(struct fb_image32))) -+ return -EFAULT; -+ -+ fb_image_from_compat(img, img_compat); -+ -+ return fbcon_decor_ioctl_dosetpic(vc, &img, origin); -+ } -+ -+ case FBIOCONDECOR_SETCFG32: -+ { -+ struct vc_decor32 cfg_compat; -+ struct vc_decor cfg; -+ -+ if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32))) -+ return -EFAULT; -+ -+ vc_decor_from_compat(cfg, cfg_compat); -+ -+ return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin); -+ } -+ -+ case FBIOCONDECOR_GETCFG32: -+ { -+ int rval; -+ struct vc_decor32 cfg_compat; -+ struct vc_decor cfg; -+ -+ if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32))) -+ return -EFAULT; -+ cfg.theme = compat_ptr(cfg_compat.theme); -+ -+ rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg); -+ -+ vc_decor_to_compat(cfg_compat, cfg); -+ -+ if (copy_to_user((struct vc_decor32 __user *)data, &cfg_compat, sizeof(struct vc_decor32))) -+ return -EFAULT; -+ return rval; -+ } -+ -+ case FBIOCONDECOR_SETSTATE32: -+ { -+ compat_uint_t state_compat = 0; -+ unsigned int state = 0; -+ -+ if (get_user(state_compat, (compat_uint_t __user *)data)) -+ return -EFAULT; -+ -+ state = (unsigned int)state_compat; -+ -+ return fbcon_decor_ioctl_dosetstate(vc, state, origin); -+ } -+ -+ case FBIOCONDECOR_GETSTATE32: -+ { -+ compat_uint_t state_compat = 0; -+ unsigned int state = 0; -+ -+ fbcon_decor_ioctl_dogetstate(vc, &state); -+ state_compat = (compat_uint_t)state; -+ -+ return put_user(state_compat, (compat_uint_t __user *)data); -+ } -+ -+ default: -+ return -ENOIOCTLCMD; -+ } -+} -+#else -+ #define fbcon_decor_compat_ioctl NULL -+#endif -+ -+static struct file_operations fbcon_decor_ops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = fbcon_decor_ioctl, -+ .compat_ioctl = fbcon_decor_compat_ioctl -+}; -+ -+static struct miscdevice fbcon_decor_dev = { -+ .minor = MISC_DYNAMIC_MINOR, -+ .name = "fbcondecor", -+ .fops = &fbcon_decor_ops -+}; -+ -+void fbcon_decor_reset() -+{ -+ int i; -+ -+ for (i = 0; i < num_registered_fb; i++) { -+ registered_fb[i]->bgdecor.data = NULL; -+ registered_fb[i]->bgdecor.cmap.red = NULL; -+ } -+ -+ for (i = 0; i < MAX_NR_CONSOLES && vc_cons[i].d; i++) { -+ vc_cons[i].d->vc_decor.state = vc_cons[i].d->vc_decor.twidth = -+ vc_cons[i].d->vc_decor.theight = 0; -+ vc_cons[i].d->vc_decor.theme = NULL; -+ } -+ -+ return; -+} -+ -+int fbcon_decor_init() -+{ -+ int i; -+ -+ fbcon_decor_reset(); -+ -+ if (initialized) -+ return 0; -+ -+ i = misc_register(&fbcon_decor_dev); -+ if (i) { -+ printk(KERN_ERR "fbcondecor: failed to register device\n"); -+ return i; -+ } -+ -+ fbcon_decor_call_helper("init", 0); -+ initialized = 1; -+ return 0; -+} -+ -+int fbcon_decor_exit(void) -+{ -+ fbcon_decor_reset(); -+ return 0; -+} -+ -+EXPORT_SYMBOL(fbcon_decor_path); -diff -Nur linux-3.18.9.orig/drivers/video/console/fbcondecor.h linux-3.18.9/drivers/video/console/fbcondecor.h ---- linux-3.18.9.orig/drivers/video/console/fbcondecor.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.9/drivers/video/console/fbcondecor.h 2015-03-15 14:34:13.076143680 -0500 -@@ -0,0 +1,79 @@ -+/* -+ * linux/drivers/video/console/fbcondecor.h -- Framebuffer Console Decoration headers -+ * -+ * Copyright (C) 2004 Michal Januszewski -+ * -+ */ -+ -+#ifndef __FBCON_DECOR_H -+#define __FBCON_DECOR_H -+ -+#ifndef _LINUX_FB_H -+#include -+#endif -+ -+/* This is needed for vc_cons in fbcmap.c */ -+#include -+ -+struct fb_cursor; -+struct fb_info; -+struct vc_data; -+ -+#ifdef CONFIG_FB_CON_DECOR -+/* fbcondecor.c */ -+int fbcon_decor_init(void); -+void fbcon_decor_reset(void); -+int fbcon_decor_exit(void); -+int fbcon_decor_call_helper(char* cmd, unsigned short cons); -+int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw); -+ -+/* cfbcondecor.c */ -+void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx); -+void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor); -+void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width); -+void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only); -+void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank); -+void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width); -+void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes, int srclinesbytes, int bpp); -+void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc); -+ -+/* vt.c */ -+void acquire_console_sem(void); -+void release_console_sem(void); -+void do_unblank_screen(int entering_gfx); -+ -+/* struct vc_data *y */ -+#define fbcon_decor_active_vc(y) (y->vc_decor.state && y->vc_decor.theme) -+ -+/* struct fb_info *x, struct vc_data *y */ -+#define fbcon_decor_active_nores(x,y) (x->bgdecor.data && fbcon_decor_active_vc(y)) -+ -+/* struct fb_info *x, struct vc_data *y */ -+#define fbcon_decor_active(x,y) (fbcon_decor_active_nores(x,y) && \ -+ x->bgdecor.width == x->var.xres && \ -+ x->bgdecor.height == x->var.yres && \ -+ x->bgdecor.depth == x->var.bits_per_pixel) -+ -+ -+#else /* CONFIG_FB_CON_DECOR */ -+ -+static inline void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx) {} -+static inline void fbcon_decor_putc(struct vc_data *vc, struct fb_info *info, int c, int ypos, int xpos) {} -+static inline void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) {} -+static inline void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) {} -+static inline void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only) {} -+static inline void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) {} -+static inline void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) {} -+static inline void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) {} -+static inline int fbcon_decor_call_helper(char* cmd, unsigned short cons) { return 0; } -+static inline int fbcon_decor_init(void) { return 0; } -+static inline int fbcon_decor_exit(void) { return 0; } -+static inline int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) { return 0; } -+ -+#define fbcon_decor_active_vc(y) (0) -+#define fbcon_decor_active_nores(x,y) (0) -+#define fbcon_decor_active(x,y) (0) -+ -+#endif /* CONFIG_FB_CON_DECOR */ -+ -+#endif /* __FBCON_DECOR_H */ -diff -Nur linux-3.18.9.orig/drivers/video/console/Kconfig linux-3.18.9/drivers/video/console/Kconfig ---- linux-3.18.9.orig/drivers/video/console/Kconfig 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/drivers/video/console/Kconfig 2015-03-15 14:34:13.068143682 -0500 -@@ -126,6 +126,19 @@ - such that other users of the framebuffer will remain normally - oriented. - -+config FB_CON_DECOR -+ bool "Support for the Framebuffer Console Decorations" -+ depends on FRAMEBUFFER_CONSOLE=y && !FB_TILEBLITTING -+ default n -+ ---help--- -+ This option enables support for framebuffer console decorations which -+ makes it possible to display images in the background of the system -+ consoles. Note that userspace utilities are necessary in order to take -+ advantage of these features. Refer to Documentation/fb/fbcondecor.txt -+ for more information. -+ -+ If unsure, say N. -+ - config STI_CONSOLE - bool "STI text console" - depends on PARISC -diff -Nur linux-3.18.9.orig/drivers/video/console/Makefile linux-3.18.9/drivers/video/console/Makefile ---- linux-3.18.9.orig/drivers/video/console/Makefile 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/drivers/video/console/Makefile 2015-03-15 14:34:13.068143682 -0500 -@@ -16,4 +16,5 @@ - fbcon_ccw.o - endif - -+obj-$(CONFIG_FB_CON_DECOR) += fbcondecor.o cfbcondecor.o - obj-$(CONFIG_FB_STI) += sticore.o -diff -Nur linux-3.18.9.orig/drivers/video/fbdev/core/fbcmap.c linux-3.18.9/drivers/video/fbdev/core/fbcmap.c ---- linux-3.18.9.orig/drivers/video/fbdev/core/fbcmap.c 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/drivers/video/fbdev/core/fbcmap.c 2015-03-15 14:34:13.076143680 -0500 -@@ -17,6 +17,8 @@ - #include - #include - -+#include "../../console/fbcondecor.h" -+ - static u16 red2[] __read_mostly = { - 0x0000, 0xaaaa - }; -@@ -257,6 +259,10 @@ - if (rc == 0) - fb_copy_cmap(cmap, &info->cmap); - -+ if (fbcon_decor_active(info, vc_cons[fg_console].d) && -+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) -+ fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d); -+ - return rc; - } - -diff -Nur linux-3.18.9.orig/include/linux/console_decor.h linux-3.18.9/include/linux/console_decor.h ---- linux-3.18.9.orig/include/linux/console_decor.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.9/include/linux/console_decor.h 2015-03-15 14:34:13.076143680 -0500 -@@ -0,0 +1,46 @@ -+#ifndef _LINUX_CONSOLE_DECOR_H_ -+#define _LINUX_CONSOLE_DECOR_H_ 1 -+ -+/* A structure used by the framebuffer console decorations (drivers/video/console/fbcondecor.c) */ -+struct vc_decor { -+ __u8 bg_color; /* The color that is to be treated as transparent */ -+ __u8 state; /* Current decor state: 0 = off, 1 = on */ -+ __u16 tx, ty; /* Top left corner coordinates of the text field */ -+ __u16 twidth, theight; /* Width and height of the text field */ -+ char* theme; -+}; -+ -+#ifdef __KERNEL__ -+#ifdef CONFIG_COMPAT -+#include -+ -+struct vc_decor32 { -+ __u8 bg_color; /* The color that is to be treated as transparent */ -+ __u8 state; /* Current decor state: 0 = off, 1 = on */ -+ __u16 tx, ty; /* Top left corner coordinates of the text field */ -+ __u16 twidth, theight; /* Width and height of the text field */ -+ compat_uptr_t theme; -+}; -+ -+#define vc_decor_from_compat(to, from) \ -+ (to).bg_color = (from).bg_color; \ -+ (to).state = (from).state; \ -+ (to).tx = (from).tx; \ -+ (to).ty = (from).ty; \ -+ (to).twidth = (from).twidth; \ -+ (to).theight = (from).theight; \ -+ (to).theme = compat_ptr((from).theme) -+ -+#define vc_decor_to_compat(to, from) \ -+ (to).bg_color = (from).bg_color; \ -+ (to).state = (from).state; \ -+ (to).tx = (from).tx; \ -+ (to).ty = (from).ty; \ -+ (to).twidth = (from).twidth; \ -+ (to).theight = (from).theight; \ -+ (to).theme = ptr_to_compat((from).theme) -+ -+#endif /* CONFIG_COMPAT */ -+#endif /* __KERNEL__ */ -+ -+#endif -diff -Nur linux-3.18.9.orig/include/linux/console_struct.h linux-3.18.9/include/linux/console_struct.h ---- linux-3.18.9.orig/include/linux/console_struct.h 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/include/linux/console_struct.h 2015-03-15 14:34:13.076143680 -0500 -@@ -20,6 +20,7 @@ - struct uni_pagedir; - - #define NPAR 16 -+#include - - struct vc_data { - struct tty_port port; /* Upper level data */ -@@ -108,6 +109,8 @@ - struct uni_pagedir *vc_uni_pagedir; - struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */ - bool vc_panic_force_write; /* when oops/panic this VC can accept forced output/blanking */ -+ -+ struct vc_decor vc_decor; - /* additional information is in vt_kern.h */ - }; - -diff -Nur linux-3.18.9.orig/include/linux/fb.h linux-3.18.9/include/linux/fb.h ---- linux-3.18.9.orig/include/linux/fb.h 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/include/linux/fb.h 2015-03-15 14:34:13.080143679 -0500 -@@ -220,6 +220,34 @@ - }; - #endif - -+#ifdef __KERNEL__ -+#ifdef CONFIG_COMPAT -+struct fb_image32 { -+ __u32 dx; /* Where to place image */ -+ __u32 dy; -+ __u32 width; /* Size of image */ -+ __u32 height; -+ __u32 fg_color; /* Only used when a mono bitmap */ -+ __u32 bg_color; -+ __u8 depth; /* Depth of the image */ -+ const compat_uptr_t data; /* Pointer to image data */ -+ struct fb_cmap32 cmap; /* color map info */ -+}; -+ -+#define fb_image_from_compat(to, from) \ -+ (to).dx = (from).dx; \ -+ (to).dy = (from).dy; \ -+ (to).width = (from).width; \ -+ (to).height = (from).height; \ -+ (to).fg_color = (from).fg_color; \ -+ (to).bg_color = (from).bg_color; \ -+ (to).depth = (from).depth; \ -+ (to).data = compat_ptr((from).data); \ -+ fb_cmap_from_compat((to).cmap, (from).cmap) -+ -+#endif /* CONFIG_COMPAT */ -+#endif /* __KERNEL__ */ -+ - /* - * Frame buffer operations - * -@@ -490,6 +518,9 @@ - #define FBINFO_STATE_SUSPENDED 1 - u32 state; /* Hardware state i.e suspend */ - void *fbcon_par; /* fbcon use-only private area */ -+ -+ struct fb_image bgdecor; -+ - /* From here on everything is device dependent */ - void *par; - /* we need the PCI or similar aperture base/size not -diff -Nur linux-3.18.9.orig/include/uapi/linux/fb.h linux-3.18.9/include/uapi/linux/fb.h ---- linux-3.18.9.orig/include/uapi/linux/fb.h 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/include/uapi/linux/fb.h 2015-03-15 14:34:13.080143679 -0500 -@@ -8,6 +8,25 @@ - - #define FB_MAX 32 /* sufficient for now */ - -+struct fbcon_decor_iowrapper -+{ -+ unsigned short vc; /* Virtual console */ -+ unsigned char origin; /* Point of origin of the request */ -+ void *data; -+}; -+ -+#ifdef __KERNEL__ -+#ifdef CONFIG_COMPAT -+#include -+struct fbcon_decor_iowrapper32 -+{ -+ unsigned short vc; /* Virtual console */ -+ unsigned char origin; /* Point of origin of the request */ -+ compat_uptr_t data; -+}; -+#endif /* CONFIG_COMPAT */ -+#endif /* __KERNEL__ */ -+ - /* ioctls - 0x46 is 'F' */ - #define FBIOGET_VSCREENINFO 0x4600 -@@ -35,6 +54,25 @@ - #define FBIOGET_DISPINFO 0x4618 - #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) - -+#define FBIOCONDECOR_SETCFG _IOWR('F', 0x19, struct fbcon_decor_iowrapper) -+#define FBIOCONDECOR_GETCFG _IOR('F', 0x1A, struct fbcon_decor_iowrapper) -+#define FBIOCONDECOR_SETSTATE _IOWR('F', 0x1B, struct fbcon_decor_iowrapper) -+#define FBIOCONDECOR_GETSTATE _IOR('F', 0x1C, struct fbcon_decor_iowrapper) -+#define FBIOCONDECOR_SETPIC _IOWR('F', 0x1D, struct fbcon_decor_iowrapper) -+#ifdef __KERNEL__ -+#ifdef CONFIG_COMPAT -+#define FBIOCONDECOR_SETCFG32 _IOWR('F', 0x19, struct fbcon_decor_iowrapper32) -+#define FBIOCONDECOR_GETCFG32 _IOR('F', 0x1A, struct fbcon_decor_iowrapper32) -+#define FBIOCONDECOR_SETSTATE32 _IOWR('F', 0x1B, struct fbcon_decor_iowrapper32) -+#define FBIOCONDECOR_GETSTATE32 _IOR('F', 0x1C, struct fbcon_decor_iowrapper32) -+#define FBIOCONDECOR_SETPIC32 _IOWR('F', 0x1D, struct fbcon_decor_iowrapper32) -+#endif /* CONFIG_COMPAT */ -+#endif /* __KERNEL__ */ -+ -+#define FBCON_DECOR_THEME_LEN 128 /* Maximum lenght of a theme name */ -+#define FBCON_DECOR_IO_ORIG_KERNEL 0 /* Kernel ioctl origin */ -+#define FBCON_DECOR_IO_ORIG_USER 1 /* User ioctl origin */ -+ - #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ - #define FB_TYPE_PLANES 1 /* Non interleaved planes */ - #define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes */ -@@ -277,6 +315,29 @@ - __u32 reserved[4]; /* Reserved for future compatibility */ - }; - -+#ifdef __KERNEL__ -+#ifdef CONFIG_COMPAT -+struct fb_cmap32 { -+ __u32 start; -+ __u32 len; /* Number of entries */ -+ compat_uptr_t red; /* Red values */ -+ compat_uptr_t green; -+ compat_uptr_t blue; -+ compat_uptr_t transp; /* transparency, can be NULL */ -+}; -+ -+#define fb_cmap_from_compat(to, from) \ -+ (to).start = (from).start; \ -+ (to).len = (from).len; \ -+ (to).red = compat_ptr((from).red); \ -+ (to).green = compat_ptr((from).green); \ -+ (to).blue = compat_ptr((from).blue); \ -+ (to).transp = compat_ptr((from).transp) -+ -+#endif /* CONFIG_COMPAT */ -+#endif /* __KERNEL__ */ -+ -+ - struct fb_cmap { - __u32 start; /* First entry */ - __u32 len; /* Number of entries */ -diff -Nur linux-3.18.9.orig/kernel/sysctl.c linux-3.18.9/kernel/sysctl.c ---- linux-3.18.9.orig/kernel/sysctl.c 2015-03-06 16:53:42.000000000 -0600 -+++ linux-3.18.9/kernel/sysctl.c 2015-03-15 14:34:13.080143679 -0500 -@@ -145,6 +145,10 @@ - static unsigned long hung_task_timeout_max = (LONG_MAX/HZ); - #endif - -+#ifdef CONFIG_FB_CON_DECOR -+extern char fbcon_decor_path[]; -+#endif -+ - #ifdef CONFIG_INOTIFY_USER - #include - #endif -@@ -257,6 +261,15 @@ - .mode = 0555, - .child = dev_table, - }, -+#ifdef CONFIG_FB_CON_DECOR -+ { -+ .procname = "fbcondecor", -+ .data = &fbcon_decor_path, -+ .maxlen = KMOD_PATH_LEN, -+ .mode = 0644, -+ .proc_handler = &proc_dostring, -+ }, -+#endif - { } - }; - diff --git a/target/linux/patches/3.18.14/gemalto.patch b/target/linux/patches/3.18.14/gemalto.patch deleted file mode 100644 index 65f7af1d7..000000000 --- a/target/linux/patches/3.18.14/gemalto.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c linux-2.6.36/drivers/serial/8250/serial_cs.c ---- linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c 2010-10-20 22:30:22.000000000 +0200 -+++ linux-2.6.36/drivers/tty/serial/8250/serial_cs.c 2010-12-13 23:03:40.000000000 +0100 -@@ -794,6 +794,7 @@ - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), -+ PCMCIA_DEVICE_MANF_CARD(0x0157, 0x0100), /* Gemalto SCR */ - PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ - PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ - PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), diff --git a/target/linux/patches/3.18.14/initramfs-nosizelimit.patch b/target/linux/patches/3.18.14/initramfs-nosizelimit.patch deleted file mode 100644 index 40d2f6bd8..000000000 --- a/target/linux/patches/3.18.14/initramfs-nosizelimit.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 9a18df7a71bfa620b1278777d64783a359d7eb4e Mon Sep 17 00:00:00 2001 -From: Thorsten Glaser -Date: Sun, 4 May 2014 01:37:54 +0200 -Subject: [PATCH] mount tmpfs-as-rootfs (initramfs) with -o - nr_blocks=0,nr_inodes=0 - -I would have preferred to write this patch to be able to pass -rootflags=nr_blocks=0,nr_inodes=0 on the kernel command line, -and then hand these rootflags over to the initramfs (tmpfs) -mount in the same way the kernel hands them over to the block -device rootfs mount. But at least the Debian/m68k initrd also -parses $rootflags from the environment and adds it to the call -to the user-space mount for the eventual root device, which -would make the kernel command line rootflags option be used in -both places (tmpfs and e.g. ext4) which is guaranteed to error -out in at least one of them. - -This change is intended to aid people in a setup where the -initrd is the final root filesystem, i.e. not mounted over. -This is especially useful in automated tests running on qemu -for boards with constrained memory (e.g. 64 MiB on sh4). - -Considering that the initramfs is normally emptied out then -overmounted, this change is probably safe for setups where -initramfs just hosts early userspace, too, since the tmpfs -backing it is not accessible any more later on, AFAICT. - -Signed-off-by: Thorsten Glaser ---- - init/do_mounts.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/init/do_mounts.c b/init/do_mounts.c -index 82f2288..55a4cfe 100644 ---- a/init/do_mounts.c -+++ b/init/do_mounts.c -@@ -594,6 +594,7 @@ out: - } - - static bool is_tmpfs; -+static char tmpfs_rootflags[] = "nr_blocks=0,nr_inodes=0"; - static struct dentry *rootfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) - { -@@ -606,6 +607,9 @@ static struct dentry *rootfs_mount(struct file_system_type *fs_type, - if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) - fill = shmem_fill_super; - -+ if (is_tmpfs) -+ data = tmpfs_rootflags; -+ - return mount_nodev(fs_type, flags, data, fill); - } - --- -2.0.0.rc0 - diff --git a/target/linux/patches/3.18.14/lemote-rfkill.patch b/target/linux/patches/3.18.14/lemote-rfkill.patch deleted file mode 100644 index a61488434..000000000 --- a/target/linux/patches/3.18.14/lemote-rfkill.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -Nur linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c ---- linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-19 00:15:34.000000000 +0100 -+++ linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-27 23:29:46.000000000 +0200 -@@ -22,6 +22,9 @@ - - static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) - { -+#ifdef CONFIG_LEMOTE_MACH2F -+ return 1; -+#else - u8 gpio; - - gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); -@@ -29,6 +32,7 @@ - gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); - - return gpio & priv->rfkill_mask; -+#endif - } - - void rtl8187_rfkill_init(struct ieee80211_hw *hw) diff --git a/target/linux/patches/3.18.14/microblaze-ethernet.patch b/target/linux/patches/3.18.14/microblaze-ethernet.patch deleted file mode 100644 index 742ab477e..000000000 --- a/target/linux/patches/3.18.14/microblaze-ethernet.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c ---- linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-11-29 19:42:37.000000000 +0100 -+++ linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-12-23 20:01:14.000000000 +0100 -@@ -1282,6 +1282,7 @@ - { .compatible = "xlnx,opb-ethernetlite-1.01.b", }, - { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, - { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, -+ { .compatible = "xlnx,xps-ethernetlite-2.00.b", }, - { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, - { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, - { /* end of list */ }, diff --git a/target/linux/patches/3.18.14/mkpiggy.patch b/target/linux/patches/3.18.14/mkpiggy.patch deleted file mode 100644 index 751678b74..000000000 --- a/target/linux/patches/3.18.14/mkpiggy.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff -Nur linux-3.13.3.orig/arch/x86/boot/compressed/mkpiggy.c linux-3.13.3/arch/x86/boot/compressed/mkpiggy.c ---- linux-3.13.3.orig/arch/x86/boot/compressed/mkpiggy.c 2014-02-13 23:00:14.000000000 +0100 -+++ linux-3.13.3/arch/x86/boot/compressed/mkpiggy.c 2014-02-17 11:09:06.000000000 +0100 -@@ -29,7 +29,14 @@ - #include - #include - #include --#include -+ -+static uint32_t getle32(const void *p) -+{ -+ const uint8_t *cp = p; -+ -+ return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + -+ ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); -+} - - int main(int argc, char *argv[]) - { -@@ -63,7 +70,7 @@ - } - - ilen = ftell(f); -- olen = get_unaligned_le32(&olen); -+ olen = getle32(&olen); - - /* - * Now we have the input (compressed) and output (uncompressed) diff --git a/target/linux/patches/3.18.14/mtd-rootfs.patch b/target/linux/patches/3.18.14/mtd-rootfs.patch deleted file mode 100644 index 775d5fc80..000000000 --- a/target/linux/patches/3.18.14/mtd-rootfs.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff -Nur linux-3.5.orig//drivers/mtd/mtdpart.c linux-3.5/drivers/mtd/mtdpart.c ---- linux-3.5.orig//drivers/mtd/mtdpart.c 2012-07-21 22:58:29.000000000 +0200 -+++ linux-3.5/drivers/mtd/mtdpart.c 2012-07-31 23:59:07.000000000 +0200 -@@ -30,6 +30,7 @@ - #include - #include - #include -+#include - - #include "mtdcore.h" - -@@ -637,6 +638,14 @@ - if (IS_ERR(slave)) - return PTR_ERR(slave); - -+ if (strcmp(parts[i].name, "rootfs") == 0) { -+ if (ROOT_DEV == 0) { -+ printk(KERN_NOTICE "mtd: partition \"rootfs\" " -+ "set to be root filesystem\n"); -+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, i); -+ } -+ } -+ - mutex_lock(&mtd_partitions_mutex); - list_add(&slave->list, &mtd_partitions); - mutex_unlock(&mtd_partitions_mutex); diff --git a/target/linux/patches/3.18.14/nfsv3-tcp.patch b/target/linux/patches/3.18.14/nfsv3-tcp.patch deleted file mode 100644 index d5e07e1c2..000000000 --- a/target/linux/patches/3.18.14/nfsv3-tcp.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-3.15-rc5.orig/fs/nfs/nfsroot.c linux-3.15-rc5/fs/nfs/nfsroot.c ---- linux-3.15-rc5.orig/fs/nfs/nfsroot.c 2014-05-09 22:10:52.000000000 +0200 -+++ linux-3.15-rc5/fs/nfs/nfsroot.c 2014-05-16 15:45:38.000000000 +0200 -@@ -87,7 +87,7 @@ - #define NFS_ROOT "/tftpboot/%s" - - /* Default NFSROOT mount options. */ --#define NFS_DEF_OPTIONS "vers=2,udp,rsize=4096,wsize=4096" -+#define NFS_DEF_OPTIONS "nfsvers=3,proto=tcp,rsize=4096,wsize=4096" - - /* Parameters passed from the kernel command line */ - static char nfs_root_parms[256] __initdata = ""; diff --git a/target/linux/patches/3.18.14/non-static.patch b/target/linux/patches/3.18.14/non-static.patch deleted file mode 100644 index a967703d0..000000000 --- a/target/linux/patches/3.18.14/non-static.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -Nur linux-2.6.39-rc6.orig/fs/namei.c linux-2.6.39-rc6/fs/namei.c ---- linux-2.6.39-rc6.orig/fs/namei.c 2011-05-04 04:59:13.000000000 +0200 -+++ linux-2.6.39-rc6/fs/namei.c 2011-05-05 11:30:14.000000000 +0200 -@@ -1769,7 +1769,7 @@ - * needs parent already locked. Doesn't follow mounts. - * SMP-safe. - */ --static struct dentry *lookup_hash(struct nameidata *nd) -+struct dentry *lookup_hash(struct nameidata *nd) - { - return __lookup_hash(&nd->last, nd->path.dentry, nd); - } -diff -Nur linux-2.6.39-rc6.orig/fs/splice.c linux-2.6.39-rc6/fs/splice.c ---- linux-2.6.39-rc6.orig/fs/splice.c 2011-05-04 04:59:13.000000000 +0200 -+++ linux-2.6.39-rc6/fs/splice.c 2011-05-05 11:31:04.000000000 +0200 -@@ -1081,7 +1081,7 @@ - /* - * Attempt to initiate a splice from pipe to file. - */ --static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags) - { - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, -@@ -1109,7 +1109,7 @@ - /* - * Attempt to initiate a splice from a file to a pipe. - */ --static long do_splice_to(struct file *in, loff_t *ppos, -+long do_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) - { diff --git a/target/linux/patches/3.18.14/ppc64-missing-zlib.patch b/target/linux/patches/3.18.14/ppc64-missing-zlib.patch deleted file mode 100644 index c6e0616be..000000000 --- a/target/linux/patches/3.18.14/ppc64-missing-zlib.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig ---- linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig 2013-11-01 15:23:09.000000000 +0100 -@@ -17,6 +17,7 @@ - select PPC_NATIVE - select PPC_PCI_CHOICE if EXPERT - select ZLIB_DEFLATE -+ select ZLIB_INFLATE - select PPC_DOORBELL - select HAVE_CONTEXT_TRACKING - select HOTPLUG_CPU if SMP diff --git a/target/linux/patches/3.18.14/realtime.patch b/target/linux/patches/3.18.14/realtime.patch deleted file mode 100644 index 28b9b271c..000000000 --- a/target/linux/patches/3.18.14/realtime.patch +++ /dev/null @@ -1,36846 +0,0 @@ -diff -Nur linux-3.18.14.orig/arch/alpha/mm/fault.c linux-3.18.14-rt/arch/alpha/mm/fault.c ---- linux-3.18.14.orig/arch/alpha/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/alpha/mm/fault.c 2015-05-31 15:32:45.517635394 -0500 -@@ -107,7 +107,7 @@ - - /* If we're in an interrupt context, or have no user context, - we must not take the fault. */ -- if (!mm || in_atomic()) -+ if (!mm || pagefault_disabled()) - goto no_context; - - #ifdef CONFIG_ALPHA_LARGE_VMALLOC -diff -Nur linux-3.18.14.orig/arch/arm/include/asm/cmpxchg.h linux-3.18.14-rt/arch/arm/include/asm/cmpxchg.h ---- linux-3.18.14.orig/arch/arm/include/asm/cmpxchg.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/include/asm/cmpxchg.h 2015-05-31 15:32:45.557635393 -0500 -@@ -129,6 +129,8 @@ - - #else /* min ARCH >= ARMv6 */ - -+#define __HAVE_ARCH_CMPXCHG 1 -+ - extern void __bad_cmpxchg(volatile void *ptr, int size); - - /* -diff -Nur linux-3.18.14.orig/arch/arm/include/asm/futex.h linux-3.18.14-rt/arch/arm/include/asm/futex.h ---- linux-3.18.14.orig/arch/arm/include/asm/futex.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/include/asm/futex.h 2015-05-31 15:32:45.561635393 -0500 -@@ -93,6 +93,8 @@ - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) - return -EFAULT; - -+ preempt_disable_rt(); -+ - __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" - "1: " TUSER(ldr) " %1, [%4]\n" - " teq %1, %2\n" -@@ -104,6 +106,8 @@ - : "cc", "memory"); - - *uval = val; -+ -+ preempt_enable_rt(); - return ret; - } - -diff -Nur linux-3.18.14.orig/arch/arm/include/asm/switch_to.h linux-3.18.14-rt/arch/arm/include/asm/switch_to.h ---- linux-3.18.14.orig/arch/arm/include/asm/switch_to.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/include/asm/switch_to.h 2015-05-31 15:32:45.565635393 -0500 -@@ -3,6 +3,13 @@ - - #include - -+#if defined CONFIG_PREEMPT_RT_FULL && defined CONFIG_HIGHMEM -+void switch_kmaps(struct task_struct *prev_p, struct task_struct *next_p); -+#else -+static inline void -+switch_kmaps(struct task_struct *prev_p, struct task_struct *next_p) { } -+#endif -+ - /* - * For v7 SMP cores running a preemptible kernel we may be pre-empted - * during a TLB maintenance operation, so execute an inner-shareable dsb -@@ -22,6 +29,7 @@ - - #define switch_to(prev,next,last) \ - do { \ -+ switch_kmaps(prev, next); \ - last = __switch_to(prev,task_thread_info(prev), task_thread_info(next)); \ - } while (0) - -diff -Nur linux-3.18.14.orig/arch/arm/include/asm/thread_info.h linux-3.18.14-rt/arch/arm/include/asm/thread_info.h ---- linux-3.18.14.orig/arch/arm/include/asm/thread_info.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/include/asm/thread_info.h 2015-05-31 15:32:45.585635393 -0500 -@@ -51,6 +51,7 @@ - struct thread_info { - unsigned long flags; /* low level flags */ - int preempt_count; /* 0 => preemptable, <0 => bug */ -+ int preempt_lazy_count; /* 0 => preemptable, <0 => bug */ - mm_segment_t addr_limit; /* address limit */ - struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ -@@ -149,6 +150,7 @@ - #define TIF_SIGPENDING 0 - #define TIF_NEED_RESCHED 1 - #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ -+#define TIF_NEED_RESCHED_LAZY 3 - #define TIF_UPROBE 7 - #define TIF_SYSCALL_TRACE 8 - #define TIF_SYSCALL_AUDIT 9 -@@ -162,6 +164,7 @@ - #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) - #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) - #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) -+#define _TIF_NEED_RESCHED_LAZY (1 << TIF_NEED_RESCHED_LAZY) - #define _TIF_UPROBE (1 << TIF_UPROBE) - #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) - #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) -diff -Nur linux-3.18.14.orig/arch/arm/Kconfig linux-3.18.14-rt/arch/arm/Kconfig ---- linux-3.18.14.orig/arch/arm/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/Kconfig 2015-05-31 15:32:45.529635394 -0500 -@@ -62,6 +62,7 @@ - select HAVE_PERF_EVENTS - select HAVE_PERF_REGS - select HAVE_PERF_USER_STACK_DUMP -+ select HAVE_PREEMPT_LAZY - select HAVE_RCU_TABLE_FREE if (SMP && ARM_LPAE) - select HAVE_REGS_AND_STACK_ACCESS_API - select HAVE_SYSCALL_TRACEPOINTS -diff -Nur linux-3.18.14.orig/arch/arm/kernel/asm-offsets.c linux-3.18.14-rt/arch/arm/kernel/asm-offsets.c ---- linux-3.18.14.orig/arch/arm/kernel/asm-offsets.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/kernel/asm-offsets.c 2015-05-31 15:32:45.605635393 -0500 -@@ -64,6 +64,7 @@ - BLANK(); - DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); - DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); -+ DEFINE(TI_PREEMPT_LAZY, offsetof(struct thread_info, preempt_lazy_count)); - DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); - DEFINE(TI_TASK, offsetof(struct thread_info, task)); - DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain)); -diff -Nur linux-3.18.14.orig/arch/arm/kernel/entry-armv.S linux-3.18.14-rt/arch/arm/kernel/entry-armv.S ---- linux-3.18.14.orig/arch/arm/kernel/entry-armv.S 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/kernel/entry-armv.S 2015-05-31 15:32:45.613635393 -0500 -@@ -207,11 +207,18 @@ - #ifdef CONFIG_PREEMPT - get_thread_info tsk - ldr r8, [tsk, #TI_PREEMPT] @ get preempt count -- ldr r0, [tsk, #TI_FLAGS] @ get flags - teq r8, #0 @ if preempt count != 0 -+ bne 1f @ return from exeption -+ ldr r0, [tsk, #TI_FLAGS] @ get flags -+ tst r0, #_TIF_NEED_RESCHED @ if NEED_RESCHED is set -+ blne svc_preempt @ preempt! -+ -+ ldr r8, [tsk, #TI_PREEMPT_LAZY] @ get preempt lazy count -+ teq r8, #0 @ if preempt lazy count != 0 - movne r0, #0 @ force flags to 0 -- tst r0, #_TIF_NEED_RESCHED -+ tst r0, #_TIF_NEED_RESCHED_LAZY - blne svc_preempt -+1: - #endif - - svc_exit r5, irq = 1 @ return from exception -@@ -226,6 +233,8 @@ - 1: bl preempt_schedule_irq @ irq en/disable is done inside - ldr r0, [tsk, #TI_FLAGS] @ get new tasks TI_FLAGS - tst r0, #_TIF_NEED_RESCHED -+ bne 1b -+ tst r0, #_TIF_NEED_RESCHED_LAZY - reteq r8 @ go again - b 1b - #endif -diff -Nur linux-3.18.14.orig/arch/arm/kernel/process.c linux-3.18.14-rt/arch/arm/kernel/process.c ---- linux-3.18.14.orig/arch/arm/kernel/process.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/kernel/process.c 2015-05-31 15:32:45.617635393 -0500 -@@ -437,6 +437,30 @@ - } - - #ifdef CONFIG_MMU -+/* -+ * CONFIG_SPLIT_PTLOCK_CPUS results in a page->ptl lock. If the lock is not -+ * initialized by pgtable_page_ctor() then a coredump of the vector page will -+ * fail. -+ */ -+static int __init vectors_user_mapping_init_page(void) -+{ -+ struct page *page; -+ unsigned long addr = 0xffff0000; -+ pgd_t *pgd; -+ pud_t *pud; -+ pmd_t *pmd; -+ -+ pgd = pgd_offset_k(addr); -+ pud = pud_offset(pgd, addr); -+ pmd = pmd_offset(pud, addr); -+ page = pmd_page(*(pmd)); -+ -+ pgtable_page_ctor(page); -+ -+ return 0; -+} -+late_initcall(vectors_user_mapping_init_page); -+ - #ifdef CONFIG_KUSER_HELPERS - /* - * The vectors page is always readable from user space for the -diff -Nur linux-3.18.14.orig/arch/arm/kernel/process.c.orig linux-3.18.14-rt/arch/arm/kernel/process.c.orig ---- linux-3.18.14.orig/arch/arm/kernel/process.c.orig 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/arch/arm/kernel/process.c.orig 2015-05-20 10:04:50.000000000 -0500 -@@ -0,0 +1,560 @@ -+/* -+ * linux/arch/arm/kernel/process.c -+ * -+ * Copyright (C) 1996-2000 Russell King - Converted to ARM. -+ * Original Copyright (C) 1995 Linus Torvalds -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "reboot.h" -+ -+#ifdef CONFIG_CC_STACKPROTECTOR -+#include -+unsigned long __stack_chk_guard __read_mostly; -+EXPORT_SYMBOL(__stack_chk_guard); -+#endif -+ -+static const char *processor_modes[] __maybe_unused = { -+ "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" , -+ "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26", -+ "USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" , -+ "UK8_32" , "UK9_32" , "UK10_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32" -+}; -+ -+static const char *isa_modes[] __maybe_unused = { -+ "ARM" , "Thumb" , "Jazelle", "ThumbEE" -+}; -+ -+extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); -+typedef void (*phys_reset_t)(unsigned long); -+ -+/* -+ * A temporary stack to use for CPU reset. This is static so that we -+ * don't clobber it with the identity mapping. When running with this -+ * stack, any references to the current task *will not work* so you -+ * should really do as little as possible before jumping to your reset -+ * code. -+ */ -+static u64 soft_restart_stack[16]; -+ -+static void __soft_restart(void *addr) -+{ -+ phys_reset_t phys_reset; -+ -+ /* Take out a flat memory mapping. */ -+ setup_mm_for_reboot(); -+ -+ /* Clean and invalidate caches */ -+ flush_cache_all(); -+ -+ /* Turn off caching */ -+ cpu_proc_fin(); -+ -+ /* Push out any further dirty data, and ensure cache is empty */ -+ flush_cache_all(); -+ -+ /* Switch to the identity mapping. */ -+ phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset); -+ phys_reset((unsigned long)addr); -+ -+ /* Should never get here. */ -+ BUG(); -+} -+ -+void _soft_restart(unsigned long addr, bool disable_l2) -+{ -+ u64 *stack = soft_restart_stack + ARRAY_SIZE(soft_restart_stack); -+ -+ /* Disable interrupts first */ -+ raw_local_irq_disable(); -+ local_fiq_disable(); -+ -+ /* Disable the L2 if we're the last man standing. */ -+ if (disable_l2) -+ outer_disable(); -+ -+ /* Change to the new stack and continue with the reset. */ -+ call_with_stack(__soft_restart, (void *)addr, (void *)stack); -+ -+ /* Should never get here. */ -+ BUG(); -+} -+ -+void soft_restart(unsigned long addr) -+{ -+ _soft_restart(addr, num_online_cpus() == 1); -+} -+ -+/* -+ * Function pointers to optional machine specific functions -+ */ -+void (*pm_power_off)(void); -+EXPORT_SYMBOL(pm_power_off); -+ -+void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); -+ -+/* -+ * This is our default idle handler. -+ */ -+ -+void (*arm_pm_idle)(void); -+ -+/* -+ * Called from the core idle loop. -+ */ -+ -+void arch_cpu_idle(void) -+{ -+ if (arm_pm_idle) -+ arm_pm_idle(); -+ else -+ cpu_do_idle(); -+ local_irq_enable(); -+} -+ -+void arch_cpu_idle_prepare(void) -+{ -+ local_fiq_enable(); -+} -+ -+void arch_cpu_idle_enter(void) -+{ -+ ledtrig_cpu(CPU_LED_IDLE_START); -+#ifdef CONFIG_PL310_ERRATA_769419 -+ wmb(); -+#endif -+} -+ -+void arch_cpu_idle_exit(void) -+{ -+ ledtrig_cpu(CPU_LED_IDLE_END); -+} -+ -+#ifdef CONFIG_HOTPLUG_CPU -+void arch_cpu_idle_dead(void) -+{ -+ cpu_die(); -+} -+#endif -+ -+/* -+ * Called by kexec, immediately prior to machine_kexec(). -+ * -+ * This must completely disable all secondary CPUs; simply causing those CPUs -+ * to execute e.g. a RAM-based pin loop is not sufficient. This allows the -+ * kexec'd kernel to use any and all RAM as it sees fit, without having to -+ * avoid any code or data used by any SW CPU pin loop. The CPU hotplug -+ * functionality embodied in disable_nonboot_cpus() to achieve this. -+ */ -+void machine_shutdown(void) -+{ -+ disable_nonboot_cpus(); -+} -+ -+/* -+ * Halting simply requires that the secondary CPUs stop performing any -+ * activity (executing tasks, handling interrupts). smp_send_stop() -+ * achieves this. -+ */ -+void machine_halt(void) -+{ -+ local_irq_disable(); -+ smp_send_stop(); -+ -+ local_irq_disable(); -+ while (1); -+} -+ -+/* -+ * Power-off simply requires that the secondary CPUs stop performing any -+ * activity (executing tasks, handling interrupts). smp_send_stop() -+ * achieves this. When the system power is turned off, it will take all CPUs -+ * with it. -+ */ -+void machine_power_off(void) -+{ -+ local_irq_disable(); -+ smp_send_stop(); -+ -+ if (pm_power_off) -+ pm_power_off(); -+} -+ -+/* -+ * Restart requires that the secondary CPUs stop performing any activity -+ * while the primary CPU resets the system. Systems with a single CPU can -+ * use soft_restart() as their machine descriptor's .restart hook, since that -+ * will cause the only available CPU to reset. Systems with multiple CPUs must -+ * provide a HW restart implementation, to ensure that all CPUs reset at once. -+ * This is required so that any code running after reset on the primary CPU -+ * doesn't have to co-ordinate with other CPUs to ensure they aren't still -+ * executing pre-reset code, and using RAM that the primary CPU's code wishes -+ * to use. Implementing such co-ordination would be essentially impossible. -+ */ -+void machine_restart(char *cmd) -+{ -+ local_irq_disable(); -+ smp_send_stop(); -+ -+ if (arm_pm_restart) -+ arm_pm_restart(reboot_mode, cmd); -+ else -+ do_kernel_restart(cmd); -+ -+ /* Give a grace period for failure to restart of 1s */ -+ mdelay(1000); -+ -+ /* Whoops - the platform was unable to reboot. Tell the user! */ -+ printk("Reboot failed -- System halted\n"); -+ local_irq_disable(); -+ while (1); -+} -+ -+void __show_regs(struct pt_regs *regs) -+{ -+ unsigned long flags; -+ char buf[64]; -+ -+ show_regs_print_info(KERN_DEFAULT); -+ -+ print_symbol("PC is at %s\n", instruction_pointer(regs)); -+ print_symbol("LR is at %s\n", regs->ARM_lr); -+ printk("pc : [<%08lx>] lr : [<%08lx>] psr: %08lx\n" -+ "sp : %08lx ip : %08lx fp : %08lx\n", -+ regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr, -+ regs->ARM_sp, regs->ARM_ip, regs->ARM_fp); -+ printk("r10: %08lx r9 : %08lx r8 : %08lx\n", -+ regs->ARM_r10, regs->ARM_r9, -+ regs->ARM_r8); -+ printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", -+ regs->ARM_r7, regs->ARM_r6, -+ regs->ARM_r5, regs->ARM_r4); -+ printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", -+ regs->ARM_r3, regs->ARM_r2, -+ regs->ARM_r1, regs->ARM_r0); -+ -+ flags = regs->ARM_cpsr; -+ buf[0] = flags & PSR_N_BIT ? 'N' : 'n'; -+ buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z'; -+ buf[2] = flags & PSR_C_BIT ? 'C' : 'c'; -+ buf[3] = flags & PSR_V_BIT ? 'V' : 'v'; -+ buf[4] = '\0'; -+ -+#ifndef CONFIG_CPU_V7M -+ printk("Flags: %s IRQs o%s FIQs o%s Mode %s ISA %s Segment %s\n", -+ buf, interrupts_enabled(regs) ? "n" : "ff", -+ fast_interrupts_enabled(regs) ? "n" : "ff", -+ processor_modes[processor_mode(regs)], -+ isa_modes[isa_mode(regs)], -+ get_fs() == get_ds() ? "kernel" : "user"); -+#else -+ printk("xPSR: %08lx\n", regs->ARM_cpsr); -+#endif -+ -+#ifdef CONFIG_CPU_CP15 -+ { -+ unsigned int ctrl; -+ -+ buf[0] = '\0'; -+#ifdef CONFIG_CPU_CP15_MMU -+ { -+ unsigned int transbase, dac; -+ asm("mrc p15, 0, %0, c2, c0\n\t" -+ "mrc p15, 0, %1, c3, c0\n" -+ : "=r" (transbase), "=r" (dac)); -+ snprintf(buf, sizeof(buf), " Table: %08x DAC: %08x", -+ transbase, dac); -+ } -+#endif -+ asm("mrc p15, 0, %0, c1, c0\n" : "=r" (ctrl)); -+ -+ printk("Control: %08x%s\n", ctrl, buf); -+ } -+#endif -+} -+ -+void show_regs(struct pt_regs * regs) -+{ -+ __show_regs(regs); -+ dump_stack(); -+} -+ -+ATOMIC_NOTIFIER_HEAD(thread_notify_head); -+ -+EXPORT_SYMBOL_GPL(thread_notify_head); -+ -+/* -+ * Free current thread data structures etc.. -+ */ -+void exit_thread(void) -+{ -+ thread_notify(THREAD_NOTIFY_EXIT, current_thread_info()); -+} -+ -+void flush_thread(void) -+{ -+ struct thread_info *thread = current_thread_info(); -+ struct task_struct *tsk = current; -+ -+ flush_ptrace_hw_breakpoint(tsk); -+ -+ memset(thread->used_cp, 0, sizeof(thread->used_cp)); -+ memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); -+ memset(&thread->fpstate, 0, sizeof(union fp_state)); -+ -+ flush_tls(); -+ -+ thread_notify(THREAD_NOTIFY_FLUSH, thread); -+} -+ -+void release_thread(struct task_struct *dead_task) -+{ -+} -+ -+asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); -+ -+int -+copy_thread(unsigned long clone_flags, unsigned long stack_start, -+ unsigned long stk_sz, struct task_struct *p) -+{ -+ struct thread_info *thread = task_thread_info(p); -+ struct pt_regs *childregs = task_pt_regs(p); -+ -+ memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); -+ -+ if (likely(!(p->flags & PF_KTHREAD))) { -+ *childregs = *current_pt_regs(); -+ childregs->ARM_r0 = 0; -+ if (stack_start) -+ childregs->ARM_sp = stack_start; -+ } else { -+ memset(childregs, 0, sizeof(struct pt_regs)); -+ thread->cpu_context.r4 = stk_sz; -+ thread->cpu_context.r5 = stack_start; -+ childregs->ARM_cpsr = SVC_MODE; -+ } -+ thread->cpu_context.pc = (unsigned long)ret_from_fork; -+ thread->cpu_context.sp = (unsigned long)childregs; -+ -+ clear_ptrace_hw_breakpoint(p); -+ -+ if (clone_flags & CLONE_SETTLS) -+ thread->tp_value[0] = childregs->ARM_r3; -+ thread->tp_value[1] = get_tpuser(); -+ -+ thread_notify(THREAD_NOTIFY_COPY, thread); -+ -+ return 0; -+} -+ -+/* -+ * Fill in the task's elfregs structure for a core dump. -+ */ -+int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs) -+{ -+ elf_core_copy_regs(elfregs, task_pt_regs(t)); -+ return 1; -+} -+ -+/* -+ * fill in the fpe structure for a core dump... -+ */ -+int dump_fpu (struct pt_regs *regs, struct user_fp *fp) -+{ -+ struct thread_info *thread = current_thread_info(); -+ int used_math = thread->used_cp[1] | thread->used_cp[2]; -+ -+ if (used_math) -+ memcpy(fp, &thread->fpstate.soft, sizeof (*fp)); -+ -+ return used_math != 0; -+} -+EXPORT_SYMBOL(dump_fpu); -+ -+unsigned long get_wchan(struct task_struct *p) -+{ -+ struct stackframe frame; -+ unsigned long stack_page; -+ int count = 0; -+ if (!p || p == current || p->state == TASK_RUNNING) -+ return 0; -+ -+ frame.fp = thread_saved_fp(p); -+ frame.sp = thread_saved_sp(p); -+ frame.lr = 0; /* recovered from the stack */ -+ frame.pc = thread_saved_pc(p); -+ stack_page = (unsigned long)task_stack_page(p); -+ do { -+ if (frame.sp < stack_page || -+ frame.sp >= stack_page + THREAD_SIZE || -+ unwind_frame(&frame) < 0) -+ return 0; -+ if (!in_sched_functions(frame.pc)) -+ return frame.pc; -+ } while (count ++ < 16); -+ return 0; -+} -+ -+unsigned long arch_randomize_brk(struct mm_struct *mm) -+{ -+ unsigned long range_end = mm->brk + 0x02000000; -+ return randomize_range(mm->brk, range_end, 0) ? : mm->brk; -+} -+ -+#ifdef CONFIG_MMU -+#ifdef CONFIG_KUSER_HELPERS -+/* -+ * The vectors page is always readable from user space for the -+ * atomic helpers. Insert it into the gate_vma so that it is visible -+ * through ptrace and /proc//mem. -+ */ -+static struct vm_area_struct gate_vma = { -+ .vm_start = 0xffff0000, -+ .vm_end = 0xffff0000 + PAGE_SIZE, -+ .vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC, -+}; -+ -+static int __init gate_vma_init(void) -+{ -+ gate_vma.vm_page_prot = PAGE_READONLY_EXEC; -+ return 0; -+} -+arch_initcall(gate_vma_init); -+ -+struct vm_area_struct *get_gate_vma(struct mm_struct *mm) -+{ -+ return &gate_vma; -+} -+ -+int in_gate_area(struct mm_struct *mm, unsigned long addr) -+{ -+ return (addr >= gate_vma.vm_start) && (addr < gate_vma.vm_end); -+} -+ -+int in_gate_area_no_mm(unsigned long addr) -+{ -+ return in_gate_area(NULL, addr); -+} -+#define is_gate_vma(vma) ((vma) == &gate_vma) -+#else -+#define is_gate_vma(vma) 0 -+#endif -+ -+const char *arch_vma_name(struct vm_area_struct *vma) -+{ -+ return is_gate_vma(vma) ? "[vectors]" : NULL; -+} -+ -+/* If possible, provide a placement hint at a random offset from the -+ * stack for the signal page. -+ */ -+static unsigned long sigpage_addr(const struct mm_struct *mm, -+ unsigned int npages) -+{ -+ unsigned long offset; -+ unsigned long first; -+ unsigned long last; -+ unsigned long addr; -+ unsigned int slots; -+ -+ first = PAGE_ALIGN(mm->start_stack); -+ -+ last = TASK_SIZE - (npages << PAGE_SHIFT); -+ -+ /* No room after stack? */ -+ if (first > last) -+ return 0; -+ -+ /* Just enough room? */ -+ if (first == last) -+ return first; -+ -+ slots = ((last - first) >> PAGE_SHIFT) + 1; -+ -+ offset = get_random_int() % slots; -+ -+ addr = first + (offset << PAGE_SHIFT); -+ -+ return addr; -+} -+ -+static struct page *signal_page; -+extern struct page *get_signal_page(void); -+ -+static const struct vm_special_mapping sigpage_mapping = { -+ .name = "[sigpage]", -+ .pages = &signal_page, -+}; -+ -+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) -+{ -+ struct mm_struct *mm = current->mm; -+ struct vm_area_struct *vma; -+ unsigned long addr; -+ unsigned long hint; -+ int ret = 0; -+ -+ if (!signal_page) -+ signal_page = get_signal_page(); -+ if (!signal_page) -+ return -ENOMEM; -+ -+ down_write(&mm->mmap_sem); -+ hint = sigpage_addr(mm, 1); -+ addr = get_unmapped_area(NULL, hint, PAGE_SIZE, 0, 0); -+ if (IS_ERR_VALUE(addr)) { -+ ret = addr; -+ goto up_fail; -+ } -+ -+ vma = _install_special_mapping(mm, addr, PAGE_SIZE, -+ VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, -+ &sigpage_mapping); -+ -+ if (IS_ERR(vma)) { -+ ret = PTR_ERR(vma); -+ goto up_fail; -+ } -+ -+ mm->context.sigpage = addr; -+ -+ up_fail: -+ up_write(&mm->mmap_sem); -+ return ret; -+} -+#endif -diff -Nur linux-3.18.14.orig/arch/arm/kernel/signal.c linux-3.18.14-rt/arch/arm/kernel/signal.c ---- linux-3.18.14.orig/arch/arm/kernel/signal.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/kernel/signal.c 2015-05-31 15:32:45.617635393 -0500 -@@ -574,7 +574,8 @@ - do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) - { - do { -- if (likely(thread_flags & _TIF_NEED_RESCHED)) { -+ if (likely(thread_flags & (_TIF_NEED_RESCHED | -+ _TIF_NEED_RESCHED_LAZY))) { - schedule(); - } else { - if (unlikely(!user_mode(regs))) -diff -Nur linux-3.18.14.orig/arch/arm/kernel/unwind.c linux-3.18.14-rt/arch/arm/kernel/unwind.c ---- linux-3.18.14.orig/arch/arm/kernel/unwind.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/kernel/unwind.c 2015-05-31 15:32:45.653635392 -0500 -@@ -93,7 +93,7 @@ - static const struct unwind_idx *__origin_unwind_idx; - extern const struct unwind_idx __stop_unwind_idx[]; - --static DEFINE_SPINLOCK(unwind_lock); -+static DEFINE_RAW_SPINLOCK(unwind_lock); - static LIST_HEAD(unwind_tables); - - /* Convert a prel31 symbol to an absolute address */ -@@ -201,7 +201,7 @@ - /* module unwind tables */ - struct unwind_table *table; - -- spin_lock_irqsave(&unwind_lock, flags); -+ raw_spin_lock_irqsave(&unwind_lock, flags); - list_for_each_entry(table, &unwind_tables, list) { - if (addr >= table->begin_addr && - addr < table->end_addr) { -@@ -213,7 +213,7 @@ - break; - } - } -- spin_unlock_irqrestore(&unwind_lock, flags); -+ raw_spin_unlock_irqrestore(&unwind_lock, flags); - } - - pr_debug("%s: idx = %p\n", __func__, idx); -@@ -530,9 +530,9 @@ - tab->begin_addr = text_addr; - tab->end_addr = text_addr + text_size; - -- spin_lock_irqsave(&unwind_lock, flags); -+ raw_spin_lock_irqsave(&unwind_lock, flags); - list_add_tail(&tab->list, &unwind_tables); -- spin_unlock_irqrestore(&unwind_lock, flags); -+ raw_spin_unlock_irqrestore(&unwind_lock, flags); - - return tab; - } -@@ -544,9 +544,9 @@ - if (!tab) - return; - -- spin_lock_irqsave(&unwind_lock, flags); -+ raw_spin_lock_irqsave(&unwind_lock, flags); - list_del(&tab->list); -- spin_unlock_irqrestore(&unwind_lock, flags); -+ raw_spin_unlock_irqrestore(&unwind_lock, flags); - - kfree(tab); - } -diff -Nur linux-3.18.14.orig/arch/arm/kvm/arm.c linux-3.18.14-rt/arch/arm/kvm/arm.c ---- linux-3.18.14.orig/arch/arm/kvm/arm.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/kvm/arm.c 2015-05-31 15:32:45.669635392 -0500 -@@ -455,9 +455,9 @@ - - static void vcpu_pause(struct kvm_vcpu *vcpu) - { -- wait_queue_head_t *wq = kvm_arch_vcpu_wq(vcpu); -+ struct swait_head *wq = kvm_arch_vcpu_wq(vcpu); - -- wait_event_interruptible(*wq, !vcpu->arch.pause); -+ swait_event_interruptible(*wq, !vcpu->arch.pause); - } - - static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu) -diff -Nur linux-3.18.14.orig/arch/arm/kvm/arm.c.orig linux-3.18.14-rt/arch/arm/kvm/arm.c.orig ---- linux-3.18.14.orig/arch/arm/kvm/arm.c.orig 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/arch/arm/kvm/arm.c.orig 2015-05-20 10:04:50.000000000 -0500 -@@ -0,0 +1,1060 @@ -+/* -+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University -+ * Author: Christoffer Dall -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License, version 2, as -+ * published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define CREATE_TRACE_POINTS -+#include "trace.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifdef REQUIRES_VIRT -+__asm__(".arch_extension virt"); -+#endif -+ -+static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page); -+static kvm_cpu_context_t __percpu *kvm_host_cpu_state; -+static unsigned long hyp_default_vectors; -+ -+/* Per-CPU variable containing the currently running vcpu. */ -+static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_arm_running_vcpu); -+ -+/* The VMID used in the VTTBR */ -+static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1); -+static u8 kvm_next_vmid; -+static DEFINE_SPINLOCK(kvm_vmid_lock); -+ -+static bool vgic_present; -+ -+static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu) -+{ -+ BUG_ON(preemptible()); -+ __this_cpu_write(kvm_arm_running_vcpu, vcpu); -+} -+ -+/** -+ * kvm_arm_get_running_vcpu - get the vcpu running on the current CPU. -+ * Must be called from non-preemptible context -+ */ -+struct kvm_vcpu *kvm_arm_get_running_vcpu(void) -+{ -+ BUG_ON(preemptible()); -+ return __this_cpu_read(kvm_arm_running_vcpu); -+} -+ -+/** -+ * kvm_arm_get_running_vcpus - get the per-CPU array of currently running vcpus. -+ */ -+struct kvm_vcpu * __percpu *kvm_get_running_vcpus(void) -+{ -+ return &kvm_arm_running_vcpu; -+} -+ -+int kvm_arch_hardware_enable(void) -+{ -+ return 0; -+} -+ -+int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) -+{ -+ return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE; -+} -+ -+int kvm_arch_hardware_setup(void) -+{ -+ return 0; -+} -+ -+void kvm_arch_check_processor_compat(void *rtn) -+{ -+ *(int *)rtn = 0; -+} -+ -+ -+/** -+ * kvm_arch_init_vm - initializes a VM data structure -+ * @kvm: pointer to the KVM struct -+ */ -+int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) -+{ -+ int ret = 0; -+ -+ if (type) -+ return -EINVAL; -+ -+ ret = kvm_alloc_stage2_pgd(kvm); -+ if (ret) -+ goto out_fail_alloc; -+ -+ ret = create_hyp_mappings(kvm, kvm + 1); -+ if (ret) -+ goto out_free_stage2_pgd; -+ -+ kvm_timer_init(kvm); -+ -+ /* Mark the initial VMID generation invalid */ -+ kvm->arch.vmid_gen = 0; -+ -+ return ret; -+out_free_stage2_pgd: -+ kvm_free_stage2_pgd(kvm); -+out_fail_alloc: -+ return ret; -+} -+ -+int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) -+{ -+ return VM_FAULT_SIGBUS; -+} -+ -+ -+/** -+ * kvm_arch_destroy_vm - destroy the VM data structure -+ * @kvm: pointer to the KVM struct -+ */ -+void kvm_arch_destroy_vm(struct kvm *kvm) -+{ -+ int i; -+ -+ kvm_free_stage2_pgd(kvm); -+ -+ for (i = 0; i < KVM_MAX_VCPUS; ++i) { -+ if (kvm->vcpus[i]) { -+ kvm_arch_vcpu_free(kvm->vcpus[i]); -+ kvm->vcpus[i] = NULL; -+ } -+ } -+ -+ kvm_vgic_destroy(kvm); -+} -+ -+int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) -+{ -+ int r; -+ switch (ext) { -+ case KVM_CAP_IRQCHIP: -+ r = vgic_present; -+ break; -+ case KVM_CAP_DEVICE_CTRL: -+ case KVM_CAP_USER_MEMORY: -+ case KVM_CAP_SYNC_MMU: -+ case KVM_CAP_DESTROY_MEMORY_REGION_WORKS: -+ case KVM_CAP_ONE_REG: -+ case KVM_CAP_ARM_PSCI: -+ case KVM_CAP_ARM_PSCI_0_2: -+ case KVM_CAP_READONLY_MEM: -+ r = 1; -+ break; -+ case KVM_CAP_COALESCED_MMIO: -+ r = KVM_COALESCED_MMIO_PAGE_OFFSET; -+ break; -+ case KVM_CAP_ARM_SET_DEVICE_ADDR: -+ r = 1; -+ break; -+ case KVM_CAP_NR_VCPUS: -+ r = num_online_cpus(); -+ break; -+ case KVM_CAP_MAX_VCPUS: -+ r = KVM_MAX_VCPUS; -+ break; -+ default: -+ r = kvm_arch_dev_ioctl_check_extension(ext); -+ break; -+ } -+ return r; -+} -+ -+long kvm_arch_dev_ioctl(struct file *filp, -+ unsigned int ioctl, unsigned long arg) -+{ -+ return -EINVAL; -+} -+ -+ -+struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) -+{ -+ int err; -+ struct kvm_vcpu *vcpu; -+ -+ if (irqchip_in_kernel(kvm) && vgic_initialized(kvm)) { -+ err = -EBUSY; -+ goto out; -+ } -+ -+ vcpu = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL); -+ if (!vcpu) { -+ err = -ENOMEM; -+ goto out; -+ } -+ -+ err = kvm_vcpu_init(vcpu, kvm, id); -+ if (err) -+ goto free_vcpu; -+ -+ err = create_hyp_mappings(vcpu, vcpu + 1); -+ if (err) -+ goto vcpu_uninit; -+ -+ return vcpu; -+vcpu_uninit: -+ kvm_vcpu_uninit(vcpu); -+free_vcpu: -+ kmem_cache_free(kvm_vcpu_cache, vcpu); -+out: -+ return ERR_PTR(err); -+} -+ -+int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) -+{ -+ return 0; -+} -+ -+void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu) -+{ -+ kvm_mmu_free_memory_caches(vcpu); -+ kvm_timer_vcpu_terminate(vcpu); -+ kvm_vgic_vcpu_destroy(vcpu); -+ kmem_cache_free(kvm_vcpu_cache, vcpu); -+} -+ -+void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) -+{ -+ kvm_arch_vcpu_free(vcpu); -+} -+ -+int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) -+{ -+ return 0; -+} -+ -+int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) -+{ -+ /* Force users to call KVM_ARM_VCPU_INIT */ -+ vcpu->arch.target = -1; -+ -+ /* Set up the timer */ -+ kvm_timer_vcpu_init(vcpu); -+ -+ return 0; -+} -+ -+void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) -+{ -+ vcpu->cpu = cpu; -+ vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state); -+ -+ /* -+ * Check whether this vcpu requires the cache to be flushed on -+ * this physical CPU. This is a consequence of doing dcache -+ * operations by set/way on this vcpu. We do it here to be in -+ * a non-preemptible section. -+ */ -+ if (cpumask_test_and_clear_cpu(cpu, &vcpu->arch.require_dcache_flush)) -+ flush_cache_all(); /* We'd really want v7_flush_dcache_all() */ -+ -+ kvm_arm_set_running_vcpu(vcpu); -+} -+ -+void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) -+{ -+ /* -+ * The arch-generic KVM code expects the cpu field of a vcpu to be -1 -+ * if the vcpu is no longer assigned to a cpu. This is used for the -+ * optimized make_all_cpus_request path. -+ */ -+ vcpu->cpu = -1; -+ -+ kvm_arm_set_running_vcpu(NULL); -+} -+ -+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, -+ struct kvm_guest_debug *dbg) -+{ -+ return -EINVAL; -+} -+ -+ -+int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, -+ struct kvm_mp_state *mp_state) -+{ -+ return -EINVAL; -+} -+ -+int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, -+ struct kvm_mp_state *mp_state) -+{ -+ return -EINVAL; -+} -+ -+/** -+ * kvm_arch_vcpu_runnable - determine if the vcpu can be scheduled -+ * @v: The VCPU pointer -+ * -+ * If the guest CPU is not waiting for interrupts or an interrupt line is -+ * asserted, the CPU is by definition runnable. -+ */ -+int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) -+{ -+ return !!v->arch.irq_lines || kvm_vgic_vcpu_pending_irq(v); -+} -+ -+/* Just ensure a guest exit from a particular CPU */ -+static void exit_vm_noop(void *info) -+{ -+} -+ -+void force_vm_exit(const cpumask_t *mask) -+{ -+ smp_call_function_many(mask, exit_vm_noop, NULL, true); -+} -+ -+/** -+ * need_new_vmid_gen - check that the VMID is still valid -+ * @kvm: The VM's VMID to checkt -+ * -+ * return true if there is a new generation of VMIDs being used -+ * -+ * The hardware supports only 256 values with the value zero reserved for the -+ * host, so we check if an assigned value belongs to a previous generation, -+ * which which requires us to assign a new value. If we're the first to use a -+ * VMID for the new generation, we must flush necessary caches and TLBs on all -+ * CPUs. -+ */ -+static bool need_new_vmid_gen(struct kvm *kvm) -+{ -+ return unlikely(kvm->arch.vmid_gen != atomic64_read(&kvm_vmid_gen)); -+} -+ -+/** -+ * update_vttbr - Update the VTTBR with a valid VMID before the guest runs -+ * @kvm The guest that we are about to run -+ * -+ * Called from kvm_arch_vcpu_ioctl_run before entering the guest to ensure the -+ * VM has a valid VMID, otherwise assigns a new one and flushes corresponding -+ * caches and TLBs. -+ */ -+static void update_vttbr(struct kvm *kvm) -+{ -+ phys_addr_t pgd_phys; -+ u64 vmid; -+ -+ if (!need_new_vmid_gen(kvm)) -+ return; -+ -+ spin_lock(&kvm_vmid_lock); -+ -+ /* -+ * We need to re-check the vmid_gen here to ensure that if another vcpu -+ * already allocated a valid vmid for this vm, then this vcpu should -+ * use the same vmid. -+ */ -+ if (!need_new_vmid_gen(kvm)) { -+ spin_unlock(&kvm_vmid_lock); -+ return; -+ } -+ -+ /* First user of a new VMID generation? */ -+ if (unlikely(kvm_next_vmid == 0)) { -+ atomic64_inc(&kvm_vmid_gen); -+ kvm_next_vmid = 1; -+ -+ /* -+ * On SMP we know no other CPUs can use this CPU's or each -+ * other's VMID after force_vm_exit returns since the -+ * kvm_vmid_lock blocks them from reentry to the guest. -+ */ -+ force_vm_exit(cpu_all_mask); -+ /* -+ * Now broadcast TLB + ICACHE invalidation over the inner -+ * shareable domain to make sure all data structures are -+ * clean. -+ */ -+ kvm_call_hyp(__kvm_flush_vm_context); -+ } -+ -+ kvm->arch.vmid_gen = atomic64_read(&kvm_vmid_gen); -+ kvm->arch.vmid = kvm_next_vmid; -+ kvm_next_vmid++; -+ -+ /* update vttbr to be used with the new vmid */ -+ pgd_phys = virt_to_phys(kvm_get_hwpgd(kvm)); -+ BUG_ON(pgd_phys & ~VTTBR_BADDR_MASK); -+ vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK; -+ kvm->arch.vttbr = pgd_phys | vmid; -+ -+ spin_unlock(&kvm_vmid_lock); -+} -+ -+static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) -+{ -+ struct kvm *kvm = vcpu->kvm; -+ int ret; -+ -+ if (likely(vcpu->arch.has_run_once)) -+ return 0; -+ -+ vcpu->arch.has_run_once = true; -+ -+ /* -+ * Map the VGIC hardware resources before running a vcpu the first -+ * time on this VM. -+ */ -+ if (unlikely(!vgic_initialized(kvm))) { -+ ret = kvm_vgic_map_resources(kvm); -+ if (ret) -+ return ret; -+ } -+ -+ /* -+ * Enable the arch timers only if we have an in-kernel VGIC -+ * and it has been properly initialized, since we cannot handle -+ * interrupts from the virtual timer with a userspace gic. -+ */ -+ if (irqchip_in_kernel(kvm) && vgic_initialized(kvm)) -+ kvm_timer_enable(kvm); -+ -+ return 0; -+} -+ -+static void vcpu_pause(struct kvm_vcpu *vcpu) -+{ -+ wait_queue_head_t *wq = kvm_arch_vcpu_wq(vcpu); -+ -+ wait_event_interruptible(*wq, !vcpu->arch.pause); -+} -+ -+static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu) -+{ -+ return vcpu->arch.target >= 0; -+} -+ -+/** -+ * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code -+ * @vcpu: The VCPU pointer -+ * @run: The kvm_run structure pointer used for userspace state exchange -+ * -+ * This function is called through the VCPU_RUN ioctl called from user space. It -+ * will execute VM code in a loop until the time slice for the process is used -+ * or some emulation is needed from user space in which case the function will -+ * return with return value 0 and with the kvm_run structure filled in with the -+ * required data for the requested emulation. -+ */ -+int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) -+{ -+ int ret; -+ sigset_t sigsaved; -+ -+ if (unlikely(!kvm_vcpu_initialized(vcpu))) -+ return -ENOEXEC; -+ -+ ret = kvm_vcpu_first_run_init(vcpu); -+ if (ret) -+ return ret; -+ -+ if (run->exit_reason == KVM_EXIT_MMIO) { -+ ret = kvm_handle_mmio_return(vcpu, vcpu->run); -+ if (ret) -+ return ret; -+ } -+ -+ if (vcpu->sigset_active) -+ sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); -+ -+ ret = 1; -+ run->exit_reason = KVM_EXIT_UNKNOWN; -+ while (ret > 0) { -+ /* -+ * Check conditions before entering the guest -+ */ -+ cond_resched(); -+ -+ update_vttbr(vcpu->kvm); -+ -+ if (vcpu->arch.pause) -+ vcpu_pause(vcpu); -+ -+ kvm_vgic_flush_hwstate(vcpu); -+ kvm_timer_flush_hwstate(vcpu); -+ -+ local_irq_disable(); -+ -+ /* -+ * Re-check atomic conditions -+ */ -+ if (signal_pending(current)) { -+ ret = -EINTR; -+ run->exit_reason = KVM_EXIT_INTR; -+ } -+ -+ if (ret <= 0 || need_new_vmid_gen(vcpu->kvm)) { -+ local_irq_enable(); -+ kvm_timer_sync_hwstate(vcpu); -+ kvm_vgic_sync_hwstate(vcpu); -+ continue; -+ } -+ -+ /************************************************************** -+ * Enter the guest -+ */ -+ trace_kvm_entry(*vcpu_pc(vcpu)); -+ kvm_guest_enter(); -+ vcpu->mode = IN_GUEST_MODE; -+ -+ ret = kvm_call_hyp(__kvm_vcpu_run, vcpu); -+ -+ vcpu->mode = OUTSIDE_GUEST_MODE; -+ vcpu->arch.last_pcpu = smp_processor_id(); -+ kvm_guest_exit(); -+ trace_kvm_exit(*vcpu_pc(vcpu)); -+ /* -+ * We may have taken a host interrupt in HYP mode (ie -+ * while executing the guest). This interrupt is still -+ * pending, as we haven't serviced it yet! -+ * -+ * We're now back in SVC mode, with interrupts -+ * disabled. Enabling the interrupts now will have -+ * the effect of taking the interrupt again, in SVC -+ * mode this time. -+ */ -+ local_irq_enable(); -+ -+ /* -+ * Back from guest -+ *************************************************************/ -+ -+ kvm_timer_sync_hwstate(vcpu); -+ kvm_vgic_sync_hwstate(vcpu); -+ -+ ret = handle_exit(vcpu, run, ret); -+ } -+ -+ if (vcpu->sigset_active) -+ sigprocmask(SIG_SETMASK, &sigsaved, NULL); -+ return ret; -+} -+ -+static int vcpu_interrupt_line(struct kvm_vcpu *vcpu, int number, bool level) -+{ -+ int bit_index; -+ bool set; -+ unsigned long *ptr; -+ -+ if (number == KVM_ARM_IRQ_CPU_IRQ) -+ bit_index = __ffs(HCR_VI); -+ else /* KVM_ARM_IRQ_CPU_FIQ */ -+ bit_index = __ffs(HCR_VF); -+ -+ ptr = (unsigned long *)&vcpu->arch.irq_lines; -+ if (level) -+ set = test_and_set_bit(bit_index, ptr); -+ else -+ set = test_and_clear_bit(bit_index, ptr); -+ -+ /* -+ * If we didn't change anything, no need to wake up or kick other CPUs -+ */ -+ if (set == level) -+ return 0; -+ -+ /* -+ * The vcpu irq_lines field was updated, wake up sleeping VCPUs and -+ * trigger a world-switch round on the running physical CPU to set the -+ * virtual IRQ/FIQ fields in the HCR appropriately. -+ */ -+ kvm_vcpu_kick(vcpu); -+ -+ return 0; -+} -+ -+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, -+ bool line_status) -+{ -+ u32 irq = irq_level->irq; -+ unsigned int irq_type, vcpu_idx, irq_num; -+ int nrcpus = atomic_read(&kvm->online_vcpus); -+ struct kvm_vcpu *vcpu = NULL; -+ bool level = irq_level->level; -+ -+ irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK; -+ vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK; -+ irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK; -+ -+ trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level); -+ -+ switch (irq_type) { -+ case KVM_ARM_IRQ_TYPE_CPU: -+ if (irqchip_in_kernel(kvm)) -+ return -ENXIO; -+ -+ if (vcpu_idx >= nrcpus) -+ return -EINVAL; -+ -+ vcpu = kvm_get_vcpu(kvm, vcpu_idx); -+ if (!vcpu) -+ return -EINVAL; -+ -+ if (irq_num > KVM_ARM_IRQ_CPU_FIQ) -+ return -EINVAL; -+ -+ return vcpu_interrupt_line(vcpu, irq_num, level); -+ case KVM_ARM_IRQ_TYPE_PPI: -+ if (!irqchip_in_kernel(kvm)) -+ return -ENXIO; -+ -+ if (vcpu_idx >= nrcpus) -+ return -EINVAL; -+ -+ vcpu = kvm_get_vcpu(kvm, vcpu_idx); -+ if (!vcpu) -+ return -EINVAL; -+ -+ if (irq_num < VGIC_NR_SGIS || irq_num >= VGIC_NR_PRIVATE_IRQS) -+ return -EINVAL; -+ -+ return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level); -+ case KVM_ARM_IRQ_TYPE_SPI: -+ if (!irqchip_in_kernel(kvm)) -+ return -ENXIO; -+ -+ if (irq_num < VGIC_NR_PRIVATE_IRQS) -+ return -EINVAL; -+ -+ return kvm_vgic_inject_irq(kvm, 0, irq_num, level); -+ } -+ -+ return -EINVAL; -+} -+ -+static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu, -+ struct kvm_vcpu_init *init) -+{ -+ int ret; -+ -+ ret = kvm_vcpu_set_target(vcpu, init); -+ if (ret) -+ return ret; -+ -+ /* -+ * Ensure a rebooted VM will fault in RAM pages and detect if the -+ * guest MMU is turned off and flush the caches as needed. -+ */ -+ if (vcpu->arch.has_run_once) -+ stage2_unmap_vm(vcpu->kvm); -+ -+ vcpu_reset_hcr(vcpu); -+ -+ /* -+ * Handle the "start in power-off" case by marking the VCPU as paused. -+ */ -+ if (test_bit(KVM_ARM_VCPU_POWER_OFF, vcpu->arch.features)) -+ vcpu->arch.pause = true; -+ else -+ vcpu->arch.pause = false; -+ -+ return 0; -+} -+ -+long kvm_arch_vcpu_ioctl(struct file *filp, -+ unsigned int ioctl, unsigned long arg) -+{ -+ struct kvm_vcpu *vcpu = filp->private_data; -+ void __user *argp = (void __user *)arg; -+ -+ switch (ioctl) { -+ case KVM_ARM_VCPU_INIT: { -+ struct kvm_vcpu_init init; -+ -+ if (copy_from_user(&init, argp, sizeof(init))) -+ return -EFAULT; -+ -+ return kvm_arch_vcpu_ioctl_vcpu_init(vcpu, &init); -+ } -+ case KVM_SET_ONE_REG: -+ case KVM_GET_ONE_REG: { -+ struct kvm_one_reg reg; -+ -+ if (unlikely(!kvm_vcpu_initialized(vcpu))) -+ return -ENOEXEC; -+ -+ if (copy_from_user(®, argp, sizeof(reg))) -+ return -EFAULT; -+ if (ioctl == KVM_SET_ONE_REG) -+ return kvm_arm_set_reg(vcpu, ®); -+ else -+ return kvm_arm_get_reg(vcpu, ®); -+ } -+ case KVM_GET_REG_LIST: { -+ struct kvm_reg_list __user *user_list = argp; -+ struct kvm_reg_list reg_list; -+ unsigned n; -+ -+ if (unlikely(!kvm_vcpu_initialized(vcpu))) -+ return -ENOEXEC; -+ -+ if (copy_from_user(®_list, user_list, sizeof(reg_list))) -+ return -EFAULT; -+ n = reg_list.n; -+ reg_list.n = kvm_arm_num_regs(vcpu); -+ if (copy_to_user(user_list, ®_list, sizeof(reg_list))) -+ return -EFAULT; -+ if (n < reg_list.n) -+ return -E2BIG; -+ return kvm_arm_copy_reg_indices(vcpu, user_list->reg); -+ } -+ default: -+ return -EINVAL; -+ } -+} -+ -+int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) -+{ -+ return -EINVAL; -+} -+ -+static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm, -+ struct kvm_arm_device_addr *dev_addr) -+{ -+ unsigned long dev_id, type; -+ -+ dev_id = (dev_addr->id & KVM_ARM_DEVICE_ID_MASK) >> -+ KVM_ARM_DEVICE_ID_SHIFT; -+ type = (dev_addr->id & KVM_ARM_DEVICE_TYPE_MASK) >> -+ KVM_ARM_DEVICE_TYPE_SHIFT; -+ -+ switch (dev_id) { -+ case KVM_ARM_DEVICE_VGIC_V2: -+ if (!vgic_present) -+ return -ENXIO; -+ return kvm_vgic_addr(kvm, type, &dev_addr->addr, true); -+ default: -+ return -ENODEV; -+ } -+} -+ -+long kvm_arch_vm_ioctl(struct file *filp, -+ unsigned int ioctl, unsigned long arg) -+{ -+ struct kvm *kvm = filp->private_data; -+ void __user *argp = (void __user *)arg; -+ -+ switch (ioctl) { -+ case KVM_CREATE_IRQCHIP: { -+ if (vgic_present) -+ return kvm_vgic_create(kvm); -+ else -+ return -ENXIO; -+ } -+ case KVM_ARM_SET_DEVICE_ADDR: { -+ struct kvm_arm_device_addr dev_addr; -+ -+ if (copy_from_user(&dev_addr, argp, sizeof(dev_addr))) -+ return -EFAULT; -+ return kvm_vm_ioctl_set_device_addr(kvm, &dev_addr); -+ } -+ case KVM_ARM_PREFERRED_TARGET: { -+ int err; -+ struct kvm_vcpu_init init; -+ -+ err = kvm_vcpu_preferred_target(&init); -+ if (err) -+ return err; -+ -+ if (copy_to_user(argp, &init, sizeof(init))) -+ return -EFAULT; -+ -+ return 0; -+ } -+ default: -+ return -EINVAL; -+ } -+} -+ -+static void cpu_init_hyp_mode(void *dummy) -+{ -+ phys_addr_t boot_pgd_ptr; -+ phys_addr_t pgd_ptr; -+ unsigned long hyp_stack_ptr; -+ unsigned long stack_page; -+ unsigned long vector_ptr; -+ -+ /* Switch from the HYP stub to our own HYP init vector */ -+ __hyp_set_vectors(kvm_get_idmap_vector()); -+ -+ boot_pgd_ptr = kvm_mmu_get_boot_httbr(); -+ pgd_ptr = kvm_mmu_get_httbr(); -+ stack_page = __this_cpu_read(kvm_arm_hyp_stack_page); -+ hyp_stack_ptr = stack_page + PAGE_SIZE; -+ vector_ptr = (unsigned long)__kvm_hyp_vector; -+ -+ __cpu_init_hyp_mode(boot_pgd_ptr, pgd_ptr, hyp_stack_ptr, vector_ptr); -+} -+ -+static int hyp_init_cpu_notify(struct notifier_block *self, -+ unsigned long action, void *cpu) -+{ -+ switch (action) { -+ case CPU_STARTING: -+ case CPU_STARTING_FROZEN: -+ if (__hyp_get_vectors() == hyp_default_vectors) -+ cpu_init_hyp_mode(NULL); -+ break; -+ } -+ -+ return NOTIFY_OK; -+} -+ -+static struct notifier_block hyp_init_cpu_nb = { -+ .notifier_call = hyp_init_cpu_notify, -+}; -+ -+#ifdef CONFIG_CPU_PM -+static int hyp_init_cpu_pm_notifier(struct notifier_block *self, -+ unsigned long cmd, -+ void *v) -+{ -+ if (cmd == CPU_PM_EXIT && -+ __hyp_get_vectors() == hyp_default_vectors) { -+ cpu_init_hyp_mode(NULL); -+ return NOTIFY_OK; -+ } -+ -+ return NOTIFY_DONE; -+} -+ -+static struct notifier_block hyp_init_cpu_pm_nb = { -+ .notifier_call = hyp_init_cpu_pm_notifier, -+}; -+ -+static void __init hyp_cpu_pm_init(void) -+{ -+ cpu_pm_register_notifier(&hyp_init_cpu_pm_nb); -+} -+#else -+static inline void hyp_cpu_pm_init(void) -+{ -+} -+#endif -+ -+/** -+ * Inits Hyp-mode on all online CPUs -+ */ -+static int init_hyp_mode(void) -+{ -+ int cpu; -+ int err = 0; -+ -+ /* -+ * Allocate Hyp PGD and setup Hyp identity mapping -+ */ -+ err = kvm_mmu_init(); -+ if (err) -+ goto out_err; -+ -+ /* -+ * It is probably enough to obtain the default on one -+ * CPU. It's unlikely to be different on the others. -+ */ -+ hyp_default_vectors = __hyp_get_vectors(); -+ -+ /* -+ * Allocate stack pages for Hypervisor-mode -+ */ -+ for_each_possible_cpu(cpu) { -+ unsigned long stack_page; -+ -+ stack_page = __get_free_page(GFP_KERNEL); -+ if (!stack_page) { -+ err = -ENOMEM; -+ goto out_free_stack_pages; -+ } -+ -+ per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page; -+ } -+ -+ /* -+ * Map the Hyp-code called directly from the host -+ */ -+ err = create_hyp_mappings(__kvm_hyp_code_start, __kvm_hyp_code_end); -+ if (err) { -+ kvm_err("Cannot map world-switch code\n"); -+ goto out_free_mappings; -+ } -+ -+ /* -+ * Map the Hyp stack pages -+ */ -+ for_each_possible_cpu(cpu) { -+ char *stack_page = (char *)per_cpu(kvm_arm_hyp_stack_page, cpu); -+ err = create_hyp_mappings(stack_page, stack_page + PAGE_SIZE); -+ -+ if (err) { -+ kvm_err("Cannot map hyp stack\n"); -+ goto out_free_mappings; -+ } -+ } -+ -+ /* -+ * Map the host CPU structures -+ */ -+ kvm_host_cpu_state = alloc_percpu(kvm_cpu_context_t); -+ if (!kvm_host_cpu_state) { -+ err = -ENOMEM; -+ kvm_err("Cannot allocate host CPU state\n"); -+ goto out_free_mappings; -+ } -+ -+ for_each_possible_cpu(cpu) { -+ kvm_cpu_context_t *cpu_ctxt; -+ -+ cpu_ctxt = per_cpu_ptr(kvm_host_cpu_state, cpu); -+ err = create_hyp_mappings(cpu_ctxt, cpu_ctxt + 1); -+ -+ if (err) { -+ kvm_err("Cannot map host CPU state: %d\n", err); -+ goto out_free_context; -+ } -+ } -+ -+ /* -+ * Execute the init code on each CPU. -+ */ -+ on_each_cpu(cpu_init_hyp_mode, NULL, 1); -+ -+ /* -+ * Init HYP view of VGIC -+ */ -+ err = kvm_vgic_hyp_init(); -+ if (err) -+ goto out_free_context; -+ -+#ifdef CONFIG_KVM_ARM_VGIC -+ vgic_present = true; -+#endif -+ -+ /* -+ * Init HYP architected timer support -+ */ -+ err = kvm_timer_hyp_init(); -+ if (err) -+ goto out_free_mappings; -+ -+#ifndef CONFIG_HOTPLUG_CPU -+ free_boot_hyp_pgd(); -+#endif -+ -+ kvm_perf_init(); -+ -+ kvm_info("Hyp mode initialized successfully\n"); -+ -+ return 0; -+out_free_context: -+ free_percpu(kvm_host_cpu_state); -+out_free_mappings: -+ free_hyp_pgds(); -+out_free_stack_pages: -+ for_each_possible_cpu(cpu) -+ free_page(per_cpu(kvm_arm_hyp_stack_page, cpu)); -+out_err: -+ kvm_err("error initializing Hyp mode: %d\n", err); -+ return err; -+} -+ -+static void check_kvm_target_cpu(void *ret) -+{ -+ *(int *)ret = kvm_target_cpu(); -+} -+ -+/** -+ * Initialize Hyp-mode and memory mappings on all CPUs. -+ */ -+int kvm_arch_init(void *opaque) -+{ -+ int err; -+ int ret, cpu; -+ -+ if (!is_hyp_mode_available()) { -+ kvm_err("HYP mode not available\n"); -+ return -ENODEV; -+ } -+ -+ for_each_online_cpu(cpu) { -+ smp_call_function_single(cpu, check_kvm_target_cpu, &ret, 1); -+ if (ret < 0) { -+ kvm_err("Error, CPU %d not supported!\n", cpu); -+ return -ENODEV; -+ } -+ } -+ -+ cpu_notifier_register_begin(); -+ -+ err = init_hyp_mode(); -+ if (err) -+ goto out_err; -+ -+ err = __register_cpu_notifier(&hyp_init_cpu_nb); -+ if (err) { -+ kvm_err("Cannot register HYP init CPU notifier (%d)\n", err); -+ goto out_err; -+ } -+ -+ cpu_notifier_register_done(); -+ -+ hyp_cpu_pm_init(); -+ -+ kvm_coproc_table_init(); -+ return 0; -+out_err: -+ cpu_notifier_register_done(); -+ return err; -+} -+ -+/* NOP: Compiling as a module not supported */ -+void kvm_arch_exit(void) -+{ -+ kvm_perf_teardown(); -+} -+ -+static int arm_init(void) -+{ -+ int rc = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE); -+ return rc; -+} -+ -+module_init(arm_init); -diff -Nur linux-3.18.14.orig/arch/arm/kvm/psci.c linux-3.18.14-rt/arch/arm/kvm/psci.c ---- linux-3.18.14.orig/arch/arm/kvm/psci.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/kvm/psci.c 2015-05-31 15:32:45.673635392 -0500 -@@ -67,7 +67,7 @@ - { - struct kvm *kvm = source_vcpu->kvm; - struct kvm_vcpu *vcpu = NULL, *tmp; -- wait_queue_head_t *wq; -+ struct swait_head *wq; - unsigned long cpu_id; - unsigned long context_id; - unsigned long mpidr; -@@ -124,7 +124,7 @@ - smp_mb(); /* Make sure the above is visible */ - - wq = kvm_arch_vcpu_wq(vcpu); -- wake_up_interruptible(wq); -+ swait_wake_interruptible(wq); - - return PSCI_RET_SUCCESS; - } -diff -Nur linux-3.18.14.orig/arch/arm/kvm/psci.c.orig linux-3.18.14-rt/arch/arm/kvm/psci.c.orig ---- linux-3.18.14.orig/arch/arm/kvm/psci.c.orig 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/arch/arm/kvm/psci.c.orig 2015-05-20 10:04:50.000000000 -0500 -@@ -0,0 +1,337 @@ -+/* -+ * Copyright (C) 2012 - ARM Ltd -+ * Author: Marc Zyngier -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+/* -+ * This is an implementation of the Power State Coordination Interface -+ * as described in ARM document number ARM DEN 0022A. -+ */ -+ -+#define AFFINITY_MASK(level) ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1) -+ -+static unsigned long psci_affinity_mask(unsigned long affinity_level) -+{ -+ if (affinity_level <= 3) -+ return MPIDR_HWID_BITMASK & AFFINITY_MASK(affinity_level); -+ -+ return 0; -+} -+ -+static unsigned long kvm_psci_vcpu_suspend(struct kvm_vcpu *vcpu) -+{ -+ /* -+ * NOTE: For simplicity, we make VCPU suspend emulation to be -+ * same-as WFI (Wait-for-interrupt) emulation. -+ * -+ * This means for KVM the wakeup events are interrupts and -+ * this is consistent with intended use of StateID as described -+ * in section 5.4.1 of PSCI v0.2 specification (ARM DEN 0022A). -+ * -+ * Further, we also treat power-down request to be same as -+ * stand-by request as-per section 5.4.2 clause 3 of PSCI v0.2 -+ * specification (ARM DEN 0022A). This means all suspend states -+ * for KVM will preserve the register state. -+ */ -+ kvm_vcpu_block(vcpu); -+ -+ return PSCI_RET_SUCCESS; -+} -+ -+static void kvm_psci_vcpu_off(struct kvm_vcpu *vcpu) -+{ -+ vcpu->arch.pause = true; -+} -+ -+static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) -+{ -+ struct kvm *kvm = source_vcpu->kvm; -+ struct kvm_vcpu *vcpu = NULL, *tmp; -+ wait_queue_head_t *wq; -+ unsigned long cpu_id; -+ unsigned long context_id; -+ unsigned long mpidr; -+ phys_addr_t target_pc; -+ int i; -+ -+ cpu_id = *vcpu_reg(source_vcpu, 1); -+ if (vcpu_mode_is_32bit(source_vcpu)) -+ cpu_id &= ~((u32) 0); -+ -+ kvm_for_each_vcpu(i, tmp, kvm) { -+ mpidr = kvm_vcpu_get_mpidr(tmp); -+ if ((mpidr & MPIDR_HWID_BITMASK) == (cpu_id & MPIDR_HWID_BITMASK)) { -+ vcpu = tmp; -+ break; -+ } -+ } -+ -+ /* -+ * Make sure the caller requested a valid CPU and that the CPU is -+ * turned off. -+ */ -+ if (!vcpu) -+ return PSCI_RET_INVALID_PARAMS; -+ if (!vcpu->arch.pause) { -+ if (kvm_psci_version(source_vcpu) != KVM_ARM_PSCI_0_1) -+ return PSCI_RET_ALREADY_ON; -+ else -+ return PSCI_RET_INVALID_PARAMS; -+ } -+ -+ target_pc = *vcpu_reg(source_vcpu, 2); -+ context_id = *vcpu_reg(source_vcpu, 3); -+ -+ kvm_reset_vcpu(vcpu); -+ -+ /* Gracefully handle Thumb2 entry point */ -+ if (vcpu_mode_is_32bit(vcpu) && (target_pc & 1)) { -+ target_pc &= ~((phys_addr_t) 1); -+ vcpu_set_thumb(vcpu); -+ } -+ -+ /* Propagate caller endianness */ -+ if (kvm_vcpu_is_be(source_vcpu)) -+ kvm_vcpu_set_be(vcpu); -+ -+ *vcpu_pc(vcpu) = target_pc; -+ /* -+ * NOTE: We always update r0 (or x0) because for PSCI v0.1 -+ * the general puspose registers are undefined upon CPU_ON. -+ */ -+ *vcpu_reg(vcpu, 0) = context_id; -+ vcpu->arch.pause = false; -+ smp_mb(); /* Make sure the above is visible */ -+ -+ wq = kvm_arch_vcpu_wq(vcpu); -+ wake_up_interruptible(wq); -+ -+ return PSCI_RET_SUCCESS; -+} -+ -+static unsigned long kvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu) -+{ -+ int i; -+ unsigned long mpidr; -+ unsigned long target_affinity; -+ unsigned long target_affinity_mask; -+ unsigned long lowest_affinity_level; -+ struct kvm *kvm = vcpu->kvm; -+ struct kvm_vcpu *tmp; -+ -+ target_affinity = *vcpu_reg(vcpu, 1); -+ lowest_affinity_level = *vcpu_reg(vcpu, 2); -+ -+ /* Determine target affinity mask */ -+ target_affinity_mask = psci_affinity_mask(lowest_affinity_level); -+ if (!target_affinity_mask) -+ return PSCI_RET_INVALID_PARAMS; -+ -+ /* Ignore other bits of target affinity */ -+ target_affinity &= target_affinity_mask; -+ -+ /* -+ * If one or more VCPU matching target affinity are running -+ * then ON else OFF -+ */ -+ kvm_for_each_vcpu(i, tmp, kvm) { -+ mpidr = kvm_vcpu_get_mpidr(tmp); -+ if (((mpidr & target_affinity_mask) == target_affinity) && -+ !tmp->arch.pause) { -+ return PSCI_0_2_AFFINITY_LEVEL_ON; -+ } -+ } -+ -+ return PSCI_0_2_AFFINITY_LEVEL_OFF; -+} -+ -+static void kvm_prepare_system_event(struct kvm_vcpu *vcpu, u32 type) -+{ -+ int i; -+ struct kvm_vcpu *tmp; -+ -+ /* -+ * The KVM ABI specifies that a system event exit may call KVM_RUN -+ * again and may perform shutdown/reboot at a later time that when the -+ * actual request is made. Since we are implementing PSCI and a -+ * caller of PSCI reboot and shutdown expects that the system shuts -+ * down or reboots immediately, let's make sure that VCPUs are not run -+ * after this call is handled and before the VCPUs have been -+ * re-initialized. -+ */ -+ kvm_for_each_vcpu(i, tmp, vcpu->kvm) { -+ tmp->arch.pause = true; -+ kvm_vcpu_kick(tmp); -+ } -+ -+ memset(&vcpu->run->system_event, 0, sizeof(vcpu->run->system_event)); -+ vcpu->run->system_event.type = type; -+ vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT; -+} -+ -+static void kvm_psci_system_off(struct kvm_vcpu *vcpu) -+{ -+ kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN); -+} -+ -+static void kvm_psci_system_reset(struct kvm_vcpu *vcpu) -+{ -+ kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_RESET); -+} -+ -+int kvm_psci_version(struct kvm_vcpu *vcpu) -+{ -+ if (test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features)) -+ return KVM_ARM_PSCI_0_2; -+ -+ return KVM_ARM_PSCI_0_1; -+} -+ -+static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu) -+{ -+ int ret = 1; -+ unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0); -+ unsigned long val; -+ -+ switch (psci_fn) { -+ case PSCI_0_2_FN_PSCI_VERSION: -+ /* -+ * Bits[31:16] = Major Version = 0 -+ * Bits[15:0] = Minor Version = 2 -+ */ -+ val = 2; -+ break; -+ case PSCI_0_2_FN_CPU_SUSPEND: -+ case PSCI_0_2_FN64_CPU_SUSPEND: -+ val = kvm_psci_vcpu_suspend(vcpu); -+ break; -+ case PSCI_0_2_FN_CPU_OFF: -+ kvm_psci_vcpu_off(vcpu); -+ val = PSCI_RET_SUCCESS; -+ break; -+ case PSCI_0_2_FN_CPU_ON: -+ case PSCI_0_2_FN64_CPU_ON: -+ val = kvm_psci_vcpu_on(vcpu); -+ break; -+ case PSCI_0_2_FN_AFFINITY_INFO: -+ case PSCI_0_2_FN64_AFFINITY_INFO: -+ val = kvm_psci_vcpu_affinity_info(vcpu); -+ break; -+ case PSCI_0_2_FN_MIGRATE: -+ case PSCI_0_2_FN64_MIGRATE: -+ val = PSCI_RET_NOT_SUPPORTED; -+ break; -+ case PSCI_0_2_FN_MIGRATE_INFO_TYPE: -+ /* -+ * Trusted OS is MP hence does not require migration -+ * or -+ * Trusted OS is not present -+ */ -+ val = PSCI_0_2_TOS_MP; -+ break; -+ case PSCI_0_2_FN_MIGRATE_INFO_UP_CPU: -+ case PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU: -+ val = PSCI_RET_NOT_SUPPORTED; -+ break; -+ case PSCI_0_2_FN_SYSTEM_OFF: -+ kvm_psci_system_off(vcpu); -+ /* -+ * We should'nt be going back to guest VCPU after -+ * receiving SYSTEM_OFF request. -+ * -+ * If user space accidently/deliberately resumes -+ * guest VCPU after SYSTEM_OFF request then guest -+ * VCPU should see internal failure from PSCI return -+ * value. To achieve this, we preload r0 (or x0) with -+ * PSCI return value INTERNAL_FAILURE. -+ */ -+ val = PSCI_RET_INTERNAL_FAILURE; -+ ret = 0; -+ break; -+ case PSCI_0_2_FN_SYSTEM_RESET: -+ kvm_psci_system_reset(vcpu); -+ /* -+ * Same reason as SYSTEM_OFF for preloading r0 (or x0) -+ * with PSCI return value INTERNAL_FAILURE. -+ */ -+ val = PSCI_RET_INTERNAL_FAILURE; -+ ret = 0; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ *vcpu_reg(vcpu, 0) = val; -+ return ret; -+} -+ -+static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu) -+{ -+ unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0); -+ unsigned long val; -+ -+ switch (psci_fn) { -+ case KVM_PSCI_FN_CPU_OFF: -+ kvm_psci_vcpu_off(vcpu); -+ val = PSCI_RET_SUCCESS; -+ break; -+ case KVM_PSCI_FN_CPU_ON: -+ val = kvm_psci_vcpu_on(vcpu); -+ break; -+ case KVM_PSCI_FN_CPU_SUSPEND: -+ case KVM_PSCI_FN_MIGRATE: -+ val = PSCI_RET_NOT_SUPPORTED; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ *vcpu_reg(vcpu, 0) = val; -+ return 1; -+} -+ -+/** -+ * kvm_psci_call - handle PSCI call if r0 value is in range -+ * @vcpu: Pointer to the VCPU struct -+ * -+ * Handle PSCI calls from guests through traps from HVC instructions. -+ * The calling convention is similar to SMC calls to the secure world -+ * where the function number is placed in r0. -+ * -+ * This function returns: > 0 (success), 0 (success but exit to user -+ * space), and < 0 (errors) -+ * -+ * Errors: -+ * -EINVAL: Unrecognized PSCI function -+ */ -+int kvm_psci_call(struct kvm_vcpu *vcpu) -+{ -+ switch (kvm_psci_version(vcpu)) { -+ case KVM_ARM_PSCI_0_2: -+ return kvm_psci_0_2_call(vcpu); -+ case KVM_ARM_PSCI_0_1: -+ return kvm_psci_0_1_call(vcpu); -+ default: -+ return -EINVAL; -+ }; -+} -diff -Nur linux-3.18.14.orig/arch/arm/mach-at91/at91rm9200_time.c linux-3.18.14-rt/arch/arm/mach-at91/at91rm9200_time.c ---- linux-3.18.14.orig/arch/arm/mach-at91/at91rm9200_time.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mach-at91/at91rm9200_time.c 2015-05-31 15:32:45.673635392 -0500 -@@ -135,6 +135,7 @@ - break; - case CLOCK_EVT_MODE_SHUTDOWN: - case CLOCK_EVT_MODE_UNUSED: -+ remove_irq(NR_IRQS_LEGACY + AT91_ID_SYS, &at91rm9200_timer_irq); - case CLOCK_EVT_MODE_RESUME: - irqmask = 0; - break; -diff -Nur linux-3.18.14.orig/arch/arm/mach-exynos/platsmp.c linux-3.18.14-rt/arch/arm/mach-exynos/platsmp.c ---- linux-3.18.14.orig/arch/arm/mach-exynos/platsmp.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mach-exynos/platsmp.c 2015-05-31 15:32:45.673635392 -0500 -@@ -137,7 +137,7 @@ - return (void __iomem *)(S5P_VA_SCU); - } - --static DEFINE_SPINLOCK(boot_lock); -+static DEFINE_RAW_SPINLOCK(boot_lock); - - static void exynos_secondary_init(unsigned int cpu) - { -@@ -150,8 +150,8 @@ - /* - * Synchronise with the boot thread. - */ -- spin_lock(&boot_lock); -- spin_unlock(&boot_lock); -+ raw_spin_lock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - } - - static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) -@@ -165,7 +165,7 @@ - * Set synchronisation state between this boot processor - * and the secondary one - */ -- spin_lock(&boot_lock); -+ raw_spin_lock(&boot_lock); - - /* - * The secondary processor is waiting to be released from -@@ -192,7 +192,7 @@ - - if (timeout == 0) { - printk(KERN_ERR "cpu1 power enable failed"); -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - return -ETIMEDOUT; - } - } -@@ -242,7 +242,7 @@ - * calibrations, then wait for it to finish - */ - fail: -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - - return pen_release != -1 ? ret : 0; - } -diff -Nur linux-3.18.14.orig/arch/arm/mach-hisi/platmcpm.c linux-3.18.14-rt/arch/arm/mach-hisi/platmcpm.c ---- linux-3.18.14.orig/arch/arm/mach-hisi/platmcpm.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mach-hisi/platmcpm.c 2015-05-31 15:32:45.677635392 -0500 -@@ -57,7 +57,7 @@ - - static void __iomem *sysctrl, *fabric; - static int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER]; --static DEFINE_SPINLOCK(boot_lock); -+static DEFINE_RAW_SPINLOCK(boot_lock); - static u32 fabric_phys_addr; - /* - * [0]: bootwrapper physical address -@@ -104,7 +104,7 @@ - if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER) - return -EINVAL; - -- spin_lock_irq(&boot_lock); -+ raw_spin_lock_irq(&boot_lock); - - if (hip04_cpu_table[cluster][cpu]) - goto out; -@@ -133,7 +133,7 @@ - udelay(20); - out: - hip04_cpu_table[cluster][cpu]++; -- spin_unlock_irq(&boot_lock); -+ raw_spin_unlock_irq(&boot_lock); - - return 0; - } -@@ -149,7 +149,7 @@ - - __mcpm_cpu_going_down(cpu, cluster); - -- spin_lock(&boot_lock); -+ raw_spin_lock(&boot_lock); - BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP); - hip04_cpu_table[cluster][cpu]--; - if (hip04_cpu_table[cluster][cpu] == 1) { -@@ -162,7 +162,7 @@ - - last_man = hip04_cluster_is_down(cluster); - if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) { -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - /* Since it's Cortex A15, disable L2 prefetching. */ - asm volatile( - "mcr p15, 1, %0, c15, c0, 3 \n\t" -@@ -173,7 +173,7 @@ - hip04_set_snoop_filter(cluster, 0); - __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN); - } else { -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - v7_exit_coherency_flush(louis); - } - -@@ -192,7 +192,7 @@ - cpu >= HIP04_MAX_CPUS_PER_CLUSTER); - - count = TIMEOUT_MSEC / POLL_MSEC; -- spin_lock_irq(&boot_lock); -+ raw_spin_lock_irq(&boot_lock); - for (tries = 0; tries < count; tries++) { - if (hip04_cpu_table[cluster][cpu]) { - ret = -EBUSY; -@@ -202,10 +202,10 @@ - data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); - if (data & CORE_WFI_STATUS(cpu)) - break; -- spin_unlock_irq(&boot_lock); -+ raw_spin_unlock_irq(&boot_lock); - /* Wait for clean L2 when the whole cluster is down. */ - msleep(POLL_MSEC); -- spin_lock_irq(&boot_lock); -+ raw_spin_lock_irq(&boot_lock); - } - if (tries >= count) - goto err; -@@ -220,10 +220,10 @@ - } - if (tries >= count) - goto err; -- spin_unlock_irq(&boot_lock); -+ raw_spin_unlock_irq(&boot_lock); - return 0; - err: -- spin_unlock_irq(&boot_lock); -+ raw_spin_unlock_irq(&boot_lock); - return ret; - } - -@@ -235,10 +235,10 @@ - cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); - cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); - -- spin_lock(&boot_lock); -+ raw_spin_lock(&boot_lock); - if (!hip04_cpu_table[cluster][cpu]) - hip04_cpu_table[cluster][cpu] = 1; -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - } - - static void __naked hip04_mcpm_power_up_setup(unsigned int affinity_level) -diff -Nur linux-3.18.14.orig/arch/arm/mach-omap2/omap-smp.c linux-3.18.14-rt/arch/arm/mach-omap2/omap-smp.c ---- linux-3.18.14.orig/arch/arm/mach-omap2/omap-smp.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mach-omap2/omap-smp.c 2015-05-31 15:32:45.697635392 -0500 -@@ -43,7 +43,7 @@ - /* SCU base address */ - static void __iomem *scu_base; - --static DEFINE_SPINLOCK(boot_lock); -+static DEFINE_RAW_SPINLOCK(boot_lock); - - void __iomem *omap4_get_scu_base(void) - { -@@ -74,8 +74,8 @@ - /* - * Synchronise with the boot thread. - */ -- spin_lock(&boot_lock); -- spin_unlock(&boot_lock); -+ raw_spin_lock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - } - - static int omap4_boot_secondary(unsigned int cpu, struct task_struct *idle) -@@ -89,7 +89,7 @@ - * Set synchronisation state between this boot processor - * and the secondary one - */ -- spin_lock(&boot_lock); -+ raw_spin_lock(&boot_lock); - - /* - * Update the AuxCoreBoot0 with boot state for secondary core. -@@ -166,7 +166,7 @@ - * Now the secondary core is starting up let it run its - * calibrations, then wait for it to finish - */ -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - - return 0; - } -diff -Nur linux-3.18.14.orig/arch/arm/mach-prima2/platsmp.c linux-3.18.14-rt/arch/arm/mach-prima2/platsmp.c ---- linux-3.18.14.orig/arch/arm/mach-prima2/platsmp.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mach-prima2/platsmp.c 2015-05-31 15:32:45.721635392 -0500 -@@ -23,7 +23,7 @@ - static void __iomem *scu_base; - static void __iomem *rsc_base; - --static DEFINE_SPINLOCK(boot_lock); -+static DEFINE_RAW_SPINLOCK(boot_lock); - - static struct map_desc scu_io_desc __initdata = { - .length = SZ_4K, -@@ -56,8 +56,8 @@ - /* - * Synchronise with the boot thread. - */ -- spin_lock(&boot_lock); -- spin_unlock(&boot_lock); -+ raw_spin_lock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - } - - static struct of_device_id rsc_ids[] = { -@@ -95,7 +95,7 @@ - /* make sure write buffer is drained */ - mb(); - -- spin_lock(&boot_lock); -+ raw_spin_lock(&boot_lock); - - /* - * The secondary processor is waiting to be released from -@@ -127,7 +127,7 @@ - * now the secondary core is starting up let it run its - * calibrations, then wait for it to finish - */ -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - - return pen_release != -1 ? -ENOSYS : 0; - } -diff -Nur linux-3.18.14.orig/arch/arm/mach-qcom/platsmp.c linux-3.18.14-rt/arch/arm/mach-qcom/platsmp.c ---- linux-3.18.14.orig/arch/arm/mach-qcom/platsmp.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mach-qcom/platsmp.c 2015-05-31 15:32:45.741635391 -0500 -@@ -46,7 +46,7 @@ - - extern void secondary_startup(void); - --static DEFINE_SPINLOCK(boot_lock); -+static DEFINE_RAW_SPINLOCK(boot_lock); - - #ifdef CONFIG_HOTPLUG_CPU - static void __ref qcom_cpu_die(unsigned int cpu) -@@ -60,8 +60,8 @@ - /* - * Synchronise with the boot thread. - */ -- spin_lock(&boot_lock); -- spin_unlock(&boot_lock); -+ raw_spin_lock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - } - - static int scss_release_secondary(unsigned int cpu) -@@ -284,7 +284,7 @@ - * set synchronisation state between this boot processor - * and the secondary one - */ -- spin_lock(&boot_lock); -+ raw_spin_lock(&boot_lock); - - /* - * Send the secondary CPU a soft interrupt, thereby causing -@@ -297,7 +297,7 @@ - * now the secondary core is starting up let it run its - * calibrations, then wait for it to finish - */ -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - - return ret; - } -diff -Nur linux-3.18.14.orig/arch/arm/mach-spear/platsmp.c linux-3.18.14-rt/arch/arm/mach-spear/platsmp.c ---- linux-3.18.14.orig/arch/arm/mach-spear/platsmp.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mach-spear/platsmp.c 2015-05-31 15:32:45.749635392 -0500 -@@ -32,7 +32,7 @@ - sync_cache_w(&pen_release); - } - --static DEFINE_SPINLOCK(boot_lock); -+static DEFINE_RAW_SPINLOCK(boot_lock); - - static void __iomem *scu_base = IOMEM(VA_SCU_BASE); - -@@ -47,8 +47,8 @@ - /* - * Synchronise with the boot thread. - */ -- spin_lock(&boot_lock); -- spin_unlock(&boot_lock); -+ raw_spin_lock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - } - - static int spear13xx_boot_secondary(unsigned int cpu, struct task_struct *idle) -@@ -59,7 +59,7 @@ - * set synchronisation state between this boot processor - * and the secondary one - */ -- spin_lock(&boot_lock); -+ raw_spin_lock(&boot_lock); - - /* - * The secondary processor is waiting to be released from -@@ -84,7 +84,7 @@ - * now the secondary core is starting up let it run its - * calibrations, then wait for it to finish - */ -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - - return pen_release != -1 ? -ENOSYS : 0; - } -diff -Nur linux-3.18.14.orig/arch/arm/mach-sti/platsmp.c linux-3.18.14-rt/arch/arm/mach-sti/platsmp.c ---- linux-3.18.14.orig/arch/arm/mach-sti/platsmp.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mach-sti/platsmp.c 2015-05-31 15:32:45.765635392 -0500 -@@ -34,7 +34,7 @@ - sync_cache_w(&pen_release); - } - --static DEFINE_SPINLOCK(boot_lock); -+static DEFINE_RAW_SPINLOCK(boot_lock); - - static void sti_secondary_init(unsigned int cpu) - { -@@ -49,8 +49,8 @@ - /* - * Synchronise with the boot thread. - */ -- spin_lock(&boot_lock); -- spin_unlock(&boot_lock); -+ raw_spin_lock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - } - - static int sti_boot_secondary(unsigned int cpu, struct task_struct *idle) -@@ -61,7 +61,7 @@ - * set synchronisation state between this boot processor - * and the secondary one - */ -- spin_lock(&boot_lock); -+ raw_spin_lock(&boot_lock); - - /* - * The secondary processor is waiting to be released from -@@ -92,7 +92,7 @@ - * now the secondary core is starting up let it run its - * calibrations, then wait for it to finish - */ -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - - return pen_release != -1 ? -ENOSYS : 0; - } -diff -Nur linux-3.18.14.orig/arch/arm/mach-ux500/platsmp.c linux-3.18.14-rt/arch/arm/mach-ux500/platsmp.c ---- linux-3.18.14.orig/arch/arm/mach-ux500/platsmp.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mach-ux500/platsmp.c 2015-05-31 15:32:45.793635391 -0500 -@@ -51,7 +51,7 @@ - return NULL; - } - --static DEFINE_SPINLOCK(boot_lock); -+static DEFINE_RAW_SPINLOCK(boot_lock); - - static void ux500_secondary_init(unsigned int cpu) - { -@@ -64,8 +64,8 @@ - /* - * Synchronise with the boot thread. - */ -- spin_lock(&boot_lock); -- spin_unlock(&boot_lock); -+ raw_spin_lock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - } - - static int ux500_boot_secondary(unsigned int cpu, struct task_struct *idle) -@@ -76,7 +76,7 @@ - * set synchronisation state between this boot processor - * and the secondary one - */ -- spin_lock(&boot_lock); -+ raw_spin_lock(&boot_lock); - - /* - * The secondary processor is waiting to be released from -@@ -97,7 +97,7 @@ - * now the secondary core is starting up let it run its - * calibrations, then wait for it to finish - */ -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - - return pen_release != -1 ? -ENOSYS : 0; - } -diff -Nur linux-3.18.14.orig/arch/arm/mm/fault.c linux-3.18.14-rt/arch/arm/mm/fault.c ---- linux-3.18.14.orig/arch/arm/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mm/fault.c 2015-05-31 15:32:45.797635391 -0500 -@@ -277,7 +277,7 @@ - * If we're in an interrupt or have no user - * context, we must not take the fault.. - */ -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto no_context; - - if (user_mode(regs)) -@@ -431,6 +431,9 @@ - if (addr < TASK_SIZE) - return do_page_fault(addr, fsr, regs); - -+ if (interrupts_enabled(regs)) -+ local_irq_enable(); -+ - if (user_mode(regs)) - goto bad_area; - -@@ -498,6 +501,9 @@ - static int - do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) - { -+ if (interrupts_enabled(regs)) -+ local_irq_enable(); -+ - do_bad_area(addr, fsr, regs); - return 0; - } -diff -Nur linux-3.18.14.orig/arch/arm/mm/highmem.c linux-3.18.14-rt/arch/arm/mm/highmem.c ---- linux-3.18.14.orig/arch/arm/mm/highmem.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/mm/highmem.c 2015-05-31 15:32:45.805635391 -0500 -@@ -53,6 +53,7 @@ - - void *kmap_atomic(struct page *page) - { -+ pte_t pte = mk_pte(page, kmap_prot); - unsigned int idx; - unsigned long vaddr; - void *kmap; -@@ -91,7 +92,10 @@ - * in place, so the contained TLB flush ensures the TLB is updated - * with the new mapping. - */ -- set_fixmap_pte(idx, mk_pte(page, kmap_prot)); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ current->kmap_pte[type] = pte; -+#endif -+ set_fixmap_pte(idx, pte); - - return (void *)vaddr; - } -@@ -108,12 +112,15 @@ - - if (cache_is_vivt()) - __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ current->kmap_pte[type] = __pte(0); -+#endif - #ifdef CONFIG_DEBUG_HIGHMEM - BUG_ON(vaddr != __fix_to_virt(idx)); -- set_fixmap_pte(idx, __pte(0)); - #else - (void) idx; /* to kill a warning */ - #endif -+ set_fixmap_pte(idx, __pte(0)); - kmap_atomic_idx_pop(); - } else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) { - /* this address was obtained through kmap_high_get() */ -@@ -125,6 +132,7 @@ - - void *kmap_atomic_pfn(unsigned long pfn) - { -+ pte_t pte = pfn_pte(pfn, kmap_prot); - unsigned long vaddr; - int idx, type; - struct page *page = pfn_to_page(pfn); -@@ -139,7 +147,10 @@ - #ifdef CONFIG_DEBUG_HIGHMEM - BUG_ON(!pte_none(*(fixmap_page_table + idx))); - #endif -- set_fixmap_pte(idx, pfn_pte(pfn, kmap_prot)); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ current->kmap_pte[type] = pte; -+#endif -+ set_fixmap_pte(idx, pte); - - return (void *)vaddr; - } -@@ -153,3 +164,28 @@ - - return pte_page(get_fixmap_pte(vaddr)); - } -+ -+#if defined CONFIG_PREEMPT_RT_FULL -+void switch_kmaps(struct task_struct *prev_p, struct task_struct *next_p) -+{ -+ int i; -+ -+ /* -+ * Clear @prev's kmap_atomic mappings -+ */ -+ for (i = 0; i < prev_p->kmap_idx; i++) { -+ int idx = i + KM_TYPE_NR * smp_processor_id(); -+ -+ set_fixmap_pte(idx, __pte(0)); -+ } -+ /* -+ * Restore @next_p's kmap_atomic mappings -+ */ -+ for (i = 0; i < next_p->kmap_idx; i++) { -+ int idx = i + KM_TYPE_NR * smp_processor_id(); -+ -+ if (!pte_none(next_p->kmap_pte[i])) -+ set_fixmap_pte(idx, next_p->kmap_pte[i]); -+ } -+} -+#endif -diff -Nur linux-3.18.14.orig/arch/arm/plat-versatile/platsmp.c linux-3.18.14-rt/arch/arm/plat-versatile/platsmp.c ---- linux-3.18.14.orig/arch/arm/plat-versatile/platsmp.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm/plat-versatile/platsmp.c 2015-05-31 15:32:45.889635390 -0500 -@@ -30,7 +30,7 @@ - sync_cache_w(&pen_release); - } - --static DEFINE_SPINLOCK(boot_lock); -+static DEFINE_RAW_SPINLOCK(boot_lock); - - void versatile_secondary_init(unsigned int cpu) - { -@@ -43,8 +43,8 @@ - /* - * Synchronise with the boot thread. - */ -- spin_lock(&boot_lock); -- spin_unlock(&boot_lock); -+ raw_spin_lock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - } - - int versatile_boot_secondary(unsigned int cpu, struct task_struct *idle) -@@ -55,7 +55,7 @@ - * Set synchronisation state between this boot processor - * and the secondary one - */ -- spin_lock(&boot_lock); -+ raw_spin_lock(&boot_lock); - - /* - * This is really belt and braces; we hold unintended secondary -@@ -85,7 +85,7 @@ - * now the secondary core is starting up let it run its - * calibrations, then wait for it to finish - */ -- spin_unlock(&boot_lock); -+ raw_spin_unlock(&boot_lock); - - return pen_release != -1 ? -ENOSYS : 0; - } -diff -Nur linux-3.18.14.orig/arch/arm64/include/asm/thread_info.h linux-3.18.14-rt/arch/arm64/include/asm/thread_info.h ---- linux-3.18.14.orig/arch/arm64/include/asm/thread_info.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm64/include/asm/thread_info.h 2015-05-31 15:32:45.925635390 -0500 -@@ -50,6 +50,7 @@ - struct exec_domain *exec_domain; /* execution domain */ - struct restart_block restart_block; - int preempt_count; /* 0 => preemptable, <0 => bug */ -+ int preempt_lazy_count; /* 0 => preemptable, <0 => bug */ - int cpu; /* cpu */ - }; - -@@ -108,6 +109,7 @@ - #define TIF_NEED_RESCHED 1 - #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ - #define TIF_FOREIGN_FPSTATE 3 /* CPU's FP state is not current's */ -+#define TIF_NEED_RESCHED_LAZY 4 - #define TIF_NOHZ 7 - #define TIF_SYSCALL_TRACE 8 - #define TIF_SYSCALL_AUDIT 9 -@@ -124,6 +126,7 @@ - #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) - #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) - #define _TIF_FOREIGN_FPSTATE (1 << TIF_FOREIGN_FPSTATE) -+#define _TIF_NEED_RESCHED_LAZY (1 << TIF_NEED_RESCHED_LAZY) - #define _TIF_NOHZ (1 << TIF_NOHZ) - #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) - #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) -diff -Nur linux-3.18.14.orig/arch/arm64/Kconfig linux-3.18.14-rt/arch/arm64/Kconfig ---- linux-3.18.14.orig/arch/arm64/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm64/Kconfig 2015-05-31 15:32:45.905635390 -0500 -@@ -59,8 +59,10 @@ - select HAVE_PERF_REGS - select HAVE_PERF_USER_STACK_DUMP - select HAVE_RCU_TABLE_FREE -+ select HAVE_PREEMPT_LAZY - select HAVE_SYSCALL_TRACEPOINTS - select IRQ_DOMAIN -+ select IRQ_FORCED_THREADING - select MODULES_USE_ELF_RELA - select NO_BOOTMEM - select OF -diff -Nur linux-3.18.14.orig/arch/arm64/kernel/asm-offsets.c linux-3.18.14-rt/arch/arm64/kernel/asm-offsets.c ---- linux-3.18.14.orig/arch/arm64/kernel/asm-offsets.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm64/kernel/asm-offsets.c 2015-05-31 15:32:45.925635390 -0500 -@@ -36,6 +36,7 @@ - BLANK(); - DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); - DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); -+ DEFINE(TI_PREEMPT_LAZY, offsetof(struct thread_info, preempt_lazy_count)); - DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); - DEFINE(TI_TASK, offsetof(struct thread_info, task)); - DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain)); -diff -Nur linux-3.18.14.orig/arch/arm64/kernel/entry.S linux-3.18.14-rt/arch/arm64/kernel/entry.S ---- linux-3.18.14.orig/arch/arm64/kernel/entry.S 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm64/kernel/entry.S 2015-05-31 15:32:45.925635390 -0500 -@@ -367,11 +367,16 @@ - #ifdef CONFIG_PREEMPT - get_thread_info tsk - ldr w24, [tsk, #TI_PREEMPT] // get preempt count -- cbnz w24, 1f // preempt count != 0 -+ cbnz w24, 2f // preempt count != 0 - ldr x0, [tsk, #TI_FLAGS] // get flags -- tbz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling? -- bl el1_preempt -+ tbnz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling? -+ -+ ldr w24, [tsk, #TI_PREEMPT_LAZY] // get preempt lazy count -+ cbnz w24, 2f // preempt lazy count != 0 -+ tbz x0, #TIF_NEED_RESCHED_LAZY, 2f // needs rescheduling? - 1: -+ bl el1_preempt -+2: - #endif - #ifdef CONFIG_TRACE_IRQFLAGS - bl trace_hardirqs_on -@@ -385,6 +390,7 @@ - 1: bl preempt_schedule_irq // irq en/disable is done inside - ldr x0, [tsk, #TI_FLAGS] // get new tasks TI_FLAGS - tbnz x0, #TIF_NEED_RESCHED, 1b // needs rescheduling? -+ tbnz x0, #TIF_NEED_RESCHED_LAZY, 1b // needs rescheduling? - ret x24 - #endif - -@@ -621,6 +627,7 @@ - str x0, [sp, #S_X0] // returned x0 - work_pending: - tbnz x1, #TIF_NEED_RESCHED, work_resched -+ tbnz x1, #TIF_NEED_RESCHED_LAZY, work_resched - /* TIF_SIGPENDING, TIF_NOTIFY_RESUME or TIF_FOREIGN_FPSTATE case */ - ldr x2, [sp, #S_PSTATE] - mov x0, sp // 'regs' -diff -Nur linux-3.18.14.orig/arch/arm64/kernel/perf_event.c linux-3.18.14-rt/arch/arm64/kernel/perf_event.c ---- linux-3.18.14.orig/arch/arm64/kernel/perf_event.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/arm64/kernel/perf_event.c 2015-05-31 15:32:45.925635390 -0500 -@@ -461,7 +461,7 @@ - } - - err = request_irq(irq, armpmu->handle_irq, -- IRQF_NOBALANCING, -+ IRQF_NOBALANCING | IRQF_NO_THREAD, - "arm-pmu", armpmu); - if (err) { - pr_err("unable to request IRQ%d for ARM PMU counters\n", -diff -Nur linux-3.18.14.orig/arch/avr32/mm/fault.c linux-3.18.14-rt/arch/avr32/mm/fault.c ---- linux-3.18.14.orig/arch/avr32/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/avr32/mm/fault.c 2015-05-31 15:32:45.933635390 -0500 -@@ -81,7 +81,7 @@ - * If we're in an interrupt or have no user context, we must - * not take the fault... - */ -- if (in_atomic() || !mm || regs->sr & SYSREG_BIT(GM)) -+ if (!mm || regs->sr & SYSREG_BIT(GM) || pagefault_disabled()) - goto no_context; - - local_irq_enable(); -diff -Nur linux-3.18.14.orig/arch/cris/mm/fault.c linux-3.18.14-rt/arch/cris/mm/fault.c ---- linux-3.18.14.orig/arch/cris/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/cris/mm/fault.c 2015-05-31 15:32:45.945635390 -0500 -@@ -113,7 +113,7 @@ - * user context, we must not take the fault. - */ - -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto no_context; - - if (user_mode(regs)) -diff -Nur linux-3.18.14.orig/arch/frv/mm/fault.c linux-3.18.14-rt/arch/frv/mm/fault.c ---- linux-3.18.14.orig/arch/frv/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/frv/mm/fault.c 2015-05-31 15:32:45.953635390 -0500 -@@ -78,7 +78,7 @@ - * If we're in an interrupt or have no user - * context, we must not take the fault.. - */ -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto no_context; - - if (user_mode(__frame)) -diff -Nur linux-3.18.14.orig/arch/ia64/mm/fault.c linux-3.18.14-rt/arch/ia64/mm/fault.c ---- linux-3.18.14.orig/arch/ia64/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/ia64/mm/fault.c 2015-05-31 15:32:45.961635389 -0500 -@@ -96,7 +96,7 @@ - /* - * If we're in an interrupt or have no user context, we must not take the fault.. - */ -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto no_context; - - #ifdef CONFIG_VIRTUAL_MEM_MAP -diff -Nur linux-3.18.14.orig/arch/Kconfig linux-3.18.14-rt/arch/Kconfig ---- linux-3.18.14.orig/arch/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/Kconfig 2015-05-31 15:32:45.501635394 -0500 -@@ -6,6 +6,7 @@ - tristate "OProfile system profiling" - depends on PROFILING - depends on HAVE_OPROFILE -+ depends on !PREEMPT_RT_FULL - select RING_BUFFER - select RING_BUFFER_ALLOW_SWAP - help -diff -Nur linux-3.18.14.orig/arch/m32r/mm/fault.c linux-3.18.14-rt/arch/m32r/mm/fault.c ---- linux-3.18.14.orig/arch/m32r/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/m32r/mm/fault.c 2015-05-31 15:32:45.985635389 -0500 -@@ -114,7 +114,7 @@ - * If we're in an interrupt or have no user context or are running in an - * atomic region then we must not take the fault.. - */ -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto bad_area_nosemaphore; - - if (error_code & ACE_USERMODE) -diff -Nur linux-3.18.14.orig/arch/m68k/mm/fault.c linux-3.18.14-rt/arch/m68k/mm/fault.c ---- linux-3.18.14.orig/arch/m68k/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/m68k/mm/fault.c 2015-05-31 15:32:45.985635389 -0500 -@@ -81,7 +81,7 @@ - * If we're in an interrupt or have no user - * context, we must not take the fault.. - */ -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto no_context; - - if (user_mode(regs)) -diff -Nur linux-3.18.14.orig/arch/microblaze/mm/fault.c linux-3.18.14-rt/arch/microblaze/mm/fault.c ---- linux-3.18.14.orig/arch/microblaze/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/microblaze/mm/fault.c 2015-05-31 15:32:46.005635389 -0500 -@@ -107,7 +107,7 @@ - if ((error_code & 0x13) == 0x13 || (error_code & 0x11) == 0x11) - is_write = 0; - -- if (unlikely(in_atomic() || !mm)) { -+ if (unlikely(!mm || pagefault_disabled())) { - if (kernel_mode(regs)) - goto bad_area_nosemaphore; - -diff -Nur linux-3.18.14.orig/arch/mips/Kconfig linux-3.18.14-rt/arch/mips/Kconfig ---- linux-3.18.14.orig/arch/mips/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/mips/Kconfig 2015-05-31 15:32:46.033635389 -0500 -@@ -2196,7 +2196,7 @@ - # - config HIGHMEM - bool "High Memory Support" -- depends on 32BIT && CPU_SUPPORTS_HIGHMEM && SYS_SUPPORTS_HIGHMEM && !CPU_MIPS32_3_5_EVA -+ depends on 32BIT && CPU_SUPPORTS_HIGHMEM && SYS_SUPPORTS_HIGHMEM && !CPU_MIPS32_3_5_EVA && !PREEMPT_RT_FULL - - config CPU_SUPPORTS_HIGHMEM - bool -diff -Nur linux-3.18.14.orig/arch/mips/kernel/signal.c linux-3.18.14-rt/arch/mips/kernel/signal.c ---- linux-3.18.14.orig/arch/mips/kernel/signal.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/mips/kernel/signal.c 2015-05-31 15:32:46.057635389 -0500 -@@ -613,6 +613,7 @@ - __u32 thread_info_flags) - { - local_irq_enable(); -+ preempt_check_resched(); - - user_exit(); - -diff -Nur linux-3.18.14.orig/arch/mips/mm/fault.c linux-3.18.14-rt/arch/mips/mm/fault.c ---- linux-3.18.14.orig/arch/mips/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/mips/mm/fault.c 2015-05-31 15:32:46.069635388 -0500 -@@ -89,7 +89,7 @@ - * If we're in an interrupt or have no user - * context, we must not take the fault.. - */ -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto bad_area_nosemaphore; - - if (user_mode(regs)) -diff -Nur linux-3.18.14.orig/arch/mips/mm/init.c linux-3.18.14-rt/arch/mips/mm/init.c ---- linux-3.18.14.orig/arch/mips/mm/init.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/mips/mm/init.c 2015-05-31 15:32:46.069635388 -0500 -@@ -90,7 +90,7 @@ - - BUG_ON(Page_dcache_dirty(page)); - -- pagefault_disable(); -+ raw_pagefault_disable(); - idx = (addr >> PAGE_SHIFT) & (FIX_N_COLOURS - 1); - idx += in_interrupt() ? FIX_N_COLOURS : 0; - vaddr = __fix_to_virt(FIX_CMAP_END - idx); -@@ -146,7 +146,7 @@ - tlbw_use_hazard(); - write_c0_entryhi(old_ctx); - local_irq_restore(flags); -- pagefault_enable(); -+ raw_pagefault_enable(); - } - - void copy_user_highpage(struct page *to, struct page *from, -diff -Nur linux-3.18.14.orig/arch/mn10300/mm/fault.c linux-3.18.14-rt/arch/mn10300/mm/fault.c ---- linux-3.18.14.orig/arch/mn10300/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/mn10300/mm/fault.c 2015-05-31 15:32:46.113635388 -0500 -@@ -168,7 +168,7 @@ - * If we're in an interrupt or have no user - * context, we must not take the fault.. - */ -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto no_context; - - if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) -diff -Nur linux-3.18.14.orig/arch/parisc/mm/fault.c linux-3.18.14-rt/arch/parisc/mm/fault.c ---- linux-3.18.14.orig/arch/parisc/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/parisc/mm/fault.c 2015-05-31 15:32:46.113635388 -0500 -@@ -207,7 +207,7 @@ - int fault; - unsigned int flags; - -- if (in_atomic()) -+ if (pagefault_disabled()) - goto no_context; - - tsk = current; -diff -Nur linux-3.18.14.orig/arch/powerpc/include/asm/kvm_host.h linux-3.18.14-rt/arch/powerpc/include/asm/kvm_host.h ---- linux-3.18.14.orig/arch/powerpc/include/asm/kvm_host.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/powerpc/include/asm/kvm_host.h 2015-05-31 15:32:46.145635388 -0500 -@@ -296,7 +296,7 @@ - u8 in_guest; - struct list_head runnable_threads; - spinlock_t lock; -- wait_queue_head_t wq; -+ struct swait_head wq; - u64 stolen_tb; - u64 preempt_tb; - struct kvm_vcpu *runner; -@@ -618,7 +618,7 @@ - u8 prodded; - u32 last_inst; - -- wait_queue_head_t *wqp; -+ struct swait_head *wqp; - struct kvmppc_vcore *vcore; - int ret; - int trap; -diff -Nur linux-3.18.14.orig/arch/powerpc/include/asm/thread_info.h linux-3.18.14-rt/arch/powerpc/include/asm/thread_info.h ---- linux-3.18.14.orig/arch/powerpc/include/asm/thread_info.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/powerpc/include/asm/thread_info.h 2015-05-31 15:32:46.165635388 -0500 -@@ -43,6 +43,8 @@ - int cpu; /* cpu we're on */ - int preempt_count; /* 0 => preemptable, - <0 => BUG */ -+ int preempt_lazy_count; /* 0 => preemptable, -+ <0 => BUG */ - struct restart_block restart_block; - unsigned long local_flags; /* private flags for thread */ - -@@ -88,8 +90,7 @@ - #define TIF_SYSCALL_TRACE 0 /* syscall trace active */ - #define TIF_SIGPENDING 1 /* signal pending */ - #define TIF_NEED_RESCHED 2 /* rescheduling necessary */ --#define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling -- TIF_NEED_RESCHED */ -+#define TIF_NEED_RESCHED_LAZY 3 /* lazy rescheduling necessary */ - #define TIF_32BIT 4 /* 32 bit binary */ - #define TIF_RESTORE_TM 5 /* need to restore TM FP/VEC/VSX */ - #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ -@@ -107,6 +108,8 @@ - #if defined(CONFIG_PPC64) - #define TIF_ELF2ABI 18 /* function descriptors must die! */ - #endif -+#define TIF_POLLING_NRFLAG 19 /* true if poll_idle() is polling -+ TIF_NEED_RESCHED */ - - /* as above, but as bit values */ - #define _TIF_SYSCALL_TRACE (1<flags) - set_bits(irqtp->flags, &curtp->flags); - } -+#endif - - irq_hw_number_t virq_to_hw(unsigned int virq) - { -diff -Nur linux-3.18.14.orig/arch/powerpc/kernel/misc_32.S linux-3.18.14-rt/arch/powerpc/kernel/misc_32.S ---- linux-3.18.14.orig/arch/powerpc/kernel/misc_32.S 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/powerpc/kernel/misc_32.S 2015-05-31 15:32:46.261635387 -0500 -@@ -40,6 +40,7 @@ - * We store the saved ksp_limit in the unused part - * of the STACK_FRAME_OVERHEAD - */ -+#ifndef CONFIG_PREEMPT_RT_FULL - _GLOBAL(call_do_softirq) - mflr r0 - stw r0,4(r1) -@@ -56,6 +57,7 @@ - stw r10,THREAD+KSP_LIMIT(r2) - mtlr r0 - blr -+#endif - - /* - * void call_do_irq(struct pt_regs *regs, struct thread_info *irqtp); -diff -Nur linux-3.18.14.orig/arch/powerpc/kernel/misc_64.S linux-3.18.14-rt/arch/powerpc/kernel/misc_64.S ---- linux-3.18.14.orig/arch/powerpc/kernel/misc_64.S 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/powerpc/kernel/misc_64.S 2015-05-31 15:32:46.261635387 -0500 -@@ -29,6 +29,7 @@ - - .text - -+#ifndef CONFIG_PREEMPT_RT_FULL - _GLOBAL(call_do_softirq) - mflr r0 - std r0,16(r1) -@@ -39,6 +40,7 @@ - ld r0,16(r1) - mtlr r0 - blr -+#endif - - _GLOBAL(call_do_irq) - mflr r0 -diff -Nur linux-3.18.14.orig/arch/powerpc/kernel/time.c linux-3.18.14-rt/arch/powerpc/kernel/time.c ---- linux-3.18.14.orig/arch/powerpc/kernel/time.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/powerpc/kernel/time.c 2015-05-31 15:32:46.261635387 -0500 -@@ -424,7 +424,7 @@ - EXPORT_SYMBOL(profile_pc); - #endif - --#ifdef CONFIG_IRQ_WORK -+#if defined(CONFIG_IRQ_WORK) - - /* - * 64-bit uses a byte in the PACA, 32-bit uses a per-cpu variable... -diff -Nur linux-3.18.14.orig/arch/powerpc/kvm/book3s_hv.c linux-3.18.14-rt/arch/powerpc/kvm/book3s_hv.c ---- linux-3.18.14.orig/arch/powerpc/kvm/book3s_hv.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/powerpc/kvm/book3s_hv.c 2015-05-31 15:32:46.301635387 -0500 -@@ -84,11 +84,11 @@ - { - int me; - int cpu = vcpu->cpu; -- wait_queue_head_t *wqp; -+ struct swait_head *wqp; - - wqp = kvm_arch_vcpu_wq(vcpu); -- if (waitqueue_active(wqp)) { -- wake_up_interruptible(wqp); -+ if (swaitqueue_active(wqp)) { -+ swait_wake_interruptible(wqp); - ++vcpu->stat.halt_wakeup; - } - -@@ -639,8 +639,8 @@ - tvcpu->arch.prodded = 1; - smp_mb(); - if (vcpu->arch.ceded) { -- if (waitqueue_active(&vcpu->wq)) { -- wake_up_interruptible(&vcpu->wq); -+ if (swaitqueue_active(&vcpu->wq)) { -+ swait_wake_interruptible(&vcpu->wq); - vcpu->stat.halt_wakeup++; - } - } -@@ -1357,7 +1357,7 @@ - - INIT_LIST_HEAD(&vcore->runnable_threads); - spin_lock_init(&vcore->lock); -- init_waitqueue_head(&vcore->wq); -+ init_swait_head(&vcore->wq); - vcore->preempt_tb = TB_NIL; - vcore->lpcr = kvm->arch.lpcr; - vcore->first_vcpuid = core * threads_per_subcore; -@@ -1826,13 +1826,13 @@ - */ - static void kvmppc_vcore_blocked(struct kvmppc_vcore *vc) - { -- DEFINE_WAIT(wait); -+ DEFINE_SWAITER(wait); - -- prepare_to_wait(&vc->wq, &wait, TASK_INTERRUPTIBLE); -+ swait_prepare(&vc->wq, &wait, TASK_INTERRUPTIBLE); - vc->vcore_state = VCORE_SLEEPING; - spin_unlock(&vc->lock); - schedule(); -- finish_wait(&vc->wq, &wait); -+ swait_finish(&vc->wq, &wait); - spin_lock(&vc->lock); - vc->vcore_state = VCORE_INACTIVE; - } -@@ -1873,7 +1873,7 @@ - kvmppc_create_dtl_entry(vcpu, vc); - kvmppc_start_thread(vcpu); - } else if (vc->vcore_state == VCORE_SLEEPING) { -- wake_up(&vc->wq); -+ swait_wake(&vc->wq); - } - - } -diff -Nur linux-3.18.14.orig/arch/powerpc/kvm/Kconfig linux-3.18.14-rt/arch/powerpc/kvm/Kconfig ---- linux-3.18.14.orig/arch/powerpc/kvm/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/powerpc/kvm/Kconfig 2015-05-31 15:32:46.281635387 -0500 -@@ -157,6 +157,7 @@ - config KVM_MPIC - bool "KVM in-kernel MPIC emulation" - depends on KVM && E500 -+ depends on !PREEMPT_RT_FULL - select HAVE_KVM_IRQCHIP - select HAVE_KVM_IRQFD - select HAVE_KVM_IRQ_ROUTING -diff -Nur linux-3.18.14.orig/arch/powerpc/mm/fault.c linux-3.18.14-rt/arch/powerpc/mm/fault.c ---- linux-3.18.14.orig/arch/powerpc/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/powerpc/mm/fault.c 2015-05-31 15:32:46.325635386 -0500 -@@ -273,7 +273,7 @@ - if (!arch_irq_disabled_regs(regs)) - local_irq_enable(); - -- if (in_atomic() || mm == NULL) { -+ if (in_atomic() || mm == NULL || pagefault_disabled()) { - if (!user_mode(regs)) { - rc = SIGSEGV; - goto bail; -diff -Nur linux-3.18.14.orig/arch/s390/include/asm/kvm_host.h linux-3.18.14-rt/arch/s390/include/asm/kvm_host.h ---- linux-3.18.14.orig/arch/s390/include/asm/kvm_host.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/s390/include/asm/kvm_host.h 2015-05-31 15:32:46.369635386 -0500 -@@ -311,7 +311,7 @@ - struct list_head list; - atomic_t active; - struct kvm_s390_float_interrupt *float_int; -- wait_queue_head_t *wq; -+ struct swait_head *wq; - atomic_t *cpuflags; - unsigned int action_bits; - }; -diff -Nur linux-3.18.14.orig/arch/s390/kvm/interrupt.c linux-3.18.14-rt/arch/s390/kvm/interrupt.c ---- linux-3.18.14.orig/arch/s390/kvm/interrupt.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/s390/kvm/interrupt.c 2015-05-31 15:32:46.385635386 -0500 -@@ -620,13 +620,13 @@ - - void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu) - { -- if (waitqueue_active(&vcpu->wq)) { -+ if (swaitqueue_active(&vcpu->wq)) { - /* - * The vcpu gave up the cpu voluntarily, mark it as a good - * yield-candidate. - */ - vcpu->preempted = true; -- wake_up_interruptible(&vcpu->wq); -+ swait_wake_interruptible(&vcpu->wq); - vcpu->stat.halt_wakeup++; - } - } -@@ -747,7 +747,7 @@ - spin_lock(&li->lock); - list_add(&inti->list, &li->list); - atomic_set(&li->active, 1); -- BUG_ON(waitqueue_active(li->wq)); -+ BUG_ON(swaitqueue_active(li->wq)); - spin_unlock(&li->lock); - return 0; - } -@@ -772,7 +772,7 @@ - spin_lock(&li->lock); - list_add(&inti->list, &li->list); - atomic_set(&li->active, 1); -- BUG_ON(waitqueue_active(li->wq)); -+ BUG_ON(swaitqueue_active(li->wq)); - spin_unlock(&li->lock); - return 0; - } -diff -Nur linux-3.18.14.orig/arch/s390/kvm/interrupt.c.orig linux-3.18.14-rt/arch/s390/kvm/interrupt.c.orig ---- linux-3.18.14.orig/arch/s390/kvm/interrupt.c.orig 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/arch/s390/kvm/interrupt.c.orig 2015-05-20 10:04:50.000000000 -0500 -@@ -0,0 +1,1541 @@ -+/* -+ * handling kvm guest interrupts -+ * -+ * Copyright IBM Corp. 2008,2014 -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License (version 2 only) -+ * as published by the Free Software Foundation. -+ * -+ * Author(s): Carsten Otte -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "kvm-s390.h" -+#include "gaccess.h" -+#include "trace-s390.h" -+ -+#define IOINT_SCHID_MASK 0x0000ffff -+#define IOINT_SSID_MASK 0x00030000 -+#define IOINT_CSSID_MASK 0x03fc0000 -+#define IOINT_AI_MASK 0x04000000 -+#define PFAULT_INIT 0x0600 -+ -+static int __must_check deliver_ckc_interrupt(struct kvm_vcpu *vcpu); -+ -+static int is_ioint(u64 type) -+{ -+ return ((type & 0xfffe0000u) != 0xfffe0000u); -+} -+ -+int psw_extint_disabled(struct kvm_vcpu *vcpu) -+{ -+ return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT); -+} -+ -+static int psw_ioint_disabled(struct kvm_vcpu *vcpu) -+{ -+ return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO); -+} -+ -+static int psw_mchk_disabled(struct kvm_vcpu *vcpu) -+{ -+ return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK); -+} -+ -+static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) -+{ -+ if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) || -+ (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO) || -+ (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT)) -+ return 0; -+ return 1; -+} -+ -+static int ckc_interrupts_enabled(struct kvm_vcpu *vcpu) -+{ -+ if (psw_extint_disabled(vcpu) || -+ !(vcpu->arch.sie_block->gcr[0] & 0x800ul)) -+ return 0; -+ if (guestdbg_enabled(vcpu) && guestdbg_sstep_enabled(vcpu)) -+ /* No timer interrupts when single stepping */ -+ return 0; -+ return 1; -+} -+ -+static u64 int_word_to_isc_bits(u32 int_word) -+{ -+ u8 isc = (int_word & 0x38000000) >> 27; -+ -+ return (0x80 >> isc) << 24; -+} -+ -+static int __must_check __interrupt_is_deliverable(struct kvm_vcpu *vcpu, -+ struct kvm_s390_interrupt_info *inti) -+{ -+ switch (inti->type) { -+ case KVM_S390_INT_EXTERNAL_CALL: -+ if (psw_extint_disabled(vcpu)) -+ return 0; -+ if (vcpu->arch.sie_block->gcr[0] & 0x2000ul) -+ return 1; -+ return 0; -+ case KVM_S390_INT_EMERGENCY: -+ if (psw_extint_disabled(vcpu)) -+ return 0; -+ if (vcpu->arch.sie_block->gcr[0] & 0x4000ul) -+ return 1; -+ return 0; -+ case KVM_S390_INT_CLOCK_COMP: -+ return ckc_interrupts_enabled(vcpu); -+ case KVM_S390_INT_CPU_TIMER: -+ if (psw_extint_disabled(vcpu)) -+ return 0; -+ if (vcpu->arch.sie_block->gcr[0] & 0x400ul) -+ return 1; -+ return 0; -+ case KVM_S390_INT_SERVICE: -+ case KVM_S390_INT_PFAULT_INIT: -+ case KVM_S390_INT_PFAULT_DONE: -+ case KVM_S390_INT_VIRTIO: -+ if (psw_extint_disabled(vcpu)) -+ return 0; -+ if (vcpu->arch.sie_block->gcr[0] & 0x200ul) -+ return 1; -+ return 0; -+ case KVM_S390_PROGRAM_INT: -+ case KVM_S390_SIGP_STOP: -+ case KVM_S390_SIGP_SET_PREFIX: -+ case KVM_S390_RESTART: -+ return 1; -+ case KVM_S390_MCHK: -+ if (psw_mchk_disabled(vcpu)) -+ return 0; -+ if (vcpu->arch.sie_block->gcr[14] & inti->mchk.cr14) -+ return 1; -+ return 0; -+ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: -+ if (psw_ioint_disabled(vcpu)) -+ return 0; -+ if (vcpu->arch.sie_block->gcr[6] & -+ int_word_to_isc_bits(inti->io.io_int_word)) -+ return 1; -+ return 0; -+ default: -+ printk(KERN_WARNING "illegal interrupt type %llx\n", -+ inti->type); -+ BUG(); -+ } -+ return 0; -+} -+ -+static void __set_cpu_idle(struct kvm_vcpu *vcpu) -+{ -+ atomic_set_mask(CPUSTAT_WAIT, &vcpu->arch.sie_block->cpuflags); -+ set_bit(vcpu->vcpu_id, vcpu->arch.local_int.float_int->idle_mask); -+} -+ -+static void __unset_cpu_idle(struct kvm_vcpu *vcpu) -+{ -+ atomic_clear_mask(CPUSTAT_WAIT, &vcpu->arch.sie_block->cpuflags); -+ clear_bit(vcpu->vcpu_id, vcpu->arch.local_int.float_int->idle_mask); -+} -+ -+static void __reset_intercept_indicators(struct kvm_vcpu *vcpu) -+{ -+ atomic_clear_mask(CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT, -+ &vcpu->arch.sie_block->cpuflags); -+ vcpu->arch.sie_block->lctl = 0x0000; -+ vcpu->arch.sie_block->ictl &= ~(ICTL_LPSW | ICTL_STCTL | ICTL_PINT); -+ -+ if (guestdbg_enabled(vcpu)) { -+ vcpu->arch.sie_block->lctl |= (LCTL_CR0 | LCTL_CR9 | -+ LCTL_CR10 | LCTL_CR11); -+ vcpu->arch.sie_block->ictl |= (ICTL_STCTL | ICTL_PINT); -+ } -+ -+ if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) -+ atomic_set_mask(CPUSTAT_STOP_INT, &vcpu->arch.sie_block->cpuflags); -+} -+ -+static void __set_cpuflag(struct kvm_vcpu *vcpu, u32 flag) -+{ -+ atomic_set_mask(flag, &vcpu->arch.sie_block->cpuflags); -+} -+ -+static void __set_intercept_indicator(struct kvm_vcpu *vcpu, -+ struct kvm_s390_interrupt_info *inti) -+{ -+ switch (inti->type) { -+ case KVM_S390_INT_EXTERNAL_CALL: -+ case KVM_S390_INT_EMERGENCY: -+ case KVM_S390_INT_SERVICE: -+ case KVM_S390_INT_PFAULT_INIT: -+ case KVM_S390_INT_PFAULT_DONE: -+ case KVM_S390_INT_VIRTIO: -+ case KVM_S390_INT_CLOCK_COMP: -+ case KVM_S390_INT_CPU_TIMER: -+ if (psw_extint_disabled(vcpu)) -+ __set_cpuflag(vcpu, CPUSTAT_EXT_INT); -+ else -+ vcpu->arch.sie_block->lctl |= LCTL_CR0; -+ break; -+ case KVM_S390_SIGP_STOP: -+ __set_cpuflag(vcpu, CPUSTAT_STOP_INT); -+ break; -+ case KVM_S390_MCHK: -+ if (psw_mchk_disabled(vcpu)) -+ vcpu->arch.sie_block->ictl |= ICTL_LPSW; -+ else -+ vcpu->arch.sie_block->lctl |= LCTL_CR14; -+ break; -+ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: -+ if (psw_ioint_disabled(vcpu)) -+ __set_cpuflag(vcpu, CPUSTAT_IO_INT); -+ else -+ vcpu->arch.sie_block->lctl |= LCTL_CR6; -+ break; -+ default: -+ BUG(); -+ } -+} -+ -+static u16 get_ilc(struct kvm_vcpu *vcpu) -+{ -+ const unsigned short table[] = { 2, 4, 4, 6 }; -+ -+ switch (vcpu->arch.sie_block->icptcode) { -+ case ICPT_INST: -+ case ICPT_INSTPROGI: -+ case ICPT_OPEREXC: -+ case ICPT_PARTEXEC: -+ case ICPT_IOINST: -+ /* last instruction only stored for these icptcodes */ -+ return table[vcpu->arch.sie_block->ipa >> 14]; -+ case ICPT_PROGI: -+ return vcpu->arch.sie_block->pgmilc; -+ default: -+ return 0; -+ } -+} -+ -+static int __must_check __deliver_prog_irq(struct kvm_vcpu *vcpu, -+ struct kvm_s390_pgm_info *pgm_info) -+{ -+ int rc = 0; -+ u16 ilc = get_ilc(vcpu); -+ -+ switch (pgm_info->code & ~PGM_PER) { -+ case PGM_AFX_TRANSLATION: -+ case PGM_ASX_TRANSLATION: -+ case PGM_EX_TRANSLATION: -+ case PGM_LFX_TRANSLATION: -+ case PGM_LSTE_SEQUENCE: -+ case PGM_LSX_TRANSLATION: -+ case PGM_LX_TRANSLATION: -+ case PGM_PRIMARY_AUTHORITY: -+ case PGM_SECONDARY_AUTHORITY: -+ case PGM_SPACE_SWITCH: -+ rc = put_guest_lc(vcpu, pgm_info->trans_exc_code, -+ (u64 *)__LC_TRANS_EXC_CODE); -+ break; -+ case PGM_ALEN_TRANSLATION: -+ case PGM_ALE_SEQUENCE: -+ case PGM_ASTE_INSTANCE: -+ case PGM_ASTE_SEQUENCE: -+ case PGM_ASTE_VALIDITY: -+ case PGM_EXTENDED_AUTHORITY: -+ rc = put_guest_lc(vcpu, pgm_info->exc_access_id, -+ (u8 *)__LC_EXC_ACCESS_ID); -+ break; -+ case PGM_ASCE_TYPE: -+ case PGM_PAGE_TRANSLATION: -+ case PGM_REGION_FIRST_TRANS: -+ case PGM_REGION_SECOND_TRANS: -+ case PGM_REGION_THIRD_TRANS: -+ case PGM_SEGMENT_TRANSLATION: -+ rc = put_guest_lc(vcpu, pgm_info->trans_exc_code, -+ (u64 *)__LC_TRANS_EXC_CODE); -+ rc |= put_guest_lc(vcpu, pgm_info->exc_access_id, -+ (u8 *)__LC_EXC_ACCESS_ID); -+ rc |= put_guest_lc(vcpu, pgm_info->op_access_id, -+ (u8 *)__LC_OP_ACCESS_ID); -+ break; -+ case PGM_MONITOR: -+ rc = put_guest_lc(vcpu, pgm_info->mon_class_nr, -+ (u16 *)__LC_MON_CLASS_NR); -+ rc |= put_guest_lc(vcpu, pgm_info->mon_code, -+ (u64 *)__LC_MON_CODE); -+ break; -+ case PGM_DATA: -+ rc = put_guest_lc(vcpu, pgm_info->data_exc_code, -+ (u32 *)__LC_DATA_EXC_CODE); -+ break; -+ case PGM_PROTECTION: -+ rc = put_guest_lc(vcpu, pgm_info->trans_exc_code, -+ (u64 *)__LC_TRANS_EXC_CODE); -+ rc |= put_guest_lc(vcpu, pgm_info->exc_access_id, -+ (u8 *)__LC_EXC_ACCESS_ID); -+ break; -+ } -+ -+ if (pgm_info->code & PGM_PER) { -+ rc |= put_guest_lc(vcpu, pgm_info->per_code, -+ (u8 *) __LC_PER_CODE); -+ rc |= put_guest_lc(vcpu, pgm_info->per_atmid, -+ (u8 *)__LC_PER_ATMID); -+ rc |= put_guest_lc(vcpu, pgm_info->per_address, -+ (u64 *) __LC_PER_ADDRESS); -+ rc |= put_guest_lc(vcpu, pgm_info->per_access_id, -+ (u8 *) __LC_PER_ACCESS_ID); -+ } -+ -+ rc |= put_guest_lc(vcpu, ilc, (u16 *) __LC_PGM_ILC); -+ rc |= put_guest_lc(vcpu, pgm_info->code, -+ (u16 *)__LC_PGM_INT_CODE); -+ rc |= write_guest_lc(vcpu, __LC_PGM_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_PGM_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ -+ return rc; -+} -+ -+static int __must_check __do_deliver_interrupt(struct kvm_vcpu *vcpu, -+ struct kvm_s390_interrupt_info *inti) -+{ -+ const unsigned short table[] = { 2, 4, 4, 6 }; -+ int rc = 0; -+ -+ switch (inti->type) { -+ case KVM_S390_INT_EMERGENCY: -+ VCPU_EVENT(vcpu, 4, "%s", "interrupt: sigp emerg"); -+ vcpu->stat.deliver_emergency_signal++; -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ inti->emerg.code, 0); -+ rc = put_guest_lc(vcpu, 0x1201, (u16 *)__LC_EXT_INT_CODE); -+ rc |= put_guest_lc(vcpu, inti->emerg.code, -+ (u16 *)__LC_EXT_CPU_ADDR); -+ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ break; -+ case KVM_S390_INT_EXTERNAL_CALL: -+ VCPU_EVENT(vcpu, 4, "%s", "interrupt: sigp ext call"); -+ vcpu->stat.deliver_external_call++; -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ inti->extcall.code, 0); -+ rc = put_guest_lc(vcpu, 0x1202, (u16 *)__LC_EXT_INT_CODE); -+ rc |= put_guest_lc(vcpu, inti->extcall.code, -+ (u16 *)__LC_EXT_CPU_ADDR); -+ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ break; -+ case KVM_S390_INT_CLOCK_COMP: -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ inti->ext.ext_params, 0); -+ rc = deliver_ckc_interrupt(vcpu); -+ break; -+ case KVM_S390_INT_CPU_TIMER: -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ inti->ext.ext_params, 0); -+ rc = put_guest_lc(vcpu, EXT_IRQ_CPU_TIMER, -+ (u16 *)__LC_EXT_INT_CODE); -+ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ rc |= put_guest_lc(vcpu, inti->ext.ext_params, -+ (u32 *)__LC_EXT_PARAMS); -+ break; -+ case KVM_S390_INT_SERVICE: -+ VCPU_EVENT(vcpu, 4, "interrupt: sclp parm:%x", -+ inti->ext.ext_params); -+ vcpu->stat.deliver_service_signal++; -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ inti->ext.ext_params, 0); -+ rc = put_guest_lc(vcpu, 0x2401, (u16 *)__LC_EXT_INT_CODE); -+ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ rc |= put_guest_lc(vcpu, inti->ext.ext_params, -+ (u32 *)__LC_EXT_PARAMS); -+ break; -+ case KVM_S390_INT_PFAULT_INIT: -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, 0, -+ inti->ext.ext_params2); -+ rc = put_guest_lc(vcpu, EXT_IRQ_CP_SERVICE, -+ (u16 *) __LC_EXT_INT_CODE); -+ rc |= put_guest_lc(vcpu, PFAULT_INIT, (u16 *) __LC_EXT_CPU_ADDR); -+ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ rc |= put_guest_lc(vcpu, inti->ext.ext_params2, -+ (u64 *) __LC_EXT_PARAMS2); -+ break; -+ case KVM_S390_INT_PFAULT_DONE: -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, 0, -+ inti->ext.ext_params2); -+ rc = put_guest_lc(vcpu, 0x2603, (u16 *)__LC_EXT_INT_CODE); -+ rc |= put_guest_lc(vcpu, 0x0680, (u16 *)__LC_EXT_CPU_ADDR); -+ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ rc |= put_guest_lc(vcpu, inti->ext.ext_params2, -+ (u64 *)__LC_EXT_PARAMS2); -+ break; -+ case KVM_S390_INT_VIRTIO: -+ VCPU_EVENT(vcpu, 4, "interrupt: virtio parm:%x,parm64:%llx", -+ inti->ext.ext_params, inti->ext.ext_params2); -+ vcpu->stat.deliver_virtio_interrupt++; -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ inti->ext.ext_params, -+ inti->ext.ext_params2); -+ rc = put_guest_lc(vcpu, 0x2603, (u16 *)__LC_EXT_INT_CODE); -+ rc |= put_guest_lc(vcpu, 0x0d00, (u16 *)__LC_EXT_CPU_ADDR); -+ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ rc |= put_guest_lc(vcpu, inti->ext.ext_params, -+ (u32 *)__LC_EXT_PARAMS); -+ rc |= put_guest_lc(vcpu, inti->ext.ext_params2, -+ (u64 *)__LC_EXT_PARAMS2); -+ break; -+ case KVM_S390_SIGP_STOP: -+ VCPU_EVENT(vcpu, 4, "%s", "interrupt: cpu stop"); -+ vcpu->stat.deliver_stop_signal++; -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ 0, 0); -+ __set_intercept_indicator(vcpu, inti); -+ break; -+ -+ case KVM_S390_SIGP_SET_PREFIX: -+ VCPU_EVENT(vcpu, 4, "interrupt: set prefix to %x", -+ inti->prefix.address); -+ vcpu->stat.deliver_prefix_signal++; -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ inti->prefix.address, 0); -+ kvm_s390_set_prefix(vcpu, inti->prefix.address); -+ break; -+ -+ case KVM_S390_RESTART: -+ VCPU_EVENT(vcpu, 4, "%s", "interrupt: cpu restart"); -+ vcpu->stat.deliver_restart_signal++; -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ 0, 0); -+ rc = write_guest_lc(vcpu, -+ offsetof(struct _lowcore, restart_old_psw), -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, offsetof(struct _lowcore, restart_psw), -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ break; -+ case KVM_S390_PROGRAM_INT: -+ VCPU_EVENT(vcpu, 4, "interrupt: pgm check code:%x, ilc:%x", -+ inti->pgm.code, -+ table[vcpu->arch.sie_block->ipa >> 14]); -+ vcpu->stat.deliver_program_int++; -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ inti->pgm.code, 0); -+ rc = __deliver_prog_irq(vcpu, &inti->pgm); -+ break; -+ -+ case KVM_S390_MCHK: -+ VCPU_EVENT(vcpu, 4, "interrupt: machine check mcic=%llx", -+ inti->mchk.mcic); -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ inti->mchk.cr14, -+ inti->mchk.mcic); -+ rc = kvm_s390_vcpu_store_status(vcpu, -+ KVM_S390_STORE_STATUS_PREFIXED); -+ rc |= put_guest_lc(vcpu, inti->mchk.mcic, (u64 *)__LC_MCCK_CODE); -+ rc |= write_guest_lc(vcpu, __LC_MCK_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_MCK_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ break; -+ -+ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: -+ { -+ __u32 param0 = ((__u32)inti->io.subchannel_id << 16) | -+ inti->io.subchannel_nr; -+ __u64 param1 = ((__u64)inti->io.io_int_parm << 32) | -+ inti->io.io_int_word; -+ VCPU_EVENT(vcpu, 4, "interrupt: I/O %llx", inti->type); -+ vcpu->stat.deliver_io_int++; -+ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, -+ param0, param1); -+ rc = put_guest_lc(vcpu, inti->io.subchannel_id, -+ (u16 *)__LC_SUBCHANNEL_ID); -+ rc |= put_guest_lc(vcpu, inti->io.subchannel_nr, -+ (u16 *)__LC_SUBCHANNEL_NR); -+ rc |= put_guest_lc(vcpu, inti->io.io_int_parm, -+ (u32 *)__LC_IO_INT_PARM); -+ rc |= put_guest_lc(vcpu, inti->io.io_int_word, -+ (u32 *)__LC_IO_INT_WORD); -+ rc |= write_guest_lc(vcpu, __LC_IO_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_IO_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ break; -+ } -+ default: -+ BUG(); -+ } -+ -+ return rc; -+} -+ -+static int __must_check deliver_ckc_interrupt(struct kvm_vcpu *vcpu) -+{ -+ int rc; -+ -+ rc = put_guest_lc(vcpu, 0x1004, (u16 __user *)__LC_EXT_INT_CODE); -+ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, -+ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); -+ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, -+ &vcpu->arch.sie_block->gpsw, -+ sizeof(psw_t)); -+ return rc; -+} -+ -+/* Check whether SIGP interpretation facility has an external call pending */ -+int kvm_s390_si_ext_call_pending(struct kvm_vcpu *vcpu) -+{ -+ atomic_t *sigp_ctrl = &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].ctrl; -+ -+ if (!psw_extint_disabled(vcpu) && -+ (vcpu->arch.sie_block->gcr[0] & 0x2000ul) && -+ (atomic_read(sigp_ctrl) & SIGP_CTRL_C) && -+ (atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_ECALL_PEND)) -+ return 1; -+ -+ return 0; -+} -+ -+int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; -+ struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int; -+ struct kvm_s390_interrupt_info *inti; -+ int rc = 0; -+ -+ if (atomic_read(&li->active)) { -+ spin_lock(&li->lock); -+ list_for_each_entry(inti, &li->list, list) -+ if (__interrupt_is_deliverable(vcpu, inti)) { -+ rc = 1; -+ break; -+ } -+ spin_unlock(&li->lock); -+ } -+ -+ if ((!rc) && atomic_read(&fi->active)) { -+ spin_lock(&fi->lock); -+ list_for_each_entry(inti, &fi->list, list) -+ if (__interrupt_is_deliverable(vcpu, inti)) { -+ rc = 1; -+ break; -+ } -+ spin_unlock(&fi->lock); -+ } -+ -+ if (!rc && kvm_cpu_has_pending_timer(vcpu)) -+ rc = 1; -+ -+ if (!rc && kvm_s390_si_ext_call_pending(vcpu)) -+ rc = 1; -+ -+ return rc; -+} -+ -+int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) -+{ -+ if (!(vcpu->arch.sie_block->ckc < -+ get_tod_clock_fast() + vcpu->arch.sie_block->epoch)) -+ return 0; -+ if (!ckc_interrupts_enabled(vcpu)) -+ return 0; -+ return 1; -+} -+ -+int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) -+{ -+ u64 now, sltime; -+ -+ vcpu->stat.exit_wait_state++; -+ -+ /* fast path */ -+ if (kvm_cpu_has_pending_timer(vcpu) || kvm_arch_vcpu_runnable(vcpu)) -+ return 0; -+ -+ if (psw_interrupts_disabled(vcpu)) { -+ VCPU_EVENT(vcpu, 3, "%s", "disabled wait"); -+ return -EOPNOTSUPP; /* disabled wait */ -+ } -+ -+ __set_cpu_idle(vcpu); -+ if (!ckc_interrupts_enabled(vcpu)) { -+ VCPU_EVENT(vcpu, 3, "%s", "enabled wait w/o timer"); -+ goto no_timer; -+ } -+ -+ now = get_tod_clock_fast() + vcpu->arch.sie_block->epoch; -+ sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now); -+ hrtimer_start(&vcpu->arch.ckc_timer, ktime_set (0, sltime) , HRTIMER_MODE_REL); -+ VCPU_EVENT(vcpu, 5, "enabled wait via clock comparator: %llx ns", sltime); -+no_timer: -+ srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx); -+ kvm_vcpu_block(vcpu); -+ __unset_cpu_idle(vcpu); -+ vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); -+ -+ hrtimer_cancel(&vcpu->arch.ckc_timer); -+ return 0; -+} -+ -+void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu) -+{ -+ if (waitqueue_active(&vcpu->wq)) { -+ /* -+ * The vcpu gave up the cpu voluntarily, mark it as a good -+ * yield-candidate. -+ */ -+ vcpu->preempted = true; -+ wake_up_interruptible(&vcpu->wq); -+ vcpu->stat.halt_wakeup++; -+ } -+} -+ -+enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer) -+{ -+ struct kvm_vcpu *vcpu; -+ u64 now, sltime; -+ -+ vcpu = container_of(timer, struct kvm_vcpu, arch.ckc_timer); -+ now = get_tod_clock_fast() + vcpu->arch.sie_block->epoch; -+ sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now); -+ -+ /* -+ * If the monotonic clock runs faster than the tod clock we might be -+ * woken up too early and have to go back to sleep to avoid deadlocks. -+ */ -+ if (vcpu->arch.sie_block->ckc > now && -+ hrtimer_forward_now(timer, ns_to_ktime(sltime))) -+ return HRTIMER_RESTART; -+ kvm_s390_vcpu_wakeup(vcpu); -+ return HRTIMER_NORESTART; -+} -+ -+void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; -+ struct kvm_s390_interrupt_info *n, *inti = NULL; -+ -+ spin_lock(&li->lock); -+ list_for_each_entry_safe(inti, n, &li->list, list) { -+ list_del(&inti->list); -+ kfree(inti); -+ } -+ atomic_set(&li->active, 0); -+ spin_unlock(&li->lock); -+ -+ /* clear pending external calls set by sigp interpretation facility */ -+ atomic_clear_mask(CPUSTAT_ECALL_PEND, &vcpu->arch.sie_block->cpuflags); -+ atomic_clear_mask(SIGP_CTRL_C, -+ &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].ctrl); -+} -+ -+int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; -+ struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int; -+ struct kvm_s390_interrupt_info *n, *inti = NULL; -+ int deliver; -+ int rc = 0; -+ -+ __reset_intercept_indicators(vcpu); -+ if (atomic_read(&li->active)) { -+ do { -+ deliver = 0; -+ spin_lock(&li->lock); -+ list_for_each_entry_safe(inti, n, &li->list, list) { -+ if (__interrupt_is_deliverable(vcpu, inti)) { -+ list_del(&inti->list); -+ deliver = 1; -+ break; -+ } -+ __set_intercept_indicator(vcpu, inti); -+ } -+ if (list_empty(&li->list)) -+ atomic_set(&li->active, 0); -+ spin_unlock(&li->lock); -+ if (deliver) { -+ rc = __do_deliver_interrupt(vcpu, inti); -+ kfree(inti); -+ } -+ } while (!rc && deliver); -+ } -+ -+ if (!rc && kvm_cpu_has_pending_timer(vcpu)) -+ rc = deliver_ckc_interrupt(vcpu); -+ -+ if (!rc && atomic_read(&fi->active)) { -+ do { -+ deliver = 0; -+ spin_lock(&fi->lock); -+ list_for_each_entry_safe(inti, n, &fi->list, list) { -+ if (__interrupt_is_deliverable(vcpu, inti)) { -+ list_del(&inti->list); -+ fi->irq_count--; -+ deliver = 1; -+ break; -+ } -+ __set_intercept_indicator(vcpu, inti); -+ } -+ if (list_empty(&fi->list)) -+ atomic_set(&fi->active, 0); -+ spin_unlock(&fi->lock); -+ if (deliver) { -+ rc = __do_deliver_interrupt(vcpu, inti); -+ kfree(inti); -+ } -+ } while (!rc && deliver); -+ } -+ -+ return rc; -+} -+ -+int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code) -+{ -+ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; -+ struct kvm_s390_interrupt_info *inti; -+ -+ inti = kzalloc(sizeof(*inti), GFP_KERNEL); -+ if (!inti) -+ return -ENOMEM; -+ -+ inti->type = KVM_S390_PROGRAM_INT; -+ inti->pgm.code = code; -+ -+ VCPU_EVENT(vcpu, 3, "inject: program check %d (from kernel)", code); -+ trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, inti->type, code, 0, 1); -+ spin_lock(&li->lock); -+ list_add(&inti->list, &li->list); -+ atomic_set(&li->active, 1); -+ BUG_ON(waitqueue_active(li->wq)); -+ spin_unlock(&li->lock); -+ return 0; -+} -+ -+int kvm_s390_inject_prog_irq(struct kvm_vcpu *vcpu, -+ struct kvm_s390_pgm_info *pgm_info) -+{ -+ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; -+ struct kvm_s390_interrupt_info *inti; -+ -+ inti = kzalloc(sizeof(*inti), GFP_KERNEL); -+ if (!inti) -+ return -ENOMEM; -+ -+ VCPU_EVENT(vcpu, 3, "inject: prog irq %d (from kernel)", -+ pgm_info->code); -+ trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_PROGRAM_INT, -+ pgm_info->code, 0, 1); -+ -+ inti->type = KVM_S390_PROGRAM_INT; -+ memcpy(&inti->pgm, pgm_info, sizeof(inti->pgm)); -+ spin_lock(&li->lock); -+ list_add(&inti->list, &li->list); -+ atomic_set(&li->active, 1); -+ BUG_ON(waitqueue_active(li->wq)); -+ spin_unlock(&li->lock); -+ return 0; -+} -+ -+struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, -+ u64 cr6, u64 schid) -+{ -+ struct kvm_s390_float_interrupt *fi; -+ struct kvm_s390_interrupt_info *inti, *iter; -+ -+ if ((!schid && !cr6) || (schid && cr6)) -+ return NULL; -+ fi = &kvm->arch.float_int; -+ spin_lock(&fi->lock); -+ inti = NULL; -+ list_for_each_entry(iter, &fi->list, list) { -+ if (!is_ioint(iter->type)) -+ continue; -+ if (cr6 && -+ ((cr6 & int_word_to_isc_bits(iter->io.io_int_word)) == 0)) -+ continue; -+ if (schid) { -+ if (((schid & 0x00000000ffff0000) >> 16) != -+ iter->io.subchannel_id) -+ continue; -+ if ((schid & 0x000000000000ffff) != -+ iter->io.subchannel_nr) -+ continue; -+ } -+ inti = iter; -+ break; -+ } -+ if (inti) { -+ list_del_init(&inti->list); -+ fi->irq_count--; -+ } -+ if (list_empty(&fi->list)) -+ atomic_set(&fi->active, 0); -+ spin_unlock(&fi->lock); -+ return inti; -+} -+ -+static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) -+{ -+ struct kvm_s390_local_interrupt *li; -+ struct kvm_s390_float_interrupt *fi; -+ struct kvm_s390_interrupt_info *iter; -+ struct kvm_vcpu *dst_vcpu = NULL; -+ int sigcpu; -+ int rc = 0; -+ -+ fi = &kvm->arch.float_int; -+ spin_lock(&fi->lock); -+ if (fi->irq_count >= KVM_S390_MAX_FLOAT_IRQS) { -+ rc = -EINVAL; -+ goto unlock_fi; -+ } -+ fi->irq_count++; -+ if (!is_ioint(inti->type)) { -+ list_add_tail(&inti->list, &fi->list); -+ } else { -+ u64 isc_bits = int_word_to_isc_bits(inti->io.io_int_word); -+ -+ /* Keep I/O interrupts sorted in isc order. */ -+ list_for_each_entry(iter, &fi->list, list) { -+ if (!is_ioint(iter->type)) -+ continue; -+ if (int_word_to_isc_bits(iter->io.io_int_word) -+ <= isc_bits) -+ continue; -+ break; -+ } -+ list_add_tail(&inti->list, &iter->list); -+ } -+ atomic_set(&fi->active, 1); -+ if (atomic_read(&kvm->online_vcpus) == 0) -+ goto unlock_fi; -+ sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS); -+ if (sigcpu == KVM_MAX_VCPUS) { -+ do { -+ sigcpu = fi->next_rr_cpu++; -+ if (sigcpu == KVM_MAX_VCPUS) -+ sigcpu = fi->next_rr_cpu = 0; -+ } while (kvm_get_vcpu(kvm, sigcpu) == NULL); -+ } -+ dst_vcpu = kvm_get_vcpu(kvm, sigcpu); -+ li = &dst_vcpu->arch.local_int; -+ spin_lock(&li->lock); -+ atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); -+ spin_unlock(&li->lock); -+ kvm_s390_vcpu_wakeup(kvm_get_vcpu(kvm, sigcpu)); -+unlock_fi: -+ spin_unlock(&fi->lock); -+ return rc; -+} -+ -+int kvm_s390_inject_vm(struct kvm *kvm, -+ struct kvm_s390_interrupt *s390int) -+{ -+ struct kvm_s390_interrupt_info *inti; -+ int rc; -+ -+ inti = kzalloc(sizeof(*inti), GFP_KERNEL); -+ if (!inti) -+ return -ENOMEM; -+ -+ inti->type = s390int->type; -+ switch (inti->type) { -+ case KVM_S390_INT_VIRTIO: -+ VM_EVENT(kvm, 5, "inject: virtio parm:%x,parm64:%llx", -+ s390int->parm, s390int->parm64); -+ inti->ext.ext_params = s390int->parm; -+ inti->ext.ext_params2 = s390int->parm64; -+ break; -+ case KVM_S390_INT_SERVICE: -+ VM_EVENT(kvm, 5, "inject: sclp parm:%x", s390int->parm); -+ inti->ext.ext_params = s390int->parm; -+ break; -+ case KVM_S390_INT_PFAULT_DONE: -+ inti->type = s390int->type; -+ inti->ext.ext_params2 = s390int->parm64; -+ break; -+ case KVM_S390_MCHK: -+ VM_EVENT(kvm, 5, "inject: machine check parm64:%llx", -+ s390int->parm64); -+ inti->mchk.cr14 = s390int->parm; /* upper bits are not used */ -+ inti->mchk.mcic = s390int->parm64; -+ break; -+ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: -+ if (inti->type & IOINT_AI_MASK) -+ VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)"); -+ else -+ VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x", -+ s390int->type & IOINT_CSSID_MASK, -+ s390int->type & IOINT_SSID_MASK, -+ s390int->type & IOINT_SCHID_MASK); -+ inti->io.subchannel_id = s390int->parm >> 16; -+ inti->io.subchannel_nr = s390int->parm & 0x0000ffffu; -+ inti->io.io_int_parm = s390int->parm64 >> 32; -+ inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull; -+ break; -+ default: -+ kfree(inti); -+ return -EINVAL; -+ } -+ trace_kvm_s390_inject_vm(s390int->type, s390int->parm, s390int->parm64, -+ 2); -+ -+ rc = __inject_vm(kvm, inti); -+ if (rc) -+ kfree(inti); -+ return rc; -+} -+ -+int kvm_s390_reinject_io_int(struct kvm *kvm, -+ struct kvm_s390_interrupt_info *inti) -+{ -+ return __inject_vm(kvm, inti); -+} -+ -+int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, -+ struct kvm_s390_interrupt *s390int) -+{ -+ struct kvm_s390_local_interrupt *li; -+ struct kvm_s390_interrupt_info *inti; -+ -+ inti = kzalloc(sizeof(*inti), GFP_KERNEL); -+ if (!inti) -+ return -ENOMEM; -+ -+ switch (s390int->type) { -+ case KVM_S390_PROGRAM_INT: -+ if (s390int->parm & 0xffff0000) { -+ kfree(inti); -+ return -EINVAL; -+ } -+ inti->type = s390int->type; -+ inti->pgm.code = s390int->parm; -+ VCPU_EVENT(vcpu, 3, "inject: program check %d (from user)", -+ s390int->parm); -+ break; -+ case KVM_S390_SIGP_SET_PREFIX: -+ inti->prefix.address = s390int->parm; -+ inti->type = s390int->type; -+ VCPU_EVENT(vcpu, 3, "inject: set prefix to %x (from user)", -+ s390int->parm); -+ break; -+ case KVM_S390_SIGP_STOP: -+ case KVM_S390_RESTART: -+ case KVM_S390_INT_CLOCK_COMP: -+ case KVM_S390_INT_CPU_TIMER: -+ VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type); -+ inti->type = s390int->type; -+ break; -+ case KVM_S390_INT_EXTERNAL_CALL: -+ if (s390int->parm & 0xffff0000) { -+ kfree(inti); -+ return -EINVAL; -+ } -+ VCPU_EVENT(vcpu, 3, "inject: external call source-cpu:%u", -+ s390int->parm); -+ inti->type = s390int->type; -+ inti->extcall.code = s390int->parm; -+ break; -+ case KVM_S390_INT_EMERGENCY: -+ if (s390int->parm & 0xffff0000) { -+ kfree(inti); -+ return -EINVAL; -+ } -+ VCPU_EVENT(vcpu, 3, "inject: emergency %u\n", s390int->parm); -+ inti->type = s390int->type; -+ inti->emerg.code = s390int->parm; -+ break; -+ case KVM_S390_MCHK: -+ VCPU_EVENT(vcpu, 5, "inject: machine check parm64:%llx", -+ s390int->parm64); -+ inti->type = s390int->type; -+ inti->mchk.mcic = s390int->parm64; -+ break; -+ case KVM_S390_INT_PFAULT_INIT: -+ inti->type = s390int->type; -+ inti->ext.ext_params2 = s390int->parm64; -+ break; -+ case KVM_S390_INT_VIRTIO: -+ case KVM_S390_INT_SERVICE: -+ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: -+ default: -+ kfree(inti); -+ return -EINVAL; -+ } -+ trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, s390int->type, s390int->parm, -+ s390int->parm64, 2); -+ -+ li = &vcpu->arch.local_int; -+ spin_lock(&li->lock); -+ if (inti->type == KVM_S390_PROGRAM_INT) -+ list_add(&inti->list, &li->list); -+ else -+ list_add_tail(&inti->list, &li->list); -+ atomic_set(&li->active, 1); -+ if (inti->type == KVM_S390_SIGP_STOP) -+ li->action_bits |= ACTION_STOP_ON_STOP; -+ atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); -+ spin_unlock(&li->lock); -+ kvm_s390_vcpu_wakeup(vcpu); -+ return 0; -+} -+ -+void kvm_s390_clear_float_irqs(struct kvm *kvm) -+{ -+ struct kvm_s390_float_interrupt *fi; -+ struct kvm_s390_interrupt_info *n, *inti = NULL; -+ -+ fi = &kvm->arch.float_int; -+ spin_lock(&fi->lock); -+ list_for_each_entry_safe(inti, n, &fi->list, list) { -+ list_del(&inti->list); -+ kfree(inti); -+ } -+ fi->irq_count = 0; -+ atomic_set(&fi->active, 0); -+ spin_unlock(&fi->lock); -+} -+ -+static void inti_to_irq(struct kvm_s390_interrupt_info *inti, -+ struct kvm_s390_irq *irq) -+{ -+ irq->type = inti->type; -+ switch (inti->type) { -+ case KVM_S390_INT_PFAULT_INIT: -+ case KVM_S390_INT_PFAULT_DONE: -+ case KVM_S390_INT_VIRTIO: -+ case KVM_S390_INT_SERVICE: -+ irq->u.ext = inti->ext; -+ break; -+ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: -+ irq->u.io = inti->io; -+ break; -+ case KVM_S390_MCHK: -+ irq->u.mchk = inti->mchk; -+ break; -+ } -+} -+ -+static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len) -+{ -+ struct kvm_s390_interrupt_info *inti; -+ struct kvm_s390_float_interrupt *fi; -+ struct kvm_s390_irq *buf; -+ int max_irqs; -+ int ret = 0; -+ int n = 0; -+ -+ if (len > KVM_S390_FLIC_MAX_BUFFER || len == 0) -+ return -EINVAL; -+ -+ /* -+ * We are already using -ENOMEM to signal -+ * userspace it may retry with a bigger buffer, -+ * so we need to use something else for this case -+ */ -+ buf = vzalloc(len); -+ if (!buf) -+ return -ENOBUFS; -+ -+ max_irqs = len / sizeof(struct kvm_s390_irq); -+ -+ fi = &kvm->arch.float_int; -+ spin_lock(&fi->lock); -+ list_for_each_entry(inti, &fi->list, list) { -+ if (n == max_irqs) { -+ /* signal userspace to try again */ -+ ret = -ENOMEM; -+ break; -+ } -+ inti_to_irq(inti, &buf[n]); -+ n++; -+ } -+ spin_unlock(&fi->lock); -+ if (!ret && n > 0) { -+ if (copy_to_user(usrbuf, buf, sizeof(struct kvm_s390_irq) * n)) -+ ret = -EFAULT; -+ } -+ vfree(buf); -+ -+ return ret < 0 ? ret : n; -+} -+ -+static int flic_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr) -+{ -+ int r; -+ -+ switch (attr->group) { -+ case KVM_DEV_FLIC_GET_ALL_IRQS: -+ r = get_all_floating_irqs(dev->kvm, (u8 __user *) attr->addr, -+ attr->attr); -+ break; -+ default: -+ r = -EINVAL; -+ } -+ -+ return r; -+} -+ -+static inline int copy_irq_from_user(struct kvm_s390_interrupt_info *inti, -+ u64 addr) -+{ -+ struct kvm_s390_irq __user *uptr = (struct kvm_s390_irq __user *) addr; -+ void *target = NULL; -+ void __user *source; -+ u64 size; -+ -+ if (get_user(inti->type, (u64 __user *)addr)) -+ return -EFAULT; -+ -+ switch (inti->type) { -+ case KVM_S390_INT_PFAULT_INIT: -+ case KVM_S390_INT_PFAULT_DONE: -+ case KVM_S390_INT_VIRTIO: -+ case KVM_S390_INT_SERVICE: -+ target = (void *) &inti->ext; -+ source = &uptr->u.ext; -+ size = sizeof(inti->ext); -+ break; -+ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: -+ target = (void *) &inti->io; -+ source = &uptr->u.io; -+ size = sizeof(inti->io); -+ break; -+ case KVM_S390_MCHK: -+ target = (void *) &inti->mchk; -+ source = &uptr->u.mchk; -+ size = sizeof(inti->mchk); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ if (copy_from_user(target, source, size)) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static int enqueue_floating_irq(struct kvm_device *dev, -+ struct kvm_device_attr *attr) -+{ -+ struct kvm_s390_interrupt_info *inti = NULL; -+ int r = 0; -+ int len = attr->attr; -+ -+ if (len % sizeof(struct kvm_s390_irq) != 0) -+ return -EINVAL; -+ else if (len > KVM_S390_FLIC_MAX_BUFFER) -+ return -EINVAL; -+ -+ while (len >= sizeof(struct kvm_s390_irq)) { -+ inti = kzalloc(sizeof(*inti), GFP_KERNEL); -+ if (!inti) -+ return -ENOMEM; -+ -+ r = copy_irq_from_user(inti, attr->addr); -+ if (r) { -+ kfree(inti); -+ return r; -+ } -+ r = __inject_vm(dev->kvm, inti); -+ if (r) { -+ kfree(inti); -+ return r; -+ } -+ len -= sizeof(struct kvm_s390_irq); -+ attr->addr += sizeof(struct kvm_s390_irq); -+ } -+ -+ return r; -+} -+ -+static struct s390_io_adapter *get_io_adapter(struct kvm *kvm, unsigned int id) -+{ -+ if (id >= MAX_S390_IO_ADAPTERS) -+ return NULL; -+ return kvm->arch.adapters[id]; -+} -+ -+static int register_io_adapter(struct kvm_device *dev, -+ struct kvm_device_attr *attr) -+{ -+ struct s390_io_adapter *adapter; -+ struct kvm_s390_io_adapter adapter_info; -+ -+ if (copy_from_user(&adapter_info, -+ (void __user *)attr->addr, sizeof(adapter_info))) -+ return -EFAULT; -+ -+ if ((adapter_info.id >= MAX_S390_IO_ADAPTERS) || -+ (dev->kvm->arch.adapters[adapter_info.id] != NULL)) -+ return -EINVAL; -+ -+ adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); -+ if (!adapter) -+ return -ENOMEM; -+ -+ INIT_LIST_HEAD(&adapter->maps); -+ init_rwsem(&adapter->maps_lock); -+ atomic_set(&adapter->nr_maps, 0); -+ adapter->id = adapter_info.id; -+ adapter->isc = adapter_info.isc; -+ adapter->maskable = adapter_info.maskable; -+ adapter->masked = false; -+ adapter->swap = adapter_info.swap; -+ dev->kvm->arch.adapters[adapter->id] = adapter; -+ -+ return 0; -+} -+ -+int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked) -+{ -+ int ret; -+ struct s390_io_adapter *adapter = get_io_adapter(kvm, id); -+ -+ if (!adapter || !adapter->maskable) -+ return -EINVAL; -+ ret = adapter->masked; -+ adapter->masked = masked; -+ return ret; -+} -+ -+static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr) -+{ -+ struct s390_io_adapter *adapter = get_io_adapter(kvm, id); -+ struct s390_map_info *map; -+ int ret; -+ -+ if (!adapter || !addr) -+ return -EINVAL; -+ -+ map = kzalloc(sizeof(*map), GFP_KERNEL); -+ if (!map) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ INIT_LIST_HEAD(&map->list); -+ map->guest_addr = addr; -+ map->addr = gmap_translate(kvm->arch.gmap, addr); -+ if (map->addr == -EFAULT) { -+ ret = -EFAULT; -+ goto out; -+ } -+ ret = get_user_pages_fast(map->addr, 1, 1, &map->page); -+ if (ret < 0) -+ goto out; -+ BUG_ON(ret != 1); -+ down_write(&adapter->maps_lock); -+ if (atomic_inc_return(&adapter->nr_maps) < MAX_S390_ADAPTER_MAPS) { -+ list_add_tail(&map->list, &adapter->maps); -+ ret = 0; -+ } else { -+ put_page(map->page); -+ ret = -EINVAL; -+ } -+ up_write(&adapter->maps_lock); -+out: -+ if (ret) -+ kfree(map); -+ return ret; -+} -+ -+static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr) -+{ -+ struct s390_io_adapter *adapter = get_io_adapter(kvm, id); -+ struct s390_map_info *map, *tmp; -+ int found = 0; -+ -+ if (!adapter || !addr) -+ return -EINVAL; -+ -+ down_write(&adapter->maps_lock); -+ list_for_each_entry_safe(map, tmp, &adapter->maps, list) { -+ if (map->guest_addr == addr) { -+ found = 1; -+ atomic_dec(&adapter->nr_maps); -+ list_del(&map->list); -+ put_page(map->page); -+ kfree(map); -+ break; -+ } -+ } -+ up_write(&adapter->maps_lock); -+ -+ return found ? 0 : -EINVAL; -+} -+ -+void kvm_s390_destroy_adapters(struct kvm *kvm) -+{ -+ int i; -+ struct s390_map_info *map, *tmp; -+ -+ for (i = 0; i < MAX_S390_IO_ADAPTERS; i++) { -+ if (!kvm->arch.adapters[i]) -+ continue; -+ list_for_each_entry_safe(map, tmp, -+ &kvm->arch.adapters[i]->maps, list) { -+ list_del(&map->list); -+ put_page(map->page); -+ kfree(map); -+ } -+ kfree(kvm->arch.adapters[i]); -+ } -+} -+ -+static int modify_io_adapter(struct kvm_device *dev, -+ struct kvm_device_attr *attr) -+{ -+ struct kvm_s390_io_adapter_req req; -+ struct s390_io_adapter *adapter; -+ int ret; -+ -+ if (copy_from_user(&req, (void __user *)attr->addr, sizeof(req))) -+ return -EFAULT; -+ -+ adapter = get_io_adapter(dev->kvm, req.id); -+ if (!adapter) -+ return -EINVAL; -+ switch (req.type) { -+ case KVM_S390_IO_ADAPTER_MASK: -+ ret = kvm_s390_mask_adapter(dev->kvm, req.id, req.mask); -+ if (ret > 0) -+ ret = 0; -+ break; -+ case KVM_S390_IO_ADAPTER_MAP: -+ ret = kvm_s390_adapter_map(dev->kvm, req.id, req.addr); -+ break; -+ case KVM_S390_IO_ADAPTER_UNMAP: -+ ret = kvm_s390_adapter_unmap(dev->kvm, req.id, req.addr); -+ break; -+ default: -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) -+{ -+ int r = 0; -+ unsigned int i; -+ struct kvm_vcpu *vcpu; -+ -+ switch (attr->group) { -+ case KVM_DEV_FLIC_ENQUEUE: -+ r = enqueue_floating_irq(dev, attr); -+ break; -+ case KVM_DEV_FLIC_CLEAR_IRQS: -+ kvm_s390_clear_float_irqs(dev->kvm); -+ break; -+ case KVM_DEV_FLIC_APF_ENABLE: -+ dev->kvm->arch.gmap->pfault_enabled = 1; -+ break; -+ case KVM_DEV_FLIC_APF_DISABLE_WAIT: -+ dev->kvm->arch.gmap->pfault_enabled = 0; -+ /* -+ * Make sure no async faults are in transition when -+ * clearing the queues. So we don't need to worry -+ * about late coming workers. -+ */ -+ synchronize_srcu(&dev->kvm->srcu); -+ kvm_for_each_vcpu(i, vcpu, dev->kvm) -+ kvm_clear_async_pf_completion_queue(vcpu); -+ break; -+ case KVM_DEV_FLIC_ADAPTER_REGISTER: -+ r = register_io_adapter(dev, attr); -+ break; -+ case KVM_DEV_FLIC_ADAPTER_MODIFY: -+ r = modify_io_adapter(dev, attr); -+ break; -+ default: -+ r = -EINVAL; -+ } -+ -+ return r; -+} -+ -+static int flic_create(struct kvm_device *dev, u32 type) -+{ -+ if (!dev) -+ return -EINVAL; -+ if (dev->kvm->arch.flic) -+ return -EINVAL; -+ dev->kvm->arch.flic = dev; -+ return 0; -+} -+ -+static void flic_destroy(struct kvm_device *dev) -+{ -+ dev->kvm->arch.flic = NULL; -+ kfree(dev); -+} -+ -+/* s390 floating irq controller (flic) */ -+struct kvm_device_ops kvm_flic_ops = { -+ .name = "kvm-flic", -+ .get_attr = flic_get_attr, -+ .set_attr = flic_set_attr, -+ .create = flic_create, -+ .destroy = flic_destroy, -+}; -+ -+static unsigned long get_ind_bit(__u64 addr, unsigned long bit_nr, bool swap) -+{ -+ unsigned long bit; -+ -+ bit = bit_nr + (addr % PAGE_SIZE) * 8; -+ -+ return swap ? (bit ^ (BITS_PER_LONG - 1)) : bit; -+} -+ -+static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter, -+ u64 addr) -+{ -+ struct s390_map_info *map; -+ -+ if (!adapter) -+ return NULL; -+ -+ list_for_each_entry(map, &adapter->maps, list) { -+ if (map->guest_addr == addr) -+ return map; -+ } -+ return NULL; -+} -+ -+static int adapter_indicators_set(struct kvm *kvm, -+ struct s390_io_adapter *adapter, -+ struct kvm_s390_adapter_int *adapter_int) -+{ -+ unsigned long bit; -+ int summary_set, idx; -+ struct s390_map_info *info; -+ void *map; -+ -+ info = get_map_info(adapter, adapter_int->ind_addr); -+ if (!info) -+ return -1; -+ map = page_address(info->page); -+ bit = get_ind_bit(info->addr, adapter_int->ind_offset, adapter->swap); -+ set_bit(bit, map); -+ idx = srcu_read_lock(&kvm->srcu); -+ mark_page_dirty(kvm, info->guest_addr >> PAGE_SHIFT); -+ set_page_dirty_lock(info->page); -+ info = get_map_info(adapter, adapter_int->summary_addr); -+ if (!info) { -+ srcu_read_unlock(&kvm->srcu, idx); -+ return -1; -+ } -+ map = page_address(info->page); -+ bit = get_ind_bit(info->addr, adapter_int->summary_offset, -+ adapter->swap); -+ summary_set = test_and_set_bit(bit, map); -+ mark_page_dirty(kvm, info->guest_addr >> PAGE_SHIFT); -+ set_page_dirty_lock(info->page); -+ srcu_read_unlock(&kvm->srcu, idx); -+ return summary_set ? 0 : 1; -+} -+ -+/* -+ * < 0 - not injected due to error -+ * = 0 - coalesced, summary indicator already active -+ * > 0 - injected interrupt -+ */ -+static int set_adapter_int(struct kvm_kernel_irq_routing_entry *e, -+ struct kvm *kvm, int irq_source_id, int level, -+ bool line_status) -+{ -+ int ret; -+ struct s390_io_adapter *adapter; -+ -+ /* We're only interested in the 0->1 transition. */ -+ if (!level) -+ return 0; -+ adapter = get_io_adapter(kvm, e->adapter.adapter_id); -+ if (!adapter) -+ return -1; -+ down_read(&adapter->maps_lock); -+ ret = adapter_indicators_set(kvm, adapter, &e->adapter); -+ up_read(&adapter->maps_lock); -+ if ((ret > 0) && !adapter->masked) { -+ struct kvm_s390_interrupt s390int = { -+ .type = KVM_S390_INT_IO(1, 0, 0, 0), -+ .parm = 0, -+ .parm64 = (adapter->isc << 27) | 0x80000000, -+ }; -+ ret = kvm_s390_inject_vm(kvm, &s390int); -+ if (ret == 0) -+ ret = 1; -+ } -+ return ret; -+} -+ -+int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e, -+ const struct kvm_irq_routing_entry *ue) -+{ -+ int ret; -+ -+ switch (ue->type) { -+ case KVM_IRQ_ROUTING_S390_ADAPTER: -+ e->set = set_adapter_int; -+ e->adapter.summary_addr = ue->u.adapter.summary_addr; -+ e->adapter.ind_addr = ue->u.adapter.ind_addr; -+ e->adapter.summary_offset = ue->u.adapter.summary_offset; -+ e->adapter.ind_offset = ue->u.adapter.ind_offset; -+ e->adapter.adapter_id = ue->u.adapter.adapter_id; -+ ret = 0; -+ break; -+ default: -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, -+ int irq_source_id, int level, bool line_status) -+{ -+ return -EINVAL; -+} -diff -Nur linux-3.18.14.orig/arch/s390/mm/fault.c linux-3.18.14-rt/arch/s390/mm/fault.c ---- linux-3.18.14.orig/arch/s390/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/s390/mm/fault.c 2015-05-31 15:32:46.401635385 -0500 -@@ -435,7 +435,8 @@ - * user context. - */ - fault = VM_FAULT_BADCONTEXT; -- if (unlikely(!user_space_fault(regs) || in_atomic() || !mm)) -+ if (unlikely(!user_space_fault(regs) || !mm || -+ tsk->pagefault_disabled)) - goto out; - - address = trans_exc_code & __FAIL_ADDR_MASK; -diff -Nur linux-3.18.14.orig/arch/score/mm/fault.c linux-3.18.14-rt/arch/score/mm/fault.c ---- linux-3.18.14.orig/arch/score/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/score/mm/fault.c 2015-05-31 15:32:46.413635385 -0500 -@@ -73,7 +73,7 @@ - * If we're in an interrupt or have no user - * context, we must not take the fault.. - */ -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto bad_area_nosemaphore; - - if (user_mode(regs)) -diff -Nur linux-3.18.14.orig/arch/sh/kernel/irq.c linux-3.18.14-rt/arch/sh/kernel/irq.c ---- linux-3.18.14.orig/arch/sh/kernel/irq.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/sh/kernel/irq.c 2015-05-31 15:32:46.429635385 -0500 -@@ -149,6 +149,7 @@ - hardirq_ctx[cpu] = NULL; - } - -+#ifndef CONFIG_PREEMPT_RT_FULL - void do_softirq_own_stack(void) - { - struct thread_info *curctx; -@@ -176,6 +177,7 @@ - "r5", "r6", "r7", "r8", "r9", "r15", "t", "pr" - ); - } -+#endif - #else - static inline void handle_one_irq(unsigned int irq) - { -diff -Nur linux-3.18.14.orig/arch/sh/mm/fault.c linux-3.18.14-rt/arch/sh/mm/fault.c ---- linux-3.18.14.orig/arch/sh/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/sh/mm/fault.c 2015-05-31 15:32:46.469635385 -0500 -@@ -440,7 +440,7 @@ - * If we're in an interrupt, have no user context or are running - * in an atomic region then we must not take the fault: - */ -- if (unlikely(in_atomic() || !mm)) { -+ if (unlikely(!mm || pagefault_disabled())) { - bad_area_nosemaphore(regs, error_code, address); - return; - } -diff -Nur linux-3.18.14.orig/arch/sparc/Kconfig linux-3.18.14-rt/arch/sparc/Kconfig ---- linux-3.18.14.orig/arch/sparc/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/sparc/Kconfig 2015-05-31 15:32:46.469635385 -0500 -@@ -182,12 +182,10 @@ - source kernel/Kconfig.hz - - config RWSEM_GENERIC_SPINLOCK -- bool -- default y if SPARC32 -+ def_bool PREEMPT_RT_FULL - - config RWSEM_XCHGADD_ALGORITHM -- bool -- default y if SPARC64 -+ def_bool !RWSEM_GENERIC_SPINLOCK && !PREEMPT_RT_FULL - - config GENERIC_HWEIGHT - bool -@@ -528,6 +526,10 @@ - - source "fs/Kconfig.binfmt" - -+config EARLY_PRINTK -+ bool -+ default y -+ - config COMPAT - bool - depends on SPARC64 -diff -Nur linux-3.18.14.orig/arch/sparc/kernel/irq_64.c linux-3.18.14-rt/arch/sparc/kernel/irq_64.c ---- linux-3.18.14.orig/arch/sparc/kernel/irq_64.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/sparc/kernel/irq_64.c 2015-05-31 15:32:46.477635385 -0500 -@@ -849,6 +849,7 @@ - set_irq_regs(old_regs); - } - -+#ifndef CONFIG_PREEMPT_RT_FULL - void do_softirq_own_stack(void) - { - void *orig_sp, *sp = softirq_stack[smp_processor_id()]; -@@ -863,6 +864,7 @@ - __asm__ __volatile__("mov %0, %%sp" - : : "r" (orig_sp)); - } -+#endif - - #ifdef CONFIG_HOTPLUG_CPU - void fixup_irqs(void) -diff -Nur linux-3.18.14.orig/arch/sparc/kernel/setup_32.c linux-3.18.14-rt/arch/sparc/kernel/setup_32.c ---- linux-3.18.14.orig/arch/sparc/kernel/setup_32.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/sparc/kernel/setup_32.c 2015-05-31 15:32:46.489635385 -0500 -@@ -309,6 +309,7 @@ - - boot_flags_init(*cmdline_p); - -+ early_console = &prom_early_console; - register_console(&prom_early_console); - - printk("ARCH: "); -diff -Nur linux-3.18.14.orig/arch/sparc/kernel/setup_64.c linux-3.18.14-rt/arch/sparc/kernel/setup_64.c ---- linux-3.18.14.orig/arch/sparc/kernel/setup_64.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/sparc/kernel/setup_64.c 2015-05-31 15:32:46.509635384 -0500 -@@ -563,6 +563,12 @@ - pause_patch(); - } - -+static inline void register_prom_console(void) -+{ -+ early_console = &prom_early_console; -+ register_console(&prom_early_console); -+} -+ - void __init setup_arch(char **cmdline_p) - { - /* Initialize PROM console and command line. */ -@@ -574,7 +580,7 @@ - #ifdef CONFIG_EARLYFB - if (btext_find_display()) - #endif -- register_console(&prom_early_console); -+ register_prom_console(); - - if (tlb_type == hypervisor) - printk("ARCH: SUN4V\n"); -diff -Nur linux-3.18.14.orig/arch/sparc/mm/fault_32.c linux-3.18.14-rt/arch/sparc/mm/fault_32.c ---- linux-3.18.14.orig/arch/sparc/mm/fault_32.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/sparc/mm/fault_32.c 2015-05-31 15:32:46.529635385 -0500 -@@ -196,7 +196,7 @@ - * If we're in an interrupt or have no user - * context, we must not take the fault.. - */ -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto no_context; - - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); -diff -Nur linux-3.18.14.orig/arch/sparc/mm/fault_64.c linux-3.18.14-rt/arch/sparc/mm/fault_64.c ---- linux-3.18.14.orig/arch/sparc/mm/fault_64.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/sparc/mm/fault_64.c 2015-05-31 15:32:46.529635385 -0500 -@@ -330,7 +330,7 @@ - * If we're in an interrupt or have no user - * context, we must not take the fault.. - */ -- if (in_atomic() || !mm) -+ if (!mm || pagefault_disabled()) - goto intr_or_no_mm; - - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); -diff -Nur linux-3.18.14.orig/arch/tile/mm/fault.c linux-3.18.14-rt/arch/tile/mm/fault.c ---- linux-3.18.14.orig/arch/tile/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/tile/mm/fault.c 2015-05-31 15:32:46.533635385 -0500 -@@ -357,7 +357,7 @@ - * If we're in an interrupt, have no user context or are running in an - * atomic region then we must not take the fault. - */ -- if (in_atomic() || !mm) { -+ if (!mm || pagefault_disabled()) { - vma = NULL; /* happy compiler */ - goto bad_area_nosemaphore; - } -diff -Nur linux-3.18.14.orig/arch/um/kernel/trap.c linux-3.18.14-rt/arch/um/kernel/trap.c ---- linux-3.18.14.orig/arch/um/kernel/trap.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/um/kernel/trap.c 2015-05-31 15:32:46.537635384 -0500 -@@ -38,7 +38,7 @@ - * If the fault was during atomic operation, don't take the fault, just - * fail. - */ -- if (in_atomic()) -+ if (pagefault_disabled()) - goto out_nosemaphore; - - if (is_user) -diff -Nur linux-3.18.14.orig/arch/x86/crypto/aesni-intel_glue.c linux-3.18.14-rt/arch/x86/crypto/aesni-intel_glue.c ---- linux-3.18.14.orig/arch/x86/crypto/aesni-intel_glue.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/crypto/aesni-intel_glue.c 2015-05-31 15:32:46.569635384 -0500 -@@ -381,14 +381,14 @@ - err = blkcipher_walk_virt(desc, &walk); - desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; - -- kernel_fpu_begin(); - while ((nbytes = walk.nbytes)) { -+ kernel_fpu_begin(); - aesni_ecb_enc(ctx, walk.dst.virt.addr, walk.src.virt.addr, -- nbytes & AES_BLOCK_MASK); -+ nbytes & AES_BLOCK_MASK); -+ kernel_fpu_end(); - nbytes &= AES_BLOCK_SIZE - 1; - err = blkcipher_walk_done(desc, &walk, nbytes); - } -- kernel_fpu_end(); - - return err; - } -@@ -405,14 +405,14 @@ - err = blkcipher_walk_virt(desc, &walk); - desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; - -- kernel_fpu_begin(); - while ((nbytes = walk.nbytes)) { -+ kernel_fpu_begin(); - aesni_ecb_dec(ctx, walk.dst.virt.addr, walk.src.virt.addr, - nbytes & AES_BLOCK_MASK); -+ kernel_fpu_end(); - nbytes &= AES_BLOCK_SIZE - 1; - err = blkcipher_walk_done(desc, &walk, nbytes); - } -- kernel_fpu_end(); - - return err; - } -@@ -429,14 +429,14 @@ - err = blkcipher_walk_virt(desc, &walk); - desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; - -- kernel_fpu_begin(); - while ((nbytes = walk.nbytes)) { -+ kernel_fpu_begin(); - aesni_cbc_enc(ctx, walk.dst.virt.addr, walk.src.virt.addr, - nbytes & AES_BLOCK_MASK, walk.iv); -+ kernel_fpu_end(); - nbytes &= AES_BLOCK_SIZE - 1; - err = blkcipher_walk_done(desc, &walk, nbytes); - } -- kernel_fpu_end(); - - return err; - } -@@ -453,14 +453,14 @@ - err = blkcipher_walk_virt(desc, &walk); - desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; - -- kernel_fpu_begin(); - while ((nbytes = walk.nbytes)) { -+ kernel_fpu_begin(); - aesni_cbc_dec(ctx, walk.dst.virt.addr, walk.src.virt.addr, - nbytes & AES_BLOCK_MASK, walk.iv); -+ kernel_fpu_end(); - nbytes &= AES_BLOCK_SIZE - 1; - err = blkcipher_walk_done(desc, &walk, nbytes); - } -- kernel_fpu_end(); - - return err; - } -@@ -512,18 +512,20 @@ - err = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE); - desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; - -- kernel_fpu_begin(); - while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) { -+ kernel_fpu_begin(); - aesni_ctr_enc_tfm(ctx, walk.dst.virt.addr, walk.src.virt.addr, - nbytes & AES_BLOCK_MASK, walk.iv); -+ kernel_fpu_end(); - nbytes &= AES_BLOCK_SIZE - 1; - err = blkcipher_walk_done(desc, &walk, nbytes); - } - if (walk.nbytes) { -+ kernel_fpu_begin(); - ctr_crypt_final(ctx, &walk); -+ kernel_fpu_end(); - err = blkcipher_walk_done(desc, &walk, 0); - } -- kernel_fpu_end(); - - return err; - } -diff -Nur linux-3.18.14.orig/arch/x86/crypto/cast5_avx_glue.c linux-3.18.14-rt/arch/x86/crypto/cast5_avx_glue.c ---- linux-3.18.14.orig/arch/x86/crypto/cast5_avx_glue.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/crypto/cast5_avx_glue.c 2015-05-31 15:32:46.585635384 -0500 -@@ -60,7 +60,7 @@ - static int ecb_crypt(struct blkcipher_desc *desc, struct blkcipher_walk *walk, - bool enc) - { -- bool fpu_enabled = false; -+ bool fpu_enabled; - struct cast5_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); - const unsigned int bsize = CAST5_BLOCK_SIZE; - unsigned int nbytes; -@@ -76,7 +76,7 @@ - u8 *wsrc = walk->src.virt.addr; - u8 *wdst = walk->dst.virt.addr; - -- fpu_enabled = cast5_fpu_begin(fpu_enabled, nbytes); -+ fpu_enabled = cast5_fpu_begin(false, nbytes); - - /* Process multi-block batch */ - if (nbytes >= bsize * CAST5_PARALLEL_BLOCKS) { -@@ -104,10 +104,9 @@ - } while (nbytes >= bsize); - - done: -+ cast5_fpu_end(fpu_enabled); - err = blkcipher_walk_done(desc, walk, nbytes); - } -- -- cast5_fpu_end(fpu_enabled); - return err; - } - -@@ -228,7 +227,7 @@ - static int cbc_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, - struct scatterlist *src, unsigned int nbytes) - { -- bool fpu_enabled = false; -+ bool fpu_enabled; - struct blkcipher_walk walk; - int err; - -@@ -237,12 +236,11 @@ - desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; - - while ((nbytes = walk.nbytes)) { -- fpu_enabled = cast5_fpu_begin(fpu_enabled, nbytes); -+ fpu_enabled = cast5_fpu_begin(false, nbytes); - nbytes = __cbc_decrypt(desc, &walk); -+ cast5_fpu_end(fpu_enabled); - err = blkcipher_walk_done(desc, &walk, nbytes); - } -- -- cast5_fpu_end(fpu_enabled); - return err; - } - -@@ -312,7 +310,7 @@ - static int ctr_crypt(struct blkcipher_desc *desc, struct scatterlist *dst, - struct scatterlist *src, unsigned int nbytes) - { -- bool fpu_enabled = false; -+ bool fpu_enabled; - struct blkcipher_walk walk; - int err; - -@@ -321,13 +319,12 @@ - desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; - - while ((nbytes = walk.nbytes) >= CAST5_BLOCK_SIZE) { -- fpu_enabled = cast5_fpu_begin(fpu_enabled, nbytes); -+ fpu_enabled = cast5_fpu_begin(false, nbytes); - nbytes = __ctr_crypt(desc, &walk); -+ cast5_fpu_end(fpu_enabled); - err = blkcipher_walk_done(desc, &walk, nbytes); - } - -- cast5_fpu_end(fpu_enabled); -- - if (walk.nbytes) { - ctr_crypt_final(desc, &walk); - err = blkcipher_walk_done(desc, &walk, 0); -diff -Nur linux-3.18.14.orig/arch/x86/crypto/glue_helper.c linux-3.18.14-rt/arch/x86/crypto/glue_helper.c ---- linux-3.18.14.orig/arch/x86/crypto/glue_helper.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/crypto/glue_helper.c 2015-05-31 15:32:46.589635384 -0500 -@@ -39,7 +39,7 @@ - void *ctx = crypto_blkcipher_ctx(desc->tfm); - const unsigned int bsize = 128 / 8; - unsigned int nbytes, i, func_bytes; -- bool fpu_enabled = false; -+ bool fpu_enabled; - int err; - - err = blkcipher_walk_virt(desc, walk); -@@ -49,7 +49,7 @@ - u8 *wdst = walk->dst.virt.addr; - - fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit, -- desc, fpu_enabled, nbytes); -+ desc, false, nbytes); - - for (i = 0; i < gctx->num_funcs; i++) { - func_bytes = bsize * gctx->funcs[i].num_blocks; -@@ -71,10 +71,10 @@ - } - - done: -+ glue_fpu_end(fpu_enabled); - err = blkcipher_walk_done(desc, walk, nbytes); - } - -- glue_fpu_end(fpu_enabled); - return err; - } - -@@ -194,7 +194,7 @@ - struct scatterlist *src, unsigned int nbytes) - { - const unsigned int bsize = 128 / 8; -- bool fpu_enabled = false; -+ bool fpu_enabled; - struct blkcipher_walk walk; - int err; - -@@ -203,12 +203,12 @@ - - while ((nbytes = walk.nbytes)) { - fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit, -- desc, fpu_enabled, nbytes); -+ desc, false, nbytes); - nbytes = __glue_cbc_decrypt_128bit(gctx, desc, &walk); -+ glue_fpu_end(fpu_enabled); - err = blkcipher_walk_done(desc, &walk, nbytes); - } - -- glue_fpu_end(fpu_enabled); - return err; - } - EXPORT_SYMBOL_GPL(glue_cbc_decrypt_128bit); -@@ -278,7 +278,7 @@ - struct scatterlist *src, unsigned int nbytes) - { - const unsigned int bsize = 128 / 8; -- bool fpu_enabled = false; -+ bool fpu_enabled; - struct blkcipher_walk walk; - int err; - -@@ -287,13 +287,12 @@ - - while ((nbytes = walk.nbytes) >= bsize) { - fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit, -- desc, fpu_enabled, nbytes); -+ desc, false, nbytes); - nbytes = __glue_ctr_crypt_128bit(gctx, desc, &walk); -+ glue_fpu_end(fpu_enabled); - err = blkcipher_walk_done(desc, &walk, nbytes); - } - -- glue_fpu_end(fpu_enabled); -- - if (walk.nbytes) { - glue_ctr_crypt_final_128bit( - gctx->funcs[gctx->num_funcs - 1].fn_u.ctr, desc, &walk); -@@ -348,7 +347,7 @@ - void *tweak_ctx, void *crypt_ctx) - { - const unsigned int bsize = 128 / 8; -- bool fpu_enabled = false; -+ bool fpu_enabled; - struct blkcipher_walk walk; - int err; - -@@ -361,21 +360,21 @@ - - /* set minimum length to bsize, for tweak_fn */ - fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit, -- desc, fpu_enabled, -+ desc, false, - nbytes < bsize ? bsize : nbytes); -- - /* calculate first value of T */ - tweak_fn(tweak_ctx, walk.iv, walk.iv); -+ glue_fpu_end(fpu_enabled); - - while (nbytes) { -+ fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit, -+ desc, false, nbytes); - nbytes = __glue_xts_crypt_128bit(gctx, crypt_ctx, desc, &walk); - -+ glue_fpu_end(fpu_enabled); - err = blkcipher_walk_done(desc, &walk, nbytes); - nbytes = walk.nbytes; - } -- -- glue_fpu_end(fpu_enabled); -- - return err; - } - EXPORT_SYMBOL_GPL(glue_xts_crypt_128bit); -diff -Nur linux-3.18.14.orig/arch/x86/include/asm/preempt.h linux-3.18.14-rt/arch/x86/include/asm/preempt.h ---- linux-3.18.14.orig/arch/x86/include/asm/preempt.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/include/asm/preempt.h 2015-05-31 15:32:46.597635384 -0500 -@@ -85,17 +85,33 @@ - * a decrement which hits zero means we have no preempt_count and should - * reschedule. - */ --static __always_inline bool __preempt_count_dec_and_test(void) -+static __always_inline bool ____preempt_count_dec_and_test(void) - { - GEN_UNARY_RMWcc("decl", __preempt_count, __percpu_arg(0), "e"); - } - -+static __always_inline bool __preempt_count_dec_and_test(void) -+{ -+ if (____preempt_count_dec_and_test()) -+ return true; -+#ifdef CONFIG_PREEMPT_LAZY -+ return test_thread_flag(TIF_NEED_RESCHED_LAZY); -+#else -+ return false; -+#endif -+} -+ - /* - * Returns true when we need to resched and can (barring IRQ state). - */ - static __always_inline bool should_resched(void) - { -+#ifdef CONFIG_PREEMPT_LAZY -+ return unlikely(!raw_cpu_read_4(__preempt_count) || \ -+ test_thread_flag(TIF_NEED_RESCHED_LAZY)); -+#else - return unlikely(!raw_cpu_read_4(__preempt_count)); -+#endif - } - - #ifdef CONFIG_PREEMPT -diff -Nur linux-3.18.14.orig/arch/x86/include/asm/signal.h linux-3.18.14-rt/arch/x86/include/asm/signal.h ---- linux-3.18.14.orig/arch/x86/include/asm/signal.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/include/asm/signal.h 2015-05-31 15:32:46.597635384 -0500 -@@ -23,6 +23,19 @@ - unsigned long sig[_NSIG_WORDS]; - } sigset_t; - -+/* -+ * Because some traps use the IST stack, we must keep preemption -+ * disabled while calling do_trap(), but do_trap() may call -+ * force_sig_info() which will grab the signal spin_locks for the -+ * task, which in PREEMPT_RT_FULL are mutexes. By defining -+ * ARCH_RT_DELAYS_SIGNAL_SEND the force_sig_info() will set -+ * TIF_NOTIFY_RESUME and set up the signal to be sent on exit of the -+ * trap. -+ */ -+#if defined(CONFIG_PREEMPT_RT_FULL) && defined(CONFIG_X86_64) -+#define ARCH_RT_DELAYS_SIGNAL_SEND -+#endif -+ - #ifndef CONFIG_COMPAT - typedef sigset_t compat_sigset_t; - #endif -diff -Nur linux-3.18.14.orig/arch/x86/include/asm/stackprotector.h linux-3.18.14-rt/arch/x86/include/asm/stackprotector.h ---- linux-3.18.14.orig/arch/x86/include/asm/stackprotector.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/include/asm/stackprotector.h 2015-05-31 15:32:46.613635384 -0500 -@@ -57,7 +57,7 @@ - */ - static __always_inline void boot_init_stack_canary(void) - { -- u64 canary; -+ u64 uninitialized_var(canary); - u64 tsc; - - #ifdef CONFIG_X86_64 -@@ -68,8 +68,16 @@ - * of randomness. The TSC only matters for very early init, - * there it already has some randomness on most systems. Later - * on during the bootup the random pool has true entropy too. -+ * -+ * For preempt-rt we need to weaken the randomness a bit, as -+ * we can't call into the random generator from atomic context -+ * due to locking constraints. We just leave canary -+ * uninitialized and use the TSC based randomness on top of -+ * it. - */ -+#ifndef CONFIG_PREEMPT_RT_FULL - get_random_bytes(&canary, sizeof(canary)); -+#endif - tsc = __native_read_tsc(); - canary += tsc + (tsc << 32UL); - -diff -Nur linux-3.18.14.orig/arch/x86/include/asm/thread_info.h linux-3.18.14-rt/arch/x86/include/asm/thread_info.h ---- linux-3.18.14.orig/arch/x86/include/asm/thread_info.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/include/asm/thread_info.h 2015-05-31 15:32:46.621635383 -0500 -@@ -30,6 +30,8 @@ - __u32 status; /* thread synchronous flags */ - __u32 cpu; /* current CPU */ - int saved_preempt_count; -+ int preempt_lazy_count; /* 0 => lazy preemptable -+ <0 => BUG */ - mm_segment_t addr_limit; - struct restart_block restart_block; - void __user *sysenter_return; -@@ -75,6 +77,7 @@ - #define TIF_SYSCALL_EMU 6 /* syscall emulation active */ - #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ - #define TIF_SECCOMP 8 /* secure computing */ -+#define TIF_NEED_RESCHED_LAZY 9 /* lazy rescheduling necessary */ - #define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */ - #define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */ - #define TIF_UPROBE 12 /* breakpointed or singlestepping */ -@@ -100,6 +103,7 @@ - #define _TIF_SYSCALL_EMU (1 << TIF_SYSCALL_EMU) - #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) - #define _TIF_SECCOMP (1 << TIF_SECCOMP) -+#define _TIF_NEED_RESCHED_LAZY (1 << TIF_NEED_RESCHED_LAZY) - #define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY) - #define _TIF_USER_RETURN_NOTIFY (1 << TIF_USER_RETURN_NOTIFY) - #define _TIF_UPROBE (1 << TIF_UPROBE) -@@ -150,6 +154,8 @@ - #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) - #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW) - -+#define _TIF_NEED_RESCHED_MASK (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY) -+ - #define STACK_WARN (THREAD_SIZE/8) - #define KERNEL_STACK_OFFSET (5*(BITS_PER_LONG/8)) - -diff -Nur linux-3.18.14.orig/arch/x86/include/asm/uv/uv_bau.h linux-3.18.14-rt/arch/x86/include/asm/uv/uv_bau.h ---- linux-3.18.14.orig/arch/x86/include/asm/uv/uv_bau.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/include/asm/uv/uv_bau.h 2015-05-31 15:32:46.621635383 -0500 -@@ -615,9 +615,9 @@ - cycles_t send_message; - cycles_t period_end; - cycles_t period_time; -- spinlock_t uvhub_lock; -- spinlock_t queue_lock; -- spinlock_t disable_lock; -+ raw_spinlock_t uvhub_lock; -+ raw_spinlock_t queue_lock; -+ raw_spinlock_t disable_lock; - /* tunables */ - int max_concurr; - int max_concurr_const; -@@ -776,15 +776,15 @@ - * to be lowered below the current 'v'. atomic_add_unless can only stop - * on equal. - */ --static inline int atomic_inc_unless_ge(spinlock_t *lock, atomic_t *v, int u) -+static inline int atomic_inc_unless_ge(raw_spinlock_t *lock, atomic_t *v, int u) - { -- spin_lock(lock); -+ raw_spin_lock(lock); - if (atomic_read(v) >= u) { -- spin_unlock(lock); -+ raw_spin_unlock(lock); - return 0; - } - atomic_inc(v); -- spin_unlock(lock); -+ raw_spin_unlock(lock); - return 1; - } - -diff -Nur linux-3.18.14.orig/arch/x86/include/asm/uv/uv_hub.h linux-3.18.14-rt/arch/x86/include/asm/uv/uv_hub.h ---- linux-3.18.14.orig/arch/x86/include/asm/uv/uv_hub.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/include/asm/uv/uv_hub.h 2015-05-31 15:32:46.621635383 -0500 -@@ -492,7 +492,7 @@ - unsigned short nr_online_cpus; - unsigned short pnode; - short memory_nid; -- spinlock_t nmi_lock; /* obsolete, see uv_hub_nmi */ -+ raw_spinlock_t nmi_lock; /* obsolete, see uv_hub_nmi */ - unsigned long nmi_count; /* obsolete, see uv_hub_nmi */ - }; - extern struct uv_blade_info *uv_blade_info; -diff -Nur linux-3.18.14.orig/arch/x86/Kconfig linux-3.18.14-rt/arch/x86/Kconfig ---- linux-3.18.14.orig/arch/x86/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/Kconfig 2015-05-31 15:32:46.561635384 -0500 -@@ -21,6 +21,7 @@ - ### Arch settings - config X86 - def_bool y -+ select HAVE_PREEMPT_LAZY - select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI - select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS - select ARCH_HAS_FAST_MULTIPLIER -@@ -197,8 +198,11 @@ - def_bool y - depends on ISA_DMA_API - -+config RWSEM_GENERIC_SPINLOCK -+ def_bool PREEMPT_RT_FULL -+ - config RWSEM_XCHGADD_ALGORITHM -- def_bool y -+ def_bool !RWSEM_GENERIC_SPINLOCK && !PREEMPT_RT_FULL - - config GENERIC_CALIBRATE_DELAY - def_bool y -@@ -811,7 +815,7 @@ - config MAXSMP - bool "Enable Maximum number of SMP Processors and NUMA Nodes" - depends on X86_64 && SMP && DEBUG_KERNEL -- select CPUMASK_OFFSTACK -+ select CPUMASK_OFFSTACK if !PREEMPT_RT_FULL - ---help--- - Enable maximum number of CPUS and NUMA Nodes for this architecture. - If unsure, say N. -diff -Nur linux-3.18.14.orig/arch/x86/kernel/apic/io_apic.c linux-3.18.14-rt/arch/x86/kernel/apic/io_apic.c ---- linux-3.18.14.orig/arch/x86/kernel/apic/io_apic.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kernel/apic/io_apic.c 2015-05-31 15:32:46.629635384 -0500 -@@ -2494,7 +2494,8 @@ - static inline bool ioapic_irqd_mask(struct irq_data *data, struct irq_cfg *cfg) - { - /* If we are moving the irq we need to mask it */ -- if (unlikely(irqd_is_setaffinity_pending(data))) { -+ if (unlikely(irqd_is_setaffinity_pending(data) && -+ !irqd_irq_inprogress(data))) { - mask_ioapic(cfg); - return true; - } -diff -Nur linux-3.18.14.orig/arch/x86/kernel/apic/x2apic_uv_x.c linux-3.18.14-rt/arch/x86/kernel/apic/x2apic_uv_x.c ---- linux-3.18.14.orig/arch/x86/kernel/apic/x2apic_uv_x.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kernel/apic/x2apic_uv_x.c 2015-05-31 15:32:46.629635384 -0500 -@@ -918,7 +918,7 @@ - uv_blade_info[blade].pnode = pnode; - uv_blade_info[blade].nr_possible_cpus = 0; - uv_blade_info[blade].nr_online_cpus = 0; -- spin_lock_init(&uv_blade_info[blade].nmi_lock); -+ raw_spin_lock_init(&uv_blade_info[blade].nmi_lock); - min_pnode = min(pnode, min_pnode); - max_pnode = max(pnode, max_pnode); - blade++; -diff -Nur linux-3.18.14.orig/arch/x86/kernel/asm-offsets.c linux-3.18.14-rt/arch/x86/kernel/asm-offsets.c ---- linux-3.18.14.orig/arch/x86/kernel/asm-offsets.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kernel/asm-offsets.c 2015-05-31 15:32:46.633635383 -0500 -@@ -32,6 +32,7 @@ - OFFSET(TI_flags, thread_info, flags); - OFFSET(TI_status, thread_info, status); - OFFSET(TI_addr_limit, thread_info, addr_limit); -+ OFFSET(TI_preempt_lazy_count, thread_info, preempt_lazy_count); - - BLANK(); - OFFSET(crypto_tfm_ctx_offset, crypto_tfm, __crt_ctx); -@@ -71,4 +72,5 @@ - - BLANK(); - DEFINE(PTREGS_SIZE, sizeof(struct pt_regs)); -+ DEFINE(_PREEMPT_ENABLED, PREEMPT_ENABLED); - } -diff -Nur linux-3.18.14.orig/arch/x86/kernel/cpu/mcheck/mce.c linux-3.18.14-rt/arch/x86/kernel/cpu/mcheck/mce.c ---- linux-3.18.14.orig/arch/x86/kernel/cpu/mcheck/mce.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kernel/cpu/mcheck/mce.c 2015-05-31 15:32:46.641635383 -0500 -@@ -41,6 +41,8 @@ - #include - #include - #include -+#include -+#include - - #include - #include -@@ -1266,7 +1268,7 @@ - static unsigned long check_interval = 5 * 60; /* 5 minutes */ - - static DEFINE_PER_CPU(unsigned long, mce_next_interval); /* in jiffies */ --static DEFINE_PER_CPU(struct timer_list, mce_timer); -+static DEFINE_PER_CPU(struct hrtimer, mce_timer); - - static unsigned long mce_adjust_timer_default(unsigned long interval) - { -@@ -1283,14 +1285,11 @@ - return test_and_clear_bit(0, v); - } - --static void mce_timer_fn(unsigned long data) -+static enum hrtimer_restart mce_timer_fn(struct hrtimer *timer) - { -- struct timer_list *t = this_cpu_ptr(&mce_timer); - unsigned long iv; - int notify; - -- WARN_ON(smp_processor_id() != data); -- - if (mce_available(this_cpu_ptr(&cpu_info))) { - machine_check_poll(MCP_TIMESTAMP, - this_cpu_ptr(&mce_poll_banks)); -@@ -1313,9 +1312,11 @@ - __this_cpu_write(mce_next_interval, iv); - /* Might have become 0 after CMCI storm subsided */ - if (iv) { -- t->expires = jiffies + iv; -- add_timer_on(t, smp_processor_id()); -+ hrtimer_forward_now(timer, ns_to_ktime( -+ jiffies_to_usecs(iv) * 1000ULL)); -+ return HRTIMER_RESTART; - } -+ return HRTIMER_NORESTART; - } - - /* -@@ -1323,28 +1324,37 @@ - */ - void mce_timer_kick(unsigned long interval) - { -- struct timer_list *t = this_cpu_ptr(&mce_timer); -- unsigned long when = jiffies + interval; -+ struct hrtimer *t = this_cpu_ptr(&mce_timer); - unsigned long iv = __this_cpu_read(mce_next_interval); - -- if (timer_pending(t)) { -- if (time_before(when, t->expires)) -- mod_timer_pinned(t, when); -+ if (hrtimer_active(t)) { -+ s64 exp; -+ s64 intv_us; -+ -+ intv_us = jiffies_to_usecs(interval); -+ exp = ktime_to_us(hrtimer_expires_remaining(t)); -+ if (intv_us < exp) { -+ hrtimer_cancel(t); -+ hrtimer_start_range_ns(t, -+ ns_to_ktime(intv_us * 1000), -+ 0, HRTIMER_MODE_REL_PINNED); -+ } - } else { -- t->expires = round_jiffies(when); -- add_timer_on(t, smp_processor_id()); -+ hrtimer_start_range_ns(t, -+ ns_to_ktime(jiffies_to_usecs(interval) * 1000ULL), -+ 0, HRTIMER_MODE_REL_PINNED); - } - if (interval < iv) - __this_cpu_write(mce_next_interval, interval); - } - --/* Must not be called in IRQ context where del_timer_sync() can deadlock */ -+/* Must not be called in IRQ context where hrtimer_cancel() can deadlock */ - static void mce_timer_delete_all(void) - { - int cpu; - - for_each_online_cpu(cpu) -- del_timer_sync(&per_cpu(mce_timer, cpu)); -+ hrtimer_cancel(&per_cpu(mce_timer, cpu)); - } - - static void mce_do_trigger(struct work_struct *work) -@@ -1354,6 +1364,56 @@ - - static DECLARE_WORK(mce_trigger_work, mce_do_trigger); - -+static void __mce_notify_work(struct swork_event *event) -+{ -+ /* Not more than two messages every minute */ -+ static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2); -+ -+ /* wake processes polling /dev/mcelog */ -+ wake_up_interruptible(&mce_chrdev_wait); -+ -+ /* -+ * There is no risk of missing notifications because -+ * work_pending is always cleared before the function is -+ * executed. -+ */ -+ if (mce_helper[0] && !work_pending(&mce_trigger_work)) -+ schedule_work(&mce_trigger_work); -+ -+ if (__ratelimit(&ratelimit)) -+ pr_info(HW_ERR "Machine check events logged\n"); -+} -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+static bool notify_work_ready __read_mostly; -+static struct swork_event notify_work; -+ -+static int mce_notify_work_init(void) -+{ -+ int err; -+ -+ err = swork_get(); -+ if (err) -+ return err; -+ -+ INIT_SWORK(¬ify_work, __mce_notify_work); -+ notify_work_ready = true; -+ return 0; -+} -+ -+static void mce_notify_work(void) -+{ -+ if (notify_work_ready) -+ swork_queue(¬ify_work); -+} -+#else -+static void mce_notify_work(void) -+{ -+ __mce_notify_work(NULL); -+} -+static inline int mce_notify_work_init(void) { return 0; } -+#endif -+ - /* - * Notify the user(s) about new machine check events. - * Can be called from interrupt context, but not from machine check/NMI -@@ -1361,19 +1421,8 @@ - */ - int mce_notify_irq(void) - { -- /* Not more than two messages every minute */ -- static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2); -- - if (test_and_clear_bit(0, &mce_need_notify)) { -- /* wake processes polling /dev/mcelog */ -- wake_up_interruptible(&mce_chrdev_wait); -- -- if (mce_helper[0]) -- schedule_work(&mce_trigger_work); -- -- if (__ratelimit(&ratelimit)) -- pr_info(HW_ERR "Machine check events logged\n"); -- -+ mce_notify_work(); - return 1; - } - return 0; -@@ -1644,7 +1693,7 @@ - } - } - --static void mce_start_timer(unsigned int cpu, struct timer_list *t) -+static void mce_start_timer(unsigned int cpu, struct hrtimer *t) - { - unsigned long iv = check_interval * HZ; - -@@ -1653,16 +1702,17 @@ - - per_cpu(mce_next_interval, cpu) = iv; - -- t->expires = round_jiffies(jiffies + iv); -- add_timer_on(t, cpu); -+ hrtimer_start_range_ns(t, ns_to_ktime(jiffies_to_usecs(iv) * 1000ULL), -+ 0, HRTIMER_MODE_REL_PINNED); - } - - static void __mcheck_cpu_init_timer(void) - { -- struct timer_list *t = this_cpu_ptr(&mce_timer); -+ struct hrtimer *t = this_cpu_ptr(&mce_timer); - unsigned int cpu = smp_processor_id(); - -- setup_timer(t, mce_timer_fn, cpu); -+ hrtimer_init(t, CLOCK_MONOTONIC, HRTIMER_MODE_REL); -+ t->function = mce_timer_fn; - mce_start_timer(cpu, t); - } - -@@ -2339,6 +2389,8 @@ - if (!mce_available(raw_cpu_ptr(&cpu_info))) - return; - -+ hrtimer_cancel(this_cpu_ptr(&mce_timer)); -+ - if (!(action & CPU_TASKS_FROZEN)) - cmci_clear(); - for (i = 0; i < mca_cfg.banks; i++) { -@@ -2365,6 +2417,7 @@ - if (b->init) - wrmsrl(MSR_IA32_MCx_CTL(i), b->ctl); - } -+ __mcheck_cpu_init_timer(); - } - - /* Get notified when a cpu comes on/off. Be hotplug friendly. */ -@@ -2372,7 +2425,6 @@ - mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) - { - unsigned int cpu = (unsigned long)hcpu; -- struct timer_list *t = &per_cpu(mce_timer, cpu); - - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_ONLINE: -@@ -2392,11 +2444,9 @@ - break; - case CPU_DOWN_PREPARE: - smp_call_function_single(cpu, mce_disable_cpu, &action, 1); -- del_timer_sync(t); - break; - case CPU_DOWN_FAILED: - smp_call_function_single(cpu, mce_reenable_cpu, &action, 1); -- mce_start_timer(cpu, t); - break; - } - -@@ -2435,6 +2485,10 @@ - goto err_out; - } - -+ err = mce_notify_work_init(); -+ if (err) -+ goto err_out; -+ - if (!zalloc_cpumask_var(&mce_device_initialized, GFP_KERNEL)) { - err = -ENOMEM; - goto err_out; -diff -Nur linux-3.18.14.orig/arch/x86/kernel/entry_32.S linux-3.18.14-rt/arch/x86/kernel/entry_32.S ---- linux-3.18.14.orig/arch/x86/kernel/entry_32.S 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kernel/entry_32.S 2015-05-31 15:32:46.641635383 -0500 -@@ -359,8 +359,24 @@ - ENTRY(resume_kernel) - DISABLE_INTERRUPTS(CLBR_ANY) - need_resched: -+ # preempt count == 0 + NEED_RS set? - cmpl $0,PER_CPU_VAR(__preempt_count) -+#ifndef CONFIG_PREEMPT_LAZY - jnz restore_all -+#else -+ jz test_int_off -+ -+ # atleast preempt count == 0 ? -+ cmpl $_PREEMPT_ENABLED,PER_CPU_VAR(__preempt_count) -+ jne restore_all -+ -+ cmpl $0,TI_preempt_lazy_count(%ebp) # non-zero preempt_lazy_count ? -+ jnz restore_all -+ -+ testl $_TIF_NEED_RESCHED_LAZY, TI_flags(%ebp) -+ jz restore_all -+test_int_off: -+#endif - testl $X86_EFLAGS_IF,PT_EFLAGS(%esp) # interrupts off (exception path) ? - jz restore_all - call preempt_schedule_irq -@@ -591,7 +607,7 @@ - ALIGN - RING0_PTREGS_FRAME # can't unwind into user space anyway - work_pending: -- testb $_TIF_NEED_RESCHED, %cl -+ testl $_TIF_NEED_RESCHED_MASK, %ecx - jz work_notifysig - work_resched: - call schedule -@@ -604,7 +620,7 @@ - andl $_TIF_WORK_MASK, %ecx # is there any work to be done other - # than syscall tracing? - jz restore_all -- testb $_TIF_NEED_RESCHED, %cl -+ testl $_TIF_NEED_RESCHED_MASK, %ecx - jnz work_resched - - work_notifysig: # deal with pending signals and -diff -Nur linux-3.18.14.orig/arch/x86/kernel/entry_64.S linux-3.18.14-rt/arch/x86/kernel/entry_64.S ---- linux-3.18.14.orig/arch/x86/kernel/entry_64.S 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kernel/entry_64.S 2015-05-31 15:32:46.649635383 -0500 -@@ -454,8 +454,8 @@ - /* Handle reschedules */ - /* edx: work, edi: workmask */ - sysret_careful: -- bt $TIF_NEED_RESCHED,%edx -- jnc sysret_signal -+ testl $_TIF_NEED_RESCHED_MASK,%edx -+ jz sysret_signal - TRACE_IRQS_ON - ENABLE_INTERRUPTS(CLBR_NONE) - pushq_cfi %rdi -@@ -554,8 +554,8 @@ - /* First do a reschedule test. */ - /* edx: work, edi: workmask */ - int_careful: -- bt $TIF_NEED_RESCHED,%edx -- jnc int_very_careful -+ testl $_TIF_NEED_RESCHED_MASK,%edx -+ jz int_very_careful - TRACE_IRQS_ON - ENABLE_INTERRUPTS(CLBR_NONE) - pushq_cfi %rdi -@@ -870,8 +870,8 @@ - /* edi: workmask, edx: work */ - retint_careful: - CFI_RESTORE_STATE -- bt $TIF_NEED_RESCHED,%edx -- jnc retint_signal -+ testl $_TIF_NEED_RESCHED_MASK,%edx -+ jz retint_signal - TRACE_IRQS_ON - ENABLE_INTERRUPTS(CLBR_NONE) - pushq_cfi %rdi -@@ -903,7 +903,22 @@ - /* rcx: threadinfo. interrupts off. */ - ENTRY(retint_kernel) - cmpl $0,PER_CPU_VAR(__preempt_count) -+#ifndef CONFIG_PREEMPT_LAZY - jnz retint_restore_args -+#else -+ jz check_int_off -+ -+ # atleast preempt count == 0 ? -+ cmpl $_PREEMPT_ENABLED,PER_CPU_VAR(__preempt_count) -+ jnz retint_restore_args -+ -+ cmpl $0, TI_preempt_lazy_count(%rcx) -+ jnz retint_restore_args -+ -+ bt $TIF_NEED_RESCHED_LAZY,TI_flags(%rcx) -+ jnc retint_restore_args -+check_int_off: -+#endif - bt $9,EFLAGS-ARGOFFSET(%rsp) /* interrupts off? */ - jnc retint_restore_args - call preempt_schedule_irq -@@ -1119,6 +1134,7 @@ - jmp 2b - .previous - -+#ifndef CONFIG_PREEMPT_RT_FULL - /* Call softirq on interrupt stack. Interrupts are off. */ - ENTRY(do_softirq_own_stack) - CFI_STARTPROC -@@ -1138,6 +1154,7 @@ - ret - CFI_ENDPROC - END(do_softirq_own_stack) -+#endif - - #ifdef CONFIG_XEN - idtentry xen_hypervisor_callback xen_do_hypervisor_callback has_error_code=0 -@@ -1302,7 +1319,7 @@ - movq %rsp,%rdi /* &pt_regs */ - call sync_regs - movq %rax,%rsp /* switch stack for scheduling */ -- testl $_TIF_NEED_RESCHED,%ebx -+ testl $_TIF_NEED_RESCHED_MASK,%ebx - jnz paranoid_schedule - movl %ebx,%edx /* arg3: thread flags */ - TRACE_IRQS_ON -diff -Nur linux-3.18.14.orig/arch/x86/kernel/irq_32.c linux-3.18.14-rt/arch/x86/kernel/irq_32.c ---- linux-3.18.14.orig/arch/x86/kernel/irq_32.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kernel/irq_32.c 2015-05-31 15:32:46.653635383 -0500 -@@ -142,6 +142,7 @@ - cpu, per_cpu(hardirq_stack, cpu), per_cpu(softirq_stack, cpu)); - } - -+#ifndef CONFIG_PREEMPT_RT_FULL - void do_softirq_own_stack(void) - { - struct thread_info *curstk; -@@ -160,6 +161,7 @@ - - call_on_stack(__do_softirq, isp); - } -+#endif - - bool handle_irq(unsigned irq, struct pt_regs *regs) - { -diff -Nur linux-3.18.14.orig/arch/x86/kernel/process_32.c linux-3.18.14-rt/arch/x86/kernel/process_32.c ---- linux-3.18.14.orig/arch/x86/kernel/process_32.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kernel/process_32.c 2015-05-31 15:32:46.653635383 -0500 -@@ -35,6 +35,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -214,6 +215,35 @@ - } - EXPORT_SYMBOL_GPL(start_thread); - -+#ifdef CONFIG_PREEMPT_RT_FULL -+static void switch_kmaps(struct task_struct *prev_p, struct task_struct *next_p) -+{ -+ int i; -+ -+ /* -+ * Clear @prev's kmap_atomic mappings -+ */ -+ for (i = 0; i < prev_p->kmap_idx; i++) { -+ int idx = i + KM_TYPE_NR * smp_processor_id(); -+ pte_t *ptep = kmap_pte - idx; -+ -+ kpte_clear_flush(ptep, __fix_to_virt(FIX_KMAP_BEGIN + idx)); -+ } -+ /* -+ * Restore @next_p's kmap_atomic mappings -+ */ -+ for (i = 0; i < next_p->kmap_idx; i++) { -+ int idx = i + KM_TYPE_NR * smp_processor_id(); -+ -+ if (!pte_none(next_p->kmap_pte[i])) -+ set_pte(kmap_pte - idx, next_p->kmap_pte[i]); -+ } -+} -+#else -+static inline void -+switch_kmaps(struct task_struct *prev_p, struct task_struct *next_p) { } -+#endif -+ - - /* - * switch_to(x,y) should switch tasks from x to y. -@@ -301,6 +331,8 @@ - task_thread_info(next_p)->flags & _TIF_WORK_CTXSW_NEXT)) - __switch_to_xtra(prev_p, next_p, tss); - -+ switch_kmaps(prev_p, next_p); -+ - /* - * Leave lazy mode, flushing any hypercalls made here. - * This must be done before restoring TLS segments so -diff -Nur linux-3.18.14.orig/arch/x86/kernel/signal.c linux-3.18.14-rt/arch/x86/kernel/signal.c ---- linux-3.18.14.orig/arch/x86/kernel/signal.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kernel/signal.c 2015-05-31 15:32:46.653635383 -0500 -@@ -746,6 +746,14 @@ - mce_notify_process(); - #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ - -+#ifdef ARCH_RT_DELAYS_SIGNAL_SEND -+ if (unlikely(current->forced_info.si_signo)) { -+ struct task_struct *t = current; -+ force_sig_info(t->forced_info.si_signo, &t->forced_info, t); -+ t->forced_info.si_signo = 0; -+ } -+#endif -+ - if (thread_info_flags & _TIF_UPROBE) - uprobe_notify_resume(regs); - -diff -Nur linux-3.18.14.orig/arch/x86/kernel/traps.c linux-3.18.14-rt/arch/x86/kernel/traps.c ---- linux-3.18.14.orig/arch/x86/kernel/traps.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kernel/traps.c 2015-05-31 15:32:46.657635383 -0500 -@@ -87,9 +87,21 @@ - local_irq_enable(); - } - --static inline void preempt_conditional_sti(struct pt_regs *regs) -+static inline void conditional_sti_ist(struct pt_regs *regs) - { -+#ifdef CONFIG_X86_64 -+ /* -+ * X86_64 uses a per CPU stack on the IST for certain traps -+ * like int3. The task can not be preempted when using one -+ * of these stacks, thus preemption must be disabled, otherwise -+ * the stack can be corrupted if the task is scheduled out, -+ * and another task comes in and uses this stack. -+ * -+ * On x86_32 the task keeps its own stack and it is OK if the -+ * task schedules out. -+ */ - preempt_count_inc(); -+#endif - if (regs->flags & X86_EFLAGS_IF) - local_irq_enable(); - } -@@ -100,11 +112,13 @@ - local_irq_disable(); - } - --static inline void preempt_conditional_cli(struct pt_regs *regs) -+static inline void conditional_cli_ist(struct pt_regs *regs) - { - if (regs->flags & X86_EFLAGS_IF) - local_irq_disable(); -+#ifdef CONFIG_X86_64 - preempt_count_dec(); -+#endif - } - - static nokprobe_inline int -@@ -372,9 +386,9 @@ - * as we may switch to the interrupt stack. - */ - debug_stack_usage_inc(); -- preempt_conditional_sti(regs); -+ conditional_sti_ist(regs); - do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL); -- preempt_conditional_cli(regs); -+ conditional_cli_ist(regs); - debug_stack_usage_dec(); - exit: - exception_exit(prev_state); -@@ -517,12 +531,12 @@ - debug_stack_usage_inc(); - - /* It's safe to allow irq's after DR6 has been saved */ -- preempt_conditional_sti(regs); -+ conditional_sti_ist(regs); - - if (regs->flags & X86_VM_MASK) { - handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, - X86_TRAP_DB); -- preempt_conditional_cli(regs); -+ conditional_cli_ist(regs); - debug_stack_usage_dec(); - goto exit; - } -@@ -542,7 +556,7 @@ - si_code = get_si_code(tsk->thread.debugreg6); - if (tsk->thread.debugreg6 & (DR_STEP | DR_TRAP_BITS) || user_icebp) - send_sigtrap(tsk, regs, error_code, si_code); -- preempt_conditional_cli(regs); -+ conditional_cli_ist(regs); - debug_stack_usage_dec(); - - exit: -diff -Nur linux-3.18.14.orig/arch/x86/kvm/lapic.c linux-3.18.14-rt/arch/x86/kvm/lapic.c ---- linux-3.18.14.orig/arch/x86/kvm/lapic.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kvm/lapic.c 2015-05-31 15:32:46.693635383 -0500 -@@ -1034,8 +1034,38 @@ - apic->divide_count); - } - -+ -+static enum hrtimer_restart apic_timer_fn(struct hrtimer *data); -+ -+static void apic_timer_expired(struct hrtimer *data) -+{ -+ int ret, i = 0; -+ enum hrtimer_restart r; -+ struct kvm_timer *ktimer = container_of(data, struct kvm_timer, timer); -+ -+ r = apic_timer_fn(data); -+ -+ if (r == HRTIMER_RESTART) { -+ do { -+ ret = hrtimer_start_expires(data, HRTIMER_MODE_ABS); -+ if (ret == -ETIME) -+ hrtimer_add_expires_ns(&ktimer->timer, -+ ktimer->period); -+ i++; -+ } while (ret == -ETIME && i < 10); -+ -+ if (ret == -ETIME) { -+ printk_once(KERN_ERR "%s: failed to reprogram timer\n", -+ __func__); -+ WARN_ON_ONCE(1); -+ } -+ } -+} -+ -+ - static void start_apic_timer(struct kvm_lapic *apic) - { -+ int ret; - ktime_t now; - atomic_set(&apic->lapic_timer.pending, 0); - -@@ -1065,9 +1095,11 @@ - } - } - -- hrtimer_start(&apic->lapic_timer.timer, -+ ret = hrtimer_start(&apic->lapic_timer.timer, - ktime_add_ns(now, apic->lapic_timer.period), - HRTIMER_MODE_ABS); -+ if (ret == -ETIME) -+ apic_timer_expired(&apic->lapic_timer.timer); - - apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016" - PRIx64 ", " -@@ -1097,8 +1129,10 @@ - ns = (tscdeadline - guest_tsc) * 1000000ULL; - do_div(ns, this_tsc_khz); - } -- hrtimer_start(&apic->lapic_timer.timer, -+ ret = hrtimer_start(&apic->lapic_timer.timer, - ktime_add_ns(now, ns), HRTIMER_MODE_ABS); -+ if (ret == -ETIME) -+ apic_timer_expired(&apic->lapic_timer.timer); - - local_irq_restore(flags); - } -@@ -1539,7 +1573,7 @@ - struct kvm_timer *ktimer = container_of(data, struct kvm_timer, timer); - struct kvm_lapic *apic = container_of(ktimer, struct kvm_lapic, lapic_timer); - struct kvm_vcpu *vcpu = apic->vcpu; -- wait_queue_head_t *q = &vcpu->wq; -+ struct swait_head *q = &vcpu->wq; - - /* - * There is a race window between reading and incrementing, but we do -@@ -1553,8 +1587,8 @@ - kvm_make_request(KVM_REQ_PENDING_TIMER, vcpu); - } - -- if (waitqueue_active(q)) -- wake_up_interruptible(q); -+ if (swaitqueue_active(q)) -+ swait_wake_interruptible(q); - - if (lapic_is_periodic(apic)) { - hrtimer_add_expires_ns(&ktimer->timer, ktimer->period); -@@ -1587,6 +1621,7 @@ - hrtimer_init(&apic->lapic_timer.timer, CLOCK_MONOTONIC, - HRTIMER_MODE_ABS); - apic->lapic_timer.timer.function = apic_timer_fn; -+ apic->lapic_timer.timer.irqsafe = 1; - - /* - * APIC is created enabled. This will prevent kvm_lapic_set_base from -@@ -1707,7 +1742,8 @@ - - timer = &vcpu->arch.apic->lapic_timer.timer; - if (hrtimer_cancel(timer)) -- hrtimer_start_expires(timer, HRTIMER_MODE_ABS); -+ if (hrtimer_start_expires(timer, HRTIMER_MODE_ABS) == -ETIME) -+ apic_timer_expired(timer); - } - - /* -diff -Nur linux-3.18.14.orig/arch/x86/kvm/x86.c linux-3.18.14-rt/arch/x86/kvm/x86.c ---- linux-3.18.14.orig/arch/x86/kvm/x86.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/kvm/x86.c 2015-05-31 15:32:46.697635383 -0500 -@@ -5772,6 +5772,13 @@ - goto out; - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) { -+ printk(KERN_ERR "RT requires X86_FEATURE_CONSTANT_TSC\n"); -+ return -EOPNOTSUPP; -+ } -+#endif -+ - r = kvm_mmu_module_init(); - if (r) - goto out_free_percpu; -diff -Nur linux-3.18.14.orig/arch/x86/mm/fault.c linux-3.18.14-rt/arch/x86/mm/fault.c ---- linux-3.18.14.orig/arch/x86/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/mm/fault.c 2015-05-31 15:32:46.729635382 -0500 -@@ -1128,7 +1128,7 @@ - * If we're in an interrupt, have no user context or are running - * in an atomic region then we must not take the fault: - */ -- if (unlikely(in_atomic() || !mm)) { -+ if (unlikely(!mm || pagefault_disabled())) { - bad_area_nosemaphore(regs, error_code, address); - return; - } -diff -Nur linux-3.18.14.orig/arch/x86/mm/highmem_32.c linux-3.18.14-rt/arch/x86/mm/highmem_32.c ---- linux-3.18.14.orig/arch/x86/mm/highmem_32.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/mm/highmem_32.c 2015-05-31 15:32:46.729635382 -0500 -@@ -32,6 +32,7 @@ - */ - void *kmap_atomic_prot(struct page *page, pgprot_t prot) - { -+ pte_t pte = mk_pte(page, prot); - unsigned long vaddr; - int idx, type; - -@@ -45,7 +46,10 @@ - idx = type + KM_TYPE_NR*smp_processor_id(); - vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); - BUG_ON(!pte_none(*(kmap_pte-idx))); -- set_pte(kmap_pte-idx, mk_pte(page, prot)); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ current->kmap_pte[type] = pte; -+#endif -+ set_pte(kmap_pte-idx, pte); - arch_flush_lazy_mmu_mode(); - - return (void *)vaddr; -@@ -88,6 +92,9 @@ - * is a bad idea also, in case the page changes cacheability - * attributes or becomes a protected page in a hypervisor. - */ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ current->kmap_pte[type] = __pte(0); -+#endif - kpte_clear_flush(kmap_pte-idx, vaddr); - kmap_atomic_idx_pop(); - arch_flush_lazy_mmu_mode(); -diff -Nur linux-3.18.14.orig/arch/x86/mm/iomap_32.c linux-3.18.14-rt/arch/x86/mm/iomap_32.c ---- linux-3.18.14.orig/arch/x86/mm/iomap_32.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/mm/iomap_32.c 2015-05-31 15:32:46.733635383 -0500 -@@ -56,6 +56,7 @@ - - void *kmap_atomic_prot_pfn(unsigned long pfn, pgprot_t prot) - { -+ pte_t pte = pfn_pte(pfn, prot); - unsigned long vaddr; - int idx, type; - -@@ -64,7 +65,12 @@ - type = kmap_atomic_idx_push(); - idx = type + KM_TYPE_NR * smp_processor_id(); - vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); -- set_pte(kmap_pte - idx, pfn_pte(pfn, prot)); -+ WARN_ON(!pte_none(*(kmap_pte - idx))); -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ current->kmap_pte[type] = pte; -+#endif -+ set_pte(kmap_pte - idx, pte); - arch_flush_lazy_mmu_mode(); - - return (void *)vaddr; -@@ -110,6 +116,9 @@ - * is a bad idea also, in case the page changes cacheability - * attributes or becomes a protected page in a hypervisor. - */ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ current->kmap_pte[type] = __pte(0); -+#endif - kpte_clear_flush(kmap_pte-idx, vaddr); - kmap_atomic_idx_pop(); - } -diff -Nur linux-3.18.14.orig/arch/x86/platform/uv/tlb_uv.c linux-3.18.14-rt/arch/x86/platform/uv/tlb_uv.c ---- linux-3.18.14.orig/arch/x86/platform/uv/tlb_uv.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/platform/uv/tlb_uv.c 2015-05-31 15:32:46.733635383 -0500 -@@ -714,9 +714,9 @@ - - quiesce_local_uvhub(hmaster); - -- spin_lock(&hmaster->queue_lock); -+ raw_spin_lock(&hmaster->queue_lock); - reset_with_ipi(&bau_desc->distribution, bcp); -- spin_unlock(&hmaster->queue_lock); -+ raw_spin_unlock(&hmaster->queue_lock); - - end_uvhub_quiesce(hmaster); - -@@ -736,9 +736,9 @@ - - quiesce_local_uvhub(hmaster); - -- spin_lock(&hmaster->queue_lock); -+ raw_spin_lock(&hmaster->queue_lock); - reset_with_ipi(&bau_desc->distribution, bcp); -- spin_unlock(&hmaster->queue_lock); -+ raw_spin_unlock(&hmaster->queue_lock); - - end_uvhub_quiesce(hmaster); - -@@ -759,7 +759,7 @@ - cycles_t tm1; - - hmaster = bcp->uvhub_master; -- spin_lock(&hmaster->disable_lock); -+ raw_spin_lock(&hmaster->disable_lock); - if (!bcp->baudisabled) { - stat->s_bau_disabled++; - tm1 = get_cycles(); -@@ -772,7 +772,7 @@ - } - } - } -- spin_unlock(&hmaster->disable_lock); -+ raw_spin_unlock(&hmaster->disable_lock); - } - - static void count_max_concurr(int stat, struct bau_control *bcp, -@@ -835,7 +835,7 @@ - */ - static void uv1_throttle(struct bau_control *hmaster, struct ptc_stats *stat) - { -- spinlock_t *lock = &hmaster->uvhub_lock; -+ raw_spinlock_t *lock = &hmaster->uvhub_lock; - atomic_t *v; - - v = &hmaster->active_descriptor_count; -@@ -968,7 +968,7 @@ - struct bau_control *hmaster; - - hmaster = bcp->uvhub_master; -- spin_lock(&hmaster->disable_lock); -+ raw_spin_lock(&hmaster->disable_lock); - if (bcp->baudisabled && (get_cycles() >= bcp->set_bau_on_time)) { - stat->s_bau_reenabled++; - for_each_present_cpu(tcpu) { -@@ -980,10 +980,10 @@ - tbcp->period_giveups = 0; - } - } -- spin_unlock(&hmaster->disable_lock); -+ raw_spin_unlock(&hmaster->disable_lock); - return 0; - } -- spin_unlock(&hmaster->disable_lock); -+ raw_spin_unlock(&hmaster->disable_lock); - return -1; - } - -@@ -1899,9 +1899,9 @@ - bcp->cong_reps = congested_reps; - bcp->disabled_period = sec_2_cycles(disabled_period); - bcp->giveup_limit = giveup_limit; -- spin_lock_init(&bcp->queue_lock); -- spin_lock_init(&bcp->uvhub_lock); -- spin_lock_init(&bcp->disable_lock); -+ raw_spin_lock_init(&bcp->queue_lock); -+ raw_spin_lock_init(&bcp->uvhub_lock); -+ raw_spin_lock_init(&bcp->disable_lock); - } - } - -diff -Nur linux-3.18.14.orig/arch/x86/platform/uv/uv_time.c linux-3.18.14-rt/arch/x86/platform/uv/uv_time.c ---- linux-3.18.14.orig/arch/x86/platform/uv/uv_time.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/x86/platform/uv/uv_time.c 2015-05-31 15:32:46.737635383 -0500 -@@ -58,7 +58,7 @@ - - /* There is one of these allocated per node */ - struct uv_rtc_timer_head { -- spinlock_t lock; -+ raw_spinlock_t lock; - /* next cpu waiting for timer, local node relative: */ - int next_cpu; - /* number of cpus on this node: */ -@@ -178,7 +178,7 @@ - uv_rtc_deallocate_timers(); - return -ENOMEM; - } -- spin_lock_init(&head->lock); -+ raw_spin_lock_init(&head->lock); - head->ncpus = uv_blade_nr_possible_cpus(bid); - head->next_cpu = -1; - blade_info[bid] = head; -@@ -232,7 +232,7 @@ - unsigned long flags; - int next_cpu; - -- spin_lock_irqsave(&head->lock, flags); -+ raw_spin_lock_irqsave(&head->lock, flags); - - next_cpu = head->next_cpu; - *t = expires; -@@ -244,12 +244,12 @@ - if (uv_setup_intr(cpu, expires)) { - *t = ULLONG_MAX; - uv_rtc_find_next_timer(head, pnode); -- spin_unlock_irqrestore(&head->lock, flags); -+ raw_spin_unlock_irqrestore(&head->lock, flags); - return -ETIME; - } - } - -- spin_unlock_irqrestore(&head->lock, flags); -+ raw_spin_unlock_irqrestore(&head->lock, flags); - return 0; - } - -@@ -268,7 +268,7 @@ - unsigned long flags; - int rc = 0; - -- spin_lock_irqsave(&head->lock, flags); -+ raw_spin_lock_irqsave(&head->lock, flags); - - if ((head->next_cpu == bcpu && uv_read_rtc(NULL) >= *t) || force) - rc = 1; -@@ -280,7 +280,7 @@ - uv_rtc_find_next_timer(head, pnode); - } - -- spin_unlock_irqrestore(&head->lock, flags); -+ raw_spin_unlock_irqrestore(&head->lock, flags); - - return rc; - } -@@ -300,13 +300,18 @@ - static cycle_t uv_read_rtc(struct clocksource *cs) - { - unsigned long offset; -+ cycle_t cycles; - -+ preempt_disable(); - if (uv_get_min_hub_revision_id() == 1) - offset = 0; - else - offset = (uv_blade_processor_id() * L1_CACHE_BYTES) % PAGE_SIZE; - -- return (cycle_t)uv_read_local_mmr(UVH_RTC | offset); -+ cycles = (cycle_t)uv_read_local_mmr(UVH_RTC | offset); -+ preempt_enable(); -+ -+ return cycles; - } - - /* -diff -Nur linux-3.18.14.orig/arch/xtensa/mm/fault.c linux-3.18.14-rt/arch/xtensa/mm/fault.c ---- linux-3.18.14.orig/arch/xtensa/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/arch/xtensa/mm/fault.c 2015-05-31 15:32:46.741635382 -0500 -@@ -57,7 +57,7 @@ - /* If we're in an interrupt or have no user - * context, we must not take the fault.. - */ -- if (in_atomic() || !mm) { -+ if (!mm || pagefault_disabled()) { - bad_page_fault(regs, address, SIGSEGV); - return; - } -diff -Nur linux-3.18.14.orig/block/blk-core.c linux-3.18.14-rt/block/blk-core.c ---- linux-3.18.14.orig/block/blk-core.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/block/blk-core.c 2015-05-31 15:32:46.757635382 -0500 -@@ -100,6 +100,9 @@ - - INIT_LIST_HEAD(&rq->queuelist); - INIT_LIST_HEAD(&rq->timeout_list); -+#if CONFIG_PREEMPT_RT_FULL -+ INIT_WORK(&rq->work, __blk_mq_complete_request_remote_work); -+#endif - rq->cpu = -1; - rq->q = q; - rq->__sector = (sector_t) -1; -@@ -194,7 +197,7 @@ - **/ - void blk_start_queue(struct request_queue *q) - { -- WARN_ON(!irqs_disabled()); -+ WARN_ON_NONRT(!irqs_disabled()); - - queue_flag_clear(QUEUE_FLAG_STOPPED, q); - __blk_run_queue(q); -@@ -627,7 +630,7 @@ - q->bypass_depth = 1; - __set_bit(QUEUE_FLAG_BYPASS, &q->queue_flags); - -- init_waitqueue_head(&q->mq_freeze_wq); -+ init_swait_head(&q->mq_freeze_wq); - - if (blkcg_init_queue(q)) - goto fail_bdi; -@@ -3037,7 +3040,7 @@ - blk_run_queue_async(q); - else - __blk_run_queue(q); -- spin_unlock(q->queue_lock); -+ spin_unlock_irq(q->queue_lock); - } - - static void flush_plug_callbacks(struct blk_plug *plug, bool from_schedule) -@@ -3085,7 +3088,6 @@ - void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule) - { - struct request_queue *q; -- unsigned long flags; - struct request *rq; - LIST_HEAD(list); - unsigned int depth; -@@ -3105,11 +3107,6 @@ - q = NULL; - depth = 0; - -- /* -- * Save and disable interrupts here, to avoid doing it for every -- * queue lock we have to take. -- */ -- local_irq_save(flags); - while (!list_empty(&list)) { - rq = list_entry_rq(list.next); - list_del_init(&rq->queuelist); -@@ -3122,7 +3119,7 @@ - queue_unplugged(q, depth, from_schedule); - q = rq->q; - depth = 0; -- spin_lock(q->queue_lock); -+ spin_lock_irq(q->queue_lock); - } - - /* -@@ -3149,8 +3146,6 @@ - */ - if (q) - queue_unplugged(q, depth, from_schedule); -- -- local_irq_restore(flags); - } - - void blk_finish_plug(struct blk_plug *plug) -diff -Nur linux-3.18.14.orig/block/blk-ioc.c linux-3.18.14-rt/block/blk-ioc.c ---- linux-3.18.14.orig/block/blk-ioc.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/block/blk-ioc.c 2015-05-31 15:32:46.761635382 -0500 -@@ -7,6 +7,7 @@ - #include - #include - #include -+#include - - #include "blk.h" - -@@ -109,7 +110,7 @@ - spin_unlock(q->queue_lock); - } else { - spin_unlock_irqrestore(&ioc->lock, flags); -- cpu_relax(); -+ cpu_chill(); - spin_lock_irqsave_nested(&ioc->lock, flags, 1); - } - } -@@ -187,7 +188,7 @@ - spin_unlock(icq->q->queue_lock); - } else { - spin_unlock_irqrestore(&ioc->lock, flags); -- cpu_relax(); -+ cpu_chill(); - goto retry; - } - } -diff -Nur linux-3.18.14.orig/block/blk-iopoll.c linux-3.18.14-rt/block/blk-iopoll.c ---- linux-3.18.14.orig/block/blk-iopoll.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/block/blk-iopoll.c 2015-05-31 15:32:46.761635382 -0500 -@@ -35,6 +35,7 @@ - list_add_tail(&iop->list, this_cpu_ptr(&blk_cpu_iopoll)); - __raise_softirq_irqoff(BLOCK_IOPOLL_SOFTIRQ); - local_irq_restore(flags); -+ preempt_check_resched_rt(); - } - EXPORT_SYMBOL(blk_iopoll_sched); - -@@ -132,6 +133,7 @@ - __raise_softirq_irqoff(BLOCK_IOPOLL_SOFTIRQ); - - local_irq_enable(); -+ preempt_check_resched_rt(); - } - - /** -@@ -201,6 +203,7 @@ - this_cpu_ptr(&blk_cpu_iopoll)); - __raise_softirq_irqoff(BLOCK_IOPOLL_SOFTIRQ); - local_irq_enable(); -+ preempt_check_resched_rt(); - } - - return NOTIFY_OK; -diff -Nur linux-3.18.14.orig/block/blk-mq.c linux-3.18.14-rt/block/blk-mq.c ---- linux-3.18.14.orig/block/blk-mq.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/block/blk-mq.c 2015-05-31 15:32:46.789635382 -0500 -@@ -85,7 +85,7 @@ - if (percpu_ref_tryget_live(&q->mq_usage_counter)) - return 0; - -- ret = wait_event_interruptible(q->mq_freeze_wq, -+ ret = swait_event_interruptible(q->mq_freeze_wq, - !q->mq_freeze_depth || blk_queue_dying(q)); - if (blk_queue_dying(q)) - return -ENODEV; -@@ -104,7 +104,7 @@ - struct request_queue *q = - container_of(ref, struct request_queue, mq_usage_counter); - -- wake_up_all(&q->mq_freeze_wq); -+ swait_wake_all(&q->mq_freeze_wq); - } - - static void blk_mq_freeze_queue_start(struct request_queue *q) -@@ -123,7 +123,7 @@ - - static void blk_mq_freeze_queue_wait(struct request_queue *q) - { -- wait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->mq_usage_counter)); -+ swait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->mq_usage_counter)); - } - - /* -@@ -146,7 +146,7 @@ - spin_unlock_irq(q->queue_lock); - if (wake) { - percpu_ref_reinit(&q->mq_usage_counter); -- wake_up_all(&q->mq_freeze_wq); -+ swait_wake_all(&q->mq_freeze_wq); - } - } - -@@ -194,6 +194,9 @@ - rq->resid_len = 0; - rq->sense = NULL; - -+#ifdef CONFIG_PREEMPT_RT_FULL -+ INIT_WORK(&rq->work, __blk_mq_complete_request_remote_work); -+#endif - INIT_LIST_HEAD(&rq->timeout_list); - rq->timeout = 0; - -@@ -313,6 +316,17 @@ - } - EXPORT_SYMBOL(blk_mq_end_request); - -+#ifdef CONFIG_PREEMPT_RT_FULL -+ -+void __blk_mq_complete_request_remote_work(struct work_struct *work) -+{ -+ struct request *rq = container_of(work, struct request, work); -+ -+ rq->q->softirq_done_fn(rq); -+} -+ -+#else -+ - static void __blk_mq_complete_request_remote(void *data) - { - struct request *rq = data; -@@ -320,6 +334,8 @@ - rq->q->softirq_done_fn(rq); - } - -+#endif -+ - static void blk_mq_ipi_complete_request(struct request *rq) - { - struct blk_mq_ctx *ctx = rq->mq_ctx; -@@ -331,19 +347,23 @@ - return; - } - -- cpu = get_cpu(); -+ cpu = get_cpu_light(); - if (!test_bit(QUEUE_FLAG_SAME_FORCE, &rq->q->queue_flags)) - shared = cpus_share_cache(cpu, ctx->cpu); - - if (cpu != ctx->cpu && !shared && cpu_online(ctx->cpu)) { -+#ifdef CONFIG_PREEMPT_RT_FULL -+ schedule_work_on(ctx->cpu, &rq->work); -+#else - rq->csd.func = __blk_mq_complete_request_remote; - rq->csd.info = rq; - rq->csd.flags = 0; - smp_call_function_single_async(ctx->cpu, &rq->csd); -+#endif - } else { - rq->q->softirq_done_fn(rq); - } -- put_cpu(); -+ put_cpu_light(); - } - - void __blk_mq_complete_request(struct request *rq) -@@ -814,9 +834,9 @@ - test_bit(BLK_MQ_S_STOPPED, &hctx->state)) - continue; - -- preempt_disable(); -+ migrate_disable(); - blk_mq_run_hw_queue(hctx, async); -- preempt_enable(); -+ migrate_enable(); - } - } - EXPORT_SYMBOL(blk_mq_run_queues); -@@ -843,9 +863,9 @@ - { - clear_bit(BLK_MQ_S_STOPPED, &hctx->state); - -- preempt_disable(); -+ migrate_disable(); - blk_mq_run_hw_queue(hctx, false); -- preempt_enable(); -+ migrate_enable(); - } - EXPORT_SYMBOL(blk_mq_start_hw_queue); - -@@ -870,9 +890,9 @@ - continue; - - clear_bit(BLK_MQ_S_STOPPED, &hctx->state); -- preempt_disable(); -+ migrate_disable(); - blk_mq_run_hw_queue(hctx, async); -- preempt_enable(); -+ migrate_enable(); - } - } - EXPORT_SYMBOL(blk_mq_start_stopped_hw_queues); -@@ -1494,7 +1514,7 @@ - { - struct blk_mq_hw_ctx *hctx = data; - -- if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) -+ if (action == CPU_POST_DEAD) - return blk_mq_hctx_cpu_offline(hctx, cpu); - else if (action == CPU_ONLINE || action == CPU_ONLINE_FROZEN) - return blk_mq_hctx_cpu_online(hctx, cpu); -diff -Nur linux-3.18.14.orig/block/blk-mq-cpu.c linux-3.18.14-rt/block/blk-mq-cpu.c ---- linux-3.18.14.orig/block/blk-mq-cpu.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/block/blk-mq-cpu.c 2015-05-31 15:32:46.773635382 -0500 -@@ -16,7 +16,7 @@ - #include "blk-mq.h" - - static LIST_HEAD(blk_mq_cpu_notify_list); --static DEFINE_RAW_SPINLOCK(blk_mq_cpu_notify_lock); -+static DEFINE_SPINLOCK(blk_mq_cpu_notify_lock); - - static int blk_mq_main_cpu_notify(struct notifier_block *self, - unsigned long action, void *hcpu) -@@ -25,7 +25,10 @@ - struct blk_mq_cpu_notifier *notify; - int ret = NOTIFY_OK; - -- raw_spin_lock(&blk_mq_cpu_notify_lock); -+ if (action != CPU_POST_DEAD) -+ return NOTIFY_OK; -+ -+ spin_lock(&blk_mq_cpu_notify_lock); - - list_for_each_entry(notify, &blk_mq_cpu_notify_list, list) { - ret = notify->notify(notify->data, action, cpu); -@@ -33,7 +36,7 @@ - break; - } - -- raw_spin_unlock(&blk_mq_cpu_notify_lock); -+ spin_unlock(&blk_mq_cpu_notify_lock); - return ret; - } - -@@ -41,16 +44,16 @@ - { - BUG_ON(!notifier->notify); - -- raw_spin_lock(&blk_mq_cpu_notify_lock); -+ spin_lock(&blk_mq_cpu_notify_lock); - list_add_tail(¬ifier->list, &blk_mq_cpu_notify_list); -- raw_spin_unlock(&blk_mq_cpu_notify_lock); -+ spin_unlock(&blk_mq_cpu_notify_lock); - } - - void blk_mq_unregister_cpu_notifier(struct blk_mq_cpu_notifier *notifier) - { -- raw_spin_lock(&blk_mq_cpu_notify_lock); -+ spin_lock(&blk_mq_cpu_notify_lock); - list_del(¬ifier->list); -- raw_spin_unlock(&blk_mq_cpu_notify_lock); -+ spin_unlock(&blk_mq_cpu_notify_lock); - } - - void blk_mq_init_cpu_notifier(struct blk_mq_cpu_notifier *notifier, -diff -Nur linux-3.18.14.orig/block/blk-mq.h linux-3.18.14-rt/block/blk-mq.h ---- linux-3.18.14.orig/block/blk-mq.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/block/blk-mq.h 2015-05-31 15:32:46.789635382 -0500 -@@ -73,7 +73,10 @@ - static inline struct blk_mq_ctx *__blk_mq_get_ctx(struct request_queue *q, - unsigned int cpu) - { -- return per_cpu_ptr(q->queue_ctx, cpu); -+ struct blk_mq_ctx *ctx; -+ -+ ctx = per_cpu_ptr(q->queue_ctx, cpu); -+ return ctx; - } - - /* -@@ -84,12 +87,12 @@ - */ - static inline struct blk_mq_ctx *blk_mq_get_ctx(struct request_queue *q) - { -- return __blk_mq_get_ctx(q, get_cpu()); -+ return __blk_mq_get_ctx(q, get_cpu_light()); - } - - static inline void blk_mq_put_ctx(struct blk_mq_ctx *ctx) - { -- put_cpu(); -+ put_cpu_light(); - } - - struct blk_mq_alloc_data { -diff -Nur linux-3.18.14.orig/block/blk-softirq.c linux-3.18.14-rt/block/blk-softirq.c ---- linux-3.18.14.orig/block/blk-softirq.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/block/blk-softirq.c 2015-05-31 15:32:46.789635382 -0500 -@@ -51,6 +51,7 @@ - raise_softirq_irqoff(BLOCK_SOFTIRQ); - - local_irq_restore(flags); -+ preempt_check_resched_rt(); - } - - /* -@@ -93,6 +94,7 @@ - this_cpu_ptr(&blk_cpu_done)); - raise_softirq_irqoff(BLOCK_SOFTIRQ); - local_irq_enable(); -+ preempt_check_resched_rt(); - } - - return NOTIFY_OK; -@@ -150,6 +152,7 @@ - goto do_local; - - local_irq_restore(flags); -+ preempt_check_resched_rt(); - } - - /** -diff -Nur linux-3.18.14.orig/block/bounce.c linux-3.18.14-rt/block/bounce.c ---- linux-3.18.14.orig/block/bounce.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/block/bounce.c 2015-05-31 15:32:46.793635382 -0500 -@@ -54,11 +54,11 @@ - unsigned long flags; - unsigned char *vto; - -- local_irq_save(flags); -+ local_irq_save_nort(flags); - vto = kmap_atomic(to->bv_page); - memcpy(vto + to->bv_offset, vfrom, to->bv_len); - kunmap_atomic(vto); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - } - - #else /* CONFIG_HIGHMEM */ -diff -Nur linux-3.18.14.orig/crypto/algapi.c linux-3.18.14-rt/crypto/algapi.c ---- linux-3.18.14.orig/crypto/algapi.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/crypto/algapi.c 2015-05-31 15:32:46.809635382 -0500 -@@ -698,13 +698,13 @@ - - int crypto_register_notifier(struct notifier_block *nb) - { -- return blocking_notifier_chain_register(&crypto_chain, nb); -+ return srcu_notifier_chain_register(&crypto_chain, nb); - } - EXPORT_SYMBOL_GPL(crypto_register_notifier); - - int crypto_unregister_notifier(struct notifier_block *nb) - { -- return blocking_notifier_chain_unregister(&crypto_chain, nb); -+ return srcu_notifier_chain_unregister(&crypto_chain, nb); - } - EXPORT_SYMBOL_GPL(crypto_unregister_notifier); - -diff -Nur linux-3.18.14.orig/crypto/api.c linux-3.18.14-rt/crypto/api.c ---- linux-3.18.14.orig/crypto/api.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/crypto/api.c 2015-05-31 15:32:46.861635382 -0500 -@@ -31,7 +31,7 @@ - DECLARE_RWSEM(crypto_alg_sem); - EXPORT_SYMBOL_GPL(crypto_alg_sem); - --BLOCKING_NOTIFIER_HEAD(crypto_chain); -+SRCU_NOTIFIER_HEAD(crypto_chain); - EXPORT_SYMBOL_GPL(crypto_chain); - - static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg); -@@ -236,10 +236,10 @@ - { - int ok; - -- ok = blocking_notifier_call_chain(&crypto_chain, val, v); -+ ok = srcu_notifier_call_chain(&crypto_chain, val, v); - if (ok == NOTIFY_DONE) { - request_module("cryptomgr"); -- ok = blocking_notifier_call_chain(&crypto_chain, val, v); -+ ok = srcu_notifier_call_chain(&crypto_chain, val, v); - } - - return ok; -diff -Nur linux-3.18.14.orig/crypto/internal.h linux-3.18.14-rt/crypto/internal.h ---- linux-3.18.14.orig/crypto/internal.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/crypto/internal.h 2015-05-31 15:32:46.865635381 -0500 -@@ -48,7 +48,7 @@ - - extern struct list_head crypto_alg_list; - extern struct rw_semaphore crypto_alg_sem; --extern struct blocking_notifier_head crypto_chain; -+extern struct srcu_notifier_head crypto_chain; - - #ifdef CONFIG_PROC_FS - void __init crypto_init_proc(void); -@@ -142,7 +142,7 @@ - - static inline void crypto_notify(unsigned long val, void *v) - { -- blocking_notifier_call_chain(&crypto_chain, val, v); -+ srcu_notifier_call_chain(&crypto_chain, val, v); - } - - #endif /* _CRYPTO_INTERNAL_H */ -diff -Nur linux-3.18.14.orig/Documentation/hwlat_detector.txt linux-3.18.14-rt/Documentation/hwlat_detector.txt ---- linux-3.18.14.orig/Documentation/hwlat_detector.txt 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/Documentation/hwlat_detector.txt 2015-05-31 15:32:45.457635394 -0500 -@@ -0,0 +1,64 @@ -+Introduction: -+------------- -+ -+The module hwlat_detector is a special purpose kernel module that is used to -+detect large system latencies induced by the behavior of certain underlying -+hardware or firmware, independent of Linux itself. The code was developed -+originally to detect SMIs (System Management Interrupts) on x86 systems, -+however there is nothing x86 specific about this patchset. It was -+originally written for use by the "RT" patch since the Real Time -+kernel is highly latency sensitive. -+ -+SMIs are usually not serviced by the Linux kernel, which typically does not -+even know that they are occuring. SMIs are instead are set up by BIOS code -+and are serviced by BIOS code, usually for "critical" events such as -+management of thermal sensors and fans. Sometimes though, SMIs are used for -+other tasks and those tasks can spend an inordinate amount of time in the -+handler (sometimes measured in milliseconds). Obviously this is a problem if -+you are trying to keep event service latencies down in the microsecond range. -+ -+The hardware latency detector works by hogging all of the cpus for configurable -+amounts of time (by calling stop_machine()), polling the CPU Time Stamp Counter -+for some period, then looking for gaps in the TSC data. Any gap indicates a -+time when the polling was interrupted and since the machine is stopped and -+interrupts turned off the only thing that could do that would be an SMI. -+ -+Note that the SMI detector should *NEVER* be used in a production environment. -+It is intended to be run manually to determine if the hardware platform has a -+problem with long system firmware service routines. -+ -+Usage: -+------ -+ -+Loading the module hwlat_detector passing the parameter "enabled=1" (or by -+setting the "enable" entry in "hwlat_detector" debugfs toggled on) is the only -+step required to start the hwlat_detector. It is possible to redefine the -+threshold in microseconds (us) above which latency spikes will be taken -+into account (parameter "threshold="). -+ -+Example: -+ -+ # modprobe hwlat_detector enabled=1 threshold=100 -+ -+After the module is loaded, it creates a directory named "hwlat_detector" under -+the debugfs mountpoint, "/debug/hwlat_detector" for this text. It is necessary -+to have debugfs mounted, which might be on /sys/debug on your system. -+ -+The /debug/hwlat_detector interface contains the following files: -+ -+count - number of latency spikes observed since last reset -+enable - a global enable/disable toggle (0/1), resets count -+max - maximum hardware latency actually observed (usecs) -+sample - a pipe from which to read current raw sample data -+ in the format -+ (can be opened O_NONBLOCK for a single sample) -+threshold - minimum latency value to be considered (usecs) -+width - time period to sample with CPUs held (usecs) -+ must be less than the total window size (enforced) -+window - total period of sampling, width being inside (usecs) -+ -+By default we will set width to 500,000 and window to 1,000,000, meaning that -+we will sample every 1,000,000 usecs (1s) for 500,000 usecs (0.5s). If we -+observe any latencies that exceed the threshold (initially 100 usecs), -+then we write to a global sample ring buffer of 8K samples, which is -+consumed by reading from the "sample" (pipe) debugfs file interface. -diff -Nur linux-3.18.14.orig/Documentation/sysrq.txt linux-3.18.14-rt/Documentation/sysrq.txt ---- linux-3.18.14.orig/Documentation/sysrq.txt 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/Documentation/sysrq.txt 2015-05-31 15:32:45.461635394 -0500 -@@ -59,10 +59,17 @@ - On other - If you know of the key combos for other architectures, please - let me know so I can add them to this section. - --On all - write a character to /proc/sysrq-trigger. e.g.: -- -+On all - write a character to /proc/sysrq-trigger, e.g.: - echo t > /proc/sysrq-trigger - -+On all - Enable network SysRq by writing a cookie to icmp_echo_sysrq, e.g. -+ echo 0x01020304 >/proc/sys/net/ipv4/icmp_echo_sysrq -+ Send an ICMP echo request with this pattern plus the particular -+ SysRq command key. Example: -+ # ping -c1 -s57 -p0102030468 -+ will trigger the SysRq-H (help) command. -+ -+ - * What are the 'command' keys? - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 'b' - Will immediately reboot the system without syncing or unmounting -diff -Nur linux-3.18.14.orig/Documentation/trace/histograms.txt linux-3.18.14-rt/Documentation/trace/histograms.txt ---- linux-3.18.14.orig/Documentation/trace/histograms.txt 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/Documentation/trace/histograms.txt 2015-05-31 15:32:45.461635394 -0500 -@@ -0,0 +1,186 @@ -+ Using the Linux Kernel Latency Histograms -+ -+ -+This document gives a short explanation how to enable, configure and use -+latency histograms. Latency histograms are primarily relevant in the -+context of real-time enabled kernels (CONFIG_PREEMPT/CONFIG_PREEMPT_RT) -+and are used in the quality management of the Linux real-time -+capabilities. -+ -+ -+* Purpose of latency histograms -+ -+A latency histogram continuously accumulates the frequencies of latency -+data. There are two types of histograms -+- potential sources of latencies -+- effective latencies -+ -+ -+* Potential sources of latencies -+ -+Potential sources of latencies are code segments where interrupts, -+preemption or both are disabled (aka critical sections). To create -+histograms of potential sources of latency, the kernel stores the time -+stamp at the start of a critical section, determines the time elapsed -+when the end of the section is reached, and increments the frequency -+counter of that latency value - irrespective of whether any concurrently -+running process is affected by latency or not. -+- Configuration items (in the Kernel hacking/Tracers submenu) -+ CONFIG_INTERRUPT_OFF_LATENCY -+ CONFIG_PREEMPT_OFF_LATENCY -+ -+ -+* Effective latencies -+ -+Effective latencies are actually occuring during wakeup of a process. To -+determine effective latencies, the kernel stores the time stamp when a -+process is scheduled to be woken up, and determines the duration of the -+wakeup time shortly before control is passed over to this process. Note -+that the apparent latency in user space may be somewhat longer, since the -+process may be interrupted after control is passed over to it but before -+the execution in user space takes place. Simply measuring the interval -+between enqueuing and wakeup may also not appropriate in cases when a -+process is scheduled as a result of a timer expiration. The timer may have -+missed its deadline, e.g. due to disabled interrupts, but this latency -+would not be registered. Therefore, the offsets of missed timers are -+recorded in a separate histogram. If both wakeup latency and missed timer -+offsets are configured and enabled, a third histogram may be enabled that -+records the overall latency as a sum of the timer latency, if any, and the -+wakeup latency. This histogram is called "timerandwakeup". -+- Configuration items (in the Kernel hacking/Tracers submenu) -+ CONFIG_WAKEUP_LATENCY -+ CONFIG_MISSED_TIMER_OFSETS -+ -+ -+* Usage -+ -+The interface to the administration of the latency histograms is located -+in the debugfs file system. To mount it, either enter -+ -+mount -t sysfs nodev /sys -+mount -t debugfs nodev /sys/kernel/debug -+ -+from shell command line level, or add -+ -+nodev /sys sysfs defaults 0 0 -+nodev /sys/kernel/debug debugfs defaults 0 0 -+ -+to the file /etc/fstab. All latency histogram related files are then -+available in the directory /sys/kernel/debug/tracing/latency_hist. A -+particular histogram type is enabled by writing non-zero to the related -+variable in the /sys/kernel/debug/tracing/latency_hist/enable directory. -+Select "preemptirqsoff" for the histograms of potential sources of -+latencies and "wakeup" for histograms of effective latencies etc. The -+histogram data - one per CPU - are available in the files -+ -+/sys/kernel/debug/tracing/latency_hist/preemptoff/CPUx -+/sys/kernel/debug/tracing/latency_hist/irqsoff/CPUx -+/sys/kernel/debug/tracing/latency_hist/preemptirqsoff/CPUx -+/sys/kernel/debug/tracing/latency_hist/wakeup/CPUx -+/sys/kernel/debug/tracing/latency_hist/wakeup/sharedprio/CPUx -+/sys/kernel/debug/tracing/latency_hist/missed_timer_offsets/CPUx -+/sys/kernel/debug/tracing/latency_hist/timerandwakeup/CPUx -+ -+The histograms are reset by writing non-zero to the file "reset" in a -+particular latency directory. To reset all latency data, use -+ -+#!/bin/sh -+ -+TRACINGDIR=/sys/kernel/debug/tracing -+HISTDIR=$TRACINGDIR/latency_hist -+ -+if test -d $HISTDIR -+then -+ cd $HISTDIR -+ for i in `find . | grep /reset$` -+ do -+ echo 1 >$i -+ done -+fi -+ -+ -+* Data format -+ -+Latency data are stored with a resolution of one microsecond. The -+maximum latency is 10,240 microseconds. The data are only valid, if the -+overflow register is empty. Every output line contains the latency in -+microseconds in the first row and the number of samples in the second -+row. To display only lines with a positive latency count, use, for -+example, -+ -+grep -v " 0$" /sys/kernel/debug/tracing/latency_hist/preemptoff/CPU0 -+ -+#Minimum latency: 0 microseconds. -+#Average latency: 0 microseconds. -+#Maximum latency: 25 microseconds. -+#Total samples: 3104770694 -+#There are 0 samples greater or equal than 10240 microseconds -+#usecs samples -+ 0 2984486876 -+ 1 49843506 -+ 2 58219047 -+ 3 5348126 -+ 4 2187960 -+ 5 3388262 -+ 6 959289 -+ 7 208294 -+ 8 40420 -+ 9 4485 -+ 10 14918 -+ 11 18340 -+ 12 25052 -+ 13 19455 -+ 14 5602 -+ 15 969 -+ 16 47 -+ 17 18 -+ 18 14 -+ 19 1 -+ 20 3 -+ 21 2 -+ 22 5 -+ 23 2 -+ 25 1 -+ -+ -+* Wakeup latency of a selected process -+ -+To only collect wakeup latency data of a particular process, write the -+PID of the requested process to -+ -+/sys/kernel/debug/tracing/latency_hist/wakeup/pid -+ -+PIDs are not considered, if this variable is set to 0. -+ -+ -+* Details of the process with the highest wakeup latency so far -+ -+Selected data of the process that suffered from the highest wakeup -+latency that occurred in a particular CPU are available in the file -+ -+/sys/kernel/debug/tracing/latency_hist/wakeup/max_latency-CPUx. -+ -+In addition, other relevant system data at the time when the -+latency occurred are given. -+ -+The format of the data is (all in one line): -+ () \ -+<- -+ -+The value of is only relevant in the combined timer -+and wakeup latency recording. In the wakeup recording, it is -+always 0, in the missed_timer_offsets recording, it is the same -+as . -+ -+When retrospectively searching for the origin of a latency and -+tracing was not enabled, it may be helpful to know the name and -+some basic data of the task that (finally) was switching to the -+late real-tlme task. In addition to the victim's data, also the -+data of the possible culprit are therefore displayed after the -+"<-" symbol. -+ -+Finally, the timestamp of the time when the latency occurred -+in . after the most recent system boot -+is provided. -+ -+These data are also reset when the wakeup histogram is reset. -diff -Nur linux-3.18.14.orig/drivers/acpi/acpica/acglobal.h linux-3.18.14-rt/drivers/acpi/acpica/acglobal.h ---- linux-3.18.14.orig/drivers/acpi/acpica/acglobal.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/acpi/acpica/acglobal.h 2015-05-31 15:32:46.885635381 -0500 -@@ -112,7 +112,7 @@ - * interrupt level - */ - ACPI_GLOBAL(acpi_spinlock, acpi_gbl_gpe_lock); /* For GPE data structs and registers */ --ACPI_GLOBAL(acpi_spinlock, acpi_gbl_hardware_lock); /* For ACPI H/W except GPE registers */ -+ACPI_GLOBAL(acpi_raw_spinlock, acpi_gbl_hardware_lock); /* For ACPI H/W except GPE registers */ - ACPI_GLOBAL(acpi_spinlock, acpi_gbl_reference_count_lock); - - /* Mutex for _OSI support */ -diff -Nur linux-3.18.14.orig/drivers/acpi/acpica/hwregs.c linux-3.18.14-rt/drivers/acpi/acpica/hwregs.c ---- linux-3.18.14.orig/drivers/acpi/acpica/hwregs.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/acpi/acpica/hwregs.c 2015-05-31 15:32:46.929635381 -0500 -@@ -269,14 +269,14 @@ - ACPI_BITMASK_ALL_FIXED_STATUS, - ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address))); - -- lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); -+ raw_spin_lock_irqsave(acpi_gbl_hardware_lock, lock_flags); - - /* Clear the fixed events in PM1 A/B */ - - status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, - ACPI_BITMASK_ALL_FIXED_STATUS); - -- acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); -+ raw_spin_unlock_irqrestore(acpi_gbl_hardware_lock, lock_flags); - - if (ACPI_FAILURE(status)) { - goto exit; -diff -Nur linux-3.18.14.orig/drivers/acpi/acpica/hwxface.c linux-3.18.14-rt/drivers/acpi/acpica/hwxface.c ---- linux-3.18.14.orig/drivers/acpi/acpica/hwxface.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/acpi/acpica/hwxface.c 2015-05-31 15:32:46.973635380 -0500 -@@ -374,7 +374,7 @@ - return_ACPI_STATUS(AE_BAD_PARAMETER); - } - -- lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); -+ raw_spin_lock_irqsave(acpi_gbl_hardware_lock, lock_flags); - - /* - * At this point, we know that the parent register is one of the -@@ -435,7 +435,7 @@ - - unlock_and_exit: - -- acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); -+ raw_spin_unlock_irqrestore(acpi_gbl_hardware_lock, lock_flags); - return_ACPI_STATUS(status); - } - -diff -Nur linux-3.18.14.orig/drivers/acpi/acpica/utmutex.c linux-3.18.14-rt/drivers/acpi/acpica/utmutex.c ---- linux-3.18.14.orig/drivers/acpi/acpica/utmutex.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/acpi/acpica/utmutex.c 2015-05-31 15:32:46.973635380 -0500 -@@ -88,7 +88,7 @@ - return_ACPI_STATUS (status); - } - -- status = acpi_os_create_lock (&acpi_gbl_hardware_lock); -+ status = acpi_os_create_raw_lock (&acpi_gbl_hardware_lock); - if (ACPI_FAILURE (status)) { - return_ACPI_STATUS (status); - } -@@ -141,7 +141,7 @@ - /* Delete the spinlocks */ - - acpi_os_delete_lock(acpi_gbl_gpe_lock); -- acpi_os_delete_lock(acpi_gbl_hardware_lock); -+ acpi_os_delete_raw_lock(acpi_gbl_hardware_lock); - acpi_os_delete_lock(acpi_gbl_reference_count_lock); - - /* Delete the reader/writer lock */ -diff -Nur linux-3.18.14.orig/drivers/ata/libata-sff.c linux-3.18.14-rt/drivers/ata/libata-sff.c ---- linux-3.18.14.orig/drivers/ata/libata-sff.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/ata/libata-sff.c 2015-05-31 15:32:46.993635380 -0500 -@@ -678,9 +678,9 @@ - unsigned long flags; - unsigned int consumed; - -- local_irq_save(flags); -+ local_irq_save_nort(flags); - consumed = ata_sff_data_xfer32(dev, buf, buflen, rw); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - return consumed; - } -@@ -719,7 +719,7 @@ - unsigned long flags; - - /* FIXME: use a bounce buffer */ -- local_irq_save(flags); -+ local_irq_save_nort(flags); - buf = kmap_atomic(page); - - /* do the actual data transfer */ -@@ -727,7 +727,7 @@ - do_write); - - kunmap_atomic(buf); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - } else { - buf = page_address(page); - ap->ops->sff_data_xfer(qc->dev, buf + offset, qc->sect_size, -@@ -864,7 +864,7 @@ - unsigned long flags; - - /* FIXME: use bounce buffer */ -- local_irq_save(flags); -+ local_irq_save_nort(flags); - buf = kmap_atomic(page); - - /* do the actual data transfer */ -@@ -872,7 +872,7 @@ - count, rw); - - kunmap_atomic(buf); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - } else { - buf = page_address(page); - consumed = ap->ops->sff_data_xfer(dev, buf + offset, -diff -Nur linux-3.18.14.orig/drivers/char/random.c linux-3.18.14-rt/drivers/char/random.c ---- linux-3.18.14.orig/drivers/char/random.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/char/random.c 2015-05-31 15:32:47.013635380 -0500 -@@ -776,8 +776,6 @@ - } sample; - long delta, delta2, delta3; - -- preempt_disable(); -- - sample.jiffies = jiffies; - sample.cycles = random_get_entropy(); - sample.num = num; -@@ -818,7 +816,6 @@ - */ - credit_entropy_bits(r, min_t(int, fls(delta>>1), 11)); - } -- preempt_enable(); - } - - void add_input_randomness(unsigned int type, unsigned int code, -@@ -871,28 +868,27 @@ - return *(ptr + f->reg_idx++); - } - --void add_interrupt_randomness(int irq, int irq_flags) -+void add_interrupt_randomness(int irq, int irq_flags, __u64 ip) - { - struct entropy_store *r; - struct fast_pool *fast_pool = this_cpu_ptr(&irq_randomness); -- struct pt_regs *regs = get_irq_regs(); - unsigned long now = jiffies; - cycles_t cycles = random_get_entropy(); - __u32 c_high, j_high; -- __u64 ip; - unsigned long seed; - int credit = 0; - - if (cycles == 0) -- cycles = get_reg(fast_pool, regs); -+ cycles = get_reg(fast_pool, NULL); - c_high = (sizeof(cycles) > 4) ? cycles >> 32 : 0; - j_high = (sizeof(now) > 4) ? now >> 32 : 0; - fast_pool->pool[0] ^= cycles ^ j_high ^ irq; - fast_pool->pool[1] ^= now ^ c_high; -- ip = regs ? instruction_pointer(regs) : _RET_IP_; -+ if (!ip) -+ ip = _RET_IP_; - fast_pool->pool[2] ^= ip; - fast_pool->pool[3] ^= (sizeof(ip) > 4) ? ip >> 32 : -- get_reg(fast_pool, regs); -+ get_reg(fast_pool, NULL); - - fast_mix(fast_pool); - add_interrupt_bench(cycles); -diff -Nur linux-3.18.14.orig/drivers/clocksource/tcb_clksrc.c linux-3.18.14-rt/drivers/clocksource/tcb_clksrc.c ---- linux-3.18.14.orig/drivers/clocksource/tcb_clksrc.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/clocksource/tcb_clksrc.c 2015-05-31 15:32:47.025635380 -0500 -@@ -23,8 +23,7 @@ - * this 32 bit free-running counter. the second channel is not used. - * - * - The third channel may be used to provide a 16-bit clockevent -- * source, used in either periodic or oneshot mode. This runs -- * at 32 KiHZ, and can handle delays of up to two seconds. -+ * source, used in either periodic or oneshot mode. - * - * A boot clocksource and clockevent source are also currently needed, - * unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so -@@ -74,6 +73,7 @@ - struct tc_clkevt_device { - struct clock_event_device clkevt; - struct clk *clk; -+ u32 freq; - void __iomem *regs; - }; - -@@ -82,13 +82,6 @@ - return container_of(clkevt, struct tc_clkevt_device, clkevt); - } - --/* For now, we always use the 32K clock ... this optimizes for NO_HZ, -- * because using one of the divided clocks would usually mean the -- * tick rate can never be less than several dozen Hz (vs 0.5 Hz). -- * -- * A divided clock could be good for high resolution timers, since -- * 30.5 usec resolution can seem "low". -- */ - static u32 timer_clock; - - static void tc_mode(enum clock_event_mode m, struct clock_event_device *d) -@@ -111,11 +104,12 @@ - case CLOCK_EVT_MODE_PERIODIC: - clk_enable(tcd->clk); - -- /* slow clock, count up to RC, then irq and restart */ -+ /* count up to RC, then irq and restart */ - __raw_writel(timer_clock - | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, - regs + ATMEL_TC_REG(2, CMR)); -- __raw_writel((32768 + HZ/2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); -+ __raw_writel((tcd->freq + HZ / 2) / HZ, -+ tcaddr + ATMEL_TC_REG(2, RC)); - - /* Enable clock and interrupts on RC compare */ - __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); -@@ -128,7 +122,7 @@ - case CLOCK_EVT_MODE_ONESHOT: - clk_enable(tcd->clk); - -- /* slow clock, count up to RC, then irq and stop */ -+ /* count up to RC, then irq and stop */ - __raw_writel(timer_clock | ATMEL_TC_CPCSTOP - | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, - regs + ATMEL_TC_REG(2, CMR)); -@@ -157,8 +151,12 @@ - .name = "tc_clkevt", - .features = CLOCK_EVT_FEAT_PERIODIC - | CLOCK_EVT_FEAT_ONESHOT, -+#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK - /* Should be lower than at91rm9200's system timer */ - .rating = 125, -+#else -+ .rating = 200, -+#endif - .set_next_event = tc_next_event, - .set_mode = tc_mode, - }, -@@ -178,8 +176,9 @@ - return IRQ_NONE; - } - --static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) -+static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) - { -+ unsigned divisor = atmel_tc_divisors[divisor_idx]; - int ret; - struct clk *t2_clk = tc->clk[2]; - int irq = tc->irq[2]; -@@ -193,7 +192,11 @@ - clkevt.regs = tc->regs; - clkevt.clk = t2_clk; - -- timer_clock = clk32k_divisor_idx; -+ timer_clock = divisor_idx; -+ if (!divisor) -+ clkevt.freq = 32768; -+ else -+ clkevt.freq = clk_get_rate(t2_clk) / divisor; - - clkevt.clkevt.cpumask = cpumask_of(0); - -@@ -203,7 +206,7 @@ - return ret; - } - -- clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); -+ clockevents_config_and_register(&clkevt.clkevt, clkevt.freq, 1, 0xffff); - - return ret; - } -@@ -340,7 +343,11 @@ - goto err_disable_t1; - - /* channel 2: periodic and oneshot timer support */ -+#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK - ret = setup_clkevents(tc, clk32k_divisor_idx); -+#else -+ ret = setup_clkevents(tc, best_divisor_idx); -+#endif - if (ret) - goto err_unregister_clksrc; - -diff -Nur linux-3.18.14.orig/drivers/clocksource/timer-atmel-pit.c linux-3.18.14-rt/drivers/clocksource/timer-atmel-pit.c ---- linux-3.18.14.orig/drivers/clocksource/timer-atmel-pit.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/clocksource/timer-atmel-pit.c 2015-05-31 15:32:47.025635380 -0500 -@@ -90,6 +90,7 @@ - return elapsed; - } - -+static struct irqaction at91sam926x_pit_irq; - /* - * Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16) - */ -@@ -100,6 +101,8 @@ - - switch (mode) { - case CLOCK_EVT_MODE_PERIODIC: -+ /* Set up irq handler */ -+ setup_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq); - /* update clocksource counter */ - data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); - pit_write(data->base, AT91_PIT_MR, -@@ -113,6 +116,7 @@ - /* disable irq, leaving the clocksource active */ - pit_write(data->base, AT91_PIT_MR, - (data->cycle - 1) | AT91_PIT_PITEN); -+ remove_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq); - break; - case CLOCK_EVT_MODE_RESUME: - break; -diff -Nur linux-3.18.14.orig/drivers/cpufreq/Kconfig.x86 linux-3.18.14-rt/drivers/cpufreq/Kconfig.x86 ---- linux-3.18.14.orig/drivers/cpufreq/Kconfig.x86 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/cpufreq/Kconfig.x86 2015-05-31 15:32:47.065635380 -0500 -@@ -113,7 +113,7 @@ - - config X86_POWERNOW_K8 - tristate "AMD Opteron/Athlon64 PowerNow!" -- depends on ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ -+ depends on ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ && !PREEMPT_RT_BASE - help - This adds the CPUFreq driver for K8/early Opteron/Athlon64 processors. - Support for K10 and newer processors is now in acpi-cpufreq. -diff -Nur linux-3.18.14.orig/drivers/gpio/gpio-omap.c linux-3.18.14-rt/drivers/gpio/gpio-omap.c ---- linux-3.18.14.orig/drivers/gpio/gpio-omap.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/gpio/gpio-omap.c 2015-05-31 15:32:47.073635379 -0500 -@@ -57,7 +57,7 @@ - u32 saved_datain; - u32 level_mask; - u32 toggle_mask; -- spinlock_t lock; -+ raw_spinlock_t lock; - struct gpio_chip chip; - struct clk *dbck; - u32 mod_usage; -@@ -503,19 +503,19 @@ - (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))) - return -EINVAL; - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - offset = GPIO_INDEX(bank, gpio); - retval = omap_set_gpio_triggering(bank, offset, type); - if (!LINE_USED(bank->mod_usage, offset)) { - omap_enable_gpio_module(bank, offset); - omap_set_gpio_direction(bank, offset, 1); - } else if (!omap_gpio_is_input(bank, BIT(offset))) { -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - return -EINVAL; - } - - bank->irq_usage |= BIT(GPIO_INDEX(bank, gpio)); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - - if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) - __irq_set_handler_locked(d->irq, handle_level_irq); -@@ -633,14 +633,14 @@ - return -EINVAL; - } - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - if (enable) - bank->context.wake_en |= gpio_bit; - else - bank->context.wake_en &= ~gpio_bit; - - writel_relaxed(bank->context.wake_en, bank->base + bank->regs->wkup_en); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - - return 0; - } -@@ -675,7 +675,7 @@ - if (!BANK_USED(bank)) - pm_runtime_get_sync(bank->dev); - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - /* Set trigger to none. You need to enable the desired trigger with - * request_irq() or set_irq_type(). Only do this if the IRQ line has - * not already been requested. -@@ -685,7 +685,7 @@ - omap_enable_gpio_module(bank, offset); - } - bank->mod_usage |= BIT(offset); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - - return 0; - } -@@ -695,11 +695,11 @@ - struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); - unsigned long flags; - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - bank->mod_usage &= ~(BIT(offset)); - omap_disable_gpio_module(bank, offset); - omap_reset_gpio(bank, bank->chip.base + offset); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - - /* - * If this is the last gpio to be freed in the bank, -@@ -799,12 +799,12 @@ - unsigned long flags; - unsigned offset = GPIO_INDEX(bank, gpio); - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - gpio_unlock_as_irq(&bank->chip, offset); - bank->irq_usage &= ~(BIT(offset)); - omap_disable_gpio_module(bank, offset); - omap_reset_gpio(bank, gpio); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - - /* - * If this is the last IRQ to be freed in the bank, -@@ -828,10 +828,10 @@ - unsigned int gpio = omap_irq_to_gpio(bank, d->hwirq); - unsigned long flags; - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - omap_set_gpio_irqenable(bank, gpio, 0); - omap_set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), IRQ_TYPE_NONE); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - } - - static void omap_gpio_unmask_irq(struct irq_data *d) -@@ -842,7 +842,7 @@ - u32 trigger = irqd_get_trigger_type(d); - unsigned long flags; - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - if (trigger) - omap_set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), trigger); - -@@ -854,7 +854,7 @@ - } - - omap_set_gpio_irqenable(bank, gpio, 1); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - } - - /*---------------------------------------------------------------------*/ -@@ -867,9 +867,9 @@ - OMAP_MPUIO_GPIO_MASKIT / bank->stride; - unsigned long flags; - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - writel_relaxed(0xffff & ~bank->context.wake_en, mask_reg); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - - return 0; - } -@@ -882,9 +882,9 @@ - OMAP_MPUIO_GPIO_MASKIT / bank->stride; - unsigned long flags; - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - writel_relaxed(bank->context.wake_en, mask_reg); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - - return 0; - } -@@ -930,9 +930,9 @@ - - bank = container_of(chip, struct gpio_bank, chip); - reg = bank->base + bank->regs->direction; -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - dir = !!(readl_relaxed(reg) & BIT(offset)); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - return dir; - } - -@@ -942,9 +942,9 @@ - unsigned long flags; - - bank = container_of(chip, struct gpio_bank, chip); -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - omap_set_gpio_direction(bank, offset, 1); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - return 0; - } - -@@ -968,10 +968,10 @@ - unsigned long flags; - - bank = container_of(chip, struct gpio_bank, chip); -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - bank->set_dataout(bank, offset, value); - omap_set_gpio_direction(bank, offset, 0); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - return 0; - } - -@@ -983,9 +983,9 @@ - - bank = container_of(chip, struct gpio_bank, chip); - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - omap2_set_gpio_debounce(bank, offset, debounce); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - - return 0; - } -@@ -996,9 +996,9 @@ - unsigned long flags; - - bank = container_of(chip, struct gpio_bank, chip); -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - bank->set_dataout(bank, offset, value); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - } - - /*---------------------------------------------------------------------*/ -@@ -1223,7 +1223,7 @@ - else - bank->set_dataout = omap_set_gpio_dataout_mask; - -- spin_lock_init(&bank->lock); -+ raw_spin_lock_init(&bank->lock); - - /* Static mapping, never released */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -@@ -1270,7 +1270,7 @@ - unsigned long flags; - u32 wake_low, wake_hi; - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - - /* - * Only edges can generate a wakeup event to the PRCM. -@@ -1323,7 +1323,7 @@ - bank->get_context_loss_count(bank->dev); - - omap_gpio_dbck_disable(bank); -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - - return 0; - } -@@ -1338,7 +1338,7 @@ - unsigned long flags; - int c; - -- spin_lock_irqsave(&bank->lock, flags); -+ raw_spin_lock_irqsave(&bank->lock, flags); - - /* - * On the first resume during the probe, the context has not -@@ -1374,14 +1374,14 @@ - if (c != bank->context_loss_count) { - omap_gpio_restore_context(bank); - } else { -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - return 0; - } - } - } - - if (!bank->workaround_enabled) { -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - return 0; - } - -@@ -1436,7 +1436,7 @@ - } - - bank->workaround_enabled = false; -- spin_unlock_irqrestore(&bank->lock, flags); -+ raw_spin_unlock_irqrestore(&bank->lock, flags); - - return 0; - } -diff -Nur linux-3.18.14.orig/drivers/gpu/drm/i915/i915_gem.c linux-3.18.14-rt/drivers/gpu/drm/i915/i915_gem.c ---- linux-3.18.14.orig/drivers/gpu/drm/i915/i915_gem.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/gpu/drm/i915/i915_gem.c 2015-05-31 15:32:47.081635379 -0500 -@@ -5144,7 +5144,7 @@ - if (!mutex_is_locked(mutex)) - return false; - --#if defined(CONFIG_SMP) && !defined(CONFIG_DEBUG_MUTEXES) -+#if defined(CONFIG_SMP) && !defined(CONFIG_DEBUG_MUTEXES) && !defined(CONFIG_PREEMPT_RT_BASE) - return mutex->owner == task; - #else - /* Since UP may be pre-empted, we cannot assume that we own the lock */ -diff -Nur linux-3.18.14.orig/drivers/gpu/drm/i915/i915_gem_execbuffer.c linux-3.18.14-rt/drivers/gpu/drm/i915/i915_gem_execbuffer.c ---- linux-3.18.14.orig/drivers/gpu/drm/i915/i915_gem_execbuffer.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/gpu/drm/i915/i915_gem_execbuffer.c 2015-05-31 15:32:47.121635379 -0500 -@@ -1170,7 +1170,9 @@ - return ret; - } - -+#ifndef CONFIG_PREEMPT_RT_BASE - trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags); -+#endif - - i915_gem_execbuffer_move_to_active(vmas, ring); - i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj); -diff -Nur linux-3.18.14.orig/drivers/i2c/busses/i2c-omap.c linux-3.18.14-rt/drivers/i2c/busses/i2c-omap.c ---- linux-3.18.14.orig/drivers/i2c/busses/i2c-omap.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/i2c/busses/i2c-omap.c 2015-05-31 15:32:47.125635379 -0500 -@@ -875,15 +875,12 @@ - u16 mask; - u16 stat; - -- spin_lock(&dev->lock); -- mask = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); - stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); -+ mask = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); - - if (stat & mask) - ret = IRQ_WAKE_THREAD; - -- spin_unlock(&dev->lock); -- - return ret; - } - -diff -Nur linux-3.18.14.orig/drivers/ide/alim15x3.c linux-3.18.14-rt/drivers/ide/alim15x3.c ---- linux-3.18.14.orig/drivers/ide/alim15x3.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/ide/alim15x3.c 2015-05-31 15:32:47.137635379 -0500 -@@ -234,7 +234,7 @@ - - isa_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); - -- local_irq_save(flags); -+ local_irq_save_nort(flags); - - if (m5229_revision < 0xC2) { - /* -@@ -325,7 +325,7 @@ - } - pci_dev_put(north); - pci_dev_put(isa_dev); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - return 0; - } - -diff -Nur linux-3.18.14.orig/drivers/ide/hpt366.c linux-3.18.14-rt/drivers/ide/hpt366.c ---- linux-3.18.14.orig/drivers/ide/hpt366.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/ide/hpt366.c 2015-05-31 15:32:47.169635379 -0500 -@@ -1241,7 +1241,7 @@ - - dma_old = inb(base + 2); - -- local_irq_save(flags); -+ local_irq_save_nort(flags); - - dma_new = dma_old; - pci_read_config_byte(dev, hwif->channel ? 0x4b : 0x43, &masterdma); -@@ -1252,7 +1252,7 @@ - if (dma_new != dma_old) - outb(dma_new, base + 2); - -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx\n", - hwif->name, base, base + 7); -diff -Nur linux-3.18.14.orig/drivers/ide/ide-io.c linux-3.18.14-rt/drivers/ide/ide-io.c ---- linux-3.18.14.orig/drivers/ide/ide-io.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/ide/ide-io.c 2015-05-31 15:32:47.169635379 -0500 -@@ -659,7 +659,7 @@ - /* disable_irq_nosync ?? */ - disable_irq(hwif->irq); - /* local CPU only, as if we were handling an interrupt */ -- local_irq_disable(); -+ local_irq_disable_nort(); - if (hwif->polling) { - startstop = handler(drive); - } else if (drive_is_ready(drive)) { -diff -Nur linux-3.18.14.orig/drivers/ide/ide-iops.c linux-3.18.14-rt/drivers/ide/ide-iops.c ---- linux-3.18.14.orig/drivers/ide/ide-iops.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/ide/ide-iops.c 2015-05-31 15:32:47.185635379 -0500 -@@ -129,12 +129,12 @@ - if ((stat & ATA_BUSY) == 0) - break; - -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - *rstat = stat; - return -EBUSY; - } - } -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - } - /* - * Allow status to settle, then read it again. -diff -Nur linux-3.18.14.orig/drivers/ide/ide-io-std.c linux-3.18.14-rt/drivers/ide/ide-io-std.c ---- linux-3.18.14.orig/drivers/ide/ide-io-std.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/ide/ide-io-std.c 2015-05-31 15:32:47.169635379 -0500 -@@ -175,7 +175,7 @@ - unsigned long uninitialized_var(flags); - - if ((io_32bit & 2) && !mmio) { -- local_irq_save(flags); -+ local_irq_save_nort(flags); - ata_vlb_sync(io_ports->nsect_addr); - } - -@@ -186,7 +186,7 @@ - insl(data_addr, buf, words); - - if ((io_32bit & 2) && !mmio) -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - if (((len + 1) & 3) < 2) - return; -@@ -219,7 +219,7 @@ - unsigned long uninitialized_var(flags); - - if ((io_32bit & 2) && !mmio) { -- local_irq_save(flags); -+ local_irq_save_nort(flags); - ata_vlb_sync(io_ports->nsect_addr); - } - -@@ -230,7 +230,7 @@ - outsl(data_addr, buf, words); - - if ((io_32bit & 2) && !mmio) -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - if (((len + 1) & 3) < 2) - return; -diff -Nur linux-3.18.14.orig/drivers/ide/ide-probe.c linux-3.18.14-rt/drivers/ide/ide-probe.c ---- linux-3.18.14.orig/drivers/ide/ide-probe.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/ide/ide-probe.c 2015-05-31 15:32:47.185635379 -0500 -@@ -196,10 +196,10 @@ - int bswap = 1; - - /* local CPU only; some systems need this */ -- local_irq_save(flags); -+ local_irq_save_nort(flags); - /* read 512 bytes of id info */ - hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - drive->dev_flags |= IDE_DFLAG_ID_READ; - #ifdef DEBUG -diff -Nur linux-3.18.14.orig/drivers/ide/ide-taskfile.c linux-3.18.14-rt/drivers/ide/ide-taskfile.c ---- linux-3.18.14.orig/drivers/ide/ide-taskfile.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/ide/ide-taskfile.c 2015-05-31 15:32:47.185635379 -0500 -@@ -250,7 +250,7 @@ - - page_is_high = PageHighMem(page); - if (page_is_high) -- local_irq_save(flags); -+ local_irq_save_nort(flags); - - buf = kmap_atomic(page) + offset; - -@@ -271,7 +271,7 @@ - kunmap_atomic(buf); - - if (page_is_high) -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - len -= nr_bytes; - } -@@ -414,7 +414,7 @@ - } - - if ((drive->dev_flags & IDE_DFLAG_UNMASK) == 0) -- local_irq_disable(); -+ local_irq_disable_nort(); - - ide_set_handler(drive, &task_pio_intr, WAIT_WORSTCASE); - -diff -Nur linux-3.18.14.orig/drivers/infiniband/ulp/ipoib/ipoib_multicast.c linux-3.18.14-rt/drivers/infiniband/ulp/ipoib/ipoib_multicast.c ---- linux-3.18.14.orig/drivers/infiniband/ulp/ipoib/ipoib_multicast.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/infiniband/ulp/ipoib/ipoib_multicast.c 2015-05-31 15:32:47.205635378 -0500 -@@ -796,7 +796,7 @@ - - ipoib_mcast_stop_thread(dev, 0); - -- local_irq_save(flags); -+ local_irq_save_nort(flags); - netif_addr_lock(dev); - spin_lock(&priv->lock); - -@@ -878,7 +878,7 @@ - - spin_unlock(&priv->lock); - netif_addr_unlock(dev); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - /* We have to cancel outside of the spinlock */ - list_for_each_entry_safe(mcast, tmcast, &remove_list, list) { -diff -Nur linux-3.18.14.orig/drivers/input/gameport/gameport.c linux-3.18.14-rt/drivers/input/gameport/gameport.c ---- linux-3.18.14.orig/drivers/input/gameport/gameport.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/input/gameport/gameport.c 2015-05-31 15:32:47.225635378 -0500 -@@ -124,12 +124,12 @@ - tx = 1 << 30; - - for(i = 0; i < 50; i++) { -- local_irq_save(flags); -+ local_irq_save_nort(flags); - GET_TIME(t1); - for (t = 0; t < 50; t++) gameport_read(gameport); - GET_TIME(t2); - GET_TIME(t3); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - udelay(i * 10); - if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; - } -@@ -148,11 +148,11 @@ - tx = 1 << 30; - - for(i = 0; i < 50; i++) { -- local_irq_save(flags); -+ local_irq_save_nort(flags); - rdtscl(t1); - for (t = 0; t < 50; t++) gameport_read(gameport); - rdtscl(t2); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - udelay(i * 10); - if (t2 - t1 < tx) tx = t2 - t1; - } -diff -Nur linux-3.18.14.orig/drivers/leds/trigger/Kconfig linux-3.18.14-rt/drivers/leds/trigger/Kconfig ---- linux-3.18.14.orig/drivers/leds/trigger/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/leds/trigger/Kconfig 2015-05-31 15:32:47.229635378 -0500 -@@ -61,7 +61,7 @@ - - config LEDS_TRIGGER_CPU - bool "LED CPU Trigger" -- depends on LEDS_TRIGGERS -+ depends on LEDS_TRIGGERS && !PREEMPT_RT_BASE - help - This allows LEDs to be controlled by active CPUs. This shows - the active CPUs across an array of LEDs so you can see which -diff -Nur linux-3.18.14.orig/drivers/md/bcache/Kconfig linux-3.18.14-rt/drivers/md/bcache/Kconfig ---- linux-3.18.14.orig/drivers/md/bcache/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/md/bcache/Kconfig 2015-05-31 15:32:47.245635378 -0500 -@@ -1,6 +1,7 @@ - - config BCACHE - tristate "Block device as cache" -+ depends on !PREEMPT_RT_FULL - ---help--- - Allows a block device to be used as cache for other devices; uses - a btree for indexing and the layout is optimized for SSDs. -diff -Nur linux-3.18.14.orig/drivers/md/dm.c linux-3.18.14-rt/drivers/md/dm.c ---- linux-3.18.14.orig/drivers/md/dm.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/md/dm.c 2015-05-31 15:32:47.261635378 -0500 -@@ -1898,14 +1898,14 @@ - if (map_request(ti, clone, md)) - goto requeued; - -- BUG_ON(!irqs_disabled()); -+ BUG_ON_NONRT(!irqs_disabled()); - spin_lock(q->queue_lock); - } - - goto out; - - requeued: -- BUG_ON(!irqs_disabled()); -+ BUG_ON_NONRT(!irqs_disabled()); - spin_lock(q->queue_lock); - - delay_and_out: -diff -Nur linux-3.18.14.orig/drivers/md/raid5.c linux-3.18.14-rt/drivers/md/raid5.c ---- linux-3.18.14.orig/drivers/md/raid5.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/md/raid5.c 2015-05-31 15:32:47.265635378 -0500 -@@ -1649,8 +1649,9 @@ - struct raid5_percpu *percpu; - unsigned long cpu; - -- cpu = get_cpu(); -+ cpu = get_cpu_light(); - percpu = per_cpu_ptr(conf->percpu, cpu); -+ spin_lock(&percpu->lock); - if (test_bit(STRIPE_OP_BIOFILL, &ops_request)) { - ops_run_biofill(sh); - overlap_clear++; -@@ -1702,7 +1703,8 @@ - if (test_and_clear_bit(R5_Overlap, &dev->flags)) - wake_up(&sh->raid_conf->wait_for_overlap); - } -- put_cpu(); -+ spin_unlock(&percpu->lock); -+ put_cpu_light(); - } - - static int grow_one_stripe(struct r5conf *conf, int hash) -@@ -5708,6 +5710,7 @@ - __func__, cpu); - break; - } -+ spin_lock_init(&per_cpu_ptr(conf->percpu, cpu)->lock); - } - put_online_cpus(); - -diff -Nur linux-3.18.14.orig/drivers/md/raid5.h linux-3.18.14-rt/drivers/md/raid5.h ---- linux-3.18.14.orig/drivers/md/raid5.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/md/raid5.h 2015-05-31 15:32:47.293635378 -0500 -@@ -457,6 +457,7 @@ - int recovery_disabled; - /* per cpu variables */ - struct raid5_percpu { -+ spinlock_t lock; /* Protection for -RT */ - struct page *spare_page; /* Used when checking P/Q in raid6 */ - void *scribble; /* space for constructing buffer - * lists and performing address -diff -Nur linux-3.18.14.orig/drivers/misc/hwlat_detector.c linux-3.18.14-rt/drivers/misc/hwlat_detector.c ---- linux-3.18.14.orig/drivers/misc/hwlat_detector.c 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/drivers/misc/hwlat_detector.c 2015-05-31 15:32:47.377635377 -0500 -@@ -0,0 +1,1240 @@ -+/* -+ * hwlat_detector.c - A simple Hardware Latency detector. -+ * -+ * Use this module to detect large system latencies induced by the behavior of -+ * certain underlying system hardware or firmware, independent of Linux itself. -+ * The code was developed originally to detect the presence of SMIs on Intel -+ * and AMD systems, although there is no dependency upon x86 herein. -+ * -+ * The classical example usage of this module is in detecting the presence of -+ * SMIs or System Management Interrupts on Intel and AMD systems. An SMI is a -+ * somewhat special form of hardware interrupt spawned from earlier CPU debug -+ * modes in which the (BIOS/EFI/etc.) firmware arranges for the South Bridge -+ * LPC (or other device) to generate a special interrupt under certain -+ * circumstances, for example, upon expiration of a special SMI timer device, -+ * due to certain external thermal readings, on certain I/O address accesses, -+ * and other situations. An SMI hits a special CPU pin, triggers a special -+ * SMI mode (complete with special memory map), and the OS is unaware. -+ * -+ * Although certain hardware-inducing latencies are necessary (for example, -+ * a modern system often requires an SMI handler for correct thermal control -+ * and remote management) they can wreak havoc upon any OS-level performance -+ * guarantees toward low-latency, especially when the OS is not even made -+ * aware of the presence of these interrupts. For this reason, we need a -+ * somewhat brute force mechanism to detect these interrupts. In this case, -+ * we do it by hogging all of the CPU(s) for configurable timer intervals, -+ * sampling the built-in CPU timer, looking for discontiguous readings. -+ * -+ * WARNING: This implementation necessarily introduces latencies. Therefore, -+ * you should NEVER use this module in a production environment -+ * requiring any kind of low-latency performance guarantee(s). -+ * -+ * Copyright (C) 2008-2009 Jon Masters, Red Hat, Inc. -+ * -+ * Includes useful feedback from Clark Williams -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define BUF_SIZE_DEFAULT 262144UL /* 8K*(sizeof(entry)) */ -+#define BUF_FLAGS (RB_FL_OVERWRITE) /* no block on full */ -+#define U64STR_SIZE 22 /* 20 digits max */ -+ -+#define VERSION "1.0.0" -+#define BANNER "hwlat_detector: " -+#define DRVNAME "hwlat_detector" -+#define DEFAULT_SAMPLE_WINDOW 1000000 /* 1s */ -+#define DEFAULT_SAMPLE_WIDTH 500000 /* 0.5s */ -+#define DEFAULT_LAT_THRESHOLD 10 /* 10us */ -+ -+/* Module metadata */ -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Jon Masters "); -+MODULE_DESCRIPTION("A simple hardware latency detector"); -+MODULE_VERSION(VERSION); -+ -+/* Module parameters */ -+ -+static int debug; -+static int enabled; -+static int threshold; -+ -+module_param(debug, int, 0); /* enable debug */ -+module_param(enabled, int, 0); /* enable detector */ -+module_param(threshold, int, 0); /* latency threshold */ -+ -+/* Buffering and sampling */ -+ -+static struct ring_buffer *ring_buffer; /* sample buffer */ -+static DEFINE_MUTEX(ring_buffer_mutex); /* lock changes */ -+static unsigned long buf_size = BUF_SIZE_DEFAULT; -+static struct task_struct *kthread; /* sampling thread */ -+ -+/* DebugFS filesystem entries */ -+ -+static struct dentry *debug_dir; /* debugfs directory */ -+static struct dentry *debug_max; /* maximum TSC delta */ -+static struct dentry *debug_count; /* total detect count */ -+static struct dentry *debug_sample_width; /* sample width us */ -+static struct dentry *debug_sample_window; /* sample window us */ -+static struct dentry *debug_sample; /* raw samples us */ -+static struct dentry *debug_threshold; /* threshold us */ -+static struct dentry *debug_enable; /* enable/disable */ -+ -+/* Individual samples and global state */ -+ -+struct sample; /* latency sample */ -+struct data; /* Global state */ -+ -+/* Sampling functions */ -+static int __buffer_add_sample(struct sample *sample); -+static struct sample *buffer_get_sample(struct sample *sample); -+ -+/* Threading and state */ -+static int kthread_fn(void *unused); -+static int start_kthread(void); -+static int stop_kthread(void); -+static void __reset_stats(void); -+static int init_stats(void); -+ -+/* Debugfs interface */ -+static ssize_t simple_data_read(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos, const u64 *entry); -+static ssize_t simple_data_write(struct file *filp, const char __user *ubuf, -+ size_t cnt, loff_t *ppos, u64 *entry); -+static int debug_sample_fopen(struct inode *inode, struct file *filp); -+static ssize_t debug_sample_fread(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos); -+static int debug_sample_release(struct inode *inode, struct file *filp); -+static int debug_enable_fopen(struct inode *inode, struct file *filp); -+static ssize_t debug_enable_fread(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos); -+static ssize_t debug_enable_fwrite(struct file *file, -+ const char __user *user_buffer, -+ size_t user_size, loff_t *offset); -+ -+/* Initialization functions */ -+static int init_debugfs(void); -+static void free_debugfs(void); -+static int detector_init(void); -+static void detector_exit(void); -+ -+/* Individual latency samples are stored here when detected and packed into -+ * the ring_buffer circular buffer, where they are overwritten when -+ * more than buf_size/sizeof(sample) samples are received. */ -+struct sample { -+ u64 seqnum; /* unique sequence */ -+ u64 duration; /* ktime delta */ -+ u64 outer_duration; /* ktime delta (outer loop) */ -+ struct timespec timestamp; /* wall time */ -+ unsigned long lost; -+}; -+ -+/* keep the global state somewhere. */ -+static struct data { -+ -+ struct mutex lock; /* protect changes */ -+ -+ u64 count; /* total since reset */ -+ u64 max_sample; /* max hardware latency */ -+ u64 threshold; /* sample threshold level */ -+ -+ u64 sample_window; /* total sampling window (on+off) */ -+ u64 sample_width; /* active sampling portion of window */ -+ -+ atomic_t sample_open; /* whether the sample file is open */ -+ -+ wait_queue_head_t wq; /* waitqeue for new sample values */ -+ -+} data; -+ -+/** -+ * __buffer_add_sample - add a new latency sample recording to the ring buffer -+ * @sample: The new latency sample value -+ * -+ * This receives a new latency sample and records it in a global ring buffer. -+ * No additional locking is used in this case. -+ */ -+static int __buffer_add_sample(struct sample *sample) -+{ -+ return ring_buffer_write(ring_buffer, -+ sizeof(struct sample), sample); -+} -+ -+/** -+ * buffer_get_sample - remove a hardware latency sample from the ring buffer -+ * @sample: Pre-allocated storage for the sample -+ * -+ * This retrieves a hardware latency sample from the global circular buffer -+ */ -+static struct sample *buffer_get_sample(struct sample *sample) -+{ -+ struct ring_buffer_event *e = NULL; -+ struct sample *s = NULL; -+ unsigned int cpu = 0; -+ -+ if (!sample) -+ return NULL; -+ -+ mutex_lock(&ring_buffer_mutex); -+ for_each_online_cpu(cpu) { -+ e = ring_buffer_consume(ring_buffer, cpu, NULL, &sample->lost); -+ if (e) -+ break; -+ } -+ -+ if (e) { -+ s = ring_buffer_event_data(e); -+ memcpy(sample, s, sizeof(struct sample)); -+ } else -+ sample = NULL; -+ mutex_unlock(&ring_buffer_mutex); -+ -+ return sample; -+} -+ -+#ifndef CONFIG_TRACING -+#define time_type ktime_t -+#define time_get() ktime_get() -+#define time_to_us(x) ktime_to_us(x) -+#define time_sub(a, b) ktime_sub(a, b) -+#define init_time(a, b) (a).tv64 = b -+#define time_u64(a) ((a).tv64) -+#else -+#define time_type u64 -+#define time_get() trace_clock_local() -+#define time_to_us(x) div_u64(x, 1000) -+#define time_sub(a, b) ((a) - (b)) -+#define init_time(a, b) (a = b) -+#define time_u64(a) a -+#endif -+/** -+ * get_sample - sample the CPU TSC and look for likely hardware latencies -+ * -+ * Used to repeatedly capture the CPU TSC (or similar), looking for potential -+ * hardware-induced latency. Called with interrupts disabled and with -+ * data.lock held. -+ */ -+static int get_sample(void) -+{ -+ time_type start, t1, t2, last_t2; -+ s64 diff, total = 0; -+ u64 sample = 0; -+ u64 outer_sample = 0; -+ int ret = -1; -+ -+ init_time(last_t2, 0); -+ start = time_get(); /* start timestamp */ -+ -+ do { -+ -+ t1 = time_get(); /* we'll look for a discontinuity */ -+ t2 = time_get(); -+ -+ if (time_u64(last_t2)) { -+ /* Check the delta from outer loop (t2 to next t1) */ -+ diff = time_to_us(time_sub(t1, last_t2)); -+ /* This shouldn't happen */ -+ if (diff < 0) { -+ pr_err(BANNER "time running backwards\n"); -+ goto out; -+ } -+ if (diff > outer_sample) -+ outer_sample = diff; -+ } -+ last_t2 = t2; -+ -+ total = time_to_us(time_sub(t2, start)); /* sample width */ -+ -+ /* This checks the inner loop (t1 to t2) */ -+ diff = time_to_us(time_sub(t2, t1)); /* current diff */ -+ -+ /* This shouldn't happen */ -+ if (diff < 0) { -+ pr_err(BANNER "time running backwards\n"); -+ goto out; -+ } -+ -+ if (diff > sample) -+ sample = diff; /* only want highest value */ -+ -+ } while (total <= data.sample_width); -+ -+ ret = 0; -+ -+ /* If we exceed the threshold value, we have found a hardware latency */ -+ if (sample > data.threshold || outer_sample > data.threshold) { -+ struct sample s; -+ -+ ret = 1; -+ -+ data.count++; -+ s.seqnum = data.count; -+ s.duration = sample; -+ s.outer_duration = outer_sample; -+ s.timestamp = CURRENT_TIME; -+ __buffer_add_sample(&s); -+ -+ /* Keep a running maximum ever recorded hardware latency */ -+ if (sample > data.max_sample) -+ data.max_sample = sample; -+ } -+ -+out: -+ return ret; -+} -+ -+/* -+ * kthread_fn - The CPU time sampling/hardware latency detection kernel thread -+ * @unused: A required part of the kthread API. -+ * -+ * Used to periodically sample the CPU TSC via a call to get_sample. We -+ * disable interrupts, which does (intentionally) introduce latency since we -+ * need to ensure nothing else might be running (and thus pre-empting). -+ * Obviously this should never be used in production environments. -+ * -+ * Currently this runs on which ever CPU it was scheduled on, but most -+ * real-worald hardware latency situations occur across several CPUs, -+ * but we might later generalize this if we find there are any actualy -+ * systems with alternate SMI delivery or other hardware latencies. -+ */ -+static int kthread_fn(void *unused) -+{ -+ int ret; -+ u64 interval; -+ -+ while (!kthread_should_stop()) { -+ -+ mutex_lock(&data.lock); -+ -+ local_irq_disable(); -+ ret = get_sample(); -+ local_irq_enable(); -+ -+ if (ret > 0) -+ wake_up(&data.wq); /* wake up reader(s) */ -+ -+ interval = data.sample_window - data.sample_width; -+ do_div(interval, USEC_PER_MSEC); /* modifies interval value */ -+ -+ mutex_unlock(&data.lock); -+ -+ if (msleep_interruptible(interval)) -+ break; -+ } -+ -+ return 0; -+} -+ -+/** -+ * start_kthread - Kick off the hardware latency sampling/detector kthread -+ * -+ * This starts a kernel thread that will sit and sample the CPU timestamp -+ * counter (TSC or similar) and look for potential hardware latencies. -+ */ -+static int start_kthread(void) -+{ -+ kthread = kthread_run(kthread_fn, NULL, -+ DRVNAME); -+ if (IS_ERR(kthread)) { -+ pr_err(BANNER "could not start sampling thread\n"); -+ enabled = 0; -+ return -ENOMEM; -+ } -+ -+ return 0; -+} -+ -+/** -+ * stop_kthread - Inform the hardware latency samping/detector kthread to stop -+ * -+ * This kicks the running hardware latency sampling/detector kernel thread and -+ * tells it to stop sampling now. Use this on unload and at system shutdown. -+ */ -+static int stop_kthread(void) -+{ -+ int ret; -+ -+ ret = kthread_stop(kthread); -+ -+ return ret; -+} -+ -+/** -+ * __reset_stats - Reset statistics for the hardware latency detector -+ * -+ * We use data to store various statistics and global state. We call this -+ * function in order to reset those when "enable" is toggled on or off, and -+ * also at initialization. Should be called with data.lock held. -+ */ -+static void __reset_stats(void) -+{ -+ data.count = 0; -+ data.max_sample = 0; -+ ring_buffer_reset(ring_buffer); /* flush out old sample entries */ -+} -+ -+/** -+ * init_stats - Setup global state statistics for the hardware latency detector -+ * -+ * We use data to store various statistics and global state. We also use -+ * a global ring buffer (ring_buffer) to keep raw samples of detected hardware -+ * induced system latencies. This function initializes these structures and -+ * allocates the global ring buffer also. -+ */ -+static int init_stats(void) -+{ -+ int ret = -ENOMEM; -+ -+ mutex_init(&data.lock); -+ init_waitqueue_head(&data.wq); -+ atomic_set(&data.sample_open, 0); -+ -+ ring_buffer = ring_buffer_alloc(buf_size, BUF_FLAGS); -+ -+ if (WARN(!ring_buffer, KERN_ERR BANNER -+ "failed to allocate ring buffer!\n")) -+ goto out; -+ -+ __reset_stats(); -+ data.threshold = threshold ?: DEFAULT_LAT_THRESHOLD; /* threshold us */ -+ data.sample_window = DEFAULT_SAMPLE_WINDOW; /* window us */ -+ data.sample_width = DEFAULT_SAMPLE_WIDTH; /* width us */ -+ -+ ret = 0; -+ -+out: -+ return ret; -+ -+} -+ -+/* -+ * simple_data_read - Wrapper read function for global state debugfs entries -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The userspace provided buffer to read value into -+ * @cnt: The maximum number of bytes to read -+ * @ppos: The current "file" position -+ * @entry: The entry to read from -+ * -+ * This function provides a generic read implementation for the global state -+ * "data" structure debugfs filesystem entries. It would be nice to use -+ * simple_attr_read directly, but we need to make sure that the data.lock -+ * is held during the actual read. -+ */ -+static ssize_t simple_data_read(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos, const u64 *entry) -+{ -+ char buf[U64STR_SIZE]; -+ u64 val = 0; -+ int len = 0; -+ -+ memset(buf, 0, sizeof(buf)); -+ -+ if (!entry) -+ return -EFAULT; -+ -+ mutex_lock(&data.lock); -+ val = *entry; -+ mutex_unlock(&data.lock); -+ -+ len = snprintf(buf, sizeof(buf), "%llu\n", (unsigned long long)val); -+ -+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, len); -+ -+} -+ -+/* -+ * simple_data_write - Wrapper write function for global state debugfs entries -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The userspace provided buffer to write value from -+ * @cnt: The maximum number of bytes to write -+ * @ppos: The current "file" position -+ * @entry: The entry to write to -+ * -+ * This function provides a generic write implementation for the global state -+ * "data" structure debugfs filesystem entries. It would be nice to use -+ * simple_attr_write directly, but we need to make sure that the data.lock -+ * is held during the actual write. -+ */ -+static ssize_t simple_data_write(struct file *filp, const char __user *ubuf, -+ size_t cnt, loff_t *ppos, u64 *entry) -+{ -+ char buf[U64STR_SIZE]; -+ int csize = min(cnt, sizeof(buf)); -+ u64 val = 0; -+ int err = 0; -+ -+ memset(buf, '\0', sizeof(buf)); -+ if (copy_from_user(buf, ubuf, csize)) -+ return -EFAULT; -+ -+ buf[U64STR_SIZE-1] = '\0'; /* just in case */ -+ err = kstrtoull(buf, 10, &val); -+ if (err) -+ return -EINVAL; -+ -+ mutex_lock(&data.lock); -+ *entry = val; -+ mutex_unlock(&data.lock); -+ -+ return csize; -+} -+ -+/** -+ * debug_count_fopen - Open function for "count" debugfs entry -+ * @inode: The in-kernel inode representation of the debugfs "file" -+ * @filp: The active open file structure for the debugfs "file" -+ * -+ * This function provides an open implementation for the "count" debugfs -+ * interface to the hardware latency detector. -+ */ -+static int debug_count_fopen(struct inode *inode, struct file *filp) -+{ -+ return 0; -+} -+ -+/** -+ * debug_count_fread - Read function for "count" debugfs entry -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The userspace provided buffer to read value into -+ * @cnt: The maximum number of bytes to read -+ * @ppos: The current "file" position -+ * -+ * This function provides a read implementation for the "count" debugfs -+ * interface to the hardware latency detector. Can be used to read the -+ * number of latency readings exceeding the configured threshold since -+ * the detector was last reset (e.g. by writing a zero into "count"). -+ */ -+static ssize_t debug_count_fread(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ return simple_data_read(filp, ubuf, cnt, ppos, &data.count); -+} -+ -+/** -+ * debug_count_fwrite - Write function for "count" debugfs entry -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The user buffer that contains the value to write -+ * @cnt: The maximum number of bytes to write to "file" -+ * @ppos: The current position in the debugfs "file" -+ * -+ * This function provides a write implementation for the "count" debugfs -+ * interface to the hardware latency detector. Can be used to write a -+ * desired value, especially to zero the total count. -+ */ -+static ssize_t debug_count_fwrite(struct file *filp, -+ const char __user *ubuf, -+ size_t cnt, -+ loff_t *ppos) -+{ -+ return simple_data_write(filp, ubuf, cnt, ppos, &data.count); -+} -+ -+/** -+ * debug_enable_fopen - Dummy open function for "enable" debugfs interface -+ * @inode: The in-kernel inode representation of the debugfs "file" -+ * @filp: The active open file structure for the debugfs "file" -+ * -+ * This function provides an open implementation for the "enable" debugfs -+ * interface to the hardware latency detector. -+ */ -+static int debug_enable_fopen(struct inode *inode, struct file *filp) -+{ -+ return 0; -+} -+ -+/** -+ * debug_enable_fread - Read function for "enable" debugfs interface -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The userspace provided buffer to read value into -+ * @cnt: The maximum number of bytes to read -+ * @ppos: The current "file" position -+ * -+ * This function provides a read implementation for the "enable" debugfs -+ * interface to the hardware latency detector. Can be used to determine -+ * whether the detector is currently enabled ("0\n" or "1\n" returned). -+ */ -+static ssize_t debug_enable_fread(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ char buf[4]; -+ -+ if ((cnt < sizeof(buf)) || (*ppos)) -+ return 0; -+ -+ buf[0] = enabled ? '1' : '0'; -+ buf[1] = '\n'; -+ buf[2] = '\0'; -+ if (copy_to_user(ubuf, buf, strlen(buf))) -+ return -EFAULT; -+ return *ppos = strlen(buf); -+} -+ -+/** -+ * debug_enable_fwrite - Write function for "enable" debugfs interface -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The user buffer that contains the value to write -+ * @cnt: The maximum number of bytes to write to "file" -+ * @ppos: The current position in the debugfs "file" -+ * -+ * This function provides a write implementation for the "enable" debugfs -+ * interface to the hardware latency detector. Can be used to enable or -+ * disable the detector, which will have the side-effect of possibly -+ * also resetting the global stats and kicking off the measuring -+ * kthread (on an enable) or the converse (upon a disable). -+ */ -+static ssize_t debug_enable_fwrite(struct file *filp, -+ const char __user *ubuf, -+ size_t cnt, -+ loff_t *ppos) -+{ -+ char buf[4]; -+ int csize = min(cnt, sizeof(buf)); -+ long val = 0; -+ int err = 0; -+ -+ memset(buf, '\0', sizeof(buf)); -+ if (copy_from_user(buf, ubuf, csize)) -+ return -EFAULT; -+ -+ buf[sizeof(buf)-1] = '\0'; /* just in case */ -+ err = kstrtoul(buf, 10, &val); -+ if (0 != err) -+ return -EINVAL; -+ -+ if (val) { -+ if (enabled) -+ goto unlock; -+ enabled = 1; -+ __reset_stats(); -+ if (start_kthread()) -+ return -EFAULT; -+ } else { -+ if (!enabled) -+ goto unlock; -+ enabled = 0; -+ err = stop_kthread(); -+ if (err) { -+ pr_err(BANNER "cannot stop kthread\n"); -+ return -EFAULT; -+ } -+ wake_up(&data.wq); /* reader(s) should return */ -+ } -+unlock: -+ return csize; -+} -+ -+/** -+ * debug_max_fopen - Open function for "max" debugfs entry -+ * @inode: The in-kernel inode representation of the debugfs "file" -+ * @filp: The active open file structure for the debugfs "file" -+ * -+ * This function provides an open implementation for the "max" debugfs -+ * interface to the hardware latency detector. -+ */ -+static int debug_max_fopen(struct inode *inode, struct file *filp) -+{ -+ return 0; -+} -+ -+/** -+ * debug_max_fread - Read function for "max" debugfs entry -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The userspace provided buffer to read value into -+ * @cnt: The maximum number of bytes to read -+ * @ppos: The current "file" position -+ * -+ * This function provides a read implementation for the "max" debugfs -+ * interface to the hardware latency detector. Can be used to determine -+ * the maximum latency value observed since it was last reset. -+ */ -+static ssize_t debug_max_fread(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ return simple_data_read(filp, ubuf, cnt, ppos, &data.max_sample); -+} -+ -+/** -+ * debug_max_fwrite - Write function for "max" debugfs entry -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The user buffer that contains the value to write -+ * @cnt: The maximum number of bytes to write to "file" -+ * @ppos: The current position in the debugfs "file" -+ * -+ * This function provides a write implementation for the "max" debugfs -+ * interface to the hardware latency detector. Can be used to reset the -+ * maximum or set it to some other desired value - if, then, subsequent -+ * measurements exceed this value, the maximum will be updated. -+ */ -+static ssize_t debug_max_fwrite(struct file *filp, -+ const char __user *ubuf, -+ size_t cnt, -+ loff_t *ppos) -+{ -+ return simple_data_write(filp, ubuf, cnt, ppos, &data.max_sample); -+} -+ -+ -+/** -+ * debug_sample_fopen - An open function for "sample" debugfs interface -+ * @inode: The in-kernel inode representation of this debugfs "file" -+ * @filp: The active open file structure for the debugfs "file" -+ * -+ * This function handles opening the "sample" file within the hardware -+ * latency detector debugfs directory interface. This file is used to read -+ * raw samples from the global ring_buffer and allows the user to see a -+ * running latency history. Can be opened blocking or non-blocking, -+ * affecting whether it behaves as a buffer read pipe, or does not. -+ * Implements simple locking to prevent multiple simultaneous use. -+ */ -+static int debug_sample_fopen(struct inode *inode, struct file *filp) -+{ -+ if (!atomic_add_unless(&data.sample_open, 1, 1)) -+ return -EBUSY; -+ else -+ return 0; -+} -+ -+/** -+ * debug_sample_fread - A read function for "sample" debugfs interface -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The user buffer that will contain the samples read -+ * @cnt: The maximum bytes to read from the debugfs "file" -+ * @ppos: The current position in the debugfs "file" -+ * -+ * This function handles reading from the "sample" file within the hardware -+ * latency detector debugfs directory interface. This file is used to read -+ * raw samples from the global ring_buffer and allows the user to see a -+ * running latency history. By default this will block pending a new -+ * value written into the sample buffer, unless there are already a -+ * number of value(s) waiting in the buffer, or the sample file was -+ * previously opened in a non-blocking mode of operation. -+ */ -+static ssize_t debug_sample_fread(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ int len = 0; -+ char buf[64]; -+ struct sample *sample = NULL; -+ -+ if (!enabled) -+ return 0; -+ -+ sample = kzalloc(sizeof(struct sample), GFP_KERNEL); -+ if (!sample) -+ return -ENOMEM; -+ -+ while (!buffer_get_sample(sample)) { -+ -+ DEFINE_WAIT(wait); -+ -+ if (filp->f_flags & O_NONBLOCK) { -+ len = -EAGAIN; -+ goto out; -+ } -+ -+ prepare_to_wait(&data.wq, &wait, TASK_INTERRUPTIBLE); -+ schedule(); -+ finish_wait(&data.wq, &wait); -+ -+ if (signal_pending(current)) { -+ len = -EINTR; -+ goto out; -+ } -+ -+ if (!enabled) { /* enable was toggled */ -+ len = 0; -+ goto out; -+ } -+ } -+ -+ len = snprintf(buf, sizeof(buf), "%010lu.%010lu\t%llu\t%llu\n", -+ sample->timestamp.tv_sec, -+ sample->timestamp.tv_nsec, -+ sample->duration, -+ sample->outer_duration); -+ -+ -+ /* handling partial reads is more trouble than it's worth */ -+ if (len > cnt) -+ goto out; -+ -+ if (copy_to_user(ubuf, buf, len)) -+ len = -EFAULT; -+ -+out: -+ kfree(sample); -+ return len; -+} -+ -+/** -+ * debug_sample_release - Release function for "sample" debugfs interface -+ * @inode: The in-kernel inode represenation of the debugfs "file" -+ * @filp: The active open file structure for the debugfs "file" -+ * -+ * This function completes the close of the debugfs interface "sample" file. -+ * Frees the sample_open "lock" so that other users may open the interface. -+ */ -+static int debug_sample_release(struct inode *inode, struct file *filp) -+{ -+ atomic_dec(&data.sample_open); -+ -+ return 0; -+} -+ -+/** -+ * debug_threshold_fopen - Open function for "threshold" debugfs entry -+ * @inode: The in-kernel inode representation of the debugfs "file" -+ * @filp: The active open file structure for the debugfs "file" -+ * -+ * This function provides an open implementation for the "threshold" debugfs -+ * interface to the hardware latency detector. -+ */ -+static int debug_threshold_fopen(struct inode *inode, struct file *filp) -+{ -+ return 0; -+} -+ -+/** -+ * debug_threshold_fread - Read function for "threshold" debugfs entry -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The userspace provided buffer to read value into -+ * @cnt: The maximum number of bytes to read -+ * @ppos: The current "file" position -+ * -+ * This function provides a read implementation for the "threshold" debugfs -+ * interface to the hardware latency detector. It can be used to determine -+ * the current threshold level at which a latency will be recorded in the -+ * global ring buffer, typically on the order of 10us. -+ */ -+static ssize_t debug_threshold_fread(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ return simple_data_read(filp, ubuf, cnt, ppos, &data.threshold); -+} -+ -+/** -+ * debug_threshold_fwrite - Write function for "threshold" debugfs entry -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The user buffer that contains the value to write -+ * @cnt: The maximum number of bytes to write to "file" -+ * @ppos: The current position in the debugfs "file" -+ * -+ * This function provides a write implementation for the "threshold" debugfs -+ * interface to the hardware latency detector. It can be used to configure -+ * the threshold level at which any subsequently detected latencies will -+ * be recorded into the global ring buffer. -+ */ -+static ssize_t debug_threshold_fwrite(struct file *filp, -+ const char __user *ubuf, -+ size_t cnt, -+ loff_t *ppos) -+{ -+ int ret; -+ -+ ret = simple_data_write(filp, ubuf, cnt, ppos, &data.threshold); -+ -+ if (enabled) -+ wake_up_process(kthread); -+ -+ return ret; -+} -+ -+/** -+ * debug_width_fopen - Open function for "width" debugfs entry -+ * @inode: The in-kernel inode representation of the debugfs "file" -+ * @filp: The active open file structure for the debugfs "file" -+ * -+ * This function provides an open implementation for the "width" debugfs -+ * interface to the hardware latency detector. -+ */ -+static int debug_width_fopen(struct inode *inode, struct file *filp) -+{ -+ return 0; -+} -+ -+/** -+ * debug_width_fread - Read function for "width" debugfs entry -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The userspace provided buffer to read value into -+ * @cnt: The maximum number of bytes to read -+ * @ppos: The current "file" position -+ * -+ * This function provides a read implementation for the "width" debugfs -+ * interface to the hardware latency detector. It can be used to determine -+ * for how many us of the total window us we will actively sample for any -+ * hardware-induced latecy periods. Obviously, it is not possible to -+ * sample constantly and have the system respond to a sample reader, or, -+ * worse, without having the system appear to have gone out to lunch. -+ */ -+static ssize_t debug_width_fread(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ return simple_data_read(filp, ubuf, cnt, ppos, &data.sample_width); -+} -+ -+/** -+ * debug_width_fwrite - Write function for "width" debugfs entry -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The user buffer that contains the value to write -+ * @cnt: The maximum number of bytes to write to "file" -+ * @ppos: The current position in the debugfs "file" -+ * -+ * This function provides a write implementation for the "width" debugfs -+ * interface to the hardware latency detector. It can be used to configure -+ * for how many us of the total window us we will actively sample for any -+ * hardware-induced latency periods. Obviously, it is not possible to -+ * sample constantly and have the system respond to a sample reader, or, -+ * worse, without having the system appear to have gone out to lunch. It -+ * is enforced that width is less that the total window size. -+ */ -+static ssize_t debug_width_fwrite(struct file *filp, -+ const char __user *ubuf, -+ size_t cnt, -+ loff_t *ppos) -+{ -+ char buf[U64STR_SIZE]; -+ int csize = min(cnt, sizeof(buf)); -+ u64 val = 0; -+ int err = 0; -+ -+ memset(buf, '\0', sizeof(buf)); -+ if (copy_from_user(buf, ubuf, csize)) -+ return -EFAULT; -+ -+ buf[U64STR_SIZE-1] = '\0'; /* just in case */ -+ err = kstrtoull(buf, 10, &val); -+ if (0 != err) -+ return -EINVAL; -+ -+ mutex_lock(&data.lock); -+ if (val < data.sample_window) -+ data.sample_width = val; -+ else { -+ mutex_unlock(&data.lock); -+ return -EINVAL; -+ } -+ mutex_unlock(&data.lock); -+ -+ if (enabled) -+ wake_up_process(kthread); -+ -+ return csize; -+} -+ -+/** -+ * debug_window_fopen - Open function for "window" debugfs entry -+ * @inode: The in-kernel inode representation of the debugfs "file" -+ * @filp: The active open file structure for the debugfs "file" -+ * -+ * This function provides an open implementation for the "window" debugfs -+ * interface to the hardware latency detector. The window is the total time -+ * in us that will be considered one sample period. Conceptually, windows -+ * occur back-to-back and contain a sample width period during which -+ * actual sampling occurs. -+ */ -+static int debug_window_fopen(struct inode *inode, struct file *filp) -+{ -+ return 0; -+} -+ -+/** -+ * debug_window_fread - Read function for "window" debugfs entry -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The userspace provided buffer to read value into -+ * @cnt: The maximum number of bytes to read -+ * @ppos: The current "file" position -+ * -+ * This function provides a read implementation for the "window" debugfs -+ * interface to the hardware latency detector. The window is the total time -+ * in us that will be considered one sample period. Conceptually, windows -+ * occur back-to-back and contain a sample width period during which -+ * actual sampling occurs. Can be used to read the total window size. -+ */ -+static ssize_t debug_window_fread(struct file *filp, char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ return simple_data_read(filp, ubuf, cnt, ppos, &data.sample_window); -+} -+ -+/** -+ * debug_window_fwrite - Write function for "window" debugfs entry -+ * @filp: The active open file structure for the debugfs "file" -+ * @ubuf: The user buffer that contains the value to write -+ * @cnt: The maximum number of bytes to write to "file" -+ * @ppos: The current position in the debugfs "file" -+ * -+ * This function provides a write implementation for the "window" debufds -+ * interface to the hardware latency detetector. The window is the total time -+ * in us that will be considered one sample period. Conceptually, windows -+ * occur back-to-back and contain a sample width period during which -+ * actual sampling occurs. Can be used to write a new total window size. It -+ * is enfoced that any value written must be greater than the sample width -+ * size, or an error results. -+ */ -+static ssize_t debug_window_fwrite(struct file *filp, -+ const char __user *ubuf, -+ size_t cnt, -+ loff_t *ppos) -+{ -+ char buf[U64STR_SIZE]; -+ int csize = min(cnt, sizeof(buf)); -+ u64 val = 0; -+ int err = 0; -+ -+ memset(buf, '\0', sizeof(buf)); -+ if (copy_from_user(buf, ubuf, csize)) -+ return -EFAULT; -+ -+ buf[U64STR_SIZE-1] = '\0'; /* just in case */ -+ err = kstrtoull(buf, 10, &val); -+ if (0 != err) -+ return -EINVAL; -+ -+ mutex_lock(&data.lock); -+ if (data.sample_width < val) -+ data.sample_window = val; -+ else { -+ mutex_unlock(&data.lock); -+ return -EINVAL; -+ } -+ mutex_unlock(&data.lock); -+ -+ return csize; -+} -+ -+/* -+ * Function pointers for the "count" debugfs file operations -+ */ -+static const struct file_operations count_fops = { -+ .open = debug_count_fopen, -+ .read = debug_count_fread, -+ .write = debug_count_fwrite, -+ .owner = THIS_MODULE, -+}; -+ -+/* -+ * Function pointers for the "enable" debugfs file operations -+ */ -+static const struct file_operations enable_fops = { -+ .open = debug_enable_fopen, -+ .read = debug_enable_fread, -+ .write = debug_enable_fwrite, -+ .owner = THIS_MODULE, -+}; -+ -+/* -+ * Function pointers for the "max" debugfs file operations -+ */ -+static const struct file_operations max_fops = { -+ .open = debug_max_fopen, -+ .read = debug_max_fread, -+ .write = debug_max_fwrite, -+ .owner = THIS_MODULE, -+}; -+ -+/* -+ * Function pointers for the "sample" debugfs file operations -+ */ -+static const struct file_operations sample_fops = { -+ .open = debug_sample_fopen, -+ .read = debug_sample_fread, -+ .release = debug_sample_release, -+ .owner = THIS_MODULE, -+}; -+ -+/* -+ * Function pointers for the "threshold" debugfs file operations -+ */ -+static const struct file_operations threshold_fops = { -+ .open = debug_threshold_fopen, -+ .read = debug_threshold_fread, -+ .write = debug_threshold_fwrite, -+ .owner = THIS_MODULE, -+}; -+ -+/* -+ * Function pointers for the "width" debugfs file operations -+ */ -+static const struct file_operations width_fops = { -+ .open = debug_width_fopen, -+ .read = debug_width_fread, -+ .write = debug_width_fwrite, -+ .owner = THIS_MODULE, -+}; -+ -+/* -+ * Function pointers for the "window" debugfs file operations -+ */ -+static const struct file_operations window_fops = { -+ .open = debug_window_fopen, -+ .read = debug_window_fread, -+ .write = debug_window_fwrite, -+ .owner = THIS_MODULE, -+}; -+ -+/** -+ * init_debugfs - A function to initialize the debugfs interface files -+ * -+ * This function creates entries in debugfs for "hwlat_detector", including -+ * files to read values from the detector, current samples, and the -+ * maximum sample that has been captured since the hardware latency -+ * dectector was started. -+ */ -+static int init_debugfs(void) -+{ -+ int ret = -ENOMEM; -+ -+ debug_dir = debugfs_create_dir(DRVNAME, NULL); -+ if (!debug_dir) -+ goto err_debug_dir; -+ -+ debug_sample = debugfs_create_file("sample", 0444, -+ debug_dir, NULL, -+ &sample_fops); -+ if (!debug_sample) -+ goto err_sample; -+ -+ debug_count = debugfs_create_file("count", 0444, -+ debug_dir, NULL, -+ &count_fops); -+ if (!debug_count) -+ goto err_count; -+ -+ debug_max = debugfs_create_file("max", 0444, -+ debug_dir, NULL, -+ &max_fops); -+ if (!debug_max) -+ goto err_max; -+ -+ debug_sample_window = debugfs_create_file("window", 0644, -+ debug_dir, NULL, -+ &window_fops); -+ if (!debug_sample_window) -+ goto err_window; -+ -+ debug_sample_width = debugfs_create_file("width", 0644, -+ debug_dir, NULL, -+ &width_fops); -+ if (!debug_sample_width) -+ goto err_width; -+ -+ debug_threshold = debugfs_create_file("threshold", 0644, -+ debug_dir, NULL, -+ &threshold_fops); -+ if (!debug_threshold) -+ goto err_threshold; -+ -+ debug_enable = debugfs_create_file("enable", 0644, -+ debug_dir, &enabled, -+ &enable_fops); -+ if (!debug_enable) -+ goto err_enable; -+ -+ else { -+ ret = 0; -+ goto out; -+ } -+ -+err_enable: -+ debugfs_remove(debug_threshold); -+err_threshold: -+ debugfs_remove(debug_sample_width); -+err_width: -+ debugfs_remove(debug_sample_window); -+err_window: -+ debugfs_remove(debug_max); -+err_max: -+ debugfs_remove(debug_count); -+err_count: -+ debugfs_remove(debug_sample); -+err_sample: -+ debugfs_remove(debug_dir); -+err_debug_dir: -+out: -+ return ret; -+} -+ -+/** -+ * free_debugfs - A function to cleanup the debugfs file interface -+ */ -+static void free_debugfs(void) -+{ -+ /* could also use a debugfs_remove_recursive */ -+ debugfs_remove(debug_enable); -+ debugfs_remove(debug_threshold); -+ debugfs_remove(debug_sample_width); -+ debugfs_remove(debug_sample_window); -+ debugfs_remove(debug_max); -+ debugfs_remove(debug_count); -+ debugfs_remove(debug_sample); -+ debugfs_remove(debug_dir); -+} -+ -+/** -+ * detector_init - Standard module initialization code -+ */ -+static int detector_init(void) -+{ -+ int ret = -ENOMEM; -+ -+ pr_info(BANNER "version %s\n", VERSION); -+ -+ ret = init_stats(); -+ if (0 != ret) -+ goto out; -+ -+ ret = init_debugfs(); -+ if (0 != ret) -+ goto err_stats; -+ -+ if (enabled) -+ ret = start_kthread(); -+ -+ goto out; -+ -+err_stats: -+ ring_buffer_free(ring_buffer); -+out: -+ return ret; -+ -+} -+ -+/** -+ * detector_exit - Standard module cleanup code -+ */ -+static void detector_exit(void) -+{ -+ int err; -+ -+ if (enabled) { -+ enabled = 0; -+ err = stop_kthread(); -+ if (err) -+ pr_err(BANNER "cannot stop kthread\n"); -+ } -+ -+ free_debugfs(); -+ ring_buffer_free(ring_buffer); /* free up the ring buffer */ -+ -+} -+ -+module_init(detector_init); -+module_exit(detector_exit); -diff -Nur linux-3.18.14.orig/drivers/misc/Kconfig linux-3.18.14-rt/drivers/misc/Kconfig ---- linux-3.18.14.orig/drivers/misc/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/misc/Kconfig 2015-05-31 15:32:47.297635378 -0500 -@@ -54,6 +54,7 @@ - config ATMEL_TCLIB - bool "Atmel AT32/AT91 Timer/Counter Library" - depends on (AVR32 || ARCH_AT91) -+ default y if PREEMPT_RT_FULL - help - Select this if you want a library to allocate the Timer/Counter - blocks found on many Atmel processors. This facilitates using -@@ -69,8 +70,7 @@ - are combined to make a single 32-bit timer. - - When GENERIC_CLOCKEVENTS is defined, the third timer channel -- may be used as a clock event device supporting oneshot mode -- (delays of up to two seconds) based on the 32 KiHz clock. -+ may be used as a clock event device supporting oneshot mode. - - config ATMEL_TCB_CLKSRC_BLOCK - int -@@ -84,6 +84,15 @@ - TC can be used for other purposes, such as PWM generation and - interval timing. - -+config ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK -+ bool "TC Block use 32 KiHz clock" -+ depends on ATMEL_TCB_CLKSRC -+ default y if !PREEMPT_RT_FULL -+ help -+ Select this to use 32 KiHz base clock rate as TC block clock -+ source for clock events. -+ -+ - config DUMMY_IRQ - tristate "Dummy IRQ handler" - default n -@@ -113,6 +122,35 @@ - for information on the specific driver level and support statement - for your IBM server. - -+config HWLAT_DETECTOR -+ tristate "Testing module to detect hardware-induced latencies" -+ depends on DEBUG_FS -+ depends on RING_BUFFER -+ default m -+ ---help--- -+ A simple hardware latency detector. Use this module to detect -+ large latencies introduced by the behavior of the underlying -+ system firmware external to Linux. We do this using periodic -+ use of stop_machine to grab all available CPUs and measure -+ for unexplainable gaps in the CPU timestamp counter(s). By -+ default, the module is not enabled until the "enable" file -+ within the "hwlat_detector" debugfs directory is toggled. -+ -+ This module is often used to detect SMI (System Management -+ Interrupts) on x86 systems, though is not x86 specific. To -+ this end, we default to using a sample window of 1 second, -+ during which we will sample for 0.5 seconds. If an SMI or -+ similar event occurs during that time, it is recorded -+ into an 8K samples global ring buffer until retreived. -+ -+ WARNING: This software should never be enabled (it can be built -+ but should not be turned on after it is loaded) in a production -+ environment where high latencies are a concern since the -+ sampling mechanism actually introduces latencies for -+ regular tasks while the CPU(s) are being held. -+ -+ If unsure, say N -+ - config PHANTOM - tristate "Sensable PHANToM (PCI)" - depends on PCI -diff -Nur linux-3.18.14.orig/drivers/misc/Makefile linux-3.18.14-rt/drivers/misc/Makefile ---- linux-3.18.14.orig/drivers/misc/Makefile 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/misc/Makefile 2015-05-31 15:32:47.349635377 -0500 -@@ -38,6 +38,7 @@ - obj-$(CONFIG_HMC6352) += hmc6352.o - obj-y += eeprom/ - obj-y += cb710/ -+obj-$(CONFIG_HWLAT_DETECTOR) += hwlat_detector.o - obj-$(CONFIG_SPEAR13XX_PCIE_GADGET) += spear13xx_pcie_gadget.o - obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o - obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o -diff -Nur linux-3.18.14.orig/drivers/mmc/host/mmci.c linux-3.18.14-rt/drivers/mmc/host/mmci.c ---- linux-3.18.14.orig/drivers/mmc/host/mmci.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/mmc/host/mmci.c 2015-05-31 15:32:47.393635377 -0500 -@@ -1153,15 +1153,12 @@ - struct sg_mapping_iter *sg_miter = &host->sg_miter; - struct variant_data *variant = host->variant; - void __iomem *base = host->base; -- unsigned long flags; - u32 status; - - status = readl(base + MMCISTATUS); - - dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); - -- local_irq_save(flags); -- - do { - unsigned int remain, len; - char *buffer; -@@ -1201,8 +1198,6 @@ - - sg_miter_stop(sg_miter); - -- local_irq_restore(flags); -- - /* - * If we have less than the fifo 'half-full' threshold to transfer, - * trigger a PIO interrupt as soon as any data is available. -diff -Nur linux-3.18.14.orig/drivers/mmc/host/sdhci.c linux-3.18.14-rt/drivers/mmc/host/sdhci.c ---- linux-3.18.14.orig/drivers/mmc/host/sdhci.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/mmc/host/sdhci.c 2015-05-31 15:32:47.397635376 -0500 -@@ -2565,6 +2565,31 @@ - return isr ? IRQ_HANDLED : IRQ_NONE; - } - -+#ifdef CONFIG_PREEMPT_RT_BASE -+static irqreturn_t sdhci_rt_irq(int irq, void *dev_id) -+{ -+ irqreturn_t ret; -+ -+ local_bh_disable(); -+ ret = sdhci_irq(irq, dev_id); -+ local_bh_enable(); -+ if (ret == IRQ_WAKE_THREAD) -+ ret = sdhci_thread_irq(irq, dev_id); -+ return ret; -+} -+#endif -+ -+static int sdhci_req_irq(struct sdhci_host *host) -+{ -+#ifdef CONFIG_PREEMPT_RT_BASE -+ return request_threaded_irq(host->irq, NULL, sdhci_rt_irq, -+ IRQF_SHARED, mmc_hostname(host->mmc), host); -+#else -+ return request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, -+ IRQF_SHARED, mmc_hostname(host->mmc), host); -+#endif -+} -+ - /*****************************************************************************\ - * * - * Suspend/resume * -@@ -2632,9 +2657,7 @@ - } - - if (!device_may_wakeup(mmc_dev(host->mmc))) { -- ret = request_threaded_irq(host->irq, sdhci_irq, -- sdhci_thread_irq, IRQF_SHARED, -- mmc_hostname(host->mmc), host); -+ ret = sdhci_req_irq(host); - if (ret) - return ret; - } else { -@@ -3253,8 +3276,7 @@ - - sdhci_init(host, 0); - -- ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, -- IRQF_SHARED, mmc_hostname(mmc), host); -+ ret = sdhci_req_irq(host); - if (ret) { - pr_err("%s: Failed to request IRQ %d: %d\n", - mmc_hostname(mmc), host->irq, ret); -diff -Nur linux-3.18.14.orig/drivers/net/ethernet/3com/3c59x.c linux-3.18.14-rt/drivers/net/ethernet/3com/3c59x.c ---- linux-3.18.14.orig/drivers/net/ethernet/3com/3c59x.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/ethernet/3com/3c59x.c 2015-05-31 15:32:47.425635376 -0500 -@@ -842,9 +842,9 @@ - { - struct vortex_private *vp = netdev_priv(dev); - unsigned long flags; -- local_irq_save(flags); -+ local_irq_save_nort(flags); - (vp->full_bus_master_rx ? boomerang_interrupt:vortex_interrupt)(dev->irq,dev); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - } - #endif - -@@ -1916,12 +1916,12 @@ - * Block interrupts because vortex_interrupt does a bare spin_lock() - */ - unsigned long flags; -- local_irq_save(flags); -+ local_irq_save_nort(flags); - if (vp->full_bus_master_tx) - boomerang_interrupt(dev->irq, dev); - else - vortex_interrupt(dev->irq, dev); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - } - } - -diff -Nur linux-3.18.14.orig/drivers/net/ethernet/atheros/atl1c/atl1c_main.c linux-3.18.14-rt/drivers/net/ethernet/atheros/atl1c/atl1c_main.c ---- linux-3.18.14.orig/drivers/net/ethernet/atheros/atl1c/atl1c_main.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/ethernet/atheros/atl1c/atl1c_main.c 2015-05-31 15:32:47.437635376 -0500 -@@ -2213,11 +2213,7 @@ - } - - tpd_req = atl1c_cal_tpd_req(skb); -- if (!spin_trylock_irqsave(&adapter->tx_lock, flags)) { -- if (netif_msg_pktdata(adapter)) -- dev_info(&adapter->pdev->dev, "tx locked\n"); -- return NETDEV_TX_LOCKED; -- } -+ spin_lock_irqsave(&adapter->tx_lock, flags); - - if (atl1c_tpd_avail(adapter, type) < tpd_req) { - /* no enough descriptor, just stop queue */ -diff -Nur linux-3.18.14.orig/drivers/net/ethernet/atheros/atl1e/atl1e_main.c linux-3.18.14-rt/drivers/net/ethernet/atheros/atl1e/atl1e_main.c ---- linux-3.18.14.orig/drivers/net/ethernet/atheros/atl1e/atl1e_main.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/ethernet/atheros/atl1e/atl1e_main.c 2015-05-31 15:32:47.445635376 -0500 -@@ -1880,8 +1880,7 @@ - return NETDEV_TX_OK; - } - tpd_req = atl1e_cal_tdp_req(skb); -- if (!spin_trylock_irqsave(&adapter->tx_lock, flags)) -- return NETDEV_TX_LOCKED; -+ spin_lock_irqsave(&adapter->tx_lock, flags); - - if (atl1e_tpd_avail(adapter) < tpd_req) { - /* no enough descriptor, just stop queue */ -diff -Nur linux-3.18.14.orig/drivers/net/ethernet/chelsio/cxgb/sge.c linux-3.18.14-rt/drivers/net/ethernet/chelsio/cxgb/sge.c ---- linux-3.18.14.orig/drivers/net/ethernet/chelsio/cxgb/sge.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/ethernet/chelsio/cxgb/sge.c 2015-05-31 15:32:47.493635375 -0500 -@@ -1663,8 +1663,7 @@ - struct cmdQ *q = &sge->cmdQ[qid]; - unsigned int credits, pidx, genbit, count, use_sched_skb = 0; - -- if (!spin_trylock(&q->lock)) -- return NETDEV_TX_LOCKED; -+ spin_lock(&q->lock); - - reclaim_completed_tx(sge, q); - -diff -Nur linux-3.18.14.orig/drivers/net/ethernet/freescale/gianfar.c linux-3.18.14-rt/drivers/net/ethernet/freescale/gianfar.c ---- linux-3.18.14.orig/drivers/net/ethernet/freescale/gianfar.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/ethernet/freescale/gianfar.c 2015-05-31 15:32:47.525635375 -0500 -@@ -1483,7 +1483,7 @@ - - if (netif_running(ndev)) { - -- local_irq_save(flags); -+ local_irq_save_nort(flags); - lock_tx_qs(priv); - - gfar_halt_nodisable(priv); -@@ -1499,7 +1499,7 @@ - gfar_write(®s->maccfg1, tempval); - - unlock_tx_qs(priv); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - disable_napi(priv); - -@@ -1541,7 +1541,7 @@ - /* Disable Magic Packet mode, in case something - * else woke us up. - */ -- local_irq_save(flags); -+ local_irq_save_nort(flags); - lock_tx_qs(priv); - - tempval = gfar_read(®s->maccfg2); -@@ -1551,7 +1551,7 @@ - gfar_start(priv); - - unlock_tx_qs(priv); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - netif_device_attach(ndev); - -@@ -3307,14 +3307,14 @@ - dev->stats.tx_dropped++; - atomic64_inc(&priv->extra_stats.tx_underrun); - -- local_irq_save(flags); -+ local_irq_save_nort(flags); - lock_tx_qs(priv); - - /* Reactivate the Tx Queues */ - gfar_write(®s->tstat, gfargrp->tstat); - - unlock_tx_qs(priv); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - } - netif_dbg(priv, tx_err, dev, "Transmit Error\n"); - } -diff -Nur linux-3.18.14.orig/drivers/net/ethernet/neterion/s2io.c linux-3.18.14-rt/drivers/net/ethernet/neterion/s2io.c ---- linux-3.18.14.orig/drivers/net/ethernet/neterion/s2io.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/ethernet/neterion/s2io.c 2015-05-31 15:32:47.537635375 -0500 -@@ -4084,12 +4084,7 @@ - [skb->priority & (MAX_TX_FIFOS - 1)]; - fifo = &mac_control->fifos[queue]; - -- if (do_spin_lock) -- spin_lock_irqsave(&fifo->tx_lock, flags); -- else { -- if (unlikely(!spin_trylock_irqsave(&fifo->tx_lock, flags))) -- return NETDEV_TX_LOCKED; -- } -+ spin_lock_irqsave(&fifo->tx_lock, flags); - - if (sp->config.multiq) { - if (__netif_subqueue_stopped(dev, fifo->fifo_no)) { -diff -Nur linux-3.18.14.orig/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c linux-3.18.14-rt/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c ---- linux-3.18.14.orig/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c 2015-05-31 15:32:47.549635375 -0500 -@@ -2137,10 +2137,8 @@ - struct pch_gbe_tx_ring *tx_ring = adapter->tx_ring; - unsigned long flags; - -- if (!spin_trylock_irqsave(&tx_ring->tx_lock, flags)) { -- /* Collision - tell upper layer to requeue */ -- return NETDEV_TX_LOCKED; -- } -+ spin_lock_irqsave(&tx_ring->tx_lock, flags); -+ - if (unlikely(!PCH_GBE_DESC_UNUSED(tx_ring))) { - netif_stop_queue(netdev); - spin_unlock_irqrestore(&tx_ring->tx_lock, flags); -diff -Nur linux-3.18.14.orig/drivers/net/ethernet/realtek/8139too.c linux-3.18.14-rt/drivers/net/ethernet/realtek/8139too.c ---- linux-3.18.14.orig/drivers/net/ethernet/realtek/8139too.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/ethernet/realtek/8139too.c 2015-05-31 15:32:47.557635375 -0500 -@@ -2215,7 +2215,7 @@ - struct rtl8139_private *tp = netdev_priv(dev); - const int irq = tp->pci_dev->irq; - -- disable_irq(irq); -+ disable_irq_nosync(irq); - rtl8139_interrupt(irq, dev); - enable_irq(irq); - } -diff -Nur linux-3.18.14.orig/drivers/net/ethernet/tehuti/tehuti.c linux-3.18.14-rt/drivers/net/ethernet/tehuti/tehuti.c ---- linux-3.18.14.orig/drivers/net/ethernet/tehuti/tehuti.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/ethernet/tehuti/tehuti.c 2015-05-31 15:32:47.581635375 -0500 -@@ -1629,13 +1629,8 @@ - unsigned long flags; - - ENTER; -- local_irq_save(flags); -- if (!spin_trylock(&priv->tx_lock)) { -- local_irq_restore(flags); -- DBG("%s[%s]: TX locked, returning NETDEV_TX_LOCKED\n", -- BDX_DRV_NAME, ndev->name); -- return NETDEV_TX_LOCKED; -- } -+ -+ spin_lock_irqsave(&priv->tx_lock, flags); - - /* build tx descriptor */ - BDX_ASSERT(f->m.wptr >= f->m.memsz); /* started with valid wptr */ -diff -Nur linux-3.18.14.orig/drivers/net/rionet.c linux-3.18.14-rt/drivers/net/rionet.c ---- linux-3.18.14.orig/drivers/net/rionet.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/rionet.c 2015-05-31 15:32:47.597635374 -0500 -@@ -174,11 +174,7 @@ - unsigned long flags; - int add_num = 1; - -- local_irq_save(flags); -- if (!spin_trylock(&rnet->tx_lock)) { -- local_irq_restore(flags); -- return NETDEV_TX_LOCKED; -- } -+ spin_lock_irqsave(&rnet->tx_lock, flags); - - if (is_multicast_ether_addr(eth->h_dest)) - add_num = nets[rnet->mport->id].nact; -diff -Nur linux-3.18.14.orig/drivers/net/wireless/orinoco/orinoco_usb.c linux-3.18.14-rt/drivers/net/wireless/orinoco/orinoco_usb.c ---- linux-3.18.14.orig/drivers/net/wireless/orinoco/orinoco_usb.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/net/wireless/orinoco/orinoco_usb.c 2015-05-31 15:32:47.613635374 -0500 -@@ -699,7 +699,7 @@ - while (!ctx->done.done && msecs--) - udelay(1000); - } else { -- wait_event_interruptible(ctx->done.wait, -+ swait_event_interruptible(ctx->done.wait, - ctx->done.done); - } - break; -diff -Nur linux-3.18.14.orig/drivers/pci/access.c linux-3.18.14-rt/drivers/pci/access.c ---- linux-3.18.14.orig/drivers/pci/access.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/pci/access.c 2015-05-31 15:32:47.665635374 -0500 -@@ -434,7 +434,7 @@ - WARN_ON(!dev->block_cfg_access); - - dev->block_cfg_access = 0; -- wake_up_all(&pci_cfg_wait); -+ wake_up_all_locked(&pci_cfg_wait); - raw_spin_unlock_irqrestore(&pci_lock, flags); - } - EXPORT_SYMBOL_GPL(pci_cfg_access_unlock); -diff -Nur linux-3.18.14.orig/drivers/scsi/fcoe/fcoe.c linux-3.18.14-rt/drivers/scsi/fcoe/fcoe.c ---- linux-3.18.14.orig/drivers/scsi/fcoe/fcoe.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/scsi/fcoe/fcoe.c 2015-05-31 15:32:47.677635374 -0500 -@@ -1286,7 +1286,7 @@ - struct sk_buff *skb; - #ifdef CONFIG_SMP - struct fcoe_percpu_s *p0; -- unsigned targ_cpu = get_cpu(); -+ unsigned targ_cpu = get_cpu_light(); - #endif /* CONFIG_SMP */ - - FCOE_DBG("Destroying receive thread for CPU %d\n", cpu); -@@ -1342,7 +1342,7 @@ - kfree_skb(skb); - spin_unlock_bh(&p->fcoe_rx_list.lock); - } -- put_cpu(); -+ put_cpu_light(); - #else - /* - * This a non-SMP scenario where the singular Rx thread is -@@ -1566,11 +1566,11 @@ - static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen) - { - struct fcoe_percpu_s *fps; -- int rc; -+ int rc, cpu = get_cpu_light(); - -- fps = &get_cpu_var(fcoe_percpu); -+ fps = &per_cpu(fcoe_percpu, cpu); - rc = fcoe_get_paged_crc_eof(skb, tlen, fps); -- put_cpu_var(fcoe_percpu); -+ put_cpu_light(); - - return rc; - } -@@ -1768,11 +1768,11 @@ - return 0; - } - -- stats = per_cpu_ptr(lport->stats, get_cpu()); -+ stats = per_cpu_ptr(lport->stats, get_cpu_light()); - stats->InvalidCRCCount++; - if (stats->InvalidCRCCount < 5) - printk(KERN_WARNING "fcoe: dropping frame with CRC error\n"); -- put_cpu(); -+ put_cpu_light(); - return -EINVAL; - } - -@@ -1848,13 +1848,13 @@ - goto drop; - - if (!fcoe_filter_frames(lport, fp)) { -- put_cpu(); -+ put_cpu_light(); - fc_exch_recv(lport, fp); - return; - } - drop: - stats->ErrorFrames++; -- put_cpu(); -+ put_cpu_light(); - kfree_skb(skb); - } - -diff -Nur linux-3.18.14.orig/drivers/scsi/fcoe/fcoe_ctlr.c linux-3.18.14-rt/drivers/scsi/fcoe/fcoe_ctlr.c ---- linux-3.18.14.orig/drivers/scsi/fcoe/fcoe_ctlr.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/scsi/fcoe/fcoe_ctlr.c 2015-05-31 15:32:47.681635374 -0500 -@@ -831,7 +831,7 @@ - - INIT_LIST_HEAD(&del_list); - -- stats = per_cpu_ptr(fip->lp->stats, get_cpu()); -+ stats = per_cpu_ptr(fip->lp->stats, get_cpu_light()); - - list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { - deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2; -@@ -867,7 +867,7 @@ - sel_time = fcf->time; - } - } -- put_cpu(); -+ put_cpu_light(); - - list_for_each_entry_safe(fcf, next, &del_list, list) { - /* Removes fcf from current list */ -diff -Nur linux-3.18.14.orig/drivers/scsi/libfc/fc_exch.c linux-3.18.14-rt/drivers/scsi/libfc/fc_exch.c ---- linux-3.18.14.orig/drivers/scsi/libfc/fc_exch.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/scsi/libfc/fc_exch.c 2015-05-31 15:32:47.689635374 -0500 -@@ -816,10 +816,10 @@ - } - memset(ep, 0, sizeof(*ep)); - -- cpu = get_cpu(); -+ cpu = get_cpu_light(); - pool = per_cpu_ptr(mp->pool, cpu); - spin_lock_bh(&pool->lock); -- put_cpu(); -+ put_cpu_light(); - - /* peek cache of free slot */ - if (pool->left != FC_XID_UNKNOWN) { -diff -Nur linux-3.18.14.orig/drivers/scsi/libsas/sas_ata.c linux-3.18.14-rt/drivers/scsi/libsas/sas_ata.c ---- linux-3.18.14.orig/drivers/scsi/libsas/sas_ata.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/scsi/libsas/sas_ata.c 2015-05-31 15:32:47.689635374 -0500 -@@ -191,7 +191,7 @@ - /* TODO: audit callers to ensure they are ready for qc_issue to - * unconditionally re-enable interrupts - */ -- local_irq_save(flags); -+ local_irq_save_nort(flags); - spin_unlock(ap->lock); - - /* If the device fell off, no sense in issuing commands */ -@@ -261,7 +261,7 @@ - - out: - spin_lock(ap->lock); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - return ret; - } - -diff -Nur linux-3.18.14.orig/drivers/scsi/qla2xxx/qla_inline.h linux-3.18.14-rt/drivers/scsi/qla2xxx/qla_inline.h ---- linux-3.18.14.orig/drivers/scsi/qla2xxx/qla_inline.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/scsi/qla2xxx/qla_inline.h 2015-05-31 15:32:47.693635374 -0500 -@@ -59,12 +59,12 @@ - { - unsigned long flags; - struct qla_hw_data *ha = rsp->hw; -- local_irq_save(flags); -+ local_irq_save_nort(flags); - if (IS_P3P_TYPE(ha)) - qla82xx_poll(0, rsp); - else - ha->isp_ops->intr_handler(0, rsp); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - } - - static inline uint8_t * -diff -Nur linux-3.18.14.orig/drivers/thermal/x86_pkg_temp_thermal.c linux-3.18.14-rt/drivers/thermal/x86_pkg_temp_thermal.c ---- linux-3.18.14.orig/drivers/thermal/x86_pkg_temp_thermal.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/thermal/x86_pkg_temp_thermal.c 2015-05-31 15:32:47.701635374 -0500 -@@ -29,6 +29,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -352,7 +353,7 @@ - } - } - --static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val) -+static void platform_thermal_notify_work(struct swork_event *event) - { - unsigned long flags; - int cpu = smp_processor_id(); -@@ -369,7 +370,7 @@ - pkg_work_scheduled[phy_id]) { - disable_pkg_thres_interrupt(); - spin_unlock_irqrestore(&pkg_work_lock, flags); -- return -EINVAL; -+ return; - } - pkg_work_scheduled[phy_id] = 1; - spin_unlock_irqrestore(&pkg_work_lock, flags); -@@ -378,9 +379,48 @@ - schedule_delayed_work_on(cpu, - &per_cpu(pkg_temp_thermal_threshold_work, cpu), - msecs_to_jiffies(notify_delay_ms)); -+} -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+static struct swork_event notify_work; -+ -+static int thermal_notify_work_init(void) -+{ -+ int err; -+ -+ err = swork_get(); -+ if (err) -+ return err; -+ -+ INIT_SWORK(¬ify_work, platform_thermal_notify_work); - return 0; - } - -+static void thermal_notify_work_cleanup(void) -+{ -+ swork_put(); -+} -+ -+static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val) -+{ -+ swork_queue(¬ify_work); -+ return 0; -+} -+ -+#else /* !CONFIG_PREEMPT_RT_FULL */ -+ -+static int thermal_notify_work_init(void) { return 0; } -+ -+static int thermal_notify_work_cleanup(void) { } -+ -+static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val) -+{ -+ platform_thermal_notify_work(NULL); -+ -+ return 0; -+} -+#endif /* CONFIG_PREEMPT_RT_FULL */ -+ - static int find_siblings_cpu(int cpu) - { - int i; -@@ -584,6 +624,9 @@ - if (!x86_match_cpu(pkg_temp_thermal_ids)) - return -ENODEV; - -+ if (!thermal_notify_work_init()) -+ return -ENODEV; -+ - spin_lock_init(&pkg_work_lock); - platform_thermal_package_notify = - pkg_temp_thermal_platform_thermal_notify; -@@ -608,7 +651,7 @@ - kfree(pkg_work_scheduled); - platform_thermal_package_notify = NULL; - platform_thermal_package_rate_control = NULL; -- -+ thermal_notify_work_cleanup(); - return -ENODEV; - } - -@@ -633,6 +676,7 @@ - mutex_unlock(&phy_dev_list_mutex); - platform_thermal_package_notify = NULL; - platform_thermal_package_rate_control = NULL; -+ thermal_notify_work_cleanup(); - for_each_online_cpu(i) - cancel_delayed_work_sync( - &per_cpu(pkg_temp_thermal_threshold_work, i)); -diff -Nur linux-3.18.14.orig/drivers/tty/serial/8250/8250_core.c linux-3.18.14-rt/drivers/tty/serial/8250/8250_core.c ---- linux-3.18.14.orig/drivers/tty/serial/8250/8250_core.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/tty/serial/8250/8250_core.c 2015-05-31 15:32:47.753635373 -0500 -@@ -37,6 +37,7 @@ - #include - #include - #include -+#include - #include - #include - #ifdef CONFIG_SPARC -@@ -81,7 +82,16 @@ - #define DEBUG_INTR(fmt...) do { } while (0) - #endif - --#define PASS_LIMIT 512 -+/* -+ * On -rt we can have a more delays, and legitimately -+ * so - so don't drop work spuriously and spam the -+ * syslog: -+ */ -+#ifdef CONFIG_PREEMPT_RT_FULL -+# define PASS_LIMIT 1000000 -+#else -+# define PASS_LIMIT 512 -+#endif - - #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -@@ -3197,7 +3207,7 @@ - - serial8250_rpm_get(up); - -- if (port->sysrq || oops_in_progress) -+ if (port->sysrq || oops_in_progress || in_kdb_printk()) - locked = spin_trylock_irqsave(&port->lock, flags); - else - spin_lock_irqsave(&port->lock, flags); -diff -Nur linux-3.18.14.orig/drivers/tty/serial/amba-pl011.c linux-3.18.14-rt/drivers/tty/serial/amba-pl011.c ---- linux-3.18.14.orig/drivers/tty/serial/amba-pl011.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/tty/serial/amba-pl011.c 2015-05-31 15:32:47.777635373 -0500 -@@ -1935,13 +1935,19 @@ - - clk_enable(uap->clk); - -- local_irq_save(flags); -+ /* -+ * local_irq_save(flags); -+ * -+ * This local_irq_save() is nonsense. If we come in via sysrq -+ * handling then interrupts are already disabled. Aside of -+ * that the port.sysrq check is racy on SMP regardless. -+ */ - if (uap->port.sysrq) - locked = 0; - else if (oops_in_progress) -- locked = spin_trylock(&uap->port.lock); -+ locked = spin_trylock_irqsave(&uap->port.lock, flags); - else -- spin_lock(&uap->port.lock); -+ spin_lock_irqsave(&uap->port.lock, flags); - - /* - * First save the CR then disable the interrupts -@@ -1963,8 +1969,7 @@ - writew(old_cr, uap->port.membase + UART011_CR); - - if (locked) -- spin_unlock(&uap->port.lock); -- local_irq_restore(flags); -+ spin_unlock_irqrestore(&uap->port.lock, flags); - - clk_disable(uap->clk); - } -diff -Nur linux-3.18.14.orig/drivers/tty/serial/omap-serial.c linux-3.18.14-rt/drivers/tty/serial/omap-serial.c ---- linux-3.18.14.orig/drivers/tty/serial/omap-serial.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/tty/serial/omap-serial.c 2015-05-31 15:32:47.781635373 -0500 -@@ -1270,13 +1270,10 @@ - - pm_runtime_get_sync(up->dev); - -- local_irq_save(flags); -- if (up->port.sysrq) -- locked = 0; -- else if (oops_in_progress) -- locked = spin_trylock(&up->port.lock); -+ if (up->port.sysrq || oops_in_progress) -+ locked = spin_trylock_irqsave(&up->port.lock, flags); - else -- spin_lock(&up->port.lock); -+ spin_lock_irqsave(&up->port.lock, flags); - - /* - * First save the IER then disable the interrupts -@@ -1305,8 +1302,7 @@ - pm_runtime_mark_last_busy(up->dev); - pm_runtime_put_autosuspend(up->dev); - if (locked) -- spin_unlock(&up->port.lock); -- local_irq_restore(flags); -+ spin_unlock_irqrestore(&up->port.lock, flags); - } - - static int __init -diff -Nur linux-3.18.14.orig/drivers/usb/core/hcd.c linux-3.18.14-rt/drivers/usb/core/hcd.c ---- linux-3.18.14.orig/drivers/usb/core/hcd.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/usb/core/hcd.c 2015-05-31 15:32:47.785635373 -0500 -@@ -1681,9 +1681,9 @@ - * and no one may trigger the above deadlock situation when - * running complete() in tasklet. - */ -- local_irq_save(flags); -+ local_irq_save_nort(flags); - urb->complete(urb); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - usb_anchor_resume_wakeups(anchor); - atomic_dec(&urb->use_count); -diff -Nur linux-3.18.14.orig/drivers/usb/gadget/function/f_fs.c linux-3.18.14-rt/drivers/usb/gadget/function/f_fs.c ---- linux-3.18.14.orig/drivers/usb/gadget/function/f_fs.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/usb/gadget/function/f_fs.c 2015-05-31 15:32:47.809635373 -0500 -@@ -1428,7 +1428,7 @@ - pr_info("%s(): freeing\n", __func__); - ffs_data_clear(ffs); - BUG_ON(waitqueue_active(&ffs->ev.waitq) || -- waitqueue_active(&ffs->ep0req_completion.wait)); -+ swaitqueue_active(&ffs->ep0req_completion.wait)); - kfree(ffs->dev_name); - kfree(ffs); - } -diff -Nur linux-3.18.14.orig/drivers/usb/gadget/legacy/inode.c linux-3.18.14-rt/drivers/usb/gadget/legacy/inode.c ---- linux-3.18.14.orig/drivers/usb/gadget/legacy/inode.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/drivers/usb/gadget/legacy/inode.c 2015-05-31 15:32:47.837635372 -0500 -@@ -339,7 +339,7 @@ - spin_unlock_irq (&epdata->dev->lock); - - if (likely (value == 0)) { -- value = wait_event_interruptible (done.wait, done.done); -+ value = swait_event_interruptible (done.wait, done.done); - if (value != 0) { - spin_lock_irq (&epdata->dev->lock); - if (likely (epdata->ep != NULL)) { -@@ -348,7 +348,7 @@ - usb_ep_dequeue (epdata->ep, epdata->req); - spin_unlock_irq (&epdata->dev->lock); - -- wait_event (done.wait, done.done); -+ swait_event (done.wait, done.done); - if (epdata->status == -ECONNRESET) - epdata->status = -EINTR; - } else { -diff -Nur linux-3.18.14.orig/fs/aio.c linux-3.18.14-rt/fs/aio.c ---- linux-3.18.14.orig/fs/aio.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/aio.c 2015-05-31 15:32:47.853635372 -0500 -@@ -40,6 +40,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -110,7 +111,7 @@ - struct page **ring_pages; - long nr_pages; - -- struct work_struct free_work; -+ struct swork_event free_work; - - /* - * signals when all in-flight requests are done -@@ -226,6 +227,7 @@ - .mount = aio_mount, - .kill_sb = kill_anon_super, - }; -+ BUG_ON(swork_get()); - aio_mnt = kern_mount(&aio_fs); - if (IS_ERR(aio_mnt)) - panic("Failed to create aio fs mount."); -@@ -505,9 +507,9 @@ - return cancel(kiocb); - } - --static void free_ioctx(struct work_struct *work) -+static void free_ioctx(struct swork_event *sev) - { -- struct kioctx *ctx = container_of(work, struct kioctx, free_work); -+ struct kioctx *ctx = container_of(sev, struct kioctx, free_work); - - pr_debug("freeing %p\n", ctx); - -@@ -526,8 +528,8 @@ - if (ctx->requests_done) - complete(ctx->requests_done); - -- INIT_WORK(&ctx->free_work, free_ioctx); -- schedule_work(&ctx->free_work); -+ INIT_SWORK(&ctx->free_work, free_ioctx); -+ swork_queue(&ctx->free_work); - } - - /* -@@ -535,9 +537,9 @@ - * and ctx->users has dropped to 0, so we know no more kiocbs can be submitted - - * now it's safe to cancel any that need to be. - */ --static void free_ioctx_users(struct percpu_ref *ref) -+static void free_ioctx_users_work(struct swork_event *sev) - { -- struct kioctx *ctx = container_of(ref, struct kioctx, users); -+ struct kioctx *ctx = container_of(sev, struct kioctx, free_work); - struct kiocb *req; - - spin_lock_irq(&ctx->ctx_lock); -@@ -556,6 +558,14 @@ - percpu_ref_put(&ctx->reqs); - } - -+static void free_ioctx_users(struct percpu_ref *ref) -+{ -+ struct kioctx *ctx = container_of(ref, struct kioctx, users); -+ -+ INIT_SWORK(&ctx->free_work, free_ioctx_users_work); -+ swork_queue(&ctx->free_work); -+} -+ - static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm) - { - unsigned i, new_nr; -diff -Nur linux-3.18.14.orig/fs/autofs4/autofs_i.h linux-3.18.14-rt/fs/autofs4/autofs_i.h ---- linux-3.18.14.orig/fs/autofs4/autofs_i.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/autofs4/autofs_i.h 2015-05-31 15:32:47.865635372 -0500 -@@ -34,6 +34,7 @@ - #include - #include - #include -+#include - #include - #include - -diff -Nur linux-3.18.14.orig/fs/autofs4/expire.c linux-3.18.14-rt/fs/autofs4/expire.c ---- linux-3.18.14.orig/fs/autofs4/expire.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/autofs4/expire.c 2015-05-31 15:32:47.897635372 -0500 -@@ -151,7 +151,7 @@ - parent = p->d_parent; - if (!spin_trylock(&parent->d_lock)) { - spin_unlock(&p->d_lock); -- cpu_relax(); -+ cpu_chill(); - goto relock; - } - spin_unlock(&p->d_lock); -diff -Nur linux-3.18.14.orig/fs/buffer.c linux-3.18.14-rt/fs/buffer.c ---- linux-3.18.14.orig/fs/buffer.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/buffer.c 2015-05-31 15:32:47.905635372 -0500 -@@ -301,8 +301,7 @@ - * decide that the page is now completely done. - */ - first = page_buffers(page); -- local_irq_save(flags); -- bit_spin_lock(BH_Uptodate_Lock, &first->b_state); -+ flags = bh_uptodate_lock_irqsave(first); - clear_buffer_async_read(bh); - unlock_buffer(bh); - tmp = bh; -@@ -315,8 +314,7 @@ - } - tmp = tmp->b_this_page; - } while (tmp != bh); -- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); -- local_irq_restore(flags); -+ bh_uptodate_unlock_irqrestore(first, flags); - - /* - * If none of the buffers had errors and they are all -@@ -328,9 +326,7 @@ - return; - - still_busy: -- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); -- local_irq_restore(flags); -- return; -+ bh_uptodate_unlock_irqrestore(first, flags); - } - - /* -@@ -358,8 +354,7 @@ - } - - first = page_buffers(page); -- local_irq_save(flags); -- bit_spin_lock(BH_Uptodate_Lock, &first->b_state); -+ flags = bh_uptodate_lock_irqsave(first); - - clear_buffer_async_write(bh); - unlock_buffer(bh); -@@ -371,15 +366,12 @@ - } - tmp = tmp->b_this_page; - } -- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); -- local_irq_restore(flags); -+ bh_uptodate_unlock_irqrestore(first, flags); - end_page_writeback(page); - return; - - still_busy: -- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); -- local_irq_restore(flags); -- return; -+ bh_uptodate_unlock_irqrestore(first, flags); - } - EXPORT_SYMBOL(end_buffer_async_write); - -@@ -3325,6 +3317,7 @@ - struct buffer_head *ret = kmem_cache_zalloc(bh_cachep, gfp_flags); - if (ret) { - INIT_LIST_HEAD(&ret->b_assoc_buffers); -+ buffer_head_init_locks(ret); - preempt_disable(); - __this_cpu_inc(bh_accounting.nr); - recalc_bh_state(); -diff -Nur linux-3.18.14.orig/fs/dcache.c linux-3.18.14-rt/fs/dcache.c ---- linux-3.18.14.orig/fs/dcache.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/dcache.c 2015-05-31 15:32:47.929635371 -0500 -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -552,7 +553,7 @@ - - failed: - spin_unlock(&dentry->d_lock); -- cpu_relax(); -+ cpu_chill(); - return dentry; /* try again with same dentry */ - } - -@@ -2285,7 +2286,7 @@ - if (dentry->d_lockref.count == 1) { - if (!spin_trylock(&inode->i_lock)) { - spin_unlock(&dentry->d_lock); -- cpu_relax(); -+ cpu_chill(); - goto again; - } - dentry->d_flags &= ~DCACHE_CANT_MOUNT; -diff -Nur linux-3.18.14.orig/fs/eventpoll.c linux-3.18.14-rt/fs/eventpoll.c ---- linux-3.18.14.orig/fs/eventpoll.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/eventpoll.c 2015-05-31 15:32:47.945635371 -0500 -@@ -505,12 +505,12 @@ - */ - static void ep_poll_safewake(wait_queue_head_t *wq) - { -- int this_cpu = get_cpu(); -+ int this_cpu = get_cpu_light(); - - ep_call_nested(&poll_safewake_ncalls, EP_MAX_NESTS, - ep_poll_wakeup_proc, NULL, wq, (void *) (long) this_cpu); - -- put_cpu(); -+ put_cpu_light(); - } - - static void ep_remove_wait_queue(struct eppoll_entry *pwq) -diff -Nur linux-3.18.14.orig/fs/exec.c linux-3.18.14-rt/fs/exec.c ---- linux-3.18.14.orig/fs/exec.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/exec.c 2015-05-31 15:32:47.945635371 -0500 -@@ -841,12 +841,14 @@ - } - } - task_lock(tsk); -+ preempt_disable_rt(); - active_mm = tsk->active_mm; - tsk->mm = mm; - tsk->active_mm = mm; - activate_mm(active_mm, mm); - tsk->mm->vmacache_seqnum = 0; - vmacache_flush(tsk); -+ preempt_enable_rt(); - task_unlock(tsk); - if (old_mm) { - up_read(&old_mm->mmap_sem); -diff -Nur linux-3.18.14.orig/fs/jbd/checkpoint.c linux-3.18.14-rt/fs/jbd/checkpoint.c ---- linux-3.18.14.orig/fs/jbd/checkpoint.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/jbd/checkpoint.c 2015-05-31 15:32:47.957635371 -0500 -@@ -129,6 +129,8 @@ - if (journal->j_flags & JFS_ABORT) - return; - spin_unlock(&journal->j_state_lock); -+ if (current->plug) -+ io_schedule(); - mutex_lock(&journal->j_checkpoint_mutex); - - /* -diff -Nur linux-3.18.14.orig/fs/jbd2/checkpoint.c linux-3.18.14-rt/fs/jbd2/checkpoint.c ---- linux-3.18.14.orig/fs/jbd2/checkpoint.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/jbd2/checkpoint.c 2015-05-31 15:32:47.969635371 -0500 -@@ -116,6 +116,8 @@ - nblocks = jbd2_space_needed(journal); - while (jbd2_log_space_left(journal) < nblocks) { - write_unlock(&journal->j_state_lock); -+ if (current->plug) -+ io_schedule(); - mutex_lock(&journal->j_checkpoint_mutex); - - /* -diff -Nur linux-3.18.14.orig/fs/namespace.c linux-3.18.14-rt/fs/namespace.c ---- linux-3.18.14.orig/fs/namespace.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/namespace.c 2015-05-31 15:32:47.969635371 -0500 -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - #include /* init_rootfs */ -@@ -344,8 +345,11 @@ - * incremented count after it has set MNT_WRITE_HOLD. - */ - smp_mb(); -- while (ACCESS_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD) -- cpu_relax(); -+ while (ACCESS_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD) { -+ preempt_enable(); -+ cpu_chill(); -+ preempt_disable(); -+ } - /* - * After the slowpath clears MNT_WRITE_HOLD, mnt_is_readonly will - * be set to match its requirements. So we must not load that until -diff -Nur linux-3.18.14.orig/fs/ntfs/aops.c linux-3.18.14-rt/fs/ntfs/aops.c ---- linux-3.18.14.orig/fs/ntfs/aops.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/ntfs/aops.c 2015-05-31 15:32:47.969635371 -0500 -@@ -107,8 +107,7 @@ - "0x%llx.", (unsigned long long)bh->b_blocknr); - } - first = page_buffers(page); -- local_irq_save(flags); -- bit_spin_lock(BH_Uptodate_Lock, &first->b_state); -+ flags = bh_uptodate_lock_irqsave(first); - clear_buffer_async_read(bh); - unlock_buffer(bh); - tmp = bh; -@@ -123,8 +122,7 @@ - } - tmp = tmp->b_this_page; - } while (tmp != bh); -- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); -- local_irq_restore(flags); -+ bh_uptodate_unlock_irqrestore(first, flags); - /* - * If none of the buffers had errors then we can set the page uptodate, - * but we first have to perform the post read mst fixups, if the -@@ -145,13 +143,13 @@ - recs = PAGE_CACHE_SIZE / rec_size; - /* Should have been verified before we got here... */ - BUG_ON(!recs); -- local_irq_save(flags); -+ local_irq_save_nort(flags); - kaddr = kmap_atomic(page); - for (i = 0; i < recs; i++) - post_read_mst_fixup((NTFS_RECORD*)(kaddr + - i * rec_size), rec_size); - kunmap_atomic(kaddr); -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - flush_dcache_page(page); - if (likely(page_uptodate && !PageError(page))) - SetPageUptodate(page); -@@ -159,9 +157,7 @@ - unlock_page(page); - return; - still_busy: -- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); -- local_irq_restore(flags); -- return; -+ bh_uptodate_unlock_irqrestore(first, flags); - } - - /** -diff -Nur linux-3.18.14.orig/fs/timerfd.c linux-3.18.14-rt/fs/timerfd.c ---- linux-3.18.14.orig/fs/timerfd.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/timerfd.c 2015-05-31 15:32:47.969635371 -0500 -@@ -449,7 +449,10 @@ - break; - } - spin_unlock_irq(&ctx->wqh.lock); -- cpu_relax(); -+ if (isalarm(ctx)) -+ hrtimer_wait_for_timer(&ctx->t.alarm.timer); -+ else -+ hrtimer_wait_for_timer(&ctx->t.tmr); - } - - /* -diff -Nur linux-3.18.14.orig/fs/xfs/xfs_linux.h linux-3.18.14-rt/fs/xfs/xfs_linux.h ---- linux-3.18.14.orig/fs/xfs/xfs_linux.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/fs/xfs/xfs_linux.h 2015-05-31 15:32:47.989635371 -0500 -@@ -119,7 +119,7 @@ - /* - * Feature macros (disable/enable) - */ --#ifdef CONFIG_SMP -+#if defined(CONFIG_SMP) && !defined(CONFIG_PREEMPT_RT_FULL) - #define HAVE_PERCPU_SB /* per cpu superblock counters are a 2.6 feature */ - #else - #undef HAVE_PERCPU_SB /* per cpu superblock counters are a 2.6 feature */ -diff -Nur linux-3.18.14.orig/include/acpi/platform/aclinux.h linux-3.18.14-rt/include/acpi/platform/aclinux.h ---- linux-3.18.14.orig/include/acpi/platform/aclinux.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/acpi/platform/aclinux.h 2015-05-31 15:32:48.013635371 -0500 -@@ -123,6 +123,7 @@ - - #define acpi_cache_t struct kmem_cache - #define acpi_spinlock spinlock_t * -+#define acpi_raw_spinlock raw_spinlock_t * - #define acpi_cpu_flags unsigned long - - /* Use native linux version of acpi_os_allocate_zeroed */ -@@ -141,6 +142,20 @@ - #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_thread_id - #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_create_lock - -+#define acpi_os_create_raw_lock(__handle) \ -+({ \ -+ raw_spinlock_t *lock = ACPI_ALLOCATE(sizeof(*lock)); \ -+ \ -+ if (lock) { \ -+ *(__handle) = lock; \ -+ raw_spin_lock_init(*(__handle)); \ -+ } \ -+ lock ? AE_OK : AE_NO_MEMORY; \ -+ }) -+ -+#define acpi_os_delete_raw_lock(__handle) kfree(__handle) -+ -+ - /* - * OSL interfaces used by debugger/disassembler - */ -diff -Nur linux-3.18.14.orig/include/asm-generic/bug.h linux-3.18.14-rt/include/asm-generic/bug.h ---- linux-3.18.14.orig/include/asm-generic/bug.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/asm-generic/bug.h 2015-05-31 15:32:48.037635370 -0500 -@@ -206,6 +206,20 @@ - # define WARN_ON_SMP(x) ({0;}) - #endif - -+#ifdef CONFIG_PREEMPT_RT_BASE -+# define BUG_ON_RT(c) BUG_ON(c) -+# define BUG_ON_NONRT(c) do { } while (0) -+# define WARN_ON_RT(condition) WARN_ON(condition) -+# define WARN_ON_NONRT(condition) do { } while (0) -+# define WARN_ON_ONCE_NONRT(condition) do { } while (0) -+#else -+# define BUG_ON_RT(c) do { } while (0) -+# define BUG_ON_NONRT(c) BUG_ON(c) -+# define WARN_ON_RT(condition) do { } while (0) -+# define WARN_ON_NONRT(condition) WARN_ON(condition) -+# define WARN_ON_ONCE_NONRT(condition) WARN_ON_ONCE(condition) -+#endif -+ - #endif /* __ASSEMBLY__ */ - - #endif -diff -Nur linux-3.18.14.orig/include/linux/blkdev.h linux-3.18.14-rt/include/linux/blkdev.h ---- linux-3.18.14.orig/include/linux/blkdev.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/blkdev.h 2015-05-31 15:32:48.077635370 -0500 -@@ -101,6 +101,7 @@ - struct list_head queuelist; - union { - struct call_single_data csd; -+ struct work_struct work; - unsigned long fifo_time; - }; - -@@ -478,7 +479,7 @@ - struct throtl_data *td; - #endif - struct rcu_head rcu_head; -- wait_queue_head_t mq_freeze_wq; -+ struct swait_head mq_freeze_wq; - struct percpu_ref mq_usage_counter; - struct list_head all_q_node; - -diff -Nur linux-3.18.14.orig/include/linux/blk-mq.h linux-3.18.14-rt/include/linux/blk-mq.h ---- linux-3.18.14.orig/include/linux/blk-mq.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/blk-mq.h 2015-05-31 15:32:48.069635370 -0500 -@@ -169,6 +169,7 @@ - - struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *, const int ctx_index); - struct blk_mq_hw_ctx *blk_mq_alloc_single_hw_queue(struct blk_mq_tag_set *, unsigned int, int); -+void __blk_mq_complete_request_remote_work(struct work_struct *work); - - void blk_mq_start_request(struct request *rq); - void blk_mq_end_request(struct request *rq, int error); -diff -Nur linux-3.18.14.orig/include/linux/bottom_half.h linux-3.18.14-rt/include/linux/bottom_half.h ---- linux-3.18.14.orig/include/linux/bottom_half.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/bottom_half.h 2015-05-31 15:32:48.081635370 -0500 -@@ -4,6 +4,17 @@ - #include - #include - -+#ifdef CONFIG_PREEMPT_RT_FULL -+ -+extern void local_bh_disable(void); -+extern void _local_bh_enable(void); -+extern void local_bh_enable(void); -+extern void local_bh_enable_ip(unsigned long ip); -+extern void __local_bh_disable_ip(unsigned long ip, unsigned int cnt); -+extern void __local_bh_enable_ip(unsigned long ip, unsigned int cnt); -+ -+#else -+ - #ifdef CONFIG_TRACE_IRQFLAGS - extern void __local_bh_disable_ip(unsigned long ip, unsigned int cnt); - #else -@@ -31,5 +42,6 @@ - { - __local_bh_enable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET); - } -+#endif - - #endif /* _LINUX_BH_H */ -diff -Nur linux-3.18.14.orig/include/linux/buffer_head.h linux-3.18.14-rt/include/linux/buffer_head.h ---- linux-3.18.14.orig/include/linux/buffer_head.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/buffer_head.h 2015-05-31 15:32:48.109635370 -0500 -@@ -75,8 +75,52 @@ - struct address_space *b_assoc_map; /* mapping this buffer is - associated with */ - atomic_t b_count; /* users using this buffer_head */ -+#ifdef CONFIG_PREEMPT_RT_BASE -+ spinlock_t b_uptodate_lock; -+#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || \ -+ defined(CONFIG_JBD2) || defined(CONFIG_JBD2_MODULE) -+ spinlock_t b_state_lock; -+ spinlock_t b_journal_head_lock; -+#endif -+#endif - }; - -+static inline unsigned long bh_uptodate_lock_irqsave(struct buffer_head *bh) -+{ -+ unsigned long flags; -+ -+#ifndef CONFIG_PREEMPT_RT_BASE -+ local_irq_save(flags); -+ bit_spin_lock(BH_Uptodate_Lock, &bh->b_state); -+#else -+ spin_lock_irqsave(&bh->b_uptodate_lock, flags); -+#endif -+ return flags; -+} -+ -+static inline void -+bh_uptodate_unlock_irqrestore(struct buffer_head *bh, unsigned long flags) -+{ -+#ifndef CONFIG_PREEMPT_RT_BASE -+ bit_spin_unlock(BH_Uptodate_Lock, &bh->b_state); -+ local_irq_restore(flags); -+#else -+ spin_unlock_irqrestore(&bh->b_uptodate_lock, flags); -+#endif -+} -+ -+static inline void buffer_head_init_locks(struct buffer_head *bh) -+{ -+#ifdef CONFIG_PREEMPT_RT_BASE -+ spin_lock_init(&bh->b_uptodate_lock); -+#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || \ -+ defined(CONFIG_JBD2) || defined(CONFIG_JBD2_MODULE) -+ spin_lock_init(&bh->b_state_lock); -+ spin_lock_init(&bh->b_journal_head_lock); -+#endif -+#endif -+} -+ - /* - * macro tricks to expand the set_buffer_foo(), clear_buffer_foo() - * and buffer_foo() functions. -diff -Nur linux-3.18.14.orig/include/linux/cgroup.h linux-3.18.14-rt/include/linux/cgroup.h ---- linux-3.18.14.orig/include/linux/cgroup.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/cgroup.h 2015-05-31 15:32:48.117635370 -0500 -@@ -22,6 +22,7 @@ - #include - #include - #include -+#include - - #ifdef CONFIG_CGROUPS - -@@ -91,6 +92,7 @@ - /* percpu_ref killing and RCU release */ - struct rcu_head rcu_head; - struct work_struct destroy_work; -+ struct swork_event destroy_swork; - }; - - /* bits in struct cgroup_subsys_state flags field */ -diff -Nur linux-3.18.14.orig/include/linux/completion.h linux-3.18.14-rt/include/linux/completion.h ---- linux-3.18.14.orig/include/linux/completion.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/completion.h 2015-05-31 15:32:48.117635370 -0500 -@@ -7,8 +7,7 @@ - * Atomic wait-for-completion handler data structures. - * See kernel/sched/completion.c for details. - */ -- --#include -+#include - - /* - * struct completion - structure used to maintain state for a "completion" -@@ -24,11 +23,11 @@ - */ - struct completion { - unsigned int done; -- wait_queue_head_t wait; -+ struct swait_head wait; - }; - - #define COMPLETION_INITIALIZER(work) \ -- { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) } -+ { 0, SWAIT_HEAD_INITIALIZER((work).wait) } - - #define COMPLETION_INITIALIZER_ONSTACK(work) \ - ({ init_completion(&work); work; }) -@@ -73,7 +72,7 @@ - static inline void init_completion(struct completion *x) - { - x->done = 0; -- init_waitqueue_head(&x->wait); -+ init_swait_head(&x->wait); - } - - /** -diff -Nur linux-3.18.14.orig/include/linux/cpu.h linux-3.18.14-rt/include/linux/cpu.h ---- linux-3.18.14.orig/include/linux/cpu.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/cpu.h 2015-05-31 15:32:48.129635370 -0500 -@@ -217,6 +217,8 @@ - extern void put_online_cpus(void); - extern void cpu_hotplug_disable(void); - extern void cpu_hotplug_enable(void); -+extern void pin_current_cpu(void); -+extern void unpin_current_cpu(void); - #define hotcpu_notifier(fn, pri) cpu_notifier(fn, pri) - #define __hotcpu_notifier(fn, pri) __cpu_notifier(fn, pri) - #define register_hotcpu_notifier(nb) register_cpu_notifier(nb) -@@ -235,6 +237,8 @@ - #define put_online_cpus() do { } while (0) - #define cpu_hotplug_disable() do { } while (0) - #define cpu_hotplug_enable() do { } while (0) -+static inline void pin_current_cpu(void) { } -+static inline void unpin_current_cpu(void) { } - #define hotcpu_notifier(fn, pri) do { (void)(fn); } while (0) - #define __hotcpu_notifier(fn, pri) do { (void)(fn); } while (0) - /* These aren't inline functions due to a GCC bug. */ -diff -Nur linux-3.18.14.orig/include/linux/delay.h linux-3.18.14-rt/include/linux/delay.h ---- linux-3.18.14.orig/include/linux/delay.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/delay.h 2015-05-31 15:32:48.129635370 -0500 -@@ -52,4 +52,10 @@ - msleep(seconds * 1000); - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+extern void cpu_chill(void); -+#else -+# define cpu_chill() cpu_relax() -+#endif -+ - #endif /* defined(_LINUX_DELAY_H) */ -diff -Nur linux-3.18.14.orig/include/linux/ftrace_event.h linux-3.18.14-rt/include/linux/ftrace_event.h ---- linux-3.18.14.orig/include/linux/ftrace_event.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/ftrace_event.h 2015-05-31 15:32:48.157635370 -0500 -@@ -61,6 +61,9 @@ - unsigned char flags; - unsigned char preempt_count; - int pid; -+ unsigned short migrate_disable; -+ unsigned short padding; -+ unsigned char preempt_lazy_count; - }; - - #define FTRACE_MAX_EVENT \ -diff -Nur linux-3.18.14.orig/include/linux/highmem.h linux-3.18.14-rt/include/linux/highmem.h ---- linux-3.18.14.orig/include/linux/highmem.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/highmem.h 2015-05-31 15:32:48.157635370 -0500 -@@ -7,6 +7,7 @@ - #include - #include - #include -+#include - - #include - -@@ -85,32 +86,51 @@ - - #if defined(CONFIG_HIGHMEM) || defined(CONFIG_X86_32) - -+#ifndef CONFIG_PREEMPT_RT_FULL - DECLARE_PER_CPU(int, __kmap_atomic_idx); -+#endif - - static inline int kmap_atomic_idx_push(void) - { -+#ifndef CONFIG_PREEMPT_RT_FULL - int idx = __this_cpu_inc_return(__kmap_atomic_idx) - 1; - --#ifdef CONFIG_DEBUG_HIGHMEM -+# ifdef CONFIG_DEBUG_HIGHMEM - WARN_ON_ONCE(in_irq() && !irqs_disabled()); - BUG_ON(idx >= KM_TYPE_NR); --#endif -+# endif - return idx; -+#else -+ current->kmap_idx++; -+ BUG_ON(current->kmap_idx > KM_TYPE_NR); -+ return current->kmap_idx - 1; -+#endif - } - - static inline int kmap_atomic_idx(void) - { -+#ifndef CONFIG_PREEMPT_RT_FULL - return __this_cpu_read(__kmap_atomic_idx) - 1; -+#else -+ return current->kmap_idx - 1; -+#endif - } - - static inline void kmap_atomic_idx_pop(void) - { --#ifdef CONFIG_DEBUG_HIGHMEM -+#ifndef CONFIG_PREEMPT_RT_FULL -+# ifdef CONFIG_DEBUG_HIGHMEM - int idx = __this_cpu_dec_return(__kmap_atomic_idx); - - BUG_ON(idx < 0); --#else -+# else - __this_cpu_dec(__kmap_atomic_idx); -+# endif -+#else -+ current->kmap_idx--; -+# ifdef CONFIG_DEBUG_HIGHMEM -+ BUG_ON(current->kmap_idx < 0); -+# endif - #endif - } - -diff -Nur linux-3.18.14.orig/include/linux/hrtimer.h linux-3.18.14-rt/include/linux/hrtimer.h ---- linux-3.18.14.orig/include/linux/hrtimer.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/hrtimer.h 2015-05-31 15:32:48.161635369 -0500 -@@ -111,6 +111,11 @@ - enum hrtimer_restart (*function)(struct hrtimer *); - struct hrtimer_clock_base *base; - unsigned long state; -+ struct list_head cb_entry; -+ int irqsafe; -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ ktime_t praecox; -+#endif - #ifdef CONFIG_TIMER_STATS - int start_pid; - void *start_site; -@@ -147,6 +152,7 @@ - int index; - clockid_t clockid; - struct timerqueue_head active; -+ struct list_head expired; - ktime_t resolution; - ktime_t (*get_time)(void); - ktime_t softirq_time; -@@ -192,6 +198,9 @@ - unsigned long nr_hangs; - ktime_t max_hang_time; - #endif -+#ifdef CONFIG_PREEMPT_RT_BASE -+ wait_queue_head_t wait; -+#endif - struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; - }; - -@@ -379,6 +388,13 @@ - return hrtimer_start_expires(timer, HRTIMER_MODE_ABS); - } - -+/* Softirq preemption could deadlock timer removal */ -+#ifdef CONFIG_PREEMPT_RT_BASE -+ extern void hrtimer_wait_for_timer(const struct hrtimer *timer); -+#else -+# define hrtimer_wait_for_timer(timer) do { cpu_relax(); } while (0) -+#endif -+ - /* Query timers: */ - extern ktime_t hrtimer_get_remaining(const struct hrtimer *timer); - extern int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp); -diff -Nur linux-3.18.14.orig/include/linux/idr.h linux-3.18.14-rt/include/linux/idr.h ---- linux-3.18.14.orig/include/linux/idr.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/idr.h 2015-05-31 15:32:48.161635369 -0500 -@@ -95,10 +95,14 @@ - * Each idr_preload() should be matched with an invocation of this - * function. See idr_preload() for details. - */ -+#ifdef CONFIG_PREEMPT_RT_FULL -+void idr_preload_end(void); -+#else - static inline void idr_preload_end(void) - { - preempt_enable(); - } -+#endif - - /** - * idr_find - return pointer for given id -diff -Nur linux-3.18.14.orig/include/linux/init_task.h linux-3.18.14-rt/include/linux/init_task.h ---- linux-3.18.14.orig/include/linux/init_task.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/init_task.h 2015-05-31 15:32:48.177635369 -0500 -@@ -147,9 +147,16 @@ - # define INIT_PERF_EVENTS(tsk) - #endif - -+#ifdef CONFIG_PREEMPT_RT_BASE -+# define INIT_TIMER_LIST .posix_timer_list = NULL, -+#else -+# define INIT_TIMER_LIST -+#endif -+ - #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN - # define INIT_VTIME(tsk) \ -- .vtime_seqlock = __SEQLOCK_UNLOCKED(tsk.vtime_seqlock), \ -+ .vtime_lock = __RAW_SPIN_LOCK_UNLOCKED(tsk.vtime_lock), \ -+ .vtime_seq = SEQCNT_ZERO(tsk.vtime_seq), \ - .vtime_snap = 0, \ - .vtime_snap_whence = VTIME_SYS, - #else -@@ -219,6 +226,7 @@ - .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \ - .pi_lock = __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock), \ - .timer_slack_ns = 50000, /* 50 usec default slack */ \ -+ INIT_TIMER_LIST \ - .pids = { \ - [PIDTYPE_PID] = INIT_PID_LINK(PIDTYPE_PID), \ - [PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID), \ -diff -Nur linux-3.18.14.orig/include/linux/interrupt.h linux-3.18.14-rt/include/linux/interrupt.h ---- linux-3.18.14.orig/include/linux/interrupt.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/interrupt.h 2015-05-31 15:32:48.181635369 -0500 -@@ -57,6 +57,7 @@ - * IRQF_NO_THREAD - Interrupt cannot be threaded - * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device - * resume time. -+ * IRQF_NO_SOFTIRQ_CALL - Do not process softirqs in the irq thread context (RT) - */ - #define IRQF_DISABLED 0x00000020 - #define IRQF_SHARED 0x00000080 -@@ -70,6 +71,7 @@ - #define IRQF_FORCE_RESUME 0x00008000 - #define IRQF_NO_THREAD 0x00010000 - #define IRQF_EARLY_RESUME 0x00020000 -+#define IRQF_NO_SOFTIRQ_CALL 0x00080000 - - #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) - -@@ -180,7 +182,7 @@ - #ifdef CONFIG_LOCKDEP - # define local_irq_enable_in_hardirq() do { } while (0) - #else --# define local_irq_enable_in_hardirq() local_irq_enable() -+# define local_irq_enable_in_hardirq() local_irq_enable_nort() - #endif - - extern void disable_irq_nosync(unsigned int irq); -@@ -210,6 +212,7 @@ - unsigned int irq; - struct kref kref; - struct work_struct work; -+ struct list_head list; - void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask); - void (*release)(struct kref *ref); - }; -@@ -358,9 +361,13 @@ - - - #ifdef CONFIG_IRQ_FORCED_THREADING -+# ifndef CONFIG_PREEMPT_RT_BASE - extern bool force_irqthreads; -+# else -+# define force_irqthreads (true) -+# endif - #else --#define force_irqthreads (0) -+#define force_irqthreads (false) - #endif - - #ifndef __ARCH_SET_SOFTIRQ_PENDING -@@ -416,9 +423,10 @@ - void (*action)(struct softirq_action *); - }; - -+#ifndef CONFIG_PREEMPT_RT_FULL - asmlinkage void do_softirq(void); - asmlinkage void __do_softirq(void); -- -+static inline void thread_do_softirq(void) { do_softirq(); } - #ifdef __ARCH_HAS_DO_SOFTIRQ - void do_softirq_own_stack(void); - #else -@@ -427,6 +435,9 @@ - __do_softirq(); - } - #endif -+#else -+extern void thread_do_softirq(void); -+#endif - - extern void open_softirq(int nr, void (*action)(struct softirq_action *)); - extern void softirq_init(void); -@@ -434,6 +445,7 @@ - - extern void raise_softirq_irqoff(unsigned int nr); - extern void raise_softirq(unsigned int nr); -+extern void softirq_check_pending_idle(void); - - DECLARE_PER_CPU(struct task_struct *, ksoftirqd); - -@@ -455,8 +467,9 @@ - to be executed on some cpu at least once after this. - * If the tasklet is already scheduled, but its execution is still not - started, it will be executed only once. -- * If this tasklet is already running on another CPU (or schedule is called -- from tasklet itself), it is rescheduled for later. -+ * If this tasklet is already running on another CPU, it is rescheduled -+ for later. -+ * Schedule must not be called from the tasklet itself (a lockup occurs) - * Tasklet is strictly serialized wrt itself, but not - wrt another tasklets. If client needs some intertask synchronization, - he makes it with spinlocks. -@@ -481,27 +494,36 @@ - enum - { - TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */ -- TASKLET_STATE_RUN /* Tasklet is running (SMP only) */ -+ TASKLET_STATE_RUN, /* Tasklet is running (SMP only) */ -+ TASKLET_STATE_PENDING /* Tasklet is pending */ - }; - --#ifdef CONFIG_SMP -+#define TASKLET_STATEF_SCHED (1 << TASKLET_STATE_SCHED) -+#define TASKLET_STATEF_RUN (1 << TASKLET_STATE_RUN) -+#define TASKLET_STATEF_PENDING (1 << TASKLET_STATE_PENDING) -+ -+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) - static inline int tasklet_trylock(struct tasklet_struct *t) - { - return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state); - } - -+static inline int tasklet_tryunlock(struct tasklet_struct *t) -+{ -+ return cmpxchg(&t->state, TASKLET_STATEF_RUN, 0) == TASKLET_STATEF_RUN; -+} -+ - static inline void tasklet_unlock(struct tasklet_struct *t) - { - smp_mb__before_atomic(); - clear_bit(TASKLET_STATE_RUN, &(t)->state); - } - --static inline void tasklet_unlock_wait(struct tasklet_struct *t) --{ -- while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); } --} -+extern void tasklet_unlock_wait(struct tasklet_struct *t); -+ - #else - #define tasklet_trylock(t) 1 -+#define tasklet_tryunlock(t) 1 - #define tasklet_unlock_wait(t) do { } while (0) - #define tasklet_unlock(t) do { } while (0) - #endif -@@ -550,17 +572,8 @@ - smp_mb(); - } - --static inline void tasklet_enable(struct tasklet_struct *t) --{ -- smp_mb__before_atomic(); -- atomic_dec(&t->count); --} -- --static inline void tasklet_hi_enable(struct tasklet_struct *t) --{ -- smp_mb__before_atomic(); -- atomic_dec(&t->count); --} -+extern void tasklet_enable(struct tasklet_struct *t); -+extern void tasklet_hi_enable(struct tasklet_struct *t); - - extern void tasklet_kill(struct tasklet_struct *t); - extern void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu); -@@ -592,6 +605,12 @@ - tasklet_kill(&ttimer->tasklet); - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+extern void softirq_early_init(void); -+#else -+static inline void softirq_early_init(void) { } -+#endif -+ - /* - * Autoprobing for irqs: - * -diff -Nur linux-3.18.14.orig/include/linux/irqdesc.h linux-3.18.14-rt/include/linux/irqdesc.h ---- linux-3.18.14.orig/include/linux/irqdesc.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/irqdesc.h 2015-05-31 15:32:48.217635369 -0500 -@@ -63,6 +63,7 @@ - unsigned int irqs_unhandled; - atomic_t threads_handled; - int threads_handled_last; -+ u64 random_ip; - raw_spinlock_t lock; - struct cpumask *percpu_enabled; - #ifdef CONFIG_SMP -diff -Nur linux-3.18.14.orig/include/linux/irqflags.h linux-3.18.14-rt/include/linux/irqflags.h ---- linux-3.18.14.orig/include/linux/irqflags.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/irqflags.h 2015-05-31 15:32:48.233635369 -0500 -@@ -25,8 +25,6 @@ - # define trace_softirqs_enabled(p) ((p)->softirqs_enabled) - # define trace_hardirq_enter() do { current->hardirq_context++; } while (0) - # define trace_hardirq_exit() do { current->hardirq_context--; } while (0) --# define lockdep_softirq_enter() do { current->softirq_context++; } while (0) --# define lockdep_softirq_exit() do { current->softirq_context--; } while (0) - # define INIT_TRACE_IRQFLAGS .softirqs_enabled = 1, - #else - # define trace_hardirqs_on() do { } while (0) -@@ -39,9 +37,15 @@ - # define trace_softirqs_enabled(p) 0 - # define trace_hardirq_enter() do { } while (0) - # define trace_hardirq_exit() do { } while (0) -+# define INIT_TRACE_IRQFLAGS -+#endif -+ -+#if defined(CONFIG_TRACE_IRQFLAGS) && !defined(CONFIG_PREEMPT_RT_FULL) -+# define lockdep_softirq_enter() do { current->softirq_context++; } while (0) -+# define lockdep_softirq_exit() do { current->softirq_context--; } while (0) -+#else - # define lockdep_softirq_enter() do { } while (0) - # define lockdep_softirq_exit() do { } while (0) --# define INIT_TRACE_IRQFLAGS - #endif - - #if defined(CONFIG_IRQSOFF_TRACER) || \ -@@ -147,4 +151,23 @@ - - #endif /* CONFIG_TRACE_IRQFLAGS_SUPPORT */ - -+/* -+ * local_irq* variants depending on RT/!RT -+ */ -+#ifdef CONFIG_PREEMPT_RT_FULL -+# define local_irq_disable_nort() do { } while (0) -+# define local_irq_enable_nort() do { } while (0) -+# define local_irq_save_nort(flags) local_save_flags(flags) -+# define local_irq_restore_nort(flags) (void)(flags) -+# define local_irq_disable_rt() local_irq_disable() -+# define local_irq_enable_rt() local_irq_enable() -+#else -+# define local_irq_disable_nort() local_irq_disable() -+# define local_irq_enable_nort() local_irq_enable() -+# define local_irq_save_nort(flags) local_irq_save(flags) -+# define local_irq_restore_nort(flags) local_irq_restore(flags) -+# define local_irq_disable_rt() do { } while (0) -+# define local_irq_enable_rt() do { } while (0) -+#endif -+ - #endif -diff -Nur linux-3.18.14.orig/include/linux/irq.h linux-3.18.14-rt/include/linux/irq.h ---- linux-3.18.14.orig/include/linux/irq.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/irq.h 2015-05-31 15:32:48.185635369 -0500 -@@ -73,6 +73,7 @@ - * IRQ_IS_POLLED - Always polled by another interrupt. Exclude - * it from the spurious interrupt detection - * mechanism and from core side polling. -+ * IRQ_NO_SOFTIRQ_CALL - No softirq processing in the irq thread context (RT) - */ - enum { - IRQ_TYPE_NONE = 0x00000000, -@@ -98,13 +99,14 @@ - IRQ_NOTHREAD = (1 << 16), - IRQ_PER_CPU_DEVID = (1 << 17), - IRQ_IS_POLLED = (1 << 18), -+ IRQ_NO_SOFTIRQ_CALL = (1 << 19), - }; - - #define IRQF_MODIFY_MASK \ - (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ - IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \ - IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID | \ -- IRQ_IS_POLLED) -+ IRQ_IS_POLLED | IRQ_NO_SOFTIRQ_CALL) - - #define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING) - -diff -Nur linux-3.18.14.orig/include/linux/irq_work.h linux-3.18.14-rt/include/linux/irq_work.h ---- linux-3.18.14.orig/include/linux/irq_work.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/irq_work.h 2015-05-31 15:32:48.217635369 -0500 -@@ -16,6 +16,7 @@ - #define IRQ_WORK_BUSY 2UL - #define IRQ_WORK_FLAGS 3UL - #define IRQ_WORK_LAZY 4UL /* Doesn't want IPI, wait for tick */ -+#define IRQ_WORK_HARD_IRQ 8UL /* Run hard IRQ context, even on RT */ - - struct irq_work { - unsigned long flags; -diff -Nur linux-3.18.14.orig/include/linux/jbd_common.h linux-3.18.14-rt/include/linux/jbd_common.h ---- linux-3.18.14.orig/include/linux/jbd_common.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/jbd_common.h 2015-05-31 15:32:48.237635369 -0500 -@@ -15,32 +15,56 @@ - - static inline void jbd_lock_bh_state(struct buffer_head *bh) - { -+#ifndef CONFIG_PREEMPT_RT_BASE - bit_spin_lock(BH_State, &bh->b_state); -+#else -+ spin_lock(&bh->b_state_lock); -+#endif - } - - static inline int jbd_trylock_bh_state(struct buffer_head *bh) - { -+#ifndef CONFIG_PREEMPT_RT_BASE - return bit_spin_trylock(BH_State, &bh->b_state); -+#else -+ return spin_trylock(&bh->b_state_lock); -+#endif - } - - static inline int jbd_is_locked_bh_state(struct buffer_head *bh) - { -+#ifndef CONFIG_PREEMPT_RT_BASE - return bit_spin_is_locked(BH_State, &bh->b_state); -+#else -+ return spin_is_locked(&bh->b_state_lock); -+#endif - } - - static inline void jbd_unlock_bh_state(struct buffer_head *bh) - { -+#ifndef CONFIG_PREEMPT_RT_BASE - bit_spin_unlock(BH_State, &bh->b_state); -+#else -+ spin_unlock(&bh->b_state_lock); -+#endif - } - - static inline void jbd_lock_bh_journal_head(struct buffer_head *bh) - { -+#ifndef CONFIG_PREEMPT_RT_BASE - bit_spin_lock(BH_JournalHead, &bh->b_state); -+#else -+ spin_lock(&bh->b_journal_head_lock); -+#endif - } - - static inline void jbd_unlock_bh_journal_head(struct buffer_head *bh) - { -+#ifndef CONFIG_PREEMPT_RT_BASE - bit_spin_unlock(BH_JournalHead, &bh->b_state); -+#else -+ spin_unlock(&bh->b_journal_head_lock); -+#endif - } - - #endif -diff -Nur linux-3.18.14.orig/include/linux/jump_label.h linux-3.18.14-rt/include/linux/jump_label.h ---- linux-3.18.14.orig/include/linux/jump_label.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/jump_label.h 2015-05-31 15:32:48.237635369 -0500 -@@ -55,7 +55,8 @@ - "%s used before call to jump_label_init", \ - __func__) - --#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL) -+#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL) && \ -+ !defined(CONFIG_PREEMPT_BASE) - - struct static_key { - atomic_t enabled; -diff -Nur linux-3.18.14.orig/include/linux/kdb.h linux-3.18.14-rt/include/linux/kdb.h ---- linux-3.18.14.orig/include/linux/kdb.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/kdb.h 2015-05-31 15:32:48.245635369 -0500 -@@ -116,7 +116,7 @@ - extern __printf(1, 0) int vkdb_printf(const char *fmt, va_list args); - extern __printf(1, 2) int kdb_printf(const char *, ...); - typedef __printf(1, 2) int (*kdb_printf_t)(const char *, ...); -- -+#define in_kdb_printk() (kdb_trap_printk) - extern void kdb_init(int level); - - /* Access to kdb specific polling devices */ -@@ -151,6 +151,7 @@ - extern int kdb_unregister(char *); - #else /* ! CONFIG_KGDB_KDB */ - static inline __printf(1, 2) int kdb_printf(const char *fmt, ...) { return 0; } -+#define in_kdb_printk() (0) - static inline void kdb_init(int level) {} - static inline int kdb_register(char *cmd, kdb_func_t func, char *usage, - char *help, short minlen) { return 0; } -diff -Nur linux-3.18.14.orig/include/linux/kernel.h linux-3.18.14-rt/include/linux/kernel.h ---- linux-3.18.14.orig/include/linux/kernel.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/kernel.h 2015-05-31 15:32:48.245635369 -0500 -@@ -451,6 +451,7 @@ - SYSTEM_HALT, - SYSTEM_POWER_OFF, - SYSTEM_RESTART, -+ SYSTEM_SUSPEND, - } system_state; - - #define TAINT_PROPRIETARY_MODULE 0 -diff -Nur linux-3.18.14.orig/include/linux/kvm_host.h linux-3.18.14-rt/include/linux/kvm_host.h ---- linux-3.18.14.orig/include/linux/kvm_host.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/kvm_host.h 2015-05-31 15:32:48.253635368 -0500 -@@ -245,7 +245,7 @@ - - int fpu_active; - int guest_fpu_loaded, guest_xcr0_loaded; -- wait_queue_head_t wq; -+ struct swait_head wq; - struct pid *pid; - int sigset_active; - sigset_t sigset; -@@ -688,7 +688,7 @@ - } - #endif - --static inline wait_queue_head_t *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) -+static inline struct swait_head *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) - { - #ifdef __KVM_HAVE_ARCH_WQP - return vcpu->arch.wqp; -diff -Nur linux-3.18.14.orig/include/linux/kvm_host.h.orig linux-3.18.14-rt/include/linux/kvm_host.h.orig ---- linux-3.18.14.orig/include/linux/kvm_host.h.orig 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/kvm_host.h.orig 2015-05-20 10:04:50.000000000 -0500 -@@ -0,0 +1,1111 @@ -+#ifndef __KVM_HOST_H -+#define __KVM_HOST_H -+ -+/* -+ * This work is licensed under the terms of the GNU GPL, version 2. See -+ * the COPYING file in the top-level directory. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include -+ -+#ifndef KVM_MMIO_SIZE -+#define KVM_MMIO_SIZE 8 -+#endif -+ -+/* -+ * The bit 16 ~ bit 31 of kvm_memory_region::flags are internally used -+ * in kvm, other bits are visible for userspace which are defined in -+ * include/linux/kvm_h. -+ */ -+#define KVM_MEMSLOT_INVALID (1UL << 16) -+#define KVM_MEMSLOT_INCOHERENT (1UL << 17) -+ -+/* Two fragments for cross MMIO pages. */ -+#define KVM_MAX_MMIO_FRAGMENTS 2 -+ -+/* -+ * For the normal pfn, the highest 12 bits should be zero, -+ * so we can mask bit 62 ~ bit 52 to indicate the error pfn, -+ * mask bit 63 to indicate the noslot pfn. -+ */ -+#define KVM_PFN_ERR_MASK (0x7ffULL << 52) -+#define KVM_PFN_ERR_NOSLOT_MASK (0xfffULL << 52) -+#define KVM_PFN_NOSLOT (0x1ULL << 63) -+ -+#define KVM_PFN_ERR_FAULT (KVM_PFN_ERR_MASK) -+#define KVM_PFN_ERR_HWPOISON (KVM_PFN_ERR_MASK + 1) -+#define KVM_PFN_ERR_RO_FAULT (KVM_PFN_ERR_MASK + 2) -+ -+/* -+ * error pfns indicate that the gfn is in slot but faild to -+ * translate it to pfn on host. -+ */ -+static inline bool is_error_pfn(pfn_t pfn) -+{ -+ return !!(pfn & KVM_PFN_ERR_MASK); -+} -+ -+/* -+ * error_noslot pfns indicate that the gfn can not be -+ * translated to pfn - it is not in slot or failed to -+ * translate it to pfn. -+ */ -+static inline bool is_error_noslot_pfn(pfn_t pfn) -+{ -+ return !!(pfn & KVM_PFN_ERR_NOSLOT_MASK); -+} -+ -+/* noslot pfn indicates that the gfn is not in slot. */ -+static inline bool is_noslot_pfn(pfn_t pfn) -+{ -+ return pfn == KVM_PFN_NOSLOT; -+} -+ -+/* -+ * architectures with KVM_HVA_ERR_BAD other than PAGE_OFFSET (e.g. s390) -+ * provide own defines and kvm_is_error_hva -+ */ -+#ifndef KVM_HVA_ERR_BAD -+ -+#define KVM_HVA_ERR_BAD (PAGE_OFFSET) -+#define KVM_HVA_ERR_RO_BAD (PAGE_OFFSET + PAGE_SIZE) -+ -+static inline bool kvm_is_error_hva(unsigned long addr) -+{ -+ return addr >= PAGE_OFFSET; -+} -+ -+#endif -+ -+#define KVM_ERR_PTR_BAD_PAGE (ERR_PTR(-ENOENT)) -+ -+static inline bool is_error_page(struct page *page) -+{ -+ return IS_ERR(page); -+} -+ -+/* -+ * vcpu->requests bit members -+ */ -+#define KVM_REQ_TLB_FLUSH 0 -+#define KVM_REQ_MIGRATE_TIMER 1 -+#define KVM_REQ_REPORT_TPR_ACCESS 2 -+#define KVM_REQ_MMU_RELOAD 3 -+#define KVM_REQ_TRIPLE_FAULT 4 -+#define KVM_REQ_PENDING_TIMER 5 -+#define KVM_REQ_UNHALT 6 -+#define KVM_REQ_MMU_SYNC 7 -+#define KVM_REQ_CLOCK_UPDATE 8 -+#define KVM_REQ_KICK 9 -+#define KVM_REQ_DEACTIVATE_FPU 10 -+#define KVM_REQ_EVENT 11 -+#define KVM_REQ_APF_HALT 12 -+#define KVM_REQ_STEAL_UPDATE 13 -+#define KVM_REQ_NMI 14 -+#define KVM_REQ_PMU 15 -+#define KVM_REQ_PMI 16 -+#define KVM_REQ_WATCHDOG 17 -+#define KVM_REQ_MASTERCLOCK_UPDATE 18 -+#define KVM_REQ_MCLOCK_INPROGRESS 19 -+#define KVM_REQ_EPR_EXIT 20 -+#define KVM_REQ_SCAN_IOAPIC 21 -+#define KVM_REQ_GLOBAL_CLOCK_UPDATE 22 -+#define KVM_REQ_ENABLE_IBS 23 -+#define KVM_REQ_DISABLE_IBS 24 -+#define KVM_REQ_APIC_PAGE_RELOAD 25 -+ -+#define KVM_USERSPACE_IRQ_SOURCE_ID 0 -+#define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1 -+ -+extern struct kmem_cache *kvm_vcpu_cache; -+ -+extern spinlock_t kvm_lock; -+extern struct list_head vm_list; -+ -+struct kvm_io_range { -+ gpa_t addr; -+ int len; -+ struct kvm_io_device *dev; -+}; -+ -+#define NR_IOBUS_DEVS 1000 -+ -+struct kvm_io_bus { -+ int dev_count; -+ int ioeventfd_count; -+ struct kvm_io_range range[]; -+}; -+ -+enum kvm_bus { -+ KVM_MMIO_BUS, -+ KVM_PIO_BUS, -+ KVM_VIRTIO_CCW_NOTIFY_BUS, -+ KVM_FAST_MMIO_BUS, -+ KVM_NR_BUSES -+}; -+ -+int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, -+ int len, const void *val); -+int kvm_io_bus_write_cookie(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, -+ int len, const void *val, long cookie); -+int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, -+ void *val); -+int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, -+ int len, struct kvm_io_device *dev); -+int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, -+ struct kvm_io_device *dev); -+ -+#ifdef CONFIG_KVM_ASYNC_PF -+struct kvm_async_pf { -+ struct work_struct work; -+ struct list_head link; -+ struct list_head queue; -+ struct kvm_vcpu *vcpu; -+ struct mm_struct *mm; -+ gva_t gva; -+ unsigned long addr; -+ struct kvm_arch_async_pf arch; -+ bool wakeup_all; -+}; -+ -+void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu); -+void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu); -+int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, unsigned long hva, -+ struct kvm_arch_async_pf *arch); -+int kvm_async_pf_wakeup_all(struct kvm_vcpu *vcpu); -+#endif -+ -+/* -+ * Carry out a gup that requires IO. Allow the mm to relinquish the mmap -+ * semaphore if the filemap/swap has to wait on a page lock. pagep == NULL -+ * controls whether we retry the gup one more time to completion in that case. -+ * Typically this is called after a FAULT_FLAG_RETRY_NOWAIT in the main tdp -+ * handler. -+ */ -+int kvm_get_user_page_io(struct task_struct *tsk, struct mm_struct *mm, -+ unsigned long addr, bool write_fault, -+ struct page **pagep); -+ -+enum { -+ OUTSIDE_GUEST_MODE, -+ IN_GUEST_MODE, -+ EXITING_GUEST_MODE, -+ READING_SHADOW_PAGE_TABLES, -+}; -+ -+/* -+ * Sometimes a large or cross-page mmio needs to be broken up into separate -+ * exits for userspace servicing. -+ */ -+struct kvm_mmio_fragment { -+ gpa_t gpa; -+ void *data; -+ unsigned len; -+}; -+ -+struct kvm_vcpu { -+ struct kvm *kvm; -+#ifdef CONFIG_PREEMPT_NOTIFIERS -+ struct preempt_notifier preempt_notifier; -+#endif -+ int cpu; -+ int vcpu_id; -+ int srcu_idx; -+ int mode; -+ unsigned long requests; -+ unsigned long guest_debug; -+ -+ struct mutex mutex; -+ struct kvm_run *run; -+ -+ int fpu_active; -+ int guest_fpu_loaded, guest_xcr0_loaded; -+ wait_queue_head_t wq; -+ struct pid *pid; -+ int sigset_active; -+ sigset_t sigset; -+ struct kvm_vcpu_stat stat; -+ -+#ifdef CONFIG_HAS_IOMEM -+ int mmio_needed; -+ int mmio_read_completed; -+ int mmio_is_write; -+ int mmio_cur_fragment; -+ int mmio_nr_fragments; -+ struct kvm_mmio_fragment mmio_fragments[KVM_MAX_MMIO_FRAGMENTS]; -+#endif -+ -+#ifdef CONFIG_KVM_ASYNC_PF -+ struct { -+ u32 queued; -+ struct list_head queue; -+ struct list_head done; -+ spinlock_t lock; -+ } async_pf; -+#endif -+ -+#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT -+ /* -+ * Cpu relax intercept or pause loop exit optimization -+ * in_spin_loop: set when a vcpu does a pause loop exit -+ * or cpu relax intercepted. -+ * dy_eligible: indicates whether vcpu is eligible for directed yield. -+ */ -+ struct { -+ bool in_spin_loop; -+ bool dy_eligible; -+ } spin_loop; -+#endif -+ bool preempted; -+ struct kvm_vcpu_arch arch; -+}; -+ -+static inline int kvm_vcpu_exiting_guest_mode(struct kvm_vcpu *vcpu) -+{ -+ return cmpxchg(&vcpu->mode, IN_GUEST_MODE, EXITING_GUEST_MODE); -+} -+ -+/* -+ * Some of the bitops functions do not support too long bitmaps. -+ * This number must be determined not to exceed such limits. -+ */ -+#define KVM_MEM_MAX_NR_PAGES ((1UL << 31) - 1) -+ -+struct kvm_memory_slot { -+ gfn_t base_gfn; -+ unsigned long npages; -+ unsigned long *dirty_bitmap; -+ struct kvm_arch_memory_slot arch; -+ unsigned long userspace_addr; -+ u32 flags; -+ short id; -+}; -+ -+static inline unsigned long kvm_dirty_bitmap_bytes(struct kvm_memory_slot *memslot) -+{ -+ return ALIGN(memslot->npages, BITS_PER_LONG) / 8; -+} -+ -+struct kvm_s390_adapter_int { -+ u64 ind_addr; -+ u64 summary_addr; -+ u64 ind_offset; -+ u32 summary_offset; -+ u32 adapter_id; -+}; -+ -+struct kvm_kernel_irq_routing_entry { -+ u32 gsi; -+ u32 type; -+ int (*set)(struct kvm_kernel_irq_routing_entry *e, -+ struct kvm *kvm, int irq_source_id, int level, -+ bool line_status); -+ union { -+ struct { -+ unsigned irqchip; -+ unsigned pin; -+ } irqchip; -+ struct msi_msg msi; -+ struct kvm_s390_adapter_int adapter; -+ }; -+ struct hlist_node link; -+}; -+ -+#ifndef KVM_PRIVATE_MEM_SLOTS -+#define KVM_PRIVATE_MEM_SLOTS 0 -+#endif -+ -+#ifndef KVM_MEM_SLOTS_NUM -+#define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS) -+#endif -+ -+/* -+ * Note: -+ * memslots are not sorted by id anymore, please use id_to_memslot() -+ * to get the memslot by its id. -+ */ -+struct kvm_memslots { -+ u64 generation; -+ struct kvm_memory_slot memslots[KVM_MEM_SLOTS_NUM]; -+ /* The mapping table from slot id to the index in memslots[]. */ -+ short id_to_index[KVM_MEM_SLOTS_NUM]; -+}; -+ -+struct kvm { -+ spinlock_t mmu_lock; -+ struct mutex slots_lock; -+ struct mm_struct *mm; /* userspace tied to this vm */ -+ struct kvm_memslots *memslots; -+ struct srcu_struct srcu; -+ struct srcu_struct irq_srcu; -+#ifdef CONFIG_KVM_APIC_ARCHITECTURE -+ u32 bsp_vcpu_id; -+#endif -+ struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; -+ atomic_t online_vcpus; -+ int last_boosted_vcpu; -+ struct list_head vm_list; -+ struct mutex lock; -+ struct kvm_io_bus *buses[KVM_NR_BUSES]; -+#ifdef CONFIG_HAVE_KVM_EVENTFD -+ struct { -+ spinlock_t lock; -+ struct list_head items; -+ struct list_head resampler_list; -+ struct mutex resampler_lock; -+ } irqfds; -+ struct list_head ioeventfds; -+#endif -+ struct kvm_vm_stat stat; -+ struct kvm_arch arch; -+ atomic_t users_count; -+#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET -+ struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; -+ spinlock_t ring_lock; -+ struct list_head coalesced_zones; -+#endif -+ -+ struct mutex irq_lock; -+#ifdef CONFIG_HAVE_KVM_IRQCHIP -+ /* -+ * Update side is protected by irq_lock. -+ */ -+ struct kvm_irq_routing_table __rcu *irq_routing; -+ struct hlist_head mask_notifier_list; -+#endif -+#ifdef CONFIG_HAVE_KVM_IRQFD -+ struct hlist_head irq_ack_notifier_list; -+#endif -+ -+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) -+ struct mmu_notifier mmu_notifier; -+ unsigned long mmu_notifier_seq; -+ long mmu_notifier_count; -+#endif -+ long tlbs_dirty; -+ struct list_head devices; -+}; -+ -+#define kvm_err(fmt, ...) \ -+ pr_err("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) -+#define kvm_info(fmt, ...) \ -+ pr_info("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) -+#define kvm_debug(fmt, ...) \ -+ pr_debug("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) -+#define kvm_pr_unimpl(fmt, ...) \ -+ pr_err_ratelimited("kvm [%i]: " fmt, \ -+ task_tgid_nr(current), ## __VA_ARGS__) -+ -+/* The guest did something we don't support. */ -+#define vcpu_unimpl(vcpu, fmt, ...) \ -+ kvm_pr_unimpl("vcpu%i " fmt, (vcpu)->vcpu_id, ## __VA_ARGS__) -+ -+static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i) -+{ -+ smp_rmb(); -+ return kvm->vcpus[i]; -+} -+ -+#define kvm_for_each_vcpu(idx, vcpup, kvm) \ -+ for (idx = 0; \ -+ idx < atomic_read(&kvm->online_vcpus) && \ -+ (vcpup = kvm_get_vcpu(kvm, idx)) != NULL; \ -+ idx++) -+ -+#define kvm_for_each_memslot(memslot, slots) \ -+ for (memslot = &slots->memslots[0]; \ -+ memslot < slots->memslots + KVM_MEM_SLOTS_NUM && memslot->npages;\ -+ memslot++) -+ -+int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id); -+void kvm_vcpu_uninit(struct kvm_vcpu *vcpu); -+ -+int __must_check vcpu_load(struct kvm_vcpu *vcpu); -+void vcpu_put(struct kvm_vcpu *vcpu); -+ -+#ifdef CONFIG_HAVE_KVM_IRQFD -+int kvm_irqfd_init(void); -+void kvm_irqfd_exit(void); -+#else -+static inline int kvm_irqfd_init(void) -+{ -+ return 0; -+} -+ -+static inline void kvm_irqfd_exit(void) -+{ -+} -+#endif -+int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, -+ struct module *module); -+void kvm_exit(void); -+ -+void kvm_get_kvm(struct kvm *kvm); -+void kvm_put_kvm(struct kvm *kvm); -+ -+static inline struct kvm_memslots *kvm_memslots(struct kvm *kvm) -+{ -+ return rcu_dereference_check(kvm->memslots, -+ srcu_read_lock_held(&kvm->srcu) -+ || lockdep_is_held(&kvm->slots_lock)); -+} -+ -+static inline struct kvm_memory_slot * -+id_to_memslot(struct kvm_memslots *slots, int id) -+{ -+ int index = slots->id_to_index[id]; -+ struct kvm_memory_slot *slot; -+ -+ slot = &slots->memslots[index]; -+ -+ WARN_ON(slot->id != id); -+ return slot; -+} -+ -+/* -+ * KVM_SET_USER_MEMORY_REGION ioctl allows the following operations: -+ * - create a new memory slot -+ * - delete an existing memory slot -+ * - modify an existing memory slot -+ * -- move it in the guest physical memory space -+ * -- just change its flags -+ * -+ * Since flags can be changed by some of these operations, the following -+ * differentiation is the best we can do for __kvm_set_memory_region(): -+ */ -+enum kvm_mr_change { -+ KVM_MR_CREATE, -+ KVM_MR_DELETE, -+ KVM_MR_MOVE, -+ KVM_MR_FLAGS_ONLY, -+}; -+ -+int kvm_set_memory_region(struct kvm *kvm, -+ struct kvm_userspace_memory_region *mem); -+int __kvm_set_memory_region(struct kvm *kvm, -+ struct kvm_userspace_memory_region *mem); -+void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free, -+ struct kvm_memory_slot *dont); -+int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, -+ unsigned long npages); -+void kvm_arch_memslots_updated(struct kvm *kvm); -+int kvm_arch_prepare_memory_region(struct kvm *kvm, -+ struct kvm_memory_slot *memslot, -+ struct kvm_userspace_memory_region *mem, -+ enum kvm_mr_change change); -+void kvm_arch_commit_memory_region(struct kvm *kvm, -+ struct kvm_userspace_memory_region *mem, -+ const struct kvm_memory_slot *old, -+ enum kvm_mr_change change); -+bool kvm_largepages_enabled(void); -+void kvm_disable_largepages(void); -+/* flush all memory translations */ -+void kvm_arch_flush_shadow_all(struct kvm *kvm); -+/* flush memory translations pointing to 'slot' */ -+void kvm_arch_flush_shadow_memslot(struct kvm *kvm, -+ struct kvm_memory_slot *slot); -+ -+int gfn_to_page_many_atomic(struct kvm *kvm, gfn_t gfn, struct page **pages, -+ int nr_pages); -+ -+struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); -+unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn); -+unsigned long gfn_to_hva_prot(struct kvm *kvm, gfn_t gfn, bool *writable); -+unsigned long gfn_to_hva_memslot(struct kvm_memory_slot *slot, gfn_t gfn); -+unsigned long gfn_to_hva_memslot_prot(struct kvm_memory_slot *slot, gfn_t gfn, -+ bool *writable); -+void kvm_release_page_clean(struct page *page); -+void kvm_release_page_dirty(struct page *page); -+void kvm_set_page_accessed(struct page *page); -+ -+pfn_t gfn_to_pfn_atomic(struct kvm *kvm, gfn_t gfn); -+pfn_t gfn_to_pfn_async(struct kvm *kvm, gfn_t gfn, bool *async, -+ bool write_fault, bool *writable); -+pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn); -+pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault, -+ bool *writable); -+pfn_t gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn); -+pfn_t gfn_to_pfn_memslot_atomic(struct kvm_memory_slot *slot, gfn_t gfn); -+ -+void kvm_release_pfn_clean(pfn_t pfn); -+void kvm_set_pfn_dirty(pfn_t pfn); -+void kvm_set_pfn_accessed(pfn_t pfn); -+void kvm_get_pfn(pfn_t pfn); -+ -+int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset, -+ int len); -+int kvm_read_guest_atomic(struct kvm *kvm, gpa_t gpa, void *data, -+ unsigned long len); -+int kvm_read_guest(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len); -+int kvm_read_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, -+ void *data, unsigned long len); -+int kvm_write_guest_page(struct kvm *kvm, gfn_t gfn, const void *data, -+ int offset, int len); -+int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data, -+ unsigned long len); -+int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, -+ void *data, unsigned long len); -+int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, -+ gpa_t gpa, unsigned long len); -+int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len); -+int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len); -+struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn); -+int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn); -+unsigned long kvm_host_page_size(struct kvm *kvm, gfn_t gfn); -+void mark_page_dirty(struct kvm *kvm, gfn_t gfn); -+ -+void kvm_vcpu_block(struct kvm_vcpu *vcpu); -+void kvm_vcpu_kick(struct kvm_vcpu *vcpu); -+int kvm_vcpu_yield_to(struct kvm_vcpu *target); -+void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu); -+void kvm_load_guest_fpu(struct kvm_vcpu *vcpu); -+void kvm_put_guest_fpu(struct kvm_vcpu *vcpu); -+ -+void kvm_flush_remote_tlbs(struct kvm *kvm); -+void kvm_reload_remote_mmus(struct kvm *kvm); -+void kvm_make_mclock_inprogress_request(struct kvm *kvm); -+void kvm_make_scan_ioapic_request(struct kvm *kvm); -+bool kvm_make_all_cpus_request(struct kvm *kvm, unsigned int req); -+ -+long kvm_arch_dev_ioctl(struct file *filp, -+ unsigned int ioctl, unsigned long arg); -+long kvm_arch_vcpu_ioctl(struct file *filp, -+ unsigned int ioctl, unsigned long arg); -+int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf); -+ -+int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext); -+ -+int kvm_get_dirty_log(struct kvm *kvm, -+ struct kvm_dirty_log *log, int *is_dirty); -+int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, -+ struct kvm_dirty_log *log); -+ -+int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, -+ bool line_status); -+long kvm_arch_vm_ioctl(struct file *filp, -+ unsigned int ioctl, unsigned long arg); -+ -+int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu); -+int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu); -+ -+int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, -+ struct kvm_translation *tr); -+ -+int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); -+int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); -+int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, -+ struct kvm_sregs *sregs); -+int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, -+ struct kvm_sregs *sregs); -+int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, -+ struct kvm_mp_state *mp_state); -+int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, -+ struct kvm_mp_state *mp_state); -+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, -+ struct kvm_guest_debug *dbg); -+int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run); -+ -+int kvm_arch_init(void *opaque); -+void kvm_arch_exit(void); -+ -+int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu); -+void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu); -+ -+void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu); -+ -+void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu); -+void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu); -+void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu); -+struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id); -+int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu); -+int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu); -+void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu); -+ -+int kvm_arch_hardware_enable(void); -+void kvm_arch_hardware_disable(void); -+int kvm_arch_hardware_setup(void); -+void kvm_arch_hardware_unsetup(void); -+void kvm_arch_check_processor_compat(void *rtn); -+int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu); -+int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu); -+ -+void *kvm_kvzalloc(unsigned long size); -+void kvm_kvfree(const void *addr); -+ -+#ifndef __KVM_HAVE_ARCH_VM_ALLOC -+static inline struct kvm *kvm_arch_alloc_vm(void) -+{ -+ return kzalloc(sizeof(struct kvm), GFP_KERNEL); -+} -+ -+static inline void kvm_arch_free_vm(struct kvm *kvm) -+{ -+ kfree(kvm); -+} -+#endif -+ -+#ifdef __KVM_HAVE_ARCH_NONCOHERENT_DMA -+void kvm_arch_register_noncoherent_dma(struct kvm *kvm); -+void kvm_arch_unregister_noncoherent_dma(struct kvm *kvm); -+bool kvm_arch_has_noncoherent_dma(struct kvm *kvm); -+#else -+static inline void kvm_arch_register_noncoherent_dma(struct kvm *kvm) -+{ -+} -+ -+static inline void kvm_arch_unregister_noncoherent_dma(struct kvm *kvm) -+{ -+} -+ -+static inline bool kvm_arch_has_noncoherent_dma(struct kvm *kvm) -+{ -+ return false; -+} -+#endif -+ -+static inline wait_queue_head_t *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) -+{ -+#ifdef __KVM_HAVE_ARCH_WQP -+ return vcpu->arch.wqp; -+#else -+ return &vcpu->wq; -+#endif -+} -+ -+int kvm_arch_init_vm(struct kvm *kvm, unsigned long type); -+void kvm_arch_destroy_vm(struct kvm *kvm); -+void kvm_arch_sync_events(struct kvm *kvm); -+ -+int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu); -+void kvm_vcpu_kick(struct kvm_vcpu *vcpu); -+ -+bool kvm_is_reserved_pfn(pfn_t pfn); -+ -+struct kvm_irq_ack_notifier { -+ struct hlist_node link; -+ unsigned gsi; -+ void (*irq_acked)(struct kvm_irq_ack_notifier *kian); -+}; -+ -+struct kvm_assigned_dev_kernel { -+ struct kvm_irq_ack_notifier ack_notifier; -+ struct list_head list; -+ int assigned_dev_id; -+ int host_segnr; -+ int host_busnr; -+ int host_devfn; -+ unsigned int entries_nr; -+ int host_irq; -+ bool host_irq_disabled; -+ bool pci_2_3; -+ struct msix_entry *host_msix_entries; -+ int guest_irq; -+ struct msix_entry *guest_msix_entries; -+ unsigned long irq_requested_type; -+ int irq_source_id; -+ int flags; -+ struct pci_dev *dev; -+ struct kvm *kvm; -+ spinlock_t intx_lock; -+ spinlock_t intx_mask_lock; -+ char irq_name[32]; -+ struct pci_saved_state *pci_saved_state; -+}; -+ -+struct kvm_irq_mask_notifier { -+ void (*func)(struct kvm_irq_mask_notifier *kimn, bool masked); -+ int irq; -+ struct hlist_node link; -+}; -+ -+void kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, -+ struct kvm_irq_mask_notifier *kimn); -+void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, -+ struct kvm_irq_mask_notifier *kimn); -+void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin, -+ bool mask); -+ -+int kvm_irq_map_gsi(struct kvm *kvm, -+ struct kvm_kernel_irq_routing_entry *entries, int gsi); -+int kvm_irq_map_chip_pin(struct kvm *kvm, unsigned irqchip, unsigned pin); -+ -+int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level, -+ bool line_status); -+int kvm_set_irq_inatomic(struct kvm *kvm, int irq_source_id, u32 irq, int level); -+int kvm_set_msi(struct kvm_kernel_irq_routing_entry *irq_entry, struct kvm *kvm, -+ int irq_source_id, int level, bool line_status); -+bool kvm_irq_has_notifier(struct kvm *kvm, unsigned irqchip, unsigned pin); -+void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin); -+void kvm_register_irq_ack_notifier(struct kvm *kvm, -+ struct kvm_irq_ack_notifier *kian); -+void kvm_unregister_irq_ack_notifier(struct kvm *kvm, -+ struct kvm_irq_ack_notifier *kian); -+int kvm_request_irq_source_id(struct kvm *kvm); -+void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); -+ -+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -+int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot); -+void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot); -+int kvm_iommu_map_guest(struct kvm *kvm); -+int kvm_iommu_unmap_guest(struct kvm *kvm); -+int kvm_assign_device(struct kvm *kvm, -+ struct kvm_assigned_dev_kernel *assigned_dev); -+int kvm_deassign_device(struct kvm *kvm, -+ struct kvm_assigned_dev_kernel *assigned_dev); -+#else -+static inline int kvm_iommu_map_pages(struct kvm *kvm, -+ struct kvm_memory_slot *slot) -+{ -+ return 0; -+} -+ -+static inline void kvm_iommu_unmap_pages(struct kvm *kvm, -+ struct kvm_memory_slot *slot) -+{ -+} -+ -+static inline int kvm_iommu_unmap_guest(struct kvm *kvm) -+{ -+ return 0; -+} -+#endif -+ -+static inline void kvm_guest_enter(void) -+{ -+ unsigned long flags; -+ -+ BUG_ON(preemptible()); -+ -+ local_irq_save(flags); -+ guest_enter(); -+ local_irq_restore(flags); -+ -+ /* KVM does not hold any references to rcu protected data when it -+ * switches CPU into a guest mode. In fact switching to a guest mode -+ * is very similar to exiting to userspace from rcu point of view. In -+ * addition CPU may stay in a guest mode for quite a long time (up to -+ * one time slice). Lets treat guest mode as quiescent state, just like -+ * we do with user-mode execution. -+ */ -+ rcu_virt_note_context_switch(smp_processor_id()); -+} -+ -+static inline void kvm_guest_exit(void) -+{ -+ unsigned long flags; -+ -+ local_irq_save(flags); -+ guest_exit(); -+ local_irq_restore(flags); -+} -+ -+/* -+ * search_memslots() and __gfn_to_memslot() are here because they are -+ * used in non-modular code in arch/powerpc/kvm/book3s_hv_rm_mmu.c. -+ * gfn_to_memslot() itself isn't here as an inline because that would -+ * bloat other code too much. -+ */ -+static inline struct kvm_memory_slot * -+search_memslots(struct kvm_memslots *slots, gfn_t gfn) -+{ -+ struct kvm_memory_slot *memslot; -+ -+ kvm_for_each_memslot(memslot, slots) -+ if (gfn >= memslot->base_gfn && -+ gfn < memslot->base_gfn + memslot->npages) -+ return memslot; -+ -+ return NULL; -+} -+ -+static inline struct kvm_memory_slot * -+__gfn_to_memslot(struct kvm_memslots *slots, gfn_t gfn) -+{ -+ return search_memslots(slots, gfn); -+} -+ -+static inline unsigned long -+__gfn_to_hva_memslot(struct kvm_memory_slot *slot, gfn_t gfn) -+{ -+ return slot->userspace_addr + (gfn - slot->base_gfn) * PAGE_SIZE; -+} -+ -+static inline int memslot_id(struct kvm *kvm, gfn_t gfn) -+{ -+ return gfn_to_memslot(kvm, gfn)->id; -+} -+ -+static inline gfn_t -+hva_to_gfn_memslot(unsigned long hva, struct kvm_memory_slot *slot) -+{ -+ gfn_t gfn_offset = (hva - slot->userspace_addr) >> PAGE_SHIFT; -+ -+ return slot->base_gfn + gfn_offset; -+} -+ -+static inline gpa_t gfn_to_gpa(gfn_t gfn) -+{ -+ return (gpa_t)gfn << PAGE_SHIFT; -+} -+ -+static inline gfn_t gpa_to_gfn(gpa_t gpa) -+{ -+ return (gfn_t)(gpa >> PAGE_SHIFT); -+} -+ -+static inline hpa_t pfn_to_hpa(pfn_t pfn) -+{ -+ return (hpa_t)pfn << PAGE_SHIFT; -+} -+ -+static inline bool kvm_is_error_gpa(struct kvm *kvm, gpa_t gpa) -+{ -+ unsigned long hva = gfn_to_hva(kvm, gpa_to_gfn(gpa)); -+ -+ return kvm_is_error_hva(hva); -+} -+ -+static inline void kvm_migrate_timers(struct kvm_vcpu *vcpu) -+{ -+ set_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests); -+} -+ -+enum kvm_stat_kind { -+ KVM_STAT_VM, -+ KVM_STAT_VCPU, -+}; -+ -+struct kvm_stats_debugfs_item { -+ const char *name; -+ int offset; -+ enum kvm_stat_kind kind; -+ struct dentry *dentry; -+}; -+extern struct kvm_stats_debugfs_item debugfs_entries[]; -+extern struct dentry *kvm_debugfs_dir; -+ -+#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) -+static inline int mmu_notifier_retry(struct kvm *kvm, unsigned long mmu_seq) -+{ -+ if (unlikely(kvm->mmu_notifier_count)) -+ return 1; -+ /* -+ * Ensure the read of mmu_notifier_count happens before the read -+ * of mmu_notifier_seq. This interacts with the smp_wmb() in -+ * mmu_notifier_invalidate_range_end to make sure that the caller -+ * either sees the old (non-zero) value of mmu_notifier_count or -+ * the new (incremented) value of mmu_notifier_seq. -+ * PowerPC Book3s HV KVM calls this under a per-page lock -+ * rather than under kvm->mmu_lock, for scalability, so -+ * can't rely on kvm->mmu_lock to keep things ordered. -+ */ -+ smp_rmb(); -+ if (kvm->mmu_notifier_seq != mmu_seq) -+ return 1; -+ return 0; -+} -+#endif -+ -+#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING -+ -+#ifdef CONFIG_S390 -+#define KVM_MAX_IRQ_ROUTES 4096 //FIXME: we can have more than that... -+#else -+#define KVM_MAX_IRQ_ROUTES 1024 -+#endif -+ -+int kvm_setup_default_irq_routing(struct kvm *kvm); -+int kvm_set_irq_routing(struct kvm *kvm, -+ const struct kvm_irq_routing_entry *entries, -+ unsigned nr, -+ unsigned flags); -+int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e, -+ const struct kvm_irq_routing_entry *ue); -+void kvm_free_irq_routing(struct kvm *kvm); -+ -+#else -+ -+static inline void kvm_free_irq_routing(struct kvm *kvm) {} -+ -+#endif -+ -+int kvm_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi); -+ -+#ifdef CONFIG_HAVE_KVM_EVENTFD -+ -+void kvm_eventfd_init(struct kvm *kvm); -+int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args); -+ -+#ifdef CONFIG_HAVE_KVM_IRQFD -+int kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args); -+void kvm_irqfd_release(struct kvm *kvm); -+void kvm_irq_routing_update(struct kvm *); -+#else -+static inline int kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args) -+{ -+ return -EINVAL; -+} -+ -+static inline void kvm_irqfd_release(struct kvm *kvm) {} -+#endif -+ -+#else -+ -+static inline void kvm_eventfd_init(struct kvm *kvm) {} -+ -+static inline int kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args) -+{ -+ return -EINVAL; -+} -+ -+static inline void kvm_irqfd_release(struct kvm *kvm) {} -+ -+#ifdef CONFIG_HAVE_KVM_IRQCHIP -+static inline void kvm_irq_routing_update(struct kvm *kvm) -+{ -+} -+#endif -+ -+static inline int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) -+{ -+ return -ENOSYS; -+} -+ -+#endif /* CONFIG_HAVE_KVM_EVENTFD */ -+ -+#ifdef CONFIG_KVM_APIC_ARCHITECTURE -+static inline bool kvm_vcpu_is_bsp(struct kvm_vcpu *vcpu) -+{ -+ return vcpu->kvm->bsp_vcpu_id == vcpu->vcpu_id; -+} -+ -+bool kvm_vcpu_compatible(struct kvm_vcpu *vcpu); -+ -+#else -+ -+static inline bool kvm_vcpu_compatible(struct kvm_vcpu *vcpu) { return true; } -+ -+#endif -+ -+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -+ -+long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl, -+ unsigned long arg); -+ -+void kvm_free_all_assigned_devices(struct kvm *kvm); -+ -+#else -+ -+static inline long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl, -+ unsigned long arg) -+{ -+ return -ENOTTY; -+} -+ -+static inline void kvm_free_all_assigned_devices(struct kvm *kvm) {} -+ -+#endif -+ -+static inline void kvm_make_request(int req, struct kvm_vcpu *vcpu) -+{ -+ set_bit(req, &vcpu->requests); -+} -+ -+static inline bool kvm_check_request(int req, struct kvm_vcpu *vcpu) -+{ -+ if (test_bit(req, &vcpu->requests)) { -+ clear_bit(req, &vcpu->requests); -+ return true; -+ } else { -+ return false; -+ } -+} -+ -+extern bool kvm_rebooting; -+ -+struct kvm_device { -+ struct kvm_device_ops *ops; -+ struct kvm *kvm; -+ void *private; -+ struct list_head vm_node; -+}; -+ -+/* create, destroy, and name are mandatory */ -+struct kvm_device_ops { -+ const char *name; -+ int (*create)(struct kvm_device *dev, u32 type); -+ -+ /* -+ * Destroy is responsible for freeing dev. -+ * -+ * Destroy may be called before or after destructors are called -+ * on emulated I/O regions, depending on whether a reference is -+ * held by a vcpu or other kvm component that gets destroyed -+ * after the emulated I/O. -+ */ -+ void (*destroy)(struct kvm_device *dev); -+ -+ int (*set_attr)(struct kvm_device *dev, struct kvm_device_attr *attr); -+ int (*get_attr)(struct kvm_device *dev, struct kvm_device_attr *attr); -+ int (*has_attr)(struct kvm_device *dev, struct kvm_device_attr *attr); -+ long (*ioctl)(struct kvm_device *dev, unsigned int ioctl, -+ unsigned long arg); -+}; -+ -+void kvm_device_get(struct kvm_device *dev); -+void kvm_device_put(struct kvm_device *dev); -+struct kvm_device *kvm_device_from_filp(struct file *filp); -+int kvm_register_device_ops(struct kvm_device_ops *ops, u32 type); -+void kvm_unregister_device_ops(u32 type); -+ -+extern struct kvm_device_ops kvm_mpic_ops; -+extern struct kvm_device_ops kvm_xics_ops; -+ -+#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT -+ -+static inline void kvm_vcpu_set_in_spin_loop(struct kvm_vcpu *vcpu, bool val) -+{ -+ vcpu->spin_loop.in_spin_loop = val; -+} -+static inline void kvm_vcpu_set_dy_eligible(struct kvm_vcpu *vcpu, bool val) -+{ -+ vcpu->spin_loop.dy_eligible = val; -+} -+ -+#else /* !CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT */ -+ -+static inline void kvm_vcpu_set_in_spin_loop(struct kvm_vcpu *vcpu, bool val) -+{ -+} -+ -+static inline void kvm_vcpu_set_dy_eligible(struct kvm_vcpu *vcpu, bool val) -+{ -+} -+#endif /* CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT */ -+#endif -+ -diff -Nur linux-3.18.14.orig/include/linux/lglock.h linux-3.18.14-rt/include/linux/lglock.h ---- linux-3.18.14.orig/include/linux/lglock.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/lglock.h 2015-05-31 15:32:48.261635369 -0500 -@@ -34,22 +34,39 @@ - #endif - - struct lglock { -+#ifndef CONFIG_PREEMPT_RT_FULL - arch_spinlock_t __percpu *lock; -+#else -+ struct rt_mutex __percpu *lock; -+#endif - #ifdef CONFIG_DEBUG_LOCK_ALLOC - struct lock_class_key lock_key; - struct lockdep_map lock_dep_map; - #endif - }; - --#define DEFINE_LGLOCK(name) \ -+#ifndef CONFIG_PREEMPT_RT_FULL -+# define DEFINE_LGLOCK(name) \ - static DEFINE_PER_CPU(arch_spinlock_t, name ## _lock) \ - = __ARCH_SPIN_LOCK_UNLOCKED; \ - struct lglock name = { .lock = &name ## _lock } - --#define DEFINE_STATIC_LGLOCK(name) \ -+# define DEFINE_STATIC_LGLOCK(name) \ - static DEFINE_PER_CPU(arch_spinlock_t, name ## _lock) \ - = __ARCH_SPIN_LOCK_UNLOCKED; \ - static struct lglock name = { .lock = &name ## _lock } -+#else -+ -+# define DEFINE_LGLOCK(name) \ -+ static DEFINE_PER_CPU(struct rt_mutex, name ## _lock) \ -+ = __RT_MUTEX_INITIALIZER( name ## _lock); \ -+ struct lglock name = { .lock = &name ## _lock } -+ -+# define DEFINE_STATIC_LGLOCK(name) \ -+ static DEFINE_PER_CPU(struct rt_mutex, name ## _lock) \ -+ = __RT_MUTEX_INITIALIZER( name ## _lock); \ -+ static struct lglock name = { .lock = &name ## _lock } -+#endif - - void lg_lock_init(struct lglock *lg, char *name); - void lg_local_lock(struct lglock *lg); -@@ -59,6 +76,12 @@ - void lg_global_lock(struct lglock *lg); - void lg_global_unlock(struct lglock *lg); - -+#ifndef CONFIG_PREEMPT_RT_FULL -+#define lg_global_trylock_relax(name) lg_global_lock(name) -+#else -+void lg_global_trylock_relax(struct lglock *lg); -+#endif -+ - #else - /* When !CONFIG_SMP, map lglock to spinlock */ - #define lglock spinlock -diff -Nur linux-3.18.14.orig/include/linux/list_bl.h linux-3.18.14-rt/include/linux/list_bl.h ---- linux-3.18.14.orig/include/linux/list_bl.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/list_bl.h 2015-05-31 15:32:48.265635369 -0500 -@@ -2,6 +2,7 @@ - #define _LINUX_LIST_BL_H - - #include -+#include - #include - - /* -@@ -32,13 +33,22 @@ - - struct hlist_bl_head { - struct hlist_bl_node *first; -+#ifdef CONFIG_PREEMPT_RT_BASE -+ raw_spinlock_t lock; -+#endif - }; - - struct hlist_bl_node { - struct hlist_bl_node *next, **pprev; - }; --#define INIT_HLIST_BL_HEAD(ptr) \ -- ((ptr)->first = NULL) -+ -+static inline void INIT_HLIST_BL_HEAD(struct hlist_bl_head *h) -+{ -+ h->first = NULL; -+#ifdef CONFIG_PREEMPT_RT_BASE -+ raw_spin_lock_init(&h->lock); -+#endif -+} - - static inline void INIT_HLIST_BL_NODE(struct hlist_bl_node *h) - { -@@ -117,12 +127,26 @@ - - static inline void hlist_bl_lock(struct hlist_bl_head *b) - { -+#ifndef CONFIG_PREEMPT_RT_BASE - bit_spin_lock(0, (unsigned long *)b); -+#else -+ raw_spin_lock(&b->lock); -+#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) -+ __set_bit(0, (unsigned long *)b); -+#endif -+#endif - } - - static inline void hlist_bl_unlock(struct hlist_bl_head *b) - { -+#ifndef CONFIG_PREEMPT_RT_BASE - __bit_spin_unlock(0, (unsigned long *)b); -+#else -+#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) -+ __clear_bit(0, (unsigned long *)b); -+#endif -+ raw_spin_unlock(&b->lock); -+#endif - } - - static inline bool hlist_bl_is_locked(struct hlist_bl_head *b) -diff -Nur linux-3.18.14.orig/include/linux/locallock.h linux-3.18.14-rt/include/linux/locallock.h ---- linux-3.18.14.orig/include/linux/locallock.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/locallock.h 2015-05-31 15:32:48.273635368 -0500 -@@ -0,0 +1,270 @@ -+#ifndef _LINUX_LOCALLOCK_H -+#define _LINUX_LOCALLOCK_H -+ -+#include -+#include -+ -+#ifdef CONFIG_PREEMPT_RT_BASE -+ -+#ifdef CONFIG_DEBUG_SPINLOCK -+# define LL_WARN(cond) WARN_ON(cond) -+#else -+# define LL_WARN(cond) do { } while (0) -+#endif -+ -+/* -+ * per cpu lock based substitute for local_irq_*() -+ */ -+struct local_irq_lock { -+ spinlock_t lock; -+ struct task_struct *owner; -+ int nestcnt; -+ unsigned long flags; -+}; -+ -+#define DEFINE_LOCAL_IRQ_LOCK(lvar) \ -+ DEFINE_PER_CPU(struct local_irq_lock, lvar) = { \ -+ .lock = __SPIN_LOCK_UNLOCKED((lvar).lock) } -+ -+#define DECLARE_LOCAL_IRQ_LOCK(lvar) \ -+ DECLARE_PER_CPU(struct local_irq_lock, lvar) -+ -+#define local_irq_lock_init(lvar) \ -+ do { \ -+ int __cpu; \ -+ for_each_possible_cpu(__cpu) \ -+ spin_lock_init(&per_cpu(lvar, __cpu).lock); \ -+ } while (0) -+ -+/* -+ * spin_lock|trylock|unlock_local flavour that does not migrate disable -+ * used for __local_lock|trylock|unlock where get_local_var/put_local_var -+ * already takes care of the migrate_disable/enable -+ * for CONFIG_PREEMPT_BASE map to the normal spin_* calls. -+ */ -+#ifdef CONFIG_PREEMPT_RT_FULL -+# define spin_lock_local(lock) rt_spin_lock(lock) -+# define spin_trylock_local(lock) rt_spin_trylock(lock) -+# define spin_unlock_local(lock) rt_spin_unlock(lock) -+#else -+# define spin_lock_local(lock) spin_lock(lock) -+# define spin_trylock_local(lock) spin_trylock(lock) -+# define spin_unlock_local(lock) spin_unlock(lock) -+#endif -+ -+static inline void __local_lock(struct local_irq_lock *lv) -+{ -+ if (lv->owner != current) { -+ spin_lock_local(&lv->lock); -+ LL_WARN(lv->owner); -+ LL_WARN(lv->nestcnt); -+ lv->owner = current; -+ } -+ lv->nestcnt++; -+} -+ -+#define local_lock(lvar) \ -+ do { __local_lock(&get_local_var(lvar)); } while (0) -+ -+static inline int __local_trylock(struct local_irq_lock *lv) -+{ -+ if (lv->owner != current && spin_trylock_local(&lv->lock)) { -+ LL_WARN(lv->owner); -+ LL_WARN(lv->nestcnt); -+ lv->owner = current; -+ lv->nestcnt = 1; -+ return 1; -+ } -+ return 0; -+} -+ -+#define local_trylock(lvar) \ -+ ({ \ -+ int __locked; \ -+ __locked = __local_trylock(&get_local_var(lvar)); \ -+ if (!__locked) \ -+ put_local_var(lvar); \ -+ __locked; \ -+ }) -+ -+static inline void __local_unlock(struct local_irq_lock *lv) -+{ -+ LL_WARN(lv->nestcnt == 0); -+ LL_WARN(lv->owner != current); -+ if (--lv->nestcnt) -+ return; -+ -+ lv->owner = NULL; -+ spin_unlock_local(&lv->lock); -+} -+ -+#define local_unlock(lvar) \ -+ do { \ -+ __local_unlock(&__get_cpu_var(lvar)); \ -+ put_local_var(lvar); \ -+ } while (0) -+ -+static inline void __local_lock_irq(struct local_irq_lock *lv) -+{ -+ spin_lock_irqsave(&lv->lock, lv->flags); -+ LL_WARN(lv->owner); -+ LL_WARN(lv->nestcnt); -+ lv->owner = current; -+ lv->nestcnt = 1; -+} -+ -+#define local_lock_irq(lvar) \ -+ do { __local_lock_irq(&get_local_var(lvar)); } while (0) -+ -+#define local_lock_irq_on(lvar, cpu) \ -+ do { __local_lock_irq(&per_cpu(lvar, cpu)); } while (0) -+ -+static inline void __local_unlock_irq(struct local_irq_lock *lv) -+{ -+ LL_WARN(!lv->nestcnt); -+ LL_WARN(lv->owner != current); -+ lv->owner = NULL; -+ lv->nestcnt = 0; -+ spin_unlock_irq(&lv->lock); -+} -+ -+#define local_unlock_irq(lvar) \ -+ do { \ -+ __local_unlock_irq(&__get_cpu_var(lvar)); \ -+ put_local_var(lvar); \ -+ } while (0) -+ -+#define local_unlock_irq_on(lvar, cpu) \ -+ do { \ -+ __local_unlock_irq(&per_cpu(lvar, cpu)); \ -+ } while (0) -+ -+static inline int __local_lock_irqsave(struct local_irq_lock *lv) -+{ -+ if (lv->owner != current) { -+ __local_lock_irq(lv); -+ return 0; -+ } else { -+ lv->nestcnt++; -+ return 1; -+ } -+} -+ -+#define local_lock_irqsave(lvar, _flags) \ -+ do { \ -+ if (__local_lock_irqsave(&get_local_var(lvar))) \ -+ put_local_var(lvar); \ -+ _flags = __get_cpu_var(lvar).flags; \ -+ } while (0) -+ -+#define local_lock_irqsave_on(lvar, _flags, cpu) \ -+ do { \ -+ __local_lock_irqsave(&per_cpu(lvar, cpu)); \ -+ _flags = per_cpu(lvar, cpu).flags; \ -+ } while (0) -+ -+static inline int __local_unlock_irqrestore(struct local_irq_lock *lv, -+ unsigned long flags) -+{ -+ LL_WARN(!lv->nestcnt); -+ LL_WARN(lv->owner != current); -+ if (--lv->nestcnt) -+ return 0; -+ -+ lv->owner = NULL; -+ spin_unlock_irqrestore(&lv->lock, lv->flags); -+ return 1; -+} -+ -+#define local_unlock_irqrestore(lvar, flags) \ -+ do { \ -+ if (__local_unlock_irqrestore(&__get_cpu_var(lvar), flags)) \ -+ put_local_var(lvar); \ -+ } while (0) -+ -+#define local_unlock_irqrestore_on(lvar, flags, cpu) \ -+ do { \ -+ __local_unlock_irqrestore(&per_cpu(lvar, cpu), flags); \ -+ } while (0) -+ -+#define local_spin_trylock_irq(lvar, lock) \ -+ ({ \ -+ int __locked; \ -+ local_lock_irq(lvar); \ -+ __locked = spin_trylock(lock); \ -+ if (!__locked) \ -+ local_unlock_irq(lvar); \ -+ __locked; \ -+ }) -+ -+#define local_spin_lock_irq(lvar, lock) \ -+ do { \ -+ local_lock_irq(lvar); \ -+ spin_lock(lock); \ -+ } while (0) -+ -+#define local_spin_unlock_irq(lvar, lock) \ -+ do { \ -+ spin_unlock(lock); \ -+ local_unlock_irq(lvar); \ -+ } while (0) -+ -+#define local_spin_lock_irqsave(lvar, lock, flags) \ -+ do { \ -+ local_lock_irqsave(lvar, flags); \ -+ spin_lock(lock); \ -+ } while (0) -+ -+#define local_spin_unlock_irqrestore(lvar, lock, flags) \ -+ do { \ -+ spin_unlock(lock); \ -+ local_unlock_irqrestore(lvar, flags); \ -+ } while (0) -+ -+#define get_locked_var(lvar, var) \ -+ (*({ \ -+ local_lock(lvar); \ -+ &__get_cpu_var(var); \ -+ })) -+ -+#define put_locked_var(lvar, var) local_unlock(lvar); -+ -+#define local_lock_cpu(lvar) \ -+ ({ \ -+ local_lock(lvar); \ -+ smp_processor_id(); \ -+ }) -+ -+#define local_unlock_cpu(lvar) local_unlock(lvar) -+ -+#else /* PREEMPT_RT_BASE */ -+ -+#define DEFINE_LOCAL_IRQ_LOCK(lvar) __typeof__(const int) lvar -+#define DECLARE_LOCAL_IRQ_LOCK(lvar) extern __typeof__(const int) lvar -+ -+static inline void local_irq_lock_init(int lvar) { } -+ -+#define local_lock(lvar) preempt_disable() -+#define local_unlock(lvar) preempt_enable() -+#define local_lock_irq(lvar) local_irq_disable() -+#define local_unlock_irq(lvar) local_irq_enable() -+#define local_lock_irqsave(lvar, flags) local_irq_save(flags) -+#define local_unlock_irqrestore(lvar, flags) local_irq_restore(flags) -+ -+#define local_spin_trylock_irq(lvar, lock) spin_trylock_irq(lock) -+#define local_spin_lock_irq(lvar, lock) spin_lock_irq(lock) -+#define local_spin_unlock_irq(lvar, lock) spin_unlock_irq(lock) -+#define local_spin_lock_irqsave(lvar, lock, flags) \ -+ spin_lock_irqsave(lock, flags) -+#define local_spin_unlock_irqrestore(lvar, lock, flags) \ -+ spin_unlock_irqrestore(lock, flags) -+ -+#define get_locked_var(lvar, var) get_cpu_var(var) -+#define put_locked_var(lvar, var) put_cpu_var(var) -+ -+#define local_lock_cpu(lvar) get_cpu() -+#define local_unlock_cpu(lvar) put_cpu() -+ -+#endif -+ -+#endif -diff -Nur linux-3.18.14.orig/include/linux/mm_types.h linux-3.18.14-rt/include/linux/mm_types.h ---- linux-3.18.14.orig/include/linux/mm_types.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/mm_types.h 2015-05-31 15:32:48.273635368 -0500 -@@ -11,6 +11,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -454,6 +455,9 @@ - bool tlb_flush_pending; - #endif - struct uprobes_state uprobes_state; -+#ifdef CONFIG_PREEMPT_RT_BASE -+ struct rcu_head delayed_drop; -+#endif - }; - - static inline void mm_init_cpumask(struct mm_struct *mm) -diff -Nur linux-3.18.14.orig/include/linux/mutex.h linux-3.18.14-rt/include/linux/mutex.h ---- linux-3.18.14.orig/include/linux/mutex.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/mutex.h 2015-05-31 15:32:48.273635368 -0500 -@@ -19,6 +19,17 @@ - #include - #include - -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \ -+ , .dep_map = { .name = #lockname } -+#else -+# define __DEP_MAP_MUTEX_INITIALIZER(lockname) -+#endif -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+# include -+#else -+ - /* - * Simple, straightforward mutexes with strict semantics: - * -@@ -100,13 +111,6 @@ - static inline void mutex_destroy(struct mutex *lock) {} - #endif - --#ifdef CONFIG_DEBUG_LOCK_ALLOC --# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \ -- , .dep_map = { .name = #lockname } --#else --# define __DEP_MAP_MUTEX_INITIALIZER(lockname) --#endif -- - #define __MUTEX_INITIALIZER(lockname) \ - { .count = ATOMIC_INIT(1) \ - , .wait_lock = __SPIN_LOCK_UNLOCKED(lockname.wait_lock) \ -@@ -174,6 +178,8 @@ - extern int mutex_trylock(struct mutex *lock); - extern void mutex_unlock(struct mutex *lock); - -+#endif /* !PREEMPT_RT_FULL */ -+ - extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock); - - #endif /* __LINUX_MUTEX_H */ -diff -Nur linux-3.18.14.orig/include/linux/mutex_rt.h linux-3.18.14-rt/include/linux/mutex_rt.h ---- linux-3.18.14.orig/include/linux/mutex_rt.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/mutex_rt.h 2015-05-31 15:32:48.273635368 -0500 -@@ -0,0 +1,84 @@ -+#ifndef __LINUX_MUTEX_RT_H -+#define __LINUX_MUTEX_RT_H -+ -+#ifndef __LINUX_MUTEX_H -+#error "Please include mutex.h" -+#endif -+ -+#include -+ -+/* FIXME: Just for __lockfunc */ -+#include -+ -+struct mutex { -+ struct rt_mutex lock; -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ struct lockdep_map dep_map; -+#endif -+}; -+ -+#define __MUTEX_INITIALIZER(mutexname) \ -+ { \ -+ .lock = __RT_MUTEX_INITIALIZER(mutexname.lock) \ -+ __DEP_MAP_MUTEX_INITIALIZER(mutexname) \ -+ } -+ -+#define DEFINE_MUTEX(mutexname) \ -+ struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) -+ -+extern void __mutex_do_init(struct mutex *lock, const char *name, struct lock_class_key *key); -+extern void __lockfunc _mutex_lock(struct mutex *lock); -+extern int __lockfunc _mutex_lock_interruptible(struct mutex *lock); -+extern int __lockfunc _mutex_lock_killable(struct mutex *lock); -+extern void __lockfunc _mutex_lock_nested(struct mutex *lock, int subclass); -+extern void __lockfunc _mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest_lock); -+extern int __lockfunc _mutex_lock_interruptible_nested(struct mutex *lock, int subclass); -+extern int __lockfunc _mutex_lock_killable_nested(struct mutex *lock, int subclass); -+extern int __lockfunc _mutex_trylock(struct mutex *lock); -+extern void __lockfunc _mutex_unlock(struct mutex *lock); -+ -+#define mutex_is_locked(l) rt_mutex_is_locked(&(l)->lock) -+#define mutex_lock(l) _mutex_lock(l) -+#define mutex_lock_interruptible(l) _mutex_lock_interruptible(l) -+#define mutex_lock_killable(l) _mutex_lock_killable(l) -+#define mutex_trylock(l) _mutex_trylock(l) -+#define mutex_unlock(l) _mutex_unlock(l) -+#define mutex_destroy(l) rt_mutex_destroy(&(l)->lock) -+ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+# define mutex_lock_nested(l, s) _mutex_lock_nested(l, s) -+# define mutex_lock_interruptible_nested(l, s) \ -+ _mutex_lock_interruptible_nested(l, s) -+# define mutex_lock_killable_nested(l, s) \ -+ _mutex_lock_killable_nested(l, s) -+ -+# define mutex_lock_nest_lock(lock, nest_lock) \ -+do { \ -+ typecheck(struct lockdep_map *, &(nest_lock)->dep_map); \ -+ _mutex_lock_nest_lock(lock, &(nest_lock)->dep_map); \ -+} while (0) -+ -+#else -+# define mutex_lock_nested(l, s) _mutex_lock(l) -+# define mutex_lock_interruptible_nested(l, s) \ -+ _mutex_lock_interruptible(l) -+# define mutex_lock_killable_nested(l, s) \ -+ _mutex_lock_killable(l) -+# define mutex_lock_nest_lock(lock, nest_lock) mutex_lock(lock) -+#endif -+ -+# define mutex_init(mutex) \ -+do { \ -+ static struct lock_class_key __key; \ -+ \ -+ rt_mutex_init(&(mutex)->lock); \ -+ __mutex_do_init((mutex), #mutex, &__key); \ -+} while (0) -+ -+# define __mutex_init(mutex, name, key) \ -+do { \ -+ rt_mutex_init(&(mutex)->lock); \ -+ __mutex_do_init((mutex), name, key); \ -+} while (0) -+ -+#endif -diff -Nur linux-3.18.14.orig/include/linux/netdevice.h linux-3.18.14-rt/include/linux/netdevice.h ---- linux-3.18.14.orig/include/linux/netdevice.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/netdevice.h 2015-05-31 15:32:48.305635368 -0500 -@@ -2351,6 +2351,7 @@ - unsigned int dropped; - struct sk_buff_head input_pkt_queue; - struct napi_struct backlog; -+ struct sk_buff_head tofree_queue; - - #ifdef CONFIG_NET_FLOW_LIMIT - struct sd_flow_limit __rcu *flow_limit; -diff -Nur linux-3.18.14.orig/include/linux/netfilter/x_tables.h linux-3.18.14-rt/include/linux/netfilter/x_tables.h ---- linux-3.18.14.orig/include/linux/netfilter/x_tables.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/netfilter/x_tables.h 2015-05-31 15:32:48.305635368 -0500 -@@ -3,6 +3,7 @@ - - - #include -+#include - #include - - /** -@@ -282,6 +283,8 @@ - */ - DECLARE_PER_CPU(seqcount_t, xt_recseq); - -+DECLARE_LOCAL_IRQ_LOCK(xt_write_lock); -+ - /** - * xt_write_recseq_begin - start of a write section - * -@@ -296,6 +299,9 @@ - { - unsigned int addend; - -+ /* RT protection */ -+ local_lock(xt_write_lock); -+ - /* - * Low order bit of sequence is set if we already - * called xt_write_recseq_begin(). -@@ -326,6 +332,7 @@ - /* this is kind of a write_seqcount_end(), but addend is 0 or 1 */ - smp_wmb(); - __this_cpu_add(xt_recseq.sequence, addend); -+ local_unlock(xt_write_lock); - } - - /* -diff -Nur linux-3.18.14.orig/include/linux/notifier.h linux-3.18.14-rt/include/linux/notifier.h ---- linux-3.18.14.orig/include/linux/notifier.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/notifier.h 2015-05-31 15:32:48.305635368 -0500 -@@ -6,7 +6,7 @@ - * - * Alan Cox - */ -- -+ - #ifndef _LINUX_NOTIFIER_H - #define _LINUX_NOTIFIER_H - #include -@@ -42,9 +42,7 @@ - * in srcu_notifier_call_chain(): no cache bounces and no memory barriers. - * As compensation, srcu_notifier_chain_unregister() is rather expensive. - * SRCU notifier chains should be used when the chain will be called very -- * often but notifier_blocks will seldom be removed. Also, SRCU notifier -- * chains are slightly more difficult to use because they require special -- * runtime initialization. -+ * often but notifier_blocks will seldom be removed. - */ - - typedef int (*notifier_fn_t)(struct notifier_block *nb, -@@ -88,7 +86,7 @@ - (name)->head = NULL; \ - } while (0) - --/* srcu_notifier_heads must be initialized and cleaned up dynamically */ -+/* srcu_notifier_heads must be cleaned up dynamically */ - extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); - #define srcu_cleanup_notifier_head(name) \ - cleanup_srcu_struct(&(name)->srcu); -@@ -101,7 +99,13 @@ - .head = NULL } - #define RAW_NOTIFIER_INIT(name) { \ - .head = NULL } --/* srcu_notifier_heads cannot be initialized statically */ -+ -+#define SRCU_NOTIFIER_INIT(name, pcpu) \ -+ { \ -+ .mutex = __MUTEX_INITIALIZER(name.mutex), \ -+ .head = NULL, \ -+ .srcu = __SRCU_STRUCT_INIT(name.srcu, pcpu), \ -+ } - - #define ATOMIC_NOTIFIER_HEAD(name) \ - struct atomic_notifier_head name = \ -@@ -113,6 +117,18 @@ - struct raw_notifier_head name = \ - RAW_NOTIFIER_INIT(name) - -+#define _SRCU_NOTIFIER_HEAD(name, mod) \ -+ static DEFINE_PER_CPU(struct srcu_struct_array, \ -+ name##_head_srcu_array); \ -+ mod struct srcu_notifier_head name = \ -+ SRCU_NOTIFIER_INIT(name, name##_head_srcu_array) -+ -+#define SRCU_NOTIFIER_HEAD(name) \ -+ _SRCU_NOTIFIER_HEAD(name, ) -+ -+#define SRCU_NOTIFIER_HEAD_STATIC(name) \ -+ _SRCU_NOTIFIER_HEAD(name, static) -+ - #ifdef __KERNEL__ - - extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh, -@@ -182,12 +198,12 @@ - - /* - * Declared notifiers so far. I can imagine quite a few more chains -- * over time (eg laptop power reset chains, reboot chain (to clean -+ * over time (eg laptop power reset chains, reboot chain (to clean - * device units up), device [un]mount chain, module load/unload chain, -- * low memory chain, screenblank chain (for plug in modular screenblankers) -+ * low memory chain, screenblank chain (for plug in modular screenblankers) - * VC switch chains (for loadable kernel svgalib VC switch helpers) etc... - */ -- -+ - /* CPU notfiers are defined in include/linux/cpu.h. */ - - /* netdevice notifiers are defined in include/linux/netdevice.h */ -diff -Nur linux-3.18.14.orig/include/linux/percpu.h linux-3.18.14-rt/include/linux/percpu.h ---- linux-3.18.14.orig/include/linux/percpu.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/percpu.h 2015-05-31 15:32:48.305635368 -0500 -@@ -23,6 +23,35 @@ - PERCPU_MODULE_RESERVE) - #endif - -+#ifdef CONFIG_PREEMPT_RT_FULL -+ -+#define get_local_var(var) (*({ \ -+ migrate_disable(); \ -+ &__get_cpu_var(var); })) -+ -+#define put_local_var(var) do { \ -+ (void)&(var); \ -+ migrate_enable(); \ -+} while (0) -+ -+# define get_local_ptr(var) ({ \ -+ migrate_disable(); \ -+ this_cpu_ptr(var); }) -+ -+# define put_local_ptr(var) do { \ -+ (void)(var); \ -+ migrate_enable(); \ -+} while (0) -+ -+#else -+ -+#define get_local_var(var) get_cpu_var(var) -+#define put_local_var(var) put_cpu_var(var) -+#define get_local_ptr(var) get_cpu_ptr(var) -+#define put_local_ptr(var) put_cpu_ptr(var) -+ -+#endif -+ - /* minimum unit size, also is the maximum supported allocation size */ - #define PCPU_MIN_UNIT_SIZE PFN_ALIGN(32 << 10) - -diff -Nur linux-3.18.14.orig/include/linux/pid.h linux-3.18.14-rt/include/linux/pid.h ---- linux-3.18.14.orig/include/linux/pid.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/pid.h 2015-05-31 15:32:48.341635368 -0500 -@@ -2,6 +2,7 @@ - #define _LINUX_PID_H - - #include -+#include - - enum pid_type - { -diff -Nur linux-3.18.14.orig/include/linux/preempt.h linux-3.18.14-rt/include/linux/preempt.h ---- linux-3.18.14.orig/include/linux/preempt.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/preempt.h 2015-05-31 15:32:48.341635368 -0500 -@@ -33,6 +33,20 @@ - #define preempt_count_inc() preempt_count_add(1) - #define preempt_count_dec() preempt_count_sub(1) - -+#ifdef CONFIG_PREEMPT_LAZY -+#define add_preempt_lazy_count(val) do { preempt_lazy_count() += (val); } while (0) -+#define sub_preempt_lazy_count(val) do { preempt_lazy_count() -= (val); } while (0) -+#define inc_preempt_lazy_count() add_preempt_lazy_count(1) -+#define dec_preempt_lazy_count() sub_preempt_lazy_count(1) -+#define preempt_lazy_count() (current_thread_info()->preempt_lazy_count) -+#else -+#define add_preempt_lazy_count(val) do { } while (0) -+#define sub_preempt_lazy_count(val) do { } while (0) -+#define inc_preempt_lazy_count() do { } while (0) -+#define dec_preempt_lazy_count() do { } while (0) -+#define preempt_lazy_count() (0) -+#endif -+ - #ifdef CONFIG_PREEMPT_COUNT - - #define preempt_disable() \ -@@ -41,13 +55,25 @@ - barrier(); \ - } while (0) - -+#define preempt_lazy_disable() \ -+do { \ -+ inc_preempt_lazy_count(); \ -+ barrier(); \ -+} while (0) -+ - #define sched_preempt_enable_no_resched() \ - do { \ - barrier(); \ - preempt_count_dec(); \ - } while (0) - --#define preempt_enable_no_resched() sched_preempt_enable_no_resched() -+#ifdef CONFIG_PREEMPT_RT_BASE -+# define preempt_enable_no_resched() sched_preempt_enable_no_resched() -+# define preempt_check_resched_rt() preempt_check_resched() -+#else -+# define preempt_enable_no_resched() preempt_enable() -+# define preempt_check_resched_rt() barrier(); -+#endif - - #ifdef CONFIG_PREEMPT - #define preempt_enable() \ -@@ -63,6 +89,13 @@ - __preempt_schedule(); \ - } while (0) - -+#define preempt_lazy_enable() \ -+do { \ -+ dec_preempt_lazy_count(); \ -+ barrier(); \ -+ preempt_check_resched(); \ -+} while (0) -+ - #else - #define preempt_enable() \ - do { \ -@@ -121,6 +154,7 @@ - #define preempt_disable_notrace() barrier() - #define preempt_enable_no_resched_notrace() barrier() - #define preempt_enable_notrace() barrier() -+#define preempt_check_resched_rt() barrier() - - #endif /* CONFIG_PREEMPT_COUNT */ - -@@ -140,10 +174,31 @@ - } while (0) - #define preempt_fold_need_resched() \ - do { \ -- if (tif_need_resched()) \ -+ if (tif_need_resched_now()) \ - set_preempt_need_resched(); \ - } while (0) - -+#ifdef CONFIG_PREEMPT_RT_FULL -+# define preempt_disable_rt() preempt_disable() -+# define preempt_enable_rt() preempt_enable() -+# define preempt_disable_nort() barrier() -+# define preempt_enable_nort() barrier() -+# ifdef CONFIG_SMP -+ extern void migrate_disable(void); -+ extern void migrate_enable(void); -+# else /* CONFIG_SMP */ -+# define migrate_disable() barrier() -+# define migrate_enable() barrier() -+# endif /* CONFIG_SMP */ -+#else -+# define preempt_disable_rt() barrier() -+# define preempt_enable_rt() barrier() -+# define preempt_disable_nort() preempt_disable() -+# define preempt_enable_nort() preempt_enable() -+# define migrate_disable() preempt_disable() -+# define migrate_enable() preempt_enable() -+#endif -+ - #ifdef CONFIG_PREEMPT_NOTIFIERS - - struct preempt_notifier; -diff -Nur linux-3.18.14.orig/include/linux/preempt_mask.h linux-3.18.14-rt/include/linux/preempt_mask.h ---- linux-3.18.14.orig/include/linux/preempt_mask.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/preempt_mask.h 2015-05-31 15:32:48.341635368 -0500 -@@ -44,16 +44,26 @@ - #define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - #define NMI_OFFSET (1UL << NMI_SHIFT) - --#define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET) -+#ifndef CONFIG_PREEMPT_RT_FULL -+# define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET) -+#else -+# define SOFTIRQ_DISABLE_OFFSET (0) -+#endif - - #define PREEMPT_ACTIVE_BITS 1 - #define PREEMPT_ACTIVE_SHIFT (NMI_SHIFT + NMI_BITS) - #define PREEMPT_ACTIVE (__IRQ_MASK(PREEMPT_ACTIVE_BITS) << PREEMPT_ACTIVE_SHIFT) - - #define hardirq_count() (preempt_count() & HARDIRQ_MASK) --#define softirq_count() (preempt_count() & SOFTIRQ_MASK) - #define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \ - | NMI_MASK)) -+#ifndef CONFIG_PREEMPT_RT_FULL -+# define softirq_count() (preempt_count() & SOFTIRQ_MASK) -+# define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET) -+#else -+# define softirq_count() (0UL) -+extern int in_serving_softirq(void); -+#endif - - /* - * Are we doing bottom half or hardware interrupt processing? -@@ -64,7 +74,6 @@ - #define in_irq() (hardirq_count()) - #define in_softirq() (softirq_count()) - #define in_interrupt() (irq_count()) --#define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET) - - /* - * Are we in NMI context? -diff -Nur linux-3.18.14.orig/include/linux/printk.h linux-3.18.14-rt/include/linux/printk.h ---- linux-3.18.14.orig/include/linux/printk.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/printk.h 2015-05-31 15:32:48.341635368 -0500 -@@ -119,9 +119,11 @@ - extern asmlinkage __printf(1, 2) - void early_printk(const char *fmt, ...); - void early_vprintk(const char *fmt, va_list ap); -+extern void printk_kill(void); - #else - static inline __printf(1, 2) __cold - void early_printk(const char *s, ...) { } -+static inline void printk_kill(void) { } - #endif - - #ifdef CONFIG_PRINTK -@@ -155,7 +157,6 @@ - #define printk_ratelimit() __printk_ratelimit(__func__) - extern bool printk_timed_ratelimit(unsigned long *caller_jiffies, - unsigned int interval_msec); -- - extern int printk_delay_msec; - extern int dmesg_restrict; - extern int kptr_restrict; -diff -Nur linux-3.18.14.orig/include/linux/radix-tree.h linux-3.18.14-rt/include/linux/radix-tree.h ---- linux-3.18.14.orig/include/linux/radix-tree.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/radix-tree.h 2015-05-31 15:32:48.341635368 -0500 -@@ -277,8 +277,13 @@ - unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root, - void ***results, unsigned long *indices, - unsigned long first_index, unsigned int max_items); -+#ifndef CONFIG_PREEMPT_RT_FULL - int radix_tree_preload(gfp_t gfp_mask); - int radix_tree_maybe_preload(gfp_t gfp_mask); -+#else -+static inline int radix_tree_preload(gfp_t gm) { return 0; } -+static inline int radix_tree_maybe_preload(gfp_t gfp_mask) { return 0; } -+#endif - void radix_tree_init(void); - void *radix_tree_tag_set(struct radix_tree_root *root, - unsigned long index, unsigned int tag); -@@ -303,7 +308,7 @@ - - static inline void radix_tree_preload_end(void) - { -- preempt_enable(); -+ preempt_enable_nort(); - } - - /** -diff -Nur linux-3.18.14.orig/include/linux/random.h linux-3.18.14-rt/include/linux/random.h ---- linux-3.18.14.orig/include/linux/random.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/random.h 2015-05-31 15:32:48.341635368 -0500 -@@ -11,7 +11,7 @@ - extern void add_device_randomness(const void *, unsigned int); - extern void add_input_randomness(unsigned int type, unsigned int code, - unsigned int value); --extern void add_interrupt_randomness(int irq, int irq_flags); -+extern void add_interrupt_randomness(int irq, int irq_flags, __u64 ip); - - extern void get_random_bytes(void *buf, int nbytes); - extern void get_random_bytes_arch(void *buf, int nbytes); -diff -Nur linux-3.18.14.orig/include/linux/rcupdate.h linux-3.18.14-rt/include/linux/rcupdate.h ---- linux-3.18.14.orig/include/linux/rcupdate.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/rcupdate.h 2015-05-31 15:32:48.341635368 -0500 -@@ -147,6 +147,9 @@ - - #endif /* #else #ifdef CONFIG_PREEMPT_RCU */ - -+#ifdef CONFIG_PREEMPT_RT_FULL -+#define call_rcu_bh call_rcu -+#else - /** - * call_rcu_bh() - Queue an RCU for invocation after a quicker grace period. - * @head: structure to be used for queueing the RCU updates. -@@ -170,6 +173,7 @@ - */ - void call_rcu_bh(struct rcu_head *head, - void (*func)(struct rcu_head *head)); -+#endif - - /** - * call_rcu_sched() - Queue an RCU for invocation after sched grace period. -@@ -231,6 +235,11 @@ - * types of kernel builds, the rcu_read_lock() nesting depth is unknowable. - */ - #define rcu_preempt_depth() (current->rcu_read_lock_nesting) -+#ifndef CONFIG_PREEMPT_RT_FULL -+#define sched_rcu_preempt_depth() rcu_preempt_depth() -+#else -+static inline int sched_rcu_preempt_depth(void) { return 0; } -+#endif - - #else /* #ifdef CONFIG_PREEMPT_RCU */ - -@@ -254,6 +263,8 @@ - return 0; - } - -+#define sched_rcu_preempt_depth() rcu_preempt_depth() -+ - #endif /* #else #ifdef CONFIG_PREEMPT_RCU */ - - /* Internal to kernel */ -@@ -430,7 +441,14 @@ - int debug_lockdep_rcu_enabled(void); - - int rcu_read_lock_held(void); -+#ifdef CONFIG_PREEMPT_RT_FULL -+static inline int rcu_read_lock_bh_held(void) -+{ -+ return rcu_read_lock_held(); -+} -+#else - int rcu_read_lock_bh_held(void); -+#endif - - /** - * rcu_read_lock_sched_held() - might we be in RCU-sched read-side critical section? -@@ -955,10 +973,14 @@ - static inline void rcu_read_lock_bh(void) - { - local_bh_disable(); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ rcu_read_lock(); -+#else - __acquire(RCU_BH); - rcu_lock_acquire(&rcu_bh_lock_map); - rcu_lockdep_assert(rcu_is_watching(), - "rcu_read_lock_bh() used illegally while idle"); -+#endif - } - - /* -@@ -968,10 +990,14 @@ - */ - static inline void rcu_read_unlock_bh(void) - { -+#ifdef CONFIG_PREEMPT_RT_FULL -+ rcu_read_unlock(); -+#else - rcu_lockdep_assert(rcu_is_watching(), - "rcu_read_unlock_bh() used illegally while idle"); - rcu_lock_release(&rcu_bh_lock_map); - __release(RCU_BH); -+#endif - local_bh_enable(); - } - -diff -Nur linux-3.18.14.orig/include/linux/rcutree.h linux-3.18.14-rt/include/linux/rcutree.h ---- linux-3.18.14.orig/include/linux/rcutree.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/rcutree.h 2015-05-31 15:32:48.361635367 -0500 -@@ -46,7 +46,11 @@ - rcu_note_context_switch(cpu); - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+# define synchronize_rcu_bh synchronize_rcu -+#else - void synchronize_rcu_bh(void); -+#endif - void synchronize_sched_expedited(void); - void synchronize_rcu_expedited(void); - -@@ -74,7 +78,11 @@ - } - - void rcu_barrier(void); -+#ifdef CONFIG_PREEMPT_RT_FULL -+# define rcu_barrier_bh rcu_barrier -+#else - void rcu_barrier_bh(void); -+#endif - void rcu_barrier_sched(void); - unsigned long get_state_synchronize_rcu(void); - void cond_synchronize_rcu(unsigned long oldstate); -@@ -82,12 +90,10 @@ - extern unsigned long rcutorture_testseq; - extern unsigned long rcutorture_vernum; - long rcu_batches_completed(void); --long rcu_batches_completed_bh(void); - long rcu_batches_completed_sched(void); - void show_rcu_gp_kthreads(void); - - void rcu_force_quiescent_state(void); --void rcu_bh_force_quiescent_state(void); - void rcu_sched_force_quiescent_state(void); - - void exit_rcu(void); -@@ -97,4 +103,12 @@ - - bool rcu_is_watching(void); - -+#ifndef CONFIG_PREEMPT_RT_FULL -+void rcu_bh_force_quiescent_state(void); -+long rcu_batches_completed_bh(void); -+#else -+# define rcu_bh_force_quiescent_state rcu_force_quiescent_state -+# define rcu_batches_completed_bh rcu_batches_completed -+#endif -+ - #endif /* __LINUX_RCUTREE_H */ -diff -Nur linux-3.18.14.orig/include/linux/rtmutex.h linux-3.18.14-rt/include/linux/rtmutex.h ---- linux-3.18.14.orig/include/linux/rtmutex.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/rtmutex.h 2015-05-31 15:32:48.377635367 -0500 -@@ -14,10 +14,14 @@ - - #include - #include --#include -+#include - - extern int max_lock_depth; /* for sysctl */ - -+#ifdef CONFIG_DEBUG_MUTEXES -+#include -+#endif -+ - /** - * The rt_mutex structure - * -@@ -31,8 +35,8 @@ - struct rb_root waiters; - struct rb_node *waiters_leftmost; - struct task_struct *owner; --#ifdef CONFIG_DEBUG_RT_MUTEXES - int save_state; -+#ifdef CONFIG_DEBUG_RT_MUTEXES - const char *name, *file; - int line; - void *magic; -@@ -55,22 +59,33 @@ - # define rt_mutex_debug_check_no_locks_held(task) do { } while (0) - #endif - -+# define rt_mutex_init(mutex) \ -+ do { \ -+ raw_spin_lock_init(&(mutex)->wait_lock); \ -+ __rt_mutex_init(mutex, #mutex); \ -+ } while (0) -+ - #ifdef CONFIG_DEBUG_RT_MUTEXES - # define __DEBUG_RT_MUTEX_INITIALIZER(mutexname) \ - , .name = #mutexname, .file = __FILE__, .line = __LINE__ --# define rt_mutex_init(mutex) __rt_mutex_init(mutex, __func__) - extern void rt_mutex_debug_task_free(struct task_struct *tsk); - #else - # define __DEBUG_RT_MUTEX_INITIALIZER(mutexname) --# define rt_mutex_init(mutex) __rt_mutex_init(mutex, NULL) - # define rt_mutex_debug_task_free(t) do { } while (0) - #endif - --#define __RT_MUTEX_INITIALIZER(mutexname) \ -- { .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(mutexname.wait_lock) \ -+#define __RT_MUTEX_INITIALIZER_PLAIN(mutexname) \ -+ .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(mutexname.wait_lock) \ - , .waiters = RB_ROOT \ - , .owner = NULL \ -- __DEBUG_RT_MUTEX_INITIALIZER(mutexname)} -+ __DEBUG_RT_MUTEX_INITIALIZER(mutexname) -+ -+#define __RT_MUTEX_INITIALIZER(mutexname) \ -+ { __RT_MUTEX_INITIALIZER_PLAIN(mutexname) } -+ -+#define __RT_MUTEX_INITIALIZER_SAVE_STATE(mutexname) \ -+ { __RT_MUTEX_INITIALIZER_PLAIN(mutexname) \ -+ , .save_state = 1 } - - #define DEFINE_RT_MUTEX(mutexname) \ - struct rt_mutex mutexname = __RT_MUTEX_INITIALIZER(mutexname) -@@ -91,6 +106,7 @@ - - extern void rt_mutex_lock(struct rt_mutex *lock); - extern int rt_mutex_lock_interruptible(struct rt_mutex *lock); -+extern int rt_mutex_lock_killable(struct rt_mutex *lock); - extern int rt_mutex_timed_lock(struct rt_mutex *lock, - struct hrtimer_sleeper *timeout); - -diff -Nur linux-3.18.14.orig/include/linux/rwlock_rt.h linux-3.18.14-rt/include/linux/rwlock_rt.h ---- linux-3.18.14.orig/include/linux/rwlock_rt.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/rwlock_rt.h 2015-05-31 15:32:48.377635367 -0500 -@@ -0,0 +1,99 @@ -+#ifndef __LINUX_RWLOCK_RT_H -+#define __LINUX_RWLOCK_RT_H -+ -+#ifndef __LINUX_SPINLOCK_H -+#error Do not include directly. Use spinlock.h -+#endif -+ -+#define rwlock_init(rwl) \ -+do { \ -+ static struct lock_class_key __key; \ -+ \ -+ rt_mutex_init(&(rwl)->lock); \ -+ __rt_rwlock_init(rwl, #rwl, &__key); \ -+} while (0) -+ -+extern void __lockfunc rt_write_lock(rwlock_t *rwlock); -+extern void __lockfunc rt_read_lock(rwlock_t *rwlock); -+extern int __lockfunc rt_write_trylock(rwlock_t *rwlock); -+extern int __lockfunc rt_write_trylock_irqsave(rwlock_t *trylock, unsigned long *flags); -+extern int __lockfunc rt_read_trylock(rwlock_t *rwlock); -+extern void __lockfunc rt_write_unlock(rwlock_t *rwlock); -+extern void __lockfunc rt_read_unlock(rwlock_t *rwlock); -+extern unsigned long __lockfunc rt_write_lock_irqsave(rwlock_t *rwlock); -+extern unsigned long __lockfunc rt_read_lock_irqsave(rwlock_t *rwlock); -+extern void __rt_rwlock_init(rwlock_t *rwlock, char *name, struct lock_class_key *key); -+ -+#define read_trylock(lock) __cond_lock(lock, rt_read_trylock(lock)) -+#define write_trylock(lock) __cond_lock(lock, rt_write_trylock(lock)) -+ -+#define write_trylock_irqsave(lock, flags) \ -+ __cond_lock(lock, rt_write_trylock_irqsave(lock, &flags)) -+ -+#define read_lock_irqsave(lock, flags) \ -+ do { \ -+ typecheck(unsigned long, flags); \ -+ flags = rt_read_lock_irqsave(lock); \ -+ } while (0) -+ -+#define write_lock_irqsave(lock, flags) \ -+ do { \ -+ typecheck(unsigned long, flags); \ -+ flags = rt_write_lock_irqsave(lock); \ -+ } while (0) -+ -+#define read_lock(lock) rt_read_lock(lock) -+ -+#define read_lock_bh(lock) \ -+ do { \ -+ local_bh_disable(); \ -+ rt_read_lock(lock); \ -+ } while (0) -+ -+#define read_lock_irq(lock) read_lock(lock) -+ -+#define write_lock(lock) rt_write_lock(lock) -+ -+#define write_lock_bh(lock) \ -+ do { \ -+ local_bh_disable(); \ -+ rt_write_lock(lock); \ -+ } while (0) -+ -+#define write_lock_irq(lock) write_lock(lock) -+ -+#define read_unlock(lock) rt_read_unlock(lock) -+ -+#define read_unlock_bh(lock) \ -+ do { \ -+ rt_read_unlock(lock); \ -+ local_bh_enable(); \ -+ } while (0) -+ -+#define read_unlock_irq(lock) read_unlock(lock) -+ -+#define write_unlock(lock) rt_write_unlock(lock) -+ -+#define write_unlock_bh(lock) \ -+ do { \ -+ rt_write_unlock(lock); \ -+ local_bh_enable(); \ -+ } while (0) -+ -+#define write_unlock_irq(lock) write_unlock(lock) -+ -+#define read_unlock_irqrestore(lock, flags) \ -+ do { \ -+ typecheck(unsigned long, flags); \ -+ (void) flags; \ -+ rt_read_unlock(lock); \ -+ } while (0) -+ -+#define write_unlock_irqrestore(lock, flags) \ -+ do { \ -+ typecheck(unsigned long, flags); \ -+ (void) flags; \ -+ rt_write_unlock(lock); \ -+ } while (0) -+ -+#endif -diff -Nur linux-3.18.14.orig/include/linux/rwlock_types.h linux-3.18.14-rt/include/linux/rwlock_types.h ---- linux-3.18.14.orig/include/linux/rwlock_types.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/rwlock_types.h 2015-05-31 15:32:48.377635367 -0500 -@@ -1,6 +1,10 @@ - #ifndef __LINUX_RWLOCK_TYPES_H - #define __LINUX_RWLOCK_TYPES_H - -+#if !defined(__LINUX_SPINLOCK_TYPES_H) -+# error "Do not include directly, include spinlock_types.h" -+#endif -+ - /* - * include/linux/rwlock_types.h - generic rwlock type definitions - * and initializers -@@ -43,6 +47,7 @@ - RW_DEP_MAP_INIT(lockname) } - #endif - --#define DEFINE_RWLOCK(x) rwlock_t x = __RW_LOCK_UNLOCKED(x) -+#define DEFINE_RWLOCK(name) \ -+ rwlock_t name __cacheline_aligned_in_smp = __RW_LOCK_UNLOCKED(name) - - #endif /* __LINUX_RWLOCK_TYPES_H */ -diff -Nur linux-3.18.14.orig/include/linux/rwlock_types_rt.h linux-3.18.14-rt/include/linux/rwlock_types_rt.h ---- linux-3.18.14.orig/include/linux/rwlock_types_rt.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/rwlock_types_rt.h 2015-05-31 15:32:48.377635367 -0500 -@@ -0,0 +1,33 @@ -+#ifndef __LINUX_RWLOCK_TYPES_RT_H -+#define __LINUX_RWLOCK_TYPES_RT_H -+ -+#ifndef __LINUX_SPINLOCK_TYPES_H -+#error "Do not include directly. Include spinlock_types.h instead" -+#endif -+ -+/* -+ * rwlocks - rtmutex which allows single reader recursion -+ */ -+typedef struct { -+ struct rt_mutex lock; -+ int read_depth; -+ unsigned int break_lock; -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ struct lockdep_map dep_map; -+#endif -+} rwlock_t; -+ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+# define RW_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname } -+#else -+# define RW_DEP_MAP_INIT(lockname) -+#endif -+ -+#define __RW_LOCK_UNLOCKED(name) \ -+ { .lock = __RT_MUTEX_INITIALIZER_SAVE_STATE(name.lock), \ -+ RW_DEP_MAP_INIT(name) } -+ -+#define DEFINE_RWLOCK(name) \ -+ rwlock_t name __cacheline_aligned_in_smp = __RW_LOCK_UNLOCKED(name) -+ -+#endif -diff -Nur linux-3.18.14.orig/include/linux/rwsem.h linux-3.18.14-rt/include/linux/rwsem.h ---- linux-3.18.14.orig/include/linux/rwsem.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/rwsem.h 2015-05-31 15:32:48.377635367 -0500 -@@ -18,6 +18,10 @@ - #include - #endif - -+#ifdef CONFIG_PREEMPT_RT_FULL -+#include -+#else /* PREEMPT_RT_FULL */ -+ - struct rw_semaphore; - - #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK -@@ -177,4 +181,6 @@ - # define up_read_non_owner(sem) up_read(sem) - #endif - -+#endif /* !PREEMPT_RT_FULL */ -+ - #endif /* _LINUX_RWSEM_H */ -diff -Nur linux-3.18.14.orig/include/linux/rwsem_rt.h linux-3.18.14-rt/include/linux/rwsem_rt.h ---- linux-3.18.14.orig/include/linux/rwsem_rt.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/rwsem_rt.h 2015-05-31 15:32:48.377635367 -0500 -@@ -0,0 +1,134 @@ -+#ifndef _LINUX_RWSEM_RT_H -+#define _LINUX_RWSEM_RT_H -+ -+#ifndef _LINUX_RWSEM_H -+#error "Include rwsem.h" -+#endif -+ -+/* -+ * RW-semaphores are a spinlock plus a reader-depth count. -+ * -+ * Note that the semantics are different from the usual -+ * Linux rw-sems, in PREEMPT_RT mode we do not allow -+ * multiple readers to hold the lock at once, we only allow -+ * a read-lock owner to read-lock recursively. This is -+ * better for latency, makes the implementation inherently -+ * fair and makes it simpler as well. -+ */ -+ -+#include -+ -+struct rw_semaphore { -+ struct rt_mutex lock; -+ int read_depth; -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ struct lockdep_map dep_map; -+#endif -+}; -+ -+#define __RWSEM_INITIALIZER(name) \ -+ { .lock = __RT_MUTEX_INITIALIZER(name.lock), \ -+ RW_DEP_MAP_INIT(name) } -+ -+#define DECLARE_RWSEM(lockname) \ -+ struct rw_semaphore lockname = __RWSEM_INITIALIZER(lockname) -+ -+extern void __rt_rwsem_init(struct rw_semaphore *rwsem, const char *name, -+ struct lock_class_key *key); -+ -+#define __rt_init_rwsem(sem, name, key) \ -+ do { \ -+ rt_mutex_init(&(sem)->lock); \ -+ __rt_rwsem_init((sem), (name), (key));\ -+ } while (0) -+ -+#define __init_rwsem(sem, name, key) __rt_init_rwsem(sem, name, key) -+ -+# define rt_init_rwsem(sem) \ -+do { \ -+ static struct lock_class_key __key; \ -+ \ -+ __rt_init_rwsem((sem), #sem, &__key); \ -+} while (0) -+ -+extern void rt_down_write(struct rw_semaphore *rwsem); -+extern void rt_down_read_nested(struct rw_semaphore *rwsem, int subclass); -+extern void rt_down_write_nested(struct rw_semaphore *rwsem, int subclass); -+extern void rt_down_write_nested_lock(struct rw_semaphore *rwsem, -+ struct lockdep_map *nest); -+extern void rt_down_read(struct rw_semaphore *rwsem); -+extern int rt_down_write_trylock(struct rw_semaphore *rwsem); -+extern int rt_down_read_trylock(struct rw_semaphore *rwsem); -+extern void rt_up_read(struct rw_semaphore *rwsem); -+extern void rt_up_write(struct rw_semaphore *rwsem); -+extern void rt_downgrade_write(struct rw_semaphore *rwsem); -+ -+#define init_rwsem(sem) rt_init_rwsem(sem) -+#define rwsem_is_locked(s) rt_mutex_is_locked(&(s)->lock) -+ -+static inline int rwsem_is_contended(struct rw_semaphore *sem) -+{ -+ /* rt_mutex_has_waiters() */ -+ return !RB_EMPTY_ROOT(&sem->lock.waiters); -+} -+ -+static inline void down_read(struct rw_semaphore *sem) -+{ -+ rt_down_read(sem); -+} -+ -+static inline int down_read_trylock(struct rw_semaphore *sem) -+{ -+ return rt_down_read_trylock(sem); -+} -+ -+static inline void down_write(struct rw_semaphore *sem) -+{ -+ rt_down_write(sem); -+} -+ -+static inline int down_write_trylock(struct rw_semaphore *sem) -+{ -+ return rt_down_write_trylock(sem); -+} -+ -+static inline void up_read(struct rw_semaphore *sem) -+{ -+ rt_up_read(sem); -+} -+ -+static inline void up_write(struct rw_semaphore *sem) -+{ -+ rt_up_write(sem); -+} -+ -+static inline void downgrade_write(struct rw_semaphore *sem) -+{ -+ rt_downgrade_write(sem); -+} -+ -+static inline void down_read_nested(struct rw_semaphore *sem, int subclass) -+{ -+ return rt_down_read_nested(sem, subclass); -+} -+ -+static inline void down_write_nested(struct rw_semaphore *sem, int subclass) -+{ -+ rt_down_write_nested(sem, subclass); -+} -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+static inline void down_write_nest_lock(struct rw_semaphore *sem, -+ struct rw_semaphore *nest_lock) -+{ -+ rt_down_write_nested_lock(sem, &nest_lock->dep_map); -+} -+ -+#else -+ -+static inline void down_write_nest_lock(struct rw_semaphore *sem, -+ struct rw_semaphore *nest_lock) -+{ -+ rt_down_write_nested_lock(sem, NULL); -+} -+#endif -+#endif -diff -Nur linux-3.18.14.orig/include/linux/sched.h linux-3.18.14-rt/include/linux/sched.h ---- linux-3.18.14.orig/include/linux/sched.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/sched.h 2015-05-31 15:32:48.381635367 -0500 -@@ -26,6 +26,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -56,6 +57,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -235,10 +237,7 @@ - TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \ - __TASK_TRACED | EXIT_ZOMBIE | EXIT_DEAD) - --#define task_is_traced(task) ((task->state & __TASK_TRACED) != 0) - #define task_is_stopped(task) ((task->state & __TASK_STOPPED) != 0) --#define task_is_stopped_or_traced(task) \ -- ((task->state & (__TASK_STOPPED | __TASK_TRACED)) != 0) - #define task_contributes_to_load(task) \ - ((task->state & TASK_UNINTERRUPTIBLE) != 0 && \ - (task->flags & PF_FROZEN) == 0) -@@ -1234,6 +1233,7 @@ - - struct task_struct { - volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ -+ volatile long saved_state; /* saved state for "spinlock sleepers" */ - void *stack; - atomic_t usage; - unsigned int flags; /* per process flags, defined below */ -@@ -1270,6 +1270,12 @@ - #endif - - unsigned int policy; -+#ifdef CONFIG_PREEMPT_RT_FULL -+ int migrate_disable; -+# ifdef CONFIG_SCHED_DEBUG -+ int migrate_disable_atomic; -+# endif -+#endif - int nr_cpus_allowed; - cpumask_t cpus_allowed; - -@@ -1371,7 +1377,8 @@ - struct cputime prev_cputime; - #endif - #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN -- seqlock_t vtime_seqlock; -+ raw_spinlock_t vtime_lock; -+ seqcount_t vtime_seq; - unsigned long long vtime_snap; - enum { - VTIME_SLEEPING = 0, -@@ -1387,6 +1394,9 @@ - - struct task_cputime cputime_expires; - struct list_head cpu_timers[3]; -+#ifdef CONFIG_PREEMPT_RT_BASE -+ struct task_struct *posix_timer_list; -+#endif - - /* process credentials */ - const struct cred __rcu *real_cred; /* objective and real subjective task -@@ -1419,10 +1429,15 @@ - /* signal handlers */ - struct signal_struct *signal; - struct sighand_struct *sighand; -+ struct sigqueue *sigqueue_cache; - - sigset_t blocked, real_blocked; - sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */ - struct sigpending pending; -+#ifdef CONFIG_PREEMPT_RT_FULL -+ /* TODO: move me into ->restart_block ? */ -+ struct siginfo forced_info; -+#endif - - unsigned long sas_ss_sp; - size_t sas_ss_size; -@@ -1460,6 +1475,9 @@ - /* mutex deadlock detection */ - struct mutex_waiter *blocked_on; - #endif -+#ifdef CONFIG_PREEMPT_RT_FULL -+ int pagefault_disabled; -+#endif - #ifdef CONFIG_TRACE_IRQFLAGS - unsigned int irq_events; - unsigned long hardirq_enable_ip; -@@ -1644,6 +1662,12 @@ - unsigned long trace; - /* bitmask and counter of trace recursion */ - unsigned long trace_recursion; -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ u64 preempt_timestamp_hist; -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ long timer_offset; -+#endif -+#endif - #endif /* CONFIG_TRACING */ - #ifdef CONFIG_MEMCG /* memcg uses this to do batch job */ - unsigned int memcg_kmem_skip_account; -@@ -1661,11 +1685,19 @@ - unsigned int sequential_io; - unsigned int sequential_io_avg; - #endif -+#ifdef CONFIG_PREEMPT_RT_BASE -+ struct rcu_head put_rcu; -+ int softirq_nestcnt; -+ unsigned int softirqs_raised; -+#endif -+#ifdef CONFIG_PREEMPT_RT_FULL -+# if defined CONFIG_HIGHMEM || defined CONFIG_X86_32 -+ int kmap_idx; -+ pte_t kmap_pte[KM_TYPE_NR]; -+# endif -+#endif - }; - --/* Future-safe accessor for struct task_struct's cpus_allowed. */ --#define tsk_cpus_allowed(tsk) (&(tsk)->cpus_allowed) -- - #define TNF_MIGRATED 0x01 - #define TNF_NO_GROUP 0x02 - #define TNF_SHARED 0x04 -@@ -1700,6 +1732,17 @@ - } - #endif - -+#ifdef CONFIG_PREEMPT_RT_FULL -+static inline bool cur_pf_disabled(void) { return current->pagefault_disabled; } -+#else -+static inline bool cur_pf_disabled(void) { return false; } -+#endif -+ -+static inline bool pagefault_disabled(void) -+{ -+ return in_atomic() || cur_pf_disabled(); -+} -+ - static inline struct pid *task_pid(struct task_struct *task) - { - return task->pids[PIDTYPE_PID].pid; -@@ -1853,6 +1896,15 @@ - extern void free_task(struct task_struct *tsk); - #define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while(0) - -+#ifdef CONFIG_PREEMPT_RT_BASE -+extern void __put_task_struct_cb(struct rcu_head *rhp); -+ -+static inline void put_task_struct(struct task_struct *t) -+{ -+ if (atomic_dec_and_test(&t->usage)) -+ call_rcu(&t->put_rcu, __put_task_struct_cb); -+} -+#else - extern void __put_task_struct(struct task_struct *t); - - static inline void put_task_struct(struct task_struct *t) -@@ -1860,6 +1912,7 @@ - if (atomic_dec_and_test(&t->usage)) - __put_task_struct(t); - } -+#endif - - #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN - extern void task_cputime(struct task_struct *t, -@@ -1898,6 +1951,7 @@ - /* - * Per process flags - */ -+#define PF_IN_SOFTIRQ 0x00000001 /* Task is serving softirq */ - #define PF_EXITING 0x00000004 /* getting shut down */ - #define PF_EXITPIDONE 0x00000008 /* pi exit done on shut down */ - #define PF_VCPU 0x00000010 /* I'm a virtual CPU */ -@@ -2058,6 +2112,10 @@ - - extern int set_cpus_allowed_ptr(struct task_struct *p, - const struct cpumask *new_mask); -+int migrate_me(void); -+void tell_sched_cpu_down_begin(int cpu); -+void tell_sched_cpu_down_done(int cpu); -+ - #else - static inline void do_set_cpus_allowed(struct task_struct *p, - const struct cpumask *new_mask) -@@ -2070,6 +2128,9 @@ - return -EINVAL; - return 0; - } -+static inline int migrate_me(void) { return 0; } -+static inline void tell_sched_cpu_down_begin(int cpu) { } -+static inline void tell_sched_cpu_down_done(int cpu) { } - #endif - - #ifdef CONFIG_NO_HZ_COMMON -@@ -2290,6 +2351,7 @@ - - extern int wake_up_state(struct task_struct *tsk, unsigned int state); - extern int wake_up_process(struct task_struct *tsk); -+extern int wake_up_lock_sleeper(struct task_struct * tsk); - extern void wake_up_new_task(struct task_struct *tsk); - #ifdef CONFIG_SMP - extern void kick_process(struct task_struct *tsk); -@@ -2406,12 +2468,24 @@ - - /* mmdrop drops the mm and the page tables */ - extern void __mmdrop(struct mm_struct *); -+ - static inline void mmdrop(struct mm_struct * mm) - { - if (unlikely(atomic_dec_and_test(&mm->mm_count))) - __mmdrop(mm); - } - -+#ifdef CONFIG_PREEMPT_RT_BASE -+extern void __mmdrop_delayed(struct rcu_head *rhp); -+static inline void mmdrop_delayed(struct mm_struct *mm) -+{ -+ if (atomic_dec_and_test(&mm->mm_count)) -+ call_rcu(&mm->delayed_drop, __mmdrop_delayed); -+} -+#else -+# define mmdrop_delayed(mm) mmdrop(mm) -+#endif -+ - /* mmput gets rid of the mappings and all user-space */ - extern void mmput(struct mm_struct *); - /* Grab a reference to a task's mm, if it is not already going away */ -@@ -2719,6 +2793,43 @@ - return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED)); - } - -+#ifdef CONFIG_PREEMPT_LAZY -+static inline void set_tsk_need_resched_lazy(struct task_struct *tsk) -+{ -+ set_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY); -+} -+ -+static inline void clear_tsk_need_resched_lazy(struct task_struct *tsk) -+{ -+ clear_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY); -+} -+ -+static inline int test_tsk_need_resched_lazy(struct task_struct *tsk) -+{ -+ return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY)); -+} -+ -+static inline int need_resched_lazy(void) -+{ -+ return test_thread_flag(TIF_NEED_RESCHED_LAZY); -+} -+ -+static inline int need_resched_now(void) -+{ -+ return test_thread_flag(TIF_NEED_RESCHED); -+} -+ -+#else -+static inline void clear_tsk_need_resched_lazy(struct task_struct *tsk) { } -+static inline int need_resched_lazy(void) { return 0; } -+ -+static inline int need_resched_now(void) -+{ -+ return test_thread_flag(TIF_NEED_RESCHED); -+} -+ -+#endif -+ - static inline int restart_syscall(void) - { - set_tsk_thread_flag(current, TIF_SIGPENDING); -@@ -2750,6 +2861,51 @@ - return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p); - } - -+static inline bool __task_is_stopped_or_traced(struct task_struct *task) -+{ -+ if (task->state & (__TASK_STOPPED | __TASK_TRACED)) -+ return true; -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (task->saved_state & (__TASK_STOPPED | __TASK_TRACED)) -+ return true; -+#endif -+ return false; -+} -+ -+static inline bool task_is_stopped_or_traced(struct task_struct *task) -+{ -+ bool traced_stopped; -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ unsigned long flags; -+ -+ raw_spin_lock_irqsave(&task->pi_lock, flags); -+ traced_stopped = __task_is_stopped_or_traced(task); -+ raw_spin_unlock_irqrestore(&task->pi_lock, flags); -+#else -+ traced_stopped = __task_is_stopped_or_traced(task); -+#endif -+ return traced_stopped; -+} -+ -+static inline bool task_is_traced(struct task_struct *task) -+{ -+ bool traced = false; -+ -+ if (task->state & __TASK_TRACED) -+ return true; -+#ifdef CONFIG_PREEMPT_RT_FULL -+ /* in case the task is sleeping on tasklist_lock */ -+ raw_spin_lock_irq(&task->pi_lock); -+ if (task->state & __TASK_TRACED) -+ traced = true; -+ else if (task->saved_state & __TASK_TRACED) -+ traced = true; -+ raw_spin_unlock_irq(&task->pi_lock); -+#endif -+ return traced; -+} -+ - /* - * cond_resched() and cond_resched_lock(): latency reduction via - * explicit rescheduling in places that are safe. The return -@@ -2766,7 +2922,7 @@ - - extern int __cond_resched_lock(spinlock_t *lock); - --#ifdef CONFIG_PREEMPT_COUNT -+#if defined(CONFIG_PREEMPT_COUNT) && !defined(CONFIG_PREEMPT_RT_FULL) - #define PREEMPT_LOCK_OFFSET PREEMPT_OFFSET - #else - #define PREEMPT_LOCK_OFFSET 0 -@@ -2777,12 +2933,16 @@ - __cond_resched_lock(lock); \ - }) - -+#ifndef CONFIG_PREEMPT_RT_FULL - extern int __cond_resched_softirq(void); - - #define cond_resched_softirq() ({ \ - __might_sleep(__FILE__, __LINE__, SOFTIRQ_DISABLE_OFFSET); \ - __cond_resched_softirq(); \ - }) -+#else -+# define cond_resched_softirq() cond_resched() -+#endif - - static inline void cond_resched_rcu(void) - { -@@ -2949,6 +3109,26 @@ - - #endif /* CONFIG_SMP */ - -+static inline int __migrate_disabled(struct task_struct *p) -+{ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ return p->migrate_disable; -+#else -+ return 0; -+#endif -+} -+ -+/* Future-safe accessor for struct task_struct's cpus_allowed. */ -+static inline const struct cpumask *tsk_cpus_allowed(struct task_struct *p) -+{ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (p->migrate_disable) -+ return cpumask_of(task_cpu(p)); -+#endif -+ -+ return &p->cpus_allowed; -+} -+ - extern long sched_setaffinity(pid_t pid, const struct cpumask *new_mask); - extern long sched_getaffinity(pid_t pid, struct cpumask *mask); - -diff -Nur linux-3.18.14.orig/include/linux/seqlock.h linux-3.18.14-rt/include/linux/seqlock.h ---- linux-3.18.14.orig/include/linux/seqlock.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/seqlock.h 2015-05-31 15:32:48.381635367 -0500 -@@ -219,20 +219,30 @@ - return __read_seqcount_retry(s, start); - } - -- -- --static inline void raw_write_seqcount_begin(seqcount_t *s) -+static inline void __raw_write_seqcount_begin(seqcount_t *s) - { - s->sequence++; - smp_wmb(); - } - --static inline void raw_write_seqcount_end(seqcount_t *s) -+static inline void raw_write_seqcount_begin(seqcount_t *s) -+{ -+ preempt_disable_rt(); -+ __raw_write_seqcount_begin(s); -+} -+ -+static inline void __raw_write_seqcount_end(seqcount_t *s) - { - smp_wmb(); - s->sequence++; - } - -+static inline void raw_write_seqcount_end(seqcount_t *s) -+{ -+ __raw_write_seqcount_end(s); -+ preempt_enable_rt(); -+} -+ - /* - * raw_write_seqcount_latch - redirect readers to even/odd copy - * @s: pointer to seqcount_t -@@ -305,10 +315,32 @@ - /* - * Read side functions for starting and finalizing a read side section. - */ -+#ifndef CONFIG_PREEMPT_RT_FULL - static inline unsigned read_seqbegin(const seqlock_t *sl) - { - return read_seqcount_begin(&sl->seqcount); - } -+#else -+/* -+ * Starvation safe read side for RT -+ */ -+static inline unsigned read_seqbegin(seqlock_t *sl) -+{ -+ unsigned ret; -+ -+repeat: -+ ret = ACCESS_ONCE(sl->seqcount.sequence); -+ if (unlikely(ret & 1)) { -+ /* -+ * Take the lock and let the writer proceed (i.e. evtl -+ * boost it), otherwise we could loop here forever. -+ */ -+ spin_unlock_wait(&sl->lock); -+ goto repeat; -+ } -+ return ret; -+} -+#endif - - static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start) - { -@@ -323,36 +355,36 @@ - static inline void write_seqlock(seqlock_t *sl) - { - spin_lock(&sl->lock); -- write_seqcount_begin(&sl->seqcount); -+ __raw_write_seqcount_begin(&sl->seqcount); - } - - static inline void write_sequnlock(seqlock_t *sl) - { -- write_seqcount_end(&sl->seqcount); -+ __raw_write_seqcount_end(&sl->seqcount); - spin_unlock(&sl->lock); - } - - static inline void write_seqlock_bh(seqlock_t *sl) - { - spin_lock_bh(&sl->lock); -- write_seqcount_begin(&sl->seqcount); -+ __raw_write_seqcount_begin(&sl->seqcount); - } - - static inline void write_sequnlock_bh(seqlock_t *sl) - { -- write_seqcount_end(&sl->seqcount); -+ __raw_write_seqcount_end(&sl->seqcount); - spin_unlock_bh(&sl->lock); - } - - static inline void write_seqlock_irq(seqlock_t *sl) - { - spin_lock_irq(&sl->lock); -- write_seqcount_begin(&sl->seqcount); -+ __raw_write_seqcount_begin(&sl->seqcount); - } - - static inline void write_sequnlock_irq(seqlock_t *sl) - { -- write_seqcount_end(&sl->seqcount); -+ __raw_write_seqcount_end(&sl->seqcount); - spin_unlock_irq(&sl->lock); - } - -@@ -361,7 +393,7 @@ - unsigned long flags; - - spin_lock_irqsave(&sl->lock, flags); -- write_seqcount_begin(&sl->seqcount); -+ __raw_write_seqcount_begin(&sl->seqcount); - return flags; - } - -@@ -371,7 +403,7 @@ - static inline void - write_sequnlock_irqrestore(seqlock_t *sl, unsigned long flags) - { -- write_seqcount_end(&sl->seqcount); -+ __raw_write_seqcount_end(&sl->seqcount); - spin_unlock_irqrestore(&sl->lock, flags); - } - -diff -Nur linux-3.18.14.orig/include/linux/signal.h linux-3.18.14-rt/include/linux/signal.h ---- linux-3.18.14.orig/include/linux/signal.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/signal.h 2015-05-31 15:32:48.381635367 -0500 -@@ -218,6 +218,7 @@ - } - - extern void flush_sigqueue(struct sigpending *queue); -+extern void flush_task_sigqueue(struct task_struct *tsk); - - /* Test if 'sig' is valid signal. Use this instead of testing _NSIG directly */ - static inline int valid_signal(unsigned long sig) -diff -Nur linux-3.18.14.orig/include/linux/skbuff.h linux-3.18.14-rt/include/linux/skbuff.h ---- linux-3.18.14.orig/include/linux/skbuff.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/skbuff.h 2015-05-31 15:32:48.405635367 -0500 -@@ -172,6 +172,7 @@ - - __u32 qlen; - spinlock_t lock; -+ raw_spinlock_t raw_lock; - }; - - struct sk_buff; -@@ -1328,6 +1329,12 @@ - __skb_queue_head_init(list); - } - -+static inline void skb_queue_head_init_raw(struct sk_buff_head *list) -+{ -+ raw_spin_lock_init(&list->raw_lock); -+ __skb_queue_head_init(list); -+} -+ - static inline void skb_queue_head_init_class(struct sk_buff_head *list, - struct lock_class_key *class) - { -diff -Nur linux-3.18.14.orig/include/linux/skbuff.h.orig linux-3.18.14-rt/include/linux/skbuff.h.orig ---- linux-3.18.14.orig/include/linux/skbuff.h.orig 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/skbuff.h.orig 2015-05-20 10:04:50.000000000 -0500 -@@ -0,0 +1,3364 @@ -+/* -+ * Definitions for the 'struct sk_buff' memory handlers. -+ * -+ * Authors: -+ * Alan Cox, -+ * Florian La Roche, -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _LINUX_SKBUFF_H -+#define _LINUX_SKBUFF_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* A. Checksumming of received packets by device. -+ * -+ * CHECKSUM_NONE: -+ * -+ * Device failed to checksum this packet e.g. due to lack of capabilities. -+ * The packet contains full (though not verified) checksum in packet but -+ * not in skb->csum. Thus, skb->csum is undefined in this case. -+ * -+ * CHECKSUM_UNNECESSARY: -+ * -+ * The hardware you're dealing with doesn't calculate the full checksum -+ * (as in CHECKSUM_COMPLETE), but it does parse headers and verify checksums -+ * for specific protocols. For such packets it will set CHECKSUM_UNNECESSARY -+ * if their checksums are okay. skb->csum is still undefined in this case -+ * though. It is a bad option, but, unfortunately, nowadays most vendors do -+ * this. Apparently with the secret goal to sell you new devices, when you -+ * will add new protocol to your host, f.e. IPv6 8) -+ * -+ * CHECKSUM_UNNECESSARY is applicable to following protocols: -+ * TCP: IPv6 and IPv4. -+ * UDP: IPv4 and IPv6. A device may apply CHECKSUM_UNNECESSARY to a -+ * zero UDP checksum for either IPv4 or IPv6, the networking stack -+ * may perform further validation in this case. -+ * GRE: only if the checksum is present in the header. -+ * SCTP: indicates the CRC in SCTP header has been validated. -+ * -+ * skb->csum_level indicates the number of consecutive checksums found in -+ * the packet minus one that have been verified as CHECKSUM_UNNECESSARY. -+ * For instance if a device receives an IPv6->UDP->GRE->IPv4->TCP packet -+ * and a device is able to verify the checksums for UDP (possibly zero), -+ * GRE (checksum flag is set), and TCP-- skb->csum_level would be set to -+ * two. If the device were only able to verify the UDP checksum and not -+ * GRE, either because it doesn't support GRE checksum of because GRE -+ * checksum is bad, skb->csum_level would be set to zero (TCP checksum is -+ * not considered in this case). -+ * -+ * CHECKSUM_COMPLETE: -+ * -+ * This is the most generic way. The device supplied checksum of the _whole_ -+ * packet as seen by netif_rx() and fills out in skb->csum. Meaning, the -+ * hardware doesn't need to parse L3/L4 headers to implement this. -+ * -+ * Note: Even if device supports only some protocols, but is able to produce -+ * skb->csum, it MUST use CHECKSUM_COMPLETE, not CHECKSUM_UNNECESSARY. -+ * -+ * CHECKSUM_PARTIAL: -+ * -+ * This is identical to the case for output below. This may occur on a packet -+ * received directly from another Linux OS, e.g., a virtualized Linux kernel -+ * on the same host. The packet can be treated in the same way as -+ * CHECKSUM_UNNECESSARY, except that on output (i.e., forwarding) the -+ * checksum must be filled in by the OS or the hardware. -+ * -+ * B. Checksumming on output. -+ * -+ * CHECKSUM_NONE: -+ * -+ * The skb was already checksummed by the protocol, or a checksum is not -+ * required. -+ * -+ * CHECKSUM_PARTIAL: -+ * -+ * The device is required to checksum the packet as seen by hard_start_xmit() -+ * from skb->csum_start up to the end, and to record/write the checksum at -+ * offset skb->csum_start + skb->csum_offset. -+ * -+ * The device must show its capabilities in dev->features, set up at device -+ * setup time, e.g. netdev_features.h: -+ * -+ * NETIF_F_HW_CSUM - It's a clever device, it's able to checksum everything. -+ * NETIF_F_IP_CSUM - Device is dumb, it's able to checksum only TCP/UDP over -+ * IPv4. Sigh. Vendors like this way for an unknown reason. -+ * Though, see comment above about CHECKSUM_UNNECESSARY. 8) -+ * NETIF_F_IPV6_CSUM - About as dumb as the last one but does IPv6 instead. -+ * NETIF_F_... - Well, you get the picture. -+ * -+ * CHECKSUM_UNNECESSARY: -+ * -+ * Normally, the device will do per protocol specific checksumming. Protocol -+ * implementations that do not want the NIC to perform the checksum -+ * calculation should use this flag in their outgoing skbs. -+ * -+ * NETIF_F_FCOE_CRC - This indicates that the device can do FCoE FC CRC -+ * offload. Correspondingly, the FCoE protocol driver -+ * stack should use CHECKSUM_UNNECESSARY. -+ * -+ * Any questions? No questions, good. --ANK -+ */ -+ -+/* Don't change this without changing skb_csum_unnecessary! */ -+#define CHECKSUM_NONE 0 -+#define CHECKSUM_UNNECESSARY 1 -+#define CHECKSUM_COMPLETE 2 -+#define CHECKSUM_PARTIAL 3 -+ -+/* Maximum value in skb->csum_level */ -+#define SKB_MAX_CSUM_LEVEL 3 -+ -+#define SKB_DATA_ALIGN(X) ALIGN(X, SMP_CACHE_BYTES) -+#define SKB_WITH_OVERHEAD(X) \ -+ ((X) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) -+#define SKB_MAX_ORDER(X, ORDER) \ -+ SKB_WITH_OVERHEAD((PAGE_SIZE << (ORDER)) - (X)) -+#define SKB_MAX_HEAD(X) (SKB_MAX_ORDER((X), 0)) -+#define SKB_MAX_ALLOC (SKB_MAX_ORDER(0, 2)) -+ -+/* return minimum truesize of one skb containing X bytes of data */ -+#define SKB_TRUESIZE(X) ((X) + \ -+ SKB_DATA_ALIGN(sizeof(struct sk_buff)) + \ -+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) -+ -+struct net_device; -+struct scatterlist; -+struct pipe_inode_info; -+ -+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) -+struct nf_conntrack { -+ atomic_t use; -+}; -+#endif -+ -+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) -+struct nf_bridge_info { -+ atomic_t use; -+ unsigned int mask; -+ struct net_device *physindev; -+ struct net_device *physoutdev; -+ unsigned long data[32 / sizeof(unsigned long)]; -+}; -+#endif -+ -+struct sk_buff_head { -+ /* These two members must be first. */ -+ struct sk_buff *next; -+ struct sk_buff *prev; -+ -+ __u32 qlen; -+ spinlock_t lock; -+}; -+ -+struct sk_buff; -+ -+/* To allow 64K frame to be packed as single skb without frag_list we -+ * require 64K/PAGE_SIZE pages plus 1 additional page to allow for -+ * buffers which do not start on a page boundary. -+ * -+ * Since GRO uses frags we allocate at least 16 regardless of page -+ * size. -+ */ -+#if (65536/PAGE_SIZE + 1) < 16 -+#define MAX_SKB_FRAGS 16UL -+#else -+#define MAX_SKB_FRAGS (65536/PAGE_SIZE + 1) -+#endif -+ -+typedef struct skb_frag_struct skb_frag_t; -+ -+struct skb_frag_struct { -+ struct { -+ struct page *p; -+ } page; -+#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536) -+ __u32 page_offset; -+ __u32 size; -+#else -+ __u16 page_offset; -+ __u16 size; -+#endif -+}; -+ -+static inline unsigned int skb_frag_size(const skb_frag_t *frag) -+{ -+ return frag->size; -+} -+ -+static inline void skb_frag_size_set(skb_frag_t *frag, unsigned int size) -+{ -+ frag->size = size; -+} -+ -+static inline void skb_frag_size_add(skb_frag_t *frag, int delta) -+{ -+ frag->size += delta; -+} -+ -+static inline void skb_frag_size_sub(skb_frag_t *frag, int delta) -+{ -+ frag->size -= delta; -+} -+ -+#define HAVE_HW_TIME_STAMP -+ -+/** -+ * struct skb_shared_hwtstamps - hardware time stamps -+ * @hwtstamp: hardware time stamp transformed into duration -+ * since arbitrary point in time -+ * -+ * Software time stamps generated by ktime_get_real() are stored in -+ * skb->tstamp. -+ * -+ * hwtstamps can only be compared against other hwtstamps from -+ * the same device. -+ * -+ * This structure is attached to packets as part of the -+ * &skb_shared_info. Use skb_hwtstamps() to get a pointer. -+ */ -+struct skb_shared_hwtstamps { -+ ktime_t hwtstamp; -+}; -+ -+/* Definitions for tx_flags in struct skb_shared_info */ -+enum { -+ /* generate hardware time stamp */ -+ SKBTX_HW_TSTAMP = 1 << 0, -+ -+ /* generate software time stamp when queueing packet to NIC */ -+ SKBTX_SW_TSTAMP = 1 << 1, -+ -+ /* device driver is going to provide hardware time stamp */ -+ SKBTX_IN_PROGRESS = 1 << 2, -+ -+ /* device driver supports TX zero-copy buffers */ -+ SKBTX_DEV_ZEROCOPY = 1 << 3, -+ -+ /* generate wifi status information (where possible) */ -+ SKBTX_WIFI_STATUS = 1 << 4, -+ -+ /* This indicates at least one fragment might be overwritten -+ * (as in vmsplice(), sendfile() ...) -+ * If we need to compute a TX checksum, we'll need to copy -+ * all frags to avoid possible bad checksum -+ */ -+ SKBTX_SHARED_FRAG = 1 << 5, -+ -+ /* generate software time stamp when entering packet scheduling */ -+ SKBTX_SCHED_TSTAMP = 1 << 6, -+ -+ /* generate software timestamp on peer data acknowledgment */ -+ SKBTX_ACK_TSTAMP = 1 << 7, -+}; -+ -+#define SKBTX_ANY_SW_TSTAMP (SKBTX_SW_TSTAMP | \ -+ SKBTX_SCHED_TSTAMP | \ -+ SKBTX_ACK_TSTAMP) -+#define SKBTX_ANY_TSTAMP (SKBTX_HW_TSTAMP | SKBTX_ANY_SW_TSTAMP) -+ -+/* -+ * The callback notifies userspace to release buffers when skb DMA is done in -+ * lower device, the skb last reference should be 0 when calling this. -+ * The zerocopy_success argument is true if zero copy transmit occurred, -+ * false on data copy or out of memory error caused by data copy attempt. -+ * The ctx field is used to track device context. -+ * The desc field is used to track userspace buffer index. -+ */ -+struct ubuf_info { -+ void (*callback)(struct ubuf_info *, bool zerocopy_success); -+ void *ctx; -+ unsigned long desc; -+}; -+ -+/* This data is invariant across clones and lives at -+ * the end of the header data, ie. at skb->end. -+ */ -+struct skb_shared_info { -+ unsigned char nr_frags; -+ __u8 tx_flags; -+ unsigned short gso_size; -+ /* Warning: this field is not always filled in (UFO)! */ -+ unsigned short gso_segs; -+ unsigned short gso_type; -+ struct sk_buff *frag_list; -+ struct skb_shared_hwtstamps hwtstamps; -+ u32 tskey; -+ __be32 ip6_frag_id; -+ -+ /* -+ * Warning : all fields before dataref are cleared in __alloc_skb() -+ */ -+ atomic_t dataref; -+ -+ /* Intermediate layers must ensure that destructor_arg -+ * remains valid until skb destructor */ -+ void * destructor_arg; -+ -+ /* must be last field, see pskb_expand_head() */ -+ skb_frag_t frags[MAX_SKB_FRAGS]; -+}; -+ -+/* We divide dataref into two halves. The higher 16 bits hold references -+ * to the payload part of skb->data. The lower 16 bits hold references to -+ * the entire skb->data. A clone of a headerless skb holds the length of -+ * the header in skb->hdr_len. -+ * -+ * All users must obey the rule that the skb->data reference count must be -+ * greater than or equal to the payload reference count. -+ * -+ * Holding a reference to the payload part means that the user does not -+ * care about modifications to the header part of skb->data. -+ */ -+#define SKB_DATAREF_SHIFT 16 -+#define SKB_DATAREF_MASK ((1 << SKB_DATAREF_SHIFT) - 1) -+ -+ -+enum { -+ SKB_FCLONE_UNAVAILABLE, /* skb has no fclone (from head_cache) */ -+ SKB_FCLONE_ORIG, /* orig skb (from fclone_cache) */ -+ SKB_FCLONE_CLONE, /* companion fclone skb (from fclone_cache) */ -+ SKB_FCLONE_FREE, /* this companion fclone skb is available */ -+}; -+ -+enum { -+ SKB_GSO_TCPV4 = 1 << 0, -+ SKB_GSO_UDP = 1 << 1, -+ -+ /* This indicates the skb is from an untrusted source. */ -+ SKB_GSO_DODGY = 1 << 2, -+ -+ /* This indicates the tcp segment has CWR set. */ -+ SKB_GSO_TCP_ECN = 1 << 3, -+ -+ SKB_GSO_TCPV6 = 1 << 4, -+ -+ SKB_GSO_FCOE = 1 << 5, -+ -+ SKB_GSO_GRE = 1 << 6, -+ -+ SKB_GSO_GRE_CSUM = 1 << 7, -+ -+ SKB_GSO_IPIP = 1 << 8, -+ -+ SKB_GSO_SIT = 1 << 9, -+ -+ SKB_GSO_UDP_TUNNEL = 1 << 10, -+ -+ SKB_GSO_UDP_TUNNEL_CSUM = 1 << 11, -+ -+ SKB_GSO_MPLS = 1 << 12, -+ -+}; -+ -+#if BITS_PER_LONG > 32 -+#define NET_SKBUFF_DATA_USES_OFFSET 1 -+#endif -+ -+#ifdef NET_SKBUFF_DATA_USES_OFFSET -+typedef unsigned int sk_buff_data_t; -+#else -+typedef unsigned char *sk_buff_data_t; -+#endif -+ -+/** -+ * struct skb_mstamp - multi resolution time stamps -+ * @stamp_us: timestamp in us resolution -+ * @stamp_jiffies: timestamp in jiffies -+ */ -+struct skb_mstamp { -+ union { -+ u64 v64; -+ struct { -+ u32 stamp_us; -+ u32 stamp_jiffies; -+ }; -+ }; -+}; -+ -+/** -+ * skb_mstamp_get - get current timestamp -+ * @cl: place to store timestamps -+ */ -+static inline void skb_mstamp_get(struct skb_mstamp *cl) -+{ -+ u64 val = local_clock(); -+ -+ do_div(val, NSEC_PER_USEC); -+ cl->stamp_us = (u32)val; -+ cl->stamp_jiffies = (u32)jiffies; -+} -+ -+/** -+ * skb_mstamp_delta - compute the difference in usec between two skb_mstamp -+ * @t1: pointer to newest sample -+ * @t0: pointer to oldest sample -+ */ -+static inline u32 skb_mstamp_us_delta(const struct skb_mstamp *t1, -+ const struct skb_mstamp *t0) -+{ -+ s32 delta_us = t1->stamp_us - t0->stamp_us; -+ u32 delta_jiffies = t1->stamp_jiffies - t0->stamp_jiffies; -+ -+ /* If delta_us is negative, this might be because interval is too big, -+ * or local_clock() drift is too big : fallback using jiffies. -+ */ -+ if (delta_us <= 0 || -+ delta_jiffies >= (INT_MAX / (USEC_PER_SEC / HZ))) -+ -+ delta_us = jiffies_to_usecs(delta_jiffies); -+ -+ return delta_us; -+} -+ -+ -+/** -+ * struct sk_buff - socket buffer -+ * @next: Next buffer in list -+ * @prev: Previous buffer in list -+ * @tstamp: Time we arrived/left -+ * @sk: Socket we are owned by -+ * @dev: Device we arrived on/are leaving by -+ * @cb: Control buffer. Free for use by every layer. Put private vars here -+ * @_skb_refdst: destination entry (with norefcount bit) -+ * @sp: the security path, used for xfrm -+ * @len: Length of actual data -+ * @data_len: Data length -+ * @mac_len: Length of link layer header -+ * @hdr_len: writable header length of cloned skb -+ * @csum: Checksum (must include start/offset pair) -+ * @csum_start: Offset from skb->head where checksumming should start -+ * @csum_offset: Offset from csum_start where checksum should be stored -+ * @priority: Packet queueing priority -+ * @ignore_df: allow local fragmentation -+ * @cloned: Head may be cloned (check refcnt to be sure) -+ * @ip_summed: Driver fed us an IP checksum -+ * @nohdr: Payload reference only, must not modify header -+ * @nfctinfo: Relationship of this skb to the connection -+ * @pkt_type: Packet class -+ * @fclone: skbuff clone status -+ * @ipvs_property: skbuff is owned by ipvs -+ * @peeked: this packet has been seen already, so stats have been -+ * done for it, don't do them again -+ * @nf_trace: netfilter packet trace flag -+ * @protocol: Packet protocol from driver -+ * @destructor: Destruct function -+ * @nfct: Associated connection, if any -+ * @nf_bridge: Saved data about a bridged frame - see br_netfilter.c -+ * @skb_iif: ifindex of device we arrived on -+ * @tc_index: Traffic control index -+ * @tc_verd: traffic control verdict -+ * @hash: the packet hash -+ * @queue_mapping: Queue mapping for multiqueue devices -+ * @xmit_more: More SKBs are pending for this queue -+ * @ndisc_nodetype: router type (from link layer) -+ * @ooo_okay: allow the mapping of a socket to a queue to be changed -+ * @l4_hash: indicate hash is a canonical 4-tuple hash over transport -+ * ports. -+ * @sw_hash: indicates hash was computed in software stack -+ * @wifi_acked_valid: wifi_acked was set -+ * @wifi_acked: whether frame was acked on wifi or not -+ * @no_fcs: Request NIC to treat last 4 bytes as Ethernet FCS -+ * @napi_id: id of the NAPI struct this skb came from -+ * @secmark: security marking -+ * @mark: Generic packet mark -+ * @dropcount: total number of sk_receive_queue overflows -+ * @vlan_proto: vlan encapsulation protocol -+ * @vlan_tci: vlan tag control information -+ * @inner_protocol: Protocol (encapsulation) -+ * @inner_transport_header: Inner transport layer header (encapsulation) -+ * @inner_network_header: Network layer header (encapsulation) -+ * @inner_mac_header: Link layer header (encapsulation) -+ * @transport_header: Transport layer header -+ * @network_header: Network layer header -+ * @mac_header: Link layer header -+ * @tail: Tail pointer -+ * @end: End pointer -+ * @head: Head of buffer -+ * @data: Data head pointer -+ * @truesize: Buffer size -+ * @users: User count - see {datagram,tcp}.c -+ */ -+ -+struct sk_buff { -+ /* These two members must be first. */ -+ struct sk_buff *next; -+ struct sk_buff *prev; -+ -+ union { -+ ktime_t tstamp; -+ struct skb_mstamp skb_mstamp; -+ }; -+ -+ struct sock *sk; -+ struct net_device *dev; -+ -+ /* -+ * This is the control buffer. It is free to use for every -+ * layer. Please put your private variables there. If you -+ * want to keep them across layers you have to do a skb_clone() -+ * first. This is owned by whoever has the skb queued ATM. -+ */ -+ char cb[48] __aligned(8); -+ -+ unsigned long _skb_refdst; -+ void (*destructor)(struct sk_buff *skb); -+#ifdef CONFIG_XFRM -+ struct sec_path *sp; -+#endif -+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) -+ struct nf_conntrack *nfct; -+#endif -+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) -+ struct nf_bridge_info *nf_bridge; -+#endif -+ unsigned int len, -+ data_len; -+ __u16 mac_len, -+ hdr_len; -+ -+ /* Following fields are _not_ copied in __copy_skb_header() -+ * Note that queue_mapping is here mostly to fill a hole. -+ */ -+ kmemcheck_bitfield_begin(flags1); -+ __u16 queue_mapping; -+ __u8 cloned:1, -+ nohdr:1, -+ fclone:2, -+ peeked:1, -+ head_frag:1, -+ xmit_more:1; -+ /* one bit hole */ -+ kmemcheck_bitfield_end(flags1); -+ -+ /* fields enclosed in headers_start/headers_end are copied -+ * using a single memcpy() in __copy_skb_header() -+ */ -+ /* private: */ -+ __u32 headers_start[0]; -+ /* public: */ -+ -+/* if you move pkt_type around you also must adapt those constants */ -+#ifdef __BIG_ENDIAN_BITFIELD -+#define PKT_TYPE_MAX (7 << 5) -+#else -+#define PKT_TYPE_MAX 7 -+#endif -+#define PKT_TYPE_OFFSET() offsetof(struct sk_buff, __pkt_type_offset) -+ -+ __u8 __pkt_type_offset[0]; -+ __u8 pkt_type:3; -+ __u8 pfmemalloc:1; -+ __u8 ignore_df:1; -+ __u8 nfctinfo:3; -+ -+ __u8 nf_trace:1; -+ __u8 ip_summed:2; -+ __u8 ooo_okay:1; -+ __u8 l4_hash:1; -+ __u8 sw_hash:1; -+ __u8 wifi_acked_valid:1; -+ __u8 wifi_acked:1; -+ -+ __u8 no_fcs:1; -+ /* Indicates the inner headers are valid in the skbuff. */ -+ __u8 encapsulation:1; -+ __u8 encap_hdr_csum:1; -+ __u8 csum_valid:1; -+ __u8 csum_complete_sw:1; -+ __u8 csum_level:2; -+ __u8 csum_bad:1; -+ -+#ifdef CONFIG_IPV6_NDISC_NODETYPE -+ __u8 ndisc_nodetype:2; -+#endif -+ __u8 ipvs_property:1; -+ __u8 inner_protocol_type:1; -+ /* 4 or 6 bit hole */ -+ -+#ifdef CONFIG_NET_SCHED -+ __u16 tc_index; /* traffic control index */ -+#ifdef CONFIG_NET_CLS_ACT -+ __u16 tc_verd; /* traffic control verdict */ -+#endif -+#endif -+ -+ union { -+ __wsum csum; -+ struct { -+ __u16 csum_start; -+ __u16 csum_offset; -+ }; -+ }; -+ __u32 priority; -+ int skb_iif; -+ __u32 hash; -+ __be16 vlan_proto; -+ __u16 vlan_tci; -+#ifdef CONFIG_NET_RX_BUSY_POLL -+ unsigned int napi_id; -+#endif -+#ifdef CONFIG_NETWORK_SECMARK -+ __u32 secmark; -+#endif -+ union { -+ __u32 mark; -+ __u32 dropcount; -+ __u32 reserved_tailroom; -+ }; -+ -+ union { -+ __be16 inner_protocol; -+ __u8 inner_ipproto; -+ }; -+ -+ __u16 inner_transport_header; -+ __u16 inner_network_header; -+ __u16 inner_mac_header; -+ -+ __be16 protocol; -+ __u16 transport_header; -+ __u16 network_header; -+ __u16 mac_header; -+ -+ /* private: */ -+ __u32 headers_end[0]; -+ /* public: */ -+ -+ /* These elements must be at the end, see alloc_skb() for details. */ -+ sk_buff_data_t tail; -+ sk_buff_data_t end; -+ unsigned char *head, -+ *data; -+ unsigned int truesize; -+ atomic_t users; -+}; -+ -+#ifdef __KERNEL__ -+/* -+ * Handling routines are only of interest to the kernel -+ */ -+#include -+ -+ -+#define SKB_ALLOC_FCLONE 0x01 -+#define SKB_ALLOC_RX 0x02 -+ -+/* Returns true if the skb was allocated from PFMEMALLOC reserves */ -+static inline bool skb_pfmemalloc(const struct sk_buff *skb) -+{ -+ return unlikely(skb->pfmemalloc); -+} -+ -+/* -+ * skb might have a dst pointer attached, refcounted or not. -+ * _skb_refdst low order bit is set if refcount was _not_ taken -+ */ -+#define SKB_DST_NOREF 1UL -+#define SKB_DST_PTRMASK ~(SKB_DST_NOREF) -+ -+/** -+ * skb_dst - returns skb dst_entry -+ * @skb: buffer -+ * -+ * Returns skb dst_entry, regardless of reference taken or not. -+ */ -+static inline struct dst_entry *skb_dst(const struct sk_buff *skb) -+{ -+ /* If refdst was not refcounted, check we still are in a -+ * rcu_read_lock section -+ */ -+ WARN_ON((skb->_skb_refdst & SKB_DST_NOREF) && -+ !rcu_read_lock_held() && -+ !rcu_read_lock_bh_held()); -+ return (struct dst_entry *)(skb->_skb_refdst & SKB_DST_PTRMASK); -+} -+ -+/** -+ * skb_dst_set - sets skb dst -+ * @skb: buffer -+ * @dst: dst entry -+ * -+ * Sets skb dst, assuming a reference was taken on dst and should -+ * be released by skb_dst_drop() -+ */ -+static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst) -+{ -+ skb->_skb_refdst = (unsigned long)dst; -+} -+ -+void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst, -+ bool force); -+ -+/** -+ * skb_dst_set_noref - sets skb dst, hopefully, without taking reference -+ * @skb: buffer -+ * @dst: dst entry -+ * -+ * Sets skb dst, assuming a reference was not taken on dst. -+ * If dst entry is cached, we do not take reference and dst_release -+ * will be avoided by refdst_drop. If dst entry is not cached, we take -+ * reference, so that last dst_release can destroy the dst immediately. -+ */ -+static inline void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst) -+{ -+ __skb_dst_set_noref(skb, dst, false); -+} -+ -+/** -+ * skb_dst_set_noref_force - sets skb dst, without taking reference -+ * @skb: buffer -+ * @dst: dst entry -+ * -+ * Sets skb dst, assuming a reference was not taken on dst. -+ * No reference is taken and no dst_release will be called. While for -+ * cached dsts deferred reclaim is a basic feature, for entries that are -+ * not cached it is caller's job to guarantee that last dst_release for -+ * provided dst happens when nobody uses it, eg. after a RCU grace period. -+ */ -+static inline void skb_dst_set_noref_force(struct sk_buff *skb, -+ struct dst_entry *dst) -+{ -+ __skb_dst_set_noref(skb, dst, true); -+} -+ -+/** -+ * skb_dst_is_noref - Test if skb dst isn't refcounted -+ * @skb: buffer -+ */ -+static inline bool skb_dst_is_noref(const struct sk_buff *skb) -+{ -+ return (skb->_skb_refdst & SKB_DST_NOREF) && skb_dst(skb); -+} -+ -+static inline struct rtable *skb_rtable(const struct sk_buff *skb) -+{ -+ return (struct rtable *)skb_dst(skb); -+} -+ -+void kfree_skb(struct sk_buff *skb); -+void kfree_skb_list(struct sk_buff *segs); -+void skb_tx_error(struct sk_buff *skb); -+void consume_skb(struct sk_buff *skb); -+void __kfree_skb(struct sk_buff *skb); -+extern struct kmem_cache *skbuff_head_cache; -+ -+void kfree_skb_partial(struct sk_buff *skb, bool head_stolen); -+bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, -+ bool *fragstolen, int *delta_truesize); -+ -+struct sk_buff *__alloc_skb(unsigned int size, gfp_t priority, int flags, -+ int node); -+struct sk_buff *__build_skb(void *data, unsigned int frag_size); -+struct sk_buff *build_skb(void *data, unsigned int frag_size); -+static inline struct sk_buff *alloc_skb(unsigned int size, -+ gfp_t priority) -+{ -+ return __alloc_skb(size, priority, 0, NUMA_NO_NODE); -+} -+ -+struct sk_buff *alloc_skb_with_frags(unsigned long header_len, -+ unsigned long data_len, -+ int max_page_order, -+ int *errcode, -+ gfp_t gfp_mask); -+ -+/* Layout of fast clones : [skb1][skb2][fclone_ref] */ -+struct sk_buff_fclones { -+ struct sk_buff skb1; -+ -+ struct sk_buff skb2; -+ -+ atomic_t fclone_ref; -+}; -+ -+/** -+ * skb_fclone_busy - check if fclone is busy -+ * @skb: buffer -+ * -+ * Returns true is skb is a fast clone, and its clone is not freed. -+ * Some drivers call skb_orphan() in their ndo_start_xmit(), -+ * so we also check that this didnt happen. -+ */ -+static inline bool skb_fclone_busy(const struct sock *sk, -+ const struct sk_buff *skb) -+{ -+ const struct sk_buff_fclones *fclones; -+ -+ fclones = container_of(skb, struct sk_buff_fclones, skb1); -+ -+ return skb->fclone == SKB_FCLONE_ORIG && -+ fclones->skb2.fclone == SKB_FCLONE_CLONE && -+ fclones->skb2.sk == sk; -+} -+ -+static inline struct sk_buff *alloc_skb_fclone(unsigned int size, -+ gfp_t priority) -+{ -+ return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, NUMA_NO_NODE); -+} -+ -+struct sk_buff *__alloc_skb_head(gfp_t priority, int node); -+static inline struct sk_buff *alloc_skb_head(gfp_t priority) -+{ -+ return __alloc_skb_head(priority, -1); -+} -+ -+struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src); -+int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask); -+struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t priority); -+struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t priority); -+struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom, -+ gfp_t gfp_mask, bool fclone); -+static inline struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, -+ gfp_t gfp_mask) -+{ -+ return __pskb_copy_fclone(skb, headroom, gfp_mask, false); -+} -+ -+int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask); -+struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, -+ unsigned int headroom); -+struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom, -+ int newtailroom, gfp_t priority); -+int skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg, -+ int offset, int len); -+int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, -+ int len); -+int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer); -+int skb_pad(struct sk_buff *skb, int pad); -+#define dev_kfree_skb(a) consume_skb(a) -+ -+int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, -+ int getfrag(void *from, char *to, int offset, -+ int len, int odd, struct sk_buff *skb), -+ void *from, int length); -+ -+struct skb_seq_state { -+ __u32 lower_offset; -+ __u32 upper_offset; -+ __u32 frag_idx; -+ __u32 stepped_offset; -+ struct sk_buff *root_skb; -+ struct sk_buff *cur_skb; -+ __u8 *frag_data; -+}; -+ -+void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from, -+ unsigned int to, struct skb_seq_state *st); -+unsigned int skb_seq_read(unsigned int consumed, const u8 **data, -+ struct skb_seq_state *st); -+void skb_abort_seq_read(struct skb_seq_state *st); -+ -+unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, -+ unsigned int to, struct ts_config *config, -+ struct ts_state *state); -+ -+/* -+ * Packet hash types specify the type of hash in skb_set_hash. -+ * -+ * Hash types refer to the protocol layer addresses which are used to -+ * construct a packet's hash. The hashes are used to differentiate or identify -+ * flows of the protocol layer for the hash type. Hash types are either -+ * layer-2 (L2), layer-3 (L3), or layer-4 (L4). -+ * -+ * Properties of hashes: -+ * -+ * 1) Two packets in different flows have different hash values -+ * 2) Two packets in the same flow should have the same hash value -+ * -+ * A hash at a higher layer is considered to be more specific. A driver should -+ * set the most specific hash possible. -+ * -+ * A driver cannot indicate a more specific hash than the layer at which a hash -+ * was computed. For instance an L3 hash cannot be set as an L4 hash. -+ * -+ * A driver may indicate a hash level which is less specific than the -+ * actual layer the hash was computed on. For instance, a hash computed -+ * at L4 may be considered an L3 hash. This should only be done if the -+ * driver can't unambiguously determine that the HW computed the hash at -+ * the higher layer. Note that the "should" in the second property above -+ * permits this. -+ */ -+enum pkt_hash_types { -+ PKT_HASH_TYPE_NONE, /* Undefined type */ -+ PKT_HASH_TYPE_L2, /* Input: src_MAC, dest_MAC */ -+ PKT_HASH_TYPE_L3, /* Input: src_IP, dst_IP */ -+ PKT_HASH_TYPE_L4, /* Input: src_IP, dst_IP, src_port, dst_port */ -+}; -+ -+static inline void -+skb_set_hash(struct sk_buff *skb, __u32 hash, enum pkt_hash_types type) -+{ -+ skb->l4_hash = (type == PKT_HASH_TYPE_L4); -+ skb->sw_hash = 0; -+ skb->hash = hash; -+} -+ -+void __skb_get_hash(struct sk_buff *skb); -+static inline __u32 skb_get_hash(struct sk_buff *skb) -+{ -+ if (!skb->l4_hash && !skb->sw_hash) -+ __skb_get_hash(skb); -+ -+ return skb->hash; -+} -+ -+static inline __u32 skb_get_hash_raw(const struct sk_buff *skb) -+{ -+ return skb->hash; -+} -+ -+static inline void skb_clear_hash(struct sk_buff *skb) -+{ -+ skb->hash = 0; -+ skb->sw_hash = 0; -+ skb->l4_hash = 0; -+} -+ -+static inline void skb_clear_hash_if_not_l4(struct sk_buff *skb) -+{ -+ if (!skb->l4_hash) -+ skb_clear_hash(skb); -+} -+ -+static inline void skb_copy_hash(struct sk_buff *to, const struct sk_buff *from) -+{ -+ to->hash = from->hash; -+ to->sw_hash = from->sw_hash; -+ to->l4_hash = from->l4_hash; -+}; -+ -+#ifdef NET_SKBUFF_DATA_USES_OFFSET -+static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) -+{ -+ return skb->head + skb->end; -+} -+ -+static inline unsigned int skb_end_offset(const struct sk_buff *skb) -+{ -+ return skb->end; -+} -+#else -+static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) -+{ -+ return skb->end; -+} -+ -+static inline unsigned int skb_end_offset(const struct sk_buff *skb) -+{ -+ return skb->end - skb->head; -+} -+#endif -+ -+/* Internal */ -+#define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB))) -+ -+static inline struct skb_shared_hwtstamps *skb_hwtstamps(struct sk_buff *skb) -+{ -+ return &skb_shinfo(skb)->hwtstamps; -+} -+ -+/** -+ * skb_queue_empty - check if a queue is empty -+ * @list: queue head -+ * -+ * Returns true if the queue is empty, false otherwise. -+ */ -+static inline int skb_queue_empty(const struct sk_buff_head *list) -+{ -+ return list->next == (const struct sk_buff *) list; -+} -+ -+/** -+ * skb_queue_is_last - check if skb is the last entry in the queue -+ * @list: queue head -+ * @skb: buffer -+ * -+ * Returns true if @skb is the last buffer on the list. -+ */ -+static inline bool skb_queue_is_last(const struct sk_buff_head *list, -+ const struct sk_buff *skb) -+{ -+ return skb->next == (const struct sk_buff *) list; -+} -+ -+/** -+ * skb_queue_is_first - check if skb is the first entry in the queue -+ * @list: queue head -+ * @skb: buffer -+ * -+ * Returns true if @skb is the first buffer on the list. -+ */ -+static inline bool skb_queue_is_first(const struct sk_buff_head *list, -+ const struct sk_buff *skb) -+{ -+ return skb->prev == (const struct sk_buff *) list; -+} -+ -+/** -+ * skb_queue_next - return the next packet in the queue -+ * @list: queue head -+ * @skb: current buffer -+ * -+ * Return the next packet in @list after @skb. It is only valid to -+ * call this if skb_queue_is_last() evaluates to false. -+ */ -+static inline struct sk_buff *skb_queue_next(const struct sk_buff_head *list, -+ const struct sk_buff *skb) -+{ -+ /* This BUG_ON may seem severe, but if we just return then we -+ * are going to dereference garbage. -+ */ -+ BUG_ON(skb_queue_is_last(list, skb)); -+ return skb->next; -+} -+ -+/** -+ * skb_queue_prev - return the prev packet in the queue -+ * @list: queue head -+ * @skb: current buffer -+ * -+ * Return the prev packet in @list before @skb. It is only valid to -+ * call this if skb_queue_is_first() evaluates to false. -+ */ -+static inline struct sk_buff *skb_queue_prev(const struct sk_buff_head *list, -+ const struct sk_buff *skb) -+{ -+ /* This BUG_ON may seem severe, but if we just return then we -+ * are going to dereference garbage. -+ */ -+ BUG_ON(skb_queue_is_first(list, skb)); -+ return skb->prev; -+} -+ -+/** -+ * skb_get - reference buffer -+ * @skb: buffer to reference -+ * -+ * Makes another reference to a socket buffer and returns a pointer -+ * to the buffer. -+ */ -+static inline struct sk_buff *skb_get(struct sk_buff *skb) -+{ -+ atomic_inc(&skb->users); -+ return skb; -+} -+ -+/* -+ * If users == 1, we are the only owner and are can avoid redundant -+ * atomic change. -+ */ -+ -+/** -+ * skb_cloned - is the buffer a clone -+ * @skb: buffer to check -+ * -+ * Returns true if the buffer was generated with skb_clone() and is -+ * one of multiple shared copies of the buffer. Cloned buffers are -+ * shared data so must not be written to under normal circumstances. -+ */ -+static inline int skb_cloned(const struct sk_buff *skb) -+{ -+ return skb->cloned && -+ (atomic_read(&skb_shinfo(skb)->dataref) & SKB_DATAREF_MASK) != 1; -+} -+ -+static inline int skb_unclone(struct sk_buff *skb, gfp_t pri) -+{ -+ might_sleep_if(pri & __GFP_WAIT); -+ -+ if (skb_cloned(skb)) -+ return pskb_expand_head(skb, 0, 0, pri); -+ -+ return 0; -+} -+ -+/** -+ * skb_header_cloned - is the header a clone -+ * @skb: buffer to check -+ * -+ * Returns true if modifying the header part of the buffer requires -+ * the data to be copied. -+ */ -+static inline int skb_header_cloned(const struct sk_buff *skb) -+{ -+ int dataref; -+ -+ if (!skb->cloned) -+ return 0; -+ -+ dataref = atomic_read(&skb_shinfo(skb)->dataref); -+ dataref = (dataref & SKB_DATAREF_MASK) - (dataref >> SKB_DATAREF_SHIFT); -+ return dataref != 1; -+} -+ -+/** -+ * skb_header_release - release reference to header -+ * @skb: buffer to operate on -+ * -+ * Drop a reference to the header part of the buffer. This is done -+ * by acquiring a payload reference. You must not read from the header -+ * part of skb->data after this. -+ * Note : Check if you can use __skb_header_release() instead. -+ */ -+static inline void skb_header_release(struct sk_buff *skb) -+{ -+ BUG_ON(skb->nohdr); -+ skb->nohdr = 1; -+ atomic_add(1 << SKB_DATAREF_SHIFT, &skb_shinfo(skb)->dataref); -+} -+ -+/** -+ * __skb_header_release - release reference to header -+ * @skb: buffer to operate on -+ * -+ * Variant of skb_header_release() assuming skb is private to caller. -+ * We can avoid one atomic operation. -+ */ -+static inline void __skb_header_release(struct sk_buff *skb) -+{ -+ skb->nohdr = 1; -+ atomic_set(&skb_shinfo(skb)->dataref, 1 + (1 << SKB_DATAREF_SHIFT)); -+} -+ -+ -+/** -+ * skb_shared - is the buffer shared -+ * @skb: buffer to check -+ * -+ * Returns true if more than one person has a reference to this -+ * buffer. -+ */ -+static inline int skb_shared(const struct sk_buff *skb) -+{ -+ return atomic_read(&skb->users) != 1; -+} -+ -+/** -+ * skb_share_check - check if buffer is shared and if so clone it -+ * @skb: buffer to check -+ * @pri: priority for memory allocation -+ * -+ * If the buffer is shared the buffer is cloned and the old copy -+ * drops a reference. A new clone with a single reference is returned. -+ * If the buffer is not shared the original buffer is returned. When -+ * being called from interrupt status or with spinlocks held pri must -+ * be GFP_ATOMIC. -+ * -+ * NULL is returned on a memory allocation failure. -+ */ -+static inline struct sk_buff *skb_share_check(struct sk_buff *skb, gfp_t pri) -+{ -+ might_sleep_if(pri & __GFP_WAIT); -+ if (skb_shared(skb)) { -+ struct sk_buff *nskb = skb_clone(skb, pri); -+ -+ if (likely(nskb)) -+ consume_skb(skb); -+ else -+ kfree_skb(skb); -+ skb = nskb; -+ } -+ return skb; -+} -+ -+/* -+ * Copy shared buffers into a new sk_buff. We effectively do COW on -+ * packets to handle cases where we have a local reader and forward -+ * and a couple of other messy ones. The normal one is tcpdumping -+ * a packet thats being forwarded. -+ */ -+ -+/** -+ * skb_unshare - make a copy of a shared buffer -+ * @skb: buffer to check -+ * @pri: priority for memory allocation -+ * -+ * If the socket buffer is a clone then this function creates a new -+ * copy of the data, drops a reference count on the old copy and returns -+ * the new copy with the reference count at 1. If the buffer is not a clone -+ * the original buffer is returned. When called with a spinlock held or -+ * from interrupt state @pri must be %GFP_ATOMIC -+ * -+ * %NULL is returned on a memory allocation failure. -+ */ -+static inline struct sk_buff *skb_unshare(struct sk_buff *skb, -+ gfp_t pri) -+{ -+ might_sleep_if(pri & __GFP_WAIT); -+ if (skb_cloned(skb)) { -+ struct sk_buff *nskb = skb_copy(skb, pri); -+ -+ /* Free our shared copy */ -+ if (likely(nskb)) -+ consume_skb(skb); -+ else -+ kfree_skb(skb); -+ skb = nskb; -+ } -+ return skb; -+} -+ -+/** -+ * skb_peek - peek at the head of an &sk_buff_head -+ * @list_: list to peek at -+ * -+ * Peek an &sk_buff. Unlike most other operations you _MUST_ -+ * be careful with this one. A peek leaves the buffer on the -+ * list and someone else may run off with it. You must hold -+ * the appropriate locks or have a private queue to do this. -+ * -+ * Returns %NULL for an empty list or a pointer to the head element. -+ * The reference count is not incremented and the reference is therefore -+ * volatile. Use with caution. -+ */ -+static inline struct sk_buff *skb_peek(const struct sk_buff_head *list_) -+{ -+ struct sk_buff *skb = list_->next; -+ -+ if (skb == (struct sk_buff *)list_) -+ skb = NULL; -+ return skb; -+} -+ -+/** -+ * skb_peek_next - peek skb following the given one from a queue -+ * @skb: skb to start from -+ * @list_: list to peek at -+ * -+ * Returns %NULL when the end of the list is met or a pointer to the -+ * next element. The reference count is not incremented and the -+ * reference is therefore volatile. Use with caution. -+ */ -+static inline struct sk_buff *skb_peek_next(struct sk_buff *skb, -+ const struct sk_buff_head *list_) -+{ -+ struct sk_buff *next = skb->next; -+ -+ if (next == (struct sk_buff *)list_) -+ next = NULL; -+ return next; -+} -+ -+/** -+ * skb_peek_tail - peek at the tail of an &sk_buff_head -+ * @list_: list to peek at -+ * -+ * Peek an &sk_buff. Unlike most other operations you _MUST_ -+ * be careful with this one. A peek leaves the buffer on the -+ * list and someone else may run off with it. You must hold -+ * the appropriate locks or have a private queue to do this. -+ * -+ * Returns %NULL for an empty list or a pointer to the tail element. -+ * The reference count is not incremented and the reference is therefore -+ * volatile. Use with caution. -+ */ -+static inline struct sk_buff *skb_peek_tail(const struct sk_buff_head *list_) -+{ -+ struct sk_buff *skb = list_->prev; -+ -+ if (skb == (struct sk_buff *)list_) -+ skb = NULL; -+ return skb; -+ -+} -+ -+/** -+ * skb_queue_len - get queue length -+ * @list_: list to measure -+ * -+ * Return the length of an &sk_buff queue. -+ */ -+static inline __u32 skb_queue_len(const struct sk_buff_head *list_) -+{ -+ return list_->qlen; -+} -+ -+/** -+ * __skb_queue_head_init - initialize non-spinlock portions of sk_buff_head -+ * @list: queue to initialize -+ * -+ * This initializes only the list and queue length aspects of -+ * an sk_buff_head object. This allows to initialize the list -+ * aspects of an sk_buff_head without reinitializing things like -+ * the spinlock. It can also be used for on-stack sk_buff_head -+ * objects where the spinlock is known to not be used. -+ */ -+static inline void __skb_queue_head_init(struct sk_buff_head *list) -+{ -+ list->prev = list->next = (struct sk_buff *)list; -+ list->qlen = 0; -+} -+ -+/* -+ * This function creates a split out lock class for each invocation; -+ * this is needed for now since a whole lot of users of the skb-queue -+ * infrastructure in drivers have different locking usage (in hardirq) -+ * than the networking core (in softirq only). In the long run either the -+ * network layer or drivers should need annotation to consolidate the -+ * main types of usage into 3 classes. -+ */ -+static inline void skb_queue_head_init(struct sk_buff_head *list) -+{ -+ spin_lock_init(&list->lock); -+ __skb_queue_head_init(list); -+} -+ -+static inline void skb_queue_head_init_class(struct sk_buff_head *list, -+ struct lock_class_key *class) -+{ -+ skb_queue_head_init(list); -+ lockdep_set_class(&list->lock, class); -+} -+ -+/* -+ * Insert an sk_buff on a list. -+ * -+ * The "__skb_xxxx()" functions are the non-atomic ones that -+ * can only be called with interrupts disabled. -+ */ -+void skb_insert(struct sk_buff *old, struct sk_buff *newsk, -+ struct sk_buff_head *list); -+static inline void __skb_insert(struct sk_buff *newsk, -+ struct sk_buff *prev, struct sk_buff *next, -+ struct sk_buff_head *list) -+{ -+ newsk->next = next; -+ newsk->prev = prev; -+ next->prev = prev->next = newsk; -+ list->qlen++; -+} -+ -+static inline void __skb_queue_splice(const struct sk_buff_head *list, -+ struct sk_buff *prev, -+ struct sk_buff *next) -+{ -+ struct sk_buff *first = list->next; -+ struct sk_buff *last = list->prev; -+ -+ first->prev = prev; -+ prev->next = first; -+ -+ last->next = next; -+ next->prev = last; -+} -+ -+/** -+ * skb_queue_splice - join two skb lists, this is designed for stacks -+ * @list: the new list to add -+ * @head: the place to add it in the first list -+ */ -+static inline void skb_queue_splice(const struct sk_buff_head *list, -+ struct sk_buff_head *head) -+{ -+ if (!skb_queue_empty(list)) { -+ __skb_queue_splice(list, (struct sk_buff *) head, head->next); -+ head->qlen += list->qlen; -+ } -+} -+ -+/** -+ * skb_queue_splice_init - join two skb lists and reinitialise the emptied list -+ * @list: the new list to add -+ * @head: the place to add it in the first list -+ * -+ * The list at @list is reinitialised -+ */ -+static inline void skb_queue_splice_init(struct sk_buff_head *list, -+ struct sk_buff_head *head) -+{ -+ if (!skb_queue_empty(list)) { -+ __skb_queue_splice(list, (struct sk_buff *) head, head->next); -+ head->qlen += list->qlen; -+ __skb_queue_head_init(list); -+ } -+} -+ -+/** -+ * skb_queue_splice_tail - join two skb lists, each list being a queue -+ * @list: the new list to add -+ * @head: the place to add it in the first list -+ */ -+static inline void skb_queue_splice_tail(const struct sk_buff_head *list, -+ struct sk_buff_head *head) -+{ -+ if (!skb_queue_empty(list)) { -+ __skb_queue_splice(list, head->prev, (struct sk_buff *) head); -+ head->qlen += list->qlen; -+ } -+} -+ -+/** -+ * skb_queue_splice_tail_init - join two skb lists and reinitialise the emptied list -+ * @list: the new list to add -+ * @head: the place to add it in the first list -+ * -+ * Each of the lists is a queue. -+ * The list at @list is reinitialised -+ */ -+static inline void skb_queue_splice_tail_init(struct sk_buff_head *list, -+ struct sk_buff_head *head) -+{ -+ if (!skb_queue_empty(list)) { -+ __skb_queue_splice(list, head->prev, (struct sk_buff *) head); -+ head->qlen += list->qlen; -+ __skb_queue_head_init(list); -+ } -+} -+ -+/** -+ * __skb_queue_after - queue a buffer at the list head -+ * @list: list to use -+ * @prev: place after this buffer -+ * @newsk: buffer to queue -+ * -+ * Queue a buffer int the middle of a list. This function takes no locks -+ * and you must therefore hold required locks before calling it. -+ * -+ * A buffer cannot be placed on two lists at the same time. -+ */ -+static inline void __skb_queue_after(struct sk_buff_head *list, -+ struct sk_buff *prev, -+ struct sk_buff *newsk) -+{ -+ __skb_insert(newsk, prev, prev->next, list); -+} -+ -+void skb_append(struct sk_buff *old, struct sk_buff *newsk, -+ struct sk_buff_head *list); -+ -+static inline void __skb_queue_before(struct sk_buff_head *list, -+ struct sk_buff *next, -+ struct sk_buff *newsk) -+{ -+ __skb_insert(newsk, next->prev, next, list); -+} -+ -+/** -+ * __skb_queue_head - queue a buffer at the list head -+ * @list: list to use -+ * @newsk: buffer to queue -+ * -+ * Queue a buffer at the start of a list. This function takes no locks -+ * and you must therefore hold required locks before calling it. -+ * -+ * A buffer cannot be placed on two lists at the same time. -+ */ -+void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk); -+static inline void __skb_queue_head(struct sk_buff_head *list, -+ struct sk_buff *newsk) -+{ -+ __skb_queue_after(list, (struct sk_buff *)list, newsk); -+} -+ -+/** -+ * __skb_queue_tail - queue a buffer at the list tail -+ * @list: list to use -+ * @newsk: buffer to queue -+ * -+ * Queue a buffer at the end of a list. This function takes no locks -+ * and you must therefore hold required locks before calling it. -+ * -+ * A buffer cannot be placed on two lists at the same time. -+ */ -+void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk); -+static inline void __skb_queue_tail(struct sk_buff_head *list, -+ struct sk_buff *newsk) -+{ -+ __skb_queue_before(list, (struct sk_buff *)list, newsk); -+} -+ -+/* -+ * remove sk_buff from list. _Must_ be called atomically, and with -+ * the list known.. -+ */ -+void skb_unlink(struct sk_buff *skb, struct sk_buff_head *list); -+static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list) -+{ -+ struct sk_buff *next, *prev; -+ -+ list->qlen--; -+ next = skb->next; -+ prev = skb->prev; -+ skb->next = skb->prev = NULL; -+ next->prev = prev; -+ prev->next = next; -+} -+ -+/** -+ * __skb_dequeue - remove from the head of the queue -+ * @list: list to dequeue from -+ * -+ * Remove the head of the list. This function does not take any locks -+ * so must be used with appropriate locks held only. The head item is -+ * returned or %NULL if the list is empty. -+ */ -+struct sk_buff *skb_dequeue(struct sk_buff_head *list); -+static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list) -+{ -+ struct sk_buff *skb = skb_peek(list); -+ if (skb) -+ __skb_unlink(skb, list); -+ return skb; -+} -+ -+/** -+ * __skb_dequeue_tail - remove from the tail of the queue -+ * @list: list to dequeue from -+ * -+ * Remove the tail of the list. This function does not take any locks -+ * so must be used with appropriate locks held only. The tail item is -+ * returned or %NULL if the list is empty. -+ */ -+struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list); -+static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list) -+{ -+ struct sk_buff *skb = skb_peek_tail(list); -+ if (skb) -+ __skb_unlink(skb, list); -+ return skb; -+} -+ -+ -+static inline bool skb_is_nonlinear(const struct sk_buff *skb) -+{ -+ return skb->data_len; -+} -+ -+static inline unsigned int skb_headlen(const struct sk_buff *skb) -+{ -+ return skb->len - skb->data_len; -+} -+ -+static inline int skb_pagelen(const struct sk_buff *skb) -+{ -+ int i, len = 0; -+ -+ for (i = (int)skb_shinfo(skb)->nr_frags - 1; i >= 0; i--) -+ len += skb_frag_size(&skb_shinfo(skb)->frags[i]); -+ return len + skb_headlen(skb); -+} -+ -+/** -+ * __skb_fill_page_desc - initialise a paged fragment in an skb -+ * @skb: buffer containing fragment to be initialised -+ * @i: paged fragment index to initialise -+ * @page: the page to use for this fragment -+ * @off: the offset to the data with @page -+ * @size: the length of the data -+ * -+ * Initialises the @i'th fragment of @skb to point to &size bytes at -+ * offset @off within @page. -+ * -+ * Does not take any additional reference on the fragment. -+ */ -+static inline void __skb_fill_page_desc(struct sk_buff *skb, int i, -+ struct page *page, int off, int size) -+{ -+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; -+ -+ /* -+ * Propagate page->pfmemalloc to the skb if we can. The problem is -+ * that not all callers have unique ownership of the page. If -+ * pfmemalloc is set, we check the mapping as a mapping implies -+ * page->index is set (index and pfmemalloc share space). -+ * If it's a valid mapping, we cannot use page->pfmemalloc but we -+ * do not lose pfmemalloc information as the pages would not be -+ * allocated using __GFP_MEMALLOC. -+ */ -+ frag->page.p = page; -+ frag->page_offset = off; -+ skb_frag_size_set(frag, size); -+ -+ page = compound_head(page); -+ if (page->pfmemalloc && !page->mapping) -+ skb->pfmemalloc = true; -+} -+ -+/** -+ * skb_fill_page_desc - initialise a paged fragment in an skb -+ * @skb: buffer containing fragment to be initialised -+ * @i: paged fragment index to initialise -+ * @page: the page to use for this fragment -+ * @off: the offset to the data with @page -+ * @size: the length of the data -+ * -+ * As per __skb_fill_page_desc() -- initialises the @i'th fragment of -+ * @skb to point to @size bytes at offset @off within @page. In -+ * addition updates @skb such that @i is the last fragment. -+ * -+ * Does not take any additional reference on the fragment. -+ */ -+static inline void skb_fill_page_desc(struct sk_buff *skb, int i, -+ struct page *page, int off, int size) -+{ -+ __skb_fill_page_desc(skb, i, page, off, size); -+ skb_shinfo(skb)->nr_frags = i + 1; -+} -+ -+void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, -+ int size, unsigned int truesize); -+ -+void skb_coalesce_rx_frag(struct sk_buff *skb, int i, int size, -+ unsigned int truesize); -+ -+#define SKB_PAGE_ASSERT(skb) BUG_ON(skb_shinfo(skb)->nr_frags) -+#define SKB_FRAG_ASSERT(skb) BUG_ON(skb_has_frag_list(skb)) -+#define SKB_LINEAR_ASSERT(skb) BUG_ON(skb_is_nonlinear(skb)) -+ -+#ifdef NET_SKBUFF_DATA_USES_OFFSET -+static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) -+{ -+ return skb->head + skb->tail; -+} -+ -+static inline void skb_reset_tail_pointer(struct sk_buff *skb) -+{ -+ skb->tail = skb->data - skb->head; -+} -+ -+static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) -+{ -+ skb_reset_tail_pointer(skb); -+ skb->tail += offset; -+} -+ -+#else /* NET_SKBUFF_DATA_USES_OFFSET */ -+static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) -+{ -+ return skb->tail; -+} -+ -+static inline void skb_reset_tail_pointer(struct sk_buff *skb) -+{ -+ skb->tail = skb->data; -+} -+ -+static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) -+{ -+ skb->tail = skb->data + offset; -+} -+ -+#endif /* NET_SKBUFF_DATA_USES_OFFSET */ -+ -+/* -+ * Add data to an sk_buff -+ */ -+unsigned char *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len); -+unsigned char *skb_put(struct sk_buff *skb, unsigned int len); -+static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len) -+{ -+ unsigned char *tmp = skb_tail_pointer(skb); -+ SKB_LINEAR_ASSERT(skb); -+ skb->tail += len; -+ skb->len += len; -+ return tmp; -+} -+ -+unsigned char *skb_push(struct sk_buff *skb, unsigned int len); -+static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len) -+{ -+ skb->data -= len; -+ skb->len += len; -+ return skb->data; -+} -+ -+unsigned char *skb_pull(struct sk_buff *skb, unsigned int len); -+static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len) -+{ -+ skb->len -= len; -+ BUG_ON(skb->len < skb->data_len); -+ return skb->data += len; -+} -+ -+static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int len) -+{ -+ return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len); -+} -+ -+unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta); -+ -+static inline unsigned char *__pskb_pull(struct sk_buff *skb, unsigned int len) -+{ -+ if (len > skb_headlen(skb) && -+ !__pskb_pull_tail(skb, len - skb_headlen(skb))) -+ return NULL; -+ skb->len -= len; -+ return skb->data += len; -+} -+ -+static inline unsigned char *pskb_pull(struct sk_buff *skb, unsigned int len) -+{ -+ return unlikely(len > skb->len) ? NULL : __pskb_pull(skb, len); -+} -+ -+static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len) -+{ -+ if (likely(len <= skb_headlen(skb))) -+ return 1; -+ if (unlikely(len > skb->len)) -+ return 0; -+ return __pskb_pull_tail(skb, len - skb_headlen(skb)) != NULL; -+} -+ -+/** -+ * skb_headroom - bytes at buffer head -+ * @skb: buffer to check -+ * -+ * Return the number of bytes of free space at the head of an &sk_buff. -+ */ -+static inline unsigned int skb_headroom(const struct sk_buff *skb) -+{ -+ return skb->data - skb->head; -+} -+ -+/** -+ * skb_tailroom - bytes at buffer end -+ * @skb: buffer to check -+ * -+ * Return the number of bytes of free space at the tail of an sk_buff -+ */ -+static inline int skb_tailroom(const struct sk_buff *skb) -+{ -+ return skb_is_nonlinear(skb) ? 0 : skb->end - skb->tail; -+} -+ -+/** -+ * skb_availroom - bytes at buffer end -+ * @skb: buffer to check -+ * -+ * Return the number of bytes of free space at the tail of an sk_buff -+ * allocated by sk_stream_alloc() -+ */ -+static inline int skb_availroom(const struct sk_buff *skb) -+{ -+ if (skb_is_nonlinear(skb)) -+ return 0; -+ -+ return skb->end - skb->tail - skb->reserved_tailroom; -+} -+ -+/** -+ * skb_reserve - adjust headroom -+ * @skb: buffer to alter -+ * @len: bytes to move -+ * -+ * Increase the headroom of an empty &sk_buff by reducing the tail -+ * room. This is only allowed for an empty buffer. -+ */ -+static inline void skb_reserve(struct sk_buff *skb, int len) -+{ -+ skb->data += len; -+ skb->tail += len; -+} -+ -+#define ENCAP_TYPE_ETHER 0 -+#define ENCAP_TYPE_IPPROTO 1 -+ -+static inline void skb_set_inner_protocol(struct sk_buff *skb, -+ __be16 protocol) -+{ -+ skb->inner_protocol = protocol; -+ skb->inner_protocol_type = ENCAP_TYPE_ETHER; -+} -+ -+static inline void skb_set_inner_ipproto(struct sk_buff *skb, -+ __u8 ipproto) -+{ -+ skb->inner_ipproto = ipproto; -+ skb->inner_protocol_type = ENCAP_TYPE_IPPROTO; -+} -+ -+static inline void skb_reset_inner_headers(struct sk_buff *skb) -+{ -+ skb->inner_mac_header = skb->mac_header; -+ skb->inner_network_header = skb->network_header; -+ skb->inner_transport_header = skb->transport_header; -+} -+ -+static inline void skb_reset_mac_len(struct sk_buff *skb) -+{ -+ skb->mac_len = skb->network_header - skb->mac_header; -+} -+ -+static inline unsigned char *skb_inner_transport_header(const struct sk_buff -+ *skb) -+{ -+ return skb->head + skb->inner_transport_header; -+} -+ -+static inline void skb_reset_inner_transport_header(struct sk_buff *skb) -+{ -+ skb->inner_transport_header = skb->data - skb->head; -+} -+ -+static inline void skb_set_inner_transport_header(struct sk_buff *skb, -+ const int offset) -+{ -+ skb_reset_inner_transport_header(skb); -+ skb->inner_transport_header += offset; -+} -+ -+static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb) -+{ -+ return skb->head + skb->inner_network_header; -+} -+ -+static inline void skb_reset_inner_network_header(struct sk_buff *skb) -+{ -+ skb->inner_network_header = skb->data - skb->head; -+} -+ -+static inline void skb_set_inner_network_header(struct sk_buff *skb, -+ const int offset) -+{ -+ skb_reset_inner_network_header(skb); -+ skb->inner_network_header += offset; -+} -+ -+static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb) -+{ -+ return skb->head + skb->inner_mac_header; -+} -+ -+static inline void skb_reset_inner_mac_header(struct sk_buff *skb) -+{ -+ skb->inner_mac_header = skb->data - skb->head; -+} -+ -+static inline void skb_set_inner_mac_header(struct sk_buff *skb, -+ const int offset) -+{ -+ skb_reset_inner_mac_header(skb); -+ skb->inner_mac_header += offset; -+} -+static inline bool skb_transport_header_was_set(const struct sk_buff *skb) -+{ -+ return skb->transport_header != (typeof(skb->transport_header))~0U; -+} -+ -+static inline unsigned char *skb_transport_header(const struct sk_buff *skb) -+{ -+ return skb->head + skb->transport_header; -+} -+ -+static inline void skb_reset_transport_header(struct sk_buff *skb) -+{ -+ skb->transport_header = skb->data - skb->head; -+} -+ -+static inline void skb_set_transport_header(struct sk_buff *skb, -+ const int offset) -+{ -+ skb_reset_transport_header(skb); -+ skb->transport_header += offset; -+} -+ -+static inline unsigned char *skb_network_header(const struct sk_buff *skb) -+{ -+ return skb->head + skb->network_header; -+} -+ -+static inline void skb_reset_network_header(struct sk_buff *skb) -+{ -+ skb->network_header = skb->data - skb->head; -+} -+ -+static inline void skb_set_network_header(struct sk_buff *skb, const int offset) -+{ -+ skb_reset_network_header(skb); -+ skb->network_header += offset; -+} -+ -+static inline unsigned char *skb_mac_header(const struct sk_buff *skb) -+{ -+ return skb->head + skb->mac_header; -+} -+ -+static inline int skb_mac_header_was_set(const struct sk_buff *skb) -+{ -+ return skb->mac_header != (typeof(skb->mac_header))~0U; -+} -+ -+static inline void skb_reset_mac_header(struct sk_buff *skb) -+{ -+ skb->mac_header = skb->data - skb->head; -+} -+ -+static inline void skb_set_mac_header(struct sk_buff *skb, const int offset) -+{ -+ skb_reset_mac_header(skb); -+ skb->mac_header += offset; -+} -+ -+static inline void skb_pop_mac_header(struct sk_buff *skb) -+{ -+ skb->mac_header = skb->network_header; -+} -+ -+static inline void skb_probe_transport_header(struct sk_buff *skb, -+ const int offset_hint) -+{ -+ struct flow_keys keys; -+ -+ if (skb_transport_header_was_set(skb)) -+ return; -+ else if (skb_flow_dissect(skb, &keys)) -+ skb_set_transport_header(skb, keys.thoff); -+ else -+ skb_set_transport_header(skb, offset_hint); -+} -+ -+static inline void skb_mac_header_rebuild(struct sk_buff *skb) -+{ -+ if (skb_mac_header_was_set(skb)) { -+ const unsigned char *old_mac = skb_mac_header(skb); -+ -+ skb_set_mac_header(skb, -skb->mac_len); -+ memmove(skb_mac_header(skb), old_mac, skb->mac_len); -+ } -+} -+ -+static inline int skb_checksum_start_offset(const struct sk_buff *skb) -+{ -+ return skb->csum_start - skb_headroom(skb); -+} -+ -+static inline int skb_transport_offset(const struct sk_buff *skb) -+{ -+ return skb_transport_header(skb) - skb->data; -+} -+ -+static inline u32 skb_network_header_len(const struct sk_buff *skb) -+{ -+ return skb->transport_header - skb->network_header; -+} -+ -+static inline u32 skb_inner_network_header_len(const struct sk_buff *skb) -+{ -+ return skb->inner_transport_header - skb->inner_network_header; -+} -+ -+static inline int skb_network_offset(const struct sk_buff *skb) -+{ -+ return skb_network_header(skb) - skb->data; -+} -+ -+static inline int skb_inner_network_offset(const struct sk_buff *skb) -+{ -+ return skb_inner_network_header(skb) - skb->data; -+} -+ -+static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len) -+{ -+ return pskb_may_pull(skb, skb_network_offset(skb) + len); -+} -+ -+/* -+ * CPUs often take a performance hit when accessing unaligned memory -+ * locations. The actual performance hit varies, it can be small if the -+ * hardware handles it or large if we have to take an exception and fix it -+ * in software. -+ * -+ * Since an ethernet header is 14 bytes network drivers often end up with -+ * the IP header at an unaligned offset. The IP header can be aligned by -+ * shifting the start of the packet by 2 bytes. Drivers should do this -+ * with: -+ * -+ * skb_reserve(skb, NET_IP_ALIGN); -+ * -+ * The downside to this alignment of the IP header is that the DMA is now -+ * unaligned. On some architectures the cost of an unaligned DMA is high -+ * and this cost outweighs the gains made by aligning the IP header. -+ * -+ * Since this trade off varies between architectures, we allow NET_IP_ALIGN -+ * to be overridden. -+ */ -+#ifndef NET_IP_ALIGN -+#define NET_IP_ALIGN 2 -+#endif -+ -+/* -+ * The networking layer reserves some headroom in skb data (via -+ * dev_alloc_skb). This is used to avoid having to reallocate skb data when -+ * the header has to grow. In the default case, if the header has to grow -+ * 32 bytes or less we avoid the reallocation. -+ * -+ * Unfortunately this headroom changes the DMA alignment of the resulting -+ * network packet. As for NET_IP_ALIGN, this unaligned DMA is expensive -+ * on some architectures. An architecture can override this value, -+ * perhaps setting it to a cacheline in size (since that will maintain -+ * cacheline alignment of the DMA). It must be a power of 2. -+ * -+ * Various parts of the networking layer expect at least 32 bytes of -+ * headroom, you should not reduce this. -+ * -+ * Using max(32, L1_CACHE_BYTES) makes sense (especially with RPS) -+ * to reduce average number of cache lines per packet. -+ * get_rps_cpus() for example only access one 64 bytes aligned block : -+ * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) -+ */ -+#ifndef NET_SKB_PAD -+#define NET_SKB_PAD max(32, L1_CACHE_BYTES) -+#endif -+ -+int ___pskb_trim(struct sk_buff *skb, unsigned int len); -+ -+static inline void __skb_trim(struct sk_buff *skb, unsigned int len) -+{ -+ if (unlikely(skb_is_nonlinear(skb))) { -+ WARN_ON(1); -+ return; -+ } -+ skb->len = len; -+ skb_set_tail_pointer(skb, len); -+} -+ -+void skb_trim(struct sk_buff *skb, unsigned int len); -+ -+static inline int __pskb_trim(struct sk_buff *skb, unsigned int len) -+{ -+ if (skb->data_len) -+ return ___pskb_trim(skb, len); -+ __skb_trim(skb, len); -+ return 0; -+} -+ -+static inline int pskb_trim(struct sk_buff *skb, unsigned int len) -+{ -+ return (len < skb->len) ? __pskb_trim(skb, len) : 0; -+} -+ -+/** -+ * pskb_trim_unique - remove end from a paged unique (not cloned) buffer -+ * @skb: buffer to alter -+ * @len: new length -+ * -+ * This is identical to pskb_trim except that the caller knows that -+ * the skb is not cloned so we should never get an error due to out- -+ * of-memory. -+ */ -+static inline void pskb_trim_unique(struct sk_buff *skb, unsigned int len) -+{ -+ int err = pskb_trim(skb, len); -+ BUG_ON(err); -+} -+ -+/** -+ * skb_orphan - orphan a buffer -+ * @skb: buffer to orphan -+ * -+ * If a buffer currently has an owner then we call the owner's -+ * destructor function and make the @skb unowned. The buffer continues -+ * to exist but is no longer charged to its former owner. -+ */ -+static inline void skb_orphan(struct sk_buff *skb) -+{ -+ if (skb->destructor) { -+ skb->destructor(skb); -+ skb->destructor = NULL; -+ skb->sk = NULL; -+ } else { -+ BUG_ON(skb->sk); -+ } -+} -+ -+/** -+ * skb_orphan_frags - orphan the frags contained in a buffer -+ * @skb: buffer to orphan frags from -+ * @gfp_mask: allocation mask for replacement pages -+ * -+ * For each frag in the SKB which needs a destructor (i.e. has an -+ * owner) create a copy of that frag and release the original -+ * page by calling the destructor. -+ */ -+static inline int skb_orphan_frags(struct sk_buff *skb, gfp_t gfp_mask) -+{ -+ if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY))) -+ return 0; -+ return skb_copy_ubufs(skb, gfp_mask); -+} -+ -+/** -+ * __skb_queue_purge - empty a list -+ * @list: list to empty -+ * -+ * Delete all buffers on an &sk_buff list. Each buffer is removed from -+ * the list and one reference dropped. This function does not take the -+ * list lock and the caller must hold the relevant locks to use it. -+ */ -+void skb_queue_purge(struct sk_buff_head *list); -+static inline void __skb_queue_purge(struct sk_buff_head *list) -+{ -+ struct sk_buff *skb; -+ while ((skb = __skb_dequeue(list)) != NULL) -+ kfree_skb(skb); -+} -+ -+#define NETDEV_FRAG_PAGE_MAX_ORDER get_order(32768) -+#define NETDEV_FRAG_PAGE_MAX_SIZE (PAGE_SIZE << NETDEV_FRAG_PAGE_MAX_ORDER) -+#define NETDEV_PAGECNT_MAX_BIAS NETDEV_FRAG_PAGE_MAX_SIZE -+ -+void *netdev_alloc_frag(unsigned int fragsz); -+ -+struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int length, -+ gfp_t gfp_mask); -+ -+/** -+ * netdev_alloc_skb - allocate an skbuff for rx on a specific device -+ * @dev: network device to receive on -+ * @length: length to allocate -+ * -+ * Allocate a new &sk_buff and assign it a usage count of one. The -+ * buffer has unspecified headroom built in. Users should allocate -+ * the headroom they think they need without accounting for the -+ * built in space. The built in space is used for optimisations. -+ * -+ * %NULL is returned if there is no free memory. Although this function -+ * allocates memory it can be called from an interrupt. -+ */ -+static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev, -+ unsigned int length) -+{ -+ return __netdev_alloc_skb(dev, length, GFP_ATOMIC); -+} -+ -+/* legacy helper around __netdev_alloc_skb() */ -+static inline struct sk_buff *__dev_alloc_skb(unsigned int length, -+ gfp_t gfp_mask) -+{ -+ return __netdev_alloc_skb(NULL, length, gfp_mask); -+} -+ -+/* legacy helper around netdev_alloc_skb() */ -+static inline struct sk_buff *dev_alloc_skb(unsigned int length) -+{ -+ return netdev_alloc_skb(NULL, length); -+} -+ -+ -+static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, -+ unsigned int length, gfp_t gfp) -+{ -+ struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); -+ -+ if (NET_IP_ALIGN && skb) -+ skb_reserve(skb, NET_IP_ALIGN); -+ return skb; -+} -+ -+static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, -+ unsigned int length) -+{ -+ return __netdev_alloc_skb_ip_align(dev, length, GFP_ATOMIC); -+} -+ -+/** -+ * __skb_alloc_pages - allocate pages for ps-rx on a skb and preserve pfmemalloc data -+ * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX -+ * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used -+ * @order: size of the allocation -+ * -+ * Allocate a new page. -+ * -+ * %NULL is returned if there is no free memory. -+*/ -+static inline struct page *__skb_alloc_pages(gfp_t gfp_mask, -+ struct sk_buff *skb, -+ unsigned int order) -+{ -+ struct page *page; -+ -+ gfp_mask |= __GFP_COLD; -+ -+ if (!(gfp_mask & __GFP_NOMEMALLOC)) -+ gfp_mask |= __GFP_MEMALLOC; -+ -+ page = alloc_pages_node(NUMA_NO_NODE, gfp_mask, order); -+ if (skb && page && page->pfmemalloc) -+ skb->pfmemalloc = true; -+ -+ return page; -+} -+ -+/** -+ * __skb_alloc_page - allocate a page for ps-rx for a given skb and preserve pfmemalloc data -+ * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX -+ * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used -+ * -+ * Allocate a new page. -+ * -+ * %NULL is returned if there is no free memory. -+ */ -+static inline struct page *__skb_alloc_page(gfp_t gfp_mask, -+ struct sk_buff *skb) -+{ -+ return __skb_alloc_pages(gfp_mask, skb, 0); -+} -+ -+/** -+ * skb_propagate_pfmemalloc - Propagate pfmemalloc if skb is allocated after RX page -+ * @page: The page that was allocated from skb_alloc_page -+ * @skb: The skb that may need pfmemalloc set -+ */ -+static inline void skb_propagate_pfmemalloc(struct page *page, -+ struct sk_buff *skb) -+{ -+ if (page && page->pfmemalloc) -+ skb->pfmemalloc = true; -+} -+ -+/** -+ * skb_frag_page - retrieve the page referred to by a paged fragment -+ * @frag: the paged fragment -+ * -+ * Returns the &struct page associated with @frag. -+ */ -+static inline struct page *skb_frag_page(const skb_frag_t *frag) -+{ -+ return frag->page.p; -+} -+ -+/** -+ * __skb_frag_ref - take an addition reference on a paged fragment. -+ * @frag: the paged fragment -+ * -+ * Takes an additional reference on the paged fragment @frag. -+ */ -+static inline void __skb_frag_ref(skb_frag_t *frag) -+{ -+ get_page(skb_frag_page(frag)); -+} -+ -+/** -+ * skb_frag_ref - take an addition reference on a paged fragment of an skb. -+ * @skb: the buffer -+ * @f: the fragment offset. -+ * -+ * Takes an additional reference on the @f'th paged fragment of @skb. -+ */ -+static inline void skb_frag_ref(struct sk_buff *skb, int f) -+{ -+ __skb_frag_ref(&skb_shinfo(skb)->frags[f]); -+} -+ -+/** -+ * __skb_frag_unref - release a reference on a paged fragment. -+ * @frag: the paged fragment -+ * -+ * Releases a reference on the paged fragment @frag. -+ */ -+static inline void __skb_frag_unref(skb_frag_t *frag) -+{ -+ put_page(skb_frag_page(frag)); -+} -+ -+/** -+ * skb_frag_unref - release a reference on a paged fragment of an skb. -+ * @skb: the buffer -+ * @f: the fragment offset -+ * -+ * Releases a reference on the @f'th paged fragment of @skb. -+ */ -+static inline void skb_frag_unref(struct sk_buff *skb, int f) -+{ -+ __skb_frag_unref(&skb_shinfo(skb)->frags[f]); -+} -+ -+/** -+ * skb_frag_address - gets the address of the data contained in a paged fragment -+ * @frag: the paged fragment buffer -+ * -+ * Returns the address of the data within @frag. The page must already -+ * be mapped. -+ */ -+static inline void *skb_frag_address(const skb_frag_t *frag) -+{ -+ return page_address(skb_frag_page(frag)) + frag->page_offset; -+} -+ -+/** -+ * skb_frag_address_safe - gets the address of the data contained in a paged fragment -+ * @frag: the paged fragment buffer -+ * -+ * Returns the address of the data within @frag. Checks that the page -+ * is mapped and returns %NULL otherwise. -+ */ -+static inline void *skb_frag_address_safe(const skb_frag_t *frag) -+{ -+ void *ptr = page_address(skb_frag_page(frag)); -+ if (unlikely(!ptr)) -+ return NULL; -+ -+ return ptr + frag->page_offset; -+} -+ -+/** -+ * __skb_frag_set_page - sets the page contained in a paged fragment -+ * @frag: the paged fragment -+ * @page: the page to set -+ * -+ * Sets the fragment @frag to contain @page. -+ */ -+static inline void __skb_frag_set_page(skb_frag_t *frag, struct page *page) -+{ -+ frag->page.p = page; -+} -+ -+/** -+ * skb_frag_set_page - sets the page contained in a paged fragment of an skb -+ * @skb: the buffer -+ * @f: the fragment offset -+ * @page: the page to set -+ * -+ * Sets the @f'th fragment of @skb to contain @page. -+ */ -+static inline void skb_frag_set_page(struct sk_buff *skb, int f, -+ struct page *page) -+{ -+ __skb_frag_set_page(&skb_shinfo(skb)->frags[f], page); -+} -+ -+bool skb_page_frag_refill(unsigned int sz, struct page_frag *pfrag, gfp_t prio); -+ -+/** -+ * skb_frag_dma_map - maps a paged fragment via the DMA API -+ * @dev: the device to map the fragment to -+ * @frag: the paged fragment to map -+ * @offset: the offset within the fragment (starting at the -+ * fragment's own offset) -+ * @size: the number of bytes to map -+ * @dir: the direction of the mapping (%PCI_DMA_*) -+ * -+ * Maps the page associated with @frag to @device. -+ */ -+static inline dma_addr_t skb_frag_dma_map(struct device *dev, -+ const skb_frag_t *frag, -+ size_t offset, size_t size, -+ enum dma_data_direction dir) -+{ -+ return dma_map_page(dev, skb_frag_page(frag), -+ frag->page_offset + offset, size, dir); -+} -+ -+static inline struct sk_buff *pskb_copy(struct sk_buff *skb, -+ gfp_t gfp_mask) -+{ -+ return __pskb_copy(skb, skb_headroom(skb), gfp_mask); -+} -+ -+ -+static inline struct sk_buff *pskb_copy_for_clone(struct sk_buff *skb, -+ gfp_t gfp_mask) -+{ -+ return __pskb_copy_fclone(skb, skb_headroom(skb), gfp_mask, true); -+} -+ -+ -+/** -+ * skb_clone_writable - is the header of a clone writable -+ * @skb: buffer to check -+ * @len: length up to which to write -+ * -+ * Returns true if modifying the header part of the cloned buffer -+ * does not requires the data to be copied. -+ */ -+static inline int skb_clone_writable(const struct sk_buff *skb, unsigned int len) -+{ -+ return !skb_header_cloned(skb) && -+ skb_headroom(skb) + len <= skb->hdr_len; -+} -+ -+static inline int __skb_cow(struct sk_buff *skb, unsigned int headroom, -+ int cloned) -+{ -+ int delta = 0; -+ -+ if (headroom > skb_headroom(skb)) -+ delta = headroom - skb_headroom(skb); -+ -+ if (delta || cloned) -+ return pskb_expand_head(skb, ALIGN(delta, NET_SKB_PAD), 0, -+ GFP_ATOMIC); -+ return 0; -+} -+ -+/** -+ * skb_cow - copy header of skb when it is required -+ * @skb: buffer to cow -+ * @headroom: needed headroom -+ * -+ * If the skb passed lacks sufficient headroom or its data part -+ * is shared, data is reallocated. If reallocation fails, an error -+ * is returned and original skb is not changed. -+ * -+ * The result is skb with writable area skb->head...skb->tail -+ * and at least @headroom of space at head. -+ */ -+static inline int skb_cow(struct sk_buff *skb, unsigned int headroom) -+{ -+ return __skb_cow(skb, headroom, skb_cloned(skb)); -+} -+ -+/** -+ * skb_cow_head - skb_cow but only making the head writable -+ * @skb: buffer to cow -+ * @headroom: needed headroom -+ * -+ * This function is identical to skb_cow except that we replace the -+ * skb_cloned check by skb_header_cloned. It should be used when -+ * you only need to push on some header and do not need to modify -+ * the data. -+ */ -+static inline int skb_cow_head(struct sk_buff *skb, unsigned int headroom) -+{ -+ return __skb_cow(skb, headroom, skb_header_cloned(skb)); -+} -+ -+/** -+ * skb_padto - pad an skbuff up to a minimal size -+ * @skb: buffer to pad -+ * @len: minimal length -+ * -+ * Pads up a buffer to ensure the trailing bytes exist and are -+ * blanked. If the buffer already contains sufficient data it -+ * is untouched. Otherwise it is extended. Returns zero on -+ * success. The skb is freed on error. -+ */ -+ -+static inline int skb_padto(struct sk_buff *skb, unsigned int len) -+{ -+ unsigned int size = skb->len; -+ if (likely(size >= len)) -+ return 0; -+ return skb_pad(skb, len - size); -+} -+ -+static inline int skb_add_data(struct sk_buff *skb, -+ char __user *from, int copy) -+{ -+ const int off = skb->len; -+ -+ if (skb->ip_summed == CHECKSUM_NONE) { -+ int err = 0; -+ __wsum csum = csum_and_copy_from_user(from, skb_put(skb, copy), -+ copy, 0, &err); -+ if (!err) { -+ skb->csum = csum_block_add(skb->csum, csum, off); -+ return 0; -+ } -+ } else if (!copy_from_user(skb_put(skb, copy), from, copy)) -+ return 0; -+ -+ __skb_trim(skb, off); -+ return -EFAULT; -+} -+ -+static inline bool skb_can_coalesce(struct sk_buff *skb, int i, -+ const struct page *page, int off) -+{ -+ if (i) { -+ const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i - 1]; -+ -+ return page == skb_frag_page(frag) && -+ off == frag->page_offset + skb_frag_size(frag); -+ } -+ return false; -+} -+ -+static inline int __skb_linearize(struct sk_buff *skb) -+{ -+ return __pskb_pull_tail(skb, skb->data_len) ? 0 : -ENOMEM; -+} -+ -+/** -+ * skb_linearize - convert paged skb to linear one -+ * @skb: buffer to linarize -+ * -+ * If there is no free memory -ENOMEM is returned, otherwise zero -+ * is returned and the old skb data released. -+ */ -+static inline int skb_linearize(struct sk_buff *skb) -+{ -+ return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0; -+} -+ -+/** -+ * skb_has_shared_frag - can any frag be overwritten -+ * @skb: buffer to test -+ * -+ * Return true if the skb has at least one frag that might be modified -+ * by an external entity (as in vmsplice()/sendfile()) -+ */ -+static inline bool skb_has_shared_frag(const struct sk_buff *skb) -+{ -+ return skb_is_nonlinear(skb) && -+ skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; -+} -+ -+/** -+ * skb_linearize_cow - make sure skb is linear and writable -+ * @skb: buffer to process -+ * -+ * If there is no free memory -ENOMEM is returned, otherwise zero -+ * is returned and the old skb data released. -+ */ -+static inline int skb_linearize_cow(struct sk_buff *skb) -+{ -+ return skb_is_nonlinear(skb) || skb_cloned(skb) ? -+ __skb_linearize(skb) : 0; -+} -+ -+/** -+ * skb_postpull_rcsum - update checksum for received skb after pull -+ * @skb: buffer to update -+ * @start: start of data before pull -+ * @len: length of data pulled -+ * -+ * After doing a pull on a received packet, you need to call this to -+ * update the CHECKSUM_COMPLETE checksum, or set ip_summed to -+ * CHECKSUM_NONE so that it can be recomputed from scratch. -+ */ -+ -+static inline void skb_postpull_rcsum(struct sk_buff *skb, -+ const void *start, unsigned int len) -+{ -+ if (skb->ip_summed == CHECKSUM_COMPLETE) -+ skb->csum = csum_sub(skb->csum, csum_partial(start, len, 0)); -+} -+ -+unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len); -+ -+/** -+ * pskb_trim_rcsum - trim received skb and update checksum -+ * @skb: buffer to trim -+ * @len: new length -+ * -+ * This is exactly the same as pskb_trim except that it ensures the -+ * checksum of received packets are still valid after the operation. -+ */ -+ -+static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len) -+{ -+ if (likely(len >= skb->len)) -+ return 0; -+ if (skb->ip_summed == CHECKSUM_COMPLETE) -+ skb->ip_summed = CHECKSUM_NONE; -+ return __pskb_trim(skb, len); -+} -+ -+#define skb_queue_walk(queue, skb) \ -+ for (skb = (queue)->next; \ -+ skb != (struct sk_buff *)(queue); \ -+ skb = skb->next) -+ -+#define skb_queue_walk_safe(queue, skb, tmp) \ -+ for (skb = (queue)->next, tmp = skb->next; \ -+ skb != (struct sk_buff *)(queue); \ -+ skb = tmp, tmp = skb->next) -+ -+#define skb_queue_walk_from(queue, skb) \ -+ for (; skb != (struct sk_buff *)(queue); \ -+ skb = skb->next) -+ -+#define skb_queue_walk_from_safe(queue, skb, tmp) \ -+ for (tmp = skb->next; \ -+ skb != (struct sk_buff *)(queue); \ -+ skb = tmp, tmp = skb->next) -+ -+#define skb_queue_reverse_walk(queue, skb) \ -+ for (skb = (queue)->prev; \ -+ skb != (struct sk_buff *)(queue); \ -+ skb = skb->prev) -+ -+#define skb_queue_reverse_walk_safe(queue, skb, tmp) \ -+ for (skb = (queue)->prev, tmp = skb->prev; \ -+ skb != (struct sk_buff *)(queue); \ -+ skb = tmp, tmp = skb->prev) -+ -+#define skb_queue_reverse_walk_from_safe(queue, skb, tmp) \ -+ for (tmp = skb->prev; \ -+ skb != (struct sk_buff *)(queue); \ -+ skb = tmp, tmp = skb->prev) -+ -+static inline bool skb_has_frag_list(const struct sk_buff *skb) -+{ -+ return skb_shinfo(skb)->frag_list != NULL; -+} -+ -+static inline void skb_frag_list_init(struct sk_buff *skb) -+{ -+ skb_shinfo(skb)->frag_list = NULL; -+} -+ -+static inline void skb_frag_add_head(struct sk_buff *skb, struct sk_buff *frag) -+{ -+ frag->next = skb_shinfo(skb)->frag_list; -+ skb_shinfo(skb)->frag_list = frag; -+} -+ -+#define skb_walk_frags(skb, iter) \ -+ for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next) -+ -+struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, -+ int *peeked, int *off, int *err); -+struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, -+ int *err); -+unsigned int datagram_poll(struct file *file, struct socket *sock, -+ struct poll_table_struct *wait); -+int skb_copy_datagram_iovec(const struct sk_buff *from, int offset, -+ struct iovec *to, int size); -+int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, int hlen, -+ struct iovec *iov); -+int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, -+ const struct iovec *from, int from_offset, -+ int len); -+int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *frm, -+ int offset, size_t count); -+int skb_copy_datagram_const_iovec(const struct sk_buff *from, int offset, -+ const struct iovec *to, int to_offset, -+ int size); -+void skb_free_datagram(struct sock *sk, struct sk_buff *skb); -+void skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb); -+int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags); -+int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len); -+int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len); -+__wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, -+ int len, __wsum csum); -+int skb_splice_bits(struct sk_buff *skb, unsigned int offset, -+ struct pipe_inode_info *pipe, unsigned int len, -+ unsigned int flags); -+void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); -+unsigned int skb_zerocopy_headlen(const struct sk_buff *from); -+int skb_zerocopy(struct sk_buff *to, struct sk_buff *from, -+ int len, int hlen); -+void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); -+int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); -+void skb_scrub_packet(struct sk_buff *skb, bool xnet); -+unsigned int skb_gso_transport_seglen(const struct sk_buff *skb); -+struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features); -+struct sk_buff *skb_vlan_untag(struct sk_buff *skb); -+ -+struct skb_checksum_ops { -+ __wsum (*update)(const void *mem, int len, __wsum wsum); -+ __wsum (*combine)(__wsum csum, __wsum csum2, int offset, int len); -+}; -+ -+__wsum __skb_checksum(const struct sk_buff *skb, int offset, int len, -+ __wsum csum, const struct skb_checksum_ops *ops); -+__wsum skb_checksum(const struct sk_buff *skb, int offset, int len, -+ __wsum csum); -+ -+static inline void *__skb_header_pointer(const struct sk_buff *skb, int offset, -+ int len, void *data, int hlen, void *buffer) -+{ -+ if (hlen - offset >= len) -+ return data + offset; -+ -+ if (!skb || -+ skb_copy_bits(skb, offset, buffer, len) < 0) -+ return NULL; -+ -+ return buffer; -+} -+ -+static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, -+ int len, void *buffer) -+{ -+ return __skb_header_pointer(skb, offset, len, skb->data, -+ skb_headlen(skb), buffer); -+} -+ -+/** -+ * skb_needs_linearize - check if we need to linearize a given skb -+ * depending on the given device features. -+ * @skb: socket buffer to check -+ * @features: net device features -+ * -+ * Returns true if either: -+ * 1. skb has frag_list and the device doesn't support FRAGLIST, or -+ * 2. skb is fragmented and the device does not support SG. -+ */ -+static inline bool skb_needs_linearize(struct sk_buff *skb, -+ netdev_features_t features) -+{ -+ return skb_is_nonlinear(skb) && -+ ((skb_has_frag_list(skb) && !(features & NETIF_F_FRAGLIST)) || -+ (skb_shinfo(skb)->nr_frags && !(features & NETIF_F_SG))); -+} -+ -+static inline void skb_copy_from_linear_data(const struct sk_buff *skb, -+ void *to, -+ const unsigned int len) -+{ -+ memcpy(to, skb->data, len); -+} -+ -+static inline void skb_copy_from_linear_data_offset(const struct sk_buff *skb, -+ const int offset, void *to, -+ const unsigned int len) -+{ -+ memcpy(to, skb->data + offset, len); -+} -+ -+static inline void skb_copy_to_linear_data(struct sk_buff *skb, -+ const void *from, -+ const unsigned int len) -+{ -+ memcpy(skb->data, from, len); -+} -+ -+static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb, -+ const int offset, -+ const void *from, -+ const unsigned int len) -+{ -+ memcpy(skb->data + offset, from, len); -+} -+ -+void skb_init(void); -+ -+static inline ktime_t skb_get_ktime(const struct sk_buff *skb) -+{ -+ return skb->tstamp; -+} -+ -+/** -+ * skb_get_timestamp - get timestamp from a skb -+ * @skb: skb to get stamp from -+ * @stamp: pointer to struct timeval to store stamp in -+ * -+ * Timestamps are stored in the skb as offsets to a base timestamp. -+ * This function converts the offset back to a struct timeval and stores -+ * it in stamp. -+ */ -+static inline void skb_get_timestamp(const struct sk_buff *skb, -+ struct timeval *stamp) -+{ -+ *stamp = ktime_to_timeval(skb->tstamp); -+} -+ -+static inline void skb_get_timestampns(const struct sk_buff *skb, -+ struct timespec *stamp) -+{ -+ *stamp = ktime_to_timespec(skb->tstamp); -+} -+ -+static inline void __net_timestamp(struct sk_buff *skb) -+{ -+ skb->tstamp = ktime_get_real(); -+} -+ -+static inline ktime_t net_timedelta(ktime_t t) -+{ -+ return ktime_sub(ktime_get_real(), t); -+} -+ -+static inline ktime_t net_invalid_timestamp(void) -+{ -+ return ktime_set(0, 0); -+} -+ -+struct sk_buff *skb_clone_sk(struct sk_buff *skb); -+ -+#ifdef CONFIG_NETWORK_PHY_TIMESTAMPING -+ -+void skb_clone_tx_timestamp(struct sk_buff *skb); -+bool skb_defer_rx_timestamp(struct sk_buff *skb); -+ -+#else /* CONFIG_NETWORK_PHY_TIMESTAMPING */ -+ -+static inline void skb_clone_tx_timestamp(struct sk_buff *skb) -+{ -+} -+ -+static inline bool skb_defer_rx_timestamp(struct sk_buff *skb) -+{ -+ return false; -+} -+ -+#endif /* !CONFIG_NETWORK_PHY_TIMESTAMPING */ -+ -+/** -+ * skb_complete_tx_timestamp() - deliver cloned skb with tx timestamps -+ * -+ * PHY drivers may accept clones of transmitted packets for -+ * timestamping via their phy_driver.txtstamp method. These drivers -+ * must call this function to return the skb back to the stack, with -+ * or without a timestamp. -+ * -+ * @skb: clone of the the original outgoing packet -+ * @hwtstamps: hardware time stamps, may be NULL if not available -+ * -+ */ -+void skb_complete_tx_timestamp(struct sk_buff *skb, -+ struct skb_shared_hwtstamps *hwtstamps); -+ -+void __skb_tstamp_tx(struct sk_buff *orig_skb, -+ struct skb_shared_hwtstamps *hwtstamps, -+ struct sock *sk, int tstype); -+ -+/** -+ * skb_tstamp_tx - queue clone of skb with send time stamps -+ * @orig_skb: the original outgoing packet -+ * @hwtstamps: hardware time stamps, may be NULL if not available -+ * -+ * If the skb has a socket associated, then this function clones the -+ * skb (thus sharing the actual data and optional structures), stores -+ * the optional hardware time stamping information (if non NULL) or -+ * generates a software time stamp (otherwise), then queues the clone -+ * to the error queue of the socket. Errors are silently ignored. -+ */ -+void skb_tstamp_tx(struct sk_buff *orig_skb, -+ struct skb_shared_hwtstamps *hwtstamps); -+ -+static inline void sw_tx_timestamp(struct sk_buff *skb) -+{ -+ if (skb_shinfo(skb)->tx_flags & SKBTX_SW_TSTAMP && -+ !(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) -+ skb_tstamp_tx(skb, NULL); -+} -+ -+/** -+ * skb_tx_timestamp() - Driver hook for transmit timestamping -+ * -+ * Ethernet MAC Drivers should call this function in their hard_xmit() -+ * function immediately before giving the sk_buff to the MAC hardware. -+ * -+ * Specifically, one should make absolutely sure that this function is -+ * called before TX completion of this packet can trigger. Otherwise -+ * the packet could potentially already be freed. -+ * -+ * @skb: A socket buffer. -+ */ -+static inline void skb_tx_timestamp(struct sk_buff *skb) -+{ -+ skb_clone_tx_timestamp(skb); -+ sw_tx_timestamp(skb); -+} -+ -+/** -+ * skb_complete_wifi_ack - deliver skb with wifi status -+ * -+ * @skb: the original outgoing packet -+ * @acked: ack status -+ * -+ */ -+void skb_complete_wifi_ack(struct sk_buff *skb, bool acked); -+ -+__sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len); -+__sum16 __skb_checksum_complete(struct sk_buff *skb); -+ -+static inline int skb_csum_unnecessary(const struct sk_buff *skb) -+{ -+ return ((skb->ip_summed & CHECKSUM_UNNECESSARY) || skb->csum_valid); -+} -+ -+/** -+ * skb_checksum_complete - Calculate checksum of an entire packet -+ * @skb: packet to process -+ * -+ * This function calculates the checksum over the entire packet plus -+ * the value of skb->csum. The latter can be used to supply the -+ * checksum of a pseudo header as used by TCP/UDP. It returns the -+ * checksum. -+ * -+ * For protocols that contain complete checksums such as ICMP/TCP/UDP, -+ * this function can be used to verify that checksum on received -+ * packets. In that case the function should return zero if the -+ * checksum is correct. In particular, this function will return zero -+ * if skb->ip_summed is CHECKSUM_UNNECESSARY which indicates that the -+ * hardware has already verified the correctness of the checksum. -+ */ -+static inline __sum16 skb_checksum_complete(struct sk_buff *skb) -+{ -+ return skb_csum_unnecessary(skb) ? -+ 0 : __skb_checksum_complete(skb); -+} -+ -+static inline void __skb_decr_checksum_unnecessary(struct sk_buff *skb) -+{ -+ if (skb->ip_summed == CHECKSUM_UNNECESSARY) { -+ if (skb->csum_level == 0) -+ skb->ip_summed = CHECKSUM_NONE; -+ else -+ skb->csum_level--; -+ } -+} -+ -+static inline void __skb_incr_checksum_unnecessary(struct sk_buff *skb) -+{ -+ if (skb->ip_summed == CHECKSUM_UNNECESSARY) { -+ if (skb->csum_level < SKB_MAX_CSUM_LEVEL) -+ skb->csum_level++; -+ } else if (skb->ip_summed == CHECKSUM_NONE) { -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+ skb->csum_level = 0; -+ } -+} -+ -+static inline void __skb_mark_checksum_bad(struct sk_buff *skb) -+{ -+ /* Mark current checksum as bad (typically called from GRO -+ * path). In the case that ip_summed is CHECKSUM_NONE -+ * this must be the first checksum encountered in the packet. -+ * When ip_summed is CHECKSUM_UNNECESSARY, this is the first -+ * checksum after the last one validated. For UDP, a zero -+ * checksum can not be marked as bad. -+ */ -+ -+ if (skb->ip_summed == CHECKSUM_NONE || -+ skb->ip_summed == CHECKSUM_UNNECESSARY) -+ skb->csum_bad = 1; -+} -+ -+/* Check if we need to perform checksum complete validation. -+ * -+ * Returns true if checksum complete is needed, false otherwise -+ * (either checksum is unnecessary or zero checksum is allowed). -+ */ -+static inline bool __skb_checksum_validate_needed(struct sk_buff *skb, -+ bool zero_okay, -+ __sum16 check) -+{ -+ if (skb_csum_unnecessary(skb) || (zero_okay && !check)) { -+ skb->csum_valid = 1; -+ __skb_decr_checksum_unnecessary(skb); -+ return false; -+ } -+ -+ return true; -+} -+ -+/* For small packets <= CHECKSUM_BREAK peform checksum complete directly -+ * in checksum_init. -+ */ -+#define CHECKSUM_BREAK 76 -+ -+/* Unset checksum-complete -+ * -+ * Unset checksum complete can be done when packet is being modified -+ * (uncompressed for instance) and checksum-complete value is -+ * invalidated. -+ */ -+static inline void skb_checksum_complete_unset(struct sk_buff *skb) -+{ -+ if (skb->ip_summed == CHECKSUM_COMPLETE) -+ skb->ip_summed = CHECKSUM_NONE; -+} -+ -+/* Validate (init) checksum based on checksum complete. -+ * -+ * Return values: -+ * 0: checksum is validated or try to in skb_checksum_complete. In the latter -+ * case the ip_summed will not be CHECKSUM_UNNECESSARY and the pseudo -+ * checksum is stored in skb->csum for use in __skb_checksum_complete -+ * non-zero: value of invalid checksum -+ * -+ */ -+static inline __sum16 __skb_checksum_validate_complete(struct sk_buff *skb, -+ bool complete, -+ __wsum psum) -+{ -+ if (skb->ip_summed == CHECKSUM_COMPLETE) { -+ if (!csum_fold(csum_add(psum, skb->csum))) { -+ skb->csum_valid = 1; -+ return 0; -+ } -+ } else if (skb->csum_bad) { -+ /* ip_summed == CHECKSUM_NONE in this case */ -+ return 1; -+ } -+ -+ skb->csum = psum; -+ -+ if (complete || skb->len <= CHECKSUM_BREAK) { -+ __sum16 csum; -+ -+ csum = __skb_checksum_complete(skb); -+ skb->csum_valid = !csum; -+ return csum; -+ } -+ -+ return 0; -+} -+ -+static inline __wsum null_compute_pseudo(struct sk_buff *skb, int proto) -+{ -+ return 0; -+} -+ -+/* Perform checksum validate (init). Note that this is a macro since we only -+ * want to calculate the pseudo header which is an input function if necessary. -+ * First we try to validate without any computation (checksum unnecessary) and -+ * then calculate based on checksum complete calling the function to compute -+ * pseudo header. -+ * -+ * Return values: -+ * 0: checksum is validated or try to in skb_checksum_complete -+ * non-zero: value of invalid checksum -+ */ -+#define __skb_checksum_validate(skb, proto, complete, \ -+ zero_okay, check, compute_pseudo) \ -+({ \ -+ __sum16 __ret = 0; \ -+ skb->csum_valid = 0; \ -+ if (__skb_checksum_validate_needed(skb, zero_okay, check)) \ -+ __ret = __skb_checksum_validate_complete(skb, \ -+ complete, compute_pseudo(skb, proto)); \ -+ __ret; \ -+}) -+ -+#define skb_checksum_init(skb, proto, compute_pseudo) \ -+ __skb_checksum_validate(skb, proto, false, false, 0, compute_pseudo) -+ -+#define skb_checksum_init_zero_check(skb, proto, check, compute_pseudo) \ -+ __skb_checksum_validate(skb, proto, false, true, check, compute_pseudo) -+ -+#define skb_checksum_validate(skb, proto, compute_pseudo) \ -+ __skb_checksum_validate(skb, proto, true, false, 0, compute_pseudo) -+ -+#define skb_checksum_validate_zero_check(skb, proto, check, \ -+ compute_pseudo) \ -+ __skb_checksum_validate_(skb, proto, true, true, check, compute_pseudo) -+ -+#define skb_checksum_simple_validate(skb) \ -+ __skb_checksum_validate(skb, 0, true, false, 0, null_compute_pseudo) -+ -+static inline bool __skb_checksum_convert_check(struct sk_buff *skb) -+{ -+ return (skb->ip_summed == CHECKSUM_NONE && -+ skb->csum_valid && !skb->csum_bad); -+} -+ -+static inline void __skb_checksum_convert(struct sk_buff *skb, -+ __sum16 check, __wsum pseudo) -+{ -+ skb->csum = ~pseudo; -+ skb->ip_summed = CHECKSUM_COMPLETE; -+} -+ -+#define skb_checksum_try_convert(skb, proto, check, compute_pseudo) \ -+do { \ -+ if (__skb_checksum_convert_check(skb)) \ -+ __skb_checksum_convert(skb, check, \ -+ compute_pseudo(skb, proto)); \ -+} while (0) -+ -+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) -+void nf_conntrack_destroy(struct nf_conntrack *nfct); -+static inline void nf_conntrack_put(struct nf_conntrack *nfct) -+{ -+ if (nfct && atomic_dec_and_test(&nfct->use)) -+ nf_conntrack_destroy(nfct); -+} -+static inline void nf_conntrack_get(struct nf_conntrack *nfct) -+{ -+ if (nfct) -+ atomic_inc(&nfct->use); -+} -+#endif -+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) -+static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge) -+{ -+ if (nf_bridge && atomic_dec_and_test(&nf_bridge->use)) -+ kfree(nf_bridge); -+} -+static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge) -+{ -+ if (nf_bridge) -+ atomic_inc(&nf_bridge->use); -+} -+#endif /* CONFIG_BRIDGE_NETFILTER */ -+static inline void nf_reset(struct sk_buff *skb) -+{ -+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) -+ nf_conntrack_put(skb->nfct); -+ skb->nfct = NULL; -+#endif -+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) -+ nf_bridge_put(skb->nf_bridge); -+ skb->nf_bridge = NULL; -+#endif -+} -+ -+static inline void nf_reset_trace(struct sk_buff *skb) -+{ -+#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || defined(CONFIG_NF_TABLES) -+ skb->nf_trace = 0; -+#endif -+} -+ -+/* Note: This doesn't put any conntrack and bridge info in dst. */ -+static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src, -+ bool copy) -+{ -+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) -+ dst->nfct = src->nfct; -+ nf_conntrack_get(src->nfct); -+ if (copy) -+ dst->nfctinfo = src->nfctinfo; -+#endif -+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) -+ dst->nf_bridge = src->nf_bridge; -+ nf_bridge_get(src->nf_bridge); -+#endif -+#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || defined(CONFIG_NF_TABLES) -+ if (copy) -+ dst->nf_trace = src->nf_trace; -+#endif -+} -+ -+static inline void nf_copy(struct sk_buff *dst, const struct sk_buff *src) -+{ -+#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) -+ nf_conntrack_put(dst->nfct); -+#endif -+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) -+ nf_bridge_put(dst->nf_bridge); -+#endif -+ __nf_copy(dst, src, true); -+} -+ -+#ifdef CONFIG_NETWORK_SECMARK -+static inline void skb_copy_secmark(struct sk_buff *to, const struct sk_buff *from) -+{ -+ to->secmark = from->secmark; -+} -+ -+static inline void skb_init_secmark(struct sk_buff *skb) -+{ -+ skb->secmark = 0; -+} -+#else -+static inline void skb_copy_secmark(struct sk_buff *to, const struct sk_buff *from) -+{ } -+ -+static inline void skb_init_secmark(struct sk_buff *skb) -+{ } -+#endif -+ -+static inline bool skb_irq_freeable(const struct sk_buff *skb) -+{ -+ return !skb->destructor && -+#if IS_ENABLED(CONFIG_XFRM) -+ !skb->sp && -+#endif -+#if IS_ENABLED(CONFIG_NF_CONNTRACK) -+ !skb->nfct && -+#endif -+ !skb->_skb_refdst && -+ !skb_has_frag_list(skb); -+} -+ -+static inline void skb_set_queue_mapping(struct sk_buff *skb, u16 queue_mapping) -+{ -+ skb->queue_mapping = queue_mapping; -+} -+ -+static inline u16 skb_get_queue_mapping(const struct sk_buff *skb) -+{ -+ return skb->queue_mapping; -+} -+ -+static inline void skb_copy_queue_mapping(struct sk_buff *to, const struct sk_buff *from) -+{ -+ to->queue_mapping = from->queue_mapping; -+} -+ -+static inline void skb_record_rx_queue(struct sk_buff *skb, u16 rx_queue) -+{ -+ skb->queue_mapping = rx_queue + 1; -+} -+ -+static inline u16 skb_get_rx_queue(const struct sk_buff *skb) -+{ -+ return skb->queue_mapping - 1; -+} -+ -+static inline bool skb_rx_queue_recorded(const struct sk_buff *skb) -+{ -+ return skb->queue_mapping != 0; -+} -+ -+u16 __skb_tx_hash(const struct net_device *dev, struct sk_buff *skb, -+ unsigned int num_tx_queues); -+ -+static inline struct sec_path *skb_sec_path(struct sk_buff *skb) -+{ -+#ifdef CONFIG_XFRM -+ return skb->sp; -+#else -+ return NULL; -+#endif -+} -+ -+/* Keeps track of mac header offset relative to skb->head. -+ * It is useful for TSO of Tunneling protocol. e.g. GRE. -+ * For non-tunnel skb it points to skb_mac_header() and for -+ * tunnel skb it points to outer mac header. -+ * Keeps track of level of encapsulation of network headers. -+ */ -+struct skb_gso_cb { -+ int mac_offset; -+ int encap_level; -+ __u16 csum_start; -+}; -+#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb) -+ -+static inline int skb_tnl_header_len(const struct sk_buff *inner_skb) -+{ -+ return (skb_mac_header(inner_skb) - inner_skb->head) - -+ SKB_GSO_CB(inner_skb)->mac_offset; -+} -+ -+static inline int gso_pskb_expand_head(struct sk_buff *skb, int extra) -+{ -+ int new_headroom, headroom; -+ int ret; -+ -+ headroom = skb_headroom(skb); -+ ret = pskb_expand_head(skb, extra, 0, GFP_ATOMIC); -+ if (ret) -+ return ret; -+ -+ new_headroom = skb_headroom(skb); -+ SKB_GSO_CB(skb)->mac_offset += (new_headroom - headroom); -+ return 0; -+} -+ -+/* Compute the checksum for a gso segment. First compute the checksum value -+ * from the start of transport header to SKB_GSO_CB(skb)->csum_start, and -+ * then add in skb->csum (checksum from csum_start to end of packet). -+ * skb->csum and csum_start are then updated to reflect the checksum of the -+ * resultant packet starting from the transport header-- the resultant checksum -+ * is in the res argument (i.e. normally zero or ~ of checksum of a pseudo -+ * header. -+ */ -+static inline __sum16 gso_make_checksum(struct sk_buff *skb, __wsum res) -+{ -+ int plen = SKB_GSO_CB(skb)->csum_start - skb_headroom(skb) - -+ skb_transport_offset(skb); -+ __u16 csum; -+ -+ csum = csum_fold(csum_partial(skb_transport_header(skb), -+ plen, skb->csum)); -+ skb->csum = res; -+ SKB_GSO_CB(skb)->csum_start -= plen; -+ -+ return csum; -+} -+ -+static inline bool skb_is_gso(const struct sk_buff *skb) -+{ -+ return skb_shinfo(skb)->gso_size; -+} -+ -+/* Note: Should be called only if skb_is_gso(skb) is true */ -+static inline bool skb_is_gso_v6(const struct sk_buff *skb) -+{ -+ return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6; -+} -+ -+void __skb_warn_lro_forwarding(const struct sk_buff *skb); -+ -+static inline bool skb_warn_if_lro(const struct sk_buff *skb) -+{ -+ /* LRO sets gso_size but not gso_type, whereas if GSO is really -+ * wanted then gso_type will be set. */ -+ const struct skb_shared_info *shinfo = skb_shinfo(skb); -+ -+ if (skb_is_nonlinear(skb) && shinfo->gso_size != 0 && -+ unlikely(shinfo->gso_type == 0)) { -+ __skb_warn_lro_forwarding(skb); -+ return true; -+ } -+ return false; -+} -+ -+static inline void skb_forward_csum(struct sk_buff *skb) -+{ -+ /* Unfortunately we don't support this one. Any brave souls? */ -+ if (skb->ip_summed == CHECKSUM_COMPLETE) -+ skb->ip_summed = CHECKSUM_NONE; -+} -+ -+/** -+ * skb_checksum_none_assert - make sure skb ip_summed is CHECKSUM_NONE -+ * @skb: skb to check -+ * -+ * fresh skbs have their ip_summed set to CHECKSUM_NONE. -+ * Instead of forcing ip_summed to CHECKSUM_NONE, we can -+ * use this helper, to document places where we make this assertion. -+ */ -+static inline void skb_checksum_none_assert(const struct sk_buff *skb) -+{ -+#ifdef DEBUG -+ BUG_ON(skb->ip_summed != CHECKSUM_NONE); -+#endif -+} -+ -+bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off); -+ -+int skb_checksum_setup(struct sk_buff *skb, bool recalculate); -+ -+u32 skb_get_poff(const struct sk_buff *skb); -+u32 __skb_get_poff(const struct sk_buff *skb, void *data, -+ const struct flow_keys *keys, int hlen); -+ -+/** -+ * skb_head_is_locked - Determine if the skb->head is locked down -+ * @skb: skb to check -+ * -+ * The head on skbs build around a head frag can be removed if they are -+ * not cloned. This function returns true if the skb head is locked down -+ * due to either being allocated via kmalloc, or by being a clone with -+ * multiple references to the head. -+ */ -+static inline bool skb_head_is_locked(const struct sk_buff *skb) -+{ -+ return !skb->head_frag || skb_cloned(skb); -+} -+ -+/** -+ * skb_gso_network_seglen - Return length of individual segments of a gso packet -+ * -+ * @skb: GSO skb -+ * -+ * skb_gso_network_seglen is used to determine the real size of the -+ * individual segments, including Layer3 (IP, IPv6) and L4 headers (TCP/UDP). -+ * -+ * The MAC/L2 header is not accounted for. -+ */ -+static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb) -+{ -+ unsigned int hdr_len = skb_transport_header(skb) - -+ skb_network_header(skb); -+ return hdr_len + skb_gso_transport_seglen(skb); -+} -+#endif /* __KERNEL__ */ -+#endif /* _LINUX_SKBUFF_H */ -diff -Nur linux-3.18.14.orig/include/linux/smp.h linux-3.18.14-rt/include/linux/smp.h ---- linux-3.18.14.orig/include/linux/smp.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/smp.h 2015-05-31 15:32:48.405635367 -0500 -@@ -178,6 +178,9 @@ - #define get_cpu() ({ preempt_disable(); smp_processor_id(); }) - #define put_cpu() preempt_enable() - -+#define get_cpu_light() ({ migrate_disable(); smp_processor_id(); }) -+#define put_cpu_light() migrate_enable() -+ - /* - * Callback to arch code if there's nosmp or maxcpus=0 on the - * boot command line: -diff -Nur linux-3.18.14.orig/include/linux/spinlock_api_smp.h linux-3.18.14-rt/include/linux/spinlock_api_smp.h ---- linux-3.18.14.orig/include/linux/spinlock_api_smp.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/spinlock_api_smp.h 2015-05-31 15:32:48.409635367 -0500 -@@ -187,6 +187,8 @@ - return 0; - } - --#include -+#ifndef CONFIG_PREEMPT_RT_FULL -+# include -+#endif - - #endif /* __LINUX_SPINLOCK_API_SMP_H */ -diff -Nur linux-3.18.14.orig/include/linux/spinlock.h linux-3.18.14-rt/include/linux/spinlock.h ---- linux-3.18.14.orig/include/linux/spinlock.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/spinlock.h 2015-05-31 15:32:48.405635367 -0500 -@@ -278,7 +278,11 @@ - #define raw_spin_can_lock(lock) (!raw_spin_is_locked(lock)) - - /* Include rwlock functions */ --#include -+#ifdef CONFIG_PREEMPT_RT_FULL -+# include -+#else -+# include -+#endif - - /* - * Pull the _spin_*()/_read_*()/_write_*() functions/declarations: -@@ -289,6 +293,10 @@ - # include - #endif - -+#ifdef CONFIG_PREEMPT_RT_FULL -+# include -+#else /* PREEMPT_RT_FULL */ -+ - /* - * Map the spin_lock functions to the raw variants for PREEMPT_RT=n - */ -@@ -418,4 +426,6 @@ - #define atomic_dec_and_lock(atomic, lock) \ - __cond_lock(lock, _atomic_dec_and_lock(atomic, lock)) - -+#endif /* !PREEMPT_RT_FULL */ -+ - #endif /* __LINUX_SPINLOCK_H */ -diff -Nur linux-3.18.14.orig/include/linux/spinlock_rt.h linux-3.18.14-rt/include/linux/spinlock_rt.h ---- linux-3.18.14.orig/include/linux/spinlock_rt.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/spinlock_rt.h 2015-05-31 15:32:48.413635367 -0500 -@@ -0,0 +1,167 @@ -+#ifndef __LINUX_SPINLOCK_RT_H -+#define __LINUX_SPINLOCK_RT_H -+ -+#ifndef __LINUX_SPINLOCK_H -+#error Do not include directly. Use spinlock.h -+#endif -+ -+#include -+ -+extern void -+__rt_spin_lock_init(spinlock_t *lock, char *name, struct lock_class_key *key); -+ -+#define spin_lock_init(slock) \ -+do { \ -+ static struct lock_class_key __key; \ -+ \ -+ rt_mutex_init(&(slock)->lock); \ -+ __rt_spin_lock_init(slock, #slock, &__key); \ -+} while (0) -+ -+extern void __lockfunc rt_spin_lock(spinlock_t *lock); -+extern unsigned long __lockfunc rt_spin_lock_trace_flags(spinlock_t *lock); -+extern void __lockfunc rt_spin_lock_nested(spinlock_t *lock, int subclass); -+extern void __lockfunc rt_spin_unlock(spinlock_t *lock); -+extern void __lockfunc rt_spin_unlock_after_trylock_in_irq(spinlock_t *lock); -+extern void __lockfunc rt_spin_unlock_wait(spinlock_t *lock); -+extern int __lockfunc rt_spin_trylock_irqsave(spinlock_t *lock, unsigned long *flags); -+extern int __lockfunc rt_spin_trylock_bh(spinlock_t *lock); -+extern int __lockfunc rt_spin_trylock(spinlock_t *lock); -+extern int atomic_dec_and_spin_lock(atomic_t *atomic, spinlock_t *lock); -+ -+/* -+ * lockdep-less calls, for derived types like rwlock: -+ * (for trylock they can use rt_mutex_trylock() directly. -+ */ -+extern void __lockfunc __rt_spin_lock(struct rt_mutex *lock); -+extern void __lockfunc __rt_spin_unlock(struct rt_mutex *lock); -+extern int __lockfunc __rt_spin_trylock(struct rt_mutex *lock); -+ -+#define spin_lock(lock) \ -+ do { \ -+ migrate_disable(); \ -+ rt_spin_lock(lock); \ -+ } while (0) -+ -+#define spin_lock_bh(lock) \ -+ do { \ -+ local_bh_disable(); \ -+ migrate_disable(); \ -+ rt_spin_lock(lock); \ -+ } while (0) -+ -+#define spin_lock_irq(lock) spin_lock(lock) -+ -+#define spin_do_trylock(lock) __cond_lock(lock, rt_spin_trylock(lock)) -+ -+#define spin_trylock(lock) \ -+({ \ -+ int __locked; \ -+ migrate_disable(); \ -+ __locked = spin_do_trylock(lock); \ -+ if (!__locked) \ -+ migrate_enable(); \ -+ __locked; \ -+}) -+ -+#ifdef CONFIG_LOCKDEP -+# define spin_lock_nested(lock, subclass) \ -+ do { \ -+ migrate_disable(); \ -+ rt_spin_lock_nested(lock, subclass); \ -+ } while (0) -+ -+# define spin_lock_irqsave_nested(lock, flags, subclass) \ -+ do { \ -+ typecheck(unsigned long, flags); \ -+ flags = 0; \ -+ migrate_disable(); \ -+ rt_spin_lock_nested(lock, subclass); \ -+ } while (0) -+#else -+# define spin_lock_nested(lock, subclass) spin_lock(lock) -+ -+# define spin_lock_irqsave_nested(lock, flags, subclass) \ -+ do { \ -+ typecheck(unsigned long, flags); \ -+ flags = 0; \ -+ spin_lock(lock); \ -+ } while (0) -+#endif -+ -+#define spin_lock_irqsave(lock, flags) \ -+ do { \ -+ typecheck(unsigned long, flags); \ -+ flags = 0; \ -+ spin_lock(lock); \ -+ } while (0) -+ -+static inline unsigned long spin_lock_trace_flags(spinlock_t *lock) -+{ -+ unsigned long flags = 0; -+#ifdef CONFIG_TRACE_IRQFLAGS -+ flags = rt_spin_lock_trace_flags(lock); -+#else -+ spin_lock(lock); /* lock_local */ -+#endif -+ return flags; -+} -+ -+/* FIXME: we need rt_spin_lock_nest_lock */ -+#define spin_lock_nest_lock(lock, nest_lock) spin_lock_nested(lock, 0) -+ -+#define spin_unlock(lock) \ -+ do { \ -+ rt_spin_unlock(lock); \ -+ migrate_enable(); \ -+ } while (0) -+ -+#define spin_unlock_bh(lock) \ -+ do { \ -+ rt_spin_unlock(lock); \ -+ migrate_enable(); \ -+ local_bh_enable(); \ -+ } while (0) -+ -+#define spin_unlock_irq(lock) spin_unlock(lock) -+ -+#define spin_unlock_irqrestore(lock, flags) \ -+ do { \ -+ typecheck(unsigned long, flags); \ -+ (void) flags; \ -+ spin_unlock(lock); \ -+ } while (0) -+ -+#define spin_trylock_bh(lock) __cond_lock(lock, rt_spin_trylock_bh(lock)) -+#define spin_trylock_irq(lock) spin_trylock(lock) -+ -+#define spin_trylock_irqsave(lock, flags) \ -+ rt_spin_trylock_irqsave(lock, &(flags)) -+ -+#define spin_unlock_wait(lock) rt_spin_unlock_wait(lock) -+ -+#ifdef CONFIG_GENERIC_LOCKBREAK -+# define spin_is_contended(lock) ((lock)->break_lock) -+#else -+# define spin_is_contended(lock) (((void)(lock), 0)) -+#endif -+ -+static inline int spin_can_lock(spinlock_t *lock) -+{ -+ return !rt_mutex_is_locked(&lock->lock); -+} -+ -+static inline int spin_is_locked(spinlock_t *lock) -+{ -+ return rt_mutex_is_locked(&lock->lock); -+} -+ -+static inline void assert_spin_locked(spinlock_t *lock) -+{ -+ BUG_ON(!spin_is_locked(lock)); -+} -+ -+#define atomic_dec_and_lock(atomic, lock) \ -+ atomic_dec_and_spin_lock(atomic, lock) -+ -+#endif -diff -Nur linux-3.18.14.orig/include/linux/spinlock_types.h linux-3.18.14-rt/include/linux/spinlock_types.h ---- linux-3.18.14.orig/include/linux/spinlock_types.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/spinlock_types.h 2015-05-31 15:32:48.413635367 -0500 -@@ -9,80 +9,15 @@ - * Released under the General Public License (GPL). - */ - --#if defined(CONFIG_SMP) --# include --#else --# include --#endif -- --#include -- --typedef struct raw_spinlock { -- arch_spinlock_t raw_lock; --#ifdef CONFIG_GENERIC_LOCKBREAK -- unsigned int break_lock; --#endif --#ifdef CONFIG_DEBUG_SPINLOCK -- unsigned int magic, owner_cpu; -- void *owner; --#endif --#ifdef CONFIG_DEBUG_LOCK_ALLOC -- struct lockdep_map dep_map; --#endif --} raw_spinlock_t; -- --#define SPINLOCK_MAGIC 0xdead4ead -- --#define SPINLOCK_OWNER_INIT ((void *)-1L) -- --#ifdef CONFIG_DEBUG_LOCK_ALLOC --# define SPIN_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname } --#else --# define SPIN_DEP_MAP_INIT(lockname) --#endif -+#include - --#ifdef CONFIG_DEBUG_SPINLOCK --# define SPIN_DEBUG_INIT(lockname) \ -- .magic = SPINLOCK_MAGIC, \ -- .owner_cpu = -1, \ -- .owner = SPINLOCK_OWNER_INIT, -+#ifndef CONFIG_PREEMPT_RT_FULL -+# include -+# include - #else --# define SPIN_DEBUG_INIT(lockname) -+# include -+# include -+# include - #endif - --#define __RAW_SPIN_LOCK_INITIALIZER(lockname) \ -- { \ -- .raw_lock = __ARCH_SPIN_LOCK_UNLOCKED, \ -- SPIN_DEBUG_INIT(lockname) \ -- SPIN_DEP_MAP_INIT(lockname) } -- --#define __RAW_SPIN_LOCK_UNLOCKED(lockname) \ -- (raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname) -- --#define DEFINE_RAW_SPINLOCK(x) raw_spinlock_t x = __RAW_SPIN_LOCK_UNLOCKED(x) -- --typedef struct spinlock { -- union { -- struct raw_spinlock rlock; -- --#ifdef CONFIG_DEBUG_LOCK_ALLOC --# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) -- struct { -- u8 __padding[LOCK_PADSIZE]; -- struct lockdep_map dep_map; -- }; --#endif -- }; --} spinlock_t; -- --#define __SPIN_LOCK_INITIALIZER(lockname) \ -- { { .rlock = __RAW_SPIN_LOCK_INITIALIZER(lockname) } } -- --#define __SPIN_LOCK_UNLOCKED(lockname) \ -- (spinlock_t ) __SPIN_LOCK_INITIALIZER(lockname) -- --#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x) -- --#include -- - #endif /* __LINUX_SPINLOCK_TYPES_H */ -diff -Nur linux-3.18.14.orig/include/linux/spinlock_types_nort.h linux-3.18.14-rt/include/linux/spinlock_types_nort.h ---- linux-3.18.14.orig/include/linux/spinlock_types_nort.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/spinlock_types_nort.h 2015-05-31 15:32:48.413635367 -0500 -@@ -0,0 +1,33 @@ -+#ifndef __LINUX_SPINLOCK_TYPES_NORT_H -+#define __LINUX_SPINLOCK_TYPES_NORT_H -+ -+#ifndef __LINUX_SPINLOCK_TYPES_H -+#error "Do not include directly. Include spinlock_types.h instead" -+#endif -+ -+/* -+ * The non RT version maps spinlocks to raw_spinlocks -+ */ -+typedef struct spinlock { -+ union { -+ struct raw_spinlock rlock; -+ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) -+ struct { -+ u8 __padding[LOCK_PADSIZE]; -+ struct lockdep_map dep_map; -+ }; -+#endif -+ }; -+} spinlock_t; -+ -+#define __SPIN_LOCK_INITIALIZER(lockname) \ -+ { { .rlock = __RAW_SPIN_LOCK_INITIALIZER(lockname) } } -+ -+#define __SPIN_LOCK_UNLOCKED(lockname) \ -+ (spinlock_t ) __SPIN_LOCK_INITIALIZER(lockname) -+ -+#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x) -+ -+#endif -diff -Nur linux-3.18.14.orig/include/linux/spinlock_types_raw.h linux-3.18.14-rt/include/linux/spinlock_types_raw.h ---- linux-3.18.14.orig/include/linux/spinlock_types_raw.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/spinlock_types_raw.h 2015-05-31 15:32:48.413635367 -0500 -@@ -0,0 +1,56 @@ -+#ifndef __LINUX_SPINLOCK_TYPES_RAW_H -+#define __LINUX_SPINLOCK_TYPES_RAW_H -+ -+#if defined(CONFIG_SMP) -+# include -+#else -+# include -+#endif -+ -+#include -+ -+typedef struct raw_spinlock { -+ arch_spinlock_t raw_lock; -+#ifdef CONFIG_GENERIC_LOCKBREAK -+ unsigned int break_lock; -+#endif -+#ifdef CONFIG_DEBUG_SPINLOCK -+ unsigned int magic, owner_cpu; -+ void *owner; -+#endif -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ struct lockdep_map dep_map; -+#endif -+} raw_spinlock_t; -+ -+#define SPINLOCK_MAGIC 0xdead4ead -+ -+#define SPINLOCK_OWNER_INIT ((void *)-1L) -+ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+# define SPIN_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname } -+#else -+# define SPIN_DEP_MAP_INIT(lockname) -+#endif -+ -+#ifdef CONFIG_DEBUG_SPINLOCK -+# define SPIN_DEBUG_INIT(lockname) \ -+ .magic = SPINLOCK_MAGIC, \ -+ .owner_cpu = -1, \ -+ .owner = SPINLOCK_OWNER_INIT, -+#else -+# define SPIN_DEBUG_INIT(lockname) -+#endif -+ -+#define __RAW_SPIN_LOCK_INITIALIZER(lockname) \ -+ { \ -+ .raw_lock = __ARCH_SPIN_LOCK_UNLOCKED, \ -+ SPIN_DEBUG_INIT(lockname) \ -+ SPIN_DEP_MAP_INIT(lockname) } -+ -+#define __RAW_SPIN_LOCK_UNLOCKED(lockname) \ -+ (raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname) -+ -+#define DEFINE_RAW_SPINLOCK(x) raw_spinlock_t x = __RAW_SPIN_LOCK_UNLOCKED(x) -+ -+#endif -diff -Nur linux-3.18.14.orig/include/linux/spinlock_types_rt.h linux-3.18.14-rt/include/linux/spinlock_types_rt.h ---- linux-3.18.14.orig/include/linux/spinlock_types_rt.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/spinlock_types_rt.h 2015-05-31 15:32:48.413635367 -0500 -@@ -0,0 +1,51 @@ -+#ifndef __LINUX_SPINLOCK_TYPES_RT_H -+#define __LINUX_SPINLOCK_TYPES_RT_H -+ -+#ifndef __LINUX_SPINLOCK_TYPES_H -+#error "Do not include directly. Include spinlock_types.h instead" -+#endif -+ -+#include -+ -+/* -+ * PREEMPT_RT: spinlocks - an RT mutex plus lock-break field: -+ */ -+typedef struct spinlock { -+ struct rt_mutex lock; -+ unsigned int break_lock; -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ struct lockdep_map dep_map; -+#endif -+} spinlock_t; -+ -+#ifdef CONFIG_DEBUG_RT_MUTEXES -+# define __RT_SPIN_INITIALIZER(name) \ -+ { \ -+ .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock), \ -+ .save_state = 1, \ -+ .file = __FILE__, \ -+ .line = __LINE__ , \ -+ } -+#else -+# define __RT_SPIN_INITIALIZER(name) \ -+ { \ -+ .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock), \ -+ .save_state = 1, \ -+ } -+#endif -+ -+/* -+.wait_list = PLIST_HEAD_INIT_RAW((name).lock.wait_list, (name).lock.wait_lock) -+*/ -+ -+#define __SPIN_LOCK_UNLOCKED(name) \ -+ { .lock = __RT_SPIN_INITIALIZER(name.lock), \ -+ SPIN_DEP_MAP_INIT(name) } -+ -+#define __DEFINE_SPINLOCK(name) \ -+ spinlock_t name = __SPIN_LOCK_UNLOCKED(name) -+ -+#define DEFINE_SPINLOCK(name) \ -+ spinlock_t name __cacheline_aligned_in_smp = __SPIN_LOCK_UNLOCKED(name) -+ -+#endif -diff -Nur linux-3.18.14.orig/include/linux/srcu.h linux-3.18.14-rt/include/linux/srcu.h ---- linux-3.18.14.orig/include/linux/srcu.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/srcu.h 2015-05-31 15:32:48.445635367 -0500 -@@ -84,10 +84,10 @@ - - void process_srcu(struct work_struct *work); - --#define __SRCU_STRUCT_INIT(name) \ -+#define __SRCU_STRUCT_INIT(name, pcpu_name) \ - { \ - .completed = -300, \ -- .per_cpu_ref = &name##_srcu_array, \ -+ .per_cpu_ref = &pcpu_name, \ - .queue_lock = __SPIN_LOCK_UNLOCKED(name.queue_lock), \ - .running = false, \ - .batch_queue = RCU_BATCH_INIT(name.batch_queue), \ -@@ -104,11 +104,12 @@ - */ - #define DEFINE_SRCU(name) \ - static DEFINE_PER_CPU(struct srcu_struct_array, name##_srcu_array);\ -- struct srcu_struct name = __SRCU_STRUCT_INIT(name); -+ struct srcu_struct name = __SRCU_STRUCT_INIT(name, name##_srcu_array); - - #define DEFINE_STATIC_SRCU(name) \ - static DEFINE_PER_CPU(struct srcu_struct_array, name##_srcu_array);\ -- static struct srcu_struct name = __SRCU_STRUCT_INIT(name); -+ static struct srcu_struct name = __SRCU_STRUCT_INIT(\ -+ name, name##_srcu_array); - - /** - * call_srcu() - Queue a callback for invocation after an SRCU grace period -diff -Nur linux-3.18.14.orig/include/linux/swap.h linux-3.18.14-rt/include/linux/swap.h ---- linux-3.18.14.orig/include/linux/swap.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/swap.h 2015-05-31 15:32:48.449635367 -0500 -@@ -11,6 +11,7 @@ - #include - #include - #include -+#include - #include - - struct notifier_block; -@@ -260,7 +261,8 @@ - void *workingset_eviction(struct address_space *mapping, struct page *page); - bool workingset_refault(void *shadow); - void workingset_activation(struct page *page); --extern struct list_lru workingset_shadow_nodes; -+extern struct list_lru __workingset_shadow_nodes; -+DECLARE_LOCAL_IRQ_LOCK(workingset_shadow_lock); - - static inline unsigned int workingset_node_pages(struct radix_tree_node *node) - { -diff -Nur linux-3.18.14.orig/include/linux/sysctl.h linux-3.18.14-rt/include/linux/sysctl.h ---- linux-3.18.14.orig/include/linux/sysctl.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/sysctl.h 2015-05-31 15:32:48.449635367 -0500 -@@ -25,6 +25,7 @@ - #include - #include - #include -+#include - #include - - /* For the /proc/sys support */ -diff -Nur linux-3.18.14.orig/include/linux/thread_info.h linux-3.18.14-rt/include/linux/thread_info.h ---- linux-3.18.14.orig/include/linux/thread_info.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/thread_info.h 2015-05-31 15:32:48.449635367 -0500 -@@ -102,7 +102,17 @@ - #define test_thread_flag(flag) \ - test_ti_thread_flag(current_thread_info(), flag) - --#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED) -+#ifdef CONFIG_PREEMPT_LAZY -+#define tif_need_resched() (test_thread_flag(TIF_NEED_RESCHED) || \ -+ test_thread_flag(TIF_NEED_RESCHED_LAZY)) -+#define tif_need_resched_now() (test_thread_flag(TIF_NEED_RESCHED)) -+#define tif_need_resched_lazy() test_thread_flag(TIF_NEED_RESCHED_LAZY)) -+ -+#else -+#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED) -+#define tif_need_resched_now() test_thread_flag(TIF_NEED_RESCHED) -+#define tif_need_resched_lazy() 0 -+#endif - - #if defined TIF_RESTORE_SIGMASK && !defined HAVE_SET_RESTORE_SIGMASK - /* -diff -Nur linux-3.18.14.orig/include/linux/timer.h linux-3.18.14-rt/include/linux/timer.h ---- linux-3.18.14.orig/include/linux/timer.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/timer.h 2015-05-31 15:32:48.449635367 -0500 -@@ -241,7 +241,7 @@ - - extern int try_to_del_timer_sync(struct timer_list *timer); - --#ifdef CONFIG_SMP -+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) - extern int del_timer_sync(struct timer_list *timer); - #else - # define del_timer_sync(t) del_timer(t) -diff -Nur linux-3.18.14.orig/include/linux/uaccess.h linux-3.18.14-rt/include/linux/uaccess.h ---- linux-3.18.14.orig/include/linux/uaccess.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/uaccess.h 2015-05-31 15:32:48.449635367 -0500 -@@ -6,14 +6,9 @@ - - /* - * These routines enable/disable the pagefault handler in that -- * it will not take any locks and go straight to the fixup table. -- * -- * They have great resemblance to the preempt_disable/enable calls -- * and in fact they are identical; this is because currently there is -- * no other way to make the pagefault handlers do this. So we do -- * disable preemption but we don't necessarily care about that. -+ * it will not take any MM locks and go straight to the fixup table. - */ --static inline void pagefault_disable(void) -+static inline void raw_pagefault_disable(void) - { - preempt_count_inc(); - /* -@@ -23,7 +18,7 @@ - barrier(); - } - --static inline void pagefault_enable(void) -+static inline void raw_pagefault_enable(void) - { - #ifndef CONFIG_PREEMPT - /* -@@ -37,6 +32,21 @@ - #endif - } - -+#ifndef CONFIG_PREEMPT_RT_FULL -+static inline void pagefault_disable(void) -+{ -+ raw_pagefault_disable(); -+} -+ -+static inline void pagefault_enable(void) -+{ -+ raw_pagefault_enable(); -+} -+#else -+extern void pagefault_disable(void); -+extern void pagefault_enable(void); -+#endif -+ - #ifndef ARCH_HAS_NOCACHE_UACCESS - - static inline unsigned long __copy_from_user_inatomic_nocache(void *to, -@@ -76,9 +86,9 @@ - mm_segment_t old_fs = get_fs(); \ - \ - set_fs(KERNEL_DS); \ -- pagefault_disable(); \ -+ raw_pagefault_disable(); \ - ret = __copy_from_user_inatomic(&(retval), (__force typeof(retval) __user *)(addr), sizeof(retval)); \ -- pagefault_enable(); \ -+ raw_pagefault_enable(); \ - set_fs(old_fs); \ - ret; \ - }) -diff -Nur linux-3.18.14.orig/include/linux/uprobes.h linux-3.18.14-rt/include/linux/uprobes.h ---- linux-3.18.14.orig/include/linux/uprobes.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/uprobes.h 2015-05-31 15:32:48.481635367 -0500 -@@ -27,6 +27,7 @@ - #include - #include - #include -+#include - - struct vm_area_struct; - struct mm_struct; -diff -Nur linux-3.18.14.orig/include/linux/vmstat.h linux-3.18.14-rt/include/linux/vmstat.h ---- linux-3.18.14.orig/include/linux/vmstat.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/vmstat.h 2015-05-31 15:32:48.481635367 -0500 -@@ -33,7 +33,9 @@ - */ - static inline void __count_vm_event(enum vm_event_item item) - { -+ preempt_disable_rt(); - raw_cpu_inc(vm_event_states.event[item]); -+ preempt_enable_rt(); - } - - static inline void count_vm_event(enum vm_event_item item) -@@ -43,7 +45,9 @@ - - static inline void __count_vm_events(enum vm_event_item item, long delta) - { -+ preempt_disable_rt(); - raw_cpu_add(vm_event_states.event[item], delta); -+ preempt_enable_rt(); - } - - static inline void count_vm_events(enum vm_event_item item, long delta) -diff -Nur linux-3.18.14.orig/include/linux/wait.h linux-3.18.14-rt/include/linux/wait.h ---- linux-3.18.14.orig/include/linux/wait.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/linux/wait.h 2015-05-31 15:32:48.481635367 -0500 -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - - typedef struct __wait_queue wait_queue_t; - typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key); -diff -Nur linux-3.18.14.orig/include/linux/wait-simple.h linux-3.18.14-rt/include/linux/wait-simple.h ---- linux-3.18.14.orig/include/linux/wait-simple.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/wait-simple.h 2015-05-31 15:32:48.481635367 -0500 -@@ -0,0 +1,207 @@ -+#ifndef _LINUX_WAIT_SIMPLE_H -+#define _LINUX_WAIT_SIMPLE_H -+ -+#include -+#include -+ -+#include -+ -+struct swaiter { -+ struct task_struct *task; -+ struct list_head node; -+}; -+ -+#define DEFINE_SWAITER(name) \ -+ struct swaiter name = { \ -+ .task = current, \ -+ .node = LIST_HEAD_INIT((name).node), \ -+ } -+ -+struct swait_head { -+ raw_spinlock_t lock; -+ struct list_head list; -+}; -+ -+#define SWAIT_HEAD_INITIALIZER(name) { \ -+ .lock = __RAW_SPIN_LOCK_UNLOCKED(name.lock), \ -+ .list = LIST_HEAD_INIT((name).list), \ -+ } -+ -+#define DEFINE_SWAIT_HEAD(name) \ -+ struct swait_head name = SWAIT_HEAD_INITIALIZER(name) -+ -+extern void __init_swait_head(struct swait_head *h, struct lock_class_key *key); -+ -+#define init_swait_head(swh) \ -+ do { \ -+ static struct lock_class_key __key; \ -+ \ -+ __init_swait_head((swh), &__key); \ -+ } while (0) -+ -+/* -+ * Waiter functions -+ */ -+extern void swait_prepare_locked(struct swait_head *head, struct swaiter *w); -+extern void swait_prepare(struct swait_head *head, struct swaiter *w, int state); -+extern void swait_finish_locked(struct swait_head *head, struct swaiter *w); -+extern void swait_finish(struct swait_head *head, struct swaiter *w); -+ -+/* Check whether a head has waiters enqueued */ -+static inline bool swaitqueue_active(struct swait_head *h) -+{ -+ /* Make sure the condition is visible before checking list_empty() */ -+ smp_mb(); -+ return !list_empty(&h->list); -+} -+ -+/* -+ * Wakeup functions -+ */ -+extern unsigned int __swait_wake(struct swait_head *head, unsigned int state, unsigned int num); -+extern unsigned int __swait_wake_locked(struct swait_head *head, unsigned int state, unsigned int num); -+ -+#define swait_wake(head) __swait_wake(head, TASK_NORMAL, 1) -+#define swait_wake_interruptible(head) __swait_wake(head, TASK_INTERRUPTIBLE, 1) -+#define swait_wake_all(head) __swait_wake(head, TASK_NORMAL, 0) -+#define swait_wake_all_interruptible(head) __swait_wake(head, TASK_INTERRUPTIBLE, 0) -+ -+/* -+ * Event API -+ */ -+#define __swait_event(wq, condition) \ -+do { \ -+ DEFINE_SWAITER(__wait); \ -+ \ -+ for (;;) { \ -+ swait_prepare(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ -+ if (condition) \ -+ break; \ -+ schedule(); \ -+ } \ -+ swait_finish(&wq, &__wait); \ -+} while (0) -+ -+/** -+ * swait_event - sleep until a condition gets true -+ * @wq: the waitqueue to wait on -+ * @condition: a C expression for the event to wait for -+ * -+ * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the -+ * @condition evaluates to true. The @condition is checked each time -+ * the waitqueue @wq is woken up. -+ * -+ * wake_up() has to be called after changing any variable that could -+ * change the result of the wait condition. -+ */ -+#define swait_event(wq, condition) \ -+do { \ -+ if (condition) \ -+ break; \ -+ __swait_event(wq, condition); \ -+} while (0) -+ -+#define __swait_event_interruptible(wq, condition, ret) \ -+do { \ -+ DEFINE_SWAITER(__wait); \ -+ \ -+ for (;;) { \ -+ swait_prepare(&wq, &__wait, TASK_INTERRUPTIBLE); \ -+ if (condition) \ -+ break; \ -+ if (signal_pending(current)) { \ -+ ret = -ERESTARTSYS; \ -+ break; \ -+ } \ -+ schedule(); \ -+ } \ -+ swait_finish(&wq, &__wait); \ -+} while (0) -+ -+#define __swait_event_interruptible_timeout(wq, condition, ret) \ -+do { \ -+ DEFINE_SWAITER(__wait); \ -+ \ -+ for (;;) { \ -+ swait_prepare(&wq, &__wait, TASK_INTERRUPTIBLE); \ -+ if (condition) \ -+ break; \ -+ if (signal_pending(current)) { \ -+ ret = -ERESTARTSYS; \ -+ break; \ -+ } \ -+ ret = schedule_timeout(ret); \ -+ if (!ret) \ -+ break; \ -+ } \ -+ swait_finish(&wq, &__wait); \ -+} while (0) -+ -+/** -+ * swait_event_interruptible - sleep until a condition gets true -+ * @wq: the waitqueue to wait on -+ * @condition: a C expression for the event to wait for -+ * -+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the -+ * @condition evaluates to true. The @condition is checked each time -+ * the waitqueue @wq is woken up. -+ * -+ * wake_up() has to be called after changing any variable that could -+ * change the result of the wait condition. -+ */ -+#define swait_event_interruptible(wq, condition) \ -+({ \ -+ int __ret = 0; \ -+ if (!(condition)) \ -+ __swait_event_interruptible(wq, condition, __ret); \ -+ __ret; \ -+}) -+ -+#define swait_event_interruptible_timeout(wq, condition, timeout) \ -+({ \ -+ int __ret = timeout; \ -+ if (!(condition)) \ -+ __swait_event_interruptible_timeout(wq, condition, __ret); \ -+ __ret; \ -+}) -+ -+#define __swait_event_timeout(wq, condition, ret) \ -+do { \ -+ DEFINE_SWAITER(__wait); \ -+ \ -+ for (;;) { \ -+ swait_prepare(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ -+ if (condition) \ -+ break; \ -+ ret = schedule_timeout(ret); \ -+ if (!ret) \ -+ break; \ -+ } \ -+ swait_finish(&wq, &__wait); \ -+} while (0) -+ -+/** -+ * swait_event_timeout - sleep until a condition gets true or a timeout elapses -+ * @wq: the waitqueue to wait on -+ * @condition: a C expression for the event to wait for -+ * @timeout: timeout, in jiffies -+ * -+ * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the -+ * @condition evaluates to true. The @condition is checked each time -+ * the waitqueue @wq is woken up. -+ * -+ * wake_up() has to be called after changing any variable that could -+ * change the result of the wait condition. -+ * -+ * The function returns 0 if the @timeout elapsed, and the remaining -+ * jiffies if the condition evaluated to true before the timeout elapsed. -+ */ -+#define swait_event_timeout(wq, condition, timeout) \ -+({ \ -+ long __ret = timeout; \ -+ if (!(condition)) \ -+ __swait_event_timeout(wq, condition, __ret); \ -+ __ret; \ -+}) -+ -+#endif -diff -Nur linux-3.18.14.orig/include/linux/work-simple.h linux-3.18.14-rt/include/linux/work-simple.h ---- linux-3.18.14.orig/include/linux/work-simple.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/linux/work-simple.h 2015-05-31 15:32:48.481635367 -0500 -@@ -0,0 +1,24 @@ -+#ifndef _LINUX_SWORK_H -+#define _LINUX_SWORK_H -+ -+#include -+ -+struct swork_event { -+ struct list_head item; -+ unsigned long flags; -+ void (*func)(struct swork_event *); -+}; -+ -+static inline void INIT_SWORK(struct swork_event *event, -+ void (*func)(struct swork_event *)) -+{ -+ event->flags = 0; -+ event->func = func; -+} -+ -+bool swork_queue(struct swork_event *sev); -+ -+int swork_get(void); -+void swork_put(void); -+ -+#endif /* _LINUX_SWORK_H */ -diff -Nur linux-3.18.14.orig/include/net/dst.h linux-3.18.14-rt/include/net/dst.h ---- linux-3.18.14.orig/include/net/dst.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/net/dst.h 2015-05-31 15:32:48.497635366 -0500 -@@ -403,7 +403,7 @@ - static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n, - struct sk_buff *skb) - { -- const struct hh_cache *hh; -+ struct hh_cache *hh; - - if (dst->pending_confirm) { - unsigned long now = jiffies; -diff -Nur linux-3.18.14.orig/include/net/neighbour.h linux-3.18.14-rt/include/net/neighbour.h ---- linux-3.18.14.orig/include/net/neighbour.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/net/neighbour.h 2015-05-31 15:32:48.521635366 -0500 -@@ -387,7 +387,7 @@ - } - #endif - --static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb) -+static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb) - { - unsigned int seq; - int hh_len; -@@ -442,7 +442,7 @@ - - #define NEIGH_CB(skb) ((struct neighbour_cb *)(skb)->cb) - --static inline void neigh_ha_snapshot(char *dst, const struct neighbour *n, -+static inline void neigh_ha_snapshot(char *dst, struct neighbour *n, - const struct net_device *dev) - { - unsigned int seq; -diff -Nur linux-3.18.14.orig/include/net/netns/ipv4.h linux-3.18.14-rt/include/net/netns/ipv4.h ---- linux-3.18.14.orig/include/net/netns/ipv4.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/include/net/netns/ipv4.h 2015-05-31 15:32:48.521635366 -0500 -@@ -67,6 +67,7 @@ - - int sysctl_icmp_echo_ignore_all; - int sysctl_icmp_echo_ignore_broadcasts; -+ int sysctl_icmp_echo_sysrq; - int sysctl_icmp_ignore_bogus_error_responses; - int sysctl_icmp_ratelimit; - int sysctl_icmp_ratemask; -diff -Nur linux-3.18.14.orig/include/trace/events/hist.h linux-3.18.14-rt/include/trace/events/hist.h ---- linux-3.18.14.orig/include/trace/events/hist.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/trace/events/hist.h 2015-05-31 15:32:48.521635366 -0500 -@@ -0,0 +1,72 @@ -+#undef TRACE_SYSTEM -+#define TRACE_SYSTEM hist -+ -+#if !defined(_TRACE_HIST_H) || defined(TRACE_HEADER_MULTI_READ) -+#define _TRACE_HIST_H -+ -+#include "latency_hist.h" -+#include -+ -+#if !defined(CONFIG_PREEMPT_OFF_HIST) && !defined(CONFIG_INTERRUPT_OFF_HIST) -+#define trace_preemptirqsoff_hist(a, b) -+#else -+TRACE_EVENT(preemptirqsoff_hist, -+ -+ TP_PROTO(int reason, int starthist), -+ -+ TP_ARGS(reason, starthist), -+ -+ TP_STRUCT__entry( -+ __field(int, reason) -+ __field(int, starthist) -+ ), -+ -+ TP_fast_assign( -+ __entry->reason = reason; -+ __entry->starthist = starthist; -+ ), -+ -+ TP_printk("reason=%s starthist=%s", getaction(__entry->reason), -+ __entry->starthist ? "start" : "stop") -+); -+#endif -+ -+#ifndef CONFIG_MISSED_TIMER_OFFSETS_HIST -+#define trace_hrtimer_interrupt(a, b, c, d) -+#else -+TRACE_EVENT(hrtimer_interrupt, -+ -+ TP_PROTO(int cpu, long long offset, struct task_struct *curr, -+ struct task_struct *task), -+ -+ TP_ARGS(cpu, offset, curr, task), -+ -+ TP_STRUCT__entry( -+ __field(int, cpu) -+ __field(long long, offset) -+ __array(char, ccomm, TASK_COMM_LEN) -+ __field(int, cprio) -+ __array(char, tcomm, TASK_COMM_LEN) -+ __field(int, tprio) -+ ), -+ -+ TP_fast_assign( -+ __entry->cpu = cpu; -+ __entry->offset = offset; -+ memcpy(__entry->ccomm, curr->comm, TASK_COMM_LEN); -+ __entry->cprio = curr->prio; -+ memcpy(__entry->tcomm, task != NULL ? task->comm : "", -+ task != NULL ? TASK_COMM_LEN : 7); -+ __entry->tprio = task != NULL ? task->prio : -1; -+ ), -+ -+ TP_printk("cpu=%d offset=%lld curr=%s[%d] thread=%s[%d]", -+ __entry->cpu, __entry->offset, __entry->ccomm, -+ __entry->cprio, __entry->tcomm, __entry->tprio) -+); -+#endif -+ -+#endif /* _TRACE_HIST_H */ -+ -+/* This part must be outside protection */ -+#include -diff -Nur linux-3.18.14.orig/include/trace/events/latency_hist.h linux-3.18.14-rt/include/trace/events/latency_hist.h ---- linux-3.18.14.orig/include/trace/events/latency_hist.h 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/include/trace/events/latency_hist.h 2015-05-31 15:32:48.521635366 -0500 -@@ -0,0 +1,29 @@ -+#ifndef _LATENCY_HIST_H -+#define _LATENCY_HIST_H -+ -+enum hist_action { -+ IRQS_ON, -+ PREEMPT_ON, -+ TRACE_STOP, -+ IRQS_OFF, -+ PREEMPT_OFF, -+ TRACE_START, -+}; -+ -+static char *actions[] = { -+ "IRQS_ON", -+ "PREEMPT_ON", -+ "TRACE_STOP", -+ "IRQS_OFF", -+ "PREEMPT_OFF", -+ "TRACE_START", -+}; -+ -+static inline char *getaction(int action) -+{ -+ if (action >= 0 && action <= sizeof(actions)/sizeof(actions[0])) -+ return actions[action]; -+ return "unknown"; -+} -+ -+#endif /* _LATENCY_HIST_H */ -diff -Nur linux-3.18.14.orig/init/Kconfig linux-3.18.14-rt/init/Kconfig ---- linux-3.18.14.orig/init/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/init/Kconfig 2015-05-31 15:32:48.525635366 -0500 -@@ -635,7 +635,7 @@ - - config RCU_FAST_NO_HZ - bool "Accelerate last non-dyntick-idle CPU's grace periods" -- depends on NO_HZ_COMMON && SMP -+ depends on NO_HZ_COMMON && SMP && !PREEMPT_RT_FULL - default n - help - This option permits CPUs to enter dynticks-idle state even if -@@ -662,7 +662,7 @@ - config RCU_BOOST - bool "Enable RCU priority boosting" - depends on RT_MUTEXES && PREEMPT_RCU -- default n -+ default y if PREEMPT_RT_FULL - help - This option boosts the priority of preempted RCU readers that - block the current preemptible RCU grace period for too long. -@@ -1106,6 +1106,7 @@ - config RT_GROUP_SCHED - bool "Group scheduling for SCHED_RR/FIFO" - depends on CGROUP_SCHED -+ depends on !PREEMPT_RT_FULL - default n - help - This feature lets you explicitly allocate real CPU bandwidth -@@ -1677,6 +1678,7 @@ - - config SLAB - bool "SLAB" -+ depends on !PREEMPT_RT_FULL - help - The regular slab allocator that is established and known to work - well in all environments. It organizes cache hot objects in -@@ -1695,6 +1697,7 @@ - config SLOB - depends on EXPERT - bool "SLOB (Simple Allocator)" -+ depends on !PREEMPT_RT_FULL - help - SLOB replaces the stock allocator with a drastically simpler - allocator. SLOB is generally more space efficient but -diff -Nur linux-3.18.14.orig/init/main.c linux-3.18.14-rt/init/main.c ---- linux-3.18.14.orig/init/main.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/init/main.c 2015-05-31 15:32:48.545635366 -0500 -@@ -533,6 +533,7 @@ - setup_command_line(command_line); - setup_nr_cpu_ids(); - setup_per_cpu_areas(); -+ softirq_early_init(); - smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ - - build_all_zonelists(NULL, NULL); -diff -Nur linux-3.18.14.orig/init/Makefile linux-3.18.14-rt/init/Makefile ---- linux-3.18.14.orig/init/Makefile 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/init/Makefile 2015-05-31 15:32:48.525635366 -0500 -@@ -33,4 +33,4 @@ - include/generated/compile.h: FORCE - @$($(quiet)chk_compile.h) - $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkcompile_h $@ \ -- "$(UTS_MACHINE)" "$(CONFIG_SMP)" "$(CONFIG_PREEMPT)" "$(CC) $(KBUILD_CFLAGS)" -+ "$(UTS_MACHINE)" "$(CONFIG_SMP)" "$(CONFIG_PREEMPT)" "$(CONFIG_PREEMPT_RT_FULL)" "$(CC) $(KBUILD_CFLAGS)" -diff -Nur linux-3.18.14.orig/ipc/mqueue.c linux-3.18.14-rt/ipc/mqueue.c ---- linux-3.18.14.orig/ipc/mqueue.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/ipc/mqueue.c 2015-05-31 15:32:48.557635366 -0500 -@@ -923,12 +923,17 @@ - struct msg_msg *message, - struct ext_wait_queue *receiver) - { -+ /* -+ * Keep them in one critical section for PREEMPT_RT: -+ */ -+ preempt_disable_rt(); - receiver->msg = message; - list_del(&receiver->list); - receiver->state = STATE_PENDING; - wake_up_process(receiver->task); - smp_wmb(); - receiver->state = STATE_READY; -+ preempt_enable_rt(); - } - - /* pipelined_receive() - if there is task waiting in sys_mq_timedsend() -@@ -942,13 +947,18 @@ - wake_up_interruptible(&info->wait_q); - return; - } -- if (msg_insert(sender->msg, info)) -- return; -- list_del(&sender->list); -- sender->state = STATE_PENDING; -- wake_up_process(sender->task); -- smp_wmb(); -- sender->state = STATE_READY; -+ /* -+ * Keep them in one critical section for PREEMPT_RT: -+ */ -+ preempt_disable_rt(); -+ if (!msg_insert(sender->msg, info)) { -+ list_del(&sender->list); -+ sender->state = STATE_PENDING; -+ wake_up_process(sender->task); -+ smp_wmb(); -+ sender->state = STATE_READY; -+ } -+ preempt_enable_rt(); - } - - SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, -diff -Nur linux-3.18.14.orig/ipc/msg.c linux-3.18.14-rt/ipc/msg.c ---- linux-3.18.14.orig/ipc/msg.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/ipc/msg.c 2015-05-31 15:32:48.577635366 -0500 -@@ -188,6 +188,12 @@ - struct msg_receiver *msr, *t; - - list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) { -+ /* -+ * Make sure that the wakeup doesnt preempt -+ * this CPU prematurely. (on PREEMPT_RT) -+ */ -+ preempt_disable_rt(); -+ - msr->r_msg = NULL; /* initialize expunge ordering */ - wake_up_process(msr->r_tsk); - /* -@@ -198,6 +204,8 @@ - */ - smp_mb(); - msr->r_msg = ERR_PTR(res); -+ -+ preempt_enable_rt(); - } - } - -@@ -574,6 +582,11 @@ - if (testmsg(msg, msr->r_msgtype, msr->r_mode) && - !security_msg_queue_msgrcv(msq, msg, msr->r_tsk, - msr->r_msgtype, msr->r_mode)) { -+ /* -+ * Make sure that the wakeup doesnt preempt -+ * this CPU prematurely. (on PREEMPT_RT) -+ */ -+ preempt_disable_rt(); - - list_del(&msr->r_list); - if (msr->r_maxsize < msg->m_ts) { -@@ -595,12 +608,13 @@ - */ - smp_mb(); - msr->r_msg = msg; -+ preempt_enable_rt(); - - return 1; - } -+ preempt_enable_rt(); - } - } -- - return 0; - } - -diff -Nur linux-3.18.14.orig/ipc/sem.c linux-3.18.14-rt/ipc/sem.c ---- linux-3.18.14.orig/ipc/sem.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/ipc/sem.c 2015-05-31 15:32:48.577635366 -0500 -@@ -673,6 +673,13 @@ - static void wake_up_sem_queue_prepare(struct list_head *pt, - struct sem_queue *q, int error) - { -+#ifdef CONFIG_PREEMPT_RT_BASE -+ struct task_struct *p = q->sleeper; -+ get_task_struct(p); -+ q->status = error; -+ wake_up_process(p); -+ put_task_struct(p); -+#else - if (list_empty(pt)) { - /* - * Hold preempt off so that we don't get preempted and have the -@@ -684,6 +691,7 @@ - q->pid = error; - - list_add_tail(&q->list, pt); -+#endif - } - - /** -@@ -697,6 +705,7 @@ - */ - static void wake_up_sem_queue_do(struct list_head *pt) - { -+#ifndef CONFIG_PREEMPT_RT_BASE - struct sem_queue *q, *t; - int did_something; - -@@ -709,6 +718,7 @@ - } - if (did_something) - preempt_enable(); -+#endif - } - - static void unlink_queue(struct sem_array *sma, struct sem_queue *q) -diff -Nur linux-3.18.14.orig/kernel/cgroup.c linux-3.18.14-rt/kernel/cgroup.c ---- linux-3.18.14.orig/kernel/cgroup.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/cgroup.c 2015-05-31 15:32:48.597635365 -0500 -@@ -4355,10 +4355,10 @@ - queue_work(cgroup_destroy_wq, &css->destroy_work); - } - --static void css_release_work_fn(struct work_struct *work) -+static void css_release_work_fn(struct swork_event *sev) - { - struct cgroup_subsys_state *css = -- container_of(work, struct cgroup_subsys_state, destroy_work); -+ container_of(sev, struct cgroup_subsys_state, destroy_swork); - struct cgroup_subsys *ss = css->ss; - struct cgroup *cgrp = css->cgroup; - -@@ -4395,8 +4395,8 @@ - struct cgroup_subsys_state *css = - container_of(ref, struct cgroup_subsys_state, refcnt); - -- INIT_WORK(&css->destroy_work, css_release_work_fn); -- queue_work(cgroup_destroy_wq, &css->destroy_work); -+ INIT_SWORK(&css->destroy_swork, css_release_work_fn); -+ swork_queue(&css->destroy_swork); - } - - static void init_and_link_css(struct cgroup_subsys_state *css, -@@ -4997,6 +4997,7 @@ - */ - cgroup_destroy_wq = alloc_workqueue("cgroup_destroy", 0, 1); - BUG_ON(!cgroup_destroy_wq); -+ BUG_ON(swork_get()); - - /* - * Used to destroy pidlists and separate to serve as flush domain. -diff -Nur linux-3.18.14.orig/kernel/cpu.c linux-3.18.14-rt/kernel/cpu.c ---- linux-3.18.14.orig/kernel/cpu.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/cpu.c 2015-05-31 15:32:48.601635365 -0500 -@@ -86,6 +86,290 @@ - #define cpuhp_lock_acquire() lock_map_acquire(&cpu_hotplug.dep_map) - #define cpuhp_lock_release() lock_map_release(&cpu_hotplug.dep_map) - -+/** -+ * hotplug_pcp - per cpu hotplug descriptor -+ * @unplug: set when pin_current_cpu() needs to sync tasks -+ * @sync_tsk: the task that waits for tasks to finish pinned sections -+ * @refcount: counter of tasks in pinned sections -+ * @grab_lock: set when the tasks entering pinned sections should wait -+ * @synced: notifier for @sync_tsk to tell cpu_down it's finished -+ * @mutex: the mutex to make tasks wait (used when @grab_lock is true) -+ * @mutex_init: zero if the mutex hasn't been initialized yet. -+ * -+ * Although @unplug and @sync_tsk may point to the same task, the @unplug -+ * is used as a flag and still exists after @sync_tsk has exited and -+ * @sync_tsk set to NULL. -+ */ -+struct hotplug_pcp { -+ struct task_struct *unplug; -+ struct task_struct *sync_tsk; -+ int refcount; -+ int grab_lock; -+ struct completion synced; -+ struct completion unplug_wait; -+#ifdef CONFIG_PREEMPT_RT_FULL -+ /* -+ * Note, on PREEMPT_RT, the hotplug lock must save the state of -+ * the task, otherwise the mutex will cause the task to fail -+ * to sleep when required. (Because it's called from migrate_disable()) -+ * -+ * The spinlock_t on PREEMPT_RT is a mutex that saves the task's -+ * state. -+ */ -+ spinlock_t lock; -+#else -+ struct mutex mutex; -+#endif -+ int mutex_init; -+}; -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+# define hotplug_lock(hp) rt_spin_lock(&(hp)->lock) -+# define hotplug_unlock(hp) rt_spin_unlock(&(hp)->lock) -+#else -+# define hotplug_lock(hp) mutex_lock(&(hp)->mutex) -+# define hotplug_unlock(hp) mutex_unlock(&(hp)->mutex) -+#endif -+ -+static DEFINE_PER_CPU(struct hotplug_pcp, hotplug_pcp); -+ -+/** -+ * pin_current_cpu - Prevent the current cpu from being unplugged -+ * -+ * Lightweight version of get_online_cpus() to prevent cpu from being -+ * unplugged when code runs in a migration disabled region. -+ * -+ * Must be called with preemption disabled (preempt_count = 1)! -+ */ -+void pin_current_cpu(void) -+{ -+ struct hotplug_pcp *hp; -+ int force = 0; -+ -+retry: -+ hp = &__get_cpu_var(hotplug_pcp); -+ -+ if (!hp->unplug || hp->refcount || force || preempt_count() > 1 || -+ hp->unplug == current) { -+ hp->refcount++; -+ return; -+ } -+ if (hp->grab_lock) { -+ preempt_enable(); -+ hotplug_lock(hp); -+ hotplug_unlock(hp); -+ } else { -+ preempt_enable(); -+ /* -+ * Try to push this task off of this CPU. -+ */ -+ if (!migrate_me()) { -+ preempt_disable(); -+ hp = &__get_cpu_var(hotplug_pcp); -+ if (!hp->grab_lock) { -+ /* -+ * Just let it continue it's already pinned -+ * or about to sleep. -+ */ -+ force = 1; -+ goto retry; -+ } -+ preempt_enable(); -+ } -+ } -+ preempt_disable(); -+ goto retry; -+} -+ -+/** -+ * unpin_current_cpu - Allow unplug of current cpu -+ * -+ * Must be called with preemption or interrupts disabled! -+ */ -+void unpin_current_cpu(void) -+{ -+ struct hotplug_pcp *hp = &__get_cpu_var(hotplug_pcp); -+ -+ WARN_ON(hp->refcount <= 0); -+ -+ /* This is safe. sync_unplug_thread is pinned to this cpu */ -+ if (!--hp->refcount && hp->unplug && hp->unplug != current) -+ wake_up_process(hp->unplug); -+} -+ -+static void wait_for_pinned_cpus(struct hotplug_pcp *hp) -+{ -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ while (hp->refcount) { -+ schedule_preempt_disabled(); -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ } -+} -+ -+static int sync_unplug_thread(void *data) -+{ -+ struct hotplug_pcp *hp = data; -+ -+ wait_for_completion(&hp->unplug_wait); -+ preempt_disable(); -+ hp->unplug = current; -+ wait_for_pinned_cpus(hp); -+ -+ /* -+ * This thread will synchronize the cpu_down() with threads -+ * that have pinned the CPU. When the pinned CPU count reaches -+ * zero, we inform the cpu_down code to continue to the next step. -+ */ -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ preempt_enable(); -+ complete(&hp->synced); -+ -+ /* -+ * If all succeeds, the next step will need tasks to wait till -+ * the CPU is offline before continuing. To do this, the grab_lock -+ * is set and tasks going into pin_current_cpu() will block on the -+ * mutex. But we still need to wait for those that are already in -+ * pinned CPU sections. If the cpu_down() failed, the kthread_should_stop() -+ * will kick this thread out. -+ */ -+ while (!hp->grab_lock && !kthread_should_stop()) { -+ schedule(); -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ } -+ -+ /* Make sure grab_lock is seen before we see a stale completion */ -+ smp_mb(); -+ -+ /* -+ * Now just before cpu_down() enters stop machine, we need to make -+ * sure all tasks that are in pinned CPU sections are out, and new -+ * tasks will now grab the lock, keeping them from entering pinned -+ * CPU sections. -+ */ -+ if (!kthread_should_stop()) { -+ preempt_disable(); -+ wait_for_pinned_cpus(hp); -+ preempt_enable(); -+ complete(&hp->synced); -+ } -+ -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ while (!kthread_should_stop()) { -+ schedule(); -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ } -+ set_current_state(TASK_RUNNING); -+ -+ /* -+ * Force this thread off this CPU as it's going down and -+ * we don't want any more work on this CPU. -+ */ -+ current->flags &= ~PF_NO_SETAFFINITY; -+ set_cpus_allowed_ptr(current, cpu_present_mask); -+ migrate_me(); -+ return 0; -+} -+ -+static void __cpu_unplug_sync(struct hotplug_pcp *hp) -+{ -+ wake_up_process(hp->sync_tsk); -+ wait_for_completion(&hp->synced); -+} -+ -+static void __cpu_unplug_wait(unsigned int cpu) -+{ -+ struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu); -+ -+ complete(&hp->unplug_wait); -+ wait_for_completion(&hp->synced); -+} -+ -+/* -+ * Start the sync_unplug_thread on the target cpu and wait for it to -+ * complete. -+ */ -+static int cpu_unplug_begin(unsigned int cpu) -+{ -+ struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu); -+ int err; -+ -+ /* Protected by cpu_hotplug.lock */ -+ if (!hp->mutex_init) { -+#ifdef CONFIG_PREEMPT_RT_FULL -+ spin_lock_init(&hp->lock); -+#else -+ mutex_init(&hp->mutex); -+#endif -+ hp->mutex_init = 1; -+ } -+ -+ /* Inform the scheduler to migrate tasks off this CPU */ -+ tell_sched_cpu_down_begin(cpu); -+ -+ init_completion(&hp->synced); -+ init_completion(&hp->unplug_wait); -+ -+ hp->sync_tsk = kthread_create(sync_unplug_thread, hp, "sync_unplug/%d", cpu); -+ if (IS_ERR(hp->sync_tsk)) { -+ err = PTR_ERR(hp->sync_tsk); -+ hp->sync_tsk = NULL; -+ return err; -+ } -+ kthread_bind(hp->sync_tsk, cpu); -+ -+ /* -+ * Wait for tasks to get out of the pinned sections, -+ * it's still OK if new tasks enter. Some CPU notifiers will -+ * wait for tasks that are going to enter these sections and -+ * we must not have them block. -+ */ -+ wake_up_process(hp->sync_tsk); -+ return 0; -+} -+ -+static void cpu_unplug_sync(unsigned int cpu) -+{ -+ struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu); -+ -+ init_completion(&hp->synced); -+ /* The completion needs to be initialzied before setting grab_lock */ -+ smp_wmb(); -+ -+ /* Grab the mutex before setting grab_lock */ -+ hotplug_lock(hp); -+ hp->grab_lock = 1; -+ -+ /* -+ * The CPU notifiers have been completed. -+ * Wait for tasks to get out of pinned CPU sections and have new -+ * tasks block until the CPU is completely down. -+ */ -+ __cpu_unplug_sync(hp); -+ -+ /* All done with the sync thread */ -+ kthread_stop(hp->sync_tsk); -+ hp->sync_tsk = NULL; -+} -+ -+static void cpu_unplug_done(unsigned int cpu) -+{ -+ struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu); -+ -+ hp->unplug = NULL; -+ /* Let all tasks know cpu unplug is finished before cleaning up */ -+ smp_wmb(); -+ -+ if (hp->sync_tsk) -+ kthread_stop(hp->sync_tsk); -+ -+ if (hp->grab_lock) { -+ hotplug_unlock(hp); -+ /* protected by cpu_hotplug.lock */ -+ hp->grab_lock = 0; -+ } -+ tell_sched_cpu_down_done(cpu); -+} -+ - void get_online_cpus(void) - { - might_sleep(); -@@ -102,6 +386,7 @@ - { - if (cpu_hotplug.active_writer == current) - return true; -+ - if (!mutex_trylock(&cpu_hotplug.lock)) - return false; - cpuhp_lock_acquire_tryread(); -@@ -349,13 +634,15 @@ - /* Requires cpu_add_remove_lock to be held */ - static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) - { -- int err, nr_calls = 0; -+ int mycpu, err, nr_calls = 0; - void *hcpu = (void *)(long)cpu; - unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0; - struct take_cpu_down_param tcd_param = { - .mod = mod, - .hcpu = hcpu, - }; -+ cpumask_var_t cpumask; -+ cpumask_var_t cpumask_org; - - if (num_online_cpus() == 1) - return -EBUSY; -@@ -363,7 +650,34 @@ - if (!cpu_online(cpu)) - return -EINVAL; - -+ /* Move the downtaker off the unplug cpu */ -+ if (!alloc_cpumask_var(&cpumask, GFP_KERNEL)) -+ return -ENOMEM; -+ if (!alloc_cpumask_var(&cpumask_org, GFP_KERNEL)) { -+ free_cpumask_var(cpumask); -+ return -ENOMEM; -+ } -+ -+ cpumask_copy(cpumask_org, tsk_cpus_allowed(current)); -+ cpumask_andnot(cpumask, cpu_online_mask, cpumask_of(cpu)); -+ set_cpus_allowed_ptr(current, cpumask); -+ free_cpumask_var(cpumask); -+ migrate_disable(); -+ mycpu = smp_processor_id(); -+ if (mycpu == cpu) { -+ printk(KERN_ERR "Yuck! Still on unplug CPU\n!"); -+ migrate_enable(); -+ err = -EBUSY; -+ goto restore_cpus; -+ } -+ migrate_enable(); -+ - cpu_hotplug_begin(); -+ err = cpu_unplug_begin(cpu); -+ if (err) { -+ printk("cpu_unplug_begin(%d) failed\n", cpu); -+ goto out_cancel; -+ } - - err = __cpu_notify(CPU_DOWN_PREPARE | mod, hcpu, -1, &nr_calls); - if (err) { -@@ -389,8 +703,12 @@ - #endif - synchronize_rcu(); - -+ __cpu_unplug_wait(cpu); - smpboot_park_threads(cpu); - -+ /* Notifiers are done. Don't let any more tasks pin this CPU. */ -+ cpu_unplug_sync(cpu); -+ - /* - * So now all preempt/rcu users must observe !cpu_active(). - */ -@@ -423,9 +741,14 @@ - check_for_tasks(cpu); - - out_release: -+ cpu_unplug_done(cpu); -+out_cancel: - cpu_hotplug_done(); - if (!err) - cpu_notify_nofail(CPU_POST_DEAD | mod, hcpu); -+restore_cpus: -+ set_cpus_allowed_ptr(current, cpumask_org); -+ free_cpumask_var(cpumask_org); - return err; - } - -diff -Nur linux-3.18.14.orig/kernel/debug/kdb/kdb_io.c linux-3.18.14-rt/kernel/debug/kdb/kdb_io.c ---- linux-3.18.14.orig/kernel/debug/kdb/kdb_io.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/debug/kdb/kdb_io.c 2015-05-31 15:32:48.605635366 -0500 -@@ -554,7 +554,6 @@ - int linecount; - int colcount; - int logging, saved_loglevel = 0; -- int saved_trap_printk; - int got_printf_lock = 0; - int retlen = 0; - int fnd, len; -@@ -565,8 +564,6 @@ - unsigned long uninitialized_var(flags); - - preempt_disable(); -- saved_trap_printk = kdb_trap_printk; -- kdb_trap_printk = 0; - - /* Serialize kdb_printf if multiple cpus try to write at once. - * But if any cpu goes recursive in kdb, just print the output, -@@ -833,7 +830,6 @@ - } else { - __release(kdb_printf_lock); - } -- kdb_trap_printk = saved_trap_printk; - preempt_enable(); - return retlen; - } -@@ -843,9 +839,11 @@ - va_list ap; - int r; - -+ kdb_trap_printk++; - va_start(ap, fmt); - r = vkdb_printf(fmt, ap); - va_end(ap); -+ kdb_trap_printk--; - - return r; - } -diff -Nur linux-3.18.14.orig/kernel/events/core.c linux-3.18.14-rt/kernel/events/core.c ---- linux-3.18.14.orig/kernel/events/core.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/events/core.c 2015-05-31 15:32:48.637635365 -0500 -@@ -6346,6 +6346,7 @@ - - hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hwc->hrtimer.function = perf_swevent_hrtimer; -+ hwc->hrtimer.irqsafe = 1; - - /* - * Since hrtimers have a fixed rate, we can do a static freq->period -diff -Nur linux-3.18.14.orig/kernel/exit.c linux-3.18.14-rt/kernel/exit.c ---- linux-3.18.14.orig/kernel/exit.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/exit.c 2015-05-31 15:32:48.649635365 -0500 -@@ -147,7 +147,7 @@ - * Do this under ->siglock, we can race with another thread - * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals. - */ -- flush_sigqueue(&tsk->pending); -+ flush_task_sigqueue(tsk); - tsk->sighand = NULL; - spin_unlock(&sighand->siglock); - -diff -Nur linux-3.18.14.orig/kernel/fork.c linux-3.18.14-rt/kernel/fork.c ---- linux-3.18.14.orig/kernel/fork.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/fork.c 2015-05-31 15:32:48.657635365 -0500 -@@ -97,7 +97,7 @@ - - DEFINE_PER_CPU(unsigned long, process_counts) = 0; - --__cacheline_aligned DEFINE_RWLOCK(tasklist_lock); /* outer */ -+DEFINE_RWLOCK(tasklist_lock); /* outer */ - - #ifdef CONFIG_PROVE_RCU - int lockdep_tasklist_lock_is_held(void) -@@ -233,7 +233,9 @@ - if (atomic_dec_and_test(&sig->sigcnt)) - free_signal_struct(sig); - } -- -+#ifdef CONFIG_PREEMPT_RT_BASE -+static -+#endif - void __put_task_struct(struct task_struct *tsk) - { - WARN_ON(!tsk->exit_state); -@@ -249,7 +251,18 @@ - if (!profile_handoff_task(tsk)) - free_task(tsk); - } -+#ifndef CONFIG_PREEMPT_RT_BASE - EXPORT_SYMBOL_GPL(__put_task_struct); -+#else -+void __put_task_struct_cb(struct rcu_head *rhp) -+{ -+ struct task_struct *tsk = container_of(rhp, struct task_struct, put_rcu); -+ -+ __put_task_struct(tsk); -+ -+} -+EXPORT_SYMBOL_GPL(__put_task_struct_cb); -+#endif - - void __init __weak arch_task_cache_init(void) { } - -@@ -643,6 +656,19 @@ - } - EXPORT_SYMBOL_GPL(__mmdrop); - -+#ifdef CONFIG_PREEMPT_RT_BASE -+/* -+ * RCU callback for delayed mm drop. Not strictly rcu, but we don't -+ * want another facility to make this work. -+ */ -+void __mmdrop_delayed(struct rcu_head *rhp) -+{ -+ struct mm_struct *mm = container_of(rhp, struct mm_struct, delayed_drop); -+ -+ __mmdrop(mm); -+} -+#endif -+ - /* - * Decrement the use count and release all resources for an mm. - */ -@@ -1157,6 +1183,9 @@ - */ - static void posix_cpu_timers_init(struct task_struct *tsk) - { -+#ifdef CONFIG_PREEMPT_RT_BASE -+ tsk->posix_timer_list = NULL; -+#endif - tsk->cputime_expires.prof_exp = 0; - tsk->cputime_expires.virt_exp = 0; - tsk->cputime_expires.sched_exp = 0; -@@ -1284,6 +1313,7 @@ - spin_lock_init(&p->alloc_lock); - - init_sigpending(&p->pending); -+ p->sigqueue_cache = NULL; - - p->utime = p->stime = p->gtime = 0; - p->utimescaled = p->stimescaled = 0; -@@ -1291,7 +1321,8 @@ - p->prev_cputime.utime = p->prev_cputime.stime = 0; - #endif - #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN -- seqlock_init(&p->vtime_seqlock); -+ raw_spin_lock_init(&p->vtime_lock); -+ seqcount_init(&p->vtime_seq); - p->vtime_snap = 0; - p->vtime_snap_whence = VTIME_SLEEPING; - #endif -@@ -1342,6 +1373,9 @@ - p->hardirq_context = 0; - p->softirq_context = 0; - #endif -+#ifdef CONFIG_PREEMPT_RT_FULL -+ p->pagefault_disabled = 0; -+#endif - #ifdef CONFIG_LOCKDEP - p->lockdep_depth = 0; /* no locks held yet */ - p->curr_chain_key = 0; -diff -Nur linux-3.18.14.orig/kernel/futex.c linux-3.18.14-rt/kernel/futex.c ---- linux-3.18.14.orig/kernel/futex.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/futex.c 2015-05-31 15:32:48.665635365 -0500 -@@ -738,7 +738,9 @@ - * task still owns the PI-state: - */ - if (head->next != next) { -+ raw_spin_unlock_irq(&curr->pi_lock); - spin_unlock(&hb->lock); -+ raw_spin_lock_irq(&curr->pi_lock); - continue; - } - -@@ -1705,6 +1707,16 @@ - requeue_pi_wake_futex(this, &key2, hb2); - drop_count++; - continue; -+ } else if (ret == -EAGAIN) { -+ /* -+ * Waiter was woken by timeout or -+ * signal and has set pi_blocked_on to -+ * PI_WAKEUP_INPROGRESS before we -+ * tried to enqueue it on the rtmutex. -+ */ -+ this->pi_state = NULL; -+ free_pi_state(pi_state); -+ continue; - } else if (ret) { - /* -EDEADLK */ - this->pi_state = NULL; -@@ -2549,7 +2561,7 @@ - struct hrtimer_sleeper timeout, *to = NULL; - struct rt_mutex_waiter rt_waiter; - struct rt_mutex *pi_mutex = NULL; -- struct futex_hash_bucket *hb; -+ struct futex_hash_bucket *hb, *hb2; - union futex_key key2 = FUTEX_KEY_INIT; - struct futex_q q = futex_q_init; - int res, ret; -@@ -2574,10 +2586,7 @@ - * The waiter is allocated on our stack, manipulated by the requeue - * code while we sleep on uaddr. - */ -- debug_rt_mutex_init_waiter(&rt_waiter); -- RB_CLEAR_NODE(&rt_waiter.pi_tree_entry); -- RB_CLEAR_NODE(&rt_waiter.tree_entry); -- rt_waiter.task = NULL; -+ rt_mutex_init_waiter(&rt_waiter, false); - - ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE); - if (unlikely(ret != 0)) -@@ -2608,20 +2617,55 @@ - /* Queue the futex_q, drop the hb lock, wait for wakeup. */ - futex_wait_queue_me(hb, &q, to); - -- spin_lock(&hb->lock); -- ret = handle_early_requeue_pi_wakeup(hb, &q, &key2, to); -- spin_unlock(&hb->lock); -- if (ret) -- goto out_put_keys; -+ /* -+ * On RT we must avoid races with requeue and trying to block -+ * on two mutexes (hb->lock and uaddr2's rtmutex) by -+ * serializing access to pi_blocked_on with pi_lock. -+ */ -+ raw_spin_lock_irq(¤t->pi_lock); -+ if (current->pi_blocked_on) { -+ /* -+ * We have been requeued or are in the process of -+ * being requeued. -+ */ -+ raw_spin_unlock_irq(¤t->pi_lock); -+ } else { -+ /* -+ * Setting pi_blocked_on to PI_WAKEUP_INPROGRESS -+ * prevents a concurrent requeue from moving us to the -+ * uaddr2 rtmutex. After that we can safely acquire -+ * (and possibly block on) hb->lock. -+ */ -+ current->pi_blocked_on = PI_WAKEUP_INPROGRESS; -+ raw_spin_unlock_irq(¤t->pi_lock); -+ -+ spin_lock(&hb->lock); -+ -+ /* -+ * Clean up pi_blocked_on. We might leak it otherwise -+ * when we succeeded with the hb->lock in the fast -+ * path. -+ */ -+ raw_spin_lock_irq(¤t->pi_lock); -+ current->pi_blocked_on = NULL; -+ raw_spin_unlock_irq(¤t->pi_lock); -+ -+ ret = handle_early_requeue_pi_wakeup(hb, &q, &key2, to); -+ spin_unlock(&hb->lock); -+ if (ret) -+ goto out_put_keys; -+ } - - /* -- * In order for us to be here, we know our q.key == key2, and since -- * we took the hb->lock above, we also know that futex_requeue() has -- * completed and we no longer have to concern ourselves with a wakeup -- * race with the atomic proxy lock acquisition by the requeue code. The -- * futex_requeue dropped our key1 reference and incremented our key2 -- * reference count. -+ * In order to be here, we have either been requeued, are in -+ * the process of being requeued, or requeue successfully -+ * acquired uaddr2 on our behalf. If pi_blocked_on was -+ * non-null above, we may be racing with a requeue. Do not -+ * rely on q->lock_ptr to be hb2->lock until after blocking on -+ * hb->lock or hb2->lock. The futex_requeue dropped our key1 -+ * reference and incremented our key2 reference count. - */ -+ hb2 = hash_futex(&key2); - - /* Check if the requeue code acquired the second futex for us. */ - if (!q.rt_waiter) { -@@ -2630,9 +2674,10 @@ - * did a lock-steal - fix up the PI-state in that case. - */ - if (q.pi_state && (q.pi_state->owner != current)) { -- spin_lock(q.lock_ptr); -+ spin_lock(&hb2->lock); -+ BUG_ON(&hb2->lock != q.lock_ptr); - ret = fixup_pi_state_owner(uaddr2, &q, current); -- spin_unlock(q.lock_ptr); -+ spin_unlock(&hb2->lock); - } - } else { - /* -@@ -2645,7 +2690,8 @@ - ret = rt_mutex_finish_proxy_lock(pi_mutex, to, &rt_waiter); - debug_rt_mutex_free_waiter(&rt_waiter); - -- spin_lock(q.lock_ptr); -+ spin_lock(&hb2->lock); -+ BUG_ON(&hb2->lock != q.lock_ptr); - /* - * Fixup the pi_state owner and possibly acquire the lock if we - * haven't already. -diff -Nur linux-3.18.14.orig/kernel/irq/handle.c linux-3.18.14-rt/kernel/irq/handle.c ---- linux-3.18.14.orig/kernel/irq/handle.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/irq/handle.c 2015-05-31 15:32:48.677635365 -0500 -@@ -133,6 +133,8 @@ - irqreturn_t - handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) - { -+ struct pt_regs *regs = get_irq_regs(); -+ u64 ip = regs ? instruction_pointer(regs) : 0; - irqreturn_t retval = IRQ_NONE; - unsigned int flags = 0, irq = desc->irq_data.irq; - -@@ -173,7 +175,11 @@ - action = action->next; - } while (action); - -- add_interrupt_randomness(irq, flags); -+#ifndef CONFIG_PREEMPT_RT_FULL -+ add_interrupt_randomness(irq, flags, ip); -+#else -+ desc->random_ip = ip; -+#endif - - if (!noirqdebug) - note_interrupt(irq, desc, retval); -diff -Nur linux-3.18.14.orig/kernel/irq/manage.c linux-3.18.14-rt/kernel/irq/manage.c ---- linux-3.18.14.orig/kernel/irq/manage.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/irq/manage.c 2015-05-31 15:32:48.697635365 -0500 -@@ -22,6 +22,7 @@ - #include "internals.h" - - #ifdef CONFIG_IRQ_FORCED_THREADING -+# ifndef CONFIG_PREEMPT_RT_BASE - __read_mostly bool force_irqthreads; - - static int __init setup_forced_irqthreads(char *arg) -@@ -30,6 +31,7 @@ - return 0; - } - early_param("threadirqs", setup_forced_irqthreads); -+# endif - #endif - - static void __synchronize_hardirq(struct irq_desc *desc) -@@ -173,6 +175,62 @@ - irq_get_pending(struct cpumask *mask, struct irq_desc *desc) { } - #endif - -+#ifdef CONFIG_PREEMPT_RT_FULL -+static void _irq_affinity_notify(struct irq_affinity_notify *notify); -+static struct task_struct *set_affinity_helper; -+static LIST_HEAD(affinity_list); -+static DEFINE_RAW_SPINLOCK(affinity_list_lock); -+ -+static int set_affinity_thread(void *unused) -+{ -+ while (1) { -+ struct irq_affinity_notify *notify; -+ int empty; -+ -+ set_current_state(TASK_INTERRUPTIBLE); -+ -+ raw_spin_lock_irq(&affinity_list_lock); -+ empty = list_empty(&affinity_list); -+ raw_spin_unlock_irq(&affinity_list_lock); -+ -+ if (empty) -+ schedule(); -+ if (kthread_should_stop()) -+ break; -+ set_current_state(TASK_RUNNING); -+try_next: -+ notify = NULL; -+ -+ raw_spin_lock_irq(&affinity_list_lock); -+ if (!list_empty(&affinity_list)) { -+ notify = list_first_entry(&affinity_list, -+ struct irq_affinity_notify, list); -+ list_del_init(¬ify->list); -+ } -+ raw_spin_unlock_irq(&affinity_list_lock); -+ -+ if (!notify) -+ continue; -+ _irq_affinity_notify(notify); -+ goto try_next; -+ } -+ return 0; -+} -+ -+static void init_helper_thread(void) -+{ -+ if (set_affinity_helper) -+ return; -+ set_affinity_helper = kthread_run(set_affinity_thread, NULL, -+ "affinity-cb"); -+ WARN_ON(IS_ERR(set_affinity_helper)); -+} -+#else -+ -+static inline void init_helper_thread(void) { } -+ -+#endif -+ - int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask, - bool force) - { -@@ -211,7 +269,17 @@ - - if (desc->affinity_notify) { - kref_get(&desc->affinity_notify->kref); -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ raw_spin_lock(&affinity_list_lock); -+ if (list_empty(&desc->affinity_notify->list)) -+ list_add_tail(&affinity_list, -+ &desc->affinity_notify->list); -+ raw_spin_unlock(&affinity_list_lock); -+ wake_up_process(set_affinity_helper); -+#else - schedule_work(&desc->affinity_notify->work); -+#endif - } - irqd_set(data, IRQD_AFFINITY_SET); - -@@ -246,10 +314,8 @@ - } - EXPORT_SYMBOL_GPL(irq_set_affinity_hint); - --static void irq_affinity_notify(struct work_struct *work) -+static void _irq_affinity_notify(struct irq_affinity_notify *notify) - { -- struct irq_affinity_notify *notify = -- container_of(work, struct irq_affinity_notify, work); - struct irq_desc *desc = irq_to_desc(notify->irq); - cpumask_var_t cpumask; - unsigned long flags; -@@ -271,6 +337,13 @@ - kref_put(¬ify->kref, notify->release); - } - -+static void irq_affinity_notify(struct work_struct *work) -+{ -+ struct irq_affinity_notify *notify = -+ container_of(work, struct irq_affinity_notify, work); -+ _irq_affinity_notify(notify); -+} -+ - /** - * irq_set_affinity_notifier - control notification of IRQ affinity changes - * @irq: Interrupt for which to enable/disable notification -@@ -300,6 +373,8 @@ - notify->irq = irq; - kref_init(¬ify->kref); - INIT_WORK(¬ify->work, irq_affinity_notify); -+ INIT_LIST_HEAD(¬ify->list); -+ init_helper_thread(); - } - - raw_spin_lock_irqsave(&desc->lock, flags); -@@ -788,7 +863,15 @@ - local_bh_disable(); - ret = action->thread_fn(action->irq, action->dev_id); - irq_finalize_oneshot(desc, action); -- local_bh_enable(); -+ /* -+ * Interrupts which have real time requirements can be set up -+ * to avoid softirq processing in the thread handler. This is -+ * safe as these interrupts do not raise soft interrupts. -+ */ -+ if (irq_settings_no_softirq_call(desc)) -+ _local_bh_enable(); -+ else -+ local_bh_enable(); - return ret; - } - -@@ -871,6 +954,12 @@ - if (action_ret == IRQ_HANDLED) - atomic_inc(&desc->threads_handled); - -+#ifdef CONFIG_PREEMPT_RT_FULL -+ migrate_disable(); -+ add_interrupt_randomness(action->irq, 0, -+ desc->random_ip ^ (unsigned long) action); -+ migrate_enable(); -+#endif - wake_threads_waitq(desc); - } - -@@ -1184,6 +1273,9 @@ - irqd_set(&desc->irq_data, IRQD_NO_BALANCING); - } - -+ if (new->flags & IRQF_NO_SOFTIRQ_CALL) -+ irq_settings_set_no_softirq_call(desc); -+ - /* Set default affinity mask once everything is setup */ - setup_affinity(irq, desc, mask); - -diff -Nur linux-3.18.14.orig/kernel/irq/settings.h linux-3.18.14-rt/kernel/irq/settings.h ---- linux-3.18.14.orig/kernel/irq/settings.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/irq/settings.h 2015-05-31 15:32:48.697635365 -0500 -@@ -15,6 +15,7 @@ - _IRQ_NESTED_THREAD = IRQ_NESTED_THREAD, - _IRQ_PER_CPU_DEVID = IRQ_PER_CPU_DEVID, - _IRQ_IS_POLLED = IRQ_IS_POLLED, -+ _IRQ_NO_SOFTIRQ_CALL = IRQ_NO_SOFTIRQ_CALL, - _IRQF_MODIFY_MASK = IRQF_MODIFY_MASK, - }; - -@@ -28,6 +29,7 @@ - #define IRQ_NESTED_THREAD GOT_YOU_MORON - #define IRQ_PER_CPU_DEVID GOT_YOU_MORON - #define IRQ_IS_POLLED GOT_YOU_MORON -+#define IRQ_NO_SOFTIRQ_CALL GOT_YOU_MORON - #undef IRQF_MODIFY_MASK - #define IRQF_MODIFY_MASK GOT_YOU_MORON - -@@ -38,6 +40,16 @@ - desc->status_use_accessors |= (set & _IRQF_MODIFY_MASK); - } - -+static inline bool irq_settings_no_softirq_call(struct irq_desc *desc) -+{ -+ return desc->status_use_accessors & _IRQ_NO_SOFTIRQ_CALL; -+} -+ -+static inline void irq_settings_set_no_softirq_call(struct irq_desc *desc) -+{ -+ desc->status_use_accessors |= _IRQ_NO_SOFTIRQ_CALL; -+} -+ - static inline bool irq_settings_is_per_cpu(struct irq_desc *desc) - { - return desc->status_use_accessors & _IRQ_PER_CPU; -diff -Nur linux-3.18.14.orig/kernel/irq/spurious.c linux-3.18.14-rt/kernel/irq/spurious.c ---- linux-3.18.14.orig/kernel/irq/spurious.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/irq/spurious.c 2015-05-31 15:32:48.709635364 -0500 -@@ -444,6 +444,10 @@ - - static int __init irqfixup_setup(char *str) - { -+#ifdef CONFIG_PREEMPT_RT_BASE -+ pr_warn("irqfixup boot option not supported w/ CONFIG_PREEMPT_RT_BASE\n"); -+ return 1; -+#endif - irqfixup = 1; - printk(KERN_WARNING "Misrouted IRQ fixup support enabled.\n"); - printk(KERN_WARNING "This may impact system performance.\n"); -@@ -456,6 +460,10 @@ - - static int __init irqpoll_setup(char *str) - { -+#ifdef CONFIG_PREEMPT_RT_BASE -+ pr_warn("irqpoll boot option not supported w/ CONFIG_PREEMPT_RT_BASE\n"); -+ return 1; -+#endif - irqfixup = 2; - printk(KERN_WARNING "Misrouted IRQ fixup and polling support " - "enabled\n"); -diff -Nur linux-3.18.14.orig/kernel/irq_work.c linux-3.18.14-rt/kernel/irq_work.c ---- linux-3.18.14.orig/kernel/irq_work.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/irq_work.c 2015-05-31 15:32:48.713635365 -0500 -@@ -17,6 +17,7 @@ - #include - #include - #include -+#include - #include - - -@@ -65,6 +66,8 @@ - */ - bool irq_work_queue_on(struct irq_work *work, int cpu) - { -+ struct llist_head *list; -+ - /* All work should have been flushed before going offline */ - WARN_ON_ONCE(cpu_is_offline(cpu)); - -@@ -75,7 +78,12 @@ - if (!irq_work_claim(work)) - return false; - -- if (llist_add(&work->llnode, &per_cpu(raised_list, cpu))) -+ if (IS_ENABLED(CONFIG_PREEMPT_RT_FULL) && !(work->flags & IRQ_WORK_HARD_IRQ)) -+ list = &per_cpu(lazy_list, cpu); -+ else -+ list = &per_cpu(raised_list, cpu); -+ -+ if (llist_add(&work->llnode, list)) - arch_send_call_function_single_ipi(cpu); - - return true; -@@ -86,6 +94,9 @@ - /* Enqueue the irq work @work on the current CPU */ - bool irq_work_queue(struct irq_work *work) - { -+ struct llist_head *list; -+ bool lazy_work, realtime = IS_ENABLED(CONFIG_PREEMPT_RT_FULL); -+ - /* Only queue if not already pending */ - if (!irq_work_claim(work)) - return false; -@@ -93,13 +104,15 @@ - /* Queue the entry and raise the IPI if needed. */ - preempt_disable(); - -- /* If the work is "lazy", handle it from next tick if any */ -- if (work->flags & IRQ_WORK_LAZY) { -- if (llist_add(&work->llnode, this_cpu_ptr(&lazy_list)) && -- tick_nohz_tick_stopped()) -- arch_irq_work_raise(); -- } else { -- if (llist_add(&work->llnode, this_cpu_ptr(&raised_list))) -+ lazy_work = work->flags & IRQ_WORK_LAZY; -+ -+ if (lazy_work || (realtime && !(work->flags & IRQ_WORK_HARD_IRQ))) -+ list = this_cpu_ptr(&lazy_list); -+ else -+ list = this_cpu_ptr(&raised_list); -+ -+ if (llist_add(&work->llnode, list)) { -+ if (!lazy_work || tick_nohz_tick_stopped()) - arch_irq_work_raise(); - } - -@@ -116,9 +129,8 @@ - raised = this_cpu_ptr(&raised_list); - lazy = this_cpu_ptr(&lazy_list); - -- if (llist_empty(raised) || arch_irq_work_has_interrupt()) -- if (llist_empty(lazy)) -- return false; -+ if (llist_empty(raised) && llist_empty(lazy)) -+ return false; - - /* All work should have been flushed before going offline */ - WARN_ON_ONCE(cpu_is_offline(smp_processor_id())); -@@ -132,7 +144,7 @@ - struct irq_work *work; - struct llist_node *llnode; - -- BUG_ON(!irqs_disabled()); -+ BUG_ON(!IS_ENABLED(CONFIG_PREEMPT_RT_FULL) && !irqs_disabled()); - - if (llist_empty(list)) - return; -@@ -169,17 +181,26 @@ - void irq_work_run(void) - { - irq_work_run_list(this_cpu_ptr(&raised_list)); -- irq_work_run_list(this_cpu_ptr(&lazy_list)); -+ if (IS_ENABLED(CONFIG_PREEMPT_RT_FULL)) { -+ /* -+ * NOTE: we raise softirq via IPI for safety, -+ * and execute in irq_work_tick() to move the -+ * overhead from hard to soft irq context. -+ */ -+ if (!llist_empty(this_cpu_ptr(&lazy_list))) -+ raise_softirq(TIMER_SOFTIRQ); -+ } else -+ irq_work_run_list(this_cpu_ptr(&lazy_list)); - } - EXPORT_SYMBOL_GPL(irq_work_run); - - void irq_work_tick(void) - { -- struct llist_head *raised = &__get_cpu_var(raised_list); -+ struct llist_head *raised = this_cpu_ptr(&raised_list); - - if (!llist_empty(raised) && !arch_irq_work_has_interrupt()) - irq_work_run_list(raised); -- irq_work_run_list(&__get_cpu_var(lazy_list)); -+ irq_work_run_list(this_cpu_ptr(&lazy_list)); - } - - /* -diff -Nur linux-3.18.14.orig/kernel/Kconfig.locks linux-3.18.14-rt/kernel/Kconfig.locks ---- linux-3.18.14.orig/kernel/Kconfig.locks 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/Kconfig.locks 2015-05-31 15:32:48.585635365 -0500 -@@ -225,11 +225,11 @@ - - config MUTEX_SPIN_ON_OWNER - def_bool y -- depends on SMP && !DEBUG_MUTEXES && ARCH_SUPPORTS_ATOMIC_RMW -+ depends on SMP && !DEBUG_MUTEXES && ARCH_SUPPORTS_ATOMIC_RMW && !PREEMPT_RT_FULL - - config RWSEM_SPIN_ON_OWNER - def_bool y -- depends on SMP && RWSEM_XCHGADD_ALGORITHM && ARCH_SUPPORTS_ATOMIC_RMW -+ depends on SMP && RWSEM_XCHGADD_ALGORITHM && ARCH_SUPPORTS_ATOMIC_RMW && !PREEMPT_RT_FULL - - config ARCH_USE_QUEUE_RWLOCK - bool -diff -Nur linux-3.18.14.orig/kernel/Kconfig.preempt linux-3.18.14-rt/kernel/Kconfig.preempt ---- linux-3.18.14.orig/kernel/Kconfig.preempt 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/Kconfig.preempt 2015-05-31 15:32:48.589635366 -0500 -@@ -1,3 +1,16 @@ -+config PREEMPT -+ bool -+ select PREEMPT_COUNT -+ -+config PREEMPT_RT_BASE -+ bool -+ select PREEMPT -+ -+config HAVE_PREEMPT_LAZY -+ bool -+ -+config PREEMPT_LAZY -+ def_bool y if HAVE_PREEMPT_LAZY && PREEMPT_RT_FULL - - choice - prompt "Preemption Model" -@@ -33,9 +46,9 @@ - - Select this if you are building a kernel for a desktop system. - --config PREEMPT -+config PREEMPT__LL - bool "Preemptible Kernel (Low-Latency Desktop)" -- select PREEMPT_COUNT -+ select PREEMPT - select UNINLINE_SPIN_UNLOCK if !ARCH_INLINE_SPIN_UNLOCK - help - This option reduces the latency of the kernel by making -@@ -52,6 +65,22 @@ - embedded system with latency requirements in the milliseconds - range. - -+config PREEMPT_RTB -+ bool "Preemptible Kernel (Basic RT)" -+ select PREEMPT_RT_BASE -+ help -+ This option is basically the same as (Low-Latency Desktop) but -+ enables changes which are preliminary for the full preemptible -+ RT kernel. -+ -+config PREEMPT_RT_FULL -+ bool "Fully Preemptible Kernel (RT)" -+ depends on IRQ_FORCED_THREADING -+ select PREEMPT_RT_BASE -+ select PREEMPT_RCU -+ help -+ All and everything -+ - endchoice - - config PREEMPT_COUNT -diff -Nur linux-3.18.14.orig/kernel/ksysfs.c linux-3.18.14-rt/kernel/ksysfs.c ---- linux-3.18.14.orig/kernel/ksysfs.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/ksysfs.c 2015-05-31 15:32:48.733635364 -0500 -@@ -136,6 +136,15 @@ - - #endif /* CONFIG_KEXEC */ - -+#if defined(CONFIG_PREEMPT_RT_FULL) -+static ssize_t realtime_show(struct kobject *kobj, -+ struct kobj_attribute *attr, char *buf) -+{ -+ return sprintf(buf, "%d\n", 1); -+} -+KERNEL_ATTR_RO(realtime); -+#endif -+ - /* whether file capabilities are enabled */ - static ssize_t fscaps_show(struct kobject *kobj, - struct kobj_attribute *attr, char *buf) -@@ -203,6 +212,9 @@ - &vmcoreinfo_attr.attr, - #endif - &rcu_expedited_attr.attr, -+#ifdef CONFIG_PREEMPT_RT_FULL -+ &realtime_attr.attr, -+#endif - NULL - }; - -diff -Nur linux-3.18.14.orig/kernel/locking/lglock.c linux-3.18.14-rt/kernel/locking/lglock.c ---- linux-3.18.14.orig/kernel/locking/lglock.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/locking/lglock.c 2015-05-31 15:32:48.749635364 -0500 -@@ -4,6 +4,15 @@ - #include - #include - -+#ifndef CONFIG_PREEMPT_RT_FULL -+# define lg_lock_ptr arch_spinlock_t -+# define lg_do_lock(l) arch_spin_lock(l) -+# define lg_do_unlock(l) arch_spin_unlock(l) -+#else -+# define lg_lock_ptr struct rt_mutex -+# define lg_do_lock(l) __rt_spin_lock(l) -+# define lg_do_unlock(l) __rt_spin_unlock(l) -+#endif - /* - * Note there is no uninit, so lglocks cannot be defined in - * modules (but it's fine to use them from there) -@@ -12,51 +21,60 @@ - - void lg_lock_init(struct lglock *lg, char *name) - { -+#ifdef CONFIG_PREEMPT_RT_FULL -+ int i; -+ -+ for_each_possible_cpu(i) { -+ struct rt_mutex *lock = per_cpu_ptr(lg->lock, i); -+ -+ rt_mutex_init(lock); -+ } -+#endif - LOCKDEP_INIT_MAP(&lg->lock_dep_map, name, &lg->lock_key, 0); - } - EXPORT_SYMBOL(lg_lock_init); - - void lg_local_lock(struct lglock *lg) - { -- arch_spinlock_t *lock; -+ lg_lock_ptr *lock; - -- preempt_disable(); -+ migrate_disable(); - lock_acquire_shared(&lg->lock_dep_map, 0, 0, NULL, _RET_IP_); - lock = this_cpu_ptr(lg->lock); -- arch_spin_lock(lock); -+ lg_do_lock(lock); - } - EXPORT_SYMBOL(lg_local_lock); - - void lg_local_unlock(struct lglock *lg) - { -- arch_spinlock_t *lock; -+ lg_lock_ptr *lock; - - lock_release(&lg->lock_dep_map, 1, _RET_IP_); - lock = this_cpu_ptr(lg->lock); -- arch_spin_unlock(lock); -- preempt_enable(); -+ lg_do_unlock(lock); -+ migrate_enable(); - } - EXPORT_SYMBOL(lg_local_unlock); - - void lg_local_lock_cpu(struct lglock *lg, int cpu) - { -- arch_spinlock_t *lock; -+ lg_lock_ptr *lock; - -- preempt_disable(); -+ preempt_disable_nort(); - lock_acquire_shared(&lg->lock_dep_map, 0, 0, NULL, _RET_IP_); - lock = per_cpu_ptr(lg->lock, cpu); -- arch_spin_lock(lock); -+ lg_do_lock(lock); - } - EXPORT_SYMBOL(lg_local_lock_cpu); - - void lg_local_unlock_cpu(struct lglock *lg, int cpu) - { -- arch_spinlock_t *lock; -+ lg_lock_ptr *lock; - - lock_release(&lg->lock_dep_map, 1, _RET_IP_); - lock = per_cpu_ptr(lg->lock, cpu); -- arch_spin_unlock(lock); -- preempt_enable(); -+ lg_do_unlock(lock); -+ preempt_enable_nort(); - } - EXPORT_SYMBOL(lg_local_unlock_cpu); - -@@ -64,12 +82,12 @@ - { - int i; - -- preempt_disable(); -+ preempt_disable_nort(); - lock_acquire_exclusive(&lg->lock_dep_map, 0, 0, NULL, _RET_IP_); - for_each_possible_cpu(i) { -- arch_spinlock_t *lock; -+ lg_lock_ptr *lock; - lock = per_cpu_ptr(lg->lock, i); -- arch_spin_lock(lock); -+ lg_do_lock(lock); - } - } - EXPORT_SYMBOL(lg_global_lock); -@@ -80,10 +98,35 @@ - - lock_release(&lg->lock_dep_map, 1, _RET_IP_); - for_each_possible_cpu(i) { -- arch_spinlock_t *lock; -+ lg_lock_ptr *lock; - lock = per_cpu_ptr(lg->lock, i); -- arch_spin_unlock(lock); -+ lg_do_unlock(lock); - } -- preempt_enable(); -+ preempt_enable_nort(); - } - EXPORT_SYMBOL(lg_global_unlock); -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+/* -+ * HACK: If you use this, you get to keep the pieces. -+ * Used in queue_stop_cpus_work() when stop machinery -+ * is called from inactive CPU, so we can't schedule. -+ */ -+# define lg_do_trylock_relax(l) \ -+ do { \ -+ while (!__rt_spin_trylock(l)) \ -+ cpu_relax(); \ -+ } while (0) -+ -+void lg_global_trylock_relax(struct lglock *lg) -+{ -+ int i; -+ -+ lock_acquire_exclusive(&lg->lock_dep_map, 0, 0, NULL, _RET_IP_); -+ for_each_possible_cpu(i) { -+ lg_lock_ptr *lock; -+ lock = per_cpu_ptr(lg->lock, i); -+ lg_do_trylock_relax(lock); -+ } -+} -+#endif -diff -Nur linux-3.18.14.orig/kernel/locking/lockdep.c linux-3.18.14-rt/kernel/locking/lockdep.c ---- linux-3.18.14.orig/kernel/locking/lockdep.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/locking/lockdep.c 2015-05-31 15:32:48.749635364 -0500 -@@ -3542,6 +3542,7 @@ - } - } - -+#ifndef CONFIG_PREEMPT_RT_FULL - /* - * We dont accurately track softirq state in e.g. - * hardirq contexts (such as on 4KSTACKS), so only -@@ -3556,6 +3557,7 @@ - DEBUG_LOCKS_WARN_ON(!current->softirqs_enabled); - } - } -+#endif - - if (!debug_locks) - print_irqtrace_events(current); -diff -Nur linux-3.18.14.orig/kernel/locking/Makefile linux-3.18.14-rt/kernel/locking/Makefile ---- linux-3.18.14.orig/kernel/locking/Makefile 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/locking/Makefile 2015-05-31 15:32:48.737635364 -0500 -@@ -1,5 +1,5 @@ - --obj-y += mutex.o semaphore.o rwsem.o mcs_spinlock.o -+obj-y += semaphore.o mcs_spinlock.o - - ifdef CONFIG_FUNCTION_TRACER - CFLAGS_REMOVE_lockdep.o = -pg -@@ -8,7 +8,11 @@ - CFLAGS_REMOVE_rtmutex-debug.o = -pg - endif - -+ifneq ($(CONFIG_PREEMPT_RT_FULL),y) -+obj-y += mutex.o - obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o -+obj-y += rwsem.o -+endif - obj-$(CONFIG_LOCKDEP) += lockdep.o - ifeq ($(CONFIG_PROC_FS),y) - obj-$(CONFIG_LOCKDEP) += lockdep_proc.o -@@ -21,8 +25,11 @@ - obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o - obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o - obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o -+ifneq ($(CONFIG_PREEMPT_RT_FULL),y) - obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o - obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o -+endif - obj-$(CONFIG_PERCPU_RWSEM) += percpu-rwsem.o -+obj-$(CONFIG_PREEMPT_RT_FULL) += rt.o - obj-$(CONFIG_QUEUE_RWLOCK) += qrwlock.o - obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o -diff -Nur linux-3.18.14.orig/kernel/locking/percpu-rwsem.c linux-3.18.14-rt/kernel/locking/percpu-rwsem.c ---- linux-3.18.14.orig/kernel/locking/percpu-rwsem.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/locking/percpu-rwsem.c 2015-05-31 15:32:48.757635364 -0500 -@@ -84,8 +84,12 @@ - - down_read(&brw->rw_sem); - atomic_inc(&brw->slow_read_ctr); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ up_read(&brw->rw_sem); -+#else - /* avoid up_read()->rwsem_release() */ - __up_read(&brw->rw_sem); -+#endif - } - - void percpu_up_read(struct percpu_rw_semaphore *brw) -diff -Nur linux-3.18.14.orig/kernel/locking/rt.c linux-3.18.14-rt/kernel/locking/rt.c ---- linux-3.18.14.orig/kernel/locking/rt.c 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/kernel/locking/rt.c 2015-05-31 15:32:48.757635364 -0500 -@@ -0,0 +1,456 @@ -+/* -+ * kernel/rt.c -+ * -+ * Real-Time Preemption Support -+ * -+ * started by Ingo Molnar: -+ * -+ * Copyright (C) 2004-2006 Red Hat, Inc., Ingo Molnar -+ * Copyright (C) 2006, Timesys Corp., Thomas Gleixner -+ * -+ * historic credit for proving that Linux spinlocks can be implemented via -+ * RT-aware mutexes goes to many people: The Pmutex project (Dirk Grambow -+ * and others) who prototyped it on 2.4 and did lots of comparative -+ * research and analysis; TimeSys, for proving that you can implement a -+ * fully preemptible kernel via the use of IRQ threading and mutexes; -+ * Bill Huey for persuasively arguing on lkml that the mutex model is the -+ * right one; and to MontaVista, who ported pmutexes to 2.6. -+ * -+ * This code is a from-scratch implementation and is not based on pmutexes, -+ * but the idea of converting spinlocks to mutexes is used here too. -+ * -+ * lock debugging, locking tree, deadlock detection: -+ * -+ * Copyright (C) 2004, LynuxWorks, Inc., Igor Manyilov, Bill Huey -+ * Released under the General Public License (GPL). -+ * -+ * Includes portions of the generic R/W semaphore implementation from: -+ * -+ * Copyright (c) 2001 David Howells (dhowells@redhat.com). -+ * - Derived partially from idea by Andrea Arcangeli -+ * - Derived also from comments by Linus -+ * -+ * Pending ownership of locks and ownership stealing: -+ * -+ * Copyright (C) 2005, Kihon Technologies Inc., Steven Rostedt -+ * -+ * (also by Steven Rostedt) -+ * - Converted single pi_lock to individual task locks. -+ * -+ * By Esben Nielsen: -+ * Doing priority inheritance with help of the scheduler. -+ * -+ * Copyright (C) 2006, Timesys Corp., Thomas Gleixner -+ * - major rework based on Esben Nielsens initial patch -+ * - replaced thread_info references by task_struct refs -+ * - removed task->pending_owner dependency -+ * - BKL drop/reacquire for semaphore style locks to avoid deadlocks -+ * in the scheduler return path as discussed with Steven Rostedt -+ * -+ * Copyright (C) 2006, Kihon Technologies Inc. -+ * Steven Rostedt -+ * - debugged and patched Thomas Gleixner's rework. -+ * - added back the cmpxchg to the rework. -+ * - turned atomic require back on for SMP. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "rtmutex_common.h" -+ -+/* -+ * struct mutex functions -+ */ -+void __mutex_do_init(struct mutex *mutex, const char *name, -+ struct lock_class_key *key) -+{ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ /* -+ * Make sure we are not reinitializing a held lock: -+ */ -+ debug_check_no_locks_freed((void *)mutex, sizeof(*mutex)); -+ lockdep_init_map(&mutex->dep_map, name, key, 0); -+#endif -+ mutex->lock.save_state = 0; -+} -+EXPORT_SYMBOL(__mutex_do_init); -+ -+void __lockfunc _mutex_lock(struct mutex *lock) -+{ -+ mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_); -+ rt_mutex_lock(&lock->lock); -+} -+EXPORT_SYMBOL(_mutex_lock); -+ -+int __lockfunc _mutex_lock_interruptible(struct mutex *lock) -+{ -+ int ret; -+ -+ mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_); -+ ret = rt_mutex_lock_interruptible(&lock->lock); -+ if (ret) -+ mutex_release(&lock->dep_map, 1, _RET_IP_); -+ return ret; -+} -+EXPORT_SYMBOL(_mutex_lock_interruptible); -+ -+int __lockfunc _mutex_lock_killable(struct mutex *lock) -+{ -+ int ret; -+ -+ mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_); -+ ret = rt_mutex_lock_killable(&lock->lock); -+ if (ret) -+ mutex_release(&lock->dep_map, 1, _RET_IP_); -+ return ret; -+} -+EXPORT_SYMBOL(_mutex_lock_killable); -+ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+void __lockfunc _mutex_lock_nested(struct mutex *lock, int subclass) -+{ -+ mutex_acquire_nest(&lock->dep_map, subclass, 0, NULL, _RET_IP_); -+ rt_mutex_lock(&lock->lock); -+} -+EXPORT_SYMBOL(_mutex_lock_nested); -+ -+void __lockfunc _mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest) -+{ -+ mutex_acquire_nest(&lock->dep_map, 0, 0, nest, _RET_IP_); -+ rt_mutex_lock(&lock->lock); -+} -+EXPORT_SYMBOL(_mutex_lock_nest_lock); -+ -+int __lockfunc _mutex_lock_interruptible_nested(struct mutex *lock, int subclass) -+{ -+ int ret; -+ -+ mutex_acquire_nest(&lock->dep_map, subclass, 0, NULL, _RET_IP_); -+ ret = rt_mutex_lock_interruptible(&lock->lock); -+ if (ret) -+ mutex_release(&lock->dep_map, 1, _RET_IP_); -+ return ret; -+} -+EXPORT_SYMBOL(_mutex_lock_interruptible_nested); -+ -+int __lockfunc _mutex_lock_killable_nested(struct mutex *lock, int subclass) -+{ -+ int ret; -+ -+ mutex_acquire(&lock->dep_map, subclass, 0, _RET_IP_); -+ ret = rt_mutex_lock_killable(&lock->lock); -+ if (ret) -+ mutex_release(&lock->dep_map, 1, _RET_IP_); -+ return ret; -+} -+EXPORT_SYMBOL(_mutex_lock_killable_nested); -+#endif -+ -+int __lockfunc _mutex_trylock(struct mutex *lock) -+{ -+ int ret = rt_mutex_trylock(&lock->lock); -+ -+ if (ret) -+ mutex_acquire(&lock->dep_map, 0, 1, _RET_IP_); -+ -+ return ret; -+} -+EXPORT_SYMBOL(_mutex_trylock); -+ -+void __lockfunc _mutex_unlock(struct mutex *lock) -+{ -+ mutex_release(&lock->dep_map, 1, _RET_IP_); -+ rt_mutex_unlock(&lock->lock); -+} -+EXPORT_SYMBOL(_mutex_unlock); -+ -+/* -+ * rwlock_t functions -+ */ -+int __lockfunc rt_write_trylock(rwlock_t *rwlock) -+{ -+ int ret; -+ -+ migrate_disable(); -+ ret = rt_mutex_trylock(&rwlock->lock); -+ if (ret) -+ rwlock_acquire(&rwlock->dep_map, 0, 1, _RET_IP_); -+ else -+ migrate_enable(); -+ -+ return ret; -+} -+EXPORT_SYMBOL(rt_write_trylock); -+ -+int __lockfunc rt_write_trylock_irqsave(rwlock_t *rwlock, unsigned long *flags) -+{ -+ int ret; -+ -+ *flags = 0; -+ ret = rt_write_trylock(rwlock); -+ return ret; -+} -+EXPORT_SYMBOL(rt_write_trylock_irqsave); -+ -+int __lockfunc rt_read_trylock(rwlock_t *rwlock) -+{ -+ struct rt_mutex *lock = &rwlock->lock; -+ int ret = 1; -+ -+ /* -+ * recursive read locks succeed when current owns the lock, -+ * but not when read_depth == 0 which means that the lock is -+ * write locked. -+ */ -+ if (rt_mutex_owner(lock) != current) { -+ migrate_disable(); -+ ret = rt_mutex_trylock(lock); -+ if (ret) -+ rwlock_acquire(&rwlock->dep_map, 0, 1, _RET_IP_); -+ else -+ migrate_enable(); -+ -+ } else if (!rwlock->read_depth) { -+ ret = 0; -+ } -+ -+ if (ret) -+ rwlock->read_depth++; -+ -+ return ret; -+} -+EXPORT_SYMBOL(rt_read_trylock); -+ -+void __lockfunc rt_write_lock(rwlock_t *rwlock) -+{ -+ rwlock_acquire(&rwlock->dep_map, 0, 0, _RET_IP_); -+ migrate_disable(); -+ __rt_spin_lock(&rwlock->lock); -+} -+EXPORT_SYMBOL(rt_write_lock); -+ -+void __lockfunc rt_read_lock(rwlock_t *rwlock) -+{ -+ struct rt_mutex *lock = &rwlock->lock; -+ -+ -+ /* -+ * recursive read locks succeed when current owns the lock -+ */ -+ if (rt_mutex_owner(lock) != current) { -+ migrate_disable(); -+ rwlock_acquire(&rwlock->dep_map, 0, 0, _RET_IP_); -+ __rt_spin_lock(lock); -+ } -+ rwlock->read_depth++; -+} -+ -+EXPORT_SYMBOL(rt_read_lock); -+ -+void __lockfunc rt_write_unlock(rwlock_t *rwlock) -+{ -+ /* NOTE: we always pass in '1' for nested, for simplicity */ -+ rwlock_release(&rwlock->dep_map, 1, _RET_IP_); -+ __rt_spin_unlock(&rwlock->lock); -+ migrate_enable(); -+} -+EXPORT_SYMBOL(rt_write_unlock); -+ -+void __lockfunc rt_read_unlock(rwlock_t *rwlock) -+{ -+ /* Release the lock only when read_depth is down to 0 */ -+ if (--rwlock->read_depth == 0) { -+ rwlock_release(&rwlock->dep_map, 1, _RET_IP_); -+ __rt_spin_unlock(&rwlock->lock); -+ migrate_enable(); -+ } -+} -+EXPORT_SYMBOL(rt_read_unlock); -+ -+unsigned long __lockfunc rt_write_lock_irqsave(rwlock_t *rwlock) -+{ -+ rt_write_lock(rwlock); -+ -+ return 0; -+} -+EXPORT_SYMBOL(rt_write_lock_irqsave); -+ -+unsigned long __lockfunc rt_read_lock_irqsave(rwlock_t *rwlock) -+{ -+ rt_read_lock(rwlock); -+ -+ return 0; -+} -+EXPORT_SYMBOL(rt_read_lock_irqsave); -+ -+void __rt_rwlock_init(rwlock_t *rwlock, char *name, struct lock_class_key *key) -+{ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ /* -+ * Make sure we are not reinitializing a held lock: -+ */ -+ debug_check_no_locks_freed((void *)rwlock, sizeof(*rwlock)); -+ lockdep_init_map(&rwlock->dep_map, name, key, 0); -+#endif -+ rwlock->lock.save_state = 1; -+ rwlock->read_depth = 0; -+} -+EXPORT_SYMBOL(__rt_rwlock_init); -+ -+/* -+ * rw_semaphores -+ */ -+ -+void rt_up_write(struct rw_semaphore *rwsem) -+{ -+ rwsem_release(&rwsem->dep_map, 1, _RET_IP_); -+ rt_mutex_unlock(&rwsem->lock); -+} -+EXPORT_SYMBOL(rt_up_write); -+ -+void rt_up_read(struct rw_semaphore *rwsem) -+{ -+ rwsem_release(&rwsem->dep_map, 1, _RET_IP_); -+ if (--rwsem->read_depth == 0) -+ rt_mutex_unlock(&rwsem->lock); -+} -+EXPORT_SYMBOL(rt_up_read); -+ -+/* -+ * downgrade a write lock into a read lock -+ * - just wake up any readers at the front of the queue -+ */ -+void rt_downgrade_write(struct rw_semaphore *rwsem) -+{ -+ BUG_ON(rt_mutex_owner(&rwsem->lock) != current); -+ rwsem->read_depth = 1; -+} -+EXPORT_SYMBOL(rt_downgrade_write); -+ -+int rt_down_write_trylock(struct rw_semaphore *rwsem) -+{ -+ int ret = rt_mutex_trylock(&rwsem->lock); -+ -+ if (ret) -+ rwsem_acquire(&rwsem->dep_map, 0, 1, _RET_IP_); -+ return ret; -+} -+EXPORT_SYMBOL(rt_down_write_trylock); -+ -+void rt_down_write(struct rw_semaphore *rwsem) -+{ -+ rwsem_acquire(&rwsem->dep_map, 0, 0, _RET_IP_); -+ rt_mutex_lock(&rwsem->lock); -+} -+EXPORT_SYMBOL(rt_down_write); -+ -+void rt_down_write_nested(struct rw_semaphore *rwsem, int subclass) -+{ -+ rwsem_acquire(&rwsem->dep_map, subclass, 0, _RET_IP_); -+ rt_mutex_lock(&rwsem->lock); -+} -+EXPORT_SYMBOL(rt_down_write_nested); -+ -+void rt_down_write_nested_lock(struct rw_semaphore *rwsem, -+ struct lockdep_map *nest) -+{ -+ rwsem_acquire_nest(&rwsem->dep_map, 0, 0, nest, _RET_IP_); -+ rt_mutex_lock(&rwsem->lock); -+} -+EXPORT_SYMBOL(rt_down_write_nested_lock); -+ -+int rt_down_read_trylock(struct rw_semaphore *rwsem) -+{ -+ struct rt_mutex *lock = &rwsem->lock; -+ int ret = 1; -+ -+ /* -+ * recursive read locks succeed when current owns the rwsem, -+ * but not when read_depth == 0 which means that the rwsem is -+ * write locked. -+ */ -+ if (rt_mutex_owner(lock) != current) -+ ret = rt_mutex_trylock(&rwsem->lock); -+ else if (!rwsem->read_depth) -+ ret = 0; -+ -+ if (ret) { -+ rwsem->read_depth++; -+ rwsem_acquire(&rwsem->dep_map, 0, 1, _RET_IP_); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(rt_down_read_trylock); -+ -+static void __rt_down_read(struct rw_semaphore *rwsem, int subclass) -+{ -+ struct rt_mutex *lock = &rwsem->lock; -+ -+ rwsem_acquire_read(&rwsem->dep_map, subclass, 0, _RET_IP_); -+ -+ if (rt_mutex_owner(lock) != current) -+ rt_mutex_lock(&rwsem->lock); -+ rwsem->read_depth++; -+} -+ -+void rt_down_read(struct rw_semaphore *rwsem) -+{ -+ __rt_down_read(rwsem, 0); -+} -+EXPORT_SYMBOL(rt_down_read); -+ -+void rt_down_read_nested(struct rw_semaphore *rwsem, int subclass) -+{ -+ __rt_down_read(rwsem, subclass); -+} -+EXPORT_SYMBOL(rt_down_read_nested); -+ -+void __rt_rwsem_init(struct rw_semaphore *rwsem, const char *name, -+ struct lock_class_key *key) -+{ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ /* -+ * Make sure we are not reinitializing a held lock: -+ */ -+ debug_check_no_locks_freed((void *)rwsem, sizeof(*rwsem)); -+ lockdep_init_map(&rwsem->dep_map, name, key, 0); -+#endif -+ rwsem->read_depth = 0; -+ rwsem->lock.save_state = 0; -+} -+EXPORT_SYMBOL(__rt_rwsem_init); -+ -+/** -+ * atomic_dec_and_mutex_lock - return holding mutex if we dec to 0 -+ * @cnt: the atomic which we are to dec -+ * @lock: the mutex to return holding if we dec to 0 -+ * -+ * return true and hold lock if we dec to 0, return false otherwise -+ */ -+int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock) -+{ -+ /* dec if we can't possibly hit 0 */ -+ if (atomic_add_unless(cnt, -1, 1)) -+ return 0; -+ /* we might hit 0, so take the lock */ -+ mutex_lock(lock); -+ if (!atomic_dec_and_test(cnt)) { -+ /* when we actually did the dec, we didn't hit 0 */ -+ mutex_unlock(lock); -+ return 0; -+ } -+ /* we hit 0, and we hold the lock */ -+ return 1; -+} -+EXPORT_SYMBOL(atomic_dec_and_mutex_lock); -diff -Nur linux-3.18.14.orig/kernel/locking/rtmutex.c linux-3.18.14-rt/kernel/locking/rtmutex.c ---- linux-3.18.14.orig/kernel/locking/rtmutex.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/locking/rtmutex.c 2015-05-31 15:32:48.769635364 -0500 -@@ -7,6 +7,11 @@ - * Copyright (C) 2005-2006 Timesys Corp., Thomas Gleixner - * Copyright (C) 2005 Kihon Technologies Inc., Steven Rostedt - * Copyright (C) 2006 Esben Nielsen -+ * Adaptive Spinlocks: -+ * Copyright (C) 2008 Novell, Inc., Gregory Haskins, Sven Dietrich, -+ * and Peter Morreale, -+ * Adaptive Spinlocks simplification: -+ * Copyright (C) 2008 Red Hat, Inc., Steven Rostedt - * - * See Documentation/locking/rt-mutex-design.txt for details. - */ -@@ -16,6 +21,7 @@ - #include - #include - #include -+#include - - #include "rtmutex_common.h" - -@@ -69,6 +75,12 @@ - clear_rt_mutex_waiters(lock); - } - -+static int rt_mutex_real_waiter(struct rt_mutex_waiter *waiter) -+{ -+ return waiter && waiter != PI_WAKEUP_INPROGRESS && -+ waiter != PI_REQUEUE_INPROGRESS; -+} -+ - /* - * We can speed up the acquire/release, if the architecture - * supports cmpxchg and if there's no debugging state to be set up -@@ -333,6 +345,14 @@ - return debug_rt_mutex_detect_deadlock(waiter, chwalk); - } - -+static void rt_mutex_wake_waiter(struct rt_mutex_waiter *waiter) -+{ -+ if (waiter->savestate) -+ wake_up_lock_sleeper(waiter->task); -+ else -+ wake_up_process(waiter->task); -+} -+ - /* - * Max number of times we'll walk the boosting chain: - */ -@@ -340,7 +360,8 @@ - - static inline struct rt_mutex *task_blocked_on_lock(struct task_struct *p) - { -- return p->pi_blocked_on ? p->pi_blocked_on->lock : NULL; -+ return rt_mutex_real_waiter(p->pi_blocked_on) ? -+ p->pi_blocked_on->lock : NULL; - } - - /* -@@ -477,7 +498,7 @@ - * reached or the state of the chain has changed while we - * dropped the locks. - */ -- if (!waiter) -+ if (!rt_mutex_real_waiter(waiter)) - goto out_unlock_pi; - - /* -@@ -639,13 +660,16 @@ - * follow here. This is the end of the chain we are walking. - */ - if (!rt_mutex_owner(lock)) { -+ struct rt_mutex_waiter *lock_top_waiter; -+ - /* - * If the requeue [7] above changed the top waiter, - * then we need to wake the new top waiter up to try - * to get the lock. - */ -- if (prerequeue_top_waiter != rt_mutex_top_waiter(lock)) -- wake_up_process(rt_mutex_top_waiter(lock)->task); -+ lock_top_waiter = rt_mutex_top_waiter(lock); -+ if (prerequeue_top_waiter != lock_top_waiter) -+ rt_mutex_wake_waiter(lock_top_waiter); - raw_spin_unlock(&lock->wait_lock); - return 0; - } -@@ -738,6 +762,25 @@ - return ret; - } - -+ -+#define STEAL_NORMAL 0 -+#define STEAL_LATERAL 1 -+ -+/* -+ * Note that RT tasks are excluded from lateral-steals to prevent the -+ * introduction of an unbounded latency -+ */ -+static inline int lock_is_stealable(struct task_struct *task, -+ struct task_struct *pendowner, int mode) -+{ -+ if (mode == STEAL_NORMAL || rt_task(task)) { -+ if (task->prio >= pendowner->prio) -+ return 0; -+ } else if (task->prio > pendowner->prio) -+ return 0; -+ return 1; -+} -+ - /* - * Try to take an rt-mutex - * -@@ -748,8 +791,9 @@ - * @waiter: The waiter that is queued to the lock's wait list if the - * callsite called task_blocked_on_lock(), otherwise NULL - */ --static int try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task, -- struct rt_mutex_waiter *waiter) -+static int __try_to_take_rt_mutex(struct rt_mutex *lock, -+ struct task_struct *task, -+ struct rt_mutex_waiter *waiter, int mode) - { - unsigned long flags; - -@@ -788,8 +832,10 @@ - * If waiter is not the highest priority waiter of - * @lock, give up. - */ -- if (waiter != rt_mutex_top_waiter(lock)) -+ if (waiter != rt_mutex_top_waiter(lock)) { -+ /* XXX lock_is_stealable() ? */ - return 0; -+ } - - /* - * We can acquire the lock. Remove the waiter from the -@@ -807,14 +853,10 @@ - * not need to be dequeued. - */ - if (rt_mutex_has_waiters(lock)) { -- /* -- * If @task->prio is greater than or equal to -- * the top waiter priority (kernel view), -- * @task lost. -- */ -- if (task->prio >= rt_mutex_top_waiter(lock)->prio) -- return 0; -+ struct task_struct *pown = rt_mutex_top_waiter(lock)->task; - -+ if (task != pown && !lock_is_stealable(task, pown, mode)) -+ return 0; - /* - * The current top waiter stays enqueued. We - * don't have to change anything in the lock -@@ -863,6 +905,369 @@ - return 1; - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+/* -+ * preemptible spin_lock functions: -+ */ -+static inline void rt_spin_lock_fastlock(struct rt_mutex *lock, -+ void (*slowfn)(struct rt_mutex *lock)) -+{ -+ might_sleep(); -+ -+ if (likely(rt_mutex_cmpxchg(lock, NULL, current))) -+ rt_mutex_deadlock_account_lock(lock, current); -+ else -+ slowfn(lock); -+} -+ -+static inline void rt_spin_lock_fastunlock(struct rt_mutex *lock, -+ void (*slowfn)(struct rt_mutex *lock)) -+{ -+ if (likely(rt_mutex_cmpxchg(lock, current, NULL))) -+ rt_mutex_deadlock_account_unlock(current); -+ else -+ slowfn(lock); -+} -+#ifdef CONFIG_SMP -+/* -+ * Note that owner is a speculative pointer and dereferencing relies -+ * on rcu_read_lock() and the check against the lock owner. -+ */ -+static int adaptive_wait(struct rt_mutex *lock, -+ struct task_struct *owner) -+{ -+ int res = 0; -+ -+ rcu_read_lock(); -+ for (;;) { -+ if (owner != rt_mutex_owner(lock)) -+ break; -+ /* -+ * Ensure that owner->on_cpu is dereferenced _after_ -+ * checking the above to be valid. -+ */ -+ barrier(); -+ if (!owner->on_cpu) { -+ res = 1; -+ break; -+ } -+ cpu_relax(); -+ } -+ rcu_read_unlock(); -+ return res; -+} -+#else -+static int adaptive_wait(struct rt_mutex *lock, -+ struct task_struct *orig_owner) -+{ -+ return 1; -+} -+#endif -+ -+# define pi_lock(lock) raw_spin_lock_irq(lock) -+# define pi_unlock(lock) raw_spin_unlock_irq(lock) -+ -+static int task_blocks_on_rt_mutex(struct rt_mutex *lock, -+ struct rt_mutex_waiter *waiter, -+ struct task_struct *task, -+ enum rtmutex_chainwalk chwalk); -+/* -+ * Slow path lock function spin_lock style: this variant is very -+ * careful not to miss any non-lock wakeups. -+ * -+ * We store the current state under p->pi_lock in p->saved_state and -+ * the try_to_wake_up() code handles this accordingly. -+ */ -+static void noinline __sched rt_spin_lock_slowlock(struct rt_mutex *lock) -+{ -+ struct task_struct *lock_owner, *self = current; -+ struct rt_mutex_waiter waiter, *top_waiter; -+ int ret; -+ -+ rt_mutex_init_waiter(&waiter, true); -+ -+ raw_spin_lock(&lock->wait_lock); -+ -+ if (__try_to_take_rt_mutex(lock, self, NULL, STEAL_LATERAL)) { -+ raw_spin_unlock(&lock->wait_lock); -+ return; -+ } -+ -+ BUG_ON(rt_mutex_owner(lock) == self); -+ -+ /* -+ * We save whatever state the task is in and we'll restore it -+ * after acquiring the lock taking real wakeups into account -+ * as well. We are serialized via pi_lock against wakeups. See -+ * try_to_wake_up(). -+ */ -+ pi_lock(&self->pi_lock); -+ self->saved_state = self->state; -+ __set_current_state(TASK_UNINTERRUPTIBLE); -+ pi_unlock(&self->pi_lock); -+ -+ ret = task_blocks_on_rt_mutex(lock, &waiter, self, 0); -+ BUG_ON(ret); -+ -+ for (;;) { -+ /* Try to acquire the lock again. */ -+ if (__try_to_take_rt_mutex(lock, self, &waiter, STEAL_LATERAL)) -+ break; -+ -+ top_waiter = rt_mutex_top_waiter(lock); -+ lock_owner = rt_mutex_owner(lock); -+ -+ raw_spin_unlock(&lock->wait_lock); -+ -+ debug_rt_mutex_print_deadlock(&waiter); -+ -+ if (top_waiter != &waiter || adaptive_wait(lock, lock_owner)) -+ schedule_rt_mutex(lock); -+ -+ raw_spin_lock(&lock->wait_lock); -+ -+ pi_lock(&self->pi_lock); -+ __set_current_state(TASK_UNINTERRUPTIBLE); -+ pi_unlock(&self->pi_lock); -+ } -+ -+ /* -+ * Restore the task state to current->saved_state. We set it -+ * to the original state above and the try_to_wake_up() code -+ * has possibly updated it when a real (non-rtmutex) wakeup -+ * happened while we were blocked. Clear saved_state so -+ * try_to_wakeup() does not get confused. -+ */ -+ pi_lock(&self->pi_lock); -+ __set_current_state(self->saved_state); -+ self->saved_state = TASK_RUNNING; -+ pi_unlock(&self->pi_lock); -+ -+ /* -+ * try_to_take_rt_mutex() sets the waiter bit -+ * unconditionally. We might have to fix that up: -+ */ -+ fixup_rt_mutex_waiters(lock); -+ -+ BUG_ON(rt_mutex_has_waiters(lock) && &waiter == rt_mutex_top_waiter(lock)); -+ BUG_ON(!RB_EMPTY_NODE(&waiter.tree_entry)); -+ -+ raw_spin_unlock(&lock->wait_lock); -+ -+ debug_rt_mutex_free_waiter(&waiter); -+} -+ -+static void wakeup_next_waiter(struct rt_mutex *lock); -+/* -+ * Slow path to release a rt_mutex spin_lock style -+ */ -+static void __sched __rt_spin_lock_slowunlock(struct rt_mutex *lock) -+{ -+ debug_rt_mutex_unlock(lock); -+ -+ rt_mutex_deadlock_account_unlock(current); -+ -+ if (!rt_mutex_has_waiters(lock)) { -+ lock->owner = NULL; -+ raw_spin_unlock(&lock->wait_lock); -+ return; -+ } -+ -+ wakeup_next_waiter(lock); -+ -+ raw_spin_unlock(&lock->wait_lock); -+ -+ /* Undo pi boosting.when necessary */ -+ rt_mutex_adjust_prio(current); -+} -+ -+static void noinline __sched rt_spin_lock_slowunlock(struct rt_mutex *lock) -+{ -+ raw_spin_lock(&lock->wait_lock); -+ __rt_spin_lock_slowunlock(lock); -+} -+ -+static void noinline __sched rt_spin_lock_slowunlock_hirq(struct rt_mutex *lock) -+{ -+ int ret; -+ -+ do { -+ ret = raw_spin_trylock(&lock->wait_lock); -+ } while (!ret); -+ -+ __rt_spin_lock_slowunlock(lock); -+} -+ -+void __lockfunc rt_spin_lock(spinlock_t *lock) -+{ -+ rt_spin_lock_fastlock(&lock->lock, rt_spin_lock_slowlock); -+ spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); -+} -+EXPORT_SYMBOL(rt_spin_lock); -+ -+void __lockfunc __rt_spin_lock(struct rt_mutex *lock) -+{ -+ rt_spin_lock_fastlock(lock, rt_spin_lock_slowlock); -+} -+EXPORT_SYMBOL(__rt_spin_lock); -+ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+void __lockfunc rt_spin_lock_nested(spinlock_t *lock, int subclass) -+{ -+ rt_spin_lock_fastlock(&lock->lock, rt_spin_lock_slowlock); -+ spin_acquire(&lock->dep_map, subclass, 0, _RET_IP_); -+} -+EXPORT_SYMBOL(rt_spin_lock_nested); -+#endif -+ -+void __lockfunc rt_spin_unlock(spinlock_t *lock) -+{ -+ /* NOTE: we always pass in '1' for nested, for simplicity */ -+ spin_release(&lock->dep_map, 1, _RET_IP_); -+ rt_spin_lock_fastunlock(&lock->lock, rt_spin_lock_slowunlock); -+} -+EXPORT_SYMBOL(rt_spin_unlock); -+ -+void __lockfunc rt_spin_unlock_after_trylock_in_irq(spinlock_t *lock) -+{ -+ /* NOTE: we always pass in '1' for nested, for simplicity */ -+ spin_release(&lock->dep_map, 1, _RET_IP_); -+ rt_spin_lock_fastunlock(&lock->lock, rt_spin_lock_slowunlock_hirq); -+} -+ -+void __lockfunc __rt_spin_unlock(struct rt_mutex *lock) -+{ -+ rt_spin_lock_fastunlock(lock, rt_spin_lock_slowunlock); -+} -+EXPORT_SYMBOL(__rt_spin_unlock); -+ -+/* -+ * Wait for the lock to get unlocked: instead of polling for an unlock -+ * (like raw spinlocks do), we lock and unlock, to force the kernel to -+ * schedule if there's contention: -+ */ -+void __lockfunc rt_spin_unlock_wait(spinlock_t *lock) -+{ -+ spin_lock(lock); -+ spin_unlock(lock); -+} -+EXPORT_SYMBOL(rt_spin_unlock_wait); -+ -+int __lockfunc __rt_spin_trylock(struct rt_mutex *lock) -+{ -+ return rt_mutex_trylock(lock); -+} -+ -+int __lockfunc rt_spin_trylock(spinlock_t *lock) -+{ -+ int ret = rt_mutex_trylock(&lock->lock); -+ -+ if (ret) -+ spin_acquire(&lock->dep_map, 0, 1, _RET_IP_); -+ return ret; -+} -+EXPORT_SYMBOL(rt_spin_trylock); -+ -+int __lockfunc rt_spin_trylock_bh(spinlock_t *lock) -+{ -+ int ret; -+ -+ local_bh_disable(); -+ ret = rt_mutex_trylock(&lock->lock); -+ if (ret) { -+ migrate_disable(); -+ spin_acquire(&lock->dep_map, 0, 1, _RET_IP_); -+ } else -+ local_bh_enable(); -+ return ret; -+} -+EXPORT_SYMBOL(rt_spin_trylock_bh); -+ -+int __lockfunc rt_spin_trylock_irqsave(spinlock_t *lock, unsigned long *flags) -+{ -+ int ret; -+ -+ *flags = 0; -+ ret = rt_mutex_trylock(&lock->lock); -+ if (ret) { -+ migrate_disable(); -+ spin_acquire(&lock->dep_map, 0, 1, _RET_IP_); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(rt_spin_trylock_irqsave); -+ -+int atomic_dec_and_spin_lock(atomic_t *atomic, spinlock_t *lock) -+{ -+ /* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */ -+ if (atomic_add_unless(atomic, -1, 1)) -+ return 0; -+ migrate_disable(); -+ rt_spin_lock(lock); -+ if (atomic_dec_and_test(atomic)) -+ return 1; -+ rt_spin_unlock(lock); -+ migrate_enable(); -+ return 0; -+} -+EXPORT_SYMBOL(atomic_dec_and_spin_lock); -+ -+ void -+__rt_spin_lock_init(spinlock_t *lock, char *name, struct lock_class_key *key) -+{ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ /* -+ * Make sure we are not reinitializing a held lock: -+ */ -+ debug_check_no_locks_freed((void *)lock, sizeof(*lock)); -+ lockdep_init_map(&lock->dep_map, name, key, 0); -+#endif -+} -+EXPORT_SYMBOL(__rt_spin_lock_init); -+ -+#endif /* PREEMPT_RT_FULL */ -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ static inline int __sched -+__mutex_lock_check_stamp(struct rt_mutex *lock, struct ww_acquire_ctx *ctx) -+{ -+ struct ww_mutex *ww = container_of(lock, struct ww_mutex, base.lock); -+ struct ww_acquire_ctx *hold_ctx = ACCESS_ONCE(ww->ctx); -+ -+ if (!hold_ctx) -+ return 0; -+ -+ if (unlikely(ctx == hold_ctx)) -+ return -EALREADY; -+ -+ if (ctx->stamp - hold_ctx->stamp <= LONG_MAX && -+ (ctx->stamp != hold_ctx->stamp || ctx > hold_ctx)) { -+#ifdef CONFIG_DEBUG_MUTEXES -+ DEBUG_LOCKS_WARN_ON(ctx->contending_lock); -+ ctx->contending_lock = ww; -+#endif -+ return -EDEADLK; -+ } -+ -+ return 0; -+} -+#else -+ static inline int __sched -+__mutex_lock_check_stamp(struct rt_mutex *lock, struct ww_acquire_ctx *ctx) -+{ -+ BUG(); -+ return 0; -+} -+ -+#endif -+ -+static inline int -+try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task, -+ struct rt_mutex_waiter *waiter) -+{ -+ return __try_to_take_rt_mutex(lock, task, waiter, STEAL_NORMAL); -+} -+ - /* - * Task blocks on lock. - * -@@ -894,6 +1299,23 @@ - return -EDEADLK; - - raw_spin_lock_irqsave(&task->pi_lock, flags); -+ -+ /* -+ * In the case of futex requeue PI, this will be a proxy -+ * lock. The task will wake unaware that it is enqueueed on -+ * this lock. Avoid blocking on two locks and corrupting -+ * pi_blocked_on via the PI_WAKEUP_INPROGRESS -+ * flag. futex_wait_requeue_pi() sets this when it wakes up -+ * before requeue (due to a signal or timeout). Do not enqueue -+ * the task if PI_WAKEUP_INPROGRESS is set. -+ */ -+ if (task != current && task->pi_blocked_on == PI_WAKEUP_INPROGRESS) { -+ raw_spin_unlock_irqrestore(&task->pi_lock, flags); -+ return -EAGAIN; -+ } -+ -+ BUG_ON(rt_mutex_real_waiter(task->pi_blocked_on)); -+ - __rt_mutex_adjust_prio(task); - waiter->task = task; - waiter->lock = lock; -@@ -917,7 +1339,7 @@ - rt_mutex_enqueue_pi(owner, waiter); - - __rt_mutex_adjust_prio(owner); -- if (owner->pi_blocked_on) -+ if (rt_mutex_real_waiter(owner->pi_blocked_on)) - chain_walk = 1; - } else if (rt_mutex_cond_detect_deadlock(waiter, chwalk)) { - chain_walk = 1; -@@ -994,7 +1416,7 @@ - * long as we hold lock->wait_lock. The waiter task needs to - * acquire it in order to dequeue the waiter. - */ -- wake_up_process(waiter->task); -+ rt_mutex_wake_waiter(waiter); - } - - /* -@@ -1008,7 +1430,7 @@ - { - bool is_top_waiter = (waiter == rt_mutex_top_waiter(lock)); - struct task_struct *owner = rt_mutex_owner(lock); -- struct rt_mutex *next_lock; -+ struct rt_mutex *next_lock = NULL; - unsigned long flags; - - raw_spin_lock_irqsave(¤t->pi_lock, flags); -@@ -1033,7 +1455,8 @@ - __rt_mutex_adjust_prio(owner); - - /* Store the lock on which owner is blocked or NULL */ -- next_lock = task_blocked_on_lock(owner); -+ if (rt_mutex_real_waiter(owner->pi_blocked_on)) -+ next_lock = task_blocked_on_lock(owner); - - raw_spin_unlock_irqrestore(&owner->pi_lock, flags); - -@@ -1069,17 +1492,17 @@ - raw_spin_lock_irqsave(&task->pi_lock, flags); - - waiter = task->pi_blocked_on; -- if (!waiter || (waiter->prio == task->prio && -+ if (!rt_mutex_real_waiter(waiter) || (waiter->prio == task->prio && - !dl_prio(task->prio))) { - raw_spin_unlock_irqrestore(&task->pi_lock, flags); - return; - } - next_lock = waiter->lock; -- raw_spin_unlock_irqrestore(&task->pi_lock, flags); - - /* gets dropped in rt_mutex_adjust_prio_chain()! */ - get_task_struct(task); - -+ raw_spin_unlock_irqrestore(&task->pi_lock, flags); - rt_mutex_adjust_prio_chain(task, RT_MUTEX_MIN_CHAINWALK, NULL, - next_lock, NULL, task); - } -@@ -1097,7 +1520,8 @@ - static int __sched - __rt_mutex_slowlock(struct rt_mutex *lock, int state, - struct hrtimer_sleeper *timeout, -- struct rt_mutex_waiter *waiter) -+ struct rt_mutex_waiter *waiter, -+ struct ww_acquire_ctx *ww_ctx) - { - int ret = 0; - -@@ -1120,6 +1544,12 @@ - break; - } - -+ if (ww_ctx && ww_ctx->acquired > 0) { -+ ret = __mutex_lock_check_stamp(lock, ww_ctx); -+ if (ret) -+ break; -+ } -+ - raw_spin_unlock(&lock->wait_lock); - - debug_rt_mutex_print_deadlock(waiter); -@@ -1153,25 +1583,102 @@ - } - } - -+static __always_inline void ww_mutex_lock_acquired(struct ww_mutex *ww, -+ struct ww_acquire_ctx *ww_ctx) -+{ -+#ifdef CONFIG_DEBUG_MUTEXES -+ /* -+ * If this WARN_ON triggers, you used ww_mutex_lock to acquire, -+ * but released with a normal mutex_unlock in this call. -+ * -+ * This should never happen, always use ww_mutex_unlock. -+ */ -+ DEBUG_LOCKS_WARN_ON(ww->ctx); -+ -+ /* -+ * Not quite done after calling ww_acquire_done() ? -+ */ -+ DEBUG_LOCKS_WARN_ON(ww_ctx->done_acquire); -+ -+ if (ww_ctx->contending_lock) { -+ /* -+ * After -EDEADLK you tried to -+ * acquire a different ww_mutex? Bad! -+ */ -+ DEBUG_LOCKS_WARN_ON(ww_ctx->contending_lock != ww); -+ -+ /* -+ * You called ww_mutex_lock after receiving -EDEADLK, -+ * but 'forgot' to unlock everything else first? -+ */ -+ DEBUG_LOCKS_WARN_ON(ww_ctx->acquired > 0); -+ ww_ctx->contending_lock = NULL; -+ } -+ -+ /* -+ * Naughty, using a different class will lead to undefined behavior! -+ */ -+ DEBUG_LOCKS_WARN_ON(ww_ctx->ww_class != ww->ww_class); -+#endif -+ ww_ctx->acquired++; -+} -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+static void ww_mutex_account_lock(struct rt_mutex *lock, -+ struct ww_acquire_ctx *ww_ctx) -+{ -+ struct ww_mutex *ww = container_of(lock, struct ww_mutex, base.lock); -+ struct rt_mutex_waiter *waiter, *n; -+ -+ /* -+ * This branch gets optimized out for the common case, -+ * and is only important for ww_mutex_lock. -+ */ -+ ww_mutex_lock_acquired(ww, ww_ctx); -+ ww->ctx = ww_ctx; -+ -+ /* -+ * Give any possible sleeping processes the chance to wake up, -+ * so they can recheck if they have to back off. -+ */ -+ rbtree_postorder_for_each_entry_safe(waiter, n, &lock->waiters, -+ tree_entry) { -+ /* XXX debug rt mutex waiter wakeup */ -+ -+ BUG_ON(waiter->lock != lock); -+ rt_mutex_wake_waiter(waiter); -+ } -+} -+ -+#else -+ -+static void ww_mutex_account_lock(struct rt_mutex *lock, -+ struct ww_acquire_ctx *ww_ctx) -+{ -+ BUG(); -+} -+#endif -+ - /* - * Slow path lock function: - */ - static int __sched - rt_mutex_slowlock(struct rt_mutex *lock, int state, - struct hrtimer_sleeper *timeout, -- enum rtmutex_chainwalk chwalk) -+ enum rtmutex_chainwalk chwalk, -+ struct ww_acquire_ctx *ww_ctx) - { - struct rt_mutex_waiter waiter; - int ret = 0; - -- debug_rt_mutex_init_waiter(&waiter); -- RB_CLEAR_NODE(&waiter.pi_tree_entry); -- RB_CLEAR_NODE(&waiter.tree_entry); -+ rt_mutex_init_waiter(&waiter, false); - - raw_spin_lock(&lock->wait_lock); - - /* Try to acquire the lock again: */ - if (try_to_take_rt_mutex(lock, current, NULL)) { -+ if (ww_ctx) -+ ww_mutex_account_lock(lock, ww_ctx); - raw_spin_unlock(&lock->wait_lock); - return 0; - } -@@ -1188,14 +1695,23 @@ - ret = task_blocks_on_rt_mutex(lock, &waiter, current, chwalk); - - if (likely(!ret)) -- ret = __rt_mutex_slowlock(lock, state, timeout, &waiter); -+ ret = __rt_mutex_slowlock(lock, state, timeout, &waiter, ww_ctx); -+ else if (ww_ctx) { -+ /* ww_mutex received EDEADLK, let it become EALREADY */ -+ ret = __mutex_lock_check_stamp(lock, ww_ctx); -+ BUG_ON(!ret); -+ } - - set_current_state(TASK_RUNNING); - - if (unlikely(ret)) { - if (rt_mutex_has_waiters(lock)) - remove_waiter(lock, &waiter); -- rt_mutex_handle_deadlock(ret, chwalk, &waiter); -+ /* ww_mutex want to report EDEADLK/EALREADY, let them */ -+ if (!ww_ctx) -+ rt_mutex_handle_deadlock(ret, chwalk, &waiter); -+ } else if (ww_ctx) { -+ ww_mutex_account_lock(lock, ww_ctx); - } - - /* -@@ -1234,7 +1750,8 @@ - * The mutex has currently no owner. Lock the wait lock and - * try to acquire the lock. - */ -- raw_spin_lock(&lock->wait_lock); -+ if (!raw_spin_trylock(&lock->wait_lock)) -+ return 0; - - ret = try_to_take_rt_mutex(lock, current, NULL); - -@@ -1320,31 +1837,36 @@ - */ - static inline int - rt_mutex_fastlock(struct rt_mutex *lock, int state, -+ struct ww_acquire_ctx *ww_ctx, - int (*slowfn)(struct rt_mutex *lock, int state, - struct hrtimer_sleeper *timeout, -- enum rtmutex_chainwalk chwalk)) -+ enum rtmutex_chainwalk chwalk, -+ struct ww_acquire_ctx *ww_ctx)) - { - if (likely(rt_mutex_cmpxchg(lock, NULL, current))) { - rt_mutex_deadlock_account_lock(lock, current); - return 0; - } else -- return slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK); -+ return slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK, -+ ww_ctx); - } - - static inline int - rt_mutex_timed_fastlock(struct rt_mutex *lock, int state, - struct hrtimer_sleeper *timeout, - enum rtmutex_chainwalk chwalk, -+ struct ww_acquire_ctx *ww_ctx, - int (*slowfn)(struct rt_mutex *lock, int state, - struct hrtimer_sleeper *timeout, -- enum rtmutex_chainwalk chwalk)) -+ enum rtmutex_chainwalk chwalk, -+ struct ww_acquire_ctx *ww_ctx)) - { - if (chwalk == RT_MUTEX_MIN_CHAINWALK && - likely(rt_mutex_cmpxchg(lock, NULL, current))) { - rt_mutex_deadlock_account_lock(lock, current); - return 0; - } else -- return slowfn(lock, state, timeout, chwalk); -+ return slowfn(lock, state, timeout, chwalk, ww_ctx); - } - - static inline int -@@ -1377,7 +1899,7 @@ - { - might_sleep(); - -- rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, rt_mutex_slowlock); -+ rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, NULL, rt_mutex_slowlock); - } - EXPORT_SYMBOL_GPL(rt_mutex_lock); - -@@ -1394,7 +1916,7 @@ - { - might_sleep(); - -- return rt_mutex_fastlock(lock, TASK_INTERRUPTIBLE, rt_mutex_slowlock); -+ return rt_mutex_fastlock(lock, TASK_INTERRUPTIBLE, NULL, rt_mutex_slowlock); - } - EXPORT_SYMBOL_GPL(rt_mutex_lock_interruptible); - -@@ -1407,11 +1929,30 @@ - might_sleep(); - - return rt_mutex_timed_fastlock(lock, TASK_INTERRUPTIBLE, timeout, -- RT_MUTEX_FULL_CHAINWALK, -+ RT_MUTEX_FULL_CHAINWALK, NULL, - rt_mutex_slowlock); - } - - /** -+ * rt_mutex_lock_killable - lock a rt_mutex killable -+ * -+ * @lock: the rt_mutex to be locked -+ * @detect_deadlock: deadlock detection on/off -+ * -+ * Returns: -+ * 0 on success -+ * -EINTR when interrupted by a signal -+ * -EDEADLK when the lock would deadlock (when deadlock detection is on) -+ */ -+int __sched rt_mutex_lock_killable(struct rt_mutex *lock) -+{ -+ might_sleep(); -+ -+ return rt_mutex_fastlock(lock, TASK_KILLABLE, NULL, rt_mutex_slowlock); -+} -+EXPORT_SYMBOL_GPL(rt_mutex_lock_killable); -+ -+/** - * rt_mutex_timed_lock - lock a rt_mutex interruptible - * the timeout structure is provided - * by the caller -@@ -1431,6 +1972,7 @@ - - return rt_mutex_timed_fastlock(lock, TASK_INTERRUPTIBLE, timeout, - RT_MUTEX_MIN_CHAINWALK, -+ NULL, - rt_mutex_slowlock); - } - EXPORT_SYMBOL_GPL(rt_mutex_timed_lock); -@@ -1489,13 +2031,12 @@ - void __rt_mutex_init(struct rt_mutex *lock, const char *name) - { - lock->owner = NULL; -- raw_spin_lock_init(&lock->wait_lock); - lock->waiters = RB_ROOT; - lock->waiters_leftmost = NULL; - - debug_rt_mutex_init(lock, name); - } --EXPORT_SYMBOL_GPL(__rt_mutex_init); -+EXPORT_SYMBOL(__rt_mutex_init); - - /** - * rt_mutex_init_proxy_locked - initialize and lock a rt_mutex on behalf of a -@@ -1510,7 +2051,7 @@ - void rt_mutex_init_proxy_locked(struct rt_mutex *lock, - struct task_struct *proxy_owner) - { -- __rt_mutex_init(lock, NULL); -+ rt_mutex_init(lock); - debug_rt_mutex_proxy_lock(lock, proxy_owner); - rt_mutex_set_owner(lock, proxy_owner); - rt_mutex_deadlock_account_lock(lock, proxy_owner); -@@ -1558,6 +2099,35 @@ - return 1; - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+ /* -+ * In PREEMPT_RT there's an added race. -+ * If the task, that we are about to requeue, times out, -+ * it can set the PI_WAKEUP_INPROGRESS. This tells the requeue -+ * to skip this task. But right after the task sets -+ * its pi_blocked_on to PI_WAKEUP_INPROGRESS it can then -+ * block on the spin_lock(&hb->lock), which in RT is an rtmutex. -+ * This will replace the PI_WAKEUP_INPROGRESS with the actual -+ * lock that it blocks on. We *must not* place this task -+ * on this proxy lock in that case. -+ * -+ * To prevent this race, we first take the task's pi_lock -+ * and check if it has updated its pi_blocked_on. If it has, -+ * we assume that it woke up and we return -EAGAIN. -+ * Otherwise, we set the task's pi_blocked_on to -+ * PI_REQUEUE_INPROGRESS, so that if the task is waking up -+ * it will know that we are in the process of requeuing it. -+ */ -+ raw_spin_lock_irq(&task->pi_lock); -+ if (task->pi_blocked_on) { -+ raw_spin_unlock_irq(&task->pi_lock); -+ raw_spin_unlock(&lock->wait_lock); -+ return -EAGAIN; -+ } -+ task->pi_blocked_on = PI_REQUEUE_INPROGRESS; -+ raw_spin_unlock_irq(&task->pi_lock); -+#endif -+ - /* We enforce deadlock detection for futexes */ - ret = task_blocks_on_rt_mutex(lock, waiter, task, - RT_MUTEX_FULL_CHAINWALK); -@@ -1627,7 +2197,7 @@ - - set_current_state(TASK_INTERRUPTIBLE); - -- ret = __rt_mutex_slowlock(lock, TASK_INTERRUPTIBLE, to, waiter); -+ ret = __rt_mutex_slowlock(lock, TASK_INTERRUPTIBLE, to, waiter, NULL); - - set_current_state(TASK_RUNNING); - -@@ -1644,3 +2214,89 @@ - - return ret; - } -+ -+static inline int -+ww_mutex_deadlock_injection(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) -+{ -+#ifdef CONFIG_DEBUG_WW_MUTEX_SLOWPATH -+ unsigned tmp; -+ -+ if (ctx->deadlock_inject_countdown-- == 0) { -+ tmp = ctx->deadlock_inject_interval; -+ if (tmp > UINT_MAX/4) -+ tmp = UINT_MAX; -+ else -+ tmp = tmp*2 + tmp + tmp/2; -+ -+ ctx->deadlock_inject_interval = tmp; -+ ctx->deadlock_inject_countdown = tmp; -+ ctx->contending_lock = lock; -+ -+ ww_mutex_unlock(lock); -+ -+ return -EDEADLK; -+ } -+#endif -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PREEMPT_RT_FULL -+int __sched -+__ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ww_ctx) -+{ -+ int ret; -+ -+ might_sleep(); -+ -+ mutex_acquire_nest(&lock->base.dep_map, 0, 0, &ww_ctx->dep_map, _RET_IP_); -+ ret = rt_mutex_slowlock(&lock->base.lock, TASK_INTERRUPTIBLE, NULL, 0, ww_ctx); -+ if (ret) -+ mutex_release(&lock->base.dep_map, 1, _RET_IP_); -+ else if (!ret && ww_ctx->acquired > 1) -+ return ww_mutex_deadlock_injection(lock, ww_ctx); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(__ww_mutex_lock_interruptible); -+ -+int __sched -+__ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ww_ctx) -+{ -+ int ret; -+ -+ might_sleep(); -+ -+ mutex_acquire_nest(&lock->base.dep_map, 0, 0, &ww_ctx->dep_map, _RET_IP_); -+ ret = rt_mutex_slowlock(&lock->base.lock, TASK_UNINTERRUPTIBLE, NULL, 0, ww_ctx); -+ if (ret) -+ mutex_release(&lock->base.dep_map, 1, _RET_IP_); -+ else if (!ret && ww_ctx->acquired > 1) -+ return ww_mutex_deadlock_injection(lock, ww_ctx); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(__ww_mutex_lock); -+ -+void __sched ww_mutex_unlock(struct ww_mutex *lock) -+{ -+ int nest = !!lock->ctx; -+ -+ /* -+ * The unlocking fastpath is the 0->1 transition from 'locked' -+ * into 'unlocked' state: -+ */ -+ if (nest) { -+#ifdef CONFIG_DEBUG_MUTEXES -+ DEBUG_LOCKS_WARN_ON(!lock->ctx->acquired); -+#endif -+ if (lock->ctx->acquired > 0) -+ lock->ctx->acquired--; -+ lock->ctx = NULL; -+ } -+ -+ mutex_release(&lock->base.dep_map, nest, _RET_IP_); -+ rt_mutex_unlock(&lock->base.lock); -+} -+EXPORT_SYMBOL(ww_mutex_unlock); -+#endif -diff -Nur linux-3.18.14.orig/kernel/locking/rtmutex_common.h linux-3.18.14-rt/kernel/locking/rtmutex_common.h ---- linux-3.18.14.orig/kernel/locking/rtmutex_common.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/locking/rtmutex_common.h 2015-05-31 15:32:48.769635364 -0500 -@@ -49,6 +49,7 @@ - struct rb_node pi_tree_entry; - struct task_struct *task; - struct rt_mutex *lock; -+ bool savestate; - #ifdef CONFIG_DEBUG_RT_MUTEXES - unsigned long ip; - struct pid *deadlock_task_pid; -@@ -119,6 +120,9 @@ - /* - * PI-futex support (proxy locking functions, etc.): - */ -+#define PI_WAKEUP_INPROGRESS ((struct rt_mutex_waiter *) 1) -+#define PI_REQUEUE_INPROGRESS ((struct rt_mutex_waiter *) 2) -+ - extern struct task_struct *rt_mutex_next_owner(struct rt_mutex *lock); - extern void rt_mutex_init_proxy_locked(struct rt_mutex *lock, - struct task_struct *proxy_owner); -@@ -138,4 +142,14 @@ - # include "rtmutex.h" - #endif - -+static inline void -+rt_mutex_init_waiter(struct rt_mutex_waiter *waiter, bool savestate) -+{ -+ debug_rt_mutex_init_waiter(waiter); -+ waiter->task = NULL; -+ waiter->savestate = savestate; -+ RB_CLEAR_NODE(&waiter->pi_tree_entry); -+ RB_CLEAR_NODE(&waiter->tree_entry); -+} -+ - #endif -diff -Nur linux-3.18.14.orig/kernel/locking/spinlock.c linux-3.18.14-rt/kernel/locking/spinlock.c ---- linux-3.18.14.orig/kernel/locking/spinlock.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/locking/spinlock.c 2015-05-31 15:32:48.769635364 -0500 -@@ -124,8 +124,11 @@ - * __[spin|read|write]_lock_bh() - */ - BUILD_LOCK_OPS(spin, raw_spinlock); -+ -+#ifndef CONFIG_PREEMPT_RT_FULL - BUILD_LOCK_OPS(read, rwlock); - BUILD_LOCK_OPS(write, rwlock); -+#endif - - #endif - -@@ -209,6 +212,8 @@ - EXPORT_SYMBOL(_raw_spin_unlock_bh); - #endif - -+#ifndef CONFIG_PREEMPT_RT_FULL -+ - #ifndef CONFIG_INLINE_READ_TRYLOCK - int __lockfunc _raw_read_trylock(rwlock_t *lock) - { -@@ -353,6 +358,8 @@ - EXPORT_SYMBOL(_raw_write_unlock_bh); - #endif - -+#endif /* !PREEMPT_RT_FULL */ -+ - #ifdef CONFIG_DEBUG_LOCK_ALLOC - - void __lockfunc _raw_spin_lock_nested(raw_spinlock_t *lock, int subclass) -diff -Nur linux-3.18.14.orig/kernel/locking/spinlock_debug.c linux-3.18.14-rt/kernel/locking/spinlock_debug.c ---- linux-3.18.14.orig/kernel/locking/spinlock_debug.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/locking/spinlock_debug.c 2015-05-31 15:32:48.793635364 -0500 -@@ -31,6 +31,7 @@ - - EXPORT_SYMBOL(__raw_spin_lock_init); - -+#ifndef CONFIG_PREEMPT_RT_FULL - void __rwlock_init(rwlock_t *lock, const char *name, - struct lock_class_key *key) - { -@@ -48,6 +49,7 @@ - } - - EXPORT_SYMBOL(__rwlock_init); -+#endif - - static void spin_dump(raw_spinlock_t *lock, const char *msg) - { -@@ -159,6 +161,7 @@ - arch_spin_unlock(&lock->raw_lock); - } - -+#ifndef CONFIG_PREEMPT_RT_FULL - static void rwlock_bug(rwlock_t *lock, const char *msg) - { - if (!debug_locks_off()) -@@ -300,3 +303,5 @@ - debug_write_unlock(lock); - arch_write_unlock(&lock->raw_lock); - } -+ -+#endif -diff -Nur linux-3.18.14.orig/kernel/panic.c linux-3.18.14-rt/kernel/panic.c ---- linux-3.18.14.orig/kernel/panic.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/panic.c 2015-05-31 15:32:48.793635364 -0500 -@@ -384,9 +384,11 @@ - - static int init_oops_id(void) - { -+#ifndef CONFIG_PREEMPT_RT_FULL - if (!oops_id) - get_random_bytes(&oops_id, sizeof(oops_id)); - else -+#endif - oops_id++; - - return 0; -diff -Nur linux-3.18.14.orig/kernel/power/hibernate.c linux-3.18.14-rt/kernel/power/hibernate.c ---- linux-3.18.14.orig/kernel/power/hibernate.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/power/hibernate.c 2015-05-31 15:32:48.797635364 -0500 -@@ -287,6 +287,8 @@ - - local_irq_disable(); - -+ system_state = SYSTEM_SUSPEND; -+ - error = syscore_suspend(); - if (error) { - printk(KERN_ERR "PM: Some system devices failed to power down, " -@@ -316,6 +318,7 @@ - syscore_resume(); - - Enable_irqs: -+ system_state = SYSTEM_RUNNING; - local_irq_enable(); - - Enable_cpus: -@@ -439,6 +442,7 @@ - goto Enable_cpus; - - local_irq_disable(); -+ system_state = SYSTEM_SUSPEND; - - error = syscore_suspend(); - if (error) -@@ -472,6 +476,7 @@ - syscore_resume(); - - Enable_irqs: -+ system_state = SYSTEM_RUNNING; - local_irq_enable(); - - Enable_cpus: -@@ -557,6 +562,7 @@ - goto Platform_finish; - - local_irq_disable(); -+ system_state = SYSTEM_SUSPEND; - syscore_suspend(); - if (pm_wakeup_pending()) { - error = -EAGAIN; -@@ -569,6 +575,7 @@ - - Power_up: - syscore_resume(); -+ system_state = SYSTEM_RUNNING; - local_irq_enable(); - enable_nonboot_cpus(); - -diff -Nur linux-3.18.14.orig/kernel/power/suspend.c linux-3.18.14-rt/kernel/power/suspend.c ---- linux-3.18.14.orig/kernel/power/suspend.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/power/suspend.c 2015-05-31 15:32:48.797635364 -0500 -@@ -318,6 +318,8 @@ - arch_suspend_disable_irqs(); - BUG_ON(!irqs_disabled()); - -+ system_state = SYSTEM_SUSPEND; -+ - error = syscore_suspend(); - if (!error) { - *wakeup = pm_wakeup_pending(); -@@ -332,6 +334,8 @@ - syscore_resume(); - } - -+ system_state = SYSTEM_RUNNING; -+ - arch_suspend_enable_irqs(); - BUG_ON(irqs_disabled()); - -diff -Nur linux-3.18.14.orig/kernel/printk/printk.c linux-3.18.14-rt/kernel/printk/printk.c ---- linux-3.18.14.orig/kernel/printk/printk.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/printk/printk.c 2015-05-31 15:32:48.801635363 -0500 -@@ -1165,6 +1165,7 @@ - { - char *text; - int len = 0; -+ int attempts = 0; - - text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL); - if (!text) -@@ -1176,7 +1177,14 @@ - u64 seq; - u32 idx; - enum log_flags prev; -- -+ int num_msg; -+try_again: -+ attempts++; -+ if (attempts > 10) { -+ len = -EBUSY; -+ goto out; -+ } -+ num_msg = 0; - if (clear_seq < log_first_seq) { - /* messages are gone, move to first available one */ - clear_seq = log_first_seq; -@@ -1197,6 +1205,14 @@ - prev = msg->flags; - idx = log_next(idx); - seq++; -+ num_msg++; -+ if (num_msg > 5) { -+ num_msg = 0; -+ raw_spin_unlock_irq(&logbuf_lock); -+ raw_spin_lock_irq(&logbuf_lock); -+ if (clear_seq < log_first_seq) -+ goto try_again; -+ } - } - - /* move first record forward until length fits into the buffer */ -@@ -1210,6 +1226,14 @@ - prev = msg->flags; - idx = log_next(idx); - seq++; -+ num_msg++; -+ if (num_msg > 5) { -+ num_msg = 0; -+ raw_spin_unlock_irq(&logbuf_lock); -+ raw_spin_lock_irq(&logbuf_lock); -+ if (clear_seq < log_first_seq) -+ goto try_again; -+ } - } - - /* last message fitting into this dump */ -@@ -1250,6 +1274,7 @@ - clear_seq = log_next_seq; - clear_idx = log_next_idx; - } -+out: - raw_spin_unlock_irq(&logbuf_lock); - - kfree(text); -@@ -1407,6 +1432,7 @@ - if (!console_drivers) - return; - -+ migrate_disable(); - for_each_console(con) { - if (exclusive_console && con != exclusive_console) - continue; -@@ -1419,6 +1445,7 @@ - continue; - con->write(con, text, len); - } -+ migrate_enable(); - } - - /* -@@ -1479,6 +1506,15 @@ - static int console_trylock_for_printk(void) - { - unsigned int cpu = smp_processor_id(); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ int lock = !early_boot_irqs_disabled && (preempt_count() == 0) && -+ !irqs_disabled(); -+#else -+ int lock = 1; -+#endif -+ -+ if (!lock) -+ return 0; - - if (!console_trylock()) - return 0; -@@ -1613,6 +1649,62 @@ - return textlen; - } - -+#ifdef CONFIG_EARLY_PRINTK -+struct console *early_console; -+ -+void early_vprintk(const char *fmt, va_list ap) -+{ -+ if (early_console) { -+ char buf[512]; -+ int n = vscnprintf(buf, sizeof(buf), fmt, ap); -+ -+ early_console->write(early_console, buf, n); -+ } -+} -+ -+asmlinkage void early_printk(const char *fmt, ...) -+{ -+ va_list ap; -+ -+ va_start(ap, fmt); -+ early_vprintk(fmt, ap); -+ va_end(ap); -+} -+ -+/* -+ * This is independent of any log levels - a global -+ * kill switch that turns off all of printk. -+ * -+ * Used by the NMI watchdog if early-printk is enabled. -+ */ -+static bool __read_mostly printk_killswitch; -+ -+static int __init force_early_printk_setup(char *str) -+{ -+ printk_killswitch = true; -+ return 0; -+} -+early_param("force_early_printk", force_early_printk_setup); -+ -+void printk_kill(void) -+{ -+ printk_killswitch = true; -+} -+ -+static int forced_early_printk(const char *fmt, va_list ap) -+{ -+ if (!printk_killswitch) -+ return 0; -+ early_vprintk(fmt, ap); -+ return 1; -+} -+#else -+static inline int forced_early_printk(const char *fmt, va_list ap) -+{ -+ return 0; -+} -+#endif -+ - asmlinkage int vprintk_emit(int facility, int level, - const char *dict, size_t dictlen, - const char *fmt, va_list args) -@@ -1629,6 +1721,13 @@ - /* cpu currently holding logbuf_lock in this function */ - static volatile unsigned int logbuf_cpu = UINT_MAX; - -+ /* -+ * Fall back to early_printk if a debugging subsystem has -+ * killed printk output -+ */ -+ if (unlikely(forced_early_printk(fmt, args))) -+ return 1; -+ - if (level == SCHED_MESSAGE_LOGLEVEL) { - level = -1; - in_sched = true; -@@ -1769,8 +1868,7 @@ - * console_sem which would prevent anyone from printing to - * console - */ -- preempt_disable(); -- -+ migrate_disable(); - /* - * Try to acquire and then immediately release the console - * semaphore. The release will print out buffers and wake up -@@ -1778,7 +1876,7 @@ - */ - if (console_trylock_for_printk()) - console_unlock(); -- preempt_enable(); -+ migrate_enable(); - lockdep_on(); - } - -@@ -1878,29 +1976,6 @@ - - #endif /* CONFIG_PRINTK */ - --#ifdef CONFIG_EARLY_PRINTK --struct console *early_console; -- --void early_vprintk(const char *fmt, va_list ap) --{ -- if (early_console) { -- char buf[512]; -- int n = vscnprintf(buf, sizeof(buf), fmt, ap); -- -- early_console->write(early_console, buf, n); -- } --} -- --asmlinkage __visible void early_printk(const char *fmt, ...) --{ -- va_list ap; -- -- va_start(ap, fmt); -- early_vprintk(fmt, ap); -- va_end(ap); --} --#endif -- - static int __add_preferred_console(char *name, int idx, char *options, - char *brl_options) - { -@@ -2140,11 +2215,16 @@ - goto out; - - len = cont_print_text(text, size); -+#ifndef CONFIG_PREEMPT_RT_FULL - raw_spin_unlock(&logbuf_lock); - stop_critical_timings(); - call_console_drivers(cont.level, text, len); - start_critical_timings(); - local_irq_restore(flags); -+#else -+ raw_spin_unlock_irqrestore(&logbuf_lock, flags); -+ call_console_drivers(cont.level, text, len); -+#endif - return; - out: - raw_spin_unlock_irqrestore(&logbuf_lock, flags); -@@ -2232,12 +2312,17 @@ - console_idx = log_next(console_idx); - console_seq++; - console_prev = msg->flags; -+#ifdef CONFIG_PREEMPT_RT_FULL -+ raw_spin_unlock_irqrestore(&logbuf_lock, flags); -+ call_console_drivers(level, text, len); -+#else - raw_spin_unlock(&logbuf_lock); - - stop_critical_timings(); /* don't trace print latency */ - call_console_drivers(level, text, len); - start_critical_timings(); - local_irq_restore(flags); -+#endif - } - console_locked = 0; - -diff -Nur linux-3.18.14.orig/kernel/ptrace.c linux-3.18.14-rt/kernel/ptrace.c ---- linux-3.18.14.orig/kernel/ptrace.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/ptrace.c 2015-05-31 15:32:48.801635363 -0500 -@@ -129,7 +129,12 @@ - - spin_lock_irq(&task->sighand->siglock); - if (task_is_traced(task) && !__fatal_signal_pending(task)) { -- task->state = __TASK_TRACED; -+ raw_spin_lock_irq(&task->pi_lock); -+ if (task->state & __TASK_TRACED) -+ task->state = __TASK_TRACED; -+ else -+ task->saved_state = __TASK_TRACED; -+ raw_spin_unlock_irq(&task->pi_lock); - ret = true; - } - spin_unlock_irq(&task->sighand->siglock); -diff -Nur linux-3.18.14.orig/kernel/rcu/tiny.c linux-3.18.14-rt/kernel/rcu/tiny.c ---- linux-3.18.14.orig/kernel/rcu/tiny.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/rcu/tiny.c 2015-05-31 15:32:48.801635363 -0500 -@@ -370,6 +370,7 @@ - } - EXPORT_SYMBOL_GPL(call_rcu_sched); - -+#ifndef CONFIG_PREEMPT_RT_FULL - /* - * Post an RCU bottom-half callback to be invoked after any subsequent - * quiescent state. -@@ -379,6 +380,7 @@ - __call_rcu(head, func, &rcu_bh_ctrlblk); - } - EXPORT_SYMBOL_GPL(call_rcu_bh); -+#endif - - void rcu_init(void) - { -diff -Nur linux-3.18.14.orig/kernel/rcu/tree.c linux-3.18.14-rt/kernel/rcu/tree.c ---- linux-3.18.14.orig/kernel/rcu/tree.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/rcu/tree.c 2015-05-31 15:32:48.805635363 -0500 -@@ -56,6 +56,11 @@ - #include - #include - #include -+#include -+#include -+#include -+#include -+#include "../time/tick-internal.h" - - #include "tree.h" - #include "rcu.h" -@@ -152,8 +157,6 @@ - */ - static int rcu_scheduler_fully_active __read_mostly; - --#ifdef CONFIG_RCU_BOOST -- - /* - * Control variables for per-CPU and per-rcu_node kthreads. These - * handle all flavors of RCU. -@@ -163,8 +166,6 @@ - DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); - DEFINE_PER_CPU(char, rcu_cpu_has_work); - --#endif /* #ifdef CONFIG_RCU_BOOST */ -- - static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu); - static void invoke_rcu_core(void); - static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp); -@@ -207,6 +208,19 @@ - } - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+static void rcu_preempt_qs(void); -+ -+void rcu_bh_qs(void) -+{ -+ unsigned long flags; -+ -+ /* Callers to this function, rcu_preempt_qs(), must disable irqs. */ -+ local_irq_save(flags); -+ rcu_preempt_qs(); -+ local_irq_restore(flags); -+} -+#else - void rcu_bh_qs(void) - { - if (!__this_cpu_read(rcu_bh_data.passed_quiesce)) { -@@ -216,6 +230,7 @@ - __this_cpu_write(rcu_bh_data.passed_quiesce, 1); - } - } -+#endif - - static DEFINE_PER_CPU(int, rcu_sched_qs_mask); - -@@ -336,6 +351,7 @@ - } - EXPORT_SYMBOL_GPL(rcu_batches_completed_sched); - -+#ifndef CONFIG_PREEMPT_RT_FULL - /* - * Return the number of RCU BH batches processed thus far for debug & stats. - */ -@@ -363,6 +379,13 @@ - } - EXPORT_SYMBOL_GPL(rcu_bh_force_quiescent_state); - -+#else -+void rcu_force_quiescent_state(void) -+{ -+} -+EXPORT_SYMBOL_GPL(rcu_force_quiescent_state); -+#endif -+ - /* - * Show the state of the grace-period kthreads. - */ -@@ -1411,7 +1434,7 @@ - !ACCESS_ONCE(rsp->gp_flags) || - !rsp->gp_kthread) - return; -- wake_up(&rsp->gp_wq); -+ swait_wake(&rsp->gp_wq); - } - - /* -@@ -1793,7 +1816,7 @@ - ACCESS_ONCE(rsp->gpnum), - TPS("reqwait")); - rsp->gp_state = RCU_GP_WAIT_GPS; -- wait_event_interruptible(rsp->gp_wq, -+ swait_event_interruptible(rsp->gp_wq, - ACCESS_ONCE(rsp->gp_flags) & - RCU_GP_FLAG_INIT); - /* Locking provides needed memory barrier. */ -@@ -1821,7 +1844,7 @@ - ACCESS_ONCE(rsp->gpnum), - TPS("fqswait")); - rsp->gp_state = RCU_GP_WAIT_FQS; -- ret = wait_event_interruptible_timeout(rsp->gp_wq, -+ ret = swait_event_interruptible_timeout(rsp->gp_wq, - ((gf = ACCESS_ONCE(rsp->gp_flags)) & - RCU_GP_FLAG_FQS) || - (!ACCESS_ONCE(rnp->qsmask) && -@@ -2565,16 +2588,14 @@ - /* - * Do RCU core processing for the current CPU. - */ --static void rcu_process_callbacks(struct softirq_action *unused) -+static void rcu_process_callbacks(void) - { - struct rcu_state *rsp; - - if (cpu_is_offline(smp_processor_id())) - return; -- trace_rcu_utilization(TPS("Start RCU core")); - for_each_rcu_flavor(rsp) - __rcu_process_callbacks(rsp); -- trace_rcu_utilization(TPS("End RCU core")); - } - - /* -@@ -2588,18 +2609,105 @@ - { - if (unlikely(!ACCESS_ONCE(rcu_scheduler_fully_active))) - return; -- if (likely(!rsp->boost)) { -- rcu_do_batch(rsp, rdp); -+ rcu_do_batch(rsp, rdp); -+} -+ -+static void rcu_wake_cond(struct task_struct *t, int status) -+{ -+ /* -+ * If the thread is yielding, only wake it when this -+ * is invoked from idle -+ */ -+ if (t && (status != RCU_KTHREAD_YIELDING || is_idle_task(current))) -+ wake_up_process(t); -+} -+ -+/* -+ * Wake up this CPU's rcuc kthread to do RCU core processing. -+ */ -+static void invoke_rcu_core(void) -+{ -+ unsigned long flags; -+ struct task_struct *t; -+ -+ if (!cpu_online(smp_processor_id())) - return; -+ local_irq_save(flags); -+ __this_cpu_write(rcu_cpu_has_work, 1); -+ t = __this_cpu_read(rcu_cpu_kthread_task); -+ if (t != NULL && current != t) -+ rcu_wake_cond(t, __this_cpu_read(rcu_cpu_kthread_status)); -+ local_irq_restore(flags); -+} -+ -+static void rcu_cpu_kthread_park(unsigned int cpu) -+{ -+ per_cpu(rcu_cpu_kthread_status, cpu) = RCU_KTHREAD_OFFCPU; -+} -+ -+static int rcu_cpu_kthread_should_run(unsigned int cpu) -+{ -+ return __this_cpu_read(rcu_cpu_has_work); -+} -+ -+/* -+ * Per-CPU kernel thread that invokes RCU callbacks. This replaces the -+ * RCU softirq used in flavors and configurations of RCU that do not -+ * support RCU priority boosting. -+ */ -+static void rcu_cpu_kthread(unsigned int cpu) -+{ -+ unsigned int *statusp = &__get_cpu_var(rcu_cpu_kthread_status); -+ char work, *workp = &__get_cpu_var(rcu_cpu_has_work); -+ int spincnt; -+ -+ for (spincnt = 0; spincnt < 10; spincnt++) { -+ trace_rcu_utilization(TPS("Start CPU kthread@rcu_wait")); -+ local_bh_disable(); -+ *statusp = RCU_KTHREAD_RUNNING; -+ this_cpu_inc(rcu_cpu_kthread_loops); -+ local_irq_disable(); -+ work = *workp; -+ *workp = 0; -+ local_irq_enable(); -+ if (work) -+ rcu_process_callbacks(); -+ local_bh_enable(); -+ if (*workp == 0) { -+ trace_rcu_utilization(TPS("End CPU kthread@rcu_wait")); -+ *statusp = RCU_KTHREAD_WAITING; -+ return; -+ } - } -- invoke_rcu_callbacks_kthread(); -+ *statusp = RCU_KTHREAD_YIELDING; -+ trace_rcu_utilization(TPS("Start CPU kthread@rcu_yield")); -+ schedule_timeout_interruptible(2); -+ trace_rcu_utilization(TPS("End CPU kthread@rcu_yield")); -+ *statusp = RCU_KTHREAD_WAITING; - } - --static void invoke_rcu_core(void) -+static struct smp_hotplug_thread rcu_cpu_thread_spec = { -+ .store = &rcu_cpu_kthread_task, -+ .thread_should_run = rcu_cpu_kthread_should_run, -+ .thread_fn = rcu_cpu_kthread, -+ .thread_comm = "rcuc/%u", -+ .setup = rcu_cpu_kthread_setup, -+ .park = rcu_cpu_kthread_park, -+}; -+ -+/* -+ * Spawn per-CPU RCU core processing kthreads. -+ */ -+static int __init rcu_spawn_core_kthreads(void) - { -- if (cpu_online(smp_processor_id())) -- raise_softirq(RCU_SOFTIRQ); -+ int cpu; -+ -+ for_each_possible_cpu(cpu) -+ per_cpu(rcu_cpu_has_work, cpu) = 0; -+ BUG_ON(smpboot_register_percpu_thread(&rcu_cpu_thread_spec)); -+ return 0; - } -+early_initcall(rcu_spawn_core_kthreads); - - /* - * Handle any core-RCU processing required by a call_rcu() invocation. -@@ -2734,6 +2842,7 @@ - } - EXPORT_SYMBOL_GPL(call_rcu_sched); - -+#ifndef CONFIG_PREEMPT_RT_FULL - /* - * Queue an RCU callback for invocation after a quicker grace period. - */ -@@ -2742,6 +2851,7 @@ - __call_rcu(head, func, &rcu_bh_state, -1, 0); - } - EXPORT_SYMBOL_GPL(call_rcu_bh); -+#endif - - /* - * Queue an RCU callback for lazy invocation after a grace period. -@@ -2833,6 +2943,7 @@ - } - EXPORT_SYMBOL_GPL(synchronize_sched); - -+#ifndef CONFIG_PREEMPT_RT_FULL - /** - * synchronize_rcu_bh - wait until an rcu_bh grace period has elapsed. - * -@@ -2859,6 +2970,7 @@ - wait_rcu_gp(call_rcu_bh); - } - EXPORT_SYMBOL_GPL(synchronize_rcu_bh); -+#endif - - /** - * get_state_synchronize_rcu - Snapshot current RCU state -@@ -3341,6 +3453,7 @@ - mutex_unlock(&rsp->barrier_mutex); - } - -+#ifndef CONFIG_PREEMPT_RT_FULL - /** - * rcu_barrier_bh - Wait until all in-flight call_rcu_bh() callbacks complete. - */ -@@ -3349,6 +3462,7 @@ - _rcu_barrier(&rcu_bh_state); - } - EXPORT_SYMBOL_GPL(rcu_barrier_bh); -+#endif - - /** - * rcu_barrier_sched - Wait for in-flight call_rcu_sched() callbacks. -@@ -3658,7 +3772,7 @@ - } - - rsp->rda = rda; -- init_waitqueue_head(&rsp->gp_wq); -+ init_swait_head(&rsp->gp_wq); - rnp = rsp->level[rcu_num_lvls - 1]; - for_each_possible_cpu(i) { - while (i > rnp->grphi) -@@ -3755,7 +3869,6 @@ - rcu_init_one(&rcu_bh_state, &rcu_bh_data); - rcu_init_one(&rcu_sched_state, &rcu_sched_data); - __rcu_init_preempt(); -- open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); - - /* - * We don't need protection against CPU-hotplug here because -diff -Nur linux-3.18.14.orig/kernel/rcu/tree.h linux-3.18.14-rt/kernel/rcu/tree.h ---- linux-3.18.14.orig/kernel/rcu/tree.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/rcu/tree.h 2015-05-31 15:32:48.809635364 -0500 -@@ -28,6 +28,7 @@ - #include - #include - #include -+#include - - /* - * Define shape of hierarchy based on NR_CPUS, CONFIG_RCU_FANOUT, and -@@ -172,11 +173,6 @@ - /* queued on this rcu_node structure that */ - /* are blocking the current grace period, */ - /* there can be no such task. */ -- struct completion boost_completion; -- /* Used to ensure that the rt_mutex used */ -- /* to carry out the boosting is fully */ -- /* released with no future boostee accesses */ -- /* before that rt_mutex is re-initialized. */ - struct rt_mutex boost_mtx; - /* Used only for the priority-boosting */ - /* side effect, not as a lock. */ -@@ -208,7 +204,7 @@ - /* This can happen due to race conditions. */ - #endif /* #ifdef CONFIG_RCU_BOOST */ - #ifdef CONFIG_RCU_NOCB_CPU -- wait_queue_head_t nocb_gp_wq[2]; -+ struct swait_head nocb_gp_wq[2]; - /* Place for rcu_nocb_kthread() to wait GP. */ - #endif /* #ifdef CONFIG_RCU_NOCB_CPU */ - int need_future_gp[2]; -@@ -348,7 +344,7 @@ - atomic_long_t nocb_follower_count_lazy; /* (approximate). */ - int nocb_p_count; /* # CBs being invoked by kthread */ - int nocb_p_count_lazy; /* (approximate). */ -- wait_queue_head_t nocb_wq; /* For nocb kthreads to sleep on. */ -+ struct swait_head nocb_wq; /* For nocb kthreads to sleep on. */ - struct task_struct *nocb_kthread; - int nocb_defer_wakeup; /* Defer wakeup of nocb_kthread. */ - -@@ -439,7 +435,7 @@ - unsigned long gpnum; /* Current gp number. */ - unsigned long completed; /* # of last completed gp. */ - struct task_struct *gp_kthread; /* Task for grace periods. */ -- wait_queue_head_t gp_wq; /* Where GP task waits. */ -+ struct swait_head gp_wq; /* Where GP task waits. */ - short gp_flags; /* Commands for GP task. */ - short gp_state; /* GP kthread sleep state. */ - -@@ -570,10 +566,9 @@ - static void __init __rcu_init_preempt(void); - static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags); - static void rcu_preempt_boost_start_gp(struct rcu_node *rnp); --static void invoke_rcu_callbacks_kthread(void); - static bool rcu_is_callbacks_kthread(void); -+static void rcu_cpu_kthread_setup(unsigned int cpu); - #ifdef CONFIG_RCU_BOOST --static void rcu_preempt_do_callbacks(void); - static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp, - struct rcu_node *rnp); - #endif /* #ifdef CONFIG_RCU_BOOST */ -diff -Nur linux-3.18.14.orig/kernel/rcu/tree_plugin.h linux-3.18.14-rt/kernel/rcu/tree_plugin.h ---- linux-3.18.14.orig/kernel/rcu/tree_plugin.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/rcu/tree_plugin.h 2015-05-31 15:32:48.829635363 -0500 -@@ -24,12 +24,6 @@ - * Paul E. McKenney - */ - --#include --#include --#include --#include --#include "../time/tick-internal.h" -- - #define RCU_KTHREAD_PRIO 1 - - #ifdef CONFIG_RCU_BOOST -@@ -335,7 +329,7 @@ - } - - /* Hardware IRQ handlers cannot block, complain if they get here. */ -- if (WARN_ON_ONCE(in_irq() || in_serving_softirq())) { -+ if (WARN_ON_ONCE(preempt_count() & (HARDIRQ_MASK | SOFTIRQ_OFFSET))) { - local_irq_restore(flags); - return; - } -@@ -398,10 +392,8 @@ - - #ifdef CONFIG_RCU_BOOST - /* Unboost if we were boosted. */ -- if (drop_boost_mutex) { -+ if (drop_boost_mutex) - rt_mutex_unlock(&rnp->boost_mtx); -- complete(&rnp->boost_completion); -- } - #endif /* #ifdef CONFIG_RCU_BOOST */ - - /* -@@ -635,15 +627,6 @@ - t->rcu_read_unlock_special.b.need_qs = true; - } - --#ifdef CONFIG_RCU_BOOST -- --static void rcu_preempt_do_callbacks(void) --{ -- rcu_do_batch(&rcu_preempt_state, this_cpu_ptr(&rcu_preempt_data)); --} -- --#endif /* #ifdef CONFIG_RCU_BOOST */ -- - /* - * Queue a preemptible-RCU callback for invocation after a grace period. - */ -@@ -1072,6 +1055,19 @@ - - #endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */ - -+/* -+ * If boosting, set rcuc kthreads to realtime priority. -+ */ -+static void rcu_cpu_kthread_setup(unsigned int cpu) -+{ -+#ifdef CONFIG_RCU_BOOST -+ struct sched_param sp; -+ -+ sp.sched_priority = RCU_KTHREAD_PRIO; -+ sched_setscheduler_nocheck(current, SCHED_FIFO, &sp); -+#endif /* #ifdef CONFIG_RCU_BOOST */ -+} -+ - #ifdef CONFIG_RCU_BOOST - - #include "../locking/rtmutex_common.h" -@@ -1103,16 +1099,6 @@ - - #endif /* #else #ifdef CONFIG_RCU_TRACE */ - --static void rcu_wake_cond(struct task_struct *t, int status) --{ -- /* -- * If the thread is yielding, only wake it when this -- * is invoked from idle -- */ -- if (status != RCU_KTHREAD_YIELDING || is_idle_task(current)) -- wake_up_process(t); --} -- - /* - * Carry out RCU priority boosting on the task indicated by ->exp_tasks - * or ->boost_tasks, advancing the pointer to the next task in the -@@ -1175,15 +1161,11 @@ - */ - t = container_of(tb, struct task_struct, rcu_node_entry); - rt_mutex_init_proxy_locked(&rnp->boost_mtx, t); -- init_completion(&rnp->boost_completion); - raw_spin_unlock_irqrestore(&rnp->lock, flags); - /* Lock only for side effect: boosts task t's priority. */ - rt_mutex_lock(&rnp->boost_mtx); - rt_mutex_unlock(&rnp->boost_mtx); /* Then keep lockdep happy. */ - -- /* Wait for boostee to be done w/boost_mtx before reinitializing. */ -- wait_for_completion(&rnp->boost_completion); -- - return ACCESS_ONCE(rnp->exp_tasks) != NULL || - ACCESS_ONCE(rnp->boost_tasks) != NULL; - } -@@ -1261,23 +1243,6 @@ - } - - /* -- * Wake up the per-CPU kthread to invoke RCU callbacks. -- */ --static void invoke_rcu_callbacks_kthread(void) --{ -- unsigned long flags; -- -- local_irq_save(flags); -- __this_cpu_write(rcu_cpu_has_work, 1); -- if (__this_cpu_read(rcu_cpu_kthread_task) != NULL && -- current != __this_cpu_read(rcu_cpu_kthread_task)) { -- rcu_wake_cond(__this_cpu_read(rcu_cpu_kthread_task), -- __this_cpu_read(rcu_cpu_kthread_status)); -- } -- local_irq_restore(flags); --} -- --/* - * Is the current CPU running the RCU-callbacks kthread? - * Caller must have preemption disabled. - */ -@@ -1332,67 +1297,6 @@ - return 0; - } - --static void rcu_kthread_do_work(void) --{ -- rcu_do_batch(&rcu_sched_state, this_cpu_ptr(&rcu_sched_data)); -- rcu_do_batch(&rcu_bh_state, this_cpu_ptr(&rcu_bh_data)); -- rcu_preempt_do_callbacks(); --} -- --static void rcu_cpu_kthread_setup(unsigned int cpu) --{ -- struct sched_param sp; -- -- sp.sched_priority = RCU_KTHREAD_PRIO; -- sched_setscheduler_nocheck(current, SCHED_FIFO, &sp); --} -- --static void rcu_cpu_kthread_park(unsigned int cpu) --{ -- per_cpu(rcu_cpu_kthread_status, cpu) = RCU_KTHREAD_OFFCPU; --} -- --static int rcu_cpu_kthread_should_run(unsigned int cpu) --{ -- return __this_cpu_read(rcu_cpu_has_work); --} -- --/* -- * Per-CPU kernel thread that invokes RCU callbacks. This replaces the -- * RCU softirq used in flavors and configurations of RCU that do not -- * support RCU priority boosting. -- */ --static void rcu_cpu_kthread(unsigned int cpu) --{ -- unsigned int *statusp = this_cpu_ptr(&rcu_cpu_kthread_status); -- char work, *workp = this_cpu_ptr(&rcu_cpu_has_work); -- int spincnt; -- -- for (spincnt = 0; spincnt < 10; spincnt++) { -- trace_rcu_utilization(TPS("Start CPU kthread@rcu_wait")); -- local_bh_disable(); -- *statusp = RCU_KTHREAD_RUNNING; -- this_cpu_inc(rcu_cpu_kthread_loops); -- local_irq_disable(); -- work = *workp; -- *workp = 0; -- local_irq_enable(); -- if (work) -- rcu_kthread_do_work(); -- local_bh_enable(); -- if (*workp == 0) { -- trace_rcu_utilization(TPS("End CPU kthread@rcu_wait")); -- *statusp = RCU_KTHREAD_WAITING; -- return; -- } -- } -- *statusp = RCU_KTHREAD_YIELDING; -- trace_rcu_utilization(TPS("Start CPU kthread@rcu_yield")); -- schedule_timeout_interruptible(2); -- trace_rcu_utilization(TPS("End CPU kthread@rcu_yield")); -- *statusp = RCU_KTHREAD_WAITING; --} -- - /* - * Set the per-rcu_node kthread's affinity to cover all CPUs that are - * served by the rcu_node in question. The CPU hotplug lock is still -@@ -1426,26 +1330,13 @@ - free_cpumask_var(cm); - } - --static struct smp_hotplug_thread rcu_cpu_thread_spec = { -- .store = &rcu_cpu_kthread_task, -- .thread_should_run = rcu_cpu_kthread_should_run, -- .thread_fn = rcu_cpu_kthread, -- .thread_comm = "rcuc/%u", -- .setup = rcu_cpu_kthread_setup, -- .park = rcu_cpu_kthread_park, --}; -- - /* - * Spawn boost kthreads -- called as soon as the scheduler is running. - */ - static void __init rcu_spawn_boost_kthreads(void) - { - struct rcu_node *rnp; -- int cpu; - -- for_each_possible_cpu(cpu) -- per_cpu(rcu_cpu_has_work, cpu) = 0; -- BUG_ON(smpboot_register_percpu_thread(&rcu_cpu_thread_spec)); - rnp = rcu_get_root(rcu_state_p); - (void)rcu_spawn_one_boost_kthread(rcu_state_p, rnp); - if (NUM_RCU_NODES > 1) { -@@ -1472,11 +1363,6 @@ - raw_spin_unlock_irqrestore(&rnp->lock, flags); - } - --static void invoke_rcu_callbacks_kthread(void) --{ -- WARN_ON_ONCE(1); --} -- - static bool rcu_is_callbacks_kthread(void) - { - return false; -@@ -1500,7 +1386,7 @@ - - #endif /* #else #ifdef CONFIG_RCU_BOOST */ - --#if !defined(CONFIG_RCU_FAST_NO_HZ) -+#if !defined(CONFIG_RCU_FAST_NO_HZ) || defined(CONFIG_PREEMPT_RT_FULL) - - /* - * Check to see if any future RCU-related work will need to be done -@@ -1518,7 +1404,9 @@ - return rcu_cpu_has_callbacks(cpu, NULL); - } - #endif /* #ifndef CONFIG_RCU_NOCB_CPU_ALL */ -+#endif /* !defined(CONFIG_RCU_FAST_NO_HZ) || defined(CONFIG_PREEMPT_RT_FULL) */ - -+#if !defined(CONFIG_RCU_FAST_NO_HZ) - /* - * Because we do not have RCU_FAST_NO_HZ, don't bother cleaning up - * after it. -@@ -1615,6 +1503,8 @@ - return cbs_ready; - } - -+#ifndef CONFIG_PREEMPT_RT_FULL -+ - /* - * Allow the CPU to enter dyntick-idle mode unless it has callbacks ready - * to invoke. If the CPU has callbacks, try to advance them. Tell the -@@ -1655,7 +1545,7 @@ - return 0; - } - #endif /* #ifndef CONFIG_RCU_NOCB_CPU_ALL */ -- -+#endif /* #ifndef CONFIG_PREEMPT_RT_FULL */ - /* - * Prepare a CPU for idle from an RCU perspective. The first major task - * is to sense whether nohz mode has been enabled or disabled via sysfs. -@@ -2001,7 +1891,7 @@ - */ - static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) - { -- wake_up_all(&rnp->nocb_gp_wq[rnp->completed & 0x1]); -+ swait_wake_all(&rnp->nocb_gp_wq[rnp->completed & 0x1]); - } - - /* -@@ -2019,8 +1909,8 @@ - - static void rcu_init_one_nocb(struct rcu_node *rnp) - { -- init_waitqueue_head(&rnp->nocb_gp_wq[0]); -- init_waitqueue_head(&rnp->nocb_gp_wq[1]); -+ init_swait_head(&rnp->nocb_gp_wq[0]); -+ init_swait_head(&rnp->nocb_gp_wq[1]); - } - - #ifndef CONFIG_RCU_NOCB_CPU_ALL -@@ -2045,7 +1935,7 @@ - if (ACCESS_ONCE(rdp_leader->nocb_leader_sleep) || force) { - /* Prior smp_mb__after_atomic() orders against prior enqueue. */ - ACCESS_ONCE(rdp_leader->nocb_leader_sleep) = false; -- wake_up(&rdp_leader->nocb_wq); -+ swait_wake(&rdp_leader->nocb_wq); - } - } - -@@ -2238,7 +2128,7 @@ - */ - trace_rcu_future_gp(rnp, rdp, c, TPS("StartWait")); - for (;;) { -- wait_event_interruptible( -+ swait_event_interruptible( - rnp->nocb_gp_wq[c & 0x1], - (d = ULONG_CMP_GE(ACCESS_ONCE(rnp->completed), c))); - if (likely(d)) -@@ -2266,7 +2156,7 @@ - /* Wait for callbacks to appear. */ - if (!rcu_nocb_poll) { - trace_rcu_nocb_wake(my_rdp->rsp->name, my_rdp->cpu, "Sleep"); -- wait_event_interruptible(my_rdp->nocb_wq, -+ swait_event_interruptible(my_rdp->nocb_wq, - !ACCESS_ONCE(my_rdp->nocb_leader_sleep)); - /* Memory barrier handled by smp_mb() calls below and repoll. */ - } else if (firsttime) { -@@ -2347,7 +2237,7 @@ - * List was empty, wake up the follower. - * Memory barriers supplied by atomic_long_add(). - */ -- wake_up(&rdp->nocb_wq); -+ swait_wake(&rdp->nocb_wq); - } - } - -@@ -2368,7 +2258,7 @@ - if (!rcu_nocb_poll) { - trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, - "FollowerSleep"); -- wait_event_interruptible(rdp->nocb_wq, -+ swait_event_interruptible(rdp->nocb_wq, - ACCESS_ONCE(rdp->nocb_follower_head)); - } else if (firsttime) { - /* Don't drown trace log with "Poll"! */ -@@ -2539,7 +2429,7 @@ - static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp) - { - rdp->nocb_tail = &rdp->nocb_head; -- init_waitqueue_head(&rdp->nocb_wq); -+ init_swait_head(&rdp->nocb_wq); - rdp->nocb_follower_tail = &rdp->nocb_follower_head; - } - -diff -Nur linux-3.18.14.orig/kernel/rcu/update.c linux-3.18.14-rt/kernel/rcu/update.c ---- linux-3.18.14.orig/kernel/rcu/update.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/rcu/update.c 2015-05-31 15:32:48.829635363 -0500 -@@ -170,6 +170,7 @@ - } - EXPORT_SYMBOL_GPL(rcu_read_lock_held); - -+#ifndef CONFIG_PREEMPT_RT_FULL - /** - * rcu_read_lock_bh_held() - might we be in RCU-bh read-side critical section? - * -@@ -196,6 +197,7 @@ - return in_softirq() || irqs_disabled(); - } - EXPORT_SYMBOL_GPL(rcu_read_lock_bh_held); -+#endif - - #endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ - -diff -Nur linux-3.18.14.orig/kernel/relay.c linux-3.18.14-rt/kernel/relay.c ---- linux-3.18.14.orig/kernel/relay.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/relay.c 2015-05-31 15:32:48.829635363 -0500 -@@ -339,6 +339,10 @@ - { - struct rchan_buf *buf = (struct rchan_buf *)data; - wake_up_interruptible(&buf->read_wait); -+ /* -+ * Stupid polling for now: -+ */ -+ mod_timer(&buf->timer, jiffies + 1); - } - - /** -@@ -356,6 +360,7 @@ - init_waitqueue_head(&buf->read_wait); - kref_init(&buf->kref); - setup_timer(&buf->timer, wakeup_readers, (unsigned long)buf); -+ mod_timer(&buf->timer, jiffies + 1); - } else - del_timer_sync(&buf->timer); - -@@ -739,15 +744,6 @@ - else - buf->early_bytes += buf->chan->subbuf_size - - buf->padding[old_subbuf]; -- smp_mb(); -- if (waitqueue_active(&buf->read_wait)) -- /* -- * Calling wake_up_interruptible() from here -- * will deadlock if we happen to be logging -- * from the scheduler (trying to re-grab -- * rq->lock), so defer it. -- */ -- mod_timer(&buf->timer, jiffies + 1); - } - - old = buf->data; -diff -Nur linux-3.18.14.orig/kernel/res_counter.c linux-3.18.14-rt/kernel/res_counter.c ---- linux-3.18.14.orig/kernel/res_counter.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/res_counter.c 2015-05-31 15:32:48.845635363 -0500 -@@ -59,7 +59,7 @@ - - r = ret = 0; - *limit_fail_at = NULL; -- local_irq_save(flags); -+ local_irq_save_nort(flags); - for (c = counter; c != NULL; c = c->parent) { - spin_lock(&c->lock); - r = res_counter_charge_locked(c, val, force); -@@ -79,7 +79,7 @@ - spin_unlock(&u->lock); - } - } -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - - return ret; - } -@@ -104,7 +104,7 @@ - struct res_counter *c; - u64 ret = 0; - -- local_irq_save(flags); -+ local_irq_save_nort(flags); - for (c = counter; c != top; c = c->parent) { - u64 r; - spin_lock(&c->lock); -@@ -113,7 +113,7 @@ - ret = r; - spin_unlock(&c->lock); - } -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - return ret; - } - -diff -Nur linux-3.18.14.orig/kernel/sched/completion.c linux-3.18.14-rt/kernel/sched/completion.c ---- linux-3.18.14.orig/kernel/sched/completion.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/sched/completion.c 2015-05-31 15:32:48.889635363 -0500 -@@ -30,10 +30,10 @@ - { - unsigned long flags; - -- spin_lock_irqsave(&x->wait.lock, flags); -+ raw_spin_lock_irqsave(&x->wait.lock, flags); - x->done++; -- __wake_up_locked(&x->wait, TASK_NORMAL, 1); -- spin_unlock_irqrestore(&x->wait.lock, flags); -+ __swait_wake_locked(&x->wait, TASK_NORMAL, 1); -+ raw_spin_unlock_irqrestore(&x->wait.lock, flags); - } - EXPORT_SYMBOL(complete); - -@@ -50,10 +50,10 @@ - { - unsigned long flags; - -- spin_lock_irqsave(&x->wait.lock, flags); -+ raw_spin_lock_irqsave(&x->wait.lock, flags); - x->done += UINT_MAX/2; -- __wake_up_locked(&x->wait, TASK_NORMAL, 0); -- spin_unlock_irqrestore(&x->wait.lock, flags); -+ __swait_wake_locked(&x->wait, TASK_NORMAL, 0); -+ raw_spin_unlock_irqrestore(&x->wait.lock, flags); - } - EXPORT_SYMBOL(complete_all); - -@@ -62,20 +62,20 @@ - long (*action)(long), long timeout, int state) - { - if (!x->done) { -- DECLARE_WAITQUEUE(wait, current); -+ DEFINE_SWAITER(wait); - -- __add_wait_queue_tail_exclusive(&x->wait, &wait); -+ swait_prepare_locked(&x->wait, &wait); - do { - if (signal_pending_state(state, current)) { - timeout = -ERESTARTSYS; - break; - } - __set_current_state(state); -- spin_unlock_irq(&x->wait.lock); -+ raw_spin_unlock_irq(&x->wait.lock); - timeout = action(timeout); -- spin_lock_irq(&x->wait.lock); -+ raw_spin_lock_irq(&x->wait.lock); - } while (!x->done && timeout); -- __remove_wait_queue(&x->wait, &wait); -+ swait_finish_locked(&x->wait, &wait); - if (!x->done) - return timeout; - } -@@ -89,9 +89,9 @@ - { - might_sleep(); - -- spin_lock_irq(&x->wait.lock); -+ raw_spin_lock_irq(&x->wait.lock); - timeout = do_wait_for_common(x, action, timeout, state); -- spin_unlock_irq(&x->wait.lock); -+ raw_spin_unlock_irq(&x->wait.lock); - return timeout; - } - -@@ -267,12 +267,12 @@ - unsigned long flags; - int ret = 1; - -- spin_lock_irqsave(&x->wait.lock, flags); -+ raw_spin_lock_irqsave(&x->wait.lock, flags); - if (!x->done) - ret = 0; - else - x->done--; -- spin_unlock_irqrestore(&x->wait.lock, flags); -+ raw_spin_unlock_irqrestore(&x->wait.lock, flags); - return ret; - } - EXPORT_SYMBOL(try_wait_for_completion); -@@ -290,10 +290,10 @@ - unsigned long flags; - int ret = 1; - -- spin_lock_irqsave(&x->wait.lock, flags); -+ raw_spin_lock_irqsave(&x->wait.lock, flags); - if (!x->done) - ret = 0; -- spin_unlock_irqrestore(&x->wait.lock, flags); -+ raw_spin_unlock_irqrestore(&x->wait.lock, flags); - return ret; - } - EXPORT_SYMBOL(completion_done); -diff -Nur linux-3.18.14.orig/kernel/sched/core.c linux-3.18.14-rt/kernel/sched/core.c ---- linux-3.18.14.orig/kernel/sched/core.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/sched/core.c 2015-05-31 15:32:48.893635363 -0500 -@@ -280,7 +280,11 @@ - * Number of tasks to iterate in a single balance run. - * Limited because this is done with IRQs disabled. - */ -+#ifndef CONFIG_PREEMPT_RT_FULL - const_debug unsigned int sysctl_sched_nr_migrate = 32; -+#else -+const_debug unsigned int sysctl_sched_nr_migrate = 8; -+#endif - - /* - * period over which we average the RT time consumption, measured -@@ -516,6 +520,7 @@ - - hrtimer_init(&rq->hrtick_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - rq->hrtick_timer.function = hrtick; -+ rq->hrtick_timer.irqsafe = 1; - } - #else /* CONFIG_SCHED_HRTICK */ - static inline void hrtick_clear(struct rq *rq) -@@ -627,6 +632,38 @@ - trace_sched_wake_idle_without_ipi(cpu); - } - -+#ifdef CONFIG_PREEMPT_LAZY -+void resched_curr_lazy(struct rq *rq) -+{ -+ struct task_struct *curr = rq->curr; -+ int cpu; -+ -+ if (!sched_feat(PREEMPT_LAZY)) { -+ resched_curr(rq); -+ return; -+ } -+ -+ lockdep_assert_held(&rq->lock); -+ -+ if (test_tsk_need_resched(curr)) -+ return; -+ -+ if (test_tsk_need_resched_lazy(curr)) -+ return; -+ -+ set_tsk_need_resched_lazy(curr); -+ -+ cpu = cpu_of(rq); -+ if (cpu == smp_processor_id()) -+ return; -+ -+ /* NEED_RESCHED_LAZY must be visible before we test polling */ -+ smp_mb(); -+ if (!tsk_is_polling(curr)) -+ smp_send_reschedule(cpu); -+} -+#endif -+ - void resched_cpu(int cpu) - { - struct rq *rq = cpu_rq(cpu); -@@ -650,12 +687,14 @@ - */ - int get_nohz_timer_target(int pinned) - { -- int cpu = smp_processor_id(); -+ int cpu; - int i; - struct sched_domain *sd; - -+ preempt_disable_rt(); -+ cpu = smp_processor_id(); - if (pinned || !get_sysctl_timer_migration() || !idle_cpu(cpu)) -- return cpu; -+ goto preempt_en_rt; - - rcu_read_lock(); - for_each_domain(cpu, sd) { -@@ -668,6 +707,8 @@ - } - unlock: - rcu_read_unlock(); -+preempt_en_rt: -+ preempt_enable_rt(); - return cpu; - } - /* -@@ -745,14 +786,29 @@ - #endif /* CONFIG_NO_HZ_COMMON */ - - #ifdef CONFIG_NO_HZ_FULL -+ -+static int ksoftirqd_running(void) -+{ -+ struct task_struct *softirqd; -+ -+ if (!IS_ENABLED(CONFIG_PREEMPT_RT_FULL)) -+ return 0; -+ softirqd = this_cpu_ksoftirqd(); -+ if (softirqd && softirqd->on_rq) -+ return 1; -+ return 0; -+} -+ - bool sched_can_stop_tick(void) - { - /* - * More than one running task need preemption. - * nr_running update is assumed to be visible - * after IPI is sent from wakers. -+ * -+ * NOTE, RT: if ksoftirqd is awake, subtract it. - */ -- if (this_rq()->nr_running > 1) -+ if (this_rq()->nr_running - ksoftirqd_running() > 1) - return false; - - return true; -@@ -1198,6 +1254,18 @@ - - static int migration_cpu_stop(void *data); - -+static bool check_task_state(struct task_struct *p, long match_state) -+{ -+ bool match = false; -+ -+ raw_spin_lock_irq(&p->pi_lock); -+ if (p->state == match_state || p->saved_state == match_state) -+ match = true; -+ raw_spin_unlock_irq(&p->pi_lock); -+ -+ return match; -+} -+ - /* - * wait_task_inactive - wait for a thread to unschedule. - * -@@ -1242,7 +1310,7 @@ - * is actually now running somewhere else! - */ - while (task_running(rq, p)) { -- if (match_state && unlikely(p->state != match_state)) -+ if (match_state && !check_task_state(p, match_state)) - return 0; - cpu_relax(); - } -@@ -1257,7 +1325,8 @@ - running = task_running(rq, p); - queued = task_on_rq_queued(p); - ncsw = 0; -- if (!match_state || p->state == match_state) -+ if (!match_state || p->state == match_state || -+ p->saved_state == match_state) - ncsw = p->nvcsw | LONG_MIN; /* sets MSB */ - task_rq_unlock(rq, p, &flags); - -@@ -1482,10 +1551,6 @@ - { - activate_task(rq, p, en_flags); - p->on_rq = TASK_ON_RQ_QUEUED; -- -- /* if a worker is waking up, notify workqueue */ -- if (p->flags & PF_WQ_WORKER) -- wq_worker_waking_up(p, cpu_of(rq)); - } - - /* -@@ -1699,8 +1764,27 @@ - */ - smp_mb__before_spinlock(); - raw_spin_lock_irqsave(&p->pi_lock, flags); -- if (!(p->state & state)) -+ if (!(p->state & state)) { -+ /* -+ * The task might be running due to a spinlock sleeper -+ * wakeup. Check the saved state and set it to running -+ * if the wakeup condition is true. -+ */ -+ if (!(wake_flags & WF_LOCK_SLEEPER)) { -+ if (p->saved_state & state) { -+ p->saved_state = TASK_RUNNING; -+ success = 1; -+ } -+ } - goto out; -+ } -+ -+ /* -+ * If this is a regular wakeup, then we can unconditionally -+ * clear the saved state of a "lock sleeper". -+ */ -+ if (!(wake_flags & WF_LOCK_SLEEPER)) -+ p->saved_state = TASK_RUNNING; - - success = 1; /* we're going to change ->state */ - cpu = task_cpu(p); -@@ -1743,42 +1827,6 @@ - } - - /** -- * try_to_wake_up_local - try to wake up a local task with rq lock held -- * @p: the thread to be awakened -- * -- * Put @p on the run-queue if it's not already there. The caller must -- * ensure that this_rq() is locked, @p is bound to this_rq() and not -- * the current task. -- */ --static void try_to_wake_up_local(struct task_struct *p) --{ -- struct rq *rq = task_rq(p); -- -- if (WARN_ON_ONCE(rq != this_rq()) || -- WARN_ON_ONCE(p == current)) -- return; -- -- lockdep_assert_held(&rq->lock); -- -- if (!raw_spin_trylock(&p->pi_lock)) { -- raw_spin_unlock(&rq->lock); -- raw_spin_lock(&p->pi_lock); -- raw_spin_lock(&rq->lock); -- } -- -- if (!(p->state & TASK_NORMAL)) -- goto out; -- -- if (!task_on_rq_queued(p)) -- ttwu_activate(rq, p, ENQUEUE_WAKEUP); -- -- ttwu_do_wakeup(rq, p, 0); -- ttwu_stat(p, smp_processor_id(), 0); --out: -- raw_spin_unlock(&p->pi_lock); --} -- --/** - * wake_up_process - Wake up a specific process - * @p: The process to be woken up. - * -@@ -1792,11 +1840,23 @@ - */ - int wake_up_process(struct task_struct *p) - { -- WARN_ON(task_is_stopped_or_traced(p)); -+ WARN_ON(__task_is_stopped_or_traced(p)); - return try_to_wake_up(p, TASK_NORMAL, 0); - } - EXPORT_SYMBOL(wake_up_process); - -+/** -+ * wake_up_lock_sleeper - Wake up a specific process blocked on a "sleeping lock" -+ * @p: The process to be woken up. -+ * -+ * Same as wake_up_process() above, but wake_flags=WF_LOCK_SLEEPER to indicate -+ * the nature of the wakeup. -+ */ -+int wake_up_lock_sleeper(struct task_struct *p) -+{ -+ return try_to_wake_up(p, TASK_ALL, WF_LOCK_SLEEPER); -+} -+ - int wake_up_state(struct task_struct *p, unsigned int state) - { - return try_to_wake_up(p, state, 0); -@@ -1987,6 +2047,9 @@ - p->on_cpu = 0; - #endif - init_task_preempt_count(p); -+#ifdef CONFIG_HAVE_PREEMPT_LAZY -+ task_thread_info(p)->preempt_lazy_count = 0; -+#endif - #ifdef CONFIG_SMP - plist_node_init(&p->pushable_tasks, MAX_PRIO); - RB_CLEAR_NODE(&p->pushable_dl_tasks); -@@ -2270,8 +2333,12 @@ - finish_arch_post_lock_switch(); - - fire_sched_in_preempt_notifiers(current); -+ /* -+ * We use mmdrop_delayed() here so we don't have to do the -+ * full __mmdrop() when we are the last user. -+ */ - if (mm) -- mmdrop(mm); -+ mmdrop_delayed(mm); - if (unlikely(prev_state == TASK_DEAD)) { - if (prev->sched_class->task_dead) - prev->sched_class->task_dead(prev); -@@ -2696,6 +2763,133 @@ - schedstat_inc(this_rq(), sched_count); - } - -+#if defined(CONFIG_PREEMPT_RT_FULL) && defined(CONFIG_SMP) -+#define MIGRATE_DISABLE_SET_AFFIN (1<<30) /* Can't make a negative */ -+#define migrate_disabled_updated(p) ((p)->migrate_disable & MIGRATE_DISABLE_SET_AFFIN) -+#define migrate_disable_count(p) ((p)->migrate_disable & ~MIGRATE_DISABLE_SET_AFFIN) -+ -+static inline void update_migrate_disable(struct task_struct *p) -+{ -+ const struct cpumask *mask; -+ -+ if (likely(!p->migrate_disable)) -+ return; -+ -+ /* Did we already update affinity? */ -+ if (unlikely(migrate_disabled_updated(p))) -+ return; -+ -+ /* -+ * Since this is always current we can get away with only locking -+ * rq->lock, the ->cpus_allowed value can normally only be changed -+ * while holding both p->pi_lock and rq->lock, but seeing that this -+ * is current, we cannot actually be waking up, so all code that -+ * relies on serialization against p->pi_lock is out of scope. -+ * -+ * Having rq->lock serializes us against things like -+ * set_cpus_allowed_ptr() that can still happen concurrently. -+ */ -+ mask = tsk_cpus_allowed(p); -+ -+ if (p->sched_class->set_cpus_allowed) -+ p->sched_class->set_cpus_allowed(p, mask); -+ /* mask==cpumask_of(task_cpu(p)) which has a cpumask_weight==1 */ -+ p->nr_cpus_allowed = 1; -+ -+ /* Let migrate_enable know to fix things back up */ -+ p->migrate_disable |= MIGRATE_DISABLE_SET_AFFIN; -+} -+ -+void migrate_disable(void) -+{ -+ struct task_struct *p = current; -+ -+ if (in_atomic()) { -+#ifdef CONFIG_SCHED_DEBUG -+ p->migrate_disable_atomic++; -+#endif -+ return; -+ } -+ -+#ifdef CONFIG_SCHED_DEBUG -+ if (unlikely(p->migrate_disable_atomic)) { -+ tracing_off(); -+ WARN_ON_ONCE(1); -+ } -+#endif -+ -+ if (p->migrate_disable) { -+ p->migrate_disable++; -+ return; -+ } -+ -+ preempt_disable(); -+ preempt_lazy_disable(); -+ pin_current_cpu(); -+ p->migrate_disable = 1; -+ preempt_enable(); -+} -+EXPORT_SYMBOL(migrate_disable); -+ -+void migrate_enable(void) -+{ -+ struct task_struct *p = current; -+ const struct cpumask *mask; -+ unsigned long flags; -+ struct rq *rq; -+ -+ if (in_atomic()) { -+#ifdef CONFIG_SCHED_DEBUG -+ p->migrate_disable_atomic--; -+#endif -+ return; -+ } -+ -+#ifdef CONFIG_SCHED_DEBUG -+ if (unlikely(p->migrate_disable_atomic)) { -+ tracing_off(); -+ WARN_ON_ONCE(1); -+ } -+#endif -+ WARN_ON_ONCE(p->migrate_disable <= 0); -+ -+ if (migrate_disable_count(p) > 1) { -+ p->migrate_disable--; -+ return; -+ } -+ -+ preempt_disable(); -+ if (unlikely(migrate_disabled_updated(p))) { -+ /* -+ * Undo whatever update_migrate_disable() did, also see there -+ * about locking. -+ */ -+ rq = this_rq(); -+ raw_spin_lock_irqsave(&rq->lock, flags); -+ -+ /* -+ * Clearing migrate_disable causes tsk_cpus_allowed to -+ * show the tasks original cpu affinity. -+ */ -+ p->migrate_disable = 0; -+ mask = tsk_cpus_allowed(p); -+ if (p->sched_class->set_cpus_allowed) -+ p->sched_class->set_cpus_allowed(p, mask); -+ p->nr_cpus_allowed = cpumask_weight(mask); -+ raw_spin_unlock_irqrestore(&rq->lock, flags); -+ } else -+ p->migrate_disable = 0; -+ -+ unpin_current_cpu(); -+ preempt_enable(); -+ preempt_lazy_enable(); -+} -+EXPORT_SYMBOL(migrate_enable); -+#else -+static inline void update_migrate_disable(struct task_struct *p) { } -+#define migrate_disabled_updated(p) 0 -+#endif -+ - /* - * Pick up the highest-prio task: - */ -@@ -2799,6 +2993,8 @@ - smp_mb__before_spinlock(); - raw_spin_lock_irq(&rq->lock); - -+ update_migrate_disable(prev); -+ - switch_count = &prev->nivcsw; - if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { - if (unlikely(signal_pending_state(prev->state, prev))) { -@@ -2806,19 +3002,6 @@ - } else { - deactivate_task(rq, prev, DEQUEUE_SLEEP); - prev->on_rq = 0; -- -- /* -- * If a worker went to sleep, notify and ask workqueue -- * whether it wants to wake up a task to maintain -- * concurrency. -- */ -- if (prev->flags & PF_WQ_WORKER) { -- struct task_struct *to_wakeup; -- -- to_wakeup = wq_worker_sleeping(prev, cpu); -- if (to_wakeup) -- try_to_wake_up_local(to_wakeup); -- } - } - switch_count = &prev->nvcsw; - } -@@ -2828,6 +3011,7 @@ - - next = pick_next_task(rq, prev); - clear_tsk_need_resched(prev); -+ clear_tsk_need_resched_lazy(prev); - clear_preempt_need_resched(); - rq->skip_clock_update = 0; - -@@ -2857,9 +3041,20 @@ - - static inline void sched_submit_work(struct task_struct *tsk) - { -- if (!tsk->state || tsk_is_pi_blocked(tsk)) -+ if (!tsk->state) - return; - /* -+ * If a worker went to sleep, notify and ask workqueue whether -+ * it wants to wake up a task to maintain concurrency. -+ */ -+ if (tsk->flags & PF_WQ_WORKER) -+ wq_worker_sleeping(tsk); -+ -+ -+ if (tsk_is_pi_blocked(tsk)) -+ return; -+ -+ /* - * If we are going to sleep and we have plugged IO queued, - * make sure to submit it to avoid deadlocks. - */ -@@ -2867,12 +3062,19 @@ - blk_schedule_flush_plug(tsk); - } - -+static inline void sched_update_worker(struct task_struct *tsk) -+{ -+ if (tsk->flags & PF_WQ_WORKER) -+ wq_worker_running(tsk); -+} -+ - asmlinkage __visible void __sched schedule(void) - { - struct task_struct *tsk = current; - - sched_submit_work(tsk); - __schedule(); -+ sched_update_worker(tsk); - } - EXPORT_SYMBOL(schedule); - -@@ -2922,9 +3124,26 @@ - if (likely(!preemptible())) - return; - -+#ifdef CONFIG_PREEMPT_LAZY -+ /* -+ * Check for lazy preemption -+ */ -+ if (current_thread_info()->preempt_lazy_count && -+ !test_thread_flag(TIF_NEED_RESCHED)) -+ return; -+#endif - do { - __preempt_count_add(PREEMPT_ACTIVE); -+ /* -+ * The add/subtract must not be traced by the function -+ * tracer. But we still want to account for the -+ * preempt off latency tracer. Since the _notrace versions -+ * of add/subtract skip the accounting for latency tracer -+ * we must force it manually. -+ */ -+ start_critical_timings(); - __schedule(); -+ stop_critical_timings(); - __preempt_count_sub(PREEMPT_ACTIVE); - - /* -@@ -4236,9 +4455,16 @@ - - static void __cond_resched(void) - { -- __preempt_count_add(PREEMPT_ACTIVE); -- __schedule(); -- __preempt_count_sub(PREEMPT_ACTIVE); -+ do { -+ __preempt_count_add(PREEMPT_ACTIVE); -+ __schedule(); -+ __preempt_count_sub(PREEMPT_ACTIVE); -+ /* -+ * Check again in case we missed a preemption -+ * opportunity between schedule and now. -+ */ -+ barrier(); -+ } while (need_resched()); - } - - int __sched _cond_resched(void) -@@ -4279,6 +4505,7 @@ - } - EXPORT_SYMBOL(__cond_resched_lock); - -+#ifndef CONFIG_PREEMPT_RT_FULL - int __sched __cond_resched_softirq(void) - { - BUG_ON(!in_softirq()); -@@ -4292,6 +4519,7 @@ - return 0; - } - EXPORT_SYMBOL(__cond_resched_softirq); -+#endif - - /** - * yield - yield the current processor to other threads. -@@ -4653,7 +4881,9 @@ - - /* Set the preempt count _outside_ the spinlocks! */ - init_idle_preempt_count(idle, cpu); -- -+#ifdef CONFIG_HAVE_PREEMPT_LAZY -+ task_thread_info(idle)->preempt_lazy_count = 0; -+#endif - /* - * The idle tasks have their own, simple scheduling class: - */ -@@ -4695,11 +4925,91 @@ - - void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask) - { -- if (p->sched_class && p->sched_class->set_cpus_allowed) -- p->sched_class->set_cpus_allowed(p, new_mask); -+ if (!migrate_disabled_updated(p)) { -+ if (p->sched_class && p->sched_class->set_cpus_allowed) -+ p->sched_class->set_cpus_allowed(p, new_mask); -+ p->nr_cpus_allowed = cpumask_weight(new_mask); -+ } - - cpumask_copy(&p->cpus_allowed, new_mask); -- p->nr_cpus_allowed = cpumask_weight(new_mask); -+} -+ -+static DEFINE_PER_CPU(struct cpumask, sched_cpumasks); -+static DEFINE_MUTEX(sched_down_mutex); -+static cpumask_t sched_down_cpumask; -+ -+void tell_sched_cpu_down_begin(int cpu) -+{ -+ mutex_lock(&sched_down_mutex); -+ cpumask_set_cpu(cpu, &sched_down_cpumask); -+ mutex_unlock(&sched_down_mutex); -+} -+ -+void tell_sched_cpu_down_done(int cpu) -+{ -+ mutex_lock(&sched_down_mutex); -+ cpumask_clear_cpu(cpu, &sched_down_cpumask); -+ mutex_unlock(&sched_down_mutex); -+} -+ -+/** -+ * migrate_me - try to move the current task off this cpu -+ * -+ * Used by the pin_current_cpu() code to try to get tasks -+ * to move off the current CPU as it is going down. -+ * It will only move the task if the task isn't pinned to -+ * the CPU (with migrate_disable, affinity or NO_SETAFFINITY) -+ * and the task has to be in a RUNNING state. Otherwise the -+ * movement of the task will wake it up (change its state -+ * to running) when the task did not expect it. -+ * -+ * Returns 1 if it succeeded in moving the current task -+ * 0 otherwise. -+ */ -+int migrate_me(void) -+{ -+ struct task_struct *p = current; -+ struct migration_arg arg; -+ struct cpumask *cpumask; -+ struct cpumask *mask; -+ unsigned long flags; -+ unsigned int dest_cpu; -+ struct rq *rq; -+ -+ /* -+ * We can not migrate tasks bounded to a CPU or tasks not -+ * running. The movement of the task will wake it up. -+ */ -+ if (p->flags & PF_NO_SETAFFINITY || p->state) -+ return 0; -+ -+ mutex_lock(&sched_down_mutex); -+ rq = task_rq_lock(p, &flags); -+ -+ cpumask = &__get_cpu_var(sched_cpumasks); -+ mask = &p->cpus_allowed; -+ -+ cpumask_andnot(cpumask, mask, &sched_down_cpumask); -+ -+ if (!cpumask_weight(cpumask)) { -+ /* It's only on this CPU? */ -+ task_rq_unlock(rq, p, &flags); -+ mutex_unlock(&sched_down_mutex); -+ return 0; -+ } -+ -+ dest_cpu = cpumask_any_and(cpu_active_mask, cpumask); -+ -+ arg.task = p; -+ arg.dest_cpu = dest_cpu; -+ -+ task_rq_unlock(rq, p, &flags); -+ -+ stop_one_cpu(cpu_of(rq), migration_cpu_stop, &arg); -+ tlb_migrate_finish(p->mm); -+ mutex_unlock(&sched_down_mutex); -+ -+ return 1; - } - - /* -@@ -4745,7 +5055,7 @@ - do_set_cpus_allowed(p, new_mask); - - /* Can the task run on the task's current CPU? If so, we're done */ -- if (cpumask_test_cpu(task_cpu(p), new_mask)) -+ if (cpumask_test_cpu(task_cpu(p), new_mask) || __migrate_disabled(p)) - goto out; - - dest_cpu = cpumask_any_and(cpu_active_mask, new_mask); -@@ -4885,6 +5195,8 @@ - - #ifdef CONFIG_HOTPLUG_CPU - -+static DEFINE_PER_CPU(struct mm_struct *, idle_last_mm); -+ - /* - * Ensures that the idle task is using init_mm right before its cpu goes - * offline. -@@ -4899,7 +5211,11 @@ - switch_mm(mm, &init_mm, current); - finish_arch_post_lock_switch(); - } -- mmdrop(mm); -+ /* -+ * Defer the cleanup to an alive cpu. On RT we can neither -+ * call mmdrop() nor mmdrop_delayed() from here. -+ */ -+ per_cpu(idle_last_mm, smp_processor_id()) = mm; - } - - /* -@@ -5242,6 +5558,10 @@ - - case CPU_DEAD: - calc_load_migrate(rq); -+ if (per_cpu(idle_last_mm, cpu)) { -+ mmdrop(per_cpu(idle_last_mm, cpu)); -+ per_cpu(idle_last_mm, cpu) = NULL; -+ } - break; - #endif - } -@@ -7183,7 +7503,8 @@ - #ifdef CONFIG_DEBUG_ATOMIC_SLEEP - static inline int preempt_count_equals(int preempt_offset) - { -- int nested = (preempt_count() & ~PREEMPT_ACTIVE) + rcu_preempt_depth(); -+ int nested = (preempt_count() & ~PREEMPT_ACTIVE) + -+ sched_rcu_preempt_depth(); - - return (nested == preempt_offset); - } -diff -Nur linux-3.18.14.orig/kernel/sched/cputime.c linux-3.18.14-rt/kernel/sched/cputime.c ---- linux-3.18.14.orig/kernel/sched/cputime.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/sched/cputime.c 2015-05-31 15:32:48.893635363 -0500 -@@ -675,37 +675,45 @@ - - void vtime_account_system(struct task_struct *tsk) - { -- write_seqlock(&tsk->vtime_seqlock); -+ raw_spin_lock(&tsk->vtime_lock); -+ write_seqcount_begin(&tsk->vtime_seq); - __vtime_account_system(tsk); -- write_sequnlock(&tsk->vtime_seqlock); -+ write_seqcount_end(&tsk->vtime_seq); -+ raw_spin_unlock(&tsk->vtime_lock); - } - - void vtime_gen_account_irq_exit(struct task_struct *tsk) - { -- write_seqlock(&tsk->vtime_seqlock); -+ raw_spin_lock(&tsk->vtime_lock); -+ write_seqcount_begin(&tsk->vtime_seq); - __vtime_account_system(tsk); - if (context_tracking_in_user()) - tsk->vtime_snap_whence = VTIME_USER; -- write_sequnlock(&tsk->vtime_seqlock); -+ write_seqcount_end(&tsk->vtime_seq); -+ raw_spin_unlock(&tsk->vtime_lock); - } - - void vtime_account_user(struct task_struct *tsk) - { - cputime_t delta_cpu; - -- write_seqlock(&tsk->vtime_seqlock); -+ raw_spin_lock(&tsk->vtime_lock); -+ write_seqcount_begin(&tsk->vtime_seq); - delta_cpu = get_vtime_delta(tsk); - tsk->vtime_snap_whence = VTIME_SYS; - account_user_time(tsk, delta_cpu, cputime_to_scaled(delta_cpu)); -- write_sequnlock(&tsk->vtime_seqlock); -+ write_seqcount_end(&tsk->vtime_seq); -+ raw_spin_unlock(&tsk->vtime_lock); - } - - void vtime_user_enter(struct task_struct *tsk) - { -- write_seqlock(&tsk->vtime_seqlock); -+ raw_spin_lock(&tsk->vtime_lock); -+ write_seqcount_begin(&tsk->vtime_seq); - __vtime_account_system(tsk); - tsk->vtime_snap_whence = VTIME_USER; -- write_sequnlock(&tsk->vtime_seqlock); -+ write_seqcount_end(&tsk->vtime_seq); -+ raw_spin_unlock(&tsk->vtime_lock); - } - - void vtime_guest_enter(struct task_struct *tsk) -@@ -717,19 +725,23 @@ - * synchronization against the reader (task_gtime()) - * that can thus safely catch up with a tickless delta. - */ -- write_seqlock(&tsk->vtime_seqlock); -+ raw_spin_lock(&tsk->vtime_lock); -+ write_seqcount_begin(&tsk->vtime_seq); - __vtime_account_system(tsk); - current->flags |= PF_VCPU; -- write_sequnlock(&tsk->vtime_seqlock); -+ write_seqcount_end(&tsk->vtime_seq); -+ raw_spin_unlock(&tsk->vtime_lock); - } - EXPORT_SYMBOL_GPL(vtime_guest_enter); - - void vtime_guest_exit(struct task_struct *tsk) - { -- write_seqlock(&tsk->vtime_seqlock); -+ raw_spin_lock(&tsk->vtime_lock); -+ write_seqcount_begin(&tsk->vtime_seq); - __vtime_account_system(tsk); - current->flags &= ~PF_VCPU; -- write_sequnlock(&tsk->vtime_seqlock); -+ write_seqcount_end(&tsk->vtime_seq); -+ raw_spin_unlock(&tsk->vtime_lock); - } - EXPORT_SYMBOL_GPL(vtime_guest_exit); - -@@ -742,24 +754,30 @@ - - void arch_vtime_task_switch(struct task_struct *prev) - { -- write_seqlock(&prev->vtime_seqlock); -+ raw_spin_lock(&prev->vtime_lock); -+ write_seqcount_begin(&prev->vtime_seq); - prev->vtime_snap_whence = VTIME_SLEEPING; -- write_sequnlock(&prev->vtime_seqlock); -+ write_seqcount_end(&prev->vtime_seq); -+ raw_spin_unlock(&prev->vtime_lock); - -- write_seqlock(¤t->vtime_seqlock); -+ raw_spin_lock(¤t->vtime_lock); -+ write_seqcount_begin(¤t->vtime_seq); - current->vtime_snap_whence = VTIME_SYS; - current->vtime_snap = sched_clock_cpu(smp_processor_id()); -- write_sequnlock(¤t->vtime_seqlock); -+ write_seqcount_end(¤t->vtime_seq); -+ raw_spin_unlock(¤t->vtime_lock); - } - - void vtime_init_idle(struct task_struct *t, int cpu) - { - unsigned long flags; - -- write_seqlock_irqsave(&t->vtime_seqlock, flags); -+ raw_spin_lock_irqsave(&t->vtime_lock, flags); -+ write_seqcount_begin(&t->vtime_seq); - t->vtime_snap_whence = VTIME_SYS; - t->vtime_snap = sched_clock_cpu(cpu); -- write_sequnlock_irqrestore(&t->vtime_seqlock, flags); -+ write_seqcount_end(&t->vtime_seq); -+ raw_spin_unlock_irqrestore(&t->vtime_lock, flags); - } - - cputime_t task_gtime(struct task_struct *t) -@@ -768,13 +786,13 @@ - cputime_t gtime; - - do { -- seq = read_seqbegin(&t->vtime_seqlock); -+ seq = read_seqcount_begin(&t->vtime_seq); - - gtime = t->gtime; - if (t->flags & PF_VCPU) - gtime += vtime_delta(t); - -- } while (read_seqretry(&t->vtime_seqlock, seq)); -+ } while (read_seqcount_retry(&t->vtime_seq, seq)); - - return gtime; - } -@@ -797,7 +815,7 @@ - *udelta = 0; - *sdelta = 0; - -- seq = read_seqbegin(&t->vtime_seqlock); -+ seq = read_seqcount_begin(&t->vtime_seq); - - if (u_dst) - *u_dst = *u_src; -@@ -821,7 +839,7 @@ - if (t->vtime_snap_whence == VTIME_SYS) - *sdelta = delta; - } -- } while (read_seqretry(&t->vtime_seqlock, seq)); -+ } while (read_seqcount_retry(&t->vtime_seq, seq)); - } - - -diff -Nur linux-3.18.14.orig/kernel/sched/deadline.c linux-3.18.14-rt/kernel/sched/deadline.c ---- linux-3.18.14.orig/kernel/sched/deadline.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/sched/deadline.c 2015-05-31 15:32:48.893635363 -0500 -@@ -570,6 +570,7 @@ - - hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - timer->function = dl_task_timer; -+ timer->irqsafe = 1; - } - - static -diff -Nur linux-3.18.14.orig/kernel/sched/debug.c linux-3.18.14-rt/kernel/sched/debug.c ---- linux-3.18.14.orig/kernel/sched/debug.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/sched/debug.c 2015-05-31 15:32:48.897635363 -0500 -@@ -256,6 +256,9 @@ - P(rt_throttled); - PN(rt_time); - PN(rt_runtime); -+#ifdef CONFIG_SMP -+ P(rt_nr_migratory); -+#endif - - #undef PN - #undef P -@@ -634,6 +637,10 @@ - #endif - P(policy); - P(prio); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ P(migrate_disable); -+#endif -+ P(nr_cpus_allowed); - #undef PN - #undef __PN - #undef P -diff -Nur linux-3.18.14.orig/kernel/sched/fair.c linux-3.18.14-rt/kernel/sched/fair.c ---- linux-3.18.14.orig/kernel/sched/fair.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/sched/fair.c 2015-05-31 15:32:48.897635363 -0500 -@@ -2951,7 +2951,7 @@ - ideal_runtime = sched_slice(cfs_rq, curr); - delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime; - if (delta_exec > ideal_runtime) { -- resched_curr(rq_of(cfs_rq)); -+ resched_curr_lazy(rq_of(cfs_rq)); - /* - * The current task ran long enough, ensure it doesn't get - * re-elected due to buddy favours. -@@ -2975,7 +2975,7 @@ - return; - - if (delta > ideal_runtime) -- resched_curr(rq_of(cfs_rq)); -+ resched_curr_lazy(rq_of(cfs_rq)); - } - - static void -@@ -3115,7 +3115,7 @@ - * validating it and just reschedule. - */ - if (queued) { -- resched_curr(rq_of(cfs_rq)); -+ resched_curr_lazy(rq_of(cfs_rq)); - return; - } - /* -@@ -3306,7 +3306,7 @@ - * hierarchy can be throttled - */ - if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr)) -- resched_curr(rq_of(cfs_rq)); -+ resched_curr_lazy(rq_of(cfs_rq)); - } - - static __always_inline -@@ -3925,7 +3925,7 @@ - - if (delta < 0) { - if (rq->curr == p) -- resched_curr(rq); -+ resched_curr_lazy(rq); - return; - } - hrtick_start(rq, delta); -@@ -4792,7 +4792,7 @@ - return; - - preempt: -- resched_curr(rq); -+ resched_curr_lazy(rq); - /* - * Only set the backward buddy when the current task is still - * on the rq. This can happen when a wakeup gets interleaved -@@ -7576,7 +7576,7 @@ - * 'current' within the tree based on its new key value. - */ - swap(curr->vruntime, se->vruntime); -- resched_curr(rq); -+ resched_curr_lazy(rq); - } - - se->vruntime -= cfs_rq->min_vruntime; -@@ -7601,7 +7601,7 @@ - */ - if (rq->curr == p) { - if (p->prio > oldprio) -- resched_curr(rq); -+ resched_curr_lazy(rq); - } else - check_preempt_curr(rq, p, 0); - } -diff -Nur linux-3.18.14.orig/kernel/sched/features.h linux-3.18.14-rt/kernel/sched/features.h ---- linux-3.18.14.orig/kernel/sched/features.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/sched/features.h 2015-05-31 15:32:48.897635363 -0500 -@@ -50,12 +50,18 @@ - */ - SCHED_FEAT(NONTASK_CAPACITY, true) - -+#ifdef CONFIG_PREEMPT_RT_FULL -+SCHED_FEAT(TTWU_QUEUE, false) -+# ifdef CONFIG_PREEMPT_LAZY -+SCHED_FEAT(PREEMPT_LAZY, true) -+# endif -+#else - /* - * Queue remote wakeups on the target CPU and process them - * using the scheduler IPI. Reduces rq->lock contention/bounces. - */ - SCHED_FEAT(TTWU_QUEUE, true) -- -+#endif - SCHED_FEAT(FORCE_SD_OVERLAP, false) - SCHED_FEAT(RT_RUNTIME_SHARE, true) - SCHED_FEAT(LB_MIN, false) -diff -Nur linux-3.18.14.orig/kernel/sched/Makefile linux-3.18.14-rt/kernel/sched/Makefile ---- linux-3.18.14.orig/kernel/sched/Makefile 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/sched/Makefile 2015-05-31 15:32:48.861635363 -0500 -@@ -13,7 +13,7 @@ - - obj-y += core.o proc.o clock.o cputime.o - obj-y += idle_task.o fair.o rt.o deadline.o stop_task.o --obj-y += wait.o completion.o idle.o -+obj-y += wait.o wait-simple.o work-simple.o completion.o idle.o - obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o - obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o - obj-$(CONFIG_SCHEDSTATS) += stats.o -diff -Nur linux-3.18.14.orig/kernel/sched/rt.c linux-3.18.14-rt/kernel/sched/rt.c ---- linux-3.18.14.orig/kernel/sched/rt.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/sched/rt.c 2015-05-31 15:32:48.897635363 -0500 -@@ -43,6 +43,7 @@ - - hrtimer_init(&rt_b->rt_period_timer, - CLOCK_MONOTONIC, HRTIMER_MODE_REL); -+ rt_b->rt_period_timer.irqsafe = 1; - rt_b->rt_period_timer.function = sched_rt_period_timer; - } - -diff -Nur linux-3.18.14.orig/kernel/sched/sched.h linux-3.18.14-rt/kernel/sched/sched.h ---- linux-3.18.14.orig/kernel/sched/sched.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/sched/sched.h 2015-05-31 15:32:48.897635363 -0500 -@@ -1018,6 +1018,7 @@ - #define WF_SYNC 0x01 /* waker goes to sleep after wakeup */ - #define WF_FORK 0x02 /* child wakeup after fork */ - #define WF_MIGRATED 0x4 /* internal use, task got migrated */ -+#define WF_LOCK_SLEEPER 0x08 /* wakeup spinlock "sleeper" */ - - /* - * To aid in avoiding the subversion of "niceness" due to uneven distribution -@@ -1210,6 +1211,15 @@ - extern void resched_curr(struct rq *rq); - extern void resched_cpu(int cpu); - -+#ifdef CONFIG_PREEMPT_LAZY -+extern void resched_curr_lazy(struct rq *rq); -+#else -+static inline void resched_curr_lazy(struct rq *rq) -+{ -+ resched_curr(rq); -+} -+#endif -+ - extern struct rt_bandwidth def_rt_bandwidth; - extern void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime); - -diff -Nur linux-3.18.14.orig/kernel/sched/wait-simple.c linux-3.18.14-rt/kernel/sched/wait-simple.c ---- linux-3.18.14.orig/kernel/sched/wait-simple.c 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/kernel/sched/wait-simple.c 2015-05-31 15:32:48.897635363 -0500 -@@ -0,0 +1,115 @@ -+/* -+ * Simple waitqueues without fancy flags and callbacks -+ * -+ * (C) 2011 Thomas Gleixner -+ * -+ * Based on kernel/wait.c -+ * -+ * For licencing details see kernel-base/COPYING -+ */ -+#include -+#include -+#include -+#include -+ -+/* Adds w to head->list. Must be called with head->lock locked. */ -+static inline void __swait_enqueue(struct swait_head *head, struct swaiter *w) -+{ -+ list_add(&w->node, &head->list); -+ /* We can't let the condition leak before the setting of head */ -+ smp_mb(); -+} -+ -+/* Removes w from head->list. Must be called with head->lock locked. */ -+static inline void __swait_dequeue(struct swaiter *w) -+{ -+ list_del_init(&w->node); -+} -+ -+void __init_swait_head(struct swait_head *head, struct lock_class_key *key) -+{ -+ raw_spin_lock_init(&head->lock); -+ lockdep_set_class(&head->lock, key); -+ INIT_LIST_HEAD(&head->list); -+} -+EXPORT_SYMBOL(__init_swait_head); -+ -+void swait_prepare_locked(struct swait_head *head, struct swaiter *w) -+{ -+ w->task = current; -+ if (list_empty(&w->node)) -+ __swait_enqueue(head, w); -+} -+ -+void swait_prepare(struct swait_head *head, struct swaiter *w, int state) -+{ -+ unsigned long flags; -+ -+ raw_spin_lock_irqsave(&head->lock, flags); -+ swait_prepare_locked(head, w); -+ __set_current_state(state); -+ raw_spin_unlock_irqrestore(&head->lock, flags); -+} -+EXPORT_SYMBOL(swait_prepare); -+ -+void swait_finish_locked(struct swait_head *head, struct swaiter *w) -+{ -+ __set_current_state(TASK_RUNNING); -+ if (w->task) -+ __swait_dequeue(w); -+} -+ -+void swait_finish(struct swait_head *head, struct swaiter *w) -+{ -+ unsigned long flags; -+ -+ __set_current_state(TASK_RUNNING); -+ if (w->task) { -+ raw_spin_lock_irqsave(&head->lock, flags); -+ __swait_dequeue(w); -+ raw_spin_unlock_irqrestore(&head->lock, flags); -+ } -+} -+EXPORT_SYMBOL(swait_finish); -+ -+unsigned int -+__swait_wake_locked(struct swait_head *head, unsigned int state, unsigned int num) -+{ -+ struct swaiter *curr, *next; -+ int woken = 0; -+ -+ list_for_each_entry_safe(curr, next, &head->list, node) { -+ if (wake_up_state(curr->task, state)) { -+ __swait_dequeue(curr); -+ /* -+ * The waiting task can free the waiter as -+ * soon as curr->task = NULL is written, -+ * without taking any locks. A memory barrier -+ * is required here to prevent the following -+ * store to curr->task from getting ahead of -+ * the dequeue operation. -+ */ -+ smp_wmb(); -+ curr->task = NULL; -+ if (++woken == num) -+ break; -+ } -+ } -+ return woken; -+} -+ -+unsigned int -+__swait_wake(struct swait_head *head, unsigned int state, unsigned int num) -+{ -+ unsigned long flags; -+ int woken; -+ -+ if (!swaitqueue_active(head)) -+ return 0; -+ -+ raw_spin_lock_irqsave(&head->lock, flags); -+ woken = __swait_wake_locked(head, state, num); -+ raw_spin_unlock_irqrestore(&head->lock, flags); -+ return woken; -+} -+EXPORT_SYMBOL(__swait_wake); -diff -Nur linux-3.18.14.orig/kernel/sched/work-simple.c linux-3.18.14-rt/kernel/sched/work-simple.c ---- linux-3.18.14.orig/kernel/sched/work-simple.c 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/kernel/sched/work-simple.c 2015-05-31 15:32:48.901635363 -0500 -@@ -0,0 +1,172 @@ -+/* -+ * Copyright (C) 2014 BMW Car IT GmbH, Daniel Wagner daniel.wagner@bmw-carit.de -+ * -+ * Provides a framework for enqueuing callbacks from irq context -+ * PREEMPT_RT_FULL safe. The callbacks are executed in kthread context. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#define SWORK_EVENT_PENDING (1 << 0) -+ -+static DEFINE_MUTEX(worker_mutex); -+static struct sworker *glob_worker; -+ -+struct sworker { -+ struct list_head events; -+ struct swait_head wq; -+ -+ raw_spinlock_t lock; -+ -+ struct task_struct *task; -+ int refs; -+}; -+ -+static bool swork_readable(struct sworker *worker) -+{ -+ bool r; -+ -+ if (kthread_should_stop()) -+ return true; -+ -+ raw_spin_lock_irq(&worker->lock); -+ r = !list_empty(&worker->events); -+ raw_spin_unlock_irq(&worker->lock); -+ -+ return r; -+} -+ -+static int swork_kthread(void *arg) -+{ -+ struct sworker *worker = arg; -+ -+ for (;;) { -+ swait_event_interruptible(worker->wq, -+ swork_readable(worker)); -+ if (kthread_should_stop()) -+ break; -+ -+ raw_spin_lock_irq(&worker->lock); -+ while (!list_empty(&worker->events)) { -+ struct swork_event *sev; -+ -+ sev = list_first_entry(&worker->events, -+ struct swork_event, item); -+ list_del(&sev->item); -+ raw_spin_unlock_irq(&worker->lock); -+ -+ WARN_ON_ONCE(!test_and_clear_bit(SWORK_EVENT_PENDING, -+ &sev->flags)); -+ sev->func(sev); -+ raw_spin_lock_irq(&worker->lock); -+ } -+ raw_spin_unlock_irq(&worker->lock); -+ } -+ return 0; -+} -+ -+static struct sworker *swork_create(void) -+{ -+ struct sworker *worker; -+ -+ worker = kzalloc(sizeof(*worker), GFP_KERNEL); -+ if (!worker) -+ return ERR_PTR(-ENOMEM); -+ -+ INIT_LIST_HEAD(&worker->events); -+ raw_spin_lock_init(&worker->lock); -+ init_swait_head(&worker->wq); -+ -+ worker->task = kthread_run(swork_kthread, worker, "kswork"); -+ if (IS_ERR(worker->task)) { -+ kfree(worker); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ return worker; -+} -+ -+static void swork_destroy(struct sworker *worker) -+{ -+ kthread_stop(worker->task); -+ -+ WARN_ON(!list_empty(&worker->events)); -+ kfree(worker); -+} -+ -+/** -+ * swork_queue - queue swork -+ * -+ * Returns %false if @work was already on a queue, %true otherwise. -+ * -+ * The work is queued and processed on a random CPU -+ */ -+bool swork_queue(struct swork_event *sev) -+{ -+ unsigned long flags; -+ -+ if (test_and_set_bit(SWORK_EVENT_PENDING, &sev->flags)) -+ return false; -+ -+ raw_spin_lock_irqsave(&glob_worker->lock, flags); -+ list_add_tail(&sev->item, &glob_worker->events); -+ raw_spin_unlock_irqrestore(&glob_worker->lock, flags); -+ -+ swait_wake(&glob_worker->wq); -+ return true; -+} -+EXPORT_SYMBOL_GPL(swork_queue); -+ -+/** -+ * swork_get - get an instance of the sworker -+ * -+ * Returns an negative error code if the initialization if the worker did not -+ * work, %0 otherwise. -+ * -+ */ -+int swork_get(void) -+{ -+ struct sworker *worker; -+ -+ mutex_lock(&worker_mutex); -+ if (!glob_worker) { -+ worker = swork_create(); -+ if (IS_ERR(worker)) { -+ mutex_unlock(&worker_mutex); -+ return -ENOMEM; -+ } -+ -+ glob_worker = worker; -+ } -+ -+ glob_worker->refs++; -+ mutex_unlock(&worker_mutex); -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(swork_get); -+ -+/** -+ * swork_put - puts an instance of the sworker -+ * -+ * Will destroy the sworker thread. This function must not be called until all -+ * queued events have been completed. -+ */ -+void swork_put(void) -+{ -+ mutex_lock(&worker_mutex); -+ -+ glob_worker->refs--; -+ if (glob_worker->refs > 0) -+ goto out; -+ -+ swork_destroy(glob_worker); -+ glob_worker = NULL; -+out: -+ mutex_unlock(&worker_mutex); -+} -+EXPORT_SYMBOL_GPL(swork_put); -diff -Nur linux-3.18.14.orig/kernel/signal.c linux-3.18.14-rt/kernel/signal.c ---- linux-3.18.14.orig/kernel/signal.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/signal.c 2015-05-31 15:32:48.921635363 -0500 -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -352,13 +353,45 @@ - return false; - } - -+#ifdef __HAVE_ARCH_CMPXCHG -+static inline struct sigqueue *get_task_cache(struct task_struct *t) -+{ -+ struct sigqueue *q = t->sigqueue_cache; -+ -+ if (cmpxchg(&t->sigqueue_cache, q, NULL) != q) -+ return NULL; -+ return q; -+} -+ -+static inline int put_task_cache(struct task_struct *t, struct sigqueue *q) -+{ -+ if (cmpxchg(&t->sigqueue_cache, NULL, q) == NULL) -+ return 0; -+ return 1; -+} -+ -+#else -+ -+static inline struct sigqueue *get_task_cache(struct task_struct *t) -+{ -+ return NULL; -+} -+ -+static inline int put_task_cache(struct task_struct *t, struct sigqueue *q) -+{ -+ return 1; -+} -+ -+#endif -+ - /* - * allocate a new signal queue record - * - this may be called without locks if and only if t == current, otherwise an - * appropriate lock must be held to stop the target task from exiting - */ - static struct sigqueue * --__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimit) -+__sigqueue_do_alloc(int sig, struct task_struct *t, gfp_t flags, -+ int override_rlimit, int fromslab) - { - struct sigqueue *q = NULL; - struct user_struct *user; -@@ -375,7 +408,10 @@ - if (override_rlimit || - atomic_read(&user->sigpending) <= - task_rlimit(t, RLIMIT_SIGPENDING)) { -- q = kmem_cache_alloc(sigqueue_cachep, flags); -+ if (!fromslab) -+ q = get_task_cache(t); -+ if (!q) -+ q = kmem_cache_alloc(sigqueue_cachep, flags); - } else { - print_dropped_signal(sig); - } -@@ -392,6 +428,13 @@ - return q; - } - -+static struct sigqueue * -+__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, -+ int override_rlimit) -+{ -+ return __sigqueue_do_alloc(sig, t, flags, override_rlimit, 0); -+} -+ - static void __sigqueue_free(struct sigqueue *q) - { - if (q->flags & SIGQUEUE_PREALLOC) -@@ -401,6 +444,21 @@ - kmem_cache_free(sigqueue_cachep, q); - } - -+static void sigqueue_free_current(struct sigqueue *q) -+{ -+ struct user_struct *up; -+ -+ if (q->flags & SIGQUEUE_PREALLOC) -+ return; -+ -+ up = q->user; -+ if (rt_prio(current->normal_prio) && !put_task_cache(current, q)) { -+ atomic_dec(&up->sigpending); -+ free_uid(up); -+ } else -+ __sigqueue_free(q); -+} -+ - void flush_sigqueue(struct sigpending *queue) - { - struct sigqueue *q; -@@ -414,6 +472,21 @@ - } - - /* -+ * Called from __exit_signal. Flush tsk->pending and -+ * tsk->sigqueue_cache -+ */ -+void flush_task_sigqueue(struct task_struct *tsk) -+{ -+ struct sigqueue *q; -+ -+ flush_sigqueue(&tsk->pending); -+ -+ q = get_task_cache(tsk); -+ if (q) -+ kmem_cache_free(sigqueue_cachep, q); -+} -+ -+/* - * Flush all pending signals for a task. - */ - void __flush_signals(struct task_struct *t) -@@ -565,7 +638,7 @@ - still_pending: - list_del_init(&first->list); - copy_siginfo(info, &first->info); -- __sigqueue_free(first); -+ sigqueue_free_current(first); - } else { - /* - * Ok, it wasn't in the queue. This must be -@@ -611,6 +684,8 @@ - { - int signr; - -+ WARN_ON_ONCE(tsk != current); -+ - /* We only dequeue private signals from ourselves, we don't let - * signalfd steal them - */ -@@ -1207,8 +1282,8 @@ - * We don't want to have recursive SIGSEGV's etc, for example, - * that is why we also clear SIGNAL_UNKILLABLE. - */ --int --force_sig_info(int sig, struct siginfo *info, struct task_struct *t) -+static int -+do_force_sig_info(int sig, struct siginfo *info, struct task_struct *t) - { - unsigned long int flags; - int ret, blocked, ignored; -@@ -1233,6 +1308,39 @@ - return ret; - } - -+int force_sig_info(int sig, struct siginfo *info, struct task_struct *t) -+{ -+/* -+ * On some archs, PREEMPT_RT has to delay sending a signal from a trap -+ * since it can not enable preemption, and the signal code's spin_locks -+ * turn into mutexes. Instead, it must set TIF_NOTIFY_RESUME which will -+ * send the signal on exit of the trap. -+ */ -+#ifdef ARCH_RT_DELAYS_SIGNAL_SEND -+ if (in_atomic()) { -+ if (WARN_ON_ONCE(t != current)) -+ return 0; -+ if (WARN_ON_ONCE(t->forced_info.si_signo)) -+ return 0; -+ -+ if (is_si_special(info)) { -+ WARN_ON_ONCE(info != SEND_SIG_PRIV); -+ t->forced_info.si_signo = sig; -+ t->forced_info.si_errno = 0; -+ t->forced_info.si_code = SI_KERNEL; -+ t->forced_info.si_pid = 0; -+ t->forced_info.si_uid = 0; -+ } else { -+ t->forced_info = *info; -+ } -+ -+ set_tsk_thread_flag(t, TIF_NOTIFY_RESUME); -+ return 0; -+ } -+#endif -+ return do_force_sig_info(sig, info, t); -+} -+ - /* - * Nuke all other threads in the group. - */ -@@ -1267,12 +1375,12 @@ - * Disable interrupts early to avoid deadlocks. - * See rcu_read_unlock() comment header for details. - */ -- local_irq_save(*flags); -+ local_irq_save_nort(*flags); - rcu_read_lock(); - sighand = rcu_dereference(tsk->sighand); - if (unlikely(sighand == NULL)) { - rcu_read_unlock(); -- local_irq_restore(*flags); -+ local_irq_restore_nort(*flags); - break; - } - -@@ -1283,7 +1391,7 @@ - } - spin_unlock(&sighand->siglock); - rcu_read_unlock(); -- local_irq_restore(*flags); -+ local_irq_restore_nort(*flags); - } - - return sighand; -@@ -1528,7 +1636,8 @@ - */ - struct sigqueue *sigqueue_alloc(void) - { -- struct sigqueue *q = __sigqueue_alloc(-1, current, GFP_KERNEL, 0); -+ /* Preallocated sigqueue objects always from the slabcache ! */ -+ struct sigqueue *q = __sigqueue_do_alloc(-1, current, GFP_KERNEL, 0, 1); - - if (q) - q->flags |= SIGQUEUE_PREALLOC; -@@ -1889,15 +1998,7 @@ - if (gstop_done && ptrace_reparented(current)) - do_notify_parent_cldstop(current, false, why); - -- /* -- * Don't want to allow preemption here, because -- * sys_ptrace() needs this task to be inactive. -- * -- * XXX: implement read_unlock_no_resched(). -- */ -- preempt_disable(); - read_unlock(&tasklist_lock); -- preempt_enable_no_resched(); - freezable_schedule(); - } else { - /* -diff -Nur linux-3.18.14.orig/kernel/softirq.c linux-3.18.14-rt/kernel/softirq.c ---- linux-3.18.14.orig/kernel/softirq.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/softirq.c 2015-05-31 15:32:48.921635363 -0500 -@@ -21,10 +21,12 @@ - #include - #include - #include -+#include - #include - #include - #include - #include -+#include - #include - - #define CREATE_TRACE_POINTS -@@ -62,6 +64,98 @@ - "TASKLET", "SCHED", "HRTIMER", "RCU" - }; - -+#ifdef CONFIG_NO_HZ_COMMON -+# ifdef CONFIG_PREEMPT_RT_FULL -+ -+struct softirq_runner { -+ struct task_struct *runner[NR_SOFTIRQS]; -+}; -+ -+static DEFINE_PER_CPU(struct softirq_runner, softirq_runners); -+ -+static inline void softirq_set_runner(unsigned int sirq) -+{ -+ struct softirq_runner *sr = &__get_cpu_var(softirq_runners); -+ -+ sr->runner[sirq] = current; -+} -+ -+static inline void softirq_clr_runner(unsigned int sirq) -+{ -+ struct softirq_runner *sr = &__get_cpu_var(softirq_runners); -+ -+ sr->runner[sirq] = NULL; -+} -+ -+/* -+ * On preempt-rt a softirq running context might be blocked on a -+ * lock. There might be no other runnable task on this CPU because the -+ * lock owner runs on some other CPU. So we have to go into idle with -+ * the pending bit set. Therefor we need to check this otherwise we -+ * warn about false positives which confuses users and defeats the -+ * whole purpose of this test. -+ * -+ * This code is called with interrupts disabled. -+ */ -+void softirq_check_pending_idle(void) -+{ -+ static int rate_limit; -+ struct softirq_runner *sr = &__get_cpu_var(softirq_runners); -+ u32 warnpending; -+ int i; -+ -+ if (rate_limit >= 10) -+ return; -+ -+ warnpending = local_softirq_pending() & SOFTIRQ_STOP_IDLE_MASK; -+ for (i = 0; i < NR_SOFTIRQS; i++) { -+ struct task_struct *tsk = sr->runner[i]; -+ -+ /* -+ * The wakeup code in rtmutex.c wakes up the task -+ * _before_ it sets pi_blocked_on to NULL under -+ * tsk->pi_lock. So we need to check for both: state -+ * and pi_blocked_on. -+ */ -+ if (tsk) { -+ raw_spin_lock(&tsk->pi_lock); -+ if (tsk->pi_blocked_on || tsk->state == TASK_RUNNING) { -+ /* Clear all bits pending in that task */ -+ warnpending &= ~(tsk->softirqs_raised); -+ warnpending &= ~(1 << i); -+ } -+ raw_spin_unlock(&tsk->pi_lock); -+ } -+ } -+ -+ if (warnpending) { -+ printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n", -+ warnpending); -+ rate_limit++; -+ } -+} -+# else -+/* -+ * On !PREEMPT_RT we just printk rate limited: -+ */ -+void softirq_check_pending_idle(void) -+{ -+ static int rate_limit; -+ -+ if (rate_limit < 10 && -+ (local_softirq_pending() & SOFTIRQ_STOP_IDLE_MASK)) { -+ printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n", -+ local_softirq_pending()); -+ rate_limit++; -+ } -+} -+# endif -+ -+#else /* !CONFIG_NO_HZ_COMMON */ -+static inline void softirq_set_runner(unsigned int sirq) { } -+static inline void softirq_clr_runner(unsigned int sirq) { } -+#endif -+ - /* - * we cannot loop indefinitely here to avoid userspace starvation, - * but we also don't want to introduce a worst case 1/HZ latency -@@ -77,6 +171,70 @@ - wake_up_process(tsk); - } - -+static void handle_softirq(unsigned int vec_nr) -+{ -+ struct softirq_action *h = softirq_vec + vec_nr; -+ int prev_count; -+ -+ prev_count = preempt_count(); -+ -+ kstat_incr_softirqs_this_cpu(vec_nr); -+ -+ trace_softirq_entry(vec_nr); -+ h->action(h); -+ trace_softirq_exit(vec_nr); -+ if (unlikely(prev_count != preempt_count())) { -+ pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n", -+ vec_nr, softirq_to_name[vec_nr], h->action, -+ prev_count, preempt_count()); -+ preempt_count_set(prev_count); -+ } -+} -+ -+#ifndef CONFIG_PREEMPT_RT_FULL -+static inline int ksoftirqd_softirq_pending(void) -+{ -+ return local_softirq_pending(); -+} -+ -+static void handle_pending_softirqs(u32 pending, int need_rcu_bh_qs) -+{ -+ struct softirq_action *h = softirq_vec; -+ int softirq_bit; -+ -+ local_irq_enable(); -+ -+ h = softirq_vec; -+ -+ while ((softirq_bit = ffs(pending))) { -+ unsigned int vec_nr; -+ -+ h += softirq_bit - 1; -+ vec_nr = h - softirq_vec; -+ handle_softirq(vec_nr); -+ -+ h++; -+ pending >>= softirq_bit; -+ } -+ -+ if (need_rcu_bh_qs) -+ rcu_bh_qs(); -+ local_irq_disable(); -+} -+ -+static void run_ksoftirqd(unsigned int cpu) -+{ -+ local_irq_disable(); -+ if (ksoftirqd_softirq_pending()) { -+ __do_softirq(); -+ rcu_note_context_switch(cpu); -+ local_irq_enable(); -+ cond_resched(); -+ return; -+ } -+ local_irq_enable(); -+} -+ - /* - * preempt_count and SOFTIRQ_OFFSET usage: - * - preempt_count is changed by SOFTIRQ_OFFSET on entering or leaving -@@ -228,10 +386,8 @@ - unsigned long end = jiffies + MAX_SOFTIRQ_TIME; - unsigned long old_flags = current->flags; - int max_restart = MAX_SOFTIRQ_RESTART; -- struct softirq_action *h; - bool in_hardirq; - __u32 pending; -- int softirq_bit; - - /* - * Mask out PF_MEMALLOC s current task context is borrowed for the -@@ -250,36 +406,7 @@ - /* Reset the pending bitmask before enabling irqs */ - set_softirq_pending(0); - -- local_irq_enable(); -- -- h = softirq_vec; -- -- while ((softirq_bit = ffs(pending))) { -- unsigned int vec_nr; -- int prev_count; -- -- h += softirq_bit - 1; -- -- vec_nr = h - softirq_vec; -- prev_count = preempt_count(); -- -- kstat_incr_softirqs_this_cpu(vec_nr); -- -- trace_softirq_entry(vec_nr); -- h->action(h); -- trace_softirq_exit(vec_nr); -- if (unlikely(prev_count != preempt_count())) { -- pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n", -- vec_nr, softirq_to_name[vec_nr], h->action, -- prev_count, preempt_count()); -- preempt_count_set(prev_count); -- } -- h++; -- pending >>= softirq_bit; -- } -- -- rcu_bh_qs(); -- local_irq_disable(); -+ handle_pending_softirqs(pending, 1); - - pending = local_softirq_pending(); - if (pending) { -@@ -316,6 +443,285 @@ - } - - /* -+ * This function must run with irqs disabled! -+ */ -+void raise_softirq_irqoff(unsigned int nr) -+{ -+ __raise_softirq_irqoff(nr); -+ -+ /* -+ * If we're in an interrupt or softirq, we're done -+ * (this also catches softirq-disabled code). We will -+ * actually run the softirq once we return from -+ * the irq or softirq. -+ * -+ * Otherwise we wake up ksoftirqd to make sure we -+ * schedule the softirq soon. -+ */ -+ if (!in_interrupt()) -+ wakeup_softirqd(); -+} -+ -+void __raise_softirq_irqoff(unsigned int nr) -+{ -+ trace_softirq_raise(nr); -+ or_softirq_pending(1UL << nr); -+} -+ -+static inline void local_bh_disable_nort(void) { local_bh_disable(); } -+static inline void _local_bh_enable_nort(void) { _local_bh_enable(); } -+static void ksoftirqd_set_sched_params(unsigned int cpu) { } -+static void ksoftirqd_clr_sched_params(unsigned int cpu, bool online) { } -+ -+#else /* !PREEMPT_RT_FULL */ -+ -+/* -+ * On RT we serialize softirq execution with a cpu local lock per softirq -+ */ -+static DEFINE_PER_CPU(struct local_irq_lock [NR_SOFTIRQS], local_softirq_locks); -+ -+void __init softirq_early_init(void) -+{ -+ int i; -+ -+ for (i = 0; i < NR_SOFTIRQS; i++) -+ local_irq_lock_init(local_softirq_locks[i]); -+} -+ -+static void lock_softirq(int which) -+{ -+ local_lock(local_softirq_locks[which]); -+} -+ -+static void unlock_softirq(int which) -+{ -+ local_unlock(local_softirq_locks[which]); -+} -+ -+static void do_single_softirq(int which, int need_rcu_bh_qs) -+{ -+ unsigned long old_flags = current->flags; -+ -+ current->flags &= ~PF_MEMALLOC; -+ vtime_account_irq_enter(current); -+ current->flags |= PF_IN_SOFTIRQ; -+ lockdep_softirq_enter(); -+ local_irq_enable(); -+ handle_softirq(which); -+ local_irq_disable(); -+ lockdep_softirq_exit(); -+ current->flags &= ~PF_IN_SOFTIRQ; -+ vtime_account_irq_enter(current); -+ tsk_restore_flags(current, old_flags, PF_MEMALLOC); -+} -+ -+/* -+ * Called with interrupts disabled. Process softirqs which were raised -+ * in current context (or on behalf of ksoftirqd). -+ */ -+static void do_current_softirqs(int need_rcu_bh_qs) -+{ -+ while (current->softirqs_raised) { -+ int i = __ffs(current->softirqs_raised); -+ unsigned int pending, mask = (1U << i); -+ -+ current->softirqs_raised &= ~mask; -+ local_irq_enable(); -+ -+ /* -+ * If the lock is contended, we boost the owner to -+ * process the softirq or leave the critical section -+ * now. -+ */ -+ lock_softirq(i); -+ local_irq_disable(); -+ softirq_set_runner(i); -+ /* -+ * Check with the local_softirq_pending() bits, -+ * whether we need to process this still or if someone -+ * else took care of it. -+ */ -+ pending = local_softirq_pending(); -+ if (pending & mask) { -+ set_softirq_pending(pending & ~mask); -+ do_single_softirq(i, need_rcu_bh_qs); -+ } -+ softirq_clr_runner(i); -+ unlock_softirq(i); -+ WARN_ON(current->softirq_nestcnt != 1); -+ } -+} -+ -+static void __local_bh_disable(void) -+{ -+ if (++current->softirq_nestcnt == 1) -+ migrate_disable(); -+} -+ -+void local_bh_disable(void) -+{ -+ __local_bh_disable(); -+} -+EXPORT_SYMBOL(local_bh_disable); -+ -+void __local_bh_disable_ip(unsigned long ip, unsigned int cnt) -+{ -+ __local_bh_disable(); -+ if (cnt & PREEMPT_CHECK_OFFSET) -+ preempt_disable(); -+} -+ -+static void __local_bh_enable(void) -+{ -+ if (WARN_ON(current->softirq_nestcnt == 0)) -+ return; -+ -+ local_irq_disable(); -+ if (current->softirq_nestcnt == 1 && current->softirqs_raised) -+ do_current_softirqs(1); -+ local_irq_enable(); -+ -+ if (--current->softirq_nestcnt == 0) -+ migrate_enable(); -+} -+ -+void local_bh_enable(void) -+{ -+ __local_bh_enable(); -+} -+EXPORT_SYMBOL(local_bh_enable); -+ -+extern void __local_bh_enable_ip(unsigned long ip, unsigned int cnt) -+{ -+ __local_bh_enable(); -+ if (cnt & PREEMPT_CHECK_OFFSET) -+ preempt_enable(); -+} -+ -+void local_bh_enable_ip(unsigned long ip) -+{ -+ local_bh_enable(); -+} -+EXPORT_SYMBOL(local_bh_enable_ip); -+ -+void _local_bh_enable(void) -+{ -+ if (WARN_ON(current->softirq_nestcnt == 0)) -+ return; -+ if (--current->softirq_nestcnt == 0) -+ migrate_enable(); -+} -+EXPORT_SYMBOL(_local_bh_enable); -+ -+int in_serving_softirq(void) -+{ -+ return current->flags & PF_IN_SOFTIRQ; -+} -+EXPORT_SYMBOL(in_serving_softirq); -+ -+/* Called with preemption disabled */ -+static void run_ksoftirqd(unsigned int cpu) -+{ -+ local_irq_disable(); -+ current->softirq_nestcnt++; -+ -+ do_current_softirqs(1); -+ current->softirq_nestcnt--; -+ rcu_note_context_switch(cpu); -+ local_irq_enable(); -+} -+ -+/* -+ * Called from netif_rx_ni(). Preemption enabled, but migration -+ * disabled. So the cpu can't go away under us. -+ */ -+void thread_do_softirq(void) -+{ -+ if (!in_serving_softirq() && current->softirqs_raised) { -+ current->softirq_nestcnt++; -+ do_current_softirqs(0); -+ current->softirq_nestcnt--; -+ } -+} -+ -+static void do_raise_softirq_irqoff(unsigned int nr) -+{ -+ trace_softirq_raise(nr); -+ or_softirq_pending(1UL << nr); -+ -+ /* -+ * If we are not in a hard interrupt and inside a bh disabled -+ * region, we simply raise the flag on current. local_bh_enable() -+ * will make sure that the softirq is executed. Otherwise we -+ * delegate it to ksoftirqd. -+ */ -+ if (!in_irq() && current->softirq_nestcnt) -+ current->softirqs_raised |= (1U << nr); -+ else if (__this_cpu_read(ksoftirqd)) -+ __this_cpu_read(ksoftirqd)->softirqs_raised |= (1U << nr); -+} -+ -+void __raise_softirq_irqoff(unsigned int nr) -+{ -+ do_raise_softirq_irqoff(nr); -+ if (!in_irq() && !current->softirq_nestcnt) -+ wakeup_softirqd(); -+} -+ -+/* -+ * This function must run with irqs disabled! -+ */ -+void raise_softirq_irqoff(unsigned int nr) -+{ -+ do_raise_softirq_irqoff(nr); -+ -+ /* -+ * If we're in an hard interrupt we let irq return code deal -+ * with the wakeup of ksoftirqd. -+ */ -+ if (in_irq()) -+ return; -+ /* -+ * If we are in thread context but outside of a bh disabled -+ * region, we need to wake ksoftirqd as well. -+ * -+ * CHECKME: Some of the places which do that could be wrapped -+ * into local_bh_disable/enable pairs. Though it's unclear -+ * whether this is worth the effort. To find those places just -+ * raise a WARN() if the condition is met. -+ */ -+ if (!current->softirq_nestcnt) -+ wakeup_softirqd(); -+} -+ -+static inline int ksoftirqd_softirq_pending(void) -+{ -+ return current->softirqs_raised; -+} -+ -+static inline void local_bh_disable_nort(void) { } -+static inline void _local_bh_enable_nort(void) { } -+ -+static inline void ksoftirqd_set_sched_params(unsigned int cpu) -+{ -+ struct sched_param param = { .sched_priority = 1 }; -+ -+ sched_setscheduler(current, SCHED_FIFO, ¶m); -+ /* Take over all pending softirqs when starting */ -+ local_irq_disable(); -+ current->softirqs_raised = local_softirq_pending(); -+ local_irq_enable(); -+} -+ -+static inline void ksoftirqd_clr_sched_params(unsigned int cpu, bool online) -+{ -+ struct sched_param param = { .sched_priority = 0 }; -+ -+ sched_setscheduler(current, SCHED_NORMAL, ¶m); -+} -+ -+#endif /* PREEMPT_RT_FULL */ -+/* - * Enter an interrupt context. - */ - void irq_enter(void) -@@ -326,9 +732,9 @@ - * Prevent raise_softirq from needlessly waking up ksoftirqd - * here, as softirq will be serviced on return from interrupt. - */ -- local_bh_disable(); -+ local_bh_disable_nort(); - tick_irq_enter(); -- _local_bh_enable(); -+ _local_bh_enable_nort(); - } - - __irq_enter(); -@@ -336,6 +742,7 @@ - - static inline void invoke_softirq(void) - { -+#ifndef CONFIG_PREEMPT_RT_FULL - if (!force_irqthreads) { - #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK - /* -@@ -355,6 +762,15 @@ - } else { - wakeup_softirqd(); - } -+#else /* PREEMPT_RT_FULL */ -+ unsigned long flags; -+ -+ local_irq_save(flags); -+ if (__this_cpu_read(ksoftirqd) && -+ __this_cpu_read(ksoftirqd)->softirqs_raised) -+ wakeup_softirqd(); -+ local_irq_restore(flags); -+#endif - } - - static inline void tick_irq_exit(void) -@@ -391,26 +807,6 @@ - trace_hardirq_exit(); /* must be last! */ - } - --/* -- * This function must run with irqs disabled! -- */ --inline void raise_softirq_irqoff(unsigned int nr) --{ -- __raise_softirq_irqoff(nr); -- -- /* -- * If we're in an interrupt or softirq, we're done -- * (this also catches softirq-disabled code). We will -- * actually run the softirq once we return from -- * the irq or softirq. -- * -- * Otherwise we wake up ksoftirqd to make sure we -- * schedule the softirq soon. -- */ -- if (!in_interrupt()) -- wakeup_softirqd(); --} -- - void raise_softirq(unsigned int nr) - { - unsigned long flags; -@@ -420,12 +816,6 @@ - local_irq_restore(flags); - } - --void __raise_softirq_irqoff(unsigned int nr) --{ -- trace_softirq_raise(nr); -- or_softirq_pending(1UL << nr); --} -- - void open_softirq(int nr, void (*action)(struct softirq_action *)) - { - softirq_vec[nr].action = action; -@@ -442,15 +832,45 @@ - static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec); - static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec); - -+static void inline -+__tasklet_common_schedule(struct tasklet_struct *t, struct tasklet_head *head, unsigned int nr) -+{ -+ if (tasklet_trylock(t)) { -+again: -+ /* We may have been preempted before tasklet_trylock -+ * and __tasklet_action may have already run. -+ * So double check the sched bit while the takslet -+ * is locked before adding it to the list. -+ */ -+ if (test_bit(TASKLET_STATE_SCHED, &t->state)) { -+ t->next = NULL; -+ *head->tail = t; -+ head->tail = &(t->next); -+ raise_softirq_irqoff(nr); -+ tasklet_unlock(t); -+ } else { -+ /* This is subtle. If we hit the corner case above -+ * It is possible that we get preempted right here, -+ * and another task has successfully called -+ * tasklet_schedule(), then this function, and -+ * failed on the trylock. Thus we must be sure -+ * before releasing the tasklet lock, that the -+ * SCHED_BIT is clear. Otherwise the tasklet -+ * may get its SCHED_BIT set, but not added to the -+ * list -+ */ -+ if (!tasklet_tryunlock(t)) -+ goto again; -+ } -+ } -+} -+ - void __tasklet_schedule(struct tasklet_struct *t) - { - unsigned long flags; - - local_irq_save(flags); -- t->next = NULL; -- *__this_cpu_read(tasklet_vec.tail) = t; -- __this_cpu_write(tasklet_vec.tail, &(t->next)); -- raise_softirq_irqoff(TASKLET_SOFTIRQ); -+ __tasklet_common_schedule(t, &__get_cpu_var(tasklet_vec), TASKLET_SOFTIRQ); - local_irq_restore(flags); - } - EXPORT_SYMBOL(__tasklet_schedule); -@@ -460,10 +880,7 @@ - unsigned long flags; - - local_irq_save(flags); -- t->next = NULL; -- *__this_cpu_read(tasklet_hi_vec.tail) = t; -- __this_cpu_write(tasklet_hi_vec.tail, &(t->next)); -- raise_softirq_irqoff(HI_SOFTIRQ); -+ __tasklet_common_schedule(t, &__get_cpu_var(tasklet_hi_vec), HI_SOFTIRQ); - local_irq_restore(flags); - } - EXPORT_SYMBOL(__tasklet_hi_schedule); -@@ -472,48 +889,116 @@ - { - BUG_ON(!irqs_disabled()); - -- t->next = __this_cpu_read(tasklet_hi_vec.head); -- __this_cpu_write(tasklet_hi_vec.head, t); -- __raise_softirq_irqoff(HI_SOFTIRQ); -+ __tasklet_hi_schedule(t); - } - EXPORT_SYMBOL(__tasklet_hi_schedule_first); - --static void tasklet_action(struct softirq_action *a) -+void tasklet_enable(struct tasklet_struct *t) - { -- struct tasklet_struct *list; -+ if (!atomic_dec_and_test(&t->count)) -+ return; -+ if (test_and_clear_bit(TASKLET_STATE_PENDING, &t->state)) -+ tasklet_schedule(t); -+} -+EXPORT_SYMBOL(tasklet_enable); - -- local_irq_disable(); -- list = __this_cpu_read(tasklet_vec.head); -- __this_cpu_write(tasklet_vec.head, NULL); -- __this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head)); -- local_irq_enable(); -+void tasklet_hi_enable(struct tasklet_struct *t) -+{ -+ if (!atomic_dec_and_test(&t->count)) -+ return; -+ if (test_and_clear_bit(TASKLET_STATE_PENDING, &t->state)) -+ tasklet_hi_schedule(t); -+} -+EXPORT_SYMBOL(tasklet_hi_enable); -+ -+static void __tasklet_action(struct softirq_action *a, -+ struct tasklet_struct *list) -+{ -+ int loops = 1000000; - - while (list) { - struct tasklet_struct *t = list; - - list = list->next; - -- if (tasklet_trylock(t)) { -- if (!atomic_read(&t->count)) { -- if (!test_and_clear_bit(TASKLET_STATE_SCHED, -- &t->state)) -- BUG(); -- t->func(t->data); -- tasklet_unlock(t); -- continue; -- } -- tasklet_unlock(t); -+ /* -+ * Should always succeed - after a tasklist got on the -+ * list (after getting the SCHED bit set from 0 to 1), -+ * nothing but the tasklet softirq it got queued to can -+ * lock it: -+ */ -+ if (!tasklet_trylock(t)) { -+ WARN_ON(1); -+ continue; - } - -- local_irq_disable(); - t->next = NULL; -- *__this_cpu_read(tasklet_vec.tail) = t; -- __this_cpu_write(tasklet_vec.tail, &(t->next)); -- __raise_softirq_irqoff(TASKLET_SOFTIRQ); -- local_irq_enable(); -+ -+ /* -+ * If we cannot handle the tasklet because it's disabled, -+ * mark it as pending. tasklet_enable() will later -+ * re-schedule the tasklet. -+ */ -+ if (unlikely(atomic_read(&t->count))) { -+out_disabled: -+ /* implicit unlock: */ -+ wmb(); -+ t->state = TASKLET_STATEF_PENDING; -+ continue; -+ } -+ -+ /* -+ * After this point on the tasklet might be rescheduled -+ * on another CPU, but it can only be added to another -+ * CPU's tasklet list if we unlock the tasklet (which we -+ * dont do yet). -+ */ -+ if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) -+ WARN_ON(1); -+ -+again: -+ t->func(t->data); -+ -+ /* -+ * Try to unlock the tasklet. We must use cmpxchg, because -+ * another CPU might have scheduled or disabled the tasklet. -+ * We only allow the STATE_RUN -> 0 transition here. -+ */ -+ while (!tasklet_tryunlock(t)) { -+ /* -+ * If it got disabled meanwhile, bail out: -+ */ -+ if (atomic_read(&t->count)) -+ goto out_disabled; -+ /* -+ * If it got scheduled meanwhile, re-execute -+ * the tasklet function: -+ */ -+ if (test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) -+ goto again; -+ if (!--loops) { -+ printk("hm, tasklet state: %08lx\n", t->state); -+ WARN_ON(1); -+ tasklet_unlock(t); -+ break; -+ } -+ } - } - } - -+static void tasklet_action(struct softirq_action *a) -+{ -+ struct tasklet_struct *list; -+ -+ local_irq_disable(); -+ list = __get_cpu_var(tasklet_vec).head; -+ __get_cpu_var(tasklet_vec).head = NULL; -+ __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head; -+ local_irq_enable(); -+ -+ __tasklet_action(a, list); -+} -+ - static void tasklet_hi_action(struct softirq_action *a) - { - struct tasklet_struct *list; -@@ -524,30 +1009,7 @@ - __this_cpu_write(tasklet_hi_vec.tail, this_cpu_ptr(&tasklet_hi_vec.head)); - local_irq_enable(); - -- while (list) { -- struct tasklet_struct *t = list; -- -- list = list->next; -- -- if (tasklet_trylock(t)) { -- if (!atomic_read(&t->count)) { -- if (!test_and_clear_bit(TASKLET_STATE_SCHED, -- &t->state)) -- BUG(); -- t->func(t->data); -- tasklet_unlock(t); -- continue; -- } -- tasklet_unlock(t); -- } -- -- local_irq_disable(); -- t->next = NULL; -- *__this_cpu_read(tasklet_hi_vec.tail) = t; -- __this_cpu_write(tasklet_hi_vec.tail, &(t->next)); -- __raise_softirq_irqoff(HI_SOFTIRQ); -- local_irq_enable(); -- } -+ __tasklet_action(a, list); - } - - void tasklet_init(struct tasklet_struct *t, -@@ -568,7 +1030,7 @@ - - while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) { - do { -- yield(); -+ msleep(1); - } while (test_bit(TASKLET_STATE_SCHED, &t->state)); - } - tasklet_unlock_wait(t); -@@ -642,26 +1104,26 @@ - open_softirq(HI_SOFTIRQ, tasklet_hi_action); - } - --static int ksoftirqd_should_run(unsigned int cpu) --{ -- return local_softirq_pending(); --} -- --static void run_ksoftirqd(unsigned int cpu) -+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) -+void tasklet_unlock_wait(struct tasklet_struct *t) - { -- local_irq_disable(); -- if (local_softirq_pending()) { -+ while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { - /* -- * We can safely run softirq on inline stack, as we are not deep -- * in the task stack here. -+ * Hack for now to avoid this busy-loop: - */ -- __do_softirq(); -- rcu_note_context_switch(cpu); -- local_irq_enable(); -- cond_resched(); -- return; -+#ifdef CONFIG_PREEMPT_RT_FULL -+ msleep(1); -+#else -+ barrier(); -+#endif - } -- local_irq_enable(); -+} -+EXPORT_SYMBOL(tasklet_unlock_wait); -+#endif -+ -+static int ksoftirqd_should_run(unsigned int cpu) -+{ -+ return ksoftirqd_softirq_pending(); - } - - #ifdef CONFIG_HOTPLUG_CPU -@@ -743,6 +1205,8 @@ - - static struct smp_hotplug_thread softirq_threads = { - .store = &ksoftirqd, -+ .setup = ksoftirqd_set_sched_params, -+ .cleanup = ksoftirqd_clr_sched_params, - .thread_should_run = ksoftirqd_should_run, - .thread_fn = run_ksoftirqd, - .thread_comm = "ksoftirqd/%u", -diff -Nur linux-3.18.14.orig/kernel/stop_machine.c linux-3.18.14-rt/kernel/stop_machine.c ---- linux-3.18.14.orig/kernel/stop_machine.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/stop_machine.c 2015-05-31 15:32:48.925635362 -0500 -@@ -30,12 +30,12 @@ - atomic_t nr_todo; /* nr left to execute */ - bool executed; /* actually executed? */ - int ret; /* collected return value */ -- struct completion completion; /* fired if nr_todo reaches 0 */ -+ struct task_struct *waiter; /* woken when nr_todo reaches 0 */ - }; - - /* the actual stopper, one per every possible cpu, enabled on online cpus */ - struct cpu_stopper { -- spinlock_t lock; -+ raw_spinlock_t lock; - bool enabled; /* is this stopper enabled? */ - struct list_head works; /* list of pending works */ - }; -@@ -56,7 +56,7 @@ - { - memset(done, 0, sizeof(*done)); - atomic_set(&done->nr_todo, nr_todo); -- init_completion(&done->completion); -+ done->waiter = current; - } - - /* signal completion unless @done is NULL */ -@@ -65,8 +65,10 @@ - if (done) { - if (executed) - done->executed = true; -- if (atomic_dec_and_test(&done->nr_todo)) -- complete(&done->completion); -+ if (atomic_dec_and_test(&done->nr_todo)) { -+ wake_up_process(done->waiter); -+ done->waiter = NULL; -+ } - } - } - -@@ -78,7 +80,7 @@ - - unsigned long flags; - -- spin_lock_irqsave(&stopper->lock, flags); -+ raw_spin_lock_irqsave(&stopper->lock, flags); - - if (stopper->enabled) { - list_add_tail(&work->list, &stopper->works); -@@ -86,7 +88,23 @@ - } else - cpu_stop_signal_done(work->done, false); - -- spin_unlock_irqrestore(&stopper->lock, flags); -+ raw_spin_unlock_irqrestore(&stopper->lock, flags); -+} -+ -+static void wait_for_stop_done(struct cpu_stop_done *done) -+{ -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ while (atomic_read(&done->nr_todo)) { -+ schedule(); -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ } -+ /* -+ * We need to wait until cpu_stop_signal_done() has cleared -+ * done->waiter. -+ */ -+ while (done->waiter) -+ cpu_relax(); -+ set_current_state(TASK_RUNNING); - } - - /** -@@ -120,7 +138,7 @@ - - cpu_stop_init_done(&done, 1); - cpu_stop_queue_work(cpu, &work); -- wait_for_completion(&done.completion); -+ wait_for_stop_done(&done); - return done.executed ? done.ret : -ENOENT; - } - -@@ -248,7 +266,7 @@ - struct irq_cpu_stop_queue_work_info call_args; - struct multi_stop_data msdata; - -- preempt_disable(); -+ preempt_disable_nort(); - msdata = (struct multi_stop_data){ - .fn = fn, - .data = arg, -@@ -281,7 +299,7 @@ - * This relies on the stopper workqueues to be FIFO. - */ - if (!cpu_active(cpu1) || !cpu_active(cpu2)) { -- preempt_enable(); -+ preempt_enable_nort(); - return -ENOENT; - } - -@@ -295,9 +313,9 @@ - &irq_cpu_stop_queue_work, - &call_args, 1); - lg_local_unlock(&stop_cpus_lock); -- preempt_enable(); -+ preempt_enable_nort(); - -- wait_for_completion(&done.completion); -+ wait_for_stop_done(&done); - - return done.executed ? done.ret : -ENOENT; - } -@@ -329,7 +347,7 @@ - - static void queue_stop_cpus_work(const struct cpumask *cpumask, - cpu_stop_fn_t fn, void *arg, -- struct cpu_stop_done *done) -+ struct cpu_stop_done *done, bool inactive) - { - struct cpu_stop_work *work; - unsigned int cpu; -@@ -343,11 +361,13 @@ - } - - /* -- * Disable preemption while queueing to avoid getting -- * preempted by a stopper which might wait for other stoppers -- * to enter @fn which can lead to deadlock. -+ * Make sure that all work is queued on all cpus before -+ * any of the cpus can execute it. - */ -- lg_global_lock(&stop_cpus_lock); -+ if (!inactive) -+ lg_global_lock(&stop_cpus_lock); -+ else -+ lg_global_trylock_relax(&stop_cpus_lock); - for_each_cpu(cpu, cpumask) - cpu_stop_queue_work(cpu, &per_cpu(stop_cpus_work, cpu)); - lg_global_unlock(&stop_cpus_lock); -@@ -359,8 +379,8 @@ - struct cpu_stop_done done; - - cpu_stop_init_done(&done, cpumask_weight(cpumask)); -- queue_stop_cpus_work(cpumask, fn, arg, &done); -- wait_for_completion(&done.completion); -+ queue_stop_cpus_work(cpumask, fn, arg, &done, false); -+ wait_for_stop_done(&done); - return done.executed ? done.ret : -ENOENT; - } - -@@ -439,9 +459,9 @@ - unsigned long flags; - int run; - -- spin_lock_irqsave(&stopper->lock, flags); -+ raw_spin_lock_irqsave(&stopper->lock, flags); - run = !list_empty(&stopper->works); -- spin_unlock_irqrestore(&stopper->lock, flags); -+ raw_spin_unlock_irqrestore(&stopper->lock, flags); - return run; - } - -@@ -453,13 +473,13 @@ - - repeat: - work = NULL; -- spin_lock_irq(&stopper->lock); -+ raw_spin_lock_irq(&stopper->lock); - if (!list_empty(&stopper->works)) { - work = list_first_entry(&stopper->works, - struct cpu_stop_work, list); - list_del_init(&work->list); - } -- spin_unlock_irq(&stopper->lock); -+ raw_spin_unlock_irq(&stopper->lock); - - if (work) { - cpu_stop_fn_t fn = work->fn; -@@ -467,6 +487,16 @@ - struct cpu_stop_done *done = work->done; - char ksym_buf[KSYM_NAME_LEN] __maybe_unused; - -+ /* -+ * Wait until the stopper finished scheduling on all -+ * cpus -+ */ -+ lg_global_lock(&stop_cpus_lock); -+ /* -+ * Let other cpu threads continue as well -+ */ -+ lg_global_unlock(&stop_cpus_lock); -+ - /* cpu stop callbacks are not allowed to sleep */ - preempt_disable(); - -@@ -481,7 +511,13 @@ - kallsyms_lookup((unsigned long)fn, NULL, NULL, NULL, - ksym_buf), arg); - -+ /* -+ * Make sure that the wakeup and setting done->waiter -+ * to NULL is atomic. -+ */ -+ local_irq_disable(); - cpu_stop_signal_done(done, true); -+ local_irq_enable(); - goto repeat; - } - } -@@ -500,20 +536,20 @@ - unsigned long flags; - - /* drain remaining works */ -- spin_lock_irqsave(&stopper->lock, flags); -+ raw_spin_lock_irqsave(&stopper->lock, flags); - list_for_each_entry(work, &stopper->works, list) - cpu_stop_signal_done(work->done, false); - stopper->enabled = false; -- spin_unlock_irqrestore(&stopper->lock, flags); -+ raw_spin_unlock_irqrestore(&stopper->lock, flags); - } - - static void cpu_stop_unpark(unsigned int cpu) - { - struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); - -- spin_lock_irq(&stopper->lock); -+ raw_spin_lock_irq(&stopper->lock); - stopper->enabled = true; -- spin_unlock_irq(&stopper->lock); -+ raw_spin_unlock_irq(&stopper->lock); - } - - static struct smp_hotplug_thread cpu_stop_threads = { -@@ -535,10 +571,12 @@ - for_each_possible_cpu(cpu) { - struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); - -- spin_lock_init(&stopper->lock); -+ raw_spin_lock_init(&stopper->lock); - INIT_LIST_HEAD(&stopper->works); - } - -+ lg_lock_init(&stop_cpus_lock, "stop_cpus_lock"); -+ - BUG_ON(smpboot_register_percpu_thread(&cpu_stop_threads)); - stop_machine_initialized = true; - return 0; -@@ -634,11 +672,11 @@ - set_state(&msdata, MULTI_STOP_PREPARE); - cpu_stop_init_done(&done, num_active_cpus()); - queue_stop_cpus_work(cpu_active_mask, multi_cpu_stop, &msdata, -- &done); -+ &done, true); - ret = multi_cpu_stop(&msdata); - - /* Busy wait for completion. */ -- while (!completion_done(&done.completion)) -+ while (atomic_read(&done.nr_todo)) - cpu_relax(); - - mutex_unlock(&stop_cpus_mutex); -diff -Nur linux-3.18.14.orig/kernel/time/hrtimer.c linux-3.18.14-rt/kernel/time/hrtimer.c ---- linux-3.18.14.orig/kernel/time/hrtimer.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/hrtimer.c 2015-05-31 15:32:48.925635362 -0500 -@@ -48,11 +48,13 @@ - #include - #include - #include -+#include - #include - - #include - - #include -+#include - - #include "timekeeping.h" - -@@ -568,8 +570,7 @@ - * When the callback is running, we do not reprogram the clock event - * device. The timer callback is either running on a different CPU or - * the callback is executed in the hrtimer_interrupt context. The -- * reprogramming is handled either by the softirq, which called the -- * callback or at the end of the hrtimer_interrupt. -+ * reprogramming is handled at the end of the hrtimer_interrupt. - */ - if (hrtimer_callback_running(timer)) - return 0; -@@ -604,6 +605,9 @@ - return res; - } - -+static void __run_hrtimer(struct hrtimer *timer, ktime_t *now); -+static int hrtimer_rt_defer(struct hrtimer *timer); -+ - /* - * Initialize the high resolution related parts of cpu_base - */ -@@ -613,6 +617,21 @@ - base->hres_active = 0; - } - -+static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, -+ struct hrtimer_clock_base *base, -+ int wakeup) -+{ -+ if (!hrtimer_reprogram(timer, base)) -+ return 0; -+ if (!wakeup) -+ return -ETIME; -+#ifdef CONFIG_PREEMPT_RT_BASE -+ if (!hrtimer_rt_defer(timer)) -+ return -ETIME; -+#endif -+ return 1; -+} -+ - static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base) - { - ktime_t *offs_real = &base->clock_base[HRTIMER_BASE_REALTIME].offset; -@@ -678,6 +697,44 @@ - - static DECLARE_WORK(hrtimer_work, clock_was_set_work); - -+#ifdef CONFIG_PREEMPT_RT_FULL -+/* -+ * RT can not call schedule_work from real interrupt context. -+ * Need to make a thread to do the real work. -+ */ -+static struct task_struct *clock_set_delay_thread; -+static bool do_clock_set_delay; -+ -+static int run_clock_set_delay(void *ignore) -+{ -+ while (!kthread_should_stop()) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ if (do_clock_set_delay) { -+ do_clock_set_delay = false; -+ schedule_work(&hrtimer_work); -+ } -+ schedule(); -+ } -+ __set_current_state(TASK_RUNNING); -+ return 0; -+} -+ -+void clock_was_set_delayed(void) -+{ -+ do_clock_set_delay = true; -+ /* Make visible before waking up process */ -+ smp_wmb(); -+ wake_up_process(clock_set_delay_thread); -+} -+ -+static __init int create_clock_set_delay_thread(void) -+{ -+ clock_set_delay_thread = kthread_run(run_clock_set_delay, NULL, "kclksetdelayd"); -+ BUG_ON(!clock_set_delay_thread); -+ return 0; -+} -+early_initcall(create_clock_set_delay_thread); -+#else /* PREEMPT_RT_FULL */ - /* - * Called from timekeeping and resume code to reprogramm the hrtimer - * interrupt device on all cpus. -@@ -686,6 +743,7 @@ - { - schedule_work(&hrtimer_work); - } -+#endif - - #else - -@@ -694,6 +752,13 @@ - static inline int hrtimer_switch_to_hres(void) { return 0; } - static inline void - hrtimer_force_reprogram(struct hrtimer_cpu_base *base, int skip_equal) { } -+static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, -+ struct hrtimer_clock_base *base, -+ int wakeup) -+{ -+ return 0; -+} -+ - static inline int hrtimer_reprogram(struct hrtimer *timer, - struct hrtimer_clock_base *base) - { -@@ -701,7 +766,6 @@ - } - static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) { } - static inline void retrigger_next_event(void *arg) { } -- - #endif /* CONFIG_HIGH_RES_TIMERS */ - - /* -@@ -819,6 +883,32 @@ - } - EXPORT_SYMBOL_GPL(hrtimer_forward); - -+#ifdef CONFIG_PREEMPT_RT_BASE -+# define wake_up_timer_waiters(b) wake_up(&(b)->wait) -+ -+/** -+ * hrtimer_wait_for_timer - Wait for a running timer -+ * -+ * @timer: timer to wait for -+ * -+ * The function waits in case the timers callback function is -+ * currently executed on the waitqueue of the timer base. The -+ * waitqueue is woken up after the timer callback function has -+ * finished execution. -+ */ -+void hrtimer_wait_for_timer(const struct hrtimer *timer) -+{ -+ struct hrtimer_clock_base *base = timer->base; -+ -+ if (base && base->cpu_base && !timer->irqsafe) -+ wait_event(base->cpu_base->wait, -+ !(timer->state & HRTIMER_STATE_CALLBACK)); -+} -+ -+#else -+# define wake_up_timer_waiters(b) do { } while (0) -+#endif -+ - /* - * enqueue_hrtimer - internal function to (re)start a timer - * -@@ -862,6 +952,11 @@ - if (!(timer->state & HRTIMER_STATE_ENQUEUED)) - goto out; - -+ if (unlikely(!list_empty(&timer->cb_entry))) { -+ list_del_init(&timer->cb_entry); -+ goto out; -+ } -+ - next_timer = timerqueue_getnext(&base->active); - timerqueue_del(&base->active, &timer->node); - if (&timer->node == next_timer) { -@@ -949,7 +1044,16 @@ - new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED); - - timer_stats_hrtimer_set_start_info(timer); -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ { -+ ktime_t now = new_base->get_time(); - -+ if (ktime_to_ns(tim) < ktime_to_ns(now)) -+ timer->praecox = now; -+ else -+ timer->praecox = ktime_set(0, 0); -+ } -+#endif - leftmost = enqueue_hrtimer(timer, new_base); - - if (!leftmost) { -@@ -963,15 +1067,26 @@ - * on dynticks target. - */ - wake_up_nohz_cpu(new_base->cpu_base->cpu); -- } else if (new_base->cpu_base == this_cpu_ptr(&hrtimer_bases) && -- hrtimer_reprogram(timer, new_base)) { -+ } else if (new_base->cpu_base == this_cpu_ptr(&hrtimer_bases)) { -+ -+ ret = hrtimer_enqueue_reprogram(timer, new_base, wakeup); -+ if (ret < 0) { -+ /* -+ * In case we failed to reprogram the timer (mostly -+ * because out current timer is already elapsed), -+ * remove it again and report a failure. This avoids -+ * stale base->first entries. -+ */ -+ debug_deactivate(timer); -+ __remove_hrtimer(timer, new_base, -+ timer->state & HRTIMER_STATE_CALLBACK, 0); -+ } else if (ret > 0) { - /* - * Only allow reprogramming if the new base is on this CPU. - * (it might still be on another CPU if the timer was pending) - * - * XXX send_remote_softirq() ? - */ -- if (wakeup) { - /* - * We need to drop cpu_base->lock to avoid a - * lock ordering issue vs. rq->lock. -@@ -979,9 +1094,7 @@ - raw_spin_unlock(&new_base->cpu_base->lock); - raise_softirq_irqoff(HRTIMER_SOFTIRQ); - local_irq_restore(flags); -- return ret; -- } else { -- __raise_softirq_irqoff(HRTIMER_SOFTIRQ); -+ return 0; - } - } - -@@ -1072,7 +1185,7 @@ - - if (ret >= 0) - return ret; -- cpu_relax(); -+ hrtimer_wait_for_timer(timer); - } - } - EXPORT_SYMBOL_GPL(hrtimer_cancel); -@@ -1151,6 +1264,7 @@ - - base = hrtimer_clockid_to_base(clock_id); - timer->base = &cpu_base->clock_base[base]; -+ INIT_LIST_HEAD(&timer->cb_entry); - timerqueue_init(&timer->node); - - #ifdef CONFIG_TIMER_STATS -@@ -1234,6 +1348,126 @@ - timer->state &= ~HRTIMER_STATE_CALLBACK; - } - -+static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer); -+ -+#ifdef CONFIG_PREEMPT_RT_BASE -+static void hrtimer_rt_reprogram(int restart, struct hrtimer *timer, -+ struct hrtimer_clock_base *base) -+{ -+ /* -+ * Note, we clear the callback flag before we requeue the -+ * timer otherwise we trigger the callback_running() check -+ * in hrtimer_reprogram(). -+ */ -+ timer->state &= ~HRTIMER_STATE_CALLBACK; -+ -+ if (restart != HRTIMER_NORESTART) { -+ BUG_ON(hrtimer_active(timer)); -+ /* -+ * Enqueue the timer, if it's the leftmost timer then -+ * we need to reprogram it. -+ */ -+ if (!enqueue_hrtimer(timer, base)) -+ return; -+ -+#ifndef CONFIG_HIGH_RES_TIMERS -+ } -+#else -+ if (base->cpu_base->hres_active && -+ hrtimer_reprogram(timer, base)) -+ goto requeue; -+ -+ } else if (hrtimer_active(timer)) { -+ /* -+ * If the timer was rearmed on another CPU, reprogram -+ * the event device. -+ */ -+ if (&timer->node == base->active.next && -+ base->cpu_base->hres_active && -+ hrtimer_reprogram(timer, base)) -+ goto requeue; -+ } -+ return; -+ -+requeue: -+ /* -+ * Timer is expired. Thus move it from tree to pending list -+ * again. -+ */ -+ __remove_hrtimer(timer, base, timer->state, 0); -+ list_add_tail(&timer->cb_entry, &base->expired); -+#endif -+} -+ -+/* -+ * The changes in mainline which removed the callback modes from -+ * hrtimer are not yet working with -rt. The non wakeup_process() -+ * based callbacks which involve sleeping locks need to be treated -+ * seperately. -+ */ -+static void hrtimer_rt_run_pending(void) -+{ -+ enum hrtimer_restart (*fn)(struct hrtimer *); -+ struct hrtimer_cpu_base *cpu_base; -+ struct hrtimer_clock_base *base; -+ struct hrtimer *timer; -+ int index, restart; -+ -+ local_irq_disable(); -+ cpu_base = &per_cpu(hrtimer_bases, smp_processor_id()); -+ -+ raw_spin_lock(&cpu_base->lock); -+ -+ for (index = 0; index < HRTIMER_MAX_CLOCK_BASES; index++) { -+ base = &cpu_base->clock_base[index]; -+ -+ while (!list_empty(&base->expired)) { -+ timer = list_first_entry(&base->expired, -+ struct hrtimer, cb_entry); -+ -+ /* -+ * Same as the above __run_hrtimer function -+ * just we run with interrupts enabled. -+ */ -+ debug_hrtimer_deactivate(timer); -+ __remove_hrtimer(timer, base, HRTIMER_STATE_CALLBACK, 0); -+ timer_stats_account_hrtimer(timer); -+ fn = timer->function; -+ -+ raw_spin_unlock_irq(&cpu_base->lock); -+ restart = fn(timer); -+ raw_spin_lock_irq(&cpu_base->lock); -+ -+ hrtimer_rt_reprogram(restart, timer, base); -+ } -+ } -+ -+ raw_spin_unlock_irq(&cpu_base->lock); -+ -+ wake_up_timer_waiters(cpu_base); -+} -+ -+static int hrtimer_rt_defer(struct hrtimer *timer) -+{ -+ if (timer->irqsafe) -+ return 0; -+ -+ __remove_hrtimer(timer, timer->base, timer->state, 0); -+ list_add_tail(&timer->cb_entry, &timer->base->expired); -+ return 1; -+} -+ -+#else -+ -+static inline void hrtimer_rt_run_pending(void) -+{ -+ hrtimer_peek_ahead_timers(); -+} -+ -+static inline int hrtimer_rt_defer(struct hrtimer *timer) { return 0; } -+ -+#endif -+ - #ifdef CONFIG_HIGH_RES_TIMERS - - /* -@@ -1244,7 +1478,7 @@ - { - struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases); - ktime_t expires_next, now, entry_time, delta; -- int i, retries = 0; -+ int i, retries = 0, raise = 0; - - BUG_ON(!cpu_base->hres_active); - cpu_base->nr_events++; -@@ -1279,6 +1513,15 @@ - - timer = container_of(node, struct hrtimer, node); - -+ trace_hrtimer_interrupt(raw_smp_processor_id(), -+ ktime_to_ns(ktime_sub(ktime_to_ns(timer->praecox) ? -+ timer->praecox : hrtimer_get_expires(timer), -+ basenow)), -+ current, -+ timer->function == hrtimer_wakeup ? -+ container_of(timer, struct hrtimer_sleeper, -+ timer)->task : NULL); -+ - /* - * The immediate goal for using the softexpires is - * minimizing wakeups, not running timers at the -@@ -1304,7 +1547,10 @@ - break; - } - -- __run_hrtimer(timer, &basenow); -+ if (!hrtimer_rt_defer(timer)) -+ __run_hrtimer(timer, &basenow); -+ else -+ raise = 1; - } - } - -@@ -1319,7 +1565,7 @@ - if (expires_next.tv64 == KTIME_MAX || - !tick_program_event(expires_next, 0)) { - cpu_base->hang_detected = 0; -- return; -+ goto out; - } - - /* -@@ -1363,6 +1609,9 @@ - tick_program_event(expires_next, 1); - printk_once(KERN_WARNING "hrtimer: interrupt took %llu ns\n", - ktime_to_ns(delta)); -+out: -+ if (raise) -+ raise_softirq_irqoff(HRTIMER_SOFTIRQ); - } - - /* -@@ -1398,18 +1647,18 @@ - __hrtimer_peek_ahead_timers(); - local_irq_restore(flags); - } -- --static void run_hrtimer_softirq(struct softirq_action *h) --{ -- hrtimer_peek_ahead_timers(); --} -- - #else /* CONFIG_HIGH_RES_TIMERS */ - - static inline void __hrtimer_peek_ahead_timers(void) { } - - #endif /* !CONFIG_HIGH_RES_TIMERS */ - -+ -+static void run_hrtimer_softirq(struct softirq_action *h) -+{ -+ hrtimer_rt_run_pending(); -+} -+ - /* - * Called from timer softirq every jiffy, expire hrtimers: - * -@@ -1442,7 +1691,7 @@ - struct timerqueue_node *node; - struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases); - struct hrtimer_clock_base *base; -- int index, gettime = 1; -+ int index, gettime = 1, raise = 0; - - if (hrtimer_hres_active()) - return; -@@ -1467,10 +1716,16 @@ - hrtimer_get_expires_tv64(timer)) - break; - -- __run_hrtimer(timer, &base->softirq_time); -+ if (!hrtimer_rt_defer(timer)) -+ __run_hrtimer(timer, &base->softirq_time); -+ else -+ raise = 1; - } - raw_spin_unlock(&cpu_base->lock); - } -+ -+ if (raise) -+ raise_softirq_irqoff(HRTIMER_SOFTIRQ); - } - - /* -@@ -1492,16 +1747,18 @@ - void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task) - { - sl->timer.function = hrtimer_wakeup; -+ sl->timer.irqsafe = 1; - sl->task = task; - } - EXPORT_SYMBOL_GPL(hrtimer_init_sleeper); - --static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode) -+static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode, -+ unsigned long state) - { - hrtimer_init_sleeper(t, current); - - do { -- set_current_state(TASK_INTERRUPTIBLE); -+ set_current_state(state); - hrtimer_start_expires(&t->timer, mode); - if (!hrtimer_active(&t->timer)) - t->task = NULL; -@@ -1545,7 +1802,8 @@ - HRTIMER_MODE_ABS); - hrtimer_set_expires_tv64(&t.timer, restart->nanosleep.expires); - -- if (do_nanosleep(&t, HRTIMER_MODE_ABS)) -+ /* cpu_chill() does not care about restart state. */ -+ if (do_nanosleep(&t, HRTIMER_MODE_ABS, TASK_INTERRUPTIBLE)) - goto out; - - rmtp = restart->nanosleep.rmtp; -@@ -1562,8 +1820,10 @@ - return ret; - } - --long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, -- const enum hrtimer_mode mode, const clockid_t clockid) -+static long -+__hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, -+ const enum hrtimer_mode mode, const clockid_t clockid, -+ unsigned long state) - { - struct restart_block *restart; - struct hrtimer_sleeper t; -@@ -1576,7 +1836,7 @@ - - hrtimer_init_on_stack(&t.timer, clockid, mode); - hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack); -- if (do_nanosleep(&t, mode)) -+ if (do_nanosleep(&t, mode, state)) - goto out; - - /* Absolute timers do not update the rmtp value and restart: */ -@@ -1603,6 +1863,12 @@ - return ret; - } - -+long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, -+ const enum hrtimer_mode mode, const clockid_t clockid) -+{ -+ return __hrtimer_nanosleep(rqtp, rmtp, mode, clockid, TASK_INTERRUPTIBLE); -+} -+ - SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, - struct timespec __user *, rmtp) - { -@@ -1617,6 +1883,26 @@ - return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC); - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+/* -+ * Sleep for 1 ms in hope whoever holds what we want will let it go. -+ */ -+void cpu_chill(void) -+{ -+ struct timespec tu = { -+ .tv_nsec = NSEC_PER_MSEC, -+ }; -+ unsigned int freeze_flag = current->flags & PF_NOFREEZE; -+ -+ current->flags |= PF_NOFREEZE; -+ __hrtimer_nanosleep(&tu, NULL, HRTIMER_MODE_REL, CLOCK_MONOTONIC, -+ TASK_UNINTERRUPTIBLE); -+ if (!freeze_flag) -+ current->flags &= ~PF_NOFREEZE; -+} -+EXPORT_SYMBOL(cpu_chill); -+#endif -+ - /* - * Functions related to boot-time initialization: - */ -@@ -1628,10 +1914,14 @@ - for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { - cpu_base->clock_base[i].cpu_base = cpu_base; - timerqueue_init_head(&cpu_base->clock_base[i].active); -+ INIT_LIST_HEAD(&cpu_base->clock_base[i].expired); - } - - cpu_base->cpu = cpu; - hrtimer_init_hres(cpu_base); -+#ifdef CONFIG_PREEMPT_RT_BASE -+ init_waitqueue_head(&cpu_base->wait); -+#endif - } - - #ifdef CONFIG_HOTPLUG_CPU -@@ -1744,9 +2034,7 @@ - hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE, - (void *)(long)smp_processor_id()); - register_cpu_notifier(&hrtimers_nb); --#ifdef CONFIG_HIGH_RES_TIMERS - open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq); --#endif - } - - /** -diff -Nur linux-3.18.14.orig/kernel/time/itimer.c linux-3.18.14-rt/kernel/time/itimer.c ---- linux-3.18.14.orig/kernel/time/itimer.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/itimer.c 2015-05-31 15:32:48.957635362 -0500 -@@ -213,6 +213,7 @@ - /* We are sharing ->siglock with it_real_fn() */ - if (hrtimer_try_to_cancel(timer) < 0) { - spin_unlock_irq(&tsk->sighand->siglock); -+ hrtimer_wait_for_timer(&tsk->signal->real_timer); - goto again; - } - expires = timeval_to_ktime(value->it_value); -diff -Nur linux-3.18.14.orig/kernel/time/jiffies.c linux-3.18.14-rt/kernel/time/jiffies.c ---- linux-3.18.14.orig/kernel/time/jiffies.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/jiffies.c 2015-05-31 15:32:48.957635362 -0500 -@@ -73,7 +73,8 @@ - .shift = JIFFIES_SHIFT, - }; - --__cacheline_aligned_in_smp DEFINE_SEQLOCK(jiffies_lock); -+__cacheline_aligned_in_smp DEFINE_RAW_SPINLOCK(jiffies_lock); -+__cacheline_aligned_in_smp seqcount_t jiffies_seq; - - #if (BITS_PER_LONG < 64) - u64 get_jiffies_64(void) -@@ -82,9 +83,9 @@ - u64 ret; - - do { -- seq = read_seqbegin(&jiffies_lock); -+ seq = read_seqcount_begin(&jiffies_seq); - ret = jiffies_64; -- } while (read_seqretry(&jiffies_lock, seq)); -+ } while (read_seqcount_retry(&jiffies_seq, seq)); - return ret; - } - EXPORT_SYMBOL(get_jiffies_64); -diff -Nur linux-3.18.14.orig/kernel/time/ntp.c linux-3.18.14-rt/kernel/time/ntp.c ---- linux-3.18.14.orig/kernel/time/ntp.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/ntp.c 2015-05-31 15:32:48.957635362 -0500 -@@ -10,6 +10,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -519,10 +520,52 @@ - &sync_cmos_work, timespec_to_jiffies(&next)); - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+/* -+ * RT can not call schedule_delayed_work from real interrupt context. -+ * Need to make a thread to do the real work. -+ */ -+static struct task_struct *cmos_delay_thread; -+static bool do_cmos_delay; -+ -+static int run_cmos_delay(void *ignore) -+{ -+ while (!kthread_should_stop()) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ if (do_cmos_delay) { -+ do_cmos_delay = false; -+ queue_delayed_work(system_power_efficient_wq, -+ &sync_cmos_work, 0); -+ } -+ schedule(); -+ } -+ __set_current_state(TASK_RUNNING); -+ return 0; -+} -+ -+void ntp_notify_cmos_timer(void) -+{ -+ do_cmos_delay = true; -+ /* Make visible before waking up process */ -+ smp_wmb(); -+ wake_up_process(cmos_delay_thread); -+} -+ -+static __init int create_cmos_delay_thread(void) -+{ -+ cmos_delay_thread = kthread_run(run_cmos_delay, NULL, "kcmosdelayd"); -+ BUG_ON(!cmos_delay_thread); -+ return 0; -+} -+early_initcall(create_cmos_delay_thread); -+ -+#else -+ - void ntp_notify_cmos_timer(void) - { - queue_delayed_work(system_power_efficient_wq, &sync_cmos_work, 0); - } -+#endif /* CONFIG_PREEMPT_RT_FULL */ - - #else - void ntp_notify_cmos_timer(void) { } -diff -Nur linux-3.18.14.orig/kernel/time/posix-cpu-timers.c linux-3.18.14-rt/kernel/time/posix-cpu-timers.c ---- linux-3.18.14.orig/kernel/time/posix-cpu-timers.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/posix-cpu-timers.c 2015-05-31 15:32:48.961635362 -0500 -@@ -3,6 +3,7 @@ - */ - - #include -+#include - #include - #include - #include -@@ -626,7 +627,7 @@ - /* - * Disarm any old timer after extracting its expiry time. - */ -- WARN_ON_ONCE(!irqs_disabled()); -+ WARN_ON_ONCE_NONRT(!irqs_disabled()); - - ret = 0; - old_incr = timer->it.cpu.incr; -@@ -1047,7 +1048,7 @@ - /* - * Now re-arm for the new expiry time. - */ -- WARN_ON_ONCE(!irqs_disabled()); -+ WARN_ON_ONCE_NONRT(!irqs_disabled()); - arm_timer(timer); - unlock_task_sighand(p, &flags); - -@@ -1113,10 +1114,11 @@ - sig = tsk->signal; - if (sig->cputimer.running) { - struct task_cputime group_sample; -+ unsigned long flags; - -- raw_spin_lock(&sig->cputimer.lock); -+ raw_spin_lock_irqsave(&sig->cputimer.lock, flags); - group_sample = sig->cputimer.cputime; -- raw_spin_unlock(&sig->cputimer.lock); -+ raw_spin_unlock_irqrestore(&sig->cputimer.lock, flags); - - if (task_cputime_expired(&group_sample, &sig->cputime_expires)) - return 1; -@@ -1130,13 +1132,13 @@ - * already updated our counts. We need to check if any timers fire now. - * Interrupts are disabled. - */ --void run_posix_cpu_timers(struct task_struct *tsk) -+static void __run_posix_cpu_timers(struct task_struct *tsk) - { - LIST_HEAD(firing); - struct k_itimer *timer, *next; - unsigned long flags; - -- WARN_ON_ONCE(!irqs_disabled()); -+ WARN_ON_ONCE_NONRT(!irqs_disabled()); - - /* - * The fast path checks that there are no expired thread or thread -@@ -1194,6 +1196,190 @@ - } - } - -+#ifdef CONFIG_PREEMPT_RT_BASE -+#include -+#include -+DEFINE_PER_CPU(struct task_struct *, posix_timer_task); -+DEFINE_PER_CPU(struct task_struct *, posix_timer_tasklist); -+ -+static int posix_cpu_timers_thread(void *data) -+{ -+ int cpu = (long)data; -+ -+ BUG_ON(per_cpu(posix_timer_task,cpu) != current); -+ -+ while (!kthread_should_stop()) { -+ struct task_struct *tsk = NULL; -+ struct task_struct *next = NULL; -+ -+ if (cpu_is_offline(cpu)) -+ goto wait_to_die; -+ -+ /* grab task list */ -+ raw_local_irq_disable(); -+ tsk = per_cpu(posix_timer_tasklist, cpu); -+ per_cpu(posix_timer_tasklist, cpu) = NULL; -+ raw_local_irq_enable(); -+ -+ /* its possible the list is empty, just return */ -+ if (!tsk) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule(); -+ __set_current_state(TASK_RUNNING); -+ continue; -+ } -+ -+ /* Process task list */ -+ while (1) { -+ /* save next */ -+ next = tsk->posix_timer_list; -+ -+ /* run the task timers, clear its ptr and -+ * unreference it -+ */ -+ __run_posix_cpu_timers(tsk); -+ tsk->posix_timer_list = NULL; -+ put_task_struct(tsk); -+ -+ /* check if this is the last on the list */ -+ if (next == tsk) -+ break; -+ tsk = next; -+ } -+ } -+ return 0; -+ -+wait_to_die: -+ /* Wait for kthread_stop */ -+ set_current_state(TASK_INTERRUPTIBLE); -+ while (!kthread_should_stop()) { -+ schedule(); -+ set_current_state(TASK_INTERRUPTIBLE); -+ } -+ __set_current_state(TASK_RUNNING); -+ return 0; -+} -+ -+static inline int __fastpath_timer_check(struct task_struct *tsk) -+{ -+ /* tsk == current, ensure it is safe to use ->signal/sighand */ -+ if (unlikely(tsk->exit_state)) -+ return 0; -+ -+ if (!task_cputime_zero(&tsk->cputime_expires)) -+ return 1; -+ -+ if (!task_cputime_zero(&tsk->signal->cputime_expires)) -+ return 1; -+ -+ return 0; -+} -+ -+void run_posix_cpu_timers(struct task_struct *tsk) -+{ -+ unsigned long cpu = smp_processor_id(); -+ struct task_struct *tasklist; -+ -+ BUG_ON(!irqs_disabled()); -+ if(!per_cpu(posix_timer_task, cpu)) -+ return; -+ /* get per-cpu references */ -+ tasklist = per_cpu(posix_timer_tasklist, cpu); -+ -+ /* check to see if we're already queued */ -+ if (!tsk->posix_timer_list && __fastpath_timer_check(tsk)) { -+ get_task_struct(tsk); -+ if (tasklist) { -+ tsk->posix_timer_list = tasklist; -+ } else { -+ /* -+ * The list is terminated by a self-pointing -+ * task_struct -+ */ -+ tsk->posix_timer_list = tsk; -+ } -+ per_cpu(posix_timer_tasklist, cpu) = tsk; -+ -+ wake_up_process(per_cpu(posix_timer_task, cpu)); -+ } -+} -+ -+/* -+ * posix_cpu_thread_call - callback that gets triggered when a CPU is added. -+ * Here we can start up the necessary migration thread for the new CPU. -+ */ -+static int posix_cpu_thread_call(struct notifier_block *nfb, -+ unsigned long action, void *hcpu) -+{ -+ int cpu = (long)hcpu; -+ struct task_struct *p; -+ struct sched_param param; -+ -+ switch (action) { -+ case CPU_UP_PREPARE: -+ p = kthread_create(posix_cpu_timers_thread, hcpu, -+ "posixcputmr/%d",cpu); -+ if (IS_ERR(p)) -+ return NOTIFY_BAD; -+ p->flags |= PF_NOFREEZE; -+ kthread_bind(p, cpu); -+ /* Must be high prio to avoid getting starved */ -+ param.sched_priority = MAX_RT_PRIO-1; -+ sched_setscheduler(p, SCHED_FIFO, ¶m); -+ per_cpu(posix_timer_task,cpu) = p; -+ break; -+ case CPU_ONLINE: -+ /* Strictly unneccessary, as first user will wake it. */ -+ wake_up_process(per_cpu(posix_timer_task,cpu)); -+ break; -+#ifdef CONFIG_HOTPLUG_CPU -+ case CPU_UP_CANCELED: -+ /* Unbind it from offline cpu so it can run. Fall thru. */ -+ kthread_bind(per_cpu(posix_timer_task, cpu), -+ cpumask_any(cpu_online_mask)); -+ kthread_stop(per_cpu(posix_timer_task,cpu)); -+ per_cpu(posix_timer_task,cpu) = NULL; -+ break; -+ case CPU_DEAD: -+ kthread_stop(per_cpu(posix_timer_task,cpu)); -+ per_cpu(posix_timer_task,cpu) = NULL; -+ break; -+#endif -+ } -+ return NOTIFY_OK; -+} -+ -+/* Register at highest priority so that task migration (migrate_all_tasks) -+ * happens before everything else. -+ */ -+static struct notifier_block posix_cpu_thread_notifier = { -+ .notifier_call = posix_cpu_thread_call, -+ .priority = 10 -+}; -+ -+static int __init posix_cpu_thread_init(void) -+{ -+ void *hcpu = (void *)(long)smp_processor_id(); -+ /* Start one for boot CPU. */ -+ unsigned long cpu; -+ -+ /* init the per-cpu posix_timer_tasklets */ -+ for_each_possible_cpu(cpu) -+ per_cpu(posix_timer_tasklist, cpu) = NULL; -+ -+ posix_cpu_thread_call(&posix_cpu_thread_notifier, CPU_UP_PREPARE, hcpu); -+ posix_cpu_thread_call(&posix_cpu_thread_notifier, CPU_ONLINE, hcpu); -+ register_cpu_notifier(&posix_cpu_thread_notifier); -+ return 0; -+} -+early_initcall(posix_cpu_thread_init); -+#else /* CONFIG_PREEMPT_RT_BASE */ -+void run_posix_cpu_timers(struct task_struct *tsk) -+{ -+ __run_posix_cpu_timers(tsk); -+} -+#endif /* CONFIG_PREEMPT_RT_BASE */ -+ - /* - * Set one of the process-wide special case CPU timers or RLIMIT_CPU. - * The tsk->sighand->siglock must be held by the caller. -diff -Nur linux-3.18.14.orig/kernel/time/posix-timers.c linux-3.18.14-rt/kernel/time/posix-timers.c ---- linux-3.18.14.orig/kernel/time/posix-timers.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/posix-timers.c 2015-05-31 15:32:48.961635362 -0500 -@@ -499,6 +499,7 @@ - static struct pid *good_sigevent(sigevent_t * event) - { - struct task_struct *rtn = current->group_leader; -+ int sig = event->sigev_signo; - - if ((event->sigev_notify & SIGEV_THREAD_ID ) && - (!(rtn = find_task_by_vpid(event->sigev_notify_thread_id)) || -@@ -507,7 +508,8 @@ - return NULL; - - if (((event->sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE) && -- ((event->sigev_signo <= 0) || (event->sigev_signo > SIGRTMAX))) -+ (sig <= 0 || sig > SIGRTMAX || sig_kernel_only(sig) || -+ sig_kernel_coredump(sig))) - return NULL; - - return task_pid(rtn); -@@ -819,6 +821,20 @@ - return overrun; - } - -+/* -+ * Protected by RCU! -+ */ -+static void timer_wait_for_callback(struct k_clock *kc, struct k_itimer *timr) -+{ -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (kc->timer_set == common_timer_set) -+ hrtimer_wait_for_timer(&timr->it.real.timer); -+ else -+ /* FIXME: Whacky hack for posix-cpu-timers */ -+ schedule_timeout(1); -+#endif -+} -+ - /* Set a POSIX.1b interval timer. */ - /* timr->it_lock is taken. */ - static int -@@ -896,6 +912,7 @@ - if (!timr) - return -EINVAL; - -+ rcu_read_lock(); - kc = clockid_to_kclock(timr->it_clock); - if (WARN_ON_ONCE(!kc || !kc->timer_set)) - error = -EINVAL; -@@ -904,9 +921,12 @@ - - unlock_timer(timr, flag); - if (error == TIMER_RETRY) { -+ timer_wait_for_callback(kc, timr); - rtn = NULL; // We already got the old time... -+ rcu_read_unlock(); - goto retry; - } -+ rcu_read_unlock(); - - if (old_setting && !error && - copy_to_user(old_setting, &old_spec, sizeof (old_spec))) -@@ -944,10 +964,15 @@ - if (!timer) - return -EINVAL; - -+ rcu_read_lock(); - if (timer_delete_hook(timer) == TIMER_RETRY) { - unlock_timer(timer, flags); -+ timer_wait_for_callback(clockid_to_kclock(timer->it_clock), -+ timer); -+ rcu_read_unlock(); - goto retry_delete; - } -+ rcu_read_unlock(); - - spin_lock(¤t->sighand->siglock); - list_del(&timer->list); -@@ -973,8 +998,18 @@ - retry_delete: - spin_lock_irqsave(&timer->it_lock, flags); - -+ /* On RT we can race with a deletion */ -+ if (!timer->it_signal) { -+ unlock_timer(timer, flags); -+ return; -+ } -+ - if (timer_delete_hook(timer) == TIMER_RETRY) { -+ rcu_read_lock(); - unlock_timer(timer, flags); -+ timer_wait_for_callback(clockid_to_kclock(timer->it_clock), -+ timer); -+ rcu_read_unlock(); - goto retry_delete; - } - list_del(&timer->list); -diff -Nur linux-3.18.14.orig/kernel/time/tick-common.c linux-3.18.14-rt/kernel/time/tick-common.c ---- linux-3.18.14.orig/kernel/time/tick-common.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/tick-common.c 2015-05-31 15:32:48.961635362 -0500 -@@ -78,13 +78,15 @@ - static void tick_periodic(int cpu) - { - if (tick_do_timer_cpu == cpu) { -- write_seqlock(&jiffies_lock); -+ raw_spin_lock(&jiffies_lock); -+ write_seqcount_begin(&jiffies_seq); - - /* Keep track of the next tick event */ - tick_next_period = ktime_add(tick_next_period, tick_period); - - do_timer(1); -- write_sequnlock(&jiffies_lock); -+ write_seqcount_end(&jiffies_seq); -+ raw_spin_unlock(&jiffies_lock); - update_wall_time(); - } - -@@ -146,9 +148,9 @@ - ktime_t next; - - do { -- seq = read_seqbegin(&jiffies_lock); -+ seq = read_seqcount_begin(&jiffies_seq); - next = tick_next_period; -- } while (read_seqretry(&jiffies_lock, seq)); -+ } while (read_seqcount_retry(&jiffies_seq, seq)); - - clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); - -diff -Nur linux-3.18.14.orig/kernel/time/tick-internal.h linux-3.18.14-rt/kernel/time/tick-internal.h ---- linux-3.18.14.orig/kernel/time/tick-internal.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/tick-internal.h 2015-05-31 15:32:48.961635362 -0500 -@@ -6,7 +6,8 @@ - - #include "timekeeping.h" - --extern seqlock_t jiffies_lock; -+extern raw_spinlock_t jiffies_lock; -+extern seqcount_t jiffies_seq; - - #define CS_NAME_LEN 32 - -diff -Nur linux-3.18.14.orig/kernel/time/tick-sched.c linux-3.18.14-rt/kernel/time/tick-sched.c ---- linux-3.18.14.orig/kernel/time/tick-sched.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/tick-sched.c 2015-05-31 15:32:48.961635362 -0500 -@@ -62,7 +62,8 @@ - return; - - /* Reevalute with jiffies_lock held */ -- write_seqlock(&jiffies_lock); -+ raw_spin_lock(&jiffies_lock); -+ write_seqcount_begin(&jiffies_seq); - - delta = ktime_sub(now, last_jiffies_update); - if (delta.tv64 >= tick_period.tv64) { -@@ -85,10 +86,12 @@ - /* Keep the tick_next_period variable up to date */ - tick_next_period = ktime_add(last_jiffies_update, tick_period); - } else { -- write_sequnlock(&jiffies_lock); -+ write_seqcount_end(&jiffies_seq); -+ raw_spin_unlock(&jiffies_lock); - return; - } -- write_sequnlock(&jiffies_lock); -+ write_seqcount_end(&jiffies_seq); -+ raw_spin_unlock(&jiffies_lock); - update_wall_time(); - } - -@@ -99,12 +102,14 @@ - { - ktime_t period; - -- write_seqlock(&jiffies_lock); -+ raw_spin_lock(&jiffies_lock); -+ write_seqcount_begin(&jiffies_seq); - /* Did we start the jiffies update yet ? */ - if (last_jiffies_update.tv64 == 0) - last_jiffies_update = tick_next_period; - period = last_jiffies_update; -- write_sequnlock(&jiffies_lock); -+ write_seqcount_end(&jiffies_seq); -+ raw_spin_unlock(&jiffies_lock); - return period; - } - -@@ -176,6 +181,11 @@ - return false; - } - -+ if (!arch_irq_work_has_interrupt()) { -+ trace_tick_stop(0, "missing irq work interrupt\n"); -+ return false; -+ } -+ - /* sched_clock_tick() needs us? */ - #ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK - /* -@@ -217,11 +227,17 @@ - - static void nohz_full_kick_work_func(struct irq_work *work) - { -+ unsigned long flags; -+ -+ /* ksoftirqd processes sirqs with interrupts enabled */ -+ local_irq_save(flags); - __tick_nohz_full_check(); -+ local_irq_restore(flags); - } - - static DEFINE_PER_CPU(struct irq_work, nohz_full_kick_work) = { - .func = nohz_full_kick_work_func, -+ .flags = IRQ_WORK_HARD_IRQ, - }; - - /* -@@ -580,10 +596,10 @@ - - /* Read jiffies and the time when jiffies were updated last */ - do { -- seq = read_seqbegin(&jiffies_lock); -+ seq = read_seqcount_begin(&jiffies_seq); - last_update = last_jiffies_update; - last_jiffies = jiffies; -- } while (read_seqretry(&jiffies_lock, seq)); -+ } while (read_seqcount_retry(&jiffies_seq, seq)); - - if (rcu_needs_cpu(cpu, &rcu_delta_jiffies) || - arch_needs_cpu() || irq_work_needs_cpu()) { -@@ -761,14 +777,7 @@ - return false; - - if (unlikely(local_softirq_pending() && cpu_online(cpu))) { -- static int ratelimit; -- -- if (ratelimit < 10 && -- (local_softirq_pending() & SOFTIRQ_STOP_IDLE_MASK)) { -- pr_warn("NOHZ: local_softirq_pending %02x\n", -- (unsigned int) local_softirq_pending()); -- ratelimit++; -- } -+ softirq_check_pending_idle(); - return false; - } - -@@ -1156,6 +1165,7 @@ - * Emulate tick processing via per-CPU hrtimers: - */ - hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); -+ ts->sched_timer.irqsafe = 1; - ts->sched_timer.function = tick_sched_timer; - - /* Get the next period (per cpu) */ -diff -Nur linux-3.18.14.orig/kernel/time/timekeeping.c linux-3.18.14-rt/kernel/time/timekeeping.c ---- linux-3.18.14.orig/kernel/time/timekeeping.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/timekeeping.c 2015-05-31 15:32:48.969635362 -0500 -@@ -1814,8 +1814,10 @@ - */ - void xtime_update(unsigned long ticks) - { -- write_seqlock(&jiffies_lock); -+ raw_spin_lock(&jiffies_lock); -+ write_seqcount_begin(&jiffies_seq); - do_timer(ticks); -- write_sequnlock(&jiffies_lock); -+ write_seqcount_end(&jiffies_seq); -+ raw_spin_unlock(&jiffies_lock); - update_wall_time(); - } -diff -Nur linux-3.18.14.orig/kernel/time/timer.c linux-3.18.14-rt/kernel/time/timer.c ---- linux-3.18.14.orig/kernel/time/timer.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/time/timer.c 2015-05-31 15:32:48.973635362 -0500 -@@ -78,6 +78,9 @@ - struct tvec_base { - spinlock_t lock; - struct timer_list *running_timer; -+#ifdef CONFIG_PREEMPT_RT_FULL -+ wait_queue_head_t wait_for_running_timer; -+#endif - unsigned long timer_jiffies; - unsigned long next_timer; - unsigned long active_timers; -@@ -758,6 +761,36 @@ - } - } - -+#ifndef CONFIG_PREEMPT_RT_FULL -+static inline struct tvec_base *switch_timer_base(struct timer_list *timer, -+ struct tvec_base *old, -+ struct tvec_base *new) -+{ -+ /* See the comment in lock_timer_base() */ -+ timer_set_base(timer, NULL); -+ spin_unlock(&old->lock); -+ spin_lock(&new->lock); -+ timer_set_base(timer, new); -+ return new; -+} -+#else -+static inline struct tvec_base *switch_timer_base(struct timer_list *timer, -+ struct tvec_base *old, -+ struct tvec_base *new) -+{ -+ /* -+ * We cannot do the above because we might be preempted and -+ * then the preempter would see NULL and loop forever. -+ */ -+ if (spin_trylock(&new->lock)) { -+ timer_set_base(timer, new); -+ spin_unlock(&old->lock); -+ return new; -+ } -+ return old; -+} -+#endif -+ - static inline int - __mod_timer(struct timer_list *timer, unsigned long expires, - bool pending_only, int pinned) -@@ -788,14 +821,8 @@ - * handler yet has not finished. This also guarantees that - * the timer is serialized wrt itself. - */ -- if (likely(base->running_timer != timer)) { -- /* See the comment in lock_timer_base() */ -- timer_set_base(timer, NULL); -- spin_unlock(&base->lock); -- base = new_base; -- spin_lock(&base->lock); -- timer_set_base(timer, base); -- } -+ if (likely(base->running_timer != timer)) -+ base = switch_timer_base(timer, base, new_base); - } - - timer->expires = expires; -@@ -969,6 +996,29 @@ - } - EXPORT_SYMBOL_GPL(add_timer_on); - -+#ifdef CONFIG_PREEMPT_RT_FULL -+/* -+ * Wait for a running timer -+ */ -+static void wait_for_running_timer(struct timer_list *timer) -+{ -+ struct tvec_base *base = timer->base; -+ -+ if (base->running_timer == timer) -+ wait_event(base->wait_for_running_timer, -+ base->running_timer != timer); -+} -+ -+# define wakeup_timer_waiters(b) wake_up(&(b)->wait_for_running_timer) -+#else -+static inline void wait_for_running_timer(struct timer_list *timer) -+{ -+ cpu_relax(); -+} -+ -+# define wakeup_timer_waiters(b) do { } while (0) -+#endif -+ - /** - * del_timer - deactive a timer. - * @timer: the timer to be deactivated -@@ -1026,7 +1076,7 @@ - } - EXPORT_SYMBOL(try_to_del_timer_sync); - --#ifdef CONFIG_SMP -+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) - /** - * del_timer_sync - deactivate a timer and wait for the handler to finish. - * @timer: the timer to be deactivated -@@ -1086,7 +1136,7 @@ - int ret = try_to_del_timer_sync(timer); - if (ret >= 0) - return ret; -- cpu_relax(); -+ wait_for_running_timer(timer); - } - } - EXPORT_SYMBOL(del_timer_sync); -@@ -1207,15 +1257,17 @@ - if (irqsafe) { - spin_unlock(&base->lock); - call_timer_fn(timer, fn, data); -+ base->running_timer = NULL; - spin_lock(&base->lock); - } else { - spin_unlock_irq(&base->lock); - call_timer_fn(timer, fn, data); -+ base->running_timer = NULL; - spin_lock_irq(&base->lock); - } - } - } -- base->running_timer = NULL; -+ wakeup_timer_waiters(base); - spin_unlock_irq(&base->lock); - } - -@@ -1355,17 +1407,31 @@ - if (cpu_is_offline(smp_processor_id())) - return expires; - -+#ifdef CONFIG_PREEMPT_RT_FULL -+ /* -+ * On PREEMPT_RT we cannot sleep here. If the trylock does not -+ * succeed then we return the worst-case 'expires in 1 tick' -+ * value. We use the rt functions here directly to avoid a -+ * migrate_disable() call. -+ */ -+ if (!spin_do_trylock(&base->lock)) -+ return now + 1; -+#else - spin_lock(&base->lock); -+#endif - if (base->active_timers) { - if (time_before_eq(base->next_timer, base->timer_jiffies)) - base->next_timer = __next_timer_interrupt(base); - expires = base->next_timer; - } -+#ifdef CONFIG_PREEMPT_RT_FULL -+ rt_spin_unlock_after_trylock_in_irq(&base->lock); -+#else - spin_unlock(&base->lock); -+#endif - - if (time_before_eq(expires, now)) - return now; -- - return cmp_next_hrtimer_event(now, expires); - } - #endif -@@ -1381,13 +1447,13 @@ - - /* Note: this timer irq context must be accounted for as well. */ - account_process_tick(p, user_tick); -+ scheduler_tick(); - run_local_timers(); - rcu_check_callbacks(cpu, user_tick); --#ifdef CONFIG_IRQ_WORK -- if (in_irq()) -- irq_work_tick(); -+ -+#if defined(CONFIG_IRQ_WORK) && !defined(CONFIG_PREEMPT_RT_FULL) -+ irq_work_tick(); - #endif -- scheduler_tick(); - run_posix_cpu_timers(p); - } - -@@ -1400,6 +1466,10 @@ - - hrtimer_run_pending(); - -+#if defined(CONFIG_IRQ_WORK) && defined(CONFIG_PREEMPT_RT_FULL) -+ irq_work_tick(); -+#endif -+ - if (time_after_eq(jiffies, base->timer_jiffies)) - __run_timers(base); - } -@@ -1574,6 +1644,9 @@ - base = per_cpu(tvec_bases, cpu); - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+ init_waitqueue_head(&base->wait_for_running_timer); -+#endif - - for (j = 0; j < TVN_SIZE; j++) { - INIT_LIST_HEAD(base->tv5.vec + j); -@@ -1613,7 +1686,7 @@ - - BUG_ON(cpu_online(cpu)); - old_base = per_cpu(tvec_bases, cpu); -- new_base = get_cpu_var(tvec_bases); -+ new_base = get_local_var(tvec_bases); - /* - * The caller is globally serialized and nobody else - * takes two locks at once, deadlock is not possible. -@@ -1634,7 +1707,7 @@ - - spin_unlock(&old_base->lock); - spin_unlock_irq(&new_base->lock); -- put_cpu_var(tvec_bases); -+ put_local_var(tvec_bases); - } - #endif /* CONFIG_HOTPLUG_CPU */ - -diff -Nur linux-3.18.14.orig/kernel/trace/Kconfig linux-3.18.14-rt/kernel/trace/Kconfig ---- linux-3.18.14.orig/kernel/trace/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/trace/Kconfig 2015-05-31 15:32:48.973635362 -0500 -@@ -187,6 +187,24 @@ - enabled. This option and the preempt-off timing option can be - used together or separately.) - -+config INTERRUPT_OFF_HIST -+ bool "Interrupts-off Latency Histogram" -+ depends on IRQSOFF_TRACER -+ help -+ This option generates continuously updated histograms (one per cpu) -+ of the duration of time periods with interrupts disabled. The -+ histograms are disabled by default. To enable them, write a non-zero -+ number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/preemptirqsoff -+ -+ If PREEMPT_OFF_HIST is also selected, additional histograms (one -+ per cpu) are generated that accumulate the duration of time periods -+ when both interrupts and preemption are disabled. The histogram data -+ will be located in the debug file system at -+ -+ /sys/kernel/debug/tracing/latency_hist/irqsoff -+ - config PREEMPT_TRACER - bool "Preemption-off Latency Tracer" - default n -@@ -211,6 +229,24 @@ - enabled. This option and the irqs-off timing option can be - used together or separately.) - -+config PREEMPT_OFF_HIST -+ bool "Preemption-off Latency Histogram" -+ depends on PREEMPT_TRACER -+ help -+ This option generates continuously updated histograms (one per cpu) -+ of the duration of time periods with preemption disabled. The -+ histograms are disabled by default. To enable them, write a non-zero -+ number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/preemptirqsoff -+ -+ If INTERRUPT_OFF_HIST is also selected, additional histograms (one -+ per cpu) are generated that accumulate the duration of time periods -+ when both interrupts and preemption are disabled. The histogram data -+ will be located in the debug file system at -+ -+ /sys/kernel/debug/tracing/latency_hist/preemptoff -+ - config SCHED_TRACER - bool "Scheduling Latency Tracer" - select GENERIC_TRACER -@@ -221,6 +257,74 @@ - This tracer tracks the latency of the highest priority task - to be scheduled in, starting from the point it has woken up. - -+config WAKEUP_LATENCY_HIST -+ bool "Scheduling Latency Histogram" -+ depends on SCHED_TRACER -+ help -+ This option generates continuously updated histograms (one per cpu) -+ of the scheduling latency of the highest priority task. -+ The histograms are disabled by default. To enable them, write a -+ non-zero number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/wakeup -+ -+ Two different algorithms are used, one to determine the latency of -+ processes that exclusively use the highest priority of the system and -+ another one to determine the latency of processes that share the -+ highest system priority with other processes. The former is used to -+ improve hardware and system software, the latter to optimize the -+ priority design of a given system. The histogram data will be -+ located in the debug file system at -+ -+ /sys/kernel/debug/tracing/latency_hist/wakeup -+ -+ and -+ -+ /sys/kernel/debug/tracing/latency_hist/wakeup/sharedprio -+ -+ If both Scheduling Latency Histogram and Missed Timer Offsets -+ Histogram are selected, additional histogram data will be collected -+ that contain, in addition to the wakeup latency, the timer latency, in -+ case the wakeup was triggered by an expired timer. These histograms -+ are available in the -+ -+ /sys/kernel/debug/tracing/latency_hist/timerandwakeup -+ -+ directory. They reflect the apparent interrupt and scheduling latency -+ and are best suitable to determine the worst-case latency of a given -+ system. To enable these histograms, write a non-zero number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/timerandwakeup -+ -+config MISSED_TIMER_OFFSETS_HIST -+ depends on HIGH_RES_TIMERS -+ select GENERIC_TRACER -+ bool "Missed Timer Offsets Histogram" -+ help -+ Generate a histogram of missed timer offsets in microseconds. The -+ histograms are disabled by default. To enable them, write a non-zero -+ number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/missed_timer_offsets -+ -+ The histogram data will be located in the debug file system at -+ -+ /sys/kernel/debug/tracing/latency_hist/missed_timer_offsets -+ -+ If both Scheduling Latency Histogram and Missed Timer Offsets -+ Histogram are selected, additional histogram data will be collected -+ that contain, in addition to the wakeup latency, the timer latency, in -+ case the wakeup was triggered by an expired timer. These histograms -+ are available in the -+ -+ /sys/kernel/debug/tracing/latency_hist/timerandwakeup -+ -+ directory. They reflect the apparent interrupt and scheduling latency -+ and are best suitable to determine the worst-case latency of a given -+ system. To enable these histograms, write a non-zero number to -+ -+ /sys/kernel/debug/tracing/latency_hist/enable/timerandwakeup -+ - config ENABLE_DEFAULT_TRACERS - bool "Trace process context switches and events" - depends on !GENERIC_TRACER -diff -Nur linux-3.18.14.orig/kernel/trace/latency_hist.c linux-3.18.14-rt/kernel/trace/latency_hist.c ---- linux-3.18.14.orig/kernel/trace/latency_hist.c 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/kernel/trace/latency_hist.c 2015-05-31 15:32:48.989635362 -0500 -@@ -0,0 +1,1178 @@ -+/* -+ * kernel/trace/latency_hist.c -+ * -+ * Add support for histograms of preemption-off latency and -+ * interrupt-off latency and wakeup latency, it depends on -+ * Real-Time Preemption Support. -+ * -+ * Copyright (C) 2005 MontaVista Software, Inc. -+ * Yi Yang -+ * -+ * Converted to work with the new latency tracer. -+ * Copyright (C) 2008 Red Hat, Inc. -+ * Steven Rostedt -+ * -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "trace.h" -+#include -+ -+#define NSECS_PER_USECS 1000L -+ -+#define CREATE_TRACE_POINTS -+#include -+ -+enum { -+ IRQSOFF_LATENCY = 0, -+ PREEMPTOFF_LATENCY, -+ PREEMPTIRQSOFF_LATENCY, -+ WAKEUP_LATENCY, -+ WAKEUP_LATENCY_SHAREDPRIO, -+ MISSED_TIMER_OFFSETS, -+ TIMERANDWAKEUP_LATENCY, -+ MAX_LATENCY_TYPE, -+}; -+ -+#define MAX_ENTRY_NUM 10240 -+ -+struct hist_data { -+ atomic_t hist_mode; /* 0 log, 1 don't log */ -+ long offset; /* set it to MAX_ENTRY_NUM/2 for a bipolar scale */ -+ long min_lat; -+ long max_lat; -+ unsigned long long below_hist_bound_samples; -+ unsigned long long above_hist_bound_samples; -+ long long accumulate_lat; -+ unsigned long long total_samples; -+ unsigned long long hist_array[MAX_ENTRY_NUM]; -+}; -+ -+struct enable_data { -+ int latency_type; -+ int enabled; -+}; -+ -+static char *latency_hist_dir_root = "latency_hist"; -+ -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+static DEFINE_PER_CPU(struct hist_data, irqsoff_hist); -+static char *irqsoff_hist_dir = "irqsoff"; -+static DEFINE_PER_CPU(cycles_t, hist_irqsoff_start); -+static DEFINE_PER_CPU(int, hist_irqsoff_counting); -+#endif -+ -+#ifdef CONFIG_PREEMPT_OFF_HIST -+static DEFINE_PER_CPU(struct hist_data, preemptoff_hist); -+static char *preemptoff_hist_dir = "preemptoff"; -+static DEFINE_PER_CPU(cycles_t, hist_preemptoff_start); -+static DEFINE_PER_CPU(int, hist_preemptoff_counting); -+#endif -+ -+#if defined(CONFIG_PREEMPT_OFF_HIST) && defined(CONFIG_INTERRUPT_OFF_HIST) -+static DEFINE_PER_CPU(struct hist_data, preemptirqsoff_hist); -+static char *preemptirqsoff_hist_dir = "preemptirqsoff"; -+static DEFINE_PER_CPU(cycles_t, hist_preemptirqsoff_start); -+static DEFINE_PER_CPU(int, hist_preemptirqsoff_counting); -+#endif -+ -+#if defined(CONFIG_PREEMPT_OFF_HIST) || defined(CONFIG_INTERRUPT_OFF_HIST) -+static notrace void probe_preemptirqsoff_hist(void *v, int reason, int start); -+static struct enable_data preemptirqsoff_enabled_data = { -+ .latency_type = PREEMPTIRQSOFF_LATENCY, -+ .enabled = 0, -+}; -+#endif -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+struct maxlatproc_data { -+ char comm[FIELD_SIZEOF(struct task_struct, comm)]; -+ char current_comm[FIELD_SIZEOF(struct task_struct, comm)]; -+ int pid; -+ int current_pid; -+ int prio; -+ int current_prio; -+ long latency; -+ long timeroffset; -+ cycle_t timestamp; -+}; -+#endif -+ -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+static DEFINE_PER_CPU(struct hist_data, wakeup_latency_hist); -+static DEFINE_PER_CPU(struct hist_data, wakeup_latency_hist_sharedprio); -+static char *wakeup_latency_hist_dir = "wakeup"; -+static char *wakeup_latency_hist_dir_sharedprio = "sharedprio"; -+static notrace void probe_wakeup_latency_hist_start(void *v, -+ struct task_struct *p, int success); -+static notrace void probe_wakeup_latency_hist_stop(void *v, -+ struct task_struct *prev, struct task_struct *next); -+static notrace void probe_sched_migrate_task(void *, -+ struct task_struct *task, int cpu); -+static struct enable_data wakeup_latency_enabled_data = { -+ .latency_type = WAKEUP_LATENCY, -+ .enabled = 0, -+}; -+static DEFINE_PER_CPU(struct maxlatproc_data, wakeup_maxlatproc); -+static DEFINE_PER_CPU(struct maxlatproc_data, wakeup_maxlatproc_sharedprio); -+static DEFINE_PER_CPU(struct task_struct *, wakeup_task); -+static DEFINE_PER_CPU(int, wakeup_sharedprio); -+static unsigned long wakeup_pid; -+#endif -+ -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+static DEFINE_PER_CPU(struct hist_data, missed_timer_offsets); -+static char *missed_timer_offsets_dir = "missed_timer_offsets"; -+static notrace void probe_hrtimer_interrupt(void *v, int cpu, -+ long long offset, struct task_struct *curr, struct task_struct *task); -+static struct enable_data missed_timer_offsets_enabled_data = { -+ .latency_type = MISSED_TIMER_OFFSETS, -+ .enabled = 0, -+}; -+static DEFINE_PER_CPU(struct maxlatproc_data, missed_timer_offsets_maxlatproc); -+static unsigned long missed_timer_offsets_pid; -+#endif -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+static DEFINE_PER_CPU(struct hist_data, timerandwakeup_latency_hist); -+static char *timerandwakeup_latency_hist_dir = "timerandwakeup"; -+static struct enable_data timerandwakeup_enabled_data = { -+ .latency_type = TIMERANDWAKEUP_LATENCY, -+ .enabled = 0, -+}; -+static DEFINE_PER_CPU(struct maxlatproc_data, timerandwakeup_maxlatproc); -+#endif -+ -+void notrace latency_hist(int latency_type, int cpu, long latency, -+ long timeroffset, cycle_t stop, -+ struct task_struct *p) -+{ -+ struct hist_data *my_hist; -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ struct maxlatproc_data *mp = NULL; -+#endif -+ -+ if (!cpu_possible(cpu) || latency_type < 0 || -+ latency_type >= MAX_LATENCY_TYPE) -+ return; -+ -+ switch (latency_type) { -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ case IRQSOFF_LATENCY: -+ my_hist = &per_cpu(irqsoff_hist, cpu); -+ break; -+#endif -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ case PREEMPTOFF_LATENCY: -+ my_hist = &per_cpu(preemptoff_hist, cpu); -+ break; -+#endif -+#if defined(CONFIG_PREEMPT_OFF_HIST) && defined(CONFIG_INTERRUPT_OFF_HIST) -+ case PREEMPTIRQSOFF_LATENCY: -+ my_hist = &per_cpu(preemptirqsoff_hist, cpu); -+ break; -+#endif -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ case WAKEUP_LATENCY: -+ my_hist = &per_cpu(wakeup_latency_hist, cpu); -+ mp = &per_cpu(wakeup_maxlatproc, cpu); -+ break; -+ case WAKEUP_LATENCY_SHAREDPRIO: -+ my_hist = &per_cpu(wakeup_latency_hist_sharedprio, cpu); -+ mp = &per_cpu(wakeup_maxlatproc_sharedprio, cpu); -+ break; -+#endif -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ case MISSED_TIMER_OFFSETS: -+ my_hist = &per_cpu(missed_timer_offsets, cpu); -+ mp = &per_cpu(missed_timer_offsets_maxlatproc, cpu); -+ break; -+#endif -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ case TIMERANDWAKEUP_LATENCY: -+ my_hist = &per_cpu(timerandwakeup_latency_hist, cpu); -+ mp = &per_cpu(timerandwakeup_maxlatproc, cpu); -+ break; -+#endif -+ -+ default: -+ return; -+ } -+ -+ latency += my_hist->offset; -+ -+ if (atomic_read(&my_hist->hist_mode) == 0) -+ return; -+ -+ if (latency < 0 || latency >= MAX_ENTRY_NUM) { -+ if (latency < 0) -+ my_hist->below_hist_bound_samples++; -+ else -+ my_hist->above_hist_bound_samples++; -+ } else -+ my_hist->hist_array[latency]++; -+ -+ if (unlikely(latency > my_hist->max_lat || -+ my_hist->min_lat == LONG_MAX)) { -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ if (latency_type == WAKEUP_LATENCY || -+ latency_type == WAKEUP_LATENCY_SHAREDPRIO || -+ latency_type == MISSED_TIMER_OFFSETS || -+ latency_type == TIMERANDWAKEUP_LATENCY) { -+ strncpy(mp->comm, p->comm, sizeof(mp->comm)); -+ strncpy(mp->current_comm, current->comm, -+ sizeof(mp->current_comm)); -+ mp->pid = task_pid_nr(p); -+ mp->current_pid = task_pid_nr(current); -+ mp->prio = p->prio; -+ mp->current_prio = current->prio; -+ mp->latency = latency; -+ mp->timeroffset = timeroffset; -+ mp->timestamp = stop; -+ } -+#endif -+ my_hist->max_lat = latency; -+ } -+ if (unlikely(latency < my_hist->min_lat)) -+ my_hist->min_lat = latency; -+ my_hist->total_samples++; -+ my_hist->accumulate_lat += latency; -+} -+ -+static void *l_start(struct seq_file *m, loff_t *pos) -+{ -+ loff_t *index_ptr = NULL; -+ loff_t index = *pos; -+ struct hist_data *my_hist = m->private; -+ -+ if (index == 0) { -+ char minstr[32], avgstr[32], maxstr[32]; -+ -+ atomic_dec(&my_hist->hist_mode); -+ -+ if (likely(my_hist->total_samples)) { -+ long avg = (long) div64_s64(my_hist->accumulate_lat, -+ my_hist->total_samples); -+ snprintf(minstr, sizeof(minstr), "%ld", -+ my_hist->min_lat - my_hist->offset); -+ snprintf(avgstr, sizeof(avgstr), "%ld", -+ avg - my_hist->offset); -+ snprintf(maxstr, sizeof(maxstr), "%ld", -+ my_hist->max_lat - my_hist->offset); -+ } else { -+ strcpy(minstr, ""); -+ strcpy(avgstr, minstr); -+ strcpy(maxstr, minstr); -+ } -+ -+ seq_printf(m, "#Minimum latency: %s microseconds\n" -+ "#Average latency: %s microseconds\n" -+ "#Maximum latency: %s microseconds\n" -+ "#Total samples: %llu\n" -+ "#There are %llu samples lower than %ld" -+ " microseconds.\n" -+ "#There are %llu samples greater or equal" -+ " than %ld microseconds.\n" -+ "#usecs\t%16s\n", -+ minstr, avgstr, maxstr, -+ my_hist->total_samples, -+ my_hist->below_hist_bound_samples, -+ -my_hist->offset, -+ my_hist->above_hist_bound_samples, -+ MAX_ENTRY_NUM - my_hist->offset, -+ "samples"); -+ } -+ if (index < MAX_ENTRY_NUM) { -+ index_ptr = kmalloc(sizeof(loff_t), GFP_KERNEL); -+ if (index_ptr) -+ *index_ptr = index; -+ } -+ -+ return index_ptr; -+} -+ -+static void *l_next(struct seq_file *m, void *p, loff_t *pos) -+{ -+ loff_t *index_ptr = p; -+ struct hist_data *my_hist = m->private; -+ -+ if (++*pos >= MAX_ENTRY_NUM) { -+ atomic_inc(&my_hist->hist_mode); -+ return NULL; -+ } -+ *index_ptr = *pos; -+ return index_ptr; -+} -+ -+static void l_stop(struct seq_file *m, void *p) -+{ -+ kfree(p); -+} -+ -+static int l_show(struct seq_file *m, void *p) -+{ -+ int index = *(loff_t *) p; -+ struct hist_data *my_hist = m->private; -+ -+ seq_printf(m, "%6ld\t%16llu\n", index - my_hist->offset, -+ my_hist->hist_array[index]); -+ return 0; -+} -+ -+static const struct seq_operations latency_hist_seq_op = { -+ .start = l_start, -+ .next = l_next, -+ .stop = l_stop, -+ .show = l_show -+}; -+ -+static int latency_hist_open(struct inode *inode, struct file *file) -+{ -+ int ret; -+ -+ ret = seq_open(file, &latency_hist_seq_op); -+ if (!ret) { -+ struct seq_file *seq = file->private_data; -+ seq->private = inode->i_private; -+ } -+ return ret; -+} -+ -+static const struct file_operations latency_hist_fops = { -+ .open = latency_hist_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = seq_release, -+}; -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+static void clear_maxlatprocdata(struct maxlatproc_data *mp) -+{ -+ mp->comm[0] = mp->current_comm[0] = '\0'; -+ mp->prio = mp->current_prio = mp->pid = mp->current_pid = -+ mp->latency = mp->timeroffset = -1; -+ mp->timestamp = 0; -+} -+#endif -+ -+static void hist_reset(struct hist_data *hist) -+{ -+ atomic_dec(&hist->hist_mode); -+ -+ memset(hist->hist_array, 0, sizeof(hist->hist_array)); -+ hist->below_hist_bound_samples = 0ULL; -+ hist->above_hist_bound_samples = 0ULL; -+ hist->min_lat = LONG_MAX; -+ hist->max_lat = LONG_MIN; -+ hist->total_samples = 0ULL; -+ hist->accumulate_lat = 0LL; -+ -+ atomic_inc(&hist->hist_mode); -+} -+ -+static ssize_t -+latency_hist_reset(struct file *file, const char __user *a, -+ size_t size, loff_t *off) -+{ -+ int cpu; -+ struct hist_data *hist = NULL; -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ struct maxlatproc_data *mp = NULL; -+#endif -+ off_t latency_type = (off_t) file->private_data; -+ -+ for_each_online_cpu(cpu) { -+ -+ switch (latency_type) { -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ case PREEMPTOFF_LATENCY: -+ hist = &per_cpu(preemptoff_hist, cpu); -+ break; -+#endif -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ case IRQSOFF_LATENCY: -+ hist = &per_cpu(irqsoff_hist, cpu); -+ break; -+#endif -+#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) -+ case PREEMPTIRQSOFF_LATENCY: -+ hist = &per_cpu(preemptirqsoff_hist, cpu); -+ break; -+#endif -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ case WAKEUP_LATENCY: -+ hist = &per_cpu(wakeup_latency_hist, cpu); -+ mp = &per_cpu(wakeup_maxlatproc, cpu); -+ break; -+ case WAKEUP_LATENCY_SHAREDPRIO: -+ hist = &per_cpu(wakeup_latency_hist_sharedprio, cpu); -+ mp = &per_cpu(wakeup_maxlatproc_sharedprio, cpu); -+ break; -+#endif -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ case MISSED_TIMER_OFFSETS: -+ hist = &per_cpu(missed_timer_offsets, cpu); -+ mp = &per_cpu(missed_timer_offsets_maxlatproc, cpu); -+ break; -+#endif -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ case TIMERANDWAKEUP_LATENCY: -+ hist = &per_cpu(timerandwakeup_latency_hist, cpu); -+ mp = &per_cpu(timerandwakeup_maxlatproc, cpu); -+ break; -+#endif -+ } -+ -+ hist_reset(hist); -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ if (latency_type == WAKEUP_LATENCY || -+ latency_type == WAKEUP_LATENCY_SHAREDPRIO || -+ latency_type == MISSED_TIMER_OFFSETS || -+ latency_type == TIMERANDWAKEUP_LATENCY) -+ clear_maxlatprocdata(mp); -+#endif -+ } -+ -+ return size; -+} -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+static ssize_t -+show_pid(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) -+{ -+ char buf[64]; -+ int r; -+ unsigned long *this_pid = file->private_data; -+ -+ r = snprintf(buf, sizeof(buf), "%lu\n", *this_pid); -+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); -+} -+ -+static ssize_t do_pid(struct file *file, const char __user *ubuf, -+ size_t cnt, loff_t *ppos) -+{ -+ char buf[64]; -+ unsigned long pid; -+ unsigned long *this_pid = file->private_data; -+ -+ if (cnt >= sizeof(buf)) -+ return -EINVAL; -+ -+ if (copy_from_user(&buf, ubuf, cnt)) -+ return -EFAULT; -+ -+ buf[cnt] = '\0'; -+ -+ if (kstrtoul(buf, 10, &pid)) -+ return -EINVAL; -+ -+ *this_pid = pid; -+ -+ return cnt; -+} -+#endif -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+static ssize_t -+show_maxlatproc(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) -+{ -+ int r; -+ struct maxlatproc_data *mp = file->private_data; -+ int strmaxlen = (TASK_COMM_LEN * 2) + (8 * 8); -+ unsigned long long t; -+ unsigned long usecs, secs; -+ char *buf; -+ -+ if (mp->pid == -1 || mp->current_pid == -1) { -+ buf = "(none)\n"; -+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, -+ strlen(buf)); -+ } -+ -+ buf = kmalloc(strmaxlen, GFP_KERNEL); -+ if (buf == NULL) -+ return -ENOMEM; -+ -+ t = ns2usecs(mp->timestamp); -+ usecs = do_div(t, USEC_PER_SEC); -+ secs = (unsigned long) t; -+ r = snprintf(buf, strmaxlen, -+ "%d %d %ld (%ld) %s <- %d %d %s %lu.%06lu\n", mp->pid, -+ MAX_RT_PRIO-1 - mp->prio, mp->latency, mp->timeroffset, mp->comm, -+ mp->current_pid, MAX_RT_PRIO-1 - mp->current_prio, mp->current_comm, -+ secs, usecs); -+ r = simple_read_from_buffer(ubuf, cnt, ppos, buf, r); -+ kfree(buf); -+ return r; -+} -+#endif -+ -+static ssize_t -+show_enable(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) -+{ -+ char buf[64]; -+ struct enable_data *ed = file->private_data; -+ int r; -+ -+ r = snprintf(buf, sizeof(buf), "%d\n", ed->enabled); -+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); -+} -+ -+static ssize_t -+do_enable(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) -+{ -+ char buf[64]; -+ long enable; -+ struct enable_data *ed = file->private_data; -+ -+ if (cnt >= sizeof(buf)) -+ return -EINVAL; -+ -+ if (copy_from_user(&buf, ubuf, cnt)) -+ return -EFAULT; -+ -+ buf[cnt] = 0; -+ -+ if (kstrtoul(buf, 10, &enable)) -+ return -EINVAL; -+ -+ if ((enable && ed->enabled) || (!enable && !ed->enabled)) -+ return cnt; -+ -+ if (enable) { -+ int ret; -+ -+ switch (ed->latency_type) { -+#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) -+ case PREEMPTIRQSOFF_LATENCY: -+ ret = register_trace_preemptirqsoff_hist( -+ probe_preemptirqsoff_hist, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_preemptirqsoff_hist " -+ "to trace_preemptirqsoff_hist\n"); -+ return ret; -+ } -+ break; -+#endif -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ case WAKEUP_LATENCY: -+ ret = register_trace_sched_wakeup( -+ probe_wakeup_latency_hist_start, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_wakeup_latency_hist_start " -+ "to trace_sched_wakeup\n"); -+ return ret; -+ } -+ ret = register_trace_sched_wakeup_new( -+ probe_wakeup_latency_hist_start, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_wakeup_latency_hist_start " -+ "to trace_sched_wakeup_new\n"); -+ unregister_trace_sched_wakeup( -+ probe_wakeup_latency_hist_start, NULL); -+ return ret; -+ } -+ ret = register_trace_sched_switch( -+ probe_wakeup_latency_hist_stop, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_wakeup_latency_hist_stop " -+ "to trace_sched_switch\n"); -+ unregister_trace_sched_wakeup( -+ probe_wakeup_latency_hist_start, NULL); -+ unregister_trace_sched_wakeup_new( -+ probe_wakeup_latency_hist_start, NULL); -+ return ret; -+ } -+ ret = register_trace_sched_migrate_task( -+ probe_sched_migrate_task, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_sched_migrate_task " -+ "to trace_sched_migrate_task\n"); -+ unregister_trace_sched_wakeup( -+ probe_wakeup_latency_hist_start, NULL); -+ unregister_trace_sched_wakeup_new( -+ probe_wakeup_latency_hist_start, NULL); -+ unregister_trace_sched_switch( -+ probe_wakeup_latency_hist_stop, NULL); -+ return ret; -+ } -+ break; -+#endif -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ case MISSED_TIMER_OFFSETS: -+ ret = register_trace_hrtimer_interrupt( -+ probe_hrtimer_interrupt, NULL); -+ if (ret) { -+ pr_info("wakeup trace: Couldn't assign " -+ "probe_hrtimer_interrupt " -+ "to trace_hrtimer_interrupt\n"); -+ return ret; -+ } -+ break; -+#endif -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ case TIMERANDWAKEUP_LATENCY: -+ if (!wakeup_latency_enabled_data.enabled || -+ !missed_timer_offsets_enabled_data.enabled) -+ return -EINVAL; -+ break; -+#endif -+ default: -+ break; -+ } -+ } else { -+ switch (ed->latency_type) { -+#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) -+ case PREEMPTIRQSOFF_LATENCY: -+ { -+ int cpu; -+ -+ unregister_trace_preemptirqsoff_hist( -+ probe_preemptirqsoff_hist, NULL); -+ for_each_online_cpu(cpu) { -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ per_cpu(hist_irqsoff_counting, -+ cpu) = 0; -+#endif -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ per_cpu(hist_preemptoff_counting, -+ cpu) = 0; -+#endif -+#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) -+ per_cpu(hist_preemptirqsoff_counting, -+ cpu) = 0; -+#endif -+ } -+ } -+ break; -+#endif -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ case WAKEUP_LATENCY: -+ { -+ int cpu; -+ -+ unregister_trace_sched_wakeup( -+ probe_wakeup_latency_hist_start, NULL); -+ unregister_trace_sched_wakeup_new( -+ probe_wakeup_latency_hist_start, NULL); -+ unregister_trace_sched_switch( -+ probe_wakeup_latency_hist_stop, NULL); -+ unregister_trace_sched_migrate_task( -+ probe_sched_migrate_task, NULL); -+ -+ for_each_online_cpu(cpu) { -+ per_cpu(wakeup_task, cpu) = NULL; -+ per_cpu(wakeup_sharedprio, cpu) = 0; -+ } -+ } -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ timerandwakeup_enabled_data.enabled = 0; -+#endif -+ break; -+#endif -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ case MISSED_TIMER_OFFSETS: -+ unregister_trace_hrtimer_interrupt( -+ probe_hrtimer_interrupt, NULL); -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ timerandwakeup_enabled_data.enabled = 0; -+#endif -+ break; -+#endif -+ default: -+ break; -+ } -+ } -+ ed->enabled = enable; -+ return cnt; -+} -+ -+static const struct file_operations latency_hist_reset_fops = { -+ .open = tracing_open_generic, -+ .write = latency_hist_reset, -+}; -+ -+static const struct file_operations enable_fops = { -+ .open = tracing_open_generic, -+ .read = show_enable, -+ .write = do_enable, -+}; -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+static const struct file_operations pid_fops = { -+ .open = tracing_open_generic, -+ .read = show_pid, -+ .write = do_pid, -+}; -+ -+static const struct file_operations maxlatproc_fops = { -+ .open = tracing_open_generic, -+ .read = show_maxlatproc, -+}; -+#endif -+ -+#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) -+static notrace void probe_preemptirqsoff_hist(void *v, int reason, -+ int starthist) -+{ -+ int cpu = raw_smp_processor_id(); -+ int time_set = 0; -+ -+ if (starthist) { -+ cycle_t uninitialized_var(start); -+ -+ if (!preempt_count() && !irqs_disabled()) -+ return; -+ -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ if ((reason == IRQS_OFF || reason == TRACE_START) && -+ !per_cpu(hist_irqsoff_counting, cpu)) { -+ per_cpu(hist_irqsoff_counting, cpu) = 1; -+ start = ftrace_now(cpu); -+ time_set++; -+ per_cpu(hist_irqsoff_start, cpu) = start; -+ } -+#endif -+ -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ if ((reason == PREEMPT_OFF || reason == TRACE_START) && -+ !per_cpu(hist_preemptoff_counting, cpu)) { -+ per_cpu(hist_preemptoff_counting, cpu) = 1; -+ if (!(time_set++)) -+ start = ftrace_now(cpu); -+ per_cpu(hist_preemptoff_start, cpu) = start; -+ } -+#endif -+ -+#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) -+ if (per_cpu(hist_irqsoff_counting, cpu) && -+ per_cpu(hist_preemptoff_counting, cpu) && -+ !per_cpu(hist_preemptirqsoff_counting, cpu)) { -+ per_cpu(hist_preemptirqsoff_counting, cpu) = 1; -+ if (!time_set) -+ start = ftrace_now(cpu); -+ per_cpu(hist_preemptirqsoff_start, cpu) = start; -+ } -+#endif -+ } else { -+ cycle_t uninitialized_var(stop); -+ -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ if ((reason == IRQS_ON || reason == TRACE_STOP) && -+ per_cpu(hist_irqsoff_counting, cpu)) { -+ cycle_t start = per_cpu(hist_irqsoff_start, cpu); -+ -+ stop = ftrace_now(cpu); -+ time_set++; -+ if (start) { -+ long latency = ((long) (stop - start)) / -+ NSECS_PER_USECS; -+ -+ latency_hist(IRQSOFF_LATENCY, cpu, latency, 0, -+ stop, NULL); -+ } -+ per_cpu(hist_irqsoff_counting, cpu) = 0; -+ } -+#endif -+ -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ if ((reason == PREEMPT_ON || reason == TRACE_STOP) && -+ per_cpu(hist_preemptoff_counting, cpu)) { -+ cycle_t start = per_cpu(hist_preemptoff_start, cpu); -+ -+ if (!(time_set++)) -+ stop = ftrace_now(cpu); -+ if (start) { -+ long latency = ((long) (stop - start)) / -+ NSECS_PER_USECS; -+ -+ latency_hist(PREEMPTOFF_LATENCY, cpu, latency, -+ 0, stop, NULL); -+ } -+ per_cpu(hist_preemptoff_counting, cpu) = 0; -+ } -+#endif -+ -+#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) -+ if ((!per_cpu(hist_irqsoff_counting, cpu) || -+ !per_cpu(hist_preemptoff_counting, cpu)) && -+ per_cpu(hist_preemptirqsoff_counting, cpu)) { -+ cycle_t start = per_cpu(hist_preemptirqsoff_start, cpu); -+ -+ if (!time_set) -+ stop = ftrace_now(cpu); -+ if (start) { -+ long latency = ((long) (stop - start)) / -+ NSECS_PER_USECS; -+ -+ latency_hist(PREEMPTIRQSOFF_LATENCY, cpu, -+ latency, 0, stop, NULL); -+ } -+ per_cpu(hist_preemptirqsoff_counting, cpu) = 0; -+ } -+#endif -+ } -+} -+#endif -+ -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+static DEFINE_RAW_SPINLOCK(wakeup_lock); -+static notrace void probe_sched_migrate_task(void *v, struct task_struct *task, -+ int cpu) -+{ -+ int old_cpu = task_cpu(task); -+ -+ if (cpu != old_cpu) { -+ unsigned long flags; -+ struct task_struct *cpu_wakeup_task; -+ -+ raw_spin_lock_irqsave(&wakeup_lock, flags); -+ -+ cpu_wakeup_task = per_cpu(wakeup_task, old_cpu); -+ if (task == cpu_wakeup_task) { -+ put_task_struct(cpu_wakeup_task); -+ per_cpu(wakeup_task, old_cpu) = NULL; -+ cpu_wakeup_task = per_cpu(wakeup_task, cpu) = task; -+ get_task_struct(cpu_wakeup_task); -+ } -+ -+ raw_spin_unlock_irqrestore(&wakeup_lock, flags); -+ } -+} -+ -+static notrace void probe_wakeup_latency_hist_start(void *v, -+ struct task_struct *p, int success) -+{ -+ unsigned long flags; -+ struct task_struct *curr = current; -+ int cpu = task_cpu(p); -+ struct task_struct *cpu_wakeup_task; -+ -+ raw_spin_lock_irqsave(&wakeup_lock, flags); -+ -+ cpu_wakeup_task = per_cpu(wakeup_task, cpu); -+ -+ if (wakeup_pid) { -+ if ((cpu_wakeup_task && p->prio == cpu_wakeup_task->prio) || -+ p->prio == curr->prio) -+ per_cpu(wakeup_sharedprio, cpu) = 1; -+ if (likely(wakeup_pid != task_pid_nr(p))) -+ goto out; -+ } else { -+ if (likely(!rt_task(p)) || -+ (cpu_wakeup_task && p->prio > cpu_wakeup_task->prio) || -+ p->prio > curr->prio) -+ goto out; -+ if ((cpu_wakeup_task && p->prio == cpu_wakeup_task->prio) || -+ p->prio == curr->prio) -+ per_cpu(wakeup_sharedprio, cpu) = 1; -+ } -+ -+ if (cpu_wakeup_task) -+ put_task_struct(cpu_wakeup_task); -+ cpu_wakeup_task = per_cpu(wakeup_task, cpu) = p; -+ get_task_struct(cpu_wakeup_task); -+ cpu_wakeup_task->preempt_timestamp_hist = -+ ftrace_now(raw_smp_processor_id()); -+out: -+ raw_spin_unlock_irqrestore(&wakeup_lock, flags); -+} -+ -+static notrace void probe_wakeup_latency_hist_stop(void *v, -+ struct task_struct *prev, struct task_struct *next) -+{ -+ unsigned long flags; -+ int cpu = task_cpu(next); -+ long latency; -+ cycle_t stop; -+ struct task_struct *cpu_wakeup_task; -+ -+ raw_spin_lock_irqsave(&wakeup_lock, flags); -+ -+ cpu_wakeup_task = per_cpu(wakeup_task, cpu); -+ -+ if (cpu_wakeup_task == NULL) -+ goto out; -+ -+ /* Already running? */ -+ if (unlikely(current == cpu_wakeup_task)) -+ goto out_reset; -+ -+ if (next != cpu_wakeup_task) { -+ if (next->prio < cpu_wakeup_task->prio) -+ goto out_reset; -+ -+ if (next->prio == cpu_wakeup_task->prio) -+ per_cpu(wakeup_sharedprio, cpu) = 1; -+ -+ goto out; -+ } -+ -+ if (current->prio == cpu_wakeup_task->prio) -+ per_cpu(wakeup_sharedprio, cpu) = 1; -+ -+ /* -+ * The task we are waiting for is about to be switched to. -+ * Calculate latency and store it in histogram. -+ */ -+ stop = ftrace_now(raw_smp_processor_id()); -+ -+ latency = ((long) (stop - next->preempt_timestamp_hist)) / -+ NSECS_PER_USECS; -+ -+ if (per_cpu(wakeup_sharedprio, cpu)) { -+ latency_hist(WAKEUP_LATENCY_SHAREDPRIO, cpu, latency, 0, stop, -+ next); -+ per_cpu(wakeup_sharedprio, cpu) = 0; -+ } else { -+ latency_hist(WAKEUP_LATENCY, cpu, latency, 0, stop, next); -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ if (timerandwakeup_enabled_data.enabled) { -+ latency_hist(TIMERANDWAKEUP_LATENCY, cpu, -+ next->timer_offset + latency, next->timer_offset, -+ stop, next); -+ } -+#endif -+ } -+ -+out_reset: -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ next->timer_offset = 0; -+#endif -+ put_task_struct(cpu_wakeup_task); -+ per_cpu(wakeup_task, cpu) = NULL; -+out: -+ raw_spin_unlock_irqrestore(&wakeup_lock, flags); -+} -+#endif -+ -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+static notrace void probe_hrtimer_interrupt(void *v, int cpu, -+ long long latency_ns, struct task_struct *curr, -+ struct task_struct *task) -+{ -+ if (latency_ns <= 0 && task != NULL && rt_task(task) && -+ (task->prio < curr->prio || -+ (task->prio == curr->prio && -+ !cpumask_test_cpu(cpu, &task->cpus_allowed)))) { -+ long latency; -+ cycle_t now; -+ -+ if (missed_timer_offsets_pid) { -+ if (likely(missed_timer_offsets_pid != -+ task_pid_nr(task))) -+ return; -+ } -+ -+ now = ftrace_now(cpu); -+ latency = (long) div_s64(-latency_ns, NSECS_PER_USECS); -+ latency_hist(MISSED_TIMER_OFFSETS, cpu, latency, latency, now, -+ task); -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ task->timer_offset = latency; -+#endif -+ } -+} -+#endif -+ -+static __init int latency_hist_init(void) -+{ -+ struct dentry *latency_hist_root = NULL; -+ struct dentry *dentry; -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ struct dentry *dentry_sharedprio; -+#endif -+ struct dentry *entry; -+ struct dentry *enable_root; -+ int i = 0; -+ struct hist_data *my_hist; -+ char name[64]; -+ char *cpufmt = "CPU%d"; -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ char *cpufmt_maxlatproc = "max_latency-CPU%d"; -+ struct maxlatproc_data *mp = NULL; -+#endif -+ -+ dentry = tracing_init_dentry(); -+ latency_hist_root = debugfs_create_dir(latency_hist_dir_root, dentry); -+ enable_root = debugfs_create_dir("enable", latency_hist_root); -+ -+#ifdef CONFIG_INTERRUPT_OFF_HIST -+ dentry = debugfs_create_dir(irqsoff_hist_dir, latency_hist_root); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(irqsoff_hist, i), &latency_hist_fops); -+ my_hist = &per_cpu(irqsoff_hist, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ } -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)IRQSOFF_LATENCY, &latency_hist_reset_fops); -+#endif -+ -+#ifdef CONFIG_PREEMPT_OFF_HIST -+ dentry = debugfs_create_dir(preemptoff_hist_dir, -+ latency_hist_root); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(preemptoff_hist, i), &latency_hist_fops); -+ my_hist = &per_cpu(preemptoff_hist, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ } -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)PREEMPTOFF_LATENCY, &latency_hist_reset_fops); -+#endif -+ -+#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) -+ dentry = debugfs_create_dir(preemptirqsoff_hist_dir, -+ latency_hist_root); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(preemptirqsoff_hist, i), &latency_hist_fops); -+ my_hist = &per_cpu(preemptirqsoff_hist, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ } -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)PREEMPTIRQSOFF_LATENCY, &latency_hist_reset_fops); -+#endif -+ -+#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) -+ entry = debugfs_create_file("preemptirqsoff", 0644, -+ enable_root, (void *)&preemptirqsoff_enabled_data, -+ &enable_fops); -+#endif -+ -+#ifdef CONFIG_WAKEUP_LATENCY_HIST -+ dentry = debugfs_create_dir(wakeup_latency_hist_dir, -+ latency_hist_root); -+ dentry_sharedprio = debugfs_create_dir( -+ wakeup_latency_hist_dir_sharedprio, dentry); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(wakeup_latency_hist, i), -+ &latency_hist_fops); -+ my_hist = &per_cpu(wakeup_latency_hist, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ -+ entry = debugfs_create_file(name, 0444, dentry_sharedprio, -+ &per_cpu(wakeup_latency_hist_sharedprio, i), -+ &latency_hist_fops); -+ my_hist = &per_cpu(wakeup_latency_hist_sharedprio, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ -+ sprintf(name, cpufmt_maxlatproc, i); -+ -+ mp = &per_cpu(wakeup_maxlatproc, i); -+ entry = debugfs_create_file(name, 0444, dentry, mp, -+ &maxlatproc_fops); -+ clear_maxlatprocdata(mp); -+ -+ mp = &per_cpu(wakeup_maxlatproc_sharedprio, i); -+ entry = debugfs_create_file(name, 0444, dentry_sharedprio, mp, -+ &maxlatproc_fops); -+ clear_maxlatprocdata(mp); -+ } -+ entry = debugfs_create_file("pid", 0644, dentry, -+ (void *)&wakeup_pid, &pid_fops); -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)WAKEUP_LATENCY, &latency_hist_reset_fops); -+ entry = debugfs_create_file("reset", 0644, dentry_sharedprio, -+ (void *)WAKEUP_LATENCY_SHAREDPRIO, &latency_hist_reset_fops); -+ entry = debugfs_create_file("wakeup", 0644, -+ enable_root, (void *)&wakeup_latency_enabled_data, -+ &enable_fops); -+#endif -+ -+#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST -+ dentry = debugfs_create_dir(missed_timer_offsets_dir, -+ latency_hist_root); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(missed_timer_offsets, i), &latency_hist_fops); -+ my_hist = &per_cpu(missed_timer_offsets, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ -+ sprintf(name, cpufmt_maxlatproc, i); -+ mp = &per_cpu(missed_timer_offsets_maxlatproc, i); -+ entry = debugfs_create_file(name, 0444, dentry, mp, -+ &maxlatproc_fops); -+ clear_maxlatprocdata(mp); -+ } -+ entry = debugfs_create_file("pid", 0644, dentry, -+ (void *)&missed_timer_offsets_pid, &pid_fops); -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)MISSED_TIMER_OFFSETS, &latency_hist_reset_fops); -+ entry = debugfs_create_file("missed_timer_offsets", 0644, -+ enable_root, (void *)&missed_timer_offsets_enabled_data, -+ &enable_fops); -+#endif -+ -+#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ -+ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) -+ dentry = debugfs_create_dir(timerandwakeup_latency_hist_dir, -+ latency_hist_root); -+ for_each_possible_cpu(i) { -+ sprintf(name, cpufmt, i); -+ entry = debugfs_create_file(name, 0444, dentry, -+ &per_cpu(timerandwakeup_latency_hist, i), -+ &latency_hist_fops); -+ my_hist = &per_cpu(timerandwakeup_latency_hist, i); -+ atomic_set(&my_hist->hist_mode, 1); -+ my_hist->min_lat = LONG_MAX; -+ -+ sprintf(name, cpufmt_maxlatproc, i); -+ mp = &per_cpu(timerandwakeup_maxlatproc, i); -+ entry = debugfs_create_file(name, 0444, dentry, mp, -+ &maxlatproc_fops); -+ clear_maxlatprocdata(mp); -+ } -+ entry = debugfs_create_file("reset", 0644, dentry, -+ (void *)TIMERANDWAKEUP_LATENCY, &latency_hist_reset_fops); -+ entry = debugfs_create_file("timerandwakeup", 0644, -+ enable_root, (void *)&timerandwakeup_enabled_data, -+ &enable_fops); -+#endif -+ return 0; -+} -+ -+device_initcall(latency_hist_init); -diff -Nur linux-3.18.14.orig/kernel/trace/Makefile linux-3.18.14-rt/kernel/trace/Makefile ---- linux-3.18.14.orig/kernel/trace/Makefile 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/trace/Makefile 2015-05-31 15:32:48.989635362 -0500 -@@ -36,6 +36,10 @@ - obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o - obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o - obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o -+obj-$(CONFIG_INTERRUPT_OFF_HIST) += latency_hist.o -+obj-$(CONFIG_PREEMPT_OFF_HIST) += latency_hist.o -+obj-$(CONFIG_WAKEUP_LATENCY_HIST) += latency_hist.o -+obj-$(CONFIG_MISSED_TIMER_OFFSETS_HIST) += latency_hist.o - obj-$(CONFIG_NOP_TRACER) += trace_nop.o - obj-$(CONFIG_STACK_TRACER) += trace_stack.o - obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o -diff -Nur linux-3.18.14.orig/kernel/trace/trace.c linux-3.18.14-rt/kernel/trace/trace.c ---- linux-3.18.14.orig/kernel/trace/trace.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/trace/trace.c 2015-05-31 15:32:49.021635361 -0500 -@@ -1579,6 +1579,7 @@ - struct task_struct *tsk = current; - - entry->preempt_count = pc & 0xff; -+ entry->preempt_lazy_count = preempt_lazy_count(); - entry->pid = (tsk) ? tsk->pid : 0; - entry->flags = - #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT -@@ -1588,8 +1589,11 @@ - #endif - ((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) | - ((pc & SOFTIRQ_MASK) ? TRACE_FLAG_SOFTIRQ : 0) | -- (tif_need_resched() ? TRACE_FLAG_NEED_RESCHED : 0) | -+ (tif_need_resched_now() ? TRACE_FLAG_NEED_RESCHED : 0) | -+ (need_resched_lazy() ? TRACE_FLAG_NEED_RESCHED_LAZY : 0) | - (test_preempt_need_resched() ? TRACE_FLAG_PREEMPT_RESCHED : 0); -+ -+ entry->migrate_disable = (tsk) ? __migrate_disabled(tsk) & 0xFF : 0; - } - EXPORT_SYMBOL_GPL(tracing_generic_entry_update); - -@@ -2509,14 +2513,17 @@ - - static void print_lat_help_header(struct seq_file *m) - { -- seq_puts(m, "# _------=> CPU# \n"); -- seq_puts(m, "# / _-----=> irqs-off \n"); -- seq_puts(m, "# | / _----=> need-resched \n"); -- seq_puts(m, "# || / _---=> hardirq/softirq \n"); -- seq_puts(m, "# ||| / _--=> preempt-depth \n"); -- seq_puts(m, "# |||| / delay \n"); -- seq_puts(m, "# cmd pid ||||| time | caller \n"); -- seq_puts(m, "# \\ / ||||| \\ | / \n"); -+ seq_puts(m, "# _--------=> CPU# \n"); -+ seq_puts(m, "# / _-------=> irqs-off \n"); -+ seq_puts(m, "# | / _------=> need-resched \n"); -+ seq_puts(m, "# || / _-----=> need-resched_lazy \n"); -+ seq_puts(m, "# ||| / _----=> hardirq/softirq \n"); -+ seq_puts(m, "# |||| / _---=> preempt-depth \n"); -+ seq_puts(m, "# ||||| / _--=> preempt-lazy-depth\n"); -+ seq_puts(m, "# |||||| / _-=> migrate-disable \n"); -+ seq_puts(m, "# ||||||| / delay \n"); -+ seq_puts(m, "# cmd pid |||||||| time | caller \n"); -+ seq_puts(m, "# \\ / |||||||| \\ | / \n"); - } - - static void print_event_info(struct trace_buffer *buf, struct seq_file *m) -@@ -2540,13 +2547,16 @@ - static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file *m) - { - print_event_info(buf, m); -- seq_puts(m, "# _-----=> irqs-off\n"); -- seq_puts(m, "# / _----=> need-resched\n"); -- seq_puts(m, "# | / _---=> hardirq/softirq\n"); -- seq_puts(m, "# || / _--=> preempt-depth\n"); -- seq_puts(m, "# ||| / delay\n"); -- seq_puts(m, "# TASK-PID CPU# |||| TIMESTAMP FUNCTION\n"); -- seq_puts(m, "# | | | |||| | |\n"); -+ seq_puts(m, "# _-------=> irqs-off \n"); -+ seq_puts(m, "# / _------=> need-resched \n"); -+ seq_puts(m, "# |/ _-----=> need-resched_lazy \n"); -+ seq_puts(m, "# ||/ _----=> hardirq/softirq \n"); -+ seq_puts(m, "# |||/ _---=> preempt-depth \n"); -+ seq_puts(m, "# ||||/ _--=> preempt-lazy-depth\n"); -+ seq_puts(m, "# ||||| / _-=> migrate-disable \n"); -+ seq_puts(m, "# |||||| / delay\n"); -+ seq_puts(m, "# TASK-PID CPU# |||||| TIMESTAMP FUNCTION\n"); -+ seq_puts(m, "# | | | |||||| | |\n"); - } - - void -diff -Nur linux-3.18.14.orig/kernel/trace/trace_events.c linux-3.18.14-rt/kernel/trace/trace_events.c ---- linux-3.18.14.orig/kernel/trace/trace_events.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/trace/trace_events.c 2015-05-31 15:32:49.025635362 -0500 -@@ -162,6 +162,8 @@ - __common_field(unsigned char, flags); - __common_field(unsigned char, preempt_count); - __common_field(int, pid); -+ __common_field(unsigned short, migrate_disable); -+ __common_field(unsigned short, padding); - - return ret; - } -diff -Nur linux-3.18.14.orig/kernel/trace/trace.h linux-3.18.14-rt/kernel/trace/trace.h ---- linux-3.18.14.orig/kernel/trace/trace.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/trace/trace.h 2015-05-31 15:32:49.021635361 -0500 -@@ -119,6 +119,7 @@ - * NEED_RESCHED - reschedule is requested - * HARDIRQ - inside an interrupt handler - * SOFTIRQ - inside a softirq handler -+ * NEED_RESCHED_LAZY - lazy reschedule is requested - */ - enum trace_flag_type { - TRACE_FLAG_IRQS_OFF = 0x01, -@@ -127,6 +128,7 @@ - TRACE_FLAG_HARDIRQ = 0x08, - TRACE_FLAG_SOFTIRQ = 0x10, - TRACE_FLAG_PREEMPT_RESCHED = 0x20, -+ TRACE_FLAG_NEED_RESCHED_LAZY = 0x40, - }; - - #define TRACE_BUF_SIZE 1024 -diff -Nur linux-3.18.14.orig/kernel/trace/trace_irqsoff.c linux-3.18.14-rt/kernel/trace/trace_irqsoff.c ---- linux-3.18.14.orig/kernel/trace/trace_irqsoff.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/trace/trace_irqsoff.c 2015-05-31 15:32:49.025635362 -0500 -@@ -17,6 +17,7 @@ - #include - - #include "trace.h" -+#include - - static struct trace_array *irqsoff_trace __read_mostly; - static int tracer_enabled __read_mostly; -@@ -435,11 +436,13 @@ - { - if (preempt_trace() || irq_trace()) - start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); -+ trace_preemptirqsoff_hist(TRACE_START, 1); - } - EXPORT_SYMBOL_GPL(start_critical_timings); - - void stop_critical_timings(void) - { -+ trace_preemptirqsoff_hist(TRACE_STOP, 0); - if (preempt_trace() || irq_trace()) - stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); - } -@@ -449,6 +452,7 @@ - #ifdef CONFIG_PROVE_LOCKING - void time_hardirqs_on(unsigned long a0, unsigned long a1) - { -+ trace_preemptirqsoff_hist(IRQS_ON, 0); - if (!preempt_trace() && irq_trace()) - stop_critical_timing(a0, a1); - } -@@ -457,6 +461,7 @@ - { - if (!preempt_trace() && irq_trace()) - start_critical_timing(a0, a1); -+ trace_preemptirqsoff_hist(IRQS_OFF, 1); - } - - #else /* !CONFIG_PROVE_LOCKING */ -@@ -482,6 +487,7 @@ - */ - void trace_hardirqs_on(void) - { -+ trace_preemptirqsoff_hist(IRQS_ON, 0); - if (!preempt_trace() && irq_trace()) - stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); - } -@@ -491,11 +497,13 @@ - { - if (!preempt_trace() && irq_trace()) - start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); -+ trace_preemptirqsoff_hist(IRQS_OFF, 1); - } - EXPORT_SYMBOL(trace_hardirqs_off); - - __visible void trace_hardirqs_on_caller(unsigned long caller_addr) - { -+ trace_preemptirqsoff_hist(IRQS_ON, 0); - if (!preempt_trace() && irq_trace()) - stop_critical_timing(CALLER_ADDR0, caller_addr); - } -@@ -505,6 +513,7 @@ - { - if (!preempt_trace() && irq_trace()) - start_critical_timing(CALLER_ADDR0, caller_addr); -+ trace_preemptirqsoff_hist(IRQS_OFF, 1); - } - EXPORT_SYMBOL(trace_hardirqs_off_caller); - -@@ -514,12 +523,14 @@ - #ifdef CONFIG_PREEMPT_TRACER - void trace_preempt_on(unsigned long a0, unsigned long a1) - { -+ trace_preemptirqsoff_hist(PREEMPT_ON, 0); - if (preempt_trace() && !irq_trace()) - stop_critical_timing(a0, a1); - } - - void trace_preempt_off(unsigned long a0, unsigned long a1) - { -+ trace_preemptirqsoff_hist(PREEMPT_ON, 1); - if (preempt_trace() && !irq_trace()) - start_critical_timing(a0, a1); - } -diff -Nur linux-3.18.14.orig/kernel/trace/trace_output.c linux-3.18.14-rt/kernel/trace/trace_output.c ---- linux-3.18.14.orig/kernel/trace/trace_output.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/trace/trace_output.c 2015-05-31 15:32:49.025635362 -0500 -@@ -410,6 +410,7 @@ - { - char hardsoft_irq; - char need_resched; -+ char need_resched_lazy; - char irqs_off; - int hardirq; - int softirq; -@@ -438,6 +439,8 @@ - need_resched = '.'; - break; - } -+ need_resched_lazy = -+ (entry->flags & TRACE_FLAG_NEED_RESCHED_LAZY) ? 'L' : '.'; - - hardsoft_irq = - (hardirq && softirq) ? 'H' : -@@ -445,8 +448,9 @@ - softirq ? 's' : - '.'; - -- if (!trace_seq_printf(s, "%c%c%c", -- irqs_off, need_resched, hardsoft_irq)) -+ if (!trace_seq_printf(s, "%c%c%c%c", -+ irqs_off, need_resched, need_resched_lazy, -+ hardsoft_irq)) - return 0; - - if (entry->preempt_count) -@@ -454,6 +458,16 @@ - else - ret = trace_seq_putc(s, '.'); - -+ if (entry->preempt_lazy_count) -+ ret = trace_seq_printf(s, "%x", entry->preempt_lazy_count); -+ else -+ ret = trace_seq_putc(s, '.'); -+ -+ if (entry->migrate_disable) -+ ret = trace_seq_printf(s, "%x", entry->migrate_disable); -+ else -+ ret = trace_seq_putc(s, '.'); -+ - return ret; - } - -diff -Nur linux-3.18.14.orig/kernel/user.c linux-3.18.14-rt/kernel/user.c ---- linux-3.18.14.orig/kernel/user.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/user.c 2015-05-31 15:32:49.045635362 -0500 -@@ -158,11 +158,11 @@ - if (!up) - return; - -- local_irq_save(flags); -+ local_irq_save_nort(flags); - if (atomic_dec_and_lock(&up->__count, &uidhash_lock)) - free_user(up, flags); - else -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - } - - struct user_struct *alloc_uid(kuid_t uid) -diff -Nur linux-3.18.14.orig/kernel/watchdog.c linux-3.18.14-rt/kernel/watchdog.c ---- linux-3.18.14.orig/kernel/watchdog.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/watchdog.c 2015-05-31 15:32:49.065635361 -0500 -@@ -248,6 +248,8 @@ - - #ifdef CONFIG_HARDLOCKUP_DETECTOR - -+static DEFINE_RAW_SPINLOCK(watchdog_output_lock); -+ - static struct perf_event_attr wd_hw_attr = { - .type = PERF_TYPE_HARDWARE, - .config = PERF_COUNT_HW_CPU_CYCLES, -@@ -281,13 +283,21 @@ - /* only print hardlockups once */ - if (__this_cpu_read(hard_watchdog_warn) == true) - return; -+ /* -+ * If early-printk is enabled then make sure we do not -+ * lock up in printk() and kill console logging: -+ */ -+ printk_kill(); - -- if (hardlockup_panic) -+ if (hardlockup_panic) { - panic("Watchdog detected hard LOCKUP on cpu %d", - this_cpu); -- else -+ } else { -+ raw_spin_lock(&watchdog_output_lock); - WARN(1, "Watchdog detected hard LOCKUP on cpu %d", - this_cpu); -+ raw_spin_unlock(&watchdog_output_lock); -+ } - - __this_cpu_write(hard_watchdog_warn, true); - return; -@@ -430,6 +440,7 @@ - /* kick off the timer for the hardlockup detector */ - hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hrtimer->function = watchdog_timer_fn; -+ hrtimer->irqsafe = 1; - - /* Enable the perf event */ - watchdog_nmi_enable(cpu); -diff -Nur linux-3.18.14.orig/kernel/workqueue.c linux-3.18.14-rt/kernel/workqueue.c ---- linux-3.18.14.orig/kernel/workqueue.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/workqueue.c 2015-05-31 15:32:49.069635361 -0500 -@@ -48,6 +48,8 @@ - #include - #include - #include -+#include -+#include - - #include "workqueue_internal.h" - -@@ -121,15 +123,20 @@ - * cpu or grabbing pool->lock is enough for read access. If - * POOL_DISASSOCIATED is set, it's identical to L. - * -+ * On RT we need the extra protection via rt_lock_idle_list() for -+ * the list manipulations against read access from -+ * wq_worker_sleeping(). All other places are nicely serialized via -+ * pool->lock. -+ * - * A: pool->attach_mutex protected. - * - * PL: wq_pool_mutex protected. - * -- * PR: wq_pool_mutex protected for writes. Sched-RCU protected for reads. -+ * PR: wq_pool_mutex protected for writes. RCU protected for reads. - * - * WQ: wq->mutex protected. - * -- * WR: wq->mutex protected for writes. Sched-RCU protected for reads. -+ * WR: wq->mutex protected for writes. RCU protected for reads. - * - * MD: wq_mayday_lock protected. - */ -@@ -177,7 +184,7 @@ - atomic_t nr_running ____cacheline_aligned_in_smp; - - /* -- * Destruction of pool is sched-RCU protected to allow dereferences -+ * Destruction of pool is RCU protected to allow dereferences - * from get_work_pool(). - */ - struct rcu_head rcu; -@@ -206,7 +213,7 @@ - /* - * Release of unbound pwq is punted to system_wq. See put_pwq() - * and pwq_unbound_release_workfn() for details. pool_workqueue -- * itself is also sched-RCU protected so that the first pwq can be -+ * itself is also RCU protected so that the first pwq can be - * determined without grabbing wq->mutex. - */ - struct work_struct unbound_release_work; -@@ -321,6 +328,8 @@ - struct workqueue_struct *system_freezable_power_efficient_wq __read_mostly; - EXPORT_SYMBOL_GPL(system_freezable_power_efficient_wq); - -+static DEFINE_LOCAL_IRQ_LOCK(pendingb_lock); -+ - static int worker_thread(void *__worker); - static void copy_workqueue_attrs(struct workqueue_attrs *to, - const struct workqueue_attrs *from); -@@ -329,14 +338,14 @@ - #include - - #define assert_rcu_or_pool_mutex() \ -- rcu_lockdep_assert(rcu_read_lock_sched_held() || \ -+ rcu_lockdep_assert(rcu_read_lock_held() || \ - lockdep_is_held(&wq_pool_mutex), \ -- "sched RCU or wq_pool_mutex should be held") -+ "RCU or wq_pool_mutex should be held") - - #define assert_rcu_or_wq_mutex(wq) \ -- rcu_lockdep_assert(rcu_read_lock_sched_held() || \ -+ rcu_lockdep_assert(rcu_read_lock_held() || \ - lockdep_is_held(&wq->mutex), \ -- "sched RCU or wq->mutex should be held") -+ "RCU or wq->mutex should be held") - - #define for_each_cpu_worker_pool(pool, cpu) \ - for ((pool) = &per_cpu(cpu_worker_pools, cpu)[0]; \ -@@ -348,7 +357,7 @@ - * @pool: iteration cursor - * @pi: integer used for iteration - * -- * This must be called either with wq_pool_mutex held or sched RCU read -+ * This must be called either with wq_pool_mutex held or RCU read - * locked. If the pool needs to be used beyond the locking in effect, the - * caller is responsible for guaranteeing that the pool stays online. - * -@@ -380,7 +389,7 @@ - * @pwq: iteration cursor - * @wq: the target workqueue - * -- * This must be called either with wq->mutex held or sched RCU read locked. -+ * This must be called either with wq->mutex held or RCU read locked. - * If the pwq needs to be used beyond the locking in effect, the caller is - * responsible for guaranteeing that the pwq stays online. - * -@@ -392,6 +401,31 @@ - if (({ assert_rcu_or_wq_mutex(wq); false; })) { } \ - else - -+#ifdef CONFIG_PREEMPT_RT_BASE -+static inline void rt_lock_idle_list(struct worker_pool *pool) -+{ -+ preempt_disable(); -+} -+static inline void rt_unlock_idle_list(struct worker_pool *pool) -+{ -+ preempt_enable(); -+} -+static inline void sched_lock_idle_list(struct worker_pool *pool) { } -+static inline void sched_unlock_idle_list(struct worker_pool *pool) { } -+#else -+static inline void rt_lock_idle_list(struct worker_pool *pool) { } -+static inline void rt_unlock_idle_list(struct worker_pool *pool) { } -+static inline void sched_lock_idle_list(struct worker_pool *pool) -+{ -+ spin_lock_irq(&pool->lock); -+} -+static inline void sched_unlock_idle_list(struct worker_pool *pool) -+{ -+ spin_unlock_irq(&pool->lock); -+} -+#endif -+ -+ - #ifdef CONFIG_DEBUG_OBJECTS_WORK - - static struct debug_obj_descr work_debug_descr; -@@ -542,7 +576,7 @@ - * @wq: the target workqueue - * @node: the node ID - * -- * This must be called either with pwq_lock held or sched RCU read locked. -+ * This must be called either with pwq_lock held or RCU read locked. - * If the pwq needs to be used beyond the locking in effect, the caller is - * responsible for guaranteeing that the pwq stays online. - * -@@ -646,8 +680,8 @@ - * @work: the work item of interest - * - * Pools are created and destroyed under wq_pool_mutex, and allows read -- * access under sched-RCU read lock. As such, this function should be -- * called under wq_pool_mutex or with preemption disabled. -+ * access under RCU read lock. As such, this function should be -+ * called under wq_pool_mutex or inside of a rcu_read_lock() region. - * - * All fields of the returned pool are accessible as long as the above - * mentioned locking is in effect. If the returned pool needs to be used -@@ -784,51 +818,44 @@ - */ - static void wake_up_worker(struct worker_pool *pool) - { -- struct worker *worker = first_idle_worker(pool); -+ struct worker *worker; -+ -+ rt_lock_idle_list(pool); -+ -+ worker = first_idle_worker(pool); - - if (likely(worker)) - wake_up_process(worker->task); -+ -+ rt_unlock_idle_list(pool); - } - - /** -- * wq_worker_waking_up - a worker is waking up -- * @task: task waking up -- * @cpu: CPU @task is waking up to -- * -- * This function is called during try_to_wake_up() when a worker is -- * being awoken. -+ * wq_worker_running - a worker is running again -+ * @task: task returning from sleep - * -- * CONTEXT: -- * spin_lock_irq(rq->lock) -+ * This function is called when a worker returns from schedule() - */ --void wq_worker_waking_up(struct task_struct *task, int cpu) -+void wq_worker_running(struct task_struct *task) - { - struct worker *worker = kthread_data(task); - -- if (!(worker->flags & WORKER_NOT_RUNNING)) { -- WARN_ON_ONCE(worker->pool->cpu != cpu); -+ if (!worker->sleeping) -+ return; -+ if (!(worker->flags & WORKER_NOT_RUNNING)) - atomic_inc(&worker->pool->nr_running); -- } -+ worker->sleeping = 0; - } - - /** - * wq_worker_sleeping - a worker is going to sleep - * @task: task going to sleep -- * @cpu: CPU in question, must be the current CPU number -- * -- * This function is called during schedule() when a busy worker is -- * going to sleep. Worker on the same cpu can be woken up by -- * returning pointer to its task. -- * -- * CONTEXT: -- * spin_lock_irq(rq->lock) -- * -- * Return: -- * Worker task on @cpu to wake up, %NULL if none. -+ * This function is called from schedule() when a busy worker is -+ * going to sleep. - */ --struct task_struct *wq_worker_sleeping(struct task_struct *task, int cpu) -+void wq_worker_sleeping(struct task_struct *task) - { -- struct worker *worker = kthread_data(task), *to_wakeup = NULL; -+ struct worker *worker = kthread_data(task); - struct worker_pool *pool; - - /* -@@ -837,29 +864,26 @@ - * checking NOT_RUNNING. - */ - if (worker->flags & WORKER_NOT_RUNNING) -- return NULL; -+ return; - - pool = worker->pool; - -- /* this can only happen on the local cpu */ -- if (WARN_ON_ONCE(cpu != raw_smp_processor_id() || pool->cpu != cpu)) -- return NULL; -+ if (WARN_ON_ONCE(worker->sleeping)) -+ return; -+ -+ worker->sleeping = 1; - - /* - * The counterpart of the following dec_and_test, implied mb, - * worklist not empty test sequence is in insert_work(). - * Please read comment there. -- * -- * NOT_RUNNING is clear. This means that we're bound to and -- * running on the local cpu w/ rq lock held and preemption -- * disabled, which in turn means that none else could be -- * manipulating idle_list, so dereferencing idle_list without pool -- * lock is safe. - */ - if (atomic_dec_and_test(&pool->nr_running) && -- !list_empty(&pool->worklist)) -- to_wakeup = first_idle_worker(pool); -- return to_wakeup ? to_wakeup->task : NULL; -+ !list_empty(&pool->worklist)) { -+ sched_lock_idle_list(pool); -+ wake_up_worker(pool); -+ sched_unlock_idle_list(pool); -+ } - } - - /** -@@ -1053,12 +1077,12 @@ - { - if (pwq) { - /* -- * As both pwqs and pools are sched-RCU protected, the -+ * As both pwqs and pools are RCU protected, the - * following lock operations are safe. - */ -- spin_lock_irq(&pwq->pool->lock); -+ local_spin_lock_irq(pendingb_lock, &pwq->pool->lock); - put_pwq(pwq); -- spin_unlock_irq(&pwq->pool->lock); -+ local_spin_unlock_irq(pendingb_lock, &pwq->pool->lock); - } - } - -@@ -1160,7 +1184,7 @@ - struct worker_pool *pool; - struct pool_workqueue *pwq; - -- local_irq_save(*flags); -+ local_lock_irqsave(pendingb_lock, *flags); - - /* try to steal the timer if it exists */ - if (is_dwork) { -@@ -1179,6 +1203,7 @@ - if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) - return 0; - -+ rcu_read_lock(); - /* - * The queueing is in progress, or it is already queued. Try to - * steal it from ->worklist without clearing WORK_STRUCT_PENDING. -@@ -1217,14 +1242,16 @@ - set_work_pool_and_keep_pending(work, pool->id); - - spin_unlock(&pool->lock); -+ rcu_read_unlock(); - return 1; - } - spin_unlock(&pool->lock); - fail: -- local_irq_restore(*flags); -+ rcu_read_unlock(); -+ local_unlock_irqrestore(pendingb_lock, *flags); - if (work_is_canceling(work)) - return -ENOENT; -- cpu_relax(); -+ cpu_chill(); - return -EAGAIN; - } - -@@ -1293,7 +1320,7 @@ - * queued or lose PENDING. Grabbing PENDING and queueing should - * happen with IRQ disabled. - */ -- WARN_ON_ONCE(!irqs_disabled()); -+ WARN_ON_ONCE_NONRT(!irqs_disabled()); - - debug_work_activate(work); - -@@ -1301,6 +1328,8 @@ - if (unlikely(wq->flags & __WQ_DRAINING) && - WARN_ON_ONCE(!is_chained_work(wq))) - return; -+ -+ rcu_read_lock(); - retry: - if (req_cpu == WORK_CPU_UNBOUND) - cpu = raw_smp_processor_id(); -@@ -1357,10 +1386,8 @@ - /* pwq determined, queue */ - trace_workqueue_queue_work(req_cpu, pwq, work); - -- if (WARN_ON(!list_empty(&work->entry))) { -- spin_unlock(&pwq->pool->lock); -- return; -- } -+ if (WARN_ON(!list_empty(&work->entry))) -+ goto out; - - pwq->nr_in_flight[pwq->work_color]++; - work_flags = work_color_to_flags(pwq->work_color); -@@ -1376,7 +1403,9 @@ - - insert_work(pwq, work, worklist, work_flags); - -+out: - spin_unlock(&pwq->pool->lock); -+ rcu_read_unlock(); - } - - /** -@@ -1396,14 +1425,14 @@ - bool ret = false; - unsigned long flags; - -- local_irq_save(flags); -+ local_lock_irqsave(pendingb_lock,flags); - - if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { - __queue_work(cpu, wq, work); - ret = true; - } - -- local_irq_restore(flags); -+ local_unlock_irqrestore(pendingb_lock, flags); - return ret; - } - EXPORT_SYMBOL(queue_work_on); -@@ -1470,14 +1499,14 @@ - unsigned long flags; - - /* read the comment in __queue_work() */ -- local_irq_save(flags); -+ local_lock_irqsave(pendingb_lock, flags); - - if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { - __queue_delayed_work(cpu, wq, dwork, delay); - ret = true; - } - -- local_irq_restore(flags); -+ local_unlock_irqrestore(pendingb_lock, flags); - return ret; - } - EXPORT_SYMBOL(queue_delayed_work_on); -@@ -1512,7 +1541,7 @@ - - if (likely(ret >= 0)) { - __queue_delayed_work(cpu, wq, dwork, delay); -- local_irq_restore(flags); -+ local_unlock_irqrestore(pendingb_lock, flags); - } - - /* -ENOENT from try_to_grab_pending() becomes %true */ -@@ -1545,7 +1574,9 @@ - worker->last_active = jiffies; - - /* idle_list is LIFO */ -+ rt_lock_idle_list(pool); - list_add(&worker->entry, &pool->idle_list); -+ rt_unlock_idle_list(pool); - - if (too_many_workers(pool) && !timer_pending(&pool->idle_timer)) - mod_timer(&pool->idle_timer, jiffies + IDLE_WORKER_TIMEOUT); -@@ -1578,7 +1609,9 @@ - return; - worker_clr_flags(worker, WORKER_IDLE); - pool->nr_idle--; -+ rt_lock_idle_list(pool); - list_del_init(&worker->entry); -+ rt_unlock_idle_list(pool); - } - - static struct worker *alloc_worker(int node) -@@ -1746,7 +1779,9 @@ - pool->nr_workers--; - pool->nr_idle--; - -+ rt_lock_idle_list(pool); - list_del_init(&worker->entry); -+ rt_unlock_idle_list(pool); - worker->flags |= WORKER_DIE; - wake_up_process(worker->task); - } -@@ -2641,14 +2676,14 @@ - - might_sleep(); - -- local_irq_disable(); -+ rcu_read_lock(); - pool = get_work_pool(work); - if (!pool) { -- local_irq_enable(); -+ rcu_read_unlock(); - return false; - } - -- spin_lock(&pool->lock); -+ spin_lock_irq(&pool->lock); - /* see the comment in try_to_grab_pending() with the same code */ - pwq = get_work_pwq(work); - if (pwq) { -@@ -2675,10 +2710,11 @@ - else - lock_map_acquire_read(&pwq->wq->lockdep_map); - lock_map_release(&pwq->wq->lockdep_map); -- -+ rcu_read_unlock(); - return true; - already_gone: - spin_unlock_irq(&pool->lock); -+ rcu_read_unlock(); - return false; - } - -@@ -2765,7 +2801,7 @@ - - /* tell other tasks trying to grab @work to back off */ - mark_work_canceling(work); -- local_irq_restore(flags); -+ local_unlock_irqrestore(pendingb_lock, flags); - - flush_work(work); - clear_work_data(work); -@@ -2820,10 +2856,10 @@ - */ - bool flush_delayed_work(struct delayed_work *dwork) - { -- local_irq_disable(); -+ local_lock_irq(pendingb_lock); - if (del_timer_sync(&dwork->timer)) - __queue_work(dwork->cpu, dwork->wq, &dwork->work); -- local_irq_enable(); -+ local_unlock_irq(pendingb_lock); - return flush_work(&dwork->work); - } - EXPORT_SYMBOL(flush_delayed_work); -@@ -2858,7 +2894,7 @@ - - set_work_pool_and_clear_pending(&dwork->work, - get_work_pool_id(&dwork->work)); -- local_irq_restore(flags); -+ local_unlock_irqrestore(pendingb_lock, flags); - return ret; - } - EXPORT_SYMBOL(cancel_delayed_work); -@@ -3044,7 +3080,8 @@ - const char *delim = ""; - int node, written = 0; - -- rcu_read_lock_sched(); -+ get_online_cpus(); -+ rcu_read_lock(); - for_each_node(node) { - written += scnprintf(buf + written, PAGE_SIZE - written, - "%s%d:%d", delim, node, -@@ -3052,7 +3089,8 @@ - delim = " "; - } - written += scnprintf(buf + written, PAGE_SIZE - written, "\n"); -- rcu_read_unlock_sched(); -+ rcu_read_unlock(); -+ put_online_cpus(); - - return written; - } -@@ -3420,7 +3458,7 @@ - * put_unbound_pool - put a worker_pool - * @pool: worker_pool to put - * -- * Put @pool. If its refcnt reaches zero, it gets destroyed in sched-RCU -+ * Put @pool. If its refcnt reaches zero, it gets destroyed in RCU - * safe manner. get_unbound_pool() calls this function on its failure path - * and this function should be able to release pools which went through, - * successfully or not, init_worker_pool(). -@@ -3474,8 +3512,8 @@ - del_timer_sync(&pool->idle_timer); - del_timer_sync(&pool->mayday_timer); - -- /* sched-RCU protected to allow dereferences from get_work_pool() */ -- call_rcu_sched(&pool->rcu, rcu_free_pool); -+ /* RCU protected to allow dereferences from get_work_pool() */ -+ call_rcu(&pool->rcu, rcu_free_pool); - } - - /** -@@ -3580,7 +3618,7 @@ - put_unbound_pool(pool); - mutex_unlock(&wq_pool_mutex); - -- call_rcu_sched(&pwq->rcu, rcu_free_pwq); -+ call_rcu(&pwq->rcu, rcu_free_pwq); - - /* - * If we're the last pwq going away, @wq is already dead and no one -@@ -4292,7 +4330,8 @@ - struct pool_workqueue *pwq; - bool ret; - -- rcu_read_lock_sched(); -+ rcu_read_lock(); -+ preempt_disable(); - - if (cpu == WORK_CPU_UNBOUND) - cpu = smp_processor_id(); -@@ -4303,7 +4342,8 @@ - pwq = unbound_pwq_by_node(wq, cpu_to_node(cpu)); - - ret = !list_empty(&pwq->delayed_works); -- rcu_read_unlock_sched(); -+ preempt_enable(); -+ rcu_read_unlock(); - - return ret; - } -@@ -4329,16 +4369,15 @@ - if (work_pending(work)) - ret |= WORK_BUSY_PENDING; - -- local_irq_save(flags); -+ rcu_read_lock(); - pool = get_work_pool(work); - if (pool) { -- spin_lock(&pool->lock); -+ spin_lock_irqsave(&pool->lock, flags); - if (find_worker_executing_work(pool, work)) - ret |= WORK_BUSY_RUNNING; -- spin_unlock(&pool->lock); -+ spin_unlock_irqrestore(&pool->lock, flags); - } -- local_irq_restore(flags); -- -+ rcu_read_unlock(); - return ret; - } - EXPORT_SYMBOL_GPL(work_busy); -@@ -4767,16 +4806,16 @@ - * nr_active is monotonically decreasing. It's safe - * to peek without lock. - */ -- rcu_read_lock_sched(); -+ rcu_read_lock(); - for_each_pwq(pwq, wq) { - WARN_ON_ONCE(pwq->nr_active < 0); - if (pwq->nr_active) { - busy = true; -- rcu_read_unlock_sched(); -+ rcu_read_unlock(); - goto out_unlock; - } - } -- rcu_read_unlock_sched(); -+ rcu_read_unlock(); - } - out_unlock: - mutex_unlock(&wq_pool_mutex); -diff -Nur linux-3.18.14.orig/kernel/workqueue_internal.h linux-3.18.14-rt/kernel/workqueue_internal.h ---- linux-3.18.14.orig/kernel/workqueue_internal.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/kernel/workqueue_internal.h 2015-05-31 15:32:49.069635361 -0500 -@@ -43,6 +43,7 @@ - unsigned long last_active; /* L: last active timestamp */ - unsigned int flags; /* X: flags */ - int id; /* I: worker id */ -+ int sleeping; /* None */ - - /* - * Opaque string set with work_set_desc(). Printed out with task -@@ -68,7 +69,7 @@ - * Scheduler hooks for concurrency managed workqueue. Only to be used from - * sched/core.c and workqueue.c. - */ --void wq_worker_waking_up(struct task_struct *task, int cpu); --struct task_struct *wq_worker_sleeping(struct task_struct *task, int cpu); -+void wq_worker_running(struct task_struct *task); -+void wq_worker_sleeping(struct task_struct *task); - - #endif /* _KERNEL_WORKQUEUE_INTERNAL_H */ -diff -Nur linux-3.18.14.orig/lib/debugobjects.c linux-3.18.14-rt/lib/debugobjects.c ---- linux-3.18.14.orig/lib/debugobjects.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/lib/debugobjects.c 2015-05-31 15:32:49.113635361 -0500 -@@ -309,7 +309,10 @@ - struct debug_obj *obj; - unsigned long flags; - -- fill_pool(); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (preempt_count() == 0 && !irqs_disabled()) -+#endif -+ fill_pool(); - - db = get_bucket((unsigned long) addr); - -diff -Nur linux-3.18.14.orig/lib/idr.c linux-3.18.14-rt/lib/idr.c ---- linux-3.18.14.orig/lib/idr.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/lib/idr.c 2015-05-31 15:32:49.141635361 -0500 -@@ -31,6 +31,7 @@ - #include - #include - #include -+#include - - #define MAX_IDR_SHIFT (sizeof(int) * 8 - 1) - #define MAX_IDR_BIT (1U << MAX_IDR_SHIFT) -@@ -367,6 +368,35 @@ - idr_mark_full(pa, id); - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+static DEFINE_LOCAL_IRQ_LOCK(idr_lock); -+ -+static inline void idr_preload_lock(void) -+{ -+ local_lock(idr_lock); -+} -+ -+static inline void idr_preload_unlock(void) -+{ -+ local_unlock(idr_lock); -+} -+ -+void idr_preload_end(void) -+{ -+ idr_preload_unlock(); -+} -+EXPORT_SYMBOL(idr_preload_end); -+#else -+static inline void idr_preload_lock(void) -+{ -+ preempt_disable(); -+} -+ -+static inline void idr_preload_unlock(void) -+{ -+ preempt_enable(); -+} -+#endif - - /** - * idr_preload - preload for idr_alloc() -@@ -402,7 +432,7 @@ - WARN_ON_ONCE(in_interrupt()); - might_sleep_if(gfp_mask & __GFP_WAIT); - -- preempt_disable(); -+ idr_preload_lock(); - - /* - * idr_alloc() is likely to succeed w/o full idr_layer buffer and -@@ -414,9 +444,9 @@ - while (__this_cpu_read(idr_preload_cnt) < MAX_IDR_FREE) { - struct idr_layer *new; - -- preempt_enable(); -+ idr_preload_unlock(); - new = kmem_cache_zalloc(idr_layer_cache, gfp_mask); -- preempt_disable(); -+ idr_preload_lock(); - if (!new) - break; - -diff -Nur linux-3.18.14.orig/lib/Kconfig linux-3.18.14-rt/lib/Kconfig ---- linux-3.18.14.orig/lib/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/lib/Kconfig 2015-05-31 15:32:49.085635361 -0500 -@@ -383,6 +383,7 @@ - - config CPUMASK_OFFSTACK - bool "Force CPU masks off stack" if DEBUG_PER_CPU_MAPS -+ depends on !PREEMPT_RT_FULL - help - Use dynamic allocation for cpumask_var_t, instead of putting - them on the stack. This is a bit more expensive, but avoids -diff -Nur linux-3.18.14.orig/lib/Kconfig.debug linux-3.18.14-rt/lib/Kconfig.debug ---- linux-3.18.14.orig/lib/Kconfig.debug 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/lib/Kconfig.debug 2015-05-31 15:32:49.097635361 -0500 -@@ -639,7 +639,7 @@ - - config DEBUG_SHIRQ - bool "Debug shared IRQ handlers" -- depends on DEBUG_KERNEL -+ depends on DEBUG_KERNEL && !PREEMPT_RT_BASE - help - Enable this to generate a spurious interrupt as soon as a shared - interrupt handler is registered, and just before one is deregistered. -diff -Nur linux-3.18.14.orig/lib/locking-selftest.c linux-3.18.14-rt/lib/locking-selftest.c ---- linux-3.18.14.orig/lib/locking-selftest.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/lib/locking-selftest.c 2015-05-31 15:32:49.141635361 -0500 -@@ -590,6 +590,8 @@ - #include "locking-selftest-spin-hardirq.h" - GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_spin) - -+#ifndef CONFIG_PREEMPT_RT_FULL -+ - #include "locking-selftest-rlock-hardirq.h" - GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_rlock) - -@@ -605,9 +607,12 @@ - #include "locking-selftest-wlock-softirq.h" - GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_soft_wlock) - -+#endif -+ - #undef E1 - #undef E2 - -+#ifndef CONFIG_PREEMPT_RT_FULL - /* - * Enabling hardirqs with a softirq-safe lock held: - */ -@@ -640,6 +645,8 @@ - #undef E1 - #undef E2 - -+#endif -+ - /* - * Enabling irqs with an irq-safe lock held: - */ -@@ -663,6 +670,8 @@ - #include "locking-selftest-spin-hardirq.h" - GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_spin) - -+#ifndef CONFIG_PREEMPT_RT_FULL -+ - #include "locking-selftest-rlock-hardirq.h" - GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_rlock) - -@@ -678,6 +687,8 @@ - #include "locking-selftest-wlock-softirq.h" - GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_soft_wlock) - -+#endif -+ - #undef E1 - #undef E2 - -@@ -709,6 +720,8 @@ - #include "locking-selftest-spin-hardirq.h" - GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_spin) - -+#ifndef CONFIG_PREEMPT_RT_FULL -+ - #include "locking-selftest-rlock-hardirq.h" - GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_rlock) - -@@ -724,6 +737,8 @@ - #include "locking-selftest-wlock-softirq.h" - GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_soft_wlock) - -+#endif -+ - #undef E1 - #undef E2 - #undef E3 -@@ -757,6 +772,8 @@ - #include "locking-selftest-spin-hardirq.h" - GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_spin) - -+#ifndef CONFIG_PREEMPT_RT_FULL -+ - #include "locking-selftest-rlock-hardirq.h" - GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_rlock) - -@@ -772,10 +789,14 @@ - #include "locking-selftest-wlock-softirq.h" - GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_soft_wlock) - -+#endif -+ - #undef E1 - #undef E2 - #undef E3 - -+#ifndef CONFIG_PREEMPT_RT_FULL -+ - /* - * read-lock / write-lock irq inversion. - * -@@ -838,6 +859,10 @@ - #undef E2 - #undef E3 - -+#endif -+ -+#ifndef CONFIG_PREEMPT_RT_FULL -+ - /* - * read-lock / write-lock recursion that is actually safe. - */ -@@ -876,6 +901,8 @@ - #undef E2 - #undef E3 - -+#endif -+ - /* - * read-lock / write-lock recursion that is unsafe. - */ -@@ -1858,6 +1885,7 @@ - - printk(" --------------------------------------------------------------------------\n"); - -+#ifndef CONFIG_PREEMPT_RT_FULL - /* - * irq-context testcases: - */ -@@ -1870,6 +1898,28 @@ - - DO_TESTCASE_6x2("irq read-recursion", irq_read_recursion); - // DO_TESTCASE_6x2B("irq read-recursion #2", irq_read_recursion2); -+#else -+ /* On -rt, we only do hardirq context test for raw spinlock */ -+ DO_TESTCASE_1B("hard-irqs-on + irq-safe-A", irqsafe1_hard_spin, 12); -+ DO_TESTCASE_1B("hard-irqs-on + irq-safe-A", irqsafe1_hard_spin, 21); -+ -+ DO_TESTCASE_1B("hard-safe-A + irqs-on", irqsafe2B_hard_spin, 12); -+ DO_TESTCASE_1B("hard-safe-A + irqs-on", irqsafe2B_hard_spin, 21); -+ -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 123); -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 132); -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 213); -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 231); -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 312); -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 321); -+ -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 123); -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 132); -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 213); -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 231); -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 312); -+ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 321); -+#endif - - ww_tests(); - -diff -Nur linux-3.18.14.orig/lib/percpu_ida.c linux-3.18.14-rt/lib/percpu_ida.c ---- linux-3.18.14.orig/lib/percpu_ida.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/lib/percpu_ida.c 2015-05-31 15:32:49.161635360 -0500 -@@ -29,6 +29,9 @@ - #include - #include - #include -+#include -+ -+static DEFINE_LOCAL_IRQ_LOCK(irq_off_lock); - - struct percpu_ida_cpu { - /* -@@ -151,13 +154,13 @@ - unsigned long flags; - int tag; - -- local_irq_save(flags); -+ local_lock_irqsave(irq_off_lock, flags); - tags = this_cpu_ptr(pool->tag_cpu); - - /* Fastpath */ - tag = alloc_local_tag(tags); - if (likely(tag >= 0)) { -- local_irq_restore(flags); -+ local_unlock_irqrestore(irq_off_lock, flags); - return tag; - } - -@@ -176,6 +179,7 @@ - - if (!tags->nr_free) - alloc_global_tags(pool, tags); -+ - if (!tags->nr_free) - steal_tags(pool, tags); - -@@ -187,7 +191,7 @@ - } - - spin_unlock(&pool->lock); -- local_irq_restore(flags); -+ local_unlock_irqrestore(irq_off_lock, flags); - - if (tag >= 0 || state == TASK_RUNNING) - break; -@@ -199,7 +203,7 @@ - - schedule(); - -- local_irq_save(flags); -+ local_lock_irqsave(irq_off_lock, flags); - tags = this_cpu_ptr(pool->tag_cpu); - } - if (state != TASK_RUNNING) -@@ -224,7 +228,7 @@ - - BUG_ON(tag >= pool->nr_tags); - -- local_irq_save(flags); -+ local_lock_irqsave(irq_off_lock, flags); - tags = this_cpu_ptr(pool->tag_cpu); - - spin_lock(&tags->lock); -@@ -256,7 +260,7 @@ - spin_unlock(&pool->lock); - } - -- local_irq_restore(flags); -+ local_unlock_irqrestore(irq_off_lock, flags); - } - EXPORT_SYMBOL_GPL(percpu_ida_free); - -@@ -348,7 +352,7 @@ - struct percpu_ida_cpu *remote; - unsigned cpu, i, err = 0; - -- local_irq_save(flags); -+ local_lock_irqsave(irq_off_lock, flags); - for_each_possible_cpu(cpu) { - remote = per_cpu_ptr(pool->tag_cpu, cpu); - spin_lock(&remote->lock); -@@ -370,7 +374,7 @@ - } - spin_unlock(&pool->lock); - out: -- local_irq_restore(flags); -+ local_unlock_irqrestore(irq_off_lock, flags); - return err; - } - EXPORT_SYMBOL_GPL(percpu_ida_for_each_free); -diff -Nur linux-3.18.14.orig/lib/radix-tree.c linux-3.18.14-rt/lib/radix-tree.c ---- linux-3.18.14.orig/lib/radix-tree.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/lib/radix-tree.c 2015-05-31 15:32:49.161635360 -0500 -@@ -195,12 +195,13 @@ - * succeed in getting a node here (and never reach - * kmem_cache_alloc) - */ -- rtp = this_cpu_ptr(&radix_tree_preloads); -+ rtp = &get_cpu_var(radix_tree_preloads); - if (rtp->nr) { - ret = rtp->nodes[rtp->nr - 1]; - rtp->nodes[rtp->nr - 1] = NULL; - rtp->nr--; - } -+ put_cpu_var(radix_tree_preloads); - /* - * Update the allocation stack trace as this is more useful - * for debugging. -@@ -240,6 +241,7 @@ - call_rcu(&node->rcu_head, radix_tree_node_rcu_free); - } - -+#ifndef CONFIG_PREEMPT_RT_FULL - /* - * Load up this CPU's radix_tree_node buffer with sufficient objects to - * ensure that the addition of a single element in the tree cannot fail. On -@@ -305,6 +307,7 @@ - return 0; - } - EXPORT_SYMBOL(radix_tree_maybe_preload); -+#endif - - /* - * Return the maximum key which can be store into a -diff -Nur linux-3.18.14.orig/lib/scatterlist.c linux-3.18.14-rt/lib/scatterlist.c ---- linux-3.18.14.orig/lib/scatterlist.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/lib/scatterlist.c 2015-05-31 15:32:49.161635360 -0500 -@@ -592,7 +592,7 @@ - flush_kernel_dcache_page(miter->page); - - if (miter->__flags & SG_MITER_ATOMIC) { -- WARN_ON_ONCE(preemptible()); -+ WARN_ON_ONCE(!pagefault_disabled()); - kunmap_atomic(miter->addr); - } else - kunmap(miter->page); -@@ -637,7 +637,7 @@ - if (!sg_miter_skip(&miter, skip)) - return false; - -- local_irq_save(flags); -+ local_irq_save_nort(flags); - - while (sg_miter_next(&miter) && offset < buflen) { - unsigned int len; -@@ -654,7 +654,7 @@ - - sg_miter_stop(&miter); - -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - return offset; - } - -diff -Nur linux-3.18.14.orig/lib/smp_processor_id.c linux-3.18.14-rt/lib/smp_processor_id.c ---- linux-3.18.14.orig/lib/smp_processor_id.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/lib/smp_processor_id.c 2015-05-31 15:32:49.161635360 -0500 -@@ -39,8 +39,9 @@ - if (!printk_ratelimit()) - goto out_enable; - -- printk(KERN_ERR "BUG: using %s%s() in preemptible [%08x] code: %s/%d\n", -- what1, what2, preempt_count() - 1, current->comm, current->pid); -+ printk(KERN_ERR "BUG: using %s%s() in preemptible [%08x %08x] code: %s/%d\n", -+ what1, what2, preempt_count() - 1, __migrate_disabled(current), -+ current->comm, current->pid); - - print_symbol("caller is %s\n", (long)__builtin_return_address(0)); - dump_stack(); -diff -Nur linux-3.18.14.orig/mm/filemap.c linux-3.18.14-rt/mm/filemap.c ---- linux-3.18.14.orig/mm/filemap.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/filemap.c 2015-05-31 15:32:49.181635360 -0500 -@@ -168,7 +168,9 @@ - if (!workingset_node_pages(node) && - list_empty(&node->private_list)) { - node->private_data = mapping; -- list_lru_add(&workingset_shadow_nodes, &node->private_list); -+ local_lock(workingset_shadow_lock); -+ list_lru_add(&__workingset_shadow_nodes, &node->private_list); -+ local_unlock(workingset_shadow_lock); - } - } - -@@ -535,9 +537,12 @@ - * node->private_list is protected by - * mapping->tree_lock. - */ -- if (!list_empty(&node->private_list)) -- list_lru_del(&workingset_shadow_nodes, -+ if (!list_empty(&node->private_list)) { -+ local_lock(workingset_shadow_lock); -+ list_lru_del(&__workingset_shadow_nodes, - &node->private_list); -+ local_unlock(workingset_shadow_lock); -+ } - } - return 0; - } -diff -Nur linux-3.18.14.orig/mm/highmem.c linux-3.18.14-rt/mm/highmem.c ---- linux-3.18.14.orig/mm/highmem.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/highmem.c 2015-05-31 15:32:49.201635360 -0500 -@@ -29,10 +29,11 @@ - #include - #include - -- -+#ifndef CONFIG_PREEMPT_RT_FULL - #if defined(CONFIG_HIGHMEM) || defined(CONFIG_X86_32) - DEFINE_PER_CPU(int, __kmap_atomic_idx); - #endif -+#endif - - /* - * Virtual_count is not a pure "count". -@@ -107,8 +108,9 @@ - unsigned long totalhigh_pages __read_mostly; - EXPORT_SYMBOL(totalhigh_pages); - -- -+#ifndef CONFIG_PREEMPT_RT_FULL - EXPORT_PER_CPU_SYMBOL(__kmap_atomic_idx); -+#endif - - unsigned int nr_free_highpages (void) - { -diff -Nur linux-3.18.14.orig/mm/Kconfig linux-3.18.14-rt/mm/Kconfig ---- linux-3.18.14.orig/mm/Kconfig 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/Kconfig 2015-05-31 15:32:49.177635360 -0500 -@@ -408,7 +408,7 @@ - - config TRANSPARENT_HUGEPAGE - bool "Transparent Hugepage Support" -- depends on HAVE_ARCH_TRANSPARENT_HUGEPAGE -+ depends on HAVE_ARCH_TRANSPARENT_HUGEPAGE && !PREEMPT_RT_FULL - select COMPACTION - help - Transparent Hugepages allows the kernel to use huge pages and -diff -Nur linux-3.18.14.orig/mm/memcontrol.c linux-3.18.14-rt/mm/memcontrol.c ---- linux-3.18.14.orig/mm/memcontrol.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/memcontrol.c 2015-05-31 15:32:49.213635360 -0500 -@@ -60,6 +60,8 @@ - #include - #include - #include -+#include -+ - #include "slab.h" - - #include -@@ -87,6 +89,7 @@ - #define do_swap_account 0 - #endif - -+static DEFINE_LOCAL_IRQ_LOCK(event_lock); - - static const char * const mem_cgroup_stat_names[] = { - "cache", -@@ -2376,14 +2379,17 @@ - */ - static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages) - { -- struct memcg_stock_pcp *stock = &get_cpu_var(memcg_stock); -+ struct memcg_stock_pcp *stock; -+ int cpu = get_cpu_light(); -+ -+ stock = &per_cpu(memcg_stock, cpu); - - if (stock->cached != memcg) { /* reset if necessary */ - drain_stock(stock); - stock->cached = memcg; - } - stock->nr_pages += nr_pages; -- put_cpu_var(memcg_stock); -+ put_cpu_light(); - } - - /* -@@ -2397,7 +2403,7 @@ - - /* Notify other cpus that system-wide "drain" is running */ - get_online_cpus(); -- curcpu = get_cpu(); -+ curcpu = get_cpu_light(); - for_each_online_cpu(cpu) { - struct memcg_stock_pcp *stock = &per_cpu(memcg_stock, cpu); - struct mem_cgroup *memcg; -@@ -2414,7 +2420,7 @@ - schedule_work_on(cpu, &stock->work); - } - } -- put_cpu(); -+ put_cpu_light(); - - if (!sync) - goto out; -@@ -3419,12 +3425,12 @@ - move_unlock_mem_cgroup(from, &flags); - ret = 0; - -- local_irq_disable(); -+ local_lock_irq(event_lock); - mem_cgroup_charge_statistics(to, page, nr_pages); - memcg_check_events(to, page); - mem_cgroup_charge_statistics(from, page, -nr_pages); - memcg_check_events(from, page); -- local_irq_enable(); -+ local_unlock_irq(event_lock); - out_unlock: - unlock_page(page); - out: -@@ -6406,10 +6412,10 @@ - VM_BUG_ON_PAGE(!PageTransHuge(page), page); - } - -- local_irq_disable(); -+ local_lock_irq(event_lock); - mem_cgroup_charge_statistics(memcg, page, nr_pages); - memcg_check_events(memcg, page); -- local_irq_enable(); -+ local_unlock_irq(event_lock); - - if (do_swap_account && PageSwapCache(page)) { - swp_entry_t entry = { .val = page_private(page) }; -@@ -6468,14 +6474,14 @@ - memcg_oom_recover(memcg); - } - -- local_irq_save(flags); -+ local_lock_irqsave(event_lock, flags); - __this_cpu_sub(memcg->stat->count[MEM_CGROUP_STAT_RSS], nr_anon); - __this_cpu_sub(memcg->stat->count[MEM_CGROUP_STAT_CACHE], nr_file); - __this_cpu_sub(memcg->stat->count[MEM_CGROUP_STAT_RSS_HUGE], nr_huge); - __this_cpu_add(memcg->stat->events[MEM_CGROUP_EVENTS_PGPGOUT], pgpgout); - __this_cpu_add(memcg->stat->nr_page_events, nr_anon + nr_file); - memcg_check_events(memcg, dummy_page); -- local_irq_restore(flags); -+ local_unlock_irqrestore(event_lock, flags); - } - - static void uncharge_list(struct list_head *page_list) -diff -Nur linux-3.18.14.orig/mm/memory.c linux-3.18.14-rt/mm/memory.c ---- linux-3.18.14.orig/mm/memory.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/memory.c 2015-05-31 15:32:49.229635360 -0500 -@@ -3244,6 +3244,32 @@ - return 0; - } - -+#ifdef CONFIG_PREEMPT_RT_FULL -+void pagefault_disable(void) -+{ -+ migrate_disable(); -+ current->pagefault_disabled++; -+ /* -+ * make sure to have issued the store before a pagefault -+ * can hit. -+ */ -+ barrier(); -+} -+EXPORT_SYMBOL(pagefault_disable); -+ -+void pagefault_enable(void) -+{ -+ /* -+ * make sure to issue those last loads/stores before enabling -+ * the pagefault handler again. -+ */ -+ barrier(); -+ current->pagefault_disabled--; -+ migrate_enable(); -+} -+EXPORT_SYMBOL(pagefault_enable); -+#endif -+ - /* - * By the time we get here, we already hold the mm semaphore - * -diff -Nur linux-3.18.14.orig/mm/mmu_context.c linux-3.18.14-rt/mm/mmu_context.c ---- linux-3.18.14.orig/mm/mmu_context.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/mmu_context.c 2015-05-31 15:32:49.249635360 -0500 -@@ -23,6 +23,7 @@ - struct task_struct *tsk = current; - - task_lock(tsk); -+ preempt_disable_rt(); - active_mm = tsk->active_mm; - if (active_mm != mm) { - atomic_inc(&mm->mm_count); -@@ -30,6 +31,7 @@ - } - tsk->mm = mm; - switch_mm(active_mm, mm, tsk); -+ preempt_enable_rt(); - task_unlock(tsk); - #ifdef finish_arch_post_lock_switch - finish_arch_post_lock_switch(); -diff -Nur linux-3.18.14.orig/mm/page_alloc.c linux-3.18.14-rt/mm/page_alloc.c ---- linux-3.18.14.orig/mm/page_alloc.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/page_alloc.c 2015-05-31 15:32:49.253635359 -0500 -@@ -59,6 +59,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -230,6 +231,18 @@ - EXPORT_SYMBOL(nr_online_nodes); - #endif - -+static DEFINE_LOCAL_IRQ_LOCK(pa_lock); -+ -+#ifdef CONFIG_PREEMPT_RT_BASE -+# define cpu_lock_irqsave(cpu, flags) \ -+ local_lock_irqsave_on(pa_lock, flags, cpu) -+# define cpu_unlock_irqrestore(cpu, flags) \ -+ local_unlock_irqrestore_on(pa_lock, flags, cpu) -+#else -+# define cpu_lock_irqsave(cpu, flags) local_irq_save(flags) -+# define cpu_unlock_irqrestore(cpu, flags) local_irq_restore(flags) -+#endif -+ - int page_group_by_mobility_disabled __read_mostly; - - void set_pageblock_migratetype(struct page *page, int migratetype) -@@ -654,7 +667,7 @@ - } - - /* -- * Frees a number of pages from the PCP lists -+ * Frees a number of pages which have been collected from the pcp lists. - * Assumes all pages on list are in same zone, and of same order. - * count is the number of pages to free. - * -@@ -665,18 +678,51 @@ - * pinned" detection logic. - */ - static void free_pcppages_bulk(struct zone *zone, int count, -- struct per_cpu_pages *pcp) -+ struct list_head *list) - { -- int migratetype = 0; -- int batch_free = 0; - int to_free = count; - unsigned long nr_scanned; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&zone->lock, flags); - -- spin_lock(&zone->lock); - nr_scanned = zone_page_state(zone, NR_PAGES_SCANNED); - if (nr_scanned) - __mod_zone_page_state(zone, NR_PAGES_SCANNED, -nr_scanned); - -+ while (!list_empty(list)) { -+ struct page *page = list_first_entry(list, struct page, lru); -+ int mt; /* migratetype of the to-be-freed page */ -+ -+ /* must delete as __free_one_page list manipulates */ -+ list_del(&page->lru); -+ -+ mt = get_freepage_migratetype(page); -+ if (unlikely(has_isolate_pageblock(zone))) -+ mt = get_pageblock_migratetype(page); -+ -+ /* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */ -+ __free_one_page(page, page_to_pfn(page), zone, 0, mt); -+ trace_mm_page_pcpu_drain(page, 0, mt); -+ to_free--; -+ } -+ WARN_ON(to_free != 0); -+ spin_unlock_irqrestore(&zone->lock, flags); -+} -+ -+/* -+ * Moves a number of pages from the PCP lists to free list which -+ * is freed outside of the locked region. -+ * -+ * Assumes all pages on list are in same zone, and of same order. -+ * count is the number of pages to free. -+ */ -+static void isolate_pcp_pages(int to_free, struct per_cpu_pages *src, -+ struct list_head *dst) -+{ -+ int migratetype = 0; -+ int batch_free = 0; -+ - while (to_free) { - struct page *page; - struct list_head *list; -@@ -692,7 +738,7 @@ - batch_free++; - if (++migratetype == MIGRATE_PCPTYPES) - migratetype = 0; -- list = &pcp->lists[migratetype]; -+ list = &src->lists[migratetype]; - } while (list_empty(list)); - - /* This is the only non-empty list. Free them all. */ -@@ -700,21 +746,11 @@ - batch_free = to_free; - - do { -- int mt; /* migratetype of the to-be-freed page */ -- -- page = list_entry(list->prev, struct page, lru); -- /* must delete as __free_one_page list manipulates */ -+ page = list_last_entry(list, struct page, lru); - list_del(&page->lru); -- mt = get_freepage_migratetype(page); -- if (unlikely(has_isolate_pageblock(zone))) -- mt = get_pageblock_migratetype(page); -- -- /* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */ -- __free_one_page(page, page_to_pfn(page), zone, 0, mt); -- trace_mm_page_pcpu_drain(page, 0, mt); -+ list_add(&page->lru, dst); - } while (--to_free && --batch_free && !list_empty(list)); - } -- spin_unlock(&zone->lock); - } - - static void free_one_page(struct zone *zone, -@@ -723,7 +759,9 @@ - int migratetype) - { - unsigned long nr_scanned; -- spin_lock(&zone->lock); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&zone->lock, flags); - nr_scanned = zone_page_state(zone, NR_PAGES_SCANNED); - if (nr_scanned) - __mod_zone_page_state(zone, NR_PAGES_SCANNED, -nr_scanned); -@@ -733,7 +771,7 @@ - migratetype = get_pfnblock_migratetype(page, pfn); - } - __free_one_page(page, pfn, zone, order, migratetype); -- spin_unlock(&zone->lock); -+ spin_unlock_irqrestore(&zone->lock, flags); - } - - static bool free_pages_prepare(struct page *page, unsigned int order) -@@ -773,11 +811,11 @@ - return; - - migratetype = get_pfnblock_migratetype(page, pfn); -- local_irq_save(flags); -+ local_lock_irqsave(pa_lock, flags); - __count_vm_events(PGFREE, 1 << order); - set_freepage_migratetype(page, migratetype); - free_one_page(page_zone(page), page, pfn, order, migratetype); -- local_irq_restore(flags); -+ local_unlock_irqrestore(pa_lock, flags); - } - - void __init __free_pages_bootmem(struct page *page, unsigned int order) -@@ -1251,16 +1289,18 @@ - void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp) - { - unsigned long flags; -+ LIST_HEAD(dst); - int to_drain, batch; - -- local_irq_save(flags); -+ local_lock_irqsave(pa_lock, flags); - batch = ACCESS_ONCE(pcp->batch); - to_drain = min(pcp->count, batch); - if (to_drain > 0) { -- free_pcppages_bulk(zone, to_drain, pcp); -+ isolate_pcp_pages(to_drain, pcp, &dst); - pcp->count -= to_drain; - } -- local_irq_restore(flags); -+ local_unlock_irqrestore(pa_lock, flags); -+ free_pcppages_bulk(zone, to_drain, &dst); - } - #endif - -@@ -1279,16 +1319,21 @@ - for_each_populated_zone(zone) { - struct per_cpu_pageset *pset; - struct per_cpu_pages *pcp; -+ LIST_HEAD(dst); -+ int count; - -- local_irq_save(flags); -+ cpu_lock_irqsave(cpu, flags); - pset = per_cpu_ptr(zone->pageset, cpu); - - pcp = &pset->pcp; -- if (pcp->count) { -- free_pcppages_bulk(zone, pcp->count, pcp); -+ count = pcp->count; -+ if (count) { -+ isolate_pcp_pages(count, pcp, &dst); - pcp->count = 0; - } -- local_irq_restore(flags); -+ cpu_unlock_irqrestore(cpu, flags); -+ if (count) -+ free_pcppages_bulk(zone, count, &dst); - } - } - -@@ -1341,7 +1386,12 @@ - else - cpumask_clear_cpu(cpu, &cpus_with_pcps); - } -+#ifndef CONFIG_PREEMPT_RT_BASE - on_each_cpu_mask(&cpus_with_pcps, drain_local_pages, NULL, 1); -+#else -+ for_each_cpu(cpu, &cpus_with_pcps) -+ drain_pages(cpu); -+#endif - } - - #ifdef CONFIG_HIBERNATION -@@ -1397,7 +1447,7 @@ - - migratetype = get_pfnblock_migratetype(page, pfn); - set_freepage_migratetype(page, migratetype); -- local_irq_save(flags); -+ local_lock_irqsave(pa_lock, flags); - __count_vm_event(PGFREE); - - /* -@@ -1423,12 +1473,17 @@ - pcp->count++; - if (pcp->count >= pcp->high) { - unsigned long batch = ACCESS_ONCE(pcp->batch); -- free_pcppages_bulk(zone, batch, pcp); -+ LIST_HEAD(dst); -+ -+ isolate_pcp_pages(batch, pcp, &dst); - pcp->count -= batch; -+ local_unlock_irqrestore(pa_lock, flags); -+ free_pcppages_bulk(zone, batch, &dst); -+ return; - } - - out: -- local_irq_restore(flags); -+ local_unlock_irqrestore(pa_lock, flags); - } - - /* -@@ -1558,7 +1613,7 @@ - struct per_cpu_pages *pcp; - struct list_head *list; - -- local_irq_save(flags); -+ local_lock_irqsave(pa_lock, flags); - pcp = &this_cpu_ptr(zone->pageset)->pcp; - list = &pcp->lists[migratetype]; - if (list_empty(list)) { -@@ -1590,13 +1645,15 @@ - */ - WARN_ON_ONCE(order > 1); - } -- spin_lock_irqsave(&zone->lock, flags); -+ local_spin_lock_irqsave(pa_lock, &zone->lock, flags); - page = __rmqueue(zone, order, migratetype); -- spin_unlock(&zone->lock); -- if (!page) -+ if (!page) { -+ spin_unlock(&zone->lock); - goto failed; -+ } - __mod_zone_freepage_state(zone, -(1 << order), - get_freepage_migratetype(page)); -+ spin_unlock(&zone->lock); - } - - __mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order)); -@@ -1606,7 +1663,7 @@ - - __count_zone_vm_events(PGALLOC, zone, 1 << order); - zone_statistics(preferred_zone, zone, gfp_flags); -- local_irq_restore(flags); -+ local_unlock_irqrestore(pa_lock, flags); - - VM_BUG_ON_PAGE(bad_range(zone, page), page); - if (prep_new_page(page, order, gfp_flags)) -@@ -1614,7 +1671,7 @@ - return page; - - failed: -- local_irq_restore(flags); -+ local_unlock_irqrestore(pa_lock, flags); - return NULL; - } - -@@ -2325,8 +2382,8 @@ - count_vm_event(COMPACTSTALL); - - /* Page migration frees to the PCP lists but we want merging */ -- drain_pages(get_cpu()); -- put_cpu(); -+ drain_pages(get_cpu_light()); -+ put_cpu_light(); - - page = get_page_from_freelist(gfp_mask, nodemask, - order, zonelist, high_zoneidx, -@@ -5565,6 +5622,7 @@ - void __init page_alloc_init(void) - { - hotcpu_notifier(page_alloc_cpu_notify, 0); -+ local_irq_lock_init(pa_lock); - } - - /* -@@ -6459,7 +6517,7 @@ - struct per_cpu_pageset *pset; - - /* avoid races with drain_pages() */ -- local_irq_save(flags); -+ local_lock_irqsave(pa_lock, flags); - if (zone->pageset != &boot_pageset) { - for_each_online_cpu(cpu) { - pset = per_cpu_ptr(zone->pageset, cpu); -@@ -6468,7 +6526,7 @@ - free_percpu(zone->pageset); - zone->pageset = &boot_pageset; - } -- local_irq_restore(flags); -+ local_unlock_irqrestore(pa_lock, flags); - } - - #ifdef CONFIG_MEMORY_HOTREMOVE -diff -Nur linux-3.18.14.orig/mm/slab.h linux-3.18.14-rt/mm/slab.h ---- linux-3.18.14.orig/mm/slab.h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/slab.h 2015-05-31 15:32:49.257635359 -0500 -@@ -315,7 +315,11 @@ - * The slab lists for all objects. - */ - struct kmem_cache_node { -+#ifdef CONFIG_SLUB -+ raw_spinlock_t list_lock; -+#else - spinlock_t list_lock; -+#endif - - #ifdef CONFIG_SLAB - struct list_head slabs_partial; /* partial list first, better asm code */ -diff -Nur linux-3.18.14.orig/mm/slub.c linux-3.18.14-rt/mm/slub.c ---- linux-3.18.14.orig/mm/slub.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/slub.c 2015-05-31 15:32:49.257635359 -0500 -@@ -1044,7 +1044,7 @@ - { - struct kmem_cache_node *n = get_node(s, page_to_nid(page)); - -- spin_lock_irqsave(&n->list_lock, *flags); -+ raw_spin_lock_irqsave(&n->list_lock, *flags); - slab_lock(page); - - if (!check_slab(s, page)) -@@ -1091,7 +1091,7 @@ - - fail: - slab_unlock(page); -- spin_unlock_irqrestore(&n->list_lock, *flags); -+ raw_spin_unlock_irqrestore(&n->list_lock, *flags); - slab_fix(s, "Object at 0x%p not freed", object); - return NULL; - } -@@ -1219,6 +1219,12 @@ - - #endif /* CONFIG_SLUB_DEBUG */ - -+struct slub_free_list { -+ raw_spinlock_t lock; -+ struct list_head list; -+}; -+static DEFINE_PER_CPU(struct slub_free_list, slub_free_list); -+ - /* - * Hooks for other subsystems that check memory allocations. In a typical - * production configuration these hooks all should produce no code at all. -@@ -1303,10 +1309,15 @@ - struct page *page; - struct kmem_cache_order_objects oo = s->oo; - gfp_t alloc_gfp; -+ bool enableirqs; - - flags &= gfp_allowed_mask; - -- if (flags & __GFP_WAIT) -+ enableirqs = (flags & __GFP_WAIT) != 0; -+#ifdef CONFIG_PREEMPT_RT_FULL -+ enableirqs |= system_state == SYSTEM_RUNNING; -+#endif -+ if (enableirqs) - local_irq_enable(); - - flags |= s->allocflags; -@@ -1347,7 +1358,7 @@ - kmemcheck_mark_unallocated_pages(page, pages); - } - -- if (flags & __GFP_WAIT) -+ if (enableirqs) - local_irq_disable(); - if (!page) - return NULL; -@@ -1365,8 +1376,10 @@ - void *object) - { - setup_object_debug(s, page, object); -+#ifndef CONFIG_PREEMPT_RT_FULL - if (unlikely(s->ctor)) - s->ctor(object); -+#endif - } - - static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) -@@ -1442,6 +1455,16 @@ - memcg_uncharge_slab(s, order); - } - -+static void free_delayed(struct list_head *h) -+{ -+ while(!list_empty(h)) { -+ struct page *page = list_first_entry(h, struct page, lru); -+ -+ list_del(&page->lru); -+ __free_slab(page->slab_cache, page); -+ } -+} -+ - #define need_reserve_slab_rcu \ - (sizeof(((struct page *)NULL)->lru) < sizeof(struct rcu_head)) - -@@ -1476,6 +1499,12 @@ - } - - call_rcu(head, rcu_free_slab); -+ } else if (irqs_disabled()) { -+ struct slub_free_list *f = &__get_cpu_var(slub_free_list); -+ -+ raw_spin_lock(&f->lock); -+ list_add(&page->lru, &f->list); -+ raw_spin_unlock(&f->lock); - } else - __free_slab(s, page); - } -@@ -1589,7 +1618,7 @@ - if (!n || !n->nr_partial) - return NULL; - -- spin_lock(&n->list_lock); -+ raw_spin_lock(&n->list_lock); - list_for_each_entry_safe(page, page2, &n->partial, lru) { - void *t; - -@@ -1614,7 +1643,7 @@ - break; - - } -- spin_unlock(&n->list_lock); -+ raw_spin_unlock(&n->list_lock); - return object; - } - -@@ -1860,7 +1889,7 @@ - * that acquire_slab() will see a slab page that - * is frozen - */ -- spin_lock(&n->list_lock); -+ raw_spin_lock(&n->list_lock); - } - } else { - m = M_FULL; -@@ -1871,7 +1900,7 @@ - * slabs from diagnostic functions will not see - * any frozen slabs. - */ -- spin_lock(&n->list_lock); -+ raw_spin_lock(&n->list_lock); - } - } - -@@ -1906,7 +1935,7 @@ - goto redo; - - if (lock) -- spin_unlock(&n->list_lock); -+ raw_spin_unlock(&n->list_lock); - - if (m == M_FREE) { - stat(s, DEACTIVATE_EMPTY); -@@ -1938,10 +1967,10 @@ - n2 = get_node(s, page_to_nid(page)); - if (n != n2) { - if (n) -- spin_unlock(&n->list_lock); -+ raw_spin_unlock(&n->list_lock); - - n = n2; -- spin_lock(&n->list_lock); -+ raw_spin_lock(&n->list_lock); - } - - do { -@@ -1970,7 +1999,7 @@ - } - - if (n) -- spin_unlock(&n->list_lock); -+ raw_spin_unlock(&n->list_lock); - - while (discard_page) { - page = discard_page; -@@ -2008,14 +2037,21 @@ - pobjects = oldpage->pobjects; - pages = oldpage->pages; - if (drain && pobjects > s->cpu_partial) { -+ struct slub_free_list *f; - unsigned long flags; -+ LIST_HEAD(tofree); - /* - * partial array is full. Move the existing - * set to the per node partial list. - */ - local_irq_save(flags); - unfreeze_partials(s, this_cpu_ptr(s->cpu_slab)); -+ f = &__get_cpu_var(slub_free_list); -+ raw_spin_lock(&f->lock); -+ list_splice_init(&f->list, &tofree); -+ raw_spin_unlock(&f->lock); - local_irq_restore(flags); -+ free_delayed(&tofree); - oldpage = NULL; - pobjects = 0; - pages = 0; -@@ -2079,7 +2115,22 @@ - - static void flush_all(struct kmem_cache *s) - { -+ LIST_HEAD(tofree); -+ int cpu; -+ - on_each_cpu_cond(has_cpu_slab, flush_cpu_slab, s, 1, GFP_ATOMIC); -+ for_each_online_cpu(cpu) { -+ struct slub_free_list *f; -+ -+ if (!has_cpu_slab(cpu, s)) -+ continue; -+ -+ f = &per_cpu(slub_free_list, cpu); -+ raw_spin_lock_irq(&f->lock); -+ list_splice_init(&f->list, &tofree); -+ raw_spin_unlock_irq(&f->lock); -+ free_delayed(&tofree); -+ } - } - - /* -@@ -2115,10 +2166,10 @@ - unsigned long x = 0; - struct page *page; - -- spin_lock_irqsave(&n->list_lock, flags); -+ raw_spin_lock_irqsave(&n->list_lock, flags); - list_for_each_entry(page, &n->partial, lru) - x += get_count(page); -- spin_unlock_irqrestore(&n->list_lock, flags); -+ raw_spin_unlock_irqrestore(&n->list_lock, flags); - return x; - } - #endif /* CONFIG_SLUB_DEBUG || CONFIG_SYSFS */ -@@ -2255,9 +2306,11 @@ - static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, - unsigned long addr, struct kmem_cache_cpu *c) - { -+ struct slub_free_list *f; - void *freelist; - struct page *page; - unsigned long flags; -+ LIST_HEAD(tofree); - - local_irq_save(flags); - #ifdef CONFIG_PREEMPT -@@ -2325,7 +2378,13 @@ - VM_BUG_ON(!c->page->frozen); - c->freelist = get_freepointer(s, freelist); - c->tid = next_tid(c->tid); -+out: -+ f = &__get_cpu_var(slub_free_list); -+ raw_spin_lock(&f->lock); -+ list_splice_init(&f->list, &tofree); -+ raw_spin_unlock(&f->lock); - local_irq_restore(flags); -+ free_delayed(&tofree); - return freelist; - - new_slab: -@@ -2342,8 +2401,7 @@ - - if (unlikely(!freelist)) { - slab_out_of_memory(s, gfpflags, node); -- local_irq_restore(flags); -- return NULL; -+ goto out; - } - - page = c->page; -@@ -2358,8 +2416,7 @@ - deactivate_slab(s, page, get_freepointer(s, freelist)); - c->page = NULL; - c->freelist = NULL; -- local_irq_restore(flags); -- return freelist; -+ goto out; - } - - /* -@@ -2444,6 +2501,10 @@ - - if (unlikely(gfpflags & __GFP_ZERO) && object) - memset(object, 0, s->object_size); -+#ifdef CONFIG_PREEMPT_RT_FULL -+ if (unlikely(s->ctor) && object) -+ s->ctor(object); -+#endif - - slab_post_alloc_hook(s, gfpflags, object); - -@@ -2531,7 +2592,7 @@ - - do { - if (unlikely(n)) { -- spin_unlock_irqrestore(&n->list_lock, flags); -+ raw_spin_unlock_irqrestore(&n->list_lock, flags); - n = NULL; - } - prior = page->freelist; -@@ -2563,7 +2624,7 @@ - * Otherwise the list_lock will synchronize with - * other processors updating the list of slabs. - */ -- spin_lock_irqsave(&n->list_lock, flags); -+ raw_spin_lock_irqsave(&n->list_lock, flags); - - } - } -@@ -2605,7 +2666,7 @@ - add_partial(n, page, DEACTIVATE_TO_TAIL); - stat(s, FREE_ADD_PARTIAL); - } -- spin_unlock_irqrestore(&n->list_lock, flags); -+ raw_spin_unlock_irqrestore(&n->list_lock, flags); - return; - - slab_empty: -@@ -2620,7 +2681,7 @@ - remove_full(s, n, page); - } - -- spin_unlock_irqrestore(&n->list_lock, flags); -+ raw_spin_unlock_irqrestore(&n->list_lock, flags); - stat(s, FREE_SLAB); - discard_slab(s, page); - } -@@ -2816,7 +2877,7 @@ - init_kmem_cache_node(struct kmem_cache_node *n) - { - n->nr_partial = 0; -- spin_lock_init(&n->list_lock); -+ raw_spin_lock_init(&n->list_lock); - INIT_LIST_HEAD(&n->partial); - #ifdef CONFIG_SLUB_DEBUG - atomic_long_set(&n->nr_slabs, 0); -@@ -3373,7 +3434,7 @@ - for (i = 0; i < objects; i++) - INIT_LIST_HEAD(slabs_by_inuse + i); - -- spin_lock_irqsave(&n->list_lock, flags); -+ raw_spin_lock_irqsave(&n->list_lock, flags); - - /* - * Build lists indexed by the items in use in each slab. -@@ -3394,7 +3455,7 @@ - for (i = objects - 1; i > 0; i--) - list_splice(slabs_by_inuse + i, n->partial.prev); - -- spin_unlock_irqrestore(&n->list_lock, flags); -+ raw_spin_unlock_irqrestore(&n->list_lock, flags); - - /* Release empty slabs */ - list_for_each_entry_safe(page, t, slabs_by_inuse, lru) -@@ -3567,6 +3628,12 @@ - { - static __initdata struct kmem_cache boot_kmem_cache, - boot_kmem_cache_node; -+ int cpu; -+ -+ for_each_possible_cpu(cpu) { -+ raw_spin_lock_init(&per_cpu(slub_free_list, cpu).lock); -+ INIT_LIST_HEAD(&per_cpu(slub_free_list, cpu).list); -+ } - - if (debug_guardpage_minorder()) - slub_max_order = 0; -@@ -3815,7 +3882,7 @@ - struct page *page; - unsigned long flags; - -- spin_lock_irqsave(&n->list_lock, flags); -+ raw_spin_lock_irqsave(&n->list_lock, flags); - - list_for_each_entry(page, &n->partial, lru) { - validate_slab_slab(s, page, map); -@@ -3837,7 +3904,7 @@ - s->name, count, atomic_long_read(&n->nr_slabs)); - - out: -- spin_unlock_irqrestore(&n->list_lock, flags); -+ raw_spin_unlock_irqrestore(&n->list_lock, flags); - return count; - } - -@@ -4025,12 +4092,12 @@ - if (!atomic_long_read(&n->nr_slabs)) - continue; - -- spin_lock_irqsave(&n->list_lock, flags); -+ raw_spin_lock_irqsave(&n->list_lock, flags); - list_for_each_entry(page, &n->partial, lru) - process_slab(&t, s, page, alloc, map); - list_for_each_entry(page, &n->full, lru) - process_slab(&t, s, page, alloc, map); -- spin_unlock_irqrestore(&n->list_lock, flags); -+ raw_spin_unlock_irqrestore(&n->list_lock, flags); - } - - for (i = 0; i < t.count; i++) { -diff -Nur linux-3.18.14.orig/mm/swap.c linux-3.18.14-rt/mm/swap.c ---- linux-3.18.14.orig/mm/swap.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/swap.c 2015-05-31 15:32:49.285635359 -0500 -@@ -31,6 +31,7 @@ - #include - #include - #include -+#include - - #include "internal.h" - -@@ -44,6 +45,9 @@ - static DEFINE_PER_CPU(struct pagevec, lru_rotate_pvecs); - static DEFINE_PER_CPU(struct pagevec, lru_deactivate_pvecs); - -+static DEFINE_LOCAL_IRQ_LOCK(rotate_lock); -+static DEFINE_LOCAL_IRQ_LOCK(swapvec_lock); -+ - /* - * This path almost never happens for VM activity - pages are normally - * freed via pagevecs. But it gets used by networking. -@@ -473,11 +477,11 @@ - unsigned long flags; - - page_cache_get(page); -- local_irq_save(flags); -+ local_lock_irqsave(rotate_lock, flags); - pvec = this_cpu_ptr(&lru_rotate_pvecs); - if (!pagevec_add(pvec, page)) - pagevec_move_tail(pvec); -- local_irq_restore(flags); -+ local_unlock_irqrestore(rotate_lock, flags); - } - } - -@@ -528,12 +532,13 @@ - void activate_page(struct page *page) - { - if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { -- struct pagevec *pvec = &get_cpu_var(activate_page_pvecs); -+ struct pagevec *pvec = &get_locked_var(swapvec_lock, -+ activate_page_pvecs); - - page_cache_get(page); - if (!pagevec_add(pvec, page)) - pagevec_lru_move_fn(pvec, __activate_page, NULL); -- put_cpu_var(activate_page_pvecs); -+ put_locked_var(swapvec_lock, activate_page_pvecs); - } - } - -@@ -559,7 +564,7 @@ - - static void __lru_cache_activate_page(struct page *page) - { -- struct pagevec *pvec = &get_cpu_var(lru_add_pvec); -+ struct pagevec *pvec = &get_locked_var(swapvec_lock, lru_add_pvec); - int i; - - /* -@@ -581,7 +586,7 @@ - } - } - -- put_cpu_var(lru_add_pvec); -+ put_locked_var(swapvec_lock, lru_add_pvec); - } - - /* -@@ -620,13 +625,13 @@ - - static void __lru_cache_add(struct page *page) - { -- struct pagevec *pvec = &get_cpu_var(lru_add_pvec); -+ struct pagevec *pvec = &get_locked_var(swapvec_lock, lru_add_pvec); - - page_cache_get(page); - if (!pagevec_space(pvec)) - __pagevec_lru_add(pvec); - pagevec_add(pvec, page); -- put_cpu_var(lru_add_pvec); -+ put_locked_var(swapvec_lock, lru_add_pvec); - } - - /** -@@ -806,9 +811,9 @@ - unsigned long flags; - - /* No harm done if a racing interrupt already did this */ -- local_irq_save(flags); -+ local_lock_irqsave(rotate_lock, flags); - pagevec_move_tail(pvec); -- local_irq_restore(flags); -+ local_unlock_irqrestore(rotate_lock, flags); - } - - pvec = &per_cpu(lru_deactivate_pvecs, cpu); -@@ -836,18 +841,19 @@ - return; - - if (likely(get_page_unless_zero(page))) { -- struct pagevec *pvec = &get_cpu_var(lru_deactivate_pvecs); -+ struct pagevec *pvec = &get_locked_var(swapvec_lock, -+ lru_deactivate_pvecs); - - if (!pagevec_add(pvec, page)) - pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL); -- put_cpu_var(lru_deactivate_pvecs); -+ put_locked_var(swapvec_lock, lru_deactivate_pvecs); - } - } - - void lru_add_drain(void) - { -- lru_add_drain_cpu(get_cpu()); -- put_cpu(); -+ lru_add_drain_cpu(local_lock_cpu(swapvec_lock)); -+ local_unlock_cpu(swapvec_lock); - } - - static void lru_add_drain_per_cpu(struct work_struct *dummy) -diff -Nur linux-3.18.14.orig/mm/truncate.c linux-3.18.14-rt/mm/truncate.c ---- linux-3.18.14.orig/mm/truncate.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/truncate.c 2015-05-31 15:32:49.293635359 -0500 -@@ -56,8 +56,11 @@ - * protected by mapping->tree_lock. - */ - if (!workingset_node_shadows(node) && -- !list_empty(&node->private_list)) -- list_lru_del(&workingset_shadow_nodes, &node->private_list); -+ !list_empty(&node->private_list)) { -+ local_lock(workingset_shadow_lock); -+ list_lru_del(&__workingset_shadow_nodes, &node->private_list); -+ local_unlock(workingset_shadow_lock); -+ } - __radix_tree_delete_node(&mapping->page_tree, node); - unlock: - spin_unlock_irq(&mapping->tree_lock); -diff -Nur linux-3.18.14.orig/mm/vmalloc.c linux-3.18.14-rt/mm/vmalloc.c ---- linux-3.18.14.orig/mm/vmalloc.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/vmalloc.c 2015-05-31 15:32:49.297635359 -0500 -@@ -798,7 +798,7 @@ - struct vmap_block *vb; - struct vmap_area *va; - unsigned long vb_idx; -- int node, err; -+ int node, err, cpu; - - node = numa_node_id(); - -@@ -836,11 +836,12 @@ - BUG_ON(err); - radix_tree_preload_end(); - -- vbq = &get_cpu_var(vmap_block_queue); -+ cpu = get_cpu_light(); -+ vbq = &__get_cpu_var(vmap_block_queue); - spin_lock(&vbq->lock); - list_add_rcu(&vb->free_list, &vbq->free); - spin_unlock(&vbq->lock); -- put_cpu_var(vmap_block_queue); -+ put_cpu_light(); - - return vb; - } -@@ -908,6 +909,7 @@ - struct vmap_block *vb; - unsigned long addr = 0; - unsigned int order; -+ int cpu = 0; - - BUG_ON(size & ~PAGE_MASK); - BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC); -@@ -923,7 +925,8 @@ - - again: - rcu_read_lock(); -- vbq = &get_cpu_var(vmap_block_queue); -+ cpu = get_cpu_light(); -+ vbq = &__get_cpu_var(vmap_block_queue); - list_for_each_entry_rcu(vb, &vbq->free, free_list) { - int i; - -@@ -947,7 +950,7 @@ - spin_unlock(&vb->lock); - } - -- put_cpu_var(vmap_block_queue); -+ put_cpu_light(); - rcu_read_unlock(); - - if (!addr) { -diff -Nur linux-3.18.14.orig/mm/vmstat.c linux-3.18.14-rt/mm/vmstat.c ---- linux-3.18.14.orig/mm/vmstat.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/vmstat.c 2015-05-31 15:32:49.297635359 -0500 -@@ -221,6 +221,7 @@ - long x; - long t; - -+ preempt_disable_rt(); - x = delta + __this_cpu_read(*p); - - t = __this_cpu_read(pcp->stat_threshold); -@@ -230,6 +231,7 @@ - x = 0; - } - __this_cpu_write(*p, x); -+ preempt_enable_rt(); - } - EXPORT_SYMBOL(__mod_zone_page_state); - -@@ -262,6 +264,7 @@ - s8 __percpu *p = pcp->vm_stat_diff + item; - s8 v, t; - -+ preempt_disable_rt(); - v = __this_cpu_inc_return(*p); - t = __this_cpu_read(pcp->stat_threshold); - if (unlikely(v > t)) { -@@ -270,6 +273,7 @@ - zone_page_state_add(v + overstep, zone, item); - __this_cpu_write(*p, -overstep); - } -+ preempt_enable_rt(); - } - - void __inc_zone_page_state(struct page *page, enum zone_stat_item item) -@@ -284,6 +288,7 @@ - s8 __percpu *p = pcp->vm_stat_diff + item; - s8 v, t; - -+ preempt_disable_rt(); - v = __this_cpu_dec_return(*p); - t = __this_cpu_read(pcp->stat_threshold); - if (unlikely(v < - t)) { -@@ -292,6 +297,7 @@ - zone_page_state_add(v - overstep, zone, item); - __this_cpu_write(*p, overstep); - } -+ preempt_enable_rt(); - } - - void __dec_zone_page_state(struct page *page, enum zone_stat_item item) -diff -Nur linux-3.18.14.orig/mm/workingset.c linux-3.18.14-rt/mm/workingset.c ---- linux-3.18.14.orig/mm/workingset.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/mm/workingset.c 2015-05-31 15:32:49.321635359 -0500 -@@ -264,7 +264,8 @@ - * point where they would still be useful. - */ - --struct list_lru workingset_shadow_nodes; -+struct list_lru __workingset_shadow_nodes; -+DEFINE_LOCAL_IRQ_LOCK(workingset_shadow_lock); - - static unsigned long count_shadow_nodes(struct shrinker *shrinker, - struct shrink_control *sc) -@@ -274,9 +275,9 @@ - unsigned long pages; - - /* list_lru lock nests inside IRQ-safe mapping->tree_lock */ -- local_irq_disable(); -- shadow_nodes = list_lru_count_node(&workingset_shadow_nodes, sc->nid); -- local_irq_enable(); -+ local_lock_irq(workingset_shadow_lock); -+ shadow_nodes = list_lru_count_node(&__workingset_shadow_nodes, sc->nid); -+ local_unlock_irq(workingset_shadow_lock); - - pages = node_present_pages(sc->nid); - /* -@@ -362,9 +363,9 @@ - spin_unlock(&mapping->tree_lock); - ret = LRU_REMOVED_RETRY; - out: -- local_irq_enable(); -+ local_unlock_irq(workingset_shadow_lock); - cond_resched(); -- local_irq_disable(); -+ local_lock_irq(workingset_shadow_lock); - spin_lock(lru_lock); - return ret; - } -@@ -375,10 +376,10 @@ - unsigned long ret; - - /* list_lru lock nests inside IRQ-safe mapping->tree_lock */ -- local_irq_disable(); -- ret = list_lru_walk_node(&workingset_shadow_nodes, sc->nid, -+ local_lock_irq(workingset_shadow_lock); -+ ret = list_lru_walk_node(&__workingset_shadow_nodes, sc->nid, - shadow_lru_isolate, NULL, &sc->nr_to_scan); -- local_irq_enable(); -+ local_unlock_irq(workingset_shadow_lock); - return ret; - } - -@@ -399,7 +400,7 @@ - { - int ret; - -- ret = list_lru_init_key(&workingset_shadow_nodes, &shadow_nodes_key); -+ ret = list_lru_init_key(&__workingset_shadow_nodes, &shadow_nodes_key); - if (ret) - goto err; - ret = register_shrinker(&workingset_shadow_shrinker); -@@ -407,7 +408,7 @@ - goto err_list_lru; - return 0; - err_list_lru: -- list_lru_destroy(&workingset_shadow_nodes); -+ list_lru_destroy(&__workingset_shadow_nodes); - err: - return ret; - } -diff -Nur linux-3.18.14.orig/net/core/dev.c linux-3.18.14-rt/net/core/dev.c ---- linux-3.18.14.orig/net/core/dev.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/core/dev.c 2015-05-31 15:32:49.369635359 -0500 -@@ -182,6 +182,7 @@ - static DEFINE_HASHTABLE(napi_hash, 8); - - static seqcount_t devnet_rename_seq; -+static DEFINE_MUTEX(devnet_rename_mutex); - - static inline void dev_base_seq_inc(struct net *net) - { -@@ -203,14 +204,14 @@ - static inline void rps_lock(struct softnet_data *sd) - { - #ifdef CONFIG_RPS -- spin_lock(&sd->input_pkt_queue.lock); -+ raw_spin_lock(&sd->input_pkt_queue.raw_lock); - #endif - } - - static inline void rps_unlock(struct softnet_data *sd) - { - #ifdef CONFIG_RPS -- spin_unlock(&sd->input_pkt_queue.lock); -+ raw_spin_unlock(&sd->input_pkt_queue.raw_lock); - #endif - } - -@@ -832,7 +833,8 @@ - strcpy(name, dev->name); - rcu_read_unlock(); - if (read_seqcount_retry(&devnet_rename_seq, seq)) { -- cond_resched(); -+ mutex_lock(&devnet_rename_mutex); -+ mutex_unlock(&devnet_rename_mutex); - goto retry; - } - -@@ -1101,20 +1103,17 @@ - if (dev->flags & IFF_UP) - return -EBUSY; - -- write_seqcount_begin(&devnet_rename_seq); -+ mutex_lock(&devnet_rename_mutex); -+ __raw_write_seqcount_begin(&devnet_rename_seq); - -- if (strncmp(newname, dev->name, IFNAMSIZ) == 0) { -- write_seqcount_end(&devnet_rename_seq); -- return 0; -- } -+ if (strncmp(newname, dev->name, IFNAMSIZ) == 0) -+ goto outunlock; - - memcpy(oldname, dev->name, IFNAMSIZ); - - err = dev_get_valid_name(net, dev, newname); -- if (err < 0) { -- write_seqcount_end(&devnet_rename_seq); -- return err; -- } -+ if (err < 0) -+ goto outunlock; - - if (oldname[0] && !strchr(oldname, '%')) - netdev_info(dev, "renamed from %s\n", oldname); -@@ -1127,11 +1126,12 @@ - if (ret) { - memcpy(dev->name, oldname, IFNAMSIZ); - dev->name_assign_type = old_assign_type; -- write_seqcount_end(&devnet_rename_seq); -- return ret; -+ err = ret; -+ goto outunlock; - } - -- write_seqcount_end(&devnet_rename_seq); -+ __raw_write_seqcount_end(&devnet_rename_seq); -+ mutex_unlock(&devnet_rename_mutex); - - netdev_adjacent_rename_links(dev, oldname); - -@@ -1152,7 +1152,8 @@ - /* err >= 0 after dev_alloc_name() or stores the first errno */ - if (err >= 0) { - err = ret; -- write_seqcount_begin(&devnet_rename_seq); -+ mutex_lock(&devnet_rename_mutex); -+ __raw_write_seqcount_begin(&devnet_rename_seq); - memcpy(dev->name, oldname, IFNAMSIZ); - memcpy(oldname, newname, IFNAMSIZ); - dev->name_assign_type = old_assign_type; -@@ -1165,6 +1166,11 @@ - } - - return err; -+ -+outunlock: -+ __raw_write_seqcount_end(&devnet_rename_seq); -+ mutex_unlock(&devnet_rename_mutex); -+ return err; - } - - /** -@@ -2160,6 +2166,7 @@ - sd->output_queue_tailp = &q->next_sched; - raise_softirq_irqoff(NET_TX_SOFTIRQ); - local_irq_restore(flags); -+ preempt_check_resched_rt(); - } - - void __netif_schedule(struct Qdisc *q) -@@ -2241,6 +2248,7 @@ - __this_cpu_write(softnet_data.completion_queue, skb); - raise_softirq_irqoff(NET_TX_SOFTIRQ); - local_irq_restore(flags); -+ preempt_check_resched_rt(); - } - EXPORT_SYMBOL(__dev_kfree_skb_irq); - -@@ -3334,6 +3342,7 @@ - rps_unlock(sd); - - local_irq_restore(flags); -+ preempt_check_resched_rt(); - - atomic_long_inc(&skb->dev->rx_dropped); - kfree_skb(skb); -@@ -3352,7 +3361,7 @@ - struct rps_dev_flow voidflow, *rflow = &voidflow; - int cpu; - -- preempt_disable(); -+ migrate_disable(); - rcu_read_lock(); - - cpu = get_rps_cpu(skb->dev, skb, &rflow); -@@ -3362,13 +3371,13 @@ - ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail); - - rcu_read_unlock(); -- preempt_enable(); -+ migrate_enable(); - } else - #endif - { - unsigned int qtail; -- ret = enqueue_to_backlog(skb, get_cpu(), &qtail); -- put_cpu(); -+ ret = enqueue_to_backlog(skb, get_cpu_light(), &qtail); -+ put_cpu_light(); - } - return ret; - } -@@ -3402,16 +3411,44 @@ - - trace_netif_rx_ni_entry(skb); - -- preempt_disable(); -+ local_bh_disable(); - err = netif_rx_internal(skb); -- if (local_softirq_pending()) -- do_softirq(); -- preempt_enable(); -+ local_bh_enable(); - - return err; - } - EXPORT_SYMBOL(netif_rx_ni); - -+#ifdef CONFIG_PREEMPT_RT_FULL -+/* -+ * RT runs ksoftirqd as a real time thread and the root_lock is a -+ * "sleeping spinlock". If the trylock fails then we can go into an -+ * infinite loop when ksoftirqd preempted the task which actually -+ * holds the lock, because we requeue q and raise NET_TX softirq -+ * causing ksoftirqd to loop forever. -+ * -+ * It's safe to use spin_lock on RT here as softirqs run in thread -+ * context and cannot deadlock against the thread which is holding -+ * root_lock. -+ * -+ * On !RT the trylock might fail, but there we bail out from the -+ * softirq loop after 10 attempts which we can't do on RT. And the -+ * task holding root_lock cannot be preempted, so the only downside of -+ * that trylock is that we need 10 loops to decide that we should have -+ * given up in the first one :) -+ */ -+static inline int take_root_lock(spinlock_t *lock) -+{ -+ spin_lock(lock); -+ return 1; -+} -+#else -+static inline int take_root_lock(spinlock_t *lock) -+{ -+ return spin_trylock(lock); -+} -+#endif -+ - static void net_tx_action(struct softirq_action *h) - { - struct softnet_data *sd = this_cpu_ptr(&softnet_data); -@@ -3453,7 +3490,7 @@ - head = head->next_sched; - - root_lock = qdisc_lock(q); -- if (spin_trylock(root_lock)) { -+ if (take_root_lock(root_lock)) { - smp_mb__before_atomic(); - clear_bit(__QDISC_STATE_SCHED, - &q->state); -@@ -3846,7 +3883,7 @@ - skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) { - if (skb->dev == dev) { - __skb_unlink(skb, &sd->input_pkt_queue); -- kfree_skb(skb); -+ __skb_queue_tail(&sd->tofree_queue, skb); - input_queue_head_incr(sd); - } - } -@@ -3855,10 +3892,13 @@ - skb_queue_walk_safe(&sd->process_queue, skb, tmp) { - if (skb->dev == dev) { - __skb_unlink(skb, &sd->process_queue); -- kfree_skb(skb); -+ __skb_queue_tail(&sd->tofree_queue, skb); - input_queue_head_incr(sd); - } - } -+ -+ if (!skb_queue_empty(&sd->tofree_queue)) -+ raise_softirq_irqoff(NET_RX_SOFTIRQ); - } - - static int napi_gro_complete(struct sk_buff *skb) -@@ -4321,6 +4361,7 @@ - } else - #endif - local_irq_enable(); -+ preempt_check_resched_rt(); - } - - static int process_backlog(struct napi_struct *napi, int quota) -@@ -4392,6 +4433,7 @@ - local_irq_save(flags); - ____napi_schedule(this_cpu_ptr(&softnet_data), n); - local_irq_restore(flags); -+ preempt_check_resched_rt(); - } - EXPORT_SYMBOL(__napi_schedule); - -@@ -4514,10 +4556,17 @@ - struct softnet_data *sd = this_cpu_ptr(&softnet_data); - unsigned long time_limit = jiffies + 2; - int budget = netdev_budget; -+ struct sk_buff *skb; - void *have; - - local_irq_disable(); - -+ while ((skb = __skb_dequeue(&sd->tofree_queue))) { -+ local_irq_enable(); -+ kfree_skb(skb); -+ local_irq_disable(); -+ } -+ - while (!list_empty(&sd->poll_list)) { - struct napi_struct *n; - int work, weight; -@@ -7006,6 +7055,7 @@ - - raise_softirq_irqoff(NET_TX_SOFTIRQ); - local_irq_enable(); -+ preempt_check_resched_rt(); - - /* Process offline CPU's input_pkt_queue */ - while ((skb = __skb_dequeue(&oldsd->process_queue))) { -@@ -7016,6 +7066,9 @@ - netif_rx_internal(skb); - input_queue_head_incr(oldsd); - } -+ while ((skb = __skb_dequeue(&oldsd->tofree_queue))) { -+ kfree_skb(skb); -+ } - - return NOTIFY_OK; - } -@@ -7317,8 +7370,9 @@ - for_each_possible_cpu(i) { - struct softnet_data *sd = &per_cpu(softnet_data, i); - -- skb_queue_head_init(&sd->input_pkt_queue); -- skb_queue_head_init(&sd->process_queue); -+ skb_queue_head_init_raw(&sd->input_pkt_queue); -+ skb_queue_head_init_raw(&sd->process_queue); -+ skb_queue_head_init_raw(&sd->tofree_queue); - INIT_LIST_HEAD(&sd->poll_list); - sd->output_queue_tailp = &sd->output_queue; - #ifdef CONFIG_RPS -diff -Nur linux-3.18.14.orig/net/core/skbuff.c linux-3.18.14-rt/net/core/skbuff.c ---- linux-3.18.14.orig/net/core/skbuff.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/core/skbuff.c 2015-05-31 15:32:49.393635358 -0500 -@@ -63,6 +63,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -353,6 +354,7 @@ - unsigned int pagecnt_bias; - }; - static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache); -+static DEFINE_LOCAL_IRQ_LOCK(netdev_alloc_lock); - - static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) - { -@@ -361,7 +363,7 @@ - int order; - unsigned long flags; - -- local_irq_save(flags); -+ local_lock_irqsave(netdev_alloc_lock, flags); - nc = this_cpu_ptr(&netdev_alloc_cache); - if (unlikely(!nc->frag.page)) { - refill: -@@ -407,7 +409,7 @@ - nc->frag.offset += fragsz; - nc->pagecnt_bias--; - end: -- local_irq_restore(flags); -+ local_unlock_irqrestore(netdev_alloc_lock, flags); - return data; - } - -diff -Nur linux-3.18.14.orig/net/core/skbuff.c.orig linux-3.18.14-rt/net/core/skbuff.c.orig ---- linux-3.18.14.orig/net/core/skbuff.c.orig 1969-12-31 18:00:00.000000000 -0600 -+++ linux-3.18.14-rt/net/core/skbuff.c.orig 2015-05-20 10:04:50.000000000 -0500 -@@ -0,0 +1,4231 @@ -+/* -+ * Routines having to do with the 'struct sk_buff' memory handlers. -+ * -+ * Authors: Alan Cox -+ * Florian La Roche -+ * -+ * Fixes: -+ * Alan Cox : Fixed the worst of the load -+ * balancer bugs. -+ * Dave Platt : Interrupt stacking fix. -+ * Richard Kooijman : Timestamp fixes. -+ * Alan Cox : Changed buffer format. -+ * Alan Cox : destructor hook for AF_UNIX etc. -+ * Linus Torvalds : Better skb_clone. -+ * Alan Cox : Added skb_copy. -+ * Alan Cox : Added all the changed routines Linus -+ * only put in the headers -+ * Ray VanTassle : Fixed --skb->lock in free -+ * Alan Cox : skb_copy copy arp field -+ * Andi Kleen : slabified it. -+ * Robert Olsson : Removed skb_head_pool -+ * -+ * NOTE: -+ * The __skb_ routines should be called with interrupts -+ * disabled, or you better be *real* sure that the operation is atomic -+ * with respect to whatever list is being frobbed (e.g. via lock_sock() -+ * or via disabling bottom half handlers, etc). -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * The functions in this file will not compile correctly with gcc 2.4.x -+ */ -+ -+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#ifdef CONFIG_NET_CLS_ACT -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+struct kmem_cache *skbuff_head_cache __read_mostly; -+static struct kmem_cache *skbuff_fclone_cache __read_mostly; -+ -+/** -+ * skb_panic - private function for out-of-line support -+ * @skb: buffer -+ * @sz: size -+ * @addr: address -+ * @msg: skb_over_panic or skb_under_panic -+ * -+ * Out-of-line support for skb_put() and skb_push(). -+ * Called via the wrapper skb_over_panic() or skb_under_panic(). -+ * Keep out of line to prevent kernel bloat. -+ * __builtin_return_address is not used because it is not always reliable. -+ */ -+static void skb_panic(struct sk_buff *skb, unsigned int sz, void *addr, -+ const char msg[]) -+{ -+ pr_emerg("%s: text:%p len:%d put:%d head:%p data:%p tail:%#lx end:%#lx dev:%s\n", -+ msg, addr, skb->len, sz, skb->head, skb->data, -+ (unsigned long)skb->tail, (unsigned long)skb->end, -+ skb->dev ? skb->dev->name : ""); -+ BUG(); -+} -+ -+static void skb_over_panic(struct sk_buff *skb, unsigned int sz, void *addr) -+{ -+ skb_panic(skb, sz, addr, __func__); -+} -+ -+static void skb_under_panic(struct sk_buff *skb, unsigned int sz, void *addr) -+{ -+ skb_panic(skb, sz, addr, __func__); -+} -+ -+/* -+ * kmalloc_reserve is a wrapper around kmalloc_node_track_caller that tells -+ * the caller if emergency pfmemalloc reserves are being used. If it is and -+ * the socket is later found to be SOCK_MEMALLOC then PFMEMALLOC reserves -+ * may be used. Otherwise, the packet data may be discarded until enough -+ * memory is free -+ */ -+#define kmalloc_reserve(size, gfp, node, pfmemalloc) \ -+ __kmalloc_reserve(size, gfp, node, _RET_IP_, pfmemalloc) -+ -+static void *__kmalloc_reserve(size_t size, gfp_t flags, int node, -+ unsigned long ip, bool *pfmemalloc) -+{ -+ void *obj; -+ bool ret_pfmemalloc = false; -+ -+ /* -+ * Try a regular allocation, when that fails and we're not entitled -+ * to the reserves, fail. -+ */ -+ obj = kmalloc_node_track_caller(size, -+ flags | __GFP_NOMEMALLOC | __GFP_NOWARN, -+ node); -+ if (obj || !(gfp_pfmemalloc_allowed(flags))) -+ goto out; -+ -+ /* Try again but now we are using pfmemalloc reserves */ -+ ret_pfmemalloc = true; -+ obj = kmalloc_node_track_caller(size, flags, node); -+ -+out: -+ if (pfmemalloc) -+ *pfmemalloc = ret_pfmemalloc; -+ -+ return obj; -+} -+ -+/* Allocate a new skbuff. We do this ourselves so we can fill in a few -+ * 'private' fields and also do memory statistics to find all the -+ * [BEEP] leaks. -+ * -+ */ -+ -+struct sk_buff *__alloc_skb_head(gfp_t gfp_mask, int node) -+{ -+ struct sk_buff *skb; -+ -+ /* Get the HEAD */ -+ skb = kmem_cache_alloc_node(skbuff_head_cache, -+ gfp_mask & ~__GFP_DMA, node); -+ if (!skb) -+ goto out; -+ -+ /* -+ * Only clear those fields we need to clear, not those that we will -+ * actually initialise below. Hence, don't put any more fields after -+ * the tail pointer in struct sk_buff! -+ */ -+ memset(skb, 0, offsetof(struct sk_buff, tail)); -+ skb->head = NULL; -+ skb->truesize = sizeof(struct sk_buff); -+ atomic_set(&skb->users, 1); -+ -+ skb->mac_header = (typeof(skb->mac_header))~0U; -+out: -+ return skb; -+} -+ -+/** -+ * __alloc_skb - allocate a network buffer -+ * @size: size to allocate -+ * @gfp_mask: allocation mask -+ * @flags: If SKB_ALLOC_FCLONE is set, allocate from fclone cache -+ * instead of head cache and allocate a cloned (child) skb. -+ * If SKB_ALLOC_RX is set, __GFP_MEMALLOC will be used for -+ * allocations in case the data is required for writeback -+ * @node: numa node to allocate memory on -+ * -+ * Allocate a new &sk_buff. The returned buffer has no headroom and a -+ * tail room of at least size bytes. The object has a reference count -+ * of one. The return is the buffer. On a failure the return is %NULL. -+ * -+ * Buffers may only be allocated from interrupts using a @gfp_mask of -+ * %GFP_ATOMIC. -+ */ -+struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, -+ int flags, int node) -+{ -+ struct kmem_cache *cache; -+ struct skb_shared_info *shinfo; -+ struct sk_buff *skb; -+ u8 *data; -+ bool pfmemalloc; -+ -+ cache = (flags & SKB_ALLOC_FCLONE) -+ ? skbuff_fclone_cache : skbuff_head_cache; -+ -+ if (sk_memalloc_socks() && (flags & SKB_ALLOC_RX)) -+ gfp_mask |= __GFP_MEMALLOC; -+ -+ /* Get the HEAD */ -+ skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); -+ if (!skb) -+ goto out; -+ prefetchw(skb); -+ -+ /* We do our best to align skb_shared_info on a separate cache -+ * line. It usually works because kmalloc(X > SMP_CACHE_BYTES) gives -+ * aligned memory blocks, unless SLUB/SLAB debug is enabled. -+ * Both skb->head and skb_shared_info are cache line aligned. -+ */ -+ size = SKB_DATA_ALIGN(size); -+ size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); -+ data = kmalloc_reserve(size, gfp_mask, node, &pfmemalloc); -+ if (!data) -+ goto nodata; -+ /* kmalloc(size) might give us more room than requested. -+ * Put skb_shared_info exactly at the end of allocated zone, -+ * to allow max possible filling before reallocation. -+ */ -+ size = SKB_WITH_OVERHEAD(ksize(data)); -+ prefetchw(data + size); -+ -+ /* -+ * Only clear those fields we need to clear, not those that we will -+ * actually initialise below. Hence, don't put any more fields after -+ * the tail pointer in struct sk_buff! -+ */ -+ memset(skb, 0, offsetof(struct sk_buff, tail)); -+ /* Account for allocated memory : skb + skb->head */ -+ skb->truesize = SKB_TRUESIZE(size); -+ skb->pfmemalloc = pfmemalloc; -+ atomic_set(&skb->users, 1); -+ skb->head = data; -+ skb->data = data; -+ skb_reset_tail_pointer(skb); -+ skb->end = skb->tail + size; -+ skb->mac_header = (typeof(skb->mac_header))~0U; -+ skb->transport_header = (typeof(skb->transport_header))~0U; -+ -+ /* make sure we initialize shinfo sequentially */ -+ shinfo = skb_shinfo(skb); -+ memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); -+ atomic_set(&shinfo->dataref, 1); -+ kmemcheck_annotate_variable(shinfo->destructor_arg); -+ -+ if (flags & SKB_ALLOC_FCLONE) { -+ struct sk_buff_fclones *fclones; -+ -+ fclones = container_of(skb, struct sk_buff_fclones, skb1); -+ -+ kmemcheck_annotate_bitfield(&fclones->skb2, flags1); -+ skb->fclone = SKB_FCLONE_ORIG; -+ atomic_set(&fclones->fclone_ref, 1); -+ -+ fclones->skb2.fclone = SKB_FCLONE_FREE; -+ fclones->skb2.pfmemalloc = pfmemalloc; -+ } -+out: -+ return skb; -+nodata: -+ kmem_cache_free(cache, skb); -+ skb = NULL; -+ goto out; -+} -+EXPORT_SYMBOL(__alloc_skb); -+ -+/** -+ * __build_skb - build a network buffer -+ * @data: data buffer provided by caller -+ * @frag_size: size of data, or 0 if head was kmalloced -+ * -+ * Allocate a new &sk_buff. Caller provides space holding head and -+ * skb_shared_info. @data must have been allocated by kmalloc() only if -+ * @frag_size is 0, otherwise data should come from the page allocator -+ * or vmalloc() -+ * The return is the new skb buffer. -+ * On a failure the return is %NULL, and @data is not freed. -+ * Notes : -+ * Before IO, driver allocates only data buffer where NIC put incoming frame -+ * Driver should add room at head (NET_SKB_PAD) and -+ * MUST add room at tail (SKB_DATA_ALIGN(skb_shared_info)) -+ * After IO, driver calls build_skb(), to allocate sk_buff and populate it -+ * before giving packet to stack. -+ * RX rings only contains data buffers, not full skbs. -+ */ -+struct sk_buff *__build_skb(void *data, unsigned int frag_size) -+{ -+ struct skb_shared_info *shinfo; -+ struct sk_buff *skb; -+ unsigned int size = frag_size ? : ksize(data); -+ -+ skb = kmem_cache_alloc(skbuff_head_cache, GFP_ATOMIC); -+ if (!skb) -+ return NULL; -+ -+ size -= SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); -+ -+ memset(skb, 0, offsetof(struct sk_buff, tail)); -+ skb->truesize = SKB_TRUESIZE(size); -+ atomic_set(&skb->users, 1); -+ skb->head = data; -+ skb->data = data; -+ skb_reset_tail_pointer(skb); -+ skb->end = skb->tail + size; -+ skb->mac_header = (typeof(skb->mac_header))~0U; -+ skb->transport_header = (typeof(skb->transport_header))~0U; -+ -+ /* make sure we initialize shinfo sequentially */ -+ shinfo = skb_shinfo(skb); -+ memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); -+ atomic_set(&shinfo->dataref, 1); -+ kmemcheck_annotate_variable(shinfo->destructor_arg); -+ -+ return skb; -+} -+ -+/* build_skb() is wrapper over __build_skb(), that specifically -+ * takes care of skb->head and skb->pfmemalloc -+ * This means that if @frag_size is not zero, then @data must be backed -+ * by a page fragment, not kmalloc() or vmalloc() -+ */ -+struct sk_buff *build_skb(void *data, unsigned int frag_size) -+{ -+ struct sk_buff *skb = __build_skb(data, frag_size); -+ -+ if (skb && frag_size) { -+ skb->head_frag = 1; -+ if (virt_to_head_page(data)->pfmemalloc) -+ skb->pfmemalloc = 1; -+ } -+ return skb; -+} -+EXPORT_SYMBOL(build_skb); -+ -+struct netdev_alloc_cache { -+ struct page_frag frag; -+ /* we maintain a pagecount bias, so that we dont dirty cache line -+ * containing page->_count every time we allocate a fragment. -+ */ -+ unsigned int pagecnt_bias; -+}; -+static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache); -+ -+static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) -+{ -+ struct netdev_alloc_cache *nc; -+ void *data = NULL; -+ int order; -+ unsigned long flags; -+ -+ local_irq_save(flags); -+ nc = this_cpu_ptr(&netdev_alloc_cache); -+ if (unlikely(!nc->frag.page)) { -+refill: -+ for (order = NETDEV_FRAG_PAGE_MAX_ORDER; ;) { -+ gfp_t gfp = gfp_mask; -+ -+ if (order) -+ gfp |= __GFP_COMP | __GFP_NOWARN | -+ __GFP_NOMEMALLOC; -+ nc->frag.page = alloc_pages(gfp, order); -+ if (likely(nc->frag.page)) -+ break; -+ if (--order < 0) -+ goto end; -+ } -+ nc->frag.size = PAGE_SIZE << order; -+ /* Even if we own the page, we do not use atomic_set(). -+ * This would break get_page_unless_zero() users. -+ */ -+ atomic_add(NETDEV_PAGECNT_MAX_BIAS - 1, -+ &nc->frag.page->_count); -+ nc->pagecnt_bias = NETDEV_PAGECNT_MAX_BIAS; -+ nc->frag.offset = 0; -+ } -+ -+ if (nc->frag.offset + fragsz > nc->frag.size) { -+ if (atomic_read(&nc->frag.page->_count) != nc->pagecnt_bias) { -+ if (!atomic_sub_and_test(nc->pagecnt_bias, -+ &nc->frag.page->_count)) -+ goto refill; -+ /* OK, page count is 0, we can safely set it */ -+ atomic_set(&nc->frag.page->_count, -+ NETDEV_PAGECNT_MAX_BIAS); -+ } else { -+ atomic_add(NETDEV_PAGECNT_MAX_BIAS - nc->pagecnt_bias, -+ &nc->frag.page->_count); -+ } -+ nc->pagecnt_bias = NETDEV_PAGECNT_MAX_BIAS; -+ nc->frag.offset = 0; -+ } -+ -+ data = page_address(nc->frag.page) + nc->frag.offset; -+ nc->frag.offset += fragsz; -+ nc->pagecnt_bias--; -+end: -+ local_irq_restore(flags); -+ return data; -+} -+ -+/** -+ * netdev_alloc_frag - allocate a page fragment -+ * @fragsz: fragment size -+ * -+ * Allocates a frag from a page for receive buffer. -+ * Uses GFP_ATOMIC allocations. -+ */ -+void *netdev_alloc_frag(unsigned int fragsz) -+{ -+ return __netdev_alloc_frag(fragsz, GFP_ATOMIC | __GFP_COLD); -+} -+EXPORT_SYMBOL(netdev_alloc_frag); -+ -+/** -+ * __netdev_alloc_skb - allocate an skbuff for rx on a specific device -+ * @dev: network device to receive on -+ * @length: length to allocate -+ * @gfp_mask: get_free_pages mask, passed to alloc_skb -+ * -+ * Allocate a new &sk_buff and assign it a usage count of one. The -+ * buffer has unspecified headroom built in. Users should allocate -+ * the headroom they think they need without accounting for the -+ * built in space. The built in space is used for optimisations. -+ * -+ * %NULL is returned if there is no free memory. -+ */ -+struct sk_buff *__netdev_alloc_skb(struct net_device *dev, -+ unsigned int length, gfp_t gfp_mask) -+{ -+ struct sk_buff *skb = NULL; -+ unsigned int fragsz = SKB_DATA_ALIGN(length + NET_SKB_PAD) + -+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); -+ -+ if (fragsz <= PAGE_SIZE && !(gfp_mask & (__GFP_WAIT | GFP_DMA))) { -+ void *data; -+ -+ if (sk_memalloc_socks()) -+ gfp_mask |= __GFP_MEMALLOC; -+ -+ data = __netdev_alloc_frag(fragsz, gfp_mask); -+ -+ if (likely(data)) { -+ skb = build_skb(data, fragsz); -+ if (unlikely(!skb)) -+ put_page(virt_to_head_page(data)); -+ } -+ } else { -+ skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, -+ SKB_ALLOC_RX, NUMA_NO_NODE); -+ } -+ if (likely(skb)) { -+ skb_reserve(skb, NET_SKB_PAD); -+ skb->dev = dev; -+ } -+ return skb; -+} -+EXPORT_SYMBOL(__netdev_alloc_skb); -+ -+void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, -+ int size, unsigned int truesize) -+{ -+ skb_fill_page_desc(skb, i, page, off, size); -+ skb->len += size; -+ skb->data_len += size; -+ skb->truesize += truesize; -+} -+EXPORT_SYMBOL(skb_add_rx_frag); -+ -+void skb_coalesce_rx_frag(struct sk_buff *skb, int i, int size, -+ unsigned int truesize) -+{ -+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; -+ -+ skb_frag_size_add(frag, size); -+ skb->len += size; -+ skb->data_len += size; -+ skb->truesize += truesize; -+} -+EXPORT_SYMBOL(skb_coalesce_rx_frag); -+ -+static void skb_drop_list(struct sk_buff **listp) -+{ -+ kfree_skb_list(*listp); -+ *listp = NULL; -+} -+ -+static inline void skb_drop_fraglist(struct sk_buff *skb) -+{ -+ skb_drop_list(&skb_shinfo(skb)->frag_list); -+} -+ -+static void skb_clone_fraglist(struct sk_buff *skb) -+{ -+ struct sk_buff *list; -+ -+ skb_walk_frags(skb, list) -+ skb_get(list); -+} -+ -+static void skb_free_head(struct sk_buff *skb) -+{ -+ if (skb->head_frag) -+ put_page(virt_to_head_page(skb->head)); -+ else -+ kfree(skb->head); -+} -+ -+static void skb_release_data(struct sk_buff *skb) -+{ -+ struct skb_shared_info *shinfo = skb_shinfo(skb); -+ int i; -+ -+ if (skb->cloned && -+ atomic_sub_return(skb->nohdr ? (1 << SKB_DATAREF_SHIFT) + 1 : 1, -+ &shinfo->dataref)) -+ return; -+ -+ for (i = 0; i < shinfo->nr_frags; i++) -+ __skb_frag_unref(&shinfo->frags[i]); -+ -+ /* -+ * If skb buf is from userspace, we need to notify the caller -+ * the lower device DMA has done; -+ */ -+ if (shinfo->tx_flags & SKBTX_DEV_ZEROCOPY) { -+ struct ubuf_info *uarg; -+ -+ uarg = shinfo->destructor_arg; -+ if (uarg->callback) -+ uarg->callback(uarg, true); -+ } -+ -+ if (shinfo->frag_list) -+ kfree_skb_list(shinfo->frag_list); -+ -+ skb_free_head(skb); -+} -+ -+/* -+ * Free an skbuff by memory without cleaning the state. -+ */ -+static void kfree_skbmem(struct sk_buff *skb) -+{ -+ struct sk_buff_fclones *fclones; -+ -+ switch (skb->fclone) { -+ case SKB_FCLONE_UNAVAILABLE: -+ kmem_cache_free(skbuff_head_cache, skb); -+ break; -+ -+ case SKB_FCLONE_ORIG: -+ fclones = container_of(skb, struct sk_buff_fclones, skb1); -+ if (atomic_dec_and_test(&fclones->fclone_ref)) -+ kmem_cache_free(skbuff_fclone_cache, fclones); -+ break; -+ -+ case SKB_FCLONE_CLONE: -+ fclones = container_of(skb, struct sk_buff_fclones, skb2); -+ -+ /* The clone portion is available for -+ * fast-cloning again. -+ */ -+ skb->fclone = SKB_FCLONE_FREE; -+ -+ if (atomic_dec_and_test(&fclones->fclone_ref)) -+ kmem_cache_free(skbuff_fclone_cache, fclones); -+ break; -+ } -+} -+ -+static void skb_release_head_state(struct sk_buff *skb) -+{ -+ skb_dst_drop(skb); -+#ifdef CONFIG_XFRM -+ secpath_put(skb->sp); -+#endif -+ if (skb->destructor) { -+ WARN_ON(in_irq()); -+ skb->destructor(skb); -+ } -+#if IS_ENABLED(CONFIG_NF_CONNTRACK) -+ nf_conntrack_put(skb->nfct); -+#endif -+#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) -+ nf_bridge_put(skb->nf_bridge); -+#endif -+/* XXX: IS this still necessary? - JHS */ -+#ifdef CONFIG_NET_SCHED -+ skb->tc_index = 0; -+#ifdef CONFIG_NET_CLS_ACT -+ skb->tc_verd = 0; -+#endif -+#endif -+} -+ -+/* Free everything but the sk_buff shell. */ -+static void skb_release_all(struct sk_buff *skb) -+{ -+ skb_release_head_state(skb); -+ if (likely(skb->head)) -+ skb_release_data(skb); -+} -+ -+/** -+ * __kfree_skb - private function -+ * @skb: buffer -+ * -+ * Free an sk_buff. Release anything attached to the buffer. -+ * Clean the state. This is an internal helper function. Users should -+ * always call kfree_skb -+ */ -+ -+void __kfree_skb(struct sk_buff *skb) -+{ -+ skb_release_all(skb); -+ kfree_skbmem(skb); -+} -+EXPORT_SYMBOL(__kfree_skb); -+ -+/** -+ * kfree_skb - free an sk_buff -+ * @skb: buffer to free -+ * -+ * Drop a reference to the buffer and free it if the usage count has -+ * hit zero. -+ */ -+void kfree_skb(struct sk_buff *skb) -+{ -+ if (unlikely(!skb)) -+ return; -+ if (likely(atomic_read(&skb->users) == 1)) -+ smp_rmb(); -+ else if (likely(!atomic_dec_and_test(&skb->users))) -+ return; -+ trace_kfree_skb(skb, __builtin_return_address(0)); -+ __kfree_skb(skb); -+} -+EXPORT_SYMBOL(kfree_skb); -+ -+void kfree_skb_list(struct sk_buff *segs) -+{ -+ while (segs) { -+ struct sk_buff *next = segs->next; -+ -+ kfree_skb(segs); -+ segs = next; -+ } -+} -+EXPORT_SYMBOL(kfree_skb_list); -+ -+/** -+ * skb_tx_error - report an sk_buff xmit error -+ * @skb: buffer that triggered an error -+ * -+ * Report xmit error if a device callback is tracking this skb. -+ * skb must be freed afterwards. -+ */ -+void skb_tx_error(struct sk_buff *skb) -+{ -+ if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) { -+ struct ubuf_info *uarg; -+ -+ uarg = skb_shinfo(skb)->destructor_arg; -+ if (uarg->callback) -+ uarg->callback(uarg, false); -+ skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY; -+ } -+} -+EXPORT_SYMBOL(skb_tx_error); -+ -+/** -+ * consume_skb - free an skbuff -+ * @skb: buffer to free -+ * -+ * Drop a ref to the buffer and free it if the usage count has hit zero -+ * Functions identically to kfree_skb, but kfree_skb assumes that the frame -+ * is being dropped after a failure and notes that -+ */ -+void consume_skb(struct sk_buff *skb) -+{ -+ if (unlikely(!skb)) -+ return; -+ if (likely(atomic_read(&skb->users) == 1)) -+ smp_rmb(); -+ else if (likely(!atomic_dec_and_test(&skb->users))) -+ return; -+ trace_consume_skb(skb); -+ __kfree_skb(skb); -+} -+EXPORT_SYMBOL(consume_skb); -+ -+/* Make sure a field is enclosed inside headers_start/headers_end section */ -+#define CHECK_SKB_FIELD(field) \ -+ BUILD_BUG_ON(offsetof(struct sk_buff, field) < \ -+ offsetof(struct sk_buff, headers_start)); \ -+ BUILD_BUG_ON(offsetof(struct sk_buff, field) > \ -+ offsetof(struct sk_buff, headers_end)); \ -+ -+static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) -+{ -+ new->tstamp = old->tstamp; -+ /* We do not copy old->sk */ -+ new->dev = old->dev; -+ memcpy(new->cb, old->cb, sizeof(old->cb)); -+ skb_dst_copy(new, old); -+#ifdef CONFIG_XFRM -+ new->sp = secpath_get(old->sp); -+#endif -+ __nf_copy(new, old, false); -+ -+ /* Note : this field could be in headers_start/headers_end section -+ * It is not yet because we do not want to have a 16 bit hole -+ */ -+ new->queue_mapping = old->queue_mapping; -+ -+ memcpy(&new->headers_start, &old->headers_start, -+ offsetof(struct sk_buff, headers_end) - -+ offsetof(struct sk_buff, headers_start)); -+ CHECK_SKB_FIELD(protocol); -+ CHECK_SKB_FIELD(csum); -+ CHECK_SKB_FIELD(hash); -+ CHECK_SKB_FIELD(priority); -+ CHECK_SKB_FIELD(skb_iif); -+ CHECK_SKB_FIELD(vlan_proto); -+ CHECK_SKB_FIELD(vlan_tci); -+ CHECK_SKB_FIELD(transport_header); -+ CHECK_SKB_FIELD(network_header); -+ CHECK_SKB_FIELD(mac_header); -+ CHECK_SKB_FIELD(inner_protocol); -+ CHECK_SKB_FIELD(inner_transport_header); -+ CHECK_SKB_FIELD(inner_network_header); -+ CHECK_SKB_FIELD(inner_mac_header); -+ CHECK_SKB_FIELD(mark); -+#ifdef CONFIG_NETWORK_SECMARK -+ CHECK_SKB_FIELD(secmark); -+#endif -+#ifdef CONFIG_NET_RX_BUSY_POLL -+ CHECK_SKB_FIELD(napi_id); -+#endif -+#ifdef CONFIG_NET_SCHED -+ CHECK_SKB_FIELD(tc_index); -+#ifdef CONFIG_NET_CLS_ACT -+ CHECK_SKB_FIELD(tc_verd); -+#endif -+#endif -+ -+} -+ -+/* -+ * You should not add any new code to this function. Add it to -+ * __copy_skb_header above instead. -+ */ -+static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb) -+{ -+#define C(x) n->x = skb->x -+ -+ n->next = n->prev = NULL; -+ n->sk = NULL; -+ __copy_skb_header(n, skb); -+ -+ C(len); -+ C(data_len); -+ C(mac_len); -+ n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len; -+ n->cloned = 1; -+ n->nohdr = 0; -+ n->destructor = NULL; -+ C(tail); -+ C(end); -+ C(head); -+ C(head_frag); -+ C(data); -+ C(truesize); -+ atomic_set(&n->users, 1); -+ -+ atomic_inc(&(skb_shinfo(skb)->dataref)); -+ skb->cloned = 1; -+ -+ return n; -+#undef C -+} -+ -+/** -+ * skb_morph - morph one skb into another -+ * @dst: the skb to receive the contents -+ * @src: the skb to supply the contents -+ * -+ * This is identical to skb_clone except that the target skb is -+ * supplied by the user. -+ * -+ * The target skb is returned upon exit. -+ */ -+struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src) -+{ -+ skb_release_all(dst); -+ return __skb_clone(dst, src); -+} -+EXPORT_SYMBOL_GPL(skb_morph); -+ -+/** -+ * skb_copy_ubufs - copy userspace skb frags buffers to kernel -+ * @skb: the skb to modify -+ * @gfp_mask: allocation priority -+ * -+ * This must be called on SKBTX_DEV_ZEROCOPY skb. -+ * It will copy all frags into kernel and drop the reference -+ * to userspace pages. -+ * -+ * If this function is called from an interrupt gfp_mask() must be -+ * %GFP_ATOMIC. -+ * -+ * Returns 0 on success or a negative error code on failure -+ * to allocate kernel memory to copy to. -+ */ -+int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) -+{ -+ int i; -+ int num_frags = skb_shinfo(skb)->nr_frags; -+ struct page *page, *head = NULL; -+ struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg; -+ -+ for (i = 0; i < num_frags; i++) { -+ u8 *vaddr; -+ skb_frag_t *f = &skb_shinfo(skb)->frags[i]; -+ -+ page = alloc_page(gfp_mask); -+ if (!page) { -+ while (head) { -+ struct page *next = (struct page *)page_private(head); -+ put_page(head); -+ head = next; -+ } -+ return -ENOMEM; -+ } -+ vaddr = kmap_atomic(skb_frag_page(f)); -+ memcpy(page_address(page), -+ vaddr + f->page_offset, skb_frag_size(f)); -+ kunmap_atomic(vaddr); -+ set_page_private(page, (unsigned long)head); -+ head = page; -+ } -+ -+ /* skb frags release userspace buffers */ -+ for (i = 0; i < num_frags; i++) -+ skb_frag_unref(skb, i); -+ -+ uarg->callback(uarg, false); -+ -+ /* skb frags point to kernel buffers */ -+ for (i = num_frags - 1; i >= 0; i--) { -+ __skb_fill_page_desc(skb, i, head, 0, -+ skb_shinfo(skb)->frags[i].size); -+ head = (struct page *)page_private(head); -+ } -+ -+ skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY; -+ return 0; -+} -+EXPORT_SYMBOL_GPL(skb_copy_ubufs); -+ -+/** -+ * skb_clone - duplicate an sk_buff -+ * @skb: buffer to clone -+ * @gfp_mask: allocation priority -+ * -+ * Duplicate an &sk_buff. The new one is not owned by a socket. Both -+ * copies share the same packet data but not structure. The new -+ * buffer has a reference count of 1. If the allocation fails the -+ * function returns %NULL otherwise the new buffer is returned. -+ * -+ * If this function is called from an interrupt gfp_mask() must be -+ * %GFP_ATOMIC. -+ */ -+ -+struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) -+{ -+ struct sk_buff_fclones *fclones = container_of(skb, -+ struct sk_buff_fclones, -+ skb1); -+ struct sk_buff *n = &fclones->skb2; -+ -+ if (skb_orphan_frags(skb, gfp_mask)) -+ return NULL; -+ -+ if (skb->fclone == SKB_FCLONE_ORIG && -+ n->fclone == SKB_FCLONE_FREE) { -+ n->fclone = SKB_FCLONE_CLONE; -+ atomic_inc(&fclones->fclone_ref); -+ } else { -+ if (skb_pfmemalloc(skb)) -+ gfp_mask |= __GFP_MEMALLOC; -+ -+ n = kmem_cache_alloc(skbuff_head_cache, gfp_mask); -+ if (!n) -+ return NULL; -+ -+ kmemcheck_annotate_bitfield(n, flags1); -+ n->fclone = SKB_FCLONE_UNAVAILABLE; -+ } -+ -+ return __skb_clone(n, skb); -+} -+EXPORT_SYMBOL(skb_clone); -+ -+static void skb_headers_offset_update(struct sk_buff *skb, int off) -+{ -+ /* Only adjust this if it actually is csum_start rather than csum */ -+ if (skb->ip_summed == CHECKSUM_PARTIAL) -+ skb->csum_start += off; -+ /* {transport,network,mac}_header and tail are relative to skb->head */ -+ skb->transport_header += off; -+ skb->network_header += off; -+ if (skb_mac_header_was_set(skb)) -+ skb->mac_header += off; -+ skb->inner_transport_header += off; -+ skb->inner_network_header += off; -+ skb->inner_mac_header += off; -+} -+ -+static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) -+{ -+ __copy_skb_header(new, old); -+ -+ skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size; -+ skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; -+ skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; -+} -+ -+static inline int skb_alloc_rx_flag(const struct sk_buff *skb) -+{ -+ if (skb_pfmemalloc(skb)) -+ return SKB_ALLOC_RX; -+ return 0; -+} -+ -+/** -+ * skb_copy - create private copy of an sk_buff -+ * @skb: buffer to copy -+ * @gfp_mask: allocation priority -+ * -+ * Make a copy of both an &sk_buff and its data. This is used when the -+ * caller wishes to modify the data and needs a private copy of the -+ * data to alter. Returns %NULL on failure or the pointer to the buffer -+ * on success. The returned buffer has a reference count of 1. -+ * -+ * As by-product this function converts non-linear &sk_buff to linear -+ * one, so that &sk_buff becomes completely private and caller is allowed -+ * to modify all the data of returned buffer. This means that this -+ * function is not recommended for use in circumstances when only -+ * header is going to be modified. Use pskb_copy() instead. -+ */ -+ -+struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask) -+{ -+ int headerlen = skb_headroom(skb); -+ unsigned int size = skb_end_offset(skb) + skb->data_len; -+ struct sk_buff *n = __alloc_skb(size, gfp_mask, -+ skb_alloc_rx_flag(skb), NUMA_NO_NODE); -+ -+ if (!n) -+ return NULL; -+ -+ /* Set the data pointer */ -+ skb_reserve(n, headerlen); -+ /* Set the tail pointer and length */ -+ skb_put(n, skb->len); -+ -+ if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len)) -+ BUG(); -+ -+ copy_skb_header(n, skb); -+ return n; -+} -+EXPORT_SYMBOL(skb_copy); -+ -+/** -+ * __pskb_copy_fclone - create copy of an sk_buff with private head. -+ * @skb: buffer to copy -+ * @headroom: headroom of new skb -+ * @gfp_mask: allocation priority -+ * @fclone: if true allocate the copy of the skb from the fclone -+ * cache instead of the head cache; it is recommended to set this -+ * to true for the cases where the copy will likely be cloned -+ * -+ * Make a copy of both an &sk_buff and part of its data, located -+ * in header. Fragmented data remain shared. This is used when -+ * the caller wishes to modify only header of &sk_buff and needs -+ * private copy of the header to alter. Returns %NULL on failure -+ * or the pointer to the buffer on success. -+ * The returned buffer has a reference count of 1. -+ */ -+ -+struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom, -+ gfp_t gfp_mask, bool fclone) -+{ -+ unsigned int size = skb_headlen(skb) + headroom; -+ int flags = skb_alloc_rx_flag(skb) | (fclone ? SKB_ALLOC_FCLONE : 0); -+ struct sk_buff *n = __alloc_skb(size, gfp_mask, flags, NUMA_NO_NODE); -+ -+ if (!n) -+ goto out; -+ -+ /* Set the data pointer */ -+ skb_reserve(n, headroom); -+ /* Set the tail pointer and length */ -+ skb_put(n, skb_headlen(skb)); -+ /* Copy the bytes */ -+ skb_copy_from_linear_data(skb, n->data, n->len); -+ -+ n->truesize += skb->data_len; -+ n->data_len = skb->data_len; -+ n->len = skb->len; -+ -+ if (skb_shinfo(skb)->nr_frags) { -+ int i; -+ -+ if (skb_orphan_frags(skb, gfp_mask)) { -+ kfree_skb(n); -+ n = NULL; -+ goto out; -+ } -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i]; -+ skb_frag_ref(skb, i); -+ } -+ skb_shinfo(n)->nr_frags = i; -+ } -+ -+ if (skb_has_frag_list(skb)) { -+ skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list; -+ skb_clone_fraglist(n); -+ } -+ -+ copy_skb_header(n, skb); -+out: -+ return n; -+} -+EXPORT_SYMBOL(__pskb_copy_fclone); -+ -+/** -+ * pskb_expand_head - reallocate header of &sk_buff -+ * @skb: buffer to reallocate -+ * @nhead: room to add at head -+ * @ntail: room to add at tail -+ * @gfp_mask: allocation priority -+ * -+ * Expands (or creates identical copy, if @nhead and @ntail are zero) -+ * header of @skb. &sk_buff itself is not changed. &sk_buff MUST have -+ * reference count of 1. Returns zero in the case of success or error, -+ * if expansion failed. In the last case, &sk_buff is not changed. -+ * -+ * All the pointers pointing into skb header may change and must be -+ * reloaded after call to this function. -+ */ -+ -+int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, -+ gfp_t gfp_mask) -+{ -+ int i; -+ u8 *data; -+ int size = nhead + skb_end_offset(skb) + ntail; -+ long off; -+ -+ BUG_ON(nhead < 0); -+ -+ if (skb_shared(skb)) -+ BUG(); -+ -+ size = SKB_DATA_ALIGN(size); -+ -+ if (skb_pfmemalloc(skb)) -+ gfp_mask |= __GFP_MEMALLOC; -+ data = kmalloc_reserve(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), -+ gfp_mask, NUMA_NO_NODE, NULL); -+ if (!data) -+ goto nodata; -+ size = SKB_WITH_OVERHEAD(ksize(data)); -+ -+ /* Copy only real data... and, alas, header. This should be -+ * optimized for the cases when header is void. -+ */ -+ memcpy(data + nhead, skb->head, skb_tail_pointer(skb) - skb->head); -+ -+ memcpy((struct skb_shared_info *)(data + size), -+ skb_shinfo(skb), -+ offsetof(struct skb_shared_info, frags[skb_shinfo(skb)->nr_frags])); -+ -+ /* -+ * if shinfo is shared we must drop the old head gracefully, but if it -+ * is not we can just drop the old head and let the existing refcount -+ * be since all we did is relocate the values -+ */ -+ if (skb_cloned(skb)) { -+ /* copy this zero copy skb frags */ -+ if (skb_orphan_frags(skb, gfp_mask)) -+ goto nofrags; -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) -+ skb_frag_ref(skb, i); -+ -+ if (skb_has_frag_list(skb)) -+ skb_clone_fraglist(skb); -+ -+ skb_release_data(skb); -+ } else { -+ skb_free_head(skb); -+ } -+ off = (data + nhead) - skb->head; -+ -+ skb->head = data; -+ skb->head_frag = 0; -+ skb->data += off; -+#ifdef NET_SKBUFF_DATA_USES_OFFSET -+ skb->end = size; -+ off = nhead; -+#else -+ skb->end = skb->head + size; -+#endif -+ skb->tail += off; -+ skb_headers_offset_update(skb, nhead); -+ skb->cloned = 0; -+ skb->hdr_len = 0; -+ skb->nohdr = 0; -+ atomic_set(&skb_shinfo(skb)->dataref, 1); -+ return 0; -+ -+nofrags: -+ kfree(data); -+nodata: -+ return -ENOMEM; -+} -+EXPORT_SYMBOL(pskb_expand_head); -+ -+/* Make private copy of skb with writable head and some headroom */ -+ -+struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom) -+{ -+ struct sk_buff *skb2; -+ int delta = headroom - skb_headroom(skb); -+ -+ if (delta <= 0) -+ skb2 = pskb_copy(skb, GFP_ATOMIC); -+ else { -+ skb2 = skb_clone(skb, GFP_ATOMIC); -+ if (skb2 && pskb_expand_head(skb2, SKB_DATA_ALIGN(delta), 0, -+ GFP_ATOMIC)) { -+ kfree_skb(skb2); -+ skb2 = NULL; -+ } -+ } -+ return skb2; -+} -+EXPORT_SYMBOL(skb_realloc_headroom); -+ -+/** -+ * skb_copy_expand - copy and expand sk_buff -+ * @skb: buffer to copy -+ * @newheadroom: new free bytes at head -+ * @newtailroom: new free bytes at tail -+ * @gfp_mask: allocation priority -+ * -+ * Make a copy of both an &sk_buff and its data and while doing so -+ * allocate additional space. -+ * -+ * This is used when the caller wishes to modify the data and needs a -+ * private copy of the data to alter as well as more space for new fields. -+ * Returns %NULL on failure or the pointer to the buffer -+ * on success. The returned buffer has a reference count of 1. -+ * -+ * You must pass %GFP_ATOMIC as the allocation priority if this function -+ * is called from an interrupt. -+ */ -+struct sk_buff *skb_copy_expand(const struct sk_buff *skb, -+ int newheadroom, int newtailroom, -+ gfp_t gfp_mask) -+{ -+ /* -+ * Allocate the copy buffer -+ */ -+ struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom, -+ gfp_mask, skb_alloc_rx_flag(skb), -+ NUMA_NO_NODE); -+ int oldheadroom = skb_headroom(skb); -+ int head_copy_len, head_copy_off; -+ -+ if (!n) -+ return NULL; -+ -+ skb_reserve(n, newheadroom); -+ -+ /* Set the tail pointer and length */ -+ skb_put(n, skb->len); -+ -+ head_copy_len = oldheadroom; -+ head_copy_off = 0; -+ if (newheadroom <= head_copy_len) -+ head_copy_len = newheadroom; -+ else -+ head_copy_off = newheadroom - head_copy_len; -+ -+ /* Copy the linear header and data. */ -+ if (skb_copy_bits(skb, -head_copy_len, n->head + head_copy_off, -+ skb->len + head_copy_len)) -+ BUG(); -+ -+ copy_skb_header(n, skb); -+ -+ skb_headers_offset_update(n, newheadroom - oldheadroom); -+ -+ return n; -+} -+EXPORT_SYMBOL(skb_copy_expand); -+ -+/** -+ * skb_pad - zero pad the tail of an skb -+ * @skb: buffer to pad -+ * @pad: space to pad -+ * -+ * Ensure that a buffer is followed by a padding area that is zero -+ * filled. Used by network drivers which may DMA or transfer data -+ * beyond the buffer end onto the wire. -+ * -+ * May return error in out of memory cases. The skb is freed on error. -+ */ -+ -+int skb_pad(struct sk_buff *skb, int pad) -+{ -+ int err; -+ int ntail; -+ -+ /* If the skbuff is non linear tailroom is always zero.. */ -+ if (!skb_cloned(skb) && skb_tailroom(skb) >= pad) { -+ memset(skb->data+skb->len, 0, pad); -+ return 0; -+ } -+ -+ ntail = skb->data_len + pad - (skb->end - skb->tail); -+ if (likely(skb_cloned(skb) || ntail > 0)) { -+ err = pskb_expand_head(skb, 0, ntail, GFP_ATOMIC); -+ if (unlikely(err)) -+ goto free_skb; -+ } -+ -+ /* FIXME: The use of this function with non-linear skb's really needs -+ * to be audited. -+ */ -+ err = skb_linearize(skb); -+ if (unlikely(err)) -+ goto free_skb; -+ -+ memset(skb->data + skb->len, 0, pad); -+ return 0; -+ -+free_skb: -+ kfree_skb(skb); -+ return err; -+} -+EXPORT_SYMBOL(skb_pad); -+ -+/** -+ * pskb_put - add data to the tail of a potentially fragmented buffer -+ * @skb: start of the buffer to use -+ * @tail: tail fragment of the buffer to use -+ * @len: amount of data to add -+ * -+ * This function extends the used data area of the potentially -+ * fragmented buffer. @tail must be the last fragment of @skb -- or -+ * @skb itself. If this would exceed the total buffer size the kernel -+ * will panic. A pointer to the first byte of the extra data is -+ * returned. -+ */ -+ -+unsigned char *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len) -+{ -+ if (tail != skb) { -+ skb->data_len += len; -+ skb->len += len; -+ } -+ return skb_put(tail, len); -+} -+EXPORT_SYMBOL_GPL(pskb_put); -+ -+/** -+ * skb_put - add data to a buffer -+ * @skb: buffer to use -+ * @len: amount of data to add -+ * -+ * This function extends the used data area of the buffer. If this would -+ * exceed the total buffer size the kernel will panic. A pointer to the -+ * first byte of the extra data is returned. -+ */ -+unsigned char *skb_put(struct sk_buff *skb, unsigned int len) -+{ -+ unsigned char *tmp = skb_tail_pointer(skb); -+ SKB_LINEAR_ASSERT(skb); -+ skb->tail += len; -+ skb->len += len; -+ if (unlikely(skb->tail > skb->end)) -+ skb_over_panic(skb, len, __builtin_return_address(0)); -+ return tmp; -+} -+EXPORT_SYMBOL(skb_put); -+ -+/** -+ * skb_push - add data to the start of a buffer -+ * @skb: buffer to use -+ * @len: amount of data to add -+ * -+ * This function extends the used data area of the buffer at the buffer -+ * start. If this would exceed the total buffer headroom the kernel will -+ * panic. A pointer to the first byte of the extra data is returned. -+ */ -+unsigned char *skb_push(struct sk_buff *skb, unsigned int len) -+{ -+ skb->data -= len; -+ skb->len += len; -+ if (unlikely(skb->datahead)) -+ skb_under_panic(skb, len, __builtin_return_address(0)); -+ return skb->data; -+} -+EXPORT_SYMBOL(skb_push); -+ -+/** -+ * skb_pull - remove data from the start of a buffer -+ * @skb: buffer to use -+ * @len: amount of data to remove -+ * -+ * This function removes data from the start of a buffer, returning -+ * the memory to the headroom. A pointer to the next data in the buffer -+ * is returned. Once the data has been pulled future pushes will overwrite -+ * the old data. -+ */ -+unsigned char *skb_pull(struct sk_buff *skb, unsigned int len) -+{ -+ return skb_pull_inline(skb, len); -+} -+EXPORT_SYMBOL(skb_pull); -+ -+/** -+ * skb_trim - remove end from a buffer -+ * @skb: buffer to alter -+ * @len: new length -+ * -+ * Cut the length of a buffer down by removing data from the tail. If -+ * the buffer is already under the length specified it is not modified. -+ * The skb must be linear. -+ */ -+void skb_trim(struct sk_buff *skb, unsigned int len) -+{ -+ if (skb->len > len) -+ __skb_trim(skb, len); -+} -+EXPORT_SYMBOL(skb_trim); -+ -+/* Trims skb to length len. It can change skb pointers. -+ */ -+ -+int ___pskb_trim(struct sk_buff *skb, unsigned int len) -+{ -+ struct sk_buff **fragp; -+ struct sk_buff *frag; -+ int offset = skb_headlen(skb); -+ int nfrags = skb_shinfo(skb)->nr_frags; -+ int i; -+ int err; -+ -+ if (skb_cloned(skb) && -+ unlikely((err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))) -+ return err; -+ -+ i = 0; -+ if (offset >= len) -+ goto drop_pages; -+ -+ for (; i < nfrags; i++) { -+ int end = offset + skb_frag_size(&skb_shinfo(skb)->frags[i]); -+ -+ if (end < len) { -+ offset = end; -+ continue; -+ } -+ -+ skb_frag_size_set(&skb_shinfo(skb)->frags[i++], len - offset); -+ -+drop_pages: -+ skb_shinfo(skb)->nr_frags = i; -+ -+ for (; i < nfrags; i++) -+ skb_frag_unref(skb, i); -+ -+ if (skb_has_frag_list(skb)) -+ skb_drop_fraglist(skb); -+ goto done; -+ } -+ -+ for (fragp = &skb_shinfo(skb)->frag_list; (frag = *fragp); -+ fragp = &frag->next) { -+ int end = offset + frag->len; -+ -+ if (skb_shared(frag)) { -+ struct sk_buff *nfrag; -+ -+ nfrag = skb_clone(frag, GFP_ATOMIC); -+ if (unlikely(!nfrag)) -+ return -ENOMEM; -+ -+ nfrag->next = frag->next; -+ consume_skb(frag); -+ frag = nfrag; -+ *fragp = frag; -+ } -+ -+ if (end < len) { -+ offset = end; -+ continue; -+ } -+ -+ if (end > len && -+ unlikely((err = pskb_trim(frag, len - offset)))) -+ return err; -+ -+ if (frag->next) -+ skb_drop_list(&frag->next); -+ break; -+ } -+ -+done: -+ if (len > skb_headlen(skb)) { -+ skb->data_len -= skb->len - len; -+ skb->len = len; -+ } else { -+ skb->len = len; -+ skb->data_len = 0; -+ skb_set_tail_pointer(skb, len); -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(___pskb_trim); -+ -+/** -+ * __pskb_pull_tail - advance tail of skb header -+ * @skb: buffer to reallocate -+ * @delta: number of bytes to advance tail -+ * -+ * The function makes a sense only on a fragmented &sk_buff, -+ * it expands header moving its tail forward and copying necessary -+ * data from fragmented part. -+ * -+ * &sk_buff MUST have reference count of 1. -+ * -+ * Returns %NULL (and &sk_buff does not change) if pull failed -+ * or value of new tail of skb in the case of success. -+ * -+ * All the pointers pointing into skb header may change and must be -+ * reloaded after call to this function. -+ */ -+ -+/* Moves tail of skb head forward, copying data from fragmented part, -+ * when it is necessary. -+ * 1. It may fail due to malloc failure. -+ * 2. It may change skb pointers. -+ * -+ * It is pretty complicated. Luckily, it is called only in exceptional cases. -+ */ -+unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta) -+{ -+ /* If skb has not enough free space at tail, get new one -+ * plus 128 bytes for future expansions. If we have enough -+ * room at tail, reallocate without expansion only if skb is cloned. -+ */ -+ int i, k, eat = (skb->tail + delta) - skb->end; -+ -+ if (eat > 0 || skb_cloned(skb)) { -+ if (pskb_expand_head(skb, 0, eat > 0 ? eat + 128 : 0, -+ GFP_ATOMIC)) -+ return NULL; -+ } -+ -+ if (skb_copy_bits(skb, skb_headlen(skb), skb_tail_pointer(skb), delta)) -+ BUG(); -+ -+ /* Optimization: no fragments, no reasons to preestimate -+ * size of pulled pages. Superb. -+ */ -+ if (!skb_has_frag_list(skb)) -+ goto pull_pages; -+ -+ /* Estimate size of pulled pages. */ -+ eat = delta; -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ int size = skb_frag_size(&skb_shinfo(skb)->frags[i]); -+ -+ if (size >= eat) -+ goto pull_pages; -+ eat -= size; -+ } -+ -+ /* If we need update frag list, we are in troubles. -+ * Certainly, it possible to add an offset to skb data, -+ * but taking into account that pulling is expected to -+ * be very rare operation, it is worth to fight against -+ * further bloating skb head and crucify ourselves here instead. -+ * Pure masohism, indeed. 8)8) -+ */ -+ if (eat) { -+ struct sk_buff *list = skb_shinfo(skb)->frag_list; -+ struct sk_buff *clone = NULL; -+ struct sk_buff *insp = NULL; -+ -+ do { -+ BUG_ON(!list); -+ -+ if (list->len <= eat) { -+ /* Eaten as whole. */ -+ eat -= list->len; -+ list = list->next; -+ insp = list; -+ } else { -+ /* Eaten partially. */ -+ -+ if (skb_shared(list)) { -+ /* Sucks! We need to fork list. :-( */ -+ clone = skb_clone(list, GFP_ATOMIC); -+ if (!clone) -+ return NULL; -+ insp = list->next; -+ list = clone; -+ } else { -+ /* This may be pulled without -+ * problems. */ -+ insp = list; -+ } -+ if (!pskb_pull(list, eat)) { -+ kfree_skb(clone); -+ return NULL; -+ } -+ break; -+ } -+ } while (eat); -+ -+ /* Free pulled out fragments. */ -+ while ((list = skb_shinfo(skb)->frag_list) != insp) { -+ skb_shinfo(skb)->frag_list = list->next; -+ kfree_skb(list); -+ } -+ /* And insert new clone at head. */ -+ if (clone) { -+ clone->next = list; -+ skb_shinfo(skb)->frag_list = clone; -+ } -+ } -+ /* Success! Now we may commit changes to skb data. */ -+ -+pull_pages: -+ eat = delta; -+ k = 0; -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ int size = skb_frag_size(&skb_shinfo(skb)->frags[i]); -+ -+ if (size <= eat) { -+ skb_frag_unref(skb, i); -+ eat -= size; -+ } else { -+ skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i]; -+ if (eat) { -+ skb_shinfo(skb)->frags[k].page_offset += eat; -+ skb_frag_size_sub(&skb_shinfo(skb)->frags[k], eat); -+ eat = 0; -+ } -+ k++; -+ } -+ } -+ skb_shinfo(skb)->nr_frags = k; -+ -+ skb->tail += delta; -+ skb->data_len -= delta; -+ -+ return skb_tail_pointer(skb); -+} -+EXPORT_SYMBOL(__pskb_pull_tail); -+ -+/** -+ * skb_copy_bits - copy bits from skb to kernel buffer -+ * @skb: source skb -+ * @offset: offset in source -+ * @to: destination buffer -+ * @len: number of bytes to copy -+ * -+ * Copy the specified number of bytes from the source skb to the -+ * destination buffer. -+ * -+ * CAUTION ! : -+ * If its prototype is ever changed, -+ * check arch/{*}/net/{*}.S files, -+ * since it is called from BPF assembly code. -+ */ -+int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len) -+{ -+ int start = skb_headlen(skb); -+ struct sk_buff *frag_iter; -+ int i, copy; -+ -+ if (offset > (int)skb->len - len) -+ goto fault; -+ -+ /* Copy header. */ -+ if ((copy = start - offset) > 0) { -+ if (copy > len) -+ copy = len; -+ skb_copy_from_linear_data_offset(skb, offset, to, copy); -+ if ((len -= copy) == 0) -+ return 0; -+ offset += copy; -+ to += copy; -+ } -+ -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ int end; -+ skb_frag_t *f = &skb_shinfo(skb)->frags[i]; -+ -+ WARN_ON(start > offset + len); -+ -+ end = start + skb_frag_size(f); -+ if ((copy = end - offset) > 0) { -+ u8 *vaddr; -+ -+ if (copy > len) -+ copy = len; -+ -+ vaddr = kmap_atomic(skb_frag_page(f)); -+ memcpy(to, -+ vaddr + f->page_offset + offset - start, -+ copy); -+ kunmap_atomic(vaddr); -+ -+ if ((len -= copy) == 0) -+ return 0; -+ offset += copy; -+ to += copy; -+ } -+ start = end; -+ } -+ -+ skb_walk_frags(skb, frag_iter) { -+ int end; -+ -+ WARN_ON(start > offset + len); -+ -+ end = start + frag_iter->len; -+ if ((copy = end - offset) > 0) { -+ if (copy > len) -+ copy = len; -+ if (skb_copy_bits(frag_iter, offset - start, to, copy)) -+ goto fault; -+ if ((len -= copy) == 0) -+ return 0; -+ offset += copy; -+ to += copy; -+ } -+ start = end; -+ } -+ -+ if (!len) -+ return 0; -+ -+fault: -+ return -EFAULT; -+} -+EXPORT_SYMBOL(skb_copy_bits); -+ -+/* -+ * Callback from splice_to_pipe(), if we need to release some pages -+ * at the end of the spd in case we error'ed out in filling the pipe. -+ */ -+static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) -+{ -+ put_page(spd->pages[i]); -+} -+ -+static struct page *linear_to_page(struct page *page, unsigned int *len, -+ unsigned int *offset, -+ struct sock *sk) -+{ -+ struct page_frag *pfrag = sk_page_frag(sk); -+ -+ if (!sk_page_frag_refill(sk, pfrag)) -+ return NULL; -+ -+ *len = min_t(unsigned int, *len, pfrag->size - pfrag->offset); -+ -+ memcpy(page_address(pfrag->page) + pfrag->offset, -+ page_address(page) + *offset, *len); -+ *offset = pfrag->offset; -+ pfrag->offset += *len; -+ -+ return pfrag->page; -+} -+ -+static bool spd_can_coalesce(const struct splice_pipe_desc *spd, -+ struct page *page, -+ unsigned int offset) -+{ -+ return spd->nr_pages && -+ spd->pages[spd->nr_pages - 1] == page && -+ (spd->partial[spd->nr_pages - 1].offset + -+ spd->partial[spd->nr_pages - 1].len == offset); -+} -+ -+/* -+ * Fill page/offset/length into spd, if it can hold more pages. -+ */ -+static bool spd_fill_page(struct splice_pipe_desc *spd, -+ struct pipe_inode_info *pipe, struct page *page, -+ unsigned int *len, unsigned int offset, -+ bool linear, -+ struct sock *sk) -+{ -+ if (unlikely(spd->nr_pages == MAX_SKB_FRAGS)) -+ return true; -+ -+ if (linear) { -+ page = linear_to_page(page, len, &offset, sk); -+ if (!page) -+ return true; -+ } -+ if (spd_can_coalesce(spd, page, offset)) { -+ spd->partial[spd->nr_pages - 1].len += *len; -+ return false; -+ } -+ get_page(page); -+ spd->pages[spd->nr_pages] = page; -+ spd->partial[spd->nr_pages].len = *len; -+ spd->partial[spd->nr_pages].offset = offset; -+ spd->nr_pages++; -+ -+ return false; -+} -+ -+static bool __splice_segment(struct page *page, unsigned int poff, -+ unsigned int plen, unsigned int *off, -+ unsigned int *len, -+ struct splice_pipe_desc *spd, bool linear, -+ struct sock *sk, -+ struct pipe_inode_info *pipe) -+{ -+ if (!*len) -+ return true; -+ -+ /* skip this segment if already processed */ -+ if (*off >= plen) { -+ *off -= plen; -+ return false; -+ } -+ -+ /* ignore any bits we already processed */ -+ poff += *off; -+ plen -= *off; -+ *off = 0; -+ -+ do { -+ unsigned int flen = min(*len, plen); -+ -+ if (spd_fill_page(spd, pipe, page, &flen, poff, -+ linear, sk)) -+ return true; -+ poff += flen; -+ plen -= flen; -+ *len -= flen; -+ } while (*len && plen); -+ -+ return false; -+} -+ -+/* -+ * Map linear and fragment data from the skb to spd. It reports true if the -+ * pipe is full or if we already spliced the requested length. -+ */ -+static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe, -+ unsigned int *offset, unsigned int *len, -+ struct splice_pipe_desc *spd, struct sock *sk) -+{ -+ int seg; -+ -+ /* map the linear part : -+ * If skb->head_frag is set, this 'linear' part is backed by a -+ * fragment, and if the head is not shared with any clones then -+ * we can avoid a copy since we own the head portion of this page. -+ */ -+ if (__splice_segment(virt_to_page(skb->data), -+ (unsigned long) skb->data & (PAGE_SIZE - 1), -+ skb_headlen(skb), -+ offset, len, spd, -+ skb_head_is_locked(skb), -+ sk, pipe)) -+ return true; -+ -+ /* -+ * then map the fragments -+ */ -+ for (seg = 0; seg < skb_shinfo(skb)->nr_frags; seg++) { -+ const skb_frag_t *f = &skb_shinfo(skb)->frags[seg]; -+ -+ if (__splice_segment(skb_frag_page(f), -+ f->page_offset, skb_frag_size(f), -+ offset, len, spd, false, sk, pipe)) -+ return true; -+ } -+ -+ return false; -+} -+ -+/* -+ * Map data from the skb to a pipe. Should handle both the linear part, -+ * the fragments, and the frag list. It does NOT handle frag lists within -+ * the frag list, if such a thing exists. We'd probably need to recurse to -+ * handle that cleanly. -+ */ -+int skb_splice_bits(struct sk_buff *skb, unsigned int offset, -+ struct pipe_inode_info *pipe, unsigned int tlen, -+ unsigned int flags) -+{ -+ struct partial_page partial[MAX_SKB_FRAGS]; -+ struct page *pages[MAX_SKB_FRAGS]; -+ struct splice_pipe_desc spd = { -+ .pages = pages, -+ .partial = partial, -+ .nr_pages_max = MAX_SKB_FRAGS, -+ .flags = flags, -+ .ops = &nosteal_pipe_buf_ops, -+ .spd_release = sock_spd_release, -+ }; -+ struct sk_buff *frag_iter; -+ struct sock *sk = skb->sk; -+ int ret = 0; -+ -+ /* -+ * __skb_splice_bits() only fails if the output has no room left, -+ * so no point in going over the frag_list for the error case. -+ */ -+ if (__skb_splice_bits(skb, pipe, &offset, &tlen, &spd, sk)) -+ goto done; -+ else if (!tlen) -+ goto done; -+ -+ /* -+ * now see if we have a frag_list to map -+ */ -+ skb_walk_frags(skb, frag_iter) { -+ if (!tlen) -+ break; -+ if (__skb_splice_bits(frag_iter, pipe, &offset, &tlen, &spd, sk)) -+ break; -+ } -+ -+done: -+ if (spd.nr_pages) { -+ /* -+ * Drop the socket lock, otherwise we have reverse -+ * locking dependencies between sk_lock and i_mutex -+ * here as compared to sendfile(). We enter here -+ * with the socket lock held, and splice_to_pipe() will -+ * grab the pipe inode lock. For sendfile() emulation, -+ * we call into ->sendpage() with the i_mutex lock held -+ * and networking will grab the socket lock. -+ */ -+ release_sock(sk); -+ ret = splice_to_pipe(pipe, &spd); -+ lock_sock(sk); -+ } -+ -+ return ret; -+} -+ -+/** -+ * skb_store_bits - store bits from kernel buffer to skb -+ * @skb: destination buffer -+ * @offset: offset in destination -+ * @from: source buffer -+ * @len: number of bytes to copy -+ * -+ * Copy the specified number of bytes from the source buffer to the -+ * destination skb. This function handles all the messy bits of -+ * traversing fragment lists and such. -+ */ -+ -+int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len) -+{ -+ int start = skb_headlen(skb); -+ struct sk_buff *frag_iter; -+ int i, copy; -+ -+ if (offset > (int)skb->len - len) -+ goto fault; -+ -+ if ((copy = start - offset) > 0) { -+ if (copy > len) -+ copy = len; -+ skb_copy_to_linear_data_offset(skb, offset, from, copy); -+ if ((len -= copy) == 0) -+ return 0; -+ offset += copy; -+ from += copy; -+ } -+ -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; -+ int end; -+ -+ WARN_ON(start > offset + len); -+ -+ end = start + skb_frag_size(frag); -+ if ((copy = end - offset) > 0) { -+ u8 *vaddr; -+ -+ if (copy > len) -+ copy = len; -+ -+ vaddr = kmap_atomic(skb_frag_page(frag)); -+ memcpy(vaddr + frag->page_offset + offset - start, -+ from, copy); -+ kunmap_atomic(vaddr); -+ -+ if ((len -= copy) == 0) -+ return 0; -+ offset += copy; -+ from += copy; -+ } -+ start = end; -+ } -+ -+ skb_walk_frags(skb, frag_iter) { -+ int end; -+ -+ WARN_ON(start > offset + len); -+ -+ end = start + frag_iter->len; -+ if ((copy = end - offset) > 0) { -+ if (copy > len) -+ copy = len; -+ if (skb_store_bits(frag_iter, offset - start, -+ from, copy)) -+ goto fault; -+ if ((len -= copy) == 0) -+ return 0; -+ offset += copy; -+ from += copy; -+ } -+ start = end; -+ } -+ if (!len) -+ return 0; -+ -+fault: -+ return -EFAULT; -+} -+EXPORT_SYMBOL(skb_store_bits); -+ -+/* Checksum skb data. */ -+__wsum __skb_checksum(const struct sk_buff *skb, int offset, int len, -+ __wsum csum, const struct skb_checksum_ops *ops) -+{ -+ int start = skb_headlen(skb); -+ int i, copy = start - offset; -+ struct sk_buff *frag_iter; -+ int pos = 0; -+ -+ /* Checksum header. */ -+ if (copy > 0) { -+ if (copy > len) -+ copy = len; -+ csum = ops->update(skb->data + offset, copy, csum); -+ if ((len -= copy) == 0) -+ return csum; -+ offset += copy; -+ pos = copy; -+ } -+ -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ int end; -+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; -+ -+ WARN_ON(start > offset + len); -+ -+ end = start + skb_frag_size(frag); -+ if ((copy = end - offset) > 0) { -+ __wsum csum2; -+ u8 *vaddr; -+ -+ if (copy > len) -+ copy = len; -+ vaddr = kmap_atomic(skb_frag_page(frag)); -+ csum2 = ops->update(vaddr + frag->page_offset + -+ offset - start, copy, 0); -+ kunmap_atomic(vaddr); -+ csum = ops->combine(csum, csum2, pos, copy); -+ if (!(len -= copy)) -+ return csum; -+ offset += copy; -+ pos += copy; -+ } -+ start = end; -+ } -+ -+ skb_walk_frags(skb, frag_iter) { -+ int end; -+ -+ WARN_ON(start > offset + len); -+ -+ end = start + frag_iter->len; -+ if ((copy = end - offset) > 0) { -+ __wsum csum2; -+ if (copy > len) -+ copy = len; -+ csum2 = __skb_checksum(frag_iter, offset - start, -+ copy, 0, ops); -+ csum = ops->combine(csum, csum2, pos, copy); -+ if ((len -= copy) == 0) -+ return csum; -+ offset += copy; -+ pos += copy; -+ } -+ start = end; -+ } -+ BUG_ON(len); -+ -+ return csum; -+} -+EXPORT_SYMBOL(__skb_checksum); -+ -+__wsum skb_checksum(const struct sk_buff *skb, int offset, -+ int len, __wsum csum) -+{ -+ const struct skb_checksum_ops ops = { -+ .update = csum_partial_ext, -+ .combine = csum_block_add_ext, -+ }; -+ -+ return __skb_checksum(skb, offset, len, csum, &ops); -+} -+EXPORT_SYMBOL(skb_checksum); -+ -+/* Both of above in one bottle. */ -+ -+__wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, -+ u8 *to, int len, __wsum csum) -+{ -+ int start = skb_headlen(skb); -+ int i, copy = start - offset; -+ struct sk_buff *frag_iter; -+ int pos = 0; -+ -+ /* Copy header. */ -+ if (copy > 0) { -+ if (copy > len) -+ copy = len; -+ csum = csum_partial_copy_nocheck(skb->data + offset, to, -+ copy, csum); -+ if ((len -= copy) == 0) -+ return csum; -+ offset += copy; -+ to += copy; -+ pos = copy; -+ } -+ -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ int end; -+ -+ WARN_ON(start > offset + len); -+ -+ end = start + skb_frag_size(&skb_shinfo(skb)->frags[i]); -+ if ((copy = end - offset) > 0) { -+ __wsum csum2; -+ u8 *vaddr; -+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; -+ -+ if (copy > len) -+ copy = len; -+ vaddr = kmap_atomic(skb_frag_page(frag)); -+ csum2 = csum_partial_copy_nocheck(vaddr + -+ frag->page_offset + -+ offset - start, to, -+ copy, 0); -+ kunmap_atomic(vaddr); -+ csum = csum_block_add(csum, csum2, pos); -+ if (!(len -= copy)) -+ return csum; -+ offset += copy; -+ to += copy; -+ pos += copy; -+ } -+ start = end; -+ } -+ -+ skb_walk_frags(skb, frag_iter) { -+ __wsum csum2; -+ int end; -+ -+ WARN_ON(start > offset + len); -+ -+ end = start + frag_iter->len; -+ if ((copy = end - offset) > 0) { -+ if (copy > len) -+ copy = len; -+ csum2 = skb_copy_and_csum_bits(frag_iter, -+ offset - start, -+ to, copy, 0); -+ csum = csum_block_add(csum, csum2, pos); -+ if ((len -= copy) == 0) -+ return csum; -+ offset += copy; -+ to += copy; -+ pos += copy; -+ } -+ start = end; -+ } -+ BUG_ON(len); -+ return csum; -+} -+EXPORT_SYMBOL(skb_copy_and_csum_bits); -+ -+ /** -+ * skb_zerocopy_headlen - Calculate headroom needed for skb_zerocopy() -+ * @from: source buffer -+ * -+ * Calculates the amount of linear headroom needed in the 'to' skb passed -+ * into skb_zerocopy(). -+ */ -+unsigned int -+skb_zerocopy_headlen(const struct sk_buff *from) -+{ -+ unsigned int hlen = 0; -+ -+ if (!from->head_frag || -+ skb_headlen(from) < L1_CACHE_BYTES || -+ skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS) -+ hlen = skb_headlen(from); -+ -+ if (skb_has_frag_list(from)) -+ hlen = from->len; -+ -+ return hlen; -+} -+EXPORT_SYMBOL_GPL(skb_zerocopy_headlen); -+ -+/** -+ * skb_zerocopy - Zero copy skb to skb -+ * @to: destination buffer -+ * @from: source buffer -+ * @len: number of bytes to copy from source buffer -+ * @hlen: size of linear headroom in destination buffer -+ * -+ * Copies up to `len` bytes from `from` to `to` by creating references -+ * to the frags in the source buffer. -+ * -+ * The `hlen` as calculated by skb_zerocopy_headlen() specifies the -+ * headroom in the `to` buffer. -+ * -+ * Return value: -+ * 0: everything is OK -+ * -ENOMEM: couldn't orphan frags of @from due to lack of memory -+ * -EFAULT: skb_copy_bits() found some problem with skb geometry -+ */ -+int -+skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen) -+{ -+ int i, j = 0; -+ int plen = 0; /* length of skb->head fragment */ -+ int ret; -+ struct page *page; -+ unsigned int offset; -+ -+ BUG_ON(!from->head_frag && !hlen); -+ -+ /* dont bother with small payloads */ -+ if (len <= skb_tailroom(to)) -+ return skb_copy_bits(from, 0, skb_put(to, len), len); -+ -+ if (hlen) { -+ ret = skb_copy_bits(from, 0, skb_put(to, hlen), hlen); -+ if (unlikely(ret)) -+ return ret; -+ len -= hlen; -+ } else { -+ plen = min_t(int, skb_headlen(from), len); -+ if (plen) { -+ page = virt_to_head_page(from->head); -+ offset = from->data - (unsigned char *)page_address(page); -+ __skb_fill_page_desc(to, 0, page, offset, plen); -+ get_page(page); -+ j = 1; -+ len -= plen; -+ } -+ } -+ -+ to->truesize += len + plen; -+ to->len += len + plen; -+ to->data_len += len + plen; -+ -+ if (unlikely(skb_orphan_frags(from, GFP_ATOMIC))) { -+ skb_tx_error(from); -+ return -ENOMEM; -+ } -+ -+ for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { -+ if (!len) -+ break; -+ skb_shinfo(to)->frags[j] = skb_shinfo(from)->frags[i]; -+ skb_shinfo(to)->frags[j].size = min_t(int, skb_shinfo(to)->frags[j].size, len); -+ len -= skb_shinfo(to)->frags[j].size; -+ skb_frag_ref(to, j); -+ j++; -+ } -+ skb_shinfo(to)->nr_frags = j; -+ -+ return 0; -+} -+EXPORT_SYMBOL_GPL(skb_zerocopy); -+ -+void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) -+{ -+ __wsum csum; -+ long csstart; -+ -+ if (skb->ip_summed == CHECKSUM_PARTIAL) -+ csstart = skb_checksum_start_offset(skb); -+ else -+ csstart = skb_headlen(skb); -+ -+ BUG_ON(csstart > skb_headlen(skb)); -+ -+ skb_copy_from_linear_data(skb, to, csstart); -+ -+ csum = 0; -+ if (csstart != skb->len) -+ csum = skb_copy_and_csum_bits(skb, csstart, to + csstart, -+ skb->len - csstart, 0); -+ -+ if (skb->ip_summed == CHECKSUM_PARTIAL) { -+ long csstuff = csstart + skb->csum_offset; -+ -+ *((__sum16 *)(to + csstuff)) = csum_fold(csum); -+ } -+} -+EXPORT_SYMBOL(skb_copy_and_csum_dev); -+ -+/** -+ * skb_dequeue - remove from the head of the queue -+ * @list: list to dequeue from -+ * -+ * Remove the head of the list. The list lock is taken so the function -+ * may be used safely with other locking list functions. The head item is -+ * returned or %NULL if the list is empty. -+ */ -+ -+struct sk_buff *skb_dequeue(struct sk_buff_head *list) -+{ -+ unsigned long flags; -+ struct sk_buff *result; -+ -+ spin_lock_irqsave(&list->lock, flags); -+ result = __skb_dequeue(list); -+ spin_unlock_irqrestore(&list->lock, flags); -+ return result; -+} -+EXPORT_SYMBOL(skb_dequeue); -+ -+/** -+ * skb_dequeue_tail - remove from the tail of the queue -+ * @list: list to dequeue from -+ * -+ * Remove the tail of the list. The list lock is taken so the function -+ * may be used safely with other locking list functions. The tail item is -+ * returned or %NULL if the list is empty. -+ */ -+struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list) -+{ -+ unsigned long flags; -+ struct sk_buff *result; -+ -+ spin_lock_irqsave(&list->lock, flags); -+ result = __skb_dequeue_tail(list); -+ spin_unlock_irqrestore(&list->lock, flags); -+ return result; -+} -+EXPORT_SYMBOL(skb_dequeue_tail); -+ -+/** -+ * skb_queue_purge - empty a list -+ * @list: list to empty -+ * -+ * Delete all buffers on an &sk_buff list. Each buffer is removed from -+ * the list and one reference dropped. This function takes the list -+ * lock and is atomic with respect to other list locking functions. -+ */ -+void skb_queue_purge(struct sk_buff_head *list) -+{ -+ struct sk_buff *skb; -+ while ((skb = skb_dequeue(list)) != NULL) -+ kfree_skb(skb); -+} -+EXPORT_SYMBOL(skb_queue_purge); -+ -+/** -+ * skb_queue_head - queue a buffer at the list head -+ * @list: list to use -+ * @newsk: buffer to queue -+ * -+ * Queue a buffer at the start of the list. This function takes the -+ * list lock and can be used safely with other locking &sk_buff functions -+ * safely. -+ * -+ * A buffer cannot be placed on two lists at the same time. -+ */ -+void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&list->lock, flags); -+ __skb_queue_head(list, newsk); -+ spin_unlock_irqrestore(&list->lock, flags); -+} -+EXPORT_SYMBOL(skb_queue_head); -+ -+/** -+ * skb_queue_tail - queue a buffer at the list tail -+ * @list: list to use -+ * @newsk: buffer to queue -+ * -+ * Queue a buffer at the tail of the list. This function takes the -+ * list lock and can be used safely with other locking &sk_buff functions -+ * safely. -+ * -+ * A buffer cannot be placed on two lists at the same time. -+ */ -+void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&list->lock, flags); -+ __skb_queue_tail(list, newsk); -+ spin_unlock_irqrestore(&list->lock, flags); -+} -+EXPORT_SYMBOL(skb_queue_tail); -+ -+/** -+ * skb_unlink - remove a buffer from a list -+ * @skb: buffer to remove -+ * @list: list to use -+ * -+ * Remove a packet from a list. The list locks are taken and this -+ * function is atomic with respect to other list locked calls -+ * -+ * You must know what list the SKB is on. -+ */ -+void skb_unlink(struct sk_buff *skb, struct sk_buff_head *list) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&list->lock, flags); -+ __skb_unlink(skb, list); -+ spin_unlock_irqrestore(&list->lock, flags); -+} -+EXPORT_SYMBOL(skb_unlink); -+ -+/** -+ * skb_append - append a buffer -+ * @old: buffer to insert after -+ * @newsk: buffer to insert -+ * @list: list to use -+ * -+ * Place a packet after a given packet in a list. The list locks are taken -+ * and this function is atomic with respect to other list locked calls. -+ * A buffer cannot be placed on two lists at the same time. -+ */ -+void skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&list->lock, flags); -+ __skb_queue_after(list, old, newsk); -+ spin_unlock_irqrestore(&list->lock, flags); -+} -+EXPORT_SYMBOL(skb_append); -+ -+/** -+ * skb_insert - insert a buffer -+ * @old: buffer to insert before -+ * @newsk: buffer to insert -+ * @list: list to use -+ * -+ * Place a packet before a given packet in a list. The list locks are -+ * taken and this function is atomic with respect to other list locked -+ * calls. -+ * -+ * A buffer cannot be placed on two lists at the same time. -+ */ -+void skb_insert(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&list->lock, flags); -+ __skb_insert(newsk, old->prev, old, list); -+ spin_unlock_irqrestore(&list->lock, flags); -+} -+EXPORT_SYMBOL(skb_insert); -+ -+static inline void skb_split_inside_header(struct sk_buff *skb, -+ struct sk_buff* skb1, -+ const u32 len, const int pos) -+{ -+ int i; -+ -+ skb_copy_from_linear_data_offset(skb, len, skb_put(skb1, pos - len), -+ pos - len); -+ /* And move data appendix as is. */ -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) -+ skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i]; -+ -+ skb_shinfo(skb1)->nr_frags = skb_shinfo(skb)->nr_frags; -+ skb_shinfo(skb)->nr_frags = 0; -+ skb1->data_len = skb->data_len; -+ skb1->len += skb1->data_len; -+ skb->data_len = 0; -+ skb->len = len; -+ skb_set_tail_pointer(skb, len); -+} -+ -+static inline void skb_split_no_header(struct sk_buff *skb, -+ struct sk_buff* skb1, -+ const u32 len, int pos) -+{ -+ int i, k = 0; -+ const int nfrags = skb_shinfo(skb)->nr_frags; -+ -+ skb_shinfo(skb)->nr_frags = 0; -+ skb1->len = skb1->data_len = skb->len - len; -+ skb->len = len; -+ skb->data_len = len - pos; -+ -+ for (i = 0; i < nfrags; i++) { -+ int size = skb_frag_size(&skb_shinfo(skb)->frags[i]); -+ -+ if (pos + size > len) { -+ skb_shinfo(skb1)->frags[k] = skb_shinfo(skb)->frags[i]; -+ -+ if (pos < len) { -+ /* Split frag. -+ * We have two variants in this case: -+ * 1. Move all the frag to the second -+ * part, if it is possible. F.e. -+ * this approach is mandatory for TUX, -+ * where splitting is expensive. -+ * 2. Split is accurately. We make this. -+ */ -+ skb_frag_ref(skb, i); -+ skb_shinfo(skb1)->frags[0].page_offset += len - pos; -+ skb_frag_size_sub(&skb_shinfo(skb1)->frags[0], len - pos); -+ skb_frag_size_set(&skb_shinfo(skb)->frags[i], len - pos); -+ skb_shinfo(skb)->nr_frags++; -+ } -+ k++; -+ } else -+ skb_shinfo(skb)->nr_frags++; -+ pos += size; -+ } -+ skb_shinfo(skb1)->nr_frags = k; -+} -+ -+/** -+ * skb_split - Split fragmented skb to two parts at length len. -+ * @skb: the buffer to split -+ * @skb1: the buffer to receive the second part -+ * @len: new length for skb -+ */ -+void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) -+{ -+ int pos = skb_headlen(skb); -+ -+ skb_shinfo(skb1)->tx_flags = skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; -+ if (len < pos) /* Split line is inside header. */ -+ skb_split_inside_header(skb, skb1, len, pos); -+ else /* Second chunk has no header, nothing to copy. */ -+ skb_split_no_header(skb, skb1, len, pos); -+} -+EXPORT_SYMBOL(skb_split); -+ -+/* Shifting from/to a cloned skb is a no-go. -+ * -+ * Caller cannot keep skb_shinfo related pointers past calling here! -+ */ -+static int skb_prepare_for_shift(struct sk_buff *skb) -+{ -+ return skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC); -+} -+ -+/** -+ * skb_shift - Shifts paged data partially from skb to another -+ * @tgt: buffer into which tail data gets added -+ * @skb: buffer from which the paged data comes from -+ * @shiftlen: shift up to this many bytes -+ * -+ * Attempts to shift up to shiftlen worth of bytes, which may be less than -+ * the length of the skb, from skb to tgt. Returns number bytes shifted. -+ * It's up to caller to free skb if everything was shifted. -+ * -+ * If @tgt runs out of frags, the whole operation is aborted. -+ * -+ * Skb cannot include anything else but paged data while tgt is allowed -+ * to have non-paged data as well. -+ * -+ * TODO: full sized shift could be optimized but that would need -+ * specialized skb free'er to handle frags without up-to-date nr_frags. -+ */ -+int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen) -+{ -+ int from, to, merge, todo; -+ struct skb_frag_struct *fragfrom, *fragto; -+ -+ BUG_ON(shiftlen > skb->len); -+ BUG_ON(skb_headlen(skb)); /* Would corrupt stream */ -+ -+ todo = shiftlen; -+ from = 0; -+ to = skb_shinfo(tgt)->nr_frags; -+ fragfrom = &skb_shinfo(skb)->frags[from]; -+ -+ /* Actual merge is delayed until the point when we know we can -+ * commit all, so that we don't have to undo partial changes -+ */ -+ if (!to || -+ !skb_can_coalesce(tgt, to, skb_frag_page(fragfrom), -+ fragfrom->page_offset)) { -+ merge = -1; -+ } else { -+ merge = to - 1; -+ -+ todo -= skb_frag_size(fragfrom); -+ if (todo < 0) { -+ if (skb_prepare_for_shift(skb) || -+ skb_prepare_for_shift(tgt)) -+ return 0; -+ -+ /* All previous frag pointers might be stale! */ -+ fragfrom = &skb_shinfo(skb)->frags[from]; -+ fragto = &skb_shinfo(tgt)->frags[merge]; -+ -+ skb_frag_size_add(fragto, shiftlen); -+ skb_frag_size_sub(fragfrom, shiftlen); -+ fragfrom->page_offset += shiftlen; -+ -+ goto onlymerged; -+ } -+ -+ from++; -+ } -+ -+ /* Skip full, not-fitting skb to avoid expensive operations */ -+ if ((shiftlen == skb->len) && -+ (skb_shinfo(skb)->nr_frags - from) > (MAX_SKB_FRAGS - to)) -+ return 0; -+ -+ if (skb_prepare_for_shift(skb) || skb_prepare_for_shift(tgt)) -+ return 0; -+ -+ while ((todo > 0) && (from < skb_shinfo(skb)->nr_frags)) { -+ if (to == MAX_SKB_FRAGS) -+ return 0; -+ -+ fragfrom = &skb_shinfo(skb)->frags[from]; -+ fragto = &skb_shinfo(tgt)->frags[to]; -+ -+ if (todo >= skb_frag_size(fragfrom)) { -+ *fragto = *fragfrom; -+ todo -= skb_frag_size(fragfrom); -+ from++; -+ to++; -+ -+ } else { -+ __skb_frag_ref(fragfrom); -+ fragto->page = fragfrom->page; -+ fragto->page_offset = fragfrom->page_offset; -+ skb_frag_size_set(fragto, todo); -+ -+ fragfrom->page_offset += todo; -+ skb_frag_size_sub(fragfrom, todo); -+ todo = 0; -+ -+ to++; -+ break; -+ } -+ } -+ -+ /* Ready to "commit" this state change to tgt */ -+ skb_shinfo(tgt)->nr_frags = to; -+ -+ if (merge >= 0) { -+ fragfrom = &skb_shinfo(skb)->frags[0]; -+ fragto = &skb_shinfo(tgt)->frags[merge]; -+ -+ skb_frag_size_add(fragto, skb_frag_size(fragfrom)); -+ __skb_frag_unref(fragfrom); -+ } -+ -+ /* Reposition in the original skb */ -+ to = 0; -+ while (from < skb_shinfo(skb)->nr_frags) -+ skb_shinfo(skb)->frags[to++] = skb_shinfo(skb)->frags[from++]; -+ skb_shinfo(skb)->nr_frags = to; -+ -+ BUG_ON(todo > 0 && !skb_shinfo(skb)->nr_frags); -+ -+onlymerged: -+ /* Most likely the tgt won't ever need its checksum anymore, skb on -+ * the other hand might need it if it needs to be resent -+ */ -+ tgt->ip_summed = CHECKSUM_PARTIAL; -+ skb->ip_summed = CHECKSUM_PARTIAL; -+ -+ /* Yak, is it really working this way? Some helper please? */ -+ skb->len -= shiftlen; -+ skb->data_len -= shiftlen; -+ skb->truesize -= shiftlen; -+ tgt->len += shiftlen; -+ tgt->data_len += shiftlen; -+ tgt->truesize += shiftlen; -+ -+ return shiftlen; -+} -+ -+/** -+ * skb_prepare_seq_read - Prepare a sequential read of skb data -+ * @skb: the buffer to read -+ * @from: lower offset of data to be read -+ * @to: upper offset of data to be read -+ * @st: state variable -+ * -+ * Initializes the specified state variable. Must be called before -+ * invoking skb_seq_read() for the first time. -+ */ -+void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from, -+ unsigned int to, struct skb_seq_state *st) -+{ -+ st->lower_offset = from; -+ st->upper_offset = to; -+ st->root_skb = st->cur_skb = skb; -+ st->frag_idx = st->stepped_offset = 0; -+ st->frag_data = NULL; -+} -+EXPORT_SYMBOL(skb_prepare_seq_read); -+ -+/** -+ * skb_seq_read - Sequentially read skb data -+ * @consumed: number of bytes consumed by the caller so far -+ * @data: destination pointer for data to be returned -+ * @st: state variable -+ * -+ * Reads a block of skb data at @consumed relative to the -+ * lower offset specified to skb_prepare_seq_read(). Assigns -+ * the head of the data block to @data and returns the length -+ * of the block or 0 if the end of the skb data or the upper -+ * offset has been reached. -+ * -+ * The caller is not required to consume all of the data -+ * returned, i.e. @consumed is typically set to the number -+ * of bytes already consumed and the next call to -+ * skb_seq_read() will return the remaining part of the block. -+ * -+ * Note 1: The size of each block of data returned can be arbitrary, -+ * this limitation is the cost for zerocopy sequential -+ * reads of potentially non linear data. -+ * -+ * Note 2: Fragment lists within fragments are not implemented -+ * at the moment, state->root_skb could be replaced with -+ * a stack for this purpose. -+ */ -+unsigned int skb_seq_read(unsigned int consumed, const u8 **data, -+ struct skb_seq_state *st) -+{ -+ unsigned int block_limit, abs_offset = consumed + st->lower_offset; -+ skb_frag_t *frag; -+ -+ if (unlikely(abs_offset >= st->upper_offset)) { -+ if (st->frag_data) { -+ kunmap_atomic(st->frag_data); -+ st->frag_data = NULL; -+ } -+ return 0; -+ } -+ -+next_skb: -+ block_limit = skb_headlen(st->cur_skb) + st->stepped_offset; -+ -+ if (abs_offset < block_limit && !st->frag_data) { -+ *data = st->cur_skb->data + (abs_offset - st->stepped_offset); -+ return block_limit - abs_offset; -+ } -+ -+ if (st->frag_idx == 0 && !st->frag_data) -+ st->stepped_offset += skb_headlen(st->cur_skb); -+ -+ while (st->frag_idx < skb_shinfo(st->cur_skb)->nr_frags) { -+ frag = &skb_shinfo(st->cur_skb)->frags[st->frag_idx]; -+ block_limit = skb_frag_size(frag) + st->stepped_offset; -+ -+ if (abs_offset < block_limit) { -+ if (!st->frag_data) -+ st->frag_data = kmap_atomic(skb_frag_page(frag)); -+ -+ *data = (u8 *) st->frag_data + frag->page_offset + -+ (abs_offset - st->stepped_offset); -+ -+ return block_limit - abs_offset; -+ } -+ -+ if (st->frag_data) { -+ kunmap_atomic(st->frag_data); -+ st->frag_data = NULL; -+ } -+ -+ st->frag_idx++; -+ st->stepped_offset += skb_frag_size(frag); -+ } -+ -+ if (st->frag_data) { -+ kunmap_atomic(st->frag_data); -+ st->frag_data = NULL; -+ } -+ -+ if (st->root_skb == st->cur_skb && skb_has_frag_list(st->root_skb)) { -+ st->cur_skb = skb_shinfo(st->root_skb)->frag_list; -+ st->frag_idx = 0; -+ goto next_skb; -+ } else if (st->cur_skb->next) { -+ st->cur_skb = st->cur_skb->next; -+ st->frag_idx = 0; -+ goto next_skb; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(skb_seq_read); -+ -+/** -+ * skb_abort_seq_read - Abort a sequential read of skb data -+ * @st: state variable -+ * -+ * Must be called if skb_seq_read() was not called until it -+ * returned 0. -+ */ -+void skb_abort_seq_read(struct skb_seq_state *st) -+{ -+ if (st->frag_data) -+ kunmap_atomic(st->frag_data); -+} -+EXPORT_SYMBOL(skb_abort_seq_read); -+ -+#define TS_SKB_CB(state) ((struct skb_seq_state *) &((state)->cb)) -+ -+static unsigned int skb_ts_get_next_block(unsigned int offset, const u8 **text, -+ struct ts_config *conf, -+ struct ts_state *state) -+{ -+ return skb_seq_read(offset, text, TS_SKB_CB(state)); -+} -+ -+static void skb_ts_finish(struct ts_config *conf, struct ts_state *state) -+{ -+ skb_abort_seq_read(TS_SKB_CB(state)); -+} -+ -+/** -+ * skb_find_text - Find a text pattern in skb data -+ * @skb: the buffer to look in -+ * @from: search offset -+ * @to: search limit -+ * @config: textsearch configuration -+ * @state: uninitialized textsearch state variable -+ * -+ * Finds a pattern in the skb data according to the specified -+ * textsearch configuration. Use textsearch_next() to retrieve -+ * subsequent occurrences of the pattern. Returns the offset -+ * to the first occurrence or UINT_MAX if no match was found. -+ */ -+unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, -+ unsigned int to, struct ts_config *config, -+ struct ts_state *state) -+{ -+ unsigned int ret; -+ -+ config->get_next_block = skb_ts_get_next_block; -+ config->finish = skb_ts_finish; -+ -+ skb_prepare_seq_read(skb, from, to, TS_SKB_CB(state)); -+ -+ ret = textsearch_find(config, state); -+ return (ret <= to - from ? ret : UINT_MAX); -+} -+EXPORT_SYMBOL(skb_find_text); -+ -+/** -+ * skb_append_datato_frags - append the user data to a skb -+ * @sk: sock structure -+ * @skb: skb structure to be appended with user data. -+ * @getfrag: call back function to be used for getting the user data -+ * @from: pointer to user message iov -+ * @length: length of the iov message -+ * -+ * Description: This procedure append the user data in the fragment part -+ * of the skb if any page alloc fails user this procedure returns -ENOMEM -+ */ -+int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, -+ int (*getfrag)(void *from, char *to, int offset, -+ int len, int odd, struct sk_buff *skb), -+ void *from, int length) -+{ -+ int frg_cnt = skb_shinfo(skb)->nr_frags; -+ int copy; -+ int offset = 0; -+ int ret; -+ struct page_frag *pfrag = ¤t->task_frag; -+ -+ do { -+ /* Return error if we don't have space for new frag */ -+ if (frg_cnt >= MAX_SKB_FRAGS) -+ return -EMSGSIZE; -+ -+ if (!sk_page_frag_refill(sk, pfrag)) -+ return -ENOMEM; -+ -+ /* copy the user data to page */ -+ copy = min_t(int, length, pfrag->size - pfrag->offset); -+ -+ ret = getfrag(from, page_address(pfrag->page) + pfrag->offset, -+ offset, copy, 0, skb); -+ if (ret < 0) -+ return -EFAULT; -+ -+ /* copy was successful so update the size parameters */ -+ skb_fill_page_desc(skb, frg_cnt, pfrag->page, pfrag->offset, -+ copy); -+ frg_cnt++; -+ pfrag->offset += copy; -+ get_page(pfrag->page); -+ -+ skb->truesize += copy; -+ atomic_add(copy, &sk->sk_wmem_alloc); -+ skb->len += copy; -+ skb->data_len += copy; -+ offset += copy; -+ length -= copy; -+ -+ } while (length > 0); -+ -+ return 0; -+} -+EXPORT_SYMBOL(skb_append_datato_frags); -+ -+/** -+ * skb_pull_rcsum - pull skb and update receive checksum -+ * @skb: buffer to update -+ * @len: length of data pulled -+ * -+ * This function performs an skb_pull on the packet and updates -+ * the CHECKSUM_COMPLETE checksum. It should be used on -+ * receive path processing instead of skb_pull unless you know -+ * that the checksum difference is zero (e.g., a valid IP header) -+ * or you are setting ip_summed to CHECKSUM_NONE. -+ */ -+unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len) -+{ -+ BUG_ON(len > skb->len); -+ skb->len -= len; -+ BUG_ON(skb->len < skb->data_len); -+ skb_postpull_rcsum(skb, skb->data, len); -+ return skb->data += len; -+} -+EXPORT_SYMBOL_GPL(skb_pull_rcsum); -+ -+/** -+ * skb_segment - Perform protocol segmentation on skb. -+ * @head_skb: buffer to segment -+ * @features: features for the output path (see dev->features) -+ * -+ * This function performs segmentation on the given skb. It returns -+ * a pointer to the first in a list of new skbs for the segments. -+ * In case of error it returns ERR_PTR(err). -+ */ -+struct sk_buff *skb_segment(struct sk_buff *head_skb, -+ netdev_features_t features) -+{ -+ struct sk_buff *segs = NULL; -+ struct sk_buff *tail = NULL; -+ struct sk_buff *list_skb = skb_shinfo(head_skb)->frag_list; -+ skb_frag_t *frag = skb_shinfo(head_skb)->frags; -+ unsigned int mss = skb_shinfo(head_skb)->gso_size; -+ unsigned int doffset = head_skb->data - skb_mac_header(head_skb); -+ struct sk_buff *frag_skb = head_skb; -+ unsigned int offset = doffset; -+ unsigned int tnl_hlen = skb_tnl_header_len(head_skb); -+ unsigned int headroom; -+ unsigned int len; -+ __be16 proto; -+ bool csum; -+ int sg = !!(features & NETIF_F_SG); -+ int nfrags = skb_shinfo(head_skb)->nr_frags; -+ int err = -ENOMEM; -+ int i = 0; -+ int pos; -+ int dummy; -+ -+ __skb_push(head_skb, doffset); -+ proto = skb_network_protocol(head_skb, &dummy); -+ if (unlikely(!proto)) -+ return ERR_PTR(-EINVAL); -+ -+ csum = !head_skb->encap_hdr_csum && -+ !!can_checksum_protocol(features, proto); -+ -+ headroom = skb_headroom(head_skb); -+ pos = skb_headlen(head_skb); -+ -+ do { -+ struct sk_buff *nskb; -+ skb_frag_t *nskb_frag; -+ int hsize; -+ int size; -+ -+ len = head_skb->len - offset; -+ if (len > mss) -+ len = mss; -+ -+ hsize = skb_headlen(head_skb) - offset; -+ if (hsize < 0) -+ hsize = 0; -+ if (hsize > len || !sg) -+ hsize = len; -+ -+ if (!hsize && i >= nfrags && skb_headlen(list_skb) && -+ (skb_headlen(list_skb) == len || sg)) { -+ BUG_ON(skb_headlen(list_skb) > len); -+ -+ i = 0; -+ nfrags = skb_shinfo(list_skb)->nr_frags; -+ frag = skb_shinfo(list_skb)->frags; -+ frag_skb = list_skb; -+ pos += skb_headlen(list_skb); -+ -+ while (pos < offset + len) { -+ BUG_ON(i >= nfrags); -+ -+ size = skb_frag_size(frag); -+ if (pos + size > offset + len) -+ break; -+ -+ i++; -+ pos += size; -+ frag++; -+ } -+ -+ nskb = skb_clone(list_skb, GFP_ATOMIC); -+ list_skb = list_skb->next; -+ -+ if (unlikely(!nskb)) -+ goto err; -+ -+ if (unlikely(pskb_trim(nskb, len))) { -+ kfree_skb(nskb); -+ goto err; -+ } -+ -+ hsize = skb_end_offset(nskb); -+ if (skb_cow_head(nskb, doffset + headroom)) { -+ kfree_skb(nskb); -+ goto err; -+ } -+ -+ nskb->truesize += skb_end_offset(nskb) - hsize; -+ skb_release_head_state(nskb); -+ __skb_push(nskb, doffset); -+ } else { -+ nskb = __alloc_skb(hsize + doffset + headroom, -+ GFP_ATOMIC, skb_alloc_rx_flag(head_skb), -+ NUMA_NO_NODE); -+ -+ if (unlikely(!nskb)) -+ goto err; -+ -+ skb_reserve(nskb, headroom); -+ __skb_put(nskb, doffset); -+ } -+ -+ if (segs) -+ tail->next = nskb; -+ else -+ segs = nskb; -+ tail = nskb; -+ -+ __copy_skb_header(nskb, head_skb); -+ -+ skb_headers_offset_update(nskb, skb_headroom(nskb) - headroom); -+ skb_reset_mac_len(nskb); -+ -+ skb_copy_from_linear_data_offset(head_skb, -tnl_hlen, -+ nskb->data - tnl_hlen, -+ doffset + tnl_hlen); -+ -+ if (nskb->len == len + doffset) -+ goto perform_csum_check; -+ -+ if (!sg) { -+ nskb->ip_summed = CHECKSUM_NONE; -+ nskb->csum = skb_copy_and_csum_bits(head_skb, offset, -+ skb_put(nskb, len), -+ len, 0); -+ SKB_GSO_CB(nskb)->csum_start = -+ skb_headroom(nskb) + doffset; -+ continue; -+ } -+ -+ nskb_frag = skb_shinfo(nskb)->frags; -+ -+ skb_copy_from_linear_data_offset(head_skb, offset, -+ skb_put(nskb, hsize), hsize); -+ -+ skb_shinfo(nskb)->tx_flags = skb_shinfo(head_skb)->tx_flags & -+ SKBTX_SHARED_FRAG; -+ -+ while (pos < offset + len) { -+ if (i >= nfrags) { -+ BUG_ON(skb_headlen(list_skb)); -+ -+ i = 0; -+ nfrags = skb_shinfo(list_skb)->nr_frags; -+ frag = skb_shinfo(list_skb)->frags; -+ frag_skb = list_skb; -+ -+ BUG_ON(!nfrags); -+ -+ list_skb = list_skb->next; -+ } -+ -+ if (unlikely(skb_shinfo(nskb)->nr_frags >= -+ MAX_SKB_FRAGS)) { -+ net_warn_ratelimited( -+ "skb_segment: too many frags: %u %u\n", -+ pos, mss); -+ goto err; -+ } -+ -+ if (unlikely(skb_orphan_frags(frag_skb, GFP_ATOMIC))) -+ goto err; -+ -+ *nskb_frag = *frag; -+ __skb_frag_ref(nskb_frag); -+ size = skb_frag_size(nskb_frag); -+ -+ if (pos < offset) { -+ nskb_frag->page_offset += offset - pos; -+ skb_frag_size_sub(nskb_frag, offset - pos); -+ } -+ -+ skb_shinfo(nskb)->nr_frags++; -+ -+ if (pos + size <= offset + len) { -+ i++; -+ frag++; -+ pos += size; -+ } else { -+ skb_frag_size_sub(nskb_frag, pos + size - (offset + len)); -+ goto skip_fraglist; -+ } -+ -+ nskb_frag++; -+ } -+ -+skip_fraglist: -+ nskb->data_len = len - hsize; -+ nskb->len += nskb->data_len; -+ nskb->truesize += nskb->data_len; -+ -+perform_csum_check: -+ if (!csum) { -+ nskb->csum = skb_checksum(nskb, doffset, -+ nskb->len - doffset, 0); -+ nskb->ip_summed = CHECKSUM_NONE; -+ SKB_GSO_CB(nskb)->csum_start = -+ skb_headroom(nskb) + doffset; -+ } -+ } while ((offset += len) < head_skb->len); -+ -+ /* Some callers want to get the end of the list. -+ * Put it in segs->prev to avoid walking the list. -+ * (see validate_xmit_skb_list() for example) -+ */ -+ segs->prev = tail; -+ return segs; -+ -+err: -+ kfree_skb_list(segs); -+ return ERR_PTR(err); -+} -+EXPORT_SYMBOL_GPL(skb_segment); -+ -+int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) -+{ -+ struct skb_shared_info *pinfo, *skbinfo = skb_shinfo(skb); -+ unsigned int offset = skb_gro_offset(skb); -+ unsigned int headlen = skb_headlen(skb); -+ struct sk_buff *nskb, *lp, *p = *head; -+ unsigned int len = skb_gro_len(skb); -+ unsigned int delta_truesize; -+ unsigned int headroom; -+ -+ if (unlikely(p->len + len >= 65536)) -+ return -E2BIG; -+ -+ lp = NAPI_GRO_CB(p)->last; -+ pinfo = skb_shinfo(lp); -+ -+ if (headlen <= offset) { -+ skb_frag_t *frag; -+ skb_frag_t *frag2; -+ int i = skbinfo->nr_frags; -+ int nr_frags = pinfo->nr_frags + i; -+ -+ if (nr_frags > MAX_SKB_FRAGS) -+ goto merge; -+ -+ offset -= headlen; -+ pinfo->nr_frags = nr_frags; -+ skbinfo->nr_frags = 0; -+ -+ frag = pinfo->frags + nr_frags; -+ frag2 = skbinfo->frags + i; -+ do { -+ *--frag = *--frag2; -+ } while (--i); -+ -+ frag->page_offset += offset; -+ skb_frag_size_sub(frag, offset); -+ -+ /* all fragments truesize : remove (head size + sk_buff) */ -+ delta_truesize = skb->truesize - -+ SKB_TRUESIZE(skb_end_offset(skb)); -+ -+ skb->truesize -= skb->data_len; -+ skb->len -= skb->data_len; -+ skb->data_len = 0; -+ -+ NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE; -+ goto done; -+ } else if (skb->head_frag) { -+ int nr_frags = pinfo->nr_frags; -+ skb_frag_t *frag = pinfo->frags + nr_frags; -+ struct page *page = virt_to_head_page(skb->head); -+ unsigned int first_size = headlen - offset; -+ unsigned int first_offset; -+ -+ if (nr_frags + 1 + skbinfo->nr_frags > MAX_SKB_FRAGS) -+ goto merge; -+ -+ first_offset = skb->data - -+ (unsigned char *)page_address(page) + -+ offset; -+ -+ pinfo->nr_frags = nr_frags + 1 + skbinfo->nr_frags; -+ -+ frag->page.p = page; -+ frag->page_offset = first_offset; -+ skb_frag_size_set(frag, first_size); -+ -+ memcpy(frag + 1, skbinfo->frags, sizeof(*frag) * skbinfo->nr_frags); -+ /* We dont need to clear skbinfo->nr_frags here */ -+ -+ delta_truesize = skb->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff)); -+ NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE_STOLEN_HEAD; -+ goto done; -+ } -+ /* switch back to head shinfo */ -+ pinfo = skb_shinfo(p); -+ -+ if (pinfo->frag_list) -+ goto merge; -+ if (skb_gro_len(p) != pinfo->gso_size) -+ return -E2BIG; -+ -+ headroom = skb_headroom(p); -+ nskb = alloc_skb(headroom + skb_gro_offset(p), GFP_ATOMIC); -+ if (unlikely(!nskb)) -+ return -ENOMEM; -+ -+ __copy_skb_header(nskb, p); -+ nskb->mac_len = p->mac_len; -+ -+ skb_reserve(nskb, headroom); -+ __skb_put(nskb, skb_gro_offset(p)); -+ -+ skb_set_mac_header(nskb, skb_mac_header(p) - p->data); -+ skb_set_network_header(nskb, skb_network_offset(p)); -+ skb_set_transport_header(nskb, skb_transport_offset(p)); -+ -+ __skb_pull(p, skb_gro_offset(p)); -+ memcpy(skb_mac_header(nskb), skb_mac_header(p), -+ p->data - skb_mac_header(p)); -+ -+ skb_shinfo(nskb)->frag_list = p; -+ skb_shinfo(nskb)->gso_size = pinfo->gso_size; -+ pinfo->gso_size = 0; -+ __skb_header_release(p); -+ NAPI_GRO_CB(nskb)->last = p; -+ -+ nskb->data_len += p->len; -+ nskb->truesize += p->truesize; -+ nskb->len += p->len; -+ -+ *head = nskb; -+ nskb->next = p->next; -+ p->next = NULL; -+ -+ p = nskb; -+ -+merge: -+ delta_truesize = skb->truesize; -+ if (offset > headlen) { -+ unsigned int eat = offset - headlen; -+ -+ skbinfo->frags[0].page_offset += eat; -+ skb_frag_size_sub(&skbinfo->frags[0], eat); -+ skb->data_len -= eat; -+ skb->len -= eat; -+ offset = headlen; -+ } -+ -+ __skb_pull(skb, offset); -+ -+ if (NAPI_GRO_CB(p)->last == p) -+ skb_shinfo(p)->frag_list = skb; -+ else -+ NAPI_GRO_CB(p)->last->next = skb; -+ NAPI_GRO_CB(p)->last = skb; -+ __skb_header_release(skb); -+ lp = p; -+ -+done: -+ NAPI_GRO_CB(p)->count++; -+ p->data_len += len; -+ p->truesize += delta_truesize; -+ p->len += len; -+ if (lp != p) { -+ lp->data_len += len; -+ lp->truesize += delta_truesize; -+ lp->len += len; -+ } -+ NAPI_GRO_CB(skb)->same_flow = 1; -+ return 0; -+} -+ -+void __init skb_init(void) -+{ -+ skbuff_head_cache = kmem_cache_create("skbuff_head_cache", -+ sizeof(struct sk_buff), -+ 0, -+ SLAB_HWCACHE_ALIGN|SLAB_PANIC, -+ NULL); -+ skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache", -+ sizeof(struct sk_buff_fclones), -+ 0, -+ SLAB_HWCACHE_ALIGN|SLAB_PANIC, -+ NULL); -+} -+ -+/** -+ * skb_to_sgvec - Fill a scatter-gather list from a socket buffer -+ * @skb: Socket buffer containing the buffers to be mapped -+ * @sg: The scatter-gather list to map into -+ * @offset: The offset into the buffer's contents to start mapping -+ * @len: Length of buffer space to be mapped -+ * -+ * Fill the specified scatter-gather list with mappings/pointers into a -+ * region of the buffer space attached to a socket buffer. -+ */ -+static int -+__skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) -+{ -+ int start = skb_headlen(skb); -+ int i, copy = start - offset; -+ struct sk_buff *frag_iter; -+ int elt = 0; -+ -+ if (copy > 0) { -+ if (copy > len) -+ copy = len; -+ sg_set_buf(sg, skb->data + offset, copy); -+ elt++; -+ if ((len -= copy) == 0) -+ return elt; -+ offset += copy; -+ } -+ -+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { -+ int end; -+ -+ WARN_ON(start > offset + len); -+ -+ end = start + skb_frag_size(&skb_shinfo(skb)->frags[i]); -+ if ((copy = end - offset) > 0) { -+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; -+ -+ if (copy > len) -+ copy = len; -+ sg_set_page(&sg[elt], skb_frag_page(frag), copy, -+ frag->page_offset+offset-start); -+ elt++; -+ if (!(len -= copy)) -+ return elt; -+ offset += copy; -+ } -+ start = end; -+ } -+ -+ skb_walk_frags(skb, frag_iter) { -+ int end; -+ -+ WARN_ON(start > offset + len); -+ -+ end = start + frag_iter->len; -+ if ((copy = end - offset) > 0) { -+ if (copy > len) -+ copy = len; -+ elt += __skb_to_sgvec(frag_iter, sg+elt, offset - start, -+ copy); -+ if ((len -= copy) == 0) -+ return elt; -+ offset += copy; -+ } -+ start = end; -+ } -+ BUG_ON(len); -+ return elt; -+} -+ -+/* As compared with skb_to_sgvec, skb_to_sgvec_nomark only map skb to given -+ * sglist without mark the sg which contain last skb data as the end. -+ * So the caller can mannipulate sg list as will when padding new data after -+ * the first call without calling sg_unmark_end to expend sg list. -+ * -+ * Scenario to use skb_to_sgvec_nomark: -+ * 1. sg_init_table -+ * 2. skb_to_sgvec_nomark(payload1) -+ * 3. skb_to_sgvec_nomark(payload2) -+ * -+ * This is equivalent to: -+ * 1. sg_init_table -+ * 2. skb_to_sgvec(payload1) -+ * 3. sg_unmark_end -+ * 4. skb_to_sgvec(payload2) -+ * -+ * When mapping mutilple payload conditionally, skb_to_sgvec_nomark -+ * is more preferable. -+ */ -+int skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg, -+ int offset, int len) -+{ -+ return __skb_to_sgvec(skb, sg, offset, len); -+} -+EXPORT_SYMBOL_GPL(skb_to_sgvec_nomark); -+ -+int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) -+{ -+ int nsg = __skb_to_sgvec(skb, sg, offset, len); -+ -+ sg_mark_end(&sg[nsg - 1]); -+ -+ return nsg; -+} -+EXPORT_SYMBOL_GPL(skb_to_sgvec); -+ -+/** -+ * skb_cow_data - Check that a socket buffer's data buffers are writable -+ * @skb: The socket buffer to check. -+ * @tailbits: Amount of trailing space to be added -+ * @trailer: Returned pointer to the skb where the @tailbits space begins -+ * -+ * Make sure that the data buffers attached to a socket buffer are -+ * writable. If they are not, private copies are made of the data buffers -+ * and the socket buffer is set to use these instead. -+ * -+ * If @tailbits is given, make sure that there is space to write @tailbits -+ * bytes of data beyond current end of socket buffer. @trailer will be -+ * set to point to the skb in which this space begins. -+ * -+ * The number of scatterlist elements required to completely map the -+ * COW'd and extended socket buffer will be returned. -+ */ -+int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer) -+{ -+ int copyflag; -+ int elt; -+ struct sk_buff *skb1, **skb_p; -+ -+ /* If skb is cloned or its head is paged, reallocate -+ * head pulling out all the pages (pages are considered not writable -+ * at the moment even if they are anonymous). -+ */ -+ if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) && -+ __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL) -+ return -ENOMEM; -+ -+ /* Easy case. Most of packets will go this way. */ -+ if (!skb_has_frag_list(skb)) { -+ /* A little of trouble, not enough of space for trailer. -+ * This should not happen, when stack is tuned to generate -+ * good frames. OK, on miss we reallocate and reserve even more -+ * space, 128 bytes is fair. */ -+ -+ if (skb_tailroom(skb) < tailbits && -+ pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, GFP_ATOMIC)) -+ return -ENOMEM; -+ -+ /* Voila! */ -+ *trailer = skb; -+ return 1; -+ } -+ -+ /* Misery. We are in troubles, going to mincer fragments... */ -+ -+ elt = 1; -+ skb_p = &skb_shinfo(skb)->frag_list; -+ copyflag = 0; -+ -+ while ((skb1 = *skb_p) != NULL) { -+ int ntail = 0; -+ -+ /* The fragment is partially pulled by someone, -+ * this can happen on input. Copy it and everything -+ * after it. */ -+ -+ if (skb_shared(skb1)) -+ copyflag = 1; -+ -+ /* If the skb is the last, worry about trailer. */ -+ -+ if (skb1->next == NULL && tailbits) { -+ if (skb_shinfo(skb1)->nr_frags || -+ skb_has_frag_list(skb1) || -+ skb_tailroom(skb1) < tailbits) -+ ntail = tailbits + 128; -+ } -+ -+ if (copyflag || -+ skb_cloned(skb1) || -+ ntail || -+ skb_shinfo(skb1)->nr_frags || -+ skb_has_frag_list(skb1)) { -+ struct sk_buff *skb2; -+ -+ /* Fuck, we are miserable poor guys... */ -+ if (ntail == 0) -+ skb2 = skb_copy(skb1, GFP_ATOMIC); -+ else -+ skb2 = skb_copy_expand(skb1, -+ skb_headroom(skb1), -+ ntail, -+ GFP_ATOMIC); -+ if (unlikely(skb2 == NULL)) -+ return -ENOMEM; -+ -+ if (skb1->sk) -+ skb_set_owner_w(skb2, skb1->sk); -+ -+ /* Looking around. Are we still alive? -+ * OK, link new skb, drop old one */ -+ -+ skb2->next = skb1->next; -+ *skb_p = skb2; -+ kfree_skb(skb1); -+ skb1 = skb2; -+ } -+ elt++; -+ *trailer = skb1; -+ skb_p = &skb1->next; -+ } -+ -+ return elt; -+} -+EXPORT_SYMBOL_GPL(skb_cow_data); -+ -+static void sock_rmem_free(struct sk_buff *skb) -+{ -+ struct sock *sk = skb->sk; -+ -+ atomic_sub(skb->truesize, &sk->sk_rmem_alloc); -+} -+ -+/* -+ * Note: We dont mem charge error packets (no sk_forward_alloc changes) -+ */ -+int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb) -+{ -+ if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= -+ (unsigned int)sk->sk_rcvbuf) -+ return -ENOMEM; -+ -+ skb_orphan(skb); -+ skb->sk = sk; -+ skb->destructor = sock_rmem_free; -+ atomic_add(skb->truesize, &sk->sk_rmem_alloc); -+ -+ /* before exiting rcu section, make sure dst is refcounted */ -+ skb_dst_force(skb); -+ -+ skb_queue_tail(&sk->sk_error_queue, skb); -+ if (!sock_flag(sk, SOCK_DEAD)) -+ sk->sk_data_ready(sk); -+ return 0; -+} -+EXPORT_SYMBOL(sock_queue_err_skb); -+ -+struct sk_buff *sock_dequeue_err_skb(struct sock *sk) -+{ -+ struct sk_buff_head *q = &sk->sk_error_queue; -+ struct sk_buff *skb, *skb_next; -+ unsigned long flags; -+ int err = 0; -+ -+ spin_lock_irqsave(&q->lock, flags); -+ skb = __skb_dequeue(q); -+ if (skb && (skb_next = skb_peek(q))) -+ err = SKB_EXT_ERR(skb_next)->ee.ee_errno; -+ spin_unlock_irqrestore(&q->lock, flags); -+ -+ sk->sk_err = err; -+ if (err) -+ sk->sk_error_report(sk); -+ -+ return skb; -+} -+EXPORT_SYMBOL(sock_dequeue_err_skb); -+ -+/** -+ * skb_clone_sk - create clone of skb, and take reference to socket -+ * @skb: the skb to clone -+ * -+ * This function creates a clone of a buffer that holds a reference on -+ * sk_refcnt. Buffers created via this function are meant to be -+ * returned using sock_queue_err_skb, or free via kfree_skb. -+ * -+ * When passing buffers allocated with this function to sock_queue_err_skb -+ * it is necessary to wrap the call with sock_hold/sock_put in order to -+ * prevent the socket from being released prior to being enqueued on -+ * the sk_error_queue. -+ */ -+struct sk_buff *skb_clone_sk(struct sk_buff *skb) -+{ -+ struct sock *sk = skb->sk; -+ struct sk_buff *clone; -+ -+ if (!sk || !atomic_inc_not_zero(&sk->sk_refcnt)) -+ return NULL; -+ -+ clone = skb_clone(skb, GFP_ATOMIC); -+ if (!clone) { -+ sock_put(sk); -+ return NULL; -+ } -+ -+ clone->sk = sk; -+ clone->destructor = sock_efree; -+ -+ return clone; -+} -+EXPORT_SYMBOL(skb_clone_sk); -+ -+static void __skb_complete_tx_timestamp(struct sk_buff *skb, -+ struct sock *sk, -+ int tstype) -+{ -+ struct sock_exterr_skb *serr; -+ int err; -+ -+ serr = SKB_EXT_ERR(skb); -+ memset(serr, 0, sizeof(*serr)); -+ serr->ee.ee_errno = ENOMSG; -+ serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING; -+ serr->ee.ee_info = tstype; -+ if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) { -+ serr->ee.ee_data = skb_shinfo(skb)->tskey; -+ if (sk->sk_protocol == IPPROTO_TCP) -+ serr->ee.ee_data -= sk->sk_tskey; -+ } -+ -+ err = sock_queue_err_skb(sk, skb); -+ -+ if (err) -+ kfree_skb(skb); -+} -+ -+void skb_complete_tx_timestamp(struct sk_buff *skb, -+ struct skb_shared_hwtstamps *hwtstamps) -+{ -+ struct sock *sk = skb->sk; -+ -+ /* take a reference to prevent skb_orphan() from freeing the socket */ -+ sock_hold(sk); -+ -+ *skb_hwtstamps(skb) = *hwtstamps; -+ __skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND); -+ -+ sock_put(sk); -+} -+EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp); -+ -+void __skb_tstamp_tx(struct sk_buff *orig_skb, -+ struct skb_shared_hwtstamps *hwtstamps, -+ struct sock *sk, int tstype) -+{ -+ struct sk_buff *skb; -+ -+ if (!sk) -+ return; -+ -+ if (hwtstamps) -+ *skb_hwtstamps(orig_skb) = *hwtstamps; -+ else -+ orig_skb->tstamp = ktime_get_real(); -+ -+ skb = skb_clone(orig_skb, GFP_ATOMIC); -+ if (!skb) -+ return; -+ -+ __skb_complete_tx_timestamp(skb, sk, tstype); -+} -+EXPORT_SYMBOL_GPL(__skb_tstamp_tx); -+ -+void skb_tstamp_tx(struct sk_buff *orig_skb, -+ struct skb_shared_hwtstamps *hwtstamps) -+{ -+ return __skb_tstamp_tx(orig_skb, hwtstamps, orig_skb->sk, -+ SCM_TSTAMP_SND); -+} -+EXPORT_SYMBOL_GPL(skb_tstamp_tx); -+ -+void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) -+{ -+ struct sock *sk = skb->sk; -+ struct sock_exterr_skb *serr; -+ int err; -+ -+ skb->wifi_acked_valid = 1; -+ skb->wifi_acked = acked; -+ -+ serr = SKB_EXT_ERR(skb); -+ memset(serr, 0, sizeof(*serr)); -+ serr->ee.ee_errno = ENOMSG; -+ serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS; -+ -+ /* take a reference to prevent skb_orphan() from freeing the socket */ -+ sock_hold(sk); -+ -+ err = sock_queue_err_skb(sk, skb); -+ if (err) -+ kfree_skb(skb); -+ -+ sock_put(sk); -+} -+EXPORT_SYMBOL_GPL(skb_complete_wifi_ack); -+ -+ -+/** -+ * skb_partial_csum_set - set up and verify partial csum values for packet -+ * @skb: the skb to set -+ * @start: the number of bytes after skb->data to start checksumming. -+ * @off: the offset from start to place the checksum. -+ * -+ * For untrusted partially-checksummed packets, we need to make sure the values -+ * for skb->csum_start and skb->csum_offset are valid so we don't oops. -+ * -+ * This function checks and sets those values and skb->ip_summed: if this -+ * returns false you should drop the packet. -+ */ -+bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off) -+{ -+ if (unlikely(start > skb_headlen(skb)) || -+ unlikely((int)start + off > skb_headlen(skb) - 2)) { -+ net_warn_ratelimited("bad partial csum: csum=%u/%u len=%u\n", -+ start, off, skb_headlen(skb)); -+ return false; -+ } -+ skb->ip_summed = CHECKSUM_PARTIAL; -+ skb->csum_start = skb_headroom(skb) + start; -+ skb->csum_offset = off; -+ skb_set_transport_header(skb, start); -+ return true; -+} -+EXPORT_SYMBOL_GPL(skb_partial_csum_set); -+ -+static int skb_maybe_pull_tail(struct sk_buff *skb, unsigned int len, -+ unsigned int max) -+{ -+ if (skb_headlen(skb) >= len) -+ return 0; -+ -+ /* If we need to pullup then pullup to the max, so we -+ * won't need to do it again. -+ */ -+ if (max > skb->len) -+ max = skb->len; -+ -+ if (__pskb_pull_tail(skb, max - skb_headlen(skb)) == NULL) -+ return -ENOMEM; -+ -+ if (skb_headlen(skb) < len) -+ return -EPROTO; -+ -+ return 0; -+} -+ -+#define MAX_TCP_HDR_LEN (15 * 4) -+ -+static __sum16 *skb_checksum_setup_ip(struct sk_buff *skb, -+ typeof(IPPROTO_IP) proto, -+ unsigned int off) -+{ -+ switch (proto) { -+ int err; -+ -+ case IPPROTO_TCP: -+ err = skb_maybe_pull_tail(skb, off + sizeof(struct tcphdr), -+ off + MAX_TCP_HDR_LEN); -+ if (!err && !skb_partial_csum_set(skb, off, -+ offsetof(struct tcphdr, -+ check))) -+ err = -EPROTO; -+ return err ? ERR_PTR(err) : &tcp_hdr(skb)->check; -+ -+ case IPPROTO_UDP: -+ err = skb_maybe_pull_tail(skb, off + sizeof(struct udphdr), -+ off + sizeof(struct udphdr)); -+ if (!err && !skb_partial_csum_set(skb, off, -+ offsetof(struct udphdr, -+ check))) -+ err = -EPROTO; -+ return err ? ERR_PTR(err) : &udp_hdr(skb)->check; -+ } -+ -+ return ERR_PTR(-EPROTO); -+} -+ -+/* This value should be large enough to cover a tagged ethernet header plus -+ * maximally sized IP and TCP or UDP headers. -+ */ -+#define MAX_IP_HDR_LEN 128 -+ -+static int skb_checksum_setup_ipv4(struct sk_buff *skb, bool recalculate) -+{ -+ unsigned int off; -+ bool fragment; -+ __sum16 *csum; -+ int err; -+ -+ fragment = false; -+ -+ err = skb_maybe_pull_tail(skb, -+ sizeof(struct iphdr), -+ MAX_IP_HDR_LEN); -+ if (err < 0) -+ goto out; -+ -+ if (ip_hdr(skb)->frag_off & htons(IP_OFFSET | IP_MF)) -+ fragment = true; -+ -+ off = ip_hdrlen(skb); -+ -+ err = -EPROTO; -+ -+ if (fragment) -+ goto out; -+ -+ csum = skb_checksum_setup_ip(skb, ip_hdr(skb)->protocol, off); -+ if (IS_ERR(csum)) -+ return PTR_ERR(csum); -+ -+ if (recalculate) -+ *csum = ~csum_tcpudp_magic(ip_hdr(skb)->saddr, -+ ip_hdr(skb)->daddr, -+ skb->len - off, -+ ip_hdr(skb)->protocol, 0); -+ err = 0; -+ -+out: -+ return err; -+} -+ -+/* This value should be large enough to cover a tagged ethernet header plus -+ * an IPv6 header, all options, and a maximal TCP or UDP header. -+ */ -+#define MAX_IPV6_HDR_LEN 256 -+ -+#define OPT_HDR(type, skb, off) \ -+ (type *)(skb_network_header(skb) + (off)) -+ -+static int skb_checksum_setup_ipv6(struct sk_buff *skb, bool recalculate) -+{ -+ int err; -+ u8 nexthdr; -+ unsigned int off; -+ unsigned int len; -+ bool fragment; -+ bool done; -+ __sum16 *csum; -+ -+ fragment = false; -+ done = false; -+ -+ off = sizeof(struct ipv6hdr); -+ -+ err = skb_maybe_pull_tail(skb, off, MAX_IPV6_HDR_LEN); -+ if (err < 0) -+ goto out; -+ -+ nexthdr = ipv6_hdr(skb)->nexthdr; -+ -+ len = sizeof(struct ipv6hdr) + ntohs(ipv6_hdr(skb)->payload_len); -+ while (off <= len && !done) { -+ switch (nexthdr) { -+ case IPPROTO_DSTOPTS: -+ case IPPROTO_HOPOPTS: -+ case IPPROTO_ROUTING: { -+ struct ipv6_opt_hdr *hp; -+ -+ err = skb_maybe_pull_tail(skb, -+ off + -+ sizeof(struct ipv6_opt_hdr), -+ MAX_IPV6_HDR_LEN); -+ if (err < 0) -+ goto out; -+ -+ hp = OPT_HDR(struct ipv6_opt_hdr, skb, off); -+ nexthdr = hp->nexthdr; -+ off += ipv6_optlen(hp); -+ break; -+ } -+ case IPPROTO_AH: { -+ struct ip_auth_hdr *hp; -+ -+ err = skb_maybe_pull_tail(skb, -+ off + -+ sizeof(struct ip_auth_hdr), -+ MAX_IPV6_HDR_LEN); -+ if (err < 0) -+ goto out; -+ -+ hp = OPT_HDR(struct ip_auth_hdr, skb, off); -+ nexthdr = hp->nexthdr; -+ off += ipv6_authlen(hp); -+ break; -+ } -+ case IPPROTO_FRAGMENT: { -+ struct frag_hdr *hp; -+ -+ err = skb_maybe_pull_tail(skb, -+ off + -+ sizeof(struct frag_hdr), -+ MAX_IPV6_HDR_LEN); -+ if (err < 0) -+ goto out; -+ -+ hp = OPT_HDR(struct frag_hdr, skb, off); -+ -+ if (hp->frag_off & htons(IP6_OFFSET | IP6_MF)) -+ fragment = true; -+ -+ nexthdr = hp->nexthdr; -+ off += sizeof(struct frag_hdr); -+ break; -+ } -+ default: -+ done = true; -+ break; -+ } -+ } -+ -+ err = -EPROTO; -+ -+ if (!done || fragment) -+ goto out; -+ -+ csum = skb_checksum_setup_ip(skb, nexthdr, off); -+ if (IS_ERR(csum)) -+ return PTR_ERR(csum); -+ -+ if (recalculate) -+ *csum = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, -+ &ipv6_hdr(skb)->daddr, -+ skb->len - off, nexthdr, 0); -+ err = 0; -+ -+out: -+ return err; -+} -+ -+/** -+ * skb_checksum_setup - set up partial checksum offset -+ * @skb: the skb to set up -+ * @recalculate: if true the pseudo-header checksum will be recalculated -+ */ -+int skb_checksum_setup(struct sk_buff *skb, bool recalculate) -+{ -+ int err; -+ -+ switch (skb->protocol) { -+ case htons(ETH_P_IP): -+ err = skb_checksum_setup_ipv4(skb, recalculate); -+ break; -+ -+ case htons(ETH_P_IPV6): -+ err = skb_checksum_setup_ipv6(skb, recalculate); -+ break; -+ -+ default: -+ err = -EPROTO; -+ break; -+ } -+ -+ return err; -+} -+EXPORT_SYMBOL(skb_checksum_setup); -+ -+void __skb_warn_lro_forwarding(const struct sk_buff *skb) -+{ -+ net_warn_ratelimited("%s: received packets cannot be forwarded while LRO is enabled\n", -+ skb->dev->name); -+} -+EXPORT_SYMBOL(__skb_warn_lro_forwarding); -+ -+void kfree_skb_partial(struct sk_buff *skb, bool head_stolen) -+{ -+ if (head_stolen) { -+ skb_release_head_state(skb); -+ kmem_cache_free(skbuff_head_cache, skb); -+ } else { -+ __kfree_skb(skb); -+ } -+} -+EXPORT_SYMBOL(kfree_skb_partial); -+ -+/** -+ * skb_try_coalesce - try to merge skb to prior one -+ * @to: prior buffer -+ * @from: buffer to add -+ * @fragstolen: pointer to boolean -+ * @delta_truesize: how much more was allocated than was requested -+ */ -+bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, -+ bool *fragstolen, int *delta_truesize) -+{ -+ int i, delta, len = from->len; -+ -+ *fragstolen = false; -+ -+ if (skb_cloned(to)) -+ return false; -+ -+ if (len <= skb_tailroom(to)) { -+ if (len) -+ BUG_ON(skb_copy_bits(from, 0, skb_put(to, len), len)); -+ *delta_truesize = 0; -+ return true; -+ } -+ -+ if (skb_has_frag_list(to) || skb_has_frag_list(from)) -+ return false; -+ -+ if (skb_headlen(from) != 0) { -+ struct page *page; -+ unsigned int offset; -+ -+ if (skb_shinfo(to)->nr_frags + -+ skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS) -+ return false; -+ -+ if (skb_head_is_locked(from)) -+ return false; -+ -+ delta = from->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff)); -+ -+ page = virt_to_head_page(from->head); -+ offset = from->data - (unsigned char *)page_address(page); -+ -+ skb_fill_page_desc(to, skb_shinfo(to)->nr_frags, -+ page, offset, skb_headlen(from)); -+ *fragstolen = true; -+ } else { -+ if (skb_shinfo(to)->nr_frags + -+ skb_shinfo(from)->nr_frags > MAX_SKB_FRAGS) -+ return false; -+ -+ delta = from->truesize - SKB_TRUESIZE(skb_end_offset(from)); -+ } -+ -+ WARN_ON_ONCE(delta < len); -+ -+ memcpy(skb_shinfo(to)->frags + skb_shinfo(to)->nr_frags, -+ skb_shinfo(from)->frags, -+ skb_shinfo(from)->nr_frags * sizeof(skb_frag_t)); -+ skb_shinfo(to)->nr_frags += skb_shinfo(from)->nr_frags; -+ -+ if (!skb_cloned(from)) -+ skb_shinfo(from)->nr_frags = 0; -+ -+ /* if the skb is not cloned this does nothing -+ * since we set nr_frags to 0. -+ */ -+ for (i = 0; i < skb_shinfo(from)->nr_frags; i++) -+ skb_frag_ref(from, i); -+ -+ to->truesize += delta; -+ to->len += len; -+ to->data_len += len; -+ -+ *delta_truesize = delta; -+ return true; -+} -+EXPORT_SYMBOL(skb_try_coalesce); -+ -+/** -+ * skb_scrub_packet - scrub an skb -+ * -+ * @skb: buffer to clean -+ * @xnet: packet is crossing netns -+ * -+ * skb_scrub_packet can be used after encapsulating or decapsulting a packet -+ * into/from a tunnel. Some information have to be cleared during these -+ * operations. -+ * skb_scrub_packet can also be used to clean a skb before injecting it in -+ * another namespace (@xnet == true). We have to clear all information in the -+ * skb that could impact namespace isolation. -+ */ -+void skb_scrub_packet(struct sk_buff *skb, bool xnet) -+{ -+ skb->tstamp.tv64 = 0; -+ skb->pkt_type = PACKET_HOST; -+ skb->skb_iif = 0; -+ skb->ignore_df = 0; -+ skb_dst_drop(skb); -+ secpath_reset(skb); -+ nf_reset(skb); -+ nf_reset_trace(skb); -+ -+ if (!xnet) -+ return; -+ -+ skb_orphan(skb); -+ skb->mark = 0; -+} -+EXPORT_SYMBOL_GPL(skb_scrub_packet); -+ -+/** -+ * skb_gso_transport_seglen - Return length of individual segments of a gso packet -+ * -+ * @skb: GSO skb -+ * -+ * skb_gso_transport_seglen is used to determine the real size of the -+ * individual segments, including Layer4 headers (TCP/UDP). -+ * -+ * The MAC/L2 or network (IP, IPv6) headers are not accounted for. -+ */ -+unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) -+{ -+ const struct skb_shared_info *shinfo = skb_shinfo(skb); -+ unsigned int thlen = 0; -+ -+ if (skb->encapsulation) { -+ thlen = skb_inner_transport_header(skb) - -+ skb_transport_header(skb); -+ -+ if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) -+ thlen += inner_tcp_hdrlen(skb); -+ } else if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) { -+ thlen = tcp_hdrlen(skb); -+ } -+ /* UFO sets gso_size to the size of the fragmentation -+ * payload, i.e. the size of the L4 (UDP) header is already -+ * accounted for. -+ */ -+ return thlen + shinfo->gso_size; -+} -+EXPORT_SYMBOL_GPL(skb_gso_transport_seglen); -+ -+static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb) -+{ -+ if (skb_cow(skb, skb_headroom(skb)) < 0) { -+ kfree_skb(skb); -+ return NULL; -+ } -+ -+ memmove(skb->data - ETH_HLEN, skb->data - VLAN_ETH_HLEN, 2 * ETH_ALEN); -+ skb->mac_header += VLAN_HLEN; -+ return skb; -+} -+ -+struct sk_buff *skb_vlan_untag(struct sk_buff *skb) -+{ -+ struct vlan_hdr *vhdr; -+ u16 vlan_tci; -+ -+ if (unlikely(vlan_tx_tag_present(skb))) { -+ /* vlan_tci is already set-up so leave this for another time */ -+ return skb; -+ } -+ -+ skb = skb_share_check(skb, GFP_ATOMIC); -+ if (unlikely(!skb)) -+ goto err_free; -+ -+ if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) -+ goto err_free; -+ -+ vhdr = (struct vlan_hdr *)skb->data; -+ vlan_tci = ntohs(vhdr->h_vlan_TCI); -+ __vlan_hwaccel_put_tag(skb, skb->protocol, vlan_tci); -+ -+ skb_pull_rcsum(skb, VLAN_HLEN); -+ vlan_set_encap_proto(skb, vhdr); -+ -+ skb = skb_reorder_vlan_header(skb); -+ if (unlikely(!skb)) -+ goto err_free; -+ -+ skb_reset_network_header(skb); -+ skb_reset_transport_header(skb); -+ skb_reset_mac_len(skb); -+ -+ return skb; -+ -+err_free: -+ kfree_skb(skb); -+ return NULL; -+} -+EXPORT_SYMBOL(skb_vlan_untag); -+ -+/** -+ * alloc_skb_with_frags - allocate skb with page frags -+ * -+ * @header_len: size of linear part -+ * @data_len: needed length in frags -+ * @max_page_order: max page order desired. -+ * @errcode: pointer to error code if any -+ * @gfp_mask: allocation mask -+ * -+ * This can be used to allocate a paged skb, given a maximal order for frags. -+ */ -+struct sk_buff *alloc_skb_with_frags(unsigned long header_len, -+ unsigned long data_len, -+ int max_page_order, -+ int *errcode, -+ gfp_t gfp_mask) -+{ -+ int npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT; -+ unsigned long chunk; -+ struct sk_buff *skb; -+ struct page *page; -+ gfp_t gfp_head; -+ int i; -+ -+ *errcode = -EMSGSIZE; -+ /* Note this test could be relaxed, if we succeed to allocate -+ * high order pages... -+ */ -+ if (npages > MAX_SKB_FRAGS) -+ return NULL; -+ -+ gfp_head = gfp_mask; -+ if (gfp_head & __GFP_WAIT) -+ gfp_head |= __GFP_REPEAT; -+ -+ *errcode = -ENOBUFS; -+ skb = alloc_skb(header_len, gfp_head); -+ if (!skb) -+ return NULL; -+ -+ skb->truesize += npages << PAGE_SHIFT; -+ -+ for (i = 0; npages > 0; i++) { -+ int order = max_page_order; -+ -+ while (order) { -+ if (npages >= 1 << order) { -+ page = alloc_pages(gfp_mask | -+ __GFP_COMP | -+ __GFP_NOWARN | -+ __GFP_NORETRY, -+ order); -+ if (page) -+ goto fill_page; -+ /* Do not retry other high order allocations */ -+ order = 1; -+ max_page_order = 0; -+ } -+ order--; -+ } -+ page = alloc_page(gfp_mask); -+ if (!page) -+ goto failure; -+fill_page: -+ chunk = min_t(unsigned long, data_len, -+ PAGE_SIZE << order); -+ skb_fill_page_desc(skb, i, page, 0, chunk); -+ data_len -= chunk; -+ npages -= 1 << order; -+ } -+ return skb; -+ -+failure: -+ kfree_skb(skb); -+ return NULL; -+} -+EXPORT_SYMBOL(alloc_skb_with_frags); -diff -Nur linux-3.18.14.orig/net/core/sock.c linux-3.18.14-rt/net/core/sock.c ---- linux-3.18.14.orig/net/core/sock.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/core/sock.c 2015-05-31 15:32:49.433635358 -0500 -@@ -2345,12 +2345,11 @@ - if (sk->sk_lock.owned) - __lock_sock(sk); - sk->sk_lock.owned = 1; -- spin_unlock(&sk->sk_lock.slock); -+ spin_unlock_bh(&sk->sk_lock.slock); - /* - * The sk_lock has mutex_lock() semantics here: - */ - mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_); -- local_bh_enable(); - } - EXPORT_SYMBOL(lock_sock_nested); - -diff -Nur linux-3.18.14.orig/net/ipv4/icmp.c linux-3.18.14-rt/net/ipv4/icmp.c ---- linux-3.18.14.orig/net/ipv4/icmp.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/ipv4/icmp.c 2015-05-31 15:32:49.457635357 -0500 -@@ -69,6 +69,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -864,6 +865,30 @@ - } - - /* -+ * 32bit and 64bit have different timestamp length, so we check for -+ * the cookie at offset 20 and verify it is repeated at offset 50 -+ */ -+#define CO_POS0 20 -+#define CO_POS1 50 -+#define CO_SIZE sizeof(int) -+#define ICMP_SYSRQ_SIZE 57 -+ -+/* -+ * We got a ICMP_SYSRQ_SIZE sized ping request. Check for the cookie -+ * pattern and if it matches send the next byte as a trigger to sysrq. -+ */ -+static void icmp_check_sysrq(struct net *net, struct sk_buff *skb) -+{ -+ int cookie = htonl(net->ipv4.sysctl_icmp_echo_sysrq); -+ char *p = skb->data; -+ -+ if (!memcmp(&cookie, p + CO_POS0, CO_SIZE) && -+ !memcmp(&cookie, p + CO_POS1, CO_SIZE) && -+ p[CO_POS0 + CO_SIZE] == p[CO_POS1 + CO_SIZE]) -+ handle_sysrq(p[CO_POS0 + CO_SIZE]); -+} -+ -+/* - * Handle ICMP_ECHO ("ping") requests. - * - * RFC 1122: 3.2.2.6 MUST have an echo server that answers ICMP echo -@@ -890,6 +915,11 @@ - icmp_param.data_len = skb->len; - icmp_param.head_len = sizeof(struct icmphdr); - icmp_reply(&icmp_param, skb); -+ -+ if (skb->len == ICMP_SYSRQ_SIZE && -+ net->ipv4.sysctl_icmp_echo_sysrq) { -+ icmp_check_sysrq(net, skb); -+ } - } - } - -diff -Nur linux-3.18.14.orig/net/ipv4/sysctl_net_ipv4.c linux-3.18.14-rt/net/ipv4/sysctl_net_ipv4.c ---- linux-3.18.14.orig/net/ipv4/sysctl_net_ipv4.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/ipv4/sysctl_net_ipv4.c 2015-05-31 15:32:49.485635357 -0500 -@@ -779,6 +779,13 @@ - .proc_handler = proc_dointvec - }, - { -+ .procname = "icmp_echo_sysrq", -+ .data = &init_net.ipv4.sysctl_icmp_echo_sysrq, -+ .maxlen = sizeof(int), -+ .mode = 0644, -+ .proc_handler = proc_dointvec -+ }, -+ { - .procname = "icmp_ignore_bogus_error_responses", - .data = &init_net.ipv4.sysctl_icmp_ignore_bogus_error_responses, - .maxlen = sizeof(int), -diff -Nur linux-3.18.14.orig/net/mac80211/rx.c linux-3.18.14-rt/net/mac80211/rx.c ---- linux-3.18.14.orig/net/mac80211/rx.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/mac80211/rx.c 2015-05-31 15:32:49.501635357 -0500 -@@ -3360,7 +3360,7 @@ - struct ieee80211_supported_band *sband; - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - -- WARN_ON_ONCE(softirq_count() == 0); -+ WARN_ON_ONCE_NONRT(softirq_count() == 0); - - if (WARN_ON(status->band >= IEEE80211_NUM_BANDS)) - goto drop; -diff -Nur linux-3.18.14.orig/net/netfilter/core.c linux-3.18.14-rt/net/netfilter/core.c ---- linux-3.18.14.orig/net/netfilter/core.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/netfilter/core.c 2015-05-31 15:32:49.549635357 -0500 -@@ -21,11 +21,17 @@ - #include - #include - #include -+#include - #include - #include - - #include "nf_internals.h" - -+#ifdef CONFIG_PREEMPT_RT_BASE -+DEFINE_LOCAL_IRQ_LOCK(xt_write_lock); -+EXPORT_PER_CPU_SYMBOL(xt_write_lock); -+#endif -+ - static DEFINE_MUTEX(afinfo_mutex); - - const struct nf_afinfo __rcu *nf_afinfo[NFPROTO_NUMPROTO] __read_mostly; -diff -Nur linux-3.18.14.orig/net/packet/af_packet.c linux-3.18.14-rt/net/packet/af_packet.c ---- linux-3.18.14.orig/net/packet/af_packet.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/packet/af_packet.c 2015-05-31 15:32:49.557635357 -0500 -@@ -63,6 +63,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -692,7 +693,7 @@ - if (BLOCK_NUM_PKTS(pbd)) { - while (atomic_read(&pkc->blk_fill_in_prog)) { - /* Waiting for skb_copy_bits to finish... */ -- cpu_relax(); -+ cpu_chill(); - } - } - -@@ -943,7 +944,7 @@ - if (!(status & TP_STATUS_BLK_TMO)) { - while (atomic_read(&pkc->blk_fill_in_prog)) { - /* Waiting for skb_copy_bits to finish... */ -- cpu_relax(); -+ cpu_chill(); - } - } - prb_close_block(pkc, pbd, po, status); -diff -Nur linux-3.18.14.orig/net/rds/ib_rdma.c linux-3.18.14-rt/net/rds/ib_rdma.c ---- linux-3.18.14.orig/net/rds/ib_rdma.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/rds/ib_rdma.c 2015-05-31 15:32:49.573635357 -0500 -@@ -34,6 +34,7 @@ - #include - #include - #include -+#include - - #include "rds.h" - #include "ib.h" -@@ -286,7 +287,7 @@ - for_each_online_cpu(cpu) { - flag = &per_cpu(clean_list_grace, cpu); - while (test_bit(CLEAN_LIST_BUSY_BIT, flag)) -- cpu_relax(); -+ cpu_chill(); - } - } - -diff -Nur linux-3.18.14.orig/net/sched/sch_generic.c linux-3.18.14-rt/net/sched/sch_generic.c ---- linux-3.18.14.orig/net/sched/sch_generic.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/sched/sch_generic.c 2015-05-31 15:32:49.593635356 -0500 -@@ -894,7 +894,7 @@ - /* Wait for outstanding qdisc_run calls. */ - list_for_each_entry(dev, head, close_list) - while (some_qdisc_is_busy(dev)) -- yield(); -+ msleep(1); - } - - void dev_deactivate(struct net_device *dev) -diff -Nur linux-3.18.14.orig/net/sunrpc/svc_xprt.c linux-3.18.14-rt/net/sunrpc/svc_xprt.c ---- linux-3.18.14.orig/net/sunrpc/svc_xprt.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/net/sunrpc/svc_xprt.c 2015-05-31 15:32:49.617635356 -0500 -@@ -357,7 +357,7 @@ - return; - } - -- cpu = get_cpu(); -+ cpu = get_cpu_light(); - pool = svc_pool_for_cpu(xprt->xpt_server, cpu); - spin_lock_bh(&pool->sp_lock); - -@@ -390,7 +390,7 @@ - } - - spin_unlock_bh(&pool->sp_lock); -- put_cpu(); -+ put_cpu_light(); - } - - /* -diff -Nur linux-3.18.14.orig/scripts/mkcompile_h linux-3.18.14-rt/scripts/mkcompile_h ---- linux-3.18.14.orig/scripts/mkcompile_h 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/scripts/mkcompile_h 2015-05-31 15:32:49.641635356 -0500 -@@ -4,7 +4,8 @@ - ARCH=$2 - SMP=$3 - PREEMPT=$4 --CC=$5 -+RT=$5 -+CC=$6 - - vecho() { [ "${quiet}" = "silent_" ] || echo "$@" ; } - -@@ -57,6 +58,7 @@ - CONFIG_FLAGS="" - if [ -n "$SMP" ] ; then CONFIG_FLAGS="SMP"; fi - if [ -n "$PREEMPT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT"; fi -+if [ -n "$RT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS RT"; fi - UTS_VERSION="$UTS_VERSION $CONFIG_FLAGS $TIMESTAMP" - - # Truncate to maximum length -diff -Nur linux-3.18.14.orig/sound/core/pcm_native.c linux-3.18.14-rt/sound/core/pcm_native.c ---- linux-3.18.14.orig/sound/core/pcm_native.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/sound/core/pcm_native.c 2015-05-31 15:32:49.661635356 -0500 -@@ -104,7 +104,7 @@ - void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) - { - if (!substream->pcm->nonatomic) -- local_irq_disable(); -+ local_irq_disable_nort(); - snd_pcm_stream_lock(substream); - } - EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); -@@ -113,7 +113,7 @@ - { - snd_pcm_stream_unlock(substream); - if (!substream->pcm->nonatomic) -- local_irq_enable(); -+ local_irq_enable_nort(); - } - EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq); - -@@ -121,7 +121,7 @@ - { - unsigned long flags = 0; - if (!substream->pcm->nonatomic) -- local_irq_save(flags); -+ local_irq_save_nort(flags); - snd_pcm_stream_lock(substream); - return flags; - } -@@ -132,7 +132,7 @@ - { - snd_pcm_stream_unlock(substream); - if (!substream->pcm->nonatomic) -- local_irq_restore(flags); -+ local_irq_restore_nort(flags); - } - EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); - -diff -Nur linux-3.18.14.orig/virt/kvm/async_pf.c linux-3.18.14-rt/virt/kvm/async_pf.c ---- linux-3.18.14.orig/virt/kvm/async_pf.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/virt/kvm/async_pf.c 2015-05-31 15:32:49.661635356 -0500 -@@ -94,8 +94,8 @@ - - trace_kvm_async_pf_completed(addr, gva); - -- if (waitqueue_active(&vcpu->wq)) -- wake_up_interruptible(&vcpu->wq); -+ if (swaitqueue_active(&vcpu->wq)) -+ swait_wake_interruptible(&vcpu->wq); - - mmput(mm); - kvm_put_kvm(vcpu->kvm); -diff -Nur linux-3.18.14.orig/virt/kvm/kvm_main.c linux-3.18.14-rt/virt/kvm/kvm_main.c ---- linux-3.18.14.orig/virt/kvm/kvm_main.c 2015-05-20 10:04:50.000000000 -0500 -+++ linux-3.18.14-rt/virt/kvm/kvm_main.c 2015-05-31 15:32:49.697635356 -0500 -@@ -221,7 +221,7 @@ - vcpu->kvm = kvm; - vcpu->vcpu_id = id; - vcpu->pid = NULL; -- init_waitqueue_head(&vcpu->wq); -+ init_swait_head(&vcpu->wq); - kvm_async_pf_vcpu_init(vcpu); - - page = alloc_page(GFP_KERNEL | __GFP_ZERO); -@@ -1741,10 +1741,10 @@ - */ - void kvm_vcpu_block(struct kvm_vcpu *vcpu) - { -- DEFINE_WAIT(wait); -+ DEFINE_SWAITER(wait); - - for (;;) { -- prepare_to_wait(&vcpu->wq, &wait, TASK_INTERRUPTIBLE); -+ swait_prepare(&vcpu->wq, &wait, TASK_INTERRUPTIBLE); - - if (kvm_arch_vcpu_runnable(vcpu)) { - kvm_make_request(KVM_REQ_UNHALT, vcpu); -@@ -1758,7 +1758,7 @@ - schedule(); - } - -- finish_wait(&vcpu->wq, &wait); -+ swait_finish(&vcpu->wq, &wait); - } - EXPORT_SYMBOL_GPL(kvm_vcpu_block); - -@@ -1770,11 +1770,11 @@ - { - int me; - int cpu = vcpu->cpu; -- wait_queue_head_t *wqp; -+ struct swait_head *wqp; - - wqp = kvm_arch_vcpu_wq(vcpu); -- if (waitqueue_active(wqp)) { -- wake_up_interruptible(wqp); -+ if (swaitqueue_active(wqp)) { -+ swait_wake_interruptible(wqp); - ++vcpu->stat.halt_wakeup; - } - -@@ -1879,7 +1879,7 @@ - continue; - if (vcpu == me) - continue; -- if (waitqueue_active(&vcpu->wq) && !kvm_arch_vcpu_runnable(vcpu)) -+ if (swaitqueue_active(&vcpu->wq) && !kvm_arch_vcpu_runnable(vcpu)) - continue; - if (!kvm_vcpu_eligible_for_directed_yield(vcpu)) - continue; diff --git a/target/linux/patches/3.18.14/regmap-bool.patch b/target/linux/patches/3.18.14/regmap-bool.patch deleted file mode 100644 index 5c0ff5e2c..000000000 --- a/target/linux/patches/3.18.14/regmap-bool.patch +++ /dev/null @@ -1,27 +0,0 @@ -diff -Nur linux-3.18.5.orig/drivers/base/regmap/Kconfig linux-3.18.5/drivers/base/regmap/Kconfig ---- linux-3.18.5.orig/drivers/base/regmap/Kconfig 2015-01-30 02:41:03.000000000 +0100 -+++ linux-3.18.5/drivers/base/regmap/Kconfig 2015-02-02 11:53:27.854106073 +0100 -@@ -10,19 +10,19 @@ - bool - - config REGMAP_I2C -- tristate -+ bool - depends on I2C - - config REGMAP_SPI -- tristate -+ bool - depends on SPI - - config REGMAP_SPMI -- tristate -+ bool - depends on SPMI - - config REGMAP_MMIO -- tristate -+ bool - - config REGMAP_IRQ - bool diff --git a/target/linux/patches/3.18.14/relocs.patch b/target/linux/patches/3.18.14/relocs.patch deleted file mode 100644 index 69a7c88a9..000000000 --- a/target/linux/patches/3.18.14/relocs.patch +++ /dev/null @@ -1,2709 +0,0 @@ -diff -Nur linux-3.13.6.orig/arch/x86/tools/relocs.c linux-3.13.6/arch/x86/tools/relocs.c ---- linux-3.13.6.orig/arch/x86/tools/relocs.c 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/arch/x86/tools/relocs.c 2014-03-15 19:39:45.000000000 +0100 -@@ -126,6 +126,7 @@ - - if (err) { - regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); -+ printf("foo: %s\n", sym_regex[i]); - die("%s", errbuf); - } - } -diff -Nur linux-3.13.6.orig/arch/x86/tools/relocs.h linux-3.13.6/arch/x86/tools/relocs.h ---- linux-3.13.6.orig/arch/x86/tools/relocs.h 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/arch/x86/tools/relocs.h 2014-03-15 18:48:40.000000000 +0100 -@@ -9,11 +9,19 @@ - #include - #include - #include -+#ifdef __linux__ - #include - #include - #define USE_BSD - #include -+#else -+#include "elf.h" -+#endif -+#ifdef __APPLE__ -+#include -+#else - #include -+#endif - #include - - void die(char *fmt, ...); -diff -Nur linux-3.13.6.orig/tools/include/elf.h linux-3.13.6/tools/include/elf.h ---- linux-3.13.6.orig/tools/include/elf.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.13.6/tools/include/elf.h 2014-03-15 18:47:36.000000000 +0100 -@@ -0,0 +1,2671 @@ -+#ifndef _ELF_H -+#define _ELF_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#include -+ -+typedef uint16_t Elf32_Half; -+typedef uint16_t Elf64_Half; -+ -+typedef uint32_t Elf32_Word; -+typedef int32_t Elf32_Sword; -+typedef uint32_t Elf64_Word; -+typedef int32_t Elf64_Sword; -+ -+typedef uint64_t Elf32_Xword; -+typedef int64_t Elf32_Sxword; -+typedef uint64_t Elf64_Xword; -+typedef int64_t Elf64_Sxword; -+ -+typedef uint32_t Elf32_Addr; -+typedef uint64_t Elf64_Addr; -+ -+typedef uint32_t Elf32_Off; -+typedef uint64_t Elf64_Off; -+ -+typedef uint16_t Elf32_Section; -+typedef uint16_t Elf64_Section; -+ -+typedef Elf32_Half Elf32_Versym; -+typedef Elf64_Half Elf64_Versym; -+ -+#define EI_NIDENT (16) -+ -+typedef struct { -+ unsigned char e_ident[EI_NIDENT]; -+ Elf32_Half e_type; -+ Elf32_Half e_machine; -+ Elf32_Word e_version; -+ Elf32_Addr e_entry; -+ Elf32_Off e_phoff; -+ Elf32_Off e_shoff; -+ Elf32_Word e_flags; -+ Elf32_Half e_ehsize; -+ Elf32_Half e_phentsize; -+ Elf32_Half e_phnum; -+ Elf32_Half e_shentsize; -+ Elf32_Half e_shnum; -+ Elf32_Half e_shstrndx; -+} Elf32_Ehdr; -+ -+typedef struct { -+ unsigned char e_ident[EI_NIDENT]; -+ Elf64_Half e_type; -+ Elf64_Half e_machine; -+ Elf64_Word e_version; -+ Elf64_Addr e_entry; -+ Elf64_Off e_phoff; -+ Elf64_Off e_shoff; -+ Elf64_Word e_flags; -+ Elf64_Half e_ehsize; -+ Elf64_Half e_phentsize; -+ Elf64_Half e_phnum; -+ Elf64_Half e_shentsize; -+ Elf64_Half e_shnum; -+ Elf64_Half e_shstrndx; -+} Elf64_Ehdr; -+ -+#define EI_MAG0 0 -+#define ELFMAG0 0x7f -+ -+#define EI_MAG1 1 -+#define ELFMAG1 'E' -+ -+#define EI_MAG2 2 -+#define ELFMAG2 'L' -+ -+#define EI_MAG3 3 -+#define ELFMAG3 'F' -+ -+ -+#define ELFMAG "\177ELF" -+#define SELFMAG 4 -+ -+#define EI_CLASS 4 -+#define ELFCLASSNONE 0 -+#define ELFCLASS32 1 -+#define ELFCLASS64 2 -+#define ELFCLASSNUM 3 -+ -+#define EI_DATA 5 -+#define ELFDATANONE 0 -+#define ELFDATA2LSB 1 -+#define ELFDATA2MSB 2 -+#define ELFDATANUM 3 -+ -+#define EI_VERSION 6 -+ -+ -+#define EI_OSABI 7 -+#define ELFOSABI_NONE 0 -+#define ELFOSABI_SYSV 0 -+#define ELFOSABI_HPUX 1 -+#define ELFOSABI_NETBSD 2 -+#define ELFOSABI_LINUX 3 -+#define ELFOSABI_GNU 3 -+#define ELFOSABI_SOLARIS 6 -+#define ELFOSABI_AIX 7 -+#define ELFOSABI_IRIX 8 -+#define ELFOSABI_FREEBSD 9 -+#define ELFOSABI_TRU64 10 -+#define ELFOSABI_MODESTO 11 -+#define ELFOSABI_OPENBSD 12 -+#define ELFOSABI_ARM 97 -+#define ELFOSABI_STANDALONE 255 -+ -+#define EI_ABIVERSION 8 -+ -+#define EI_PAD 9 -+ -+ -+ -+#define ET_NONE 0 -+#define ET_REL 1 -+#define ET_EXEC 2 -+#define ET_DYN 3 -+#define ET_CORE 4 -+#define ET_NUM 5 -+#define ET_LOOS 0xfe00 -+#define ET_HIOS 0xfeff -+#define ET_LOPROC 0xff00 -+#define ET_HIPROC 0xffff -+ -+ -+ -+#define EM_NONE 0 -+#define EM_M32 1 -+#define EM_SPARC 2 -+#define EM_386 3 -+#define EM_68K 4 -+#define EM_88K 5 -+#define EM_860 7 -+#define EM_MIPS 8 -+#define EM_S370 9 -+#define EM_MIPS_RS3_LE 10 -+ -+#define EM_PARISC 15 -+#define EM_VPP500 17 -+#define EM_SPARC32PLUS 18 -+#define EM_960 19 -+#define EM_PPC 20 -+#define EM_PPC64 21 -+#define EM_S390 22 -+ -+#define EM_V800 36 -+#define EM_FR20 37 -+#define EM_RH32 38 -+#define EM_RCE 39 -+#define EM_ARM 40 -+#define EM_FAKE_ALPHA 41 -+#define EM_SH 42 -+#define EM_SPARCV9 43 -+#define EM_TRICORE 44 -+#define EM_ARC 45 -+#define EM_H8_300 46 -+#define EM_H8_300H 47 -+#define EM_H8S 48 -+#define EM_H8_500 49 -+#define EM_IA_64 50 -+#define EM_MIPS_X 51 -+#define EM_COLDFIRE 52 -+#define EM_68HC12 53 -+#define EM_MMA 54 -+#define EM_PCP 55 -+#define EM_NCPU 56 -+#define EM_NDR1 57 -+#define EM_STARCORE 58 -+#define EM_ME16 59 -+#define EM_ST100 60 -+#define EM_TINYJ 61 -+#define EM_X86_64 62 -+#define EM_PDSP 63 -+ -+#define EM_FX66 66 -+#define EM_ST9PLUS 67 -+#define EM_ST7 68 -+#define EM_68HC16 69 -+#define EM_68HC11 70 -+#define EM_68HC08 71 -+#define EM_68HC05 72 -+#define EM_SVX 73 -+#define EM_ST19 74 -+#define EM_VAX 75 -+#define EM_CRIS 76 -+#define EM_JAVELIN 77 -+#define EM_FIREPATH 78 -+#define EM_ZSP 79 -+#define EM_MMIX 80 -+#define EM_HUANY 81 -+#define EM_PRISM 82 -+#define EM_AVR 83 -+#define EM_FR30 84 -+#define EM_D10V 85 -+#define EM_D30V 86 -+#define EM_V850 87 -+#define EM_M32R 88 -+#define EM_MN10300 89 -+#define EM_MN10200 90 -+#define EM_PJ 91 -+#define EM_OPENRISC 92 -+#define EM_ARC_A5 93 -+#define EM_XTENSA 94 -+#define EM_AARCH64 183 -+#define EM_TILEPRO 188 -+#define EM_MICROBLAZE 189 -+#define EM_TILEGX 191 -+#define EM_NUM 192 -+#define EM_ALPHA 0x9026 -+ -+#define EV_NONE 0 -+#define EV_CURRENT 1 -+#define EV_NUM 2 -+ -+typedef struct { -+ Elf32_Word sh_name; -+ Elf32_Word sh_type; -+ Elf32_Word sh_flags; -+ Elf32_Addr sh_addr; -+ Elf32_Off sh_offset; -+ Elf32_Word sh_size; -+ Elf32_Word sh_link; -+ Elf32_Word sh_info; -+ Elf32_Word sh_addralign; -+ Elf32_Word sh_entsize; -+} Elf32_Shdr; -+ -+typedef struct { -+ Elf64_Word sh_name; -+ Elf64_Word sh_type; -+ Elf64_Xword sh_flags; -+ Elf64_Addr sh_addr; -+ Elf64_Off sh_offset; -+ Elf64_Xword sh_size; -+ Elf64_Word sh_link; -+ Elf64_Word sh_info; -+ Elf64_Xword sh_addralign; -+ Elf64_Xword sh_entsize; -+} Elf64_Shdr; -+ -+ -+ -+#define SHN_UNDEF 0 -+#define SHN_LORESERVE 0xff00 -+#define SHN_LOPROC 0xff00 -+#define SHN_BEFORE 0xff00 -+ -+#define SHN_AFTER 0xff01 -+ -+#define SHN_HIPROC 0xff1f -+#define SHN_LOOS 0xff20 -+#define SHN_HIOS 0xff3f -+#define SHN_ABS 0xfff1 -+#define SHN_COMMON 0xfff2 -+#define SHN_XINDEX 0xffff -+#define SHN_HIRESERVE 0xffff -+ -+ -+ -+#define SHT_NULL 0 -+#define SHT_PROGBITS 1 -+#define SHT_SYMTAB 2 -+#define SHT_STRTAB 3 -+#define SHT_RELA 4 -+#define SHT_HASH 5 -+#define SHT_DYNAMIC 6 -+#define SHT_NOTE 7 -+#define SHT_NOBITS 8 -+#define SHT_REL 9 -+#define SHT_SHLIB 10 -+#define SHT_DYNSYM 11 -+#define SHT_INIT_ARRAY 14 -+#define SHT_FINI_ARRAY 15 -+#define SHT_PREINIT_ARRAY 16 -+#define SHT_GROUP 17 -+#define SHT_SYMTAB_SHNDX 18 -+#define SHT_NUM 19 -+#define SHT_LOOS 0x60000000 -+#define SHT_GNU_ATTRIBUTES 0x6ffffff5 -+#define SHT_GNU_HASH 0x6ffffff6 -+#define SHT_GNU_LIBLIST 0x6ffffff7 -+#define SHT_CHECKSUM 0x6ffffff8 -+#define SHT_LOSUNW 0x6ffffffa -+#define SHT_SUNW_move 0x6ffffffa -+#define SHT_SUNW_COMDAT 0x6ffffffb -+#define SHT_SUNW_syminfo 0x6ffffffc -+#define SHT_GNU_verdef 0x6ffffffd -+#define SHT_GNU_verneed 0x6ffffffe -+#define SHT_GNU_versym 0x6fffffff -+#define SHT_HISUNW 0x6fffffff -+#define SHT_HIOS 0x6fffffff -+#define SHT_LOPROC 0x70000000 -+#define SHT_HIPROC 0x7fffffff -+#define SHT_LOUSER 0x80000000 -+#define SHT_HIUSER 0x8fffffff -+ -+#define SHF_WRITE (1 << 0) -+#define SHF_ALLOC (1 << 1) -+#define SHF_EXECINSTR (1 << 2) -+#define SHF_MERGE (1 << 4) -+#define SHF_STRINGS (1 << 5) -+#define SHF_INFO_LINK (1 << 6) -+#define SHF_LINK_ORDER (1 << 7) -+#define SHF_OS_NONCONFORMING (1 << 8) -+ -+#define SHF_GROUP (1 << 9) -+#define SHF_TLS (1 << 10) -+#define SHF_MASKOS 0x0ff00000 -+#define SHF_MASKPROC 0xf0000000 -+#define SHF_ORDERED (1 << 30) -+#define SHF_EXCLUDE (1 << 31) -+ -+#define GRP_COMDAT 0x1 -+ -+typedef struct { -+ Elf32_Word st_name; -+ Elf32_Addr st_value; -+ Elf32_Word st_size; -+ unsigned char st_info; -+ unsigned char st_other; -+ Elf32_Section st_shndx; -+} Elf32_Sym; -+ -+typedef struct { -+ Elf64_Word st_name; -+ unsigned char st_info; -+ unsigned char st_other; -+ Elf64_Section st_shndx; -+ Elf64_Addr st_value; -+ Elf64_Xword st_size; -+} Elf64_Sym; -+ -+typedef struct { -+ Elf32_Half si_boundto; -+ Elf32_Half si_flags; -+} Elf32_Syminfo; -+ -+typedef struct { -+ Elf64_Half si_boundto; -+ Elf64_Half si_flags; -+} Elf64_Syminfo; -+ -+#define SYMINFO_BT_SELF 0xffff -+#define SYMINFO_BT_PARENT 0xfffe -+#define SYMINFO_BT_LOWRESERVE 0xff00 -+ -+#define SYMINFO_FLG_DIRECT 0x0001 -+#define SYMINFO_FLG_PASSTHRU 0x0002 -+#define SYMINFO_FLG_COPY 0x0004 -+#define SYMINFO_FLG_LAZYLOAD 0x0008 -+ -+#define SYMINFO_NONE 0 -+#define SYMINFO_CURRENT 1 -+#define SYMINFO_NUM 2 -+ -+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -+#define ELF32_ST_TYPE(val) ((val) & 0xf) -+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -+ -+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) -+ -+#define STB_LOCAL 0 -+#define STB_GLOBAL 1 -+#define STB_WEAK 2 -+#define STB_NUM 3 -+#define STB_LOOS 10 -+#define STB_GNU_UNIQUE 10 -+#define STB_HIOS 12 -+#define STB_LOPROC 13 -+#define STB_HIPROC 15 -+ -+#define STT_NOTYPE 0 -+#define STT_OBJECT 1 -+#define STT_FUNC 2 -+#define STT_SECTION 3 -+#define STT_FILE 4 -+#define STT_COMMON 5 -+#define STT_TLS 6 -+#define STT_NUM 7 -+#define STT_LOOS 10 -+#define STT_GNU_IFUNC 10 -+#define STT_HIOS 12 -+#define STT_LOPROC 13 -+#define STT_HIPROC 15 -+ -+#define STN_UNDEF 0 -+ -+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) -+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) -+ -+#define STV_DEFAULT 0 -+#define STV_INTERNAL 1 -+#define STV_HIDDEN 2 -+#define STV_PROTECTED 3 -+ -+ -+ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; -+ Elf32_Word r_info; -+} Elf32_Rel; -+ -+typedef struct { -+ Elf64_Addr r_offset; -+ Elf64_Xword r_info; -+} Elf64_Rel; -+ -+ -+ -+typedef struct { -+ Elf32_Addr r_offset; -+ Elf32_Word r_info; -+ Elf32_Sword r_addend; -+} Elf32_Rela; -+ -+typedef struct { -+ Elf64_Addr r_offset; -+ Elf64_Xword r_info; -+ Elf64_Sxword r_addend; -+} Elf64_Rela; -+ -+ -+ -+#define ELF32_R_SYM(val) ((val) >> 8) -+#define ELF32_R_TYPE(val) ((val) & 0xff) -+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) -+ -+#define ELF64_R_SYM(i) ((i) >> 32) -+#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) -+ -+ -+ -+typedef struct { -+ Elf32_Word p_type; -+ Elf32_Off p_offset; -+ Elf32_Addr p_vaddr; -+ Elf32_Addr p_paddr; -+ Elf32_Word p_filesz; -+ Elf32_Word p_memsz; -+ Elf32_Word p_flags; -+ Elf32_Word p_align; -+} Elf32_Phdr; -+ -+typedef struct { -+ Elf64_Word p_type; -+ Elf64_Word p_flags; -+ Elf64_Off p_offset; -+ Elf64_Addr p_vaddr; -+ Elf64_Addr p_paddr; -+ Elf64_Xword p_filesz; -+ Elf64_Xword p_memsz; -+ Elf64_Xword p_align; -+} Elf64_Phdr; -+ -+ -+ -+#define PT_NULL 0 -+#define PT_LOAD 1 -+#define PT_DYNAMIC 2 -+#define PT_INTERP 3 -+#define PT_NOTE 4 -+#define PT_SHLIB 5 -+#define PT_PHDR 6 -+#define PT_TLS 7 -+#define PT_NUM 8 -+#define PT_LOOS 0x60000000 -+#define PT_GNU_EH_FRAME 0x6474e550 -+#define PT_GNU_STACK 0x6474e551 -+#define PT_GNU_RELRO 0x6474e552 -+#define PT_LOSUNW 0x6ffffffa -+#define PT_SUNWBSS 0x6ffffffa -+#define PT_SUNWSTACK 0x6ffffffb -+#define PT_HISUNW 0x6fffffff -+#define PT_HIOS 0x6fffffff -+#define PT_LOPROC 0x70000000 -+#define PT_HIPROC 0x7fffffff -+ -+ -+#define PN_XNUM 0xffff -+ -+ -+#define PF_X (1 << 0) -+#define PF_W (1 << 1) -+#define PF_R (1 << 2) -+#define PF_MASKOS 0x0ff00000 -+#define PF_MASKPROC 0xf0000000 -+ -+ -+ -+#define NT_PRSTATUS 1 -+#define NT_FPREGSET 2 -+#define NT_PRPSINFO 3 -+#define NT_PRXREG 4 -+#define NT_TASKSTRUCT 4 -+#define NT_PLATFORM 5 -+#define NT_AUXV 6 -+#define NT_GWINDOWS 7 -+#define NT_ASRS 8 -+#define NT_PSTATUS 10 -+#define NT_PSINFO 13 -+#define NT_PRCRED 14 -+#define NT_UTSNAME 15 -+#define NT_LWPSTATUS 16 -+#define NT_LWPSINFO 17 -+#define NT_PRFPXREG 20 -+#define NT_SIGINFO 0x53494749 -+#define NT_FILE 0x46494c45 -+#define NT_PRXFPREG 0x46e62b7f -+#define NT_PPC_VMX 0x100 -+#define NT_PPC_SPE 0x101 -+#define NT_PPC_VSX 0x102 -+#define NT_386_TLS 0x200 -+#define NT_386_IOPERM 0x201 -+#define NT_X86_XSTATE 0x202 -+#define NT_S390_HIGH_GPRS 0x300 -+#define NT_S390_TIMER 0x301 -+#define NT_S390_TODCMP 0x302 -+#define NT_S390_TODPREG 0x303 -+#define NT_S390_CTRS 0x304 -+#define NT_S390_PREFIX 0x305 -+#define NT_S390_LAST_BREAK 0x306 -+#define NT_S390_SYSTEM_CALL 0x307 -+#define NT_S390_TDB 0x308 -+#define NT_ARM_VFP 0x400 -+#define NT_ARM_TLS 0x401 -+#define NT_ARM_HW_BREAK 0x402 -+#define NT_ARM_HW_WATCH 0x403 -+#define NT_METAG_CBUF 0x500 -+#define NT_METAG_RPIPE 0x501 -+#define NT_METAG_TLS 0x502 -+#define NT_VERSION 1 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Sword d_tag; -+ union { -+ Elf32_Word d_val; -+ Elf32_Addr d_ptr; -+ } d_un; -+} Elf32_Dyn; -+ -+typedef struct { -+ Elf64_Sxword d_tag; -+ union { -+ Elf64_Xword d_val; -+ Elf64_Addr d_ptr; -+ } d_un; -+} Elf64_Dyn; -+ -+ -+ -+#define DT_NULL 0 -+#define DT_NEEDED 1 -+#define DT_PLTRELSZ 2 -+#define DT_PLTGOT 3 -+#define DT_HASH 4 -+#define DT_STRTAB 5 -+#define DT_SYMTAB 6 -+#define DT_RELA 7 -+#define DT_RELASZ 8 -+#define DT_RELAENT 9 -+#define DT_STRSZ 10 -+#define DT_SYMENT 11 -+#define DT_INIT 12 -+#define DT_FINI 13 -+#define DT_SONAME 14 -+#define DT_RPATH 15 -+#define DT_SYMBOLIC 16 -+#define DT_REL 17 -+#define DT_RELSZ 18 -+#define DT_RELENT 19 -+#define DT_PLTREL 20 -+#define DT_DEBUG 21 -+#define DT_TEXTREL 22 -+#define DT_JMPREL 23 -+#define DT_BIND_NOW 24 -+#define DT_INIT_ARRAY 25 -+#define DT_FINI_ARRAY 26 -+#define DT_INIT_ARRAYSZ 27 -+#define DT_FINI_ARRAYSZ 28 -+#define DT_RUNPATH 29 -+#define DT_FLAGS 30 -+#define DT_ENCODING 32 -+#define DT_PREINIT_ARRAY 32 -+#define DT_PREINIT_ARRAYSZ 33 -+#define DT_NUM 34 -+#define DT_LOOS 0x6000000d -+#define DT_HIOS 0x6ffff000 -+#define DT_LOPROC 0x70000000 -+#define DT_HIPROC 0x7fffffff -+#define DT_PROCNUM DT_MIPS_NUM -+ -+#define DT_VALRNGLO 0x6ffffd00 -+#define DT_GNU_PRELINKED 0x6ffffdf5 -+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 -+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 -+#define DT_CHECKSUM 0x6ffffdf8 -+#define DT_PLTPADSZ 0x6ffffdf9 -+#define DT_MOVEENT 0x6ffffdfa -+#define DT_MOVESZ 0x6ffffdfb -+#define DT_FEATURE_1 0x6ffffdfc -+#define DT_POSFLAG_1 0x6ffffdfd -+ -+#define DT_SYMINSZ 0x6ffffdfe -+#define DT_SYMINENT 0x6ffffdff -+#define DT_VALRNGHI 0x6ffffdff -+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) -+#define DT_VALNUM 12 -+ -+#define DT_ADDRRNGLO 0x6ffffe00 -+#define DT_GNU_HASH 0x6ffffef5 -+#define DT_TLSDESC_PLT 0x6ffffef6 -+#define DT_TLSDESC_GOT 0x6ffffef7 -+#define DT_GNU_CONFLICT 0x6ffffef8 -+#define DT_GNU_LIBLIST 0x6ffffef9 -+#define DT_CONFIG 0x6ffffefa -+#define DT_DEPAUDIT 0x6ffffefb -+#define DT_AUDIT 0x6ffffefc -+#define DT_PLTPAD 0x6ffffefd -+#define DT_MOVETAB 0x6ffffefe -+#define DT_SYMINFO 0x6ffffeff -+#define DT_ADDRRNGHI 0x6ffffeff -+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) -+#define DT_ADDRNUM 11 -+ -+ -+ -+#define DT_VERSYM 0x6ffffff0 -+ -+#define DT_RELACOUNT 0x6ffffff9 -+#define DT_RELCOUNT 0x6ffffffa -+ -+ -+#define DT_FLAGS_1 0x6ffffffb -+#define DT_VERDEF 0x6ffffffc -+ -+#define DT_VERDEFNUM 0x6ffffffd -+#define DT_VERNEED 0x6ffffffe -+ -+#define DT_VERNEEDNUM 0x6fffffff -+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) -+#define DT_VERSIONTAGNUM 16 -+ -+ -+ -+#define DT_AUXILIARY 0x7ffffffd -+#define DT_FILTER 0x7fffffff -+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -+#define DT_EXTRANUM 3 -+ -+ -+#define DF_ORIGIN 0x00000001 -+#define DF_SYMBOLIC 0x00000002 -+#define DF_TEXTREL 0x00000004 -+#define DF_BIND_NOW 0x00000008 -+#define DF_STATIC_TLS 0x00000010 -+ -+ -+ -+#define DF_1_NOW 0x00000001 -+#define DF_1_GLOBAL 0x00000002 -+#define DF_1_GROUP 0x00000004 -+#define DF_1_NODELETE 0x00000008 -+#define DF_1_LOADFLTR 0x00000010 -+#define DF_1_INITFIRST 0x00000020 -+#define DF_1_NOOPEN 0x00000040 -+#define DF_1_ORIGIN 0x00000080 -+#define DF_1_DIRECT 0x00000100 -+#define DF_1_TRANS 0x00000200 -+#define DF_1_INTERPOSE 0x00000400 -+#define DF_1_NODEFLIB 0x00000800 -+#define DF_1_NODUMP 0x00001000 -+#define DF_1_CONFALT 0x00002000 -+#define DF_1_ENDFILTEE 0x00004000 -+#define DF_1_DISPRELDNE 0x00008000 -+#define DF_1_DISPRELPND 0x00010000 -+#define DF_1_NODIRECT 0x00020000 -+#define DF_1_IGNMULDEF 0x00040000 -+#define DF_1_NOKSYMS 0x00080000 -+#define DF_1_NOHDR 0x00100000 -+#define DF_1_EDITED 0x00200000 -+#define DF_1_NORELOC 0x00400000 -+#define DF_1_SYMINTPOSE 0x00800000 -+#define DF_1_GLOBAUDIT 0x01000000 -+#define DF_1_SINGLETON 0x02000000 -+ -+#define DTF_1_PARINIT 0x00000001 -+#define DTF_1_CONFEXP 0x00000002 -+ -+ -+#define DF_P1_LAZYLOAD 0x00000001 -+#define DF_P1_GROUPPERM 0x00000002 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Half vd_version; -+ Elf32_Half vd_flags; -+ Elf32_Half vd_ndx; -+ Elf32_Half vd_cnt; -+ Elf32_Word vd_hash; -+ Elf32_Word vd_aux; -+ Elf32_Word vd_next; -+} Elf32_Verdef; -+ -+typedef struct { -+ Elf64_Half vd_version; -+ Elf64_Half vd_flags; -+ Elf64_Half vd_ndx; -+ Elf64_Half vd_cnt; -+ Elf64_Word vd_hash; -+ Elf64_Word vd_aux; -+ Elf64_Word vd_next; -+} Elf64_Verdef; -+ -+ -+ -+#define VER_DEF_NONE 0 -+#define VER_DEF_CURRENT 1 -+#define VER_DEF_NUM 2 -+ -+ -+#define VER_FLG_BASE 0x1 -+#define VER_FLG_WEAK 0x2 -+ -+ -+#define VER_NDX_LOCAL 0 -+#define VER_NDX_GLOBAL 1 -+#define VER_NDX_LORESERVE 0xff00 -+#define VER_NDX_ELIMINATE 0xff01 -+ -+ -+ -+typedef struct { -+ Elf32_Word vda_name; -+ Elf32_Word vda_next; -+} Elf32_Verdaux; -+ -+typedef struct { -+ Elf64_Word vda_name; -+ Elf64_Word vda_next; -+} Elf64_Verdaux; -+ -+ -+ -+ -+typedef struct { -+ Elf32_Half vn_version; -+ Elf32_Half vn_cnt; -+ Elf32_Word vn_file; -+ Elf32_Word vn_aux; -+ Elf32_Word vn_next; -+} Elf32_Verneed; -+ -+typedef struct { -+ Elf64_Half vn_version; -+ Elf64_Half vn_cnt; -+ Elf64_Word vn_file; -+ Elf64_Word vn_aux; -+ Elf64_Word vn_next; -+} Elf64_Verneed; -+ -+ -+ -+#define VER_NEED_NONE 0 -+#define VER_NEED_CURRENT 1 -+#define VER_NEED_NUM 2 -+ -+ -+ -+typedef struct { -+ Elf32_Word vna_hash; -+ Elf32_Half vna_flags; -+ Elf32_Half vna_other; -+ Elf32_Word vna_name; -+ Elf32_Word vna_next; -+} Elf32_Vernaux; -+ -+typedef struct { -+ Elf64_Word vna_hash; -+ Elf64_Half vna_flags; -+ Elf64_Half vna_other; -+ Elf64_Word vna_name; -+ Elf64_Word vna_next; -+} Elf64_Vernaux; -+ -+ -+ -+#define VER_FLG_WEAK 0x2 -+ -+ -+ -+typedef struct { -+ uint32_t a_type; -+ union { -+ uint32_t a_val; -+ } a_un; -+} Elf32_auxv_t; -+ -+typedef struct { -+ uint64_t a_type; -+ union { -+ uint64_t a_val; -+ } a_un; -+} Elf64_auxv_t; -+ -+ -+ -+#define AT_NULL 0 -+#define AT_IGNORE 1 -+#define AT_EXECFD 2 -+#define AT_PHDR 3 -+#define AT_PHENT 4 -+#define AT_PHNUM 5 -+#define AT_PAGESZ 6 -+#define AT_BASE 7 -+#define AT_FLAGS 8 -+#define AT_ENTRY 9 -+#define AT_NOTELF 10 -+#define AT_UID 11 -+#define AT_EUID 12 -+#define AT_GID 13 -+#define AT_EGID 14 -+#define AT_CLKTCK 17 -+ -+ -+#define AT_PLATFORM 15 -+#define AT_HWCAP 16 -+ -+ -+ -+ -+#define AT_FPUCW 18 -+ -+ -+#define AT_DCACHEBSIZE 19 -+#define AT_ICACHEBSIZE 20 -+#define AT_UCACHEBSIZE 21 -+ -+ -+ -+#define AT_IGNOREPPC 22 -+ -+#define AT_SECURE 23 -+ -+#define AT_BASE_PLATFORM 24 -+ -+#define AT_RANDOM 25 -+ -+#define AT_HWCAP2 26 -+ -+#define AT_EXECFN 31 -+ -+ -+ -+#define AT_SYSINFO 32 -+#define AT_SYSINFO_EHDR 33 -+ -+ -+ -+#define AT_L1I_CACHESHAPE 34 -+#define AT_L1D_CACHESHAPE 35 -+#define AT_L2_CACHESHAPE 36 -+#define AT_L3_CACHESHAPE 37 -+ -+ -+ -+ -+typedef struct { -+ Elf32_Word n_namesz; -+ Elf32_Word n_descsz; -+ Elf32_Word n_type; -+} Elf32_Nhdr; -+ -+typedef struct { -+ Elf64_Word n_namesz; -+ Elf64_Word n_descsz; -+ Elf64_Word n_type; -+} Elf64_Nhdr; -+ -+ -+ -+ -+#define ELF_NOTE_SOLARIS "SUNW Solaris" -+ -+ -+#define ELF_NOTE_GNU "GNU" -+ -+ -+ -+ -+ -+#define ELF_NOTE_PAGESIZE_HINT 1 -+ -+ -+#define NT_GNU_ABI_TAG 1 -+#define ELF_NOTE_ABI NT_GNU_ABI_TAG -+ -+ -+ -+#define ELF_NOTE_OS_LINUX 0 -+#define ELF_NOTE_OS_GNU 1 -+#define ELF_NOTE_OS_SOLARIS2 2 -+#define ELF_NOTE_OS_FREEBSD 3 -+ -+#define NT_GNU_BUILD_ID 3 -+#define NT_GNU_GOLD_VERSION 4 -+ -+ -+ -+typedef struct { -+ Elf32_Xword m_value; -+ Elf32_Word m_info; -+ Elf32_Word m_poffset; -+ Elf32_Half m_repeat; -+ Elf32_Half m_stride; -+} Elf32_Move; -+ -+typedef struct { -+ Elf64_Xword m_value; -+ Elf64_Xword m_info; -+ Elf64_Xword m_poffset; -+ Elf64_Half m_repeat; -+ Elf64_Half m_stride; -+} Elf64_Move; -+ -+ -+#define ELF32_M_SYM(info) ((info) >> 8) -+#define ELF32_M_SIZE(info) ((unsigned char) (info)) -+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) -+ -+#define ELF64_M_SYM(info) ELF32_M_SYM (info) -+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) -+ -+#define EF_CPU32 0x00810000 -+ -+#define R_68K_NONE 0 -+#define R_68K_32 1 -+#define R_68K_16 2 -+#define R_68K_8 3 -+#define R_68K_PC32 4 -+#define R_68K_PC16 5 -+#define R_68K_PC8 6 -+#define R_68K_GOT32 7 -+#define R_68K_GOT16 8 -+#define R_68K_GOT8 9 -+#define R_68K_GOT32O 10 -+#define R_68K_GOT16O 11 -+#define R_68K_GOT8O 12 -+#define R_68K_PLT32 13 -+#define R_68K_PLT16 14 -+#define R_68K_PLT8 15 -+#define R_68K_PLT32O 16 -+#define R_68K_PLT16O 17 -+#define R_68K_PLT8O 18 -+#define R_68K_COPY 19 -+#define R_68K_GLOB_DAT 20 -+#define R_68K_JMP_SLOT 21 -+#define R_68K_RELATIVE 22 -+#define R_68K_NUM 23 -+ -+#define R_386_NONE 0 -+#define R_386_32 1 -+#define R_386_PC32 2 -+#define R_386_GOT32 3 -+#define R_386_PLT32 4 -+#define R_386_COPY 5 -+#define R_386_GLOB_DAT 6 -+#define R_386_JMP_SLOT 7 -+#define R_386_RELATIVE 8 -+#define R_386_GOTOFF 9 -+#define R_386_GOTPC 10 -+#define R_386_32PLT 11 -+#define R_386_TLS_TPOFF 14 -+#define R_386_TLS_IE 15 -+#define R_386_TLS_GOTIE 16 -+#define R_386_TLS_LE 17 -+#define R_386_TLS_GD 18 -+#define R_386_TLS_LDM 19 -+#define R_386_16 20 -+#define R_386_PC16 21 -+#define R_386_8 22 -+#define R_386_PC8 23 -+#define R_386_TLS_GD_32 24 -+#define R_386_TLS_GD_PUSH 25 -+#define R_386_TLS_GD_CALL 26 -+#define R_386_TLS_GD_POP 27 -+#define R_386_TLS_LDM_32 28 -+#define R_386_TLS_LDM_PUSH 29 -+#define R_386_TLS_LDM_CALL 30 -+#define R_386_TLS_LDM_POP 31 -+#define R_386_TLS_LDO_32 32 -+#define R_386_TLS_IE_32 33 -+#define R_386_TLS_LE_32 34 -+#define R_386_TLS_DTPMOD32 35 -+#define R_386_TLS_DTPOFF32 36 -+#define R_386_TLS_TPOFF32 37 -+#define R_386_SIZE32 38 -+#define R_386_TLS_GOTDESC 39 -+#define R_386_TLS_DESC_CALL 40 -+#define R_386_TLS_DESC 41 -+#define R_386_IRELATIVE 42 -+#define R_386_NUM 43 -+ -+ -+ -+ -+ -+#define STT_SPARC_REGISTER 13 -+ -+ -+ -+#define EF_SPARCV9_MM 3 -+#define EF_SPARCV9_TSO 0 -+#define EF_SPARCV9_PSO 1 -+#define EF_SPARCV9_RMO 2 -+#define EF_SPARC_LEDATA 0x800000 -+#define EF_SPARC_EXT_MASK 0xFFFF00 -+#define EF_SPARC_32PLUS 0x000100 -+#define EF_SPARC_SUN_US1 0x000200 -+#define EF_SPARC_HAL_R1 0x000400 -+#define EF_SPARC_SUN_US3 0x000800 -+ -+ -+ -+#define R_SPARC_NONE 0 -+#define R_SPARC_8 1 -+#define R_SPARC_16 2 -+#define R_SPARC_32 3 -+#define R_SPARC_DISP8 4 -+#define R_SPARC_DISP16 5 -+#define R_SPARC_DISP32 6 -+#define R_SPARC_WDISP30 7 -+#define R_SPARC_WDISP22 8 -+#define R_SPARC_HI22 9 -+#define R_SPARC_22 10 -+#define R_SPARC_13 11 -+#define R_SPARC_LO10 12 -+#define R_SPARC_GOT10 13 -+#define R_SPARC_GOT13 14 -+#define R_SPARC_GOT22 15 -+#define R_SPARC_PC10 16 -+#define R_SPARC_PC22 17 -+#define R_SPARC_WPLT30 18 -+#define R_SPARC_COPY 19 -+#define R_SPARC_GLOB_DAT 20 -+#define R_SPARC_JMP_SLOT 21 -+#define R_SPARC_RELATIVE 22 -+#define R_SPARC_UA32 23 -+ -+ -+ -+#define R_SPARC_PLT32 24 -+#define R_SPARC_HIPLT22 25 -+#define R_SPARC_LOPLT10 26 -+#define R_SPARC_PCPLT32 27 -+#define R_SPARC_PCPLT22 28 -+#define R_SPARC_PCPLT10 29 -+#define R_SPARC_10 30 -+#define R_SPARC_11 31 -+#define R_SPARC_64 32 -+#define R_SPARC_OLO10 33 -+#define R_SPARC_HH22 34 -+#define R_SPARC_HM10 35 -+#define R_SPARC_LM22 36 -+#define R_SPARC_PC_HH22 37 -+#define R_SPARC_PC_HM10 38 -+#define R_SPARC_PC_LM22 39 -+#define R_SPARC_WDISP16 40 -+#define R_SPARC_WDISP19 41 -+#define R_SPARC_GLOB_JMP 42 -+#define R_SPARC_7 43 -+#define R_SPARC_5 44 -+#define R_SPARC_6 45 -+#define R_SPARC_DISP64 46 -+#define R_SPARC_PLT64 47 -+#define R_SPARC_HIX22 48 -+#define R_SPARC_LOX10 49 -+#define R_SPARC_H44 50 -+#define R_SPARC_M44 51 -+#define R_SPARC_L44 52 -+#define R_SPARC_REGISTER 53 -+#define R_SPARC_UA64 54 -+#define R_SPARC_UA16 55 -+#define R_SPARC_TLS_GD_HI22 56 -+#define R_SPARC_TLS_GD_LO10 57 -+#define R_SPARC_TLS_GD_ADD 58 -+#define R_SPARC_TLS_GD_CALL 59 -+#define R_SPARC_TLS_LDM_HI22 60 -+#define R_SPARC_TLS_LDM_LO10 61 -+#define R_SPARC_TLS_LDM_ADD 62 -+#define R_SPARC_TLS_LDM_CALL 63 -+#define R_SPARC_TLS_LDO_HIX22 64 -+#define R_SPARC_TLS_LDO_LOX10 65 -+#define R_SPARC_TLS_LDO_ADD 66 -+#define R_SPARC_TLS_IE_HI22 67 -+#define R_SPARC_TLS_IE_LO10 68 -+#define R_SPARC_TLS_IE_LD 69 -+#define R_SPARC_TLS_IE_LDX 70 -+#define R_SPARC_TLS_IE_ADD 71 -+#define R_SPARC_TLS_LE_HIX22 72 -+#define R_SPARC_TLS_LE_LOX10 73 -+#define R_SPARC_TLS_DTPMOD32 74 -+#define R_SPARC_TLS_DTPMOD64 75 -+#define R_SPARC_TLS_DTPOFF32 76 -+#define R_SPARC_TLS_DTPOFF64 77 -+#define R_SPARC_TLS_TPOFF32 78 -+#define R_SPARC_TLS_TPOFF64 79 -+#define R_SPARC_GOTDATA_HIX22 80 -+#define R_SPARC_GOTDATA_LOX10 81 -+#define R_SPARC_GOTDATA_OP_HIX22 82 -+#define R_SPARC_GOTDATA_OP_LOX10 83 -+#define R_SPARC_GOTDATA_OP 84 -+#define R_SPARC_H34 85 -+#define R_SPARC_SIZE32 86 -+#define R_SPARC_SIZE64 87 -+#define R_SPARC_GNU_VTINHERIT 250 -+#define R_SPARC_GNU_VTENTRY 251 -+#define R_SPARC_REV32 252 -+ -+#define R_SPARC_NUM 253 -+ -+ -+ -+#define DT_SPARC_REGISTER 0x70000001 -+#define DT_SPARC_NUM 2 -+ -+ -+#define EF_MIPS_NOREORDER 1 -+#define EF_MIPS_PIC 2 -+#define EF_MIPS_CPIC 4 -+#define EF_MIPS_XGOT 8 -+#define EF_MIPS_64BIT_WHIRL 16 -+#define EF_MIPS_ABI2 32 -+#define EF_MIPS_ABI_ON32 64 -+#define EF_MIPS_ARCH 0xf0000000 -+ -+ -+ -+#define EF_MIPS_ARCH_1 0x00000000 -+#define EF_MIPS_ARCH_2 0x10000000 -+#define EF_MIPS_ARCH_3 0x20000000 -+#define EF_MIPS_ARCH_4 0x30000000 -+#define EF_MIPS_ARCH_5 0x40000000 -+#define EF_MIPS_ARCH_32 0x50000000 -+#define EF_MIPS_ARCH_64 0x60000000 -+#define EF_MIPS_ARCH_32R2 0x70000000 -+#define EF_MIPS_ARCH_64R2 0x80000000 -+ -+ -+#define E_MIPS_ARCH_1 0x00000000 -+#define E_MIPS_ARCH_2 0x10000000 -+#define E_MIPS_ARCH_3 0x20000000 -+#define E_MIPS_ARCH_4 0x30000000 -+#define E_MIPS_ARCH_5 0x40000000 -+#define E_MIPS_ARCH_32 0x50000000 -+#define E_MIPS_ARCH_64 0x60000000 -+ -+ -+ -+#define SHN_MIPS_ACOMMON 0xff00 -+#define SHN_MIPS_TEXT 0xff01 -+#define SHN_MIPS_DATA 0xff02 -+#define SHN_MIPS_SCOMMON 0xff03 -+#define SHN_MIPS_SUNDEFINED 0xff04 -+ -+ -+ -+#define SHT_MIPS_LIBLIST 0x70000000 -+#define SHT_MIPS_MSYM 0x70000001 -+#define SHT_MIPS_CONFLICT 0x70000002 -+#define SHT_MIPS_GPTAB 0x70000003 -+#define SHT_MIPS_UCODE 0x70000004 -+#define SHT_MIPS_DEBUG 0x70000005 -+#define SHT_MIPS_REGINFO 0x70000006 -+#define SHT_MIPS_PACKAGE 0x70000007 -+#define SHT_MIPS_PACKSYM 0x70000008 -+#define SHT_MIPS_RELD 0x70000009 -+#define SHT_MIPS_IFACE 0x7000000b -+#define SHT_MIPS_CONTENT 0x7000000c -+#define SHT_MIPS_OPTIONS 0x7000000d -+#define SHT_MIPS_SHDR 0x70000010 -+#define SHT_MIPS_FDESC 0x70000011 -+#define SHT_MIPS_EXTSYM 0x70000012 -+#define SHT_MIPS_DENSE 0x70000013 -+#define SHT_MIPS_PDESC 0x70000014 -+#define SHT_MIPS_LOCSYM 0x70000015 -+#define SHT_MIPS_AUXSYM 0x70000016 -+#define SHT_MIPS_OPTSYM 0x70000017 -+#define SHT_MIPS_LOCSTR 0x70000018 -+#define SHT_MIPS_LINE 0x70000019 -+#define SHT_MIPS_RFDESC 0x7000001a -+#define SHT_MIPS_DELTASYM 0x7000001b -+#define SHT_MIPS_DELTAINST 0x7000001c -+#define SHT_MIPS_DELTACLASS 0x7000001d -+#define SHT_MIPS_DWARF 0x7000001e -+#define SHT_MIPS_DELTADECL 0x7000001f -+#define SHT_MIPS_SYMBOL_LIB 0x70000020 -+#define SHT_MIPS_EVENTS 0x70000021 -+#define SHT_MIPS_TRANSLATE 0x70000022 -+#define SHT_MIPS_PIXIE 0x70000023 -+#define SHT_MIPS_XLATE 0x70000024 -+#define SHT_MIPS_XLATE_DEBUG 0x70000025 -+#define SHT_MIPS_WHIRL 0x70000026 -+#define SHT_MIPS_EH_REGION 0x70000027 -+#define SHT_MIPS_XLATE_OLD 0x70000028 -+#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -+ -+ -+ -+#define SHF_MIPS_GPREL 0x10000000 -+#define SHF_MIPS_MERGE 0x20000000 -+#define SHF_MIPS_ADDR 0x40000000 -+#define SHF_MIPS_STRINGS 0x80000000 -+#define SHF_MIPS_NOSTRIP 0x08000000 -+#define SHF_MIPS_LOCAL 0x04000000 -+#define SHF_MIPS_NAMES 0x02000000 -+#define SHF_MIPS_NODUPE 0x01000000 -+ -+ -+ -+ -+ -+#define STO_MIPS_DEFAULT 0x0 -+#define STO_MIPS_INTERNAL 0x1 -+#define STO_MIPS_HIDDEN 0x2 -+#define STO_MIPS_PROTECTED 0x3 -+#define STO_MIPS_PLT 0x8 -+#define STO_MIPS_SC_ALIGN_UNUSED 0xff -+ -+ -+#define STB_MIPS_SPLIT_COMMON 13 -+ -+ -+ -+typedef union { -+ struct { -+ Elf32_Word gt_current_g_value; -+ Elf32_Word gt_unused; -+ } gt_header; -+ struct { -+ Elf32_Word gt_g_value; -+ Elf32_Word gt_bytes; -+ } gt_entry; -+} Elf32_gptab; -+ -+ -+ -+typedef struct { -+ Elf32_Word ri_gprmask; -+ Elf32_Word ri_cprmask[4]; -+ Elf32_Sword ri_gp_value; -+} Elf32_RegInfo; -+ -+ -+ -+typedef struct { -+ unsigned char kind; -+ -+ unsigned char size; -+ Elf32_Section section; -+ -+ Elf32_Word info; -+} Elf_Options; -+ -+ -+ -+#define ODK_NULL 0 -+#define ODK_REGINFO 1 -+#define ODK_EXCEPTIONS 2 -+#define ODK_PAD 3 -+#define ODK_HWPATCH 4 -+#define ODK_FILL 5 -+#define ODK_TAGS 6 -+#define ODK_HWAND 7 -+#define ODK_HWOR 8 -+ -+ -+ -+#define OEX_FPU_MIN 0x1f -+#define OEX_FPU_MAX 0x1f00 -+#define OEX_PAGE0 0x10000 -+#define OEX_SMM 0x20000 -+#define OEX_FPDBUG 0x40000 -+#define OEX_PRECISEFP OEX_FPDBUG -+#define OEX_DISMISS 0x80000 -+ -+#define OEX_FPU_INVAL 0x10 -+#define OEX_FPU_DIV0 0x08 -+#define OEX_FPU_OFLO 0x04 -+#define OEX_FPU_UFLO 0x02 -+#define OEX_FPU_INEX 0x01 -+ -+ -+ -+#define OHW_R4KEOP 0x1 -+#define OHW_R8KPFETCH 0x2 -+#define OHW_R5KEOP 0x4 -+#define OHW_R5KCVTL 0x8 -+ -+#define OPAD_PREFIX 0x1 -+#define OPAD_POSTFIX 0x2 -+#define OPAD_SYMBOL 0x4 -+ -+ -+ -+typedef struct { -+ Elf32_Word hwp_flags1; -+ Elf32_Word hwp_flags2; -+} Elf_Options_Hw; -+ -+ -+ -+#define OHWA0_R4KEOP_CHECKED 0x00000001 -+#define OHWA1_R4KEOP_CLEAN 0x00000002 -+ -+ -+ -+#define R_MIPS_NONE 0 -+#define R_MIPS_16 1 -+#define R_MIPS_32 2 -+#define R_MIPS_REL32 3 -+#define R_MIPS_26 4 -+#define R_MIPS_HI16 5 -+#define R_MIPS_LO16 6 -+#define R_MIPS_GPREL16 7 -+#define R_MIPS_LITERAL 8 -+#define R_MIPS_GOT16 9 -+#define R_MIPS_PC16 10 -+#define R_MIPS_CALL16 11 -+#define R_MIPS_GPREL32 12 -+ -+#define R_MIPS_SHIFT5 16 -+#define R_MIPS_SHIFT6 17 -+#define R_MIPS_64 18 -+#define R_MIPS_GOT_DISP 19 -+#define R_MIPS_GOT_PAGE 20 -+#define R_MIPS_GOT_OFST 21 -+#define R_MIPS_GOT_HI16 22 -+#define R_MIPS_GOT_LO16 23 -+#define R_MIPS_SUB 24 -+#define R_MIPS_INSERT_A 25 -+#define R_MIPS_INSERT_B 26 -+#define R_MIPS_DELETE 27 -+#define R_MIPS_HIGHER 28 -+#define R_MIPS_HIGHEST 29 -+#define R_MIPS_CALL_HI16 30 -+#define R_MIPS_CALL_LO16 31 -+#define R_MIPS_SCN_DISP 32 -+#define R_MIPS_REL16 33 -+#define R_MIPS_ADD_IMMEDIATE 34 -+#define R_MIPS_PJUMP 35 -+#define R_MIPS_RELGOT 36 -+#define R_MIPS_JALR 37 -+#define R_MIPS_TLS_DTPMOD32 38 -+#define R_MIPS_TLS_DTPREL32 39 -+#define R_MIPS_TLS_DTPMOD64 40 -+#define R_MIPS_TLS_DTPREL64 41 -+#define R_MIPS_TLS_GD 42 -+#define R_MIPS_TLS_LDM 43 -+#define R_MIPS_TLS_DTPREL_HI16 44 -+#define R_MIPS_TLS_DTPREL_LO16 45 -+#define R_MIPS_TLS_GOTTPREL 46 -+#define R_MIPS_TLS_TPREL32 47 -+#define R_MIPS_TLS_TPREL64 48 -+#define R_MIPS_TLS_TPREL_HI16 49 -+#define R_MIPS_TLS_TPREL_LO16 50 -+#define R_MIPS_GLOB_DAT 51 -+#define R_MIPS_COPY 126 -+#define R_MIPS_JUMP_SLOT 127 -+ -+#define R_MIPS_NUM 128 -+ -+ -+ -+#define PT_MIPS_REGINFO 0x70000000 -+#define PT_MIPS_RTPROC 0x70000001 -+#define PT_MIPS_OPTIONS 0x70000002 -+ -+ -+ -+#define PF_MIPS_LOCAL 0x10000000 -+ -+ -+ -+#define DT_MIPS_RLD_VERSION 0x70000001 -+#define DT_MIPS_TIME_STAMP 0x70000002 -+#define DT_MIPS_ICHECKSUM 0x70000003 -+#define DT_MIPS_IVERSION 0x70000004 -+#define DT_MIPS_FLAGS 0x70000005 -+#define DT_MIPS_BASE_ADDRESS 0x70000006 -+#define DT_MIPS_MSYM 0x70000007 -+#define DT_MIPS_CONFLICT 0x70000008 -+#define DT_MIPS_LIBLIST 0x70000009 -+#define DT_MIPS_LOCAL_GOTNO 0x7000000a -+#define DT_MIPS_CONFLICTNO 0x7000000b -+#define DT_MIPS_LIBLISTNO 0x70000010 -+#define DT_MIPS_SYMTABNO 0x70000011 -+#define DT_MIPS_UNREFEXTNO 0x70000012 -+#define DT_MIPS_GOTSYM 0x70000013 -+#define DT_MIPS_HIPAGENO 0x70000014 -+#define DT_MIPS_RLD_MAP 0x70000016 -+#define DT_MIPS_DELTA_CLASS 0x70000017 -+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 -+ -+#define DT_MIPS_DELTA_INSTANCE 0x70000019 -+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a -+ -+#define DT_MIPS_DELTA_RELOC 0x7000001b -+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c -+ -+#define DT_MIPS_DELTA_SYM 0x7000001d -+ -+#define DT_MIPS_DELTA_SYM_NO 0x7000001e -+ -+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 -+ -+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 -+ -+#define DT_MIPS_CXX_FLAGS 0x70000022 -+#define DT_MIPS_PIXIE_INIT 0x70000023 -+#define DT_MIPS_SYMBOL_LIB 0x70000024 -+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -+#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -+#define DT_MIPS_OPTIONS 0x70000029 -+#define DT_MIPS_INTERFACE 0x7000002a -+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -+#define DT_MIPS_INTERFACE_SIZE 0x7000002c -+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d -+ -+#define DT_MIPS_PERF_SUFFIX 0x7000002e -+ -+#define DT_MIPS_COMPACT_SIZE 0x7000002f -+#define DT_MIPS_GP_VALUE 0x70000030 -+#define DT_MIPS_AUX_DYNAMIC 0x70000031 -+ -+#define DT_MIPS_PLTGOT 0x70000032 -+ -+#define DT_MIPS_RWPLT 0x70000034 -+#define DT_MIPS_NUM 0x35 -+ -+ -+ -+#define RHF_NONE 0 -+#define RHF_QUICKSTART (1 << 0) -+#define RHF_NOTPOT (1 << 1) -+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) -+#define RHF_NO_MOVE (1 << 3) -+#define RHF_SGI_ONLY (1 << 4) -+#define RHF_GUARANTEE_INIT (1 << 5) -+#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -+#define RHF_GUARANTEE_START_INIT (1 << 7) -+#define RHF_PIXIE (1 << 8) -+#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -+#define RHF_REQUICKSTART (1 << 10) -+#define RHF_REQUICKSTARTED (1 << 11) -+#define RHF_CORD (1 << 12) -+#define RHF_NO_UNRES_UNDEF (1 << 13) -+#define RHF_RLD_ORDER_SAFE (1 << 14) -+ -+ -+ -+typedef struct -+{ -+ Elf32_Word l_name; -+ Elf32_Word l_time_stamp; -+ Elf32_Word l_checksum; -+ Elf32_Word l_version; -+ Elf32_Word l_flags; -+} Elf32_Lib; -+ -+typedef struct -+{ -+ Elf64_Word l_name; -+ Elf64_Word l_time_stamp; -+ Elf64_Word l_checksum; -+ Elf64_Word l_version; -+ Elf64_Word l_flags; -+} Elf64_Lib; -+ -+ -+ -+ -+#define LL_NONE 0 -+#define LL_EXACT_MATCH (1 << 0) -+#define LL_IGNORE_INT_VER (1 << 1) -+#define LL_REQUIRE_MINOR (1 << 2) -+#define LL_EXPORTS (1 << 3) -+#define LL_DELAY_LOAD (1 << 4) -+#define LL_DELTA (1 << 5) -+ -+ -+ -+typedef Elf32_Addr Elf32_Conflict; -+ -+ -+ -+ -+ -+ -+#define EF_PARISC_TRAPNIL 0x00010000 -+#define EF_PARISC_EXT 0x00020000 -+#define EF_PARISC_LSB 0x00040000 -+#define EF_PARISC_WIDE 0x00080000 -+#define EF_PARISC_NO_KABP 0x00100000 -+ -+#define EF_PARISC_LAZYSWAP 0x00400000 -+#define EF_PARISC_ARCH 0x0000ffff -+ -+ -+ -+#define EFA_PARISC_1_0 0x020b -+#define EFA_PARISC_1_1 0x0210 -+#define EFA_PARISC_2_0 0x0214 -+ -+ -+ -+#define SHN_PARISC_ANSI_COMMON 0xff00 -+ -+#define SHN_PARISC_HUGE_COMMON 0xff01 -+ -+ -+ -+#define SHT_PARISC_EXT 0x70000000 -+#define SHT_PARISC_UNWIND 0x70000001 -+#define SHT_PARISC_DOC 0x70000002 -+ -+ -+ -+#define SHF_PARISC_SHORT 0x20000000 -+#define SHF_PARISC_HUGE 0x40000000 -+#define SHF_PARISC_SBP 0x80000000 -+ -+ -+ -+#define STT_PARISC_MILLICODE 13 -+ -+#define STT_HP_OPAQUE (STT_LOOS + 0x1) -+#define STT_HP_STUB (STT_LOOS + 0x2) -+ -+ -+ -+#define R_PARISC_NONE 0 -+#define R_PARISC_DIR32 1 -+#define R_PARISC_DIR21L 2 -+#define R_PARISC_DIR17R 3 -+#define R_PARISC_DIR17F 4 -+#define R_PARISC_DIR14R 6 -+#define R_PARISC_PCREL32 9 -+#define R_PARISC_PCREL21L 10 -+#define R_PARISC_PCREL17R 11 -+#define R_PARISC_PCREL17F 12 -+#define R_PARISC_PCREL14R 14 -+#define R_PARISC_DPREL21L 18 -+#define R_PARISC_DPREL14R 22 -+#define R_PARISC_GPREL21L 26 -+#define R_PARISC_GPREL14R 30 -+#define R_PARISC_LTOFF21L 34 -+#define R_PARISC_LTOFF14R 38 -+#define R_PARISC_SECREL32 41 -+#define R_PARISC_SEGBASE 48 -+#define R_PARISC_SEGREL32 49 -+#define R_PARISC_PLTOFF21L 50 -+#define R_PARISC_PLTOFF14R 54 -+#define R_PARISC_LTOFF_FPTR32 57 -+#define R_PARISC_LTOFF_FPTR21L 58 -+#define R_PARISC_LTOFF_FPTR14R 62 -+#define R_PARISC_FPTR64 64 -+#define R_PARISC_PLABEL32 65 -+#define R_PARISC_PLABEL21L 66 -+#define R_PARISC_PLABEL14R 70 -+#define R_PARISC_PCREL64 72 -+#define R_PARISC_PCREL22F 74 -+#define R_PARISC_PCREL14WR 75 -+#define R_PARISC_PCREL14DR 76 -+#define R_PARISC_PCREL16F 77 -+#define R_PARISC_PCREL16WF 78 -+#define R_PARISC_PCREL16DF 79 -+#define R_PARISC_DIR64 80 -+#define R_PARISC_DIR14WR 83 -+#define R_PARISC_DIR14DR 84 -+#define R_PARISC_DIR16F 85 -+#define R_PARISC_DIR16WF 86 -+#define R_PARISC_DIR16DF 87 -+#define R_PARISC_GPREL64 88 -+#define R_PARISC_GPREL14WR 91 -+#define R_PARISC_GPREL14DR 92 -+#define R_PARISC_GPREL16F 93 -+#define R_PARISC_GPREL16WF 94 -+#define R_PARISC_GPREL16DF 95 -+#define R_PARISC_LTOFF64 96 -+#define R_PARISC_LTOFF14WR 99 -+#define R_PARISC_LTOFF14DR 100 -+#define R_PARISC_LTOFF16F 101 -+#define R_PARISC_LTOFF16WF 102 -+#define R_PARISC_LTOFF16DF 103 -+#define R_PARISC_SECREL64 104 -+#define R_PARISC_SEGREL64 112 -+#define R_PARISC_PLTOFF14WR 115 -+#define R_PARISC_PLTOFF14DR 116 -+#define R_PARISC_PLTOFF16F 117 -+#define R_PARISC_PLTOFF16WF 118 -+#define R_PARISC_PLTOFF16DF 119 -+#define R_PARISC_LTOFF_FPTR64 120 -+#define R_PARISC_LTOFF_FPTR14WR 123 -+#define R_PARISC_LTOFF_FPTR14DR 124 -+#define R_PARISC_LTOFF_FPTR16F 125 -+#define R_PARISC_LTOFF_FPTR16WF 126 -+#define R_PARISC_LTOFF_FPTR16DF 127 -+#define R_PARISC_LORESERVE 128 -+#define R_PARISC_COPY 128 -+#define R_PARISC_IPLT 129 -+#define R_PARISC_EPLT 130 -+#define R_PARISC_TPREL32 153 -+#define R_PARISC_TPREL21L 154 -+#define R_PARISC_TPREL14R 158 -+#define R_PARISC_LTOFF_TP21L 162 -+#define R_PARISC_LTOFF_TP14R 166 -+#define R_PARISC_LTOFF_TP14F 167 -+#define R_PARISC_TPREL64 216 -+#define R_PARISC_TPREL14WR 219 -+#define R_PARISC_TPREL14DR 220 -+#define R_PARISC_TPREL16F 221 -+#define R_PARISC_TPREL16WF 222 -+#define R_PARISC_TPREL16DF 223 -+#define R_PARISC_LTOFF_TP64 224 -+#define R_PARISC_LTOFF_TP14WR 227 -+#define R_PARISC_LTOFF_TP14DR 228 -+#define R_PARISC_LTOFF_TP16F 229 -+#define R_PARISC_LTOFF_TP16WF 230 -+#define R_PARISC_LTOFF_TP16DF 231 -+#define R_PARISC_GNU_VTENTRY 232 -+#define R_PARISC_GNU_VTINHERIT 233 -+#define R_PARISC_TLS_GD21L 234 -+#define R_PARISC_TLS_GD14R 235 -+#define R_PARISC_TLS_GDCALL 236 -+#define R_PARISC_TLS_LDM21L 237 -+#define R_PARISC_TLS_LDM14R 238 -+#define R_PARISC_TLS_LDMCALL 239 -+#define R_PARISC_TLS_LDO21L 240 -+#define R_PARISC_TLS_LDO14R 241 -+#define R_PARISC_TLS_DTPMOD32 242 -+#define R_PARISC_TLS_DTPMOD64 243 -+#define R_PARISC_TLS_DTPOFF32 244 -+#define R_PARISC_TLS_DTPOFF64 245 -+#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L -+#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R -+#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L -+#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R -+#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 -+#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 -+#define R_PARISC_HIRESERVE 255 -+ -+ -+ -+#define PT_HP_TLS (PT_LOOS + 0x0) -+#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -+#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -+#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -+#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -+#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -+#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -+#define PT_HP_PARALLEL (PT_LOOS + 0x10) -+#define PT_HP_FASTBIND (PT_LOOS + 0x11) -+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -+#define PT_HP_STACK (PT_LOOS + 0x14) -+ -+#define PT_PARISC_ARCHEXT 0x70000000 -+#define PT_PARISC_UNWIND 0x70000001 -+ -+ -+ -+#define PF_PARISC_SBP 0x08000000 -+ -+#define PF_HP_PAGE_SIZE 0x00100000 -+#define PF_HP_FAR_SHARED 0x00200000 -+#define PF_HP_NEAR_SHARED 0x00400000 -+#define PF_HP_CODE 0x01000000 -+#define PF_HP_MODIFY 0x02000000 -+#define PF_HP_LAZYSWAP 0x04000000 -+#define PF_HP_SBP 0x08000000 -+ -+ -+ -+ -+ -+ -+#define EF_ALPHA_32BIT 1 -+#define EF_ALPHA_CANRELAX 2 -+ -+ -+ -+ -+#define SHT_ALPHA_DEBUG 0x70000001 -+#define SHT_ALPHA_REGINFO 0x70000002 -+ -+ -+ -+#define SHF_ALPHA_GPREL 0x10000000 -+ -+ -+#define STO_ALPHA_NOPV 0x80 -+#define STO_ALPHA_STD_GPLOAD 0x88 -+ -+ -+ -+#define R_ALPHA_NONE 0 -+#define R_ALPHA_REFLONG 1 -+#define R_ALPHA_REFQUAD 2 -+#define R_ALPHA_GPREL32 3 -+#define R_ALPHA_LITERAL 4 -+#define R_ALPHA_LITUSE 5 -+#define R_ALPHA_GPDISP 6 -+#define R_ALPHA_BRADDR 7 -+#define R_ALPHA_HINT 8 -+#define R_ALPHA_SREL16 9 -+#define R_ALPHA_SREL32 10 -+#define R_ALPHA_SREL64 11 -+#define R_ALPHA_GPRELHIGH 17 -+#define R_ALPHA_GPRELLOW 18 -+#define R_ALPHA_GPREL16 19 -+#define R_ALPHA_COPY 24 -+#define R_ALPHA_GLOB_DAT 25 -+#define R_ALPHA_JMP_SLOT 26 -+#define R_ALPHA_RELATIVE 27 -+#define R_ALPHA_TLS_GD_HI 28 -+#define R_ALPHA_TLSGD 29 -+#define R_ALPHA_TLS_LDM 30 -+#define R_ALPHA_DTPMOD64 31 -+#define R_ALPHA_GOTDTPREL 32 -+#define R_ALPHA_DTPREL64 33 -+#define R_ALPHA_DTPRELHI 34 -+#define R_ALPHA_DTPRELLO 35 -+#define R_ALPHA_DTPREL16 36 -+#define R_ALPHA_GOTTPREL 37 -+#define R_ALPHA_TPREL64 38 -+#define R_ALPHA_TPRELHI 39 -+#define R_ALPHA_TPRELLO 40 -+#define R_ALPHA_TPREL16 41 -+ -+#define R_ALPHA_NUM 46 -+ -+ -+#define LITUSE_ALPHA_ADDR 0 -+#define LITUSE_ALPHA_BASE 1 -+#define LITUSE_ALPHA_BYTOFF 2 -+#define LITUSE_ALPHA_JSR 3 -+#define LITUSE_ALPHA_TLS_GD 4 -+#define LITUSE_ALPHA_TLS_LDM 5 -+ -+ -+#define DT_ALPHA_PLTRO (DT_LOPROC + 0) -+#define DT_ALPHA_NUM 1 -+ -+ -+ -+ -+#define EF_PPC_EMB 0x80000000 -+ -+ -+#define EF_PPC_RELOCATABLE 0x00010000 -+#define EF_PPC_RELOCATABLE_LIB 0x00008000 -+ -+ -+ -+#define R_PPC_NONE 0 -+#define R_PPC_ADDR32 1 -+#define R_PPC_ADDR24 2 -+#define R_PPC_ADDR16 3 -+#define R_PPC_ADDR16_LO 4 -+#define R_PPC_ADDR16_HI 5 -+#define R_PPC_ADDR16_HA 6 -+#define R_PPC_ADDR14 7 -+#define R_PPC_ADDR14_BRTAKEN 8 -+#define R_PPC_ADDR14_BRNTAKEN 9 -+#define R_PPC_REL24 10 -+#define R_PPC_REL14 11 -+#define R_PPC_REL14_BRTAKEN 12 -+#define R_PPC_REL14_BRNTAKEN 13 -+#define R_PPC_GOT16 14 -+#define R_PPC_GOT16_LO 15 -+#define R_PPC_GOT16_HI 16 -+#define R_PPC_GOT16_HA 17 -+#define R_PPC_PLTREL24 18 -+#define R_PPC_COPY 19 -+#define R_PPC_GLOB_DAT 20 -+#define R_PPC_JMP_SLOT 21 -+#define R_PPC_RELATIVE 22 -+#define R_PPC_LOCAL24PC 23 -+#define R_PPC_UADDR32 24 -+#define R_PPC_UADDR16 25 -+#define R_PPC_REL32 26 -+#define R_PPC_PLT32 27 -+#define R_PPC_PLTREL32 28 -+#define R_PPC_PLT16_LO 29 -+#define R_PPC_PLT16_HI 30 -+#define R_PPC_PLT16_HA 31 -+#define R_PPC_SDAREL16 32 -+#define R_PPC_SECTOFF 33 -+#define R_PPC_SECTOFF_LO 34 -+#define R_PPC_SECTOFF_HI 35 -+#define R_PPC_SECTOFF_HA 36 -+ -+ -+#define R_PPC_TLS 67 -+#define R_PPC_DTPMOD32 68 -+#define R_PPC_TPREL16 69 -+#define R_PPC_TPREL16_LO 70 -+#define R_PPC_TPREL16_HI 71 -+#define R_PPC_TPREL16_HA 72 -+#define R_PPC_TPREL32 73 -+#define R_PPC_DTPREL16 74 -+#define R_PPC_DTPREL16_LO 75 -+#define R_PPC_DTPREL16_HI 76 -+#define R_PPC_DTPREL16_HA 77 -+#define R_PPC_DTPREL32 78 -+#define R_PPC_GOT_TLSGD16 79 -+#define R_PPC_GOT_TLSGD16_LO 80 -+#define R_PPC_GOT_TLSGD16_HI 81 -+#define R_PPC_GOT_TLSGD16_HA 82 -+#define R_PPC_GOT_TLSLD16 83 -+#define R_PPC_GOT_TLSLD16_LO 84 -+#define R_PPC_GOT_TLSLD16_HI 85 -+#define R_PPC_GOT_TLSLD16_HA 86 -+#define R_PPC_GOT_TPREL16 87 -+#define R_PPC_GOT_TPREL16_LO 88 -+#define R_PPC_GOT_TPREL16_HI 89 -+#define R_PPC_GOT_TPREL16_HA 90 -+#define R_PPC_GOT_DTPREL16 91 -+#define R_PPC_GOT_DTPREL16_LO 92 -+#define R_PPC_GOT_DTPREL16_HI 93 -+#define R_PPC_GOT_DTPREL16_HA 94 -+ -+ -+ -+#define R_PPC_EMB_NADDR32 101 -+#define R_PPC_EMB_NADDR16 102 -+#define R_PPC_EMB_NADDR16_LO 103 -+#define R_PPC_EMB_NADDR16_HI 104 -+#define R_PPC_EMB_NADDR16_HA 105 -+#define R_PPC_EMB_SDAI16 106 -+#define R_PPC_EMB_SDA2I16 107 -+#define R_PPC_EMB_SDA2REL 108 -+#define R_PPC_EMB_SDA21 109 -+#define R_PPC_EMB_MRKREF 110 -+#define R_PPC_EMB_RELSEC16 111 -+#define R_PPC_EMB_RELST_LO 112 -+#define R_PPC_EMB_RELST_HI 113 -+#define R_PPC_EMB_RELST_HA 114 -+#define R_PPC_EMB_BIT_FLD 115 -+#define R_PPC_EMB_RELSDA 116 -+ -+ -+#define R_PPC_DIAB_SDA21_LO 180 -+#define R_PPC_DIAB_SDA21_HI 181 -+#define R_PPC_DIAB_SDA21_HA 182 -+#define R_PPC_DIAB_RELSDA_LO 183 -+#define R_PPC_DIAB_RELSDA_HI 184 -+#define R_PPC_DIAB_RELSDA_HA 185 -+ -+ -+#define R_PPC_IRELATIVE 248 -+ -+ -+#define R_PPC_REL16 249 -+#define R_PPC_REL16_LO 250 -+#define R_PPC_REL16_HI 251 -+#define R_PPC_REL16_HA 252 -+ -+ -+ -+#define R_PPC_TOC16 255 -+ -+ -+#define DT_PPC_GOT (DT_LOPROC + 0) -+#define DT_PPC_NUM 1 -+ -+ -+#define R_PPC64_NONE R_PPC_NONE -+#define R_PPC64_ADDR32 R_PPC_ADDR32 -+#define R_PPC64_ADDR24 R_PPC_ADDR24 -+#define R_PPC64_ADDR16 R_PPC_ADDR16 -+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO -+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI -+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA -+#define R_PPC64_ADDR14 R_PPC_ADDR14 -+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -+#define R_PPC64_REL24 R_PPC_REL24 -+#define R_PPC64_REL14 R_PPC_REL14 -+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -+#define R_PPC64_GOT16 R_PPC_GOT16 -+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA -+ -+#define R_PPC64_COPY R_PPC_COPY -+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -+#define R_PPC64_RELATIVE R_PPC_RELATIVE -+ -+#define R_PPC64_UADDR32 R_PPC_UADDR32 -+#define R_PPC64_UADDR16 R_PPC_UADDR16 -+#define R_PPC64_REL32 R_PPC_REL32 -+#define R_PPC64_PLT32 R_PPC_PLT32 -+#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA -+ -+#define R_PPC64_SECTOFF R_PPC_SECTOFF -+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -+#define R_PPC64_ADDR30 37 -+#define R_PPC64_ADDR64 38 -+#define R_PPC64_ADDR16_HIGHER 39 -+#define R_PPC64_ADDR16_HIGHERA 40 -+#define R_PPC64_ADDR16_HIGHEST 41 -+#define R_PPC64_ADDR16_HIGHESTA 42 -+#define R_PPC64_UADDR64 43 -+#define R_PPC64_REL64 44 -+#define R_PPC64_PLT64 45 -+#define R_PPC64_PLTREL64 46 -+#define R_PPC64_TOC16 47 -+#define R_PPC64_TOC16_LO 48 -+#define R_PPC64_TOC16_HI 49 -+#define R_PPC64_TOC16_HA 50 -+#define R_PPC64_TOC 51 -+#define R_PPC64_PLTGOT16 52 -+#define R_PPC64_PLTGOT16_LO 53 -+#define R_PPC64_PLTGOT16_HI 54 -+#define R_PPC64_PLTGOT16_HA 55 -+ -+#define R_PPC64_ADDR16_DS 56 -+#define R_PPC64_ADDR16_LO_DS 57 -+#define R_PPC64_GOT16_DS 58 -+#define R_PPC64_GOT16_LO_DS 59 -+#define R_PPC64_PLT16_LO_DS 60 -+#define R_PPC64_SECTOFF_DS 61 -+#define R_PPC64_SECTOFF_LO_DS 62 -+#define R_PPC64_TOC16_DS 63 -+#define R_PPC64_TOC16_LO_DS 64 -+#define R_PPC64_PLTGOT16_DS 65 -+#define R_PPC64_PLTGOT16_LO_DS 66 -+ -+ -+#define R_PPC64_TLS 67 -+#define R_PPC64_DTPMOD64 68 -+#define R_PPC64_TPREL16 69 -+#define R_PPC64_TPREL16_LO 70 -+#define R_PPC64_TPREL16_HI 71 -+#define R_PPC64_TPREL16_HA 72 -+#define R_PPC64_TPREL64 73 -+#define R_PPC64_DTPREL16 74 -+#define R_PPC64_DTPREL16_LO 75 -+#define R_PPC64_DTPREL16_HI 76 -+#define R_PPC64_DTPREL16_HA 77 -+#define R_PPC64_DTPREL64 78 -+#define R_PPC64_GOT_TLSGD16 79 -+#define R_PPC64_GOT_TLSGD16_LO 80 -+#define R_PPC64_GOT_TLSGD16_HI 81 -+#define R_PPC64_GOT_TLSGD16_HA 82 -+#define R_PPC64_GOT_TLSLD16 83 -+#define R_PPC64_GOT_TLSLD16_LO 84 -+#define R_PPC64_GOT_TLSLD16_HI 85 -+#define R_PPC64_GOT_TLSLD16_HA 86 -+#define R_PPC64_GOT_TPREL16_DS 87 -+#define R_PPC64_GOT_TPREL16_LO_DS 88 -+#define R_PPC64_GOT_TPREL16_HI 89 -+#define R_PPC64_GOT_TPREL16_HA 90 -+#define R_PPC64_GOT_DTPREL16_DS 91 -+#define R_PPC64_GOT_DTPREL16_LO_DS 92 -+#define R_PPC64_GOT_DTPREL16_HI 93 -+#define R_PPC64_GOT_DTPREL16_HA 94 -+#define R_PPC64_TPREL16_DS 95 -+#define R_PPC64_TPREL16_LO_DS 96 -+#define R_PPC64_TPREL16_HIGHER 97 -+#define R_PPC64_TPREL16_HIGHERA 98 -+#define R_PPC64_TPREL16_HIGHEST 99 -+#define R_PPC64_TPREL16_HIGHESTA 100 -+#define R_PPC64_DTPREL16_DS 101 -+#define R_PPC64_DTPREL16_LO_DS 102 -+#define R_PPC64_DTPREL16_HIGHER 103 -+#define R_PPC64_DTPREL16_HIGHERA 104 -+#define R_PPC64_DTPREL16_HIGHEST 105 -+#define R_PPC64_DTPREL16_HIGHESTA 106 -+ -+ -+#define R_PPC64_JMP_IREL 247 -+#define R_PPC64_IRELATIVE 248 -+#define R_PPC64_REL16 249 -+#define R_PPC64_REL16_LO 250 -+#define R_PPC64_REL16_HI 251 -+#define R_PPC64_REL16_HA 252 -+ -+ -+#define DT_PPC64_GLINK (DT_LOPROC + 0) -+#define DT_PPC64_OPD (DT_LOPROC + 1) -+#define DT_PPC64_OPDSZ (DT_LOPROC + 2) -+#define DT_PPC64_NUM 3 -+ -+ -+ -+ -+ -+#define EF_ARM_RELEXEC 0x01 -+#define EF_ARM_HASENTRY 0x02 -+#define EF_ARM_INTERWORK 0x04 -+#define EF_ARM_APCS_26 0x08 -+#define EF_ARM_APCS_FLOAT 0x10 -+#define EF_ARM_PIC 0x20 -+#define EF_ARM_ALIGN8 0x40 -+#define EF_ARM_NEW_ABI 0x80 -+#define EF_ARM_OLD_ABI 0x100 -+#define EF_ARM_SOFT_FLOAT 0x200 -+#define EF_ARM_VFP_FLOAT 0x400 -+#define EF_ARM_MAVERICK_FLOAT 0x800 -+ -+#define EF_ARM_ABI_FLOAT_SOFT 0x200 -+#define EF_ARM_ABI_FLOAT_HARD 0x400 -+ -+ -+#define EF_ARM_SYMSARESORTED 0x04 -+#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -+#define EF_ARM_MAPSYMSFIRST 0x10 -+#define EF_ARM_EABIMASK 0XFF000000 -+ -+ -+#define EF_ARM_BE8 0x00800000 -+#define EF_ARM_LE8 0x00400000 -+ -+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -+#define EF_ARM_EABI_UNKNOWN 0x00000000 -+#define EF_ARM_EABI_VER1 0x01000000 -+#define EF_ARM_EABI_VER2 0x02000000 -+#define EF_ARM_EABI_VER3 0x03000000 -+#define EF_ARM_EABI_VER4 0x04000000 -+#define EF_ARM_EABI_VER5 0x05000000 -+ -+ -+#define STT_ARM_TFUNC STT_LOPROC -+#define STT_ARM_16BIT STT_HIPROC -+ -+ -+#define SHF_ARM_ENTRYSECT 0x10000000 -+#define SHF_ARM_COMDEF 0x80000000 -+ -+ -+ -+#define PF_ARM_SB 0x10000000 -+ -+#define PF_ARM_PI 0x20000000 -+#define PF_ARM_ABS 0x40000000 -+ -+ -+#define PT_ARM_EXIDX (PT_LOPROC + 1) -+ -+ -+#define SHT_ARM_EXIDX (SHT_LOPROC + 1) -+#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) -+#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) -+ -+ -+#define R_AARCH64_NONE 0 -+#define R_AARCH64_ABS64 257 -+#define R_AARCH64_ABS32 258 -+#define R_AARCH64_COPY 1024 -+#define R_AARCH64_GLOB_DAT 1025 -+#define R_AARCH64_JUMP_SLOT 1026 -+#define R_AARCH64_RELATIVE 1027 -+#define R_AARCH64_TLS_DTPMOD64 1028 -+#define R_AARCH64_TLS_DTPREL64 1029 -+#define R_AARCH64_TLS_TPREL64 1030 -+#define R_AARCH64_TLSDESC 1031 -+ -+ -+#define R_ARM_NONE 0 -+#define R_ARM_PC24 1 -+#define R_ARM_ABS32 2 -+#define R_ARM_REL32 3 -+#define R_ARM_PC13 4 -+#define R_ARM_ABS16 5 -+#define R_ARM_ABS12 6 -+#define R_ARM_THM_ABS5 7 -+#define R_ARM_ABS8 8 -+#define R_ARM_SBREL32 9 -+#define R_ARM_THM_PC22 10 -+#define R_ARM_THM_PC8 11 -+#define R_ARM_AMP_VCALL9 12 -+#define R_ARM_TLS_DESC 13 -+#define R_ARM_THM_SWI8 14 -+#define R_ARM_XPC25 15 -+#define R_ARM_THM_XPC22 16 -+#define R_ARM_TLS_DTPMOD32 17 -+#define R_ARM_TLS_DTPOFF32 18 -+#define R_ARM_TLS_TPOFF32 19 -+#define R_ARM_COPY 20 -+#define R_ARM_GLOB_DAT 21 -+#define R_ARM_JUMP_SLOT 22 -+#define R_ARM_RELATIVE 23 -+#define R_ARM_GOTOFF 24 -+#define R_ARM_GOTPC 25 -+#define R_ARM_GOT32 26 -+#define R_ARM_PLT32 27 -+#define R_ARM_CALL 28 -+#define R_ARM_JUMP24 29 -+#define R_ARM_THM_JUMP24 30 -+#define R_ARM_BASE_ABS 31 -+#define R_ARM_ALU_PCREL_7_0 32 -+#define R_ARM_ALU_PCREL_15_8 33 -+#define R_ARM_ALU_PCREL_23_15 34 -+#define R_ARM_LDR_SBREL_11_0 35 -+#define R_ARM_ALU_SBREL_19_12 36 -+#define R_ARM_ALU_SBREL_27_20 37 -+#define R_ARM_TARGET1 38 -+#define R_ARM_SBREL31 39 -+#define R_ARM_V4BX 40 -+#define R_ARM_TARGET2 41 -+#define R_ARM_PREL31 42 -+#define R_ARM_MOVW_ABS_NC 43 -+#define R_ARM_MOVT_ABS 44 -+#define R_ARM_MOVW_PREL_NC 45 -+#define R_ARM_MOVT_PREL 46 -+#define R_ARM_THM_MOVW_ABS_NC 47 -+#define R_ARM_THM_MOVT_ABS 48 -+#define R_ARM_THM_MOVW_PREL_NC 49 -+#define R_ARM_THM_MOVT_PREL 50 -+#define R_ARM_THM_JUMP19 51 -+#define R_ARM_THM_JUMP6 52 -+#define R_ARM_THM_ALU_PREL_11_0 53 -+#define R_ARM_THM_PC12 54 -+#define R_ARM_ABS32_NOI 55 -+#define R_ARM_REL32_NOI 56 -+#define R_ARM_ALU_PC_G0_NC 57 -+#define R_ARM_ALU_PC_G0 58 -+#define R_ARM_ALU_PC_G1_NC 59 -+#define R_ARM_ALU_PC_G1 60 -+#define R_ARM_ALU_PC_G2 61 -+#define R_ARM_LDR_PC_G1 62 -+#define R_ARM_LDR_PC_G2 63 -+#define R_ARM_LDRS_PC_G0 64 -+#define R_ARM_LDRS_PC_G1 65 -+#define R_ARM_LDRS_PC_G2 66 -+#define R_ARM_LDC_PC_G0 67 -+#define R_ARM_LDC_PC_G1 68 -+#define R_ARM_LDC_PC_G2 69 -+#define R_ARM_ALU_SB_G0_NC 70 -+#define R_ARM_ALU_SB_G0 71 -+#define R_ARM_ALU_SB_G1_NC 72 -+#define R_ARM_ALU_SB_G1 73 -+#define R_ARM_ALU_SB_G2 74 -+#define R_ARM_LDR_SB_G0 75 -+#define R_ARM_LDR_SB_G1 76 -+#define R_ARM_LDR_SB_G2 77 -+#define R_ARM_LDRS_SB_G0 78 -+#define R_ARM_LDRS_SB_G1 79 -+#define R_ARM_LDRS_SB_G2 80 -+#define R_ARM_LDC_SB_G0 81 -+#define R_ARM_LDC_SB_G1 82 -+#define R_ARM_LDC_SB_G2 83 -+#define R_ARM_MOVW_BREL_NC 84 -+#define R_ARM_MOVT_BREL 85 -+#define R_ARM_MOVW_BREL 86 -+#define R_ARM_THM_MOVW_BREL_NC 87 -+#define R_ARM_THM_MOVT_BREL 88 -+#define R_ARM_THM_MOVW_BREL 89 -+#define R_ARM_TLS_GOTDESC 90 -+#define R_ARM_TLS_CALL 91 -+#define R_ARM_TLS_DESCSEQ 92 -+#define R_ARM_THM_TLS_CALL 93 -+#define R_ARM_PLT32_ABS 94 -+#define R_ARM_GOT_ABS 95 -+#define R_ARM_GOT_PREL 96 -+#define R_ARM_GOT_BREL12 97 -+#define R_ARM_GOTOFF12 98 -+#define R_ARM_GOTRELAX 99 -+#define R_ARM_GNU_VTENTRY 100 -+#define R_ARM_GNU_VTINHERIT 101 -+#define R_ARM_THM_PC11 102 -+#define R_ARM_THM_PC9 103 -+#define R_ARM_TLS_GD32 104 -+ -+#define R_ARM_TLS_LDM32 105 -+ -+#define R_ARM_TLS_LDO32 106 -+ -+#define R_ARM_TLS_IE32 107 -+ -+#define R_ARM_TLS_LE32 108 -+#define R_ARM_TLS_LDO12 109 -+#define R_ARM_TLS_LE12 110 -+#define R_ARM_TLS_IE12GP 111 -+#define R_ARM_ME_TOO 128 -+#define R_ARM_THM_TLS_DESCSEQ 129 -+#define R_ARM_THM_TLS_DESCSEQ16 129 -+#define R_ARM_THM_TLS_DESCSEQ32 130 -+#define R_ARM_THM_GOT_BREL12 131 -+#define R_ARM_IRELATIVE 160 -+#define R_ARM_RXPC25 249 -+#define R_ARM_RSBREL32 250 -+#define R_ARM_THM_RPC22 251 -+#define R_ARM_RREL32 252 -+#define R_ARM_RABS22 253 -+#define R_ARM_RPC24 254 -+#define R_ARM_RBASE 255 -+ -+#define R_ARM_NUM 256 -+ -+ -+ -+ -+#define EF_IA_64_MASKOS 0x0000000f -+#define EF_IA_64_ABI64 0x00000010 -+#define EF_IA_64_ARCH 0xff000000 -+ -+ -+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) -+#define PT_IA_64_UNWIND (PT_LOPROC + 1) -+#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) -+#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) -+#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) -+ -+ -+#define PF_IA_64_NORECOV 0x80000000 -+ -+ -+#define SHT_IA_64_EXT (SHT_LOPROC + 0) -+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) -+ -+ -+#define SHF_IA_64_SHORT 0x10000000 -+#define SHF_IA_64_NORECOV 0x20000000 -+ -+ -+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -+#define DT_IA_64_NUM 1 -+ -+ -+#define R_IA64_NONE 0x00 -+#define R_IA64_IMM14 0x21 -+#define R_IA64_IMM22 0x22 -+#define R_IA64_IMM64 0x23 -+#define R_IA64_DIR32MSB 0x24 -+#define R_IA64_DIR32LSB 0x25 -+#define R_IA64_DIR64MSB 0x26 -+#define R_IA64_DIR64LSB 0x27 -+#define R_IA64_GPREL22 0x2a -+#define R_IA64_GPREL64I 0x2b -+#define R_IA64_GPREL32MSB 0x2c -+#define R_IA64_GPREL32LSB 0x2d -+#define R_IA64_GPREL64MSB 0x2e -+#define R_IA64_GPREL64LSB 0x2f -+#define R_IA64_LTOFF22 0x32 -+#define R_IA64_LTOFF64I 0x33 -+#define R_IA64_PLTOFF22 0x3a -+#define R_IA64_PLTOFF64I 0x3b -+#define R_IA64_PLTOFF64MSB 0x3e -+#define R_IA64_PLTOFF64LSB 0x3f -+#define R_IA64_FPTR64I 0x43 -+#define R_IA64_FPTR32MSB 0x44 -+#define R_IA64_FPTR32LSB 0x45 -+#define R_IA64_FPTR64MSB 0x46 -+#define R_IA64_FPTR64LSB 0x47 -+#define R_IA64_PCREL60B 0x48 -+#define R_IA64_PCREL21B 0x49 -+#define R_IA64_PCREL21M 0x4a -+#define R_IA64_PCREL21F 0x4b -+#define R_IA64_PCREL32MSB 0x4c -+#define R_IA64_PCREL32LSB 0x4d -+#define R_IA64_PCREL64MSB 0x4e -+#define R_IA64_PCREL64LSB 0x4f -+#define R_IA64_LTOFF_FPTR22 0x52 -+#define R_IA64_LTOFF_FPTR64I 0x53 -+#define R_IA64_LTOFF_FPTR32MSB 0x54 -+#define R_IA64_LTOFF_FPTR32LSB 0x55 -+#define R_IA64_LTOFF_FPTR64MSB 0x56 -+#define R_IA64_LTOFF_FPTR64LSB 0x57 -+#define R_IA64_SEGREL32MSB 0x5c -+#define R_IA64_SEGREL32LSB 0x5d -+#define R_IA64_SEGREL64MSB 0x5e -+#define R_IA64_SEGREL64LSB 0x5f -+#define R_IA64_SECREL32MSB 0x64 -+#define R_IA64_SECREL32LSB 0x65 -+#define R_IA64_SECREL64MSB 0x66 -+#define R_IA64_SECREL64LSB 0x67 -+#define R_IA64_REL32MSB 0x6c -+#define R_IA64_REL32LSB 0x6d -+#define R_IA64_REL64MSB 0x6e -+#define R_IA64_REL64LSB 0x6f -+#define R_IA64_LTV32MSB 0x74 -+#define R_IA64_LTV32LSB 0x75 -+#define R_IA64_LTV64MSB 0x76 -+#define R_IA64_LTV64LSB 0x77 -+#define R_IA64_PCREL21BI 0x79 -+#define R_IA64_PCREL22 0x7a -+#define R_IA64_PCREL64I 0x7b -+#define R_IA64_IPLTMSB 0x80 -+#define R_IA64_IPLTLSB 0x81 -+#define R_IA64_COPY 0x84 -+#define R_IA64_SUB 0x85 -+#define R_IA64_LTOFF22X 0x86 -+#define R_IA64_LDXMOV 0x87 -+#define R_IA64_TPREL14 0x91 -+#define R_IA64_TPREL22 0x92 -+#define R_IA64_TPREL64I 0x93 -+#define R_IA64_TPREL64MSB 0x96 -+#define R_IA64_TPREL64LSB 0x97 -+#define R_IA64_LTOFF_TPREL22 0x9a -+#define R_IA64_DTPMOD64MSB 0xa6 -+#define R_IA64_DTPMOD64LSB 0xa7 -+#define R_IA64_LTOFF_DTPMOD22 0xaa -+#define R_IA64_DTPREL14 0xb1 -+#define R_IA64_DTPREL22 0xb2 -+#define R_IA64_DTPREL64I 0xb3 -+#define R_IA64_DTPREL32MSB 0xb4 -+#define R_IA64_DTPREL32LSB 0xb5 -+#define R_IA64_DTPREL64MSB 0xb6 -+#define R_IA64_DTPREL64LSB 0xb7 -+#define R_IA64_LTOFF_DTPREL22 0xba -+ -+ -+ -+ -+#define R_SH_NONE 0 -+#define R_SH_DIR32 1 -+#define R_SH_REL32 2 -+#define R_SH_DIR8WPN 3 -+#define R_SH_IND12W 4 -+#define R_SH_DIR8WPL 5 -+#define R_SH_DIR8WPZ 6 -+#define R_SH_DIR8BP 7 -+#define R_SH_DIR8W 8 -+#define R_SH_DIR8L 9 -+#define R_SH_SWITCH16 25 -+#define R_SH_SWITCH32 26 -+#define R_SH_USES 27 -+#define R_SH_COUNT 28 -+#define R_SH_ALIGN 29 -+#define R_SH_CODE 30 -+#define R_SH_DATA 31 -+#define R_SH_LABEL 32 -+#define R_SH_SWITCH8 33 -+#define R_SH_GNU_VTINHERIT 34 -+#define R_SH_GNU_VTENTRY 35 -+#define R_SH_TLS_GD_32 144 -+#define R_SH_TLS_LD_32 145 -+#define R_SH_TLS_LDO_32 146 -+#define R_SH_TLS_IE_32 147 -+#define R_SH_TLS_LE_32 148 -+#define R_SH_TLS_DTPMOD32 149 -+#define R_SH_TLS_DTPOFF32 150 -+#define R_SH_TLS_TPOFF32 151 -+#define R_SH_GOT32 160 -+#define R_SH_PLT32 161 -+#define R_SH_COPY 162 -+#define R_SH_GLOB_DAT 163 -+#define R_SH_JMP_SLOT 164 -+#define R_SH_RELATIVE 165 -+#define R_SH_GOTOFF 166 -+#define R_SH_GOTPC 167 -+ -+#define R_SH_NUM 256 -+ -+ -+ -+#define R_390_NONE 0 -+#define R_390_8 1 -+#define R_390_12 2 -+#define R_390_16 3 -+#define R_390_32 4 -+#define R_390_PC32 5 -+#define R_390_GOT12 6 -+#define R_390_GOT32 7 -+#define R_390_PLT32 8 -+#define R_390_COPY 9 -+#define R_390_GLOB_DAT 10 -+#define R_390_JMP_SLOT 11 -+#define R_390_RELATIVE 12 -+#define R_390_GOTOFF32 13 -+#define R_390_GOTPC 14 -+#define R_390_GOT16 15 -+#define R_390_PC16 16 -+#define R_390_PC16DBL 17 -+#define R_390_PLT16DBL 18 -+#define R_390_PC32DBL 19 -+#define R_390_PLT32DBL 20 -+#define R_390_GOTPCDBL 21 -+#define R_390_64 22 -+#define R_390_PC64 23 -+#define R_390_GOT64 24 -+#define R_390_PLT64 25 -+#define R_390_GOTENT 26 -+#define R_390_GOTOFF16 27 -+#define R_390_GOTOFF64 28 -+#define R_390_GOTPLT12 29 -+#define R_390_GOTPLT16 30 -+#define R_390_GOTPLT32 31 -+#define R_390_GOTPLT64 32 -+#define R_390_GOTPLTENT 33 -+#define R_390_PLTOFF16 34 -+#define R_390_PLTOFF32 35 -+#define R_390_PLTOFF64 36 -+#define R_390_TLS_LOAD 37 -+#define R_390_TLS_GDCALL 38 -+ -+#define R_390_TLS_LDCALL 39 -+ -+#define R_390_TLS_GD32 40 -+ -+#define R_390_TLS_GD64 41 -+ -+#define R_390_TLS_GOTIE12 42 -+ -+#define R_390_TLS_GOTIE32 43 -+ -+#define R_390_TLS_GOTIE64 44 -+ -+#define R_390_TLS_LDM32 45 -+ -+#define R_390_TLS_LDM64 46 -+ -+#define R_390_TLS_IE32 47 -+ -+#define R_390_TLS_IE64 48 -+ -+#define R_390_TLS_IEENT 49 -+ -+#define R_390_TLS_LE32 50 -+ -+#define R_390_TLS_LE64 51 -+ -+#define R_390_TLS_LDO32 52 -+ -+#define R_390_TLS_LDO64 53 -+ -+#define R_390_TLS_DTPMOD 54 -+#define R_390_TLS_DTPOFF 55 -+#define R_390_TLS_TPOFF 56 -+ -+#define R_390_20 57 -+#define R_390_GOT20 58 -+#define R_390_GOTPLT20 59 -+#define R_390_TLS_GOTIE20 60 -+ -+ -+#define R_390_NUM 61 -+ -+ -+ -+#define R_CRIS_NONE 0 -+#define R_CRIS_8 1 -+#define R_CRIS_16 2 -+#define R_CRIS_32 3 -+#define R_CRIS_8_PCREL 4 -+#define R_CRIS_16_PCREL 5 -+#define R_CRIS_32_PCREL 6 -+#define R_CRIS_GNU_VTINHERIT 7 -+#define R_CRIS_GNU_VTENTRY 8 -+#define R_CRIS_COPY 9 -+#define R_CRIS_GLOB_DAT 10 -+#define R_CRIS_JUMP_SLOT 11 -+#define R_CRIS_RELATIVE 12 -+#define R_CRIS_16_GOT 13 -+#define R_CRIS_32_GOT 14 -+#define R_CRIS_16_GOTPLT 15 -+#define R_CRIS_32_GOTPLT 16 -+#define R_CRIS_32_GOTREL 17 -+#define R_CRIS_32_PLT_GOTREL 18 -+#define R_CRIS_32_PLT_PCREL 19 -+ -+#define R_CRIS_NUM 20 -+ -+ -+ -+#define R_X86_64_NONE 0 -+#define R_X86_64_64 1 -+#define R_X86_64_PC32 2 -+#define R_X86_64_GOT32 3 -+#define R_X86_64_PLT32 4 -+#define R_X86_64_COPY 5 -+#define R_X86_64_GLOB_DAT 6 -+#define R_X86_64_JUMP_SLOT 7 -+#define R_X86_64_RELATIVE 8 -+#define R_X86_64_GOTPCREL 9 -+ -+#define R_X86_64_32 10 -+#define R_X86_64_32S 11 -+#define R_X86_64_16 12 -+#define R_X86_64_PC16 13 -+#define R_X86_64_8 14 -+#define R_X86_64_PC8 15 -+#define R_X86_64_DTPMOD64 16 -+#define R_X86_64_DTPOFF64 17 -+#define R_X86_64_TPOFF64 18 -+#define R_X86_64_TLSGD 19 -+ -+#define R_X86_64_TLSLD 20 -+ -+#define R_X86_64_DTPOFF32 21 -+#define R_X86_64_GOTTPOFF 22 -+ -+#define R_X86_64_TPOFF32 23 -+#define R_X86_64_PC64 24 -+#define R_X86_64_GOTOFF64 25 -+#define R_X86_64_GOTPC32 26 -+#define R_X86_64_GOT64 27 -+#define R_X86_64_GOTPCREL64 28 -+#define R_X86_64_GOTPC64 29 -+#define R_X86_64_GOTPLT64 30 -+#define R_X86_64_PLTOFF64 31 -+#define R_X86_64_SIZE32 32 -+#define R_X86_64_SIZE64 33 -+ -+#define R_X86_64_GOTPC32_TLSDESC 34 -+#define R_X86_64_TLSDESC_CALL 35 -+ -+#define R_X86_64_TLSDESC 36 -+#define R_X86_64_IRELATIVE 37 -+#define R_X86_64_RELATIVE64 38 -+#define R_X86_64_NUM 39 -+ -+ -+ -+#define R_MN10300_NONE 0 -+#define R_MN10300_32 1 -+#define R_MN10300_16 2 -+#define R_MN10300_8 3 -+#define R_MN10300_PCREL32 4 -+#define R_MN10300_PCREL16 5 -+#define R_MN10300_PCREL8 6 -+#define R_MN10300_GNU_VTINHERIT 7 -+#define R_MN10300_GNU_VTENTRY 8 -+#define R_MN10300_24 9 -+#define R_MN10300_GOTPC32 10 -+#define R_MN10300_GOTPC16 11 -+#define R_MN10300_GOTOFF32 12 -+#define R_MN10300_GOTOFF24 13 -+#define R_MN10300_GOTOFF16 14 -+#define R_MN10300_PLT32 15 -+#define R_MN10300_PLT16 16 -+#define R_MN10300_GOT32 17 -+#define R_MN10300_GOT24 18 -+#define R_MN10300_GOT16 19 -+#define R_MN10300_COPY 20 -+#define R_MN10300_GLOB_DAT 21 -+#define R_MN10300_JMP_SLOT 22 -+#define R_MN10300_RELATIVE 23 -+ -+#define R_MN10300_NUM 24 -+ -+ -+ -+#define R_M32R_NONE 0 -+#define R_M32R_16 1 -+#define R_M32R_32 2 -+#define R_M32R_24 3 -+#define R_M32R_10_PCREL 4 -+#define R_M32R_18_PCREL 5 -+#define R_M32R_26_PCREL 6 -+#define R_M32R_HI16_ULO 7 -+#define R_M32R_HI16_SLO 8 -+#define R_M32R_LO16 9 -+#define R_M32R_SDA16 10 -+#define R_M32R_GNU_VTINHERIT 11 -+#define R_M32R_GNU_VTENTRY 12 -+ -+#define R_M32R_16_RELA 33 -+#define R_M32R_32_RELA 34 -+#define R_M32R_24_RELA 35 -+#define R_M32R_10_PCREL_RELA 36 -+#define R_M32R_18_PCREL_RELA 37 -+#define R_M32R_26_PCREL_RELA 38 -+#define R_M32R_HI16_ULO_RELA 39 -+#define R_M32R_HI16_SLO_RELA 40 -+#define R_M32R_LO16_RELA 41 -+#define R_M32R_SDA16_RELA 42 -+#define R_M32R_RELA_GNU_VTINHERIT 43 -+#define R_M32R_RELA_GNU_VTENTRY 44 -+#define R_M32R_REL32 45 -+ -+#define R_M32R_GOT24 48 -+#define R_M32R_26_PLTREL 49 -+#define R_M32R_COPY 50 -+#define R_M32R_GLOB_DAT 51 -+#define R_M32R_JMP_SLOT 52 -+#define R_M32R_RELATIVE 53 -+#define R_M32R_GOTOFF 54 -+#define R_M32R_GOTPC24 55 -+#define R_M32R_GOT16_HI_ULO 56 -+ -+#define R_M32R_GOT16_HI_SLO 57 -+ -+#define R_M32R_GOT16_LO 58 -+#define R_M32R_GOTPC_HI_ULO 59 -+ -+#define R_M32R_GOTPC_HI_SLO 60 -+ -+#define R_M32R_GOTPC_LO 61 -+ -+#define R_M32R_GOTOFF_HI_ULO 62 -+ -+#define R_M32R_GOTOFF_HI_SLO 63 -+ -+#define R_M32R_GOTOFF_LO 64 -+#define R_M32R_NUM 256 -+ -+#define R_MICROBLAZE_NONE 0 -+#define R_MICROBLAZE_32 1 -+#define R_MICROBLAZE_32_PCREL 2 -+#define R_MICROBLAZE_64_PCREL 3 -+#define R_MICROBLAZE_32_PCREL_LO 4 -+#define R_MICROBLAZE_64 5 -+#define R_MICROBLAZE_32_LO 6 -+#define R_MICROBLAZE_SRO32 7 -+#define R_MICROBLAZE_SRW32 8 -+#define R_MICROBLAZE_64_NONE 9 -+#define R_MICROBLAZE_32_SYM_OP_SYM 10 -+#define R_MICROBLAZE_GNU_VTINHERIT 11 -+#define R_MICROBLAZE_GNU_VTENTRY 12 -+#define R_MICROBLAZE_GOTPC_64 13 -+#define R_MICROBLAZE_GOT_64 14 -+#define R_MICROBLAZE_PLT_64 15 -+#define R_MICROBLAZE_REL 16 -+#define R_MICROBLAZE_JUMP_SLOT 17 -+#define R_MICROBLAZE_GLOB_DAT 18 -+#define R_MICROBLAZE_GOTOFF_64 19 -+#define R_MICROBLAZE_GOTOFF_32 20 -+#define R_MICROBLAZE_COPY 21 -+#define R_MICROBLAZE_TLS 22 -+#define R_MICROBLAZE_TLSGD 23 -+#define R_MICROBLAZE_TLSLD 24 -+#define R_MICROBLAZE_TLSDTPMOD32 25 -+#define R_MICROBLAZE_TLSDTPREL32 26 -+#define R_MICROBLAZE_TLSDTPREL64 27 -+#define R_MICROBLAZE_TLSGOTTPREL32 28 -+#define R_MICROBLAZE_TLSTPREL32 29 -+ -+#ifdef __cplusplus -+} -+#endif -+ -+ -+#endif diff --git a/target/linux/patches/3.18.14/sgidefs.patch b/target/linux/patches/3.18.14/sgidefs.patch deleted file mode 100644 index f00a284d9..000000000 --- a/target/linux/patches/3.18.14/sgidefs.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff -Nur linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h ---- linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h 2013-11-08 22:01:28.000000000 +0100 -@@ -11,14 +11,6 @@ - #define __ASM_SGIDEFS_H - - /* -- * Using a Linux compiler for building Linux seems logic but not to -- * everybody. -- */ --#ifndef __linux__ --#error Use a Linux compiler or give up. --#endif -- --/* - * Definitions for the ISA levels - * - * With the introduction of MIPS32 / MIPS64 instruction sets definitions diff --git a/target/linux/patches/3.18.14/sortext.patch b/target/linux/patches/3.18.14/sortext.patch deleted file mode 100644 index 8fd4e1d6b..000000000 --- a/target/linux/patches/3.18.14/sortext.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -Nur linux-3.12.6.orig/arch/arm/Kconfig linux-3.12.6/arch/arm/Kconfig ---- linux-3.12.6.orig/arch/arm/Kconfig 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/arch/arm/Kconfig 2013-12-28 19:29:33.000000000 +0100 -@@ -6,7 +6,6 @@ - select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST - select ARCH_HAVE_CUSTOM_GPIO_H - select ARCH_WANT_IPC_PARSE_VERSION -- select BUILDTIME_EXTABLE_SORT if MMU - select CLONE_BACKWARDS - select CPU_PM if (SUSPEND || CPU_IDLE) - select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && !CPU_BIG_ENDIAN && MMU -diff -Nur linux-3.12.6.orig/arch/mips/Kconfig linux-3.12.6/arch/mips/Kconfig ---- linux-3.12.6.orig/arch/mips/Kconfig 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/arch/mips/Kconfig 2013-12-28 19:30:06.000000000 +0100 -@@ -35,7 +35,6 @@ - select HAVE_MEMBLOCK_NODE_MAP - select ARCH_DISCARD_MEMBLOCK - select GENERIC_SMP_IDLE_THREAD -- select BUILDTIME_EXTABLE_SORT - select GENERIC_CLOCKEVENTS - select GENERIC_CMOS_UPDATE - select HAVE_MOD_ARCH_SPECIFIC -diff -Nur linux-3.12.6.orig/arch/x86/Kconfig linux-3.12.6/arch/x86/Kconfig ---- linux-3.12.6.orig/arch/x86/Kconfig 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/arch/x86/Kconfig 2013-12-28 19:29:50.000000000 +0100 -@@ -100,7 +100,6 @@ - select GENERIC_SMP_IDLE_THREAD - select ARCH_WANT_IPC_PARSE_VERSION if X86_32 - select HAVE_ARCH_SECCOMP_FILTER -- select BUILDTIME_EXTABLE_SORT - select GENERIC_CMOS_UPDATE - select HAVE_ARCH_SOFT_DIRTY - select CLOCKSOURCE_WATCHDOG diff --git a/target/linux/patches/3.18.14/startup.patch b/target/linux/patches/3.18.14/startup.patch deleted file mode 100644 index d396b75e4..000000000 --- a/target/linux/patches/3.18.14/startup.patch +++ /dev/null @@ -1,37 +0,0 @@ -diff -Nur linux-3.13.3.orig/init/main.c linux-3.13.3/init/main.c ---- linux-3.13.3.orig/init/main.c 2014-02-13 23:00:14.000000000 +0100 -+++ linux-3.13.3/init/main.c 2014-02-17 11:35:14.000000000 +0100 -@@ -916,6 +917,8 @@ - if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - pr_err("Warning: unable to open an initial console.\n"); - -+ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); -+ - (void) sys_dup(0); - (void) sys_dup(0); - /* -diff -Nur linux-3.13.6.orig/init/initramfs.c linux-3.13.6/init/initramfs.c ---- linux-3.13.6.orig/init/initramfs.c 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/init/initramfs.c 2014-03-15 12:11:31.882731916 +0100 -@@ -622,6 +622,9 @@ - */ - load_default_modules(); - } -+#ifdef CONFIG_DEVTMPFS_MOUNT -+ devtmpfs_mount("dev"); -+#endif - return 0; - } - rootfs_initcall(populate_rootfs); -diff -Nur linux-3.13.6.orig/init/main.c linux-3.13.6/init/main.c ---- linux-3.13.6.orig/init/main.c 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/init/main.c 2014-03-15 12:13:16.459024452 +0100 -@@ -924,7 +924,7 @@ - */ - - if (!ramdisk_execute_command) -- ramdisk_execute_command = "/init"; -+ ramdisk_execute_command = "/sbin/init"; - - if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { - ramdisk_execute_command = NULL; diff --git a/target/linux/patches/3.18.14/wlan-cf.patch b/target/linux/patches/3.18.14/wlan-cf.patch deleted file mode 100644 index fc20759e2..000000000 --- a/target/linux/patches/3.18.14/wlan-cf.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c ---- linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c 2011-05-19 06:06:34.000000000 +0200 -+++ linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c 2011-09-12 02:46:26.987984145 +0200 -@@ -623,6 +623,7 @@ - static struct pcmcia_device_id hostap_cs_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), -+ PCMCIA_DEVICE_MANF_CARD(0x0004, 0x2003), - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), - PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), - PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), diff --git a/target/linux/patches/3.18.14/xargs.patch b/target/linux/patches/3.18.14/xargs.patch deleted file mode 100644 index 2c7b3df59..000000000 --- a/target/linux/patches/3.18.14/xargs.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-3.12.6.orig/scripts/Makefile.modpost linux-3.12.6/scripts/Makefile.modpost ---- linux-3.12.6.orig/scripts/Makefile.modpost 2013-12-20 16:51:33.000000000 +0100 -+++ linux-3.12.6/scripts/Makefile.modpost 2014-01-25 14:55:33.000000000 +0100 -@@ -60,7 +60,7 @@ - modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers - - # Step 1), find all modules listed in $(MODVERDIR)/ --MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs -r grep -h '\.ko$$' | sort -u -+MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs grep -h '\.ko$$' | sort -u - __modules := $(shell $(MODLISTCMD)) - modules := $(patsubst %.o,%.ko, $(wildcard $(__modules:.ko=.o))) - diff --git a/target/linux/patches/3.18.14/yaffs2.patch b/target/linux/patches/3.18.14/yaffs2.patch deleted file mode 100644 index 2ade5bf7e..000000000 --- a/target/linux/patches/3.18.14/yaffs2.patch +++ /dev/null @@ -1,16559 +0,0 @@ -diff -Nur linux-3.18.14.orig/fs/Kconfig linux-3.18.14/fs/Kconfig ---- linux-3.18.14.orig/fs/Kconfig 2015-05-20 17:04:50.000000000 +0200 -+++ linux-3.18.14/fs/Kconfig 2015-06-14 21:22:55.000000000 +0200 -@@ -191,6 +191,7 @@ - source "fs/befs/Kconfig" - source "fs/bfs/Kconfig" - source "fs/efs/Kconfig" -+source "fs/yaffs2/Kconfig" - source "fs/jffs2/Kconfig" - # UBIFS File system configuration - source "fs/ubifs/Kconfig" -diff -Nur linux-3.18.14.orig/fs/Makefile linux-3.18.14/fs/Makefile ---- linux-3.18.14.orig/fs/Makefile 2015-05-20 17:04:50.000000000 +0200 -+++ linux-3.18.14/fs/Makefile 2015-06-14 21:22:55.000000000 +0200 -@@ -126,3 +126,4 @@ - obj-$(CONFIG_CEPH_FS) += ceph/ - obj-$(CONFIG_PSTORE) += pstore/ - obj-$(CONFIG_EFIVAR_FS) += efivarfs/ -+obj-$(CONFIG_YAFFS_FS) += yaffs2/ -diff -Nur linux-3.18.14.orig/fs/yaffs2/Kconfig linux-3.18.14/fs/yaffs2/Kconfig ---- linux-3.18.14.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/Kconfig 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,171 @@ -+# -+# yaffs file system configurations -+# -+ -+config YAFFS_FS -+ tristate "yaffs2 file system support" -+ default n -+ depends on MTD_BLOCK -+ select YAFFS_YAFFS1 -+ select YAFFS_YAFFS2 -+ help -+ yaffs2, or Yet Another Flash File System, is a file system -+ optimised for NAND Flash chips. -+ -+ To compile the yaffs2 file system support as a module, choose M -+ here: the module will be called yaffs2. -+ -+ If unsure, say N. -+ -+ Further information on yaffs2 is available at -+ . -+ -+config YAFFS_YAFFS1 -+ bool "512 byte / page devices" -+ depends on YAFFS_FS -+ default y -+ help -+ Enable yaffs1 support -- yaffs for 512 byte / page devices -+ -+ Not needed for 2K-page devices. -+ -+ If unsure, say Y. -+ -+config YAFFS_9BYTE_TAGS -+ bool "Use older-style on-NAND data format with pageStatus byte" -+ depends on YAFFS_YAFFS1 -+ default n -+ help -+ -+ Older-style on-NAND data format has a "pageStatus" byte to record -+ chunk/page state. This byte is zero when the page is discarded. -+ Choose this option if you have existing on-NAND data using this -+ format that you need to continue to support. New data written -+ also uses the older-style format. Note: Use of this option -+ generally requires that MTD's oob layout be adjusted to use the -+ older-style format. See notes on tags formats and MTD versions -+ in yaffs_mtdif1.c. -+ -+ If unsure, say N. -+ -+config YAFFS_DOES_ECC -+ bool "Lets yaffs do its own ECC" -+ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS -+ default n -+ help -+ This enables yaffs to use its own ECC functions instead of using -+ the ones from the generic MTD-NAND driver. -+ -+ If unsure, say N. -+ -+config YAFFS_ECC_WRONG_ORDER -+ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" -+ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS -+ default n -+ help -+ This makes yaffs_ecc.c use the same ecc byte order as Steven -+ Hill's nand_ecc.c. If not set, then you get the same ecc byte -+ order as SmartMedia. -+ -+ If unsure, say N. -+ -+config YAFFS_YAFFS2 -+ bool "2048 byte (or larger) / page devices" -+ depends on YAFFS_FS -+ default y -+ help -+ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices -+ -+ If unsure, say Y. -+ -+config YAFFS_AUTO_YAFFS2 -+ bool "Autoselect yaffs2 format" -+ depends on YAFFS_YAFFS2 -+ default y -+ help -+ Without this, you need to explicitely use yaffs2 as the file -+ system type. With this, you can say "yaffs" and yaffs or yaffs2 -+ will be used depending on the device page size (yaffs on -+ 512-byte page devices, yaffs2 on 2K page devices). -+ -+ If unsure, say Y. -+ -+config YAFFS_DISABLE_TAGS_ECC -+ bool "Disable yaffs from doing ECC on tags by default" -+ depends on YAFFS_FS && YAFFS_YAFFS2 -+ default n -+ help -+ This defaults yaffs to using its own ECC calculations on tags instead of -+ just relying on the MTD. -+ This behavior can also be overridden with tags_ecc_on and -+ tags_ecc_off mount options. -+ -+ If unsure, say N. -+ -+config YAFFS_ALWAYS_CHECK_CHUNK_ERASED -+ bool "Force chunk erase check" -+ depends on YAFFS_FS -+ default n -+ help -+ Normally yaffs only checks chunks before writing until an erased -+ chunk is found. This helps to detect any partially written -+ chunks that might have happened due to power loss. -+ -+ Enabling this forces on the test that chunks are erased in flash -+ before writing to them. This takes more time but is potentially -+ a bit more secure. -+ -+ Suggest setting Y during development and ironing out driver -+ issues etc. Suggest setting to N if you want faster writing. -+ -+ If unsure, say Y. -+ -+config YAFFS_EMPTY_LOST_AND_FOUND -+ bool "Empty lost and found on boot" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is enabled then the contents of lost and found is -+ automatically dumped at mount. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BLOCK_REFRESHING -+ bool "Disable yaffs2 block refreshing" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is set, then block refreshing is disabled. -+ Block refreshing infrequently refreshes the oldest block in -+ a yaffs2 file system. This mechanism helps to refresh flash to -+ mitigate against data loss. This is particularly useful for MLC. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BACKGROUND -+ bool "Disable yaffs2 background processing" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is set, then background processing is disabled. -+ Background processing makes many foreground activities faster. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BAD_BLOCK_MARKING -+ bool "Disable yaffs2 bad block marking" -+ depends on YAFFS_FS -+ default n -+ help -+ Useful during early flash bring up to prevent problems causing -+ lots of bad block marking. -+ -+ If unsure, say N. -+ -+config YAFFS_XATTR -+ bool "Enable yaffs2 xattr support" -+ depends on YAFFS_FS -+ default y -+ help -+ If this is set then yaffs2 will provide xattr support. -+ If unsure, say Y. -diff -Nur linux-3.18.14.orig/fs/yaffs2/Makefile linux-3.18.14/fs/yaffs2/Makefile ---- linux-3.18.14.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/Makefile 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,18 @@ -+# -+# Makefile for the linux YAFFS filesystem routines. -+# -+ -+obj-$(CONFIG_YAFFS_FS) += yaffs.o -+ -+yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o -+yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o -+yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o -+yaffs-y += yaffs_mtdif.o -+yaffs-y += yaffs_nameval.o yaffs_attribs.o -+yaffs-y += yaffs_allocator.o -+yaffs-y += yaffs_yaffs1.o -+yaffs-y += yaffs_yaffs2.o -+yaffs-y += yaffs_bitmap.o -+yaffs-y += yaffs_summary.o -+yaffs-y += yaffs_verify.o -+ -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_allocator.c linux-3.18.14/fs/yaffs2/yaffs_allocator.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_allocator.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,357 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_allocator.h" -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yportenv.h" -+ -+/* -+ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks -+ * of approx 100 objects that are themn allocated singly. -+ * This is basically a simplified slab allocator. -+ * -+ * We don't use the Linux slab allocator because slab does not allow -+ * us to dump all the objects in one hit when we do a umount and tear -+ * down all the tnodes and objects. slab requires that we first free -+ * the individual objects. -+ * -+ * Once yaffs has been mainlined I shall try to motivate for a change -+ * to slab to provide the extra features we need here. -+ */ -+ -+struct yaffs_tnode_list { -+ struct yaffs_tnode_list *next; -+ struct yaffs_tnode *tnodes; -+}; -+ -+struct yaffs_obj_list { -+ struct yaffs_obj_list *next; -+ struct yaffs_obj *objects; -+}; -+ -+struct yaffs_allocator { -+ int n_tnodes_created; -+ struct yaffs_tnode *free_tnodes; -+ int n_free_tnodes; -+ struct yaffs_tnode_list *alloc_tnode_list; -+ -+ int n_obj_created; -+ struct list_head free_objs; -+ int n_free_objects; -+ -+ struct yaffs_obj_list *allocated_obj_list; -+}; -+ -+static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ struct yaffs_tnode_list *tmp; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ while (allocator->alloc_tnode_list) { -+ tmp = allocator->alloc_tnode_list->next; -+ -+ kfree(allocator->alloc_tnode_list->tnodes); -+ kfree(allocator->alloc_tnode_list); -+ allocator->alloc_tnode_list = tmp; -+ } -+ -+ allocator->free_tnodes = NULL; -+ allocator->n_free_tnodes = 0; -+ allocator->n_tnodes_created = 0; -+} -+ -+static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator->alloc_tnode_list = NULL; -+ allocator->free_tnodes = NULL; -+ allocator->n_free_tnodes = 0; -+ allocator->n_tnodes_created = 0; -+} -+ -+static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ int i; -+ struct yaffs_tnode *new_tnodes; -+ u8 *mem; -+ struct yaffs_tnode *curr; -+ struct yaffs_tnode *next; -+ struct yaffs_tnode_list *tnl; -+ -+ if (!allocator) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ if (n_tnodes < 1) -+ return YAFFS_OK; -+ -+ /* make these things */ -+ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); -+ mem = (u8 *) new_tnodes; -+ -+ if (!new_tnodes) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs: Could not allocate Tnodes"); -+ return YAFFS_FAIL; -+ } -+ -+ /* New hookup for wide tnodes */ -+ for (i = 0; i < n_tnodes - 1; i++) { -+ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; -+ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; -+ curr->internal[0] = next; -+ } -+ -+ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; -+ curr->internal[0] = allocator->free_tnodes; -+ allocator->free_tnodes = (struct yaffs_tnode *)mem; -+ -+ allocator->n_free_tnodes += n_tnodes; -+ allocator->n_tnodes_created += n_tnodes; -+ -+ /* Now add this bunch of tnodes to a list for freeing up. -+ * NB If we can't add this to the management list it isn't fatal -+ * but it just means we can't free this bunch of tnodes later. -+ */ -+ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); -+ if (!tnl) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Could not add tnodes to management list"); -+ return YAFFS_FAIL; -+ } else { -+ tnl->tnodes = new_tnodes; -+ tnl->next = allocator->alloc_tnode_list; -+ allocator->alloc_tnode_list = tnl; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); -+ -+ return YAFFS_OK; -+} -+ -+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ struct yaffs_tnode *tn = NULL; -+ -+ if (!allocator) { -+ BUG(); -+ return NULL; -+ } -+ -+ /* If there are none left make more */ -+ if (!allocator->free_tnodes) -+ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); -+ -+ if (allocator->free_tnodes) { -+ tn = allocator->free_tnodes; -+ allocator->free_tnodes = allocator->free_tnodes->internal[0]; -+ allocator->n_free_tnodes--; -+ } -+ -+ return tn; -+} -+ -+/* FreeTnode frees up a tnode and puts it back on the free list */ -+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ if (tn) { -+ tn->internal[0] = allocator->free_tnodes; -+ allocator->free_tnodes = tn; -+ allocator->n_free_tnodes++; -+ } -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+/*--------------- yaffs_obj alloaction ------------------------ -+ * -+ * Free yaffs_objs are stored in a list using obj->siblings. -+ * The blocks of allocated objects are stored in a linked list. -+ */ -+ -+static void yaffs_init_raw_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator->allocated_obj_list = NULL; -+ INIT_LIST_HEAD(&allocator->free_objs); -+ allocator->n_free_objects = 0; -+} -+ -+static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ struct yaffs_obj_list *tmp; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ while (allocator->allocated_obj_list) { -+ tmp = allocator->allocated_obj_list->next; -+ kfree(allocator->allocated_obj_list->objects); -+ kfree(allocator->allocated_obj_list); -+ allocator->allocated_obj_list = tmp; -+ } -+ -+ INIT_LIST_HEAD(&allocator->free_objs); -+ allocator->n_free_objects = 0; -+ allocator->n_obj_created = 0; -+} -+ -+static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ int i; -+ struct yaffs_obj *new_objs; -+ struct yaffs_obj_list *list; -+ -+ if (!allocator) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ if (n_obj < 1) -+ return YAFFS_OK; -+ -+ /* make these things */ -+ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); -+ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); -+ -+ if (!new_objs || !list) { -+ kfree(new_objs); -+ new_objs = NULL; -+ kfree(list); -+ list = NULL; -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, -+ "Could not allocate more objects"); -+ return YAFFS_FAIL; -+ } -+ -+ /* Hook them into the free list */ -+ for (i = 0; i < n_obj; i++) -+ list_add(&new_objs[i].siblings, &allocator->free_objs); -+ -+ allocator->n_free_objects += n_obj; -+ allocator->n_obj_created += n_obj; -+ -+ /* Now add this bunch of Objects to a list for freeing up. */ -+ -+ list->objects = new_objs; -+ list->next = allocator->allocated_obj_list; -+ allocator->allocated_obj_list = list; -+ -+ return YAFFS_OK; -+} -+ -+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj = NULL; -+ struct list_head *lh; -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return obj; -+ } -+ -+ /* If there are none left make more */ -+ if (list_empty(&allocator->free_objs)) -+ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); -+ -+ if (!list_empty(&allocator->free_objs)) { -+ lh = allocator->free_objs.next; -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ list_del_init(lh); -+ allocator->n_free_objects--; -+ } -+ -+ return obj; -+} -+ -+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) -+{ -+ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ /* Link into the free list. */ -+ list_add(&obj->siblings, &allocator->free_objs); -+ allocator->n_free_objects++; -+} -+ -+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ -+ if (!dev->allocator) { -+ BUG(); -+ return; -+ } -+ -+ yaffs_deinit_raw_tnodes(dev); -+ yaffs_deinit_raw_objs(dev); -+ kfree(dev->allocator); -+ dev->allocator = NULL; -+} -+ -+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator; -+ -+ if (dev->allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); -+ if (allocator) { -+ dev->allocator = allocator; -+ yaffs_init_raw_tnodes(dev); -+ yaffs_init_raw_objs(dev); -+ } -+} -+ -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_allocator.h linux-3.18.14/fs/yaffs2/yaffs_allocator.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_allocator.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,30 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_ALLOCATOR_H__ -+#define __YAFFS_ALLOCATOR_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); -+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); -+ -+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); -+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); -+ -+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); -+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_attribs.c linux-3.18.14/fs/yaffs2/yaffs_attribs.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_attribs.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,132 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_attribs.h" -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) -+#define IATTR_UID ia_uid -+#define IATTR_GID ia_gid -+#else -+#define IATTR_UID ia_uid.val -+#define IATTR_GID ia_gid.val -+#endif -+ -+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) -+{ -+ obj->yst_uid = oh->yst_uid; -+ obj->yst_gid = oh->yst_gid; -+ obj->yst_atime = oh->yst_atime; -+ obj->yst_mtime = oh->yst_mtime; -+ obj->yst_ctime = oh->yst_ctime; -+ obj->yst_rdev = oh->yst_rdev; -+} -+ -+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) -+{ -+ oh->yst_uid = obj->yst_uid; -+ oh->yst_gid = obj->yst_gid; -+ oh->yst_atime = obj->yst_atime; -+ oh->yst_mtime = obj->yst_mtime; -+ oh->yst_ctime = obj->yst_ctime; -+ oh->yst_rdev = obj->yst_rdev; -+ -+} -+ -+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) -+{ -+ obj->yst_mtime = Y_CURRENT_TIME; -+ if (do_a) -+ obj->yst_atime = obj->yst_mtime; -+ if (do_c) -+ obj->yst_ctime = obj->yst_mtime; -+} -+ -+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) -+{ -+ yaffs_load_current_time(obj, 1, 1); -+ obj->yst_rdev = rdev; -+ obj->yst_uid = uid; -+ obj->yst_gid = gid; -+} -+ -+static loff_t yaffs_get_file_size(struct yaffs_obj *obj) -+{ -+ YCHAR *alias = NULL; -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return obj->variant.file_variant.file_size; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ alias = obj->variant.symlink_variant.alias; -+ if (!alias) -+ return 0; -+ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); -+ default: -+ return 0; -+ } -+} -+ -+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) -+{ -+ unsigned int valid = attr->ia_valid; -+ -+ if (valid & ATTR_MODE) -+ obj->yst_mode = attr->ia_mode; -+ if (valid & ATTR_UID) -+ obj->yst_uid = attr->IATTR_UID; -+ if (valid & ATTR_GID) -+ obj->yst_gid = attr->IATTR_GID; -+ -+ if (valid & ATTR_ATIME) -+ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); -+ if (valid & ATTR_CTIME) -+ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); -+ if (valid & ATTR_MTIME) -+ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); -+ -+ if (valid & ATTR_SIZE) -+ yaffs_resize_file(obj, attr->ia_size); -+ -+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); -+ -+ return YAFFS_OK; -+ -+} -+ -+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) -+{ -+ unsigned int valid = 0; -+ -+ attr->ia_mode = obj->yst_mode; -+ valid |= ATTR_MODE; -+ attr->IATTR_UID = obj->yst_uid; -+ valid |= ATTR_UID; -+ attr->IATTR_GID = obj->yst_gid; -+ valid |= ATTR_GID; -+ -+ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; -+ valid |= ATTR_ATIME; -+ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; -+ valid |= ATTR_CTIME; -+ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; -+ valid |= ATTR_MTIME; -+ -+ attr->ia_size = yaffs_get_file_size(obj); -+ valid |= ATTR_SIZE; -+ -+ attr->ia_valid = valid; -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_attribs.h linux-3.18.14/fs/yaffs2/yaffs_attribs.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_attribs.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,28 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_ATTRIBS_H__ -+#define __YAFFS_ATTRIBS_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); -+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); -+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); -+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); -+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); -+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_bitmap.c linux-3.18.14/fs/yaffs2/yaffs_bitmap.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_bitmap.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,97 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_bitmap.h" -+#include "yaffs_trace.h" -+/* -+ * Chunk bitmap manipulations -+ */ -+ -+static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "BlockBits block %d is not valid", -+ blk); -+ BUG(); -+ } -+ return dev->chunk_bits + -+ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); -+} -+ -+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block || -+ chunk < 0 || chunk >= dev->param.chunks_per_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Chunk Id (%d:%d) invalid", -+ blk, chunk); -+ BUG(); -+ } -+} -+ -+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ memset(blk_bits, 0, dev->chunk_bit_stride); -+} -+ -+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); -+} -+ -+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ blk_bits[chunk / 8] |= (1 << (chunk & 7)); -+} -+ -+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; -+} -+ -+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ int i; -+ -+ for (i = 0; i < dev->chunk_bit_stride; i++) { -+ if (*blk_bits) -+ return 1; -+ blk_bits++; -+ } -+ return 0; -+} -+ -+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ int i; -+ int n = 0; -+ -+ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) -+ n += hweight8(*blk_bits); -+ -+ return n; -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_bitmap.h linux-3.18.14/fs/yaffs2/yaffs_bitmap.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_bitmap.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,33 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* -+ * Chunk bitmap manipulations -+ */ -+ -+#ifndef __YAFFS_BITMAP_H__ -+#define __YAFFS_BITMAP_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); -+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); -+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); -+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.18.14/fs/yaffs2/yaffs_checkptrw.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_checkptrw.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,466 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_checkptrw.h" -+#include "yaffs_getblockinfo.h" -+ -+struct yaffs_checkpt_chunk_hdr { -+ int version; -+ int seq; -+ u32 sum; -+ u32 xor; -+} ; -+ -+ -+static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) -+{ -+ return chunk - dev->chunk_offset; -+} -+ -+static int apply_block_offset(struct yaffs_dev *dev, int block) -+{ -+ return block - dev->block_offset; -+} -+ -+static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_chunk_hdr hdr; -+ -+ hdr.version = YAFFS_CHECKPOINT_VERSION; -+ hdr.seq = dev->checkpt_page_seq; -+ hdr.sum = dev->checkpt_sum; -+ hdr.xor = dev->checkpt_xor; -+ -+ dev->checkpt_byte_offs = sizeof(hdr); -+ -+ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); -+} -+ -+static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_chunk_hdr hdr; -+ -+ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); -+ -+ dev->checkpt_byte_offs = sizeof(hdr); -+ -+ return hdr.version == YAFFS_CHECKPOINT_VERSION && -+ hdr.seq == dev->checkpt_page_seq && -+ hdr.sum == dev->checkpt_sum && -+ hdr.xor == dev->checkpt_xor; -+} -+ -+static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) -+{ -+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpt blocks_avail = %d", blocks_avail); -+ -+ return (blocks_avail <= 0) ? 0 : 1; -+} -+ -+static int yaffs_checkpt_erase(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (!dev->drv.drv_erase_fn) -+ return 0; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checking blocks %d to %d", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); -+ int offset_i = apply_block_offset(dev, i); -+ int result; -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "erasing checkpt block %d", i); -+ -+ dev->n_erasures++; -+ -+ result = dev->drv.drv_erase_fn(dev, offset_i); -+ if(result) { -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += -+ dev->param.chunks_per_block; -+ } else { -+ dev->drv.drv_mark_bad_fn(dev, offset_i); -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ } -+ } -+ } -+ -+ dev->blocks_in_checkpt = 0; -+ -+ return 1; -+} -+ -+static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) -+{ -+ int i; -+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "allocating checkpt block: erased %d reserved %d avail %d next %d ", -+ dev->n_erased_blocks, dev->param.n_reserved_blocks, -+ blocks_avail, dev->checkpt_next_block); -+ -+ if (dev->checkpt_next_block >= 0 && -+ dev->checkpt_next_block <= dev->internal_end_block && -+ blocks_avail > 0) { -+ -+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; -+ i++) { -+ struct yaffs_block_info *bi; -+ -+ bi = yaffs_get_block_info(dev, i); -+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ dev->checkpt_next_block = i + 1; -+ dev->checkpt_cur_block = i; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "allocating checkpt block %d", i); -+ return; -+ } -+ } -+ } -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); -+ -+ dev->checkpt_next_block = -1; -+ dev->checkpt_cur_block = -1; -+} -+ -+static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) -+{ -+ int i; -+ struct yaffs_ext_tags tags; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "find next checkpt block: start: blocks %d next %d", -+ dev->blocks_in_checkpt, dev->checkpt_next_block); -+ -+ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) -+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; -+ i++) { -+ int chunk = i * dev->param.chunks_per_block; -+ enum yaffs_block_state state; -+ u32 seq; -+ -+ dev->tagger.read_chunk_tags_fn(dev, -+ apply_chunk_offset(dev, chunk), -+ NULL, &tags); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", -+ i, (int) state, -+ tags.obj_id, tags.seq_number, -+ tags.ecc_result); -+ -+ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ continue; -+ -+ dev->tagger.query_block_fn(dev, -+ apply_block_offset(dev, i), -+ &state, &seq); -+ if (state == YAFFS_BLOCK_STATE_DEAD) -+ continue; -+ -+ /* Right kind of block */ -+ dev->checkpt_next_block = tags.obj_id; -+ dev->checkpt_cur_block = i; -+ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; -+ dev->blocks_in_checkpt++; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "found checkpt block %d", i); -+ return; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); -+ -+ dev->checkpt_next_block = -1; -+ dev->checkpt_cur_block = -1; -+} -+ -+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) -+{ -+ int i; -+ -+ dev->checkpt_open_write = writing; -+ -+ /* Got the functions we need? */ -+ if (!dev->tagger.write_chunk_tags_fn || -+ !dev->tagger.read_chunk_tags_fn || -+ !dev->drv.drv_erase_fn || -+ !dev->drv.drv_mark_bad_fn) -+ return 0; -+ -+ if (writing && !yaffs2_checkpt_space_ok(dev)) -+ return 0; -+ -+ if (!dev->checkpt_buffer) -+ dev->checkpt_buffer = -+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ dev->checkpt_page_seq = 0; -+ dev->checkpt_byte_count = 0; -+ dev->checkpt_sum = 0; -+ dev->checkpt_xor = 0; -+ dev->checkpt_cur_block = -1; -+ dev->checkpt_cur_chunk = -1; -+ dev->checkpt_next_block = dev->internal_start_block; -+ -+ if (writing) { -+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); -+ yaffs2_checkpt_init_chunk_hdr(dev); -+ return yaffs_checkpt_erase(dev); -+ } -+ -+ /* Opening for a read */ -+ /* Set to a value that will kick off a read */ -+ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; -+ /* A checkpoint block list of 1 checkpoint block per 16 block is -+ * (hopefully) going to be way more than we need */ -+ dev->blocks_in_checkpt = 0; -+ dev->checkpt_max_blocks = -+ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; -+ if (!dev->checkpt_block_list) -+ dev->checkpt_block_list = -+ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); -+ -+ if (!dev->checkpt_block_list) -+ return 0; -+ -+ for (i = 0; i < dev->checkpt_max_blocks; i++) -+ dev->checkpt_block_list[i] = -1; -+ -+ return 1; -+} -+ -+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) -+{ -+ u32 composite_sum; -+ -+ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); -+ *sum = composite_sum; -+ return 1; -+} -+ -+static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) -+{ -+ int chunk; -+ int offset_chunk; -+ struct yaffs_ext_tags tags; -+ -+ if (dev->checkpt_cur_block < 0) { -+ yaffs2_checkpt_find_erased_block(dev); -+ dev->checkpt_cur_chunk = 0; -+ } -+ -+ if (dev->checkpt_cur_block < 0) -+ return 0; -+ -+ tags.is_deleted = 0; -+ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ -+ tags.chunk_id = dev->checkpt_page_seq + 1; -+ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; -+ tags.n_bytes = dev->data_bytes_per_chunk; -+ if (dev->checkpt_cur_chunk == 0) { -+ /* First chunk we write for the block? Set block state to -+ checkpoint */ -+ struct yaffs_block_info *bi = -+ yaffs_get_block_info(dev, dev->checkpt_cur_block); -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ dev->blocks_in_checkpt++; -+ } -+ -+ chunk = -+ dev->checkpt_cur_block * dev->param.chunks_per_block + -+ dev->checkpt_cur_chunk; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", -+ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, -+ tags.obj_id, tags.chunk_id); -+ -+ offset_chunk = apply_chunk_offset(dev, chunk); -+ -+ dev->n_page_writes++; -+ -+ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, -+ dev->checkpt_buffer, &tags); -+ dev->checkpt_page_seq++; -+ dev->checkpt_cur_chunk++; -+ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { -+ dev->checkpt_cur_chunk = 0; -+ dev->checkpt_cur_block = -1; -+ } -+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); -+ -+ yaffs2_checkpt_init_chunk_hdr(dev); -+ -+ -+ return 1; -+} -+ -+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) -+{ -+ int i = 0; -+ int ok = 1; -+ u8 *data_bytes = (u8 *) data; -+ -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ if (!dev->checkpt_open_write) -+ return -1; -+ -+ while (i < n_bytes && ok) { -+ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; -+ dev->checkpt_sum += *data_bytes; -+ dev->checkpt_xor ^= *data_bytes; -+ -+ dev->checkpt_byte_offs++; -+ i++; -+ data_bytes++; -+ dev->checkpt_byte_count++; -+ -+ if (dev->checkpt_byte_offs < 0 || -+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) -+ ok = yaffs2_checkpt_flush_buffer(dev); -+ } -+ -+ return i; -+} -+ -+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) -+{ -+ int i = 0; -+ struct yaffs_ext_tags tags; -+ int chunk; -+ int offset_chunk; -+ u8 *data_bytes = (u8 *) data; -+ -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ if (dev->checkpt_open_write) -+ return -1; -+ -+ while (i < n_bytes) { -+ -+ if (dev->checkpt_byte_offs < 0 || -+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { -+ -+ if (dev->checkpt_cur_block < 0) { -+ yaffs2_checkpt_find_block(dev); -+ dev->checkpt_cur_chunk = 0; -+ } -+ -+ /* Bail out if we can't find a checpoint block */ -+ if (dev->checkpt_cur_block < 0) -+ break; -+ -+ chunk = dev->checkpt_cur_block * -+ dev->param.chunks_per_block + -+ dev->checkpt_cur_chunk; -+ -+ offset_chunk = apply_chunk_offset(dev, chunk); -+ dev->n_page_reads++; -+ -+ /* Read in the next chunk */ -+ dev->tagger.read_chunk_tags_fn(dev, -+ offset_chunk, -+ dev->checkpt_buffer, -+ &tags); -+ -+ /* Bail out if the chunk is corrupted. */ -+ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || -+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || -+ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ break; -+ -+ /* Bail out if it is not a checkpoint chunk. */ -+ if(!yaffs2_checkpt_check_chunk_hdr(dev)) -+ break; -+ -+ dev->checkpt_page_seq++; -+ dev->checkpt_cur_chunk++; -+ -+ if (dev->checkpt_cur_chunk >= -+ dev->param.chunks_per_block) -+ dev->checkpt_cur_block = -1; -+ -+ } -+ -+ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; -+ dev->checkpt_sum += *data_bytes; -+ dev->checkpt_xor ^= *data_bytes; -+ dev->checkpt_byte_offs++; -+ i++; -+ data_bytes++; -+ dev->checkpt_byte_count++; -+ } -+ -+ return i; /* Number of bytes read */ -+} -+ -+int yaffs_checkpt_close(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (dev->checkpt_open_write) { -+ if (dev->checkpt_byte_offs != -+ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) -+ yaffs2_checkpt_flush_buffer(dev); -+ } else if (dev->checkpt_block_list) { -+ for (i = 0; -+ i < dev->blocks_in_checkpt && -+ dev->checkpt_block_list[i] >= 0; i++) { -+ int blk = dev->checkpt_block_list[i]; -+ struct yaffs_block_info *bi = NULL; -+ -+ if (dev->internal_start_block <= blk && -+ blk <= dev->internal_end_block) -+ bi = yaffs_get_block_info(dev, blk); -+ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ } -+ } -+ -+ dev->n_free_chunks -= -+ dev->blocks_in_checkpt * dev->param.chunks_per_block; -+ dev->n_erased_blocks -= dev->blocks_in_checkpt; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", -+ dev->checkpt_byte_count); -+ -+ if (dev->checkpt_buffer) -+ return 1; -+ else -+ return 0; -+} -+ -+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) -+{ -+ /* Erase the checkpoint data */ -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpoint invalidate of %d blocks", -+ dev->blocks_in_checkpt); -+ -+ return yaffs_checkpt_erase(dev); -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.18.14/fs/yaffs2/yaffs_checkptrw.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_checkptrw.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,33 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_CHECKPTRW_H__ -+#define __YAFFS_CHECKPTRW_H__ -+ -+#include "yaffs_guts.h" -+ -+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); -+ -+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); -+ -+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); -+ -+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); -+ -+int yaffs_checkpt_close(struct yaffs_dev *dev); -+ -+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_ecc.c linux-3.18.14/fs/yaffs2/yaffs_ecc.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_ecc.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,281 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * This code implements the ECC algorithm used in SmartMedia. -+ * -+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. -+ * The two unused bit are set to 1. -+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two -+ * such ECC blocks are used on a 512-byte NAND page. -+ * -+ */ -+ -+#include "yportenv.h" -+ -+#include "yaffs_ecc.h" -+ -+/* Table generated by gen-ecc.c -+ * Using a table means we do not have to calculate p1..p4 and p1'..p4' -+ * for each byte of data. These are instead provided in a table in bits7..2. -+ * Bit 0 of each entry indicates whether the entry has an odd or even parity, -+ * and therefore this bytes influence on the line parity. -+ */ -+ -+static const unsigned char column_parity_table[] = { -+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, -+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, -+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, -+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, -+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, -+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, -+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, -+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, -+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, -+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, -+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, -+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, -+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, -+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, -+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, -+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, -+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, -+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, -+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, -+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, -+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, -+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, -+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, -+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, -+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, -+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, -+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, -+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, -+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, -+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, -+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, -+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, -+}; -+ -+ -+/* Calculate the ECC for a 256-byte block of data */ -+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) -+{ -+ unsigned int i; -+ unsigned char col_parity = 0; -+ unsigned char line_parity = 0; -+ unsigned char line_parity_prime = 0; -+ unsigned char t; -+ unsigned char b; -+ -+ for (i = 0; i < 256; i++) { -+ b = column_parity_table[*data++]; -+ col_parity ^= b; -+ -+ if (b & 0x01) { /* odd number of bits in the byte */ -+ line_parity ^= i; -+ line_parity_prime ^= ~i; -+ } -+ } -+ -+ ecc[2] = (~col_parity) | 0x03; -+ -+ t = 0; -+ if (line_parity & 0x80) -+ t |= 0x80; -+ if (line_parity_prime & 0x80) -+ t |= 0x40; -+ if (line_parity & 0x40) -+ t |= 0x20; -+ if (line_parity_prime & 0x40) -+ t |= 0x10; -+ if (line_parity & 0x20) -+ t |= 0x08; -+ if (line_parity_prime & 0x20) -+ t |= 0x04; -+ if (line_parity & 0x10) -+ t |= 0x02; -+ if (line_parity_prime & 0x10) -+ t |= 0x01; -+ ecc[1] = ~t; -+ -+ t = 0; -+ if (line_parity & 0x08) -+ t |= 0x80; -+ if (line_parity_prime & 0x08) -+ t |= 0x40; -+ if (line_parity & 0x04) -+ t |= 0x20; -+ if (line_parity_prime & 0x04) -+ t |= 0x10; -+ if (line_parity & 0x02) -+ t |= 0x08; -+ if (line_parity_prime & 0x02) -+ t |= 0x04; -+ if (line_parity & 0x01) -+ t |= 0x02; -+ if (line_parity_prime & 0x01) -+ t |= 0x01; -+ ecc[0] = ~t; -+ -+} -+ -+/* Correct the ECC on a 256 byte block of data */ -+ -+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, -+ const unsigned char *test_ecc) -+{ -+ unsigned char d0, d1, d2; /* deltas */ -+ -+ d0 = read_ecc[0] ^ test_ecc[0]; -+ d1 = read_ecc[1] ^ test_ecc[1]; -+ d2 = read_ecc[2] ^ test_ecc[2]; -+ -+ if ((d0 | d1 | d2) == 0) -+ return 0; /* no error */ -+ -+ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && -+ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && -+ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { -+ /* Single bit (recoverable) error in data */ -+ -+ unsigned byte; -+ unsigned bit; -+ -+ bit = byte = 0; -+ -+ if (d1 & 0x80) -+ byte |= 0x80; -+ if (d1 & 0x20) -+ byte |= 0x40; -+ if (d1 & 0x08) -+ byte |= 0x20; -+ if (d1 & 0x02) -+ byte |= 0x10; -+ if (d0 & 0x80) -+ byte |= 0x08; -+ if (d0 & 0x20) -+ byte |= 0x04; -+ if (d0 & 0x08) -+ byte |= 0x02; -+ if (d0 & 0x02) -+ byte |= 0x01; -+ -+ if (d2 & 0x80) -+ bit |= 0x04; -+ if (d2 & 0x20) -+ bit |= 0x02; -+ if (d2 & 0x08) -+ bit |= 0x01; -+ -+ data[byte] ^= (1 << bit); -+ -+ return 1; /* Corrected the error */ -+ } -+ -+ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { -+ /* Reccoverable error in ecc */ -+ -+ read_ecc[0] = test_ecc[0]; -+ read_ecc[1] = test_ecc[1]; -+ read_ecc[2] = test_ecc[2]; -+ -+ return 1; /* Corrected the error */ -+ } -+ -+ /* Unrecoverable error */ -+ -+ return -1; -+ -+} -+ -+/* -+ * ECCxxxOther does ECC calcs on arbitrary n bytes of data -+ */ -+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *ecc_other) -+{ -+ unsigned int i; -+ unsigned char col_parity = 0; -+ unsigned line_parity = 0; -+ unsigned line_parity_prime = 0; -+ unsigned char b; -+ -+ for (i = 0; i < n_bytes; i++) { -+ b = column_parity_table[*data++]; -+ col_parity ^= b; -+ -+ if (b & 0x01) { -+ /* odd number of bits in the byte */ -+ line_parity ^= i; -+ line_parity_prime ^= ~i; -+ } -+ -+ } -+ -+ ecc_other->col_parity = (col_parity >> 2) & 0x3f; -+ ecc_other->line_parity = line_parity; -+ ecc_other->line_parity_prime = line_parity_prime; -+} -+ -+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *read_ecc, -+ const struct yaffs_ecc_other *test_ecc) -+{ -+ unsigned char delta_col; /* column parity delta */ -+ unsigned delta_line; /* line parity delta */ -+ unsigned delta_line_prime; /* line parity delta */ -+ unsigned bit; -+ -+ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; -+ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; -+ delta_line_prime = -+ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; -+ -+ if ((delta_col | delta_line | delta_line_prime) == 0) -+ return 0; /* no error */ -+ -+ if (delta_line == ~delta_line_prime && -+ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { -+ /* Single bit (recoverable) error in data */ -+ -+ bit = 0; -+ -+ if (delta_col & 0x20) -+ bit |= 0x04; -+ if (delta_col & 0x08) -+ bit |= 0x02; -+ if (delta_col & 0x02) -+ bit |= 0x01; -+ -+ if (delta_line >= n_bytes) -+ return -1; -+ -+ data[delta_line] ^= (1 << bit); -+ -+ return 1; /* corrected */ -+ } -+ -+ if ((hweight32(delta_line) + -+ hweight32(delta_line_prime) + -+ hweight8(delta_col)) == 1) { -+ /* Reccoverable error in ecc */ -+ -+ *read_ecc = *test_ecc; -+ return 1; /* corrected */ -+ } -+ -+ /* Unrecoverable error */ -+ -+ return -1; -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_ecc.h linux-3.18.14/fs/yaffs2/yaffs_ecc.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_ecc.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,44 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* -+ * This code implements the ECC algorithm used in SmartMedia. -+ * -+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. -+ * The two unused bit are set to 1. -+ * The ECC can correct single bit errors in a 256-byte page of data. -+ * Thus, two such ECC blocks are used on a 512-byte NAND page. -+ * -+ */ -+ -+#ifndef __YAFFS_ECC_H__ -+#define __YAFFS_ECC_H__ -+ -+struct yaffs_ecc_other { -+ unsigned char col_parity; -+ unsigned line_parity; -+ unsigned line_parity_prime; -+}; -+ -+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); -+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, -+ const unsigned char *test_ecc); -+ -+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *ecc); -+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *read_ecc, -+ const struct yaffs_ecc_other *test_ecc); -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.18.14/fs/yaffs2/yaffs_getblockinfo.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_getblockinfo.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,35 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_GETBLOCKINFO_H__ -+#define __YAFFS_GETBLOCKINFO_H__ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+ -+/* Function to manipulate block info */ -+static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev -+ *dev, int blk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs: get_block_info block %d is not valid", -+ blk); -+ BUG(); -+ } -+ return &dev->block_info[blk - dev->internal_start_block]; -+} -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_guts.c linux-3.18.14/fs/yaffs2/yaffs_guts.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_guts.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,5140 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yportenv.h" -+#include "yaffs_trace.h" -+ -+#include "yaffs_guts.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_tagscompat.h" -+#include "yaffs_tagsmarshall.h" -+#include "yaffs_nand.h" -+#include "yaffs_yaffs1.h" -+#include "yaffs_yaffs2.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_verify.h" -+#include "yaffs_nand.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_nameval.h" -+#include "yaffs_allocator.h" -+#include "yaffs_attribs.h" -+#include "yaffs_summary.h" -+ -+/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ -+#define YAFFS_GC_GOOD_ENOUGH 2 -+#define YAFFS_GC_PASSIVE_THRESHOLD 4 -+ -+#include "yaffs_ecc.h" -+ -+/* Forward declarations */ -+ -+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, -+ const u8 *buffer, int n_bytes, int use_reserve); -+ -+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, -+ int buffer_size); -+ -+/* Function to calculate chunk and offset */ -+ -+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, -+ int *chunk_out, u32 *offset_out) -+{ -+ int chunk; -+ u32 offset; -+ -+ chunk = (u32) (addr >> dev->chunk_shift); -+ -+ if (dev->chunk_div == 1) { -+ /* easy power of 2 case */ -+ offset = (u32) (addr & dev->chunk_mask); -+ } else { -+ /* Non power-of-2 case */ -+ -+ loff_t chunk_base; -+ -+ chunk /= dev->chunk_div; -+ -+ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; -+ offset = (u32) (addr - chunk_base); -+ } -+ -+ *chunk_out = chunk; -+ *offset_out = offset; -+} -+ -+/* Function to return the number of shifts for a power of 2 greater than or -+ * equal to the given number -+ * Note we don't try to cater for all possible numbers and this does not have to -+ * be hellishly efficient. -+ */ -+ -+static inline u32 calc_shifts_ceiling(u32 x) -+{ -+ int extra_bits; -+ int shifts; -+ -+ shifts = extra_bits = 0; -+ -+ while (x > 1) { -+ if (x & 1) -+ extra_bits++; -+ x >>= 1; -+ shifts++; -+ } -+ -+ if (extra_bits) -+ shifts++; -+ -+ return shifts; -+} -+ -+/* Function to return the number of shifts to get a 1 in bit 0 -+ */ -+ -+static inline u32 calc_shifts(u32 x) -+{ -+ u32 shifts; -+ -+ shifts = 0; -+ -+ if (!x) -+ return 0; -+ -+ while (!(x & 1)) { -+ x >>= 1; -+ shifts++; -+ } -+ -+ return shifts; -+} -+ -+/* -+ * Temporary buffer manipulations. -+ */ -+ -+static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) -+{ -+ int i; -+ u8 *buf = (u8 *) 1; -+ -+ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); -+ -+ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { -+ dev->temp_buffer[i].in_use = 0; -+ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ dev->temp_buffer[i].buffer = buf; -+ } -+ -+ return buf ? YAFFS_OK : YAFFS_FAIL; -+} -+ -+u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) -+{ -+ int i; -+ -+ dev->temp_in_use++; -+ if (dev->temp_in_use > dev->max_temp) -+ dev->max_temp = dev->temp_in_use; -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ if (dev->temp_buffer[i].in_use == 0) { -+ dev->temp_buffer[i].in_use = 1; -+ return dev->temp_buffer[i].buffer; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); -+ /* -+ * If we got here then we have to allocate an unmanaged one -+ * This is not good. -+ */ -+ -+ dev->unmanaged_buffer_allocs++; -+ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); -+ -+} -+ -+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) -+{ -+ int i; -+ -+ dev->temp_in_use--; -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ if (dev->temp_buffer[i].buffer == buffer) { -+ dev->temp_buffer[i].in_use = 0; -+ return; -+ } -+ } -+ -+ if (buffer) { -+ /* assume it is an unmanaged one. */ -+ yaffs_trace(YAFFS_TRACE_BUFFERS, -+ "Releasing unmanaged temp buffer"); -+ kfree(buffer); -+ dev->unmanaged_buffer_deallocs++; -+ } -+ -+} -+ -+/* -+ * Functions for robustisizing TODO -+ * -+ */ -+ -+static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, -+ const struct yaffs_ext_tags *tags) -+{ -+ (void) dev; -+ (void) nand_chunk; -+ (void) data; -+ (void) tags; -+} -+ -+static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, -+ const struct yaffs_ext_tags *tags) -+{ -+ (void) dev; -+ (void) nand_chunk; -+ (void) tags; -+} -+ -+void yaffs_handle_chunk_error(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi) -+{ -+ if (!bi->gc_prioritise) { -+ bi->gc_prioritise = 1; -+ dev->has_pending_prioritised_gc = 1; -+ bi->chunk_error_strikes++; -+ -+ if (bi->chunk_error_strikes > 3) { -+ bi->needs_retiring = 1; /* Too many stikes, so retire */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Block struck out"); -+ -+ } -+ } -+} -+ -+static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, -+ int erased_ok) -+{ -+ int flash_block = nand_chunk / dev->param.chunks_per_block; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -+ -+ yaffs_handle_chunk_error(dev, bi); -+ -+ if (erased_ok) { -+ /* Was an actual write failure, -+ * so mark the block for retirement.*/ -+ bi->needs_retiring = 1; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Block %d needs retiring", flash_block); -+ } -+ -+ /* Delete the chunk */ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ yaffs_skip_rest_of_block(dev); -+} -+ -+/* -+ * Verification code -+ */ -+ -+/* -+ * Simple hash function. Needs to have a reasonable spread -+ */ -+ -+static inline int yaffs_hash_fn(int n) -+{ -+ if (n < 0) -+ n = -n; -+ return n % YAFFS_NOBJECT_BUCKETS; -+} -+ -+/* -+ * Access functions to useful fake objects. -+ * Note that root might have a presence in NAND if permissions are set. -+ */ -+ -+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) -+{ -+ return dev->root_dir; -+} -+ -+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) -+{ -+ return dev->lost_n_found; -+} -+ -+/* -+ * Erased NAND checking functions -+ */ -+ -+int yaffs_check_ff(u8 *buffer, int n_bytes) -+{ -+ /* Horrible, slow implementation */ -+ while (n_bytes--) { -+ if (*buffer != 0xff) -+ return 0; -+ buffer++; -+ } -+ return 1; -+} -+ -+static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) -+{ -+ int retval = YAFFS_OK; -+ u8 *data = yaffs_get_temp_buffer(dev); -+ struct yaffs_ext_tags tags; -+ int result; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); -+ -+ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) -+ retval = YAFFS_FAIL; -+ -+ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || -+ tags.chunk_used) { -+ yaffs_trace(YAFFS_TRACE_NANDACCESS, -+ "Chunk %d not erased", nand_chunk); -+ retval = YAFFS_FAIL; -+ } -+ -+ yaffs_release_temp_buffer(dev, data); -+ -+ return retval; -+ -+} -+ -+static int yaffs_verify_chunk_written(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, -+ struct yaffs_ext_tags *tags) -+{ -+ int retval = YAFFS_OK; -+ struct yaffs_ext_tags temp_tags; -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ int result; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); -+ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || -+ temp_tags.obj_id != tags->obj_id || -+ temp_tags.chunk_id != tags->chunk_id || -+ temp_tags.n_bytes != tags->n_bytes) -+ retval = YAFFS_FAIL; -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ return retval; -+} -+ -+ -+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) -+{ -+ int reserved_chunks; -+ int reserved_blocks = dev->param.n_reserved_blocks; -+ int checkpt_blocks; -+ -+ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); -+ -+ reserved_chunks = -+ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; -+ -+ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); -+} -+ -+static int yaffs_find_alloc_block(struct yaffs_dev *dev) -+{ -+ int i; -+ struct yaffs_block_info *bi; -+ -+ if (dev->n_erased_blocks < 1) { -+ /* Hoosterman we've got a problem. -+ * Can't get space to gc -+ */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: no more erased blocks"); -+ -+ return -1; -+ } -+ -+ /* Find an empty block. */ -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ dev->alloc_block_finder++; -+ if (dev->alloc_block_finder < dev->internal_start_block -+ || dev->alloc_block_finder > dev->internal_end_block) { -+ dev->alloc_block_finder = dev->internal_start_block; -+ } -+ -+ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->seq_number++; -+ bi->seq_number = dev->seq_number; -+ dev->n_erased_blocks--; -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, -+ "Allocated block %d, seq %d, %d left" , -+ dev->alloc_block_finder, dev->seq_number, -+ dev->n_erased_blocks); -+ return dev->alloc_block_finder; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs tragedy: no more erased blocks, but there should have been %d", -+ dev->n_erased_blocks); -+ -+ return -1; -+} -+ -+static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, -+ struct yaffs_block_info **block_ptr) -+{ -+ int ret_val; -+ struct yaffs_block_info *bi; -+ -+ if (dev->alloc_block < 0) { -+ /* Get next block to allocate off */ -+ dev->alloc_block = yaffs_find_alloc_block(dev); -+ dev->alloc_page = 0; -+ } -+ -+ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { -+ /* No space unless we're allowed to use the reserve. */ -+ return -1; -+ } -+ -+ if (dev->n_erased_blocks < dev->param.n_reserved_blocks -+ && dev->alloc_page == 0) -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); -+ -+ /* Next page please.... */ -+ if (dev->alloc_block >= 0) { -+ bi = yaffs_get_block_info(dev, dev->alloc_block); -+ -+ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + -+ dev->alloc_page; -+ bi->pages_in_use++; -+ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); -+ -+ dev->alloc_page++; -+ -+ dev->n_free_chunks--; -+ -+ /* If the block is full set the state to full */ -+ if (dev->alloc_page >= dev->param.chunks_per_block) { -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ -+ if (block_ptr) -+ *block_ptr = bi; -+ -+ return ret_val; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); -+ -+ return -1; -+} -+ -+static int yaffs_get_erased_chunks(struct yaffs_dev *dev) -+{ -+ int n; -+ -+ n = dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ if (dev->alloc_block > 0) -+ n += (dev->param.chunks_per_block - dev->alloc_page); -+ -+ return n; -+ -+} -+ -+/* -+ * yaffs_skip_rest_of_block() skips over the rest of the allocation block -+ * if we don't want to write to it. -+ */ -+void yaffs_skip_rest_of_block(struct yaffs_dev *dev) -+{ -+ struct yaffs_block_info *bi; -+ -+ if (dev->alloc_block > 0) { -+ bi = yaffs_get_block_info(dev, dev->alloc_block); -+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ } -+} -+ -+static int yaffs_write_new_chunk(struct yaffs_dev *dev, -+ const u8 *data, -+ struct yaffs_ext_tags *tags, int use_reserver) -+{ -+ int attempts = 0; -+ int write_ok = 0; -+ int chunk; -+ -+ yaffs2_checkpt_invalidate(dev); -+ -+ do { -+ struct yaffs_block_info *bi = 0; -+ int erased_ok = 0; -+ -+ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); -+ if (chunk < 0) { -+ /* no space */ -+ break; -+ } -+ -+ /* First check this chunk is erased, if it needs -+ * checking. The checking policy (unless forced -+ * always on) is as follows: -+ * -+ * Check the first page we try to write in a block. -+ * If the check passes then we don't need to check any -+ * more. If the check fails, we check again... -+ * If the block has been erased, we don't need to check. -+ * -+ * However, if the block has been prioritised for gc, -+ * then we think there might be something odd about -+ * this block and stop using it. -+ * -+ * Rationale: We should only ever see chunks that have -+ * not been erased if there was a partially written -+ * chunk due to power loss. This checking policy should -+ * catch that case with very few checks and thus save a -+ * lot of checks that are most likely not needed. -+ * -+ * Mods to the above -+ * If an erase check fails or the write fails we skip the -+ * rest of the block. -+ */ -+ -+ /* let's give it a try */ -+ attempts++; -+ -+ if (dev->param.always_check_erased) -+ bi->skip_erased_check = 0; -+ -+ if (!bi->skip_erased_check) { -+ erased_ok = yaffs_check_chunk_erased(dev, chunk); -+ if (erased_ok != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs chunk %d was not erased", -+ chunk); -+ -+ /* If not erased, delete this one, -+ * skip rest of block and -+ * try another chunk */ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ yaffs_skip_rest_of_block(dev); -+ continue; -+ } -+ } -+ -+ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); -+ -+ if (!bi->skip_erased_check) -+ write_ok = -+ yaffs_verify_chunk_written(dev, chunk, data, tags); -+ -+ if (write_ok != YAFFS_OK) { -+ /* Clean up aborted write, skip to next block and -+ * try another chunk */ -+ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); -+ continue; -+ } -+ -+ bi->skip_erased_check = 1; -+ -+ /* Copy the data into the robustification buffer */ -+ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); -+ -+ } while (write_ok != YAFFS_OK && -+ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); -+ -+ if (!write_ok) -+ chunk = -1; -+ -+ if (attempts > 1) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs write required %d attempts", -+ attempts); -+ dev->n_retried_writes += (attempts - 1); -+ } -+ -+ return chunk; -+} -+ -+/* -+ * Block retiring for handling a broken block. -+ */ -+ -+static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -+ -+ yaffs2_checkpt_invalidate(dev); -+ -+ yaffs2_clear_oldest_dirty_seq(dev, bi); -+ -+ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { -+ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Failed to mark bad and erase block %d", -+ flash_block); -+ } else { -+ struct yaffs_ext_tags tags; -+ int chunk_id = -+ flash_block * dev->param.chunks_per_block; -+ -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ memset(buffer, 0xff, dev->data_bytes_per_chunk); -+ memset(&tags, 0, sizeof(tags)); -+ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; -+ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - -+ dev->chunk_offset, -+ buffer, -+ &tags) != YAFFS_OK) -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Failed to write bad block marker to block %d", -+ flash_block); -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ } -+ -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ bi->gc_prioritise = 0; -+ bi->needs_retiring = 0; -+ -+ dev->n_retired_blocks++; -+} -+ -+/*---------------- Name handling functions ------------*/ -+ -+static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, -+ const YCHAR *oh_name, int buff_size) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ if (dev->param.auto_unicode) { -+ if (*oh_name) { -+ /* It is an ASCII name, do an ASCII to -+ * unicode conversion */ -+ const char *ascii_oh_name = (const char *)oh_name; -+ int n = buff_size - 1; -+ while (n > 0 && *ascii_oh_name) { -+ *name = *ascii_oh_name; -+ name++; -+ ascii_oh_name++; -+ n--; -+ } -+ } else { -+ strncpy(name, oh_name + 1, buff_size - 1); -+ } -+ } else { -+#else -+ (void) dev; -+ { -+#endif -+ strncpy(name, oh_name, buff_size - 1); -+ } -+} -+ -+static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, -+ const YCHAR *name) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ -+ int is_ascii; -+ const YCHAR *w; -+ -+ if (dev->param.auto_unicode) { -+ -+ is_ascii = 1; -+ w = name; -+ -+ /* Figure out if the name will fit in ascii character set */ -+ while (is_ascii && *w) { -+ if ((*w) & 0xff00) -+ is_ascii = 0; -+ w++; -+ } -+ -+ if (is_ascii) { -+ /* It is an ASCII name, so convert unicode to ascii */ -+ char *ascii_oh_name = (char *)oh_name; -+ int n = YAFFS_MAX_NAME_LENGTH - 1; -+ while (n > 0 && *name) { -+ *ascii_oh_name = *name; -+ name++; -+ ascii_oh_name++; -+ n--; -+ } -+ } else { -+ /* Unicode name, so save starting at the second YCHAR */ -+ *oh_name = 0; -+ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); -+ } -+ } else { -+#else -+ dev = dev; -+ { -+#endif -+ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); -+ } -+} -+ -+static u16 yaffs_calc_name_sum(const YCHAR *name) -+{ -+ u16 sum = 0; -+ u16 i = 1; -+ -+ if (!name) -+ return 0; -+ -+ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { -+ -+ /* 0x1f mask is case insensitive */ -+ sum += ((*name) & 0x1f) * i; -+ i++; -+ name++; -+ } -+ return sum; -+} -+ -+ -+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) -+{ -+ memset(obj->short_name, 0, sizeof(obj->short_name)); -+ -+ if (name && !name[0]) { -+ yaffs_fix_null_name(obj, obj->short_name, -+ YAFFS_SHORT_NAME_LENGTH); -+ name = obj->short_name; -+ } else if (name && -+ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= -+ YAFFS_SHORT_NAME_LENGTH) { -+ strcpy(obj->short_name, name); -+ } -+ -+ obj->sum = yaffs_calc_name_sum(name); -+} -+ -+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, -+ const struct yaffs_obj_hdr *oh) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; -+ memset(tmp_name, 0, sizeof(tmp_name)); -+ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, -+ YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_set_obj_name(obj, tmp_name); -+#else -+ yaffs_set_obj_name(obj, oh->name); -+#endif -+} -+ -+loff_t yaffs_max_file_size(struct yaffs_dev *dev) -+{ -+ if(sizeof(loff_t) < 8) -+ return YAFFS_MAX_FILE_SIZE_32; -+ else -+ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; -+} -+ -+/*-------------------- TNODES ------------------- -+ -+ * List of spare tnodes -+ * The list is hooked together using the first pointer -+ * in the tnode. -+ */ -+ -+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) -+{ -+ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); -+ -+ if (tn) { -+ memset(tn, 0, dev->tnode_size); -+ dev->n_tnodes++; -+ } -+ -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+ -+ return tn; -+} -+ -+/* FreeTnode frees up a tnode and puts it back on the free list */ -+static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) -+{ -+ yaffs_free_raw_tnode(dev, tn); -+ dev->n_tnodes--; -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ yaffs_deinit_raw_tnodes_and_objs(dev); -+ dev->n_obj = 0; -+ dev->n_tnodes = 0; -+} -+ -+static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos, unsigned val) -+{ -+ u32 *map = (u32 *) tn; -+ u32 bit_in_map; -+ u32 bit_in_word; -+ u32 word_in_map; -+ u32 mask; -+ -+ pos &= YAFFS_TNODES_LEVEL0_MASK; -+ val >>= dev->chunk_grp_bits; -+ -+ bit_in_map = pos * dev->tnode_width; -+ word_in_map = bit_in_map / 32; -+ bit_in_word = bit_in_map & (32 - 1); -+ -+ mask = dev->tnode_mask << bit_in_word; -+ -+ map[word_in_map] &= ~mask; -+ map[word_in_map] |= (mask & (val << bit_in_word)); -+ -+ if (dev->tnode_width > (32 - bit_in_word)) { -+ bit_in_word = (32 - bit_in_word); -+ word_in_map++; -+ mask = -+ dev->tnode_mask >> bit_in_word; -+ map[word_in_map] &= ~mask; -+ map[word_in_map] |= (mask & (val >> bit_in_word)); -+ } -+} -+ -+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos) -+{ -+ u32 *map = (u32 *) tn; -+ u32 bit_in_map; -+ u32 bit_in_word; -+ u32 word_in_map; -+ u32 val; -+ -+ pos &= YAFFS_TNODES_LEVEL0_MASK; -+ -+ bit_in_map = pos * dev->tnode_width; -+ word_in_map = bit_in_map / 32; -+ bit_in_word = bit_in_map & (32 - 1); -+ -+ val = map[word_in_map] >> bit_in_word; -+ -+ if (dev->tnode_width > (32 - bit_in_word)) { -+ bit_in_word = (32 - bit_in_word); -+ word_in_map++; -+ val |= (map[word_in_map] << bit_in_word); -+ } -+ -+ val &= dev->tnode_mask; -+ val <<= dev->chunk_grp_bits; -+ -+ return val; -+} -+ -+/* ------------------- End of individual tnode manipulation -----------------*/ -+ -+/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ -+ * The look up tree is represented by the top tnode and the number of top_level -+ * in the tree. 0 means only the level 0 tnode is in the tree. -+ */ -+ -+/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ -+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id) -+{ -+ struct yaffs_tnode *tn = file_struct->top; -+ u32 i; -+ int required_depth; -+ int level = file_struct->top_level; -+ -+ (void) dev; -+ -+ /* Check sane level and chunk Id */ -+ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) -+ return NULL; -+ -+ if (chunk_id > YAFFS_MAX_CHUNK_ID) -+ return NULL; -+ -+ /* First check we're tall enough (ie enough top_level) */ -+ -+ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (i) { -+ i >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ if (required_depth > file_struct->top_level) -+ return NULL; /* Not tall enough, so we can't find it */ -+ -+ /* Traverse down to level 0 */ -+ while (level > 0 && tn) { -+ tn = tn->internal[(chunk_id >> -+ (YAFFS_TNODES_LEVEL0_BITS + -+ (level - 1) * -+ YAFFS_TNODES_INTERNAL_BITS)) & -+ YAFFS_TNODES_INTERNAL_MASK]; -+ level--; -+ } -+ -+ return tn; -+} -+ -+/* add_find_tnode_0 finds the level 0 tnode if it exists, -+ * otherwise first expands the tree. -+ * This happens in two steps: -+ * 1. If the tree isn't tall enough, then make it taller. -+ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. -+ * -+ * Used when modifying the tree. -+ * -+ * If the tn argument is NULL, then a fresh tnode will be added otherwise the -+ * specified tn will be plugged into the ttree. -+ */ -+ -+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id, -+ struct yaffs_tnode *passed_tn) -+{ -+ int required_depth; -+ int i; -+ int l; -+ struct yaffs_tnode *tn; -+ u32 x; -+ -+ /* Check sane level and page Id */ -+ if (file_struct->top_level < 0 || -+ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) -+ return NULL; -+ -+ if (chunk_id > YAFFS_MAX_CHUNK_ID) -+ return NULL; -+ -+ /* First check we're tall enough (ie enough top_level) */ -+ -+ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (x) { -+ x >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ if (required_depth > file_struct->top_level) { -+ /* Not tall enough, gotta make the tree taller */ -+ for (i = file_struct->top_level; i < required_depth; i++) { -+ -+ tn = yaffs_get_tnode(dev); -+ -+ if (tn) { -+ tn->internal[0] = file_struct->top; -+ file_struct->top = tn; -+ file_struct->top_level++; -+ } else { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs: no more tnodes"); -+ return NULL; -+ } -+ } -+ } -+ -+ /* Traverse down to level 0, adding anything we need */ -+ -+ l = file_struct->top_level; -+ tn = file_struct->top; -+ -+ if (l > 0) { -+ while (l > 0 && tn) { -+ x = (chunk_id >> -+ (YAFFS_TNODES_LEVEL0_BITS + -+ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & -+ YAFFS_TNODES_INTERNAL_MASK; -+ -+ if ((l > 1) && !tn->internal[x]) { -+ /* Add missing non-level-zero tnode */ -+ tn->internal[x] = yaffs_get_tnode(dev); -+ if (!tn->internal[x]) -+ return NULL; -+ } else if (l == 1) { -+ /* Looking from level 1 at level 0 */ -+ if (passed_tn) { -+ /* If we already have one, release it */ -+ if (tn->internal[x]) -+ yaffs_free_tnode(dev, -+ tn->internal[x]); -+ tn->internal[x] = passed_tn; -+ -+ } else if (!tn->internal[x]) { -+ /* Don't have one, none passed in */ -+ tn->internal[x] = yaffs_get_tnode(dev); -+ if (!tn->internal[x]) -+ return NULL; -+ } -+ } -+ -+ tn = tn->internal[x]; -+ l--; -+ } -+ } else { -+ /* top is level 0 */ -+ if (passed_tn) { -+ memcpy(tn, passed_tn, -+ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); -+ yaffs_free_tnode(dev, passed_tn); -+ } -+ } -+ -+ return tn; -+} -+ -+static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, -+ int chunk_obj) -+{ -+ return (tags->chunk_id == chunk_obj && -+ tags->obj_id == obj_id && -+ !tags->is_deleted) ? 1 : 0; -+ -+} -+ -+static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, -+ struct yaffs_ext_tags *tags, int obj_id, -+ int inode_chunk) -+{ -+ int j; -+ -+ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { -+ if (yaffs_check_chunk_bit -+ (dev, the_chunk / dev->param.chunks_per_block, -+ the_chunk % dev->param.chunks_per_block)) { -+ -+ if (dev->chunk_grp_size == 1) -+ return the_chunk; -+ else { -+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, -+ tags); -+ if (yaffs_tags_match(tags, -+ obj_id, inode_chunk)) { -+ /* found it; */ -+ return the_chunk; -+ } -+ } -+ } -+ the_chunk++; -+ } -+ return -1; -+} -+ -+int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags) -+{ -+ /*Get the Tnode, then get the level 0 offset chunk offset */ -+ struct yaffs_tnode *tn; -+ int the_chunk = -1; -+ struct yaffs_ext_tags local_tags; -+ int ret_val = -1; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!tags) { -+ /* Passed a NULL, so use our own tags space */ -+ tags = &local_tags; -+ } -+ -+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -+ -+ if (!tn) -+ return ret_val; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, -+ inode_chunk); -+ return ret_val; -+} -+ -+static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags) -+{ -+ /* Get the Tnode, then get the level 0 offset chunk offset */ -+ struct yaffs_tnode *tn; -+ int the_chunk = -1; -+ struct yaffs_ext_tags local_tags; -+ struct yaffs_dev *dev = in->my_dev; -+ int ret_val = -1; -+ -+ if (!tags) { -+ /* Passed a NULL, so use our own tags space */ -+ tags = &local_tags; -+ } -+ -+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -+ -+ if (!tn) -+ return ret_val; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, -+ inode_chunk); -+ -+ /* Delete the entry in the filestructure (if found) */ -+ if (ret_val != -1) -+ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); -+ -+ return ret_val; -+} -+ -+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ int nand_chunk, int in_scan) -+{ -+ /* NB in_scan is zero unless scanning. -+ * For forward scanning, in_scan is > 0; -+ * for backward scanning in_scan is < 0 -+ * -+ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. -+ */ -+ -+ struct yaffs_tnode *tn; -+ struct yaffs_dev *dev = in->my_dev; -+ int existing_cunk; -+ struct yaffs_ext_tags existing_tags; -+ struct yaffs_ext_tags new_tags; -+ unsigned existing_serial, new_serial; -+ -+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { -+ /* Just ignore an attempt at putting a chunk into a non-file -+ * during scanning. -+ * If it is not during Scanning then something went wrong! -+ */ -+ if (!in_scan) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy:attempt to put data chunk into a non-file" -+ ); -+ BUG(); -+ } -+ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ return YAFFS_OK; -+ } -+ -+ tn = yaffs_add_find_tnode_0(dev, -+ &in->variant.file_variant, -+ inode_chunk, NULL); -+ if (!tn) -+ return YAFFS_FAIL; -+ -+ if (!nand_chunk) -+ /* Dummy insert, bail now */ -+ return YAFFS_OK; -+ -+ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ if (in_scan != 0) { -+ /* If we're scanning then we need to test for duplicates -+ * NB This does not need to be efficient since it should only -+ * happen when the power fails during a write, then only one -+ * chunk should ever be affected. -+ * -+ * Correction for YAFFS2: This could happen quite a lot and we -+ * need to think about efficiency! TODO -+ * Update: For backward scanning we don't need to re-read tags -+ * so this is quite cheap. -+ */ -+ -+ if (existing_cunk > 0) { -+ /* NB Right now existing chunk will not be real -+ * chunk_id if the chunk group size > 1 -+ * thus we have to do a FindChunkInFile to get the -+ * real chunk id. -+ * -+ * We have a duplicate now we need to decide which -+ * one to use: -+ * -+ * Backwards scanning YAFFS2: The old one is what -+ * we use, dump the new one. -+ * YAFFS1: Get both sets of tags and compare serial -+ * numbers. -+ */ -+ -+ if (in_scan > 0) { -+ /* Only do this for forward scanning */ -+ yaffs_rd_chunk_tags_nand(dev, -+ nand_chunk, -+ NULL, &new_tags); -+ -+ /* Do a proper find */ -+ existing_cunk = -+ yaffs_find_chunk_in_file(in, inode_chunk, -+ &existing_tags); -+ } -+ -+ if (existing_cunk <= 0) { -+ /*Hoosterman - how did this happen? */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: existing chunk < 0 in scan" -+ ); -+ -+ } -+ -+ /* NB The deleted flags should be false, otherwise -+ * the chunks will not be loaded during a scan -+ */ -+ -+ if (in_scan > 0) { -+ new_serial = new_tags.serial_number; -+ existing_serial = existing_tags.serial_number; -+ } -+ -+ if ((in_scan > 0) && -+ (existing_cunk <= 0 || -+ ((existing_serial + 1) & 3) == new_serial)) { -+ /* Forward scanning. -+ * Use new -+ * Delete the old one and drop through to -+ * update the tnode -+ */ -+ yaffs_chunk_del(dev, existing_cunk, 1, -+ __LINE__); -+ } else { -+ /* Backward scanning or we want to use the -+ * existing one -+ * Delete the new one and return early so that -+ * the tnode isn't changed -+ */ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ return YAFFS_OK; -+ } -+ } -+ -+ } -+ -+ if (existing_cunk == 0) -+ in->n_data_chunks++; -+ -+ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); -+ -+ return YAFFS_OK; -+} -+ -+static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) -+{ -+ struct yaffs_block_info *the_block; -+ unsigned block_no; -+ -+ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); -+ -+ block_no = chunk / dev->param.chunks_per_block; -+ the_block = yaffs_get_block_info(dev, block_no); -+ if (the_block) { -+ the_block->soft_del_pages++; -+ dev->n_free_chunks++; -+ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); -+ } -+} -+ -+/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all -+ * the chunks in the file. -+ * All soft deleting does is increment the block's softdelete count and pulls -+ * the chunk out of the tnode. -+ * Thus, essentially this is the same as DeleteWorker except that the chunks -+ * are soft deleted. -+ */ -+ -+static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, -+ u32 level, int chunk_offset) -+{ -+ int i; -+ int the_chunk; -+ int all_done = 1; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!tn) -+ return 1; -+ -+ if (level > 0) { -+ for (i = YAFFS_NTNODES_INTERNAL - 1; -+ all_done && i >= 0; -+ i--) { -+ if (tn->internal[i]) { -+ all_done = -+ yaffs_soft_del_worker(in, -+ tn->internal[i], -+ level - 1, -+ (chunk_offset << -+ YAFFS_TNODES_INTERNAL_BITS) -+ + i); -+ if (all_done) { -+ yaffs_free_tnode(dev, -+ tn->internal[i]); -+ tn->internal[i] = NULL; -+ } else { -+ /* Can this happen? */ -+ } -+ } -+ } -+ return (all_done) ? 1 : 0; -+ } -+ -+ /* level 0 */ -+ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { -+ the_chunk = yaffs_get_group_base(dev, tn, i); -+ if (the_chunk) { -+ yaffs_soft_del_chunk(dev, the_chunk); -+ yaffs_load_tnode_0(dev, tn, i, 0); -+ } -+ } -+ return 1; -+} -+ -+static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ struct yaffs_obj *parent; -+ -+ yaffs_verify_obj_in_dir(obj); -+ parent = obj->parent; -+ -+ yaffs_verify_dir(parent); -+ -+ if (dev && dev->param.remove_obj_fn) -+ dev->param.remove_obj_fn(obj); -+ -+ list_del_init(&obj->siblings); -+ obj->parent = NULL; -+ -+ yaffs_verify_dir(parent); -+} -+ -+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) -+{ -+ if (!directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: Trying to add an object to a null pointer directory" -+ ); -+ BUG(); -+ return; -+ } -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: Trying to add an object to a non-directory" -+ ); -+ BUG(); -+ } -+ -+ if (obj->siblings.prev == NULL) { -+ /* Not initialised */ -+ BUG(); -+ } -+ -+ yaffs_verify_dir(directory); -+ -+ yaffs_remove_obj_from_dir(obj); -+ -+ /* Now add it */ -+ list_add(&obj->siblings, &directory->variant.dir_variant.children); -+ obj->parent = directory; -+ -+ if (directory == obj->my_dev->unlinked_dir -+ || directory == obj->my_dev->del_dir) { -+ obj->unlinked = 1; -+ obj->my_dev->n_unlinked_files++; -+ obj->rename_allowed = 0; -+ } -+ -+ yaffs_verify_dir(directory); -+ yaffs_verify_obj_in_dir(obj); -+} -+ -+static int yaffs_change_obj_name(struct yaffs_obj *obj, -+ struct yaffs_obj *new_dir, -+ const YCHAR *new_name, int force, int shadows) -+{ -+ int unlink_op; -+ int del_op; -+ struct yaffs_obj *existing_target; -+ -+ if (new_dir == NULL) -+ new_dir = obj->parent; /* use the old directory */ -+ -+ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_change_obj_name: new_dir is not a directory" -+ ); -+ BUG(); -+ } -+ -+ unlink_op = (new_dir == obj->my_dev->unlinked_dir); -+ del_op = (new_dir == obj->my_dev->del_dir); -+ -+ existing_target = yaffs_find_by_name(new_dir, new_name); -+ -+ /* If the object is a file going into the unlinked directory, -+ * then it is OK to just stuff it in since duplicate names are OK. -+ * else only proceed if the new name does not exist and we're putting -+ * it into a directory. -+ */ -+ if (!(unlink_op || del_op || force || -+ shadows > 0 || !existing_target) || -+ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) -+ return YAFFS_FAIL; -+ -+ yaffs_set_obj_name(obj, new_name); -+ obj->dirty = 1; -+ yaffs_add_obj_to_dir(new_dir, obj); -+ -+ if (unlink_op) -+ obj->unlinked = 1; -+ -+ /* If it is a deletion then we mark it as a shrink for gc */ -+ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+/*------------------------ Short Operations Cache ------------------------------ -+ * In many situations where there is no high level buffering a lot of -+ * reads might be short sequential reads, and a lot of writes may be short -+ * sequential writes. eg. scanning/writing a jpeg file. -+ * In these cases, a short read/write cache can provide a huge perfomance -+ * benefit with dumb-as-a-rock code. -+ * In Linux, the page cache provides read buffering and the short op cache -+ * provides write buffering. -+ * -+ * There are a small number (~10) of cache chunks per device so that we don't -+ * need a very intelligent search. -+ */ -+ -+static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ struct yaffs_cache *cache; -+ int n_caches = obj->my_dev->param.n_caches; -+ -+ for (i = 0; i < n_caches; i++) { -+ cache = &dev->cache[i]; -+ if (cache->object == obj && cache->dirty) -+ return 1; -+ } -+ -+ return 0; -+} -+ -+static void yaffs_flush_single_cache(struct yaffs_cache *cache, int discard) -+{ -+ -+ if (!cache || cache->locked) -+ return; -+ -+ /* Write it out and free it up if need be.*/ -+ if (cache->dirty) { -+ yaffs_wr_data_obj(cache->object, -+ cache->chunk_id, -+ cache->data, -+ cache->n_bytes, -+ 1); -+ -+ cache->dirty = 0; -+ } -+ -+ if (discard) -+ cache->object = NULL; -+} -+ -+static void yaffs_flush_file_cache(struct yaffs_obj *obj, int discard) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ struct yaffs_cache *cache; -+ int n_caches = obj->my_dev->param.n_caches; -+ -+ if (n_caches < 1) -+ return; -+ -+ -+ /* Find the chunks for this object and flush them. */ -+ for (i = 0; i < n_caches; i++) { -+ cache = &dev->cache[i]; -+ if (cache->object == obj) -+ yaffs_flush_single_cache(cache, discard); -+ } -+ -+} -+ -+ -+void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard) -+{ -+ struct yaffs_obj *obj; -+ int n_caches = dev->param.n_caches; -+ int i; -+ -+ /* Find a dirty object in the cache and flush it... -+ * until there are no further dirty objects. -+ */ -+ do { -+ obj = NULL; -+ for (i = 0; i < n_caches && !obj; i++) { -+ if (dev->cache[i].object && dev->cache[i].dirty) -+ obj = dev->cache[i].object; -+ } -+ if (obj) -+ yaffs_flush_file_cache(obj, discard); -+ } while (obj); -+ -+} -+ -+/* Grab us an unused cache chunk for use. -+ * First look for an empty one. -+ * Then look for the least recently used non-dirty one. -+ * Then look for the least recently used dirty one...., flush and look again. -+ */ -+static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (dev->param.n_caches > 0) { -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (!dev->cache[i].object) -+ return &dev->cache[i]; -+ } -+ } -+ -+ return NULL; -+} -+ -+static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) -+{ -+ struct yaffs_cache *cache; -+ int usage; -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return NULL; -+ -+ /* First look for an unused cache */ -+ -+ cache = yaffs_grab_chunk_worker(dev); -+ -+ if (cache) -+ return cache; -+ -+ /* -+ * Thery were all in use. -+ * Find the LRU cache and flush it if it is dirty. -+ */ -+ -+ usage = -1; -+ cache = NULL; -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object && -+ !dev->cache[i].locked && -+ (dev->cache[i].last_use < usage || !cache)) { -+ usage = dev->cache[i].last_use; -+ cache = &dev->cache[i]; -+ } -+ } -+ -+#if 1 -+ yaffs_flush_single_cache(cache, 1); -+#else -+ yaffs_flush_file_cache(cache->object, 1); -+ cache = yaffs_grab_chunk_worker(dev); -+#endif -+ -+ return cache; -+} -+ -+/* Find a cached chunk */ -+static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, -+ int chunk_id) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return NULL; -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object == obj && -+ dev->cache[i].chunk_id == chunk_id) { -+ dev->cache_hits++; -+ -+ return &dev->cache[i]; -+ } -+ } -+ return NULL; -+} -+ -+/* Mark the chunk for the least recently used algorithym */ -+static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, -+ int is_write) -+{ -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return; -+ -+ if (dev->cache_last_use < 0 || -+ dev->cache_last_use > 100000000) { -+ /* Reset the cache usages */ -+ for (i = 1; i < dev->param.n_caches; i++) -+ dev->cache[i].last_use = 0; -+ -+ dev->cache_last_use = 0; -+ } -+ dev->cache_last_use++; -+ cache->last_use = dev->cache_last_use; -+ -+ if (is_write) -+ cache->dirty = 1; -+} -+ -+/* Invalidate a single cache page. -+ * Do this when a whole page gets written, -+ * ie the short cache for this page is no longer valid. -+ */ -+static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) -+{ -+ struct yaffs_cache *cache; -+ -+ if (object->my_dev->param.n_caches > 0) { -+ cache = yaffs_find_chunk_cache(object, chunk_id); -+ -+ if (cache) -+ cache->object = NULL; -+ } -+} -+ -+/* Invalidate all the cache pages associated with this object -+ * Do this whenever ther file is deleted or resized. -+ */ -+static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) -+{ -+ int i; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (dev->param.n_caches > 0) { -+ /* Invalidate it. */ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object == in) -+ dev->cache[i].object = NULL; -+ } -+ } -+} -+ -+static void yaffs_unhash_obj(struct yaffs_obj *obj) -+{ -+ int bucket; -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ /* If it is still linked into the bucket list, free from the list */ -+ if (!list_empty(&obj->hash_link)) { -+ list_del_init(&obj->hash_link); -+ bucket = yaffs_hash_fn(obj->obj_id); -+ dev->obj_bucket[bucket].count--; -+ } -+} -+ -+/* FreeObject frees up a Object and puts it back on the free list */ -+static void yaffs_free_obj(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ -+ if (!obj) { -+ BUG(); -+ return; -+ } -+ dev = obj->my_dev; -+ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", -+ obj, obj->my_inode); -+ if (obj->parent) -+ BUG(); -+ if (!list_empty(&obj->siblings)) -+ BUG(); -+ -+ if (obj->my_inode) { -+ /* We're still hooked up to a cached inode. -+ * Don't delete now, but mark for later deletion -+ */ -+ obj->defered_free = 1; -+ return; -+ } -+ -+ yaffs_unhash_obj(obj); -+ -+ yaffs_free_raw_obj(dev, obj); -+ dev->n_obj--; -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+void yaffs_handle_defered_free(struct yaffs_obj *obj) -+{ -+ if (obj->defered_free) -+ yaffs_free_obj(obj); -+} -+ -+static int yaffs_generic_obj_del(struct yaffs_obj *in) -+{ -+ /* Iinvalidate the file's data in the cache, without flushing. */ -+ yaffs_invalidate_whole_cache(in); -+ -+ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { -+ /* Move to unlinked directory so we have a deletion record */ -+ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, -+ 0); -+ } -+ -+ yaffs_remove_obj_from_dir(in); -+ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); -+ in->hdr_chunk = 0; -+ -+ yaffs_free_obj(in); -+ return YAFFS_OK; -+ -+} -+ -+static void yaffs_soft_del_file(struct yaffs_obj *obj) -+{ -+ if (!obj->deleted || -+ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || -+ obj->soft_del) -+ return; -+ -+ if (obj->n_data_chunks <= 0) { -+ /* Empty file with no duplicate object headers, -+ * just delete it immediately */ -+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); -+ obj->variant.file_variant.top = NULL; -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: Deleting empty file %d", -+ obj->obj_id); -+ yaffs_generic_obj_del(obj); -+ } else { -+ yaffs_soft_del_worker(obj, -+ obj->variant.file_variant.top, -+ obj->variant. -+ file_variant.top_level, 0); -+ obj->soft_del = 1; -+ } -+} -+ -+/* Pruning removes any part of the file structure tree that is beyond the -+ * bounds of the file (ie that does not point to chunks). -+ * -+ * A file should only get pruned when its size is reduced. -+ * -+ * Before pruning, the chunks must be pulled from the tree and the -+ * level 0 tnode entries must be zeroed out. -+ * Could also use this for file deletion, but that's probably better handled -+ * by a special case. -+ * -+ * This function is recursive. For levels > 0 the function is called again on -+ * any sub-tree. For level == 0 we just check if the sub-tree has data. -+ * If there is no data in a subtree then it is pruned. -+ */ -+ -+static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, -+ struct yaffs_tnode *tn, u32 level, -+ int del0) -+{ -+ int i; -+ int has_data; -+ -+ if (!tn) -+ return tn; -+ -+ has_data = 0; -+ -+ if (level > 0) { -+ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { -+ if (tn->internal[i]) { -+ tn->internal[i] = -+ yaffs_prune_worker(dev, -+ tn->internal[i], -+ level - 1, -+ (i == 0) ? del0 : 1); -+ } -+ -+ if (tn->internal[i]) -+ has_data++; -+ } -+ } else { -+ int tnode_size_u32 = dev->tnode_size / sizeof(u32); -+ u32 *map = (u32 *) tn; -+ -+ for (i = 0; !has_data && i < tnode_size_u32; i++) { -+ if (map[i]) -+ has_data++; -+ } -+ } -+ -+ if (has_data == 0 && del0) { -+ /* Free and return NULL */ -+ yaffs_free_tnode(dev, tn); -+ tn = NULL; -+ } -+ return tn; -+} -+ -+static int yaffs_prune_tree(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct) -+{ -+ int i; -+ int has_data; -+ int done = 0; -+ struct yaffs_tnode *tn; -+ -+ if (file_struct->top_level < 1) -+ return YAFFS_OK; -+ -+ file_struct->top = -+ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); -+ -+ /* Now we have a tree with all the non-zero branches NULL but -+ * the height is the same as it was. -+ * Let's see if we can trim internal tnodes to shorten the tree. -+ * We can do this if only the 0th element in the tnode is in use -+ * (ie all the non-zero are NULL) -+ */ -+ -+ while (file_struct->top_level && !done) { -+ tn = file_struct->top; -+ -+ has_data = 0; -+ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { -+ if (tn->internal[i]) -+ has_data++; -+ } -+ -+ if (!has_data) { -+ file_struct->top = tn->internal[0]; -+ file_struct->top_level--; -+ yaffs_free_tnode(dev, tn); -+ } else { -+ done = 1; -+ } -+ } -+ -+ return YAFFS_OK; -+} -+ -+/*-------------------- End of File Structure functions.-------------------*/ -+ -+/* alloc_empty_obj gets us a clean Object.*/ -+static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); -+ -+ if (!obj) -+ return obj; -+ -+ dev->n_obj++; -+ -+ /* Now sweeten it up... */ -+ -+ memset(obj, 0, sizeof(struct yaffs_obj)); -+ obj->being_created = 1; -+ -+ obj->my_dev = dev; -+ obj->hdr_chunk = 0; -+ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; -+ INIT_LIST_HEAD(&(obj->hard_links)); -+ INIT_LIST_HEAD(&(obj->hash_link)); -+ INIT_LIST_HEAD(&obj->siblings); -+ -+ /* Now make the directory sane */ -+ if (dev->root_dir) { -+ obj->parent = dev->root_dir; -+ list_add(&(obj->siblings), -+ &dev->root_dir->variant.dir_variant.children); -+ } -+ -+ /* Add it to the lost and found directory. -+ * NB Can't put root or lost-n-found in lost-n-found so -+ * check if lost-n-found exists first -+ */ -+ if (dev->lost_n_found) -+ yaffs_add_obj_to_dir(dev->lost_n_found, obj); -+ -+ obj->being_created = 0; -+ -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+ -+ return obj; -+} -+ -+static int yaffs_find_nice_bucket(struct yaffs_dev *dev) -+{ -+ int i; -+ int l = 999; -+ int lowest = 999999; -+ -+ /* Search for the shortest list or one that -+ * isn't too long. -+ */ -+ -+ for (i = 0; i < 10 && lowest > 4; i++) { -+ dev->bucket_finder++; -+ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; -+ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { -+ lowest = dev->obj_bucket[dev->bucket_finder].count; -+ l = dev->bucket_finder; -+ } -+ } -+ -+ return l; -+} -+ -+static int yaffs_new_obj_id(struct yaffs_dev *dev) -+{ -+ int bucket = yaffs_find_nice_bucket(dev); -+ int found = 0; -+ struct list_head *i; -+ u32 n = (u32) bucket; -+ -+ /* -+ * Now find an object value that has not already been taken -+ * by scanning the list, incrementing each time by number of buckets. -+ */ -+ while (!found) { -+ found = 1; -+ n += YAFFS_NOBJECT_BUCKETS; -+ list_for_each(i, &dev->obj_bucket[bucket].list) { -+ /* Check if this value is already taken. */ -+ if (i && list_entry(i, struct yaffs_obj, -+ hash_link)->obj_id == n) -+ found = 0; -+ } -+ } -+ return n; -+} -+ -+static void yaffs_hash_obj(struct yaffs_obj *in) -+{ -+ int bucket = yaffs_hash_fn(in->obj_id); -+ struct yaffs_dev *dev = in->my_dev; -+ -+ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); -+ dev->obj_bucket[bucket].count++; -+} -+ -+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) -+{ -+ int bucket = yaffs_hash_fn(number); -+ struct list_head *i; -+ struct yaffs_obj *in; -+ -+ list_for_each(i, &dev->obj_bucket[bucket].list) { -+ /* Look if it is in the list */ -+ in = list_entry(i, struct yaffs_obj, hash_link); -+ if (in->obj_id == number) { -+ /* Don't show if it is defered free */ -+ if (in->defered_free) -+ return NULL; -+ return in; -+ } -+ } -+ -+ return NULL; -+} -+ -+static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, -+ enum yaffs_obj_type type) -+{ -+ struct yaffs_obj *the_obj = NULL; -+ struct yaffs_tnode *tn = NULL; -+ -+ if (number < 0) -+ number = yaffs_new_obj_id(dev); -+ -+ if (type == YAFFS_OBJECT_TYPE_FILE) { -+ tn = yaffs_get_tnode(dev); -+ if (!tn) -+ return NULL; -+ } -+ -+ the_obj = yaffs_alloc_empty_obj(dev); -+ if (!the_obj) { -+ if (tn) -+ yaffs_free_tnode(dev, tn); -+ return NULL; -+ } -+ -+ the_obj->fake = 0; -+ the_obj->rename_allowed = 1; -+ the_obj->unlink_allowed = 1; -+ the_obj->obj_id = number; -+ yaffs_hash_obj(the_obj); -+ the_obj->variant_type = type; -+ yaffs_load_current_time(the_obj, 1, 1); -+ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ the_obj->variant.file_variant.file_size = 0; -+ the_obj->variant.file_variant.scanned_size = 0; -+ the_obj->variant.file_variant.shrink_size = -+ yaffs_max_file_size(dev); -+ the_obj->variant.file_variant.top_level = 0; -+ the_obj->variant.file_variant.top = tn; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); -+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* No action required */ -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* todo this should not happen */ -+ break; -+ } -+ return the_obj; -+} -+ -+static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, -+ int number, u32 mode) -+{ -+ -+ struct yaffs_obj *obj = -+ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); -+ -+ if (!obj) -+ return NULL; -+ -+ obj->fake = 1; /* it is fake so it might not use NAND */ -+ obj->rename_allowed = 0; -+ obj->unlink_allowed = 0; -+ obj->deleted = 0; -+ obj->unlinked = 0; -+ obj->yst_mode = mode; -+ obj->my_dev = dev; -+ obj->hdr_chunk = 0; /* Not a valid chunk. */ -+ return obj; -+ -+} -+ -+ -+static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ dev->n_obj = 0; -+ dev->n_tnodes = 0; -+ yaffs_init_raw_tnodes_and_objs(dev); -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ INIT_LIST_HEAD(&dev->obj_bucket[i].list); -+ dev->obj_bucket[i].count = 0; -+ } -+} -+ -+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, -+ int number, -+ enum yaffs_obj_type type) -+{ -+ struct yaffs_obj *the_obj = NULL; -+ -+ if (number > 0) -+ the_obj = yaffs_find_by_number(dev, number); -+ -+ if (!the_obj) -+ the_obj = yaffs_new_obj(dev, number, type); -+ -+ return the_obj; -+ -+} -+ -+YCHAR *yaffs_clone_str(const YCHAR *str) -+{ -+ YCHAR *new_str = NULL; -+ int len; -+ -+ if (!str) -+ str = _Y(""); -+ -+ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); -+ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); -+ if (new_str) { -+ strncpy(new_str, str, len); -+ new_str[len] = 0; -+ } -+ return new_str; -+ -+} -+/* -+ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new -+ * link (ie. name) is created or deleted in the directory. -+ * -+ * ie. -+ * create dir/a : update dir's mtime/ctime -+ * rm dir/a: update dir's mtime/ctime -+ * modify dir/a: don't update dir's mtimme/ctime -+ * -+ * This can be handled immediately or defered. Defering helps reduce the number -+ * of updates when many files in a directory are changed within a brief period. -+ * -+ * If the directory updating is defered then yaffs_update_dirty_dirs must be -+ * called periodically. -+ */ -+ -+static void yaffs_update_parent(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ -+ if (!obj) -+ return; -+ dev = obj->my_dev; -+ obj->dirty = 1; -+ yaffs_load_current_time(obj, 0, 1); -+ if (dev->param.defered_dir_update) { -+ struct list_head *link = &obj->variant.dir_variant.dirty; -+ -+ if (list_empty(link)) { -+ list_add(link, &dev->dirty_dirs); -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "Added object %d to dirty directories", -+ obj->obj_id); -+ } -+ -+ } else { -+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); -+ } -+} -+ -+void yaffs_update_dirty_dirs(struct yaffs_dev *dev) -+{ -+ struct list_head *link; -+ struct yaffs_obj *obj; -+ struct yaffs_dir_var *d_s; -+ union yaffs_obj_var *o_v; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); -+ -+ while (!list_empty(&dev->dirty_dirs)) { -+ link = dev->dirty_dirs.next; -+ list_del_init(link); -+ -+ d_s = list_entry(link, struct yaffs_dir_var, dirty); -+ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); -+ obj = list_entry(o_v, struct yaffs_obj, variant); -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", -+ obj->obj_id); -+ -+ if (obj->dirty) -+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); -+ } -+} -+ -+/* -+ * Mknod (create) a new object. -+ * equiv_obj only has meaning for a hard link; -+ * alias_str only has meaning for a symlink. -+ * rdev only has meaning for devices (a subset of special objects) -+ */ -+ -+static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, -+ struct yaffs_obj *parent, -+ const YCHAR *name, -+ u32 mode, -+ u32 uid, -+ u32 gid, -+ struct yaffs_obj *equiv_obj, -+ const YCHAR *alias_str, u32 rdev) -+{ -+ struct yaffs_obj *in; -+ YCHAR *str = NULL; -+ struct yaffs_dev *dev = parent->my_dev; -+ -+ /* Check if the entry exists. -+ * If it does then fail the call since we don't want a dup. */ -+ if (yaffs_find_by_name(parent, name)) -+ return NULL; -+ -+ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ str = yaffs_clone_str(alias_str); -+ if (!str) -+ return NULL; -+ } -+ -+ in = yaffs_new_obj(dev, -1, type); -+ -+ if (!in) { -+ kfree(str); -+ return NULL; -+ } -+ -+ in->hdr_chunk = 0; -+ in->valid = 1; -+ in->variant_type = type; -+ -+ in->yst_mode = mode; -+ -+ yaffs_attribs_init(in, gid, uid, rdev); -+ -+ in->n_data_chunks = 0; -+ -+ yaffs_set_obj_name(in, name); -+ in->dirty = 1; -+ -+ yaffs_add_obj_to_dir(parent, in); -+ -+ in->my_dev = parent->my_dev; -+ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ in->variant.symlink_variant.alias = str; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ in->variant.hardlink_variant.equiv_obj = equiv_obj; -+ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; -+ list_add(&in->hard_links, &equiv_obj->hard_links); -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* do nothing */ -+ break; -+ } -+ -+ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { -+ /* Could not create the object header, fail */ -+ yaffs_del_obj(in); -+ in = NULL; -+ } -+ -+ if (in) -+ yaffs_update_parent(parent); -+ -+ return in; -+} -+ -+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, -+ uid, gid, NULL, NULL, 0); -+} -+ -+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, -+ u32 mode, u32 uid, u32 gid) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, -+ mode, uid, gid, NULL, NULL, 0); -+} -+ -+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, u32 rdev) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, -+ uid, gid, NULL, NULL, rdev); -+} -+ -+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, const YCHAR *alias) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, -+ uid, gid, NULL, alias, 0); -+} -+ -+/* yaffs_link_obj returns the object id of the equivalent object.*/ -+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, -+ struct yaffs_obj *equiv_obj) -+{ -+ /* Get the real object in case we were fed a hard link obj */ -+ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); -+ -+ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, -+ parent, name, 0, 0, 0, -+ equiv_obj, NULL, 0)) -+ return equiv_obj; -+ -+ return NULL; -+ -+} -+ -+ -+ -+/*---------------------- Block Management and Page Allocation -------------*/ -+ -+static void yaffs_deinit_blocks(struct yaffs_dev *dev) -+{ -+ if (dev->block_info_alt && dev->block_info) -+ vfree(dev->block_info); -+ else -+ kfree(dev->block_info); -+ -+ dev->block_info_alt = 0; -+ -+ dev->block_info = NULL; -+ -+ if (dev->chunk_bits_alt && dev->chunk_bits) -+ vfree(dev->chunk_bits); -+ else -+ kfree(dev->chunk_bits); -+ dev->chunk_bits_alt = 0; -+ dev->chunk_bits = NULL; -+} -+ -+static int yaffs_init_blocks(struct yaffs_dev *dev) -+{ -+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ -+ dev->block_info = NULL; -+ dev->chunk_bits = NULL; -+ dev->alloc_block = -1; /* force it to get a new one */ -+ -+ /* If the first allocation strategy fails, thry the alternate one */ -+ dev->block_info = -+ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); -+ if (!dev->block_info) { -+ dev->block_info = -+ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); -+ dev->block_info_alt = 1; -+ } else { -+ dev->block_info_alt = 0; -+ } -+ -+ if (!dev->block_info) -+ goto alloc_error; -+ -+ /* Set up dynamic blockinfo stuff. Round up bytes. */ -+ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; -+ dev->chunk_bits = -+ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); -+ if (!dev->chunk_bits) { -+ dev->chunk_bits = -+ vmalloc(dev->chunk_bit_stride * n_blocks); -+ dev->chunk_bits_alt = 1; -+ } else { -+ dev->chunk_bits_alt = 0; -+ } -+ if (!dev->chunk_bits) -+ goto alloc_error; -+ -+ -+ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); -+ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); -+ return YAFFS_OK; -+ -+alloc_error: -+ yaffs_deinit_blocks(dev); -+ return YAFFS_FAIL; -+} -+ -+ -+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); -+ int erased_ok = 0; -+ int i; -+ -+ /* If the block is still healthy erase it and mark as clean. -+ * If the block has had a data failure, then retire it. -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, -+ "yaffs_block_became_dirty block %d state %d %s", -+ block_no, bi->block_state, -+ (bi->needs_retiring) ? "needs retiring" : ""); -+ -+ yaffs2_clear_oldest_dirty_seq(dev, bi); -+ -+ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; -+ -+ /* If this is the block being garbage collected then stop gc'ing */ -+ if (block_no == dev->gc_block) -+ dev->gc_block = 0; -+ -+ /* If this block is currently the best candidate for gc -+ * then drop as a candidate */ -+ if (block_no == dev->gc_dirtiest) { -+ dev->gc_dirtiest = 0; -+ dev->gc_pages_in_use = 0; -+ } -+ -+ if (!bi->needs_retiring) { -+ yaffs2_checkpt_invalidate(dev); -+ erased_ok = yaffs_erase_block(dev, block_no); -+ if (!erased_ok) { -+ dev->n_erase_failures++; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Erasure failed %d", block_no); -+ } -+ } -+ -+ /* Verify erasure if needed */ -+ if (erased_ok && -+ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || -+ !yaffs_skip_verification(dev))) { -+ for (i = 0; i < dev->param.chunks_per_block; i++) { -+ if (!yaffs_check_chunk_erased(dev, -+ block_no * dev->param.chunks_per_block + i)) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ ">>Block %d erasure supposedly OK, but chunk %d not erased", -+ block_no, i); -+ } -+ } -+ } -+ -+ if (!erased_ok) { -+ /* We lost a block of free space */ -+ dev->n_free_chunks -= dev->param.chunks_per_block; -+ yaffs_retire_block(dev, block_no); -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Block %d retired", block_no); -+ return; -+ } -+ -+ /* Clean it up... */ -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ bi->seq_number = 0; -+ dev->n_erased_blocks++; -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ bi->has_shrink_hdr = 0; -+ bi->skip_erased_check = 1; /* Clean, so no need to check */ -+ bi->gc_prioritise = 0; -+ bi->has_summary = 0; -+ -+ yaffs_clear_chunk_bits(dev, block_no); -+ -+ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); -+} -+ -+static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, -+ int old_chunk, u8 *buffer) -+{ -+ int new_chunk; -+ int mark_flash = 1; -+ struct yaffs_ext_tags tags; -+ struct yaffs_obj *object; -+ int matching_chunk; -+ int ret_val = YAFFS_OK; -+ -+ memset(&tags, 0, sizeof(tags)); -+ yaffs_rd_chunk_tags_nand(dev, old_chunk, -+ buffer, &tags); -+ object = yaffs_find_by_number(dev, tags.obj_id); -+ -+ yaffs_trace(YAFFS_TRACE_GC_DETAIL, -+ "Collecting chunk in block %d, %d %d %d ", -+ dev->gc_chunk, tags.obj_id, -+ tags.chunk_id, tags.n_bytes); -+ -+ if (object && !yaffs_skip_verification(dev)) { -+ if (tags.chunk_id == 0) -+ matching_chunk = -+ object->hdr_chunk; -+ else if (object->soft_del) -+ /* Defeat the test */ -+ matching_chunk = old_chunk; -+ else -+ matching_chunk = -+ yaffs_find_chunk_in_file -+ (object, tags.chunk_id, -+ NULL); -+ -+ if (old_chunk != matching_chunk) -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "gc: page in gc mismatch: %d %d %d %d", -+ old_chunk, -+ matching_chunk, -+ tags.obj_id, -+ tags.chunk_id); -+ } -+ -+ if (!object) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "page %d in gc has no object: %d %d %d ", -+ old_chunk, -+ tags.obj_id, tags.chunk_id, -+ tags.n_bytes); -+ } -+ -+ if (object && -+ object->deleted && -+ object->soft_del && tags.chunk_id != 0) { -+ /* Data chunk in a soft deleted file, -+ * throw it away. -+ * It's a soft deleted data chunk, -+ * No need to copy this, just forget -+ * about it and fix up the object. -+ */ -+ -+ /* Free chunks already includes -+ * softdeleted chunks, how ever this -+ * chunk is going to soon be really -+ * deleted which will increment free -+ * chunks. We have to decrement free -+ * chunks so this works out properly. -+ */ -+ dev->n_free_chunks--; -+ bi->soft_del_pages--; -+ -+ object->n_data_chunks--; -+ if (object->n_data_chunks <= 0) { -+ /* remeber to clean up obj */ -+ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; -+ dev->n_clean_ups++; -+ } -+ mark_flash = 0; -+ } else if (object) { -+ /* It's either a data chunk in a live -+ * file or an ObjectHeader, so we're -+ * interested in it. -+ * NB Need to keep the ObjectHeaders of -+ * deleted files until the whole file -+ * has been deleted off -+ */ -+ tags.serial_number++; -+ dev->n_gc_copies++; -+ -+ if (tags.chunk_id == 0) { -+ /* It is an object Id, -+ * We need to nuke the -+ * shrinkheader flags since its -+ * work is done. -+ * Also need to clean up -+ * shadowing. -+ */ -+ struct yaffs_obj_hdr *oh; -+ oh = (struct yaffs_obj_hdr *) buffer; -+ -+ oh->is_shrink = 0; -+ tags.extra_is_shrink = 0; -+ oh->shadows_obj = 0; -+ oh->inband_shadowed_obj_id = 0; -+ tags.extra_shadows = 0; -+ -+ /* Update file size */ -+ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { -+ yaffs_oh_size_load(oh, -+ object->variant.file_variant.file_size); -+ tags.extra_file_size = -+ object->variant.file_variant.file_size; -+ } -+ -+ yaffs_verify_oh(object, oh, &tags, 1); -+ new_chunk = -+ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); -+ } else { -+ new_chunk = -+ yaffs_write_new_chunk(dev, buffer, &tags, 1); -+ } -+ -+ if (new_chunk < 0) { -+ ret_val = YAFFS_FAIL; -+ } else { -+ -+ /* Now fix up the Tnodes etc. */ -+ -+ if (tags.chunk_id == 0) { -+ /* It's a header */ -+ object->hdr_chunk = new_chunk; -+ object->serial = tags.serial_number; -+ } else { -+ /* It's a data chunk */ -+ yaffs_put_chunk_in_file(object, tags.chunk_id, -+ new_chunk, 0); -+ } -+ } -+ } -+ if (ret_val == YAFFS_OK) -+ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); -+ return ret_val; -+} -+ -+static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) -+{ -+ int old_chunk; -+ int ret_val = YAFFS_OK; -+ int i; -+ int is_checkpt_block; -+ int max_copies; -+ int chunks_before = yaffs_get_erased_chunks(dev); -+ int chunks_after; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); -+ -+ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "Collecting block %d, in use %d, shrink %d, whole_block %d", -+ block, bi->pages_in_use, bi->has_shrink_hdr, -+ whole_block); -+ -+ /*yaffs_verify_free_chunks(dev); */ -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) -+ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; -+ -+ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ -+ -+ dev->gc_disable = 1; -+ -+ yaffs_summary_gc(dev, block); -+ -+ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "Collecting block %d that has no chunks in use", -+ block); -+ yaffs_block_became_dirty(dev, block); -+ } else { -+ -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ yaffs_verify_blk(dev, bi, block); -+ -+ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; -+ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; -+ -+ for (/* init already done */ ; -+ ret_val == YAFFS_OK && -+ dev->gc_chunk < dev->param.chunks_per_block && -+ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && -+ max_copies > 0; -+ dev->gc_chunk++, old_chunk++) { -+ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { -+ /* Page is in use and might need to be copied */ -+ max_copies--; -+ ret_val = yaffs_gc_process_chunk(dev, bi, -+ old_chunk, buffer); -+ } -+ } -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ -+ yaffs_verify_collected_blk(dev, bi, block); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { -+ /* -+ * The gc did not complete. Set block state back to FULL -+ * because checkpointing does not restore gc. -+ */ -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ } else { -+ /* The gc completed. */ -+ /* Do any required cleanups */ -+ for (i = 0; i < dev->n_clean_ups; i++) { -+ /* Time to delete the file too */ -+ struct yaffs_obj *object = -+ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); -+ if (object) { -+ yaffs_free_tnode(dev, -+ object->variant.file_variant.top); -+ object->variant.file_variant.top = NULL; -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: About to finally delete object %d", -+ object->obj_id); -+ yaffs_generic_obj_del(object); -+ object->my_dev->n_deleted_files--; -+ } -+ -+ } -+ chunks_after = yaffs_get_erased_chunks(dev); -+ if (chunks_before >= chunks_after) -+ yaffs_trace(YAFFS_TRACE_GC, -+ "gc did not increase free chunks before %d after %d", -+ chunks_before, chunks_after); -+ dev->gc_block = 0; -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ -+ dev->gc_disable = 0; -+ -+ return ret_val; -+} -+ -+/* -+ * find_gc_block() selects the dirtiest block (or close enough) -+ * for garbage collection. -+ */ -+ -+static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, -+ int aggressive, int background) -+{ -+ int i; -+ int iterations; -+ unsigned selected = 0; -+ int prioritised = 0; -+ int prioritised_exist = 0; -+ struct yaffs_block_info *bi; -+ int threshold; -+ -+ /* First let's see if we need to grab a prioritised block */ -+ if (dev->has_pending_prioritised_gc && !aggressive) { -+ dev->gc_dirtiest = 0; -+ bi = dev->block_info; -+ for (i = dev->internal_start_block; -+ i <= dev->internal_end_block && !selected; i++) { -+ -+ if (bi->gc_prioritise) { -+ prioritised_exist = 1; -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && -+ yaffs_block_ok_for_gc(dev, bi)) { -+ selected = i; -+ prioritised = 1; -+ } -+ } -+ bi++; -+ } -+ -+ /* -+ * If there is a prioritised block and none was selected then -+ * this happened because there is at least one old dirty block -+ * gumming up the works. Let's gc the oldest dirty block. -+ */ -+ -+ if (prioritised_exist && -+ !selected && dev->oldest_dirty_block > 0) -+ selected = dev->oldest_dirty_block; -+ -+ if (!prioritised_exist) /* None found, so we can clear this */ -+ dev->has_pending_prioritised_gc = 0; -+ } -+ -+ /* If we're doing aggressive GC then we are happy to take a less-dirty -+ * block, and search harder. -+ * else (leasurely gc), then we only bother to do this if the -+ * block has only a few pages in use. -+ */ -+ -+ if (!selected) { -+ int pages_used; -+ int n_blocks = -+ dev->internal_end_block - dev->internal_start_block + 1; -+ if (aggressive) { -+ threshold = dev->param.chunks_per_block; -+ iterations = n_blocks; -+ } else { -+ int max_threshold; -+ -+ if (background) -+ max_threshold = dev->param.chunks_per_block / 2; -+ else -+ max_threshold = dev->param.chunks_per_block / 8; -+ -+ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) -+ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; -+ -+ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; -+ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) -+ threshold = YAFFS_GC_PASSIVE_THRESHOLD; -+ if (threshold > max_threshold) -+ threshold = max_threshold; -+ -+ iterations = n_blocks / 16 + 1; -+ if (iterations > 100) -+ iterations = 100; -+ } -+ -+ for (i = 0; -+ i < iterations && -+ (dev->gc_dirtiest < 1 || -+ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); -+ i++) { -+ dev->gc_block_finder++; -+ if (dev->gc_block_finder < dev->internal_start_block || -+ dev->gc_block_finder > dev->internal_end_block) -+ dev->gc_block_finder = -+ dev->internal_start_block; -+ -+ bi = yaffs_get_block_info(dev, dev->gc_block_finder); -+ -+ pages_used = bi->pages_in_use - bi->soft_del_pages; -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && -+ pages_used < dev->param.chunks_per_block && -+ (dev->gc_dirtiest < 1 || -+ pages_used < dev->gc_pages_in_use) && -+ yaffs_block_ok_for_gc(dev, bi)) { -+ dev->gc_dirtiest = dev->gc_block_finder; -+ dev->gc_pages_in_use = pages_used; -+ } -+ } -+ -+ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) -+ selected = dev->gc_dirtiest; -+ } -+ -+ /* -+ * If nothing has been selected for a while, try the oldest dirty -+ * because that's gumming up the works. -+ */ -+ -+ if (!selected && dev->param.is_yaffs2 && -+ dev->gc_not_done >= (background ? 10 : 20)) { -+ yaffs2_find_oldest_dirty_seq(dev); -+ if (dev->oldest_dirty_block > 0) { -+ selected = dev->oldest_dirty_block; -+ dev->gc_dirtiest = selected; -+ dev->oldest_dirty_gc_count++; -+ bi = yaffs_get_block_info(dev, selected); -+ dev->gc_pages_in_use = -+ bi->pages_in_use - bi->soft_del_pages; -+ } else { -+ dev->gc_not_done = 0; -+ } -+ } -+ -+ if (selected) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC Selected block %d with %d free, prioritised:%d", -+ selected, -+ dev->param.chunks_per_block - dev->gc_pages_in_use, -+ prioritised); -+ -+ dev->n_gc_blocks++; -+ if (background) -+ dev->bg_gcs++; -+ -+ dev->gc_dirtiest = 0; -+ dev->gc_pages_in_use = 0; -+ dev->gc_not_done = 0; -+ if (dev->refresh_skip > 0) -+ dev->refresh_skip--; -+ } else { -+ dev->gc_not_done++; -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", -+ dev->gc_block_finder, dev->gc_not_done, threshold, -+ dev->gc_dirtiest, dev->gc_pages_in_use, -+ dev->oldest_dirty_block, background ? " bg" : ""); -+ } -+ -+ return selected; -+} -+ -+/* New garbage collector -+ * If we're very low on erased blocks then we do aggressive garbage collection -+ * otherwise we do "leasurely" garbage collection. -+ * Aggressive gc looks further (whole array) and will accept less dirty blocks. -+ * Passive gc only inspects smaller areas and only accepts more dirty blocks. -+ * -+ * The idea is to help clear out space in a more spread-out manner. -+ * Dunno if it really does anything useful. -+ */ -+static int yaffs_check_gc(struct yaffs_dev *dev, int background) -+{ -+ int aggressive = 0; -+ int gc_ok = YAFFS_OK; -+ int max_tries = 0; -+ int min_erased; -+ int erased_chunks; -+ int checkpt_block_adjust; -+ -+ if (dev->param.gc_control_fn && -+ (dev->param.gc_control_fn(dev) & 1) == 0) -+ return YAFFS_OK; -+ -+ if (dev->gc_disable) -+ /* Bail out so we don't get recursive gc */ -+ return YAFFS_OK; -+ -+ /* This loop should pass the first time. -+ * Only loops here if the collection does not increase space. -+ */ -+ -+ do { -+ max_tries++; -+ -+ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); -+ -+ min_erased = -+ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; -+ erased_chunks = -+ dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ /* If we need a block soon then do aggressive gc. */ -+ if (dev->n_erased_blocks < min_erased) -+ aggressive = 1; -+ else { -+ if (!background -+ && erased_chunks > (dev->n_free_chunks / 4)) -+ break; -+ -+ if (dev->gc_skip > 20) -+ dev->gc_skip = 20; -+ if (erased_chunks < dev->n_free_chunks / 2 || -+ dev->gc_skip < 1 || background) -+ aggressive = 0; -+ else { -+ dev->gc_skip--; -+ break; -+ } -+ } -+ -+ dev->gc_skip = 5; -+ -+ /* If we don't already have a block being gc'd then see if we -+ * should start another */ -+ -+ if (dev->gc_block < 1 && !aggressive) { -+ dev->gc_block = yaffs2_find_refresh_block(dev); -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ if (dev->gc_block < 1) { -+ dev->gc_block = -+ yaffs_find_gc_block(dev, aggressive, background); -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ -+ if (dev->gc_block > 0) { -+ dev->all_gcs++; -+ if (!aggressive) -+ dev->passive_gc_count++; -+ -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: GC n_erased_blocks %d aggressive %d", -+ dev->n_erased_blocks, aggressive); -+ -+ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); -+ } -+ -+ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && -+ dev->gc_block > 0) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", -+ dev->n_erased_blocks, max_tries, -+ dev->gc_block); -+ } -+ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && -+ (dev->gc_block > 0) && (max_tries < 2)); -+ -+ return aggressive ? gc_ok : YAFFS_OK; -+} -+ -+/* -+ * yaffs_bg_gc() -+ * Garbage collects. Intended to be called from a background thread. -+ * Returns non-zero if at least half the free chunks are erased. -+ */ -+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) -+{ -+ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); -+ -+ yaffs_check_gc(dev, 1); -+ return erased_chunks > dev->n_free_chunks / 2; -+} -+ -+/*-------------------- Data file manipulation -----------------*/ -+ -+static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) -+{ -+ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); -+ -+ if (nand_chunk >= 0) -+ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, -+ buffer, NULL); -+ else { -+ yaffs_trace(YAFFS_TRACE_NANDACCESS, -+ "Chunk %d not found zero instead", -+ nand_chunk); -+ /* get sane (zero) data if you read a hole */ -+ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); -+ return 0; -+ } -+ -+} -+ -+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, -+ int lyn) -+{ -+ int block; -+ int page; -+ struct yaffs_ext_tags tags; -+ struct yaffs_block_info *bi; -+ -+ if (chunk_id <= 0) -+ return; -+ -+ dev->n_deletions++; -+ block = chunk_id / dev->param.chunks_per_block; -+ page = chunk_id % dev->param.chunks_per_block; -+ -+ if (!yaffs_check_chunk_bit(dev, block, page)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Deleting invalid chunk %d", chunk_id); -+ -+ bi = yaffs_get_block_info(dev, block); -+ -+ yaffs2_update_oldest_dirty_seq(dev, block, bi); -+ -+ yaffs_trace(YAFFS_TRACE_DELETION, -+ "line %d delete of chunk %d", -+ lyn, chunk_id); -+ -+ if (!dev->param.is_yaffs2 && mark_flash && -+ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { -+ -+ memset(&tags, 0, sizeof(tags)); -+ tags.is_deleted = 1; -+ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); -+ yaffs_handle_chunk_update(dev, chunk_id, &tags); -+ } else { -+ dev->n_unmarked_deletions++; -+ } -+ -+ /* Pull out of the management area. -+ * If the whole block became dirty, this will kick off an erasure. -+ */ -+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || -+ bi->block_state == YAFFS_BLOCK_STATE_FULL || -+ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { -+ dev->n_free_chunks++; -+ yaffs_clear_chunk_bit(dev, block, page); -+ bi->pages_in_use--; -+ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && -+ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ yaffs_block_became_dirty(dev, block); -+ } -+ } -+} -+ -+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, -+ const u8 *buffer, int n_bytes, int use_reserve) -+{ -+ /* Find old chunk Need to do this to get serial number -+ * Write new one and patch into tree. -+ * Invalidate old tags. -+ */ -+ -+ int prev_chunk_id; -+ struct yaffs_ext_tags prev_tags; -+ int new_chunk_id; -+ struct yaffs_ext_tags new_tags; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ yaffs_check_gc(dev, 0); -+ -+ /* Get the previous chunk at this location in the file if it exists. -+ * If it does not exist then put a zero into the tree. This creates -+ * the tnode now, rather than later when it is harder to clean up. -+ */ -+ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); -+ if (prev_chunk_id < 1 && -+ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) -+ return 0; -+ -+ /* Set up new tags */ -+ memset(&new_tags, 0, sizeof(new_tags)); -+ -+ new_tags.chunk_id = inode_chunk; -+ new_tags.obj_id = in->obj_id; -+ new_tags.serial_number = -+ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; -+ new_tags.n_bytes = n_bytes; -+ -+ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Writing %d bytes to chunk!!!!!!!!!", -+ n_bytes); -+ BUG(); -+ } -+ -+ new_chunk_id = -+ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); -+ -+ if (new_chunk_id > 0) { -+ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); -+ -+ if (prev_chunk_id > 0) -+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -+ -+ yaffs_verify_file_sane(in); -+ } -+ return new_chunk_id; -+ -+} -+ -+ -+ -+static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, -+ const YCHAR *name, const void *value, int size, -+ int flags) -+{ -+ struct yaffs_xattr_mod xmod; -+ int result; -+ -+ xmod.set = set; -+ xmod.name = name; -+ xmod.data = value; -+ xmod.size = size; -+ xmod.flags = flags; -+ xmod.result = -ENOSPC; -+ -+ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); -+ -+ if (result > 0) -+ return xmod.result; -+ else -+ return -ENOSPC; -+} -+ -+static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, -+ struct yaffs_xattr_mod *xmod) -+{ -+ int retval = 0; -+ int x_offs = sizeof(struct yaffs_obj_hdr); -+ struct yaffs_dev *dev = obj->my_dev; -+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); -+ char *x_buffer = buffer + x_offs; -+ -+ if (xmod->set) -+ retval = -+ nval_set(x_buffer, x_size, xmod->name, xmod->data, -+ xmod->size, xmod->flags); -+ else -+ retval = nval_del(x_buffer, x_size, xmod->name); -+ -+ obj->has_xattr = nval_hasvalues(x_buffer, x_size); -+ obj->xattr_known = 1; -+ xmod->result = retval; -+ -+ return retval; -+} -+ -+static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, -+ void *value, int size) -+{ -+ char *buffer = NULL; -+ int result; -+ struct yaffs_ext_tags tags; -+ struct yaffs_dev *dev = obj->my_dev; -+ int x_offs = sizeof(struct yaffs_obj_hdr); -+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); -+ char *x_buffer; -+ int retval = 0; -+ -+ if (obj->hdr_chunk < 1) -+ return -ENODATA; -+ -+ /* If we know that the object has no xattribs then don't do all the -+ * reading and parsing. -+ */ -+ if (obj->xattr_known && !obj->has_xattr) { -+ if (name) -+ return -ENODATA; -+ else -+ return 0; -+ } -+ -+ buffer = (char *)yaffs_get_temp_buffer(dev); -+ if (!buffer) -+ return -ENOMEM; -+ -+ result = -+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); -+ -+ if (result != YAFFS_OK) -+ retval = -ENOENT; -+ else { -+ x_buffer = buffer + x_offs; -+ -+ if (!obj->xattr_known) { -+ obj->has_xattr = nval_hasvalues(x_buffer, x_size); -+ obj->xattr_known = 1; -+ } -+ -+ if (name) -+ retval = nval_get(x_buffer, x_size, name, value, size); -+ else -+ retval = nval_list(x_buffer, x_size, value, size); -+ } -+ yaffs_release_temp_buffer(dev, (u8 *) buffer); -+ return retval; -+} -+ -+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, -+ const void *value, int size, int flags) -+{ -+ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); -+} -+ -+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) -+{ -+ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); -+} -+ -+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, -+ int size) -+{ -+ return yaffs_do_xattrib_fetch(obj, name, value, size); -+} -+ -+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) -+{ -+ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); -+} -+ -+static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) -+{ -+ u8 *buf; -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_dev *dev; -+ struct yaffs_ext_tags tags; -+ int result; -+ int alloc_failed = 0; -+ -+ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) -+ return; -+ -+ dev = in->my_dev; -+ in->lazy_loaded = 0; -+ buf = yaffs_get_temp_buffer(dev); -+ -+ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); -+ oh = (struct yaffs_obj_hdr *)buf; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ yaffs_set_obj_name_from_oh(in, oh); -+ -+ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ in->variant.symlink_variant.alias = -+ yaffs_clone_str(oh->alias); -+ if (!in->variant.symlink_variant.alias) -+ alloc_failed = 1; /* Not returned */ -+ } -+ yaffs_release_temp_buffer(dev, buf); -+} -+ -+/* UpdateObjectHeader updates the header on NAND for an object. -+ * If name is not NULL, then that new name is used. -+ */ -+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, -+ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) -+{ -+ -+ struct yaffs_block_info *bi; -+ struct yaffs_dev *dev = in->my_dev; -+ int prev_chunk_id; -+ int ret_val = 0; -+ int result = 0; -+ int new_chunk_id; -+ struct yaffs_ext_tags new_tags; -+ struct yaffs_ext_tags old_tags; -+ const YCHAR *alias = NULL; -+ u8 *buffer = NULL; -+ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; -+ struct yaffs_obj_hdr *oh = NULL; -+ loff_t file_size = 0; -+ -+ strcpy(old_name, _Y("silly old name")); -+ -+ if (in->fake && in != dev->root_dir && !force && !xmod) -+ return ret_val; -+ -+ yaffs_check_gc(dev, 0); -+ yaffs_check_obj_details_loaded(in); -+ -+ buffer = yaffs_get_temp_buffer(in->my_dev); -+ oh = (struct yaffs_obj_hdr *)buffer; -+ -+ prev_chunk_id = in->hdr_chunk; -+ -+ if (prev_chunk_id > 0) { -+ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, -+ buffer, &old_tags); -+ -+ yaffs_verify_oh(in, oh, &old_tags, 0); -+ memcpy(old_name, oh->name, sizeof(oh->name)); -+ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); -+ } else { -+ memset(buffer, 0xff, dev->data_bytes_per_chunk); -+ } -+ -+ oh->type = in->variant_type; -+ oh->yst_mode = in->yst_mode; -+ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; -+ -+ yaffs_load_attribs_oh(oh, in); -+ -+ if (in->parent) -+ oh->parent_obj_id = in->parent->obj_id; -+ else -+ oh->parent_obj_id = 0; -+ -+ if (name && *name) { -+ memset(oh->name, 0, sizeof(oh->name)); -+ yaffs_load_oh_from_name(dev, oh->name, name); -+ } else if (prev_chunk_id > 0) { -+ memcpy(oh->name, old_name, sizeof(oh->name)); -+ } else { -+ memset(oh->name, 0, sizeof(oh->name)); -+ } -+ -+ oh->is_shrink = is_shrink; -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Should not happen */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && -+ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) -+ file_size = in->variant.file_variant.file_size; -+ yaffs_oh_size_load(oh, file_size); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ oh->equiv_id = in->variant.hardlink_variant.equiv_id; -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ alias = in->variant.symlink_variant.alias; -+ if (!alias) -+ alias = _Y("no alias"); -+ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); -+ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; -+ break; -+ } -+ -+ /* process any xattrib modifications */ -+ if (xmod) -+ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); -+ -+ /* Tags */ -+ memset(&new_tags, 0, sizeof(new_tags)); -+ in->serial++; -+ new_tags.chunk_id = 0; -+ new_tags.obj_id = in->obj_id; -+ new_tags.serial_number = in->serial; -+ -+ /* Add extra info for file header */ -+ new_tags.extra_available = 1; -+ new_tags.extra_parent_id = oh->parent_obj_id; -+ new_tags.extra_file_size = file_size; -+ new_tags.extra_is_shrink = oh->is_shrink; -+ new_tags.extra_equiv_id = oh->equiv_id; -+ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; -+ new_tags.extra_obj_type = in->variant_type; -+ yaffs_verify_oh(in, oh, &new_tags, 1); -+ -+ /* Create new chunk in NAND */ -+ new_chunk_id = -+ yaffs_write_new_chunk(dev, buffer, &new_tags, -+ (prev_chunk_id > 0) ? 1 : 0); -+ -+ if (buffer) -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ if (new_chunk_id < 0) -+ return new_chunk_id; -+ -+ in->hdr_chunk = new_chunk_id; -+ -+ if (prev_chunk_id > 0) -+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -+ -+ if (!yaffs_obj_cache_dirty(in)) -+ in->dirty = 0; -+ -+ /* If this was a shrink, then mark the block -+ * that the chunk lives on */ -+ if (is_shrink) { -+ bi = yaffs_get_block_info(in->my_dev, -+ new_chunk_id / -+ in->my_dev->param.chunks_per_block); -+ bi->has_shrink_hdr = 1; -+ } -+ -+ -+ return new_chunk_id; -+} -+ -+/*--------------------- File read/write ------------------------ -+ * Read and write have very similar structures. -+ * In general the read/write has three parts to it -+ * An incomplete chunk to start with (if the read/write is not chunk-aligned) -+ * Some complete chunks -+ * An incomplete chunk to end off with -+ * -+ * Curve-balls: the first chunk might also be the last chunk. -+ */ -+ -+int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) -+{ -+ int chunk; -+ u32 start; -+ int n_copy; -+ int n = n_bytes; -+ int n_done = 0; -+ struct yaffs_cache *cache; -+ struct yaffs_dev *dev; -+ -+ dev = in->my_dev; -+ -+ while (n > 0) { -+ yaffs_addr_to_chunk(dev, offset, &chunk, &start); -+ chunk++; -+ -+ /* OK now check for the curveball where the start and end are in -+ * the same chunk. -+ */ -+ if ((start + n) < dev->data_bytes_per_chunk) -+ n_copy = n; -+ else -+ n_copy = dev->data_bytes_per_chunk - start; -+ -+ cache = yaffs_find_chunk_cache(in, chunk); -+ -+ /* If the chunk is already in the cache or it is less than -+ * a whole chunk or we're using inband tags then use the cache -+ * (if there is caching) else bypass the cache. -+ */ -+ if (cache || n_copy != dev->data_bytes_per_chunk || -+ dev->param.inband_tags) { -+ if (dev->param.n_caches > 0) { -+ -+ /* If we can't find the data in the cache, -+ * then load it up. */ -+ -+ if (!cache) { -+ cache = -+ yaffs_grab_chunk_cache(in->my_dev); -+ cache->object = in; -+ cache->chunk_id = chunk; -+ cache->dirty = 0; -+ cache->locked = 0; -+ yaffs_rd_data_obj(in, chunk, -+ cache->data); -+ cache->n_bytes = 0; -+ } -+ -+ yaffs_use_cache(dev, cache, 0); -+ -+ cache->locked = 1; -+ -+ memcpy(buffer, &cache->data[start], n_copy); -+ -+ cache->locked = 0; -+ } else { -+ /* Read into the local buffer then copy.. */ -+ -+ u8 *local_buffer = -+ yaffs_get_temp_buffer(dev); -+ yaffs_rd_data_obj(in, chunk, local_buffer); -+ -+ memcpy(buffer, &local_buffer[start], n_copy); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ } else { -+ /* A full chunk. Read directly into the buffer. */ -+ yaffs_rd_data_obj(in, chunk, buffer); -+ } -+ n -= n_copy; -+ offset += n_copy; -+ buffer += n_copy; -+ n_done += n_copy; -+ } -+ return n_done; -+} -+ -+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_through) -+{ -+ -+ int chunk; -+ u32 start; -+ int n_copy; -+ int n = n_bytes; -+ int n_done = 0; -+ int n_writeback; -+ loff_t start_write = offset; -+ int chunk_written = 0; -+ u32 n_bytes_read; -+ loff_t chunk_start; -+ struct yaffs_dev *dev; -+ -+ dev = in->my_dev; -+ -+ while (n > 0 && chunk_written >= 0) { -+ yaffs_addr_to_chunk(dev, offset, &chunk, &start); -+ -+ if (((loff_t)chunk) * -+ dev->data_bytes_per_chunk + start != offset || -+ start >= dev->data_bytes_per_chunk) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "AddrToChunk of offset %lld gives chunk %d start %d", -+ offset, chunk, start); -+ } -+ chunk++; /* File pos to chunk in file offset */ -+ -+ /* OK now check for the curveball where the start and end are in -+ * the same chunk. -+ */ -+ -+ if ((start + n) < dev->data_bytes_per_chunk) { -+ n_copy = n; -+ -+ /* Now calculate how many bytes to write back.... -+ * If we're overwriting and not writing to then end of -+ * file then we need to write back as much as was there -+ * before. -+ */ -+ -+ chunk_start = (((loff_t)(chunk - 1)) * -+ dev->data_bytes_per_chunk); -+ -+ if (chunk_start > in->variant.file_variant.file_size) -+ n_bytes_read = 0; /* Past end of file */ -+ else -+ n_bytes_read = -+ in->variant.file_variant.file_size - -+ chunk_start; -+ -+ if (n_bytes_read > dev->data_bytes_per_chunk) -+ n_bytes_read = dev->data_bytes_per_chunk; -+ -+ n_writeback = -+ (n_bytes_read > -+ (start + n)) ? n_bytes_read : (start + n); -+ -+ if (n_writeback < 0 || -+ n_writeback > dev->data_bytes_per_chunk) -+ BUG(); -+ -+ } else { -+ n_copy = dev->data_bytes_per_chunk - start; -+ n_writeback = dev->data_bytes_per_chunk; -+ } -+ -+ if (n_copy != dev->data_bytes_per_chunk || -+ !dev->param.cache_bypass_aligned || -+ dev->param.inband_tags) { -+ /* An incomplete start or end chunk (or maybe both -+ * start and end chunk), or we're using inband tags, -+ * or we're forcing writes through the cache, -+ * so we want to use the cache buffers. -+ */ -+ if (dev->param.n_caches > 0) { -+ struct yaffs_cache *cache; -+ -+ /* If we can't find the data in the cache, then -+ * load the cache */ -+ cache = yaffs_find_chunk_cache(in, chunk); -+ -+ if (!cache && -+ yaffs_check_alloc_available(dev, 1)) { -+ cache = yaffs_grab_chunk_cache(dev); -+ cache->object = in; -+ cache->chunk_id = chunk; -+ cache->dirty = 0; -+ cache->locked = 0; -+ yaffs_rd_data_obj(in, chunk, -+ cache->data); -+ } else if (cache && -+ !cache->dirty && -+ !yaffs_check_alloc_available(dev, -+ 1)) { -+ /* Drop the cache if it was a read cache -+ * item and no space check has been made -+ * for it. -+ */ -+ cache = NULL; -+ } -+ -+ if (cache) { -+ yaffs_use_cache(dev, cache, 1); -+ cache->locked = 1; -+ -+ memcpy(&cache->data[start], buffer, -+ n_copy); -+ -+ cache->locked = 0; -+ cache->n_bytes = n_writeback; -+ -+ if (write_through) { -+ chunk_written = -+ yaffs_wr_data_obj -+ (cache->object, -+ cache->chunk_id, -+ cache->data, -+ cache->n_bytes, 1); -+ cache->dirty = 0; -+ } -+ } else { -+ chunk_written = -1; /* fail write */ -+ } -+ } else { -+ /* An incomplete start or end chunk (or maybe -+ * both start and end chunk). Read into the -+ * local buffer then copy over and write back. -+ */ -+ -+ u8 *local_buffer = yaffs_get_temp_buffer(dev); -+ -+ yaffs_rd_data_obj(in, chunk, local_buffer); -+ memcpy(&local_buffer[start], buffer, n_copy); -+ -+ chunk_written = -+ yaffs_wr_data_obj(in, chunk, -+ local_buffer, -+ n_writeback, 0); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ } else { -+ /* A full chunk. Write directly from the buffer. */ -+ -+ chunk_written = -+ yaffs_wr_data_obj(in, chunk, buffer, -+ dev->data_bytes_per_chunk, 0); -+ -+ /* Since we've overwritten the cached data, -+ * we better invalidate it. */ -+ yaffs_invalidate_chunk_cache(in, chunk); -+ } -+ -+ if (chunk_written >= 0) { -+ n -= n_copy; -+ offset += n_copy; -+ buffer += n_copy; -+ n_done += n_copy; -+ } -+ } -+ -+ /* Update file object */ -+ -+ if ((start_write + n_done) > in->variant.file_variant.file_size) -+ in->variant.file_variant.file_size = (start_write + n_done); -+ -+ in->dirty = 1; -+ return n_done; -+} -+ -+int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_through) -+{ -+ yaffs2_handle_hole(in, offset); -+ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); -+} -+ -+/* ---------------------- File resizing stuff ------------------ */ -+ -+static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) -+{ -+ -+ struct yaffs_dev *dev = in->my_dev; -+ loff_t old_size = in->variant.file_variant.file_size; -+ int i; -+ int chunk_id; -+ u32 dummy; -+ int last_del; -+ int start_del; -+ -+ if (old_size > 0) -+ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); -+ else -+ last_del = 0; -+ -+ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, -+ &start_del, &dummy); -+ last_del++; -+ start_del++; -+ -+ /* Delete backwards so that we don't end up with holes if -+ * power is lost part-way through the operation. -+ */ -+ for (i = last_del; i >= start_del; i--) { -+ /* NB this could be optimised somewhat, -+ * eg. could retrieve the tags and write them without -+ * using yaffs_chunk_del -+ */ -+ -+ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); -+ -+ if (chunk_id < 1) -+ continue; -+ -+ if (chunk_id < -+ (dev->internal_start_block * dev->param.chunks_per_block) || -+ chunk_id >= -+ ((dev->internal_end_block + 1) * -+ dev->param.chunks_per_block)) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Found daft chunk_id %d for %d", -+ chunk_id, i); -+ } else { -+ in->n_data_chunks--; -+ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); -+ } -+ } -+} -+ -+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) -+{ -+ int new_full; -+ u32 new_partial; -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); -+ -+ yaffs_prune_chunks(obj, new_size); -+ -+ if (new_partial != 0) { -+ int last_chunk = 1 + new_full; -+ u8 *local_buffer = yaffs_get_temp_buffer(dev); -+ -+ /* Rewrite the last chunk with its new size and zero pad */ -+ yaffs_rd_data_obj(obj, last_chunk, local_buffer); -+ memset(local_buffer + new_partial, 0, -+ dev->data_bytes_per_chunk - new_partial); -+ -+ yaffs_wr_data_obj(obj, last_chunk, local_buffer, -+ new_partial, 1); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ -+ obj->variant.file_variant.file_size = new_size; -+ -+ yaffs_prune_tree(dev, &obj->variant.file_variant); -+} -+ -+int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) -+{ -+ struct yaffs_dev *dev = in->my_dev; -+ loff_t old_size = in->variant.file_variant.file_size; -+ -+ yaffs_flush_file_cache(in, 1); -+ yaffs_invalidate_whole_cache(in); -+ -+ yaffs_check_gc(dev, 0); -+ -+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return YAFFS_FAIL; -+ -+ if (new_size == old_size) -+ return YAFFS_OK; -+ -+ if (new_size > old_size) { -+ yaffs2_handle_hole(in, new_size); -+ in->variant.file_variant.file_size = new_size; -+ } else { -+ /* new_size < old_size */ -+ yaffs_resize_file_down(in, new_size); -+ } -+ -+ /* Write a new object header to reflect the resize. -+ * show we've shrunk the file, if need be -+ * Do this only if the file is not in the deleted directories -+ * and is not shadowed. -+ */ -+ if (in->parent && -+ !in->is_shadowed && -+ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && -+ in->parent->obj_id != YAFFS_OBJECTID_DELETED) -+ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); -+ -+ return YAFFS_OK; -+} -+ -+int yaffs_flush_file(struct yaffs_obj *in, -+ int update_time, -+ int data_sync, -+ int discard_cache) -+{ -+ if (!in->dirty) -+ return YAFFS_OK; -+ -+ yaffs_flush_file_cache(in, discard_cache); -+ -+ if (data_sync) -+ return YAFFS_OK; -+ -+ if (update_time) -+ yaffs_load_current_time(in, 0, 0); -+ -+ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? -+ YAFFS_OK : YAFFS_FAIL; -+} -+ -+ -+/* yaffs_del_file deletes the whole file data -+ * and the inode associated with the file. -+ * It does not delete the links associated with the file. -+ */ -+static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) -+{ -+ int ret_val; -+ int del_now = 0; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!in->my_inode) -+ del_now = 1; -+ -+ if (del_now) { -+ ret_val = -+ yaffs_change_obj_name(in, in->my_dev->del_dir, -+ _Y("deleted"), 0, 0); -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: immediate deletion of file %d", -+ in->obj_id); -+ in->deleted = 1; -+ in->my_dev->n_deleted_files++; -+ if (dev->param.disable_soft_del || dev->param.is_yaffs2) -+ yaffs_resize_file(in, 0); -+ yaffs_soft_del_file(in); -+ } else { -+ ret_val = -+ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, -+ _Y("unlinked"), 0, 0); -+ } -+ return ret_val; -+} -+ -+static int yaffs_del_file(struct yaffs_obj *in) -+{ -+ int ret_val = YAFFS_OK; -+ int deleted; /* Need to cache value on stack if in is freed */ -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (dev->param.disable_soft_del || dev->param.is_yaffs2) -+ yaffs_resize_file(in, 0); -+ -+ if (in->n_data_chunks > 0) { -+ /* Use soft deletion if there is data in the file. -+ * That won't be the case if it has been resized to zero. -+ */ -+ if (!in->unlinked) -+ ret_val = yaffs_unlink_file_if_needed(in); -+ -+ deleted = in->deleted; -+ -+ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { -+ in->deleted = 1; -+ deleted = 1; -+ in->my_dev->n_deleted_files++; -+ yaffs_soft_del_file(in); -+ } -+ return deleted ? YAFFS_OK : YAFFS_FAIL; -+ } else { -+ /* The file has no data chunks so we toss it immediately */ -+ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); -+ in->variant.file_variant.top = NULL; -+ yaffs_generic_obj_del(in); -+ -+ return YAFFS_OK; -+ } -+} -+ -+int yaffs_is_non_empty_dir(struct yaffs_obj *obj) -+{ -+ return (obj && -+ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && -+ !(list_empty(&obj->variant.dir_variant.children)); -+} -+ -+static int yaffs_del_dir(struct yaffs_obj *obj) -+{ -+ /* First check that the directory is empty. */ -+ if (yaffs_is_non_empty_dir(obj)) -+ return YAFFS_FAIL; -+ -+ return yaffs_generic_obj_del(obj); -+} -+ -+static int yaffs_del_symlink(struct yaffs_obj *in) -+{ -+ kfree(in->variant.symlink_variant.alias); -+ in->variant.symlink_variant.alias = NULL; -+ -+ return yaffs_generic_obj_del(in); -+} -+ -+static int yaffs_del_link(struct yaffs_obj *in) -+{ -+ /* remove this hardlink from the list associated with the equivalent -+ * object -+ */ -+ list_del_init(&in->hard_links); -+ return yaffs_generic_obj_del(in); -+} -+ -+int yaffs_del_obj(struct yaffs_obj *obj) -+{ -+ int ret_val = -1; -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ ret_val = yaffs_del_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ if (!list_empty(&obj->variant.dir_variant.dirty)) { -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "Remove object %d from dirty directories", -+ obj->obj_id); -+ list_del_init(&obj->variant.dir_variant.dirty); -+ } -+ return yaffs_del_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ ret_val = yaffs_del_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ ret_val = yaffs_del_link(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ ret_val = yaffs_generic_obj_del(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ ret_val = 0; -+ break; /* should not happen. */ -+ } -+ return ret_val; -+} -+ -+ -+static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, -+ struct yaffs_obj *to_dir) -+{ -+ struct yaffs_obj *obj; -+ struct list_head *lh; -+ struct list_head *n; -+ -+ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ yaffs_add_obj_to_dir(to_dir, obj); -+ } -+} -+ -+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, -+ enum yaffs_obj_type type) -+{ -+ /* Tear down the old variant */ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ /* Nuke file data */ -+ yaffs_resize_file(obj, 0); -+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); -+ obj->variant.file_variant.top = NULL; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Put the children in lost and found. */ -+ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); -+ if (!list_empty(&obj->variant.dir_variant.dirty)) -+ list_del_init(&obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ /* Nuke symplink data */ -+ kfree(obj->variant.symlink_variant.alias); -+ obj->variant.symlink_variant.alias = NULL; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ list_del_init(&obj->hard_links); -+ break; -+ default: -+ break; -+ } -+ -+ memset(&obj->variant, 0, sizeof(obj->variant)); -+ -+ /*Set up new variant if the memset is not enough. */ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ INIT_LIST_HEAD(&obj->variant.dir_variant.children); -+ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ default: -+ break; -+ } -+ -+ obj->variant_type = type; -+ -+ return obj; -+ -+} -+ -+static int yaffs_unlink_worker(struct yaffs_obj *obj) -+{ -+ int del_now = 0; -+ -+ if (!obj) -+ return YAFFS_FAIL; -+ -+ if (!obj->my_inode) -+ del_now = 1; -+ -+ yaffs_update_parent(obj->parent); -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { -+ return yaffs_del_link(obj); -+ } else if (!list_empty(&obj->hard_links)) { -+ /* Curve ball: We're unlinking an object that has a hardlink. -+ * -+ * This problem arises because we are not strictly following -+ * The Linux link/inode model. -+ * -+ * We can't really delete the object. -+ * Instead, we do the following: -+ * - Select a hardlink. -+ * - Unhook it from the hard links -+ * - Move it from its parent directory so that the rename works. -+ * - Rename the object to the hardlink's name. -+ * - Delete the hardlink -+ */ -+ -+ struct yaffs_obj *hl; -+ struct yaffs_obj *parent; -+ int ret_val; -+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ hl = list_entry(obj->hard_links.next, struct yaffs_obj, -+ hard_links); -+ -+ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); -+ parent = hl->parent; -+ -+ list_del_init(&hl->hard_links); -+ -+ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); -+ -+ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); -+ -+ if (ret_val == YAFFS_OK) -+ ret_val = yaffs_generic_obj_del(hl); -+ -+ return ret_val; -+ -+ } else if (del_now) { -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return yaffs_del_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ list_del_init(&obj->variant.dir_variant.dirty); -+ return yaffs_del_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ return yaffs_del_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ return yaffs_generic_obj_del(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ default: -+ return YAFFS_FAIL; -+ } -+ } else if (yaffs_is_non_empty_dir(obj)) { -+ return YAFFS_FAIL; -+ } else { -+ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, -+ _Y("unlinked"), 0, 0); -+ } -+} -+ -+static int yaffs_unlink_obj(struct yaffs_obj *obj) -+{ -+ if (obj && obj->unlink_allowed) -+ return yaffs_unlink_worker(obj); -+ -+ return YAFFS_FAIL; -+} -+ -+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) -+{ -+ struct yaffs_obj *obj; -+ -+ obj = yaffs_find_by_name(dir, name); -+ return yaffs_unlink_obj(obj); -+} -+ -+/* Note: -+ * If old_name is NULL then we take old_dir as the object to be renamed. -+ */ -+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, -+ struct yaffs_obj *new_dir, const YCHAR *new_name) -+{ -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_obj *existing_target = NULL; -+ int force = 0; -+ int result; -+ struct yaffs_dev *dev; -+ -+ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ dev = old_dir->my_dev; -+ -+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE -+ /* Special case for case insemsitive systems. -+ * While look-up is case insensitive, the name isn't. -+ * Therefore we might want to change x.txt to X.txt -+ */ -+ if (old_dir == new_dir && -+ old_name && new_name && -+ strcmp(old_name, new_name) == 0) -+ force = 1; -+#endif -+ -+ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > -+ YAFFS_MAX_NAME_LENGTH) -+ /* ENAMETOOLONG */ -+ return YAFFS_FAIL; -+ -+ if (old_name) -+ obj = yaffs_find_by_name(old_dir, old_name); -+ else{ -+ obj = old_dir; -+ old_dir = obj->parent; -+ } -+ -+ if (obj && obj->rename_allowed) { -+ /* Now handle an existing target, if there is one */ -+ existing_target = yaffs_find_by_name(new_dir, new_name); -+ if (yaffs_is_non_empty_dir(existing_target)) { -+ return YAFFS_FAIL; /* ENOTEMPTY */ -+ } else if (existing_target && existing_target != obj) { -+ /* Nuke the target first, using shadowing, -+ * but only if it isn't the same object. -+ * -+ * Note we must disable gc here otherwise it can mess -+ * up the shadowing. -+ * -+ */ -+ dev->gc_disable = 1; -+ yaffs_change_obj_name(obj, new_dir, new_name, force, -+ existing_target->obj_id); -+ existing_target->is_shadowed = 1; -+ yaffs_unlink_obj(existing_target); -+ dev->gc_disable = 0; -+ } -+ -+ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); -+ -+ yaffs_update_parent(old_dir); -+ if (new_dir != old_dir) -+ yaffs_update_parent(new_dir); -+ -+ return result; -+ } -+ return YAFFS_FAIL; -+} -+ -+/*----------------------- Initialisation Scanning ---------------------- */ -+ -+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, -+ int backward_scanning) -+{ -+ struct yaffs_obj *obj; -+ -+ if (backward_scanning) { -+ /* Handle YAFFS2 case (backward scanning) -+ * If the shadowed object exists then ignore. -+ */ -+ obj = yaffs_find_by_number(dev, obj_id); -+ if (obj) -+ return; -+ } -+ -+ /* Let's create it (if it does not exist) assuming it is a file so that -+ * it can do shrinking etc. -+ * We put it in unlinked dir to be cleaned up after the scanning -+ */ -+ obj = -+ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); -+ if (!obj) -+ return; -+ obj->is_shadowed = 1; -+ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); -+ obj->variant.file_variant.shrink_size = 0; -+ obj->valid = 1; /* So that we don't read any other info. */ -+} -+ -+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) -+{ -+ struct list_head *lh; -+ struct list_head *save; -+ struct yaffs_obj *hl; -+ struct yaffs_obj *in; -+ -+ list_for_each_safe(lh, save, hard_list) { -+ hl = list_entry(lh, struct yaffs_obj, hard_links); -+ in = yaffs_find_by_number(dev, -+ hl->variant.hardlink_variant.equiv_id); -+ -+ if (in) { -+ /* Add the hardlink pointers */ -+ hl->variant.hardlink_variant.equiv_obj = in; -+ list_add(&hl->hard_links, &in->hard_links); -+ } else { -+ /* Todo Need to report/handle this better. -+ * Got a problem... hardlink to a non-existant object -+ */ -+ hl->variant.hardlink_variant.equiv_obj = NULL; -+ INIT_LIST_HEAD(&hl->hard_links); -+ } -+ } -+} -+ -+static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) -+{ -+ /* -+ * Sort out state of unlinked and deleted objects after scanning. -+ */ -+ struct list_head *i; -+ struct list_head *n; -+ struct yaffs_obj *l; -+ -+ if (dev->read_only) -+ return; -+ -+ /* Soft delete all the unlinked files */ -+ list_for_each_safe(i, n, -+ &dev->unlinked_dir->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ yaffs_del_obj(l); -+ } -+ -+ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ yaffs_del_obj(l); -+ } -+} -+ -+/* -+ * This code iterates through all the objects making sure that they are rooted. -+ * Any unrooted objects are re-rooted in lost+found. -+ * An object needs to be in one of: -+ * - Directly under deleted, unlinked -+ * - Directly or indirectly under root. -+ * -+ * Note: -+ * This code assumes that we don't ever change the current relationships -+ * between directories: -+ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL -+ * lost-n-found->parent == root_dir -+ * -+ * This fixes the problem where directories might have inadvertently been -+ * deleted leaving the object "hanging" without being rooted in the -+ * directory tree. -+ */ -+ -+static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) -+{ -+ return (obj == dev->del_dir || -+ obj == dev->unlinked_dir || obj == dev->root_dir); -+} -+ -+static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_obj *parent; -+ int i; -+ struct list_head *lh; -+ struct list_head *n; -+ int depth_limit; -+ int hanging; -+ -+ if (dev->read_only) -+ return; -+ -+ /* Iterate through the objects in each hash entry, -+ * looking at each object. -+ * Make sure it is rooted. -+ */ -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ parent = obj->parent; -+ -+ if (yaffs_has_null_parent(dev, obj)) { -+ /* These directories are not hanging */ -+ hanging = 0; -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ hanging = 1; -+ } else if (yaffs_has_null_parent(dev, parent)) { -+ hanging = 0; -+ } else { -+ /* -+ * Need to follow the parent chain to -+ * see if it is hanging. -+ */ -+ hanging = 0; -+ depth_limit = 100; -+ -+ while (parent != dev->root_dir && -+ parent->parent && -+ parent->parent->variant_type == -+ YAFFS_OBJECT_TYPE_DIRECTORY && -+ depth_limit > 0) { -+ parent = parent->parent; -+ depth_limit--; -+ } -+ if (parent != dev->root_dir) -+ hanging = 1; -+ } -+ if (hanging) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Hanging object %d moved to lost and found", -+ obj->obj_id); -+ yaffs_add_obj_to_dir(dev->lost_n_found, obj); -+ } -+ } -+ } -+} -+ -+/* -+ * Delete directory contents for cleaning up lost and found. -+ */ -+static void yaffs_del_dir_contents(struct yaffs_obj *dir) -+{ -+ struct yaffs_obj *obj; -+ struct list_head *lh; -+ struct list_head *n; -+ -+ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) -+ BUG(); -+ -+ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) -+ yaffs_del_dir_contents(obj); -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Deleting lost_found object %d", -+ obj->obj_id); -+ yaffs_unlink_obj(obj); -+ } -+} -+ -+static void yaffs_empty_l_n_f(struct yaffs_dev *dev) -+{ -+ yaffs_del_dir_contents(dev->lost_n_found); -+} -+ -+ -+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, -+ const YCHAR *name) -+{ -+ int sum; -+ struct list_head *i; -+ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; -+ struct yaffs_obj *l; -+ -+ if (!name) -+ return NULL; -+ -+ if (!directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_find_by_name: null pointer directory" -+ ); -+ BUG(); -+ return NULL; -+ } -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_find_by_name: non-directory" -+ ); -+ BUG(); -+ } -+ -+ sum = yaffs_calc_name_sum(name); -+ -+ list_for_each(i, &directory->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ -+ if (l->parent != directory) -+ BUG(); -+ -+ yaffs_check_obj_details_loaded(l); -+ -+ /* Special case for lost-n-found */ -+ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { -+ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) -+ return l; -+ } else if (l->sum == sum || l->hdr_chunk <= 0) { -+ /* LostnFound chunk called Objxxx -+ * Do a real check -+ */ -+ yaffs_get_obj_name(l, buffer, -+ YAFFS_MAX_NAME_LENGTH + 1); -+ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) -+ return l; -+ } -+ } -+ return NULL; -+} -+ -+/* GetEquivalentObject dereferences any hard links to get to the -+ * actual object. -+ */ -+ -+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) -+{ -+ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { -+ obj = obj->variant.hardlink_variant.equiv_obj; -+ yaffs_check_obj_details_loaded(obj); -+ } -+ return obj; -+} -+ -+/* -+ * A note or two on object names. -+ * * If the object name is missing, we then make one up in the form objnnn -+ * -+ * * ASCII names are stored in the object header's name field from byte zero -+ * * Unicode names are historically stored starting from byte zero. -+ * -+ * Then there are automatic Unicode names... -+ * The purpose of these is to save names in a way that can be read as -+ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII -+ * system to share files. -+ * -+ * These automatic unicode are stored slightly differently... -+ * - If the name can fit in the ASCII character space then they are saved as -+ * ascii names as per above. -+ * - If the name needs Unicode then the name is saved in Unicode -+ * starting at oh->name[1]. -+ -+ */ -+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, -+ int buffer_size) -+{ -+ /* Create an object name if we could not find one. */ -+ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { -+ YCHAR local_name[20]; -+ YCHAR num_string[20]; -+ YCHAR *x = &num_string[19]; -+ unsigned v = obj->obj_id; -+ num_string[19] = 0; -+ while (v > 0) { -+ x--; -+ *x = '0' + (v % 10); -+ v /= 10; -+ } -+ /* make up a name */ -+ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); -+ strcat(local_name, x); -+ strncpy(name, local_name, buffer_size - 1); -+ } -+} -+ -+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) -+{ -+ memset(name, 0, buffer_size * sizeof(YCHAR)); -+ yaffs_check_obj_details_loaded(obj); -+ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { -+ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); -+ } else if (obj->short_name[0]) { -+ strcpy(name, obj->short_name); -+ } else if (obj->hdr_chunk > 0) { -+ int result; -+ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); -+ -+ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; -+ -+ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); -+ -+ if (obj->hdr_chunk > 0) { -+ result = yaffs_rd_chunk_tags_nand(obj->my_dev, -+ obj->hdr_chunk, -+ buffer, NULL); -+ } -+ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, -+ buffer_size); -+ -+ yaffs_release_temp_buffer(obj->my_dev, buffer); -+ } -+ -+ yaffs_fix_null_name(obj, name, buffer_size); -+ -+ return strnlen(name, YAFFS_MAX_NAME_LENGTH); -+} -+ -+loff_t yaffs_get_obj_length(struct yaffs_obj *obj) -+{ -+ /* Dereference any hard linking */ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ return obj->variant.file_variant.file_size; -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ if (!obj->variant.symlink_variant.alias) -+ return 0; -+ return strnlen(obj->variant.symlink_variant.alias, -+ YAFFS_MAX_ALIAS_LENGTH); -+ } else { -+ /* Only a directory should drop through to here */ -+ return obj->my_dev->data_bytes_per_chunk; -+ } -+} -+ -+int yaffs_get_obj_link_count(struct yaffs_obj *obj) -+{ -+ int count = 0; -+ struct list_head *i; -+ -+ if (!obj->unlinked) -+ count++; /* the object itself */ -+ -+ list_for_each(i, &obj->hard_links) -+ count++; /* add the hard links; */ -+ -+ return count; -+} -+ -+int yaffs_get_obj_inode(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ return obj->obj_id; -+} -+ -+unsigned yaffs_get_obj_type(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return DT_REG; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ return DT_DIR; -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ return DT_LNK; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ return DT_REG; -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ if (S_ISFIFO(obj->yst_mode)) -+ return DT_FIFO; -+ if (S_ISCHR(obj->yst_mode)) -+ return DT_CHR; -+ if (S_ISBLK(obj->yst_mode)) -+ return DT_BLK; -+ if (S_ISSOCK(obj->yst_mode)) -+ return DT_SOCK; -+ return DT_REG; -+ break; -+ default: -+ return DT_REG; -+ break; -+ } -+} -+ -+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) -+ return yaffs_clone_str(obj->variant.symlink_variant.alias); -+ else -+ return yaffs_clone_str(_Y("")); -+} -+ -+/*--------------------------- Initialisation code -------------------------- */ -+ -+static int yaffs_check_dev_fns(struct yaffs_dev *dev) -+{ -+ struct yaffs_driver *drv = &dev->drv; -+ struct yaffs_tags_handler *tagger = &dev->tagger; -+ -+ /* Common functions, gotta have */ -+ if (!drv->drv_read_chunk_fn || -+ !drv->drv_write_chunk_fn || -+ !drv->drv_erase_fn) -+ return 0; -+ -+ if (dev->param.is_yaffs2 && -+ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) -+ return 0; -+ -+ /* Install the default tags marshalling functions if needed. */ -+ yaffs_tags_compat_install(dev); -+ yaffs_tags_marshall_install(dev); -+ -+ /* Check we now have the marshalling functions required. */ -+ if (!tagger->write_chunk_tags_fn || -+ !tagger->read_chunk_tags_fn || -+ !tagger->query_block_fn || -+ !tagger->mark_bad_fn) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs_create_initial_dir(struct yaffs_dev *dev) -+{ -+ /* Initialise the unlinked, deleted, root and lost+found directories */ -+ dev->lost_n_found = dev->root_dir = NULL; -+ dev->unlinked_dir = dev->del_dir = NULL; -+ dev->unlinked_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); -+ dev->del_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); -+ dev->root_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, -+ YAFFS_ROOT_MODE | S_IFDIR); -+ dev->lost_n_found = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, -+ YAFFS_LOSTNFOUND_MODE | S_IFDIR); -+ -+ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir -+ && dev->del_dir) { -+ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); -+ return YAFFS_OK; -+ } -+ return YAFFS_FAIL; -+} -+ -+/* Low level init. -+ * Typically only used by yaffs_guts_initialise, but also used by the -+ * Low level yaffs driver tests. -+ */ -+ -+int yaffs_guts_ll_init(struct yaffs_dev *dev) -+{ -+ -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); -+ -+ if (!dev) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Need a device" -+ ); -+ return YAFFS_FAIL; -+ } -+ -+ if (dev->ll_init) -+ return YAFFS_OK; -+ -+ dev->internal_start_block = dev->param.start_block; -+ dev->internal_end_block = dev->param.end_block; -+ dev->block_offset = 0; -+ dev->chunk_offset = 0; -+ dev->n_free_chunks = 0; -+ -+ dev->gc_block = 0; -+ -+ if (dev->param.start_block == 0) { -+ dev->internal_start_block = dev->param.start_block + 1; -+ dev->internal_end_block = dev->param.end_block + 1; -+ dev->block_offset = 1; -+ dev->chunk_offset = dev->param.chunks_per_block; -+ } -+ -+ /* Check geometry parameters. */ -+ -+ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && -+ dev->param.total_bytes_per_chunk < 1024) || -+ (!dev->param.is_yaffs2 && -+ dev->param.total_bytes_per_chunk < 512) || -+ (dev->param.inband_tags && !dev->param.is_yaffs2) || -+ dev->param.chunks_per_block < 2 || -+ dev->param.n_reserved_blocks < 2 || -+ dev->internal_start_block <= 0 || -+ dev->internal_end_block <= 0 || -+ dev->internal_end_block <= -+ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) -+ ) { -+ /* otherwise it is too small */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", -+ dev->param.total_bytes_per_chunk, -+ dev->param.is_yaffs2 ? "2" : "", -+ dev->param.inband_tags); -+ return YAFFS_FAIL; -+ } -+ -+ /* Sort out space for inband tags, if required */ -+ if (dev->param.inband_tags) -+ dev->data_bytes_per_chunk = -+ dev->param.total_bytes_per_chunk - -+ sizeof(struct yaffs_packed_tags2_tags_only); -+ else -+ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; -+ -+ /* Got the right mix of functions? */ -+ if (!yaffs_check_dev_fns(dev)) { -+ /* Function missing */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "device function(s) missing or wrong"); -+ -+ return YAFFS_FAIL; -+ } -+ -+ if (yaffs_init_nand(dev) != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); -+ return YAFFS_FAIL; -+ } -+ -+ return YAFFS_OK; -+} -+ -+ -+int yaffs_guts_format_dev(struct yaffs_dev *dev) -+{ -+ int i; -+ enum yaffs_block_state state; -+ u32 dummy; -+ -+ if(yaffs_guts_ll_init(dev) != YAFFS_OK) -+ return YAFFS_FAIL; -+ -+ if(dev->is_mounted) -+ return YAFFS_FAIL; -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ yaffs_query_init_block_state(dev, i, &state, &dummy); -+ if (state != YAFFS_BLOCK_STATE_DEAD) -+ yaffs_erase_block(dev, i); -+ } -+ -+ return YAFFS_OK; -+} -+ -+ -+int yaffs_guts_initialise(struct yaffs_dev *dev) -+{ -+ int init_failed = 0; -+ unsigned x; -+ int bits; -+ -+ if(yaffs_guts_ll_init(dev) != YAFFS_OK) -+ return YAFFS_FAIL; -+ -+ if (dev->is_mounted) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); -+ return YAFFS_FAIL; -+ } -+ -+ dev->is_mounted = 1; -+ -+ /* OK now calculate a few things for the device */ -+ -+ /* -+ * Calculate all the chunk size manipulation numbers: -+ */ -+ x = dev->data_bytes_per_chunk; -+ /* We always use dev->chunk_shift and dev->chunk_div */ -+ dev->chunk_shift = calc_shifts(x); -+ x >>= dev->chunk_shift; -+ dev->chunk_div = x; -+ /* We only use chunk mask if chunk_div is 1 */ -+ dev->chunk_mask = (1 << dev->chunk_shift) - 1; -+ -+ /* -+ * Calculate chunk_grp_bits. -+ * We need to find the next power of 2 > than internal_end_block -+ */ -+ -+ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); -+ -+ bits = calc_shifts_ceiling(x); -+ -+ /* Set up tnode width if wide tnodes are enabled. */ -+ if (!dev->param.wide_tnodes_disabled) { -+ /* bits must be even so that we end up with 32-bit words */ -+ if (bits & 1) -+ bits++; -+ if (bits < 16) -+ dev->tnode_width = 16; -+ else -+ dev->tnode_width = bits; -+ } else { -+ dev->tnode_width = 16; -+ } -+ -+ dev->tnode_mask = (1 << dev->tnode_width) - 1; -+ -+ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), -+ * so if the bitwidth of the -+ * chunk range we're using is greater than 16 we need -+ * to figure out chunk shift and chunk_grp_size -+ */ -+ -+ if (bits <= dev->tnode_width) -+ dev->chunk_grp_bits = 0; -+ else -+ dev->chunk_grp_bits = bits - dev->tnode_width; -+ -+ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; -+ if (dev->tnode_size < sizeof(struct yaffs_tnode)) -+ dev->tnode_size = sizeof(struct yaffs_tnode); -+ -+ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; -+ -+ if (dev->param.chunks_per_block < dev->chunk_grp_size) { -+ /* We have a problem because the soft delete won't work if -+ * the chunk group size > chunks per block. -+ * This can be remedied by using larger "virtual blocks". -+ */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); -+ -+ return YAFFS_FAIL; -+ } -+ -+ /* Finished verifying the device, continue with initialisation */ -+ -+ /* More device initialisation */ -+ dev->all_gcs = 0; -+ dev->passive_gc_count = 0; -+ dev->oldest_dirty_gc_count = 0; -+ dev->bg_gcs = 0; -+ dev->gc_block_finder = 0; -+ dev->buffered_block = -1; -+ dev->doing_buffered_block_rewrite = 0; -+ dev->n_deleted_files = 0; -+ dev->n_bg_deletions = 0; -+ dev->n_unlinked_files = 0; -+ dev->n_ecc_fixed = 0; -+ dev->n_ecc_unfixed = 0; -+ dev->n_tags_ecc_fixed = 0; -+ dev->n_tags_ecc_unfixed = 0; -+ dev->n_erase_failures = 0; -+ dev->n_erased_blocks = 0; -+ dev->gc_disable = 0; -+ dev->has_pending_prioritised_gc = 1; -+ /* Assume the worst for now, will get fixed on first GC */ -+ INIT_LIST_HEAD(&dev->dirty_dirs); -+ dev->oldest_dirty_seq = 0; -+ dev->oldest_dirty_block = 0; -+ -+ /* Initialise temporary buffers and caches. */ -+ if (!yaffs_init_tmp_buffers(dev)) -+ init_failed = 1; -+ -+ dev->cache = NULL; -+ dev->gc_cleanup_list = NULL; -+ -+ if (!init_failed && dev->param.n_caches > 0) { -+ int i; -+ void *buf; -+ int cache_bytes = -+ dev->param.n_caches * sizeof(struct yaffs_cache); -+ -+ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) -+ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; -+ -+ dev->cache = kmalloc(cache_bytes, GFP_NOFS); -+ -+ buf = (u8 *) dev->cache; -+ -+ if (dev->cache) -+ memset(dev->cache, 0, cache_bytes); -+ -+ for (i = 0; i < dev->param.n_caches && buf; i++) { -+ dev->cache[i].object = NULL; -+ dev->cache[i].last_use = 0; -+ dev->cache[i].dirty = 0; -+ dev->cache[i].data = buf = -+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ } -+ if (!buf) -+ init_failed = 1; -+ -+ dev->cache_last_use = 0; -+ } -+ -+ dev->cache_hits = 0; -+ -+ if (!init_failed) { -+ dev->gc_cleanup_list = -+ kmalloc(dev->param.chunks_per_block * sizeof(u32), -+ GFP_NOFS); -+ if (!dev->gc_cleanup_list) -+ init_failed = 1; -+ } -+ -+ if (dev->param.is_yaffs2) -+ dev->param.use_header_file_size = 1; -+ -+ if (!init_failed && !yaffs_init_blocks(dev)) -+ init_failed = 1; -+ -+ yaffs_init_tnodes_and_objs(dev); -+ -+ if (!init_failed && !yaffs_create_initial_dir(dev)) -+ init_failed = 1; -+ -+ if (!init_failed && dev->param.is_yaffs2 && -+ !dev->param.disable_summary && -+ !yaffs_summary_init(dev)) -+ init_failed = 1; -+ -+ if (!init_failed) { -+ /* Now scan the flash. */ -+ if (dev->param.is_yaffs2) { -+ if (yaffs2_checkpt_restore(dev)) { -+ yaffs_check_obj_details_loaded(dev->root_dir); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | -+ YAFFS_TRACE_MOUNT, -+ "yaffs: restored from checkpoint" -+ ); -+ } else { -+ -+ /* Clean up the mess caused by an aborted -+ * checkpoint load then scan backwards. -+ */ -+ yaffs_deinit_blocks(dev); -+ -+ yaffs_deinit_tnodes_and_objs(dev); -+ -+ dev->n_erased_blocks = 0; -+ dev->n_free_chunks = 0; -+ dev->alloc_block = -1; -+ dev->alloc_page = -1; -+ dev->n_deleted_files = 0; -+ dev->n_unlinked_files = 0; -+ dev->n_bg_deletions = 0; -+ -+ if (!init_failed && !yaffs_init_blocks(dev)) -+ init_failed = 1; -+ -+ yaffs_init_tnodes_and_objs(dev); -+ -+ if (!init_failed -+ && !yaffs_create_initial_dir(dev)) -+ init_failed = 1; -+ -+ if (!init_failed && !yaffs2_scan_backwards(dev)) -+ init_failed = 1; -+ } -+ } else if (!yaffs1_scan(dev)) { -+ init_failed = 1; -+ } -+ -+ yaffs_strip_deleted_objs(dev); -+ yaffs_fix_hanging_objs(dev); -+ if (dev->param.empty_lost_n_found) -+ yaffs_empty_l_n_f(dev); -+ } -+ -+ if (init_failed) { -+ /* Clean up the mess */ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: yaffs_guts_initialise() aborted."); -+ -+ yaffs_deinitialise(dev); -+ return YAFFS_FAIL; -+ } -+ -+ /* Zero out stats */ -+ dev->n_page_reads = 0; -+ dev->n_page_writes = 0; -+ dev->n_erasures = 0; -+ dev->n_gc_copies = 0; -+ dev->n_retried_writes = 0; -+ -+ dev->n_retired_blocks = 0; -+ -+ yaffs_verify_free_chunks(dev); -+ yaffs_verify_blocks(dev); -+ -+ /* Clean up any aborted checkpoint data */ -+ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) -+ yaffs2_checkpt_invalidate(dev); -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: yaffs_guts_initialise() done."); -+ return YAFFS_OK; -+} -+ -+void yaffs_deinitialise(struct yaffs_dev *dev) -+{ -+ if (dev->is_mounted) { -+ int i; -+ -+ yaffs_deinit_blocks(dev); -+ yaffs_deinit_tnodes_and_objs(dev); -+ yaffs_summary_deinit(dev); -+ -+ if (dev->param.n_caches > 0 && dev->cache) { -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ kfree(dev->cache[i].data); -+ dev->cache[i].data = NULL; -+ } -+ -+ kfree(dev->cache); -+ dev->cache = NULL; -+ } -+ -+ kfree(dev->gc_cleanup_list); -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ kfree(dev->temp_buffer[i].buffer); -+ dev->temp_buffer[i].buffer = NULL; -+ } -+ -+ kfree(dev->checkpt_buffer); -+ dev->checkpt_buffer = NULL; -+ kfree(dev->checkpt_block_list); -+ dev->checkpt_block_list = NULL; -+ -+ dev->is_mounted = 0; -+ -+ yaffs_deinit_nand(dev); -+ } -+} -+ -+int yaffs_count_free_chunks(struct yaffs_dev *dev) -+{ -+ int n_free = 0; -+ int b; -+ struct yaffs_block_info *blk; -+ -+ blk = dev->block_info; -+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { -+ switch (blk->block_state) { -+ case YAFFS_BLOCK_STATE_EMPTY: -+ case YAFFS_BLOCK_STATE_ALLOCATING: -+ case YAFFS_BLOCK_STATE_COLLECTING: -+ case YAFFS_BLOCK_STATE_FULL: -+ n_free += -+ (dev->param.chunks_per_block - blk->pages_in_use + -+ blk->soft_del_pages); -+ break; -+ default: -+ break; -+ } -+ blk++; -+ } -+ return n_free; -+} -+ -+int yaffs_get_n_free_chunks(struct yaffs_dev *dev) -+{ -+ /* This is what we report to the outside world */ -+ int n_free; -+ int n_dirty_caches; -+ int blocks_for_checkpt; -+ int i; -+ -+ n_free = dev->n_free_chunks; -+ n_free += dev->n_deleted_files; -+ -+ /* Now count and subtract the number of dirty chunks in the cache. */ -+ -+ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].dirty) -+ n_dirty_caches++; -+ } -+ -+ n_free -= n_dirty_caches; -+ -+ n_free -= -+ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); -+ -+ /* Now figure checkpoint space and report that... */ -+ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); -+ -+ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); -+ -+ if (n_free < 0) -+ n_free = 0; -+ -+ return n_free; -+} -+ -+ -+ -+/* -+ * Marshalling functions to get loff_t file sizes into and out of -+ * object headers. -+ */ -+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) -+{ -+ oh->file_size_low = (fsize & 0xFFFFFFFF); -+ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); -+} -+ -+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) -+{ -+ loff_t retval; -+ -+ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) -+ retval = (((loff_t) oh->file_size_high) << 32) | -+ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); -+ else -+ retval = (loff_t) oh->file_size_low; -+ -+ return retval; -+} -+ -+ -+void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) -+{ -+ int i; -+ struct yaffs_block_info *bi; -+ int s; -+ -+ for(i = 0; i < 10; i++) -+ bs[i] = 0; -+ -+ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ bi = yaffs_get_block_info(dev, i); -+ s = bi->block_state; -+ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) -+ bs[0]++; -+ else -+ bs[s]++; -+ } -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_guts.h linux-3.18.14/fs/yaffs2/yaffs_guts.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_guts.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,1010 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_GUTS_H__ -+#define __YAFFS_GUTS_H__ -+ -+#include "yportenv.h" -+ -+#define YAFFS_OK 1 -+#define YAFFS_FAIL 0 -+ -+/* Give us a Y=0x59, -+ * Give us an A=0x41, -+ * Give us an FF=0xff -+ * Give us an S=0x53 -+ * And what have we got... -+ */ -+#define YAFFS_MAGIC 0x5941ff53 -+ -+/* -+ * Tnodes form a tree with the tnodes in "levels" -+ * Levels greater than 0 hold 8 slots which point to other tnodes. -+ * Those at level 0 hold 16 slots which point to chunks in NAND. -+ * -+ * A maximum level of 8 thust supports files of size up to: -+ * -+ * 2^(3*MAX_LEVEL+4) -+ * -+ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives -+ * a maximum file size of around 512Gbytees with 2k chunks. -+ */ -+#define YAFFS_NTNODES_LEVEL0 16 -+#define YAFFS_TNODES_LEVEL0_BITS 4 -+#define YAFFS_TNODES_LEVEL0_MASK 0xf -+ -+#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) -+#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) -+#define YAFFS_TNODES_INTERNAL_MASK 0x7 -+#define YAFFS_TNODES_MAX_LEVEL 8 -+#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ -+ YAFFS_TNODES_INTERNAL_BITS * \ -+ YAFFS_TNODES_MAX_LEVEL) -+#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) -+ -+#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff -+ -+/* Constants for YAFFS1 mode */ -+#define YAFFS_BYTES_PER_SPARE 16 -+#define YAFFS_BYTES_PER_CHUNK 512 -+#define YAFFS_CHUNK_SIZE_SHIFT 9 -+#define YAFFS_CHUNKS_PER_BLOCK 32 -+#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) -+ -+#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 -+#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 -+ -+ -+ -+#define YAFFS_ALLOCATION_NOBJECTS 100 -+#define YAFFS_ALLOCATION_NTNODES 100 -+#define YAFFS_ALLOCATION_NLINKS 100 -+ -+#define YAFFS_NOBJECT_BUCKETS 256 -+ -+#define YAFFS_OBJECT_SPACE 0x40000 -+#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) -+ -+/* Binary data version stamps */ -+#define YAFFS_SUMMARY_VERSION 1 -+#define YAFFS_CHECKPOINT_VERSION 7 -+ -+#ifdef CONFIG_YAFFS_UNICODE -+#define YAFFS_MAX_NAME_LENGTH 127 -+#define YAFFS_MAX_ALIAS_LENGTH 79 -+#else -+#define YAFFS_MAX_NAME_LENGTH 255 -+#define YAFFS_MAX_ALIAS_LENGTH 159 -+#endif -+ -+#define YAFFS_SHORT_NAME_LENGTH 15 -+ -+/* Some special object ids for pseudo objects */ -+#define YAFFS_OBJECTID_ROOT 1 -+#define YAFFS_OBJECTID_LOSTNFOUND 2 -+#define YAFFS_OBJECTID_UNLINKED 3 -+#define YAFFS_OBJECTID_DELETED 4 -+ -+/* Fake object Id for summary data */ -+#define YAFFS_OBJECTID_SUMMARY 0x10 -+ -+/* Pseudo object ids for checkpointing */ -+#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 -+#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 -+ -+#define YAFFS_MAX_SHORT_OP_CACHES 20 -+ -+#define YAFFS_N_TEMP_BUFFERS 6 -+ -+/* We limit the number attempts at sucessfully saving a chunk of data. -+ * Small-page devices have 32 pages per block; large-page devices have 64. -+ * Default to something in the order of 5 to 10 blocks worth of chunks. -+ */ -+#define YAFFS_WR_ATTEMPTS (5*64) -+ -+/* Sequence numbers are used in YAFFS2 to determine block allocation order. -+ * The range is limited slightly to help distinguish bad numbers from good. -+ * This also allows us to perhaps in the future use special numbers for -+ * special purposes. -+ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, -+ * and is a larger number than the lifetime of a 2GB device. -+ */ -+#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 -+#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 -+ -+/* Special sequence number for bad block that failed to be marked bad */ -+#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 -+ -+/* ChunkCache is used for short read/write operations.*/ -+struct yaffs_cache { -+ struct yaffs_obj *object; -+ int chunk_id; -+ int last_use; -+ int dirty; -+ int n_bytes; /* Only valid if the cache is dirty */ -+ int locked; /* Can't push out or flush while locked. */ -+ u8 *data; -+}; -+ -+/* yaffs1 tags structures in RAM -+ * NB This uses bitfield. Bitfields should not straddle a u32 boundary -+ * otherwise the structure size will get blown out. -+ */ -+ -+struct yaffs_tags { -+ u32 chunk_id:20; -+ u32 serial_number:2; -+ u32 n_bytes_lsb:10; -+ u32 obj_id:18; -+ u32 ecc:12; -+ u32 n_bytes_msb:2; -+}; -+ -+union yaffs_tags_union { -+ struct yaffs_tags as_tags; -+ u8 as_bytes[8]; -+}; -+ -+ -+/* Stuff used for extended tags in YAFFS2 */ -+ -+enum yaffs_ecc_result { -+ YAFFS_ECC_RESULT_UNKNOWN, -+ YAFFS_ECC_RESULT_NO_ERROR, -+ YAFFS_ECC_RESULT_FIXED, -+ YAFFS_ECC_RESULT_UNFIXED -+}; -+ -+enum yaffs_obj_type { -+ YAFFS_OBJECT_TYPE_UNKNOWN, -+ YAFFS_OBJECT_TYPE_FILE, -+ YAFFS_OBJECT_TYPE_SYMLINK, -+ YAFFS_OBJECT_TYPE_DIRECTORY, -+ YAFFS_OBJECT_TYPE_HARDLINK, -+ YAFFS_OBJECT_TYPE_SPECIAL -+}; -+ -+#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL -+ -+struct yaffs_ext_tags { -+ unsigned chunk_used; /* Status of the chunk: used or unused */ -+ unsigned obj_id; /* If 0 this is not used */ -+ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ -+ unsigned n_bytes; /* Only valid for data chunks */ -+ -+ /* The following stuff only has meaning when we read */ -+ enum yaffs_ecc_result ecc_result; -+ unsigned block_bad; -+ -+ /* YAFFS 1 stuff */ -+ unsigned is_deleted; /* The chunk is marked deleted */ -+ unsigned serial_number; /* Yaffs1 2-bit serial number */ -+ -+ /* YAFFS2 stuff */ -+ unsigned seq_number; /* The sequence number of this block */ -+ -+ /* Extra info if this is an object header (YAFFS2 only) */ -+ -+ unsigned extra_available; /* Extra info available if not zero */ -+ unsigned extra_parent_id; /* The parent object */ -+ unsigned extra_is_shrink; /* Is it a shrink header? */ -+ unsigned extra_shadows; /* Does this shadow another object? */ -+ -+ enum yaffs_obj_type extra_obj_type; /* What object type? */ -+ -+ loff_t extra_file_size; /* Length if it is a file */ -+ unsigned extra_equiv_id; /* Equivalent object for a hard link */ -+}; -+ -+/* Spare structure for YAFFS1 */ -+struct yaffs_spare { -+ u8 tb0; -+ u8 tb1; -+ u8 tb2; -+ u8 tb3; -+ u8 page_status; /* set to 0 to delete the chunk */ -+ u8 block_status; -+ u8 tb4; -+ u8 tb5; -+ u8 ecc1[3]; -+ u8 tb6; -+ u8 tb7; -+ u8 ecc2[3]; -+}; -+ -+/*Special structure for passing through to mtd */ -+struct yaffs_nand_spare { -+ struct yaffs_spare spare; -+ int eccres1; -+ int eccres2; -+}; -+ -+/* Block data in RAM */ -+ -+enum yaffs_block_state { -+ YAFFS_BLOCK_STATE_UNKNOWN = 0, -+ -+ YAFFS_BLOCK_STATE_SCANNING, -+ /* Being scanned */ -+ -+ YAFFS_BLOCK_STATE_NEEDS_SCAN, -+ /* The block might have something on it (ie it is allocating or full, -+ * perhaps empty) but it needs to be scanned to determine its true -+ * state. -+ * This state is only valid during scanning. -+ * NB We tolerate empty because the pre-scanner might be incapable of -+ * deciding -+ * However, if this state is returned on a YAFFS2 device, -+ * then we expect a sequence number -+ */ -+ -+ YAFFS_BLOCK_STATE_EMPTY, -+ /* This block is empty */ -+ -+ YAFFS_BLOCK_STATE_ALLOCATING, -+ /* This block is partially allocated. -+ * At least one page holds valid data. -+ * This is the one currently being used for page -+ * allocation. Should never be more than one of these. -+ * If a block is only partially allocated at mount it is treated as -+ * full. -+ */ -+ -+ YAFFS_BLOCK_STATE_FULL, -+ /* All the pages in this block have been allocated. -+ * If a block was only partially allocated when mounted we treat -+ * it as fully allocated. -+ */ -+ -+ YAFFS_BLOCK_STATE_DIRTY, -+ /* The block was full and now all chunks have been deleted. -+ * Erase me, reuse me. -+ */ -+ -+ YAFFS_BLOCK_STATE_CHECKPOINT, -+ /* This block is assigned to holding checkpoint data. */ -+ -+ YAFFS_BLOCK_STATE_COLLECTING, -+ /* This block is being garbage collected */ -+ -+ YAFFS_BLOCK_STATE_DEAD -+ /* This block has failed and is not in use */ -+}; -+ -+#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) -+ -+struct yaffs_block_info { -+ -+ s32 soft_del_pages:10; /* number of soft deleted pages */ -+ s32 pages_in_use:10; /* number of pages in use */ -+ u32 block_state:4; /* One of the above block states. */ -+ /* NB use unsigned because enum is sometimes -+ * an int */ -+ u32 needs_retiring:1; /* Data has failed on this block, */ -+ /*need to get valid data off and retire*/ -+ u32 skip_erased_check:1;/* Skip the erased check on this block */ -+ u32 gc_prioritise:1; /* An ECC check or blank check has failed. -+ Block should be prioritised for GC */ -+ u32 chunk_error_strikes:3; /* How many times we've had ecc etc -+ failures on this block and tried to reuse it */ -+ u32 has_summary:1; /* The block has a summary */ -+ -+ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ -+ u32 seq_number; /* block sequence number for yaffs2 */ -+ -+}; -+ -+/* -------------------------- Object structure -------------------------------*/ -+/* This is the object structure as stored on NAND */ -+ -+struct yaffs_obj_hdr { -+ enum yaffs_obj_type type; -+ -+ /* Apply to everything */ -+ int parent_obj_id; -+ u16 sum_no_longer_used; /* checksum of name. No longer used */ -+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ /* The following apply to all object types except for hard links */ -+ u32 yst_mode; /* protection */ -+ -+ u32 yst_uid; -+ u32 yst_gid; -+ u32 yst_atime; -+ u32 yst_mtime; -+ u32 yst_ctime; -+ -+ /* File size applies to files only */ -+ u32 file_size_low; -+ -+ /* Equivalent object id applies to hard links only. */ -+ int equiv_id; -+ -+ /* Alias is for symlinks only. */ -+ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; -+ -+ u32 yst_rdev; /* stuff for block and char devices (major/min) */ -+ -+ u32 win_ctime[2]; -+ u32 win_atime[2]; -+ u32 win_mtime[2]; -+ -+ u32 inband_shadowed_obj_id; -+ u32 inband_is_shrink; -+ -+ u32 file_size_high; -+ u32 reserved[1]; -+ int shadows_obj; /* This object header shadows the -+ specified object if > 0 */ -+ -+ /* is_shrink applies to object headers written when wemake a hole. */ -+ u32 is_shrink; -+ -+}; -+ -+/*--------------------------- Tnode -------------------------- */ -+ -+struct yaffs_tnode { -+ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; -+}; -+ -+/*------------------------ Object -----------------------------*/ -+/* An object can be one of: -+ * - a directory (no data, has children links -+ * - a regular file (data.... not prunes :->). -+ * - a symlink [symbolic link] (the alias). -+ * - a hard link -+ */ -+ -+struct yaffs_file_var { -+ loff_t file_size; -+ loff_t scanned_size; -+ loff_t shrink_size; -+ int top_level; -+ struct yaffs_tnode *top; -+}; -+ -+struct yaffs_dir_var { -+ struct list_head children; /* list of child links */ -+ struct list_head dirty; /* Entry for list of dirty directories */ -+}; -+ -+struct yaffs_symlink_var { -+ YCHAR *alias; -+}; -+ -+struct yaffs_hardlink_var { -+ struct yaffs_obj *equiv_obj; -+ u32 equiv_id; -+}; -+ -+union yaffs_obj_var { -+ struct yaffs_file_var file_variant; -+ struct yaffs_dir_var dir_variant; -+ struct yaffs_symlink_var symlink_variant; -+ struct yaffs_hardlink_var hardlink_variant; -+}; -+ -+struct yaffs_obj { -+ u8 deleted:1; /* This should only apply to unlinked files. */ -+ u8 soft_del:1; /* it has also been soft deleted */ -+ u8 unlinked:1; /* An unlinked file.*/ -+ u8 fake:1; /* A fake object has no presence on NAND. */ -+ u8 rename_allowed:1; /* Some objects cannot be renamed. */ -+ u8 unlink_allowed:1; -+ u8 dirty:1; /* the object needs to be written to flash */ -+ u8 valid:1; /* When the file system is being loaded up, this -+ * object might be created before the data -+ * is available -+ * ie. file data chunks encountered before -+ * the header. -+ */ -+ u8 lazy_loaded:1; /* This object has been lazy loaded and -+ * is missing some detail */ -+ -+ u8 defered_free:1; /* Object is removed from NAND, but is -+ * still in the inode cache. -+ * Free of object is defered. -+ * until the inode is released. -+ */ -+ u8 being_created:1; /* This object is still being created -+ * so skip some verification checks. */ -+ u8 is_shadowed:1; /* This object is shadowed on the way -+ * to being renamed. */ -+ -+ u8 xattr_known:1; /* We know if this has object has xattribs -+ * or not. */ -+ u8 has_xattr:1; /* This object has xattribs. -+ * Only valid if xattr_known. */ -+ -+ u8 serial; /* serial number of chunk in NAND.*/ -+ u16 sum; /* sum of the name to speed searching */ -+ -+ struct yaffs_dev *my_dev; /* The device I'm on */ -+ -+ struct list_head hash_link; /* list of objects in hash bucket */ -+ -+ struct list_head hard_links; /* hard linked object chain*/ -+ -+ /* directory structure stuff */ -+ /* also used for linking up the free list */ -+ struct yaffs_obj *parent; -+ struct list_head siblings; -+ -+ /* Where's my object header in NAND? */ -+ int hdr_chunk; -+ -+ int n_data_chunks; /* Number of data chunks for this file. */ -+ -+ u32 obj_id; /* the object id value */ -+ -+ u32 yst_mode; -+ -+ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; -+ -+#ifdef CONFIG_YAFFS_WINCE -+ u32 win_ctime[2]; -+ u32 win_mtime[2]; -+ u32 win_atime[2]; -+#else -+ u32 yst_uid; -+ u32 yst_gid; -+ u32 yst_atime; -+ u32 yst_mtime; -+ u32 yst_ctime; -+#endif -+ -+ u32 yst_rdev; -+ -+ void *my_inode; -+ -+ enum yaffs_obj_type variant_type; -+ -+ union yaffs_obj_var variant; -+ -+}; -+ -+struct yaffs_obj_bucket { -+ struct list_head list; -+ int count; -+}; -+ -+/* yaffs_checkpt_obj holds the definition of an object as dumped -+ * by checkpointing. -+ */ -+ -+struct yaffs_checkpt_obj { -+ int struct_type; -+ u32 obj_id; -+ u32 parent_id; -+ int hdr_chunk; -+ enum yaffs_obj_type variant_type:3; -+ u8 deleted:1; -+ u8 soft_del:1; -+ u8 unlinked:1; -+ u8 fake:1; -+ u8 rename_allowed:1; -+ u8 unlink_allowed:1; -+ u8 serial; -+ int n_data_chunks; -+ loff_t size_or_equiv_obj; -+}; -+ -+/*--------------------- Temporary buffers ---------------- -+ * -+ * These are chunk-sized working buffers. Each device has a few. -+ */ -+ -+struct yaffs_buffer { -+ u8 *buffer; -+ int in_use; -+}; -+ -+/*----------------- Device ---------------------------------*/ -+ -+struct yaffs_param { -+ const YCHAR *name; -+ -+ /* -+ * Entry parameters set up way early. Yaffs sets up the rest. -+ * The structure should be zeroed out before use so that unused -+ * and default values are zero. -+ */ -+ -+ int inband_tags; /* Use unband tags */ -+ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to -+ be a power of 2 */ -+ int chunks_per_block; /* does not need to be a power of 2 */ -+ int spare_bytes_per_chunk; /* spare area size */ -+ int start_block; /* Start block we're allowed to use */ -+ int end_block; /* End block we're allowed to use */ -+ int n_reserved_blocks; /* Tuneable so that we can reduce -+ * reserved blocks on NOR and RAM. */ -+ -+ int n_caches; /* If <= 0, then short op caching is disabled, -+ * else the number of short op caches. -+ */ -+ int cache_bypass_aligned; /* If non-zero then bypass the cache for -+ * aligned writes. -+ */ -+ -+ int use_nand_ecc; /* Flag to decide whether or not to use -+ * NAND driver ECC on data (yaffs1) */ -+ int tags_9bytes; /* Use 9 byte tags */ -+ int no_tags_ecc; /* Flag to decide whether or not to do ECC -+ * on packed tags (yaffs2) */ -+ -+ int is_yaffs2; /* Use yaffs2 mode on this device */ -+ -+ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ -+ -+ int refresh_period; /* How often to check for a block refresh */ -+ -+ /* Checkpoint control. Can be set before or after initialisation */ -+ u8 skip_checkpt_rd; -+ u8 skip_checkpt_wr; -+ -+ int enable_xattr; /* Enable xattribs */ -+ -+ int max_objects; /* -+ * Set to limit the number of objects created. -+ * 0 = no limit. -+ */ -+ -+ /* The remove_obj_fn function must be supplied by OS flavours that -+ * need it. -+ * yaffs direct uses it to implement the faster readdir. -+ * Linux uses it to protect the directory during unlocking. -+ */ -+ void (*remove_obj_fn) (struct yaffs_obj *obj); -+ -+ /* Callback to mark the superblock dirty */ -+ void (*sb_dirty_fn) (struct yaffs_dev *dev); -+ -+ /* Callback to control garbage collection. */ -+ unsigned (*gc_control_fn) (struct yaffs_dev *dev); -+ -+ /* Debug control flags. Don't use unless you know what you're doing */ -+ int use_header_file_size; /* Flag to determine if we should use -+ * file sizes from the header */ -+ int disable_lazy_load; /* Disable lazy loading on this device */ -+ int wide_tnodes_disabled; /* Set to disable wide tnodes */ -+ int disable_soft_del; /* yaffs 1 only: Set to disable the use of -+ * softdeletion. */ -+ -+ int defered_dir_update; /* Set to defer directory updates */ -+ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ int auto_unicode; -+#endif -+ int always_check_erased; /* Force chunk erased check always on */ -+ -+ int disable_summary; -+ int disable_bad_block_marking; -+ -+}; -+ -+struct yaffs_driver { -+ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, int data_len, -+ const u8 *oob, int oob_len); -+ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, -+ u8 *data, int data_len, -+ u8 *oob, int oob_len, -+ enum yaffs_ecc_result *ecc_result); -+ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_initialise_fn) (struct yaffs_dev *dev); -+ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); -+}; -+ -+struct yaffs_tags_handler { -+ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ const struct yaffs_ext_tags *tags); -+ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, -+ int nand_chunk, u8 *data, -+ struct yaffs_ext_tags *tags); -+ -+ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number); -+ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); -+}; -+ -+struct yaffs_dev { -+ struct yaffs_param param; -+ struct yaffs_driver drv; -+ struct yaffs_tags_handler tagger; -+ -+ /* Context storage. Holds extra OS specific data for this device */ -+ -+ void *os_context; -+ void *driver_context; -+ -+ struct list_head dev_list; -+ -+ int ll_init; -+ /* Runtime parameters. Set up by YAFFS. */ -+ int data_bytes_per_chunk; -+ -+ /* Non-wide tnode stuff */ -+ u16 chunk_grp_bits; /* Number of bits that need to be resolved if -+ * the tnodes are not wide enough. -+ */ -+ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ -+ -+ /* Stuff to support wide tnodes */ -+ u32 tnode_width; -+ u32 tnode_mask; -+ u32 tnode_size; -+ -+ /* Stuff for figuring out file offset to chunk conversions */ -+ u32 chunk_shift; /* Shift value */ -+ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ -+ u32 chunk_mask; /* Mask to use for power-of-2 case */ -+ -+ int is_mounted; -+ int read_only; -+ int is_checkpointed; -+ -+ /* Stuff to support block offsetting to support start block zero */ -+ int internal_start_block; -+ int internal_end_block; -+ int block_offset; -+ int chunk_offset; -+ -+ /* Runtime checkpointing stuff */ -+ int checkpt_page_seq; /* running sequence number of checkpt pages */ -+ int checkpt_byte_count; -+ int checkpt_byte_offs; -+ u8 *checkpt_buffer; -+ int checkpt_open_write; -+ int blocks_in_checkpt; -+ int checkpt_cur_chunk; -+ int checkpt_cur_block; -+ int checkpt_next_block; -+ int *checkpt_block_list; -+ int checkpt_max_blocks; -+ u32 checkpt_sum; -+ u32 checkpt_xor; -+ -+ int checkpoint_blocks_required; /* Number of blocks needed to store -+ * current checkpoint set */ -+ -+ /* Block Info */ -+ struct yaffs_block_info *block_info; -+ u8 *chunk_bits; /* bitmap of chunks in use */ -+ u8 block_info_alt:1; /* allocated using alternative alloc */ -+ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ -+ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. -+ * Must be consistent with chunks_per_block. -+ */ -+ -+ int n_erased_blocks; -+ int alloc_block; /* Current block being allocated off */ -+ u32 alloc_page; -+ int alloc_block_finder; /* Used to search for next allocation block */ -+ -+ /* Object and Tnode memory management */ -+ void *allocator; -+ int n_obj; -+ int n_tnodes; -+ -+ int n_hardlinks; -+ -+ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; -+ u32 bucket_finder; -+ -+ int n_free_chunks; -+ -+ /* Garbage collection control */ -+ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ -+ u32 n_clean_ups; -+ -+ unsigned has_pending_prioritised_gc; /* We think this device might -+ have pending prioritised gcs */ -+ unsigned gc_disable; -+ unsigned gc_block_finder; -+ unsigned gc_dirtiest; -+ unsigned gc_pages_in_use; -+ unsigned gc_not_done; -+ unsigned gc_block; -+ unsigned gc_chunk; -+ unsigned gc_skip; -+ struct yaffs_summary_tags *gc_sum_tags; -+ -+ /* Special directories */ -+ struct yaffs_obj *root_dir; -+ struct yaffs_obj *lost_n_found; -+ -+ int buffered_block; /* Which block is buffered here? */ -+ int doing_buffered_block_rewrite; -+ -+ struct yaffs_cache *cache; -+ int cache_last_use; -+ -+ /* Stuff for background deletion and unlinked files. */ -+ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted -+ files live. */ -+ struct yaffs_obj *del_dir; /* Directory where deleted objects are -+ sent to disappear. */ -+ struct yaffs_obj *unlinked_deletion; /* Current file being -+ background deleted. */ -+ int n_deleted_files; /* Count of files awaiting deletion; */ -+ int n_unlinked_files; /* Count of unlinked files. */ -+ int n_bg_deletions; /* Count of background deletions. */ -+ -+ /* Temporary buffer management */ -+ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; -+ int max_temp; -+ int temp_in_use; -+ int unmanaged_buffer_allocs; -+ int unmanaged_buffer_deallocs; -+ -+ /* yaffs2 runtime stuff */ -+ unsigned seq_number; /* Sequence number of currently -+ allocating block */ -+ unsigned oldest_dirty_seq; -+ unsigned oldest_dirty_block; -+ -+ /* Block refreshing */ -+ int refresh_skip; /* A skip down counter. -+ * Refresh happens when this gets to zero. */ -+ -+ /* Dirty directory handling */ -+ struct list_head dirty_dirs; /* List of dirty directories */ -+ -+ /* Summary */ -+ int chunks_per_summary; -+ struct yaffs_summary_tags *sum_tags; -+ -+ /* Statistics */ -+ u32 n_page_writes; -+ u32 n_page_reads; -+ u32 n_erasures; -+ u32 n_bad_queries; -+ u32 n_bad_markings; -+ u32 n_erase_failures; -+ u32 n_gc_copies; -+ u32 all_gcs; -+ u32 passive_gc_count; -+ u32 oldest_dirty_gc_count; -+ u32 n_gc_blocks; -+ u32 bg_gcs; -+ u32 n_retried_writes; -+ u32 n_retired_blocks; -+ u32 n_ecc_fixed; -+ u32 n_ecc_unfixed; -+ u32 n_tags_ecc_fixed; -+ u32 n_tags_ecc_unfixed; -+ u32 n_deletions; -+ u32 n_unmarked_deletions; -+ u32 refresh_count; -+ u32 cache_hits; -+ u32 tags_used; -+ u32 summary_used; -+ -+}; -+ -+/* The CheckpointDevice structure holds the device information that changes -+ *at runtime and must be preserved over unmount/mount cycles. -+ */ -+struct yaffs_checkpt_dev { -+ int struct_type; -+ int n_erased_blocks; -+ int alloc_block; /* Current block being allocated off */ -+ u32 alloc_page; -+ int n_free_chunks; -+ -+ int n_deleted_files; /* Count of files awaiting deletion; */ -+ int n_unlinked_files; /* Count of unlinked files. */ -+ int n_bg_deletions; /* Count of background deletions. */ -+ -+ /* yaffs2 runtime stuff */ -+ unsigned seq_number; /* Sequence number of currently -+ * allocating block */ -+ -+}; -+ -+struct yaffs_checkpt_validity { -+ int struct_type; -+ u32 magic; -+ u32 version; -+ u32 head; -+}; -+ -+struct yaffs_shadow_fixer { -+ int obj_id; -+ int shadowed_id; -+ struct yaffs_shadow_fixer *next; -+}; -+ -+/* Structure for doing xattr modifications */ -+struct yaffs_xattr_mod { -+ int set; /* If 0 then this is a deletion */ -+ const YCHAR *name; -+ const void *data; -+ int size; -+ int flags; -+ int result; -+}; -+ -+/*----------------------- YAFFS Functions -----------------------*/ -+ -+int yaffs_guts_initialise(struct yaffs_dev *dev); -+void yaffs_deinitialise(struct yaffs_dev *dev); -+ -+int yaffs_get_n_free_chunks(struct yaffs_dev *dev); -+ -+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, -+ struct yaffs_obj *new_dir, const YCHAR * new_name); -+ -+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); -+int yaffs_del_obj(struct yaffs_obj *obj); -+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, -+ enum yaffs_obj_type type); -+ -+ -+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); -+loff_t yaffs_get_obj_length(struct yaffs_obj *obj); -+int yaffs_get_obj_inode(struct yaffs_obj *obj); -+unsigned yaffs_get_obj_type(struct yaffs_obj *obj); -+int yaffs_get_obj_link_count(struct yaffs_obj *obj); -+ -+/* File operations */ -+int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, -+ int n_bytes); -+int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, -+ int n_bytes, int write_trhrough); -+int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); -+ -+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid); -+ -+int yaffs_flush_file(struct yaffs_obj *in, -+ int update_time, -+ int data_sync, -+ int discard_cache); -+ -+/* Flushing and checkpointing */ -+void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard); -+ -+int yaffs_checkpoint_save(struct yaffs_dev *dev); -+int yaffs_checkpoint_restore(struct yaffs_dev *dev); -+ -+/* Directory operations */ -+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, -+ u32 mode, u32 uid, u32 gid); -+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, -+ const YCHAR *name); -+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); -+ -+/* Link operations */ -+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, -+ struct yaffs_obj *equiv_obj); -+ -+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); -+ -+/* Symlink operations */ -+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, const YCHAR *alias); -+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); -+ -+/* Special inodes (fifos, sockets and devices) */ -+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, u32 rdev); -+ -+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, -+ const void *value, int size, int flags); -+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, -+ int size); -+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); -+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); -+ -+/* Special directories */ -+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); -+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); -+ -+void yaffs_handle_defered_free(struct yaffs_obj *obj); -+ -+void yaffs_update_dirty_dirs(struct yaffs_dev *dev); -+ -+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); -+ -+/* Debug dump */ -+int yaffs_dump_obj(struct yaffs_obj *obj); -+ -+void yaffs_guts_test(struct yaffs_dev *dev); -+int yaffs_guts_ll_init(struct yaffs_dev *dev); -+ -+ -+/* A few useful functions to be used within the core files*/ -+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, -+ int lyn); -+int yaffs_check_ff(u8 *buffer, int n_bytes); -+void yaffs_handle_chunk_error(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi); -+ -+u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); -+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); -+ -+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, -+ int number, -+ enum yaffs_obj_type type); -+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ int nand_chunk, int in_scan); -+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); -+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, -+ const struct yaffs_obj_hdr *oh); -+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); -+YCHAR *yaffs_clone_str(const YCHAR *str); -+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); -+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); -+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, -+ int force, int is_shrink, int shadows, -+ struct yaffs_xattr_mod *xop); -+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, -+ int backward_scanning); -+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); -+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); -+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id, -+ struct yaffs_tnode *passed_tn); -+ -+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_trhrough); -+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); -+void yaffs_skip_rest_of_block(struct yaffs_dev *dev); -+ -+int yaffs_count_free_chunks(struct yaffs_dev *dev); -+ -+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id); -+ -+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos); -+ -+int yaffs_is_non_empty_dir(struct yaffs_obj *obj); -+ -+int yaffs_guts_format_dev(struct yaffs_dev *dev); -+ -+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, -+ int *chunk_out, u32 *offset_out); -+/* -+ * Marshalling functions to get loff_t file sizes into aand out of -+ * object headers. -+ */ -+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); -+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); -+loff_t yaffs_max_file_size(struct yaffs_dev *dev); -+ -+/* -+ * Debug function to count number of blocks in each state -+ * NB Needs to be called with correct number of integers -+ */ -+ -+void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); -+ -+int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_linux.h linux-3.18.14/fs/yaffs2/yaffs_linux.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_linux.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,48 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_LINUX_H__ -+#define __YAFFS_LINUX_H__ -+ -+#include "yportenv.h" -+ -+struct yaffs_linux_context { -+ struct list_head context_list; /* List of these we have mounted */ -+ struct yaffs_dev *dev; -+ struct super_block *super; -+ struct task_struct *bg_thread; /* Background thread for this device */ -+ int bg_running; -+ struct mutex gross_lock; /* Gross locking mutex*/ -+ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size -+ * at compile time so we have to allocate it. -+ */ -+ struct list_head search_contexts; -+ struct task_struct *readdir_process; -+ unsigned mount_id; -+ int dirty; -+}; -+ -+#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) -+#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+#define WRITE_SIZE_STR "writesize" -+#define WRITE_SIZE(mtd) ((mtd)->writesize) -+#else -+#define WRITE_SIZE_STR "oobblock" -+#define WRITE_SIZE(mtd) ((mtd)->oobblock) -+#endif -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_mtdif.c linux-3.18.14/fs/yaffs2/yaffs_mtdif.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_mtdif.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,310 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yportenv.h" -+ -+#include "yaffs_mtdif.h" -+ -+#include "linux/mtd/mtd.h" -+#include "linux/types.h" -+#include "linux/time.h" -+#include "linux/mtd/nand.h" -+#include "linux/kernel.h" -+#include "linux/version.h" -+#include "linux/types.h" -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) -+#include "uapi/linux/major.h" -+#endif -+ -+#include "yaffs_trace.h" -+#include "yaffs_guts.h" -+#include "yaffs_linux.h" -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) -+#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) -+#define mtd_erase(m, ei) (m)->erase(m, ei) -+#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) -+#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) -+#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) -+#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) -+#endif -+ -+ -+ -+int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ u32 addr = -+ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * -+ dev->param.chunks_per_block; -+ struct erase_info ei; -+ int retval = 0; -+ -+ ei.mtd = mtd; -+ ei.addr = addr; -+ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; -+ ei.time = 1000; -+ ei.retries = 2; -+ ei.callback = NULL; -+ ei.priv = (u_long) dev; -+ -+ retval = mtd_erase(mtd, &ei); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+ -+static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, int data_len, -+ const u8 *oob, int oob_len) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ loff_t addr; -+ struct mtd_oob_ops ops; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", -+ dev, nand_chunk, data, data_len, oob, oob_len); -+ -+ if (!data || !data_len) { -+ data = NULL; -+ data_len = 0; -+ } -+ -+ if (!oob || !oob_len) { -+ oob = NULL; -+ oob_len = 0; -+ } -+ -+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; -+ memset(&ops, 0, sizeof(ops)); -+ ops.mode = MTD_OPS_AUTO_OOB; -+ ops.len = (data) ? data_len : 0; -+ ops.ooblen = oob_len; -+ ops.datbuf = (u8 *)data; -+ ops.oobbuf = (u8 *)oob; -+ -+ retval = mtd_write_oob(mtd, addr, &ops); -+ if (retval) { -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "write_oob failed, chunk %d, mtd error %d", -+ nand_chunk, retval); -+ } -+ return retval ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, -+ u8 *data, int data_len, -+ u8 *oob, int oob_len, -+ enum yaffs_ecc_result *ecc_result) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ loff_t addr; -+ struct mtd_oob_ops ops; -+ int retval; -+ -+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; -+ memset(&ops, 0, sizeof(ops)); -+ ops.mode = MTD_OPS_AUTO_OOB; -+ ops.len = (data) ? data_len : 0; -+ ops.ooblen = oob_len; -+ ops.datbuf = data; -+ ops.oobbuf = oob; -+ -+#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) -+ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; -+ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. -+ */ -+ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; -+#endif -+ /* Read page and oob using MTD. -+ * Check status and determine ECC result. -+ */ -+ retval = mtd_read_oob(mtd, addr, &ops); -+ if (retval) -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "read_oob failed, chunk %d, mtd error %d", -+ nand_chunk, retval); -+ -+ switch (retval) { -+ case 0: -+ /* no error */ -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ break; -+ -+ case -EUCLEAN: -+ /* MTD's ECC fixed the data */ -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_FIXED; -+ dev->n_ecc_fixed++; -+ break; -+ -+ case -EBADMSG: -+ default: -+ /* MTD's ECC could not fix the data */ -+ dev->n_ecc_unfixed++; -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ return YAFFS_FAIL; -+ } -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ -+ loff_t addr; -+ struct erase_info ei; -+ int retval = 0; -+ u32 block_size; -+ -+ block_size = dev->param.total_bytes_per_chunk * -+ dev->param.chunks_per_block; -+ addr = ((loff_t) block_no) * block_size; -+ -+ ei.mtd = mtd; -+ ei.addr = addr; -+ ei.len = block_size; -+ ei.time = 1000; -+ ei.retries = 2; -+ ei.callback = NULL; -+ ei.priv = (u_long) dev; -+ -+ retval = mtd_erase(mtd, &ei); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); -+ -+ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); -+ return (retval) ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); -+ -+ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); -+ return (retval) ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_initialise(struct yaffs_dev *dev) -+{ -+ return YAFFS_OK; -+} -+ -+static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) -+{ -+ return YAFFS_OK; -+} -+ -+ -+void yaffs_mtd_drv_install(struct yaffs_dev *dev) -+{ -+ struct yaffs_driver *drv = &dev->drv; -+ -+ drv->drv_write_chunk_fn = yaffs_mtd_write; -+ drv->drv_read_chunk_fn = yaffs_mtd_read; -+ drv->drv_erase_fn = yaffs_mtd_erase; -+ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; -+ drv->drv_check_bad_fn = yaffs_mtd_check_bad; -+ drv->drv_initialise_fn = yaffs_mtd_initialise; -+ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; -+} -+ -+ -+struct mtd_info * yaffs_get_mtd_device(dev_t sdev) -+{ -+ struct mtd_info *mtd; -+ -+ mtd = yaffs_get_mtd_device(sdev); -+ -+ /* Check it's an mtd device..... */ -+ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) -+ return NULL; /* This isn't an mtd device */ -+ -+ /* Check it's NAND */ -+ if (mtd->type != MTD_NANDFLASH) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: MTD device is not NAND it's type %d", -+ mtd->type); -+ return NULL; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); -+ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); -+ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -+ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); -+#else -+ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); -+#endif -+ -+ return mtd; -+} -+ -+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) -+{ -+ if (yaffs_version == 2) { -+ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || -+ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && -+ !inband_tags) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device does not have the right page sizes" -+ ); -+ return -1; -+ } -+ } else { -+ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || -+ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device does not support have the right page sizes" -+ ); -+ return -1; -+ } -+ } -+ -+ return 0; -+} -+ -+ -+void yaffs_put_mtd_device(struct mtd_info *mtd) -+{ -+ if(mtd) -+ put_mtd_device(mtd); -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_mtdif.h linux-3.18.14/fs/yaffs2/yaffs_mtdif.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_mtdif.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,25 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_MTDIF_H__ -+#define __YAFFS_MTDIF_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_mtd_drv_install(struct yaffs_dev *dev); -+struct mtd_info * yaffs_get_mtd_device(dev_t sdev); -+void yaffs_put_mtd_device(struct mtd_info *mtd); -+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_nameval.c linux-3.18.14/fs/yaffs2/yaffs_nameval.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_nameval.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,208 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * This simple implementation of a name-value store assumes a small number of -+* values and fits into a small finite buffer. -+ * -+ * Each attribute is stored as a record: -+ * sizeof(int) bytes record size. -+ * strnlen+1 bytes name null terminated. -+ * nbytes value. -+ * ---------- -+ * total size stored in record size -+ * -+ * This code has not been tested with unicode yet. -+ */ -+ -+#include "yaffs_nameval.h" -+ -+#include "yportenv.h" -+ -+static int nval_find(const char *xb, int xb_size, const YCHAR *name, -+ int *exist_size) -+{ -+ int pos = 0; -+ int size; -+ -+ memcpy(&size, xb, sizeof(int)); -+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { -+ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), -+ name, size)) { -+ if (exist_size) -+ *exist_size = size; -+ return pos; -+ } -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ if (exist_size) -+ *exist_size = 0; -+ return -ENODATA; -+} -+ -+static int nval_used(const char *xb, int xb_size) -+{ -+ int pos = 0; -+ int size; -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ return pos; -+} -+ -+int nval_del(char *xb, int xb_size, const YCHAR *name) -+{ -+ int pos = nval_find(xb, xb_size, name, NULL); -+ int size; -+ -+ if (pos < 0 || pos >= xb_size) -+ return -ENODATA; -+ -+ /* Find size, shift rest over this record, -+ * then zero out the rest of buffer */ -+ memcpy(&size, xb + pos, sizeof(int)); -+ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); -+ memset(xb + (xb_size - size), 0, size); -+ return 0; -+} -+ -+int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, -+ int bsize, int flags) -+{ -+ int pos; -+ int namelen = strnlen(name, xb_size); -+ int reclen; -+ int size_exist = 0; -+ int space; -+ int start; -+ -+ pos = nval_find(xb, xb_size, name, &size_exist); -+ -+ if (flags & XATTR_CREATE && pos >= 0) -+ return -EEXIST; -+ if (flags & XATTR_REPLACE && pos < 0) -+ return -ENODATA; -+ -+ start = nval_used(xb, xb_size); -+ space = xb_size - start + size_exist; -+ -+ reclen = (sizeof(int) + namelen + 1 + bsize); -+ -+ if (reclen > space) -+ return -ENOSPC; -+ -+ if (pos >= 0) { -+ nval_del(xb, xb_size, name); -+ start = nval_used(xb, xb_size); -+ } -+ -+ pos = start; -+ -+ memcpy(xb + pos, &reclen, sizeof(int)); -+ pos += sizeof(int); -+ strncpy((YCHAR *) (xb + pos), name, reclen); -+ pos += (namelen + 1); -+ memcpy(xb + pos, buf, bsize); -+ return 0; -+} -+ -+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, -+ int bsize) -+{ -+ int pos = nval_find(xb, xb_size, name, NULL); -+ int size; -+ -+ if (pos >= 0 && pos < xb_size) { -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ pos += sizeof(int); /* advance past record length */ -+ size -= sizeof(int); -+ -+ /* Advance over name string */ -+ while (xb[pos] && size > 0 && pos < xb_size) { -+ pos++; -+ size--; -+ } -+ /*Advance over NUL */ -+ pos++; -+ size--; -+ -+ /* If bsize is zero then this is a size query. -+ * Return the size, but don't copy. -+ */ -+ if (!bsize) -+ return size; -+ -+ if (size <= bsize) { -+ memcpy(buf, xb + pos, size); -+ return size; -+ } -+ } -+ if (pos >= 0) -+ return -ERANGE; -+ -+ return -ENODATA; -+} -+ -+int nval_list(const char *xb, int xb_size, char *buf, int bsize) -+{ -+ int pos = 0; -+ int size; -+ int name_len; -+ int ncopied = 0; -+ int filled = 0; -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ while (size > sizeof(int) && -+ size <= xb_size && -+ (pos + size) < xb_size && -+ !filled) { -+ pos += sizeof(int); -+ size -= sizeof(int); -+ name_len = strnlen((YCHAR *) (xb + pos), size); -+ if (ncopied + name_len + 1 < bsize) { -+ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); -+ buf += name_len; -+ *buf = '\0'; -+ buf++; -+ if (sizeof(YCHAR) > 1) { -+ *buf = '\0'; -+ buf++; -+ } -+ ncopied += (name_len + 1); -+ } else { -+ filled = 1; -+ } -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ return ncopied; -+} -+ -+int nval_hasvalues(const char *xb, int xb_size) -+{ -+ return nval_used(xb, xb_size) > 0; -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_nameval.h linux-3.18.14/fs/yaffs2/yaffs_nameval.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_nameval.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,28 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __NAMEVAL_H__ -+#define __NAMEVAL_H__ -+ -+#include "yportenv.h" -+ -+int nval_del(char *xb, int xb_size, const YCHAR * name); -+int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, -+ int bsize, int flags); -+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, -+ int bsize); -+int nval_list(const char *xb, int xb_size, char *buf, int bsize); -+int nval_hasvalues(const char *xb, int xb_size); -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_nand.c linux-3.18.14/fs/yaffs2/yaffs_nand.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_nand.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,122 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_nand.h" -+#include "yaffs_tagscompat.h" -+ -+#include "yaffs_getblockinfo.h" -+#include "yaffs_summary.h" -+ -+static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) -+{ -+ return chunk - dev->chunk_offset; -+} -+ -+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, -+ u8 *buffer, struct yaffs_ext_tags *tags) -+{ -+ int result; -+ struct yaffs_ext_tags local_tags; -+ int flash_chunk = apply_chunk_offset(dev, nand_chunk); -+ -+ dev->n_page_reads++; -+ -+ /* If there are no tags provided use local tags. */ -+ if (!tags) -+ tags = &local_tags; -+ -+ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); -+ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { -+ -+ struct yaffs_block_info *bi; -+ bi = yaffs_get_block_info(dev, -+ nand_chunk / -+ dev->param.chunks_per_block); -+ yaffs_handle_chunk_error(dev, bi); -+ } -+ return result; -+} -+ -+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *buffer, struct yaffs_ext_tags *tags) -+{ -+ int result; -+ int flash_chunk = apply_chunk_offset(dev, nand_chunk); -+ -+ dev->n_page_writes++; -+ -+ if (!tags) { -+ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ tags->seq_number = dev->seq_number; -+ tags->chunk_used = 1; -+ yaffs_trace(YAFFS_TRACE_WRITE, -+ "Writing chunk %d tags %d %d", -+ nand_chunk, tags->obj_id, tags->chunk_id); -+ -+ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, -+ buffer, tags); -+ -+ yaffs_summary_add(dev, tags, nand_chunk); -+ -+ return result; -+} -+ -+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ block_no -= dev->block_offset; -+ dev->n_bad_markings++; -+ -+ if (dev->param.disable_bad_block_marking) -+ return YAFFS_OK; -+ -+ return dev->tagger.mark_bad_fn(dev, block_no); -+} -+ -+ -+int yaffs_query_init_block_state(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ block_no -= dev->block_offset; -+ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); -+} -+ -+int yaffs_erase_block(struct yaffs_dev *dev, int block_no) -+{ -+ int result; -+ -+ block_no -= dev->block_offset; -+ dev->n_erasures++; -+ result = dev->drv.drv_erase_fn(dev, block_no); -+ return result; -+} -+ -+int yaffs_init_nand(struct yaffs_dev *dev) -+{ -+ if (dev->drv.drv_initialise_fn) -+ return dev->drv.drv_initialise_fn(dev); -+ return YAFFS_OK; -+} -+ -+int yaffs_deinit_nand(struct yaffs_dev *dev) -+{ -+ if (dev->drv.drv_deinitialise_fn) -+ return dev->drv.drv_deinitialise_fn(dev); -+ return YAFFS_OK; -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_nand.h linux-3.18.14/fs/yaffs2/yaffs_nand.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_nand.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_NAND_H__ -+#define __YAFFS_NAND_H__ -+#include "yaffs_guts.h" -+ -+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, -+ u8 *buffer, struct yaffs_ext_tags *tags); -+ -+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *buffer, struct yaffs_ext_tags *tags); -+ -+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); -+ -+int yaffs_query_init_block_state(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ unsigned *seq_number); -+ -+int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); -+ -+int yaffs_init_nand(struct yaffs_dev *dev); -+int yaffs_deinit_nand(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.18.14/fs/yaffs2/yaffs_packedtags1.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_packedtags1.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,56 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_packedtags1.h" -+#include "yportenv.h" -+ -+static const u8 all_ff[20] = { -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff -+}; -+ -+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, -+ const struct yaffs_ext_tags *t) -+{ -+ pt->chunk_id = t->chunk_id; -+ pt->serial_number = t->serial_number; -+ pt->n_bytes = t->n_bytes; -+ pt->obj_id = t->obj_id; -+ pt->ecc = 0; -+ pt->deleted = (t->is_deleted) ? 0 : 1; -+ pt->unused_stuff = 0; -+ pt->should_be_ff = 0xffffffff; -+} -+ -+void yaffs_unpack_tags1(struct yaffs_ext_tags *t, -+ const struct yaffs_packed_tags1 *pt) -+{ -+ -+ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { -+ t->block_bad = 0; -+ if (pt->should_be_ff != 0xffffffff) -+ t->block_bad = 1; -+ t->chunk_used = 1; -+ t->obj_id = pt->obj_id; -+ t->chunk_id = pt->chunk_id; -+ t->n_bytes = pt->n_bytes; -+ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ t->is_deleted = (pt->deleted) ? 0 : 1; -+ t->serial_number = pt->serial_number; -+ } else { -+ memset(t, 0, sizeof(struct yaffs_ext_tags)); -+ } -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.18.14/fs/yaffs2/yaffs_packedtags1.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_packedtags1.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ -+ -+#ifndef __YAFFS_PACKEDTAGS1_H__ -+#define __YAFFS_PACKEDTAGS1_H__ -+ -+#include "yaffs_guts.h" -+ -+struct yaffs_packed_tags1 { -+ u32 chunk_id:20; -+ u32 serial_number:2; -+ u32 n_bytes:10; -+ u32 obj_id:18; -+ u32 ecc:12; -+ u32 deleted:1; -+ u32 unused_stuff:1; -+ unsigned should_be_ff; -+ -+}; -+ -+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, -+ const struct yaffs_ext_tags *t); -+void yaffs_unpack_tags1(struct yaffs_ext_tags *t, -+ const struct yaffs_packed_tags1 *pt); -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.18.14/fs/yaffs2/yaffs_packedtags2.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_packedtags2.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,197 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_packedtags2.h" -+#include "yportenv.h" -+#include "yaffs_trace.h" -+ -+/* This code packs a set of extended tags into a binary structure for -+ * NAND storage -+ */ -+ -+/* Some of the information is "extra" struff which can be packed in to -+ * speed scanning -+ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. -+ */ -+ -+/* Extra flags applied to chunk_id */ -+ -+#define EXTRA_HEADER_INFO_FLAG 0x80000000 -+#define EXTRA_SHRINK_FLAG 0x40000000 -+#define EXTRA_SHADOWS_FLAG 0x20000000 -+#define EXTRA_SPARE_FLAGS 0x10000000 -+ -+#define ALL_EXTRA_FLAGS 0xf0000000 -+ -+/* Also, the top 4 bits of the object Id are set to the object type. */ -+#define EXTRA_OBJECT_TYPE_SHIFT (28) -+#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) -+ -+static void yaffs_dump_packed_tags2_tags_only( -+ const struct yaffs_packed_tags2_tags_only *ptt) -+{ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "packed tags obj %d chunk %d byte %d seq %d", -+ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); -+} -+ -+static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) -+{ -+ yaffs_dump_packed_tags2_tags_only(&pt->t); -+} -+ -+static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) -+{ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", -+ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, -+ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, -+ t->seq_number); -+ -+} -+ -+static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) -+{ -+ if (t->chunk_id != 0 || !t->extra_available) -+ return 0; -+ -+ /* Check if the file size is too long to store */ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && -+ (t->extra_file_size >> 31) != 0) -+ return 0; -+ return 1; -+} -+ -+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, -+ const struct yaffs_ext_tags *t) -+{ -+ ptt->chunk_id = t->chunk_id; -+ ptt->seq_number = t->seq_number; -+ ptt->n_bytes = t->n_bytes; -+ ptt->obj_id = t->obj_id; -+ -+ /* Only store extra tags for object headers. -+ * If it is a file then only store if the file size is short\ -+ * enough to fit. -+ */ -+ if (yaffs_check_tags_extra_packable(t)) { -+ /* Store the extra header info instead */ -+ /* We save the parent object in the chunk_id */ -+ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; -+ if (t->extra_is_shrink) -+ ptt->chunk_id |= EXTRA_SHRINK_FLAG; -+ if (t->extra_shadows) -+ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; -+ -+ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; -+ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); -+ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ ptt->n_bytes = t->extra_equiv_id; -+ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) -+ ptt->n_bytes = (unsigned) t->extra_file_size; -+ else -+ ptt->n_bytes = 0; -+ } -+ -+ yaffs_dump_packed_tags2_tags_only(ptt); -+ yaffs_dump_tags2(t); -+} -+ -+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, -+ const struct yaffs_ext_tags *t, int tags_ecc) -+{ -+ yaffs_pack_tags2_tags_only(&pt->t, t); -+ -+ if (tags_ecc) -+ yaffs_ecc_calc_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &pt->ecc); -+} -+ -+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, -+ struct yaffs_packed_tags2_tags_only *ptt) -+{ -+ memset(t, 0, sizeof(struct yaffs_ext_tags)); -+ -+ if (ptt->seq_number == 0xffffffff) -+ return; -+ -+ t->block_bad = 0; -+ t->chunk_used = 1; -+ t->obj_id = ptt->obj_id; -+ t->chunk_id = ptt->chunk_id; -+ t->n_bytes = ptt->n_bytes; -+ t->is_deleted = 0; -+ t->serial_number = 0; -+ t->seq_number = ptt->seq_number; -+ -+ /* Do extra header info stuff */ -+ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { -+ t->chunk_id = 0; -+ t->n_bytes = 0; -+ -+ t->extra_available = 1; -+ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); -+ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; -+ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; -+ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; -+ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; -+ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ t->extra_equiv_id = ptt->n_bytes; -+ else -+ t->extra_file_size = ptt->n_bytes; -+ } -+ yaffs_dump_packed_tags2_tags_only(ptt); -+ yaffs_dump_tags2(t); -+} -+ -+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, -+ int tags_ecc) -+{ -+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ -+ if (pt->t.seq_number != 0xffffffff && tags_ecc) { -+ /* Chunk is in use and we need to do ECC */ -+ -+ struct yaffs_ecc_other ecc; -+ int result; -+ yaffs_ecc_calc_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &ecc); -+ result = -+ yaffs_ecc_correct_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &pt->ecc, &ecc); -+ switch (result) { -+ case 0: -+ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ break; -+ case 1: -+ ecc_result = YAFFS_ECC_RESULT_FIXED; -+ break; -+ case -1: -+ ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ break; -+ default: -+ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; -+ } -+ } -+ yaffs_unpack_tags2_tags_only(t, &pt->t); -+ -+ t->ecc_result = ecc_result; -+ -+ yaffs_dump_packed_tags2(pt); -+ yaffs_dump_tags2(t); -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.18.14/fs/yaffs2/yaffs_packedtags2.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_packedtags2.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,47 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ -+ -+#ifndef __YAFFS_PACKEDTAGS2_H__ -+#define __YAFFS_PACKEDTAGS2_H__ -+ -+#include "yaffs_guts.h" -+#include "yaffs_ecc.h" -+ -+struct yaffs_packed_tags2_tags_only { -+ unsigned seq_number; -+ unsigned obj_id; -+ unsigned chunk_id; -+ unsigned n_bytes; -+}; -+ -+struct yaffs_packed_tags2 { -+ struct yaffs_packed_tags2_tags_only t; -+ struct yaffs_ecc_other ecc; -+}; -+ -+/* Full packed tags with ECC, used for oob tags */ -+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, -+ const struct yaffs_ext_tags *t, int tags_ecc); -+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, -+ int tags_ecc); -+ -+/* Only the tags part (no ECC for use with inband tags */ -+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, -+ const struct yaffs_ext_tags *t); -+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, -+ struct yaffs_packed_tags2_tags_only *pt); -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_summary.c linux-3.18.14/fs/yaffs2/yaffs_summary.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_summary.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,312 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Summaries write the useful part of the tags for the chunks in a block into an -+ * an array which is written to the last n chunks of the block. -+ * Reading the summaries gives all the tags for the block in one read. Much -+ * faster. -+ * -+ * Chunks holding summaries are marked with tags making it look like -+ * they are part of a fake file. -+ * -+ * The summary could also be used during gc. -+ * -+ */ -+ -+#include "yaffs_summary.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_nand.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_bitmap.h" -+ -+/* -+ * The summary is built up in an array of summary tags. -+ * This gets written to the last one or two (maybe more) chunks in a block. -+ * A summary header is written as the first part of each chunk of summary data. -+ * The summary header must match or the summary is rejected. -+ */ -+ -+/* Summary tags don't need the sequence number because that is redundant. */ -+struct yaffs_summary_tags { -+ unsigned obj_id; -+ unsigned chunk_id; -+ unsigned n_bytes; -+}; -+ -+/* Summary header */ -+struct yaffs_summary_header { -+ unsigned version; /* Must match current version */ -+ unsigned block; /* Must be this block */ -+ unsigned seq; /* Must be this sequence number */ -+ unsigned sum; /* Just add up all the bytes in the tags */ -+}; -+ -+ -+static void yaffs_summary_clear(struct yaffs_dev *dev) -+{ -+ if (!dev->sum_tags) -+ return; -+ memset(dev->sum_tags, 0, dev->chunks_per_summary * -+ sizeof(struct yaffs_summary_tags)); -+} -+ -+ -+void yaffs_summary_deinit(struct yaffs_dev *dev) -+{ -+ kfree(dev->sum_tags); -+ dev->sum_tags = NULL; -+ kfree(dev->gc_sum_tags); -+ dev->gc_sum_tags = NULL; -+ dev->chunks_per_summary = 0; -+} -+ -+int yaffs_summary_init(struct yaffs_dev *dev) -+{ -+ int sum_bytes; -+ int chunks_used; /* Number of chunks used by summary */ -+ int sum_tags_bytes; -+ -+ sum_bytes = dev->param.chunks_per_block * -+ sizeof(struct yaffs_summary_tags); -+ -+ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ -+ (dev->data_bytes_per_chunk - -+ sizeof(struct yaffs_summary_header)); -+ -+ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; -+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); -+ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); -+ if (!dev->sum_tags || !dev->gc_sum_tags) { -+ yaffs_summary_deinit(dev); -+ return YAFFS_FAIL; -+ } -+ -+ yaffs_summary_clear(dev); -+ -+ return YAFFS_OK; -+} -+ -+static unsigned yaffs_summary_sum(struct yaffs_dev *dev) -+{ -+ u8 *sum_buffer = (u8 *)dev->sum_tags; -+ int i; -+ unsigned sum = 0; -+ -+ i = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ while (i > 0) { -+ sum += *sum_buffer; -+ sum_buffer++; -+ i--; -+ } -+ -+ return sum; -+} -+ -+static int yaffs_summary_write(struct yaffs_dev *dev, int blk) -+{ -+ struct yaffs_ext_tags tags; -+ u8 *buffer; -+ u8 *sum_buffer = (u8 *)dev->sum_tags; -+ int n_bytes; -+ int chunk_in_nand; -+ int chunk_in_block; -+ int result; -+ int this_tx; -+ struct yaffs_summary_header hdr; -+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ -+ buffer = yaffs_get_temp_buffer(dev); -+ n_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); -+ tags.obj_id = YAFFS_OBJECTID_SUMMARY; -+ tags.chunk_id = 1; -+ chunk_in_block = dev->chunks_per_summary; -+ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + -+ dev->chunks_per_summary; -+ hdr.version = YAFFS_SUMMARY_VERSION; -+ hdr.block = blk; -+ hdr.seq = bi->seq_number; -+ hdr.sum = yaffs_summary_sum(dev); -+ -+ do { -+ this_tx = n_bytes; -+ if (this_tx > sum_bytes_per_chunk) -+ this_tx = sum_bytes_per_chunk; -+ memcpy(buffer, &hdr, sizeof(hdr)); -+ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); -+ tags.n_bytes = this_tx + sizeof(hdr); -+ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, -+ buffer, &tags); -+ -+ if (result != YAFFS_OK) -+ break; -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ dev->n_free_chunks--; -+ -+ n_bytes -= this_tx; -+ sum_buffer += this_tx; -+ chunk_in_nand++; -+ chunk_in_block++; -+ tags.chunk_id++; -+ } while (result == YAFFS_OK && n_bytes > 0); -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ -+ if (result == YAFFS_OK) -+ bi->has_summary = 1; -+ -+ -+ return result; -+} -+ -+int yaffs_summary_read(struct yaffs_dev *dev, -+ struct yaffs_summary_tags *st, -+ int blk) -+{ -+ struct yaffs_ext_tags tags; -+ u8 *buffer; -+ u8 *sum_buffer = (u8 *)st; -+ int n_bytes; -+ int chunk_id; -+ int chunk_in_nand; -+ int chunk_in_block; -+ int result; -+ int this_tx; -+ struct yaffs_summary_header hdr; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); -+ int sum_tags_bytes; -+ -+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ buffer = yaffs_get_temp_buffer(dev); -+ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; -+ chunk_in_block = dev->chunks_per_summary; -+ chunk_in_nand = blk * dev->param.chunks_per_block + -+ dev->chunks_per_summary; -+ chunk_id = 1; -+ do { -+ this_tx = n_bytes; -+ if (this_tx > sum_bytes_per_chunk) -+ this_tx = sum_bytes_per_chunk; -+ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, -+ buffer, &tags); -+ -+ if (tags.chunk_id != chunk_id || -+ tags.obj_id != YAFFS_OBJECTID_SUMMARY || -+ tags.chunk_used == 0 || -+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || -+ tags.n_bytes != (this_tx + sizeof(hdr))) -+ result = YAFFS_FAIL; -+ if (result != YAFFS_OK) -+ break; -+ -+ if (st == dev->sum_tags) { -+ /* If we're scanning then update the block info */ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ } -+ memcpy(&hdr, buffer, sizeof(hdr)); -+ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); -+ n_bytes -= this_tx; -+ sum_buffer += this_tx; -+ chunk_in_nand++; -+ chunk_in_block++; -+ chunk_id++; -+ } while (result == YAFFS_OK && n_bytes > 0); -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ if (result == YAFFS_OK) { -+ /* Verify header */ -+ if (hdr.version != YAFFS_SUMMARY_VERSION || -+ hdr.seq != bi->seq_number || -+ hdr.sum != yaffs_summary_sum(dev)) -+ result = YAFFS_FAIL; -+ } -+ -+ if (st == dev->sum_tags && result == YAFFS_OK) -+ bi->has_summary = 1; -+ -+ return result; -+} -+ -+int yaffs_summary_add(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_nand) -+{ -+ struct yaffs_packed_tags2_tags_only tags_only; -+ struct yaffs_summary_tags *sum_tags; -+ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; -+ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; -+ -+ if (!dev->sum_tags) -+ return YAFFS_OK; -+ -+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { -+ yaffs_pack_tags2_tags_only(&tags_only, tags); -+ sum_tags = &dev->sum_tags[chunk_in_block]; -+ sum_tags->chunk_id = tags_only.chunk_id; -+ sum_tags->n_bytes = tags_only.n_bytes; -+ sum_tags->obj_id = tags_only.obj_id; -+ -+ if (chunk_in_block == dev->chunks_per_summary - 1) { -+ /* Time to write out the summary */ -+ yaffs_summary_write(dev, block_in_nand); -+ yaffs_summary_clear(dev); -+ yaffs_skip_rest_of_block(dev); -+ } -+ } -+ return YAFFS_OK; -+} -+ -+int yaffs_summary_fetch(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block) -+{ -+ struct yaffs_packed_tags2_tags_only tags_only; -+ struct yaffs_summary_tags *sum_tags; -+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { -+ sum_tags = &dev->sum_tags[chunk_in_block]; -+ tags_only.chunk_id = sum_tags->chunk_id; -+ tags_only.n_bytes = sum_tags->n_bytes; -+ tags_only.obj_id = sum_tags->obj_id; -+ yaffs_unpack_tags2_tags_only(tags, &tags_only); -+ return YAFFS_OK; -+ } -+ return YAFFS_FAIL; -+} -+ -+void yaffs_summary_gc(struct yaffs_dev *dev, int blk) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ int i; -+ -+ if (!bi->has_summary) -+ return; -+ -+ for (i = dev->chunks_per_summary; -+ i < dev->param.chunks_per_block; -+ i++) { -+ if (yaffs_check_chunk_bit(dev, blk, i)) { -+ yaffs_clear_chunk_bit(dev, blk, i); -+ bi->pages_in_use--; -+ dev->n_free_chunks++; -+ } -+ } -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_summary.h linux-3.18.14/fs/yaffs2/yaffs_summary.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_summary.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,37 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_SUMMARY_H__ -+#define __YAFFS_SUMMARY_H__ -+ -+#include "yaffs_packedtags2.h" -+ -+ -+int yaffs_summary_init(struct yaffs_dev *dev); -+void yaffs_summary_deinit(struct yaffs_dev *dev); -+ -+int yaffs_summary_add(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block); -+int yaffs_summary_fetch(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block); -+int yaffs_summary_read(struct yaffs_dev *dev, -+ struct yaffs_summary_tags *st, -+ int blk); -+void yaffs_summary_gc(struct yaffs_dev *dev, int blk); -+ -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.18.14/fs/yaffs2/yaffs_tagscompat.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_tagscompat.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,381 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_tagscompat.h" -+#include "yaffs_ecc.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_trace.h" -+ -+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); -+ -+ -+/********** Tags ECC calculations *********/ -+ -+ -+void yaffs_calc_tags_ecc(struct yaffs_tags *tags) -+{ -+ /* Calculate an ecc */ -+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; -+ unsigned i, j; -+ unsigned ecc = 0; -+ unsigned bit = 0; -+ -+ tags->ecc = 0; -+ -+ for (i = 0; i < 8; i++) { -+ for (j = 1; j & 0xff; j <<= 1) { -+ bit++; -+ if (b[i] & j) -+ ecc ^= bit; -+ } -+ } -+ tags->ecc = ecc; -+} -+ -+int yaffs_check_tags_ecc(struct yaffs_tags *tags) -+{ -+ unsigned ecc = tags->ecc; -+ -+ yaffs_calc_tags_ecc(tags); -+ -+ ecc ^= tags->ecc; -+ -+ if (ecc && ecc <= 64) { -+ /* TODO: Handle the failure better. Retire? */ -+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; -+ -+ ecc--; -+ -+ b[ecc / 8] ^= (1 << (ecc & 7)); -+ -+ /* Now recvalc the ecc */ -+ yaffs_calc_tags_ecc(tags); -+ -+ return 1; /* recovered error */ -+ } else if (ecc) { -+ /* Wierd ecc failure value */ -+ /* TODO Need to do somethiong here */ -+ return -1; /* unrecovered error */ -+ } -+ return 0; -+} -+ -+/********** Tags **********/ -+ -+static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, -+ struct yaffs_tags *tags_ptr) -+{ -+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; -+ -+ yaffs_calc_tags_ecc(tags_ptr); -+ -+ spare_ptr->tb0 = tu->as_bytes[0]; -+ spare_ptr->tb1 = tu->as_bytes[1]; -+ spare_ptr->tb2 = tu->as_bytes[2]; -+ spare_ptr->tb3 = tu->as_bytes[3]; -+ spare_ptr->tb4 = tu->as_bytes[4]; -+ spare_ptr->tb5 = tu->as_bytes[5]; -+ spare_ptr->tb6 = tu->as_bytes[6]; -+ spare_ptr->tb7 = tu->as_bytes[7]; -+} -+ -+static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, -+ struct yaffs_spare *spare_ptr, -+ struct yaffs_tags *tags_ptr) -+{ -+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; -+ int result; -+ -+ tu->as_bytes[0] = spare_ptr->tb0; -+ tu->as_bytes[1] = spare_ptr->tb1; -+ tu->as_bytes[2] = spare_ptr->tb2; -+ tu->as_bytes[3] = spare_ptr->tb3; -+ tu->as_bytes[4] = spare_ptr->tb4; -+ tu->as_bytes[5] = spare_ptr->tb5; -+ tu->as_bytes[6] = spare_ptr->tb6; -+ tu->as_bytes[7] = spare_ptr->tb7; -+ -+ result = yaffs_check_tags_ecc(tags_ptr); -+ if (result > 0) -+ dev->n_tags_ecc_fixed++; -+ else if (result < 0) -+ dev->n_tags_ecc_unfixed++; -+} -+ -+static void yaffs_spare_init(struct yaffs_spare *spare) -+{ -+ memset(spare, 0xff, sizeof(struct yaffs_spare)); -+} -+ -+static int yaffs_wr_nand(struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ struct yaffs_spare *spare) -+{ -+ int data_size = dev->data_bytes_per_chunk; -+ -+ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *) spare, sizeof(*spare)); -+} -+ -+static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, -+ struct yaffs_spare *spare, -+ enum yaffs_ecc_result *ecc_result, -+ int correct_errors) -+{ -+ int ret_val; -+ struct yaffs_spare local_spare; -+ int data_size; -+ int spare_size; -+ int ecc_result1, ecc_result2; -+ u8 calc_ecc[3]; -+ -+ if (!spare) { -+ /* If we don't have a real spare, then we use a local one. */ -+ /* Need this for the calculation of the ecc */ -+ spare = &local_spare; -+ } -+ data_size = dev->data_bytes_per_chunk; -+ spare_size = sizeof(struct yaffs_spare); -+ -+ if (dev->param.use_nand_ecc) -+ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *) spare, spare_size, -+ ecc_result); -+ -+ -+ /* Handle the ECC at this level. */ -+ -+ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *)spare, spare_size, -+ NULL); -+ if (!data || !correct_errors) -+ return ret_val; -+ -+ /* Do ECC correction if needed. */ -+ yaffs_ecc_calc(data, calc_ecc); -+ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); -+ yaffs_ecc_calc(&data[256], calc_ecc); -+ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); -+ -+ if (ecc_result1 > 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error fix performed on chunk %d:0", -+ nand_chunk); -+ dev->n_ecc_fixed++; -+ } else if (ecc_result1 < 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error unfixed on chunk %d:0", -+ nand_chunk); -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (ecc_result2 > 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error fix performed on chunk %d:1", -+ nand_chunk); -+ dev->n_ecc_fixed++; -+ } else if (ecc_result2 < 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error unfixed on chunk %d:1", -+ nand_chunk); -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (ecc_result1 || ecc_result2) { -+ /* We had a data problem on this page */ -+ yaffs_handle_rd_data_error(dev, nand_chunk); -+ } -+ -+ if (ecc_result1 < 0 || ecc_result2 < 0) -+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ else if (ecc_result1 > 0 || ecc_result2 > 0) -+ *ecc_result = YAFFS_ECC_RESULT_FIXED; -+ else -+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ -+ return ret_val; -+} -+ -+/* -+ * Functions for robustisizing -+ */ -+ -+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) -+{ -+ int flash_block = nand_chunk / dev->param.chunks_per_block; -+ -+ /* Mark the block for retirement */ -+ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> -+ needs_retiring = 1; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>>Block %d marked for retirement", -+ flash_block); -+ -+ /* TODO: -+ * Just do a garbage collection on the affected block -+ * then retire the block -+ * NB recursion -+ */ -+} -+ -+static int yaffs_tags_compat_wr(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, const struct yaffs_ext_tags *ext_tags) -+{ -+ struct yaffs_spare spare; -+ struct yaffs_tags tags; -+ -+ yaffs_spare_init(&spare); -+ -+ if (ext_tags->is_deleted) -+ spare.page_status = 0; -+ else { -+ tags.obj_id = ext_tags->obj_id; -+ tags.chunk_id = ext_tags->chunk_id; -+ -+ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); -+ -+ if (dev->data_bytes_per_chunk >= 1024) -+ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; -+ else -+ tags.n_bytes_msb = 3; -+ -+ tags.serial_number = ext_tags->serial_number; -+ -+ if (!dev->param.use_nand_ecc && data) { -+ yaffs_ecc_calc(data, spare.ecc1); -+ yaffs_ecc_calc(&data[256], spare.ecc2); -+ } -+ -+ yaffs_load_tags_to_spare(&spare, &tags); -+ } -+ return yaffs_wr_nand(dev, nand_chunk, data, &spare); -+} -+ -+static int yaffs_tags_compat_rd(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, struct yaffs_ext_tags *ext_tags) -+{ -+ struct yaffs_spare spare; -+ struct yaffs_tags tags; -+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; -+ static struct yaffs_spare spare_ff; -+ static int init; -+ int deleted; -+ -+ if (!init) { -+ memset(&spare_ff, 0xff, sizeof(spare_ff)); -+ init = 1; -+ } -+ -+ if (!yaffs_rd_chunk_nand(dev, nand_chunk, -+ data, &spare, &ecc_result, 1)) -+ return YAFFS_FAIL; -+ -+ /* ext_tags may be NULL */ -+ if (!ext_tags) -+ return YAFFS_OK; -+ -+ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; -+ -+ ext_tags->is_deleted = deleted; -+ ext_tags->ecc_result = ecc_result; -+ ext_tags->block_bad = 0; /* We're reading it */ -+ /* therefore it is not a bad block */ -+ ext_tags->chunk_used = -+ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; -+ -+ if (ext_tags->chunk_used) { -+ yaffs_get_tags_from_spare(dev, &spare, &tags); -+ ext_tags->obj_id = tags.obj_id; -+ ext_tags->chunk_id = tags.chunk_id; -+ ext_tags->n_bytes = tags.n_bytes_lsb; -+ -+ if (dev->data_bytes_per_chunk >= 1024) -+ ext_tags->n_bytes |= -+ (((unsigned)tags.n_bytes_msb) << 10); -+ -+ ext_tags->serial_number = tags.serial_number; -+ } -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) -+{ -+ struct yaffs_spare spare; -+ -+ memset(&spare, 0xff, sizeof(struct yaffs_spare)); -+ -+ spare.block_status = 'Y'; -+ -+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, -+ &spare); -+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, -+ NULL, &spare); -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ struct yaffs_spare spare0, spare1; -+ static struct yaffs_spare spare_ff; -+ static int init; -+ enum yaffs_ecc_result dummy; -+ -+ if (!init) { -+ memset(&spare_ff, 0xff, sizeof(spare_ff)); -+ init = 1; -+ } -+ -+ *seq_number = 0; -+ -+ /* Look for bad block markers in the first two chunks */ -+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, -+ NULL, &spare0, &dummy, 0); -+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, -+ NULL, &spare1, &dummy, 0); -+ -+ if (hweight8(spare0.block_status & spare1.block_status) < 7) -+ *state = YAFFS_BLOCK_STATE_DEAD; -+ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) -+ *state = YAFFS_BLOCK_STATE_EMPTY; -+ else -+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; -+ -+ return YAFFS_OK; -+} -+ -+void yaffs_tags_compat_install(struct yaffs_dev *dev) -+{ -+ if(dev->param.is_yaffs2) -+ return; -+ if(!dev->tagger.write_chunk_tags_fn) -+ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; -+ if(!dev->tagger.read_chunk_tags_fn) -+ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; -+ if(!dev->tagger.query_block_fn) -+ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; -+ if(!dev->tagger.mark_bad_fn) -+ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.18.14/fs/yaffs2/yaffs_tagscompat.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_tagscompat.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,44 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_TAGSCOMPAT_H__ -+#define __YAFFS_TAGSCOMPAT_H__ -+ -+ -+#include "yaffs_guts.h" -+ -+#if 0 -+ -+ -+int yaffs_tags_compat_wr(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, const struct yaffs_ext_tags *tags); -+int yaffs_tags_compat_rd(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, struct yaffs_ext_tags *tags); -+int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); -+int yaffs_tags_compat_query_block(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number); -+ -+#endif -+ -+ -+void yaffs_tags_compat_install(struct yaffs_dev *dev); -+void yaffs_calc_tags_ecc(struct yaffs_tags *tags); -+int yaffs_check_tags_ecc(struct yaffs_tags *tags); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.18.14/fs/yaffs2/yaffs_tagsmarshall.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_tagsmarshall.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,199 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yaffs_packedtags2.h" -+ -+static int yaffs_tags_marshall_write(struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ const struct yaffs_ext_tags *tags) -+{ -+ struct yaffs_packed_tags2 pt; -+ int retval; -+ -+ int packed_tags_size = -+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); -+ void *packed_tags_ptr = -+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_tags_marshall_write chunk %d data %p tags %p", -+ nand_chunk, data, tags); -+ -+ /* For yaffs2 writing there must be both data and tags. -+ * If we're using inband tags, then the tags are stuffed into -+ * the end of the data buffer. -+ */ -+ if (!data || !tags) -+ BUG(); -+ else if (dev->param.inband_tags) { -+ struct yaffs_packed_tags2_tags_only *pt2tp; -+ pt2tp = -+ (struct yaffs_packed_tags2_tags_only *)(data + -+ dev-> -+ data_bytes_per_chunk); -+ yaffs_pack_tags2_tags_only(pt2tp, tags); -+ } else { -+ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); -+ } -+ -+ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ (dev->param.inband_tags) ? NULL : packed_tags_ptr, -+ (dev->param.inband_tags) ? 0 : packed_tags_size); -+ -+ return retval; -+} -+ -+static int yaffs_tags_marshall_read(struct yaffs_dev *dev, -+ int nand_chunk, u8 *data, -+ struct yaffs_ext_tags *tags) -+{ -+ int retval = 0; -+ int local_data = 0; -+ u8 spare_buffer[100]; -+ enum yaffs_ecc_result ecc_result; -+ -+ struct yaffs_packed_tags2 pt; -+ -+ int packed_tags_size = -+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); -+ void *packed_tags_ptr = -+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_tags_marshall_read chunk %d data %p tags %p", -+ nand_chunk, data, tags); -+ -+ if (dev->param.inband_tags) { -+ if (!data) { -+ local_data = 1; -+ data = yaffs_get_temp_buffer(dev); -+ } -+ } -+ -+ if (dev->param.inband_tags || (data && !tags)) -+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ NULL, 0, -+ &ecc_result); -+ else if (tags) -+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ spare_buffer, packed_tags_size, -+ &ecc_result); -+ else -+ BUG(); -+ -+ -+ if (dev->param.inband_tags) { -+ if (tags) { -+ struct yaffs_packed_tags2_tags_only *pt2tp; -+ pt2tp = -+ (struct yaffs_packed_tags2_tags_only *) -+ &data[dev->data_bytes_per_chunk]; -+ yaffs_unpack_tags2_tags_only(tags, pt2tp); -+ } -+ } else if (tags) { -+ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); -+ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); -+ } -+ -+ if (local_data) -+ yaffs_release_temp_buffer(dev, data); -+ -+ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { -+ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { -+ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) -+ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; -+ dev->n_ecc_fixed++; -+ } -+ -+ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) -+ return YAFFS_OK; -+ else -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", -+ block_no); -+ -+ retval = dev->drv.drv_check_bad_fn(dev, block_no); -+ -+ if (retval== YAFFS_FAIL) { -+ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); -+ -+ *state = YAFFS_BLOCK_STATE_DEAD; -+ *seq_number = 0; -+ } else { -+ struct yaffs_ext_tags t; -+ -+ yaffs_tags_marshall_read(dev, -+ block_no * dev->param.chunks_per_block, -+ NULL, &t); -+ -+ if (t.chunk_used) { -+ *seq_number = t.seq_number; -+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; -+ } else { -+ *seq_number = 0; -+ *state = YAFFS_BLOCK_STATE_EMPTY; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "block query returns seq %d state %d", -+ *seq_number, *state); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ else -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ return dev->drv.drv_mark_bad_fn(dev, block_no); -+ -+} -+ -+ -+void yaffs_tags_marshall_install(struct yaffs_dev *dev) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!dev->tagger.write_chunk_tags_fn) -+ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; -+ -+ if (!dev->tagger.read_chunk_tags_fn) -+ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; -+ -+ if (!dev->tagger.query_block_fn) -+ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; -+ -+ if (!dev->tagger.mark_bad_fn) -+ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; -+ -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.18.14/fs/yaffs2/yaffs_tagsmarshall.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_tagsmarshall.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,22 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_TAGSMARSHALL_H__ -+#define __YAFFS_TAGSMARSHALL_H__ -+ -+#include "yaffs_guts.h" -+void yaffs_tags_marshall_install(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_trace.h linux-3.18.14/fs/yaffs2/yaffs_trace.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_trace.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,57 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YTRACE_H__ -+#define __YTRACE_H__ -+ -+extern unsigned int yaffs_trace_mask; -+extern unsigned int yaffs_wr_attempts; -+ -+/* -+ * Tracing flags. -+ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. -+ */ -+ -+#define YAFFS_TRACE_OS 0x00000002 -+#define YAFFS_TRACE_ALLOCATE 0x00000004 -+#define YAFFS_TRACE_SCAN 0x00000008 -+#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 -+#define YAFFS_TRACE_ERASE 0x00000020 -+#define YAFFS_TRACE_GC 0x00000040 -+#define YAFFS_TRACE_WRITE 0x00000080 -+#define YAFFS_TRACE_TRACING 0x00000100 -+#define YAFFS_TRACE_DELETION 0x00000200 -+#define YAFFS_TRACE_BUFFERS 0x00000400 -+#define YAFFS_TRACE_NANDACCESS 0x00000800 -+#define YAFFS_TRACE_GC_DETAIL 0x00001000 -+#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 -+#define YAFFS_TRACE_MTD 0x00004000 -+#define YAFFS_TRACE_CHECKPOINT 0x00008000 -+ -+#define YAFFS_TRACE_VERIFY 0x00010000 -+#define YAFFS_TRACE_VERIFY_NAND 0x00020000 -+#define YAFFS_TRACE_VERIFY_FULL 0x00040000 -+#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 -+ -+#define YAFFS_TRACE_SYNC 0x00100000 -+#define YAFFS_TRACE_BACKGROUND 0x00200000 -+#define YAFFS_TRACE_LOCK 0x00400000 -+#define YAFFS_TRACE_MOUNT 0x00800000 -+ -+#define YAFFS_TRACE_ERROR 0x40000000 -+#define YAFFS_TRACE_BUG 0x80000000 -+#define YAFFS_TRACE_ALWAYS 0xf0000000 -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_verify.c linux-3.18.14/fs/yaffs2/yaffs_verify.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_verify.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,529 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_verify.h" -+#include "yaffs_trace.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_nand.h" -+ -+int yaffs_skip_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & -+ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); -+} -+ -+static int yaffs_skip_full_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); -+} -+ -+static int yaffs_skip_nand_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); -+} -+ -+static const char * const block_state_name[] = { -+ "Unknown", -+ "Needs scan", -+ "Scanning", -+ "Empty", -+ "Allocating", -+ "Full", -+ "Dirty", -+ "Checkpoint", -+ "Collecting", -+ "Dead" -+}; -+ -+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) -+{ -+ int actually_used; -+ int in_use; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Report illegal runtime states */ -+ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has undefined state %d", -+ n, bi->block_state); -+ -+ switch (bi->block_state) { -+ case YAFFS_BLOCK_STATE_UNKNOWN: -+ case YAFFS_BLOCK_STATE_SCANNING: -+ case YAFFS_BLOCK_STATE_NEEDS_SCAN: -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has bad run-state %s", -+ n, block_state_name[bi->block_state]); -+ } -+ -+ /* Check pages in use and soft deletions are legal */ -+ -+ actually_used = bi->pages_in_use - bi->soft_del_pages; -+ -+ if (bi->pages_in_use < 0 || -+ bi->pages_in_use > dev->param.chunks_per_block || -+ bi->soft_del_pages < 0 || -+ bi->soft_del_pages > dev->param.chunks_per_block || -+ actually_used < 0 || actually_used > dev->param.chunks_per_block) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has illegal values pages_in_used %d soft_del_pages %d", -+ n, bi->pages_in_use, bi->soft_del_pages); -+ -+ /* Check chunk bitmap legal */ -+ in_use = yaffs_count_chunk_bits(dev, n); -+ if (in_use != bi->pages_in_use) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", -+ n, bi->pages_in_use, in_use); -+} -+ -+void yaffs_verify_collected_blk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, int n) -+{ -+ yaffs_verify_blk(dev, bi, n); -+ -+ /* After collection the block should be in the erased state */ -+ -+ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && -+ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Block %d is in state %d after gc, should be erased", -+ n, bi->block_state); -+ } -+} -+ -+void yaffs_verify_blocks(struct yaffs_dev *dev) -+{ -+ int i; -+ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; -+ int illegal_states = 0; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ memset(state_count, 0, sizeof(state_count)); -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); -+ yaffs_verify_blk(dev, bi, i); -+ -+ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) -+ state_count[bi->block_state]++; -+ else -+ illegal_states++; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); -+ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "%d blocks have illegal states", -+ illegal_states); -+ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Too many allocating blocks"); -+ -+ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "%s %d blocks", -+ block_state_name[i], state_count[i]); -+ -+ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Checkpoint block count wrong dev %d count %d", -+ dev->blocks_in_checkpt, -+ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); -+ -+ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Erased block count wrong dev %d count %d", -+ dev->n_erased_blocks, -+ state_count[YAFFS_BLOCK_STATE_EMPTY]); -+ -+ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Too many collecting blocks %d (max is 1)", -+ state_count[YAFFS_BLOCK_STATE_COLLECTING]); -+} -+ -+/* -+ * Verify the object header. oh must be valid, but obj and tags may be NULL in -+ * which case those tests will not be performed. -+ */ -+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, -+ struct yaffs_ext_tags *tags, int parent_check) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ if (!(tags && obj && oh)) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Verifying object header tags %p obj %p oh %p", -+ tags, obj, oh); -+ return; -+ } -+ -+ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || -+ oh->type > YAFFS_OBJECT_TYPE_MAX) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header type is illegal value 0x%x", -+ tags->obj_id, oh->type); -+ -+ if (tags->obj_id != obj->obj_id) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch obj_id %d", -+ tags->obj_id, obj->obj_id); -+ -+ /* -+ * Check that the object's parent ids match if parent_check requested. -+ * -+ * Tests do not apply to the root object. -+ */ -+ -+ if (parent_check && tags->obj_id > 1 && !obj->parent) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch parent_id %d obj->parent is NULL", -+ tags->obj_id, oh->parent_obj_id); -+ -+ if (parent_check && obj->parent && -+ oh->parent_obj_id != obj->parent->obj_id && -+ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || -+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch parent_id %d parent_obj_id %d", -+ tags->obj_id, oh->parent_obj_id, -+ obj->parent->obj_id); -+ -+ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header name is NULL", -+ obj->obj_id); -+ -+ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header name is 0xff", -+ obj->obj_id); -+} -+ -+void yaffs_verify_file(struct yaffs_obj *obj) -+{ -+ u32 x; -+ int required_depth; -+ int actual_depth; -+ int last_chunk; -+ u32 offset_in_chunk; -+ u32 the_chunk; -+ -+ u32 i; -+ struct yaffs_dev *dev; -+ struct yaffs_ext_tags tags; -+ struct yaffs_tnode *tn; -+ u32 obj_id; -+ -+ if (!obj) -+ return; -+ -+ if (yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ dev = obj->my_dev; -+ obj_id = obj->obj_id; -+ -+ -+ /* Check file size is consistent with tnode depth */ -+ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, -+ &last_chunk, &offset_in_chunk); -+ last_chunk++; -+ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (x > 0) { -+ x >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ actual_depth = obj->variant.file_variant.top_level; -+ -+ /* Check that the chunks in the tnode tree are all correct. -+ * We do this by scanning through the tnode tree and -+ * checking the tags for every chunk match. -+ */ -+ -+ if (yaffs_skip_nand_verification(dev)) -+ return; -+ -+ for (i = 1; i <= last_chunk; i++) { -+ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); -+ -+ if (!tn) -+ continue; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, i); -+ if (the_chunk > 0) { -+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, -+ &tags); -+ if (tags.obj_id != obj_id || tags.chunk_id != i) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", -+ obj_id, i, the_chunk, -+ tags.obj_id, tags.chunk_id); -+ } -+ } -+} -+ -+void yaffs_verify_link(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ /* Verify sane equivalent object */ -+} -+ -+void yaffs_verify_symlink(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ /* Verify symlink string */ -+} -+ -+void yaffs_verify_special(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+} -+ -+void yaffs_verify_obj(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ u32 chunk_min; -+ u32 chunk_max; -+ u32 chunk_id_ok; -+ u32 chunk_in_range; -+ u32 chunk_wrongly_deleted; -+ u32 chunk_valid; -+ -+ if (!obj) -+ return; -+ -+ if (obj->being_created) -+ return; -+ -+ dev = obj->my_dev; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Check sane object header chunk */ -+ -+ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; -+ chunk_max = -+ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; -+ -+ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && -+ ((unsigned)(obj->hdr_chunk)) <= chunk_max); -+ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); -+ chunk_valid = chunk_in_range && -+ yaffs_check_chunk_bit(dev, -+ obj->hdr_chunk / dev->param.chunks_per_block, -+ obj->hdr_chunk % dev->param.chunks_per_block); -+ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; -+ -+ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has chunk_id %d %s %s", -+ obj->obj_id, obj->hdr_chunk, -+ chunk_id_ok ? "" : ",out of range", -+ chunk_wrongly_deleted ? ",marked as deleted" : ""); -+ -+ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { -+ struct yaffs_ext_tags tags; -+ struct yaffs_obj_hdr *oh; -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ oh = (struct yaffs_obj_hdr *)buffer; -+ -+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); -+ -+ yaffs_verify_oh(obj, oh, &tags, 1); -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ -+ /* Verify it has a parent */ -+ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has parent pointer %p which does not look like an object", -+ obj->obj_id, obj->parent); -+ } -+ -+ /* Verify parent is a directory */ -+ if (obj->parent && -+ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d's parent is not a directory (type %d)", -+ obj->obj_id, obj->parent->variant_type); -+ } -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ yaffs_verify_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ yaffs_verify_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ yaffs_verify_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ yaffs_verify_link(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ yaffs_verify_special(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ default: -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has illegaltype %d", -+ obj->obj_id, obj->variant_type); -+ break; -+ } -+} -+ -+void yaffs_verify_objects(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ int i; -+ struct list_head *lh; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each(lh, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ yaffs_verify_obj(obj); -+ } -+ } -+} -+ -+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) -+{ -+ struct list_head *lh; -+ struct yaffs_obj *list_obj; -+ int count = 0; -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); -+ BUG(); -+ return; -+ } -+ -+ if (yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ if (!obj->parent) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); -+ BUG(); -+ return; -+ } -+ -+ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); -+ BUG(); -+ } -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ list_for_each(lh, &obj->parent->variant.dir_variant.children) { -+ list_obj = list_entry(lh, struct yaffs_obj, siblings); -+ yaffs_verify_obj(list_obj); -+ if (obj == list_obj) -+ count++; -+ } -+ -+ if (count != 1) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Object in directory %d times", -+ count); -+ BUG(); -+ } -+} -+ -+void yaffs_verify_dir(struct yaffs_obj *directory) -+{ -+ struct list_head *lh; -+ struct yaffs_obj *list_obj; -+ -+ if (!directory) { -+ BUG(); -+ return; -+ } -+ -+ if (yaffs_skip_full_verification(directory->my_dev)) -+ return; -+ -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Directory has wrong type: %d", -+ directory->variant_type); -+ BUG(); -+ } -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ list_for_each(lh, &directory->variant.dir_variant.children) { -+ list_obj = list_entry(lh, struct yaffs_obj, siblings); -+ if (list_obj->parent != directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Object in directory list has wrong parent %p", -+ list_obj->parent); -+ BUG(); -+ } -+ yaffs_verify_obj_in_dir(list_obj); -+ } -+} -+ -+static int yaffs_free_verification_failures; -+ -+void yaffs_verify_free_chunks(struct yaffs_dev *dev) -+{ -+ int counted; -+ int difference; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ counted = yaffs_count_free_chunks(dev); -+ -+ difference = dev->n_free_chunks - counted; -+ -+ if (difference) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Freechunks verification failure %d %d %d", -+ dev->n_free_chunks, counted, difference); -+ yaffs_free_verification_failures++; -+ } -+} -+ -+int yaffs_verify_file_sane(struct yaffs_obj *in) -+{ -+ (void) in; -+ return YAFFS_OK; -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_verify.h linux-3.18.14/fs/yaffs2/yaffs_verify.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_verify.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,43 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_VERIFY_H__ -+#define __YAFFS_VERIFY_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, -+ int n); -+void yaffs_verify_collected_blk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, int n); -+void yaffs_verify_blocks(struct yaffs_dev *dev); -+ -+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, -+ struct yaffs_ext_tags *tags, int parent_check); -+void yaffs_verify_file(struct yaffs_obj *obj); -+void yaffs_verify_link(struct yaffs_obj *obj); -+void yaffs_verify_symlink(struct yaffs_obj *obj); -+void yaffs_verify_special(struct yaffs_obj *obj); -+void yaffs_verify_obj(struct yaffs_obj *obj); -+void yaffs_verify_objects(struct yaffs_dev *dev); -+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); -+void yaffs_verify_dir(struct yaffs_obj *directory); -+void yaffs_verify_free_chunks(struct yaffs_dev *dev); -+ -+int yaffs_verify_file_sane(struct yaffs_obj *obj); -+ -+int yaffs_skip_verification(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_vfs.c linux-3.18.14/fs/yaffs2/yaffs_vfs.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_vfs.c 2015-06-15 18:42:09.000000000 +0200 -@@ -0,0 +1,3658 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * Acknowledgements: -+ * Luc van OostenRyck for numerous patches. -+ * Nick Bane for numerous patches. -+ * Nick Bane for 2.5/2.6 integration. -+ * Andras Toth for mknod rdev issue. -+ * Michael Fischer for finding the problem with inode inconsistency. -+ * Some code bodily lifted from JFFS -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * -+ * This is the file system front-end to YAFFS that hooks it up to -+ * the VFS. -+ * -+ * Special notes: -+ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with -+ * this superblock -+ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this -+ * superblock -+ * >> inode->u.generic_ip points to the associated struct yaffs_obj. -+ */ -+ -+/* -+ * There are two variants of the VFS glue code. This variant should compile -+ * for any version of Linux. -+ */ -+#include -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) -+#define YAFFS_COMPILE_BACKGROUND -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) -+#define YAFFS_COMPILE_FREEZER -+#endif -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) -+#define YAFFS_COMPILE_EXPORTFS -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) -+#define YAFFS_USE_SETATTR_COPY -+#define YAFFS_USE_TRUNCATE_SETSIZE -+#endif -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) -+#define YAFFS_HAS_EVICT_INODE -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) -+#define YAFFS_NEW_FOLLOW_LINK 1 -+#else -+#define YAFFS_NEW_FOLLOW_LINK 0 -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) -+#define YAFFS_HAS_WRITE_SUPER -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+#include -+#endif -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+#include -+#endif -+ -+#ifdef YAFFS_COMPILE_BACKGROUND -+#include -+#include -+#endif -+#ifdef YAFFS_COMPILE_FREEZER -+#include -+#endif -+ -+#include -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ -+#include -+ -+#define UnlockPage(p) unlock_page(p) -+#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) -+ -+/* FIXME: use sb->s_id instead ? */ -+#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) -+ -+#else -+ -+#include -+#define BDEVNAME_SIZE 0 -+#define yaffs_devname(sb, buf) kdevname(sb->s_dev) -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) -+/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ -+#define __user -+#endif -+ -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) -+#define YPROC_ROOT (&proc_root) -+#else -+#define YPROC_ROOT NULL -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) -+#define Y_INIT_TIMER(a) init_timer(a) -+#else -+#define Y_INIT_TIMER(a) init_timer_on_stack(a) -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) -+#define YAFFS_USE_WRITE_BEGIN_END 1 -+#else -+#define YAFFS_USE_WRITE_BEGIN_END 0 -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) -+#define YAFFS_SUPER_HAS_DIRTY -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) -+#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) -+static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) -+{ -+ uint64_t result = partition_size; -+ do_div(result, block_size); -+ return (uint32_t) result; -+} -+#else -+#define YCALCBLOCKS(s, b) ((s)/(b)) -+#endif -+ -+#include -+#include -+ -+#include "yportenv.h" -+#include "yaffs_trace.h" -+#include "yaffs_guts.h" -+#include "yaffs_attribs.h" -+ -+#include "yaffs_linux.h" -+ -+#include "yaffs_mtdif.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_getblockinfo.h" -+ -+unsigned int yaffs_trace_mask = -+ YAFFS_TRACE_BAD_BLOCKS | -+ YAFFS_TRACE_ALWAYS | -+ 0; -+ -+unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; -+unsigned int yaffs_auto_checkpoint = 1; -+unsigned int yaffs_gc_control = 1; -+unsigned int yaffs_bg_enable = 1; -+unsigned int yaffs_auto_select = 1; -+/* Module Parameters */ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+module_param(yaffs_trace_mask, uint, 0644); -+module_param(yaffs_wr_attempts, uint, 0644); -+module_param(yaffs_auto_checkpoint, uint, 0644); -+module_param(yaffs_gc_control, uint, 0644); -+module_param(yaffs_bg_enable, uint, 0644); -+#else -+MODULE_PARM(yaffs_trace_mask, "i"); -+MODULE_PARM(yaffs_wr_attempts, "i"); -+MODULE_PARM(yaffs_auto_checkpoint, "i"); -+MODULE_PARM(yaffs_gc_control, "i"); -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) -+/* use iget and read_inode */ -+#define Y_IGET(sb, inum) iget((sb), (inum)) -+ -+#else -+/* Call local equivalent */ -+#define YAFFS_USE_OWN_IGET -+#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) -+ -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) -+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) -+#else -+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) -+#endif -+ -+#define yaffs_inode_to_obj(iptr) \ -+ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) -+#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) -+#else -+#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+#define Y_CLEAR_INODE(i) clear_inode(i) -+#else -+#define Y_CLEAR_INODE(i) end_writeback(i) -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) -+#define YAFFS_USE_DIR_ITERATE -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0)) -+#define YAFFS_NEW_PROCFS -+#include -+#endif -+ -+ -+#define update_dir_time(dir) do {\ -+ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ -+ } while (0) -+ -+static void yaffs_fill_inode_from_obj(struct inode *inode, -+ struct yaffs_obj *obj); -+ -+ -+static void yaffs_gross_lock(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); -+ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); -+} -+ -+static void yaffs_gross_unlock(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); -+ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); -+} -+ -+ -+static int yaffs_readpage_nolock(struct file *f, struct page *pg) -+{ -+ /* Lifted from jffs2 */ -+ -+ struct yaffs_obj *obj; -+ unsigned char *pg_buf; -+ int ret; -+ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readpage_nolock at %lld, size %08x", -+ (long long)pos, -+ (unsigned)PAGE_CACHE_SIZE); -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ BUG_ON(!PageLocked(pg)); -+#else -+ if (!PageLocked(pg)) -+ PAGE_BUG(pg); -+#endif -+ -+ pg_buf = kmap(pg); -+ /* FIXME: Can kmap fail? */ -+ -+ yaffs_gross_lock(dev); -+ -+ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (ret >= 0) -+ ret = 0; -+ -+ if (ret) { -+ ClearPageUptodate(pg); -+ SetPageError(pg); -+ } else { -+ SetPageUptodate(pg); -+ ClearPageError(pg); -+ } -+ -+ flush_dcache_page(pg); -+ kunmap(pg); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); -+ return ret; -+} -+ -+static int yaffs_readpage_unlock(struct file *f, struct page *pg) -+{ -+ int ret = yaffs_readpage_nolock(f, pg); -+ UnlockPage(pg); -+ return ret; -+} -+ -+static int yaffs_readpage(struct file *f, struct page *pg) -+{ -+ int ret; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); -+ ret = yaffs_readpage_unlock(f, pg); -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); -+ return ret; -+} -+ -+ -+static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) -+{ -+ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); -+ -+ if (lc) -+ lc->dirty = val; -+ -+# ifdef YAFFS_SUPER_HAS_DIRTY -+ { -+ struct super_block *sb = lc->super; -+ -+ if (sb) -+ sb->s_dirt = val; -+ } -+#endif -+ -+} -+ -+static void yaffs_set_super_dirty(struct yaffs_dev *dev) -+{ -+ yaffs_set_super_dirty_val(dev, 1); -+} -+ -+static void yaffs_clear_super_dirty(struct yaffs_dev *dev) -+{ -+ yaffs_set_super_dirty_val(dev, 0); -+} -+ -+static int yaffs_check_super_dirty(struct yaffs_dev *dev) -+{ -+ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); -+ -+ if (lc && lc->dirty) -+ return 1; -+ -+# ifdef YAFFS_SUPER_HAS_DIRTY -+ { -+ struct super_block *sb = lc->super; -+ -+ if (sb && sb->s_dirt) -+ return 1; -+ } -+#endif -+ return 0; -+ -+} -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_writepage(struct page *page, struct writeback_control *wbc) -+#else -+static int yaffs_writepage(struct page *page) -+#endif -+{ -+ struct yaffs_dev *dev; -+ struct address_space *mapping = page->mapping; -+ struct inode *inode; -+ unsigned long end_index; -+ char *buffer; -+ struct yaffs_obj *obj; -+ int n_written = 0; -+ unsigned n_bytes; -+ loff_t i_size; -+ -+ if (!mapping) -+ BUG(); -+ inode = mapping->host; -+ if (!inode) -+ BUG(); -+ i_size = i_size_read(inode); -+ -+ end_index = i_size >> PAGE_CACHE_SHIFT; -+ -+ if (page->index < end_index) -+ n_bytes = PAGE_CACHE_SIZE; -+ else { -+ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); -+ -+ if (page->index > end_index || !n_bytes) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_writepage at %lld, inode size = %lld!!", -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, -+ inode->i_size); -+ yaffs_trace(YAFFS_TRACE_OS, -+ " -> don't care!!"); -+ -+ zero_user_segment(page, 0, PAGE_CACHE_SIZE); -+ set_page_writeback(page); -+ unlock_page(page); -+ end_page_writeback(page); -+ return 0; -+ } -+ } -+ -+ if (n_bytes != PAGE_CACHE_SIZE) -+ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); -+ -+ get_page(page); -+ -+ buffer = kmap(page); -+ -+ obj = yaffs_inode_to_obj(inode); -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_writepage at %lld, size %08x", -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "writepag0: obj = %lld, ino = %lld", -+ obj->variant.file_variant.file_size, inode->i_size); -+ -+ n_written = yaffs_wr_file(obj, buffer, -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); -+ -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "writepag1: obj = %lld, ino = %lld", -+ obj->variant.file_variant.file_size, inode->i_size); -+ -+ yaffs_gross_unlock(dev); -+ -+ kunmap(page); -+ set_page_writeback(page); -+ unlock_page(page); -+ end_page_writeback(page); -+ put_page(page); -+ -+ return (n_written == n_bytes) ? 0 : -ENOSPC; -+} -+ -+/* Space holding and freeing is done to ensure we have space available for write_begin/end */ -+/* For now we just assume few parallel writes and check against a small number. */ -+/* Todo: need to do this with a counter to handle parallel reads better */ -+ -+static ssize_t yaffs_hold_space(struct file *f) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ int n_free_chunks; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ n_free_chunks = yaffs_get_n_free_chunks(dev); -+ -+ yaffs_gross_unlock(dev); -+ -+ return (n_free_chunks > 20) ? 1 : 0; -+} -+ -+static void yaffs_release_space(struct file *f) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_gross_unlock(dev); -+} -+ -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+static int yaffs_write_begin(struct file *filp, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned flags, -+ struct page **pagep, void **fsdata) -+{ -+ struct page *pg = NULL; -+ pgoff_t index = pos >> PAGE_CACHE_SHIFT; -+ -+ int ret = 0; -+ int space_held = 0; -+ -+ /* Get a page */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) -+ pg = grab_cache_page_write_begin(mapping, index, flags); -+#else -+ pg = __grab_cache_page(mapping, index); -+#endif -+ -+ *pagep = pg; -+ if (!pg) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "start yaffs_write_begin index %d(%x) uptodate %d", -+ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); -+ -+ /* Get fs space */ -+ space_held = yaffs_hold_space(filp); -+ -+ if (!space_held) { -+ ret = -ENOSPC; -+ goto out; -+ } -+ -+ /* Update page if required */ -+ -+ if (!Page_Uptodate(pg)) -+ ret = yaffs_readpage_nolock(filp, pg); -+ -+ if (ret) -+ goto out; -+ -+ /* Happy path return */ -+ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); -+ -+ return 0; -+ -+out: -+ yaffs_trace(YAFFS_TRACE_OS, -+ "end yaffs_write_begin fail returning %d", ret); -+ if (space_held) -+ yaffs_release_space(filp); -+ if (pg) { -+ unlock_page(pg); -+ page_cache_release(pg); -+ } -+ return ret; -+} -+ -+#else -+ -+static int yaffs_prepare_write(struct file *f, struct page *pg, -+ unsigned offset, unsigned to) -+{ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); -+ -+ if (!Page_Uptodate(pg)) -+ return yaffs_readpage_nolock(f, pg); -+ return 0; -+} -+#endif -+ -+ -+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, -+ loff_t * pos) -+{ -+ struct yaffs_obj *obj; -+ int n_written; -+ loff_t ipos; -+ struct inode *inode; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write: hey obj is null!"); -+ return -EINVAL; -+ } -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ inode = f->f_dentry->d_inode; -+ -+ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) -+ ipos = inode->i_size; -+ else -+ ipos = *pos; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", -+ (unsigned)n, (unsigned)n, obj->obj_id, ipos); -+ -+ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); -+ -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write: %d(%x) bytes written", -+ (unsigned)n, (unsigned)n); -+ -+ if (n_written > 0) { -+ ipos += n_written; -+ *pos = ipos; -+ if (ipos > inode->i_size) { -+ inode->i_size = ipos; -+ inode->i_blocks = (ipos + 511) >> 9; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write size updated to %lld bytes, %d blocks", -+ ipos, (int)(inode->i_blocks)); -+ } -+ -+ } -+ yaffs_gross_unlock(dev); -+ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; -+} -+ -+ -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+static int yaffs_write_end(struct file *filp, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned copied, -+ struct page *pg, void *fsdadata) -+{ -+ int ret = 0; -+ void *addr, *kva; -+ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); -+ -+ kva = kmap(pg); -+ addr = kva + offset_into_page; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_write_end addr %p pos %lld n_bytes %d", -+ addr, pos, copied); -+ -+ ret = yaffs_file_write(filp, addr, copied, &pos); -+ -+ if (ret != copied) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_write_end not same size ret %d copied %d", -+ ret, copied); -+ SetPageError(pg); -+ } -+ -+ kunmap(pg); -+ -+ yaffs_release_space(filp); -+ unlock_page(pg); -+ page_cache_release(pg); -+ return ret; -+} -+#else -+ -+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, -+ unsigned to) -+{ -+ void *addr, *kva; -+ -+ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; -+ int n_bytes = to - offset; -+ int n_written; -+ -+ kva = kmap(pg); -+ addr = kva + offset; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write addr %p pos %lld n_bytes %d", -+ addr, pos, n_bytes); -+ -+ n_written = yaffs_file_write(f, addr, n_bytes, &pos); -+ -+ if (n_written != n_bytes) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write not same size n_written %d n_bytes %d", -+ n_written, n_bytes); -+ SetPageError(pg); -+ } -+ kunmap(pg); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write returning %d", -+ n_written == n_bytes ? 0 : n_written); -+ -+ return n_written == n_bytes ? 0 : n_written; -+} -+#endif -+ -+static struct address_space_operations yaffs_file_address_operations = { -+ .readpage = yaffs_readpage, -+ .writepage = yaffs_writepage, -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+ .write_begin = yaffs_write_begin, -+ .write_end = yaffs_write_end, -+#else -+ .prepare_write = yaffs_prepare_write, -+ .commit_write = yaffs_commit_write, -+#endif -+}; -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_file_flush(struct file *file, fl_owner_t id) -+#else -+static int yaffs_file_flush(struct file *file) -+#endif -+{ -+ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); -+ -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_flush object %d (%s)", -+ obj->obj_id, -+ obj->dirty ? "dirty" : "clean"); -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_flush_file(obj, 1, 0, 0); -+ -+ yaffs_gross_unlock(dev); -+ -+ return 0; -+} -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) -+static int yaffs_sync_object(struct file *file, int datasync) -+#else -+static int yaffs_sync_object(struct file *file, struct dentry *dentry, -+ int datasync) -+#endif -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) -+ struct dentry *dentry = file->f_path.dentry; -+#endif -+ -+ obj = yaffs_dentry_to_obj(dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, -+ "yaffs_sync_object"); -+ yaffs_gross_lock(dev); -+ yaffs_flush_file(obj, 1, datasync, 0); -+ yaffs_gross_unlock(dev); -+ return 0; -+} -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) -+static const struct file_operations yaffs_file_operations = { -+ .read = do_sync_read, -+ .write = do_sync_write, -+ .read_iter = generic_file_read_iter, -+ .write_iter = generic_file_write_iter, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+ .splice_read = generic_file_splice_read, -+ .splice_write = iter_file_splice_write, -+ .llseek = generic_file_llseek, -+}; -+ -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) -+ -+static const struct file_operations yaffs_file_operations = { -+ .read = do_sync_read, -+ .write = do_sync_write, -+ .aio_read = generic_file_aio_read, -+ .aio_write = generic_file_aio_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+ .sendfile = generic_file_sendfile, -+}; -+ -+#else -+ -+static const struct file_operations yaffs_file_operations = { -+ .read = generic_file_read, -+ .write = generic_file_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ .sendfile = generic_file_sendfile, -+#endif -+}; -+#endif -+ -+ -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) -+static void zero_user_segment(struct page *page, unsigned start, unsigned end) -+{ -+ void *kaddr = kmap_atomic(page, KM_USER0); -+ memset(kaddr + start, 0, end - start); -+ kunmap_atomic(kaddr, KM_USER0); -+ flush_dcache_page(page); -+} -+#endif -+ -+ -+static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) -+{ -+#ifdef YAFFS_USE_TRUNCATE_SETSIZE -+ truncate_setsize(inode, newsize); -+ return 0; -+#else -+ truncate_inode_pages(&inode->i_data, newsize); -+ return 0; -+#endif -+ -+} -+ -+ -+static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) -+{ -+#ifdef YAFFS_USE_SETATTR_COPY -+ setattr_copy(inode, attr); -+ return 0; -+#else -+ return inode_setattr(inode, attr); -+#endif -+ -+} -+ -+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_setattr of object %d", -+ yaffs_inode_to_obj(inode)->obj_id); -+#if 0 -+ /* Fail if a requested resize >= 2GB */ -+ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) -+ error = -EINVAL; -+#endif -+ -+ if (error == 0) -+ error = inode_change_ok(inode, attr); -+ if (error == 0) { -+ int result; -+ if (!error) { -+ error = yaffs_vfs_setattr(inode, attr); -+ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); -+ if (attr->ia_valid & ATTR_SIZE) { -+ yaffs_vfs_setsize(inode, attr->ia_size); -+ inode->i_blocks = (inode->i_size + 511) >> 9; -+ } -+ } -+ dev = yaffs_inode_to_obj(inode)->my_dev; -+ if (attr->ia_valid & ATTR_SIZE) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "resize to %d(%x)", -+ (int)(attr->ia_size), -+ (int)(attr->ia_size)); -+ } -+ yaffs_gross_lock(dev); -+ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); -+ if (result == YAFFS_OK) { -+ error = 0; -+ } else { -+ error = -EPERM; -+ } -+ yaffs_gross_unlock(dev); -+ -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); -+ -+ return error; -+} -+ -+static int yaffs_setxattr(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ int result; -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ result = yaffs_set_xattrib(obj, name, value, size, flags); -+ if (result == YAFFS_OK) -+ error = 0; -+ else if (result < 0) -+ error = result; -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); -+ -+ return error; -+} -+ -+static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, -+ void *buff, size_t size) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_getxattr \"%s\" from object %d", -+ name, obj->obj_id); -+ -+ if (error == 0) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ error = yaffs_get_xattrib(obj, name, buff, size); -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); -+ -+ return error; -+} -+ -+static int yaffs_removexattr(struct dentry *dentry, const char *name) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_removexattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ int result; -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ result = yaffs_remove_xattrib(obj, name); -+ if (result == YAFFS_OK) -+ error = 0; -+ else if (result < 0) -+ error = result; -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_removexattr done returning %d", error); -+ -+ return error; -+} -+ -+static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_listxattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ error = yaffs_list_xattrib(obj, buff, size); -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_listxattr done returning %d", error); -+ -+ return error; -+} -+ -+ -+static const struct inode_operations yaffs_file_inode_operations = { -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+ -+static int yaffs_readlink(struct dentry *dentry, char __user * buffer, -+ int buflen) -+{ -+ unsigned char *alias; -+ int ret; -+ -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (!alias) -+ return -ENOMEM; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) -+ ret = vfs_readlink(dentry, buffer, buflen, alias); -+#else -+ ret = readlink_copy(buffer, buflen, alias); -+#endif -+ kfree(alias); -+ return ret; -+} -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ void *ret; -+#else -+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ int ret -+#endif -+ unsigned char *alias; -+ int ret_int = 0; -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); -+ yaffs_gross_unlock(dev); -+ -+ if (!alias) { -+ ret_int = -ENOMEM; -+ goto out; -+ } -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+ nd_set_link(nd, alias); -+ ret = alias; -+out: -+ if (ret_int) -+ ret = ERR_PTR(ret_int); -+ return ret; -+#else -+ ret = vfs_follow_link(nd, alias); -+ kfree(alias); -+out: -+ if (ret_int) -+ ret = ret_int; -+ return ret; -+#endif -+} -+ -+ -+#ifdef YAFFS_HAS_PUT_INODE -+ -+/* For now put inode is just for debugging -+ * Put inode is called when the inode **structure** is put. -+ */ -+static void yaffs_put_inode(struct inode *inode) -+{ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_put_inode: ino %d, count %d"), -+ (int)inode->i_ino, atomic_read(&inode->i_count); -+ -+} -+#endif -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) -+{ -+ kfree(alias); -+} -+#endif -+ -+static const struct inode_operations yaffs_symlink_inode_operations = { -+ .readlink = yaffs_readlink, -+ .follow_link = yaffs_follow_link, -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+ .put_link = yaffs_put_link, -+#endif -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+#ifdef YAFFS_USE_OWN_IGET -+ -+static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) -+{ -+ struct inode *inode; -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); -+ -+ inode = iget_locked(sb, ino); -+ if (!inode) -+ return ERR_PTR(-ENOMEM); -+ if (!(inode->i_state & I_NEW)) -+ return inode; -+ -+ /* NB This is called as a side effect of other functions, but -+ * we had to release the lock to prevent deadlocks, so -+ * need to lock again. -+ */ -+ -+ yaffs_gross_lock(dev); -+ -+ obj = yaffs_find_by_number(dev, inode->i_ino); -+ -+ yaffs_fill_inode_from_obj(inode, obj); -+ -+ yaffs_gross_unlock(dev); -+ -+ unlock_new_inode(inode); -+ return inode; -+} -+ -+#else -+ -+static void yaffs_read_inode(struct inode *inode) -+{ -+ /* NB This is called as a side effect of other functions, but -+ * we had to release the lock to prevent deadlocks, so -+ * need to lock again. -+ */ -+ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_inode for %d", (int)inode->i_ino); -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_lock(dev); -+ -+ obj = yaffs_find_by_number(dev, inode->i_ino); -+ -+ yaffs_fill_inode_from_obj(inode, obj); -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_unlock(dev); -+} -+ -+#endif -+ -+ -+ -+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, -+ struct yaffs_obj *obj) -+{ -+ struct inode *inode; -+ -+ if (!sb) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for NULL super_block!!"); -+ return NULL; -+ -+ } -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for NULL object!!"); -+ return NULL; -+ -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for object %d", obj->obj_id); -+ -+ inode = Y_IGET(sb, obj->obj_id); -+ if (IS_ERR(inode)) -+ return NULL; -+ -+ /* NB Side effect: iget calls back to yaffs_read_inode(). */ -+ /* iget also increments the inode's i_count */ -+ /* NB You can't be holding gross_lock or deadlock will happen! */ -+ -+ return inode; -+} -+ -+ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -+#define YCRED(x) x -+#else -+#define YCRED(x) (x->cred) -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) -+#define YPROC_uid(p) (YCRED(p)->fsuid) -+#define YPROC_gid(p) (YCRED(p)->fsgid) -+#define EXTRACT_gid(x) x -+#define EXTRACT_uid(x) x -+#define MAKE_gid(x) x -+#define MAKE_uid(x) x -+#else -+#define YPROC_uid(p) from_kuid(&init_user_ns, YCRED(p)->fsuid) -+#define YPROC_gid(p) from_kgid(&init_user_ns, YCRED(p)->fsgid) -+#define EXTRACT_gid(x) from_kgid(&init_user_ns, x) -+#define EXTRACT_uid(x) from_kuid(&init_user_ns, x) -+#define MAKE_gid(x) make_kgid(&init_user_ns, x) -+#define MAKE_uid(x) make_kuid(&init_user_ns, x) -+#endif -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t rdev) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, -+ dev_t rdev) -+#else -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, -+ int rdev) -+#endif -+{ -+ struct inode *inode; -+ -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_dev *dev; -+ -+ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); -+ -+ int error = -ENOSPC; -+ uid_t uid = YPROC_uid(current); -+ gid_t gid = -+ (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); -+ -+ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) -+ mode |= S_ISGID; -+ -+ if (parent) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: parent object %d type %d", -+ parent->obj_id, parent->variant_type); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: could not get parent object"); -+ return -EPERM; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: making oject for %s, mode %x dev %x", -+ dentry->d_name.name, mode, rdev); -+ -+ dev = parent->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ switch (mode & S_IFMT) { -+ default: -+ /* Special (socket, fifo, device...) */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ obj = -+ yaffs_create_special(parent, dentry->d_name.name, mode, uid, -+ gid, old_encode_dev(rdev)); -+#else -+ obj = -+ yaffs_create_special(parent, dentry->d_name.name, mode, uid, -+ gid, rdev); -+#endif -+ break; -+ case S_IFREG: /* file */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); -+ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, -+ gid); -+ break; -+ case S_IFDIR: /* directory */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); -+ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, -+ uid, gid); -+ break; -+ case S_IFLNK: /* symlink */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); -+ obj = NULL; /* Do we ever get here? */ -+ break; -+ } -+ -+ /* Can not call yaffs_get_inode() with gross lock held */ -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); -+ d_instantiate(dentry, inode); -+ update_dir_time(dir); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod created object %d count = %d", -+ obj->obj_id, atomic_read(&inode->i_count)); -+ error = 0; -+ yaffs_fill_inode_from_obj(dir, parent); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); -+ error = -ENOMEM; -+ } -+ -+ return error; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -+#else -+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) -+#endif -+{ -+ int ret_val; -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); -+ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); -+ return ret_val; -+} -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool dummy) -+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ struct nameidata *n) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, -+ struct nameidata *n) -+#else -+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) -+#endif -+{ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); -+ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, -+ unsigned int dummy) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, -+ struct nameidata *n) -+#else -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) -+#endif -+{ -+ struct yaffs_obj *obj; -+ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ -+ -+ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_lock(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", -+ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); -+ -+ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); -+ -+ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ -+ -+ /* Can't hold gross lock when calling yaffs_get_inode() */ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_lookup found %d", obj->obj_id); -+ -+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); -+ -+ } -+ -+/* added NCB for 2.5/6 compatability - forces add even if inode is -+ * NULL which creates dentry hash */ -+ d_add(dentry, inode); -+ -+ return NULL; -+} -+ -+/* -+ * Create a link... -+ */ -+static int yaffs_link(struct dentry *old_dentry, struct inode *dir, -+ struct dentry *dentry) -+{ -+ struct inode *inode = old_dentry->d_inode; -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_obj *link = NULL; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); -+ -+ obj = yaffs_inode_to_obj(inode); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ -+ link = -+ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, -+ obj); -+ -+ if (link) { -+ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); -+ d_instantiate(dentry, old_dentry->d_inode); -+ atomic_inc(&old_dentry->d_inode->i_count); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_link link count %d i_count %d", -+ old_dentry->d_inode->i_nlink, -+ atomic_read(&old_dentry->d_inode->i_count)); -+ } -+ -+ yaffs_gross_unlock(dev); -+ -+ if (link) { -+ update_dir_time(dir); -+ return 0; -+ } -+ -+ return -EPERM; -+} -+ -+static int yaffs_symlink(struct inode *dir, struct dentry *dentry, -+ const char *symname) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ uid_t uid = YPROC_uid(current); -+ gid_t gid = -+ (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); -+ -+ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > -+ YAFFS_MAX_NAME_LENGTH) -+ return -ENAMETOOLONG; -+ -+ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > -+ YAFFS_MAX_ALIAS_LENGTH) -+ return -ENAMETOOLONG; -+ -+ dev = yaffs_inode_to_obj(dir)->my_dev; -+ yaffs_gross_lock(dev); -+ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, -+ S_IFLNK | S_IRWXUGO, uid, gid, symname); -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ struct inode *inode; -+ -+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); -+ d_instantiate(dentry, inode); -+ update_dir_time(dir); -+ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); -+ return 0; -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); -+ } -+ -+ return -ENOMEM; -+} -+ -+/* -+ * The VFS layer already does all the dentry stuff for rename. -+ * -+ * NB: POSIX says you can rename an object over an old object of the same name -+ */ -+static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, -+ struct inode *new_dir, struct dentry *new_dentry) -+{ -+ struct yaffs_dev *dev; -+ int ret_val = YAFFS_FAIL; -+ struct yaffs_obj *target; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); -+ dev = yaffs_inode_to_obj(old_dir)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ /* Check if the target is an existing directory that is not empty. */ -+ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), -+ new_dentry->d_name.name); -+ -+ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && -+ !list_empty(&target->variant.dir_variant.children)) { -+ -+ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); -+ -+ ret_val = YAFFS_FAIL; -+ } else { -+ /* Now does unlinking internally using shadowing mechanism */ -+ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); -+ -+ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), -+ old_dentry->d_name.name, -+ yaffs_inode_to_obj(new_dir), -+ new_dentry->d_name.name); -+ } -+ yaffs_gross_unlock(dev); -+ -+ if (ret_val == YAFFS_OK) { -+ if (target) -+ inode_dec_link_count(new_dentry->d_inode); -+ -+ update_dir_time(old_dir); -+ if (old_dir != new_dir) -+ update_dir_time(new_dir); -+ return 0; -+ } else { -+ return -ENOTEMPTY; -+ } -+} -+ -+ -+ -+ -+static int yaffs_unlink(struct inode *dir, struct dentry *dentry) -+{ -+ int ret_val; -+ -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", -+ (int)(dir->i_ino), dentry->d_name.name); -+ obj = yaffs_inode_to_obj(dir); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ ret_val = yaffs_unlinker(obj, dentry->d_name.name); -+ -+ if (ret_val == YAFFS_OK) { -+ inode_dec_link_count(dentry->d_inode); -+ dir->i_version++; -+ yaffs_gross_unlock(dev); -+ update_dir_time(dir); -+ return 0; -+ } -+ yaffs_gross_unlock(dev); -+ return -ENOTEMPTY; -+} -+ -+ -+ -+static const struct inode_operations yaffs_dir_inode_operations = { -+ .create = yaffs_create, -+ .lookup = yaffs_lookup, -+ .link = yaffs_link, -+ .unlink = yaffs_unlink, -+ .symlink = yaffs_symlink, -+ .mkdir = yaffs_mkdir, -+ .rmdir = yaffs_unlink, -+ .mknod = yaffs_mknod, -+ .rename = yaffs_rename, -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+/*-----------------------------------------------------------------*/ -+/* Directory search context allows us to unlock access to yaffs during -+ * filldir without causing problems with the directory being modified. -+ * This is similar to the tried and tested mechanism used in yaffs direct. -+ * -+ * A search context iterates along a doubly linked list of siblings in the -+ * directory. If the iterating object is deleted then this would corrupt -+ * the list iteration, likely causing a crash. The search context avoids -+ * this by using the remove_obj_fn to move the search context to the -+ * next object before the object is deleted. -+ * -+ * Many readdirs (and thus seach conexts) may be alive simulateously so -+ * each struct yaffs_dev has a list of these. -+ * -+ * A seach context lives for the duration of a readdir. -+ * -+ * All these functions must be called while yaffs is locked. -+ */ -+ -+struct yaffs_search_context { -+ struct yaffs_dev *dev; -+ struct yaffs_obj *dir_obj; -+ struct yaffs_obj *next_return; -+ struct list_head others; -+}; -+ -+/* -+ * yaffs_new_search() creates a new search context, initialises it and -+ * adds it to the device's search context list. -+ * -+ * Called at start of readdir. -+ */ -+static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) -+{ -+ struct yaffs_dev *dev = dir->my_dev; -+ struct yaffs_search_context *sc = -+ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); -+ if (sc) { -+ sc->dir_obj = dir; -+ sc->dev = dev; -+ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) -+ sc->next_return = NULL; -+ else -+ sc->next_return = -+ list_entry(dir->variant.dir_variant.children.next, -+ struct yaffs_obj, siblings); -+ INIT_LIST_HEAD(&sc->others); -+ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); -+ } -+ return sc; -+} -+ -+/* -+ * yaffs_search_end() disposes of a search context and cleans up. -+ */ -+static void yaffs_search_end(struct yaffs_search_context *sc) -+{ -+ if (sc) { -+ list_del(&sc->others); -+ kfree(sc); -+ } -+} -+ -+/* -+ * yaffs_search_advance() moves a search context to the next object. -+ * Called when the search iterates or when an object removal causes -+ * the search context to be moved to the next object. -+ */ -+static void yaffs_search_advance(struct yaffs_search_context *sc) -+{ -+ if (!sc) -+ return; -+ -+ if (sc->next_return == NULL || -+ list_empty(&sc->dir_obj->variant.dir_variant.children)) -+ sc->next_return = NULL; -+ else { -+ struct list_head *next = sc->next_return->siblings.next; -+ -+ if (next == &sc->dir_obj->variant.dir_variant.children) -+ sc->next_return = NULL; /* end of list */ -+ else -+ sc->next_return = -+ list_entry(next, struct yaffs_obj, siblings); -+ } -+} -+ -+/* -+ * yaffs_remove_obj_callback() is called when an object is unlinked. -+ * We check open search contexts and advance any which are currently -+ * on the object being iterated. -+ */ -+static void yaffs_remove_obj_callback(struct yaffs_obj *obj) -+{ -+ -+ struct list_head *i; -+ struct yaffs_search_context *sc; -+ struct list_head *search_contexts = -+ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); -+ -+ /* Iterate through the directory search contexts. -+ * If any are currently on the object being removed, then advance -+ * the search context to the next object to prevent a hanging pointer. -+ */ -+ list_for_each(i, search_contexts) { -+ sc = list_entry(i, struct yaffs_search_context, others); -+ if (sc->next_return == obj) -+ yaffs_search_advance(sc); -+ } -+ -+} -+ -+ -+/*-----------------------------------------------------------------*/ -+ -+#ifdef YAFFS_USE_DIR_ITERATE -+static int yaffs_iterate(struct file *f, struct dir_context *dc) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ struct yaffs_search_context *sc; -+ unsigned long curoffs; -+ struct yaffs_obj *l; -+ int ret_val = 0; -+ -+ char name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_dev_to_lc(dev)->readdir_process = current; -+ -+ sc = yaffs_new_search(obj); -+ if (!sc) { -+ ret_val = -ENOMEM; -+ goto out; -+ } -+ -+ if (!dir_emit_dots(f, dc)) -+ return 0; -+ -+ curoffs = 1; -+ -+ while (sc->next_return) { -+ curoffs++; -+ l = sc->next_return; -+ if (curoffs >= dc->pos) { -+ int this_inode = yaffs_get_obj_inode(l); -+ int this_type = yaffs_get_obj_type(l); -+ -+ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: %s inode %d", -+ name, yaffs_get_obj_inode(l)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (!dir_emit(dc, -+ name, -+ strlen(name), -+ this_inode, -+ this_type)) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ -+ yaffs_gross_lock(dev); -+ -+ dc->pos++; -+ f->f_pos++; -+ } -+ yaffs_search_advance(sc); -+ } -+ -+out: -+ yaffs_search_end(sc); -+ yaffs_dev_to_lc(dev)->readdir_process = NULL; -+ yaffs_gross_unlock(dev); -+ -+ return ret_val; -+} -+ -+#else -+ -+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ struct yaffs_search_context *sc; -+ struct inode *inode = f->f_dentry->d_inode; -+ unsigned long offset, curoffs; -+ struct yaffs_obj *l; -+ int ret_val = 0; -+ -+ char name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_dev_to_lc(dev)->readdir_process = current; -+ -+ offset = f->f_pos; -+ -+ sc = yaffs_new_search(obj); -+ if (!sc) { -+ ret_val = -ENOMEM; -+ goto out; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: starting at %d", (int)offset); -+ -+ if (offset == 0) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry . ino %d", -+ (int)inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ f->f_pos++; -+ } -+ if (offset == 1) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry .. ino %d", -+ (int)f->f_dentry->d_parent->d_inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (filldir(dirent, "..", 2, offset, -+ f->f_dentry->d_parent->d_inode->i_ino, -+ DT_DIR) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ f->f_pos++; -+ } -+ -+ curoffs = 1; -+ -+ /* If the directory has changed since the open or last call to -+ readdir, rewind to after the 2 canned entries. */ -+ if (f->f_version != inode->i_version) { -+ offset = 2; -+ f->f_pos = offset; -+ f->f_version = inode->i_version; -+ } -+ -+ while (sc->next_return) { -+ curoffs++; -+ l = sc->next_return; -+ if (curoffs >= offset) { -+ int this_inode = yaffs_get_obj_inode(l); -+ int this_type = yaffs_get_obj_type(l); -+ -+ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: %s inode %d", -+ name, yaffs_get_obj_inode(l)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (filldir(dirent, -+ name, -+ strlen(name), -+ offset, this_inode, this_type) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ -+ yaffs_gross_lock(dev); -+ -+ offset++; -+ f->f_pos++; -+ } -+ yaffs_search_advance(sc); -+ } -+ -+out: -+ yaffs_search_end(sc); -+ yaffs_dev_to_lc(dev)->readdir_process = NULL; -+ yaffs_gross_unlock(dev); -+ -+ return ret_val; -+} -+ -+#endif -+ -+static const struct file_operations yaffs_dir_operations = { -+ .read = generic_read_dir, -+#ifdef YAFFS_USE_DIR_ITERATE -+ .iterate = yaffs_iterate, -+#else -+ .readdir = yaffs_readdir, -+#endif -+ .fsync = yaffs_sync_object, -+ .llseek = generic_file_llseek, -+}; -+ -+static void yaffs_fill_inode_from_obj(struct inode *inode, -+ struct yaffs_obj *obj) -+{ -+ if (inode && obj) { -+ -+ /* Check mode against the variant type and attempt to repair if broken. */ -+ u32 mode = obj->yst_mode; -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (!S_ISREG(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFREG; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ if (!S_ISLNK(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFLNK; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ if (!S_ISDIR(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFDIR; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ default: -+ /* TODO? */ -+ break; -+ } -+ -+ inode->i_flags |= S_NOATIME; -+ -+ inode->i_ino = obj->obj_id; -+ inode->i_mode = obj->yst_mode; -+ inode->i_uid = MAKE_uid(obj->yst_uid); -+ inode->i_gid = MAKE_gid(obj->yst_gid); -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+ inode->i_blksize = inode->i_sb->s_blocksize; -+#endif -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ -+ inode->i_rdev = old_decode_dev(obj->yst_rdev); -+ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); -+ inode->i_atime.tv_nsec = 0; -+ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; -+ inode->i_mtime.tv_nsec = 0; -+ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; -+ inode->i_ctime.tv_nsec = 0; -+#else -+ inode->i_rdev = obj->yst_rdev; -+ inode->i_atime = obj->yst_atime; -+ inode->i_mtime = obj->yst_mtime; -+ inode->i_ctime = obj->yst_ctime; -+#endif -+ inode->i_size = yaffs_get_obj_length(obj); -+ inode->i_blocks = (inode->i_size + 511) >> 9; -+ -+ set_nlink(inode, yaffs_get_obj_link_count(obj)); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", -+ inode->i_mode, obj->yst_uid, obj->yst_gid, -+ inode->i_size, atomic_read(&inode->i_count)); -+ -+ switch (obj->yst_mode & S_IFMT) { -+ default: /* fifo, device or socket */ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ init_special_inode(inode, obj->yst_mode, -+ old_decode_dev(obj->yst_rdev)); -+#else -+ init_special_inode(inode, obj->yst_mode, -+ (dev_t) (obj->yst_rdev)); -+#endif -+ break; -+ case S_IFREG: /* file */ -+ inode->i_op = &yaffs_file_inode_operations; -+ inode->i_fop = &yaffs_file_operations; -+ inode->i_mapping->a_ops = -+ &yaffs_file_address_operations; -+ break; -+ case S_IFDIR: /* directory */ -+ inode->i_op = &yaffs_dir_inode_operations; -+ inode->i_fop = &yaffs_dir_operations; -+ break; -+ case S_IFLNK: /* symlink */ -+ inode->i_op = &yaffs_symlink_inode_operations; -+ break; -+ } -+ -+ yaffs_inode_to_obj_lv(inode) = obj; -+ -+ obj->my_inode = inode; -+ -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_fill_inode invalid parameters"); -+ } -+ -+} -+ -+ -+ -+/* -+ * yaffs background thread functions . -+ * yaffs_bg_thread_fn() the thread function -+ * yaffs_bg_start() launches the background thread. -+ * yaffs_bg_stop() cleans up the background thread. -+ * -+ * NB: -+ * The thread should only run after the yaffs is initialised -+ * The thread should be stopped before yaffs is unmounted. -+ * The thread should not do any writing while the fs is in read only. -+ */ -+ -+static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) -+{ -+ unsigned erased_chunks = -+ dev->n_erased_blocks * dev->param.chunks_per_block; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ unsigned scattered = 0; /* Free chunks not in an erased block */ -+ -+ if (erased_chunks < dev->n_free_chunks) -+ scattered = (dev->n_free_chunks - erased_chunks); -+ -+ if (!context->bg_running) -+ return 0; -+ else if (scattered < (dev->param.chunks_per_block * 2)) -+ return 0; -+ else if (erased_chunks > dev->n_free_chunks / 2) -+ return 0; -+ else if (erased_chunks > dev->n_free_chunks / 4) -+ return 1; -+ else -+ return 2; -+} -+ -+#ifdef YAFFS_COMPILE_BACKGROUND -+ -+void yaffs_background_waker(unsigned long data) -+{ -+ wake_up_process((struct task_struct *)data); -+} -+ -+static int yaffs_bg_thread_fn(void *data) -+{ -+ struct yaffs_dev *dev = (struct yaffs_dev *)data; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ unsigned long now = jiffies; -+ unsigned long next_dir_update = now; -+ unsigned long next_gc = now; -+ unsigned long expires; -+ unsigned int urgency; -+ -+ int gc_result; -+ struct timer_list timer; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "yaffs_background starting for dev %p", (void *)dev); -+ -+#ifdef YAFFS_COMPILE_FREEZER -+ set_freezable(); -+#endif -+ while (context->bg_running) { -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); -+ -+ if (kthread_should_stop()) -+ break; -+ -+#ifdef YAFFS_COMPILE_FREEZER -+ if (try_to_freeze()) -+ continue; -+#endif -+ yaffs_gross_lock(dev); -+ -+ now = jiffies; -+ -+ if (time_after(now, next_dir_update) && yaffs_bg_enable) { -+ yaffs_update_dirty_dirs(dev); -+ next_dir_update = now + HZ; -+ } -+ -+ if (time_after(now, next_gc) && yaffs_bg_enable) { -+ if (!dev->is_checkpointed) { -+ urgency = yaffs_bg_gc_urgency(dev); -+ gc_result = yaffs_bg_gc(dev, urgency); -+ if (urgency > 1) -+ next_gc = now + HZ / 20 + 1; -+ else if (urgency > 0) -+ next_gc = now + HZ / 10 + 1; -+ else -+ next_gc = now + HZ * 2; -+ } else { -+ /* -+ * gc not running so set to next_dir_update -+ * to cut down on wake ups -+ */ -+ next_gc = next_dir_update; -+ } -+ } -+ yaffs_gross_unlock(dev); -+#if 1 -+ expires = next_dir_update; -+ if (time_before(next_gc, expires)) -+ expires = next_gc; -+ if (time_before(expires, now)) -+ expires = now + HZ; -+ -+ Y_INIT_TIMER(&timer); -+ timer.expires = expires + 1; -+ timer.data = (unsigned long)current; -+ timer.function = yaffs_background_waker; -+ -+ set_current_state(TASK_INTERRUPTIBLE); -+ add_timer(&timer); -+ schedule(); -+ del_timer_sync(&timer); -+#else -+ msleep(10); -+#endif -+ } -+ -+ return 0; -+} -+ -+static int yaffs_bg_start(struct yaffs_dev *dev) -+{ -+ int retval = 0; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ -+ if (dev->read_only) -+ return -1; -+ -+ context->bg_running = 1; -+ -+ context->bg_thread = kthread_run(yaffs_bg_thread_fn, -+ (void *)dev, "yaffs-bg-%d", -+ context->mount_id); -+ -+ if (IS_ERR(context->bg_thread)) { -+ retval = PTR_ERR(context->bg_thread); -+ context->bg_thread = NULL; -+ context->bg_running = 0; -+ } -+ return retval; -+} -+ -+static void yaffs_bg_stop(struct yaffs_dev *dev) -+{ -+ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); -+ -+ ctxt->bg_running = 0; -+ -+ if (ctxt->bg_thread) { -+ kthread_stop(ctxt->bg_thread); -+ ctxt->bg_thread = NULL; -+ } -+} -+#else -+static int yaffs_bg_thread_fn(void *data) -+{ -+ return 0; -+} -+ -+static int yaffs_bg_start(struct yaffs_dev *dev) -+{ -+ return 0; -+} -+ -+static void yaffs_bg_stop(struct yaffs_dev *dev) -+{ -+} -+#endif -+ -+ -+static void yaffs_flush_inodes(struct super_block *sb) -+{ -+ struct inode *iptr; -+ struct yaffs_obj *obj; -+ -+ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { -+ obj = yaffs_inode_to_obj(iptr); -+ if (obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "flushing obj %d", -+ obj->obj_id); -+ yaffs_flush_file(obj, 1, 0, 0); -+ } -+ } -+} -+ -+static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ if (!dev) -+ return; -+ -+ yaffs_flush_inodes(sb); -+ yaffs_update_dirty_dirs(dev); -+ yaffs_flush_whole_cache(dev, 1); -+ if (do_checkpoint) -+ yaffs_checkpoint_save(dev); -+} -+ -+static LIST_HEAD(yaffs_context_list); -+struct mutex yaffs_context_lock; -+ -+static void yaffs_put_super(struct super_block *sb) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, -+ "yaffs_put_super"); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, -+ "Shutting down yaffs background thread"); -+ yaffs_bg_stop(dev); -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, -+ "yaffs background thread shut down"); -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_flush_super(sb, 1); -+ -+ yaffs_deinitialise(dev); -+ -+ yaffs_gross_unlock(dev); -+ -+ mutex_lock(&yaffs_context_lock); -+ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); -+ mutex_unlock(&yaffs_context_lock); -+ -+ if (yaffs_dev_to_lc(dev)->spare_buffer) { -+ kfree(yaffs_dev_to_lc(dev)->spare_buffer); -+ yaffs_dev_to_lc(dev)->spare_buffer = NULL; -+ } -+ -+ kfree(dev); -+ -+ yaffs_put_mtd_device(mtd); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, -+ "yaffs_put_super done"); -+} -+ -+ -+static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) -+{ -+ return yaffs_gc_control; -+} -+ -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+ -+static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, -+ uint32_t generation) -+{ -+ return Y_IGET(sb, ino); -+} -+ -+static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, -+ struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, -+ yaffs2_nfs_get_inode); -+} -+ -+static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, -+ struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ return generic_fh_to_parent(sb, fid, fh_len, fh_type, -+ yaffs2_nfs_get_inode); -+} -+ -+struct dentry *yaffs2_get_parent(struct dentry *dentry) -+{ -+ -+ struct super_block *sb = dentry->d_inode->i_sb; -+ struct dentry *parent = ERR_PTR(-ENOENT); -+ struct inode *inode; -+ unsigned long parent_ino; -+ struct yaffs_obj *d_obj; -+ struct yaffs_obj *parent_obj; -+ -+ d_obj = yaffs_inode_to_obj(dentry->d_inode); -+ -+ if (d_obj) { -+ parent_obj = d_obj->parent; -+ if (parent_obj) { -+ parent_ino = yaffs_get_obj_inode(parent_obj); -+ inode = Y_IGET(sb, parent_ino); -+ -+ if (IS_ERR(inode)) { -+ parent = ERR_CAST(inode); -+ } else { -+ parent = d_obtain_alias(inode); -+ if (!IS_ERR(parent)) { -+ parent = ERR_PTR(-ENOMEM); -+ iput(inode); -+ } -+ } -+ } -+ } -+ -+ return parent; -+} -+ -+/* Just declare a zero structure as a NULL value implies -+ * using the default functions of exportfs. -+ */ -+ -+static struct export_operations yaffs_export_ops = { -+ .fh_to_dentry = yaffs2_fh_to_dentry, -+ .fh_to_parent = yaffs2_fh_to_parent, -+ .get_parent = yaffs2_get_parent, -+}; -+ -+#endif -+ -+static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) -+{ -+ /* Clear the association between the inode and -+ * the struct yaffs_obj. -+ */ -+ obj->my_inode = NULL; -+ yaffs_inode_to_obj_lv(inode) = NULL; -+ -+ /* If the object freeing was deferred, then the real -+ * free happens now. -+ * This should fix the inode inconsistency problem. -+ */ -+ yaffs_handle_defered_free(obj); -+} -+ -+#ifdef YAFFS_HAS_EVICT_INODE -+/* yaffs_evict_inode combines into one operation what was previously done in -+ * yaffs_clear_inode() and yaffs_delete_inode() -+ * -+ */ -+static void yaffs_evict_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ int deleteme = 0; -+ -+ obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_evict_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (!inode->i_nlink && !is_bad_inode(inode)) -+ deleteme = 1; -+ truncate_inode_pages(&inode->i_data, 0); -+ Y_CLEAR_INODE(inode); -+ -+ if (deleteme && obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_del_obj(obj); -+ yaffs_gross_unlock(dev); -+ } -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_unstitch_obj(inode, obj); -+ yaffs_gross_unlock(dev); -+ } -+} -+#else -+ -+/* clear is called to tell the fs to release any per-inode data it holds. -+ * The object might still exist on disk and is just being thrown out of the cache -+ * or else the object has actually been deleted and we're being called via -+ * the chain -+ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() -+ */ -+ -+static void yaffs_clear_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_clear_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_unstitch_obj(inode, obj); -+ yaffs_gross_unlock(dev); -+ } -+ -+} -+ -+/* delete is called when the link count is zero and the inode -+ * is put (ie. nobody wants to know about it anymore, time to -+ * delete the file). -+ * NB Must call clear_inode() -+ */ -+static void yaffs_delete_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_delete_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_del_obj(obj); -+ yaffs_gross_unlock(dev); -+ } -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) -+ truncate_inode_pages(&inode->i_data, 0); -+#endif -+ clear_inode(inode); -+} -+#endif -+ -+ -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ struct super_block *sb = dentry->d_sb; -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+#else -+static int yaffs_statfs(struct super_block *sb, struct statfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+#endif -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); -+ -+ yaffs_gross_lock(dev); -+ -+ buf->f_type = YAFFS_MAGIC; -+ buf->f_bsize = sb->s_blocksize; -+ buf->f_namelen = 255; -+ -+ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { -+ /* Do this if chunk size is not a power of 2 */ -+ -+ uint64_t bytes_in_dev; -+ uint64_t bytes_free; -+ -+ bytes_in_dev = -+ ((uint64_t) -+ ((dev->param.end_block - dev->param.start_block + -+ 1))) * ((uint64_t) (dev->param.chunks_per_block * -+ dev->data_bytes_per_chunk)); -+ -+ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ -+ buf->f_blocks = bytes_in_dev; -+ -+ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * -+ ((uint64_t) (dev->data_bytes_per_chunk)); -+ -+ do_div(bytes_free, sb->s_blocksize); -+ -+ buf->f_bfree = bytes_free; -+ -+ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { -+ -+ buf->f_blocks = -+ (dev->param.end_block - dev->param.start_block + 1) * -+ dev->param.chunks_per_block / -+ (sb->s_blocksize / dev->data_bytes_per_chunk); -+ buf->f_bfree = -+ yaffs_get_n_free_chunks(dev) / -+ (sb->s_blocksize / dev->data_bytes_per_chunk); -+ } else { -+ buf->f_blocks = -+ (dev->param.end_block - dev->param.start_block + 1) * -+ dev->param.chunks_per_block * -+ (dev->data_bytes_per_chunk / sb->s_blocksize); -+ -+ buf->f_bfree = -+ yaffs_get_n_free_chunks(dev) * -+ (dev->data_bytes_per_chunk / sb->s_blocksize); -+ } -+ -+ buf->f_files = 0; -+ buf->f_ffree = 0; -+ buf->f_bavail = buf->f_bfree; -+ -+ yaffs_gross_unlock(dev); -+ return 0; -+} -+ -+ -+ -+static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) -+{ -+ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); -+ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); -+ int do_checkpoint; -+ int dirty = yaffs_check_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, -+ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", -+ gc_urgent, -+ dirty ? "dirty" : "clean", -+ request_checkpoint ? "checkpoint requested" : "no checkpoint", -+ oneshot_checkpoint ? " one-shot" : ""); -+ -+ yaffs_gross_lock(dev); -+ do_checkpoint = ((request_checkpoint && !gc_urgent) || -+ oneshot_checkpoint) && !dev->is_checkpointed; -+ -+ if (dirty || do_checkpoint) { -+ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); -+ yaffs_clear_super_dirty(dev); -+ if (oneshot_checkpoint) -+ yaffs_auto_checkpoint &= ~4; -+ } -+ yaffs_gross_unlock(dev); -+ -+ return 0; -+} -+ -+ -+#ifdef YAFFS_HAS_WRITE_SUPER -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static void yaffs_write_super(struct super_block *sb) -+#else -+static int yaffs_write_super(struct super_block *sb) -+#endif -+{ -+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, -+ "yaffs_write_super %s", -+ request_checkpoint ? " checkpt" : ""); -+ -+ yaffs_do_sync_fs(sb, request_checkpoint); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) -+ return 0; -+#endif -+} -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_sync_fs(struct super_block *sb, int wait) -+#else -+static int yaffs_sync_fs(struct super_block *sb) -+#endif -+{ -+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, -+ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); -+ -+ yaffs_do_sync_fs(sb, request_checkpoint); -+ -+ return 0; -+} -+ -+/* the function only is used to change dev->read_only when this file system -+ * is remounted. -+ */ -+static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) -+{ -+ int read_only = 0; -+ struct mtd_info *mtd; -+ struct yaffs_dev *dev = 0; -+ -+ /* Get the device */ -+ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); -+ if (!mtd) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device #%u doesn't appear to exist", -+ MINOR(sb->s_dev)); -+ return 1; -+ } -+ -+ /* Check it's NAND */ -+ if (mtd->type != MTD_NANDFLASH) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device is not NAND it's type %d", -+ mtd->type); -+ return 1; -+ } -+ -+ read_only = ((*flags & MS_RDONLY) != 0); -+ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { -+ read_only = 1; -+ printk(KERN_INFO -+ "yaffs: mtd is read only, setting superblock read only"); -+ *flags |= MS_RDONLY; -+ } -+ -+ dev = sb->s_fs_info; -+ dev->read_only = read_only; -+ -+ return 0; -+} -+ -+static const struct super_operations yaffs_super_ops = { -+ .statfs = yaffs_statfs, -+ -+#ifndef YAFFS_USE_OWN_IGET -+ .read_inode = yaffs_read_inode, -+#endif -+#ifdef YAFFS_HAS_PUT_INODE -+ .put_inode = yaffs_put_inode, -+#endif -+ .put_super = yaffs_put_super, -+#ifdef YAFFS_HAS_EVICT_INODE -+ .evict_inode = yaffs_evict_inode, -+#else -+ .delete_inode = yaffs_delete_inode, -+ .clear_inode = yaffs_clear_inode, -+#endif -+ .sync_fs = yaffs_sync_fs, -+#ifdef YAFFS_HAS_WRITE_SUPER -+ .write_super = yaffs_write_super, -+#endif -+ .remount_fs = yaffs_remount_fs, -+}; -+ -+struct yaffs_options { -+ int inband_tags; -+ int skip_checkpoint_read; -+ int skip_checkpoint_write; -+ int no_cache; -+ int tags_ecc_on; -+ int tags_ecc_overridden; -+ int lazy_loading_enabled; -+ int lazy_loading_overridden; -+ int empty_lost_and_found; -+ int empty_lost_and_found_overridden; -+ int disable_summary; -+}; -+ -+#define MAX_OPT_LEN 30 -+static int yaffs_parse_options(struct yaffs_options *options, -+ const char *options_str) -+{ -+ char cur_opt[MAX_OPT_LEN + 1]; -+ int p; -+ int error = 0; -+ -+ /* Parse through the options which is a comma seperated list */ -+ -+ while (options_str && *options_str && !error) { -+ memset(cur_opt, 0, MAX_OPT_LEN + 1); -+ p = 0; -+ -+ while (*options_str == ',') -+ options_str++; -+ -+ while (*options_str && *options_str != ',') { -+ if (p < MAX_OPT_LEN) { -+ cur_opt[p] = *options_str; -+ p++; -+ } -+ options_str++; -+ } -+ -+ if (!strcmp(cur_opt, "inband-tags")) { -+ options->inband_tags = 1; -+ } else if (!strcmp(cur_opt, "tags-ecc-off")) { -+ options->tags_ecc_on = 0; -+ options->tags_ecc_overridden = 1; -+ } else if (!strcmp(cur_opt, "tags-ecc-on")) { -+ options->tags_ecc_on = 1; -+ options->tags_ecc_overridden = 1; -+ } else if (!strcmp(cur_opt, "lazy-loading-off")) { -+ options->lazy_loading_enabled = 0; -+ options->lazy_loading_overridden = 1; -+ } else if (!strcmp(cur_opt, "lazy-loading-on")) { -+ options->lazy_loading_enabled = 1; -+ options->lazy_loading_overridden = 1; -+ } else if (!strcmp(cur_opt, "disable-summary")) { -+ options->disable_summary = 1; -+ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { -+ options->empty_lost_and_found = 0; -+ options->empty_lost_and_found_overridden = 1; -+ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { -+ options->empty_lost_and_found = 1; -+ options->empty_lost_and_found_overridden = 1; -+ } else if (!strcmp(cur_opt, "no-cache")) { -+ options->no_cache = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { -+ options->skip_checkpoint_read = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { -+ options->skip_checkpoint_write = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint")) { -+ options->skip_checkpoint_read = 1; -+ options->skip_checkpoint_write = 1; -+ } else { -+ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", -+ cur_opt); -+ error = 1; -+ } -+ } -+ -+ return error; -+} -+ -+ -+static struct dentry *yaffs_make_root(struct inode *inode) -+{ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) -+ struct dentry *root = d_alloc_root(inode); -+ -+ if (!root) -+ iput(inode); -+ -+ return root; -+#else -+ return d_make_root(inode); -+#endif -+} -+ -+ -+ -+ -+static struct super_block *yaffs_internal_read_super(int yaffs_version, -+ struct super_block *sb, -+ void *data, int silent) -+{ -+ int n_blocks; -+ struct inode *inode = NULL; -+ struct dentry *root; -+ struct yaffs_dev *dev = 0; -+ char devname_buf[BDEVNAME_SIZE + 1]; -+ struct mtd_info *mtd; -+ int err; -+ char *data_str = (char *)data; -+ struct yaffs_linux_context *context = NULL; -+ struct yaffs_param *param; -+ -+ int read_only = 0; -+ int inband_tags = 0; -+ -+ struct yaffs_options options; -+ -+ unsigned mount_id; -+ int found; -+ struct yaffs_linux_context *context_iterator; -+ struct list_head *l; -+ -+ if (!sb) { -+ printk(KERN_INFO "yaffs: sb is NULL\n"); -+ return NULL; -+ } -+ -+ sb->s_magic = YAFFS_MAGIC; -+ sb->s_op = &yaffs_super_ops; -+ sb->s_flags |= MS_NOATIME; -+ -+ read_only = ((sb->s_flags & MS_RDONLY) != 0); -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+ sb->s_export_op = &yaffs_export_ops; -+#endif -+ -+ if (!sb->s_dev) -+ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); -+ else if (!yaffs_devname(sb, devname_buf)) -+ printk(KERN_INFO "yaffs: devname is NULL\n"); -+ else -+ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", -+ sb->s_dev, -+ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); -+ -+ if (!data_str) -+ data_str = ""; -+ -+ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); -+ -+ memset(&options, 0, sizeof(options)); -+ -+ if (yaffs_parse_options(&options, data_str)) { -+ /* Option parsing failed */ -+ return NULL; -+ } -+ -+ sb->s_blocksize = PAGE_CACHE_SIZE; -+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: Using yaffs%d", yaffs_version); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", -+ MAJOR(sb->s_dev), MINOR(sb->s_dev), -+ yaffs_devname(sb, devname_buf)); -+ -+ /* Get the device */ -+ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); -+ if (IS_ERR(mtd)) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: MTD device %u either not valid or unavailable", -+ MINOR(sb->s_dev)); -+ return NULL; -+ } -+ -+ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); -+ yaffs_version = 2; -+ } -+ -+ /* Added NCB 26/5/2006 for completeness */ -+ if (yaffs_version == 2 && !options.inband_tags -+ && WRITE_SIZE(mtd) == 512) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); -+ yaffs_version = 1; -+ } -+ -+ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || -+ options.inband_tags) -+ inband_tags = 1; -+ -+ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) -+ return NULL; -+ -+ /* OK, so if we got here, we have an MTD that's NAND and looks -+ * like it has the right capabilities -+ * Set the struct yaffs_dev up for mtd -+ */ -+ -+ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { -+ read_only = 1; -+ printk(KERN_INFO -+ "yaffs: mtd is read only, setting superblock read only\n" -+ ); -+ sb->s_flags |= MS_RDONLY; -+ } -+ -+ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); -+ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); -+ -+ if (!dev || !context) { -+ kfree(dev); -+ kfree(context); -+ dev = NULL; -+ context = NULL; -+ -+ /* Deep shit could not allocate device structure */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." -+ ); -+ return NULL; -+ } -+ memset(dev, 0, sizeof(struct yaffs_dev)); -+ param = &(dev->param); -+ -+ memset(context, 0, sizeof(struct yaffs_linux_context)); -+ dev->os_context = context; -+ INIT_LIST_HEAD(&(context->context_list)); -+ context->dev = dev; -+ context->super = sb; -+ -+ dev->read_only = read_only; -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ sb->s_fs_info = dev; -+#else -+ sb->u.generic_sbp = dev; -+#endif -+ -+ -+ dev->driver_context = mtd; -+ param->name = mtd->name; -+ -+ /* Set up the memory size parameters.... */ -+ -+ -+ param->n_reserved_blocks = 5; -+ param->n_caches = (options.no_cache) ? 0 : 10; -+ param->inband_tags = inband_tags; -+ -+ param->enable_xattr = 1; -+ if (options.lazy_loading_overridden) -+ param->disable_lazy_load = !options.lazy_loading_enabled; -+ -+ param->defered_dir_update = 1; -+ -+ if (options.tags_ecc_overridden) -+ param->no_tags_ecc = !options.tags_ecc_on; -+ -+ param->empty_lost_n_found = 1; -+ param->refresh_period = 500; -+ param->disable_summary = options.disable_summary; -+ -+ -+#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING -+ param->disable_bad_block_marking = 1; -+#endif -+ if (options.empty_lost_and_found_overridden) -+ param->empty_lost_n_found = options.empty_lost_and_found; -+ -+ /* ... and the functions. */ -+ if (yaffs_version == 2) { -+ param->is_yaffs2 = 1; -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+ param->total_bytes_per_chunk = mtd->writesize; -+ param->chunks_per_block = mtd->erasesize / mtd->writesize; -+#else -+ param->total_bytes_per_chunk = mtd->oobblock; -+ param->chunks_per_block = mtd->erasesize / mtd->oobblock; -+#endif -+ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); -+ -+ param->start_block = 0; -+ param->end_block = n_blocks - 1; -+ } else { -+ param->is_yaffs2 = 0; -+ n_blocks = YCALCBLOCKS(mtd->size, -+ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); -+ -+ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; -+ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; -+ } -+ -+ param->start_block = 0; -+ param->end_block = n_blocks - 1; -+ -+ yaffs_mtd_drv_install(dev); -+ -+ param->sb_dirty_fn = yaffs_set_super_dirty; -+ param->gc_control_fn = yaffs_gc_control_callback; -+ -+ yaffs_dev_to_lc(dev)->super = sb; -+ -+ param->use_nand_ecc = 1; -+ -+ param->skip_checkpt_rd = options.skip_checkpoint_read; -+ param->skip_checkpt_wr = options.skip_checkpoint_write; -+ -+ mutex_lock(&yaffs_context_lock); -+ /* Get a mount id */ -+ found = 0; -+ for (mount_id = 0; !found; mount_id++) { -+ found = 1; -+ list_for_each(l, &yaffs_context_list) { -+ context_iterator = -+ list_entry(l, struct yaffs_linux_context, -+ context_list); -+ if (context_iterator->mount_id == mount_id) -+ found = 0; -+ } -+ } -+ context->mount_id = mount_id; -+ -+ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), -+ &yaffs_context_list); -+ mutex_unlock(&yaffs_context_lock); -+ -+ /* Directory search handling... */ -+ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); -+ param->remove_obj_fn = yaffs_remove_obj_callback; -+ -+ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); -+ -+ yaffs_gross_lock(dev); -+ -+ err = yaffs_guts_initialise(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: guts initialised %s", -+ (err == YAFFS_OK) ? "OK" : "FAILED"); -+ -+ if (err == YAFFS_OK) -+ yaffs_bg_start(dev); -+ -+ if (!context->bg_thread) -+ param->defered_dir_update = 0; -+ -+ sb->s_maxbytes = yaffs_max_file_size(dev); -+ -+ /* Release lock before yaffs_get_inode() */ -+ yaffs_gross_unlock(dev); -+ -+ /* Create root inode */ -+ if (err == YAFFS_OK) -+ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); -+ -+ if (!inode) -+ return NULL; -+ -+ inode->i_op = &yaffs_dir_inode_operations; -+ inode->i_fop = &yaffs_dir_operations; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); -+ -+ root = yaffs_make_root(inode); -+ -+ if (!root) -+ return NULL; -+ -+ sb->s_root = root; -+ if(!dev->is_checkpointed) -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs_read_super: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); -+ return sb; -+} -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data) -+{ -+ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); -+} -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data, struct vfsmount *mnt) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs_internal_read_super_mtd, mnt); -+} -+#else -+static struct super_block *yaffs_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs_internal_read_super_mtd); -+} -+#endif -+ -+static struct file_system_type yaffs_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "yaffs", -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+ .mount = yaffs_mount, -+#else -+ .get_sb = yaffs_read_super, -+#endif -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV, -+}; -+#else -+static struct super_block *yaffs_read_super(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(1, sb, data, silent); -+} -+ -+static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, -+ FS_REQUIRES_DEV); -+#endif -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data) -+{ -+ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); -+} -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs2_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, void *data, -+ struct vfsmount *mnt) -+{ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs2_internal_read_super_mtd, mnt); -+} -+#else -+static struct super_block *yaffs2_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs2_internal_read_super_mtd); -+} -+#endif -+ -+static struct file_system_type yaffs2_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "yaffs2", -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+ .mount = yaffs2_mount, -+#else -+ .get_sb = yaffs2_read_super, -+#endif -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV, -+}; -+#else -+static struct super_block *yaffs2_read_super(struct super_block *sb, -+ void *data, int silent) -+{ -+ return yaffs_internal_read_super(2, sb, data, silent); -+} -+ -+static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, -+ FS_REQUIRES_DEV); -+#endif -+ -+ -+static struct proc_dir_entry *my_proc_entry; -+ -+static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) -+{ -+ struct yaffs_param *param = &dev->param; -+ int bs[10]; -+ -+ yaffs_count_blocks_by_state(dev,bs); -+ -+ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); -+ buf += sprintf(buf, "end_block............ %d\n", param->end_block); -+ buf += sprintf(buf, "total_bytes_per_chunk %d\n", -+ param->total_bytes_per_chunk); -+ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); -+ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); -+ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); -+ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); -+ buf += sprintf(buf, "empty_lost_n_found... %d\n", -+ param->empty_lost_n_found); -+ buf += sprintf(buf, "disable_lazy_load.... %d\n", -+ param->disable_lazy_load); -+ buf += sprintf(buf, "disable_bad_block_mrk %d\n", -+ param->disable_bad_block_marking); -+ buf += sprintf(buf, "refresh_period....... %d\n", -+ param->refresh_period); -+ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); -+ buf += sprintf(buf, "n_reserved_blocks.... %d\n", -+ param->n_reserved_blocks); -+ buf += sprintf(buf, "always_check_erased.. %d\n", -+ param->always_check_erased); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "block count by state\n"); -+ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", -+ bs[0], bs[1], bs[2], bs[3], bs[4]); -+ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", -+ bs[5], bs[6], bs[7], bs[8], bs[9]); -+ -+ return buf; -+} -+ -+static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) -+{ -+ buf += sprintf(buf, "max file size....... %lld\n", -+ (long long) yaffs_max_file_size(dev)); -+ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", -+ dev->data_bytes_per_chunk); -+ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); -+ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); -+ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); -+ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", -+ dev->blocks_in_checkpt); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); -+ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); -+ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); -+ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); -+ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); -+ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); -+ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); -+ buf += sprintf(buf, "passive_gc_count..... %u\n", -+ dev->passive_gc_count); -+ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", -+ dev->oldest_dirty_gc_count); -+ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); -+ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); -+ buf += sprintf(buf, "n_retried_writes..... %u\n", -+ dev->n_retried_writes); -+ buf += sprintf(buf, "n_retired_blocks..... %u\n", -+ dev->n_retired_blocks); -+ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); -+ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); -+ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", -+ dev->n_tags_ecc_fixed); -+ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", -+ dev->n_tags_ecc_unfixed); -+ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); -+ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); -+ buf += sprintf(buf, "n_unlinked_files..... %u\n", -+ dev->n_unlinked_files); -+ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); -+ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); -+ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); -+ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); -+ -+ return buf; -+} -+ -+static int yaffs_proc_read(char *page, -+ char **start, -+ off_t offset, int count, int *eof, void *data) -+{ -+ struct list_head *item; -+ char *buf = page; -+ int step = offset; -+ int n = 0; -+ -+ /* Get proc_file_read() to step 'offset' by one on each sucessive call. -+ * We use 'offset' (*ppos) to indicate where we are in dev_list. -+ * This also assumes the user has posted a read buffer large -+ * enough to hold the complete output; but that's life in /proc. -+ */ -+ -+ *(int *)start = 1; -+ -+ /* Print header first */ -+ if (step == 0) -+ buf += -+ sprintf(buf, -+ "Multi-version YAFFS\n"); -+ else if (step == 1) -+ buf += sprintf(buf, "\n"); -+ else { -+ step -= 2; -+ -+ mutex_lock(&yaffs_context_lock); -+ -+ /* Locate and print the Nth entry. Order N-squared but N is small. */ -+ list_for_each(item, &yaffs_context_list) { -+ struct yaffs_linux_context *dc = -+ list_entry(item, struct yaffs_linux_context, -+ context_list); -+ struct yaffs_dev *dev = dc->dev; -+ -+ if (n < (step & ~1)) { -+ n += 2; -+ continue; -+ } -+ if ((step & 1) == 0) { -+ buf += -+ sprintf(buf, "\nDevice %d \"%s\"\n", n, -+ dev->param.name); -+ buf = yaffs_dump_dev_part0(buf, dev); -+ } else { -+ buf = yaffs_dump_dev_part1(buf, dev); -+ } -+ -+ break; -+ } -+ mutex_unlock(&yaffs_context_lock); -+ } -+ -+ return buf - page < count ? buf - page : count; -+} -+ -+/** -+ * Set the verbosity of the warnings and error messages. -+ * -+ * Note that the names can only be a..z or _ with the current code. -+ */ -+ -+static struct { -+ char *mask_name; -+ unsigned mask_bitfield; -+} mask_flags[] = { -+ {"allocate", YAFFS_TRACE_ALLOCATE}, -+ {"always", YAFFS_TRACE_ALWAYS}, -+ {"background", YAFFS_TRACE_BACKGROUND}, -+ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, -+ {"buffers", YAFFS_TRACE_BUFFERS}, -+ {"bug", YAFFS_TRACE_BUG}, -+ {"checkpt", YAFFS_TRACE_CHECKPOINT}, -+ {"deletion", YAFFS_TRACE_DELETION}, -+ {"erase", YAFFS_TRACE_ERASE}, -+ {"error", YAFFS_TRACE_ERROR}, -+ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, -+ {"gc", YAFFS_TRACE_GC}, -+ {"lock", YAFFS_TRACE_LOCK}, -+ {"mtd", YAFFS_TRACE_MTD}, -+ {"nandaccess", YAFFS_TRACE_NANDACCESS}, -+ {"os", YAFFS_TRACE_OS}, -+ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, -+ {"scan", YAFFS_TRACE_SCAN}, -+ {"mount", YAFFS_TRACE_MOUNT}, -+ {"tracing", YAFFS_TRACE_TRACING}, -+ {"sync", YAFFS_TRACE_SYNC}, -+ {"write", YAFFS_TRACE_WRITE}, -+ {"verify", YAFFS_TRACE_VERIFY}, -+ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, -+ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, -+ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, -+ {"all", 0xffffffff}, -+ {"none", 0}, -+ {NULL, 0}, -+}; -+ -+#define MAX_MASK_NAME_LENGTH 40 -+static int yaffs_proc_write_trace_options(struct file *file, const char *buf, -+ unsigned long count) -+{ -+ unsigned rg = 0, mask_bitfield; -+ char *end; -+ char *mask_name; -+ const char *x; -+ char substring[MAX_MASK_NAME_LENGTH + 1]; -+ int i; -+ int done = 0; -+ int add, len = 0; -+ int pos = 0; -+ -+ rg = yaffs_trace_mask; -+ -+ while (!done && (pos < count)) { -+ done = 1; -+ while ((pos < count) && isspace(buf[pos])) -+ pos++; -+ -+ switch (buf[pos]) { -+ case '+': -+ case '-': -+ case '=': -+ add = buf[pos]; -+ pos++; -+ break; -+ -+ default: -+ add = ' '; -+ break; -+ } -+ mask_name = NULL; -+ -+ mask_bitfield = simple_strtoul(buf + pos, &end, 0); -+ -+ if (end > buf + pos) { -+ mask_name = "numeral"; -+ len = end - (buf + pos); -+ pos += len; -+ done = 0; -+ } else { -+ for (x = buf + pos, i = 0; -+ (*x == '_' || (*x >= 'a' && *x <= 'z')) && -+ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) -+ substring[i] = *x; -+ substring[i] = '\0'; -+ -+ for (i = 0; mask_flags[i].mask_name != NULL; i++) { -+ if (strcmp(substring, mask_flags[i].mask_name) -+ == 0) { -+ mask_name = mask_flags[i].mask_name; -+ mask_bitfield = -+ mask_flags[i].mask_bitfield; -+ done = 0; -+ break; -+ } -+ } -+ } -+ -+ if (mask_name != NULL) { -+ done = 0; -+ switch (add) { -+ case '-': -+ rg &= ~mask_bitfield; -+ break; -+ case '+': -+ rg |= mask_bitfield; -+ break; -+ case '=': -+ rg = mask_bitfield; -+ break; -+ default: -+ rg |= mask_bitfield; -+ break; -+ } -+ } -+ } -+ -+ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; -+ -+ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); -+ -+ if (rg & YAFFS_TRACE_ALWAYS) { -+ for (i = 0; mask_flags[i].mask_name != NULL; i++) { -+ char flag; -+ flag = ((rg & mask_flags[i].mask_bitfield) == -+ mask_flags[i].mask_bitfield) ? '+' : '-'; -+ printk(KERN_DEBUG "%c%s\n", flag, -+ mask_flags[i].mask_name); -+ } -+ } -+ -+ return count; -+} -+ -+/* Debug strings are of the form: -+ * .bnnn print info on block n -+ * .cobjn,chunkn print nand chunk id for objn:chunkn -+ */ -+ -+static int yaffs_proc_debug_write(struct file *file, const char *buf, -+ unsigned long count) -+{ -+ -+ char str[100]; -+ char *p0; -+ char *p1; -+ long p1_val; -+ long p0_val; -+ char cmd; -+ struct list_head *item; -+ -+ memset(str, 0, sizeof(str)); -+ memcpy(str, buf, min((size_t)count, sizeof(str) -1)); -+ -+ cmd = str[1]; -+ -+ p0 = str + 2; -+ -+ p1 = p0; -+ -+ while (*p1 && *p1 != ',') { -+ p1++; -+ } -+ *p1 = '\0'; -+ p1++; -+ -+ p0_val = simple_strtol(p0, NULL, 0); -+ p1_val = simple_strtol(p1, NULL, 0); -+ -+ -+ mutex_lock(&yaffs_context_lock); -+ -+ /* Locate and print the Nth entry. Order N-squared but N is small. */ -+ list_for_each(item, &yaffs_context_list) { -+ struct yaffs_linux_context *dc = -+ list_entry(item, struct yaffs_linux_context, -+ context_list); -+ struct yaffs_dev *dev = dc->dev; -+ -+ if (cmd == 'b') { -+ struct yaffs_block_info *bi; -+ -+ bi = yaffs_get_block_info(dev,p0_val); -+ -+ if(bi) { -+ printk("Block %d: state %d, retire %d, use %d, seq %d\n", -+ (int)p0_val, bi->block_state, -+ bi->needs_retiring, bi->pages_in_use, -+ bi->seq_number); -+ } -+ } else if (cmd == 'c') { -+ struct yaffs_obj *obj; -+ int nand_chunk; -+ -+ obj = yaffs_find_by_number(dev, p0_val); -+ if (!obj) -+ printk("No obj %d\n", (int)p0_val); -+ else { -+ if(p1_val == 0) -+ nand_chunk = obj->hdr_chunk; -+ else -+ nand_chunk = -+ yaffs_find_chunk_in_file(obj, -+ p1_val, NULL); -+ printk("Nand chunk for %d:%d is %d\n", -+ (int)p0_val, (int)p1_val, nand_chunk); -+ } -+ } -+ } -+ -+ mutex_unlock(&yaffs_context_lock); -+ -+ return count; -+} -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) -+static int yaffs_proc_write(struct file *file, const char *buf, -+ unsigned long count, void *ppos) -+#else -+static ssize_t yaffs_proc_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) -+#endif -+{ -+ if (buf[0] == '.') -+ return yaffs_proc_debug_write(file, buf, count); -+ return yaffs_proc_write_trace_options(file, buf, count); -+} -+ -+/* Stuff to handle installation of file systems */ -+struct file_system_to_install { -+ struct file_system_type *fst; -+ int installed; -+}; -+ -+static struct file_system_to_install fs_to_install[] = { -+ {&yaffs_fs_type, 0}, -+ {&yaffs2_fs_type, 0}, -+ {NULL, 0} -+}; -+ -+ -+#ifdef YAFFS_NEW_PROCFS -+static int yaffs_proc_show(struct seq_file *m, void *v) -+{ -+ /* FIXME: Unify in a better way? */ -+ char buffer[512]; -+ char *start; -+ int len; -+ -+ len = yaffs_proc_read(buffer, &start, 0, sizeof(buffer), NULL, NULL); -+ seq_puts(m, buffer); -+ return 0; -+} -+ -+static int yaffs_proc_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, yaffs_proc_show, NULL); -+} -+ -+static struct file_operations procfs_ops = { -+ .owner = THIS_MODULE, -+ .open = yaffs_proc_open, -+ .read = seq_read, -+ .write = yaffs_proc_write, -+}; -+ -+static int yaffs_procfs_init(void) -+{ -+ /* Install the proc_fs entries */ -+ my_proc_entry = proc_create("yaffs", -+ S_IRUGO | S_IFREG, -+ YPROC_ROOT, -+ &procfs_ops); -+ -+ if (my_proc_entry) { -+ return 0; -+ } else { -+ return -ENOMEM; -+ } -+} -+ -+#else -+ -+ -+static int yaffs_procfs_init(void) -+{ -+ /* Install the proc_fs entries */ -+ my_proc_entry = create_proc_entry("yaffs", -+ S_IRUGO | S_IFREG, YPROC_ROOT); -+ -+ if (my_proc_entry) { -+ my_proc_entry->write_proc = yaffs_proc_write; -+ my_proc_entry->read_proc = yaffs_proc_read; -+ my_proc_entry->data = NULL; -+ return 0; -+ } else { -+ return -ENOMEM; -+ } -+} -+ -+#endif -+ -+ -+static int __init init_yaffs_fs(void) -+{ -+ int error = 0; -+ struct file_system_to_install *fsinst; -+ -+ mutex_init(&yaffs_context_lock); -+ -+ error = yaffs_procfs_init(); -+ if (error) -+ return error; -+ -+ /* Now add the file system entries */ -+ -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst && !error) { -+ error = register_filesystem(fsinst->fst); -+ if (!error) -+ fsinst->installed = 1; -+ fsinst++; -+ } -+ -+ /* Any errors? uninstall */ -+ if (error) { -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst) { -+ if (fsinst->installed) { -+ unregister_filesystem(fsinst->fst); -+ fsinst->installed = 0; -+ } -+ fsinst++; -+ } -+ } -+ -+ return error; -+} -+ -+static void __exit exit_yaffs_fs(void) -+{ -+ -+ struct file_system_to_install *fsinst; -+ -+ remove_proc_entry("yaffs", YPROC_ROOT); -+ -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst) { -+ if (fsinst->installed) { -+ unregister_filesystem(fsinst->fst); -+ fsinst->installed = 0; -+ } -+ fsinst++; -+ } -+} -+ -+module_init(init_yaffs_fs) -+ module_exit(exit_yaffs_fs) -+ -+ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); -+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); -+MODULE_LICENSE("GPL"); -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.18.14/fs/yaffs2/yaffs_yaffs1.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_yaffs1.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,422 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_yaffs1.h" -+#include "yportenv.h" -+#include "yaffs_trace.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_nand.h" -+#include "yaffs_attribs.h" -+ -+int yaffs1_scan(struct yaffs_dev *dev) -+{ -+ struct yaffs_ext_tags tags; -+ int blk; -+ int result; -+ int chunk; -+ int c; -+ int deleted; -+ enum yaffs_block_state state; -+ LIST_HEAD(hard_list); -+ struct yaffs_block_info *bi; -+ u32 seq_number; -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_obj *in; -+ struct yaffs_obj *parent; -+ int alloc_failed = 0; -+ struct yaffs_shadow_fixer *shadow_fixers = NULL; -+ u8 *chunk_data; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs1_scan starts intstartblk %d intendblk %d...", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ chunk_data = yaffs_get_temp_buffer(dev); -+ -+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; -+ -+ /* Scan all the blocks to determine their state */ -+ bi = dev->block_info; -+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; -+ blk++) { -+ yaffs_clear_chunk_bits(dev, blk); -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ -+ yaffs_query_init_block_state(dev, blk, &state, &seq_number); -+ -+ bi->block_state = state; -+ bi->seq_number = seq_number; -+ -+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) -+ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, -+ "Block scanning block %d state %d seq %d", -+ blk, state, seq_number); -+ -+ if (state == YAFFS_BLOCK_STATE_DEAD) { -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, -+ "block %d is bad", blk); -+ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += dev->param.chunks_per_block; -+ } -+ bi++; -+ } -+ -+ /* For each block.... */ -+ for (blk = dev->internal_start_block; -+ !alloc_failed && blk <= dev->internal_end_block; blk++) { -+ -+ cond_resched(); -+ -+ bi = yaffs_get_block_info(dev, blk); -+ state = bi->block_state; -+ -+ deleted = 0; -+ -+ /* For each chunk in each block that needs scanning.... */ -+ for (c = 0; -+ !alloc_failed && c < dev->param.chunks_per_block && -+ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { -+ /* Read the tags and decide what to do */ -+ chunk = blk * dev->param.chunks_per_block + c; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, -+ &tags); -+ -+ /* Let's have a good look at this chunk... */ -+ -+ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || -+ tags.is_deleted) { -+ /* YAFFS1 only... -+ * A deleted chunk -+ */ -+ deleted++; -+ dev->n_free_chunks++; -+ } else if (!tags.chunk_used) { -+ /* An unassigned chunk in the block -+ * This means that either the block is empty or -+ * this is the one being allocated from -+ */ -+ -+ if (c == 0) { -+ /* We're looking at the first chunk in -+ *the block so the block is unused */ -+ state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ } else { -+ /* this is the block being allocated */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Allocating from %d %d", -+ blk, c); -+ state = YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->alloc_block = blk; -+ dev->alloc_page = c; -+ dev->alloc_block_finder = blk; -+ -+ } -+ -+ dev->n_free_chunks += -+ (dev->param.chunks_per_block - c); -+ } else if (tags.chunk_id > 0) { -+ /* chunk_id > 0 so it is a data chunk... */ -+ unsigned int endpos; -+ -+ yaffs_set_chunk_bit(dev, blk, c); -+ bi->pages_in_use++; -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ YAFFS_OBJECT_TYPE_FILE); -+ /* PutChunkIntoFile checks for a clash -+ * (two data chunks with the same chunk_id). -+ */ -+ -+ if (!in) -+ alloc_failed = 1; -+ -+ if (in) { -+ if (!yaffs_put_chunk_in_file -+ (in, tags.chunk_id, chunk, 1)) -+ alloc_failed = 1; -+ } -+ -+ endpos = -+ (tags.chunk_id - 1) * -+ dev->data_bytes_per_chunk + -+ tags.n_bytes; -+ if (in && -+ in->variant_type == -+ YAFFS_OBJECT_TYPE_FILE && -+ in->variant.file_variant.scanned_size < -+ endpos) { -+ in->variant.file_variant.scanned_size = -+ endpos; -+ if (!dev->param.use_header_file_size) { -+ in->variant. -+ file_variant.file_size = -+ in->variant. -+ file_variant.scanned_size; -+ } -+ -+ } -+ } else { -+ /* chunk_id == 0, so it is an ObjectHeader. -+ * Make the object -+ */ -+ yaffs_set_chunk_bit(dev, blk, c); -+ bi->pages_in_use++; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, -+ chunk_data, -+ NULL); -+ -+ oh = (struct yaffs_obj_hdr *)chunk_data; -+ -+ in = yaffs_find_by_number(dev, tags.obj_id); -+ if (in && in->variant_type != oh->type) { -+ /* This should not happen, but somehow -+ * Wev'e ended up with an obj_id that -+ * has been reused but not yet deleted, -+ * and worse still it has changed type. -+ * Delete the old object. -+ */ -+ -+ yaffs_del_obj(in); -+ in = NULL; -+ } -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ oh->type); -+ -+ if (!in) -+ alloc_failed = 1; -+ -+ if (in && oh->shadows_obj > 0) { -+ -+ struct yaffs_shadow_fixer *fixer; -+ fixer = -+ kmalloc(sizeof -+ (struct yaffs_shadow_fixer), -+ GFP_NOFS); -+ if (fixer) { -+ fixer->next = shadow_fixers; -+ shadow_fixers = fixer; -+ fixer->obj_id = tags.obj_id; -+ fixer->shadowed_id = -+ oh->shadows_obj; -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Shadow fixer: %d shadows %d", -+ fixer->obj_id, -+ fixer->shadowed_id); -+ -+ } -+ -+ } -+ -+ if (in && in->valid) { -+ /* We have already filled this one. -+ * We have a duplicate and need to -+ * resolve it. */ -+ -+ unsigned existing_serial = in->serial; -+ unsigned new_serial = -+ tags.serial_number; -+ -+ if (((existing_serial + 1) & 3) == -+ new_serial) { -+ /* Use new one - destroy the -+ * exisiting one */ -+ yaffs_chunk_del(dev, -+ in->hdr_chunk, -+ 1, __LINE__); -+ in->valid = 0; -+ } else { -+ /* Use existing - destroy -+ * this one. */ -+ yaffs_chunk_del(dev, chunk, 1, -+ __LINE__); -+ } -+ } -+ -+ if (in && !in->valid && -+ (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == -+ YAFFS_OBJECTID_LOSTNFOUND)) { -+ /* We only load some info, don't fiddle -+ * with directory structure */ -+ in->valid = 1; -+ in->variant_type = oh->type; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->hdr_chunk = chunk; -+ in->serial = tags.serial_number; -+ -+ } else if (in && !in->valid) { -+ /* we need to load this info */ -+ -+ in->valid = 1; -+ in->variant_type = oh->type; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->hdr_chunk = chunk; -+ in->serial = tags.serial_number; -+ -+ yaffs_set_obj_name_from_oh(in, oh); -+ in->dirty = 0; -+ -+ /* directory stuff... -+ * hook up to parent -+ */ -+ -+ parent = -+ yaffs_find_or_create_by_number -+ (dev, oh->parent_obj_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ if (!parent) -+ alloc_failed = 1; -+ if (parent && parent->variant_type == -+ YAFFS_OBJECT_TYPE_UNKNOWN) { -+ /* Set up as a directory */ -+ parent->variant_type = -+ YAFFS_OBJECT_TYPE_DIRECTORY; -+ INIT_LIST_HEAD(&parent-> -+ variant.dir_variant. -+ children); -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ /* Hoosterman, a problem.... -+ * We're trying to use a -+ * non-directory as a directory -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." -+ ); -+ parent = dev->lost_n_found; -+ } -+ -+ yaffs_add_obj_to_dir(parent, in); -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Todo got a problem */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (dev->param. -+ use_header_file_size) -+ in->variant. -+ file_variant.file_size -+ = yaffs_oh_to_size(oh); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ in->variant. -+ hardlink_variant.equiv_id = -+ oh->equiv_id; -+ list_add(&in->hard_links, -+ &hard_list); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ in->variant.symlink_variant. -+ alias = -+ yaffs_clone_str(oh->alias); -+ if (!in->variant. -+ symlink_variant.alias) -+ alloc_failed = 1; -+ break; -+ } -+ } -+ } -+ } -+ -+ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* If we got this far while scanning, -+ * then the block is fully allocated. */ -+ state = YAFFS_BLOCK_STATE_FULL; -+ } -+ -+ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ /* If the block was partially allocated then -+ * treat it as fully allocated. */ -+ state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ -+ bi->block_state = state; -+ -+ /* Now let's see if it was dirty */ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state == YAFFS_BLOCK_STATE_FULL) -+ yaffs_block_became_dirty(dev, blk); -+ } -+ -+ /* Ok, we've done all the scanning. -+ * Fix up the hard link chains. -+ * We should now have scanned all the objects, now it's time to add -+ * these hardlinks. -+ */ -+ -+ yaffs_link_fixup(dev, &hard_list); -+ -+ /* -+ * Fix up any shadowed objects. -+ * There should not be more than one of these. -+ */ -+ { -+ struct yaffs_shadow_fixer *fixer; -+ struct yaffs_obj *obj; -+ -+ while (shadow_fixers) { -+ fixer = shadow_fixers; -+ shadow_fixers = fixer->next; -+ /* Complete the rename transaction by deleting the -+ * shadowed object then setting the object header -+ to unshadowed. -+ */ -+ obj = yaffs_find_by_number(dev, fixer->shadowed_id); -+ if (obj) -+ yaffs_del_obj(obj); -+ -+ obj = yaffs_find_by_number(dev, fixer->obj_id); -+ -+ if (obj) -+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); -+ -+ kfree(fixer); -+ } -+ } -+ -+ yaffs_release_temp_buffer(dev, chunk_data); -+ -+ if (alloc_failed) -+ return YAFFS_FAIL; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.18.14/fs/yaffs2/yaffs_yaffs1.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_yaffs1.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,22 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_YAFFS1_H__ -+#define __YAFFS_YAFFS1_H__ -+ -+#include "yaffs_guts.h" -+int yaffs1_scan(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.18.14/fs/yaffs2/yaffs_yaffs2.c ---- linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_yaffs2.c 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,1532 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yaffs_yaffs2.h" -+#include "yaffs_checkptrw.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_nand.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_verify.h" -+#include "yaffs_attribs.h" -+#include "yaffs_summary.h" -+ -+/* -+ * Checkpoints are really no benefit on very small partitions. -+ * -+ * To save space on small partitions don't bother with checkpoints unless -+ * the partition is at least this big. -+ */ -+#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 -+#define YAFFS_SMALL_HOLE_THRESHOLD 4 -+ -+/* -+ * Oldest Dirty Sequence Number handling. -+ */ -+ -+/* yaffs_calc_oldest_dirty_seq() -+ * yaffs2_find_oldest_dirty_seq() -+ * Calculate the oldest dirty sequence number if we don't know it. -+ */ -+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) -+{ -+ int i; -+ unsigned seq; -+ unsigned block_no = 0; -+ struct yaffs_block_info *b; -+ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ /* Find the oldest dirty sequence number. */ -+ seq = dev->seq_number + 1; -+ b = dev->block_info; -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ if (b->block_state == YAFFS_BLOCK_STATE_FULL && -+ (b->pages_in_use - b->soft_del_pages) < -+ dev->param.chunks_per_block && -+ b->seq_number < seq) { -+ seq = b->seq_number; -+ block_no = i; -+ } -+ b++; -+ } -+ -+ if (block_no) { -+ dev->oldest_dirty_seq = seq; -+ dev->oldest_dirty_block = block_no; -+ } -+} -+ -+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!dev->oldest_dirty_seq) -+ yaffs_calc_oldest_dirty_seq(dev); -+} -+ -+/* -+ * yaffs_clear_oldest_dirty_seq() -+ * Called when a block is erased or marked bad. (ie. when its seq_number -+ * becomes invalid). If the value matches the oldest then we clear -+ * dev->oldest_dirty_seq to force its recomputation. -+ */ -+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi) -+{ -+ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { -+ dev->oldest_dirty_seq = 0; -+ dev->oldest_dirty_block = 0; -+ } -+} -+ -+/* -+ * yaffs2_update_oldest_dirty_seq() -+ * Update the oldest dirty sequence number whenever we dirty a block. -+ * Only do this if the oldest_dirty_seq is actually being tracked. -+ */ -+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, -+ struct yaffs_block_info *bi) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (dev->oldest_dirty_seq) { -+ if (dev->oldest_dirty_seq > bi->seq_number) { -+ dev->oldest_dirty_seq = bi->seq_number; -+ dev->oldest_dirty_block = block_no; -+ } -+ } -+} -+ -+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) -+{ -+ -+ if (!dev->param.is_yaffs2) -+ return 1; /* disqualification only applies to yaffs2. */ -+ -+ if (!bi->has_shrink_hdr) -+ return 1; /* can gc */ -+ -+ yaffs2_find_oldest_dirty_seq(dev); -+ -+ /* Can't do gc of this block if there are any blocks older than this -+ * one that have discarded pages. -+ */ -+ return (bi->seq_number <= dev->oldest_dirty_seq); -+} -+ -+/* -+ * yaffs2_find_refresh_block() -+ * periodically finds the oldest full block by sequence number for refreshing. -+ * Only for yaffs2. -+ */ -+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) -+{ -+ u32 b; -+ u32 oldest = 0; -+ u32 oldest_seq = 0; -+ struct yaffs_block_info *bi; -+ -+ if (!dev->param.is_yaffs2) -+ return oldest; -+ -+ /* -+ * If refresh period < 10 then refreshing is disabled. -+ */ -+ if (dev->param.refresh_period < 10) -+ return oldest; -+ -+ /* -+ * Fix broken values. -+ */ -+ if (dev->refresh_skip > dev->param.refresh_period) -+ dev->refresh_skip = dev->param.refresh_period; -+ -+ if (dev->refresh_skip > 0) -+ return oldest; -+ -+ /* -+ * Refresh skip is now zero. -+ * We'll do a refresh this time around.... -+ * Update the refresh skip and find the oldest block. -+ */ -+ dev->refresh_skip = dev->param.refresh_period; -+ dev->refresh_count++; -+ bi = dev->block_info; -+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { -+ -+ if (oldest < 1 || bi->seq_number < oldest_seq) { -+ oldest = b; -+ oldest_seq = bi->seq_number; -+ } -+ } -+ bi++; -+ } -+ -+ if (oldest > 0) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC refresh count %d selected block %d with seq_number %d", -+ dev->refresh_count, oldest, oldest_seq); -+ } -+ -+ return oldest; -+} -+ -+int yaffs2_checkpt_required(struct yaffs_dev *dev) -+{ -+ int nblocks; -+ -+ if (!dev->param.is_yaffs2) -+ return 0; -+ -+ nblocks = dev->internal_end_block - dev->internal_start_block + 1; -+ -+ return !dev->param.skip_checkpt_wr && -+ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); -+} -+ -+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) -+{ -+ int retval; -+ int n_bytes = 0; -+ int n_blocks; -+ int dev_blocks; -+ -+ if (!dev->param.is_yaffs2) -+ return 0; -+ -+ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { -+ /* Not a valid value so recalculate */ -+ dev_blocks = dev->param.end_block - dev->param.start_block + 1; -+ n_bytes += sizeof(struct yaffs_checkpt_validity); -+ n_bytes += sizeof(struct yaffs_checkpt_dev); -+ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); -+ n_bytes += dev_blocks * dev->chunk_bit_stride; -+ n_bytes += -+ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * -+ dev->n_obj; -+ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; -+ n_bytes += sizeof(struct yaffs_checkpt_validity); -+ n_bytes += sizeof(u32); /* checksum */ -+ -+ /* Round up and add 2 blocks to allow for some bad blocks, -+ * so add 3 */ -+ -+ n_blocks = -+ (n_bytes / -+ (dev->data_bytes_per_chunk * -+ dev->param.chunks_per_block)) + 3; -+ -+ dev->checkpoint_blocks_required = n_blocks; -+ } -+ -+ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; -+ if (retval < 0) -+ retval = 0; -+ return retval; -+} -+ -+/*--------------------- Checkpointing --------------------*/ -+ -+static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) -+{ -+ struct yaffs_checkpt_validity cp; -+ -+ memset(&cp, 0, sizeof(cp)); -+ -+ cp.struct_type = sizeof(cp); -+ cp.magic = YAFFS_MAGIC; -+ cp.version = YAFFS_CHECKPOINT_VERSION; -+ cp.head = (head) ? 1 : 0; -+ -+ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) -+{ -+ struct yaffs_checkpt_validity cp; -+ int ok; -+ -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ -+ if (ok) -+ ok = (cp.struct_type == sizeof(cp)) && -+ (cp.magic == YAFFS_MAGIC) && -+ (cp.version == YAFFS_CHECKPOINT_VERSION) && -+ (cp.head == ((head) ? 1 : 0)); -+ return ok ? 1 : 0; -+} -+ -+static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, -+ struct yaffs_dev *dev) -+{ -+ cp->n_erased_blocks = dev->n_erased_blocks; -+ cp->alloc_block = dev->alloc_block; -+ cp->alloc_page = dev->alloc_page; -+ cp->n_free_chunks = dev->n_free_chunks; -+ -+ cp->n_deleted_files = dev->n_deleted_files; -+ cp->n_unlinked_files = dev->n_unlinked_files; -+ cp->n_bg_deletions = dev->n_bg_deletions; -+ cp->seq_number = dev->seq_number; -+ -+} -+ -+static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, -+ struct yaffs_checkpt_dev *cp) -+{ -+ dev->n_erased_blocks = cp->n_erased_blocks; -+ dev->alloc_block = cp->alloc_block; -+ dev->alloc_page = cp->alloc_page; -+ dev->n_free_chunks = cp->n_free_chunks; -+ -+ dev->n_deleted_files = cp->n_deleted_files; -+ dev->n_unlinked_files = cp->n_unlinked_files; -+ dev->n_bg_deletions = cp->n_bg_deletions; -+ dev->seq_number = cp->seq_number; -+} -+ -+static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_dev cp; -+ u32 n_bytes; -+ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ int ok; -+ -+ /* Write device runtime values */ -+ yaffs2_dev_to_checkpt_dev(&cp, dev); -+ cp.struct_type = sizeof(cp); -+ -+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (!ok) -+ return 0; -+ -+ /* Write block info */ -+ n_bytes = n_blocks * sizeof(struct yaffs_block_info); -+ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); -+ if (!ok) -+ return 0; -+ -+ /* Write chunk bits */ -+ n_bytes = n_blocks * dev->chunk_bit_stride; -+ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_dev cp; -+ u32 n_bytes; -+ u32 n_blocks = -+ (dev->internal_end_block - dev->internal_start_block + 1); -+ int ok; -+ -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (!ok) -+ return 0; -+ -+ if (cp.struct_type != sizeof(cp)) -+ return 0; -+ -+ yaffs_checkpt_dev_to_dev(dev, &cp); -+ -+ n_bytes = n_blocks * sizeof(struct yaffs_block_info); -+ -+ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); -+ -+ if (!ok) -+ return 0; -+ -+ n_bytes = n_blocks * dev->chunk_bit_stride; -+ -+ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); -+ -+ return ok ? 1 : 0; -+} -+ -+static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, -+ struct yaffs_obj *obj) -+{ -+ cp->obj_id = obj->obj_id; -+ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; -+ cp->hdr_chunk = obj->hdr_chunk; -+ cp->variant_type = obj->variant_type; -+ cp->deleted = obj->deleted; -+ cp->soft_del = obj->soft_del; -+ cp->unlinked = obj->unlinked; -+ cp->fake = obj->fake; -+ cp->rename_allowed = obj->rename_allowed; -+ cp->unlink_allowed = obj->unlink_allowed; -+ cp->serial = obj->serial; -+ cp->n_data_chunks = obj->n_data_chunks; -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; -+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; -+} -+ -+static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, -+ struct yaffs_checkpt_obj *cp) -+{ -+ struct yaffs_obj *parent; -+ -+ if (obj->variant_type != cp->variant_type) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", -+ cp->obj_id, cp->variant_type, cp->hdr_chunk, -+ obj->variant_type); -+ return 0; -+ } -+ -+ obj->obj_id = cp->obj_id; -+ -+ if (cp->parent_id) -+ parent = yaffs_find_or_create_by_number(obj->my_dev, -+ cp->parent_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ else -+ parent = NULL; -+ -+ if (parent) { -+ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", -+ cp->obj_id, cp->parent_id, -+ cp->variant_type, cp->hdr_chunk, -+ parent->variant_type); -+ return 0; -+ } -+ yaffs_add_obj_to_dir(parent, obj); -+ } -+ -+ obj->hdr_chunk = cp->hdr_chunk; -+ obj->variant_type = cp->variant_type; -+ obj->deleted = cp->deleted; -+ obj->soft_del = cp->soft_del; -+ obj->unlinked = cp->unlinked; -+ obj->fake = cp->fake; -+ obj->rename_allowed = cp->rename_allowed; -+ obj->unlink_allowed = cp->unlink_allowed; -+ obj->serial = cp->serial; -+ obj->n_data_chunks = cp->n_data_chunks; -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; -+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; -+ -+ if (obj->hdr_chunk > 0) -+ obj->lazy_loaded = 1; -+ return 1; -+} -+ -+static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, -+ struct yaffs_tnode *tn, u32 level, -+ int chunk_offset) -+{ -+ int i; -+ struct yaffs_dev *dev = in->my_dev; -+ int ok = 1; -+ u32 base_offset; -+ -+ if (!tn) -+ return 1; -+ -+ if (level > 0) { -+ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { -+ if (!tn->internal[i]) -+ continue; -+ ok = yaffs2_checkpt_tnode_worker(in, -+ tn->internal[i], -+ level - 1, -+ (chunk_offset << -+ YAFFS_TNODES_INTERNAL_BITS) + i); -+ } -+ return ok; -+ } -+ -+ /* Level 0 tnode */ -+ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; -+ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == -+ sizeof(base_offset)); -+ if (ok) -+ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == -+ dev->tnode_size); -+ -+ return ok; -+} -+ -+static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) -+{ -+ u32 end_marker = ~0; -+ int ok = 1; -+ -+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return ok; -+ -+ ok = yaffs2_checkpt_tnode_worker(obj, -+ obj->variant.file_variant.top, -+ obj->variant.file_variant. -+ top_level, 0); -+ if (ok) -+ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, -+ sizeof(end_marker)) == sizeof(end_marker)); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) -+{ -+ u32 base_chunk; -+ int ok = 1; -+ struct yaffs_dev *dev = obj->my_dev; -+ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; -+ struct yaffs_tnode *tn; -+ int nread = 0; -+ -+ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == -+ sizeof(base_chunk)); -+ -+ while (ok && (~base_chunk)) { -+ nread++; -+ /* Read level 0 tnode */ -+ -+ tn = yaffs_get_tnode(dev); -+ if (tn) -+ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == -+ dev->tnode_size); -+ else -+ ok = 0; -+ -+ if (tn && ok) -+ ok = yaffs_add_find_tnode_0(dev, -+ file_stuct_ptr, -+ base_chunk, tn) ? 1 : 0; -+ -+ if (ok) -+ ok = (yaffs2_checkpt_rd -+ (dev, &base_chunk, -+ sizeof(base_chunk)) == sizeof(base_chunk)); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint read tnodes %d records, last %d. ok %d", -+ nread, base_chunk, ok); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_checkpt_obj cp; -+ int i; -+ int ok = 1; -+ struct list_head *lh; -+ -+ /* Iterate through the objects in each hash entry, -+ * dumping them to the checkpointing stream. -+ */ -+ -+ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each(lh, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ if (!obj->defered_free) { -+ yaffs2_obj_checkpt_obj(&cp, obj); -+ cp.struct_type = sizeof(cp); -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", -+ cp.obj_id, cp.parent_id, -+ cp.variant_type, cp.hdr_chunk, obj); -+ -+ ok = (yaffs2_checkpt_wr(dev, &cp, -+ sizeof(cp)) == sizeof(cp)); -+ -+ if (ok && -+ obj->variant_type == -+ YAFFS_OBJECT_TYPE_FILE) -+ ok = yaffs2_wr_checkpt_tnodes(obj); -+ } -+ } -+ } -+ -+ /* Dump end of list */ -+ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); -+ cp.struct_type = sizeof(cp); -+ -+ if (ok) -+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_checkpt_obj cp; -+ int ok = 1; -+ int done = 0; -+ LIST_HEAD(hard_list); -+ -+ -+ while (ok && !done) { -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (cp.struct_type != sizeof(cp)) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "struct size %d instead of %d ok %d", -+ cp.struct_type, (int)sizeof(cp), ok); -+ ok = 0; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint read object %d parent %d type %d chunk %d ", -+ cp.obj_id, cp.parent_id, cp.variant_type, -+ cp.hdr_chunk); -+ -+ if (ok && cp.obj_id == ~0) { -+ done = 1; -+ } else if (ok) { -+ obj = -+ yaffs_find_or_create_by_number(dev, cp.obj_id, -+ cp.variant_type); -+ if (obj) { -+ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); -+ if (!ok) -+ break; -+ if (obj->variant_type == -+ YAFFS_OBJECT_TYPE_FILE) { -+ ok = yaffs2_rd_checkpt_tnodes(obj); -+ } else if (obj->variant_type == -+ YAFFS_OBJECT_TYPE_HARDLINK) { -+ list_add(&obj->hard_links, &hard_list); -+ } -+ } else { -+ ok = 0; -+ } -+ } -+ } -+ -+ if (ok) -+ yaffs_link_fixup(dev, &hard_list); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) -+{ -+ u32 checkpt_sum; -+ int ok; -+ -+ yaffs2_get_checkpt_sum(dev, &checkpt_sum); -+ -+ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == -+ sizeof(checkpt_sum)); -+ -+ if (!ok) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) -+{ -+ u32 checkpt_sum0; -+ u32 checkpt_sum1; -+ int ok; -+ -+ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); -+ -+ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == -+ sizeof(checkpt_sum1)); -+ -+ if (!ok) -+ return 0; -+ -+ if (checkpt_sum0 != checkpt_sum1) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) -+{ -+ int ok = 1; -+ -+ if (!yaffs2_checkpt_required(dev)) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "skipping checkpoint write"); -+ ok = 0; -+ } -+ -+ if (ok) -+ ok = yaffs2_checkpt_open(dev, 1); -+ -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint validity"); -+ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint device"); -+ ok = yaffs2_wr_checkpt_dev(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint objects"); -+ ok = yaffs2_wr_checkpt_objs(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint validity"); -+ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); -+ } -+ -+ if (ok) -+ ok = yaffs2_wr_checkpt_sum(dev); -+ -+ if (!yaffs_checkpt_close(dev)) -+ ok = 0; -+ -+ if (ok) -+ dev->is_checkpointed = 1; -+ else -+ dev->is_checkpointed = 0; -+ -+ return dev->is_checkpointed; -+} -+ -+static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) -+{ -+ int ok = 1; -+ -+ if (!dev->param.is_yaffs2) -+ ok = 0; -+ -+ if (ok && dev->param.skip_checkpt_rd) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "skipping checkpoint read"); -+ ok = 0; -+ } -+ -+ if (ok) -+ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ -+ -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint validity"); -+ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint device"); -+ ok = yaffs2_rd_checkpt_dev(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint objects"); -+ ok = yaffs2_rd_checkpt_objs(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint validity"); -+ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); -+ } -+ -+ if (ok) { -+ ok = yaffs2_rd_checkpt_sum(dev); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint checksum %d", ok); -+ } -+ -+ if (!yaffs_checkpt_close(dev)) -+ ok = 0; -+ -+ if (ok) -+ dev->is_checkpointed = 1; -+ else -+ dev->is_checkpointed = 0; -+ -+ return ok ? 1 : 0; -+} -+ -+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) -+{ -+ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { -+ dev->is_checkpointed = 0; -+ yaffs2_checkpt_invalidate_stream(dev); -+ } -+ if (dev->param.sb_dirty_fn) -+ dev->param.sb_dirty_fn(dev); -+} -+ -+int yaffs_checkpoint_save(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "save entry: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ yaffs_verify_objects(dev); -+ yaffs_verify_blocks(dev); -+ yaffs_verify_free_chunks(dev); -+ -+ if (!dev->is_checkpointed) { -+ yaffs2_checkpt_invalidate(dev); -+ yaffs2_wr_checkpt_data(dev); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, -+ "save exit: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ return dev->is_checkpointed; -+} -+ -+int yaffs2_checkpt_restore(struct yaffs_dev *dev) -+{ -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "restore entry: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ retval = yaffs2_rd_checkpt_data(dev); -+ -+ if (dev->is_checkpointed) { -+ yaffs_verify_objects(dev); -+ yaffs_verify_blocks(dev); -+ yaffs_verify_free_chunks(dev); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "restore exit: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ return retval; -+} -+ -+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) -+{ -+ /* if new_size > old_file_size. -+ * We're going to be writing a hole. -+ * If the hole is small then write zeros otherwise write a start -+ * of hole marker. -+ */ -+ loff_t old_file_size; -+ loff_t increase; -+ int small_hole; -+ int result = YAFFS_OK; -+ struct yaffs_dev *dev = NULL; -+ u8 *local_buffer = NULL; -+ int small_increase_ok = 0; -+ -+ if (!obj) -+ return YAFFS_FAIL; -+ -+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return YAFFS_FAIL; -+ -+ dev = obj->my_dev; -+ -+ /* Bail out if not yaffs2 mode */ -+ if (!dev->param.is_yaffs2) -+ return YAFFS_OK; -+ -+ old_file_size = obj->variant.file_variant.file_size; -+ -+ if (new_size <= old_file_size) -+ return YAFFS_OK; -+ -+ increase = new_size - old_file_size; -+ -+ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && -+ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) -+ small_hole = 1; -+ else -+ small_hole = 0; -+ -+ if (small_hole) -+ local_buffer = yaffs_get_temp_buffer(dev); -+ -+ if (local_buffer) { -+ /* fill hole with zero bytes */ -+ loff_t pos = old_file_size; -+ int this_write; -+ int written; -+ memset(local_buffer, 0, dev->data_bytes_per_chunk); -+ small_increase_ok = 1; -+ -+ while (increase > 0 && small_increase_ok) { -+ this_write = increase; -+ if (this_write > dev->data_bytes_per_chunk) -+ this_write = dev->data_bytes_per_chunk; -+ written = -+ yaffs_do_file_wr(obj, local_buffer, pos, this_write, -+ 0); -+ if (written == this_write) { -+ pos += this_write; -+ increase -= this_write; -+ } else { -+ small_increase_ok = 0; -+ } -+ } -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ -+ /* If out of space then reverse any chunks we've added */ -+ if (!small_increase_ok) -+ yaffs_resize_file_down(obj, old_file_size); -+ } -+ -+ if (!small_increase_ok && -+ obj->parent && -+ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && -+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { -+ /* Write a hole start header with the old file size */ -+ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); -+ } -+ -+ return result; -+} -+ -+struct yaffs_block_index { -+ int seq; -+ int block; -+}; -+ -+static int yaffs2_ybicmp(const void *a, const void *b) -+{ -+ int aseq = ((struct yaffs_block_index *)a)->seq; -+ int bseq = ((struct yaffs_block_index *)b)->seq; -+ int ablock = ((struct yaffs_block_index *)a)->block; -+ int bblock = ((struct yaffs_block_index *)b)->block; -+ -+ if (aseq == bseq) -+ return ablock - bblock; -+ -+ return aseq - bseq; -+} -+ -+static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, -+ int blk, int chunk_in_block, -+ int *found_chunks, -+ u8 *chunk_data, -+ struct list_head *hard_list, -+ int summary_available) -+{ -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_obj *in; -+ struct yaffs_obj *parent; -+ int equiv_id; -+ loff_t file_size; -+ int is_shrink; -+ int is_unlinked; -+ struct yaffs_ext_tags tags; -+ int result; -+ int alloc_failed = 0; -+ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; -+ struct yaffs_file_var *file_var; -+ struct yaffs_hardlink_var *hl_var; -+ struct yaffs_symlink_var *sl_var; -+ -+ if (summary_available) { -+ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); -+ tags.seq_number = bi->seq_number; -+ } -+ -+ if (!summary_available || tags.obj_id == 0) { -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); -+ dev->tags_used++; -+ } else { -+ dev->summary_used++; -+ } -+ -+ /* Let's have a good look at this chunk... */ -+ -+ if (!tags.chunk_used) { -+ /* An unassigned chunk in the block. -+ * If there are used chunks after this one, then -+ * it is a chunk that was skipped due to failing -+ * the erased check. Just skip it so that it can -+ * be deleted. -+ * But, more typically, We get here when this is -+ * an unallocated chunk and his means that -+ * either the block is empty or this is the one -+ * being allocated from -+ */ -+ -+ if (*found_chunks) { -+ /* This is a chunk that was skipped due -+ * to failing the erased check */ -+ } else if (chunk_in_block == 0) { -+ /* We're looking at the first chunk in -+ * the block so the block is unused */ -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ } else { -+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ if (dev->seq_number == bi->seq_number) { -+ /* Allocating from this block*/ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Allocating from %d %d", -+ blk, chunk_in_block); -+ -+ bi->block_state = -+ YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->alloc_block = blk; -+ dev->alloc_page = chunk_in_block; -+ dev->alloc_block_finder = blk; -+ } else { -+ /* This is a partially written block -+ * that is not the current -+ * allocation block. -+ */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Partially written block %d detected. gc will fix this.", -+ blk); -+ } -+ } -+ } -+ -+ dev->n_free_chunks++; -+ -+ } else if (tags.ecc_result == -+ YAFFS_ECC_RESULT_UNFIXED) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Unfixed ECC in chunk(%d:%d), chunk ignored", -+ blk, chunk_in_block); -+ dev->n_free_chunks++; -+ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || -+ tags.chunk_id > YAFFS_MAX_CHUNK_ID || -+ tags.obj_id == YAFFS_OBJECTID_SUMMARY || -+ (tags.chunk_id > 0 && -+ tags.n_bytes > dev->data_bytes_per_chunk) || -+ tags.seq_number != bi->seq_number) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", -+ blk, chunk_in_block, tags.obj_id, -+ tags.chunk_id, tags.n_bytes); -+ dev->n_free_chunks++; -+ } else if (tags.chunk_id > 0) { -+ /* chunk_id > 0 so it is a data chunk... */ -+ loff_t endpos; -+ loff_t chunk_base = (tags.chunk_id - 1) * -+ dev->data_bytes_per_chunk; -+ -+ *found_chunks = 1; -+ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ YAFFS_OBJECT_TYPE_FILE); -+ if (!in) -+ /* Out of memory */ -+ alloc_failed = 1; -+ -+ if (in && -+ in->variant_type == YAFFS_OBJECT_TYPE_FILE && -+ chunk_base < in->variant.file_variant.shrink_size) { -+ /* This has not been invalidated by -+ * a resize */ -+ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, -+ chunk, -1)) -+ alloc_failed = 1; -+ -+ /* File size is calculated by looking at -+ * the data chunks if we have not -+ * seen an object header yet. -+ * Stop this practice once we find an -+ * object header. -+ */ -+ endpos = chunk_base + tags.n_bytes; -+ -+ if (!in->valid && -+ in->variant.file_variant.scanned_size < endpos) { -+ in->variant.file_variant. -+ scanned_size = endpos; -+ in->variant.file_variant. -+ file_size = endpos; -+ } -+ } else if (in) { -+ /* This chunk has been invalidated by a -+ * resize, or a past file deletion -+ * so delete the chunk*/ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ } -+ } else { -+ /* chunk_id == 0, so it is an ObjectHeader. -+ * Thus, we read in the object header and make -+ * the object -+ */ -+ *found_chunks = 1; -+ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ -+ oh = NULL; -+ in = NULL; -+ -+ if (tags.extra_available) { -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ tags.extra_obj_type); -+ if (!in) -+ alloc_failed = 1; -+ } -+ -+ if (!in || -+ (!in->valid && dev->param.disable_lazy_load) || -+ tags.extra_shadows || -+ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { -+ -+ /* If we don't have valid info then we -+ * need to read the chunk -+ * TODO In future we can probably defer -+ * reading the chunk and living with -+ * invalid data until needed. -+ */ -+ -+ result = yaffs_rd_chunk_tags_nand(dev, -+ chunk, -+ chunk_data, -+ NULL); -+ -+ oh = (struct yaffs_obj_hdr *)chunk_data; -+ -+ if (dev->param.inband_tags) { -+ /* Fix up the header if they got -+ * corrupted by inband tags */ -+ oh->shadows_obj = -+ oh->inband_shadowed_obj_id; -+ oh->is_shrink = -+ oh->inband_is_shrink; -+ } -+ -+ if (!in) { -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, oh->type); -+ if (!in) -+ alloc_failed = 1; -+ } -+ } -+ -+ if (!in) { -+ /* TODO Hoosterman we have a problem! */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", -+ tags.obj_id, chunk); -+ return YAFFS_FAIL; -+ } -+ -+ if (in->valid) { -+ /* We have already filled this one. -+ * We have a duplicate that will be -+ * discarded, but we first have to suck -+ * out resize info if it is a file. -+ */ -+ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && -+ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || -+ (tags.extra_available && -+ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) -+ )) { -+ loff_t this_size = (oh) ? -+ yaffs_oh_to_size(oh) : -+ tags.extra_file_size; -+ u32 parent_obj_id = (oh) ? -+ oh->parent_obj_id : -+ tags.extra_parent_id; -+ -+ is_shrink = (oh) ? -+ oh->is_shrink : -+ tags.extra_is_shrink; -+ -+ /* If it is deleted (unlinked -+ * at start also means deleted) -+ * we treat the file size as -+ * being zeroed at this point. -+ */ -+ if (parent_obj_id == YAFFS_OBJECTID_DELETED || -+ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { -+ this_size = 0; -+ is_shrink = 1; -+ } -+ -+ if (is_shrink && -+ in->variant.file_variant.shrink_size > -+ this_size) -+ in->variant.file_variant.shrink_size = -+ this_size; -+ -+ if (is_shrink) -+ bi->has_shrink_hdr = 1; -+ } -+ /* Use existing - destroy this one. */ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ } -+ -+ if (!in->valid && in->variant_type != -+ (oh ? oh->type : tags.extra_obj_type)) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", -+ oh ? oh->type : tags.extra_obj_type, -+ in->variant_type, tags.obj_id, -+ chunk); -+ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); -+ } -+ -+ if (!in->valid && -+ (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { -+ /* We only load some info, don't fiddle -+ * with directory structure */ -+ in->valid = 1; -+ -+ if (oh) { -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->lazy_loaded = 0; -+ } else { -+ in->lazy_loaded = 1; -+ } -+ in->hdr_chunk = chunk; -+ -+ } else if (!in->valid) { -+ /* we need to load this info */ -+ in->valid = 1; -+ in->hdr_chunk = chunk; -+ if (oh) { -+ in->variant_type = oh->type; -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ -+ if (oh->shadows_obj > 0) -+ yaffs_handle_shadowed_obj(dev, -+ oh->shadows_obj, 1); -+ -+ yaffs_set_obj_name_from_oh(in, oh); -+ parent = yaffs_find_or_create_by_number(dev, -+ oh->parent_obj_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ file_size = yaffs_oh_to_size(oh); -+ is_shrink = oh->is_shrink; -+ equiv_id = oh->equiv_id; -+ } else { -+ in->variant_type = tags.extra_obj_type; -+ parent = yaffs_find_or_create_by_number(dev, -+ tags.extra_parent_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ file_size = tags.extra_file_size; -+ is_shrink = tags.extra_is_shrink; -+ equiv_id = tags.extra_equiv_id; -+ in->lazy_loaded = 1; -+ } -+ in->dirty = 0; -+ -+ if (!parent) -+ alloc_failed = 1; -+ -+ /* directory stuff... -+ * hook up to parent -+ */ -+ -+ if (parent && -+ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { -+ /* Set up as a directory */ -+ parent->variant_type = -+ YAFFS_OBJECT_TYPE_DIRECTORY; -+ INIT_LIST_HEAD(&parent-> -+ variant.dir_variant.children); -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ /* Hoosterman, another problem.... -+ * Trying to use a non-directory as a directory -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." -+ ); -+ parent = dev->lost_n_found; -+ } -+ yaffs_add_obj_to_dir(parent, in); -+ -+ is_unlinked = (parent == dev->del_dir) || -+ (parent == dev->unlinked_dir); -+ -+ if (is_shrink) -+ /* Mark the block */ -+ bi->has_shrink_hdr = 1; -+ -+ /* Note re hardlinks. -+ * Since we might scan a hardlink before its equivalent -+ * object is scanned we put them all in a list. -+ * After scanning is complete, we should have all the -+ * objects, so we run through this list and fix up all -+ * the chains. -+ */ -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Todo got a problem */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ file_var = &in->variant.file_variant; -+ if (file_var->scanned_size < file_size) { -+ /* This covers the case where the file -+ * size is greater than the data held. -+ * This will happen if the file is -+ * resized to be larger than its -+ * current data extents. -+ */ -+ file_var->file_size = file_size; -+ file_var->scanned_size = file_size; -+ } -+ -+ if (file_var->shrink_size > file_size) -+ file_var->shrink_size = file_size; -+ -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ hl_var = &in->variant.hardlink_variant; -+ if (!is_unlinked) { -+ hl_var->equiv_id = equiv_id; -+ list_add(&in->hard_links, hard_list); -+ } -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ sl_var = &in->variant.symlink_variant; -+ if (oh) { -+ sl_var->alias = -+ yaffs_clone_str(oh->alias); -+ if (!sl_var->alias) -+ alloc_failed = 1; -+ } -+ break; -+ } -+ } -+ } -+ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+int yaffs2_scan_backwards(struct yaffs_dev *dev) -+{ -+ int blk; -+ int block_iter; -+ int start_iter; -+ int end_iter; -+ int n_to_scan = 0; -+ enum yaffs_block_state state; -+ int c; -+ LIST_HEAD(hard_list); -+ struct yaffs_block_info *bi; -+ u32 seq_number; -+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ u8 *chunk_data; -+ int found_chunks; -+ int alloc_failed = 0; -+ struct yaffs_block_index *block_index = NULL; -+ int alt_block_index = 0; -+ int summary_available; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; -+ -+ block_index = -+ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); -+ -+ if (!block_index) { -+ block_index = -+ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); -+ alt_block_index = 1; -+ } -+ -+ if (!block_index) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs2_scan_backwards() could not allocate block index!" -+ ); -+ return YAFFS_FAIL; -+ } -+ -+ dev->blocks_in_checkpt = 0; -+ -+ chunk_data = yaffs_get_temp_buffer(dev); -+ -+ /* Scan all the blocks to determine their state */ -+ bi = dev->block_info; -+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; -+ blk++) { -+ yaffs_clear_chunk_bits(dev, blk); -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ -+ yaffs_query_init_block_state(dev, blk, &state, &seq_number); -+ -+ bi->block_state = state; -+ bi->seq_number = seq_number; -+ -+ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, -+ "Block scanning block %d state %d seq %d", -+ blk, bi->block_state, seq_number); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { -+ dev->blocks_in_checkpt++; -+ -+ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, -+ "block %d is bad", blk); -+ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += dev->param.chunks_per_block; -+ } else if (bi->block_state == -+ YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* Determine the highest sequence number */ -+ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && -+ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { -+ block_index[n_to_scan].seq = seq_number; -+ block_index[n_to_scan].block = blk; -+ n_to_scan++; -+ if (seq_number >= dev->seq_number) -+ dev->seq_number = seq_number; -+ } else { -+ /* TODO: Nasty sequence number! */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Block scanning block %d has bad sequence number %d", -+ blk, seq_number); -+ } -+ } -+ bi++; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); -+ -+ cond_resched(); -+ -+ /* Sort the blocks by sequence number */ -+ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), -+ yaffs2_ybicmp, NULL); -+ -+ cond_resched(); -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); -+ -+ /* Now scan the blocks looking at the data. */ -+ start_iter = 0; -+ end_iter = n_to_scan - 1; -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); -+ -+ /* For each block.... backwards */ -+ for (block_iter = end_iter; -+ !alloc_failed && block_iter >= start_iter; -+ block_iter--) { -+ /* Cooperative multitasking! This loop can run for so -+ long that watchdog timers expire. */ -+ cond_resched(); -+ -+ /* get the block to scan in the correct order */ -+ blk = block_index[block_iter].block; -+ bi = yaffs_get_block_info(dev, blk); -+ -+ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); -+ -+ /* For each chunk in each block that needs scanning.... */ -+ found_chunks = 0; -+ if (summary_available) -+ c = dev->chunks_per_summary - 1; -+ else -+ c = dev->param.chunks_per_block - 1; -+ -+ for (/* c is already initialised */; -+ !alloc_failed && c >= 0 && -+ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); -+ c--) { -+ /* Scan backwards... -+ * Read the tags and decide what to do -+ */ -+ if (yaffs2_scan_chunk(dev, bi, blk, c, -+ &found_chunks, chunk_data, -+ &hard_list, summary_available) == -+ YAFFS_FAIL) -+ alloc_failed = 1; -+ } -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* If we got this far while scanning, then the block -+ * is fully allocated. */ -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ } -+ -+ /* Now let's see if it was dirty */ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state == YAFFS_BLOCK_STATE_FULL) { -+ yaffs_block_became_dirty(dev, blk); -+ } -+ } -+ -+ yaffs_skip_rest_of_block(dev); -+ -+ if (alt_block_index) -+ vfree(block_index); -+ else -+ kfree(block_index); -+ -+ /* Ok, we've done all the scanning. -+ * Fix up the hard link chains. -+ * We have scanned all the objects, now it's time to add these -+ * hardlinks. -+ */ -+ yaffs_link_fixup(dev, &hard_list); -+ -+ yaffs_release_temp_buffer(dev, chunk_data); -+ -+ if (alloc_failed) -+ return YAFFS_FAIL; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.18.14/fs/yaffs2/yaffs_yaffs2.h ---- linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yaffs_yaffs2.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_YAFFS2_H__ -+#define __YAFFS_YAFFS2_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); -+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); -+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi); -+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, -+ struct yaffs_block_info *bi); -+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); -+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); -+int yaffs2_checkpt_required(struct yaffs_dev *dev); -+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); -+ -+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); -+int yaffs2_checkpt_save(struct yaffs_dev *dev); -+int yaffs2_checkpt_restore(struct yaffs_dev *dev); -+ -+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); -+int yaffs2_scan_backwards(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.18.14.orig/fs/yaffs2/yportenv.h linux-3.18.14/fs/yaffs2/yportenv.h ---- linux-3.18.14.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.18.14/fs/yaffs2/yportenv.h 2015-06-14 21:23:22.000000000 +0200 -@@ -0,0 +1,85 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YPORTENV_H__ -+#define __YPORTENV_H__ -+ -+/* -+ * Define the MTD version in terms of Linux Kernel versions -+ * This allows yaffs to be used independantly of the kernel -+ * as well as with it. -+ */ -+ -+#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) -+ -+#ifdef YAFFS_OUT_OF_TREE -+#include "moduleconfig.h" -+#endif -+ -+#include -+#define MTD_VERSION_CODE LINUX_VERSION_CODE -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* These type wrappings are used to support Unicode names in WinCE. */ -+#define YCHAR char -+#define YUCHAR unsigned char -+#define _Y(x) x -+ -+#define YAFFS_LOSTNFOUND_NAME "lost+found" -+#define YAFFS_LOSTNFOUND_PREFIX "obj" -+ -+ -+#define YAFFS_ROOT_MODE 0755 -+#define YAFFS_LOSTNFOUND_MODE 0700 -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+#define Y_CURRENT_TIME CURRENT_TIME.tv_sec -+#define Y_TIME_CONVERT(x) (x).tv_sec -+#else -+#define Y_CURRENT_TIME CURRENT_TIME -+#define Y_TIME_CONVERT(x) (x) -+#endif -+ -+#define compile_time_assertion(assertion) \ -+ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) -+ -+ -+#define yaffs_printf(msk, fmt, ...) \ -+ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) -+ -+#define yaffs_trace(msk, fmt, ...) do { \ -+ if (yaffs_trace_mask & (msk)) \ -+ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ -+} while (0) -+ -+ -+#endif diff --git a/target/linux/patches/3.18.16/bsd-compatibility.patch b/target/linux/patches/3.18.16/bsd-compatibility.patch new file mode 100644 index 000000000..b954b658f --- /dev/null +++ b/target/linux/patches/3.18.16/bsd-compatibility.patch @@ -0,0 +1,2538 @@ +diff -Nur linux-3.11.5.orig/scripts/Makefile.lib linux-3.11.5/scripts/Makefile.lib +--- linux-3.11.5.orig/scripts/Makefile.lib 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/Makefile.lib 2013-10-16 18:09:31.000000000 +0200 +@@ -281,7 +281,12 @@ + size_append = printf $(shell \ + dec_size=0; \ + for F in $1; do \ +- fsize=$$(stat -c "%s" $$F); \ ++ if stat -qs .>/dev/null 2>&1; then \ ++ statcmd='stat -f %z'; \ ++ else \ ++ statcmd='stat -c %s'; \ ++ fi; \ ++ fsize=$$($$statcmd $$F); \ + dec_size=$$(expr $$dec_size + $$fsize); \ + done; \ + printf "%08x\n" $$dec_size | \ +diff -Nur linux-3.11.5.orig/scripts/mod/mk_elfconfig.c linux-3.11.5/scripts/mod/mk_elfconfig.c +--- linux-3.11.5.orig/scripts/mod/mk_elfconfig.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/mk_elfconfig.c 2013-10-16 18:09:31.000000000 +0200 +@@ -1,7 +1,18 @@ + #include + #include + #include +-#include ++ ++#define EI_NIDENT (16) ++#define ELFMAG "\177ELF" ++ ++#define SELFMAG 4 ++#define EI_CLASS 4 ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ + + int + main(int argc, char **argv) +diff -Nur linux-3.11.5.orig/scripts/mod/modpost.h linux-3.11.5/scripts/mod/modpost.h +--- linux-3.11.5.orig/scripts/mod/modpost.h 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/modpost.h 2013-10-16 18:09:31.000000000 +0200 +@@ -7,7 +7,2453 @@ + #include + #include + #include +-#include ++ ++ ++/* This file defines standard ELF types, structures, and macros. ++ Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++#ifndef _ELF_H ++#define _ELF_H 1 ++ ++__BEGIN_DECLS ++ ++/* Standard ELF types. */ ++ ++#include ++ ++/* Type for a 16-bit quantity. */ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++/* Types for signed and unsigned 32-bit quantities. */ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++/* Types for signed and unsigned 64-bit quantities. */ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++/* Type of addresses. */ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++/* Type of file offsets. */ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++/* Type for section indices, which are 16-bit quantities. */ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++/* Type for version symbol information. */ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++ ++/* The ELF file header. This appears at the start of every ELF file. */ ++ ++#define EI_NIDENT (16) ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf32_Half e_type; /* Object file type */ ++ Elf32_Half e_machine; /* Architecture */ ++ Elf32_Word e_version; /* Object file version */ ++ Elf32_Addr e_entry; /* Entry point virtual address */ ++ Elf32_Off e_phoff; /* Program header table file offset */ ++ Elf32_Off e_shoff; /* Section header table file offset */ ++ Elf32_Word e_flags; /* Processor-specific flags */ ++ Elf32_Half e_ehsize; /* ELF header size in bytes */ ++ Elf32_Half e_phentsize; /* Program header table entry size */ ++ Elf32_Half e_phnum; /* Program header table entry count */ ++ Elf32_Half e_shentsize; /* Section header table entry size */ ++ Elf32_Half e_shnum; /* Section header table entry count */ ++ Elf32_Half e_shstrndx; /* Section header string table index */ ++} Elf32_Ehdr; ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf64_Half e_type; /* Object file type */ ++ Elf64_Half e_machine; /* Architecture */ ++ Elf64_Word e_version; /* Object file version */ ++ Elf64_Addr e_entry; /* Entry point virtual address */ ++ Elf64_Off e_phoff; /* Program header table file offset */ ++ Elf64_Off e_shoff; /* Section header table file offset */ ++ Elf64_Word e_flags; /* Processor-specific flags */ ++ Elf64_Half e_ehsize; /* ELF header size in bytes */ ++ Elf64_Half e_phentsize; /* Program header table entry size */ ++ Elf64_Half e_phnum; /* Program header table entry count */ ++ Elf64_Half e_shentsize; /* Section header table entry size */ ++ Elf64_Half e_shnum; /* Section header table entry count */ ++ Elf64_Half e_shstrndx; /* Section header string table index */ ++} Elf64_Ehdr; ++ ++/* Fields in the e_ident array. The EI_* macros are indices into the ++ array. The macros under each EI_* macro are the values the byte ++ may have. */ ++ ++#define EI_MAG0 0 /* File identification byte 0 index */ ++#define ELFMAG0 0x7f /* Magic number byte 0 */ ++ ++#define EI_MAG1 1 /* File identification byte 1 index */ ++#define ELFMAG1 'E' /* Magic number byte 1 */ ++ ++#define EI_MAG2 2 /* File identification byte 2 index */ ++#define ELFMAG2 'L' /* Magic number byte 2 */ ++ ++#define EI_MAG3 3 /* File identification byte 3 index */ ++#define ELFMAG3 'F' /* Magic number byte 3 */ ++ ++/* Conglomeration of the identification bytes, for easy testing as a word. */ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 /* File class byte index */ ++#define ELFCLASSNONE 0 /* Invalid class */ ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATANONE 0 /* Invalid data encoding */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 /* File version byte index */ ++ /* Value must be EV_CURRENT */ ++ ++#define EI_OSABI 7 /* OS ABI identification */ ++#define ELFOSABI_NONE 0 /* UNIX System V ABI */ ++#define ELFOSABI_SYSV 0 /* Alias. */ ++#define ELFOSABI_HPUX 1 /* HP-UX */ ++#define ELFOSABI_NETBSD 2 /* NetBSD. */ ++#define ELFOSABI_LINUX 3 /* Linux. */ ++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ ++#define ELFOSABI_AIX 7 /* IBM AIX. */ ++#define ELFOSABI_IRIX 8 /* SGI Irix. */ ++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ ++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ ++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ ++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ ++#define ELFOSABI_ARM 97 /* ARM */ ++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ ++ ++#define EI_ABIVERSION 8 /* ABI version */ ++ ++#define EI_PAD 9 /* Byte index of padding bytes */ ++ ++/* Legal values for e_type (object file type). */ ++ ++#define ET_NONE 0 /* No file type */ ++#define ET_REL 1 /* Relocatable file */ ++#define ET_EXEC 2 /* Executable file */ ++#define ET_DYN 3 /* Shared object file */ ++#define ET_CORE 4 /* Core file */ ++#define ET_NUM 5 /* Number of defined types */ ++#define ET_LOOS 0xfe00 /* OS-specific range start */ ++#define ET_HIOS 0xfeff /* OS-specific range end */ ++#define ET_LOPROC 0xff00 /* Processor-specific range start */ ++#define ET_HIPROC 0xffff /* Processor-specific range end */ ++ ++/* Legal values for e_machine (architecture). */ ++ ++#define EM_NONE 0 /* No machine */ ++#define EM_M32 1 /* AT&T WE 32100 */ ++#define EM_SPARC 2 /* SUN SPARC */ ++#define EM_386 3 /* Intel 80386 */ ++#define EM_68K 4 /* Motorola m68k family */ ++#define EM_88K 5 /* Motorola m88k family */ ++#define EM_860 7 /* Intel 80860 */ ++#define EM_MIPS 8 /* MIPS R3000 big-endian */ ++#define EM_S370 9 /* IBM System/370 */ ++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ ++ ++#define EM_PARISC 15 /* HPPA */ ++#define EM_VPP500 17 /* Fujitsu VPP500 */ ++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ ++#define EM_960 19 /* Intel 80960 */ ++#define EM_PPC 20 /* PowerPC */ ++#define EM_PPC64 21 /* PowerPC 64-bit */ ++#define EM_S390 22 /* IBM S390 */ ++ ++#define EM_V800 36 /* NEC V800 series */ ++#define EM_FR20 37 /* Fujitsu FR20 */ ++#define EM_RH32 38 /* TRW RH-32 */ ++#define EM_RCE 39 /* Motorola RCE */ ++#define EM_ARM 40 /* ARM */ ++#define EM_FAKE_ALPHA 41 /* Digital Alpha */ ++#define EM_SH 42 /* Hitachi SH */ ++#define EM_SPARCV9 43 /* SPARC v9 64-bit */ ++#define EM_TRICORE 44 /* Siemens Tricore */ ++#define EM_ARC 45 /* Argonaut RISC Core */ ++#define EM_H8_300 46 /* Hitachi H8/300 */ ++#define EM_H8_300H 47 /* Hitachi H8/300H */ ++#define EM_H8S 48 /* Hitachi H8S */ ++#define EM_H8_500 49 /* Hitachi H8/500 */ ++#define EM_IA_64 50 /* Intel Merced */ ++#define EM_MIPS_X 51 /* Stanford MIPS-X */ ++#define EM_COLDFIRE 52 /* Motorola Coldfire */ ++#define EM_68HC12 53 /* Motorola M68HC12 */ ++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ ++#define EM_PCP 55 /* Siemens PCP */ ++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ ++#define EM_NDR1 57 /* Denso NDR1 microprocessor */ ++#define EM_STARCORE 58 /* Motorola Start*Core processor */ ++#define EM_ME16 59 /* Toyota ME16 processor */ ++#define EM_ST100 60 /* STMicroelectronic ST100 processor */ ++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ ++#define EM_X86_64 62 /* AMD x86-64 architecture */ ++#define EM_PDSP 63 /* Sony DSP Processor */ ++ ++#define EM_FX66 66 /* Siemens FX66 microcontroller */ ++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ ++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ ++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ ++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ ++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ ++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ ++#define EM_SVX 73 /* Silicon Graphics SVx */ ++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ ++#define EM_VAX 75 /* Digital VAX */ ++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ ++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ ++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ ++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ ++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ ++#define EM_HUANY 81 /* Harvard University machine-independent object files */ ++#define EM_PRISM 82 /* SiTera Prism */ ++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ ++#define EM_FR30 84 /* Fujitsu FR30 */ ++#define EM_D10V 85 /* Mitsubishi D10V */ ++#define EM_D30V 86 /* Mitsubishi D30V */ ++#define EM_V850 87 /* NEC v850 */ ++#define EM_M32R 88 /* Mitsubishi M32R */ ++#define EM_MN10300 89 /* Matsushita MN10300 */ ++#define EM_MN10200 90 /* Matsushita MN10200 */ ++#define EM_PJ 91 /* picoJava */ ++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ ++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ ++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ ++#define EM_NUM 95 ++ ++/* If it is necessary to assign new unofficial EM_* values, please ++ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the ++ chances of collision with official or non-GNU unofficial values. */ ++ ++#define EM_ALPHA 0x9026 ++ ++/* Legal values for e_version (version). */ ++ ++#define EV_NONE 0 /* Invalid ELF version */ ++#define EV_CURRENT 1 /* Current version */ ++#define EV_NUM 2 ++ ++/* Section header. */ ++ ++typedef struct ++{ ++ Elf32_Word sh_name; /* Section name (string tbl index) */ ++ Elf32_Word sh_type; /* Section type */ ++ Elf32_Word sh_flags; /* Section flags */ ++ Elf32_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf32_Off sh_offset; /* Section file offset */ ++ Elf32_Word sh_size; /* Section size in bytes */ ++ Elf32_Word sh_link; /* Link to another section */ ++ Elf32_Word sh_info; /* Additional section information */ ++ Elf32_Word sh_addralign; /* Section alignment */ ++ Elf32_Word sh_entsize; /* Entry size if section holds table */ ++} Elf32_Shdr; ++ ++typedef struct ++{ ++ Elf64_Word sh_name; /* Section name (string tbl index) */ ++ Elf64_Word sh_type; /* Section type */ ++ Elf64_Xword sh_flags; /* Section flags */ ++ Elf64_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf64_Off sh_offset; /* Section file offset */ ++ Elf64_Xword sh_size; /* Section size in bytes */ ++ Elf64_Word sh_link; /* Link to another section */ ++ Elf64_Word sh_info; /* Additional section information */ ++ Elf64_Xword sh_addralign; /* Section alignment */ ++ Elf64_Xword sh_entsize; /* Entry size if section holds table */ ++} Elf64_Shdr; ++ ++/* Special section indices. */ ++ ++#define SHN_UNDEF 0 /* Undefined section */ ++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ ++#define SHN_LOPROC 0xff00 /* Start of processor-specific */ ++#define SHN_HIPROC 0xff1f /* End of processor-specific */ ++#define SHN_LOOS 0xff20 /* Start of OS-specific */ ++#define SHN_HIOS 0xff3f /* End of OS-specific */ ++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ ++#define SHN_COMMON 0xfff2 /* Associated symbol is common */ ++#define SHN_XINDEX 0xffff /* Index is in extra table. */ ++#define SHN_HIRESERVE 0xffff /* End of reserved indices */ ++ ++/* Legal values for sh_type (section type). */ ++ ++#define SHT_NULL 0 /* Section header table entry unused */ ++#define SHT_PROGBITS 1 /* Program data */ ++#define SHT_SYMTAB 2 /* Symbol table */ ++#define SHT_STRTAB 3 /* String table */ ++#define SHT_RELA 4 /* Relocation entries with addends */ ++#define SHT_HASH 5 /* Symbol hash table */ ++#define SHT_DYNAMIC 6 /* Dynamic linking information */ ++#define SHT_NOTE 7 /* Notes */ ++#define SHT_NOBITS 8 /* Program space with no data (bss) */ ++#define SHT_REL 9 /* Relocation entries, no addends */ ++#define SHT_SHLIB 10 /* Reserved */ ++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ ++#define SHT_INIT_ARRAY 14 /* Array of constructors */ ++#define SHT_FINI_ARRAY 15 /* Array of destructors */ ++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ ++#define SHT_GROUP 17 /* Section group */ ++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ ++#define SHT_NUM 19 /* Number of defined types. */ ++#define SHT_LOOS 0x60000000 /* Start OS-specific */ ++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ ++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ ++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ ++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ ++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ ++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ ++#define SHT_HIOS 0x6fffffff /* End OS-specific type */ ++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define SHT_LOUSER 0x80000000 /* Start of application-specific */ ++#define SHT_HIUSER 0x8fffffff /* End of application-specific */ ++ ++/* Legal values for sh_flags (section flags). */ ++ ++#define SHF_WRITE (1 << 0) /* Writable */ ++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ ++#define SHF_EXECINSTR (1 << 2) /* Executable */ ++#define SHF_MERGE (1 << 4) /* Might be merged */ ++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ ++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ ++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ ++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling ++ required */ ++#define SHF_GROUP (1 << 9) /* Section is member of a group. */ ++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ ++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ ++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Section group handling. */ ++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ ++ ++/* Symbol table entry. */ ++ ++typedef struct ++{ ++ Elf32_Word st_name; /* Symbol name (string tbl index) */ ++ Elf32_Addr st_value; /* Symbol value */ ++ Elf32_Word st_size; /* Symbol size */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf32_Section st_shndx; /* Section index */ ++} Elf32_Sym; ++ ++typedef struct ++{ ++ Elf64_Word st_name; /* Symbol name (string tbl index) */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf64_Section st_shndx; /* Section index */ ++ Elf64_Addr st_value; /* Symbol value */ ++ Elf64_Xword st_size; /* Symbol size */ ++} Elf64_Sym; ++ ++/* The syminfo section if available contains additional information about ++ every dynamic symbol. */ ++ ++typedef struct ++{ ++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf32_Half si_flags; /* Per symbol flags */ ++} Elf32_Syminfo; ++ ++typedef struct ++{ ++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf64_Half si_flags; /* Per symbol flags */ ++} Elf64_Syminfo; ++ ++/* Possible values for si_boundto. */ ++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ ++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ ++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ ++ ++/* Possible bitmasks for si_flags. */ ++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ ++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ ++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ ++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy ++ loaded */ ++/* Syminfo version values. */ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++ ++/* How to extract and insert information held in the st_info field. */ ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++/* Legal values for ST_BIND subfield of st_info (symbol binding). */ ++ ++#define STB_LOCAL 0 /* Local symbol */ ++#define STB_GLOBAL 1 /* Global symbol */ ++#define STB_WEAK 2 /* Weak symbol */ ++#define STB_NUM 3 /* Number of defined types. */ ++#define STB_LOOS 10 /* Start of OS-specific */ ++#define STB_HIOS 12 /* End of OS-specific */ ++#define STB_LOPROC 13 /* Start of processor-specific */ ++#define STB_HIPROC 15 /* End of processor-specific */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_NOTYPE 0 /* Symbol type is unspecified */ ++#define STT_OBJECT 1 /* Symbol is a data object */ ++#define STT_FUNC 2 /* Symbol is a code object */ ++#define STT_SECTION 3 /* Symbol associated with a section */ ++#define STT_FILE 4 /* Symbol's name is file name */ ++#define STT_COMMON 5 /* Symbol is a common data object */ ++#define STT_TLS 6 /* Symbol is thread-local data object*/ ++#define STT_NUM 7 /* Number of defined types. */ ++#define STT_LOOS 10 /* Start of OS-specific */ ++#define STT_HIOS 12 /* End of OS-specific */ ++#define STT_LOPROC 13 /* Start of processor-specific */ ++#define STT_HIPROC 15 /* End of processor-specific */ ++ ++ ++/* Symbol table indices are found in the hash buckets and chain table ++ of a symbol hash table section. This special index value indicates ++ the end of a chain, meaning no further symbols are found in that bucket. */ ++ ++#define STN_UNDEF 0 /* End of a chain. */ ++ ++ ++/* How to extract and insert information held in the st_other field. */ ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++ ++/* For ELF64 the definitions are the same. */ ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++/* Symbol visibility specification encoded in the st_other field. */ ++#define STV_DEFAULT 0 /* Default symbol visibility rules */ ++#define STV_INTERNAL 1 /* Processor specific hidden class */ ++#define STV_HIDDEN 2 /* Sym unavailable in other modules */ ++#define STV_PROTECTED 3 /* Not preemptible, not exported */ ++ ++ ++/* Relocation table entry without addend (in section of type SHT_REL). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++} Elf32_Rel; ++ ++/* I have seen two different definitions of the Elf64_Rel and ++ Elf64_Rela structures, so we'll leave them out until Novell (or ++ whoever) gets their act together. */ ++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++} Elf64_Rel; ++ ++/* Relocation table entry with addend (in section of type SHT_RELA). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++ Elf32_Sword r_addend; /* Addend */ ++} Elf32_Rela; ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++ Elf64_Sxword r_addend; /* Addend */ ++} Elf64_Rela; ++ ++/* How to extract and insert information held in the r_info field. */ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++/* Program segment header. */ ++ ++typedef struct ++{ ++ Elf32_Word p_type; /* Segment type */ ++ Elf32_Off p_offset; /* Segment file offset */ ++ Elf32_Addr p_vaddr; /* Segment virtual address */ ++ Elf32_Addr p_paddr; /* Segment physical address */ ++ Elf32_Word p_filesz; /* Segment size in file */ ++ Elf32_Word p_memsz; /* Segment size in memory */ ++ Elf32_Word p_flags; /* Segment flags */ ++ Elf32_Word p_align; /* Segment alignment */ ++} Elf32_Phdr; ++ ++typedef struct ++{ ++ Elf64_Word p_type; /* Segment type */ ++ Elf64_Word p_flags; /* Segment flags */ ++ Elf64_Off p_offset; /* Segment file offset */ ++ Elf64_Addr p_vaddr; /* Segment virtual address */ ++ Elf64_Addr p_paddr; /* Segment physical address */ ++ Elf64_Xword p_filesz; /* Segment size in file */ ++ Elf64_Xword p_memsz; /* Segment size in memory */ ++ Elf64_Xword p_align; /* Segment alignment */ ++} Elf64_Phdr; ++ ++/* Legal values for p_type (segment type). */ ++ ++#define PT_NULL 0 /* Program header table entry unused */ ++#define PT_LOAD 1 /* Loadable program segment */ ++#define PT_DYNAMIC 2 /* Dynamic linking information */ ++#define PT_INTERP 3 /* Program interpreter */ ++#define PT_NOTE 4 /* Auxiliary information */ ++#define PT_SHLIB 5 /* Reserved */ ++#define PT_PHDR 6 /* Entry for header table itself */ ++#define PT_TLS 7 /* Thread-local storage segment */ ++#define PT_NUM 8 /* Number of defined types */ ++#define PT_LOOS 0x60000000 /* Start of OS-specific */ ++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ ++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ ++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff /* End of OS-specific */ ++#define PT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define PT_HIPROC 0x7fffffff /* End of processor-specific */ ++ ++/* Legal values for p_flags (segment flags). */ ++ ++#define PF_X (1 << 0) /* Segment is executable */ ++#define PF_W (1 << 1) /* Segment is writable */ ++#define PF_R (1 << 2) /* Segment is readable */ ++#define PF_MASKOS 0x0ff00000 /* OS-specific */ ++#define PF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Legal values for note segment descriptor types for core files. */ ++ ++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ ++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ ++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ ++#define NT_PRXREG 4 /* Contains copy of prxregset struct */ ++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ ++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ ++#define NT_AUXV 6 /* Contains copy of auxv array */ ++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ ++#define NT_ASRS 8 /* Contains copy of asrset struct */ ++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ ++#define NT_PSINFO 13 /* Contains copy of psinfo struct */ ++#define NT_PRCRED 14 /* Contains copy of prcred struct */ ++#define NT_UTSNAME 15 /* Contains copy of utsname struct */ ++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ ++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ ++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ ++ ++/* Legal values for the note segment descriptor types for object files. */ ++ ++#define NT_VERSION 1 /* Contains a version string. */ ++ ++ ++/* Dynamic section entry. */ ++ ++typedef struct ++{ ++ Elf32_Sword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf32_Word d_val; /* Integer value */ ++ Elf32_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct ++{ ++ Elf64_Sxword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf64_Xword d_val; /* Integer value */ ++ Elf64_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf64_Dyn; ++ ++/* Legal values for d_tag (dynamic entry type). */ ++ ++#define DT_NULL 0 /* Marks end of dynamic section */ ++#define DT_NEEDED 1 /* Name of needed library */ ++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ ++#define DT_PLTGOT 3 /* Processor defined value */ ++#define DT_HASH 4 /* Address of symbol hash table */ ++#define DT_STRTAB 5 /* Address of string table */ ++#define DT_SYMTAB 6 /* Address of symbol table */ ++#define DT_RELA 7 /* Address of Rela relocs */ ++#define DT_RELASZ 8 /* Total size of Rela relocs */ ++#define DT_RELAENT 9 /* Size of one Rela reloc */ ++#define DT_STRSZ 10 /* Size of string table */ ++#define DT_SYMENT 11 /* Size of one symbol table entry */ ++#define DT_INIT 12 /* Address of init function */ ++#define DT_FINI 13 /* Address of termination function */ ++#define DT_SONAME 14 /* Name of shared object */ ++#define DT_RPATH 15 /* Library search path (deprecated) */ ++#define DT_SYMBOLIC 16 /* Start symbol search here */ ++#define DT_REL 17 /* Address of Rel relocs */ ++#define DT_RELSZ 18 /* Total size of Rel relocs */ ++#define DT_RELENT 19 /* Size of one Rel reloc */ ++#define DT_PLTREL 20 /* Type of reloc in PLT */ ++#define DT_DEBUG 21 /* For debugging; unspecified */ ++#define DT_TEXTREL 22 /* Reloc might modify .text */ ++#define DT_JMPREL 23 /* Address of PLT relocs */ ++#define DT_BIND_NOW 24 /* Process relocations of object */ ++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ ++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ ++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ ++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ ++#define DT_RUNPATH 29 /* Library search path */ ++#define DT_FLAGS 30 /* Flags for the object being loaded */ ++#define DT_ENCODING 32 /* Start of encoded range */ ++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ ++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ ++#define DT_NUM 34 /* Number used */ ++#define DT_LOOS 0x6000000d /* Start of OS-specific */ ++#define DT_HIOS 0x6ffff000 /* End of OS-specific */ ++#define DT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define DT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ ++ ++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the ++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's ++ approach. */ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ ++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting ++ the following DT_* entry. */ ++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ ++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ ++#define DT_VALNUM 12 ++ ++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the ++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. ++ ++ If any adjustment is made to the ELF object after it has been ++ built these entries will need to be adjusted. */ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ ++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ ++#define DT_CONFIG 0x6ffffefa /* Configuration information. */ ++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ ++#define DT_AUDIT 0x6ffffefc /* Object auditing. */ ++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ ++#define DT_MOVETAB 0x6ffffefe /* Move table. */ ++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ ++#define DT_ADDRNUM 10 ++ ++/* The versioning entry types. The next are defined as part of the ++ GNU extension. */ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++/* These were chosen by Sun. */ ++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ ++#define DT_VERDEF 0x6ffffffc /* Address of version definition ++ table */ ++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ ++#define DT_VERNEED 0x6ffffffe /* Address of table with needed ++ versions */ ++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ ++#define DT_VERSIONTAGNUM 16 ++ ++/* Sun added these machine-independent extensions in the "processor-specific" ++ range. Be compatible. */ ++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ ++#define DT_FILTER 0x7fffffff /* Shared object to get values from */ ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++/* Values of `d_un.d_val' in the DT_FLAGS entry. */ ++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ ++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ ++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ ++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ ++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ ++ ++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 ++ entry in the dynamic section. */ ++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ ++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ ++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ ++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ ++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ ++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ ++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ ++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ ++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ ++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ ++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ ++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ ++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ ++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ ++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ ++ ++/* Flags for the feature selection in DT_FEATURE_1. */ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ ++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ ++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not ++ generally available. */ ++ ++/* Version definition sections. */ ++ ++typedef struct ++{ ++ Elf32_Half vd_version; /* Version revision */ ++ Elf32_Half vd_flags; /* Version information */ ++ Elf32_Half vd_ndx; /* Version Index */ ++ Elf32_Half vd_cnt; /* Number of associated aux entries */ ++ Elf32_Word vd_hash; /* Version name hash value */ ++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf32_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf32_Verdef; ++ ++typedef struct ++{ ++ Elf64_Half vd_version; /* Version revision */ ++ Elf64_Half vd_flags; /* Version information */ ++ Elf64_Half vd_ndx; /* Version Index */ ++ Elf64_Half vd_cnt; /* Number of associated aux entries */ ++ Elf64_Word vd_hash; /* Version name hash value */ ++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf64_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf64_Verdef; ++ ++ ++/* Legal values for vd_version (version revision). */ ++#define VER_DEF_NONE 0 /* No version */ ++#define VER_DEF_CURRENT 1 /* Current version */ ++#define VER_DEF_NUM 2 /* Given version number */ ++ ++/* Legal values for vd_flags (version information flags). */ ++#define VER_FLG_BASE 0x1 /* Version definition of file itself */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++/* Versym symbol index values. */ ++#define VER_NDX_LOCAL 0 /* Symbol is local. */ ++#define VER_NDX_GLOBAL 1 /* Symbol is global. */ ++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ ++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ ++ ++/* Auxialiary version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vda_name; /* Version or dependency names */ ++ Elf32_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf32_Verdaux; ++ ++typedef struct ++{ ++ Elf64_Word vda_name; /* Version or dependency names */ ++ Elf64_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf64_Verdaux; ++ ++ ++/* Version dependency section. */ ++ ++typedef struct ++{ ++ Elf32_Half vn_version; /* Version of structure */ ++ Elf32_Half vn_cnt; /* Number of associated aux entries */ ++ Elf32_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf32_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf32_Verneed; ++ ++typedef struct ++{ ++ Elf64_Half vn_version; /* Version of structure */ ++ Elf64_Half vn_cnt; /* Number of associated aux entries */ ++ Elf64_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf64_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf64_Verneed; ++ ++ ++/* Legal values for vn_version (version revision). */ ++#define VER_NEED_NONE 0 /* No version */ ++#define VER_NEED_CURRENT 1 /* Current version */ ++#define VER_NEED_NUM 2 /* Given version number */ ++ ++/* Auxiliary needed version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vna_hash; /* Hash value of dependency name */ ++ Elf32_Half vna_flags; /* Dependency specific information */ ++ Elf32_Half vna_other; /* Unused */ ++ Elf32_Word vna_name; /* Dependency name string offset */ ++ Elf32_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf32_Vernaux; ++ ++typedef struct ++{ ++ Elf64_Word vna_hash; /* Hash value of dependency name */ ++ Elf64_Half vna_flags; /* Dependency specific information */ ++ Elf64_Half vna_other; /* Unused */ ++ Elf64_Word vna_name; /* Dependency name string offset */ ++ Elf64_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf64_Vernaux; ++ ++ ++/* Legal values for vna_flags. */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++ ++/* Auxiliary vector. */ ++ ++/* This vector is normally only used by the program interpreter. The ++ usual definition in an ABI supplement uses the name auxv_t. The ++ vector is not usually defined in a standard file, but it ++ can't hurt. We rename it to avoid conflicts. The sizes of these ++ types are an arrangement between the exec server and the program ++ interpreter, so we don't fully specify them here. */ ++ ++typedef struct ++{ ++ int a_type; /* Entry type */ ++ union ++ { ++ long int a_val; /* Integer value */ ++ void *a_ptr; /* Pointer value */ ++ void (*a_fcn) (void); /* Function pointer value */ ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct ++{ ++ long int a_type; /* Entry type */ ++ union ++ { ++ long int a_val; /* Integer value */ ++ void *a_ptr; /* Pointer value */ ++ void (*a_fcn) (void); /* Function pointer value */ ++ } a_un; ++} Elf64_auxv_t; ++ ++/* Legal values for a_type (entry type). */ ++ ++#define AT_NULL 0 /* End of vector */ ++#define AT_IGNORE 1 /* Entry should be ignored */ ++#define AT_EXECFD 2 /* File descriptor of program */ ++#define AT_PHDR 3 /* Program headers for program */ ++#define AT_PHENT 4 /* Size of program header entry */ ++#define AT_PHNUM 5 /* Number of program headers */ ++#define AT_PAGESZ 6 /* System page size */ ++#define AT_BASE 7 /* Base address of interpreter */ ++#define AT_FLAGS 8 /* Flags */ ++#define AT_ENTRY 9 /* Entry point of program */ ++#define AT_NOTELF 10 /* Program is not ELF */ ++#define AT_UID 11 /* Real uid */ ++#define AT_EUID 12 /* Effective uid */ ++#define AT_GID 13 /* Real gid */ ++#define AT_EGID 14 /* Effective gid */ ++#define AT_CLKTCK 17 /* Frequency of times() */ ++ ++/* Some more special a_type values describing the hardware. */ ++#define AT_PLATFORM 15 /* String identifying platform. */ ++#define AT_HWCAP 16 /* Machine dependent hints about ++ processor capabilities. */ ++ ++/* This entry gives some information about the FPU initialization ++ performed by the kernel. */ ++#define AT_FPUCW 18 /* Used FPU control word. */ ++ ++/* Cache block sizes. */ ++#define AT_DCACHEBSIZE 19 /* Data cache block size. */ ++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ ++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ ++ ++/* A special ignored value for PPC, used by the kernel to control the ++ interpretation of the AUXV. Must be > 16. */ ++#define AT_IGNOREPPC 22 /* Entry should be ignored. */ ++ ++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ ++ ++/* Pointer to the global system page used for system calls and other ++ nice things. */ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++ ++/* Note section contents. Each entry in the note section begins with ++ a header of a fixed form. */ ++ ++typedef struct ++{ ++ Elf32_Word n_namesz; /* Length of the note's name. */ ++ Elf32_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf32_Word n_type; /* Type of the note. */ ++} Elf32_Nhdr; ++ ++typedef struct ++{ ++ Elf64_Word n_namesz; /* Length of the note's name. */ ++ Elf64_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf64_Word n_type; /* Type of the note. */ ++} Elf64_Nhdr; ++ ++/* Known names of notes. */ ++ ++/* Solaris entries in the note section have this name. */ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++/* Note entries for GNU systems have this name. */ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++/* Defined types of notes for Solaris. */ ++ ++/* Value of descriptor (one word) is desired pagesize for the binary. */ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++/* Defined note types for GNU systems. */ ++ ++/* ABI information. The descriptor consists of words: ++ word 0: OS descriptor ++ word 1: major version of the ABI ++ word 2: minor version of the ABI ++ word 3: subminor version of the ABI ++*/ ++#define ELF_NOTE_ABI 1 ++ ++/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI ++ note section entry. */ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++ ++/* Move records. */ ++typedef struct ++{ ++ Elf32_Xword m_value; /* Symbol value. */ ++ Elf32_Word m_info; /* Size and index. */ ++ Elf32_Word m_poffset; /* Symbol offset. */ ++ Elf32_Half m_repeat; /* Repeat count. */ ++ Elf32_Half m_stride; /* Stride info. */ ++} Elf32_Move; ++ ++typedef struct ++{ ++ Elf64_Xword m_value; /* Symbol value. */ ++ Elf64_Xword m_info; /* Size and index. */ ++ Elf64_Xword m_poffset; /* Symbol offset. */ ++ Elf64_Half m_repeat; /* Repeat count. */ ++ Elf64_Half m_stride; /* Stride info. */ ++} Elf64_Move; ++ ++/* Macro to construct move records. */ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++ ++/* Motorola 68k specific definitions. */ ++ ++/* Values for Elf32_Ehdr.e_flags. */ ++#define EF_CPU32 0x00810000 ++ ++/* m68k relocs. */ ++ ++#define R_68K_NONE 0 /* No reloc */ ++#define R_68K_32 1 /* Direct 32 bit */ ++#define R_68K_16 2 /* Direct 16 bit */ ++#define R_68K_8 3 /* Direct 8 bit */ ++#define R_68K_PC32 4 /* PC relative 32 bit */ ++#define R_68K_PC16 5 /* PC relative 16 bit */ ++#define R_68K_PC8 6 /* PC relative 8 bit */ ++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ ++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ ++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ ++#define R_68K_GOT32O 10 /* 32 bit GOT offset */ ++#define R_68K_GOT16O 11 /* 16 bit GOT offset */ ++#define R_68K_GOT8O 12 /* 8 bit GOT offset */ ++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ ++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ ++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ ++#define R_68K_PLT32O 16 /* 32 bit PLT offset */ ++#define R_68K_PLT16O 17 /* 16 bit PLT offset */ ++#define R_68K_PLT8O 18 /* 8 bit PLT offset */ ++#define R_68K_COPY 19 /* Copy symbol at runtime */ ++#define R_68K_GLOB_DAT 20 /* Create GOT entry */ ++#define R_68K_JMP_SLOT 21 /* Create PLT entry */ ++#define R_68K_RELATIVE 22 /* Adjust by program base */ ++/* Keep this the last entry. */ ++#define R_68K_NUM 23 ++ ++/* Intel 80386 specific definitions. */ ++ ++/* i386 relocs. */ ++ ++#define R_386_NONE 0 /* No reloc */ ++#define R_386_32 1 /* Direct 32 bit */ ++#define R_386_PC32 2 /* PC relative 32 bit */ ++#define R_386_GOT32 3 /* 32 bit GOT entry */ ++#define R_386_PLT32 4 /* 32 bit PLT address */ ++#define R_386_COPY 5 /* Copy symbol at runtime */ ++#define R_386_GLOB_DAT 6 /* Create GOT entry */ ++#define R_386_JMP_SLOT 7 /* Create PLT entry */ ++#define R_386_RELATIVE 8 /* Adjust by program base */ ++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ ++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ ++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS ++ block offset */ ++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block ++ offset */ ++#define R_386_TLS_LE 17 /* Offset relative to static TLS ++ block */ ++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of ++ general dynamic thread local data */ ++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of ++ local dynamic thread local data ++ in LE code */ ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic ++ thread local data */ ++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ ++#define R_386_TLS_GD_CALL 26 /* Relocation for call to ++ __tls_get_addr() */ ++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ ++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic ++ thread local data in LE code */ ++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ ++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to ++ __tls_get_addr() in LDM code */ ++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ ++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ ++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS ++ block offset */ ++#define R_386_TLS_LE_32 34 /* Negated offset relative to static ++ TLS block */ ++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ ++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ ++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ ++/* Keep this the last entry. */ ++#define R_386_NUM 38 ++ ++/* SUN SPARC specific definitions. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_REGISTER 13 /* Global register reserved to app. */ ++ ++/* Values for Elf64_Ehdr.e_flags. */ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 /* little endian data */ ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ ++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ ++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ ++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ ++ ++/* SPARC relocs. */ ++ ++#define R_SPARC_NONE 0 /* No reloc */ ++#define R_SPARC_8 1 /* Direct 8 bit */ ++#define R_SPARC_16 2 /* Direct 16 bit */ ++#define R_SPARC_32 3 /* Direct 32 bit */ ++#define R_SPARC_DISP8 4 /* PC relative 8 bit */ ++#define R_SPARC_DISP16 5 /* PC relative 16 bit */ ++#define R_SPARC_DISP32 6 /* PC relative 32 bit */ ++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ ++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ ++#define R_SPARC_HI22 9 /* High 22 bit */ ++#define R_SPARC_22 10 /* Direct 22 bit */ ++#define R_SPARC_13 11 /* Direct 13 bit */ ++#define R_SPARC_LO10 12 /* Truncated 10 bit */ ++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ ++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ ++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ ++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ ++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ ++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ ++#define R_SPARC_COPY 19 /* Copy symbol at runtime */ ++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ ++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ ++#define R_SPARC_RELATIVE 22 /* Adjust by program base */ ++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ ++ ++/* Additional Sparc64 relocs. */ ++ ++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ ++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ ++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ ++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ ++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ ++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ ++#define R_SPARC_10 30 /* Direct 10 bit */ ++#define R_SPARC_11 31 /* Direct 11 bit */ ++#define R_SPARC_64 32 /* Direct 64 bit */ ++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ ++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ ++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ ++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ ++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ ++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ ++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ ++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ ++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ ++#define R_SPARC_7 43 /* Direct 7 bit */ ++#define R_SPARC_5 44 /* Direct 5 bit */ ++#define R_SPARC_6 45 /* Direct 6 bit */ ++#define R_SPARC_DISP64 46 /* PC relative 64 bit */ ++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ ++#define R_SPARC_HIX22 48 /* High 22 bit complemented */ ++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ ++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ ++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ ++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ ++#define R_SPARC_REGISTER 53 /* Global register usage */ ++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ ++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++/* Keep this the last entry. */ ++#define R_SPARC_NUM 80 ++ ++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++/* Bits present in AT_HWCAP, primarily for Sparc32. */ ++ ++#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ ++#define HWCAP_SPARC_STBAR 2 ++#define HWCAP_SPARC_SWAP 4 ++#define HWCAP_SPARC_MULDIV 8 ++#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ ++#define HWCAP_SPARC_ULTRA3 32 ++ ++/* MIPS R3000 specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ ++#define EF_MIPS_PIC 2 /* Contains PIC code */ ++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ ++ ++/* Legal values for MIPS architecture level. */ ++ ++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* The following are non-official names and should not be used. */ ++ ++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* Special section indices. */ ++ ++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ ++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ ++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ ++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ ++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ ++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ ++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ ++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ ++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++/* Symbol tables. */ ++ ++/* MIPS specific values for `st_other'. */ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++/* MIPS specific values for `st_info'. */ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++/* Entries found in sections of type SHT_MIPS_GPTAB. */ ++ ++typedef union ++{ ++ struct ++ { ++ Elf32_Word gt_current_g_value; /* -G value used for compilation */ ++ Elf32_Word gt_unused; /* Not used */ ++ } gt_header; /* First entry in section */ ++ struct ++ { ++ Elf32_Word gt_g_value; /* If this value were used for -G */ ++ Elf32_Word gt_bytes; /* This many bytes would be used */ ++ } gt_entry; /* Subsequent entries in section */ ++} Elf32_gptab; ++ ++/* Entry found in sections of type SHT_MIPS_REGINFO. */ ++ ++typedef struct ++{ ++ Elf32_Word ri_gprmask; /* General registers used */ ++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ ++ Elf32_Sword ri_gp_value; /* $gp register value */ ++} Elf32_RegInfo; ++ ++/* Entries found in sections of type SHT_MIPS_OPTIONS. */ ++ ++typedef struct ++{ ++ unsigned char kind; /* Determines interpretation of the ++ variable part of descriptor. */ ++ unsigned char size; /* Size of descriptor, including header. */ ++ Elf32_Section section; /* Section header index of section affected, ++ 0 for global options. */ ++ Elf32_Word info; /* Kind-specific information. */ ++} Elf_Options; ++ ++/* Values for `kind' field in Elf_Options. */ ++ ++#define ODK_NULL 0 /* Undefined. */ ++#define ODK_REGINFO 1 /* Register usage information. */ ++#define ODK_EXCEPTIONS 2 /* Exception processing options. */ ++#define ODK_PAD 3 /* Section padding options. */ ++#define ODK_HWPATCH 4 /* Hardware workarounds performed */ ++#define ODK_FILL 5 /* record the fill value used by the linker. */ ++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ ++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ ++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ ++ ++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ ++ ++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ ++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ ++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ ++#define OEX_SMM 0x20000 /* Force sequential memory mode? */ ++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ ++ ++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ ++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ ++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ ++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++/* Entry found in `.options' section. */ ++ ++typedef struct ++{ ++ Elf32_Word hwp_flags1; /* Extra flags. */ ++ Elf32_Word hwp_flags2; /* Extra flags. */ ++} Elf_Options_Hw; ++ ++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++/* MIPS relocs. */ ++ ++#define R_MIPS_NONE 0 /* No reloc */ ++#define R_MIPS_16 1 /* Direct 16 bit */ ++#define R_MIPS_32 2 /* Direct 32 bit */ ++#define R_MIPS_REL32 3 /* PC relative 32 bit */ ++#define R_MIPS_26 4 /* Direct 26 bit shifted */ ++#define R_MIPS_HI16 5 /* High 16 bit */ ++#define R_MIPS_LO16 6 /* Low 16 bit */ ++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ ++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ ++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ ++#define R_MIPS_PC16 10 /* PC relative 16 bit */ ++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ ++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++/* Keep this the last entry. */ ++#define R_MIPS_NUM 38 ++ ++/* Legal values for p_type field of Elf32_Phdr. */ ++ ++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ ++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++/* Special program header types. */ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++/* Legal values for d_tag field of Elf32_Dyn. */ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ ++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ ++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ ++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ ++#define DT_MIPS_FLAGS 0x70000005 /* Flags */ ++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ ++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ ++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ ++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ ++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ ++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ ++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ ++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ ++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ ++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in ++ DT_MIPS_DELTA_CLASS. */ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in ++ DT_MIPS_DELTA_INSTANCE. */ ++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in ++ DT_MIPS_DELTA_RELOC. */ ++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta ++ relocations refer to. */ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in ++ DT_MIPS_DELTA_SYM. */ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the ++ class declaration. */ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in ++ DT_MIPS_DELTA_CLASSSYM. */ ++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ ++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve ++ function stored in GOT. */ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added ++ by rld on dlopen() calls. */ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ ++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ ++#define DT_MIPS_NUM 0x32 ++ ++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ ++ ++#define RHF_NONE 0 /* No flags */ ++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ ++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++/* Entries found in sections of type SHT_MIPS_LIBLIST. */ ++ ++typedef struct ++{ ++ Elf32_Word l_name; /* Name (string table index) */ ++ Elf32_Word l_time_stamp; /* Timestamp */ ++ Elf32_Word l_checksum; /* Checksum */ ++ Elf32_Word l_version; /* Interface version */ ++ Elf32_Word l_flags; /* Flags */ ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; /* Name (string table index) */ ++ Elf64_Word l_time_stamp; /* Timestamp */ ++ Elf64_Word l_checksum; /* Checksum */ ++ Elf64_Word l_version; /* Interface version */ ++ Elf64_Word l_flags; /* Flags */ ++} Elf64_Lib; ++ ++ ++/* Legal values for l_flags. */ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ ++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++/* Entries found in sections of type SHT_MIPS_CONFLICT. */ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++/* HPPA specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ ++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ ++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ ++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ ++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch ++ prediction. */ ++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ ++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ ++ ++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ ++ ++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ ++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ ++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ ++ ++/* Additional section indeces. */ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared ++ symbols in ANSI C. */ ++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ ++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ ++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ ++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ ++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++/* HPPA relocs. */ ++ ++#define R_PARISC_NONE 0 /* No reloc. */ ++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ ++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ ++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ ++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ ++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ ++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ ++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ ++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ ++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ ++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ ++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ ++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ ++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ ++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ ++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ ++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ ++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ ++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ ++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ ++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ ++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ ++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ ++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ ++#define R_PARISC_FPTR64 64 /* 64 bits function address. */ ++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ ++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ ++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ ++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ ++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ ++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ ++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ ++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ ++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ ++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ ++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ ++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ ++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ ++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ ++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ ++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 /* Copy relocation. */ ++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ ++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ ++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ ++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ ++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ ++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ ++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ ++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ ++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_HIRESERVE 255 ++ ++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++/* Alpha specific definitions. */ ++ ++/* Legal values for e_flags field of Elf64_Ehdr. */ ++ ++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ ++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ ++ ++/* Legal values for sh_type field of Elf64_Shdr. */ ++ ++/* These two are primerily concerned with ECOFF debugging info. */ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++/* Legal values for sh_flags field of Elf64_Shdr. */ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++/* Legal values for st_other field of Elf64_Sym. */ ++#define STO_ALPHA_NOPV 0x80 /* No PV required. */ ++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ ++ ++/* Alpha relocs. */ ++ ++#define R_ALPHA_NONE 0 /* No reloc */ ++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ ++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ ++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ ++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ ++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ ++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ ++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ ++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ ++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ ++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ ++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ ++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ ++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ ++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ ++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ ++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ ++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ ++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++/* Keep this the last entry. */ ++#define R_ALPHA_NUM 46 ++ ++/* Magic values of the LITUSE relocation addend. */ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++ ++/* PowerPC specific declarations */ ++ ++/* Values for Elf32/64_Ehdr.e_flags. */ ++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ ++ ++/* Cygnus local bits below */ ++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib ++ flag */ ++ ++/* PowerPC relocations defined by the ABIs */ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 /* 32bit absolute address */ ++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ ++#define R_PPC_ADDR16 3 /* 16bit absolute address */ ++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ ++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ ++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ ++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 /* PC relative 26 bit */ ++#define R_PPC_REL14 11 /* PC relative 16 bit */ ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++/* PowerPC relocations defined for the TLS access ABI. */ ++#define R_PPC_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ ++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ ++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ ++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ ++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ ++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ ++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ ++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ ++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ ++ ++/* Keep this the last entry. */ ++#define R_PPC_NUM 95 ++ ++/* The remaining relocs are from the Embedded ELF ABI, and are not ++ in the SVR4 ELF ABI. */ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ ++ ++/* Diab tool relocations. */ ++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ ++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ ++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ ++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ ++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ ++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ ++ ++/* This is a phony reloc to handle any old fashioned TOC16 references ++ that may still be in object files. */ ++#define R_PPC_TOC16 255 ++ ++ ++/* PowerPC64 relocations defined by the ABIs */ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ ++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ ++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ ++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ ++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ ++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ ++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ ++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ ++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ ++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ ++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ ++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ ++#define R_PPC64_PLT64 45 /* doubleword64 L + A */ ++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ ++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ ++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ ++#define R_PPC64_TOC 51 /* doubleword64 .TOC */ ++#define R_PPC64_PLTGOT16 52 /* half16* M + A */ ++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ ++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ ++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ ++ ++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ ++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ ++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ ++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ ++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ ++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ ++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ ++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ ++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ ++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ ++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ ++ ++/* PowerPC64 relocations defined for the TLS access ABI. */ ++#define R_PPC64_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ ++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ ++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ ++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ ++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ ++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ ++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ ++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ ++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ ++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ ++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ ++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ ++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ ++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ ++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ ++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ ++ ++/* Keep this the last entry. */ ++#define R_PPC64_NUM 107 ++ ++/* PowerPC64 specific values for the Dyn d_tag field. */ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_NUM 1 ++ ++ ++/* ARM specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++ ++/* Other constants defined in the ARM ELF spec. version B-01. */ ++/* NB. These conflict with values defined above. */ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++ ++/* Additional symbol types for Thumb */ ++#define STT_ARM_TFUNC 0xd ++ ++/* ARM-specific values for sh_flags */ ++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ ++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined ++ in the input to a link step */ ++ ++/* ARM-specific program header flags */ ++#define PF_ARM_SB 0x10000000 /* Segment contains the location ++ addressed by the static base */ ++ ++/* ARM relocs. */ ++#define R_ARM_NONE 0 /* No reloc */ ++#define R_ARM_PC24 1 /* PC relative 26 bit branch */ ++#define R_ARM_ABS32 2 /* Direct 32 bit */ ++#define R_ARM_REL32 3 /* PC relative 32 bit */ ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 /* Direct 16 bit */ ++#define R_ARM_ABS12 6 /* Direct 12 bit */ ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 /* Direct 8 bit */ ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_SWI24 13 ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_COPY 20 /* Copy symbol at runtime */ ++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ ++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ ++#define R_ARM_RELATIVE 23 /* Adjust by program base */ ++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ ++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ ++#define R_ARM_GOT32 26 /* 32 bit GOT entry */ ++#define R_ARM_PLT32 27 /* 32 bit PLT address */ ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ ++#define R_ARM_THM_PC9 103 /* thumb conditional branch */ ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++/* Keep this the last entry. */ ++#define R_ARM_NUM 256 ++ ++/* IA-64 specific declarations. */ ++ ++/* Processor specific flags for the Ehdr e_flags field. */ ++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ ++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ ++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ ++ ++/* Processor specific flags for the Phdr p_flags field. */ ++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ ++ ++/* Processor specific flags for the Shdr sh_flags field. */ ++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ ++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Dyn d_tag field. */ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++/* IA-64 relocations. */ ++#define R_IA64_NONE 0x00 /* none */ ++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ ++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ ++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ ++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ ++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ ++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ ++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ ++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ ++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ ++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ ++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ ++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ ++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ ++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ ++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ ++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ ++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ ++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ ++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ ++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ ++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ ++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ ++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ ++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ ++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ ++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ ++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ ++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ ++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ ++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ ++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ ++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ ++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ ++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ ++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ ++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ ++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ ++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ ++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ ++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ ++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ ++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ ++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ ++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ ++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ ++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ ++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ ++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ ++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ ++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ ++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ ++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ ++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ ++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ ++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ ++#define R_IA64_COPY 0x84 /* copy relocation */ ++#define R_IA64_SUB 0x85 /* Addend and symbol difference */ ++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ ++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ ++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ ++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ ++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ ++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ ++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ ++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ ++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ ++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ ++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ ++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ ++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ ++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ ++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ ++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ ++ ++/* SH specific declarations */ ++ ++/* SH relocs. */ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++/* Keep this the last entry. */ ++#define R_SH_NUM 256 ++ ++/* Additional s390 relocs */ ++ ++#define R_390_NONE 0 /* No reloc. */ ++#define R_390_8 1 /* Direct 8 bit. */ ++#define R_390_12 2 /* Direct 12 bit. */ ++#define R_390_16 3 /* Direct 16 bit. */ ++#define R_390_32 4 /* Direct 32 bit. */ ++#define R_390_PC32 5 /* PC relative 32 bit. */ ++#define R_390_GOT12 6 /* 12 bit GOT offset. */ ++#define R_390_GOT32 7 /* 32 bit GOT offset. */ ++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ ++#define R_390_COPY 9 /* Copy symbol at runtime. */ ++#define R_390_GLOB_DAT 10 /* Create GOT entry. */ ++#define R_390_JMP_SLOT 11 /* Create PLT entry. */ ++#define R_390_RELATIVE 12 /* Adjust by program base. */ ++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ ++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ ++#define R_390_GOT16 15 /* 16 bit GOT offset. */ ++#define R_390_PC16 16 /* PC relative 16 bit. */ ++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ ++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ ++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ ++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ ++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ ++#define R_390_64 22 /* Direct 64 bit. */ ++#define R_390_PC64 23 /* PC relative 64 bit. */ ++#define R_390_GOT64 24 /* 64 bit GOT offset. */ ++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ ++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ ++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ ++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ ++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ ++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ ++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ ++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ ++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ ++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ ++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ ++#define R_390_TLS_GDCALL 38 /* Tag for function call in general ++ dynamic TLS code. */ ++#define R_390_TLS_LDCALL 39 /* Tag for function call in local ++ dynamic TLS code. */ ++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ ++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ ++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS ++ block. */ ++ ++/* Keep this the last entry. */ ++#define R_390_NUM 57 ++ ++/* CRIS relocations. */ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++/* AMD x86-64 relocations. */ ++#define R_X86_64_NONE 0 /* No reloc */ ++#define R_X86_64_64 1 /* Direct 64 bit */ ++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ ++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ ++#define R_X86_64_PLT32 4 /* 32 bit PLT address */ ++#define R_X86_64_COPY 5 /* Copy symbol at runtime */ ++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ ++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ ++#define R_X86_64_RELATIVE 8 /* Adjust by program base */ ++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative ++ offset to GOT */ ++#define R_X86_64_32 10 /* Direct 32 bit zero extended */ ++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ ++#define R_X86_64_16 12 /* Direct 16 bit zero extended */ ++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ ++#define R_X86_64_8 14 /* Direct 8 bit sign extended */ ++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ ++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ ++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ ++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ ++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset ++ to two GOT entries for GD symbol */ ++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset ++ to two GOT entries for LD symbol */ ++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ ++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset ++ to GOT entry for IE symbol */ ++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ ++ ++#define R_X86_64_NUM 24 ++ ++__END_DECLS ++ ++#endif /* elf.h */ + + #include "elfconfig.h" + +@@ -185,3 +2631,4 @@ + void fatal(const char *fmt, ...); + void warn(const char *fmt, ...); + void merror(const char *fmt, ...); ++ +diff -Nur linux-3.11.5.orig/scripts/mod/sumversion.c linux-3.11.5/scripts/mod/sumversion.c +--- linux-3.11.5.orig/scripts/mod/sumversion.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/mod/sumversion.c 2013-10-16 18:09:31.000000000 +0200 +@@ -1,4 +1,4 @@ +-#include ++/* #include */ + #ifdef __sun__ + #include + #else +diff -Nur linux-3.11.5.orig/tools/include/tools/linux_types.h linux-3.11.5/tools/include/tools/linux_types.h +--- linux-3.11.5.orig/tools/include/tools/linux_types.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.11.5/tools/include/tools/linux_types.h 2013-10-16 18:09:31.000000000 +0200 +@@ -0,0 +1,22 @@ ++#ifndef __LINUX_TYPES_H ++#define __LINUX_TYPES_H ++ ++#include ++ ++typedef uint8_t __u8; ++typedef uint8_t __be8; ++typedef uint8_t __le8; ++ ++typedef uint16_t __u16; ++typedef uint16_t __be16; ++typedef uint16_t __le16; ++ ++typedef uint32_t __u32; ++typedef uint32_t __be32; ++typedef uint32_t __le32; ++ ++typedef uint64_t __u64; ++typedef uint64_t __be64; ++typedef uint64_t __le64; ++ ++#endif diff --git a/target/linux/patches/3.18.16/cleankernel.patch b/target/linux/patches/3.18.16/cleankernel.patch new file mode 100644 index 000000000..d8c055dc3 --- /dev/null +++ b/target/linux/patches/3.18.16/cleankernel.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.5.orig/scripts/Makefile.headersinst linux-3.11.5/scripts/Makefile.headersinst +--- linux-3.11.5.orig/scripts/Makefile.headersinst 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/Makefile.headersinst 2013-10-15 16:33:10.000000000 +0200 +@@ -107,7 +107,6 @@ + + targets += $(install-file) + $(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE +- $(if $(unwanted),$(call cmd,remove),) + $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) + $(call if_changed,install) + diff --git a/target/linux/patches/3.18.16/cris-header.patch b/target/linux/patches/3.18.16/cris-header.patch new file mode 100644 index 000000000..3db07e530 --- /dev/null +++ b/target/linux/patches/3.18.16/cris-header.patch @@ -0,0 +1,50 @@ +diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild +--- linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 ++++ linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild 2014-09-26 19:24:50.000000000 +0200 +@@ -1 +1,2 @@ + # CRISv10 arch ++header-y += ptrace.h +diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild +--- linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 ++++ linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild 2014-09-26 19:24:31.000000000 +0200 +@@ -1 +1,2 @@ + # CRISv32 arch ++header-y += ptrace.h +diff -Nur linux-3.16.2.orig/arch/cris/include/asm/Kbuild linux-3.16.2/arch/cris/include/asm/Kbuild +--- linux-3.16.2.orig/arch/cris/include/asm/Kbuild 2014-09-06 01:37:11.000000000 +0200 ++++ linux-3.16.2/arch/cris/include/asm/Kbuild 2014-09-26 19:24:31.000000000 +0200 +@@ -1,8 +1,3 @@ +- +-header-y += arch-v10/ +-header-y += arch-v32/ +- +- + generic-y += barrier.h + generic-y += clkdev.h + generic-y += cputime.h +diff -Nur linux-3.16.2.orig/arch/cris/include/uapi/asm/Kbuild linux-3.16.2/arch/cris/include/uapi/asm/Kbuild +--- linux-3.16.2.orig/arch/cris/include/uapi/asm/Kbuild 2014-09-06 01:37:11.000000000 +0200 ++++ linux-3.16.2/arch/cris/include/uapi/asm/Kbuild 2014-09-26 19:24:31.000000000 +0200 +@@ -1,8 +1,8 @@ + # UAPI Header export list + include include/uapi/asm-generic/Kbuild.asm + +-header-y += arch-v10/ +-header-y += arch-v32/ ++header-y += ../arch-v10/arch/ ++header-y += ../arch-v32/arch/ + header-y += auxvec.h + header-y += bitsperlong.h + header-y += byteorder.h +diff -Nur linux-3.16.2.orig/scripts/headers.sh linux-3.16.2/scripts/headers.sh +--- linux-3.16.2.orig/scripts/headers.sh 2014-09-06 01:37:11.000000000 +0200 ++++ linux-3.16.2/scripts/headers.sh 2014-09-26 19:24:31.000000000 +0200 +@@ -19,8 +19,6 @@ + case ${arch} in + um) # no userspace export + ;; +- cris) # headers export are known broken +- ;; + *) + if [ -d ${srctree}/arch/${arch} ]; then + do_command $1 ${arch} diff --git a/target/linux/patches/3.18.16/cris-initramfs.patch b/target/linux/patches/3.18.16/cris-initramfs.patch new file mode 100644 index 000000000..b709e705e --- /dev/null +++ b/target/linux/patches/3.18.16/cris-initramfs.patch @@ -0,0 +1,22 @@ +diff -Nur linux-3.18.12.orig/arch/cris/arch-v10/mm/init.c linux-3.18.12/arch/cris/arch-v10/mm/init.c +--- linux-3.18.12.orig/arch/cris/arch-v10/mm/init.c 2015-04-20 21:48:02.000000000 +0200 ++++ linux-3.18.12/arch/cris/arch-v10/mm/init.c 2015-05-16 01:46:37.000000000 +0200 +@@ -261,3 +261,7 @@ + { + flush_etrax_cacherange(0, 8192); + } ++ ++void free_initrd_mem(unsigned long start, unsigned long end) ++{ ++} +diff -Nur linux-3.18.12.orig/arch/cris/arch-v32/mm/init.c linux-3.18.12/arch/cris/arch-v32/mm/init.c +--- linux-3.18.12.orig/arch/cris/arch-v32/mm/init.c 2015-04-20 21:48:02.000000000 +0200 ++++ linux-3.18.12/arch/cris/arch-v32/mm/init.c 2015-05-16 01:46:54.000000000 +0200 +@@ -171,3 +171,7 @@ + + mem_map = contig_page_data.node_mem_map; + } ++ ++void free_initrd_mem(unsigned long start, unsigned long end) ++{ ++} diff --git a/target/linux/patches/3.18.16/defaults.patch b/target/linux/patches/3.18.16/defaults.patch new file mode 100644 index 000000000..6cdca084e --- /dev/null +++ b/target/linux/patches/3.18.16/defaults.patch @@ -0,0 +1,46 @@ +diff -Nur linux-3.0.4.orig/fs/Kconfig linux-3.0.4/fs/Kconfig +--- linux-3.0.4.orig/fs/Kconfig 2011-08-29 22:56:30.000000000 +0200 ++++ linux-3.0.4/fs/Kconfig 2011-10-15 22:08:44.000000000 +0200 +@@ -47,7 +47,7 @@ + def_bool n + + config EXPORTFS +- tristate ++ def_bool y + + config FILE_LOCKING + bool "Enable POSIX file locking API" if EXPERT +diff -Nur linux-3.0.4.orig/fs/notify/Kconfig linux-3.0.4/fs/notify/Kconfig +--- linux-3.0.4.orig/fs/notify/Kconfig 2011-08-29 22:56:30.000000000 +0200 ++++ linux-3.0.4/fs/notify/Kconfig 2011-10-15 22:02:00.000000000 +0200 +@@ -1,5 +1,5 @@ + config FSNOTIFY +- def_bool n ++ def_bool y + + source "fs/notify/dnotify/Kconfig" + source "fs/notify/inotify/Kconfig" +diff -Nur linux-3.11.10.orig/drivers/scsi/Kconfig linux-3.11.10/drivers/scsi/Kconfig +--- linux-3.11.10.orig/drivers/scsi/Kconfig 2013-11-29 19:42:37.000000000 +0100 ++++ linux-3.11.10/drivers/scsi/Kconfig 2013-12-27 19:13:21.000000000 +0100 +@@ -2,7 +2,7 @@ + + config SCSI_MOD + tristate +- default y if SCSI=n || SCSI=y ++ default y if SCSI=y + default m if SCSI=m + + config RAID_ATTRS +diff -Nur linux-3.11.10.orig/usr/Kconfig linux-3.11.10/usr/Kconfig +--- linux-3.11.10.orig/usr/Kconfig 2013-11-29 19:42:37.000000000 +0100 ++++ linux-3.11.10/usr/Kconfig 2013-12-27 19:15:16.000000000 +0100 +@@ -47,7 +47,7 @@ + + config RD_GZIP + bool "Support initial ramdisks compressed using gzip" if EXPERT +- default y ++ default n + depends on BLK_DEV_INITRD + select DECOMPRESS_GZIP + help diff --git a/target/linux/patches/3.18.16/export-symbol-for-exmap.patch b/target/linux/patches/3.18.16/export-symbol-for-exmap.patch new file mode 100644 index 000000000..4f0fc8449 --- /dev/null +++ b/target/linux/patches/3.18.16/export-symbol-for-exmap.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.5.orig/kernel/pid.c linux-3.11.5/kernel/pid.c +--- linux-3.11.5.orig/kernel/pid.c 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/kernel/pid.c 2013-10-29 15:37:02.000000000 +0100 +@@ -450,6 +450,7 @@ + { + return find_task_by_pid_ns(vnr, task_active_pid_ns(current)); + } ++EXPORT_SYMBOL(find_task_by_vpid); + + struct pid *get_task_pid(struct task_struct *task, enum pid_type type) + { diff --git a/target/linux/patches/3.18.16/fblogo.patch b/target/linux/patches/3.18.16/fblogo.patch new file mode 100644 index 000000000..5b9070242 --- /dev/null +++ b/target/linux/patches/3.18.16/fblogo.patch @@ -0,0 +1,2057 @@ +diff -Nur linux-3.18.9.orig/Documentation/fb/00-INDEX linux-3.18.9/Documentation/fb/00-INDEX +--- linux-3.18.9.orig/Documentation/fb/00-INDEX 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/Documentation/fb/00-INDEX 2015-03-15 14:34:13.068143682 -0500 +@@ -23,6 +23,8 @@ + - info on the driver for EP93xx LCD controller. + fbcon.txt + - intro to and usage guide for the framebuffer console (fbcon). ++fbcondecor.txt ++ - info on the Framebuffer Console Decoration + framebuffer.txt + - introduction to frame buffer devices. + gxfb.txt +diff -Nur linux-3.18.9.orig/Documentation/fb/fbcondecor.txt linux-3.18.9/Documentation/fb/fbcondecor.txt +--- linux-3.18.9.orig/Documentation/fb/fbcondecor.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.9/Documentation/fb/fbcondecor.txt 2015-03-15 14:34:13.068143682 -0500 +@@ -0,0 +1,207 @@ ++What is it? ++----------- ++ ++The framebuffer decorations are a kernel feature which allows displaying a ++background picture on selected consoles. ++ ++What do I need to get it to work? ++--------------------------------- ++ ++To get fbcondecor up-and-running you will have to: ++ 1) get a copy of splashutils [1] or a similar program ++ 2) get some fbcondecor themes ++ 3) build the kernel helper program ++ 4) build your kernel with the FB_CON_DECOR option enabled. ++ ++To get fbcondecor operational right after fbcon initialization is finished, you ++will have to include a theme and the kernel helper into your initramfs image. ++Please refer to splashutils documentation for instructions on how to do that. ++ ++[1] The splashutils package can be downloaded from: ++ http://github.com/alanhaggai/fbsplash ++ ++The userspace helper ++-------------------- ++ ++The userspace fbcondecor helper (by default: /sbin/fbcondecor_helper) is called by the ++kernel whenever an important event occurs and the kernel needs some kind of ++job to be carried out. Important events include console switches and video ++mode switches (the kernel requests background images and configuration ++parameters for the current console). The fbcondecor helper must be accessible at ++all times. If it's not, fbcondecor will be switched off automatically. ++ ++It's possible to set path to the fbcondecor helper by writing it to ++/proc/sys/kernel/fbcondecor. ++ ++***************************************************************************** ++ ++The information below is mostly technical stuff. There's probably no need to ++read it unless you plan to develop a userspace helper. ++ ++The fbcondecor protocol ++----------------------- ++ ++The fbcondecor protocol defines a communication interface between the kernel and ++the userspace fbcondecor helper. ++ ++The kernel side is responsible for: ++ ++ * rendering console text, using an image as a background (instead of a ++ standard solid color fbcon uses), ++ * accepting commands from the user via ioctls on the fbcondecor device, ++ * calling the userspace helper to set things up as soon as the fb subsystem ++ is initialized. ++ ++The userspace helper is responsible for everything else, including parsing ++configuration files, decompressing the image files whenever the kernel needs ++it, and communicating with the kernel if necessary. ++ ++The fbcondecor protocol specifies how communication is done in both ways: ++kernel->userspace and userspace->helper. ++ ++Kernel -> Userspace ++------------------- ++ ++The kernel communicates with the userspace helper by calling it and specifying ++the task to be done in a series of arguments. ++ ++The arguments follow the pattern: ++ ++ ++All commands defined in fbcondecor protocol v2 have the following parameters: ++ virtual console ++ framebuffer number ++ theme ++ ++Fbcondecor protocol v1 specified an additional 'fbcondecor mode' after the ++framebuffer number. Fbcondecor protocol v1 is deprecated and should not be used. ++ ++Fbcondecor protocol v2 specifies the following commands: ++ ++getpic ++------ ++ The kernel issues this command to request image data. It's up to the ++ userspace helper to find a background image appropriate for the specified ++ theme and the current resolution. The userspace helper should respond by ++ issuing the FBIOCONDECOR_SETPIC ioctl. ++ ++init ++---- ++ The kernel issues this command after the fbcondecor device is created and ++ the fbcondecor interface is initialized. Upon receiving 'init', the userspace ++ helper should parse the kernel command line (/proc/cmdline) or otherwise ++ decide whether fbcondecor is to be activated. ++ ++ To activate fbcondecor on the first console the helper should issue the ++ FBIOCONDECOR_SETCFG, FBIOCONDECOR_SETPIC and FBIOCONDECOR_SETSTATE commands, ++ in the above-mentioned order. ++ ++ When the userspace helper is called in an early phase of the boot process ++ (right after the initialization of fbcon), no filesystems will be mounted. ++ The helper program should mount sysfs and then create the appropriate ++ framebuffer, fbcondecor and tty0 devices (if they don't already exist) to get ++ current display settings and to be able to communicate with the kernel side. ++ It should probably also mount the procfs to be able to parse the kernel ++ command line parameters. ++ ++ Note that the console sem is not held when the kernel calls fbcondecor_helper ++ with the 'init' command. The fbcondecor helper should perform all ioctls with ++ origin set to FBCON_DECOR_IO_ORIG_USER. ++ ++modechange ++---------- ++ The kernel issues this command on a mode change. The helper's response should ++ be similar to the response to the 'init' command. Note that this time the ++ console sem is held and all ioctls must be performed with origin set to ++ FBCON_DECOR_IO_ORIG_KERNEL. ++ ++ ++Userspace -> Kernel ++------------------- ++ ++Userspace programs can communicate with fbcondecor via ioctls on the ++fbcondecor device. These ioctls are to be used by both the userspace helper ++(called only by the kernel) and userspace configuration tools (run by the users). ++ ++The fbcondecor helper should set the origin field to FBCON_DECOR_IO_ORIG_KERNEL ++when doing the appropriate ioctls. All userspace configuration tools should ++use FBCON_DECOR_IO_ORIG_USER. Failure to set the appropriate value in the origin ++field when performing ioctls from the kernel helper will most likely result ++in a console deadlock. ++ ++FBCON_DECOR_IO_ORIG_KERNEL instructs fbcondecor not to try to acquire the console ++semaphore. Not surprisingly, FBCON_DECOR_IO_ORIG_USER instructs it to acquire ++the console sem. ++ ++The framebuffer console decoration provides the following ioctls (all defined in ++linux/fb.h): ++ ++FBIOCONDECOR_SETPIC ++description: loads a background picture for a virtual console ++argument: struct fbcon_decor_iowrapper*; data: struct fb_image* ++notes: ++If called for consoles other than the current foreground one, the picture data ++will be ignored. ++ ++If the current virtual console is running in a 8-bpp mode, the cmap substruct ++of fb_image has to be filled appropriately: start should be set to 16 (first ++16 colors are reserved for fbcon), len to a value <= 240 and red, green and ++blue should point to valid cmap data. The transp field is ingored. The fields ++dx, dy, bg_color, fg_color in fb_image are ignored as well. ++ ++FBIOCONDECOR_SETCFG ++description: sets the fbcondecor config for a virtual console ++argument: struct fbcon_decor_iowrapper*; data: struct vc_decor* ++notes: The structure has to be filled with valid data. ++ ++FBIOCONDECOR_GETCFG ++description: gets the fbcondecor config for a virtual console ++argument: struct fbcon_decor_iowrapper*; data: struct vc_decor* ++ ++FBIOCONDECOR_SETSTATE ++description: sets the fbcondecor state for a virtual console ++argument: struct fbcon_decor_iowrapper*; data: unsigned int* ++ values: 0 = disabled, 1 = enabled. ++ ++FBIOCONDECOR_GETSTATE ++description: gets the fbcondecor state for a virtual console ++argument: struct fbcon_decor_iowrapper*; data: unsigned int* ++ values: as in FBIOCONDECOR_SETSTATE ++ ++Info on used structures: ++ ++Definition of struct vc_decor can be found in linux/console_decor.h. It's ++heavily commented. Note that the 'theme' field should point to a string ++no longer than FBCON_DECOR_THEME_LEN. When FBIOCONDECOR_GETCFG call is ++performed, the theme field should point to a char buffer of length ++FBCON_DECOR_THEME_LEN. ++ ++Definition of struct fbcon_decor_iowrapper can be found in linux/fb.h. ++The fields in this struct have the following meaning: ++ ++vc: ++Virtual console number. ++ ++origin: ++Specifies if the ioctl is performed as a response to a kernel request. The ++fbcondecor helper should set this field to FBCON_DECOR_IO_ORIG_KERNEL, userspace ++programs should set it to FBCON_DECOR_IO_ORIG_USER. This field is necessary to ++avoid console semaphore deadlocks. ++ ++data: ++Pointer to a data structure appropriate for the performed ioctl. Type of ++the data struct is specified in the ioctls description. ++ ++***************************************************************************** ++ ++Credit ++------ ++ ++Original 'bootsplash' project & implementation by: ++ Volker Poplawski , Stefan Reinauer , ++ Steffen Winterfeldt , Michael Schroeder , ++ Ken Wimer . ++ ++Fbcondecor, fbcondecor protocol design, current implementation & docs by: ++ Michal Januszewski ++ +diff -Nur linux-3.18.9.orig/drivers/Makefile linux-3.18.9/drivers/Makefile +--- linux-3.18.9.orig/drivers/Makefile 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/drivers/Makefile 2015-03-15 14:34:13.068143682 -0500 +@@ -17,6 +17,10 @@ + obj-$(CONFIG_PCI) += pci/ + obj-$(CONFIG_PARISC) += parisc/ + obj-$(CONFIG_RAPIDIO) += rapidio/ ++# tty/ comes before char/ so that the VT console is the boot-time ++# default. ++obj-y += tty/ ++obj-y += char/ + obj-y += video/ + obj-y += idle/ + +@@ -45,11 +49,6 @@ + # reset controllers early, since gpu drivers might rely on them to initialize + obj-$(CONFIG_RESET_CONTROLLER) += reset/ + +-# tty/ comes before char/ so that the VT console is the boot-time +-# default. +-obj-y += tty/ +-obj-y += char/ +- + # gpu/ comes after char for AGP vs DRM startup + obj-y += gpu/ + +diff -Nur linux-3.18.9.orig/drivers/video/console/bitblit.c linux-3.18.9/drivers/video/console/bitblit.c +--- linux-3.18.9.orig/drivers/video/console/bitblit.c 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/drivers/video/console/bitblit.c 2015-03-15 14:34:13.068143682 -0500 +@@ -18,6 +18,7 @@ + #include + #include + #include "fbcon.h" ++#include "fbcondecor.h" + + /* + * Accelerated handlers. +@@ -55,6 +56,13 @@ + area.height = height * vc->vc_font.height; + area.width = width * vc->vc_font.width; + ++ if (fbcon_decor_active(info, vc)) { ++ area.sx += vc->vc_decor.tx; ++ area.sy += vc->vc_decor.ty; ++ area.dx += vc->vc_decor.tx; ++ area.dy += vc->vc_decor.ty; ++ } ++ + info->fbops->fb_copyarea(info, &area); + } + +@@ -379,11 +387,15 @@ + cursor.image.depth = 1; + cursor.rop = ROP_XOR; + +- if (info->fbops->fb_cursor) +- err = info->fbops->fb_cursor(info, &cursor); ++ if (fbcon_decor_active(info, vc)) { ++ fbcon_decor_cursor(info, &cursor); ++ } else { ++ if (info->fbops->fb_cursor) ++ err = info->fbops->fb_cursor(info, &cursor); + +- if (err) +- soft_cursor(info, &cursor); ++ if (err) ++ soft_cursor(info, &cursor); ++ } + + ops->cursor_reset = 0; + } +diff -Nur linux-3.18.9.orig/drivers/video/console/cfbcondecor.c linux-3.18.9/drivers/video/console/cfbcondecor.c +--- linux-3.18.9.orig/drivers/video/console/cfbcondecor.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.9/drivers/video/console/cfbcondecor.c 2015-03-15 14:34:13.072143681 -0500 +@@ -0,0 +1,471 @@ ++/* ++ * linux/drivers/video/cfbcon_decor.c -- Framebuffer decor render functions ++ * ++ * Copyright (C) 2004 Michal Januszewski ++ * ++ * Code based upon "Bootdecor" (C) 2001-2003 ++ * Volker Poplawski , ++ * Stefan Reinauer , ++ * Steffen Winterfeldt , ++ * Michael Schroeder , ++ * Ken Wimer . ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fbcon.h" ++#include "fbcondecor.h" ++ ++#define parse_pixel(shift,bpp,type) \ ++ do { \ ++ if (d & (0x80 >> (shift))) \ ++ dd2[(shift)] = fgx; \ ++ else \ ++ dd2[(shift)] = transparent ? *(type *)decor_src : bgx; \ ++ decor_src += (bpp); \ ++ } while (0) \ ++ ++extern int get_color(struct vc_data *vc, struct fb_info *info, ++ u16 c, int is_fg); ++ ++void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) ++{ ++ int i, j, k; ++ int minlen = min(min(info->var.red.length, info->var.green.length), ++ info->var.blue.length); ++ u32 col; ++ ++ for (j = i = 0; i < 16; i++) { ++ k = color_table[i]; ++ ++ col = ((vc->vc_palette[j++] >> (8-minlen)) ++ << info->var.red.offset); ++ col |= ((vc->vc_palette[j++] >> (8-minlen)) ++ << info->var.green.offset); ++ col |= ((vc->vc_palette[j++] >> (8-minlen)) ++ << info->var.blue.offset); ++ ((u32 *)info->pseudo_palette)[k] = col; ++ } ++} ++ ++void fbcon_decor_renderc(struct fb_info *info, int ypos, int xpos, int height, ++ int width, u8* src, u32 fgx, u32 bgx, u8 transparent) ++{ ++ unsigned int x, y; ++ u32 dd; ++ int bytespp = ((info->var.bits_per_pixel + 7) >> 3); ++ unsigned int d = ypos * info->fix.line_length + xpos * bytespp; ++ unsigned int ds = (ypos * info->var.xres + xpos) * bytespp; ++ u16 dd2[4]; ++ ++ u8* decor_src = (u8 *)(info->bgdecor.data + ds); ++ u8* dst = (u8 *)(info->screen_base + d); ++ ++ if ((ypos + height) > info->var.yres || (xpos + width) > info->var.xres) ++ return; ++ ++ for (y = 0; y < height; y++) { ++ switch (info->var.bits_per_pixel) { ++ ++ case 32: ++ for (x = 0; x < width; x++) { ++ ++ if ((x & 7) == 0) ++ d = *src++; ++ if (d & 0x80) ++ dd = fgx; ++ else ++ dd = transparent ? ++ *(u32 *)decor_src : bgx; ++ ++ d <<= 1; ++ decor_src += 4; ++ fb_writel(dd, dst); ++ dst += 4; ++ } ++ break; ++ case 24: ++ for (x = 0; x < width; x++) { ++ ++ if ((x & 7) == 0) ++ d = *src++; ++ if (d & 0x80) ++ dd = fgx; ++ else ++ dd = transparent ? ++ (*(u32 *)decor_src & 0xffffff) : bgx; ++ ++ d <<= 1; ++ decor_src += 3; ++#ifdef __LITTLE_ENDIAN ++ fb_writew(dd & 0xffff, dst); ++ dst += 2; ++ fb_writeb((dd >> 16), dst); ++#else ++ fb_writew(dd >> 8, dst); ++ dst += 2; ++ fb_writeb(dd & 0xff, dst); ++#endif ++ dst++; ++ } ++ break; ++ case 16: ++ for (x = 0; x < width; x += 2) { ++ if ((x & 7) == 0) ++ d = *src++; ++ ++ parse_pixel(0, 2, u16); ++ parse_pixel(1, 2, u16); ++#ifdef __LITTLE_ENDIAN ++ dd = dd2[0] | (dd2[1] << 16); ++#else ++ dd = dd2[1] | (dd2[0] << 16); ++#endif ++ d <<= 2; ++ fb_writel(dd, dst); ++ dst += 4; ++ } ++ break; ++ ++ case 8: ++ for (x = 0; x < width; x += 4) { ++ if ((x & 7) == 0) ++ d = *src++; ++ ++ parse_pixel(0, 1, u8); ++ parse_pixel(1, 1, u8); ++ parse_pixel(2, 1, u8); ++ parse_pixel(3, 1, u8); ++ ++#ifdef __LITTLE_ENDIAN ++ dd = dd2[0] | (dd2[1] << 8) | (dd2[2] << 16) | (dd2[3] << 24); ++#else ++ dd = dd2[3] | (dd2[2] << 8) | (dd2[1] << 16) | (dd2[0] << 24); ++#endif ++ d <<= 4; ++ fb_writel(dd, dst); ++ dst += 4; ++ } ++ } ++ ++ dst += info->fix.line_length - width * bytespp; ++ decor_src += (info->var.xres - width) * bytespp; ++ } ++} ++ ++#define cc2cx(a) \ ++ ((info->fix.visual == FB_VISUAL_TRUECOLOR || \ ++ info->fix.visual == FB_VISUAL_DIRECTCOLOR) ? \ ++ ((u32*)info->pseudo_palette)[a] : a) ++ ++void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, ++ const unsigned short *s, int count, int yy, int xx) ++{ ++ unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; ++ struct fbcon_ops *ops = info->fbcon_par; ++ int fg_color, bg_color, transparent; ++ u8 *src; ++ u32 bgx, fgx; ++ u16 c = scr_readw(s); ++ ++ fg_color = get_color(vc, info, c, 1); ++ bg_color = get_color(vc, info, c, 0); ++ ++ /* Don't paint the background image if console is blanked */ ++ transparent = ops->blank_state ? 0 : ++ (vc->vc_decor.bg_color == bg_color); ++ ++ xx = xx * vc->vc_font.width + vc->vc_decor.tx; ++ yy = yy * vc->vc_font.height + vc->vc_decor.ty; ++ ++ fgx = cc2cx(fg_color); ++ bgx = cc2cx(bg_color); ++ ++ while (count--) { ++ c = scr_readw(s++); ++ src = vc->vc_font.data + (c & charmask) * vc->vc_font.height * ++ ((vc->vc_font.width + 7) >> 3); ++ ++ fbcon_decor_renderc(info, yy, xx, vc->vc_font.height, ++ vc->vc_font.width, src, fgx, bgx, transparent); ++ xx += vc->vc_font.width; ++ } ++} ++ ++void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) ++{ ++ int i; ++ unsigned int dsize, s_pitch; ++ struct fbcon_ops *ops = info->fbcon_par; ++ struct vc_data* vc; ++ u8 *src; ++ ++ /* we really don't need any cursors while the console is blanked */ ++ if (info->state != FBINFO_STATE_RUNNING || ops->blank_state) ++ return; ++ ++ vc = vc_cons[ops->currcon].d; ++ ++ src = kmalloc(64 + sizeof(struct fb_image), GFP_ATOMIC); ++ if (!src) ++ return; ++ ++ s_pitch = (cursor->image.width + 7) >> 3; ++ dsize = s_pitch * cursor->image.height; ++ if (cursor->enable) { ++ switch (cursor->rop) { ++ case ROP_XOR: ++ for (i = 0; i < dsize; i++) ++ src[i] = cursor->image.data[i] ^ cursor->mask[i]; ++ break; ++ case ROP_COPY: ++ default: ++ for (i = 0; i < dsize; i++) ++ src[i] = cursor->image.data[i] & cursor->mask[i]; ++ break; ++ } ++ } else ++ memcpy(src, cursor->image.data, dsize); ++ ++ fbcon_decor_renderc(info, ++ cursor->image.dy + vc->vc_decor.ty, ++ cursor->image.dx + vc->vc_decor.tx, ++ cursor->image.height, ++ cursor->image.width, ++ (u8*)src, ++ cc2cx(cursor->image.fg_color), ++ cc2cx(cursor->image.bg_color), ++ cursor->image.bg_color == vc->vc_decor.bg_color); ++ ++ kfree(src); ++} ++ ++static void decorset(u8 *dst, int height, int width, int dstbytes, ++ u32 bgx, int bpp) ++{ ++ int i; ++ ++ if (bpp == 8) ++ bgx |= bgx << 8; ++ if (bpp == 16 || bpp == 8) ++ bgx |= bgx << 16; ++ ++ while (height-- > 0) { ++ u8 *p = dst; ++ ++ switch (bpp) { ++ ++ case 32: ++ for (i=0; i < width; i++) { ++ fb_writel(bgx, p); p += 4; ++ } ++ break; ++ case 24: ++ for (i=0; i < width; i++) { ++#ifdef __LITTLE_ENDIAN ++ fb_writew((bgx & 0xffff),(u16*)p); p += 2; ++ fb_writeb((bgx >> 16),p++); ++#else ++ fb_writew((bgx >> 8),(u16*)p); p += 2; ++ fb_writeb((bgx & 0xff),p++); ++#endif ++ } ++ case 16: ++ for (i=0; i < width/4; i++) { ++ fb_writel(bgx,p); p += 4; ++ fb_writel(bgx,p); p += 4; ++ } ++ if (width & 2) { ++ fb_writel(bgx,p); p += 4; ++ } ++ if (width & 1) ++ fb_writew(bgx,(u16*)p); ++ break; ++ case 8: ++ for (i=0; i < width/4; i++) { ++ fb_writel(bgx,p); p += 4; ++ } ++ ++ if (width & 2) { ++ fb_writew(bgx,p); p += 2; ++ } ++ if (width & 1) ++ fb_writeb(bgx,(u8*)p); ++ break; ++ ++ } ++ dst += dstbytes; ++ } ++} ++ ++void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes, ++ int srclinebytes, int bpp) ++{ ++ int i; ++ ++ while (height-- > 0) { ++ u32 *p = (u32 *)dst; ++ u32 *q = (u32 *)src; ++ ++ switch (bpp) { ++ ++ case 32: ++ for (i=0; i < width; i++) ++ fb_writel(*q++, p++); ++ break; ++ case 24: ++ for (i=0; i < (width*3/4); i++) ++ fb_writel(*q++, p++); ++ if ((width*3) % 4) { ++ if (width & 2) { ++ fb_writeb(*(u8*)q, (u8*)p); ++ } else if (width & 1) { ++ fb_writew(*(u16*)q, (u16*)p); ++ fb_writeb(*(u8*)((u16*)q+1),(u8*)((u16*)p+2)); ++ } ++ } ++ break; ++ case 16: ++ for (i=0; i < width/4; i++) { ++ fb_writel(*q++, p++); ++ fb_writel(*q++, p++); ++ } ++ if (width & 2) ++ fb_writel(*q++, p++); ++ if (width & 1) ++ fb_writew(*(u16*)q, (u16*)p); ++ break; ++ case 8: ++ for (i=0; i < width/4; i++) ++ fb_writel(*q++, p++); ++ ++ if (width & 2) { ++ fb_writew(*(u16*)q, (u16*)p); ++ q = (u32*) ((u16*)q + 1); ++ p = (u32*) ((u16*)p + 1); ++ } ++ if (width & 1) ++ fb_writeb(*(u8*)q, (u8*)p); ++ break; ++ } ++ ++ dst += linebytes; ++ src += srclinebytes; ++ } ++} ++ ++static void decorfill(struct fb_info *info, int sy, int sx, int height, ++ int width) ++{ ++ int bytespp = ((info->var.bits_per_pixel + 7) >> 3); ++ int d = sy * info->fix.line_length + sx * bytespp; ++ int ds = (sy * info->var.xres + sx) * bytespp; ++ ++ fbcon_decor_copy((u8 *)(info->screen_base + d), (u8 *)(info->bgdecor.data + ds), ++ height, width, info->fix.line_length, info->var.xres * bytespp, ++ info->var.bits_per_pixel); ++} ++ ++void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, ++ int height, int width) ++{ ++ int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; ++ struct fbcon_ops *ops = info->fbcon_par; ++ u8 *dst; ++ int transparent, bg_color = attr_bgcol_ec(bgshift, vc, info); ++ ++ transparent = (vc->vc_decor.bg_color == bg_color); ++ sy = sy * vc->vc_font.height + vc->vc_decor.ty; ++ sx = sx * vc->vc_font.width + vc->vc_decor.tx; ++ height *= vc->vc_font.height; ++ width *= vc->vc_font.width; ++ ++ /* Don't paint the background image if console is blanked */ ++ if (transparent && !ops->blank_state) { ++ decorfill(info, sy, sx, height, width); ++ } else { ++ dst = (u8 *)(info->screen_base + sy * info->fix.line_length + ++ sx * ((info->var.bits_per_pixel + 7) >> 3)); ++ decorset(dst, height, width, info->fix.line_length, cc2cx(bg_color), ++ info->var.bits_per_pixel); ++ } ++} ++ ++void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, ++ int bottom_only) ++{ ++ unsigned int tw = vc->vc_cols*vc->vc_font.width; ++ unsigned int th = vc->vc_rows*vc->vc_font.height; ++ ++ if (!bottom_only) { ++ /* top margin */ ++ decorfill(info, 0, 0, vc->vc_decor.ty, info->var.xres); ++ /* left margin */ ++ decorfill(info, vc->vc_decor.ty, 0, th, vc->vc_decor.tx); ++ /* right margin */ ++ decorfill(info, vc->vc_decor.ty, vc->vc_decor.tx + tw, th, ++ info->var.xres - vc->vc_decor.tx - tw); ++ } ++ decorfill(info, vc->vc_decor.ty + th, 0, ++ info->var.yres - vc->vc_decor.ty - th, info->var.xres); ++} ++ ++void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, ++ int sx, int dx, int width) ++{ ++ u16 *d = (u16 *) (vc->vc_origin + vc->vc_size_row * y + dx * 2); ++ u16 *s = d + (dx - sx); ++ u16 *start = d; ++ u16 *ls = d; ++ u16 *le = d + width; ++ u16 c; ++ int x = dx; ++ u16 attr = 1; ++ ++ do { ++ c = scr_readw(d); ++ if (attr != (c & 0xff00)) { ++ attr = c & 0xff00; ++ if (d > start) { ++ fbcon_decor_putcs(vc, info, start, d - start, y, x); ++ x += d - start; ++ start = d; ++ } ++ } ++ if (s >= ls && s < le && c == scr_readw(s)) { ++ if (d > start) { ++ fbcon_decor_putcs(vc, info, start, d - start, y, x); ++ x += d - start + 1; ++ start = d + 1; ++ } else { ++ x++; ++ start++; ++ } ++ } ++ s++; ++ d++; ++ } while (d < le); ++ if (d > start) ++ fbcon_decor_putcs(vc, info, start, d - start, y, x); ++} ++ ++void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) ++{ ++ if (blank) { ++ decorset((u8 *)info->screen_base, info->var.yres, info->var.xres, ++ info->fix.line_length, 0, info->var.bits_per_pixel); ++ } else { ++ update_screen(vc); ++ fbcon_decor_clear_margins(vc, info, 0); ++ } ++} ++ +diff -Nur linux-3.18.9.orig/drivers/video/console/fbcon.c linux-3.18.9/drivers/video/console/fbcon.c +--- linux-3.18.9.orig/drivers/video/console/fbcon.c 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/drivers/video/console/fbcon.c 2015-03-15 14:34:13.072143681 -0500 +@@ -79,6 +79,7 @@ + #include + + #include "fbcon.h" ++#include "fbcondecor.h" + + #ifdef FBCONDEBUG + # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) +@@ -94,7 +95,7 @@ + + static struct display fb_display[MAX_NR_CONSOLES]; + +-static signed char con2fb_map[MAX_NR_CONSOLES]; ++signed char con2fb_map[MAX_NR_CONSOLES]; + static signed char con2fb_map_boot[MAX_NR_CONSOLES]; + + static int logo_lines; +@@ -286,7 +287,7 @@ + !vt_force_oops_output(vc); + } + +-static int get_color(struct vc_data *vc, struct fb_info *info, ++int get_color(struct vc_data *vc, struct fb_info *info, + u16 c, int is_fg) + { + int depth = fb_get_color_depth(&info->var, &info->fix); +@@ -550,6 +551,9 @@ + info_idx = -1; + } else { + fbcon_has_console_bind = 1; ++#ifdef CONFIG_FB_CON_DECOR ++ fbcon_decor_init(); ++#endif + } + + return err; +@@ -1007,6 +1011,12 @@ + rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols /= vc->vc_font.width; + rows /= vc->vc_font.height; ++ ++ if (fbcon_decor_active(info, vc)) { ++ cols = vc->vc_decor.twidth / vc->vc_font.width; ++ rows = vc->vc_decor.theight / vc->vc_font.height; ++ } ++ + vc_resize(vc, cols, rows); + + DPRINTK("mode: %s\n", info->fix.id); +@@ -1036,7 +1046,7 @@ + cap = info->flags; + + if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW || +- (info->fix.type == FB_TYPE_TEXT)) ++ (info->fix.type == FB_TYPE_TEXT) || fbcon_decor_active(info, vc)) + logo = 0; + + if (var_to_display(p, &info->var, info)) +@@ -1260,6 +1270,11 @@ + fbcon_clear_margins(vc, 0); + } + ++ if (fbcon_decor_active(info, vc)) { ++ fbcon_decor_clear(vc, info, sy, sx, height, width); ++ return; ++ } ++ + /* Split blits that cross physical y_wrap boundary */ + + y_break = p->vrows - p->yscroll; +@@ -1279,10 +1294,15 @@ + struct display *p = &fb_display[vc->vc_num]; + struct fbcon_ops *ops = info->fbcon_par; + +- if (!fbcon_is_inactive(vc, info)) +- ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, +- get_color(vc, info, scr_readw(s), 1), +- get_color(vc, info, scr_readw(s), 0)); ++ if (!fbcon_is_inactive(vc, info)) { ++ ++ if (fbcon_decor_active(info, vc)) ++ fbcon_decor_putcs(vc, info, s, count, ypos, xpos); ++ else ++ ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, ++ get_color(vc, info, scr_readw(s), 1), ++ get_color(vc, info, scr_readw(s), 0)); ++ } + } + + static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) +@@ -1298,8 +1318,13 @@ + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; + +- if (!fbcon_is_inactive(vc, info)) +- ops->clear_margins(vc, info, bottom_only); ++ if (!fbcon_is_inactive(vc, info)) { ++ if (fbcon_decor_active(info, vc)) { ++ fbcon_decor_clear_margins(vc, info, bottom_only); ++ } else { ++ ops->clear_margins(vc, info, bottom_only); ++ } ++ } + } + + static void fbcon_cursor(struct vc_data *vc, int mode) +@@ -1819,7 +1844,7 @@ + count = vc->vc_rows; + if (softback_top) + fbcon_softback_note(vc, t, count); +- if (logo_shown >= 0) ++ if (logo_shown >= 0 || fbcon_decor_active(info, vc)) + goto redraw_up; + switch (p->scrollmode) { + case SCROLL_MOVE: +@@ -1912,6 +1937,8 @@ + count = vc->vc_rows; + if (logo_shown >= 0) + goto redraw_down; ++ if (fbcon_decor_active(info, vc)) ++ goto redraw_down; + switch (p->scrollmode) { + case SCROLL_MOVE: + fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, +@@ -2060,6 +2087,13 @@ + } + return; + } ++ ++ if (fbcon_decor_active(info, vc) && sy == dy && height == 1) { ++ /* must use slower redraw bmove to keep background pic intact */ ++ fbcon_decor_bmove_redraw(vc, info, sy, sx, dx, width); ++ return; ++ } ++ + ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, + height, width); + } +@@ -2130,8 +2164,8 @@ + var.yres = virt_h * virt_fh; + x_diff = info->var.xres - var.xres; + y_diff = info->var.yres - var.yres; +- if (x_diff < 0 || x_diff > virt_fw || +- y_diff < 0 || y_diff > virt_fh) { ++ if ((x_diff < 0 || x_diff > virt_fw || ++ y_diff < 0 || y_diff > virt_fh) && !vc->vc_decor.state) { + const struct fb_videomode *mode; + + DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); +@@ -2167,6 +2201,21 @@ + + info = registered_fb[con2fb_map[vc->vc_num]]; + ops = info->fbcon_par; ++ prev_console = ops->currcon; ++ if (prev_console != -1) ++ old_info = registered_fb[con2fb_map[prev_console]]; ++ ++#ifdef CONFIG_FB_CON_DECOR ++ if (!fbcon_decor_active_vc(vc) && info->fix.visual == FB_VISUAL_DIRECTCOLOR) { ++ struct vc_data *vc_curr = vc_cons[prev_console].d; ++ if (vc_curr && fbcon_decor_active_vc(vc_curr)) { ++ /* Clear the screen to avoid displaying funky colors during ++ * palette updates. */ ++ memset((u8*)info->screen_base + info->fix.line_length * info->var.yoffset, ++ 0, info->var.yres * info->fix.line_length); ++ } ++ } ++#endif + + if (softback_top) { + if (softback_lines) +@@ -2185,9 +2234,6 @@ + logo_shown = FBCON_LOGO_CANSHOW; + } + +- prev_console = ops->currcon; +- if (prev_console != -1) +- old_info = registered_fb[con2fb_map[prev_console]]; + /* + * FIXME: If we have multiple fbdev's loaded, we need to + * update all info->currcon. Perhaps, we can place this +@@ -2231,6 +2277,18 @@ + fbcon_del_cursor_timer(old_info); + } + ++ if (fbcon_decor_active_vc(vc)) { ++ struct vc_data *vc_curr = vc_cons[prev_console].d; ++ ++ if (!vc_curr->vc_decor.theme || ++ strcmp(vc->vc_decor.theme, vc_curr->vc_decor.theme) || ++ (fbcon_decor_active_nores(info, vc_curr) && ++ !fbcon_decor_active(info, vc_curr))) { ++ fbcon_decor_disable(vc, 0); ++ fbcon_decor_call_helper("modechange", vc->vc_num); ++ } ++ } ++ + if (fbcon_is_inactive(vc, info) || + ops->blank_state != FB_BLANK_UNBLANK) + fbcon_del_cursor_timer(info); +@@ -2339,15 +2397,20 @@ + } + } + +- if (!fbcon_is_inactive(vc, info)) { ++ if (!fbcon_is_inactive(vc, info)) { + if (ops->blank_state != blank) { + ops->blank_state = blank; + fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); + ops->cursor_flash = (!blank); + +- if (!(info->flags & FBINFO_MISC_USEREVENT)) +- if (fb_blank(info, blank)) +- fbcon_generic_blank(vc, info, blank); ++ if (!(info->flags & FBINFO_MISC_USEREVENT)) { ++ if (fb_blank(info, blank)) { ++ if (fbcon_decor_active(info, vc)) ++ fbcon_decor_blank(vc, info, blank); ++ else ++ fbcon_generic_blank(vc, info, blank); ++ } ++ } + } + + if (!blank) +@@ -2522,13 +2585,22 @@ + } + + if (resize) { ++ /* reset wrap/pan */ + int cols, rows; + + cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); + rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); ++ ++ if (fbcon_decor_active(info, vc)) { ++ info->var.xoffset = info->var.yoffset = p->yscroll = 0; ++ cols = vc->vc_decor.twidth; ++ rows = vc->vc_decor.theight; ++ } + cols /= w; + rows /= h; ++ + vc_resize(vc, cols, rows); ++ + if (CON_IS_VISIBLE(vc) && softback_buf) + fbcon_update_softback(vc); + } else if (CON_IS_VISIBLE(vc) +@@ -2657,7 +2729,11 @@ + int i, j, k, depth; + u8 val; + +- if (fbcon_is_inactive(vc, info)) ++ if (fbcon_is_inactive(vc, info) ++#ifdef CONFIG_FB_CON_DECOR ++ || vc->vc_num != fg_console ++#endif ++ ) + return -EINVAL; + + if (!CON_IS_VISIBLE(vc)) +@@ -2683,14 +2759,56 @@ + } else + fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap); + +- return fb_set_cmap(&palette_cmap, info); ++ if (fbcon_decor_active(info, vc_cons[fg_console].d) && ++ info->fix.visual == FB_VISUAL_DIRECTCOLOR) { ++ ++ u16 *red, *green, *blue; ++ int minlen = min(min(info->var.red.length, info->var.green.length), ++ info->var.blue.length); ++ int h; ++ ++ struct fb_cmap cmap = { ++ .start = 0, ++ .len = (1 << minlen), ++ .red = NULL, ++ .green = NULL, ++ .blue = NULL, ++ .transp = NULL ++ }; ++ ++ red = kmalloc(256 * sizeof(u16) * 3, GFP_KERNEL); ++ ++ if (!red) ++ goto out; ++ ++ green = red + 256; ++ blue = green + 256; ++ cmap.red = red; ++ cmap.green = green; ++ cmap.blue = blue; ++ ++ for (i = 0; i < cmap.len; i++) { ++ red[i] = green[i] = blue[i] = (0xffff * i)/(cmap.len-1); ++ } ++ ++ h = fb_set_cmap(&cmap, info); ++ fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d); ++ kfree(red); ++ ++ return h; ++ ++ } else if (fbcon_decor_active(info, vc_cons[fg_console].d) && ++ info->var.bits_per_pixel == 8 && info->bgdecor.cmap.red != NULL) ++ fb_set_cmap(&info->bgdecor.cmap, info); ++ ++out: return fb_set_cmap(&palette_cmap, info); + } + + static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) + { + unsigned long p; + int line; +- ++ + if (vc->vc_num != fg_console || !softback_lines) + return (u16 *) (vc->vc_origin + offset); + line = offset / vc->vc_size_row; +@@ -2909,7 +3027,14 @@ + rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols /= vc->vc_font.width; + rows /= vc->vc_font.height; +- vc_resize(vc, cols, rows); ++ ++ if (!fbcon_decor_active_nores(info, vc)) { ++ vc_resize(vc, cols, rows); ++ } else { ++ fbcon_decor_disable(vc, 0); ++ fbcon_decor_call_helper("modechange", vc->vc_num); ++ } ++ + updatescrollmode(p, info, vc); + scrollback_max = 0; + scrollback_current = 0; +@@ -2954,7 +3079,9 @@ + rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); + cols /= vc->vc_font.width; + rows /= vc->vc_font.height; +- vc_resize(vc, cols, rows); ++ if (!fbcon_decor_active_nores(info, vc)) { ++ vc_resize(vc, cols, rows); ++ } + } + + if (fg != -1) +@@ -3596,6 +3723,7 @@ + } + } + ++ fbcon_decor_exit(); + fbcon_has_exited = 1; + } + +diff -Nur linux-3.18.9.orig/drivers/video/console/fbcondecor.c linux-3.18.9/drivers/video/console/fbcondecor.c +--- linux-3.18.9.orig/drivers/video/console/fbcondecor.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.9/drivers/video/console/fbcondecor.c 2015-03-15 14:34:13.076143680 -0500 +@@ -0,0 +1,555 @@ ++/* ++ * linux/drivers/video/console/fbcondecor.c -- Framebuffer console decorations ++ * ++ * Copyright (C) 2004-2009 Michal Januszewski ++ * ++ * Code based upon "Bootsplash" (C) 2001-2003 ++ * Volker Poplawski , ++ * Stefan Reinauer , ++ * Steffen Winterfeldt , ++ * Michael Schroeder , ++ * Ken Wimer . ++ * ++ * Compat ioctl support by Thorsten Klein . ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "fbcon.h" ++#include "fbcondecor.h" ++ ++extern signed char con2fb_map[]; ++static int fbcon_decor_enable(struct vc_data *vc); ++char fbcon_decor_path[KMOD_PATH_LEN] = "/sbin/fbcondecor_helper"; ++static int initialized = 0; ++ ++int fbcon_decor_call_helper(char* cmd, unsigned short vc) ++{ ++ char *envp[] = { ++ "HOME=/", ++ "PATH=/sbin:/bin", ++ NULL ++ }; ++ ++ char tfb[5]; ++ char tcons[5]; ++ unsigned char fb = (int) con2fb_map[vc]; ++ ++ char *argv[] = { ++ fbcon_decor_path, ++ "2", ++ cmd, ++ tcons, ++ tfb, ++ vc_cons[vc].d->vc_decor.theme, ++ NULL ++ }; ++ ++ snprintf(tfb,5,"%d",fb); ++ snprintf(tcons,5,"%d",vc); ++ ++ return call_usermodehelper(fbcon_decor_path, argv, envp, UMH_WAIT_EXEC); ++} ++ ++/* Disables fbcondecor on a virtual console; called with console sem held. */ ++int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) ++{ ++ struct fb_info* info; ++ ++ if (!vc->vc_decor.state) ++ return -EINVAL; ++ ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ ++ if (info == NULL) ++ return -EINVAL; ++ ++ vc->vc_decor.state = 0; ++ vc_resize(vc, info->var.xres / vc->vc_font.width, ++ info->var.yres / vc->vc_font.height); ++ ++ if (fg_console == vc->vc_num && redraw) { ++ redraw_screen(vc, 0); ++ update_region(vc, vc->vc_origin + ++ vc->vc_size_row * vc->vc_top, ++ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); ++ } ++ ++ printk(KERN_INFO "fbcondecor: switched decor state to 'off' on console %d\n", ++ vc->vc_num); ++ ++ return 0; ++} ++ ++/* Enables fbcondecor on a virtual console; called with console sem held. */ ++static int fbcon_decor_enable(struct vc_data *vc) ++{ ++ struct fb_info* info; ++ ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ ++ if (vc->vc_decor.twidth == 0 || vc->vc_decor.theight == 0 || ++ info == NULL || vc->vc_decor.state || (!info->bgdecor.data && ++ vc->vc_num == fg_console)) ++ return -EINVAL; ++ ++ vc->vc_decor.state = 1; ++ vc_resize(vc, vc->vc_decor.twidth / vc->vc_font.width, ++ vc->vc_decor.theight / vc->vc_font.height); ++ ++ if (fg_console == vc->vc_num) { ++ redraw_screen(vc, 0); ++ update_region(vc, vc->vc_origin + ++ vc->vc_size_row * vc->vc_top, ++ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); ++ fbcon_decor_clear_margins(vc, info, 0); ++ } ++ ++ printk(KERN_INFO "fbcondecor: switched decor state to 'on' on console %d\n", ++ vc->vc_num); ++ ++ return 0; ++} ++ ++static inline int fbcon_decor_ioctl_dosetstate(struct vc_data *vc, unsigned int state, unsigned char origin) ++{ ++ int ret; ++ ++// if (origin == FBCON_DECOR_IO_ORIG_USER) ++ console_lock(); ++ if (!state) ++ ret = fbcon_decor_disable(vc, 1); ++ else ++ ret = fbcon_decor_enable(vc); ++// if (origin == FBCON_DECOR_IO_ORIG_USER) ++ console_unlock(); ++ ++ return ret; ++} ++ ++static inline void fbcon_decor_ioctl_dogetstate(struct vc_data *vc, unsigned int *state) ++{ ++ *state = vc->vc_decor.state; ++} ++ ++static int fbcon_decor_ioctl_dosetcfg(struct vc_data *vc, struct vc_decor *cfg, unsigned char origin) ++{ ++ struct fb_info *info; ++ int len; ++ char *tmp; ++ ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ ++ if (info == NULL || !cfg->twidth || !cfg->theight || ++ cfg->tx + cfg->twidth > info->var.xres || ++ cfg->ty + cfg->theight > info->var.yres) ++ return -EINVAL; ++ ++ len = strlen_user(cfg->theme); ++ if (!len || len > FBCON_DECOR_THEME_LEN) ++ return -EINVAL; ++ tmp = kmalloc(len, GFP_KERNEL); ++ if (!tmp) ++ return -ENOMEM; ++ if (copy_from_user(tmp, (void __user *)cfg->theme, len)) ++ return -EFAULT; ++ cfg->theme = tmp; ++ cfg->state = 0; ++ ++ /* If this ioctl is a response to a request from kernel, the console sem ++ * is already held; we also don't need to disable decor because either the ++ * new config and background picture will be successfully loaded, and the ++ * decor will stay on, or in case of a failure it'll be turned off in fbcon. */ ++// if (origin == FBCON_DECOR_IO_ORIG_USER) { ++ console_lock(); ++ if (vc->vc_decor.state) ++ fbcon_decor_disable(vc, 1); ++// } ++ ++ if (vc->vc_decor.theme) ++ kfree(vc->vc_decor.theme); ++ ++ vc->vc_decor = *cfg; ++ ++// if (origin == FBCON_DECOR_IO_ORIG_USER) ++ console_unlock(); ++ ++ printk(KERN_INFO "fbcondecor: console %d using theme '%s'\n", ++ vc->vc_num, vc->vc_decor.theme); ++ return 0; ++} ++ ++static int fbcon_decor_ioctl_dogetcfg(struct vc_data *vc, struct vc_decor *decor) ++{ ++ char __user *tmp; ++ ++ tmp = decor->theme; ++ *decor = vc->vc_decor; ++ decor->theme = tmp; ++ ++ if (vc->vc_decor.theme) { ++ if (copy_to_user(tmp, vc->vc_decor.theme, strlen(vc->vc_decor.theme) + 1)) ++ return -EFAULT; ++ } else ++ if (put_user(0, tmp)) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static int fbcon_decor_ioctl_dosetpic(struct vc_data *vc, struct fb_image *img, unsigned char origin) ++{ ++ struct fb_info *info; ++ int len; ++ u8 *tmp; ++ ++ if (vc->vc_num != fg_console) ++ return -EINVAL; ++ ++ info = registered_fb[(int) con2fb_map[vc->vc_num]]; ++ ++ if (info == NULL) ++ return -EINVAL; ++ ++ if (img->width != info->var.xres || img->height != info->var.yres) { ++ printk(KERN_ERR "fbcondecor: picture dimensions mismatch\n"); ++ printk(KERN_ERR "%dx%d vs %dx%d\n", img->width, img->height, info->var.xres, info->var.yres); ++ return -EINVAL; ++ } ++ ++ if (img->depth != info->var.bits_per_pixel) { ++ printk(KERN_ERR "fbcondecor: picture depth mismatch\n"); ++ return -EINVAL; ++ } ++ ++ if (img->depth == 8) { ++ if (!img->cmap.len || !img->cmap.red || !img->cmap.green || ++ !img->cmap.blue) ++ return -EINVAL; ++ ++ tmp = vmalloc(img->cmap.len * 3 * 2); ++ if (!tmp) ++ return -ENOMEM; ++ ++ if (copy_from_user(tmp, ++ (void __user*)img->cmap.red, (img->cmap.len << 1)) || ++ copy_from_user(tmp + (img->cmap.len << 1), ++ (void __user*)img->cmap.green, (img->cmap.len << 1)) || ++ copy_from_user(tmp + (img->cmap.len << 2), ++ (void __user*)img->cmap.blue, (img->cmap.len << 1))) { ++ vfree(tmp); ++ return -EFAULT; ++ } ++ ++ img->cmap.transp = NULL; ++ img->cmap.red = (u16*)tmp; ++ img->cmap.green = img->cmap.red + img->cmap.len; ++ img->cmap.blue = img->cmap.green + img->cmap.len; ++ } else { ++ img->cmap.red = NULL; ++ } ++ ++ len = ((img->depth + 7) >> 3) * img->width * img->height; ++ ++ /* ++ * Allocate an additional byte so that we never go outside of the ++ * buffer boundaries in the rendering functions in a 24 bpp mode. ++ */ ++ tmp = vmalloc(len + 1); ++ ++ if (!tmp) ++ goto out; ++ ++ if (copy_from_user(tmp, (void __user*)img->data, len)) ++ goto out; ++ ++ img->data = tmp; ++ ++ /* If this ioctl is a response to a request from kernel, the console sem ++ * is already held. */ ++// if (origin == FBCON_DECOR_IO_ORIG_USER) ++ console_lock(); ++ ++ if (info->bgdecor.data) ++ vfree((u8*)info->bgdecor.data); ++ if (info->bgdecor.cmap.red) ++ vfree(info->bgdecor.cmap.red); ++ ++ info->bgdecor = *img; ++ ++ if (fbcon_decor_active_vc(vc) && fg_console == vc->vc_num) { ++ redraw_screen(vc, 0); ++ update_region(vc, vc->vc_origin + ++ vc->vc_size_row * vc->vc_top, ++ vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); ++ fbcon_decor_clear_margins(vc, info, 0); ++ } ++ ++// if (origin == FBCON_DECOR_IO_ORIG_USER) ++ console_unlock(); ++ ++ return 0; ++ ++out: if (img->cmap.red) ++ vfree(img->cmap.red); ++ ++ if (tmp) ++ vfree(tmp); ++ return -ENOMEM; ++} ++ ++static long fbcon_decor_ioctl(struct file *filp, u_int cmd, u_long arg) ++{ ++ struct fbcon_decor_iowrapper __user *wrapper = (void __user*) arg; ++ struct vc_data *vc = NULL; ++ unsigned short vc_num = 0; ++ unsigned char origin = 0; ++ void __user *data = NULL; ++ ++ if (!access_ok(VERIFY_READ, wrapper, ++ sizeof(struct fbcon_decor_iowrapper))) ++ return -EFAULT; ++ ++ __get_user(vc_num, &wrapper->vc); ++ __get_user(origin, &wrapper->origin); ++ __get_user(data, &wrapper->data); ++ ++ if (!vc_cons_allocated(vc_num)) ++ return -EINVAL; ++ ++ vc = vc_cons[vc_num].d; ++ ++ switch (cmd) { ++ case FBIOCONDECOR_SETPIC: ++ { ++ struct fb_image img; ++ if (copy_from_user(&img, (struct fb_image __user *)data, sizeof(struct fb_image))) ++ return -EFAULT; ++ ++ return fbcon_decor_ioctl_dosetpic(vc, &img, origin); ++ } ++ case FBIOCONDECOR_SETCFG: ++ { ++ struct vc_decor cfg; ++ if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor))) ++ return -EFAULT; ++ ++ return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin); ++ } ++ case FBIOCONDECOR_GETCFG: ++ { ++ int rval; ++ struct vc_decor cfg; ++ ++ if (copy_from_user(&cfg, (struct vc_decor __user *)data, sizeof(struct vc_decor))) ++ return -EFAULT; ++ ++ rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg); ++ ++ if (copy_to_user(data, &cfg, sizeof(struct vc_decor))) ++ return -EFAULT; ++ return rval; ++ } ++ case FBIOCONDECOR_SETSTATE: ++ { ++ unsigned int state = 0; ++ if (get_user(state, (unsigned int __user *)data)) ++ return -EFAULT; ++ return fbcon_decor_ioctl_dosetstate(vc, state, origin); ++ } ++ case FBIOCONDECOR_GETSTATE: ++ { ++ unsigned int state = 0; ++ fbcon_decor_ioctl_dogetstate(vc, &state); ++ return put_user(state, (unsigned int __user *)data); ++ } ++ ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++ ++#ifdef CONFIG_COMPAT ++ ++static long fbcon_decor_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { ++ ++ struct fbcon_decor_iowrapper32 __user *wrapper = (void __user *)arg; ++ struct vc_data *vc = NULL; ++ unsigned short vc_num = 0; ++ unsigned char origin = 0; ++ compat_uptr_t data_compat = 0; ++ void __user *data = NULL; ++ ++ if (!access_ok(VERIFY_READ, wrapper, ++ sizeof(struct fbcon_decor_iowrapper32))) ++ return -EFAULT; ++ ++ __get_user(vc_num, &wrapper->vc); ++ __get_user(origin, &wrapper->origin); ++ __get_user(data_compat, &wrapper->data); ++ data = compat_ptr(data_compat); ++ ++ if (!vc_cons_allocated(vc_num)) ++ return -EINVAL; ++ ++ vc = vc_cons[vc_num].d; ++ ++ switch (cmd) { ++ case FBIOCONDECOR_SETPIC32: ++ { ++ struct fb_image32 img_compat; ++ struct fb_image img; ++ ++ if (copy_from_user(&img_compat, (struct fb_image32 __user *)data, sizeof(struct fb_image32))) ++ return -EFAULT; ++ ++ fb_image_from_compat(img, img_compat); ++ ++ return fbcon_decor_ioctl_dosetpic(vc, &img, origin); ++ } ++ ++ case FBIOCONDECOR_SETCFG32: ++ { ++ struct vc_decor32 cfg_compat; ++ struct vc_decor cfg; ++ ++ if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32))) ++ return -EFAULT; ++ ++ vc_decor_from_compat(cfg, cfg_compat); ++ ++ return fbcon_decor_ioctl_dosetcfg(vc, &cfg, origin); ++ } ++ ++ case FBIOCONDECOR_GETCFG32: ++ { ++ int rval; ++ struct vc_decor32 cfg_compat; ++ struct vc_decor cfg; ++ ++ if (copy_from_user(&cfg_compat, (struct vc_decor32 __user *)data, sizeof(struct vc_decor32))) ++ return -EFAULT; ++ cfg.theme = compat_ptr(cfg_compat.theme); ++ ++ rval = fbcon_decor_ioctl_dogetcfg(vc, &cfg); ++ ++ vc_decor_to_compat(cfg_compat, cfg); ++ ++ if (copy_to_user((struct vc_decor32 __user *)data, &cfg_compat, sizeof(struct vc_decor32))) ++ return -EFAULT; ++ return rval; ++ } ++ ++ case FBIOCONDECOR_SETSTATE32: ++ { ++ compat_uint_t state_compat = 0; ++ unsigned int state = 0; ++ ++ if (get_user(state_compat, (compat_uint_t __user *)data)) ++ return -EFAULT; ++ ++ state = (unsigned int)state_compat; ++ ++ return fbcon_decor_ioctl_dosetstate(vc, state, origin); ++ } ++ ++ case FBIOCONDECOR_GETSTATE32: ++ { ++ compat_uint_t state_compat = 0; ++ unsigned int state = 0; ++ ++ fbcon_decor_ioctl_dogetstate(vc, &state); ++ state_compat = (compat_uint_t)state; ++ ++ return put_user(state_compat, (compat_uint_t __user *)data); ++ } ++ ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++#else ++ #define fbcon_decor_compat_ioctl NULL ++#endif ++ ++static struct file_operations fbcon_decor_ops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = fbcon_decor_ioctl, ++ .compat_ioctl = fbcon_decor_compat_ioctl ++}; ++ ++static struct miscdevice fbcon_decor_dev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "fbcondecor", ++ .fops = &fbcon_decor_ops ++}; ++ ++void fbcon_decor_reset() ++{ ++ int i; ++ ++ for (i = 0; i < num_registered_fb; i++) { ++ registered_fb[i]->bgdecor.data = NULL; ++ registered_fb[i]->bgdecor.cmap.red = NULL; ++ } ++ ++ for (i = 0; i < MAX_NR_CONSOLES && vc_cons[i].d; i++) { ++ vc_cons[i].d->vc_decor.state = vc_cons[i].d->vc_decor.twidth = ++ vc_cons[i].d->vc_decor.theight = 0; ++ vc_cons[i].d->vc_decor.theme = NULL; ++ } ++ ++ return; ++} ++ ++int fbcon_decor_init() ++{ ++ int i; ++ ++ fbcon_decor_reset(); ++ ++ if (initialized) ++ return 0; ++ ++ i = misc_register(&fbcon_decor_dev); ++ if (i) { ++ printk(KERN_ERR "fbcondecor: failed to register device\n"); ++ return i; ++ } ++ ++ fbcon_decor_call_helper("init", 0); ++ initialized = 1; ++ return 0; ++} ++ ++int fbcon_decor_exit(void) ++{ ++ fbcon_decor_reset(); ++ return 0; ++} ++ ++EXPORT_SYMBOL(fbcon_decor_path); +diff -Nur linux-3.18.9.orig/drivers/video/console/fbcondecor.h linux-3.18.9/drivers/video/console/fbcondecor.h +--- linux-3.18.9.orig/drivers/video/console/fbcondecor.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.9/drivers/video/console/fbcondecor.h 2015-03-15 14:34:13.076143680 -0500 +@@ -0,0 +1,79 @@ ++/* ++ * linux/drivers/video/console/fbcondecor.h -- Framebuffer Console Decoration headers ++ * ++ * Copyright (C) 2004 Michal Januszewski ++ * ++ */ ++ ++#ifndef __FBCON_DECOR_H ++#define __FBCON_DECOR_H ++ ++#ifndef _LINUX_FB_H ++#include ++#endif ++ ++/* This is needed for vc_cons in fbcmap.c */ ++#include ++ ++struct fb_cursor; ++struct fb_info; ++struct vc_data; ++ ++#ifdef CONFIG_FB_CON_DECOR ++/* fbcondecor.c */ ++int fbcon_decor_init(void); ++void fbcon_decor_reset(void); ++int fbcon_decor_exit(void); ++int fbcon_decor_call_helper(char* cmd, unsigned short cons); ++int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw); ++ ++/* cfbcondecor.c */ ++void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx); ++void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor); ++void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width); ++void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only); ++void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank); ++void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width); ++void fbcon_decor_copy(u8 *dst, u8 *src, int height, int width, int linebytes, int srclinesbytes, int bpp); ++void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc); ++ ++/* vt.c */ ++void acquire_console_sem(void); ++void release_console_sem(void); ++void do_unblank_screen(int entering_gfx); ++ ++/* struct vc_data *y */ ++#define fbcon_decor_active_vc(y) (y->vc_decor.state && y->vc_decor.theme) ++ ++/* struct fb_info *x, struct vc_data *y */ ++#define fbcon_decor_active_nores(x,y) (x->bgdecor.data && fbcon_decor_active_vc(y)) ++ ++/* struct fb_info *x, struct vc_data *y */ ++#define fbcon_decor_active(x,y) (fbcon_decor_active_nores(x,y) && \ ++ x->bgdecor.width == x->var.xres && \ ++ x->bgdecor.height == x->var.yres && \ ++ x->bgdecor.depth == x->var.bits_per_pixel) ++ ++ ++#else /* CONFIG_FB_CON_DECOR */ ++ ++static inline void fbcon_decor_putcs(struct vc_data *vc, struct fb_info *info, const unsigned short *s, int count, int yy, int xx) {} ++static inline void fbcon_decor_putc(struct vc_data *vc, struct fb_info *info, int c, int ypos, int xpos) {} ++static inline void fbcon_decor_cursor(struct fb_info *info, struct fb_cursor *cursor) {} ++static inline void fbcon_decor_clear(struct vc_data *vc, struct fb_info *info, int sy, int sx, int height, int width) {} ++static inline void fbcon_decor_clear_margins(struct vc_data *vc, struct fb_info *info, int bottom_only) {} ++static inline void fbcon_decor_blank(struct vc_data *vc, struct fb_info *info, int blank) {} ++static inline void fbcon_decor_bmove_redraw(struct vc_data *vc, struct fb_info *info, int y, int sx, int dx, int width) {} ++static inline void fbcon_decor_fix_pseudo_pal(struct fb_info *info, struct vc_data *vc) {} ++static inline int fbcon_decor_call_helper(char* cmd, unsigned short cons) { return 0; } ++static inline int fbcon_decor_init(void) { return 0; } ++static inline int fbcon_decor_exit(void) { return 0; } ++static inline int fbcon_decor_disable(struct vc_data *vc, unsigned char redraw) { return 0; } ++ ++#define fbcon_decor_active_vc(y) (0) ++#define fbcon_decor_active_nores(x,y) (0) ++#define fbcon_decor_active(x,y) (0) ++ ++#endif /* CONFIG_FB_CON_DECOR */ ++ ++#endif /* __FBCON_DECOR_H */ +diff -Nur linux-3.18.9.orig/drivers/video/console/Kconfig linux-3.18.9/drivers/video/console/Kconfig +--- linux-3.18.9.orig/drivers/video/console/Kconfig 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/drivers/video/console/Kconfig 2015-03-15 14:34:13.068143682 -0500 +@@ -126,6 +126,19 @@ + such that other users of the framebuffer will remain normally + oriented. + ++config FB_CON_DECOR ++ bool "Support for the Framebuffer Console Decorations" ++ depends on FRAMEBUFFER_CONSOLE=y && !FB_TILEBLITTING ++ default n ++ ---help--- ++ This option enables support for framebuffer console decorations which ++ makes it possible to display images in the background of the system ++ consoles. Note that userspace utilities are necessary in order to take ++ advantage of these features. Refer to Documentation/fb/fbcondecor.txt ++ for more information. ++ ++ If unsure, say N. ++ + config STI_CONSOLE + bool "STI text console" + depends on PARISC +diff -Nur linux-3.18.9.orig/drivers/video/console/Makefile linux-3.18.9/drivers/video/console/Makefile +--- linux-3.18.9.orig/drivers/video/console/Makefile 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/drivers/video/console/Makefile 2015-03-15 14:34:13.068143682 -0500 +@@ -16,4 +16,5 @@ + fbcon_ccw.o + endif + ++obj-$(CONFIG_FB_CON_DECOR) += fbcondecor.o cfbcondecor.o + obj-$(CONFIG_FB_STI) += sticore.o +diff -Nur linux-3.18.9.orig/drivers/video/fbdev/core/fbcmap.c linux-3.18.9/drivers/video/fbdev/core/fbcmap.c +--- linux-3.18.9.orig/drivers/video/fbdev/core/fbcmap.c 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/drivers/video/fbdev/core/fbcmap.c 2015-03-15 14:34:13.076143680 -0500 +@@ -17,6 +17,8 @@ + #include + #include + ++#include "../../console/fbcondecor.h" ++ + static u16 red2[] __read_mostly = { + 0x0000, 0xaaaa + }; +@@ -257,6 +259,10 @@ + if (rc == 0) + fb_copy_cmap(cmap, &info->cmap); + ++ if (fbcon_decor_active(info, vc_cons[fg_console].d) && ++ info->fix.visual == FB_VISUAL_DIRECTCOLOR) ++ fbcon_decor_fix_pseudo_pal(info, vc_cons[fg_console].d); ++ + return rc; + } + +diff -Nur linux-3.18.9.orig/include/linux/console_decor.h linux-3.18.9/include/linux/console_decor.h +--- linux-3.18.9.orig/include/linux/console_decor.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.9/include/linux/console_decor.h 2015-03-15 14:34:13.076143680 -0500 +@@ -0,0 +1,46 @@ ++#ifndef _LINUX_CONSOLE_DECOR_H_ ++#define _LINUX_CONSOLE_DECOR_H_ 1 ++ ++/* A structure used by the framebuffer console decorations (drivers/video/console/fbcondecor.c) */ ++struct vc_decor { ++ __u8 bg_color; /* The color that is to be treated as transparent */ ++ __u8 state; /* Current decor state: 0 = off, 1 = on */ ++ __u16 tx, ty; /* Top left corner coordinates of the text field */ ++ __u16 twidth, theight; /* Width and height of the text field */ ++ char* theme; ++}; ++ ++#ifdef __KERNEL__ ++#ifdef CONFIG_COMPAT ++#include ++ ++struct vc_decor32 { ++ __u8 bg_color; /* The color that is to be treated as transparent */ ++ __u8 state; /* Current decor state: 0 = off, 1 = on */ ++ __u16 tx, ty; /* Top left corner coordinates of the text field */ ++ __u16 twidth, theight; /* Width and height of the text field */ ++ compat_uptr_t theme; ++}; ++ ++#define vc_decor_from_compat(to, from) \ ++ (to).bg_color = (from).bg_color; \ ++ (to).state = (from).state; \ ++ (to).tx = (from).tx; \ ++ (to).ty = (from).ty; \ ++ (to).twidth = (from).twidth; \ ++ (to).theight = (from).theight; \ ++ (to).theme = compat_ptr((from).theme) ++ ++#define vc_decor_to_compat(to, from) \ ++ (to).bg_color = (from).bg_color; \ ++ (to).state = (from).state; \ ++ (to).tx = (from).tx; \ ++ (to).ty = (from).ty; \ ++ (to).twidth = (from).twidth; \ ++ (to).theight = (from).theight; \ ++ (to).theme = ptr_to_compat((from).theme) ++ ++#endif /* CONFIG_COMPAT */ ++#endif /* __KERNEL__ */ ++ ++#endif +diff -Nur linux-3.18.9.orig/include/linux/console_struct.h linux-3.18.9/include/linux/console_struct.h +--- linux-3.18.9.orig/include/linux/console_struct.h 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/include/linux/console_struct.h 2015-03-15 14:34:13.076143680 -0500 +@@ -20,6 +20,7 @@ + struct uni_pagedir; + + #define NPAR 16 ++#include + + struct vc_data { + struct tty_port port; /* Upper level data */ +@@ -108,6 +109,8 @@ + struct uni_pagedir *vc_uni_pagedir; + struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */ + bool vc_panic_force_write; /* when oops/panic this VC can accept forced output/blanking */ ++ ++ struct vc_decor vc_decor; + /* additional information is in vt_kern.h */ + }; + +diff -Nur linux-3.18.9.orig/include/linux/fb.h linux-3.18.9/include/linux/fb.h +--- linux-3.18.9.orig/include/linux/fb.h 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/include/linux/fb.h 2015-03-15 14:34:13.080143679 -0500 +@@ -220,6 +220,34 @@ + }; + #endif + ++#ifdef __KERNEL__ ++#ifdef CONFIG_COMPAT ++struct fb_image32 { ++ __u32 dx; /* Where to place image */ ++ __u32 dy; ++ __u32 width; /* Size of image */ ++ __u32 height; ++ __u32 fg_color; /* Only used when a mono bitmap */ ++ __u32 bg_color; ++ __u8 depth; /* Depth of the image */ ++ const compat_uptr_t data; /* Pointer to image data */ ++ struct fb_cmap32 cmap; /* color map info */ ++}; ++ ++#define fb_image_from_compat(to, from) \ ++ (to).dx = (from).dx; \ ++ (to).dy = (from).dy; \ ++ (to).width = (from).width; \ ++ (to).height = (from).height; \ ++ (to).fg_color = (from).fg_color; \ ++ (to).bg_color = (from).bg_color; \ ++ (to).depth = (from).depth; \ ++ (to).data = compat_ptr((from).data); \ ++ fb_cmap_from_compat((to).cmap, (from).cmap) ++ ++#endif /* CONFIG_COMPAT */ ++#endif /* __KERNEL__ */ ++ + /* + * Frame buffer operations + * +@@ -490,6 +518,9 @@ + #define FBINFO_STATE_SUSPENDED 1 + u32 state; /* Hardware state i.e suspend */ + void *fbcon_par; /* fbcon use-only private area */ ++ ++ struct fb_image bgdecor; ++ + /* From here on everything is device dependent */ + void *par; + /* we need the PCI or similar aperture base/size not +diff -Nur linux-3.18.9.orig/include/uapi/linux/fb.h linux-3.18.9/include/uapi/linux/fb.h +--- linux-3.18.9.orig/include/uapi/linux/fb.h 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/include/uapi/linux/fb.h 2015-03-15 14:34:13.080143679 -0500 +@@ -8,6 +8,25 @@ + + #define FB_MAX 32 /* sufficient for now */ + ++struct fbcon_decor_iowrapper ++{ ++ unsigned short vc; /* Virtual console */ ++ unsigned char origin; /* Point of origin of the request */ ++ void *data; ++}; ++ ++#ifdef __KERNEL__ ++#ifdef CONFIG_COMPAT ++#include ++struct fbcon_decor_iowrapper32 ++{ ++ unsigned short vc; /* Virtual console */ ++ unsigned char origin; /* Point of origin of the request */ ++ compat_uptr_t data; ++}; ++#endif /* CONFIG_COMPAT */ ++#endif /* __KERNEL__ */ ++ + /* ioctls + 0x46 is 'F' */ + #define FBIOGET_VSCREENINFO 0x4600 +@@ -35,6 +54,25 @@ + #define FBIOGET_DISPINFO 0x4618 + #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) + ++#define FBIOCONDECOR_SETCFG _IOWR('F', 0x19, struct fbcon_decor_iowrapper) ++#define FBIOCONDECOR_GETCFG _IOR('F', 0x1A, struct fbcon_decor_iowrapper) ++#define FBIOCONDECOR_SETSTATE _IOWR('F', 0x1B, struct fbcon_decor_iowrapper) ++#define FBIOCONDECOR_GETSTATE _IOR('F', 0x1C, struct fbcon_decor_iowrapper) ++#define FBIOCONDECOR_SETPIC _IOWR('F', 0x1D, struct fbcon_decor_iowrapper) ++#ifdef __KERNEL__ ++#ifdef CONFIG_COMPAT ++#define FBIOCONDECOR_SETCFG32 _IOWR('F', 0x19, struct fbcon_decor_iowrapper32) ++#define FBIOCONDECOR_GETCFG32 _IOR('F', 0x1A, struct fbcon_decor_iowrapper32) ++#define FBIOCONDECOR_SETSTATE32 _IOWR('F', 0x1B, struct fbcon_decor_iowrapper32) ++#define FBIOCONDECOR_GETSTATE32 _IOR('F', 0x1C, struct fbcon_decor_iowrapper32) ++#define FBIOCONDECOR_SETPIC32 _IOWR('F', 0x1D, struct fbcon_decor_iowrapper32) ++#endif /* CONFIG_COMPAT */ ++#endif /* __KERNEL__ */ ++ ++#define FBCON_DECOR_THEME_LEN 128 /* Maximum lenght of a theme name */ ++#define FBCON_DECOR_IO_ORIG_KERNEL 0 /* Kernel ioctl origin */ ++#define FBCON_DECOR_IO_ORIG_USER 1 /* User ioctl origin */ ++ + #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ + #define FB_TYPE_PLANES 1 /* Non interleaved planes */ + #define FB_TYPE_INTERLEAVED_PLANES 2 /* Interleaved planes */ +@@ -277,6 +315,29 @@ + __u32 reserved[4]; /* Reserved for future compatibility */ + }; + ++#ifdef __KERNEL__ ++#ifdef CONFIG_COMPAT ++struct fb_cmap32 { ++ __u32 start; ++ __u32 len; /* Number of entries */ ++ compat_uptr_t red; /* Red values */ ++ compat_uptr_t green; ++ compat_uptr_t blue; ++ compat_uptr_t transp; /* transparency, can be NULL */ ++}; ++ ++#define fb_cmap_from_compat(to, from) \ ++ (to).start = (from).start; \ ++ (to).len = (from).len; \ ++ (to).red = compat_ptr((from).red); \ ++ (to).green = compat_ptr((from).green); \ ++ (to).blue = compat_ptr((from).blue); \ ++ (to).transp = compat_ptr((from).transp) ++ ++#endif /* CONFIG_COMPAT */ ++#endif /* __KERNEL__ */ ++ ++ + struct fb_cmap { + __u32 start; /* First entry */ + __u32 len; /* Number of entries */ +diff -Nur linux-3.18.9.orig/kernel/sysctl.c linux-3.18.9/kernel/sysctl.c +--- linux-3.18.9.orig/kernel/sysctl.c 2015-03-06 16:53:42.000000000 -0600 ++++ linux-3.18.9/kernel/sysctl.c 2015-03-15 14:34:13.080143679 -0500 +@@ -145,6 +145,10 @@ + static unsigned long hung_task_timeout_max = (LONG_MAX/HZ); + #endif + ++#ifdef CONFIG_FB_CON_DECOR ++extern char fbcon_decor_path[]; ++#endif ++ + #ifdef CONFIG_INOTIFY_USER + #include + #endif +@@ -257,6 +261,15 @@ + .mode = 0555, + .child = dev_table, + }, ++#ifdef CONFIG_FB_CON_DECOR ++ { ++ .procname = "fbcondecor", ++ .data = &fbcon_decor_path, ++ .maxlen = KMOD_PATH_LEN, ++ .mode = 0644, ++ .proc_handler = &proc_dostring, ++ }, ++#endif + { } + }; + diff --git a/target/linux/patches/3.18.16/gemalto.patch b/target/linux/patches/3.18.16/gemalto.patch new file mode 100644 index 000000000..65f7af1d7 --- /dev/null +++ b/target/linux/patches/3.18.16/gemalto.patch @@ -0,0 +1,11 @@ +diff -Nur linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c linux-2.6.36/drivers/serial/8250/serial_cs.c +--- linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/drivers/tty/serial/8250/serial_cs.c 2010-12-13 23:03:40.000000000 +0100 +@@ -794,6 +794,7 @@ + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), ++ PCMCIA_DEVICE_MANF_CARD(0x0157, 0x0100), /* Gemalto SCR */ + PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ + PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ + PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), diff --git a/target/linux/patches/3.18.16/initramfs-nosizelimit.patch b/target/linux/patches/3.18.16/initramfs-nosizelimit.patch new file mode 100644 index 000000000..40d2f6bd8 --- /dev/null +++ b/target/linux/patches/3.18.16/initramfs-nosizelimit.patch @@ -0,0 +1,57 @@ +From 9a18df7a71bfa620b1278777d64783a359d7eb4e Mon Sep 17 00:00:00 2001 +From: Thorsten Glaser +Date: Sun, 4 May 2014 01:37:54 +0200 +Subject: [PATCH] mount tmpfs-as-rootfs (initramfs) with -o + nr_blocks=0,nr_inodes=0 + +I would have preferred to write this patch to be able to pass +rootflags=nr_blocks=0,nr_inodes=0 on the kernel command line, +and then hand these rootflags over to the initramfs (tmpfs) +mount in the same way the kernel hands them over to the block +device rootfs mount. But at least the Debian/m68k initrd also +parses $rootflags from the environment and adds it to the call +to the user-space mount for the eventual root device, which +would make the kernel command line rootflags option be used in +both places (tmpfs and e.g. ext4) which is guaranteed to error +out in at least one of them. + +This change is intended to aid people in a setup where the +initrd is the final root filesystem, i.e. not mounted over. +This is especially useful in automated tests running on qemu +for boards with constrained memory (e.g. 64 MiB on sh4). + +Considering that the initramfs is normally emptied out then +overmounted, this change is probably safe for setups where +initramfs just hosts early userspace, too, since the tmpfs +backing it is not accessible any more later on, AFAICT. + +Signed-off-by: Thorsten Glaser +--- + init/do_mounts.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/init/do_mounts.c b/init/do_mounts.c +index 82f2288..55a4cfe 100644 +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -594,6 +594,7 @@ out: + } + + static bool is_tmpfs; ++static char tmpfs_rootflags[] = "nr_blocks=0,nr_inodes=0"; + static struct dentry *rootfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) + { +@@ -606,6 +607,9 @@ static struct dentry *rootfs_mount(struct file_system_type *fs_type, + if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) + fill = shmem_fill_super; + ++ if (is_tmpfs) ++ data = tmpfs_rootflags; ++ + return mount_nodev(fs_type, flags, data, fill); + } + +-- +2.0.0.rc0 + diff --git a/target/linux/patches/3.18.16/lemote-rfkill.patch b/target/linux/patches/3.18.16/lemote-rfkill.patch new file mode 100644 index 000000000..a61488434 --- /dev/null +++ b/target/linux/patches/3.18.16/lemote-rfkill.patch @@ -0,0 +1,21 @@ +diff -Nur linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c +--- linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-19 00:15:34.000000000 +0100 ++++ linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-27 23:29:46.000000000 +0200 +@@ -22,6 +22,9 @@ + + static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) + { ++#ifdef CONFIG_LEMOTE_MACH2F ++ return 1; ++#else + u8 gpio; + + gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); +@@ -29,6 +32,7 @@ + gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); + + return gpio & priv->rfkill_mask; ++#endif + } + + void rtl8187_rfkill_init(struct ieee80211_hw *hw) diff --git a/target/linux/patches/3.18.16/microblaze-ethernet.patch b/target/linux/patches/3.18.16/microblaze-ethernet.patch new file mode 100644 index 000000000..742ab477e --- /dev/null +++ b/target/linux/patches/3.18.16/microblaze-ethernet.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c +--- linux-3.11.10.orig/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-11-29 19:42:37.000000000 +0100 ++++ linux-3.11.10/drivers/net/ethernet/xilinx/xilinx_emaclite.c 2013-12-23 20:01:14.000000000 +0100 +@@ -1282,6 +1282,7 @@ + { .compatible = "xlnx,opb-ethernetlite-1.01.b", }, + { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, + { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, ++ { .compatible = "xlnx,xps-ethernetlite-2.00.b", }, + { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, + { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, + { /* end of list */ }, diff --git a/target/linux/patches/3.18.16/mkpiggy.patch b/target/linux/patches/3.18.16/mkpiggy.patch new file mode 100644 index 000000000..751678b74 --- /dev/null +++ b/target/linux/patches/3.18.16/mkpiggy.patch @@ -0,0 +1,28 @@ +diff -Nur linux-3.13.3.orig/arch/x86/boot/compressed/mkpiggy.c linux-3.13.3/arch/x86/boot/compressed/mkpiggy.c +--- linux-3.13.3.orig/arch/x86/boot/compressed/mkpiggy.c 2014-02-13 23:00:14.000000000 +0100 ++++ linux-3.13.3/arch/x86/boot/compressed/mkpiggy.c 2014-02-17 11:09:06.000000000 +0100 +@@ -29,7 +29,14 @@ + #include + #include + #include +-#include ++ ++static uint32_t getle32(const void *p) ++{ ++ const uint8_t *cp = p; ++ ++ return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + ++ ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); ++} + + int main(int argc, char *argv[]) + { +@@ -63,7 +70,7 @@ + } + + ilen = ftell(f); +- olen = get_unaligned_le32(&olen); ++ olen = getle32(&olen); + + /* + * Now we have the input (compressed) and output (uncompressed) diff --git a/target/linux/patches/3.18.16/mtd-rootfs.patch b/target/linux/patches/3.18.16/mtd-rootfs.patch new file mode 100644 index 000000000..775d5fc80 --- /dev/null +++ b/target/linux/patches/3.18.16/mtd-rootfs.patch @@ -0,0 +1,26 @@ +diff -Nur linux-3.5.orig//drivers/mtd/mtdpart.c linux-3.5/drivers/mtd/mtdpart.c +--- linux-3.5.orig//drivers/mtd/mtdpart.c 2012-07-21 22:58:29.000000000 +0200 ++++ linux-3.5/drivers/mtd/mtdpart.c 2012-07-31 23:59:07.000000000 +0200 +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + + #include "mtdcore.h" + +@@ -637,6 +638,14 @@ + if (IS_ERR(slave)) + return PTR_ERR(slave); + ++ if (strcmp(parts[i].name, "rootfs") == 0) { ++ if (ROOT_DEV == 0) { ++ printk(KERN_NOTICE "mtd: partition \"rootfs\" " ++ "set to be root filesystem\n"); ++ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, i); ++ } ++ } ++ + mutex_lock(&mtd_partitions_mutex); + list_add(&slave->list, &mtd_partitions); + mutex_unlock(&mtd_partitions_mutex); diff --git a/target/linux/patches/3.18.16/nfsv3-tcp.patch b/target/linux/patches/3.18.16/nfsv3-tcp.patch new file mode 100644 index 000000000..d5e07e1c2 --- /dev/null +++ b/target/linux/patches/3.18.16/nfsv3-tcp.patch @@ -0,0 +1,12 @@ +diff -Nur linux-3.15-rc5.orig/fs/nfs/nfsroot.c linux-3.15-rc5/fs/nfs/nfsroot.c +--- linux-3.15-rc5.orig/fs/nfs/nfsroot.c 2014-05-09 22:10:52.000000000 +0200 ++++ linux-3.15-rc5/fs/nfs/nfsroot.c 2014-05-16 15:45:38.000000000 +0200 +@@ -87,7 +87,7 @@ + #define NFS_ROOT "/tftpboot/%s" + + /* Default NFSROOT mount options. */ +-#define NFS_DEF_OPTIONS "vers=2,udp,rsize=4096,wsize=4096" ++#define NFS_DEF_OPTIONS "nfsvers=3,proto=tcp,rsize=4096,wsize=4096" + + /* Parameters passed from the kernel command line */ + static char nfs_root_parms[256] __initdata = ""; diff --git a/target/linux/patches/3.18.16/non-static.patch b/target/linux/patches/3.18.16/non-static.patch new file mode 100644 index 000000000..a967703d0 --- /dev/null +++ b/target/linux/patches/3.18.16/non-static.patch @@ -0,0 +1,33 @@ +diff -Nur linux-2.6.39-rc6.orig/fs/namei.c linux-2.6.39-rc6/fs/namei.c +--- linux-2.6.39-rc6.orig/fs/namei.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6/fs/namei.c 2011-05-05 11:30:14.000000000 +0200 +@@ -1769,7 +1769,7 @@ + * needs parent already locked. Doesn't follow mounts. + * SMP-safe. + */ +-static struct dentry *lookup_hash(struct nameidata *nd) ++struct dentry *lookup_hash(struct nameidata *nd) + { + return __lookup_hash(&nd->last, nd->path.dentry, nd); + } +diff -Nur linux-2.6.39-rc6.orig/fs/splice.c linux-2.6.39-rc6/fs/splice.c +--- linux-2.6.39-rc6.orig/fs/splice.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6/fs/splice.c 2011-05-05 11:31:04.000000000 +0200 +@@ -1081,7 +1081,7 @@ + /* + * Attempt to initiate a splice from pipe to file. + */ +-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) + { + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, +@@ -1109,7 +1109,7 @@ + /* + * Attempt to initiate a splice from a file to a pipe. + */ +-static long do_splice_to(struct file *in, loff_t *ppos, ++long do_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) + { diff --git a/target/linux/patches/3.18.16/ppc64-missing-zlib.patch b/target/linux/patches/3.18.16/ppc64-missing-zlib.patch new file mode 100644 index 000000000..c6e0616be --- /dev/null +++ b/target/linux/patches/3.18.16/ppc64-missing-zlib.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig +--- linux-3.11.5.orig/arch/powerpc/platforms/pseries/Kconfig 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/powerpc/platforms/pseries/Kconfig 2013-11-01 15:23:09.000000000 +0100 +@@ -17,6 +17,7 @@ + select PPC_NATIVE + select PPC_PCI_CHOICE if EXPERT + select ZLIB_DEFLATE ++ select ZLIB_INFLATE + select PPC_DOORBELL + select HAVE_CONTEXT_TRACKING + select HOTPLUG_CPU if SMP diff --git a/target/linux/patches/3.18.16/realtime.patch b/target/linux/patches/3.18.16/realtime.patch new file mode 100644 index 000000000..28b9b271c --- /dev/null +++ b/target/linux/patches/3.18.16/realtime.patch @@ -0,0 +1,36846 @@ +diff -Nur linux-3.18.14.orig/arch/alpha/mm/fault.c linux-3.18.14-rt/arch/alpha/mm/fault.c +--- linux-3.18.14.orig/arch/alpha/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/alpha/mm/fault.c 2015-05-31 15:32:45.517635394 -0500 +@@ -107,7 +107,7 @@ + + /* If we're in an interrupt context, or have no user context, + we must not take the fault. */ +- if (!mm || in_atomic()) ++ if (!mm || pagefault_disabled()) + goto no_context; + + #ifdef CONFIG_ALPHA_LARGE_VMALLOC +diff -Nur linux-3.18.14.orig/arch/arm/include/asm/cmpxchg.h linux-3.18.14-rt/arch/arm/include/asm/cmpxchg.h +--- linux-3.18.14.orig/arch/arm/include/asm/cmpxchg.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/include/asm/cmpxchg.h 2015-05-31 15:32:45.557635393 -0500 +@@ -129,6 +129,8 @@ + + #else /* min ARCH >= ARMv6 */ + ++#define __HAVE_ARCH_CMPXCHG 1 ++ + extern void __bad_cmpxchg(volatile void *ptr, int size); + + /* +diff -Nur linux-3.18.14.orig/arch/arm/include/asm/futex.h linux-3.18.14-rt/arch/arm/include/asm/futex.h +--- linux-3.18.14.orig/arch/arm/include/asm/futex.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/include/asm/futex.h 2015-05-31 15:32:45.561635393 -0500 +@@ -93,6 +93,8 @@ + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + ++ preempt_disable_rt(); ++ + __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" + "1: " TUSER(ldr) " %1, [%4]\n" + " teq %1, %2\n" +@@ -104,6 +106,8 @@ + : "cc", "memory"); + + *uval = val; ++ ++ preempt_enable_rt(); + return ret; + } + +diff -Nur linux-3.18.14.orig/arch/arm/include/asm/switch_to.h linux-3.18.14-rt/arch/arm/include/asm/switch_to.h +--- linux-3.18.14.orig/arch/arm/include/asm/switch_to.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/include/asm/switch_to.h 2015-05-31 15:32:45.565635393 -0500 +@@ -3,6 +3,13 @@ + + #include + ++#if defined CONFIG_PREEMPT_RT_FULL && defined CONFIG_HIGHMEM ++void switch_kmaps(struct task_struct *prev_p, struct task_struct *next_p); ++#else ++static inline void ++switch_kmaps(struct task_struct *prev_p, struct task_struct *next_p) { } ++#endif ++ + /* + * For v7 SMP cores running a preemptible kernel we may be pre-empted + * during a TLB maintenance operation, so execute an inner-shareable dsb +@@ -22,6 +29,7 @@ + + #define switch_to(prev,next,last) \ + do { \ ++ switch_kmaps(prev, next); \ + last = __switch_to(prev,task_thread_info(prev), task_thread_info(next)); \ + } while (0) + +diff -Nur linux-3.18.14.orig/arch/arm/include/asm/thread_info.h linux-3.18.14-rt/arch/arm/include/asm/thread_info.h +--- linux-3.18.14.orig/arch/arm/include/asm/thread_info.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/include/asm/thread_info.h 2015-05-31 15:32:45.585635393 -0500 +@@ -51,6 +51,7 @@ + struct thread_info { + unsigned long flags; /* low level flags */ + int preempt_count; /* 0 => preemptable, <0 => bug */ ++ int preempt_lazy_count; /* 0 => preemptable, <0 => bug */ + mm_segment_t addr_limit; /* address limit */ + struct task_struct *task; /* main task structure */ + struct exec_domain *exec_domain; /* execution domain */ +@@ -149,6 +150,7 @@ + #define TIF_SIGPENDING 0 + #define TIF_NEED_RESCHED 1 + #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ ++#define TIF_NEED_RESCHED_LAZY 3 + #define TIF_UPROBE 7 + #define TIF_SYSCALL_TRACE 8 + #define TIF_SYSCALL_AUDIT 9 +@@ -162,6 +164,7 @@ + #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) + #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) + #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) ++#define _TIF_NEED_RESCHED_LAZY (1 << TIF_NEED_RESCHED_LAZY) + #define _TIF_UPROBE (1 << TIF_UPROBE) + #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) + #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) +diff -Nur linux-3.18.14.orig/arch/arm/Kconfig linux-3.18.14-rt/arch/arm/Kconfig +--- linux-3.18.14.orig/arch/arm/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/Kconfig 2015-05-31 15:32:45.529635394 -0500 +@@ -62,6 +62,7 @@ + select HAVE_PERF_EVENTS + select HAVE_PERF_REGS + select HAVE_PERF_USER_STACK_DUMP ++ select HAVE_PREEMPT_LAZY + select HAVE_RCU_TABLE_FREE if (SMP && ARM_LPAE) + select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_SYSCALL_TRACEPOINTS +diff -Nur linux-3.18.14.orig/arch/arm/kernel/asm-offsets.c linux-3.18.14-rt/arch/arm/kernel/asm-offsets.c +--- linux-3.18.14.orig/arch/arm/kernel/asm-offsets.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/kernel/asm-offsets.c 2015-05-31 15:32:45.605635393 -0500 +@@ -64,6 +64,7 @@ + BLANK(); + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); ++ DEFINE(TI_PREEMPT_LAZY, offsetof(struct thread_info, preempt_lazy_count)); + DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); + DEFINE(TI_TASK, offsetof(struct thread_info, task)); + DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain)); +diff -Nur linux-3.18.14.orig/arch/arm/kernel/entry-armv.S linux-3.18.14-rt/arch/arm/kernel/entry-armv.S +--- linux-3.18.14.orig/arch/arm/kernel/entry-armv.S 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/kernel/entry-armv.S 2015-05-31 15:32:45.613635393 -0500 +@@ -207,11 +207,18 @@ + #ifdef CONFIG_PREEMPT + get_thread_info tsk + ldr r8, [tsk, #TI_PREEMPT] @ get preempt count +- ldr r0, [tsk, #TI_FLAGS] @ get flags + teq r8, #0 @ if preempt count != 0 ++ bne 1f @ return from exeption ++ ldr r0, [tsk, #TI_FLAGS] @ get flags ++ tst r0, #_TIF_NEED_RESCHED @ if NEED_RESCHED is set ++ blne svc_preempt @ preempt! ++ ++ ldr r8, [tsk, #TI_PREEMPT_LAZY] @ get preempt lazy count ++ teq r8, #0 @ if preempt lazy count != 0 + movne r0, #0 @ force flags to 0 +- tst r0, #_TIF_NEED_RESCHED ++ tst r0, #_TIF_NEED_RESCHED_LAZY + blne svc_preempt ++1: + #endif + + svc_exit r5, irq = 1 @ return from exception +@@ -226,6 +233,8 @@ + 1: bl preempt_schedule_irq @ irq en/disable is done inside + ldr r0, [tsk, #TI_FLAGS] @ get new tasks TI_FLAGS + tst r0, #_TIF_NEED_RESCHED ++ bne 1b ++ tst r0, #_TIF_NEED_RESCHED_LAZY + reteq r8 @ go again + b 1b + #endif +diff -Nur linux-3.18.14.orig/arch/arm/kernel/process.c linux-3.18.14-rt/arch/arm/kernel/process.c +--- linux-3.18.14.orig/arch/arm/kernel/process.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/kernel/process.c 2015-05-31 15:32:45.617635393 -0500 +@@ -437,6 +437,30 @@ + } + + #ifdef CONFIG_MMU ++/* ++ * CONFIG_SPLIT_PTLOCK_CPUS results in a page->ptl lock. If the lock is not ++ * initialized by pgtable_page_ctor() then a coredump of the vector page will ++ * fail. ++ */ ++static int __init vectors_user_mapping_init_page(void) ++{ ++ struct page *page; ++ unsigned long addr = 0xffff0000; ++ pgd_t *pgd; ++ pud_t *pud; ++ pmd_t *pmd; ++ ++ pgd = pgd_offset_k(addr); ++ pud = pud_offset(pgd, addr); ++ pmd = pmd_offset(pud, addr); ++ page = pmd_page(*(pmd)); ++ ++ pgtable_page_ctor(page); ++ ++ return 0; ++} ++late_initcall(vectors_user_mapping_init_page); ++ + #ifdef CONFIG_KUSER_HELPERS + /* + * The vectors page is always readable from user space for the +diff -Nur linux-3.18.14.orig/arch/arm/kernel/process.c.orig linux-3.18.14-rt/arch/arm/kernel/process.c.orig +--- linux-3.18.14.orig/arch/arm/kernel/process.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/arch/arm/kernel/process.c.orig 2015-05-20 10:04:50.000000000 -0500 +@@ -0,0 +1,560 @@ ++/* ++ * linux/arch/arm/kernel/process.c ++ * ++ * Copyright (C) 1996-2000 Russell King - Converted to ARM. ++ * Original Copyright (C) 1995 Linus Torvalds ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "reboot.h" ++ ++#ifdef CONFIG_CC_STACKPROTECTOR ++#include ++unsigned long __stack_chk_guard __read_mostly; ++EXPORT_SYMBOL(__stack_chk_guard); ++#endif ++ ++static const char *processor_modes[] __maybe_unused = { ++ "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" , ++ "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26", ++ "USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" , ++ "UK8_32" , "UK9_32" , "UK10_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32" ++}; ++ ++static const char *isa_modes[] __maybe_unused = { ++ "ARM" , "Thumb" , "Jazelle", "ThumbEE" ++}; ++ ++extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); ++typedef void (*phys_reset_t)(unsigned long); ++ ++/* ++ * A temporary stack to use for CPU reset. This is static so that we ++ * don't clobber it with the identity mapping. When running with this ++ * stack, any references to the current task *will not work* so you ++ * should really do as little as possible before jumping to your reset ++ * code. ++ */ ++static u64 soft_restart_stack[16]; ++ ++static void __soft_restart(void *addr) ++{ ++ phys_reset_t phys_reset; ++ ++ /* Take out a flat memory mapping. */ ++ setup_mm_for_reboot(); ++ ++ /* Clean and invalidate caches */ ++ flush_cache_all(); ++ ++ /* Turn off caching */ ++ cpu_proc_fin(); ++ ++ /* Push out any further dirty data, and ensure cache is empty */ ++ flush_cache_all(); ++ ++ /* Switch to the identity mapping. */ ++ phys_reset = (phys_reset_t)(unsigned long)virt_to_phys(cpu_reset); ++ phys_reset((unsigned long)addr); ++ ++ /* Should never get here. */ ++ BUG(); ++} ++ ++void _soft_restart(unsigned long addr, bool disable_l2) ++{ ++ u64 *stack = soft_restart_stack + ARRAY_SIZE(soft_restart_stack); ++ ++ /* Disable interrupts first */ ++ raw_local_irq_disable(); ++ local_fiq_disable(); ++ ++ /* Disable the L2 if we're the last man standing. */ ++ if (disable_l2) ++ outer_disable(); ++ ++ /* Change to the new stack and continue with the reset. */ ++ call_with_stack(__soft_restart, (void *)addr, (void *)stack); ++ ++ /* Should never get here. */ ++ BUG(); ++} ++ ++void soft_restart(unsigned long addr) ++{ ++ _soft_restart(addr, num_online_cpus() == 1); ++} ++ ++/* ++ * Function pointers to optional machine specific functions ++ */ ++void (*pm_power_off)(void); ++EXPORT_SYMBOL(pm_power_off); ++ ++void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); ++ ++/* ++ * This is our default idle handler. ++ */ ++ ++void (*arm_pm_idle)(void); ++ ++/* ++ * Called from the core idle loop. ++ */ ++ ++void arch_cpu_idle(void) ++{ ++ if (arm_pm_idle) ++ arm_pm_idle(); ++ else ++ cpu_do_idle(); ++ local_irq_enable(); ++} ++ ++void arch_cpu_idle_prepare(void) ++{ ++ local_fiq_enable(); ++} ++ ++void arch_cpu_idle_enter(void) ++{ ++ ledtrig_cpu(CPU_LED_IDLE_START); ++#ifdef CONFIG_PL310_ERRATA_769419 ++ wmb(); ++#endif ++} ++ ++void arch_cpu_idle_exit(void) ++{ ++ ledtrig_cpu(CPU_LED_IDLE_END); ++} ++ ++#ifdef CONFIG_HOTPLUG_CPU ++void arch_cpu_idle_dead(void) ++{ ++ cpu_die(); ++} ++#endif ++ ++/* ++ * Called by kexec, immediately prior to machine_kexec(). ++ * ++ * This must completely disable all secondary CPUs; simply causing those CPUs ++ * to execute e.g. a RAM-based pin loop is not sufficient. This allows the ++ * kexec'd kernel to use any and all RAM as it sees fit, without having to ++ * avoid any code or data used by any SW CPU pin loop. The CPU hotplug ++ * functionality embodied in disable_nonboot_cpus() to achieve this. ++ */ ++void machine_shutdown(void) ++{ ++ disable_nonboot_cpus(); ++} ++ ++/* ++ * Halting simply requires that the secondary CPUs stop performing any ++ * activity (executing tasks, handling interrupts). smp_send_stop() ++ * achieves this. ++ */ ++void machine_halt(void) ++{ ++ local_irq_disable(); ++ smp_send_stop(); ++ ++ local_irq_disable(); ++ while (1); ++} ++ ++/* ++ * Power-off simply requires that the secondary CPUs stop performing any ++ * activity (executing tasks, handling interrupts). smp_send_stop() ++ * achieves this. When the system power is turned off, it will take all CPUs ++ * with it. ++ */ ++void machine_power_off(void) ++{ ++ local_irq_disable(); ++ smp_send_stop(); ++ ++ if (pm_power_off) ++ pm_power_off(); ++} ++ ++/* ++ * Restart requires that the secondary CPUs stop performing any activity ++ * while the primary CPU resets the system. Systems with a single CPU can ++ * use soft_restart() as their machine descriptor's .restart hook, since that ++ * will cause the only available CPU to reset. Systems with multiple CPUs must ++ * provide a HW restart implementation, to ensure that all CPUs reset at once. ++ * This is required so that any code running after reset on the primary CPU ++ * doesn't have to co-ordinate with other CPUs to ensure they aren't still ++ * executing pre-reset code, and using RAM that the primary CPU's code wishes ++ * to use. Implementing such co-ordination would be essentially impossible. ++ */ ++void machine_restart(char *cmd) ++{ ++ local_irq_disable(); ++ smp_send_stop(); ++ ++ if (arm_pm_restart) ++ arm_pm_restart(reboot_mode, cmd); ++ else ++ do_kernel_restart(cmd); ++ ++ /* Give a grace period for failure to restart of 1s */ ++ mdelay(1000); ++ ++ /* Whoops - the platform was unable to reboot. Tell the user! */ ++ printk("Reboot failed -- System halted\n"); ++ local_irq_disable(); ++ while (1); ++} ++ ++void __show_regs(struct pt_regs *regs) ++{ ++ unsigned long flags; ++ char buf[64]; ++ ++ show_regs_print_info(KERN_DEFAULT); ++ ++ print_symbol("PC is at %s\n", instruction_pointer(regs)); ++ print_symbol("LR is at %s\n", regs->ARM_lr); ++ printk("pc : [<%08lx>] lr : [<%08lx>] psr: %08lx\n" ++ "sp : %08lx ip : %08lx fp : %08lx\n", ++ regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr, ++ regs->ARM_sp, regs->ARM_ip, regs->ARM_fp); ++ printk("r10: %08lx r9 : %08lx r8 : %08lx\n", ++ regs->ARM_r10, regs->ARM_r9, ++ regs->ARM_r8); ++ printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", ++ regs->ARM_r7, regs->ARM_r6, ++ regs->ARM_r5, regs->ARM_r4); ++ printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", ++ regs->ARM_r3, regs->ARM_r2, ++ regs->ARM_r1, regs->ARM_r0); ++ ++ flags = regs->ARM_cpsr; ++ buf[0] = flags & PSR_N_BIT ? 'N' : 'n'; ++ buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z'; ++ buf[2] = flags & PSR_C_BIT ? 'C' : 'c'; ++ buf[3] = flags & PSR_V_BIT ? 'V' : 'v'; ++ buf[4] = '\0'; ++ ++#ifndef CONFIG_CPU_V7M ++ printk("Flags: %s IRQs o%s FIQs o%s Mode %s ISA %s Segment %s\n", ++ buf, interrupts_enabled(regs) ? "n" : "ff", ++ fast_interrupts_enabled(regs) ? "n" : "ff", ++ processor_modes[processor_mode(regs)], ++ isa_modes[isa_mode(regs)], ++ get_fs() == get_ds() ? "kernel" : "user"); ++#else ++ printk("xPSR: %08lx\n", regs->ARM_cpsr); ++#endif ++ ++#ifdef CONFIG_CPU_CP15 ++ { ++ unsigned int ctrl; ++ ++ buf[0] = '\0'; ++#ifdef CONFIG_CPU_CP15_MMU ++ { ++ unsigned int transbase, dac; ++ asm("mrc p15, 0, %0, c2, c0\n\t" ++ "mrc p15, 0, %1, c3, c0\n" ++ : "=r" (transbase), "=r" (dac)); ++ snprintf(buf, sizeof(buf), " Table: %08x DAC: %08x", ++ transbase, dac); ++ } ++#endif ++ asm("mrc p15, 0, %0, c1, c0\n" : "=r" (ctrl)); ++ ++ printk("Control: %08x%s\n", ctrl, buf); ++ } ++#endif ++} ++ ++void show_regs(struct pt_regs * regs) ++{ ++ __show_regs(regs); ++ dump_stack(); ++} ++ ++ATOMIC_NOTIFIER_HEAD(thread_notify_head); ++ ++EXPORT_SYMBOL_GPL(thread_notify_head); ++ ++/* ++ * Free current thread data structures etc.. ++ */ ++void exit_thread(void) ++{ ++ thread_notify(THREAD_NOTIFY_EXIT, current_thread_info()); ++} ++ ++void flush_thread(void) ++{ ++ struct thread_info *thread = current_thread_info(); ++ struct task_struct *tsk = current; ++ ++ flush_ptrace_hw_breakpoint(tsk); ++ ++ memset(thread->used_cp, 0, sizeof(thread->used_cp)); ++ memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); ++ memset(&thread->fpstate, 0, sizeof(union fp_state)); ++ ++ flush_tls(); ++ ++ thread_notify(THREAD_NOTIFY_FLUSH, thread); ++} ++ ++void release_thread(struct task_struct *dead_task) ++{ ++} ++ ++asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); ++ ++int ++copy_thread(unsigned long clone_flags, unsigned long stack_start, ++ unsigned long stk_sz, struct task_struct *p) ++{ ++ struct thread_info *thread = task_thread_info(p); ++ struct pt_regs *childregs = task_pt_regs(p); ++ ++ memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); ++ ++ if (likely(!(p->flags & PF_KTHREAD))) { ++ *childregs = *current_pt_regs(); ++ childregs->ARM_r0 = 0; ++ if (stack_start) ++ childregs->ARM_sp = stack_start; ++ } else { ++ memset(childregs, 0, sizeof(struct pt_regs)); ++ thread->cpu_context.r4 = stk_sz; ++ thread->cpu_context.r5 = stack_start; ++ childregs->ARM_cpsr = SVC_MODE; ++ } ++ thread->cpu_context.pc = (unsigned long)ret_from_fork; ++ thread->cpu_context.sp = (unsigned long)childregs; ++ ++ clear_ptrace_hw_breakpoint(p); ++ ++ if (clone_flags & CLONE_SETTLS) ++ thread->tp_value[0] = childregs->ARM_r3; ++ thread->tp_value[1] = get_tpuser(); ++ ++ thread_notify(THREAD_NOTIFY_COPY, thread); ++ ++ return 0; ++} ++ ++/* ++ * Fill in the task's elfregs structure for a core dump. ++ */ ++int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs) ++{ ++ elf_core_copy_regs(elfregs, task_pt_regs(t)); ++ return 1; ++} ++ ++/* ++ * fill in the fpe structure for a core dump... ++ */ ++int dump_fpu (struct pt_regs *regs, struct user_fp *fp) ++{ ++ struct thread_info *thread = current_thread_info(); ++ int used_math = thread->used_cp[1] | thread->used_cp[2]; ++ ++ if (used_math) ++ memcpy(fp, &thread->fpstate.soft, sizeof (*fp)); ++ ++ return used_math != 0; ++} ++EXPORT_SYMBOL(dump_fpu); ++ ++unsigned long get_wchan(struct task_struct *p) ++{ ++ struct stackframe frame; ++ unsigned long stack_page; ++ int count = 0; ++ if (!p || p == current || p->state == TASK_RUNNING) ++ return 0; ++ ++ frame.fp = thread_saved_fp(p); ++ frame.sp = thread_saved_sp(p); ++ frame.lr = 0; /* recovered from the stack */ ++ frame.pc = thread_saved_pc(p); ++ stack_page = (unsigned long)task_stack_page(p); ++ do { ++ if (frame.sp < stack_page || ++ frame.sp >= stack_page + THREAD_SIZE || ++ unwind_frame(&frame) < 0) ++ return 0; ++ if (!in_sched_functions(frame.pc)) ++ return frame.pc; ++ } while (count ++ < 16); ++ return 0; ++} ++ ++unsigned long arch_randomize_brk(struct mm_struct *mm) ++{ ++ unsigned long range_end = mm->brk + 0x02000000; ++ return randomize_range(mm->brk, range_end, 0) ? : mm->brk; ++} ++ ++#ifdef CONFIG_MMU ++#ifdef CONFIG_KUSER_HELPERS ++/* ++ * The vectors page is always readable from user space for the ++ * atomic helpers. Insert it into the gate_vma so that it is visible ++ * through ptrace and /proc//mem. ++ */ ++static struct vm_area_struct gate_vma = { ++ .vm_start = 0xffff0000, ++ .vm_end = 0xffff0000 + PAGE_SIZE, ++ .vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYEXEC, ++}; ++ ++static int __init gate_vma_init(void) ++{ ++ gate_vma.vm_page_prot = PAGE_READONLY_EXEC; ++ return 0; ++} ++arch_initcall(gate_vma_init); ++ ++struct vm_area_struct *get_gate_vma(struct mm_struct *mm) ++{ ++ return &gate_vma; ++} ++ ++int in_gate_area(struct mm_struct *mm, unsigned long addr) ++{ ++ return (addr >= gate_vma.vm_start) && (addr < gate_vma.vm_end); ++} ++ ++int in_gate_area_no_mm(unsigned long addr) ++{ ++ return in_gate_area(NULL, addr); ++} ++#define is_gate_vma(vma) ((vma) == &gate_vma) ++#else ++#define is_gate_vma(vma) 0 ++#endif ++ ++const char *arch_vma_name(struct vm_area_struct *vma) ++{ ++ return is_gate_vma(vma) ? "[vectors]" : NULL; ++} ++ ++/* If possible, provide a placement hint at a random offset from the ++ * stack for the signal page. ++ */ ++static unsigned long sigpage_addr(const struct mm_struct *mm, ++ unsigned int npages) ++{ ++ unsigned long offset; ++ unsigned long first; ++ unsigned long last; ++ unsigned long addr; ++ unsigned int slots; ++ ++ first = PAGE_ALIGN(mm->start_stack); ++ ++ last = TASK_SIZE - (npages << PAGE_SHIFT); ++ ++ /* No room after stack? */ ++ if (first > last) ++ return 0; ++ ++ /* Just enough room? */ ++ if (first == last) ++ return first; ++ ++ slots = ((last - first) >> PAGE_SHIFT) + 1; ++ ++ offset = get_random_int() % slots; ++ ++ addr = first + (offset << PAGE_SHIFT); ++ ++ return addr; ++} ++ ++static struct page *signal_page; ++extern struct page *get_signal_page(void); ++ ++static const struct vm_special_mapping sigpage_mapping = { ++ .name = "[sigpage]", ++ .pages = &signal_page, ++}; ++ ++int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) ++{ ++ struct mm_struct *mm = current->mm; ++ struct vm_area_struct *vma; ++ unsigned long addr; ++ unsigned long hint; ++ int ret = 0; ++ ++ if (!signal_page) ++ signal_page = get_signal_page(); ++ if (!signal_page) ++ return -ENOMEM; ++ ++ down_write(&mm->mmap_sem); ++ hint = sigpage_addr(mm, 1); ++ addr = get_unmapped_area(NULL, hint, PAGE_SIZE, 0, 0); ++ if (IS_ERR_VALUE(addr)) { ++ ret = addr; ++ goto up_fail; ++ } ++ ++ vma = _install_special_mapping(mm, addr, PAGE_SIZE, ++ VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, ++ &sigpage_mapping); ++ ++ if (IS_ERR(vma)) { ++ ret = PTR_ERR(vma); ++ goto up_fail; ++ } ++ ++ mm->context.sigpage = addr; ++ ++ up_fail: ++ up_write(&mm->mmap_sem); ++ return ret; ++} ++#endif +diff -Nur linux-3.18.14.orig/arch/arm/kernel/signal.c linux-3.18.14-rt/arch/arm/kernel/signal.c +--- linux-3.18.14.orig/arch/arm/kernel/signal.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/kernel/signal.c 2015-05-31 15:32:45.617635393 -0500 +@@ -574,7 +574,8 @@ + do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall) + { + do { +- if (likely(thread_flags & _TIF_NEED_RESCHED)) { ++ if (likely(thread_flags & (_TIF_NEED_RESCHED | ++ _TIF_NEED_RESCHED_LAZY))) { + schedule(); + } else { + if (unlikely(!user_mode(regs))) +diff -Nur linux-3.18.14.orig/arch/arm/kernel/unwind.c linux-3.18.14-rt/arch/arm/kernel/unwind.c +--- linux-3.18.14.orig/arch/arm/kernel/unwind.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/kernel/unwind.c 2015-05-31 15:32:45.653635392 -0500 +@@ -93,7 +93,7 @@ + static const struct unwind_idx *__origin_unwind_idx; + extern const struct unwind_idx __stop_unwind_idx[]; + +-static DEFINE_SPINLOCK(unwind_lock); ++static DEFINE_RAW_SPINLOCK(unwind_lock); + static LIST_HEAD(unwind_tables); + + /* Convert a prel31 symbol to an absolute address */ +@@ -201,7 +201,7 @@ + /* module unwind tables */ + struct unwind_table *table; + +- spin_lock_irqsave(&unwind_lock, flags); ++ raw_spin_lock_irqsave(&unwind_lock, flags); + list_for_each_entry(table, &unwind_tables, list) { + if (addr >= table->begin_addr && + addr < table->end_addr) { +@@ -213,7 +213,7 @@ + break; + } + } +- spin_unlock_irqrestore(&unwind_lock, flags); ++ raw_spin_unlock_irqrestore(&unwind_lock, flags); + } + + pr_debug("%s: idx = %p\n", __func__, idx); +@@ -530,9 +530,9 @@ + tab->begin_addr = text_addr; + tab->end_addr = text_addr + text_size; + +- spin_lock_irqsave(&unwind_lock, flags); ++ raw_spin_lock_irqsave(&unwind_lock, flags); + list_add_tail(&tab->list, &unwind_tables); +- spin_unlock_irqrestore(&unwind_lock, flags); ++ raw_spin_unlock_irqrestore(&unwind_lock, flags); + + return tab; + } +@@ -544,9 +544,9 @@ + if (!tab) + return; + +- spin_lock_irqsave(&unwind_lock, flags); ++ raw_spin_lock_irqsave(&unwind_lock, flags); + list_del(&tab->list); +- spin_unlock_irqrestore(&unwind_lock, flags); ++ raw_spin_unlock_irqrestore(&unwind_lock, flags); + + kfree(tab); + } +diff -Nur linux-3.18.14.orig/arch/arm/kvm/arm.c linux-3.18.14-rt/arch/arm/kvm/arm.c +--- linux-3.18.14.orig/arch/arm/kvm/arm.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/kvm/arm.c 2015-05-31 15:32:45.669635392 -0500 +@@ -455,9 +455,9 @@ + + static void vcpu_pause(struct kvm_vcpu *vcpu) + { +- wait_queue_head_t *wq = kvm_arch_vcpu_wq(vcpu); ++ struct swait_head *wq = kvm_arch_vcpu_wq(vcpu); + +- wait_event_interruptible(*wq, !vcpu->arch.pause); ++ swait_event_interruptible(*wq, !vcpu->arch.pause); + } + + static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu) +diff -Nur linux-3.18.14.orig/arch/arm/kvm/arm.c.orig linux-3.18.14-rt/arch/arm/kvm/arm.c.orig +--- linux-3.18.14.orig/arch/arm/kvm/arm.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/arch/arm/kvm/arm.c.orig 2015-05-20 10:04:50.000000000 -0500 +@@ -0,0 +1,1060 @@ ++/* ++ * Copyright (C) 2012 - Virtual Open Systems and Columbia University ++ * Author: Christoffer Dall ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License, version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CREATE_TRACE_POINTS ++#include "trace.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef REQUIRES_VIRT ++__asm__(".arch_extension virt"); ++#endif ++ ++static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page); ++static kvm_cpu_context_t __percpu *kvm_host_cpu_state; ++static unsigned long hyp_default_vectors; ++ ++/* Per-CPU variable containing the currently running vcpu. */ ++static DEFINE_PER_CPU(struct kvm_vcpu *, kvm_arm_running_vcpu); ++ ++/* The VMID used in the VTTBR */ ++static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1); ++static u8 kvm_next_vmid; ++static DEFINE_SPINLOCK(kvm_vmid_lock); ++ ++static bool vgic_present; ++ ++static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu) ++{ ++ BUG_ON(preemptible()); ++ __this_cpu_write(kvm_arm_running_vcpu, vcpu); ++} ++ ++/** ++ * kvm_arm_get_running_vcpu - get the vcpu running on the current CPU. ++ * Must be called from non-preemptible context ++ */ ++struct kvm_vcpu *kvm_arm_get_running_vcpu(void) ++{ ++ BUG_ON(preemptible()); ++ return __this_cpu_read(kvm_arm_running_vcpu); ++} ++ ++/** ++ * kvm_arm_get_running_vcpus - get the per-CPU array of currently running vcpus. ++ */ ++struct kvm_vcpu * __percpu *kvm_get_running_vcpus(void) ++{ ++ return &kvm_arm_running_vcpu; ++} ++ ++int kvm_arch_hardware_enable(void) ++{ ++ return 0; ++} ++ ++int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) ++{ ++ return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE; ++} ++ ++int kvm_arch_hardware_setup(void) ++{ ++ return 0; ++} ++ ++void kvm_arch_check_processor_compat(void *rtn) ++{ ++ *(int *)rtn = 0; ++} ++ ++ ++/** ++ * kvm_arch_init_vm - initializes a VM data structure ++ * @kvm: pointer to the KVM struct ++ */ ++int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) ++{ ++ int ret = 0; ++ ++ if (type) ++ return -EINVAL; ++ ++ ret = kvm_alloc_stage2_pgd(kvm); ++ if (ret) ++ goto out_fail_alloc; ++ ++ ret = create_hyp_mappings(kvm, kvm + 1); ++ if (ret) ++ goto out_free_stage2_pgd; ++ ++ kvm_timer_init(kvm); ++ ++ /* Mark the initial VMID generation invalid */ ++ kvm->arch.vmid_gen = 0; ++ ++ return ret; ++out_free_stage2_pgd: ++ kvm_free_stage2_pgd(kvm); ++out_fail_alloc: ++ return ret; ++} ++ ++int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) ++{ ++ return VM_FAULT_SIGBUS; ++} ++ ++ ++/** ++ * kvm_arch_destroy_vm - destroy the VM data structure ++ * @kvm: pointer to the KVM struct ++ */ ++void kvm_arch_destroy_vm(struct kvm *kvm) ++{ ++ int i; ++ ++ kvm_free_stage2_pgd(kvm); ++ ++ for (i = 0; i < KVM_MAX_VCPUS; ++i) { ++ if (kvm->vcpus[i]) { ++ kvm_arch_vcpu_free(kvm->vcpus[i]); ++ kvm->vcpus[i] = NULL; ++ } ++ } ++ ++ kvm_vgic_destroy(kvm); ++} ++ ++int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) ++{ ++ int r; ++ switch (ext) { ++ case KVM_CAP_IRQCHIP: ++ r = vgic_present; ++ break; ++ case KVM_CAP_DEVICE_CTRL: ++ case KVM_CAP_USER_MEMORY: ++ case KVM_CAP_SYNC_MMU: ++ case KVM_CAP_DESTROY_MEMORY_REGION_WORKS: ++ case KVM_CAP_ONE_REG: ++ case KVM_CAP_ARM_PSCI: ++ case KVM_CAP_ARM_PSCI_0_2: ++ case KVM_CAP_READONLY_MEM: ++ r = 1; ++ break; ++ case KVM_CAP_COALESCED_MMIO: ++ r = KVM_COALESCED_MMIO_PAGE_OFFSET; ++ break; ++ case KVM_CAP_ARM_SET_DEVICE_ADDR: ++ r = 1; ++ break; ++ case KVM_CAP_NR_VCPUS: ++ r = num_online_cpus(); ++ break; ++ case KVM_CAP_MAX_VCPUS: ++ r = KVM_MAX_VCPUS; ++ break; ++ default: ++ r = kvm_arch_dev_ioctl_check_extension(ext); ++ break; ++ } ++ return r; ++} ++ ++long kvm_arch_dev_ioctl(struct file *filp, ++ unsigned int ioctl, unsigned long arg) ++{ ++ return -EINVAL; ++} ++ ++ ++struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) ++{ ++ int err; ++ struct kvm_vcpu *vcpu; ++ ++ if (irqchip_in_kernel(kvm) && vgic_initialized(kvm)) { ++ err = -EBUSY; ++ goto out; ++ } ++ ++ vcpu = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL); ++ if (!vcpu) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ err = kvm_vcpu_init(vcpu, kvm, id); ++ if (err) ++ goto free_vcpu; ++ ++ err = create_hyp_mappings(vcpu, vcpu + 1); ++ if (err) ++ goto vcpu_uninit; ++ ++ return vcpu; ++vcpu_uninit: ++ kvm_vcpu_uninit(vcpu); ++free_vcpu: ++ kmem_cache_free(kvm_vcpu_cache, vcpu); ++out: ++ return ERR_PTR(err); ++} ++ ++int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) ++{ ++ return 0; ++} ++ ++void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu) ++{ ++ kvm_mmu_free_memory_caches(vcpu); ++ kvm_timer_vcpu_terminate(vcpu); ++ kvm_vgic_vcpu_destroy(vcpu); ++ kmem_cache_free(kvm_vcpu_cache, vcpu); ++} ++ ++void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) ++{ ++ kvm_arch_vcpu_free(vcpu); ++} ++ ++int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) ++{ ++ return 0; ++} ++ ++int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) ++{ ++ /* Force users to call KVM_ARM_VCPU_INIT */ ++ vcpu->arch.target = -1; ++ ++ /* Set up the timer */ ++ kvm_timer_vcpu_init(vcpu); ++ ++ return 0; ++} ++ ++void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) ++{ ++ vcpu->cpu = cpu; ++ vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state); ++ ++ /* ++ * Check whether this vcpu requires the cache to be flushed on ++ * this physical CPU. This is a consequence of doing dcache ++ * operations by set/way on this vcpu. We do it here to be in ++ * a non-preemptible section. ++ */ ++ if (cpumask_test_and_clear_cpu(cpu, &vcpu->arch.require_dcache_flush)) ++ flush_cache_all(); /* We'd really want v7_flush_dcache_all() */ ++ ++ kvm_arm_set_running_vcpu(vcpu); ++} ++ ++void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) ++{ ++ /* ++ * The arch-generic KVM code expects the cpu field of a vcpu to be -1 ++ * if the vcpu is no longer assigned to a cpu. This is used for the ++ * optimized make_all_cpus_request path. ++ */ ++ vcpu->cpu = -1; ++ ++ kvm_arm_set_running_vcpu(NULL); ++} ++ ++int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, ++ struct kvm_guest_debug *dbg) ++{ ++ return -EINVAL; ++} ++ ++ ++int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, ++ struct kvm_mp_state *mp_state) ++{ ++ return -EINVAL; ++} ++ ++int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, ++ struct kvm_mp_state *mp_state) ++{ ++ return -EINVAL; ++} ++ ++/** ++ * kvm_arch_vcpu_runnable - determine if the vcpu can be scheduled ++ * @v: The VCPU pointer ++ * ++ * If the guest CPU is not waiting for interrupts or an interrupt line is ++ * asserted, the CPU is by definition runnable. ++ */ ++int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) ++{ ++ return !!v->arch.irq_lines || kvm_vgic_vcpu_pending_irq(v); ++} ++ ++/* Just ensure a guest exit from a particular CPU */ ++static void exit_vm_noop(void *info) ++{ ++} ++ ++void force_vm_exit(const cpumask_t *mask) ++{ ++ smp_call_function_many(mask, exit_vm_noop, NULL, true); ++} ++ ++/** ++ * need_new_vmid_gen - check that the VMID is still valid ++ * @kvm: The VM's VMID to checkt ++ * ++ * return true if there is a new generation of VMIDs being used ++ * ++ * The hardware supports only 256 values with the value zero reserved for the ++ * host, so we check if an assigned value belongs to a previous generation, ++ * which which requires us to assign a new value. If we're the first to use a ++ * VMID for the new generation, we must flush necessary caches and TLBs on all ++ * CPUs. ++ */ ++static bool need_new_vmid_gen(struct kvm *kvm) ++{ ++ return unlikely(kvm->arch.vmid_gen != atomic64_read(&kvm_vmid_gen)); ++} ++ ++/** ++ * update_vttbr - Update the VTTBR with a valid VMID before the guest runs ++ * @kvm The guest that we are about to run ++ * ++ * Called from kvm_arch_vcpu_ioctl_run before entering the guest to ensure the ++ * VM has a valid VMID, otherwise assigns a new one and flushes corresponding ++ * caches and TLBs. ++ */ ++static void update_vttbr(struct kvm *kvm) ++{ ++ phys_addr_t pgd_phys; ++ u64 vmid; ++ ++ if (!need_new_vmid_gen(kvm)) ++ return; ++ ++ spin_lock(&kvm_vmid_lock); ++ ++ /* ++ * We need to re-check the vmid_gen here to ensure that if another vcpu ++ * already allocated a valid vmid for this vm, then this vcpu should ++ * use the same vmid. ++ */ ++ if (!need_new_vmid_gen(kvm)) { ++ spin_unlock(&kvm_vmid_lock); ++ return; ++ } ++ ++ /* First user of a new VMID generation? */ ++ if (unlikely(kvm_next_vmid == 0)) { ++ atomic64_inc(&kvm_vmid_gen); ++ kvm_next_vmid = 1; ++ ++ /* ++ * On SMP we know no other CPUs can use this CPU's or each ++ * other's VMID after force_vm_exit returns since the ++ * kvm_vmid_lock blocks them from reentry to the guest. ++ */ ++ force_vm_exit(cpu_all_mask); ++ /* ++ * Now broadcast TLB + ICACHE invalidation over the inner ++ * shareable domain to make sure all data structures are ++ * clean. ++ */ ++ kvm_call_hyp(__kvm_flush_vm_context); ++ } ++ ++ kvm->arch.vmid_gen = atomic64_read(&kvm_vmid_gen); ++ kvm->arch.vmid = kvm_next_vmid; ++ kvm_next_vmid++; ++ ++ /* update vttbr to be used with the new vmid */ ++ pgd_phys = virt_to_phys(kvm_get_hwpgd(kvm)); ++ BUG_ON(pgd_phys & ~VTTBR_BADDR_MASK); ++ vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK; ++ kvm->arch.vttbr = pgd_phys | vmid; ++ ++ spin_unlock(&kvm_vmid_lock); ++} ++ ++static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) ++{ ++ struct kvm *kvm = vcpu->kvm; ++ int ret; ++ ++ if (likely(vcpu->arch.has_run_once)) ++ return 0; ++ ++ vcpu->arch.has_run_once = true; ++ ++ /* ++ * Map the VGIC hardware resources before running a vcpu the first ++ * time on this VM. ++ */ ++ if (unlikely(!vgic_initialized(kvm))) { ++ ret = kvm_vgic_map_resources(kvm); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * Enable the arch timers only if we have an in-kernel VGIC ++ * and it has been properly initialized, since we cannot handle ++ * interrupts from the virtual timer with a userspace gic. ++ */ ++ if (irqchip_in_kernel(kvm) && vgic_initialized(kvm)) ++ kvm_timer_enable(kvm); ++ ++ return 0; ++} ++ ++static void vcpu_pause(struct kvm_vcpu *vcpu) ++{ ++ wait_queue_head_t *wq = kvm_arch_vcpu_wq(vcpu); ++ ++ wait_event_interruptible(*wq, !vcpu->arch.pause); ++} ++ ++static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu) ++{ ++ return vcpu->arch.target >= 0; ++} ++ ++/** ++ * kvm_arch_vcpu_ioctl_run - the main VCPU run function to execute guest code ++ * @vcpu: The VCPU pointer ++ * @run: The kvm_run structure pointer used for userspace state exchange ++ * ++ * This function is called through the VCPU_RUN ioctl called from user space. It ++ * will execute VM code in a loop until the time slice for the process is used ++ * or some emulation is needed from user space in which case the function will ++ * return with return value 0 and with the kvm_run structure filled in with the ++ * required data for the requested emulation. ++ */ ++int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) ++{ ++ int ret; ++ sigset_t sigsaved; ++ ++ if (unlikely(!kvm_vcpu_initialized(vcpu))) ++ return -ENOEXEC; ++ ++ ret = kvm_vcpu_first_run_init(vcpu); ++ if (ret) ++ return ret; ++ ++ if (run->exit_reason == KVM_EXIT_MMIO) { ++ ret = kvm_handle_mmio_return(vcpu, vcpu->run); ++ if (ret) ++ return ret; ++ } ++ ++ if (vcpu->sigset_active) ++ sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); ++ ++ ret = 1; ++ run->exit_reason = KVM_EXIT_UNKNOWN; ++ while (ret > 0) { ++ /* ++ * Check conditions before entering the guest ++ */ ++ cond_resched(); ++ ++ update_vttbr(vcpu->kvm); ++ ++ if (vcpu->arch.pause) ++ vcpu_pause(vcpu); ++ ++ kvm_vgic_flush_hwstate(vcpu); ++ kvm_timer_flush_hwstate(vcpu); ++ ++ local_irq_disable(); ++ ++ /* ++ * Re-check atomic conditions ++ */ ++ if (signal_pending(current)) { ++ ret = -EINTR; ++ run->exit_reason = KVM_EXIT_INTR; ++ } ++ ++ if (ret <= 0 || need_new_vmid_gen(vcpu->kvm)) { ++ local_irq_enable(); ++ kvm_timer_sync_hwstate(vcpu); ++ kvm_vgic_sync_hwstate(vcpu); ++ continue; ++ } ++ ++ /************************************************************** ++ * Enter the guest ++ */ ++ trace_kvm_entry(*vcpu_pc(vcpu)); ++ kvm_guest_enter(); ++ vcpu->mode = IN_GUEST_MODE; ++ ++ ret = kvm_call_hyp(__kvm_vcpu_run, vcpu); ++ ++ vcpu->mode = OUTSIDE_GUEST_MODE; ++ vcpu->arch.last_pcpu = smp_processor_id(); ++ kvm_guest_exit(); ++ trace_kvm_exit(*vcpu_pc(vcpu)); ++ /* ++ * We may have taken a host interrupt in HYP mode (ie ++ * while executing the guest). This interrupt is still ++ * pending, as we haven't serviced it yet! ++ * ++ * We're now back in SVC mode, with interrupts ++ * disabled. Enabling the interrupts now will have ++ * the effect of taking the interrupt again, in SVC ++ * mode this time. ++ */ ++ local_irq_enable(); ++ ++ /* ++ * Back from guest ++ *************************************************************/ ++ ++ kvm_timer_sync_hwstate(vcpu); ++ kvm_vgic_sync_hwstate(vcpu); ++ ++ ret = handle_exit(vcpu, run, ret); ++ } ++ ++ if (vcpu->sigset_active) ++ sigprocmask(SIG_SETMASK, &sigsaved, NULL); ++ return ret; ++} ++ ++static int vcpu_interrupt_line(struct kvm_vcpu *vcpu, int number, bool level) ++{ ++ int bit_index; ++ bool set; ++ unsigned long *ptr; ++ ++ if (number == KVM_ARM_IRQ_CPU_IRQ) ++ bit_index = __ffs(HCR_VI); ++ else /* KVM_ARM_IRQ_CPU_FIQ */ ++ bit_index = __ffs(HCR_VF); ++ ++ ptr = (unsigned long *)&vcpu->arch.irq_lines; ++ if (level) ++ set = test_and_set_bit(bit_index, ptr); ++ else ++ set = test_and_clear_bit(bit_index, ptr); ++ ++ /* ++ * If we didn't change anything, no need to wake up or kick other CPUs ++ */ ++ if (set == level) ++ return 0; ++ ++ /* ++ * The vcpu irq_lines field was updated, wake up sleeping VCPUs and ++ * trigger a world-switch round on the running physical CPU to set the ++ * virtual IRQ/FIQ fields in the HCR appropriately. ++ */ ++ kvm_vcpu_kick(vcpu); ++ ++ return 0; ++} ++ ++int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, ++ bool line_status) ++{ ++ u32 irq = irq_level->irq; ++ unsigned int irq_type, vcpu_idx, irq_num; ++ int nrcpus = atomic_read(&kvm->online_vcpus); ++ struct kvm_vcpu *vcpu = NULL; ++ bool level = irq_level->level; ++ ++ irq_type = (irq >> KVM_ARM_IRQ_TYPE_SHIFT) & KVM_ARM_IRQ_TYPE_MASK; ++ vcpu_idx = (irq >> KVM_ARM_IRQ_VCPU_SHIFT) & KVM_ARM_IRQ_VCPU_MASK; ++ irq_num = (irq >> KVM_ARM_IRQ_NUM_SHIFT) & KVM_ARM_IRQ_NUM_MASK; ++ ++ trace_kvm_irq_line(irq_type, vcpu_idx, irq_num, irq_level->level); ++ ++ switch (irq_type) { ++ case KVM_ARM_IRQ_TYPE_CPU: ++ if (irqchip_in_kernel(kvm)) ++ return -ENXIO; ++ ++ if (vcpu_idx >= nrcpus) ++ return -EINVAL; ++ ++ vcpu = kvm_get_vcpu(kvm, vcpu_idx); ++ if (!vcpu) ++ return -EINVAL; ++ ++ if (irq_num > KVM_ARM_IRQ_CPU_FIQ) ++ return -EINVAL; ++ ++ return vcpu_interrupt_line(vcpu, irq_num, level); ++ case KVM_ARM_IRQ_TYPE_PPI: ++ if (!irqchip_in_kernel(kvm)) ++ return -ENXIO; ++ ++ if (vcpu_idx >= nrcpus) ++ return -EINVAL; ++ ++ vcpu = kvm_get_vcpu(kvm, vcpu_idx); ++ if (!vcpu) ++ return -EINVAL; ++ ++ if (irq_num < VGIC_NR_SGIS || irq_num >= VGIC_NR_PRIVATE_IRQS) ++ return -EINVAL; ++ ++ return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level); ++ case KVM_ARM_IRQ_TYPE_SPI: ++ if (!irqchip_in_kernel(kvm)) ++ return -ENXIO; ++ ++ if (irq_num < VGIC_NR_PRIVATE_IRQS) ++ return -EINVAL; ++ ++ return kvm_vgic_inject_irq(kvm, 0, irq_num, level); ++ } ++ ++ return -EINVAL; ++} ++ ++static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu, ++ struct kvm_vcpu_init *init) ++{ ++ int ret; ++ ++ ret = kvm_vcpu_set_target(vcpu, init); ++ if (ret) ++ return ret; ++ ++ /* ++ * Ensure a rebooted VM will fault in RAM pages and detect if the ++ * guest MMU is turned off and flush the caches as needed. ++ */ ++ if (vcpu->arch.has_run_once) ++ stage2_unmap_vm(vcpu->kvm); ++ ++ vcpu_reset_hcr(vcpu); ++ ++ /* ++ * Handle the "start in power-off" case by marking the VCPU as paused. ++ */ ++ if (test_bit(KVM_ARM_VCPU_POWER_OFF, vcpu->arch.features)) ++ vcpu->arch.pause = true; ++ else ++ vcpu->arch.pause = false; ++ ++ return 0; ++} ++ ++long kvm_arch_vcpu_ioctl(struct file *filp, ++ unsigned int ioctl, unsigned long arg) ++{ ++ struct kvm_vcpu *vcpu = filp->private_data; ++ void __user *argp = (void __user *)arg; ++ ++ switch (ioctl) { ++ case KVM_ARM_VCPU_INIT: { ++ struct kvm_vcpu_init init; ++ ++ if (copy_from_user(&init, argp, sizeof(init))) ++ return -EFAULT; ++ ++ return kvm_arch_vcpu_ioctl_vcpu_init(vcpu, &init); ++ } ++ case KVM_SET_ONE_REG: ++ case KVM_GET_ONE_REG: { ++ struct kvm_one_reg reg; ++ ++ if (unlikely(!kvm_vcpu_initialized(vcpu))) ++ return -ENOEXEC; ++ ++ if (copy_from_user(®, argp, sizeof(reg))) ++ return -EFAULT; ++ if (ioctl == KVM_SET_ONE_REG) ++ return kvm_arm_set_reg(vcpu, ®); ++ else ++ return kvm_arm_get_reg(vcpu, ®); ++ } ++ case KVM_GET_REG_LIST: { ++ struct kvm_reg_list __user *user_list = argp; ++ struct kvm_reg_list reg_list; ++ unsigned n; ++ ++ if (unlikely(!kvm_vcpu_initialized(vcpu))) ++ return -ENOEXEC; ++ ++ if (copy_from_user(®_list, user_list, sizeof(reg_list))) ++ return -EFAULT; ++ n = reg_list.n; ++ reg_list.n = kvm_arm_num_regs(vcpu); ++ if (copy_to_user(user_list, ®_list, sizeof(reg_list))) ++ return -EFAULT; ++ if (n < reg_list.n) ++ return -E2BIG; ++ return kvm_arm_copy_reg_indices(vcpu, user_list->reg); ++ } ++ default: ++ return -EINVAL; ++ } ++} ++ ++int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) ++{ ++ return -EINVAL; ++} ++ ++static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm, ++ struct kvm_arm_device_addr *dev_addr) ++{ ++ unsigned long dev_id, type; ++ ++ dev_id = (dev_addr->id & KVM_ARM_DEVICE_ID_MASK) >> ++ KVM_ARM_DEVICE_ID_SHIFT; ++ type = (dev_addr->id & KVM_ARM_DEVICE_TYPE_MASK) >> ++ KVM_ARM_DEVICE_TYPE_SHIFT; ++ ++ switch (dev_id) { ++ case KVM_ARM_DEVICE_VGIC_V2: ++ if (!vgic_present) ++ return -ENXIO; ++ return kvm_vgic_addr(kvm, type, &dev_addr->addr, true); ++ default: ++ return -ENODEV; ++ } ++} ++ ++long kvm_arch_vm_ioctl(struct file *filp, ++ unsigned int ioctl, unsigned long arg) ++{ ++ struct kvm *kvm = filp->private_data; ++ void __user *argp = (void __user *)arg; ++ ++ switch (ioctl) { ++ case KVM_CREATE_IRQCHIP: { ++ if (vgic_present) ++ return kvm_vgic_create(kvm); ++ else ++ return -ENXIO; ++ } ++ case KVM_ARM_SET_DEVICE_ADDR: { ++ struct kvm_arm_device_addr dev_addr; ++ ++ if (copy_from_user(&dev_addr, argp, sizeof(dev_addr))) ++ return -EFAULT; ++ return kvm_vm_ioctl_set_device_addr(kvm, &dev_addr); ++ } ++ case KVM_ARM_PREFERRED_TARGET: { ++ int err; ++ struct kvm_vcpu_init init; ++ ++ err = kvm_vcpu_preferred_target(&init); ++ if (err) ++ return err; ++ ++ if (copy_to_user(argp, &init, sizeof(init))) ++ return -EFAULT; ++ ++ return 0; ++ } ++ default: ++ return -EINVAL; ++ } ++} ++ ++static void cpu_init_hyp_mode(void *dummy) ++{ ++ phys_addr_t boot_pgd_ptr; ++ phys_addr_t pgd_ptr; ++ unsigned long hyp_stack_ptr; ++ unsigned long stack_page; ++ unsigned long vector_ptr; ++ ++ /* Switch from the HYP stub to our own HYP init vector */ ++ __hyp_set_vectors(kvm_get_idmap_vector()); ++ ++ boot_pgd_ptr = kvm_mmu_get_boot_httbr(); ++ pgd_ptr = kvm_mmu_get_httbr(); ++ stack_page = __this_cpu_read(kvm_arm_hyp_stack_page); ++ hyp_stack_ptr = stack_page + PAGE_SIZE; ++ vector_ptr = (unsigned long)__kvm_hyp_vector; ++ ++ __cpu_init_hyp_mode(boot_pgd_ptr, pgd_ptr, hyp_stack_ptr, vector_ptr); ++} ++ ++static int hyp_init_cpu_notify(struct notifier_block *self, ++ unsigned long action, void *cpu) ++{ ++ switch (action) { ++ case CPU_STARTING: ++ case CPU_STARTING_FROZEN: ++ if (__hyp_get_vectors() == hyp_default_vectors) ++ cpu_init_hyp_mode(NULL); ++ break; ++ } ++ ++ return NOTIFY_OK; ++} ++ ++static struct notifier_block hyp_init_cpu_nb = { ++ .notifier_call = hyp_init_cpu_notify, ++}; ++ ++#ifdef CONFIG_CPU_PM ++static int hyp_init_cpu_pm_notifier(struct notifier_block *self, ++ unsigned long cmd, ++ void *v) ++{ ++ if (cmd == CPU_PM_EXIT && ++ __hyp_get_vectors() == hyp_default_vectors) { ++ cpu_init_hyp_mode(NULL); ++ return NOTIFY_OK; ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block hyp_init_cpu_pm_nb = { ++ .notifier_call = hyp_init_cpu_pm_notifier, ++}; ++ ++static void __init hyp_cpu_pm_init(void) ++{ ++ cpu_pm_register_notifier(&hyp_init_cpu_pm_nb); ++} ++#else ++static inline void hyp_cpu_pm_init(void) ++{ ++} ++#endif ++ ++/** ++ * Inits Hyp-mode on all online CPUs ++ */ ++static int init_hyp_mode(void) ++{ ++ int cpu; ++ int err = 0; ++ ++ /* ++ * Allocate Hyp PGD and setup Hyp identity mapping ++ */ ++ err = kvm_mmu_init(); ++ if (err) ++ goto out_err; ++ ++ /* ++ * It is probably enough to obtain the default on one ++ * CPU. It's unlikely to be different on the others. ++ */ ++ hyp_default_vectors = __hyp_get_vectors(); ++ ++ /* ++ * Allocate stack pages for Hypervisor-mode ++ */ ++ for_each_possible_cpu(cpu) { ++ unsigned long stack_page; ++ ++ stack_page = __get_free_page(GFP_KERNEL); ++ if (!stack_page) { ++ err = -ENOMEM; ++ goto out_free_stack_pages; ++ } ++ ++ per_cpu(kvm_arm_hyp_stack_page, cpu) = stack_page; ++ } ++ ++ /* ++ * Map the Hyp-code called directly from the host ++ */ ++ err = create_hyp_mappings(__kvm_hyp_code_start, __kvm_hyp_code_end); ++ if (err) { ++ kvm_err("Cannot map world-switch code\n"); ++ goto out_free_mappings; ++ } ++ ++ /* ++ * Map the Hyp stack pages ++ */ ++ for_each_possible_cpu(cpu) { ++ char *stack_page = (char *)per_cpu(kvm_arm_hyp_stack_page, cpu); ++ err = create_hyp_mappings(stack_page, stack_page + PAGE_SIZE); ++ ++ if (err) { ++ kvm_err("Cannot map hyp stack\n"); ++ goto out_free_mappings; ++ } ++ } ++ ++ /* ++ * Map the host CPU structures ++ */ ++ kvm_host_cpu_state = alloc_percpu(kvm_cpu_context_t); ++ if (!kvm_host_cpu_state) { ++ err = -ENOMEM; ++ kvm_err("Cannot allocate host CPU state\n"); ++ goto out_free_mappings; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ kvm_cpu_context_t *cpu_ctxt; ++ ++ cpu_ctxt = per_cpu_ptr(kvm_host_cpu_state, cpu); ++ err = create_hyp_mappings(cpu_ctxt, cpu_ctxt + 1); ++ ++ if (err) { ++ kvm_err("Cannot map host CPU state: %d\n", err); ++ goto out_free_context; ++ } ++ } ++ ++ /* ++ * Execute the init code on each CPU. ++ */ ++ on_each_cpu(cpu_init_hyp_mode, NULL, 1); ++ ++ /* ++ * Init HYP view of VGIC ++ */ ++ err = kvm_vgic_hyp_init(); ++ if (err) ++ goto out_free_context; ++ ++#ifdef CONFIG_KVM_ARM_VGIC ++ vgic_present = true; ++#endif ++ ++ /* ++ * Init HYP architected timer support ++ */ ++ err = kvm_timer_hyp_init(); ++ if (err) ++ goto out_free_mappings; ++ ++#ifndef CONFIG_HOTPLUG_CPU ++ free_boot_hyp_pgd(); ++#endif ++ ++ kvm_perf_init(); ++ ++ kvm_info("Hyp mode initialized successfully\n"); ++ ++ return 0; ++out_free_context: ++ free_percpu(kvm_host_cpu_state); ++out_free_mappings: ++ free_hyp_pgds(); ++out_free_stack_pages: ++ for_each_possible_cpu(cpu) ++ free_page(per_cpu(kvm_arm_hyp_stack_page, cpu)); ++out_err: ++ kvm_err("error initializing Hyp mode: %d\n", err); ++ return err; ++} ++ ++static void check_kvm_target_cpu(void *ret) ++{ ++ *(int *)ret = kvm_target_cpu(); ++} ++ ++/** ++ * Initialize Hyp-mode and memory mappings on all CPUs. ++ */ ++int kvm_arch_init(void *opaque) ++{ ++ int err; ++ int ret, cpu; ++ ++ if (!is_hyp_mode_available()) { ++ kvm_err("HYP mode not available\n"); ++ return -ENODEV; ++ } ++ ++ for_each_online_cpu(cpu) { ++ smp_call_function_single(cpu, check_kvm_target_cpu, &ret, 1); ++ if (ret < 0) { ++ kvm_err("Error, CPU %d not supported!\n", cpu); ++ return -ENODEV; ++ } ++ } ++ ++ cpu_notifier_register_begin(); ++ ++ err = init_hyp_mode(); ++ if (err) ++ goto out_err; ++ ++ err = __register_cpu_notifier(&hyp_init_cpu_nb); ++ if (err) { ++ kvm_err("Cannot register HYP init CPU notifier (%d)\n", err); ++ goto out_err; ++ } ++ ++ cpu_notifier_register_done(); ++ ++ hyp_cpu_pm_init(); ++ ++ kvm_coproc_table_init(); ++ return 0; ++out_err: ++ cpu_notifier_register_done(); ++ return err; ++} ++ ++/* NOP: Compiling as a module not supported */ ++void kvm_arch_exit(void) ++{ ++ kvm_perf_teardown(); ++} ++ ++static int arm_init(void) ++{ ++ int rc = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE); ++ return rc; ++} ++ ++module_init(arm_init); +diff -Nur linux-3.18.14.orig/arch/arm/kvm/psci.c linux-3.18.14-rt/arch/arm/kvm/psci.c +--- linux-3.18.14.orig/arch/arm/kvm/psci.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/kvm/psci.c 2015-05-31 15:32:45.673635392 -0500 +@@ -67,7 +67,7 @@ + { + struct kvm *kvm = source_vcpu->kvm; + struct kvm_vcpu *vcpu = NULL, *tmp; +- wait_queue_head_t *wq; ++ struct swait_head *wq; + unsigned long cpu_id; + unsigned long context_id; + unsigned long mpidr; +@@ -124,7 +124,7 @@ + smp_mb(); /* Make sure the above is visible */ + + wq = kvm_arch_vcpu_wq(vcpu); +- wake_up_interruptible(wq); ++ swait_wake_interruptible(wq); + + return PSCI_RET_SUCCESS; + } +diff -Nur linux-3.18.14.orig/arch/arm/kvm/psci.c.orig linux-3.18.14-rt/arch/arm/kvm/psci.c.orig +--- linux-3.18.14.orig/arch/arm/kvm/psci.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/arch/arm/kvm/psci.c.orig 2015-05-20 10:04:50.000000000 -0500 +@@ -0,0 +1,337 @@ ++/* ++ * Copyright (C) 2012 - ARM Ltd ++ * Author: Marc Zyngier ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* ++ * This is an implementation of the Power State Coordination Interface ++ * as described in ARM document number ARM DEN 0022A. ++ */ ++ ++#define AFFINITY_MASK(level) ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1) ++ ++static unsigned long psci_affinity_mask(unsigned long affinity_level) ++{ ++ if (affinity_level <= 3) ++ return MPIDR_HWID_BITMASK & AFFINITY_MASK(affinity_level); ++ ++ return 0; ++} ++ ++static unsigned long kvm_psci_vcpu_suspend(struct kvm_vcpu *vcpu) ++{ ++ /* ++ * NOTE: For simplicity, we make VCPU suspend emulation to be ++ * same-as WFI (Wait-for-interrupt) emulation. ++ * ++ * This means for KVM the wakeup events are interrupts and ++ * this is consistent with intended use of StateID as described ++ * in section 5.4.1 of PSCI v0.2 specification (ARM DEN 0022A). ++ * ++ * Further, we also treat power-down request to be same as ++ * stand-by request as-per section 5.4.2 clause 3 of PSCI v0.2 ++ * specification (ARM DEN 0022A). This means all suspend states ++ * for KVM will preserve the register state. ++ */ ++ kvm_vcpu_block(vcpu); ++ ++ return PSCI_RET_SUCCESS; ++} ++ ++static void kvm_psci_vcpu_off(struct kvm_vcpu *vcpu) ++{ ++ vcpu->arch.pause = true; ++} ++ ++static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) ++{ ++ struct kvm *kvm = source_vcpu->kvm; ++ struct kvm_vcpu *vcpu = NULL, *tmp; ++ wait_queue_head_t *wq; ++ unsigned long cpu_id; ++ unsigned long context_id; ++ unsigned long mpidr; ++ phys_addr_t target_pc; ++ int i; ++ ++ cpu_id = *vcpu_reg(source_vcpu, 1); ++ if (vcpu_mode_is_32bit(source_vcpu)) ++ cpu_id &= ~((u32) 0); ++ ++ kvm_for_each_vcpu(i, tmp, kvm) { ++ mpidr = kvm_vcpu_get_mpidr(tmp); ++ if ((mpidr & MPIDR_HWID_BITMASK) == (cpu_id & MPIDR_HWID_BITMASK)) { ++ vcpu = tmp; ++ break; ++ } ++ } ++ ++ /* ++ * Make sure the caller requested a valid CPU and that the CPU is ++ * turned off. ++ */ ++ if (!vcpu) ++ return PSCI_RET_INVALID_PARAMS; ++ if (!vcpu->arch.pause) { ++ if (kvm_psci_version(source_vcpu) != KVM_ARM_PSCI_0_1) ++ return PSCI_RET_ALREADY_ON; ++ else ++ return PSCI_RET_INVALID_PARAMS; ++ } ++ ++ target_pc = *vcpu_reg(source_vcpu, 2); ++ context_id = *vcpu_reg(source_vcpu, 3); ++ ++ kvm_reset_vcpu(vcpu); ++ ++ /* Gracefully handle Thumb2 entry point */ ++ if (vcpu_mode_is_32bit(vcpu) && (target_pc & 1)) { ++ target_pc &= ~((phys_addr_t) 1); ++ vcpu_set_thumb(vcpu); ++ } ++ ++ /* Propagate caller endianness */ ++ if (kvm_vcpu_is_be(source_vcpu)) ++ kvm_vcpu_set_be(vcpu); ++ ++ *vcpu_pc(vcpu) = target_pc; ++ /* ++ * NOTE: We always update r0 (or x0) because for PSCI v0.1 ++ * the general puspose registers are undefined upon CPU_ON. ++ */ ++ *vcpu_reg(vcpu, 0) = context_id; ++ vcpu->arch.pause = false; ++ smp_mb(); /* Make sure the above is visible */ ++ ++ wq = kvm_arch_vcpu_wq(vcpu); ++ wake_up_interruptible(wq); ++ ++ return PSCI_RET_SUCCESS; ++} ++ ++static unsigned long kvm_psci_vcpu_affinity_info(struct kvm_vcpu *vcpu) ++{ ++ int i; ++ unsigned long mpidr; ++ unsigned long target_affinity; ++ unsigned long target_affinity_mask; ++ unsigned long lowest_affinity_level; ++ struct kvm *kvm = vcpu->kvm; ++ struct kvm_vcpu *tmp; ++ ++ target_affinity = *vcpu_reg(vcpu, 1); ++ lowest_affinity_level = *vcpu_reg(vcpu, 2); ++ ++ /* Determine target affinity mask */ ++ target_affinity_mask = psci_affinity_mask(lowest_affinity_level); ++ if (!target_affinity_mask) ++ return PSCI_RET_INVALID_PARAMS; ++ ++ /* Ignore other bits of target affinity */ ++ target_affinity &= target_affinity_mask; ++ ++ /* ++ * If one or more VCPU matching target affinity are running ++ * then ON else OFF ++ */ ++ kvm_for_each_vcpu(i, tmp, kvm) { ++ mpidr = kvm_vcpu_get_mpidr(tmp); ++ if (((mpidr & target_affinity_mask) == target_affinity) && ++ !tmp->arch.pause) { ++ return PSCI_0_2_AFFINITY_LEVEL_ON; ++ } ++ } ++ ++ return PSCI_0_2_AFFINITY_LEVEL_OFF; ++} ++ ++static void kvm_prepare_system_event(struct kvm_vcpu *vcpu, u32 type) ++{ ++ int i; ++ struct kvm_vcpu *tmp; ++ ++ /* ++ * The KVM ABI specifies that a system event exit may call KVM_RUN ++ * again and may perform shutdown/reboot at a later time that when the ++ * actual request is made. Since we are implementing PSCI and a ++ * caller of PSCI reboot and shutdown expects that the system shuts ++ * down or reboots immediately, let's make sure that VCPUs are not run ++ * after this call is handled and before the VCPUs have been ++ * re-initialized. ++ */ ++ kvm_for_each_vcpu(i, tmp, vcpu->kvm) { ++ tmp->arch.pause = true; ++ kvm_vcpu_kick(tmp); ++ } ++ ++ memset(&vcpu->run->system_event, 0, sizeof(vcpu->run->system_event)); ++ vcpu->run->system_event.type = type; ++ vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT; ++} ++ ++static void kvm_psci_system_off(struct kvm_vcpu *vcpu) ++{ ++ kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_SHUTDOWN); ++} ++ ++static void kvm_psci_system_reset(struct kvm_vcpu *vcpu) ++{ ++ kvm_prepare_system_event(vcpu, KVM_SYSTEM_EVENT_RESET); ++} ++ ++int kvm_psci_version(struct kvm_vcpu *vcpu) ++{ ++ if (test_bit(KVM_ARM_VCPU_PSCI_0_2, vcpu->arch.features)) ++ return KVM_ARM_PSCI_0_2; ++ ++ return KVM_ARM_PSCI_0_1; ++} ++ ++static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu) ++{ ++ int ret = 1; ++ unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0); ++ unsigned long val; ++ ++ switch (psci_fn) { ++ case PSCI_0_2_FN_PSCI_VERSION: ++ /* ++ * Bits[31:16] = Major Version = 0 ++ * Bits[15:0] = Minor Version = 2 ++ */ ++ val = 2; ++ break; ++ case PSCI_0_2_FN_CPU_SUSPEND: ++ case PSCI_0_2_FN64_CPU_SUSPEND: ++ val = kvm_psci_vcpu_suspend(vcpu); ++ break; ++ case PSCI_0_2_FN_CPU_OFF: ++ kvm_psci_vcpu_off(vcpu); ++ val = PSCI_RET_SUCCESS; ++ break; ++ case PSCI_0_2_FN_CPU_ON: ++ case PSCI_0_2_FN64_CPU_ON: ++ val = kvm_psci_vcpu_on(vcpu); ++ break; ++ case PSCI_0_2_FN_AFFINITY_INFO: ++ case PSCI_0_2_FN64_AFFINITY_INFO: ++ val = kvm_psci_vcpu_affinity_info(vcpu); ++ break; ++ case PSCI_0_2_FN_MIGRATE: ++ case PSCI_0_2_FN64_MIGRATE: ++ val = PSCI_RET_NOT_SUPPORTED; ++ break; ++ case PSCI_0_2_FN_MIGRATE_INFO_TYPE: ++ /* ++ * Trusted OS is MP hence does not require migration ++ * or ++ * Trusted OS is not present ++ */ ++ val = PSCI_0_2_TOS_MP; ++ break; ++ case PSCI_0_2_FN_MIGRATE_INFO_UP_CPU: ++ case PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU: ++ val = PSCI_RET_NOT_SUPPORTED; ++ break; ++ case PSCI_0_2_FN_SYSTEM_OFF: ++ kvm_psci_system_off(vcpu); ++ /* ++ * We should'nt be going back to guest VCPU after ++ * receiving SYSTEM_OFF request. ++ * ++ * If user space accidently/deliberately resumes ++ * guest VCPU after SYSTEM_OFF request then guest ++ * VCPU should see internal failure from PSCI return ++ * value. To achieve this, we preload r0 (or x0) with ++ * PSCI return value INTERNAL_FAILURE. ++ */ ++ val = PSCI_RET_INTERNAL_FAILURE; ++ ret = 0; ++ break; ++ case PSCI_0_2_FN_SYSTEM_RESET: ++ kvm_psci_system_reset(vcpu); ++ /* ++ * Same reason as SYSTEM_OFF for preloading r0 (or x0) ++ * with PSCI return value INTERNAL_FAILURE. ++ */ ++ val = PSCI_RET_INTERNAL_FAILURE; ++ ret = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ *vcpu_reg(vcpu, 0) = val; ++ return ret; ++} ++ ++static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu) ++{ ++ unsigned long psci_fn = *vcpu_reg(vcpu, 0) & ~((u32) 0); ++ unsigned long val; ++ ++ switch (psci_fn) { ++ case KVM_PSCI_FN_CPU_OFF: ++ kvm_psci_vcpu_off(vcpu); ++ val = PSCI_RET_SUCCESS; ++ break; ++ case KVM_PSCI_FN_CPU_ON: ++ val = kvm_psci_vcpu_on(vcpu); ++ break; ++ case KVM_PSCI_FN_CPU_SUSPEND: ++ case KVM_PSCI_FN_MIGRATE: ++ val = PSCI_RET_NOT_SUPPORTED; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ *vcpu_reg(vcpu, 0) = val; ++ return 1; ++} ++ ++/** ++ * kvm_psci_call - handle PSCI call if r0 value is in range ++ * @vcpu: Pointer to the VCPU struct ++ * ++ * Handle PSCI calls from guests through traps from HVC instructions. ++ * The calling convention is similar to SMC calls to the secure world ++ * where the function number is placed in r0. ++ * ++ * This function returns: > 0 (success), 0 (success but exit to user ++ * space), and < 0 (errors) ++ * ++ * Errors: ++ * -EINVAL: Unrecognized PSCI function ++ */ ++int kvm_psci_call(struct kvm_vcpu *vcpu) ++{ ++ switch (kvm_psci_version(vcpu)) { ++ case KVM_ARM_PSCI_0_2: ++ return kvm_psci_0_2_call(vcpu); ++ case KVM_ARM_PSCI_0_1: ++ return kvm_psci_0_1_call(vcpu); ++ default: ++ return -EINVAL; ++ }; ++} +diff -Nur linux-3.18.14.orig/arch/arm/mach-at91/at91rm9200_time.c linux-3.18.14-rt/arch/arm/mach-at91/at91rm9200_time.c +--- linux-3.18.14.orig/arch/arm/mach-at91/at91rm9200_time.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mach-at91/at91rm9200_time.c 2015-05-31 15:32:45.673635392 -0500 +@@ -135,6 +135,7 @@ + break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: ++ remove_irq(NR_IRQS_LEGACY + AT91_ID_SYS, &at91rm9200_timer_irq); + case CLOCK_EVT_MODE_RESUME: + irqmask = 0; + break; +diff -Nur linux-3.18.14.orig/arch/arm/mach-exynos/platsmp.c linux-3.18.14-rt/arch/arm/mach-exynos/platsmp.c +--- linux-3.18.14.orig/arch/arm/mach-exynos/platsmp.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mach-exynos/platsmp.c 2015-05-31 15:32:45.673635392 -0500 +@@ -137,7 +137,7 @@ + return (void __iomem *)(S5P_VA_SCU); + } + +-static DEFINE_SPINLOCK(boot_lock); ++static DEFINE_RAW_SPINLOCK(boot_lock); + + static void exynos_secondary_init(unsigned int cpu) + { +@@ -150,8 +150,8 @@ + /* + * Synchronise with the boot thread. + */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); ++ raw_spin_lock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + } + + static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) +@@ -165,7 +165,7 @@ + * Set synchronisation state between this boot processor + * and the secondary one + */ +- spin_lock(&boot_lock); ++ raw_spin_lock(&boot_lock); + + /* + * The secondary processor is waiting to be released from +@@ -192,7 +192,7 @@ + + if (timeout == 0) { + printk(KERN_ERR "cpu1 power enable failed"); +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + return -ETIMEDOUT; + } + } +@@ -242,7 +242,7 @@ + * calibrations, then wait for it to finish + */ + fail: +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + + return pen_release != -1 ? ret : 0; + } +diff -Nur linux-3.18.14.orig/arch/arm/mach-hisi/platmcpm.c linux-3.18.14-rt/arch/arm/mach-hisi/platmcpm.c +--- linux-3.18.14.orig/arch/arm/mach-hisi/platmcpm.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mach-hisi/platmcpm.c 2015-05-31 15:32:45.677635392 -0500 +@@ -57,7 +57,7 @@ + + static void __iomem *sysctrl, *fabric; + static int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER]; +-static DEFINE_SPINLOCK(boot_lock); ++static DEFINE_RAW_SPINLOCK(boot_lock); + static u32 fabric_phys_addr; + /* + * [0]: bootwrapper physical address +@@ -104,7 +104,7 @@ + if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER) + return -EINVAL; + +- spin_lock_irq(&boot_lock); ++ raw_spin_lock_irq(&boot_lock); + + if (hip04_cpu_table[cluster][cpu]) + goto out; +@@ -133,7 +133,7 @@ + udelay(20); + out: + hip04_cpu_table[cluster][cpu]++; +- spin_unlock_irq(&boot_lock); ++ raw_spin_unlock_irq(&boot_lock); + + return 0; + } +@@ -149,7 +149,7 @@ + + __mcpm_cpu_going_down(cpu, cluster); + +- spin_lock(&boot_lock); ++ raw_spin_lock(&boot_lock); + BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP); + hip04_cpu_table[cluster][cpu]--; + if (hip04_cpu_table[cluster][cpu] == 1) { +@@ -162,7 +162,7 @@ + + last_man = hip04_cluster_is_down(cluster); + if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) { +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + /* Since it's Cortex A15, disable L2 prefetching. */ + asm volatile( + "mcr p15, 1, %0, c15, c0, 3 \n\t" +@@ -173,7 +173,7 @@ + hip04_set_snoop_filter(cluster, 0); + __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN); + } else { +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + v7_exit_coherency_flush(louis); + } + +@@ -192,7 +192,7 @@ + cpu >= HIP04_MAX_CPUS_PER_CLUSTER); + + count = TIMEOUT_MSEC / POLL_MSEC; +- spin_lock_irq(&boot_lock); ++ raw_spin_lock_irq(&boot_lock); + for (tries = 0; tries < count; tries++) { + if (hip04_cpu_table[cluster][cpu]) { + ret = -EBUSY; +@@ -202,10 +202,10 @@ + data = readl_relaxed(sysctrl + SC_CPU_RESET_STATUS(cluster)); + if (data & CORE_WFI_STATUS(cpu)) + break; +- spin_unlock_irq(&boot_lock); ++ raw_spin_unlock_irq(&boot_lock); + /* Wait for clean L2 when the whole cluster is down. */ + msleep(POLL_MSEC); +- spin_lock_irq(&boot_lock); ++ raw_spin_lock_irq(&boot_lock); + } + if (tries >= count) + goto err; +@@ -220,10 +220,10 @@ + } + if (tries >= count) + goto err; +- spin_unlock_irq(&boot_lock); ++ raw_spin_unlock_irq(&boot_lock); + return 0; + err: +- spin_unlock_irq(&boot_lock); ++ raw_spin_unlock_irq(&boot_lock); + return ret; + } + +@@ -235,10 +235,10 @@ + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + +- spin_lock(&boot_lock); ++ raw_spin_lock(&boot_lock); + if (!hip04_cpu_table[cluster][cpu]) + hip04_cpu_table[cluster][cpu] = 1; +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + } + + static void __naked hip04_mcpm_power_up_setup(unsigned int affinity_level) +diff -Nur linux-3.18.14.orig/arch/arm/mach-omap2/omap-smp.c linux-3.18.14-rt/arch/arm/mach-omap2/omap-smp.c +--- linux-3.18.14.orig/arch/arm/mach-omap2/omap-smp.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mach-omap2/omap-smp.c 2015-05-31 15:32:45.697635392 -0500 +@@ -43,7 +43,7 @@ + /* SCU base address */ + static void __iomem *scu_base; + +-static DEFINE_SPINLOCK(boot_lock); ++static DEFINE_RAW_SPINLOCK(boot_lock); + + void __iomem *omap4_get_scu_base(void) + { +@@ -74,8 +74,8 @@ + /* + * Synchronise with the boot thread. + */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); ++ raw_spin_lock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + } + + static int omap4_boot_secondary(unsigned int cpu, struct task_struct *idle) +@@ -89,7 +89,7 @@ + * Set synchronisation state between this boot processor + * and the secondary one + */ +- spin_lock(&boot_lock); ++ raw_spin_lock(&boot_lock); + + /* + * Update the AuxCoreBoot0 with boot state for secondary core. +@@ -166,7 +166,7 @@ + * Now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + + return 0; + } +diff -Nur linux-3.18.14.orig/arch/arm/mach-prima2/platsmp.c linux-3.18.14-rt/arch/arm/mach-prima2/platsmp.c +--- linux-3.18.14.orig/arch/arm/mach-prima2/platsmp.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mach-prima2/platsmp.c 2015-05-31 15:32:45.721635392 -0500 +@@ -23,7 +23,7 @@ + static void __iomem *scu_base; + static void __iomem *rsc_base; + +-static DEFINE_SPINLOCK(boot_lock); ++static DEFINE_RAW_SPINLOCK(boot_lock); + + static struct map_desc scu_io_desc __initdata = { + .length = SZ_4K, +@@ -56,8 +56,8 @@ + /* + * Synchronise with the boot thread. + */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); ++ raw_spin_lock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + } + + static struct of_device_id rsc_ids[] = { +@@ -95,7 +95,7 @@ + /* make sure write buffer is drained */ + mb(); + +- spin_lock(&boot_lock); ++ raw_spin_lock(&boot_lock); + + /* + * The secondary processor is waiting to be released from +@@ -127,7 +127,7 @@ + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + + return pen_release != -1 ? -ENOSYS : 0; + } +diff -Nur linux-3.18.14.orig/arch/arm/mach-qcom/platsmp.c linux-3.18.14-rt/arch/arm/mach-qcom/platsmp.c +--- linux-3.18.14.orig/arch/arm/mach-qcom/platsmp.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mach-qcom/platsmp.c 2015-05-31 15:32:45.741635391 -0500 +@@ -46,7 +46,7 @@ + + extern void secondary_startup(void); + +-static DEFINE_SPINLOCK(boot_lock); ++static DEFINE_RAW_SPINLOCK(boot_lock); + + #ifdef CONFIG_HOTPLUG_CPU + static void __ref qcom_cpu_die(unsigned int cpu) +@@ -60,8 +60,8 @@ + /* + * Synchronise with the boot thread. + */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); ++ raw_spin_lock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + } + + static int scss_release_secondary(unsigned int cpu) +@@ -284,7 +284,7 @@ + * set synchronisation state between this boot processor + * and the secondary one + */ +- spin_lock(&boot_lock); ++ raw_spin_lock(&boot_lock); + + /* + * Send the secondary CPU a soft interrupt, thereby causing +@@ -297,7 +297,7 @@ + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + + return ret; + } +diff -Nur linux-3.18.14.orig/arch/arm/mach-spear/platsmp.c linux-3.18.14-rt/arch/arm/mach-spear/platsmp.c +--- linux-3.18.14.orig/arch/arm/mach-spear/platsmp.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mach-spear/platsmp.c 2015-05-31 15:32:45.749635392 -0500 +@@ -32,7 +32,7 @@ + sync_cache_w(&pen_release); + } + +-static DEFINE_SPINLOCK(boot_lock); ++static DEFINE_RAW_SPINLOCK(boot_lock); + + static void __iomem *scu_base = IOMEM(VA_SCU_BASE); + +@@ -47,8 +47,8 @@ + /* + * Synchronise with the boot thread. + */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); ++ raw_spin_lock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + } + + static int spear13xx_boot_secondary(unsigned int cpu, struct task_struct *idle) +@@ -59,7 +59,7 @@ + * set synchronisation state between this boot processor + * and the secondary one + */ +- spin_lock(&boot_lock); ++ raw_spin_lock(&boot_lock); + + /* + * The secondary processor is waiting to be released from +@@ -84,7 +84,7 @@ + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + + return pen_release != -1 ? -ENOSYS : 0; + } +diff -Nur linux-3.18.14.orig/arch/arm/mach-sti/platsmp.c linux-3.18.14-rt/arch/arm/mach-sti/platsmp.c +--- linux-3.18.14.orig/arch/arm/mach-sti/platsmp.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mach-sti/platsmp.c 2015-05-31 15:32:45.765635392 -0500 +@@ -34,7 +34,7 @@ + sync_cache_w(&pen_release); + } + +-static DEFINE_SPINLOCK(boot_lock); ++static DEFINE_RAW_SPINLOCK(boot_lock); + + static void sti_secondary_init(unsigned int cpu) + { +@@ -49,8 +49,8 @@ + /* + * Synchronise with the boot thread. + */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); ++ raw_spin_lock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + } + + static int sti_boot_secondary(unsigned int cpu, struct task_struct *idle) +@@ -61,7 +61,7 @@ + * set synchronisation state between this boot processor + * and the secondary one + */ +- spin_lock(&boot_lock); ++ raw_spin_lock(&boot_lock); + + /* + * The secondary processor is waiting to be released from +@@ -92,7 +92,7 @@ + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + + return pen_release != -1 ? -ENOSYS : 0; + } +diff -Nur linux-3.18.14.orig/arch/arm/mach-ux500/platsmp.c linux-3.18.14-rt/arch/arm/mach-ux500/platsmp.c +--- linux-3.18.14.orig/arch/arm/mach-ux500/platsmp.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mach-ux500/platsmp.c 2015-05-31 15:32:45.793635391 -0500 +@@ -51,7 +51,7 @@ + return NULL; + } + +-static DEFINE_SPINLOCK(boot_lock); ++static DEFINE_RAW_SPINLOCK(boot_lock); + + static void ux500_secondary_init(unsigned int cpu) + { +@@ -64,8 +64,8 @@ + /* + * Synchronise with the boot thread. + */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); ++ raw_spin_lock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + } + + static int ux500_boot_secondary(unsigned int cpu, struct task_struct *idle) +@@ -76,7 +76,7 @@ + * set synchronisation state between this boot processor + * and the secondary one + */ +- spin_lock(&boot_lock); ++ raw_spin_lock(&boot_lock); + + /* + * The secondary processor is waiting to be released from +@@ -97,7 +97,7 @@ + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + + return pen_release != -1 ? -ENOSYS : 0; + } +diff -Nur linux-3.18.14.orig/arch/arm/mm/fault.c linux-3.18.14-rt/arch/arm/mm/fault.c +--- linux-3.18.14.orig/arch/arm/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mm/fault.c 2015-05-31 15:32:45.797635391 -0500 +@@ -277,7 +277,7 @@ + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto no_context; + + if (user_mode(regs)) +@@ -431,6 +431,9 @@ + if (addr < TASK_SIZE) + return do_page_fault(addr, fsr, regs); + ++ if (interrupts_enabled(regs)) ++ local_irq_enable(); ++ + if (user_mode(regs)) + goto bad_area; + +@@ -498,6 +501,9 @@ + static int + do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) + { ++ if (interrupts_enabled(regs)) ++ local_irq_enable(); ++ + do_bad_area(addr, fsr, regs); + return 0; + } +diff -Nur linux-3.18.14.orig/arch/arm/mm/highmem.c linux-3.18.14-rt/arch/arm/mm/highmem.c +--- linux-3.18.14.orig/arch/arm/mm/highmem.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/mm/highmem.c 2015-05-31 15:32:45.805635391 -0500 +@@ -53,6 +53,7 @@ + + void *kmap_atomic(struct page *page) + { ++ pte_t pte = mk_pte(page, kmap_prot); + unsigned int idx; + unsigned long vaddr; + void *kmap; +@@ -91,7 +92,10 @@ + * in place, so the contained TLB flush ensures the TLB is updated + * with the new mapping. + */ +- set_fixmap_pte(idx, mk_pte(page, kmap_prot)); ++#ifdef CONFIG_PREEMPT_RT_FULL ++ current->kmap_pte[type] = pte; ++#endif ++ set_fixmap_pte(idx, pte); + + return (void *)vaddr; + } +@@ -108,12 +112,15 @@ + + if (cache_is_vivt()) + __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); ++#ifdef CONFIG_PREEMPT_RT_FULL ++ current->kmap_pte[type] = __pte(0); ++#endif + #ifdef CONFIG_DEBUG_HIGHMEM + BUG_ON(vaddr != __fix_to_virt(idx)); +- set_fixmap_pte(idx, __pte(0)); + #else + (void) idx; /* to kill a warning */ + #endif ++ set_fixmap_pte(idx, __pte(0)); + kmap_atomic_idx_pop(); + } else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) { + /* this address was obtained through kmap_high_get() */ +@@ -125,6 +132,7 @@ + + void *kmap_atomic_pfn(unsigned long pfn) + { ++ pte_t pte = pfn_pte(pfn, kmap_prot); + unsigned long vaddr; + int idx, type; + struct page *page = pfn_to_page(pfn); +@@ -139,7 +147,10 @@ + #ifdef CONFIG_DEBUG_HIGHMEM + BUG_ON(!pte_none(*(fixmap_page_table + idx))); + #endif +- set_fixmap_pte(idx, pfn_pte(pfn, kmap_prot)); ++#ifdef CONFIG_PREEMPT_RT_FULL ++ current->kmap_pte[type] = pte; ++#endif ++ set_fixmap_pte(idx, pte); + + return (void *)vaddr; + } +@@ -153,3 +164,28 @@ + + return pte_page(get_fixmap_pte(vaddr)); + } ++ ++#if defined CONFIG_PREEMPT_RT_FULL ++void switch_kmaps(struct task_struct *prev_p, struct task_struct *next_p) ++{ ++ int i; ++ ++ /* ++ * Clear @prev's kmap_atomic mappings ++ */ ++ for (i = 0; i < prev_p->kmap_idx; i++) { ++ int idx = i + KM_TYPE_NR * smp_processor_id(); ++ ++ set_fixmap_pte(idx, __pte(0)); ++ } ++ /* ++ * Restore @next_p's kmap_atomic mappings ++ */ ++ for (i = 0; i < next_p->kmap_idx; i++) { ++ int idx = i + KM_TYPE_NR * smp_processor_id(); ++ ++ if (!pte_none(next_p->kmap_pte[i])) ++ set_fixmap_pte(idx, next_p->kmap_pte[i]); ++ } ++} ++#endif +diff -Nur linux-3.18.14.orig/arch/arm/plat-versatile/platsmp.c linux-3.18.14-rt/arch/arm/plat-versatile/platsmp.c +--- linux-3.18.14.orig/arch/arm/plat-versatile/platsmp.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm/plat-versatile/platsmp.c 2015-05-31 15:32:45.889635390 -0500 +@@ -30,7 +30,7 @@ + sync_cache_w(&pen_release); + } + +-static DEFINE_SPINLOCK(boot_lock); ++static DEFINE_RAW_SPINLOCK(boot_lock); + + void versatile_secondary_init(unsigned int cpu) + { +@@ -43,8 +43,8 @@ + /* + * Synchronise with the boot thread. + */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); ++ raw_spin_lock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + } + + int versatile_boot_secondary(unsigned int cpu, struct task_struct *idle) +@@ -55,7 +55,7 @@ + * Set synchronisation state between this boot processor + * and the secondary one + */ +- spin_lock(&boot_lock); ++ raw_spin_lock(&boot_lock); + + /* + * This is really belt and braces; we hold unintended secondary +@@ -85,7 +85,7 @@ + * now the secondary core is starting up let it run its + * calibrations, then wait for it to finish + */ +- spin_unlock(&boot_lock); ++ raw_spin_unlock(&boot_lock); + + return pen_release != -1 ? -ENOSYS : 0; + } +diff -Nur linux-3.18.14.orig/arch/arm64/include/asm/thread_info.h linux-3.18.14-rt/arch/arm64/include/asm/thread_info.h +--- linux-3.18.14.orig/arch/arm64/include/asm/thread_info.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm64/include/asm/thread_info.h 2015-05-31 15:32:45.925635390 -0500 +@@ -50,6 +50,7 @@ + struct exec_domain *exec_domain; /* execution domain */ + struct restart_block restart_block; + int preempt_count; /* 0 => preemptable, <0 => bug */ ++ int preempt_lazy_count; /* 0 => preemptable, <0 => bug */ + int cpu; /* cpu */ + }; + +@@ -108,6 +109,7 @@ + #define TIF_NEED_RESCHED 1 + #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ + #define TIF_FOREIGN_FPSTATE 3 /* CPU's FP state is not current's */ ++#define TIF_NEED_RESCHED_LAZY 4 + #define TIF_NOHZ 7 + #define TIF_SYSCALL_TRACE 8 + #define TIF_SYSCALL_AUDIT 9 +@@ -124,6 +126,7 @@ + #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) + #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) + #define _TIF_FOREIGN_FPSTATE (1 << TIF_FOREIGN_FPSTATE) ++#define _TIF_NEED_RESCHED_LAZY (1 << TIF_NEED_RESCHED_LAZY) + #define _TIF_NOHZ (1 << TIF_NOHZ) + #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) + #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) +diff -Nur linux-3.18.14.orig/arch/arm64/Kconfig linux-3.18.14-rt/arch/arm64/Kconfig +--- linux-3.18.14.orig/arch/arm64/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm64/Kconfig 2015-05-31 15:32:45.905635390 -0500 +@@ -59,8 +59,10 @@ + select HAVE_PERF_REGS + select HAVE_PERF_USER_STACK_DUMP + select HAVE_RCU_TABLE_FREE ++ select HAVE_PREEMPT_LAZY + select HAVE_SYSCALL_TRACEPOINTS + select IRQ_DOMAIN ++ select IRQ_FORCED_THREADING + select MODULES_USE_ELF_RELA + select NO_BOOTMEM + select OF +diff -Nur linux-3.18.14.orig/arch/arm64/kernel/asm-offsets.c linux-3.18.14-rt/arch/arm64/kernel/asm-offsets.c +--- linux-3.18.14.orig/arch/arm64/kernel/asm-offsets.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm64/kernel/asm-offsets.c 2015-05-31 15:32:45.925635390 -0500 +@@ -36,6 +36,7 @@ + BLANK(); + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); ++ DEFINE(TI_PREEMPT_LAZY, offsetof(struct thread_info, preempt_lazy_count)); + DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); + DEFINE(TI_TASK, offsetof(struct thread_info, task)); + DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain)); +diff -Nur linux-3.18.14.orig/arch/arm64/kernel/entry.S linux-3.18.14-rt/arch/arm64/kernel/entry.S +--- linux-3.18.14.orig/arch/arm64/kernel/entry.S 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm64/kernel/entry.S 2015-05-31 15:32:45.925635390 -0500 +@@ -367,11 +367,16 @@ + #ifdef CONFIG_PREEMPT + get_thread_info tsk + ldr w24, [tsk, #TI_PREEMPT] // get preempt count +- cbnz w24, 1f // preempt count != 0 ++ cbnz w24, 2f // preempt count != 0 + ldr x0, [tsk, #TI_FLAGS] // get flags +- tbz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling? +- bl el1_preempt ++ tbnz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling? ++ ++ ldr w24, [tsk, #TI_PREEMPT_LAZY] // get preempt lazy count ++ cbnz w24, 2f // preempt lazy count != 0 ++ tbz x0, #TIF_NEED_RESCHED_LAZY, 2f // needs rescheduling? + 1: ++ bl el1_preempt ++2: + #endif + #ifdef CONFIG_TRACE_IRQFLAGS + bl trace_hardirqs_on +@@ -385,6 +390,7 @@ + 1: bl preempt_schedule_irq // irq en/disable is done inside + ldr x0, [tsk, #TI_FLAGS] // get new tasks TI_FLAGS + tbnz x0, #TIF_NEED_RESCHED, 1b // needs rescheduling? ++ tbnz x0, #TIF_NEED_RESCHED_LAZY, 1b // needs rescheduling? + ret x24 + #endif + +@@ -621,6 +627,7 @@ + str x0, [sp, #S_X0] // returned x0 + work_pending: + tbnz x1, #TIF_NEED_RESCHED, work_resched ++ tbnz x1, #TIF_NEED_RESCHED_LAZY, work_resched + /* TIF_SIGPENDING, TIF_NOTIFY_RESUME or TIF_FOREIGN_FPSTATE case */ + ldr x2, [sp, #S_PSTATE] + mov x0, sp // 'regs' +diff -Nur linux-3.18.14.orig/arch/arm64/kernel/perf_event.c linux-3.18.14-rt/arch/arm64/kernel/perf_event.c +--- linux-3.18.14.orig/arch/arm64/kernel/perf_event.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/arm64/kernel/perf_event.c 2015-05-31 15:32:45.925635390 -0500 +@@ -461,7 +461,7 @@ + } + + err = request_irq(irq, armpmu->handle_irq, +- IRQF_NOBALANCING, ++ IRQF_NOBALANCING | IRQF_NO_THREAD, + "arm-pmu", armpmu); + if (err) { + pr_err("unable to request IRQ%d for ARM PMU counters\n", +diff -Nur linux-3.18.14.orig/arch/avr32/mm/fault.c linux-3.18.14-rt/arch/avr32/mm/fault.c +--- linux-3.18.14.orig/arch/avr32/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/avr32/mm/fault.c 2015-05-31 15:32:45.933635390 -0500 +@@ -81,7 +81,7 @@ + * If we're in an interrupt or have no user context, we must + * not take the fault... + */ +- if (in_atomic() || !mm || regs->sr & SYSREG_BIT(GM)) ++ if (!mm || regs->sr & SYSREG_BIT(GM) || pagefault_disabled()) + goto no_context; + + local_irq_enable(); +diff -Nur linux-3.18.14.orig/arch/cris/mm/fault.c linux-3.18.14-rt/arch/cris/mm/fault.c +--- linux-3.18.14.orig/arch/cris/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/cris/mm/fault.c 2015-05-31 15:32:45.945635390 -0500 +@@ -113,7 +113,7 @@ + * user context, we must not take the fault. + */ + +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto no_context; + + if (user_mode(regs)) +diff -Nur linux-3.18.14.orig/arch/frv/mm/fault.c linux-3.18.14-rt/arch/frv/mm/fault.c +--- linux-3.18.14.orig/arch/frv/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/frv/mm/fault.c 2015-05-31 15:32:45.953635390 -0500 +@@ -78,7 +78,7 @@ + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto no_context; + + if (user_mode(__frame)) +diff -Nur linux-3.18.14.orig/arch/ia64/mm/fault.c linux-3.18.14-rt/arch/ia64/mm/fault.c +--- linux-3.18.14.orig/arch/ia64/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/ia64/mm/fault.c 2015-05-31 15:32:45.961635389 -0500 +@@ -96,7 +96,7 @@ + /* + * If we're in an interrupt or have no user context, we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto no_context; + + #ifdef CONFIG_VIRTUAL_MEM_MAP +diff -Nur linux-3.18.14.orig/arch/Kconfig linux-3.18.14-rt/arch/Kconfig +--- linux-3.18.14.orig/arch/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/Kconfig 2015-05-31 15:32:45.501635394 -0500 +@@ -6,6 +6,7 @@ + tristate "OProfile system profiling" + depends on PROFILING + depends on HAVE_OPROFILE ++ depends on !PREEMPT_RT_FULL + select RING_BUFFER + select RING_BUFFER_ALLOW_SWAP + help +diff -Nur linux-3.18.14.orig/arch/m32r/mm/fault.c linux-3.18.14-rt/arch/m32r/mm/fault.c +--- linux-3.18.14.orig/arch/m32r/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/m32r/mm/fault.c 2015-05-31 15:32:45.985635389 -0500 +@@ -114,7 +114,7 @@ + * If we're in an interrupt or have no user context or are running in an + * atomic region then we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto bad_area_nosemaphore; + + if (error_code & ACE_USERMODE) +diff -Nur linux-3.18.14.orig/arch/m68k/mm/fault.c linux-3.18.14-rt/arch/m68k/mm/fault.c +--- linux-3.18.14.orig/arch/m68k/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/m68k/mm/fault.c 2015-05-31 15:32:45.985635389 -0500 +@@ -81,7 +81,7 @@ + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto no_context; + + if (user_mode(regs)) +diff -Nur linux-3.18.14.orig/arch/microblaze/mm/fault.c linux-3.18.14-rt/arch/microblaze/mm/fault.c +--- linux-3.18.14.orig/arch/microblaze/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/microblaze/mm/fault.c 2015-05-31 15:32:46.005635389 -0500 +@@ -107,7 +107,7 @@ + if ((error_code & 0x13) == 0x13 || (error_code & 0x11) == 0x11) + is_write = 0; + +- if (unlikely(in_atomic() || !mm)) { ++ if (unlikely(!mm || pagefault_disabled())) { + if (kernel_mode(regs)) + goto bad_area_nosemaphore; + +diff -Nur linux-3.18.14.orig/arch/mips/Kconfig linux-3.18.14-rt/arch/mips/Kconfig +--- linux-3.18.14.orig/arch/mips/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/mips/Kconfig 2015-05-31 15:32:46.033635389 -0500 +@@ -2196,7 +2196,7 @@ + # + config HIGHMEM + bool "High Memory Support" +- depends on 32BIT && CPU_SUPPORTS_HIGHMEM && SYS_SUPPORTS_HIGHMEM && !CPU_MIPS32_3_5_EVA ++ depends on 32BIT && CPU_SUPPORTS_HIGHMEM && SYS_SUPPORTS_HIGHMEM && !CPU_MIPS32_3_5_EVA && !PREEMPT_RT_FULL + + config CPU_SUPPORTS_HIGHMEM + bool +diff -Nur linux-3.18.14.orig/arch/mips/kernel/signal.c linux-3.18.14-rt/arch/mips/kernel/signal.c +--- linux-3.18.14.orig/arch/mips/kernel/signal.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/mips/kernel/signal.c 2015-05-31 15:32:46.057635389 -0500 +@@ -613,6 +613,7 @@ + __u32 thread_info_flags) + { + local_irq_enable(); ++ preempt_check_resched(); + + user_exit(); + +diff -Nur linux-3.18.14.orig/arch/mips/mm/fault.c linux-3.18.14-rt/arch/mips/mm/fault.c +--- linux-3.18.14.orig/arch/mips/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/mips/mm/fault.c 2015-05-31 15:32:46.069635388 -0500 +@@ -89,7 +89,7 @@ + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto bad_area_nosemaphore; + + if (user_mode(regs)) +diff -Nur linux-3.18.14.orig/arch/mips/mm/init.c linux-3.18.14-rt/arch/mips/mm/init.c +--- linux-3.18.14.orig/arch/mips/mm/init.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/mips/mm/init.c 2015-05-31 15:32:46.069635388 -0500 +@@ -90,7 +90,7 @@ + + BUG_ON(Page_dcache_dirty(page)); + +- pagefault_disable(); ++ raw_pagefault_disable(); + idx = (addr >> PAGE_SHIFT) & (FIX_N_COLOURS - 1); + idx += in_interrupt() ? FIX_N_COLOURS : 0; + vaddr = __fix_to_virt(FIX_CMAP_END - idx); +@@ -146,7 +146,7 @@ + tlbw_use_hazard(); + write_c0_entryhi(old_ctx); + local_irq_restore(flags); +- pagefault_enable(); ++ raw_pagefault_enable(); + } + + void copy_user_highpage(struct page *to, struct page *from, +diff -Nur linux-3.18.14.orig/arch/mn10300/mm/fault.c linux-3.18.14-rt/arch/mn10300/mm/fault.c +--- linux-3.18.14.orig/arch/mn10300/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/mn10300/mm/fault.c 2015-05-31 15:32:46.113635388 -0500 +@@ -168,7 +168,7 @@ + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto no_context; + + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) +diff -Nur linux-3.18.14.orig/arch/parisc/mm/fault.c linux-3.18.14-rt/arch/parisc/mm/fault.c +--- linux-3.18.14.orig/arch/parisc/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/parisc/mm/fault.c 2015-05-31 15:32:46.113635388 -0500 +@@ -207,7 +207,7 @@ + int fault; + unsigned int flags; + +- if (in_atomic()) ++ if (pagefault_disabled()) + goto no_context; + + tsk = current; +diff -Nur linux-3.18.14.orig/arch/powerpc/include/asm/kvm_host.h linux-3.18.14-rt/arch/powerpc/include/asm/kvm_host.h +--- linux-3.18.14.orig/arch/powerpc/include/asm/kvm_host.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/powerpc/include/asm/kvm_host.h 2015-05-31 15:32:46.145635388 -0500 +@@ -296,7 +296,7 @@ + u8 in_guest; + struct list_head runnable_threads; + spinlock_t lock; +- wait_queue_head_t wq; ++ struct swait_head wq; + u64 stolen_tb; + u64 preempt_tb; + struct kvm_vcpu *runner; +@@ -618,7 +618,7 @@ + u8 prodded; + u32 last_inst; + +- wait_queue_head_t *wqp; ++ struct swait_head *wqp; + struct kvmppc_vcore *vcore; + int ret; + int trap; +diff -Nur linux-3.18.14.orig/arch/powerpc/include/asm/thread_info.h linux-3.18.14-rt/arch/powerpc/include/asm/thread_info.h +--- linux-3.18.14.orig/arch/powerpc/include/asm/thread_info.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/powerpc/include/asm/thread_info.h 2015-05-31 15:32:46.165635388 -0500 +@@ -43,6 +43,8 @@ + int cpu; /* cpu we're on */ + int preempt_count; /* 0 => preemptable, + <0 => BUG */ ++ int preempt_lazy_count; /* 0 => preemptable, ++ <0 => BUG */ + struct restart_block restart_block; + unsigned long local_flags; /* private flags for thread */ + +@@ -88,8 +90,7 @@ + #define TIF_SYSCALL_TRACE 0 /* syscall trace active */ + #define TIF_SIGPENDING 1 /* signal pending */ + #define TIF_NEED_RESCHED 2 /* rescheduling necessary */ +-#define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling +- TIF_NEED_RESCHED */ ++#define TIF_NEED_RESCHED_LAZY 3 /* lazy rescheduling necessary */ + #define TIF_32BIT 4 /* 32 bit binary */ + #define TIF_RESTORE_TM 5 /* need to restore TM FP/VEC/VSX */ + #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ +@@ -107,6 +108,8 @@ + #if defined(CONFIG_PPC64) + #define TIF_ELF2ABI 18 /* function descriptors must die! */ + #endif ++#define TIF_POLLING_NRFLAG 19 /* true if poll_idle() is polling ++ TIF_NEED_RESCHED */ + + /* as above, but as bit values */ + #define _TIF_SYSCALL_TRACE (1<flags) + set_bits(irqtp->flags, &curtp->flags); + } ++#endif + + irq_hw_number_t virq_to_hw(unsigned int virq) + { +diff -Nur linux-3.18.14.orig/arch/powerpc/kernel/misc_32.S linux-3.18.14-rt/arch/powerpc/kernel/misc_32.S +--- linux-3.18.14.orig/arch/powerpc/kernel/misc_32.S 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/powerpc/kernel/misc_32.S 2015-05-31 15:32:46.261635387 -0500 +@@ -40,6 +40,7 @@ + * We store the saved ksp_limit in the unused part + * of the STACK_FRAME_OVERHEAD + */ ++#ifndef CONFIG_PREEMPT_RT_FULL + _GLOBAL(call_do_softirq) + mflr r0 + stw r0,4(r1) +@@ -56,6 +57,7 @@ + stw r10,THREAD+KSP_LIMIT(r2) + mtlr r0 + blr ++#endif + + /* + * void call_do_irq(struct pt_regs *regs, struct thread_info *irqtp); +diff -Nur linux-3.18.14.orig/arch/powerpc/kernel/misc_64.S linux-3.18.14-rt/arch/powerpc/kernel/misc_64.S +--- linux-3.18.14.orig/arch/powerpc/kernel/misc_64.S 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/powerpc/kernel/misc_64.S 2015-05-31 15:32:46.261635387 -0500 +@@ -29,6 +29,7 @@ + + .text + ++#ifndef CONFIG_PREEMPT_RT_FULL + _GLOBAL(call_do_softirq) + mflr r0 + std r0,16(r1) +@@ -39,6 +40,7 @@ + ld r0,16(r1) + mtlr r0 + blr ++#endif + + _GLOBAL(call_do_irq) + mflr r0 +diff -Nur linux-3.18.14.orig/arch/powerpc/kernel/time.c linux-3.18.14-rt/arch/powerpc/kernel/time.c +--- linux-3.18.14.orig/arch/powerpc/kernel/time.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/powerpc/kernel/time.c 2015-05-31 15:32:46.261635387 -0500 +@@ -424,7 +424,7 @@ + EXPORT_SYMBOL(profile_pc); + #endif + +-#ifdef CONFIG_IRQ_WORK ++#if defined(CONFIG_IRQ_WORK) + + /* + * 64-bit uses a byte in the PACA, 32-bit uses a per-cpu variable... +diff -Nur linux-3.18.14.orig/arch/powerpc/kvm/book3s_hv.c linux-3.18.14-rt/arch/powerpc/kvm/book3s_hv.c +--- linux-3.18.14.orig/arch/powerpc/kvm/book3s_hv.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/powerpc/kvm/book3s_hv.c 2015-05-31 15:32:46.301635387 -0500 +@@ -84,11 +84,11 @@ + { + int me; + int cpu = vcpu->cpu; +- wait_queue_head_t *wqp; ++ struct swait_head *wqp; + + wqp = kvm_arch_vcpu_wq(vcpu); +- if (waitqueue_active(wqp)) { +- wake_up_interruptible(wqp); ++ if (swaitqueue_active(wqp)) { ++ swait_wake_interruptible(wqp); + ++vcpu->stat.halt_wakeup; + } + +@@ -639,8 +639,8 @@ + tvcpu->arch.prodded = 1; + smp_mb(); + if (vcpu->arch.ceded) { +- if (waitqueue_active(&vcpu->wq)) { +- wake_up_interruptible(&vcpu->wq); ++ if (swaitqueue_active(&vcpu->wq)) { ++ swait_wake_interruptible(&vcpu->wq); + vcpu->stat.halt_wakeup++; + } + } +@@ -1357,7 +1357,7 @@ + + INIT_LIST_HEAD(&vcore->runnable_threads); + spin_lock_init(&vcore->lock); +- init_waitqueue_head(&vcore->wq); ++ init_swait_head(&vcore->wq); + vcore->preempt_tb = TB_NIL; + vcore->lpcr = kvm->arch.lpcr; + vcore->first_vcpuid = core * threads_per_subcore; +@@ -1826,13 +1826,13 @@ + */ + static void kvmppc_vcore_blocked(struct kvmppc_vcore *vc) + { +- DEFINE_WAIT(wait); ++ DEFINE_SWAITER(wait); + +- prepare_to_wait(&vc->wq, &wait, TASK_INTERRUPTIBLE); ++ swait_prepare(&vc->wq, &wait, TASK_INTERRUPTIBLE); + vc->vcore_state = VCORE_SLEEPING; + spin_unlock(&vc->lock); + schedule(); +- finish_wait(&vc->wq, &wait); ++ swait_finish(&vc->wq, &wait); + spin_lock(&vc->lock); + vc->vcore_state = VCORE_INACTIVE; + } +@@ -1873,7 +1873,7 @@ + kvmppc_create_dtl_entry(vcpu, vc); + kvmppc_start_thread(vcpu); + } else if (vc->vcore_state == VCORE_SLEEPING) { +- wake_up(&vc->wq); ++ swait_wake(&vc->wq); + } + + } +diff -Nur linux-3.18.14.orig/arch/powerpc/kvm/Kconfig linux-3.18.14-rt/arch/powerpc/kvm/Kconfig +--- linux-3.18.14.orig/arch/powerpc/kvm/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/powerpc/kvm/Kconfig 2015-05-31 15:32:46.281635387 -0500 +@@ -157,6 +157,7 @@ + config KVM_MPIC + bool "KVM in-kernel MPIC emulation" + depends on KVM && E500 ++ depends on !PREEMPT_RT_FULL + select HAVE_KVM_IRQCHIP + select HAVE_KVM_IRQFD + select HAVE_KVM_IRQ_ROUTING +diff -Nur linux-3.18.14.orig/arch/powerpc/mm/fault.c linux-3.18.14-rt/arch/powerpc/mm/fault.c +--- linux-3.18.14.orig/arch/powerpc/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/powerpc/mm/fault.c 2015-05-31 15:32:46.325635386 -0500 +@@ -273,7 +273,7 @@ + if (!arch_irq_disabled_regs(regs)) + local_irq_enable(); + +- if (in_atomic() || mm == NULL) { ++ if (in_atomic() || mm == NULL || pagefault_disabled()) { + if (!user_mode(regs)) { + rc = SIGSEGV; + goto bail; +diff -Nur linux-3.18.14.orig/arch/s390/include/asm/kvm_host.h linux-3.18.14-rt/arch/s390/include/asm/kvm_host.h +--- linux-3.18.14.orig/arch/s390/include/asm/kvm_host.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/s390/include/asm/kvm_host.h 2015-05-31 15:32:46.369635386 -0500 +@@ -311,7 +311,7 @@ + struct list_head list; + atomic_t active; + struct kvm_s390_float_interrupt *float_int; +- wait_queue_head_t *wq; ++ struct swait_head *wq; + atomic_t *cpuflags; + unsigned int action_bits; + }; +diff -Nur linux-3.18.14.orig/arch/s390/kvm/interrupt.c linux-3.18.14-rt/arch/s390/kvm/interrupt.c +--- linux-3.18.14.orig/arch/s390/kvm/interrupt.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/s390/kvm/interrupt.c 2015-05-31 15:32:46.385635386 -0500 +@@ -620,13 +620,13 @@ + + void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu) + { +- if (waitqueue_active(&vcpu->wq)) { ++ if (swaitqueue_active(&vcpu->wq)) { + /* + * The vcpu gave up the cpu voluntarily, mark it as a good + * yield-candidate. + */ + vcpu->preempted = true; +- wake_up_interruptible(&vcpu->wq); ++ swait_wake_interruptible(&vcpu->wq); + vcpu->stat.halt_wakeup++; + } + } +@@ -747,7 +747,7 @@ + spin_lock(&li->lock); + list_add(&inti->list, &li->list); + atomic_set(&li->active, 1); +- BUG_ON(waitqueue_active(li->wq)); ++ BUG_ON(swaitqueue_active(li->wq)); + spin_unlock(&li->lock); + return 0; + } +@@ -772,7 +772,7 @@ + spin_lock(&li->lock); + list_add(&inti->list, &li->list); + atomic_set(&li->active, 1); +- BUG_ON(waitqueue_active(li->wq)); ++ BUG_ON(swaitqueue_active(li->wq)); + spin_unlock(&li->lock); + return 0; + } +diff -Nur linux-3.18.14.orig/arch/s390/kvm/interrupt.c.orig linux-3.18.14-rt/arch/s390/kvm/interrupt.c.orig +--- linux-3.18.14.orig/arch/s390/kvm/interrupt.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/arch/s390/kvm/interrupt.c.orig 2015-05-20 10:04:50.000000000 -0500 +@@ -0,0 +1,1541 @@ ++/* ++ * handling kvm guest interrupts ++ * ++ * Copyright IBM Corp. 2008,2014 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License (version 2 only) ++ * as published by the Free Software Foundation. ++ * ++ * Author(s): Carsten Otte ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "kvm-s390.h" ++#include "gaccess.h" ++#include "trace-s390.h" ++ ++#define IOINT_SCHID_MASK 0x0000ffff ++#define IOINT_SSID_MASK 0x00030000 ++#define IOINT_CSSID_MASK 0x03fc0000 ++#define IOINT_AI_MASK 0x04000000 ++#define PFAULT_INIT 0x0600 ++ ++static int __must_check deliver_ckc_interrupt(struct kvm_vcpu *vcpu); ++ ++static int is_ioint(u64 type) ++{ ++ return ((type & 0xfffe0000u) != 0xfffe0000u); ++} ++ ++int psw_extint_disabled(struct kvm_vcpu *vcpu) ++{ ++ return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT); ++} ++ ++static int psw_ioint_disabled(struct kvm_vcpu *vcpu) ++{ ++ return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO); ++} ++ ++static int psw_mchk_disabled(struct kvm_vcpu *vcpu) ++{ ++ return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK); ++} ++ ++static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) ++{ ++ if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) || ++ (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO) || ++ (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT)) ++ return 0; ++ return 1; ++} ++ ++static int ckc_interrupts_enabled(struct kvm_vcpu *vcpu) ++{ ++ if (psw_extint_disabled(vcpu) || ++ !(vcpu->arch.sie_block->gcr[0] & 0x800ul)) ++ return 0; ++ if (guestdbg_enabled(vcpu) && guestdbg_sstep_enabled(vcpu)) ++ /* No timer interrupts when single stepping */ ++ return 0; ++ return 1; ++} ++ ++static u64 int_word_to_isc_bits(u32 int_word) ++{ ++ u8 isc = (int_word & 0x38000000) >> 27; ++ ++ return (0x80 >> isc) << 24; ++} ++ ++static int __must_check __interrupt_is_deliverable(struct kvm_vcpu *vcpu, ++ struct kvm_s390_interrupt_info *inti) ++{ ++ switch (inti->type) { ++ case KVM_S390_INT_EXTERNAL_CALL: ++ if (psw_extint_disabled(vcpu)) ++ return 0; ++ if (vcpu->arch.sie_block->gcr[0] & 0x2000ul) ++ return 1; ++ return 0; ++ case KVM_S390_INT_EMERGENCY: ++ if (psw_extint_disabled(vcpu)) ++ return 0; ++ if (vcpu->arch.sie_block->gcr[0] & 0x4000ul) ++ return 1; ++ return 0; ++ case KVM_S390_INT_CLOCK_COMP: ++ return ckc_interrupts_enabled(vcpu); ++ case KVM_S390_INT_CPU_TIMER: ++ if (psw_extint_disabled(vcpu)) ++ return 0; ++ if (vcpu->arch.sie_block->gcr[0] & 0x400ul) ++ return 1; ++ return 0; ++ case KVM_S390_INT_SERVICE: ++ case KVM_S390_INT_PFAULT_INIT: ++ case KVM_S390_INT_PFAULT_DONE: ++ case KVM_S390_INT_VIRTIO: ++ if (psw_extint_disabled(vcpu)) ++ return 0; ++ if (vcpu->arch.sie_block->gcr[0] & 0x200ul) ++ return 1; ++ return 0; ++ case KVM_S390_PROGRAM_INT: ++ case KVM_S390_SIGP_STOP: ++ case KVM_S390_SIGP_SET_PREFIX: ++ case KVM_S390_RESTART: ++ return 1; ++ case KVM_S390_MCHK: ++ if (psw_mchk_disabled(vcpu)) ++ return 0; ++ if (vcpu->arch.sie_block->gcr[14] & inti->mchk.cr14) ++ return 1; ++ return 0; ++ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: ++ if (psw_ioint_disabled(vcpu)) ++ return 0; ++ if (vcpu->arch.sie_block->gcr[6] & ++ int_word_to_isc_bits(inti->io.io_int_word)) ++ return 1; ++ return 0; ++ default: ++ printk(KERN_WARNING "illegal interrupt type %llx\n", ++ inti->type); ++ BUG(); ++ } ++ return 0; ++} ++ ++static void __set_cpu_idle(struct kvm_vcpu *vcpu) ++{ ++ atomic_set_mask(CPUSTAT_WAIT, &vcpu->arch.sie_block->cpuflags); ++ set_bit(vcpu->vcpu_id, vcpu->arch.local_int.float_int->idle_mask); ++} ++ ++static void __unset_cpu_idle(struct kvm_vcpu *vcpu) ++{ ++ atomic_clear_mask(CPUSTAT_WAIT, &vcpu->arch.sie_block->cpuflags); ++ clear_bit(vcpu->vcpu_id, vcpu->arch.local_int.float_int->idle_mask); ++} ++ ++static void __reset_intercept_indicators(struct kvm_vcpu *vcpu) ++{ ++ atomic_clear_mask(CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT, ++ &vcpu->arch.sie_block->cpuflags); ++ vcpu->arch.sie_block->lctl = 0x0000; ++ vcpu->arch.sie_block->ictl &= ~(ICTL_LPSW | ICTL_STCTL | ICTL_PINT); ++ ++ if (guestdbg_enabled(vcpu)) { ++ vcpu->arch.sie_block->lctl |= (LCTL_CR0 | LCTL_CR9 | ++ LCTL_CR10 | LCTL_CR11); ++ vcpu->arch.sie_block->ictl |= (ICTL_STCTL | ICTL_PINT); ++ } ++ ++ if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) ++ atomic_set_mask(CPUSTAT_STOP_INT, &vcpu->arch.sie_block->cpuflags); ++} ++ ++static void __set_cpuflag(struct kvm_vcpu *vcpu, u32 flag) ++{ ++ atomic_set_mask(flag, &vcpu->arch.sie_block->cpuflags); ++} ++ ++static void __set_intercept_indicator(struct kvm_vcpu *vcpu, ++ struct kvm_s390_interrupt_info *inti) ++{ ++ switch (inti->type) { ++ case KVM_S390_INT_EXTERNAL_CALL: ++ case KVM_S390_INT_EMERGENCY: ++ case KVM_S390_INT_SERVICE: ++ case KVM_S390_INT_PFAULT_INIT: ++ case KVM_S390_INT_PFAULT_DONE: ++ case KVM_S390_INT_VIRTIO: ++ case KVM_S390_INT_CLOCK_COMP: ++ case KVM_S390_INT_CPU_TIMER: ++ if (psw_extint_disabled(vcpu)) ++ __set_cpuflag(vcpu, CPUSTAT_EXT_INT); ++ else ++ vcpu->arch.sie_block->lctl |= LCTL_CR0; ++ break; ++ case KVM_S390_SIGP_STOP: ++ __set_cpuflag(vcpu, CPUSTAT_STOP_INT); ++ break; ++ case KVM_S390_MCHK: ++ if (psw_mchk_disabled(vcpu)) ++ vcpu->arch.sie_block->ictl |= ICTL_LPSW; ++ else ++ vcpu->arch.sie_block->lctl |= LCTL_CR14; ++ break; ++ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: ++ if (psw_ioint_disabled(vcpu)) ++ __set_cpuflag(vcpu, CPUSTAT_IO_INT); ++ else ++ vcpu->arch.sie_block->lctl |= LCTL_CR6; ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static u16 get_ilc(struct kvm_vcpu *vcpu) ++{ ++ const unsigned short table[] = { 2, 4, 4, 6 }; ++ ++ switch (vcpu->arch.sie_block->icptcode) { ++ case ICPT_INST: ++ case ICPT_INSTPROGI: ++ case ICPT_OPEREXC: ++ case ICPT_PARTEXEC: ++ case ICPT_IOINST: ++ /* last instruction only stored for these icptcodes */ ++ return table[vcpu->arch.sie_block->ipa >> 14]; ++ case ICPT_PROGI: ++ return vcpu->arch.sie_block->pgmilc; ++ default: ++ return 0; ++ } ++} ++ ++static int __must_check __deliver_prog_irq(struct kvm_vcpu *vcpu, ++ struct kvm_s390_pgm_info *pgm_info) ++{ ++ int rc = 0; ++ u16 ilc = get_ilc(vcpu); ++ ++ switch (pgm_info->code & ~PGM_PER) { ++ case PGM_AFX_TRANSLATION: ++ case PGM_ASX_TRANSLATION: ++ case PGM_EX_TRANSLATION: ++ case PGM_LFX_TRANSLATION: ++ case PGM_LSTE_SEQUENCE: ++ case PGM_LSX_TRANSLATION: ++ case PGM_LX_TRANSLATION: ++ case PGM_PRIMARY_AUTHORITY: ++ case PGM_SECONDARY_AUTHORITY: ++ case PGM_SPACE_SWITCH: ++ rc = put_guest_lc(vcpu, pgm_info->trans_exc_code, ++ (u64 *)__LC_TRANS_EXC_CODE); ++ break; ++ case PGM_ALEN_TRANSLATION: ++ case PGM_ALE_SEQUENCE: ++ case PGM_ASTE_INSTANCE: ++ case PGM_ASTE_SEQUENCE: ++ case PGM_ASTE_VALIDITY: ++ case PGM_EXTENDED_AUTHORITY: ++ rc = put_guest_lc(vcpu, pgm_info->exc_access_id, ++ (u8 *)__LC_EXC_ACCESS_ID); ++ break; ++ case PGM_ASCE_TYPE: ++ case PGM_PAGE_TRANSLATION: ++ case PGM_REGION_FIRST_TRANS: ++ case PGM_REGION_SECOND_TRANS: ++ case PGM_REGION_THIRD_TRANS: ++ case PGM_SEGMENT_TRANSLATION: ++ rc = put_guest_lc(vcpu, pgm_info->trans_exc_code, ++ (u64 *)__LC_TRANS_EXC_CODE); ++ rc |= put_guest_lc(vcpu, pgm_info->exc_access_id, ++ (u8 *)__LC_EXC_ACCESS_ID); ++ rc |= put_guest_lc(vcpu, pgm_info->op_access_id, ++ (u8 *)__LC_OP_ACCESS_ID); ++ break; ++ case PGM_MONITOR: ++ rc = put_guest_lc(vcpu, pgm_info->mon_class_nr, ++ (u16 *)__LC_MON_CLASS_NR); ++ rc |= put_guest_lc(vcpu, pgm_info->mon_code, ++ (u64 *)__LC_MON_CODE); ++ break; ++ case PGM_DATA: ++ rc = put_guest_lc(vcpu, pgm_info->data_exc_code, ++ (u32 *)__LC_DATA_EXC_CODE); ++ break; ++ case PGM_PROTECTION: ++ rc = put_guest_lc(vcpu, pgm_info->trans_exc_code, ++ (u64 *)__LC_TRANS_EXC_CODE); ++ rc |= put_guest_lc(vcpu, pgm_info->exc_access_id, ++ (u8 *)__LC_EXC_ACCESS_ID); ++ break; ++ } ++ ++ if (pgm_info->code & PGM_PER) { ++ rc |= put_guest_lc(vcpu, pgm_info->per_code, ++ (u8 *) __LC_PER_CODE); ++ rc |= put_guest_lc(vcpu, pgm_info->per_atmid, ++ (u8 *)__LC_PER_ATMID); ++ rc |= put_guest_lc(vcpu, pgm_info->per_address, ++ (u64 *) __LC_PER_ADDRESS); ++ rc |= put_guest_lc(vcpu, pgm_info->per_access_id, ++ (u8 *) __LC_PER_ACCESS_ID); ++ } ++ ++ rc |= put_guest_lc(vcpu, ilc, (u16 *) __LC_PGM_ILC); ++ rc |= put_guest_lc(vcpu, pgm_info->code, ++ (u16 *)__LC_PGM_INT_CODE); ++ rc |= write_guest_lc(vcpu, __LC_PGM_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_PGM_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ ++ return rc; ++} ++ ++static int __must_check __do_deliver_interrupt(struct kvm_vcpu *vcpu, ++ struct kvm_s390_interrupt_info *inti) ++{ ++ const unsigned short table[] = { 2, 4, 4, 6 }; ++ int rc = 0; ++ ++ switch (inti->type) { ++ case KVM_S390_INT_EMERGENCY: ++ VCPU_EVENT(vcpu, 4, "%s", "interrupt: sigp emerg"); ++ vcpu->stat.deliver_emergency_signal++; ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ inti->emerg.code, 0); ++ rc = put_guest_lc(vcpu, 0x1201, (u16 *)__LC_EXT_INT_CODE); ++ rc |= put_guest_lc(vcpu, inti->emerg.code, ++ (u16 *)__LC_EXT_CPU_ADDR); ++ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ break; ++ case KVM_S390_INT_EXTERNAL_CALL: ++ VCPU_EVENT(vcpu, 4, "%s", "interrupt: sigp ext call"); ++ vcpu->stat.deliver_external_call++; ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ inti->extcall.code, 0); ++ rc = put_guest_lc(vcpu, 0x1202, (u16 *)__LC_EXT_INT_CODE); ++ rc |= put_guest_lc(vcpu, inti->extcall.code, ++ (u16 *)__LC_EXT_CPU_ADDR); ++ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ break; ++ case KVM_S390_INT_CLOCK_COMP: ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ inti->ext.ext_params, 0); ++ rc = deliver_ckc_interrupt(vcpu); ++ break; ++ case KVM_S390_INT_CPU_TIMER: ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ inti->ext.ext_params, 0); ++ rc = put_guest_lc(vcpu, EXT_IRQ_CPU_TIMER, ++ (u16 *)__LC_EXT_INT_CODE); ++ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ rc |= put_guest_lc(vcpu, inti->ext.ext_params, ++ (u32 *)__LC_EXT_PARAMS); ++ break; ++ case KVM_S390_INT_SERVICE: ++ VCPU_EVENT(vcpu, 4, "interrupt: sclp parm:%x", ++ inti->ext.ext_params); ++ vcpu->stat.deliver_service_signal++; ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ inti->ext.ext_params, 0); ++ rc = put_guest_lc(vcpu, 0x2401, (u16 *)__LC_EXT_INT_CODE); ++ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ rc |= put_guest_lc(vcpu, inti->ext.ext_params, ++ (u32 *)__LC_EXT_PARAMS); ++ break; ++ case KVM_S390_INT_PFAULT_INIT: ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, 0, ++ inti->ext.ext_params2); ++ rc = put_guest_lc(vcpu, EXT_IRQ_CP_SERVICE, ++ (u16 *) __LC_EXT_INT_CODE); ++ rc |= put_guest_lc(vcpu, PFAULT_INIT, (u16 *) __LC_EXT_CPU_ADDR); ++ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ rc |= put_guest_lc(vcpu, inti->ext.ext_params2, ++ (u64 *) __LC_EXT_PARAMS2); ++ break; ++ case KVM_S390_INT_PFAULT_DONE: ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, 0, ++ inti->ext.ext_params2); ++ rc = put_guest_lc(vcpu, 0x2603, (u16 *)__LC_EXT_INT_CODE); ++ rc |= put_guest_lc(vcpu, 0x0680, (u16 *)__LC_EXT_CPU_ADDR); ++ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ rc |= put_guest_lc(vcpu, inti->ext.ext_params2, ++ (u64 *)__LC_EXT_PARAMS2); ++ break; ++ case KVM_S390_INT_VIRTIO: ++ VCPU_EVENT(vcpu, 4, "interrupt: virtio parm:%x,parm64:%llx", ++ inti->ext.ext_params, inti->ext.ext_params2); ++ vcpu->stat.deliver_virtio_interrupt++; ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ inti->ext.ext_params, ++ inti->ext.ext_params2); ++ rc = put_guest_lc(vcpu, 0x2603, (u16 *)__LC_EXT_INT_CODE); ++ rc |= put_guest_lc(vcpu, 0x0d00, (u16 *)__LC_EXT_CPU_ADDR); ++ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ rc |= put_guest_lc(vcpu, inti->ext.ext_params, ++ (u32 *)__LC_EXT_PARAMS); ++ rc |= put_guest_lc(vcpu, inti->ext.ext_params2, ++ (u64 *)__LC_EXT_PARAMS2); ++ break; ++ case KVM_S390_SIGP_STOP: ++ VCPU_EVENT(vcpu, 4, "%s", "interrupt: cpu stop"); ++ vcpu->stat.deliver_stop_signal++; ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ 0, 0); ++ __set_intercept_indicator(vcpu, inti); ++ break; ++ ++ case KVM_S390_SIGP_SET_PREFIX: ++ VCPU_EVENT(vcpu, 4, "interrupt: set prefix to %x", ++ inti->prefix.address); ++ vcpu->stat.deliver_prefix_signal++; ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ inti->prefix.address, 0); ++ kvm_s390_set_prefix(vcpu, inti->prefix.address); ++ break; ++ ++ case KVM_S390_RESTART: ++ VCPU_EVENT(vcpu, 4, "%s", "interrupt: cpu restart"); ++ vcpu->stat.deliver_restart_signal++; ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ 0, 0); ++ rc = write_guest_lc(vcpu, ++ offsetof(struct _lowcore, restart_old_psw), ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, offsetof(struct _lowcore, restart_psw), ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ break; ++ case KVM_S390_PROGRAM_INT: ++ VCPU_EVENT(vcpu, 4, "interrupt: pgm check code:%x, ilc:%x", ++ inti->pgm.code, ++ table[vcpu->arch.sie_block->ipa >> 14]); ++ vcpu->stat.deliver_program_int++; ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ inti->pgm.code, 0); ++ rc = __deliver_prog_irq(vcpu, &inti->pgm); ++ break; ++ ++ case KVM_S390_MCHK: ++ VCPU_EVENT(vcpu, 4, "interrupt: machine check mcic=%llx", ++ inti->mchk.mcic); ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ inti->mchk.cr14, ++ inti->mchk.mcic); ++ rc = kvm_s390_vcpu_store_status(vcpu, ++ KVM_S390_STORE_STATUS_PREFIXED); ++ rc |= put_guest_lc(vcpu, inti->mchk.mcic, (u64 *)__LC_MCCK_CODE); ++ rc |= write_guest_lc(vcpu, __LC_MCK_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_MCK_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ break; ++ ++ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: ++ { ++ __u32 param0 = ((__u32)inti->io.subchannel_id << 16) | ++ inti->io.subchannel_nr; ++ __u64 param1 = ((__u64)inti->io.io_int_parm << 32) | ++ inti->io.io_int_word; ++ VCPU_EVENT(vcpu, 4, "interrupt: I/O %llx", inti->type); ++ vcpu->stat.deliver_io_int++; ++ trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, ++ param0, param1); ++ rc = put_guest_lc(vcpu, inti->io.subchannel_id, ++ (u16 *)__LC_SUBCHANNEL_ID); ++ rc |= put_guest_lc(vcpu, inti->io.subchannel_nr, ++ (u16 *)__LC_SUBCHANNEL_NR); ++ rc |= put_guest_lc(vcpu, inti->io.io_int_parm, ++ (u32 *)__LC_IO_INT_PARM); ++ rc |= put_guest_lc(vcpu, inti->io.io_int_word, ++ (u32 *)__LC_IO_INT_WORD); ++ rc |= write_guest_lc(vcpu, __LC_IO_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_IO_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ break; ++ } ++ default: ++ BUG(); ++ } ++ ++ return rc; ++} ++ ++static int __must_check deliver_ckc_interrupt(struct kvm_vcpu *vcpu) ++{ ++ int rc; ++ ++ rc = put_guest_lc(vcpu, 0x1004, (u16 __user *)__LC_EXT_INT_CODE); ++ rc |= write_guest_lc(vcpu, __LC_EXT_OLD_PSW, ++ &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); ++ rc |= read_guest_lc(vcpu, __LC_EXT_NEW_PSW, ++ &vcpu->arch.sie_block->gpsw, ++ sizeof(psw_t)); ++ return rc; ++} ++ ++/* Check whether SIGP interpretation facility has an external call pending */ ++int kvm_s390_si_ext_call_pending(struct kvm_vcpu *vcpu) ++{ ++ atomic_t *sigp_ctrl = &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].ctrl; ++ ++ if (!psw_extint_disabled(vcpu) && ++ (vcpu->arch.sie_block->gcr[0] & 0x2000ul) && ++ (atomic_read(sigp_ctrl) & SIGP_CTRL_C) && ++ (atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_ECALL_PEND)) ++ return 1; ++ ++ return 0; ++} ++ ++int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu) ++{ ++ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; ++ struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int; ++ struct kvm_s390_interrupt_info *inti; ++ int rc = 0; ++ ++ if (atomic_read(&li->active)) { ++ spin_lock(&li->lock); ++ list_for_each_entry(inti, &li->list, list) ++ if (__interrupt_is_deliverable(vcpu, inti)) { ++ rc = 1; ++ break; ++ } ++ spin_unlock(&li->lock); ++ } ++ ++ if ((!rc) && atomic_read(&fi->active)) { ++ spin_lock(&fi->lock); ++ list_for_each_entry(inti, &fi->list, list) ++ if (__interrupt_is_deliverable(vcpu, inti)) { ++ rc = 1; ++ break; ++ } ++ spin_unlock(&fi->lock); ++ } ++ ++ if (!rc && kvm_cpu_has_pending_timer(vcpu)) ++ rc = 1; ++ ++ if (!rc && kvm_s390_si_ext_call_pending(vcpu)) ++ rc = 1; ++ ++ return rc; ++} ++ ++int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) ++{ ++ if (!(vcpu->arch.sie_block->ckc < ++ get_tod_clock_fast() + vcpu->arch.sie_block->epoch)) ++ return 0; ++ if (!ckc_interrupts_enabled(vcpu)) ++ return 0; ++ return 1; ++} ++ ++int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) ++{ ++ u64 now, sltime; ++ ++ vcpu->stat.exit_wait_state++; ++ ++ /* fast path */ ++ if (kvm_cpu_has_pending_timer(vcpu) || kvm_arch_vcpu_runnable(vcpu)) ++ return 0; ++ ++ if (psw_interrupts_disabled(vcpu)) { ++ VCPU_EVENT(vcpu, 3, "%s", "disabled wait"); ++ return -EOPNOTSUPP; /* disabled wait */ ++ } ++ ++ __set_cpu_idle(vcpu); ++ if (!ckc_interrupts_enabled(vcpu)) { ++ VCPU_EVENT(vcpu, 3, "%s", "enabled wait w/o timer"); ++ goto no_timer; ++ } ++ ++ now = get_tod_clock_fast() + vcpu->arch.sie_block->epoch; ++ sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now); ++ hrtimer_start(&vcpu->arch.ckc_timer, ktime_set (0, sltime) , HRTIMER_MODE_REL); ++ VCPU_EVENT(vcpu, 5, "enabled wait via clock comparator: %llx ns", sltime); ++no_timer: ++ srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx); ++ kvm_vcpu_block(vcpu); ++ __unset_cpu_idle(vcpu); ++ vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); ++ ++ hrtimer_cancel(&vcpu->arch.ckc_timer); ++ return 0; ++} ++ ++void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu) ++{ ++ if (waitqueue_active(&vcpu->wq)) { ++ /* ++ * The vcpu gave up the cpu voluntarily, mark it as a good ++ * yield-candidate. ++ */ ++ vcpu->preempted = true; ++ wake_up_interruptible(&vcpu->wq); ++ vcpu->stat.halt_wakeup++; ++ } ++} ++ ++enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer) ++{ ++ struct kvm_vcpu *vcpu; ++ u64 now, sltime; ++ ++ vcpu = container_of(timer, struct kvm_vcpu, arch.ckc_timer); ++ now = get_tod_clock_fast() + vcpu->arch.sie_block->epoch; ++ sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now); ++ ++ /* ++ * If the monotonic clock runs faster than the tod clock we might be ++ * woken up too early and have to go back to sleep to avoid deadlocks. ++ */ ++ if (vcpu->arch.sie_block->ckc > now && ++ hrtimer_forward_now(timer, ns_to_ktime(sltime))) ++ return HRTIMER_RESTART; ++ kvm_s390_vcpu_wakeup(vcpu); ++ return HRTIMER_NORESTART; ++} ++ ++void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu) ++{ ++ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; ++ struct kvm_s390_interrupt_info *n, *inti = NULL; ++ ++ spin_lock(&li->lock); ++ list_for_each_entry_safe(inti, n, &li->list, list) { ++ list_del(&inti->list); ++ kfree(inti); ++ } ++ atomic_set(&li->active, 0); ++ spin_unlock(&li->lock); ++ ++ /* clear pending external calls set by sigp interpretation facility */ ++ atomic_clear_mask(CPUSTAT_ECALL_PEND, &vcpu->arch.sie_block->cpuflags); ++ atomic_clear_mask(SIGP_CTRL_C, ++ &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].ctrl); ++} ++ ++int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) ++{ ++ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; ++ struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int; ++ struct kvm_s390_interrupt_info *n, *inti = NULL; ++ int deliver; ++ int rc = 0; ++ ++ __reset_intercept_indicators(vcpu); ++ if (atomic_read(&li->active)) { ++ do { ++ deliver = 0; ++ spin_lock(&li->lock); ++ list_for_each_entry_safe(inti, n, &li->list, list) { ++ if (__interrupt_is_deliverable(vcpu, inti)) { ++ list_del(&inti->list); ++ deliver = 1; ++ break; ++ } ++ __set_intercept_indicator(vcpu, inti); ++ } ++ if (list_empty(&li->list)) ++ atomic_set(&li->active, 0); ++ spin_unlock(&li->lock); ++ if (deliver) { ++ rc = __do_deliver_interrupt(vcpu, inti); ++ kfree(inti); ++ } ++ } while (!rc && deliver); ++ } ++ ++ if (!rc && kvm_cpu_has_pending_timer(vcpu)) ++ rc = deliver_ckc_interrupt(vcpu); ++ ++ if (!rc && atomic_read(&fi->active)) { ++ do { ++ deliver = 0; ++ spin_lock(&fi->lock); ++ list_for_each_entry_safe(inti, n, &fi->list, list) { ++ if (__interrupt_is_deliverable(vcpu, inti)) { ++ list_del(&inti->list); ++ fi->irq_count--; ++ deliver = 1; ++ break; ++ } ++ __set_intercept_indicator(vcpu, inti); ++ } ++ if (list_empty(&fi->list)) ++ atomic_set(&fi->active, 0); ++ spin_unlock(&fi->lock); ++ if (deliver) { ++ rc = __do_deliver_interrupt(vcpu, inti); ++ kfree(inti); ++ } ++ } while (!rc && deliver); ++ } ++ ++ return rc; ++} ++ ++int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code) ++{ ++ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; ++ struct kvm_s390_interrupt_info *inti; ++ ++ inti = kzalloc(sizeof(*inti), GFP_KERNEL); ++ if (!inti) ++ return -ENOMEM; ++ ++ inti->type = KVM_S390_PROGRAM_INT; ++ inti->pgm.code = code; ++ ++ VCPU_EVENT(vcpu, 3, "inject: program check %d (from kernel)", code); ++ trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, inti->type, code, 0, 1); ++ spin_lock(&li->lock); ++ list_add(&inti->list, &li->list); ++ atomic_set(&li->active, 1); ++ BUG_ON(waitqueue_active(li->wq)); ++ spin_unlock(&li->lock); ++ return 0; ++} ++ ++int kvm_s390_inject_prog_irq(struct kvm_vcpu *vcpu, ++ struct kvm_s390_pgm_info *pgm_info) ++{ ++ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; ++ struct kvm_s390_interrupt_info *inti; ++ ++ inti = kzalloc(sizeof(*inti), GFP_KERNEL); ++ if (!inti) ++ return -ENOMEM; ++ ++ VCPU_EVENT(vcpu, 3, "inject: prog irq %d (from kernel)", ++ pgm_info->code); ++ trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_PROGRAM_INT, ++ pgm_info->code, 0, 1); ++ ++ inti->type = KVM_S390_PROGRAM_INT; ++ memcpy(&inti->pgm, pgm_info, sizeof(inti->pgm)); ++ spin_lock(&li->lock); ++ list_add(&inti->list, &li->list); ++ atomic_set(&li->active, 1); ++ BUG_ON(waitqueue_active(li->wq)); ++ spin_unlock(&li->lock); ++ return 0; ++} ++ ++struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, ++ u64 cr6, u64 schid) ++{ ++ struct kvm_s390_float_interrupt *fi; ++ struct kvm_s390_interrupt_info *inti, *iter; ++ ++ if ((!schid && !cr6) || (schid && cr6)) ++ return NULL; ++ fi = &kvm->arch.float_int; ++ spin_lock(&fi->lock); ++ inti = NULL; ++ list_for_each_entry(iter, &fi->list, list) { ++ if (!is_ioint(iter->type)) ++ continue; ++ if (cr6 && ++ ((cr6 & int_word_to_isc_bits(iter->io.io_int_word)) == 0)) ++ continue; ++ if (schid) { ++ if (((schid & 0x00000000ffff0000) >> 16) != ++ iter->io.subchannel_id) ++ continue; ++ if ((schid & 0x000000000000ffff) != ++ iter->io.subchannel_nr) ++ continue; ++ } ++ inti = iter; ++ break; ++ } ++ if (inti) { ++ list_del_init(&inti->list); ++ fi->irq_count--; ++ } ++ if (list_empty(&fi->list)) ++ atomic_set(&fi->active, 0); ++ spin_unlock(&fi->lock); ++ return inti; ++} ++ ++static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) ++{ ++ struct kvm_s390_local_interrupt *li; ++ struct kvm_s390_float_interrupt *fi; ++ struct kvm_s390_interrupt_info *iter; ++ struct kvm_vcpu *dst_vcpu = NULL; ++ int sigcpu; ++ int rc = 0; ++ ++ fi = &kvm->arch.float_int; ++ spin_lock(&fi->lock); ++ if (fi->irq_count >= KVM_S390_MAX_FLOAT_IRQS) { ++ rc = -EINVAL; ++ goto unlock_fi; ++ } ++ fi->irq_count++; ++ if (!is_ioint(inti->type)) { ++ list_add_tail(&inti->list, &fi->list); ++ } else { ++ u64 isc_bits = int_word_to_isc_bits(inti->io.io_int_word); ++ ++ /* Keep I/O interrupts sorted in isc order. */ ++ list_for_each_entry(iter, &fi->list, list) { ++ if (!is_ioint(iter->type)) ++ continue; ++ if (int_word_to_isc_bits(iter->io.io_int_word) ++ <= isc_bits) ++ continue; ++ break; ++ } ++ list_add_tail(&inti->list, &iter->list); ++ } ++ atomic_set(&fi->active, 1); ++ if (atomic_read(&kvm->online_vcpus) == 0) ++ goto unlock_fi; ++ sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS); ++ if (sigcpu == KVM_MAX_VCPUS) { ++ do { ++ sigcpu = fi->next_rr_cpu++; ++ if (sigcpu == KVM_MAX_VCPUS) ++ sigcpu = fi->next_rr_cpu = 0; ++ } while (kvm_get_vcpu(kvm, sigcpu) == NULL); ++ } ++ dst_vcpu = kvm_get_vcpu(kvm, sigcpu); ++ li = &dst_vcpu->arch.local_int; ++ spin_lock(&li->lock); ++ atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); ++ spin_unlock(&li->lock); ++ kvm_s390_vcpu_wakeup(kvm_get_vcpu(kvm, sigcpu)); ++unlock_fi: ++ spin_unlock(&fi->lock); ++ return rc; ++} ++ ++int kvm_s390_inject_vm(struct kvm *kvm, ++ struct kvm_s390_interrupt *s390int) ++{ ++ struct kvm_s390_interrupt_info *inti; ++ int rc; ++ ++ inti = kzalloc(sizeof(*inti), GFP_KERNEL); ++ if (!inti) ++ return -ENOMEM; ++ ++ inti->type = s390int->type; ++ switch (inti->type) { ++ case KVM_S390_INT_VIRTIO: ++ VM_EVENT(kvm, 5, "inject: virtio parm:%x,parm64:%llx", ++ s390int->parm, s390int->parm64); ++ inti->ext.ext_params = s390int->parm; ++ inti->ext.ext_params2 = s390int->parm64; ++ break; ++ case KVM_S390_INT_SERVICE: ++ VM_EVENT(kvm, 5, "inject: sclp parm:%x", s390int->parm); ++ inti->ext.ext_params = s390int->parm; ++ break; ++ case KVM_S390_INT_PFAULT_DONE: ++ inti->type = s390int->type; ++ inti->ext.ext_params2 = s390int->parm64; ++ break; ++ case KVM_S390_MCHK: ++ VM_EVENT(kvm, 5, "inject: machine check parm64:%llx", ++ s390int->parm64); ++ inti->mchk.cr14 = s390int->parm; /* upper bits are not used */ ++ inti->mchk.mcic = s390int->parm64; ++ break; ++ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: ++ if (inti->type & IOINT_AI_MASK) ++ VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)"); ++ else ++ VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x", ++ s390int->type & IOINT_CSSID_MASK, ++ s390int->type & IOINT_SSID_MASK, ++ s390int->type & IOINT_SCHID_MASK); ++ inti->io.subchannel_id = s390int->parm >> 16; ++ inti->io.subchannel_nr = s390int->parm & 0x0000ffffu; ++ inti->io.io_int_parm = s390int->parm64 >> 32; ++ inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull; ++ break; ++ default: ++ kfree(inti); ++ return -EINVAL; ++ } ++ trace_kvm_s390_inject_vm(s390int->type, s390int->parm, s390int->parm64, ++ 2); ++ ++ rc = __inject_vm(kvm, inti); ++ if (rc) ++ kfree(inti); ++ return rc; ++} ++ ++int kvm_s390_reinject_io_int(struct kvm *kvm, ++ struct kvm_s390_interrupt_info *inti) ++{ ++ return __inject_vm(kvm, inti); ++} ++ ++int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, ++ struct kvm_s390_interrupt *s390int) ++{ ++ struct kvm_s390_local_interrupt *li; ++ struct kvm_s390_interrupt_info *inti; ++ ++ inti = kzalloc(sizeof(*inti), GFP_KERNEL); ++ if (!inti) ++ return -ENOMEM; ++ ++ switch (s390int->type) { ++ case KVM_S390_PROGRAM_INT: ++ if (s390int->parm & 0xffff0000) { ++ kfree(inti); ++ return -EINVAL; ++ } ++ inti->type = s390int->type; ++ inti->pgm.code = s390int->parm; ++ VCPU_EVENT(vcpu, 3, "inject: program check %d (from user)", ++ s390int->parm); ++ break; ++ case KVM_S390_SIGP_SET_PREFIX: ++ inti->prefix.address = s390int->parm; ++ inti->type = s390int->type; ++ VCPU_EVENT(vcpu, 3, "inject: set prefix to %x (from user)", ++ s390int->parm); ++ break; ++ case KVM_S390_SIGP_STOP: ++ case KVM_S390_RESTART: ++ case KVM_S390_INT_CLOCK_COMP: ++ case KVM_S390_INT_CPU_TIMER: ++ VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type); ++ inti->type = s390int->type; ++ break; ++ case KVM_S390_INT_EXTERNAL_CALL: ++ if (s390int->parm & 0xffff0000) { ++ kfree(inti); ++ return -EINVAL; ++ } ++ VCPU_EVENT(vcpu, 3, "inject: external call source-cpu:%u", ++ s390int->parm); ++ inti->type = s390int->type; ++ inti->extcall.code = s390int->parm; ++ break; ++ case KVM_S390_INT_EMERGENCY: ++ if (s390int->parm & 0xffff0000) { ++ kfree(inti); ++ return -EINVAL; ++ } ++ VCPU_EVENT(vcpu, 3, "inject: emergency %u\n", s390int->parm); ++ inti->type = s390int->type; ++ inti->emerg.code = s390int->parm; ++ break; ++ case KVM_S390_MCHK: ++ VCPU_EVENT(vcpu, 5, "inject: machine check parm64:%llx", ++ s390int->parm64); ++ inti->type = s390int->type; ++ inti->mchk.mcic = s390int->parm64; ++ break; ++ case KVM_S390_INT_PFAULT_INIT: ++ inti->type = s390int->type; ++ inti->ext.ext_params2 = s390int->parm64; ++ break; ++ case KVM_S390_INT_VIRTIO: ++ case KVM_S390_INT_SERVICE: ++ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: ++ default: ++ kfree(inti); ++ return -EINVAL; ++ } ++ trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, s390int->type, s390int->parm, ++ s390int->parm64, 2); ++ ++ li = &vcpu->arch.local_int; ++ spin_lock(&li->lock); ++ if (inti->type == KVM_S390_PROGRAM_INT) ++ list_add(&inti->list, &li->list); ++ else ++ list_add_tail(&inti->list, &li->list); ++ atomic_set(&li->active, 1); ++ if (inti->type == KVM_S390_SIGP_STOP) ++ li->action_bits |= ACTION_STOP_ON_STOP; ++ atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); ++ spin_unlock(&li->lock); ++ kvm_s390_vcpu_wakeup(vcpu); ++ return 0; ++} ++ ++void kvm_s390_clear_float_irqs(struct kvm *kvm) ++{ ++ struct kvm_s390_float_interrupt *fi; ++ struct kvm_s390_interrupt_info *n, *inti = NULL; ++ ++ fi = &kvm->arch.float_int; ++ spin_lock(&fi->lock); ++ list_for_each_entry_safe(inti, n, &fi->list, list) { ++ list_del(&inti->list); ++ kfree(inti); ++ } ++ fi->irq_count = 0; ++ atomic_set(&fi->active, 0); ++ spin_unlock(&fi->lock); ++} ++ ++static void inti_to_irq(struct kvm_s390_interrupt_info *inti, ++ struct kvm_s390_irq *irq) ++{ ++ irq->type = inti->type; ++ switch (inti->type) { ++ case KVM_S390_INT_PFAULT_INIT: ++ case KVM_S390_INT_PFAULT_DONE: ++ case KVM_S390_INT_VIRTIO: ++ case KVM_S390_INT_SERVICE: ++ irq->u.ext = inti->ext; ++ break; ++ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: ++ irq->u.io = inti->io; ++ break; ++ case KVM_S390_MCHK: ++ irq->u.mchk = inti->mchk; ++ break; ++ } ++} ++ ++static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len) ++{ ++ struct kvm_s390_interrupt_info *inti; ++ struct kvm_s390_float_interrupt *fi; ++ struct kvm_s390_irq *buf; ++ int max_irqs; ++ int ret = 0; ++ int n = 0; ++ ++ if (len > KVM_S390_FLIC_MAX_BUFFER || len == 0) ++ return -EINVAL; ++ ++ /* ++ * We are already using -ENOMEM to signal ++ * userspace it may retry with a bigger buffer, ++ * so we need to use something else for this case ++ */ ++ buf = vzalloc(len); ++ if (!buf) ++ return -ENOBUFS; ++ ++ max_irqs = len / sizeof(struct kvm_s390_irq); ++ ++ fi = &kvm->arch.float_int; ++ spin_lock(&fi->lock); ++ list_for_each_entry(inti, &fi->list, list) { ++ if (n == max_irqs) { ++ /* signal userspace to try again */ ++ ret = -ENOMEM; ++ break; ++ } ++ inti_to_irq(inti, &buf[n]); ++ n++; ++ } ++ spin_unlock(&fi->lock); ++ if (!ret && n > 0) { ++ if (copy_to_user(usrbuf, buf, sizeof(struct kvm_s390_irq) * n)) ++ ret = -EFAULT; ++ } ++ vfree(buf); ++ ++ return ret < 0 ? ret : n; ++} ++ ++static int flic_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr) ++{ ++ int r; ++ ++ switch (attr->group) { ++ case KVM_DEV_FLIC_GET_ALL_IRQS: ++ r = get_all_floating_irqs(dev->kvm, (u8 __user *) attr->addr, ++ attr->attr); ++ break; ++ default: ++ r = -EINVAL; ++ } ++ ++ return r; ++} ++ ++static inline int copy_irq_from_user(struct kvm_s390_interrupt_info *inti, ++ u64 addr) ++{ ++ struct kvm_s390_irq __user *uptr = (struct kvm_s390_irq __user *) addr; ++ void *target = NULL; ++ void __user *source; ++ u64 size; ++ ++ if (get_user(inti->type, (u64 __user *)addr)) ++ return -EFAULT; ++ ++ switch (inti->type) { ++ case KVM_S390_INT_PFAULT_INIT: ++ case KVM_S390_INT_PFAULT_DONE: ++ case KVM_S390_INT_VIRTIO: ++ case KVM_S390_INT_SERVICE: ++ target = (void *) &inti->ext; ++ source = &uptr->u.ext; ++ size = sizeof(inti->ext); ++ break; ++ case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: ++ target = (void *) &inti->io; ++ source = &uptr->u.io; ++ size = sizeof(inti->io); ++ break; ++ case KVM_S390_MCHK: ++ target = (void *) &inti->mchk; ++ source = &uptr->u.mchk; ++ size = sizeof(inti->mchk); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (copy_from_user(target, source, size)) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++static int enqueue_floating_irq(struct kvm_device *dev, ++ struct kvm_device_attr *attr) ++{ ++ struct kvm_s390_interrupt_info *inti = NULL; ++ int r = 0; ++ int len = attr->attr; ++ ++ if (len % sizeof(struct kvm_s390_irq) != 0) ++ return -EINVAL; ++ else if (len > KVM_S390_FLIC_MAX_BUFFER) ++ return -EINVAL; ++ ++ while (len >= sizeof(struct kvm_s390_irq)) { ++ inti = kzalloc(sizeof(*inti), GFP_KERNEL); ++ if (!inti) ++ return -ENOMEM; ++ ++ r = copy_irq_from_user(inti, attr->addr); ++ if (r) { ++ kfree(inti); ++ return r; ++ } ++ r = __inject_vm(dev->kvm, inti); ++ if (r) { ++ kfree(inti); ++ return r; ++ } ++ len -= sizeof(struct kvm_s390_irq); ++ attr->addr += sizeof(struct kvm_s390_irq); ++ } ++ ++ return r; ++} ++ ++static struct s390_io_adapter *get_io_adapter(struct kvm *kvm, unsigned int id) ++{ ++ if (id >= MAX_S390_IO_ADAPTERS) ++ return NULL; ++ return kvm->arch.adapters[id]; ++} ++ ++static int register_io_adapter(struct kvm_device *dev, ++ struct kvm_device_attr *attr) ++{ ++ struct s390_io_adapter *adapter; ++ struct kvm_s390_io_adapter adapter_info; ++ ++ if (copy_from_user(&adapter_info, ++ (void __user *)attr->addr, sizeof(adapter_info))) ++ return -EFAULT; ++ ++ if ((adapter_info.id >= MAX_S390_IO_ADAPTERS) || ++ (dev->kvm->arch.adapters[adapter_info.id] != NULL)) ++ return -EINVAL; ++ ++ adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); ++ if (!adapter) ++ return -ENOMEM; ++ ++ INIT_LIST_HEAD(&adapter->maps); ++ init_rwsem(&adapter->maps_lock); ++ atomic_set(&adapter->nr_maps, 0); ++ adapter->id = adapter_info.id; ++ adapter->isc = adapter_info.isc; ++ adapter->maskable = adapter_info.maskable; ++ adapter->masked = false; ++ adapter->swap = adapter_info.swap; ++ dev->kvm->arch.adapters[adapter->id] = adapter; ++ ++ return 0; ++} ++ ++int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked) ++{ ++ int ret; ++ struct s390_io_adapter *adapter = get_io_adapter(kvm, id); ++ ++ if (!adapter || !adapter->maskable) ++ return -EINVAL; ++ ret = adapter->masked; ++ adapter->masked = masked; ++ return ret; ++} ++ ++static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr) ++{ ++ struct s390_io_adapter *adapter = get_io_adapter(kvm, id); ++ struct s390_map_info *map; ++ int ret; ++ ++ if (!adapter || !addr) ++ return -EINVAL; ++ ++ map = kzalloc(sizeof(*map), GFP_KERNEL); ++ if (!map) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ INIT_LIST_HEAD(&map->list); ++ map->guest_addr = addr; ++ map->addr = gmap_translate(kvm->arch.gmap, addr); ++ if (map->addr == -EFAULT) { ++ ret = -EFAULT; ++ goto out; ++ } ++ ret = get_user_pages_fast(map->addr, 1, 1, &map->page); ++ if (ret < 0) ++ goto out; ++ BUG_ON(ret != 1); ++ down_write(&adapter->maps_lock); ++ if (atomic_inc_return(&adapter->nr_maps) < MAX_S390_ADAPTER_MAPS) { ++ list_add_tail(&map->list, &adapter->maps); ++ ret = 0; ++ } else { ++ put_page(map->page); ++ ret = -EINVAL; ++ } ++ up_write(&adapter->maps_lock); ++out: ++ if (ret) ++ kfree(map); ++ return ret; ++} ++ ++static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr) ++{ ++ struct s390_io_adapter *adapter = get_io_adapter(kvm, id); ++ struct s390_map_info *map, *tmp; ++ int found = 0; ++ ++ if (!adapter || !addr) ++ return -EINVAL; ++ ++ down_write(&adapter->maps_lock); ++ list_for_each_entry_safe(map, tmp, &adapter->maps, list) { ++ if (map->guest_addr == addr) { ++ found = 1; ++ atomic_dec(&adapter->nr_maps); ++ list_del(&map->list); ++ put_page(map->page); ++ kfree(map); ++ break; ++ } ++ } ++ up_write(&adapter->maps_lock); ++ ++ return found ? 0 : -EINVAL; ++} ++ ++void kvm_s390_destroy_adapters(struct kvm *kvm) ++{ ++ int i; ++ struct s390_map_info *map, *tmp; ++ ++ for (i = 0; i < MAX_S390_IO_ADAPTERS; i++) { ++ if (!kvm->arch.adapters[i]) ++ continue; ++ list_for_each_entry_safe(map, tmp, ++ &kvm->arch.adapters[i]->maps, list) { ++ list_del(&map->list); ++ put_page(map->page); ++ kfree(map); ++ } ++ kfree(kvm->arch.adapters[i]); ++ } ++} ++ ++static int modify_io_adapter(struct kvm_device *dev, ++ struct kvm_device_attr *attr) ++{ ++ struct kvm_s390_io_adapter_req req; ++ struct s390_io_adapter *adapter; ++ int ret; ++ ++ if (copy_from_user(&req, (void __user *)attr->addr, sizeof(req))) ++ return -EFAULT; ++ ++ adapter = get_io_adapter(dev->kvm, req.id); ++ if (!adapter) ++ return -EINVAL; ++ switch (req.type) { ++ case KVM_S390_IO_ADAPTER_MASK: ++ ret = kvm_s390_mask_adapter(dev->kvm, req.id, req.mask); ++ if (ret > 0) ++ ret = 0; ++ break; ++ case KVM_S390_IO_ADAPTER_MAP: ++ ret = kvm_s390_adapter_map(dev->kvm, req.id, req.addr); ++ break; ++ case KVM_S390_IO_ADAPTER_UNMAP: ++ ret = kvm_s390_adapter_unmap(dev->kvm, req.id, req.addr); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) ++{ ++ int r = 0; ++ unsigned int i; ++ struct kvm_vcpu *vcpu; ++ ++ switch (attr->group) { ++ case KVM_DEV_FLIC_ENQUEUE: ++ r = enqueue_floating_irq(dev, attr); ++ break; ++ case KVM_DEV_FLIC_CLEAR_IRQS: ++ kvm_s390_clear_float_irqs(dev->kvm); ++ break; ++ case KVM_DEV_FLIC_APF_ENABLE: ++ dev->kvm->arch.gmap->pfault_enabled = 1; ++ break; ++ case KVM_DEV_FLIC_APF_DISABLE_WAIT: ++ dev->kvm->arch.gmap->pfault_enabled = 0; ++ /* ++ * Make sure no async faults are in transition when ++ * clearing the queues. So we don't need to worry ++ * about late coming workers. ++ */ ++ synchronize_srcu(&dev->kvm->srcu); ++ kvm_for_each_vcpu(i, vcpu, dev->kvm) ++ kvm_clear_async_pf_completion_queue(vcpu); ++ break; ++ case KVM_DEV_FLIC_ADAPTER_REGISTER: ++ r = register_io_adapter(dev, attr); ++ break; ++ case KVM_DEV_FLIC_ADAPTER_MODIFY: ++ r = modify_io_adapter(dev, attr); ++ break; ++ default: ++ r = -EINVAL; ++ } ++ ++ return r; ++} ++ ++static int flic_create(struct kvm_device *dev, u32 type) ++{ ++ if (!dev) ++ return -EINVAL; ++ if (dev->kvm->arch.flic) ++ return -EINVAL; ++ dev->kvm->arch.flic = dev; ++ return 0; ++} ++ ++static void flic_destroy(struct kvm_device *dev) ++{ ++ dev->kvm->arch.flic = NULL; ++ kfree(dev); ++} ++ ++/* s390 floating irq controller (flic) */ ++struct kvm_device_ops kvm_flic_ops = { ++ .name = "kvm-flic", ++ .get_attr = flic_get_attr, ++ .set_attr = flic_set_attr, ++ .create = flic_create, ++ .destroy = flic_destroy, ++}; ++ ++static unsigned long get_ind_bit(__u64 addr, unsigned long bit_nr, bool swap) ++{ ++ unsigned long bit; ++ ++ bit = bit_nr + (addr % PAGE_SIZE) * 8; ++ ++ return swap ? (bit ^ (BITS_PER_LONG - 1)) : bit; ++} ++ ++static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter, ++ u64 addr) ++{ ++ struct s390_map_info *map; ++ ++ if (!adapter) ++ return NULL; ++ ++ list_for_each_entry(map, &adapter->maps, list) { ++ if (map->guest_addr == addr) ++ return map; ++ } ++ return NULL; ++} ++ ++static int adapter_indicators_set(struct kvm *kvm, ++ struct s390_io_adapter *adapter, ++ struct kvm_s390_adapter_int *adapter_int) ++{ ++ unsigned long bit; ++ int summary_set, idx; ++ struct s390_map_info *info; ++ void *map; ++ ++ info = get_map_info(adapter, adapter_int->ind_addr); ++ if (!info) ++ return -1; ++ map = page_address(info->page); ++ bit = get_ind_bit(info->addr, adapter_int->ind_offset, adapter->swap); ++ set_bit(bit, map); ++ idx = srcu_read_lock(&kvm->srcu); ++ mark_page_dirty(kvm, info->guest_addr >> PAGE_SHIFT); ++ set_page_dirty_lock(info->page); ++ info = get_map_info(adapter, adapter_int->summary_addr); ++ if (!info) { ++ srcu_read_unlock(&kvm->srcu, idx); ++ return -1; ++ } ++ map = page_address(info->page); ++ bit = get_ind_bit(info->addr, adapter_int->summary_offset, ++ adapter->swap); ++ summary_set = test_and_set_bit(bit, map); ++ mark_page_dirty(kvm, info->guest_addr >> PAGE_SHIFT); ++ set_page_dirty_lock(info->page); ++ srcu_read_unlock(&kvm->srcu, idx); ++ return summary_set ? 0 : 1; ++} ++ ++/* ++ * < 0 - not injected due to error ++ * = 0 - coalesced, summary indicator already active ++ * > 0 - injected interrupt ++ */ ++static int set_adapter_int(struct kvm_kernel_irq_routing_entry *e, ++ struct kvm *kvm, int irq_source_id, int level, ++ bool line_status) ++{ ++ int ret; ++ struct s390_io_adapter *adapter; ++ ++ /* We're only interested in the 0->1 transition. */ ++ if (!level) ++ return 0; ++ adapter = get_io_adapter(kvm, e->adapter.adapter_id); ++ if (!adapter) ++ return -1; ++ down_read(&adapter->maps_lock); ++ ret = adapter_indicators_set(kvm, adapter, &e->adapter); ++ up_read(&adapter->maps_lock); ++ if ((ret > 0) && !adapter->masked) { ++ struct kvm_s390_interrupt s390int = { ++ .type = KVM_S390_INT_IO(1, 0, 0, 0), ++ .parm = 0, ++ .parm64 = (adapter->isc << 27) | 0x80000000, ++ }; ++ ret = kvm_s390_inject_vm(kvm, &s390int); ++ if (ret == 0) ++ ret = 1; ++ } ++ return ret; ++} ++ ++int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e, ++ const struct kvm_irq_routing_entry *ue) ++{ ++ int ret; ++ ++ switch (ue->type) { ++ case KVM_IRQ_ROUTING_S390_ADAPTER: ++ e->set = set_adapter_int; ++ e->adapter.summary_addr = ue->u.adapter.summary_addr; ++ e->adapter.ind_addr = ue->u.adapter.ind_addr; ++ e->adapter.summary_offset = ue->u.adapter.summary_offset; ++ e->adapter.ind_offset = ue->u.adapter.ind_offset; ++ e->adapter.adapter_id = ue->u.adapter.adapter_id; ++ ret = 0; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, ++ int irq_source_id, int level, bool line_status) ++{ ++ return -EINVAL; ++} +diff -Nur linux-3.18.14.orig/arch/s390/mm/fault.c linux-3.18.14-rt/arch/s390/mm/fault.c +--- linux-3.18.14.orig/arch/s390/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/s390/mm/fault.c 2015-05-31 15:32:46.401635385 -0500 +@@ -435,7 +435,8 @@ + * user context. + */ + fault = VM_FAULT_BADCONTEXT; +- if (unlikely(!user_space_fault(regs) || in_atomic() || !mm)) ++ if (unlikely(!user_space_fault(regs) || !mm || ++ tsk->pagefault_disabled)) + goto out; + + address = trans_exc_code & __FAIL_ADDR_MASK; +diff -Nur linux-3.18.14.orig/arch/score/mm/fault.c linux-3.18.14-rt/arch/score/mm/fault.c +--- linux-3.18.14.orig/arch/score/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/score/mm/fault.c 2015-05-31 15:32:46.413635385 -0500 +@@ -73,7 +73,7 @@ + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto bad_area_nosemaphore; + + if (user_mode(regs)) +diff -Nur linux-3.18.14.orig/arch/sh/kernel/irq.c linux-3.18.14-rt/arch/sh/kernel/irq.c +--- linux-3.18.14.orig/arch/sh/kernel/irq.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/sh/kernel/irq.c 2015-05-31 15:32:46.429635385 -0500 +@@ -149,6 +149,7 @@ + hardirq_ctx[cpu] = NULL; + } + ++#ifndef CONFIG_PREEMPT_RT_FULL + void do_softirq_own_stack(void) + { + struct thread_info *curctx; +@@ -176,6 +177,7 @@ + "r5", "r6", "r7", "r8", "r9", "r15", "t", "pr" + ); + } ++#endif + #else + static inline void handle_one_irq(unsigned int irq) + { +diff -Nur linux-3.18.14.orig/arch/sh/mm/fault.c linux-3.18.14-rt/arch/sh/mm/fault.c +--- linux-3.18.14.orig/arch/sh/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/sh/mm/fault.c 2015-05-31 15:32:46.469635385 -0500 +@@ -440,7 +440,7 @@ + * If we're in an interrupt, have no user context or are running + * in an atomic region then we must not take the fault: + */ +- if (unlikely(in_atomic() || !mm)) { ++ if (unlikely(!mm || pagefault_disabled())) { + bad_area_nosemaphore(regs, error_code, address); + return; + } +diff -Nur linux-3.18.14.orig/arch/sparc/Kconfig linux-3.18.14-rt/arch/sparc/Kconfig +--- linux-3.18.14.orig/arch/sparc/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/sparc/Kconfig 2015-05-31 15:32:46.469635385 -0500 +@@ -182,12 +182,10 @@ + source kernel/Kconfig.hz + + config RWSEM_GENERIC_SPINLOCK +- bool +- default y if SPARC32 ++ def_bool PREEMPT_RT_FULL + + config RWSEM_XCHGADD_ALGORITHM +- bool +- default y if SPARC64 ++ def_bool !RWSEM_GENERIC_SPINLOCK && !PREEMPT_RT_FULL + + config GENERIC_HWEIGHT + bool +@@ -528,6 +526,10 @@ + + source "fs/Kconfig.binfmt" + ++config EARLY_PRINTK ++ bool ++ default y ++ + config COMPAT + bool + depends on SPARC64 +diff -Nur linux-3.18.14.orig/arch/sparc/kernel/irq_64.c linux-3.18.14-rt/arch/sparc/kernel/irq_64.c +--- linux-3.18.14.orig/arch/sparc/kernel/irq_64.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/sparc/kernel/irq_64.c 2015-05-31 15:32:46.477635385 -0500 +@@ -849,6 +849,7 @@ + set_irq_regs(old_regs); + } + ++#ifndef CONFIG_PREEMPT_RT_FULL + void do_softirq_own_stack(void) + { + void *orig_sp, *sp = softirq_stack[smp_processor_id()]; +@@ -863,6 +864,7 @@ + __asm__ __volatile__("mov %0, %%sp" + : : "r" (orig_sp)); + } ++#endif + + #ifdef CONFIG_HOTPLUG_CPU + void fixup_irqs(void) +diff -Nur linux-3.18.14.orig/arch/sparc/kernel/setup_32.c linux-3.18.14-rt/arch/sparc/kernel/setup_32.c +--- linux-3.18.14.orig/arch/sparc/kernel/setup_32.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/sparc/kernel/setup_32.c 2015-05-31 15:32:46.489635385 -0500 +@@ -309,6 +309,7 @@ + + boot_flags_init(*cmdline_p); + ++ early_console = &prom_early_console; + register_console(&prom_early_console); + + printk("ARCH: "); +diff -Nur linux-3.18.14.orig/arch/sparc/kernel/setup_64.c linux-3.18.14-rt/arch/sparc/kernel/setup_64.c +--- linux-3.18.14.orig/arch/sparc/kernel/setup_64.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/sparc/kernel/setup_64.c 2015-05-31 15:32:46.509635384 -0500 +@@ -563,6 +563,12 @@ + pause_patch(); + } + ++static inline void register_prom_console(void) ++{ ++ early_console = &prom_early_console; ++ register_console(&prom_early_console); ++} ++ + void __init setup_arch(char **cmdline_p) + { + /* Initialize PROM console and command line. */ +@@ -574,7 +580,7 @@ + #ifdef CONFIG_EARLYFB + if (btext_find_display()) + #endif +- register_console(&prom_early_console); ++ register_prom_console(); + + if (tlb_type == hypervisor) + printk("ARCH: SUN4V\n"); +diff -Nur linux-3.18.14.orig/arch/sparc/mm/fault_32.c linux-3.18.14-rt/arch/sparc/mm/fault_32.c +--- linux-3.18.14.orig/arch/sparc/mm/fault_32.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/sparc/mm/fault_32.c 2015-05-31 15:32:46.529635385 -0500 +@@ -196,7 +196,7 @@ + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto no_context; + + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); +diff -Nur linux-3.18.14.orig/arch/sparc/mm/fault_64.c linux-3.18.14-rt/arch/sparc/mm/fault_64.c +--- linux-3.18.14.orig/arch/sparc/mm/fault_64.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/sparc/mm/fault_64.c 2015-05-31 15:32:46.529635385 -0500 +@@ -330,7 +330,7 @@ + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ +- if (in_atomic() || !mm) ++ if (!mm || pagefault_disabled()) + goto intr_or_no_mm; + + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); +diff -Nur linux-3.18.14.orig/arch/tile/mm/fault.c linux-3.18.14-rt/arch/tile/mm/fault.c +--- linux-3.18.14.orig/arch/tile/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/tile/mm/fault.c 2015-05-31 15:32:46.533635385 -0500 +@@ -357,7 +357,7 @@ + * If we're in an interrupt, have no user context or are running in an + * atomic region then we must not take the fault. + */ +- if (in_atomic() || !mm) { ++ if (!mm || pagefault_disabled()) { + vma = NULL; /* happy compiler */ + goto bad_area_nosemaphore; + } +diff -Nur linux-3.18.14.orig/arch/um/kernel/trap.c linux-3.18.14-rt/arch/um/kernel/trap.c +--- linux-3.18.14.orig/arch/um/kernel/trap.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/um/kernel/trap.c 2015-05-31 15:32:46.537635384 -0500 +@@ -38,7 +38,7 @@ + * If the fault was during atomic operation, don't take the fault, just + * fail. + */ +- if (in_atomic()) ++ if (pagefault_disabled()) + goto out_nosemaphore; + + if (is_user) +diff -Nur linux-3.18.14.orig/arch/x86/crypto/aesni-intel_glue.c linux-3.18.14-rt/arch/x86/crypto/aesni-intel_glue.c +--- linux-3.18.14.orig/arch/x86/crypto/aesni-intel_glue.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/crypto/aesni-intel_glue.c 2015-05-31 15:32:46.569635384 -0500 +@@ -381,14 +381,14 @@ + err = blkcipher_walk_virt(desc, &walk); + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + +- kernel_fpu_begin(); + while ((nbytes = walk.nbytes)) { ++ kernel_fpu_begin(); + aesni_ecb_enc(ctx, walk.dst.virt.addr, walk.src.virt.addr, +- nbytes & AES_BLOCK_MASK); ++ nbytes & AES_BLOCK_MASK); ++ kernel_fpu_end(); + nbytes &= AES_BLOCK_SIZE - 1; + err = blkcipher_walk_done(desc, &walk, nbytes); + } +- kernel_fpu_end(); + + return err; + } +@@ -405,14 +405,14 @@ + err = blkcipher_walk_virt(desc, &walk); + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + +- kernel_fpu_begin(); + while ((nbytes = walk.nbytes)) { ++ kernel_fpu_begin(); + aesni_ecb_dec(ctx, walk.dst.virt.addr, walk.src.virt.addr, + nbytes & AES_BLOCK_MASK); ++ kernel_fpu_end(); + nbytes &= AES_BLOCK_SIZE - 1; + err = blkcipher_walk_done(desc, &walk, nbytes); + } +- kernel_fpu_end(); + + return err; + } +@@ -429,14 +429,14 @@ + err = blkcipher_walk_virt(desc, &walk); + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + +- kernel_fpu_begin(); + while ((nbytes = walk.nbytes)) { ++ kernel_fpu_begin(); + aesni_cbc_enc(ctx, walk.dst.virt.addr, walk.src.virt.addr, + nbytes & AES_BLOCK_MASK, walk.iv); ++ kernel_fpu_end(); + nbytes &= AES_BLOCK_SIZE - 1; + err = blkcipher_walk_done(desc, &walk, nbytes); + } +- kernel_fpu_end(); + + return err; + } +@@ -453,14 +453,14 @@ + err = blkcipher_walk_virt(desc, &walk); + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + +- kernel_fpu_begin(); + while ((nbytes = walk.nbytes)) { ++ kernel_fpu_begin(); + aesni_cbc_dec(ctx, walk.dst.virt.addr, walk.src.virt.addr, + nbytes & AES_BLOCK_MASK, walk.iv); ++ kernel_fpu_end(); + nbytes &= AES_BLOCK_SIZE - 1; + err = blkcipher_walk_done(desc, &walk, nbytes); + } +- kernel_fpu_end(); + + return err; + } +@@ -512,18 +512,20 @@ + err = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE); + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + +- kernel_fpu_begin(); + while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) { ++ kernel_fpu_begin(); + aesni_ctr_enc_tfm(ctx, walk.dst.virt.addr, walk.src.virt.addr, + nbytes & AES_BLOCK_MASK, walk.iv); ++ kernel_fpu_end(); + nbytes &= AES_BLOCK_SIZE - 1; + err = blkcipher_walk_done(desc, &walk, nbytes); + } + if (walk.nbytes) { ++ kernel_fpu_begin(); + ctr_crypt_final(ctx, &walk); ++ kernel_fpu_end(); + err = blkcipher_walk_done(desc, &walk, 0); + } +- kernel_fpu_end(); + + return err; + } +diff -Nur linux-3.18.14.orig/arch/x86/crypto/cast5_avx_glue.c linux-3.18.14-rt/arch/x86/crypto/cast5_avx_glue.c +--- linux-3.18.14.orig/arch/x86/crypto/cast5_avx_glue.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/crypto/cast5_avx_glue.c 2015-05-31 15:32:46.585635384 -0500 +@@ -60,7 +60,7 @@ + static int ecb_crypt(struct blkcipher_desc *desc, struct blkcipher_walk *walk, + bool enc) + { +- bool fpu_enabled = false; ++ bool fpu_enabled; + struct cast5_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + const unsigned int bsize = CAST5_BLOCK_SIZE; + unsigned int nbytes; +@@ -76,7 +76,7 @@ + u8 *wsrc = walk->src.virt.addr; + u8 *wdst = walk->dst.virt.addr; + +- fpu_enabled = cast5_fpu_begin(fpu_enabled, nbytes); ++ fpu_enabled = cast5_fpu_begin(false, nbytes); + + /* Process multi-block batch */ + if (nbytes >= bsize * CAST5_PARALLEL_BLOCKS) { +@@ -104,10 +104,9 @@ + } while (nbytes >= bsize); + + done: ++ cast5_fpu_end(fpu_enabled); + err = blkcipher_walk_done(desc, walk, nbytes); + } +- +- cast5_fpu_end(fpu_enabled); + return err; + } + +@@ -228,7 +227,7 @@ + static int cbc_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) + { +- bool fpu_enabled = false; ++ bool fpu_enabled; + struct blkcipher_walk walk; + int err; + +@@ -237,12 +236,11 @@ + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + + while ((nbytes = walk.nbytes)) { +- fpu_enabled = cast5_fpu_begin(fpu_enabled, nbytes); ++ fpu_enabled = cast5_fpu_begin(false, nbytes); + nbytes = __cbc_decrypt(desc, &walk); ++ cast5_fpu_end(fpu_enabled); + err = blkcipher_walk_done(desc, &walk, nbytes); + } +- +- cast5_fpu_end(fpu_enabled); + return err; + } + +@@ -312,7 +310,7 @@ + static int ctr_crypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) + { +- bool fpu_enabled = false; ++ bool fpu_enabled; + struct blkcipher_walk walk; + int err; + +@@ -321,13 +319,12 @@ + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + + while ((nbytes = walk.nbytes) >= CAST5_BLOCK_SIZE) { +- fpu_enabled = cast5_fpu_begin(fpu_enabled, nbytes); ++ fpu_enabled = cast5_fpu_begin(false, nbytes); + nbytes = __ctr_crypt(desc, &walk); ++ cast5_fpu_end(fpu_enabled); + err = blkcipher_walk_done(desc, &walk, nbytes); + } + +- cast5_fpu_end(fpu_enabled); +- + if (walk.nbytes) { + ctr_crypt_final(desc, &walk); + err = blkcipher_walk_done(desc, &walk, 0); +diff -Nur linux-3.18.14.orig/arch/x86/crypto/glue_helper.c linux-3.18.14-rt/arch/x86/crypto/glue_helper.c +--- linux-3.18.14.orig/arch/x86/crypto/glue_helper.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/crypto/glue_helper.c 2015-05-31 15:32:46.589635384 -0500 +@@ -39,7 +39,7 @@ + void *ctx = crypto_blkcipher_ctx(desc->tfm); + const unsigned int bsize = 128 / 8; + unsigned int nbytes, i, func_bytes; +- bool fpu_enabled = false; ++ bool fpu_enabled; + int err; + + err = blkcipher_walk_virt(desc, walk); +@@ -49,7 +49,7 @@ + u8 *wdst = walk->dst.virt.addr; + + fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit, +- desc, fpu_enabled, nbytes); ++ desc, false, nbytes); + + for (i = 0; i < gctx->num_funcs; i++) { + func_bytes = bsize * gctx->funcs[i].num_blocks; +@@ -71,10 +71,10 @@ + } + + done: ++ glue_fpu_end(fpu_enabled); + err = blkcipher_walk_done(desc, walk, nbytes); + } + +- glue_fpu_end(fpu_enabled); + return err; + } + +@@ -194,7 +194,7 @@ + struct scatterlist *src, unsigned int nbytes) + { + const unsigned int bsize = 128 / 8; +- bool fpu_enabled = false; ++ bool fpu_enabled; + struct blkcipher_walk walk; + int err; + +@@ -203,12 +203,12 @@ + + while ((nbytes = walk.nbytes)) { + fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit, +- desc, fpu_enabled, nbytes); ++ desc, false, nbytes); + nbytes = __glue_cbc_decrypt_128bit(gctx, desc, &walk); ++ glue_fpu_end(fpu_enabled); + err = blkcipher_walk_done(desc, &walk, nbytes); + } + +- glue_fpu_end(fpu_enabled); + return err; + } + EXPORT_SYMBOL_GPL(glue_cbc_decrypt_128bit); +@@ -278,7 +278,7 @@ + struct scatterlist *src, unsigned int nbytes) + { + const unsigned int bsize = 128 / 8; +- bool fpu_enabled = false; ++ bool fpu_enabled; + struct blkcipher_walk walk; + int err; + +@@ -287,13 +287,12 @@ + + while ((nbytes = walk.nbytes) >= bsize) { + fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit, +- desc, fpu_enabled, nbytes); ++ desc, false, nbytes); + nbytes = __glue_ctr_crypt_128bit(gctx, desc, &walk); ++ glue_fpu_end(fpu_enabled); + err = blkcipher_walk_done(desc, &walk, nbytes); + } + +- glue_fpu_end(fpu_enabled); +- + if (walk.nbytes) { + glue_ctr_crypt_final_128bit( + gctx->funcs[gctx->num_funcs - 1].fn_u.ctr, desc, &walk); +@@ -348,7 +347,7 @@ + void *tweak_ctx, void *crypt_ctx) + { + const unsigned int bsize = 128 / 8; +- bool fpu_enabled = false; ++ bool fpu_enabled; + struct blkcipher_walk walk; + int err; + +@@ -361,21 +360,21 @@ + + /* set minimum length to bsize, for tweak_fn */ + fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit, +- desc, fpu_enabled, ++ desc, false, + nbytes < bsize ? bsize : nbytes); +- + /* calculate first value of T */ + tweak_fn(tweak_ctx, walk.iv, walk.iv); ++ glue_fpu_end(fpu_enabled); + + while (nbytes) { ++ fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit, ++ desc, false, nbytes); + nbytes = __glue_xts_crypt_128bit(gctx, crypt_ctx, desc, &walk); + ++ glue_fpu_end(fpu_enabled); + err = blkcipher_walk_done(desc, &walk, nbytes); + nbytes = walk.nbytes; + } +- +- glue_fpu_end(fpu_enabled); +- + return err; + } + EXPORT_SYMBOL_GPL(glue_xts_crypt_128bit); +diff -Nur linux-3.18.14.orig/arch/x86/include/asm/preempt.h linux-3.18.14-rt/arch/x86/include/asm/preempt.h +--- linux-3.18.14.orig/arch/x86/include/asm/preempt.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/include/asm/preempt.h 2015-05-31 15:32:46.597635384 -0500 +@@ -85,17 +85,33 @@ + * a decrement which hits zero means we have no preempt_count and should + * reschedule. + */ +-static __always_inline bool __preempt_count_dec_and_test(void) ++static __always_inline bool ____preempt_count_dec_and_test(void) + { + GEN_UNARY_RMWcc("decl", __preempt_count, __percpu_arg(0), "e"); + } + ++static __always_inline bool __preempt_count_dec_and_test(void) ++{ ++ if (____preempt_count_dec_and_test()) ++ return true; ++#ifdef CONFIG_PREEMPT_LAZY ++ return test_thread_flag(TIF_NEED_RESCHED_LAZY); ++#else ++ return false; ++#endif ++} ++ + /* + * Returns true when we need to resched and can (barring IRQ state). + */ + static __always_inline bool should_resched(void) + { ++#ifdef CONFIG_PREEMPT_LAZY ++ return unlikely(!raw_cpu_read_4(__preempt_count) || \ ++ test_thread_flag(TIF_NEED_RESCHED_LAZY)); ++#else + return unlikely(!raw_cpu_read_4(__preempt_count)); ++#endif + } + + #ifdef CONFIG_PREEMPT +diff -Nur linux-3.18.14.orig/arch/x86/include/asm/signal.h linux-3.18.14-rt/arch/x86/include/asm/signal.h +--- linux-3.18.14.orig/arch/x86/include/asm/signal.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/include/asm/signal.h 2015-05-31 15:32:46.597635384 -0500 +@@ -23,6 +23,19 @@ + unsigned long sig[_NSIG_WORDS]; + } sigset_t; + ++/* ++ * Because some traps use the IST stack, we must keep preemption ++ * disabled while calling do_trap(), but do_trap() may call ++ * force_sig_info() which will grab the signal spin_locks for the ++ * task, which in PREEMPT_RT_FULL are mutexes. By defining ++ * ARCH_RT_DELAYS_SIGNAL_SEND the force_sig_info() will set ++ * TIF_NOTIFY_RESUME and set up the signal to be sent on exit of the ++ * trap. ++ */ ++#if defined(CONFIG_PREEMPT_RT_FULL) && defined(CONFIG_X86_64) ++#define ARCH_RT_DELAYS_SIGNAL_SEND ++#endif ++ + #ifndef CONFIG_COMPAT + typedef sigset_t compat_sigset_t; + #endif +diff -Nur linux-3.18.14.orig/arch/x86/include/asm/stackprotector.h linux-3.18.14-rt/arch/x86/include/asm/stackprotector.h +--- linux-3.18.14.orig/arch/x86/include/asm/stackprotector.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/include/asm/stackprotector.h 2015-05-31 15:32:46.613635384 -0500 +@@ -57,7 +57,7 @@ + */ + static __always_inline void boot_init_stack_canary(void) + { +- u64 canary; ++ u64 uninitialized_var(canary); + u64 tsc; + + #ifdef CONFIG_X86_64 +@@ -68,8 +68,16 @@ + * of randomness. The TSC only matters for very early init, + * there it already has some randomness on most systems. Later + * on during the bootup the random pool has true entropy too. ++ * ++ * For preempt-rt we need to weaken the randomness a bit, as ++ * we can't call into the random generator from atomic context ++ * due to locking constraints. We just leave canary ++ * uninitialized and use the TSC based randomness on top of ++ * it. + */ ++#ifndef CONFIG_PREEMPT_RT_FULL + get_random_bytes(&canary, sizeof(canary)); ++#endif + tsc = __native_read_tsc(); + canary += tsc + (tsc << 32UL); + +diff -Nur linux-3.18.14.orig/arch/x86/include/asm/thread_info.h linux-3.18.14-rt/arch/x86/include/asm/thread_info.h +--- linux-3.18.14.orig/arch/x86/include/asm/thread_info.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/include/asm/thread_info.h 2015-05-31 15:32:46.621635383 -0500 +@@ -30,6 +30,8 @@ + __u32 status; /* thread synchronous flags */ + __u32 cpu; /* current CPU */ + int saved_preempt_count; ++ int preempt_lazy_count; /* 0 => lazy preemptable ++ <0 => BUG */ + mm_segment_t addr_limit; + struct restart_block restart_block; + void __user *sysenter_return; +@@ -75,6 +77,7 @@ + #define TIF_SYSCALL_EMU 6 /* syscall emulation active */ + #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ + #define TIF_SECCOMP 8 /* secure computing */ ++#define TIF_NEED_RESCHED_LAZY 9 /* lazy rescheduling necessary */ + #define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */ + #define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */ + #define TIF_UPROBE 12 /* breakpointed or singlestepping */ +@@ -100,6 +103,7 @@ + #define _TIF_SYSCALL_EMU (1 << TIF_SYSCALL_EMU) + #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) + #define _TIF_SECCOMP (1 << TIF_SECCOMP) ++#define _TIF_NEED_RESCHED_LAZY (1 << TIF_NEED_RESCHED_LAZY) + #define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY) + #define _TIF_USER_RETURN_NOTIFY (1 << TIF_USER_RETURN_NOTIFY) + #define _TIF_UPROBE (1 << TIF_UPROBE) +@@ -150,6 +154,8 @@ + #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) + #define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW) + ++#define _TIF_NEED_RESCHED_MASK (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY) ++ + #define STACK_WARN (THREAD_SIZE/8) + #define KERNEL_STACK_OFFSET (5*(BITS_PER_LONG/8)) + +diff -Nur linux-3.18.14.orig/arch/x86/include/asm/uv/uv_bau.h linux-3.18.14-rt/arch/x86/include/asm/uv/uv_bau.h +--- linux-3.18.14.orig/arch/x86/include/asm/uv/uv_bau.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/include/asm/uv/uv_bau.h 2015-05-31 15:32:46.621635383 -0500 +@@ -615,9 +615,9 @@ + cycles_t send_message; + cycles_t period_end; + cycles_t period_time; +- spinlock_t uvhub_lock; +- spinlock_t queue_lock; +- spinlock_t disable_lock; ++ raw_spinlock_t uvhub_lock; ++ raw_spinlock_t queue_lock; ++ raw_spinlock_t disable_lock; + /* tunables */ + int max_concurr; + int max_concurr_const; +@@ -776,15 +776,15 @@ + * to be lowered below the current 'v'. atomic_add_unless can only stop + * on equal. + */ +-static inline int atomic_inc_unless_ge(spinlock_t *lock, atomic_t *v, int u) ++static inline int atomic_inc_unless_ge(raw_spinlock_t *lock, atomic_t *v, int u) + { +- spin_lock(lock); ++ raw_spin_lock(lock); + if (atomic_read(v) >= u) { +- spin_unlock(lock); ++ raw_spin_unlock(lock); + return 0; + } + atomic_inc(v); +- spin_unlock(lock); ++ raw_spin_unlock(lock); + return 1; + } + +diff -Nur linux-3.18.14.orig/arch/x86/include/asm/uv/uv_hub.h linux-3.18.14-rt/arch/x86/include/asm/uv/uv_hub.h +--- linux-3.18.14.orig/arch/x86/include/asm/uv/uv_hub.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/include/asm/uv/uv_hub.h 2015-05-31 15:32:46.621635383 -0500 +@@ -492,7 +492,7 @@ + unsigned short nr_online_cpus; + unsigned short pnode; + short memory_nid; +- spinlock_t nmi_lock; /* obsolete, see uv_hub_nmi */ ++ raw_spinlock_t nmi_lock; /* obsolete, see uv_hub_nmi */ + unsigned long nmi_count; /* obsolete, see uv_hub_nmi */ + }; + extern struct uv_blade_info *uv_blade_info; +diff -Nur linux-3.18.14.orig/arch/x86/Kconfig linux-3.18.14-rt/arch/x86/Kconfig +--- linux-3.18.14.orig/arch/x86/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/Kconfig 2015-05-31 15:32:46.561635384 -0500 +@@ -21,6 +21,7 @@ + ### Arch settings + config X86 + def_bool y ++ select HAVE_PREEMPT_LAZY + select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI + select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS + select ARCH_HAS_FAST_MULTIPLIER +@@ -197,8 +198,11 @@ + def_bool y + depends on ISA_DMA_API + ++config RWSEM_GENERIC_SPINLOCK ++ def_bool PREEMPT_RT_FULL ++ + config RWSEM_XCHGADD_ALGORITHM +- def_bool y ++ def_bool !RWSEM_GENERIC_SPINLOCK && !PREEMPT_RT_FULL + + config GENERIC_CALIBRATE_DELAY + def_bool y +@@ -811,7 +815,7 @@ + config MAXSMP + bool "Enable Maximum number of SMP Processors and NUMA Nodes" + depends on X86_64 && SMP && DEBUG_KERNEL +- select CPUMASK_OFFSTACK ++ select CPUMASK_OFFSTACK if !PREEMPT_RT_FULL + ---help--- + Enable maximum number of CPUS and NUMA Nodes for this architecture. + If unsure, say N. +diff -Nur linux-3.18.14.orig/arch/x86/kernel/apic/io_apic.c linux-3.18.14-rt/arch/x86/kernel/apic/io_apic.c +--- linux-3.18.14.orig/arch/x86/kernel/apic/io_apic.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kernel/apic/io_apic.c 2015-05-31 15:32:46.629635384 -0500 +@@ -2494,7 +2494,8 @@ + static inline bool ioapic_irqd_mask(struct irq_data *data, struct irq_cfg *cfg) + { + /* If we are moving the irq we need to mask it */ +- if (unlikely(irqd_is_setaffinity_pending(data))) { ++ if (unlikely(irqd_is_setaffinity_pending(data) && ++ !irqd_irq_inprogress(data))) { + mask_ioapic(cfg); + return true; + } +diff -Nur linux-3.18.14.orig/arch/x86/kernel/apic/x2apic_uv_x.c linux-3.18.14-rt/arch/x86/kernel/apic/x2apic_uv_x.c +--- linux-3.18.14.orig/arch/x86/kernel/apic/x2apic_uv_x.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kernel/apic/x2apic_uv_x.c 2015-05-31 15:32:46.629635384 -0500 +@@ -918,7 +918,7 @@ + uv_blade_info[blade].pnode = pnode; + uv_blade_info[blade].nr_possible_cpus = 0; + uv_blade_info[blade].nr_online_cpus = 0; +- spin_lock_init(&uv_blade_info[blade].nmi_lock); ++ raw_spin_lock_init(&uv_blade_info[blade].nmi_lock); + min_pnode = min(pnode, min_pnode); + max_pnode = max(pnode, max_pnode); + blade++; +diff -Nur linux-3.18.14.orig/arch/x86/kernel/asm-offsets.c linux-3.18.14-rt/arch/x86/kernel/asm-offsets.c +--- linux-3.18.14.orig/arch/x86/kernel/asm-offsets.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kernel/asm-offsets.c 2015-05-31 15:32:46.633635383 -0500 +@@ -32,6 +32,7 @@ + OFFSET(TI_flags, thread_info, flags); + OFFSET(TI_status, thread_info, status); + OFFSET(TI_addr_limit, thread_info, addr_limit); ++ OFFSET(TI_preempt_lazy_count, thread_info, preempt_lazy_count); + + BLANK(); + OFFSET(crypto_tfm_ctx_offset, crypto_tfm, __crt_ctx); +@@ -71,4 +72,5 @@ + + BLANK(); + DEFINE(PTREGS_SIZE, sizeof(struct pt_regs)); ++ DEFINE(_PREEMPT_ENABLED, PREEMPT_ENABLED); + } +diff -Nur linux-3.18.14.orig/arch/x86/kernel/cpu/mcheck/mce.c linux-3.18.14-rt/arch/x86/kernel/cpu/mcheck/mce.c +--- linux-3.18.14.orig/arch/x86/kernel/cpu/mcheck/mce.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kernel/cpu/mcheck/mce.c 2015-05-31 15:32:46.641635383 -0500 +@@ -41,6 +41,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -1266,7 +1268,7 @@ + static unsigned long check_interval = 5 * 60; /* 5 minutes */ + + static DEFINE_PER_CPU(unsigned long, mce_next_interval); /* in jiffies */ +-static DEFINE_PER_CPU(struct timer_list, mce_timer); ++static DEFINE_PER_CPU(struct hrtimer, mce_timer); + + static unsigned long mce_adjust_timer_default(unsigned long interval) + { +@@ -1283,14 +1285,11 @@ + return test_and_clear_bit(0, v); + } + +-static void mce_timer_fn(unsigned long data) ++static enum hrtimer_restart mce_timer_fn(struct hrtimer *timer) + { +- struct timer_list *t = this_cpu_ptr(&mce_timer); + unsigned long iv; + int notify; + +- WARN_ON(smp_processor_id() != data); +- + if (mce_available(this_cpu_ptr(&cpu_info))) { + machine_check_poll(MCP_TIMESTAMP, + this_cpu_ptr(&mce_poll_banks)); +@@ -1313,9 +1312,11 @@ + __this_cpu_write(mce_next_interval, iv); + /* Might have become 0 after CMCI storm subsided */ + if (iv) { +- t->expires = jiffies + iv; +- add_timer_on(t, smp_processor_id()); ++ hrtimer_forward_now(timer, ns_to_ktime( ++ jiffies_to_usecs(iv) * 1000ULL)); ++ return HRTIMER_RESTART; + } ++ return HRTIMER_NORESTART; + } + + /* +@@ -1323,28 +1324,37 @@ + */ + void mce_timer_kick(unsigned long interval) + { +- struct timer_list *t = this_cpu_ptr(&mce_timer); +- unsigned long when = jiffies + interval; ++ struct hrtimer *t = this_cpu_ptr(&mce_timer); + unsigned long iv = __this_cpu_read(mce_next_interval); + +- if (timer_pending(t)) { +- if (time_before(when, t->expires)) +- mod_timer_pinned(t, when); ++ if (hrtimer_active(t)) { ++ s64 exp; ++ s64 intv_us; ++ ++ intv_us = jiffies_to_usecs(interval); ++ exp = ktime_to_us(hrtimer_expires_remaining(t)); ++ if (intv_us < exp) { ++ hrtimer_cancel(t); ++ hrtimer_start_range_ns(t, ++ ns_to_ktime(intv_us * 1000), ++ 0, HRTIMER_MODE_REL_PINNED); ++ } + } else { +- t->expires = round_jiffies(when); +- add_timer_on(t, smp_processor_id()); ++ hrtimer_start_range_ns(t, ++ ns_to_ktime(jiffies_to_usecs(interval) * 1000ULL), ++ 0, HRTIMER_MODE_REL_PINNED); + } + if (interval < iv) + __this_cpu_write(mce_next_interval, interval); + } + +-/* Must not be called in IRQ context where del_timer_sync() can deadlock */ ++/* Must not be called in IRQ context where hrtimer_cancel() can deadlock */ + static void mce_timer_delete_all(void) + { + int cpu; + + for_each_online_cpu(cpu) +- del_timer_sync(&per_cpu(mce_timer, cpu)); ++ hrtimer_cancel(&per_cpu(mce_timer, cpu)); + } + + static void mce_do_trigger(struct work_struct *work) +@@ -1354,6 +1364,56 @@ + + static DECLARE_WORK(mce_trigger_work, mce_do_trigger); + ++static void __mce_notify_work(struct swork_event *event) ++{ ++ /* Not more than two messages every minute */ ++ static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2); ++ ++ /* wake processes polling /dev/mcelog */ ++ wake_up_interruptible(&mce_chrdev_wait); ++ ++ /* ++ * There is no risk of missing notifications because ++ * work_pending is always cleared before the function is ++ * executed. ++ */ ++ if (mce_helper[0] && !work_pending(&mce_trigger_work)) ++ schedule_work(&mce_trigger_work); ++ ++ if (__ratelimit(&ratelimit)) ++ pr_info(HW_ERR "Machine check events logged\n"); ++} ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++static bool notify_work_ready __read_mostly; ++static struct swork_event notify_work; ++ ++static int mce_notify_work_init(void) ++{ ++ int err; ++ ++ err = swork_get(); ++ if (err) ++ return err; ++ ++ INIT_SWORK(¬ify_work, __mce_notify_work); ++ notify_work_ready = true; ++ return 0; ++} ++ ++static void mce_notify_work(void) ++{ ++ if (notify_work_ready) ++ swork_queue(¬ify_work); ++} ++#else ++static void mce_notify_work(void) ++{ ++ __mce_notify_work(NULL); ++} ++static inline int mce_notify_work_init(void) { return 0; } ++#endif ++ + /* + * Notify the user(s) about new machine check events. + * Can be called from interrupt context, but not from machine check/NMI +@@ -1361,19 +1421,8 @@ + */ + int mce_notify_irq(void) + { +- /* Not more than two messages every minute */ +- static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2); +- + if (test_and_clear_bit(0, &mce_need_notify)) { +- /* wake processes polling /dev/mcelog */ +- wake_up_interruptible(&mce_chrdev_wait); +- +- if (mce_helper[0]) +- schedule_work(&mce_trigger_work); +- +- if (__ratelimit(&ratelimit)) +- pr_info(HW_ERR "Machine check events logged\n"); +- ++ mce_notify_work(); + return 1; + } + return 0; +@@ -1644,7 +1693,7 @@ + } + } + +-static void mce_start_timer(unsigned int cpu, struct timer_list *t) ++static void mce_start_timer(unsigned int cpu, struct hrtimer *t) + { + unsigned long iv = check_interval * HZ; + +@@ -1653,16 +1702,17 @@ + + per_cpu(mce_next_interval, cpu) = iv; + +- t->expires = round_jiffies(jiffies + iv); +- add_timer_on(t, cpu); ++ hrtimer_start_range_ns(t, ns_to_ktime(jiffies_to_usecs(iv) * 1000ULL), ++ 0, HRTIMER_MODE_REL_PINNED); + } + + static void __mcheck_cpu_init_timer(void) + { +- struct timer_list *t = this_cpu_ptr(&mce_timer); ++ struct hrtimer *t = this_cpu_ptr(&mce_timer); + unsigned int cpu = smp_processor_id(); + +- setup_timer(t, mce_timer_fn, cpu); ++ hrtimer_init(t, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ t->function = mce_timer_fn; + mce_start_timer(cpu, t); + } + +@@ -2339,6 +2389,8 @@ + if (!mce_available(raw_cpu_ptr(&cpu_info))) + return; + ++ hrtimer_cancel(this_cpu_ptr(&mce_timer)); ++ + if (!(action & CPU_TASKS_FROZEN)) + cmci_clear(); + for (i = 0; i < mca_cfg.banks; i++) { +@@ -2365,6 +2417,7 @@ + if (b->init) + wrmsrl(MSR_IA32_MCx_CTL(i), b->ctl); + } ++ __mcheck_cpu_init_timer(); + } + + /* Get notified when a cpu comes on/off. Be hotplug friendly. */ +@@ -2372,7 +2425,6 @@ + mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) + { + unsigned int cpu = (unsigned long)hcpu; +- struct timer_list *t = &per_cpu(mce_timer, cpu); + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_ONLINE: +@@ -2392,11 +2444,9 @@ + break; + case CPU_DOWN_PREPARE: + smp_call_function_single(cpu, mce_disable_cpu, &action, 1); +- del_timer_sync(t); + break; + case CPU_DOWN_FAILED: + smp_call_function_single(cpu, mce_reenable_cpu, &action, 1); +- mce_start_timer(cpu, t); + break; + } + +@@ -2435,6 +2485,10 @@ + goto err_out; + } + ++ err = mce_notify_work_init(); ++ if (err) ++ goto err_out; ++ + if (!zalloc_cpumask_var(&mce_device_initialized, GFP_KERNEL)) { + err = -ENOMEM; + goto err_out; +diff -Nur linux-3.18.14.orig/arch/x86/kernel/entry_32.S linux-3.18.14-rt/arch/x86/kernel/entry_32.S +--- linux-3.18.14.orig/arch/x86/kernel/entry_32.S 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kernel/entry_32.S 2015-05-31 15:32:46.641635383 -0500 +@@ -359,8 +359,24 @@ + ENTRY(resume_kernel) + DISABLE_INTERRUPTS(CLBR_ANY) + need_resched: ++ # preempt count == 0 + NEED_RS set? + cmpl $0,PER_CPU_VAR(__preempt_count) ++#ifndef CONFIG_PREEMPT_LAZY + jnz restore_all ++#else ++ jz test_int_off ++ ++ # atleast preempt count == 0 ? ++ cmpl $_PREEMPT_ENABLED,PER_CPU_VAR(__preempt_count) ++ jne restore_all ++ ++ cmpl $0,TI_preempt_lazy_count(%ebp) # non-zero preempt_lazy_count ? ++ jnz restore_all ++ ++ testl $_TIF_NEED_RESCHED_LAZY, TI_flags(%ebp) ++ jz restore_all ++test_int_off: ++#endif + testl $X86_EFLAGS_IF,PT_EFLAGS(%esp) # interrupts off (exception path) ? + jz restore_all + call preempt_schedule_irq +@@ -591,7 +607,7 @@ + ALIGN + RING0_PTREGS_FRAME # can't unwind into user space anyway + work_pending: +- testb $_TIF_NEED_RESCHED, %cl ++ testl $_TIF_NEED_RESCHED_MASK, %ecx + jz work_notifysig + work_resched: + call schedule +@@ -604,7 +620,7 @@ + andl $_TIF_WORK_MASK, %ecx # is there any work to be done other + # than syscall tracing? + jz restore_all +- testb $_TIF_NEED_RESCHED, %cl ++ testl $_TIF_NEED_RESCHED_MASK, %ecx + jnz work_resched + + work_notifysig: # deal with pending signals and +diff -Nur linux-3.18.14.orig/arch/x86/kernel/entry_64.S linux-3.18.14-rt/arch/x86/kernel/entry_64.S +--- linux-3.18.14.orig/arch/x86/kernel/entry_64.S 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kernel/entry_64.S 2015-05-31 15:32:46.649635383 -0500 +@@ -454,8 +454,8 @@ + /* Handle reschedules */ + /* edx: work, edi: workmask */ + sysret_careful: +- bt $TIF_NEED_RESCHED,%edx +- jnc sysret_signal ++ testl $_TIF_NEED_RESCHED_MASK,%edx ++ jz sysret_signal + TRACE_IRQS_ON + ENABLE_INTERRUPTS(CLBR_NONE) + pushq_cfi %rdi +@@ -554,8 +554,8 @@ + /* First do a reschedule test. */ + /* edx: work, edi: workmask */ + int_careful: +- bt $TIF_NEED_RESCHED,%edx +- jnc int_very_careful ++ testl $_TIF_NEED_RESCHED_MASK,%edx ++ jz int_very_careful + TRACE_IRQS_ON + ENABLE_INTERRUPTS(CLBR_NONE) + pushq_cfi %rdi +@@ -870,8 +870,8 @@ + /* edi: workmask, edx: work */ + retint_careful: + CFI_RESTORE_STATE +- bt $TIF_NEED_RESCHED,%edx +- jnc retint_signal ++ testl $_TIF_NEED_RESCHED_MASK,%edx ++ jz retint_signal + TRACE_IRQS_ON + ENABLE_INTERRUPTS(CLBR_NONE) + pushq_cfi %rdi +@@ -903,7 +903,22 @@ + /* rcx: threadinfo. interrupts off. */ + ENTRY(retint_kernel) + cmpl $0,PER_CPU_VAR(__preempt_count) ++#ifndef CONFIG_PREEMPT_LAZY + jnz retint_restore_args ++#else ++ jz check_int_off ++ ++ # atleast preempt count == 0 ? ++ cmpl $_PREEMPT_ENABLED,PER_CPU_VAR(__preempt_count) ++ jnz retint_restore_args ++ ++ cmpl $0, TI_preempt_lazy_count(%rcx) ++ jnz retint_restore_args ++ ++ bt $TIF_NEED_RESCHED_LAZY,TI_flags(%rcx) ++ jnc retint_restore_args ++check_int_off: ++#endif + bt $9,EFLAGS-ARGOFFSET(%rsp) /* interrupts off? */ + jnc retint_restore_args + call preempt_schedule_irq +@@ -1119,6 +1134,7 @@ + jmp 2b + .previous + ++#ifndef CONFIG_PREEMPT_RT_FULL + /* Call softirq on interrupt stack. Interrupts are off. */ + ENTRY(do_softirq_own_stack) + CFI_STARTPROC +@@ -1138,6 +1154,7 @@ + ret + CFI_ENDPROC + END(do_softirq_own_stack) ++#endif + + #ifdef CONFIG_XEN + idtentry xen_hypervisor_callback xen_do_hypervisor_callback has_error_code=0 +@@ -1302,7 +1319,7 @@ + movq %rsp,%rdi /* &pt_regs */ + call sync_regs + movq %rax,%rsp /* switch stack for scheduling */ +- testl $_TIF_NEED_RESCHED,%ebx ++ testl $_TIF_NEED_RESCHED_MASK,%ebx + jnz paranoid_schedule + movl %ebx,%edx /* arg3: thread flags */ + TRACE_IRQS_ON +diff -Nur linux-3.18.14.orig/arch/x86/kernel/irq_32.c linux-3.18.14-rt/arch/x86/kernel/irq_32.c +--- linux-3.18.14.orig/arch/x86/kernel/irq_32.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kernel/irq_32.c 2015-05-31 15:32:46.653635383 -0500 +@@ -142,6 +142,7 @@ + cpu, per_cpu(hardirq_stack, cpu), per_cpu(softirq_stack, cpu)); + } + ++#ifndef CONFIG_PREEMPT_RT_FULL + void do_softirq_own_stack(void) + { + struct thread_info *curstk; +@@ -160,6 +161,7 @@ + + call_on_stack(__do_softirq, isp); + } ++#endif + + bool handle_irq(unsigned irq, struct pt_regs *regs) + { +diff -Nur linux-3.18.14.orig/arch/x86/kernel/process_32.c linux-3.18.14-rt/arch/x86/kernel/process_32.c +--- linux-3.18.14.orig/arch/x86/kernel/process_32.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kernel/process_32.c 2015-05-31 15:32:46.653635383 -0500 +@@ -35,6 +35,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -214,6 +215,35 @@ + } + EXPORT_SYMBOL_GPL(start_thread); + ++#ifdef CONFIG_PREEMPT_RT_FULL ++static void switch_kmaps(struct task_struct *prev_p, struct task_struct *next_p) ++{ ++ int i; ++ ++ /* ++ * Clear @prev's kmap_atomic mappings ++ */ ++ for (i = 0; i < prev_p->kmap_idx; i++) { ++ int idx = i + KM_TYPE_NR * smp_processor_id(); ++ pte_t *ptep = kmap_pte - idx; ++ ++ kpte_clear_flush(ptep, __fix_to_virt(FIX_KMAP_BEGIN + idx)); ++ } ++ /* ++ * Restore @next_p's kmap_atomic mappings ++ */ ++ for (i = 0; i < next_p->kmap_idx; i++) { ++ int idx = i + KM_TYPE_NR * smp_processor_id(); ++ ++ if (!pte_none(next_p->kmap_pte[i])) ++ set_pte(kmap_pte - idx, next_p->kmap_pte[i]); ++ } ++} ++#else ++static inline void ++switch_kmaps(struct task_struct *prev_p, struct task_struct *next_p) { } ++#endif ++ + + /* + * switch_to(x,y) should switch tasks from x to y. +@@ -301,6 +331,8 @@ + task_thread_info(next_p)->flags & _TIF_WORK_CTXSW_NEXT)) + __switch_to_xtra(prev_p, next_p, tss); + ++ switch_kmaps(prev_p, next_p); ++ + /* + * Leave lazy mode, flushing any hypercalls made here. + * This must be done before restoring TLS segments so +diff -Nur linux-3.18.14.orig/arch/x86/kernel/signal.c linux-3.18.14-rt/arch/x86/kernel/signal.c +--- linux-3.18.14.orig/arch/x86/kernel/signal.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kernel/signal.c 2015-05-31 15:32:46.653635383 -0500 +@@ -746,6 +746,14 @@ + mce_notify_process(); + #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ + ++#ifdef ARCH_RT_DELAYS_SIGNAL_SEND ++ if (unlikely(current->forced_info.si_signo)) { ++ struct task_struct *t = current; ++ force_sig_info(t->forced_info.si_signo, &t->forced_info, t); ++ t->forced_info.si_signo = 0; ++ } ++#endif ++ + if (thread_info_flags & _TIF_UPROBE) + uprobe_notify_resume(regs); + +diff -Nur linux-3.18.14.orig/arch/x86/kernel/traps.c linux-3.18.14-rt/arch/x86/kernel/traps.c +--- linux-3.18.14.orig/arch/x86/kernel/traps.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kernel/traps.c 2015-05-31 15:32:46.657635383 -0500 +@@ -87,9 +87,21 @@ + local_irq_enable(); + } + +-static inline void preempt_conditional_sti(struct pt_regs *regs) ++static inline void conditional_sti_ist(struct pt_regs *regs) + { ++#ifdef CONFIG_X86_64 ++ /* ++ * X86_64 uses a per CPU stack on the IST for certain traps ++ * like int3. The task can not be preempted when using one ++ * of these stacks, thus preemption must be disabled, otherwise ++ * the stack can be corrupted if the task is scheduled out, ++ * and another task comes in and uses this stack. ++ * ++ * On x86_32 the task keeps its own stack and it is OK if the ++ * task schedules out. ++ */ + preempt_count_inc(); ++#endif + if (regs->flags & X86_EFLAGS_IF) + local_irq_enable(); + } +@@ -100,11 +112,13 @@ + local_irq_disable(); + } + +-static inline void preempt_conditional_cli(struct pt_regs *regs) ++static inline void conditional_cli_ist(struct pt_regs *regs) + { + if (regs->flags & X86_EFLAGS_IF) + local_irq_disable(); ++#ifdef CONFIG_X86_64 + preempt_count_dec(); ++#endif + } + + static nokprobe_inline int +@@ -372,9 +386,9 @@ + * as we may switch to the interrupt stack. + */ + debug_stack_usage_inc(); +- preempt_conditional_sti(regs); ++ conditional_sti_ist(regs); + do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL); +- preempt_conditional_cli(regs); ++ conditional_cli_ist(regs); + debug_stack_usage_dec(); + exit: + exception_exit(prev_state); +@@ -517,12 +531,12 @@ + debug_stack_usage_inc(); + + /* It's safe to allow irq's after DR6 has been saved */ +- preempt_conditional_sti(regs); ++ conditional_sti_ist(regs); + + if (regs->flags & X86_VM_MASK) { + handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, + X86_TRAP_DB); +- preempt_conditional_cli(regs); ++ conditional_cli_ist(regs); + debug_stack_usage_dec(); + goto exit; + } +@@ -542,7 +556,7 @@ + si_code = get_si_code(tsk->thread.debugreg6); + if (tsk->thread.debugreg6 & (DR_STEP | DR_TRAP_BITS) || user_icebp) + send_sigtrap(tsk, regs, error_code, si_code); +- preempt_conditional_cli(regs); ++ conditional_cli_ist(regs); + debug_stack_usage_dec(); + + exit: +diff -Nur linux-3.18.14.orig/arch/x86/kvm/lapic.c linux-3.18.14-rt/arch/x86/kvm/lapic.c +--- linux-3.18.14.orig/arch/x86/kvm/lapic.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kvm/lapic.c 2015-05-31 15:32:46.693635383 -0500 +@@ -1034,8 +1034,38 @@ + apic->divide_count); + } + ++ ++static enum hrtimer_restart apic_timer_fn(struct hrtimer *data); ++ ++static void apic_timer_expired(struct hrtimer *data) ++{ ++ int ret, i = 0; ++ enum hrtimer_restart r; ++ struct kvm_timer *ktimer = container_of(data, struct kvm_timer, timer); ++ ++ r = apic_timer_fn(data); ++ ++ if (r == HRTIMER_RESTART) { ++ do { ++ ret = hrtimer_start_expires(data, HRTIMER_MODE_ABS); ++ if (ret == -ETIME) ++ hrtimer_add_expires_ns(&ktimer->timer, ++ ktimer->period); ++ i++; ++ } while (ret == -ETIME && i < 10); ++ ++ if (ret == -ETIME) { ++ printk_once(KERN_ERR "%s: failed to reprogram timer\n", ++ __func__); ++ WARN_ON_ONCE(1); ++ } ++ } ++} ++ ++ + static void start_apic_timer(struct kvm_lapic *apic) + { ++ int ret; + ktime_t now; + atomic_set(&apic->lapic_timer.pending, 0); + +@@ -1065,9 +1095,11 @@ + } + } + +- hrtimer_start(&apic->lapic_timer.timer, ++ ret = hrtimer_start(&apic->lapic_timer.timer, + ktime_add_ns(now, apic->lapic_timer.period), + HRTIMER_MODE_ABS); ++ if (ret == -ETIME) ++ apic_timer_expired(&apic->lapic_timer.timer); + + apic_debug("%s: bus cycle is %" PRId64 "ns, now 0x%016" + PRIx64 ", " +@@ -1097,8 +1129,10 @@ + ns = (tscdeadline - guest_tsc) * 1000000ULL; + do_div(ns, this_tsc_khz); + } +- hrtimer_start(&apic->lapic_timer.timer, ++ ret = hrtimer_start(&apic->lapic_timer.timer, + ktime_add_ns(now, ns), HRTIMER_MODE_ABS); ++ if (ret == -ETIME) ++ apic_timer_expired(&apic->lapic_timer.timer); + + local_irq_restore(flags); + } +@@ -1539,7 +1573,7 @@ + struct kvm_timer *ktimer = container_of(data, struct kvm_timer, timer); + struct kvm_lapic *apic = container_of(ktimer, struct kvm_lapic, lapic_timer); + struct kvm_vcpu *vcpu = apic->vcpu; +- wait_queue_head_t *q = &vcpu->wq; ++ struct swait_head *q = &vcpu->wq; + + /* + * There is a race window between reading and incrementing, but we do +@@ -1553,8 +1587,8 @@ + kvm_make_request(KVM_REQ_PENDING_TIMER, vcpu); + } + +- if (waitqueue_active(q)) +- wake_up_interruptible(q); ++ if (swaitqueue_active(q)) ++ swait_wake_interruptible(q); + + if (lapic_is_periodic(apic)) { + hrtimer_add_expires_ns(&ktimer->timer, ktimer->period); +@@ -1587,6 +1621,7 @@ + hrtimer_init(&apic->lapic_timer.timer, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS); + apic->lapic_timer.timer.function = apic_timer_fn; ++ apic->lapic_timer.timer.irqsafe = 1; + + /* + * APIC is created enabled. This will prevent kvm_lapic_set_base from +@@ -1707,7 +1742,8 @@ + + timer = &vcpu->arch.apic->lapic_timer.timer; + if (hrtimer_cancel(timer)) +- hrtimer_start_expires(timer, HRTIMER_MODE_ABS); ++ if (hrtimer_start_expires(timer, HRTIMER_MODE_ABS) == -ETIME) ++ apic_timer_expired(timer); + } + + /* +diff -Nur linux-3.18.14.orig/arch/x86/kvm/x86.c linux-3.18.14-rt/arch/x86/kvm/x86.c +--- linux-3.18.14.orig/arch/x86/kvm/x86.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/kvm/x86.c 2015-05-31 15:32:46.697635383 -0500 +@@ -5772,6 +5772,13 @@ + goto out; + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++ if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) { ++ printk(KERN_ERR "RT requires X86_FEATURE_CONSTANT_TSC\n"); ++ return -EOPNOTSUPP; ++ } ++#endif ++ + r = kvm_mmu_module_init(); + if (r) + goto out_free_percpu; +diff -Nur linux-3.18.14.orig/arch/x86/mm/fault.c linux-3.18.14-rt/arch/x86/mm/fault.c +--- linux-3.18.14.orig/arch/x86/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/mm/fault.c 2015-05-31 15:32:46.729635382 -0500 +@@ -1128,7 +1128,7 @@ + * If we're in an interrupt, have no user context or are running + * in an atomic region then we must not take the fault: + */ +- if (unlikely(in_atomic() || !mm)) { ++ if (unlikely(!mm || pagefault_disabled())) { + bad_area_nosemaphore(regs, error_code, address); + return; + } +diff -Nur linux-3.18.14.orig/arch/x86/mm/highmem_32.c linux-3.18.14-rt/arch/x86/mm/highmem_32.c +--- linux-3.18.14.orig/arch/x86/mm/highmem_32.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/mm/highmem_32.c 2015-05-31 15:32:46.729635382 -0500 +@@ -32,6 +32,7 @@ + */ + void *kmap_atomic_prot(struct page *page, pgprot_t prot) + { ++ pte_t pte = mk_pte(page, prot); + unsigned long vaddr; + int idx, type; + +@@ -45,7 +46,10 @@ + idx = type + KM_TYPE_NR*smp_processor_id(); + vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); + BUG_ON(!pte_none(*(kmap_pte-idx))); +- set_pte(kmap_pte-idx, mk_pte(page, prot)); ++#ifdef CONFIG_PREEMPT_RT_FULL ++ current->kmap_pte[type] = pte; ++#endif ++ set_pte(kmap_pte-idx, pte); + arch_flush_lazy_mmu_mode(); + + return (void *)vaddr; +@@ -88,6 +92,9 @@ + * is a bad idea also, in case the page changes cacheability + * attributes or becomes a protected page in a hypervisor. + */ ++#ifdef CONFIG_PREEMPT_RT_FULL ++ current->kmap_pte[type] = __pte(0); ++#endif + kpte_clear_flush(kmap_pte-idx, vaddr); + kmap_atomic_idx_pop(); + arch_flush_lazy_mmu_mode(); +diff -Nur linux-3.18.14.orig/arch/x86/mm/iomap_32.c linux-3.18.14-rt/arch/x86/mm/iomap_32.c +--- linux-3.18.14.orig/arch/x86/mm/iomap_32.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/mm/iomap_32.c 2015-05-31 15:32:46.733635383 -0500 +@@ -56,6 +56,7 @@ + + void *kmap_atomic_prot_pfn(unsigned long pfn, pgprot_t prot) + { ++ pte_t pte = pfn_pte(pfn, prot); + unsigned long vaddr; + int idx, type; + +@@ -64,7 +65,12 @@ + type = kmap_atomic_idx_push(); + idx = type + KM_TYPE_NR * smp_processor_id(); + vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); +- set_pte(kmap_pte - idx, pfn_pte(pfn, prot)); ++ WARN_ON(!pte_none(*(kmap_pte - idx))); ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++ current->kmap_pte[type] = pte; ++#endif ++ set_pte(kmap_pte - idx, pte); + arch_flush_lazy_mmu_mode(); + + return (void *)vaddr; +@@ -110,6 +116,9 @@ + * is a bad idea also, in case the page changes cacheability + * attributes or becomes a protected page in a hypervisor. + */ ++#ifdef CONFIG_PREEMPT_RT_FULL ++ current->kmap_pte[type] = __pte(0); ++#endif + kpte_clear_flush(kmap_pte-idx, vaddr); + kmap_atomic_idx_pop(); + } +diff -Nur linux-3.18.14.orig/arch/x86/platform/uv/tlb_uv.c linux-3.18.14-rt/arch/x86/platform/uv/tlb_uv.c +--- linux-3.18.14.orig/arch/x86/platform/uv/tlb_uv.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/platform/uv/tlb_uv.c 2015-05-31 15:32:46.733635383 -0500 +@@ -714,9 +714,9 @@ + + quiesce_local_uvhub(hmaster); + +- spin_lock(&hmaster->queue_lock); ++ raw_spin_lock(&hmaster->queue_lock); + reset_with_ipi(&bau_desc->distribution, bcp); +- spin_unlock(&hmaster->queue_lock); ++ raw_spin_unlock(&hmaster->queue_lock); + + end_uvhub_quiesce(hmaster); + +@@ -736,9 +736,9 @@ + + quiesce_local_uvhub(hmaster); + +- spin_lock(&hmaster->queue_lock); ++ raw_spin_lock(&hmaster->queue_lock); + reset_with_ipi(&bau_desc->distribution, bcp); +- spin_unlock(&hmaster->queue_lock); ++ raw_spin_unlock(&hmaster->queue_lock); + + end_uvhub_quiesce(hmaster); + +@@ -759,7 +759,7 @@ + cycles_t tm1; + + hmaster = bcp->uvhub_master; +- spin_lock(&hmaster->disable_lock); ++ raw_spin_lock(&hmaster->disable_lock); + if (!bcp->baudisabled) { + stat->s_bau_disabled++; + tm1 = get_cycles(); +@@ -772,7 +772,7 @@ + } + } + } +- spin_unlock(&hmaster->disable_lock); ++ raw_spin_unlock(&hmaster->disable_lock); + } + + static void count_max_concurr(int stat, struct bau_control *bcp, +@@ -835,7 +835,7 @@ + */ + static void uv1_throttle(struct bau_control *hmaster, struct ptc_stats *stat) + { +- spinlock_t *lock = &hmaster->uvhub_lock; ++ raw_spinlock_t *lock = &hmaster->uvhub_lock; + atomic_t *v; + + v = &hmaster->active_descriptor_count; +@@ -968,7 +968,7 @@ + struct bau_control *hmaster; + + hmaster = bcp->uvhub_master; +- spin_lock(&hmaster->disable_lock); ++ raw_spin_lock(&hmaster->disable_lock); + if (bcp->baudisabled && (get_cycles() >= bcp->set_bau_on_time)) { + stat->s_bau_reenabled++; + for_each_present_cpu(tcpu) { +@@ -980,10 +980,10 @@ + tbcp->period_giveups = 0; + } + } +- spin_unlock(&hmaster->disable_lock); ++ raw_spin_unlock(&hmaster->disable_lock); + return 0; + } +- spin_unlock(&hmaster->disable_lock); ++ raw_spin_unlock(&hmaster->disable_lock); + return -1; + } + +@@ -1899,9 +1899,9 @@ + bcp->cong_reps = congested_reps; + bcp->disabled_period = sec_2_cycles(disabled_period); + bcp->giveup_limit = giveup_limit; +- spin_lock_init(&bcp->queue_lock); +- spin_lock_init(&bcp->uvhub_lock); +- spin_lock_init(&bcp->disable_lock); ++ raw_spin_lock_init(&bcp->queue_lock); ++ raw_spin_lock_init(&bcp->uvhub_lock); ++ raw_spin_lock_init(&bcp->disable_lock); + } + } + +diff -Nur linux-3.18.14.orig/arch/x86/platform/uv/uv_time.c linux-3.18.14-rt/arch/x86/platform/uv/uv_time.c +--- linux-3.18.14.orig/arch/x86/platform/uv/uv_time.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/x86/platform/uv/uv_time.c 2015-05-31 15:32:46.737635383 -0500 +@@ -58,7 +58,7 @@ + + /* There is one of these allocated per node */ + struct uv_rtc_timer_head { +- spinlock_t lock; ++ raw_spinlock_t lock; + /* next cpu waiting for timer, local node relative: */ + int next_cpu; + /* number of cpus on this node: */ +@@ -178,7 +178,7 @@ + uv_rtc_deallocate_timers(); + return -ENOMEM; + } +- spin_lock_init(&head->lock); ++ raw_spin_lock_init(&head->lock); + head->ncpus = uv_blade_nr_possible_cpus(bid); + head->next_cpu = -1; + blade_info[bid] = head; +@@ -232,7 +232,7 @@ + unsigned long flags; + int next_cpu; + +- spin_lock_irqsave(&head->lock, flags); ++ raw_spin_lock_irqsave(&head->lock, flags); + + next_cpu = head->next_cpu; + *t = expires; +@@ -244,12 +244,12 @@ + if (uv_setup_intr(cpu, expires)) { + *t = ULLONG_MAX; + uv_rtc_find_next_timer(head, pnode); +- spin_unlock_irqrestore(&head->lock, flags); ++ raw_spin_unlock_irqrestore(&head->lock, flags); + return -ETIME; + } + } + +- spin_unlock_irqrestore(&head->lock, flags); ++ raw_spin_unlock_irqrestore(&head->lock, flags); + return 0; + } + +@@ -268,7 +268,7 @@ + unsigned long flags; + int rc = 0; + +- spin_lock_irqsave(&head->lock, flags); ++ raw_spin_lock_irqsave(&head->lock, flags); + + if ((head->next_cpu == bcpu && uv_read_rtc(NULL) >= *t) || force) + rc = 1; +@@ -280,7 +280,7 @@ + uv_rtc_find_next_timer(head, pnode); + } + +- spin_unlock_irqrestore(&head->lock, flags); ++ raw_spin_unlock_irqrestore(&head->lock, flags); + + return rc; + } +@@ -300,13 +300,18 @@ + static cycle_t uv_read_rtc(struct clocksource *cs) + { + unsigned long offset; ++ cycle_t cycles; + ++ preempt_disable(); + if (uv_get_min_hub_revision_id() == 1) + offset = 0; + else + offset = (uv_blade_processor_id() * L1_CACHE_BYTES) % PAGE_SIZE; + +- return (cycle_t)uv_read_local_mmr(UVH_RTC | offset); ++ cycles = (cycle_t)uv_read_local_mmr(UVH_RTC | offset); ++ preempt_enable(); ++ ++ return cycles; + } + + /* +diff -Nur linux-3.18.14.orig/arch/xtensa/mm/fault.c linux-3.18.14-rt/arch/xtensa/mm/fault.c +--- linux-3.18.14.orig/arch/xtensa/mm/fault.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/arch/xtensa/mm/fault.c 2015-05-31 15:32:46.741635382 -0500 +@@ -57,7 +57,7 @@ + /* If we're in an interrupt or have no user + * context, we must not take the fault.. + */ +- if (in_atomic() || !mm) { ++ if (!mm || pagefault_disabled()) { + bad_page_fault(regs, address, SIGSEGV); + return; + } +diff -Nur linux-3.18.14.orig/block/blk-core.c linux-3.18.14-rt/block/blk-core.c +--- linux-3.18.14.orig/block/blk-core.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/block/blk-core.c 2015-05-31 15:32:46.757635382 -0500 +@@ -100,6 +100,9 @@ + + INIT_LIST_HEAD(&rq->queuelist); + INIT_LIST_HEAD(&rq->timeout_list); ++#if CONFIG_PREEMPT_RT_FULL ++ INIT_WORK(&rq->work, __blk_mq_complete_request_remote_work); ++#endif + rq->cpu = -1; + rq->q = q; + rq->__sector = (sector_t) -1; +@@ -194,7 +197,7 @@ + **/ + void blk_start_queue(struct request_queue *q) + { +- WARN_ON(!irqs_disabled()); ++ WARN_ON_NONRT(!irqs_disabled()); + + queue_flag_clear(QUEUE_FLAG_STOPPED, q); + __blk_run_queue(q); +@@ -627,7 +630,7 @@ + q->bypass_depth = 1; + __set_bit(QUEUE_FLAG_BYPASS, &q->queue_flags); + +- init_waitqueue_head(&q->mq_freeze_wq); ++ init_swait_head(&q->mq_freeze_wq); + + if (blkcg_init_queue(q)) + goto fail_bdi; +@@ -3037,7 +3040,7 @@ + blk_run_queue_async(q); + else + __blk_run_queue(q); +- spin_unlock(q->queue_lock); ++ spin_unlock_irq(q->queue_lock); + } + + static void flush_plug_callbacks(struct blk_plug *plug, bool from_schedule) +@@ -3085,7 +3088,6 @@ + void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule) + { + struct request_queue *q; +- unsigned long flags; + struct request *rq; + LIST_HEAD(list); + unsigned int depth; +@@ -3105,11 +3107,6 @@ + q = NULL; + depth = 0; + +- /* +- * Save and disable interrupts here, to avoid doing it for every +- * queue lock we have to take. +- */ +- local_irq_save(flags); + while (!list_empty(&list)) { + rq = list_entry_rq(list.next); + list_del_init(&rq->queuelist); +@@ -3122,7 +3119,7 @@ + queue_unplugged(q, depth, from_schedule); + q = rq->q; + depth = 0; +- spin_lock(q->queue_lock); ++ spin_lock_irq(q->queue_lock); + } + + /* +@@ -3149,8 +3146,6 @@ + */ + if (q) + queue_unplugged(q, depth, from_schedule); +- +- local_irq_restore(flags); + } + + void blk_finish_plug(struct blk_plug *plug) +diff -Nur linux-3.18.14.orig/block/blk-ioc.c linux-3.18.14-rt/block/blk-ioc.c +--- linux-3.18.14.orig/block/blk-ioc.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/block/blk-ioc.c 2015-05-31 15:32:46.761635382 -0500 +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + + #include "blk.h" + +@@ -109,7 +110,7 @@ + spin_unlock(q->queue_lock); + } else { + spin_unlock_irqrestore(&ioc->lock, flags); +- cpu_relax(); ++ cpu_chill(); + spin_lock_irqsave_nested(&ioc->lock, flags, 1); + } + } +@@ -187,7 +188,7 @@ + spin_unlock(icq->q->queue_lock); + } else { + spin_unlock_irqrestore(&ioc->lock, flags); +- cpu_relax(); ++ cpu_chill(); + goto retry; + } + } +diff -Nur linux-3.18.14.orig/block/blk-iopoll.c linux-3.18.14-rt/block/blk-iopoll.c +--- linux-3.18.14.orig/block/blk-iopoll.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/block/blk-iopoll.c 2015-05-31 15:32:46.761635382 -0500 +@@ -35,6 +35,7 @@ + list_add_tail(&iop->list, this_cpu_ptr(&blk_cpu_iopoll)); + __raise_softirq_irqoff(BLOCK_IOPOLL_SOFTIRQ); + local_irq_restore(flags); ++ preempt_check_resched_rt(); + } + EXPORT_SYMBOL(blk_iopoll_sched); + +@@ -132,6 +133,7 @@ + __raise_softirq_irqoff(BLOCK_IOPOLL_SOFTIRQ); + + local_irq_enable(); ++ preempt_check_resched_rt(); + } + + /** +@@ -201,6 +203,7 @@ + this_cpu_ptr(&blk_cpu_iopoll)); + __raise_softirq_irqoff(BLOCK_IOPOLL_SOFTIRQ); + local_irq_enable(); ++ preempt_check_resched_rt(); + } + + return NOTIFY_OK; +diff -Nur linux-3.18.14.orig/block/blk-mq.c linux-3.18.14-rt/block/blk-mq.c +--- linux-3.18.14.orig/block/blk-mq.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/block/blk-mq.c 2015-05-31 15:32:46.789635382 -0500 +@@ -85,7 +85,7 @@ + if (percpu_ref_tryget_live(&q->mq_usage_counter)) + return 0; + +- ret = wait_event_interruptible(q->mq_freeze_wq, ++ ret = swait_event_interruptible(q->mq_freeze_wq, + !q->mq_freeze_depth || blk_queue_dying(q)); + if (blk_queue_dying(q)) + return -ENODEV; +@@ -104,7 +104,7 @@ + struct request_queue *q = + container_of(ref, struct request_queue, mq_usage_counter); + +- wake_up_all(&q->mq_freeze_wq); ++ swait_wake_all(&q->mq_freeze_wq); + } + + static void blk_mq_freeze_queue_start(struct request_queue *q) +@@ -123,7 +123,7 @@ + + static void blk_mq_freeze_queue_wait(struct request_queue *q) + { +- wait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->mq_usage_counter)); ++ swait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->mq_usage_counter)); + } + + /* +@@ -146,7 +146,7 @@ + spin_unlock_irq(q->queue_lock); + if (wake) { + percpu_ref_reinit(&q->mq_usage_counter); +- wake_up_all(&q->mq_freeze_wq); ++ swait_wake_all(&q->mq_freeze_wq); + } + } + +@@ -194,6 +194,9 @@ + rq->resid_len = 0; + rq->sense = NULL; + ++#ifdef CONFIG_PREEMPT_RT_FULL ++ INIT_WORK(&rq->work, __blk_mq_complete_request_remote_work); ++#endif + INIT_LIST_HEAD(&rq->timeout_list); + rq->timeout = 0; + +@@ -313,6 +316,17 @@ + } + EXPORT_SYMBOL(blk_mq_end_request); + ++#ifdef CONFIG_PREEMPT_RT_FULL ++ ++void __blk_mq_complete_request_remote_work(struct work_struct *work) ++{ ++ struct request *rq = container_of(work, struct request, work); ++ ++ rq->q->softirq_done_fn(rq); ++} ++ ++#else ++ + static void __blk_mq_complete_request_remote(void *data) + { + struct request *rq = data; +@@ -320,6 +334,8 @@ + rq->q->softirq_done_fn(rq); + } + ++#endif ++ + static void blk_mq_ipi_complete_request(struct request *rq) + { + struct blk_mq_ctx *ctx = rq->mq_ctx; +@@ -331,19 +347,23 @@ + return; + } + +- cpu = get_cpu(); ++ cpu = get_cpu_light(); + if (!test_bit(QUEUE_FLAG_SAME_FORCE, &rq->q->queue_flags)) + shared = cpus_share_cache(cpu, ctx->cpu); + + if (cpu != ctx->cpu && !shared && cpu_online(ctx->cpu)) { ++#ifdef CONFIG_PREEMPT_RT_FULL ++ schedule_work_on(ctx->cpu, &rq->work); ++#else + rq->csd.func = __blk_mq_complete_request_remote; + rq->csd.info = rq; + rq->csd.flags = 0; + smp_call_function_single_async(ctx->cpu, &rq->csd); ++#endif + } else { + rq->q->softirq_done_fn(rq); + } +- put_cpu(); ++ put_cpu_light(); + } + + void __blk_mq_complete_request(struct request *rq) +@@ -814,9 +834,9 @@ + test_bit(BLK_MQ_S_STOPPED, &hctx->state)) + continue; + +- preempt_disable(); ++ migrate_disable(); + blk_mq_run_hw_queue(hctx, async); +- preempt_enable(); ++ migrate_enable(); + } + } + EXPORT_SYMBOL(blk_mq_run_queues); +@@ -843,9 +863,9 @@ + { + clear_bit(BLK_MQ_S_STOPPED, &hctx->state); + +- preempt_disable(); ++ migrate_disable(); + blk_mq_run_hw_queue(hctx, false); +- preempt_enable(); ++ migrate_enable(); + } + EXPORT_SYMBOL(blk_mq_start_hw_queue); + +@@ -870,9 +890,9 @@ + continue; + + clear_bit(BLK_MQ_S_STOPPED, &hctx->state); +- preempt_disable(); ++ migrate_disable(); + blk_mq_run_hw_queue(hctx, async); +- preempt_enable(); ++ migrate_enable(); + } + } + EXPORT_SYMBOL(blk_mq_start_stopped_hw_queues); +@@ -1494,7 +1514,7 @@ + { + struct blk_mq_hw_ctx *hctx = data; + +- if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) ++ if (action == CPU_POST_DEAD) + return blk_mq_hctx_cpu_offline(hctx, cpu); + else if (action == CPU_ONLINE || action == CPU_ONLINE_FROZEN) + return blk_mq_hctx_cpu_online(hctx, cpu); +diff -Nur linux-3.18.14.orig/block/blk-mq-cpu.c linux-3.18.14-rt/block/blk-mq-cpu.c +--- linux-3.18.14.orig/block/blk-mq-cpu.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/block/blk-mq-cpu.c 2015-05-31 15:32:46.773635382 -0500 +@@ -16,7 +16,7 @@ + #include "blk-mq.h" + + static LIST_HEAD(blk_mq_cpu_notify_list); +-static DEFINE_RAW_SPINLOCK(blk_mq_cpu_notify_lock); ++static DEFINE_SPINLOCK(blk_mq_cpu_notify_lock); + + static int blk_mq_main_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +@@ -25,7 +25,10 @@ + struct blk_mq_cpu_notifier *notify; + int ret = NOTIFY_OK; + +- raw_spin_lock(&blk_mq_cpu_notify_lock); ++ if (action != CPU_POST_DEAD) ++ return NOTIFY_OK; ++ ++ spin_lock(&blk_mq_cpu_notify_lock); + + list_for_each_entry(notify, &blk_mq_cpu_notify_list, list) { + ret = notify->notify(notify->data, action, cpu); +@@ -33,7 +36,7 @@ + break; + } + +- raw_spin_unlock(&blk_mq_cpu_notify_lock); ++ spin_unlock(&blk_mq_cpu_notify_lock); + return ret; + } + +@@ -41,16 +44,16 @@ + { + BUG_ON(!notifier->notify); + +- raw_spin_lock(&blk_mq_cpu_notify_lock); ++ spin_lock(&blk_mq_cpu_notify_lock); + list_add_tail(¬ifier->list, &blk_mq_cpu_notify_list); +- raw_spin_unlock(&blk_mq_cpu_notify_lock); ++ spin_unlock(&blk_mq_cpu_notify_lock); + } + + void blk_mq_unregister_cpu_notifier(struct blk_mq_cpu_notifier *notifier) + { +- raw_spin_lock(&blk_mq_cpu_notify_lock); ++ spin_lock(&blk_mq_cpu_notify_lock); + list_del(¬ifier->list); +- raw_spin_unlock(&blk_mq_cpu_notify_lock); ++ spin_unlock(&blk_mq_cpu_notify_lock); + } + + void blk_mq_init_cpu_notifier(struct blk_mq_cpu_notifier *notifier, +diff -Nur linux-3.18.14.orig/block/blk-mq.h linux-3.18.14-rt/block/blk-mq.h +--- linux-3.18.14.orig/block/blk-mq.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/block/blk-mq.h 2015-05-31 15:32:46.789635382 -0500 +@@ -73,7 +73,10 @@ + static inline struct blk_mq_ctx *__blk_mq_get_ctx(struct request_queue *q, + unsigned int cpu) + { +- return per_cpu_ptr(q->queue_ctx, cpu); ++ struct blk_mq_ctx *ctx; ++ ++ ctx = per_cpu_ptr(q->queue_ctx, cpu); ++ return ctx; + } + + /* +@@ -84,12 +87,12 @@ + */ + static inline struct blk_mq_ctx *blk_mq_get_ctx(struct request_queue *q) + { +- return __blk_mq_get_ctx(q, get_cpu()); ++ return __blk_mq_get_ctx(q, get_cpu_light()); + } + + static inline void blk_mq_put_ctx(struct blk_mq_ctx *ctx) + { +- put_cpu(); ++ put_cpu_light(); + } + + struct blk_mq_alloc_data { +diff -Nur linux-3.18.14.orig/block/blk-softirq.c linux-3.18.14-rt/block/blk-softirq.c +--- linux-3.18.14.orig/block/blk-softirq.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/block/blk-softirq.c 2015-05-31 15:32:46.789635382 -0500 +@@ -51,6 +51,7 @@ + raise_softirq_irqoff(BLOCK_SOFTIRQ); + + local_irq_restore(flags); ++ preempt_check_resched_rt(); + } + + /* +@@ -93,6 +94,7 @@ + this_cpu_ptr(&blk_cpu_done)); + raise_softirq_irqoff(BLOCK_SOFTIRQ); + local_irq_enable(); ++ preempt_check_resched_rt(); + } + + return NOTIFY_OK; +@@ -150,6 +152,7 @@ + goto do_local; + + local_irq_restore(flags); ++ preempt_check_resched_rt(); + } + + /** +diff -Nur linux-3.18.14.orig/block/bounce.c linux-3.18.14-rt/block/bounce.c +--- linux-3.18.14.orig/block/bounce.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/block/bounce.c 2015-05-31 15:32:46.793635382 -0500 +@@ -54,11 +54,11 @@ + unsigned long flags; + unsigned char *vto; + +- local_irq_save(flags); ++ local_irq_save_nort(flags); + vto = kmap_atomic(to->bv_page); + memcpy(vto + to->bv_offset, vfrom, to->bv_len); + kunmap_atomic(vto); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + } + + #else /* CONFIG_HIGHMEM */ +diff -Nur linux-3.18.14.orig/crypto/algapi.c linux-3.18.14-rt/crypto/algapi.c +--- linux-3.18.14.orig/crypto/algapi.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/crypto/algapi.c 2015-05-31 15:32:46.809635382 -0500 +@@ -698,13 +698,13 @@ + + int crypto_register_notifier(struct notifier_block *nb) + { +- return blocking_notifier_chain_register(&crypto_chain, nb); ++ return srcu_notifier_chain_register(&crypto_chain, nb); + } + EXPORT_SYMBOL_GPL(crypto_register_notifier); + + int crypto_unregister_notifier(struct notifier_block *nb) + { +- return blocking_notifier_chain_unregister(&crypto_chain, nb); ++ return srcu_notifier_chain_unregister(&crypto_chain, nb); + } + EXPORT_SYMBOL_GPL(crypto_unregister_notifier); + +diff -Nur linux-3.18.14.orig/crypto/api.c linux-3.18.14-rt/crypto/api.c +--- linux-3.18.14.orig/crypto/api.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/crypto/api.c 2015-05-31 15:32:46.861635382 -0500 +@@ -31,7 +31,7 @@ + DECLARE_RWSEM(crypto_alg_sem); + EXPORT_SYMBOL_GPL(crypto_alg_sem); + +-BLOCKING_NOTIFIER_HEAD(crypto_chain); ++SRCU_NOTIFIER_HEAD(crypto_chain); + EXPORT_SYMBOL_GPL(crypto_chain); + + static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg); +@@ -236,10 +236,10 @@ + { + int ok; + +- ok = blocking_notifier_call_chain(&crypto_chain, val, v); ++ ok = srcu_notifier_call_chain(&crypto_chain, val, v); + if (ok == NOTIFY_DONE) { + request_module("cryptomgr"); +- ok = blocking_notifier_call_chain(&crypto_chain, val, v); ++ ok = srcu_notifier_call_chain(&crypto_chain, val, v); + } + + return ok; +diff -Nur linux-3.18.14.orig/crypto/internal.h linux-3.18.14-rt/crypto/internal.h +--- linux-3.18.14.orig/crypto/internal.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/crypto/internal.h 2015-05-31 15:32:46.865635381 -0500 +@@ -48,7 +48,7 @@ + + extern struct list_head crypto_alg_list; + extern struct rw_semaphore crypto_alg_sem; +-extern struct blocking_notifier_head crypto_chain; ++extern struct srcu_notifier_head crypto_chain; + + #ifdef CONFIG_PROC_FS + void __init crypto_init_proc(void); +@@ -142,7 +142,7 @@ + + static inline void crypto_notify(unsigned long val, void *v) + { +- blocking_notifier_call_chain(&crypto_chain, val, v); ++ srcu_notifier_call_chain(&crypto_chain, val, v); + } + + #endif /* _CRYPTO_INTERNAL_H */ +diff -Nur linux-3.18.14.orig/Documentation/hwlat_detector.txt linux-3.18.14-rt/Documentation/hwlat_detector.txt +--- linux-3.18.14.orig/Documentation/hwlat_detector.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/Documentation/hwlat_detector.txt 2015-05-31 15:32:45.457635394 -0500 +@@ -0,0 +1,64 @@ ++Introduction: ++------------- ++ ++The module hwlat_detector is a special purpose kernel module that is used to ++detect large system latencies induced by the behavior of certain underlying ++hardware or firmware, independent of Linux itself. The code was developed ++originally to detect SMIs (System Management Interrupts) on x86 systems, ++however there is nothing x86 specific about this patchset. It was ++originally written for use by the "RT" patch since the Real Time ++kernel is highly latency sensitive. ++ ++SMIs are usually not serviced by the Linux kernel, which typically does not ++even know that they are occuring. SMIs are instead are set up by BIOS code ++and are serviced by BIOS code, usually for "critical" events such as ++management of thermal sensors and fans. Sometimes though, SMIs are used for ++other tasks and those tasks can spend an inordinate amount of time in the ++handler (sometimes measured in milliseconds). Obviously this is a problem if ++you are trying to keep event service latencies down in the microsecond range. ++ ++The hardware latency detector works by hogging all of the cpus for configurable ++amounts of time (by calling stop_machine()), polling the CPU Time Stamp Counter ++for some period, then looking for gaps in the TSC data. Any gap indicates a ++time when the polling was interrupted and since the machine is stopped and ++interrupts turned off the only thing that could do that would be an SMI. ++ ++Note that the SMI detector should *NEVER* be used in a production environment. ++It is intended to be run manually to determine if the hardware platform has a ++problem with long system firmware service routines. ++ ++Usage: ++------ ++ ++Loading the module hwlat_detector passing the parameter "enabled=1" (or by ++setting the "enable" entry in "hwlat_detector" debugfs toggled on) is the only ++step required to start the hwlat_detector. It is possible to redefine the ++threshold in microseconds (us) above which latency spikes will be taken ++into account (parameter "threshold="). ++ ++Example: ++ ++ # modprobe hwlat_detector enabled=1 threshold=100 ++ ++After the module is loaded, it creates a directory named "hwlat_detector" under ++the debugfs mountpoint, "/debug/hwlat_detector" for this text. It is necessary ++to have debugfs mounted, which might be on /sys/debug on your system. ++ ++The /debug/hwlat_detector interface contains the following files: ++ ++count - number of latency spikes observed since last reset ++enable - a global enable/disable toggle (0/1), resets count ++max - maximum hardware latency actually observed (usecs) ++sample - a pipe from which to read current raw sample data ++ in the format ++ (can be opened O_NONBLOCK for a single sample) ++threshold - minimum latency value to be considered (usecs) ++width - time period to sample with CPUs held (usecs) ++ must be less than the total window size (enforced) ++window - total period of sampling, width being inside (usecs) ++ ++By default we will set width to 500,000 and window to 1,000,000, meaning that ++we will sample every 1,000,000 usecs (1s) for 500,000 usecs (0.5s). If we ++observe any latencies that exceed the threshold (initially 100 usecs), ++then we write to a global sample ring buffer of 8K samples, which is ++consumed by reading from the "sample" (pipe) debugfs file interface. +diff -Nur linux-3.18.14.orig/Documentation/sysrq.txt linux-3.18.14-rt/Documentation/sysrq.txt +--- linux-3.18.14.orig/Documentation/sysrq.txt 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/Documentation/sysrq.txt 2015-05-31 15:32:45.461635394 -0500 +@@ -59,10 +59,17 @@ + On other - If you know of the key combos for other architectures, please + let me know so I can add them to this section. + +-On all - write a character to /proc/sysrq-trigger. e.g.: +- ++On all - write a character to /proc/sysrq-trigger, e.g.: + echo t > /proc/sysrq-trigger + ++On all - Enable network SysRq by writing a cookie to icmp_echo_sysrq, e.g. ++ echo 0x01020304 >/proc/sys/net/ipv4/icmp_echo_sysrq ++ Send an ICMP echo request with this pattern plus the particular ++ SysRq command key. Example: ++ # ping -c1 -s57 -p0102030468 ++ will trigger the SysRq-H (help) command. ++ ++ + * What are the 'command' keys? + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 'b' - Will immediately reboot the system without syncing or unmounting +diff -Nur linux-3.18.14.orig/Documentation/trace/histograms.txt linux-3.18.14-rt/Documentation/trace/histograms.txt +--- linux-3.18.14.orig/Documentation/trace/histograms.txt 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/Documentation/trace/histograms.txt 2015-05-31 15:32:45.461635394 -0500 +@@ -0,0 +1,186 @@ ++ Using the Linux Kernel Latency Histograms ++ ++ ++This document gives a short explanation how to enable, configure and use ++latency histograms. Latency histograms are primarily relevant in the ++context of real-time enabled kernels (CONFIG_PREEMPT/CONFIG_PREEMPT_RT) ++and are used in the quality management of the Linux real-time ++capabilities. ++ ++ ++* Purpose of latency histograms ++ ++A latency histogram continuously accumulates the frequencies of latency ++data. There are two types of histograms ++- potential sources of latencies ++- effective latencies ++ ++ ++* Potential sources of latencies ++ ++Potential sources of latencies are code segments where interrupts, ++preemption or both are disabled (aka critical sections). To create ++histograms of potential sources of latency, the kernel stores the time ++stamp at the start of a critical section, determines the time elapsed ++when the end of the section is reached, and increments the frequency ++counter of that latency value - irrespective of whether any concurrently ++running process is affected by latency or not. ++- Configuration items (in the Kernel hacking/Tracers submenu) ++ CONFIG_INTERRUPT_OFF_LATENCY ++ CONFIG_PREEMPT_OFF_LATENCY ++ ++ ++* Effective latencies ++ ++Effective latencies are actually occuring during wakeup of a process. To ++determine effective latencies, the kernel stores the time stamp when a ++process is scheduled to be woken up, and determines the duration of the ++wakeup time shortly before control is passed over to this process. Note ++that the apparent latency in user space may be somewhat longer, since the ++process may be interrupted after control is passed over to it but before ++the execution in user space takes place. Simply measuring the interval ++between enqueuing and wakeup may also not appropriate in cases when a ++process is scheduled as a result of a timer expiration. The timer may have ++missed its deadline, e.g. due to disabled interrupts, but this latency ++would not be registered. Therefore, the offsets of missed timers are ++recorded in a separate histogram. If both wakeup latency and missed timer ++offsets are configured and enabled, a third histogram may be enabled that ++records the overall latency as a sum of the timer latency, if any, and the ++wakeup latency. This histogram is called "timerandwakeup". ++- Configuration items (in the Kernel hacking/Tracers submenu) ++ CONFIG_WAKEUP_LATENCY ++ CONFIG_MISSED_TIMER_OFSETS ++ ++ ++* Usage ++ ++The interface to the administration of the latency histograms is located ++in the debugfs file system. To mount it, either enter ++ ++mount -t sysfs nodev /sys ++mount -t debugfs nodev /sys/kernel/debug ++ ++from shell command line level, or add ++ ++nodev /sys sysfs defaults 0 0 ++nodev /sys/kernel/debug debugfs defaults 0 0 ++ ++to the file /etc/fstab. All latency histogram related files are then ++available in the directory /sys/kernel/debug/tracing/latency_hist. A ++particular histogram type is enabled by writing non-zero to the related ++variable in the /sys/kernel/debug/tracing/latency_hist/enable directory. ++Select "preemptirqsoff" for the histograms of potential sources of ++latencies and "wakeup" for histograms of effective latencies etc. The ++histogram data - one per CPU - are available in the files ++ ++/sys/kernel/debug/tracing/latency_hist/preemptoff/CPUx ++/sys/kernel/debug/tracing/latency_hist/irqsoff/CPUx ++/sys/kernel/debug/tracing/latency_hist/preemptirqsoff/CPUx ++/sys/kernel/debug/tracing/latency_hist/wakeup/CPUx ++/sys/kernel/debug/tracing/latency_hist/wakeup/sharedprio/CPUx ++/sys/kernel/debug/tracing/latency_hist/missed_timer_offsets/CPUx ++/sys/kernel/debug/tracing/latency_hist/timerandwakeup/CPUx ++ ++The histograms are reset by writing non-zero to the file "reset" in a ++particular latency directory. To reset all latency data, use ++ ++#!/bin/sh ++ ++TRACINGDIR=/sys/kernel/debug/tracing ++HISTDIR=$TRACINGDIR/latency_hist ++ ++if test -d $HISTDIR ++then ++ cd $HISTDIR ++ for i in `find . | grep /reset$` ++ do ++ echo 1 >$i ++ done ++fi ++ ++ ++* Data format ++ ++Latency data are stored with a resolution of one microsecond. The ++maximum latency is 10,240 microseconds. The data are only valid, if the ++overflow register is empty. Every output line contains the latency in ++microseconds in the first row and the number of samples in the second ++row. To display only lines with a positive latency count, use, for ++example, ++ ++grep -v " 0$" /sys/kernel/debug/tracing/latency_hist/preemptoff/CPU0 ++ ++#Minimum latency: 0 microseconds. ++#Average latency: 0 microseconds. ++#Maximum latency: 25 microseconds. ++#Total samples: 3104770694 ++#There are 0 samples greater or equal than 10240 microseconds ++#usecs samples ++ 0 2984486876 ++ 1 49843506 ++ 2 58219047 ++ 3 5348126 ++ 4 2187960 ++ 5 3388262 ++ 6 959289 ++ 7 208294 ++ 8 40420 ++ 9 4485 ++ 10 14918 ++ 11 18340 ++ 12 25052 ++ 13 19455 ++ 14 5602 ++ 15 969 ++ 16 47 ++ 17 18 ++ 18 14 ++ 19 1 ++ 20 3 ++ 21 2 ++ 22 5 ++ 23 2 ++ 25 1 ++ ++ ++* Wakeup latency of a selected process ++ ++To only collect wakeup latency data of a particular process, write the ++PID of the requested process to ++ ++/sys/kernel/debug/tracing/latency_hist/wakeup/pid ++ ++PIDs are not considered, if this variable is set to 0. ++ ++ ++* Details of the process with the highest wakeup latency so far ++ ++Selected data of the process that suffered from the highest wakeup ++latency that occurred in a particular CPU are available in the file ++ ++/sys/kernel/debug/tracing/latency_hist/wakeup/max_latency-CPUx. ++ ++In addition, other relevant system data at the time when the ++latency occurred are given. ++ ++The format of the data is (all in one line): ++ () \ ++<- ++ ++The value of is only relevant in the combined timer ++and wakeup latency recording. In the wakeup recording, it is ++always 0, in the missed_timer_offsets recording, it is the same ++as . ++ ++When retrospectively searching for the origin of a latency and ++tracing was not enabled, it may be helpful to know the name and ++some basic data of the task that (finally) was switching to the ++late real-tlme task. In addition to the victim's data, also the ++data of the possible culprit are therefore displayed after the ++"<-" symbol. ++ ++Finally, the timestamp of the time when the latency occurred ++in . after the most recent system boot ++is provided. ++ ++These data are also reset when the wakeup histogram is reset. +diff -Nur linux-3.18.14.orig/drivers/acpi/acpica/acglobal.h linux-3.18.14-rt/drivers/acpi/acpica/acglobal.h +--- linux-3.18.14.orig/drivers/acpi/acpica/acglobal.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/acpi/acpica/acglobal.h 2015-05-31 15:32:46.885635381 -0500 +@@ -112,7 +112,7 @@ + * interrupt level + */ + ACPI_GLOBAL(acpi_spinlock, acpi_gbl_gpe_lock); /* For GPE data structs and registers */ +-ACPI_GLOBAL(acpi_spinlock, acpi_gbl_hardware_lock); /* For ACPI H/W except GPE registers */ ++ACPI_GLOBAL(acpi_raw_spinlock, acpi_gbl_hardware_lock); /* For ACPI H/W except GPE registers */ + ACPI_GLOBAL(acpi_spinlock, acpi_gbl_reference_count_lock); + + /* Mutex for _OSI support */ +diff -Nur linux-3.18.14.orig/drivers/acpi/acpica/hwregs.c linux-3.18.14-rt/drivers/acpi/acpica/hwregs.c +--- linux-3.18.14.orig/drivers/acpi/acpica/hwregs.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/acpi/acpica/hwregs.c 2015-05-31 15:32:46.929635381 -0500 +@@ -269,14 +269,14 @@ + ACPI_BITMASK_ALL_FIXED_STATUS, + ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address))); + +- lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); ++ raw_spin_lock_irqsave(acpi_gbl_hardware_lock, lock_flags); + + /* Clear the fixed events in PM1 A/B */ + + status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, + ACPI_BITMASK_ALL_FIXED_STATUS); + +- acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); ++ raw_spin_unlock_irqrestore(acpi_gbl_hardware_lock, lock_flags); + + if (ACPI_FAILURE(status)) { + goto exit; +diff -Nur linux-3.18.14.orig/drivers/acpi/acpica/hwxface.c linux-3.18.14-rt/drivers/acpi/acpica/hwxface.c +--- linux-3.18.14.orig/drivers/acpi/acpica/hwxface.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/acpi/acpica/hwxface.c 2015-05-31 15:32:46.973635380 -0500 +@@ -374,7 +374,7 @@ + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + +- lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); ++ raw_spin_lock_irqsave(acpi_gbl_hardware_lock, lock_flags); + + /* + * At this point, we know that the parent register is one of the +@@ -435,7 +435,7 @@ + + unlock_and_exit: + +- acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); ++ raw_spin_unlock_irqrestore(acpi_gbl_hardware_lock, lock_flags); + return_ACPI_STATUS(status); + } + +diff -Nur linux-3.18.14.orig/drivers/acpi/acpica/utmutex.c linux-3.18.14-rt/drivers/acpi/acpica/utmutex.c +--- linux-3.18.14.orig/drivers/acpi/acpica/utmutex.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/acpi/acpica/utmutex.c 2015-05-31 15:32:46.973635380 -0500 +@@ -88,7 +88,7 @@ + return_ACPI_STATUS (status); + } + +- status = acpi_os_create_lock (&acpi_gbl_hardware_lock); ++ status = acpi_os_create_raw_lock (&acpi_gbl_hardware_lock); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } +@@ -141,7 +141,7 @@ + /* Delete the spinlocks */ + + acpi_os_delete_lock(acpi_gbl_gpe_lock); +- acpi_os_delete_lock(acpi_gbl_hardware_lock); ++ acpi_os_delete_raw_lock(acpi_gbl_hardware_lock); + acpi_os_delete_lock(acpi_gbl_reference_count_lock); + + /* Delete the reader/writer lock */ +diff -Nur linux-3.18.14.orig/drivers/ata/libata-sff.c linux-3.18.14-rt/drivers/ata/libata-sff.c +--- linux-3.18.14.orig/drivers/ata/libata-sff.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/ata/libata-sff.c 2015-05-31 15:32:46.993635380 -0500 +@@ -678,9 +678,9 @@ + unsigned long flags; + unsigned int consumed; + +- local_irq_save(flags); ++ local_irq_save_nort(flags); + consumed = ata_sff_data_xfer32(dev, buf, buflen, rw); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + return consumed; + } +@@ -719,7 +719,7 @@ + unsigned long flags; + + /* FIXME: use a bounce buffer */ +- local_irq_save(flags); ++ local_irq_save_nort(flags); + buf = kmap_atomic(page); + + /* do the actual data transfer */ +@@ -727,7 +727,7 @@ + do_write); + + kunmap_atomic(buf); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + } else { + buf = page_address(page); + ap->ops->sff_data_xfer(qc->dev, buf + offset, qc->sect_size, +@@ -864,7 +864,7 @@ + unsigned long flags; + + /* FIXME: use bounce buffer */ +- local_irq_save(flags); ++ local_irq_save_nort(flags); + buf = kmap_atomic(page); + + /* do the actual data transfer */ +@@ -872,7 +872,7 @@ + count, rw); + + kunmap_atomic(buf); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + } else { + buf = page_address(page); + consumed = ap->ops->sff_data_xfer(dev, buf + offset, +diff -Nur linux-3.18.14.orig/drivers/char/random.c linux-3.18.14-rt/drivers/char/random.c +--- linux-3.18.14.orig/drivers/char/random.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/char/random.c 2015-05-31 15:32:47.013635380 -0500 +@@ -776,8 +776,6 @@ + } sample; + long delta, delta2, delta3; + +- preempt_disable(); +- + sample.jiffies = jiffies; + sample.cycles = random_get_entropy(); + sample.num = num; +@@ -818,7 +816,6 @@ + */ + credit_entropy_bits(r, min_t(int, fls(delta>>1), 11)); + } +- preempt_enable(); + } + + void add_input_randomness(unsigned int type, unsigned int code, +@@ -871,28 +868,27 @@ + return *(ptr + f->reg_idx++); + } + +-void add_interrupt_randomness(int irq, int irq_flags) ++void add_interrupt_randomness(int irq, int irq_flags, __u64 ip) + { + struct entropy_store *r; + struct fast_pool *fast_pool = this_cpu_ptr(&irq_randomness); +- struct pt_regs *regs = get_irq_regs(); + unsigned long now = jiffies; + cycles_t cycles = random_get_entropy(); + __u32 c_high, j_high; +- __u64 ip; + unsigned long seed; + int credit = 0; + + if (cycles == 0) +- cycles = get_reg(fast_pool, regs); ++ cycles = get_reg(fast_pool, NULL); + c_high = (sizeof(cycles) > 4) ? cycles >> 32 : 0; + j_high = (sizeof(now) > 4) ? now >> 32 : 0; + fast_pool->pool[0] ^= cycles ^ j_high ^ irq; + fast_pool->pool[1] ^= now ^ c_high; +- ip = regs ? instruction_pointer(regs) : _RET_IP_; ++ if (!ip) ++ ip = _RET_IP_; + fast_pool->pool[2] ^= ip; + fast_pool->pool[3] ^= (sizeof(ip) > 4) ? ip >> 32 : +- get_reg(fast_pool, regs); ++ get_reg(fast_pool, NULL); + + fast_mix(fast_pool); + add_interrupt_bench(cycles); +diff -Nur linux-3.18.14.orig/drivers/clocksource/tcb_clksrc.c linux-3.18.14-rt/drivers/clocksource/tcb_clksrc.c +--- linux-3.18.14.orig/drivers/clocksource/tcb_clksrc.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/clocksource/tcb_clksrc.c 2015-05-31 15:32:47.025635380 -0500 +@@ -23,8 +23,7 @@ + * this 32 bit free-running counter. the second channel is not used. + * + * - The third channel may be used to provide a 16-bit clockevent +- * source, used in either periodic or oneshot mode. This runs +- * at 32 KiHZ, and can handle delays of up to two seconds. ++ * source, used in either periodic or oneshot mode. + * + * A boot clocksource and clockevent source are also currently needed, + * unless the relevant platforms (ARM/AT91, AVR32/AT32) are changed so +@@ -74,6 +73,7 @@ + struct tc_clkevt_device { + struct clock_event_device clkevt; + struct clk *clk; ++ u32 freq; + void __iomem *regs; + }; + +@@ -82,13 +82,6 @@ + return container_of(clkevt, struct tc_clkevt_device, clkevt); + } + +-/* For now, we always use the 32K clock ... this optimizes for NO_HZ, +- * because using one of the divided clocks would usually mean the +- * tick rate can never be less than several dozen Hz (vs 0.5 Hz). +- * +- * A divided clock could be good for high resolution timers, since +- * 30.5 usec resolution can seem "low". +- */ + static u32 timer_clock; + + static void tc_mode(enum clock_event_mode m, struct clock_event_device *d) +@@ -111,11 +104,12 @@ + case CLOCK_EVT_MODE_PERIODIC: + clk_enable(tcd->clk); + +- /* slow clock, count up to RC, then irq and restart */ ++ /* count up to RC, then irq and restart */ + __raw_writel(timer_clock + | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, + regs + ATMEL_TC_REG(2, CMR)); +- __raw_writel((32768 + HZ/2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); ++ __raw_writel((tcd->freq + HZ / 2) / HZ, ++ tcaddr + ATMEL_TC_REG(2, RC)); + + /* Enable clock and interrupts on RC compare */ + __raw_writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); +@@ -128,7 +122,7 @@ + case CLOCK_EVT_MODE_ONESHOT: + clk_enable(tcd->clk); + +- /* slow clock, count up to RC, then irq and stop */ ++ /* count up to RC, then irq and stop */ + __raw_writel(timer_clock | ATMEL_TC_CPCSTOP + | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, + regs + ATMEL_TC_REG(2, CMR)); +@@ -157,8 +151,12 @@ + .name = "tc_clkevt", + .features = CLOCK_EVT_FEAT_PERIODIC + | CLOCK_EVT_FEAT_ONESHOT, ++#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK + /* Should be lower than at91rm9200's system timer */ + .rating = 125, ++#else ++ .rating = 200, ++#endif + .set_next_event = tc_next_event, + .set_mode = tc_mode, + }, +@@ -178,8 +176,9 @@ + return IRQ_NONE; + } + +-static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) ++static int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) + { ++ unsigned divisor = atmel_tc_divisors[divisor_idx]; + int ret; + struct clk *t2_clk = tc->clk[2]; + int irq = tc->irq[2]; +@@ -193,7 +192,11 @@ + clkevt.regs = tc->regs; + clkevt.clk = t2_clk; + +- timer_clock = clk32k_divisor_idx; ++ timer_clock = divisor_idx; ++ if (!divisor) ++ clkevt.freq = 32768; ++ else ++ clkevt.freq = clk_get_rate(t2_clk) / divisor; + + clkevt.clkevt.cpumask = cpumask_of(0); + +@@ -203,7 +206,7 @@ + return ret; + } + +- clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); ++ clockevents_config_and_register(&clkevt.clkevt, clkevt.freq, 1, 0xffff); + + return ret; + } +@@ -340,7 +343,11 @@ + goto err_disable_t1; + + /* channel 2: periodic and oneshot timer support */ ++#ifdef CONFIG_ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK + ret = setup_clkevents(tc, clk32k_divisor_idx); ++#else ++ ret = setup_clkevents(tc, best_divisor_idx); ++#endif + if (ret) + goto err_unregister_clksrc; + +diff -Nur linux-3.18.14.orig/drivers/clocksource/timer-atmel-pit.c linux-3.18.14-rt/drivers/clocksource/timer-atmel-pit.c +--- linux-3.18.14.orig/drivers/clocksource/timer-atmel-pit.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/clocksource/timer-atmel-pit.c 2015-05-31 15:32:47.025635380 -0500 +@@ -90,6 +90,7 @@ + return elapsed; + } + ++static struct irqaction at91sam926x_pit_irq; + /* + * Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16) + */ +@@ -100,6 +101,8 @@ + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: ++ /* Set up irq handler */ ++ setup_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq); + /* update clocksource counter */ + data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); + pit_write(data->base, AT91_PIT_MR, +@@ -113,6 +116,7 @@ + /* disable irq, leaving the clocksource active */ + pit_write(data->base, AT91_PIT_MR, + (data->cycle - 1) | AT91_PIT_PITEN); ++ remove_irq(at91sam926x_pit_irq.irq, &at91sam926x_pit_irq); + break; + case CLOCK_EVT_MODE_RESUME: + break; +diff -Nur linux-3.18.14.orig/drivers/cpufreq/Kconfig.x86 linux-3.18.14-rt/drivers/cpufreq/Kconfig.x86 +--- linux-3.18.14.orig/drivers/cpufreq/Kconfig.x86 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/cpufreq/Kconfig.x86 2015-05-31 15:32:47.065635380 -0500 +@@ -113,7 +113,7 @@ + + config X86_POWERNOW_K8 + tristate "AMD Opteron/Athlon64 PowerNow!" +- depends on ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ ++ depends on ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ && !PREEMPT_RT_BASE + help + This adds the CPUFreq driver for K8/early Opteron/Athlon64 processors. + Support for K10 and newer processors is now in acpi-cpufreq. +diff -Nur linux-3.18.14.orig/drivers/gpio/gpio-omap.c linux-3.18.14-rt/drivers/gpio/gpio-omap.c +--- linux-3.18.14.orig/drivers/gpio/gpio-omap.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/gpio/gpio-omap.c 2015-05-31 15:32:47.073635379 -0500 +@@ -57,7 +57,7 @@ + u32 saved_datain; + u32 level_mask; + u32 toggle_mask; +- spinlock_t lock; ++ raw_spinlock_t lock; + struct gpio_chip chip; + struct clk *dbck; + u32 mod_usage; +@@ -503,19 +503,19 @@ + (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))) + return -EINVAL; + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + offset = GPIO_INDEX(bank, gpio); + retval = omap_set_gpio_triggering(bank, offset, type); + if (!LINE_USED(bank->mod_usage, offset)) { + omap_enable_gpio_module(bank, offset); + omap_set_gpio_direction(bank, offset, 1); + } else if (!omap_gpio_is_input(bank, BIT(offset))) { +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + return -EINVAL; + } + + bank->irq_usage |= BIT(GPIO_INDEX(bank, gpio)); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) + __irq_set_handler_locked(d->irq, handle_level_irq); +@@ -633,14 +633,14 @@ + return -EINVAL; + } + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + if (enable) + bank->context.wake_en |= gpio_bit; + else + bank->context.wake_en &= ~gpio_bit; + + writel_relaxed(bank->context.wake_en, bank->base + bank->regs->wkup_en); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; + } +@@ -675,7 +675,7 @@ + if (!BANK_USED(bank)) + pm_runtime_get_sync(bank->dev); + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + /* Set trigger to none. You need to enable the desired trigger with + * request_irq() or set_irq_type(). Only do this if the IRQ line has + * not already been requested. +@@ -685,7 +685,7 @@ + omap_enable_gpio_module(bank, offset); + } + bank->mod_usage |= BIT(offset); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; + } +@@ -695,11 +695,11 @@ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + unsigned long flags; + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + bank->mod_usage &= ~(BIT(offset)); + omap_disable_gpio_module(bank, offset); + omap_reset_gpio(bank, bank->chip.base + offset); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + + /* + * If this is the last gpio to be freed in the bank, +@@ -799,12 +799,12 @@ + unsigned long flags; + unsigned offset = GPIO_INDEX(bank, gpio); + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + gpio_unlock_as_irq(&bank->chip, offset); + bank->irq_usage &= ~(BIT(offset)); + omap_disable_gpio_module(bank, offset); + omap_reset_gpio(bank, gpio); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + + /* + * If this is the last IRQ to be freed in the bank, +@@ -828,10 +828,10 @@ + unsigned int gpio = omap_irq_to_gpio(bank, d->hwirq); + unsigned long flags; + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + omap_set_gpio_irqenable(bank, gpio, 0); + omap_set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), IRQ_TYPE_NONE); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + } + + static void omap_gpio_unmask_irq(struct irq_data *d) +@@ -842,7 +842,7 @@ + u32 trigger = irqd_get_trigger_type(d); + unsigned long flags; + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + if (trigger) + omap_set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), trigger); + +@@ -854,7 +854,7 @@ + } + + omap_set_gpio_irqenable(bank, gpio, 1); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + } + + /*---------------------------------------------------------------------*/ +@@ -867,9 +867,9 @@ + OMAP_MPUIO_GPIO_MASKIT / bank->stride; + unsigned long flags; + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + writel_relaxed(0xffff & ~bank->context.wake_en, mask_reg); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; + } +@@ -882,9 +882,9 @@ + OMAP_MPUIO_GPIO_MASKIT / bank->stride; + unsigned long flags; + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + writel_relaxed(bank->context.wake_en, mask_reg); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; + } +@@ -930,9 +930,9 @@ + + bank = container_of(chip, struct gpio_bank, chip); + reg = bank->base + bank->regs->direction; +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + dir = !!(readl_relaxed(reg) & BIT(offset)); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + return dir; + } + +@@ -942,9 +942,9 @@ + unsigned long flags; + + bank = container_of(chip, struct gpio_bank, chip); +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + omap_set_gpio_direction(bank, offset, 1); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + return 0; + } + +@@ -968,10 +968,10 @@ + unsigned long flags; + + bank = container_of(chip, struct gpio_bank, chip); +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + bank->set_dataout(bank, offset, value); + omap_set_gpio_direction(bank, offset, 0); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + return 0; + } + +@@ -983,9 +983,9 @@ + + bank = container_of(chip, struct gpio_bank, chip); + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + omap2_set_gpio_debounce(bank, offset, debounce); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; + } +@@ -996,9 +996,9 @@ + unsigned long flags; + + bank = container_of(chip, struct gpio_bank, chip); +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + bank->set_dataout(bank, offset, value); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + } + + /*---------------------------------------------------------------------*/ +@@ -1223,7 +1223,7 @@ + else + bank->set_dataout = omap_set_gpio_dataout_mask; + +- spin_lock_init(&bank->lock); ++ raw_spin_lock_init(&bank->lock); + + /* Static mapping, never released */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +@@ -1270,7 +1270,7 @@ + unsigned long flags; + u32 wake_low, wake_hi; + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + + /* + * Only edges can generate a wakeup event to the PRCM. +@@ -1323,7 +1323,7 @@ + bank->get_context_loss_count(bank->dev); + + omap_gpio_dbck_disable(bank); +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; + } +@@ -1338,7 +1338,7 @@ + unsigned long flags; + int c; + +- spin_lock_irqsave(&bank->lock, flags); ++ raw_spin_lock_irqsave(&bank->lock, flags); + + /* + * On the first resume during the probe, the context has not +@@ -1374,14 +1374,14 @@ + if (c != bank->context_loss_count) { + omap_gpio_restore_context(bank); + } else { +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + return 0; + } + } + } + + if (!bank->workaround_enabled) { +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + return 0; + } + +@@ -1436,7 +1436,7 @@ + } + + bank->workaround_enabled = false; +- spin_unlock_irqrestore(&bank->lock, flags); ++ raw_spin_unlock_irqrestore(&bank->lock, flags); + + return 0; + } +diff -Nur linux-3.18.14.orig/drivers/gpu/drm/i915/i915_gem.c linux-3.18.14-rt/drivers/gpu/drm/i915/i915_gem.c +--- linux-3.18.14.orig/drivers/gpu/drm/i915/i915_gem.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/gpu/drm/i915/i915_gem.c 2015-05-31 15:32:47.081635379 -0500 +@@ -5144,7 +5144,7 @@ + if (!mutex_is_locked(mutex)) + return false; + +-#if defined(CONFIG_SMP) && !defined(CONFIG_DEBUG_MUTEXES) ++#if defined(CONFIG_SMP) && !defined(CONFIG_DEBUG_MUTEXES) && !defined(CONFIG_PREEMPT_RT_BASE) + return mutex->owner == task; + #else + /* Since UP may be pre-empted, we cannot assume that we own the lock */ +diff -Nur linux-3.18.14.orig/drivers/gpu/drm/i915/i915_gem_execbuffer.c linux-3.18.14-rt/drivers/gpu/drm/i915/i915_gem_execbuffer.c +--- linux-3.18.14.orig/drivers/gpu/drm/i915/i915_gem_execbuffer.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/gpu/drm/i915/i915_gem_execbuffer.c 2015-05-31 15:32:47.121635379 -0500 +@@ -1170,7 +1170,9 @@ + return ret; + } + ++#ifndef CONFIG_PREEMPT_RT_BASE + trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags); ++#endif + + i915_gem_execbuffer_move_to_active(vmas, ring); + i915_gem_execbuffer_retire_commands(dev, file, ring, batch_obj); +diff -Nur linux-3.18.14.orig/drivers/i2c/busses/i2c-omap.c linux-3.18.14-rt/drivers/i2c/busses/i2c-omap.c +--- linux-3.18.14.orig/drivers/i2c/busses/i2c-omap.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/i2c/busses/i2c-omap.c 2015-05-31 15:32:47.125635379 -0500 +@@ -875,15 +875,12 @@ + u16 mask; + u16 stat; + +- spin_lock(&dev->lock); +- mask = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); + stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); ++ mask = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); + + if (stat & mask) + ret = IRQ_WAKE_THREAD; + +- spin_unlock(&dev->lock); +- + return ret; + } + +diff -Nur linux-3.18.14.orig/drivers/ide/alim15x3.c linux-3.18.14-rt/drivers/ide/alim15x3.c +--- linux-3.18.14.orig/drivers/ide/alim15x3.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/ide/alim15x3.c 2015-05-31 15:32:47.137635379 -0500 +@@ -234,7 +234,7 @@ + + isa_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); + +- local_irq_save(flags); ++ local_irq_save_nort(flags); + + if (m5229_revision < 0xC2) { + /* +@@ -325,7 +325,7 @@ + } + pci_dev_put(north); + pci_dev_put(isa_dev); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + return 0; + } + +diff -Nur linux-3.18.14.orig/drivers/ide/hpt366.c linux-3.18.14-rt/drivers/ide/hpt366.c +--- linux-3.18.14.orig/drivers/ide/hpt366.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/ide/hpt366.c 2015-05-31 15:32:47.169635379 -0500 +@@ -1241,7 +1241,7 @@ + + dma_old = inb(base + 2); + +- local_irq_save(flags); ++ local_irq_save_nort(flags); + + dma_new = dma_old; + pci_read_config_byte(dev, hwif->channel ? 0x4b : 0x43, &masterdma); +@@ -1252,7 +1252,7 @@ + if (dma_new != dma_old) + outb(dma_new, base + 2); + +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx\n", + hwif->name, base, base + 7); +diff -Nur linux-3.18.14.orig/drivers/ide/ide-io.c linux-3.18.14-rt/drivers/ide/ide-io.c +--- linux-3.18.14.orig/drivers/ide/ide-io.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/ide/ide-io.c 2015-05-31 15:32:47.169635379 -0500 +@@ -659,7 +659,7 @@ + /* disable_irq_nosync ?? */ + disable_irq(hwif->irq); + /* local CPU only, as if we were handling an interrupt */ +- local_irq_disable(); ++ local_irq_disable_nort(); + if (hwif->polling) { + startstop = handler(drive); + } else if (drive_is_ready(drive)) { +diff -Nur linux-3.18.14.orig/drivers/ide/ide-iops.c linux-3.18.14-rt/drivers/ide/ide-iops.c +--- linux-3.18.14.orig/drivers/ide/ide-iops.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/ide/ide-iops.c 2015-05-31 15:32:47.185635379 -0500 +@@ -129,12 +129,12 @@ + if ((stat & ATA_BUSY) == 0) + break; + +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + *rstat = stat; + return -EBUSY; + } + } +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + } + /* + * Allow status to settle, then read it again. +diff -Nur linux-3.18.14.orig/drivers/ide/ide-io-std.c linux-3.18.14-rt/drivers/ide/ide-io-std.c +--- linux-3.18.14.orig/drivers/ide/ide-io-std.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/ide/ide-io-std.c 2015-05-31 15:32:47.169635379 -0500 +@@ -175,7 +175,7 @@ + unsigned long uninitialized_var(flags); + + if ((io_32bit & 2) && !mmio) { +- local_irq_save(flags); ++ local_irq_save_nort(flags); + ata_vlb_sync(io_ports->nsect_addr); + } + +@@ -186,7 +186,7 @@ + insl(data_addr, buf, words); + + if ((io_32bit & 2) && !mmio) +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + if (((len + 1) & 3) < 2) + return; +@@ -219,7 +219,7 @@ + unsigned long uninitialized_var(flags); + + if ((io_32bit & 2) && !mmio) { +- local_irq_save(flags); ++ local_irq_save_nort(flags); + ata_vlb_sync(io_ports->nsect_addr); + } + +@@ -230,7 +230,7 @@ + outsl(data_addr, buf, words); + + if ((io_32bit & 2) && !mmio) +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + if (((len + 1) & 3) < 2) + return; +diff -Nur linux-3.18.14.orig/drivers/ide/ide-probe.c linux-3.18.14-rt/drivers/ide/ide-probe.c +--- linux-3.18.14.orig/drivers/ide/ide-probe.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/ide/ide-probe.c 2015-05-31 15:32:47.185635379 -0500 +@@ -196,10 +196,10 @@ + int bswap = 1; + + /* local CPU only; some systems need this */ +- local_irq_save(flags); ++ local_irq_save_nort(flags); + /* read 512 bytes of id info */ + hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + drive->dev_flags |= IDE_DFLAG_ID_READ; + #ifdef DEBUG +diff -Nur linux-3.18.14.orig/drivers/ide/ide-taskfile.c linux-3.18.14-rt/drivers/ide/ide-taskfile.c +--- linux-3.18.14.orig/drivers/ide/ide-taskfile.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/ide/ide-taskfile.c 2015-05-31 15:32:47.185635379 -0500 +@@ -250,7 +250,7 @@ + + page_is_high = PageHighMem(page); + if (page_is_high) +- local_irq_save(flags); ++ local_irq_save_nort(flags); + + buf = kmap_atomic(page) + offset; + +@@ -271,7 +271,7 @@ + kunmap_atomic(buf); + + if (page_is_high) +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + len -= nr_bytes; + } +@@ -414,7 +414,7 @@ + } + + if ((drive->dev_flags & IDE_DFLAG_UNMASK) == 0) +- local_irq_disable(); ++ local_irq_disable_nort(); + + ide_set_handler(drive, &task_pio_intr, WAIT_WORSTCASE); + +diff -Nur linux-3.18.14.orig/drivers/infiniband/ulp/ipoib/ipoib_multicast.c linux-3.18.14-rt/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +--- linux-3.18.14.orig/drivers/infiniband/ulp/ipoib/ipoib_multicast.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/infiniband/ulp/ipoib/ipoib_multicast.c 2015-05-31 15:32:47.205635378 -0500 +@@ -796,7 +796,7 @@ + + ipoib_mcast_stop_thread(dev, 0); + +- local_irq_save(flags); ++ local_irq_save_nort(flags); + netif_addr_lock(dev); + spin_lock(&priv->lock); + +@@ -878,7 +878,7 @@ + + spin_unlock(&priv->lock); + netif_addr_unlock(dev); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + /* We have to cancel outside of the spinlock */ + list_for_each_entry_safe(mcast, tmcast, &remove_list, list) { +diff -Nur linux-3.18.14.orig/drivers/input/gameport/gameport.c linux-3.18.14-rt/drivers/input/gameport/gameport.c +--- linux-3.18.14.orig/drivers/input/gameport/gameport.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/input/gameport/gameport.c 2015-05-31 15:32:47.225635378 -0500 +@@ -124,12 +124,12 @@ + tx = 1 << 30; + + for(i = 0; i < 50; i++) { +- local_irq_save(flags); ++ local_irq_save_nort(flags); + GET_TIME(t1); + for (t = 0; t < 50; t++) gameport_read(gameport); + GET_TIME(t2); + GET_TIME(t3); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + udelay(i * 10); + if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; + } +@@ -148,11 +148,11 @@ + tx = 1 << 30; + + for(i = 0; i < 50; i++) { +- local_irq_save(flags); ++ local_irq_save_nort(flags); + rdtscl(t1); + for (t = 0; t < 50; t++) gameport_read(gameport); + rdtscl(t2); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + udelay(i * 10); + if (t2 - t1 < tx) tx = t2 - t1; + } +diff -Nur linux-3.18.14.orig/drivers/leds/trigger/Kconfig linux-3.18.14-rt/drivers/leds/trigger/Kconfig +--- linux-3.18.14.orig/drivers/leds/trigger/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/leds/trigger/Kconfig 2015-05-31 15:32:47.229635378 -0500 +@@ -61,7 +61,7 @@ + + config LEDS_TRIGGER_CPU + bool "LED CPU Trigger" +- depends on LEDS_TRIGGERS ++ depends on LEDS_TRIGGERS && !PREEMPT_RT_BASE + help + This allows LEDs to be controlled by active CPUs. This shows + the active CPUs across an array of LEDs so you can see which +diff -Nur linux-3.18.14.orig/drivers/md/bcache/Kconfig linux-3.18.14-rt/drivers/md/bcache/Kconfig +--- linux-3.18.14.orig/drivers/md/bcache/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/md/bcache/Kconfig 2015-05-31 15:32:47.245635378 -0500 +@@ -1,6 +1,7 @@ + + config BCACHE + tristate "Block device as cache" ++ depends on !PREEMPT_RT_FULL + ---help--- + Allows a block device to be used as cache for other devices; uses + a btree for indexing and the layout is optimized for SSDs. +diff -Nur linux-3.18.14.orig/drivers/md/dm.c linux-3.18.14-rt/drivers/md/dm.c +--- linux-3.18.14.orig/drivers/md/dm.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/md/dm.c 2015-05-31 15:32:47.261635378 -0500 +@@ -1898,14 +1898,14 @@ + if (map_request(ti, clone, md)) + goto requeued; + +- BUG_ON(!irqs_disabled()); ++ BUG_ON_NONRT(!irqs_disabled()); + spin_lock(q->queue_lock); + } + + goto out; + + requeued: +- BUG_ON(!irqs_disabled()); ++ BUG_ON_NONRT(!irqs_disabled()); + spin_lock(q->queue_lock); + + delay_and_out: +diff -Nur linux-3.18.14.orig/drivers/md/raid5.c linux-3.18.14-rt/drivers/md/raid5.c +--- linux-3.18.14.orig/drivers/md/raid5.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/md/raid5.c 2015-05-31 15:32:47.265635378 -0500 +@@ -1649,8 +1649,9 @@ + struct raid5_percpu *percpu; + unsigned long cpu; + +- cpu = get_cpu(); ++ cpu = get_cpu_light(); + percpu = per_cpu_ptr(conf->percpu, cpu); ++ spin_lock(&percpu->lock); + if (test_bit(STRIPE_OP_BIOFILL, &ops_request)) { + ops_run_biofill(sh); + overlap_clear++; +@@ -1702,7 +1703,8 @@ + if (test_and_clear_bit(R5_Overlap, &dev->flags)) + wake_up(&sh->raid_conf->wait_for_overlap); + } +- put_cpu(); ++ spin_unlock(&percpu->lock); ++ put_cpu_light(); + } + + static int grow_one_stripe(struct r5conf *conf, int hash) +@@ -5708,6 +5710,7 @@ + __func__, cpu); + break; + } ++ spin_lock_init(&per_cpu_ptr(conf->percpu, cpu)->lock); + } + put_online_cpus(); + +diff -Nur linux-3.18.14.orig/drivers/md/raid5.h linux-3.18.14-rt/drivers/md/raid5.h +--- linux-3.18.14.orig/drivers/md/raid5.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/md/raid5.h 2015-05-31 15:32:47.293635378 -0500 +@@ -457,6 +457,7 @@ + int recovery_disabled; + /* per cpu variables */ + struct raid5_percpu { ++ spinlock_t lock; /* Protection for -RT */ + struct page *spare_page; /* Used when checking P/Q in raid6 */ + void *scribble; /* space for constructing buffer + * lists and performing address +diff -Nur linux-3.18.14.orig/drivers/misc/hwlat_detector.c linux-3.18.14-rt/drivers/misc/hwlat_detector.c +--- linux-3.18.14.orig/drivers/misc/hwlat_detector.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/drivers/misc/hwlat_detector.c 2015-05-31 15:32:47.377635377 -0500 +@@ -0,0 +1,1240 @@ ++/* ++ * hwlat_detector.c - A simple Hardware Latency detector. ++ * ++ * Use this module to detect large system latencies induced by the behavior of ++ * certain underlying system hardware or firmware, independent of Linux itself. ++ * The code was developed originally to detect the presence of SMIs on Intel ++ * and AMD systems, although there is no dependency upon x86 herein. ++ * ++ * The classical example usage of this module is in detecting the presence of ++ * SMIs or System Management Interrupts on Intel and AMD systems. An SMI is a ++ * somewhat special form of hardware interrupt spawned from earlier CPU debug ++ * modes in which the (BIOS/EFI/etc.) firmware arranges for the South Bridge ++ * LPC (or other device) to generate a special interrupt under certain ++ * circumstances, for example, upon expiration of a special SMI timer device, ++ * due to certain external thermal readings, on certain I/O address accesses, ++ * and other situations. An SMI hits a special CPU pin, triggers a special ++ * SMI mode (complete with special memory map), and the OS is unaware. ++ * ++ * Although certain hardware-inducing latencies are necessary (for example, ++ * a modern system often requires an SMI handler for correct thermal control ++ * and remote management) they can wreak havoc upon any OS-level performance ++ * guarantees toward low-latency, especially when the OS is not even made ++ * aware of the presence of these interrupts. For this reason, we need a ++ * somewhat brute force mechanism to detect these interrupts. In this case, ++ * we do it by hogging all of the CPU(s) for configurable timer intervals, ++ * sampling the built-in CPU timer, looking for discontiguous readings. ++ * ++ * WARNING: This implementation necessarily introduces latencies. Therefore, ++ * you should NEVER use this module in a production environment ++ * requiring any kind of low-latency performance guarantee(s). ++ * ++ * Copyright (C) 2008-2009 Jon Masters, Red Hat, Inc. ++ * ++ * Includes useful feedback from Clark Williams ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BUF_SIZE_DEFAULT 262144UL /* 8K*(sizeof(entry)) */ ++#define BUF_FLAGS (RB_FL_OVERWRITE) /* no block on full */ ++#define U64STR_SIZE 22 /* 20 digits max */ ++ ++#define VERSION "1.0.0" ++#define BANNER "hwlat_detector: " ++#define DRVNAME "hwlat_detector" ++#define DEFAULT_SAMPLE_WINDOW 1000000 /* 1s */ ++#define DEFAULT_SAMPLE_WIDTH 500000 /* 0.5s */ ++#define DEFAULT_LAT_THRESHOLD 10 /* 10us */ ++ ++/* Module metadata */ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jon Masters "); ++MODULE_DESCRIPTION("A simple hardware latency detector"); ++MODULE_VERSION(VERSION); ++ ++/* Module parameters */ ++ ++static int debug; ++static int enabled; ++static int threshold; ++ ++module_param(debug, int, 0); /* enable debug */ ++module_param(enabled, int, 0); /* enable detector */ ++module_param(threshold, int, 0); /* latency threshold */ ++ ++/* Buffering and sampling */ ++ ++static struct ring_buffer *ring_buffer; /* sample buffer */ ++static DEFINE_MUTEX(ring_buffer_mutex); /* lock changes */ ++static unsigned long buf_size = BUF_SIZE_DEFAULT; ++static struct task_struct *kthread; /* sampling thread */ ++ ++/* DebugFS filesystem entries */ ++ ++static struct dentry *debug_dir; /* debugfs directory */ ++static struct dentry *debug_max; /* maximum TSC delta */ ++static struct dentry *debug_count; /* total detect count */ ++static struct dentry *debug_sample_width; /* sample width us */ ++static struct dentry *debug_sample_window; /* sample window us */ ++static struct dentry *debug_sample; /* raw samples us */ ++static struct dentry *debug_threshold; /* threshold us */ ++static struct dentry *debug_enable; /* enable/disable */ ++ ++/* Individual samples and global state */ ++ ++struct sample; /* latency sample */ ++struct data; /* Global state */ ++ ++/* Sampling functions */ ++static int __buffer_add_sample(struct sample *sample); ++static struct sample *buffer_get_sample(struct sample *sample); ++ ++/* Threading and state */ ++static int kthread_fn(void *unused); ++static int start_kthread(void); ++static int stop_kthread(void); ++static void __reset_stats(void); ++static int init_stats(void); ++ ++/* Debugfs interface */ ++static ssize_t simple_data_read(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos, const u64 *entry); ++static ssize_t simple_data_write(struct file *filp, const char __user *ubuf, ++ size_t cnt, loff_t *ppos, u64 *entry); ++static int debug_sample_fopen(struct inode *inode, struct file *filp); ++static ssize_t debug_sample_fread(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos); ++static int debug_sample_release(struct inode *inode, struct file *filp); ++static int debug_enable_fopen(struct inode *inode, struct file *filp); ++static ssize_t debug_enable_fread(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos); ++static ssize_t debug_enable_fwrite(struct file *file, ++ const char __user *user_buffer, ++ size_t user_size, loff_t *offset); ++ ++/* Initialization functions */ ++static int init_debugfs(void); ++static void free_debugfs(void); ++static int detector_init(void); ++static void detector_exit(void); ++ ++/* Individual latency samples are stored here when detected and packed into ++ * the ring_buffer circular buffer, where they are overwritten when ++ * more than buf_size/sizeof(sample) samples are received. */ ++struct sample { ++ u64 seqnum; /* unique sequence */ ++ u64 duration; /* ktime delta */ ++ u64 outer_duration; /* ktime delta (outer loop) */ ++ struct timespec timestamp; /* wall time */ ++ unsigned long lost; ++}; ++ ++/* keep the global state somewhere. */ ++static struct data { ++ ++ struct mutex lock; /* protect changes */ ++ ++ u64 count; /* total since reset */ ++ u64 max_sample; /* max hardware latency */ ++ u64 threshold; /* sample threshold level */ ++ ++ u64 sample_window; /* total sampling window (on+off) */ ++ u64 sample_width; /* active sampling portion of window */ ++ ++ atomic_t sample_open; /* whether the sample file is open */ ++ ++ wait_queue_head_t wq; /* waitqeue for new sample values */ ++ ++} data; ++ ++/** ++ * __buffer_add_sample - add a new latency sample recording to the ring buffer ++ * @sample: The new latency sample value ++ * ++ * This receives a new latency sample and records it in a global ring buffer. ++ * No additional locking is used in this case. ++ */ ++static int __buffer_add_sample(struct sample *sample) ++{ ++ return ring_buffer_write(ring_buffer, ++ sizeof(struct sample), sample); ++} ++ ++/** ++ * buffer_get_sample - remove a hardware latency sample from the ring buffer ++ * @sample: Pre-allocated storage for the sample ++ * ++ * This retrieves a hardware latency sample from the global circular buffer ++ */ ++static struct sample *buffer_get_sample(struct sample *sample) ++{ ++ struct ring_buffer_event *e = NULL; ++ struct sample *s = NULL; ++ unsigned int cpu = 0; ++ ++ if (!sample) ++ return NULL; ++ ++ mutex_lock(&ring_buffer_mutex); ++ for_each_online_cpu(cpu) { ++ e = ring_buffer_consume(ring_buffer, cpu, NULL, &sample->lost); ++ if (e) ++ break; ++ } ++ ++ if (e) { ++ s = ring_buffer_event_data(e); ++ memcpy(sample, s, sizeof(struct sample)); ++ } else ++ sample = NULL; ++ mutex_unlock(&ring_buffer_mutex); ++ ++ return sample; ++} ++ ++#ifndef CONFIG_TRACING ++#define time_type ktime_t ++#define time_get() ktime_get() ++#define time_to_us(x) ktime_to_us(x) ++#define time_sub(a, b) ktime_sub(a, b) ++#define init_time(a, b) (a).tv64 = b ++#define time_u64(a) ((a).tv64) ++#else ++#define time_type u64 ++#define time_get() trace_clock_local() ++#define time_to_us(x) div_u64(x, 1000) ++#define time_sub(a, b) ((a) - (b)) ++#define init_time(a, b) (a = b) ++#define time_u64(a) a ++#endif ++/** ++ * get_sample - sample the CPU TSC and look for likely hardware latencies ++ * ++ * Used to repeatedly capture the CPU TSC (or similar), looking for potential ++ * hardware-induced latency. Called with interrupts disabled and with ++ * data.lock held. ++ */ ++static int get_sample(void) ++{ ++ time_type start, t1, t2, last_t2; ++ s64 diff, total = 0; ++ u64 sample = 0; ++ u64 outer_sample = 0; ++ int ret = -1; ++ ++ init_time(last_t2, 0); ++ start = time_get(); /* start timestamp */ ++ ++ do { ++ ++ t1 = time_get(); /* we'll look for a discontinuity */ ++ t2 = time_get(); ++ ++ if (time_u64(last_t2)) { ++ /* Check the delta from outer loop (t2 to next t1) */ ++ diff = time_to_us(time_sub(t1, last_t2)); ++ /* This shouldn't happen */ ++ if (diff < 0) { ++ pr_err(BANNER "time running backwards\n"); ++ goto out; ++ } ++ if (diff > outer_sample) ++ outer_sample = diff; ++ } ++ last_t2 = t2; ++ ++ total = time_to_us(time_sub(t2, start)); /* sample width */ ++ ++ /* This checks the inner loop (t1 to t2) */ ++ diff = time_to_us(time_sub(t2, t1)); /* current diff */ ++ ++ /* This shouldn't happen */ ++ if (diff < 0) { ++ pr_err(BANNER "time running backwards\n"); ++ goto out; ++ } ++ ++ if (diff > sample) ++ sample = diff; /* only want highest value */ ++ ++ } while (total <= data.sample_width); ++ ++ ret = 0; ++ ++ /* If we exceed the threshold value, we have found a hardware latency */ ++ if (sample > data.threshold || outer_sample > data.threshold) { ++ struct sample s; ++ ++ ret = 1; ++ ++ data.count++; ++ s.seqnum = data.count; ++ s.duration = sample; ++ s.outer_duration = outer_sample; ++ s.timestamp = CURRENT_TIME; ++ __buffer_add_sample(&s); ++ ++ /* Keep a running maximum ever recorded hardware latency */ ++ if (sample > data.max_sample) ++ data.max_sample = sample; ++ } ++ ++out: ++ return ret; ++} ++ ++/* ++ * kthread_fn - The CPU time sampling/hardware latency detection kernel thread ++ * @unused: A required part of the kthread API. ++ * ++ * Used to periodically sample the CPU TSC via a call to get_sample. We ++ * disable interrupts, which does (intentionally) introduce latency since we ++ * need to ensure nothing else might be running (and thus pre-empting). ++ * Obviously this should never be used in production environments. ++ * ++ * Currently this runs on which ever CPU it was scheduled on, but most ++ * real-worald hardware latency situations occur across several CPUs, ++ * but we might later generalize this if we find there are any actualy ++ * systems with alternate SMI delivery or other hardware latencies. ++ */ ++static int kthread_fn(void *unused) ++{ ++ int ret; ++ u64 interval; ++ ++ while (!kthread_should_stop()) { ++ ++ mutex_lock(&data.lock); ++ ++ local_irq_disable(); ++ ret = get_sample(); ++ local_irq_enable(); ++ ++ if (ret > 0) ++ wake_up(&data.wq); /* wake up reader(s) */ ++ ++ interval = data.sample_window - data.sample_width; ++ do_div(interval, USEC_PER_MSEC); /* modifies interval value */ ++ ++ mutex_unlock(&data.lock); ++ ++ if (msleep_interruptible(interval)) ++ break; ++ } ++ ++ return 0; ++} ++ ++/** ++ * start_kthread - Kick off the hardware latency sampling/detector kthread ++ * ++ * This starts a kernel thread that will sit and sample the CPU timestamp ++ * counter (TSC or similar) and look for potential hardware latencies. ++ */ ++static int start_kthread(void) ++{ ++ kthread = kthread_run(kthread_fn, NULL, ++ DRVNAME); ++ if (IS_ERR(kthread)) { ++ pr_err(BANNER "could not start sampling thread\n"); ++ enabled = 0; ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++/** ++ * stop_kthread - Inform the hardware latency samping/detector kthread to stop ++ * ++ * This kicks the running hardware latency sampling/detector kernel thread and ++ * tells it to stop sampling now. Use this on unload and at system shutdown. ++ */ ++static int stop_kthread(void) ++{ ++ int ret; ++ ++ ret = kthread_stop(kthread); ++ ++ return ret; ++} ++ ++/** ++ * __reset_stats - Reset statistics for the hardware latency detector ++ * ++ * We use data to store various statistics and global state. We call this ++ * function in order to reset those when "enable" is toggled on or off, and ++ * also at initialization. Should be called with data.lock held. ++ */ ++static void __reset_stats(void) ++{ ++ data.count = 0; ++ data.max_sample = 0; ++ ring_buffer_reset(ring_buffer); /* flush out old sample entries */ ++} ++ ++/** ++ * init_stats - Setup global state statistics for the hardware latency detector ++ * ++ * We use data to store various statistics and global state. We also use ++ * a global ring buffer (ring_buffer) to keep raw samples of detected hardware ++ * induced system latencies. This function initializes these structures and ++ * allocates the global ring buffer also. ++ */ ++static int init_stats(void) ++{ ++ int ret = -ENOMEM; ++ ++ mutex_init(&data.lock); ++ init_waitqueue_head(&data.wq); ++ atomic_set(&data.sample_open, 0); ++ ++ ring_buffer = ring_buffer_alloc(buf_size, BUF_FLAGS); ++ ++ if (WARN(!ring_buffer, KERN_ERR BANNER ++ "failed to allocate ring buffer!\n")) ++ goto out; ++ ++ __reset_stats(); ++ data.threshold = threshold ?: DEFAULT_LAT_THRESHOLD; /* threshold us */ ++ data.sample_window = DEFAULT_SAMPLE_WINDOW; /* window us */ ++ data.sample_width = DEFAULT_SAMPLE_WIDTH; /* width us */ ++ ++ ret = 0; ++ ++out: ++ return ret; ++ ++} ++ ++/* ++ * simple_data_read - Wrapper read function for global state debugfs entries ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The userspace provided buffer to read value into ++ * @cnt: The maximum number of bytes to read ++ * @ppos: The current "file" position ++ * @entry: The entry to read from ++ * ++ * This function provides a generic read implementation for the global state ++ * "data" structure debugfs filesystem entries. It would be nice to use ++ * simple_attr_read directly, but we need to make sure that the data.lock ++ * is held during the actual read. ++ */ ++static ssize_t simple_data_read(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos, const u64 *entry) ++{ ++ char buf[U64STR_SIZE]; ++ u64 val = 0; ++ int len = 0; ++ ++ memset(buf, 0, sizeof(buf)); ++ ++ if (!entry) ++ return -EFAULT; ++ ++ mutex_lock(&data.lock); ++ val = *entry; ++ mutex_unlock(&data.lock); ++ ++ len = snprintf(buf, sizeof(buf), "%llu\n", (unsigned long long)val); ++ ++ return simple_read_from_buffer(ubuf, cnt, ppos, buf, len); ++ ++} ++ ++/* ++ * simple_data_write - Wrapper write function for global state debugfs entries ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The userspace provided buffer to write value from ++ * @cnt: The maximum number of bytes to write ++ * @ppos: The current "file" position ++ * @entry: The entry to write to ++ * ++ * This function provides a generic write implementation for the global state ++ * "data" structure debugfs filesystem entries. It would be nice to use ++ * simple_attr_write directly, but we need to make sure that the data.lock ++ * is held during the actual write. ++ */ ++static ssize_t simple_data_write(struct file *filp, const char __user *ubuf, ++ size_t cnt, loff_t *ppos, u64 *entry) ++{ ++ char buf[U64STR_SIZE]; ++ int csize = min(cnt, sizeof(buf)); ++ u64 val = 0; ++ int err = 0; ++ ++ memset(buf, '\0', sizeof(buf)); ++ if (copy_from_user(buf, ubuf, csize)) ++ return -EFAULT; ++ ++ buf[U64STR_SIZE-1] = '\0'; /* just in case */ ++ err = kstrtoull(buf, 10, &val); ++ if (err) ++ return -EINVAL; ++ ++ mutex_lock(&data.lock); ++ *entry = val; ++ mutex_unlock(&data.lock); ++ ++ return csize; ++} ++ ++/** ++ * debug_count_fopen - Open function for "count" debugfs entry ++ * @inode: The in-kernel inode representation of the debugfs "file" ++ * @filp: The active open file structure for the debugfs "file" ++ * ++ * This function provides an open implementation for the "count" debugfs ++ * interface to the hardware latency detector. ++ */ ++static int debug_count_fopen(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++/** ++ * debug_count_fread - Read function for "count" debugfs entry ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The userspace provided buffer to read value into ++ * @cnt: The maximum number of bytes to read ++ * @ppos: The current "file" position ++ * ++ * This function provides a read implementation for the "count" debugfs ++ * interface to the hardware latency detector. Can be used to read the ++ * number of latency readings exceeding the configured threshold since ++ * the detector was last reset (e.g. by writing a zero into "count"). ++ */ ++static ssize_t debug_count_fread(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ return simple_data_read(filp, ubuf, cnt, ppos, &data.count); ++} ++ ++/** ++ * debug_count_fwrite - Write function for "count" debugfs entry ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The user buffer that contains the value to write ++ * @cnt: The maximum number of bytes to write to "file" ++ * @ppos: The current position in the debugfs "file" ++ * ++ * This function provides a write implementation for the "count" debugfs ++ * interface to the hardware latency detector. Can be used to write a ++ * desired value, especially to zero the total count. ++ */ ++static ssize_t debug_count_fwrite(struct file *filp, ++ const char __user *ubuf, ++ size_t cnt, ++ loff_t *ppos) ++{ ++ return simple_data_write(filp, ubuf, cnt, ppos, &data.count); ++} ++ ++/** ++ * debug_enable_fopen - Dummy open function for "enable" debugfs interface ++ * @inode: The in-kernel inode representation of the debugfs "file" ++ * @filp: The active open file structure for the debugfs "file" ++ * ++ * This function provides an open implementation for the "enable" debugfs ++ * interface to the hardware latency detector. ++ */ ++static int debug_enable_fopen(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++/** ++ * debug_enable_fread - Read function for "enable" debugfs interface ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The userspace provided buffer to read value into ++ * @cnt: The maximum number of bytes to read ++ * @ppos: The current "file" position ++ * ++ * This function provides a read implementation for the "enable" debugfs ++ * interface to the hardware latency detector. Can be used to determine ++ * whether the detector is currently enabled ("0\n" or "1\n" returned). ++ */ ++static ssize_t debug_enable_fread(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ char buf[4]; ++ ++ if ((cnt < sizeof(buf)) || (*ppos)) ++ return 0; ++ ++ buf[0] = enabled ? '1' : '0'; ++ buf[1] = '\n'; ++ buf[2] = '\0'; ++ if (copy_to_user(ubuf, buf, strlen(buf))) ++ return -EFAULT; ++ return *ppos = strlen(buf); ++} ++ ++/** ++ * debug_enable_fwrite - Write function for "enable" debugfs interface ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The user buffer that contains the value to write ++ * @cnt: The maximum number of bytes to write to "file" ++ * @ppos: The current position in the debugfs "file" ++ * ++ * This function provides a write implementation for the "enable" debugfs ++ * interface to the hardware latency detector. Can be used to enable or ++ * disable the detector, which will have the side-effect of possibly ++ * also resetting the global stats and kicking off the measuring ++ * kthread (on an enable) or the converse (upon a disable). ++ */ ++static ssize_t debug_enable_fwrite(struct file *filp, ++ const char __user *ubuf, ++ size_t cnt, ++ loff_t *ppos) ++{ ++ char buf[4]; ++ int csize = min(cnt, sizeof(buf)); ++ long val = 0; ++ int err = 0; ++ ++ memset(buf, '\0', sizeof(buf)); ++ if (copy_from_user(buf, ubuf, csize)) ++ return -EFAULT; ++ ++ buf[sizeof(buf)-1] = '\0'; /* just in case */ ++ err = kstrtoul(buf, 10, &val); ++ if (0 != err) ++ return -EINVAL; ++ ++ if (val) { ++ if (enabled) ++ goto unlock; ++ enabled = 1; ++ __reset_stats(); ++ if (start_kthread()) ++ return -EFAULT; ++ } else { ++ if (!enabled) ++ goto unlock; ++ enabled = 0; ++ err = stop_kthread(); ++ if (err) { ++ pr_err(BANNER "cannot stop kthread\n"); ++ return -EFAULT; ++ } ++ wake_up(&data.wq); /* reader(s) should return */ ++ } ++unlock: ++ return csize; ++} ++ ++/** ++ * debug_max_fopen - Open function for "max" debugfs entry ++ * @inode: The in-kernel inode representation of the debugfs "file" ++ * @filp: The active open file structure for the debugfs "file" ++ * ++ * This function provides an open implementation for the "max" debugfs ++ * interface to the hardware latency detector. ++ */ ++static int debug_max_fopen(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++/** ++ * debug_max_fread - Read function for "max" debugfs entry ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The userspace provided buffer to read value into ++ * @cnt: The maximum number of bytes to read ++ * @ppos: The current "file" position ++ * ++ * This function provides a read implementation for the "max" debugfs ++ * interface to the hardware latency detector. Can be used to determine ++ * the maximum latency value observed since it was last reset. ++ */ ++static ssize_t debug_max_fread(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ return simple_data_read(filp, ubuf, cnt, ppos, &data.max_sample); ++} ++ ++/** ++ * debug_max_fwrite - Write function for "max" debugfs entry ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The user buffer that contains the value to write ++ * @cnt: The maximum number of bytes to write to "file" ++ * @ppos: The current position in the debugfs "file" ++ * ++ * This function provides a write implementation for the "max" debugfs ++ * interface to the hardware latency detector. Can be used to reset the ++ * maximum or set it to some other desired value - if, then, subsequent ++ * measurements exceed this value, the maximum will be updated. ++ */ ++static ssize_t debug_max_fwrite(struct file *filp, ++ const char __user *ubuf, ++ size_t cnt, ++ loff_t *ppos) ++{ ++ return simple_data_write(filp, ubuf, cnt, ppos, &data.max_sample); ++} ++ ++ ++/** ++ * debug_sample_fopen - An open function for "sample" debugfs interface ++ * @inode: The in-kernel inode representation of this debugfs "file" ++ * @filp: The active open file structure for the debugfs "file" ++ * ++ * This function handles opening the "sample" file within the hardware ++ * latency detector debugfs directory interface. This file is used to read ++ * raw samples from the global ring_buffer and allows the user to see a ++ * running latency history. Can be opened blocking or non-blocking, ++ * affecting whether it behaves as a buffer read pipe, or does not. ++ * Implements simple locking to prevent multiple simultaneous use. ++ */ ++static int debug_sample_fopen(struct inode *inode, struct file *filp) ++{ ++ if (!atomic_add_unless(&data.sample_open, 1, 1)) ++ return -EBUSY; ++ else ++ return 0; ++} ++ ++/** ++ * debug_sample_fread - A read function for "sample" debugfs interface ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The user buffer that will contain the samples read ++ * @cnt: The maximum bytes to read from the debugfs "file" ++ * @ppos: The current position in the debugfs "file" ++ * ++ * This function handles reading from the "sample" file within the hardware ++ * latency detector debugfs directory interface. This file is used to read ++ * raw samples from the global ring_buffer and allows the user to see a ++ * running latency history. By default this will block pending a new ++ * value written into the sample buffer, unless there are already a ++ * number of value(s) waiting in the buffer, or the sample file was ++ * previously opened in a non-blocking mode of operation. ++ */ ++static ssize_t debug_sample_fread(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ int len = 0; ++ char buf[64]; ++ struct sample *sample = NULL; ++ ++ if (!enabled) ++ return 0; ++ ++ sample = kzalloc(sizeof(struct sample), GFP_KERNEL); ++ if (!sample) ++ return -ENOMEM; ++ ++ while (!buffer_get_sample(sample)) { ++ ++ DEFINE_WAIT(wait); ++ ++ if (filp->f_flags & O_NONBLOCK) { ++ len = -EAGAIN; ++ goto out; ++ } ++ ++ prepare_to_wait(&data.wq, &wait, TASK_INTERRUPTIBLE); ++ schedule(); ++ finish_wait(&data.wq, &wait); ++ ++ if (signal_pending(current)) { ++ len = -EINTR; ++ goto out; ++ } ++ ++ if (!enabled) { /* enable was toggled */ ++ len = 0; ++ goto out; ++ } ++ } ++ ++ len = snprintf(buf, sizeof(buf), "%010lu.%010lu\t%llu\t%llu\n", ++ sample->timestamp.tv_sec, ++ sample->timestamp.tv_nsec, ++ sample->duration, ++ sample->outer_duration); ++ ++ ++ /* handling partial reads is more trouble than it's worth */ ++ if (len > cnt) ++ goto out; ++ ++ if (copy_to_user(ubuf, buf, len)) ++ len = -EFAULT; ++ ++out: ++ kfree(sample); ++ return len; ++} ++ ++/** ++ * debug_sample_release - Release function for "sample" debugfs interface ++ * @inode: The in-kernel inode represenation of the debugfs "file" ++ * @filp: The active open file structure for the debugfs "file" ++ * ++ * This function completes the close of the debugfs interface "sample" file. ++ * Frees the sample_open "lock" so that other users may open the interface. ++ */ ++static int debug_sample_release(struct inode *inode, struct file *filp) ++{ ++ atomic_dec(&data.sample_open); ++ ++ return 0; ++} ++ ++/** ++ * debug_threshold_fopen - Open function for "threshold" debugfs entry ++ * @inode: The in-kernel inode representation of the debugfs "file" ++ * @filp: The active open file structure for the debugfs "file" ++ * ++ * This function provides an open implementation for the "threshold" debugfs ++ * interface to the hardware latency detector. ++ */ ++static int debug_threshold_fopen(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++/** ++ * debug_threshold_fread - Read function for "threshold" debugfs entry ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The userspace provided buffer to read value into ++ * @cnt: The maximum number of bytes to read ++ * @ppos: The current "file" position ++ * ++ * This function provides a read implementation for the "threshold" debugfs ++ * interface to the hardware latency detector. It can be used to determine ++ * the current threshold level at which a latency will be recorded in the ++ * global ring buffer, typically on the order of 10us. ++ */ ++static ssize_t debug_threshold_fread(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ return simple_data_read(filp, ubuf, cnt, ppos, &data.threshold); ++} ++ ++/** ++ * debug_threshold_fwrite - Write function for "threshold" debugfs entry ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The user buffer that contains the value to write ++ * @cnt: The maximum number of bytes to write to "file" ++ * @ppos: The current position in the debugfs "file" ++ * ++ * This function provides a write implementation for the "threshold" debugfs ++ * interface to the hardware latency detector. It can be used to configure ++ * the threshold level at which any subsequently detected latencies will ++ * be recorded into the global ring buffer. ++ */ ++static ssize_t debug_threshold_fwrite(struct file *filp, ++ const char __user *ubuf, ++ size_t cnt, ++ loff_t *ppos) ++{ ++ int ret; ++ ++ ret = simple_data_write(filp, ubuf, cnt, ppos, &data.threshold); ++ ++ if (enabled) ++ wake_up_process(kthread); ++ ++ return ret; ++} ++ ++/** ++ * debug_width_fopen - Open function for "width" debugfs entry ++ * @inode: The in-kernel inode representation of the debugfs "file" ++ * @filp: The active open file structure for the debugfs "file" ++ * ++ * This function provides an open implementation for the "width" debugfs ++ * interface to the hardware latency detector. ++ */ ++static int debug_width_fopen(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++/** ++ * debug_width_fread - Read function for "width" debugfs entry ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The userspace provided buffer to read value into ++ * @cnt: The maximum number of bytes to read ++ * @ppos: The current "file" position ++ * ++ * This function provides a read implementation for the "width" debugfs ++ * interface to the hardware latency detector. It can be used to determine ++ * for how many us of the total window us we will actively sample for any ++ * hardware-induced latecy periods. Obviously, it is not possible to ++ * sample constantly and have the system respond to a sample reader, or, ++ * worse, without having the system appear to have gone out to lunch. ++ */ ++static ssize_t debug_width_fread(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ return simple_data_read(filp, ubuf, cnt, ppos, &data.sample_width); ++} ++ ++/** ++ * debug_width_fwrite - Write function for "width" debugfs entry ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The user buffer that contains the value to write ++ * @cnt: The maximum number of bytes to write to "file" ++ * @ppos: The current position in the debugfs "file" ++ * ++ * This function provides a write implementation for the "width" debugfs ++ * interface to the hardware latency detector. It can be used to configure ++ * for how many us of the total window us we will actively sample for any ++ * hardware-induced latency periods. Obviously, it is not possible to ++ * sample constantly and have the system respond to a sample reader, or, ++ * worse, without having the system appear to have gone out to lunch. It ++ * is enforced that width is less that the total window size. ++ */ ++static ssize_t debug_width_fwrite(struct file *filp, ++ const char __user *ubuf, ++ size_t cnt, ++ loff_t *ppos) ++{ ++ char buf[U64STR_SIZE]; ++ int csize = min(cnt, sizeof(buf)); ++ u64 val = 0; ++ int err = 0; ++ ++ memset(buf, '\0', sizeof(buf)); ++ if (copy_from_user(buf, ubuf, csize)) ++ return -EFAULT; ++ ++ buf[U64STR_SIZE-1] = '\0'; /* just in case */ ++ err = kstrtoull(buf, 10, &val); ++ if (0 != err) ++ return -EINVAL; ++ ++ mutex_lock(&data.lock); ++ if (val < data.sample_window) ++ data.sample_width = val; ++ else { ++ mutex_unlock(&data.lock); ++ return -EINVAL; ++ } ++ mutex_unlock(&data.lock); ++ ++ if (enabled) ++ wake_up_process(kthread); ++ ++ return csize; ++} ++ ++/** ++ * debug_window_fopen - Open function for "window" debugfs entry ++ * @inode: The in-kernel inode representation of the debugfs "file" ++ * @filp: The active open file structure for the debugfs "file" ++ * ++ * This function provides an open implementation for the "window" debugfs ++ * interface to the hardware latency detector. The window is the total time ++ * in us that will be considered one sample period. Conceptually, windows ++ * occur back-to-back and contain a sample width period during which ++ * actual sampling occurs. ++ */ ++static int debug_window_fopen(struct inode *inode, struct file *filp) ++{ ++ return 0; ++} ++ ++/** ++ * debug_window_fread - Read function for "window" debugfs entry ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The userspace provided buffer to read value into ++ * @cnt: The maximum number of bytes to read ++ * @ppos: The current "file" position ++ * ++ * This function provides a read implementation for the "window" debugfs ++ * interface to the hardware latency detector. The window is the total time ++ * in us that will be considered one sample period. Conceptually, windows ++ * occur back-to-back and contain a sample width period during which ++ * actual sampling occurs. Can be used to read the total window size. ++ */ ++static ssize_t debug_window_fread(struct file *filp, char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ return simple_data_read(filp, ubuf, cnt, ppos, &data.sample_window); ++} ++ ++/** ++ * debug_window_fwrite - Write function for "window" debugfs entry ++ * @filp: The active open file structure for the debugfs "file" ++ * @ubuf: The user buffer that contains the value to write ++ * @cnt: The maximum number of bytes to write to "file" ++ * @ppos: The current position in the debugfs "file" ++ * ++ * This function provides a write implementation for the "window" debufds ++ * interface to the hardware latency detetector. The window is the total time ++ * in us that will be considered one sample period. Conceptually, windows ++ * occur back-to-back and contain a sample width period during which ++ * actual sampling occurs. Can be used to write a new total window size. It ++ * is enfoced that any value written must be greater than the sample width ++ * size, or an error results. ++ */ ++static ssize_t debug_window_fwrite(struct file *filp, ++ const char __user *ubuf, ++ size_t cnt, ++ loff_t *ppos) ++{ ++ char buf[U64STR_SIZE]; ++ int csize = min(cnt, sizeof(buf)); ++ u64 val = 0; ++ int err = 0; ++ ++ memset(buf, '\0', sizeof(buf)); ++ if (copy_from_user(buf, ubuf, csize)) ++ return -EFAULT; ++ ++ buf[U64STR_SIZE-1] = '\0'; /* just in case */ ++ err = kstrtoull(buf, 10, &val); ++ if (0 != err) ++ return -EINVAL; ++ ++ mutex_lock(&data.lock); ++ if (data.sample_width < val) ++ data.sample_window = val; ++ else { ++ mutex_unlock(&data.lock); ++ return -EINVAL; ++ } ++ mutex_unlock(&data.lock); ++ ++ return csize; ++} ++ ++/* ++ * Function pointers for the "count" debugfs file operations ++ */ ++static const struct file_operations count_fops = { ++ .open = debug_count_fopen, ++ .read = debug_count_fread, ++ .write = debug_count_fwrite, ++ .owner = THIS_MODULE, ++}; ++ ++/* ++ * Function pointers for the "enable" debugfs file operations ++ */ ++static const struct file_operations enable_fops = { ++ .open = debug_enable_fopen, ++ .read = debug_enable_fread, ++ .write = debug_enable_fwrite, ++ .owner = THIS_MODULE, ++}; ++ ++/* ++ * Function pointers for the "max" debugfs file operations ++ */ ++static const struct file_operations max_fops = { ++ .open = debug_max_fopen, ++ .read = debug_max_fread, ++ .write = debug_max_fwrite, ++ .owner = THIS_MODULE, ++}; ++ ++/* ++ * Function pointers for the "sample" debugfs file operations ++ */ ++static const struct file_operations sample_fops = { ++ .open = debug_sample_fopen, ++ .read = debug_sample_fread, ++ .release = debug_sample_release, ++ .owner = THIS_MODULE, ++}; ++ ++/* ++ * Function pointers for the "threshold" debugfs file operations ++ */ ++static const struct file_operations threshold_fops = { ++ .open = debug_threshold_fopen, ++ .read = debug_threshold_fread, ++ .write = debug_threshold_fwrite, ++ .owner = THIS_MODULE, ++}; ++ ++/* ++ * Function pointers for the "width" debugfs file operations ++ */ ++static const struct file_operations width_fops = { ++ .open = debug_width_fopen, ++ .read = debug_width_fread, ++ .write = debug_width_fwrite, ++ .owner = THIS_MODULE, ++}; ++ ++/* ++ * Function pointers for the "window" debugfs file operations ++ */ ++static const struct file_operations window_fops = { ++ .open = debug_window_fopen, ++ .read = debug_window_fread, ++ .write = debug_window_fwrite, ++ .owner = THIS_MODULE, ++}; ++ ++/** ++ * init_debugfs - A function to initialize the debugfs interface files ++ * ++ * This function creates entries in debugfs for "hwlat_detector", including ++ * files to read values from the detector, current samples, and the ++ * maximum sample that has been captured since the hardware latency ++ * dectector was started. ++ */ ++static int init_debugfs(void) ++{ ++ int ret = -ENOMEM; ++ ++ debug_dir = debugfs_create_dir(DRVNAME, NULL); ++ if (!debug_dir) ++ goto err_debug_dir; ++ ++ debug_sample = debugfs_create_file("sample", 0444, ++ debug_dir, NULL, ++ &sample_fops); ++ if (!debug_sample) ++ goto err_sample; ++ ++ debug_count = debugfs_create_file("count", 0444, ++ debug_dir, NULL, ++ &count_fops); ++ if (!debug_count) ++ goto err_count; ++ ++ debug_max = debugfs_create_file("max", 0444, ++ debug_dir, NULL, ++ &max_fops); ++ if (!debug_max) ++ goto err_max; ++ ++ debug_sample_window = debugfs_create_file("window", 0644, ++ debug_dir, NULL, ++ &window_fops); ++ if (!debug_sample_window) ++ goto err_window; ++ ++ debug_sample_width = debugfs_create_file("width", 0644, ++ debug_dir, NULL, ++ &width_fops); ++ if (!debug_sample_width) ++ goto err_width; ++ ++ debug_threshold = debugfs_create_file("threshold", 0644, ++ debug_dir, NULL, ++ &threshold_fops); ++ if (!debug_threshold) ++ goto err_threshold; ++ ++ debug_enable = debugfs_create_file("enable", 0644, ++ debug_dir, &enabled, ++ &enable_fops); ++ if (!debug_enable) ++ goto err_enable; ++ ++ else { ++ ret = 0; ++ goto out; ++ } ++ ++err_enable: ++ debugfs_remove(debug_threshold); ++err_threshold: ++ debugfs_remove(debug_sample_width); ++err_width: ++ debugfs_remove(debug_sample_window); ++err_window: ++ debugfs_remove(debug_max); ++err_max: ++ debugfs_remove(debug_count); ++err_count: ++ debugfs_remove(debug_sample); ++err_sample: ++ debugfs_remove(debug_dir); ++err_debug_dir: ++out: ++ return ret; ++} ++ ++/** ++ * free_debugfs - A function to cleanup the debugfs file interface ++ */ ++static void free_debugfs(void) ++{ ++ /* could also use a debugfs_remove_recursive */ ++ debugfs_remove(debug_enable); ++ debugfs_remove(debug_threshold); ++ debugfs_remove(debug_sample_width); ++ debugfs_remove(debug_sample_window); ++ debugfs_remove(debug_max); ++ debugfs_remove(debug_count); ++ debugfs_remove(debug_sample); ++ debugfs_remove(debug_dir); ++} ++ ++/** ++ * detector_init - Standard module initialization code ++ */ ++static int detector_init(void) ++{ ++ int ret = -ENOMEM; ++ ++ pr_info(BANNER "version %s\n", VERSION); ++ ++ ret = init_stats(); ++ if (0 != ret) ++ goto out; ++ ++ ret = init_debugfs(); ++ if (0 != ret) ++ goto err_stats; ++ ++ if (enabled) ++ ret = start_kthread(); ++ ++ goto out; ++ ++err_stats: ++ ring_buffer_free(ring_buffer); ++out: ++ return ret; ++ ++} ++ ++/** ++ * detector_exit - Standard module cleanup code ++ */ ++static void detector_exit(void) ++{ ++ int err; ++ ++ if (enabled) { ++ enabled = 0; ++ err = stop_kthread(); ++ if (err) ++ pr_err(BANNER "cannot stop kthread\n"); ++ } ++ ++ free_debugfs(); ++ ring_buffer_free(ring_buffer); /* free up the ring buffer */ ++ ++} ++ ++module_init(detector_init); ++module_exit(detector_exit); +diff -Nur linux-3.18.14.orig/drivers/misc/Kconfig linux-3.18.14-rt/drivers/misc/Kconfig +--- linux-3.18.14.orig/drivers/misc/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/misc/Kconfig 2015-05-31 15:32:47.297635378 -0500 +@@ -54,6 +54,7 @@ + config ATMEL_TCLIB + bool "Atmel AT32/AT91 Timer/Counter Library" + depends on (AVR32 || ARCH_AT91) ++ default y if PREEMPT_RT_FULL + help + Select this if you want a library to allocate the Timer/Counter + blocks found on many Atmel processors. This facilitates using +@@ -69,8 +70,7 @@ + are combined to make a single 32-bit timer. + + When GENERIC_CLOCKEVENTS is defined, the third timer channel +- may be used as a clock event device supporting oneshot mode +- (delays of up to two seconds) based on the 32 KiHz clock. ++ may be used as a clock event device supporting oneshot mode. + + config ATMEL_TCB_CLKSRC_BLOCK + int +@@ -84,6 +84,15 @@ + TC can be used for other purposes, such as PWM generation and + interval timing. + ++config ATMEL_TCB_CLKSRC_USE_SLOW_CLOCK ++ bool "TC Block use 32 KiHz clock" ++ depends on ATMEL_TCB_CLKSRC ++ default y if !PREEMPT_RT_FULL ++ help ++ Select this to use 32 KiHz base clock rate as TC block clock ++ source for clock events. ++ ++ + config DUMMY_IRQ + tristate "Dummy IRQ handler" + default n +@@ -113,6 +122,35 @@ + for information on the specific driver level and support statement + for your IBM server. + ++config HWLAT_DETECTOR ++ tristate "Testing module to detect hardware-induced latencies" ++ depends on DEBUG_FS ++ depends on RING_BUFFER ++ default m ++ ---help--- ++ A simple hardware latency detector. Use this module to detect ++ large latencies introduced by the behavior of the underlying ++ system firmware external to Linux. We do this using periodic ++ use of stop_machine to grab all available CPUs and measure ++ for unexplainable gaps in the CPU timestamp counter(s). By ++ default, the module is not enabled until the "enable" file ++ within the "hwlat_detector" debugfs directory is toggled. ++ ++ This module is often used to detect SMI (System Management ++ Interrupts) on x86 systems, though is not x86 specific. To ++ this end, we default to using a sample window of 1 second, ++ during which we will sample for 0.5 seconds. If an SMI or ++ similar event occurs during that time, it is recorded ++ into an 8K samples global ring buffer until retreived. ++ ++ WARNING: This software should never be enabled (it can be built ++ but should not be turned on after it is loaded) in a production ++ environment where high latencies are a concern since the ++ sampling mechanism actually introduces latencies for ++ regular tasks while the CPU(s) are being held. ++ ++ If unsure, say N ++ + config PHANTOM + tristate "Sensable PHANToM (PCI)" + depends on PCI +diff -Nur linux-3.18.14.orig/drivers/misc/Makefile linux-3.18.14-rt/drivers/misc/Makefile +--- linux-3.18.14.orig/drivers/misc/Makefile 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/misc/Makefile 2015-05-31 15:32:47.349635377 -0500 +@@ -38,6 +38,7 @@ + obj-$(CONFIG_HMC6352) += hmc6352.o + obj-y += eeprom/ + obj-y += cb710/ ++obj-$(CONFIG_HWLAT_DETECTOR) += hwlat_detector.o + obj-$(CONFIG_SPEAR13XX_PCIE_GADGET) += spear13xx_pcie_gadget.o + obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o + obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o +diff -Nur linux-3.18.14.orig/drivers/mmc/host/mmci.c linux-3.18.14-rt/drivers/mmc/host/mmci.c +--- linux-3.18.14.orig/drivers/mmc/host/mmci.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/mmc/host/mmci.c 2015-05-31 15:32:47.393635377 -0500 +@@ -1153,15 +1153,12 @@ + struct sg_mapping_iter *sg_miter = &host->sg_miter; + struct variant_data *variant = host->variant; + void __iomem *base = host->base; +- unsigned long flags; + u32 status; + + status = readl(base + MMCISTATUS); + + dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); + +- local_irq_save(flags); +- + do { + unsigned int remain, len; + char *buffer; +@@ -1201,8 +1198,6 @@ + + sg_miter_stop(sg_miter); + +- local_irq_restore(flags); +- + /* + * If we have less than the fifo 'half-full' threshold to transfer, + * trigger a PIO interrupt as soon as any data is available. +diff -Nur linux-3.18.14.orig/drivers/mmc/host/sdhci.c linux-3.18.14-rt/drivers/mmc/host/sdhci.c +--- linux-3.18.14.orig/drivers/mmc/host/sdhci.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/mmc/host/sdhci.c 2015-05-31 15:32:47.397635376 -0500 +@@ -2565,6 +2565,31 @@ + return isr ? IRQ_HANDLED : IRQ_NONE; + } + ++#ifdef CONFIG_PREEMPT_RT_BASE ++static irqreturn_t sdhci_rt_irq(int irq, void *dev_id) ++{ ++ irqreturn_t ret; ++ ++ local_bh_disable(); ++ ret = sdhci_irq(irq, dev_id); ++ local_bh_enable(); ++ if (ret == IRQ_WAKE_THREAD) ++ ret = sdhci_thread_irq(irq, dev_id); ++ return ret; ++} ++#endif ++ ++static int sdhci_req_irq(struct sdhci_host *host) ++{ ++#ifdef CONFIG_PREEMPT_RT_BASE ++ return request_threaded_irq(host->irq, NULL, sdhci_rt_irq, ++ IRQF_SHARED, mmc_hostname(host->mmc), host); ++#else ++ return request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, ++ IRQF_SHARED, mmc_hostname(host->mmc), host); ++#endif ++} ++ + /*****************************************************************************\ + * * + * Suspend/resume * +@@ -2632,9 +2657,7 @@ + } + + if (!device_may_wakeup(mmc_dev(host->mmc))) { +- ret = request_threaded_irq(host->irq, sdhci_irq, +- sdhci_thread_irq, IRQF_SHARED, +- mmc_hostname(host->mmc), host); ++ ret = sdhci_req_irq(host); + if (ret) + return ret; + } else { +@@ -3253,8 +3276,7 @@ + + sdhci_init(host, 0); + +- ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, +- IRQF_SHARED, mmc_hostname(mmc), host); ++ ret = sdhci_req_irq(host); + if (ret) { + pr_err("%s: Failed to request IRQ %d: %d\n", + mmc_hostname(mmc), host->irq, ret); +diff -Nur linux-3.18.14.orig/drivers/net/ethernet/3com/3c59x.c linux-3.18.14-rt/drivers/net/ethernet/3com/3c59x.c +--- linux-3.18.14.orig/drivers/net/ethernet/3com/3c59x.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/ethernet/3com/3c59x.c 2015-05-31 15:32:47.425635376 -0500 +@@ -842,9 +842,9 @@ + { + struct vortex_private *vp = netdev_priv(dev); + unsigned long flags; +- local_irq_save(flags); ++ local_irq_save_nort(flags); + (vp->full_bus_master_rx ? boomerang_interrupt:vortex_interrupt)(dev->irq,dev); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + } + #endif + +@@ -1916,12 +1916,12 @@ + * Block interrupts because vortex_interrupt does a bare spin_lock() + */ + unsigned long flags; +- local_irq_save(flags); ++ local_irq_save_nort(flags); + if (vp->full_bus_master_tx) + boomerang_interrupt(dev->irq, dev); + else + vortex_interrupt(dev->irq, dev); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + } + } + +diff -Nur linux-3.18.14.orig/drivers/net/ethernet/atheros/atl1c/atl1c_main.c linux-3.18.14-rt/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +--- linux-3.18.14.orig/drivers/net/ethernet/atheros/atl1c/atl1c_main.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/ethernet/atheros/atl1c/atl1c_main.c 2015-05-31 15:32:47.437635376 -0500 +@@ -2213,11 +2213,7 @@ + } + + tpd_req = atl1c_cal_tpd_req(skb); +- if (!spin_trylock_irqsave(&adapter->tx_lock, flags)) { +- if (netif_msg_pktdata(adapter)) +- dev_info(&adapter->pdev->dev, "tx locked\n"); +- return NETDEV_TX_LOCKED; +- } ++ spin_lock_irqsave(&adapter->tx_lock, flags); + + if (atl1c_tpd_avail(adapter, type) < tpd_req) { + /* no enough descriptor, just stop queue */ +diff -Nur linux-3.18.14.orig/drivers/net/ethernet/atheros/atl1e/atl1e_main.c linux-3.18.14-rt/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +--- linux-3.18.14.orig/drivers/net/ethernet/atheros/atl1e/atl1e_main.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/ethernet/atheros/atl1e/atl1e_main.c 2015-05-31 15:32:47.445635376 -0500 +@@ -1880,8 +1880,7 @@ + return NETDEV_TX_OK; + } + tpd_req = atl1e_cal_tdp_req(skb); +- if (!spin_trylock_irqsave(&adapter->tx_lock, flags)) +- return NETDEV_TX_LOCKED; ++ spin_lock_irqsave(&adapter->tx_lock, flags); + + if (atl1e_tpd_avail(adapter) < tpd_req) { + /* no enough descriptor, just stop queue */ +diff -Nur linux-3.18.14.orig/drivers/net/ethernet/chelsio/cxgb/sge.c linux-3.18.14-rt/drivers/net/ethernet/chelsio/cxgb/sge.c +--- linux-3.18.14.orig/drivers/net/ethernet/chelsio/cxgb/sge.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/ethernet/chelsio/cxgb/sge.c 2015-05-31 15:32:47.493635375 -0500 +@@ -1663,8 +1663,7 @@ + struct cmdQ *q = &sge->cmdQ[qid]; + unsigned int credits, pidx, genbit, count, use_sched_skb = 0; + +- if (!spin_trylock(&q->lock)) +- return NETDEV_TX_LOCKED; ++ spin_lock(&q->lock); + + reclaim_completed_tx(sge, q); + +diff -Nur linux-3.18.14.orig/drivers/net/ethernet/freescale/gianfar.c linux-3.18.14-rt/drivers/net/ethernet/freescale/gianfar.c +--- linux-3.18.14.orig/drivers/net/ethernet/freescale/gianfar.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/ethernet/freescale/gianfar.c 2015-05-31 15:32:47.525635375 -0500 +@@ -1483,7 +1483,7 @@ + + if (netif_running(ndev)) { + +- local_irq_save(flags); ++ local_irq_save_nort(flags); + lock_tx_qs(priv); + + gfar_halt_nodisable(priv); +@@ -1499,7 +1499,7 @@ + gfar_write(®s->maccfg1, tempval); + + unlock_tx_qs(priv); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + disable_napi(priv); + +@@ -1541,7 +1541,7 @@ + /* Disable Magic Packet mode, in case something + * else woke us up. + */ +- local_irq_save(flags); ++ local_irq_save_nort(flags); + lock_tx_qs(priv); + + tempval = gfar_read(®s->maccfg2); +@@ -1551,7 +1551,7 @@ + gfar_start(priv); + + unlock_tx_qs(priv); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + netif_device_attach(ndev); + +@@ -3307,14 +3307,14 @@ + dev->stats.tx_dropped++; + atomic64_inc(&priv->extra_stats.tx_underrun); + +- local_irq_save(flags); ++ local_irq_save_nort(flags); + lock_tx_qs(priv); + + /* Reactivate the Tx Queues */ + gfar_write(®s->tstat, gfargrp->tstat); + + unlock_tx_qs(priv); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + } + netif_dbg(priv, tx_err, dev, "Transmit Error\n"); + } +diff -Nur linux-3.18.14.orig/drivers/net/ethernet/neterion/s2io.c linux-3.18.14-rt/drivers/net/ethernet/neterion/s2io.c +--- linux-3.18.14.orig/drivers/net/ethernet/neterion/s2io.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/ethernet/neterion/s2io.c 2015-05-31 15:32:47.537635375 -0500 +@@ -4084,12 +4084,7 @@ + [skb->priority & (MAX_TX_FIFOS - 1)]; + fifo = &mac_control->fifos[queue]; + +- if (do_spin_lock) +- spin_lock_irqsave(&fifo->tx_lock, flags); +- else { +- if (unlikely(!spin_trylock_irqsave(&fifo->tx_lock, flags))) +- return NETDEV_TX_LOCKED; +- } ++ spin_lock_irqsave(&fifo->tx_lock, flags); + + if (sp->config.multiq) { + if (__netif_subqueue_stopped(dev, fifo->fifo_no)) { +diff -Nur linux-3.18.14.orig/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c linux-3.18.14-rt/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +--- linux-3.18.14.orig/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c 2015-05-31 15:32:47.549635375 -0500 +@@ -2137,10 +2137,8 @@ + struct pch_gbe_tx_ring *tx_ring = adapter->tx_ring; + unsigned long flags; + +- if (!spin_trylock_irqsave(&tx_ring->tx_lock, flags)) { +- /* Collision - tell upper layer to requeue */ +- return NETDEV_TX_LOCKED; +- } ++ spin_lock_irqsave(&tx_ring->tx_lock, flags); ++ + if (unlikely(!PCH_GBE_DESC_UNUSED(tx_ring))) { + netif_stop_queue(netdev); + spin_unlock_irqrestore(&tx_ring->tx_lock, flags); +diff -Nur linux-3.18.14.orig/drivers/net/ethernet/realtek/8139too.c linux-3.18.14-rt/drivers/net/ethernet/realtek/8139too.c +--- linux-3.18.14.orig/drivers/net/ethernet/realtek/8139too.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/ethernet/realtek/8139too.c 2015-05-31 15:32:47.557635375 -0500 +@@ -2215,7 +2215,7 @@ + struct rtl8139_private *tp = netdev_priv(dev); + const int irq = tp->pci_dev->irq; + +- disable_irq(irq); ++ disable_irq_nosync(irq); + rtl8139_interrupt(irq, dev); + enable_irq(irq); + } +diff -Nur linux-3.18.14.orig/drivers/net/ethernet/tehuti/tehuti.c linux-3.18.14-rt/drivers/net/ethernet/tehuti/tehuti.c +--- linux-3.18.14.orig/drivers/net/ethernet/tehuti/tehuti.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/ethernet/tehuti/tehuti.c 2015-05-31 15:32:47.581635375 -0500 +@@ -1629,13 +1629,8 @@ + unsigned long flags; + + ENTER; +- local_irq_save(flags); +- if (!spin_trylock(&priv->tx_lock)) { +- local_irq_restore(flags); +- DBG("%s[%s]: TX locked, returning NETDEV_TX_LOCKED\n", +- BDX_DRV_NAME, ndev->name); +- return NETDEV_TX_LOCKED; +- } ++ ++ spin_lock_irqsave(&priv->tx_lock, flags); + + /* build tx descriptor */ + BDX_ASSERT(f->m.wptr >= f->m.memsz); /* started with valid wptr */ +diff -Nur linux-3.18.14.orig/drivers/net/rionet.c linux-3.18.14-rt/drivers/net/rionet.c +--- linux-3.18.14.orig/drivers/net/rionet.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/rionet.c 2015-05-31 15:32:47.597635374 -0500 +@@ -174,11 +174,7 @@ + unsigned long flags; + int add_num = 1; + +- local_irq_save(flags); +- if (!spin_trylock(&rnet->tx_lock)) { +- local_irq_restore(flags); +- return NETDEV_TX_LOCKED; +- } ++ spin_lock_irqsave(&rnet->tx_lock, flags); + + if (is_multicast_ether_addr(eth->h_dest)) + add_num = nets[rnet->mport->id].nact; +diff -Nur linux-3.18.14.orig/drivers/net/wireless/orinoco/orinoco_usb.c linux-3.18.14-rt/drivers/net/wireless/orinoco/orinoco_usb.c +--- linux-3.18.14.orig/drivers/net/wireless/orinoco/orinoco_usb.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/net/wireless/orinoco/orinoco_usb.c 2015-05-31 15:32:47.613635374 -0500 +@@ -699,7 +699,7 @@ + while (!ctx->done.done && msecs--) + udelay(1000); + } else { +- wait_event_interruptible(ctx->done.wait, ++ swait_event_interruptible(ctx->done.wait, + ctx->done.done); + } + break; +diff -Nur linux-3.18.14.orig/drivers/pci/access.c linux-3.18.14-rt/drivers/pci/access.c +--- linux-3.18.14.orig/drivers/pci/access.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/pci/access.c 2015-05-31 15:32:47.665635374 -0500 +@@ -434,7 +434,7 @@ + WARN_ON(!dev->block_cfg_access); + + dev->block_cfg_access = 0; +- wake_up_all(&pci_cfg_wait); ++ wake_up_all_locked(&pci_cfg_wait); + raw_spin_unlock_irqrestore(&pci_lock, flags); + } + EXPORT_SYMBOL_GPL(pci_cfg_access_unlock); +diff -Nur linux-3.18.14.orig/drivers/scsi/fcoe/fcoe.c linux-3.18.14-rt/drivers/scsi/fcoe/fcoe.c +--- linux-3.18.14.orig/drivers/scsi/fcoe/fcoe.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/scsi/fcoe/fcoe.c 2015-05-31 15:32:47.677635374 -0500 +@@ -1286,7 +1286,7 @@ + struct sk_buff *skb; + #ifdef CONFIG_SMP + struct fcoe_percpu_s *p0; +- unsigned targ_cpu = get_cpu(); ++ unsigned targ_cpu = get_cpu_light(); + #endif /* CONFIG_SMP */ + + FCOE_DBG("Destroying receive thread for CPU %d\n", cpu); +@@ -1342,7 +1342,7 @@ + kfree_skb(skb); + spin_unlock_bh(&p->fcoe_rx_list.lock); + } +- put_cpu(); ++ put_cpu_light(); + #else + /* + * This a non-SMP scenario where the singular Rx thread is +@@ -1566,11 +1566,11 @@ + static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen) + { + struct fcoe_percpu_s *fps; +- int rc; ++ int rc, cpu = get_cpu_light(); + +- fps = &get_cpu_var(fcoe_percpu); ++ fps = &per_cpu(fcoe_percpu, cpu); + rc = fcoe_get_paged_crc_eof(skb, tlen, fps); +- put_cpu_var(fcoe_percpu); ++ put_cpu_light(); + + return rc; + } +@@ -1768,11 +1768,11 @@ + return 0; + } + +- stats = per_cpu_ptr(lport->stats, get_cpu()); ++ stats = per_cpu_ptr(lport->stats, get_cpu_light()); + stats->InvalidCRCCount++; + if (stats->InvalidCRCCount < 5) + printk(KERN_WARNING "fcoe: dropping frame with CRC error\n"); +- put_cpu(); ++ put_cpu_light(); + return -EINVAL; + } + +@@ -1848,13 +1848,13 @@ + goto drop; + + if (!fcoe_filter_frames(lport, fp)) { +- put_cpu(); ++ put_cpu_light(); + fc_exch_recv(lport, fp); + return; + } + drop: + stats->ErrorFrames++; +- put_cpu(); ++ put_cpu_light(); + kfree_skb(skb); + } + +diff -Nur linux-3.18.14.orig/drivers/scsi/fcoe/fcoe_ctlr.c linux-3.18.14-rt/drivers/scsi/fcoe/fcoe_ctlr.c +--- linux-3.18.14.orig/drivers/scsi/fcoe/fcoe_ctlr.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/scsi/fcoe/fcoe_ctlr.c 2015-05-31 15:32:47.681635374 -0500 +@@ -831,7 +831,7 @@ + + INIT_LIST_HEAD(&del_list); + +- stats = per_cpu_ptr(fip->lp->stats, get_cpu()); ++ stats = per_cpu_ptr(fip->lp->stats, get_cpu_light()); + + list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { + deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2; +@@ -867,7 +867,7 @@ + sel_time = fcf->time; + } + } +- put_cpu(); ++ put_cpu_light(); + + list_for_each_entry_safe(fcf, next, &del_list, list) { + /* Removes fcf from current list */ +diff -Nur linux-3.18.14.orig/drivers/scsi/libfc/fc_exch.c linux-3.18.14-rt/drivers/scsi/libfc/fc_exch.c +--- linux-3.18.14.orig/drivers/scsi/libfc/fc_exch.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/scsi/libfc/fc_exch.c 2015-05-31 15:32:47.689635374 -0500 +@@ -816,10 +816,10 @@ + } + memset(ep, 0, sizeof(*ep)); + +- cpu = get_cpu(); ++ cpu = get_cpu_light(); + pool = per_cpu_ptr(mp->pool, cpu); + spin_lock_bh(&pool->lock); +- put_cpu(); ++ put_cpu_light(); + + /* peek cache of free slot */ + if (pool->left != FC_XID_UNKNOWN) { +diff -Nur linux-3.18.14.orig/drivers/scsi/libsas/sas_ata.c linux-3.18.14-rt/drivers/scsi/libsas/sas_ata.c +--- linux-3.18.14.orig/drivers/scsi/libsas/sas_ata.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/scsi/libsas/sas_ata.c 2015-05-31 15:32:47.689635374 -0500 +@@ -191,7 +191,7 @@ + /* TODO: audit callers to ensure they are ready for qc_issue to + * unconditionally re-enable interrupts + */ +- local_irq_save(flags); ++ local_irq_save_nort(flags); + spin_unlock(ap->lock); + + /* If the device fell off, no sense in issuing commands */ +@@ -261,7 +261,7 @@ + + out: + spin_lock(ap->lock); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + return ret; + } + +diff -Nur linux-3.18.14.orig/drivers/scsi/qla2xxx/qla_inline.h linux-3.18.14-rt/drivers/scsi/qla2xxx/qla_inline.h +--- linux-3.18.14.orig/drivers/scsi/qla2xxx/qla_inline.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/scsi/qla2xxx/qla_inline.h 2015-05-31 15:32:47.693635374 -0500 +@@ -59,12 +59,12 @@ + { + unsigned long flags; + struct qla_hw_data *ha = rsp->hw; +- local_irq_save(flags); ++ local_irq_save_nort(flags); + if (IS_P3P_TYPE(ha)) + qla82xx_poll(0, rsp); + else + ha->isp_ops->intr_handler(0, rsp); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + } + + static inline uint8_t * +diff -Nur linux-3.18.14.orig/drivers/thermal/x86_pkg_temp_thermal.c linux-3.18.14-rt/drivers/thermal/x86_pkg_temp_thermal.c +--- linux-3.18.14.orig/drivers/thermal/x86_pkg_temp_thermal.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/thermal/x86_pkg_temp_thermal.c 2015-05-31 15:32:47.701635374 -0500 +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -352,7 +353,7 @@ + } + } + +-static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val) ++static void platform_thermal_notify_work(struct swork_event *event) + { + unsigned long flags; + int cpu = smp_processor_id(); +@@ -369,7 +370,7 @@ + pkg_work_scheduled[phy_id]) { + disable_pkg_thres_interrupt(); + spin_unlock_irqrestore(&pkg_work_lock, flags); +- return -EINVAL; ++ return; + } + pkg_work_scheduled[phy_id] = 1; + spin_unlock_irqrestore(&pkg_work_lock, flags); +@@ -378,9 +379,48 @@ + schedule_delayed_work_on(cpu, + &per_cpu(pkg_temp_thermal_threshold_work, cpu), + msecs_to_jiffies(notify_delay_ms)); ++} ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++static struct swork_event notify_work; ++ ++static int thermal_notify_work_init(void) ++{ ++ int err; ++ ++ err = swork_get(); ++ if (err) ++ return err; ++ ++ INIT_SWORK(¬ify_work, platform_thermal_notify_work); + return 0; + } + ++static void thermal_notify_work_cleanup(void) ++{ ++ swork_put(); ++} ++ ++static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val) ++{ ++ swork_queue(¬ify_work); ++ return 0; ++} ++ ++#else /* !CONFIG_PREEMPT_RT_FULL */ ++ ++static int thermal_notify_work_init(void) { return 0; } ++ ++static int thermal_notify_work_cleanup(void) { } ++ ++static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val) ++{ ++ platform_thermal_notify_work(NULL); ++ ++ return 0; ++} ++#endif /* CONFIG_PREEMPT_RT_FULL */ ++ + static int find_siblings_cpu(int cpu) + { + int i; +@@ -584,6 +624,9 @@ + if (!x86_match_cpu(pkg_temp_thermal_ids)) + return -ENODEV; + ++ if (!thermal_notify_work_init()) ++ return -ENODEV; ++ + spin_lock_init(&pkg_work_lock); + platform_thermal_package_notify = + pkg_temp_thermal_platform_thermal_notify; +@@ -608,7 +651,7 @@ + kfree(pkg_work_scheduled); + platform_thermal_package_notify = NULL; + platform_thermal_package_rate_control = NULL; +- ++ thermal_notify_work_cleanup(); + return -ENODEV; + } + +@@ -633,6 +676,7 @@ + mutex_unlock(&phy_dev_list_mutex); + platform_thermal_package_notify = NULL; + platform_thermal_package_rate_control = NULL; ++ thermal_notify_work_cleanup(); + for_each_online_cpu(i) + cancel_delayed_work_sync( + &per_cpu(pkg_temp_thermal_threshold_work, i)); +diff -Nur linux-3.18.14.orig/drivers/tty/serial/8250/8250_core.c linux-3.18.14-rt/drivers/tty/serial/8250/8250_core.c +--- linux-3.18.14.orig/drivers/tty/serial/8250/8250_core.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/tty/serial/8250/8250_core.c 2015-05-31 15:32:47.753635373 -0500 +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + #include + #include + #ifdef CONFIG_SPARC +@@ -81,7 +82,16 @@ + #define DEBUG_INTR(fmt...) do { } while (0) + #endif + +-#define PASS_LIMIT 512 ++/* ++ * On -rt we can have a more delays, and legitimately ++ * so - so don't drop work spuriously and spam the ++ * syslog: ++ */ ++#ifdef CONFIG_PREEMPT_RT_FULL ++# define PASS_LIMIT 1000000 ++#else ++# define PASS_LIMIT 512 ++#endif + + #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +@@ -3197,7 +3207,7 @@ + + serial8250_rpm_get(up); + +- if (port->sysrq || oops_in_progress) ++ if (port->sysrq || oops_in_progress || in_kdb_printk()) + locked = spin_trylock_irqsave(&port->lock, flags); + else + spin_lock_irqsave(&port->lock, flags); +diff -Nur linux-3.18.14.orig/drivers/tty/serial/amba-pl011.c linux-3.18.14-rt/drivers/tty/serial/amba-pl011.c +--- linux-3.18.14.orig/drivers/tty/serial/amba-pl011.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/tty/serial/amba-pl011.c 2015-05-31 15:32:47.777635373 -0500 +@@ -1935,13 +1935,19 @@ + + clk_enable(uap->clk); + +- local_irq_save(flags); ++ /* ++ * local_irq_save(flags); ++ * ++ * This local_irq_save() is nonsense. If we come in via sysrq ++ * handling then interrupts are already disabled. Aside of ++ * that the port.sysrq check is racy on SMP regardless. ++ */ + if (uap->port.sysrq) + locked = 0; + else if (oops_in_progress) +- locked = spin_trylock(&uap->port.lock); ++ locked = spin_trylock_irqsave(&uap->port.lock, flags); + else +- spin_lock(&uap->port.lock); ++ spin_lock_irqsave(&uap->port.lock, flags); + + /* + * First save the CR then disable the interrupts +@@ -1963,8 +1969,7 @@ + writew(old_cr, uap->port.membase + UART011_CR); + + if (locked) +- spin_unlock(&uap->port.lock); +- local_irq_restore(flags); ++ spin_unlock_irqrestore(&uap->port.lock, flags); + + clk_disable(uap->clk); + } +diff -Nur linux-3.18.14.orig/drivers/tty/serial/omap-serial.c linux-3.18.14-rt/drivers/tty/serial/omap-serial.c +--- linux-3.18.14.orig/drivers/tty/serial/omap-serial.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/tty/serial/omap-serial.c 2015-05-31 15:32:47.781635373 -0500 +@@ -1270,13 +1270,10 @@ + + pm_runtime_get_sync(up->dev); + +- local_irq_save(flags); +- if (up->port.sysrq) +- locked = 0; +- else if (oops_in_progress) +- locked = spin_trylock(&up->port.lock); ++ if (up->port.sysrq || oops_in_progress) ++ locked = spin_trylock_irqsave(&up->port.lock, flags); + else +- spin_lock(&up->port.lock); ++ spin_lock_irqsave(&up->port.lock, flags); + + /* + * First save the IER then disable the interrupts +@@ -1305,8 +1302,7 @@ + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); + if (locked) +- spin_unlock(&up->port.lock); +- local_irq_restore(flags); ++ spin_unlock_irqrestore(&up->port.lock, flags); + } + + static int __init +diff -Nur linux-3.18.14.orig/drivers/usb/core/hcd.c linux-3.18.14-rt/drivers/usb/core/hcd.c +--- linux-3.18.14.orig/drivers/usb/core/hcd.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/usb/core/hcd.c 2015-05-31 15:32:47.785635373 -0500 +@@ -1681,9 +1681,9 @@ + * and no one may trigger the above deadlock situation when + * running complete() in tasklet. + */ +- local_irq_save(flags); ++ local_irq_save_nort(flags); + urb->complete(urb); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + usb_anchor_resume_wakeups(anchor); + atomic_dec(&urb->use_count); +diff -Nur linux-3.18.14.orig/drivers/usb/gadget/function/f_fs.c linux-3.18.14-rt/drivers/usb/gadget/function/f_fs.c +--- linux-3.18.14.orig/drivers/usb/gadget/function/f_fs.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/usb/gadget/function/f_fs.c 2015-05-31 15:32:47.809635373 -0500 +@@ -1428,7 +1428,7 @@ + pr_info("%s(): freeing\n", __func__); + ffs_data_clear(ffs); + BUG_ON(waitqueue_active(&ffs->ev.waitq) || +- waitqueue_active(&ffs->ep0req_completion.wait)); ++ swaitqueue_active(&ffs->ep0req_completion.wait)); + kfree(ffs->dev_name); + kfree(ffs); + } +diff -Nur linux-3.18.14.orig/drivers/usb/gadget/legacy/inode.c linux-3.18.14-rt/drivers/usb/gadget/legacy/inode.c +--- linux-3.18.14.orig/drivers/usb/gadget/legacy/inode.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/drivers/usb/gadget/legacy/inode.c 2015-05-31 15:32:47.837635372 -0500 +@@ -339,7 +339,7 @@ + spin_unlock_irq (&epdata->dev->lock); + + if (likely (value == 0)) { +- value = wait_event_interruptible (done.wait, done.done); ++ value = swait_event_interruptible (done.wait, done.done); + if (value != 0) { + spin_lock_irq (&epdata->dev->lock); + if (likely (epdata->ep != NULL)) { +@@ -348,7 +348,7 @@ + usb_ep_dequeue (epdata->ep, epdata->req); + spin_unlock_irq (&epdata->dev->lock); + +- wait_event (done.wait, done.done); ++ swait_event (done.wait, done.done); + if (epdata->status == -ECONNRESET) + epdata->status = -EINTR; + } else { +diff -Nur linux-3.18.14.orig/fs/aio.c linux-3.18.14-rt/fs/aio.c +--- linux-3.18.14.orig/fs/aio.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/aio.c 2015-05-31 15:32:47.853635372 -0500 +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -110,7 +111,7 @@ + struct page **ring_pages; + long nr_pages; + +- struct work_struct free_work; ++ struct swork_event free_work; + + /* + * signals when all in-flight requests are done +@@ -226,6 +227,7 @@ + .mount = aio_mount, + .kill_sb = kill_anon_super, + }; ++ BUG_ON(swork_get()); + aio_mnt = kern_mount(&aio_fs); + if (IS_ERR(aio_mnt)) + panic("Failed to create aio fs mount."); +@@ -505,9 +507,9 @@ + return cancel(kiocb); + } + +-static void free_ioctx(struct work_struct *work) ++static void free_ioctx(struct swork_event *sev) + { +- struct kioctx *ctx = container_of(work, struct kioctx, free_work); ++ struct kioctx *ctx = container_of(sev, struct kioctx, free_work); + + pr_debug("freeing %p\n", ctx); + +@@ -526,8 +528,8 @@ + if (ctx->requests_done) + complete(ctx->requests_done); + +- INIT_WORK(&ctx->free_work, free_ioctx); +- schedule_work(&ctx->free_work); ++ INIT_SWORK(&ctx->free_work, free_ioctx); ++ swork_queue(&ctx->free_work); + } + + /* +@@ -535,9 +537,9 @@ + * and ctx->users has dropped to 0, so we know no more kiocbs can be submitted - + * now it's safe to cancel any that need to be. + */ +-static void free_ioctx_users(struct percpu_ref *ref) ++static void free_ioctx_users_work(struct swork_event *sev) + { +- struct kioctx *ctx = container_of(ref, struct kioctx, users); ++ struct kioctx *ctx = container_of(sev, struct kioctx, free_work); + struct kiocb *req; + + spin_lock_irq(&ctx->ctx_lock); +@@ -556,6 +558,14 @@ + percpu_ref_put(&ctx->reqs); + } + ++static void free_ioctx_users(struct percpu_ref *ref) ++{ ++ struct kioctx *ctx = container_of(ref, struct kioctx, users); ++ ++ INIT_SWORK(&ctx->free_work, free_ioctx_users_work); ++ swork_queue(&ctx->free_work); ++} ++ + static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm) + { + unsigned i, new_nr; +diff -Nur linux-3.18.14.orig/fs/autofs4/autofs_i.h linux-3.18.14-rt/fs/autofs4/autofs_i.h +--- linux-3.18.14.orig/fs/autofs4/autofs_i.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/autofs4/autofs_i.h 2015-05-31 15:32:47.865635372 -0500 +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + #include + #include + +diff -Nur linux-3.18.14.orig/fs/autofs4/expire.c linux-3.18.14-rt/fs/autofs4/expire.c +--- linux-3.18.14.orig/fs/autofs4/expire.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/autofs4/expire.c 2015-05-31 15:32:47.897635372 -0500 +@@ -151,7 +151,7 @@ + parent = p->d_parent; + if (!spin_trylock(&parent->d_lock)) { + spin_unlock(&p->d_lock); +- cpu_relax(); ++ cpu_chill(); + goto relock; + } + spin_unlock(&p->d_lock); +diff -Nur linux-3.18.14.orig/fs/buffer.c linux-3.18.14-rt/fs/buffer.c +--- linux-3.18.14.orig/fs/buffer.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/buffer.c 2015-05-31 15:32:47.905635372 -0500 +@@ -301,8 +301,7 @@ + * decide that the page is now completely done. + */ + first = page_buffers(page); +- local_irq_save(flags); +- bit_spin_lock(BH_Uptodate_Lock, &first->b_state); ++ flags = bh_uptodate_lock_irqsave(first); + clear_buffer_async_read(bh); + unlock_buffer(bh); + tmp = bh; +@@ -315,8 +314,7 @@ + } + tmp = tmp->b_this_page; + } while (tmp != bh); +- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); +- local_irq_restore(flags); ++ bh_uptodate_unlock_irqrestore(first, flags); + + /* + * If none of the buffers had errors and they are all +@@ -328,9 +326,7 @@ + return; + + still_busy: +- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); +- local_irq_restore(flags); +- return; ++ bh_uptodate_unlock_irqrestore(first, flags); + } + + /* +@@ -358,8 +354,7 @@ + } + + first = page_buffers(page); +- local_irq_save(flags); +- bit_spin_lock(BH_Uptodate_Lock, &first->b_state); ++ flags = bh_uptodate_lock_irqsave(first); + + clear_buffer_async_write(bh); + unlock_buffer(bh); +@@ -371,15 +366,12 @@ + } + tmp = tmp->b_this_page; + } +- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); +- local_irq_restore(flags); ++ bh_uptodate_unlock_irqrestore(first, flags); + end_page_writeback(page); + return; + + still_busy: +- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); +- local_irq_restore(flags); +- return; ++ bh_uptodate_unlock_irqrestore(first, flags); + } + EXPORT_SYMBOL(end_buffer_async_write); + +@@ -3325,6 +3317,7 @@ + struct buffer_head *ret = kmem_cache_zalloc(bh_cachep, gfp_flags); + if (ret) { + INIT_LIST_HEAD(&ret->b_assoc_buffers); ++ buffer_head_init_locks(ret); + preempt_disable(); + __this_cpu_inc(bh_accounting.nr); + recalc_bh_state(); +diff -Nur linux-3.18.14.orig/fs/dcache.c linux-3.18.14-rt/fs/dcache.c +--- linux-3.18.14.orig/fs/dcache.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/dcache.c 2015-05-31 15:32:47.929635371 -0500 +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -552,7 +553,7 @@ + + failed: + spin_unlock(&dentry->d_lock); +- cpu_relax(); ++ cpu_chill(); + return dentry; /* try again with same dentry */ + } + +@@ -2285,7 +2286,7 @@ + if (dentry->d_lockref.count == 1) { + if (!spin_trylock(&inode->i_lock)) { + spin_unlock(&dentry->d_lock); +- cpu_relax(); ++ cpu_chill(); + goto again; + } + dentry->d_flags &= ~DCACHE_CANT_MOUNT; +diff -Nur linux-3.18.14.orig/fs/eventpoll.c linux-3.18.14-rt/fs/eventpoll.c +--- linux-3.18.14.orig/fs/eventpoll.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/eventpoll.c 2015-05-31 15:32:47.945635371 -0500 +@@ -505,12 +505,12 @@ + */ + static void ep_poll_safewake(wait_queue_head_t *wq) + { +- int this_cpu = get_cpu(); ++ int this_cpu = get_cpu_light(); + + ep_call_nested(&poll_safewake_ncalls, EP_MAX_NESTS, + ep_poll_wakeup_proc, NULL, wq, (void *) (long) this_cpu); + +- put_cpu(); ++ put_cpu_light(); + } + + static void ep_remove_wait_queue(struct eppoll_entry *pwq) +diff -Nur linux-3.18.14.orig/fs/exec.c linux-3.18.14-rt/fs/exec.c +--- linux-3.18.14.orig/fs/exec.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/exec.c 2015-05-31 15:32:47.945635371 -0500 +@@ -841,12 +841,14 @@ + } + } + task_lock(tsk); ++ preempt_disable_rt(); + active_mm = tsk->active_mm; + tsk->mm = mm; + tsk->active_mm = mm; + activate_mm(active_mm, mm); + tsk->mm->vmacache_seqnum = 0; + vmacache_flush(tsk); ++ preempt_enable_rt(); + task_unlock(tsk); + if (old_mm) { + up_read(&old_mm->mmap_sem); +diff -Nur linux-3.18.14.orig/fs/jbd/checkpoint.c linux-3.18.14-rt/fs/jbd/checkpoint.c +--- linux-3.18.14.orig/fs/jbd/checkpoint.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/jbd/checkpoint.c 2015-05-31 15:32:47.957635371 -0500 +@@ -129,6 +129,8 @@ + if (journal->j_flags & JFS_ABORT) + return; + spin_unlock(&journal->j_state_lock); ++ if (current->plug) ++ io_schedule(); + mutex_lock(&journal->j_checkpoint_mutex); + + /* +diff -Nur linux-3.18.14.orig/fs/jbd2/checkpoint.c linux-3.18.14-rt/fs/jbd2/checkpoint.c +--- linux-3.18.14.orig/fs/jbd2/checkpoint.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/jbd2/checkpoint.c 2015-05-31 15:32:47.969635371 -0500 +@@ -116,6 +116,8 @@ + nblocks = jbd2_space_needed(journal); + while (jbd2_log_space_left(journal) < nblocks) { + write_unlock(&journal->j_state_lock); ++ if (current->plug) ++ io_schedule(); + mutex_lock(&journal->j_checkpoint_mutex); + + /* +diff -Nur linux-3.18.14.orig/fs/namespace.c linux-3.18.14-rt/fs/namespace.c +--- linux-3.18.14.orig/fs/namespace.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/namespace.c 2015-05-31 15:32:47.969635371 -0500 +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include /* init_rootfs */ +@@ -344,8 +345,11 @@ + * incremented count after it has set MNT_WRITE_HOLD. + */ + smp_mb(); +- while (ACCESS_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD) +- cpu_relax(); ++ while (ACCESS_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD) { ++ preempt_enable(); ++ cpu_chill(); ++ preempt_disable(); ++ } + /* + * After the slowpath clears MNT_WRITE_HOLD, mnt_is_readonly will + * be set to match its requirements. So we must not load that until +diff -Nur linux-3.18.14.orig/fs/ntfs/aops.c linux-3.18.14-rt/fs/ntfs/aops.c +--- linux-3.18.14.orig/fs/ntfs/aops.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/ntfs/aops.c 2015-05-31 15:32:47.969635371 -0500 +@@ -107,8 +107,7 @@ + "0x%llx.", (unsigned long long)bh->b_blocknr); + } + first = page_buffers(page); +- local_irq_save(flags); +- bit_spin_lock(BH_Uptodate_Lock, &first->b_state); ++ flags = bh_uptodate_lock_irqsave(first); + clear_buffer_async_read(bh); + unlock_buffer(bh); + tmp = bh; +@@ -123,8 +122,7 @@ + } + tmp = tmp->b_this_page; + } while (tmp != bh); +- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); +- local_irq_restore(flags); ++ bh_uptodate_unlock_irqrestore(first, flags); + /* + * If none of the buffers had errors then we can set the page uptodate, + * but we first have to perform the post read mst fixups, if the +@@ -145,13 +143,13 @@ + recs = PAGE_CACHE_SIZE / rec_size; + /* Should have been verified before we got here... */ + BUG_ON(!recs); +- local_irq_save(flags); ++ local_irq_save_nort(flags); + kaddr = kmap_atomic(page); + for (i = 0; i < recs; i++) + post_read_mst_fixup((NTFS_RECORD*)(kaddr + + i * rec_size), rec_size); + kunmap_atomic(kaddr); +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + flush_dcache_page(page); + if (likely(page_uptodate && !PageError(page))) + SetPageUptodate(page); +@@ -159,9 +157,7 @@ + unlock_page(page); + return; + still_busy: +- bit_spin_unlock(BH_Uptodate_Lock, &first->b_state); +- local_irq_restore(flags); +- return; ++ bh_uptodate_unlock_irqrestore(first, flags); + } + + /** +diff -Nur linux-3.18.14.orig/fs/timerfd.c linux-3.18.14-rt/fs/timerfd.c +--- linux-3.18.14.orig/fs/timerfd.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/timerfd.c 2015-05-31 15:32:47.969635371 -0500 +@@ -449,7 +449,10 @@ + break; + } + spin_unlock_irq(&ctx->wqh.lock); +- cpu_relax(); ++ if (isalarm(ctx)) ++ hrtimer_wait_for_timer(&ctx->t.alarm.timer); ++ else ++ hrtimer_wait_for_timer(&ctx->t.tmr); + } + + /* +diff -Nur linux-3.18.14.orig/fs/xfs/xfs_linux.h linux-3.18.14-rt/fs/xfs/xfs_linux.h +--- linux-3.18.14.orig/fs/xfs/xfs_linux.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/fs/xfs/xfs_linux.h 2015-05-31 15:32:47.989635371 -0500 +@@ -119,7 +119,7 @@ + /* + * Feature macros (disable/enable) + */ +-#ifdef CONFIG_SMP ++#if defined(CONFIG_SMP) && !defined(CONFIG_PREEMPT_RT_FULL) + #define HAVE_PERCPU_SB /* per cpu superblock counters are a 2.6 feature */ + #else + #undef HAVE_PERCPU_SB /* per cpu superblock counters are a 2.6 feature */ +diff -Nur linux-3.18.14.orig/include/acpi/platform/aclinux.h linux-3.18.14-rt/include/acpi/platform/aclinux.h +--- linux-3.18.14.orig/include/acpi/platform/aclinux.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/acpi/platform/aclinux.h 2015-05-31 15:32:48.013635371 -0500 +@@ -123,6 +123,7 @@ + + #define acpi_cache_t struct kmem_cache + #define acpi_spinlock spinlock_t * ++#define acpi_raw_spinlock raw_spinlock_t * + #define acpi_cpu_flags unsigned long + + /* Use native linux version of acpi_os_allocate_zeroed */ +@@ -141,6 +142,20 @@ + #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_thread_id + #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_create_lock + ++#define acpi_os_create_raw_lock(__handle) \ ++({ \ ++ raw_spinlock_t *lock = ACPI_ALLOCATE(sizeof(*lock)); \ ++ \ ++ if (lock) { \ ++ *(__handle) = lock; \ ++ raw_spin_lock_init(*(__handle)); \ ++ } \ ++ lock ? AE_OK : AE_NO_MEMORY; \ ++ }) ++ ++#define acpi_os_delete_raw_lock(__handle) kfree(__handle) ++ ++ + /* + * OSL interfaces used by debugger/disassembler + */ +diff -Nur linux-3.18.14.orig/include/asm-generic/bug.h linux-3.18.14-rt/include/asm-generic/bug.h +--- linux-3.18.14.orig/include/asm-generic/bug.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/asm-generic/bug.h 2015-05-31 15:32:48.037635370 -0500 +@@ -206,6 +206,20 @@ + # define WARN_ON_SMP(x) ({0;}) + #endif + ++#ifdef CONFIG_PREEMPT_RT_BASE ++# define BUG_ON_RT(c) BUG_ON(c) ++# define BUG_ON_NONRT(c) do { } while (0) ++# define WARN_ON_RT(condition) WARN_ON(condition) ++# define WARN_ON_NONRT(condition) do { } while (0) ++# define WARN_ON_ONCE_NONRT(condition) do { } while (0) ++#else ++# define BUG_ON_RT(c) do { } while (0) ++# define BUG_ON_NONRT(c) BUG_ON(c) ++# define WARN_ON_RT(condition) do { } while (0) ++# define WARN_ON_NONRT(condition) WARN_ON(condition) ++# define WARN_ON_ONCE_NONRT(condition) WARN_ON_ONCE(condition) ++#endif ++ + #endif /* __ASSEMBLY__ */ + + #endif +diff -Nur linux-3.18.14.orig/include/linux/blkdev.h linux-3.18.14-rt/include/linux/blkdev.h +--- linux-3.18.14.orig/include/linux/blkdev.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/blkdev.h 2015-05-31 15:32:48.077635370 -0500 +@@ -101,6 +101,7 @@ + struct list_head queuelist; + union { + struct call_single_data csd; ++ struct work_struct work; + unsigned long fifo_time; + }; + +@@ -478,7 +479,7 @@ + struct throtl_data *td; + #endif + struct rcu_head rcu_head; +- wait_queue_head_t mq_freeze_wq; ++ struct swait_head mq_freeze_wq; + struct percpu_ref mq_usage_counter; + struct list_head all_q_node; + +diff -Nur linux-3.18.14.orig/include/linux/blk-mq.h linux-3.18.14-rt/include/linux/blk-mq.h +--- linux-3.18.14.orig/include/linux/blk-mq.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/blk-mq.h 2015-05-31 15:32:48.069635370 -0500 +@@ -169,6 +169,7 @@ + + struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *, const int ctx_index); + struct blk_mq_hw_ctx *blk_mq_alloc_single_hw_queue(struct blk_mq_tag_set *, unsigned int, int); ++void __blk_mq_complete_request_remote_work(struct work_struct *work); + + void blk_mq_start_request(struct request *rq); + void blk_mq_end_request(struct request *rq, int error); +diff -Nur linux-3.18.14.orig/include/linux/bottom_half.h linux-3.18.14-rt/include/linux/bottom_half.h +--- linux-3.18.14.orig/include/linux/bottom_half.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/bottom_half.h 2015-05-31 15:32:48.081635370 -0500 +@@ -4,6 +4,17 @@ + #include + #include + ++#ifdef CONFIG_PREEMPT_RT_FULL ++ ++extern void local_bh_disable(void); ++extern void _local_bh_enable(void); ++extern void local_bh_enable(void); ++extern void local_bh_enable_ip(unsigned long ip); ++extern void __local_bh_disable_ip(unsigned long ip, unsigned int cnt); ++extern void __local_bh_enable_ip(unsigned long ip, unsigned int cnt); ++ ++#else ++ + #ifdef CONFIG_TRACE_IRQFLAGS + extern void __local_bh_disable_ip(unsigned long ip, unsigned int cnt); + #else +@@ -31,5 +42,6 @@ + { + __local_bh_enable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET); + } ++#endif + + #endif /* _LINUX_BH_H */ +diff -Nur linux-3.18.14.orig/include/linux/buffer_head.h linux-3.18.14-rt/include/linux/buffer_head.h +--- linux-3.18.14.orig/include/linux/buffer_head.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/buffer_head.h 2015-05-31 15:32:48.109635370 -0500 +@@ -75,8 +75,52 @@ + struct address_space *b_assoc_map; /* mapping this buffer is + associated with */ + atomic_t b_count; /* users using this buffer_head */ ++#ifdef CONFIG_PREEMPT_RT_BASE ++ spinlock_t b_uptodate_lock; ++#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || \ ++ defined(CONFIG_JBD2) || defined(CONFIG_JBD2_MODULE) ++ spinlock_t b_state_lock; ++ spinlock_t b_journal_head_lock; ++#endif ++#endif + }; + ++static inline unsigned long bh_uptodate_lock_irqsave(struct buffer_head *bh) ++{ ++ unsigned long flags; ++ ++#ifndef CONFIG_PREEMPT_RT_BASE ++ local_irq_save(flags); ++ bit_spin_lock(BH_Uptodate_Lock, &bh->b_state); ++#else ++ spin_lock_irqsave(&bh->b_uptodate_lock, flags); ++#endif ++ return flags; ++} ++ ++static inline void ++bh_uptodate_unlock_irqrestore(struct buffer_head *bh, unsigned long flags) ++{ ++#ifndef CONFIG_PREEMPT_RT_BASE ++ bit_spin_unlock(BH_Uptodate_Lock, &bh->b_state); ++ local_irq_restore(flags); ++#else ++ spin_unlock_irqrestore(&bh->b_uptodate_lock, flags); ++#endif ++} ++ ++static inline void buffer_head_init_locks(struct buffer_head *bh) ++{ ++#ifdef CONFIG_PREEMPT_RT_BASE ++ spin_lock_init(&bh->b_uptodate_lock); ++#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || \ ++ defined(CONFIG_JBD2) || defined(CONFIG_JBD2_MODULE) ++ spin_lock_init(&bh->b_state_lock); ++ spin_lock_init(&bh->b_journal_head_lock); ++#endif ++#endif ++} ++ + /* + * macro tricks to expand the set_buffer_foo(), clear_buffer_foo() + * and buffer_foo() functions. +diff -Nur linux-3.18.14.orig/include/linux/cgroup.h linux-3.18.14-rt/include/linux/cgroup.h +--- linux-3.18.14.orig/include/linux/cgroup.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/cgroup.h 2015-05-31 15:32:48.117635370 -0500 +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #ifdef CONFIG_CGROUPS + +@@ -91,6 +92,7 @@ + /* percpu_ref killing and RCU release */ + struct rcu_head rcu_head; + struct work_struct destroy_work; ++ struct swork_event destroy_swork; + }; + + /* bits in struct cgroup_subsys_state flags field */ +diff -Nur linux-3.18.14.orig/include/linux/completion.h linux-3.18.14-rt/include/linux/completion.h +--- linux-3.18.14.orig/include/linux/completion.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/completion.h 2015-05-31 15:32:48.117635370 -0500 +@@ -7,8 +7,7 @@ + * Atomic wait-for-completion handler data structures. + * See kernel/sched/completion.c for details. + */ +- +-#include ++#include + + /* + * struct completion - structure used to maintain state for a "completion" +@@ -24,11 +23,11 @@ + */ + struct completion { + unsigned int done; +- wait_queue_head_t wait; ++ struct swait_head wait; + }; + + #define COMPLETION_INITIALIZER(work) \ +- { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) } ++ { 0, SWAIT_HEAD_INITIALIZER((work).wait) } + + #define COMPLETION_INITIALIZER_ONSTACK(work) \ + ({ init_completion(&work); work; }) +@@ -73,7 +72,7 @@ + static inline void init_completion(struct completion *x) + { + x->done = 0; +- init_waitqueue_head(&x->wait); ++ init_swait_head(&x->wait); + } + + /** +diff -Nur linux-3.18.14.orig/include/linux/cpu.h linux-3.18.14-rt/include/linux/cpu.h +--- linux-3.18.14.orig/include/linux/cpu.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/cpu.h 2015-05-31 15:32:48.129635370 -0500 +@@ -217,6 +217,8 @@ + extern void put_online_cpus(void); + extern void cpu_hotplug_disable(void); + extern void cpu_hotplug_enable(void); ++extern void pin_current_cpu(void); ++extern void unpin_current_cpu(void); + #define hotcpu_notifier(fn, pri) cpu_notifier(fn, pri) + #define __hotcpu_notifier(fn, pri) __cpu_notifier(fn, pri) + #define register_hotcpu_notifier(nb) register_cpu_notifier(nb) +@@ -235,6 +237,8 @@ + #define put_online_cpus() do { } while (0) + #define cpu_hotplug_disable() do { } while (0) + #define cpu_hotplug_enable() do { } while (0) ++static inline void pin_current_cpu(void) { } ++static inline void unpin_current_cpu(void) { } + #define hotcpu_notifier(fn, pri) do { (void)(fn); } while (0) + #define __hotcpu_notifier(fn, pri) do { (void)(fn); } while (0) + /* These aren't inline functions due to a GCC bug. */ +diff -Nur linux-3.18.14.orig/include/linux/delay.h linux-3.18.14-rt/include/linux/delay.h +--- linux-3.18.14.orig/include/linux/delay.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/delay.h 2015-05-31 15:32:48.129635370 -0500 +@@ -52,4 +52,10 @@ + msleep(seconds * 1000); + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++extern void cpu_chill(void); ++#else ++# define cpu_chill() cpu_relax() ++#endif ++ + #endif /* defined(_LINUX_DELAY_H) */ +diff -Nur linux-3.18.14.orig/include/linux/ftrace_event.h linux-3.18.14-rt/include/linux/ftrace_event.h +--- linux-3.18.14.orig/include/linux/ftrace_event.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/ftrace_event.h 2015-05-31 15:32:48.157635370 -0500 +@@ -61,6 +61,9 @@ + unsigned char flags; + unsigned char preempt_count; + int pid; ++ unsigned short migrate_disable; ++ unsigned short padding; ++ unsigned char preempt_lazy_count; + }; + + #define FTRACE_MAX_EVENT \ +diff -Nur linux-3.18.14.orig/include/linux/highmem.h linux-3.18.14-rt/include/linux/highmem.h +--- linux-3.18.14.orig/include/linux/highmem.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/highmem.h 2015-05-31 15:32:48.157635370 -0500 +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + + #include + +@@ -85,32 +86,51 @@ + + #if defined(CONFIG_HIGHMEM) || defined(CONFIG_X86_32) + ++#ifndef CONFIG_PREEMPT_RT_FULL + DECLARE_PER_CPU(int, __kmap_atomic_idx); ++#endif + + static inline int kmap_atomic_idx_push(void) + { ++#ifndef CONFIG_PREEMPT_RT_FULL + int idx = __this_cpu_inc_return(__kmap_atomic_idx) - 1; + +-#ifdef CONFIG_DEBUG_HIGHMEM ++# ifdef CONFIG_DEBUG_HIGHMEM + WARN_ON_ONCE(in_irq() && !irqs_disabled()); + BUG_ON(idx >= KM_TYPE_NR); +-#endif ++# endif + return idx; ++#else ++ current->kmap_idx++; ++ BUG_ON(current->kmap_idx > KM_TYPE_NR); ++ return current->kmap_idx - 1; ++#endif + } + + static inline int kmap_atomic_idx(void) + { ++#ifndef CONFIG_PREEMPT_RT_FULL + return __this_cpu_read(__kmap_atomic_idx) - 1; ++#else ++ return current->kmap_idx - 1; ++#endif + } + + static inline void kmap_atomic_idx_pop(void) + { +-#ifdef CONFIG_DEBUG_HIGHMEM ++#ifndef CONFIG_PREEMPT_RT_FULL ++# ifdef CONFIG_DEBUG_HIGHMEM + int idx = __this_cpu_dec_return(__kmap_atomic_idx); + + BUG_ON(idx < 0); +-#else ++# else + __this_cpu_dec(__kmap_atomic_idx); ++# endif ++#else ++ current->kmap_idx--; ++# ifdef CONFIG_DEBUG_HIGHMEM ++ BUG_ON(current->kmap_idx < 0); ++# endif + #endif + } + +diff -Nur linux-3.18.14.orig/include/linux/hrtimer.h linux-3.18.14-rt/include/linux/hrtimer.h +--- linux-3.18.14.orig/include/linux/hrtimer.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/hrtimer.h 2015-05-31 15:32:48.161635369 -0500 +@@ -111,6 +111,11 @@ + enum hrtimer_restart (*function)(struct hrtimer *); + struct hrtimer_clock_base *base; + unsigned long state; ++ struct list_head cb_entry; ++ int irqsafe; ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ ktime_t praecox; ++#endif + #ifdef CONFIG_TIMER_STATS + int start_pid; + void *start_site; +@@ -147,6 +152,7 @@ + int index; + clockid_t clockid; + struct timerqueue_head active; ++ struct list_head expired; + ktime_t resolution; + ktime_t (*get_time)(void); + ktime_t softirq_time; +@@ -192,6 +198,9 @@ + unsigned long nr_hangs; + ktime_t max_hang_time; + #endif ++#ifdef CONFIG_PREEMPT_RT_BASE ++ wait_queue_head_t wait; ++#endif + struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; + }; + +@@ -379,6 +388,13 @@ + return hrtimer_start_expires(timer, HRTIMER_MODE_ABS); + } + ++/* Softirq preemption could deadlock timer removal */ ++#ifdef CONFIG_PREEMPT_RT_BASE ++ extern void hrtimer_wait_for_timer(const struct hrtimer *timer); ++#else ++# define hrtimer_wait_for_timer(timer) do { cpu_relax(); } while (0) ++#endif ++ + /* Query timers: */ + extern ktime_t hrtimer_get_remaining(const struct hrtimer *timer); + extern int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp); +diff -Nur linux-3.18.14.orig/include/linux/idr.h linux-3.18.14-rt/include/linux/idr.h +--- linux-3.18.14.orig/include/linux/idr.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/idr.h 2015-05-31 15:32:48.161635369 -0500 +@@ -95,10 +95,14 @@ + * Each idr_preload() should be matched with an invocation of this + * function. See idr_preload() for details. + */ ++#ifdef CONFIG_PREEMPT_RT_FULL ++void idr_preload_end(void); ++#else + static inline void idr_preload_end(void) + { + preempt_enable(); + } ++#endif + + /** + * idr_find - return pointer for given id +diff -Nur linux-3.18.14.orig/include/linux/init_task.h linux-3.18.14-rt/include/linux/init_task.h +--- linux-3.18.14.orig/include/linux/init_task.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/init_task.h 2015-05-31 15:32:48.177635369 -0500 +@@ -147,9 +147,16 @@ + # define INIT_PERF_EVENTS(tsk) + #endif + ++#ifdef CONFIG_PREEMPT_RT_BASE ++# define INIT_TIMER_LIST .posix_timer_list = NULL, ++#else ++# define INIT_TIMER_LIST ++#endif ++ + #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN + # define INIT_VTIME(tsk) \ +- .vtime_seqlock = __SEQLOCK_UNLOCKED(tsk.vtime_seqlock), \ ++ .vtime_lock = __RAW_SPIN_LOCK_UNLOCKED(tsk.vtime_lock), \ ++ .vtime_seq = SEQCNT_ZERO(tsk.vtime_seq), \ + .vtime_snap = 0, \ + .vtime_snap_whence = VTIME_SYS, + #else +@@ -219,6 +226,7 @@ + .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \ + .pi_lock = __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock), \ + .timer_slack_ns = 50000, /* 50 usec default slack */ \ ++ INIT_TIMER_LIST \ + .pids = { \ + [PIDTYPE_PID] = INIT_PID_LINK(PIDTYPE_PID), \ + [PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID), \ +diff -Nur linux-3.18.14.orig/include/linux/interrupt.h linux-3.18.14-rt/include/linux/interrupt.h +--- linux-3.18.14.orig/include/linux/interrupt.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/interrupt.h 2015-05-31 15:32:48.181635369 -0500 +@@ -57,6 +57,7 @@ + * IRQF_NO_THREAD - Interrupt cannot be threaded + * IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device + * resume time. ++ * IRQF_NO_SOFTIRQ_CALL - Do not process softirqs in the irq thread context (RT) + */ + #define IRQF_DISABLED 0x00000020 + #define IRQF_SHARED 0x00000080 +@@ -70,6 +71,7 @@ + #define IRQF_FORCE_RESUME 0x00008000 + #define IRQF_NO_THREAD 0x00010000 + #define IRQF_EARLY_RESUME 0x00020000 ++#define IRQF_NO_SOFTIRQ_CALL 0x00080000 + + #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) + +@@ -180,7 +182,7 @@ + #ifdef CONFIG_LOCKDEP + # define local_irq_enable_in_hardirq() do { } while (0) + #else +-# define local_irq_enable_in_hardirq() local_irq_enable() ++# define local_irq_enable_in_hardirq() local_irq_enable_nort() + #endif + + extern void disable_irq_nosync(unsigned int irq); +@@ -210,6 +212,7 @@ + unsigned int irq; + struct kref kref; + struct work_struct work; ++ struct list_head list; + void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask); + void (*release)(struct kref *ref); + }; +@@ -358,9 +361,13 @@ + + + #ifdef CONFIG_IRQ_FORCED_THREADING ++# ifndef CONFIG_PREEMPT_RT_BASE + extern bool force_irqthreads; ++# else ++# define force_irqthreads (true) ++# endif + #else +-#define force_irqthreads (0) ++#define force_irqthreads (false) + #endif + + #ifndef __ARCH_SET_SOFTIRQ_PENDING +@@ -416,9 +423,10 @@ + void (*action)(struct softirq_action *); + }; + ++#ifndef CONFIG_PREEMPT_RT_FULL + asmlinkage void do_softirq(void); + asmlinkage void __do_softirq(void); +- ++static inline void thread_do_softirq(void) { do_softirq(); } + #ifdef __ARCH_HAS_DO_SOFTIRQ + void do_softirq_own_stack(void); + #else +@@ -427,6 +435,9 @@ + __do_softirq(); + } + #endif ++#else ++extern void thread_do_softirq(void); ++#endif + + extern void open_softirq(int nr, void (*action)(struct softirq_action *)); + extern void softirq_init(void); +@@ -434,6 +445,7 @@ + + extern void raise_softirq_irqoff(unsigned int nr); + extern void raise_softirq(unsigned int nr); ++extern void softirq_check_pending_idle(void); + + DECLARE_PER_CPU(struct task_struct *, ksoftirqd); + +@@ -455,8 +467,9 @@ + to be executed on some cpu at least once after this. + * If the tasklet is already scheduled, but its execution is still not + started, it will be executed only once. +- * If this tasklet is already running on another CPU (or schedule is called +- from tasklet itself), it is rescheduled for later. ++ * If this tasklet is already running on another CPU, it is rescheduled ++ for later. ++ * Schedule must not be called from the tasklet itself (a lockup occurs) + * Tasklet is strictly serialized wrt itself, but not + wrt another tasklets. If client needs some intertask synchronization, + he makes it with spinlocks. +@@ -481,27 +494,36 @@ + enum + { + TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */ +- TASKLET_STATE_RUN /* Tasklet is running (SMP only) */ ++ TASKLET_STATE_RUN, /* Tasklet is running (SMP only) */ ++ TASKLET_STATE_PENDING /* Tasklet is pending */ + }; + +-#ifdef CONFIG_SMP ++#define TASKLET_STATEF_SCHED (1 << TASKLET_STATE_SCHED) ++#define TASKLET_STATEF_RUN (1 << TASKLET_STATE_RUN) ++#define TASKLET_STATEF_PENDING (1 << TASKLET_STATE_PENDING) ++ ++#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) + static inline int tasklet_trylock(struct tasklet_struct *t) + { + return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state); + } + ++static inline int tasklet_tryunlock(struct tasklet_struct *t) ++{ ++ return cmpxchg(&t->state, TASKLET_STATEF_RUN, 0) == TASKLET_STATEF_RUN; ++} ++ + static inline void tasklet_unlock(struct tasklet_struct *t) + { + smp_mb__before_atomic(); + clear_bit(TASKLET_STATE_RUN, &(t)->state); + } + +-static inline void tasklet_unlock_wait(struct tasklet_struct *t) +-{ +- while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); } +-} ++extern void tasklet_unlock_wait(struct tasklet_struct *t); ++ + #else + #define tasklet_trylock(t) 1 ++#define tasklet_tryunlock(t) 1 + #define tasklet_unlock_wait(t) do { } while (0) + #define tasklet_unlock(t) do { } while (0) + #endif +@@ -550,17 +572,8 @@ + smp_mb(); + } + +-static inline void tasklet_enable(struct tasklet_struct *t) +-{ +- smp_mb__before_atomic(); +- atomic_dec(&t->count); +-} +- +-static inline void tasklet_hi_enable(struct tasklet_struct *t) +-{ +- smp_mb__before_atomic(); +- atomic_dec(&t->count); +-} ++extern void tasklet_enable(struct tasklet_struct *t); ++extern void tasklet_hi_enable(struct tasklet_struct *t); + + extern void tasklet_kill(struct tasklet_struct *t); + extern void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu); +@@ -592,6 +605,12 @@ + tasklet_kill(&ttimer->tasklet); + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++extern void softirq_early_init(void); ++#else ++static inline void softirq_early_init(void) { } ++#endif ++ + /* + * Autoprobing for irqs: + * +diff -Nur linux-3.18.14.orig/include/linux/irqdesc.h linux-3.18.14-rt/include/linux/irqdesc.h +--- linux-3.18.14.orig/include/linux/irqdesc.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/irqdesc.h 2015-05-31 15:32:48.217635369 -0500 +@@ -63,6 +63,7 @@ + unsigned int irqs_unhandled; + atomic_t threads_handled; + int threads_handled_last; ++ u64 random_ip; + raw_spinlock_t lock; + struct cpumask *percpu_enabled; + #ifdef CONFIG_SMP +diff -Nur linux-3.18.14.orig/include/linux/irqflags.h linux-3.18.14-rt/include/linux/irqflags.h +--- linux-3.18.14.orig/include/linux/irqflags.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/irqflags.h 2015-05-31 15:32:48.233635369 -0500 +@@ -25,8 +25,6 @@ + # define trace_softirqs_enabled(p) ((p)->softirqs_enabled) + # define trace_hardirq_enter() do { current->hardirq_context++; } while (0) + # define trace_hardirq_exit() do { current->hardirq_context--; } while (0) +-# define lockdep_softirq_enter() do { current->softirq_context++; } while (0) +-# define lockdep_softirq_exit() do { current->softirq_context--; } while (0) + # define INIT_TRACE_IRQFLAGS .softirqs_enabled = 1, + #else + # define trace_hardirqs_on() do { } while (0) +@@ -39,9 +37,15 @@ + # define trace_softirqs_enabled(p) 0 + # define trace_hardirq_enter() do { } while (0) + # define trace_hardirq_exit() do { } while (0) ++# define INIT_TRACE_IRQFLAGS ++#endif ++ ++#if defined(CONFIG_TRACE_IRQFLAGS) && !defined(CONFIG_PREEMPT_RT_FULL) ++# define lockdep_softirq_enter() do { current->softirq_context++; } while (0) ++# define lockdep_softirq_exit() do { current->softirq_context--; } while (0) ++#else + # define lockdep_softirq_enter() do { } while (0) + # define lockdep_softirq_exit() do { } while (0) +-# define INIT_TRACE_IRQFLAGS + #endif + + #if defined(CONFIG_IRQSOFF_TRACER) || \ +@@ -147,4 +151,23 @@ + + #endif /* CONFIG_TRACE_IRQFLAGS_SUPPORT */ + ++/* ++ * local_irq* variants depending on RT/!RT ++ */ ++#ifdef CONFIG_PREEMPT_RT_FULL ++# define local_irq_disable_nort() do { } while (0) ++# define local_irq_enable_nort() do { } while (0) ++# define local_irq_save_nort(flags) local_save_flags(flags) ++# define local_irq_restore_nort(flags) (void)(flags) ++# define local_irq_disable_rt() local_irq_disable() ++# define local_irq_enable_rt() local_irq_enable() ++#else ++# define local_irq_disable_nort() local_irq_disable() ++# define local_irq_enable_nort() local_irq_enable() ++# define local_irq_save_nort(flags) local_irq_save(flags) ++# define local_irq_restore_nort(flags) local_irq_restore(flags) ++# define local_irq_disable_rt() do { } while (0) ++# define local_irq_enable_rt() do { } while (0) ++#endif ++ + #endif +diff -Nur linux-3.18.14.orig/include/linux/irq.h linux-3.18.14-rt/include/linux/irq.h +--- linux-3.18.14.orig/include/linux/irq.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/irq.h 2015-05-31 15:32:48.185635369 -0500 +@@ -73,6 +73,7 @@ + * IRQ_IS_POLLED - Always polled by another interrupt. Exclude + * it from the spurious interrupt detection + * mechanism and from core side polling. ++ * IRQ_NO_SOFTIRQ_CALL - No softirq processing in the irq thread context (RT) + */ + enum { + IRQ_TYPE_NONE = 0x00000000, +@@ -98,13 +99,14 @@ + IRQ_NOTHREAD = (1 << 16), + IRQ_PER_CPU_DEVID = (1 << 17), + IRQ_IS_POLLED = (1 << 18), ++ IRQ_NO_SOFTIRQ_CALL = (1 << 19), + }; + + #define IRQF_MODIFY_MASK \ + (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ + IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \ + IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID | \ +- IRQ_IS_POLLED) ++ IRQ_IS_POLLED | IRQ_NO_SOFTIRQ_CALL) + + #define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING) + +diff -Nur linux-3.18.14.orig/include/linux/irq_work.h linux-3.18.14-rt/include/linux/irq_work.h +--- linux-3.18.14.orig/include/linux/irq_work.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/irq_work.h 2015-05-31 15:32:48.217635369 -0500 +@@ -16,6 +16,7 @@ + #define IRQ_WORK_BUSY 2UL + #define IRQ_WORK_FLAGS 3UL + #define IRQ_WORK_LAZY 4UL /* Doesn't want IPI, wait for tick */ ++#define IRQ_WORK_HARD_IRQ 8UL /* Run hard IRQ context, even on RT */ + + struct irq_work { + unsigned long flags; +diff -Nur linux-3.18.14.orig/include/linux/jbd_common.h linux-3.18.14-rt/include/linux/jbd_common.h +--- linux-3.18.14.orig/include/linux/jbd_common.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/jbd_common.h 2015-05-31 15:32:48.237635369 -0500 +@@ -15,32 +15,56 @@ + + static inline void jbd_lock_bh_state(struct buffer_head *bh) + { ++#ifndef CONFIG_PREEMPT_RT_BASE + bit_spin_lock(BH_State, &bh->b_state); ++#else ++ spin_lock(&bh->b_state_lock); ++#endif + } + + static inline int jbd_trylock_bh_state(struct buffer_head *bh) + { ++#ifndef CONFIG_PREEMPT_RT_BASE + return bit_spin_trylock(BH_State, &bh->b_state); ++#else ++ return spin_trylock(&bh->b_state_lock); ++#endif + } + + static inline int jbd_is_locked_bh_state(struct buffer_head *bh) + { ++#ifndef CONFIG_PREEMPT_RT_BASE + return bit_spin_is_locked(BH_State, &bh->b_state); ++#else ++ return spin_is_locked(&bh->b_state_lock); ++#endif + } + + static inline void jbd_unlock_bh_state(struct buffer_head *bh) + { ++#ifndef CONFIG_PREEMPT_RT_BASE + bit_spin_unlock(BH_State, &bh->b_state); ++#else ++ spin_unlock(&bh->b_state_lock); ++#endif + } + + static inline void jbd_lock_bh_journal_head(struct buffer_head *bh) + { ++#ifndef CONFIG_PREEMPT_RT_BASE + bit_spin_lock(BH_JournalHead, &bh->b_state); ++#else ++ spin_lock(&bh->b_journal_head_lock); ++#endif + } + + static inline void jbd_unlock_bh_journal_head(struct buffer_head *bh) + { ++#ifndef CONFIG_PREEMPT_RT_BASE + bit_spin_unlock(BH_JournalHead, &bh->b_state); ++#else ++ spin_unlock(&bh->b_journal_head_lock); ++#endif + } + + #endif +diff -Nur linux-3.18.14.orig/include/linux/jump_label.h linux-3.18.14-rt/include/linux/jump_label.h +--- linux-3.18.14.orig/include/linux/jump_label.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/jump_label.h 2015-05-31 15:32:48.237635369 -0500 +@@ -55,7 +55,8 @@ + "%s used before call to jump_label_init", \ + __func__) + +-#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL) ++#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL) && \ ++ !defined(CONFIG_PREEMPT_BASE) + + struct static_key { + atomic_t enabled; +diff -Nur linux-3.18.14.orig/include/linux/kdb.h linux-3.18.14-rt/include/linux/kdb.h +--- linux-3.18.14.orig/include/linux/kdb.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/kdb.h 2015-05-31 15:32:48.245635369 -0500 +@@ -116,7 +116,7 @@ + extern __printf(1, 0) int vkdb_printf(const char *fmt, va_list args); + extern __printf(1, 2) int kdb_printf(const char *, ...); + typedef __printf(1, 2) int (*kdb_printf_t)(const char *, ...); +- ++#define in_kdb_printk() (kdb_trap_printk) + extern void kdb_init(int level); + + /* Access to kdb specific polling devices */ +@@ -151,6 +151,7 @@ + extern int kdb_unregister(char *); + #else /* ! CONFIG_KGDB_KDB */ + static inline __printf(1, 2) int kdb_printf(const char *fmt, ...) { return 0; } ++#define in_kdb_printk() (0) + static inline void kdb_init(int level) {} + static inline int kdb_register(char *cmd, kdb_func_t func, char *usage, + char *help, short minlen) { return 0; } +diff -Nur linux-3.18.14.orig/include/linux/kernel.h linux-3.18.14-rt/include/linux/kernel.h +--- linux-3.18.14.orig/include/linux/kernel.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/kernel.h 2015-05-31 15:32:48.245635369 -0500 +@@ -451,6 +451,7 @@ + SYSTEM_HALT, + SYSTEM_POWER_OFF, + SYSTEM_RESTART, ++ SYSTEM_SUSPEND, + } system_state; + + #define TAINT_PROPRIETARY_MODULE 0 +diff -Nur linux-3.18.14.orig/include/linux/kvm_host.h linux-3.18.14-rt/include/linux/kvm_host.h +--- linux-3.18.14.orig/include/linux/kvm_host.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/kvm_host.h 2015-05-31 15:32:48.253635368 -0500 +@@ -245,7 +245,7 @@ + + int fpu_active; + int guest_fpu_loaded, guest_xcr0_loaded; +- wait_queue_head_t wq; ++ struct swait_head wq; + struct pid *pid; + int sigset_active; + sigset_t sigset; +@@ -688,7 +688,7 @@ + } + #endif + +-static inline wait_queue_head_t *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) ++static inline struct swait_head *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) + { + #ifdef __KVM_HAVE_ARCH_WQP + return vcpu->arch.wqp; +diff -Nur linux-3.18.14.orig/include/linux/kvm_host.h.orig linux-3.18.14-rt/include/linux/kvm_host.h.orig +--- linux-3.18.14.orig/include/linux/kvm_host.h.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/kvm_host.h.orig 2015-05-20 10:04:50.000000000 -0500 +@@ -0,0 +1,1111 @@ ++#ifndef __KVM_HOST_H ++#define __KVM_HOST_H ++ ++/* ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#include ++ ++#ifndef KVM_MMIO_SIZE ++#define KVM_MMIO_SIZE 8 ++#endif ++ ++/* ++ * The bit 16 ~ bit 31 of kvm_memory_region::flags are internally used ++ * in kvm, other bits are visible for userspace which are defined in ++ * include/linux/kvm_h. ++ */ ++#define KVM_MEMSLOT_INVALID (1UL << 16) ++#define KVM_MEMSLOT_INCOHERENT (1UL << 17) ++ ++/* Two fragments for cross MMIO pages. */ ++#define KVM_MAX_MMIO_FRAGMENTS 2 ++ ++/* ++ * For the normal pfn, the highest 12 bits should be zero, ++ * so we can mask bit 62 ~ bit 52 to indicate the error pfn, ++ * mask bit 63 to indicate the noslot pfn. ++ */ ++#define KVM_PFN_ERR_MASK (0x7ffULL << 52) ++#define KVM_PFN_ERR_NOSLOT_MASK (0xfffULL << 52) ++#define KVM_PFN_NOSLOT (0x1ULL << 63) ++ ++#define KVM_PFN_ERR_FAULT (KVM_PFN_ERR_MASK) ++#define KVM_PFN_ERR_HWPOISON (KVM_PFN_ERR_MASK + 1) ++#define KVM_PFN_ERR_RO_FAULT (KVM_PFN_ERR_MASK + 2) ++ ++/* ++ * error pfns indicate that the gfn is in slot but faild to ++ * translate it to pfn on host. ++ */ ++static inline bool is_error_pfn(pfn_t pfn) ++{ ++ return !!(pfn & KVM_PFN_ERR_MASK); ++} ++ ++/* ++ * error_noslot pfns indicate that the gfn can not be ++ * translated to pfn - it is not in slot or failed to ++ * translate it to pfn. ++ */ ++static inline bool is_error_noslot_pfn(pfn_t pfn) ++{ ++ return !!(pfn & KVM_PFN_ERR_NOSLOT_MASK); ++} ++ ++/* noslot pfn indicates that the gfn is not in slot. */ ++static inline bool is_noslot_pfn(pfn_t pfn) ++{ ++ return pfn == KVM_PFN_NOSLOT; ++} ++ ++/* ++ * architectures with KVM_HVA_ERR_BAD other than PAGE_OFFSET (e.g. s390) ++ * provide own defines and kvm_is_error_hva ++ */ ++#ifndef KVM_HVA_ERR_BAD ++ ++#define KVM_HVA_ERR_BAD (PAGE_OFFSET) ++#define KVM_HVA_ERR_RO_BAD (PAGE_OFFSET + PAGE_SIZE) ++ ++static inline bool kvm_is_error_hva(unsigned long addr) ++{ ++ return addr >= PAGE_OFFSET; ++} ++ ++#endif ++ ++#define KVM_ERR_PTR_BAD_PAGE (ERR_PTR(-ENOENT)) ++ ++static inline bool is_error_page(struct page *page) ++{ ++ return IS_ERR(page); ++} ++ ++/* ++ * vcpu->requests bit members ++ */ ++#define KVM_REQ_TLB_FLUSH 0 ++#define KVM_REQ_MIGRATE_TIMER 1 ++#define KVM_REQ_REPORT_TPR_ACCESS 2 ++#define KVM_REQ_MMU_RELOAD 3 ++#define KVM_REQ_TRIPLE_FAULT 4 ++#define KVM_REQ_PENDING_TIMER 5 ++#define KVM_REQ_UNHALT 6 ++#define KVM_REQ_MMU_SYNC 7 ++#define KVM_REQ_CLOCK_UPDATE 8 ++#define KVM_REQ_KICK 9 ++#define KVM_REQ_DEACTIVATE_FPU 10 ++#define KVM_REQ_EVENT 11 ++#define KVM_REQ_APF_HALT 12 ++#define KVM_REQ_STEAL_UPDATE 13 ++#define KVM_REQ_NMI 14 ++#define KVM_REQ_PMU 15 ++#define KVM_REQ_PMI 16 ++#define KVM_REQ_WATCHDOG 17 ++#define KVM_REQ_MASTERCLOCK_UPDATE 18 ++#define KVM_REQ_MCLOCK_INPROGRESS 19 ++#define KVM_REQ_EPR_EXIT 20 ++#define KVM_REQ_SCAN_IOAPIC 21 ++#define KVM_REQ_GLOBAL_CLOCK_UPDATE 22 ++#define KVM_REQ_ENABLE_IBS 23 ++#define KVM_REQ_DISABLE_IBS 24 ++#define KVM_REQ_APIC_PAGE_RELOAD 25 ++ ++#define KVM_USERSPACE_IRQ_SOURCE_ID 0 ++#define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1 ++ ++extern struct kmem_cache *kvm_vcpu_cache; ++ ++extern spinlock_t kvm_lock; ++extern struct list_head vm_list; ++ ++struct kvm_io_range { ++ gpa_t addr; ++ int len; ++ struct kvm_io_device *dev; ++}; ++ ++#define NR_IOBUS_DEVS 1000 ++ ++struct kvm_io_bus { ++ int dev_count; ++ int ioeventfd_count; ++ struct kvm_io_range range[]; ++}; ++ ++enum kvm_bus { ++ KVM_MMIO_BUS, ++ KVM_PIO_BUS, ++ KVM_VIRTIO_CCW_NOTIFY_BUS, ++ KVM_FAST_MMIO_BUS, ++ KVM_NR_BUSES ++}; ++ ++int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, ++ int len, const void *val); ++int kvm_io_bus_write_cookie(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, ++ int len, const void *val, long cookie); ++int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, ++ void *val); ++int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, ++ int len, struct kvm_io_device *dev); ++int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, ++ struct kvm_io_device *dev); ++ ++#ifdef CONFIG_KVM_ASYNC_PF ++struct kvm_async_pf { ++ struct work_struct work; ++ struct list_head link; ++ struct list_head queue; ++ struct kvm_vcpu *vcpu; ++ struct mm_struct *mm; ++ gva_t gva; ++ unsigned long addr; ++ struct kvm_arch_async_pf arch; ++ bool wakeup_all; ++}; ++ ++void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu); ++void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu); ++int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, unsigned long hva, ++ struct kvm_arch_async_pf *arch); ++int kvm_async_pf_wakeup_all(struct kvm_vcpu *vcpu); ++#endif ++ ++/* ++ * Carry out a gup that requires IO. Allow the mm to relinquish the mmap ++ * semaphore if the filemap/swap has to wait on a page lock. pagep == NULL ++ * controls whether we retry the gup one more time to completion in that case. ++ * Typically this is called after a FAULT_FLAG_RETRY_NOWAIT in the main tdp ++ * handler. ++ */ ++int kvm_get_user_page_io(struct task_struct *tsk, struct mm_struct *mm, ++ unsigned long addr, bool write_fault, ++ struct page **pagep); ++ ++enum { ++ OUTSIDE_GUEST_MODE, ++ IN_GUEST_MODE, ++ EXITING_GUEST_MODE, ++ READING_SHADOW_PAGE_TABLES, ++}; ++ ++/* ++ * Sometimes a large or cross-page mmio needs to be broken up into separate ++ * exits for userspace servicing. ++ */ ++struct kvm_mmio_fragment { ++ gpa_t gpa; ++ void *data; ++ unsigned len; ++}; ++ ++struct kvm_vcpu { ++ struct kvm *kvm; ++#ifdef CONFIG_PREEMPT_NOTIFIERS ++ struct preempt_notifier preempt_notifier; ++#endif ++ int cpu; ++ int vcpu_id; ++ int srcu_idx; ++ int mode; ++ unsigned long requests; ++ unsigned long guest_debug; ++ ++ struct mutex mutex; ++ struct kvm_run *run; ++ ++ int fpu_active; ++ int guest_fpu_loaded, guest_xcr0_loaded; ++ wait_queue_head_t wq; ++ struct pid *pid; ++ int sigset_active; ++ sigset_t sigset; ++ struct kvm_vcpu_stat stat; ++ ++#ifdef CONFIG_HAS_IOMEM ++ int mmio_needed; ++ int mmio_read_completed; ++ int mmio_is_write; ++ int mmio_cur_fragment; ++ int mmio_nr_fragments; ++ struct kvm_mmio_fragment mmio_fragments[KVM_MAX_MMIO_FRAGMENTS]; ++#endif ++ ++#ifdef CONFIG_KVM_ASYNC_PF ++ struct { ++ u32 queued; ++ struct list_head queue; ++ struct list_head done; ++ spinlock_t lock; ++ } async_pf; ++#endif ++ ++#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT ++ /* ++ * Cpu relax intercept or pause loop exit optimization ++ * in_spin_loop: set when a vcpu does a pause loop exit ++ * or cpu relax intercepted. ++ * dy_eligible: indicates whether vcpu is eligible for directed yield. ++ */ ++ struct { ++ bool in_spin_loop; ++ bool dy_eligible; ++ } spin_loop; ++#endif ++ bool preempted; ++ struct kvm_vcpu_arch arch; ++}; ++ ++static inline int kvm_vcpu_exiting_guest_mode(struct kvm_vcpu *vcpu) ++{ ++ return cmpxchg(&vcpu->mode, IN_GUEST_MODE, EXITING_GUEST_MODE); ++} ++ ++/* ++ * Some of the bitops functions do not support too long bitmaps. ++ * This number must be determined not to exceed such limits. ++ */ ++#define KVM_MEM_MAX_NR_PAGES ((1UL << 31) - 1) ++ ++struct kvm_memory_slot { ++ gfn_t base_gfn; ++ unsigned long npages; ++ unsigned long *dirty_bitmap; ++ struct kvm_arch_memory_slot arch; ++ unsigned long userspace_addr; ++ u32 flags; ++ short id; ++}; ++ ++static inline unsigned long kvm_dirty_bitmap_bytes(struct kvm_memory_slot *memslot) ++{ ++ return ALIGN(memslot->npages, BITS_PER_LONG) / 8; ++} ++ ++struct kvm_s390_adapter_int { ++ u64 ind_addr; ++ u64 summary_addr; ++ u64 ind_offset; ++ u32 summary_offset; ++ u32 adapter_id; ++}; ++ ++struct kvm_kernel_irq_routing_entry { ++ u32 gsi; ++ u32 type; ++ int (*set)(struct kvm_kernel_irq_routing_entry *e, ++ struct kvm *kvm, int irq_source_id, int level, ++ bool line_status); ++ union { ++ struct { ++ unsigned irqchip; ++ unsigned pin; ++ } irqchip; ++ struct msi_msg msi; ++ struct kvm_s390_adapter_int adapter; ++ }; ++ struct hlist_node link; ++}; ++ ++#ifndef KVM_PRIVATE_MEM_SLOTS ++#define KVM_PRIVATE_MEM_SLOTS 0 ++#endif ++ ++#ifndef KVM_MEM_SLOTS_NUM ++#define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS) ++#endif ++ ++/* ++ * Note: ++ * memslots are not sorted by id anymore, please use id_to_memslot() ++ * to get the memslot by its id. ++ */ ++struct kvm_memslots { ++ u64 generation; ++ struct kvm_memory_slot memslots[KVM_MEM_SLOTS_NUM]; ++ /* The mapping table from slot id to the index in memslots[]. */ ++ short id_to_index[KVM_MEM_SLOTS_NUM]; ++}; ++ ++struct kvm { ++ spinlock_t mmu_lock; ++ struct mutex slots_lock; ++ struct mm_struct *mm; /* userspace tied to this vm */ ++ struct kvm_memslots *memslots; ++ struct srcu_struct srcu; ++ struct srcu_struct irq_srcu; ++#ifdef CONFIG_KVM_APIC_ARCHITECTURE ++ u32 bsp_vcpu_id; ++#endif ++ struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; ++ atomic_t online_vcpus; ++ int last_boosted_vcpu; ++ struct list_head vm_list; ++ struct mutex lock; ++ struct kvm_io_bus *buses[KVM_NR_BUSES]; ++#ifdef CONFIG_HAVE_KVM_EVENTFD ++ struct { ++ spinlock_t lock; ++ struct list_head items; ++ struct list_head resampler_list; ++ struct mutex resampler_lock; ++ } irqfds; ++ struct list_head ioeventfds; ++#endif ++ struct kvm_vm_stat stat; ++ struct kvm_arch arch; ++ atomic_t users_count; ++#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET ++ struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; ++ spinlock_t ring_lock; ++ struct list_head coalesced_zones; ++#endif ++ ++ struct mutex irq_lock; ++#ifdef CONFIG_HAVE_KVM_IRQCHIP ++ /* ++ * Update side is protected by irq_lock. ++ */ ++ struct kvm_irq_routing_table __rcu *irq_routing; ++ struct hlist_head mask_notifier_list; ++#endif ++#ifdef CONFIG_HAVE_KVM_IRQFD ++ struct hlist_head irq_ack_notifier_list; ++#endif ++ ++#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) ++ struct mmu_notifier mmu_notifier; ++ unsigned long mmu_notifier_seq; ++ long mmu_notifier_count; ++#endif ++ long tlbs_dirty; ++ struct list_head devices; ++}; ++ ++#define kvm_err(fmt, ...) \ ++ pr_err("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) ++#define kvm_info(fmt, ...) \ ++ pr_info("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) ++#define kvm_debug(fmt, ...) \ ++ pr_debug("kvm [%i]: " fmt, task_pid_nr(current), ## __VA_ARGS__) ++#define kvm_pr_unimpl(fmt, ...) \ ++ pr_err_ratelimited("kvm [%i]: " fmt, \ ++ task_tgid_nr(current), ## __VA_ARGS__) ++ ++/* The guest did something we don't support. */ ++#define vcpu_unimpl(vcpu, fmt, ...) \ ++ kvm_pr_unimpl("vcpu%i " fmt, (vcpu)->vcpu_id, ## __VA_ARGS__) ++ ++static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i) ++{ ++ smp_rmb(); ++ return kvm->vcpus[i]; ++} ++ ++#define kvm_for_each_vcpu(idx, vcpup, kvm) \ ++ for (idx = 0; \ ++ idx < atomic_read(&kvm->online_vcpus) && \ ++ (vcpup = kvm_get_vcpu(kvm, idx)) != NULL; \ ++ idx++) ++ ++#define kvm_for_each_memslot(memslot, slots) \ ++ for (memslot = &slots->memslots[0]; \ ++ memslot < slots->memslots + KVM_MEM_SLOTS_NUM && memslot->npages;\ ++ memslot++) ++ ++int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id); ++void kvm_vcpu_uninit(struct kvm_vcpu *vcpu); ++ ++int __must_check vcpu_load(struct kvm_vcpu *vcpu); ++void vcpu_put(struct kvm_vcpu *vcpu); ++ ++#ifdef CONFIG_HAVE_KVM_IRQFD ++int kvm_irqfd_init(void); ++void kvm_irqfd_exit(void); ++#else ++static inline int kvm_irqfd_init(void) ++{ ++ return 0; ++} ++ ++static inline void kvm_irqfd_exit(void) ++{ ++} ++#endif ++int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, ++ struct module *module); ++void kvm_exit(void); ++ ++void kvm_get_kvm(struct kvm *kvm); ++void kvm_put_kvm(struct kvm *kvm); ++ ++static inline struct kvm_memslots *kvm_memslots(struct kvm *kvm) ++{ ++ return rcu_dereference_check(kvm->memslots, ++ srcu_read_lock_held(&kvm->srcu) ++ || lockdep_is_held(&kvm->slots_lock)); ++} ++ ++static inline struct kvm_memory_slot * ++id_to_memslot(struct kvm_memslots *slots, int id) ++{ ++ int index = slots->id_to_index[id]; ++ struct kvm_memory_slot *slot; ++ ++ slot = &slots->memslots[index]; ++ ++ WARN_ON(slot->id != id); ++ return slot; ++} ++ ++/* ++ * KVM_SET_USER_MEMORY_REGION ioctl allows the following operations: ++ * - create a new memory slot ++ * - delete an existing memory slot ++ * - modify an existing memory slot ++ * -- move it in the guest physical memory space ++ * -- just change its flags ++ * ++ * Since flags can be changed by some of these operations, the following ++ * differentiation is the best we can do for __kvm_set_memory_region(): ++ */ ++enum kvm_mr_change { ++ KVM_MR_CREATE, ++ KVM_MR_DELETE, ++ KVM_MR_MOVE, ++ KVM_MR_FLAGS_ONLY, ++}; ++ ++int kvm_set_memory_region(struct kvm *kvm, ++ struct kvm_userspace_memory_region *mem); ++int __kvm_set_memory_region(struct kvm *kvm, ++ struct kvm_userspace_memory_region *mem); ++void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free, ++ struct kvm_memory_slot *dont); ++int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, ++ unsigned long npages); ++void kvm_arch_memslots_updated(struct kvm *kvm); ++int kvm_arch_prepare_memory_region(struct kvm *kvm, ++ struct kvm_memory_slot *memslot, ++ struct kvm_userspace_memory_region *mem, ++ enum kvm_mr_change change); ++void kvm_arch_commit_memory_region(struct kvm *kvm, ++ struct kvm_userspace_memory_region *mem, ++ const struct kvm_memory_slot *old, ++ enum kvm_mr_change change); ++bool kvm_largepages_enabled(void); ++void kvm_disable_largepages(void); ++/* flush all memory translations */ ++void kvm_arch_flush_shadow_all(struct kvm *kvm); ++/* flush memory translations pointing to 'slot' */ ++void kvm_arch_flush_shadow_memslot(struct kvm *kvm, ++ struct kvm_memory_slot *slot); ++ ++int gfn_to_page_many_atomic(struct kvm *kvm, gfn_t gfn, struct page **pages, ++ int nr_pages); ++ ++struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); ++unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn); ++unsigned long gfn_to_hva_prot(struct kvm *kvm, gfn_t gfn, bool *writable); ++unsigned long gfn_to_hva_memslot(struct kvm_memory_slot *slot, gfn_t gfn); ++unsigned long gfn_to_hva_memslot_prot(struct kvm_memory_slot *slot, gfn_t gfn, ++ bool *writable); ++void kvm_release_page_clean(struct page *page); ++void kvm_release_page_dirty(struct page *page); ++void kvm_set_page_accessed(struct page *page); ++ ++pfn_t gfn_to_pfn_atomic(struct kvm *kvm, gfn_t gfn); ++pfn_t gfn_to_pfn_async(struct kvm *kvm, gfn_t gfn, bool *async, ++ bool write_fault, bool *writable); ++pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn); ++pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault, ++ bool *writable); ++pfn_t gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn); ++pfn_t gfn_to_pfn_memslot_atomic(struct kvm_memory_slot *slot, gfn_t gfn); ++ ++void kvm_release_pfn_clean(pfn_t pfn); ++void kvm_set_pfn_dirty(pfn_t pfn); ++void kvm_set_pfn_accessed(pfn_t pfn); ++void kvm_get_pfn(pfn_t pfn); ++ ++int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset, ++ int len); ++int kvm_read_guest_atomic(struct kvm *kvm, gpa_t gpa, void *data, ++ unsigned long len); ++int kvm_read_guest(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len); ++int kvm_read_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, ++ void *data, unsigned long len); ++int kvm_write_guest_page(struct kvm *kvm, gfn_t gfn, const void *data, ++ int offset, int len); ++int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data, ++ unsigned long len); ++int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, ++ void *data, unsigned long len); ++int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, ++ gpa_t gpa, unsigned long len); ++int kvm_clear_guest_page(struct kvm *kvm, gfn_t gfn, int offset, int len); ++int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len); ++struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn); ++int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn); ++unsigned long kvm_host_page_size(struct kvm *kvm, gfn_t gfn); ++void mark_page_dirty(struct kvm *kvm, gfn_t gfn); ++ ++void kvm_vcpu_block(struct kvm_vcpu *vcpu); ++void kvm_vcpu_kick(struct kvm_vcpu *vcpu); ++int kvm_vcpu_yield_to(struct kvm_vcpu *target); ++void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu); ++void kvm_load_guest_fpu(struct kvm_vcpu *vcpu); ++void kvm_put_guest_fpu(struct kvm_vcpu *vcpu); ++ ++void kvm_flush_remote_tlbs(struct kvm *kvm); ++void kvm_reload_remote_mmus(struct kvm *kvm); ++void kvm_make_mclock_inprogress_request(struct kvm *kvm); ++void kvm_make_scan_ioapic_request(struct kvm *kvm); ++bool kvm_make_all_cpus_request(struct kvm *kvm, unsigned int req); ++ ++long kvm_arch_dev_ioctl(struct file *filp, ++ unsigned int ioctl, unsigned long arg); ++long kvm_arch_vcpu_ioctl(struct file *filp, ++ unsigned int ioctl, unsigned long arg); ++int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf); ++ ++int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext); ++ ++int kvm_get_dirty_log(struct kvm *kvm, ++ struct kvm_dirty_log *log, int *is_dirty); ++int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, ++ struct kvm_dirty_log *log); ++ ++int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, ++ bool line_status); ++long kvm_arch_vm_ioctl(struct file *filp, ++ unsigned int ioctl, unsigned long arg); ++ ++int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu); ++int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu); ++ ++int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, ++ struct kvm_translation *tr); ++ ++int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); ++int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); ++int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, ++ struct kvm_sregs *sregs); ++int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, ++ struct kvm_sregs *sregs); ++int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, ++ struct kvm_mp_state *mp_state); ++int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, ++ struct kvm_mp_state *mp_state); ++int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, ++ struct kvm_guest_debug *dbg); ++int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run); ++ ++int kvm_arch_init(void *opaque); ++void kvm_arch_exit(void); ++ ++int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu); ++void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu); ++ ++void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu); ++ ++void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu); ++void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu); ++void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu); ++struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id); ++int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu); ++int kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu); ++void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu); ++ ++int kvm_arch_hardware_enable(void); ++void kvm_arch_hardware_disable(void); ++int kvm_arch_hardware_setup(void); ++void kvm_arch_hardware_unsetup(void); ++void kvm_arch_check_processor_compat(void *rtn); ++int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu); ++int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu); ++ ++void *kvm_kvzalloc(unsigned long size); ++void kvm_kvfree(const void *addr); ++ ++#ifndef __KVM_HAVE_ARCH_VM_ALLOC ++static inline struct kvm *kvm_arch_alloc_vm(void) ++{ ++ return kzalloc(sizeof(struct kvm), GFP_KERNEL); ++} ++ ++static inline void kvm_arch_free_vm(struct kvm *kvm) ++{ ++ kfree(kvm); ++} ++#endif ++ ++#ifdef __KVM_HAVE_ARCH_NONCOHERENT_DMA ++void kvm_arch_register_noncoherent_dma(struct kvm *kvm); ++void kvm_arch_unregister_noncoherent_dma(struct kvm *kvm); ++bool kvm_arch_has_noncoherent_dma(struct kvm *kvm); ++#else ++static inline void kvm_arch_register_noncoherent_dma(struct kvm *kvm) ++{ ++} ++ ++static inline void kvm_arch_unregister_noncoherent_dma(struct kvm *kvm) ++{ ++} ++ ++static inline bool kvm_arch_has_noncoherent_dma(struct kvm *kvm) ++{ ++ return false; ++} ++#endif ++ ++static inline wait_queue_head_t *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) ++{ ++#ifdef __KVM_HAVE_ARCH_WQP ++ return vcpu->arch.wqp; ++#else ++ return &vcpu->wq; ++#endif ++} ++ ++int kvm_arch_init_vm(struct kvm *kvm, unsigned long type); ++void kvm_arch_destroy_vm(struct kvm *kvm); ++void kvm_arch_sync_events(struct kvm *kvm); ++ ++int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu); ++void kvm_vcpu_kick(struct kvm_vcpu *vcpu); ++ ++bool kvm_is_reserved_pfn(pfn_t pfn); ++ ++struct kvm_irq_ack_notifier { ++ struct hlist_node link; ++ unsigned gsi; ++ void (*irq_acked)(struct kvm_irq_ack_notifier *kian); ++}; ++ ++struct kvm_assigned_dev_kernel { ++ struct kvm_irq_ack_notifier ack_notifier; ++ struct list_head list; ++ int assigned_dev_id; ++ int host_segnr; ++ int host_busnr; ++ int host_devfn; ++ unsigned int entries_nr; ++ int host_irq; ++ bool host_irq_disabled; ++ bool pci_2_3; ++ struct msix_entry *host_msix_entries; ++ int guest_irq; ++ struct msix_entry *guest_msix_entries; ++ unsigned long irq_requested_type; ++ int irq_source_id; ++ int flags; ++ struct pci_dev *dev; ++ struct kvm *kvm; ++ spinlock_t intx_lock; ++ spinlock_t intx_mask_lock; ++ char irq_name[32]; ++ struct pci_saved_state *pci_saved_state; ++}; ++ ++struct kvm_irq_mask_notifier { ++ void (*func)(struct kvm_irq_mask_notifier *kimn, bool masked); ++ int irq; ++ struct hlist_node link; ++}; ++ ++void kvm_register_irq_mask_notifier(struct kvm *kvm, int irq, ++ struct kvm_irq_mask_notifier *kimn); ++void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq, ++ struct kvm_irq_mask_notifier *kimn); ++void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin, ++ bool mask); ++ ++int kvm_irq_map_gsi(struct kvm *kvm, ++ struct kvm_kernel_irq_routing_entry *entries, int gsi); ++int kvm_irq_map_chip_pin(struct kvm *kvm, unsigned irqchip, unsigned pin); ++ ++int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level, ++ bool line_status); ++int kvm_set_irq_inatomic(struct kvm *kvm, int irq_source_id, u32 irq, int level); ++int kvm_set_msi(struct kvm_kernel_irq_routing_entry *irq_entry, struct kvm *kvm, ++ int irq_source_id, int level, bool line_status); ++bool kvm_irq_has_notifier(struct kvm *kvm, unsigned irqchip, unsigned pin); ++void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin); ++void kvm_register_irq_ack_notifier(struct kvm *kvm, ++ struct kvm_irq_ack_notifier *kian); ++void kvm_unregister_irq_ack_notifier(struct kvm *kvm, ++ struct kvm_irq_ack_notifier *kian); ++int kvm_request_irq_source_id(struct kvm *kvm); ++void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); ++ ++#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT ++int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot); ++void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot); ++int kvm_iommu_map_guest(struct kvm *kvm); ++int kvm_iommu_unmap_guest(struct kvm *kvm); ++int kvm_assign_device(struct kvm *kvm, ++ struct kvm_assigned_dev_kernel *assigned_dev); ++int kvm_deassign_device(struct kvm *kvm, ++ struct kvm_assigned_dev_kernel *assigned_dev); ++#else ++static inline int kvm_iommu_map_pages(struct kvm *kvm, ++ struct kvm_memory_slot *slot) ++{ ++ return 0; ++} ++ ++static inline void kvm_iommu_unmap_pages(struct kvm *kvm, ++ struct kvm_memory_slot *slot) ++{ ++} ++ ++static inline int kvm_iommu_unmap_guest(struct kvm *kvm) ++{ ++ return 0; ++} ++#endif ++ ++static inline void kvm_guest_enter(void) ++{ ++ unsigned long flags; ++ ++ BUG_ON(preemptible()); ++ ++ local_irq_save(flags); ++ guest_enter(); ++ local_irq_restore(flags); ++ ++ /* KVM does not hold any references to rcu protected data when it ++ * switches CPU into a guest mode. In fact switching to a guest mode ++ * is very similar to exiting to userspace from rcu point of view. In ++ * addition CPU may stay in a guest mode for quite a long time (up to ++ * one time slice). Lets treat guest mode as quiescent state, just like ++ * we do with user-mode execution. ++ */ ++ rcu_virt_note_context_switch(smp_processor_id()); ++} ++ ++static inline void kvm_guest_exit(void) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ guest_exit(); ++ local_irq_restore(flags); ++} ++ ++/* ++ * search_memslots() and __gfn_to_memslot() are here because they are ++ * used in non-modular code in arch/powerpc/kvm/book3s_hv_rm_mmu.c. ++ * gfn_to_memslot() itself isn't here as an inline because that would ++ * bloat other code too much. ++ */ ++static inline struct kvm_memory_slot * ++search_memslots(struct kvm_memslots *slots, gfn_t gfn) ++{ ++ struct kvm_memory_slot *memslot; ++ ++ kvm_for_each_memslot(memslot, slots) ++ if (gfn >= memslot->base_gfn && ++ gfn < memslot->base_gfn + memslot->npages) ++ return memslot; ++ ++ return NULL; ++} ++ ++static inline struct kvm_memory_slot * ++__gfn_to_memslot(struct kvm_memslots *slots, gfn_t gfn) ++{ ++ return search_memslots(slots, gfn); ++} ++ ++static inline unsigned long ++__gfn_to_hva_memslot(struct kvm_memory_slot *slot, gfn_t gfn) ++{ ++ return slot->userspace_addr + (gfn - slot->base_gfn) * PAGE_SIZE; ++} ++ ++static inline int memslot_id(struct kvm *kvm, gfn_t gfn) ++{ ++ return gfn_to_memslot(kvm, gfn)->id; ++} ++ ++static inline gfn_t ++hva_to_gfn_memslot(unsigned long hva, struct kvm_memory_slot *slot) ++{ ++ gfn_t gfn_offset = (hva - slot->userspace_addr) >> PAGE_SHIFT; ++ ++ return slot->base_gfn + gfn_offset; ++} ++ ++static inline gpa_t gfn_to_gpa(gfn_t gfn) ++{ ++ return (gpa_t)gfn << PAGE_SHIFT; ++} ++ ++static inline gfn_t gpa_to_gfn(gpa_t gpa) ++{ ++ return (gfn_t)(gpa >> PAGE_SHIFT); ++} ++ ++static inline hpa_t pfn_to_hpa(pfn_t pfn) ++{ ++ return (hpa_t)pfn << PAGE_SHIFT; ++} ++ ++static inline bool kvm_is_error_gpa(struct kvm *kvm, gpa_t gpa) ++{ ++ unsigned long hva = gfn_to_hva(kvm, gpa_to_gfn(gpa)); ++ ++ return kvm_is_error_hva(hva); ++} ++ ++static inline void kvm_migrate_timers(struct kvm_vcpu *vcpu) ++{ ++ set_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests); ++} ++ ++enum kvm_stat_kind { ++ KVM_STAT_VM, ++ KVM_STAT_VCPU, ++}; ++ ++struct kvm_stats_debugfs_item { ++ const char *name; ++ int offset; ++ enum kvm_stat_kind kind; ++ struct dentry *dentry; ++}; ++extern struct kvm_stats_debugfs_item debugfs_entries[]; ++extern struct dentry *kvm_debugfs_dir; ++ ++#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER) ++static inline int mmu_notifier_retry(struct kvm *kvm, unsigned long mmu_seq) ++{ ++ if (unlikely(kvm->mmu_notifier_count)) ++ return 1; ++ /* ++ * Ensure the read of mmu_notifier_count happens before the read ++ * of mmu_notifier_seq. This interacts with the smp_wmb() in ++ * mmu_notifier_invalidate_range_end to make sure that the caller ++ * either sees the old (non-zero) value of mmu_notifier_count or ++ * the new (incremented) value of mmu_notifier_seq. ++ * PowerPC Book3s HV KVM calls this under a per-page lock ++ * rather than under kvm->mmu_lock, for scalability, so ++ * can't rely on kvm->mmu_lock to keep things ordered. ++ */ ++ smp_rmb(); ++ if (kvm->mmu_notifier_seq != mmu_seq) ++ return 1; ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_HAVE_KVM_IRQ_ROUTING ++ ++#ifdef CONFIG_S390 ++#define KVM_MAX_IRQ_ROUTES 4096 //FIXME: we can have more than that... ++#else ++#define KVM_MAX_IRQ_ROUTES 1024 ++#endif ++ ++int kvm_setup_default_irq_routing(struct kvm *kvm); ++int kvm_set_irq_routing(struct kvm *kvm, ++ const struct kvm_irq_routing_entry *entries, ++ unsigned nr, ++ unsigned flags); ++int kvm_set_routing_entry(struct kvm_kernel_irq_routing_entry *e, ++ const struct kvm_irq_routing_entry *ue); ++void kvm_free_irq_routing(struct kvm *kvm); ++ ++#else ++ ++static inline void kvm_free_irq_routing(struct kvm *kvm) {} ++ ++#endif ++ ++int kvm_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi); ++ ++#ifdef CONFIG_HAVE_KVM_EVENTFD ++ ++void kvm_eventfd_init(struct kvm *kvm); ++int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args); ++ ++#ifdef CONFIG_HAVE_KVM_IRQFD ++int kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args); ++void kvm_irqfd_release(struct kvm *kvm); ++void kvm_irq_routing_update(struct kvm *); ++#else ++static inline int kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args) ++{ ++ return -EINVAL; ++} ++ ++static inline void kvm_irqfd_release(struct kvm *kvm) {} ++#endif ++ ++#else ++ ++static inline void kvm_eventfd_init(struct kvm *kvm) {} ++ ++static inline int kvm_irqfd(struct kvm *kvm, struct kvm_irqfd *args) ++{ ++ return -EINVAL; ++} ++ ++static inline void kvm_irqfd_release(struct kvm *kvm) {} ++ ++#ifdef CONFIG_HAVE_KVM_IRQCHIP ++static inline void kvm_irq_routing_update(struct kvm *kvm) ++{ ++} ++#endif ++ ++static inline int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) ++{ ++ return -ENOSYS; ++} ++ ++#endif /* CONFIG_HAVE_KVM_EVENTFD */ ++ ++#ifdef CONFIG_KVM_APIC_ARCHITECTURE ++static inline bool kvm_vcpu_is_bsp(struct kvm_vcpu *vcpu) ++{ ++ return vcpu->kvm->bsp_vcpu_id == vcpu->vcpu_id; ++} ++ ++bool kvm_vcpu_compatible(struct kvm_vcpu *vcpu); ++ ++#else ++ ++static inline bool kvm_vcpu_compatible(struct kvm_vcpu *vcpu) { return true; } ++ ++#endif ++ ++#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT ++ ++long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl, ++ unsigned long arg); ++ ++void kvm_free_all_assigned_devices(struct kvm *kvm); ++ ++#else ++ ++static inline long kvm_vm_ioctl_assigned_device(struct kvm *kvm, unsigned ioctl, ++ unsigned long arg) ++{ ++ return -ENOTTY; ++} ++ ++static inline void kvm_free_all_assigned_devices(struct kvm *kvm) {} ++ ++#endif ++ ++static inline void kvm_make_request(int req, struct kvm_vcpu *vcpu) ++{ ++ set_bit(req, &vcpu->requests); ++} ++ ++static inline bool kvm_check_request(int req, struct kvm_vcpu *vcpu) ++{ ++ if (test_bit(req, &vcpu->requests)) { ++ clear_bit(req, &vcpu->requests); ++ return true; ++ } else { ++ return false; ++ } ++} ++ ++extern bool kvm_rebooting; ++ ++struct kvm_device { ++ struct kvm_device_ops *ops; ++ struct kvm *kvm; ++ void *private; ++ struct list_head vm_node; ++}; ++ ++/* create, destroy, and name are mandatory */ ++struct kvm_device_ops { ++ const char *name; ++ int (*create)(struct kvm_device *dev, u32 type); ++ ++ /* ++ * Destroy is responsible for freeing dev. ++ * ++ * Destroy may be called before or after destructors are called ++ * on emulated I/O regions, depending on whether a reference is ++ * held by a vcpu or other kvm component that gets destroyed ++ * after the emulated I/O. ++ */ ++ void (*destroy)(struct kvm_device *dev); ++ ++ int (*set_attr)(struct kvm_device *dev, struct kvm_device_attr *attr); ++ int (*get_attr)(struct kvm_device *dev, struct kvm_device_attr *attr); ++ int (*has_attr)(struct kvm_device *dev, struct kvm_device_attr *attr); ++ long (*ioctl)(struct kvm_device *dev, unsigned int ioctl, ++ unsigned long arg); ++}; ++ ++void kvm_device_get(struct kvm_device *dev); ++void kvm_device_put(struct kvm_device *dev); ++struct kvm_device *kvm_device_from_filp(struct file *filp); ++int kvm_register_device_ops(struct kvm_device_ops *ops, u32 type); ++void kvm_unregister_device_ops(u32 type); ++ ++extern struct kvm_device_ops kvm_mpic_ops; ++extern struct kvm_device_ops kvm_xics_ops; ++ ++#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT ++ ++static inline void kvm_vcpu_set_in_spin_loop(struct kvm_vcpu *vcpu, bool val) ++{ ++ vcpu->spin_loop.in_spin_loop = val; ++} ++static inline void kvm_vcpu_set_dy_eligible(struct kvm_vcpu *vcpu, bool val) ++{ ++ vcpu->spin_loop.dy_eligible = val; ++} ++ ++#else /* !CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT */ ++ ++static inline void kvm_vcpu_set_in_spin_loop(struct kvm_vcpu *vcpu, bool val) ++{ ++} ++ ++static inline void kvm_vcpu_set_dy_eligible(struct kvm_vcpu *vcpu, bool val) ++{ ++} ++#endif /* CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT */ ++#endif ++ +diff -Nur linux-3.18.14.orig/include/linux/lglock.h linux-3.18.14-rt/include/linux/lglock.h +--- linux-3.18.14.orig/include/linux/lglock.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/lglock.h 2015-05-31 15:32:48.261635369 -0500 +@@ -34,22 +34,39 @@ + #endif + + struct lglock { ++#ifndef CONFIG_PREEMPT_RT_FULL + arch_spinlock_t __percpu *lock; ++#else ++ struct rt_mutex __percpu *lock; ++#endif + #ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lock_class_key lock_key; + struct lockdep_map lock_dep_map; + #endif + }; + +-#define DEFINE_LGLOCK(name) \ ++#ifndef CONFIG_PREEMPT_RT_FULL ++# define DEFINE_LGLOCK(name) \ + static DEFINE_PER_CPU(arch_spinlock_t, name ## _lock) \ + = __ARCH_SPIN_LOCK_UNLOCKED; \ + struct lglock name = { .lock = &name ## _lock } + +-#define DEFINE_STATIC_LGLOCK(name) \ ++# define DEFINE_STATIC_LGLOCK(name) \ + static DEFINE_PER_CPU(arch_spinlock_t, name ## _lock) \ + = __ARCH_SPIN_LOCK_UNLOCKED; \ + static struct lglock name = { .lock = &name ## _lock } ++#else ++ ++# define DEFINE_LGLOCK(name) \ ++ static DEFINE_PER_CPU(struct rt_mutex, name ## _lock) \ ++ = __RT_MUTEX_INITIALIZER( name ## _lock); \ ++ struct lglock name = { .lock = &name ## _lock } ++ ++# define DEFINE_STATIC_LGLOCK(name) \ ++ static DEFINE_PER_CPU(struct rt_mutex, name ## _lock) \ ++ = __RT_MUTEX_INITIALIZER( name ## _lock); \ ++ static struct lglock name = { .lock = &name ## _lock } ++#endif + + void lg_lock_init(struct lglock *lg, char *name); + void lg_local_lock(struct lglock *lg); +@@ -59,6 +76,12 @@ + void lg_global_lock(struct lglock *lg); + void lg_global_unlock(struct lglock *lg); + ++#ifndef CONFIG_PREEMPT_RT_FULL ++#define lg_global_trylock_relax(name) lg_global_lock(name) ++#else ++void lg_global_trylock_relax(struct lglock *lg); ++#endif ++ + #else + /* When !CONFIG_SMP, map lglock to spinlock */ + #define lglock spinlock +diff -Nur linux-3.18.14.orig/include/linux/list_bl.h linux-3.18.14-rt/include/linux/list_bl.h +--- linux-3.18.14.orig/include/linux/list_bl.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/list_bl.h 2015-05-31 15:32:48.265635369 -0500 +@@ -2,6 +2,7 @@ + #define _LINUX_LIST_BL_H + + #include ++#include + #include + + /* +@@ -32,13 +33,22 @@ + + struct hlist_bl_head { + struct hlist_bl_node *first; ++#ifdef CONFIG_PREEMPT_RT_BASE ++ raw_spinlock_t lock; ++#endif + }; + + struct hlist_bl_node { + struct hlist_bl_node *next, **pprev; + }; +-#define INIT_HLIST_BL_HEAD(ptr) \ +- ((ptr)->first = NULL) ++ ++static inline void INIT_HLIST_BL_HEAD(struct hlist_bl_head *h) ++{ ++ h->first = NULL; ++#ifdef CONFIG_PREEMPT_RT_BASE ++ raw_spin_lock_init(&h->lock); ++#endif ++} + + static inline void INIT_HLIST_BL_NODE(struct hlist_bl_node *h) + { +@@ -117,12 +127,26 @@ + + static inline void hlist_bl_lock(struct hlist_bl_head *b) + { ++#ifndef CONFIG_PREEMPT_RT_BASE + bit_spin_lock(0, (unsigned long *)b); ++#else ++ raw_spin_lock(&b->lock); ++#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) ++ __set_bit(0, (unsigned long *)b); ++#endif ++#endif + } + + static inline void hlist_bl_unlock(struct hlist_bl_head *b) + { ++#ifndef CONFIG_PREEMPT_RT_BASE + __bit_spin_unlock(0, (unsigned long *)b); ++#else ++#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) ++ __clear_bit(0, (unsigned long *)b); ++#endif ++ raw_spin_unlock(&b->lock); ++#endif + } + + static inline bool hlist_bl_is_locked(struct hlist_bl_head *b) +diff -Nur linux-3.18.14.orig/include/linux/locallock.h linux-3.18.14-rt/include/linux/locallock.h +--- linux-3.18.14.orig/include/linux/locallock.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/locallock.h 2015-05-31 15:32:48.273635368 -0500 +@@ -0,0 +1,270 @@ ++#ifndef _LINUX_LOCALLOCK_H ++#define _LINUX_LOCALLOCK_H ++ ++#include ++#include ++ ++#ifdef CONFIG_PREEMPT_RT_BASE ++ ++#ifdef CONFIG_DEBUG_SPINLOCK ++# define LL_WARN(cond) WARN_ON(cond) ++#else ++# define LL_WARN(cond) do { } while (0) ++#endif ++ ++/* ++ * per cpu lock based substitute for local_irq_*() ++ */ ++struct local_irq_lock { ++ spinlock_t lock; ++ struct task_struct *owner; ++ int nestcnt; ++ unsigned long flags; ++}; ++ ++#define DEFINE_LOCAL_IRQ_LOCK(lvar) \ ++ DEFINE_PER_CPU(struct local_irq_lock, lvar) = { \ ++ .lock = __SPIN_LOCK_UNLOCKED((lvar).lock) } ++ ++#define DECLARE_LOCAL_IRQ_LOCK(lvar) \ ++ DECLARE_PER_CPU(struct local_irq_lock, lvar) ++ ++#define local_irq_lock_init(lvar) \ ++ do { \ ++ int __cpu; \ ++ for_each_possible_cpu(__cpu) \ ++ spin_lock_init(&per_cpu(lvar, __cpu).lock); \ ++ } while (0) ++ ++/* ++ * spin_lock|trylock|unlock_local flavour that does not migrate disable ++ * used for __local_lock|trylock|unlock where get_local_var/put_local_var ++ * already takes care of the migrate_disable/enable ++ * for CONFIG_PREEMPT_BASE map to the normal spin_* calls. ++ */ ++#ifdef CONFIG_PREEMPT_RT_FULL ++# define spin_lock_local(lock) rt_spin_lock(lock) ++# define spin_trylock_local(lock) rt_spin_trylock(lock) ++# define spin_unlock_local(lock) rt_spin_unlock(lock) ++#else ++# define spin_lock_local(lock) spin_lock(lock) ++# define spin_trylock_local(lock) spin_trylock(lock) ++# define spin_unlock_local(lock) spin_unlock(lock) ++#endif ++ ++static inline void __local_lock(struct local_irq_lock *lv) ++{ ++ if (lv->owner != current) { ++ spin_lock_local(&lv->lock); ++ LL_WARN(lv->owner); ++ LL_WARN(lv->nestcnt); ++ lv->owner = current; ++ } ++ lv->nestcnt++; ++} ++ ++#define local_lock(lvar) \ ++ do { __local_lock(&get_local_var(lvar)); } while (0) ++ ++static inline int __local_trylock(struct local_irq_lock *lv) ++{ ++ if (lv->owner != current && spin_trylock_local(&lv->lock)) { ++ LL_WARN(lv->owner); ++ LL_WARN(lv->nestcnt); ++ lv->owner = current; ++ lv->nestcnt = 1; ++ return 1; ++ } ++ return 0; ++} ++ ++#define local_trylock(lvar) \ ++ ({ \ ++ int __locked; \ ++ __locked = __local_trylock(&get_local_var(lvar)); \ ++ if (!__locked) \ ++ put_local_var(lvar); \ ++ __locked; \ ++ }) ++ ++static inline void __local_unlock(struct local_irq_lock *lv) ++{ ++ LL_WARN(lv->nestcnt == 0); ++ LL_WARN(lv->owner != current); ++ if (--lv->nestcnt) ++ return; ++ ++ lv->owner = NULL; ++ spin_unlock_local(&lv->lock); ++} ++ ++#define local_unlock(lvar) \ ++ do { \ ++ __local_unlock(&__get_cpu_var(lvar)); \ ++ put_local_var(lvar); \ ++ } while (0) ++ ++static inline void __local_lock_irq(struct local_irq_lock *lv) ++{ ++ spin_lock_irqsave(&lv->lock, lv->flags); ++ LL_WARN(lv->owner); ++ LL_WARN(lv->nestcnt); ++ lv->owner = current; ++ lv->nestcnt = 1; ++} ++ ++#define local_lock_irq(lvar) \ ++ do { __local_lock_irq(&get_local_var(lvar)); } while (0) ++ ++#define local_lock_irq_on(lvar, cpu) \ ++ do { __local_lock_irq(&per_cpu(lvar, cpu)); } while (0) ++ ++static inline void __local_unlock_irq(struct local_irq_lock *lv) ++{ ++ LL_WARN(!lv->nestcnt); ++ LL_WARN(lv->owner != current); ++ lv->owner = NULL; ++ lv->nestcnt = 0; ++ spin_unlock_irq(&lv->lock); ++} ++ ++#define local_unlock_irq(lvar) \ ++ do { \ ++ __local_unlock_irq(&__get_cpu_var(lvar)); \ ++ put_local_var(lvar); \ ++ } while (0) ++ ++#define local_unlock_irq_on(lvar, cpu) \ ++ do { \ ++ __local_unlock_irq(&per_cpu(lvar, cpu)); \ ++ } while (0) ++ ++static inline int __local_lock_irqsave(struct local_irq_lock *lv) ++{ ++ if (lv->owner != current) { ++ __local_lock_irq(lv); ++ return 0; ++ } else { ++ lv->nestcnt++; ++ return 1; ++ } ++} ++ ++#define local_lock_irqsave(lvar, _flags) \ ++ do { \ ++ if (__local_lock_irqsave(&get_local_var(lvar))) \ ++ put_local_var(lvar); \ ++ _flags = __get_cpu_var(lvar).flags; \ ++ } while (0) ++ ++#define local_lock_irqsave_on(lvar, _flags, cpu) \ ++ do { \ ++ __local_lock_irqsave(&per_cpu(lvar, cpu)); \ ++ _flags = per_cpu(lvar, cpu).flags; \ ++ } while (0) ++ ++static inline int __local_unlock_irqrestore(struct local_irq_lock *lv, ++ unsigned long flags) ++{ ++ LL_WARN(!lv->nestcnt); ++ LL_WARN(lv->owner != current); ++ if (--lv->nestcnt) ++ return 0; ++ ++ lv->owner = NULL; ++ spin_unlock_irqrestore(&lv->lock, lv->flags); ++ return 1; ++} ++ ++#define local_unlock_irqrestore(lvar, flags) \ ++ do { \ ++ if (__local_unlock_irqrestore(&__get_cpu_var(lvar), flags)) \ ++ put_local_var(lvar); \ ++ } while (0) ++ ++#define local_unlock_irqrestore_on(lvar, flags, cpu) \ ++ do { \ ++ __local_unlock_irqrestore(&per_cpu(lvar, cpu), flags); \ ++ } while (0) ++ ++#define local_spin_trylock_irq(lvar, lock) \ ++ ({ \ ++ int __locked; \ ++ local_lock_irq(lvar); \ ++ __locked = spin_trylock(lock); \ ++ if (!__locked) \ ++ local_unlock_irq(lvar); \ ++ __locked; \ ++ }) ++ ++#define local_spin_lock_irq(lvar, lock) \ ++ do { \ ++ local_lock_irq(lvar); \ ++ spin_lock(lock); \ ++ } while (0) ++ ++#define local_spin_unlock_irq(lvar, lock) \ ++ do { \ ++ spin_unlock(lock); \ ++ local_unlock_irq(lvar); \ ++ } while (0) ++ ++#define local_spin_lock_irqsave(lvar, lock, flags) \ ++ do { \ ++ local_lock_irqsave(lvar, flags); \ ++ spin_lock(lock); \ ++ } while (0) ++ ++#define local_spin_unlock_irqrestore(lvar, lock, flags) \ ++ do { \ ++ spin_unlock(lock); \ ++ local_unlock_irqrestore(lvar, flags); \ ++ } while (0) ++ ++#define get_locked_var(lvar, var) \ ++ (*({ \ ++ local_lock(lvar); \ ++ &__get_cpu_var(var); \ ++ })) ++ ++#define put_locked_var(lvar, var) local_unlock(lvar); ++ ++#define local_lock_cpu(lvar) \ ++ ({ \ ++ local_lock(lvar); \ ++ smp_processor_id(); \ ++ }) ++ ++#define local_unlock_cpu(lvar) local_unlock(lvar) ++ ++#else /* PREEMPT_RT_BASE */ ++ ++#define DEFINE_LOCAL_IRQ_LOCK(lvar) __typeof__(const int) lvar ++#define DECLARE_LOCAL_IRQ_LOCK(lvar) extern __typeof__(const int) lvar ++ ++static inline void local_irq_lock_init(int lvar) { } ++ ++#define local_lock(lvar) preempt_disable() ++#define local_unlock(lvar) preempt_enable() ++#define local_lock_irq(lvar) local_irq_disable() ++#define local_unlock_irq(lvar) local_irq_enable() ++#define local_lock_irqsave(lvar, flags) local_irq_save(flags) ++#define local_unlock_irqrestore(lvar, flags) local_irq_restore(flags) ++ ++#define local_spin_trylock_irq(lvar, lock) spin_trylock_irq(lock) ++#define local_spin_lock_irq(lvar, lock) spin_lock_irq(lock) ++#define local_spin_unlock_irq(lvar, lock) spin_unlock_irq(lock) ++#define local_spin_lock_irqsave(lvar, lock, flags) \ ++ spin_lock_irqsave(lock, flags) ++#define local_spin_unlock_irqrestore(lvar, lock, flags) \ ++ spin_unlock_irqrestore(lock, flags) ++ ++#define get_locked_var(lvar, var) get_cpu_var(var) ++#define put_locked_var(lvar, var) put_cpu_var(var) ++ ++#define local_lock_cpu(lvar) get_cpu() ++#define local_unlock_cpu(lvar) put_cpu() ++ ++#endif ++ ++#endif +diff -Nur linux-3.18.14.orig/include/linux/mm_types.h linux-3.18.14-rt/include/linux/mm_types.h +--- linux-3.18.14.orig/include/linux/mm_types.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/mm_types.h 2015-05-31 15:32:48.273635368 -0500 +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -454,6 +455,9 @@ + bool tlb_flush_pending; + #endif + struct uprobes_state uprobes_state; ++#ifdef CONFIG_PREEMPT_RT_BASE ++ struct rcu_head delayed_drop; ++#endif + }; + + static inline void mm_init_cpumask(struct mm_struct *mm) +diff -Nur linux-3.18.14.orig/include/linux/mutex.h linux-3.18.14-rt/include/linux/mutex.h +--- linux-3.18.14.orig/include/linux/mutex.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/mutex.h 2015-05-31 15:32:48.273635368 -0500 +@@ -19,6 +19,17 @@ + #include + #include + ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \ ++ , .dep_map = { .name = #lockname } ++#else ++# define __DEP_MAP_MUTEX_INITIALIZER(lockname) ++#endif ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++# include ++#else ++ + /* + * Simple, straightforward mutexes with strict semantics: + * +@@ -100,13 +111,6 @@ + static inline void mutex_destroy(struct mutex *lock) {} + #endif + +-#ifdef CONFIG_DEBUG_LOCK_ALLOC +-# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \ +- , .dep_map = { .name = #lockname } +-#else +-# define __DEP_MAP_MUTEX_INITIALIZER(lockname) +-#endif +- + #define __MUTEX_INITIALIZER(lockname) \ + { .count = ATOMIC_INIT(1) \ + , .wait_lock = __SPIN_LOCK_UNLOCKED(lockname.wait_lock) \ +@@ -174,6 +178,8 @@ + extern int mutex_trylock(struct mutex *lock); + extern void mutex_unlock(struct mutex *lock); + ++#endif /* !PREEMPT_RT_FULL */ ++ + extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock); + + #endif /* __LINUX_MUTEX_H */ +diff -Nur linux-3.18.14.orig/include/linux/mutex_rt.h linux-3.18.14-rt/include/linux/mutex_rt.h +--- linux-3.18.14.orig/include/linux/mutex_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/mutex_rt.h 2015-05-31 15:32:48.273635368 -0500 +@@ -0,0 +1,84 @@ ++#ifndef __LINUX_MUTEX_RT_H ++#define __LINUX_MUTEX_RT_H ++ ++#ifndef __LINUX_MUTEX_H ++#error "Please include mutex.h" ++#endif ++ ++#include ++ ++/* FIXME: Just for __lockfunc */ ++#include ++ ++struct mutex { ++ struct rt_mutex lock; ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ struct lockdep_map dep_map; ++#endif ++}; ++ ++#define __MUTEX_INITIALIZER(mutexname) \ ++ { \ ++ .lock = __RT_MUTEX_INITIALIZER(mutexname.lock) \ ++ __DEP_MAP_MUTEX_INITIALIZER(mutexname) \ ++ } ++ ++#define DEFINE_MUTEX(mutexname) \ ++ struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) ++ ++extern void __mutex_do_init(struct mutex *lock, const char *name, struct lock_class_key *key); ++extern void __lockfunc _mutex_lock(struct mutex *lock); ++extern int __lockfunc _mutex_lock_interruptible(struct mutex *lock); ++extern int __lockfunc _mutex_lock_killable(struct mutex *lock); ++extern void __lockfunc _mutex_lock_nested(struct mutex *lock, int subclass); ++extern void __lockfunc _mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest_lock); ++extern int __lockfunc _mutex_lock_interruptible_nested(struct mutex *lock, int subclass); ++extern int __lockfunc _mutex_lock_killable_nested(struct mutex *lock, int subclass); ++extern int __lockfunc _mutex_trylock(struct mutex *lock); ++extern void __lockfunc _mutex_unlock(struct mutex *lock); ++ ++#define mutex_is_locked(l) rt_mutex_is_locked(&(l)->lock) ++#define mutex_lock(l) _mutex_lock(l) ++#define mutex_lock_interruptible(l) _mutex_lock_interruptible(l) ++#define mutex_lock_killable(l) _mutex_lock_killable(l) ++#define mutex_trylock(l) _mutex_trylock(l) ++#define mutex_unlock(l) _mutex_unlock(l) ++#define mutex_destroy(l) rt_mutex_destroy(&(l)->lock) ++ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++# define mutex_lock_nested(l, s) _mutex_lock_nested(l, s) ++# define mutex_lock_interruptible_nested(l, s) \ ++ _mutex_lock_interruptible_nested(l, s) ++# define mutex_lock_killable_nested(l, s) \ ++ _mutex_lock_killable_nested(l, s) ++ ++# define mutex_lock_nest_lock(lock, nest_lock) \ ++do { \ ++ typecheck(struct lockdep_map *, &(nest_lock)->dep_map); \ ++ _mutex_lock_nest_lock(lock, &(nest_lock)->dep_map); \ ++} while (0) ++ ++#else ++# define mutex_lock_nested(l, s) _mutex_lock(l) ++# define mutex_lock_interruptible_nested(l, s) \ ++ _mutex_lock_interruptible(l) ++# define mutex_lock_killable_nested(l, s) \ ++ _mutex_lock_killable(l) ++# define mutex_lock_nest_lock(lock, nest_lock) mutex_lock(lock) ++#endif ++ ++# define mutex_init(mutex) \ ++do { \ ++ static struct lock_class_key __key; \ ++ \ ++ rt_mutex_init(&(mutex)->lock); \ ++ __mutex_do_init((mutex), #mutex, &__key); \ ++} while (0) ++ ++# define __mutex_init(mutex, name, key) \ ++do { \ ++ rt_mutex_init(&(mutex)->lock); \ ++ __mutex_do_init((mutex), name, key); \ ++} while (0) ++ ++#endif +diff -Nur linux-3.18.14.orig/include/linux/netdevice.h linux-3.18.14-rt/include/linux/netdevice.h +--- linux-3.18.14.orig/include/linux/netdevice.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/netdevice.h 2015-05-31 15:32:48.305635368 -0500 +@@ -2351,6 +2351,7 @@ + unsigned int dropped; + struct sk_buff_head input_pkt_queue; + struct napi_struct backlog; ++ struct sk_buff_head tofree_queue; + + #ifdef CONFIG_NET_FLOW_LIMIT + struct sd_flow_limit __rcu *flow_limit; +diff -Nur linux-3.18.14.orig/include/linux/netfilter/x_tables.h linux-3.18.14-rt/include/linux/netfilter/x_tables.h +--- linux-3.18.14.orig/include/linux/netfilter/x_tables.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/netfilter/x_tables.h 2015-05-31 15:32:48.305635368 -0500 +@@ -3,6 +3,7 @@ + + + #include ++#include + #include + + /** +@@ -282,6 +283,8 @@ + */ + DECLARE_PER_CPU(seqcount_t, xt_recseq); + ++DECLARE_LOCAL_IRQ_LOCK(xt_write_lock); ++ + /** + * xt_write_recseq_begin - start of a write section + * +@@ -296,6 +299,9 @@ + { + unsigned int addend; + ++ /* RT protection */ ++ local_lock(xt_write_lock); ++ + /* + * Low order bit of sequence is set if we already + * called xt_write_recseq_begin(). +@@ -326,6 +332,7 @@ + /* this is kind of a write_seqcount_end(), but addend is 0 or 1 */ + smp_wmb(); + __this_cpu_add(xt_recseq.sequence, addend); ++ local_unlock(xt_write_lock); + } + + /* +diff -Nur linux-3.18.14.orig/include/linux/notifier.h linux-3.18.14-rt/include/linux/notifier.h +--- linux-3.18.14.orig/include/linux/notifier.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/notifier.h 2015-05-31 15:32:48.305635368 -0500 +@@ -6,7 +6,7 @@ + * + * Alan Cox + */ +- ++ + #ifndef _LINUX_NOTIFIER_H + #define _LINUX_NOTIFIER_H + #include +@@ -42,9 +42,7 @@ + * in srcu_notifier_call_chain(): no cache bounces and no memory barriers. + * As compensation, srcu_notifier_chain_unregister() is rather expensive. + * SRCU notifier chains should be used when the chain will be called very +- * often but notifier_blocks will seldom be removed. Also, SRCU notifier +- * chains are slightly more difficult to use because they require special +- * runtime initialization. ++ * often but notifier_blocks will seldom be removed. + */ + + typedef int (*notifier_fn_t)(struct notifier_block *nb, +@@ -88,7 +86,7 @@ + (name)->head = NULL; \ + } while (0) + +-/* srcu_notifier_heads must be initialized and cleaned up dynamically */ ++/* srcu_notifier_heads must be cleaned up dynamically */ + extern void srcu_init_notifier_head(struct srcu_notifier_head *nh); + #define srcu_cleanup_notifier_head(name) \ + cleanup_srcu_struct(&(name)->srcu); +@@ -101,7 +99,13 @@ + .head = NULL } + #define RAW_NOTIFIER_INIT(name) { \ + .head = NULL } +-/* srcu_notifier_heads cannot be initialized statically */ ++ ++#define SRCU_NOTIFIER_INIT(name, pcpu) \ ++ { \ ++ .mutex = __MUTEX_INITIALIZER(name.mutex), \ ++ .head = NULL, \ ++ .srcu = __SRCU_STRUCT_INIT(name.srcu, pcpu), \ ++ } + + #define ATOMIC_NOTIFIER_HEAD(name) \ + struct atomic_notifier_head name = \ +@@ -113,6 +117,18 @@ + struct raw_notifier_head name = \ + RAW_NOTIFIER_INIT(name) + ++#define _SRCU_NOTIFIER_HEAD(name, mod) \ ++ static DEFINE_PER_CPU(struct srcu_struct_array, \ ++ name##_head_srcu_array); \ ++ mod struct srcu_notifier_head name = \ ++ SRCU_NOTIFIER_INIT(name, name##_head_srcu_array) ++ ++#define SRCU_NOTIFIER_HEAD(name) \ ++ _SRCU_NOTIFIER_HEAD(name, ) ++ ++#define SRCU_NOTIFIER_HEAD_STATIC(name) \ ++ _SRCU_NOTIFIER_HEAD(name, static) ++ + #ifdef __KERNEL__ + + extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh, +@@ -182,12 +198,12 @@ + + /* + * Declared notifiers so far. I can imagine quite a few more chains +- * over time (eg laptop power reset chains, reboot chain (to clean ++ * over time (eg laptop power reset chains, reboot chain (to clean + * device units up), device [un]mount chain, module load/unload chain, +- * low memory chain, screenblank chain (for plug in modular screenblankers) ++ * low memory chain, screenblank chain (for plug in modular screenblankers) + * VC switch chains (for loadable kernel svgalib VC switch helpers) etc... + */ +- ++ + /* CPU notfiers are defined in include/linux/cpu.h. */ + + /* netdevice notifiers are defined in include/linux/netdevice.h */ +diff -Nur linux-3.18.14.orig/include/linux/percpu.h linux-3.18.14-rt/include/linux/percpu.h +--- linux-3.18.14.orig/include/linux/percpu.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/percpu.h 2015-05-31 15:32:48.305635368 -0500 +@@ -23,6 +23,35 @@ + PERCPU_MODULE_RESERVE) + #endif + ++#ifdef CONFIG_PREEMPT_RT_FULL ++ ++#define get_local_var(var) (*({ \ ++ migrate_disable(); \ ++ &__get_cpu_var(var); })) ++ ++#define put_local_var(var) do { \ ++ (void)&(var); \ ++ migrate_enable(); \ ++} while (0) ++ ++# define get_local_ptr(var) ({ \ ++ migrate_disable(); \ ++ this_cpu_ptr(var); }) ++ ++# define put_local_ptr(var) do { \ ++ (void)(var); \ ++ migrate_enable(); \ ++} while (0) ++ ++#else ++ ++#define get_local_var(var) get_cpu_var(var) ++#define put_local_var(var) put_cpu_var(var) ++#define get_local_ptr(var) get_cpu_ptr(var) ++#define put_local_ptr(var) put_cpu_ptr(var) ++ ++#endif ++ + /* minimum unit size, also is the maximum supported allocation size */ + #define PCPU_MIN_UNIT_SIZE PFN_ALIGN(32 << 10) + +diff -Nur linux-3.18.14.orig/include/linux/pid.h linux-3.18.14-rt/include/linux/pid.h +--- linux-3.18.14.orig/include/linux/pid.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/pid.h 2015-05-31 15:32:48.341635368 -0500 +@@ -2,6 +2,7 @@ + #define _LINUX_PID_H + + #include ++#include + + enum pid_type + { +diff -Nur linux-3.18.14.orig/include/linux/preempt.h linux-3.18.14-rt/include/linux/preempt.h +--- linux-3.18.14.orig/include/linux/preempt.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/preempt.h 2015-05-31 15:32:48.341635368 -0500 +@@ -33,6 +33,20 @@ + #define preempt_count_inc() preempt_count_add(1) + #define preempt_count_dec() preempt_count_sub(1) + ++#ifdef CONFIG_PREEMPT_LAZY ++#define add_preempt_lazy_count(val) do { preempt_lazy_count() += (val); } while (0) ++#define sub_preempt_lazy_count(val) do { preempt_lazy_count() -= (val); } while (0) ++#define inc_preempt_lazy_count() add_preempt_lazy_count(1) ++#define dec_preempt_lazy_count() sub_preempt_lazy_count(1) ++#define preempt_lazy_count() (current_thread_info()->preempt_lazy_count) ++#else ++#define add_preempt_lazy_count(val) do { } while (0) ++#define sub_preempt_lazy_count(val) do { } while (0) ++#define inc_preempt_lazy_count() do { } while (0) ++#define dec_preempt_lazy_count() do { } while (0) ++#define preempt_lazy_count() (0) ++#endif ++ + #ifdef CONFIG_PREEMPT_COUNT + + #define preempt_disable() \ +@@ -41,13 +55,25 @@ + barrier(); \ + } while (0) + ++#define preempt_lazy_disable() \ ++do { \ ++ inc_preempt_lazy_count(); \ ++ barrier(); \ ++} while (0) ++ + #define sched_preempt_enable_no_resched() \ + do { \ + barrier(); \ + preempt_count_dec(); \ + } while (0) + +-#define preempt_enable_no_resched() sched_preempt_enable_no_resched() ++#ifdef CONFIG_PREEMPT_RT_BASE ++# define preempt_enable_no_resched() sched_preempt_enable_no_resched() ++# define preempt_check_resched_rt() preempt_check_resched() ++#else ++# define preempt_enable_no_resched() preempt_enable() ++# define preempt_check_resched_rt() barrier(); ++#endif + + #ifdef CONFIG_PREEMPT + #define preempt_enable() \ +@@ -63,6 +89,13 @@ + __preempt_schedule(); \ + } while (0) + ++#define preempt_lazy_enable() \ ++do { \ ++ dec_preempt_lazy_count(); \ ++ barrier(); \ ++ preempt_check_resched(); \ ++} while (0) ++ + #else + #define preempt_enable() \ + do { \ +@@ -121,6 +154,7 @@ + #define preempt_disable_notrace() barrier() + #define preempt_enable_no_resched_notrace() barrier() + #define preempt_enable_notrace() barrier() ++#define preempt_check_resched_rt() barrier() + + #endif /* CONFIG_PREEMPT_COUNT */ + +@@ -140,10 +174,31 @@ + } while (0) + #define preempt_fold_need_resched() \ + do { \ +- if (tif_need_resched()) \ ++ if (tif_need_resched_now()) \ + set_preempt_need_resched(); \ + } while (0) + ++#ifdef CONFIG_PREEMPT_RT_FULL ++# define preempt_disable_rt() preempt_disable() ++# define preempt_enable_rt() preempt_enable() ++# define preempt_disable_nort() barrier() ++# define preempt_enable_nort() barrier() ++# ifdef CONFIG_SMP ++ extern void migrate_disable(void); ++ extern void migrate_enable(void); ++# else /* CONFIG_SMP */ ++# define migrate_disable() barrier() ++# define migrate_enable() barrier() ++# endif /* CONFIG_SMP */ ++#else ++# define preempt_disable_rt() barrier() ++# define preempt_enable_rt() barrier() ++# define preempt_disable_nort() preempt_disable() ++# define preempt_enable_nort() preempt_enable() ++# define migrate_disable() preempt_disable() ++# define migrate_enable() preempt_enable() ++#endif ++ + #ifdef CONFIG_PREEMPT_NOTIFIERS + + struct preempt_notifier; +diff -Nur linux-3.18.14.orig/include/linux/preempt_mask.h linux-3.18.14-rt/include/linux/preempt_mask.h +--- linux-3.18.14.orig/include/linux/preempt_mask.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/preempt_mask.h 2015-05-31 15:32:48.341635368 -0500 +@@ -44,16 +44,26 @@ + #define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) + #define NMI_OFFSET (1UL << NMI_SHIFT) + +-#define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET) ++#ifndef CONFIG_PREEMPT_RT_FULL ++# define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET) ++#else ++# define SOFTIRQ_DISABLE_OFFSET (0) ++#endif + + #define PREEMPT_ACTIVE_BITS 1 + #define PREEMPT_ACTIVE_SHIFT (NMI_SHIFT + NMI_BITS) + #define PREEMPT_ACTIVE (__IRQ_MASK(PREEMPT_ACTIVE_BITS) << PREEMPT_ACTIVE_SHIFT) + + #define hardirq_count() (preempt_count() & HARDIRQ_MASK) +-#define softirq_count() (preempt_count() & SOFTIRQ_MASK) + #define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \ + | NMI_MASK)) ++#ifndef CONFIG_PREEMPT_RT_FULL ++# define softirq_count() (preempt_count() & SOFTIRQ_MASK) ++# define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET) ++#else ++# define softirq_count() (0UL) ++extern int in_serving_softirq(void); ++#endif + + /* + * Are we doing bottom half or hardware interrupt processing? +@@ -64,7 +74,6 @@ + #define in_irq() (hardirq_count()) + #define in_softirq() (softirq_count()) + #define in_interrupt() (irq_count()) +-#define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET) + + /* + * Are we in NMI context? +diff -Nur linux-3.18.14.orig/include/linux/printk.h linux-3.18.14-rt/include/linux/printk.h +--- linux-3.18.14.orig/include/linux/printk.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/printk.h 2015-05-31 15:32:48.341635368 -0500 +@@ -119,9 +119,11 @@ + extern asmlinkage __printf(1, 2) + void early_printk(const char *fmt, ...); + void early_vprintk(const char *fmt, va_list ap); ++extern void printk_kill(void); + #else + static inline __printf(1, 2) __cold + void early_printk(const char *s, ...) { } ++static inline void printk_kill(void) { } + #endif + + #ifdef CONFIG_PRINTK +@@ -155,7 +157,6 @@ + #define printk_ratelimit() __printk_ratelimit(__func__) + extern bool printk_timed_ratelimit(unsigned long *caller_jiffies, + unsigned int interval_msec); +- + extern int printk_delay_msec; + extern int dmesg_restrict; + extern int kptr_restrict; +diff -Nur linux-3.18.14.orig/include/linux/radix-tree.h linux-3.18.14-rt/include/linux/radix-tree.h +--- linux-3.18.14.orig/include/linux/radix-tree.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/radix-tree.h 2015-05-31 15:32:48.341635368 -0500 +@@ -277,8 +277,13 @@ + unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root, + void ***results, unsigned long *indices, + unsigned long first_index, unsigned int max_items); ++#ifndef CONFIG_PREEMPT_RT_FULL + int radix_tree_preload(gfp_t gfp_mask); + int radix_tree_maybe_preload(gfp_t gfp_mask); ++#else ++static inline int radix_tree_preload(gfp_t gm) { return 0; } ++static inline int radix_tree_maybe_preload(gfp_t gfp_mask) { return 0; } ++#endif + void radix_tree_init(void); + void *radix_tree_tag_set(struct radix_tree_root *root, + unsigned long index, unsigned int tag); +@@ -303,7 +308,7 @@ + + static inline void radix_tree_preload_end(void) + { +- preempt_enable(); ++ preempt_enable_nort(); + } + + /** +diff -Nur linux-3.18.14.orig/include/linux/random.h linux-3.18.14-rt/include/linux/random.h +--- linux-3.18.14.orig/include/linux/random.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/random.h 2015-05-31 15:32:48.341635368 -0500 +@@ -11,7 +11,7 @@ + extern void add_device_randomness(const void *, unsigned int); + extern void add_input_randomness(unsigned int type, unsigned int code, + unsigned int value); +-extern void add_interrupt_randomness(int irq, int irq_flags); ++extern void add_interrupt_randomness(int irq, int irq_flags, __u64 ip); + + extern void get_random_bytes(void *buf, int nbytes); + extern void get_random_bytes_arch(void *buf, int nbytes); +diff -Nur linux-3.18.14.orig/include/linux/rcupdate.h linux-3.18.14-rt/include/linux/rcupdate.h +--- linux-3.18.14.orig/include/linux/rcupdate.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/rcupdate.h 2015-05-31 15:32:48.341635368 -0500 +@@ -147,6 +147,9 @@ + + #endif /* #else #ifdef CONFIG_PREEMPT_RCU */ + ++#ifdef CONFIG_PREEMPT_RT_FULL ++#define call_rcu_bh call_rcu ++#else + /** + * call_rcu_bh() - Queue an RCU for invocation after a quicker grace period. + * @head: structure to be used for queueing the RCU updates. +@@ -170,6 +173,7 @@ + */ + void call_rcu_bh(struct rcu_head *head, + void (*func)(struct rcu_head *head)); ++#endif + + /** + * call_rcu_sched() - Queue an RCU for invocation after sched grace period. +@@ -231,6 +235,11 @@ + * types of kernel builds, the rcu_read_lock() nesting depth is unknowable. + */ + #define rcu_preempt_depth() (current->rcu_read_lock_nesting) ++#ifndef CONFIG_PREEMPT_RT_FULL ++#define sched_rcu_preempt_depth() rcu_preempt_depth() ++#else ++static inline int sched_rcu_preempt_depth(void) { return 0; } ++#endif + + #else /* #ifdef CONFIG_PREEMPT_RCU */ + +@@ -254,6 +263,8 @@ + return 0; + } + ++#define sched_rcu_preempt_depth() rcu_preempt_depth() ++ + #endif /* #else #ifdef CONFIG_PREEMPT_RCU */ + + /* Internal to kernel */ +@@ -430,7 +441,14 @@ + int debug_lockdep_rcu_enabled(void); + + int rcu_read_lock_held(void); ++#ifdef CONFIG_PREEMPT_RT_FULL ++static inline int rcu_read_lock_bh_held(void) ++{ ++ return rcu_read_lock_held(); ++} ++#else + int rcu_read_lock_bh_held(void); ++#endif + + /** + * rcu_read_lock_sched_held() - might we be in RCU-sched read-side critical section? +@@ -955,10 +973,14 @@ + static inline void rcu_read_lock_bh(void) + { + local_bh_disable(); ++#ifdef CONFIG_PREEMPT_RT_FULL ++ rcu_read_lock(); ++#else + __acquire(RCU_BH); + rcu_lock_acquire(&rcu_bh_lock_map); + rcu_lockdep_assert(rcu_is_watching(), + "rcu_read_lock_bh() used illegally while idle"); ++#endif + } + + /* +@@ -968,10 +990,14 @@ + */ + static inline void rcu_read_unlock_bh(void) + { ++#ifdef CONFIG_PREEMPT_RT_FULL ++ rcu_read_unlock(); ++#else + rcu_lockdep_assert(rcu_is_watching(), + "rcu_read_unlock_bh() used illegally while idle"); + rcu_lock_release(&rcu_bh_lock_map); + __release(RCU_BH); ++#endif + local_bh_enable(); + } + +diff -Nur linux-3.18.14.orig/include/linux/rcutree.h linux-3.18.14-rt/include/linux/rcutree.h +--- linux-3.18.14.orig/include/linux/rcutree.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/rcutree.h 2015-05-31 15:32:48.361635367 -0500 +@@ -46,7 +46,11 @@ + rcu_note_context_switch(cpu); + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++# define synchronize_rcu_bh synchronize_rcu ++#else + void synchronize_rcu_bh(void); ++#endif + void synchronize_sched_expedited(void); + void synchronize_rcu_expedited(void); + +@@ -74,7 +78,11 @@ + } + + void rcu_barrier(void); ++#ifdef CONFIG_PREEMPT_RT_FULL ++# define rcu_barrier_bh rcu_barrier ++#else + void rcu_barrier_bh(void); ++#endif + void rcu_barrier_sched(void); + unsigned long get_state_synchronize_rcu(void); + void cond_synchronize_rcu(unsigned long oldstate); +@@ -82,12 +90,10 @@ + extern unsigned long rcutorture_testseq; + extern unsigned long rcutorture_vernum; + long rcu_batches_completed(void); +-long rcu_batches_completed_bh(void); + long rcu_batches_completed_sched(void); + void show_rcu_gp_kthreads(void); + + void rcu_force_quiescent_state(void); +-void rcu_bh_force_quiescent_state(void); + void rcu_sched_force_quiescent_state(void); + + void exit_rcu(void); +@@ -97,4 +103,12 @@ + + bool rcu_is_watching(void); + ++#ifndef CONFIG_PREEMPT_RT_FULL ++void rcu_bh_force_quiescent_state(void); ++long rcu_batches_completed_bh(void); ++#else ++# define rcu_bh_force_quiescent_state rcu_force_quiescent_state ++# define rcu_batches_completed_bh rcu_batches_completed ++#endif ++ + #endif /* __LINUX_RCUTREE_H */ +diff -Nur linux-3.18.14.orig/include/linux/rtmutex.h linux-3.18.14-rt/include/linux/rtmutex.h +--- linux-3.18.14.orig/include/linux/rtmutex.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/rtmutex.h 2015-05-31 15:32:48.377635367 -0500 +@@ -14,10 +14,14 @@ + + #include + #include +-#include ++#include + + extern int max_lock_depth; /* for sysctl */ + ++#ifdef CONFIG_DEBUG_MUTEXES ++#include ++#endif ++ + /** + * The rt_mutex structure + * +@@ -31,8 +35,8 @@ + struct rb_root waiters; + struct rb_node *waiters_leftmost; + struct task_struct *owner; +-#ifdef CONFIG_DEBUG_RT_MUTEXES + int save_state; ++#ifdef CONFIG_DEBUG_RT_MUTEXES + const char *name, *file; + int line; + void *magic; +@@ -55,22 +59,33 @@ + # define rt_mutex_debug_check_no_locks_held(task) do { } while (0) + #endif + ++# define rt_mutex_init(mutex) \ ++ do { \ ++ raw_spin_lock_init(&(mutex)->wait_lock); \ ++ __rt_mutex_init(mutex, #mutex); \ ++ } while (0) ++ + #ifdef CONFIG_DEBUG_RT_MUTEXES + # define __DEBUG_RT_MUTEX_INITIALIZER(mutexname) \ + , .name = #mutexname, .file = __FILE__, .line = __LINE__ +-# define rt_mutex_init(mutex) __rt_mutex_init(mutex, __func__) + extern void rt_mutex_debug_task_free(struct task_struct *tsk); + #else + # define __DEBUG_RT_MUTEX_INITIALIZER(mutexname) +-# define rt_mutex_init(mutex) __rt_mutex_init(mutex, NULL) + # define rt_mutex_debug_task_free(t) do { } while (0) + #endif + +-#define __RT_MUTEX_INITIALIZER(mutexname) \ +- { .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(mutexname.wait_lock) \ ++#define __RT_MUTEX_INITIALIZER_PLAIN(mutexname) \ ++ .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(mutexname.wait_lock) \ + , .waiters = RB_ROOT \ + , .owner = NULL \ +- __DEBUG_RT_MUTEX_INITIALIZER(mutexname)} ++ __DEBUG_RT_MUTEX_INITIALIZER(mutexname) ++ ++#define __RT_MUTEX_INITIALIZER(mutexname) \ ++ { __RT_MUTEX_INITIALIZER_PLAIN(mutexname) } ++ ++#define __RT_MUTEX_INITIALIZER_SAVE_STATE(mutexname) \ ++ { __RT_MUTEX_INITIALIZER_PLAIN(mutexname) \ ++ , .save_state = 1 } + + #define DEFINE_RT_MUTEX(mutexname) \ + struct rt_mutex mutexname = __RT_MUTEX_INITIALIZER(mutexname) +@@ -91,6 +106,7 @@ + + extern void rt_mutex_lock(struct rt_mutex *lock); + extern int rt_mutex_lock_interruptible(struct rt_mutex *lock); ++extern int rt_mutex_lock_killable(struct rt_mutex *lock); + extern int rt_mutex_timed_lock(struct rt_mutex *lock, + struct hrtimer_sleeper *timeout); + +diff -Nur linux-3.18.14.orig/include/linux/rwlock_rt.h linux-3.18.14-rt/include/linux/rwlock_rt.h +--- linux-3.18.14.orig/include/linux/rwlock_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/rwlock_rt.h 2015-05-31 15:32:48.377635367 -0500 +@@ -0,0 +1,99 @@ ++#ifndef __LINUX_RWLOCK_RT_H ++#define __LINUX_RWLOCK_RT_H ++ ++#ifndef __LINUX_SPINLOCK_H ++#error Do not include directly. Use spinlock.h ++#endif ++ ++#define rwlock_init(rwl) \ ++do { \ ++ static struct lock_class_key __key; \ ++ \ ++ rt_mutex_init(&(rwl)->lock); \ ++ __rt_rwlock_init(rwl, #rwl, &__key); \ ++} while (0) ++ ++extern void __lockfunc rt_write_lock(rwlock_t *rwlock); ++extern void __lockfunc rt_read_lock(rwlock_t *rwlock); ++extern int __lockfunc rt_write_trylock(rwlock_t *rwlock); ++extern int __lockfunc rt_write_trylock_irqsave(rwlock_t *trylock, unsigned long *flags); ++extern int __lockfunc rt_read_trylock(rwlock_t *rwlock); ++extern void __lockfunc rt_write_unlock(rwlock_t *rwlock); ++extern void __lockfunc rt_read_unlock(rwlock_t *rwlock); ++extern unsigned long __lockfunc rt_write_lock_irqsave(rwlock_t *rwlock); ++extern unsigned long __lockfunc rt_read_lock_irqsave(rwlock_t *rwlock); ++extern void __rt_rwlock_init(rwlock_t *rwlock, char *name, struct lock_class_key *key); ++ ++#define read_trylock(lock) __cond_lock(lock, rt_read_trylock(lock)) ++#define write_trylock(lock) __cond_lock(lock, rt_write_trylock(lock)) ++ ++#define write_trylock_irqsave(lock, flags) \ ++ __cond_lock(lock, rt_write_trylock_irqsave(lock, &flags)) ++ ++#define read_lock_irqsave(lock, flags) \ ++ do { \ ++ typecheck(unsigned long, flags); \ ++ flags = rt_read_lock_irqsave(lock); \ ++ } while (0) ++ ++#define write_lock_irqsave(lock, flags) \ ++ do { \ ++ typecheck(unsigned long, flags); \ ++ flags = rt_write_lock_irqsave(lock); \ ++ } while (0) ++ ++#define read_lock(lock) rt_read_lock(lock) ++ ++#define read_lock_bh(lock) \ ++ do { \ ++ local_bh_disable(); \ ++ rt_read_lock(lock); \ ++ } while (0) ++ ++#define read_lock_irq(lock) read_lock(lock) ++ ++#define write_lock(lock) rt_write_lock(lock) ++ ++#define write_lock_bh(lock) \ ++ do { \ ++ local_bh_disable(); \ ++ rt_write_lock(lock); \ ++ } while (0) ++ ++#define write_lock_irq(lock) write_lock(lock) ++ ++#define read_unlock(lock) rt_read_unlock(lock) ++ ++#define read_unlock_bh(lock) \ ++ do { \ ++ rt_read_unlock(lock); \ ++ local_bh_enable(); \ ++ } while (0) ++ ++#define read_unlock_irq(lock) read_unlock(lock) ++ ++#define write_unlock(lock) rt_write_unlock(lock) ++ ++#define write_unlock_bh(lock) \ ++ do { \ ++ rt_write_unlock(lock); \ ++ local_bh_enable(); \ ++ } while (0) ++ ++#define write_unlock_irq(lock) write_unlock(lock) ++ ++#define read_unlock_irqrestore(lock, flags) \ ++ do { \ ++ typecheck(unsigned long, flags); \ ++ (void) flags; \ ++ rt_read_unlock(lock); \ ++ } while (0) ++ ++#define write_unlock_irqrestore(lock, flags) \ ++ do { \ ++ typecheck(unsigned long, flags); \ ++ (void) flags; \ ++ rt_write_unlock(lock); \ ++ } while (0) ++ ++#endif +diff -Nur linux-3.18.14.orig/include/linux/rwlock_types.h linux-3.18.14-rt/include/linux/rwlock_types.h +--- linux-3.18.14.orig/include/linux/rwlock_types.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/rwlock_types.h 2015-05-31 15:32:48.377635367 -0500 +@@ -1,6 +1,10 @@ + #ifndef __LINUX_RWLOCK_TYPES_H + #define __LINUX_RWLOCK_TYPES_H + ++#if !defined(__LINUX_SPINLOCK_TYPES_H) ++# error "Do not include directly, include spinlock_types.h" ++#endif ++ + /* + * include/linux/rwlock_types.h - generic rwlock type definitions + * and initializers +@@ -43,6 +47,7 @@ + RW_DEP_MAP_INIT(lockname) } + #endif + +-#define DEFINE_RWLOCK(x) rwlock_t x = __RW_LOCK_UNLOCKED(x) ++#define DEFINE_RWLOCK(name) \ ++ rwlock_t name __cacheline_aligned_in_smp = __RW_LOCK_UNLOCKED(name) + + #endif /* __LINUX_RWLOCK_TYPES_H */ +diff -Nur linux-3.18.14.orig/include/linux/rwlock_types_rt.h linux-3.18.14-rt/include/linux/rwlock_types_rt.h +--- linux-3.18.14.orig/include/linux/rwlock_types_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/rwlock_types_rt.h 2015-05-31 15:32:48.377635367 -0500 +@@ -0,0 +1,33 @@ ++#ifndef __LINUX_RWLOCK_TYPES_RT_H ++#define __LINUX_RWLOCK_TYPES_RT_H ++ ++#ifndef __LINUX_SPINLOCK_TYPES_H ++#error "Do not include directly. Include spinlock_types.h instead" ++#endif ++ ++/* ++ * rwlocks - rtmutex which allows single reader recursion ++ */ ++typedef struct { ++ struct rt_mutex lock; ++ int read_depth; ++ unsigned int break_lock; ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ struct lockdep_map dep_map; ++#endif ++} rwlock_t; ++ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++# define RW_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname } ++#else ++# define RW_DEP_MAP_INIT(lockname) ++#endif ++ ++#define __RW_LOCK_UNLOCKED(name) \ ++ { .lock = __RT_MUTEX_INITIALIZER_SAVE_STATE(name.lock), \ ++ RW_DEP_MAP_INIT(name) } ++ ++#define DEFINE_RWLOCK(name) \ ++ rwlock_t name __cacheline_aligned_in_smp = __RW_LOCK_UNLOCKED(name) ++ ++#endif +diff -Nur linux-3.18.14.orig/include/linux/rwsem.h linux-3.18.14-rt/include/linux/rwsem.h +--- linux-3.18.14.orig/include/linux/rwsem.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/rwsem.h 2015-05-31 15:32:48.377635367 -0500 +@@ -18,6 +18,10 @@ + #include + #endif + ++#ifdef CONFIG_PREEMPT_RT_FULL ++#include ++#else /* PREEMPT_RT_FULL */ ++ + struct rw_semaphore; + + #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK +@@ -177,4 +181,6 @@ + # define up_read_non_owner(sem) up_read(sem) + #endif + ++#endif /* !PREEMPT_RT_FULL */ ++ + #endif /* _LINUX_RWSEM_H */ +diff -Nur linux-3.18.14.orig/include/linux/rwsem_rt.h linux-3.18.14-rt/include/linux/rwsem_rt.h +--- linux-3.18.14.orig/include/linux/rwsem_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/rwsem_rt.h 2015-05-31 15:32:48.377635367 -0500 +@@ -0,0 +1,134 @@ ++#ifndef _LINUX_RWSEM_RT_H ++#define _LINUX_RWSEM_RT_H ++ ++#ifndef _LINUX_RWSEM_H ++#error "Include rwsem.h" ++#endif ++ ++/* ++ * RW-semaphores are a spinlock plus a reader-depth count. ++ * ++ * Note that the semantics are different from the usual ++ * Linux rw-sems, in PREEMPT_RT mode we do not allow ++ * multiple readers to hold the lock at once, we only allow ++ * a read-lock owner to read-lock recursively. This is ++ * better for latency, makes the implementation inherently ++ * fair and makes it simpler as well. ++ */ ++ ++#include ++ ++struct rw_semaphore { ++ struct rt_mutex lock; ++ int read_depth; ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ struct lockdep_map dep_map; ++#endif ++}; ++ ++#define __RWSEM_INITIALIZER(name) \ ++ { .lock = __RT_MUTEX_INITIALIZER(name.lock), \ ++ RW_DEP_MAP_INIT(name) } ++ ++#define DECLARE_RWSEM(lockname) \ ++ struct rw_semaphore lockname = __RWSEM_INITIALIZER(lockname) ++ ++extern void __rt_rwsem_init(struct rw_semaphore *rwsem, const char *name, ++ struct lock_class_key *key); ++ ++#define __rt_init_rwsem(sem, name, key) \ ++ do { \ ++ rt_mutex_init(&(sem)->lock); \ ++ __rt_rwsem_init((sem), (name), (key));\ ++ } while (0) ++ ++#define __init_rwsem(sem, name, key) __rt_init_rwsem(sem, name, key) ++ ++# define rt_init_rwsem(sem) \ ++do { \ ++ static struct lock_class_key __key; \ ++ \ ++ __rt_init_rwsem((sem), #sem, &__key); \ ++} while (0) ++ ++extern void rt_down_write(struct rw_semaphore *rwsem); ++extern void rt_down_read_nested(struct rw_semaphore *rwsem, int subclass); ++extern void rt_down_write_nested(struct rw_semaphore *rwsem, int subclass); ++extern void rt_down_write_nested_lock(struct rw_semaphore *rwsem, ++ struct lockdep_map *nest); ++extern void rt_down_read(struct rw_semaphore *rwsem); ++extern int rt_down_write_trylock(struct rw_semaphore *rwsem); ++extern int rt_down_read_trylock(struct rw_semaphore *rwsem); ++extern void rt_up_read(struct rw_semaphore *rwsem); ++extern void rt_up_write(struct rw_semaphore *rwsem); ++extern void rt_downgrade_write(struct rw_semaphore *rwsem); ++ ++#define init_rwsem(sem) rt_init_rwsem(sem) ++#define rwsem_is_locked(s) rt_mutex_is_locked(&(s)->lock) ++ ++static inline int rwsem_is_contended(struct rw_semaphore *sem) ++{ ++ /* rt_mutex_has_waiters() */ ++ return !RB_EMPTY_ROOT(&sem->lock.waiters); ++} ++ ++static inline void down_read(struct rw_semaphore *sem) ++{ ++ rt_down_read(sem); ++} ++ ++static inline int down_read_trylock(struct rw_semaphore *sem) ++{ ++ return rt_down_read_trylock(sem); ++} ++ ++static inline void down_write(struct rw_semaphore *sem) ++{ ++ rt_down_write(sem); ++} ++ ++static inline int down_write_trylock(struct rw_semaphore *sem) ++{ ++ return rt_down_write_trylock(sem); ++} ++ ++static inline void up_read(struct rw_semaphore *sem) ++{ ++ rt_up_read(sem); ++} ++ ++static inline void up_write(struct rw_semaphore *sem) ++{ ++ rt_up_write(sem); ++} ++ ++static inline void downgrade_write(struct rw_semaphore *sem) ++{ ++ rt_downgrade_write(sem); ++} ++ ++static inline void down_read_nested(struct rw_semaphore *sem, int subclass) ++{ ++ return rt_down_read_nested(sem, subclass); ++} ++ ++static inline void down_write_nested(struct rw_semaphore *sem, int subclass) ++{ ++ rt_down_write_nested(sem, subclass); ++} ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++static inline void down_write_nest_lock(struct rw_semaphore *sem, ++ struct rw_semaphore *nest_lock) ++{ ++ rt_down_write_nested_lock(sem, &nest_lock->dep_map); ++} ++ ++#else ++ ++static inline void down_write_nest_lock(struct rw_semaphore *sem, ++ struct rw_semaphore *nest_lock) ++{ ++ rt_down_write_nested_lock(sem, NULL); ++} ++#endif ++#endif +diff -Nur linux-3.18.14.orig/include/linux/sched.h linux-3.18.14-rt/include/linux/sched.h +--- linux-3.18.14.orig/include/linux/sched.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/sched.h 2015-05-31 15:32:48.381635367 -0500 +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -56,6 +57,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -235,10 +237,7 @@ + TASK_UNINTERRUPTIBLE | __TASK_STOPPED | \ + __TASK_TRACED | EXIT_ZOMBIE | EXIT_DEAD) + +-#define task_is_traced(task) ((task->state & __TASK_TRACED) != 0) + #define task_is_stopped(task) ((task->state & __TASK_STOPPED) != 0) +-#define task_is_stopped_or_traced(task) \ +- ((task->state & (__TASK_STOPPED | __TASK_TRACED)) != 0) + #define task_contributes_to_load(task) \ + ((task->state & TASK_UNINTERRUPTIBLE) != 0 && \ + (task->flags & PF_FROZEN) == 0) +@@ -1234,6 +1233,7 @@ + + struct task_struct { + volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ ++ volatile long saved_state; /* saved state for "spinlock sleepers" */ + void *stack; + atomic_t usage; + unsigned int flags; /* per process flags, defined below */ +@@ -1270,6 +1270,12 @@ + #endif + + unsigned int policy; ++#ifdef CONFIG_PREEMPT_RT_FULL ++ int migrate_disable; ++# ifdef CONFIG_SCHED_DEBUG ++ int migrate_disable_atomic; ++# endif ++#endif + int nr_cpus_allowed; + cpumask_t cpus_allowed; + +@@ -1371,7 +1377,8 @@ + struct cputime prev_cputime; + #endif + #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN +- seqlock_t vtime_seqlock; ++ raw_spinlock_t vtime_lock; ++ seqcount_t vtime_seq; + unsigned long long vtime_snap; + enum { + VTIME_SLEEPING = 0, +@@ -1387,6 +1394,9 @@ + + struct task_cputime cputime_expires; + struct list_head cpu_timers[3]; ++#ifdef CONFIG_PREEMPT_RT_BASE ++ struct task_struct *posix_timer_list; ++#endif + + /* process credentials */ + const struct cred __rcu *real_cred; /* objective and real subjective task +@@ -1419,10 +1429,15 @@ + /* signal handlers */ + struct signal_struct *signal; + struct sighand_struct *sighand; ++ struct sigqueue *sigqueue_cache; + + sigset_t blocked, real_blocked; + sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */ + struct sigpending pending; ++#ifdef CONFIG_PREEMPT_RT_FULL ++ /* TODO: move me into ->restart_block ? */ ++ struct siginfo forced_info; ++#endif + + unsigned long sas_ss_sp; + size_t sas_ss_size; +@@ -1460,6 +1475,9 @@ + /* mutex deadlock detection */ + struct mutex_waiter *blocked_on; + #endif ++#ifdef CONFIG_PREEMPT_RT_FULL ++ int pagefault_disabled; ++#endif + #ifdef CONFIG_TRACE_IRQFLAGS + unsigned int irq_events; + unsigned long hardirq_enable_ip; +@@ -1644,6 +1662,12 @@ + unsigned long trace; + /* bitmask and counter of trace recursion */ + unsigned long trace_recursion; ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++ u64 preempt_timestamp_hist; ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ long timer_offset; ++#endif ++#endif + #endif /* CONFIG_TRACING */ + #ifdef CONFIG_MEMCG /* memcg uses this to do batch job */ + unsigned int memcg_kmem_skip_account; +@@ -1661,11 +1685,19 @@ + unsigned int sequential_io; + unsigned int sequential_io_avg; + #endif ++#ifdef CONFIG_PREEMPT_RT_BASE ++ struct rcu_head put_rcu; ++ int softirq_nestcnt; ++ unsigned int softirqs_raised; ++#endif ++#ifdef CONFIG_PREEMPT_RT_FULL ++# if defined CONFIG_HIGHMEM || defined CONFIG_X86_32 ++ int kmap_idx; ++ pte_t kmap_pte[KM_TYPE_NR]; ++# endif ++#endif + }; + +-/* Future-safe accessor for struct task_struct's cpus_allowed. */ +-#define tsk_cpus_allowed(tsk) (&(tsk)->cpus_allowed) +- + #define TNF_MIGRATED 0x01 + #define TNF_NO_GROUP 0x02 + #define TNF_SHARED 0x04 +@@ -1700,6 +1732,17 @@ + } + #endif + ++#ifdef CONFIG_PREEMPT_RT_FULL ++static inline bool cur_pf_disabled(void) { return current->pagefault_disabled; } ++#else ++static inline bool cur_pf_disabled(void) { return false; } ++#endif ++ ++static inline bool pagefault_disabled(void) ++{ ++ return in_atomic() || cur_pf_disabled(); ++} ++ + static inline struct pid *task_pid(struct task_struct *task) + { + return task->pids[PIDTYPE_PID].pid; +@@ -1853,6 +1896,15 @@ + extern void free_task(struct task_struct *tsk); + #define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while(0) + ++#ifdef CONFIG_PREEMPT_RT_BASE ++extern void __put_task_struct_cb(struct rcu_head *rhp); ++ ++static inline void put_task_struct(struct task_struct *t) ++{ ++ if (atomic_dec_and_test(&t->usage)) ++ call_rcu(&t->put_rcu, __put_task_struct_cb); ++} ++#else + extern void __put_task_struct(struct task_struct *t); + + static inline void put_task_struct(struct task_struct *t) +@@ -1860,6 +1912,7 @@ + if (atomic_dec_and_test(&t->usage)) + __put_task_struct(t); + } ++#endif + + #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN + extern void task_cputime(struct task_struct *t, +@@ -1898,6 +1951,7 @@ + /* + * Per process flags + */ ++#define PF_IN_SOFTIRQ 0x00000001 /* Task is serving softirq */ + #define PF_EXITING 0x00000004 /* getting shut down */ + #define PF_EXITPIDONE 0x00000008 /* pi exit done on shut down */ + #define PF_VCPU 0x00000010 /* I'm a virtual CPU */ +@@ -2058,6 +2112,10 @@ + + extern int set_cpus_allowed_ptr(struct task_struct *p, + const struct cpumask *new_mask); ++int migrate_me(void); ++void tell_sched_cpu_down_begin(int cpu); ++void tell_sched_cpu_down_done(int cpu); ++ + #else + static inline void do_set_cpus_allowed(struct task_struct *p, + const struct cpumask *new_mask) +@@ -2070,6 +2128,9 @@ + return -EINVAL; + return 0; + } ++static inline int migrate_me(void) { return 0; } ++static inline void tell_sched_cpu_down_begin(int cpu) { } ++static inline void tell_sched_cpu_down_done(int cpu) { } + #endif + + #ifdef CONFIG_NO_HZ_COMMON +@@ -2290,6 +2351,7 @@ + + extern int wake_up_state(struct task_struct *tsk, unsigned int state); + extern int wake_up_process(struct task_struct *tsk); ++extern int wake_up_lock_sleeper(struct task_struct * tsk); + extern void wake_up_new_task(struct task_struct *tsk); + #ifdef CONFIG_SMP + extern void kick_process(struct task_struct *tsk); +@@ -2406,12 +2468,24 @@ + + /* mmdrop drops the mm and the page tables */ + extern void __mmdrop(struct mm_struct *); ++ + static inline void mmdrop(struct mm_struct * mm) + { + if (unlikely(atomic_dec_and_test(&mm->mm_count))) + __mmdrop(mm); + } + ++#ifdef CONFIG_PREEMPT_RT_BASE ++extern void __mmdrop_delayed(struct rcu_head *rhp); ++static inline void mmdrop_delayed(struct mm_struct *mm) ++{ ++ if (atomic_dec_and_test(&mm->mm_count)) ++ call_rcu(&mm->delayed_drop, __mmdrop_delayed); ++} ++#else ++# define mmdrop_delayed(mm) mmdrop(mm) ++#endif ++ + /* mmput gets rid of the mappings and all user-space */ + extern void mmput(struct mm_struct *); + /* Grab a reference to a task's mm, if it is not already going away */ +@@ -2719,6 +2793,43 @@ + return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED)); + } + ++#ifdef CONFIG_PREEMPT_LAZY ++static inline void set_tsk_need_resched_lazy(struct task_struct *tsk) ++{ ++ set_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY); ++} ++ ++static inline void clear_tsk_need_resched_lazy(struct task_struct *tsk) ++{ ++ clear_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY); ++} ++ ++static inline int test_tsk_need_resched_lazy(struct task_struct *tsk) ++{ ++ return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY)); ++} ++ ++static inline int need_resched_lazy(void) ++{ ++ return test_thread_flag(TIF_NEED_RESCHED_LAZY); ++} ++ ++static inline int need_resched_now(void) ++{ ++ return test_thread_flag(TIF_NEED_RESCHED); ++} ++ ++#else ++static inline void clear_tsk_need_resched_lazy(struct task_struct *tsk) { } ++static inline int need_resched_lazy(void) { return 0; } ++ ++static inline int need_resched_now(void) ++{ ++ return test_thread_flag(TIF_NEED_RESCHED); ++} ++ ++#endif ++ + static inline int restart_syscall(void) + { + set_tsk_thread_flag(current, TIF_SIGPENDING); +@@ -2750,6 +2861,51 @@ + return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p); + } + ++static inline bool __task_is_stopped_or_traced(struct task_struct *task) ++{ ++ if (task->state & (__TASK_STOPPED | __TASK_TRACED)) ++ return true; ++#ifdef CONFIG_PREEMPT_RT_FULL ++ if (task->saved_state & (__TASK_STOPPED | __TASK_TRACED)) ++ return true; ++#endif ++ return false; ++} ++ ++static inline bool task_is_stopped_or_traced(struct task_struct *task) ++{ ++ bool traced_stopped; ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&task->pi_lock, flags); ++ traced_stopped = __task_is_stopped_or_traced(task); ++ raw_spin_unlock_irqrestore(&task->pi_lock, flags); ++#else ++ traced_stopped = __task_is_stopped_or_traced(task); ++#endif ++ return traced_stopped; ++} ++ ++static inline bool task_is_traced(struct task_struct *task) ++{ ++ bool traced = false; ++ ++ if (task->state & __TASK_TRACED) ++ return true; ++#ifdef CONFIG_PREEMPT_RT_FULL ++ /* in case the task is sleeping on tasklist_lock */ ++ raw_spin_lock_irq(&task->pi_lock); ++ if (task->state & __TASK_TRACED) ++ traced = true; ++ else if (task->saved_state & __TASK_TRACED) ++ traced = true; ++ raw_spin_unlock_irq(&task->pi_lock); ++#endif ++ return traced; ++} ++ + /* + * cond_resched() and cond_resched_lock(): latency reduction via + * explicit rescheduling in places that are safe. The return +@@ -2766,7 +2922,7 @@ + + extern int __cond_resched_lock(spinlock_t *lock); + +-#ifdef CONFIG_PREEMPT_COUNT ++#if defined(CONFIG_PREEMPT_COUNT) && !defined(CONFIG_PREEMPT_RT_FULL) + #define PREEMPT_LOCK_OFFSET PREEMPT_OFFSET + #else + #define PREEMPT_LOCK_OFFSET 0 +@@ -2777,12 +2933,16 @@ + __cond_resched_lock(lock); \ + }) + ++#ifndef CONFIG_PREEMPT_RT_FULL + extern int __cond_resched_softirq(void); + + #define cond_resched_softirq() ({ \ + __might_sleep(__FILE__, __LINE__, SOFTIRQ_DISABLE_OFFSET); \ + __cond_resched_softirq(); \ + }) ++#else ++# define cond_resched_softirq() cond_resched() ++#endif + + static inline void cond_resched_rcu(void) + { +@@ -2949,6 +3109,26 @@ + + #endif /* CONFIG_SMP */ + ++static inline int __migrate_disabled(struct task_struct *p) ++{ ++#ifdef CONFIG_PREEMPT_RT_FULL ++ return p->migrate_disable; ++#else ++ return 0; ++#endif ++} ++ ++/* Future-safe accessor for struct task_struct's cpus_allowed. */ ++static inline const struct cpumask *tsk_cpus_allowed(struct task_struct *p) ++{ ++#ifdef CONFIG_PREEMPT_RT_FULL ++ if (p->migrate_disable) ++ return cpumask_of(task_cpu(p)); ++#endif ++ ++ return &p->cpus_allowed; ++} ++ + extern long sched_setaffinity(pid_t pid, const struct cpumask *new_mask); + extern long sched_getaffinity(pid_t pid, struct cpumask *mask); + +diff -Nur linux-3.18.14.orig/include/linux/seqlock.h linux-3.18.14-rt/include/linux/seqlock.h +--- linux-3.18.14.orig/include/linux/seqlock.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/seqlock.h 2015-05-31 15:32:48.381635367 -0500 +@@ -219,20 +219,30 @@ + return __read_seqcount_retry(s, start); + } + +- +- +-static inline void raw_write_seqcount_begin(seqcount_t *s) ++static inline void __raw_write_seqcount_begin(seqcount_t *s) + { + s->sequence++; + smp_wmb(); + } + +-static inline void raw_write_seqcount_end(seqcount_t *s) ++static inline void raw_write_seqcount_begin(seqcount_t *s) ++{ ++ preempt_disable_rt(); ++ __raw_write_seqcount_begin(s); ++} ++ ++static inline void __raw_write_seqcount_end(seqcount_t *s) + { + smp_wmb(); + s->sequence++; + } + ++static inline void raw_write_seqcount_end(seqcount_t *s) ++{ ++ __raw_write_seqcount_end(s); ++ preempt_enable_rt(); ++} ++ + /* + * raw_write_seqcount_latch - redirect readers to even/odd copy + * @s: pointer to seqcount_t +@@ -305,10 +315,32 @@ + /* + * Read side functions for starting and finalizing a read side section. + */ ++#ifndef CONFIG_PREEMPT_RT_FULL + static inline unsigned read_seqbegin(const seqlock_t *sl) + { + return read_seqcount_begin(&sl->seqcount); + } ++#else ++/* ++ * Starvation safe read side for RT ++ */ ++static inline unsigned read_seqbegin(seqlock_t *sl) ++{ ++ unsigned ret; ++ ++repeat: ++ ret = ACCESS_ONCE(sl->seqcount.sequence); ++ if (unlikely(ret & 1)) { ++ /* ++ * Take the lock and let the writer proceed (i.e. evtl ++ * boost it), otherwise we could loop here forever. ++ */ ++ spin_unlock_wait(&sl->lock); ++ goto repeat; ++ } ++ return ret; ++} ++#endif + + static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start) + { +@@ -323,36 +355,36 @@ + static inline void write_seqlock(seqlock_t *sl) + { + spin_lock(&sl->lock); +- write_seqcount_begin(&sl->seqcount); ++ __raw_write_seqcount_begin(&sl->seqcount); + } + + static inline void write_sequnlock(seqlock_t *sl) + { +- write_seqcount_end(&sl->seqcount); ++ __raw_write_seqcount_end(&sl->seqcount); + spin_unlock(&sl->lock); + } + + static inline void write_seqlock_bh(seqlock_t *sl) + { + spin_lock_bh(&sl->lock); +- write_seqcount_begin(&sl->seqcount); ++ __raw_write_seqcount_begin(&sl->seqcount); + } + + static inline void write_sequnlock_bh(seqlock_t *sl) + { +- write_seqcount_end(&sl->seqcount); ++ __raw_write_seqcount_end(&sl->seqcount); + spin_unlock_bh(&sl->lock); + } + + static inline void write_seqlock_irq(seqlock_t *sl) + { + spin_lock_irq(&sl->lock); +- write_seqcount_begin(&sl->seqcount); ++ __raw_write_seqcount_begin(&sl->seqcount); + } + + static inline void write_sequnlock_irq(seqlock_t *sl) + { +- write_seqcount_end(&sl->seqcount); ++ __raw_write_seqcount_end(&sl->seqcount); + spin_unlock_irq(&sl->lock); + } + +@@ -361,7 +393,7 @@ + unsigned long flags; + + spin_lock_irqsave(&sl->lock, flags); +- write_seqcount_begin(&sl->seqcount); ++ __raw_write_seqcount_begin(&sl->seqcount); + return flags; + } + +@@ -371,7 +403,7 @@ + static inline void + write_sequnlock_irqrestore(seqlock_t *sl, unsigned long flags) + { +- write_seqcount_end(&sl->seqcount); ++ __raw_write_seqcount_end(&sl->seqcount); + spin_unlock_irqrestore(&sl->lock, flags); + } + +diff -Nur linux-3.18.14.orig/include/linux/signal.h linux-3.18.14-rt/include/linux/signal.h +--- linux-3.18.14.orig/include/linux/signal.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/signal.h 2015-05-31 15:32:48.381635367 -0500 +@@ -218,6 +218,7 @@ + } + + extern void flush_sigqueue(struct sigpending *queue); ++extern void flush_task_sigqueue(struct task_struct *tsk); + + /* Test if 'sig' is valid signal. Use this instead of testing _NSIG directly */ + static inline int valid_signal(unsigned long sig) +diff -Nur linux-3.18.14.orig/include/linux/skbuff.h linux-3.18.14-rt/include/linux/skbuff.h +--- linux-3.18.14.orig/include/linux/skbuff.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/skbuff.h 2015-05-31 15:32:48.405635367 -0500 +@@ -172,6 +172,7 @@ + + __u32 qlen; + spinlock_t lock; ++ raw_spinlock_t raw_lock; + }; + + struct sk_buff; +@@ -1328,6 +1329,12 @@ + __skb_queue_head_init(list); + } + ++static inline void skb_queue_head_init_raw(struct sk_buff_head *list) ++{ ++ raw_spin_lock_init(&list->raw_lock); ++ __skb_queue_head_init(list); ++} ++ + static inline void skb_queue_head_init_class(struct sk_buff_head *list, + struct lock_class_key *class) + { +diff -Nur linux-3.18.14.orig/include/linux/skbuff.h.orig linux-3.18.14-rt/include/linux/skbuff.h.orig +--- linux-3.18.14.orig/include/linux/skbuff.h.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/skbuff.h.orig 2015-05-20 10:04:50.000000000 -0500 +@@ -0,0 +1,3364 @@ ++/* ++ * Definitions for the 'struct sk_buff' memory handlers. ++ * ++ * Authors: ++ * Alan Cox, ++ * Florian La Roche, ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#ifndef _LINUX_SKBUFF_H ++#define _LINUX_SKBUFF_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* A. Checksumming of received packets by device. ++ * ++ * CHECKSUM_NONE: ++ * ++ * Device failed to checksum this packet e.g. due to lack of capabilities. ++ * The packet contains full (though not verified) checksum in packet but ++ * not in skb->csum. Thus, skb->csum is undefined in this case. ++ * ++ * CHECKSUM_UNNECESSARY: ++ * ++ * The hardware you're dealing with doesn't calculate the full checksum ++ * (as in CHECKSUM_COMPLETE), but it does parse headers and verify checksums ++ * for specific protocols. For such packets it will set CHECKSUM_UNNECESSARY ++ * if their checksums are okay. skb->csum is still undefined in this case ++ * though. It is a bad option, but, unfortunately, nowadays most vendors do ++ * this. Apparently with the secret goal to sell you new devices, when you ++ * will add new protocol to your host, f.e. IPv6 8) ++ * ++ * CHECKSUM_UNNECESSARY is applicable to following protocols: ++ * TCP: IPv6 and IPv4. ++ * UDP: IPv4 and IPv6. A device may apply CHECKSUM_UNNECESSARY to a ++ * zero UDP checksum for either IPv4 or IPv6, the networking stack ++ * may perform further validation in this case. ++ * GRE: only if the checksum is present in the header. ++ * SCTP: indicates the CRC in SCTP header has been validated. ++ * ++ * skb->csum_level indicates the number of consecutive checksums found in ++ * the packet minus one that have been verified as CHECKSUM_UNNECESSARY. ++ * For instance if a device receives an IPv6->UDP->GRE->IPv4->TCP packet ++ * and a device is able to verify the checksums for UDP (possibly zero), ++ * GRE (checksum flag is set), and TCP-- skb->csum_level would be set to ++ * two. If the device were only able to verify the UDP checksum and not ++ * GRE, either because it doesn't support GRE checksum of because GRE ++ * checksum is bad, skb->csum_level would be set to zero (TCP checksum is ++ * not considered in this case). ++ * ++ * CHECKSUM_COMPLETE: ++ * ++ * This is the most generic way. The device supplied checksum of the _whole_ ++ * packet as seen by netif_rx() and fills out in skb->csum. Meaning, the ++ * hardware doesn't need to parse L3/L4 headers to implement this. ++ * ++ * Note: Even if device supports only some protocols, but is able to produce ++ * skb->csum, it MUST use CHECKSUM_COMPLETE, not CHECKSUM_UNNECESSARY. ++ * ++ * CHECKSUM_PARTIAL: ++ * ++ * This is identical to the case for output below. This may occur on a packet ++ * received directly from another Linux OS, e.g., a virtualized Linux kernel ++ * on the same host. The packet can be treated in the same way as ++ * CHECKSUM_UNNECESSARY, except that on output (i.e., forwarding) the ++ * checksum must be filled in by the OS or the hardware. ++ * ++ * B. Checksumming on output. ++ * ++ * CHECKSUM_NONE: ++ * ++ * The skb was already checksummed by the protocol, or a checksum is not ++ * required. ++ * ++ * CHECKSUM_PARTIAL: ++ * ++ * The device is required to checksum the packet as seen by hard_start_xmit() ++ * from skb->csum_start up to the end, and to record/write the checksum at ++ * offset skb->csum_start + skb->csum_offset. ++ * ++ * The device must show its capabilities in dev->features, set up at device ++ * setup time, e.g. netdev_features.h: ++ * ++ * NETIF_F_HW_CSUM - It's a clever device, it's able to checksum everything. ++ * NETIF_F_IP_CSUM - Device is dumb, it's able to checksum only TCP/UDP over ++ * IPv4. Sigh. Vendors like this way for an unknown reason. ++ * Though, see comment above about CHECKSUM_UNNECESSARY. 8) ++ * NETIF_F_IPV6_CSUM - About as dumb as the last one but does IPv6 instead. ++ * NETIF_F_... - Well, you get the picture. ++ * ++ * CHECKSUM_UNNECESSARY: ++ * ++ * Normally, the device will do per protocol specific checksumming. Protocol ++ * implementations that do not want the NIC to perform the checksum ++ * calculation should use this flag in their outgoing skbs. ++ * ++ * NETIF_F_FCOE_CRC - This indicates that the device can do FCoE FC CRC ++ * offload. Correspondingly, the FCoE protocol driver ++ * stack should use CHECKSUM_UNNECESSARY. ++ * ++ * Any questions? No questions, good. --ANK ++ */ ++ ++/* Don't change this without changing skb_csum_unnecessary! */ ++#define CHECKSUM_NONE 0 ++#define CHECKSUM_UNNECESSARY 1 ++#define CHECKSUM_COMPLETE 2 ++#define CHECKSUM_PARTIAL 3 ++ ++/* Maximum value in skb->csum_level */ ++#define SKB_MAX_CSUM_LEVEL 3 ++ ++#define SKB_DATA_ALIGN(X) ALIGN(X, SMP_CACHE_BYTES) ++#define SKB_WITH_OVERHEAD(X) \ ++ ((X) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++#define SKB_MAX_ORDER(X, ORDER) \ ++ SKB_WITH_OVERHEAD((PAGE_SIZE << (ORDER)) - (X)) ++#define SKB_MAX_HEAD(X) (SKB_MAX_ORDER((X), 0)) ++#define SKB_MAX_ALLOC (SKB_MAX_ORDER(0, 2)) ++ ++/* return minimum truesize of one skb containing X bytes of data */ ++#define SKB_TRUESIZE(X) ((X) + \ ++ SKB_DATA_ALIGN(sizeof(struct sk_buff)) + \ ++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) ++ ++struct net_device; ++struct scatterlist; ++struct pipe_inode_info; ++ ++#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) ++struct nf_conntrack { ++ atomic_t use; ++}; ++#endif ++ ++#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) ++struct nf_bridge_info { ++ atomic_t use; ++ unsigned int mask; ++ struct net_device *physindev; ++ struct net_device *physoutdev; ++ unsigned long data[32 / sizeof(unsigned long)]; ++}; ++#endif ++ ++struct sk_buff_head { ++ /* These two members must be first. */ ++ struct sk_buff *next; ++ struct sk_buff *prev; ++ ++ __u32 qlen; ++ spinlock_t lock; ++}; ++ ++struct sk_buff; ++ ++/* To allow 64K frame to be packed as single skb without frag_list we ++ * require 64K/PAGE_SIZE pages plus 1 additional page to allow for ++ * buffers which do not start on a page boundary. ++ * ++ * Since GRO uses frags we allocate at least 16 regardless of page ++ * size. ++ */ ++#if (65536/PAGE_SIZE + 1) < 16 ++#define MAX_SKB_FRAGS 16UL ++#else ++#define MAX_SKB_FRAGS (65536/PAGE_SIZE + 1) ++#endif ++ ++typedef struct skb_frag_struct skb_frag_t; ++ ++struct skb_frag_struct { ++ struct { ++ struct page *p; ++ } page; ++#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536) ++ __u32 page_offset; ++ __u32 size; ++#else ++ __u16 page_offset; ++ __u16 size; ++#endif ++}; ++ ++static inline unsigned int skb_frag_size(const skb_frag_t *frag) ++{ ++ return frag->size; ++} ++ ++static inline void skb_frag_size_set(skb_frag_t *frag, unsigned int size) ++{ ++ frag->size = size; ++} ++ ++static inline void skb_frag_size_add(skb_frag_t *frag, int delta) ++{ ++ frag->size += delta; ++} ++ ++static inline void skb_frag_size_sub(skb_frag_t *frag, int delta) ++{ ++ frag->size -= delta; ++} ++ ++#define HAVE_HW_TIME_STAMP ++ ++/** ++ * struct skb_shared_hwtstamps - hardware time stamps ++ * @hwtstamp: hardware time stamp transformed into duration ++ * since arbitrary point in time ++ * ++ * Software time stamps generated by ktime_get_real() are stored in ++ * skb->tstamp. ++ * ++ * hwtstamps can only be compared against other hwtstamps from ++ * the same device. ++ * ++ * This structure is attached to packets as part of the ++ * &skb_shared_info. Use skb_hwtstamps() to get a pointer. ++ */ ++struct skb_shared_hwtstamps { ++ ktime_t hwtstamp; ++}; ++ ++/* Definitions for tx_flags in struct skb_shared_info */ ++enum { ++ /* generate hardware time stamp */ ++ SKBTX_HW_TSTAMP = 1 << 0, ++ ++ /* generate software time stamp when queueing packet to NIC */ ++ SKBTX_SW_TSTAMP = 1 << 1, ++ ++ /* device driver is going to provide hardware time stamp */ ++ SKBTX_IN_PROGRESS = 1 << 2, ++ ++ /* device driver supports TX zero-copy buffers */ ++ SKBTX_DEV_ZEROCOPY = 1 << 3, ++ ++ /* generate wifi status information (where possible) */ ++ SKBTX_WIFI_STATUS = 1 << 4, ++ ++ /* This indicates at least one fragment might be overwritten ++ * (as in vmsplice(), sendfile() ...) ++ * If we need to compute a TX checksum, we'll need to copy ++ * all frags to avoid possible bad checksum ++ */ ++ SKBTX_SHARED_FRAG = 1 << 5, ++ ++ /* generate software time stamp when entering packet scheduling */ ++ SKBTX_SCHED_TSTAMP = 1 << 6, ++ ++ /* generate software timestamp on peer data acknowledgment */ ++ SKBTX_ACK_TSTAMP = 1 << 7, ++}; ++ ++#define SKBTX_ANY_SW_TSTAMP (SKBTX_SW_TSTAMP | \ ++ SKBTX_SCHED_TSTAMP | \ ++ SKBTX_ACK_TSTAMP) ++#define SKBTX_ANY_TSTAMP (SKBTX_HW_TSTAMP | SKBTX_ANY_SW_TSTAMP) ++ ++/* ++ * The callback notifies userspace to release buffers when skb DMA is done in ++ * lower device, the skb last reference should be 0 when calling this. ++ * The zerocopy_success argument is true if zero copy transmit occurred, ++ * false on data copy or out of memory error caused by data copy attempt. ++ * The ctx field is used to track device context. ++ * The desc field is used to track userspace buffer index. ++ */ ++struct ubuf_info { ++ void (*callback)(struct ubuf_info *, bool zerocopy_success); ++ void *ctx; ++ unsigned long desc; ++}; ++ ++/* This data is invariant across clones and lives at ++ * the end of the header data, ie. at skb->end. ++ */ ++struct skb_shared_info { ++ unsigned char nr_frags; ++ __u8 tx_flags; ++ unsigned short gso_size; ++ /* Warning: this field is not always filled in (UFO)! */ ++ unsigned short gso_segs; ++ unsigned short gso_type; ++ struct sk_buff *frag_list; ++ struct skb_shared_hwtstamps hwtstamps; ++ u32 tskey; ++ __be32 ip6_frag_id; ++ ++ /* ++ * Warning : all fields before dataref are cleared in __alloc_skb() ++ */ ++ atomic_t dataref; ++ ++ /* Intermediate layers must ensure that destructor_arg ++ * remains valid until skb destructor */ ++ void * destructor_arg; ++ ++ /* must be last field, see pskb_expand_head() */ ++ skb_frag_t frags[MAX_SKB_FRAGS]; ++}; ++ ++/* We divide dataref into two halves. The higher 16 bits hold references ++ * to the payload part of skb->data. The lower 16 bits hold references to ++ * the entire skb->data. A clone of a headerless skb holds the length of ++ * the header in skb->hdr_len. ++ * ++ * All users must obey the rule that the skb->data reference count must be ++ * greater than or equal to the payload reference count. ++ * ++ * Holding a reference to the payload part means that the user does not ++ * care about modifications to the header part of skb->data. ++ */ ++#define SKB_DATAREF_SHIFT 16 ++#define SKB_DATAREF_MASK ((1 << SKB_DATAREF_SHIFT) - 1) ++ ++ ++enum { ++ SKB_FCLONE_UNAVAILABLE, /* skb has no fclone (from head_cache) */ ++ SKB_FCLONE_ORIG, /* orig skb (from fclone_cache) */ ++ SKB_FCLONE_CLONE, /* companion fclone skb (from fclone_cache) */ ++ SKB_FCLONE_FREE, /* this companion fclone skb is available */ ++}; ++ ++enum { ++ SKB_GSO_TCPV4 = 1 << 0, ++ SKB_GSO_UDP = 1 << 1, ++ ++ /* This indicates the skb is from an untrusted source. */ ++ SKB_GSO_DODGY = 1 << 2, ++ ++ /* This indicates the tcp segment has CWR set. */ ++ SKB_GSO_TCP_ECN = 1 << 3, ++ ++ SKB_GSO_TCPV6 = 1 << 4, ++ ++ SKB_GSO_FCOE = 1 << 5, ++ ++ SKB_GSO_GRE = 1 << 6, ++ ++ SKB_GSO_GRE_CSUM = 1 << 7, ++ ++ SKB_GSO_IPIP = 1 << 8, ++ ++ SKB_GSO_SIT = 1 << 9, ++ ++ SKB_GSO_UDP_TUNNEL = 1 << 10, ++ ++ SKB_GSO_UDP_TUNNEL_CSUM = 1 << 11, ++ ++ SKB_GSO_MPLS = 1 << 12, ++ ++}; ++ ++#if BITS_PER_LONG > 32 ++#define NET_SKBUFF_DATA_USES_OFFSET 1 ++#endif ++ ++#ifdef NET_SKBUFF_DATA_USES_OFFSET ++typedef unsigned int sk_buff_data_t; ++#else ++typedef unsigned char *sk_buff_data_t; ++#endif ++ ++/** ++ * struct skb_mstamp - multi resolution time stamps ++ * @stamp_us: timestamp in us resolution ++ * @stamp_jiffies: timestamp in jiffies ++ */ ++struct skb_mstamp { ++ union { ++ u64 v64; ++ struct { ++ u32 stamp_us; ++ u32 stamp_jiffies; ++ }; ++ }; ++}; ++ ++/** ++ * skb_mstamp_get - get current timestamp ++ * @cl: place to store timestamps ++ */ ++static inline void skb_mstamp_get(struct skb_mstamp *cl) ++{ ++ u64 val = local_clock(); ++ ++ do_div(val, NSEC_PER_USEC); ++ cl->stamp_us = (u32)val; ++ cl->stamp_jiffies = (u32)jiffies; ++} ++ ++/** ++ * skb_mstamp_delta - compute the difference in usec between two skb_mstamp ++ * @t1: pointer to newest sample ++ * @t0: pointer to oldest sample ++ */ ++static inline u32 skb_mstamp_us_delta(const struct skb_mstamp *t1, ++ const struct skb_mstamp *t0) ++{ ++ s32 delta_us = t1->stamp_us - t0->stamp_us; ++ u32 delta_jiffies = t1->stamp_jiffies - t0->stamp_jiffies; ++ ++ /* If delta_us is negative, this might be because interval is too big, ++ * or local_clock() drift is too big : fallback using jiffies. ++ */ ++ if (delta_us <= 0 || ++ delta_jiffies >= (INT_MAX / (USEC_PER_SEC / HZ))) ++ ++ delta_us = jiffies_to_usecs(delta_jiffies); ++ ++ return delta_us; ++} ++ ++ ++/** ++ * struct sk_buff - socket buffer ++ * @next: Next buffer in list ++ * @prev: Previous buffer in list ++ * @tstamp: Time we arrived/left ++ * @sk: Socket we are owned by ++ * @dev: Device we arrived on/are leaving by ++ * @cb: Control buffer. Free for use by every layer. Put private vars here ++ * @_skb_refdst: destination entry (with norefcount bit) ++ * @sp: the security path, used for xfrm ++ * @len: Length of actual data ++ * @data_len: Data length ++ * @mac_len: Length of link layer header ++ * @hdr_len: writable header length of cloned skb ++ * @csum: Checksum (must include start/offset pair) ++ * @csum_start: Offset from skb->head where checksumming should start ++ * @csum_offset: Offset from csum_start where checksum should be stored ++ * @priority: Packet queueing priority ++ * @ignore_df: allow local fragmentation ++ * @cloned: Head may be cloned (check refcnt to be sure) ++ * @ip_summed: Driver fed us an IP checksum ++ * @nohdr: Payload reference only, must not modify header ++ * @nfctinfo: Relationship of this skb to the connection ++ * @pkt_type: Packet class ++ * @fclone: skbuff clone status ++ * @ipvs_property: skbuff is owned by ipvs ++ * @peeked: this packet has been seen already, so stats have been ++ * done for it, don't do them again ++ * @nf_trace: netfilter packet trace flag ++ * @protocol: Packet protocol from driver ++ * @destructor: Destruct function ++ * @nfct: Associated connection, if any ++ * @nf_bridge: Saved data about a bridged frame - see br_netfilter.c ++ * @skb_iif: ifindex of device we arrived on ++ * @tc_index: Traffic control index ++ * @tc_verd: traffic control verdict ++ * @hash: the packet hash ++ * @queue_mapping: Queue mapping for multiqueue devices ++ * @xmit_more: More SKBs are pending for this queue ++ * @ndisc_nodetype: router type (from link layer) ++ * @ooo_okay: allow the mapping of a socket to a queue to be changed ++ * @l4_hash: indicate hash is a canonical 4-tuple hash over transport ++ * ports. ++ * @sw_hash: indicates hash was computed in software stack ++ * @wifi_acked_valid: wifi_acked was set ++ * @wifi_acked: whether frame was acked on wifi or not ++ * @no_fcs: Request NIC to treat last 4 bytes as Ethernet FCS ++ * @napi_id: id of the NAPI struct this skb came from ++ * @secmark: security marking ++ * @mark: Generic packet mark ++ * @dropcount: total number of sk_receive_queue overflows ++ * @vlan_proto: vlan encapsulation protocol ++ * @vlan_tci: vlan tag control information ++ * @inner_protocol: Protocol (encapsulation) ++ * @inner_transport_header: Inner transport layer header (encapsulation) ++ * @inner_network_header: Network layer header (encapsulation) ++ * @inner_mac_header: Link layer header (encapsulation) ++ * @transport_header: Transport layer header ++ * @network_header: Network layer header ++ * @mac_header: Link layer header ++ * @tail: Tail pointer ++ * @end: End pointer ++ * @head: Head of buffer ++ * @data: Data head pointer ++ * @truesize: Buffer size ++ * @users: User count - see {datagram,tcp}.c ++ */ ++ ++struct sk_buff { ++ /* These two members must be first. */ ++ struct sk_buff *next; ++ struct sk_buff *prev; ++ ++ union { ++ ktime_t tstamp; ++ struct skb_mstamp skb_mstamp; ++ }; ++ ++ struct sock *sk; ++ struct net_device *dev; ++ ++ /* ++ * This is the control buffer. It is free to use for every ++ * layer. Please put your private variables there. If you ++ * want to keep them across layers you have to do a skb_clone() ++ * first. This is owned by whoever has the skb queued ATM. ++ */ ++ char cb[48] __aligned(8); ++ ++ unsigned long _skb_refdst; ++ void (*destructor)(struct sk_buff *skb); ++#ifdef CONFIG_XFRM ++ struct sec_path *sp; ++#endif ++#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) ++ struct nf_conntrack *nfct; ++#endif ++#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) ++ struct nf_bridge_info *nf_bridge; ++#endif ++ unsigned int len, ++ data_len; ++ __u16 mac_len, ++ hdr_len; ++ ++ /* Following fields are _not_ copied in __copy_skb_header() ++ * Note that queue_mapping is here mostly to fill a hole. ++ */ ++ kmemcheck_bitfield_begin(flags1); ++ __u16 queue_mapping; ++ __u8 cloned:1, ++ nohdr:1, ++ fclone:2, ++ peeked:1, ++ head_frag:1, ++ xmit_more:1; ++ /* one bit hole */ ++ kmemcheck_bitfield_end(flags1); ++ ++ /* fields enclosed in headers_start/headers_end are copied ++ * using a single memcpy() in __copy_skb_header() ++ */ ++ /* private: */ ++ __u32 headers_start[0]; ++ /* public: */ ++ ++/* if you move pkt_type around you also must adapt those constants */ ++#ifdef __BIG_ENDIAN_BITFIELD ++#define PKT_TYPE_MAX (7 << 5) ++#else ++#define PKT_TYPE_MAX 7 ++#endif ++#define PKT_TYPE_OFFSET() offsetof(struct sk_buff, __pkt_type_offset) ++ ++ __u8 __pkt_type_offset[0]; ++ __u8 pkt_type:3; ++ __u8 pfmemalloc:1; ++ __u8 ignore_df:1; ++ __u8 nfctinfo:3; ++ ++ __u8 nf_trace:1; ++ __u8 ip_summed:2; ++ __u8 ooo_okay:1; ++ __u8 l4_hash:1; ++ __u8 sw_hash:1; ++ __u8 wifi_acked_valid:1; ++ __u8 wifi_acked:1; ++ ++ __u8 no_fcs:1; ++ /* Indicates the inner headers are valid in the skbuff. */ ++ __u8 encapsulation:1; ++ __u8 encap_hdr_csum:1; ++ __u8 csum_valid:1; ++ __u8 csum_complete_sw:1; ++ __u8 csum_level:2; ++ __u8 csum_bad:1; ++ ++#ifdef CONFIG_IPV6_NDISC_NODETYPE ++ __u8 ndisc_nodetype:2; ++#endif ++ __u8 ipvs_property:1; ++ __u8 inner_protocol_type:1; ++ /* 4 or 6 bit hole */ ++ ++#ifdef CONFIG_NET_SCHED ++ __u16 tc_index; /* traffic control index */ ++#ifdef CONFIG_NET_CLS_ACT ++ __u16 tc_verd; /* traffic control verdict */ ++#endif ++#endif ++ ++ union { ++ __wsum csum; ++ struct { ++ __u16 csum_start; ++ __u16 csum_offset; ++ }; ++ }; ++ __u32 priority; ++ int skb_iif; ++ __u32 hash; ++ __be16 vlan_proto; ++ __u16 vlan_tci; ++#ifdef CONFIG_NET_RX_BUSY_POLL ++ unsigned int napi_id; ++#endif ++#ifdef CONFIG_NETWORK_SECMARK ++ __u32 secmark; ++#endif ++ union { ++ __u32 mark; ++ __u32 dropcount; ++ __u32 reserved_tailroom; ++ }; ++ ++ union { ++ __be16 inner_protocol; ++ __u8 inner_ipproto; ++ }; ++ ++ __u16 inner_transport_header; ++ __u16 inner_network_header; ++ __u16 inner_mac_header; ++ ++ __be16 protocol; ++ __u16 transport_header; ++ __u16 network_header; ++ __u16 mac_header; ++ ++ /* private: */ ++ __u32 headers_end[0]; ++ /* public: */ ++ ++ /* These elements must be at the end, see alloc_skb() for details. */ ++ sk_buff_data_t tail; ++ sk_buff_data_t end; ++ unsigned char *head, ++ *data; ++ unsigned int truesize; ++ atomic_t users; ++}; ++ ++#ifdef __KERNEL__ ++/* ++ * Handling routines are only of interest to the kernel ++ */ ++#include ++ ++ ++#define SKB_ALLOC_FCLONE 0x01 ++#define SKB_ALLOC_RX 0x02 ++ ++/* Returns true if the skb was allocated from PFMEMALLOC reserves */ ++static inline bool skb_pfmemalloc(const struct sk_buff *skb) ++{ ++ return unlikely(skb->pfmemalloc); ++} ++ ++/* ++ * skb might have a dst pointer attached, refcounted or not. ++ * _skb_refdst low order bit is set if refcount was _not_ taken ++ */ ++#define SKB_DST_NOREF 1UL ++#define SKB_DST_PTRMASK ~(SKB_DST_NOREF) ++ ++/** ++ * skb_dst - returns skb dst_entry ++ * @skb: buffer ++ * ++ * Returns skb dst_entry, regardless of reference taken or not. ++ */ ++static inline struct dst_entry *skb_dst(const struct sk_buff *skb) ++{ ++ /* If refdst was not refcounted, check we still are in a ++ * rcu_read_lock section ++ */ ++ WARN_ON((skb->_skb_refdst & SKB_DST_NOREF) && ++ !rcu_read_lock_held() && ++ !rcu_read_lock_bh_held()); ++ return (struct dst_entry *)(skb->_skb_refdst & SKB_DST_PTRMASK); ++} ++ ++/** ++ * skb_dst_set - sets skb dst ++ * @skb: buffer ++ * @dst: dst entry ++ * ++ * Sets skb dst, assuming a reference was taken on dst and should ++ * be released by skb_dst_drop() ++ */ ++static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst) ++{ ++ skb->_skb_refdst = (unsigned long)dst; ++} ++ ++void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst, ++ bool force); ++ ++/** ++ * skb_dst_set_noref - sets skb dst, hopefully, without taking reference ++ * @skb: buffer ++ * @dst: dst entry ++ * ++ * Sets skb dst, assuming a reference was not taken on dst. ++ * If dst entry is cached, we do not take reference and dst_release ++ * will be avoided by refdst_drop. If dst entry is not cached, we take ++ * reference, so that last dst_release can destroy the dst immediately. ++ */ ++static inline void skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst) ++{ ++ __skb_dst_set_noref(skb, dst, false); ++} ++ ++/** ++ * skb_dst_set_noref_force - sets skb dst, without taking reference ++ * @skb: buffer ++ * @dst: dst entry ++ * ++ * Sets skb dst, assuming a reference was not taken on dst. ++ * No reference is taken and no dst_release will be called. While for ++ * cached dsts deferred reclaim is a basic feature, for entries that are ++ * not cached it is caller's job to guarantee that last dst_release for ++ * provided dst happens when nobody uses it, eg. after a RCU grace period. ++ */ ++static inline void skb_dst_set_noref_force(struct sk_buff *skb, ++ struct dst_entry *dst) ++{ ++ __skb_dst_set_noref(skb, dst, true); ++} ++ ++/** ++ * skb_dst_is_noref - Test if skb dst isn't refcounted ++ * @skb: buffer ++ */ ++static inline bool skb_dst_is_noref(const struct sk_buff *skb) ++{ ++ return (skb->_skb_refdst & SKB_DST_NOREF) && skb_dst(skb); ++} ++ ++static inline struct rtable *skb_rtable(const struct sk_buff *skb) ++{ ++ return (struct rtable *)skb_dst(skb); ++} ++ ++void kfree_skb(struct sk_buff *skb); ++void kfree_skb_list(struct sk_buff *segs); ++void skb_tx_error(struct sk_buff *skb); ++void consume_skb(struct sk_buff *skb); ++void __kfree_skb(struct sk_buff *skb); ++extern struct kmem_cache *skbuff_head_cache; ++ ++void kfree_skb_partial(struct sk_buff *skb, bool head_stolen); ++bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, ++ bool *fragstolen, int *delta_truesize); ++ ++struct sk_buff *__alloc_skb(unsigned int size, gfp_t priority, int flags, ++ int node); ++struct sk_buff *__build_skb(void *data, unsigned int frag_size); ++struct sk_buff *build_skb(void *data, unsigned int frag_size); ++static inline struct sk_buff *alloc_skb(unsigned int size, ++ gfp_t priority) ++{ ++ return __alloc_skb(size, priority, 0, NUMA_NO_NODE); ++} ++ ++struct sk_buff *alloc_skb_with_frags(unsigned long header_len, ++ unsigned long data_len, ++ int max_page_order, ++ int *errcode, ++ gfp_t gfp_mask); ++ ++/* Layout of fast clones : [skb1][skb2][fclone_ref] */ ++struct sk_buff_fclones { ++ struct sk_buff skb1; ++ ++ struct sk_buff skb2; ++ ++ atomic_t fclone_ref; ++}; ++ ++/** ++ * skb_fclone_busy - check if fclone is busy ++ * @skb: buffer ++ * ++ * Returns true is skb is a fast clone, and its clone is not freed. ++ * Some drivers call skb_orphan() in their ndo_start_xmit(), ++ * so we also check that this didnt happen. ++ */ ++static inline bool skb_fclone_busy(const struct sock *sk, ++ const struct sk_buff *skb) ++{ ++ const struct sk_buff_fclones *fclones; ++ ++ fclones = container_of(skb, struct sk_buff_fclones, skb1); ++ ++ return skb->fclone == SKB_FCLONE_ORIG && ++ fclones->skb2.fclone == SKB_FCLONE_CLONE && ++ fclones->skb2.sk == sk; ++} ++ ++static inline struct sk_buff *alloc_skb_fclone(unsigned int size, ++ gfp_t priority) ++{ ++ return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, NUMA_NO_NODE); ++} ++ ++struct sk_buff *__alloc_skb_head(gfp_t priority, int node); ++static inline struct sk_buff *alloc_skb_head(gfp_t priority) ++{ ++ return __alloc_skb_head(priority, -1); ++} ++ ++struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src); ++int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask); ++struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t priority); ++struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t priority); ++struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom, ++ gfp_t gfp_mask, bool fclone); ++static inline struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, ++ gfp_t gfp_mask) ++{ ++ return __pskb_copy_fclone(skb, headroom, gfp_mask, false); ++} ++ ++int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask); ++struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, ++ unsigned int headroom); ++struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom, ++ int newtailroom, gfp_t priority); ++int skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg, ++ int offset, int len); ++int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, ++ int len); ++int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer); ++int skb_pad(struct sk_buff *skb, int pad); ++#define dev_kfree_skb(a) consume_skb(a) ++ ++int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, ++ int getfrag(void *from, char *to, int offset, ++ int len, int odd, struct sk_buff *skb), ++ void *from, int length); ++ ++struct skb_seq_state { ++ __u32 lower_offset; ++ __u32 upper_offset; ++ __u32 frag_idx; ++ __u32 stepped_offset; ++ struct sk_buff *root_skb; ++ struct sk_buff *cur_skb; ++ __u8 *frag_data; ++}; ++ ++void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from, ++ unsigned int to, struct skb_seq_state *st); ++unsigned int skb_seq_read(unsigned int consumed, const u8 **data, ++ struct skb_seq_state *st); ++void skb_abort_seq_read(struct skb_seq_state *st); ++ ++unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, ++ unsigned int to, struct ts_config *config, ++ struct ts_state *state); ++ ++/* ++ * Packet hash types specify the type of hash in skb_set_hash. ++ * ++ * Hash types refer to the protocol layer addresses which are used to ++ * construct a packet's hash. The hashes are used to differentiate or identify ++ * flows of the protocol layer for the hash type. Hash types are either ++ * layer-2 (L2), layer-3 (L3), or layer-4 (L4). ++ * ++ * Properties of hashes: ++ * ++ * 1) Two packets in different flows have different hash values ++ * 2) Two packets in the same flow should have the same hash value ++ * ++ * A hash at a higher layer is considered to be more specific. A driver should ++ * set the most specific hash possible. ++ * ++ * A driver cannot indicate a more specific hash than the layer at which a hash ++ * was computed. For instance an L3 hash cannot be set as an L4 hash. ++ * ++ * A driver may indicate a hash level which is less specific than the ++ * actual layer the hash was computed on. For instance, a hash computed ++ * at L4 may be considered an L3 hash. This should only be done if the ++ * driver can't unambiguously determine that the HW computed the hash at ++ * the higher layer. Note that the "should" in the second property above ++ * permits this. ++ */ ++enum pkt_hash_types { ++ PKT_HASH_TYPE_NONE, /* Undefined type */ ++ PKT_HASH_TYPE_L2, /* Input: src_MAC, dest_MAC */ ++ PKT_HASH_TYPE_L3, /* Input: src_IP, dst_IP */ ++ PKT_HASH_TYPE_L4, /* Input: src_IP, dst_IP, src_port, dst_port */ ++}; ++ ++static inline void ++skb_set_hash(struct sk_buff *skb, __u32 hash, enum pkt_hash_types type) ++{ ++ skb->l4_hash = (type == PKT_HASH_TYPE_L4); ++ skb->sw_hash = 0; ++ skb->hash = hash; ++} ++ ++void __skb_get_hash(struct sk_buff *skb); ++static inline __u32 skb_get_hash(struct sk_buff *skb) ++{ ++ if (!skb->l4_hash && !skb->sw_hash) ++ __skb_get_hash(skb); ++ ++ return skb->hash; ++} ++ ++static inline __u32 skb_get_hash_raw(const struct sk_buff *skb) ++{ ++ return skb->hash; ++} ++ ++static inline void skb_clear_hash(struct sk_buff *skb) ++{ ++ skb->hash = 0; ++ skb->sw_hash = 0; ++ skb->l4_hash = 0; ++} ++ ++static inline void skb_clear_hash_if_not_l4(struct sk_buff *skb) ++{ ++ if (!skb->l4_hash) ++ skb_clear_hash(skb); ++} ++ ++static inline void skb_copy_hash(struct sk_buff *to, const struct sk_buff *from) ++{ ++ to->hash = from->hash; ++ to->sw_hash = from->sw_hash; ++ to->l4_hash = from->l4_hash; ++}; ++ ++#ifdef NET_SKBUFF_DATA_USES_OFFSET ++static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) ++{ ++ return skb->head + skb->end; ++} ++ ++static inline unsigned int skb_end_offset(const struct sk_buff *skb) ++{ ++ return skb->end; ++} ++#else ++static inline unsigned char *skb_end_pointer(const struct sk_buff *skb) ++{ ++ return skb->end; ++} ++ ++static inline unsigned int skb_end_offset(const struct sk_buff *skb) ++{ ++ return skb->end - skb->head; ++} ++#endif ++ ++/* Internal */ ++#define skb_shinfo(SKB) ((struct skb_shared_info *)(skb_end_pointer(SKB))) ++ ++static inline struct skb_shared_hwtstamps *skb_hwtstamps(struct sk_buff *skb) ++{ ++ return &skb_shinfo(skb)->hwtstamps; ++} ++ ++/** ++ * skb_queue_empty - check if a queue is empty ++ * @list: queue head ++ * ++ * Returns true if the queue is empty, false otherwise. ++ */ ++static inline int skb_queue_empty(const struct sk_buff_head *list) ++{ ++ return list->next == (const struct sk_buff *) list; ++} ++ ++/** ++ * skb_queue_is_last - check if skb is the last entry in the queue ++ * @list: queue head ++ * @skb: buffer ++ * ++ * Returns true if @skb is the last buffer on the list. ++ */ ++static inline bool skb_queue_is_last(const struct sk_buff_head *list, ++ const struct sk_buff *skb) ++{ ++ return skb->next == (const struct sk_buff *) list; ++} ++ ++/** ++ * skb_queue_is_first - check if skb is the first entry in the queue ++ * @list: queue head ++ * @skb: buffer ++ * ++ * Returns true if @skb is the first buffer on the list. ++ */ ++static inline bool skb_queue_is_first(const struct sk_buff_head *list, ++ const struct sk_buff *skb) ++{ ++ return skb->prev == (const struct sk_buff *) list; ++} ++ ++/** ++ * skb_queue_next - return the next packet in the queue ++ * @list: queue head ++ * @skb: current buffer ++ * ++ * Return the next packet in @list after @skb. It is only valid to ++ * call this if skb_queue_is_last() evaluates to false. ++ */ ++static inline struct sk_buff *skb_queue_next(const struct sk_buff_head *list, ++ const struct sk_buff *skb) ++{ ++ /* This BUG_ON may seem severe, but if we just return then we ++ * are going to dereference garbage. ++ */ ++ BUG_ON(skb_queue_is_last(list, skb)); ++ return skb->next; ++} ++ ++/** ++ * skb_queue_prev - return the prev packet in the queue ++ * @list: queue head ++ * @skb: current buffer ++ * ++ * Return the prev packet in @list before @skb. It is only valid to ++ * call this if skb_queue_is_first() evaluates to false. ++ */ ++static inline struct sk_buff *skb_queue_prev(const struct sk_buff_head *list, ++ const struct sk_buff *skb) ++{ ++ /* This BUG_ON may seem severe, but if we just return then we ++ * are going to dereference garbage. ++ */ ++ BUG_ON(skb_queue_is_first(list, skb)); ++ return skb->prev; ++} ++ ++/** ++ * skb_get - reference buffer ++ * @skb: buffer to reference ++ * ++ * Makes another reference to a socket buffer and returns a pointer ++ * to the buffer. ++ */ ++static inline struct sk_buff *skb_get(struct sk_buff *skb) ++{ ++ atomic_inc(&skb->users); ++ return skb; ++} ++ ++/* ++ * If users == 1, we are the only owner and are can avoid redundant ++ * atomic change. ++ */ ++ ++/** ++ * skb_cloned - is the buffer a clone ++ * @skb: buffer to check ++ * ++ * Returns true if the buffer was generated with skb_clone() and is ++ * one of multiple shared copies of the buffer. Cloned buffers are ++ * shared data so must not be written to under normal circumstances. ++ */ ++static inline int skb_cloned(const struct sk_buff *skb) ++{ ++ return skb->cloned && ++ (atomic_read(&skb_shinfo(skb)->dataref) & SKB_DATAREF_MASK) != 1; ++} ++ ++static inline int skb_unclone(struct sk_buff *skb, gfp_t pri) ++{ ++ might_sleep_if(pri & __GFP_WAIT); ++ ++ if (skb_cloned(skb)) ++ return pskb_expand_head(skb, 0, 0, pri); ++ ++ return 0; ++} ++ ++/** ++ * skb_header_cloned - is the header a clone ++ * @skb: buffer to check ++ * ++ * Returns true if modifying the header part of the buffer requires ++ * the data to be copied. ++ */ ++static inline int skb_header_cloned(const struct sk_buff *skb) ++{ ++ int dataref; ++ ++ if (!skb->cloned) ++ return 0; ++ ++ dataref = atomic_read(&skb_shinfo(skb)->dataref); ++ dataref = (dataref & SKB_DATAREF_MASK) - (dataref >> SKB_DATAREF_SHIFT); ++ return dataref != 1; ++} ++ ++/** ++ * skb_header_release - release reference to header ++ * @skb: buffer to operate on ++ * ++ * Drop a reference to the header part of the buffer. This is done ++ * by acquiring a payload reference. You must not read from the header ++ * part of skb->data after this. ++ * Note : Check if you can use __skb_header_release() instead. ++ */ ++static inline void skb_header_release(struct sk_buff *skb) ++{ ++ BUG_ON(skb->nohdr); ++ skb->nohdr = 1; ++ atomic_add(1 << SKB_DATAREF_SHIFT, &skb_shinfo(skb)->dataref); ++} ++ ++/** ++ * __skb_header_release - release reference to header ++ * @skb: buffer to operate on ++ * ++ * Variant of skb_header_release() assuming skb is private to caller. ++ * We can avoid one atomic operation. ++ */ ++static inline void __skb_header_release(struct sk_buff *skb) ++{ ++ skb->nohdr = 1; ++ atomic_set(&skb_shinfo(skb)->dataref, 1 + (1 << SKB_DATAREF_SHIFT)); ++} ++ ++ ++/** ++ * skb_shared - is the buffer shared ++ * @skb: buffer to check ++ * ++ * Returns true if more than one person has a reference to this ++ * buffer. ++ */ ++static inline int skb_shared(const struct sk_buff *skb) ++{ ++ return atomic_read(&skb->users) != 1; ++} ++ ++/** ++ * skb_share_check - check if buffer is shared and if so clone it ++ * @skb: buffer to check ++ * @pri: priority for memory allocation ++ * ++ * If the buffer is shared the buffer is cloned and the old copy ++ * drops a reference. A new clone with a single reference is returned. ++ * If the buffer is not shared the original buffer is returned. When ++ * being called from interrupt status or with spinlocks held pri must ++ * be GFP_ATOMIC. ++ * ++ * NULL is returned on a memory allocation failure. ++ */ ++static inline struct sk_buff *skb_share_check(struct sk_buff *skb, gfp_t pri) ++{ ++ might_sleep_if(pri & __GFP_WAIT); ++ if (skb_shared(skb)) { ++ struct sk_buff *nskb = skb_clone(skb, pri); ++ ++ if (likely(nskb)) ++ consume_skb(skb); ++ else ++ kfree_skb(skb); ++ skb = nskb; ++ } ++ return skb; ++} ++ ++/* ++ * Copy shared buffers into a new sk_buff. We effectively do COW on ++ * packets to handle cases where we have a local reader and forward ++ * and a couple of other messy ones. The normal one is tcpdumping ++ * a packet thats being forwarded. ++ */ ++ ++/** ++ * skb_unshare - make a copy of a shared buffer ++ * @skb: buffer to check ++ * @pri: priority for memory allocation ++ * ++ * If the socket buffer is a clone then this function creates a new ++ * copy of the data, drops a reference count on the old copy and returns ++ * the new copy with the reference count at 1. If the buffer is not a clone ++ * the original buffer is returned. When called with a spinlock held or ++ * from interrupt state @pri must be %GFP_ATOMIC ++ * ++ * %NULL is returned on a memory allocation failure. ++ */ ++static inline struct sk_buff *skb_unshare(struct sk_buff *skb, ++ gfp_t pri) ++{ ++ might_sleep_if(pri & __GFP_WAIT); ++ if (skb_cloned(skb)) { ++ struct sk_buff *nskb = skb_copy(skb, pri); ++ ++ /* Free our shared copy */ ++ if (likely(nskb)) ++ consume_skb(skb); ++ else ++ kfree_skb(skb); ++ skb = nskb; ++ } ++ return skb; ++} ++ ++/** ++ * skb_peek - peek at the head of an &sk_buff_head ++ * @list_: list to peek at ++ * ++ * Peek an &sk_buff. Unlike most other operations you _MUST_ ++ * be careful with this one. A peek leaves the buffer on the ++ * list and someone else may run off with it. You must hold ++ * the appropriate locks or have a private queue to do this. ++ * ++ * Returns %NULL for an empty list or a pointer to the head element. ++ * The reference count is not incremented and the reference is therefore ++ * volatile. Use with caution. ++ */ ++static inline struct sk_buff *skb_peek(const struct sk_buff_head *list_) ++{ ++ struct sk_buff *skb = list_->next; ++ ++ if (skb == (struct sk_buff *)list_) ++ skb = NULL; ++ return skb; ++} ++ ++/** ++ * skb_peek_next - peek skb following the given one from a queue ++ * @skb: skb to start from ++ * @list_: list to peek at ++ * ++ * Returns %NULL when the end of the list is met or a pointer to the ++ * next element. The reference count is not incremented and the ++ * reference is therefore volatile. Use with caution. ++ */ ++static inline struct sk_buff *skb_peek_next(struct sk_buff *skb, ++ const struct sk_buff_head *list_) ++{ ++ struct sk_buff *next = skb->next; ++ ++ if (next == (struct sk_buff *)list_) ++ next = NULL; ++ return next; ++} ++ ++/** ++ * skb_peek_tail - peek at the tail of an &sk_buff_head ++ * @list_: list to peek at ++ * ++ * Peek an &sk_buff. Unlike most other operations you _MUST_ ++ * be careful with this one. A peek leaves the buffer on the ++ * list and someone else may run off with it. You must hold ++ * the appropriate locks or have a private queue to do this. ++ * ++ * Returns %NULL for an empty list or a pointer to the tail element. ++ * The reference count is not incremented and the reference is therefore ++ * volatile. Use with caution. ++ */ ++static inline struct sk_buff *skb_peek_tail(const struct sk_buff_head *list_) ++{ ++ struct sk_buff *skb = list_->prev; ++ ++ if (skb == (struct sk_buff *)list_) ++ skb = NULL; ++ return skb; ++ ++} ++ ++/** ++ * skb_queue_len - get queue length ++ * @list_: list to measure ++ * ++ * Return the length of an &sk_buff queue. ++ */ ++static inline __u32 skb_queue_len(const struct sk_buff_head *list_) ++{ ++ return list_->qlen; ++} ++ ++/** ++ * __skb_queue_head_init - initialize non-spinlock portions of sk_buff_head ++ * @list: queue to initialize ++ * ++ * This initializes only the list and queue length aspects of ++ * an sk_buff_head object. This allows to initialize the list ++ * aspects of an sk_buff_head without reinitializing things like ++ * the spinlock. It can also be used for on-stack sk_buff_head ++ * objects where the spinlock is known to not be used. ++ */ ++static inline void __skb_queue_head_init(struct sk_buff_head *list) ++{ ++ list->prev = list->next = (struct sk_buff *)list; ++ list->qlen = 0; ++} ++ ++/* ++ * This function creates a split out lock class for each invocation; ++ * this is needed for now since a whole lot of users of the skb-queue ++ * infrastructure in drivers have different locking usage (in hardirq) ++ * than the networking core (in softirq only). In the long run either the ++ * network layer or drivers should need annotation to consolidate the ++ * main types of usage into 3 classes. ++ */ ++static inline void skb_queue_head_init(struct sk_buff_head *list) ++{ ++ spin_lock_init(&list->lock); ++ __skb_queue_head_init(list); ++} ++ ++static inline void skb_queue_head_init_class(struct sk_buff_head *list, ++ struct lock_class_key *class) ++{ ++ skb_queue_head_init(list); ++ lockdep_set_class(&list->lock, class); ++} ++ ++/* ++ * Insert an sk_buff on a list. ++ * ++ * The "__skb_xxxx()" functions are the non-atomic ones that ++ * can only be called with interrupts disabled. ++ */ ++void skb_insert(struct sk_buff *old, struct sk_buff *newsk, ++ struct sk_buff_head *list); ++static inline void __skb_insert(struct sk_buff *newsk, ++ struct sk_buff *prev, struct sk_buff *next, ++ struct sk_buff_head *list) ++{ ++ newsk->next = next; ++ newsk->prev = prev; ++ next->prev = prev->next = newsk; ++ list->qlen++; ++} ++ ++static inline void __skb_queue_splice(const struct sk_buff_head *list, ++ struct sk_buff *prev, ++ struct sk_buff *next) ++{ ++ struct sk_buff *first = list->next; ++ struct sk_buff *last = list->prev; ++ ++ first->prev = prev; ++ prev->next = first; ++ ++ last->next = next; ++ next->prev = last; ++} ++ ++/** ++ * skb_queue_splice - join two skb lists, this is designed for stacks ++ * @list: the new list to add ++ * @head: the place to add it in the first list ++ */ ++static inline void skb_queue_splice(const struct sk_buff_head *list, ++ struct sk_buff_head *head) ++{ ++ if (!skb_queue_empty(list)) { ++ __skb_queue_splice(list, (struct sk_buff *) head, head->next); ++ head->qlen += list->qlen; ++ } ++} ++ ++/** ++ * skb_queue_splice_init - join two skb lists and reinitialise the emptied list ++ * @list: the new list to add ++ * @head: the place to add it in the first list ++ * ++ * The list at @list is reinitialised ++ */ ++static inline void skb_queue_splice_init(struct sk_buff_head *list, ++ struct sk_buff_head *head) ++{ ++ if (!skb_queue_empty(list)) { ++ __skb_queue_splice(list, (struct sk_buff *) head, head->next); ++ head->qlen += list->qlen; ++ __skb_queue_head_init(list); ++ } ++} ++ ++/** ++ * skb_queue_splice_tail - join two skb lists, each list being a queue ++ * @list: the new list to add ++ * @head: the place to add it in the first list ++ */ ++static inline void skb_queue_splice_tail(const struct sk_buff_head *list, ++ struct sk_buff_head *head) ++{ ++ if (!skb_queue_empty(list)) { ++ __skb_queue_splice(list, head->prev, (struct sk_buff *) head); ++ head->qlen += list->qlen; ++ } ++} ++ ++/** ++ * skb_queue_splice_tail_init - join two skb lists and reinitialise the emptied list ++ * @list: the new list to add ++ * @head: the place to add it in the first list ++ * ++ * Each of the lists is a queue. ++ * The list at @list is reinitialised ++ */ ++static inline void skb_queue_splice_tail_init(struct sk_buff_head *list, ++ struct sk_buff_head *head) ++{ ++ if (!skb_queue_empty(list)) { ++ __skb_queue_splice(list, head->prev, (struct sk_buff *) head); ++ head->qlen += list->qlen; ++ __skb_queue_head_init(list); ++ } ++} ++ ++/** ++ * __skb_queue_after - queue a buffer at the list head ++ * @list: list to use ++ * @prev: place after this buffer ++ * @newsk: buffer to queue ++ * ++ * Queue a buffer int the middle of a list. This function takes no locks ++ * and you must therefore hold required locks before calling it. ++ * ++ * A buffer cannot be placed on two lists at the same time. ++ */ ++static inline void __skb_queue_after(struct sk_buff_head *list, ++ struct sk_buff *prev, ++ struct sk_buff *newsk) ++{ ++ __skb_insert(newsk, prev, prev->next, list); ++} ++ ++void skb_append(struct sk_buff *old, struct sk_buff *newsk, ++ struct sk_buff_head *list); ++ ++static inline void __skb_queue_before(struct sk_buff_head *list, ++ struct sk_buff *next, ++ struct sk_buff *newsk) ++{ ++ __skb_insert(newsk, next->prev, next, list); ++} ++ ++/** ++ * __skb_queue_head - queue a buffer at the list head ++ * @list: list to use ++ * @newsk: buffer to queue ++ * ++ * Queue a buffer at the start of a list. This function takes no locks ++ * and you must therefore hold required locks before calling it. ++ * ++ * A buffer cannot be placed on two lists at the same time. ++ */ ++void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk); ++static inline void __skb_queue_head(struct sk_buff_head *list, ++ struct sk_buff *newsk) ++{ ++ __skb_queue_after(list, (struct sk_buff *)list, newsk); ++} ++ ++/** ++ * __skb_queue_tail - queue a buffer at the list tail ++ * @list: list to use ++ * @newsk: buffer to queue ++ * ++ * Queue a buffer at the end of a list. This function takes no locks ++ * and you must therefore hold required locks before calling it. ++ * ++ * A buffer cannot be placed on two lists at the same time. ++ */ ++void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk); ++static inline void __skb_queue_tail(struct sk_buff_head *list, ++ struct sk_buff *newsk) ++{ ++ __skb_queue_before(list, (struct sk_buff *)list, newsk); ++} ++ ++/* ++ * remove sk_buff from list. _Must_ be called atomically, and with ++ * the list known.. ++ */ ++void skb_unlink(struct sk_buff *skb, struct sk_buff_head *list); ++static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list) ++{ ++ struct sk_buff *next, *prev; ++ ++ list->qlen--; ++ next = skb->next; ++ prev = skb->prev; ++ skb->next = skb->prev = NULL; ++ next->prev = prev; ++ prev->next = next; ++} ++ ++/** ++ * __skb_dequeue - remove from the head of the queue ++ * @list: list to dequeue from ++ * ++ * Remove the head of the list. This function does not take any locks ++ * so must be used with appropriate locks held only. The head item is ++ * returned or %NULL if the list is empty. ++ */ ++struct sk_buff *skb_dequeue(struct sk_buff_head *list); ++static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list) ++{ ++ struct sk_buff *skb = skb_peek(list); ++ if (skb) ++ __skb_unlink(skb, list); ++ return skb; ++} ++ ++/** ++ * __skb_dequeue_tail - remove from the tail of the queue ++ * @list: list to dequeue from ++ * ++ * Remove the tail of the list. This function does not take any locks ++ * so must be used with appropriate locks held only. The tail item is ++ * returned or %NULL if the list is empty. ++ */ ++struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list); ++static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list) ++{ ++ struct sk_buff *skb = skb_peek_tail(list); ++ if (skb) ++ __skb_unlink(skb, list); ++ return skb; ++} ++ ++ ++static inline bool skb_is_nonlinear(const struct sk_buff *skb) ++{ ++ return skb->data_len; ++} ++ ++static inline unsigned int skb_headlen(const struct sk_buff *skb) ++{ ++ return skb->len - skb->data_len; ++} ++ ++static inline int skb_pagelen(const struct sk_buff *skb) ++{ ++ int i, len = 0; ++ ++ for (i = (int)skb_shinfo(skb)->nr_frags - 1; i >= 0; i--) ++ len += skb_frag_size(&skb_shinfo(skb)->frags[i]); ++ return len + skb_headlen(skb); ++} ++ ++/** ++ * __skb_fill_page_desc - initialise a paged fragment in an skb ++ * @skb: buffer containing fragment to be initialised ++ * @i: paged fragment index to initialise ++ * @page: the page to use for this fragment ++ * @off: the offset to the data with @page ++ * @size: the length of the data ++ * ++ * Initialises the @i'th fragment of @skb to point to &size bytes at ++ * offset @off within @page. ++ * ++ * Does not take any additional reference on the fragment. ++ */ ++static inline void __skb_fill_page_desc(struct sk_buff *skb, int i, ++ struct page *page, int off, int size) ++{ ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ ++ /* ++ * Propagate page->pfmemalloc to the skb if we can. The problem is ++ * that not all callers have unique ownership of the page. If ++ * pfmemalloc is set, we check the mapping as a mapping implies ++ * page->index is set (index and pfmemalloc share space). ++ * If it's a valid mapping, we cannot use page->pfmemalloc but we ++ * do not lose pfmemalloc information as the pages would not be ++ * allocated using __GFP_MEMALLOC. ++ */ ++ frag->page.p = page; ++ frag->page_offset = off; ++ skb_frag_size_set(frag, size); ++ ++ page = compound_head(page); ++ if (page->pfmemalloc && !page->mapping) ++ skb->pfmemalloc = true; ++} ++ ++/** ++ * skb_fill_page_desc - initialise a paged fragment in an skb ++ * @skb: buffer containing fragment to be initialised ++ * @i: paged fragment index to initialise ++ * @page: the page to use for this fragment ++ * @off: the offset to the data with @page ++ * @size: the length of the data ++ * ++ * As per __skb_fill_page_desc() -- initialises the @i'th fragment of ++ * @skb to point to @size bytes at offset @off within @page. In ++ * addition updates @skb such that @i is the last fragment. ++ * ++ * Does not take any additional reference on the fragment. ++ */ ++static inline void skb_fill_page_desc(struct sk_buff *skb, int i, ++ struct page *page, int off, int size) ++{ ++ __skb_fill_page_desc(skb, i, page, off, size); ++ skb_shinfo(skb)->nr_frags = i + 1; ++} ++ ++void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, ++ int size, unsigned int truesize); ++ ++void skb_coalesce_rx_frag(struct sk_buff *skb, int i, int size, ++ unsigned int truesize); ++ ++#define SKB_PAGE_ASSERT(skb) BUG_ON(skb_shinfo(skb)->nr_frags) ++#define SKB_FRAG_ASSERT(skb) BUG_ON(skb_has_frag_list(skb)) ++#define SKB_LINEAR_ASSERT(skb) BUG_ON(skb_is_nonlinear(skb)) ++ ++#ifdef NET_SKBUFF_DATA_USES_OFFSET ++static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) ++{ ++ return skb->head + skb->tail; ++} ++ ++static inline void skb_reset_tail_pointer(struct sk_buff *skb) ++{ ++ skb->tail = skb->data - skb->head; ++} ++ ++static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) ++{ ++ skb_reset_tail_pointer(skb); ++ skb->tail += offset; ++} ++ ++#else /* NET_SKBUFF_DATA_USES_OFFSET */ ++static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) ++{ ++ return skb->tail; ++} ++ ++static inline void skb_reset_tail_pointer(struct sk_buff *skb) ++{ ++ skb->tail = skb->data; ++} ++ ++static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) ++{ ++ skb->tail = skb->data + offset; ++} ++ ++#endif /* NET_SKBUFF_DATA_USES_OFFSET */ ++ ++/* ++ * Add data to an sk_buff ++ */ ++unsigned char *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len); ++unsigned char *skb_put(struct sk_buff *skb, unsigned int len); ++static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len) ++{ ++ unsigned char *tmp = skb_tail_pointer(skb); ++ SKB_LINEAR_ASSERT(skb); ++ skb->tail += len; ++ skb->len += len; ++ return tmp; ++} ++ ++unsigned char *skb_push(struct sk_buff *skb, unsigned int len); ++static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len) ++{ ++ skb->data -= len; ++ skb->len += len; ++ return skb->data; ++} ++ ++unsigned char *skb_pull(struct sk_buff *skb, unsigned int len); ++static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len) ++{ ++ skb->len -= len; ++ BUG_ON(skb->len < skb->data_len); ++ return skb->data += len; ++} ++ ++static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int len) ++{ ++ return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len); ++} ++ ++unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta); ++ ++static inline unsigned char *__pskb_pull(struct sk_buff *skb, unsigned int len) ++{ ++ if (len > skb_headlen(skb) && ++ !__pskb_pull_tail(skb, len - skb_headlen(skb))) ++ return NULL; ++ skb->len -= len; ++ return skb->data += len; ++} ++ ++static inline unsigned char *pskb_pull(struct sk_buff *skb, unsigned int len) ++{ ++ return unlikely(len > skb->len) ? NULL : __pskb_pull(skb, len); ++} ++ ++static inline int pskb_may_pull(struct sk_buff *skb, unsigned int len) ++{ ++ if (likely(len <= skb_headlen(skb))) ++ return 1; ++ if (unlikely(len > skb->len)) ++ return 0; ++ return __pskb_pull_tail(skb, len - skb_headlen(skb)) != NULL; ++} ++ ++/** ++ * skb_headroom - bytes at buffer head ++ * @skb: buffer to check ++ * ++ * Return the number of bytes of free space at the head of an &sk_buff. ++ */ ++static inline unsigned int skb_headroom(const struct sk_buff *skb) ++{ ++ return skb->data - skb->head; ++} ++ ++/** ++ * skb_tailroom - bytes at buffer end ++ * @skb: buffer to check ++ * ++ * Return the number of bytes of free space at the tail of an sk_buff ++ */ ++static inline int skb_tailroom(const struct sk_buff *skb) ++{ ++ return skb_is_nonlinear(skb) ? 0 : skb->end - skb->tail; ++} ++ ++/** ++ * skb_availroom - bytes at buffer end ++ * @skb: buffer to check ++ * ++ * Return the number of bytes of free space at the tail of an sk_buff ++ * allocated by sk_stream_alloc() ++ */ ++static inline int skb_availroom(const struct sk_buff *skb) ++{ ++ if (skb_is_nonlinear(skb)) ++ return 0; ++ ++ return skb->end - skb->tail - skb->reserved_tailroom; ++} ++ ++/** ++ * skb_reserve - adjust headroom ++ * @skb: buffer to alter ++ * @len: bytes to move ++ * ++ * Increase the headroom of an empty &sk_buff by reducing the tail ++ * room. This is only allowed for an empty buffer. ++ */ ++static inline void skb_reserve(struct sk_buff *skb, int len) ++{ ++ skb->data += len; ++ skb->tail += len; ++} ++ ++#define ENCAP_TYPE_ETHER 0 ++#define ENCAP_TYPE_IPPROTO 1 ++ ++static inline void skb_set_inner_protocol(struct sk_buff *skb, ++ __be16 protocol) ++{ ++ skb->inner_protocol = protocol; ++ skb->inner_protocol_type = ENCAP_TYPE_ETHER; ++} ++ ++static inline void skb_set_inner_ipproto(struct sk_buff *skb, ++ __u8 ipproto) ++{ ++ skb->inner_ipproto = ipproto; ++ skb->inner_protocol_type = ENCAP_TYPE_IPPROTO; ++} ++ ++static inline void skb_reset_inner_headers(struct sk_buff *skb) ++{ ++ skb->inner_mac_header = skb->mac_header; ++ skb->inner_network_header = skb->network_header; ++ skb->inner_transport_header = skb->transport_header; ++} ++ ++static inline void skb_reset_mac_len(struct sk_buff *skb) ++{ ++ skb->mac_len = skb->network_header - skb->mac_header; ++} ++ ++static inline unsigned char *skb_inner_transport_header(const struct sk_buff ++ *skb) ++{ ++ return skb->head + skb->inner_transport_header; ++} ++ ++static inline void skb_reset_inner_transport_header(struct sk_buff *skb) ++{ ++ skb->inner_transport_header = skb->data - skb->head; ++} ++ ++static inline void skb_set_inner_transport_header(struct sk_buff *skb, ++ const int offset) ++{ ++ skb_reset_inner_transport_header(skb); ++ skb->inner_transport_header += offset; ++} ++ ++static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb) ++{ ++ return skb->head + skb->inner_network_header; ++} ++ ++static inline void skb_reset_inner_network_header(struct sk_buff *skb) ++{ ++ skb->inner_network_header = skb->data - skb->head; ++} ++ ++static inline void skb_set_inner_network_header(struct sk_buff *skb, ++ const int offset) ++{ ++ skb_reset_inner_network_header(skb); ++ skb->inner_network_header += offset; ++} ++ ++static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb) ++{ ++ return skb->head + skb->inner_mac_header; ++} ++ ++static inline void skb_reset_inner_mac_header(struct sk_buff *skb) ++{ ++ skb->inner_mac_header = skb->data - skb->head; ++} ++ ++static inline void skb_set_inner_mac_header(struct sk_buff *skb, ++ const int offset) ++{ ++ skb_reset_inner_mac_header(skb); ++ skb->inner_mac_header += offset; ++} ++static inline bool skb_transport_header_was_set(const struct sk_buff *skb) ++{ ++ return skb->transport_header != (typeof(skb->transport_header))~0U; ++} ++ ++static inline unsigned char *skb_transport_header(const struct sk_buff *skb) ++{ ++ return skb->head + skb->transport_header; ++} ++ ++static inline void skb_reset_transport_header(struct sk_buff *skb) ++{ ++ skb->transport_header = skb->data - skb->head; ++} ++ ++static inline void skb_set_transport_header(struct sk_buff *skb, ++ const int offset) ++{ ++ skb_reset_transport_header(skb); ++ skb->transport_header += offset; ++} ++ ++static inline unsigned char *skb_network_header(const struct sk_buff *skb) ++{ ++ return skb->head + skb->network_header; ++} ++ ++static inline void skb_reset_network_header(struct sk_buff *skb) ++{ ++ skb->network_header = skb->data - skb->head; ++} ++ ++static inline void skb_set_network_header(struct sk_buff *skb, const int offset) ++{ ++ skb_reset_network_header(skb); ++ skb->network_header += offset; ++} ++ ++static inline unsigned char *skb_mac_header(const struct sk_buff *skb) ++{ ++ return skb->head + skb->mac_header; ++} ++ ++static inline int skb_mac_header_was_set(const struct sk_buff *skb) ++{ ++ return skb->mac_header != (typeof(skb->mac_header))~0U; ++} ++ ++static inline void skb_reset_mac_header(struct sk_buff *skb) ++{ ++ skb->mac_header = skb->data - skb->head; ++} ++ ++static inline void skb_set_mac_header(struct sk_buff *skb, const int offset) ++{ ++ skb_reset_mac_header(skb); ++ skb->mac_header += offset; ++} ++ ++static inline void skb_pop_mac_header(struct sk_buff *skb) ++{ ++ skb->mac_header = skb->network_header; ++} ++ ++static inline void skb_probe_transport_header(struct sk_buff *skb, ++ const int offset_hint) ++{ ++ struct flow_keys keys; ++ ++ if (skb_transport_header_was_set(skb)) ++ return; ++ else if (skb_flow_dissect(skb, &keys)) ++ skb_set_transport_header(skb, keys.thoff); ++ else ++ skb_set_transport_header(skb, offset_hint); ++} ++ ++static inline void skb_mac_header_rebuild(struct sk_buff *skb) ++{ ++ if (skb_mac_header_was_set(skb)) { ++ const unsigned char *old_mac = skb_mac_header(skb); ++ ++ skb_set_mac_header(skb, -skb->mac_len); ++ memmove(skb_mac_header(skb), old_mac, skb->mac_len); ++ } ++} ++ ++static inline int skb_checksum_start_offset(const struct sk_buff *skb) ++{ ++ return skb->csum_start - skb_headroom(skb); ++} ++ ++static inline int skb_transport_offset(const struct sk_buff *skb) ++{ ++ return skb_transport_header(skb) - skb->data; ++} ++ ++static inline u32 skb_network_header_len(const struct sk_buff *skb) ++{ ++ return skb->transport_header - skb->network_header; ++} ++ ++static inline u32 skb_inner_network_header_len(const struct sk_buff *skb) ++{ ++ return skb->inner_transport_header - skb->inner_network_header; ++} ++ ++static inline int skb_network_offset(const struct sk_buff *skb) ++{ ++ return skb_network_header(skb) - skb->data; ++} ++ ++static inline int skb_inner_network_offset(const struct sk_buff *skb) ++{ ++ return skb_inner_network_header(skb) - skb->data; ++} ++ ++static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len) ++{ ++ return pskb_may_pull(skb, skb_network_offset(skb) + len); ++} ++ ++/* ++ * CPUs often take a performance hit when accessing unaligned memory ++ * locations. The actual performance hit varies, it can be small if the ++ * hardware handles it or large if we have to take an exception and fix it ++ * in software. ++ * ++ * Since an ethernet header is 14 bytes network drivers often end up with ++ * the IP header at an unaligned offset. The IP header can be aligned by ++ * shifting the start of the packet by 2 bytes. Drivers should do this ++ * with: ++ * ++ * skb_reserve(skb, NET_IP_ALIGN); ++ * ++ * The downside to this alignment of the IP header is that the DMA is now ++ * unaligned. On some architectures the cost of an unaligned DMA is high ++ * and this cost outweighs the gains made by aligning the IP header. ++ * ++ * Since this trade off varies between architectures, we allow NET_IP_ALIGN ++ * to be overridden. ++ */ ++#ifndef NET_IP_ALIGN ++#define NET_IP_ALIGN 2 ++#endif ++ ++/* ++ * The networking layer reserves some headroom in skb data (via ++ * dev_alloc_skb). This is used to avoid having to reallocate skb data when ++ * the header has to grow. In the default case, if the header has to grow ++ * 32 bytes or less we avoid the reallocation. ++ * ++ * Unfortunately this headroom changes the DMA alignment of the resulting ++ * network packet. As for NET_IP_ALIGN, this unaligned DMA is expensive ++ * on some architectures. An architecture can override this value, ++ * perhaps setting it to a cacheline in size (since that will maintain ++ * cacheline alignment of the DMA). It must be a power of 2. ++ * ++ * Various parts of the networking layer expect at least 32 bytes of ++ * headroom, you should not reduce this. ++ * ++ * Using max(32, L1_CACHE_BYTES) makes sense (especially with RPS) ++ * to reduce average number of cache lines per packet. ++ * get_rps_cpus() for example only access one 64 bytes aligned block : ++ * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) ++ */ ++#ifndef NET_SKB_PAD ++#define NET_SKB_PAD max(32, L1_CACHE_BYTES) ++#endif ++ ++int ___pskb_trim(struct sk_buff *skb, unsigned int len); ++ ++static inline void __skb_trim(struct sk_buff *skb, unsigned int len) ++{ ++ if (unlikely(skb_is_nonlinear(skb))) { ++ WARN_ON(1); ++ return; ++ } ++ skb->len = len; ++ skb_set_tail_pointer(skb, len); ++} ++ ++void skb_trim(struct sk_buff *skb, unsigned int len); ++ ++static inline int __pskb_trim(struct sk_buff *skb, unsigned int len) ++{ ++ if (skb->data_len) ++ return ___pskb_trim(skb, len); ++ __skb_trim(skb, len); ++ return 0; ++} ++ ++static inline int pskb_trim(struct sk_buff *skb, unsigned int len) ++{ ++ return (len < skb->len) ? __pskb_trim(skb, len) : 0; ++} ++ ++/** ++ * pskb_trim_unique - remove end from a paged unique (not cloned) buffer ++ * @skb: buffer to alter ++ * @len: new length ++ * ++ * This is identical to pskb_trim except that the caller knows that ++ * the skb is not cloned so we should never get an error due to out- ++ * of-memory. ++ */ ++static inline void pskb_trim_unique(struct sk_buff *skb, unsigned int len) ++{ ++ int err = pskb_trim(skb, len); ++ BUG_ON(err); ++} ++ ++/** ++ * skb_orphan - orphan a buffer ++ * @skb: buffer to orphan ++ * ++ * If a buffer currently has an owner then we call the owner's ++ * destructor function and make the @skb unowned. The buffer continues ++ * to exist but is no longer charged to its former owner. ++ */ ++static inline void skb_orphan(struct sk_buff *skb) ++{ ++ if (skb->destructor) { ++ skb->destructor(skb); ++ skb->destructor = NULL; ++ skb->sk = NULL; ++ } else { ++ BUG_ON(skb->sk); ++ } ++} ++ ++/** ++ * skb_orphan_frags - orphan the frags contained in a buffer ++ * @skb: buffer to orphan frags from ++ * @gfp_mask: allocation mask for replacement pages ++ * ++ * For each frag in the SKB which needs a destructor (i.e. has an ++ * owner) create a copy of that frag and release the original ++ * page by calling the destructor. ++ */ ++static inline int skb_orphan_frags(struct sk_buff *skb, gfp_t gfp_mask) ++{ ++ if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY))) ++ return 0; ++ return skb_copy_ubufs(skb, gfp_mask); ++} ++ ++/** ++ * __skb_queue_purge - empty a list ++ * @list: list to empty ++ * ++ * Delete all buffers on an &sk_buff list. Each buffer is removed from ++ * the list and one reference dropped. This function does not take the ++ * list lock and the caller must hold the relevant locks to use it. ++ */ ++void skb_queue_purge(struct sk_buff_head *list); ++static inline void __skb_queue_purge(struct sk_buff_head *list) ++{ ++ struct sk_buff *skb; ++ while ((skb = __skb_dequeue(list)) != NULL) ++ kfree_skb(skb); ++} ++ ++#define NETDEV_FRAG_PAGE_MAX_ORDER get_order(32768) ++#define NETDEV_FRAG_PAGE_MAX_SIZE (PAGE_SIZE << NETDEV_FRAG_PAGE_MAX_ORDER) ++#define NETDEV_PAGECNT_MAX_BIAS NETDEV_FRAG_PAGE_MAX_SIZE ++ ++void *netdev_alloc_frag(unsigned int fragsz); ++ ++struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int length, ++ gfp_t gfp_mask); ++ ++/** ++ * netdev_alloc_skb - allocate an skbuff for rx on a specific device ++ * @dev: network device to receive on ++ * @length: length to allocate ++ * ++ * Allocate a new &sk_buff and assign it a usage count of one. The ++ * buffer has unspecified headroom built in. Users should allocate ++ * the headroom they think they need without accounting for the ++ * built in space. The built in space is used for optimisations. ++ * ++ * %NULL is returned if there is no free memory. Although this function ++ * allocates memory it can be called from an interrupt. ++ */ ++static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev, ++ unsigned int length) ++{ ++ return __netdev_alloc_skb(dev, length, GFP_ATOMIC); ++} ++ ++/* legacy helper around __netdev_alloc_skb() */ ++static inline struct sk_buff *__dev_alloc_skb(unsigned int length, ++ gfp_t gfp_mask) ++{ ++ return __netdev_alloc_skb(NULL, length, gfp_mask); ++} ++ ++/* legacy helper around netdev_alloc_skb() */ ++static inline struct sk_buff *dev_alloc_skb(unsigned int length) ++{ ++ return netdev_alloc_skb(NULL, length); ++} ++ ++ ++static inline struct sk_buff *__netdev_alloc_skb_ip_align(struct net_device *dev, ++ unsigned int length, gfp_t gfp) ++{ ++ struct sk_buff *skb = __netdev_alloc_skb(dev, length + NET_IP_ALIGN, gfp); ++ ++ if (NET_IP_ALIGN && skb) ++ skb_reserve(skb, NET_IP_ALIGN); ++ return skb; ++} ++ ++static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev, ++ unsigned int length) ++{ ++ return __netdev_alloc_skb_ip_align(dev, length, GFP_ATOMIC); ++} ++ ++/** ++ * __skb_alloc_pages - allocate pages for ps-rx on a skb and preserve pfmemalloc data ++ * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX ++ * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used ++ * @order: size of the allocation ++ * ++ * Allocate a new page. ++ * ++ * %NULL is returned if there is no free memory. ++*/ ++static inline struct page *__skb_alloc_pages(gfp_t gfp_mask, ++ struct sk_buff *skb, ++ unsigned int order) ++{ ++ struct page *page; ++ ++ gfp_mask |= __GFP_COLD; ++ ++ if (!(gfp_mask & __GFP_NOMEMALLOC)) ++ gfp_mask |= __GFP_MEMALLOC; ++ ++ page = alloc_pages_node(NUMA_NO_NODE, gfp_mask, order); ++ if (skb && page && page->pfmemalloc) ++ skb->pfmemalloc = true; ++ ++ return page; ++} ++ ++/** ++ * __skb_alloc_page - allocate a page for ps-rx for a given skb and preserve pfmemalloc data ++ * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX ++ * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used ++ * ++ * Allocate a new page. ++ * ++ * %NULL is returned if there is no free memory. ++ */ ++static inline struct page *__skb_alloc_page(gfp_t gfp_mask, ++ struct sk_buff *skb) ++{ ++ return __skb_alloc_pages(gfp_mask, skb, 0); ++} ++ ++/** ++ * skb_propagate_pfmemalloc - Propagate pfmemalloc if skb is allocated after RX page ++ * @page: The page that was allocated from skb_alloc_page ++ * @skb: The skb that may need pfmemalloc set ++ */ ++static inline void skb_propagate_pfmemalloc(struct page *page, ++ struct sk_buff *skb) ++{ ++ if (page && page->pfmemalloc) ++ skb->pfmemalloc = true; ++} ++ ++/** ++ * skb_frag_page - retrieve the page referred to by a paged fragment ++ * @frag: the paged fragment ++ * ++ * Returns the &struct page associated with @frag. ++ */ ++static inline struct page *skb_frag_page(const skb_frag_t *frag) ++{ ++ return frag->page.p; ++} ++ ++/** ++ * __skb_frag_ref - take an addition reference on a paged fragment. ++ * @frag: the paged fragment ++ * ++ * Takes an additional reference on the paged fragment @frag. ++ */ ++static inline void __skb_frag_ref(skb_frag_t *frag) ++{ ++ get_page(skb_frag_page(frag)); ++} ++ ++/** ++ * skb_frag_ref - take an addition reference on a paged fragment of an skb. ++ * @skb: the buffer ++ * @f: the fragment offset. ++ * ++ * Takes an additional reference on the @f'th paged fragment of @skb. ++ */ ++static inline void skb_frag_ref(struct sk_buff *skb, int f) ++{ ++ __skb_frag_ref(&skb_shinfo(skb)->frags[f]); ++} ++ ++/** ++ * __skb_frag_unref - release a reference on a paged fragment. ++ * @frag: the paged fragment ++ * ++ * Releases a reference on the paged fragment @frag. ++ */ ++static inline void __skb_frag_unref(skb_frag_t *frag) ++{ ++ put_page(skb_frag_page(frag)); ++} ++ ++/** ++ * skb_frag_unref - release a reference on a paged fragment of an skb. ++ * @skb: the buffer ++ * @f: the fragment offset ++ * ++ * Releases a reference on the @f'th paged fragment of @skb. ++ */ ++static inline void skb_frag_unref(struct sk_buff *skb, int f) ++{ ++ __skb_frag_unref(&skb_shinfo(skb)->frags[f]); ++} ++ ++/** ++ * skb_frag_address - gets the address of the data contained in a paged fragment ++ * @frag: the paged fragment buffer ++ * ++ * Returns the address of the data within @frag. The page must already ++ * be mapped. ++ */ ++static inline void *skb_frag_address(const skb_frag_t *frag) ++{ ++ return page_address(skb_frag_page(frag)) + frag->page_offset; ++} ++ ++/** ++ * skb_frag_address_safe - gets the address of the data contained in a paged fragment ++ * @frag: the paged fragment buffer ++ * ++ * Returns the address of the data within @frag. Checks that the page ++ * is mapped and returns %NULL otherwise. ++ */ ++static inline void *skb_frag_address_safe(const skb_frag_t *frag) ++{ ++ void *ptr = page_address(skb_frag_page(frag)); ++ if (unlikely(!ptr)) ++ return NULL; ++ ++ return ptr + frag->page_offset; ++} ++ ++/** ++ * __skb_frag_set_page - sets the page contained in a paged fragment ++ * @frag: the paged fragment ++ * @page: the page to set ++ * ++ * Sets the fragment @frag to contain @page. ++ */ ++static inline void __skb_frag_set_page(skb_frag_t *frag, struct page *page) ++{ ++ frag->page.p = page; ++} ++ ++/** ++ * skb_frag_set_page - sets the page contained in a paged fragment of an skb ++ * @skb: the buffer ++ * @f: the fragment offset ++ * @page: the page to set ++ * ++ * Sets the @f'th fragment of @skb to contain @page. ++ */ ++static inline void skb_frag_set_page(struct sk_buff *skb, int f, ++ struct page *page) ++{ ++ __skb_frag_set_page(&skb_shinfo(skb)->frags[f], page); ++} ++ ++bool skb_page_frag_refill(unsigned int sz, struct page_frag *pfrag, gfp_t prio); ++ ++/** ++ * skb_frag_dma_map - maps a paged fragment via the DMA API ++ * @dev: the device to map the fragment to ++ * @frag: the paged fragment to map ++ * @offset: the offset within the fragment (starting at the ++ * fragment's own offset) ++ * @size: the number of bytes to map ++ * @dir: the direction of the mapping (%PCI_DMA_*) ++ * ++ * Maps the page associated with @frag to @device. ++ */ ++static inline dma_addr_t skb_frag_dma_map(struct device *dev, ++ const skb_frag_t *frag, ++ size_t offset, size_t size, ++ enum dma_data_direction dir) ++{ ++ return dma_map_page(dev, skb_frag_page(frag), ++ frag->page_offset + offset, size, dir); ++} ++ ++static inline struct sk_buff *pskb_copy(struct sk_buff *skb, ++ gfp_t gfp_mask) ++{ ++ return __pskb_copy(skb, skb_headroom(skb), gfp_mask); ++} ++ ++ ++static inline struct sk_buff *pskb_copy_for_clone(struct sk_buff *skb, ++ gfp_t gfp_mask) ++{ ++ return __pskb_copy_fclone(skb, skb_headroom(skb), gfp_mask, true); ++} ++ ++ ++/** ++ * skb_clone_writable - is the header of a clone writable ++ * @skb: buffer to check ++ * @len: length up to which to write ++ * ++ * Returns true if modifying the header part of the cloned buffer ++ * does not requires the data to be copied. ++ */ ++static inline int skb_clone_writable(const struct sk_buff *skb, unsigned int len) ++{ ++ return !skb_header_cloned(skb) && ++ skb_headroom(skb) + len <= skb->hdr_len; ++} ++ ++static inline int __skb_cow(struct sk_buff *skb, unsigned int headroom, ++ int cloned) ++{ ++ int delta = 0; ++ ++ if (headroom > skb_headroom(skb)) ++ delta = headroom - skb_headroom(skb); ++ ++ if (delta || cloned) ++ return pskb_expand_head(skb, ALIGN(delta, NET_SKB_PAD), 0, ++ GFP_ATOMIC); ++ return 0; ++} ++ ++/** ++ * skb_cow - copy header of skb when it is required ++ * @skb: buffer to cow ++ * @headroom: needed headroom ++ * ++ * If the skb passed lacks sufficient headroom or its data part ++ * is shared, data is reallocated. If reallocation fails, an error ++ * is returned and original skb is not changed. ++ * ++ * The result is skb with writable area skb->head...skb->tail ++ * and at least @headroom of space at head. ++ */ ++static inline int skb_cow(struct sk_buff *skb, unsigned int headroom) ++{ ++ return __skb_cow(skb, headroom, skb_cloned(skb)); ++} ++ ++/** ++ * skb_cow_head - skb_cow but only making the head writable ++ * @skb: buffer to cow ++ * @headroom: needed headroom ++ * ++ * This function is identical to skb_cow except that we replace the ++ * skb_cloned check by skb_header_cloned. It should be used when ++ * you only need to push on some header and do not need to modify ++ * the data. ++ */ ++static inline int skb_cow_head(struct sk_buff *skb, unsigned int headroom) ++{ ++ return __skb_cow(skb, headroom, skb_header_cloned(skb)); ++} ++ ++/** ++ * skb_padto - pad an skbuff up to a minimal size ++ * @skb: buffer to pad ++ * @len: minimal length ++ * ++ * Pads up a buffer to ensure the trailing bytes exist and are ++ * blanked. If the buffer already contains sufficient data it ++ * is untouched. Otherwise it is extended. Returns zero on ++ * success. The skb is freed on error. ++ */ ++ ++static inline int skb_padto(struct sk_buff *skb, unsigned int len) ++{ ++ unsigned int size = skb->len; ++ if (likely(size >= len)) ++ return 0; ++ return skb_pad(skb, len - size); ++} ++ ++static inline int skb_add_data(struct sk_buff *skb, ++ char __user *from, int copy) ++{ ++ const int off = skb->len; ++ ++ if (skb->ip_summed == CHECKSUM_NONE) { ++ int err = 0; ++ __wsum csum = csum_and_copy_from_user(from, skb_put(skb, copy), ++ copy, 0, &err); ++ if (!err) { ++ skb->csum = csum_block_add(skb->csum, csum, off); ++ return 0; ++ } ++ } else if (!copy_from_user(skb_put(skb, copy), from, copy)) ++ return 0; ++ ++ __skb_trim(skb, off); ++ return -EFAULT; ++} ++ ++static inline bool skb_can_coalesce(struct sk_buff *skb, int i, ++ const struct page *page, int off) ++{ ++ if (i) { ++ const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i - 1]; ++ ++ return page == skb_frag_page(frag) && ++ off == frag->page_offset + skb_frag_size(frag); ++ } ++ return false; ++} ++ ++static inline int __skb_linearize(struct sk_buff *skb) ++{ ++ return __pskb_pull_tail(skb, skb->data_len) ? 0 : -ENOMEM; ++} ++ ++/** ++ * skb_linearize - convert paged skb to linear one ++ * @skb: buffer to linarize ++ * ++ * If there is no free memory -ENOMEM is returned, otherwise zero ++ * is returned and the old skb data released. ++ */ ++static inline int skb_linearize(struct sk_buff *skb) ++{ ++ return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0; ++} ++ ++/** ++ * skb_has_shared_frag - can any frag be overwritten ++ * @skb: buffer to test ++ * ++ * Return true if the skb has at least one frag that might be modified ++ * by an external entity (as in vmsplice()/sendfile()) ++ */ ++static inline bool skb_has_shared_frag(const struct sk_buff *skb) ++{ ++ return skb_is_nonlinear(skb) && ++ skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; ++} ++ ++/** ++ * skb_linearize_cow - make sure skb is linear and writable ++ * @skb: buffer to process ++ * ++ * If there is no free memory -ENOMEM is returned, otherwise zero ++ * is returned and the old skb data released. ++ */ ++static inline int skb_linearize_cow(struct sk_buff *skb) ++{ ++ return skb_is_nonlinear(skb) || skb_cloned(skb) ? ++ __skb_linearize(skb) : 0; ++} ++ ++/** ++ * skb_postpull_rcsum - update checksum for received skb after pull ++ * @skb: buffer to update ++ * @start: start of data before pull ++ * @len: length of data pulled ++ * ++ * After doing a pull on a received packet, you need to call this to ++ * update the CHECKSUM_COMPLETE checksum, or set ip_summed to ++ * CHECKSUM_NONE so that it can be recomputed from scratch. ++ */ ++ ++static inline void skb_postpull_rcsum(struct sk_buff *skb, ++ const void *start, unsigned int len) ++{ ++ if (skb->ip_summed == CHECKSUM_COMPLETE) ++ skb->csum = csum_sub(skb->csum, csum_partial(start, len, 0)); ++} ++ ++unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len); ++ ++/** ++ * pskb_trim_rcsum - trim received skb and update checksum ++ * @skb: buffer to trim ++ * @len: new length ++ * ++ * This is exactly the same as pskb_trim except that it ensures the ++ * checksum of received packets are still valid after the operation. ++ */ ++ ++static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len) ++{ ++ if (likely(len >= skb->len)) ++ return 0; ++ if (skb->ip_summed == CHECKSUM_COMPLETE) ++ skb->ip_summed = CHECKSUM_NONE; ++ return __pskb_trim(skb, len); ++} ++ ++#define skb_queue_walk(queue, skb) \ ++ for (skb = (queue)->next; \ ++ skb != (struct sk_buff *)(queue); \ ++ skb = skb->next) ++ ++#define skb_queue_walk_safe(queue, skb, tmp) \ ++ for (skb = (queue)->next, tmp = skb->next; \ ++ skb != (struct sk_buff *)(queue); \ ++ skb = tmp, tmp = skb->next) ++ ++#define skb_queue_walk_from(queue, skb) \ ++ for (; skb != (struct sk_buff *)(queue); \ ++ skb = skb->next) ++ ++#define skb_queue_walk_from_safe(queue, skb, tmp) \ ++ for (tmp = skb->next; \ ++ skb != (struct sk_buff *)(queue); \ ++ skb = tmp, tmp = skb->next) ++ ++#define skb_queue_reverse_walk(queue, skb) \ ++ for (skb = (queue)->prev; \ ++ skb != (struct sk_buff *)(queue); \ ++ skb = skb->prev) ++ ++#define skb_queue_reverse_walk_safe(queue, skb, tmp) \ ++ for (skb = (queue)->prev, tmp = skb->prev; \ ++ skb != (struct sk_buff *)(queue); \ ++ skb = tmp, tmp = skb->prev) ++ ++#define skb_queue_reverse_walk_from_safe(queue, skb, tmp) \ ++ for (tmp = skb->prev; \ ++ skb != (struct sk_buff *)(queue); \ ++ skb = tmp, tmp = skb->prev) ++ ++static inline bool skb_has_frag_list(const struct sk_buff *skb) ++{ ++ return skb_shinfo(skb)->frag_list != NULL; ++} ++ ++static inline void skb_frag_list_init(struct sk_buff *skb) ++{ ++ skb_shinfo(skb)->frag_list = NULL; ++} ++ ++static inline void skb_frag_add_head(struct sk_buff *skb, struct sk_buff *frag) ++{ ++ frag->next = skb_shinfo(skb)->frag_list; ++ skb_shinfo(skb)->frag_list = frag; ++} ++ ++#define skb_walk_frags(skb, iter) \ ++ for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next) ++ ++struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, ++ int *peeked, int *off, int *err); ++struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, ++ int *err); ++unsigned int datagram_poll(struct file *file, struct socket *sock, ++ struct poll_table_struct *wait); ++int skb_copy_datagram_iovec(const struct sk_buff *from, int offset, ++ struct iovec *to, int size); ++int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, int hlen, ++ struct iovec *iov); ++int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, ++ const struct iovec *from, int from_offset, ++ int len); ++int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *frm, ++ int offset, size_t count); ++int skb_copy_datagram_const_iovec(const struct sk_buff *from, int offset, ++ const struct iovec *to, int to_offset, ++ int size); ++void skb_free_datagram(struct sock *sk, struct sk_buff *skb); ++void skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb); ++int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags); ++int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len); ++int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len); ++__wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, ++ int len, __wsum csum); ++int skb_splice_bits(struct sk_buff *skb, unsigned int offset, ++ struct pipe_inode_info *pipe, unsigned int len, ++ unsigned int flags); ++void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); ++unsigned int skb_zerocopy_headlen(const struct sk_buff *from); ++int skb_zerocopy(struct sk_buff *to, struct sk_buff *from, ++ int len, int hlen); ++void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); ++int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); ++void skb_scrub_packet(struct sk_buff *skb, bool xnet); ++unsigned int skb_gso_transport_seglen(const struct sk_buff *skb); ++struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features); ++struct sk_buff *skb_vlan_untag(struct sk_buff *skb); ++ ++struct skb_checksum_ops { ++ __wsum (*update)(const void *mem, int len, __wsum wsum); ++ __wsum (*combine)(__wsum csum, __wsum csum2, int offset, int len); ++}; ++ ++__wsum __skb_checksum(const struct sk_buff *skb, int offset, int len, ++ __wsum csum, const struct skb_checksum_ops *ops); ++__wsum skb_checksum(const struct sk_buff *skb, int offset, int len, ++ __wsum csum); ++ ++static inline void *__skb_header_pointer(const struct sk_buff *skb, int offset, ++ int len, void *data, int hlen, void *buffer) ++{ ++ if (hlen - offset >= len) ++ return data + offset; ++ ++ if (!skb || ++ skb_copy_bits(skb, offset, buffer, len) < 0) ++ return NULL; ++ ++ return buffer; ++} ++ ++static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, ++ int len, void *buffer) ++{ ++ return __skb_header_pointer(skb, offset, len, skb->data, ++ skb_headlen(skb), buffer); ++} ++ ++/** ++ * skb_needs_linearize - check if we need to linearize a given skb ++ * depending on the given device features. ++ * @skb: socket buffer to check ++ * @features: net device features ++ * ++ * Returns true if either: ++ * 1. skb has frag_list and the device doesn't support FRAGLIST, or ++ * 2. skb is fragmented and the device does not support SG. ++ */ ++static inline bool skb_needs_linearize(struct sk_buff *skb, ++ netdev_features_t features) ++{ ++ return skb_is_nonlinear(skb) && ++ ((skb_has_frag_list(skb) && !(features & NETIF_F_FRAGLIST)) || ++ (skb_shinfo(skb)->nr_frags && !(features & NETIF_F_SG))); ++} ++ ++static inline void skb_copy_from_linear_data(const struct sk_buff *skb, ++ void *to, ++ const unsigned int len) ++{ ++ memcpy(to, skb->data, len); ++} ++ ++static inline void skb_copy_from_linear_data_offset(const struct sk_buff *skb, ++ const int offset, void *to, ++ const unsigned int len) ++{ ++ memcpy(to, skb->data + offset, len); ++} ++ ++static inline void skb_copy_to_linear_data(struct sk_buff *skb, ++ const void *from, ++ const unsigned int len) ++{ ++ memcpy(skb->data, from, len); ++} ++ ++static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb, ++ const int offset, ++ const void *from, ++ const unsigned int len) ++{ ++ memcpy(skb->data + offset, from, len); ++} ++ ++void skb_init(void); ++ ++static inline ktime_t skb_get_ktime(const struct sk_buff *skb) ++{ ++ return skb->tstamp; ++} ++ ++/** ++ * skb_get_timestamp - get timestamp from a skb ++ * @skb: skb to get stamp from ++ * @stamp: pointer to struct timeval to store stamp in ++ * ++ * Timestamps are stored in the skb as offsets to a base timestamp. ++ * This function converts the offset back to a struct timeval and stores ++ * it in stamp. ++ */ ++static inline void skb_get_timestamp(const struct sk_buff *skb, ++ struct timeval *stamp) ++{ ++ *stamp = ktime_to_timeval(skb->tstamp); ++} ++ ++static inline void skb_get_timestampns(const struct sk_buff *skb, ++ struct timespec *stamp) ++{ ++ *stamp = ktime_to_timespec(skb->tstamp); ++} ++ ++static inline void __net_timestamp(struct sk_buff *skb) ++{ ++ skb->tstamp = ktime_get_real(); ++} ++ ++static inline ktime_t net_timedelta(ktime_t t) ++{ ++ return ktime_sub(ktime_get_real(), t); ++} ++ ++static inline ktime_t net_invalid_timestamp(void) ++{ ++ return ktime_set(0, 0); ++} ++ ++struct sk_buff *skb_clone_sk(struct sk_buff *skb); ++ ++#ifdef CONFIG_NETWORK_PHY_TIMESTAMPING ++ ++void skb_clone_tx_timestamp(struct sk_buff *skb); ++bool skb_defer_rx_timestamp(struct sk_buff *skb); ++ ++#else /* CONFIG_NETWORK_PHY_TIMESTAMPING */ ++ ++static inline void skb_clone_tx_timestamp(struct sk_buff *skb) ++{ ++} ++ ++static inline bool skb_defer_rx_timestamp(struct sk_buff *skb) ++{ ++ return false; ++} ++ ++#endif /* !CONFIG_NETWORK_PHY_TIMESTAMPING */ ++ ++/** ++ * skb_complete_tx_timestamp() - deliver cloned skb with tx timestamps ++ * ++ * PHY drivers may accept clones of transmitted packets for ++ * timestamping via their phy_driver.txtstamp method. These drivers ++ * must call this function to return the skb back to the stack, with ++ * or without a timestamp. ++ * ++ * @skb: clone of the the original outgoing packet ++ * @hwtstamps: hardware time stamps, may be NULL if not available ++ * ++ */ ++void skb_complete_tx_timestamp(struct sk_buff *skb, ++ struct skb_shared_hwtstamps *hwtstamps); ++ ++void __skb_tstamp_tx(struct sk_buff *orig_skb, ++ struct skb_shared_hwtstamps *hwtstamps, ++ struct sock *sk, int tstype); ++ ++/** ++ * skb_tstamp_tx - queue clone of skb with send time stamps ++ * @orig_skb: the original outgoing packet ++ * @hwtstamps: hardware time stamps, may be NULL if not available ++ * ++ * If the skb has a socket associated, then this function clones the ++ * skb (thus sharing the actual data and optional structures), stores ++ * the optional hardware time stamping information (if non NULL) or ++ * generates a software time stamp (otherwise), then queues the clone ++ * to the error queue of the socket. Errors are silently ignored. ++ */ ++void skb_tstamp_tx(struct sk_buff *orig_skb, ++ struct skb_shared_hwtstamps *hwtstamps); ++ ++static inline void sw_tx_timestamp(struct sk_buff *skb) ++{ ++ if (skb_shinfo(skb)->tx_flags & SKBTX_SW_TSTAMP && ++ !(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) ++ skb_tstamp_tx(skb, NULL); ++} ++ ++/** ++ * skb_tx_timestamp() - Driver hook for transmit timestamping ++ * ++ * Ethernet MAC Drivers should call this function in their hard_xmit() ++ * function immediately before giving the sk_buff to the MAC hardware. ++ * ++ * Specifically, one should make absolutely sure that this function is ++ * called before TX completion of this packet can trigger. Otherwise ++ * the packet could potentially already be freed. ++ * ++ * @skb: A socket buffer. ++ */ ++static inline void skb_tx_timestamp(struct sk_buff *skb) ++{ ++ skb_clone_tx_timestamp(skb); ++ sw_tx_timestamp(skb); ++} ++ ++/** ++ * skb_complete_wifi_ack - deliver skb with wifi status ++ * ++ * @skb: the original outgoing packet ++ * @acked: ack status ++ * ++ */ ++void skb_complete_wifi_ack(struct sk_buff *skb, bool acked); ++ ++__sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len); ++__sum16 __skb_checksum_complete(struct sk_buff *skb); ++ ++static inline int skb_csum_unnecessary(const struct sk_buff *skb) ++{ ++ return ((skb->ip_summed & CHECKSUM_UNNECESSARY) || skb->csum_valid); ++} ++ ++/** ++ * skb_checksum_complete - Calculate checksum of an entire packet ++ * @skb: packet to process ++ * ++ * This function calculates the checksum over the entire packet plus ++ * the value of skb->csum. The latter can be used to supply the ++ * checksum of a pseudo header as used by TCP/UDP. It returns the ++ * checksum. ++ * ++ * For protocols that contain complete checksums such as ICMP/TCP/UDP, ++ * this function can be used to verify that checksum on received ++ * packets. In that case the function should return zero if the ++ * checksum is correct. In particular, this function will return zero ++ * if skb->ip_summed is CHECKSUM_UNNECESSARY which indicates that the ++ * hardware has already verified the correctness of the checksum. ++ */ ++static inline __sum16 skb_checksum_complete(struct sk_buff *skb) ++{ ++ return skb_csum_unnecessary(skb) ? ++ 0 : __skb_checksum_complete(skb); ++} ++ ++static inline void __skb_decr_checksum_unnecessary(struct sk_buff *skb) ++{ ++ if (skb->ip_summed == CHECKSUM_UNNECESSARY) { ++ if (skb->csum_level == 0) ++ skb->ip_summed = CHECKSUM_NONE; ++ else ++ skb->csum_level--; ++ } ++} ++ ++static inline void __skb_incr_checksum_unnecessary(struct sk_buff *skb) ++{ ++ if (skb->ip_summed == CHECKSUM_UNNECESSARY) { ++ if (skb->csum_level < SKB_MAX_CSUM_LEVEL) ++ skb->csum_level++; ++ } else if (skb->ip_summed == CHECKSUM_NONE) { ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ skb->csum_level = 0; ++ } ++} ++ ++static inline void __skb_mark_checksum_bad(struct sk_buff *skb) ++{ ++ /* Mark current checksum as bad (typically called from GRO ++ * path). In the case that ip_summed is CHECKSUM_NONE ++ * this must be the first checksum encountered in the packet. ++ * When ip_summed is CHECKSUM_UNNECESSARY, this is the first ++ * checksum after the last one validated. For UDP, a zero ++ * checksum can not be marked as bad. ++ */ ++ ++ if (skb->ip_summed == CHECKSUM_NONE || ++ skb->ip_summed == CHECKSUM_UNNECESSARY) ++ skb->csum_bad = 1; ++} ++ ++/* Check if we need to perform checksum complete validation. ++ * ++ * Returns true if checksum complete is needed, false otherwise ++ * (either checksum is unnecessary or zero checksum is allowed). ++ */ ++static inline bool __skb_checksum_validate_needed(struct sk_buff *skb, ++ bool zero_okay, ++ __sum16 check) ++{ ++ if (skb_csum_unnecessary(skb) || (zero_okay && !check)) { ++ skb->csum_valid = 1; ++ __skb_decr_checksum_unnecessary(skb); ++ return false; ++ } ++ ++ return true; ++} ++ ++/* For small packets <= CHECKSUM_BREAK peform checksum complete directly ++ * in checksum_init. ++ */ ++#define CHECKSUM_BREAK 76 ++ ++/* Unset checksum-complete ++ * ++ * Unset checksum complete can be done when packet is being modified ++ * (uncompressed for instance) and checksum-complete value is ++ * invalidated. ++ */ ++static inline void skb_checksum_complete_unset(struct sk_buff *skb) ++{ ++ if (skb->ip_summed == CHECKSUM_COMPLETE) ++ skb->ip_summed = CHECKSUM_NONE; ++} ++ ++/* Validate (init) checksum based on checksum complete. ++ * ++ * Return values: ++ * 0: checksum is validated or try to in skb_checksum_complete. In the latter ++ * case the ip_summed will not be CHECKSUM_UNNECESSARY and the pseudo ++ * checksum is stored in skb->csum for use in __skb_checksum_complete ++ * non-zero: value of invalid checksum ++ * ++ */ ++static inline __sum16 __skb_checksum_validate_complete(struct sk_buff *skb, ++ bool complete, ++ __wsum psum) ++{ ++ if (skb->ip_summed == CHECKSUM_COMPLETE) { ++ if (!csum_fold(csum_add(psum, skb->csum))) { ++ skb->csum_valid = 1; ++ return 0; ++ } ++ } else if (skb->csum_bad) { ++ /* ip_summed == CHECKSUM_NONE in this case */ ++ return 1; ++ } ++ ++ skb->csum = psum; ++ ++ if (complete || skb->len <= CHECKSUM_BREAK) { ++ __sum16 csum; ++ ++ csum = __skb_checksum_complete(skb); ++ skb->csum_valid = !csum; ++ return csum; ++ } ++ ++ return 0; ++} ++ ++static inline __wsum null_compute_pseudo(struct sk_buff *skb, int proto) ++{ ++ return 0; ++} ++ ++/* Perform checksum validate (init). Note that this is a macro since we only ++ * want to calculate the pseudo header which is an input function if necessary. ++ * First we try to validate without any computation (checksum unnecessary) and ++ * then calculate based on checksum complete calling the function to compute ++ * pseudo header. ++ * ++ * Return values: ++ * 0: checksum is validated or try to in skb_checksum_complete ++ * non-zero: value of invalid checksum ++ */ ++#define __skb_checksum_validate(skb, proto, complete, \ ++ zero_okay, check, compute_pseudo) \ ++({ \ ++ __sum16 __ret = 0; \ ++ skb->csum_valid = 0; \ ++ if (__skb_checksum_validate_needed(skb, zero_okay, check)) \ ++ __ret = __skb_checksum_validate_complete(skb, \ ++ complete, compute_pseudo(skb, proto)); \ ++ __ret; \ ++}) ++ ++#define skb_checksum_init(skb, proto, compute_pseudo) \ ++ __skb_checksum_validate(skb, proto, false, false, 0, compute_pseudo) ++ ++#define skb_checksum_init_zero_check(skb, proto, check, compute_pseudo) \ ++ __skb_checksum_validate(skb, proto, false, true, check, compute_pseudo) ++ ++#define skb_checksum_validate(skb, proto, compute_pseudo) \ ++ __skb_checksum_validate(skb, proto, true, false, 0, compute_pseudo) ++ ++#define skb_checksum_validate_zero_check(skb, proto, check, \ ++ compute_pseudo) \ ++ __skb_checksum_validate_(skb, proto, true, true, check, compute_pseudo) ++ ++#define skb_checksum_simple_validate(skb) \ ++ __skb_checksum_validate(skb, 0, true, false, 0, null_compute_pseudo) ++ ++static inline bool __skb_checksum_convert_check(struct sk_buff *skb) ++{ ++ return (skb->ip_summed == CHECKSUM_NONE && ++ skb->csum_valid && !skb->csum_bad); ++} ++ ++static inline void __skb_checksum_convert(struct sk_buff *skb, ++ __sum16 check, __wsum pseudo) ++{ ++ skb->csum = ~pseudo; ++ skb->ip_summed = CHECKSUM_COMPLETE; ++} ++ ++#define skb_checksum_try_convert(skb, proto, check, compute_pseudo) \ ++do { \ ++ if (__skb_checksum_convert_check(skb)) \ ++ __skb_checksum_convert(skb, check, \ ++ compute_pseudo(skb, proto)); \ ++} while (0) ++ ++#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) ++void nf_conntrack_destroy(struct nf_conntrack *nfct); ++static inline void nf_conntrack_put(struct nf_conntrack *nfct) ++{ ++ if (nfct && atomic_dec_and_test(&nfct->use)) ++ nf_conntrack_destroy(nfct); ++} ++static inline void nf_conntrack_get(struct nf_conntrack *nfct) ++{ ++ if (nfct) ++ atomic_inc(&nfct->use); ++} ++#endif ++#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) ++static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge) ++{ ++ if (nf_bridge && atomic_dec_and_test(&nf_bridge->use)) ++ kfree(nf_bridge); ++} ++static inline void nf_bridge_get(struct nf_bridge_info *nf_bridge) ++{ ++ if (nf_bridge) ++ atomic_inc(&nf_bridge->use); ++} ++#endif /* CONFIG_BRIDGE_NETFILTER */ ++static inline void nf_reset(struct sk_buff *skb) ++{ ++#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) ++ nf_conntrack_put(skb->nfct); ++ skb->nfct = NULL; ++#endif ++#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) ++ nf_bridge_put(skb->nf_bridge); ++ skb->nf_bridge = NULL; ++#endif ++} ++ ++static inline void nf_reset_trace(struct sk_buff *skb) ++{ ++#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || defined(CONFIG_NF_TABLES) ++ skb->nf_trace = 0; ++#endif ++} ++ ++/* Note: This doesn't put any conntrack and bridge info in dst. */ ++static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src, ++ bool copy) ++{ ++#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) ++ dst->nfct = src->nfct; ++ nf_conntrack_get(src->nfct); ++ if (copy) ++ dst->nfctinfo = src->nfctinfo; ++#endif ++#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) ++ dst->nf_bridge = src->nf_bridge; ++ nf_bridge_get(src->nf_bridge); ++#endif ++#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || defined(CONFIG_NF_TABLES) ++ if (copy) ++ dst->nf_trace = src->nf_trace; ++#endif ++} ++ ++static inline void nf_copy(struct sk_buff *dst, const struct sk_buff *src) ++{ ++#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) ++ nf_conntrack_put(dst->nfct); ++#endif ++#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) ++ nf_bridge_put(dst->nf_bridge); ++#endif ++ __nf_copy(dst, src, true); ++} ++ ++#ifdef CONFIG_NETWORK_SECMARK ++static inline void skb_copy_secmark(struct sk_buff *to, const struct sk_buff *from) ++{ ++ to->secmark = from->secmark; ++} ++ ++static inline void skb_init_secmark(struct sk_buff *skb) ++{ ++ skb->secmark = 0; ++} ++#else ++static inline void skb_copy_secmark(struct sk_buff *to, const struct sk_buff *from) ++{ } ++ ++static inline void skb_init_secmark(struct sk_buff *skb) ++{ } ++#endif ++ ++static inline bool skb_irq_freeable(const struct sk_buff *skb) ++{ ++ return !skb->destructor && ++#if IS_ENABLED(CONFIG_XFRM) ++ !skb->sp && ++#endif ++#if IS_ENABLED(CONFIG_NF_CONNTRACK) ++ !skb->nfct && ++#endif ++ !skb->_skb_refdst && ++ !skb_has_frag_list(skb); ++} ++ ++static inline void skb_set_queue_mapping(struct sk_buff *skb, u16 queue_mapping) ++{ ++ skb->queue_mapping = queue_mapping; ++} ++ ++static inline u16 skb_get_queue_mapping(const struct sk_buff *skb) ++{ ++ return skb->queue_mapping; ++} ++ ++static inline void skb_copy_queue_mapping(struct sk_buff *to, const struct sk_buff *from) ++{ ++ to->queue_mapping = from->queue_mapping; ++} ++ ++static inline void skb_record_rx_queue(struct sk_buff *skb, u16 rx_queue) ++{ ++ skb->queue_mapping = rx_queue + 1; ++} ++ ++static inline u16 skb_get_rx_queue(const struct sk_buff *skb) ++{ ++ return skb->queue_mapping - 1; ++} ++ ++static inline bool skb_rx_queue_recorded(const struct sk_buff *skb) ++{ ++ return skb->queue_mapping != 0; ++} ++ ++u16 __skb_tx_hash(const struct net_device *dev, struct sk_buff *skb, ++ unsigned int num_tx_queues); ++ ++static inline struct sec_path *skb_sec_path(struct sk_buff *skb) ++{ ++#ifdef CONFIG_XFRM ++ return skb->sp; ++#else ++ return NULL; ++#endif ++} ++ ++/* Keeps track of mac header offset relative to skb->head. ++ * It is useful for TSO of Tunneling protocol. e.g. GRE. ++ * For non-tunnel skb it points to skb_mac_header() and for ++ * tunnel skb it points to outer mac header. ++ * Keeps track of level of encapsulation of network headers. ++ */ ++struct skb_gso_cb { ++ int mac_offset; ++ int encap_level; ++ __u16 csum_start; ++}; ++#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb) ++ ++static inline int skb_tnl_header_len(const struct sk_buff *inner_skb) ++{ ++ return (skb_mac_header(inner_skb) - inner_skb->head) - ++ SKB_GSO_CB(inner_skb)->mac_offset; ++} ++ ++static inline int gso_pskb_expand_head(struct sk_buff *skb, int extra) ++{ ++ int new_headroom, headroom; ++ int ret; ++ ++ headroom = skb_headroom(skb); ++ ret = pskb_expand_head(skb, extra, 0, GFP_ATOMIC); ++ if (ret) ++ return ret; ++ ++ new_headroom = skb_headroom(skb); ++ SKB_GSO_CB(skb)->mac_offset += (new_headroom - headroom); ++ return 0; ++} ++ ++/* Compute the checksum for a gso segment. First compute the checksum value ++ * from the start of transport header to SKB_GSO_CB(skb)->csum_start, and ++ * then add in skb->csum (checksum from csum_start to end of packet). ++ * skb->csum and csum_start are then updated to reflect the checksum of the ++ * resultant packet starting from the transport header-- the resultant checksum ++ * is in the res argument (i.e. normally zero or ~ of checksum of a pseudo ++ * header. ++ */ ++static inline __sum16 gso_make_checksum(struct sk_buff *skb, __wsum res) ++{ ++ int plen = SKB_GSO_CB(skb)->csum_start - skb_headroom(skb) - ++ skb_transport_offset(skb); ++ __u16 csum; ++ ++ csum = csum_fold(csum_partial(skb_transport_header(skb), ++ plen, skb->csum)); ++ skb->csum = res; ++ SKB_GSO_CB(skb)->csum_start -= plen; ++ ++ return csum; ++} ++ ++static inline bool skb_is_gso(const struct sk_buff *skb) ++{ ++ return skb_shinfo(skb)->gso_size; ++} ++ ++/* Note: Should be called only if skb_is_gso(skb) is true */ ++static inline bool skb_is_gso_v6(const struct sk_buff *skb) ++{ ++ return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6; ++} ++ ++void __skb_warn_lro_forwarding(const struct sk_buff *skb); ++ ++static inline bool skb_warn_if_lro(const struct sk_buff *skb) ++{ ++ /* LRO sets gso_size but not gso_type, whereas if GSO is really ++ * wanted then gso_type will be set. */ ++ const struct skb_shared_info *shinfo = skb_shinfo(skb); ++ ++ if (skb_is_nonlinear(skb) && shinfo->gso_size != 0 && ++ unlikely(shinfo->gso_type == 0)) { ++ __skb_warn_lro_forwarding(skb); ++ return true; ++ } ++ return false; ++} ++ ++static inline void skb_forward_csum(struct sk_buff *skb) ++{ ++ /* Unfortunately we don't support this one. Any brave souls? */ ++ if (skb->ip_summed == CHECKSUM_COMPLETE) ++ skb->ip_summed = CHECKSUM_NONE; ++} ++ ++/** ++ * skb_checksum_none_assert - make sure skb ip_summed is CHECKSUM_NONE ++ * @skb: skb to check ++ * ++ * fresh skbs have their ip_summed set to CHECKSUM_NONE. ++ * Instead of forcing ip_summed to CHECKSUM_NONE, we can ++ * use this helper, to document places where we make this assertion. ++ */ ++static inline void skb_checksum_none_assert(const struct sk_buff *skb) ++{ ++#ifdef DEBUG ++ BUG_ON(skb->ip_summed != CHECKSUM_NONE); ++#endif ++} ++ ++bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off); ++ ++int skb_checksum_setup(struct sk_buff *skb, bool recalculate); ++ ++u32 skb_get_poff(const struct sk_buff *skb); ++u32 __skb_get_poff(const struct sk_buff *skb, void *data, ++ const struct flow_keys *keys, int hlen); ++ ++/** ++ * skb_head_is_locked - Determine if the skb->head is locked down ++ * @skb: skb to check ++ * ++ * The head on skbs build around a head frag can be removed if they are ++ * not cloned. This function returns true if the skb head is locked down ++ * due to either being allocated via kmalloc, or by being a clone with ++ * multiple references to the head. ++ */ ++static inline bool skb_head_is_locked(const struct sk_buff *skb) ++{ ++ return !skb->head_frag || skb_cloned(skb); ++} ++ ++/** ++ * skb_gso_network_seglen - Return length of individual segments of a gso packet ++ * ++ * @skb: GSO skb ++ * ++ * skb_gso_network_seglen is used to determine the real size of the ++ * individual segments, including Layer3 (IP, IPv6) and L4 headers (TCP/UDP). ++ * ++ * The MAC/L2 header is not accounted for. ++ */ ++static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb) ++{ ++ unsigned int hdr_len = skb_transport_header(skb) - ++ skb_network_header(skb); ++ return hdr_len + skb_gso_transport_seglen(skb); ++} ++#endif /* __KERNEL__ */ ++#endif /* _LINUX_SKBUFF_H */ +diff -Nur linux-3.18.14.orig/include/linux/smp.h linux-3.18.14-rt/include/linux/smp.h +--- linux-3.18.14.orig/include/linux/smp.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/smp.h 2015-05-31 15:32:48.405635367 -0500 +@@ -178,6 +178,9 @@ + #define get_cpu() ({ preempt_disable(); smp_processor_id(); }) + #define put_cpu() preempt_enable() + ++#define get_cpu_light() ({ migrate_disable(); smp_processor_id(); }) ++#define put_cpu_light() migrate_enable() ++ + /* + * Callback to arch code if there's nosmp or maxcpus=0 on the + * boot command line: +diff -Nur linux-3.18.14.orig/include/linux/spinlock_api_smp.h linux-3.18.14-rt/include/linux/spinlock_api_smp.h +--- linux-3.18.14.orig/include/linux/spinlock_api_smp.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/spinlock_api_smp.h 2015-05-31 15:32:48.409635367 -0500 +@@ -187,6 +187,8 @@ + return 0; + } + +-#include ++#ifndef CONFIG_PREEMPT_RT_FULL ++# include ++#endif + + #endif /* __LINUX_SPINLOCK_API_SMP_H */ +diff -Nur linux-3.18.14.orig/include/linux/spinlock.h linux-3.18.14-rt/include/linux/spinlock.h +--- linux-3.18.14.orig/include/linux/spinlock.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/spinlock.h 2015-05-31 15:32:48.405635367 -0500 +@@ -278,7 +278,11 @@ + #define raw_spin_can_lock(lock) (!raw_spin_is_locked(lock)) + + /* Include rwlock functions */ +-#include ++#ifdef CONFIG_PREEMPT_RT_FULL ++# include ++#else ++# include ++#endif + + /* + * Pull the _spin_*()/_read_*()/_write_*() functions/declarations: +@@ -289,6 +293,10 @@ + # include + #endif + ++#ifdef CONFIG_PREEMPT_RT_FULL ++# include ++#else /* PREEMPT_RT_FULL */ ++ + /* + * Map the spin_lock functions to the raw variants for PREEMPT_RT=n + */ +@@ -418,4 +426,6 @@ + #define atomic_dec_and_lock(atomic, lock) \ + __cond_lock(lock, _atomic_dec_and_lock(atomic, lock)) + ++#endif /* !PREEMPT_RT_FULL */ ++ + #endif /* __LINUX_SPINLOCK_H */ +diff -Nur linux-3.18.14.orig/include/linux/spinlock_rt.h linux-3.18.14-rt/include/linux/spinlock_rt.h +--- linux-3.18.14.orig/include/linux/spinlock_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/spinlock_rt.h 2015-05-31 15:32:48.413635367 -0500 +@@ -0,0 +1,167 @@ ++#ifndef __LINUX_SPINLOCK_RT_H ++#define __LINUX_SPINLOCK_RT_H ++ ++#ifndef __LINUX_SPINLOCK_H ++#error Do not include directly. Use spinlock.h ++#endif ++ ++#include ++ ++extern void ++__rt_spin_lock_init(spinlock_t *lock, char *name, struct lock_class_key *key); ++ ++#define spin_lock_init(slock) \ ++do { \ ++ static struct lock_class_key __key; \ ++ \ ++ rt_mutex_init(&(slock)->lock); \ ++ __rt_spin_lock_init(slock, #slock, &__key); \ ++} while (0) ++ ++extern void __lockfunc rt_spin_lock(spinlock_t *lock); ++extern unsigned long __lockfunc rt_spin_lock_trace_flags(spinlock_t *lock); ++extern void __lockfunc rt_spin_lock_nested(spinlock_t *lock, int subclass); ++extern void __lockfunc rt_spin_unlock(spinlock_t *lock); ++extern void __lockfunc rt_spin_unlock_after_trylock_in_irq(spinlock_t *lock); ++extern void __lockfunc rt_spin_unlock_wait(spinlock_t *lock); ++extern int __lockfunc rt_spin_trylock_irqsave(spinlock_t *lock, unsigned long *flags); ++extern int __lockfunc rt_spin_trylock_bh(spinlock_t *lock); ++extern int __lockfunc rt_spin_trylock(spinlock_t *lock); ++extern int atomic_dec_and_spin_lock(atomic_t *atomic, spinlock_t *lock); ++ ++/* ++ * lockdep-less calls, for derived types like rwlock: ++ * (for trylock they can use rt_mutex_trylock() directly. ++ */ ++extern void __lockfunc __rt_spin_lock(struct rt_mutex *lock); ++extern void __lockfunc __rt_spin_unlock(struct rt_mutex *lock); ++extern int __lockfunc __rt_spin_trylock(struct rt_mutex *lock); ++ ++#define spin_lock(lock) \ ++ do { \ ++ migrate_disable(); \ ++ rt_spin_lock(lock); \ ++ } while (0) ++ ++#define spin_lock_bh(lock) \ ++ do { \ ++ local_bh_disable(); \ ++ migrate_disable(); \ ++ rt_spin_lock(lock); \ ++ } while (0) ++ ++#define spin_lock_irq(lock) spin_lock(lock) ++ ++#define spin_do_trylock(lock) __cond_lock(lock, rt_spin_trylock(lock)) ++ ++#define spin_trylock(lock) \ ++({ \ ++ int __locked; \ ++ migrate_disable(); \ ++ __locked = spin_do_trylock(lock); \ ++ if (!__locked) \ ++ migrate_enable(); \ ++ __locked; \ ++}) ++ ++#ifdef CONFIG_LOCKDEP ++# define spin_lock_nested(lock, subclass) \ ++ do { \ ++ migrate_disable(); \ ++ rt_spin_lock_nested(lock, subclass); \ ++ } while (0) ++ ++# define spin_lock_irqsave_nested(lock, flags, subclass) \ ++ do { \ ++ typecheck(unsigned long, flags); \ ++ flags = 0; \ ++ migrate_disable(); \ ++ rt_spin_lock_nested(lock, subclass); \ ++ } while (0) ++#else ++# define spin_lock_nested(lock, subclass) spin_lock(lock) ++ ++# define spin_lock_irqsave_nested(lock, flags, subclass) \ ++ do { \ ++ typecheck(unsigned long, flags); \ ++ flags = 0; \ ++ spin_lock(lock); \ ++ } while (0) ++#endif ++ ++#define spin_lock_irqsave(lock, flags) \ ++ do { \ ++ typecheck(unsigned long, flags); \ ++ flags = 0; \ ++ spin_lock(lock); \ ++ } while (0) ++ ++static inline unsigned long spin_lock_trace_flags(spinlock_t *lock) ++{ ++ unsigned long flags = 0; ++#ifdef CONFIG_TRACE_IRQFLAGS ++ flags = rt_spin_lock_trace_flags(lock); ++#else ++ spin_lock(lock); /* lock_local */ ++#endif ++ return flags; ++} ++ ++/* FIXME: we need rt_spin_lock_nest_lock */ ++#define spin_lock_nest_lock(lock, nest_lock) spin_lock_nested(lock, 0) ++ ++#define spin_unlock(lock) \ ++ do { \ ++ rt_spin_unlock(lock); \ ++ migrate_enable(); \ ++ } while (0) ++ ++#define spin_unlock_bh(lock) \ ++ do { \ ++ rt_spin_unlock(lock); \ ++ migrate_enable(); \ ++ local_bh_enable(); \ ++ } while (0) ++ ++#define spin_unlock_irq(lock) spin_unlock(lock) ++ ++#define spin_unlock_irqrestore(lock, flags) \ ++ do { \ ++ typecheck(unsigned long, flags); \ ++ (void) flags; \ ++ spin_unlock(lock); \ ++ } while (0) ++ ++#define spin_trylock_bh(lock) __cond_lock(lock, rt_spin_trylock_bh(lock)) ++#define spin_trylock_irq(lock) spin_trylock(lock) ++ ++#define spin_trylock_irqsave(lock, flags) \ ++ rt_spin_trylock_irqsave(lock, &(flags)) ++ ++#define spin_unlock_wait(lock) rt_spin_unlock_wait(lock) ++ ++#ifdef CONFIG_GENERIC_LOCKBREAK ++# define spin_is_contended(lock) ((lock)->break_lock) ++#else ++# define spin_is_contended(lock) (((void)(lock), 0)) ++#endif ++ ++static inline int spin_can_lock(spinlock_t *lock) ++{ ++ return !rt_mutex_is_locked(&lock->lock); ++} ++ ++static inline int spin_is_locked(spinlock_t *lock) ++{ ++ return rt_mutex_is_locked(&lock->lock); ++} ++ ++static inline void assert_spin_locked(spinlock_t *lock) ++{ ++ BUG_ON(!spin_is_locked(lock)); ++} ++ ++#define atomic_dec_and_lock(atomic, lock) \ ++ atomic_dec_and_spin_lock(atomic, lock) ++ ++#endif +diff -Nur linux-3.18.14.orig/include/linux/spinlock_types.h linux-3.18.14-rt/include/linux/spinlock_types.h +--- linux-3.18.14.orig/include/linux/spinlock_types.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/spinlock_types.h 2015-05-31 15:32:48.413635367 -0500 +@@ -9,80 +9,15 @@ + * Released under the General Public License (GPL). + */ + +-#if defined(CONFIG_SMP) +-# include +-#else +-# include +-#endif +- +-#include +- +-typedef struct raw_spinlock { +- arch_spinlock_t raw_lock; +-#ifdef CONFIG_GENERIC_LOCKBREAK +- unsigned int break_lock; +-#endif +-#ifdef CONFIG_DEBUG_SPINLOCK +- unsigned int magic, owner_cpu; +- void *owner; +-#endif +-#ifdef CONFIG_DEBUG_LOCK_ALLOC +- struct lockdep_map dep_map; +-#endif +-} raw_spinlock_t; +- +-#define SPINLOCK_MAGIC 0xdead4ead +- +-#define SPINLOCK_OWNER_INIT ((void *)-1L) +- +-#ifdef CONFIG_DEBUG_LOCK_ALLOC +-# define SPIN_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname } +-#else +-# define SPIN_DEP_MAP_INIT(lockname) +-#endif ++#include + +-#ifdef CONFIG_DEBUG_SPINLOCK +-# define SPIN_DEBUG_INIT(lockname) \ +- .magic = SPINLOCK_MAGIC, \ +- .owner_cpu = -1, \ +- .owner = SPINLOCK_OWNER_INIT, ++#ifndef CONFIG_PREEMPT_RT_FULL ++# include ++# include + #else +-# define SPIN_DEBUG_INIT(lockname) ++# include ++# include ++# include + #endif + +-#define __RAW_SPIN_LOCK_INITIALIZER(lockname) \ +- { \ +- .raw_lock = __ARCH_SPIN_LOCK_UNLOCKED, \ +- SPIN_DEBUG_INIT(lockname) \ +- SPIN_DEP_MAP_INIT(lockname) } +- +-#define __RAW_SPIN_LOCK_UNLOCKED(lockname) \ +- (raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname) +- +-#define DEFINE_RAW_SPINLOCK(x) raw_spinlock_t x = __RAW_SPIN_LOCK_UNLOCKED(x) +- +-typedef struct spinlock { +- union { +- struct raw_spinlock rlock; +- +-#ifdef CONFIG_DEBUG_LOCK_ALLOC +-# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) +- struct { +- u8 __padding[LOCK_PADSIZE]; +- struct lockdep_map dep_map; +- }; +-#endif +- }; +-} spinlock_t; +- +-#define __SPIN_LOCK_INITIALIZER(lockname) \ +- { { .rlock = __RAW_SPIN_LOCK_INITIALIZER(lockname) } } +- +-#define __SPIN_LOCK_UNLOCKED(lockname) \ +- (spinlock_t ) __SPIN_LOCK_INITIALIZER(lockname) +- +-#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x) +- +-#include +- + #endif /* __LINUX_SPINLOCK_TYPES_H */ +diff -Nur linux-3.18.14.orig/include/linux/spinlock_types_nort.h linux-3.18.14-rt/include/linux/spinlock_types_nort.h +--- linux-3.18.14.orig/include/linux/spinlock_types_nort.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/spinlock_types_nort.h 2015-05-31 15:32:48.413635367 -0500 +@@ -0,0 +1,33 @@ ++#ifndef __LINUX_SPINLOCK_TYPES_NORT_H ++#define __LINUX_SPINLOCK_TYPES_NORT_H ++ ++#ifndef __LINUX_SPINLOCK_TYPES_H ++#error "Do not include directly. Include spinlock_types.h instead" ++#endif ++ ++/* ++ * The non RT version maps spinlocks to raw_spinlocks ++ */ ++typedef struct spinlock { ++ union { ++ struct raw_spinlock rlock; ++ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map)) ++ struct { ++ u8 __padding[LOCK_PADSIZE]; ++ struct lockdep_map dep_map; ++ }; ++#endif ++ }; ++} spinlock_t; ++ ++#define __SPIN_LOCK_INITIALIZER(lockname) \ ++ { { .rlock = __RAW_SPIN_LOCK_INITIALIZER(lockname) } } ++ ++#define __SPIN_LOCK_UNLOCKED(lockname) \ ++ (spinlock_t ) __SPIN_LOCK_INITIALIZER(lockname) ++ ++#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x) ++ ++#endif +diff -Nur linux-3.18.14.orig/include/linux/spinlock_types_raw.h linux-3.18.14-rt/include/linux/spinlock_types_raw.h +--- linux-3.18.14.orig/include/linux/spinlock_types_raw.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/spinlock_types_raw.h 2015-05-31 15:32:48.413635367 -0500 +@@ -0,0 +1,56 @@ ++#ifndef __LINUX_SPINLOCK_TYPES_RAW_H ++#define __LINUX_SPINLOCK_TYPES_RAW_H ++ ++#if defined(CONFIG_SMP) ++# include ++#else ++# include ++#endif ++ ++#include ++ ++typedef struct raw_spinlock { ++ arch_spinlock_t raw_lock; ++#ifdef CONFIG_GENERIC_LOCKBREAK ++ unsigned int break_lock; ++#endif ++#ifdef CONFIG_DEBUG_SPINLOCK ++ unsigned int magic, owner_cpu; ++ void *owner; ++#endif ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ struct lockdep_map dep_map; ++#endif ++} raw_spinlock_t; ++ ++#define SPINLOCK_MAGIC 0xdead4ead ++ ++#define SPINLOCK_OWNER_INIT ((void *)-1L) ++ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++# define SPIN_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname } ++#else ++# define SPIN_DEP_MAP_INIT(lockname) ++#endif ++ ++#ifdef CONFIG_DEBUG_SPINLOCK ++# define SPIN_DEBUG_INIT(lockname) \ ++ .magic = SPINLOCK_MAGIC, \ ++ .owner_cpu = -1, \ ++ .owner = SPINLOCK_OWNER_INIT, ++#else ++# define SPIN_DEBUG_INIT(lockname) ++#endif ++ ++#define __RAW_SPIN_LOCK_INITIALIZER(lockname) \ ++ { \ ++ .raw_lock = __ARCH_SPIN_LOCK_UNLOCKED, \ ++ SPIN_DEBUG_INIT(lockname) \ ++ SPIN_DEP_MAP_INIT(lockname) } ++ ++#define __RAW_SPIN_LOCK_UNLOCKED(lockname) \ ++ (raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname) ++ ++#define DEFINE_RAW_SPINLOCK(x) raw_spinlock_t x = __RAW_SPIN_LOCK_UNLOCKED(x) ++ ++#endif +diff -Nur linux-3.18.14.orig/include/linux/spinlock_types_rt.h linux-3.18.14-rt/include/linux/spinlock_types_rt.h +--- linux-3.18.14.orig/include/linux/spinlock_types_rt.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/spinlock_types_rt.h 2015-05-31 15:32:48.413635367 -0500 +@@ -0,0 +1,51 @@ ++#ifndef __LINUX_SPINLOCK_TYPES_RT_H ++#define __LINUX_SPINLOCK_TYPES_RT_H ++ ++#ifndef __LINUX_SPINLOCK_TYPES_H ++#error "Do not include directly. Include spinlock_types.h instead" ++#endif ++ ++#include ++ ++/* ++ * PREEMPT_RT: spinlocks - an RT mutex plus lock-break field: ++ */ ++typedef struct spinlock { ++ struct rt_mutex lock; ++ unsigned int break_lock; ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ struct lockdep_map dep_map; ++#endif ++} spinlock_t; ++ ++#ifdef CONFIG_DEBUG_RT_MUTEXES ++# define __RT_SPIN_INITIALIZER(name) \ ++ { \ ++ .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock), \ ++ .save_state = 1, \ ++ .file = __FILE__, \ ++ .line = __LINE__ , \ ++ } ++#else ++# define __RT_SPIN_INITIALIZER(name) \ ++ { \ ++ .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock), \ ++ .save_state = 1, \ ++ } ++#endif ++ ++/* ++.wait_list = PLIST_HEAD_INIT_RAW((name).lock.wait_list, (name).lock.wait_lock) ++*/ ++ ++#define __SPIN_LOCK_UNLOCKED(name) \ ++ { .lock = __RT_SPIN_INITIALIZER(name.lock), \ ++ SPIN_DEP_MAP_INIT(name) } ++ ++#define __DEFINE_SPINLOCK(name) \ ++ spinlock_t name = __SPIN_LOCK_UNLOCKED(name) ++ ++#define DEFINE_SPINLOCK(name) \ ++ spinlock_t name __cacheline_aligned_in_smp = __SPIN_LOCK_UNLOCKED(name) ++ ++#endif +diff -Nur linux-3.18.14.orig/include/linux/srcu.h linux-3.18.14-rt/include/linux/srcu.h +--- linux-3.18.14.orig/include/linux/srcu.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/srcu.h 2015-05-31 15:32:48.445635367 -0500 +@@ -84,10 +84,10 @@ + + void process_srcu(struct work_struct *work); + +-#define __SRCU_STRUCT_INIT(name) \ ++#define __SRCU_STRUCT_INIT(name, pcpu_name) \ + { \ + .completed = -300, \ +- .per_cpu_ref = &name##_srcu_array, \ ++ .per_cpu_ref = &pcpu_name, \ + .queue_lock = __SPIN_LOCK_UNLOCKED(name.queue_lock), \ + .running = false, \ + .batch_queue = RCU_BATCH_INIT(name.batch_queue), \ +@@ -104,11 +104,12 @@ + */ + #define DEFINE_SRCU(name) \ + static DEFINE_PER_CPU(struct srcu_struct_array, name##_srcu_array);\ +- struct srcu_struct name = __SRCU_STRUCT_INIT(name); ++ struct srcu_struct name = __SRCU_STRUCT_INIT(name, name##_srcu_array); + + #define DEFINE_STATIC_SRCU(name) \ + static DEFINE_PER_CPU(struct srcu_struct_array, name##_srcu_array);\ +- static struct srcu_struct name = __SRCU_STRUCT_INIT(name); ++ static struct srcu_struct name = __SRCU_STRUCT_INIT(\ ++ name, name##_srcu_array); + + /** + * call_srcu() - Queue a callback for invocation after an SRCU grace period +diff -Nur linux-3.18.14.orig/include/linux/swap.h linux-3.18.14-rt/include/linux/swap.h +--- linux-3.18.14.orig/include/linux/swap.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/swap.h 2015-05-31 15:32:48.449635367 -0500 +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + + struct notifier_block; +@@ -260,7 +261,8 @@ + void *workingset_eviction(struct address_space *mapping, struct page *page); + bool workingset_refault(void *shadow); + void workingset_activation(struct page *page); +-extern struct list_lru workingset_shadow_nodes; ++extern struct list_lru __workingset_shadow_nodes; ++DECLARE_LOCAL_IRQ_LOCK(workingset_shadow_lock); + + static inline unsigned int workingset_node_pages(struct radix_tree_node *node) + { +diff -Nur linux-3.18.14.orig/include/linux/sysctl.h linux-3.18.14-rt/include/linux/sysctl.h +--- linux-3.18.14.orig/include/linux/sysctl.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/sysctl.h 2015-05-31 15:32:48.449635367 -0500 +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + + /* For the /proc/sys support */ +diff -Nur linux-3.18.14.orig/include/linux/thread_info.h linux-3.18.14-rt/include/linux/thread_info.h +--- linux-3.18.14.orig/include/linux/thread_info.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/thread_info.h 2015-05-31 15:32:48.449635367 -0500 +@@ -102,7 +102,17 @@ + #define test_thread_flag(flag) \ + test_ti_thread_flag(current_thread_info(), flag) + +-#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED) ++#ifdef CONFIG_PREEMPT_LAZY ++#define tif_need_resched() (test_thread_flag(TIF_NEED_RESCHED) || \ ++ test_thread_flag(TIF_NEED_RESCHED_LAZY)) ++#define tif_need_resched_now() (test_thread_flag(TIF_NEED_RESCHED)) ++#define tif_need_resched_lazy() test_thread_flag(TIF_NEED_RESCHED_LAZY)) ++ ++#else ++#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED) ++#define tif_need_resched_now() test_thread_flag(TIF_NEED_RESCHED) ++#define tif_need_resched_lazy() 0 ++#endif + + #if defined TIF_RESTORE_SIGMASK && !defined HAVE_SET_RESTORE_SIGMASK + /* +diff -Nur linux-3.18.14.orig/include/linux/timer.h linux-3.18.14-rt/include/linux/timer.h +--- linux-3.18.14.orig/include/linux/timer.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/timer.h 2015-05-31 15:32:48.449635367 -0500 +@@ -241,7 +241,7 @@ + + extern int try_to_del_timer_sync(struct timer_list *timer); + +-#ifdef CONFIG_SMP ++#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) + extern int del_timer_sync(struct timer_list *timer); + #else + # define del_timer_sync(t) del_timer(t) +diff -Nur linux-3.18.14.orig/include/linux/uaccess.h linux-3.18.14-rt/include/linux/uaccess.h +--- linux-3.18.14.orig/include/linux/uaccess.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/uaccess.h 2015-05-31 15:32:48.449635367 -0500 +@@ -6,14 +6,9 @@ + + /* + * These routines enable/disable the pagefault handler in that +- * it will not take any locks and go straight to the fixup table. +- * +- * They have great resemblance to the preempt_disable/enable calls +- * and in fact they are identical; this is because currently there is +- * no other way to make the pagefault handlers do this. So we do +- * disable preemption but we don't necessarily care about that. ++ * it will not take any MM locks and go straight to the fixup table. + */ +-static inline void pagefault_disable(void) ++static inline void raw_pagefault_disable(void) + { + preempt_count_inc(); + /* +@@ -23,7 +18,7 @@ + barrier(); + } + +-static inline void pagefault_enable(void) ++static inline void raw_pagefault_enable(void) + { + #ifndef CONFIG_PREEMPT + /* +@@ -37,6 +32,21 @@ + #endif + } + ++#ifndef CONFIG_PREEMPT_RT_FULL ++static inline void pagefault_disable(void) ++{ ++ raw_pagefault_disable(); ++} ++ ++static inline void pagefault_enable(void) ++{ ++ raw_pagefault_enable(); ++} ++#else ++extern void pagefault_disable(void); ++extern void pagefault_enable(void); ++#endif ++ + #ifndef ARCH_HAS_NOCACHE_UACCESS + + static inline unsigned long __copy_from_user_inatomic_nocache(void *to, +@@ -76,9 +86,9 @@ + mm_segment_t old_fs = get_fs(); \ + \ + set_fs(KERNEL_DS); \ +- pagefault_disable(); \ ++ raw_pagefault_disable(); \ + ret = __copy_from_user_inatomic(&(retval), (__force typeof(retval) __user *)(addr), sizeof(retval)); \ +- pagefault_enable(); \ ++ raw_pagefault_enable(); \ + set_fs(old_fs); \ + ret; \ + }) +diff -Nur linux-3.18.14.orig/include/linux/uprobes.h linux-3.18.14-rt/include/linux/uprobes.h +--- linux-3.18.14.orig/include/linux/uprobes.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/uprobes.h 2015-05-31 15:32:48.481635367 -0500 +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + struct vm_area_struct; + struct mm_struct; +diff -Nur linux-3.18.14.orig/include/linux/vmstat.h linux-3.18.14-rt/include/linux/vmstat.h +--- linux-3.18.14.orig/include/linux/vmstat.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/vmstat.h 2015-05-31 15:32:48.481635367 -0500 +@@ -33,7 +33,9 @@ + */ + static inline void __count_vm_event(enum vm_event_item item) + { ++ preempt_disable_rt(); + raw_cpu_inc(vm_event_states.event[item]); ++ preempt_enable_rt(); + } + + static inline void count_vm_event(enum vm_event_item item) +@@ -43,7 +45,9 @@ + + static inline void __count_vm_events(enum vm_event_item item, long delta) + { ++ preempt_disable_rt(); + raw_cpu_add(vm_event_states.event[item], delta); ++ preempt_enable_rt(); + } + + static inline void count_vm_events(enum vm_event_item item, long delta) +diff -Nur linux-3.18.14.orig/include/linux/wait.h linux-3.18.14-rt/include/linux/wait.h +--- linux-3.18.14.orig/include/linux/wait.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/linux/wait.h 2015-05-31 15:32:48.481635367 -0500 +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + + typedef struct __wait_queue wait_queue_t; + typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flags, void *key); +diff -Nur linux-3.18.14.orig/include/linux/wait-simple.h linux-3.18.14-rt/include/linux/wait-simple.h +--- linux-3.18.14.orig/include/linux/wait-simple.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/wait-simple.h 2015-05-31 15:32:48.481635367 -0500 +@@ -0,0 +1,207 @@ ++#ifndef _LINUX_WAIT_SIMPLE_H ++#define _LINUX_WAIT_SIMPLE_H ++ ++#include ++#include ++ ++#include ++ ++struct swaiter { ++ struct task_struct *task; ++ struct list_head node; ++}; ++ ++#define DEFINE_SWAITER(name) \ ++ struct swaiter name = { \ ++ .task = current, \ ++ .node = LIST_HEAD_INIT((name).node), \ ++ } ++ ++struct swait_head { ++ raw_spinlock_t lock; ++ struct list_head list; ++}; ++ ++#define SWAIT_HEAD_INITIALIZER(name) { \ ++ .lock = __RAW_SPIN_LOCK_UNLOCKED(name.lock), \ ++ .list = LIST_HEAD_INIT((name).list), \ ++ } ++ ++#define DEFINE_SWAIT_HEAD(name) \ ++ struct swait_head name = SWAIT_HEAD_INITIALIZER(name) ++ ++extern void __init_swait_head(struct swait_head *h, struct lock_class_key *key); ++ ++#define init_swait_head(swh) \ ++ do { \ ++ static struct lock_class_key __key; \ ++ \ ++ __init_swait_head((swh), &__key); \ ++ } while (0) ++ ++/* ++ * Waiter functions ++ */ ++extern void swait_prepare_locked(struct swait_head *head, struct swaiter *w); ++extern void swait_prepare(struct swait_head *head, struct swaiter *w, int state); ++extern void swait_finish_locked(struct swait_head *head, struct swaiter *w); ++extern void swait_finish(struct swait_head *head, struct swaiter *w); ++ ++/* Check whether a head has waiters enqueued */ ++static inline bool swaitqueue_active(struct swait_head *h) ++{ ++ /* Make sure the condition is visible before checking list_empty() */ ++ smp_mb(); ++ return !list_empty(&h->list); ++} ++ ++/* ++ * Wakeup functions ++ */ ++extern unsigned int __swait_wake(struct swait_head *head, unsigned int state, unsigned int num); ++extern unsigned int __swait_wake_locked(struct swait_head *head, unsigned int state, unsigned int num); ++ ++#define swait_wake(head) __swait_wake(head, TASK_NORMAL, 1) ++#define swait_wake_interruptible(head) __swait_wake(head, TASK_INTERRUPTIBLE, 1) ++#define swait_wake_all(head) __swait_wake(head, TASK_NORMAL, 0) ++#define swait_wake_all_interruptible(head) __swait_wake(head, TASK_INTERRUPTIBLE, 0) ++ ++/* ++ * Event API ++ */ ++#define __swait_event(wq, condition) \ ++do { \ ++ DEFINE_SWAITER(__wait); \ ++ \ ++ for (;;) { \ ++ swait_prepare(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ ++ if (condition) \ ++ break; \ ++ schedule(); \ ++ } \ ++ swait_finish(&wq, &__wait); \ ++} while (0) ++ ++/** ++ * swait_event - sleep until a condition gets true ++ * @wq: the waitqueue to wait on ++ * @condition: a C expression for the event to wait for ++ * ++ * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the ++ * @condition evaluates to true. The @condition is checked each time ++ * the waitqueue @wq is woken up. ++ * ++ * wake_up() has to be called after changing any variable that could ++ * change the result of the wait condition. ++ */ ++#define swait_event(wq, condition) \ ++do { \ ++ if (condition) \ ++ break; \ ++ __swait_event(wq, condition); \ ++} while (0) ++ ++#define __swait_event_interruptible(wq, condition, ret) \ ++do { \ ++ DEFINE_SWAITER(__wait); \ ++ \ ++ for (;;) { \ ++ swait_prepare(&wq, &__wait, TASK_INTERRUPTIBLE); \ ++ if (condition) \ ++ break; \ ++ if (signal_pending(current)) { \ ++ ret = -ERESTARTSYS; \ ++ break; \ ++ } \ ++ schedule(); \ ++ } \ ++ swait_finish(&wq, &__wait); \ ++} while (0) ++ ++#define __swait_event_interruptible_timeout(wq, condition, ret) \ ++do { \ ++ DEFINE_SWAITER(__wait); \ ++ \ ++ for (;;) { \ ++ swait_prepare(&wq, &__wait, TASK_INTERRUPTIBLE); \ ++ if (condition) \ ++ break; \ ++ if (signal_pending(current)) { \ ++ ret = -ERESTARTSYS; \ ++ break; \ ++ } \ ++ ret = schedule_timeout(ret); \ ++ if (!ret) \ ++ break; \ ++ } \ ++ swait_finish(&wq, &__wait); \ ++} while (0) ++ ++/** ++ * swait_event_interruptible - sleep until a condition gets true ++ * @wq: the waitqueue to wait on ++ * @condition: a C expression for the event to wait for ++ * ++ * The process is put to sleep (TASK_INTERRUPTIBLE) until the ++ * @condition evaluates to true. The @condition is checked each time ++ * the waitqueue @wq is woken up. ++ * ++ * wake_up() has to be called after changing any variable that could ++ * change the result of the wait condition. ++ */ ++#define swait_event_interruptible(wq, condition) \ ++({ \ ++ int __ret = 0; \ ++ if (!(condition)) \ ++ __swait_event_interruptible(wq, condition, __ret); \ ++ __ret; \ ++}) ++ ++#define swait_event_interruptible_timeout(wq, condition, timeout) \ ++({ \ ++ int __ret = timeout; \ ++ if (!(condition)) \ ++ __swait_event_interruptible_timeout(wq, condition, __ret); \ ++ __ret; \ ++}) ++ ++#define __swait_event_timeout(wq, condition, ret) \ ++do { \ ++ DEFINE_SWAITER(__wait); \ ++ \ ++ for (;;) { \ ++ swait_prepare(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ ++ if (condition) \ ++ break; \ ++ ret = schedule_timeout(ret); \ ++ if (!ret) \ ++ break; \ ++ } \ ++ swait_finish(&wq, &__wait); \ ++} while (0) ++ ++/** ++ * swait_event_timeout - sleep until a condition gets true or a timeout elapses ++ * @wq: the waitqueue to wait on ++ * @condition: a C expression for the event to wait for ++ * @timeout: timeout, in jiffies ++ * ++ * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the ++ * @condition evaluates to true. The @condition is checked each time ++ * the waitqueue @wq is woken up. ++ * ++ * wake_up() has to be called after changing any variable that could ++ * change the result of the wait condition. ++ * ++ * The function returns 0 if the @timeout elapsed, and the remaining ++ * jiffies if the condition evaluated to true before the timeout elapsed. ++ */ ++#define swait_event_timeout(wq, condition, timeout) \ ++({ \ ++ long __ret = timeout; \ ++ if (!(condition)) \ ++ __swait_event_timeout(wq, condition, __ret); \ ++ __ret; \ ++}) ++ ++#endif +diff -Nur linux-3.18.14.orig/include/linux/work-simple.h linux-3.18.14-rt/include/linux/work-simple.h +--- linux-3.18.14.orig/include/linux/work-simple.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/linux/work-simple.h 2015-05-31 15:32:48.481635367 -0500 +@@ -0,0 +1,24 @@ ++#ifndef _LINUX_SWORK_H ++#define _LINUX_SWORK_H ++ ++#include ++ ++struct swork_event { ++ struct list_head item; ++ unsigned long flags; ++ void (*func)(struct swork_event *); ++}; ++ ++static inline void INIT_SWORK(struct swork_event *event, ++ void (*func)(struct swork_event *)) ++{ ++ event->flags = 0; ++ event->func = func; ++} ++ ++bool swork_queue(struct swork_event *sev); ++ ++int swork_get(void); ++void swork_put(void); ++ ++#endif /* _LINUX_SWORK_H */ +diff -Nur linux-3.18.14.orig/include/net/dst.h linux-3.18.14-rt/include/net/dst.h +--- linux-3.18.14.orig/include/net/dst.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/net/dst.h 2015-05-31 15:32:48.497635366 -0500 +@@ -403,7 +403,7 @@ + static inline int dst_neigh_output(struct dst_entry *dst, struct neighbour *n, + struct sk_buff *skb) + { +- const struct hh_cache *hh; ++ struct hh_cache *hh; + + if (dst->pending_confirm) { + unsigned long now = jiffies; +diff -Nur linux-3.18.14.orig/include/net/neighbour.h linux-3.18.14-rt/include/net/neighbour.h +--- linux-3.18.14.orig/include/net/neighbour.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/net/neighbour.h 2015-05-31 15:32:48.521635366 -0500 +@@ -387,7 +387,7 @@ + } + #endif + +-static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb) ++static inline int neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb) + { + unsigned int seq; + int hh_len; +@@ -442,7 +442,7 @@ + + #define NEIGH_CB(skb) ((struct neighbour_cb *)(skb)->cb) + +-static inline void neigh_ha_snapshot(char *dst, const struct neighbour *n, ++static inline void neigh_ha_snapshot(char *dst, struct neighbour *n, + const struct net_device *dev) + { + unsigned int seq; +diff -Nur linux-3.18.14.orig/include/net/netns/ipv4.h linux-3.18.14-rt/include/net/netns/ipv4.h +--- linux-3.18.14.orig/include/net/netns/ipv4.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/include/net/netns/ipv4.h 2015-05-31 15:32:48.521635366 -0500 +@@ -67,6 +67,7 @@ + + int sysctl_icmp_echo_ignore_all; + int sysctl_icmp_echo_ignore_broadcasts; ++ int sysctl_icmp_echo_sysrq; + int sysctl_icmp_ignore_bogus_error_responses; + int sysctl_icmp_ratelimit; + int sysctl_icmp_ratemask; +diff -Nur linux-3.18.14.orig/include/trace/events/hist.h linux-3.18.14-rt/include/trace/events/hist.h +--- linux-3.18.14.orig/include/trace/events/hist.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/trace/events/hist.h 2015-05-31 15:32:48.521635366 -0500 +@@ -0,0 +1,72 @@ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM hist ++ ++#if !defined(_TRACE_HIST_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_HIST_H ++ ++#include "latency_hist.h" ++#include ++ ++#if !defined(CONFIG_PREEMPT_OFF_HIST) && !defined(CONFIG_INTERRUPT_OFF_HIST) ++#define trace_preemptirqsoff_hist(a, b) ++#else ++TRACE_EVENT(preemptirqsoff_hist, ++ ++ TP_PROTO(int reason, int starthist), ++ ++ TP_ARGS(reason, starthist), ++ ++ TP_STRUCT__entry( ++ __field(int, reason) ++ __field(int, starthist) ++ ), ++ ++ TP_fast_assign( ++ __entry->reason = reason; ++ __entry->starthist = starthist; ++ ), ++ ++ TP_printk("reason=%s starthist=%s", getaction(__entry->reason), ++ __entry->starthist ? "start" : "stop") ++); ++#endif ++ ++#ifndef CONFIG_MISSED_TIMER_OFFSETS_HIST ++#define trace_hrtimer_interrupt(a, b, c, d) ++#else ++TRACE_EVENT(hrtimer_interrupt, ++ ++ TP_PROTO(int cpu, long long offset, struct task_struct *curr, ++ struct task_struct *task), ++ ++ TP_ARGS(cpu, offset, curr, task), ++ ++ TP_STRUCT__entry( ++ __field(int, cpu) ++ __field(long long, offset) ++ __array(char, ccomm, TASK_COMM_LEN) ++ __field(int, cprio) ++ __array(char, tcomm, TASK_COMM_LEN) ++ __field(int, tprio) ++ ), ++ ++ TP_fast_assign( ++ __entry->cpu = cpu; ++ __entry->offset = offset; ++ memcpy(__entry->ccomm, curr->comm, TASK_COMM_LEN); ++ __entry->cprio = curr->prio; ++ memcpy(__entry->tcomm, task != NULL ? task->comm : "", ++ task != NULL ? TASK_COMM_LEN : 7); ++ __entry->tprio = task != NULL ? task->prio : -1; ++ ), ++ ++ TP_printk("cpu=%d offset=%lld curr=%s[%d] thread=%s[%d]", ++ __entry->cpu, __entry->offset, __entry->ccomm, ++ __entry->cprio, __entry->tcomm, __entry->tprio) ++); ++#endif ++ ++#endif /* _TRACE_HIST_H */ ++ ++/* This part must be outside protection */ ++#include +diff -Nur linux-3.18.14.orig/include/trace/events/latency_hist.h linux-3.18.14-rt/include/trace/events/latency_hist.h +--- linux-3.18.14.orig/include/trace/events/latency_hist.h 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/include/trace/events/latency_hist.h 2015-05-31 15:32:48.521635366 -0500 +@@ -0,0 +1,29 @@ ++#ifndef _LATENCY_HIST_H ++#define _LATENCY_HIST_H ++ ++enum hist_action { ++ IRQS_ON, ++ PREEMPT_ON, ++ TRACE_STOP, ++ IRQS_OFF, ++ PREEMPT_OFF, ++ TRACE_START, ++}; ++ ++static char *actions[] = { ++ "IRQS_ON", ++ "PREEMPT_ON", ++ "TRACE_STOP", ++ "IRQS_OFF", ++ "PREEMPT_OFF", ++ "TRACE_START", ++}; ++ ++static inline char *getaction(int action) ++{ ++ if (action >= 0 && action <= sizeof(actions)/sizeof(actions[0])) ++ return actions[action]; ++ return "unknown"; ++} ++ ++#endif /* _LATENCY_HIST_H */ +diff -Nur linux-3.18.14.orig/init/Kconfig linux-3.18.14-rt/init/Kconfig +--- linux-3.18.14.orig/init/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/init/Kconfig 2015-05-31 15:32:48.525635366 -0500 +@@ -635,7 +635,7 @@ + + config RCU_FAST_NO_HZ + bool "Accelerate last non-dyntick-idle CPU's grace periods" +- depends on NO_HZ_COMMON && SMP ++ depends on NO_HZ_COMMON && SMP && !PREEMPT_RT_FULL + default n + help + This option permits CPUs to enter dynticks-idle state even if +@@ -662,7 +662,7 @@ + config RCU_BOOST + bool "Enable RCU priority boosting" + depends on RT_MUTEXES && PREEMPT_RCU +- default n ++ default y if PREEMPT_RT_FULL + help + This option boosts the priority of preempted RCU readers that + block the current preemptible RCU grace period for too long. +@@ -1106,6 +1106,7 @@ + config RT_GROUP_SCHED + bool "Group scheduling for SCHED_RR/FIFO" + depends on CGROUP_SCHED ++ depends on !PREEMPT_RT_FULL + default n + help + This feature lets you explicitly allocate real CPU bandwidth +@@ -1677,6 +1678,7 @@ + + config SLAB + bool "SLAB" ++ depends on !PREEMPT_RT_FULL + help + The regular slab allocator that is established and known to work + well in all environments. It organizes cache hot objects in +@@ -1695,6 +1697,7 @@ + config SLOB + depends on EXPERT + bool "SLOB (Simple Allocator)" ++ depends on !PREEMPT_RT_FULL + help + SLOB replaces the stock allocator with a drastically simpler + allocator. SLOB is generally more space efficient but +diff -Nur linux-3.18.14.orig/init/main.c linux-3.18.14-rt/init/main.c +--- linux-3.18.14.orig/init/main.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/init/main.c 2015-05-31 15:32:48.545635366 -0500 +@@ -533,6 +533,7 @@ + setup_command_line(command_line); + setup_nr_cpu_ids(); + setup_per_cpu_areas(); ++ softirq_early_init(); + smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ + + build_all_zonelists(NULL, NULL); +diff -Nur linux-3.18.14.orig/init/Makefile linux-3.18.14-rt/init/Makefile +--- linux-3.18.14.orig/init/Makefile 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/init/Makefile 2015-05-31 15:32:48.525635366 -0500 +@@ -33,4 +33,4 @@ + include/generated/compile.h: FORCE + @$($(quiet)chk_compile.h) + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkcompile_h $@ \ +- "$(UTS_MACHINE)" "$(CONFIG_SMP)" "$(CONFIG_PREEMPT)" "$(CC) $(KBUILD_CFLAGS)" ++ "$(UTS_MACHINE)" "$(CONFIG_SMP)" "$(CONFIG_PREEMPT)" "$(CONFIG_PREEMPT_RT_FULL)" "$(CC) $(KBUILD_CFLAGS)" +diff -Nur linux-3.18.14.orig/ipc/mqueue.c linux-3.18.14-rt/ipc/mqueue.c +--- linux-3.18.14.orig/ipc/mqueue.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/ipc/mqueue.c 2015-05-31 15:32:48.557635366 -0500 +@@ -923,12 +923,17 @@ + struct msg_msg *message, + struct ext_wait_queue *receiver) + { ++ /* ++ * Keep them in one critical section for PREEMPT_RT: ++ */ ++ preempt_disable_rt(); + receiver->msg = message; + list_del(&receiver->list); + receiver->state = STATE_PENDING; + wake_up_process(receiver->task); + smp_wmb(); + receiver->state = STATE_READY; ++ preempt_enable_rt(); + } + + /* pipelined_receive() - if there is task waiting in sys_mq_timedsend() +@@ -942,13 +947,18 @@ + wake_up_interruptible(&info->wait_q); + return; + } +- if (msg_insert(sender->msg, info)) +- return; +- list_del(&sender->list); +- sender->state = STATE_PENDING; +- wake_up_process(sender->task); +- smp_wmb(); +- sender->state = STATE_READY; ++ /* ++ * Keep them in one critical section for PREEMPT_RT: ++ */ ++ preempt_disable_rt(); ++ if (!msg_insert(sender->msg, info)) { ++ list_del(&sender->list); ++ sender->state = STATE_PENDING; ++ wake_up_process(sender->task); ++ smp_wmb(); ++ sender->state = STATE_READY; ++ } ++ preempt_enable_rt(); + } + + SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, +diff -Nur linux-3.18.14.orig/ipc/msg.c linux-3.18.14-rt/ipc/msg.c +--- linux-3.18.14.orig/ipc/msg.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/ipc/msg.c 2015-05-31 15:32:48.577635366 -0500 +@@ -188,6 +188,12 @@ + struct msg_receiver *msr, *t; + + list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) { ++ /* ++ * Make sure that the wakeup doesnt preempt ++ * this CPU prematurely. (on PREEMPT_RT) ++ */ ++ preempt_disable_rt(); ++ + msr->r_msg = NULL; /* initialize expunge ordering */ + wake_up_process(msr->r_tsk); + /* +@@ -198,6 +204,8 @@ + */ + smp_mb(); + msr->r_msg = ERR_PTR(res); ++ ++ preempt_enable_rt(); + } + } + +@@ -574,6 +582,11 @@ + if (testmsg(msg, msr->r_msgtype, msr->r_mode) && + !security_msg_queue_msgrcv(msq, msg, msr->r_tsk, + msr->r_msgtype, msr->r_mode)) { ++ /* ++ * Make sure that the wakeup doesnt preempt ++ * this CPU prematurely. (on PREEMPT_RT) ++ */ ++ preempt_disable_rt(); + + list_del(&msr->r_list); + if (msr->r_maxsize < msg->m_ts) { +@@ -595,12 +608,13 @@ + */ + smp_mb(); + msr->r_msg = msg; ++ preempt_enable_rt(); + + return 1; + } ++ preempt_enable_rt(); + } + } +- + return 0; + } + +diff -Nur linux-3.18.14.orig/ipc/sem.c linux-3.18.14-rt/ipc/sem.c +--- linux-3.18.14.orig/ipc/sem.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/ipc/sem.c 2015-05-31 15:32:48.577635366 -0500 +@@ -673,6 +673,13 @@ + static void wake_up_sem_queue_prepare(struct list_head *pt, + struct sem_queue *q, int error) + { ++#ifdef CONFIG_PREEMPT_RT_BASE ++ struct task_struct *p = q->sleeper; ++ get_task_struct(p); ++ q->status = error; ++ wake_up_process(p); ++ put_task_struct(p); ++#else + if (list_empty(pt)) { + /* + * Hold preempt off so that we don't get preempted and have the +@@ -684,6 +691,7 @@ + q->pid = error; + + list_add_tail(&q->list, pt); ++#endif + } + + /** +@@ -697,6 +705,7 @@ + */ + static void wake_up_sem_queue_do(struct list_head *pt) + { ++#ifndef CONFIG_PREEMPT_RT_BASE + struct sem_queue *q, *t; + int did_something; + +@@ -709,6 +718,7 @@ + } + if (did_something) + preempt_enable(); ++#endif + } + + static void unlink_queue(struct sem_array *sma, struct sem_queue *q) +diff -Nur linux-3.18.14.orig/kernel/cgroup.c linux-3.18.14-rt/kernel/cgroup.c +--- linux-3.18.14.orig/kernel/cgroup.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/cgroup.c 2015-05-31 15:32:48.597635365 -0500 +@@ -4355,10 +4355,10 @@ + queue_work(cgroup_destroy_wq, &css->destroy_work); + } + +-static void css_release_work_fn(struct work_struct *work) ++static void css_release_work_fn(struct swork_event *sev) + { + struct cgroup_subsys_state *css = +- container_of(work, struct cgroup_subsys_state, destroy_work); ++ container_of(sev, struct cgroup_subsys_state, destroy_swork); + struct cgroup_subsys *ss = css->ss; + struct cgroup *cgrp = css->cgroup; + +@@ -4395,8 +4395,8 @@ + struct cgroup_subsys_state *css = + container_of(ref, struct cgroup_subsys_state, refcnt); + +- INIT_WORK(&css->destroy_work, css_release_work_fn); +- queue_work(cgroup_destroy_wq, &css->destroy_work); ++ INIT_SWORK(&css->destroy_swork, css_release_work_fn); ++ swork_queue(&css->destroy_swork); + } + + static void init_and_link_css(struct cgroup_subsys_state *css, +@@ -4997,6 +4997,7 @@ + */ + cgroup_destroy_wq = alloc_workqueue("cgroup_destroy", 0, 1); + BUG_ON(!cgroup_destroy_wq); ++ BUG_ON(swork_get()); + + /* + * Used to destroy pidlists and separate to serve as flush domain. +diff -Nur linux-3.18.14.orig/kernel/cpu.c linux-3.18.14-rt/kernel/cpu.c +--- linux-3.18.14.orig/kernel/cpu.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/cpu.c 2015-05-31 15:32:48.601635365 -0500 +@@ -86,6 +86,290 @@ + #define cpuhp_lock_acquire() lock_map_acquire(&cpu_hotplug.dep_map) + #define cpuhp_lock_release() lock_map_release(&cpu_hotplug.dep_map) + ++/** ++ * hotplug_pcp - per cpu hotplug descriptor ++ * @unplug: set when pin_current_cpu() needs to sync tasks ++ * @sync_tsk: the task that waits for tasks to finish pinned sections ++ * @refcount: counter of tasks in pinned sections ++ * @grab_lock: set when the tasks entering pinned sections should wait ++ * @synced: notifier for @sync_tsk to tell cpu_down it's finished ++ * @mutex: the mutex to make tasks wait (used when @grab_lock is true) ++ * @mutex_init: zero if the mutex hasn't been initialized yet. ++ * ++ * Although @unplug and @sync_tsk may point to the same task, the @unplug ++ * is used as a flag and still exists after @sync_tsk has exited and ++ * @sync_tsk set to NULL. ++ */ ++struct hotplug_pcp { ++ struct task_struct *unplug; ++ struct task_struct *sync_tsk; ++ int refcount; ++ int grab_lock; ++ struct completion synced; ++ struct completion unplug_wait; ++#ifdef CONFIG_PREEMPT_RT_FULL ++ /* ++ * Note, on PREEMPT_RT, the hotplug lock must save the state of ++ * the task, otherwise the mutex will cause the task to fail ++ * to sleep when required. (Because it's called from migrate_disable()) ++ * ++ * The spinlock_t on PREEMPT_RT is a mutex that saves the task's ++ * state. ++ */ ++ spinlock_t lock; ++#else ++ struct mutex mutex; ++#endif ++ int mutex_init; ++}; ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++# define hotplug_lock(hp) rt_spin_lock(&(hp)->lock) ++# define hotplug_unlock(hp) rt_spin_unlock(&(hp)->lock) ++#else ++# define hotplug_lock(hp) mutex_lock(&(hp)->mutex) ++# define hotplug_unlock(hp) mutex_unlock(&(hp)->mutex) ++#endif ++ ++static DEFINE_PER_CPU(struct hotplug_pcp, hotplug_pcp); ++ ++/** ++ * pin_current_cpu - Prevent the current cpu from being unplugged ++ * ++ * Lightweight version of get_online_cpus() to prevent cpu from being ++ * unplugged when code runs in a migration disabled region. ++ * ++ * Must be called with preemption disabled (preempt_count = 1)! ++ */ ++void pin_current_cpu(void) ++{ ++ struct hotplug_pcp *hp; ++ int force = 0; ++ ++retry: ++ hp = &__get_cpu_var(hotplug_pcp); ++ ++ if (!hp->unplug || hp->refcount || force || preempt_count() > 1 || ++ hp->unplug == current) { ++ hp->refcount++; ++ return; ++ } ++ if (hp->grab_lock) { ++ preempt_enable(); ++ hotplug_lock(hp); ++ hotplug_unlock(hp); ++ } else { ++ preempt_enable(); ++ /* ++ * Try to push this task off of this CPU. ++ */ ++ if (!migrate_me()) { ++ preempt_disable(); ++ hp = &__get_cpu_var(hotplug_pcp); ++ if (!hp->grab_lock) { ++ /* ++ * Just let it continue it's already pinned ++ * or about to sleep. ++ */ ++ force = 1; ++ goto retry; ++ } ++ preempt_enable(); ++ } ++ } ++ preempt_disable(); ++ goto retry; ++} ++ ++/** ++ * unpin_current_cpu - Allow unplug of current cpu ++ * ++ * Must be called with preemption or interrupts disabled! ++ */ ++void unpin_current_cpu(void) ++{ ++ struct hotplug_pcp *hp = &__get_cpu_var(hotplug_pcp); ++ ++ WARN_ON(hp->refcount <= 0); ++ ++ /* This is safe. sync_unplug_thread is pinned to this cpu */ ++ if (!--hp->refcount && hp->unplug && hp->unplug != current) ++ wake_up_process(hp->unplug); ++} ++ ++static void wait_for_pinned_cpus(struct hotplug_pcp *hp) ++{ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ while (hp->refcount) { ++ schedule_preempt_disabled(); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ } ++} ++ ++static int sync_unplug_thread(void *data) ++{ ++ struct hotplug_pcp *hp = data; ++ ++ wait_for_completion(&hp->unplug_wait); ++ preempt_disable(); ++ hp->unplug = current; ++ wait_for_pinned_cpus(hp); ++ ++ /* ++ * This thread will synchronize the cpu_down() with threads ++ * that have pinned the CPU. When the pinned CPU count reaches ++ * zero, we inform the cpu_down code to continue to the next step. ++ */ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ preempt_enable(); ++ complete(&hp->synced); ++ ++ /* ++ * If all succeeds, the next step will need tasks to wait till ++ * the CPU is offline before continuing. To do this, the grab_lock ++ * is set and tasks going into pin_current_cpu() will block on the ++ * mutex. But we still need to wait for those that are already in ++ * pinned CPU sections. If the cpu_down() failed, the kthread_should_stop() ++ * will kick this thread out. ++ */ ++ while (!hp->grab_lock && !kthread_should_stop()) { ++ schedule(); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ } ++ ++ /* Make sure grab_lock is seen before we see a stale completion */ ++ smp_mb(); ++ ++ /* ++ * Now just before cpu_down() enters stop machine, we need to make ++ * sure all tasks that are in pinned CPU sections are out, and new ++ * tasks will now grab the lock, keeping them from entering pinned ++ * CPU sections. ++ */ ++ if (!kthread_should_stop()) { ++ preempt_disable(); ++ wait_for_pinned_cpus(hp); ++ preempt_enable(); ++ complete(&hp->synced); ++ } ++ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ while (!kthread_should_stop()) { ++ schedule(); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ } ++ set_current_state(TASK_RUNNING); ++ ++ /* ++ * Force this thread off this CPU as it's going down and ++ * we don't want any more work on this CPU. ++ */ ++ current->flags &= ~PF_NO_SETAFFINITY; ++ set_cpus_allowed_ptr(current, cpu_present_mask); ++ migrate_me(); ++ return 0; ++} ++ ++static void __cpu_unplug_sync(struct hotplug_pcp *hp) ++{ ++ wake_up_process(hp->sync_tsk); ++ wait_for_completion(&hp->synced); ++} ++ ++static void __cpu_unplug_wait(unsigned int cpu) ++{ ++ struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu); ++ ++ complete(&hp->unplug_wait); ++ wait_for_completion(&hp->synced); ++} ++ ++/* ++ * Start the sync_unplug_thread on the target cpu and wait for it to ++ * complete. ++ */ ++static int cpu_unplug_begin(unsigned int cpu) ++{ ++ struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu); ++ int err; ++ ++ /* Protected by cpu_hotplug.lock */ ++ if (!hp->mutex_init) { ++#ifdef CONFIG_PREEMPT_RT_FULL ++ spin_lock_init(&hp->lock); ++#else ++ mutex_init(&hp->mutex); ++#endif ++ hp->mutex_init = 1; ++ } ++ ++ /* Inform the scheduler to migrate tasks off this CPU */ ++ tell_sched_cpu_down_begin(cpu); ++ ++ init_completion(&hp->synced); ++ init_completion(&hp->unplug_wait); ++ ++ hp->sync_tsk = kthread_create(sync_unplug_thread, hp, "sync_unplug/%d", cpu); ++ if (IS_ERR(hp->sync_tsk)) { ++ err = PTR_ERR(hp->sync_tsk); ++ hp->sync_tsk = NULL; ++ return err; ++ } ++ kthread_bind(hp->sync_tsk, cpu); ++ ++ /* ++ * Wait for tasks to get out of the pinned sections, ++ * it's still OK if new tasks enter. Some CPU notifiers will ++ * wait for tasks that are going to enter these sections and ++ * we must not have them block. ++ */ ++ wake_up_process(hp->sync_tsk); ++ return 0; ++} ++ ++static void cpu_unplug_sync(unsigned int cpu) ++{ ++ struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu); ++ ++ init_completion(&hp->synced); ++ /* The completion needs to be initialzied before setting grab_lock */ ++ smp_wmb(); ++ ++ /* Grab the mutex before setting grab_lock */ ++ hotplug_lock(hp); ++ hp->grab_lock = 1; ++ ++ /* ++ * The CPU notifiers have been completed. ++ * Wait for tasks to get out of pinned CPU sections and have new ++ * tasks block until the CPU is completely down. ++ */ ++ __cpu_unplug_sync(hp); ++ ++ /* All done with the sync thread */ ++ kthread_stop(hp->sync_tsk); ++ hp->sync_tsk = NULL; ++} ++ ++static void cpu_unplug_done(unsigned int cpu) ++{ ++ struct hotplug_pcp *hp = &per_cpu(hotplug_pcp, cpu); ++ ++ hp->unplug = NULL; ++ /* Let all tasks know cpu unplug is finished before cleaning up */ ++ smp_wmb(); ++ ++ if (hp->sync_tsk) ++ kthread_stop(hp->sync_tsk); ++ ++ if (hp->grab_lock) { ++ hotplug_unlock(hp); ++ /* protected by cpu_hotplug.lock */ ++ hp->grab_lock = 0; ++ } ++ tell_sched_cpu_down_done(cpu); ++} ++ + void get_online_cpus(void) + { + might_sleep(); +@@ -102,6 +386,7 @@ + { + if (cpu_hotplug.active_writer == current) + return true; ++ + if (!mutex_trylock(&cpu_hotplug.lock)) + return false; + cpuhp_lock_acquire_tryread(); +@@ -349,13 +634,15 @@ + /* Requires cpu_add_remove_lock to be held */ + static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) + { +- int err, nr_calls = 0; ++ int mycpu, err, nr_calls = 0; + void *hcpu = (void *)(long)cpu; + unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0; + struct take_cpu_down_param tcd_param = { + .mod = mod, + .hcpu = hcpu, + }; ++ cpumask_var_t cpumask; ++ cpumask_var_t cpumask_org; + + if (num_online_cpus() == 1) + return -EBUSY; +@@ -363,7 +650,34 @@ + if (!cpu_online(cpu)) + return -EINVAL; + ++ /* Move the downtaker off the unplug cpu */ ++ if (!alloc_cpumask_var(&cpumask, GFP_KERNEL)) ++ return -ENOMEM; ++ if (!alloc_cpumask_var(&cpumask_org, GFP_KERNEL)) { ++ free_cpumask_var(cpumask); ++ return -ENOMEM; ++ } ++ ++ cpumask_copy(cpumask_org, tsk_cpus_allowed(current)); ++ cpumask_andnot(cpumask, cpu_online_mask, cpumask_of(cpu)); ++ set_cpus_allowed_ptr(current, cpumask); ++ free_cpumask_var(cpumask); ++ migrate_disable(); ++ mycpu = smp_processor_id(); ++ if (mycpu == cpu) { ++ printk(KERN_ERR "Yuck! Still on unplug CPU\n!"); ++ migrate_enable(); ++ err = -EBUSY; ++ goto restore_cpus; ++ } ++ migrate_enable(); ++ + cpu_hotplug_begin(); ++ err = cpu_unplug_begin(cpu); ++ if (err) { ++ printk("cpu_unplug_begin(%d) failed\n", cpu); ++ goto out_cancel; ++ } + + err = __cpu_notify(CPU_DOWN_PREPARE | mod, hcpu, -1, &nr_calls); + if (err) { +@@ -389,8 +703,12 @@ + #endif + synchronize_rcu(); + ++ __cpu_unplug_wait(cpu); + smpboot_park_threads(cpu); + ++ /* Notifiers are done. Don't let any more tasks pin this CPU. */ ++ cpu_unplug_sync(cpu); ++ + /* + * So now all preempt/rcu users must observe !cpu_active(). + */ +@@ -423,9 +741,14 @@ + check_for_tasks(cpu); + + out_release: ++ cpu_unplug_done(cpu); ++out_cancel: + cpu_hotplug_done(); + if (!err) + cpu_notify_nofail(CPU_POST_DEAD | mod, hcpu); ++restore_cpus: ++ set_cpus_allowed_ptr(current, cpumask_org); ++ free_cpumask_var(cpumask_org); + return err; + } + +diff -Nur linux-3.18.14.orig/kernel/debug/kdb/kdb_io.c linux-3.18.14-rt/kernel/debug/kdb/kdb_io.c +--- linux-3.18.14.orig/kernel/debug/kdb/kdb_io.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/debug/kdb/kdb_io.c 2015-05-31 15:32:48.605635366 -0500 +@@ -554,7 +554,6 @@ + int linecount; + int colcount; + int logging, saved_loglevel = 0; +- int saved_trap_printk; + int got_printf_lock = 0; + int retlen = 0; + int fnd, len; +@@ -565,8 +564,6 @@ + unsigned long uninitialized_var(flags); + + preempt_disable(); +- saved_trap_printk = kdb_trap_printk; +- kdb_trap_printk = 0; + + /* Serialize kdb_printf if multiple cpus try to write at once. + * But if any cpu goes recursive in kdb, just print the output, +@@ -833,7 +830,6 @@ + } else { + __release(kdb_printf_lock); + } +- kdb_trap_printk = saved_trap_printk; + preempt_enable(); + return retlen; + } +@@ -843,9 +839,11 @@ + va_list ap; + int r; + ++ kdb_trap_printk++; + va_start(ap, fmt); + r = vkdb_printf(fmt, ap); + va_end(ap); ++ kdb_trap_printk--; + + return r; + } +diff -Nur linux-3.18.14.orig/kernel/events/core.c linux-3.18.14-rt/kernel/events/core.c +--- linux-3.18.14.orig/kernel/events/core.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/events/core.c 2015-05-31 15:32:48.637635365 -0500 +@@ -6346,6 +6346,7 @@ + + hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hwc->hrtimer.function = perf_swevent_hrtimer; ++ hwc->hrtimer.irqsafe = 1; + + /* + * Since hrtimers have a fixed rate, we can do a static freq->period +diff -Nur linux-3.18.14.orig/kernel/exit.c linux-3.18.14-rt/kernel/exit.c +--- linux-3.18.14.orig/kernel/exit.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/exit.c 2015-05-31 15:32:48.649635365 -0500 +@@ -147,7 +147,7 @@ + * Do this under ->siglock, we can race with another thread + * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals. + */ +- flush_sigqueue(&tsk->pending); ++ flush_task_sigqueue(tsk); + tsk->sighand = NULL; + spin_unlock(&sighand->siglock); + +diff -Nur linux-3.18.14.orig/kernel/fork.c linux-3.18.14-rt/kernel/fork.c +--- linux-3.18.14.orig/kernel/fork.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/fork.c 2015-05-31 15:32:48.657635365 -0500 +@@ -97,7 +97,7 @@ + + DEFINE_PER_CPU(unsigned long, process_counts) = 0; + +-__cacheline_aligned DEFINE_RWLOCK(tasklist_lock); /* outer */ ++DEFINE_RWLOCK(tasklist_lock); /* outer */ + + #ifdef CONFIG_PROVE_RCU + int lockdep_tasklist_lock_is_held(void) +@@ -233,7 +233,9 @@ + if (atomic_dec_and_test(&sig->sigcnt)) + free_signal_struct(sig); + } +- ++#ifdef CONFIG_PREEMPT_RT_BASE ++static ++#endif + void __put_task_struct(struct task_struct *tsk) + { + WARN_ON(!tsk->exit_state); +@@ -249,7 +251,18 @@ + if (!profile_handoff_task(tsk)) + free_task(tsk); + } ++#ifndef CONFIG_PREEMPT_RT_BASE + EXPORT_SYMBOL_GPL(__put_task_struct); ++#else ++void __put_task_struct_cb(struct rcu_head *rhp) ++{ ++ struct task_struct *tsk = container_of(rhp, struct task_struct, put_rcu); ++ ++ __put_task_struct(tsk); ++ ++} ++EXPORT_SYMBOL_GPL(__put_task_struct_cb); ++#endif + + void __init __weak arch_task_cache_init(void) { } + +@@ -643,6 +656,19 @@ + } + EXPORT_SYMBOL_GPL(__mmdrop); + ++#ifdef CONFIG_PREEMPT_RT_BASE ++/* ++ * RCU callback for delayed mm drop. Not strictly rcu, but we don't ++ * want another facility to make this work. ++ */ ++void __mmdrop_delayed(struct rcu_head *rhp) ++{ ++ struct mm_struct *mm = container_of(rhp, struct mm_struct, delayed_drop); ++ ++ __mmdrop(mm); ++} ++#endif ++ + /* + * Decrement the use count and release all resources for an mm. + */ +@@ -1157,6 +1183,9 @@ + */ + static void posix_cpu_timers_init(struct task_struct *tsk) + { ++#ifdef CONFIG_PREEMPT_RT_BASE ++ tsk->posix_timer_list = NULL; ++#endif + tsk->cputime_expires.prof_exp = 0; + tsk->cputime_expires.virt_exp = 0; + tsk->cputime_expires.sched_exp = 0; +@@ -1284,6 +1313,7 @@ + spin_lock_init(&p->alloc_lock); + + init_sigpending(&p->pending); ++ p->sigqueue_cache = NULL; + + p->utime = p->stime = p->gtime = 0; + p->utimescaled = p->stimescaled = 0; +@@ -1291,7 +1321,8 @@ + p->prev_cputime.utime = p->prev_cputime.stime = 0; + #endif + #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN +- seqlock_init(&p->vtime_seqlock); ++ raw_spin_lock_init(&p->vtime_lock); ++ seqcount_init(&p->vtime_seq); + p->vtime_snap = 0; + p->vtime_snap_whence = VTIME_SLEEPING; + #endif +@@ -1342,6 +1373,9 @@ + p->hardirq_context = 0; + p->softirq_context = 0; + #endif ++#ifdef CONFIG_PREEMPT_RT_FULL ++ p->pagefault_disabled = 0; ++#endif + #ifdef CONFIG_LOCKDEP + p->lockdep_depth = 0; /* no locks held yet */ + p->curr_chain_key = 0; +diff -Nur linux-3.18.14.orig/kernel/futex.c linux-3.18.14-rt/kernel/futex.c +--- linux-3.18.14.orig/kernel/futex.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/futex.c 2015-05-31 15:32:48.665635365 -0500 +@@ -738,7 +738,9 @@ + * task still owns the PI-state: + */ + if (head->next != next) { ++ raw_spin_unlock_irq(&curr->pi_lock); + spin_unlock(&hb->lock); ++ raw_spin_lock_irq(&curr->pi_lock); + continue; + } + +@@ -1705,6 +1707,16 @@ + requeue_pi_wake_futex(this, &key2, hb2); + drop_count++; + continue; ++ } else if (ret == -EAGAIN) { ++ /* ++ * Waiter was woken by timeout or ++ * signal and has set pi_blocked_on to ++ * PI_WAKEUP_INPROGRESS before we ++ * tried to enqueue it on the rtmutex. ++ */ ++ this->pi_state = NULL; ++ free_pi_state(pi_state); ++ continue; + } else if (ret) { + /* -EDEADLK */ + this->pi_state = NULL; +@@ -2549,7 +2561,7 @@ + struct hrtimer_sleeper timeout, *to = NULL; + struct rt_mutex_waiter rt_waiter; + struct rt_mutex *pi_mutex = NULL; +- struct futex_hash_bucket *hb; ++ struct futex_hash_bucket *hb, *hb2; + union futex_key key2 = FUTEX_KEY_INIT; + struct futex_q q = futex_q_init; + int res, ret; +@@ -2574,10 +2586,7 @@ + * The waiter is allocated on our stack, manipulated by the requeue + * code while we sleep on uaddr. + */ +- debug_rt_mutex_init_waiter(&rt_waiter); +- RB_CLEAR_NODE(&rt_waiter.pi_tree_entry); +- RB_CLEAR_NODE(&rt_waiter.tree_entry); +- rt_waiter.task = NULL; ++ rt_mutex_init_waiter(&rt_waiter, false); + + ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE); + if (unlikely(ret != 0)) +@@ -2608,20 +2617,55 @@ + /* Queue the futex_q, drop the hb lock, wait for wakeup. */ + futex_wait_queue_me(hb, &q, to); + +- spin_lock(&hb->lock); +- ret = handle_early_requeue_pi_wakeup(hb, &q, &key2, to); +- spin_unlock(&hb->lock); +- if (ret) +- goto out_put_keys; ++ /* ++ * On RT we must avoid races with requeue and trying to block ++ * on two mutexes (hb->lock and uaddr2's rtmutex) by ++ * serializing access to pi_blocked_on with pi_lock. ++ */ ++ raw_spin_lock_irq(¤t->pi_lock); ++ if (current->pi_blocked_on) { ++ /* ++ * We have been requeued or are in the process of ++ * being requeued. ++ */ ++ raw_spin_unlock_irq(¤t->pi_lock); ++ } else { ++ /* ++ * Setting pi_blocked_on to PI_WAKEUP_INPROGRESS ++ * prevents a concurrent requeue from moving us to the ++ * uaddr2 rtmutex. After that we can safely acquire ++ * (and possibly block on) hb->lock. ++ */ ++ current->pi_blocked_on = PI_WAKEUP_INPROGRESS; ++ raw_spin_unlock_irq(¤t->pi_lock); ++ ++ spin_lock(&hb->lock); ++ ++ /* ++ * Clean up pi_blocked_on. We might leak it otherwise ++ * when we succeeded with the hb->lock in the fast ++ * path. ++ */ ++ raw_spin_lock_irq(¤t->pi_lock); ++ current->pi_blocked_on = NULL; ++ raw_spin_unlock_irq(¤t->pi_lock); ++ ++ ret = handle_early_requeue_pi_wakeup(hb, &q, &key2, to); ++ spin_unlock(&hb->lock); ++ if (ret) ++ goto out_put_keys; ++ } + + /* +- * In order for us to be here, we know our q.key == key2, and since +- * we took the hb->lock above, we also know that futex_requeue() has +- * completed and we no longer have to concern ourselves with a wakeup +- * race with the atomic proxy lock acquisition by the requeue code. The +- * futex_requeue dropped our key1 reference and incremented our key2 +- * reference count. ++ * In order to be here, we have either been requeued, are in ++ * the process of being requeued, or requeue successfully ++ * acquired uaddr2 on our behalf. If pi_blocked_on was ++ * non-null above, we may be racing with a requeue. Do not ++ * rely on q->lock_ptr to be hb2->lock until after blocking on ++ * hb->lock or hb2->lock. The futex_requeue dropped our key1 ++ * reference and incremented our key2 reference count. + */ ++ hb2 = hash_futex(&key2); + + /* Check if the requeue code acquired the second futex for us. */ + if (!q.rt_waiter) { +@@ -2630,9 +2674,10 @@ + * did a lock-steal - fix up the PI-state in that case. + */ + if (q.pi_state && (q.pi_state->owner != current)) { +- spin_lock(q.lock_ptr); ++ spin_lock(&hb2->lock); ++ BUG_ON(&hb2->lock != q.lock_ptr); + ret = fixup_pi_state_owner(uaddr2, &q, current); +- spin_unlock(q.lock_ptr); ++ spin_unlock(&hb2->lock); + } + } else { + /* +@@ -2645,7 +2690,8 @@ + ret = rt_mutex_finish_proxy_lock(pi_mutex, to, &rt_waiter); + debug_rt_mutex_free_waiter(&rt_waiter); + +- spin_lock(q.lock_ptr); ++ spin_lock(&hb2->lock); ++ BUG_ON(&hb2->lock != q.lock_ptr); + /* + * Fixup the pi_state owner and possibly acquire the lock if we + * haven't already. +diff -Nur linux-3.18.14.orig/kernel/irq/handle.c linux-3.18.14-rt/kernel/irq/handle.c +--- linux-3.18.14.orig/kernel/irq/handle.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/irq/handle.c 2015-05-31 15:32:48.677635365 -0500 +@@ -133,6 +133,8 @@ + irqreturn_t + handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) + { ++ struct pt_regs *regs = get_irq_regs(); ++ u64 ip = regs ? instruction_pointer(regs) : 0; + irqreturn_t retval = IRQ_NONE; + unsigned int flags = 0, irq = desc->irq_data.irq; + +@@ -173,7 +175,11 @@ + action = action->next; + } while (action); + +- add_interrupt_randomness(irq, flags); ++#ifndef CONFIG_PREEMPT_RT_FULL ++ add_interrupt_randomness(irq, flags, ip); ++#else ++ desc->random_ip = ip; ++#endif + + if (!noirqdebug) + note_interrupt(irq, desc, retval); +diff -Nur linux-3.18.14.orig/kernel/irq/manage.c linux-3.18.14-rt/kernel/irq/manage.c +--- linux-3.18.14.orig/kernel/irq/manage.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/irq/manage.c 2015-05-31 15:32:48.697635365 -0500 +@@ -22,6 +22,7 @@ + #include "internals.h" + + #ifdef CONFIG_IRQ_FORCED_THREADING ++# ifndef CONFIG_PREEMPT_RT_BASE + __read_mostly bool force_irqthreads; + + static int __init setup_forced_irqthreads(char *arg) +@@ -30,6 +31,7 @@ + return 0; + } + early_param("threadirqs", setup_forced_irqthreads); ++# endif + #endif + + static void __synchronize_hardirq(struct irq_desc *desc) +@@ -173,6 +175,62 @@ + irq_get_pending(struct cpumask *mask, struct irq_desc *desc) { } + #endif + ++#ifdef CONFIG_PREEMPT_RT_FULL ++static void _irq_affinity_notify(struct irq_affinity_notify *notify); ++static struct task_struct *set_affinity_helper; ++static LIST_HEAD(affinity_list); ++static DEFINE_RAW_SPINLOCK(affinity_list_lock); ++ ++static int set_affinity_thread(void *unused) ++{ ++ while (1) { ++ struct irq_affinity_notify *notify; ++ int empty; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ raw_spin_lock_irq(&affinity_list_lock); ++ empty = list_empty(&affinity_list); ++ raw_spin_unlock_irq(&affinity_list_lock); ++ ++ if (empty) ++ schedule(); ++ if (kthread_should_stop()) ++ break; ++ set_current_state(TASK_RUNNING); ++try_next: ++ notify = NULL; ++ ++ raw_spin_lock_irq(&affinity_list_lock); ++ if (!list_empty(&affinity_list)) { ++ notify = list_first_entry(&affinity_list, ++ struct irq_affinity_notify, list); ++ list_del_init(¬ify->list); ++ } ++ raw_spin_unlock_irq(&affinity_list_lock); ++ ++ if (!notify) ++ continue; ++ _irq_affinity_notify(notify); ++ goto try_next; ++ } ++ return 0; ++} ++ ++static void init_helper_thread(void) ++{ ++ if (set_affinity_helper) ++ return; ++ set_affinity_helper = kthread_run(set_affinity_thread, NULL, ++ "affinity-cb"); ++ WARN_ON(IS_ERR(set_affinity_helper)); ++} ++#else ++ ++static inline void init_helper_thread(void) { } ++ ++#endif ++ + int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask, + bool force) + { +@@ -211,7 +269,17 @@ + + if (desc->affinity_notify) { + kref_get(&desc->affinity_notify->kref); ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++ raw_spin_lock(&affinity_list_lock); ++ if (list_empty(&desc->affinity_notify->list)) ++ list_add_tail(&affinity_list, ++ &desc->affinity_notify->list); ++ raw_spin_unlock(&affinity_list_lock); ++ wake_up_process(set_affinity_helper); ++#else + schedule_work(&desc->affinity_notify->work); ++#endif + } + irqd_set(data, IRQD_AFFINITY_SET); + +@@ -246,10 +314,8 @@ + } + EXPORT_SYMBOL_GPL(irq_set_affinity_hint); + +-static void irq_affinity_notify(struct work_struct *work) ++static void _irq_affinity_notify(struct irq_affinity_notify *notify) + { +- struct irq_affinity_notify *notify = +- container_of(work, struct irq_affinity_notify, work); + struct irq_desc *desc = irq_to_desc(notify->irq); + cpumask_var_t cpumask; + unsigned long flags; +@@ -271,6 +337,13 @@ + kref_put(¬ify->kref, notify->release); + } + ++static void irq_affinity_notify(struct work_struct *work) ++{ ++ struct irq_affinity_notify *notify = ++ container_of(work, struct irq_affinity_notify, work); ++ _irq_affinity_notify(notify); ++} ++ + /** + * irq_set_affinity_notifier - control notification of IRQ affinity changes + * @irq: Interrupt for which to enable/disable notification +@@ -300,6 +373,8 @@ + notify->irq = irq; + kref_init(¬ify->kref); + INIT_WORK(¬ify->work, irq_affinity_notify); ++ INIT_LIST_HEAD(¬ify->list); ++ init_helper_thread(); + } + + raw_spin_lock_irqsave(&desc->lock, flags); +@@ -788,7 +863,15 @@ + local_bh_disable(); + ret = action->thread_fn(action->irq, action->dev_id); + irq_finalize_oneshot(desc, action); +- local_bh_enable(); ++ /* ++ * Interrupts which have real time requirements can be set up ++ * to avoid softirq processing in the thread handler. This is ++ * safe as these interrupts do not raise soft interrupts. ++ */ ++ if (irq_settings_no_softirq_call(desc)) ++ _local_bh_enable(); ++ else ++ local_bh_enable(); + return ret; + } + +@@ -871,6 +954,12 @@ + if (action_ret == IRQ_HANDLED) + atomic_inc(&desc->threads_handled); + ++#ifdef CONFIG_PREEMPT_RT_FULL ++ migrate_disable(); ++ add_interrupt_randomness(action->irq, 0, ++ desc->random_ip ^ (unsigned long) action); ++ migrate_enable(); ++#endif + wake_threads_waitq(desc); + } + +@@ -1184,6 +1273,9 @@ + irqd_set(&desc->irq_data, IRQD_NO_BALANCING); + } + ++ if (new->flags & IRQF_NO_SOFTIRQ_CALL) ++ irq_settings_set_no_softirq_call(desc); ++ + /* Set default affinity mask once everything is setup */ + setup_affinity(irq, desc, mask); + +diff -Nur linux-3.18.14.orig/kernel/irq/settings.h linux-3.18.14-rt/kernel/irq/settings.h +--- linux-3.18.14.orig/kernel/irq/settings.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/irq/settings.h 2015-05-31 15:32:48.697635365 -0500 +@@ -15,6 +15,7 @@ + _IRQ_NESTED_THREAD = IRQ_NESTED_THREAD, + _IRQ_PER_CPU_DEVID = IRQ_PER_CPU_DEVID, + _IRQ_IS_POLLED = IRQ_IS_POLLED, ++ _IRQ_NO_SOFTIRQ_CALL = IRQ_NO_SOFTIRQ_CALL, + _IRQF_MODIFY_MASK = IRQF_MODIFY_MASK, + }; + +@@ -28,6 +29,7 @@ + #define IRQ_NESTED_THREAD GOT_YOU_MORON + #define IRQ_PER_CPU_DEVID GOT_YOU_MORON + #define IRQ_IS_POLLED GOT_YOU_MORON ++#define IRQ_NO_SOFTIRQ_CALL GOT_YOU_MORON + #undef IRQF_MODIFY_MASK + #define IRQF_MODIFY_MASK GOT_YOU_MORON + +@@ -38,6 +40,16 @@ + desc->status_use_accessors |= (set & _IRQF_MODIFY_MASK); + } + ++static inline bool irq_settings_no_softirq_call(struct irq_desc *desc) ++{ ++ return desc->status_use_accessors & _IRQ_NO_SOFTIRQ_CALL; ++} ++ ++static inline void irq_settings_set_no_softirq_call(struct irq_desc *desc) ++{ ++ desc->status_use_accessors |= _IRQ_NO_SOFTIRQ_CALL; ++} ++ + static inline bool irq_settings_is_per_cpu(struct irq_desc *desc) + { + return desc->status_use_accessors & _IRQ_PER_CPU; +diff -Nur linux-3.18.14.orig/kernel/irq/spurious.c linux-3.18.14-rt/kernel/irq/spurious.c +--- linux-3.18.14.orig/kernel/irq/spurious.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/irq/spurious.c 2015-05-31 15:32:48.709635364 -0500 +@@ -444,6 +444,10 @@ + + static int __init irqfixup_setup(char *str) + { ++#ifdef CONFIG_PREEMPT_RT_BASE ++ pr_warn("irqfixup boot option not supported w/ CONFIG_PREEMPT_RT_BASE\n"); ++ return 1; ++#endif + irqfixup = 1; + printk(KERN_WARNING "Misrouted IRQ fixup support enabled.\n"); + printk(KERN_WARNING "This may impact system performance.\n"); +@@ -456,6 +460,10 @@ + + static int __init irqpoll_setup(char *str) + { ++#ifdef CONFIG_PREEMPT_RT_BASE ++ pr_warn("irqpoll boot option not supported w/ CONFIG_PREEMPT_RT_BASE\n"); ++ return 1; ++#endif + irqfixup = 2; + printk(KERN_WARNING "Misrouted IRQ fixup and polling support " + "enabled\n"); +diff -Nur linux-3.18.14.orig/kernel/irq_work.c linux-3.18.14-rt/kernel/irq_work.c +--- linux-3.18.14.orig/kernel/irq_work.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/irq_work.c 2015-05-31 15:32:48.713635365 -0500 +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + + +@@ -65,6 +66,8 @@ + */ + bool irq_work_queue_on(struct irq_work *work, int cpu) + { ++ struct llist_head *list; ++ + /* All work should have been flushed before going offline */ + WARN_ON_ONCE(cpu_is_offline(cpu)); + +@@ -75,7 +78,12 @@ + if (!irq_work_claim(work)) + return false; + +- if (llist_add(&work->llnode, &per_cpu(raised_list, cpu))) ++ if (IS_ENABLED(CONFIG_PREEMPT_RT_FULL) && !(work->flags & IRQ_WORK_HARD_IRQ)) ++ list = &per_cpu(lazy_list, cpu); ++ else ++ list = &per_cpu(raised_list, cpu); ++ ++ if (llist_add(&work->llnode, list)) + arch_send_call_function_single_ipi(cpu); + + return true; +@@ -86,6 +94,9 @@ + /* Enqueue the irq work @work on the current CPU */ + bool irq_work_queue(struct irq_work *work) + { ++ struct llist_head *list; ++ bool lazy_work, realtime = IS_ENABLED(CONFIG_PREEMPT_RT_FULL); ++ + /* Only queue if not already pending */ + if (!irq_work_claim(work)) + return false; +@@ -93,13 +104,15 @@ + /* Queue the entry and raise the IPI if needed. */ + preempt_disable(); + +- /* If the work is "lazy", handle it from next tick if any */ +- if (work->flags & IRQ_WORK_LAZY) { +- if (llist_add(&work->llnode, this_cpu_ptr(&lazy_list)) && +- tick_nohz_tick_stopped()) +- arch_irq_work_raise(); +- } else { +- if (llist_add(&work->llnode, this_cpu_ptr(&raised_list))) ++ lazy_work = work->flags & IRQ_WORK_LAZY; ++ ++ if (lazy_work || (realtime && !(work->flags & IRQ_WORK_HARD_IRQ))) ++ list = this_cpu_ptr(&lazy_list); ++ else ++ list = this_cpu_ptr(&raised_list); ++ ++ if (llist_add(&work->llnode, list)) { ++ if (!lazy_work || tick_nohz_tick_stopped()) + arch_irq_work_raise(); + } + +@@ -116,9 +129,8 @@ + raised = this_cpu_ptr(&raised_list); + lazy = this_cpu_ptr(&lazy_list); + +- if (llist_empty(raised) || arch_irq_work_has_interrupt()) +- if (llist_empty(lazy)) +- return false; ++ if (llist_empty(raised) && llist_empty(lazy)) ++ return false; + + /* All work should have been flushed before going offline */ + WARN_ON_ONCE(cpu_is_offline(smp_processor_id())); +@@ -132,7 +144,7 @@ + struct irq_work *work; + struct llist_node *llnode; + +- BUG_ON(!irqs_disabled()); ++ BUG_ON(!IS_ENABLED(CONFIG_PREEMPT_RT_FULL) && !irqs_disabled()); + + if (llist_empty(list)) + return; +@@ -169,17 +181,26 @@ + void irq_work_run(void) + { + irq_work_run_list(this_cpu_ptr(&raised_list)); +- irq_work_run_list(this_cpu_ptr(&lazy_list)); ++ if (IS_ENABLED(CONFIG_PREEMPT_RT_FULL)) { ++ /* ++ * NOTE: we raise softirq via IPI for safety, ++ * and execute in irq_work_tick() to move the ++ * overhead from hard to soft irq context. ++ */ ++ if (!llist_empty(this_cpu_ptr(&lazy_list))) ++ raise_softirq(TIMER_SOFTIRQ); ++ } else ++ irq_work_run_list(this_cpu_ptr(&lazy_list)); + } + EXPORT_SYMBOL_GPL(irq_work_run); + + void irq_work_tick(void) + { +- struct llist_head *raised = &__get_cpu_var(raised_list); ++ struct llist_head *raised = this_cpu_ptr(&raised_list); + + if (!llist_empty(raised) && !arch_irq_work_has_interrupt()) + irq_work_run_list(raised); +- irq_work_run_list(&__get_cpu_var(lazy_list)); ++ irq_work_run_list(this_cpu_ptr(&lazy_list)); + } + + /* +diff -Nur linux-3.18.14.orig/kernel/Kconfig.locks linux-3.18.14-rt/kernel/Kconfig.locks +--- linux-3.18.14.orig/kernel/Kconfig.locks 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/Kconfig.locks 2015-05-31 15:32:48.585635365 -0500 +@@ -225,11 +225,11 @@ + + config MUTEX_SPIN_ON_OWNER + def_bool y +- depends on SMP && !DEBUG_MUTEXES && ARCH_SUPPORTS_ATOMIC_RMW ++ depends on SMP && !DEBUG_MUTEXES && ARCH_SUPPORTS_ATOMIC_RMW && !PREEMPT_RT_FULL + + config RWSEM_SPIN_ON_OWNER + def_bool y +- depends on SMP && RWSEM_XCHGADD_ALGORITHM && ARCH_SUPPORTS_ATOMIC_RMW ++ depends on SMP && RWSEM_XCHGADD_ALGORITHM && ARCH_SUPPORTS_ATOMIC_RMW && !PREEMPT_RT_FULL + + config ARCH_USE_QUEUE_RWLOCK + bool +diff -Nur linux-3.18.14.orig/kernel/Kconfig.preempt linux-3.18.14-rt/kernel/Kconfig.preempt +--- linux-3.18.14.orig/kernel/Kconfig.preempt 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/Kconfig.preempt 2015-05-31 15:32:48.589635366 -0500 +@@ -1,3 +1,16 @@ ++config PREEMPT ++ bool ++ select PREEMPT_COUNT ++ ++config PREEMPT_RT_BASE ++ bool ++ select PREEMPT ++ ++config HAVE_PREEMPT_LAZY ++ bool ++ ++config PREEMPT_LAZY ++ def_bool y if HAVE_PREEMPT_LAZY && PREEMPT_RT_FULL + + choice + prompt "Preemption Model" +@@ -33,9 +46,9 @@ + + Select this if you are building a kernel for a desktop system. + +-config PREEMPT ++config PREEMPT__LL + bool "Preemptible Kernel (Low-Latency Desktop)" +- select PREEMPT_COUNT ++ select PREEMPT + select UNINLINE_SPIN_UNLOCK if !ARCH_INLINE_SPIN_UNLOCK + help + This option reduces the latency of the kernel by making +@@ -52,6 +65,22 @@ + embedded system with latency requirements in the milliseconds + range. + ++config PREEMPT_RTB ++ bool "Preemptible Kernel (Basic RT)" ++ select PREEMPT_RT_BASE ++ help ++ This option is basically the same as (Low-Latency Desktop) but ++ enables changes which are preliminary for the full preemptible ++ RT kernel. ++ ++config PREEMPT_RT_FULL ++ bool "Fully Preemptible Kernel (RT)" ++ depends on IRQ_FORCED_THREADING ++ select PREEMPT_RT_BASE ++ select PREEMPT_RCU ++ help ++ All and everything ++ + endchoice + + config PREEMPT_COUNT +diff -Nur linux-3.18.14.orig/kernel/ksysfs.c linux-3.18.14-rt/kernel/ksysfs.c +--- linux-3.18.14.orig/kernel/ksysfs.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/ksysfs.c 2015-05-31 15:32:48.733635364 -0500 +@@ -136,6 +136,15 @@ + + #endif /* CONFIG_KEXEC */ + ++#if defined(CONFIG_PREEMPT_RT_FULL) ++static ssize_t realtime_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", 1); ++} ++KERNEL_ATTR_RO(realtime); ++#endif ++ + /* whether file capabilities are enabled */ + static ssize_t fscaps_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +@@ -203,6 +212,9 @@ + &vmcoreinfo_attr.attr, + #endif + &rcu_expedited_attr.attr, ++#ifdef CONFIG_PREEMPT_RT_FULL ++ &realtime_attr.attr, ++#endif + NULL + }; + +diff -Nur linux-3.18.14.orig/kernel/locking/lglock.c linux-3.18.14-rt/kernel/locking/lglock.c +--- linux-3.18.14.orig/kernel/locking/lglock.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/locking/lglock.c 2015-05-31 15:32:48.749635364 -0500 +@@ -4,6 +4,15 @@ + #include + #include + ++#ifndef CONFIG_PREEMPT_RT_FULL ++# define lg_lock_ptr arch_spinlock_t ++# define lg_do_lock(l) arch_spin_lock(l) ++# define lg_do_unlock(l) arch_spin_unlock(l) ++#else ++# define lg_lock_ptr struct rt_mutex ++# define lg_do_lock(l) __rt_spin_lock(l) ++# define lg_do_unlock(l) __rt_spin_unlock(l) ++#endif + /* + * Note there is no uninit, so lglocks cannot be defined in + * modules (but it's fine to use them from there) +@@ -12,51 +21,60 @@ + + void lg_lock_init(struct lglock *lg, char *name) + { ++#ifdef CONFIG_PREEMPT_RT_FULL ++ int i; ++ ++ for_each_possible_cpu(i) { ++ struct rt_mutex *lock = per_cpu_ptr(lg->lock, i); ++ ++ rt_mutex_init(lock); ++ } ++#endif + LOCKDEP_INIT_MAP(&lg->lock_dep_map, name, &lg->lock_key, 0); + } + EXPORT_SYMBOL(lg_lock_init); + + void lg_local_lock(struct lglock *lg) + { +- arch_spinlock_t *lock; ++ lg_lock_ptr *lock; + +- preempt_disable(); ++ migrate_disable(); + lock_acquire_shared(&lg->lock_dep_map, 0, 0, NULL, _RET_IP_); + lock = this_cpu_ptr(lg->lock); +- arch_spin_lock(lock); ++ lg_do_lock(lock); + } + EXPORT_SYMBOL(lg_local_lock); + + void lg_local_unlock(struct lglock *lg) + { +- arch_spinlock_t *lock; ++ lg_lock_ptr *lock; + + lock_release(&lg->lock_dep_map, 1, _RET_IP_); + lock = this_cpu_ptr(lg->lock); +- arch_spin_unlock(lock); +- preempt_enable(); ++ lg_do_unlock(lock); ++ migrate_enable(); + } + EXPORT_SYMBOL(lg_local_unlock); + + void lg_local_lock_cpu(struct lglock *lg, int cpu) + { +- arch_spinlock_t *lock; ++ lg_lock_ptr *lock; + +- preempt_disable(); ++ preempt_disable_nort(); + lock_acquire_shared(&lg->lock_dep_map, 0, 0, NULL, _RET_IP_); + lock = per_cpu_ptr(lg->lock, cpu); +- arch_spin_lock(lock); ++ lg_do_lock(lock); + } + EXPORT_SYMBOL(lg_local_lock_cpu); + + void lg_local_unlock_cpu(struct lglock *lg, int cpu) + { +- arch_spinlock_t *lock; ++ lg_lock_ptr *lock; + + lock_release(&lg->lock_dep_map, 1, _RET_IP_); + lock = per_cpu_ptr(lg->lock, cpu); +- arch_spin_unlock(lock); +- preempt_enable(); ++ lg_do_unlock(lock); ++ preempt_enable_nort(); + } + EXPORT_SYMBOL(lg_local_unlock_cpu); + +@@ -64,12 +82,12 @@ + { + int i; + +- preempt_disable(); ++ preempt_disable_nort(); + lock_acquire_exclusive(&lg->lock_dep_map, 0, 0, NULL, _RET_IP_); + for_each_possible_cpu(i) { +- arch_spinlock_t *lock; ++ lg_lock_ptr *lock; + lock = per_cpu_ptr(lg->lock, i); +- arch_spin_lock(lock); ++ lg_do_lock(lock); + } + } + EXPORT_SYMBOL(lg_global_lock); +@@ -80,10 +98,35 @@ + + lock_release(&lg->lock_dep_map, 1, _RET_IP_); + for_each_possible_cpu(i) { +- arch_spinlock_t *lock; ++ lg_lock_ptr *lock; + lock = per_cpu_ptr(lg->lock, i); +- arch_spin_unlock(lock); ++ lg_do_unlock(lock); + } +- preempt_enable(); ++ preempt_enable_nort(); + } + EXPORT_SYMBOL(lg_global_unlock); ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++/* ++ * HACK: If you use this, you get to keep the pieces. ++ * Used in queue_stop_cpus_work() when stop machinery ++ * is called from inactive CPU, so we can't schedule. ++ */ ++# define lg_do_trylock_relax(l) \ ++ do { \ ++ while (!__rt_spin_trylock(l)) \ ++ cpu_relax(); \ ++ } while (0) ++ ++void lg_global_trylock_relax(struct lglock *lg) ++{ ++ int i; ++ ++ lock_acquire_exclusive(&lg->lock_dep_map, 0, 0, NULL, _RET_IP_); ++ for_each_possible_cpu(i) { ++ lg_lock_ptr *lock; ++ lock = per_cpu_ptr(lg->lock, i); ++ lg_do_trylock_relax(lock); ++ } ++} ++#endif +diff -Nur linux-3.18.14.orig/kernel/locking/lockdep.c linux-3.18.14-rt/kernel/locking/lockdep.c +--- linux-3.18.14.orig/kernel/locking/lockdep.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/locking/lockdep.c 2015-05-31 15:32:48.749635364 -0500 +@@ -3542,6 +3542,7 @@ + } + } + ++#ifndef CONFIG_PREEMPT_RT_FULL + /* + * We dont accurately track softirq state in e.g. + * hardirq contexts (such as on 4KSTACKS), so only +@@ -3556,6 +3557,7 @@ + DEBUG_LOCKS_WARN_ON(!current->softirqs_enabled); + } + } ++#endif + + if (!debug_locks) + print_irqtrace_events(current); +diff -Nur linux-3.18.14.orig/kernel/locking/Makefile linux-3.18.14-rt/kernel/locking/Makefile +--- linux-3.18.14.orig/kernel/locking/Makefile 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/locking/Makefile 2015-05-31 15:32:48.737635364 -0500 +@@ -1,5 +1,5 @@ + +-obj-y += mutex.o semaphore.o rwsem.o mcs_spinlock.o ++obj-y += semaphore.o mcs_spinlock.o + + ifdef CONFIG_FUNCTION_TRACER + CFLAGS_REMOVE_lockdep.o = -pg +@@ -8,7 +8,11 @@ + CFLAGS_REMOVE_rtmutex-debug.o = -pg + endif + ++ifneq ($(CONFIG_PREEMPT_RT_FULL),y) ++obj-y += mutex.o + obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o ++obj-y += rwsem.o ++endif + obj-$(CONFIG_LOCKDEP) += lockdep.o + ifeq ($(CONFIG_PROC_FS),y) + obj-$(CONFIG_LOCKDEP) += lockdep_proc.o +@@ -21,8 +25,11 @@ + obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o + obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o + obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o ++ifneq ($(CONFIG_PREEMPT_RT_FULL),y) + obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o + obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o ++endif + obj-$(CONFIG_PERCPU_RWSEM) += percpu-rwsem.o ++obj-$(CONFIG_PREEMPT_RT_FULL) += rt.o + obj-$(CONFIG_QUEUE_RWLOCK) += qrwlock.o + obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o +diff -Nur linux-3.18.14.orig/kernel/locking/percpu-rwsem.c linux-3.18.14-rt/kernel/locking/percpu-rwsem.c +--- linux-3.18.14.orig/kernel/locking/percpu-rwsem.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/locking/percpu-rwsem.c 2015-05-31 15:32:48.757635364 -0500 +@@ -84,8 +84,12 @@ + + down_read(&brw->rw_sem); + atomic_inc(&brw->slow_read_ctr); ++#ifdef CONFIG_PREEMPT_RT_FULL ++ up_read(&brw->rw_sem); ++#else + /* avoid up_read()->rwsem_release() */ + __up_read(&brw->rw_sem); ++#endif + } + + void percpu_up_read(struct percpu_rw_semaphore *brw) +diff -Nur linux-3.18.14.orig/kernel/locking/rt.c linux-3.18.14-rt/kernel/locking/rt.c +--- linux-3.18.14.orig/kernel/locking/rt.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/kernel/locking/rt.c 2015-05-31 15:32:48.757635364 -0500 +@@ -0,0 +1,456 @@ ++/* ++ * kernel/rt.c ++ * ++ * Real-Time Preemption Support ++ * ++ * started by Ingo Molnar: ++ * ++ * Copyright (C) 2004-2006 Red Hat, Inc., Ingo Molnar ++ * Copyright (C) 2006, Timesys Corp., Thomas Gleixner ++ * ++ * historic credit for proving that Linux spinlocks can be implemented via ++ * RT-aware mutexes goes to many people: The Pmutex project (Dirk Grambow ++ * and others) who prototyped it on 2.4 and did lots of comparative ++ * research and analysis; TimeSys, for proving that you can implement a ++ * fully preemptible kernel via the use of IRQ threading and mutexes; ++ * Bill Huey for persuasively arguing on lkml that the mutex model is the ++ * right one; and to MontaVista, who ported pmutexes to 2.6. ++ * ++ * This code is a from-scratch implementation and is not based on pmutexes, ++ * but the idea of converting spinlocks to mutexes is used here too. ++ * ++ * lock debugging, locking tree, deadlock detection: ++ * ++ * Copyright (C) 2004, LynuxWorks, Inc., Igor Manyilov, Bill Huey ++ * Released under the General Public License (GPL). ++ * ++ * Includes portions of the generic R/W semaphore implementation from: ++ * ++ * Copyright (c) 2001 David Howells (dhowells@redhat.com). ++ * - Derived partially from idea by Andrea Arcangeli ++ * - Derived also from comments by Linus ++ * ++ * Pending ownership of locks and ownership stealing: ++ * ++ * Copyright (C) 2005, Kihon Technologies Inc., Steven Rostedt ++ * ++ * (also by Steven Rostedt) ++ * - Converted single pi_lock to individual task locks. ++ * ++ * By Esben Nielsen: ++ * Doing priority inheritance with help of the scheduler. ++ * ++ * Copyright (C) 2006, Timesys Corp., Thomas Gleixner ++ * - major rework based on Esben Nielsens initial patch ++ * - replaced thread_info references by task_struct refs ++ * - removed task->pending_owner dependency ++ * - BKL drop/reacquire for semaphore style locks to avoid deadlocks ++ * in the scheduler return path as discussed with Steven Rostedt ++ * ++ * Copyright (C) 2006, Kihon Technologies Inc. ++ * Steven Rostedt ++ * - debugged and patched Thomas Gleixner's rework. ++ * - added back the cmpxchg to the rework. ++ * - turned atomic require back on for SMP. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rtmutex_common.h" ++ ++/* ++ * struct mutex functions ++ */ ++void __mutex_do_init(struct mutex *mutex, const char *name, ++ struct lock_class_key *key) ++{ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ /* ++ * Make sure we are not reinitializing a held lock: ++ */ ++ debug_check_no_locks_freed((void *)mutex, sizeof(*mutex)); ++ lockdep_init_map(&mutex->dep_map, name, key, 0); ++#endif ++ mutex->lock.save_state = 0; ++} ++EXPORT_SYMBOL(__mutex_do_init); ++ ++void __lockfunc _mutex_lock(struct mutex *lock) ++{ ++ mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_); ++ rt_mutex_lock(&lock->lock); ++} ++EXPORT_SYMBOL(_mutex_lock); ++ ++int __lockfunc _mutex_lock_interruptible(struct mutex *lock) ++{ ++ int ret; ++ ++ mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_); ++ ret = rt_mutex_lock_interruptible(&lock->lock); ++ if (ret) ++ mutex_release(&lock->dep_map, 1, _RET_IP_); ++ return ret; ++} ++EXPORT_SYMBOL(_mutex_lock_interruptible); ++ ++int __lockfunc _mutex_lock_killable(struct mutex *lock) ++{ ++ int ret; ++ ++ mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_); ++ ret = rt_mutex_lock_killable(&lock->lock); ++ if (ret) ++ mutex_release(&lock->dep_map, 1, _RET_IP_); ++ return ret; ++} ++EXPORT_SYMBOL(_mutex_lock_killable); ++ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++void __lockfunc _mutex_lock_nested(struct mutex *lock, int subclass) ++{ ++ mutex_acquire_nest(&lock->dep_map, subclass, 0, NULL, _RET_IP_); ++ rt_mutex_lock(&lock->lock); ++} ++EXPORT_SYMBOL(_mutex_lock_nested); ++ ++void __lockfunc _mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest) ++{ ++ mutex_acquire_nest(&lock->dep_map, 0, 0, nest, _RET_IP_); ++ rt_mutex_lock(&lock->lock); ++} ++EXPORT_SYMBOL(_mutex_lock_nest_lock); ++ ++int __lockfunc _mutex_lock_interruptible_nested(struct mutex *lock, int subclass) ++{ ++ int ret; ++ ++ mutex_acquire_nest(&lock->dep_map, subclass, 0, NULL, _RET_IP_); ++ ret = rt_mutex_lock_interruptible(&lock->lock); ++ if (ret) ++ mutex_release(&lock->dep_map, 1, _RET_IP_); ++ return ret; ++} ++EXPORT_SYMBOL(_mutex_lock_interruptible_nested); ++ ++int __lockfunc _mutex_lock_killable_nested(struct mutex *lock, int subclass) ++{ ++ int ret; ++ ++ mutex_acquire(&lock->dep_map, subclass, 0, _RET_IP_); ++ ret = rt_mutex_lock_killable(&lock->lock); ++ if (ret) ++ mutex_release(&lock->dep_map, 1, _RET_IP_); ++ return ret; ++} ++EXPORT_SYMBOL(_mutex_lock_killable_nested); ++#endif ++ ++int __lockfunc _mutex_trylock(struct mutex *lock) ++{ ++ int ret = rt_mutex_trylock(&lock->lock); ++ ++ if (ret) ++ mutex_acquire(&lock->dep_map, 0, 1, _RET_IP_); ++ ++ return ret; ++} ++EXPORT_SYMBOL(_mutex_trylock); ++ ++void __lockfunc _mutex_unlock(struct mutex *lock) ++{ ++ mutex_release(&lock->dep_map, 1, _RET_IP_); ++ rt_mutex_unlock(&lock->lock); ++} ++EXPORT_SYMBOL(_mutex_unlock); ++ ++/* ++ * rwlock_t functions ++ */ ++int __lockfunc rt_write_trylock(rwlock_t *rwlock) ++{ ++ int ret; ++ ++ migrate_disable(); ++ ret = rt_mutex_trylock(&rwlock->lock); ++ if (ret) ++ rwlock_acquire(&rwlock->dep_map, 0, 1, _RET_IP_); ++ else ++ migrate_enable(); ++ ++ return ret; ++} ++EXPORT_SYMBOL(rt_write_trylock); ++ ++int __lockfunc rt_write_trylock_irqsave(rwlock_t *rwlock, unsigned long *flags) ++{ ++ int ret; ++ ++ *flags = 0; ++ ret = rt_write_trylock(rwlock); ++ return ret; ++} ++EXPORT_SYMBOL(rt_write_trylock_irqsave); ++ ++int __lockfunc rt_read_trylock(rwlock_t *rwlock) ++{ ++ struct rt_mutex *lock = &rwlock->lock; ++ int ret = 1; ++ ++ /* ++ * recursive read locks succeed when current owns the lock, ++ * but not when read_depth == 0 which means that the lock is ++ * write locked. ++ */ ++ if (rt_mutex_owner(lock) != current) { ++ migrate_disable(); ++ ret = rt_mutex_trylock(lock); ++ if (ret) ++ rwlock_acquire(&rwlock->dep_map, 0, 1, _RET_IP_); ++ else ++ migrate_enable(); ++ ++ } else if (!rwlock->read_depth) { ++ ret = 0; ++ } ++ ++ if (ret) ++ rwlock->read_depth++; ++ ++ return ret; ++} ++EXPORT_SYMBOL(rt_read_trylock); ++ ++void __lockfunc rt_write_lock(rwlock_t *rwlock) ++{ ++ rwlock_acquire(&rwlock->dep_map, 0, 0, _RET_IP_); ++ migrate_disable(); ++ __rt_spin_lock(&rwlock->lock); ++} ++EXPORT_SYMBOL(rt_write_lock); ++ ++void __lockfunc rt_read_lock(rwlock_t *rwlock) ++{ ++ struct rt_mutex *lock = &rwlock->lock; ++ ++ ++ /* ++ * recursive read locks succeed when current owns the lock ++ */ ++ if (rt_mutex_owner(lock) != current) { ++ migrate_disable(); ++ rwlock_acquire(&rwlock->dep_map, 0, 0, _RET_IP_); ++ __rt_spin_lock(lock); ++ } ++ rwlock->read_depth++; ++} ++ ++EXPORT_SYMBOL(rt_read_lock); ++ ++void __lockfunc rt_write_unlock(rwlock_t *rwlock) ++{ ++ /* NOTE: we always pass in '1' for nested, for simplicity */ ++ rwlock_release(&rwlock->dep_map, 1, _RET_IP_); ++ __rt_spin_unlock(&rwlock->lock); ++ migrate_enable(); ++} ++EXPORT_SYMBOL(rt_write_unlock); ++ ++void __lockfunc rt_read_unlock(rwlock_t *rwlock) ++{ ++ /* Release the lock only when read_depth is down to 0 */ ++ if (--rwlock->read_depth == 0) { ++ rwlock_release(&rwlock->dep_map, 1, _RET_IP_); ++ __rt_spin_unlock(&rwlock->lock); ++ migrate_enable(); ++ } ++} ++EXPORT_SYMBOL(rt_read_unlock); ++ ++unsigned long __lockfunc rt_write_lock_irqsave(rwlock_t *rwlock) ++{ ++ rt_write_lock(rwlock); ++ ++ return 0; ++} ++EXPORT_SYMBOL(rt_write_lock_irqsave); ++ ++unsigned long __lockfunc rt_read_lock_irqsave(rwlock_t *rwlock) ++{ ++ rt_read_lock(rwlock); ++ ++ return 0; ++} ++EXPORT_SYMBOL(rt_read_lock_irqsave); ++ ++void __rt_rwlock_init(rwlock_t *rwlock, char *name, struct lock_class_key *key) ++{ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ /* ++ * Make sure we are not reinitializing a held lock: ++ */ ++ debug_check_no_locks_freed((void *)rwlock, sizeof(*rwlock)); ++ lockdep_init_map(&rwlock->dep_map, name, key, 0); ++#endif ++ rwlock->lock.save_state = 1; ++ rwlock->read_depth = 0; ++} ++EXPORT_SYMBOL(__rt_rwlock_init); ++ ++/* ++ * rw_semaphores ++ */ ++ ++void rt_up_write(struct rw_semaphore *rwsem) ++{ ++ rwsem_release(&rwsem->dep_map, 1, _RET_IP_); ++ rt_mutex_unlock(&rwsem->lock); ++} ++EXPORT_SYMBOL(rt_up_write); ++ ++void rt_up_read(struct rw_semaphore *rwsem) ++{ ++ rwsem_release(&rwsem->dep_map, 1, _RET_IP_); ++ if (--rwsem->read_depth == 0) ++ rt_mutex_unlock(&rwsem->lock); ++} ++EXPORT_SYMBOL(rt_up_read); ++ ++/* ++ * downgrade a write lock into a read lock ++ * - just wake up any readers at the front of the queue ++ */ ++void rt_downgrade_write(struct rw_semaphore *rwsem) ++{ ++ BUG_ON(rt_mutex_owner(&rwsem->lock) != current); ++ rwsem->read_depth = 1; ++} ++EXPORT_SYMBOL(rt_downgrade_write); ++ ++int rt_down_write_trylock(struct rw_semaphore *rwsem) ++{ ++ int ret = rt_mutex_trylock(&rwsem->lock); ++ ++ if (ret) ++ rwsem_acquire(&rwsem->dep_map, 0, 1, _RET_IP_); ++ return ret; ++} ++EXPORT_SYMBOL(rt_down_write_trylock); ++ ++void rt_down_write(struct rw_semaphore *rwsem) ++{ ++ rwsem_acquire(&rwsem->dep_map, 0, 0, _RET_IP_); ++ rt_mutex_lock(&rwsem->lock); ++} ++EXPORT_SYMBOL(rt_down_write); ++ ++void rt_down_write_nested(struct rw_semaphore *rwsem, int subclass) ++{ ++ rwsem_acquire(&rwsem->dep_map, subclass, 0, _RET_IP_); ++ rt_mutex_lock(&rwsem->lock); ++} ++EXPORT_SYMBOL(rt_down_write_nested); ++ ++void rt_down_write_nested_lock(struct rw_semaphore *rwsem, ++ struct lockdep_map *nest) ++{ ++ rwsem_acquire_nest(&rwsem->dep_map, 0, 0, nest, _RET_IP_); ++ rt_mutex_lock(&rwsem->lock); ++} ++EXPORT_SYMBOL(rt_down_write_nested_lock); ++ ++int rt_down_read_trylock(struct rw_semaphore *rwsem) ++{ ++ struct rt_mutex *lock = &rwsem->lock; ++ int ret = 1; ++ ++ /* ++ * recursive read locks succeed when current owns the rwsem, ++ * but not when read_depth == 0 which means that the rwsem is ++ * write locked. ++ */ ++ if (rt_mutex_owner(lock) != current) ++ ret = rt_mutex_trylock(&rwsem->lock); ++ else if (!rwsem->read_depth) ++ ret = 0; ++ ++ if (ret) { ++ rwsem->read_depth++; ++ rwsem_acquire(&rwsem->dep_map, 0, 1, _RET_IP_); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(rt_down_read_trylock); ++ ++static void __rt_down_read(struct rw_semaphore *rwsem, int subclass) ++{ ++ struct rt_mutex *lock = &rwsem->lock; ++ ++ rwsem_acquire_read(&rwsem->dep_map, subclass, 0, _RET_IP_); ++ ++ if (rt_mutex_owner(lock) != current) ++ rt_mutex_lock(&rwsem->lock); ++ rwsem->read_depth++; ++} ++ ++void rt_down_read(struct rw_semaphore *rwsem) ++{ ++ __rt_down_read(rwsem, 0); ++} ++EXPORT_SYMBOL(rt_down_read); ++ ++void rt_down_read_nested(struct rw_semaphore *rwsem, int subclass) ++{ ++ __rt_down_read(rwsem, subclass); ++} ++EXPORT_SYMBOL(rt_down_read_nested); ++ ++void __rt_rwsem_init(struct rw_semaphore *rwsem, const char *name, ++ struct lock_class_key *key) ++{ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ /* ++ * Make sure we are not reinitializing a held lock: ++ */ ++ debug_check_no_locks_freed((void *)rwsem, sizeof(*rwsem)); ++ lockdep_init_map(&rwsem->dep_map, name, key, 0); ++#endif ++ rwsem->read_depth = 0; ++ rwsem->lock.save_state = 0; ++} ++EXPORT_SYMBOL(__rt_rwsem_init); ++ ++/** ++ * atomic_dec_and_mutex_lock - return holding mutex if we dec to 0 ++ * @cnt: the atomic which we are to dec ++ * @lock: the mutex to return holding if we dec to 0 ++ * ++ * return true and hold lock if we dec to 0, return false otherwise ++ */ ++int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock) ++{ ++ /* dec if we can't possibly hit 0 */ ++ if (atomic_add_unless(cnt, -1, 1)) ++ return 0; ++ /* we might hit 0, so take the lock */ ++ mutex_lock(lock); ++ if (!atomic_dec_and_test(cnt)) { ++ /* when we actually did the dec, we didn't hit 0 */ ++ mutex_unlock(lock); ++ return 0; ++ } ++ /* we hit 0, and we hold the lock */ ++ return 1; ++} ++EXPORT_SYMBOL(atomic_dec_and_mutex_lock); +diff -Nur linux-3.18.14.orig/kernel/locking/rtmutex.c linux-3.18.14-rt/kernel/locking/rtmutex.c +--- linux-3.18.14.orig/kernel/locking/rtmutex.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/locking/rtmutex.c 2015-05-31 15:32:48.769635364 -0500 +@@ -7,6 +7,11 @@ + * Copyright (C) 2005-2006 Timesys Corp., Thomas Gleixner + * Copyright (C) 2005 Kihon Technologies Inc., Steven Rostedt + * Copyright (C) 2006 Esben Nielsen ++ * Adaptive Spinlocks: ++ * Copyright (C) 2008 Novell, Inc., Gregory Haskins, Sven Dietrich, ++ * and Peter Morreale, ++ * Adaptive Spinlocks simplification: ++ * Copyright (C) 2008 Red Hat, Inc., Steven Rostedt + * + * See Documentation/locking/rt-mutex-design.txt for details. + */ +@@ -16,6 +21,7 @@ + #include + #include + #include ++#include + + #include "rtmutex_common.h" + +@@ -69,6 +75,12 @@ + clear_rt_mutex_waiters(lock); + } + ++static int rt_mutex_real_waiter(struct rt_mutex_waiter *waiter) ++{ ++ return waiter && waiter != PI_WAKEUP_INPROGRESS && ++ waiter != PI_REQUEUE_INPROGRESS; ++} ++ + /* + * We can speed up the acquire/release, if the architecture + * supports cmpxchg and if there's no debugging state to be set up +@@ -333,6 +345,14 @@ + return debug_rt_mutex_detect_deadlock(waiter, chwalk); + } + ++static void rt_mutex_wake_waiter(struct rt_mutex_waiter *waiter) ++{ ++ if (waiter->savestate) ++ wake_up_lock_sleeper(waiter->task); ++ else ++ wake_up_process(waiter->task); ++} ++ + /* + * Max number of times we'll walk the boosting chain: + */ +@@ -340,7 +360,8 @@ + + static inline struct rt_mutex *task_blocked_on_lock(struct task_struct *p) + { +- return p->pi_blocked_on ? p->pi_blocked_on->lock : NULL; ++ return rt_mutex_real_waiter(p->pi_blocked_on) ? ++ p->pi_blocked_on->lock : NULL; + } + + /* +@@ -477,7 +498,7 @@ + * reached or the state of the chain has changed while we + * dropped the locks. + */ +- if (!waiter) ++ if (!rt_mutex_real_waiter(waiter)) + goto out_unlock_pi; + + /* +@@ -639,13 +660,16 @@ + * follow here. This is the end of the chain we are walking. + */ + if (!rt_mutex_owner(lock)) { ++ struct rt_mutex_waiter *lock_top_waiter; ++ + /* + * If the requeue [7] above changed the top waiter, + * then we need to wake the new top waiter up to try + * to get the lock. + */ +- if (prerequeue_top_waiter != rt_mutex_top_waiter(lock)) +- wake_up_process(rt_mutex_top_waiter(lock)->task); ++ lock_top_waiter = rt_mutex_top_waiter(lock); ++ if (prerequeue_top_waiter != lock_top_waiter) ++ rt_mutex_wake_waiter(lock_top_waiter); + raw_spin_unlock(&lock->wait_lock); + return 0; + } +@@ -738,6 +762,25 @@ + return ret; + } + ++ ++#define STEAL_NORMAL 0 ++#define STEAL_LATERAL 1 ++ ++/* ++ * Note that RT tasks are excluded from lateral-steals to prevent the ++ * introduction of an unbounded latency ++ */ ++static inline int lock_is_stealable(struct task_struct *task, ++ struct task_struct *pendowner, int mode) ++{ ++ if (mode == STEAL_NORMAL || rt_task(task)) { ++ if (task->prio >= pendowner->prio) ++ return 0; ++ } else if (task->prio > pendowner->prio) ++ return 0; ++ return 1; ++} ++ + /* + * Try to take an rt-mutex + * +@@ -748,8 +791,9 @@ + * @waiter: The waiter that is queued to the lock's wait list if the + * callsite called task_blocked_on_lock(), otherwise NULL + */ +-static int try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task, +- struct rt_mutex_waiter *waiter) ++static int __try_to_take_rt_mutex(struct rt_mutex *lock, ++ struct task_struct *task, ++ struct rt_mutex_waiter *waiter, int mode) + { + unsigned long flags; + +@@ -788,8 +832,10 @@ + * If waiter is not the highest priority waiter of + * @lock, give up. + */ +- if (waiter != rt_mutex_top_waiter(lock)) ++ if (waiter != rt_mutex_top_waiter(lock)) { ++ /* XXX lock_is_stealable() ? */ + return 0; ++ } + + /* + * We can acquire the lock. Remove the waiter from the +@@ -807,14 +853,10 @@ + * not need to be dequeued. + */ + if (rt_mutex_has_waiters(lock)) { +- /* +- * If @task->prio is greater than or equal to +- * the top waiter priority (kernel view), +- * @task lost. +- */ +- if (task->prio >= rt_mutex_top_waiter(lock)->prio) +- return 0; ++ struct task_struct *pown = rt_mutex_top_waiter(lock)->task; + ++ if (task != pown && !lock_is_stealable(task, pown, mode)) ++ return 0; + /* + * The current top waiter stays enqueued. We + * don't have to change anything in the lock +@@ -863,6 +905,369 @@ + return 1; + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++/* ++ * preemptible spin_lock functions: ++ */ ++static inline void rt_spin_lock_fastlock(struct rt_mutex *lock, ++ void (*slowfn)(struct rt_mutex *lock)) ++{ ++ might_sleep(); ++ ++ if (likely(rt_mutex_cmpxchg(lock, NULL, current))) ++ rt_mutex_deadlock_account_lock(lock, current); ++ else ++ slowfn(lock); ++} ++ ++static inline void rt_spin_lock_fastunlock(struct rt_mutex *lock, ++ void (*slowfn)(struct rt_mutex *lock)) ++{ ++ if (likely(rt_mutex_cmpxchg(lock, current, NULL))) ++ rt_mutex_deadlock_account_unlock(current); ++ else ++ slowfn(lock); ++} ++#ifdef CONFIG_SMP ++/* ++ * Note that owner is a speculative pointer and dereferencing relies ++ * on rcu_read_lock() and the check against the lock owner. ++ */ ++static int adaptive_wait(struct rt_mutex *lock, ++ struct task_struct *owner) ++{ ++ int res = 0; ++ ++ rcu_read_lock(); ++ for (;;) { ++ if (owner != rt_mutex_owner(lock)) ++ break; ++ /* ++ * Ensure that owner->on_cpu is dereferenced _after_ ++ * checking the above to be valid. ++ */ ++ barrier(); ++ if (!owner->on_cpu) { ++ res = 1; ++ break; ++ } ++ cpu_relax(); ++ } ++ rcu_read_unlock(); ++ return res; ++} ++#else ++static int adaptive_wait(struct rt_mutex *lock, ++ struct task_struct *orig_owner) ++{ ++ return 1; ++} ++#endif ++ ++# define pi_lock(lock) raw_spin_lock_irq(lock) ++# define pi_unlock(lock) raw_spin_unlock_irq(lock) ++ ++static int task_blocks_on_rt_mutex(struct rt_mutex *lock, ++ struct rt_mutex_waiter *waiter, ++ struct task_struct *task, ++ enum rtmutex_chainwalk chwalk); ++/* ++ * Slow path lock function spin_lock style: this variant is very ++ * careful not to miss any non-lock wakeups. ++ * ++ * We store the current state under p->pi_lock in p->saved_state and ++ * the try_to_wake_up() code handles this accordingly. ++ */ ++static void noinline __sched rt_spin_lock_slowlock(struct rt_mutex *lock) ++{ ++ struct task_struct *lock_owner, *self = current; ++ struct rt_mutex_waiter waiter, *top_waiter; ++ int ret; ++ ++ rt_mutex_init_waiter(&waiter, true); ++ ++ raw_spin_lock(&lock->wait_lock); ++ ++ if (__try_to_take_rt_mutex(lock, self, NULL, STEAL_LATERAL)) { ++ raw_spin_unlock(&lock->wait_lock); ++ return; ++ } ++ ++ BUG_ON(rt_mutex_owner(lock) == self); ++ ++ /* ++ * We save whatever state the task is in and we'll restore it ++ * after acquiring the lock taking real wakeups into account ++ * as well. We are serialized via pi_lock against wakeups. See ++ * try_to_wake_up(). ++ */ ++ pi_lock(&self->pi_lock); ++ self->saved_state = self->state; ++ __set_current_state(TASK_UNINTERRUPTIBLE); ++ pi_unlock(&self->pi_lock); ++ ++ ret = task_blocks_on_rt_mutex(lock, &waiter, self, 0); ++ BUG_ON(ret); ++ ++ for (;;) { ++ /* Try to acquire the lock again. */ ++ if (__try_to_take_rt_mutex(lock, self, &waiter, STEAL_LATERAL)) ++ break; ++ ++ top_waiter = rt_mutex_top_waiter(lock); ++ lock_owner = rt_mutex_owner(lock); ++ ++ raw_spin_unlock(&lock->wait_lock); ++ ++ debug_rt_mutex_print_deadlock(&waiter); ++ ++ if (top_waiter != &waiter || adaptive_wait(lock, lock_owner)) ++ schedule_rt_mutex(lock); ++ ++ raw_spin_lock(&lock->wait_lock); ++ ++ pi_lock(&self->pi_lock); ++ __set_current_state(TASK_UNINTERRUPTIBLE); ++ pi_unlock(&self->pi_lock); ++ } ++ ++ /* ++ * Restore the task state to current->saved_state. We set it ++ * to the original state above and the try_to_wake_up() code ++ * has possibly updated it when a real (non-rtmutex) wakeup ++ * happened while we were blocked. Clear saved_state so ++ * try_to_wakeup() does not get confused. ++ */ ++ pi_lock(&self->pi_lock); ++ __set_current_state(self->saved_state); ++ self->saved_state = TASK_RUNNING; ++ pi_unlock(&self->pi_lock); ++ ++ /* ++ * try_to_take_rt_mutex() sets the waiter bit ++ * unconditionally. We might have to fix that up: ++ */ ++ fixup_rt_mutex_waiters(lock); ++ ++ BUG_ON(rt_mutex_has_waiters(lock) && &waiter == rt_mutex_top_waiter(lock)); ++ BUG_ON(!RB_EMPTY_NODE(&waiter.tree_entry)); ++ ++ raw_spin_unlock(&lock->wait_lock); ++ ++ debug_rt_mutex_free_waiter(&waiter); ++} ++ ++static void wakeup_next_waiter(struct rt_mutex *lock); ++/* ++ * Slow path to release a rt_mutex spin_lock style ++ */ ++static void __sched __rt_spin_lock_slowunlock(struct rt_mutex *lock) ++{ ++ debug_rt_mutex_unlock(lock); ++ ++ rt_mutex_deadlock_account_unlock(current); ++ ++ if (!rt_mutex_has_waiters(lock)) { ++ lock->owner = NULL; ++ raw_spin_unlock(&lock->wait_lock); ++ return; ++ } ++ ++ wakeup_next_waiter(lock); ++ ++ raw_spin_unlock(&lock->wait_lock); ++ ++ /* Undo pi boosting.when necessary */ ++ rt_mutex_adjust_prio(current); ++} ++ ++static void noinline __sched rt_spin_lock_slowunlock(struct rt_mutex *lock) ++{ ++ raw_spin_lock(&lock->wait_lock); ++ __rt_spin_lock_slowunlock(lock); ++} ++ ++static void noinline __sched rt_spin_lock_slowunlock_hirq(struct rt_mutex *lock) ++{ ++ int ret; ++ ++ do { ++ ret = raw_spin_trylock(&lock->wait_lock); ++ } while (!ret); ++ ++ __rt_spin_lock_slowunlock(lock); ++} ++ ++void __lockfunc rt_spin_lock(spinlock_t *lock) ++{ ++ rt_spin_lock_fastlock(&lock->lock, rt_spin_lock_slowlock); ++ spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); ++} ++EXPORT_SYMBOL(rt_spin_lock); ++ ++void __lockfunc __rt_spin_lock(struct rt_mutex *lock) ++{ ++ rt_spin_lock_fastlock(lock, rt_spin_lock_slowlock); ++} ++EXPORT_SYMBOL(__rt_spin_lock); ++ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++void __lockfunc rt_spin_lock_nested(spinlock_t *lock, int subclass) ++{ ++ rt_spin_lock_fastlock(&lock->lock, rt_spin_lock_slowlock); ++ spin_acquire(&lock->dep_map, subclass, 0, _RET_IP_); ++} ++EXPORT_SYMBOL(rt_spin_lock_nested); ++#endif ++ ++void __lockfunc rt_spin_unlock(spinlock_t *lock) ++{ ++ /* NOTE: we always pass in '1' for nested, for simplicity */ ++ spin_release(&lock->dep_map, 1, _RET_IP_); ++ rt_spin_lock_fastunlock(&lock->lock, rt_spin_lock_slowunlock); ++} ++EXPORT_SYMBOL(rt_spin_unlock); ++ ++void __lockfunc rt_spin_unlock_after_trylock_in_irq(spinlock_t *lock) ++{ ++ /* NOTE: we always pass in '1' for nested, for simplicity */ ++ spin_release(&lock->dep_map, 1, _RET_IP_); ++ rt_spin_lock_fastunlock(&lock->lock, rt_spin_lock_slowunlock_hirq); ++} ++ ++void __lockfunc __rt_spin_unlock(struct rt_mutex *lock) ++{ ++ rt_spin_lock_fastunlock(lock, rt_spin_lock_slowunlock); ++} ++EXPORT_SYMBOL(__rt_spin_unlock); ++ ++/* ++ * Wait for the lock to get unlocked: instead of polling for an unlock ++ * (like raw spinlocks do), we lock and unlock, to force the kernel to ++ * schedule if there's contention: ++ */ ++void __lockfunc rt_spin_unlock_wait(spinlock_t *lock) ++{ ++ spin_lock(lock); ++ spin_unlock(lock); ++} ++EXPORT_SYMBOL(rt_spin_unlock_wait); ++ ++int __lockfunc __rt_spin_trylock(struct rt_mutex *lock) ++{ ++ return rt_mutex_trylock(lock); ++} ++ ++int __lockfunc rt_spin_trylock(spinlock_t *lock) ++{ ++ int ret = rt_mutex_trylock(&lock->lock); ++ ++ if (ret) ++ spin_acquire(&lock->dep_map, 0, 1, _RET_IP_); ++ return ret; ++} ++EXPORT_SYMBOL(rt_spin_trylock); ++ ++int __lockfunc rt_spin_trylock_bh(spinlock_t *lock) ++{ ++ int ret; ++ ++ local_bh_disable(); ++ ret = rt_mutex_trylock(&lock->lock); ++ if (ret) { ++ migrate_disable(); ++ spin_acquire(&lock->dep_map, 0, 1, _RET_IP_); ++ } else ++ local_bh_enable(); ++ return ret; ++} ++EXPORT_SYMBOL(rt_spin_trylock_bh); ++ ++int __lockfunc rt_spin_trylock_irqsave(spinlock_t *lock, unsigned long *flags) ++{ ++ int ret; ++ ++ *flags = 0; ++ ret = rt_mutex_trylock(&lock->lock); ++ if (ret) { ++ migrate_disable(); ++ spin_acquire(&lock->dep_map, 0, 1, _RET_IP_); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(rt_spin_trylock_irqsave); ++ ++int atomic_dec_and_spin_lock(atomic_t *atomic, spinlock_t *lock) ++{ ++ /* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */ ++ if (atomic_add_unless(atomic, -1, 1)) ++ return 0; ++ migrate_disable(); ++ rt_spin_lock(lock); ++ if (atomic_dec_and_test(atomic)) ++ return 1; ++ rt_spin_unlock(lock); ++ migrate_enable(); ++ return 0; ++} ++EXPORT_SYMBOL(atomic_dec_and_spin_lock); ++ ++ void ++__rt_spin_lock_init(spinlock_t *lock, char *name, struct lock_class_key *key) ++{ ++#ifdef CONFIG_DEBUG_LOCK_ALLOC ++ /* ++ * Make sure we are not reinitializing a held lock: ++ */ ++ debug_check_no_locks_freed((void *)lock, sizeof(*lock)); ++ lockdep_init_map(&lock->dep_map, name, key, 0); ++#endif ++} ++EXPORT_SYMBOL(__rt_spin_lock_init); ++ ++#endif /* PREEMPT_RT_FULL */ ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++ static inline int __sched ++__mutex_lock_check_stamp(struct rt_mutex *lock, struct ww_acquire_ctx *ctx) ++{ ++ struct ww_mutex *ww = container_of(lock, struct ww_mutex, base.lock); ++ struct ww_acquire_ctx *hold_ctx = ACCESS_ONCE(ww->ctx); ++ ++ if (!hold_ctx) ++ return 0; ++ ++ if (unlikely(ctx == hold_ctx)) ++ return -EALREADY; ++ ++ if (ctx->stamp - hold_ctx->stamp <= LONG_MAX && ++ (ctx->stamp != hold_ctx->stamp || ctx > hold_ctx)) { ++#ifdef CONFIG_DEBUG_MUTEXES ++ DEBUG_LOCKS_WARN_ON(ctx->contending_lock); ++ ctx->contending_lock = ww; ++#endif ++ return -EDEADLK; ++ } ++ ++ return 0; ++} ++#else ++ static inline int __sched ++__mutex_lock_check_stamp(struct rt_mutex *lock, struct ww_acquire_ctx *ctx) ++{ ++ BUG(); ++ return 0; ++} ++ ++#endif ++ ++static inline int ++try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task, ++ struct rt_mutex_waiter *waiter) ++{ ++ return __try_to_take_rt_mutex(lock, task, waiter, STEAL_NORMAL); ++} ++ + /* + * Task blocks on lock. + * +@@ -894,6 +1299,23 @@ + return -EDEADLK; + + raw_spin_lock_irqsave(&task->pi_lock, flags); ++ ++ /* ++ * In the case of futex requeue PI, this will be a proxy ++ * lock. The task will wake unaware that it is enqueueed on ++ * this lock. Avoid blocking on two locks and corrupting ++ * pi_blocked_on via the PI_WAKEUP_INPROGRESS ++ * flag. futex_wait_requeue_pi() sets this when it wakes up ++ * before requeue (due to a signal or timeout). Do not enqueue ++ * the task if PI_WAKEUP_INPROGRESS is set. ++ */ ++ if (task != current && task->pi_blocked_on == PI_WAKEUP_INPROGRESS) { ++ raw_spin_unlock_irqrestore(&task->pi_lock, flags); ++ return -EAGAIN; ++ } ++ ++ BUG_ON(rt_mutex_real_waiter(task->pi_blocked_on)); ++ + __rt_mutex_adjust_prio(task); + waiter->task = task; + waiter->lock = lock; +@@ -917,7 +1339,7 @@ + rt_mutex_enqueue_pi(owner, waiter); + + __rt_mutex_adjust_prio(owner); +- if (owner->pi_blocked_on) ++ if (rt_mutex_real_waiter(owner->pi_blocked_on)) + chain_walk = 1; + } else if (rt_mutex_cond_detect_deadlock(waiter, chwalk)) { + chain_walk = 1; +@@ -994,7 +1416,7 @@ + * long as we hold lock->wait_lock. The waiter task needs to + * acquire it in order to dequeue the waiter. + */ +- wake_up_process(waiter->task); ++ rt_mutex_wake_waiter(waiter); + } + + /* +@@ -1008,7 +1430,7 @@ + { + bool is_top_waiter = (waiter == rt_mutex_top_waiter(lock)); + struct task_struct *owner = rt_mutex_owner(lock); +- struct rt_mutex *next_lock; ++ struct rt_mutex *next_lock = NULL; + unsigned long flags; + + raw_spin_lock_irqsave(¤t->pi_lock, flags); +@@ -1033,7 +1455,8 @@ + __rt_mutex_adjust_prio(owner); + + /* Store the lock on which owner is blocked or NULL */ +- next_lock = task_blocked_on_lock(owner); ++ if (rt_mutex_real_waiter(owner->pi_blocked_on)) ++ next_lock = task_blocked_on_lock(owner); + + raw_spin_unlock_irqrestore(&owner->pi_lock, flags); + +@@ -1069,17 +1492,17 @@ + raw_spin_lock_irqsave(&task->pi_lock, flags); + + waiter = task->pi_blocked_on; +- if (!waiter || (waiter->prio == task->prio && ++ if (!rt_mutex_real_waiter(waiter) || (waiter->prio == task->prio && + !dl_prio(task->prio))) { + raw_spin_unlock_irqrestore(&task->pi_lock, flags); + return; + } + next_lock = waiter->lock; +- raw_spin_unlock_irqrestore(&task->pi_lock, flags); + + /* gets dropped in rt_mutex_adjust_prio_chain()! */ + get_task_struct(task); + ++ raw_spin_unlock_irqrestore(&task->pi_lock, flags); + rt_mutex_adjust_prio_chain(task, RT_MUTEX_MIN_CHAINWALK, NULL, + next_lock, NULL, task); + } +@@ -1097,7 +1520,8 @@ + static int __sched + __rt_mutex_slowlock(struct rt_mutex *lock, int state, + struct hrtimer_sleeper *timeout, +- struct rt_mutex_waiter *waiter) ++ struct rt_mutex_waiter *waiter, ++ struct ww_acquire_ctx *ww_ctx) + { + int ret = 0; + +@@ -1120,6 +1544,12 @@ + break; + } + ++ if (ww_ctx && ww_ctx->acquired > 0) { ++ ret = __mutex_lock_check_stamp(lock, ww_ctx); ++ if (ret) ++ break; ++ } ++ + raw_spin_unlock(&lock->wait_lock); + + debug_rt_mutex_print_deadlock(waiter); +@@ -1153,25 +1583,102 @@ + } + } + ++static __always_inline void ww_mutex_lock_acquired(struct ww_mutex *ww, ++ struct ww_acquire_ctx *ww_ctx) ++{ ++#ifdef CONFIG_DEBUG_MUTEXES ++ /* ++ * If this WARN_ON triggers, you used ww_mutex_lock to acquire, ++ * but released with a normal mutex_unlock in this call. ++ * ++ * This should never happen, always use ww_mutex_unlock. ++ */ ++ DEBUG_LOCKS_WARN_ON(ww->ctx); ++ ++ /* ++ * Not quite done after calling ww_acquire_done() ? ++ */ ++ DEBUG_LOCKS_WARN_ON(ww_ctx->done_acquire); ++ ++ if (ww_ctx->contending_lock) { ++ /* ++ * After -EDEADLK you tried to ++ * acquire a different ww_mutex? Bad! ++ */ ++ DEBUG_LOCKS_WARN_ON(ww_ctx->contending_lock != ww); ++ ++ /* ++ * You called ww_mutex_lock after receiving -EDEADLK, ++ * but 'forgot' to unlock everything else first? ++ */ ++ DEBUG_LOCKS_WARN_ON(ww_ctx->acquired > 0); ++ ww_ctx->contending_lock = NULL; ++ } ++ ++ /* ++ * Naughty, using a different class will lead to undefined behavior! ++ */ ++ DEBUG_LOCKS_WARN_ON(ww_ctx->ww_class != ww->ww_class); ++#endif ++ ww_ctx->acquired++; ++} ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++static void ww_mutex_account_lock(struct rt_mutex *lock, ++ struct ww_acquire_ctx *ww_ctx) ++{ ++ struct ww_mutex *ww = container_of(lock, struct ww_mutex, base.lock); ++ struct rt_mutex_waiter *waiter, *n; ++ ++ /* ++ * This branch gets optimized out for the common case, ++ * and is only important for ww_mutex_lock. ++ */ ++ ww_mutex_lock_acquired(ww, ww_ctx); ++ ww->ctx = ww_ctx; ++ ++ /* ++ * Give any possible sleeping processes the chance to wake up, ++ * so they can recheck if they have to back off. ++ */ ++ rbtree_postorder_for_each_entry_safe(waiter, n, &lock->waiters, ++ tree_entry) { ++ /* XXX debug rt mutex waiter wakeup */ ++ ++ BUG_ON(waiter->lock != lock); ++ rt_mutex_wake_waiter(waiter); ++ } ++} ++ ++#else ++ ++static void ww_mutex_account_lock(struct rt_mutex *lock, ++ struct ww_acquire_ctx *ww_ctx) ++{ ++ BUG(); ++} ++#endif ++ + /* + * Slow path lock function: + */ + static int __sched + rt_mutex_slowlock(struct rt_mutex *lock, int state, + struct hrtimer_sleeper *timeout, +- enum rtmutex_chainwalk chwalk) ++ enum rtmutex_chainwalk chwalk, ++ struct ww_acquire_ctx *ww_ctx) + { + struct rt_mutex_waiter waiter; + int ret = 0; + +- debug_rt_mutex_init_waiter(&waiter); +- RB_CLEAR_NODE(&waiter.pi_tree_entry); +- RB_CLEAR_NODE(&waiter.tree_entry); ++ rt_mutex_init_waiter(&waiter, false); + + raw_spin_lock(&lock->wait_lock); + + /* Try to acquire the lock again: */ + if (try_to_take_rt_mutex(lock, current, NULL)) { ++ if (ww_ctx) ++ ww_mutex_account_lock(lock, ww_ctx); + raw_spin_unlock(&lock->wait_lock); + return 0; + } +@@ -1188,14 +1695,23 @@ + ret = task_blocks_on_rt_mutex(lock, &waiter, current, chwalk); + + if (likely(!ret)) +- ret = __rt_mutex_slowlock(lock, state, timeout, &waiter); ++ ret = __rt_mutex_slowlock(lock, state, timeout, &waiter, ww_ctx); ++ else if (ww_ctx) { ++ /* ww_mutex received EDEADLK, let it become EALREADY */ ++ ret = __mutex_lock_check_stamp(lock, ww_ctx); ++ BUG_ON(!ret); ++ } + + set_current_state(TASK_RUNNING); + + if (unlikely(ret)) { + if (rt_mutex_has_waiters(lock)) + remove_waiter(lock, &waiter); +- rt_mutex_handle_deadlock(ret, chwalk, &waiter); ++ /* ww_mutex want to report EDEADLK/EALREADY, let them */ ++ if (!ww_ctx) ++ rt_mutex_handle_deadlock(ret, chwalk, &waiter); ++ } else if (ww_ctx) { ++ ww_mutex_account_lock(lock, ww_ctx); + } + + /* +@@ -1234,7 +1750,8 @@ + * The mutex has currently no owner. Lock the wait lock and + * try to acquire the lock. + */ +- raw_spin_lock(&lock->wait_lock); ++ if (!raw_spin_trylock(&lock->wait_lock)) ++ return 0; + + ret = try_to_take_rt_mutex(lock, current, NULL); + +@@ -1320,31 +1837,36 @@ + */ + static inline int + rt_mutex_fastlock(struct rt_mutex *lock, int state, ++ struct ww_acquire_ctx *ww_ctx, + int (*slowfn)(struct rt_mutex *lock, int state, + struct hrtimer_sleeper *timeout, +- enum rtmutex_chainwalk chwalk)) ++ enum rtmutex_chainwalk chwalk, ++ struct ww_acquire_ctx *ww_ctx)) + { + if (likely(rt_mutex_cmpxchg(lock, NULL, current))) { + rt_mutex_deadlock_account_lock(lock, current); + return 0; + } else +- return slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK); ++ return slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK, ++ ww_ctx); + } + + static inline int + rt_mutex_timed_fastlock(struct rt_mutex *lock, int state, + struct hrtimer_sleeper *timeout, + enum rtmutex_chainwalk chwalk, ++ struct ww_acquire_ctx *ww_ctx, + int (*slowfn)(struct rt_mutex *lock, int state, + struct hrtimer_sleeper *timeout, +- enum rtmutex_chainwalk chwalk)) ++ enum rtmutex_chainwalk chwalk, ++ struct ww_acquire_ctx *ww_ctx)) + { + if (chwalk == RT_MUTEX_MIN_CHAINWALK && + likely(rt_mutex_cmpxchg(lock, NULL, current))) { + rt_mutex_deadlock_account_lock(lock, current); + return 0; + } else +- return slowfn(lock, state, timeout, chwalk); ++ return slowfn(lock, state, timeout, chwalk, ww_ctx); + } + + static inline int +@@ -1377,7 +1899,7 @@ + { + might_sleep(); + +- rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, rt_mutex_slowlock); ++ rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, NULL, rt_mutex_slowlock); + } + EXPORT_SYMBOL_GPL(rt_mutex_lock); + +@@ -1394,7 +1916,7 @@ + { + might_sleep(); + +- return rt_mutex_fastlock(lock, TASK_INTERRUPTIBLE, rt_mutex_slowlock); ++ return rt_mutex_fastlock(lock, TASK_INTERRUPTIBLE, NULL, rt_mutex_slowlock); + } + EXPORT_SYMBOL_GPL(rt_mutex_lock_interruptible); + +@@ -1407,11 +1929,30 @@ + might_sleep(); + + return rt_mutex_timed_fastlock(lock, TASK_INTERRUPTIBLE, timeout, +- RT_MUTEX_FULL_CHAINWALK, ++ RT_MUTEX_FULL_CHAINWALK, NULL, + rt_mutex_slowlock); + } + + /** ++ * rt_mutex_lock_killable - lock a rt_mutex killable ++ * ++ * @lock: the rt_mutex to be locked ++ * @detect_deadlock: deadlock detection on/off ++ * ++ * Returns: ++ * 0 on success ++ * -EINTR when interrupted by a signal ++ * -EDEADLK when the lock would deadlock (when deadlock detection is on) ++ */ ++int __sched rt_mutex_lock_killable(struct rt_mutex *lock) ++{ ++ might_sleep(); ++ ++ return rt_mutex_fastlock(lock, TASK_KILLABLE, NULL, rt_mutex_slowlock); ++} ++EXPORT_SYMBOL_GPL(rt_mutex_lock_killable); ++ ++/** + * rt_mutex_timed_lock - lock a rt_mutex interruptible + * the timeout structure is provided + * by the caller +@@ -1431,6 +1972,7 @@ + + return rt_mutex_timed_fastlock(lock, TASK_INTERRUPTIBLE, timeout, + RT_MUTEX_MIN_CHAINWALK, ++ NULL, + rt_mutex_slowlock); + } + EXPORT_SYMBOL_GPL(rt_mutex_timed_lock); +@@ -1489,13 +2031,12 @@ + void __rt_mutex_init(struct rt_mutex *lock, const char *name) + { + lock->owner = NULL; +- raw_spin_lock_init(&lock->wait_lock); + lock->waiters = RB_ROOT; + lock->waiters_leftmost = NULL; + + debug_rt_mutex_init(lock, name); + } +-EXPORT_SYMBOL_GPL(__rt_mutex_init); ++EXPORT_SYMBOL(__rt_mutex_init); + + /** + * rt_mutex_init_proxy_locked - initialize and lock a rt_mutex on behalf of a +@@ -1510,7 +2051,7 @@ + void rt_mutex_init_proxy_locked(struct rt_mutex *lock, + struct task_struct *proxy_owner) + { +- __rt_mutex_init(lock, NULL); ++ rt_mutex_init(lock); + debug_rt_mutex_proxy_lock(lock, proxy_owner); + rt_mutex_set_owner(lock, proxy_owner); + rt_mutex_deadlock_account_lock(lock, proxy_owner); +@@ -1558,6 +2099,35 @@ + return 1; + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++ /* ++ * In PREEMPT_RT there's an added race. ++ * If the task, that we are about to requeue, times out, ++ * it can set the PI_WAKEUP_INPROGRESS. This tells the requeue ++ * to skip this task. But right after the task sets ++ * its pi_blocked_on to PI_WAKEUP_INPROGRESS it can then ++ * block on the spin_lock(&hb->lock), which in RT is an rtmutex. ++ * This will replace the PI_WAKEUP_INPROGRESS with the actual ++ * lock that it blocks on. We *must not* place this task ++ * on this proxy lock in that case. ++ * ++ * To prevent this race, we first take the task's pi_lock ++ * and check if it has updated its pi_blocked_on. If it has, ++ * we assume that it woke up and we return -EAGAIN. ++ * Otherwise, we set the task's pi_blocked_on to ++ * PI_REQUEUE_INPROGRESS, so that if the task is waking up ++ * it will know that we are in the process of requeuing it. ++ */ ++ raw_spin_lock_irq(&task->pi_lock); ++ if (task->pi_blocked_on) { ++ raw_spin_unlock_irq(&task->pi_lock); ++ raw_spin_unlock(&lock->wait_lock); ++ return -EAGAIN; ++ } ++ task->pi_blocked_on = PI_REQUEUE_INPROGRESS; ++ raw_spin_unlock_irq(&task->pi_lock); ++#endif ++ + /* We enforce deadlock detection for futexes */ + ret = task_blocks_on_rt_mutex(lock, waiter, task, + RT_MUTEX_FULL_CHAINWALK); +@@ -1627,7 +2197,7 @@ + + set_current_state(TASK_INTERRUPTIBLE); + +- ret = __rt_mutex_slowlock(lock, TASK_INTERRUPTIBLE, to, waiter); ++ ret = __rt_mutex_slowlock(lock, TASK_INTERRUPTIBLE, to, waiter, NULL); + + set_current_state(TASK_RUNNING); + +@@ -1644,3 +2214,89 @@ + + return ret; + } ++ ++static inline int ++ww_mutex_deadlock_injection(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) ++{ ++#ifdef CONFIG_DEBUG_WW_MUTEX_SLOWPATH ++ unsigned tmp; ++ ++ if (ctx->deadlock_inject_countdown-- == 0) { ++ tmp = ctx->deadlock_inject_interval; ++ if (tmp > UINT_MAX/4) ++ tmp = UINT_MAX; ++ else ++ tmp = tmp*2 + tmp + tmp/2; ++ ++ ctx->deadlock_inject_interval = tmp; ++ ctx->deadlock_inject_countdown = tmp; ++ ctx->contending_lock = lock; ++ ++ ww_mutex_unlock(lock); ++ ++ return -EDEADLK; ++ } ++#endif ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PREEMPT_RT_FULL ++int __sched ++__ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ww_ctx) ++{ ++ int ret; ++ ++ might_sleep(); ++ ++ mutex_acquire_nest(&lock->base.dep_map, 0, 0, &ww_ctx->dep_map, _RET_IP_); ++ ret = rt_mutex_slowlock(&lock->base.lock, TASK_INTERRUPTIBLE, NULL, 0, ww_ctx); ++ if (ret) ++ mutex_release(&lock->base.dep_map, 1, _RET_IP_); ++ else if (!ret && ww_ctx->acquired > 1) ++ return ww_mutex_deadlock_injection(lock, ww_ctx); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(__ww_mutex_lock_interruptible); ++ ++int __sched ++__ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ww_ctx) ++{ ++ int ret; ++ ++ might_sleep(); ++ ++ mutex_acquire_nest(&lock->base.dep_map, 0, 0, &ww_ctx->dep_map, _RET_IP_); ++ ret = rt_mutex_slowlock(&lock->base.lock, TASK_UNINTERRUPTIBLE, NULL, 0, ww_ctx); ++ if (ret) ++ mutex_release(&lock->base.dep_map, 1, _RET_IP_); ++ else if (!ret && ww_ctx->acquired > 1) ++ return ww_mutex_deadlock_injection(lock, ww_ctx); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(__ww_mutex_lock); ++ ++void __sched ww_mutex_unlock(struct ww_mutex *lock) ++{ ++ int nest = !!lock->ctx; ++ ++ /* ++ * The unlocking fastpath is the 0->1 transition from 'locked' ++ * into 'unlocked' state: ++ */ ++ if (nest) { ++#ifdef CONFIG_DEBUG_MUTEXES ++ DEBUG_LOCKS_WARN_ON(!lock->ctx->acquired); ++#endif ++ if (lock->ctx->acquired > 0) ++ lock->ctx->acquired--; ++ lock->ctx = NULL; ++ } ++ ++ mutex_release(&lock->base.dep_map, nest, _RET_IP_); ++ rt_mutex_unlock(&lock->base.lock); ++} ++EXPORT_SYMBOL(ww_mutex_unlock); ++#endif +diff -Nur linux-3.18.14.orig/kernel/locking/rtmutex_common.h linux-3.18.14-rt/kernel/locking/rtmutex_common.h +--- linux-3.18.14.orig/kernel/locking/rtmutex_common.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/locking/rtmutex_common.h 2015-05-31 15:32:48.769635364 -0500 +@@ -49,6 +49,7 @@ + struct rb_node pi_tree_entry; + struct task_struct *task; + struct rt_mutex *lock; ++ bool savestate; + #ifdef CONFIG_DEBUG_RT_MUTEXES + unsigned long ip; + struct pid *deadlock_task_pid; +@@ -119,6 +120,9 @@ + /* + * PI-futex support (proxy locking functions, etc.): + */ ++#define PI_WAKEUP_INPROGRESS ((struct rt_mutex_waiter *) 1) ++#define PI_REQUEUE_INPROGRESS ((struct rt_mutex_waiter *) 2) ++ + extern struct task_struct *rt_mutex_next_owner(struct rt_mutex *lock); + extern void rt_mutex_init_proxy_locked(struct rt_mutex *lock, + struct task_struct *proxy_owner); +@@ -138,4 +142,14 @@ + # include "rtmutex.h" + #endif + ++static inline void ++rt_mutex_init_waiter(struct rt_mutex_waiter *waiter, bool savestate) ++{ ++ debug_rt_mutex_init_waiter(waiter); ++ waiter->task = NULL; ++ waiter->savestate = savestate; ++ RB_CLEAR_NODE(&waiter->pi_tree_entry); ++ RB_CLEAR_NODE(&waiter->tree_entry); ++} ++ + #endif +diff -Nur linux-3.18.14.orig/kernel/locking/spinlock.c linux-3.18.14-rt/kernel/locking/spinlock.c +--- linux-3.18.14.orig/kernel/locking/spinlock.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/locking/spinlock.c 2015-05-31 15:32:48.769635364 -0500 +@@ -124,8 +124,11 @@ + * __[spin|read|write]_lock_bh() + */ + BUILD_LOCK_OPS(spin, raw_spinlock); ++ ++#ifndef CONFIG_PREEMPT_RT_FULL + BUILD_LOCK_OPS(read, rwlock); + BUILD_LOCK_OPS(write, rwlock); ++#endif + + #endif + +@@ -209,6 +212,8 @@ + EXPORT_SYMBOL(_raw_spin_unlock_bh); + #endif + ++#ifndef CONFIG_PREEMPT_RT_FULL ++ + #ifndef CONFIG_INLINE_READ_TRYLOCK + int __lockfunc _raw_read_trylock(rwlock_t *lock) + { +@@ -353,6 +358,8 @@ + EXPORT_SYMBOL(_raw_write_unlock_bh); + #endif + ++#endif /* !PREEMPT_RT_FULL */ ++ + #ifdef CONFIG_DEBUG_LOCK_ALLOC + + void __lockfunc _raw_spin_lock_nested(raw_spinlock_t *lock, int subclass) +diff -Nur linux-3.18.14.orig/kernel/locking/spinlock_debug.c linux-3.18.14-rt/kernel/locking/spinlock_debug.c +--- linux-3.18.14.orig/kernel/locking/spinlock_debug.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/locking/spinlock_debug.c 2015-05-31 15:32:48.793635364 -0500 +@@ -31,6 +31,7 @@ + + EXPORT_SYMBOL(__raw_spin_lock_init); + ++#ifndef CONFIG_PREEMPT_RT_FULL + void __rwlock_init(rwlock_t *lock, const char *name, + struct lock_class_key *key) + { +@@ -48,6 +49,7 @@ + } + + EXPORT_SYMBOL(__rwlock_init); ++#endif + + static void spin_dump(raw_spinlock_t *lock, const char *msg) + { +@@ -159,6 +161,7 @@ + arch_spin_unlock(&lock->raw_lock); + } + ++#ifndef CONFIG_PREEMPT_RT_FULL + static void rwlock_bug(rwlock_t *lock, const char *msg) + { + if (!debug_locks_off()) +@@ -300,3 +303,5 @@ + debug_write_unlock(lock); + arch_write_unlock(&lock->raw_lock); + } ++ ++#endif +diff -Nur linux-3.18.14.orig/kernel/panic.c linux-3.18.14-rt/kernel/panic.c +--- linux-3.18.14.orig/kernel/panic.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/panic.c 2015-05-31 15:32:48.793635364 -0500 +@@ -384,9 +384,11 @@ + + static int init_oops_id(void) + { ++#ifndef CONFIG_PREEMPT_RT_FULL + if (!oops_id) + get_random_bytes(&oops_id, sizeof(oops_id)); + else ++#endif + oops_id++; + + return 0; +diff -Nur linux-3.18.14.orig/kernel/power/hibernate.c linux-3.18.14-rt/kernel/power/hibernate.c +--- linux-3.18.14.orig/kernel/power/hibernate.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/power/hibernate.c 2015-05-31 15:32:48.797635364 -0500 +@@ -287,6 +287,8 @@ + + local_irq_disable(); + ++ system_state = SYSTEM_SUSPEND; ++ + error = syscore_suspend(); + if (error) { + printk(KERN_ERR "PM: Some system devices failed to power down, " +@@ -316,6 +318,7 @@ + syscore_resume(); + + Enable_irqs: ++ system_state = SYSTEM_RUNNING; + local_irq_enable(); + + Enable_cpus: +@@ -439,6 +442,7 @@ + goto Enable_cpus; + + local_irq_disable(); ++ system_state = SYSTEM_SUSPEND; + + error = syscore_suspend(); + if (error) +@@ -472,6 +476,7 @@ + syscore_resume(); + + Enable_irqs: ++ system_state = SYSTEM_RUNNING; + local_irq_enable(); + + Enable_cpus: +@@ -557,6 +562,7 @@ + goto Platform_finish; + + local_irq_disable(); ++ system_state = SYSTEM_SUSPEND; + syscore_suspend(); + if (pm_wakeup_pending()) { + error = -EAGAIN; +@@ -569,6 +575,7 @@ + + Power_up: + syscore_resume(); ++ system_state = SYSTEM_RUNNING; + local_irq_enable(); + enable_nonboot_cpus(); + +diff -Nur linux-3.18.14.orig/kernel/power/suspend.c linux-3.18.14-rt/kernel/power/suspend.c +--- linux-3.18.14.orig/kernel/power/suspend.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/power/suspend.c 2015-05-31 15:32:48.797635364 -0500 +@@ -318,6 +318,8 @@ + arch_suspend_disable_irqs(); + BUG_ON(!irqs_disabled()); + ++ system_state = SYSTEM_SUSPEND; ++ + error = syscore_suspend(); + if (!error) { + *wakeup = pm_wakeup_pending(); +@@ -332,6 +334,8 @@ + syscore_resume(); + } + ++ system_state = SYSTEM_RUNNING; ++ + arch_suspend_enable_irqs(); + BUG_ON(irqs_disabled()); + +diff -Nur linux-3.18.14.orig/kernel/printk/printk.c linux-3.18.14-rt/kernel/printk/printk.c +--- linux-3.18.14.orig/kernel/printk/printk.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/printk/printk.c 2015-05-31 15:32:48.801635363 -0500 +@@ -1165,6 +1165,7 @@ + { + char *text; + int len = 0; ++ int attempts = 0; + + text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL); + if (!text) +@@ -1176,7 +1177,14 @@ + u64 seq; + u32 idx; + enum log_flags prev; +- ++ int num_msg; ++try_again: ++ attempts++; ++ if (attempts > 10) { ++ len = -EBUSY; ++ goto out; ++ } ++ num_msg = 0; + if (clear_seq < log_first_seq) { + /* messages are gone, move to first available one */ + clear_seq = log_first_seq; +@@ -1197,6 +1205,14 @@ + prev = msg->flags; + idx = log_next(idx); + seq++; ++ num_msg++; ++ if (num_msg > 5) { ++ num_msg = 0; ++ raw_spin_unlock_irq(&logbuf_lock); ++ raw_spin_lock_irq(&logbuf_lock); ++ if (clear_seq < log_first_seq) ++ goto try_again; ++ } + } + + /* move first record forward until length fits into the buffer */ +@@ -1210,6 +1226,14 @@ + prev = msg->flags; + idx = log_next(idx); + seq++; ++ num_msg++; ++ if (num_msg > 5) { ++ num_msg = 0; ++ raw_spin_unlock_irq(&logbuf_lock); ++ raw_spin_lock_irq(&logbuf_lock); ++ if (clear_seq < log_first_seq) ++ goto try_again; ++ } + } + + /* last message fitting into this dump */ +@@ -1250,6 +1274,7 @@ + clear_seq = log_next_seq; + clear_idx = log_next_idx; + } ++out: + raw_spin_unlock_irq(&logbuf_lock); + + kfree(text); +@@ -1407,6 +1432,7 @@ + if (!console_drivers) + return; + ++ migrate_disable(); + for_each_console(con) { + if (exclusive_console && con != exclusive_console) + continue; +@@ -1419,6 +1445,7 @@ + continue; + con->write(con, text, len); + } ++ migrate_enable(); + } + + /* +@@ -1479,6 +1506,15 @@ + static int console_trylock_for_printk(void) + { + unsigned int cpu = smp_processor_id(); ++#ifdef CONFIG_PREEMPT_RT_FULL ++ int lock = !early_boot_irqs_disabled && (preempt_count() == 0) && ++ !irqs_disabled(); ++#else ++ int lock = 1; ++#endif ++ ++ if (!lock) ++ return 0; + + if (!console_trylock()) + return 0; +@@ -1613,6 +1649,62 @@ + return textlen; + } + ++#ifdef CONFIG_EARLY_PRINTK ++struct console *early_console; ++ ++void early_vprintk(const char *fmt, va_list ap) ++{ ++ if (early_console) { ++ char buf[512]; ++ int n = vscnprintf(buf, sizeof(buf), fmt, ap); ++ ++ early_console->write(early_console, buf, n); ++ } ++} ++ ++asmlinkage void early_printk(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ early_vprintk(fmt, ap); ++ va_end(ap); ++} ++ ++/* ++ * This is independent of any log levels - a global ++ * kill switch that turns off all of printk. ++ * ++ * Used by the NMI watchdog if early-printk is enabled. ++ */ ++static bool __read_mostly printk_killswitch; ++ ++static int __init force_early_printk_setup(char *str) ++{ ++ printk_killswitch = true; ++ return 0; ++} ++early_param("force_early_printk", force_early_printk_setup); ++ ++void printk_kill(void) ++{ ++ printk_killswitch = true; ++} ++ ++static int forced_early_printk(const char *fmt, va_list ap) ++{ ++ if (!printk_killswitch) ++ return 0; ++ early_vprintk(fmt, ap); ++ return 1; ++} ++#else ++static inline int forced_early_printk(const char *fmt, va_list ap) ++{ ++ return 0; ++} ++#endif ++ + asmlinkage int vprintk_emit(int facility, int level, + const char *dict, size_t dictlen, + const char *fmt, va_list args) +@@ -1629,6 +1721,13 @@ + /* cpu currently holding logbuf_lock in this function */ + static volatile unsigned int logbuf_cpu = UINT_MAX; + ++ /* ++ * Fall back to early_printk if a debugging subsystem has ++ * killed printk output ++ */ ++ if (unlikely(forced_early_printk(fmt, args))) ++ return 1; ++ + if (level == SCHED_MESSAGE_LOGLEVEL) { + level = -1; + in_sched = true; +@@ -1769,8 +1868,7 @@ + * console_sem which would prevent anyone from printing to + * console + */ +- preempt_disable(); +- ++ migrate_disable(); + /* + * Try to acquire and then immediately release the console + * semaphore. The release will print out buffers and wake up +@@ -1778,7 +1876,7 @@ + */ + if (console_trylock_for_printk()) + console_unlock(); +- preempt_enable(); ++ migrate_enable(); + lockdep_on(); + } + +@@ -1878,29 +1976,6 @@ + + #endif /* CONFIG_PRINTK */ + +-#ifdef CONFIG_EARLY_PRINTK +-struct console *early_console; +- +-void early_vprintk(const char *fmt, va_list ap) +-{ +- if (early_console) { +- char buf[512]; +- int n = vscnprintf(buf, sizeof(buf), fmt, ap); +- +- early_console->write(early_console, buf, n); +- } +-} +- +-asmlinkage __visible void early_printk(const char *fmt, ...) +-{ +- va_list ap; +- +- va_start(ap, fmt); +- early_vprintk(fmt, ap); +- va_end(ap); +-} +-#endif +- + static int __add_preferred_console(char *name, int idx, char *options, + char *brl_options) + { +@@ -2140,11 +2215,16 @@ + goto out; + + len = cont_print_text(text, size); ++#ifndef CONFIG_PREEMPT_RT_FULL + raw_spin_unlock(&logbuf_lock); + stop_critical_timings(); + call_console_drivers(cont.level, text, len); + start_critical_timings(); + local_irq_restore(flags); ++#else ++ raw_spin_unlock_irqrestore(&logbuf_lock, flags); ++ call_console_drivers(cont.level, text, len); ++#endif + return; + out: + raw_spin_unlock_irqrestore(&logbuf_lock, flags); +@@ -2232,12 +2312,17 @@ + console_idx = log_next(console_idx); + console_seq++; + console_prev = msg->flags; ++#ifdef CONFIG_PREEMPT_RT_FULL ++ raw_spin_unlock_irqrestore(&logbuf_lock, flags); ++ call_console_drivers(level, text, len); ++#else + raw_spin_unlock(&logbuf_lock); + + stop_critical_timings(); /* don't trace print latency */ + call_console_drivers(level, text, len); + start_critical_timings(); + local_irq_restore(flags); ++#endif + } + console_locked = 0; + +diff -Nur linux-3.18.14.orig/kernel/ptrace.c linux-3.18.14-rt/kernel/ptrace.c +--- linux-3.18.14.orig/kernel/ptrace.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/ptrace.c 2015-05-31 15:32:48.801635363 -0500 +@@ -129,7 +129,12 @@ + + spin_lock_irq(&task->sighand->siglock); + if (task_is_traced(task) && !__fatal_signal_pending(task)) { +- task->state = __TASK_TRACED; ++ raw_spin_lock_irq(&task->pi_lock); ++ if (task->state & __TASK_TRACED) ++ task->state = __TASK_TRACED; ++ else ++ task->saved_state = __TASK_TRACED; ++ raw_spin_unlock_irq(&task->pi_lock); + ret = true; + } + spin_unlock_irq(&task->sighand->siglock); +diff -Nur linux-3.18.14.orig/kernel/rcu/tiny.c linux-3.18.14-rt/kernel/rcu/tiny.c +--- linux-3.18.14.orig/kernel/rcu/tiny.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/rcu/tiny.c 2015-05-31 15:32:48.801635363 -0500 +@@ -370,6 +370,7 @@ + } + EXPORT_SYMBOL_GPL(call_rcu_sched); + ++#ifndef CONFIG_PREEMPT_RT_FULL + /* + * Post an RCU bottom-half callback to be invoked after any subsequent + * quiescent state. +@@ -379,6 +380,7 @@ + __call_rcu(head, func, &rcu_bh_ctrlblk); + } + EXPORT_SYMBOL_GPL(call_rcu_bh); ++#endif + + void rcu_init(void) + { +diff -Nur linux-3.18.14.orig/kernel/rcu/tree.c linux-3.18.14-rt/kernel/rcu/tree.c +--- linux-3.18.14.orig/kernel/rcu/tree.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/rcu/tree.c 2015-05-31 15:32:48.805635363 -0500 +@@ -56,6 +56,11 @@ + #include + #include + #include ++#include ++#include ++#include ++#include ++#include "../time/tick-internal.h" + + #include "tree.h" + #include "rcu.h" +@@ -152,8 +157,6 @@ + */ + static int rcu_scheduler_fully_active __read_mostly; + +-#ifdef CONFIG_RCU_BOOST +- + /* + * Control variables for per-CPU and per-rcu_node kthreads. These + * handle all flavors of RCU. +@@ -163,8 +166,6 @@ + DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); + DEFINE_PER_CPU(char, rcu_cpu_has_work); + +-#endif /* #ifdef CONFIG_RCU_BOOST */ +- + static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu); + static void invoke_rcu_core(void); + static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp); +@@ -207,6 +208,19 @@ + } + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++static void rcu_preempt_qs(void); ++ ++void rcu_bh_qs(void) ++{ ++ unsigned long flags; ++ ++ /* Callers to this function, rcu_preempt_qs(), must disable irqs. */ ++ local_irq_save(flags); ++ rcu_preempt_qs(); ++ local_irq_restore(flags); ++} ++#else + void rcu_bh_qs(void) + { + if (!__this_cpu_read(rcu_bh_data.passed_quiesce)) { +@@ -216,6 +230,7 @@ + __this_cpu_write(rcu_bh_data.passed_quiesce, 1); + } + } ++#endif + + static DEFINE_PER_CPU(int, rcu_sched_qs_mask); + +@@ -336,6 +351,7 @@ + } + EXPORT_SYMBOL_GPL(rcu_batches_completed_sched); + ++#ifndef CONFIG_PREEMPT_RT_FULL + /* + * Return the number of RCU BH batches processed thus far for debug & stats. + */ +@@ -363,6 +379,13 @@ + } + EXPORT_SYMBOL_GPL(rcu_bh_force_quiescent_state); + ++#else ++void rcu_force_quiescent_state(void) ++{ ++} ++EXPORT_SYMBOL_GPL(rcu_force_quiescent_state); ++#endif ++ + /* + * Show the state of the grace-period kthreads. + */ +@@ -1411,7 +1434,7 @@ + !ACCESS_ONCE(rsp->gp_flags) || + !rsp->gp_kthread) + return; +- wake_up(&rsp->gp_wq); ++ swait_wake(&rsp->gp_wq); + } + + /* +@@ -1793,7 +1816,7 @@ + ACCESS_ONCE(rsp->gpnum), + TPS("reqwait")); + rsp->gp_state = RCU_GP_WAIT_GPS; +- wait_event_interruptible(rsp->gp_wq, ++ swait_event_interruptible(rsp->gp_wq, + ACCESS_ONCE(rsp->gp_flags) & + RCU_GP_FLAG_INIT); + /* Locking provides needed memory barrier. */ +@@ -1821,7 +1844,7 @@ + ACCESS_ONCE(rsp->gpnum), + TPS("fqswait")); + rsp->gp_state = RCU_GP_WAIT_FQS; +- ret = wait_event_interruptible_timeout(rsp->gp_wq, ++ ret = swait_event_interruptible_timeout(rsp->gp_wq, + ((gf = ACCESS_ONCE(rsp->gp_flags)) & + RCU_GP_FLAG_FQS) || + (!ACCESS_ONCE(rnp->qsmask) && +@@ -2565,16 +2588,14 @@ + /* + * Do RCU core processing for the current CPU. + */ +-static void rcu_process_callbacks(struct softirq_action *unused) ++static void rcu_process_callbacks(void) + { + struct rcu_state *rsp; + + if (cpu_is_offline(smp_processor_id())) + return; +- trace_rcu_utilization(TPS("Start RCU core")); + for_each_rcu_flavor(rsp) + __rcu_process_callbacks(rsp); +- trace_rcu_utilization(TPS("End RCU core")); + } + + /* +@@ -2588,18 +2609,105 @@ + { + if (unlikely(!ACCESS_ONCE(rcu_scheduler_fully_active))) + return; +- if (likely(!rsp->boost)) { +- rcu_do_batch(rsp, rdp); ++ rcu_do_batch(rsp, rdp); ++} ++ ++static void rcu_wake_cond(struct task_struct *t, int status) ++{ ++ /* ++ * If the thread is yielding, only wake it when this ++ * is invoked from idle ++ */ ++ if (t && (status != RCU_KTHREAD_YIELDING || is_idle_task(current))) ++ wake_up_process(t); ++} ++ ++/* ++ * Wake up this CPU's rcuc kthread to do RCU core processing. ++ */ ++static void invoke_rcu_core(void) ++{ ++ unsigned long flags; ++ struct task_struct *t; ++ ++ if (!cpu_online(smp_processor_id())) + return; ++ local_irq_save(flags); ++ __this_cpu_write(rcu_cpu_has_work, 1); ++ t = __this_cpu_read(rcu_cpu_kthread_task); ++ if (t != NULL && current != t) ++ rcu_wake_cond(t, __this_cpu_read(rcu_cpu_kthread_status)); ++ local_irq_restore(flags); ++} ++ ++static void rcu_cpu_kthread_park(unsigned int cpu) ++{ ++ per_cpu(rcu_cpu_kthread_status, cpu) = RCU_KTHREAD_OFFCPU; ++} ++ ++static int rcu_cpu_kthread_should_run(unsigned int cpu) ++{ ++ return __this_cpu_read(rcu_cpu_has_work); ++} ++ ++/* ++ * Per-CPU kernel thread that invokes RCU callbacks. This replaces the ++ * RCU softirq used in flavors and configurations of RCU that do not ++ * support RCU priority boosting. ++ */ ++static void rcu_cpu_kthread(unsigned int cpu) ++{ ++ unsigned int *statusp = &__get_cpu_var(rcu_cpu_kthread_status); ++ char work, *workp = &__get_cpu_var(rcu_cpu_has_work); ++ int spincnt; ++ ++ for (spincnt = 0; spincnt < 10; spincnt++) { ++ trace_rcu_utilization(TPS("Start CPU kthread@rcu_wait")); ++ local_bh_disable(); ++ *statusp = RCU_KTHREAD_RUNNING; ++ this_cpu_inc(rcu_cpu_kthread_loops); ++ local_irq_disable(); ++ work = *workp; ++ *workp = 0; ++ local_irq_enable(); ++ if (work) ++ rcu_process_callbacks(); ++ local_bh_enable(); ++ if (*workp == 0) { ++ trace_rcu_utilization(TPS("End CPU kthread@rcu_wait")); ++ *statusp = RCU_KTHREAD_WAITING; ++ return; ++ } + } +- invoke_rcu_callbacks_kthread(); ++ *statusp = RCU_KTHREAD_YIELDING; ++ trace_rcu_utilization(TPS("Start CPU kthread@rcu_yield")); ++ schedule_timeout_interruptible(2); ++ trace_rcu_utilization(TPS("End CPU kthread@rcu_yield")); ++ *statusp = RCU_KTHREAD_WAITING; + } + +-static void invoke_rcu_core(void) ++static struct smp_hotplug_thread rcu_cpu_thread_spec = { ++ .store = &rcu_cpu_kthread_task, ++ .thread_should_run = rcu_cpu_kthread_should_run, ++ .thread_fn = rcu_cpu_kthread, ++ .thread_comm = "rcuc/%u", ++ .setup = rcu_cpu_kthread_setup, ++ .park = rcu_cpu_kthread_park, ++}; ++ ++/* ++ * Spawn per-CPU RCU core processing kthreads. ++ */ ++static int __init rcu_spawn_core_kthreads(void) + { +- if (cpu_online(smp_processor_id())) +- raise_softirq(RCU_SOFTIRQ); ++ int cpu; ++ ++ for_each_possible_cpu(cpu) ++ per_cpu(rcu_cpu_has_work, cpu) = 0; ++ BUG_ON(smpboot_register_percpu_thread(&rcu_cpu_thread_spec)); ++ return 0; + } ++early_initcall(rcu_spawn_core_kthreads); + + /* + * Handle any core-RCU processing required by a call_rcu() invocation. +@@ -2734,6 +2842,7 @@ + } + EXPORT_SYMBOL_GPL(call_rcu_sched); + ++#ifndef CONFIG_PREEMPT_RT_FULL + /* + * Queue an RCU callback for invocation after a quicker grace period. + */ +@@ -2742,6 +2851,7 @@ + __call_rcu(head, func, &rcu_bh_state, -1, 0); + } + EXPORT_SYMBOL_GPL(call_rcu_bh); ++#endif + + /* + * Queue an RCU callback for lazy invocation after a grace period. +@@ -2833,6 +2943,7 @@ + } + EXPORT_SYMBOL_GPL(synchronize_sched); + ++#ifndef CONFIG_PREEMPT_RT_FULL + /** + * synchronize_rcu_bh - wait until an rcu_bh grace period has elapsed. + * +@@ -2859,6 +2970,7 @@ + wait_rcu_gp(call_rcu_bh); + } + EXPORT_SYMBOL_GPL(synchronize_rcu_bh); ++#endif + + /** + * get_state_synchronize_rcu - Snapshot current RCU state +@@ -3341,6 +3453,7 @@ + mutex_unlock(&rsp->barrier_mutex); + } + ++#ifndef CONFIG_PREEMPT_RT_FULL + /** + * rcu_barrier_bh - Wait until all in-flight call_rcu_bh() callbacks complete. + */ +@@ -3349,6 +3462,7 @@ + _rcu_barrier(&rcu_bh_state); + } + EXPORT_SYMBOL_GPL(rcu_barrier_bh); ++#endif + + /** + * rcu_barrier_sched - Wait for in-flight call_rcu_sched() callbacks. +@@ -3658,7 +3772,7 @@ + } + + rsp->rda = rda; +- init_waitqueue_head(&rsp->gp_wq); ++ init_swait_head(&rsp->gp_wq); + rnp = rsp->level[rcu_num_lvls - 1]; + for_each_possible_cpu(i) { + while (i > rnp->grphi) +@@ -3755,7 +3869,6 @@ + rcu_init_one(&rcu_bh_state, &rcu_bh_data); + rcu_init_one(&rcu_sched_state, &rcu_sched_data); + __rcu_init_preempt(); +- open_softirq(RCU_SOFTIRQ, rcu_process_callbacks); + + /* + * We don't need protection against CPU-hotplug here because +diff -Nur linux-3.18.14.orig/kernel/rcu/tree.h linux-3.18.14-rt/kernel/rcu/tree.h +--- linux-3.18.14.orig/kernel/rcu/tree.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/rcu/tree.h 2015-05-31 15:32:48.809635364 -0500 +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + + /* + * Define shape of hierarchy based on NR_CPUS, CONFIG_RCU_FANOUT, and +@@ -172,11 +173,6 @@ + /* queued on this rcu_node structure that */ + /* are blocking the current grace period, */ + /* there can be no such task. */ +- struct completion boost_completion; +- /* Used to ensure that the rt_mutex used */ +- /* to carry out the boosting is fully */ +- /* released with no future boostee accesses */ +- /* before that rt_mutex is re-initialized. */ + struct rt_mutex boost_mtx; + /* Used only for the priority-boosting */ + /* side effect, not as a lock. */ +@@ -208,7 +204,7 @@ + /* This can happen due to race conditions. */ + #endif /* #ifdef CONFIG_RCU_BOOST */ + #ifdef CONFIG_RCU_NOCB_CPU +- wait_queue_head_t nocb_gp_wq[2]; ++ struct swait_head nocb_gp_wq[2]; + /* Place for rcu_nocb_kthread() to wait GP. */ + #endif /* #ifdef CONFIG_RCU_NOCB_CPU */ + int need_future_gp[2]; +@@ -348,7 +344,7 @@ + atomic_long_t nocb_follower_count_lazy; /* (approximate). */ + int nocb_p_count; /* # CBs being invoked by kthread */ + int nocb_p_count_lazy; /* (approximate). */ +- wait_queue_head_t nocb_wq; /* For nocb kthreads to sleep on. */ ++ struct swait_head nocb_wq; /* For nocb kthreads to sleep on. */ + struct task_struct *nocb_kthread; + int nocb_defer_wakeup; /* Defer wakeup of nocb_kthread. */ + +@@ -439,7 +435,7 @@ + unsigned long gpnum; /* Current gp number. */ + unsigned long completed; /* # of last completed gp. */ + struct task_struct *gp_kthread; /* Task for grace periods. */ +- wait_queue_head_t gp_wq; /* Where GP task waits. */ ++ struct swait_head gp_wq; /* Where GP task waits. */ + short gp_flags; /* Commands for GP task. */ + short gp_state; /* GP kthread sleep state. */ + +@@ -570,10 +566,9 @@ + static void __init __rcu_init_preempt(void); + static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags); + static void rcu_preempt_boost_start_gp(struct rcu_node *rnp); +-static void invoke_rcu_callbacks_kthread(void); + static bool rcu_is_callbacks_kthread(void); ++static void rcu_cpu_kthread_setup(unsigned int cpu); + #ifdef CONFIG_RCU_BOOST +-static void rcu_preempt_do_callbacks(void); + static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp, + struct rcu_node *rnp); + #endif /* #ifdef CONFIG_RCU_BOOST */ +diff -Nur linux-3.18.14.orig/kernel/rcu/tree_plugin.h linux-3.18.14-rt/kernel/rcu/tree_plugin.h +--- linux-3.18.14.orig/kernel/rcu/tree_plugin.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/rcu/tree_plugin.h 2015-05-31 15:32:48.829635363 -0500 +@@ -24,12 +24,6 @@ + * Paul E. McKenney + */ + +-#include +-#include +-#include +-#include +-#include "../time/tick-internal.h" +- + #define RCU_KTHREAD_PRIO 1 + + #ifdef CONFIG_RCU_BOOST +@@ -335,7 +329,7 @@ + } + + /* Hardware IRQ handlers cannot block, complain if they get here. */ +- if (WARN_ON_ONCE(in_irq() || in_serving_softirq())) { ++ if (WARN_ON_ONCE(preempt_count() & (HARDIRQ_MASK | SOFTIRQ_OFFSET))) { + local_irq_restore(flags); + return; + } +@@ -398,10 +392,8 @@ + + #ifdef CONFIG_RCU_BOOST + /* Unboost if we were boosted. */ +- if (drop_boost_mutex) { ++ if (drop_boost_mutex) + rt_mutex_unlock(&rnp->boost_mtx); +- complete(&rnp->boost_completion); +- } + #endif /* #ifdef CONFIG_RCU_BOOST */ + + /* +@@ -635,15 +627,6 @@ + t->rcu_read_unlock_special.b.need_qs = true; + } + +-#ifdef CONFIG_RCU_BOOST +- +-static void rcu_preempt_do_callbacks(void) +-{ +- rcu_do_batch(&rcu_preempt_state, this_cpu_ptr(&rcu_preempt_data)); +-} +- +-#endif /* #ifdef CONFIG_RCU_BOOST */ +- + /* + * Queue a preemptible-RCU callback for invocation after a grace period. + */ +@@ -1072,6 +1055,19 @@ + + #endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */ + ++/* ++ * If boosting, set rcuc kthreads to realtime priority. ++ */ ++static void rcu_cpu_kthread_setup(unsigned int cpu) ++{ ++#ifdef CONFIG_RCU_BOOST ++ struct sched_param sp; ++ ++ sp.sched_priority = RCU_KTHREAD_PRIO; ++ sched_setscheduler_nocheck(current, SCHED_FIFO, &sp); ++#endif /* #ifdef CONFIG_RCU_BOOST */ ++} ++ + #ifdef CONFIG_RCU_BOOST + + #include "../locking/rtmutex_common.h" +@@ -1103,16 +1099,6 @@ + + #endif /* #else #ifdef CONFIG_RCU_TRACE */ + +-static void rcu_wake_cond(struct task_struct *t, int status) +-{ +- /* +- * If the thread is yielding, only wake it when this +- * is invoked from idle +- */ +- if (status != RCU_KTHREAD_YIELDING || is_idle_task(current)) +- wake_up_process(t); +-} +- + /* + * Carry out RCU priority boosting on the task indicated by ->exp_tasks + * or ->boost_tasks, advancing the pointer to the next task in the +@@ -1175,15 +1161,11 @@ + */ + t = container_of(tb, struct task_struct, rcu_node_entry); + rt_mutex_init_proxy_locked(&rnp->boost_mtx, t); +- init_completion(&rnp->boost_completion); + raw_spin_unlock_irqrestore(&rnp->lock, flags); + /* Lock only for side effect: boosts task t's priority. */ + rt_mutex_lock(&rnp->boost_mtx); + rt_mutex_unlock(&rnp->boost_mtx); /* Then keep lockdep happy. */ + +- /* Wait for boostee to be done w/boost_mtx before reinitializing. */ +- wait_for_completion(&rnp->boost_completion); +- + return ACCESS_ONCE(rnp->exp_tasks) != NULL || + ACCESS_ONCE(rnp->boost_tasks) != NULL; + } +@@ -1261,23 +1243,6 @@ + } + + /* +- * Wake up the per-CPU kthread to invoke RCU callbacks. +- */ +-static void invoke_rcu_callbacks_kthread(void) +-{ +- unsigned long flags; +- +- local_irq_save(flags); +- __this_cpu_write(rcu_cpu_has_work, 1); +- if (__this_cpu_read(rcu_cpu_kthread_task) != NULL && +- current != __this_cpu_read(rcu_cpu_kthread_task)) { +- rcu_wake_cond(__this_cpu_read(rcu_cpu_kthread_task), +- __this_cpu_read(rcu_cpu_kthread_status)); +- } +- local_irq_restore(flags); +-} +- +-/* + * Is the current CPU running the RCU-callbacks kthread? + * Caller must have preemption disabled. + */ +@@ -1332,67 +1297,6 @@ + return 0; + } + +-static void rcu_kthread_do_work(void) +-{ +- rcu_do_batch(&rcu_sched_state, this_cpu_ptr(&rcu_sched_data)); +- rcu_do_batch(&rcu_bh_state, this_cpu_ptr(&rcu_bh_data)); +- rcu_preempt_do_callbacks(); +-} +- +-static void rcu_cpu_kthread_setup(unsigned int cpu) +-{ +- struct sched_param sp; +- +- sp.sched_priority = RCU_KTHREAD_PRIO; +- sched_setscheduler_nocheck(current, SCHED_FIFO, &sp); +-} +- +-static void rcu_cpu_kthread_park(unsigned int cpu) +-{ +- per_cpu(rcu_cpu_kthread_status, cpu) = RCU_KTHREAD_OFFCPU; +-} +- +-static int rcu_cpu_kthread_should_run(unsigned int cpu) +-{ +- return __this_cpu_read(rcu_cpu_has_work); +-} +- +-/* +- * Per-CPU kernel thread that invokes RCU callbacks. This replaces the +- * RCU softirq used in flavors and configurations of RCU that do not +- * support RCU priority boosting. +- */ +-static void rcu_cpu_kthread(unsigned int cpu) +-{ +- unsigned int *statusp = this_cpu_ptr(&rcu_cpu_kthread_status); +- char work, *workp = this_cpu_ptr(&rcu_cpu_has_work); +- int spincnt; +- +- for (spincnt = 0; spincnt < 10; spincnt++) { +- trace_rcu_utilization(TPS("Start CPU kthread@rcu_wait")); +- local_bh_disable(); +- *statusp = RCU_KTHREAD_RUNNING; +- this_cpu_inc(rcu_cpu_kthread_loops); +- local_irq_disable(); +- work = *workp; +- *workp = 0; +- local_irq_enable(); +- if (work) +- rcu_kthread_do_work(); +- local_bh_enable(); +- if (*workp == 0) { +- trace_rcu_utilization(TPS("End CPU kthread@rcu_wait")); +- *statusp = RCU_KTHREAD_WAITING; +- return; +- } +- } +- *statusp = RCU_KTHREAD_YIELDING; +- trace_rcu_utilization(TPS("Start CPU kthread@rcu_yield")); +- schedule_timeout_interruptible(2); +- trace_rcu_utilization(TPS("End CPU kthread@rcu_yield")); +- *statusp = RCU_KTHREAD_WAITING; +-} +- + /* + * Set the per-rcu_node kthread's affinity to cover all CPUs that are + * served by the rcu_node in question. The CPU hotplug lock is still +@@ -1426,26 +1330,13 @@ + free_cpumask_var(cm); + } + +-static struct smp_hotplug_thread rcu_cpu_thread_spec = { +- .store = &rcu_cpu_kthread_task, +- .thread_should_run = rcu_cpu_kthread_should_run, +- .thread_fn = rcu_cpu_kthread, +- .thread_comm = "rcuc/%u", +- .setup = rcu_cpu_kthread_setup, +- .park = rcu_cpu_kthread_park, +-}; +- + /* + * Spawn boost kthreads -- called as soon as the scheduler is running. + */ + static void __init rcu_spawn_boost_kthreads(void) + { + struct rcu_node *rnp; +- int cpu; + +- for_each_possible_cpu(cpu) +- per_cpu(rcu_cpu_has_work, cpu) = 0; +- BUG_ON(smpboot_register_percpu_thread(&rcu_cpu_thread_spec)); + rnp = rcu_get_root(rcu_state_p); + (void)rcu_spawn_one_boost_kthread(rcu_state_p, rnp); + if (NUM_RCU_NODES > 1) { +@@ -1472,11 +1363,6 @@ + raw_spin_unlock_irqrestore(&rnp->lock, flags); + } + +-static void invoke_rcu_callbacks_kthread(void) +-{ +- WARN_ON_ONCE(1); +-} +- + static bool rcu_is_callbacks_kthread(void) + { + return false; +@@ -1500,7 +1386,7 @@ + + #endif /* #else #ifdef CONFIG_RCU_BOOST */ + +-#if !defined(CONFIG_RCU_FAST_NO_HZ) ++#if !defined(CONFIG_RCU_FAST_NO_HZ) || defined(CONFIG_PREEMPT_RT_FULL) + + /* + * Check to see if any future RCU-related work will need to be done +@@ -1518,7 +1404,9 @@ + return rcu_cpu_has_callbacks(cpu, NULL); + } + #endif /* #ifndef CONFIG_RCU_NOCB_CPU_ALL */ ++#endif /* !defined(CONFIG_RCU_FAST_NO_HZ) || defined(CONFIG_PREEMPT_RT_FULL) */ + ++#if !defined(CONFIG_RCU_FAST_NO_HZ) + /* + * Because we do not have RCU_FAST_NO_HZ, don't bother cleaning up + * after it. +@@ -1615,6 +1503,8 @@ + return cbs_ready; + } + ++#ifndef CONFIG_PREEMPT_RT_FULL ++ + /* + * Allow the CPU to enter dyntick-idle mode unless it has callbacks ready + * to invoke. If the CPU has callbacks, try to advance them. Tell the +@@ -1655,7 +1545,7 @@ + return 0; + } + #endif /* #ifndef CONFIG_RCU_NOCB_CPU_ALL */ +- ++#endif /* #ifndef CONFIG_PREEMPT_RT_FULL */ + /* + * Prepare a CPU for idle from an RCU perspective. The first major task + * is to sense whether nohz mode has been enabled or disabled via sysfs. +@@ -2001,7 +1891,7 @@ + */ + static void rcu_nocb_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp) + { +- wake_up_all(&rnp->nocb_gp_wq[rnp->completed & 0x1]); ++ swait_wake_all(&rnp->nocb_gp_wq[rnp->completed & 0x1]); + } + + /* +@@ -2019,8 +1909,8 @@ + + static void rcu_init_one_nocb(struct rcu_node *rnp) + { +- init_waitqueue_head(&rnp->nocb_gp_wq[0]); +- init_waitqueue_head(&rnp->nocb_gp_wq[1]); ++ init_swait_head(&rnp->nocb_gp_wq[0]); ++ init_swait_head(&rnp->nocb_gp_wq[1]); + } + + #ifndef CONFIG_RCU_NOCB_CPU_ALL +@@ -2045,7 +1935,7 @@ + if (ACCESS_ONCE(rdp_leader->nocb_leader_sleep) || force) { + /* Prior smp_mb__after_atomic() orders against prior enqueue. */ + ACCESS_ONCE(rdp_leader->nocb_leader_sleep) = false; +- wake_up(&rdp_leader->nocb_wq); ++ swait_wake(&rdp_leader->nocb_wq); + } + } + +@@ -2238,7 +2128,7 @@ + */ + trace_rcu_future_gp(rnp, rdp, c, TPS("StartWait")); + for (;;) { +- wait_event_interruptible( ++ swait_event_interruptible( + rnp->nocb_gp_wq[c & 0x1], + (d = ULONG_CMP_GE(ACCESS_ONCE(rnp->completed), c))); + if (likely(d)) +@@ -2266,7 +2156,7 @@ + /* Wait for callbacks to appear. */ + if (!rcu_nocb_poll) { + trace_rcu_nocb_wake(my_rdp->rsp->name, my_rdp->cpu, "Sleep"); +- wait_event_interruptible(my_rdp->nocb_wq, ++ swait_event_interruptible(my_rdp->nocb_wq, + !ACCESS_ONCE(my_rdp->nocb_leader_sleep)); + /* Memory barrier handled by smp_mb() calls below and repoll. */ + } else if (firsttime) { +@@ -2347,7 +2237,7 @@ + * List was empty, wake up the follower. + * Memory barriers supplied by atomic_long_add(). + */ +- wake_up(&rdp->nocb_wq); ++ swait_wake(&rdp->nocb_wq); + } + } + +@@ -2368,7 +2258,7 @@ + if (!rcu_nocb_poll) { + trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, + "FollowerSleep"); +- wait_event_interruptible(rdp->nocb_wq, ++ swait_event_interruptible(rdp->nocb_wq, + ACCESS_ONCE(rdp->nocb_follower_head)); + } else if (firsttime) { + /* Don't drown trace log with "Poll"! */ +@@ -2539,7 +2429,7 @@ + static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp) + { + rdp->nocb_tail = &rdp->nocb_head; +- init_waitqueue_head(&rdp->nocb_wq); ++ init_swait_head(&rdp->nocb_wq); + rdp->nocb_follower_tail = &rdp->nocb_follower_head; + } + +diff -Nur linux-3.18.14.orig/kernel/rcu/update.c linux-3.18.14-rt/kernel/rcu/update.c +--- linux-3.18.14.orig/kernel/rcu/update.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/rcu/update.c 2015-05-31 15:32:48.829635363 -0500 +@@ -170,6 +170,7 @@ + } + EXPORT_SYMBOL_GPL(rcu_read_lock_held); + ++#ifndef CONFIG_PREEMPT_RT_FULL + /** + * rcu_read_lock_bh_held() - might we be in RCU-bh read-side critical section? + * +@@ -196,6 +197,7 @@ + return in_softirq() || irqs_disabled(); + } + EXPORT_SYMBOL_GPL(rcu_read_lock_bh_held); ++#endif + + #endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ + +diff -Nur linux-3.18.14.orig/kernel/relay.c linux-3.18.14-rt/kernel/relay.c +--- linux-3.18.14.orig/kernel/relay.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/relay.c 2015-05-31 15:32:48.829635363 -0500 +@@ -339,6 +339,10 @@ + { + struct rchan_buf *buf = (struct rchan_buf *)data; + wake_up_interruptible(&buf->read_wait); ++ /* ++ * Stupid polling for now: ++ */ ++ mod_timer(&buf->timer, jiffies + 1); + } + + /** +@@ -356,6 +360,7 @@ + init_waitqueue_head(&buf->read_wait); + kref_init(&buf->kref); + setup_timer(&buf->timer, wakeup_readers, (unsigned long)buf); ++ mod_timer(&buf->timer, jiffies + 1); + } else + del_timer_sync(&buf->timer); + +@@ -739,15 +744,6 @@ + else + buf->early_bytes += buf->chan->subbuf_size - + buf->padding[old_subbuf]; +- smp_mb(); +- if (waitqueue_active(&buf->read_wait)) +- /* +- * Calling wake_up_interruptible() from here +- * will deadlock if we happen to be logging +- * from the scheduler (trying to re-grab +- * rq->lock), so defer it. +- */ +- mod_timer(&buf->timer, jiffies + 1); + } + + old = buf->data; +diff -Nur linux-3.18.14.orig/kernel/res_counter.c linux-3.18.14-rt/kernel/res_counter.c +--- linux-3.18.14.orig/kernel/res_counter.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/res_counter.c 2015-05-31 15:32:48.845635363 -0500 +@@ -59,7 +59,7 @@ + + r = ret = 0; + *limit_fail_at = NULL; +- local_irq_save(flags); ++ local_irq_save_nort(flags); + for (c = counter; c != NULL; c = c->parent) { + spin_lock(&c->lock); + r = res_counter_charge_locked(c, val, force); +@@ -79,7 +79,7 @@ + spin_unlock(&u->lock); + } + } +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + + return ret; + } +@@ -104,7 +104,7 @@ + struct res_counter *c; + u64 ret = 0; + +- local_irq_save(flags); ++ local_irq_save_nort(flags); + for (c = counter; c != top; c = c->parent) { + u64 r; + spin_lock(&c->lock); +@@ -113,7 +113,7 @@ + ret = r; + spin_unlock(&c->lock); + } +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + return ret; + } + +diff -Nur linux-3.18.14.orig/kernel/sched/completion.c linux-3.18.14-rt/kernel/sched/completion.c +--- linux-3.18.14.orig/kernel/sched/completion.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/sched/completion.c 2015-05-31 15:32:48.889635363 -0500 +@@ -30,10 +30,10 @@ + { + unsigned long flags; + +- spin_lock_irqsave(&x->wait.lock, flags); ++ raw_spin_lock_irqsave(&x->wait.lock, flags); + x->done++; +- __wake_up_locked(&x->wait, TASK_NORMAL, 1); +- spin_unlock_irqrestore(&x->wait.lock, flags); ++ __swait_wake_locked(&x->wait, TASK_NORMAL, 1); ++ raw_spin_unlock_irqrestore(&x->wait.lock, flags); + } + EXPORT_SYMBOL(complete); + +@@ -50,10 +50,10 @@ + { + unsigned long flags; + +- spin_lock_irqsave(&x->wait.lock, flags); ++ raw_spin_lock_irqsave(&x->wait.lock, flags); + x->done += UINT_MAX/2; +- __wake_up_locked(&x->wait, TASK_NORMAL, 0); +- spin_unlock_irqrestore(&x->wait.lock, flags); ++ __swait_wake_locked(&x->wait, TASK_NORMAL, 0); ++ raw_spin_unlock_irqrestore(&x->wait.lock, flags); + } + EXPORT_SYMBOL(complete_all); + +@@ -62,20 +62,20 @@ + long (*action)(long), long timeout, int state) + { + if (!x->done) { +- DECLARE_WAITQUEUE(wait, current); ++ DEFINE_SWAITER(wait); + +- __add_wait_queue_tail_exclusive(&x->wait, &wait); ++ swait_prepare_locked(&x->wait, &wait); + do { + if (signal_pending_state(state, current)) { + timeout = -ERESTARTSYS; + break; + } + __set_current_state(state); +- spin_unlock_irq(&x->wait.lock); ++ raw_spin_unlock_irq(&x->wait.lock); + timeout = action(timeout); +- spin_lock_irq(&x->wait.lock); ++ raw_spin_lock_irq(&x->wait.lock); + } while (!x->done && timeout); +- __remove_wait_queue(&x->wait, &wait); ++ swait_finish_locked(&x->wait, &wait); + if (!x->done) + return timeout; + } +@@ -89,9 +89,9 @@ + { + might_sleep(); + +- spin_lock_irq(&x->wait.lock); ++ raw_spin_lock_irq(&x->wait.lock); + timeout = do_wait_for_common(x, action, timeout, state); +- spin_unlock_irq(&x->wait.lock); ++ raw_spin_unlock_irq(&x->wait.lock); + return timeout; + } + +@@ -267,12 +267,12 @@ + unsigned long flags; + int ret = 1; + +- spin_lock_irqsave(&x->wait.lock, flags); ++ raw_spin_lock_irqsave(&x->wait.lock, flags); + if (!x->done) + ret = 0; + else + x->done--; +- spin_unlock_irqrestore(&x->wait.lock, flags); ++ raw_spin_unlock_irqrestore(&x->wait.lock, flags); + return ret; + } + EXPORT_SYMBOL(try_wait_for_completion); +@@ -290,10 +290,10 @@ + unsigned long flags; + int ret = 1; + +- spin_lock_irqsave(&x->wait.lock, flags); ++ raw_spin_lock_irqsave(&x->wait.lock, flags); + if (!x->done) + ret = 0; +- spin_unlock_irqrestore(&x->wait.lock, flags); ++ raw_spin_unlock_irqrestore(&x->wait.lock, flags); + return ret; + } + EXPORT_SYMBOL(completion_done); +diff -Nur linux-3.18.14.orig/kernel/sched/core.c linux-3.18.14-rt/kernel/sched/core.c +--- linux-3.18.14.orig/kernel/sched/core.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/sched/core.c 2015-05-31 15:32:48.893635363 -0500 +@@ -280,7 +280,11 @@ + * Number of tasks to iterate in a single balance run. + * Limited because this is done with IRQs disabled. + */ ++#ifndef CONFIG_PREEMPT_RT_FULL + const_debug unsigned int sysctl_sched_nr_migrate = 32; ++#else ++const_debug unsigned int sysctl_sched_nr_migrate = 8; ++#endif + + /* + * period over which we average the RT time consumption, measured +@@ -516,6 +520,7 @@ + + hrtimer_init(&rq->hrtick_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + rq->hrtick_timer.function = hrtick; ++ rq->hrtick_timer.irqsafe = 1; + } + #else /* CONFIG_SCHED_HRTICK */ + static inline void hrtick_clear(struct rq *rq) +@@ -627,6 +632,38 @@ + trace_sched_wake_idle_without_ipi(cpu); + } + ++#ifdef CONFIG_PREEMPT_LAZY ++void resched_curr_lazy(struct rq *rq) ++{ ++ struct task_struct *curr = rq->curr; ++ int cpu; ++ ++ if (!sched_feat(PREEMPT_LAZY)) { ++ resched_curr(rq); ++ return; ++ } ++ ++ lockdep_assert_held(&rq->lock); ++ ++ if (test_tsk_need_resched(curr)) ++ return; ++ ++ if (test_tsk_need_resched_lazy(curr)) ++ return; ++ ++ set_tsk_need_resched_lazy(curr); ++ ++ cpu = cpu_of(rq); ++ if (cpu == smp_processor_id()) ++ return; ++ ++ /* NEED_RESCHED_LAZY must be visible before we test polling */ ++ smp_mb(); ++ if (!tsk_is_polling(curr)) ++ smp_send_reschedule(cpu); ++} ++#endif ++ + void resched_cpu(int cpu) + { + struct rq *rq = cpu_rq(cpu); +@@ -650,12 +687,14 @@ + */ + int get_nohz_timer_target(int pinned) + { +- int cpu = smp_processor_id(); ++ int cpu; + int i; + struct sched_domain *sd; + ++ preempt_disable_rt(); ++ cpu = smp_processor_id(); + if (pinned || !get_sysctl_timer_migration() || !idle_cpu(cpu)) +- return cpu; ++ goto preempt_en_rt; + + rcu_read_lock(); + for_each_domain(cpu, sd) { +@@ -668,6 +707,8 @@ + } + unlock: + rcu_read_unlock(); ++preempt_en_rt: ++ preempt_enable_rt(); + return cpu; + } + /* +@@ -745,14 +786,29 @@ + #endif /* CONFIG_NO_HZ_COMMON */ + + #ifdef CONFIG_NO_HZ_FULL ++ ++static int ksoftirqd_running(void) ++{ ++ struct task_struct *softirqd; ++ ++ if (!IS_ENABLED(CONFIG_PREEMPT_RT_FULL)) ++ return 0; ++ softirqd = this_cpu_ksoftirqd(); ++ if (softirqd && softirqd->on_rq) ++ return 1; ++ return 0; ++} ++ + bool sched_can_stop_tick(void) + { + /* + * More than one running task need preemption. + * nr_running update is assumed to be visible + * after IPI is sent from wakers. ++ * ++ * NOTE, RT: if ksoftirqd is awake, subtract it. + */ +- if (this_rq()->nr_running > 1) ++ if (this_rq()->nr_running - ksoftirqd_running() > 1) + return false; + + return true; +@@ -1198,6 +1254,18 @@ + + static int migration_cpu_stop(void *data); + ++static bool check_task_state(struct task_struct *p, long match_state) ++{ ++ bool match = false; ++ ++ raw_spin_lock_irq(&p->pi_lock); ++ if (p->state == match_state || p->saved_state == match_state) ++ match = true; ++ raw_spin_unlock_irq(&p->pi_lock); ++ ++ return match; ++} ++ + /* + * wait_task_inactive - wait for a thread to unschedule. + * +@@ -1242,7 +1310,7 @@ + * is actually now running somewhere else! + */ + while (task_running(rq, p)) { +- if (match_state && unlikely(p->state != match_state)) ++ if (match_state && !check_task_state(p, match_state)) + return 0; + cpu_relax(); + } +@@ -1257,7 +1325,8 @@ + running = task_running(rq, p); + queued = task_on_rq_queued(p); + ncsw = 0; +- if (!match_state || p->state == match_state) ++ if (!match_state || p->state == match_state || ++ p->saved_state == match_state) + ncsw = p->nvcsw | LONG_MIN; /* sets MSB */ + task_rq_unlock(rq, p, &flags); + +@@ -1482,10 +1551,6 @@ + { + activate_task(rq, p, en_flags); + p->on_rq = TASK_ON_RQ_QUEUED; +- +- /* if a worker is waking up, notify workqueue */ +- if (p->flags & PF_WQ_WORKER) +- wq_worker_waking_up(p, cpu_of(rq)); + } + + /* +@@ -1699,8 +1764,27 @@ + */ + smp_mb__before_spinlock(); + raw_spin_lock_irqsave(&p->pi_lock, flags); +- if (!(p->state & state)) ++ if (!(p->state & state)) { ++ /* ++ * The task might be running due to a spinlock sleeper ++ * wakeup. Check the saved state and set it to running ++ * if the wakeup condition is true. ++ */ ++ if (!(wake_flags & WF_LOCK_SLEEPER)) { ++ if (p->saved_state & state) { ++ p->saved_state = TASK_RUNNING; ++ success = 1; ++ } ++ } + goto out; ++ } ++ ++ /* ++ * If this is a regular wakeup, then we can unconditionally ++ * clear the saved state of a "lock sleeper". ++ */ ++ if (!(wake_flags & WF_LOCK_SLEEPER)) ++ p->saved_state = TASK_RUNNING; + + success = 1; /* we're going to change ->state */ + cpu = task_cpu(p); +@@ -1743,42 +1827,6 @@ + } + + /** +- * try_to_wake_up_local - try to wake up a local task with rq lock held +- * @p: the thread to be awakened +- * +- * Put @p on the run-queue if it's not already there. The caller must +- * ensure that this_rq() is locked, @p is bound to this_rq() and not +- * the current task. +- */ +-static void try_to_wake_up_local(struct task_struct *p) +-{ +- struct rq *rq = task_rq(p); +- +- if (WARN_ON_ONCE(rq != this_rq()) || +- WARN_ON_ONCE(p == current)) +- return; +- +- lockdep_assert_held(&rq->lock); +- +- if (!raw_spin_trylock(&p->pi_lock)) { +- raw_spin_unlock(&rq->lock); +- raw_spin_lock(&p->pi_lock); +- raw_spin_lock(&rq->lock); +- } +- +- if (!(p->state & TASK_NORMAL)) +- goto out; +- +- if (!task_on_rq_queued(p)) +- ttwu_activate(rq, p, ENQUEUE_WAKEUP); +- +- ttwu_do_wakeup(rq, p, 0); +- ttwu_stat(p, smp_processor_id(), 0); +-out: +- raw_spin_unlock(&p->pi_lock); +-} +- +-/** + * wake_up_process - Wake up a specific process + * @p: The process to be woken up. + * +@@ -1792,11 +1840,23 @@ + */ + int wake_up_process(struct task_struct *p) + { +- WARN_ON(task_is_stopped_or_traced(p)); ++ WARN_ON(__task_is_stopped_or_traced(p)); + return try_to_wake_up(p, TASK_NORMAL, 0); + } + EXPORT_SYMBOL(wake_up_process); + ++/** ++ * wake_up_lock_sleeper - Wake up a specific process blocked on a "sleeping lock" ++ * @p: The process to be woken up. ++ * ++ * Same as wake_up_process() above, but wake_flags=WF_LOCK_SLEEPER to indicate ++ * the nature of the wakeup. ++ */ ++int wake_up_lock_sleeper(struct task_struct *p) ++{ ++ return try_to_wake_up(p, TASK_ALL, WF_LOCK_SLEEPER); ++} ++ + int wake_up_state(struct task_struct *p, unsigned int state) + { + return try_to_wake_up(p, state, 0); +@@ -1987,6 +2047,9 @@ + p->on_cpu = 0; + #endif + init_task_preempt_count(p); ++#ifdef CONFIG_HAVE_PREEMPT_LAZY ++ task_thread_info(p)->preempt_lazy_count = 0; ++#endif + #ifdef CONFIG_SMP + plist_node_init(&p->pushable_tasks, MAX_PRIO); + RB_CLEAR_NODE(&p->pushable_dl_tasks); +@@ -2270,8 +2333,12 @@ + finish_arch_post_lock_switch(); + + fire_sched_in_preempt_notifiers(current); ++ /* ++ * We use mmdrop_delayed() here so we don't have to do the ++ * full __mmdrop() when we are the last user. ++ */ + if (mm) +- mmdrop(mm); ++ mmdrop_delayed(mm); + if (unlikely(prev_state == TASK_DEAD)) { + if (prev->sched_class->task_dead) + prev->sched_class->task_dead(prev); +@@ -2696,6 +2763,133 @@ + schedstat_inc(this_rq(), sched_count); + } + ++#if defined(CONFIG_PREEMPT_RT_FULL) && defined(CONFIG_SMP) ++#define MIGRATE_DISABLE_SET_AFFIN (1<<30) /* Can't make a negative */ ++#define migrate_disabled_updated(p) ((p)->migrate_disable & MIGRATE_DISABLE_SET_AFFIN) ++#define migrate_disable_count(p) ((p)->migrate_disable & ~MIGRATE_DISABLE_SET_AFFIN) ++ ++static inline void update_migrate_disable(struct task_struct *p) ++{ ++ const struct cpumask *mask; ++ ++ if (likely(!p->migrate_disable)) ++ return; ++ ++ /* Did we already update affinity? */ ++ if (unlikely(migrate_disabled_updated(p))) ++ return; ++ ++ /* ++ * Since this is always current we can get away with only locking ++ * rq->lock, the ->cpus_allowed value can normally only be changed ++ * while holding both p->pi_lock and rq->lock, but seeing that this ++ * is current, we cannot actually be waking up, so all code that ++ * relies on serialization against p->pi_lock is out of scope. ++ * ++ * Having rq->lock serializes us against things like ++ * set_cpus_allowed_ptr() that can still happen concurrently. ++ */ ++ mask = tsk_cpus_allowed(p); ++ ++ if (p->sched_class->set_cpus_allowed) ++ p->sched_class->set_cpus_allowed(p, mask); ++ /* mask==cpumask_of(task_cpu(p)) which has a cpumask_weight==1 */ ++ p->nr_cpus_allowed = 1; ++ ++ /* Let migrate_enable know to fix things back up */ ++ p->migrate_disable |= MIGRATE_DISABLE_SET_AFFIN; ++} ++ ++void migrate_disable(void) ++{ ++ struct task_struct *p = current; ++ ++ if (in_atomic()) { ++#ifdef CONFIG_SCHED_DEBUG ++ p->migrate_disable_atomic++; ++#endif ++ return; ++ } ++ ++#ifdef CONFIG_SCHED_DEBUG ++ if (unlikely(p->migrate_disable_atomic)) { ++ tracing_off(); ++ WARN_ON_ONCE(1); ++ } ++#endif ++ ++ if (p->migrate_disable) { ++ p->migrate_disable++; ++ return; ++ } ++ ++ preempt_disable(); ++ preempt_lazy_disable(); ++ pin_current_cpu(); ++ p->migrate_disable = 1; ++ preempt_enable(); ++} ++EXPORT_SYMBOL(migrate_disable); ++ ++void migrate_enable(void) ++{ ++ struct task_struct *p = current; ++ const struct cpumask *mask; ++ unsigned long flags; ++ struct rq *rq; ++ ++ if (in_atomic()) { ++#ifdef CONFIG_SCHED_DEBUG ++ p->migrate_disable_atomic--; ++#endif ++ return; ++ } ++ ++#ifdef CONFIG_SCHED_DEBUG ++ if (unlikely(p->migrate_disable_atomic)) { ++ tracing_off(); ++ WARN_ON_ONCE(1); ++ } ++#endif ++ WARN_ON_ONCE(p->migrate_disable <= 0); ++ ++ if (migrate_disable_count(p) > 1) { ++ p->migrate_disable--; ++ return; ++ } ++ ++ preempt_disable(); ++ if (unlikely(migrate_disabled_updated(p))) { ++ /* ++ * Undo whatever update_migrate_disable() did, also see there ++ * about locking. ++ */ ++ rq = this_rq(); ++ raw_spin_lock_irqsave(&rq->lock, flags); ++ ++ /* ++ * Clearing migrate_disable causes tsk_cpus_allowed to ++ * show the tasks original cpu affinity. ++ */ ++ p->migrate_disable = 0; ++ mask = tsk_cpus_allowed(p); ++ if (p->sched_class->set_cpus_allowed) ++ p->sched_class->set_cpus_allowed(p, mask); ++ p->nr_cpus_allowed = cpumask_weight(mask); ++ raw_spin_unlock_irqrestore(&rq->lock, flags); ++ } else ++ p->migrate_disable = 0; ++ ++ unpin_current_cpu(); ++ preempt_enable(); ++ preempt_lazy_enable(); ++} ++EXPORT_SYMBOL(migrate_enable); ++#else ++static inline void update_migrate_disable(struct task_struct *p) { } ++#define migrate_disabled_updated(p) 0 ++#endif ++ + /* + * Pick up the highest-prio task: + */ +@@ -2799,6 +2993,8 @@ + smp_mb__before_spinlock(); + raw_spin_lock_irq(&rq->lock); + ++ update_migrate_disable(prev); ++ + switch_count = &prev->nivcsw; + if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { + if (unlikely(signal_pending_state(prev->state, prev))) { +@@ -2806,19 +3002,6 @@ + } else { + deactivate_task(rq, prev, DEQUEUE_SLEEP); + prev->on_rq = 0; +- +- /* +- * If a worker went to sleep, notify and ask workqueue +- * whether it wants to wake up a task to maintain +- * concurrency. +- */ +- if (prev->flags & PF_WQ_WORKER) { +- struct task_struct *to_wakeup; +- +- to_wakeup = wq_worker_sleeping(prev, cpu); +- if (to_wakeup) +- try_to_wake_up_local(to_wakeup); +- } + } + switch_count = &prev->nvcsw; + } +@@ -2828,6 +3011,7 @@ + + next = pick_next_task(rq, prev); + clear_tsk_need_resched(prev); ++ clear_tsk_need_resched_lazy(prev); + clear_preempt_need_resched(); + rq->skip_clock_update = 0; + +@@ -2857,9 +3041,20 @@ + + static inline void sched_submit_work(struct task_struct *tsk) + { +- if (!tsk->state || tsk_is_pi_blocked(tsk)) ++ if (!tsk->state) + return; + /* ++ * If a worker went to sleep, notify and ask workqueue whether ++ * it wants to wake up a task to maintain concurrency. ++ */ ++ if (tsk->flags & PF_WQ_WORKER) ++ wq_worker_sleeping(tsk); ++ ++ ++ if (tsk_is_pi_blocked(tsk)) ++ return; ++ ++ /* + * If we are going to sleep and we have plugged IO queued, + * make sure to submit it to avoid deadlocks. + */ +@@ -2867,12 +3062,19 @@ + blk_schedule_flush_plug(tsk); + } + ++static inline void sched_update_worker(struct task_struct *tsk) ++{ ++ if (tsk->flags & PF_WQ_WORKER) ++ wq_worker_running(tsk); ++} ++ + asmlinkage __visible void __sched schedule(void) + { + struct task_struct *tsk = current; + + sched_submit_work(tsk); + __schedule(); ++ sched_update_worker(tsk); + } + EXPORT_SYMBOL(schedule); + +@@ -2922,9 +3124,26 @@ + if (likely(!preemptible())) + return; + ++#ifdef CONFIG_PREEMPT_LAZY ++ /* ++ * Check for lazy preemption ++ */ ++ if (current_thread_info()->preempt_lazy_count && ++ !test_thread_flag(TIF_NEED_RESCHED)) ++ return; ++#endif + do { + __preempt_count_add(PREEMPT_ACTIVE); ++ /* ++ * The add/subtract must not be traced by the function ++ * tracer. But we still want to account for the ++ * preempt off latency tracer. Since the _notrace versions ++ * of add/subtract skip the accounting for latency tracer ++ * we must force it manually. ++ */ ++ start_critical_timings(); + __schedule(); ++ stop_critical_timings(); + __preempt_count_sub(PREEMPT_ACTIVE); + + /* +@@ -4236,9 +4455,16 @@ + + static void __cond_resched(void) + { +- __preempt_count_add(PREEMPT_ACTIVE); +- __schedule(); +- __preempt_count_sub(PREEMPT_ACTIVE); ++ do { ++ __preempt_count_add(PREEMPT_ACTIVE); ++ __schedule(); ++ __preempt_count_sub(PREEMPT_ACTIVE); ++ /* ++ * Check again in case we missed a preemption ++ * opportunity between schedule and now. ++ */ ++ barrier(); ++ } while (need_resched()); + } + + int __sched _cond_resched(void) +@@ -4279,6 +4505,7 @@ + } + EXPORT_SYMBOL(__cond_resched_lock); + ++#ifndef CONFIG_PREEMPT_RT_FULL + int __sched __cond_resched_softirq(void) + { + BUG_ON(!in_softirq()); +@@ -4292,6 +4519,7 @@ + return 0; + } + EXPORT_SYMBOL(__cond_resched_softirq); ++#endif + + /** + * yield - yield the current processor to other threads. +@@ -4653,7 +4881,9 @@ + + /* Set the preempt count _outside_ the spinlocks! */ + init_idle_preempt_count(idle, cpu); +- ++#ifdef CONFIG_HAVE_PREEMPT_LAZY ++ task_thread_info(idle)->preempt_lazy_count = 0; ++#endif + /* + * The idle tasks have their own, simple scheduling class: + */ +@@ -4695,11 +4925,91 @@ + + void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask) + { +- if (p->sched_class && p->sched_class->set_cpus_allowed) +- p->sched_class->set_cpus_allowed(p, new_mask); ++ if (!migrate_disabled_updated(p)) { ++ if (p->sched_class && p->sched_class->set_cpus_allowed) ++ p->sched_class->set_cpus_allowed(p, new_mask); ++ p->nr_cpus_allowed = cpumask_weight(new_mask); ++ } + + cpumask_copy(&p->cpus_allowed, new_mask); +- p->nr_cpus_allowed = cpumask_weight(new_mask); ++} ++ ++static DEFINE_PER_CPU(struct cpumask, sched_cpumasks); ++static DEFINE_MUTEX(sched_down_mutex); ++static cpumask_t sched_down_cpumask; ++ ++void tell_sched_cpu_down_begin(int cpu) ++{ ++ mutex_lock(&sched_down_mutex); ++ cpumask_set_cpu(cpu, &sched_down_cpumask); ++ mutex_unlock(&sched_down_mutex); ++} ++ ++void tell_sched_cpu_down_done(int cpu) ++{ ++ mutex_lock(&sched_down_mutex); ++ cpumask_clear_cpu(cpu, &sched_down_cpumask); ++ mutex_unlock(&sched_down_mutex); ++} ++ ++/** ++ * migrate_me - try to move the current task off this cpu ++ * ++ * Used by the pin_current_cpu() code to try to get tasks ++ * to move off the current CPU as it is going down. ++ * It will only move the task if the task isn't pinned to ++ * the CPU (with migrate_disable, affinity or NO_SETAFFINITY) ++ * and the task has to be in a RUNNING state. Otherwise the ++ * movement of the task will wake it up (change its state ++ * to running) when the task did not expect it. ++ * ++ * Returns 1 if it succeeded in moving the current task ++ * 0 otherwise. ++ */ ++int migrate_me(void) ++{ ++ struct task_struct *p = current; ++ struct migration_arg arg; ++ struct cpumask *cpumask; ++ struct cpumask *mask; ++ unsigned long flags; ++ unsigned int dest_cpu; ++ struct rq *rq; ++ ++ /* ++ * We can not migrate tasks bounded to a CPU or tasks not ++ * running. The movement of the task will wake it up. ++ */ ++ if (p->flags & PF_NO_SETAFFINITY || p->state) ++ return 0; ++ ++ mutex_lock(&sched_down_mutex); ++ rq = task_rq_lock(p, &flags); ++ ++ cpumask = &__get_cpu_var(sched_cpumasks); ++ mask = &p->cpus_allowed; ++ ++ cpumask_andnot(cpumask, mask, &sched_down_cpumask); ++ ++ if (!cpumask_weight(cpumask)) { ++ /* It's only on this CPU? */ ++ task_rq_unlock(rq, p, &flags); ++ mutex_unlock(&sched_down_mutex); ++ return 0; ++ } ++ ++ dest_cpu = cpumask_any_and(cpu_active_mask, cpumask); ++ ++ arg.task = p; ++ arg.dest_cpu = dest_cpu; ++ ++ task_rq_unlock(rq, p, &flags); ++ ++ stop_one_cpu(cpu_of(rq), migration_cpu_stop, &arg); ++ tlb_migrate_finish(p->mm); ++ mutex_unlock(&sched_down_mutex); ++ ++ return 1; + } + + /* +@@ -4745,7 +5055,7 @@ + do_set_cpus_allowed(p, new_mask); + + /* Can the task run on the task's current CPU? If so, we're done */ +- if (cpumask_test_cpu(task_cpu(p), new_mask)) ++ if (cpumask_test_cpu(task_cpu(p), new_mask) || __migrate_disabled(p)) + goto out; + + dest_cpu = cpumask_any_and(cpu_active_mask, new_mask); +@@ -4885,6 +5195,8 @@ + + #ifdef CONFIG_HOTPLUG_CPU + ++static DEFINE_PER_CPU(struct mm_struct *, idle_last_mm); ++ + /* + * Ensures that the idle task is using init_mm right before its cpu goes + * offline. +@@ -4899,7 +5211,11 @@ + switch_mm(mm, &init_mm, current); + finish_arch_post_lock_switch(); + } +- mmdrop(mm); ++ /* ++ * Defer the cleanup to an alive cpu. On RT we can neither ++ * call mmdrop() nor mmdrop_delayed() from here. ++ */ ++ per_cpu(idle_last_mm, smp_processor_id()) = mm; + } + + /* +@@ -5242,6 +5558,10 @@ + + case CPU_DEAD: + calc_load_migrate(rq); ++ if (per_cpu(idle_last_mm, cpu)) { ++ mmdrop(per_cpu(idle_last_mm, cpu)); ++ per_cpu(idle_last_mm, cpu) = NULL; ++ } + break; + #endif + } +@@ -7183,7 +7503,8 @@ + #ifdef CONFIG_DEBUG_ATOMIC_SLEEP + static inline int preempt_count_equals(int preempt_offset) + { +- int nested = (preempt_count() & ~PREEMPT_ACTIVE) + rcu_preempt_depth(); ++ int nested = (preempt_count() & ~PREEMPT_ACTIVE) + ++ sched_rcu_preempt_depth(); + + return (nested == preempt_offset); + } +diff -Nur linux-3.18.14.orig/kernel/sched/cputime.c linux-3.18.14-rt/kernel/sched/cputime.c +--- linux-3.18.14.orig/kernel/sched/cputime.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/sched/cputime.c 2015-05-31 15:32:48.893635363 -0500 +@@ -675,37 +675,45 @@ + + void vtime_account_system(struct task_struct *tsk) + { +- write_seqlock(&tsk->vtime_seqlock); ++ raw_spin_lock(&tsk->vtime_lock); ++ write_seqcount_begin(&tsk->vtime_seq); + __vtime_account_system(tsk); +- write_sequnlock(&tsk->vtime_seqlock); ++ write_seqcount_end(&tsk->vtime_seq); ++ raw_spin_unlock(&tsk->vtime_lock); + } + + void vtime_gen_account_irq_exit(struct task_struct *tsk) + { +- write_seqlock(&tsk->vtime_seqlock); ++ raw_spin_lock(&tsk->vtime_lock); ++ write_seqcount_begin(&tsk->vtime_seq); + __vtime_account_system(tsk); + if (context_tracking_in_user()) + tsk->vtime_snap_whence = VTIME_USER; +- write_sequnlock(&tsk->vtime_seqlock); ++ write_seqcount_end(&tsk->vtime_seq); ++ raw_spin_unlock(&tsk->vtime_lock); + } + + void vtime_account_user(struct task_struct *tsk) + { + cputime_t delta_cpu; + +- write_seqlock(&tsk->vtime_seqlock); ++ raw_spin_lock(&tsk->vtime_lock); ++ write_seqcount_begin(&tsk->vtime_seq); + delta_cpu = get_vtime_delta(tsk); + tsk->vtime_snap_whence = VTIME_SYS; + account_user_time(tsk, delta_cpu, cputime_to_scaled(delta_cpu)); +- write_sequnlock(&tsk->vtime_seqlock); ++ write_seqcount_end(&tsk->vtime_seq); ++ raw_spin_unlock(&tsk->vtime_lock); + } + + void vtime_user_enter(struct task_struct *tsk) + { +- write_seqlock(&tsk->vtime_seqlock); ++ raw_spin_lock(&tsk->vtime_lock); ++ write_seqcount_begin(&tsk->vtime_seq); + __vtime_account_system(tsk); + tsk->vtime_snap_whence = VTIME_USER; +- write_sequnlock(&tsk->vtime_seqlock); ++ write_seqcount_end(&tsk->vtime_seq); ++ raw_spin_unlock(&tsk->vtime_lock); + } + + void vtime_guest_enter(struct task_struct *tsk) +@@ -717,19 +725,23 @@ + * synchronization against the reader (task_gtime()) + * that can thus safely catch up with a tickless delta. + */ +- write_seqlock(&tsk->vtime_seqlock); ++ raw_spin_lock(&tsk->vtime_lock); ++ write_seqcount_begin(&tsk->vtime_seq); + __vtime_account_system(tsk); + current->flags |= PF_VCPU; +- write_sequnlock(&tsk->vtime_seqlock); ++ write_seqcount_end(&tsk->vtime_seq); ++ raw_spin_unlock(&tsk->vtime_lock); + } + EXPORT_SYMBOL_GPL(vtime_guest_enter); + + void vtime_guest_exit(struct task_struct *tsk) + { +- write_seqlock(&tsk->vtime_seqlock); ++ raw_spin_lock(&tsk->vtime_lock); ++ write_seqcount_begin(&tsk->vtime_seq); + __vtime_account_system(tsk); + current->flags &= ~PF_VCPU; +- write_sequnlock(&tsk->vtime_seqlock); ++ write_seqcount_end(&tsk->vtime_seq); ++ raw_spin_unlock(&tsk->vtime_lock); + } + EXPORT_SYMBOL_GPL(vtime_guest_exit); + +@@ -742,24 +754,30 @@ + + void arch_vtime_task_switch(struct task_struct *prev) + { +- write_seqlock(&prev->vtime_seqlock); ++ raw_spin_lock(&prev->vtime_lock); ++ write_seqcount_begin(&prev->vtime_seq); + prev->vtime_snap_whence = VTIME_SLEEPING; +- write_sequnlock(&prev->vtime_seqlock); ++ write_seqcount_end(&prev->vtime_seq); ++ raw_spin_unlock(&prev->vtime_lock); + +- write_seqlock(¤t->vtime_seqlock); ++ raw_spin_lock(¤t->vtime_lock); ++ write_seqcount_begin(¤t->vtime_seq); + current->vtime_snap_whence = VTIME_SYS; + current->vtime_snap = sched_clock_cpu(smp_processor_id()); +- write_sequnlock(¤t->vtime_seqlock); ++ write_seqcount_end(¤t->vtime_seq); ++ raw_spin_unlock(¤t->vtime_lock); + } + + void vtime_init_idle(struct task_struct *t, int cpu) + { + unsigned long flags; + +- write_seqlock_irqsave(&t->vtime_seqlock, flags); ++ raw_spin_lock_irqsave(&t->vtime_lock, flags); ++ write_seqcount_begin(&t->vtime_seq); + t->vtime_snap_whence = VTIME_SYS; + t->vtime_snap = sched_clock_cpu(cpu); +- write_sequnlock_irqrestore(&t->vtime_seqlock, flags); ++ write_seqcount_end(&t->vtime_seq); ++ raw_spin_unlock_irqrestore(&t->vtime_lock, flags); + } + + cputime_t task_gtime(struct task_struct *t) +@@ -768,13 +786,13 @@ + cputime_t gtime; + + do { +- seq = read_seqbegin(&t->vtime_seqlock); ++ seq = read_seqcount_begin(&t->vtime_seq); + + gtime = t->gtime; + if (t->flags & PF_VCPU) + gtime += vtime_delta(t); + +- } while (read_seqretry(&t->vtime_seqlock, seq)); ++ } while (read_seqcount_retry(&t->vtime_seq, seq)); + + return gtime; + } +@@ -797,7 +815,7 @@ + *udelta = 0; + *sdelta = 0; + +- seq = read_seqbegin(&t->vtime_seqlock); ++ seq = read_seqcount_begin(&t->vtime_seq); + + if (u_dst) + *u_dst = *u_src; +@@ -821,7 +839,7 @@ + if (t->vtime_snap_whence == VTIME_SYS) + *sdelta = delta; + } +- } while (read_seqretry(&t->vtime_seqlock, seq)); ++ } while (read_seqcount_retry(&t->vtime_seq, seq)); + } + + +diff -Nur linux-3.18.14.orig/kernel/sched/deadline.c linux-3.18.14-rt/kernel/sched/deadline.c +--- linux-3.18.14.orig/kernel/sched/deadline.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/sched/deadline.c 2015-05-31 15:32:48.893635363 -0500 +@@ -570,6 +570,7 @@ + + hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + timer->function = dl_task_timer; ++ timer->irqsafe = 1; + } + + static +diff -Nur linux-3.18.14.orig/kernel/sched/debug.c linux-3.18.14-rt/kernel/sched/debug.c +--- linux-3.18.14.orig/kernel/sched/debug.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/sched/debug.c 2015-05-31 15:32:48.897635363 -0500 +@@ -256,6 +256,9 @@ + P(rt_throttled); + PN(rt_time); + PN(rt_runtime); ++#ifdef CONFIG_SMP ++ P(rt_nr_migratory); ++#endif + + #undef PN + #undef P +@@ -634,6 +637,10 @@ + #endif + P(policy); + P(prio); ++#ifdef CONFIG_PREEMPT_RT_FULL ++ P(migrate_disable); ++#endif ++ P(nr_cpus_allowed); + #undef PN + #undef __PN + #undef P +diff -Nur linux-3.18.14.orig/kernel/sched/fair.c linux-3.18.14-rt/kernel/sched/fair.c +--- linux-3.18.14.orig/kernel/sched/fair.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/sched/fair.c 2015-05-31 15:32:48.897635363 -0500 +@@ -2951,7 +2951,7 @@ + ideal_runtime = sched_slice(cfs_rq, curr); + delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime; + if (delta_exec > ideal_runtime) { +- resched_curr(rq_of(cfs_rq)); ++ resched_curr_lazy(rq_of(cfs_rq)); + /* + * The current task ran long enough, ensure it doesn't get + * re-elected due to buddy favours. +@@ -2975,7 +2975,7 @@ + return; + + if (delta > ideal_runtime) +- resched_curr(rq_of(cfs_rq)); ++ resched_curr_lazy(rq_of(cfs_rq)); + } + + static void +@@ -3115,7 +3115,7 @@ + * validating it and just reschedule. + */ + if (queued) { +- resched_curr(rq_of(cfs_rq)); ++ resched_curr_lazy(rq_of(cfs_rq)); + return; + } + /* +@@ -3306,7 +3306,7 @@ + * hierarchy can be throttled + */ + if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr)) +- resched_curr(rq_of(cfs_rq)); ++ resched_curr_lazy(rq_of(cfs_rq)); + } + + static __always_inline +@@ -3925,7 +3925,7 @@ + + if (delta < 0) { + if (rq->curr == p) +- resched_curr(rq); ++ resched_curr_lazy(rq); + return; + } + hrtick_start(rq, delta); +@@ -4792,7 +4792,7 @@ + return; + + preempt: +- resched_curr(rq); ++ resched_curr_lazy(rq); + /* + * Only set the backward buddy when the current task is still + * on the rq. This can happen when a wakeup gets interleaved +@@ -7576,7 +7576,7 @@ + * 'current' within the tree based on its new key value. + */ + swap(curr->vruntime, se->vruntime); +- resched_curr(rq); ++ resched_curr_lazy(rq); + } + + se->vruntime -= cfs_rq->min_vruntime; +@@ -7601,7 +7601,7 @@ + */ + if (rq->curr == p) { + if (p->prio > oldprio) +- resched_curr(rq); ++ resched_curr_lazy(rq); + } else + check_preempt_curr(rq, p, 0); + } +diff -Nur linux-3.18.14.orig/kernel/sched/features.h linux-3.18.14-rt/kernel/sched/features.h +--- linux-3.18.14.orig/kernel/sched/features.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/sched/features.h 2015-05-31 15:32:48.897635363 -0500 +@@ -50,12 +50,18 @@ + */ + SCHED_FEAT(NONTASK_CAPACITY, true) + ++#ifdef CONFIG_PREEMPT_RT_FULL ++SCHED_FEAT(TTWU_QUEUE, false) ++# ifdef CONFIG_PREEMPT_LAZY ++SCHED_FEAT(PREEMPT_LAZY, true) ++# endif ++#else + /* + * Queue remote wakeups on the target CPU and process them + * using the scheduler IPI. Reduces rq->lock contention/bounces. + */ + SCHED_FEAT(TTWU_QUEUE, true) +- ++#endif + SCHED_FEAT(FORCE_SD_OVERLAP, false) + SCHED_FEAT(RT_RUNTIME_SHARE, true) + SCHED_FEAT(LB_MIN, false) +diff -Nur linux-3.18.14.orig/kernel/sched/Makefile linux-3.18.14-rt/kernel/sched/Makefile +--- linux-3.18.14.orig/kernel/sched/Makefile 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/sched/Makefile 2015-05-31 15:32:48.861635363 -0500 +@@ -13,7 +13,7 @@ + + obj-y += core.o proc.o clock.o cputime.o + obj-y += idle_task.o fair.o rt.o deadline.o stop_task.o +-obj-y += wait.o completion.o idle.o ++obj-y += wait.o wait-simple.o work-simple.o completion.o idle.o + obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o + obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o + obj-$(CONFIG_SCHEDSTATS) += stats.o +diff -Nur linux-3.18.14.orig/kernel/sched/rt.c linux-3.18.14-rt/kernel/sched/rt.c +--- linux-3.18.14.orig/kernel/sched/rt.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/sched/rt.c 2015-05-31 15:32:48.897635363 -0500 +@@ -43,6 +43,7 @@ + + hrtimer_init(&rt_b->rt_period_timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ rt_b->rt_period_timer.irqsafe = 1; + rt_b->rt_period_timer.function = sched_rt_period_timer; + } + +diff -Nur linux-3.18.14.orig/kernel/sched/sched.h linux-3.18.14-rt/kernel/sched/sched.h +--- linux-3.18.14.orig/kernel/sched/sched.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/sched/sched.h 2015-05-31 15:32:48.897635363 -0500 +@@ -1018,6 +1018,7 @@ + #define WF_SYNC 0x01 /* waker goes to sleep after wakeup */ + #define WF_FORK 0x02 /* child wakeup after fork */ + #define WF_MIGRATED 0x4 /* internal use, task got migrated */ ++#define WF_LOCK_SLEEPER 0x08 /* wakeup spinlock "sleeper" */ + + /* + * To aid in avoiding the subversion of "niceness" due to uneven distribution +@@ -1210,6 +1211,15 @@ + extern void resched_curr(struct rq *rq); + extern void resched_cpu(int cpu); + ++#ifdef CONFIG_PREEMPT_LAZY ++extern void resched_curr_lazy(struct rq *rq); ++#else ++static inline void resched_curr_lazy(struct rq *rq) ++{ ++ resched_curr(rq); ++} ++#endif ++ + extern struct rt_bandwidth def_rt_bandwidth; + extern void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime); + +diff -Nur linux-3.18.14.orig/kernel/sched/wait-simple.c linux-3.18.14-rt/kernel/sched/wait-simple.c +--- linux-3.18.14.orig/kernel/sched/wait-simple.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/kernel/sched/wait-simple.c 2015-05-31 15:32:48.897635363 -0500 +@@ -0,0 +1,115 @@ ++/* ++ * Simple waitqueues without fancy flags and callbacks ++ * ++ * (C) 2011 Thomas Gleixner ++ * ++ * Based on kernel/wait.c ++ * ++ * For licencing details see kernel-base/COPYING ++ */ ++#include ++#include ++#include ++#include ++ ++/* Adds w to head->list. Must be called with head->lock locked. */ ++static inline void __swait_enqueue(struct swait_head *head, struct swaiter *w) ++{ ++ list_add(&w->node, &head->list); ++ /* We can't let the condition leak before the setting of head */ ++ smp_mb(); ++} ++ ++/* Removes w from head->list. Must be called with head->lock locked. */ ++static inline void __swait_dequeue(struct swaiter *w) ++{ ++ list_del_init(&w->node); ++} ++ ++void __init_swait_head(struct swait_head *head, struct lock_class_key *key) ++{ ++ raw_spin_lock_init(&head->lock); ++ lockdep_set_class(&head->lock, key); ++ INIT_LIST_HEAD(&head->list); ++} ++EXPORT_SYMBOL(__init_swait_head); ++ ++void swait_prepare_locked(struct swait_head *head, struct swaiter *w) ++{ ++ w->task = current; ++ if (list_empty(&w->node)) ++ __swait_enqueue(head, w); ++} ++ ++void swait_prepare(struct swait_head *head, struct swaiter *w, int state) ++{ ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&head->lock, flags); ++ swait_prepare_locked(head, w); ++ __set_current_state(state); ++ raw_spin_unlock_irqrestore(&head->lock, flags); ++} ++EXPORT_SYMBOL(swait_prepare); ++ ++void swait_finish_locked(struct swait_head *head, struct swaiter *w) ++{ ++ __set_current_state(TASK_RUNNING); ++ if (w->task) ++ __swait_dequeue(w); ++} ++ ++void swait_finish(struct swait_head *head, struct swaiter *w) ++{ ++ unsigned long flags; ++ ++ __set_current_state(TASK_RUNNING); ++ if (w->task) { ++ raw_spin_lock_irqsave(&head->lock, flags); ++ __swait_dequeue(w); ++ raw_spin_unlock_irqrestore(&head->lock, flags); ++ } ++} ++EXPORT_SYMBOL(swait_finish); ++ ++unsigned int ++__swait_wake_locked(struct swait_head *head, unsigned int state, unsigned int num) ++{ ++ struct swaiter *curr, *next; ++ int woken = 0; ++ ++ list_for_each_entry_safe(curr, next, &head->list, node) { ++ if (wake_up_state(curr->task, state)) { ++ __swait_dequeue(curr); ++ /* ++ * The waiting task can free the waiter as ++ * soon as curr->task = NULL is written, ++ * without taking any locks. A memory barrier ++ * is required here to prevent the following ++ * store to curr->task from getting ahead of ++ * the dequeue operation. ++ */ ++ smp_wmb(); ++ curr->task = NULL; ++ if (++woken == num) ++ break; ++ } ++ } ++ return woken; ++} ++ ++unsigned int ++__swait_wake(struct swait_head *head, unsigned int state, unsigned int num) ++{ ++ unsigned long flags; ++ int woken; ++ ++ if (!swaitqueue_active(head)) ++ return 0; ++ ++ raw_spin_lock_irqsave(&head->lock, flags); ++ woken = __swait_wake_locked(head, state, num); ++ raw_spin_unlock_irqrestore(&head->lock, flags); ++ return woken; ++} ++EXPORT_SYMBOL(__swait_wake); +diff -Nur linux-3.18.14.orig/kernel/sched/work-simple.c linux-3.18.14-rt/kernel/sched/work-simple.c +--- linux-3.18.14.orig/kernel/sched/work-simple.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/kernel/sched/work-simple.c 2015-05-31 15:32:48.901635363 -0500 +@@ -0,0 +1,172 @@ ++/* ++ * Copyright (C) 2014 BMW Car IT GmbH, Daniel Wagner daniel.wagner@bmw-carit.de ++ * ++ * Provides a framework for enqueuing callbacks from irq context ++ * PREEMPT_RT_FULL safe. The callbacks are executed in kthread context. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define SWORK_EVENT_PENDING (1 << 0) ++ ++static DEFINE_MUTEX(worker_mutex); ++static struct sworker *glob_worker; ++ ++struct sworker { ++ struct list_head events; ++ struct swait_head wq; ++ ++ raw_spinlock_t lock; ++ ++ struct task_struct *task; ++ int refs; ++}; ++ ++static bool swork_readable(struct sworker *worker) ++{ ++ bool r; ++ ++ if (kthread_should_stop()) ++ return true; ++ ++ raw_spin_lock_irq(&worker->lock); ++ r = !list_empty(&worker->events); ++ raw_spin_unlock_irq(&worker->lock); ++ ++ return r; ++} ++ ++static int swork_kthread(void *arg) ++{ ++ struct sworker *worker = arg; ++ ++ for (;;) { ++ swait_event_interruptible(worker->wq, ++ swork_readable(worker)); ++ if (kthread_should_stop()) ++ break; ++ ++ raw_spin_lock_irq(&worker->lock); ++ while (!list_empty(&worker->events)) { ++ struct swork_event *sev; ++ ++ sev = list_first_entry(&worker->events, ++ struct swork_event, item); ++ list_del(&sev->item); ++ raw_spin_unlock_irq(&worker->lock); ++ ++ WARN_ON_ONCE(!test_and_clear_bit(SWORK_EVENT_PENDING, ++ &sev->flags)); ++ sev->func(sev); ++ raw_spin_lock_irq(&worker->lock); ++ } ++ raw_spin_unlock_irq(&worker->lock); ++ } ++ return 0; ++} ++ ++static struct sworker *swork_create(void) ++{ ++ struct sworker *worker; ++ ++ worker = kzalloc(sizeof(*worker), GFP_KERNEL); ++ if (!worker) ++ return ERR_PTR(-ENOMEM); ++ ++ INIT_LIST_HEAD(&worker->events); ++ raw_spin_lock_init(&worker->lock); ++ init_swait_head(&worker->wq); ++ ++ worker->task = kthread_run(swork_kthread, worker, "kswork"); ++ if (IS_ERR(worker->task)) { ++ kfree(worker); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ return worker; ++} ++ ++static void swork_destroy(struct sworker *worker) ++{ ++ kthread_stop(worker->task); ++ ++ WARN_ON(!list_empty(&worker->events)); ++ kfree(worker); ++} ++ ++/** ++ * swork_queue - queue swork ++ * ++ * Returns %false if @work was already on a queue, %true otherwise. ++ * ++ * The work is queued and processed on a random CPU ++ */ ++bool swork_queue(struct swork_event *sev) ++{ ++ unsigned long flags; ++ ++ if (test_and_set_bit(SWORK_EVENT_PENDING, &sev->flags)) ++ return false; ++ ++ raw_spin_lock_irqsave(&glob_worker->lock, flags); ++ list_add_tail(&sev->item, &glob_worker->events); ++ raw_spin_unlock_irqrestore(&glob_worker->lock, flags); ++ ++ swait_wake(&glob_worker->wq); ++ return true; ++} ++EXPORT_SYMBOL_GPL(swork_queue); ++ ++/** ++ * swork_get - get an instance of the sworker ++ * ++ * Returns an negative error code if the initialization if the worker did not ++ * work, %0 otherwise. ++ * ++ */ ++int swork_get(void) ++{ ++ struct sworker *worker; ++ ++ mutex_lock(&worker_mutex); ++ if (!glob_worker) { ++ worker = swork_create(); ++ if (IS_ERR(worker)) { ++ mutex_unlock(&worker_mutex); ++ return -ENOMEM; ++ } ++ ++ glob_worker = worker; ++ } ++ ++ glob_worker->refs++; ++ mutex_unlock(&worker_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(swork_get); ++ ++/** ++ * swork_put - puts an instance of the sworker ++ * ++ * Will destroy the sworker thread. This function must not be called until all ++ * queued events have been completed. ++ */ ++void swork_put(void) ++{ ++ mutex_lock(&worker_mutex); ++ ++ glob_worker->refs--; ++ if (glob_worker->refs > 0) ++ goto out; ++ ++ swork_destroy(glob_worker); ++ glob_worker = NULL; ++out: ++ mutex_unlock(&worker_mutex); ++} ++EXPORT_SYMBOL_GPL(swork_put); +diff -Nur linux-3.18.14.orig/kernel/signal.c linux-3.18.14-rt/kernel/signal.c +--- linux-3.18.14.orig/kernel/signal.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/signal.c 2015-05-31 15:32:48.921635363 -0500 +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -352,13 +353,45 @@ + return false; + } + ++#ifdef __HAVE_ARCH_CMPXCHG ++static inline struct sigqueue *get_task_cache(struct task_struct *t) ++{ ++ struct sigqueue *q = t->sigqueue_cache; ++ ++ if (cmpxchg(&t->sigqueue_cache, q, NULL) != q) ++ return NULL; ++ return q; ++} ++ ++static inline int put_task_cache(struct task_struct *t, struct sigqueue *q) ++{ ++ if (cmpxchg(&t->sigqueue_cache, NULL, q) == NULL) ++ return 0; ++ return 1; ++} ++ ++#else ++ ++static inline struct sigqueue *get_task_cache(struct task_struct *t) ++{ ++ return NULL; ++} ++ ++static inline int put_task_cache(struct task_struct *t, struct sigqueue *q) ++{ ++ return 1; ++} ++ ++#endif ++ + /* + * allocate a new signal queue record + * - this may be called without locks if and only if t == current, otherwise an + * appropriate lock must be held to stop the target task from exiting + */ + static struct sigqueue * +-__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimit) ++__sigqueue_do_alloc(int sig, struct task_struct *t, gfp_t flags, ++ int override_rlimit, int fromslab) + { + struct sigqueue *q = NULL; + struct user_struct *user; +@@ -375,7 +408,10 @@ + if (override_rlimit || + atomic_read(&user->sigpending) <= + task_rlimit(t, RLIMIT_SIGPENDING)) { +- q = kmem_cache_alloc(sigqueue_cachep, flags); ++ if (!fromslab) ++ q = get_task_cache(t); ++ if (!q) ++ q = kmem_cache_alloc(sigqueue_cachep, flags); + } else { + print_dropped_signal(sig); + } +@@ -392,6 +428,13 @@ + return q; + } + ++static struct sigqueue * ++__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, ++ int override_rlimit) ++{ ++ return __sigqueue_do_alloc(sig, t, flags, override_rlimit, 0); ++} ++ + static void __sigqueue_free(struct sigqueue *q) + { + if (q->flags & SIGQUEUE_PREALLOC) +@@ -401,6 +444,21 @@ + kmem_cache_free(sigqueue_cachep, q); + } + ++static void sigqueue_free_current(struct sigqueue *q) ++{ ++ struct user_struct *up; ++ ++ if (q->flags & SIGQUEUE_PREALLOC) ++ return; ++ ++ up = q->user; ++ if (rt_prio(current->normal_prio) && !put_task_cache(current, q)) { ++ atomic_dec(&up->sigpending); ++ free_uid(up); ++ } else ++ __sigqueue_free(q); ++} ++ + void flush_sigqueue(struct sigpending *queue) + { + struct sigqueue *q; +@@ -414,6 +472,21 @@ + } + + /* ++ * Called from __exit_signal. Flush tsk->pending and ++ * tsk->sigqueue_cache ++ */ ++void flush_task_sigqueue(struct task_struct *tsk) ++{ ++ struct sigqueue *q; ++ ++ flush_sigqueue(&tsk->pending); ++ ++ q = get_task_cache(tsk); ++ if (q) ++ kmem_cache_free(sigqueue_cachep, q); ++} ++ ++/* + * Flush all pending signals for a task. + */ + void __flush_signals(struct task_struct *t) +@@ -565,7 +638,7 @@ + still_pending: + list_del_init(&first->list); + copy_siginfo(info, &first->info); +- __sigqueue_free(first); ++ sigqueue_free_current(first); + } else { + /* + * Ok, it wasn't in the queue. This must be +@@ -611,6 +684,8 @@ + { + int signr; + ++ WARN_ON_ONCE(tsk != current); ++ + /* We only dequeue private signals from ourselves, we don't let + * signalfd steal them + */ +@@ -1207,8 +1282,8 @@ + * We don't want to have recursive SIGSEGV's etc, for example, + * that is why we also clear SIGNAL_UNKILLABLE. + */ +-int +-force_sig_info(int sig, struct siginfo *info, struct task_struct *t) ++static int ++do_force_sig_info(int sig, struct siginfo *info, struct task_struct *t) + { + unsigned long int flags; + int ret, blocked, ignored; +@@ -1233,6 +1308,39 @@ + return ret; + } + ++int force_sig_info(int sig, struct siginfo *info, struct task_struct *t) ++{ ++/* ++ * On some archs, PREEMPT_RT has to delay sending a signal from a trap ++ * since it can not enable preemption, and the signal code's spin_locks ++ * turn into mutexes. Instead, it must set TIF_NOTIFY_RESUME which will ++ * send the signal on exit of the trap. ++ */ ++#ifdef ARCH_RT_DELAYS_SIGNAL_SEND ++ if (in_atomic()) { ++ if (WARN_ON_ONCE(t != current)) ++ return 0; ++ if (WARN_ON_ONCE(t->forced_info.si_signo)) ++ return 0; ++ ++ if (is_si_special(info)) { ++ WARN_ON_ONCE(info != SEND_SIG_PRIV); ++ t->forced_info.si_signo = sig; ++ t->forced_info.si_errno = 0; ++ t->forced_info.si_code = SI_KERNEL; ++ t->forced_info.si_pid = 0; ++ t->forced_info.si_uid = 0; ++ } else { ++ t->forced_info = *info; ++ } ++ ++ set_tsk_thread_flag(t, TIF_NOTIFY_RESUME); ++ return 0; ++ } ++#endif ++ return do_force_sig_info(sig, info, t); ++} ++ + /* + * Nuke all other threads in the group. + */ +@@ -1267,12 +1375,12 @@ + * Disable interrupts early to avoid deadlocks. + * See rcu_read_unlock() comment header for details. + */ +- local_irq_save(*flags); ++ local_irq_save_nort(*flags); + rcu_read_lock(); + sighand = rcu_dereference(tsk->sighand); + if (unlikely(sighand == NULL)) { + rcu_read_unlock(); +- local_irq_restore(*flags); ++ local_irq_restore_nort(*flags); + break; + } + +@@ -1283,7 +1391,7 @@ + } + spin_unlock(&sighand->siglock); + rcu_read_unlock(); +- local_irq_restore(*flags); ++ local_irq_restore_nort(*flags); + } + + return sighand; +@@ -1528,7 +1636,8 @@ + */ + struct sigqueue *sigqueue_alloc(void) + { +- struct sigqueue *q = __sigqueue_alloc(-1, current, GFP_KERNEL, 0); ++ /* Preallocated sigqueue objects always from the slabcache ! */ ++ struct sigqueue *q = __sigqueue_do_alloc(-1, current, GFP_KERNEL, 0, 1); + + if (q) + q->flags |= SIGQUEUE_PREALLOC; +@@ -1889,15 +1998,7 @@ + if (gstop_done && ptrace_reparented(current)) + do_notify_parent_cldstop(current, false, why); + +- /* +- * Don't want to allow preemption here, because +- * sys_ptrace() needs this task to be inactive. +- * +- * XXX: implement read_unlock_no_resched(). +- */ +- preempt_disable(); + read_unlock(&tasklist_lock); +- preempt_enable_no_resched(); + freezable_schedule(); + } else { + /* +diff -Nur linux-3.18.14.orig/kernel/softirq.c linux-3.18.14-rt/kernel/softirq.c +--- linux-3.18.14.orig/kernel/softirq.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/softirq.c 2015-05-31 15:32:48.921635363 -0500 +@@ -21,10 +21,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + + #define CREATE_TRACE_POINTS +@@ -62,6 +64,98 @@ + "TASKLET", "SCHED", "HRTIMER", "RCU" + }; + ++#ifdef CONFIG_NO_HZ_COMMON ++# ifdef CONFIG_PREEMPT_RT_FULL ++ ++struct softirq_runner { ++ struct task_struct *runner[NR_SOFTIRQS]; ++}; ++ ++static DEFINE_PER_CPU(struct softirq_runner, softirq_runners); ++ ++static inline void softirq_set_runner(unsigned int sirq) ++{ ++ struct softirq_runner *sr = &__get_cpu_var(softirq_runners); ++ ++ sr->runner[sirq] = current; ++} ++ ++static inline void softirq_clr_runner(unsigned int sirq) ++{ ++ struct softirq_runner *sr = &__get_cpu_var(softirq_runners); ++ ++ sr->runner[sirq] = NULL; ++} ++ ++/* ++ * On preempt-rt a softirq running context might be blocked on a ++ * lock. There might be no other runnable task on this CPU because the ++ * lock owner runs on some other CPU. So we have to go into idle with ++ * the pending bit set. Therefor we need to check this otherwise we ++ * warn about false positives which confuses users and defeats the ++ * whole purpose of this test. ++ * ++ * This code is called with interrupts disabled. ++ */ ++void softirq_check_pending_idle(void) ++{ ++ static int rate_limit; ++ struct softirq_runner *sr = &__get_cpu_var(softirq_runners); ++ u32 warnpending; ++ int i; ++ ++ if (rate_limit >= 10) ++ return; ++ ++ warnpending = local_softirq_pending() & SOFTIRQ_STOP_IDLE_MASK; ++ for (i = 0; i < NR_SOFTIRQS; i++) { ++ struct task_struct *tsk = sr->runner[i]; ++ ++ /* ++ * The wakeup code in rtmutex.c wakes up the task ++ * _before_ it sets pi_blocked_on to NULL under ++ * tsk->pi_lock. So we need to check for both: state ++ * and pi_blocked_on. ++ */ ++ if (tsk) { ++ raw_spin_lock(&tsk->pi_lock); ++ if (tsk->pi_blocked_on || tsk->state == TASK_RUNNING) { ++ /* Clear all bits pending in that task */ ++ warnpending &= ~(tsk->softirqs_raised); ++ warnpending &= ~(1 << i); ++ } ++ raw_spin_unlock(&tsk->pi_lock); ++ } ++ } ++ ++ if (warnpending) { ++ printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n", ++ warnpending); ++ rate_limit++; ++ } ++} ++# else ++/* ++ * On !PREEMPT_RT we just printk rate limited: ++ */ ++void softirq_check_pending_idle(void) ++{ ++ static int rate_limit; ++ ++ if (rate_limit < 10 && ++ (local_softirq_pending() & SOFTIRQ_STOP_IDLE_MASK)) { ++ printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n", ++ local_softirq_pending()); ++ rate_limit++; ++ } ++} ++# endif ++ ++#else /* !CONFIG_NO_HZ_COMMON */ ++static inline void softirq_set_runner(unsigned int sirq) { } ++static inline void softirq_clr_runner(unsigned int sirq) { } ++#endif ++ + /* + * we cannot loop indefinitely here to avoid userspace starvation, + * but we also don't want to introduce a worst case 1/HZ latency +@@ -77,6 +171,70 @@ + wake_up_process(tsk); + } + ++static void handle_softirq(unsigned int vec_nr) ++{ ++ struct softirq_action *h = softirq_vec + vec_nr; ++ int prev_count; ++ ++ prev_count = preempt_count(); ++ ++ kstat_incr_softirqs_this_cpu(vec_nr); ++ ++ trace_softirq_entry(vec_nr); ++ h->action(h); ++ trace_softirq_exit(vec_nr); ++ if (unlikely(prev_count != preempt_count())) { ++ pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n", ++ vec_nr, softirq_to_name[vec_nr], h->action, ++ prev_count, preempt_count()); ++ preempt_count_set(prev_count); ++ } ++} ++ ++#ifndef CONFIG_PREEMPT_RT_FULL ++static inline int ksoftirqd_softirq_pending(void) ++{ ++ return local_softirq_pending(); ++} ++ ++static void handle_pending_softirqs(u32 pending, int need_rcu_bh_qs) ++{ ++ struct softirq_action *h = softirq_vec; ++ int softirq_bit; ++ ++ local_irq_enable(); ++ ++ h = softirq_vec; ++ ++ while ((softirq_bit = ffs(pending))) { ++ unsigned int vec_nr; ++ ++ h += softirq_bit - 1; ++ vec_nr = h - softirq_vec; ++ handle_softirq(vec_nr); ++ ++ h++; ++ pending >>= softirq_bit; ++ } ++ ++ if (need_rcu_bh_qs) ++ rcu_bh_qs(); ++ local_irq_disable(); ++} ++ ++static void run_ksoftirqd(unsigned int cpu) ++{ ++ local_irq_disable(); ++ if (ksoftirqd_softirq_pending()) { ++ __do_softirq(); ++ rcu_note_context_switch(cpu); ++ local_irq_enable(); ++ cond_resched(); ++ return; ++ } ++ local_irq_enable(); ++} ++ + /* + * preempt_count and SOFTIRQ_OFFSET usage: + * - preempt_count is changed by SOFTIRQ_OFFSET on entering or leaving +@@ -228,10 +386,8 @@ + unsigned long end = jiffies + MAX_SOFTIRQ_TIME; + unsigned long old_flags = current->flags; + int max_restart = MAX_SOFTIRQ_RESTART; +- struct softirq_action *h; + bool in_hardirq; + __u32 pending; +- int softirq_bit; + + /* + * Mask out PF_MEMALLOC s current task context is borrowed for the +@@ -250,36 +406,7 @@ + /* Reset the pending bitmask before enabling irqs */ + set_softirq_pending(0); + +- local_irq_enable(); +- +- h = softirq_vec; +- +- while ((softirq_bit = ffs(pending))) { +- unsigned int vec_nr; +- int prev_count; +- +- h += softirq_bit - 1; +- +- vec_nr = h - softirq_vec; +- prev_count = preempt_count(); +- +- kstat_incr_softirqs_this_cpu(vec_nr); +- +- trace_softirq_entry(vec_nr); +- h->action(h); +- trace_softirq_exit(vec_nr); +- if (unlikely(prev_count != preempt_count())) { +- pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n", +- vec_nr, softirq_to_name[vec_nr], h->action, +- prev_count, preempt_count()); +- preempt_count_set(prev_count); +- } +- h++; +- pending >>= softirq_bit; +- } +- +- rcu_bh_qs(); +- local_irq_disable(); ++ handle_pending_softirqs(pending, 1); + + pending = local_softirq_pending(); + if (pending) { +@@ -316,6 +443,285 @@ + } + + /* ++ * This function must run with irqs disabled! ++ */ ++void raise_softirq_irqoff(unsigned int nr) ++{ ++ __raise_softirq_irqoff(nr); ++ ++ /* ++ * If we're in an interrupt or softirq, we're done ++ * (this also catches softirq-disabled code). We will ++ * actually run the softirq once we return from ++ * the irq or softirq. ++ * ++ * Otherwise we wake up ksoftirqd to make sure we ++ * schedule the softirq soon. ++ */ ++ if (!in_interrupt()) ++ wakeup_softirqd(); ++} ++ ++void __raise_softirq_irqoff(unsigned int nr) ++{ ++ trace_softirq_raise(nr); ++ or_softirq_pending(1UL << nr); ++} ++ ++static inline void local_bh_disable_nort(void) { local_bh_disable(); } ++static inline void _local_bh_enable_nort(void) { _local_bh_enable(); } ++static void ksoftirqd_set_sched_params(unsigned int cpu) { } ++static void ksoftirqd_clr_sched_params(unsigned int cpu, bool online) { } ++ ++#else /* !PREEMPT_RT_FULL */ ++ ++/* ++ * On RT we serialize softirq execution with a cpu local lock per softirq ++ */ ++static DEFINE_PER_CPU(struct local_irq_lock [NR_SOFTIRQS], local_softirq_locks); ++ ++void __init softirq_early_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < NR_SOFTIRQS; i++) ++ local_irq_lock_init(local_softirq_locks[i]); ++} ++ ++static void lock_softirq(int which) ++{ ++ local_lock(local_softirq_locks[which]); ++} ++ ++static void unlock_softirq(int which) ++{ ++ local_unlock(local_softirq_locks[which]); ++} ++ ++static void do_single_softirq(int which, int need_rcu_bh_qs) ++{ ++ unsigned long old_flags = current->flags; ++ ++ current->flags &= ~PF_MEMALLOC; ++ vtime_account_irq_enter(current); ++ current->flags |= PF_IN_SOFTIRQ; ++ lockdep_softirq_enter(); ++ local_irq_enable(); ++ handle_softirq(which); ++ local_irq_disable(); ++ lockdep_softirq_exit(); ++ current->flags &= ~PF_IN_SOFTIRQ; ++ vtime_account_irq_enter(current); ++ tsk_restore_flags(current, old_flags, PF_MEMALLOC); ++} ++ ++/* ++ * Called with interrupts disabled. Process softirqs which were raised ++ * in current context (or on behalf of ksoftirqd). ++ */ ++static void do_current_softirqs(int need_rcu_bh_qs) ++{ ++ while (current->softirqs_raised) { ++ int i = __ffs(current->softirqs_raised); ++ unsigned int pending, mask = (1U << i); ++ ++ current->softirqs_raised &= ~mask; ++ local_irq_enable(); ++ ++ /* ++ * If the lock is contended, we boost the owner to ++ * process the softirq or leave the critical section ++ * now. ++ */ ++ lock_softirq(i); ++ local_irq_disable(); ++ softirq_set_runner(i); ++ /* ++ * Check with the local_softirq_pending() bits, ++ * whether we need to process this still or if someone ++ * else took care of it. ++ */ ++ pending = local_softirq_pending(); ++ if (pending & mask) { ++ set_softirq_pending(pending & ~mask); ++ do_single_softirq(i, need_rcu_bh_qs); ++ } ++ softirq_clr_runner(i); ++ unlock_softirq(i); ++ WARN_ON(current->softirq_nestcnt != 1); ++ } ++} ++ ++static void __local_bh_disable(void) ++{ ++ if (++current->softirq_nestcnt == 1) ++ migrate_disable(); ++} ++ ++void local_bh_disable(void) ++{ ++ __local_bh_disable(); ++} ++EXPORT_SYMBOL(local_bh_disable); ++ ++void __local_bh_disable_ip(unsigned long ip, unsigned int cnt) ++{ ++ __local_bh_disable(); ++ if (cnt & PREEMPT_CHECK_OFFSET) ++ preempt_disable(); ++} ++ ++static void __local_bh_enable(void) ++{ ++ if (WARN_ON(current->softirq_nestcnt == 0)) ++ return; ++ ++ local_irq_disable(); ++ if (current->softirq_nestcnt == 1 && current->softirqs_raised) ++ do_current_softirqs(1); ++ local_irq_enable(); ++ ++ if (--current->softirq_nestcnt == 0) ++ migrate_enable(); ++} ++ ++void local_bh_enable(void) ++{ ++ __local_bh_enable(); ++} ++EXPORT_SYMBOL(local_bh_enable); ++ ++extern void __local_bh_enable_ip(unsigned long ip, unsigned int cnt) ++{ ++ __local_bh_enable(); ++ if (cnt & PREEMPT_CHECK_OFFSET) ++ preempt_enable(); ++} ++ ++void local_bh_enable_ip(unsigned long ip) ++{ ++ local_bh_enable(); ++} ++EXPORT_SYMBOL(local_bh_enable_ip); ++ ++void _local_bh_enable(void) ++{ ++ if (WARN_ON(current->softirq_nestcnt == 0)) ++ return; ++ if (--current->softirq_nestcnt == 0) ++ migrate_enable(); ++} ++EXPORT_SYMBOL(_local_bh_enable); ++ ++int in_serving_softirq(void) ++{ ++ return current->flags & PF_IN_SOFTIRQ; ++} ++EXPORT_SYMBOL(in_serving_softirq); ++ ++/* Called with preemption disabled */ ++static void run_ksoftirqd(unsigned int cpu) ++{ ++ local_irq_disable(); ++ current->softirq_nestcnt++; ++ ++ do_current_softirqs(1); ++ current->softirq_nestcnt--; ++ rcu_note_context_switch(cpu); ++ local_irq_enable(); ++} ++ ++/* ++ * Called from netif_rx_ni(). Preemption enabled, but migration ++ * disabled. So the cpu can't go away under us. ++ */ ++void thread_do_softirq(void) ++{ ++ if (!in_serving_softirq() && current->softirqs_raised) { ++ current->softirq_nestcnt++; ++ do_current_softirqs(0); ++ current->softirq_nestcnt--; ++ } ++} ++ ++static void do_raise_softirq_irqoff(unsigned int nr) ++{ ++ trace_softirq_raise(nr); ++ or_softirq_pending(1UL << nr); ++ ++ /* ++ * If we are not in a hard interrupt and inside a bh disabled ++ * region, we simply raise the flag on current. local_bh_enable() ++ * will make sure that the softirq is executed. Otherwise we ++ * delegate it to ksoftirqd. ++ */ ++ if (!in_irq() && current->softirq_nestcnt) ++ current->softirqs_raised |= (1U << nr); ++ else if (__this_cpu_read(ksoftirqd)) ++ __this_cpu_read(ksoftirqd)->softirqs_raised |= (1U << nr); ++} ++ ++void __raise_softirq_irqoff(unsigned int nr) ++{ ++ do_raise_softirq_irqoff(nr); ++ if (!in_irq() && !current->softirq_nestcnt) ++ wakeup_softirqd(); ++} ++ ++/* ++ * This function must run with irqs disabled! ++ */ ++void raise_softirq_irqoff(unsigned int nr) ++{ ++ do_raise_softirq_irqoff(nr); ++ ++ /* ++ * If we're in an hard interrupt we let irq return code deal ++ * with the wakeup of ksoftirqd. ++ */ ++ if (in_irq()) ++ return; ++ /* ++ * If we are in thread context but outside of a bh disabled ++ * region, we need to wake ksoftirqd as well. ++ * ++ * CHECKME: Some of the places which do that could be wrapped ++ * into local_bh_disable/enable pairs. Though it's unclear ++ * whether this is worth the effort. To find those places just ++ * raise a WARN() if the condition is met. ++ */ ++ if (!current->softirq_nestcnt) ++ wakeup_softirqd(); ++} ++ ++static inline int ksoftirqd_softirq_pending(void) ++{ ++ return current->softirqs_raised; ++} ++ ++static inline void local_bh_disable_nort(void) { } ++static inline void _local_bh_enable_nort(void) { } ++ ++static inline void ksoftirqd_set_sched_params(unsigned int cpu) ++{ ++ struct sched_param param = { .sched_priority = 1 }; ++ ++ sched_setscheduler(current, SCHED_FIFO, ¶m); ++ /* Take over all pending softirqs when starting */ ++ local_irq_disable(); ++ current->softirqs_raised = local_softirq_pending(); ++ local_irq_enable(); ++} ++ ++static inline void ksoftirqd_clr_sched_params(unsigned int cpu, bool online) ++{ ++ struct sched_param param = { .sched_priority = 0 }; ++ ++ sched_setscheduler(current, SCHED_NORMAL, ¶m); ++} ++ ++#endif /* PREEMPT_RT_FULL */ ++/* + * Enter an interrupt context. + */ + void irq_enter(void) +@@ -326,9 +732,9 @@ + * Prevent raise_softirq from needlessly waking up ksoftirqd + * here, as softirq will be serviced on return from interrupt. + */ +- local_bh_disable(); ++ local_bh_disable_nort(); + tick_irq_enter(); +- _local_bh_enable(); ++ _local_bh_enable_nort(); + } + + __irq_enter(); +@@ -336,6 +742,7 @@ + + static inline void invoke_softirq(void) + { ++#ifndef CONFIG_PREEMPT_RT_FULL + if (!force_irqthreads) { + #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK + /* +@@ -355,6 +762,15 @@ + } else { + wakeup_softirqd(); + } ++#else /* PREEMPT_RT_FULL */ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ if (__this_cpu_read(ksoftirqd) && ++ __this_cpu_read(ksoftirqd)->softirqs_raised) ++ wakeup_softirqd(); ++ local_irq_restore(flags); ++#endif + } + + static inline void tick_irq_exit(void) +@@ -391,26 +807,6 @@ + trace_hardirq_exit(); /* must be last! */ + } + +-/* +- * This function must run with irqs disabled! +- */ +-inline void raise_softirq_irqoff(unsigned int nr) +-{ +- __raise_softirq_irqoff(nr); +- +- /* +- * If we're in an interrupt or softirq, we're done +- * (this also catches softirq-disabled code). We will +- * actually run the softirq once we return from +- * the irq or softirq. +- * +- * Otherwise we wake up ksoftirqd to make sure we +- * schedule the softirq soon. +- */ +- if (!in_interrupt()) +- wakeup_softirqd(); +-} +- + void raise_softirq(unsigned int nr) + { + unsigned long flags; +@@ -420,12 +816,6 @@ + local_irq_restore(flags); + } + +-void __raise_softirq_irqoff(unsigned int nr) +-{ +- trace_softirq_raise(nr); +- or_softirq_pending(1UL << nr); +-} +- + void open_softirq(int nr, void (*action)(struct softirq_action *)) + { + softirq_vec[nr].action = action; +@@ -442,15 +832,45 @@ + static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec); + static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec); + ++static void inline ++__tasklet_common_schedule(struct tasklet_struct *t, struct tasklet_head *head, unsigned int nr) ++{ ++ if (tasklet_trylock(t)) { ++again: ++ /* We may have been preempted before tasklet_trylock ++ * and __tasklet_action may have already run. ++ * So double check the sched bit while the takslet ++ * is locked before adding it to the list. ++ */ ++ if (test_bit(TASKLET_STATE_SCHED, &t->state)) { ++ t->next = NULL; ++ *head->tail = t; ++ head->tail = &(t->next); ++ raise_softirq_irqoff(nr); ++ tasklet_unlock(t); ++ } else { ++ /* This is subtle. If we hit the corner case above ++ * It is possible that we get preempted right here, ++ * and another task has successfully called ++ * tasklet_schedule(), then this function, and ++ * failed on the trylock. Thus we must be sure ++ * before releasing the tasklet lock, that the ++ * SCHED_BIT is clear. Otherwise the tasklet ++ * may get its SCHED_BIT set, but not added to the ++ * list ++ */ ++ if (!tasklet_tryunlock(t)) ++ goto again; ++ } ++ } ++} ++ + void __tasklet_schedule(struct tasklet_struct *t) + { + unsigned long flags; + + local_irq_save(flags); +- t->next = NULL; +- *__this_cpu_read(tasklet_vec.tail) = t; +- __this_cpu_write(tasklet_vec.tail, &(t->next)); +- raise_softirq_irqoff(TASKLET_SOFTIRQ); ++ __tasklet_common_schedule(t, &__get_cpu_var(tasklet_vec), TASKLET_SOFTIRQ); + local_irq_restore(flags); + } + EXPORT_SYMBOL(__tasklet_schedule); +@@ -460,10 +880,7 @@ + unsigned long flags; + + local_irq_save(flags); +- t->next = NULL; +- *__this_cpu_read(tasklet_hi_vec.tail) = t; +- __this_cpu_write(tasklet_hi_vec.tail, &(t->next)); +- raise_softirq_irqoff(HI_SOFTIRQ); ++ __tasklet_common_schedule(t, &__get_cpu_var(tasklet_hi_vec), HI_SOFTIRQ); + local_irq_restore(flags); + } + EXPORT_SYMBOL(__tasklet_hi_schedule); +@@ -472,48 +889,116 @@ + { + BUG_ON(!irqs_disabled()); + +- t->next = __this_cpu_read(tasklet_hi_vec.head); +- __this_cpu_write(tasklet_hi_vec.head, t); +- __raise_softirq_irqoff(HI_SOFTIRQ); ++ __tasklet_hi_schedule(t); + } + EXPORT_SYMBOL(__tasklet_hi_schedule_first); + +-static void tasklet_action(struct softirq_action *a) ++void tasklet_enable(struct tasklet_struct *t) + { +- struct tasklet_struct *list; ++ if (!atomic_dec_and_test(&t->count)) ++ return; ++ if (test_and_clear_bit(TASKLET_STATE_PENDING, &t->state)) ++ tasklet_schedule(t); ++} ++EXPORT_SYMBOL(tasklet_enable); + +- local_irq_disable(); +- list = __this_cpu_read(tasklet_vec.head); +- __this_cpu_write(tasklet_vec.head, NULL); +- __this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head)); +- local_irq_enable(); ++void tasklet_hi_enable(struct tasklet_struct *t) ++{ ++ if (!atomic_dec_and_test(&t->count)) ++ return; ++ if (test_and_clear_bit(TASKLET_STATE_PENDING, &t->state)) ++ tasklet_hi_schedule(t); ++} ++EXPORT_SYMBOL(tasklet_hi_enable); ++ ++static void __tasklet_action(struct softirq_action *a, ++ struct tasklet_struct *list) ++{ ++ int loops = 1000000; + + while (list) { + struct tasklet_struct *t = list; + + list = list->next; + +- if (tasklet_trylock(t)) { +- if (!atomic_read(&t->count)) { +- if (!test_and_clear_bit(TASKLET_STATE_SCHED, +- &t->state)) +- BUG(); +- t->func(t->data); +- tasklet_unlock(t); +- continue; +- } +- tasklet_unlock(t); ++ /* ++ * Should always succeed - after a tasklist got on the ++ * list (after getting the SCHED bit set from 0 to 1), ++ * nothing but the tasklet softirq it got queued to can ++ * lock it: ++ */ ++ if (!tasklet_trylock(t)) { ++ WARN_ON(1); ++ continue; + } + +- local_irq_disable(); + t->next = NULL; +- *__this_cpu_read(tasklet_vec.tail) = t; +- __this_cpu_write(tasklet_vec.tail, &(t->next)); +- __raise_softirq_irqoff(TASKLET_SOFTIRQ); +- local_irq_enable(); ++ ++ /* ++ * If we cannot handle the tasklet because it's disabled, ++ * mark it as pending. tasklet_enable() will later ++ * re-schedule the tasklet. ++ */ ++ if (unlikely(atomic_read(&t->count))) { ++out_disabled: ++ /* implicit unlock: */ ++ wmb(); ++ t->state = TASKLET_STATEF_PENDING; ++ continue; ++ } ++ ++ /* ++ * After this point on the tasklet might be rescheduled ++ * on another CPU, but it can only be added to another ++ * CPU's tasklet list if we unlock the tasklet (which we ++ * dont do yet). ++ */ ++ if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) ++ WARN_ON(1); ++ ++again: ++ t->func(t->data); ++ ++ /* ++ * Try to unlock the tasklet. We must use cmpxchg, because ++ * another CPU might have scheduled or disabled the tasklet. ++ * We only allow the STATE_RUN -> 0 transition here. ++ */ ++ while (!tasklet_tryunlock(t)) { ++ /* ++ * If it got disabled meanwhile, bail out: ++ */ ++ if (atomic_read(&t->count)) ++ goto out_disabled; ++ /* ++ * If it got scheduled meanwhile, re-execute ++ * the tasklet function: ++ */ ++ if (test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) ++ goto again; ++ if (!--loops) { ++ printk("hm, tasklet state: %08lx\n", t->state); ++ WARN_ON(1); ++ tasklet_unlock(t); ++ break; ++ } ++ } + } + } + ++static void tasklet_action(struct softirq_action *a) ++{ ++ struct tasklet_struct *list; ++ ++ local_irq_disable(); ++ list = __get_cpu_var(tasklet_vec).head; ++ __get_cpu_var(tasklet_vec).head = NULL; ++ __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head; ++ local_irq_enable(); ++ ++ __tasklet_action(a, list); ++} ++ + static void tasklet_hi_action(struct softirq_action *a) + { + struct tasklet_struct *list; +@@ -524,30 +1009,7 @@ + __this_cpu_write(tasklet_hi_vec.tail, this_cpu_ptr(&tasklet_hi_vec.head)); + local_irq_enable(); + +- while (list) { +- struct tasklet_struct *t = list; +- +- list = list->next; +- +- if (tasklet_trylock(t)) { +- if (!atomic_read(&t->count)) { +- if (!test_and_clear_bit(TASKLET_STATE_SCHED, +- &t->state)) +- BUG(); +- t->func(t->data); +- tasklet_unlock(t); +- continue; +- } +- tasklet_unlock(t); +- } +- +- local_irq_disable(); +- t->next = NULL; +- *__this_cpu_read(tasklet_hi_vec.tail) = t; +- __this_cpu_write(tasklet_hi_vec.tail, &(t->next)); +- __raise_softirq_irqoff(HI_SOFTIRQ); +- local_irq_enable(); +- } ++ __tasklet_action(a, list); + } + + void tasklet_init(struct tasklet_struct *t, +@@ -568,7 +1030,7 @@ + + while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) { + do { +- yield(); ++ msleep(1); + } while (test_bit(TASKLET_STATE_SCHED, &t->state)); + } + tasklet_unlock_wait(t); +@@ -642,26 +1104,26 @@ + open_softirq(HI_SOFTIRQ, tasklet_hi_action); + } + +-static int ksoftirqd_should_run(unsigned int cpu) +-{ +- return local_softirq_pending(); +-} +- +-static void run_ksoftirqd(unsigned int cpu) ++#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) ++void tasklet_unlock_wait(struct tasklet_struct *t) + { +- local_irq_disable(); +- if (local_softirq_pending()) { ++ while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { + /* +- * We can safely run softirq on inline stack, as we are not deep +- * in the task stack here. ++ * Hack for now to avoid this busy-loop: + */ +- __do_softirq(); +- rcu_note_context_switch(cpu); +- local_irq_enable(); +- cond_resched(); +- return; ++#ifdef CONFIG_PREEMPT_RT_FULL ++ msleep(1); ++#else ++ barrier(); ++#endif + } +- local_irq_enable(); ++} ++EXPORT_SYMBOL(tasklet_unlock_wait); ++#endif ++ ++static int ksoftirqd_should_run(unsigned int cpu) ++{ ++ return ksoftirqd_softirq_pending(); + } + + #ifdef CONFIG_HOTPLUG_CPU +@@ -743,6 +1205,8 @@ + + static struct smp_hotplug_thread softirq_threads = { + .store = &ksoftirqd, ++ .setup = ksoftirqd_set_sched_params, ++ .cleanup = ksoftirqd_clr_sched_params, + .thread_should_run = ksoftirqd_should_run, + .thread_fn = run_ksoftirqd, + .thread_comm = "ksoftirqd/%u", +diff -Nur linux-3.18.14.orig/kernel/stop_machine.c linux-3.18.14-rt/kernel/stop_machine.c +--- linux-3.18.14.orig/kernel/stop_machine.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/stop_machine.c 2015-05-31 15:32:48.925635362 -0500 +@@ -30,12 +30,12 @@ + atomic_t nr_todo; /* nr left to execute */ + bool executed; /* actually executed? */ + int ret; /* collected return value */ +- struct completion completion; /* fired if nr_todo reaches 0 */ ++ struct task_struct *waiter; /* woken when nr_todo reaches 0 */ + }; + + /* the actual stopper, one per every possible cpu, enabled on online cpus */ + struct cpu_stopper { +- spinlock_t lock; ++ raw_spinlock_t lock; + bool enabled; /* is this stopper enabled? */ + struct list_head works; /* list of pending works */ + }; +@@ -56,7 +56,7 @@ + { + memset(done, 0, sizeof(*done)); + atomic_set(&done->nr_todo, nr_todo); +- init_completion(&done->completion); ++ done->waiter = current; + } + + /* signal completion unless @done is NULL */ +@@ -65,8 +65,10 @@ + if (done) { + if (executed) + done->executed = true; +- if (atomic_dec_and_test(&done->nr_todo)) +- complete(&done->completion); ++ if (atomic_dec_and_test(&done->nr_todo)) { ++ wake_up_process(done->waiter); ++ done->waiter = NULL; ++ } + } + } + +@@ -78,7 +80,7 @@ + + unsigned long flags; + +- spin_lock_irqsave(&stopper->lock, flags); ++ raw_spin_lock_irqsave(&stopper->lock, flags); + + if (stopper->enabled) { + list_add_tail(&work->list, &stopper->works); +@@ -86,7 +88,23 @@ + } else + cpu_stop_signal_done(work->done, false); + +- spin_unlock_irqrestore(&stopper->lock, flags); ++ raw_spin_unlock_irqrestore(&stopper->lock, flags); ++} ++ ++static void wait_for_stop_done(struct cpu_stop_done *done) ++{ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ while (atomic_read(&done->nr_todo)) { ++ schedule(); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ } ++ /* ++ * We need to wait until cpu_stop_signal_done() has cleared ++ * done->waiter. ++ */ ++ while (done->waiter) ++ cpu_relax(); ++ set_current_state(TASK_RUNNING); + } + + /** +@@ -120,7 +138,7 @@ + + cpu_stop_init_done(&done, 1); + cpu_stop_queue_work(cpu, &work); +- wait_for_completion(&done.completion); ++ wait_for_stop_done(&done); + return done.executed ? done.ret : -ENOENT; + } + +@@ -248,7 +266,7 @@ + struct irq_cpu_stop_queue_work_info call_args; + struct multi_stop_data msdata; + +- preempt_disable(); ++ preempt_disable_nort(); + msdata = (struct multi_stop_data){ + .fn = fn, + .data = arg, +@@ -281,7 +299,7 @@ + * This relies on the stopper workqueues to be FIFO. + */ + if (!cpu_active(cpu1) || !cpu_active(cpu2)) { +- preempt_enable(); ++ preempt_enable_nort(); + return -ENOENT; + } + +@@ -295,9 +313,9 @@ + &irq_cpu_stop_queue_work, + &call_args, 1); + lg_local_unlock(&stop_cpus_lock); +- preempt_enable(); ++ preempt_enable_nort(); + +- wait_for_completion(&done.completion); ++ wait_for_stop_done(&done); + + return done.executed ? done.ret : -ENOENT; + } +@@ -329,7 +347,7 @@ + + static void queue_stop_cpus_work(const struct cpumask *cpumask, + cpu_stop_fn_t fn, void *arg, +- struct cpu_stop_done *done) ++ struct cpu_stop_done *done, bool inactive) + { + struct cpu_stop_work *work; + unsigned int cpu; +@@ -343,11 +361,13 @@ + } + + /* +- * Disable preemption while queueing to avoid getting +- * preempted by a stopper which might wait for other stoppers +- * to enter @fn which can lead to deadlock. ++ * Make sure that all work is queued on all cpus before ++ * any of the cpus can execute it. + */ +- lg_global_lock(&stop_cpus_lock); ++ if (!inactive) ++ lg_global_lock(&stop_cpus_lock); ++ else ++ lg_global_trylock_relax(&stop_cpus_lock); + for_each_cpu(cpu, cpumask) + cpu_stop_queue_work(cpu, &per_cpu(stop_cpus_work, cpu)); + lg_global_unlock(&stop_cpus_lock); +@@ -359,8 +379,8 @@ + struct cpu_stop_done done; + + cpu_stop_init_done(&done, cpumask_weight(cpumask)); +- queue_stop_cpus_work(cpumask, fn, arg, &done); +- wait_for_completion(&done.completion); ++ queue_stop_cpus_work(cpumask, fn, arg, &done, false); ++ wait_for_stop_done(&done); + return done.executed ? done.ret : -ENOENT; + } + +@@ -439,9 +459,9 @@ + unsigned long flags; + int run; + +- spin_lock_irqsave(&stopper->lock, flags); ++ raw_spin_lock_irqsave(&stopper->lock, flags); + run = !list_empty(&stopper->works); +- spin_unlock_irqrestore(&stopper->lock, flags); ++ raw_spin_unlock_irqrestore(&stopper->lock, flags); + return run; + } + +@@ -453,13 +473,13 @@ + + repeat: + work = NULL; +- spin_lock_irq(&stopper->lock); ++ raw_spin_lock_irq(&stopper->lock); + if (!list_empty(&stopper->works)) { + work = list_first_entry(&stopper->works, + struct cpu_stop_work, list); + list_del_init(&work->list); + } +- spin_unlock_irq(&stopper->lock); ++ raw_spin_unlock_irq(&stopper->lock); + + if (work) { + cpu_stop_fn_t fn = work->fn; +@@ -467,6 +487,16 @@ + struct cpu_stop_done *done = work->done; + char ksym_buf[KSYM_NAME_LEN] __maybe_unused; + ++ /* ++ * Wait until the stopper finished scheduling on all ++ * cpus ++ */ ++ lg_global_lock(&stop_cpus_lock); ++ /* ++ * Let other cpu threads continue as well ++ */ ++ lg_global_unlock(&stop_cpus_lock); ++ + /* cpu stop callbacks are not allowed to sleep */ + preempt_disable(); + +@@ -481,7 +511,13 @@ + kallsyms_lookup((unsigned long)fn, NULL, NULL, NULL, + ksym_buf), arg); + ++ /* ++ * Make sure that the wakeup and setting done->waiter ++ * to NULL is atomic. ++ */ ++ local_irq_disable(); + cpu_stop_signal_done(done, true); ++ local_irq_enable(); + goto repeat; + } + } +@@ -500,20 +536,20 @@ + unsigned long flags; + + /* drain remaining works */ +- spin_lock_irqsave(&stopper->lock, flags); ++ raw_spin_lock_irqsave(&stopper->lock, flags); + list_for_each_entry(work, &stopper->works, list) + cpu_stop_signal_done(work->done, false); + stopper->enabled = false; +- spin_unlock_irqrestore(&stopper->lock, flags); ++ raw_spin_unlock_irqrestore(&stopper->lock, flags); + } + + static void cpu_stop_unpark(unsigned int cpu) + { + struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); + +- spin_lock_irq(&stopper->lock); ++ raw_spin_lock_irq(&stopper->lock); + stopper->enabled = true; +- spin_unlock_irq(&stopper->lock); ++ raw_spin_unlock_irq(&stopper->lock); + } + + static struct smp_hotplug_thread cpu_stop_threads = { +@@ -535,10 +571,12 @@ + for_each_possible_cpu(cpu) { + struct cpu_stopper *stopper = &per_cpu(cpu_stopper, cpu); + +- spin_lock_init(&stopper->lock); ++ raw_spin_lock_init(&stopper->lock); + INIT_LIST_HEAD(&stopper->works); + } + ++ lg_lock_init(&stop_cpus_lock, "stop_cpus_lock"); ++ + BUG_ON(smpboot_register_percpu_thread(&cpu_stop_threads)); + stop_machine_initialized = true; + return 0; +@@ -634,11 +672,11 @@ + set_state(&msdata, MULTI_STOP_PREPARE); + cpu_stop_init_done(&done, num_active_cpus()); + queue_stop_cpus_work(cpu_active_mask, multi_cpu_stop, &msdata, +- &done); ++ &done, true); + ret = multi_cpu_stop(&msdata); + + /* Busy wait for completion. */ +- while (!completion_done(&done.completion)) ++ while (atomic_read(&done.nr_todo)) + cpu_relax(); + + mutex_unlock(&stop_cpus_mutex); +diff -Nur linux-3.18.14.orig/kernel/time/hrtimer.c linux-3.18.14-rt/kernel/time/hrtimer.c +--- linux-3.18.14.orig/kernel/time/hrtimer.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/hrtimer.c 2015-05-31 15:32:48.925635362 -0500 +@@ -48,11 +48,13 @@ + #include + #include + #include ++#include + #include + + #include + + #include ++#include + + #include "timekeeping.h" + +@@ -568,8 +570,7 @@ + * When the callback is running, we do not reprogram the clock event + * device. The timer callback is either running on a different CPU or + * the callback is executed in the hrtimer_interrupt context. The +- * reprogramming is handled either by the softirq, which called the +- * callback or at the end of the hrtimer_interrupt. ++ * reprogramming is handled at the end of the hrtimer_interrupt. + */ + if (hrtimer_callback_running(timer)) + return 0; +@@ -604,6 +605,9 @@ + return res; + } + ++static void __run_hrtimer(struct hrtimer *timer, ktime_t *now); ++static int hrtimer_rt_defer(struct hrtimer *timer); ++ + /* + * Initialize the high resolution related parts of cpu_base + */ +@@ -613,6 +617,21 @@ + base->hres_active = 0; + } + ++static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, ++ struct hrtimer_clock_base *base, ++ int wakeup) ++{ ++ if (!hrtimer_reprogram(timer, base)) ++ return 0; ++ if (!wakeup) ++ return -ETIME; ++#ifdef CONFIG_PREEMPT_RT_BASE ++ if (!hrtimer_rt_defer(timer)) ++ return -ETIME; ++#endif ++ return 1; ++} ++ + static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base) + { + ktime_t *offs_real = &base->clock_base[HRTIMER_BASE_REALTIME].offset; +@@ -678,6 +697,44 @@ + + static DECLARE_WORK(hrtimer_work, clock_was_set_work); + ++#ifdef CONFIG_PREEMPT_RT_FULL ++/* ++ * RT can not call schedule_work from real interrupt context. ++ * Need to make a thread to do the real work. ++ */ ++static struct task_struct *clock_set_delay_thread; ++static bool do_clock_set_delay; ++ ++static int run_clock_set_delay(void *ignore) ++{ ++ while (!kthread_should_stop()) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (do_clock_set_delay) { ++ do_clock_set_delay = false; ++ schedule_work(&hrtimer_work); ++ } ++ schedule(); ++ } ++ __set_current_state(TASK_RUNNING); ++ return 0; ++} ++ ++void clock_was_set_delayed(void) ++{ ++ do_clock_set_delay = true; ++ /* Make visible before waking up process */ ++ smp_wmb(); ++ wake_up_process(clock_set_delay_thread); ++} ++ ++static __init int create_clock_set_delay_thread(void) ++{ ++ clock_set_delay_thread = kthread_run(run_clock_set_delay, NULL, "kclksetdelayd"); ++ BUG_ON(!clock_set_delay_thread); ++ return 0; ++} ++early_initcall(create_clock_set_delay_thread); ++#else /* PREEMPT_RT_FULL */ + /* + * Called from timekeeping and resume code to reprogramm the hrtimer + * interrupt device on all cpus. +@@ -686,6 +743,7 @@ + { + schedule_work(&hrtimer_work); + } ++#endif + + #else + +@@ -694,6 +752,13 @@ + static inline int hrtimer_switch_to_hres(void) { return 0; } + static inline void + hrtimer_force_reprogram(struct hrtimer_cpu_base *base, int skip_equal) { } ++static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, ++ struct hrtimer_clock_base *base, ++ int wakeup) ++{ ++ return 0; ++} ++ + static inline int hrtimer_reprogram(struct hrtimer *timer, + struct hrtimer_clock_base *base) + { +@@ -701,7 +766,6 @@ + } + static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) { } + static inline void retrigger_next_event(void *arg) { } +- + #endif /* CONFIG_HIGH_RES_TIMERS */ + + /* +@@ -819,6 +883,32 @@ + } + EXPORT_SYMBOL_GPL(hrtimer_forward); + ++#ifdef CONFIG_PREEMPT_RT_BASE ++# define wake_up_timer_waiters(b) wake_up(&(b)->wait) ++ ++/** ++ * hrtimer_wait_for_timer - Wait for a running timer ++ * ++ * @timer: timer to wait for ++ * ++ * The function waits in case the timers callback function is ++ * currently executed on the waitqueue of the timer base. The ++ * waitqueue is woken up after the timer callback function has ++ * finished execution. ++ */ ++void hrtimer_wait_for_timer(const struct hrtimer *timer) ++{ ++ struct hrtimer_clock_base *base = timer->base; ++ ++ if (base && base->cpu_base && !timer->irqsafe) ++ wait_event(base->cpu_base->wait, ++ !(timer->state & HRTIMER_STATE_CALLBACK)); ++} ++ ++#else ++# define wake_up_timer_waiters(b) do { } while (0) ++#endif ++ + /* + * enqueue_hrtimer - internal function to (re)start a timer + * +@@ -862,6 +952,11 @@ + if (!(timer->state & HRTIMER_STATE_ENQUEUED)) + goto out; + ++ if (unlikely(!list_empty(&timer->cb_entry))) { ++ list_del_init(&timer->cb_entry); ++ goto out; ++ } ++ + next_timer = timerqueue_getnext(&base->active); + timerqueue_del(&base->active, &timer->node); + if (&timer->node == next_timer) { +@@ -949,7 +1044,16 @@ + new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED); + + timer_stats_hrtimer_set_start_info(timer); ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ { ++ ktime_t now = new_base->get_time(); + ++ if (ktime_to_ns(tim) < ktime_to_ns(now)) ++ timer->praecox = now; ++ else ++ timer->praecox = ktime_set(0, 0); ++ } ++#endif + leftmost = enqueue_hrtimer(timer, new_base); + + if (!leftmost) { +@@ -963,15 +1067,26 @@ + * on dynticks target. + */ + wake_up_nohz_cpu(new_base->cpu_base->cpu); +- } else if (new_base->cpu_base == this_cpu_ptr(&hrtimer_bases) && +- hrtimer_reprogram(timer, new_base)) { ++ } else if (new_base->cpu_base == this_cpu_ptr(&hrtimer_bases)) { ++ ++ ret = hrtimer_enqueue_reprogram(timer, new_base, wakeup); ++ if (ret < 0) { ++ /* ++ * In case we failed to reprogram the timer (mostly ++ * because out current timer is already elapsed), ++ * remove it again and report a failure. This avoids ++ * stale base->first entries. ++ */ ++ debug_deactivate(timer); ++ __remove_hrtimer(timer, new_base, ++ timer->state & HRTIMER_STATE_CALLBACK, 0); ++ } else if (ret > 0) { + /* + * Only allow reprogramming if the new base is on this CPU. + * (it might still be on another CPU if the timer was pending) + * + * XXX send_remote_softirq() ? + */ +- if (wakeup) { + /* + * We need to drop cpu_base->lock to avoid a + * lock ordering issue vs. rq->lock. +@@ -979,9 +1094,7 @@ + raw_spin_unlock(&new_base->cpu_base->lock); + raise_softirq_irqoff(HRTIMER_SOFTIRQ); + local_irq_restore(flags); +- return ret; +- } else { +- __raise_softirq_irqoff(HRTIMER_SOFTIRQ); ++ return 0; + } + } + +@@ -1072,7 +1185,7 @@ + + if (ret >= 0) + return ret; +- cpu_relax(); ++ hrtimer_wait_for_timer(timer); + } + } + EXPORT_SYMBOL_GPL(hrtimer_cancel); +@@ -1151,6 +1264,7 @@ + + base = hrtimer_clockid_to_base(clock_id); + timer->base = &cpu_base->clock_base[base]; ++ INIT_LIST_HEAD(&timer->cb_entry); + timerqueue_init(&timer->node); + + #ifdef CONFIG_TIMER_STATS +@@ -1234,6 +1348,126 @@ + timer->state &= ~HRTIMER_STATE_CALLBACK; + } + ++static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer); ++ ++#ifdef CONFIG_PREEMPT_RT_BASE ++static void hrtimer_rt_reprogram(int restart, struct hrtimer *timer, ++ struct hrtimer_clock_base *base) ++{ ++ /* ++ * Note, we clear the callback flag before we requeue the ++ * timer otherwise we trigger the callback_running() check ++ * in hrtimer_reprogram(). ++ */ ++ timer->state &= ~HRTIMER_STATE_CALLBACK; ++ ++ if (restart != HRTIMER_NORESTART) { ++ BUG_ON(hrtimer_active(timer)); ++ /* ++ * Enqueue the timer, if it's the leftmost timer then ++ * we need to reprogram it. ++ */ ++ if (!enqueue_hrtimer(timer, base)) ++ return; ++ ++#ifndef CONFIG_HIGH_RES_TIMERS ++ } ++#else ++ if (base->cpu_base->hres_active && ++ hrtimer_reprogram(timer, base)) ++ goto requeue; ++ ++ } else if (hrtimer_active(timer)) { ++ /* ++ * If the timer was rearmed on another CPU, reprogram ++ * the event device. ++ */ ++ if (&timer->node == base->active.next && ++ base->cpu_base->hres_active && ++ hrtimer_reprogram(timer, base)) ++ goto requeue; ++ } ++ return; ++ ++requeue: ++ /* ++ * Timer is expired. Thus move it from tree to pending list ++ * again. ++ */ ++ __remove_hrtimer(timer, base, timer->state, 0); ++ list_add_tail(&timer->cb_entry, &base->expired); ++#endif ++} ++ ++/* ++ * The changes in mainline which removed the callback modes from ++ * hrtimer are not yet working with -rt. The non wakeup_process() ++ * based callbacks which involve sleeping locks need to be treated ++ * seperately. ++ */ ++static void hrtimer_rt_run_pending(void) ++{ ++ enum hrtimer_restart (*fn)(struct hrtimer *); ++ struct hrtimer_cpu_base *cpu_base; ++ struct hrtimer_clock_base *base; ++ struct hrtimer *timer; ++ int index, restart; ++ ++ local_irq_disable(); ++ cpu_base = &per_cpu(hrtimer_bases, smp_processor_id()); ++ ++ raw_spin_lock(&cpu_base->lock); ++ ++ for (index = 0; index < HRTIMER_MAX_CLOCK_BASES; index++) { ++ base = &cpu_base->clock_base[index]; ++ ++ while (!list_empty(&base->expired)) { ++ timer = list_first_entry(&base->expired, ++ struct hrtimer, cb_entry); ++ ++ /* ++ * Same as the above __run_hrtimer function ++ * just we run with interrupts enabled. ++ */ ++ debug_hrtimer_deactivate(timer); ++ __remove_hrtimer(timer, base, HRTIMER_STATE_CALLBACK, 0); ++ timer_stats_account_hrtimer(timer); ++ fn = timer->function; ++ ++ raw_spin_unlock_irq(&cpu_base->lock); ++ restart = fn(timer); ++ raw_spin_lock_irq(&cpu_base->lock); ++ ++ hrtimer_rt_reprogram(restart, timer, base); ++ } ++ } ++ ++ raw_spin_unlock_irq(&cpu_base->lock); ++ ++ wake_up_timer_waiters(cpu_base); ++} ++ ++static int hrtimer_rt_defer(struct hrtimer *timer) ++{ ++ if (timer->irqsafe) ++ return 0; ++ ++ __remove_hrtimer(timer, timer->base, timer->state, 0); ++ list_add_tail(&timer->cb_entry, &timer->base->expired); ++ return 1; ++} ++ ++#else ++ ++static inline void hrtimer_rt_run_pending(void) ++{ ++ hrtimer_peek_ahead_timers(); ++} ++ ++static inline int hrtimer_rt_defer(struct hrtimer *timer) { return 0; } ++ ++#endif ++ + #ifdef CONFIG_HIGH_RES_TIMERS + + /* +@@ -1244,7 +1478,7 @@ + { + struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases); + ktime_t expires_next, now, entry_time, delta; +- int i, retries = 0; ++ int i, retries = 0, raise = 0; + + BUG_ON(!cpu_base->hres_active); + cpu_base->nr_events++; +@@ -1279,6 +1513,15 @@ + + timer = container_of(node, struct hrtimer, node); + ++ trace_hrtimer_interrupt(raw_smp_processor_id(), ++ ktime_to_ns(ktime_sub(ktime_to_ns(timer->praecox) ? ++ timer->praecox : hrtimer_get_expires(timer), ++ basenow)), ++ current, ++ timer->function == hrtimer_wakeup ? ++ container_of(timer, struct hrtimer_sleeper, ++ timer)->task : NULL); ++ + /* + * The immediate goal for using the softexpires is + * minimizing wakeups, not running timers at the +@@ -1304,7 +1547,10 @@ + break; + } + +- __run_hrtimer(timer, &basenow); ++ if (!hrtimer_rt_defer(timer)) ++ __run_hrtimer(timer, &basenow); ++ else ++ raise = 1; + } + } + +@@ -1319,7 +1565,7 @@ + if (expires_next.tv64 == KTIME_MAX || + !tick_program_event(expires_next, 0)) { + cpu_base->hang_detected = 0; +- return; ++ goto out; + } + + /* +@@ -1363,6 +1609,9 @@ + tick_program_event(expires_next, 1); + printk_once(KERN_WARNING "hrtimer: interrupt took %llu ns\n", + ktime_to_ns(delta)); ++out: ++ if (raise) ++ raise_softirq_irqoff(HRTIMER_SOFTIRQ); + } + + /* +@@ -1398,18 +1647,18 @@ + __hrtimer_peek_ahead_timers(); + local_irq_restore(flags); + } +- +-static void run_hrtimer_softirq(struct softirq_action *h) +-{ +- hrtimer_peek_ahead_timers(); +-} +- + #else /* CONFIG_HIGH_RES_TIMERS */ + + static inline void __hrtimer_peek_ahead_timers(void) { } + + #endif /* !CONFIG_HIGH_RES_TIMERS */ + ++ ++static void run_hrtimer_softirq(struct softirq_action *h) ++{ ++ hrtimer_rt_run_pending(); ++} ++ + /* + * Called from timer softirq every jiffy, expire hrtimers: + * +@@ -1442,7 +1691,7 @@ + struct timerqueue_node *node; + struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases); + struct hrtimer_clock_base *base; +- int index, gettime = 1; ++ int index, gettime = 1, raise = 0; + + if (hrtimer_hres_active()) + return; +@@ -1467,10 +1716,16 @@ + hrtimer_get_expires_tv64(timer)) + break; + +- __run_hrtimer(timer, &base->softirq_time); ++ if (!hrtimer_rt_defer(timer)) ++ __run_hrtimer(timer, &base->softirq_time); ++ else ++ raise = 1; + } + raw_spin_unlock(&cpu_base->lock); + } ++ ++ if (raise) ++ raise_softirq_irqoff(HRTIMER_SOFTIRQ); + } + + /* +@@ -1492,16 +1747,18 @@ + void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task) + { + sl->timer.function = hrtimer_wakeup; ++ sl->timer.irqsafe = 1; + sl->task = task; + } + EXPORT_SYMBOL_GPL(hrtimer_init_sleeper); + +-static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode) ++static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode, ++ unsigned long state) + { + hrtimer_init_sleeper(t, current); + + do { +- set_current_state(TASK_INTERRUPTIBLE); ++ set_current_state(state); + hrtimer_start_expires(&t->timer, mode); + if (!hrtimer_active(&t->timer)) + t->task = NULL; +@@ -1545,7 +1802,8 @@ + HRTIMER_MODE_ABS); + hrtimer_set_expires_tv64(&t.timer, restart->nanosleep.expires); + +- if (do_nanosleep(&t, HRTIMER_MODE_ABS)) ++ /* cpu_chill() does not care about restart state. */ ++ if (do_nanosleep(&t, HRTIMER_MODE_ABS, TASK_INTERRUPTIBLE)) + goto out; + + rmtp = restart->nanosleep.rmtp; +@@ -1562,8 +1820,10 @@ + return ret; + } + +-long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, +- const enum hrtimer_mode mode, const clockid_t clockid) ++static long ++__hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, ++ const enum hrtimer_mode mode, const clockid_t clockid, ++ unsigned long state) + { + struct restart_block *restart; + struct hrtimer_sleeper t; +@@ -1576,7 +1836,7 @@ + + hrtimer_init_on_stack(&t.timer, clockid, mode); + hrtimer_set_expires_range_ns(&t.timer, timespec_to_ktime(*rqtp), slack); +- if (do_nanosleep(&t, mode)) ++ if (do_nanosleep(&t, mode, state)) + goto out; + + /* Absolute timers do not update the rmtp value and restart: */ +@@ -1603,6 +1863,12 @@ + return ret; + } + ++long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, ++ const enum hrtimer_mode mode, const clockid_t clockid) ++{ ++ return __hrtimer_nanosleep(rqtp, rmtp, mode, clockid, TASK_INTERRUPTIBLE); ++} ++ + SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, + struct timespec __user *, rmtp) + { +@@ -1617,6 +1883,26 @@ + return hrtimer_nanosleep(&tu, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC); + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++/* ++ * Sleep for 1 ms in hope whoever holds what we want will let it go. ++ */ ++void cpu_chill(void) ++{ ++ struct timespec tu = { ++ .tv_nsec = NSEC_PER_MSEC, ++ }; ++ unsigned int freeze_flag = current->flags & PF_NOFREEZE; ++ ++ current->flags |= PF_NOFREEZE; ++ __hrtimer_nanosleep(&tu, NULL, HRTIMER_MODE_REL, CLOCK_MONOTONIC, ++ TASK_UNINTERRUPTIBLE); ++ if (!freeze_flag) ++ current->flags &= ~PF_NOFREEZE; ++} ++EXPORT_SYMBOL(cpu_chill); ++#endif ++ + /* + * Functions related to boot-time initialization: + */ +@@ -1628,10 +1914,14 @@ + for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { + cpu_base->clock_base[i].cpu_base = cpu_base; + timerqueue_init_head(&cpu_base->clock_base[i].active); ++ INIT_LIST_HEAD(&cpu_base->clock_base[i].expired); + } + + cpu_base->cpu = cpu; + hrtimer_init_hres(cpu_base); ++#ifdef CONFIG_PREEMPT_RT_BASE ++ init_waitqueue_head(&cpu_base->wait); ++#endif + } + + #ifdef CONFIG_HOTPLUG_CPU +@@ -1744,9 +2034,7 @@ + hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE, + (void *)(long)smp_processor_id()); + register_cpu_notifier(&hrtimers_nb); +-#ifdef CONFIG_HIGH_RES_TIMERS + open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq); +-#endif + } + + /** +diff -Nur linux-3.18.14.orig/kernel/time/itimer.c linux-3.18.14-rt/kernel/time/itimer.c +--- linux-3.18.14.orig/kernel/time/itimer.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/itimer.c 2015-05-31 15:32:48.957635362 -0500 +@@ -213,6 +213,7 @@ + /* We are sharing ->siglock with it_real_fn() */ + if (hrtimer_try_to_cancel(timer) < 0) { + spin_unlock_irq(&tsk->sighand->siglock); ++ hrtimer_wait_for_timer(&tsk->signal->real_timer); + goto again; + } + expires = timeval_to_ktime(value->it_value); +diff -Nur linux-3.18.14.orig/kernel/time/jiffies.c linux-3.18.14-rt/kernel/time/jiffies.c +--- linux-3.18.14.orig/kernel/time/jiffies.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/jiffies.c 2015-05-31 15:32:48.957635362 -0500 +@@ -73,7 +73,8 @@ + .shift = JIFFIES_SHIFT, + }; + +-__cacheline_aligned_in_smp DEFINE_SEQLOCK(jiffies_lock); ++__cacheline_aligned_in_smp DEFINE_RAW_SPINLOCK(jiffies_lock); ++__cacheline_aligned_in_smp seqcount_t jiffies_seq; + + #if (BITS_PER_LONG < 64) + u64 get_jiffies_64(void) +@@ -82,9 +83,9 @@ + u64 ret; + + do { +- seq = read_seqbegin(&jiffies_lock); ++ seq = read_seqcount_begin(&jiffies_seq); + ret = jiffies_64; +- } while (read_seqretry(&jiffies_lock, seq)); ++ } while (read_seqcount_retry(&jiffies_seq, seq)); + return ret; + } + EXPORT_SYMBOL(get_jiffies_64); +diff -Nur linux-3.18.14.orig/kernel/time/ntp.c linux-3.18.14-rt/kernel/time/ntp.c +--- linux-3.18.14.orig/kernel/time/ntp.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/ntp.c 2015-05-31 15:32:48.957635362 -0500 +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -519,10 +520,52 @@ + &sync_cmos_work, timespec_to_jiffies(&next)); + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++/* ++ * RT can not call schedule_delayed_work from real interrupt context. ++ * Need to make a thread to do the real work. ++ */ ++static struct task_struct *cmos_delay_thread; ++static bool do_cmos_delay; ++ ++static int run_cmos_delay(void *ignore) ++{ ++ while (!kthread_should_stop()) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (do_cmos_delay) { ++ do_cmos_delay = false; ++ queue_delayed_work(system_power_efficient_wq, ++ &sync_cmos_work, 0); ++ } ++ schedule(); ++ } ++ __set_current_state(TASK_RUNNING); ++ return 0; ++} ++ ++void ntp_notify_cmos_timer(void) ++{ ++ do_cmos_delay = true; ++ /* Make visible before waking up process */ ++ smp_wmb(); ++ wake_up_process(cmos_delay_thread); ++} ++ ++static __init int create_cmos_delay_thread(void) ++{ ++ cmos_delay_thread = kthread_run(run_cmos_delay, NULL, "kcmosdelayd"); ++ BUG_ON(!cmos_delay_thread); ++ return 0; ++} ++early_initcall(create_cmos_delay_thread); ++ ++#else ++ + void ntp_notify_cmos_timer(void) + { + queue_delayed_work(system_power_efficient_wq, &sync_cmos_work, 0); + } ++#endif /* CONFIG_PREEMPT_RT_FULL */ + + #else + void ntp_notify_cmos_timer(void) { } +diff -Nur linux-3.18.14.orig/kernel/time/posix-cpu-timers.c linux-3.18.14-rt/kernel/time/posix-cpu-timers.c +--- linux-3.18.14.orig/kernel/time/posix-cpu-timers.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/posix-cpu-timers.c 2015-05-31 15:32:48.961635362 -0500 +@@ -3,6 +3,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -626,7 +627,7 @@ + /* + * Disarm any old timer after extracting its expiry time. + */ +- WARN_ON_ONCE(!irqs_disabled()); ++ WARN_ON_ONCE_NONRT(!irqs_disabled()); + + ret = 0; + old_incr = timer->it.cpu.incr; +@@ -1047,7 +1048,7 @@ + /* + * Now re-arm for the new expiry time. + */ +- WARN_ON_ONCE(!irqs_disabled()); ++ WARN_ON_ONCE_NONRT(!irqs_disabled()); + arm_timer(timer); + unlock_task_sighand(p, &flags); + +@@ -1113,10 +1114,11 @@ + sig = tsk->signal; + if (sig->cputimer.running) { + struct task_cputime group_sample; ++ unsigned long flags; + +- raw_spin_lock(&sig->cputimer.lock); ++ raw_spin_lock_irqsave(&sig->cputimer.lock, flags); + group_sample = sig->cputimer.cputime; +- raw_spin_unlock(&sig->cputimer.lock); ++ raw_spin_unlock_irqrestore(&sig->cputimer.lock, flags); + + if (task_cputime_expired(&group_sample, &sig->cputime_expires)) + return 1; +@@ -1130,13 +1132,13 @@ + * already updated our counts. We need to check if any timers fire now. + * Interrupts are disabled. + */ +-void run_posix_cpu_timers(struct task_struct *tsk) ++static void __run_posix_cpu_timers(struct task_struct *tsk) + { + LIST_HEAD(firing); + struct k_itimer *timer, *next; + unsigned long flags; + +- WARN_ON_ONCE(!irqs_disabled()); ++ WARN_ON_ONCE_NONRT(!irqs_disabled()); + + /* + * The fast path checks that there are no expired thread or thread +@@ -1194,6 +1196,190 @@ + } + } + ++#ifdef CONFIG_PREEMPT_RT_BASE ++#include ++#include ++DEFINE_PER_CPU(struct task_struct *, posix_timer_task); ++DEFINE_PER_CPU(struct task_struct *, posix_timer_tasklist); ++ ++static int posix_cpu_timers_thread(void *data) ++{ ++ int cpu = (long)data; ++ ++ BUG_ON(per_cpu(posix_timer_task,cpu) != current); ++ ++ while (!kthread_should_stop()) { ++ struct task_struct *tsk = NULL; ++ struct task_struct *next = NULL; ++ ++ if (cpu_is_offline(cpu)) ++ goto wait_to_die; ++ ++ /* grab task list */ ++ raw_local_irq_disable(); ++ tsk = per_cpu(posix_timer_tasklist, cpu); ++ per_cpu(posix_timer_tasklist, cpu) = NULL; ++ raw_local_irq_enable(); ++ ++ /* its possible the list is empty, just return */ ++ if (!tsk) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule(); ++ __set_current_state(TASK_RUNNING); ++ continue; ++ } ++ ++ /* Process task list */ ++ while (1) { ++ /* save next */ ++ next = tsk->posix_timer_list; ++ ++ /* run the task timers, clear its ptr and ++ * unreference it ++ */ ++ __run_posix_cpu_timers(tsk); ++ tsk->posix_timer_list = NULL; ++ put_task_struct(tsk); ++ ++ /* check if this is the last on the list */ ++ if (next == tsk) ++ break; ++ tsk = next; ++ } ++ } ++ return 0; ++ ++wait_to_die: ++ /* Wait for kthread_stop */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ while (!kthread_should_stop()) { ++ schedule(); ++ set_current_state(TASK_INTERRUPTIBLE); ++ } ++ __set_current_state(TASK_RUNNING); ++ return 0; ++} ++ ++static inline int __fastpath_timer_check(struct task_struct *tsk) ++{ ++ /* tsk == current, ensure it is safe to use ->signal/sighand */ ++ if (unlikely(tsk->exit_state)) ++ return 0; ++ ++ if (!task_cputime_zero(&tsk->cputime_expires)) ++ return 1; ++ ++ if (!task_cputime_zero(&tsk->signal->cputime_expires)) ++ return 1; ++ ++ return 0; ++} ++ ++void run_posix_cpu_timers(struct task_struct *tsk) ++{ ++ unsigned long cpu = smp_processor_id(); ++ struct task_struct *tasklist; ++ ++ BUG_ON(!irqs_disabled()); ++ if(!per_cpu(posix_timer_task, cpu)) ++ return; ++ /* get per-cpu references */ ++ tasklist = per_cpu(posix_timer_tasklist, cpu); ++ ++ /* check to see if we're already queued */ ++ if (!tsk->posix_timer_list && __fastpath_timer_check(tsk)) { ++ get_task_struct(tsk); ++ if (tasklist) { ++ tsk->posix_timer_list = tasklist; ++ } else { ++ /* ++ * The list is terminated by a self-pointing ++ * task_struct ++ */ ++ tsk->posix_timer_list = tsk; ++ } ++ per_cpu(posix_timer_tasklist, cpu) = tsk; ++ ++ wake_up_process(per_cpu(posix_timer_task, cpu)); ++ } ++} ++ ++/* ++ * posix_cpu_thread_call - callback that gets triggered when a CPU is added. ++ * Here we can start up the necessary migration thread for the new CPU. ++ */ ++static int posix_cpu_thread_call(struct notifier_block *nfb, ++ unsigned long action, void *hcpu) ++{ ++ int cpu = (long)hcpu; ++ struct task_struct *p; ++ struct sched_param param; ++ ++ switch (action) { ++ case CPU_UP_PREPARE: ++ p = kthread_create(posix_cpu_timers_thread, hcpu, ++ "posixcputmr/%d",cpu); ++ if (IS_ERR(p)) ++ return NOTIFY_BAD; ++ p->flags |= PF_NOFREEZE; ++ kthread_bind(p, cpu); ++ /* Must be high prio to avoid getting starved */ ++ param.sched_priority = MAX_RT_PRIO-1; ++ sched_setscheduler(p, SCHED_FIFO, ¶m); ++ per_cpu(posix_timer_task,cpu) = p; ++ break; ++ case CPU_ONLINE: ++ /* Strictly unneccessary, as first user will wake it. */ ++ wake_up_process(per_cpu(posix_timer_task,cpu)); ++ break; ++#ifdef CONFIG_HOTPLUG_CPU ++ case CPU_UP_CANCELED: ++ /* Unbind it from offline cpu so it can run. Fall thru. */ ++ kthread_bind(per_cpu(posix_timer_task, cpu), ++ cpumask_any(cpu_online_mask)); ++ kthread_stop(per_cpu(posix_timer_task,cpu)); ++ per_cpu(posix_timer_task,cpu) = NULL; ++ break; ++ case CPU_DEAD: ++ kthread_stop(per_cpu(posix_timer_task,cpu)); ++ per_cpu(posix_timer_task,cpu) = NULL; ++ break; ++#endif ++ } ++ return NOTIFY_OK; ++} ++ ++/* Register at highest priority so that task migration (migrate_all_tasks) ++ * happens before everything else. ++ */ ++static struct notifier_block posix_cpu_thread_notifier = { ++ .notifier_call = posix_cpu_thread_call, ++ .priority = 10 ++}; ++ ++static int __init posix_cpu_thread_init(void) ++{ ++ void *hcpu = (void *)(long)smp_processor_id(); ++ /* Start one for boot CPU. */ ++ unsigned long cpu; ++ ++ /* init the per-cpu posix_timer_tasklets */ ++ for_each_possible_cpu(cpu) ++ per_cpu(posix_timer_tasklist, cpu) = NULL; ++ ++ posix_cpu_thread_call(&posix_cpu_thread_notifier, CPU_UP_PREPARE, hcpu); ++ posix_cpu_thread_call(&posix_cpu_thread_notifier, CPU_ONLINE, hcpu); ++ register_cpu_notifier(&posix_cpu_thread_notifier); ++ return 0; ++} ++early_initcall(posix_cpu_thread_init); ++#else /* CONFIG_PREEMPT_RT_BASE */ ++void run_posix_cpu_timers(struct task_struct *tsk) ++{ ++ __run_posix_cpu_timers(tsk); ++} ++#endif /* CONFIG_PREEMPT_RT_BASE */ ++ + /* + * Set one of the process-wide special case CPU timers or RLIMIT_CPU. + * The tsk->sighand->siglock must be held by the caller. +diff -Nur linux-3.18.14.orig/kernel/time/posix-timers.c linux-3.18.14-rt/kernel/time/posix-timers.c +--- linux-3.18.14.orig/kernel/time/posix-timers.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/posix-timers.c 2015-05-31 15:32:48.961635362 -0500 +@@ -499,6 +499,7 @@ + static struct pid *good_sigevent(sigevent_t * event) + { + struct task_struct *rtn = current->group_leader; ++ int sig = event->sigev_signo; + + if ((event->sigev_notify & SIGEV_THREAD_ID ) && + (!(rtn = find_task_by_vpid(event->sigev_notify_thread_id)) || +@@ -507,7 +508,8 @@ + return NULL; + + if (((event->sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE) && +- ((event->sigev_signo <= 0) || (event->sigev_signo > SIGRTMAX))) ++ (sig <= 0 || sig > SIGRTMAX || sig_kernel_only(sig) || ++ sig_kernel_coredump(sig))) + return NULL; + + return task_pid(rtn); +@@ -819,6 +821,20 @@ + return overrun; + } + ++/* ++ * Protected by RCU! ++ */ ++static void timer_wait_for_callback(struct k_clock *kc, struct k_itimer *timr) ++{ ++#ifdef CONFIG_PREEMPT_RT_FULL ++ if (kc->timer_set == common_timer_set) ++ hrtimer_wait_for_timer(&timr->it.real.timer); ++ else ++ /* FIXME: Whacky hack for posix-cpu-timers */ ++ schedule_timeout(1); ++#endif ++} ++ + /* Set a POSIX.1b interval timer. */ + /* timr->it_lock is taken. */ + static int +@@ -896,6 +912,7 @@ + if (!timr) + return -EINVAL; + ++ rcu_read_lock(); + kc = clockid_to_kclock(timr->it_clock); + if (WARN_ON_ONCE(!kc || !kc->timer_set)) + error = -EINVAL; +@@ -904,9 +921,12 @@ + + unlock_timer(timr, flag); + if (error == TIMER_RETRY) { ++ timer_wait_for_callback(kc, timr); + rtn = NULL; // We already got the old time... ++ rcu_read_unlock(); + goto retry; + } ++ rcu_read_unlock(); + + if (old_setting && !error && + copy_to_user(old_setting, &old_spec, sizeof (old_spec))) +@@ -944,10 +964,15 @@ + if (!timer) + return -EINVAL; + ++ rcu_read_lock(); + if (timer_delete_hook(timer) == TIMER_RETRY) { + unlock_timer(timer, flags); ++ timer_wait_for_callback(clockid_to_kclock(timer->it_clock), ++ timer); ++ rcu_read_unlock(); + goto retry_delete; + } ++ rcu_read_unlock(); + + spin_lock(¤t->sighand->siglock); + list_del(&timer->list); +@@ -973,8 +998,18 @@ + retry_delete: + spin_lock_irqsave(&timer->it_lock, flags); + ++ /* On RT we can race with a deletion */ ++ if (!timer->it_signal) { ++ unlock_timer(timer, flags); ++ return; ++ } ++ + if (timer_delete_hook(timer) == TIMER_RETRY) { ++ rcu_read_lock(); + unlock_timer(timer, flags); ++ timer_wait_for_callback(clockid_to_kclock(timer->it_clock), ++ timer); ++ rcu_read_unlock(); + goto retry_delete; + } + list_del(&timer->list); +diff -Nur linux-3.18.14.orig/kernel/time/tick-common.c linux-3.18.14-rt/kernel/time/tick-common.c +--- linux-3.18.14.orig/kernel/time/tick-common.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/tick-common.c 2015-05-31 15:32:48.961635362 -0500 +@@ -78,13 +78,15 @@ + static void tick_periodic(int cpu) + { + if (tick_do_timer_cpu == cpu) { +- write_seqlock(&jiffies_lock); ++ raw_spin_lock(&jiffies_lock); ++ write_seqcount_begin(&jiffies_seq); + + /* Keep track of the next tick event */ + tick_next_period = ktime_add(tick_next_period, tick_period); + + do_timer(1); +- write_sequnlock(&jiffies_lock); ++ write_seqcount_end(&jiffies_seq); ++ raw_spin_unlock(&jiffies_lock); + update_wall_time(); + } + +@@ -146,9 +148,9 @@ + ktime_t next; + + do { +- seq = read_seqbegin(&jiffies_lock); ++ seq = read_seqcount_begin(&jiffies_seq); + next = tick_next_period; +- } while (read_seqretry(&jiffies_lock, seq)); ++ } while (read_seqcount_retry(&jiffies_seq, seq)); + + clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); + +diff -Nur linux-3.18.14.orig/kernel/time/tick-internal.h linux-3.18.14-rt/kernel/time/tick-internal.h +--- linux-3.18.14.orig/kernel/time/tick-internal.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/tick-internal.h 2015-05-31 15:32:48.961635362 -0500 +@@ -6,7 +6,8 @@ + + #include "timekeeping.h" + +-extern seqlock_t jiffies_lock; ++extern raw_spinlock_t jiffies_lock; ++extern seqcount_t jiffies_seq; + + #define CS_NAME_LEN 32 + +diff -Nur linux-3.18.14.orig/kernel/time/tick-sched.c linux-3.18.14-rt/kernel/time/tick-sched.c +--- linux-3.18.14.orig/kernel/time/tick-sched.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/tick-sched.c 2015-05-31 15:32:48.961635362 -0500 +@@ -62,7 +62,8 @@ + return; + + /* Reevalute with jiffies_lock held */ +- write_seqlock(&jiffies_lock); ++ raw_spin_lock(&jiffies_lock); ++ write_seqcount_begin(&jiffies_seq); + + delta = ktime_sub(now, last_jiffies_update); + if (delta.tv64 >= tick_period.tv64) { +@@ -85,10 +86,12 @@ + /* Keep the tick_next_period variable up to date */ + tick_next_period = ktime_add(last_jiffies_update, tick_period); + } else { +- write_sequnlock(&jiffies_lock); ++ write_seqcount_end(&jiffies_seq); ++ raw_spin_unlock(&jiffies_lock); + return; + } +- write_sequnlock(&jiffies_lock); ++ write_seqcount_end(&jiffies_seq); ++ raw_spin_unlock(&jiffies_lock); + update_wall_time(); + } + +@@ -99,12 +102,14 @@ + { + ktime_t period; + +- write_seqlock(&jiffies_lock); ++ raw_spin_lock(&jiffies_lock); ++ write_seqcount_begin(&jiffies_seq); + /* Did we start the jiffies update yet ? */ + if (last_jiffies_update.tv64 == 0) + last_jiffies_update = tick_next_period; + period = last_jiffies_update; +- write_sequnlock(&jiffies_lock); ++ write_seqcount_end(&jiffies_seq); ++ raw_spin_unlock(&jiffies_lock); + return period; + } + +@@ -176,6 +181,11 @@ + return false; + } + ++ if (!arch_irq_work_has_interrupt()) { ++ trace_tick_stop(0, "missing irq work interrupt\n"); ++ return false; ++ } ++ + /* sched_clock_tick() needs us? */ + #ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK + /* +@@ -217,11 +227,17 @@ + + static void nohz_full_kick_work_func(struct irq_work *work) + { ++ unsigned long flags; ++ ++ /* ksoftirqd processes sirqs with interrupts enabled */ ++ local_irq_save(flags); + __tick_nohz_full_check(); ++ local_irq_restore(flags); + } + + static DEFINE_PER_CPU(struct irq_work, nohz_full_kick_work) = { + .func = nohz_full_kick_work_func, ++ .flags = IRQ_WORK_HARD_IRQ, + }; + + /* +@@ -580,10 +596,10 @@ + + /* Read jiffies and the time when jiffies were updated last */ + do { +- seq = read_seqbegin(&jiffies_lock); ++ seq = read_seqcount_begin(&jiffies_seq); + last_update = last_jiffies_update; + last_jiffies = jiffies; +- } while (read_seqretry(&jiffies_lock, seq)); ++ } while (read_seqcount_retry(&jiffies_seq, seq)); + + if (rcu_needs_cpu(cpu, &rcu_delta_jiffies) || + arch_needs_cpu() || irq_work_needs_cpu()) { +@@ -761,14 +777,7 @@ + return false; + + if (unlikely(local_softirq_pending() && cpu_online(cpu))) { +- static int ratelimit; +- +- if (ratelimit < 10 && +- (local_softirq_pending() & SOFTIRQ_STOP_IDLE_MASK)) { +- pr_warn("NOHZ: local_softirq_pending %02x\n", +- (unsigned int) local_softirq_pending()); +- ratelimit++; +- } ++ softirq_check_pending_idle(); + return false; + } + +@@ -1156,6 +1165,7 @@ + * Emulate tick processing via per-CPU hrtimers: + */ + hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ++ ts->sched_timer.irqsafe = 1; + ts->sched_timer.function = tick_sched_timer; + + /* Get the next period (per cpu) */ +diff -Nur linux-3.18.14.orig/kernel/time/timekeeping.c linux-3.18.14-rt/kernel/time/timekeeping.c +--- linux-3.18.14.orig/kernel/time/timekeeping.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/timekeeping.c 2015-05-31 15:32:48.969635362 -0500 +@@ -1814,8 +1814,10 @@ + */ + void xtime_update(unsigned long ticks) + { +- write_seqlock(&jiffies_lock); ++ raw_spin_lock(&jiffies_lock); ++ write_seqcount_begin(&jiffies_seq); + do_timer(ticks); +- write_sequnlock(&jiffies_lock); ++ write_seqcount_end(&jiffies_seq); ++ raw_spin_unlock(&jiffies_lock); + update_wall_time(); + } +diff -Nur linux-3.18.14.orig/kernel/time/timer.c linux-3.18.14-rt/kernel/time/timer.c +--- linux-3.18.14.orig/kernel/time/timer.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/time/timer.c 2015-05-31 15:32:48.973635362 -0500 +@@ -78,6 +78,9 @@ + struct tvec_base { + spinlock_t lock; + struct timer_list *running_timer; ++#ifdef CONFIG_PREEMPT_RT_FULL ++ wait_queue_head_t wait_for_running_timer; ++#endif + unsigned long timer_jiffies; + unsigned long next_timer; + unsigned long active_timers; +@@ -758,6 +761,36 @@ + } + } + ++#ifndef CONFIG_PREEMPT_RT_FULL ++static inline struct tvec_base *switch_timer_base(struct timer_list *timer, ++ struct tvec_base *old, ++ struct tvec_base *new) ++{ ++ /* See the comment in lock_timer_base() */ ++ timer_set_base(timer, NULL); ++ spin_unlock(&old->lock); ++ spin_lock(&new->lock); ++ timer_set_base(timer, new); ++ return new; ++} ++#else ++static inline struct tvec_base *switch_timer_base(struct timer_list *timer, ++ struct tvec_base *old, ++ struct tvec_base *new) ++{ ++ /* ++ * We cannot do the above because we might be preempted and ++ * then the preempter would see NULL and loop forever. ++ */ ++ if (spin_trylock(&new->lock)) { ++ timer_set_base(timer, new); ++ spin_unlock(&old->lock); ++ return new; ++ } ++ return old; ++} ++#endif ++ + static inline int + __mod_timer(struct timer_list *timer, unsigned long expires, + bool pending_only, int pinned) +@@ -788,14 +821,8 @@ + * handler yet has not finished. This also guarantees that + * the timer is serialized wrt itself. + */ +- if (likely(base->running_timer != timer)) { +- /* See the comment in lock_timer_base() */ +- timer_set_base(timer, NULL); +- spin_unlock(&base->lock); +- base = new_base; +- spin_lock(&base->lock); +- timer_set_base(timer, base); +- } ++ if (likely(base->running_timer != timer)) ++ base = switch_timer_base(timer, base, new_base); + } + + timer->expires = expires; +@@ -969,6 +996,29 @@ + } + EXPORT_SYMBOL_GPL(add_timer_on); + ++#ifdef CONFIG_PREEMPT_RT_FULL ++/* ++ * Wait for a running timer ++ */ ++static void wait_for_running_timer(struct timer_list *timer) ++{ ++ struct tvec_base *base = timer->base; ++ ++ if (base->running_timer == timer) ++ wait_event(base->wait_for_running_timer, ++ base->running_timer != timer); ++} ++ ++# define wakeup_timer_waiters(b) wake_up(&(b)->wait_for_running_timer) ++#else ++static inline void wait_for_running_timer(struct timer_list *timer) ++{ ++ cpu_relax(); ++} ++ ++# define wakeup_timer_waiters(b) do { } while (0) ++#endif ++ + /** + * del_timer - deactive a timer. + * @timer: the timer to be deactivated +@@ -1026,7 +1076,7 @@ + } + EXPORT_SYMBOL(try_to_del_timer_sync); + +-#ifdef CONFIG_SMP ++#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL) + /** + * del_timer_sync - deactivate a timer and wait for the handler to finish. + * @timer: the timer to be deactivated +@@ -1086,7 +1136,7 @@ + int ret = try_to_del_timer_sync(timer); + if (ret >= 0) + return ret; +- cpu_relax(); ++ wait_for_running_timer(timer); + } + } + EXPORT_SYMBOL(del_timer_sync); +@@ -1207,15 +1257,17 @@ + if (irqsafe) { + spin_unlock(&base->lock); + call_timer_fn(timer, fn, data); ++ base->running_timer = NULL; + spin_lock(&base->lock); + } else { + spin_unlock_irq(&base->lock); + call_timer_fn(timer, fn, data); ++ base->running_timer = NULL; + spin_lock_irq(&base->lock); + } + } + } +- base->running_timer = NULL; ++ wakeup_timer_waiters(base); + spin_unlock_irq(&base->lock); + } + +@@ -1355,17 +1407,31 @@ + if (cpu_is_offline(smp_processor_id())) + return expires; + ++#ifdef CONFIG_PREEMPT_RT_FULL ++ /* ++ * On PREEMPT_RT we cannot sleep here. If the trylock does not ++ * succeed then we return the worst-case 'expires in 1 tick' ++ * value. We use the rt functions here directly to avoid a ++ * migrate_disable() call. ++ */ ++ if (!spin_do_trylock(&base->lock)) ++ return now + 1; ++#else + spin_lock(&base->lock); ++#endif + if (base->active_timers) { + if (time_before_eq(base->next_timer, base->timer_jiffies)) + base->next_timer = __next_timer_interrupt(base); + expires = base->next_timer; + } ++#ifdef CONFIG_PREEMPT_RT_FULL ++ rt_spin_unlock_after_trylock_in_irq(&base->lock); ++#else + spin_unlock(&base->lock); ++#endif + + if (time_before_eq(expires, now)) + return now; +- + return cmp_next_hrtimer_event(now, expires); + } + #endif +@@ -1381,13 +1447,13 @@ + + /* Note: this timer irq context must be accounted for as well. */ + account_process_tick(p, user_tick); ++ scheduler_tick(); + run_local_timers(); + rcu_check_callbacks(cpu, user_tick); +-#ifdef CONFIG_IRQ_WORK +- if (in_irq()) +- irq_work_tick(); ++ ++#if defined(CONFIG_IRQ_WORK) && !defined(CONFIG_PREEMPT_RT_FULL) ++ irq_work_tick(); + #endif +- scheduler_tick(); + run_posix_cpu_timers(p); + } + +@@ -1400,6 +1466,10 @@ + + hrtimer_run_pending(); + ++#if defined(CONFIG_IRQ_WORK) && defined(CONFIG_PREEMPT_RT_FULL) ++ irq_work_tick(); ++#endif ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } +@@ -1574,6 +1644,9 @@ + base = per_cpu(tvec_bases, cpu); + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++ init_waitqueue_head(&base->wait_for_running_timer); ++#endif + + for (j = 0; j < TVN_SIZE; j++) { + INIT_LIST_HEAD(base->tv5.vec + j); +@@ -1613,7 +1686,7 @@ + + BUG_ON(cpu_online(cpu)); + old_base = per_cpu(tvec_bases, cpu); +- new_base = get_cpu_var(tvec_bases); ++ new_base = get_local_var(tvec_bases); + /* + * The caller is globally serialized and nobody else + * takes two locks at once, deadlock is not possible. +@@ -1634,7 +1707,7 @@ + + spin_unlock(&old_base->lock); + spin_unlock_irq(&new_base->lock); +- put_cpu_var(tvec_bases); ++ put_local_var(tvec_bases); + } + #endif /* CONFIG_HOTPLUG_CPU */ + +diff -Nur linux-3.18.14.orig/kernel/trace/Kconfig linux-3.18.14-rt/kernel/trace/Kconfig +--- linux-3.18.14.orig/kernel/trace/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/trace/Kconfig 2015-05-31 15:32:48.973635362 -0500 +@@ -187,6 +187,24 @@ + enabled. This option and the preempt-off timing option can be + used together or separately.) + ++config INTERRUPT_OFF_HIST ++ bool "Interrupts-off Latency Histogram" ++ depends on IRQSOFF_TRACER ++ help ++ This option generates continuously updated histograms (one per cpu) ++ of the duration of time periods with interrupts disabled. The ++ histograms are disabled by default. To enable them, write a non-zero ++ number to ++ ++ /sys/kernel/debug/tracing/latency_hist/enable/preemptirqsoff ++ ++ If PREEMPT_OFF_HIST is also selected, additional histograms (one ++ per cpu) are generated that accumulate the duration of time periods ++ when both interrupts and preemption are disabled. The histogram data ++ will be located in the debug file system at ++ ++ /sys/kernel/debug/tracing/latency_hist/irqsoff ++ + config PREEMPT_TRACER + bool "Preemption-off Latency Tracer" + default n +@@ -211,6 +229,24 @@ + enabled. This option and the irqs-off timing option can be + used together or separately.) + ++config PREEMPT_OFF_HIST ++ bool "Preemption-off Latency Histogram" ++ depends on PREEMPT_TRACER ++ help ++ This option generates continuously updated histograms (one per cpu) ++ of the duration of time periods with preemption disabled. The ++ histograms are disabled by default. To enable them, write a non-zero ++ number to ++ ++ /sys/kernel/debug/tracing/latency_hist/enable/preemptirqsoff ++ ++ If INTERRUPT_OFF_HIST is also selected, additional histograms (one ++ per cpu) are generated that accumulate the duration of time periods ++ when both interrupts and preemption are disabled. The histogram data ++ will be located in the debug file system at ++ ++ /sys/kernel/debug/tracing/latency_hist/preemptoff ++ + config SCHED_TRACER + bool "Scheduling Latency Tracer" + select GENERIC_TRACER +@@ -221,6 +257,74 @@ + This tracer tracks the latency of the highest priority task + to be scheduled in, starting from the point it has woken up. + ++config WAKEUP_LATENCY_HIST ++ bool "Scheduling Latency Histogram" ++ depends on SCHED_TRACER ++ help ++ This option generates continuously updated histograms (one per cpu) ++ of the scheduling latency of the highest priority task. ++ The histograms are disabled by default. To enable them, write a ++ non-zero number to ++ ++ /sys/kernel/debug/tracing/latency_hist/enable/wakeup ++ ++ Two different algorithms are used, one to determine the latency of ++ processes that exclusively use the highest priority of the system and ++ another one to determine the latency of processes that share the ++ highest system priority with other processes. The former is used to ++ improve hardware and system software, the latter to optimize the ++ priority design of a given system. The histogram data will be ++ located in the debug file system at ++ ++ /sys/kernel/debug/tracing/latency_hist/wakeup ++ ++ and ++ ++ /sys/kernel/debug/tracing/latency_hist/wakeup/sharedprio ++ ++ If both Scheduling Latency Histogram and Missed Timer Offsets ++ Histogram are selected, additional histogram data will be collected ++ that contain, in addition to the wakeup latency, the timer latency, in ++ case the wakeup was triggered by an expired timer. These histograms ++ are available in the ++ ++ /sys/kernel/debug/tracing/latency_hist/timerandwakeup ++ ++ directory. They reflect the apparent interrupt and scheduling latency ++ and are best suitable to determine the worst-case latency of a given ++ system. To enable these histograms, write a non-zero number to ++ ++ /sys/kernel/debug/tracing/latency_hist/enable/timerandwakeup ++ ++config MISSED_TIMER_OFFSETS_HIST ++ depends on HIGH_RES_TIMERS ++ select GENERIC_TRACER ++ bool "Missed Timer Offsets Histogram" ++ help ++ Generate a histogram of missed timer offsets in microseconds. The ++ histograms are disabled by default. To enable them, write a non-zero ++ number to ++ ++ /sys/kernel/debug/tracing/latency_hist/enable/missed_timer_offsets ++ ++ The histogram data will be located in the debug file system at ++ ++ /sys/kernel/debug/tracing/latency_hist/missed_timer_offsets ++ ++ If both Scheduling Latency Histogram and Missed Timer Offsets ++ Histogram are selected, additional histogram data will be collected ++ that contain, in addition to the wakeup latency, the timer latency, in ++ case the wakeup was triggered by an expired timer. These histograms ++ are available in the ++ ++ /sys/kernel/debug/tracing/latency_hist/timerandwakeup ++ ++ directory. They reflect the apparent interrupt and scheduling latency ++ and are best suitable to determine the worst-case latency of a given ++ system. To enable these histograms, write a non-zero number to ++ ++ /sys/kernel/debug/tracing/latency_hist/enable/timerandwakeup ++ + config ENABLE_DEFAULT_TRACERS + bool "Trace process context switches and events" + depends on !GENERIC_TRACER +diff -Nur linux-3.18.14.orig/kernel/trace/latency_hist.c linux-3.18.14-rt/kernel/trace/latency_hist.c +--- linux-3.18.14.orig/kernel/trace/latency_hist.c 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/kernel/trace/latency_hist.c 2015-05-31 15:32:48.989635362 -0500 +@@ -0,0 +1,1178 @@ ++/* ++ * kernel/trace/latency_hist.c ++ * ++ * Add support for histograms of preemption-off latency and ++ * interrupt-off latency and wakeup latency, it depends on ++ * Real-Time Preemption Support. ++ * ++ * Copyright (C) 2005 MontaVista Software, Inc. ++ * Yi Yang ++ * ++ * Converted to work with the new latency tracer. ++ * Copyright (C) 2008 Red Hat, Inc. ++ * Steven Rostedt ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "trace.h" ++#include ++ ++#define NSECS_PER_USECS 1000L ++ ++#define CREATE_TRACE_POINTS ++#include ++ ++enum { ++ IRQSOFF_LATENCY = 0, ++ PREEMPTOFF_LATENCY, ++ PREEMPTIRQSOFF_LATENCY, ++ WAKEUP_LATENCY, ++ WAKEUP_LATENCY_SHAREDPRIO, ++ MISSED_TIMER_OFFSETS, ++ TIMERANDWAKEUP_LATENCY, ++ MAX_LATENCY_TYPE, ++}; ++ ++#define MAX_ENTRY_NUM 10240 ++ ++struct hist_data { ++ atomic_t hist_mode; /* 0 log, 1 don't log */ ++ long offset; /* set it to MAX_ENTRY_NUM/2 for a bipolar scale */ ++ long min_lat; ++ long max_lat; ++ unsigned long long below_hist_bound_samples; ++ unsigned long long above_hist_bound_samples; ++ long long accumulate_lat; ++ unsigned long long total_samples; ++ unsigned long long hist_array[MAX_ENTRY_NUM]; ++}; ++ ++struct enable_data { ++ int latency_type; ++ int enabled; ++}; ++ ++static char *latency_hist_dir_root = "latency_hist"; ++ ++#ifdef CONFIG_INTERRUPT_OFF_HIST ++static DEFINE_PER_CPU(struct hist_data, irqsoff_hist); ++static char *irqsoff_hist_dir = "irqsoff"; ++static DEFINE_PER_CPU(cycles_t, hist_irqsoff_start); ++static DEFINE_PER_CPU(int, hist_irqsoff_counting); ++#endif ++ ++#ifdef CONFIG_PREEMPT_OFF_HIST ++static DEFINE_PER_CPU(struct hist_data, preemptoff_hist); ++static char *preemptoff_hist_dir = "preemptoff"; ++static DEFINE_PER_CPU(cycles_t, hist_preemptoff_start); ++static DEFINE_PER_CPU(int, hist_preemptoff_counting); ++#endif ++ ++#if defined(CONFIG_PREEMPT_OFF_HIST) && defined(CONFIG_INTERRUPT_OFF_HIST) ++static DEFINE_PER_CPU(struct hist_data, preemptirqsoff_hist); ++static char *preemptirqsoff_hist_dir = "preemptirqsoff"; ++static DEFINE_PER_CPU(cycles_t, hist_preemptirqsoff_start); ++static DEFINE_PER_CPU(int, hist_preemptirqsoff_counting); ++#endif ++ ++#if defined(CONFIG_PREEMPT_OFF_HIST) || defined(CONFIG_INTERRUPT_OFF_HIST) ++static notrace void probe_preemptirqsoff_hist(void *v, int reason, int start); ++static struct enable_data preemptirqsoff_enabled_data = { ++ .latency_type = PREEMPTIRQSOFF_LATENCY, ++ .enabled = 0, ++}; ++#endif ++ ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++struct maxlatproc_data { ++ char comm[FIELD_SIZEOF(struct task_struct, comm)]; ++ char current_comm[FIELD_SIZEOF(struct task_struct, comm)]; ++ int pid; ++ int current_pid; ++ int prio; ++ int current_prio; ++ long latency; ++ long timeroffset; ++ cycle_t timestamp; ++}; ++#endif ++ ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++static DEFINE_PER_CPU(struct hist_data, wakeup_latency_hist); ++static DEFINE_PER_CPU(struct hist_data, wakeup_latency_hist_sharedprio); ++static char *wakeup_latency_hist_dir = "wakeup"; ++static char *wakeup_latency_hist_dir_sharedprio = "sharedprio"; ++static notrace void probe_wakeup_latency_hist_start(void *v, ++ struct task_struct *p, int success); ++static notrace void probe_wakeup_latency_hist_stop(void *v, ++ struct task_struct *prev, struct task_struct *next); ++static notrace void probe_sched_migrate_task(void *, ++ struct task_struct *task, int cpu); ++static struct enable_data wakeup_latency_enabled_data = { ++ .latency_type = WAKEUP_LATENCY, ++ .enabled = 0, ++}; ++static DEFINE_PER_CPU(struct maxlatproc_data, wakeup_maxlatproc); ++static DEFINE_PER_CPU(struct maxlatproc_data, wakeup_maxlatproc_sharedprio); ++static DEFINE_PER_CPU(struct task_struct *, wakeup_task); ++static DEFINE_PER_CPU(int, wakeup_sharedprio); ++static unsigned long wakeup_pid; ++#endif ++ ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++static DEFINE_PER_CPU(struct hist_data, missed_timer_offsets); ++static char *missed_timer_offsets_dir = "missed_timer_offsets"; ++static notrace void probe_hrtimer_interrupt(void *v, int cpu, ++ long long offset, struct task_struct *curr, struct task_struct *task); ++static struct enable_data missed_timer_offsets_enabled_data = { ++ .latency_type = MISSED_TIMER_OFFSETS, ++ .enabled = 0, ++}; ++static DEFINE_PER_CPU(struct maxlatproc_data, missed_timer_offsets_maxlatproc); ++static unsigned long missed_timer_offsets_pid; ++#endif ++ ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++static DEFINE_PER_CPU(struct hist_data, timerandwakeup_latency_hist); ++static char *timerandwakeup_latency_hist_dir = "timerandwakeup"; ++static struct enable_data timerandwakeup_enabled_data = { ++ .latency_type = TIMERANDWAKEUP_LATENCY, ++ .enabled = 0, ++}; ++static DEFINE_PER_CPU(struct maxlatproc_data, timerandwakeup_maxlatproc); ++#endif ++ ++void notrace latency_hist(int latency_type, int cpu, long latency, ++ long timeroffset, cycle_t stop, ++ struct task_struct *p) ++{ ++ struct hist_data *my_hist; ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++ struct maxlatproc_data *mp = NULL; ++#endif ++ ++ if (!cpu_possible(cpu) || latency_type < 0 || ++ latency_type >= MAX_LATENCY_TYPE) ++ return; ++ ++ switch (latency_type) { ++#ifdef CONFIG_INTERRUPT_OFF_HIST ++ case IRQSOFF_LATENCY: ++ my_hist = &per_cpu(irqsoff_hist, cpu); ++ break; ++#endif ++#ifdef CONFIG_PREEMPT_OFF_HIST ++ case PREEMPTOFF_LATENCY: ++ my_hist = &per_cpu(preemptoff_hist, cpu); ++ break; ++#endif ++#if defined(CONFIG_PREEMPT_OFF_HIST) && defined(CONFIG_INTERRUPT_OFF_HIST) ++ case PREEMPTIRQSOFF_LATENCY: ++ my_hist = &per_cpu(preemptirqsoff_hist, cpu); ++ break; ++#endif ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++ case WAKEUP_LATENCY: ++ my_hist = &per_cpu(wakeup_latency_hist, cpu); ++ mp = &per_cpu(wakeup_maxlatproc, cpu); ++ break; ++ case WAKEUP_LATENCY_SHAREDPRIO: ++ my_hist = &per_cpu(wakeup_latency_hist_sharedprio, cpu); ++ mp = &per_cpu(wakeup_maxlatproc_sharedprio, cpu); ++ break; ++#endif ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ case MISSED_TIMER_OFFSETS: ++ my_hist = &per_cpu(missed_timer_offsets, cpu); ++ mp = &per_cpu(missed_timer_offsets_maxlatproc, cpu); ++ break; ++#endif ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++ case TIMERANDWAKEUP_LATENCY: ++ my_hist = &per_cpu(timerandwakeup_latency_hist, cpu); ++ mp = &per_cpu(timerandwakeup_maxlatproc, cpu); ++ break; ++#endif ++ ++ default: ++ return; ++ } ++ ++ latency += my_hist->offset; ++ ++ if (atomic_read(&my_hist->hist_mode) == 0) ++ return; ++ ++ if (latency < 0 || latency >= MAX_ENTRY_NUM) { ++ if (latency < 0) ++ my_hist->below_hist_bound_samples++; ++ else ++ my_hist->above_hist_bound_samples++; ++ } else ++ my_hist->hist_array[latency]++; ++ ++ if (unlikely(latency > my_hist->max_lat || ++ my_hist->min_lat == LONG_MAX)) { ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++ if (latency_type == WAKEUP_LATENCY || ++ latency_type == WAKEUP_LATENCY_SHAREDPRIO || ++ latency_type == MISSED_TIMER_OFFSETS || ++ latency_type == TIMERANDWAKEUP_LATENCY) { ++ strncpy(mp->comm, p->comm, sizeof(mp->comm)); ++ strncpy(mp->current_comm, current->comm, ++ sizeof(mp->current_comm)); ++ mp->pid = task_pid_nr(p); ++ mp->current_pid = task_pid_nr(current); ++ mp->prio = p->prio; ++ mp->current_prio = current->prio; ++ mp->latency = latency; ++ mp->timeroffset = timeroffset; ++ mp->timestamp = stop; ++ } ++#endif ++ my_hist->max_lat = latency; ++ } ++ if (unlikely(latency < my_hist->min_lat)) ++ my_hist->min_lat = latency; ++ my_hist->total_samples++; ++ my_hist->accumulate_lat += latency; ++} ++ ++static void *l_start(struct seq_file *m, loff_t *pos) ++{ ++ loff_t *index_ptr = NULL; ++ loff_t index = *pos; ++ struct hist_data *my_hist = m->private; ++ ++ if (index == 0) { ++ char minstr[32], avgstr[32], maxstr[32]; ++ ++ atomic_dec(&my_hist->hist_mode); ++ ++ if (likely(my_hist->total_samples)) { ++ long avg = (long) div64_s64(my_hist->accumulate_lat, ++ my_hist->total_samples); ++ snprintf(minstr, sizeof(minstr), "%ld", ++ my_hist->min_lat - my_hist->offset); ++ snprintf(avgstr, sizeof(avgstr), "%ld", ++ avg - my_hist->offset); ++ snprintf(maxstr, sizeof(maxstr), "%ld", ++ my_hist->max_lat - my_hist->offset); ++ } else { ++ strcpy(minstr, ""); ++ strcpy(avgstr, minstr); ++ strcpy(maxstr, minstr); ++ } ++ ++ seq_printf(m, "#Minimum latency: %s microseconds\n" ++ "#Average latency: %s microseconds\n" ++ "#Maximum latency: %s microseconds\n" ++ "#Total samples: %llu\n" ++ "#There are %llu samples lower than %ld" ++ " microseconds.\n" ++ "#There are %llu samples greater or equal" ++ " than %ld microseconds.\n" ++ "#usecs\t%16s\n", ++ minstr, avgstr, maxstr, ++ my_hist->total_samples, ++ my_hist->below_hist_bound_samples, ++ -my_hist->offset, ++ my_hist->above_hist_bound_samples, ++ MAX_ENTRY_NUM - my_hist->offset, ++ "samples"); ++ } ++ if (index < MAX_ENTRY_NUM) { ++ index_ptr = kmalloc(sizeof(loff_t), GFP_KERNEL); ++ if (index_ptr) ++ *index_ptr = index; ++ } ++ ++ return index_ptr; ++} ++ ++static void *l_next(struct seq_file *m, void *p, loff_t *pos) ++{ ++ loff_t *index_ptr = p; ++ struct hist_data *my_hist = m->private; ++ ++ if (++*pos >= MAX_ENTRY_NUM) { ++ atomic_inc(&my_hist->hist_mode); ++ return NULL; ++ } ++ *index_ptr = *pos; ++ return index_ptr; ++} ++ ++static void l_stop(struct seq_file *m, void *p) ++{ ++ kfree(p); ++} ++ ++static int l_show(struct seq_file *m, void *p) ++{ ++ int index = *(loff_t *) p; ++ struct hist_data *my_hist = m->private; ++ ++ seq_printf(m, "%6ld\t%16llu\n", index - my_hist->offset, ++ my_hist->hist_array[index]); ++ return 0; ++} ++ ++static const struct seq_operations latency_hist_seq_op = { ++ .start = l_start, ++ .next = l_next, ++ .stop = l_stop, ++ .show = l_show ++}; ++ ++static int latency_hist_open(struct inode *inode, struct file *file) ++{ ++ int ret; ++ ++ ret = seq_open(file, &latency_hist_seq_op); ++ if (!ret) { ++ struct seq_file *seq = file->private_data; ++ seq->private = inode->i_private; ++ } ++ return ret; ++} ++ ++static const struct file_operations latency_hist_fops = { ++ .open = latency_hist_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++static void clear_maxlatprocdata(struct maxlatproc_data *mp) ++{ ++ mp->comm[0] = mp->current_comm[0] = '\0'; ++ mp->prio = mp->current_prio = mp->pid = mp->current_pid = ++ mp->latency = mp->timeroffset = -1; ++ mp->timestamp = 0; ++} ++#endif ++ ++static void hist_reset(struct hist_data *hist) ++{ ++ atomic_dec(&hist->hist_mode); ++ ++ memset(hist->hist_array, 0, sizeof(hist->hist_array)); ++ hist->below_hist_bound_samples = 0ULL; ++ hist->above_hist_bound_samples = 0ULL; ++ hist->min_lat = LONG_MAX; ++ hist->max_lat = LONG_MIN; ++ hist->total_samples = 0ULL; ++ hist->accumulate_lat = 0LL; ++ ++ atomic_inc(&hist->hist_mode); ++} ++ ++static ssize_t ++latency_hist_reset(struct file *file, const char __user *a, ++ size_t size, loff_t *off) ++{ ++ int cpu; ++ struct hist_data *hist = NULL; ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++ struct maxlatproc_data *mp = NULL; ++#endif ++ off_t latency_type = (off_t) file->private_data; ++ ++ for_each_online_cpu(cpu) { ++ ++ switch (latency_type) { ++#ifdef CONFIG_PREEMPT_OFF_HIST ++ case PREEMPTOFF_LATENCY: ++ hist = &per_cpu(preemptoff_hist, cpu); ++ break; ++#endif ++#ifdef CONFIG_INTERRUPT_OFF_HIST ++ case IRQSOFF_LATENCY: ++ hist = &per_cpu(irqsoff_hist, cpu); ++ break; ++#endif ++#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) ++ case PREEMPTIRQSOFF_LATENCY: ++ hist = &per_cpu(preemptirqsoff_hist, cpu); ++ break; ++#endif ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++ case WAKEUP_LATENCY: ++ hist = &per_cpu(wakeup_latency_hist, cpu); ++ mp = &per_cpu(wakeup_maxlatproc, cpu); ++ break; ++ case WAKEUP_LATENCY_SHAREDPRIO: ++ hist = &per_cpu(wakeup_latency_hist_sharedprio, cpu); ++ mp = &per_cpu(wakeup_maxlatproc_sharedprio, cpu); ++ break; ++#endif ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ case MISSED_TIMER_OFFSETS: ++ hist = &per_cpu(missed_timer_offsets, cpu); ++ mp = &per_cpu(missed_timer_offsets_maxlatproc, cpu); ++ break; ++#endif ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++ case TIMERANDWAKEUP_LATENCY: ++ hist = &per_cpu(timerandwakeup_latency_hist, cpu); ++ mp = &per_cpu(timerandwakeup_maxlatproc, cpu); ++ break; ++#endif ++ } ++ ++ hist_reset(hist); ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++ if (latency_type == WAKEUP_LATENCY || ++ latency_type == WAKEUP_LATENCY_SHAREDPRIO || ++ latency_type == MISSED_TIMER_OFFSETS || ++ latency_type == TIMERANDWAKEUP_LATENCY) ++ clear_maxlatprocdata(mp); ++#endif ++ } ++ ++ return size; ++} ++ ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++static ssize_t ++show_pid(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) ++{ ++ char buf[64]; ++ int r; ++ unsigned long *this_pid = file->private_data; ++ ++ r = snprintf(buf, sizeof(buf), "%lu\n", *this_pid); ++ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); ++} ++ ++static ssize_t do_pid(struct file *file, const char __user *ubuf, ++ size_t cnt, loff_t *ppos) ++{ ++ char buf[64]; ++ unsigned long pid; ++ unsigned long *this_pid = file->private_data; ++ ++ if (cnt >= sizeof(buf)) ++ return -EINVAL; ++ ++ if (copy_from_user(&buf, ubuf, cnt)) ++ return -EFAULT; ++ ++ buf[cnt] = '\0'; ++ ++ if (kstrtoul(buf, 10, &pid)) ++ return -EINVAL; ++ ++ *this_pid = pid; ++ ++ return cnt; ++} ++#endif ++ ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++static ssize_t ++show_maxlatproc(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) ++{ ++ int r; ++ struct maxlatproc_data *mp = file->private_data; ++ int strmaxlen = (TASK_COMM_LEN * 2) + (8 * 8); ++ unsigned long long t; ++ unsigned long usecs, secs; ++ char *buf; ++ ++ if (mp->pid == -1 || mp->current_pid == -1) { ++ buf = "(none)\n"; ++ return simple_read_from_buffer(ubuf, cnt, ppos, buf, ++ strlen(buf)); ++ } ++ ++ buf = kmalloc(strmaxlen, GFP_KERNEL); ++ if (buf == NULL) ++ return -ENOMEM; ++ ++ t = ns2usecs(mp->timestamp); ++ usecs = do_div(t, USEC_PER_SEC); ++ secs = (unsigned long) t; ++ r = snprintf(buf, strmaxlen, ++ "%d %d %ld (%ld) %s <- %d %d %s %lu.%06lu\n", mp->pid, ++ MAX_RT_PRIO-1 - mp->prio, mp->latency, mp->timeroffset, mp->comm, ++ mp->current_pid, MAX_RT_PRIO-1 - mp->current_prio, mp->current_comm, ++ secs, usecs); ++ r = simple_read_from_buffer(ubuf, cnt, ppos, buf, r); ++ kfree(buf); ++ return r; ++} ++#endif ++ ++static ssize_t ++show_enable(struct file *file, char __user *ubuf, size_t cnt, loff_t *ppos) ++{ ++ char buf[64]; ++ struct enable_data *ed = file->private_data; ++ int r; ++ ++ r = snprintf(buf, sizeof(buf), "%d\n", ed->enabled); ++ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); ++} ++ ++static ssize_t ++do_enable(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) ++{ ++ char buf[64]; ++ long enable; ++ struct enable_data *ed = file->private_data; ++ ++ if (cnt >= sizeof(buf)) ++ return -EINVAL; ++ ++ if (copy_from_user(&buf, ubuf, cnt)) ++ return -EFAULT; ++ ++ buf[cnt] = 0; ++ ++ if (kstrtoul(buf, 10, &enable)) ++ return -EINVAL; ++ ++ if ((enable && ed->enabled) || (!enable && !ed->enabled)) ++ return cnt; ++ ++ if (enable) { ++ int ret; ++ ++ switch (ed->latency_type) { ++#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) ++ case PREEMPTIRQSOFF_LATENCY: ++ ret = register_trace_preemptirqsoff_hist( ++ probe_preemptirqsoff_hist, NULL); ++ if (ret) { ++ pr_info("wakeup trace: Couldn't assign " ++ "probe_preemptirqsoff_hist " ++ "to trace_preemptirqsoff_hist\n"); ++ return ret; ++ } ++ break; ++#endif ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++ case WAKEUP_LATENCY: ++ ret = register_trace_sched_wakeup( ++ probe_wakeup_latency_hist_start, NULL); ++ if (ret) { ++ pr_info("wakeup trace: Couldn't assign " ++ "probe_wakeup_latency_hist_start " ++ "to trace_sched_wakeup\n"); ++ return ret; ++ } ++ ret = register_trace_sched_wakeup_new( ++ probe_wakeup_latency_hist_start, NULL); ++ if (ret) { ++ pr_info("wakeup trace: Couldn't assign " ++ "probe_wakeup_latency_hist_start " ++ "to trace_sched_wakeup_new\n"); ++ unregister_trace_sched_wakeup( ++ probe_wakeup_latency_hist_start, NULL); ++ return ret; ++ } ++ ret = register_trace_sched_switch( ++ probe_wakeup_latency_hist_stop, NULL); ++ if (ret) { ++ pr_info("wakeup trace: Couldn't assign " ++ "probe_wakeup_latency_hist_stop " ++ "to trace_sched_switch\n"); ++ unregister_trace_sched_wakeup( ++ probe_wakeup_latency_hist_start, NULL); ++ unregister_trace_sched_wakeup_new( ++ probe_wakeup_latency_hist_start, NULL); ++ return ret; ++ } ++ ret = register_trace_sched_migrate_task( ++ probe_sched_migrate_task, NULL); ++ if (ret) { ++ pr_info("wakeup trace: Couldn't assign " ++ "probe_sched_migrate_task " ++ "to trace_sched_migrate_task\n"); ++ unregister_trace_sched_wakeup( ++ probe_wakeup_latency_hist_start, NULL); ++ unregister_trace_sched_wakeup_new( ++ probe_wakeup_latency_hist_start, NULL); ++ unregister_trace_sched_switch( ++ probe_wakeup_latency_hist_stop, NULL); ++ return ret; ++ } ++ break; ++#endif ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ case MISSED_TIMER_OFFSETS: ++ ret = register_trace_hrtimer_interrupt( ++ probe_hrtimer_interrupt, NULL); ++ if (ret) { ++ pr_info("wakeup trace: Couldn't assign " ++ "probe_hrtimer_interrupt " ++ "to trace_hrtimer_interrupt\n"); ++ return ret; ++ } ++ break; ++#endif ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++ case TIMERANDWAKEUP_LATENCY: ++ if (!wakeup_latency_enabled_data.enabled || ++ !missed_timer_offsets_enabled_data.enabled) ++ return -EINVAL; ++ break; ++#endif ++ default: ++ break; ++ } ++ } else { ++ switch (ed->latency_type) { ++#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) ++ case PREEMPTIRQSOFF_LATENCY: ++ { ++ int cpu; ++ ++ unregister_trace_preemptirqsoff_hist( ++ probe_preemptirqsoff_hist, NULL); ++ for_each_online_cpu(cpu) { ++#ifdef CONFIG_INTERRUPT_OFF_HIST ++ per_cpu(hist_irqsoff_counting, ++ cpu) = 0; ++#endif ++#ifdef CONFIG_PREEMPT_OFF_HIST ++ per_cpu(hist_preemptoff_counting, ++ cpu) = 0; ++#endif ++#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) ++ per_cpu(hist_preemptirqsoff_counting, ++ cpu) = 0; ++#endif ++ } ++ } ++ break; ++#endif ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++ case WAKEUP_LATENCY: ++ { ++ int cpu; ++ ++ unregister_trace_sched_wakeup( ++ probe_wakeup_latency_hist_start, NULL); ++ unregister_trace_sched_wakeup_new( ++ probe_wakeup_latency_hist_start, NULL); ++ unregister_trace_sched_switch( ++ probe_wakeup_latency_hist_stop, NULL); ++ unregister_trace_sched_migrate_task( ++ probe_sched_migrate_task, NULL); ++ ++ for_each_online_cpu(cpu) { ++ per_cpu(wakeup_task, cpu) = NULL; ++ per_cpu(wakeup_sharedprio, cpu) = 0; ++ } ++ } ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ timerandwakeup_enabled_data.enabled = 0; ++#endif ++ break; ++#endif ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ case MISSED_TIMER_OFFSETS: ++ unregister_trace_hrtimer_interrupt( ++ probe_hrtimer_interrupt, NULL); ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++ timerandwakeup_enabled_data.enabled = 0; ++#endif ++ break; ++#endif ++ default: ++ break; ++ } ++ } ++ ed->enabled = enable; ++ return cnt; ++} ++ ++static const struct file_operations latency_hist_reset_fops = { ++ .open = tracing_open_generic, ++ .write = latency_hist_reset, ++}; ++ ++static const struct file_operations enable_fops = { ++ .open = tracing_open_generic, ++ .read = show_enable, ++ .write = do_enable, ++}; ++ ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++static const struct file_operations pid_fops = { ++ .open = tracing_open_generic, ++ .read = show_pid, ++ .write = do_pid, ++}; ++ ++static const struct file_operations maxlatproc_fops = { ++ .open = tracing_open_generic, ++ .read = show_maxlatproc, ++}; ++#endif ++ ++#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) ++static notrace void probe_preemptirqsoff_hist(void *v, int reason, ++ int starthist) ++{ ++ int cpu = raw_smp_processor_id(); ++ int time_set = 0; ++ ++ if (starthist) { ++ cycle_t uninitialized_var(start); ++ ++ if (!preempt_count() && !irqs_disabled()) ++ return; ++ ++#ifdef CONFIG_INTERRUPT_OFF_HIST ++ if ((reason == IRQS_OFF || reason == TRACE_START) && ++ !per_cpu(hist_irqsoff_counting, cpu)) { ++ per_cpu(hist_irqsoff_counting, cpu) = 1; ++ start = ftrace_now(cpu); ++ time_set++; ++ per_cpu(hist_irqsoff_start, cpu) = start; ++ } ++#endif ++ ++#ifdef CONFIG_PREEMPT_OFF_HIST ++ if ((reason == PREEMPT_OFF || reason == TRACE_START) && ++ !per_cpu(hist_preemptoff_counting, cpu)) { ++ per_cpu(hist_preemptoff_counting, cpu) = 1; ++ if (!(time_set++)) ++ start = ftrace_now(cpu); ++ per_cpu(hist_preemptoff_start, cpu) = start; ++ } ++#endif ++ ++#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) ++ if (per_cpu(hist_irqsoff_counting, cpu) && ++ per_cpu(hist_preemptoff_counting, cpu) && ++ !per_cpu(hist_preemptirqsoff_counting, cpu)) { ++ per_cpu(hist_preemptirqsoff_counting, cpu) = 1; ++ if (!time_set) ++ start = ftrace_now(cpu); ++ per_cpu(hist_preemptirqsoff_start, cpu) = start; ++ } ++#endif ++ } else { ++ cycle_t uninitialized_var(stop); ++ ++#ifdef CONFIG_INTERRUPT_OFF_HIST ++ if ((reason == IRQS_ON || reason == TRACE_STOP) && ++ per_cpu(hist_irqsoff_counting, cpu)) { ++ cycle_t start = per_cpu(hist_irqsoff_start, cpu); ++ ++ stop = ftrace_now(cpu); ++ time_set++; ++ if (start) { ++ long latency = ((long) (stop - start)) / ++ NSECS_PER_USECS; ++ ++ latency_hist(IRQSOFF_LATENCY, cpu, latency, 0, ++ stop, NULL); ++ } ++ per_cpu(hist_irqsoff_counting, cpu) = 0; ++ } ++#endif ++ ++#ifdef CONFIG_PREEMPT_OFF_HIST ++ if ((reason == PREEMPT_ON || reason == TRACE_STOP) && ++ per_cpu(hist_preemptoff_counting, cpu)) { ++ cycle_t start = per_cpu(hist_preemptoff_start, cpu); ++ ++ if (!(time_set++)) ++ stop = ftrace_now(cpu); ++ if (start) { ++ long latency = ((long) (stop - start)) / ++ NSECS_PER_USECS; ++ ++ latency_hist(PREEMPTOFF_LATENCY, cpu, latency, ++ 0, stop, NULL); ++ } ++ per_cpu(hist_preemptoff_counting, cpu) = 0; ++ } ++#endif ++ ++#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) ++ if ((!per_cpu(hist_irqsoff_counting, cpu) || ++ !per_cpu(hist_preemptoff_counting, cpu)) && ++ per_cpu(hist_preemptirqsoff_counting, cpu)) { ++ cycle_t start = per_cpu(hist_preemptirqsoff_start, cpu); ++ ++ if (!time_set) ++ stop = ftrace_now(cpu); ++ if (start) { ++ long latency = ((long) (stop - start)) / ++ NSECS_PER_USECS; ++ ++ latency_hist(PREEMPTIRQSOFF_LATENCY, cpu, ++ latency, 0, stop, NULL); ++ } ++ per_cpu(hist_preemptirqsoff_counting, cpu) = 0; ++ } ++#endif ++ } ++} ++#endif ++ ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++static DEFINE_RAW_SPINLOCK(wakeup_lock); ++static notrace void probe_sched_migrate_task(void *v, struct task_struct *task, ++ int cpu) ++{ ++ int old_cpu = task_cpu(task); ++ ++ if (cpu != old_cpu) { ++ unsigned long flags; ++ struct task_struct *cpu_wakeup_task; ++ ++ raw_spin_lock_irqsave(&wakeup_lock, flags); ++ ++ cpu_wakeup_task = per_cpu(wakeup_task, old_cpu); ++ if (task == cpu_wakeup_task) { ++ put_task_struct(cpu_wakeup_task); ++ per_cpu(wakeup_task, old_cpu) = NULL; ++ cpu_wakeup_task = per_cpu(wakeup_task, cpu) = task; ++ get_task_struct(cpu_wakeup_task); ++ } ++ ++ raw_spin_unlock_irqrestore(&wakeup_lock, flags); ++ } ++} ++ ++static notrace void probe_wakeup_latency_hist_start(void *v, ++ struct task_struct *p, int success) ++{ ++ unsigned long flags; ++ struct task_struct *curr = current; ++ int cpu = task_cpu(p); ++ struct task_struct *cpu_wakeup_task; ++ ++ raw_spin_lock_irqsave(&wakeup_lock, flags); ++ ++ cpu_wakeup_task = per_cpu(wakeup_task, cpu); ++ ++ if (wakeup_pid) { ++ if ((cpu_wakeup_task && p->prio == cpu_wakeup_task->prio) || ++ p->prio == curr->prio) ++ per_cpu(wakeup_sharedprio, cpu) = 1; ++ if (likely(wakeup_pid != task_pid_nr(p))) ++ goto out; ++ } else { ++ if (likely(!rt_task(p)) || ++ (cpu_wakeup_task && p->prio > cpu_wakeup_task->prio) || ++ p->prio > curr->prio) ++ goto out; ++ if ((cpu_wakeup_task && p->prio == cpu_wakeup_task->prio) || ++ p->prio == curr->prio) ++ per_cpu(wakeup_sharedprio, cpu) = 1; ++ } ++ ++ if (cpu_wakeup_task) ++ put_task_struct(cpu_wakeup_task); ++ cpu_wakeup_task = per_cpu(wakeup_task, cpu) = p; ++ get_task_struct(cpu_wakeup_task); ++ cpu_wakeup_task->preempt_timestamp_hist = ++ ftrace_now(raw_smp_processor_id()); ++out: ++ raw_spin_unlock_irqrestore(&wakeup_lock, flags); ++} ++ ++static notrace void probe_wakeup_latency_hist_stop(void *v, ++ struct task_struct *prev, struct task_struct *next) ++{ ++ unsigned long flags; ++ int cpu = task_cpu(next); ++ long latency; ++ cycle_t stop; ++ struct task_struct *cpu_wakeup_task; ++ ++ raw_spin_lock_irqsave(&wakeup_lock, flags); ++ ++ cpu_wakeup_task = per_cpu(wakeup_task, cpu); ++ ++ if (cpu_wakeup_task == NULL) ++ goto out; ++ ++ /* Already running? */ ++ if (unlikely(current == cpu_wakeup_task)) ++ goto out_reset; ++ ++ if (next != cpu_wakeup_task) { ++ if (next->prio < cpu_wakeup_task->prio) ++ goto out_reset; ++ ++ if (next->prio == cpu_wakeup_task->prio) ++ per_cpu(wakeup_sharedprio, cpu) = 1; ++ ++ goto out; ++ } ++ ++ if (current->prio == cpu_wakeup_task->prio) ++ per_cpu(wakeup_sharedprio, cpu) = 1; ++ ++ /* ++ * The task we are waiting for is about to be switched to. ++ * Calculate latency and store it in histogram. ++ */ ++ stop = ftrace_now(raw_smp_processor_id()); ++ ++ latency = ((long) (stop - next->preempt_timestamp_hist)) / ++ NSECS_PER_USECS; ++ ++ if (per_cpu(wakeup_sharedprio, cpu)) { ++ latency_hist(WAKEUP_LATENCY_SHAREDPRIO, cpu, latency, 0, stop, ++ next); ++ per_cpu(wakeup_sharedprio, cpu) = 0; ++ } else { ++ latency_hist(WAKEUP_LATENCY, cpu, latency, 0, stop, next); ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ if (timerandwakeup_enabled_data.enabled) { ++ latency_hist(TIMERANDWAKEUP_LATENCY, cpu, ++ next->timer_offset + latency, next->timer_offset, ++ stop, next); ++ } ++#endif ++ } ++ ++out_reset: ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ next->timer_offset = 0; ++#endif ++ put_task_struct(cpu_wakeup_task); ++ per_cpu(wakeup_task, cpu) = NULL; ++out: ++ raw_spin_unlock_irqrestore(&wakeup_lock, flags); ++} ++#endif ++ ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++static notrace void probe_hrtimer_interrupt(void *v, int cpu, ++ long long latency_ns, struct task_struct *curr, ++ struct task_struct *task) ++{ ++ if (latency_ns <= 0 && task != NULL && rt_task(task) && ++ (task->prio < curr->prio || ++ (task->prio == curr->prio && ++ !cpumask_test_cpu(cpu, &task->cpus_allowed)))) { ++ long latency; ++ cycle_t now; ++ ++ if (missed_timer_offsets_pid) { ++ if (likely(missed_timer_offsets_pid != ++ task_pid_nr(task))) ++ return; ++ } ++ ++ now = ftrace_now(cpu); ++ latency = (long) div_s64(-latency_ns, NSECS_PER_USECS); ++ latency_hist(MISSED_TIMER_OFFSETS, cpu, latency, latency, now, ++ task); ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++ task->timer_offset = latency; ++#endif ++ } ++} ++#endif ++ ++static __init int latency_hist_init(void) ++{ ++ struct dentry *latency_hist_root = NULL; ++ struct dentry *dentry; ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++ struct dentry *dentry_sharedprio; ++#endif ++ struct dentry *entry; ++ struct dentry *enable_root; ++ int i = 0; ++ struct hist_data *my_hist; ++ char name[64]; ++ char *cpufmt = "CPU%d"; ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) || \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++ char *cpufmt_maxlatproc = "max_latency-CPU%d"; ++ struct maxlatproc_data *mp = NULL; ++#endif ++ ++ dentry = tracing_init_dentry(); ++ latency_hist_root = debugfs_create_dir(latency_hist_dir_root, dentry); ++ enable_root = debugfs_create_dir("enable", latency_hist_root); ++ ++#ifdef CONFIG_INTERRUPT_OFF_HIST ++ dentry = debugfs_create_dir(irqsoff_hist_dir, latency_hist_root); ++ for_each_possible_cpu(i) { ++ sprintf(name, cpufmt, i); ++ entry = debugfs_create_file(name, 0444, dentry, ++ &per_cpu(irqsoff_hist, i), &latency_hist_fops); ++ my_hist = &per_cpu(irqsoff_hist, i); ++ atomic_set(&my_hist->hist_mode, 1); ++ my_hist->min_lat = LONG_MAX; ++ } ++ entry = debugfs_create_file("reset", 0644, dentry, ++ (void *)IRQSOFF_LATENCY, &latency_hist_reset_fops); ++#endif ++ ++#ifdef CONFIG_PREEMPT_OFF_HIST ++ dentry = debugfs_create_dir(preemptoff_hist_dir, ++ latency_hist_root); ++ for_each_possible_cpu(i) { ++ sprintf(name, cpufmt, i); ++ entry = debugfs_create_file(name, 0444, dentry, ++ &per_cpu(preemptoff_hist, i), &latency_hist_fops); ++ my_hist = &per_cpu(preemptoff_hist, i); ++ atomic_set(&my_hist->hist_mode, 1); ++ my_hist->min_lat = LONG_MAX; ++ } ++ entry = debugfs_create_file("reset", 0644, dentry, ++ (void *)PREEMPTOFF_LATENCY, &latency_hist_reset_fops); ++#endif ++ ++#if defined(CONFIG_INTERRUPT_OFF_HIST) && defined(CONFIG_PREEMPT_OFF_HIST) ++ dentry = debugfs_create_dir(preemptirqsoff_hist_dir, ++ latency_hist_root); ++ for_each_possible_cpu(i) { ++ sprintf(name, cpufmt, i); ++ entry = debugfs_create_file(name, 0444, dentry, ++ &per_cpu(preemptirqsoff_hist, i), &latency_hist_fops); ++ my_hist = &per_cpu(preemptirqsoff_hist, i); ++ atomic_set(&my_hist->hist_mode, 1); ++ my_hist->min_lat = LONG_MAX; ++ } ++ entry = debugfs_create_file("reset", 0644, dentry, ++ (void *)PREEMPTIRQSOFF_LATENCY, &latency_hist_reset_fops); ++#endif ++ ++#if defined(CONFIG_INTERRUPT_OFF_HIST) || defined(CONFIG_PREEMPT_OFF_HIST) ++ entry = debugfs_create_file("preemptirqsoff", 0644, ++ enable_root, (void *)&preemptirqsoff_enabled_data, ++ &enable_fops); ++#endif ++ ++#ifdef CONFIG_WAKEUP_LATENCY_HIST ++ dentry = debugfs_create_dir(wakeup_latency_hist_dir, ++ latency_hist_root); ++ dentry_sharedprio = debugfs_create_dir( ++ wakeup_latency_hist_dir_sharedprio, dentry); ++ for_each_possible_cpu(i) { ++ sprintf(name, cpufmt, i); ++ ++ entry = debugfs_create_file(name, 0444, dentry, ++ &per_cpu(wakeup_latency_hist, i), ++ &latency_hist_fops); ++ my_hist = &per_cpu(wakeup_latency_hist, i); ++ atomic_set(&my_hist->hist_mode, 1); ++ my_hist->min_lat = LONG_MAX; ++ ++ entry = debugfs_create_file(name, 0444, dentry_sharedprio, ++ &per_cpu(wakeup_latency_hist_sharedprio, i), ++ &latency_hist_fops); ++ my_hist = &per_cpu(wakeup_latency_hist_sharedprio, i); ++ atomic_set(&my_hist->hist_mode, 1); ++ my_hist->min_lat = LONG_MAX; ++ ++ sprintf(name, cpufmt_maxlatproc, i); ++ ++ mp = &per_cpu(wakeup_maxlatproc, i); ++ entry = debugfs_create_file(name, 0444, dentry, mp, ++ &maxlatproc_fops); ++ clear_maxlatprocdata(mp); ++ ++ mp = &per_cpu(wakeup_maxlatproc_sharedprio, i); ++ entry = debugfs_create_file(name, 0444, dentry_sharedprio, mp, ++ &maxlatproc_fops); ++ clear_maxlatprocdata(mp); ++ } ++ entry = debugfs_create_file("pid", 0644, dentry, ++ (void *)&wakeup_pid, &pid_fops); ++ entry = debugfs_create_file("reset", 0644, dentry, ++ (void *)WAKEUP_LATENCY, &latency_hist_reset_fops); ++ entry = debugfs_create_file("reset", 0644, dentry_sharedprio, ++ (void *)WAKEUP_LATENCY_SHAREDPRIO, &latency_hist_reset_fops); ++ entry = debugfs_create_file("wakeup", 0644, ++ enable_root, (void *)&wakeup_latency_enabled_data, ++ &enable_fops); ++#endif ++ ++#ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST ++ dentry = debugfs_create_dir(missed_timer_offsets_dir, ++ latency_hist_root); ++ for_each_possible_cpu(i) { ++ sprintf(name, cpufmt, i); ++ entry = debugfs_create_file(name, 0444, dentry, ++ &per_cpu(missed_timer_offsets, i), &latency_hist_fops); ++ my_hist = &per_cpu(missed_timer_offsets, i); ++ atomic_set(&my_hist->hist_mode, 1); ++ my_hist->min_lat = LONG_MAX; ++ ++ sprintf(name, cpufmt_maxlatproc, i); ++ mp = &per_cpu(missed_timer_offsets_maxlatproc, i); ++ entry = debugfs_create_file(name, 0444, dentry, mp, ++ &maxlatproc_fops); ++ clear_maxlatprocdata(mp); ++ } ++ entry = debugfs_create_file("pid", 0644, dentry, ++ (void *)&missed_timer_offsets_pid, &pid_fops); ++ entry = debugfs_create_file("reset", 0644, dentry, ++ (void *)MISSED_TIMER_OFFSETS, &latency_hist_reset_fops); ++ entry = debugfs_create_file("missed_timer_offsets", 0644, ++ enable_root, (void *)&missed_timer_offsets_enabled_data, ++ &enable_fops); ++#endif ++ ++#if defined(CONFIG_WAKEUP_LATENCY_HIST) && \ ++ defined(CONFIG_MISSED_TIMER_OFFSETS_HIST) ++ dentry = debugfs_create_dir(timerandwakeup_latency_hist_dir, ++ latency_hist_root); ++ for_each_possible_cpu(i) { ++ sprintf(name, cpufmt, i); ++ entry = debugfs_create_file(name, 0444, dentry, ++ &per_cpu(timerandwakeup_latency_hist, i), ++ &latency_hist_fops); ++ my_hist = &per_cpu(timerandwakeup_latency_hist, i); ++ atomic_set(&my_hist->hist_mode, 1); ++ my_hist->min_lat = LONG_MAX; ++ ++ sprintf(name, cpufmt_maxlatproc, i); ++ mp = &per_cpu(timerandwakeup_maxlatproc, i); ++ entry = debugfs_create_file(name, 0444, dentry, mp, ++ &maxlatproc_fops); ++ clear_maxlatprocdata(mp); ++ } ++ entry = debugfs_create_file("reset", 0644, dentry, ++ (void *)TIMERANDWAKEUP_LATENCY, &latency_hist_reset_fops); ++ entry = debugfs_create_file("timerandwakeup", 0644, ++ enable_root, (void *)&timerandwakeup_enabled_data, ++ &enable_fops); ++#endif ++ return 0; ++} ++ ++device_initcall(latency_hist_init); +diff -Nur linux-3.18.14.orig/kernel/trace/Makefile linux-3.18.14-rt/kernel/trace/Makefile +--- linux-3.18.14.orig/kernel/trace/Makefile 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/trace/Makefile 2015-05-31 15:32:48.989635362 -0500 +@@ -36,6 +36,10 @@ + obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o + obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o + obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o ++obj-$(CONFIG_INTERRUPT_OFF_HIST) += latency_hist.o ++obj-$(CONFIG_PREEMPT_OFF_HIST) += latency_hist.o ++obj-$(CONFIG_WAKEUP_LATENCY_HIST) += latency_hist.o ++obj-$(CONFIG_MISSED_TIMER_OFFSETS_HIST) += latency_hist.o + obj-$(CONFIG_NOP_TRACER) += trace_nop.o + obj-$(CONFIG_STACK_TRACER) += trace_stack.o + obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o +diff -Nur linux-3.18.14.orig/kernel/trace/trace.c linux-3.18.14-rt/kernel/trace/trace.c +--- linux-3.18.14.orig/kernel/trace/trace.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/trace/trace.c 2015-05-31 15:32:49.021635361 -0500 +@@ -1579,6 +1579,7 @@ + struct task_struct *tsk = current; + + entry->preempt_count = pc & 0xff; ++ entry->preempt_lazy_count = preempt_lazy_count(); + entry->pid = (tsk) ? tsk->pid : 0; + entry->flags = + #ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT +@@ -1588,8 +1589,11 @@ + #endif + ((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) | + ((pc & SOFTIRQ_MASK) ? TRACE_FLAG_SOFTIRQ : 0) | +- (tif_need_resched() ? TRACE_FLAG_NEED_RESCHED : 0) | ++ (tif_need_resched_now() ? TRACE_FLAG_NEED_RESCHED : 0) | ++ (need_resched_lazy() ? TRACE_FLAG_NEED_RESCHED_LAZY : 0) | + (test_preempt_need_resched() ? TRACE_FLAG_PREEMPT_RESCHED : 0); ++ ++ entry->migrate_disable = (tsk) ? __migrate_disabled(tsk) & 0xFF : 0; + } + EXPORT_SYMBOL_GPL(tracing_generic_entry_update); + +@@ -2509,14 +2513,17 @@ + + static void print_lat_help_header(struct seq_file *m) + { +- seq_puts(m, "# _------=> CPU# \n"); +- seq_puts(m, "# / _-----=> irqs-off \n"); +- seq_puts(m, "# | / _----=> need-resched \n"); +- seq_puts(m, "# || / _---=> hardirq/softirq \n"); +- seq_puts(m, "# ||| / _--=> preempt-depth \n"); +- seq_puts(m, "# |||| / delay \n"); +- seq_puts(m, "# cmd pid ||||| time | caller \n"); +- seq_puts(m, "# \\ / ||||| \\ | / \n"); ++ seq_puts(m, "# _--------=> CPU# \n"); ++ seq_puts(m, "# / _-------=> irqs-off \n"); ++ seq_puts(m, "# | / _------=> need-resched \n"); ++ seq_puts(m, "# || / _-----=> need-resched_lazy \n"); ++ seq_puts(m, "# ||| / _----=> hardirq/softirq \n"); ++ seq_puts(m, "# |||| / _---=> preempt-depth \n"); ++ seq_puts(m, "# ||||| / _--=> preempt-lazy-depth\n"); ++ seq_puts(m, "# |||||| / _-=> migrate-disable \n"); ++ seq_puts(m, "# ||||||| / delay \n"); ++ seq_puts(m, "# cmd pid |||||||| time | caller \n"); ++ seq_puts(m, "# \\ / |||||||| \\ | / \n"); + } + + static void print_event_info(struct trace_buffer *buf, struct seq_file *m) +@@ -2540,13 +2547,16 @@ + static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file *m) + { + print_event_info(buf, m); +- seq_puts(m, "# _-----=> irqs-off\n"); +- seq_puts(m, "# / _----=> need-resched\n"); +- seq_puts(m, "# | / _---=> hardirq/softirq\n"); +- seq_puts(m, "# || / _--=> preempt-depth\n"); +- seq_puts(m, "# ||| / delay\n"); +- seq_puts(m, "# TASK-PID CPU# |||| TIMESTAMP FUNCTION\n"); +- seq_puts(m, "# | | | |||| | |\n"); ++ seq_puts(m, "# _-------=> irqs-off \n"); ++ seq_puts(m, "# / _------=> need-resched \n"); ++ seq_puts(m, "# |/ _-----=> need-resched_lazy \n"); ++ seq_puts(m, "# ||/ _----=> hardirq/softirq \n"); ++ seq_puts(m, "# |||/ _---=> preempt-depth \n"); ++ seq_puts(m, "# ||||/ _--=> preempt-lazy-depth\n"); ++ seq_puts(m, "# ||||| / _-=> migrate-disable \n"); ++ seq_puts(m, "# |||||| / delay\n"); ++ seq_puts(m, "# TASK-PID CPU# |||||| TIMESTAMP FUNCTION\n"); ++ seq_puts(m, "# | | | |||||| | |\n"); + } + + void +diff -Nur linux-3.18.14.orig/kernel/trace/trace_events.c linux-3.18.14-rt/kernel/trace/trace_events.c +--- linux-3.18.14.orig/kernel/trace/trace_events.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/trace/trace_events.c 2015-05-31 15:32:49.025635362 -0500 +@@ -162,6 +162,8 @@ + __common_field(unsigned char, flags); + __common_field(unsigned char, preempt_count); + __common_field(int, pid); ++ __common_field(unsigned short, migrate_disable); ++ __common_field(unsigned short, padding); + + return ret; + } +diff -Nur linux-3.18.14.orig/kernel/trace/trace.h linux-3.18.14-rt/kernel/trace/trace.h +--- linux-3.18.14.orig/kernel/trace/trace.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/trace/trace.h 2015-05-31 15:32:49.021635361 -0500 +@@ -119,6 +119,7 @@ + * NEED_RESCHED - reschedule is requested + * HARDIRQ - inside an interrupt handler + * SOFTIRQ - inside a softirq handler ++ * NEED_RESCHED_LAZY - lazy reschedule is requested + */ + enum trace_flag_type { + TRACE_FLAG_IRQS_OFF = 0x01, +@@ -127,6 +128,7 @@ + TRACE_FLAG_HARDIRQ = 0x08, + TRACE_FLAG_SOFTIRQ = 0x10, + TRACE_FLAG_PREEMPT_RESCHED = 0x20, ++ TRACE_FLAG_NEED_RESCHED_LAZY = 0x40, + }; + + #define TRACE_BUF_SIZE 1024 +diff -Nur linux-3.18.14.orig/kernel/trace/trace_irqsoff.c linux-3.18.14-rt/kernel/trace/trace_irqsoff.c +--- linux-3.18.14.orig/kernel/trace/trace_irqsoff.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/trace/trace_irqsoff.c 2015-05-31 15:32:49.025635362 -0500 +@@ -17,6 +17,7 @@ + #include + + #include "trace.h" ++#include + + static struct trace_array *irqsoff_trace __read_mostly; + static int tracer_enabled __read_mostly; +@@ -435,11 +436,13 @@ + { + if (preempt_trace() || irq_trace()) + start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); ++ trace_preemptirqsoff_hist(TRACE_START, 1); + } + EXPORT_SYMBOL_GPL(start_critical_timings); + + void stop_critical_timings(void) + { ++ trace_preemptirqsoff_hist(TRACE_STOP, 0); + if (preempt_trace() || irq_trace()) + stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); + } +@@ -449,6 +452,7 @@ + #ifdef CONFIG_PROVE_LOCKING + void time_hardirqs_on(unsigned long a0, unsigned long a1) + { ++ trace_preemptirqsoff_hist(IRQS_ON, 0); + if (!preempt_trace() && irq_trace()) + stop_critical_timing(a0, a1); + } +@@ -457,6 +461,7 @@ + { + if (!preempt_trace() && irq_trace()) + start_critical_timing(a0, a1); ++ trace_preemptirqsoff_hist(IRQS_OFF, 1); + } + + #else /* !CONFIG_PROVE_LOCKING */ +@@ -482,6 +487,7 @@ + */ + void trace_hardirqs_on(void) + { ++ trace_preemptirqsoff_hist(IRQS_ON, 0); + if (!preempt_trace() && irq_trace()) + stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1); + } +@@ -491,11 +497,13 @@ + { + if (!preempt_trace() && irq_trace()) + start_critical_timing(CALLER_ADDR0, CALLER_ADDR1); ++ trace_preemptirqsoff_hist(IRQS_OFF, 1); + } + EXPORT_SYMBOL(trace_hardirqs_off); + + __visible void trace_hardirqs_on_caller(unsigned long caller_addr) + { ++ trace_preemptirqsoff_hist(IRQS_ON, 0); + if (!preempt_trace() && irq_trace()) + stop_critical_timing(CALLER_ADDR0, caller_addr); + } +@@ -505,6 +513,7 @@ + { + if (!preempt_trace() && irq_trace()) + start_critical_timing(CALLER_ADDR0, caller_addr); ++ trace_preemptirqsoff_hist(IRQS_OFF, 1); + } + EXPORT_SYMBOL(trace_hardirqs_off_caller); + +@@ -514,12 +523,14 @@ + #ifdef CONFIG_PREEMPT_TRACER + void trace_preempt_on(unsigned long a0, unsigned long a1) + { ++ trace_preemptirqsoff_hist(PREEMPT_ON, 0); + if (preempt_trace() && !irq_trace()) + stop_critical_timing(a0, a1); + } + + void trace_preempt_off(unsigned long a0, unsigned long a1) + { ++ trace_preemptirqsoff_hist(PREEMPT_ON, 1); + if (preempt_trace() && !irq_trace()) + start_critical_timing(a0, a1); + } +diff -Nur linux-3.18.14.orig/kernel/trace/trace_output.c linux-3.18.14-rt/kernel/trace/trace_output.c +--- linux-3.18.14.orig/kernel/trace/trace_output.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/trace/trace_output.c 2015-05-31 15:32:49.025635362 -0500 +@@ -410,6 +410,7 @@ + { + char hardsoft_irq; + char need_resched; ++ char need_resched_lazy; + char irqs_off; + int hardirq; + int softirq; +@@ -438,6 +439,8 @@ + need_resched = '.'; + break; + } ++ need_resched_lazy = ++ (entry->flags & TRACE_FLAG_NEED_RESCHED_LAZY) ? 'L' : '.'; + + hardsoft_irq = + (hardirq && softirq) ? 'H' : +@@ -445,8 +448,9 @@ + softirq ? 's' : + '.'; + +- if (!trace_seq_printf(s, "%c%c%c", +- irqs_off, need_resched, hardsoft_irq)) ++ if (!trace_seq_printf(s, "%c%c%c%c", ++ irqs_off, need_resched, need_resched_lazy, ++ hardsoft_irq)) + return 0; + + if (entry->preempt_count) +@@ -454,6 +458,16 @@ + else + ret = trace_seq_putc(s, '.'); + ++ if (entry->preempt_lazy_count) ++ ret = trace_seq_printf(s, "%x", entry->preempt_lazy_count); ++ else ++ ret = trace_seq_putc(s, '.'); ++ ++ if (entry->migrate_disable) ++ ret = trace_seq_printf(s, "%x", entry->migrate_disable); ++ else ++ ret = trace_seq_putc(s, '.'); ++ + return ret; + } + +diff -Nur linux-3.18.14.orig/kernel/user.c linux-3.18.14-rt/kernel/user.c +--- linux-3.18.14.orig/kernel/user.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/user.c 2015-05-31 15:32:49.045635362 -0500 +@@ -158,11 +158,11 @@ + if (!up) + return; + +- local_irq_save(flags); ++ local_irq_save_nort(flags); + if (atomic_dec_and_lock(&up->__count, &uidhash_lock)) + free_user(up, flags); + else +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + } + + struct user_struct *alloc_uid(kuid_t uid) +diff -Nur linux-3.18.14.orig/kernel/watchdog.c linux-3.18.14-rt/kernel/watchdog.c +--- linux-3.18.14.orig/kernel/watchdog.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/watchdog.c 2015-05-31 15:32:49.065635361 -0500 +@@ -248,6 +248,8 @@ + + #ifdef CONFIG_HARDLOCKUP_DETECTOR + ++static DEFINE_RAW_SPINLOCK(watchdog_output_lock); ++ + static struct perf_event_attr wd_hw_attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES, +@@ -281,13 +283,21 @@ + /* only print hardlockups once */ + if (__this_cpu_read(hard_watchdog_warn) == true) + return; ++ /* ++ * If early-printk is enabled then make sure we do not ++ * lock up in printk() and kill console logging: ++ */ ++ printk_kill(); + +- if (hardlockup_panic) ++ if (hardlockup_panic) { + panic("Watchdog detected hard LOCKUP on cpu %d", + this_cpu); +- else ++ } else { ++ raw_spin_lock(&watchdog_output_lock); + WARN(1, "Watchdog detected hard LOCKUP on cpu %d", + this_cpu); ++ raw_spin_unlock(&watchdog_output_lock); ++ } + + __this_cpu_write(hard_watchdog_warn, true); + return; +@@ -430,6 +440,7 @@ + /* kick off the timer for the hardlockup detector */ + hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer->function = watchdog_timer_fn; ++ hrtimer->irqsafe = 1; + + /* Enable the perf event */ + watchdog_nmi_enable(cpu); +diff -Nur linux-3.18.14.orig/kernel/workqueue.c linux-3.18.14-rt/kernel/workqueue.c +--- linux-3.18.14.orig/kernel/workqueue.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/workqueue.c 2015-05-31 15:32:49.069635361 -0500 +@@ -48,6 +48,8 @@ + #include + #include + #include ++#include ++#include + + #include "workqueue_internal.h" + +@@ -121,15 +123,20 @@ + * cpu or grabbing pool->lock is enough for read access. If + * POOL_DISASSOCIATED is set, it's identical to L. + * ++ * On RT we need the extra protection via rt_lock_idle_list() for ++ * the list manipulations against read access from ++ * wq_worker_sleeping(). All other places are nicely serialized via ++ * pool->lock. ++ * + * A: pool->attach_mutex protected. + * + * PL: wq_pool_mutex protected. + * +- * PR: wq_pool_mutex protected for writes. Sched-RCU protected for reads. ++ * PR: wq_pool_mutex protected for writes. RCU protected for reads. + * + * WQ: wq->mutex protected. + * +- * WR: wq->mutex protected for writes. Sched-RCU protected for reads. ++ * WR: wq->mutex protected for writes. RCU protected for reads. + * + * MD: wq_mayday_lock protected. + */ +@@ -177,7 +184,7 @@ + atomic_t nr_running ____cacheline_aligned_in_smp; + + /* +- * Destruction of pool is sched-RCU protected to allow dereferences ++ * Destruction of pool is RCU protected to allow dereferences + * from get_work_pool(). + */ + struct rcu_head rcu; +@@ -206,7 +213,7 @@ + /* + * Release of unbound pwq is punted to system_wq. See put_pwq() + * and pwq_unbound_release_workfn() for details. pool_workqueue +- * itself is also sched-RCU protected so that the first pwq can be ++ * itself is also RCU protected so that the first pwq can be + * determined without grabbing wq->mutex. + */ + struct work_struct unbound_release_work; +@@ -321,6 +328,8 @@ + struct workqueue_struct *system_freezable_power_efficient_wq __read_mostly; + EXPORT_SYMBOL_GPL(system_freezable_power_efficient_wq); + ++static DEFINE_LOCAL_IRQ_LOCK(pendingb_lock); ++ + static int worker_thread(void *__worker); + static void copy_workqueue_attrs(struct workqueue_attrs *to, + const struct workqueue_attrs *from); +@@ -329,14 +338,14 @@ + #include + + #define assert_rcu_or_pool_mutex() \ +- rcu_lockdep_assert(rcu_read_lock_sched_held() || \ ++ rcu_lockdep_assert(rcu_read_lock_held() || \ + lockdep_is_held(&wq_pool_mutex), \ +- "sched RCU or wq_pool_mutex should be held") ++ "RCU or wq_pool_mutex should be held") + + #define assert_rcu_or_wq_mutex(wq) \ +- rcu_lockdep_assert(rcu_read_lock_sched_held() || \ ++ rcu_lockdep_assert(rcu_read_lock_held() || \ + lockdep_is_held(&wq->mutex), \ +- "sched RCU or wq->mutex should be held") ++ "RCU or wq->mutex should be held") + + #define for_each_cpu_worker_pool(pool, cpu) \ + for ((pool) = &per_cpu(cpu_worker_pools, cpu)[0]; \ +@@ -348,7 +357,7 @@ + * @pool: iteration cursor + * @pi: integer used for iteration + * +- * This must be called either with wq_pool_mutex held or sched RCU read ++ * This must be called either with wq_pool_mutex held or RCU read + * locked. If the pool needs to be used beyond the locking in effect, the + * caller is responsible for guaranteeing that the pool stays online. + * +@@ -380,7 +389,7 @@ + * @pwq: iteration cursor + * @wq: the target workqueue + * +- * This must be called either with wq->mutex held or sched RCU read locked. ++ * This must be called either with wq->mutex held or RCU read locked. + * If the pwq needs to be used beyond the locking in effect, the caller is + * responsible for guaranteeing that the pwq stays online. + * +@@ -392,6 +401,31 @@ + if (({ assert_rcu_or_wq_mutex(wq); false; })) { } \ + else + ++#ifdef CONFIG_PREEMPT_RT_BASE ++static inline void rt_lock_idle_list(struct worker_pool *pool) ++{ ++ preempt_disable(); ++} ++static inline void rt_unlock_idle_list(struct worker_pool *pool) ++{ ++ preempt_enable(); ++} ++static inline void sched_lock_idle_list(struct worker_pool *pool) { } ++static inline void sched_unlock_idle_list(struct worker_pool *pool) { } ++#else ++static inline void rt_lock_idle_list(struct worker_pool *pool) { } ++static inline void rt_unlock_idle_list(struct worker_pool *pool) { } ++static inline void sched_lock_idle_list(struct worker_pool *pool) ++{ ++ spin_lock_irq(&pool->lock); ++} ++static inline void sched_unlock_idle_list(struct worker_pool *pool) ++{ ++ spin_unlock_irq(&pool->lock); ++} ++#endif ++ ++ + #ifdef CONFIG_DEBUG_OBJECTS_WORK + + static struct debug_obj_descr work_debug_descr; +@@ -542,7 +576,7 @@ + * @wq: the target workqueue + * @node: the node ID + * +- * This must be called either with pwq_lock held or sched RCU read locked. ++ * This must be called either with pwq_lock held or RCU read locked. + * If the pwq needs to be used beyond the locking in effect, the caller is + * responsible for guaranteeing that the pwq stays online. + * +@@ -646,8 +680,8 @@ + * @work: the work item of interest + * + * Pools are created and destroyed under wq_pool_mutex, and allows read +- * access under sched-RCU read lock. As such, this function should be +- * called under wq_pool_mutex or with preemption disabled. ++ * access under RCU read lock. As such, this function should be ++ * called under wq_pool_mutex or inside of a rcu_read_lock() region. + * + * All fields of the returned pool are accessible as long as the above + * mentioned locking is in effect. If the returned pool needs to be used +@@ -784,51 +818,44 @@ + */ + static void wake_up_worker(struct worker_pool *pool) + { +- struct worker *worker = first_idle_worker(pool); ++ struct worker *worker; ++ ++ rt_lock_idle_list(pool); ++ ++ worker = first_idle_worker(pool); + + if (likely(worker)) + wake_up_process(worker->task); ++ ++ rt_unlock_idle_list(pool); + } + + /** +- * wq_worker_waking_up - a worker is waking up +- * @task: task waking up +- * @cpu: CPU @task is waking up to +- * +- * This function is called during try_to_wake_up() when a worker is +- * being awoken. ++ * wq_worker_running - a worker is running again ++ * @task: task returning from sleep + * +- * CONTEXT: +- * spin_lock_irq(rq->lock) ++ * This function is called when a worker returns from schedule() + */ +-void wq_worker_waking_up(struct task_struct *task, int cpu) ++void wq_worker_running(struct task_struct *task) + { + struct worker *worker = kthread_data(task); + +- if (!(worker->flags & WORKER_NOT_RUNNING)) { +- WARN_ON_ONCE(worker->pool->cpu != cpu); ++ if (!worker->sleeping) ++ return; ++ if (!(worker->flags & WORKER_NOT_RUNNING)) + atomic_inc(&worker->pool->nr_running); +- } ++ worker->sleeping = 0; + } + + /** + * wq_worker_sleeping - a worker is going to sleep + * @task: task going to sleep +- * @cpu: CPU in question, must be the current CPU number +- * +- * This function is called during schedule() when a busy worker is +- * going to sleep. Worker on the same cpu can be woken up by +- * returning pointer to its task. +- * +- * CONTEXT: +- * spin_lock_irq(rq->lock) +- * +- * Return: +- * Worker task on @cpu to wake up, %NULL if none. ++ * This function is called from schedule() when a busy worker is ++ * going to sleep. + */ +-struct task_struct *wq_worker_sleeping(struct task_struct *task, int cpu) ++void wq_worker_sleeping(struct task_struct *task) + { +- struct worker *worker = kthread_data(task), *to_wakeup = NULL; ++ struct worker *worker = kthread_data(task); + struct worker_pool *pool; + + /* +@@ -837,29 +864,26 @@ + * checking NOT_RUNNING. + */ + if (worker->flags & WORKER_NOT_RUNNING) +- return NULL; ++ return; + + pool = worker->pool; + +- /* this can only happen on the local cpu */ +- if (WARN_ON_ONCE(cpu != raw_smp_processor_id() || pool->cpu != cpu)) +- return NULL; ++ if (WARN_ON_ONCE(worker->sleeping)) ++ return; ++ ++ worker->sleeping = 1; + + /* + * The counterpart of the following dec_and_test, implied mb, + * worklist not empty test sequence is in insert_work(). + * Please read comment there. +- * +- * NOT_RUNNING is clear. This means that we're bound to and +- * running on the local cpu w/ rq lock held and preemption +- * disabled, which in turn means that none else could be +- * manipulating idle_list, so dereferencing idle_list without pool +- * lock is safe. + */ + if (atomic_dec_and_test(&pool->nr_running) && +- !list_empty(&pool->worklist)) +- to_wakeup = first_idle_worker(pool); +- return to_wakeup ? to_wakeup->task : NULL; ++ !list_empty(&pool->worklist)) { ++ sched_lock_idle_list(pool); ++ wake_up_worker(pool); ++ sched_unlock_idle_list(pool); ++ } + } + + /** +@@ -1053,12 +1077,12 @@ + { + if (pwq) { + /* +- * As both pwqs and pools are sched-RCU protected, the ++ * As both pwqs and pools are RCU protected, the + * following lock operations are safe. + */ +- spin_lock_irq(&pwq->pool->lock); ++ local_spin_lock_irq(pendingb_lock, &pwq->pool->lock); + put_pwq(pwq); +- spin_unlock_irq(&pwq->pool->lock); ++ local_spin_unlock_irq(pendingb_lock, &pwq->pool->lock); + } + } + +@@ -1160,7 +1184,7 @@ + struct worker_pool *pool; + struct pool_workqueue *pwq; + +- local_irq_save(*flags); ++ local_lock_irqsave(pendingb_lock, *flags); + + /* try to steal the timer if it exists */ + if (is_dwork) { +@@ -1179,6 +1203,7 @@ + if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) + return 0; + ++ rcu_read_lock(); + /* + * The queueing is in progress, or it is already queued. Try to + * steal it from ->worklist without clearing WORK_STRUCT_PENDING. +@@ -1217,14 +1242,16 @@ + set_work_pool_and_keep_pending(work, pool->id); + + spin_unlock(&pool->lock); ++ rcu_read_unlock(); + return 1; + } + spin_unlock(&pool->lock); + fail: +- local_irq_restore(*flags); ++ rcu_read_unlock(); ++ local_unlock_irqrestore(pendingb_lock, *flags); + if (work_is_canceling(work)) + return -ENOENT; +- cpu_relax(); ++ cpu_chill(); + return -EAGAIN; + } + +@@ -1293,7 +1320,7 @@ + * queued or lose PENDING. Grabbing PENDING and queueing should + * happen with IRQ disabled. + */ +- WARN_ON_ONCE(!irqs_disabled()); ++ WARN_ON_ONCE_NONRT(!irqs_disabled()); + + debug_work_activate(work); + +@@ -1301,6 +1328,8 @@ + if (unlikely(wq->flags & __WQ_DRAINING) && + WARN_ON_ONCE(!is_chained_work(wq))) + return; ++ ++ rcu_read_lock(); + retry: + if (req_cpu == WORK_CPU_UNBOUND) + cpu = raw_smp_processor_id(); +@@ -1357,10 +1386,8 @@ + /* pwq determined, queue */ + trace_workqueue_queue_work(req_cpu, pwq, work); + +- if (WARN_ON(!list_empty(&work->entry))) { +- spin_unlock(&pwq->pool->lock); +- return; +- } ++ if (WARN_ON(!list_empty(&work->entry))) ++ goto out; + + pwq->nr_in_flight[pwq->work_color]++; + work_flags = work_color_to_flags(pwq->work_color); +@@ -1376,7 +1403,9 @@ + + insert_work(pwq, work, worklist, work_flags); + ++out: + spin_unlock(&pwq->pool->lock); ++ rcu_read_unlock(); + } + + /** +@@ -1396,14 +1425,14 @@ + bool ret = false; + unsigned long flags; + +- local_irq_save(flags); ++ local_lock_irqsave(pendingb_lock,flags); + + if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { + __queue_work(cpu, wq, work); + ret = true; + } + +- local_irq_restore(flags); ++ local_unlock_irqrestore(pendingb_lock, flags); + return ret; + } + EXPORT_SYMBOL(queue_work_on); +@@ -1470,14 +1499,14 @@ + unsigned long flags; + + /* read the comment in __queue_work() */ +- local_irq_save(flags); ++ local_lock_irqsave(pendingb_lock, flags); + + if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { + __queue_delayed_work(cpu, wq, dwork, delay); + ret = true; + } + +- local_irq_restore(flags); ++ local_unlock_irqrestore(pendingb_lock, flags); + return ret; + } + EXPORT_SYMBOL(queue_delayed_work_on); +@@ -1512,7 +1541,7 @@ + + if (likely(ret >= 0)) { + __queue_delayed_work(cpu, wq, dwork, delay); +- local_irq_restore(flags); ++ local_unlock_irqrestore(pendingb_lock, flags); + } + + /* -ENOENT from try_to_grab_pending() becomes %true */ +@@ -1545,7 +1574,9 @@ + worker->last_active = jiffies; + + /* idle_list is LIFO */ ++ rt_lock_idle_list(pool); + list_add(&worker->entry, &pool->idle_list); ++ rt_unlock_idle_list(pool); + + if (too_many_workers(pool) && !timer_pending(&pool->idle_timer)) + mod_timer(&pool->idle_timer, jiffies + IDLE_WORKER_TIMEOUT); +@@ -1578,7 +1609,9 @@ + return; + worker_clr_flags(worker, WORKER_IDLE); + pool->nr_idle--; ++ rt_lock_idle_list(pool); + list_del_init(&worker->entry); ++ rt_unlock_idle_list(pool); + } + + static struct worker *alloc_worker(int node) +@@ -1746,7 +1779,9 @@ + pool->nr_workers--; + pool->nr_idle--; + ++ rt_lock_idle_list(pool); + list_del_init(&worker->entry); ++ rt_unlock_idle_list(pool); + worker->flags |= WORKER_DIE; + wake_up_process(worker->task); + } +@@ -2641,14 +2676,14 @@ + + might_sleep(); + +- local_irq_disable(); ++ rcu_read_lock(); + pool = get_work_pool(work); + if (!pool) { +- local_irq_enable(); ++ rcu_read_unlock(); + return false; + } + +- spin_lock(&pool->lock); ++ spin_lock_irq(&pool->lock); + /* see the comment in try_to_grab_pending() with the same code */ + pwq = get_work_pwq(work); + if (pwq) { +@@ -2675,10 +2710,11 @@ + else + lock_map_acquire_read(&pwq->wq->lockdep_map); + lock_map_release(&pwq->wq->lockdep_map); +- ++ rcu_read_unlock(); + return true; + already_gone: + spin_unlock_irq(&pool->lock); ++ rcu_read_unlock(); + return false; + } + +@@ -2765,7 +2801,7 @@ + + /* tell other tasks trying to grab @work to back off */ + mark_work_canceling(work); +- local_irq_restore(flags); ++ local_unlock_irqrestore(pendingb_lock, flags); + + flush_work(work); + clear_work_data(work); +@@ -2820,10 +2856,10 @@ + */ + bool flush_delayed_work(struct delayed_work *dwork) + { +- local_irq_disable(); ++ local_lock_irq(pendingb_lock); + if (del_timer_sync(&dwork->timer)) + __queue_work(dwork->cpu, dwork->wq, &dwork->work); +- local_irq_enable(); ++ local_unlock_irq(pendingb_lock); + return flush_work(&dwork->work); + } + EXPORT_SYMBOL(flush_delayed_work); +@@ -2858,7 +2894,7 @@ + + set_work_pool_and_clear_pending(&dwork->work, + get_work_pool_id(&dwork->work)); +- local_irq_restore(flags); ++ local_unlock_irqrestore(pendingb_lock, flags); + return ret; + } + EXPORT_SYMBOL(cancel_delayed_work); +@@ -3044,7 +3080,8 @@ + const char *delim = ""; + int node, written = 0; + +- rcu_read_lock_sched(); ++ get_online_cpus(); ++ rcu_read_lock(); + for_each_node(node) { + written += scnprintf(buf + written, PAGE_SIZE - written, + "%s%d:%d", delim, node, +@@ -3052,7 +3089,8 @@ + delim = " "; + } + written += scnprintf(buf + written, PAGE_SIZE - written, "\n"); +- rcu_read_unlock_sched(); ++ rcu_read_unlock(); ++ put_online_cpus(); + + return written; + } +@@ -3420,7 +3458,7 @@ + * put_unbound_pool - put a worker_pool + * @pool: worker_pool to put + * +- * Put @pool. If its refcnt reaches zero, it gets destroyed in sched-RCU ++ * Put @pool. If its refcnt reaches zero, it gets destroyed in RCU + * safe manner. get_unbound_pool() calls this function on its failure path + * and this function should be able to release pools which went through, + * successfully or not, init_worker_pool(). +@@ -3474,8 +3512,8 @@ + del_timer_sync(&pool->idle_timer); + del_timer_sync(&pool->mayday_timer); + +- /* sched-RCU protected to allow dereferences from get_work_pool() */ +- call_rcu_sched(&pool->rcu, rcu_free_pool); ++ /* RCU protected to allow dereferences from get_work_pool() */ ++ call_rcu(&pool->rcu, rcu_free_pool); + } + + /** +@@ -3580,7 +3618,7 @@ + put_unbound_pool(pool); + mutex_unlock(&wq_pool_mutex); + +- call_rcu_sched(&pwq->rcu, rcu_free_pwq); ++ call_rcu(&pwq->rcu, rcu_free_pwq); + + /* + * If we're the last pwq going away, @wq is already dead and no one +@@ -4292,7 +4330,8 @@ + struct pool_workqueue *pwq; + bool ret; + +- rcu_read_lock_sched(); ++ rcu_read_lock(); ++ preempt_disable(); + + if (cpu == WORK_CPU_UNBOUND) + cpu = smp_processor_id(); +@@ -4303,7 +4342,8 @@ + pwq = unbound_pwq_by_node(wq, cpu_to_node(cpu)); + + ret = !list_empty(&pwq->delayed_works); +- rcu_read_unlock_sched(); ++ preempt_enable(); ++ rcu_read_unlock(); + + return ret; + } +@@ -4329,16 +4369,15 @@ + if (work_pending(work)) + ret |= WORK_BUSY_PENDING; + +- local_irq_save(flags); ++ rcu_read_lock(); + pool = get_work_pool(work); + if (pool) { +- spin_lock(&pool->lock); ++ spin_lock_irqsave(&pool->lock, flags); + if (find_worker_executing_work(pool, work)) + ret |= WORK_BUSY_RUNNING; +- spin_unlock(&pool->lock); ++ spin_unlock_irqrestore(&pool->lock, flags); + } +- local_irq_restore(flags); +- ++ rcu_read_unlock(); + return ret; + } + EXPORT_SYMBOL_GPL(work_busy); +@@ -4767,16 +4806,16 @@ + * nr_active is monotonically decreasing. It's safe + * to peek without lock. + */ +- rcu_read_lock_sched(); ++ rcu_read_lock(); + for_each_pwq(pwq, wq) { + WARN_ON_ONCE(pwq->nr_active < 0); + if (pwq->nr_active) { + busy = true; +- rcu_read_unlock_sched(); ++ rcu_read_unlock(); + goto out_unlock; + } + } +- rcu_read_unlock_sched(); ++ rcu_read_unlock(); + } + out_unlock: + mutex_unlock(&wq_pool_mutex); +diff -Nur linux-3.18.14.orig/kernel/workqueue_internal.h linux-3.18.14-rt/kernel/workqueue_internal.h +--- linux-3.18.14.orig/kernel/workqueue_internal.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/kernel/workqueue_internal.h 2015-05-31 15:32:49.069635361 -0500 +@@ -43,6 +43,7 @@ + unsigned long last_active; /* L: last active timestamp */ + unsigned int flags; /* X: flags */ + int id; /* I: worker id */ ++ int sleeping; /* None */ + + /* + * Opaque string set with work_set_desc(). Printed out with task +@@ -68,7 +69,7 @@ + * Scheduler hooks for concurrency managed workqueue. Only to be used from + * sched/core.c and workqueue.c. + */ +-void wq_worker_waking_up(struct task_struct *task, int cpu); +-struct task_struct *wq_worker_sleeping(struct task_struct *task, int cpu); ++void wq_worker_running(struct task_struct *task); ++void wq_worker_sleeping(struct task_struct *task); + + #endif /* _KERNEL_WORKQUEUE_INTERNAL_H */ +diff -Nur linux-3.18.14.orig/lib/debugobjects.c linux-3.18.14-rt/lib/debugobjects.c +--- linux-3.18.14.orig/lib/debugobjects.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/lib/debugobjects.c 2015-05-31 15:32:49.113635361 -0500 +@@ -309,7 +309,10 @@ + struct debug_obj *obj; + unsigned long flags; + +- fill_pool(); ++#ifdef CONFIG_PREEMPT_RT_FULL ++ if (preempt_count() == 0 && !irqs_disabled()) ++#endif ++ fill_pool(); + + db = get_bucket((unsigned long) addr); + +diff -Nur linux-3.18.14.orig/lib/idr.c linux-3.18.14-rt/lib/idr.c +--- linux-3.18.14.orig/lib/idr.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/lib/idr.c 2015-05-31 15:32:49.141635361 -0500 +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + #define MAX_IDR_SHIFT (sizeof(int) * 8 - 1) + #define MAX_IDR_BIT (1U << MAX_IDR_SHIFT) +@@ -367,6 +368,35 @@ + idr_mark_full(pa, id); + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++static DEFINE_LOCAL_IRQ_LOCK(idr_lock); ++ ++static inline void idr_preload_lock(void) ++{ ++ local_lock(idr_lock); ++} ++ ++static inline void idr_preload_unlock(void) ++{ ++ local_unlock(idr_lock); ++} ++ ++void idr_preload_end(void) ++{ ++ idr_preload_unlock(); ++} ++EXPORT_SYMBOL(idr_preload_end); ++#else ++static inline void idr_preload_lock(void) ++{ ++ preempt_disable(); ++} ++ ++static inline void idr_preload_unlock(void) ++{ ++ preempt_enable(); ++} ++#endif + + /** + * idr_preload - preload for idr_alloc() +@@ -402,7 +432,7 @@ + WARN_ON_ONCE(in_interrupt()); + might_sleep_if(gfp_mask & __GFP_WAIT); + +- preempt_disable(); ++ idr_preload_lock(); + + /* + * idr_alloc() is likely to succeed w/o full idr_layer buffer and +@@ -414,9 +444,9 @@ + while (__this_cpu_read(idr_preload_cnt) < MAX_IDR_FREE) { + struct idr_layer *new; + +- preempt_enable(); ++ idr_preload_unlock(); + new = kmem_cache_zalloc(idr_layer_cache, gfp_mask); +- preempt_disable(); ++ idr_preload_lock(); + if (!new) + break; + +diff -Nur linux-3.18.14.orig/lib/Kconfig linux-3.18.14-rt/lib/Kconfig +--- linux-3.18.14.orig/lib/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/lib/Kconfig 2015-05-31 15:32:49.085635361 -0500 +@@ -383,6 +383,7 @@ + + config CPUMASK_OFFSTACK + bool "Force CPU masks off stack" if DEBUG_PER_CPU_MAPS ++ depends on !PREEMPT_RT_FULL + help + Use dynamic allocation for cpumask_var_t, instead of putting + them on the stack. This is a bit more expensive, but avoids +diff -Nur linux-3.18.14.orig/lib/Kconfig.debug linux-3.18.14-rt/lib/Kconfig.debug +--- linux-3.18.14.orig/lib/Kconfig.debug 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/lib/Kconfig.debug 2015-05-31 15:32:49.097635361 -0500 +@@ -639,7 +639,7 @@ + + config DEBUG_SHIRQ + bool "Debug shared IRQ handlers" +- depends on DEBUG_KERNEL ++ depends on DEBUG_KERNEL && !PREEMPT_RT_BASE + help + Enable this to generate a spurious interrupt as soon as a shared + interrupt handler is registered, and just before one is deregistered. +diff -Nur linux-3.18.14.orig/lib/locking-selftest.c linux-3.18.14-rt/lib/locking-selftest.c +--- linux-3.18.14.orig/lib/locking-selftest.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/lib/locking-selftest.c 2015-05-31 15:32:49.141635361 -0500 +@@ -590,6 +590,8 @@ + #include "locking-selftest-spin-hardirq.h" + GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_spin) + ++#ifndef CONFIG_PREEMPT_RT_FULL ++ + #include "locking-selftest-rlock-hardirq.h" + GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_rlock) + +@@ -605,9 +607,12 @@ + #include "locking-selftest-wlock-softirq.h" + GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_soft_wlock) + ++#endif ++ + #undef E1 + #undef E2 + ++#ifndef CONFIG_PREEMPT_RT_FULL + /* + * Enabling hardirqs with a softirq-safe lock held: + */ +@@ -640,6 +645,8 @@ + #undef E1 + #undef E2 + ++#endif ++ + /* + * Enabling irqs with an irq-safe lock held: + */ +@@ -663,6 +670,8 @@ + #include "locking-selftest-spin-hardirq.h" + GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_spin) + ++#ifndef CONFIG_PREEMPT_RT_FULL ++ + #include "locking-selftest-rlock-hardirq.h" + GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_rlock) + +@@ -678,6 +687,8 @@ + #include "locking-selftest-wlock-softirq.h" + GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_soft_wlock) + ++#endif ++ + #undef E1 + #undef E2 + +@@ -709,6 +720,8 @@ + #include "locking-selftest-spin-hardirq.h" + GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_spin) + ++#ifndef CONFIG_PREEMPT_RT_FULL ++ + #include "locking-selftest-rlock-hardirq.h" + GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_rlock) + +@@ -724,6 +737,8 @@ + #include "locking-selftest-wlock-softirq.h" + GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_soft_wlock) + ++#endif ++ + #undef E1 + #undef E2 + #undef E3 +@@ -757,6 +772,8 @@ + #include "locking-selftest-spin-hardirq.h" + GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_spin) + ++#ifndef CONFIG_PREEMPT_RT_FULL ++ + #include "locking-selftest-rlock-hardirq.h" + GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_rlock) + +@@ -772,10 +789,14 @@ + #include "locking-selftest-wlock-softirq.h" + GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_soft_wlock) + ++#endif ++ + #undef E1 + #undef E2 + #undef E3 + ++#ifndef CONFIG_PREEMPT_RT_FULL ++ + /* + * read-lock / write-lock irq inversion. + * +@@ -838,6 +859,10 @@ + #undef E2 + #undef E3 + ++#endif ++ ++#ifndef CONFIG_PREEMPT_RT_FULL ++ + /* + * read-lock / write-lock recursion that is actually safe. + */ +@@ -876,6 +901,8 @@ + #undef E2 + #undef E3 + ++#endif ++ + /* + * read-lock / write-lock recursion that is unsafe. + */ +@@ -1858,6 +1885,7 @@ + + printk(" --------------------------------------------------------------------------\n"); + ++#ifndef CONFIG_PREEMPT_RT_FULL + /* + * irq-context testcases: + */ +@@ -1870,6 +1898,28 @@ + + DO_TESTCASE_6x2("irq read-recursion", irq_read_recursion); + // DO_TESTCASE_6x2B("irq read-recursion #2", irq_read_recursion2); ++#else ++ /* On -rt, we only do hardirq context test for raw spinlock */ ++ DO_TESTCASE_1B("hard-irqs-on + irq-safe-A", irqsafe1_hard_spin, 12); ++ DO_TESTCASE_1B("hard-irqs-on + irq-safe-A", irqsafe1_hard_spin, 21); ++ ++ DO_TESTCASE_1B("hard-safe-A + irqs-on", irqsafe2B_hard_spin, 12); ++ DO_TESTCASE_1B("hard-safe-A + irqs-on", irqsafe2B_hard_spin, 21); ++ ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 123); ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 132); ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 213); ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 231); ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 312); ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 321); ++ ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 123); ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 132); ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 213); ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 231); ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 312); ++ DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 321); ++#endif + + ww_tests(); + +diff -Nur linux-3.18.14.orig/lib/percpu_ida.c linux-3.18.14-rt/lib/percpu_ida.c +--- linux-3.18.14.orig/lib/percpu_ida.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/lib/percpu_ida.c 2015-05-31 15:32:49.161635360 -0500 +@@ -29,6 +29,9 @@ + #include + #include + #include ++#include ++ ++static DEFINE_LOCAL_IRQ_LOCK(irq_off_lock); + + struct percpu_ida_cpu { + /* +@@ -151,13 +154,13 @@ + unsigned long flags; + int tag; + +- local_irq_save(flags); ++ local_lock_irqsave(irq_off_lock, flags); + tags = this_cpu_ptr(pool->tag_cpu); + + /* Fastpath */ + tag = alloc_local_tag(tags); + if (likely(tag >= 0)) { +- local_irq_restore(flags); ++ local_unlock_irqrestore(irq_off_lock, flags); + return tag; + } + +@@ -176,6 +179,7 @@ + + if (!tags->nr_free) + alloc_global_tags(pool, tags); ++ + if (!tags->nr_free) + steal_tags(pool, tags); + +@@ -187,7 +191,7 @@ + } + + spin_unlock(&pool->lock); +- local_irq_restore(flags); ++ local_unlock_irqrestore(irq_off_lock, flags); + + if (tag >= 0 || state == TASK_RUNNING) + break; +@@ -199,7 +203,7 @@ + + schedule(); + +- local_irq_save(flags); ++ local_lock_irqsave(irq_off_lock, flags); + tags = this_cpu_ptr(pool->tag_cpu); + } + if (state != TASK_RUNNING) +@@ -224,7 +228,7 @@ + + BUG_ON(tag >= pool->nr_tags); + +- local_irq_save(flags); ++ local_lock_irqsave(irq_off_lock, flags); + tags = this_cpu_ptr(pool->tag_cpu); + + spin_lock(&tags->lock); +@@ -256,7 +260,7 @@ + spin_unlock(&pool->lock); + } + +- local_irq_restore(flags); ++ local_unlock_irqrestore(irq_off_lock, flags); + } + EXPORT_SYMBOL_GPL(percpu_ida_free); + +@@ -348,7 +352,7 @@ + struct percpu_ida_cpu *remote; + unsigned cpu, i, err = 0; + +- local_irq_save(flags); ++ local_lock_irqsave(irq_off_lock, flags); + for_each_possible_cpu(cpu) { + remote = per_cpu_ptr(pool->tag_cpu, cpu); + spin_lock(&remote->lock); +@@ -370,7 +374,7 @@ + } + spin_unlock(&pool->lock); + out: +- local_irq_restore(flags); ++ local_unlock_irqrestore(irq_off_lock, flags); + return err; + } + EXPORT_SYMBOL_GPL(percpu_ida_for_each_free); +diff -Nur linux-3.18.14.orig/lib/radix-tree.c linux-3.18.14-rt/lib/radix-tree.c +--- linux-3.18.14.orig/lib/radix-tree.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/lib/radix-tree.c 2015-05-31 15:32:49.161635360 -0500 +@@ -195,12 +195,13 @@ + * succeed in getting a node here (and never reach + * kmem_cache_alloc) + */ +- rtp = this_cpu_ptr(&radix_tree_preloads); ++ rtp = &get_cpu_var(radix_tree_preloads); + if (rtp->nr) { + ret = rtp->nodes[rtp->nr - 1]; + rtp->nodes[rtp->nr - 1] = NULL; + rtp->nr--; + } ++ put_cpu_var(radix_tree_preloads); + /* + * Update the allocation stack trace as this is more useful + * for debugging. +@@ -240,6 +241,7 @@ + call_rcu(&node->rcu_head, radix_tree_node_rcu_free); + } + ++#ifndef CONFIG_PREEMPT_RT_FULL + /* + * Load up this CPU's radix_tree_node buffer with sufficient objects to + * ensure that the addition of a single element in the tree cannot fail. On +@@ -305,6 +307,7 @@ + return 0; + } + EXPORT_SYMBOL(radix_tree_maybe_preload); ++#endif + + /* + * Return the maximum key which can be store into a +diff -Nur linux-3.18.14.orig/lib/scatterlist.c linux-3.18.14-rt/lib/scatterlist.c +--- linux-3.18.14.orig/lib/scatterlist.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/lib/scatterlist.c 2015-05-31 15:32:49.161635360 -0500 +@@ -592,7 +592,7 @@ + flush_kernel_dcache_page(miter->page); + + if (miter->__flags & SG_MITER_ATOMIC) { +- WARN_ON_ONCE(preemptible()); ++ WARN_ON_ONCE(!pagefault_disabled()); + kunmap_atomic(miter->addr); + } else + kunmap(miter->page); +@@ -637,7 +637,7 @@ + if (!sg_miter_skip(&miter, skip)) + return false; + +- local_irq_save(flags); ++ local_irq_save_nort(flags); + + while (sg_miter_next(&miter) && offset < buflen) { + unsigned int len; +@@ -654,7 +654,7 @@ + + sg_miter_stop(&miter); + +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + return offset; + } + +diff -Nur linux-3.18.14.orig/lib/smp_processor_id.c linux-3.18.14-rt/lib/smp_processor_id.c +--- linux-3.18.14.orig/lib/smp_processor_id.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/lib/smp_processor_id.c 2015-05-31 15:32:49.161635360 -0500 +@@ -39,8 +39,9 @@ + if (!printk_ratelimit()) + goto out_enable; + +- printk(KERN_ERR "BUG: using %s%s() in preemptible [%08x] code: %s/%d\n", +- what1, what2, preempt_count() - 1, current->comm, current->pid); ++ printk(KERN_ERR "BUG: using %s%s() in preemptible [%08x %08x] code: %s/%d\n", ++ what1, what2, preempt_count() - 1, __migrate_disabled(current), ++ current->comm, current->pid); + + print_symbol("caller is %s\n", (long)__builtin_return_address(0)); + dump_stack(); +diff -Nur linux-3.18.14.orig/mm/filemap.c linux-3.18.14-rt/mm/filemap.c +--- linux-3.18.14.orig/mm/filemap.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/filemap.c 2015-05-31 15:32:49.181635360 -0500 +@@ -168,7 +168,9 @@ + if (!workingset_node_pages(node) && + list_empty(&node->private_list)) { + node->private_data = mapping; +- list_lru_add(&workingset_shadow_nodes, &node->private_list); ++ local_lock(workingset_shadow_lock); ++ list_lru_add(&__workingset_shadow_nodes, &node->private_list); ++ local_unlock(workingset_shadow_lock); + } + } + +@@ -535,9 +537,12 @@ + * node->private_list is protected by + * mapping->tree_lock. + */ +- if (!list_empty(&node->private_list)) +- list_lru_del(&workingset_shadow_nodes, ++ if (!list_empty(&node->private_list)) { ++ local_lock(workingset_shadow_lock); ++ list_lru_del(&__workingset_shadow_nodes, + &node->private_list); ++ local_unlock(workingset_shadow_lock); ++ } + } + return 0; + } +diff -Nur linux-3.18.14.orig/mm/highmem.c linux-3.18.14-rt/mm/highmem.c +--- linux-3.18.14.orig/mm/highmem.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/highmem.c 2015-05-31 15:32:49.201635360 -0500 +@@ -29,10 +29,11 @@ + #include + #include + +- ++#ifndef CONFIG_PREEMPT_RT_FULL + #if defined(CONFIG_HIGHMEM) || defined(CONFIG_X86_32) + DEFINE_PER_CPU(int, __kmap_atomic_idx); + #endif ++#endif + + /* + * Virtual_count is not a pure "count". +@@ -107,8 +108,9 @@ + unsigned long totalhigh_pages __read_mostly; + EXPORT_SYMBOL(totalhigh_pages); + +- ++#ifndef CONFIG_PREEMPT_RT_FULL + EXPORT_PER_CPU_SYMBOL(__kmap_atomic_idx); ++#endif + + unsigned int nr_free_highpages (void) + { +diff -Nur linux-3.18.14.orig/mm/Kconfig linux-3.18.14-rt/mm/Kconfig +--- linux-3.18.14.orig/mm/Kconfig 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/Kconfig 2015-05-31 15:32:49.177635360 -0500 +@@ -408,7 +408,7 @@ + + config TRANSPARENT_HUGEPAGE + bool "Transparent Hugepage Support" +- depends on HAVE_ARCH_TRANSPARENT_HUGEPAGE ++ depends on HAVE_ARCH_TRANSPARENT_HUGEPAGE && !PREEMPT_RT_FULL + select COMPACTION + help + Transparent Hugepages allows the kernel to use huge pages and +diff -Nur linux-3.18.14.orig/mm/memcontrol.c linux-3.18.14-rt/mm/memcontrol.c +--- linux-3.18.14.orig/mm/memcontrol.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/memcontrol.c 2015-05-31 15:32:49.213635360 -0500 +@@ -60,6 +60,8 @@ + #include + #include + #include ++#include ++ + #include "slab.h" + + #include +@@ -87,6 +89,7 @@ + #define do_swap_account 0 + #endif + ++static DEFINE_LOCAL_IRQ_LOCK(event_lock); + + static const char * const mem_cgroup_stat_names[] = { + "cache", +@@ -2376,14 +2379,17 @@ + */ + static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages) + { +- struct memcg_stock_pcp *stock = &get_cpu_var(memcg_stock); ++ struct memcg_stock_pcp *stock; ++ int cpu = get_cpu_light(); ++ ++ stock = &per_cpu(memcg_stock, cpu); + + if (stock->cached != memcg) { /* reset if necessary */ + drain_stock(stock); + stock->cached = memcg; + } + stock->nr_pages += nr_pages; +- put_cpu_var(memcg_stock); ++ put_cpu_light(); + } + + /* +@@ -2397,7 +2403,7 @@ + + /* Notify other cpus that system-wide "drain" is running */ + get_online_cpus(); +- curcpu = get_cpu(); ++ curcpu = get_cpu_light(); + for_each_online_cpu(cpu) { + struct memcg_stock_pcp *stock = &per_cpu(memcg_stock, cpu); + struct mem_cgroup *memcg; +@@ -2414,7 +2420,7 @@ + schedule_work_on(cpu, &stock->work); + } + } +- put_cpu(); ++ put_cpu_light(); + + if (!sync) + goto out; +@@ -3419,12 +3425,12 @@ + move_unlock_mem_cgroup(from, &flags); + ret = 0; + +- local_irq_disable(); ++ local_lock_irq(event_lock); + mem_cgroup_charge_statistics(to, page, nr_pages); + memcg_check_events(to, page); + mem_cgroup_charge_statistics(from, page, -nr_pages); + memcg_check_events(from, page); +- local_irq_enable(); ++ local_unlock_irq(event_lock); + out_unlock: + unlock_page(page); + out: +@@ -6406,10 +6412,10 @@ + VM_BUG_ON_PAGE(!PageTransHuge(page), page); + } + +- local_irq_disable(); ++ local_lock_irq(event_lock); + mem_cgroup_charge_statistics(memcg, page, nr_pages); + memcg_check_events(memcg, page); +- local_irq_enable(); ++ local_unlock_irq(event_lock); + + if (do_swap_account && PageSwapCache(page)) { + swp_entry_t entry = { .val = page_private(page) }; +@@ -6468,14 +6474,14 @@ + memcg_oom_recover(memcg); + } + +- local_irq_save(flags); ++ local_lock_irqsave(event_lock, flags); + __this_cpu_sub(memcg->stat->count[MEM_CGROUP_STAT_RSS], nr_anon); + __this_cpu_sub(memcg->stat->count[MEM_CGROUP_STAT_CACHE], nr_file); + __this_cpu_sub(memcg->stat->count[MEM_CGROUP_STAT_RSS_HUGE], nr_huge); + __this_cpu_add(memcg->stat->events[MEM_CGROUP_EVENTS_PGPGOUT], pgpgout); + __this_cpu_add(memcg->stat->nr_page_events, nr_anon + nr_file); + memcg_check_events(memcg, dummy_page); +- local_irq_restore(flags); ++ local_unlock_irqrestore(event_lock, flags); + } + + static void uncharge_list(struct list_head *page_list) +diff -Nur linux-3.18.14.orig/mm/memory.c linux-3.18.14-rt/mm/memory.c +--- linux-3.18.14.orig/mm/memory.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/memory.c 2015-05-31 15:32:49.229635360 -0500 +@@ -3244,6 +3244,32 @@ + return 0; + } + ++#ifdef CONFIG_PREEMPT_RT_FULL ++void pagefault_disable(void) ++{ ++ migrate_disable(); ++ current->pagefault_disabled++; ++ /* ++ * make sure to have issued the store before a pagefault ++ * can hit. ++ */ ++ barrier(); ++} ++EXPORT_SYMBOL(pagefault_disable); ++ ++void pagefault_enable(void) ++{ ++ /* ++ * make sure to issue those last loads/stores before enabling ++ * the pagefault handler again. ++ */ ++ barrier(); ++ current->pagefault_disabled--; ++ migrate_enable(); ++} ++EXPORT_SYMBOL(pagefault_enable); ++#endif ++ + /* + * By the time we get here, we already hold the mm semaphore + * +diff -Nur linux-3.18.14.orig/mm/mmu_context.c linux-3.18.14-rt/mm/mmu_context.c +--- linux-3.18.14.orig/mm/mmu_context.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/mmu_context.c 2015-05-31 15:32:49.249635360 -0500 +@@ -23,6 +23,7 @@ + struct task_struct *tsk = current; + + task_lock(tsk); ++ preempt_disable_rt(); + active_mm = tsk->active_mm; + if (active_mm != mm) { + atomic_inc(&mm->mm_count); +@@ -30,6 +31,7 @@ + } + tsk->mm = mm; + switch_mm(active_mm, mm, tsk); ++ preempt_enable_rt(); + task_unlock(tsk); + #ifdef finish_arch_post_lock_switch + finish_arch_post_lock_switch(); +diff -Nur linux-3.18.14.orig/mm/page_alloc.c linux-3.18.14-rt/mm/page_alloc.c +--- linux-3.18.14.orig/mm/page_alloc.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/page_alloc.c 2015-05-31 15:32:49.253635359 -0500 +@@ -59,6 +59,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -230,6 +231,18 @@ + EXPORT_SYMBOL(nr_online_nodes); + #endif + ++static DEFINE_LOCAL_IRQ_LOCK(pa_lock); ++ ++#ifdef CONFIG_PREEMPT_RT_BASE ++# define cpu_lock_irqsave(cpu, flags) \ ++ local_lock_irqsave_on(pa_lock, flags, cpu) ++# define cpu_unlock_irqrestore(cpu, flags) \ ++ local_unlock_irqrestore_on(pa_lock, flags, cpu) ++#else ++# define cpu_lock_irqsave(cpu, flags) local_irq_save(flags) ++# define cpu_unlock_irqrestore(cpu, flags) local_irq_restore(flags) ++#endif ++ + int page_group_by_mobility_disabled __read_mostly; + + void set_pageblock_migratetype(struct page *page, int migratetype) +@@ -654,7 +667,7 @@ + } + + /* +- * Frees a number of pages from the PCP lists ++ * Frees a number of pages which have been collected from the pcp lists. + * Assumes all pages on list are in same zone, and of same order. + * count is the number of pages to free. + * +@@ -665,18 +678,51 @@ + * pinned" detection logic. + */ + static void free_pcppages_bulk(struct zone *zone, int count, +- struct per_cpu_pages *pcp) ++ struct list_head *list) + { +- int migratetype = 0; +- int batch_free = 0; + int to_free = count; + unsigned long nr_scanned; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&zone->lock, flags); + +- spin_lock(&zone->lock); + nr_scanned = zone_page_state(zone, NR_PAGES_SCANNED); + if (nr_scanned) + __mod_zone_page_state(zone, NR_PAGES_SCANNED, -nr_scanned); + ++ while (!list_empty(list)) { ++ struct page *page = list_first_entry(list, struct page, lru); ++ int mt; /* migratetype of the to-be-freed page */ ++ ++ /* must delete as __free_one_page list manipulates */ ++ list_del(&page->lru); ++ ++ mt = get_freepage_migratetype(page); ++ if (unlikely(has_isolate_pageblock(zone))) ++ mt = get_pageblock_migratetype(page); ++ ++ /* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */ ++ __free_one_page(page, page_to_pfn(page), zone, 0, mt); ++ trace_mm_page_pcpu_drain(page, 0, mt); ++ to_free--; ++ } ++ WARN_ON(to_free != 0); ++ spin_unlock_irqrestore(&zone->lock, flags); ++} ++ ++/* ++ * Moves a number of pages from the PCP lists to free list which ++ * is freed outside of the locked region. ++ * ++ * Assumes all pages on list are in same zone, and of same order. ++ * count is the number of pages to free. ++ */ ++static void isolate_pcp_pages(int to_free, struct per_cpu_pages *src, ++ struct list_head *dst) ++{ ++ int migratetype = 0; ++ int batch_free = 0; ++ + while (to_free) { + struct page *page; + struct list_head *list; +@@ -692,7 +738,7 @@ + batch_free++; + if (++migratetype == MIGRATE_PCPTYPES) + migratetype = 0; +- list = &pcp->lists[migratetype]; ++ list = &src->lists[migratetype]; + } while (list_empty(list)); + + /* This is the only non-empty list. Free them all. */ +@@ -700,21 +746,11 @@ + batch_free = to_free; + + do { +- int mt; /* migratetype of the to-be-freed page */ +- +- page = list_entry(list->prev, struct page, lru); +- /* must delete as __free_one_page list manipulates */ ++ page = list_last_entry(list, struct page, lru); + list_del(&page->lru); +- mt = get_freepage_migratetype(page); +- if (unlikely(has_isolate_pageblock(zone))) +- mt = get_pageblock_migratetype(page); +- +- /* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */ +- __free_one_page(page, page_to_pfn(page), zone, 0, mt); +- trace_mm_page_pcpu_drain(page, 0, mt); ++ list_add(&page->lru, dst); + } while (--to_free && --batch_free && !list_empty(list)); + } +- spin_unlock(&zone->lock); + } + + static void free_one_page(struct zone *zone, +@@ -723,7 +759,9 @@ + int migratetype) + { + unsigned long nr_scanned; +- spin_lock(&zone->lock); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&zone->lock, flags); + nr_scanned = zone_page_state(zone, NR_PAGES_SCANNED); + if (nr_scanned) + __mod_zone_page_state(zone, NR_PAGES_SCANNED, -nr_scanned); +@@ -733,7 +771,7 @@ + migratetype = get_pfnblock_migratetype(page, pfn); + } + __free_one_page(page, pfn, zone, order, migratetype); +- spin_unlock(&zone->lock); ++ spin_unlock_irqrestore(&zone->lock, flags); + } + + static bool free_pages_prepare(struct page *page, unsigned int order) +@@ -773,11 +811,11 @@ + return; + + migratetype = get_pfnblock_migratetype(page, pfn); +- local_irq_save(flags); ++ local_lock_irqsave(pa_lock, flags); + __count_vm_events(PGFREE, 1 << order); + set_freepage_migratetype(page, migratetype); + free_one_page(page_zone(page), page, pfn, order, migratetype); +- local_irq_restore(flags); ++ local_unlock_irqrestore(pa_lock, flags); + } + + void __init __free_pages_bootmem(struct page *page, unsigned int order) +@@ -1251,16 +1289,18 @@ + void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp) + { + unsigned long flags; ++ LIST_HEAD(dst); + int to_drain, batch; + +- local_irq_save(flags); ++ local_lock_irqsave(pa_lock, flags); + batch = ACCESS_ONCE(pcp->batch); + to_drain = min(pcp->count, batch); + if (to_drain > 0) { +- free_pcppages_bulk(zone, to_drain, pcp); ++ isolate_pcp_pages(to_drain, pcp, &dst); + pcp->count -= to_drain; + } +- local_irq_restore(flags); ++ local_unlock_irqrestore(pa_lock, flags); ++ free_pcppages_bulk(zone, to_drain, &dst); + } + #endif + +@@ -1279,16 +1319,21 @@ + for_each_populated_zone(zone) { + struct per_cpu_pageset *pset; + struct per_cpu_pages *pcp; ++ LIST_HEAD(dst); ++ int count; + +- local_irq_save(flags); ++ cpu_lock_irqsave(cpu, flags); + pset = per_cpu_ptr(zone->pageset, cpu); + + pcp = &pset->pcp; +- if (pcp->count) { +- free_pcppages_bulk(zone, pcp->count, pcp); ++ count = pcp->count; ++ if (count) { ++ isolate_pcp_pages(count, pcp, &dst); + pcp->count = 0; + } +- local_irq_restore(flags); ++ cpu_unlock_irqrestore(cpu, flags); ++ if (count) ++ free_pcppages_bulk(zone, count, &dst); + } + } + +@@ -1341,7 +1386,12 @@ + else + cpumask_clear_cpu(cpu, &cpus_with_pcps); + } ++#ifndef CONFIG_PREEMPT_RT_BASE + on_each_cpu_mask(&cpus_with_pcps, drain_local_pages, NULL, 1); ++#else ++ for_each_cpu(cpu, &cpus_with_pcps) ++ drain_pages(cpu); ++#endif + } + + #ifdef CONFIG_HIBERNATION +@@ -1397,7 +1447,7 @@ + + migratetype = get_pfnblock_migratetype(page, pfn); + set_freepage_migratetype(page, migratetype); +- local_irq_save(flags); ++ local_lock_irqsave(pa_lock, flags); + __count_vm_event(PGFREE); + + /* +@@ -1423,12 +1473,17 @@ + pcp->count++; + if (pcp->count >= pcp->high) { + unsigned long batch = ACCESS_ONCE(pcp->batch); +- free_pcppages_bulk(zone, batch, pcp); ++ LIST_HEAD(dst); ++ ++ isolate_pcp_pages(batch, pcp, &dst); + pcp->count -= batch; ++ local_unlock_irqrestore(pa_lock, flags); ++ free_pcppages_bulk(zone, batch, &dst); ++ return; + } + + out: +- local_irq_restore(flags); ++ local_unlock_irqrestore(pa_lock, flags); + } + + /* +@@ -1558,7 +1613,7 @@ + struct per_cpu_pages *pcp; + struct list_head *list; + +- local_irq_save(flags); ++ local_lock_irqsave(pa_lock, flags); + pcp = &this_cpu_ptr(zone->pageset)->pcp; + list = &pcp->lists[migratetype]; + if (list_empty(list)) { +@@ -1590,13 +1645,15 @@ + */ + WARN_ON_ONCE(order > 1); + } +- spin_lock_irqsave(&zone->lock, flags); ++ local_spin_lock_irqsave(pa_lock, &zone->lock, flags); + page = __rmqueue(zone, order, migratetype); +- spin_unlock(&zone->lock); +- if (!page) ++ if (!page) { ++ spin_unlock(&zone->lock); + goto failed; ++ } + __mod_zone_freepage_state(zone, -(1 << order), + get_freepage_migratetype(page)); ++ spin_unlock(&zone->lock); + } + + __mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order)); +@@ -1606,7 +1663,7 @@ + + __count_zone_vm_events(PGALLOC, zone, 1 << order); + zone_statistics(preferred_zone, zone, gfp_flags); +- local_irq_restore(flags); ++ local_unlock_irqrestore(pa_lock, flags); + + VM_BUG_ON_PAGE(bad_range(zone, page), page); + if (prep_new_page(page, order, gfp_flags)) +@@ -1614,7 +1671,7 @@ + return page; + + failed: +- local_irq_restore(flags); ++ local_unlock_irqrestore(pa_lock, flags); + return NULL; + } + +@@ -2325,8 +2382,8 @@ + count_vm_event(COMPACTSTALL); + + /* Page migration frees to the PCP lists but we want merging */ +- drain_pages(get_cpu()); +- put_cpu(); ++ drain_pages(get_cpu_light()); ++ put_cpu_light(); + + page = get_page_from_freelist(gfp_mask, nodemask, + order, zonelist, high_zoneidx, +@@ -5565,6 +5622,7 @@ + void __init page_alloc_init(void) + { + hotcpu_notifier(page_alloc_cpu_notify, 0); ++ local_irq_lock_init(pa_lock); + } + + /* +@@ -6459,7 +6517,7 @@ + struct per_cpu_pageset *pset; + + /* avoid races with drain_pages() */ +- local_irq_save(flags); ++ local_lock_irqsave(pa_lock, flags); + if (zone->pageset != &boot_pageset) { + for_each_online_cpu(cpu) { + pset = per_cpu_ptr(zone->pageset, cpu); +@@ -6468,7 +6526,7 @@ + free_percpu(zone->pageset); + zone->pageset = &boot_pageset; + } +- local_irq_restore(flags); ++ local_unlock_irqrestore(pa_lock, flags); + } + + #ifdef CONFIG_MEMORY_HOTREMOVE +diff -Nur linux-3.18.14.orig/mm/slab.h linux-3.18.14-rt/mm/slab.h +--- linux-3.18.14.orig/mm/slab.h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/slab.h 2015-05-31 15:32:49.257635359 -0500 +@@ -315,7 +315,11 @@ + * The slab lists for all objects. + */ + struct kmem_cache_node { ++#ifdef CONFIG_SLUB ++ raw_spinlock_t list_lock; ++#else + spinlock_t list_lock; ++#endif + + #ifdef CONFIG_SLAB + struct list_head slabs_partial; /* partial list first, better asm code */ +diff -Nur linux-3.18.14.orig/mm/slub.c linux-3.18.14-rt/mm/slub.c +--- linux-3.18.14.orig/mm/slub.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/slub.c 2015-05-31 15:32:49.257635359 -0500 +@@ -1044,7 +1044,7 @@ + { + struct kmem_cache_node *n = get_node(s, page_to_nid(page)); + +- spin_lock_irqsave(&n->list_lock, *flags); ++ raw_spin_lock_irqsave(&n->list_lock, *flags); + slab_lock(page); + + if (!check_slab(s, page)) +@@ -1091,7 +1091,7 @@ + + fail: + slab_unlock(page); +- spin_unlock_irqrestore(&n->list_lock, *flags); ++ raw_spin_unlock_irqrestore(&n->list_lock, *flags); + slab_fix(s, "Object at 0x%p not freed", object); + return NULL; + } +@@ -1219,6 +1219,12 @@ + + #endif /* CONFIG_SLUB_DEBUG */ + ++struct slub_free_list { ++ raw_spinlock_t lock; ++ struct list_head list; ++}; ++static DEFINE_PER_CPU(struct slub_free_list, slub_free_list); ++ + /* + * Hooks for other subsystems that check memory allocations. In a typical + * production configuration these hooks all should produce no code at all. +@@ -1303,10 +1309,15 @@ + struct page *page; + struct kmem_cache_order_objects oo = s->oo; + gfp_t alloc_gfp; ++ bool enableirqs; + + flags &= gfp_allowed_mask; + +- if (flags & __GFP_WAIT) ++ enableirqs = (flags & __GFP_WAIT) != 0; ++#ifdef CONFIG_PREEMPT_RT_FULL ++ enableirqs |= system_state == SYSTEM_RUNNING; ++#endif ++ if (enableirqs) + local_irq_enable(); + + flags |= s->allocflags; +@@ -1347,7 +1358,7 @@ + kmemcheck_mark_unallocated_pages(page, pages); + } + +- if (flags & __GFP_WAIT) ++ if (enableirqs) + local_irq_disable(); + if (!page) + return NULL; +@@ -1365,8 +1376,10 @@ + void *object) + { + setup_object_debug(s, page, object); ++#ifndef CONFIG_PREEMPT_RT_FULL + if (unlikely(s->ctor)) + s->ctor(object); ++#endif + } + + static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) +@@ -1442,6 +1455,16 @@ + memcg_uncharge_slab(s, order); + } + ++static void free_delayed(struct list_head *h) ++{ ++ while(!list_empty(h)) { ++ struct page *page = list_first_entry(h, struct page, lru); ++ ++ list_del(&page->lru); ++ __free_slab(page->slab_cache, page); ++ } ++} ++ + #define need_reserve_slab_rcu \ + (sizeof(((struct page *)NULL)->lru) < sizeof(struct rcu_head)) + +@@ -1476,6 +1499,12 @@ + } + + call_rcu(head, rcu_free_slab); ++ } else if (irqs_disabled()) { ++ struct slub_free_list *f = &__get_cpu_var(slub_free_list); ++ ++ raw_spin_lock(&f->lock); ++ list_add(&page->lru, &f->list); ++ raw_spin_unlock(&f->lock); + } else + __free_slab(s, page); + } +@@ -1589,7 +1618,7 @@ + if (!n || !n->nr_partial) + return NULL; + +- spin_lock(&n->list_lock); ++ raw_spin_lock(&n->list_lock); + list_for_each_entry_safe(page, page2, &n->partial, lru) { + void *t; + +@@ -1614,7 +1643,7 @@ + break; + + } +- spin_unlock(&n->list_lock); ++ raw_spin_unlock(&n->list_lock); + return object; + } + +@@ -1860,7 +1889,7 @@ + * that acquire_slab() will see a slab page that + * is frozen + */ +- spin_lock(&n->list_lock); ++ raw_spin_lock(&n->list_lock); + } + } else { + m = M_FULL; +@@ -1871,7 +1900,7 @@ + * slabs from diagnostic functions will not see + * any frozen slabs. + */ +- spin_lock(&n->list_lock); ++ raw_spin_lock(&n->list_lock); + } + } + +@@ -1906,7 +1935,7 @@ + goto redo; + + if (lock) +- spin_unlock(&n->list_lock); ++ raw_spin_unlock(&n->list_lock); + + if (m == M_FREE) { + stat(s, DEACTIVATE_EMPTY); +@@ -1938,10 +1967,10 @@ + n2 = get_node(s, page_to_nid(page)); + if (n != n2) { + if (n) +- spin_unlock(&n->list_lock); ++ raw_spin_unlock(&n->list_lock); + + n = n2; +- spin_lock(&n->list_lock); ++ raw_spin_lock(&n->list_lock); + } + + do { +@@ -1970,7 +1999,7 @@ + } + + if (n) +- spin_unlock(&n->list_lock); ++ raw_spin_unlock(&n->list_lock); + + while (discard_page) { + page = discard_page; +@@ -2008,14 +2037,21 @@ + pobjects = oldpage->pobjects; + pages = oldpage->pages; + if (drain && pobjects > s->cpu_partial) { ++ struct slub_free_list *f; + unsigned long flags; ++ LIST_HEAD(tofree); + /* + * partial array is full. Move the existing + * set to the per node partial list. + */ + local_irq_save(flags); + unfreeze_partials(s, this_cpu_ptr(s->cpu_slab)); ++ f = &__get_cpu_var(slub_free_list); ++ raw_spin_lock(&f->lock); ++ list_splice_init(&f->list, &tofree); ++ raw_spin_unlock(&f->lock); + local_irq_restore(flags); ++ free_delayed(&tofree); + oldpage = NULL; + pobjects = 0; + pages = 0; +@@ -2079,7 +2115,22 @@ + + static void flush_all(struct kmem_cache *s) + { ++ LIST_HEAD(tofree); ++ int cpu; ++ + on_each_cpu_cond(has_cpu_slab, flush_cpu_slab, s, 1, GFP_ATOMIC); ++ for_each_online_cpu(cpu) { ++ struct slub_free_list *f; ++ ++ if (!has_cpu_slab(cpu, s)) ++ continue; ++ ++ f = &per_cpu(slub_free_list, cpu); ++ raw_spin_lock_irq(&f->lock); ++ list_splice_init(&f->list, &tofree); ++ raw_spin_unlock_irq(&f->lock); ++ free_delayed(&tofree); ++ } + } + + /* +@@ -2115,10 +2166,10 @@ + unsigned long x = 0; + struct page *page; + +- spin_lock_irqsave(&n->list_lock, flags); ++ raw_spin_lock_irqsave(&n->list_lock, flags); + list_for_each_entry(page, &n->partial, lru) + x += get_count(page); +- spin_unlock_irqrestore(&n->list_lock, flags); ++ raw_spin_unlock_irqrestore(&n->list_lock, flags); + return x; + } + #endif /* CONFIG_SLUB_DEBUG || CONFIG_SYSFS */ +@@ -2255,9 +2306,11 @@ + static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, + unsigned long addr, struct kmem_cache_cpu *c) + { ++ struct slub_free_list *f; + void *freelist; + struct page *page; + unsigned long flags; ++ LIST_HEAD(tofree); + + local_irq_save(flags); + #ifdef CONFIG_PREEMPT +@@ -2325,7 +2378,13 @@ + VM_BUG_ON(!c->page->frozen); + c->freelist = get_freepointer(s, freelist); + c->tid = next_tid(c->tid); ++out: ++ f = &__get_cpu_var(slub_free_list); ++ raw_spin_lock(&f->lock); ++ list_splice_init(&f->list, &tofree); ++ raw_spin_unlock(&f->lock); + local_irq_restore(flags); ++ free_delayed(&tofree); + return freelist; + + new_slab: +@@ -2342,8 +2401,7 @@ + + if (unlikely(!freelist)) { + slab_out_of_memory(s, gfpflags, node); +- local_irq_restore(flags); +- return NULL; ++ goto out; + } + + page = c->page; +@@ -2358,8 +2416,7 @@ + deactivate_slab(s, page, get_freepointer(s, freelist)); + c->page = NULL; + c->freelist = NULL; +- local_irq_restore(flags); +- return freelist; ++ goto out; + } + + /* +@@ -2444,6 +2501,10 @@ + + if (unlikely(gfpflags & __GFP_ZERO) && object) + memset(object, 0, s->object_size); ++#ifdef CONFIG_PREEMPT_RT_FULL ++ if (unlikely(s->ctor) && object) ++ s->ctor(object); ++#endif + + slab_post_alloc_hook(s, gfpflags, object); + +@@ -2531,7 +2592,7 @@ + + do { + if (unlikely(n)) { +- spin_unlock_irqrestore(&n->list_lock, flags); ++ raw_spin_unlock_irqrestore(&n->list_lock, flags); + n = NULL; + } + prior = page->freelist; +@@ -2563,7 +2624,7 @@ + * Otherwise the list_lock will synchronize with + * other processors updating the list of slabs. + */ +- spin_lock_irqsave(&n->list_lock, flags); ++ raw_spin_lock_irqsave(&n->list_lock, flags); + + } + } +@@ -2605,7 +2666,7 @@ + add_partial(n, page, DEACTIVATE_TO_TAIL); + stat(s, FREE_ADD_PARTIAL); + } +- spin_unlock_irqrestore(&n->list_lock, flags); ++ raw_spin_unlock_irqrestore(&n->list_lock, flags); + return; + + slab_empty: +@@ -2620,7 +2681,7 @@ + remove_full(s, n, page); + } + +- spin_unlock_irqrestore(&n->list_lock, flags); ++ raw_spin_unlock_irqrestore(&n->list_lock, flags); + stat(s, FREE_SLAB); + discard_slab(s, page); + } +@@ -2816,7 +2877,7 @@ + init_kmem_cache_node(struct kmem_cache_node *n) + { + n->nr_partial = 0; +- spin_lock_init(&n->list_lock); ++ raw_spin_lock_init(&n->list_lock); + INIT_LIST_HEAD(&n->partial); + #ifdef CONFIG_SLUB_DEBUG + atomic_long_set(&n->nr_slabs, 0); +@@ -3373,7 +3434,7 @@ + for (i = 0; i < objects; i++) + INIT_LIST_HEAD(slabs_by_inuse + i); + +- spin_lock_irqsave(&n->list_lock, flags); ++ raw_spin_lock_irqsave(&n->list_lock, flags); + + /* + * Build lists indexed by the items in use in each slab. +@@ -3394,7 +3455,7 @@ + for (i = objects - 1; i > 0; i--) + list_splice(slabs_by_inuse + i, n->partial.prev); + +- spin_unlock_irqrestore(&n->list_lock, flags); ++ raw_spin_unlock_irqrestore(&n->list_lock, flags); + + /* Release empty slabs */ + list_for_each_entry_safe(page, t, slabs_by_inuse, lru) +@@ -3567,6 +3628,12 @@ + { + static __initdata struct kmem_cache boot_kmem_cache, + boot_kmem_cache_node; ++ int cpu; ++ ++ for_each_possible_cpu(cpu) { ++ raw_spin_lock_init(&per_cpu(slub_free_list, cpu).lock); ++ INIT_LIST_HEAD(&per_cpu(slub_free_list, cpu).list); ++ } + + if (debug_guardpage_minorder()) + slub_max_order = 0; +@@ -3815,7 +3882,7 @@ + struct page *page; + unsigned long flags; + +- spin_lock_irqsave(&n->list_lock, flags); ++ raw_spin_lock_irqsave(&n->list_lock, flags); + + list_for_each_entry(page, &n->partial, lru) { + validate_slab_slab(s, page, map); +@@ -3837,7 +3904,7 @@ + s->name, count, atomic_long_read(&n->nr_slabs)); + + out: +- spin_unlock_irqrestore(&n->list_lock, flags); ++ raw_spin_unlock_irqrestore(&n->list_lock, flags); + return count; + } + +@@ -4025,12 +4092,12 @@ + if (!atomic_long_read(&n->nr_slabs)) + continue; + +- spin_lock_irqsave(&n->list_lock, flags); ++ raw_spin_lock_irqsave(&n->list_lock, flags); + list_for_each_entry(page, &n->partial, lru) + process_slab(&t, s, page, alloc, map); + list_for_each_entry(page, &n->full, lru) + process_slab(&t, s, page, alloc, map); +- spin_unlock_irqrestore(&n->list_lock, flags); ++ raw_spin_unlock_irqrestore(&n->list_lock, flags); + } + + for (i = 0; i < t.count; i++) { +diff -Nur linux-3.18.14.orig/mm/swap.c linux-3.18.14-rt/mm/swap.c +--- linux-3.18.14.orig/mm/swap.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/swap.c 2015-05-31 15:32:49.285635359 -0500 +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + #include "internal.h" + +@@ -44,6 +45,9 @@ + static DEFINE_PER_CPU(struct pagevec, lru_rotate_pvecs); + static DEFINE_PER_CPU(struct pagevec, lru_deactivate_pvecs); + ++static DEFINE_LOCAL_IRQ_LOCK(rotate_lock); ++static DEFINE_LOCAL_IRQ_LOCK(swapvec_lock); ++ + /* + * This path almost never happens for VM activity - pages are normally + * freed via pagevecs. But it gets used by networking. +@@ -473,11 +477,11 @@ + unsigned long flags; + + page_cache_get(page); +- local_irq_save(flags); ++ local_lock_irqsave(rotate_lock, flags); + pvec = this_cpu_ptr(&lru_rotate_pvecs); + if (!pagevec_add(pvec, page)) + pagevec_move_tail(pvec); +- local_irq_restore(flags); ++ local_unlock_irqrestore(rotate_lock, flags); + } + } + +@@ -528,12 +532,13 @@ + void activate_page(struct page *page) + { + if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { +- struct pagevec *pvec = &get_cpu_var(activate_page_pvecs); ++ struct pagevec *pvec = &get_locked_var(swapvec_lock, ++ activate_page_pvecs); + + page_cache_get(page); + if (!pagevec_add(pvec, page)) + pagevec_lru_move_fn(pvec, __activate_page, NULL); +- put_cpu_var(activate_page_pvecs); ++ put_locked_var(swapvec_lock, activate_page_pvecs); + } + } + +@@ -559,7 +564,7 @@ + + static void __lru_cache_activate_page(struct page *page) + { +- struct pagevec *pvec = &get_cpu_var(lru_add_pvec); ++ struct pagevec *pvec = &get_locked_var(swapvec_lock, lru_add_pvec); + int i; + + /* +@@ -581,7 +586,7 @@ + } + } + +- put_cpu_var(lru_add_pvec); ++ put_locked_var(swapvec_lock, lru_add_pvec); + } + + /* +@@ -620,13 +625,13 @@ + + static void __lru_cache_add(struct page *page) + { +- struct pagevec *pvec = &get_cpu_var(lru_add_pvec); ++ struct pagevec *pvec = &get_locked_var(swapvec_lock, lru_add_pvec); + + page_cache_get(page); + if (!pagevec_space(pvec)) + __pagevec_lru_add(pvec); + pagevec_add(pvec, page); +- put_cpu_var(lru_add_pvec); ++ put_locked_var(swapvec_lock, lru_add_pvec); + } + + /** +@@ -806,9 +811,9 @@ + unsigned long flags; + + /* No harm done if a racing interrupt already did this */ +- local_irq_save(flags); ++ local_lock_irqsave(rotate_lock, flags); + pagevec_move_tail(pvec); +- local_irq_restore(flags); ++ local_unlock_irqrestore(rotate_lock, flags); + } + + pvec = &per_cpu(lru_deactivate_pvecs, cpu); +@@ -836,18 +841,19 @@ + return; + + if (likely(get_page_unless_zero(page))) { +- struct pagevec *pvec = &get_cpu_var(lru_deactivate_pvecs); ++ struct pagevec *pvec = &get_locked_var(swapvec_lock, ++ lru_deactivate_pvecs); + + if (!pagevec_add(pvec, page)) + pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL); +- put_cpu_var(lru_deactivate_pvecs); ++ put_locked_var(swapvec_lock, lru_deactivate_pvecs); + } + } + + void lru_add_drain(void) + { +- lru_add_drain_cpu(get_cpu()); +- put_cpu(); ++ lru_add_drain_cpu(local_lock_cpu(swapvec_lock)); ++ local_unlock_cpu(swapvec_lock); + } + + static void lru_add_drain_per_cpu(struct work_struct *dummy) +diff -Nur linux-3.18.14.orig/mm/truncate.c linux-3.18.14-rt/mm/truncate.c +--- linux-3.18.14.orig/mm/truncate.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/truncate.c 2015-05-31 15:32:49.293635359 -0500 +@@ -56,8 +56,11 @@ + * protected by mapping->tree_lock. + */ + if (!workingset_node_shadows(node) && +- !list_empty(&node->private_list)) +- list_lru_del(&workingset_shadow_nodes, &node->private_list); ++ !list_empty(&node->private_list)) { ++ local_lock(workingset_shadow_lock); ++ list_lru_del(&__workingset_shadow_nodes, &node->private_list); ++ local_unlock(workingset_shadow_lock); ++ } + __radix_tree_delete_node(&mapping->page_tree, node); + unlock: + spin_unlock_irq(&mapping->tree_lock); +diff -Nur linux-3.18.14.orig/mm/vmalloc.c linux-3.18.14-rt/mm/vmalloc.c +--- linux-3.18.14.orig/mm/vmalloc.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/vmalloc.c 2015-05-31 15:32:49.297635359 -0500 +@@ -798,7 +798,7 @@ + struct vmap_block *vb; + struct vmap_area *va; + unsigned long vb_idx; +- int node, err; ++ int node, err, cpu; + + node = numa_node_id(); + +@@ -836,11 +836,12 @@ + BUG_ON(err); + radix_tree_preload_end(); + +- vbq = &get_cpu_var(vmap_block_queue); ++ cpu = get_cpu_light(); ++ vbq = &__get_cpu_var(vmap_block_queue); + spin_lock(&vbq->lock); + list_add_rcu(&vb->free_list, &vbq->free); + spin_unlock(&vbq->lock); +- put_cpu_var(vmap_block_queue); ++ put_cpu_light(); + + return vb; + } +@@ -908,6 +909,7 @@ + struct vmap_block *vb; + unsigned long addr = 0; + unsigned int order; ++ int cpu = 0; + + BUG_ON(size & ~PAGE_MASK); + BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC); +@@ -923,7 +925,8 @@ + + again: + rcu_read_lock(); +- vbq = &get_cpu_var(vmap_block_queue); ++ cpu = get_cpu_light(); ++ vbq = &__get_cpu_var(vmap_block_queue); + list_for_each_entry_rcu(vb, &vbq->free, free_list) { + int i; + +@@ -947,7 +950,7 @@ + spin_unlock(&vb->lock); + } + +- put_cpu_var(vmap_block_queue); ++ put_cpu_light(); + rcu_read_unlock(); + + if (!addr) { +diff -Nur linux-3.18.14.orig/mm/vmstat.c linux-3.18.14-rt/mm/vmstat.c +--- linux-3.18.14.orig/mm/vmstat.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/vmstat.c 2015-05-31 15:32:49.297635359 -0500 +@@ -221,6 +221,7 @@ + long x; + long t; + ++ preempt_disable_rt(); + x = delta + __this_cpu_read(*p); + + t = __this_cpu_read(pcp->stat_threshold); +@@ -230,6 +231,7 @@ + x = 0; + } + __this_cpu_write(*p, x); ++ preempt_enable_rt(); + } + EXPORT_SYMBOL(__mod_zone_page_state); + +@@ -262,6 +264,7 @@ + s8 __percpu *p = pcp->vm_stat_diff + item; + s8 v, t; + ++ preempt_disable_rt(); + v = __this_cpu_inc_return(*p); + t = __this_cpu_read(pcp->stat_threshold); + if (unlikely(v > t)) { +@@ -270,6 +273,7 @@ + zone_page_state_add(v + overstep, zone, item); + __this_cpu_write(*p, -overstep); + } ++ preempt_enable_rt(); + } + + void __inc_zone_page_state(struct page *page, enum zone_stat_item item) +@@ -284,6 +288,7 @@ + s8 __percpu *p = pcp->vm_stat_diff + item; + s8 v, t; + ++ preempt_disable_rt(); + v = __this_cpu_dec_return(*p); + t = __this_cpu_read(pcp->stat_threshold); + if (unlikely(v < - t)) { +@@ -292,6 +297,7 @@ + zone_page_state_add(v - overstep, zone, item); + __this_cpu_write(*p, overstep); + } ++ preempt_enable_rt(); + } + + void __dec_zone_page_state(struct page *page, enum zone_stat_item item) +diff -Nur linux-3.18.14.orig/mm/workingset.c linux-3.18.14-rt/mm/workingset.c +--- linux-3.18.14.orig/mm/workingset.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/mm/workingset.c 2015-05-31 15:32:49.321635359 -0500 +@@ -264,7 +264,8 @@ + * point where they would still be useful. + */ + +-struct list_lru workingset_shadow_nodes; ++struct list_lru __workingset_shadow_nodes; ++DEFINE_LOCAL_IRQ_LOCK(workingset_shadow_lock); + + static unsigned long count_shadow_nodes(struct shrinker *shrinker, + struct shrink_control *sc) +@@ -274,9 +275,9 @@ + unsigned long pages; + + /* list_lru lock nests inside IRQ-safe mapping->tree_lock */ +- local_irq_disable(); +- shadow_nodes = list_lru_count_node(&workingset_shadow_nodes, sc->nid); +- local_irq_enable(); ++ local_lock_irq(workingset_shadow_lock); ++ shadow_nodes = list_lru_count_node(&__workingset_shadow_nodes, sc->nid); ++ local_unlock_irq(workingset_shadow_lock); + + pages = node_present_pages(sc->nid); + /* +@@ -362,9 +363,9 @@ + spin_unlock(&mapping->tree_lock); + ret = LRU_REMOVED_RETRY; + out: +- local_irq_enable(); ++ local_unlock_irq(workingset_shadow_lock); + cond_resched(); +- local_irq_disable(); ++ local_lock_irq(workingset_shadow_lock); + spin_lock(lru_lock); + return ret; + } +@@ -375,10 +376,10 @@ + unsigned long ret; + + /* list_lru lock nests inside IRQ-safe mapping->tree_lock */ +- local_irq_disable(); +- ret = list_lru_walk_node(&workingset_shadow_nodes, sc->nid, ++ local_lock_irq(workingset_shadow_lock); ++ ret = list_lru_walk_node(&__workingset_shadow_nodes, sc->nid, + shadow_lru_isolate, NULL, &sc->nr_to_scan); +- local_irq_enable(); ++ local_unlock_irq(workingset_shadow_lock); + return ret; + } + +@@ -399,7 +400,7 @@ + { + int ret; + +- ret = list_lru_init_key(&workingset_shadow_nodes, &shadow_nodes_key); ++ ret = list_lru_init_key(&__workingset_shadow_nodes, &shadow_nodes_key); + if (ret) + goto err; + ret = register_shrinker(&workingset_shadow_shrinker); +@@ -407,7 +408,7 @@ + goto err_list_lru; + return 0; + err_list_lru: +- list_lru_destroy(&workingset_shadow_nodes); ++ list_lru_destroy(&__workingset_shadow_nodes); + err: + return ret; + } +diff -Nur linux-3.18.14.orig/net/core/dev.c linux-3.18.14-rt/net/core/dev.c +--- linux-3.18.14.orig/net/core/dev.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/core/dev.c 2015-05-31 15:32:49.369635359 -0500 +@@ -182,6 +182,7 @@ + static DEFINE_HASHTABLE(napi_hash, 8); + + static seqcount_t devnet_rename_seq; ++static DEFINE_MUTEX(devnet_rename_mutex); + + static inline void dev_base_seq_inc(struct net *net) + { +@@ -203,14 +204,14 @@ + static inline void rps_lock(struct softnet_data *sd) + { + #ifdef CONFIG_RPS +- spin_lock(&sd->input_pkt_queue.lock); ++ raw_spin_lock(&sd->input_pkt_queue.raw_lock); + #endif + } + + static inline void rps_unlock(struct softnet_data *sd) + { + #ifdef CONFIG_RPS +- spin_unlock(&sd->input_pkt_queue.lock); ++ raw_spin_unlock(&sd->input_pkt_queue.raw_lock); + #endif + } + +@@ -832,7 +833,8 @@ + strcpy(name, dev->name); + rcu_read_unlock(); + if (read_seqcount_retry(&devnet_rename_seq, seq)) { +- cond_resched(); ++ mutex_lock(&devnet_rename_mutex); ++ mutex_unlock(&devnet_rename_mutex); + goto retry; + } + +@@ -1101,20 +1103,17 @@ + if (dev->flags & IFF_UP) + return -EBUSY; + +- write_seqcount_begin(&devnet_rename_seq); ++ mutex_lock(&devnet_rename_mutex); ++ __raw_write_seqcount_begin(&devnet_rename_seq); + +- if (strncmp(newname, dev->name, IFNAMSIZ) == 0) { +- write_seqcount_end(&devnet_rename_seq); +- return 0; +- } ++ if (strncmp(newname, dev->name, IFNAMSIZ) == 0) ++ goto outunlock; + + memcpy(oldname, dev->name, IFNAMSIZ); + + err = dev_get_valid_name(net, dev, newname); +- if (err < 0) { +- write_seqcount_end(&devnet_rename_seq); +- return err; +- } ++ if (err < 0) ++ goto outunlock; + + if (oldname[0] && !strchr(oldname, '%')) + netdev_info(dev, "renamed from %s\n", oldname); +@@ -1127,11 +1126,12 @@ + if (ret) { + memcpy(dev->name, oldname, IFNAMSIZ); + dev->name_assign_type = old_assign_type; +- write_seqcount_end(&devnet_rename_seq); +- return ret; ++ err = ret; ++ goto outunlock; + } + +- write_seqcount_end(&devnet_rename_seq); ++ __raw_write_seqcount_end(&devnet_rename_seq); ++ mutex_unlock(&devnet_rename_mutex); + + netdev_adjacent_rename_links(dev, oldname); + +@@ -1152,7 +1152,8 @@ + /* err >= 0 after dev_alloc_name() or stores the first errno */ + if (err >= 0) { + err = ret; +- write_seqcount_begin(&devnet_rename_seq); ++ mutex_lock(&devnet_rename_mutex); ++ __raw_write_seqcount_begin(&devnet_rename_seq); + memcpy(dev->name, oldname, IFNAMSIZ); + memcpy(oldname, newname, IFNAMSIZ); + dev->name_assign_type = old_assign_type; +@@ -1165,6 +1166,11 @@ + } + + return err; ++ ++outunlock: ++ __raw_write_seqcount_end(&devnet_rename_seq); ++ mutex_unlock(&devnet_rename_mutex); ++ return err; + } + + /** +@@ -2160,6 +2166,7 @@ + sd->output_queue_tailp = &q->next_sched; + raise_softirq_irqoff(NET_TX_SOFTIRQ); + local_irq_restore(flags); ++ preempt_check_resched_rt(); + } + + void __netif_schedule(struct Qdisc *q) +@@ -2241,6 +2248,7 @@ + __this_cpu_write(softnet_data.completion_queue, skb); + raise_softirq_irqoff(NET_TX_SOFTIRQ); + local_irq_restore(flags); ++ preempt_check_resched_rt(); + } + EXPORT_SYMBOL(__dev_kfree_skb_irq); + +@@ -3334,6 +3342,7 @@ + rps_unlock(sd); + + local_irq_restore(flags); ++ preempt_check_resched_rt(); + + atomic_long_inc(&skb->dev->rx_dropped); + kfree_skb(skb); +@@ -3352,7 +3361,7 @@ + struct rps_dev_flow voidflow, *rflow = &voidflow; + int cpu; + +- preempt_disable(); ++ migrate_disable(); + rcu_read_lock(); + + cpu = get_rps_cpu(skb->dev, skb, &rflow); +@@ -3362,13 +3371,13 @@ + ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail); + + rcu_read_unlock(); +- preempt_enable(); ++ migrate_enable(); + } else + #endif + { + unsigned int qtail; +- ret = enqueue_to_backlog(skb, get_cpu(), &qtail); +- put_cpu(); ++ ret = enqueue_to_backlog(skb, get_cpu_light(), &qtail); ++ put_cpu_light(); + } + return ret; + } +@@ -3402,16 +3411,44 @@ + + trace_netif_rx_ni_entry(skb); + +- preempt_disable(); ++ local_bh_disable(); + err = netif_rx_internal(skb); +- if (local_softirq_pending()) +- do_softirq(); +- preempt_enable(); ++ local_bh_enable(); + + return err; + } + EXPORT_SYMBOL(netif_rx_ni); + ++#ifdef CONFIG_PREEMPT_RT_FULL ++/* ++ * RT runs ksoftirqd as a real time thread and the root_lock is a ++ * "sleeping spinlock". If the trylock fails then we can go into an ++ * infinite loop when ksoftirqd preempted the task which actually ++ * holds the lock, because we requeue q and raise NET_TX softirq ++ * causing ksoftirqd to loop forever. ++ * ++ * It's safe to use spin_lock on RT here as softirqs run in thread ++ * context and cannot deadlock against the thread which is holding ++ * root_lock. ++ * ++ * On !RT the trylock might fail, but there we bail out from the ++ * softirq loop after 10 attempts which we can't do on RT. And the ++ * task holding root_lock cannot be preempted, so the only downside of ++ * that trylock is that we need 10 loops to decide that we should have ++ * given up in the first one :) ++ */ ++static inline int take_root_lock(spinlock_t *lock) ++{ ++ spin_lock(lock); ++ return 1; ++} ++#else ++static inline int take_root_lock(spinlock_t *lock) ++{ ++ return spin_trylock(lock); ++} ++#endif ++ + static void net_tx_action(struct softirq_action *h) + { + struct softnet_data *sd = this_cpu_ptr(&softnet_data); +@@ -3453,7 +3490,7 @@ + head = head->next_sched; + + root_lock = qdisc_lock(q); +- if (spin_trylock(root_lock)) { ++ if (take_root_lock(root_lock)) { + smp_mb__before_atomic(); + clear_bit(__QDISC_STATE_SCHED, + &q->state); +@@ -3846,7 +3883,7 @@ + skb_queue_walk_safe(&sd->input_pkt_queue, skb, tmp) { + if (skb->dev == dev) { + __skb_unlink(skb, &sd->input_pkt_queue); +- kfree_skb(skb); ++ __skb_queue_tail(&sd->tofree_queue, skb); + input_queue_head_incr(sd); + } + } +@@ -3855,10 +3892,13 @@ + skb_queue_walk_safe(&sd->process_queue, skb, tmp) { + if (skb->dev == dev) { + __skb_unlink(skb, &sd->process_queue); +- kfree_skb(skb); ++ __skb_queue_tail(&sd->tofree_queue, skb); + input_queue_head_incr(sd); + } + } ++ ++ if (!skb_queue_empty(&sd->tofree_queue)) ++ raise_softirq_irqoff(NET_RX_SOFTIRQ); + } + + static int napi_gro_complete(struct sk_buff *skb) +@@ -4321,6 +4361,7 @@ + } else + #endif + local_irq_enable(); ++ preempt_check_resched_rt(); + } + + static int process_backlog(struct napi_struct *napi, int quota) +@@ -4392,6 +4433,7 @@ + local_irq_save(flags); + ____napi_schedule(this_cpu_ptr(&softnet_data), n); + local_irq_restore(flags); ++ preempt_check_resched_rt(); + } + EXPORT_SYMBOL(__napi_schedule); + +@@ -4514,10 +4556,17 @@ + struct softnet_data *sd = this_cpu_ptr(&softnet_data); + unsigned long time_limit = jiffies + 2; + int budget = netdev_budget; ++ struct sk_buff *skb; + void *have; + + local_irq_disable(); + ++ while ((skb = __skb_dequeue(&sd->tofree_queue))) { ++ local_irq_enable(); ++ kfree_skb(skb); ++ local_irq_disable(); ++ } ++ + while (!list_empty(&sd->poll_list)) { + struct napi_struct *n; + int work, weight; +@@ -7006,6 +7055,7 @@ + + raise_softirq_irqoff(NET_TX_SOFTIRQ); + local_irq_enable(); ++ preempt_check_resched_rt(); + + /* Process offline CPU's input_pkt_queue */ + while ((skb = __skb_dequeue(&oldsd->process_queue))) { +@@ -7016,6 +7066,9 @@ + netif_rx_internal(skb); + input_queue_head_incr(oldsd); + } ++ while ((skb = __skb_dequeue(&oldsd->tofree_queue))) { ++ kfree_skb(skb); ++ } + + return NOTIFY_OK; + } +@@ -7317,8 +7370,9 @@ + for_each_possible_cpu(i) { + struct softnet_data *sd = &per_cpu(softnet_data, i); + +- skb_queue_head_init(&sd->input_pkt_queue); +- skb_queue_head_init(&sd->process_queue); ++ skb_queue_head_init_raw(&sd->input_pkt_queue); ++ skb_queue_head_init_raw(&sd->process_queue); ++ skb_queue_head_init_raw(&sd->tofree_queue); + INIT_LIST_HEAD(&sd->poll_list); + sd->output_queue_tailp = &sd->output_queue; + #ifdef CONFIG_RPS +diff -Nur linux-3.18.14.orig/net/core/skbuff.c linux-3.18.14-rt/net/core/skbuff.c +--- linux-3.18.14.orig/net/core/skbuff.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/core/skbuff.c 2015-05-31 15:32:49.393635358 -0500 +@@ -63,6 +63,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -353,6 +354,7 @@ + unsigned int pagecnt_bias; + }; + static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache); ++static DEFINE_LOCAL_IRQ_LOCK(netdev_alloc_lock); + + static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) + { +@@ -361,7 +363,7 @@ + int order; + unsigned long flags; + +- local_irq_save(flags); ++ local_lock_irqsave(netdev_alloc_lock, flags); + nc = this_cpu_ptr(&netdev_alloc_cache); + if (unlikely(!nc->frag.page)) { + refill: +@@ -407,7 +409,7 @@ + nc->frag.offset += fragsz; + nc->pagecnt_bias--; + end: +- local_irq_restore(flags); ++ local_unlock_irqrestore(netdev_alloc_lock, flags); + return data; + } + +diff -Nur linux-3.18.14.orig/net/core/skbuff.c.orig linux-3.18.14-rt/net/core/skbuff.c.orig +--- linux-3.18.14.orig/net/core/skbuff.c.orig 1969-12-31 18:00:00.000000000 -0600 ++++ linux-3.18.14-rt/net/core/skbuff.c.orig 2015-05-20 10:04:50.000000000 -0500 +@@ -0,0 +1,4231 @@ ++/* ++ * Routines having to do with the 'struct sk_buff' memory handlers. ++ * ++ * Authors: Alan Cox ++ * Florian La Roche ++ * ++ * Fixes: ++ * Alan Cox : Fixed the worst of the load ++ * balancer bugs. ++ * Dave Platt : Interrupt stacking fix. ++ * Richard Kooijman : Timestamp fixes. ++ * Alan Cox : Changed buffer format. ++ * Alan Cox : destructor hook for AF_UNIX etc. ++ * Linus Torvalds : Better skb_clone. ++ * Alan Cox : Added skb_copy. ++ * Alan Cox : Added all the changed routines Linus ++ * only put in the headers ++ * Ray VanTassle : Fixed --skb->lock in free ++ * Alan Cox : skb_copy copy arp field ++ * Andi Kleen : slabified it. ++ * Robert Olsson : Removed skb_head_pool ++ * ++ * NOTE: ++ * The __skb_ routines should be called with interrupts ++ * disabled, or you better be *real* sure that the operation is atomic ++ * with respect to whatever list is being frobbed (e.g. via lock_sock() ++ * or via disabling bottom half handlers, etc). ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++/* ++ * The functions in this file will not compile correctly with gcc 2.4.x ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef CONFIG_NET_CLS_ACT ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++struct kmem_cache *skbuff_head_cache __read_mostly; ++static struct kmem_cache *skbuff_fclone_cache __read_mostly; ++ ++/** ++ * skb_panic - private function for out-of-line support ++ * @skb: buffer ++ * @sz: size ++ * @addr: address ++ * @msg: skb_over_panic or skb_under_panic ++ * ++ * Out-of-line support for skb_put() and skb_push(). ++ * Called via the wrapper skb_over_panic() or skb_under_panic(). ++ * Keep out of line to prevent kernel bloat. ++ * __builtin_return_address is not used because it is not always reliable. ++ */ ++static void skb_panic(struct sk_buff *skb, unsigned int sz, void *addr, ++ const char msg[]) ++{ ++ pr_emerg("%s: text:%p len:%d put:%d head:%p data:%p tail:%#lx end:%#lx dev:%s\n", ++ msg, addr, skb->len, sz, skb->head, skb->data, ++ (unsigned long)skb->tail, (unsigned long)skb->end, ++ skb->dev ? skb->dev->name : ""); ++ BUG(); ++} ++ ++static void skb_over_panic(struct sk_buff *skb, unsigned int sz, void *addr) ++{ ++ skb_panic(skb, sz, addr, __func__); ++} ++ ++static void skb_under_panic(struct sk_buff *skb, unsigned int sz, void *addr) ++{ ++ skb_panic(skb, sz, addr, __func__); ++} ++ ++/* ++ * kmalloc_reserve is a wrapper around kmalloc_node_track_caller that tells ++ * the caller if emergency pfmemalloc reserves are being used. If it is and ++ * the socket is later found to be SOCK_MEMALLOC then PFMEMALLOC reserves ++ * may be used. Otherwise, the packet data may be discarded until enough ++ * memory is free ++ */ ++#define kmalloc_reserve(size, gfp, node, pfmemalloc) \ ++ __kmalloc_reserve(size, gfp, node, _RET_IP_, pfmemalloc) ++ ++static void *__kmalloc_reserve(size_t size, gfp_t flags, int node, ++ unsigned long ip, bool *pfmemalloc) ++{ ++ void *obj; ++ bool ret_pfmemalloc = false; ++ ++ /* ++ * Try a regular allocation, when that fails and we're not entitled ++ * to the reserves, fail. ++ */ ++ obj = kmalloc_node_track_caller(size, ++ flags | __GFP_NOMEMALLOC | __GFP_NOWARN, ++ node); ++ if (obj || !(gfp_pfmemalloc_allowed(flags))) ++ goto out; ++ ++ /* Try again but now we are using pfmemalloc reserves */ ++ ret_pfmemalloc = true; ++ obj = kmalloc_node_track_caller(size, flags, node); ++ ++out: ++ if (pfmemalloc) ++ *pfmemalloc = ret_pfmemalloc; ++ ++ return obj; ++} ++ ++/* Allocate a new skbuff. We do this ourselves so we can fill in a few ++ * 'private' fields and also do memory statistics to find all the ++ * [BEEP] leaks. ++ * ++ */ ++ ++struct sk_buff *__alloc_skb_head(gfp_t gfp_mask, int node) ++{ ++ struct sk_buff *skb; ++ ++ /* Get the HEAD */ ++ skb = kmem_cache_alloc_node(skbuff_head_cache, ++ gfp_mask & ~__GFP_DMA, node); ++ if (!skb) ++ goto out; ++ ++ /* ++ * Only clear those fields we need to clear, not those that we will ++ * actually initialise below. Hence, don't put any more fields after ++ * the tail pointer in struct sk_buff! ++ */ ++ memset(skb, 0, offsetof(struct sk_buff, tail)); ++ skb->head = NULL; ++ skb->truesize = sizeof(struct sk_buff); ++ atomic_set(&skb->users, 1); ++ ++ skb->mac_header = (typeof(skb->mac_header))~0U; ++out: ++ return skb; ++} ++ ++/** ++ * __alloc_skb - allocate a network buffer ++ * @size: size to allocate ++ * @gfp_mask: allocation mask ++ * @flags: If SKB_ALLOC_FCLONE is set, allocate from fclone cache ++ * instead of head cache and allocate a cloned (child) skb. ++ * If SKB_ALLOC_RX is set, __GFP_MEMALLOC will be used for ++ * allocations in case the data is required for writeback ++ * @node: numa node to allocate memory on ++ * ++ * Allocate a new &sk_buff. The returned buffer has no headroom and a ++ * tail room of at least size bytes. The object has a reference count ++ * of one. The return is the buffer. On a failure the return is %NULL. ++ * ++ * Buffers may only be allocated from interrupts using a @gfp_mask of ++ * %GFP_ATOMIC. ++ */ ++struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, ++ int flags, int node) ++{ ++ struct kmem_cache *cache; ++ struct skb_shared_info *shinfo; ++ struct sk_buff *skb; ++ u8 *data; ++ bool pfmemalloc; ++ ++ cache = (flags & SKB_ALLOC_FCLONE) ++ ? skbuff_fclone_cache : skbuff_head_cache; ++ ++ if (sk_memalloc_socks() && (flags & SKB_ALLOC_RX)) ++ gfp_mask |= __GFP_MEMALLOC; ++ ++ /* Get the HEAD */ ++ skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node); ++ if (!skb) ++ goto out; ++ prefetchw(skb); ++ ++ /* We do our best to align skb_shared_info on a separate cache ++ * line. It usually works because kmalloc(X > SMP_CACHE_BYTES) gives ++ * aligned memory blocks, unless SLUB/SLAB debug is enabled. ++ * Both skb->head and skb_shared_info are cache line aligned. ++ */ ++ size = SKB_DATA_ALIGN(size); ++ size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ data = kmalloc_reserve(size, gfp_mask, node, &pfmemalloc); ++ if (!data) ++ goto nodata; ++ /* kmalloc(size) might give us more room than requested. ++ * Put skb_shared_info exactly at the end of allocated zone, ++ * to allow max possible filling before reallocation. ++ */ ++ size = SKB_WITH_OVERHEAD(ksize(data)); ++ prefetchw(data + size); ++ ++ /* ++ * Only clear those fields we need to clear, not those that we will ++ * actually initialise below. Hence, don't put any more fields after ++ * the tail pointer in struct sk_buff! ++ */ ++ memset(skb, 0, offsetof(struct sk_buff, tail)); ++ /* Account for allocated memory : skb + skb->head */ ++ skb->truesize = SKB_TRUESIZE(size); ++ skb->pfmemalloc = pfmemalloc; ++ atomic_set(&skb->users, 1); ++ skb->head = data; ++ skb->data = data; ++ skb_reset_tail_pointer(skb); ++ skb->end = skb->tail + size; ++ skb->mac_header = (typeof(skb->mac_header))~0U; ++ skb->transport_header = (typeof(skb->transport_header))~0U; ++ ++ /* make sure we initialize shinfo sequentially */ ++ shinfo = skb_shinfo(skb); ++ memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); ++ atomic_set(&shinfo->dataref, 1); ++ kmemcheck_annotate_variable(shinfo->destructor_arg); ++ ++ if (flags & SKB_ALLOC_FCLONE) { ++ struct sk_buff_fclones *fclones; ++ ++ fclones = container_of(skb, struct sk_buff_fclones, skb1); ++ ++ kmemcheck_annotate_bitfield(&fclones->skb2, flags1); ++ skb->fclone = SKB_FCLONE_ORIG; ++ atomic_set(&fclones->fclone_ref, 1); ++ ++ fclones->skb2.fclone = SKB_FCLONE_FREE; ++ fclones->skb2.pfmemalloc = pfmemalloc; ++ } ++out: ++ return skb; ++nodata: ++ kmem_cache_free(cache, skb); ++ skb = NULL; ++ goto out; ++} ++EXPORT_SYMBOL(__alloc_skb); ++ ++/** ++ * __build_skb - build a network buffer ++ * @data: data buffer provided by caller ++ * @frag_size: size of data, or 0 if head was kmalloced ++ * ++ * Allocate a new &sk_buff. Caller provides space holding head and ++ * skb_shared_info. @data must have been allocated by kmalloc() only if ++ * @frag_size is 0, otherwise data should come from the page allocator ++ * or vmalloc() ++ * The return is the new skb buffer. ++ * On a failure the return is %NULL, and @data is not freed. ++ * Notes : ++ * Before IO, driver allocates only data buffer where NIC put incoming frame ++ * Driver should add room at head (NET_SKB_PAD) and ++ * MUST add room at tail (SKB_DATA_ALIGN(skb_shared_info)) ++ * After IO, driver calls build_skb(), to allocate sk_buff and populate it ++ * before giving packet to stack. ++ * RX rings only contains data buffers, not full skbs. ++ */ ++struct sk_buff *__build_skb(void *data, unsigned int frag_size) ++{ ++ struct skb_shared_info *shinfo; ++ struct sk_buff *skb; ++ unsigned int size = frag_size ? : ksize(data); ++ ++ skb = kmem_cache_alloc(skbuff_head_cache, GFP_ATOMIC); ++ if (!skb) ++ return NULL; ++ ++ size -= SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ ++ memset(skb, 0, offsetof(struct sk_buff, tail)); ++ skb->truesize = SKB_TRUESIZE(size); ++ atomic_set(&skb->users, 1); ++ skb->head = data; ++ skb->data = data; ++ skb_reset_tail_pointer(skb); ++ skb->end = skb->tail + size; ++ skb->mac_header = (typeof(skb->mac_header))~0U; ++ skb->transport_header = (typeof(skb->transport_header))~0U; ++ ++ /* make sure we initialize shinfo sequentially */ ++ shinfo = skb_shinfo(skb); ++ memset(shinfo, 0, offsetof(struct skb_shared_info, dataref)); ++ atomic_set(&shinfo->dataref, 1); ++ kmemcheck_annotate_variable(shinfo->destructor_arg); ++ ++ return skb; ++} ++ ++/* build_skb() is wrapper over __build_skb(), that specifically ++ * takes care of skb->head and skb->pfmemalloc ++ * This means that if @frag_size is not zero, then @data must be backed ++ * by a page fragment, not kmalloc() or vmalloc() ++ */ ++struct sk_buff *build_skb(void *data, unsigned int frag_size) ++{ ++ struct sk_buff *skb = __build_skb(data, frag_size); ++ ++ if (skb && frag_size) { ++ skb->head_frag = 1; ++ if (virt_to_head_page(data)->pfmemalloc) ++ skb->pfmemalloc = 1; ++ } ++ return skb; ++} ++EXPORT_SYMBOL(build_skb); ++ ++struct netdev_alloc_cache { ++ struct page_frag frag; ++ /* we maintain a pagecount bias, so that we dont dirty cache line ++ * containing page->_count every time we allocate a fragment. ++ */ ++ unsigned int pagecnt_bias; ++}; ++static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache); ++ ++static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask) ++{ ++ struct netdev_alloc_cache *nc; ++ void *data = NULL; ++ int order; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ nc = this_cpu_ptr(&netdev_alloc_cache); ++ if (unlikely(!nc->frag.page)) { ++refill: ++ for (order = NETDEV_FRAG_PAGE_MAX_ORDER; ;) { ++ gfp_t gfp = gfp_mask; ++ ++ if (order) ++ gfp |= __GFP_COMP | __GFP_NOWARN | ++ __GFP_NOMEMALLOC; ++ nc->frag.page = alloc_pages(gfp, order); ++ if (likely(nc->frag.page)) ++ break; ++ if (--order < 0) ++ goto end; ++ } ++ nc->frag.size = PAGE_SIZE << order; ++ /* Even if we own the page, we do not use atomic_set(). ++ * This would break get_page_unless_zero() users. ++ */ ++ atomic_add(NETDEV_PAGECNT_MAX_BIAS - 1, ++ &nc->frag.page->_count); ++ nc->pagecnt_bias = NETDEV_PAGECNT_MAX_BIAS; ++ nc->frag.offset = 0; ++ } ++ ++ if (nc->frag.offset + fragsz > nc->frag.size) { ++ if (atomic_read(&nc->frag.page->_count) != nc->pagecnt_bias) { ++ if (!atomic_sub_and_test(nc->pagecnt_bias, ++ &nc->frag.page->_count)) ++ goto refill; ++ /* OK, page count is 0, we can safely set it */ ++ atomic_set(&nc->frag.page->_count, ++ NETDEV_PAGECNT_MAX_BIAS); ++ } else { ++ atomic_add(NETDEV_PAGECNT_MAX_BIAS - nc->pagecnt_bias, ++ &nc->frag.page->_count); ++ } ++ nc->pagecnt_bias = NETDEV_PAGECNT_MAX_BIAS; ++ nc->frag.offset = 0; ++ } ++ ++ data = page_address(nc->frag.page) + nc->frag.offset; ++ nc->frag.offset += fragsz; ++ nc->pagecnt_bias--; ++end: ++ local_irq_restore(flags); ++ return data; ++} ++ ++/** ++ * netdev_alloc_frag - allocate a page fragment ++ * @fragsz: fragment size ++ * ++ * Allocates a frag from a page for receive buffer. ++ * Uses GFP_ATOMIC allocations. ++ */ ++void *netdev_alloc_frag(unsigned int fragsz) ++{ ++ return __netdev_alloc_frag(fragsz, GFP_ATOMIC | __GFP_COLD); ++} ++EXPORT_SYMBOL(netdev_alloc_frag); ++ ++/** ++ * __netdev_alloc_skb - allocate an skbuff for rx on a specific device ++ * @dev: network device to receive on ++ * @length: length to allocate ++ * @gfp_mask: get_free_pages mask, passed to alloc_skb ++ * ++ * Allocate a new &sk_buff and assign it a usage count of one. The ++ * buffer has unspecified headroom built in. Users should allocate ++ * the headroom they think they need without accounting for the ++ * built in space. The built in space is used for optimisations. ++ * ++ * %NULL is returned if there is no free memory. ++ */ ++struct sk_buff *__netdev_alloc_skb(struct net_device *dev, ++ unsigned int length, gfp_t gfp_mask) ++{ ++ struct sk_buff *skb = NULL; ++ unsigned int fragsz = SKB_DATA_ALIGN(length + NET_SKB_PAD) + ++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ ++ if (fragsz <= PAGE_SIZE && !(gfp_mask & (__GFP_WAIT | GFP_DMA))) { ++ void *data; ++ ++ if (sk_memalloc_socks()) ++ gfp_mask |= __GFP_MEMALLOC; ++ ++ data = __netdev_alloc_frag(fragsz, gfp_mask); ++ ++ if (likely(data)) { ++ skb = build_skb(data, fragsz); ++ if (unlikely(!skb)) ++ put_page(virt_to_head_page(data)); ++ } ++ } else { ++ skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, ++ SKB_ALLOC_RX, NUMA_NO_NODE); ++ } ++ if (likely(skb)) { ++ skb_reserve(skb, NET_SKB_PAD); ++ skb->dev = dev; ++ } ++ return skb; ++} ++EXPORT_SYMBOL(__netdev_alloc_skb); ++ ++void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, ++ int size, unsigned int truesize) ++{ ++ skb_fill_page_desc(skb, i, page, off, size); ++ skb->len += size; ++ skb->data_len += size; ++ skb->truesize += truesize; ++} ++EXPORT_SYMBOL(skb_add_rx_frag); ++ ++void skb_coalesce_rx_frag(struct sk_buff *skb, int i, int size, ++ unsigned int truesize) ++{ ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ ++ skb_frag_size_add(frag, size); ++ skb->len += size; ++ skb->data_len += size; ++ skb->truesize += truesize; ++} ++EXPORT_SYMBOL(skb_coalesce_rx_frag); ++ ++static void skb_drop_list(struct sk_buff **listp) ++{ ++ kfree_skb_list(*listp); ++ *listp = NULL; ++} ++ ++static inline void skb_drop_fraglist(struct sk_buff *skb) ++{ ++ skb_drop_list(&skb_shinfo(skb)->frag_list); ++} ++ ++static void skb_clone_fraglist(struct sk_buff *skb) ++{ ++ struct sk_buff *list; ++ ++ skb_walk_frags(skb, list) ++ skb_get(list); ++} ++ ++static void skb_free_head(struct sk_buff *skb) ++{ ++ if (skb->head_frag) ++ put_page(virt_to_head_page(skb->head)); ++ else ++ kfree(skb->head); ++} ++ ++static void skb_release_data(struct sk_buff *skb) ++{ ++ struct skb_shared_info *shinfo = skb_shinfo(skb); ++ int i; ++ ++ if (skb->cloned && ++ atomic_sub_return(skb->nohdr ? (1 << SKB_DATAREF_SHIFT) + 1 : 1, ++ &shinfo->dataref)) ++ return; ++ ++ for (i = 0; i < shinfo->nr_frags; i++) ++ __skb_frag_unref(&shinfo->frags[i]); ++ ++ /* ++ * If skb buf is from userspace, we need to notify the caller ++ * the lower device DMA has done; ++ */ ++ if (shinfo->tx_flags & SKBTX_DEV_ZEROCOPY) { ++ struct ubuf_info *uarg; ++ ++ uarg = shinfo->destructor_arg; ++ if (uarg->callback) ++ uarg->callback(uarg, true); ++ } ++ ++ if (shinfo->frag_list) ++ kfree_skb_list(shinfo->frag_list); ++ ++ skb_free_head(skb); ++} ++ ++/* ++ * Free an skbuff by memory without cleaning the state. ++ */ ++static void kfree_skbmem(struct sk_buff *skb) ++{ ++ struct sk_buff_fclones *fclones; ++ ++ switch (skb->fclone) { ++ case SKB_FCLONE_UNAVAILABLE: ++ kmem_cache_free(skbuff_head_cache, skb); ++ break; ++ ++ case SKB_FCLONE_ORIG: ++ fclones = container_of(skb, struct sk_buff_fclones, skb1); ++ if (atomic_dec_and_test(&fclones->fclone_ref)) ++ kmem_cache_free(skbuff_fclone_cache, fclones); ++ break; ++ ++ case SKB_FCLONE_CLONE: ++ fclones = container_of(skb, struct sk_buff_fclones, skb2); ++ ++ /* The clone portion is available for ++ * fast-cloning again. ++ */ ++ skb->fclone = SKB_FCLONE_FREE; ++ ++ if (atomic_dec_and_test(&fclones->fclone_ref)) ++ kmem_cache_free(skbuff_fclone_cache, fclones); ++ break; ++ } ++} ++ ++static void skb_release_head_state(struct sk_buff *skb) ++{ ++ skb_dst_drop(skb); ++#ifdef CONFIG_XFRM ++ secpath_put(skb->sp); ++#endif ++ if (skb->destructor) { ++ WARN_ON(in_irq()); ++ skb->destructor(skb); ++ } ++#if IS_ENABLED(CONFIG_NF_CONNTRACK) ++ nf_conntrack_put(skb->nfct); ++#endif ++#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) ++ nf_bridge_put(skb->nf_bridge); ++#endif ++/* XXX: IS this still necessary? - JHS */ ++#ifdef CONFIG_NET_SCHED ++ skb->tc_index = 0; ++#ifdef CONFIG_NET_CLS_ACT ++ skb->tc_verd = 0; ++#endif ++#endif ++} ++ ++/* Free everything but the sk_buff shell. */ ++static void skb_release_all(struct sk_buff *skb) ++{ ++ skb_release_head_state(skb); ++ if (likely(skb->head)) ++ skb_release_data(skb); ++} ++ ++/** ++ * __kfree_skb - private function ++ * @skb: buffer ++ * ++ * Free an sk_buff. Release anything attached to the buffer. ++ * Clean the state. This is an internal helper function. Users should ++ * always call kfree_skb ++ */ ++ ++void __kfree_skb(struct sk_buff *skb) ++{ ++ skb_release_all(skb); ++ kfree_skbmem(skb); ++} ++EXPORT_SYMBOL(__kfree_skb); ++ ++/** ++ * kfree_skb - free an sk_buff ++ * @skb: buffer to free ++ * ++ * Drop a reference to the buffer and free it if the usage count has ++ * hit zero. ++ */ ++void kfree_skb(struct sk_buff *skb) ++{ ++ if (unlikely(!skb)) ++ return; ++ if (likely(atomic_read(&skb->users) == 1)) ++ smp_rmb(); ++ else if (likely(!atomic_dec_and_test(&skb->users))) ++ return; ++ trace_kfree_skb(skb, __builtin_return_address(0)); ++ __kfree_skb(skb); ++} ++EXPORT_SYMBOL(kfree_skb); ++ ++void kfree_skb_list(struct sk_buff *segs) ++{ ++ while (segs) { ++ struct sk_buff *next = segs->next; ++ ++ kfree_skb(segs); ++ segs = next; ++ } ++} ++EXPORT_SYMBOL(kfree_skb_list); ++ ++/** ++ * skb_tx_error - report an sk_buff xmit error ++ * @skb: buffer that triggered an error ++ * ++ * Report xmit error if a device callback is tracking this skb. ++ * skb must be freed afterwards. ++ */ ++void skb_tx_error(struct sk_buff *skb) ++{ ++ if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) { ++ struct ubuf_info *uarg; ++ ++ uarg = skb_shinfo(skb)->destructor_arg; ++ if (uarg->callback) ++ uarg->callback(uarg, false); ++ skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY; ++ } ++} ++EXPORT_SYMBOL(skb_tx_error); ++ ++/** ++ * consume_skb - free an skbuff ++ * @skb: buffer to free ++ * ++ * Drop a ref to the buffer and free it if the usage count has hit zero ++ * Functions identically to kfree_skb, but kfree_skb assumes that the frame ++ * is being dropped after a failure and notes that ++ */ ++void consume_skb(struct sk_buff *skb) ++{ ++ if (unlikely(!skb)) ++ return; ++ if (likely(atomic_read(&skb->users) == 1)) ++ smp_rmb(); ++ else if (likely(!atomic_dec_and_test(&skb->users))) ++ return; ++ trace_consume_skb(skb); ++ __kfree_skb(skb); ++} ++EXPORT_SYMBOL(consume_skb); ++ ++/* Make sure a field is enclosed inside headers_start/headers_end section */ ++#define CHECK_SKB_FIELD(field) \ ++ BUILD_BUG_ON(offsetof(struct sk_buff, field) < \ ++ offsetof(struct sk_buff, headers_start)); \ ++ BUILD_BUG_ON(offsetof(struct sk_buff, field) > \ ++ offsetof(struct sk_buff, headers_end)); \ ++ ++static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) ++{ ++ new->tstamp = old->tstamp; ++ /* We do not copy old->sk */ ++ new->dev = old->dev; ++ memcpy(new->cb, old->cb, sizeof(old->cb)); ++ skb_dst_copy(new, old); ++#ifdef CONFIG_XFRM ++ new->sp = secpath_get(old->sp); ++#endif ++ __nf_copy(new, old, false); ++ ++ /* Note : this field could be in headers_start/headers_end section ++ * It is not yet because we do not want to have a 16 bit hole ++ */ ++ new->queue_mapping = old->queue_mapping; ++ ++ memcpy(&new->headers_start, &old->headers_start, ++ offsetof(struct sk_buff, headers_end) - ++ offsetof(struct sk_buff, headers_start)); ++ CHECK_SKB_FIELD(protocol); ++ CHECK_SKB_FIELD(csum); ++ CHECK_SKB_FIELD(hash); ++ CHECK_SKB_FIELD(priority); ++ CHECK_SKB_FIELD(skb_iif); ++ CHECK_SKB_FIELD(vlan_proto); ++ CHECK_SKB_FIELD(vlan_tci); ++ CHECK_SKB_FIELD(transport_header); ++ CHECK_SKB_FIELD(network_header); ++ CHECK_SKB_FIELD(mac_header); ++ CHECK_SKB_FIELD(inner_protocol); ++ CHECK_SKB_FIELD(inner_transport_header); ++ CHECK_SKB_FIELD(inner_network_header); ++ CHECK_SKB_FIELD(inner_mac_header); ++ CHECK_SKB_FIELD(mark); ++#ifdef CONFIG_NETWORK_SECMARK ++ CHECK_SKB_FIELD(secmark); ++#endif ++#ifdef CONFIG_NET_RX_BUSY_POLL ++ CHECK_SKB_FIELD(napi_id); ++#endif ++#ifdef CONFIG_NET_SCHED ++ CHECK_SKB_FIELD(tc_index); ++#ifdef CONFIG_NET_CLS_ACT ++ CHECK_SKB_FIELD(tc_verd); ++#endif ++#endif ++ ++} ++ ++/* ++ * You should not add any new code to this function. Add it to ++ * __copy_skb_header above instead. ++ */ ++static struct sk_buff *__skb_clone(struct sk_buff *n, struct sk_buff *skb) ++{ ++#define C(x) n->x = skb->x ++ ++ n->next = n->prev = NULL; ++ n->sk = NULL; ++ __copy_skb_header(n, skb); ++ ++ C(len); ++ C(data_len); ++ C(mac_len); ++ n->hdr_len = skb->nohdr ? skb_headroom(skb) : skb->hdr_len; ++ n->cloned = 1; ++ n->nohdr = 0; ++ n->destructor = NULL; ++ C(tail); ++ C(end); ++ C(head); ++ C(head_frag); ++ C(data); ++ C(truesize); ++ atomic_set(&n->users, 1); ++ ++ atomic_inc(&(skb_shinfo(skb)->dataref)); ++ skb->cloned = 1; ++ ++ return n; ++#undef C ++} ++ ++/** ++ * skb_morph - morph one skb into another ++ * @dst: the skb to receive the contents ++ * @src: the skb to supply the contents ++ * ++ * This is identical to skb_clone except that the target skb is ++ * supplied by the user. ++ * ++ * The target skb is returned upon exit. ++ */ ++struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src) ++{ ++ skb_release_all(dst); ++ return __skb_clone(dst, src); ++} ++EXPORT_SYMBOL_GPL(skb_morph); ++ ++/** ++ * skb_copy_ubufs - copy userspace skb frags buffers to kernel ++ * @skb: the skb to modify ++ * @gfp_mask: allocation priority ++ * ++ * This must be called on SKBTX_DEV_ZEROCOPY skb. ++ * It will copy all frags into kernel and drop the reference ++ * to userspace pages. ++ * ++ * If this function is called from an interrupt gfp_mask() must be ++ * %GFP_ATOMIC. ++ * ++ * Returns 0 on success or a negative error code on failure ++ * to allocate kernel memory to copy to. ++ */ ++int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) ++{ ++ int i; ++ int num_frags = skb_shinfo(skb)->nr_frags; ++ struct page *page, *head = NULL; ++ struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg; ++ ++ for (i = 0; i < num_frags; i++) { ++ u8 *vaddr; ++ skb_frag_t *f = &skb_shinfo(skb)->frags[i]; ++ ++ page = alloc_page(gfp_mask); ++ if (!page) { ++ while (head) { ++ struct page *next = (struct page *)page_private(head); ++ put_page(head); ++ head = next; ++ } ++ return -ENOMEM; ++ } ++ vaddr = kmap_atomic(skb_frag_page(f)); ++ memcpy(page_address(page), ++ vaddr + f->page_offset, skb_frag_size(f)); ++ kunmap_atomic(vaddr); ++ set_page_private(page, (unsigned long)head); ++ head = page; ++ } ++ ++ /* skb frags release userspace buffers */ ++ for (i = 0; i < num_frags; i++) ++ skb_frag_unref(skb, i); ++ ++ uarg->callback(uarg, false); ++ ++ /* skb frags point to kernel buffers */ ++ for (i = num_frags - 1; i >= 0; i--) { ++ __skb_fill_page_desc(skb, i, head, 0, ++ skb_shinfo(skb)->frags[i].size); ++ head = (struct page *)page_private(head); ++ } ++ ++ skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(skb_copy_ubufs); ++ ++/** ++ * skb_clone - duplicate an sk_buff ++ * @skb: buffer to clone ++ * @gfp_mask: allocation priority ++ * ++ * Duplicate an &sk_buff. The new one is not owned by a socket. Both ++ * copies share the same packet data but not structure. The new ++ * buffer has a reference count of 1. If the allocation fails the ++ * function returns %NULL otherwise the new buffer is returned. ++ * ++ * If this function is called from an interrupt gfp_mask() must be ++ * %GFP_ATOMIC. ++ */ ++ ++struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask) ++{ ++ struct sk_buff_fclones *fclones = container_of(skb, ++ struct sk_buff_fclones, ++ skb1); ++ struct sk_buff *n = &fclones->skb2; ++ ++ if (skb_orphan_frags(skb, gfp_mask)) ++ return NULL; ++ ++ if (skb->fclone == SKB_FCLONE_ORIG && ++ n->fclone == SKB_FCLONE_FREE) { ++ n->fclone = SKB_FCLONE_CLONE; ++ atomic_inc(&fclones->fclone_ref); ++ } else { ++ if (skb_pfmemalloc(skb)) ++ gfp_mask |= __GFP_MEMALLOC; ++ ++ n = kmem_cache_alloc(skbuff_head_cache, gfp_mask); ++ if (!n) ++ return NULL; ++ ++ kmemcheck_annotate_bitfield(n, flags1); ++ n->fclone = SKB_FCLONE_UNAVAILABLE; ++ } ++ ++ return __skb_clone(n, skb); ++} ++EXPORT_SYMBOL(skb_clone); ++ ++static void skb_headers_offset_update(struct sk_buff *skb, int off) ++{ ++ /* Only adjust this if it actually is csum_start rather than csum */ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ skb->csum_start += off; ++ /* {transport,network,mac}_header and tail are relative to skb->head */ ++ skb->transport_header += off; ++ skb->network_header += off; ++ if (skb_mac_header_was_set(skb)) ++ skb->mac_header += off; ++ skb->inner_transport_header += off; ++ skb->inner_network_header += off; ++ skb->inner_mac_header += off; ++} ++ ++static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) ++{ ++ __copy_skb_header(new, old); ++ ++ skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size; ++ skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; ++ skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; ++} ++ ++static inline int skb_alloc_rx_flag(const struct sk_buff *skb) ++{ ++ if (skb_pfmemalloc(skb)) ++ return SKB_ALLOC_RX; ++ return 0; ++} ++ ++/** ++ * skb_copy - create private copy of an sk_buff ++ * @skb: buffer to copy ++ * @gfp_mask: allocation priority ++ * ++ * Make a copy of both an &sk_buff and its data. This is used when the ++ * caller wishes to modify the data and needs a private copy of the ++ * data to alter. Returns %NULL on failure or the pointer to the buffer ++ * on success. The returned buffer has a reference count of 1. ++ * ++ * As by-product this function converts non-linear &sk_buff to linear ++ * one, so that &sk_buff becomes completely private and caller is allowed ++ * to modify all the data of returned buffer. This means that this ++ * function is not recommended for use in circumstances when only ++ * header is going to be modified. Use pskb_copy() instead. ++ */ ++ ++struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask) ++{ ++ int headerlen = skb_headroom(skb); ++ unsigned int size = skb_end_offset(skb) + skb->data_len; ++ struct sk_buff *n = __alloc_skb(size, gfp_mask, ++ skb_alloc_rx_flag(skb), NUMA_NO_NODE); ++ ++ if (!n) ++ return NULL; ++ ++ /* Set the data pointer */ ++ skb_reserve(n, headerlen); ++ /* Set the tail pointer and length */ ++ skb_put(n, skb->len); ++ ++ if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len)) ++ BUG(); ++ ++ copy_skb_header(n, skb); ++ return n; ++} ++EXPORT_SYMBOL(skb_copy); ++ ++/** ++ * __pskb_copy_fclone - create copy of an sk_buff with private head. ++ * @skb: buffer to copy ++ * @headroom: headroom of new skb ++ * @gfp_mask: allocation priority ++ * @fclone: if true allocate the copy of the skb from the fclone ++ * cache instead of the head cache; it is recommended to set this ++ * to true for the cases where the copy will likely be cloned ++ * ++ * Make a copy of both an &sk_buff and part of its data, located ++ * in header. Fragmented data remain shared. This is used when ++ * the caller wishes to modify only header of &sk_buff and needs ++ * private copy of the header to alter. Returns %NULL on failure ++ * or the pointer to the buffer on success. ++ * The returned buffer has a reference count of 1. ++ */ ++ ++struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom, ++ gfp_t gfp_mask, bool fclone) ++{ ++ unsigned int size = skb_headlen(skb) + headroom; ++ int flags = skb_alloc_rx_flag(skb) | (fclone ? SKB_ALLOC_FCLONE : 0); ++ struct sk_buff *n = __alloc_skb(size, gfp_mask, flags, NUMA_NO_NODE); ++ ++ if (!n) ++ goto out; ++ ++ /* Set the data pointer */ ++ skb_reserve(n, headroom); ++ /* Set the tail pointer and length */ ++ skb_put(n, skb_headlen(skb)); ++ /* Copy the bytes */ ++ skb_copy_from_linear_data(skb, n->data, n->len); ++ ++ n->truesize += skb->data_len; ++ n->data_len = skb->data_len; ++ n->len = skb->len; ++ ++ if (skb_shinfo(skb)->nr_frags) { ++ int i; ++ ++ if (skb_orphan_frags(skb, gfp_mask)) { ++ kfree_skb(n); ++ n = NULL; ++ goto out; ++ } ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i]; ++ skb_frag_ref(skb, i); ++ } ++ skb_shinfo(n)->nr_frags = i; ++ } ++ ++ if (skb_has_frag_list(skb)) { ++ skb_shinfo(n)->frag_list = skb_shinfo(skb)->frag_list; ++ skb_clone_fraglist(n); ++ } ++ ++ copy_skb_header(n, skb); ++out: ++ return n; ++} ++EXPORT_SYMBOL(__pskb_copy_fclone); ++ ++/** ++ * pskb_expand_head - reallocate header of &sk_buff ++ * @skb: buffer to reallocate ++ * @nhead: room to add at head ++ * @ntail: room to add at tail ++ * @gfp_mask: allocation priority ++ * ++ * Expands (or creates identical copy, if @nhead and @ntail are zero) ++ * header of @skb. &sk_buff itself is not changed. &sk_buff MUST have ++ * reference count of 1. Returns zero in the case of success or error, ++ * if expansion failed. In the last case, &sk_buff is not changed. ++ * ++ * All the pointers pointing into skb header may change and must be ++ * reloaded after call to this function. ++ */ ++ ++int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, ++ gfp_t gfp_mask) ++{ ++ int i; ++ u8 *data; ++ int size = nhead + skb_end_offset(skb) + ntail; ++ long off; ++ ++ BUG_ON(nhead < 0); ++ ++ if (skb_shared(skb)) ++ BUG(); ++ ++ size = SKB_DATA_ALIGN(size); ++ ++ if (skb_pfmemalloc(skb)) ++ gfp_mask |= __GFP_MEMALLOC; ++ data = kmalloc_reserve(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)), ++ gfp_mask, NUMA_NO_NODE, NULL); ++ if (!data) ++ goto nodata; ++ size = SKB_WITH_OVERHEAD(ksize(data)); ++ ++ /* Copy only real data... and, alas, header. This should be ++ * optimized for the cases when header is void. ++ */ ++ memcpy(data + nhead, skb->head, skb_tail_pointer(skb) - skb->head); ++ ++ memcpy((struct skb_shared_info *)(data + size), ++ skb_shinfo(skb), ++ offsetof(struct skb_shared_info, frags[skb_shinfo(skb)->nr_frags])); ++ ++ /* ++ * if shinfo is shared we must drop the old head gracefully, but if it ++ * is not we can just drop the old head and let the existing refcount ++ * be since all we did is relocate the values ++ */ ++ if (skb_cloned(skb)) { ++ /* copy this zero copy skb frags */ ++ if (skb_orphan_frags(skb, gfp_mask)) ++ goto nofrags; ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) ++ skb_frag_ref(skb, i); ++ ++ if (skb_has_frag_list(skb)) ++ skb_clone_fraglist(skb); ++ ++ skb_release_data(skb); ++ } else { ++ skb_free_head(skb); ++ } ++ off = (data + nhead) - skb->head; ++ ++ skb->head = data; ++ skb->head_frag = 0; ++ skb->data += off; ++#ifdef NET_SKBUFF_DATA_USES_OFFSET ++ skb->end = size; ++ off = nhead; ++#else ++ skb->end = skb->head + size; ++#endif ++ skb->tail += off; ++ skb_headers_offset_update(skb, nhead); ++ skb->cloned = 0; ++ skb->hdr_len = 0; ++ skb->nohdr = 0; ++ atomic_set(&skb_shinfo(skb)->dataref, 1); ++ return 0; ++ ++nofrags: ++ kfree(data); ++nodata: ++ return -ENOMEM; ++} ++EXPORT_SYMBOL(pskb_expand_head); ++ ++/* Make private copy of skb with writable head and some headroom */ ++ ++struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom) ++{ ++ struct sk_buff *skb2; ++ int delta = headroom - skb_headroom(skb); ++ ++ if (delta <= 0) ++ skb2 = pskb_copy(skb, GFP_ATOMIC); ++ else { ++ skb2 = skb_clone(skb, GFP_ATOMIC); ++ if (skb2 && pskb_expand_head(skb2, SKB_DATA_ALIGN(delta), 0, ++ GFP_ATOMIC)) { ++ kfree_skb(skb2); ++ skb2 = NULL; ++ } ++ } ++ return skb2; ++} ++EXPORT_SYMBOL(skb_realloc_headroom); ++ ++/** ++ * skb_copy_expand - copy and expand sk_buff ++ * @skb: buffer to copy ++ * @newheadroom: new free bytes at head ++ * @newtailroom: new free bytes at tail ++ * @gfp_mask: allocation priority ++ * ++ * Make a copy of both an &sk_buff and its data and while doing so ++ * allocate additional space. ++ * ++ * This is used when the caller wishes to modify the data and needs a ++ * private copy of the data to alter as well as more space for new fields. ++ * Returns %NULL on failure or the pointer to the buffer ++ * on success. The returned buffer has a reference count of 1. ++ * ++ * You must pass %GFP_ATOMIC as the allocation priority if this function ++ * is called from an interrupt. ++ */ ++struct sk_buff *skb_copy_expand(const struct sk_buff *skb, ++ int newheadroom, int newtailroom, ++ gfp_t gfp_mask) ++{ ++ /* ++ * Allocate the copy buffer ++ */ ++ struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom, ++ gfp_mask, skb_alloc_rx_flag(skb), ++ NUMA_NO_NODE); ++ int oldheadroom = skb_headroom(skb); ++ int head_copy_len, head_copy_off; ++ ++ if (!n) ++ return NULL; ++ ++ skb_reserve(n, newheadroom); ++ ++ /* Set the tail pointer and length */ ++ skb_put(n, skb->len); ++ ++ head_copy_len = oldheadroom; ++ head_copy_off = 0; ++ if (newheadroom <= head_copy_len) ++ head_copy_len = newheadroom; ++ else ++ head_copy_off = newheadroom - head_copy_len; ++ ++ /* Copy the linear header and data. */ ++ if (skb_copy_bits(skb, -head_copy_len, n->head + head_copy_off, ++ skb->len + head_copy_len)) ++ BUG(); ++ ++ copy_skb_header(n, skb); ++ ++ skb_headers_offset_update(n, newheadroom - oldheadroom); ++ ++ return n; ++} ++EXPORT_SYMBOL(skb_copy_expand); ++ ++/** ++ * skb_pad - zero pad the tail of an skb ++ * @skb: buffer to pad ++ * @pad: space to pad ++ * ++ * Ensure that a buffer is followed by a padding area that is zero ++ * filled. Used by network drivers which may DMA or transfer data ++ * beyond the buffer end onto the wire. ++ * ++ * May return error in out of memory cases. The skb is freed on error. ++ */ ++ ++int skb_pad(struct sk_buff *skb, int pad) ++{ ++ int err; ++ int ntail; ++ ++ /* If the skbuff is non linear tailroom is always zero.. */ ++ if (!skb_cloned(skb) && skb_tailroom(skb) >= pad) { ++ memset(skb->data+skb->len, 0, pad); ++ return 0; ++ } ++ ++ ntail = skb->data_len + pad - (skb->end - skb->tail); ++ if (likely(skb_cloned(skb) || ntail > 0)) { ++ err = pskb_expand_head(skb, 0, ntail, GFP_ATOMIC); ++ if (unlikely(err)) ++ goto free_skb; ++ } ++ ++ /* FIXME: The use of this function with non-linear skb's really needs ++ * to be audited. ++ */ ++ err = skb_linearize(skb); ++ if (unlikely(err)) ++ goto free_skb; ++ ++ memset(skb->data + skb->len, 0, pad); ++ return 0; ++ ++free_skb: ++ kfree_skb(skb); ++ return err; ++} ++EXPORT_SYMBOL(skb_pad); ++ ++/** ++ * pskb_put - add data to the tail of a potentially fragmented buffer ++ * @skb: start of the buffer to use ++ * @tail: tail fragment of the buffer to use ++ * @len: amount of data to add ++ * ++ * This function extends the used data area of the potentially ++ * fragmented buffer. @tail must be the last fragment of @skb -- or ++ * @skb itself. If this would exceed the total buffer size the kernel ++ * will panic. A pointer to the first byte of the extra data is ++ * returned. ++ */ ++ ++unsigned char *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len) ++{ ++ if (tail != skb) { ++ skb->data_len += len; ++ skb->len += len; ++ } ++ return skb_put(tail, len); ++} ++EXPORT_SYMBOL_GPL(pskb_put); ++ ++/** ++ * skb_put - add data to a buffer ++ * @skb: buffer to use ++ * @len: amount of data to add ++ * ++ * This function extends the used data area of the buffer. If this would ++ * exceed the total buffer size the kernel will panic. A pointer to the ++ * first byte of the extra data is returned. ++ */ ++unsigned char *skb_put(struct sk_buff *skb, unsigned int len) ++{ ++ unsigned char *tmp = skb_tail_pointer(skb); ++ SKB_LINEAR_ASSERT(skb); ++ skb->tail += len; ++ skb->len += len; ++ if (unlikely(skb->tail > skb->end)) ++ skb_over_panic(skb, len, __builtin_return_address(0)); ++ return tmp; ++} ++EXPORT_SYMBOL(skb_put); ++ ++/** ++ * skb_push - add data to the start of a buffer ++ * @skb: buffer to use ++ * @len: amount of data to add ++ * ++ * This function extends the used data area of the buffer at the buffer ++ * start. If this would exceed the total buffer headroom the kernel will ++ * panic. A pointer to the first byte of the extra data is returned. ++ */ ++unsigned char *skb_push(struct sk_buff *skb, unsigned int len) ++{ ++ skb->data -= len; ++ skb->len += len; ++ if (unlikely(skb->datahead)) ++ skb_under_panic(skb, len, __builtin_return_address(0)); ++ return skb->data; ++} ++EXPORT_SYMBOL(skb_push); ++ ++/** ++ * skb_pull - remove data from the start of a buffer ++ * @skb: buffer to use ++ * @len: amount of data to remove ++ * ++ * This function removes data from the start of a buffer, returning ++ * the memory to the headroom. A pointer to the next data in the buffer ++ * is returned. Once the data has been pulled future pushes will overwrite ++ * the old data. ++ */ ++unsigned char *skb_pull(struct sk_buff *skb, unsigned int len) ++{ ++ return skb_pull_inline(skb, len); ++} ++EXPORT_SYMBOL(skb_pull); ++ ++/** ++ * skb_trim - remove end from a buffer ++ * @skb: buffer to alter ++ * @len: new length ++ * ++ * Cut the length of a buffer down by removing data from the tail. If ++ * the buffer is already under the length specified it is not modified. ++ * The skb must be linear. ++ */ ++void skb_trim(struct sk_buff *skb, unsigned int len) ++{ ++ if (skb->len > len) ++ __skb_trim(skb, len); ++} ++EXPORT_SYMBOL(skb_trim); ++ ++/* Trims skb to length len. It can change skb pointers. ++ */ ++ ++int ___pskb_trim(struct sk_buff *skb, unsigned int len) ++{ ++ struct sk_buff **fragp; ++ struct sk_buff *frag; ++ int offset = skb_headlen(skb); ++ int nfrags = skb_shinfo(skb)->nr_frags; ++ int i; ++ int err; ++ ++ if (skb_cloned(skb) && ++ unlikely((err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))) ++ return err; ++ ++ i = 0; ++ if (offset >= len) ++ goto drop_pages; ++ ++ for (; i < nfrags; i++) { ++ int end = offset + skb_frag_size(&skb_shinfo(skb)->frags[i]); ++ ++ if (end < len) { ++ offset = end; ++ continue; ++ } ++ ++ skb_frag_size_set(&skb_shinfo(skb)->frags[i++], len - offset); ++ ++drop_pages: ++ skb_shinfo(skb)->nr_frags = i; ++ ++ for (; i < nfrags; i++) ++ skb_frag_unref(skb, i); ++ ++ if (skb_has_frag_list(skb)) ++ skb_drop_fraglist(skb); ++ goto done; ++ } ++ ++ for (fragp = &skb_shinfo(skb)->frag_list; (frag = *fragp); ++ fragp = &frag->next) { ++ int end = offset + frag->len; ++ ++ if (skb_shared(frag)) { ++ struct sk_buff *nfrag; ++ ++ nfrag = skb_clone(frag, GFP_ATOMIC); ++ if (unlikely(!nfrag)) ++ return -ENOMEM; ++ ++ nfrag->next = frag->next; ++ consume_skb(frag); ++ frag = nfrag; ++ *fragp = frag; ++ } ++ ++ if (end < len) { ++ offset = end; ++ continue; ++ } ++ ++ if (end > len && ++ unlikely((err = pskb_trim(frag, len - offset)))) ++ return err; ++ ++ if (frag->next) ++ skb_drop_list(&frag->next); ++ break; ++ } ++ ++done: ++ if (len > skb_headlen(skb)) { ++ skb->data_len -= skb->len - len; ++ skb->len = len; ++ } else { ++ skb->len = len; ++ skb->data_len = 0; ++ skb_set_tail_pointer(skb, len); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(___pskb_trim); ++ ++/** ++ * __pskb_pull_tail - advance tail of skb header ++ * @skb: buffer to reallocate ++ * @delta: number of bytes to advance tail ++ * ++ * The function makes a sense only on a fragmented &sk_buff, ++ * it expands header moving its tail forward and copying necessary ++ * data from fragmented part. ++ * ++ * &sk_buff MUST have reference count of 1. ++ * ++ * Returns %NULL (and &sk_buff does not change) if pull failed ++ * or value of new tail of skb in the case of success. ++ * ++ * All the pointers pointing into skb header may change and must be ++ * reloaded after call to this function. ++ */ ++ ++/* Moves tail of skb head forward, copying data from fragmented part, ++ * when it is necessary. ++ * 1. It may fail due to malloc failure. ++ * 2. It may change skb pointers. ++ * ++ * It is pretty complicated. Luckily, it is called only in exceptional cases. ++ */ ++unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta) ++{ ++ /* If skb has not enough free space at tail, get new one ++ * plus 128 bytes for future expansions. If we have enough ++ * room at tail, reallocate without expansion only if skb is cloned. ++ */ ++ int i, k, eat = (skb->tail + delta) - skb->end; ++ ++ if (eat > 0 || skb_cloned(skb)) { ++ if (pskb_expand_head(skb, 0, eat > 0 ? eat + 128 : 0, ++ GFP_ATOMIC)) ++ return NULL; ++ } ++ ++ if (skb_copy_bits(skb, skb_headlen(skb), skb_tail_pointer(skb), delta)) ++ BUG(); ++ ++ /* Optimization: no fragments, no reasons to preestimate ++ * size of pulled pages. Superb. ++ */ ++ if (!skb_has_frag_list(skb)) ++ goto pull_pages; ++ ++ /* Estimate size of pulled pages. */ ++ eat = delta; ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ int size = skb_frag_size(&skb_shinfo(skb)->frags[i]); ++ ++ if (size >= eat) ++ goto pull_pages; ++ eat -= size; ++ } ++ ++ /* If we need update frag list, we are in troubles. ++ * Certainly, it possible to add an offset to skb data, ++ * but taking into account that pulling is expected to ++ * be very rare operation, it is worth to fight against ++ * further bloating skb head and crucify ourselves here instead. ++ * Pure masohism, indeed. 8)8) ++ */ ++ if (eat) { ++ struct sk_buff *list = skb_shinfo(skb)->frag_list; ++ struct sk_buff *clone = NULL; ++ struct sk_buff *insp = NULL; ++ ++ do { ++ BUG_ON(!list); ++ ++ if (list->len <= eat) { ++ /* Eaten as whole. */ ++ eat -= list->len; ++ list = list->next; ++ insp = list; ++ } else { ++ /* Eaten partially. */ ++ ++ if (skb_shared(list)) { ++ /* Sucks! We need to fork list. :-( */ ++ clone = skb_clone(list, GFP_ATOMIC); ++ if (!clone) ++ return NULL; ++ insp = list->next; ++ list = clone; ++ } else { ++ /* This may be pulled without ++ * problems. */ ++ insp = list; ++ } ++ if (!pskb_pull(list, eat)) { ++ kfree_skb(clone); ++ return NULL; ++ } ++ break; ++ } ++ } while (eat); ++ ++ /* Free pulled out fragments. */ ++ while ((list = skb_shinfo(skb)->frag_list) != insp) { ++ skb_shinfo(skb)->frag_list = list->next; ++ kfree_skb(list); ++ } ++ /* And insert new clone at head. */ ++ if (clone) { ++ clone->next = list; ++ skb_shinfo(skb)->frag_list = clone; ++ } ++ } ++ /* Success! Now we may commit changes to skb data. */ ++ ++pull_pages: ++ eat = delta; ++ k = 0; ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ int size = skb_frag_size(&skb_shinfo(skb)->frags[i]); ++ ++ if (size <= eat) { ++ skb_frag_unref(skb, i); ++ eat -= size; ++ } else { ++ skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i]; ++ if (eat) { ++ skb_shinfo(skb)->frags[k].page_offset += eat; ++ skb_frag_size_sub(&skb_shinfo(skb)->frags[k], eat); ++ eat = 0; ++ } ++ k++; ++ } ++ } ++ skb_shinfo(skb)->nr_frags = k; ++ ++ skb->tail += delta; ++ skb->data_len -= delta; ++ ++ return skb_tail_pointer(skb); ++} ++EXPORT_SYMBOL(__pskb_pull_tail); ++ ++/** ++ * skb_copy_bits - copy bits from skb to kernel buffer ++ * @skb: source skb ++ * @offset: offset in source ++ * @to: destination buffer ++ * @len: number of bytes to copy ++ * ++ * Copy the specified number of bytes from the source skb to the ++ * destination buffer. ++ * ++ * CAUTION ! : ++ * If its prototype is ever changed, ++ * check arch/{*}/net/{*}.S files, ++ * since it is called from BPF assembly code. ++ */ ++int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len) ++{ ++ int start = skb_headlen(skb); ++ struct sk_buff *frag_iter; ++ int i, copy; ++ ++ if (offset > (int)skb->len - len) ++ goto fault; ++ ++ /* Copy header. */ ++ if ((copy = start - offset) > 0) { ++ if (copy > len) ++ copy = len; ++ skb_copy_from_linear_data_offset(skb, offset, to, copy); ++ if ((len -= copy) == 0) ++ return 0; ++ offset += copy; ++ to += copy; ++ } ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ int end; ++ skb_frag_t *f = &skb_shinfo(skb)->frags[i]; ++ ++ WARN_ON(start > offset + len); ++ ++ end = start + skb_frag_size(f); ++ if ((copy = end - offset) > 0) { ++ u8 *vaddr; ++ ++ if (copy > len) ++ copy = len; ++ ++ vaddr = kmap_atomic(skb_frag_page(f)); ++ memcpy(to, ++ vaddr + f->page_offset + offset - start, ++ copy); ++ kunmap_atomic(vaddr); ++ ++ if ((len -= copy) == 0) ++ return 0; ++ offset += copy; ++ to += copy; ++ } ++ start = end; ++ } ++ ++ skb_walk_frags(skb, frag_iter) { ++ int end; ++ ++ WARN_ON(start > offset + len); ++ ++ end = start + frag_iter->len; ++ if ((copy = end - offset) > 0) { ++ if (copy > len) ++ copy = len; ++ if (skb_copy_bits(frag_iter, offset - start, to, copy)) ++ goto fault; ++ if ((len -= copy) == 0) ++ return 0; ++ offset += copy; ++ to += copy; ++ } ++ start = end; ++ } ++ ++ if (!len) ++ return 0; ++ ++fault: ++ return -EFAULT; ++} ++EXPORT_SYMBOL(skb_copy_bits); ++ ++/* ++ * Callback from splice_to_pipe(), if we need to release some pages ++ * at the end of the spd in case we error'ed out in filling the pipe. ++ */ ++static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i) ++{ ++ put_page(spd->pages[i]); ++} ++ ++static struct page *linear_to_page(struct page *page, unsigned int *len, ++ unsigned int *offset, ++ struct sock *sk) ++{ ++ struct page_frag *pfrag = sk_page_frag(sk); ++ ++ if (!sk_page_frag_refill(sk, pfrag)) ++ return NULL; ++ ++ *len = min_t(unsigned int, *len, pfrag->size - pfrag->offset); ++ ++ memcpy(page_address(pfrag->page) + pfrag->offset, ++ page_address(page) + *offset, *len); ++ *offset = pfrag->offset; ++ pfrag->offset += *len; ++ ++ return pfrag->page; ++} ++ ++static bool spd_can_coalesce(const struct splice_pipe_desc *spd, ++ struct page *page, ++ unsigned int offset) ++{ ++ return spd->nr_pages && ++ spd->pages[spd->nr_pages - 1] == page && ++ (spd->partial[spd->nr_pages - 1].offset + ++ spd->partial[spd->nr_pages - 1].len == offset); ++} ++ ++/* ++ * Fill page/offset/length into spd, if it can hold more pages. ++ */ ++static bool spd_fill_page(struct splice_pipe_desc *spd, ++ struct pipe_inode_info *pipe, struct page *page, ++ unsigned int *len, unsigned int offset, ++ bool linear, ++ struct sock *sk) ++{ ++ if (unlikely(spd->nr_pages == MAX_SKB_FRAGS)) ++ return true; ++ ++ if (linear) { ++ page = linear_to_page(page, len, &offset, sk); ++ if (!page) ++ return true; ++ } ++ if (spd_can_coalesce(spd, page, offset)) { ++ spd->partial[spd->nr_pages - 1].len += *len; ++ return false; ++ } ++ get_page(page); ++ spd->pages[spd->nr_pages] = page; ++ spd->partial[spd->nr_pages].len = *len; ++ spd->partial[spd->nr_pages].offset = offset; ++ spd->nr_pages++; ++ ++ return false; ++} ++ ++static bool __splice_segment(struct page *page, unsigned int poff, ++ unsigned int plen, unsigned int *off, ++ unsigned int *len, ++ struct splice_pipe_desc *spd, bool linear, ++ struct sock *sk, ++ struct pipe_inode_info *pipe) ++{ ++ if (!*len) ++ return true; ++ ++ /* skip this segment if already processed */ ++ if (*off >= plen) { ++ *off -= plen; ++ return false; ++ } ++ ++ /* ignore any bits we already processed */ ++ poff += *off; ++ plen -= *off; ++ *off = 0; ++ ++ do { ++ unsigned int flen = min(*len, plen); ++ ++ if (spd_fill_page(spd, pipe, page, &flen, poff, ++ linear, sk)) ++ return true; ++ poff += flen; ++ plen -= flen; ++ *len -= flen; ++ } while (*len && plen); ++ ++ return false; ++} ++ ++/* ++ * Map linear and fragment data from the skb to spd. It reports true if the ++ * pipe is full or if we already spliced the requested length. ++ */ ++static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe, ++ unsigned int *offset, unsigned int *len, ++ struct splice_pipe_desc *spd, struct sock *sk) ++{ ++ int seg; ++ ++ /* map the linear part : ++ * If skb->head_frag is set, this 'linear' part is backed by a ++ * fragment, and if the head is not shared with any clones then ++ * we can avoid a copy since we own the head portion of this page. ++ */ ++ if (__splice_segment(virt_to_page(skb->data), ++ (unsigned long) skb->data & (PAGE_SIZE - 1), ++ skb_headlen(skb), ++ offset, len, spd, ++ skb_head_is_locked(skb), ++ sk, pipe)) ++ return true; ++ ++ /* ++ * then map the fragments ++ */ ++ for (seg = 0; seg < skb_shinfo(skb)->nr_frags; seg++) { ++ const skb_frag_t *f = &skb_shinfo(skb)->frags[seg]; ++ ++ if (__splice_segment(skb_frag_page(f), ++ f->page_offset, skb_frag_size(f), ++ offset, len, spd, false, sk, pipe)) ++ return true; ++ } ++ ++ return false; ++} ++ ++/* ++ * Map data from the skb to a pipe. Should handle both the linear part, ++ * the fragments, and the frag list. It does NOT handle frag lists within ++ * the frag list, if such a thing exists. We'd probably need to recurse to ++ * handle that cleanly. ++ */ ++int skb_splice_bits(struct sk_buff *skb, unsigned int offset, ++ struct pipe_inode_info *pipe, unsigned int tlen, ++ unsigned int flags) ++{ ++ struct partial_page partial[MAX_SKB_FRAGS]; ++ struct page *pages[MAX_SKB_FRAGS]; ++ struct splice_pipe_desc spd = { ++ .pages = pages, ++ .partial = partial, ++ .nr_pages_max = MAX_SKB_FRAGS, ++ .flags = flags, ++ .ops = &nosteal_pipe_buf_ops, ++ .spd_release = sock_spd_release, ++ }; ++ struct sk_buff *frag_iter; ++ struct sock *sk = skb->sk; ++ int ret = 0; ++ ++ /* ++ * __skb_splice_bits() only fails if the output has no room left, ++ * so no point in going over the frag_list for the error case. ++ */ ++ if (__skb_splice_bits(skb, pipe, &offset, &tlen, &spd, sk)) ++ goto done; ++ else if (!tlen) ++ goto done; ++ ++ /* ++ * now see if we have a frag_list to map ++ */ ++ skb_walk_frags(skb, frag_iter) { ++ if (!tlen) ++ break; ++ if (__skb_splice_bits(frag_iter, pipe, &offset, &tlen, &spd, sk)) ++ break; ++ } ++ ++done: ++ if (spd.nr_pages) { ++ /* ++ * Drop the socket lock, otherwise we have reverse ++ * locking dependencies between sk_lock and i_mutex ++ * here as compared to sendfile(). We enter here ++ * with the socket lock held, and splice_to_pipe() will ++ * grab the pipe inode lock. For sendfile() emulation, ++ * we call into ->sendpage() with the i_mutex lock held ++ * and networking will grab the socket lock. ++ */ ++ release_sock(sk); ++ ret = splice_to_pipe(pipe, &spd); ++ lock_sock(sk); ++ } ++ ++ return ret; ++} ++ ++/** ++ * skb_store_bits - store bits from kernel buffer to skb ++ * @skb: destination buffer ++ * @offset: offset in destination ++ * @from: source buffer ++ * @len: number of bytes to copy ++ * ++ * Copy the specified number of bytes from the source buffer to the ++ * destination skb. This function handles all the messy bits of ++ * traversing fragment lists and such. ++ */ ++ ++int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len) ++{ ++ int start = skb_headlen(skb); ++ struct sk_buff *frag_iter; ++ int i, copy; ++ ++ if (offset > (int)skb->len - len) ++ goto fault; ++ ++ if ((copy = start - offset) > 0) { ++ if (copy > len) ++ copy = len; ++ skb_copy_to_linear_data_offset(skb, offset, from, copy); ++ if ((len -= copy) == 0) ++ return 0; ++ offset += copy; ++ from += copy; ++ } ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ int end; ++ ++ WARN_ON(start > offset + len); ++ ++ end = start + skb_frag_size(frag); ++ if ((copy = end - offset) > 0) { ++ u8 *vaddr; ++ ++ if (copy > len) ++ copy = len; ++ ++ vaddr = kmap_atomic(skb_frag_page(frag)); ++ memcpy(vaddr + frag->page_offset + offset - start, ++ from, copy); ++ kunmap_atomic(vaddr); ++ ++ if ((len -= copy) == 0) ++ return 0; ++ offset += copy; ++ from += copy; ++ } ++ start = end; ++ } ++ ++ skb_walk_frags(skb, frag_iter) { ++ int end; ++ ++ WARN_ON(start > offset + len); ++ ++ end = start + frag_iter->len; ++ if ((copy = end - offset) > 0) { ++ if (copy > len) ++ copy = len; ++ if (skb_store_bits(frag_iter, offset - start, ++ from, copy)) ++ goto fault; ++ if ((len -= copy) == 0) ++ return 0; ++ offset += copy; ++ from += copy; ++ } ++ start = end; ++ } ++ if (!len) ++ return 0; ++ ++fault: ++ return -EFAULT; ++} ++EXPORT_SYMBOL(skb_store_bits); ++ ++/* Checksum skb data. */ ++__wsum __skb_checksum(const struct sk_buff *skb, int offset, int len, ++ __wsum csum, const struct skb_checksum_ops *ops) ++{ ++ int start = skb_headlen(skb); ++ int i, copy = start - offset; ++ struct sk_buff *frag_iter; ++ int pos = 0; ++ ++ /* Checksum header. */ ++ if (copy > 0) { ++ if (copy > len) ++ copy = len; ++ csum = ops->update(skb->data + offset, copy, csum); ++ if ((len -= copy) == 0) ++ return csum; ++ offset += copy; ++ pos = copy; ++ } ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ int end; ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ ++ WARN_ON(start > offset + len); ++ ++ end = start + skb_frag_size(frag); ++ if ((copy = end - offset) > 0) { ++ __wsum csum2; ++ u8 *vaddr; ++ ++ if (copy > len) ++ copy = len; ++ vaddr = kmap_atomic(skb_frag_page(frag)); ++ csum2 = ops->update(vaddr + frag->page_offset + ++ offset - start, copy, 0); ++ kunmap_atomic(vaddr); ++ csum = ops->combine(csum, csum2, pos, copy); ++ if (!(len -= copy)) ++ return csum; ++ offset += copy; ++ pos += copy; ++ } ++ start = end; ++ } ++ ++ skb_walk_frags(skb, frag_iter) { ++ int end; ++ ++ WARN_ON(start > offset + len); ++ ++ end = start + frag_iter->len; ++ if ((copy = end - offset) > 0) { ++ __wsum csum2; ++ if (copy > len) ++ copy = len; ++ csum2 = __skb_checksum(frag_iter, offset - start, ++ copy, 0, ops); ++ csum = ops->combine(csum, csum2, pos, copy); ++ if ((len -= copy) == 0) ++ return csum; ++ offset += copy; ++ pos += copy; ++ } ++ start = end; ++ } ++ BUG_ON(len); ++ ++ return csum; ++} ++EXPORT_SYMBOL(__skb_checksum); ++ ++__wsum skb_checksum(const struct sk_buff *skb, int offset, ++ int len, __wsum csum) ++{ ++ const struct skb_checksum_ops ops = { ++ .update = csum_partial_ext, ++ .combine = csum_block_add_ext, ++ }; ++ ++ return __skb_checksum(skb, offset, len, csum, &ops); ++} ++EXPORT_SYMBOL(skb_checksum); ++ ++/* Both of above in one bottle. */ ++ ++__wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, ++ u8 *to, int len, __wsum csum) ++{ ++ int start = skb_headlen(skb); ++ int i, copy = start - offset; ++ struct sk_buff *frag_iter; ++ int pos = 0; ++ ++ /* Copy header. */ ++ if (copy > 0) { ++ if (copy > len) ++ copy = len; ++ csum = csum_partial_copy_nocheck(skb->data + offset, to, ++ copy, csum); ++ if ((len -= copy) == 0) ++ return csum; ++ offset += copy; ++ to += copy; ++ pos = copy; ++ } ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ int end; ++ ++ WARN_ON(start > offset + len); ++ ++ end = start + skb_frag_size(&skb_shinfo(skb)->frags[i]); ++ if ((copy = end - offset) > 0) { ++ __wsum csum2; ++ u8 *vaddr; ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ ++ if (copy > len) ++ copy = len; ++ vaddr = kmap_atomic(skb_frag_page(frag)); ++ csum2 = csum_partial_copy_nocheck(vaddr + ++ frag->page_offset + ++ offset - start, to, ++ copy, 0); ++ kunmap_atomic(vaddr); ++ csum = csum_block_add(csum, csum2, pos); ++ if (!(len -= copy)) ++ return csum; ++ offset += copy; ++ to += copy; ++ pos += copy; ++ } ++ start = end; ++ } ++ ++ skb_walk_frags(skb, frag_iter) { ++ __wsum csum2; ++ int end; ++ ++ WARN_ON(start > offset + len); ++ ++ end = start + frag_iter->len; ++ if ((copy = end - offset) > 0) { ++ if (copy > len) ++ copy = len; ++ csum2 = skb_copy_and_csum_bits(frag_iter, ++ offset - start, ++ to, copy, 0); ++ csum = csum_block_add(csum, csum2, pos); ++ if ((len -= copy) == 0) ++ return csum; ++ offset += copy; ++ to += copy; ++ pos += copy; ++ } ++ start = end; ++ } ++ BUG_ON(len); ++ return csum; ++} ++EXPORT_SYMBOL(skb_copy_and_csum_bits); ++ ++ /** ++ * skb_zerocopy_headlen - Calculate headroom needed for skb_zerocopy() ++ * @from: source buffer ++ * ++ * Calculates the amount of linear headroom needed in the 'to' skb passed ++ * into skb_zerocopy(). ++ */ ++unsigned int ++skb_zerocopy_headlen(const struct sk_buff *from) ++{ ++ unsigned int hlen = 0; ++ ++ if (!from->head_frag || ++ skb_headlen(from) < L1_CACHE_BYTES || ++ skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS) ++ hlen = skb_headlen(from); ++ ++ if (skb_has_frag_list(from)) ++ hlen = from->len; ++ ++ return hlen; ++} ++EXPORT_SYMBOL_GPL(skb_zerocopy_headlen); ++ ++/** ++ * skb_zerocopy - Zero copy skb to skb ++ * @to: destination buffer ++ * @from: source buffer ++ * @len: number of bytes to copy from source buffer ++ * @hlen: size of linear headroom in destination buffer ++ * ++ * Copies up to `len` bytes from `from` to `to` by creating references ++ * to the frags in the source buffer. ++ * ++ * The `hlen` as calculated by skb_zerocopy_headlen() specifies the ++ * headroom in the `to` buffer. ++ * ++ * Return value: ++ * 0: everything is OK ++ * -ENOMEM: couldn't orphan frags of @from due to lack of memory ++ * -EFAULT: skb_copy_bits() found some problem with skb geometry ++ */ ++int ++skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen) ++{ ++ int i, j = 0; ++ int plen = 0; /* length of skb->head fragment */ ++ int ret; ++ struct page *page; ++ unsigned int offset; ++ ++ BUG_ON(!from->head_frag && !hlen); ++ ++ /* dont bother with small payloads */ ++ if (len <= skb_tailroom(to)) ++ return skb_copy_bits(from, 0, skb_put(to, len), len); ++ ++ if (hlen) { ++ ret = skb_copy_bits(from, 0, skb_put(to, hlen), hlen); ++ if (unlikely(ret)) ++ return ret; ++ len -= hlen; ++ } else { ++ plen = min_t(int, skb_headlen(from), len); ++ if (plen) { ++ page = virt_to_head_page(from->head); ++ offset = from->data - (unsigned char *)page_address(page); ++ __skb_fill_page_desc(to, 0, page, offset, plen); ++ get_page(page); ++ j = 1; ++ len -= plen; ++ } ++ } ++ ++ to->truesize += len + plen; ++ to->len += len + plen; ++ to->data_len += len + plen; ++ ++ if (unlikely(skb_orphan_frags(from, GFP_ATOMIC))) { ++ skb_tx_error(from); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { ++ if (!len) ++ break; ++ skb_shinfo(to)->frags[j] = skb_shinfo(from)->frags[i]; ++ skb_shinfo(to)->frags[j].size = min_t(int, skb_shinfo(to)->frags[j].size, len); ++ len -= skb_shinfo(to)->frags[j].size; ++ skb_frag_ref(to, j); ++ j++; ++ } ++ skb_shinfo(to)->nr_frags = j; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(skb_zerocopy); ++ ++void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) ++{ ++ __wsum csum; ++ long csstart; ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ csstart = skb_checksum_start_offset(skb); ++ else ++ csstart = skb_headlen(skb); ++ ++ BUG_ON(csstart > skb_headlen(skb)); ++ ++ skb_copy_from_linear_data(skb, to, csstart); ++ ++ csum = 0; ++ if (csstart != skb->len) ++ csum = skb_copy_and_csum_bits(skb, csstart, to + csstart, ++ skb->len - csstart, 0); ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ long csstuff = csstart + skb->csum_offset; ++ ++ *((__sum16 *)(to + csstuff)) = csum_fold(csum); ++ } ++} ++EXPORT_SYMBOL(skb_copy_and_csum_dev); ++ ++/** ++ * skb_dequeue - remove from the head of the queue ++ * @list: list to dequeue from ++ * ++ * Remove the head of the list. The list lock is taken so the function ++ * may be used safely with other locking list functions. The head item is ++ * returned or %NULL if the list is empty. ++ */ ++ ++struct sk_buff *skb_dequeue(struct sk_buff_head *list) ++{ ++ unsigned long flags; ++ struct sk_buff *result; ++ ++ spin_lock_irqsave(&list->lock, flags); ++ result = __skb_dequeue(list); ++ spin_unlock_irqrestore(&list->lock, flags); ++ return result; ++} ++EXPORT_SYMBOL(skb_dequeue); ++ ++/** ++ * skb_dequeue_tail - remove from the tail of the queue ++ * @list: list to dequeue from ++ * ++ * Remove the tail of the list. The list lock is taken so the function ++ * may be used safely with other locking list functions. The tail item is ++ * returned or %NULL if the list is empty. ++ */ ++struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list) ++{ ++ unsigned long flags; ++ struct sk_buff *result; ++ ++ spin_lock_irqsave(&list->lock, flags); ++ result = __skb_dequeue_tail(list); ++ spin_unlock_irqrestore(&list->lock, flags); ++ return result; ++} ++EXPORT_SYMBOL(skb_dequeue_tail); ++ ++/** ++ * skb_queue_purge - empty a list ++ * @list: list to empty ++ * ++ * Delete all buffers on an &sk_buff list. Each buffer is removed from ++ * the list and one reference dropped. This function takes the list ++ * lock and is atomic with respect to other list locking functions. ++ */ ++void skb_queue_purge(struct sk_buff_head *list) ++{ ++ struct sk_buff *skb; ++ while ((skb = skb_dequeue(list)) != NULL) ++ kfree_skb(skb); ++} ++EXPORT_SYMBOL(skb_queue_purge); ++ ++/** ++ * skb_queue_head - queue a buffer at the list head ++ * @list: list to use ++ * @newsk: buffer to queue ++ * ++ * Queue a buffer at the start of the list. This function takes the ++ * list lock and can be used safely with other locking &sk_buff functions ++ * safely. ++ * ++ * A buffer cannot be placed on two lists at the same time. ++ */ ++void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&list->lock, flags); ++ __skb_queue_head(list, newsk); ++ spin_unlock_irqrestore(&list->lock, flags); ++} ++EXPORT_SYMBOL(skb_queue_head); ++ ++/** ++ * skb_queue_tail - queue a buffer at the list tail ++ * @list: list to use ++ * @newsk: buffer to queue ++ * ++ * Queue a buffer at the tail of the list. This function takes the ++ * list lock and can be used safely with other locking &sk_buff functions ++ * safely. ++ * ++ * A buffer cannot be placed on two lists at the same time. ++ */ ++void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&list->lock, flags); ++ __skb_queue_tail(list, newsk); ++ spin_unlock_irqrestore(&list->lock, flags); ++} ++EXPORT_SYMBOL(skb_queue_tail); ++ ++/** ++ * skb_unlink - remove a buffer from a list ++ * @skb: buffer to remove ++ * @list: list to use ++ * ++ * Remove a packet from a list. The list locks are taken and this ++ * function is atomic with respect to other list locked calls ++ * ++ * You must know what list the SKB is on. ++ */ ++void skb_unlink(struct sk_buff *skb, struct sk_buff_head *list) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&list->lock, flags); ++ __skb_unlink(skb, list); ++ spin_unlock_irqrestore(&list->lock, flags); ++} ++EXPORT_SYMBOL(skb_unlink); ++ ++/** ++ * skb_append - append a buffer ++ * @old: buffer to insert after ++ * @newsk: buffer to insert ++ * @list: list to use ++ * ++ * Place a packet after a given packet in a list. The list locks are taken ++ * and this function is atomic with respect to other list locked calls. ++ * A buffer cannot be placed on two lists at the same time. ++ */ ++void skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&list->lock, flags); ++ __skb_queue_after(list, old, newsk); ++ spin_unlock_irqrestore(&list->lock, flags); ++} ++EXPORT_SYMBOL(skb_append); ++ ++/** ++ * skb_insert - insert a buffer ++ * @old: buffer to insert before ++ * @newsk: buffer to insert ++ * @list: list to use ++ * ++ * Place a packet before a given packet in a list. The list locks are ++ * taken and this function is atomic with respect to other list locked ++ * calls. ++ * ++ * A buffer cannot be placed on two lists at the same time. ++ */ ++void skb_insert(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&list->lock, flags); ++ __skb_insert(newsk, old->prev, old, list); ++ spin_unlock_irqrestore(&list->lock, flags); ++} ++EXPORT_SYMBOL(skb_insert); ++ ++static inline void skb_split_inside_header(struct sk_buff *skb, ++ struct sk_buff* skb1, ++ const u32 len, const int pos) ++{ ++ int i; ++ ++ skb_copy_from_linear_data_offset(skb, len, skb_put(skb1, pos - len), ++ pos - len); ++ /* And move data appendix as is. */ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) ++ skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i]; ++ ++ skb_shinfo(skb1)->nr_frags = skb_shinfo(skb)->nr_frags; ++ skb_shinfo(skb)->nr_frags = 0; ++ skb1->data_len = skb->data_len; ++ skb1->len += skb1->data_len; ++ skb->data_len = 0; ++ skb->len = len; ++ skb_set_tail_pointer(skb, len); ++} ++ ++static inline void skb_split_no_header(struct sk_buff *skb, ++ struct sk_buff* skb1, ++ const u32 len, int pos) ++{ ++ int i, k = 0; ++ const int nfrags = skb_shinfo(skb)->nr_frags; ++ ++ skb_shinfo(skb)->nr_frags = 0; ++ skb1->len = skb1->data_len = skb->len - len; ++ skb->len = len; ++ skb->data_len = len - pos; ++ ++ for (i = 0; i < nfrags; i++) { ++ int size = skb_frag_size(&skb_shinfo(skb)->frags[i]); ++ ++ if (pos + size > len) { ++ skb_shinfo(skb1)->frags[k] = skb_shinfo(skb)->frags[i]; ++ ++ if (pos < len) { ++ /* Split frag. ++ * We have two variants in this case: ++ * 1. Move all the frag to the second ++ * part, if it is possible. F.e. ++ * this approach is mandatory for TUX, ++ * where splitting is expensive. ++ * 2. Split is accurately. We make this. ++ */ ++ skb_frag_ref(skb, i); ++ skb_shinfo(skb1)->frags[0].page_offset += len - pos; ++ skb_frag_size_sub(&skb_shinfo(skb1)->frags[0], len - pos); ++ skb_frag_size_set(&skb_shinfo(skb)->frags[i], len - pos); ++ skb_shinfo(skb)->nr_frags++; ++ } ++ k++; ++ } else ++ skb_shinfo(skb)->nr_frags++; ++ pos += size; ++ } ++ skb_shinfo(skb1)->nr_frags = k; ++} ++ ++/** ++ * skb_split - Split fragmented skb to two parts at length len. ++ * @skb: the buffer to split ++ * @skb1: the buffer to receive the second part ++ * @len: new length for skb ++ */ ++void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) ++{ ++ int pos = skb_headlen(skb); ++ ++ skb_shinfo(skb1)->tx_flags = skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; ++ if (len < pos) /* Split line is inside header. */ ++ skb_split_inside_header(skb, skb1, len, pos); ++ else /* Second chunk has no header, nothing to copy. */ ++ skb_split_no_header(skb, skb1, len, pos); ++} ++EXPORT_SYMBOL(skb_split); ++ ++/* Shifting from/to a cloned skb is a no-go. ++ * ++ * Caller cannot keep skb_shinfo related pointers past calling here! ++ */ ++static int skb_prepare_for_shift(struct sk_buff *skb) ++{ ++ return skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC); ++} ++ ++/** ++ * skb_shift - Shifts paged data partially from skb to another ++ * @tgt: buffer into which tail data gets added ++ * @skb: buffer from which the paged data comes from ++ * @shiftlen: shift up to this many bytes ++ * ++ * Attempts to shift up to shiftlen worth of bytes, which may be less than ++ * the length of the skb, from skb to tgt. Returns number bytes shifted. ++ * It's up to caller to free skb if everything was shifted. ++ * ++ * If @tgt runs out of frags, the whole operation is aborted. ++ * ++ * Skb cannot include anything else but paged data while tgt is allowed ++ * to have non-paged data as well. ++ * ++ * TODO: full sized shift could be optimized but that would need ++ * specialized skb free'er to handle frags without up-to-date nr_frags. ++ */ ++int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen) ++{ ++ int from, to, merge, todo; ++ struct skb_frag_struct *fragfrom, *fragto; ++ ++ BUG_ON(shiftlen > skb->len); ++ BUG_ON(skb_headlen(skb)); /* Would corrupt stream */ ++ ++ todo = shiftlen; ++ from = 0; ++ to = skb_shinfo(tgt)->nr_frags; ++ fragfrom = &skb_shinfo(skb)->frags[from]; ++ ++ /* Actual merge is delayed until the point when we know we can ++ * commit all, so that we don't have to undo partial changes ++ */ ++ if (!to || ++ !skb_can_coalesce(tgt, to, skb_frag_page(fragfrom), ++ fragfrom->page_offset)) { ++ merge = -1; ++ } else { ++ merge = to - 1; ++ ++ todo -= skb_frag_size(fragfrom); ++ if (todo < 0) { ++ if (skb_prepare_for_shift(skb) || ++ skb_prepare_for_shift(tgt)) ++ return 0; ++ ++ /* All previous frag pointers might be stale! */ ++ fragfrom = &skb_shinfo(skb)->frags[from]; ++ fragto = &skb_shinfo(tgt)->frags[merge]; ++ ++ skb_frag_size_add(fragto, shiftlen); ++ skb_frag_size_sub(fragfrom, shiftlen); ++ fragfrom->page_offset += shiftlen; ++ ++ goto onlymerged; ++ } ++ ++ from++; ++ } ++ ++ /* Skip full, not-fitting skb to avoid expensive operations */ ++ if ((shiftlen == skb->len) && ++ (skb_shinfo(skb)->nr_frags - from) > (MAX_SKB_FRAGS - to)) ++ return 0; ++ ++ if (skb_prepare_for_shift(skb) || skb_prepare_for_shift(tgt)) ++ return 0; ++ ++ while ((todo > 0) && (from < skb_shinfo(skb)->nr_frags)) { ++ if (to == MAX_SKB_FRAGS) ++ return 0; ++ ++ fragfrom = &skb_shinfo(skb)->frags[from]; ++ fragto = &skb_shinfo(tgt)->frags[to]; ++ ++ if (todo >= skb_frag_size(fragfrom)) { ++ *fragto = *fragfrom; ++ todo -= skb_frag_size(fragfrom); ++ from++; ++ to++; ++ ++ } else { ++ __skb_frag_ref(fragfrom); ++ fragto->page = fragfrom->page; ++ fragto->page_offset = fragfrom->page_offset; ++ skb_frag_size_set(fragto, todo); ++ ++ fragfrom->page_offset += todo; ++ skb_frag_size_sub(fragfrom, todo); ++ todo = 0; ++ ++ to++; ++ break; ++ } ++ } ++ ++ /* Ready to "commit" this state change to tgt */ ++ skb_shinfo(tgt)->nr_frags = to; ++ ++ if (merge >= 0) { ++ fragfrom = &skb_shinfo(skb)->frags[0]; ++ fragto = &skb_shinfo(tgt)->frags[merge]; ++ ++ skb_frag_size_add(fragto, skb_frag_size(fragfrom)); ++ __skb_frag_unref(fragfrom); ++ } ++ ++ /* Reposition in the original skb */ ++ to = 0; ++ while (from < skb_shinfo(skb)->nr_frags) ++ skb_shinfo(skb)->frags[to++] = skb_shinfo(skb)->frags[from++]; ++ skb_shinfo(skb)->nr_frags = to; ++ ++ BUG_ON(todo > 0 && !skb_shinfo(skb)->nr_frags); ++ ++onlymerged: ++ /* Most likely the tgt won't ever need its checksum anymore, skb on ++ * the other hand might need it if it needs to be resent ++ */ ++ tgt->ip_summed = CHECKSUM_PARTIAL; ++ skb->ip_summed = CHECKSUM_PARTIAL; ++ ++ /* Yak, is it really working this way? Some helper please? */ ++ skb->len -= shiftlen; ++ skb->data_len -= shiftlen; ++ skb->truesize -= shiftlen; ++ tgt->len += shiftlen; ++ tgt->data_len += shiftlen; ++ tgt->truesize += shiftlen; ++ ++ return shiftlen; ++} ++ ++/** ++ * skb_prepare_seq_read - Prepare a sequential read of skb data ++ * @skb: the buffer to read ++ * @from: lower offset of data to be read ++ * @to: upper offset of data to be read ++ * @st: state variable ++ * ++ * Initializes the specified state variable. Must be called before ++ * invoking skb_seq_read() for the first time. ++ */ ++void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from, ++ unsigned int to, struct skb_seq_state *st) ++{ ++ st->lower_offset = from; ++ st->upper_offset = to; ++ st->root_skb = st->cur_skb = skb; ++ st->frag_idx = st->stepped_offset = 0; ++ st->frag_data = NULL; ++} ++EXPORT_SYMBOL(skb_prepare_seq_read); ++ ++/** ++ * skb_seq_read - Sequentially read skb data ++ * @consumed: number of bytes consumed by the caller so far ++ * @data: destination pointer for data to be returned ++ * @st: state variable ++ * ++ * Reads a block of skb data at @consumed relative to the ++ * lower offset specified to skb_prepare_seq_read(). Assigns ++ * the head of the data block to @data and returns the length ++ * of the block or 0 if the end of the skb data or the upper ++ * offset has been reached. ++ * ++ * The caller is not required to consume all of the data ++ * returned, i.e. @consumed is typically set to the number ++ * of bytes already consumed and the next call to ++ * skb_seq_read() will return the remaining part of the block. ++ * ++ * Note 1: The size of each block of data returned can be arbitrary, ++ * this limitation is the cost for zerocopy sequential ++ * reads of potentially non linear data. ++ * ++ * Note 2: Fragment lists within fragments are not implemented ++ * at the moment, state->root_skb could be replaced with ++ * a stack for this purpose. ++ */ ++unsigned int skb_seq_read(unsigned int consumed, const u8 **data, ++ struct skb_seq_state *st) ++{ ++ unsigned int block_limit, abs_offset = consumed + st->lower_offset; ++ skb_frag_t *frag; ++ ++ if (unlikely(abs_offset >= st->upper_offset)) { ++ if (st->frag_data) { ++ kunmap_atomic(st->frag_data); ++ st->frag_data = NULL; ++ } ++ return 0; ++ } ++ ++next_skb: ++ block_limit = skb_headlen(st->cur_skb) + st->stepped_offset; ++ ++ if (abs_offset < block_limit && !st->frag_data) { ++ *data = st->cur_skb->data + (abs_offset - st->stepped_offset); ++ return block_limit - abs_offset; ++ } ++ ++ if (st->frag_idx == 0 && !st->frag_data) ++ st->stepped_offset += skb_headlen(st->cur_skb); ++ ++ while (st->frag_idx < skb_shinfo(st->cur_skb)->nr_frags) { ++ frag = &skb_shinfo(st->cur_skb)->frags[st->frag_idx]; ++ block_limit = skb_frag_size(frag) + st->stepped_offset; ++ ++ if (abs_offset < block_limit) { ++ if (!st->frag_data) ++ st->frag_data = kmap_atomic(skb_frag_page(frag)); ++ ++ *data = (u8 *) st->frag_data + frag->page_offset + ++ (abs_offset - st->stepped_offset); ++ ++ return block_limit - abs_offset; ++ } ++ ++ if (st->frag_data) { ++ kunmap_atomic(st->frag_data); ++ st->frag_data = NULL; ++ } ++ ++ st->frag_idx++; ++ st->stepped_offset += skb_frag_size(frag); ++ } ++ ++ if (st->frag_data) { ++ kunmap_atomic(st->frag_data); ++ st->frag_data = NULL; ++ } ++ ++ if (st->root_skb == st->cur_skb && skb_has_frag_list(st->root_skb)) { ++ st->cur_skb = skb_shinfo(st->root_skb)->frag_list; ++ st->frag_idx = 0; ++ goto next_skb; ++ } else if (st->cur_skb->next) { ++ st->cur_skb = st->cur_skb->next; ++ st->frag_idx = 0; ++ goto next_skb; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(skb_seq_read); ++ ++/** ++ * skb_abort_seq_read - Abort a sequential read of skb data ++ * @st: state variable ++ * ++ * Must be called if skb_seq_read() was not called until it ++ * returned 0. ++ */ ++void skb_abort_seq_read(struct skb_seq_state *st) ++{ ++ if (st->frag_data) ++ kunmap_atomic(st->frag_data); ++} ++EXPORT_SYMBOL(skb_abort_seq_read); ++ ++#define TS_SKB_CB(state) ((struct skb_seq_state *) &((state)->cb)) ++ ++static unsigned int skb_ts_get_next_block(unsigned int offset, const u8 **text, ++ struct ts_config *conf, ++ struct ts_state *state) ++{ ++ return skb_seq_read(offset, text, TS_SKB_CB(state)); ++} ++ ++static void skb_ts_finish(struct ts_config *conf, struct ts_state *state) ++{ ++ skb_abort_seq_read(TS_SKB_CB(state)); ++} ++ ++/** ++ * skb_find_text - Find a text pattern in skb data ++ * @skb: the buffer to look in ++ * @from: search offset ++ * @to: search limit ++ * @config: textsearch configuration ++ * @state: uninitialized textsearch state variable ++ * ++ * Finds a pattern in the skb data according to the specified ++ * textsearch configuration. Use textsearch_next() to retrieve ++ * subsequent occurrences of the pattern. Returns the offset ++ * to the first occurrence or UINT_MAX if no match was found. ++ */ ++unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, ++ unsigned int to, struct ts_config *config, ++ struct ts_state *state) ++{ ++ unsigned int ret; ++ ++ config->get_next_block = skb_ts_get_next_block; ++ config->finish = skb_ts_finish; ++ ++ skb_prepare_seq_read(skb, from, to, TS_SKB_CB(state)); ++ ++ ret = textsearch_find(config, state); ++ return (ret <= to - from ? ret : UINT_MAX); ++} ++EXPORT_SYMBOL(skb_find_text); ++ ++/** ++ * skb_append_datato_frags - append the user data to a skb ++ * @sk: sock structure ++ * @skb: skb structure to be appended with user data. ++ * @getfrag: call back function to be used for getting the user data ++ * @from: pointer to user message iov ++ * @length: length of the iov message ++ * ++ * Description: This procedure append the user data in the fragment part ++ * of the skb if any page alloc fails user this procedure returns -ENOMEM ++ */ ++int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, ++ int (*getfrag)(void *from, char *to, int offset, ++ int len, int odd, struct sk_buff *skb), ++ void *from, int length) ++{ ++ int frg_cnt = skb_shinfo(skb)->nr_frags; ++ int copy; ++ int offset = 0; ++ int ret; ++ struct page_frag *pfrag = ¤t->task_frag; ++ ++ do { ++ /* Return error if we don't have space for new frag */ ++ if (frg_cnt >= MAX_SKB_FRAGS) ++ return -EMSGSIZE; ++ ++ if (!sk_page_frag_refill(sk, pfrag)) ++ return -ENOMEM; ++ ++ /* copy the user data to page */ ++ copy = min_t(int, length, pfrag->size - pfrag->offset); ++ ++ ret = getfrag(from, page_address(pfrag->page) + pfrag->offset, ++ offset, copy, 0, skb); ++ if (ret < 0) ++ return -EFAULT; ++ ++ /* copy was successful so update the size parameters */ ++ skb_fill_page_desc(skb, frg_cnt, pfrag->page, pfrag->offset, ++ copy); ++ frg_cnt++; ++ pfrag->offset += copy; ++ get_page(pfrag->page); ++ ++ skb->truesize += copy; ++ atomic_add(copy, &sk->sk_wmem_alloc); ++ skb->len += copy; ++ skb->data_len += copy; ++ offset += copy; ++ length -= copy; ++ ++ } while (length > 0); ++ ++ return 0; ++} ++EXPORT_SYMBOL(skb_append_datato_frags); ++ ++/** ++ * skb_pull_rcsum - pull skb and update receive checksum ++ * @skb: buffer to update ++ * @len: length of data pulled ++ * ++ * This function performs an skb_pull on the packet and updates ++ * the CHECKSUM_COMPLETE checksum. It should be used on ++ * receive path processing instead of skb_pull unless you know ++ * that the checksum difference is zero (e.g., a valid IP header) ++ * or you are setting ip_summed to CHECKSUM_NONE. ++ */ ++unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len) ++{ ++ BUG_ON(len > skb->len); ++ skb->len -= len; ++ BUG_ON(skb->len < skb->data_len); ++ skb_postpull_rcsum(skb, skb->data, len); ++ return skb->data += len; ++} ++EXPORT_SYMBOL_GPL(skb_pull_rcsum); ++ ++/** ++ * skb_segment - Perform protocol segmentation on skb. ++ * @head_skb: buffer to segment ++ * @features: features for the output path (see dev->features) ++ * ++ * This function performs segmentation on the given skb. It returns ++ * a pointer to the first in a list of new skbs for the segments. ++ * In case of error it returns ERR_PTR(err). ++ */ ++struct sk_buff *skb_segment(struct sk_buff *head_skb, ++ netdev_features_t features) ++{ ++ struct sk_buff *segs = NULL; ++ struct sk_buff *tail = NULL; ++ struct sk_buff *list_skb = skb_shinfo(head_skb)->frag_list; ++ skb_frag_t *frag = skb_shinfo(head_skb)->frags; ++ unsigned int mss = skb_shinfo(head_skb)->gso_size; ++ unsigned int doffset = head_skb->data - skb_mac_header(head_skb); ++ struct sk_buff *frag_skb = head_skb; ++ unsigned int offset = doffset; ++ unsigned int tnl_hlen = skb_tnl_header_len(head_skb); ++ unsigned int headroom; ++ unsigned int len; ++ __be16 proto; ++ bool csum; ++ int sg = !!(features & NETIF_F_SG); ++ int nfrags = skb_shinfo(head_skb)->nr_frags; ++ int err = -ENOMEM; ++ int i = 0; ++ int pos; ++ int dummy; ++ ++ __skb_push(head_skb, doffset); ++ proto = skb_network_protocol(head_skb, &dummy); ++ if (unlikely(!proto)) ++ return ERR_PTR(-EINVAL); ++ ++ csum = !head_skb->encap_hdr_csum && ++ !!can_checksum_protocol(features, proto); ++ ++ headroom = skb_headroom(head_skb); ++ pos = skb_headlen(head_skb); ++ ++ do { ++ struct sk_buff *nskb; ++ skb_frag_t *nskb_frag; ++ int hsize; ++ int size; ++ ++ len = head_skb->len - offset; ++ if (len > mss) ++ len = mss; ++ ++ hsize = skb_headlen(head_skb) - offset; ++ if (hsize < 0) ++ hsize = 0; ++ if (hsize > len || !sg) ++ hsize = len; ++ ++ if (!hsize && i >= nfrags && skb_headlen(list_skb) && ++ (skb_headlen(list_skb) == len || sg)) { ++ BUG_ON(skb_headlen(list_skb) > len); ++ ++ i = 0; ++ nfrags = skb_shinfo(list_skb)->nr_frags; ++ frag = skb_shinfo(list_skb)->frags; ++ frag_skb = list_skb; ++ pos += skb_headlen(list_skb); ++ ++ while (pos < offset + len) { ++ BUG_ON(i >= nfrags); ++ ++ size = skb_frag_size(frag); ++ if (pos + size > offset + len) ++ break; ++ ++ i++; ++ pos += size; ++ frag++; ++ } ++ ++ nskb = skb_clone(list_skb, GFP_ATOMIC); ++ list_skb = list_skb->next; ++ ++ if (unlikely(!nskb)) ++ goto err; ++ ++ if (unlikely(pskb_trim(nskb, len))) { ++ kfree_skb(nskb); ++ goto err; ++ } ++ ++ hsize = skb_end_offset(nskb); ++ if (skb_cow_head(nskb, doffset + headroom)) { ++ kfree_skb(nskb); ++ goto err; ++ } ++ ++ nskb->truesize += skb_end_offset(nskb) - hsize; ++ skb_release_head_state(nskb); ++ __skb_push(nskb, doffset); ++ } else { ++ nskb = __alloc_skb(hsize + doffset + headroom, ++ GFP_ATOMIC, skb_alloc_rx_flag(head_skb), ++ NUMA_NO_NODE); ++ ++ if (unlikely(!nskb)) ++ goto err; ++ ++ skb_reserve(nskb, headroom); ++ __skb_put(nskb, doffset); ++ } ++ ++ if (segs) ++ tail->next = nskb; ++ else ++ segs = nskb; ++ tail = nskb; ++ ++ __copy_skb_header(nskb, head_skb); ++ ++ skb_headers_offset_update(nskb, skb_headroom(nskb) - headroom); ++ skb_reset_mac_len(nskb); ++ ++ skb_copy_from_linear_data_offset(head_skb, -tnl_hlen, ++ nskb->data - tnl_hlen, ++ doffset + tnl_hlen); ++ ++ if (nskb->len == len + doffset) ++ goto perform_csum_check; ++ ++ if (!sg) { ++ nskb->ip_summed = CHECKSUM_NONE; ++ nskb->csum = skb_copy_and_csum_bits(head_skb, offset, ++ skb_put(nskb, len), ++ len, 0); ++ SKB_GSO_CB(nskb)->csum_start = ++ skb_headroom(nskb) + doffset; ++ continue; ++ } ++ ++ nskb_frag = skb_shinfo(nskb)->frags; ++ ++ skb_copy_from_linear_data_offset(head_skb, offset, ++ skb_put(nskb, hsize), hsize); ++ ++ skb_shinfo(nskb)->tx_flags = skb_shinfo(head_skb)->tx_flags & ++ SKBTX_SHARED_FRAG; ++ ++ while (pos < offset + len) { ++ if (i >= nfrags) { ++ BUG_ON(skb_headlen(list_skb)); ++ ++ i = 0; ++ nfrags = skb_shinfo(list_skb)->nr_frags; ++ frag = skb_shinfo(list_skb)->frags; ++ frag_skb = list_skb; ++ ++ BUG_ON(!nfrags); ++ ++ list_skb = list_skb->next; ++ } ++ ++ if (unlikely(skb_shinfo(nskb)->nr_frags >= ++ MAX_SKB_FRAGS)) { ++ net_warn_ratelimited( ++ "skb_segment: too many frags: %u %u\n", ++ pos, mss); ++ goto err; ++ } ++ ++ if (unlikely(skb_orphan_frags(frag_skb, GFP_ATOMIC))) ++ goto err; ++ ++ *nskb_frag = *frag; ++ __skb_frag_ref(nskb_frag); ++ size = skb_frag_size(nskb_frag); ++ ++ if (pos < offset) { ++ nskb_frag->page_offset += offset - pos; ++ skb_frag_size_sub(nskb_frag, offset - pos); ++ } ++ ++ skb_shinfo(nskb)->nr_frags++; ++ ++ if (pos + size <= offset + len) { ++ i++; ++ frag++; ++ pos += size; ++ } else { ++ skb_frag_size_sub(nskb_frag, pos + size - (offset + len)); ++ goto skip_fraglist; ++ } ++ ++ nskb_frag++; ++ } ++ ++skip_fraglist: ++ nskb->data_len = len - hsize; ++ nskb->len += nskb->data_len; ++ nskb->truesize += nskb->data_len; ++ ++perform_csum_check: ++ if (!csum) { ++ nskb->csum = skb_checksum(nskb, doffset, ++ nskb->len - doffset, 0); ++ nskb->ip_summed = CHECKSUM_NONE; ++ SKB_GSO_CB(nskb)->csum_start = ++ skb_headroom(nskb) + doffset; ++ } ++ } while ((offset += len) < head_skb->len); ++ ++ /* Some callers want to get the end of the list. ++ * Put it in segs->prev to avoid walking the list. ++ * (see validate_xmit_skb_list() for example) ++ */ ++ segs->prev = tail; ++ return segs; ++ ++err: ++ kfree_skb_list(segs); ++ return ERR_PTR(err); ++} ++EXPORT_SYMBOL_GPL(skb_segment); ++ ++int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) ++{ ++ struct skb_shared_info *pinfo, *skbinfo = skb_shinfo(skb); ++ unsigned int offset = skb_gro_offset(skb); ++ unsigned int headlen = skb_headlen(skb); ++ struct sk_buff *nskb, *lp, *p = *head; ++ unsigned int len = skb_gro_len(skb); ++ unsigned int delta_truesize; ++ unsigned int headroom; ++ ++ if (unlikely(p->len + len >= 65536)) ++ return -E2BIG; ++ ++ lp = NAPI_GRO_CB(p)->last; ++ pinfo = skb_shinfo(lp); ++ ++ if (headlen <= offset) { ++ skb_frag_t *frag; ++ skb_frag_t *frag2; ++ int i = skbinfo->nr_frags; ++ int nr_frags = pinfo->nr_frags + i; ++ ++ if (nr_frags > MAX_SKB_FRAGS) ++ goto merge; ++ ++ offset -= headlen; ++ pinfo->nr_frags = nr_frags; ++ skbinfo->nr_frags = 0; ++ ++ frag = pinfo->frags + nr_frags; ++ frag2 = skbinfo->frags + i; ++ do { ++ *--frag = *--frag2; ++ } while (--i); ++ ++ frag->page_offset += offset; ++ skb_frag_size_sub(frag, offset); ++ ++ /* all fragments truesize : remove (head size + sk_buff) */ ++ delta_truesize = skb->truesize - ++ SKB_TRUESIZE(skb_end_offset(skb)); ++ ++ skb->truesize -= skb->data_len; ++ skb->len -= skb->data_len; ++ skb->data_len = 0; ++ ++ NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE; ++ goto done; ++ } else if (skb->head_frag) { ++ int nr_frags = pinfo->nr_frags; ++ skb_frag_t *frag = pinfo->frags + nr_frags; ++ struct page *page = virt_to_head_page(skb->head); ++ unsigned int first_size = headlen - offset; ++ unsigned int first_offset; ++ ++ if (nr_frags + 1 + skbinfo->nr_frags > MAX_SKB_FRAGS) ++ goto merge; ++ ++ first_offset = skb->data - ++ (unsigned char *)page_address(page) + ++ offset; ++ ++ pinfo->nr_frags = nr_frags + 1 + skbinfo->nr_frags; ++ ++ frag->page.p = page; ++ frag->page_offset = first_offset; ++ skb_frag_size_set(frag, first_size); ++ ++ memcpy(frag + 1, skbinfo->frags, sizeof(*frag) * skbinfo->nr_frags); ++ /* We dont need to clear skbinfo->nr_frags here */ ++ ++ delta_truesize = skb->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff)); ++ NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE_STOLEN_HEAD; ++ goto done; ++ } ++ /* switch back to head shinfo */ ++ pinfo = skb_shinfo(p); ++ ++ if (pinfo->frag_list) ++ goto merge; ++ if (skb_gro_len(p) != pinfo->gso_size) ++ return -E2BIG; ++ ++ headroom = skb_headroom(p); ++ nskb = alloc_skb(headroom + skb_gro_offset(p), GFP_ATOMIC); ++ if (unlikely(!nskb)) ++ return -ENOMEM; ++ ++ __copy_skb_header(nskb, p); ++ nskb->mac_len = p->mac_len; ++ ++ skb_reserve(nskb, headroom); ++ __skb_put(nskb, skb_gro_offset(p)); ++ ++ skb_set_mac_header(nskb, skb_mac_header(p) - p->data); ++ skb_set_network_header(nskb, skb_network_offset(p)); ++ skb_set_transport_header(nskb, skb_transport_offset(p)); ++ ++ __skb_pull(p, skb_gro_offset(p)); ++ memcpy(skb_mac_header(nskb), skb_mac_header(p), ++ p->data - skb_mac_header(p)); ++ ++ skb_shinfo(nskb)->frag_list = p; ++ skb_shinfo(nskb)->gso_size = pinfo->gso_size; ++ pinfo->gso_size = 0; ++ __skb_header_release(p); ++ NAPI_GRO_CB(nskb)->last = p; ++ ++ nskb->data_len += p->len; ++ nskb->truesize += p->truesize; ++ nskb->len += p->len; ++ ++ *head = nskb; ++ nskb->next = p->next; ++ p->next = NULL; ++ ++ p = nskb; ++ ++merge: ++ delta_truesize = skb->truesize; ++ if (offset > headlen) { ++ unsigned int eat = offset - headlen; ++ ++ skbinfo->frags[0].page_offset += eat; ++ skb_frag_size_sub(&skbinfo->frags[0], eat); ++ skb->data_len -= eat; ++ skb->len -= eat; ++ offset = headlen; ++ } ++ ++ __skb_pull(skb, offset); ++ ++ if (NAPI_GRO_CB(p)->last == p) ++ skb_shinfo(p)->frag_list = skb; ++ else ++ NAPI_GRO_CB(p)->last->next = skb; ++ NAPI_GRO_CB(p)->last = skb; ++ __skb_header_release(skb); ++ lp = p; ++ ++done: ++ NAPI_GRO_CB(p)->count++; ++ p->data_len += len; ++ p->truesize += delta_truesize; ++ p->len += len; ++ if (lp != p) { ++ lp->data_len += len; ++ lp->truesize += delta_truesize; ++ lp->len += len; ++ } ++ NAPI_GRO_CB(skb)->same_flow = 1; ++ return 0; ++} ++ ++void __init skb_init(void) ++{ ++ skbuff_head_cache = kmem_cache_create("skbuff_head_cache", ++ sizeof(struct sk_buff), ++ 0, ++ SLAB_HWCACHE_ALIGN|SLAB_PANIC, ++ NULL); ++ skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache", ++ sizeof(struct sk_buff_fclones), ++ 0, ++ SLAB_HWCACHE_ALIGN|SLAB_PANIC, ++ NULL); ++} ++ ++/** ++ * skb_to_sgvec - Fill a scatter-gather list from a socket buffer ++ * @skb: Socket buffer containing the buffers to be mapped ++ * @sg: The scatter-gather list to map into ++ * @offset: The offset into the buffer's contents to start mapping ++ * @len: Length of buffer space to be mapped ++ * ++ * Fill the specified scatter-gather list with mappings/pointers into a ++ * region of the buffer space attached to a socket buffer. ++ */ ++static int ++__skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) ++{ ++ int start = skb_headlen(skb); ++ int i, copy = start - offset; ++ struct sk_buff *frag_iter; ++ int elt = 0; ++ ++ if (copy > 0) { ++ if (copy > len) ++ copy = len; ++ sg_set_buf(sg, skb->data + offset, copy); ++ elt++; ++ if ((len -= copy) == 0) ++ return elt; ++ offset += copy; ++ } ++ ++ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { ++ int end; ++ ++ WARN_ON(start > offset + len); ++ ++ end = start + skb_frag_size(&skb_shinfo(skb)->frags[i]); ++ if ((copy = end - offset) > 0) { ++ skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; ++ ++ if (copy > len) ++ copy = len; ++ sg_set_page(&sg[elt], skb_frag_page(frag), copy, ++ frag->page_offset+offset-start); ++ elt++; ++ if (!(len -= copy)) ++ return elt; ++ offset += copy; ++ } ++ start = end; ++ } ++ ++ skb_walk_frags(skb, frag_iter) { ++ int end; ++ ++ WARN_ON(start > offset + len); ++ ++ end = start + frag_iter->len; ++ if ((copy = end - offset) > 0) { ++ if (copy > len) ++ copy = len; ++ elt += __skb_to_sgvec(frag_iter, sg+elt, offset - start, ++ copy); ++ if ((len -= copy) == 0) ++ return elt; ++ offset += copy; ++ } ++ start = end; ++ } ++ BUG_ON(len); ++ return elt; ++} ++ ++/* As compared with skb_to_sgvec, skb_to_sgvec_nomark only map skb to given ++ * sglist without mark the sg which contain last skb data as the end. ++ * So the caller can mannipulate sg list as will when padding new data after ++ * the first call without calling sg_unmark_end to expend sg list. ++ * ++ * Scenario to use skb_to_sgvec_nomark: ++ * 1. sg_init_table ++ * 2. skb_to_sgvec_nomark(payload1) ++ * 3. skb_to_sgvec_nomark(payload2) ++ * ++ * This is equivalent to: ++ * 1. sg_init_table ++ * 2. skb_to_sgvec(payload1) ++ * 3. sg_unmark_end ++ * 4. skb_to_sgvec(payload2) ++ * ++ * When mapping mutilple payload conditionally, skb_to_sgvec_nomark ++ * is more preferable. ++ */ ++int skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg, ++ int offset, int len) ++{ ++ return __skb_to_sgvec(skb, sg, offset, len); ++} ++EXPORT_SYMBOL_GPL(skb_to_sgvec_nomark); ++ ++int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) ++{ ++ int nsg = __skb_to_sgvec(skb, sg, offset, len); ++ ++ sg_mark_end(&sg[nsg - 1]); ++ ++ return nsg; ++} ++EXPORT_SYMBOL_GPL(skb_to_sgvec); ++ ++/** ++ * skb_cow_data - Check that a socket buffer's data buffers are writable ++ * @skb: The socket buffer to check. ++ * @tailbits: Amount of trailing space to be added ++ * @trailer: Returned pointer to the skb where the @tailbits space begins ++ * ++ * Make sure that the data buffers attached to a socket buffer are ++ * writable. If they are not, private copies are made of the data buffers ++ * and the socket buffer is set to use these instead. ++ * ++ * If @tailbits is given, make sure that there is space to write @tailbits ++ * bytes of data beyond current end of socket buffer. @trailer will be ++ * set to point to the skb in which this space begins. ++ * ++ * The number of scatterlist elements required to completely map the ++ * COW'd and extended socket buffer will be returned. ++ */ ++int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer) ++{ ++ int copyflag; ++ int elt; ++ struct sk_buff *skb1, **skb_p; ++ ++ /* If skb is cloned or its head is paged, reallocate ++ * head pulling out all the pages (pages are considered not writable ++ * at the moment even if they are anonymous). ++ */ ++ if ((skb_cloned(skb) || skb_shinfo(skb)->nr_frags) && ++ __pskb_pull_tail(skb, skb_pagelen(skb)-skb_headlen(skb)) == NULL) ++ return -ENOMEM; ++ ++ /* Easy case. Most of packets will go this way. */ ++ if (!skb_has_frag_list(skb)) { ++ /* A little of trouble, not enough of space for trailer. ++ * This should not happen, when stack is tuned to generate ++ * good frames. OK, on miss we reallocate and reserve even more ++ * space, 128 bytes is fair. */ ++ ++ if (skb_tailroom(skb) < tailbits && ++ pskb_expand_head(skb, 0, tailbits-skb_tailroom(skb)+128, GFP_ATOMIC)) ++ return -ENOMEM; ++ ++ /* Voila! */ ++ *trailer = skb; ++ return 1; ++ } ++ ++ /* Misery. We are in troubles, going to mincer fragments... */ ++ ++ elt = 1; ++ skb_p = &skb_shinfo(skb)->frag_list; ++ copyflag = 0; ++ ++ while ((skb1 = *skb_p) != NULL) { ++ int ntail = 0; ++ ++ /* The fragment is partially pulled by someone, ++ * this can happen on input. Copy it and everything ++ * after it. */ ++ ++ if (skb_shared(skb1)) ++ copyflag = 1; ++ ++ /* If the skb is the last, worry about trailer. */ ++ ++ if (skb1->next == NULL && tailbits) { ++ if (skb_shinfo(skb1)->nr_frags || ++ skb_has_frag_list(skb1) || ++ skb_tailroom(skb1) < tailbits) ++ ntail = tailbits + 128; ++ } ++ ++ if (copyflag || ++ skb_cloned(skb1) || ++ ntail || ++ skb_shinfo(skb1)->nr_frags || ++ skb_has_frag_list(skb1)) { ++ struct sk_buff *skb2; ++ ++ /* Fuck, we are miserable poor guys... */ ++ if (ntail == 0) ++ skb2 = skb_copy(skb1, GFP_ATOMIC); ++ else ++ skb2 = skb_copy_expand(skb1, ++ skb_headroom(skb1), ++ ntail, ++ GFP_ATOMIC); ++ if (unlikely(skb2 == NULL)) ++ return -ENOMEM; ++ ++ if (skb1->sk) ++ skb_set_owner_w(skb2, skb1->sk); ++ ++ /* Looking around. Are we still alive? ++ * OK, link new skb, drop old one */ ++ ++ skb2->next = skb1->next; ++ *skb_p = skb2; ++ kfree_skb(skb1); ++ skb1 = skb2; ++ } ++ elt++; ++ *trailer = skb1; ++ skb_p = &skb1->next; ++ } ++ ++ return elt; ++} ++EXPORT_SYMBOL_GPL(skb_cow_data); ++ ++static void sock_rmem_free(struct sk_buff *skb) ++{ ++ struct sock *sk = skb->sk; ++ ++ atomic_sub(skb->truesize, &sk->sk_rmem_alloc); ++} ++ ++/* ++ * Note: We dont mem charge error packets (no sk_forward_alloc changes) ++ */ ++int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb) ++{ ++ if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= ++ (unsigned int)sk->sk_rcvbuf) ++ return -ENOMEM; ++ ++ skb_orphan(skb); ++ skb->sk = sk; ++ skb->destructor = sock_rmem_free; ++ atomic_add(skb->truesize, &sk->sk_rmem_alloc); ++ ++ /* before exiting rcu section, make sure dst is refcounted */ ++ skb_dst_force(skb); ++ ++ skb_queue_tail(&sk->sk_error_queue, skb); ++ if (!sock_flag(sk, SOCK_DEAD)) ++ sk->sk_data_ready(sk); ++ return 0; ++} ++EXPORT_SYMBOL(sock_queue_err_skb); ++ ++struct sk_buff *sock_dequeue_err_skb(struct sock *sk) ++{ ++ struct sk_buff_head *q = &sk->sk_error_queue; ++ struct sk_buff *skb, *skb_next; ++ unsigned long flags; ++ int err = 0; ++ ++ spin_lock_irqsave(&q->lock, flags); ++ skb = __skb_dequeue(q); ++ if (skb && (skb_next = skb_peek(q))) ++ err = SKB_EXT_ERR(skb_next)->ee.ee_errno; ++ spin_unlock_irqrestore(&q->lock, flags); ++ ++ sk->sk_err = err; ++ if (err) ++ sk->sk_error_report(sk); ++ ++ return skb; ++} ++EXPORT_SYMBOL(sock_dequeue_err_skb); ++ ++/** ++ * skb_clone_sk - create clone of skb, and take reference to socket ++ * @skb: the skb to clone ++ * ++ * This function creates a clone of a buffer that holds a reference on ++ * sk_refcnt. Buffers created via this function are meant to be ++ * returned using sock_queue_err_skb, or free via kfree_skb. ++ * ++ * When passing buffers allocated with this function to sock_queue_err_skb ++ * it is necessary to wrap the call with sock_hold/sock_put in order to ++ * prevent the socket from being released prior to being enqueued on ++ * the sk_error_queue. ++ */ ++struct sk_buff *skb_clone_sk(struct sk_buff *skb) ++{ ++ struct sock *sk = skb->sk; ++ struct sk_buff *clone; ++ ++ if (!sk || !atomic_inc_not_zero(&sk->sk_refcnt)) ++ return NULL; ++ ++ clone = skb_clone(skb, GFP_ATOMIC); ++ if (!clone) { ++ sock_put(sk); ++ return NULL; ++ } ++ ++ clone->sk = sk; ++ clone->destructor = sock_efree; ++ ++ return clone; ++} ++EXPORT_SYMBOL(skb_clone_sk); ++ ++static void __skb_complete_tx_timestamp(struct sk_buff *skb, ++ struct sock *sk, ++ int tstype) ++{ ++ struct sock_exterr_skb *serr; ++ int err; ++ ++ serr = SKB_EXT_ERR(skb); ++ memset(serr, 0, sizeof(*serr)); ++ serr->ee.ee_errno = ENOMSG; ++ serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING; ++ serr->ee.ee_info = tstype; ++ if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) { ++ serr->ee.ee_data = skb_shinfo(skb)->tskey; ++ if (sk->sk_protocol == IPPROTO_TCP) ++ serr->ee.ee_data -= sk->sk_tskey; ++ } ++ ++ err = sock_queue_err_skb(sk, skb); ++ ++ if (err) ++ kfree_skb(skb); ++} ++ ++void skb_complete_tx_timestamp(struct sk_buff *skb, ++ struct skb_shared_hwtstamps *hwtstamps) ++{ ++ struct sock *sk = skb->sk; ++ ++ /* take a reference to prevent skb_orphan() from freeing the socket */ ++ sock_hold(sk); ++ ++ *skb_hwtstamps(skb) = *hwtstamps; ++ __skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND); ++ ++ sock_put(sk); ++} ++EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp); ++ ++void __skb_tstamp_tx(struct sk_buff *orig_skb, ++ struct skb_shared_hwtstamps *hwtstamps, ++ struct sock *sk, int tstype) ++{ ++ struct sk_buff *skb; ++ ++ if (!sk) ++ return; ++ ++ if (hwtstamps) ++ *skb_hwtstamps(orig_skb) = *hwtstamps; ++ else ++ orig_skb->tstamp = ktime_get_real(); ++ ++ skb = skb_clone(orig_skb, GFP_ATOMIC); ++ if (!skb) ++ return; ++ ++ __skb_complete_tx_timestamp(skb, sk, tstype); ++} ++EXPORT_SYMBOL_GPL(__skb_tstamp_tx); ++ ++void skb_tstamp_tx(struct sk_buff *orig_skb, ++ struct skb_shared_hwtstamps *hwtstamps) ++{ ++ return __skb_tstamp_tx(orig_skb, hwtstamps, orig_skb->sk, ++ SCM_TSTAMP_SND); ++} ++EXPORT_SYMBOL_GPL(skb_tstamp_tx); ++ ++void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) ++{ ++ struct sock *sk = skb->sk; ++ struct sock_exterr_skb *serr; ++ int err; ++ ++ skb->wifi_acked_valid = 1; ++ skb->wifi_acked = acked; ++ ++ serr = SKB_EXT_ERR(skb); ++ memset(serr, 0, sizeof(*serr)); ++ serr->ee.ee_errno = ENOMSG; ++ serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS; ++ ++ /* take a reference to prevent skb_orphan() from freeing the socket */ ++ sock_hold(sk); ++ ++ err = sock_queue_err_skb(sk, skb); ++ if (err) ++ kfree_skb(skb); ++ ++ sock_put(sk); ++} ++EXPORT_SYMBOL_GPL(skb_complete_wifi_ack); ++ ++ ++/** ++ * skb_partial_csum_set - set up and verify partial csum values for packet ++ * @skb: the skb to set ++ * @start: the number of bytes after skb->data to start checksumming. ++ * @off: the offset from start to place the checksum. ++ * ++ * For untrusted partially-checksummed packets, we need to make sure the values ++ * for skb->csum_start and skb->csum_offset are valid so we don't oops. ++ * ++ * This function checks and sets those values and skb->ip_summed: if this ++ * returns false you should drop the packet. ++ */ ++bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off) ++{ ++ if (unlikely(start > skb_headlen(skb)) || ++ unlikely((int)start + off > skb_headlen(skb) - 2)) { ++ net_warn_ratelimited("bad partial csum: csum=%u/%u len=%u\n", ++ start, off, skb_headlen(skb)); ++ return false; ++ } ++ skb->ip_summed = CHECKSUM_PARTIAL; ++ skb->csum_start = skb_headroom(skb) + start; ++ skb->csum_offset = off; ++ skb_set_transport_header(skb, start); ++ return true; ++} ++EXPORT_SYMBOL_GPL(skb_partial_csum_set); ++ ++static int skb_maybe_pull_tail(struct sk_buff *skb, unsigned int len, ++ unsigned int max) ++{ ++ if (skb_headlen(skb) >= len) ++ return 0; ++ ++ /* If we need to pullup then pullup to the max, so we ++ * won't need to do it again. ++ */ ++ if (max > skb->len) ++ max = skb->len; ++ ++ if (__pskb_pull_tail(skb, max - skb_headlen(skb)) == NULL) ++ return -ENOMEM; ++ ++ if (skb_headlen(skb) < len) ++ return -EPROTO; ++ ++ return 0; ++} ++ ++#define MAX_TCP_HDR_LEN (15 * 4) ++ ++static __sum16 *skb_checksum_setup_ip(struct sk_buff *skb, ++ typeof(IPPROTO_IP) proto, ++ unsigned int off) ++{ ++ switch (proto) { ++ int err; ++ ++ case IPPROTO_TCP: ++ err = skb_maybe_pull_tail(skb, off + sizeof(struct tcphdr), ++ off + MAX_TCP_HDR_LEN); ++ if (!err && !skb_partial_csum_set(skb, off, ++ offsetof(struct tcphdr, ++ check))) ++ err = -EPROTO; ++ return err ? ERR_PTR(err) : &tcp_hdr(skb)->check; ++ ++ case IPPROTO_UDP: ++ err = skb_maybe_pull_tail(skb, off + sizeof(struct udphdr), ++ off + sizeof(struct udphdr)); ++ if (!err && !skb_partial_csum_set(skb, off, ++ offsetof(struct udphdr, ++ check))) ++ err = -EPROTO; ++ return err ? ERR_PTR(err) : &udp_hdr(skb)->check; ++ } ++ ++ return ERR_PTR(-EPROTO); ++} ++ ++/* This value should be large enough to cover a tagged ethernet header plus ++ * maximally sized IP and TCP or UDP headers. ++ */ ++#define MAX_IP_HDR_LEN 128 ++ ++static int skb_checksum_setup_ipv4(struct sk_buff *skb, bool recalculate) ++{ ++ unsigned int off; ++ bool fragment; ++ __sum16 *csum; ++ int err; ++ ++ fragment = false; ++ ++ err = skb_maybe_pull_tail(skb, ++ sizeof(struct iphdr), ++ MAX_IP_HDR_LEN); ++ if (err < 0) ++ goto out; ++ ++ if (ip_hdr(skb)->frag_off & htons(IP_OFFSET | IP_MF)) ++ fragment = true; ++ ++ off = ip_hdrlen(skb); ++ ++ err = -EPROTO; ++ ++ if (fragment) ++ goto out; ++ ++ csum = skb_checksum_setup_ip(skb, ip_hdr(skb)->protocol, off); ++ if (IS_ERR(csum)) ++ return PTR_ERR(csum); ++ ++ if (recalculate) ++ *csum = ~csum_tcpudp_magic(ip_hdr(skb)->saddr, ++ ip_hdr(skb)->daddr, ++ skb->len - off, ++ ip_hdr(skb)->protocol, 0); ++ err = 0; ++ ++out: ++ return err; ++} ++ ++/* This value should be large enough to cover a tagged ethernet header plus ++ * an IPv6 header, all options, and a maximal TCP or UDP header. ++ */ ++#define MAX_IPV6_HDR_LEN 256 ++ ++#define OPT_HDR(type, skb, off) \ ++ (type *)(skb_network_header(skb) + (off)) ++ ++static int skb_checksum_setup_ipv6(struct sk_buff *skb, bool recalculate) ++{ ++ int err; ++ u8 nexthdr; ++ unsigned int off; ++ unsigned int len; ++ bool fragment; ++ bool done; ++ __sum16 *csum; ++ ++ fragment = false; ++ done = false; ++ ++ off = sizeof(struct ipv6hdr); ++ ++ err = skb_maybe_pull_tail(skb, off, MAX_IPV6_HDR_LEN); ++ if (err < 0) ++ goto out; ++ ++ nexthdr = ipv6_hdr(skb)->nexthdr; ++ ++ len = sizeof(struct ipv6hdr) + ntohs(ipv6_hdr(skb)->payload_len); ++ while (off <= len && !done) { ++ switch (nexthdr) { ++ case IPPROTO_DSTOPTS: ++ case IPPROTO_HOPOPTS: ++ case IPPROTO_ROUTING: { ++ struct ipv6_opt_hdr *hp; ++ ++ err = skb_maybe_pull_tail(skb, ++ off + ++ sizeof(struct ipv6_opt_hdr), ++ MAX_IPV6_HDR_LEN); ++ if (err < 0) ++ goto out; ++ ++ hp = OPT_HDR(struct ipv6_opt_hdr, skb, off); ++ nexthdr = hp->nexthdr; ++ off += ipv6_optlen(hp); ++ break; ++ } ++ case IPPROTO_AH: { ++ struct ip_auth_hdr *hp; ++ ++ err = skb_maybe_pull_tail(skb, ++ off + ++ sizeof(struct ip_auth_hdr), ++ MAX_IPV6_HDR_LEN); ++ if (err < 0) ++ goto out; ++ ++ hp = OPT_HDR(struct ip_auth_hdr, skb, off); ++ nexthdr = hp->nexthdr; ++ off += ipv6_authlen(hp); ++ break; ++ } ++ case IPPROTO_FRAGMENT: { ++ struct frag_hdr *hp; ++ ++ err = skb_maybe_pull_tail(skb, ++ off + ++ sizeof(struct frag_hdr), ++ MAX_IPV6_HDR_LEN); ++ if (err < 0) ++ goto out; ++ ++ hp = OPT_HDR(struct frag_hdr, skb, off); ++ ++ if (hp->frag_off & htons(IP6_OFFSET | IP6_MF)) ++ fragment = true; ++ ++ nexthdr = hp->nexthdr; ++ off += sizeof(struct frag_hdr); ++ break; ++ } ++ default: ++ done = true; ++ break; ++ } ++ } ++ ++ err = -EPROTO; ++ ++ if (!done || fragment) ++ goto out; ++ ++ csum = skb_checksum_setup_ip(skb, nexthdr, off); ++ if (IS_ERR(csum)) ++ return PTR_ERR(csum); ++ ++ if (recalculate) ++ *csum = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, ++ &ipv6_hdr(skb)->daddr, ++ skb->len - off, nexthdr, 0); ++ err = 0; ++ ++out: ++ return err; ++} ++ ++/** ++ * skb_checksum_setup - set up partial checksum offset ++ * @skb: the skb to set up ++ * @recalculate: if true the pseudo-header checksum will be recalculated ++ */ ++int skb_checksum_setup(struct sk_buff *skb, bool recalculate) ++{ ++ int err; ++ ++ switch (skb->protocol) { ++ case htons(ETH_P_IP): ++ err = skb_checksum_setup_ipv4(skb, recalculate); ++ break; ++ ++ case htons(ETH_P_IPV6): ++ err = skb_checksum_setup_ipv6(skb, recalculate); ++ break; ++ ++ default: ++ err = -EPROTO; ++ break; ++ } ++ ++ return err; ++} ++EXPORT_SYMBOL(skb_checksum_setup); ++ ++void __skb_warn_lro_forwarding(const struct sk_buff *skb) ++{ ++ net_warn_ratelimited("%s: received packets cannot be forwarded while LRO is enabled\n", ++ skb->dev->name); ++} ++EXPORT_SYMBOL(__skb_warn_lro_forwarding); ++ ++void kfree_skb_partial(struct sk_buff *skb, bool head_stolen) ++{ ++ if (head_stolen) { ++ skb_release_head_state(skb); ++ kmem_cache_free(skbuff_head_cache, skb); ++ } else { ++ __kfree_skb(skb); ++ } ++} ++EXPORT_SYMBOL(kfree_skb_partial); ++ ++/** ++ * skb_try_coalesce - try to merge skb to prior one ++ * @to: prior buffer ++ * @from: buffer to add ++ * @fragstolen: pointer to boolean ++ * @delta_truesize: how much more was allocated than was requested ++ */ ++bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, ++ bool *fragstolen, int *delta_truesize) ++{ ++ int i, delta, len = from->len; ++ ++ *fragstolen = false; ++ ++ if (skb_cloned(to)) ++ return false; ++ ++ if (len <= skb_tailroom(to)) { ++ if (len) ++ BUG_ON(skb_copy_bits(from, 0, skb_put(to, len), len)); ++ *delta_truesize = 0; ++ return true; ++ } ++ ++ if (skb_has_frag_list(to) || skb_has_frag_list(from)) ++ return false; ++ ++ if (skb_headlen(from) != 0) { ++ struct page *page; ++ unsigned int offset; ++ ++ if (skb_shinfo(to)->nr_frags + ++ skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS) ++ return false; ++ ++ if (skb_head_is_locked(from)) ++ return false; ++ ++ delta = from->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff)); ++ ++ page = virt_to_head_page(from->head); ++ offset = from->data - (unsigned char *)page_address(page); ++ ++ skb_fill_page_desc(to, skb_shinfo(to)->nr_frags, ++ page, offset, skb_headlen(from)); ++ *fragstolen = true; ++ } else { ++ if (skb_shinfo(to)->nr_frags + ++ skb_shinfo(from)->nr_frags > MAX_SKB_FRAGS) ++ return false; ++ ++ delta = from->truesize - SKB_TRUESIZE(skb_end_offset(from)); ++ } ++ ++ WARN_ON_ONCE(delta < len); ++ ++ memcpy(skb_shinfo(to)->frags + skb_shinfo(to)->nr_frags, ++ skb_shinfo(from)->frags, ++ skb_shinfo(from)->nr_frags * sizeof(skb_frag_t)); ++ skb_shinfo(to)->nr_frags += skb_shinfo(from)->nr_frags; ++ ++ if (!skb_cloned(from)) ++ skb_shinfo(from)->nr_frags = 0; ++ ++ /* if the skb is not cloned this does nothing ++ * since we set nr_frags to 0. ++ */ ++ for (i = 0; i < skb_shinfo(from)->nr_frags; i++) ++ skb_frag_ref(from, i); ++ ++ to->truesize += delta; ++ to->len += len; ++ to->data_len += len; ++ ++ *delta_truesize = delta; ++ return true; ++} ++EXPORT_SYMBOL(skb_try_coalesce); ++ ++/** ++ * skb_scrub_packet - scrub an skb ++ * ++ * @skb: buffer to clean ++ * @xnet: packet is crossing netns ++ * ++ * skb_scrub_packet can be used after encapsulating or decapsulting a packet ++ * into/from a tunnel. Some information have to be cleared during these ++ * operations. ++ * skb_scrub_packet can also be used to clean a skb before injecting it in ++ * another namespace (@xnet == true). We have to clear all information in the ++ * skb that could impact namespace isolation. ++ */ ++void skb_scrub_packet(struct sk_buff *skb, bool xnet) ++{ ++ skb->tstamp.tv64 = 0; ++ skb->pkt_type = PACKET_HOST; ++ skb->skb_iif = 0; ++ skb->ignore_df = 0; ++ skb_dst_drop(skb); ++ secpath_reset(skb); ++ nf_reset(skb); ++ nf_reset_trace(skb); ++ ++ if (!xnet) ++ return; ++ ++ skb_orphan(skb); ++ skb->mark = 0; ++} ++EXPORT_SYMBOL_GPL(skb_scrub_packet); ++ ++/** ++ * skb_gso_transport_seglen - Return length of individual segments of a gso packet ++ * ++ * @skb: GSO skb ++ * ++ * skb_gso_transport_seglen is used to determine the real size of the ++ * individual segments, including Layer4 headers (TCP/UDP). ++ * ++ * The MAC/L2 or network (IP, IPv6) headers are not accounted for. ++ */ ++unsigned int skb_gso_transport_seglen(const struct sk_buff *skb) ++{ ++ const struct skb_shared_info *shinfo = skb_shinfo(skb); ++ unsigned int thlen = 0; ++ ++ if (skb->encapsulation) { ++ thlen = skb_inner_transport_header(skb) - ++ skb_transport_header(skb); ++ ++ if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) ++ thlen += inner_tcp_hdrlen(skb); ++ } else if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) { ++ thlen = tcp_hdrlen(skb); ++ } ++ /* UFO sets gso_size to the size of the fragmentation ++ * payload, i.e. the size of the L4 (UDP) header is already ++ * accounted for. ++ */ ++ return thlen + shinfo->gso_size; ++} ++EXPORT_SYMBOL_GPL(skb_gso_transport_seglen); ++ ++static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb) ++{ ++ if (skb_cow(skb, skb_headroom(skb)) < 0) { ++ kfree_skb(skb); ++ return NULL; ++ } ++ ++ memmove(skb->data - ETH_HLEN, skb->data - VLAN_ETH_HLEN, 2 * ETH_ALEN); ++ skb->mac_header += VLAN_HLEN; ++ return skb; ++} ++ ++struct sk_buff *skb_vlan_untag(struct sk_buff *skb) ++{ ++ struct vlan_hdr *vhdr; ++ u16 vlan_tci; ++ ++ if (unlikely(vlan_tx_tag_present(skb))) { ++ /* vlan_tci is already set-up so leave this for another time */ ++ return skb; ++ } ++ ++ skb = skb_share_check(skb, GFP_ATOMIC); ++ if (unlikely(!skb)) ++ goto err_free; ++ ++ if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) ++ goto err_free; ++ ++ vhdr = (struct vlan_hdr *)skb->data; ++ vlan_tci = ntohs(vhdr->h_vlan_TCI); ++ __vlan_hwaccel_put_tag(skb, skb->protocol, vlan_tci); ++ ++ skb_pull_rcsum(skb, VLAN_HLEN); ++ vlan_set_encap_proto(skb, vhdr); ++ ++ skb = skb_reorder_vlan_header(skb); ++ if (unlikely(!skb)) ++ goto err_free; ++ ++ skb_reset_network_header(skb); ++ skb_reset_transport_header(skb); ++ skb_reset_mac_len(skb); ++ ++ return skb; ++ ++err_free: ++ kfree_skb(skb); ++ return NULL; ++} ++EXPORT_SYMBOL(skb_vlan_untag); ++ ++/** ++ * alloc_skb_with_frags - allocate skb with page frags ++ * ++ * @header_len: size of linear part ++ * @data_len: needed length in frags ++ * @max_page_order: max page order desired. ++ * @errcode: pointer to error code if any ++ * @gfp_mask: allocation mask ++ * ++ * This can be used to allocate a paged skb, given a maximal order for frags. ++ */ ++struct sk_buff *alloc_skb_with_frags(unsigned long header_len, ++ unsigned long data_len, ++ int max_page_order, ++ int *errcode, ++ gfp_t gfp_mask) ++{ ++ int npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT; ++ unsigned long chunk; ++ struct sk_buff *skb; ++ struct page *page; ++ gfp_t gfp_head; ++ int i; ++ ++ *errcode = -EMSGSIZE; ++ /* Note this test could be relaxed, if we succeed to allocate ++ * high order pages... ++ */ ++ if (npages > MAX_SKB_FRAGS) ++ return NULL; ++ ++ gfp_head = gfp_mask; ++ if (gfp_head & __GFP_WAIT) ++ gfp_head |= __GFP_REPEAT; ++ ++ *errcode = -ENOBUFS; ++ skb = alloc_skb(header_len, gfp_head); ++ if (!skb) ++ return NULL; ++ ++ skb->truesize += npages << PAGE_SHIFT; ++ ++ for (i = 0; npages > 0; i++) { ++ int order = max_page_order; ++ ++ while (order) { ++ if (npages >= 1 << order) { ++ page = alloc_pages(gfp_mask | ++ __GFP_COMP | ++ __GFP_NOWARN | ++ __GFP_NORETRY, ++ order); ++ if (page) ++ goto fill_page; ++ /* Do not retry other high order allocations */ ++ order = 1; ++ max_page_order = 0; ++ } ++ order--; ++ } ++ page = alloc_page(gfp_mask); ++ if (!page) ++ goto failure; ++fill_page: ++ chunk = min_t(unsigned long, data_len, ++ PAGE_SIZE << order); ++ skb_fill_page_desc(skb, i, page, 0, chunk); ++ data_len -= chunk; ++ npages -= 1 << order; ++ } ++ return skb; ++ ++failure: ++ kfree_skb(skb); ++ return NULL; ++} ++EXPORT_SYMBOL(alloc_skb_with_frags); +diff -Nur linux-3.18.14.orig/net/core/sock.c linux-3.18.14-rt/net/core/sock.c +--- linux-3.18.14.orig/net/core/sock.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/core/sock.c 2015-05-31 15:32:49.433635358 -0500 +@@ -2345,12 +2345,11 @@ + if (sk->sk_lock.owned) + __lock_sock(sk); + sk->sk_lock.owned = 1; +- spin_unlock(&sk->sk_lock.slock); ++ spin_unlock_bh(&sk->sk_lock.slock); + /* + * The sk_lock has mutex_lock() semantics here: + */ + mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_); +- local_bh_enable(); + } + EXPORT_SYMBOL(lock_sock_nested); + +diff -Nur linux-3.18.14.orig/net/ipv4/icmp.c linux-3.18.14-rt/net/ipv4/icmp.c +--- linux-3.18.14.orig/net/ipv4/icmp.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/ipv4/icmp.c 2015-05-31 15:32:49.457635357 -0500 +@@ -69,6 +69,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -864,6 +865,30 @@ + } + + /* ++ * 32bit and 64bit have different timestamp length, so we check for ++ * the cookie at offset 20 and verify it is repeated at offset 50 ++ */ ++#define CO_POS0 20 ++#define CO_POS1 50 ++#define CO_SIZE sizeof(int) ++#define ICMP_SYSRQ_SIZE 57 ++ ++/* ++ * We got a ICMP_SYSRQ_SIZE sized ping request. Check for the cookie ++ * pattern and if it matches send the next byte as a trigger to sysrq. ++ */ ++static void icmp_check_sysrq(struct net *net, struct sk_buff *skb) ++{ ++ int cookie = htonl(net->ipv4.sysctl_icmp_echo_sysrq); ++ char *p = skb->data; ++ ++ if (!memcmp(&cookie, p + CO_POS0, CO_SIZE) && ++ !memcmp(&cookie, p + CO_POS1, CO_SIZE) && ++ p[CO_POS0 + CO_SIZE] == p[CO_POS1 + CO_SIZE]) ++ handle_sysrq(p[CO_POS0 + CO_SIZE]); ++} ++ ++/* + * Handle ICMP_ECHO ("ping") requests. + * + * RFC 1122: 3.2.2.6 MUST have an echo server that answers ICMP echo +@@ -890,6 +915,11 @@ + icmp_param.data_len = skb->len; + icmp_param.head_len = sizeof(struct icmphdr); + icmp_reply(&icmp_param, skb); ++ ++ if (skb->len == ICMP_SYSRQ_SIZE && ++ net->ipv4.sysctl_icmp_echo_sysrq) { ++ icmp_check_sysrq(net, skb); ++ } + } + } + +diff -Nur linux-3.18.14.orig/net/ipv4/sysctl_net_ipv4.c linux-3.18.14-rt/net/ipv4/sysctl_net_ipv4.c +--- linux-3.18.14.orig/net/ipv4/sysctl_net_ipv4.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/ipv4/sysctl_net_ipv4.c 2015-05-31 15:32:49.485635357 -0500 +@@ -779,6 +779,13 @@ + .proc_handler = proc_dointvec + }, + { ++ .procname = "icmp_echo_sysrq", ++ .data = &init_net.ipv4.sysctl_icmp_echo_sysrq, ++ .maxlen = sizeof(int), ++ .mode = 0644, ++ .proc_handler = proc_dointvec ++ }, ++ { + .procname = "icmp_ignore_bogus_error_responses", + .data = &init_net.ipv4.sysctl_icmp_ignore_bogus_error_responses, + .maxlen = sizeof(int), +diff -Nur linux-3.18.14.orig/net/mac80211/rx.c linux-3.18.14-rt/net/mac80211/rx.c +--- linux-3.18.14.orig/net/mac80211/rx.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/mac80211/rx.c 2015-05-31 15:32:49.501635357 -0500 +@@ -3360,7 +3360,7 @@ + struct ieee80211_supported_band *sband; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + +- WARN_ON_ONCE(softirq_count() == 0); ++ WARN_ON_ONCE_NONRT(softirq_count() == 0); + + if (WARN_ON(status->band >= IEEE80211_NUM_BANDS)) + goto drop; +diff -Nur linux-3.18.14.orig/net/netfilter/core.c linux-3.18.14-rt/net/netfilter/core.c +--- linux-3.18.14.orig/net/netfilter/core.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/netfilter/core.c 2015-05-31 15:32:49.549635357 -0500 +@@ -21,11 +21,17 @@ + #include + #include + #include ++#include + #include + #include + + #include "nf_internals.h" + ++#ifdef CONFIG_PREEMPT_RT_BASE ++DEFINE_LOCAL_IRQ_LOCK(xt_write_lock); ++EXPORT_PER_CPU_SYMBOL(xt_write_lock); ++#endif ++ + static DEFINE_MUTEX(afinfo_mutex); + + const struct nf_afinfo __rcu *nf_afinfo[NFPROTO_NUMPROTO] __read_mostly; +diff -Nur linux-3.18.14.orig/net/packet/af_packet.c linux-3.18.14-rt/net/packet/af_packet.c +--- linux-3.18.14.orig/net/packet/af_packet.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/packet/af_packet.c 2015-05-31 15:32:49.557635357 -0500 +@@ -63,6 +63,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -692,7 +693,7 @@ + if (BLOCK_NUM_PKTS(pbd)) { + while (atomic_read(&pkc->blk_fill_in_prog)) { + /* Waiting for skb_copy_bits to finish... */ +- cpu_relax(); ++ cpu_chill(); + } + } + +@@ -943,7 +944,7 @@ + if (!(status & TP_STATUS_BLK_TMO)) { + while (atomic_read(&pkc->blk_fill_in_prog)) { + /* Waiting for skb_copy_bits to finish... */ +- cpu_relax(); ++ cpu_chill(); + } + } + prb_close_block(pkc, pbd, po, status); +diff -Nur linux-3.18.14.orig/net/rds/ib_rdma.c linux-3.18.14-rt/net/rds/ib_rdma.c +--- linux-3.18.14.orig/net/rds/ib_rdma.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/rds/ib_rdma.c 2015-05-31 15:32:49.573635357 -0500 +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + + #include "rds.h" + #include "ib.h" +@@ -286,7 +287,7 @@ + for_each_online_cpu(cpu) { + flag = &per_cpu(clean_list_grace, cpu); + while (test_bit(CLEAN_LIST_BUSY_BIT, flag)) +- cpu_relax(); ++ cpu_chill(); + } + } + +diff -Nur linux-3.18.14.orig/net/sched/sch_generic.c linux-3.18.14-rt/net/sched/sch_generic.c +--- linux-3.18.14.orig/net/sched/sch_generic.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/sched/sch_generic.c 2015-05-31 15:32:49.593635356 -0500 +@@ -894,7 +894,7 @@ + /* Wait for outstanding qdisc_run calls. */ + list_for_each_entry(dev, head, close_list) + while (some_qdisc_is_busy(dev)) +- yield(); ++ msleep(1); + } + + void dev_deactivate(struct net_device *dev) +diff -Nur linux-3.18.14.orig/net/sunrpc/svc_xprt.c linux-3.18.14-rt/net/sunrpc/svc_xprt.c +--- linux-3.18.14.orig/net/sunrpc/svc_xprt.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/net/sunrpc/svc_xprt.c 2015-05-31 15:32:49.617635356 -0500 +@@ -357,7 +357,7 @@ + return; + } + +- cpu = get_cpu(); ++ cpu = get_cpu_light(); + pool = svc_pool_for_cpu(xprt->xpt_server, cpu); + spin_lock_bh(&pool->sp_lock); + +@@ -390,7 +390,7 @@ + } + + spin_unlock_bh(&pool->sp_lock); +- put_cpu(); ++ put_cpu_light(); + } + + /* +diff -Nur linux-3.18.14.orig/scripts/mkcompile_h linux-3.18.14-rt/scripts/mkcompile_h +--- linux-3.18.14.orig/scripts/mkcompile_h 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/scripts/mkcompile_h 2015-05-31 15:32:49.641635356 -0500 +@@ -4,7 +4,8 @@ + ARCH=$2 + SMP=$3 + PREEMPT=$4 +-CC=$5 ++RT=$5 ++CC=$6 + + vecho() { [ "${quiet}" = "silent_" ] || echo "$@" ; } + +@@ -57,6 +58,7 @@ + CONFIG_FLAGS="" + if [ -n "$SMP" ] ; then CONFIG_FLAGS="SMP"; fi + if [ -n "$PREEMPT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT"; fi ++if [ -n "$RT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS RT"; fi + UTS_VERSION="$UTS_VERSION $CONFIG_FLAGS $TIMESTAMP" + + # Truncate to maximum length +diff -Nur linux-3.18.14.orig/sound/core/pcm_native.c linux-3.18.14-rt/sound/core/pcm_native.c +--- linux-3.18.14.orig/sound/core/pcm_native.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/sound/core/pcm_native.c 2015-05-31 15:32:49.661635356 -0500 +@@ -104,7 +104,7 @@ + void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) + { + if (!substream->pcm->nonatomic) +- local_irq_disable(); ++ local_irq_disable_nort(); + snd_pcm_stream_lock(substream); + } + EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); +@@ -113,7 +113,7 @@ + { + snd_pcm_stream_unlock(substream); + if (!substream->pcm->nonatomic) +- local_irq_enable(); ++ local_irq_enable_nort(); + } + EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq); + +@@ -121,7 +121,7 @@ + { + unsigned long flags = 0; + if (!substream->pcm->nonatomic) +- local_irq_save(flags); ++ local_irq_save_nort(flags); + snd_pcm_stream_lock(substream); + return flags; + } +@@ -132,7 +132,7 @@ + { + snd_pcm_stream_unlock(substream); + if (!substream->pcm->nonatomic) +- local_irq_restore(flags); ++ local_irq_restore_nort(flags); + } + EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); + +diff -Nur linux-3.18.14.orig/virt/kvm/async_pf.c linux-3.18.14-rt/virt/kvm/async_pf.c +--- linux-3.18.14.orig/virt/kvm/async_pf.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/virt/kvm/async_pf.c 2015-05-31 15:32:49.661635356 -0500 +@@ -94,8 +94,8 @@ + + trace_kvm_async_pf_completed(addr, gva); + +- if (waitqueue_active(&vcpu->wq)) +- wake_up_interruptible(&vcpu->wq); ++ if (swaitqueue_active(&vcpu->wq)) ++ swait_wake_interruptible(&vcpu->wq); + + mmput(mm); + kvm_put_kvm(vcpu->kvm); +diff -Nur linux-3.18.14.orig/virt/kvm/kvm_main.c linux-3.18.14-rt/virt/kvm/kvm_main.c +--- linux-3.18.14.orig/virt/kvm/kvm_main.c 2015-05-20 10:04:50.000000000 -0500 ++++ linux-3.18.14-rt/virt/kvm/kvm_main.c 2015-05-31 15:32:49.697635356 -0500 +@@ -221,7 +221,7 @@ + vcpu->kvm = kvm; + vcpu->vcpu_id = id; + vcpu->pid = NULL; +- init_waitqueue_head(&vcpu->wq); ++ init_swait_head(&vcpu->wq); + kvm_async_pf_vcpu_init(vcpu); + + page = alloc_page(GFP_KERNEL | __GFP_ZERO); +@@ -1741,10 +1741,10 @@ + */ + void kvm_vcpu_block(struct kvm_vcpu *vcpu) + { +- DEFINE_WAIT(wait); ++ DEFINE_SWAITER(wait); + + for (;;) { +- prepare_to_wait(&vcpu->wq, &wait, TASK_INTERRUPTIBLE); ++ swait_prepare(&vcpu->wq, &wait, TASK_INTERRUPTIBLE); + + if (kvm_arch_vcpu_runnable(vcpu)) { + kvm_make_request(KVM_REQ_UNHALT, vcpu); +@@ -1758,7 +1758,7 @@ + schedule(); + } + +- finish_wait(&vcpu->wq, &wait); ++ swait_finish(&vcpu->wq, &wait); + } + EXPORT_SYMBOL_GPL(kvm_vcpu_block); + +@@ -1770,11 +1770,11 @@ + { + int me; + int cpu = vcpu->cpu; +- wait_queue_head_t *wqp; ++ struct swait_head *wqp; + + wqp = kvm_arch_vcpu_wq(vcpu); +- if (waitqueue_active(wqp)) { +- wake_up_interruptible(wqp); ++ if (swaitqueue_active(wqp)) { ++ swait_wake_interruptible(wqp); + ++vcpu->stat.halt_wakeup; + } + +@@ -1879,7 +1879,7 @@ + continue; + if (vcpu == me) + continue; +- if (waitqueue_active(&vcpu->wq) && !kvm_arch_vcpu_runnable(vcpu)) ++ if (swaitqueue_active(&vcpu->wq) && !kvm_arch_vcpu_runnable(vcpu)) + continue; + if (!kvm_vcpu_eligible_for_directed_yield(vcpu)) + continue; diff --git a/target/linux/patches/3.18.16/regmap-bool.patch b/target/linux/patches/3.18.16/regmap-bool.patch new file mode 100644 index 000000000..5c0ff5e2c --- /dev/null +++ b/target/linux/patches/3.18.16/regmap-bool.patch @@ -0,0 +1,27 @@ +diff -Nur linux-3.18.5.orig/drivers/base/regmap/Kconfig linux-3.18.5/drivers/base/regmap/Kconfig +--- linux-3.18.5.orig/drivers/base/regmap/Kconfig 2015-01-30 02:41:03.000000000 +0100 ++++ linux-3.18.5/drivers/base/regmap/Kconfig 2015-02-02 11:53:27.854106073 +0100 +@@ -10,19 +10,19 @@ + bool + + config REGMAP_I2C +- tristate ++ bool + depends on I2C + + config REGMAP_SPI +- tristate ++ bool + depends on SPI + + config REGMAP_SPMI +- tristate ++ bool + depends on SPMI + + config REGMAP_MMIO +- tristate ++ bool + + config REGMAP_IRQ + bool diff --git a/target/linux/patches/3.18.16/relocs.patch b/target/linux/patches/3.18.16/relocs.patch new file mode 100644 index 000000000..69a7c88a9 --- /dev/null +++ b/target/linux/patches/3.18.16/relocs.patch @@ -0,0 +1,2709 @@ +diff -Nur linux-3.13.6.orig/arch/x86/tools/relocs.c linux-3.13.6/arch/x86/tools/relocs.c +--- linux-3.13.6.orig/arch/x86/tools/relocs.c 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/arch/x86/tools/relocs.c 2014-03-15 19:39:45.000000000 +0100 +@@ -126,6 +126,7 @@ + + if (err) { + regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); ++ printf("foo: %s\n", sym_regex[i]); + die("%s", errbuf); + } + } +diff -Nur linux-3.13.6.orig/arch/x86/tools/relocs.h linux-3.13.6/arch/x86/tools/relocs.h +--- linux-3.13.6.orig/arch/x86/tools/relocs.h 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/arch/x86/tools/relocs.h 2014-03-15 18:48:40.000000000 +0100 +@@ -9,11 +9,19 @@ + #include + #include + #include ++#ifdef __linux__ + #include + #include + #define USE_BSD + #include ++#else ++#include "elf.h" ++#endif ++#ifdef __APPLE__ ++#include ++#else + #include ++#endif + #include + + void die(char *fmt, ...); +diff -Nur linux-3.13.6.orig/tools/include/elf.h linux-3.13.6/tools/include/elf.h +--- linux-3.13.6.orig/tools/include/elf.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.13.6/tools/include/elf.h 2014-03-15 18:47:36.000000000 +0100 +@@ -0,0 +1,2671 @@ ++#ifndef _ELF_H ++#define _ELF_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include ++ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++#define EI_NIDENT (16) ++ ++typedef struct { ++ unsigned char e_ident[EI_NIDENT]; ++ Elf32_Half e_type; ++ Elf32_Half e_machine; ++ Elf32_Word e_version; ++ Elf32_Addr e_entry; ++ Elf32_Off e_phoff; ++ Elf32_Off e_shoff; ++ Elf32_Word e_flags; ++ Elf32_Half e_ehsize; ++ Elf32_Half e_phentsize; ++ Elf32_Half e_phnum; ++ Elf32_Half e_shentsize; ++ Elf32_Half e_shnum; ++ Elf32_Half e_shstrndx; ++} Elf32_Ehdr; ++ ++typedef struct { ++ unsigned char e_ident[EI_NIDENT]; ++ Elf64_Half e_type; ++ Elf64_Half e_machine; ++ Elf64_Word e_version; ++ Elf64_Addr e_entry; ++ Elf64_Off e_phoff; ++ Elf64_Off e_shoff; ++ Elf64_Word e_flags; ++ Elf64_Half e_ehsize; ++ Elf64_Half e_phentsize; ++ Elf64_Half e_phnum; ++ Elf64_Half e_shentsize; ++ Elf64_Half e_shnum; ++ Elf64_Half e_shstrndx; ++} Elf64_Ehdr; ++ ++#define EI_MAG0 0 ++#define ELFMAG0 0x7f ++ ++#define EI_MAG1 1 ++#define ELFMAG1 'E' ++ ++#define EI_MAG2 2 ++#define ELFMAG2 'L' ++ ++#define EI_MAG3 3 ++#define ELFMAG3 'F' ++ ++ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 ++#define ELFCLASSNONE 0 ++#define ELFCLASS32 1 ++#define ELFCLASS64 2 ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 ++#define ELFDATANONE 0 ++#define ELFDATA2LSB 1 ++#define ELFDATA2MSB 2 ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 ++ ++ ++#define EI_OSABI 7 ++#define ELFOSABI_NONE 0 ++#define ELFOSABI_SYSV 0 ++#define ELFOSABI_HPUX 1 ++#define ELFOSABI_NETBSD 2 ++#define ELFOSABI_LINUX 3 ++#define ELFOSABI_GNU 3 ++#define ELFOSABI_SOLARIS 6 ++#define ELFOSABI_AIX 7 ++#define ELFOSABI_IRIX 8 ++#define ELFOSABI_FREEBSD 9 ++#define ELFOSABI_TRU64 10 ++#define ELFOSABI_MODESTO 11 ++#define ELFOSABI_OPENBSD 12 ++#define ELFOSABI_ARM 97 ++#define ELFOSABI_STANDALONE 255 ++ ++#define EI_ABIVERSION 8 ++ ++#define EI_PAD 9 ++ ++ ++ ++#define ET_NONE 0 ++#define ET_REL 1 ++#define ET_EXEC 2 ++#define ET_DYN 3 ++#define ET_CORE 4 ++#define ET_NUM 5 ++#define ET_LOOS 0xfe00 ++#define ET_HIOS 0xfeff ++#define ET_LOPROC 0xff00 ++#define ET_HIPROC 0xffff ++ ++ ++ ++#define EM_NONE 0 ++#define EM_M32 1 ++#define EM_SPARC 2 ++#define EM_386 3 ++#define EM_68K 4 ++#define EM_88K 5 ++#define EM_860 7 ++#define EM_MIPS 8 ++#define EM_S370 9 ++#define EM_MIPS_RS3_LE 10 ++ ++#define EM_PARISC 15 ++#define EM_VPP500 17 ++#define EM_SPARC32PLUS 18 ++#define EM_960 19 ++#define EM_PPC 20 ++#define EM_PPC64 21 ++#define EM_S390 22 ++ ++#define EM_V800 36 ++#define EM_FR20 37 ++#define EM_RH32 38 ++#define EM_RCE 39 ++#define EM_ARM 40 ++#define EM_FAKE_ALPHA 41 ++#define EM_SH 42 ++#define EM_SPARCV9 43 ++#define EM_TRICORE 44 ++#define EM_ARC 45 ++#define EM_H8_300 46 ++#define EM_H8_300H 47 ++#define EM_H8S 48 ++#define EM_H8_500 49 ++#define EM_IA_64 50 ++#define EM_MIPS_X 51 ++#define EM_COLDFIRE 52 ++#define EM_68HC12 53 ++#define EM_MMA 54 ++#define EM_PCP 55 ++#define EM_NCPU 56 ++#define EM_NDR1 57 ++#define EM_STARCORE 58 ++#define EM_ME16 59 ++#define EM_ST100 60 ++#define EM_TINYJ 61 ++#define EM_X86_64 62 ++#define EM_PDSP 63 ++ ++#define EM_FX66 66 ++#define EM_ST9PLUS 67 ++#define EM_ST7 68 ++#define EM_68HC16 69 ++#define EM_68HC11 70 ++#define EM_68HC08 71 ++#define EM_68HC05 72 ++#define EM_SVX 73 ++#define EM_ST19 74 ++#define EM_VAX 75 ++#define EM_CRIS 76 ++#define EM_JAVELIN 77 ++#define EM_FIREPATH 78 ++#define EM_ZSP 79 ++#define EM_MMIX 80 ++#define EM_HUANY 81 ++#define EM_PRISM 82 ++#define EM_AVR 83 ++#define EM_FR30 84 ++#define EM_D10V 85 ++#define EM_D30V 86 ++#define EM_V850 87 ++#define EM_M32R 88 ++#define EM_MN10300 89 ++#define EM_MN10200 90 ++#define EM_PJ 91 ++#define EM_OPENRISC 92 ++#define EM_ARC_A5 93 ++#define EM_XTENSA 94 ++#define EM_AARCH64 183 ++#define EM_TILEPRO 188 ++#define EM_MICROBLAZE 189 ++#define EM_TILEGX 191 ++#define EM_NUM 192 ++#define EM_ALPHA 0x9026 ++ ++#define EV_NONE 0 ++#define EV_CURRENT 1 ++#define EV_NUM 2 ++ ++typedef struct { ++ Elf32_Word sh_name; ++ Elf32_Word sh_type; ++ Elf32_Word sh_flags; ++ Elf32_Addr sh_addr; ++ Elf32_Off sh_offset; ++ Elf32_Word sh_size; ++ Elf32_Word sh_link; ++ Elf32_Word sh_info; ++ Elf32_Word sh_addralign; ++ Elf32_Word sh_entsize; ++} Elf32_Shdr; ++ ++typedef struct { ++ Elf64_Word sh_name; ++ Elf64_Word sh_type; ++ Elf64_Xword sh_flags; ++ Elf64_Addr sh_addr; ++ Elf64_Off sh_offset; ++ Elf64_Xword sh_size; ++ Elf64_Word sh_link; ++ Elf64_Word sh_info; ++ Elf64_Xword sh_addralign; ++ Elf64_Xword sh_entsize; ++} Elf64_Shdr; ++ ++ ++ ++#define SHN_UNDEF 0 ++#define SHN_LORESERVE 0xff00 ++#define SHN_LOPROC 0xff00 ++#define SHN_BEFORE 0xff00 ++ ++#define SHN_AFTER 0xff01 ++ ++#define SHN_HIPROC 0xff1f ++#define SHN_LOOS 0xff20 ++#define SHN_HIOS 0xff3f ++#define SHN_ABS 0xfff1 ++#define SHN_COMMON 0xfff2 ++#define SHN_XINDEX 0xffff ++#define SHN_HIRESERVE 0xffff ++ ++ ++ ++#define SHT_NULL 0 ++#define SHT_PROGBITS 1 ++#define SHT_SYMTAB 2 ++#define SHT_STRTAB 3 ++#define SHT_RELA 4 ++#define SHT_HASH 5 ++#define SHT_DYNAMIC 6 ++#define SHT_NOTE 7 ++#define SHT_NOBITS 8 ++#define SHT_REL 9 ++#define SHT_SHLIB 10 ++#define SHT_DYNSYM 11 ++#define SHT_INIT_ARRAY 14 ++#define SHT_FINI_ARRAY 15 ++#define SHT_PREINIT_ARRAY 16 ++#define SHT_GROUP 17 ++#define SHT_SYMTAB_SHNDX 18 ++#define SHT_NUM 19 ++#define SHT_LOOS 0x60000000 ++#define SHT_GNU_ATTRIBUTES 0x6ffffff5 ++#define SHT_GNU_HASH 0x6ffffff6 ++#define SHT_GNU_LIBLIST 0x6ffffff7 ++#define SHT_CHECKSUM 0x6ffffff8 ++#define SHT_LOSUNW 0x6ffffffa ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd ++#define SHT_GNU_verneed 0x6ffffffe ++#define SHT_GNU_versym 0x6fffffff ++#define SHT_HISUNW 0x6fffffff ++#define SHT_HIOS 0x6fffffff ++#define SHT_LOPROC 0x70000000 ++#define SHT_HIPROC 0x7fffffff ++#define SHT_LOUSER 0x80000000 ++#define SHT_HIUSER 0x8fffffff ++ ++#define SHF_WRITE (1 << 0) ++#define SHF_ALLOC (1 << 1) ++#define SHF_EXECINSTR (1 << 2) ++#define SHF_MERGE (1 << 4) ++#define SHF_STRINGS (1 << 5) ++#define SHF_INFO_LINK (1 << 6) ++#define SHF_LINK_ORDER (1 << 7) ++#define SHF_OS_NONCONFORMING (1 << 8) ++ ++#define SHF_GROUP (1 << 9) ++#define SHF_TLS (1 << 10) ++#define SHF_MASKOS 0x0ff00000 ++#define SHF_MASKPROC 0xf0000000 ++#define SHF_ORDERED (1 << 30) ++#define SHF_EXCLUDE (1 << 31) ++ ++#define GRP_COMDAT 0x1 ++ ++typedef struct { ++ Elf32_Word st_name; ++ Elf32_Addr st_value; ++ Elf32_Word st_size; ++ unsigned char st_info; ++ unsigned char st_other; ++ Elf32_Section st_shndx; ++} Elf32_Sym; ++ ++typedef struct { ++ Elf64_Word st_name; ++ unsigned char st_info; ++ unsigned char st_other; ++ Elf64_Section st_shndx; ++ Elf64_Addr st_value; ++ Elf64_Xword st_size; ++} Elf64_Sym; ++ ++typedef struct { ++ Elf32_Half si_boundto; ++ Elf32_Half si_flags; ++} Elf32_Syminfo; ++ ++typedef struct { ++ Elf64_Half si_boundto; ++ Elf64_Half si_flags; ++} Elf64_Syminfo; ++ ++#define SYMINFO_BT_SELF 0xffff ++#define SYMINFO_BT_PARENT 0xfffe ++#define SYMINFO_BT_LOWRESERVE 0xff00 ++ ++#define SYMINFO_FLG_DIRECT 0x0001 ++#define SYMINFO_FLG_PASSTHRU 0x0002 ++#define SYMINFO_FLG_COPY 0x0004 ++#define SYMINFO_FLG_LAZYLOAD 0x0008 ++ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++#define STB_LOCAL 0 ++#define STB_GLOBAL 1 ++#define STB_WEAK 2 ++#define STB_NUM 3 ++#define STB_LOOS 10 ++#define STB_GNU_UNIQUE 10 ++#define STB_HIOS 12 ++#define STB_LOPROC 13 ++#define STB_HIPROC 15 ++ ++#define STT_NOTYPE 0 ++#define STT_OBJECT 1 ++#define STT_FUNC 2 ++#define STT_SECTION 3 ++#define STT_FILE 4 ++#define STT_COMMON 5 ++#define STT_TLS 6 ++#define STT_NUM 7 ++#define STT_LOOS 10 ++#define STT_GNU_IFUNC 10 ++#define STT_HIOS 12 ++#define STT_LOPROC 13 ++#define STT_HIPROC 15 ++ ++#define STN_UNDEF 0 ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++#define STV_DEFAULT 0 ++#define STV_INTERNAL 1 ++#define STV_HIDDEN 2 ++#define STV_PROTECTED 3 ++ ++ ++ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; ++ Elf32_Word r_info; ++} Elf32_Rel; ++ ++typedef struct { ++ Elf64_Addr r_offset; ++ Elf64_Xword r_info; ++} Elf64_Rel; ++ ++ ++ ++typedef struct { ++ Elf32_Addr r_offset; ++ Elf32_Word r_info; ++ Elf32_Sword r_addend; ++} Elf32_Rela; ++ ++typedef struct { ++ Elf64_Addr r_offset; ++ Elf64_Xword r_info; ++ Elf64_Sxword r_addend; ++} Elf64_Rela; ++ ++ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++ ++ ++typedef struct { ++ Elf32_Word p_type; ++ Elf32_Off p_offset; ++ Elf32_Addr p_vaddr; ++ Elf32_Addr p_paddr; ++ Elf32_Word p_filesz; ++ Elf32_Word p_memsz; ++ Elf32_Word p_flags; ++ Elf32_Word p_align; ++} Elf32_Phdr; ++ ++typedef struct { ++ Elf64_Word p_type; ++ Elf64_Word p_flags; ++ Elf64_Off p_offset; ++ Elf64_Addr p_vaddr; ++ Elf64_Addr p_paddr; ++ Elf64_Xword p_filesz; ++ Elf64_Xword p_memsz; ++ Elf64_Xword p_align; ++} Elf64_Phdr; ++ ++ ++ ++#define PT_NULL 0 ++#define PT_LOAD 1 ++#define PT_DYNAMIC 2 ++#define PT_INTERP 3 ++#define PT_NOTE 4 ++#define PT_SHLIB 5 ++#define PT_PHDR 6 ++#define PT_TLS 7 ++#define PT_NUM 8 ++#define PT_LOOS 0x60000000 ++#define PT_GNU_EH_FRAME 0x6474e550 ++#define PT_GNU_STACK 0x6474e551 ++#define PT_GNU_RELRO 0x6474e552 ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa ++#define PT_SUNWSTACK 0x6ffffffb ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff ++#define PT_LOPROC 0x70000000 ++#define PT_HIPROC 0x7fffffff ++ ++ ++#define PN_XNUM 0xffff ++ ++ ++#define PF_X (1 << 0) ++#define PF_W (1 << 1) ++#define PF_R (1 << 2) ++#define PF_MASKOS 0x0ff00000 ++#define PF_MASKPROC 0xf0000000 ++ ++ ++ ++#define NT_PRSTATUS 1 ++#define NT_FPREGSET 2 ++#define NT_PRPSINFO 3 ++#define NT_PRXREG 4 ++#define NT_TASKSTRUCT 4 ++#define NT_PLATFORM 5 ++#define NT_AUXV 6 ++#define NT_GWINDOWS 7 ++#define NT_ASRS 8 ++#define NT_PSTATUS 10 ++#define NT_PSINFO 13 ++#define NT_PRCRED 14 ++#define NT_UTSNAME 15 ++#define NT_LWPSTATUS 16 ++#define NT_LWPSINFO 17 ++#define NT_PRFPXREG 20 ++#define NT_SIGINFO 0x53494749 ++#define NT_FILE 0x46494c45 ++#define NT_PRXFPREG 0x46e62b7f ++#define NT_PPC_VMX 0x100 ++#define NT_PPC_SPE 0x101 ++#define NT_PPC_VSX 0x102 ++#define NT_386_TLS 0x200 ++#define NT_386_IOPERM 0x201 ++#define NT_X86_XSTATE 0x202 ++#define NT_S390_HIGH_GPRS 0x300 ++#define NT_S390_TIMER 0x301 ++#define NT_S390_TODCMP 0x302 ++#define NT_S390_TODPREG 0x303 ++#define NT_S390_CTRS 0x304 ++#define NT_S390_PREFIX 0x305 ++#define NT_S390_LAST_BREAK 0x306 ++#define NT_S390_SYSTEM_CALL 0x307 ++#define NT_S390_TDB 0x308 ++#define NT_ARM_VFP 0x400 ++#define NT_ARM_TLS 0x401 ++#define NT_ARM_HW_BREAK 0x402 ++#define NT_ARM_HW_WATCH 0x403 ++#define NT_METAG_CBUF 0x500 ++#define NT_METAG_RPIPE 0x501 ++#define NT_METAG_TLS 0x502 ++#define NT_VERSION 1 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Sword d_tag; ++ union { ++ Elf32_Word d_val; ++ Elf32_Addr d_ptr; ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct { ++ Elf64_Sxword d_tag; ++ union { ++ Elf64_Xword d_val; ++ Elf64_Addr d_ptr; ++ } d_un; ++} Elf64_Dyn; ++ ++ ++ ++#define DT_NULL 0 ++#define DT_NEEDED 1 ++#define DT_PLTRELSZ 2 ++#define DT_PLTGOT 3 ++#define DT_HASH 4 ++#define DT_STRTAB 5 ++#define DT_SYMTAB 6 ++#define DT_RELA 7 ++#define DT_RELASZ 8 ++#define DT_RELAENT 9 ++#define DT_STRSZ 10 ++#define DT_SYMENT 11 ++#define DT_INIT 12 ++#define DT_FINI 13 ++#define DT_SONAME 14 ++#define DT_RPATH 15 ++#define DT_SYMBOLIC 16 ++#define DT_REL 17 ++#define DT_RELSZ 18 ++#define DT_RELENT 19 ++#define DT_PLTREL 20 ++#define DT_DEBUG 21 ++#define DT_TEXTREL 22 ++#define DT_JMPREL 23 ++#define DT_BIND_NOW 24 ++#define DT_INIT_ARRAY 25 ++#define DT_FINI_ARRAY 26 ++#define DT_INIT_ARRAYSZ 27 ++#define DT_FINI_ARRAYSZ 28 ++#define DT_RUNPATH 29 ++#define DT_FLAGS 30 ++#define DT_ENCODING 32 ++#define DT_PREINIT_ARRAY 32 ++#define DT_PREINIT_ARRAYSZ 33 ++#define DT_NUM 34 ++#define DT_LOOS 0x6000000d ++#define DT_HIOS 0x6ffff000 ++#define DT_LOPROC 0x70000000 ++#define DT_HIPROC 0x7fffffff ++#define DT_PROCNUM DT_MIPS_NUM ++ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc ++#define DT_POSFLAG_1 0x6ffffdfd ++ ++#define DT_SYMINSZ 0x6ffffdfe ++#define DT_SYMINENT 0x6ffffdff ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) ++#define DT_VALNUM 12 ++ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_HASH 0x6ffffef5 ++#define DT_TLSDESC_PLT 0x6ffffef6 ++#define DT_TLSDESC_GOT 0x6ffffef7 ++#define DT_GNU_CONFLICT 0x6ffffef8 ++#define DT_GNU_LIBLIST 0x6ffffef9 ++#define DT_CONFIG 0x6ffffefa ++#define DT_DEPAUDIT 0x6ffffefb ++#define DT_AUDIT 0x6ffffefc ++#define DT_PLTPAD 0x6ffffefd ++#define DT_MOVETAB 0x6ffffefe ++#define DT_SYMINFO 0x6ffffeff ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) ++#define DT_ADDRNUM 11 ++ ++ ++ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++ ++#define DT_FLAGS_1 0x6ffffffb ++#define DT_VERDEF 0x6ffffffc ++ ++#define DT_VERDEFNUM 0x6ffffffd ++#define DT_VERNEED 0x6ffffffe ++ ++#define DT_VERNEEDNUM 0x6fffffff ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) ++#define DT_VERSIONTAGNUM 16 ++ ++ ++ ++#define DT_AUXILIARY 0x7ffffffd ++#define DT_FILTER 0x7fffffff ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++ ++#define DF_ORIGIN 0x00000001 ++#define DF_SYMBOLIC 0x00000002 ++#define DF_TEXTREL 0x00000004 ++#define DF_BIND_NOW 0x00000008 ++#define DF_STATIC_TLS 0x00000010 ++ ++ ++ ++#define DF_1_NOW 0x00000001 ++#define DF_1_GLOBAL 0x00000002 ++#define DF_1_GROUP 0x00000004 ++#define DF_1_NODELETE 0x00000008 ++#define DF_1_LOADFLTR 0x00000010 ++#define DF_1_INITFIRST 0x00000020 ++#define DF_1_NOOPEN 0x00000040 ++#define DF_1_ORIGIN 0x00000080 ++#define DF_1_DIRECT 0x00000100 ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 ++#define DF_1_NODEFLIB 0x00000800 ++#define DF_1_NODUMP 0x00001000 ++#define DF_1_CONFALT 0x00002000 ++#define DF_1_ENDFILTEE 0x00004000 ++#define DF_1_DISPRELDNE 0x00008000 ++#define DF_1_DISPRELPND 0x00010000 ++#define DF_1_NODIRECT 0x00020000 ++#define DF_1_IGNMULDEF 0x00040000 ++#define DF_1_NOKSYMS 0x00080000 ++#define DF_1_NOHDR 0x00100000 ++#define DF_1_EDITED 0x00200000 ++#define DF_1_NORELOC 0x00400000 ++#define DF_1_SYMINTPOSE 0x00800000 ++#define DF_1_GLOBAUDIT 0x01000000 ++#define DF_1_SINGLETON 0x02000000 ++ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++ ++#define DF_P1_LAZYLOAD 0x00000001 ++#define DF_P1_GROUPPERM 0x00000002 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Half vd_version; ++ Elf32_Half vd_flags; ++ Elf32_Half vd_ndx; ++ Elf32_Half vd_cnt; ++ Elf32_Word vd_hash; ++ Elf32_Word vd_aux; ++ Elf32_Word vd_next; ++} Elf32_Verdef; ++ ++typedef struct { ++ Elf64_Half vd_version; ++ Elf64_Half vd_flags; ++ Elf64_Half vd_ndx; ++ Elf64_Half vd_cnt; ++ Elf64_Word vd_hash; ++ Elf64_Word vd_aux; ++ Elf64_Word vd_next; ++} Elf64_Verdef; ++ ++ ++ ++#define VER_DEF_NONE 0 ++#define VER_DEF_CURRENT 1 ++#define VER_DEF_NUM 2 ++ ++ ++#define VER_FLG_BASE 0x1 ++#define VER_FLG_WEAK 0x2 ++ ++ ++#define VER_NDX_LOCAL 0 ++#define VER_NDX_GLOBAL 1 ++#define VER_NDX_LORESERVE 0xff00 ++#define VER_NDX_ELIMINATE 0xff01 ++ ++ ++ ++typedef struct { ++ Elf32_Word vda_name; ++ Elf32_Word vda_next; ++} Elf32_Verdaux; ++ ++typedef struct { ++ Elf64_Word vda_name; ++ Elf64_Word vda_next; ++} Elf64_Verdaux; ++ ++ ++ ++ ++typedef struct { ++ Elf32_Half vn_version; ++ Elf32_Half vn_cnt; ++ Elf32_Word vn_file; ++ Elf32_Word vn_aux; ++ Elf32_Word vn_next; ++} Elf32_Verneed; ++ ++typedef struct { ++ Elf64_Half vn_version; ++ Elf64_Half vn_cnt; ++ Elf64_Word vn_file; ++ Elf64_Word vn_aux; ++ Elf64_Word vn_next; ++} Elf64_Verneed; ++ ++ ++ ++#define VER_NEED_NONE 0 ++#define VER_NEED_CURRENT 1 ++#define VER_NEED_NUM 2 ++ ++ ++ ++typedef struct { ++ Elf32_Word vna_hash; ++ Elf32_Half vna_flags; ++ Elf32_Half vna_other; ++ Elf32_Word vna_name; ++ Elf32_Word vna_next; ++} Elf32_Vernaux; ++ ++typedef struct { ++ Elf64_Word vna_hash; ++ Elf64_Half vna_flags; ++ Elf64_Half vna_other; ++ Elf64_Word vna_name; ++ Elf64_Word vna_next; ++} Elf64_Vernaux; ++ ++ ++ ++#define VER_FLG_WEAK 0x2 ++ ++ ++ ++typedef struct { ++ uint32_t a_type; ++ union { ++ uint32_t a_val; ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct { ++ uint64_t a_type; ++ union { ++ uint64_t a_val; ++ } a_un; ++} Elf64_auxv_t; ++ ++ ++ ++#define AT_NULL 0 ++#define AT_IGNORE 1 ++#define AT_EXECFD 2 ++#define AT_PHDR 3 ++#define AT_PHENT 4 ++#define AT_PHNUM 5 ++#define AT_PAGESZ 6 ++#define AT_BASE 7 ++#define AT_FLAGS 8 ++#define AT_ENTRY 9 ++#define AT_NOTELF 10 ++#define AT_UID 11 ++#define AT_EUID 12 ++#define AT_GID 13 ++#define AT_EGID 14 ++#define AT_CLKTCK 17 ++ ++ ++#define AT_PLATFORM 15 ++#define AT_HWCAP 16 ++ ++ ++ ++ ++#define AT_FPUCW 18 ++ ++ ++#define AT_DCACHEBSIZE 19 ++#define AT_ICACHEBSIZE 20 ++#define AT_UCACHEBSIZE 21 ++ ++ ++ ++#define AT_IGNOREPPC 22 ++ ++#define AT_SECURE 23 ++ ++#define AT_BASE_PLATFORM 24 ++ ++#define AT_RANDOM 25 ++ ++#define AT_HWCAP2 26 ++ ++#define AT_EXECFN 31 ++ ++ ++ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++ ++ ++#define AT_L1I_CACHESHAPE 34 ++#define AT_L1D_CACHESHAPE 35 ++#define AT_L2_CACHESHAPE 36 ++#define AT_L3_CACHESHAPE 37 ++ ++ ++ ++ ++typedef struct { ++ Elf32_Word n_namesz; ++ Elf32_Word n_descsz; ++ Elf32_Word n_type; ++} Elf32_Nhdr; ++ ++typedef struct { ++ Elf64_Word n_namesz; ++ Elf64_Word n_descsz; ++ Elf64_Word n_type; ++} Elf64_Nhdr; ++ ++ ++ ++ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++ ++ ++ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++#define NT_GNU_ABI_TAG 1 ++#define ELF_NOTE_ABI NT_GNU_ABI_TAG ++ ++ ++ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++#define NT_GNU_BUILD_ID 3 ++#define NT_GNU_GOLD_VERSION 4 ++ ++ ++ ++typedef struct { ++ Elf32_Xword m_value; ++ Elf32_Word m_info; ++ Elf32_Word m_poffset; ++ Elf32_Half m_repeat; ++ Elf32_Half m_stride; ++} Elf32_Move; ++ ++typedef struct { ++ Elf64_Xword m_value; ++ Elf64_Xword m_info; ++ Elf64_Xword m_poffset; ++ Elf64_Half m_repeat; ++ Elf64_Half m_stride; ++} Elf64_Move; ++ ++ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++#define EF_CPU32 0x00810000 ++ ++#define R_68K_NONE 0 ++#define R_68K_32 1 ++#define R_68K_16 2 ++#define R_68K_8 3 ++#define R_68K_PC32 4 ++#define R_68K_PC16 5 ++#define R_68K_PC8 6 ++#define R_68K_GOT32 7 ++#define R_68K_GOT16 8 ++#define R_68K_GOT8 9 ++#define R_68K_GOT32O 10 ++#define R_68K_GOT16O 11 ++#define R_68K_GOT8O 12 ++#define R_68K_PLT32 13 ++#define R_68K_PLT16 14 ++#define R_68K_PLT8 15 ++#define R_68K_PLT32O 16 ++#define R_68K_PLT16O 17 ++#define R_68K_PLT8O 18 ++#define R_68K_COPY 19 ++#define R_68K_GLOB_DAT 20 ++#define R_68K_JMP_SLOT 21 ++#define R_68K_RELATIVE 22 ++#define R_68K_NUM 23 ++ ++#define R_386_NONE 0 ++#define R_386_32 1 ++#define R_386_PC32 2 ++#define R_386_GOT32 3 ++#define R_386_PLT32 4 ++#define R_386_COPY 5 ++#define R_386_GLOB_DAT 6 ++#define R_386_JMP_SLOT 7 ++#define R_386_RELATIVE 8 ++#define R_386_GOTOFF 9 ++#define R_386_GOTPC 10 ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 ++#define R_386_TLS_IE 15 ++#define R_386_TLS_GOTIE 16 ++#define R_386_TLS_LE 17 ++#define R_386_TLS_GD 18 ++#define R_386_TLS_LDM 19 ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 ++#define R_386_TLS_GD_PUSH 25 ++#define R_386_TLS_GD_CALL 26 ++#define R_386_TLS_GD_POP 27 ++#define R_386_TLS_LDM_32 28 ++#define R_386_TLS_LDM_PUSH 29 ++#define R_386_TLS_LDM_CALL 30 ++#define R_386_TLS_LDM_POP 31 ++#define R_386_TLS_LDO_32 32 ++#define R_386_TLS_IE_32 33 ++#define R_386_TLS_LE_32 34 ++#define R_386_TLS_DTPMOD32 35 ++#define R_386_TLS_DTPOFF32 36 ++#define R_386_TLS_TPOFF32 37 ++#define R_386_SIZE32 38 ++#define R_386_TLS_GOTDESC 39 ++#define R_386_TLS_DESC_CALL 40 ++#define R_386_TLS_DESC 41 ++#define R_386_IRELATIVE 42 ++#define R_386_NUM 43 ++ ++ ++ ++ ++ ++#define STT_SPARC_REGISTER 13 ++ ++ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 ++#define EF_SPARC_SUN_US1 0x000200 ++#define EF_SPARC_HAL_R1 0x000400 ++#define EF_SPARC_SUN_US3 0x000800 ++ ++ ++ ++#define R_SPARC_NONE 0 ++#define R_SPARC_8 1 ++#define R_SPARC_16 2 ++#define R_SPARC_32 3 ++#define R_SPARC_DISP8 4 ++#define R_SPARC_DISP16 5 ++#define R_SPARC_DISP32 6 ++#define R_SPARC_WDISP30 7 ++#define R_SPARC_WDISP22 8 ++#define R_SPARC_HI22 9 ++#define R_SPARC_22 10 ++#define R_SPARC_13 11 ++#define R_SPARC_LO10 12 ++#define R_SPARC_GOT10 13 ++#define R_SPARC_GOT13 14 ++#define R_SPARC_GOT22 15 ++#define R_SPARC_PC10 16 ++#define R_SPARC_PC22 17 ++#define R_SPARC_WPLT30 18 ++#define R_SPARC_COPY 19 ++#define R_SPARC_GLOB_DAT 20 ++#define R_SPARC_JMP_SLOT 21 ++#define R_SPARC_RELATIVE 22 ++#define R_SPARC_UA32 23 ++ ++ ++ ++#define R_SPARC_PLT32 24 ++#define R_SPARC_HIPLT22 25 ++#define R_SPARC_LOPLT10 26 ++#define R_SPARC_PCPLT32 27 ++#define R_SPARC_PCPLT22 28 ++#define R_SPARC_PCPLT10 29 ++#define R_SPARC_10 30 ++#define R_SPARC_11 31 ++#define R_SPARC_64 32 ++#define R_SPARC_OLO10 33 ++#define R_SPARC_HH22 34 ++#define R_SPARC_HM10 35 ++#define R_SPARC_LM22 36 ++#define R_SPARC_PC_HH22 37 ++#define R_SPARC_PC_HM10 38 ++#define R_SPARC_PC_LM22 39 ++#define R_SPARC_WDISP16 40 ++#define R_SPARC_WDISP19 41 ++#define R_SPARC_GLOB_JMP 42 ++#define R_SPARC_7 43 ++#define R_SPARC_5 44 ++#define R_SPARC_6 45 ++#define R_SPARC_DISP64 46 ++#define R_SPARC_PLT64 47 ++#define R_SPARC_HIX22 48 ++#define R_SPARC_LOX10 49 ++#define R_SPARC_H44 50 ++#define R_SPARC_M44 51 ++#define R_SPARC_L44 52 ++#define R_SPARC_REGISTER 53 ++#define R_SPARC_UA64 54 ++#define R_SPARC_UA16 55 ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++#define R_SPARC_GOTDATA_HIX22 80 ++#define R_SPARC_GOTDATA_LOX10 81 ++#define R_SPARC_GOTDATA_OP_HIX22 82 ++#define R_SPARC_GOTDATA_OP_LOX10 83 ++#define R_SPARC_GOTDATA_OP 84 ++#define R_SPARC_H34 85 ++#define R_SPARC_SIZE32 86 ++#define R_SPARC_SIZE64 87 ++#define R_SPARC_GNU_VTINHERIT 250 ++#define R_SPARC_GNU_VTENTRY 251 ++#define R_SPARC_REV32 252 ++ ++#define R_SPARC_NUM 253 ++ ++ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++ ++#define EF_MIPS_NOREORDER 1 ++#define EF_MIPS_PIC 2 ++#define EF_MIPS_CPIC 4 ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 ++ ++ ++ ++#define EF_MIPS_ARCH_1 0x00000000 ++#define EF_MIPS_ARCH_2 0x10000000 ++#define EF_MIPS_ARCH_3 0x20000000 ++#define EF_MIPS_ARCH_4 0x30000000 ++#define EF_MIPS_ARCH_5 0x40000000 ++#define EF_MIPS_ARCH_32 0x50000000 ++#define EF_MIPS_ARCH_64 0x60000000 ++#define EF_MIPS_ARCH_32R2 0x70000000 ++#define EF_MIPS_ARCH_64R2 0x80000000 ++ ++ ++#define E_MIPS_ARCH_1 0x00000000 ++#define E_MIPS_ARCH_2 0x10000000 ++#define E_MIPS_ARCH_3 0x20000000 ++#define E_MIPS_ARCH_4 0x30000000 ++#define E_MIPS_ARCH_5 0x40000000 ++#define E_MIPS_ARCH_32 0x50000000 ++#define E_MIPS_ARCH_64 0x60000000 ++ ++ ++ ++#define SHN_MIPS_ACOMMON 0xff00 ++#define SHN_MIPS_TEXT 0xff01 ++#define SHN_MIPS_DATA 0xff02 ++#define SHN_MIPS_SCOMMON 0xff03 ++#define SHN_MIPS_SUNDEFINED 0xff04 ++ ++ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 ++#define SHT_MIPS_GPTAB 0x70000003 ++#define SHT_MIPS_UCODE 0x70000004 ++#define SHT_MIPS_DEBUG 0x70000005 ++#define SHT_MIPS_REGINFO 0x70000006 ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++ ++ ++#define SHF_MIPS_GPREL 0x10000000 ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++ ++ ++ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_PLT 0x8 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++ ++ ++typedef union { ++ struct { ++ Elf32_Word gt_current_g_value; ++ Elf32_Word gt_unused; ++ } gt_header; ++ struct { ++ Elf32_Word gt_g_value; ++ Elf32_Word gt_bytes; ++ } gt_entry; ++} Elf32_gptab; ++ ++ ++ ++typedef struct { ++ Elf32_Word ri_gprmask; ++ Elf32_Word ri_cprmask[4]; ++ Elf32_Sword ri_gp_value; ++} Elf32_RegInfo; ++ ++ ++ ++typedef struct { ++ unsigned char kind; ++ ++ unsigned char size; ++ Elf32_Section section; ++ ++ Elf32_Word info; ++} Elf_Options; ++ ++ ++ ++#define ODK_NULL 0 ++#define ODK_REGINFO 1 ++#define ODK_EXCEPTIONS 2 ++#define ODK_PAD 3 ++#define ODK_HWPATCH 4 ++#define ODK_FILL 5 ++#define ODK_TAGS 6 ++#define ODK_HWAND 7 ++#define ODK_HWOR 8 ++ ++ ++ ++#define OEX_FPU_MIN 0x1f ++#define OEX_FPU_MAX 0x1f00 ++#define OEX_PAGE0 0x10000 ++#define OEX_SMM 0x20000 ++#define OEX_FPDBUG 0x40000 ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++ ++ ++#define OHW_R4KEOP 0x1 ++#define OHW_R8KPFETCH 0x2 ++#define OHW_R5KEOP 0x4 ++#define OHW_R5KCVTL 0x8 ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++ ++ ++typedef struct { ++ Elf32_Word hwp_flags1; ++ Elf32_Word hwp_flags2; ++} Elf_Options_Hw; ++ ++ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++ ++ ++#define R_MIPS_NONE 0 ++#define R_MIPS_16 1 ++#define R_MIPS_32 2 ++#define R_MIPS_REL32 3 ++#define R_MIPS_26 4 ++#define R_MIPS_HI16 5 ++#define R_MIPS_LO16 6 ++#define R_MIPS_GPREL16 7 ++#define R_MIPS_LITERAL 8 ++#define R_MIPS_GOT16 9 ++#define R_MIPS_PC16 10 ++#define R_MIPS_CALL16 11 ++#define R_MIPS_GPREL32 12 ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++#define R_MIPS_TLS_DTPMOD32 38 ++#define R_MIPS_TLS_DTPREL32 39 ++#define R_MIPS_TLS_DTPMOD64 40 ++#define R_MIPS_TLS_DTPREL64 41 ++#define R_MIPS_TLS_GD 42 ++#define R_MIPS_TLS_LDM 43 ++#define R_MIPS_TLS_DTPREL_HI16 44 ++#define R_MIPS_TLS_DTPREL_LO16 45 ++#define R_MIPS_TLS_GOTTPREL 46 ++#define R_MIPS_TLS_TPREL32 47 ++#define R_MIPS_TLS_TPREL64 48 ++#define R_MIPS_TLS_TPREL_HI16 49 ++#define R_MIPS_TLS_TPREL_LO16 50 ++#define R_MIPS_GLOB_DAT 51 ++#define R_MIPS_COPY 126 ++#define R_MIPS_JUMP_SLOT 127 ++ ++#define R_MIPS_NUM 128 ++ ++ ++ ++#define PT_MIPS_REGINFO 0x70000000 ++#define PT_MIPS_RTPROC 0x70000001 ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 ++#define DT_MIPS_TIME_STAMP 0x70000002 ++#define DT_MIPS_ICHECKSUM 0x70000003 ++#define DT_MIPS_IVERSION 0x70000004 ++#define DT_MIPS_FLAGS 0x70000005 ++#define DT_MIPS_BASE_ADDRESS 0x70000006 ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 ++#define DT_MIPS_LIBLIST 0x70000009 ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a ++#define DT_MIPS_CONFLICTNO 0x7000000b ++#define DT_MIPS_LIBLISTNO 0x70000010 ++#define DT_MIPS_SYMTABNO 0x70000011 ++#define DT_MIPS_UNREFEXTNO 0x70000012 ++#define DT_MIPS_GOTSYM 0x70000013 ++#define DT_MIPS_HIPAGENO 0x70000014 ++#define DT_MIPS_RLD_MAP 0x70000016 ++#define DT_MIPS_DELTA_CLASS 0x70000017 ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 ++ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a ++ ++#define DT_MIPS_DELTA_RELOC 0x7000001b ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c ++ ++#define DT_MIPS_DELTA_SYM 0x7000001d ++ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e ++ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 ++ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 ++ ++#define DT_MIPS_CXX_FLAGS 0x70000022 ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 ++#define DT_MIPS_INTERFACE 0x7000002a ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d ++ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e ++ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f ++#define DT_MIPS_GP_VALUE 0x70000030 ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 ++ ++#define DT_MIPS_PLTGOT 0x70000032 ++ ++#define DT_MIPS_RWPLT 0x70000034 ++#define DT_MIPS_NUM 0x35 ++ ++ ++ ++#define RHF_NONE 0 ++#define RHF_QUICKSTART (1 << 0) ++#define RHF_NOTPOT (1 << 1) ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++ ++ ++typedef struct ++{ ++ Elf32_Word l_name; ++ Elf32_Word l_time_stamp; ++ Elf32_Word l_checksum; ++ Elf32_Word l_version; ++ Elf32_Word l_flags; ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; ++ Elf64_Word l_time_stamp; ++ Elf64_Word l_checksum; ++ Elf64_Word l_version; ++ Elf64_Word l_flags; ++} Elf64_Lib; ++ ++ ++ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) ++#define LL_IGNORE_INT_VER (1 << 1) ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++ ++ ++ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 ++#define EF_PARISC_EXT 0x00020000 ++#define EF_PARISC_LSB 0x00040000 ++#define EF_PARISC_WIDE 0x00080000 ++#define EF_PARISC_NO_KABP 0x00100000 ++ ++#define EF_PARISC_LAZYSWAP 0x00400000 ++#define EF_PARISC_ARCH 0x0000ffff ++ ++ ++ ++#define EFA_PARISC_1_0 0x020b ++#define EFA_PARISC_1_1 0x0210 ++#define EFA_PARISC_2_0 0x0214 ++ ++ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 ++ ++#define SHN_PARISC_HUGE_COMMON 0xff01 ++ ++ ++ ++#define SHT_PARISC_EXT 0x70000000 ++#define SHT_PARISC_UNWIND 0x70000001 ++#define SHT_PARISC_DOC 0x70000002 ++ ++ ++ ++#define SHF_PARISC_SHORT 0x20000000 ++#define SHF_PARISC_HUGE 0x40000000 ++#define SHF_PARISC_SBP 0x80000000 ++ ++ ++ ++#define STT_PARISC_MILLICODE 13 ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++ ++ ++#define R_PARISC_NONE 0 ++#define R_PARISC_DIR32 1 ++#define R_PARISC_DIR21L 2 ++#define R_PARISC_DIR17R 3 ++#define R_PARISC_DIR17F 4 ++#define R_PARISC_DIR14R 6 ++#define R_PARISC_PCREL32 9 ++#define R_PARISC_PCREL21L 10 ++#define R_PARISC_PCREL17R 11 ++#define R_PARISC_PCREL17F 12 ++#define R_PARISC_PCREL14R 14 ++#define R_PARISC_DPREL21L 18 ++#define R_PARISC_DPREL14R 22 ++#define R_PARISC_GPREL21L 26 ++#define R_PARISC_GPREL14R 30 ++#define R_PARISC_LTOFF21L 34 ++#define R_PARISC_LTOFF14R 38 ++#define R_PARISC_SECREL32 41 ++#define R_PARISC_SEGBASE 48 ++#define R_PARISC_SEGREL32 49 ++#define R_PARISC_PLTOFF21L 50 ++#define R_PARISC_PLTOFF14R 54 ++#define R_PARISC_LTOFF_FPTR32 57 ++#define R_PARISC_LTOFF_FPTR21L 58 ++#define R_PARISC_LTOFF_FPTR14R 62 ++#define R_PARISC_FPTR64 64 ++#define R_PARISC_PLABEL32 65 ++#define R_PARISC_PLABEL21L 66 ++#define R_PARISC_PLABEL14R 70 ++#define R_PARISC_PCREL64 72 ++#define R_PARISC_PCREL22F 74 ++#define R_PARISC_PCREL14WR 75 ++#define R_PARISC_PCREL14DR 76 ++#define R_PARISC_PCREL16F 77 ++#define R_PARISC_PCREL16WF 78 ++#define R_PARISC_PCREL16DF 79 ++#define R_PARISC_DIR64 80 ++#define R_PARISC_DIR14WR 83 ++#define R_PARISC_DIR14DR 84 ++#define R_PARISC_DIR16F 85 ++#define R_PARISC_DIR16WF 86 ++#define R_PARISC_DIR16DF 87 ++#define R_PARISC_GPREL64 88 ++#define R_PARISC_GPREL14WR 91 ++#define R_PARISC_GPREL14DR 92 ++#define R_PARISC_GPREL16F 93 ++#define R_PARISC_GPREL16WF 94 ++#define R_PARISC_GPREL16DF 95 ++#define R_PARISC_LTOFF64 96 ++#define R_PARISC_LTOFF14WR 99 ++#define R_PARISC_LTOFF14DR 100 ++#define R_PARISC_LTOFF16F 101 ++#define R_PARISC_LTOFF16WF 102 ++#define R_PARISC_LTOFF16DF 103 ++#define R_PARISC_SECREL64 104 ++#define R_PARISC_SEGREL64 112 ++#define R_PARISC_PLTOFF14WR 115 ++#define R_PARISC_PLTOFF14DR 116 ++#define R_PARISC_PLTOFF16F 117 ++#define R_PARISC_PLTOFF16WF 118 ++#define R_PARISC_PLTOFF16DF 119 ++#define R_PARISC_LTOFF_FPTR64 120 ++#define R_PARISC_LTOFF_FPTR14WR 123 ++#define R_PARISC_LTOFF_FPTR14DR 124 ++#define R_PARISC_LTOFF_FPTR16F 125 ++#define R_PARISC_LTOFF_FPTR16WF 126 ++#define R_PARISC_LTOFF_FPTR16DF 127 ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 ++#define R_PARISC_IPLT 129 ++#define R_PARISC_EPLT 130 ++#define R_PARISC_TPREL32 153 ++#define R_PARISC_TPREL21L 154 ++#define R_PARISC_TPREL14R 158 ++#define R_PARISC_LTOFF_TP21L 162 ++#define R_PARISC_LTOFF_TP14R 166 ++#define R_PARISC_LTOFF_TP14F 167 ++#define R_PARISC_TPREL64 216 ++#define R_PARISC_TPREL14WR 219 ++#define R_PARISC_TPREL14DR 220 ++#define R_PARISC_TPREL16F 221 ++#define R_PARISC_TPREL16WF 222 ++#define R_PARISC_TPREL16DF 223 ++#define R_PARISC_LTOFF_TP64 224 ++#define R_PARISC_LTOFF_TP14WR 227 ++#define R_PARISC_LTOFF_TP14DR 228 ++#define R_PARISC_LTOFF_TP16F 229 ++#define R_PARISC_LTOFF_TP16WF 230 ++#define R_PARISC_LTOFF_TP16DF 231 ++#define R_PARISC_GNU_VTENTRY 232 ++#define R_PARISC_GNU_VTINHERIT 233 ++#define R_PARISC_TLS_GD21L 234 ++#define R_PARISC_TLS_GD14R 235 ++#define R_PARISC_TLS_GDCALL 236 ++#define R_PARISC_TLS_LDM21L 237 ++#define R_PARISC_TLS_LDM14R 238 ++#define R_PARISC_TLS_LDMCALL 239 ++#define R_PARISC_TLS_LDO21L 240 ++#define R_PARISC_TLS_LDO14R 241 ++#define R_PARISC_TLS_DTPMOD32 242 ++#define R_PARISC_TLS_DTPMOD64 243 ++#define R_PARISC_TLS_DTPOFF32 244 ++#define R_PARISC_TLS_DTPOFF64 245 ++#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L ++#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R ++#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L ++#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R ++#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 ++#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 ++#define R_PARISC_HIRESERVE 255 ++ ++ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++ ++ ++ ++ ++#define EF_ALPHA_32BIT 1 ++#define EF_ALPHA_CANRELAX 2 ++ ++ ++ ++ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++ ++#define STO_ALPHA_NOPV 0x80 ++#define STO_ALPHA_STD_GPLOAD 0x88 ++ ++ ++ ++#define R_ALPHA_NONE 0 ++#define R_ALPHA_REFLONG 1 ++#define R_ALPHA_REFQUAD 2 ++#define R_ALPHA_GPREL32 3 ++#define R_ALPHA_LITERAL 4 ++#define R_ALPHA_LITUSE 5 ++#define R_ALPHA_GPDISP 6 ++#define R_ALPHA_BRADDR 7 ++#define R_ALPHA_HINT 8 ++#define R_ALPHA_SREL16 9 ++#define R_ALPHA_SREL32 10 ++#define R_ALPHA_SREL64 11 ++#define R_ALPHA_GPRELHIGH 17 ++#define R_ALPHA_GPRELLOW 18 ++#define R_ALPHA_GPREL16 19 ++#define R_ALPHA_COPY 24 ++#define R_ALPHA_GLOB_DAT 25 ++#define R_ALPHA_JMP_SLOT 26 ++#define R_ALPHA_RELATIVE 27 ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++ ++#define R_ALPHA_NUM 46 ++ ++ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++ ++#define DT_ALPHA_PLTRO (DT_LOPROC + 0) ++#define DT_ALPHA_NUM 1 ++ ++ ++ ++ ++#define EF_PPC_EMB 0x80000000 ++ ++ ++#define EF_PPC_RELOCATABLE 0x00010000 ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 ++ ++ ++ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 ++#define R_PPC_ADDR24 2 ++#define R_PPC_ADDR16 3 ++#define R_PPC_ADDR16_LO 4 ++#define R_PPC_ADDR16_HI 5 ++#define R_PPC_ADDR16_HA 6 ++#define R_PPC_ADDR14 7 ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 ++#define R_PPC_REL14 11 ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++ ++#define R_PPC_TLS 67 ++#define R_PPC_DTPMOD32 68 ++#define R_PPC_TPREL16 69 ++#define R_PPC_TPREL16_LO 70 ++#define R_PPC_TPREL16_HI 71 ++#define R_PPC_TPREL16_HA 72 ++#define R_PPC_TPREL32 73 ++#define R_PPC_DTPREL16 74 ++#define R_PPC_DTPREL16_LO 75 ++#define R_PPC_DTPREL16_HI 76 ++#define R_PPC_DTPREL16_HA 77 ++#define R_PPC_DTPREL32 78 ++#define R_PPC_GOT_TLSGD16 79 ++#define R_PPC_GOT_TLSGD16_LO 80 ++#define R_PPC_GOT_TLSGD16_HI 81 ++#define R_PPC_GOT_TLSGD16_HA 82 ++#define R_PPC_GOT_TLSLD16 83 ++#define R_PPC_GOT_TLSLD16_LO 84 ++#define R_PPC_GOT_TLSLD16_HI 85 ++#define R_PPC_GOT_TLSLD16_HA 86 ++#define R_PPC_GOT_TPREL16 87 ++#define R_PPC_GOT_TPREL16_LO 88 ++#define R_PPC_GOT_TPREL16_HI 89 ++#define R_PPC_GOT_TPREL16_HA 90 ++#define R_PPC_GOT_DTPREL16 91 ++#define R_PPC_GOT_DTPREL16_LO 92 ++#define R_PPC_GOT_DTPREL16_HI 93 ++#define R_PPC_GOT_DTPREL16_HA 94 ++ ++ ++ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 ++ ++ ++#define R_PPC_DIAB_SDA21_LO 180 ++#define R_PPC_DIAB_SDA21_HI 181 ++#define R_PPC_DIAB_SDA21_HA 182 ++#define R_PPC_DIAB_RELSDA_LO 183 ++#define R_PPC_DIAB_RELSDA_HI 184 ++#define R_PPC_DIAB_RELSDA_HA 185 ++ ++ ++#define R_PPC_IRELATIVE 248 ++ ++ ++#define R_PPC_REL16 249 ++#define R_PPC_REL16_LO 250 ++#define R_PPC_REL16_HI 251 ++#define R_PPC_REL16_HA 252 ++ ++ ++ ++#define R_PPC_TOC16 255 ++ ++ ++#define DT_PPC_GOT (DT_LOPROC + 0) ++#define DT_PPC_NUM 1 ++ ++ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 ++#define R_PPC64_ADDR24 R_PPC_ADDR24 ++#define R_PPC64_ADDR16 R_PPC_ADDR16 ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA ++#define R_PPC64_ADDR14 R_PPC_ADDR14 ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 ++#define R_PPC64_REL14 R_PPC_REL14 ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 ++#define R_PPC64_ADDR64 38 ++#define R_PPC64_ADDR16_HIGHER 39 ++#define R_PPC64_ADDR16_HIGHERA 40 ++#define R_PPC64_ADDR16_HIGHEST 41 ++#define R_PPC64_ADDR16_HIGHESTA 42 ++#define R_PPC64_UADDR64 43 ++#define R_PPC64_REL64 44 ++#define R_PPC64_PLT64 45 ++#define R_PPC64_PLTREL64 46 ++#define R_PPC64_TOC16 47 ++#define R_PPC64_TOC16_LO 48 ++#define R_PPC64_TOC16_HI 49 ++#define R_PPC64_TOC16_HA 50 ++#define R_PPC64_TOC 51 ++#define R_PPC64_PLTGOT16 52 ++#define R_PPC64_PLTGOT16_LO 53 ++#define R_PPC64_PLTGOT16_HI 54 ++#define R_PPC64_PLTGOT16_HA 55 ++ ++#define R_PPC64_ADDR16_DS 56 ++#define R_PPC64_ADDR16_LO_DS 57 ++#define R_PPC64_GOT16_DS 58 ++#define R_PPC64_GOT16_LO_DS 59 ++#define R_PPC64_PLT16_LO_DS 60 ++#define R_PPC64_SECTOFF_DS 61 ++#define R_PPC64_SECTOFF_LO_DS 62 ++#define R_PPC64_TOC16_DS 63 ++#define R_PPC64_TOC16_LO_DS 64 ++#define R_PPC64_PLTGOT16_DS 65 ++#define R_PPC64_PLTGOT16_LO_DS 66 ++ ++ ++#define R_PPC64_TLS 67 ++#define R_PPC64_DTPMOD64 68 ++#define R_PPC64_TPREL16 69 ++#define R_PPC64_TPREL16_LO 70 ++#define R_PPC64_TPREL16_HI 71 ++#define R_PPC64_TPREL16_HA 72 ++#define R_PPC64_TPREL64 73 ++#define R_PPC64_DTPREL16 74 ++#define R_PPC64_DTPREL16_LO 75 ++#define R_PPC64_DTPREL16_HI 76 ++#define R_PPC64_DTPREL16_HA 77 ++#define R_PPC64_DTPREL64 78 ++#define R_PPC64_GOT_TLSGD16 79 ++#define R_PPC64_GOT_TLSGD16_LO 80 ++#define R_PPC64_GOT_TLSGD16_HI 81 ++#define R_PPC64_GOT_TLSGD16_HA 82 ++#define R_PPC64_GOT_TLSLD16 83 ++#define R_PPC64_GOT_TLSLD16_LO 84 ++#define R_PPC64_GOT_TLSLD16_HI 85 ++#define R_PPC64_GOT_TLSLD16_HA 86 ++#define R_PPC64_GOT_TPREL16_DS 87 ++#define R_PPC64_GOT_TPREL16_LO_DS 88 ++#define R_PPC64_GOT_TPREL16_HI 89 ++#define R_PPC64_GOT_TPREL16_HA 90 ++#define R_PPC64_GOT_DTPREL16_DS 91 ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 ++#define R_PPC64_GOT_DTPREL16_HI 93 ++#define R_PPC64_GOT_DTPREL16_HA 94 ++#define R_PPC64_TPREL16_DS 95 ++#define R_PPC64_TPREL16_LO_DS 96 ++#define R_PPC64_TPREL16_HIGHER 97 ++#define R_PPC64_TPREL16_HIGHERA 98 ++#define R_PPC64_TPREL16_HIGHEST 99 ++#define R_PPC64_TPREL16_HIGHESTA 100 ++#define R_PPC64_DTPREL16_DS 101 ++#define R_PPC64_DTPREL16_LO_DS 102 ++#define R_PPC64_DTPREL16_HIGHER 103 ++#define R_PPC64_DTPREL16_HIGHERA 104 ++#define R_PPC64_DTPREL16_HIGHEST 105 ++#define R_PPC64_DTPREL16_HIGHESTA 106 ++ ++ ++#define R_PPC64_JMP_IREL 247 ++#define R_PPC64_IRELATIVE 248 ++#define R_PPC64_REL16 249 ++#define R_PPC64_REL16_LO 250 ++#define R_PPC64_REL16_HI 251 ++#define R_PPC64_REL16_HA 252 ++ ++ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_OPD (DT_LOPROC + 1) ++#define DT_PPC64_OPDSZ (DT_LOPROC + 2) ++#define DT_PPC64_NUM 3 ++ ++ ++ ++ ++ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++#define EF_ARM_SOFT_FLOAT 0x200 ++#define EF_ARM_VFP_FLOAT 0x400 ++#define EF_ARM_MAVERICK_FLOAT 0x800 ++ ++#define EF_ARM_ABI_FLOAT_SOFT 0x200 ++#define EF_ARM_ABI_FLOAT_HARD 0x400 ++ ++ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++ ++#define EF_ARM_BE8 0x00800000 ++#define EF_ARM_LE8 0x00400000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++#define EF_ARM_EABI_VER3 0x03000000 ++#define EF_ARM_EABI_VER4 0x04000000 ++#define EF_ARM_EABI_VER5 0x05000000 ++ ++ ++#define STT_ARM_TFUNC STT_LOPROC ++#define STT_ARM_16BIT STT_HIPROC ++ ++ ++#define SHF_ARM_ENTRYSECT 0x10000000 ++#define SHF_ARM_COMDEF 0x80000000 ++ ++ ++ ++#define PF_ARM_SB 0x10000000 ++ ++#define PF_ARM_PI 0x20000000 ++#define PF_ARM_ABS 0x40000000 ++ ++ ++#define PT_ARM_EXIDX (PT_LOPROC + 1) ++ ++ ++#define SHT_ARM_EXIDX (SHT_LOPROC + 1) ++#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) ++#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) ++ ++ ++#define R_AARCH64_NONE 0 ++#define R_AARCH64_ABS64 257 ++#define R_AARCH64_ABS32 258 ++#define R_AARCH64_COPY 1024 ++#define R_AARCH64_GLOB_DAT 1025 ++#define R_AARCH64_JUMP_SLOT 1026 ++#define R_AARCH64_RELATIVE 1027 ++#define R_AARCH64_TLS_DTPMOD64 1028 ++#define R_AARCH64_TLS_DTPREL64 1029 ++#define R_AARCH64_TLS_TPREL64 1030 ++#define R_AARCH64_TLSDESC 1031 ++ ++ ++#define R_ARM_NONE 0 ++#define R_ARM_PC24 1 ++#define R_ARM_ABS32 2 ++#define R_ARM_REL32 3 ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 ++#define R_ARM_ABS12 6 ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_TLS_DESC 13 ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_TLS_DTPMOD32 17 ++#define R_ARM_TLS_DTPOFF32 18 ++#define R_ARM_TLS_TPOFF32 19 ++#define R_ARM_COPY 20 ++#define R_ARM_GLOB_DAT 21 ++#define R_ARM_JUMP_SLOT 22 ++#define R_ARM_RELATIVE 23 ++#define R_ARM_GOTOFF 24 ++#define R_ARM_GOTPC 25 ++#define R_ARM_GOT32 26 ++#define R_ARM_PLT32 27 ++#define R_ARM_CALL 28 ++#define R_ARM_JUMP24 29 ++#define R_ARM_THM_JUMP24 30 ++#define R_ARM_BASE_ABS 31 ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_TARGET1 38 ++#define R_ARM_SBREL31 39 ++#define R_ARM_V4BX 40 ++#define R_ARM_TARGET2 41 ++#define R_ARM_PREL31 42 ++#define R_ARM_MOVW_ABS_NC 43 ++#define R_ARM_MOVT_ABS 44 ++#define R_ARM_MOVW_PREL_NC 45 ++#define R_ARM_MOVT_PREL 46 ++#define R_ARM_THM_MOVW_ABS_NC 47 ++#define R_ARM_THM_MOVT_ABS 48 ++#define R_ARM_THM_MOVW_PREL_NC 49 ++#define R_ARM_THM_MOVT_PREL 50 ++#define R_ARM_THM_JUMP19 51 ++#define R_ARM_THM_JUMP6 52 ++#define R_ARM_THM_ALU_PREL_11_0 53 ++#define R_ARM_THM_PC12 54 ++#define R_ARM_ABS32_NOI 55 ++#define R_ARM_REL32_NOI 56 ++#define R_ARM_ALU_PC_G0_NC 57 ++#define R_ARM_ALU_PC_G0 58 ++#define R_ARM_ALU_PC_G1_NC 59 ++#define R_ARM_ALU_PC_G1 60 ++#define R_ARM_ALU_PC_G2 61 ++#define R_ARM_LDR_PC_G1 62 ++#define R_ARM_LDR_PC_G2 63 ++#define R_ARM_LDRS_PC_G0 64 ++#define R_ARM_LDRS_PC_G1 65 ++#define R_ARM_LDRS_PC_G2 66 ++#define R_ARM_LDC_PC_G0 67 ++#define R_ARM_LDC_PC_G1 68 ++#define R_ARM_LDC_PC_G2 69 ++#define R_ARM_ALU_SB_G0_NC 70 ++#define R_ARM_ALU_SB_G0 71 ++#define R_ARM_ALU_SB_G1_NC 72 ++#define R_ARM_ALU_SB_G1 73 ++#define R_ARM_ALU_SB_G2 74 ++#define R_ARM_LDR_SB_G0 75 ++#define R_ARM_LDR_SB_G1 76 ++#define R_ARM_LDR_SB_G2 77 ++#define R_ARM_LDRS_SB_G0 78 ++#define R_ARM_LDRS_SB_G1 79 ++#define R_ARM_LDRS_SB_G2 80 ++#define R_ARM_LDC_SB_G0 81 ++#define R_ARM_LDC_SB_G1 82 ++#define R_ARM_LDC_SB_G2 83 ++#define R_ARM_MOVW_BREL_NC 84 ++#define R_ARM_MOVT_BREL 85 ++#define R_ARM_MOVW_BREL 86 ++#define R_ARM_THM_MOVW_BREL_NC 87 ++#define R_ARM_THM_MOVT_BREL 88 ++#define R_ARM_THM_MOVW_BREL 89 ++#define R_ARM_TLS_GOTDESC 90 ++#define R_ARM_TLS_CALL 91 ++#define R_ARM_TLS_DESCSEQ 92 ++#define R_ARM_THM_TLS_CALL 93 ++#define R_ARM_PLT32_ABS 94 ++#define R_ARM_GOT_ABS 95 ++#define R_ARM_GOT_PREL 96 ++#define R_ARM_GOT_BREL12 97 ++#define R_ARM_GOTOFF12 98 ++#define R_ARM_GOTRELAX 99 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 ++#define R_ARM_THM_PC9 103 ++#define R_ARM_TLS_GD32 104 ++ ++#define R_ARM_TLS_LDM32 105 ++ ++#define R_ARM_TLS_LDO32 106 ++ ++#define R_ARM_TLS_IE32 107 ++ ++#define R_ARM_TLS_LE32 108 ++#define R_ARM_TLS_LDO12 109 ++#define R_ARM_TLS_LE12 110 ++#define R_ARM_TLS_IE12GP 111 ++#define R_ARM_ME_TOO 128 ++#define R_ARM_THM_TLS_DESCSEQ 129 ++#define R_ARM_THM_TLS_DESCSEQ16 129 ++#define R_ARM_THM_TLS_DESCSEQ32 130 ++#define R_ARM_THM_GOT_BREL12 131 ++#define R_ARM_IRELATIVE 160 ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++ ++#define R_ARM_NUM 256 ++ ++ ++ ++ ++#define EF_IA_64_MASKOS 0x0000000f ++#define EF_IA_64_ABI64 0x00000010 ++#define EF_IA_64_ARCH 0xff000000 ++ ++ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) ++#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) ++#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) ++#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) ++ ++ ++#define PF_IA_64_NORECOV 0x80000000 ++ ++ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) ++ ++ ++#define SHF_IA_64_SHORT 0x10000000 ++#define SHF_IA_64_NORECOV 0x20000000 ++ ++ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++ ++#define R_IA64_NONE 0x00 ++#define R_IA64_IMM14 0x21 ++#define R_IA64_IMM22 0x22 ++#define R_IA64_IMM64 0x23 ++#define R_IA64_DIR32MSB 0x24 ++#define R_IA64_DIR32LSB 0x25 ++#define R_IA64_DIR64MSB 0x26 ++#define R_IA64_DIR64LSB 0x27 ++#define R_IA64_GPREL22 0x2a ++#define R_IA64_GPREL64I 0x2b ++#define R_IA64_GPREL32MSB 0x2c ++#define R_IA64_GPREL32LSB 0x2d ++#define R_IA64_GPREL64MSB 0x2e ++#define R_IA64_GPREL64LSB 0x2f ++#define R_IA64_LTOFF22 0x32 ++#define R_IA64_LTOFF64I 0x33 ++#define R_IA64_PLTOFF22 0x3a ++#define R_IA64_PLTOFF64I 0x3b ++#define R_IA64_PLTOFF64MSB 0x3e ++#define R_IA64_PLTOFF64LSB 0x3f ++#define R_IA64_FPTR64I 0x43 ++#define R_IA64_FPTR32MSB 0x44 ++#define R_IA64_FPTR32LSB 0x45 ++#define R_IA64_FPTR64MSB 0x46 ++#define R_IA64_FPTR64LSB 0x47 ++#define R_IA64_PCREL60B 0x48 ++#define R_IA64_PCREL21B 0x49 ++#define R_IA64_PCREL21M 0x4a ++#define R_IA64_PCREL21F 0x4b ++#define R_IA64_PCREL32MSB 0x4c ++#define R_IA64_PCREL32LSB 0x4d ++#define R_IA64_PCREL64MSB 0x4e ++#define R_IA64_PCREL64LSB 0x4f ++#define R_IA64_LTOFF_FPTR22 0x52 ++#define R_IA64_LTOFF_FPTR64I 0x53 ++#define R_IA64_LTOFF_FPTR32MSB 0x54 ++#define R_IA64_LTOFF_FPTR32LSB 0x55 ++#define R_IA64_LTOFF_FPTR64MSB 0x56 ++#define R_IA64_LTOFF_FPTR64LSB 0x57 ++#define R_IA64_SEGREL32MSB 0x5c ++#define R_IA64_SEGREL32LSB 0x5d ++#define R_IA64_SEGREL64MSB 0x5e ++#define R_IA64_SEGREL64LSB 0x5f ++#define R_IA64_SECREL32MSB 0x64 ++#define R_IA64_SECREL32LSB 0x65 ++#define R_IA64_SECREL64MSB 0x66 ++#define R_IA64_SECREL64LSB 0x67 ++#define R_IA64_REL32MSB 0x6c ++#define R_IA64_REL32LSB 0x6d ++#define R_IA64_REL64MSB 0x6e ++#define R_IA64_REL64LSB 0x6f ++#define R_IA64_LTV32MSB 0x74 ++#define R_IA64_LTV32LSB 0x75 ++#define R_IA64_LTV64MSB 0x76 ++#define R_IA64_LTV64LSB 0x77 ++#define R_IA64_PCREL21BI 0x79 ++#define R_IA64_PCREL22 0x7a ++#define R_IA64_PCREL64I 0x7b ++#define R_IA64_IPLTMSB 0x80 ++#define R_IA64_IPLTLSB 0x81 ++#define R_IA64_COPY 0x84 ++#define R_IA64_SUB 0x85 ++#define R_IA64_LTOFF22X 0x86 ++#define R_IA64_LDXMOV 0x87 ++#define R_IA64_TPREL14 0x91 ++#define R_IA64_TPREL22 0x92 ++#define R_IA64_TPREL64I 0x93 ++#define R_IA64_TPREL64MSB 0x96 ++#define R_IA64_TPREL64LSB 0x97 ++#define R_IA64_LTOFF_TPREL22 0x9a ++#define R_IA64_DTPMOD64MSB 0xa6 ++#define R_IA64_DTPMOD64LSB 0xa7 ++#define R_IA64_LTOFF_DTPMOD22 0xaa ++#define R_IA64_DTPREL14 0xb1 ++#define R_IA64_DTPREL22 0xb2 ++#define R_IA64_DTPREL64I 0xb3 ++#define R_IA64_DTPREL32MSB 0xb4 ++#define R_IA64_DTPREL32LSB 0xb5 ++#define R_IA64_DTPREL64MSB 0xb6 ++#define R_IA64_DTPREL64LSB 0xb7 ++#define R_IA64_LTOFF_DTPREL22 0xba ++ ++ ++ ++ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++ ++#define R_SH_NUM 256 ++ ++ ++ ++#define R_390_NONE 0 ++#define R_390_8 1 ++#define R_390_12 2 ++#define R_390_16 3 ++#define R_390_32 4 ++#define R_390_PC32 5 ++#define R_390_GOT12 6 ++#define R_390_GOT32 7 ++#define R_390_PLT32 8 ++#define R_390_COPY 9 ++#define R_390_GLOB_DAT 10 ++#define R_390_JMP_SLOT 11 ++#define R_390_RELATIVE 12 ++#define R_390_GOTOFF32 13 ++#define R_390_GOTPC 14 ++#define R_390_GOT16 15 ++#define R_390_PC16 16 ++#define R_390_PC16DBL 17 ++#define R_390_PLT16DBL 18 ++#define R_390_PC32DBL 19 ++#define R_390_PLT32DBL 20 ++#define R_390_GOTPCDBL 21 ++#define R_390_64 22 ++#define R_390_PC64 23 ++#define R_390_GOT64 24 ++#define R_390_PLT64 25 ++#define R_390_GOTENT 26 ++#define R_390_GOTOFF16 27 ++#define R_390_GOTOFF64 28 ++#define R_390_GOTPLT12 29 ++#define R_390_GOTPLT16 30 ++#define R_390_GOTPLT32 31 ++#define R_390_GOTPLT64 32 ++#define R_390_GOTPLTENT 33 ++#define R_390_PLTOFF16 34 ++#define R_390_PLTOFF32 35 ++#define R_390_PLTOFF64 36 ++#define R_390_TLS_LOAD 37 ++#define R_390_TLS_GDCALL 38 ++ ++#define R_390_TLS_LDCALL 39 ++ ++#define R_390_TLS_GD32 40 ++ ++#define R_390_TLS_GD64 41 ++ ++#define R_390_TLS_GOTIE12 42 ++ ++#define R_390_TLS_GOTIE32 43 ++ ++#define R_390_TLS_GOTIE64 44 ++ ++#define R_390_TLS_LDM32 45 ++ ++#define R_390_TLS_LDM64 46 ++ ++#define R_390_TLS_IE32 47 ++ ++#define R_390_TLS_IE64 48 ++ ++#define R_390_TLS_IEENT 49 ++ ++#define R_390_TLS_LE32 50 ++ ++#define R_390_TLS_LE64 51 ++ ++#define R_390_TLS_LDO32 52 ++ ++#define R_390_TLS_LDO64 53 ++ ++#define R_390_TLS_DTPMOD 54 ++#define R_390_TLS_DTPOFF 55 ++#define R_390_TLS_TPOFF 56 ++ ++#define R_390_20 57 ++#define R_390_GOT20 58 ++#define R_390_GOTPLT20 59 ++#define R_390_TLS_GOTIE20 60 ++ ++ ++#define R_390_NUM 61 ++ ++ ++ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++ ++ ++#define R_X86_64_NONE 0 ++#define R_X86_64_64 1 ++#define R_X86_64_PC32 2 ++#define R_X86_64_GOT32 3 ++#define R_X86_64_PLT32 4 ++#define R_X86_64_COPY 5 ++#define R_X86_64_GLOB_DAT 6 ++#define R_X86_64_JUMP_SLOT 7 ++#define R_X86_64_RELATIVE 8 ++#define R_X86_64_GOTPCREL 9 ++ ++#define R_X86_64_32 10 ++#define R_X86_64_32S 11 ++#define R_X86_64_16 12 ++#define R_X86_64_PC16 13 ++#define R_X86_64_8 14 ++#define R_X86_64_PC8 15 ++#define R_X86_64_DTPMOD64 16 ++#define R_X86_64_DTPOFF64 17 ++#define R_X86_64_TPOFF64 18 ++#define R_X86_64_TLSGD 19 ++ ++#define R_X86_64_TLSLD 20 ++ ++#define R_X86_64_DTPOFF32 21 ++#define R_X86_64_GOTTPOFF 22 ++ ++#define R_X86_64_TPOFF32 23 ++#define R_X86_64_PC64 24 ++#define R_X86_64_GOTOFF64 25 ++#define R_X86_64_GOTPC32 26 ++#define R_X86_64_GOT64 27 ++#define R_X86_64_GOTPCREL64 28 ++#define R_X86_64_GOTPC64 29 ++#define R_X86_64_GOTPLT64 30 ++#define R_X86_64_PLTOFF64 31 ++#define R_X86_64_SIZE32 32 ++#define R_X86_64_SIZE64 33 ++ ++#define R_X86_64_GOTPC32_TLSDESC 34 ++#define R_X86_64_TLSDESC_CALL 35 ++ ++#define R_X86_64_TLSDESC 36 ++#define R_X86_64_IRELATIVE 37 ++#define R_X86_64_RELATIVE64 38 ++#define R_X86_64_NUM 39 ++ ++ ++ ++#define R_MN10300_NONE 0 ++#define R_MN10300_32 1 ++#define R_MN10300_16 2 ++#define R_MN10300_8 3 ++#define R_MN10300_PCREL32 4 ++#define R_MN10300_PCREL16 5 ++#define R_MN10300_PCREL8 6 ++#define R_MN10300_GNU_VTINHERIT 7 ++#define R_MN10300_GNU_VTENTRY 8 ++#define R_MN10300_24 9 ++#define R_MN10300_GOTPC32 10 ++#define R_MN10300_GOTPC16 11 ++#define R_MN10300_GOTOFF32 12 ++#define R_MN10300_GOTOFF24 13 ++#define R_MN10300_GOTOFF16 14 ++#define R_MN10300_PLT32 15 ++#define R_MN10300_PLT16 16 ++#define R_MN10300_GOT32 17 ++#define R_MN10300_GOT24 18 ++#define R_MN10300_GOT16 19 ++#define R_MN10300_COPY 20 ++#define R_MN10300_GLOB_DAT 21 ++#define R_MN10300_JMP_SLOT 22 ++#define R_MN10300_RELATIVE 23 ++ ++#define R_MN10300_NUM 24 ++ ++ ++ ++#define R_M32R_NONE 0 ++#define R_M32R_16 1 ++#define R_M32R_32 2 ++#define R_M32R_24 3 ++#define R_M32R_10_PCREL 4 ++#define R_M32R_18_PCREL 5 ++#define R_M32R_26_PCREL 6 ++#define R_M32R_HI16_ULO 7 ++#define R_M32R_HI16_SLO 8 ++#define R_M32R_LO16 9 ++#define R_M32R_SDA16 10 ++#define R_M32R_GNU_VTINHERIT 11 ++#define R_M32R_GNU_VTENTRY 12 ++ ++#define R_M32R_16_RELA 33 ++#define R_M32R_32_RELA 34 ++#define R_M32R_24_RELA 35 ++#define R_M32R_10_PCREL_RELA 36 ++#define R_M32R_18_PCREL_RELA 37 ++#define R_M32R_26_PCREL_RELA 38 ++#define R_M32R_HI16_ULO_RELA 39 ++#define R_M32R_HI16_SLO_RELA 40 ++#define R_M32R_LO16_RELA 41 ++#define R_M32R_SDA16_RELA 42 ++#define R_M32R_RELA_GNU_VTINHERIT 43 ++#define R_M32R_RELA_GNU_VTENTRY 44 ++#define R_M32R_REL32 45 ++ ++#define R_M32R_GOT24 48 ++#define R_M32R_26_PLTREL 49 ++#define R_M32R_COPY 50 ++#define R_M32R_GLOB_DAT 51 ++#define R_M32R_JMP_SLOT 52 ++#define R_M32R_RELATIVE 53 ++#define R_M32R_GOTOFF 54 ++#define R_M32R_GOTPC24 55 ++#define R_M32R_GOT16_HI_ULO 56 ++ ++#define R_M32R_GOT16_HI_SLO 57 ++ ++#define R_M32R_GOT16_LO 58 ++#define R_M32R_GOTPC_HI_ULO 59 ++ ++#define R_M32R_GOTPC_HI_SLO 60 ++ ++#define R_M32R_GOTPC_LO 61 ++ ++#define R_M32R_GOTOFF_HI_ULO 62 ++ ++#define R_M32R_GOTOFF_HI_SLO 63 ++ ++#define R_M32R_GOTOFF_LO 64 ++#define R_M32R_NUM 256 ++ ++#define R_MICROBLAZE_NONE 0 ++#define R_MICROBLAZE_32 1 ++#define R_MICROBLAZE_32_PCREL 2 ++#define R_MICROBLAZE_64_PCREL 3 ++#define R_MICROBLAZE_32_PCREL_LO 4 ++#define R_MICROBLAZE_64 5 ++#define R_MICROBLAZE_32_LO 6 ++#define R_MICROBLAZE_SRO32 7 ++#define R_MICROBLAZE_SRW32 8 ++#define R_MICROBLAZE_64_NONE 9 ++#define R_MICROBLAZE_32_SYM_OP_SYM 10 ++#define R_MICROBLAZE_GNU_VTINHERIT 11 ++#define R_MICROBLAZE_GNU_VTENTRY 12 ++#define R_MICROBLAZE_GOTPC_64 13 ++#define R_MICROBLAZE_GOT_64 14 ++#define R_MICROBLAZE_PLT_64 15 ++#define R_MICROBLAZE_REL 16 ++#define R_MICROBLAZE_JUMP_SLOT 17 ++#define R_MICROBLAZE_GLOB_DAT 18 ++#define R_MICROBLAZE_GOTOFF_64 19 ++#define R_MICROBLAZE_GOTOFF_32 20 ++#define R_MICROBLAZE_COPY 21 ++#define R_MICROBLAZE_TLS 22 ++#define R_MICROBLAZE_TLSGD 23 ++#define R_MICROBLAZE_TLSLD 24 ++#define R_MICROBLAZE_TLSDTPMOD32 25 ++#define R_MICROBLAZE_TLSDTPREL32 26 ++#define R_MICROBLAZE_TLSDTPREL64 27 ++#define R_MICROBLAZE_TLSGOTTPREL32 28 ++#define R_MICROBLAZE_TLSTPREL32 29 ++ ++#ifdef __cplusplus ++} ++#endif ++ ++ ++#endif diff --git a/target/linux/patches/3.18.16/sgidefs.patch b/target/linux/patches/3.18.16/sgidefs.patch new file mode 100644 index 000000000..f00a284d9 --- /dev/null +++ b/target/linux/patches/3.18.16/sgidefs.patch @@ -0,0 +1,18 @@ +diff -Nur linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h +--- linux-3.11.5.orig/arch/mips/include/uapi/asm/sgidefs.h 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/arch/mips/include/uapi/asm/sgidefs.h 2013-11-08 22:01:28.000000000 +0100 +@@ -11,14 +11,6 @@ + #define __ASM_SGIDEFS_H + + /* +- * Using a Linux compiler for building Linux seems logic but not to +- * everybody. +- */ +-#ifndef __linux__ +-#error Use a Linux compiler or give up. +-#endif +- +-/* + * Definitions for the ISA levels + * + * With the introduction of MIPS32 / MIPS64 instruction sets definitions diff --git a/target/linux/patches/3.18.16/sortext.patch b/target/linux/patches/3.18.16/sortext.patch new file mode 100644 index 000000000..8fd4e1d6b --- /dev/null +++ b/target/linux/patches/3.18.16/sortext.patch @@ -0,0 +1,33 @@ +diff -Nur linux-3.12.6.orig/arch/arm/Kconfig linux-3.12.6/arch/arm/Kconfig +--- linux-3.12.6.orig/arch/arm/Kconfig 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/arch/arm/Kconfig 2013-12-28 19:29:33.000000000 +0100 +@@ -6,7 +6,6 @@ + select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST + select ARCH_HAVE_CUSTOM_GPIO_H + select ARCH_WANT_IPC_PARSE_VERSION +- select BUILDTIME_EXTABLE_SORT if MMU + select CLONE_BACKWARDS + select CPU_PM if (SUSPEND || CPU_IDLE) + select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && !CPU_BIG_ENDIAN && MMU +diff -Nur linux-3.12.6.orig/arch/mips/Kconfig linux-3.12.6/arch/mips/Kconfig +--- linux-3.12.6.orig/arch/mips/Kconfig 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/arch/mips/Kconfig 2013-12-28 19:30:06.000000000 +0100 +@@ -35,7 +35,6 @@ + select HAVE_MEMBLOCK_NODE_MAP + select ARCH_DISCARD_MEMBLOCK + select GENERIC_SMP_IDLE_THREAD +- select BUILDTIME_EXTABLE_SORT + select GENERIC_CLOCKEVENTS + select GENERIC_CMOS_UPDATE + select HAVE_MOD_ARCH_SPECIFIC +diff -Nur linux-3.12.6.orig/arch/x86/Kconfig linux-3.12.6/arch/x86/Kconfig +--- linux-3.12.6.orig/arch/x86/Kconfig 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/arch/x86/Kconfig 2013-12-28 19:29:50.000000000 +0100 +@@ -100,7 +100,6 @@ + select GENERIC_SMP_IDLE_THREAD + select ARCH_WANT_IPC_PARSE_VERSION if X86_32 + select HAVE_ARCH_SECCOMP_FILTER +- select BUILDTIME_EXTABLE_SORT + select GENERIC_CMOS_UPDATE + select HAVE_ARCH_SOFT_DIRTY + select CLOCKSOURCE_WATCHDOG diff --git a/target/linux/patches/3.18.16/startup.patch b/target/linux/patches/3.18.16/startup.patch new file mode 100644 index 000000000..d396b75e4 --- /dev/null +++ b/target/linux/patches/3.18.16/startup.patch @@ -0,0 +1,37 @@ +diff -Nur linux-3.13.3.orig/init/main.c linux-3.13.3/init/main.c +--- linux-3.13.3.orig/init/main.c 2014-02-13 23:00:14.000000000 +0100 ++++ linux-3.13.3/init/main.c 2014-02-17 11:35:14.000000000 +0100 +@@ -916,6 +917,8 @@ + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + pr_err("Warning: unable to open an initial console.\n"); + ++ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); ++ + (void) sys_dup(0); + (void) sys_dup(0); + /* +diff -Nur linux-3.13.6.orig/init/initramfs.c linux-3.13.6/init/initramfs.c +--- linux-3.13.6.orig/init/initramfs.c 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/init/initramfs.c 2014-03-15 12:11:31.882731916 +0100 +@@ -622,6 +622,9 @@ + */ + load_default_modules(); + } ++#ifdef CONFIG_DEVTMPFS_MOUNT ++ devtmpfs_mount("dev"); ++#endif + return 0; + } + rootfs_initcall(populate_rootfs); +diff -Nur linux-3.13.6.orig/init/main.c linux-3.13.6/init/main.c +--- linux-3.13.6.orig/init/main.c 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/init/main.c 2014-03-15 12:13:16.459024452 +0100 +@@ -924,7 +924,7 @@ + */ + + if (!ramdisk_execute_command) +- ramdisk_execute_command = "/init"; ++ ramdisk_execute_command = "/sbin/init"; + + if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { + ramdisk_execute_command = NULL; diff --git a/target/linux/patches/3.18.16/wlan-cf.patch b/target/linux/patches/3.18.16/wlan-cf.patch new file mode 100644 index 000000000..fc20759e2 --- /dev/null +++ b/target/linux/patches/3.18.16/wlan-cf.patch @@ -0,0 +1,11 @@ +diff -Nur linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c +--- linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c 2011-05-19 06:06:34.000000000 +0200 ++++ linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c 2011-09-12 02:46:26.987984145 +0200 +@@ -623,6 +623,7 @@ + static struct pcmcia_device_id hostap_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), ++ PCMCIA_DEVICE_MANF_CARD(0x0004, 0x2003), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), + PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), + PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), diff --git a/target/linux/patches/3.18.16/xargs.patch b/target/linux/patches/3.18.16/xargs.patch new file mode 100644 index 000000000..2c7b3df59 --- /dev/null +++ b/target/linux/patches/3.18.16/xargs.patch @@ -0,0 +1,12 @@ +diff -Nur linux-3.12.6.orig/scripts/Makefile.modpost linux-3.12.6/scripts/Makefile.modpost +--- linux-3.12.6.orig/scripts/Makefile.modpost 2013-12-20 16:51:33.000000000 +0100 ++++ linux-3.12.6/scripts/Makefile.modpost 2014-01-25 14:55:33.000000000 +0100 +@@ -60,7 +60,7 @@ + modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers + + # Step 1), find all modules listed in $(MODVERDIR)/ +-MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs -r grep -h '\.ko$$' | sort -u ++MODLISTCMD := find $(MODVERDIR) -name '*.mod' | xargs grep -h '\.ko$$' | sort -u + __modules := $(shell $(MODLISTCMD)) + modules := $(patsubst %.o,%.ko, $(wildcard $(__modules:.ko=.o))) + diff --git a/target/linux/patches/3.18.16/yaffs2.patch b/target/linux/patches/3.18.16/yaffs2.patch new file mode 100644 index 000000000..2ade5bf7e --- /dev/null +++ b/target/linux/patches/3.18.16/yaffs2.patch @@ -0,0 +1,16559 @@ +diff -Nur linux-3.18.14.orig/fs/Kconfig linux-3.18.14/fs/Kconfig +--- linux-3.18.14.orig/fs/Kconfig 2015-05-20 17:04:50.000000000 +0200 ++++ linux-3.18.14/fs/Kconfig 2015-06-14 21:22:55.000000000 +0200 +@@ -191,6 +191,7 @@ + source "fs/befs/Kconfig" + source "fs/bfs/Kconfig" + source "fs/efs/Kconfig" ++source "fs/yaffs2/Kconfig" + source "fs/jffs2/Kconfig" + # UBIFS File system configuration + source "fs/ubifs/Kconfig" +diff -Nur linux-3.18.14.orig/fs/Makefile linux-3.18.14/fs/Makefile +--- linux-3.18.14.orig/fs/Makefile 2015-05-20 17:04:50.000000000 +0200 ++++ linux-3.18.14/fs/Makefile 2015-06-14 21:22:55.000000000 +0200 +@@ -126,3 +126,4 @@ + obj-$(CONFIG_CEPH_FS) += ceph/ + obj-$(CONFIG_PSTORE) += pstore/ + obj-$(CONFIG_EFIVAR_FS) += efivarfs/ ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ +diff -Nur linux-3.18.14.orig/fs/yaffs2/Kconfig linux-3.18.14/fs/yaffs2/Kconfig +--- linux-3.18.14.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/Kconfig 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,171 @@ ++# ++# yaffs file system configurations ++# ++ ++config YAFFS_FS ++ tristate "yaffs2 file system support" ++ default n ++ depends on MTD_BLOCK ++ select YAFFS_YAFFS1 ++ select YAFFS_YAFFS2 ++ help ++ yaffs2, or Yet Another Flash File System, is a file system ++ optimised for NAND Flash chips. ++ ++ To compile the yaffs2 file system support as a module, choose M ++ here: the module will be called yaffs2. ++ ++ If unsure, say N. ++ ++ Further information on yaffs2 is available at ++ . ++ ++config YAFFS_YAFFS1 ++ bool "512 byte / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs1 support -- yaffs for 512 byte / page devices ++ ++ Not needed for 2K-page devices. ++ ++ If unsure, say Y. ++ ++config YAFFS_9BYTE_TAGS ++ bool "Use older-style on-NAND data format with pageStatus byte" ++ depends on YAFFS_YAFFS1 ++ default n ++ help ++ ++ Older-style on-NAND data format has a "pageStatus" byte to record ++ chunk/page state. This byte is zero when the page is discarded. ++ Choose this option if you have existing on-NAND data using this ++ format that you need to continue to support. New data written ++ also uses the older-style format. Note: Use of this option ++ generally requires that MTD's oob layout be adjusted to use the ++ older-style format. See notes on tags formats and MTD versions ++ in yaffs_mtdif1.c. ++ ++ If unsure, say N. ++ ++config YAFFS_DOES_ECC ++ bool "Lets yaffs do its own ECC" ++ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This enables yaffs to use its own ECC functions instead of using ++ the ones from the generic MTD-NAND driver. ++ ++ If unsure, say N. ++ ++config YAFFS_ECC_WRONG_ORDER ++ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" ++ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This makes yaffs_ecc.c use the same ecc byte order as Steven ++ Hill's nand_ecc.c. If not set, then you get the same ecc byte ++ order as SmartMedia. ++ ++ If unsure, say N. ++ ++config YAFFS_YAFFS2 ++ bool "2048 byte (or larger) / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices ++ ++ If unsure, say Y. ++ ++config YAFFS_AUTO_YAFFS2 ++ bool "Autoselect yaffs2 format" ++ depends on YAFFS_YAFFS2 ++ default y ++ help ++ Without this, you need to explicitely use yaffs2 as the file ++ system type. With this, you can say "yaffs" and yaffs or yaffs2 ++ will be used depending on the device page size (yaffs on ++ 512-byte page devices, yaffs2 on 2K page devices). ++ ++ If unsure, say Y. ++ ++config YAFFS_DISABLE_TAGS_ECC ++ bool "Disable yaffs from doing ECC on tags by default" ++ depends on YAFFS_FS && YAFFS_YAFFS2 ++ default n ++ help ++ This defaults yaffs to using its own ECC calculations on tags instead of ++ just relying on the MTD. ++ This behavior can also be overridden with tags_ecc_on and ++ tags_ecc_off mount options. ++ ++ If unsure, say N. ++ ++config YAFFS_ALWAYS_CHECK_CHUNK_ERASED ++ bool "Force chunk erase check" ++ depends on YAFFS_FS ++ default n ++ help ++ Normally yaffs only checks chunks before writing until an erased ++ chunk is found. This helps to detect any partially written ++ chunks that might have happened due to power loss. ++ ++ Enabling this forces on the test that chunks are erased in flash ++ before writing to them. This takes more time but is potentially ++ a bit more secure. ++ ++ Suggest setting Y during development and ironing out driver ++ issues etc. Suggest setting to N if you want faster writing. ++ ++ If unsure, say Y. ++ ++config YAFFS_EMPTY_LOST_AND_FOUND ++ bool "Empty lost and found on boot" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is enabled then the contents of lost and found is ++ automatically dumped at mount. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BLOCK_REFRESHING ++ bool "Disable yaffs2 block refreshing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then block refreshing is disabled. ++ Block refreshing infrequently refreshes the oldest block in ++ a yaffs2 file system. This mechanism helps to refresh flash to ++ mitigate against data loss. This is particularly useful for MLC. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BACKGROUND ++ bool "Disable yaffs2 background processing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then background processing is disabled. ++ Background processing makes many foreground activities faster. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BAD_BLOCK_MARKING ++ bool "Disable yaffs2 bad block marking" ++ depends on YAFFS_FS ++ default n ++ help ++ Useful during early flash bring up to prevent problems causing ++ lots of bad block marking. ++ ++ If unsure, say N. ++ ++config YAFFS_XATTR ++ bool "Enable yaffs2 xattr support" ++ depends on YAFFS_FS ++ default y ++ help ++ If this is set then yaffs2 will provide xattr support. ++ If unsure, say Y. +diff -Nur linux-3.18.14.orig/fs/yaffs2/Makefile linux-3.18.14/fs/yaffs2/Makefile +--- linux-3.18.14.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/Makefile 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,18 @@ ++# ++# Makefile for the linux YAFFS filesystem routines. ++# ++ ++obj-$(CONFIG_YAFFS_FS) += yaffs.o ++ ++yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o ++yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o ++yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o ++yaffs-y += yaffs_mtdif.o ++yaffs-y += yaffs_nameval.o yaffs_attribs.o ++yaffs-y += yaffs_allocator.o ++yaffs-y += yaffs_yaffs1.o ++yaffs-y += yaffs_yaffs2.o ++yaffs-y += yaffs_bitmap.o ++yaffs-y += yaffs_summary.o ++yaffs-y += yaffs_verify.o ++ +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_allocator.c linux-3.18.14/fs/yaffs2/yaffs_allocator.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_allocator.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,357 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_allocator.h" ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yportenv.h" ++ ++/* ++ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks ++ * of approx 100 objects that are themn allocated singly. ++ * This is basically a simplified slab allocator. ++ * ++ * We don't use the Linux slab allocator because slab does not allow ++ * us to dump all the objects in one hit when we do a umount and tear ++ * down all the tnodes and objects. slab requires that we first free ++ * the individual objects. ++ * ++ * Once yaffs has been mainlined I shall try to motivate for a change ++ * to slab to provide the extra features we need here. ++ */ ++ ++struct yaffs_tnode_list { ++ struct yaffs_tnode_list *next; ++ struct yaffs_tnode *tnodes; ++}; ++ ++struct yaffs_obj_list { ++ struct yaffs_obj_list *next; ++ struct yaffs_obj *objects; ++}; ++ ++struct yaffs_allocator { ++ int n_tnodes_created; ++ struct yaffs_tnode *free_tnodes; ++ int n_free_tnodes; ++ struct yaffs_tnode_list *alloc_tnode_list; ++ ++ int n_obj_created; ++ struct list_head free_objs; ++ int n_free_objects; ++ ++ struct yaffs_obj_list *allocated_obj_list; ++}; ++ ++static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->alloc_tnode_list) { ++ tmp = allocator->alloc_tnode_list->next; ++ ++ kfree(allocator->alloc_tnode_list->tnodes); ++ kfree(allocator->alloc_tnode_list); ++ allocator->alloc_tnode_list = tmp; ++ } ++ ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->alloc_tnode_list = NULL; ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ int i; ++ struct yaffs_tnode *new_tnodes; ++ u8 *mem; ++ struct yaffs_tnode *curr; ++ struct yaffs_tnode *next; ++ struct yaffs_tnode_list *tnl; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_tnodes < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); ++ mem = (u8 *) new_tnodes; ++ ++ if (!new_tnodes) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: Could not allocate Tnodes"); ++ return YAFFS_FAIL; ++ } ++ ++ /* New hookup for wide tnodes */ ++ for (i = 0; i < n_tnodes - 1; i++) { ++ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; ++ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; ++ curr->internal[0] = next; ++ } ++ ++ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; ++ curr->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = (struct yaffs_tnode *)mem; ++ ++ allocator->n_free_tnodes += n_tnodes; ++ allocator->n_tnodes_created += n_tnodes; ++ ++ /* Now add this bunch of tnodes to a list for freeing up. ++ * NB If we can't add this to the management list it isn't fatal ++ * but it just means we can't free this bunch of tnodes later. ++ */ ++ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); ++ if (!tnl) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Could not add tnodes to management list"); ++ return YAFFS_FAIL; ++ } else { ++ tnl->tnodes = new_tnodes; ++ tnl->next = allocator->alloc_tnode_list; ++ allocator->alloc_tnode_list = tnl; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (!allocator) { ++ BUG(); ++ return NULL; ++ } ++ ++ /* If there are none left make more */ ++ if (!allocator->free_tnodes) ++ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); ++ ++ if (allocator->free_tnodes) { ++ tn = allocator->free_tnodes; ++ allocator->free_tnodes = allocator->free_tnodes->internal[0]; ++ allocator->n_free_tnodes--; ++ } ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ if (tn) { ++ tn->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = tn; ++ allocator->n_free_tnodes++; ++ } ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++/*--------------- yaffs_obj alloaction ------------------------ ++ * ++ * Free yaffs_objs are stored in a list using obj->siblings. ++ * The blocks of allocated objects are stored in a linked list. ++ */ ++ ++static void yaffs_init_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->allocated_obj_list = NULL; ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++} ++ ++static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ struct yaffs_obj_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->allocated_obj_list) { ++ tmp = allocator->allocated_obj_list->next; ++ kfree(allocator->allocated_obj_list->objects); ++ kfree(allocator->allocated_obj_list); ++ allocator->allocated_obj_list = tmp; ++ } ++ ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++ allocator->n_obj_created = 0; ++} ++ ++static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ int i; ++ struct yaffs_obj *new_objs; ++ struct yaffs_obj_list *list; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_obj < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); ++ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); ++ ++ if (!new_objs || !list) { ++ kfree(new_objs); ++ new_objs = NULL; ++ kfree(list); ++ list = NULL; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Could not allocate more objects"); ++ return YAFFS_FAIL; ++ } ++ ++ /* Hook them into the free list */ ++ for (i = 0; i < n_obj; i++) ++ list_add(&new_objs[i].siblings, &allocator->free_objs); ++ ++ allocator->n_free_objects += n_obj; ++ allocator->n_obj_created += n_obj; ++ ++ /* Now add this bunch of Objects to a list for freeing up. */ ++ ++ list->objects = new_objs; ++ list->next = allocator->allocated_obj_list; ++ allocator->allocated_obj_list = list; ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct list_head *lh; ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return obj; ++ } ++ ++ /* If there are none left make more */ ++ if (list_empty(&allocator->free_objs)) ++ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); ++ ++ if (!list_empty(&allocator->free_objs)) { ++ lh = allocator->free_objs.next; ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ list_del_init(lh); ++ allocator->n_free_objects--; ++ } ++ ++ return obj; ++} ++ ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ /* Link into the free list. */ ++ list_add(&obj->siblings, &allocator->free_objs); ++ allocator->n_free_objects++; ++} ++ ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ ++ if (!dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ yaffs_deinit_raw_tnodes(dev); ++ yaffs_deinit_raw_objs(dev); ++ kfree(dev->allocator); ++ dev->allocator = NULL; ++} ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator; ++ ++ if (dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); ++ if (allocator) { ++ dev->allocator = allocator; ++ yaffs_init_raw_tnodes(dev); ++ yaffs_init_raw_objs(dev); ++ } ++} ++ +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_allocator.h linux-3.18.14/fs/yaffs2/yaffs_allocator.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_allocator.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,30 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ALLOCATOR_H__ ++#define __YAFFS_ALLOCATOR_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_attribs.c linux-3.18.14/fs/yaffs2/yaffs_attribs.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_attribs.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,132 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) ++#define IATTR_UID ia_uid ++#define IATTR_GID ia_gid ++#else ++#define IATTR_UID ia_uid.val ++#define IATTR_GID ia_gid.val ++#endif ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) ++{ ++ obj->yst_uid = oh->yst_uid; ++ obj->yst_gid = oh->yst_gid; ++ obj->yst_atime = oh->yst_atime; ++ obj->yst_mtime = oh->yst_mtime; ++ obj->yst_ctime = oh->yst_ctime; ++ obj->yst_rdev = oh->yst_rdev; ++} ++ ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) ++{ ++ oh->yst_uid = obj->yst_uid; ++ oh->yst_gid = obj->yst_gid; ++ oh->yst_atime = obj->yst_atime; ++ oh->yst_mtime = obj->yst_mtime; ++ oh->yst_ctime = obj->yst_ctime; ++ oh->yst_rdev = obj->yst_rdev; ++ ++} ++ ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) ++{ ++ obj->yst_mtime = Y_CURRENT_TIME; ++ if (do_a) ++ obj->yst_atime = obj->yst_mtime; ++ if (do_c) ++ obj->yst_ctime = obj->yst_mtime; ++} ++ ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) ++{ ++ yaffs_load_current_time(obj, 1, 1); ++ obj->yst_rdev = rdev; ++ obj->yst_uid = uid; ++ obj->yst_gid = gid; ++} ++ ++static loff_t yaffs_get_file_size(struct yaffs_obj *obj) ++{ ++ YCHAR *alias = NULL; ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return obj->variant.file_variant.file_size; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = obj->variant.symlink_variant.alias; ++ if (!alias) ++ return 0; ++ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); ++ default: ++ return 0; ++ } ++} ++ ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = attr->ia_valid; ++ ++ if (valid & ATTR_MODE) ++ obj->yst_mode = attr->ia_mode; ++ if (valid & ATTR_UID) ++ obj->yst_uid = attr->IATTR_UID; ++ if (valid & ATTR_GID) ++ obj->yst_gid = attr->IATTR_GID; ++ ++ if (valid & ATTR_ATIME) ++ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); ++ if (valid & ATTR_CTIME) ++ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); ++ if (valid & ATTR_MTIME) ++ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); ++ ++ if (valid & ATTR_SIZE) ++ yaffs_resize_file(obj, attr->ia_size); ++ ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ return YAFFS_OK; ++ ++} ++ ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = 0; ++ ++ attr->ia_mode = obj->yst_mode; ++ valid |= ATTR_MODE; ++ attr->IATTR_UID = obj->yst_uid; ++ valid |= ATTR_UID; ++ attr->IATTR_GID = obj->yst_gid; ++ valid |= ATTR_GID; ++ ++ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; ++ valid |= ATTR_ATIME; ++ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; ++ valid |= ATTR_CTIME; ++ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; ++ valid |= ATTR_MTIME; ++ ++ attr->ia_size = yaffs_get_file_size(obj); ++ valid |= ATTR_SIZE; ++ ++ attr->ia_valid = valid; ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_attribs.h linux-3.18.14/fs/yaffs2/yaffs_attribs.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_attribs.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ATTRIBS_H__ ++#define __YAFFS_ATTRIBS_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_bitmap.c linux-3.18.14/fs/yaffs2/yaffs_bitmap.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_bitmap.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,97 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_bitmap.h" ++#include "yaffs_trace.h" ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "BlockBits block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return dev->chunk_bits + ++ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); ++} ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block || ++ chunk < 0 || chunk >= dev->param.chunks_per_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Chunk Id (%d:%d) invalid", ++ blk, chunk); ++ BUG(); ++ } ++} ++ ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ memset(blk_bits, 0, dev->chunk_bit_stride); ++} ++ ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); ++} ++ ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] |= (1 << (chunk & 7)); ++} ++ ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; ++} ++ ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++) { ++ if (*blk_bits) ++ return 1; ++ blk_bits++; ++ } ++ return 0; ++} ++ ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ int n = 0; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) ++ n += hweight8(*blk_bits); ++ ++ return n; ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_bitmap.h linux-3.18.14/fs/yaffs2/yaffs_bitmap.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_bitmap.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++#ifndef __YAFFS_BITMAP_H__ ++#define __YAFFS_BITMAP_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.18.14/fs/yaffs2/yaffs_checkptrw.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_checkptrw.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,466 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_checkptrw.h" ++#include "yaffs_getblockinfo.h" ++ ++struct yaffs_checkpt_chunk_hdr { ++ int version; ++ int seq; ++ u32 sum; ++ u32 xor; ++} ; ++ ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++static int apply_block_offset(struct yaffs_dev *dev, int block) ++{ ++ return block - dev->block_offset; ++} ++ ++static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ hdr.version = YAFFS_CHECKPOINT_VERSION; ++ hdr.seq = dev->checkpt_page_seq; ++ hdr.sum = dev->checkpt_sum; ++ hdr.xor = dev->checkpt_xor; ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); ++} ++ ++static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ return hdr.version == YAFFS_CHECKPOINT_VERSION && ++ hdr.seq == dev->checkpt_page_seq && ++ hdr.sum == dev->checkpt_sum && ++ hdr.xor == dev->checkpt_xor; ++} ++ ++static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) ++{ ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpt blocks_avail = %d", blocks_avail); ++ ++ return (blocks_avail <= 0) ? 0 : 1; ++} ++ ++static int yaffs_checkpt_erase(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (!dev->drv.drv_erase_fn) ++ return 0; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checking blocks %d to %d", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ int offset_i = apply_block_offset(dev, i); ++ int result; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "erasing checkpt block %d", i); ++ ++ dev->n_erasures++; ++ ++ result = dev->drv.drv_erase_fn(dev, offset_i); ++ if(result) { ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += ++ dev->param.chunks_per_block; ++ } else { ++ dev->drv.drv_mark_bad_fn(dev, offset_i); ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ } ++ } ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ return 1; ++} ++ ++static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) ++{ ++ int i; ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block: erased %d reserved %d avail %d next %d ", ++ dev->n_erased_blocks, dev->param.n_reserved_blocks, ++ blocks_avail, dev->checkpt_next_block); ++ ++ if (dev->checkpt_next_block >= 0 && ++ dev->checkpt_next_block <= dev->internal_end_block && ++ blocks_avail > 0) { ++ ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev, i); ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ dev->checkpt_next_block = i + 1; ++ dev->checkpt_cur_block = i; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block %d", i); ++ return; ++ } ++ } ++ } ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_ext_tags tags; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: start: blocks %d next %d", ++ dev->blocks_in_checkpt, dev->checkpt_next_block); ++ ++ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ int chunk = i * dev->param.chunks_per_block; ++ enum yaffs_block_state state; ++ u32 seq; ++ ++ dev->tagger.read_chunk_tags_fn(dev, ++ apply_chunk_offset(dev, chunk), ++ NULL, &tags); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", ++ i, (int) state, ++ tags.obj_id, tags.seq_number, ++ tags.ecc_result); ++ ++ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ continue; ++ ++ dev->tagger.query_block_fn(dev, ++ apply_block_offset(dev, i), ++ &state, &seq); ++ if (state == YAFFS_BLOCK_STATE_DEAD) ++ continue; ++ ++ /* Right kind of block */ ++ dev->checkpt_next_block = tags.obj_id; ++ dev->checkpt_cur_block = i; ++ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; ++ dev->blocks_in_checkpt++; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "found checkpt block %d", i); ++ return; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) ++{ ++ int i; ++ ++ dev->checkpt_open_write = writing; ++ ++ /* Got the functions we need? */ ++ if (!dev->tagger.write_chunk_tags_fn || ++ !dev->tagger.read_chunk_tags_fn || ++ !dev->drv.drv_erase_fn || ++ !dev->drv.drv_mark_bad_fn) ++ return 0; ++ ++ if (writing && !yaffs2_checkpt_space_ok(dev)) ++ return 0; ++ ++ if (!dev->checkpt_buffer) ++ dev->checkpt_buffer = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ dev->checkpt_page_seq = 0; ++ dev->checkpt_byte_count = 0; ++ dev->checkpt_sum = 0; ++ dev->checkpt_xor = 0; ++ dev->checkpt_cur_block = -1; ++ dev->checkpt_cur_chunk = -1; ++ dev->checkpt_next_block = dev->internal_start_block; ++ ++ if (writing) { ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ return yaffs_checkpt_erase(dev); ++ } ++ ++ /* Opening for a read */ ++ /* Set to a value that will kick off a read */ ++ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; ++ /* A checkpoint block list of 1 checkpoint block per 16 block is ++ * (hopefully) going to be way more than we need */ ++ dev->blocks_in_checkpt = 0; ++ dev->checkpt_max_blocks = ++ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; ++ if (!dev->checkpt_block_list) ++ dev->checkpt_block_list = ++ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); ++ ++ if (!dev->checkpt_block_list) ++ return 0; ++ ++ for (i = 0; i < dev->checkpt_max_blocks; i++) ++ dev->checkpt_block_list[i] = -1; ++ ++ return 1; ++} ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) ++{ ++ u32 composite_sum; ++ ++ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); ++ *sum = composite_sum; ++ return 1; ++} ++ ++static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) ++{ ++ int chunk; ++ int offset_chunk; ++ struct yaffs_ext_tags tags; ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_erased_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) ++ return 0; ++ ++ tags.is_deleted = 0; ++ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ ++ tags.chunk_id = dev->checkpt_page_seq + 1; ++ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; ++ tags.n_bytes = dev->data_bytes_per_chunk; ++ if (dev->checkpt_cur_chunk == 0) { ++ /* First chunk we write for the block? Set block state to ++ checkpoint */ ++ struct yaffs_block_info *bi = ++ yaffs_get_block_info(dev, dev->checkpt_cur_block); ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ dev->blocks_in_checkpt++; ++ } ++ ++ chunk = ++ dev->checkpt_cur_block * dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", ++ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, ++ tags.obj_id, tags.chunk_id); ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ ++ dev->n_page_writes++; ++ ++ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, ++ dev->checkpt_buffer, &tags); ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { ++ dev->checkpt_cur_chunk = 0; ++ dev->checkpt_cur_block = -1; ++ } ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ ++ ++ return 1; ++} ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (!dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) ++ ok = yaffs2_checkpt_flush_buffer(dev); ++ } ++ ++ return i; ++} ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) ++{ ++ int i = 0; ++ struct yaffs_ext_tags tags; ++ int chunk; ++ int offset_chunk; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes) { ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ /* Bail out if we can't find a checpoint block */ ++ if (dev->checkpt_cur_block < 0) ++ break; ++ ++ chunk = dev->checkpt_cur_block * ++ dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ dev->n_page_reads++; ++ ++ /* Read in the next chunk */ ++ dev->tagger.read_chunk_tags_fn(dev, ++ offset_chunk, ++ dev->checkpt_buffer, ++ &tags); ++ ++ /* Bail out if the chunk is corrupted. */ ++ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ break; ++ ++ /* Bail out if it is not a checkpoint chunk. */ ++ if(!yaffs2_checkpt_check_chunk_hdr(dev)) ++ break; ++ ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ ++ if (dev->checkpt_cur_chunk >= ++ dev->param.chunks_per_block) ++ dev->checkpt_cur_block = -1; ++ ++ } ++ ++ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ } ++ ++ return i; /* Number of bytes read */ ++} ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->checkpt_open_write) { ++ if (dev->checkpt_byte_offs != ++ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) ++ yaffs2_checkpt_flush_buffer(dev); ++ } else if (dev->checkpt_block_list) { ++ for (i = 0; ++ i < dev->blocks_in_checkpt && ++ dev->checkpt_block_list[i] >= 0; i++) { ++ int blk = dev->checkpt_block_list[i]; ++ struct yaffs_block_info *bi = NULL; ++ ++ if (dev->internal_start_block <= blk && ++ blk <= dev->internal_end_block) ++ bi = yaffs_get_block_info(dev, blk); ++ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ } ++ } ++ ++ dev->n_free_chunks -= ++ dev->blocks_in_checkpt * dev->param.chunks_per_block; ++ dev->n_erased_blocks -= dev->blocks_in_checkpt; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", ++ dev->checkpt_byte_count); ++ ++ if (dev->checkpt_buffer) ++ return 1; ++ else ++ return 0; ++} ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) ++{ ++ /* Erase the checkpoint data */ ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint invalidate of %d blocks", ++ dev->blocks_in_checkpt); ++ ++ return yaffs_checkpt_erase(dev); ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.18.14/fs/yaffs2/yaffs_checkptrw.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_checkptrw.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_CHECKPTRW_H__ ++#define __YAFFS_CHECKPTRW_H__ ++ ++#include "yaffs_guts.h" ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev); ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_ecc.c linux-3.18.14/fs/yaffs2/yaffs_ecc.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_ecc.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,281 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two ++ * such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_ecc.h" ++ ++/* Table generated by gen-ecc.c ++ * Using a table means we do not have to calculate p1..p4 and p1'..p4' ++ * for each byte of data. These are instead provided in a table in bits7..2. ++ * Bit 0 of each entry indicates whether the entry has an odd or even parity, ++ * and therefore this bytes influence on the line parity. ++ */ ++ ++static const unsigned char column_parity_table[] = { ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++}; ++ ++ ++/* Calculate the ECC for a 256-byte block of data */ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned char line_parity = 0; ++ unsigned char line_parity_prime = 0; ++ unsigned char t; ++ unsigned char b; ++ ++ for (i = 0; i < 256; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ } ++ ++ ecc[2] = (~col_parity) | 0x03; ++ ++ t = 0; ++ if (line_parity & 0x80) ++ t |= 0x80; ++ if (line_parity_prime & 0x80) ++ t |= 0x40; ++ if (line_parity & 0x40) ++ t |= 0x20; ++ if (line_parity_prime & 0x40) ++ t |= 0x10; ++ if (line_parity & 0x20) ++ t |= 0x08; ++ if (line_parity_prime & 0x20) ++ t |= 0x04; ++ if (line_parity & 0x10) ++ t |= 0x02; ++ if (line_parity_prime & 0x10) ++ t |= 0x01; ++ ecc[1] = ~t; ++ ++ t = 0; ++ if (line_parity & 0x08) ++ t |= 0x80; ++ if (line_parity_prime & 0x08) ++ t |= 0x40; ++ if (line_parity & 0x04) ++ t |= 0x20; ++ if (line_parity_prime & 0x04) ++ t |= 0x10; ++ if (line_parity & 0x02) ++ t |= 0x08; ++ if (line_parity_prime & 0x02) ++ t |= 0x04; ++ if (line_parity & 0x01) ++ t |= 0x02; ++ if (line_parity_prime & 0x01) ++ t |= 0x01; ++ ecc[0] = ~t; ++ ++} ++ ++/* Correct the ECC on a 256 byte block of data */ ++ ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc) ++{ ++ unsigned char d0, d1, d2; /* deltas */ ++ ++ d0 = read_ecc[0] ^ test_ecc[0]; ++ d1 = read_ecc[1] ^ test_ecc[1]; ++ d2 = read_ecc[2] ^ test_ecc[2]; ++ ++ if ((d0 | d1 | d2) == 0) ++ return 0; /* no error */ ++ ++ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && ++ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && ++ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { ++ /* Single bit (recoverable) error in data */ ++ ++ unsigned byte; ++ unsigned bit; ++ ++ bit = byte = 0; ++ ++ if (d1 & 0x80) ++ byte |= 0x80; ++ if (d1 & 0x20) ++ byte |= 0x40; ++ if (d1 & 0x08) ++ byte |= 0x20; ++ if (d1 & 0x02) ++ byte |= 0x10; ++ if (d0 & 0x80) ++ byte |= 0x08; ++ if (d0 & 0x20) ++ byte |= 0x04; ++ if (d0 & 0x08) ++ byte |= 0x02; ++ if (d0 & 0x02) ++ byte |= 0x01; ++ ++ if (d2 & 0x80) ++ bit |= 0x04; ++ if (d2 & 0x20) ++ bit |= 0x02; ++ if (d2 & 0x08) ++ bit |= 0x01; ++ ++ data[byte] ^= (1 << bit); ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ read_ecc[0] = test_ecc[0]; ++ read_ecc[1] = test_ecc[1]; ++ read_ecc[2] = test_ecc[2]; ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++ ++} ++ ++/* ++ * ECCxxxOther does ECC calcs on arbitrary n bytes of data ++ */ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc_other) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned line_parity = 0; ++ unsigned line_parity_prime = 0; ++ unsigned char b; ++ ++ for (i = 0; i < n_bytes; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { ++ /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ ++ } ++ ++ ecc_other->col_parity = (col_parity >> 2) & 0x3f; ++ ecc_other->line_parity = line_parity; ++ ecc_other->line_parity_prime = line_parity_prime; ++} ++ ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc) ++{ ++ unsigned char delta_col; /* column parity delta */ ++ unsigned delta_line; /* line parity delta */ ++ unsigned delta_line_prime; /* line parity delta */ ++ unsigned bit; ++ ++ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; ++ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; ++ delta_line_prime = ++ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; ++ ++ if ((delta_col | delta_line | delta_line_prime) == 0) ++ return 0; /* no error */ ++ ++ if (delta_line == ~delta_line_prime && ++ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { ++ /* Single bit (recoverable) error in data */ ++ ++ bit = 0; ++ ++ if (delta_col & 0x20) ++ bit |= 0x04; ++ if (delta_col & 0x08) ++ bit |= 0x02; ++ if (delta_col & 0x02) ++ bit |= 0x01; ++ ++ if (delta_line >= n_bytes) ++ return -1; ++ ++ data[delta_line] ^= (1 << bit); ++ ++ return 1; /* corrected */ ++ } ++ ++ if ((hweight32(delta_line) + ++ hweight32(delta_line_prime) + ++ hweight8(delta_col)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ *read_ecc = *test_ecc; ++ return 1; /* corrected */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_ecc.h linux-3.18.14/fs/yaffs2/yaffs_ecc.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_ecc.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. ++ * Thus, two such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#ifndef __YAFFS_ECC_H__ ++#define __YAFFS_ECC_H__ ++ ++struct yaffs_ecc_other { ++ unsigned char col_parity; ++ unsigned line_parity; ++ unsigned line_parity_prime; ++}; ++ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc); ++ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc); ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc); ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.18.14/fs/yaffs2/yaffs_getblockinfo.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_getblockinfo.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,35 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GETBLOCKINFO_H__ ++#define __YAFFS_GETBLOCKINFO_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++ ++/* Function to manipulate block info */ ++static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev ++ *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs: get_block_info block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return &dev->block_info[blk - dev->internal_start_block]; ++} ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_guts.c linux-3.18.14/fs/yaffs2/yaffs_guts.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_guts.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,5140 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++#include "yaffs_guts.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_tagsmarshall.h" ++#include "yaffs_nand.h" ++#include "yaffs_yaffs1.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_verify.h" ++#include "yaffs_nand.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nameval.h" ++#include "yaffs_allocator.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ ++#define YAFFS_GC_GOOD_ENOUGH 2 ++#define YAFFS_GC_PASSIVE_THRESHOLD 4 ++ ++#include "yaffs_ecc.h" ++ ++/* Forward declarations */ ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve); ++ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size); ++ ++/* Function to calculate chunk and offset */ ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out) ++{ ++ int chunk; ++ u32 offset; ++ ++ chunk = (u32) (addr >> dev->chunk_shift); ++ ++ if (dev->chunk_div == 1) { ++ /* easy power of 2 case */ ++ offset = (u32) (addr & dev->chunk_mask); ++ } else { ++ /* Non power-of-2 case */ ++ ++ loff_t chunk_base; ++ ++ chunk /= dev->chunk_div; ++ ++ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; ++ offset = (u32) (addr - chunk_base); ++ } ++ ++ *chunk_out = chunk; ++ *offset_out = offset; ++} ++ ++/* Function to return the number of shifts for a power of 2 greater than or ++ * equal to the given number ++ * Note we don't try to cater for all possible numbers and this does not have to ++ * be hellishly efficient. ++ */ ++ ++static inline u32 calc_shifts_ceiling(u32 x) ++{ ++ int extra_bits; ++ int shifts; ++ ++ shifts = extra_bits = 0; ++ ++ while (x > 1) { ++ if (x & 1) ++ extra_bits++; ++ x >>= 1; ++ shifts++; ++ } ++ ++ if (extra_bits) ++ shifts++; ++ ++ return shifts; ++} ++ ++/* Function to return the number of shifts to get a 1 in bit 0 ++ */ ++ ++static inline u32 calc_shifts(u32 x) ++{ ++ u32 shifts; ++ ++ shifts = 0; ++ ++ if (!x) ++ return 0; ++ ++ while (!(x & 1)) { ++ x >>= 1; ++ shifts++; ++ } ++ ++ return shifts; ++} ++ ++/* ++ * Temporary buffer manipulations. ++ */ ++ ++static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) ++{ ++ int i; ++ u8 *buf = (u8 *) 1; ++ ++ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); ++ ++ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { ++ dev->temp_buffer[i].in_use = 0; ++ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ dev->temp_buffer[i].buffer = buf; ++ } ++ ++ return buf ? YAFFS_OK : YAFFS_FAIL; ++} ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) ++{ ++ int i; ++ ++ dev->temp_in_use++; ++ if (dev->temp_in_use > dev->max_temp) ++ dev->max_temp = dev->temp_in_use; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].in_use == 0) { ++ dev->temp_buffer[i].in_use = 1; ++ return dev->temp_buffer[i].buffer; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); ++ /* ++ * If we got here then we have to allocate an unmanaged one ++ * This is not good. ++ */ ++ ++ dev->unmanaged_buffer_allocs++; ++ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); ++ ++} ++ ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) ++{ ++ int i; ++ ++ dev->temp_in_use--; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].buffer == buffer) { ++ dev->temp_buffer[i].in_use = 0; ++ return; ++ } ++ } ++ ++ if (buffer) { ++ /* assume it is an unmanaged one. */ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, ++ "Releasing unmanaged temp buffer"); ++ kfree(buffer); ++ dev->unmanaged_buffer_deallocs++; ++ } ++ ++} ++ ++/* ++ * Functions for robustisizing TODO ++ * ++ */ ++ ++static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) data; ++ (void) tags; ++} ++ ++static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) tags; ++} ++ ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ if (!bi->gc_prioritise) { ++ bi->gc_prioritise = 1; ++ dev->has_pending_prioritised_gc = 1; ++ bi->chunk_error_strikes++; ++ ++ if (bi->chunk_error_strikes > 3) { ++ bi->needs_retiring = 1; /* Too many stikes, so retire */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Block struck out"); ++ ++ } ++ } ++} ++ ++static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, ++ int erased_ok) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs_handle_chunk_error(dev, bi); ++ ++ if (erased_ok) { ++ /* Was an actual write failure, ++ * so mark the block for retirement.*/ ++ bi->needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d needs retiring", flash_block); ++ } ++ ++ /* Delete the chunk */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++} ++ ++/* ++ * Verification code ++ */ ++ ++/* ++ * Simple hash function. Needs to have a reasonable spread ++ */ ++ ++static inline int yaffs_hash_fn(int n) ++{ ++ if (n < 0) ++ n = -n; ++ return n % YAFFS_NOBJECT_BUCKETS; ++} ++ ++/* ++ * Access functions to useful fake objects. ++ * Note that root might have a presence in NAND if permissions are set. ++ */ ++ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) ++{ ++ return dev->root_dir; ++} ++ ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) ++{ ++ return dev->lost_n_found; ++} ++ ++/* ++ * Erased NAND checking functions ++ */ ++ ++int yaffs_check_ff(u8 *buffer, int n_bytes) ++{ ++ /* Horrible, slow implementation */ ++ while (n_bytes--) { ++ if (*buffer != 0xff) ++ return 0; ++ buffer++; ++ } ++ return 1; ++} ++ ++static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int retval = YAFFS_OK; ++ u8 *data = yaffs_get_temp_buffer(dev); ++ struct yaffs_ext_tags tags; ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); ++ ++ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) ++ retval = YAFFS_FAIL; ++ ++ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || ++ tags.chunk_used) { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not erased", nand_chunk); ++ retval = YAFFS_FAIL; ++ } ++ ++ yaffs_release_temp_buffer(dev, data); ++ ++ return retval; ++ ++} ++ ++static int yaffs_verify_chunk_written(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = YAFFS_OK; ++ struct yaffs_ext_tags temp_tags; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); ++ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || ++ temp_tags.obj_id != tags->obj_id || ++ temp_tags.chunk_id != tags->chunk_id || ++ temp_tags.n_bytes != tags->n_bytes) ++ retval = YAFFS_FAIL; ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ return retval; ++} ++ ++ ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) ++{ ++ int reserved_chunks; ++ int reserved_blocks = dev->param.n_reserved_blocks; ++ int checkpt_blocks; ++ ++ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); ++ ++ reserved_chunks = ++ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; ++ ++ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); ++} ++ ++static int yaffs_find_alloc_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ ++ if (dev->n_erased_blocks < 1) { ++ /* Hoosterman we've got a problem. ++ * Can't get space to gc ++ */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no more erased blocks"); ++ ++ return -1; ++ } ++ ++ /* Find an empty block. */ ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ dev->alloc_block_finder++; ++ if (dev->alloc_block_finder < dev->internal_start_block ++ || dev->alloc_block_finder > dev->internal_end_block) { ++ dev->alloc_block_finder = dev->internal_start_block; ++ } ++ ++ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->seq_number++; ++ bi->seq_number = dev->seq_number; ++ dev->n_erased_blocks--; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Allocated block %d, seq %d, %d left" , ++ dev->alloc_block_finder, dev->seq_number, ++ dev->n_erased_blocks); ++ return dev->alloc_block_finder; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs tragedy: no more erased blocks, but there should have been %d", ++ dev->n_erased_blocks); ++ ++ return -1; ++} ++ ++static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, ++ struct yaffs_block_info **block_ptr) ++{ ++ int ret_val; ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block < 0) { ++ /* Get next block to allocate off */ ++ dev->alloc_block = yaffs_find_alloc_block(dev); ++ dev->alloc_page = 0; ++ } ++ ++ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { ++ /* No space unless we're allowed to use the reserve. */ ++ return -1; ++ } ++ ++ if (dev->n_erased_blocks < dev->param.n_reserved_blocks ++ && dev->alloc_page == 0) ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); ++ ++ /* Next page please.... */ ++ if (dev->alloc_block >= 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ ++ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + ++ dev->alloc_page; ++ bi->pages_in_use++; ++ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); ++ ++ dev->alloc_page++; ++ ++ dev->n_free_chunks--; ++ ++ /* If the block is full set the state to full */ ++ if (dev->alloc_page >= dev->param.chunks_per_block) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ if (block_ptr) ++ *block_ptr = bi; ++ ++ return ret_val; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); ++ ++ return -1; ++} ++ ++static int yaffs_get_erased_chunks(struct yaffs_dev *dev) ++{ ++ int n; ++ ++ n = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ if (dev->alloc_block > 0) ++ n += (dev->param.chunks_per_block - dev->alloc_page); ++ ++ return n; ++ ++} ++ ++/* ++ * yaffs_skip_rest_of_block() skips over the rest of the allocation block ++ * if we don't want to write to it. ++ */ ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev) ++{ ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block > 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ } ++} ++ ++static int yaffs_write_new_chunk(struct yaffs_dev *dev, ++ const u8 *data, ++ struct yaffs_ext_tags *tags, int use_reserver) ++{ ++ int attempts = 0; ++ int write_ok = 0; ++ int chunk; ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ do { ++ struct yaffs_block_info *bi = 0; ++ int erased_ok = 0; ++ ++ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); ++ if (chunk < 0) { ++ /* no space */ ++ break; ++ } ++ ++ /* First check this chunk is erased, if it needs ++ * checking. The checking policy (unless forced ++ * always on) is as follows: ++ * ++ * Check the first page we try to write in a block. ++ * If the check passes then we don't need to check any ++ * more. If the check fails, we check again... ++ * If the block has been erased, we don't need to check. ++ * ++ * However, if the block has been prioritised for gc, ++ * then we think there might be something odd about ++ * this block and stop using it. ++ * ++ * Rationale: We should only ever see chunks that have ++ * not been erased if there was a partially written ++ * chunk due to power loss. This checking policy should ++ * catch that case with very few checks and thus save a ++ * lot of checks that are most likely not needed. ++ * ++ * Mods to the above ++ * If an erase check fails or the write fails we skip the ++ * rest of the block. ++ */ ++ ++ /* let's give it a try */ ++ attempts++; ++ ++ if (dev->param.always_check_erased) ++ bi->skip_erased_check = 0; ++ ++ if (!bi->skip_erased_check) { ++ erased_ok = yaffs_check_chunk_erased(dev, chunk); ++ if (erased_ok != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs chunk %d was not erased", ++ chunk); ++ ++ /* If not erased, delete this one, ++ * skip rest of block and ++ * try another chunk */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++ continue; ++ } ++ } ++ ++ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); ++ ++ if (!bi->skip_erased_check) ++ write_ok = ++ yaffs_verify_chunk_written(dev, chunk, data, tags); ++ ++ if (write_ok != YAFFS_OK) { ++ /* Clean up aborted write, skip to next block and ++ * try another chunk */ ++ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); ++ continue; ++ } ++ ++ bi->skip_erased_check = 1; ++ ++ /* Copy the data into the robustification buffer */ ++ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); ++ ++ } while (write_ok != YAFFS_OK && ++ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); ++ ++ if (!write_ok) ++ chunk = -1; ++ ++ if (attempts > 1) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs write required %d attempts", ++ attempts); ++ dev->n_retried_writes += (attempts - 1); ++ } ++ ++ return chunk; ++} ++ ++/* ++ * Block retiring for handling a broken block. ++ */ ++ ++static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { ++ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to mark bad and erase block %d", ++ flash_block); ++ } else { ++ struct yaffs_ext_tags tags; ++ int chunk_id = ++ flash_block * dev->param.chunks_per_block; ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ memset(&tags, 0, sizeof(tags)); ++ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; ++ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - ++ dev->chunk_offset, ++ buffer, ++ &tags) != YAFFS_OK) ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to write bad block marker to block %d", ++ flash_block); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ } ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ bi->gc_prioritise = 0; ++ bi->needs_retiring = 0; ++ ++ dev->n_retired_blocks++; ++} ++ ++/*---------------- Name handling functions ------------*/ ++ ++static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, ++ const YCHAR *oh_name, int buff_size) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ if (dev->param.auto_unicode) { ++ if (*oh_name) { ++ /* It is an ASCII name, do an ASCII to ++ * unicode conversion */ ++ const char *ascii_oh_name = (const char *)oh_name; ++ int n = buff_size - 1; ++ while (n > 0 && *ascii_oh_name) { ++ *name = *ascii_oh_name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ strncpy(name, oh_name + 1, buff_size - 1); ++ } ++ } else { ++#else ++ (void) dev; ++ { ++#endif ++ strncpy(name, oh_name, buff_size - 1); ++ } ++} ++ ++static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, ++ const YCHAR *name) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ ++ int is_ascii; ++ const YCHAR *w; ++ ++ if (dev->param.auto_unicode) { ++ ++ is_ascii = 1; ++ w = name; ++ ++ /* Figure out if the name will fit in ascii character set */ ++ while (is_ascii && *w) { ++ if ((*w) & 0xff00) ++ is_ascii = 0; ++ w++; ++ } ++ ++ if (is_ascii) { ++ /* It is an ASCII name, so convert unicode to ascii */ ++ char *ascii_oh_name = (char *)oh_name; ++ int n = YAFFS_MAX_NAME_LENGTH - 1; ++ while (n > 0 && *name) { ++ *ascii_oh_name = *name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ /* Unicode name, so save starting at the second YCHAR */ ++ *oh_name = 0; ++ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); ++ } ++ } else { ++#else ++ dev = dev; ++ { ++#endif ++ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); ++ } ++} ++ ++static u16 yaffs_calc_name_sum(const YCHAR *name) ++{ ++ u16 sum = 0; ++ u16 i = 1; ++ ++ if (!name) ++ return 0; ++ ++ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { ++ ++ /* 0x1f mask is case insensitive */ ++ sum += ((*name) & 0x1f) * i; ++ i++; ++ name++; ++ } ++ return sum; ++} ++ ++ ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ memset(obj->short_name, 0, sizeof(obj->short_name)); ++ ++ if (name && !name[0]) { ++ yaffs_fix_null_name(obj, obj->short_name, ++ YAFFS_SHORT_NAME_LENGTH); ++ name = obj->short_name; ++ } else if (name && ++ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= ++ YAFFS_SHORT_NAME_LENGTH) { ++ strcpy(obj->short_name, name); ++ } ++ ++ obj->sum = yaffs_calc_name_sum(name); ++} ++ ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ memset(tmp_name, 0, sizeof(tmp_name)); ++ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_set_obj_name(obj, tmp_name); ++#else ++ yaffs_set_obj_name(obj, oh->name); ++#endif ++} ++ ++loff_t yaffs_max_file_size(struct yaffs_dev *dev) ++{ ++ if(sizeof(loff_t) < 8) ++ return YAFFS_MAX_FILE_SIZE_32; ++ else ++ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; ++} ++ ++/*-------------------- TNODES ------------------- ++ ++ * List of spare tnodes ++ * The list is hooked together using the first pointer ++ * in the tnode. ++ */ ++ ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); ++ ++ if (tn) { ++ memset(tn, 0, dev->tnode_size); ++ dev->n_tnodes++; ++ } ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ yaffs_free_raw_tnode(dev, tn); ++ dev->n_tnodes--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ yaffs_deinit_raw_tnodes_and_objs(dev); ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++} ++ ++static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos, unsigned val) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 mask; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ val >>= dev->chunk_grp_bits; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ mask = dev->tnode_mask << bit_in_word; ++ ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val << bit_in_word)); ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ mask = ++ dev->tnode_mask >> bit_in_word; ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val >> bit_in_word)); ++ } ++} ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 val; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ val = map[word_in_map] >> bit_in_word; ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ val |= (map[word_in_map] << bit_in_word); ++ } ++ ++ val &= dev->tnode_mask; ++ val <<= dev->chunk_grp_bits; ++ ++ return val; ++} ++ ++/* ------------------- End of individual tnode manipulation -----------------*/ ++ ++/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ ++ * The look up tree is represented by the top tnode and the number of top_level ++ * in the tree. 0 means only the level 0 tnode is in the tree. ++ */ ++ ++/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id) ++{ ++ struct yaffs_tnode *tn = file_struct->top; ++ u32 i; ++ int required_depth; ++ int level = file_struct->top_level; ++ ++ (void) dev; ++ ++ /* Check sane level and chunk Id */ ++ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (i) { ++ i >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) ++ return NULL; /* Not tall enough, so we can't find it */ ++ ++ /* Traverse down to level 0 */ ++ while (level > 0 && tn) { ++ tn = tn->internal[(chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (level - 1) * ++ YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK]; ++ level--; ++ } ++ ++ return tn; ++} ++ ++/* add_find_tnode_0 finds the level 0 tnode if it exists, ++ * otherwise first expands the tree. ++ * This happens in two steps: ++ * 1. If the tree isn't tall enough, then make it taller. ++ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. ++ * ++ * Used when modifying the tree. ++ * ++ * If the tn argument is NULL, then a fresh tnode will be added otherwise the ++ * specified tn will be plugged into the ttree. ++ */ ++ ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn) ++{ ++ int required_depth; ++ int i; ++ int l; ++ struct yaffs_tnode *tn; ++ u32 x; ++ ++ /* Check sane level and page Id */ ++ if (file_struct->top_level < 0 || ++ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) { ++ /* Not tall enough, gotta make the tree taller */ ++ for (i = file_struct->top_level; i < required_depth; i++) { ++ ++ tn = yaffs_get_tnode(dev); ++ ++ if (tn) { ++ tn->internal[0] = file_struct->top; ++ file_struct->top = tn; ++ file_struct->top_level++; ++ } else { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: no more tnodes"); ++ return NULL; ++ } ++ } ++ } ++ ++ /* Traverse down to level 0, adding anything we need */ ++ ++ l = file_struct->top_level; ++ tn = file_struct->top; ++ ++ if (l > 0) { ++ while (l > 0 && tn) { ++ x = (chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK; ++ ++ if ((l > 1) && !tn->internal[x]) { ++ /* Add missing non-level-zero tnode */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } else if (l == 1) { ++ /* Looking from level 1 at level 0 */ ++ if (passed_tn) { ++ /* If we already have one, release it */ ++ if (tn->internal[x]) ++ yaffs_free_tnode(dev, ++ tn->internal[x]); ++ tn->internal[x] = passed_tn; ++ ++ } else if (!tn->internal[x]) { ++ /* Don't have one, none passed in */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } ++ } ++ ++ tn = tn->internal[x]; ++ l--; ++ } ++ } else { ++ /* top is level 0 */ ++ if (passed_tn) { ++ memcpy(tn, passed_tn, ++ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); ++ yaffs_free_tnode(dev, passed_tn); ++ } ++ } ++ ++ return tn; ++} ++ ++static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, ++ int chunk_obj) ++{ ++ return (tags->chunk_id == chunk_obj && ++ tags->obj_id == obj_id && ++ !tags->is_deleted) ? 1 : 0; ++ ++} ++ ++static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, ++ struct yaffs_ext_tags *tags, int obj_id, ++ int inode_chunk) ++{ ++ int j; ++ ++ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { ++ if (yaffs_check_chunk_bit ++ (dev, the_chunk / dev->param.chunks_per_block, ++ the_chunk % dev->param.chunks_per_block)) { ++ ++ if (dev->chunk_grp_size == 1) ++ return the_chunk; ++ else { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ tags); ++ if (yaffs_tags_match(tags, ++ obj_id, inode_chunk)) { ++ /* found it; */ ++ return the_chunk; ++ } ++ } ++ } ++ the_chunk++; ++ } ++ return -1; ++} ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /*Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ int ret_val = -1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ return ret_val; ++} ++ ++static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /* Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ int ret_val = -1; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ ++ /* Delete the entry in the filestructure (if found) */ ++ if (ret_val != -1) ++ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); ++ ++ return ret_val; ++} ++ ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan) ++{ ++ /* NB in_scan is zero unless scanning. ++ * For forward scanning, in_scan is > 0; ++ * for backward scanning in_scan is < 0 ++ * ++ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. ++ */ ++ ++ struct yaffs_tnode *tn; ++ struct yaffs_dev *dev = in->my_dev; ++ int existing_cunk; ++ struct yaffs_ext_tags existing_tags; ++ struct yaffs_ext_tags new_tags; ++ unsigned existing_serial, new_serial; ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { ++ /* Just ignore an attempt at putting a chunk into a non-file ++ * during scanning. ++ * If it is not during Scanning then something went wrong! ++ */ ++ if (!in_scan) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy:attempt to put data chunk into a non-file" ++ ); ++ BUG(); ++ } ++ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ ++ tn = yaffs_add_find_tnode_0(dev, ++ &in->variant.file_variant, ++ inode_chunk, NULL); ++ if (!tn) ++ return YAFFS_FAIL; ++ ++ if (!nand_chunk) ++ /* Dummy insert, bail now */ ++ return YAFFS_OK; ++ ++ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ if (in_scan != 0) { ++ /* If we're scanning then we need to test for duplicates ++ * NB This does not need to be efficient since it should only ++ * happen when the power fails during a write, then only one ++ * chunk should ever be affected. ++ * ++ * Correction for YAFFS2: This could happen quite a lot and we ++ * need to think about efficiency! TODO ++ * Update: For backward scanning we don't need to re-read tags ++ * so this is quite cheap. ++ */ ++ ++ if (existing_cunk > 0) { ++ /* NB Right now existing chunk will not be real ++ * chunk_id if the chunk group size > 1 ++ * thus we have to do a FindChunkInFile to get the ++ * real chunk id. ++ * ++ * We have a duplicate now we need to decide which ++ * one to use: ++ * ++ * Backwards scanning YAFFS2: The old one is what ++ * we use, dump the new one. ++ * YAFFS1: Get both sets of tags and compare serial ++ * numbers. ++ */ ++ ++ if (in_scan > 0) { ++ /* Only do this for forward scanning */ ++ yaffs_rd_chunk_tags_nand(dev, ++ nand_chunk, ++ NULL, &new_tags); ++ ++ /* Do a proper find */ ++ existing_cunk = ++ yaffs_find_chunk_in_file(in, inode_chunk, ++ &existing_tags); ++ } ++ ++ if (existing_cunk <= 0) { ++ /*Hoosterman - how did this happen? */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: existing chunk < 0 in scan" ++ ); ++ ++ } ++ ++ /* NB The deleted flags should be false, otherwise ++ * the chunks will not be loaded during a scan ++ */ ++ ++ if (in_scan > 0) { ++ new_serial = new_tags.serial_number; ++ existing_serial = existing_tags.serial_number; ++ } ++ ++ if ((in_scan > 0) && ++ (existing_cunk <= 0 || ++ ((existing_serial + 1) & 3) == new_serial)) { ++ /* Forward scanning. ++ * Use new ++ * Delete the old one and drop through to ++ * update the tnode ++ */ ++ yaffs_chunk_del(dev, existing_cunk, 1, ++ __LINE__); ++ } else { ++ /* Backward scanning or we want to use the ++ * existing one ++ * Delete the new one and return early so that ++ * the tnode isn't changed ++ */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ } ++ ++ } ++ ++ if (existing_cunk == 0) ++ in->n_data_chunks++; ++ ++ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); ++ ++ return YAFFS_OK; ++} ++ ++static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) ++{ ++ struct yaffs_block_info *the_block; ++ unsigned block_no; ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); ++ ++ block_no = chunk / dev->param.chunks_per_block; ++ the_block = yaffs_get_block_info(dev, block_no); ++ if (the_block) { ++ the_block->soft_del_pages++; ++ dev->n_free_chunks++; ++ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); ++ } ++} ++ ++/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all ++ * the chunks in the file. ++ * All soft deleting does is increment the block's softdelete count and pulls ++ * the chunk out of the tnode. ++ * Thus, essentially this is the same as DeleteWorker except that the chunks ++ * are soft deleted. ++ */ ++ ++static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, ++ u32 level, int chunk_offset) ++{ ++ int i; ++ int the_chunk; ++ int all_done = 1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = YAFFS_NTNODES_INTERNAL - 1; ++ all_done && i >= 0; ++ i--) { ++ if (tn->internal[i]) { ++ all_done = ++ yaffs_soft_del_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) ++ + i); ++ if (all_done) { ++ yaffs_free_tnode(dev, ++ tn->internal[i]); ++ tn->internal[i] = NULL; ++ } else { ++ /* Can this happen? */ ++ } ++ } ++ } ++ return (all_done) ? 1 : 0; ++ } ++ ++ /* level 0 */ ++ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk) { ++ yaffs_soft_del_chunk(dev, the_chunk); ++ yaffs_load_tnode_0(dev, tn, i, 0); ++ } ++ } ++ return 1; ++} ++ ++static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_obj *parent; ++ ++ yaffs_verify_obj_in_dir(obj); ++ parent = obj->parent; ++ ++ yaffs_verify_dir(parent); ++ ++ if (dev && dev->param.remove_obj_fn) ++ dev->param.remove_obj_fn(obj); ++ ++ list_del_init(&obj->siblings); ++ obj->parent = NULL; ++ ++ yaffs_verify_dir(parent); ++} ++ ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) ++{ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a null pointer directory" ++ ); ++ BUG(); ++ return; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a non-directory" ++ ); ++ BUG(); ++ } ++ ++ if (obj->siblings.prev == NULL) { ++ /* Not initialised */ ++ BUG(); ++ } ++ ++ yaffs_verify_dir(directory); ++ ++ yaffs_remove_obj_from_dir(obj); ++ ++ /* Now add it */ ++ list_add(&obj->siblings, &directory->variant.dir_variant.children); ++ obj->parent = directory; ++ ++ if (directory == obj->my_dev->unlinked_dir ++ || directory == obj->my_dev->del_dir) { ++ obj->unlinked = 1; ++ obj->my_dev->n_unlinked_files++; ++ obj->rename_allowed = 0; ++ } ++ ++ yaffs_verify_dir(directory); ++ yaffs_verify_obj_in_dir(obj); ++} ++ ++static int yaffs_change_obj_name(struct yaffs_obj *obj, ++ struct yaffs_obj *new_dir, ++ const YCHAR *new_name, int force, int shadows) ++{ ++ int unlink_op; ++ int del_op; ++ struct yaffs_obj *existing_target; ++ ++ if (new_dir == NULL) ++ new_dir = obj->parent; /* use the old directory */ ++ ++ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_change_obj_name: new_dir is not a directory" ++ ); ++ BUG(); ++ } ++ ++ unlink_op = (new_dir == obj->my_dev->unlinked_dir); ++ del_op = (new_dir == obj->my_dev->del_dir); ++ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ ++ /* If the object is a file going into the unlinked directory, ++ * then it is OK to just stuff it in since duplicate names are OK. ++ * else only proceed if the new name does not exist and we're putting ++ * it into a directory. ++ */ ++ if (!(unlink_op || del_op || force || ++ shadows > 0 || !existing_target) || ++ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ return YAFFS_FAIL; ++ ++ yaffs_set_obj_name(obj, new_name); ++ obj->dirty = 1; ++ yaffs_add_obj_to_dir(new_dir, obj); ++ ++ if (unlink_op) ++ obj->unlinked = 1; ++ ++ /* If it is a deletion then we mark it as a shrink for gc */ ++ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++/*------------------------ Short Operations Cache ------------------------------ ++ * In many situations where there is no high level buffering a lot of ++ * reads might be short sequential reads, and a lot of writes may be short ++ * sequential writes. eg. scanning/writing a jpeg file. ++ * In these cases, a short read/write cache can provide a huge perfomance ++ * benefit with dumb-as-a-rock code. ++ * In Linux, the page cache provides read buffering and the short op cache ++ * provides write buffering. ++ * ++ * There are a small number (~10) of cache chunks per device so that we don't ++ * need a very intelligent search. ++ */ ++ ++static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ struct yaffs_cache *cache; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ for (i = 0; i < n_caches; i++) { ++ cache = &dev->cache[i]; ++ if (cache->object == obj && cache->dirty) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void yaffs_flush_single_cache(struct yaffs_cache *cache, int discard) ++{ ++ ++ if (!cache || cache->locked) ++ return; ++ ++ /* Write it out and free it up if need be.*/ ++ if (cache->dirty) { ++ yaffs_wr_data_obj(cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, ++ 1); ++ ++ cache->dirty = 0; ++ } ++ ++ if (discard) ++ cache->object = NULL; ++} ++ ++static void yaffs_flush_file_cache(struct yaffs_obj *obj, int discard) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ struct yaffs_cache *cache; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ if (n_caches < 1) ++ return; ++ ++ ++ /* Find the chunks for this object and flush them. */ ++ for (i = 0; i < n_caches; i++) { ++ cache = &dev->cache[i]; ++ if (cache->object == obj) ++ yaffs_flush_single_cache(cache, discard); ++ } ++ ++} ++ ++ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard) ++{ ++ struct yaffs_obj *obj; ++ int n_caches = dev->param.n_caches; ++ int i; ++ ++ /* Find a dirty object in the cache and flush it... ++ * until there are no further dirty objects. ++ */ ++ do { ++ obj = NULL; ++ for (i = 0; i < n_caches && !obj; i++) { ++ if (dev->cache[i].object && dev->cache[i].dirty) ++ obj = dev->cache[i].object; ++ } ++ if (obj) ++ yaffs_flush_file_cache(obj, discard); ++ } while (obj); ++ ++} ++ ++/* Grab us an unused cache chunk for use. ++ * First look for an empty one. ++ * Then look for the least recently used non-dirty one. ++ * Then look for the least recently used dirty one...., flush and look again. ++ */ ++static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->param.n_caches > 0) { ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (!dev->cache[i].object) ++ return &dev->cache[i]; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_cache *cache; ++ int usage; ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ /* First look for an unused cache */ ++ ++ cache = yaffs_grab_chunk_worker(dev); ++ ++ if (cache) ++ return cache; ++ ++ /* ++ * Thery were all in use. ++ * Find the LRU cache and flush it if it is dirty. ++ */ ++ ++ usage = -1; ++ cache = NULL; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object && ++ !dev->cache[i].locked && ++ (dev->cache[i].last_use < usage || !cache)) { ++ usage = dev->cache[i].last_use; ++ cache = &dev->cache[i]; ++ } ++ } ++ ++#if 1 ++ yaffs_flush_single_cache(cache, 1); ++#else ++ yaffs_flush_file_cache(cache->object, 1); ++ cache = yaffs_grab_chunk_worker(dev); ++#endif ++ ++ return cache; ++} ++ ++/* Find a cached chunk */ ++static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, ++ int chunk_id) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].chunk_id == chunk_id) { ++ dev->cache_hits++; ++ ++ return &dev->cache[i]; ++ } ++ } ++ return NULL; ++} ++ ++/* Mark the chunk for the least recently used algorithym */ ++static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, ++ int is_write) ++{ ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return; ++ ++ if (dev->cache_last_use < 0 || ++ dev->cache_last_use > 100000000) { ++ /* Reset the cache usages */ ++ for (i = 1; i < dev->param.n_caches; i++) ++ dev->cache[i].last_use = 0; ++ ++ dev->cache_last_use = 0; ++ } ++ dev->cache_last_use++; ++ cache->last_use = dev->cache_last_use; ++ ++ if (is_write) ++ cache->dirty = 1; ++} ++ ++/* Invalidate a single cache page. ++ * Do this when a whole page gets written, ++ * ie the short cache for this page is no longer valid. ++ */ ++static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) ++{ ++ struct yaffs_cache *cache; ++ ++ if (object->my_dev->param.n_caches > 0) { ++ cache = yaffs_find_chunk_cache(object, chunk_id); ++ ++ if (cache) ++ cache->object = NULL; ++ } ++} ++ ++/* Invalidate all the cache pages associated with this object ++ * Do this whenever ther file is deleted or resized. ++ */ ++static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.n_caches > 0) { ++ /* Invalidate it. */ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == in) ++ dev->cache[i].object = NULL; ++ } ++ } ++} ++ ++static void yaffs_unhash_obj(struct yaffs_obj *obj) ++{ ++ int bucket; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ /* If it is still linked into the bucket list, free from the list */ ++ if (!list_empty(&obj->hash_link)) { ++ list_del_init(&obj->hash_link); ++ bucket = yaffs_hash_fn(obj->obj_id); ++ dev->obj_bucket[bucket].count--; ++ } ++} ++ ++/* FreeObject frees up a Object and puts it back on the free list */ ++static void yaffs_free_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) { ++ BUG(); ++ return; ++ } ++ dev = obj->my_dev; ++ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", ++ obj, obj->my_inode); ++ if (obj->parent) ++ BUG(); ++ if (!list_empty(&obj->siblings)) ++ BUG(); ++ ++ if (obj->my_inode) { ++ /* We're still hooked up to a cached inode. ++ * Don't delete now, but mark for later deletion ++ */ ++ obj->defered_free = 1; ++ return; ++ } ++ ++ yaffs_unhash_obj(obj); ++ ++ yaffs_free_raw_obj(dev, obj); ++ dev->n_obj--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj) ++{ ++ if (obj->defered_free) ++ yaffs_free_obj(obj); ++} ++ ++static int yaffs_generic_obj_del(struct yaffs_obj *in) ++{ ++ /* Iinvalidate the file's data in the cache, without flushing. */ ++ yaffs_invalidate_whole_cache(in); ++ ++ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { ++ /* Move to unlinked directory so we have a deletion record */ ++ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, ++ 0); ++ } ++ ++ yaffs_remove_obj_from_dir(in); ++ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); ++ in->hdr_chunk = 0; ++ ++ yaffs_free_obj(in); ++ return YAFFS_OK; ++ ++} ++ ++static void yaffs_soft_del_file(struct yaffs_obj *obj) ++{ ++ if (!obj->deleted || ++ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || ++ obj->soft_del) ++ return; ++ ++ if (obj->n_data_chunks <= 0) { ++ /* Empty file with no duplicate object headers, ++ * just delete it immediately */ ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: Deleting empty file %d", ++ obj->obj_id); ++ yaffs_generic_obj_del(obj); ++ } else { ++ yaffs_soft_del_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant. ++ file_variant.top_level, 0); ++ obj->soft_del = 1; ++ } ++} ++ ++/* Pruning removes any part of the file structure tree that is beyond the ++ * bounds of the file (ie that does not point to chunks). ++ * ++ * A file should only get pruned when its size is reduced. ++ * ++ * Before pruning, the chunks must be pulled from the tree and the ++ * level 0 tnode entries must be zeroed out. ++ * Could also use this for file deletion, but that's probably better handled ++ * by a special case. ++ * ++ * This function is recursive. For levels > 0 the function is called again on ++ * any sub-tree. For level == 0 we just check if the sub-tree has data. ++ * If there is no data in a subtree then it is pruned. ++ */ ++ ++static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, ++ struct yaffs_tnode *tn, u32 level, ++ int del0) ++{ ++ int i; ++ int has_data; ++ ++ if (!tn) ++ return tn; ++ ++ has_data = 0; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) { ++ tn->internal[i] = ++ yaffs_prune_worker(dev, ++ tn->internal[i], ++ level - 1, ++ (i == 0) ? del0 : 1); ++ } ++ ++ if (tn->internal[i]) ++ has_data++; ++ } ++ } else { ++ int tnode_size_u32 = dev->tnode_size / sizeof(u32); ++ u32 *map = (u32 *) tn; ++ ++ for (i = 0; !has_data && i < tnode_size_u32; i++) { ++ if (map[i]) ++ has_data++; ++ } ++ } ++ ++ if (has_data == 0 && del0) { ++ /* Free and return NULL */ ++ yaffs_free_tnode(dev, tn); ++ tn = NULL; ++ } ++ return tn; ++} ++ ++static int yaffs_prune_tree(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct) ++{ ++ int i; ++ int has_data; ++ int done = 0; ++ struct yaffs_tnode *tn; ++ ++ if (file_struct->top_level < 1) ++ return YAFFS_OK; ++ ++ file_struct->top = ++ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); ++ ++ /* Now we have a tree with all the non-zero branches NULL but ++ * the height is the same as it was. ++ * Let's see if we can trim internal tnodes to shorten the tree. ++ * We can do this if only the 0th element in the tnode is in use ++ * (ie all the non-zero are NULL) ++ */ ++ ++ while (file_struct->top_level && !done) { ++ tn = file_struct->top; ++ ++ has_data = 0; ++ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) ++ has_data++; ++ } ++ ++ if (!has_data) { ++ file_struct->top = tn->internal[0]; ++ file_struct->top_level--; ++ yaffs_free_tnode(dev, tn); ++ } else { ++ done = 1; ++ } ++ } ++ ++ return YAFFS_OK; ++} ++ ++/*-------------------- End of File Structure functions.-------------------*/ ++ ++/* alloc_empty_obj gets us a clean Object.*/ ++static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); ++ ++ if (!obj) ++ return obj; ++ ++ dev->n_obj++; ++ ++ /* Now sweeten it up... */ ++ ++ memset(obj, 0, sizeof(struct yaffs_obj)); ++ obj->being_created = 1; ++ ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; ++ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; ++ INIT_LIST_HEAD(&(obj->hard_links)); ++ INIT_LIST_HEAD(&(obj->hash_link)); ++ INIT_LIST_HEAD(&obj->siblings); ++ ++ /* Now make the directory sane */ ++ if (dev->root_dir) { ++ obj->parent = dev->root_dir; ++ list_add(&(obj->siblings), ++ &dev->root_dir->variant.dir_variant.children); ++ } ++ ++ /* Add it to the lost and found directory. ++ * NB Can't put root or lost-n-found in lost-n-found so ++ * check if lost-n-found exists first ++ */ ++ if (dev->lost_n_found) ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ ++ obj->being_created = 0; ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return obj; ++} ++ ++static int yaffs_find_nice_bucket(struct yaffs_dev *dev) ++{ ++ int i; ++ int l = 999; ++ int lowest = 999999; ++ ++ /* Search for the shortest list or one that ++ * isn't too long. ++ */ ++ ++ for (i = 0; i < 10 && lowest > 4; i++) { ++ dev->bucket_finder++; ++ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; ++ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { ++ lowest = dev->obj_bucket[dev->bucket_finder].count; ++ l = dev->bucket_finder; ++ } ++ } ++ ++ return l; ++} ++ ++static int yaffs_new_obj_id(struct yaffs_dev *dev) ++{ ++ int bucket = yaffs_find_nice_bucket(dev); ++ int found = 0; ++ struct list_head *i; ++ u32 n = (u32) bucket; ++ ++ /* ++ * Now find an object value that has not already been taken ++ * by scanning the list, incrementing each time by number of buckets. ++ */ ++ while (!found) { ++ found = 1; ++ n += YAFFS_NOBJECT_BUCKETS; ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* Check if this value is already taken. */ ++ if (i && list_entry(i, struct yaffs_obj, ++ hash_link)->obj_id == n) ++ found = 0; ++ } ++ } ++ return n; ++} ++ ++static void yaffs_hash_obj(struct yaffs_obj *in) ++{ ++ int bucket = yaffs_hash_fn(in->obj_id); ++ struct yaffs_dev *dev = in->my_dev; ++ ++ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); ++ dev->obj_bucket[bucket].count++; ++} ++ ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) ++{ ++ int bucket = yaffs_hash_fn(number); ++ struct list_head *i; ++ struct yaffs_obj *in; ++ ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* Look if it is in the list */ ++ in = list_entry(i, struct yaffs_obj, hash_link); ++ if (in->obj_id == number) { ++ /* Don't show if it is defered free */ ++ if (in->defered_free) ++ return NULL; ++ return in; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (number < 0) ++ number = yaffs_new_obj_id(dev); ++ ++ if (type == YAFFS_OBJECT_TYPE_FILE) { ++ tn = yaffs_get_tnode(dev); ++ if (!tn) ++ return NULL; ++ } ++ ++ the_obj = yaffs_alloc_empty_obj(dev); ++ if (!the_obj) { ++ if (tn) ++ yaffs_free_tnode(dev, tn); ++ return NULL; ++ } ++ ++ the_obj->fake = 0; ++ the_obj->rename_allowed = 1; ++ the_obj->unlink_allowed = 1; ++ the_obj->obj_id = number; ++ yaffs_hash_obj(the_obj); ++ the_obj->variant_type = type; ++ yaffs_load_current_time(the_obj, 1, 1); ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ the_obj->variant.file_variant.file_size = 0; ++ the_obj->variant.file_variant.scanned_size = 0; ++ the_obj->variant.file_variant.shrink_size = ++ yaffs_max_file_size(dev); ++ the_obj->variant.file_variant.top_level = 0; ++ the_obj->variant.file_variant.top = tn; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* No action required */ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* todo this should not happen */ ++ break; ++ } ++ return the_obj; ++} ++ ++static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, ++ int number, u32 mode) ++{ ++ ++ struct yaffs_obj *obj = ++ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); ++ ++ if (!obj) ++ return NULL; ++ ++ obj->fake = 1; /* it is fake so it might not use NAND */ ++ obj->rename_allowed = 0; ++ obj->unlink_allowed = 0; ++ obj->deleted = 0; ++ obj->unlinked = 0; ++ obj->yst_mode = mode; ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; /* Not a valid chunk. */ ++ return obj; ++ ++} ++ ++ ++static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++ yaffs_init_raw_tnodes_and_objs(dev); ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ INIT_LIST_HEAD(&dev->obj_bucket[i].list); ++ dev->obj_bucket[i].count = 0; ++ } ++} ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ ++ if (number > 0) ++ the_obj = yaffs_find_by_number(dev, number); ++ ++ if (!the_obj) ++ the_obj = yaffs_new_obj(dev, number, type); ++ ++ return the_obj; ++ ++} ++ ++YCHAR *yaffs_clone_str(const YCHAR *str) ++{ ++ YCHAR *new_str = NULL; ++ int len; ++ ++ if (!str) ++ str = _Y(""); ++ ++ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); ++ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); ++ if (new_str) { ++ strncpy(new_str, str, len); ++ new_str[len] = 0; ++ } ++ return new_str; ++ ++} ++/* ++ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new ++ * link (ie. name) is created or deleted in the directory. ++ * ++ * ie. ++ * create dir/a : update dir's mtime/ctime ++ * rm dir/a: update dir's mtime/ctime ++ * modify dir/a: don't update dir's mtimme/ctime ++ * ++ * This can be handled immediately or defered. Defering helps reduce the number ++ * of updates when many files in a directory are changed within a brief period. ++ * ++ * If the directory updating is defered then yaffs_update_dirty_dirs must be ++ * called periodically. ++ */ ++ ++static void yaffs_update_parent(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) ++ return; ++ dev = obj->my_dev; ++ obj->dirty = 1; ++ yaffs_load_current_time(obj, 0, 1); ++ if (dev->param.defered_dir_update) { ++ struct list_head *link = &obj->variant.dir_variant.dirty; ++ ++ if (list_empty(link)) { ++ list_add(link, &dev->dirty_dirs); ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Added object %d to dirty directories", ++ obj->obj_id); ++ } ++ ++ } else { ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev) ++{ ++ struct list_head *link; ++ struct yaffs_obj *obj; ++ struct yaffs_dir_var *d_s; ++ union yaffs_obj_var *o_v; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); ++ ++ while (!list_empty(&dev->dirty_dirs)) { ++ link = dev->dirty_dirs.next; ++ list_del_init(link); ++ ++ d_s = list_entry(link, struct yaffs_dir_var, dirty); ++ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); ++ obj = list_entry(o_v, struct yaffs_obj, variant); ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", ++ obj->obj_id); ++ ++ if (obj->dirty) ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++/* ++ * Mknod (create) a new object. ++ * equiv_obj only has meaning for a hard link; ++ * alias_str only has meaning for a symlink. ++ * rdev only has meaning for devices (a subset of special objects) ++ */ ++ ++static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, ++ struct yaffs_obj *parent, ++ const YCHAR *name, ++ u32 mode, ++ u32 uid, ++ u32 gid, ++ struct yaffs_obj *equiv_obj, ++ const YCHAR *alias_str, u32 rdev) ++{ ++ struct yaffs_obj *in; ++ YCHAR *str = NULL; ++ struct yaffs_dev *dev = parent->my_dev; ++ ++ /* Check if the entry exists. ++ * If it does then fail the call since we don't want a dup. */ ++ if (yaffs_find_by_name(parent, name)) ++ return NULL; ++ ++ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ str = yaffs_clone_str(alias_str); ++ if (!str) ++ return NULL; ++ } ++ ++ in = yaffs_new_obj(dev, -1, type); ++ ++ if (!in) { ++ kfree(str); ++ return NULL; ++ } ++ ++ in->hdr_chunk = 0; ++ in->valid = 1; ++ in->variant_type = type; ++ ++ in->yst_mode = mode; ++ ++ yaffs_attribs_init(in, gid, uid, rdev); ++ ++ in->n_data_chunks = 0; ++ ++ yaffs_set_obj_name(in, name); ++ in->dirty = 1; ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ in->my_dev = parent->my_dev; ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant.alias = str; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant.hardlink_variant.equiv_obj = equiv_obj; ++ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; ++ list_add(&in->hard_links, &equiv_obj->hard_links); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* do nothing */ ++ break; ++ } ++ ++ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { ++ /* Could not create the object header, fail */ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ if (in) ++ yaffs_update_parent(parent); ++ ++ return in; ++} ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, ++ uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, ++ mode, uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, ++ uid, gid, NULL, NULL, rdev); ++} ++ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, ++ uid, gid, NULL, alias, 0); ++} ++ ++/* yaffs_link_obj returns the object id of the equivalent object.*/ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, ++ struct yaffs_obj *equiv_obj) ++{ ++ /* Get the real object in case we were fed a hard link obj */ ++ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); ++ ++ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, ++ parent, name, 0, 0, 0, ++ equiv_obj, NULL, 0)) ++ return equiv_obj; ++ ++ return NULL; ++ ++} ++ ++ ++ ++/*---------------------- Block Management and Page Allocation -------------*/ ++ ++static void yaffs_deinit_blocks(struct yaffs_dev *dev) ++{ ++ if (dev->block_info_alt && dev->block_info) ++ vfree(dev->block_info); ++ else ++ kfree(dev->block_info); ++ ++ dev->block_info_alt = 0; ++ ++ dev->block_info = NULL; ++ ++ if (dev->chunk_bits_alt && dev->chunk_bits) ++ vfree(dev->chunk_bits); ++ else ++ kfree(dev->chunk_bits); ++ dev->chunk_bits_alt = 0; ++ dev->chunk_bits = NULL; ++} ++ ++static int yaffs_init_blocks(struct yaffs_dev *dev) ++{ ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ dev->block_info = NULL; ++ dev->chunk_bits = NULL; ++ dev->alloc_block = -1; /* force it to get a new one */ ++ ++ /* If the first allocation strategy fails, thry the alternate one */ ++ dev->block_info = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); ++ if (!dev->block_info) { ++ dev->block_info = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); ++ dev->block_info_alt = 1; ++ } else { ++ dev->block_info_alt = 0; ++ } ++ ++ if (!dev->block_info) ++ goto alloc_error; ++ ++ /* Set up dynamic blockinfo stuff. Round up bytes. */ ++ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; ++ dev->chunk_bits = ++ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); ++ if (!dev->chunk_bits) { ++ dev->chunk_bits = ++ vmalloc(dev->chunk_bit_stride * n_blocks); ++ dev->chunk_bits_alt = 1; ++ } else { ++ dev->chunk_bits_alt = 0; ++ } ++ if (!dev->chunk_bits) ++ goto alloc_error; ++ ++ ++ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); ++ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); ++ return YAFFS_OK; ++ ++alloc_error: ++ yaffs_deinit_blocks(dev); ++ return YAFFS_FAIL; ++} ++ ++ ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); ++ int erased_ok = 0; ++ int i; ++ ++ /* If the block is still healthy erase it and mark as clean. ++ * If the block has had a data failure, then retire it. ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, ++ "yaffs_block_became_dirty block %d state %d %s", ++ block_no, bi->block_state, ++ (bi->needs_retiring) ? "needs retiring" : ""); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; ++ ++ /* If this is the block being garbage collected then stop gc'ing */ ++ if (block_no == dev->gc_block) ++ dev->gc_block = 0; ++ ++ /* If this block is currently the best candidate for gc ++ * then drop as a candidate */ ++ if (block_no == dev->gc_dirtiest) { ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ } ++ ++ if (!bi->needs_retiring) { ++ yaffs2_checkpt_invalidate(dev); ++ erased_ok = yaffs_erase_block(dev, block_no); ++ if (!erased_ok) { ++ dev->n_erase_failures++; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Erasure failed %d", block_no); ++ } ++ } ++ ++ /* Verify erasure if needed */ ++ if (erased_ok && ++ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || ++ !yaffs_skip_verification(dev))) { ++ for (i = 0; i < dev->param.chunks_per_block; i++) { ++ if (!yaffs_check_chunk_erased(dev, ++ block_no * dev->param.chunks_per_block + i)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ ">>Block %d erasure supposedly OK, but chunk %d not erased", ++ block_no, i); ++ } ++ } ++ } ++ ++ if (!erased_ok) { ++ /* We lost a block of free space */ ++ dev->n_free_chunks -= dev->param.chunks_per_block; ++ yaffs_retire_block(dev, block_no); ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d retired", block_no); ++ return; ++ } ++ ++ /* Clean it up... */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ bi->seq_number = 0; ++ dev->n_erased_blocks++; ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ bi->has_shrink_hdr = 0; ++ bi->skip_erased_check = 1; /* Clean, so no need to check */ ++ bi->gc_prioritise = 0; ++ bi->has_summary = 0; ++ ++ yaffs_clear_chunk_bits(dev, block_no); ++ ++ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); ++} ++ ++static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int old_chunk, u8 *buffer) ++{ ++ int new_chunk; ++ int mark_flash = 1; ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj *object; ++ int matching_chunk; ++ int ret_val = YAFFS_OK; ++ ++ memset(&tags, 0, sizeof(tags)); ++ yaffs_rd_chunk_tags_nand(dev, old_chunk, ++ buffer, &tags); ++ object = yaffs_find_by_number(dev, tags.obj_id); ++ ++ yaffs_trace(YAFFS_TRACE_GC_DETAIL, ++ "Collecting chunk in block %d, %d %d %d ", ++ dev->gc_chunk, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ ++ if (object && !yaffs_skip_verification(dev)) { ++ if (tags.chunk_id == 0) ++ matching_chunk = ++ object->hdr_chunk; ++ else if (object->soft_del) ++ /* Defeat the test */ ++ matching_chunk = old_chunk; ++ else ++ matching_chunk = ++ yaffs_find_chunk_in_file ++ (object, tags.chunk_id, ++ NULL); ++ ++ if (old_chunk != matching_chunk) ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "gc: page in gc mismatch: %d %d %d %d", ++ old_chunk, ++ matching_chunk, ++ tags.obj_id, ++ tags.chunk_id); ++ } ++ ++ if (!object) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "page %d in gc has no object: %d %d %d ", ++ old_chunk, ++ tags.obj_id, tags.chunk_id, ++ tags.n_bytes); ++ } ++ ++ if (object && ++ object->deleted && ++ object->soft_del && tags.chunk_id != 0) { ++ /* Data chunk in a soft deleted file, ++ * throw it away. ++ * It's a soft deleted data chunk, ++ * No need to copy this, just forget ++ * about it and fix up the object. ++ */ ++ ++ /* Free chunks already includes ++ * softdeleted chunks, how ever this ++ * chunk is going to soon be really ++ * deleted which will increment free ++ * chunks. We have to decrement free ++ * chunks so this works out properly. ++ */ ++ dev->n_free_chunks--; ++ bi->soft_del_pages--; ++ ++ object->n_data_chunks--; ++ if (object->n_data_chunks <= 0) { ++ /* remeber to clean up obj */ ++ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; ++ dev->n_clean_ups++; ++ } ++ mark_flash = 0; ++ } else if (object) { ++ /* It's either a data chunk in a live ++ * file or an ObjectHeader, so we're ++ * interested in it. ++ * NB Need to keep the ObjectHeaders of ++ * deleted files until the whole file ++ * has been deleted off ++ */ ++ tags.serial_number++; ++ dev->n_gc_copies++; ++ ++ if (tags.chunk_id == 0) { ++ /* It is an object Id, ++ * We need to nuke the ++ * shrinkheader flags since its ++ * work is done. ++ * Also need to clean up ++ * shadowing. ++ */ ++ struct yaffs_obj_hdr *oh; ++ oh = (struct yaffs_obj_hdr *) buffer; ++ ++ oh->is_shrink = 0; ++ tags.extra_is_shrink = 0; ++ oh->shadows_obj = 0; ++ oh->inband_shadowed_obj_id = 0; ++ tags.extra_shadows = 0; ++ ++ /* Update file size */ ++ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { ++ yaffs_oh_size_load(oh, ++ object->variant.file_variant.file_size); ++ tags.extra_file_size = ++ object->variant.file_variant.file_size; ++ } ++ ++ yaffs_verify_oh(object, oh, &tags, 1); ++ new_chunk = ++ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); ++ } else { ++ new_chunk = ++ yaffs_write_new_chunk(dev, buffer, &tags, 1); ++ } ++ ++ if (new_chunk < 0) { ++ ret_val = YAFFS_FAIL; ++ } else { ++ ++ /* Now fix up the Tnodes etc. */ ++ ++ if (tags.chunk_id == 0) { ++ /* It's a header */ ++ object->hdr_chunk = new_chunk; ++ object->serial = tags.serial_number; ++ } else { ++ /* It's a data chunk */ ++ yaffs_put_chunk_in_file(object, tags.chunk_id, ++ new_chunk, 0); ++ } ++ } ++ } ++ if (ret_val == YAFFS_OK) ++ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); ++ return ret_val; ++} ++ ++static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) ++{ ++ int old_chunk; ++ int ret_val = YAFFS_OK; ++ int i; ++ int is_checkpt_block; ++ int max_copies; ++ int chunks_before = yaffs_get_erased_chunks(dev); ++ int chunks_after; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); ++ ++ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d, in use %d, shrink %d, whole_block %d", ++ block, bi->pages_in_use, bi->has_shrink_hdr, ++ whole_block); ++ ++ /*yaffs_verify_free_chunks(dev); */ ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; ++ ++ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ ++ ++ dev->gc_disable = 1; ++ ++ yaffs_summary_gc(dev, block); ++ ++ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d that has no chunks in use", ++ block); ++ yaffs_block_became_dirty(dev, block); ++ } else { ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_verify_blk(dev, bi, block); ++ ++ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; ++ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; ++ ++ for (/* init already done */ ; ++ ret_val == YAFFS_OK && ++ dev->gc_chunk < dev->param.chunks_per_block && ++ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && ++ max_copies > 0; ++ dev->gc_chunk++, old_chunk++) { ++ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { ++ /* Page is in use and might need to be copied */ ++ max_copies--; ++ ret_val = yaffs_gc_process_chunk(dev, bi, ++ old_chunk, buffer); ++ } ++ } ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ yaffs_verify_collected_blk(dev, bi, block); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ /* ++ * The gc did not complete. Set block state back to FULL ++ * because checkpointing does not restore gc. ++ */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } else { ++ /* The gc completed. */ ++ /* Do any required cleanups */ ++ for (i = 0; i < dev->n_clean_ups; i++) { ++ /* Time to delete the file too */ ++ struct yaffs_obj *object = ++ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); ++ if (object) { ++ yaffs_free_tnode(dev, ++ object->variant.file_variant.top); ++ object->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: About to finally delete object %d", ++ object->obj_id); ++ yaffs_generic_obj_del(object); ++ object->my_dev->n_deleted_files--; ++ } ++ ++ } ++ chunks_after = yaffs_get_erased_chunks(dev); ++ if (chunks_before >= chunks_after) ++ yaffs_trace(YAFFS_TRACE_GC, ++ "gc did not increase free chunks before %d after %d", ++ chunks_before, chunks_after); ++ dev->gc_block = 0; ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ dev->gc_disable = 0; ++ ++ return ret_val; ++} ++ ++/* ++ * find_gc_block() selects the dirtiest block (or close enough) ++ * for garbage collection. ++ */ ++ ++static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, ++ int aggressive, int background) ++{ ++ int i; ++ int iterations; ++ unsigned selected = 0; ++ int prioritised = 0; ++ int prioritised_exist = 0; ++ struct yaffs_block_info *bi; ++ int threshold; ++ ++ /* First let's see if we need to grab a prioritised block */ ++ if (dev->has_pending_prioritised_gc && !aggressive) { ++ dev->gc_dirtiest = 0; ++ bi = dev->block_info; ++ for (i = dev->internal_start_block; ++ i <= dev->internal_end_block && !selected; i++) { ++ ++ if (bi->gc_prioritise) { ++ prioritised_exist = 1; ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ selected = i; ++ prioritised = 1; ++ } ++ } ++ bi++; ++ } ++ ++ /* ++ * If there is a prioritised block and none was selected then ++ * this happened because there is at least one old dirty block ++ * gumming up the works. Let's gc the oldest dirty block. ++ */ ++ ++ if (prioritised_exist && ++ !selected && dev->oldest_dirty_block > 0) ++ selected = dev->oldest_dirty_block; ++ ++ if (!prioritised_exist) /* None found, so we can clear this */ ++ dev->has_pending_prioritised_gc = 0; ++ } ++ ++ /* If we're doing aggressive GC then we are happy to take a less-dirty ++ * block, and search harder. ++ * else (leasurely gc), then we only bother to do this if the ++ * block has only a few pages in use. ++ */ ++ ++ if (!selected) { ++ int pages_used; ++ int n_blocks = ++ dev->internal_end_block - dev->internal_start_block + 1; ++ if (aggressive) { ++ threshold = dev->param.chunks_per_block; ++ iterations = n_blocks; ++ } else { ++ int max_threshold; ++ ++ if (background) ++ max_threshold = dev->param.chunks_per_block / 2; ++ else ++ max_threshold = dev->param.chunks_per_block / 8; ++ ++ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ ++ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; ++ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ if (threshold > max_threshold) ++ threshold = max_threshold; ++ ++ iterations = n_blocks / 16 + 1; ++ if (iterations > 100) ++ iterations = 100; ++ } ++ ++ for (i = 0; ++ i < iterations && ++ (dev->gc_dirtiest < 1 || ++ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); ++ i++) { ++ dev->gc_block_finder++; ++ if (dev->gc_block_finder < dev->internal_start_block || ++ dev->gc_block_finder > dev->internal_end_block) ++ dev->gc_block_finder = ++ dev->internal_start_block; ++ ++ bi = yaffs_get_block_info(dev, dev->gc_block_finder); ++ ++ pages_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ pages_used < dev->param.chunks_per_block && ++ (dev->gc_dirtiest < 1 || ++ pages_used < dev->gc_pages_in_use) && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ dev->gc_dirtiest = dev->gc_block_finder; ++ dev->gc_pages_in_use = pages_used; ++ } ++ } ++ ++ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) ++ selected = dev->gc_dirtiest; ++ } ++ ++ /* ++ * If nothing has been selected for a while, try the oldest dirty ++ * because that's gumming up the works. ++ */ ++ ++ if (!selected && dev->param.is_yaffs2 && ++ dev->gc_not_done >= (background ? 10 : 20)) { ++ yaffs2_find_oldest_dirty_seq(dev); ++ if (dev->oldest_dirty_block > 0) { ++ selected = dev->oldest_dirty_block; ++ dev->gc_dirtiest = selected; ++ dev->oldest_dirty_gc_count++; ++ bi = yaffs_get_block_info(dev, selected); ++ dev->gc_pages_in_use = ++ bi->pages_in_use - bi->soft_del_pages; ++ } else { ++ dev->gc_not_done = 0; ++ } ++ } ++ ++ if (selected) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC Selected block %d with %d free, prioritised:%d", ++ selected, ++ dev->param.chunks_per_block - dev->gc_pages_in_use, ++ prioritised); ++ ++ dev->n_gc_blocks++; ++ if (background) ++ dev->bg_gcs++; ++ ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ dev->gc_not_done = 0; ++ if (dev->refresh_skip > 0) ++ dev->refresh_skip--; ++ } else { ++ dev->gc_not_done++; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", ++ dev->gc_block_finder, dev->gc_not_done, threshold, ++ dev->gc_dirtiest, dev->gc_pages_in_use, ++ dev->oldest_dirty_block, background ? " bg" : ""); ++ } ++ ++ return selected; ++} ++ ++/* New garbage collector ++ * If we're very low on erased blocks then we do aggressive garbage collection ++ * otherwise we do "leasurely" garbage collection. ++ * Aggressive gc looks further (whole array) and will accept less dirty blocks. ++ * Passive gc only inspects smaller areas and only accepts more dirty blocks. ++ * ++ * The idea is to help clear out space in a more spread-out manner. ++ * Dunno if it really does anything useful. ++ */ ++static int yaffs_check_gc(struct yaffs_dev *dev, int background) ++{ ++ int aggressive = 0; ++ int gc_ok = YAFFS_OK; ++ int max_tries = 0; ++ int min_erased; ++ int erased_chunks; ++ int checkpt_block_adjust; ++ ++ if (dev->param.gc_control_fn && ++ (dev->param.gc_control_fn(dev) & 1) == 0) ++ return YAFFS_OK; ++ ++ if (dev->gc_disable) ++ /* Bail out so we don't get recursive gc */ ++ return YAFFS_OK; ++ ++ /* This loop should pass the first time. ++ * Only loops here if the collection does not increase space. ++ */ ++ ++ do { ++ max_tries++; ++ ++ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); ++ ++ min_erased = ++ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; ++ erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ /* If we need a block soon then do aggressive gc. */ ++ if (dev->n_erased_blocks < min_erased) ++ aggressive = 1; ++ else { ++ if (!background ++ && erased_chunks > (dev->n_free_chunks / 4)) ++ break; ++ ++ if (dev->gc_skip > 20) ++ dev->gc_skip = 20; ++ if (erased_chunks < dev->n_free_chunks / 2 || ++ dev->gc_skip < 1 || background) ++ aggressive = 0; ++ else { ++ dev->gc_skip--; ++ break; ++ } ++ } ++ ++ dev->gc_skip = 5; ++ ++ /* If we don't already have a block being gc'd then see if we ++ * should start another */ ++ ++ if (dev->gc_block < 1 && !aggressive) { ++ dev->gc_block = yaffs2_find_refresh_block(dev); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ if (dev->gc_block < 1) { ++ dev->gc_block = ++ yaffs_find_gc_block(dev, aggressive, background); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ if (dev->gc_block > 0) { ++ dev->all_gcs++; ++ if (!aggressive) ++ dev->passive_gc_count++; ++ ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC n_erased_blocks %d aggressive %d", ++ dev->n_erased_blocks, aggressive); ++ ++ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); ++ } ++ ++ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && ++ dev->gc_block > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", ++ dev->n_erased_blocks, max_tries, ++ dev->gc_block); ++ } ++ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && ++ (dev->gc_block > 0) && (max_tries < 2)); ++ ++ return aggressive ? gc_ok : YAFFS_OK; ++} ++ ++/* ++ * yaffs_bg_gc() ++ * Garbage collects. Intended to be called from a background thread. ++ * Returns non-zero if at least half the free chunks are erased. ++ */ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) ++{ ++ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); ++ ++ yaffs_check_gc(dev, 1); ++ return erased_chunks > dev->n_free_chunks / 2; ++} ++ ++/*-------------------- Data file manipulation -----------------*/ ++ ++static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) ++{ ++ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); ++ ++ if (nand_chunk >= 0) ++ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, ++ buffer, NULL); ++ else { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not found zero instead", ++ nand_chunk); ++ /* get sane (zero) data if you read a hole */ ++ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); ++ return 0; ++ } ++ ++} ++ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn) ++{ ++ int block; ++ int page; ++ struct yaffs_ext_tags tags; ++ struct yaffs_block_info *bi; ++ ++ if (chunk_id <= 0) ++ return; ++ ++ dev->n_deletions++; ++ block = chunk_id / dev->param.chunks_per_block; ++ page = chunk_id % dev->param.chunks_per_block; ++ ++ if (!yaffs_check_chunk_bit(dev, block, page)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Deleting invalid chunk %d", chunk_id); ++ ++ bi = yaffs_get_block_info(dev, block); ++ ++ yaffs2_update_oldest_dirty_seq(dev, block, bi); ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, ++ "line %d delete of chunk %d", ++ lyn, chunk_id); ++ ++ if (!dev->param.is_yaffs2 && mark_flash && ++ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { ++ ++ memset(&tags, 0, sizeof(tags)); ++ tags.is_deleted = 1; ++ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); ++ yaffs_handle_chunk_update(dev, chunk_id, &tags); ++ } else { ++ dev->n_unmarked_deletions++; ++ } ++ ++ /* Pull out of the management area. ++ * If the whole block became dirty, this will kick off an erasure. ++ */ ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || ++ bi->block_state == YAFFS_BLOCK_STATE_FULL || ++ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ dev->n_free_chunks++; ++ yaffs_clear_chunk_bit(dev, block, page); ++ bi->pages_in_use--; ++ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && ++ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ yaffs_block_became_dirty(dev, block); ++ } ++ } ++} ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve) ++{ ++ /* Find old chunk Need to do this to get serial number ++ * Write new one and patch into tree. ++ * Invalidate old tags. ++ */ ++ ++ int prev_chunk_id; ++ struct yaffs_ext_tags prev_tags; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ yaffs_check_gc(dev, 0); ++ ++ /* Get the previous chunk at this location in the file if it exists. ++ * If it does not exist then put a zero into the tree. This creates ++ * the tnode now, rather than later when it is harder to clean up. ++ */ ++ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); ++ if (prev_chunk_id < 1 && ++ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) ++ return 0; ++ ++ /* Set up new tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ ++ new_tags.chunk_id = inode_chunk; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = ++ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; ++ new_tags.n_bytes = n_bytes; ++ ++ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Writing %d bytes to chunk!!!!!!!!!", ++ n_bytes); ++ BUG(); ++ } ++ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); ++ ++ if (new_chunk_id > 0) { ++ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ yaffs_verify_file_sane(in); ++ } ++ return new_chunk_id; ++ ++} ++ ++ ++ ++static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, ++ const YCHAR *name, const void *value, int size, ++ int flags) ++{ ++ struct yaffs_xattr_mod xmod; ++ int result; ++ ++ xmod.set = set; ++ xmod.name = name; ++ xmod.data = value; ++ xmod.size = size; ++ xmod.flags = flags; ++ xmod.result = -ENOSPC; ++ ++ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); ++ ++ if (result > 0) ++ return xmod.result; ++ else ++ return -ENOSPC; ++} ++ ++static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, ++ struct yaffs_xattr_mod *xmod) ++{ ++ int retval = 0; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer = buffer + x_offs; ++ ++ if (xmod->set) ++ retval = ++ nval_set(x_buffer, x_size, xmod->name, xmod->data, ++ xmod->size, xmod->flags); ++ else ++ retval = nval_del(x_buffer, x_size, xmod->name); ++ ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ xmod->result = retval; ++ ++ return retval; ++} ++ ++static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, ++ void *value, int size) ++{ ++ char *buffer = NULL; ++ int result; ++ struct yaffs_ext_tags tags; ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer; ++ int retval = 0; ++ ++ if (obj->hdr_chunk < 1) ++ return -ENODATA; ++ ++ /* If we know that the object has no xattribs then don't do all the ++ * reading and parsing. ++ */ ++ if (obj->xattr_known && !obj->has_xattr) { ++ if (name) ++ return -ENODATA; ++ else ++ return 0; ++ } ++ ++ buffer = (char *)yaffs_get_temp_buffer(dev); ++ if (!buffer) ++ return -ENOMEM; ++ ++ result = ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ retval = -ENOENT; ++ else { ++ x_buffer = buffer + x_offs; ++ ++ if (!obj->xattr_known) { ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ } ++ ++ if (name) ++ retval = nval_get(x_buffer, x_size, name, value, size); ++ else ++ retval = nval_list(x_buffer, x_size, value, size); ++ } ++ yaffs_release_temp_buffer(dev, (u8 *) buffer); ++ return retval; ++} ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, ++ const void *value, int size, int flags) ++{ ++ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); ++} ++ ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); ++} ++ ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, ++ int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, name, value, size); ++} ++ ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); ++} ++ ++static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) ++{ ++ u8 *buf; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ ++ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) ++ return; ++ ++ dev = in->my_dev; ++ in->lazy_loaded = 0; ++ buf = yaffs_get_temp_buffer(dev); ++ ++ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); ++ oh = (struct yaffs_obj_hdr *)buf; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ yaffs_set_obj_name_from_oh(in, oh); ++ ++ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ in->variant.symlink_variant.alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant.symlink_variant.alias) ++ alloc_failed = 1; /* Not returned */ ++ } ++ yaffs_release_temp_buffer(dev, buf); ++} ++ ++/* UpdateObjectHeader updates the header on NAND for an object. ++ * If name is not NULL, then that new name is used. ++ */ ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, ++ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) ++{ ++ ++ struct yaffs_block_info *bi; ++ struct yaffs_dev *dev = in->my_dev; ++ int prev_chunk_id; ++ int ret_val = 0; ++ int result = 0; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_ext_tags old_tags; ++ const YCHAR *alias = NULL; ++ u8 *buffer = NULL; ++ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj_hdr *oh = NULL; ++ loff_t file_size = 0; ++ ++ strcpy(old_name, _Y("silly old name")); ++ ++ if (in->fake && in != dev->root_dir && !force && !xmod) ++ return ret_val; ++ ++ yaffs_check_gc(dev, 0); ++ yaffs_check_obj_details_loaded(in); ++ ++ buffer = yaffs_get_temp_buffer(in->my_dev); ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ prev_chunk_id = in->hdr_chunk; ++ ++ if (prev_chunk_id > 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, ++ buffer, &old_tags); ++ ++ yaffs_verify_oh(in, oh, &old_tags, 0); ++ memcpy(old_name, oh->name, sizeof(oh->name)); ++ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); ++ } else { ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ } ++ ++ oh->type = in->variant_type; ++ oh->yst_mode = in->yst_mode; ++ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; ++ ++ yaffs_load_attribs_oh(oh, in); ++ ++ if (in->parent) ++ oh->parent_obj_id = in->parent->obj_id; ++ else ++ oh->parent_obj_id = 0; ++ ++ if (name && *name) { ++ memset(oh->name, 0, sizeof(oh->name)); ++ yaffs_load_oh_from_name(dev, oh->name, name); ++ } else if (prev_chunk_id > 0) { ++ memcpy(oh->name, old_name, sizeof(oh->name)); ++ } else { ++ memset(oh->name, 0, sizeof(oh->name)); ++ } ++ ++ oh->is_shrink = is_shrink; ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Should not happen */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && ++ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) ++ file_size = in->variant.file_variant.file_size; ++ yaffs_oh_size_load(oh, file_size); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ oh->equiv_id = in->variant.hardlink_variant.equiv_id; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = in->variant.symlink_variant.alias; ++ if (!alias) ++ alias = _Y("no alias"); ++ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); ++ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; ++ break; ++ } ++ ++ /* process any xattrib modifications */ ++ if (xmod) ++ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); ++ ++ /* Tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ in->serial++; ++ new_tags.chunk_id = 0; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = in->serial; ++ ++ /* Add extra info for file header */ ++ new_tags.extra_available = 1; ++ new_tags.extra_parent_id = oh->parent_obj_id; ++ new_tags.extra_file_size = file_size; ++ new_tags.extra_is_shrink = oh->is_shrink; ++ new_tags.extra_equiv_id = oh->equiv_id; ++ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; ++ new_tags.extra_obj_type = in->variant_type; ++ yaffs_verify_oh(in, oh, &new_tags, 1); ++ ++ /* Create new chunk in NAND */ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, ++ (prev_chunk_id > 0) ? 1 : 0); ++ ++ if (buffer) ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (new_chunk_id < 0) ++ return new_chunk_id; ++ ++ in->hdr_chunk = new_chunk_id; ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ if (!yaffs_obj_cache_dirty(in)) ++ in->dirty = 0; ++ ++ /* If this was a shrink, then mark the block ++ * that the chunk lives on */ ++ if (is_shrink) { ++ bi = yaffs_get_block_info(in->my_dev, ++ new_chunk_id / ++ in->my_dev->param.chunks_per_block); ++ bi->has_shrink_hdr = 1; ++ } ++ ++ ++ return new_chunk_id; ++} ++ ++/*--------------------- File read/write ------------------------ ++ * Read and write have very similar structures. ++ * In general the read/write has three parts to it ++ * An incomplete chunk to start with (if the read/write is not chunk-aligned) ++ * Some complete chunks ++ * An incomplete chunk to end off with ++ * ++ * Curve-balls: the first chunk might also be the last chunk. ++ */ ++ ++int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) ++{ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ struct yaffs_cache *cache; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ chunk++; ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ if ((start + n) < dev->data_bytes_per_chunk) ++ n_copy = n; ++ else ++ n_copy = dev->data_bytes_per_chunk - start; ++ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ /* If the chunk is already in the cache or it is less than ++ * a whole chunk or we're using inband tags then use the cache ++ * (if there is caching) else bypass the cache. ++ */ ++ if (cache || n_copy != dev->data_bytes_per_chunk || ++ dev->param.inband_tags) { ++ if (dev->param.n_caches > 0) { ++ ++ /* If we can't find the data in the cache, ++ * then load it up. */ ++ ++ if (!cache) { ++ cache = ++ yaffs_grab_chunk_cache(in->my_dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ cache->n_bytes = 0; ++ } ++ ++ yaffs_use_cache(dev, cache, 0); ++ ++ cache->locked = 1; ++ ++ memcpy(buffer, &cache->data[start], n_copy); ++ ++ cache->locked = 0; ++ } else { ++ /* Read into the local buffer then copy.. */ ++ ++ u8 *local_buffer = ++ yaffs_get_temp_buffer(dev); ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ ++ memcpy(buffer, &local_buffer[start], n_copy); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Read directly into the buffer. */ ++ yaffs_rd_data_obj(in, chunk, buffer); ++ } ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ return n_done; ++} ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ int n_writeback; ++ loff_t start_write = offset; ++ int chunk_written = 0; ++ u32 n_bytes_read; ++ loff_t chunk_start; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0 && chunk_written >= 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ ++ if (((loff_t)chunk) * ++ dev->data_bytes_per_chunk + start != offset || ++ start >= dev->data_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "AddrToChunk of offset %lld gives chunk %d start %d", ++ offset, chunk, start); ++ } ++ chunk++; /* File pos to chunk in file offset */ ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ ++ if ((start + n) < dev->data_bytes_per_chunk) { ++ n_copy = n; ++ ++ /* Now calculate how many bytes to write back.... ++ * If we're overwriting and not writing to then end of ++ * file then we need to write back as much as was there ++ * before. ++ */ ++ ++ chunk_start = (((loff_t)(chunk - 1)) * ++ dev->data_bytes_per_chunk); ++ ++ if (chunk_start > in->variant.file_variant.file_size) ++ n_bytes_read = 0; /* Past end of file */ ++ else ++ n_bytes_read = ++ in->variant.file_variant.file_size - ++ chunk_start; ++ ++ if (n_bytes_read > dev->data_bytes_per_chunk) ++ n_bytes_read = dev->data_bytes_per_chunk; ++ ++ n_writeback = ++ (n_bytes_read > ++ (start + n)) ? n_bytes_read : (start + n); ++ ++ if (n_writeback < 0 || ++ n_writeback > dev->data_bytes_per_chunk) ++ BUG(); ++ ++ } else { ++ n_copy = dev->data_bytes_per_chunk - start; ++ n_writeback = dev->data_bytes_per_chunk; ++ } ++ ++ if (n_copy != dev->data_bytes_per_chunk || ++ !dev->param.cache_bypass_aligned || ++ dev->param.inband_tags) { ++ /* An incomplete start or end chunk (or maybe both ++ * start and end chunk), or we're using inband tags, ++ * or we're forcing writes through the cache, ++ * so we want to use the cache buffers. ++ */ ++ if (dev->param.n_caches > 0) { ++ struct yaffs_cache *cache; ++ ++ /* If we can't find the data in the cache, then ++ * load the cache */ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ if (!cache && ++ yaffs_check_alloc_available(dev, 1)) { ++ cache = yaffs_grab_chunk_cache(dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ } else if (cache && ++ !cache->dirty && ++ !yaffs_check_alloc_available(dev, ++ 1)) { ++ /* Drop the cache if it was a read cache ++ * item and no space check has been made ++ * for it. ++ */ ++ cache = NULL; ++ } ++ ++ if (cache) { ++ yaffs_use_cache(dev, cache, 1); ++ cache->locked = 1; ++ ++ memcpy(&cache->data[start], buffer, ++ n_copy); ++ ++ cache->locked = 0; ++ cache->n_bytes = n_writeback; ++ ++ if (write_through) { ++ chunk_written = ++ yaffs_wr_data_obj ++ (cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ } ++ } else { ++ chunk_written = -1; /* fail write */ ++ } ++ } else { ++ /* An incomplete start or end chunk (or maybe ++ * both start and end chunk). Read into the ++ * local buffer then copy over and write back. ++ */ ++ ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ memcpy(&local_buffer[start], buffer, n_copy); ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, ++ local_buffer, ++ n_writeback, 0); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Write directly from the buffer. */ ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, buffer, ++ dev->data_bytes_per_chunk, 0); ++ ++ /* Since we've overwritten the cached data, ++ * we better invalidate it. */ ++ yaffs_invalidate_chunk_cache(in, chunk); ++ } ++ ++ if (chunk_written >= 0) { ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ } ++ ++ /* Update file object */ ++ ++ if ((start_write + n_done) > in->variant.file_variant.file_size) ++ in->variant.file_variant.file_size = (start_write + n_done); ++ ++ in->dirty = 1; ++ return n_done; ++} ++ ++int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ yaffs2_handle_hole(in, offset); ++ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); ++} ++ ++/* ---------------------- File resizing stuff ------------------ */ ++ ++static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) ++{ ++ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ int i; ++ int chunk_id; ++ u32 dummy; ++ int last_del; ++ int start_del; ++ ++ if (old_size > 0) ++ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); ++ else ++ last_del = 0; ++ ++ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, ++ &start_del, &dummy); ++ last_del++; ++ start_del++; ++ ++ /* Delete backwards so that we don't end up with holes if ++ * power is lost part-way through the operation. ++ */ ++ for (i = last_del; i >= start_del; i--) { ++ /* NB this could be optimised somewhat, ++ * eg. could retrieve the tags and write them without ++ * using yaffs_chunk_del ++ */ ++ ++ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); ++ ++ if (chunk_id < 1) ++ continue; ++ ++ if (chunk_id < ++ (dev->internal_start_block * dev->param.chunks_per_block) || ++ chunk_id >= ++ ((dev->internal_end_block + 1) * ++ dev->param.chunks_per_block)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Found daft chunk_id %d for %d", ++ chunk_id, i); ++ } else { ++ in->n_data_chunks--; ++ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); ++ } ++ } ++} ++ ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) ++{ ++ int new_full; ++ u32 new_partial; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); ++ ++ yaffs_prune_chunks(obj, new_size); ++ ++ if (new_partial != 0) { ++ int last_chunk = 1 + new_full; ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ /* Rewrite the last chunk with its new size and zero pad */ ++ yaffs_rd_data_obj(obj, last_chunk, local_buffer); ++ memset(local_buffer + new_partial, 0, ++ dev->data_bytes_per_chunk - new_partial); ++ ++ yaffs_wr_data_obj(obj, last_chunk, local_buffer, ++ new_partial, 1); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ ++ obj->variant.file_variant.file_size = new_size; ++ ++ yaffs_prune_tree(dev, &obj->variant.file_variant); ++} ++ ++int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) ++{ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ ++ yaffs_flush_file_cache(in, 1); ++ yaffs_invalidate_whole_cache(in); ++ ++ yaffs_check_gc(dev, 0); ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ if (new_size == old_size) ++ return YAFFS_OK; ++ ++ if (new_size > old_size) { ++ yaffs2_handle_hole(in, new_size); ++ in->variant.file_variant.file_size = new_size; ++ } else { ++ /* new_size < old_size */ ++ yaffs_resize_file_down(in, new_size); ++ } ++ ++ /* Write a new object header to reflect the resize. ++ * show we've shrunk the file, if need be ++ * Do this only if the file is not in the deleted directories ++ * and is not shadowed. ++ */ ++ if (in->parent && ++ !in->is_shadowed && ++ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ in->parent->obj_id != YAFFS_OBJECTID_DELETED) ++ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); ++ ++ return YAFFS_OK; ++} ++ ++int yaffs_flush_file(struct yaffs_obj *in, ++ int update_time, ++ int data_sync, ++ int discard_cache) ++{ ++ if (!in->dirty) ++ return YAFFS_OK; ++ ++ yaffs_flush_file_cache(in, discard_cache); ++ ++ if (data_sync) ++ return YAFFS_OK; ++ ++ if (update_time) ++ yaffs_load_current_time(in, 0, 0); ++ ++ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? ++ YAFFS_OK : YAFFS_FAIL; ++} ++ ++ ++/* yaffs_del_file deletes the whole file data ++ * and the inode associated with the file. ++ * It does not delete the links associated with the file. ++ */ ++static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) ++{ ++ int ret_val; ++ int del_now = 0; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!in->my_inode) ++ del_now = 1; ++ ++ if (del_now) { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->del_dir, ++ _Y("deleted"), 0, 0); ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: immediate deletion of file %d", ++ in->obj_id); ++ in->deleted = 1; ++ in->my_dev->n_deleted_files++; ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ yaffs_soft_del_file(in); ++ } else { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++ return ret_val; ++} ++ ++static int yaffs_del_file(struct yaffs_obj *in) ++{ ++ int ret_val = YAFFS_OK; ++ int deleted; /* Need to cache value on stack if in is freed */ ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ ++ if (in->n_data_chunks > 0) { ++ /* Use soft deletion if there is data in the file. ++ * That won't be the case if it has been resized to zero. ++ */ ++ if (!in->unlinked) ++ ret_val = yaffs_unlink_file_if_needed(in); ++ ++ deleted = in->deleted; ++ ++ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { ++ in->deleted = 1; ++ deleted = 1; ++ in->my_dev->n_deleted_files++; ++ yaffs_soft_del_file(in); ++ } ++ return deleted ? YAFFS_OK : YAFFS_FAIL; ++ } else { ++ /* The file has no data chunks so we toss it immediately */ ++ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); ++ in->variant.file_variant.top = NULL; ++ yaffs_generic_obj_del(in); ++ ++ return YAFFS_OK; ++ } ++} ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj) ++{ ++ return (obj && ++ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && ++ !(list_empty(&obj->variant.dir_variant.children)); ++} ++ ++static int yaffs_del_dir(struct yaffs_obj *obj) ++{ ++ /* First check that the directory is empty. */ ++ if (yaffs_is_non_empty_dir(obj)) ++ return YAFFS_FAIL; ++ ++ return yaffs_generic_obj_del(obj); ++} ++ ++static int yaffs_del_symlink(struct yaffs_obj *in) ++{ ++ kfree(in->variant.symlink_variant.alias); ++ in->variant.symlink_variant.alias = NULL; ++ ++ return yaffs_generic_obj_del(in); ++} ++ ++static int yaffs_del_link(struct yaffs_obj *in) ++{ ++ /* remove this hardlink from the list associated with the equivalent ++ * object ++ */ ++ list_del_init(&in->hard_links); ++ return yaffs_generic_obj_del(in); ++} ++ ++int yaffs_del_obj(struct yaffs_obj *obj) ++{ ++ int ret_val = -1; ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ ret_val = yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!list_empty(&obj->variant.dir_variant.dirty)) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Remove object %d from dirty directories", ++ obj->obj_id); ++ list_del_init(&obj->variant.dir_variant.dirty); ++ } ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ ret_val = yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ ret_val = yaffs_del_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ ret_val = yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ ret_val = 0; ++ break; /* should not happen. */ ++ } ++ return ret_val; ++} ++ ++ ++static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, ++ struct yaffs_obj *to_dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_add_obj_to_dir(to_dir, obj); ++ } ++} ++ ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type) ++{ ++ /* Tear down the old variant */ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ /* Nuke file data */ ++ yaffs_resize_file(obj, 0); ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Put the children in lost and found. */ ++ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); ++ if (!list_empty(&obj->variant.dir_variant.dirty)) ++ list_del_init(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ /* Nuke symplink data */ ++ kfree(obj->variant.symlink_variant.alias); ++ obj->variant.symlink_variant.alias = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ list_del_init(&obj->hard_links); ++ break; ++ default: ++ break; ++ } ++ ++ memset(&obj->variant, 0, sizeof(obj->variant)); ++ ++ /*Set up new variant if the memset is not enough. */ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ default: ++ break; ++ } ++ ++ obj->variant_type = type; ++ ++ return obj; ++ ++} ++ ++static int yaffs_unlink_worker(struct yaffs_obj *obj) ++{ ++ int del_now = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (!obj->my_inode) ++ del_now = 1; ++ ++ yaffs_update_parent(obj->parent); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ return yaffs_del_link(obj); ++ } else if (!list_empty(&obj->hard_links)) { ++ /* Curve ball: We're unlinking an object that has a hardlink. ++ * ++ * This problem arises because we are not strictly following ++ * The Linux link/inode model. ++ * ++ * We can't really delete the object. ++ * Instead, we do the following: ++ * - Select a hardlink. ++ * - Unhook it from the hard links ++ * - Move it from its parent directory so that the rename works. ++ * - Rename the object to the hardlink's name. ++ * - Delete the hardlink ++ */ ++ ++ struct yaffs_obj *hl; ++ struct yaffs_obj *parent; ++ int ret_val; ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ hl = list_entry(obj->hard_links.next, struct yaffs_obj, ++ hard_links); ++ ++ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); ++ parent = hl->parent; ++ ++ list_del_init(&hl->hard_links); ++ ++ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); ++ ++ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); ++ ++ if (ret_val == YAFFS_OK) ++ ret_val = yaffs_generic_obj_del(hl); ++ ++ return ret_val; ++ ++ } else if (del_now) { ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ list_del_init(&obj->variant.dir_variant.dirty); ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ return yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ return YAFFS_FAIL; ++ } ++ } else if (yaffs_is_non_empty_dir(obj)) { ++ return YAFFS_FAIL; ++ } else { ++ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++} ++ ++static int yaffs_unlink_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->unlink_allowed) ++ return yaffs_unlink_worker(obj); ++ ++ return YAFFS_FAIL; ++} ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) ++{ ++ struct yaffs_obj *obj; ++ ++ obj = yaffs_find_by_name(dir, name); ++ return yaffs_unlink_obj(obj); ++} ++ ++/* Note: ++ * If old_name is NULL then we take old_dir as the object to be renamed. ++ */ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, ++ struct yaffs_obj *new_dir, const YCHAR *new_name) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *existing_target = NULL; ++ int force = 0; ++ int result; ++ struct yaffs_dev *dev; ++ ++ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ dev = old_dir->my_dev; ++ ++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE ++ /* Special case for case insemsitive systems. ++ * While look-up is case insensitive, the name isn't. ++ * Therefore we might want to change x.txt to X.txt ++ */ ++ if (old_dir == new_dir && ++ old_name && new_name && ++ strcmp(old_name, new_name) == 0) ++ force = 1; ++#endif ++ ++ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ /* ENAMETOOLONG */ ++ return YAFFS_FAIL; ++ ++ if (old_name) ++ obj = yaffs_find_by_name(old_dir, old_name); ++ else{ ++ obj = old_dir; ++ old_dir = obj->parent; ++ } ++ ++ if (obj && obj->rename_allowed) { ++ /* Now handle an existing target, if there is one */ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ if (yaffs_is_non_empty_dir(existing_target)) { ++ return YAFFS_FAIL; /* ENOTEMPTY */ ++ } else if (existing_target && existing_target != obj) { ++ /* Nuke the target first, using shadowing, ++ * but only if it isn't the same object. ++ * ++ * Note we must disable gc here otherwise it can mess ++ * up the shadowing. ++ * ++ */ ++ dev->gc_disable = 1; ++ yaffs_change_obj_name(obj, new_dir, new_name, force, ++ existing_target->obj_id); ++ existing_target->is_shadowed = 1; ++ yaffs_unlink_obj(existing_target); ++ dev->gc_disable = 0; ++ } ++ ++ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); ++ ++ yaffs_update_parent(old_dir); ++ if (new_dir != old_dir) ++ yaffs_update_parent(new_dir); ++ ++ return result; ++ } ++ return YAFFS_FAIL; ++} ++ ++/*----------------------- Initialisation Scanning ---------------------- */ ++ ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning) ++{ ++ struct yaffs_obj *obj; ++ ++ if (backward_scanning) { ++ /* Handle YAFFS2 case (backward scanning) ++ * If the shadowed object exists then ignore. ++ */ ++ obj = yaffs_find_by_number(dev, obj_id); ++ if (obj) ++ return; ++ } ++ ++ /* Let's create it (if it does not exist) assuming it is a file so that ++ * it can do shrinking etc. ++ * We put it in unlinked dir to be cleaned up after the scanning ++ */ ++ obj = ++ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); ++ if (!obj) ++ return; ++ obj->is_shadowed = 1; ++ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); ++ obj->variant.file_variant.shrink_size = 0; ++ obj->valid = 1; /* So that we don't read any other info. */ ++} ++ ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) ++{ ++ struct list_head *lh; ++ struct list_head *save; ++ struct yaffs_obj *hl; ++ struct yaffs_obj *in; ++ ++ list_for_each_safe(lh, save, hard_list) { ++ hl = list_entry(lh, struct yaffs_obj, hard_links); ++ in = yaffs_find_by_number(dev, ++ hl->variant.hardlink_variant.equiv_id); ++ ++ if (in) { ++ /* Add the hardlink pointers */ ++ hl->variant.hardlink_variant.equiv_obj = in; ++ list_add(&hl->hard_links, &in->hard_links); ++ } else { ++ /* Todo Need to report/handle this better. ++ * Got a problem... hardlink to a non-existant object ++ */ ++ hl->variant.hardlink_variant.equiv_obj = NULL; ++ INIT_LIST_HEAD(&hl->hard_links); ++ } ++ } ++} ++ ++static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) ++{ ++ /* ++ * Sort out state of unlinked and deleted objects after scanning. ++ */ ++ struct list_head *i; ++ struct list_head *n; ++ struct yaffs_obj *l; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Soft delete all the unlinked files */ ++ list_for_each_safe(i, n, ++ &dev->unlinked_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++ ++ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++} ++ ++/* ++ * This code iterates through all the objects making sure that they are rooted. ++ * Any unrooted objects are re-rooted in lost+found. ++ * An object needs to be in one of: ++ * - Directly under deleted, unlinked ++ * - Directly or indirectly under root. ++ * ++ * Note: ++ * This code assumes that we don't ever change the current relationships ++ * between directories: ++ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL ++ * lost-n-found->parent == root_dir ++ * ++ * This fixes the problem where directories might have inadvertently been ++ * deleted leaving the object "hanging" without being rooted in the ++ * directory tree. ++ */ ++ ++static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ return (obj == dev->del_dir || ++ obj == dev->unlinked_dir || obj == dev->root_dir); ++} ++ ++static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_obj *parent; ++ int i; ++ struct list_head *lh; ++ struct list_head *n; ++ int depth_limit; ++ int hanging; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Iterate through the objects in each hash entry, ++ * looking at each object. ++ * Make sure it is rooted. ++ */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ parent = obj->parent; ++ ++ if (yaffs_has_null_parent(dev, obj)) { ++ /* These directories are not hanging */ ++ hanging = 0; ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ hanging = 1; ++ } else if (yaffs_has_null_parent(dev, parent)) { ++ hanging = 0; ++ } else { ++ /* ++ * Need to follow the parent chain to ++ * see if it is hanging. ++ */ ++ hanging = 0; ++ depth_limit = 100; ++ ++ while (parent != dev->root_dir && ++ parent->parent && ++ parent->parent->variant_type == ++ YAFFS_OBJECT_TYPE_DIRECTORY && ++ depth_limit > 0) { ++ parent = parent->parent; ++ depth_limit--; ++ } ++ if (parent != dev->root_dir) ++ hanging = 1; ++ } ++ if (hanging) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Hanging object %d moved to lost and found", ++ obj->obj_id); ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ } ++ } ++ } ++} ++ ++/* ++ * Delete directory contents for cleaning up lost and found. ++ */ ++static void yaffs_del_dir_contents(struct yaffs_obj *dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ BUG(); ++ ++ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) ++ yaffs_del_dir_contents(obj); ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Deleting lost_found object %d", ++ obj->obj_id); ++ yaffs_unlink_obj(obj); ++ } ++} ++ ++static void yaffs_empty_l_n_f(struct yaffs_dev *dev) ++{ ++ yaffs_del_dir_contents(dev->lost_n_found); ++} ++ ++ ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, ++ const YCHAR *name) ++{ ++ int sum; ++ struct list_head *i; ++ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj *l; ++ ++ if (!name) ++ return NULL; ++ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: null pointer directory" ++ ); ++ BUG(); ++ return NULL; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: non-directory" ++ ); ++ BUG(); ++ } ++ ++ sum = yaffs_calc_name_sum(name); ++ ++ list_for_each(i, &directory->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ ++ if (l->parent != directory) ++ BUG(); ++ ++ yaffs_check_obj_details_loaded(l); ++ ++ /* Special case for lost-n-found */ ++ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) ++ return l; ++ } else if (l->sum == sum || l->hdr_chunk <= 0) { ++ /* LostnFound chunk called Objxxx ++ * Do a real check ++ */ ++ yaffs_get_obj_name(l, buffer, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) ++ return l; ++ } ++ } ++ return NULL; ++} ++ ++/* GetEquivalentObject dereferences any hard links to get to the ++ * actual object. ++ */ ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ obj = obj->variant.hardlink_variant.equiv_obj; ++ yaffs_check_obj_details_loaded(obj); ++ } ++ return obj; ++} ++ ++/* ++ * A note or two on object names. ++ * * If the object name is missing, we then make one up in the form objnnn ++ * ++ * * ASCII names are stored in the object header's name field from byte zero ++ * * Unicode names are historically stored starting from byte zero. ++ * ++ * Then there are automatic Unicode names... ++ * The purpose of these is to save names in a way that can be read as ++ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII ++ * system to share files. ++ * ++ * These automatic unicode are stored slightly differently... ++ * - If the name can fit in the ASCII character space then they are saved as ++ * ascii names as per above. ++ * - If the name needs Unicode then the name is saved in Unicode ++ * starting at oh->name[1]. ++ ++ */ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size) ++{ ++ /* Create an object name if we could not find one. */ ++ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { ++ YCHAR local_name[20]; ++ YCHAR num_string[20]; ++ YCHAR *x = &num_string[19]; ++ unsigned v = obj->obj_id; ++ num_string[19] = 0; ++ while (v > 0) { ++ x--; ++ *x = '0' + (v % 10); ++ v /= 10; ++ } ++ /* make up a name */ ++ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); ++ strcat(local_name, x); ++ strncpy(name, local_name, buffer_size - 1); ++ } ++} ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) ++{ ++ memset(name, 0, buffer_size * sizeof(YCHAR)); ++ yaffs_check_obj_details_loaded(obj); ++ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); ++ } else if (obj->short_name[0]) { ++ strcpy(name, obj->short_name); ++ } else if (obj->hdr_chunk > 0) { ++ int result; ++ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); ++ ++ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; ++ ++ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); ++ ++ if (obj->hdr_chunk > 0) { ++ result = yaffs_rd_chunk_tags_nand(obj->my_dev, ++ obj->hdr_chunk, ++ buffer, NULL); ++ } ++ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, ++ buffer_size); ++ ++ yaffs_release_temp_buffer(obj->my_dev, buffer); ++ } ++ ++ yaffs_fix_null_name(obj, name, buffer_size); ++ ++ return strnlen(name, YAFFS_MAX_NAME_LENGTH); ++} ++ ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj) ++{ ++ /* Dereference any hard linking */ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ return obj->variant.file_variant.file_size; ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ if (!obj->variant.symlink_variant.alias) ++ return 0; ++ return strnlen(obj->variant.symlink_variant.alias, ++ YAFFS_MAX_ALIAS_LENGTH); ++ } else { ++ /* Only a directory should drop through to here */ ++ return obj->my_dev->data_bytes_per_chunk; ++ } ++} ++ ++int yaffs_get_obj_link_count(struct yaffs_obj *obj) ++{ ++ int count = 0; ++ struct list_head *i; ++ ++ if (!obj->unlinked) ++ count++; /* the object itself */ ++ ++ list_for_each(i, &obj->hard_links) ++ count++; /* add the hard links; */ ++ ++ return count; ++} ++ ++int yaffs_get_obj_inode(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ return obj->obj_id; ++} ++ ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ return DT_DIR; ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return DT_LNK; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ if (S_ISFIFO(obj->yst_mode)) ++ return DT_FIFO; ++ if (S_ISCHR(obj->yst_mode)) ++ return DT_CHR; ++ if (S_ISBLK(obj->yst_mode)) ++ return DT_BLK; ++ if (S_ISSOCK(obj->yst_mode)) ++ return DT_SOCK; ++ return DT_REG; ++ break; ++ default: ++ return DT_REG; ++ break; ++ } ++} ++ ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) ++ return yaffs_clone_str(obj->variant.symlink_variant.alias); ++ else ++ return yaffs_clone_str(_Y("")); ++} ++ ++/*--------------------------- Initialisation code -------------------------- */ ++ ++static int yaffs_check_dev_fns(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ struct yaffs_tags_handler *tagger = &dev->tagger; ++ ++ /* Common functions, gotta have */ ++ if (!drv->drv_read_chunk_fn || ++ !drv->drv_write_chunk_fn || ++ !drv->drv_erase_fn) ++ return 0; ++ ++ if (dev->param.is_yaffs2 && ++ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) ++ return 0; ++ ++ /* Install the default tags marshalling functions if needed. */ ++ yaffs_tags_compat_install(dev); ++ yaffs_tags_marshall_install(dev); ++ ++ /* Check we now have the marshalling functions required. */ ++ if (!tagger->write_chunk_tags_fn || ++ !tagger->read_chunk_tags_fn || ++ !tagger->query_block_fn || ++ !tagger->mark_bad_fn) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs_create_initial_dir(struct yaffs_dev *dev) ++{ ++ /* Initialise the unlinked, deleted, root and lost+found directories */ ++ dev->lost_n_found = dev->root_dir = NULL; ++ dev->unlinked_dir = dev->del_dir = NULL; ++ dev->unlinked_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); ++ dev->del_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); ++ dev->root_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, ++ YAFFS_ROOT_MODE | S_IFDIR); ++ dev->lost_n_found = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, ++ YAFFS_LOSTNFOUND_MODE | S_IFDIR); ++ ++ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir ++ && dev->del_dir) { ++ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++/* Low level init. ++ * Typically only used by yaffs_guts_initialise, but also used by the ++ * Low level yaffs driver tests. ++ */ ++ ++int yaffs_guts_ll_init(struct yaffs_dev *dev) ++{ ++ ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); ++ ++ if (!dev) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Need a device" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ if (dev->ll_init) ++ return YAFFS_OK; ++ ++ dev->internal_start_block = dev->param.start_block; ++ dev->internal_end_block = dev->param.end_block; ++ dev->block_offset = 0; ++ dev->chunk_offset = 0; ++ dev->n_free_chunks = 0; ++ ++ dev->gc_block = 0; ++ ++ if (dev->param.start_block == 0) { ++ dev->internal_start_block = dev->param.start_block + 1; ++ dev->internal_end_block = dev->param.end_block + 1; ++ dev->block_offset = 1; ++ dev->chunk_offset = dev->param.chunks_per_block; ++ } ++ ++ /* Check geometry parameters. */ ++ ++ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 1024) || ++ (!dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 512) || ++ (dev->param.inband_tags && !dev->param.is_yaffs2) || ++ dev->param.chunks_per_block < 2 || ++ dev->param.n_reserved_blocks < 2 || ++ dev->internal_start_block <= 0 || ++ dev->internal_end_block <= 0 || ++ dev->internal_end_block <= ++ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) ++ ) { ++ /* otherwise it is too small */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", ++ dev->param.total_bytes_per_chunk, ++ dev->param.is_yaffs2 ? "2" : "", ++ dev->param.inband_tags); ++ return YAFFS_FAIL; ++ } ++ ++ /* Sort out space for inband tags, if required */ ++ if (dev->param.inband_tags) ++ dev->data_bytes_per_chunk = ++ dev->param.total_bytes_per_chunk - ++ sizeof(struct yaffs_packed_tags2_tags_only); ++ else ++ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; ++ ++ /* Got the right mix of functions? */ ++ if (!yaffs_check_dev_fns(dev)) { ++ /* Function missing */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "device function(s) missing or wrong"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ if (yaffs_init_nand(dev) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev) ++{ ++ int i; ++ enum yaffs_block_state state; ++ u32 dummy; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if(dev->is_mounted) ++ return YAFFS_FAIL; ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ yaffs_query_init_block_state(dev, i, &state, &dummy); ++ if (state != YAFFS_BLOCK_STATE_DEAD) ++ yaffs_erase_block(dev, i); ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev) ++{ ++ int init_failed = 0; ++ unsigned x; ++ int bits; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if (dev->is_mounted) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); ++ return YAFFS_FAIL; ++ } ++ ++ dev->is_mounted = 1; ++ ++ /* OK now calculate a few things for the device */ ++ ++ /* ++ * Calculate all the chunk size manipulation numbers: ++ */ ++ x = dev->data_bytes_per_chunk; ++ /* We always use dev->chunk_shift and dev->chunk_div */ ++ dev->chunk_shift = calc_shifts(x); ++ x >>= dev->chunk_shift; ++ dev->chunk_div = x; ++ /* We only use chunk mask if chunk_div is 1 */ ++ dev->chunk_mask = (1 << dev->chunk_shift) - 1; ++ ++ /* ++ * Calculate chunk_grp_bits. ++ * We need to find the next power of 2 > than internal_end_block ++ */ ++ ++ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); ++ ++ bits = calc_shifts_ceiling(x); ++ ++ /* Set up tnode width if wide tnodes are enabled. */ ++ if (!dev->param.wide_tnodes_disabled) { ++ /* bits must be even so that we end up with 32-bit words */ ++ if (bits & 1) ++ bits++; ++ if (bits < 16) ++ dev->tnode_width = 16; ++ else ++ dev->tnode_width = bits; ++ } else { ++ dev->tnode_width = 16; ++ } ++ ++ dev->tnode_mask = (1 << dev->tnode_width) - 1; ++ ++ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), ++ * so if the bitwidth of the ++ * chunk range we're using is greater than 16 we need ++ * to figure out chunk shift and chunk_grp_size ++ */ ++ ++ if (bits <= dev->tnode_width) ++ dev->chunk_grp_bits = 0; ++ else ++ dev->chunk_grp_bits = bits - dev->tnode_width; ++ ++ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; ++ if (dev->tnode_size < sizeof(struct yaffs_tnode)) ++ dev->tnode_size = sizeof(struct yaffs_tnode); ++ ++ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; ++ ++ if (dev->param.chunks_per_block < dev->chunk_grp_size) { ++ /* We have a problem because the soft delete won't work if ++ * the chunk group size > chunks per block. ++ * This can be remedied by using larger "virtual blocks". ++ */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ /* Finished verifying the device, continue with initialisation */ ++ ++ /* More device initialisation */ ++ dev->all_gcs = 0; ++ dev->passive_gc_count = 0; ++ dev->oldest_dirty_gc_count = 0; ++ dev->bg_gcs = 0; ++ dev->gc_block_finder = 0; ++ dev->buffered_block = -1; ++ dev->doing_buffered_block_rewrite = 0; ++ dev->n_deleted_files = 0; ++ dev->n_bg_deletions = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_ecc_fixed = 0; ++ dev->n_ecc_unfixed = 0; ++ dev->n_tags_ecc_fixed = 0; ++ dev->n_tags_ecc_unfixed = 0; ++ dev->n_erase_failures = 0; ++ dev->n_erased_blocks = 0; ++ dev->gc_disable = 0; ++ dev->has_pending_prioritised_gc = 1; ++ /* Assume the worst for now, will get fixed on first GC */ ++ INIT_LIST_HEAD(&dev->dirty_dirs); ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ ++ /* Initialise temporary buffers and caches. */ ++ if (!yaffs_init_tmp_buffers(dev)) ++ init_failed = 1; ++ ++ dev->cache = NULL; ++ dev->gc_cleanup_list = NULL; ++ ++ if (!init_failed && dev->param.n_caches > 0) { ++ int i; ++ void *buf; ++ int cache_bytes = ++ dev->param.n_caches * sizeof(struct yaffs_cache); ++ ++ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) ++ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; ++ ++ dev->cache = kmalloc(cache_bytes, GFP_NOFS); ++ ++ buf = (u8 *) dev->cache; ++ ++ if (dev->cache) ++ memset(dev->cache, 0, cache_bytes); ++ ++ for (i = 0; i < dev->param.n_caches && buf; i++) { ++ dev->cache[i].object = NULL; ++ dev->cache[i].last_use = 0; ++ dev->cache[i].dirty = 0; ++ dev->cache[i].data = buf = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ } ++ if (!buf) ++ init_failed = 1; ++ ++ dev->cache_last_use = 0; ++ } ++ ++ dev->cache_hits = 0; ++ ++ if (!init_failed) { ++ dev->gc_cleanup_list = ++ kmalloc(dev->param.chunks_per_block * sizeof(u32), ++ GFP_NOFS); ++ if (!dev->gc_cleanup_list) ++ init_failed = 1; ++ } ++ ++ if (dev->param.is_yaffs2) ++ dev->param.use_header_file_size = 1; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && dev->param.is_yaffs2 && ++ !dev->param.disable_summary && ++ !yaffs_summary_init(dev)) ++ init_failed = 1; ++ ++ if (!init_failed) { ++ /* Now scan the flash. */ ++ if (dev->param.is_yaffs2) { ++ if (yaffs2_checkpt_restore(dev)) { ++ yaffs_check_obj_details_loaded(dev->root_dir); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | ++ YAFFS_TRACE_MOUNT, ++ "yaffs: restored from checkpoint" ++ ); ++ } else { ++ ++ /* Clean up the mess caused by an aborted ++ * checkpoint load then scan backwards. ++ */ ++ yaffs_deinit_blocks(dev); ++ ++ yaffs_deinit_tnodes_and_objs(dev); ++ ++ dev->n_erased_blocks = 0; ++ dev->n_free_chunks = 0; ++ dev->alloc_block = -1; ++ dev->alloc_page = -1; ++ dev->n_deleted_files = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_bg_deletions = 0; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed ++ && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && !yaffs2_scan_backwards(dev)) ++ init_failed = 1; ++ } ++ } else if (!yaffs1_scan(dev)) { ++ init_failed = 1; ++ } ++ ++ yaffs_strip_deleted_objs(dev); ++ yaffs_fix_hanging_objs(dev); ++ if (dev->param.empty_lost_n_found) ++ yaffs_empty_l_n_f(dev); ++ } ++ ++ if (init_failed) { ++ /* Clean up the mess */ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() aborted."); ++ ++ yaffs_deinitialise(dev); ++ return YAFFS_FAIL; ++ } ++ ++ /* Zero out stats */ ++ dev->n_page_reads = 0; ++ dev->n_page_writes = 0; ++ dev->n_erasures = 0; ++ dev->n_gc_copies = 0; ++ dev->n_retried_writes = 0; ++ ++ dev->n_retired_blocks = 0; ++ ++ yaffs_verify_free_chunks(dev); ++ yaffs_verify_blocks(dev); ++ ++ /* Clean up any aborted checkpoint data */ ++ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() done."); ++ return YAFFS_OK; ++} ++ ++void yaffs_deinitialise(struct yaffs_dev *dev) ++{ ++ if (dev->is_mounted) { ++ int i; ++ ++ yaffs_deinit_blocks(dev); ++ yaffs_deinit_tnodes_and_objs(dev); ++ yaffs_summary_deinit(dev); ++ ++ if (dev->param.n_caches > 0 && dev->cache) { ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ kfree(dev->cache[i].data); ++ dev->cache[i].data = NULL; ++ } ++ ++ kfree(dev->cache); ++ dev->cache = NULL; ++ } ++ ++ kfree(dev->gc_cleanup_list); ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ kfree(dev->temp_buffer[i].buffer); ++ dev->temp_buffer[i].buffer = NULL; ++ } ++ ++ kfree(dev->checkpt_buffer); ++ dev->checkpt_buffer = NULL; ++ kfree(dev->checkpt_block_list); ++ dev->checkpt_block_list = NULL; ++ ++ dev->is_mounted = 0; ++ ++ yaffs_deinit_nand(dev); ++ } ++} ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev) ++{ ++ int n_free = 0; ++ int b; ++ struct yaffs_block_info *blk; ++ ++ blk = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ switch (blk->block_state) { ++ case YAFFS_BLOCK_STATE_EMPTY: ++ case YAFFS_BLOCK_STATE_ALLOCATING: ++ case YAFFS_BLOCK_STATE_COLLECTING: ++ case YAFFS_BLOCK_STATE_FULL: ++ n_free += ++ (dev->param.chunks_per_block - blk->pages_in_use + ++ blk->soft_del_pages); ++ break; ++ default: ++ break; ++ } ++ blk++; ++ } ++ return n_free; ++} ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev) ++{ ++ /* This is what we report to the outside world */ ++ int n_free; ++ int n_dirty_caches; ++ int blocks_for_checkpt; ++ int i; ++ ++ n_free = dev->n_free_chunks; ++ n_free += dev->n_deleted_files; ++ ++ /* Now count and subtract the number of dirty chunks in the cache. */ ++ ++ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].dirty) ++ n_dirty_caches++; ++ } ++ ++ n_free -= n_dirty_caches; ++ ++ n_free -= ++ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); ++ ++ /* Now figure checkpoint space and report that... */ ++ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); ++ ++ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); ++ ++ if (n_free < 0) ++ n_free = 0; ++ ++ return n_free; ++} ++ ++ ++ ++/* ++ * Marshalling functions to get loff_t file sizes into and out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) ++{ ++ oh->file_size_low = (fsize & 0xFFFFFFFF); ++ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); ++} ++ ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) ++{ ++ loff_t retval; ++ ++ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) ++ retval = (((loff_t) oh->file_size_high) << 32) | ++ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); ++ else ++ retval = (loff_t) oh->file_size_low; ++ ++ return retval; ++} ++ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ int s; ++ ++ for(i = 0; i < 10; i++) ++ bs[i] = 0; ++ ++ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ bi = yaffs_get_block_info(dev, i); ++ s = bi->block_state; ++ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) ++ bs[0]++; ++ else ++ bs[s]++; ++ } ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_guts.h linux-3.18.14/fs/yaffs2/yaffs_guts.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_guts.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,1010 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GUTS_H__ ++#define __YAFFS_GUTS_H__ ++ ++#include "yportenv.h" ++ ++#define YAFFS_OK 1 ++#define YAFFS_FAIL 0 ++ ++/* Give us a Y=0x59, ++ * Give us an A=0x41, ++ * Give us an FF=0xff ++ * Give us an S=0x53 ++ * And what have we got... ++ */ ++#define YAFFS_MAGIC 0x5941ff53 ++ ++/* ++ * Tnodes form a tree with the tnodes in "levels" ++ * Levels greater than 0 hold 8 slots which point to other tnodes. ++ * Those at level 0 hold 16 slots which point to chunks in NAND. ++ * ++ * A maximum level of 8 thust supports files of size up to: ++ * ++ * 2^(3*MAX_LEVEL+4) ++ * ++ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives ++ * a maximum file size of around 512Gbytees with 2k chunks. ++ */ ++#define YAFFS_NTNODES_LEVEL0 16 ++#define YAFFS_TNODES_LEVEL0_BITS 4 ++#define YAFFS_TNODES_LEVEL0_MASK 0xf ++ ++#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) ++#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) ++#define YAFFS_TNODES_INTERNAL_MASK 0x7 ++#define YAFFS_TNODES_MAX_LEVEL 8 ++#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ ++ YAFFS_TNODES_INTERNAL_BITS * \ ++ YAFFS_TNODES_MAX_LEVEL) ++#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) ++ ++#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff ++ ++/* Constants for YAFFS1 mode */ ++#define YAFFS_BYTES_PER_SPARE 16 ++#define YAFFS_BYTES_PER_CHUNK 512 ++#define YAFFS_CHUNK_SIZE_SHIFT 9 ++#define YAFFS_CHUNKS_PER_BLOCK 32 ++#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) ++ ++#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 ++#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 ++ ++ ++ ++#define YAFFS_ALLOCATION_NOBJECTS 100 ++#define YAFFS_ALLOCATION_NTNODES 100 ++#define YAFFS_ALLOCATION_NLINKS 100 ++ ++#define YAFFS_NOBJECT_BUCKETS 256 ++ ++#define YAFFS_OBJECT_SPACE 0x40000 ++#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) ++ ++/* Binary data version stamps */ ++#define YAFFS_SUMMARY_VERSION 1 ++#define YAFFS_CHECKPOINT_VERSION 7 ++ ++#ifdef CONFIG_YAFFS_UNICODE ++#define YAFFS_MAX_NAME_LENGTH 127 ++#define YAFFS_MAX_ALIAS_LENGTH 79 ++#else ++#define YAFFS_MAX_NAME_LENGTH 255 ++#define YAFFS_MAX_ALIAS_LENGTH 159 ++#endif ++ ++#define YAFFS_SHORT_NAME_LENGTH 15 ++ ++/* Some special object ids for pseudo objects */ ++#define YAFFS_OBJECTID_ROOT 1 ++#define YAFFS_OBJECTID_LOSTNFOUND 2 ++#define YAFFS_OBJECTID_UNLINKED 3 ++#define YAFFS_OBJECTID_DELETED 4 ++ ++/* Fake object Id for summary data */ ++#define YAFFS_OBJECTID_SUMMARY 0x10 ++ ++/* Pseudo object ids for checkpointing */ ++#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 ++#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 ++ ++#define YAFFS_MAX_SHORT_OP_CACHES 20 ++ ++#define YAFFS_N_TEMP_BUFFERS 6 ++ ++/* We limit the number attempts at sucessfully saving a chunk of data. ++ * Small-page devices have 32 pages per block; large-page devices have 64. ++ * Default to something in the order of 5 to 10 blocks worth of chunks. ++ */ ++#define YAFFS_WR_ATTEMPTS (5*64) ++ ++/* Sequence numbers are used in YAFFS2 to determine block allocation order. ++ * The range is limited slightly to help distinguish bad numbers from good. ++ * This also allows us to perhaps in the future use special numbers for ++ * special purposes. ++ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, ++ * and is a larger number than the lifetime of a 2GB device. ++ */ ++#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 ++#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 ++ ++/* Special sequence number for bad block that failed to be marked bad */ ++#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 ++ ++/* ChunkCache is used for short read/write operations.*/ ++struct yaffs_cache { ++ struct yaffs_obj *object; ++ int chunk_id; ++ int last_use; ++ int dirty; ++ int n_bytes; /* Only valid if the cache is dirty */ ++ int locked; /* Can't push out or flush while locked. */ ++ u8 *data; ++}; ++ ++/* yaffs1 tags structures in RAM ++ * NB This uses bitfield. Bitfields should not straddle a u32 boundary ++ * otherwise the structure size will get blown out. ++ */ ++ ++struct yaffs_tags { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes_lsb:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 n_bytes_msb:2; ++}; ++ ++union yaffs_tags_union { ++ struct yaffs_tags as_tags; ++ u8 as_bytes[8]; ++}; ++ ++ ++/* Stuff used for extended tags in YAFFS2 */ ++ ++enum yaffs_ecc_result { ++ YAFFS_ECC_RESULT_UNKNOWN, ++ YAFFS_ECC_RESULT_NO_ERROR, ++ YAFFS_ECC_RESULT_FIXED, ++ YAFFS_ECC_RESULT_UNFIXED ++}; ++ ++enum yaffs_obj_type { ++ YAFFS_OBJECT_TYPE_UNKNOWN, ++ YAFFS_OBJECT_TYPE_FILE, ++ YAFFS_OBJECT_TYPE_SYMLINK, ++ YAFFS_OBJECT_TYPE_DIRECTORY, ++ YAFFS_OBJECT_TYPE_HARDLINK, ++ YAFFS_OBJECT_TYPE_SPECIAL ++}; ++ ++#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL ++ ++struct yaffs_ext_tags { ++ unsigned chunk_used; /* Status of the chunk: used or unused */ ++ unsigned obj_id; /* If 0 this is not used */ ++ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ ++ unsigned n_bytes; /* Only valid for data chunks */ ++ ++ /* The following stuff only has meaning when we read */ ++ enum yaffs_ecc_result ecc_result; ++ unsigned block_bad; ++ ++ /* YAFFS 1 stuff */ ++ unsigned is_deleted; /* The chunk is marked deleted */ ++ unsigned serial_number; /* Yaffs1 2-bit serial number */ ++ ++ /* YAFFS2 stuff */ ++ unsigned seq_number; /* The sequence number of this block */ ++ ++ /* Extra info if this is an object header (YAFFS2 only) */ ++ ++ unsigned extra_available; /* Extra info available if not zero */ ++ unsigned extra_parent_id; /* The parent object */ ++ unsigned extra_is_shrink; /* Is it a shrink header? */ ++ unsigned extra_shadows; /* Does this shadow another object? */ ++ ++ enum yaffs_obj_type extra_obj_type; /* What object type? */ ++ ++ loff_t extra_file_size; /* Length if it is a file */ ++ unsigned extra_equiv_id; /* Equivalent object for a hard link */ ++}; ++ ++/* Spare structure for YAFFS1 */ ++struct yaffs_spare { ++ u8 tb0; ++ u8 tb1; ++ u8 tb2; ++ u8 tb3; ++ u8 page_status; /* set to 0 to delete the chunk */ ++ u8 block_status; ++ u8 tb4; ++ u8 tb5; ++ u8 ecc1[3]; ++ u8 tb6; ++ u8 tb7; ++ u8 ecc2[3]; ++}; ++ ++/*Special structure for passing through to mtd */ ++struct yaffs_nand_spare { ++ struct yaffs_spare spare; ++ int eccres1; ++ int eccres2; ++}; ++ ++/* Block data in RAM */ ++ ++enum yaffs_block_state { ++ YAFFS_BLOCK_STATE_UNKNOWN = 0, ++ ++ YAFFS_BLOCK_STATE_SCANNING, ++ /* Being scanned */ ++ ++ YAFFS_BLOCK_STATE_NEEDS_SCAN, ++ /* The block might have something on it (ie it is allocating or full, ++ * perhaps empty) but it needs to be scanned to determine its true ++ * state. ++ * This state is only valid during scanning. ++ * NB We tolerate empty because the pre-scanner might be incapable of ++ * deciding ++ * However, if this state is returned on a YAFFS2 device, ++ * then we expect a sequence number ++ */ ++ ++ YAFFS_BLOCK_STATE_EMPTY, ++ /* This block is empty */ ++ ++ YAFFS_BLOCK_STATE_ALLOCATING, ++ /* This block is partially allocated. ++ * At least one page holds valid data. ++ * This is the one currently being used for page ++ * allocation. Should never be more than one of these. ++ * If a block is only partially allocated at mount it is treated as ++ * full. ++ */ ++ ++ YAFFS_BLOCK_STATE_FULL, ++ /* All the pages in this block have been allocated. ++ * If a block was only partially allocated when mounted we treat ++ * it as fully allocated. ++ */ ++ ++ YAFFS_BLOCK_STATE_DIRTY, ++ /* The block was full and now all chunks have been deleted. ++ * Erase me, reuse me. ++ */ ++ ++ YAFFS_BLOCK_STATE_CHECKPOINT, ++ /* This block is assigned to holding checkpoint data. */ ++ ++ YAFFS_BLOCK_STATE_COLLECTING, ++ /* This block is being garbage collected */ ++ ++ YAFFS_BLOCK_STATE_DEAD ++ /* This block has failed and is not in use */ ++}; ++ ++#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) ++ ++struct yaffs_block_info { ++ ++ s32 soft_del_pages:10; /* number of soft deleted pages */ ++ s32 pages_in_use:10; /* number of pages in use */ ++ u32 block_state:4; /* One of the above block states. */ ++ /* NB use unsigned because enum is sometimes ++ * an int */ ++ u32 needs_retiring:1; /* Data has failed on this block, */ ++ /*need to get valid data off and retire*/ ++ u32 skip_erased_check:1;/* Skip the erased check on this block */ ++ u32 gc_prioritise:1; /* An ECC check or blank check has failed. ++ Block should be prioritised for GC */ ++ u32 chunk_error_strikes:3; /* How many times we've had ecc etc ++ failures on this block and tried to reuse it */ ++ u32 has_summary:1; /* The block has a summary */ ++ ++ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ ++ u32 seq_number; /* block sequence number for yaffs2 */ ++ ++}; ++ ++/* -------------------------- Object structure -------------------------------*/ ++/* This is the object structure as stored on NAND */ ++ ++struct yaffs_obj_hdr { ++ enum yaffs_obj_type type; ++ ++ /* Apply to everything */ ++ int parent_obj_id; ++ u16 sum_no_longer_used; /* checksum of name. No longer used */ ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ /* The following apply to all object types except for hard links */ ++ u32 yst_mode; /* protection */ ++ ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++ ++ /* File size applies to files only */ ++ u32 file_size_low; ++ ++ /* Equivalent object id applies to hard links only. */ ++ int equiv_id; ++ ++ /* Alias is for symlinks only. */ ++ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; ++ ++ u32 yst_rdev; /* stuff for block and char devices (major/min) */ ++ ++ u32 win_ctime[2]; ++ u32 win_atime[2]; ++ u32 win_mtime[2]; ++ ++ u32 inband_shadowed_obj_id; ++ u32 inband_is_shrink; ++ ++ u32 file_size_high; ++ u32 reserved[1]; ++ int shadows_obj; /* This object header shadows the ++ specified object if > 0 */ ++ ++ /* is_shrink applies to object headers written when wemake a hole. */ ++ u32 is_shrink; ++ ++}; ++ ++/*--------------------------- Tnode -------------------------- */ ++ ++struct yaffs_tnode { ++ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; ++}; ++ ++/*------------------------ Object -----------------------------*/ ++/* An object can be one of: ++ * - a directory (no data, has children links ++ * - a regular file (data.... not prunes :->). ++ * - a symlink [symbolic link] (the alias). ++ * - a hard link ++ */ ++ ++struct yaffs_file_var { ++ loff_t file_size; ++ loff_t scanned_size; ++ loff_t shrink_size; ++ int top_level; ++ struct yaffs_tnode *top; ++}; ++ ++struct yaffs_dir_var { ++ struct list_head children; /* list of child links */ ++ struct list_head dirty; /* Entry for list of dirty directories */ ++}; ++ ++struct yaffs_symlink_var { ++ YCHAR *alias; ++}; ++ ++struct yaffs_hardlink_var { ++ struct yaffs_obj *equiv_obj; ++ u32 equiv_id; ++}; ++ ++union yaffs_obj_var { ++ struct yaffs_file_var file_variant; ++ struct yaffs_dir_var dir_variant; ++ struct yaffs_symlink_var symlink_variant; ++ struct yaffs_hardlink_var hardlink_variant; ++}; ++ ++struct yaffs_obj { ++ u8 deleted:1; /* This should only apply to unlinked files. */ ++ u8 soft_del:1; /* it has also been soft deleted */ ++ u8 unlinked:1; /* An unlinked file.*/ ++ u8 fake:1; /* A fake object has no presence on NAND. */ ++ u8 rename_allowed:1; /* Some objects cannot be renamed. */ ++ u8 unlink_allowed:1; ++ u8 dirty:1; /* the object needs to be written to flash */ ++ u8 valid:1; /* When the file system is being loaded up, this ++ * object might be created before the data ++ * is available ++ * ie. file data chunks encountered before ++ * the header. ++ */ ++ u8 lazy_loaded:1; /* This object has been lazy loaded and ++ * is missing some detail */ ++ ++ u8 defered_free:1; /* Object is removed from NAND, but is ++ * still in the inode cache. ++ * Free of object is defered. ++ * until the inode is released. ++ */ ++ u8 being_created:1; /* This object is still being created ++ * so skip some verification checks. */ ++ u8 is_shadowed:1; /* This object is shadowed on the way ++ * to being renamed. */ ++ ++ u8 xattr_known:1; /* We know if this has object has xattribs ++ * or not. */ ++ u8 has_xattr:1; /* This object has xattribs. ++ * Only valid if xattr_known. */ ++ ++ u8 serial; /* serial number of chunk in NAND.*/ ++ u16 sum; /* sum of the name to speed searching */ ++ ++ struct yaffs_dev *my_dev; /* The device I'm on */ ++ ++ struct list_head hash_link; /* list of objects in hash bucket */ ++ ++ struct list_head hard_links; /* hard linked object chain*/ ++ ++ /* directory structure stuff */ ++ /* also used for linking up the free list */ ++ struct yaffs_obj *parent; ++ struct list_head siblings; ++ ++ /* Where's my object header in NAND? */ ++ int hdr_chunk; ++ ++ int n_data_chunks; /* Number of data chunks for this file. */ ++ ++ u32 obj_id; /* the object id value */ ++ ++ u32 yst_mode; ++ ++ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; ++ ++#ifdef CONFIG_YAFFS_WINCE ++ u32 win_ctime[2]; ++ u32 win_mtime[2]; ++ u32 win_atime[2]; ++#else ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++#endif ++ ++ u32 yst_rdev; ++ ++ void *my_inode; ++ ++ enum yaffs_obj_type variant_type; ++ ++ union yaffs_obj_var variant; ++ ++}; ++ ++struct yaffs_obj_bucket { ++ struct list_head list; ++ int count; ++}; ++ ++/* yaffs_checkpt_obj holds the definition of an object as dumped ++ * by checkpointing. ++ */ ++ ++struct yaffs_checkpt_obj { ++ int struct_type; ++ u32 obj_id; ++ u32 parent_id; ++ int hdr_chunk; ++ enum yaffs_obj_type variant_type:3; ++ u8 deleted:1; ++ u8 soft_del:1; ++ u8 unlinked:1; ++ u8 fake:1; ++ u8 rename_allowed:1; ++ u8 unlink_allowed:1; ++ u8 serial; ++ int n_data_chunks; ++ loff_t size_or_equiv_obj; ++}; ++ ++/*--------------------- Temporary buffers ---------------- ++ * ++ * These are chunk-sized working buffers. Each device has a few. ++ */ ++ ++struct yaffs_buffer { ++ u8 *buffer; ++ int in_use; ++}; ++ ++/*----------------- Device ---------------------------------*/ ++ ++struct yaffs_param { ++ const YCHAR *name; ++ ++ /* ++ * Entry parameters set up way early. Yaffs sets up the rest. ++ * The structure should be zeroed out before use so that unused ++ * and default values are zero. ++ */ ++ ++ int inband_tags; /* Use unband tags */ ++ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to ++ be a power of 2 */ ++ int chunks_per_block; /* does not need to be a power of 2 */ ++ int spare_bytes_per_chunk; /* spare area size */ ++ int start_block; /* Start block we're allowed to use */ ++ int end_block; /* End block we're allowed to use */ ++ int n_reserved_blocks; /* Tuneable so that we can reduce ++ * reserved blocks on NOR and RAM. */ ++ ++ int n_caches; /* If <= 0, then short op caching is disabled, ++ * else the number of short op caches. ++ */ ++ int cache_bypass_aligned; /* If non-zero then bypass the cache for ++ * aligned writes. ++ */ ++ ++ int use_nand_ecc; /* Flag to decide whether or not to use ++ * NAND driver ECC on data (yaffs1) */ ++ int tags_9bytes; /* Use 9 byte tags */ ++ int no_tags_ecc; /* Flag to decide whether or not to do ECC ++ * on packed tags (yaffs2) */ ++ ++ int is_yaffs2; /* Use yaffs2 mode on this device */ ++ ++ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ ++ ++ int refresh_period; /* How often to check for a block refresh */ ++ ++ /* Checkpoint control. Can be set before or after initialisation */ ++ u8 skip_checkpt_rd; ++ u8 skip_checkpt_wr; ++ ++ int enable_xattr; /* Enable xattribs */ ++ ++ int max_objects; /* ++ * Set to limit the number of objects created. ++ * 0 = no limit. ++ */ ++ ++ /* The remove_obj_fn function must be supplied by OS flavours that ++ * need it. ++ * yaffs direct uses it to implement the faster readdir. ++ * Linux uses it to protect the directory during unlocking. ++ */ ++ void (*remove_obj_fn) (struct yaffs_obj *obj); ++ ++ /* Callback to mark the superblock dirty */ ++ void (*sb_dirty_fn) (struct yaffs_dev *dev); ++ ++ /* Callback to control garbage collection. */ ++ unsigned (*gc_control_fn) (struct yaffs_dev *dev); ++ ++ /* Debug control flags. Don't use unless you know what you're doing */ ++ int use_header_file_size; /* Flag to determine if we should use ++ * file sizes from the header */ ++ int disable_lazy_load; /* Disable lazy loading on this device */ ++ int wide_tnodes_disabled; /* Set to disable wide tnodes */ ++ int disable_soft_del; /* yaffs 1 only: Set to disable the use of ++ * softdeletion. */ ++ ++ int defered_dir_update; /* Set to defer directory updates */ ++ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ int auto_unicode; ++#endif ++ int always_check_erased; /* Force chunk erased check always on */ ++ ++ int disable_summary; ++ int disable_bad_block_marking; ++ ++}; ++ ++struct yaffs_driver { ++ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len); ++ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result); ++ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_initialise_fn) (struct yaffs_dev *dev); ++ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); ++}; ++ ++struct yaffs_tags_handler { ++ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags); ++ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags); ++ ++ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++}; ++ ++struct yaffs_dev { ++ struct yaffs_param param; ++ struct yaffs_driver drv; ++ struct yaffs_tags_handler tagger; ++ ++ /* Context storage. Holds extra OS specific data for this device */ ++ ++ void *os_context; ++ void *driver_context; ++ ++ struct list_head dev_list; ++ ++ int ll_init; ++ /* Runtime parameters. Set up by YAFFS. */ ++ int data_bytes_per_chunk; ++ ++ /* Non-wide tnode stuff */ ++ u16 chunk_grp_bits; /* Number of bits that need to be resolved if ++ * the tnodes are not wide enough. ++ */ ++ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ ++ ++ /* Stuff to support wide tnodes */ ++ u32 tnode_width; ++ u32 tnode_mask; ++ u32 tnode_size; ++ ++ /* Stuff for figuring out file offset to chunk conversions */ ++ u32 chunk_shift; /* Shift value */ ++ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ ++ u32 chunk_mask; /* Mask to use for power-of-2 case */ ++ ++ int is_mounted; ++ int read_only; ++ int is_checkpointed; ++ ++ /* Stuff to support block offsetting to support start block zero */ ++ int internal_start_block; ++ int internal_end_block; ++ int block_offset; ++ int chunk_offset; ++ ++ /* Runtime checkpointing stuff */ ++ int checkpt_page_seq; /* running sequence number of checkpt pages */ ++ int checkpt_byte_count; ++ int checkpt_byte_offs; ++ u8 *checkpt_buffer; ++ int checkpt_open_write; ++ int blocks_in_checkpt; ++ int checkpt_cur_chunk; ++ int checkpt_cur_block; ++ int checkpt_next_block; ++ int *checkpt_block_list; ++ int checkpt_max_blocks; ++ u32 checkpt_sum; ++ u32 checkpt_xor; ++ ++ int checkpoint_blocks_required; /* Number of blocks needed to store ++ * current checkpoint set */ ++ ++ /* Block Info */ ++ struct yaffs_block_info *block_info; ++ u8 *chunk_bits; /* bitmap of chunks in use */ ++ u8 block_info_alt:1; /* allocated using alternative alloc */ ++ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ ++ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. ++ * Must be consistent with chunks_per_block. ++ */ ++ ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int alloc_block_finder; /* Used to search for next allocation block */ ++ ++ /* Object and Tnode memory management */ ++ void *allocator; ++ int n_obj; ++ int n_tnodes; ++ ++ int n_hardlinks; ++ ++ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; ++ u32 bucket_finder; ++ ++ int n_free_chunks; ++ ++ /* Garbage collection control */ ++ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ ++ u32 n_clean_ups; ++ ++ unsigned has_pending_prioritised_gc; /* We think this device might ++ have pending prioritised gcs */ ++ unsigned gc_disable; ++ unsigned gc_block_finder; ++ unsigned gc_dirtiest; ++ unsigned gc_pages_in_use; ++ unsigned gc_not_done; ++ unsigned gc_block; ++ unsigned gc_chunk; ++ unsigned gc_skip; ++ struct yaffs_summary_tags *gc_sum_tags; ++ ++ /* Special directories */ ++ struct yaffs_obj *root_dir; ++ struct yaffs_obj *lost_n_found; ++ ++ int buffered_block; /* Which block is buffered here? */ ++ int doing_buffered_block_rewrite; ++ ++ struct yaffs_cache *cache; ++ int cache_last_use; ++ ++ /* Stuff for background deletion and unlinked files. */ ++ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted ++ files live. */ ++ struct yaffs_obj *del_dir; /* Directory where deleted objects are ++ sent to disappear. */ ++ struct yaffs_obj *unlinked_deletion; /* Current file being ++ background deleted. */ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* Temporary buffer management */ ++ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; ++ int max_temp; ++ int temp_in_use; ++ int unmanaged_buffer_allocs; ++ int unmanaged_buffer_deallocs; ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ allocating block */ ++ unsigned oldest_dirty_seq; ++ unsigned oldest_dirty_block; ++ ++ /* Block refreshing */ ++ int refresh_skip; /* A skip down counter. ++ * Refresh happens when this gets to zero. */ ++ ++ /* Dirty directory handling */ ++ struct list_head dirty_dirs; /* List of dirty directories */ ++ ++ /* Summary */ ++ int chunks_per_summary; ++ struct yaffs_summary_tags *sum_tags; ++ ++ /* Statistics */ ++ u32 n_page_writes; ++ u32 n_page_reads; ++ u32 n_erasures; ++ u32 n_bad_queries; ++ u32 n_bad_markings; ++ u32 n_erase_failures; ++ u32 n_gc_copies; ++ u32 all_gcs; ++ u32 passive_gc_count; ++ u32 oldest_dirty_gc_count; ++ u32 n_gc_blocks; ++ u32 bg_gcs; ++ u32 n_retried_writes; ++ u32 n_retired_blocks; ++ u32 n_ecc_fixed; ++ u32 n_ecc_unfixed; ++ u32 n_tags_ecc_fixed; ++ u32 n_tags_ecc_unfixed; ++ u32 n_deletions; ++ u32 n_unmarked_deletions; ++ u32 refresh_count; ++ u32 cache_hits; ++ u32 tags_used; ++ u32 summary_used; ++ ++}; ++ ++/* The CheckpointDevice structure holds the device information that changes ++ *at runtime and must be preserved over unmount/mount cycles. ++ */ ++struct yaffs_checkpt_dev { ++ int struct_type; ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int n_free_chunks; ++ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ * allocating block */ ++ ++}; ++ ++struct yaffs_checkpt_validity { ++ int struct_type; ++ u32 magic; ++ u32 version; ++ u32 head; ++}; ++ ++struct yaffs_shadow_fixer { ++ int obj_id; ++ int shadowed_id; ++ struct yaffs_shadow_fixer *next; ++}; ++ ++/* Structure for doing xattr modifications */ ++struct yaffs_xattr_mod { ++ int set; /* If 0 then this is a deletion */ ++ const YCHAR *name; ++ const void *data; ++ int size; ++ int flags; ++ int result; ++}; ++ ++/*----------------------- YAFFS Functions -----------------------*/ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev); ++void yaffs_deinitialise(struct yaffs_dev *dev); ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, ++ struct yaffs_obj *new_dir, const YCHAR * new_name); ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); ++int yaffs_del_obj(struct yaffs_obj *obj); ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type); ++ ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj); ++int yaffs_get_obj_inode(struct yaffs_obj *obj); ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj); ++int yaffs_get_obj_link_count(struct yaffs_obj *obj); ++ ++/* File operations */ ++int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, ++ int n_bytes); ++int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid); ++ ++int yaffs_flush_file(struct yaffs_obj *in, ++ int update_time, ++ int data_sync, ++ int discard_cache); ++ ++/* Flushing and checkpointing */ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev, int discard); ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev); ++int yaffs_checkpoint_restore(struct yaffs_dev *dev); ++ ++/* Directory operations */ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid); ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, ++ const YCHAR *name); ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); ++ ++/* Link operations */ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, ++ struct yaffs_obj *equiv_obj); ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); ++ ++/* Symlink operations */ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias); ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); ++ ++/* Special inodes (fifos, sockets and devices) */ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev); ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, ++ const void *value, int size, int flags); ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, ++ int size); ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); ++ ++/* Special directories */ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj); ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev); ++ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); ++ ++/* Debug dump */ ++int yaffs_dump_obj(struct yaffs_obj *obj); ++ ++void yaffs_guts_test(struct yaffs_dev *dev); ++int yaffs_guts_ll_init(struct yaffs_dev *dev); ++ ++ ++/* A few useful functions to be used within the core files*/ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn); ++int yaffs_check_ff(u8 *buffer, int n_bytes); ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type); ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan); ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh); ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); ++YCHAR *yaffs_clone_str(const YCHAR *str); ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, ++ int force, int is_shrink, int shadows, ++ struct yaffs_xattr_mod *xop); ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning); ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn); ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev); ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id); ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos); ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj); ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev); ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out); ++/* ++ * Marshalling functions to get loff_t file sizes into aand out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); ++loff_t yaffs_max_file_size(struct yaffs_dev *dev); ++ ++/* ++ * Debug function to count number of blocks in each state ++ * NB Needs to be called with correct number of integers ++ */ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_linux.h linux-3.18.14/fs/yaffs2/yaffs_linux.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_linux.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,48 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_LINUX_H__ ++#define __YAFFS_LINUX_H__ ++ ++#include "yportenv.h" ++ ++struct yaffs_linux_context { ++ struct list_head context_list; /* List of these we have mounted */ ++ struct yaffs_dev *dev; ++ struct super_block *super; ++ struct task_struct *bg_thread; /* Background thread for this device */ ++ int bg_running; ++ struct mutex gross_lock; /* Gross locking mutex*/ ++ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size ++ * at compile time so we have to allocate it. ++ */ ++ struct list_head search_contexts; ++ struct task_struct *readdir_process; ++ unsigned mount_id; ++ int dirty; ++}; ++ ++#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) ++#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++#define WRITE_SIZE_STR "writesize" ++#define WRITE_SIZE(mtd) ((mtd)->writesize) ++#else ++#define WRITE_SIZE_STR "oobblock" ++#define WRITE_SIZE(mtd) ((mtd)->oobblock) ++#endif ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_mtdif.c linux-3.18.14/fs/yaffs2/yaffs_mtdif.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_mtdif.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,310 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_mtdif.h" ++ ++#include "linux/mtd/mtd.h" ++#include "linux/types.h" ++#include "linux/time.h" ++#include "linux/mtd/nand.h" ++#include "linux/kernel.h" ++#include "linux/version.h" ++#include "linux/types.h" ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) ++#include "uapi/linux/major.h" ++#endif ++ ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_linux.h" ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++#define mtd_erase(m, ei) (m)->erase(m, ei) ++#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) ++#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) ++#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) ++#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) ++#endif ++ ++ ++ ++int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ u32 addr = ++ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ struct erase_info ei; ++ int retval = 0; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++ ++static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", ++ dev, nand_chunk, data, data_len, oob, oob_len); ++ ++ if (!data || !data_len) { ++ data = NULL; ++ data_len = 0; ++ } ++ ++ if (!oob || !oob_len) { ++ oob = NULL; ++ oob_len = 0; ++ } ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = (u8 *)data; ++ ops.oobbuf = (u8 *)oob; ++ ++ retval = mtd_write_oob(mtd, addr, &ops); ++ if (retval) { ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "write_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ } ++ return retval ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = data; ++ ops.oobbuf = oob; ++ ++#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) ++ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; ++ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. ++ */ ++ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; ++#endif ++ /* Read page and oob using MTD. ++ * Check status and determine ECC result. ++ */ ++ retval = mtd_read_oob(mtd, addr, &ops); ++ if (retval) ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "read_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ ++ switch (retval) { ++ case 0: ++ /* no error */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ ++ case -EUCLEAN: ++ /* MTD's ECC fixed the data */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ break; ++ ++ case -EBADMSG: ++ default: ++ /* MTD's ECC could not fix the data */ ++ dev->n_ecc_unfixed++; ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ loff_t addr; ++ struct erase_info ei; ++ int retval = 0; ++ u32 block_size; ++ ++ block_size = dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ addr = ((loff_t) block_no) * block_size; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = block_size; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); ++ ++ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); ++ ++ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_initialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ ++ drv->drv_write_chunk_fn = yaffs_mtd_write; ++ drv->drv_read_chunk_fn = yaffs_mtd_read; ++ drv->drv_erase_fn = yaffs_mtd_erase; ++ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; ++ drv->drv_check_bad_fn = yaffs_mtd_check_bad; ++ drv->drv_initialise_fn = yaffs_mtd_initialise; ++ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; ++} ++ ++ ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev) ++{ ++ struct mtd_info *mtd; ++ ++ mtd = yaffs_get_mtd_device(sdev); ++ ++ /* Check it's an mtd device..... */ ++ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) ++ return NULL; /* This isn't an mtd device */ ++ ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device is not NAND it's type %d", ++ mtd->type); ++ return NULL; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); ++ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); ++ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); ++#else ++ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); ++#endif ++ ++ return mtd; ++} ++ ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) ++{ ++ if (yaffs_version == 2) { ++ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || ++ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && ++ !inband_tags) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not have the right page sizes" ++ ); ++ return -1; ++ } ++ } else { ++ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || ++ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support have the right page sizes" ++ ); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++void yaffs_put_mtd_device(struct mtd_info *mtd) ++{ ++ if(mtd) ++ put_mtd_device(mtd); ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_mtdif.h linux-3.18.14/fs/yaffs2/yaffs_mtdif.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_mtdif.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,25 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_MTDIF_H__ ++#define __YAFFS_MTDIF_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev); ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev); ++void yaffs_put_mtd_device(struct mtd_info *mtd); ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_nameval.c linux-3.18.14/fs/yaffs2/yaffs_nameval.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_nameval.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,208 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This simple implementation of a name-value store assumes a small number of ++* values and fits into a small finite buffer. ++ * ++ * Each attribute is stored as a record: ++ * sizeof(int) bytes record size. ++ * strnlen+1 bytes name null terminated. ++ * nbytes value. ++ * ---------- ++ * total size stored in record size ++ * ++ * This code has not been tested with unicode yet. ++ */ ++ ++#include "yaffs_nameval.h" ++ ++#include "yportenv.h" ++ ++static int nval_find(const char *xb, int xb_size, const YCHAR *name, ++ int *exist_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), ++ name, size)) { ++ if (exist_size) ++ *exist_size = size; ++ return pos; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ if (exist_size) ++ *exist_size = 0; ++ return -ENODATA; ++} ++ ++static int nval_used(const char *xb, int xb_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return pos; ++} ++ ++int nval_del(char *xb, int xb_size, const YCHAR *name) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos < 0 || pos >= xb_size) ++ return -ENODATA; ++ ++ /* Find size, shift rest over this record, ++ * then zero out the rest of buffer */ ++ memcpy(&size, xb + pos, sizeof(int)); ++ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); ++ memset(xb + (xb_size - size), 0, size); ++ return 0; ++} ++ ++int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, ++ int bsize, int flags) ++{ ++ int pos; ++ int namelen = strnlen(name, xb_size); ++ int reclen; ++ int size_exist = 0; ++ int space; ++ int start; ++ ++ pos = nval_find(xb, xb_size, name, &size_exist); ++ ++ if (flags & XATTR_CREATE && pos >= 0) ++ return -EEXIST; ++ if (flags & XATTR_REPLACE && pos < 0) ++ return -ENODATA; ++ ++ start = nval_used(xb, xb_size); ++ space = xb_size - start + size_exist; ++ ++ reclen = (sizeof(int) + namelen + 1 + bsize); ++ ++ if (reclen > space) ++ return -ENOSPC; ++ ++ if (pos >= 0) { ++ nval_del(xb, xb_size, name); ++ start = nval_used(xb, xb_size); ++ } ++ ++ pos = start; ++ ++ memcpy(xb + pos, &reclen, sizeof(int)); ++ pos += sizeof(int); ++ strncpy((YCHAR *) (xb + pos), name, reclen); ++ pos += (namelen + 1); ++ memcpy(xb + pos, buf, bsize); ++ return 0; ++} ++ ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos >= 0 && pos < xb_size) { ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ pos += sizeof(int); /* advance past record length */ ++ size -= sizeof(int); ++ ++ /* Advance over name string */ ++ while (xb[pos] && size > 0 && pos < xb_size) { ++ pos++; ++ size--; ++ } ++ /*Advance over NUL */ ++ pos++; ++ size--; ++ ++ /* If bsize is zero then this is a size query. ++ * Return the size, but don't copy. ++ */ ++ if (!bsize) ++ return size; ++ ++ if (size <= bsize) { ++ memcpy(buf, xb + pos, size); ++ return size; ++ } ++ } ++ if (pos >= 0) ++ return -ERANGE; ++ ++ return -ENODATA; ++} ++ ++int nval_list(const char *xb, int xb_size, char *buf, int bsize) ++{ ++ int pos = 0; ++ int size; ++ int name_len; ++ int ncopied = 0; ++ int filled = 0; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > sizeof(int) && ++ size <= xb_size && ++ (pos + size) < xb_size && ++ !filled) { ++ pos += sizeof(int); ++ size -= sizeof(int); ++ name_len = strnlen((YCHAR *) (xb + pos), size); ++ if (ncopied + name_len + 1 < bsize) { ++ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); ++ buf += name_len; ++ *buf = '\0'; ++ buf++; ++ if (sizeof(YCHAR) > 1) { ++ *buf = '\0'; ++ buf++; ++ } ++ ncopied += (name_len + 1); ++ } else { ++ filled = 1; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return ncopied; ++} ++ ++int nval_hasvalues(const char *xb, int xb_size) ++{ ++ return nval_used(xb, xb_size) > 0; ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_nameval.h linux-3.18.14/fs/yaffs2/yaffs_nameval.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_nameval.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __NAMEVAL_H__ ++#define __NAMEVAL_H__ ++ ++#include "yportenv.h" ++ ++int nval_del(char *xb, int xb_size, const YCHAR * name); ++int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, ++ int bsize, int flags); ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize); ++int nval_list(const char *xb, int xb_size, char *buf, int bsize); ++int nval_hasvalues(const char *xb, int xb_size); ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_nand.c linux-3.18.14/fs/yaffs2/yaffs_nand.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_nand.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,122 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_nand.h" ++#include "yaffs_tagscompat.h" ++ ++#include "yaffs_getblockinfo.h" ++#include "yaffs_summary.h" ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ struct yaffs_ext_tags local_tags; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_reads++; ++ ++ /* If there are no tags provided use local tags. */ ++ if (!tags) ++ tags = &local_tags; ++ ++ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); ++ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { ++ ++ struct yaffs_block_info *bi; ++ bi = yaffs_get_block_info(dev, ++ nand_chunk / ++ dev->param.chunks_per_block); ++ yaffs_handle_chunk_error(dev, bi); ++ } ++ return result; ++} ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_writes++; ++ ++ if (!tags) { ++ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ tags->seq_number = dev->seq_number; ++ tags->chunk_used = 1; ++ yaffs_trace(YAFFS_TRACE_WRITE, ++ "Writing chunk %d tags %d %d", ++ nand_chunk, tags->obj_id, tags->chunk_id); ++ ++ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, ++ buffer, tags); ++ ++ yaffs_summary_add(dev, tags, nand_chunk); ++ ++ return result; ++} ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ block_no -= dev->block_offset; ++ dev->n_bad_markings++; ++ ++ if (dev->param.disable_bad_block_marking) ++ return YAFFS_OK; ++ ++ return dev->tagger.mark_bad_fn(dev, block_no); ++} ++ ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ block_no -= dev->block_offset; ++ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); ++} ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ int result; ++ ++ block_no -= dev->block_offset; ++ dev->n_erasures++; ++ result = dev->drv.drv_erase_fn(dev, block_no); ++ return result; ++} ++ ++int yaffs_init_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_initialise_fn) ++ return dev->drv.drv_initialise_fn(dev); ++ return YAFFS_OK; ++} ++ ++int yaffs_deinit_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_deinitialise_fn) ++ return dev->drv.drv_deinitialise_fn(dev); ++ return YAFFS_OK; ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_nand.h linux-3.18.14/fs/yaffs2/yaffs_nand.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_nand.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_NAND_H__ ++#define __YAFFS_NAND_H__ ++#include "yaffs_guts.h" ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ unsigned *seq_number); ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); ++ ++int yaffs_init_nand(struct yaffs_dev *dev); ++int yaffs_deinit_nand(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.18.14/fs/yaffs2/yaffs_packedtags1.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_packedtags1.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,56 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags1.h" ++#include "yportenv.h" ++ ++static const u8 all_ff[20] = { ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t) ++{ ++ pt->chunk_id = t->chunk_id; ++ pt->serial_number = t->serial_number; ++ pt->n_bytes = t->n_bytes; ++ pt->obj_id = t->obj_id; ++ pt->ecc = 0; ++ pt->deleted = (t->is_deleted) ? 0 : 1; ++ pt->unused_stuff = 0; ++ pt->should_be_ff = 0xffffffff; ++} ++ ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt) ++{ ++ ++ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { ++ t->block_bad = 0; ++ if (pt->should_be_ff != 0xffffffff) ++ t->block_bad = 1; ++ t->chunk_used = 1; ++ t->obj_id = pt->obj_id; ++ t->chunk_id = pt->chunk_id; ++ t->n_bytes = pt->n_bytes; ++ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ t->is_deleted = (pt->deleted) ? 0 : 1; ++ t->serial_number = pt->serial_number; ++ } else { ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ } ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.18.14/fs/yaffs2/yaffs_packedtags1.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_packedtags1.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS1_H__ ++#define __YAFFS_PACKEDTAGS1_H__ ++ ++#include "yaffs_guts.h" ++ ++struct yaffs_packed_tags1 { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 deleted:1; ++ u32 unused_stuff:1; ++ unsigned should_be_ff; ++ ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt); ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.18.14/fs/yaffs2/yaffs_packedtags2.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_packedtags2.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,197 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags2.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++/* This code packs a set of extended tags into a binary structure for ++ * NAND storage ++ */ ++ ++/* Some of the information is "extra" struff which can be packed in to ++ * speed scanning ++ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. ++ */ ++ ++/* Extra flags applied to chunk_id */ ++ ++#define EXTRA_HEADER_INFO_FLAG 0x80000000 ++#define EXTRA_SHRINK_FLAG 0x40000000 ++#define EXTRA_SHADOWS_FLAG 0x20000000 ++#define EXTRA_SPARE_FLAGS 0x10000000 ++ ++#define ALL_EXTRA_FLAGS 0xf0000000 ++ ++/* Also, the top 4 bits of the object Id are set to the object type. */ ++#define EXTRA_OBJECT_TYPE_SHIFT (28) ++#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) ++ ++static void yaffs_dump_packed_tags2_tags_only( ++ const struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "packed tags obj %d chunk %d byte %d seq %d", ++ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); ++} ++ ++static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) ++{ ++ yaffs_dump_packed_tags2_tags_only(&pt->t); ++} ++ ++static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", ++ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, ++ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, ++ t->seq_number); ++ ++} ++ ++static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) ++{ ++ if (t->chunk_id != 0 || !t->extra_available) ++ return 0; ++ ++ /* Check if the file size is too long to store */ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && ++ (t->extra_file_size >> 31) != 0) ++ return 0; ++ return 1; ++} ++ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, ++ const struct yaffs_ext_tags *t) ++{ ++ ptt->chunk_id = t->chunk_id; ++ ptt->seq_number = t->seq_number; ++ ptt->n_bytes = t->n_bytes; ++ ptt->obj_id = t->obj_id; ++ ++ /* Only store extra tags for object headers. ++ * If it is a file then only store if the file size is short\ ++ * enough to fit. ++ */ ++ if (yaffs_check_tags_extra_packable(t)) { ++ /* Store the extra header info instead */ ++ /* We save the parent object in the chunk_id */ ++ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; ++ if (t->extra_is_shrink) ++ ptt->chunk_id |= EXTRA_SHRINK_FLAG; ++ if (t->extra_shadows) ++ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; ++ ++ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ ptt->n_bytes = t->extra_equiv_id; ++ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ ptt->n_bytes = (unsigned) t->extra_file_size; ++ else ++ ptt->n_bytes = 0; ++ } ++ ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc) ++{ ++ yaffs_pack_tags2_tags_only(&pt->t, t); ++ ++ if (tags_ecc) ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc); ++} ++ ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ ++ if (ptt->seq_number == 0xffffffff) ++ return; ++ ++ t->block_bad = 0; ++ t->chunk_used = 1; ++ t->obj_id = ptt->obj_id; ++ t->chunk_id = ptt->chunk_id; ++ t->n_bytes = ptt->n_bytes; ++ t->is_deleted = 0; ++ t->serial_number = 0; ++ t->seq_number = ptt->seq_number; ++ ++ /* Do extra header info stuff */ ++ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { ++ t->chunk_id = 0; ++ t->n_bytes = 0; ++ ++ t->extra_available = 1; ++ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); ++ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; ++ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; ++ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; ++ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ t->extra_equiv_id = ptt->n_bytes; ++ else ++ t->extra_file_size = ptt->n_bytes; ++ } ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc) ++{ ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ if (pt->t.seq_number != 0xffffffff && tags_ecc) { ++ /* Chunk is in use and we need to do ECC */ ++ ++ struct yaffs_ecc_other ecc; ++ int result; ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &ecc); ++ result = ++ yaffs_ecc_correct_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc, &ecc); ++ switch (result) { ++ case 0: ++ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ case 1: ++ ecc_result = YAFFS_ECC_RESULT_FIXED; ++ break; ++ case -1: ++ ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ break; ++ default: ++ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ } ++ } ++ yaffs_unpack_tags2_tags_only(t, &pt->t); ++ ++ t->ecc_result = ecc_result; ++ ++ yaffs_dump_packed_tags2(pt); ++ yaffs_dump_tags2(t); ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.18.14/fs/yaffs2/yaffs_packedtags2.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_packedtags2.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,47 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS2_H__ ++#define __YAFFS_PACKEDTAGS2_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_ecc.h" ++ ++struct yaffs_packed_tags2_tags_only { ++ unsigned seq_number; ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++struct yaffs_packed_tags2 { ++ struct yaffs_packed_tags2_tags_only t; ++ struct yaffs_ecc_other ecc; ++}; ++ ++/* Full packed tags with ECC, used for oob tags */ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc); ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc); ++ ++/* Only the tags part (no ECC for use with inband tags */ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *pt); ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_summary.c linux-3.18.14/fs/yaffs2/yaffs_summary.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_summary.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,312 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Summaries write the useful part of the tags for the chunks in a block into an ++ * an array which is written to the last n chunks of the block. ++ * Reading the summaries gives all the tags for the block in one read. Much ++ * faster. ++ * ++ * Chunks holding summaries are marked with tags making it look like ++ * they are part of a fake file. ++ * ++ * The summary could also be used during gc. ++ * ++ */ ++ ++#include "yaffs_summary.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_bitmap.h" ++ ++/* ++ * The summary is built up in an array of summary tags. ++ * This gets written to the last one or two (maybe more) chunks in a block. ++ * A summary header is written as the first part of each chunk of summary data. ++ * The summary header must match or the summary is rejected. ++ */ ++ ++/* Summary tags don't need the sequence number because that is redundant. */ ++struct yaffs_summary_tags { ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++/* Summary header */ ++struct yaffs_summary_header { ++ unsigned version; /* Must match current version */ ++ unsigned block; /* Must be this block */ ++ unsigned seq; /* Must be this sequence number */ ++ unsigned sum; /* Just add up all the bytes in the tags */ ++}; ++ ++ ++static void yaffs_summary_clear(struct yaffs_dev *dev) ++{ ++ if (!dev->sum_tags) ++ return; ++ memset(dev->sum_tags, 0, dev->chunks_per_summary * ++ sizeof(struct yaffs_summary_tags)); ++} ++ ++ ++void yaffs_summary_deinit(struct yaffs_dev *dev) ++{ ++ kfree(dev->sum_tags); ++ dev->sum_tags = NULL; ++ kfree(dev->gc_sum_tags); ++ dev->gc_sum_tags = NULL; ++ dev->chunks_per_summary = 0; ++} ++ ++int yaffs_summary_init(struct yaffs_dev *dev) ++{ ++ int sum_bytes; ++ int chunks_used; /* Number of chunks used by summary */ ++ int sum_tags_bytes; ++ ++ sum_bytes = dev->param.chunks_per_block * ++ sizeof(struct yaffs_summary_tags); ++ ++ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ ++ (dev->data_bytes_per_chunk - ++ sizeof(struct yaffs_summary_header)); ++ ++ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ if (!dev->sum_tags || !dev->gc_sum_tags) { ++ yaffs_summary_deinit(dev); ++ return YAFFS_FAIL; ++ } ++ ++ yaffs_summary_clear(dev); ++ ++ return YAFFS_OK; ++} ++ ++static unsigned yaffs_summary_sum(struct yaffs_dev *dev) ++{ ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int i; ++ unsigned sum = 0; ++ ++ i = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ while (i > 0) { ++ sum += *sum_buffer; ++ sum_buffer++; ++ i--; ++ } ++ ++ return sum; ++} ++ ++static int yaffs_summary_write(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int n_bytes; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); ++ tags.obj_id = YAFFS_OBJECTID_SUMMARY; ++ tags.chunk_id = 1; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ hdr.version = YAFFS_SUMMARY_VERSION; ++ hdr.block = blk; ++ hdr.seq = bi->seq_number; ++ hdr.sum = yaffs_summary_sum(dev); ++ ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ memcpy(buffer, &hdr, sizeof(hdr)); ++ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); ++ tags.n_bytes = this_tx + sizeof(hdr); ++ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ break; ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ dev->n_free_chunks--; ++ ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ tags.chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ ++ if (result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ ++ return result; ++} ++ ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)st; ++ int n_bytes; ++ int chunk_id; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ int sum_tags_bytes; ++ ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = blk * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ chunk_id = 1; ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (tags.chunk_id != chunk_id || ++ tags.obj_id != YAFFS_OBJECTID_SUMMARY || ++ tags.chunk_used == 0 || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.n_bytes != (this_tx + sizeof(hdr))) ++ result = YAFFS_FAIL; ++ if (result != YAFFS_OK) ++ break; ++ ++ if (st == dev->sum_tags) { ++ /* If we're scanning then update the block info */ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ } ++ memcpy(&hdr, buffer, sizeof(hdr)); ++ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (result == YAFFS_OK) { ++ /* Verify header */ ++ if (hdr.version != YAFFS_SUMMARY_VERSION || ++ hdr.seq != bi->seq_number || ++ hdr.sum != yaffs_summary_sum(dev)) ++ result = YAFFS_FAIL; ++ } ++ ++ if (st == dev->sum_tags && result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ return result; ++} ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_nand) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; ++ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; ++ ++ if (!dev->sum_tags) ++ return YAFFS_OK; ++ ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ yaffs_pack_tags2_tags_only(&tags_only, tags); ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ sum_tags->chunk_id = tags_only.chunk_id; ++ sum_tags->n_bytes = tags_only.n_bytes; ++ sum_tags->obj_id = tags_only.obj_id; ++ ++ if (chunk_in_block == dev->chunks_per_summary - 1) { ++ /* Time to write out the summary */ ++ yaffs_summary_write(dev, block_in_nand); ++ yaffs_summary_clear(dev); ++ yaffs_skip_rest_of_block(dev); ++ } ++ } ++ return YAFFS_OK; ++} ++ ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ tags_only.chunk_id = sum_tags->chunk_id; ++ tags_only.n_bytes = sum_tags->n_bytes; ++ tags_only.obj_id = sum_tags->obj_id; ++ yaffs_unpack_tags2_tags_only(tags, &tags_only); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int i; ++ ++ if (!bi->has_summary) ++ return; ++ ++ for (i = dev->chunks_per_summary; ++ i < dev->param.chunks_per_block; ++ i++) { ++ if (yaffs_check_chunk_bit(dev, blk, i)) { ++ yaffs_clear_chunk_bit(dev, blk, i); ++ bi->pages_in_use--; ++ dev->n_free_chunks++; ++ } ++ } ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_summary.h linux-3.18.14/fs/yaffs2/yaffs_summary.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_summary.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,37 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_SUMMARY_H__ ++#define __YAFFS_SUMMARY_H__ ++ ++#include "yaffs_packedtags2.h" ++ ++ ++int yaffs_summary_init(struct yaffs_dev *dev); ++void yaffs_summary_deinit(struct yaffs_dev *dev); ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk); ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk); ++ ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.18.14/fs/yaffs2/yaffs_tagscompat.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_tagscompat.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,381 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_ecc.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_trace.h" ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); ++ ++ ++/********** Tags ECC calculations *********/ ++ ++ ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags) ++{ ++ /* Calculate an ecc */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ unsigned i, j; ++ unsigned ecc = 0; ++ unsigned bit = 0; ++ ++ tags->ecc = 0; ++ ++ for (i = 0; i < 8; i++) { ++ for (j = 1; j & 0xff; j <<= 1) { ++ bit++; ++ if (b[i] & j) ++ ecc ^= bit; ++ } ++ } ++ tags->ecc = ecc; ++} ++ ++int yaffs_check_tags_ecc(struct yaffs_tags *tags) ++{ ++ unsigned ecc = tags->ecc; ++ ++ yaffs_calc_tags_ecc(tags); ++ ++ ecc ^= tags->ecc; ++ ++ if (ecc && ecc <= 64) { ++ /* TODO: Handle the failure better. Retire? */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ ++ ecc--; ++ ++ b[ecc / 8] ^= (1 << (ecc & 7)); ++ ++ /* Now recvalc the ecc */ ++ yaffs_calc_tags_ecc(tags); ++ ++ return 1; /* recovered error */ ++ } else if (ecc) { ++ /* Wierd ecc failure value */ ++ /* TODO Need to do somethiong here */ ++ return -1; /* unrecovered error */ ++ } ++ return 0; ++} ++ ++/********** Tags **********/ ++ ++static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ ++ yaffs_calc_tags_ecc(tags_ptr); ++ ++ spare_ptr->tb0 = tu->as_bytes[0]; ++ spare_ptr->tb1 = tu->as_bytes[1]; ++ spare_ptr->tb2 = tu->as_bytes[2]; ++ spare_ptr->tb3 = tu->as_bytes[3]; ++ spare_ptr->tb4 = tu->as_bytes[4]; ++ spare_ptr->tb5 = tu->as_bytes[5]; ++ spare_ptr->tb6 = tu->as_bytes[6]; ++ spare_ptr->tb7 = tu->as_bytes[7]; ++} ++ ++static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, ++ struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ int result; ++ ++ tu->as_bytes[0] = spare_ptr->tb0; ++ tu->as_bytes[1] = spare_ptr->tb1; ++ tu->as_bytes[2] = spare_ptr->tb2; ++ tu->as_bytes[3] = spare_ptr->tb3; ++ tu->as_bytes[4] = spare_ptr->tb4; ++ tu->as_bytes[5] = spare_ptr->tb5; ++ tu->as_bytes[6] = spare_ptr->tb6; ++ tu->as_bytes[7] = spare_ptr->tb7; ++ ++ result = yaffs_check_tags_ecc(tags_ptr); ++ if (result > 0) ++ dev->n_tags_ecc_fixed++; ++ else if (result < 0) ++ dev->n_tags_ecc_unfixed++; ++} ++ ++static void yaffs_spare_init(struct yaffs_spare *spare) ++{ ++ memset(spare, 0xff, sizeof(struct yaffs_spare)); ++} ++ ++static int yaffs_wr_nand(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ struct yaffs_spare *spare) ++{ ++ int data_size = dev->data_bytes_per_chunk; ++ ++ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, sizeof(*spare)); ++} ++ ++static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, ++ struct yaffs_spare *spare, ++ enum yaffs_ecc_result *ecc_result, ++ int correct_errors) ++{ ++ int ret_val; ++ struct yaffs_spare local_spare; ++ int data_size; ++ int spare_size; ++ int ecc_result1, ecc_result2; ++ u8 calc_ecc[3]; ++ ++ if (!spare) { ++ /* If we don't have a real spare, then we use a local one. */ ++ /* Need this for the calculation of the ecc */ ++ spare = &local_spare; ++ } ++ data_size = dev->data_bytes_per_chunk; ++ spare_size = sizeof(struct yaffs_spare); ++ ++ if (dev->param.use_nand_ecc) ++ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, spare_size, ++ ecc_result); ++ ++ ++ /* Handle the ECC at this level. */ ++ ++ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *)spare, spare_size, ++ NULL); ++ if (!data || !correct_errors) ++ return ret_val; ++ ++ /* Do ECC correction if needed. */ ++ yaffs_ecc_calc(data, calc_ecc); ++ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); ++ yaffs_ecc_calc(&data[256], calc_ecc); ++ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); ++ ++ if (ecc_result1 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result1 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result2 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result2 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result1 || ecc_result2) { ++ /* We had a data problem on this page */ ++ yaffs_handle_rd_data_error(dev, nand_chunk); ++ } ++ ++ if (ecc_result1 < 0 || ecc_result2 < 0) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ else if (ecc_result1 > 0 || ecc_result2 > 0) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ else ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ return ret_val; ++} ++ ++/* ++ * Functions for robustisizing ++ */ ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ ++ /* Mark the block for retirement */ ++ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> ++ needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>>Block %d marked for retirement", ++ flash_block); ++ ++ /* TODO: ++ * Just do a garbage collection on the affected block ++ * then retire the block ++ * NB recursion ++ */ ++} ++ ++static int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ ++ yaffs_spare_init(&spare); ++ ++ if (ext_tags->is_deleted) ++ spare.page_status = 0; ++ else { ++ tags.obj_id = ext_tags->obj_id; ++ tags.chunk_id = ext_tags->chunk_id; ++ ++ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; ++ else ++ tags.n_bytes_msb = 3; ++ ++ tags.serial_number = ext_tags->serial_number; ++ ++ if (!dev->param.use_nand_ecc && data) { ++ yaffs_ecc_calc(data, spare.ecc1); ++ yaffs_ecc_calc(&data[256], spare.ecc2); ++ } ++ ++ yaffs_load_tags_to_spare(&spare, &tags); ++ } ++ return yaffs_wr_nand(dev, nand_chunk, data, &spare); ++} ++ ++static int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ int deleted; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ if (!yaffs_rd_chunk_nand(dev, nand_chunk, ++ data, &spare, &ecc_result, 1)) ++ return YAFFS_FAIL; ++ ++ /* ext_tags may be NULL */ ++ if (!ext_tags) ++ return YAFFS_OK; ++ ++ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; ++ ++ ext_tags->is_deleted = deleted; ++ ext_tags->ecc_result = ecc_result; ++ ext_tags->block_bad = 0; /* We're reading it */ ++ /* therefore it is not a bad block */ ++ ext_tags->chunk_used = ++ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; ++ ++ if (ext_tags->chunk_used) { ++ yaffs_get_tags_from_spare(dev, &spare, &tags); ++ ext_tags->obj_id = tags.obj_id; ++ ext_tags->chunk_id = tags.chunk_id; ++ ext_tags->n_bytes = tags.n_bytes_lsb; ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ ext_tags->n_bytes |= ++ (((unsigned)tags.n_bytes_msb) << 10); ++ ++ ext_tags->serial_number = tags.serial_number; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_spare spare; ++ ++ memset(&spare, 0xff, sizeof(struct yaffs_spare)); ++ ++ spare.block_status = 'Y'; ++ ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, ++ &spare); ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, ++ NULL, &spare); ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ struct yaffs_spare spare0, spare1; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ enum yaffs_ecc_result dummy; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ *seq_number = 0; ++ ++ /* Look for bad block markers in the first two chunks */ ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, ++ NULL, &spare0, &dummy, 0); ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, ++ NULL, &spare1, &dummy, 0); ++ ++ if (hweight8(spare0.block_status & spare1.block_status) < 7) ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ else ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ ++ return YAFFS_OK; ++} ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev) ++{ ++ if(dev->param.is_yaffs2) ++ return; ++ if(!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; ++ if(!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; ++ if(!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; ++ if(!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.18.14/fs/yaffs2/yaffs_tagscompat.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_tagscompat.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSCOMPAT_H__ ++#define __YAFFS_TAGSCOMPAT_H__ ++ ++ ++#include "yaffs_guts.h" ++ ++#if 0 ++ ++ ++int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); ++int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ ++#endif ++ ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev); ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags); ++int yaffs_check_tags_ecc(struct yaffs_tags *tags); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.18.14/fs/yaffs2/yaffs_tagsmarshall.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_tagsmarshall.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,199 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_packedtags2.h" ++ ++static int yaffs_tags_marshall_write(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ struct yaffs_packed_tags2 pt; ++ int retval; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_write chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ /* For yaffs2 writing there must be both data and tags. ++ * If we're using inband tags, then the tags are stuffed into ++ * the end of the data buffer. ++ */ ++ if (!data || !tags) ++ BUG(); ++ else if (dev->param.inband_tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *)(data + ++ dev-> ++ data_bytes_per_chunk); ++ yaffs_pack_tags2_tags_only(pt2tp, tags); ++ } else { ++ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); ++ } ++ ++ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ (dev->param.inband_tags) ? NULL : packed_tags_ptr, ++ (dev->param.inband_tags) ? 0 : packed_tags_size); ++ ++ return retval; ++} ++ ++static int yaffs_tags_marshall_read(struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = 0; ++ int local_data = 0; ++ u8 spare_buffer[100]; ++ enum yaffs_ecc_result ecc_result; ++ ++ struct yaffs_packed_tags2 pt; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_read chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ if (dev->param.inband_tags) { ++ if (!data) { ++ local_data = 1; ++ data = yaffs_get_temp_buffer(dev); ++ } ++ } ++ ++ if (dev->param.inband_tags || (data && !tags)) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ NULL, 0, ++ &ecc_result); ++ else if (tags) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ spare_buffer, packed_tags_size, ++ &ecc_result); ++ else ++ BUG(); ++ ++ ++ if (dev->param.inband_tags) { ++ if (tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *) ++ &data[dev->data_bytes_per_chunk]; ++ yaffs_unpack_tags2_tags_only(tags, pt2tp); ++ } ++ } else if (tags) { ++ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); ++ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); ++ } ++ ++ if (local_data) ++ yaffs_release_temp_buffer(dev, data); ++ ++ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { ++ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { ++ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) ++ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ } ++ ++ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", ++ block_no); ++ ++ retval = dev->drv.drv_check_bad_fn(dev, block_no); ++ ++ if (retval== YAFFS_FAIL) { ++ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); ++ ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ *seq_number = 0; ++ } else { ++ struct yaffs_ext_tags t; ++ ++ yaffs_tags_marshall_read(dev, ++ block_no * dev->param.chunks_per_block, ++ NULL, &t); ++ ++ if (t.chunk_used) { ++ *seq_number = t.seq_number; ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ } else { ++ *seq_number = 0; ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "block query returns seq %d state %d", ++ *seq_number, *state); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ return dev->drv.drv_mark_bad_fn(dev, block_no); ++ ++} ++ ++ ++void yaffs_tags_marshall_install(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; ++ ++ if (!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; ++ ++ if (!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; ++ ++ if (!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; ++ ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.18.14/fs/yaffs2/yaffs_tagsmarshall.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_tagsmarshall.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSMARSHALL_H__ ++#define __YAFFS_TAGSMARSHALL_H__ ++ ++#include "yaffs_guts.h" ++void yaffs_tags_marshall_install(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_trace.h linux-3.18.14/fs/yaffs2/yaffs_trace.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_trace.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,57 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YTRACE_H__ ++#define __YTRACE_H__ ++ ++extern unsigned int yaffs_trace_mask; ++extern unsigned int yaffs_wr_attempts; ++ ++/* ++ * Tracing flags. ++ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. ++ */ ++ ++#define YAFFS_TRACE_OS 0x00000002 ++#define YAFFS_TRACE_ALLOCATE 0x00000004 ++#define YAFFS_TRACE_SCAN 0x00000008 ++#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 ++#define YAFFS_TRACE_ERASE 0x00000020 ++#define YAFFS_TRACE_GC 0x00000040 ++#define YAFFS_TRACE_WRITE 0x00000080 ++#define YAFFS_TRACE_TRACING 0x00000100 ++#define YAFFS_TRACE_DELETION 0x00000200 ++#define YAFFS_TRACE_BUFFERS 0x00000400 ++#define YAFFS_TRACE_NANDACCESS 0x00000800 ++#define YAFFS_TRACE_GC_DETAIL 0x00001000 ++#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 ++#define YAFFS_TRACE_MTD 0x00004000 ++#define YAFFS_TRACE_CHECKPOINT 0x00008000 ++ ++#define YAFFS_TRACE_VERIFY 0x00010000 ++#define YAFFS_TRACE_VERIFY_NAND 0x00020000 ++#define YAFFS_TRACE_VERIFY_FULL 0x00040000 ++#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 ++ ++#define YAFFS_TRACE_SYNC 0x00100000 ++#define YAFFS_TRACE_BACKGROUND 0x00200000 ++#define YAFFS_TRACE_LOCK 0x00400000 ++#define YAFFS_TRACE_MOUNT 0x00800000 ++ ++#define YAFFS_TRACE_ERROR 0x40000000 ++#define YAFFS_TRACE_BUG 0x80000000 ++#define YAFFS_TRACE_ALWAYS 0xf0000000 ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_verify.c linux-3.18.14/fs/yaffs2/yaffs_verify.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_verify.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,529 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_verify.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++ ++int yaffs_skip_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & ++ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_full_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_nand_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); ++} ++ ++static const char * const block_state_name[] = { ++ "Unknown", ++ "Needs scan", ++ "Scanning", ++ "Empty", ++ "Allocating", ++ "Full", ++ "Dirty", ++ "Checkpoint", ++ "Collecting", ++ "Dead" ++}; ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) ++{ ++ int actually_used; ++ int in_use; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Report illegal runtime states */ ++ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has undefined state %d", ++ n, bi->block_state); ++ ++ switch (bi->block_state) { ++ case YAFFS_BLOCK_STATE_UNKNOWN: ++ case YAFFS_BLOCK_STATE_SCANNING: ++ case YAFFS_BLOCK_STATE_NEEDS_SCAN: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has bad run-state %s", ++ n, block_state_name[bi->block_state]); ++ } ++ ++ /* Check pages in use and soft deletions are legal */ ++ ++ actually_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->pages_in_use < 0 || ++ bi->pages_in_use > dev->param.chunks_per_block || ++ bi->soft_del_pages < 0 || ++ bi->soft_del_pages > dev->param.chunks_per_block || ++ actually_used < 0 || actually_used > dev->param.chunks_per_block) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has illegal values pages_in_used %d soft_del_pages %d", ++ n, bi->pages_in_use, bi->soft_del_pages); ++ ++ /* Check chunk bitmap legal */ ++ in_use = yaffs_count_chunk_bits(dev, n); ++ if (in_use != bi->pages_in_use) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", ++ n, bi->pages_in_use, in_use); ++} ++ ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n) ++{ ++ yaffs_verify_blk(dev, bi, n); ++ ++ /* After collection the block should be in the erased state */ ++ ++ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && ++ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Block %d is in state %d after gc, should be erased", ++ n, bi->block_state); ++ } ++} ++ ++void yaffs_verify_blocks(struct yaffs_dev *dev) ++{ ++ int i; ++ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; ++ int illegal_states = 0; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ memset(state_count, 0, sizeof(state_count)); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ yaffs_verify_blk(dev, bi, i); ++ ++ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) ++ state_count[bi->block_state]++; ++ else ++ illegal_states++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%d blocks have illegal states", ++ illegal_states); ++ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many allocating blocks"); ++ ++ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%s %d blocks", ++ block_state_name[i], state_count[i]); ++ ++ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Checkpoint block count wrong dev %d count %d", ++ dev->blocks_in_checkpt, ++ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); ++ ++ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Erased block count wrong dev %d count %d", ++ dev->n_erased_blocks, ++ state_count[YAFFS_BLOCK_STATE_EMPTY]); ++ ++ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many collecting blocks %d (max is 1)", ++ state_count[YAFFS_BLOCK_STATE_COLLECTING]); ++} ++ ++/* ++ * Verify the object header. oh must be valid, but obj and tags may be NULL in ++ * which case those tests will not be performed. ++ */ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!(tags && obj && oh)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Verifying object header tags %p obj %p oh %p", ++ tags, obj, oh); ++ return; ++ } ++ ++ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || ++ oh->type > YAFFS_OBJECT_TYPE_MAX) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header type is illegal value 0x%x", ++ tags->obj_id, oh->type); ++ ++ if (tags->obj_id != obj->obj_id) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch obj_id %d", ++ tags->obj_id, obj->obj_id); ++ ++ /* ++ * Check that the object's parent ids match if parent_check requested. ++ * ++ * Tests do not apply to the root object. ++ */ ++ ++ if (parent_check && tags->obj_id > 1 && !obj->parent) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d obj->parent is NULL", ++ tags->obj_id, oh->parent_obj_id); ++ ++ if (parent_check && obj->parent && ++ oh->parent_obj_id != obj->parent->obj_id && ++ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d parent_obj_id %d", ++ tags->obj_id, oh->parent_obj_id, ++ obj->parent->obj_id); ++ ++ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is NULL", ++ obj->obj_id); ++ ++ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is 0xff", ++ obj->obj_id); ++} ++ ++void yaffs_verify_file(struct yaffs_obj *obj) ++{ ++ u32 x; ++ int required_depth; ++ int actual_depth; ++ int last_chunk; ++ u32 offset_in_chunk; ++ u32 the_chunk; ++ ++ u32 i; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ struct yaffs_tnode *tn; ++ u32 obj_id; ++ ++ if (!obj) ++ return; ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ dev = obj->my_dev; ++ obj_id = obj->obj_id; ++ ++ ++ /* Check file size is consistent with tnode depth */ ++ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, ++ &last_chunk, &offset_in_chunk); ++ last_chunk++; ++ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x > 0) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ actual_depth = obj->variant.file_variant.top_level; ++ ++ /* Check that the chunks in the tnode tree are all correct. ++ * We do this by scanning through the tnode tree and ++ * checking the tags for every chunk match. ++ */ ++ ++ if (yaffs_skip_nand_verification(dev)) ++ return; ++ ++ for (i = 1; i <= last_chunk; i++) { ++ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); ++ ++ if (!tn) ++ continue; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk > 0) { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ &tags); ++ if (tags.obj_id != obj_id || tags.chunk_id != i) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", ++ obj_id, i, the_chunk, ++ tags.obj_id, tags.chunk_id); ++ } ++ } ++} ++ ++void yaffs_verify_link(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify sane equivalent object */ ++} ++ ++void yaffs_verify_symlink(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify symlink string */ ++} ++ ++void yaffs_verify_special(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++} ++ ++void yaffs_verify_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ u32 chunk_min; ++ u32 chunk_max; ++ u32 chunk_id_ok; ++ u32 chunk_in_range; ++ u32 chunk_wrongly_deleted; ++ u32 chunk_valid; ++ ++ if (!obj) ++ return; ++ ++ if (obj->being_created) ++ return; ++ ++ dev = obj->my_dev; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Check sane object header chunk */ ++ ++ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; ++ chunk_max = ++ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; ++ ++ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && ++ ((unsigned)(obj->hdr_chunk)) <= chunk_max); ++ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); ++ chunk_valid = chunk_in_range && ++ yaffs_check_chunk_bit(dev, ++ obj->hdr_chunk / dev->param.chunks_per_block, ++ obj->hdr_chunk % dev->param.chunks_per_block); ++ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; ++ ++ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has chunk_id %d %s %s", ++ obj->obj_id, obj->hdr_chunk, ++ chunk_id_ok ? "" : ",out of range", ++ chunk_wrongly_deleted ? ",marked as deleted" : ""); ++ ++ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj_hdr *oh; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); ++ ++ yaffs_verify_oh(obj, oh, &tags, 1); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ /* Verify it has a parent */ ++ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has parent pointer %p which does not look like an object", ++ obj->obj_id, obj->parent); ++ } ++ ++ /* Verify parent is a directory */ ++ if (obj->parent && ++ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d's parent is not a directory (type %d)", ++ obj->obj_id, obj->parent->variant_type); ++ } ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ yaffs_verify_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ yaffs_verify_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ yaffs_verify_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ yaffs_verify_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ yaffs_verify_special(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has illegaltype %d", ++ obj->obj_id, obj->variant_type); ++ break; ++ } ++} ++ ++void yaffs_verify_objects(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int i; ++ struct list_head *lh; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ yaffs_verify_obj(obj); ++ } ++ } ++} ++ ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ int count = 0; ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!obj->parent) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); ++ BUG(); ++ return; ++ } ++ ++ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &obj->parent->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_verify_obj(list_obj); ++ if (obj == list_obj) ++ count++; ++ } ++ ++ if (count != 1) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory %d times", ++ count); ++ BUG(); ++ } ++} ++ ++void yaffs_verify_dir(struct yaffs_obj *directory) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ ++ if (!directory) { ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_full_verification(directory->my_dev)) ++ return; ++ ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Directory has wrong type: %d", ++ directory->variant_type); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &directory->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (list_obj->parent != directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory list has wrong parent %p", ++ list_obj->parent); ++ BUG(); ++ } ++ yaffs_verify_obj_in_dir(list_obj); ++ } ++} ++ ++static int yaffs_free_verification_failures; ++ ++void yaffs_verify_free_chunks(struct yaffs_dev *dev) ++{ ++ int counted; ++ int difference; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ counted = yaffs_count_free_chunks(dev); ++ ++ difference = dev->n_free_chunks - counted; ++ ++ if (difference) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Freechunks verification failure %d %d %d", ++ dev->n_free_chunks, counted, difference); ++ yaffs_free_verification_failures++; ++ } ++} ++ ++int yaffs_verify_file_sane(struct yaffs_obj *in) ++{ ++ (void) in; ++ return YAFFS_OK; ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_verify.h linux-3.18.14/fs/yaffs2/yaffs_verify.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_verify.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,43 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_VERIFY_H__ ++#define __YAFFS_VERIFY_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, ++ int n); ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n); ++void yaffs_verify_blocks(struct yaffs_dev *dev); ++ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check); ++void yaffs_verify_file(struct yaffs_obj *obj); ++void yaffs_verify_link(struct yaffs_obj *obj); ++void yaffs_verify_symlink(struct yaffs_obj *obj); ++void yaffs_verify_special(struct yaffs_obj *obj); ++void yaffs_verify_obj(struct yaffs_obj *obj); ++void yaffs_verify_objects(struct yaffs_dev *dev); ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); ++void yaffs_verify_dir(struct yaffs_obj *directory); ++void yaffs_verify_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_verify_file_sane(struct yaffs_obj *obj); ++ ++int yaffs_skip_verification(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_vfs.c linux-3.18.14/fs/yaffs2/yaffs_vfs.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_vfs.c 2015-06-15 18:42:09.000000000 +0200 +@@ -0,0 +1,3658 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * Acknowledgements: ++ * Luc van OostenRyck for numerous patches. ++ * Nick Bane for numerous patches. ++ * Nick Bane for 2.5/2.6 integration. ++ * Andras Toth for mknod rdev issue. ++ * Michael Fischer for finding the problem with inode inconsistency. ++ * Some code bodily lifted from JFFS ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * ++ * This is the file system front-end to YAFFS that hooks it up to ++ * the VFS. ++ * ++ * Special notes: ++ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with ++ * this superblock ++ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this ++ * superblock ++ * >> inode->u.generic_ip points to the associated struct yaffs_obj. ++ */ ++ ++/* ++ * There are two variants of the VFS glue code. This variant should compile ++ * for any version of Linux. ++ */ ++#include ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) ++#define YAFFS_COMPILE_BACKGROUND ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) ++#define YAFFS_COMPILE_FREEZER ++#endif ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) ++#define YAFFS_COMPILE_EXPORTFS ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_USE_SETATTR_COPY ++#define YAFFS_USE_TRUNCATE_SETSIZE ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_HAS_EVICT_INODE ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++#define YAFFS_NEW_FOLLOW_LINK 1 ++#else ++#define YAFFS_NEW_FOLLOW_LINK 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_HAS_WRITE_SUPER ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++#include ++#endif ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++#include ++#endif ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++#include ++#include ++#endif ++#ifdef YAFFS_COMPILE_FREEZER ++#include ++#endif ++ ++#include ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++#include ++ ++#define UnlockPage(p) unlock_page(p) ++#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) ++ ++/* FIXME: use sb->s_id instead ? */ ++#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) ++ ++#else ++ ++#include ++#define BDEVNAME_SIZE 0 ++#define yaffs_devname(sb, buf) kdevname(sb->s_dev) ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) ++/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ ++#define __user ++#endif ++ ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define YPROC_ROOT (&proc_root) ++#else ++#define YPROC_ROOT NULL ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define Y_INIT_TIMER(a) init_timer(a) ++#else ++#define Y_INIT_TIMER(a) init_timer_on_stack(a) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) ++#define YAFFS_USE_WRITE_BEGIN_END 1 ++#else ++#define YAFFS_USE_WRITE_BEGIN_END 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_SUPER_HAS_DIRTY ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) ++static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) ++{ ++ uint64_t result = partition_size; ++ do_div(result, block_size); ++ return (uint32_t) result; ++} ++#else ++#define YCALCBLOCKS(s, b) ((s)/(b)) ++#endif ++ ++#include ++#include ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#include "yaffs_linux.h" ++ ++#include "yaffs_mtdif.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_getblockinfo.h" ++ ++unsigned int yaffs_trace_mask = ++ YAFFS_TRACE_BAD_BLOCKS | ++ YAFFS_TRACE_ALWAYS | ++ 0; ++ ++unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; ++unsigned int yaffs_auto_checkpoint = 1; ++unsigned int yaffs_gc_control = 1; ++unsigned int yaffs_bg_enable = 1; ++unsigned int yaffs_auto_select = 1; ++/* Module Parameters */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++module_param(yaffs_trace_mask, uint, 0644); ++module_param(yaffs_wr_attempts, uint, 0644); ++module_param(yaffs_auto_checkpoint, uint, 0644); ++module_param(yaffs_gc_control, uint, 0644); ++module_param(yaffs_bg_enable, uint, 0644); ++#else ++MODULE_PARM(yaffs_trace_mask, "i"); ++MODULE_PARM(yaffs_wr_attempts, "i"); ++MODULE_PARM(yaffs_auto_checkpoint, "i"); ++MODULE_PARM(yaffs_gc_control, "i"); ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++/* use iget and read_inode */ ++#define Y_IGET(sb, inum) iget((sb), (inum)) ++ ++#else ++/* Call local equivalent */ ++#define YAFFS_USE_OWN_IGET ++#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) ++ ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) ++#else ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) ++#endif ++ ++#define yaffs_inode_to_obj(iptr) \ ++ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) ++#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) ++#else ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++#define Y_CLEAR_INODE(i) clear_inode(i) ++#else ++#define Y_CLEAR_INODE(i) end_writeback(i) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++#define YAFFS_USE_DIR_ITERATE ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0)) ++#define YAFFS_NEW_PROCFS ++#include ++#endif ++ ++ ++#define update_dir_time(dir) do {\ ++ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ ++ } while (0) ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj); ++ ++ ++static void yaffs_gross_lock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); ++ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); ++} ++ ++static void yaffs_gross_unlock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); ++ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++} ++ ++ ++static int yaffs_readpage_nolock(struct file *f, struct page *pg) ++{ ++ /* Lifted from jffs2 */ ++ ++ struct yaffs_obj *obj; ++ unsigned char *pg_buf; ++ int ret; ++ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readpage_nolock at %lld, size %08x", ++ (long long)pos, ++ (unsigned)PAGE_CACHE_SIZE); ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ BUG_ON(!PageLocked(pg)); ++#else ++ if (!PageLocked(pg)) ++ PAGE_BUG(pg); ++#endif ++ ++ pg_buf = kmap(pg); ++ /* FIXME: Can kmap fail? */ ++ ++ yaffs_gross_lock(dev); ++ ++ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (ret >= 0) ++ ret = 0; ++ ++ if (ret) { ++ ClearPageUptodate(pg); ++ SetPageError(pg); ++ } else { ++ SetPageUptodate(pg); ++ ClearPageError(pg); ++ } ++ ++ flush_dcache_page(pg); ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); ++ return ret; ++} ++ ++static int yaffs_readpage_unlock(struct file *f, struct page *pg) ++{ ++ int ret = yaffs_readpage_nolock(f, pg); ++ UnlockPage(pg); ++ return ret; ++} ++ ++static int yaffs_readpage(struct file *f, struct page *pg) ++{ ++ int ret; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); ++ ret = yaffs_readpage_unlock(f, pg); ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); ++ return ret; ++} ++ ++ ++static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc) ++ lc->dirty = val; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb) ++ sb->s_dirt = val; ++ } ++#endif ++ ++} ++ ++static void yaffs_set_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 1); ++} ++ ++static void yaffs_clear_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 0); ++} ++ ++static int yaffs_check_super_dirty(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc && lc->dirty) ++ return 1; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb && sb->s_dirt) ++ return 1; ++ } ++#endif ++ return 0; ++ ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_writepage(struct page *page, struct writeback_control *wbc) ++#else ++static int yaffs_writepage(struct page *page) ++#endif ++{ ++ struct yaffs_dev *dev; ++ struct address_space *mapping = page->mapping; ++ struct inode *inode; ++ unsigned long end_index; ++ char *buffer; ++ struct yaffs_obj *obj; ++ int n_written = 0; ++ unsigned n_bytes; ++ loff_t i_size; ++ ++ if (!mapping) ++ BUG(); ++ inode = mapping->host; ++ if (!inode) ++ BUG(); ++ i_size = i_size_read(inode); ++ ++ end_index = i_size >> PAGE_CACHE_SHIFT; ++ ++ if (page->index < end_index) ++ n_bytes = PAGE_CACHE_SIZE; ++ else { ++ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); ++ ++ if (page->index > end_index || !n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, inode size = %lld!!", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, ++ inode->i_size); ++ yaffs_trace(YAFFS_TRACE_OS, ++ " -> don't care!!"); ++ ++ zero_user_segment(page, 0, PAGE_CACHE_SIZE); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ return 0; ++ } ++ } ++ ++ if (n_bytes != PAGE_CACHE_SIZE) ++ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); ++ ++ get_page(page); ++ ++ buffer = kmap(page); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, size %08x", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag0: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ n_written = yaffs_wr_file(obj, buffer, ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag1: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ yaffs_gross_unlock(dev); ++ ++ kunmap(page); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ put_page(page); ++ ++ return (n_written == n_bytes) ? 0 : -ENOSPC; ++} ++ ++/* Space holding and freeing is done to ensure we have space available for write_begin/end */ ++/* For now we just assume few parallel writes and check against a small number. */ ++/* Todo: need to do this with a counter to handle parallel reads better */ ++ ++static ssize_t yaffs_hold_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ int n_free_chunks; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ n_free_chunks = yaffs_get_n_free_chunks(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ return (n_free_chunks > 20) ? 1 : 0; ++} ++ ++static void yaffs_release_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_gross_unlock(dev); ++} ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_begin(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ ++ struct page *pg = NULL; ++ pgoff_t index = pos >> PAGE_CACHE_SHIFT; ++ ++ int ret = 0; ++ int space_held = 0; ++ ++ /* Get a page */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ++ pg = grab_cache_page_write_begin(mapping, index, flags); ++#else ++ pg = __grab_cache_page(mapping, index); ++#endif ++ ++ *pagep = pg; ++ if (!pg) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "start yaffs_write_begin index %d(%x) uptodate %d", ++ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); ++ ++ /* Get fs space */ ++ space_held = yaffs_hold_space(filp); ++ ++ if (!space_held) { ++ ret = -ENOSPC; ++ goto out; ++ } ++ ++ /* Update page if required */ ++ ++ if (!Page_Uptodate(pg)) ++ ret = yaffs_readpage_nolock(filp, pg); ++ ++ if (ret) ++ goto out; ++ ++ /* Happy path return */ ++ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); ++ ++ return 0; ++ ++out: ++ yaffs_trace(YAFFS_TRACE_OS, ++ "end yaffs_write_begin fail returning %d", ret); ++ if (space_held) ++ yaffs_release_space(filp); ++ if (pg) { ++ unlock_page(pg); ++ page_cache_release(pg); ++ } ++ return ret; ++} ++ ++#else ++ ++static int yaffs_prepare_write(struct file *f, struct page *pg, ++ unsigned offset, unsigned to) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); ++ ++ if (!Page_Uptodate(pg)) ++ return yaffs_readpage_nolock(f, pg); ++ return 0; ++} ++#endif ++ ++ ++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, ++ loff_t * pos) ++{ ++ struct yaffs_obj *obj; ++ int n_written; ++ loff_t ipos; ++ struct inode *inode; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: hey obj is null!"); ++ return -EINVAL; ++ } ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ inode = f->f_dentry->d_inode; ++ ++ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) ++ ipos = inode->i_size; ++ else ++ ipos = *pos; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", ++ (unsigned)n, (unsigned)n, obj->obj_id, ipos); ++ ++ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: %d(%x) bytes written", ++ (unsigned)n, (unsigned)n); ++ ++ if (n_written > 0) { ++ ipos += n_written; ++ *pos = ipos; ++ if (ipos > inode->i_size) { ++ inode->i_size = ipos; ++ inode->i_blocks = (ipos + 511) >> 9; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write size updated to %lld bytes, %d blocks", ++ ipos, (int)(inode->i_blocks)); ++ } ++ ++ } ++ yaffs_gross_unlock(dev); ++ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; ++} ++ ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_end(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *pg, void *fsdadata) ++{ ++ int ret = 0; ++ void *addr, *kva; ++ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); ++ ++ kva = kmap(pg); ++ addr = kva + offset_into_page; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end addr %p pos %lld n_bytes %d", ++ addr, pos, copied); ++ ++ ret = yaffs_file_write(filp, addr, copied, &pos); ++ ++ if (ret != copied) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end not same size ret %d copied %d", ++ ret, copied); ++ SetPageError(pg); ++ } ++ ++ kunmap(pg); ++ ++ yaffs_release_space(filp); ++ unlock_page(pg); ++ page_cache_release(pg); ++ return ret; ++} ++#else ++ ++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, ++ unsigned to) ++{ ++ void *addr, *kva; ++ ++ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; ++ int n_bytes = to - offset; ++ int n_written; ++ ++ kva = kmap(pg); ++ addr = kva + offset; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write addr %p pos %lld n_bytes %d", ++ addr, pos, n_bytes); ++ ++ n_written = yaffs_file_write(f, addr, n_bytes, &pos); ++ ++ if (n_written != n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write not same size n_written %d n_bytes %d", ++ n_written, n_bytes); ++ SetPageError(pg); ++ } ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write returning %d", ++ n_written == n_bytes ? 0 : n_written); ++ ++ return n_written == n_bytes ? 0 : n_written; ++} ++#endif ++ ++static struct address_space_operations yaffs_file_address_operations = { ++ .readpage = yaffs_readpage, ++ .writepage = yaffs_writepage, ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++ .write_begin = yaffs_write_begin, ++ .write_end = yaffs_write_end, ++#else ++ .prepare_write = yaffs_prepare_write, ++ .commit_write = yaffs_commit_write, ++#endif ++}; ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_file_flush(struct file *file, fl_owner_t id) ++#else ++static int yaffs_file_flush(struct file *file) ++#endif ++{ ++ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); ++ ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_flush object %d (%s)", ++ obj->obj_id, ++ obj->dirty ? "dirty" : "clean"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_file(obj, 1, 0, 0); ++ ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++static int yaffs_sync_object(struct file *file, int datasync) ++#else ++static int yaffs_sync_object(struct file *file, struct dentry *dentry, ++ int datasync) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++ struct dentry *dentry = file->f_path.dentry; ++#endif ++ ++ obj = yaffs_dentry_to_obj(dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_object"); ++ yaffs_gross_lock(dev); ++ yaffs_flush_file(obj, 1, datasync, 0); ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .read_iter = generic_file_read_iter, ++ .write_iter = generic_file_write_iter, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .splice_read = generic_file_splice_read, ++ .splice_write = iter_file_splice_write, ++ .llseek = generic_file_llseek, ++}; ++ ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .sendfile = generic_file_sendfile, ++}; ++ ++#else ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = generic_file_read, ++ .write = generic_file_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ .sendfile = generic_file_sendfile, ++#endif ++}; ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++static void zero_user_segment(struct page *page, unsigned start, unsigned end) ++{ ++ void *kaddr = kmap_atomic(page, KM_USER0); ++ memset(kaddr + start, 0, end - start); ++ kunmap_atomic(kaddr, KM_USER0); ++ flush_dcache_page(page); ++} ++#endif ++ ++ ++static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) ++{ ++#ifdef YAFFS_USE_TRUNCATE_SETSIZE ++ truncate_setsize(inode, newsize); ++ return 0; ++#else ++ truncate_inode_pages(&inode->i_data, newsize); ++ return 0; ++#endif ++ ++} ++ ++ ++static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) ++{ ++#ifdef YAFFS_USE_SETATTR_COPY ++ setattr_copy(inode, attr); ++ return 0; ++#else ++ return inode_setattr(inode, attr); ++#endif ++ ++} ++ ++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_setattr of object %d", ++ yaffs_inode_to_obj(inode)->obj_id); ++#if 0 ++ /* Fail if a requested resize >= 2GB */ ++ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) ++ error = -EINVAL; ++#endif ++ ++ if (error == 0) ++ error = inode_change_ok(inode, attr); ++ if (error == 0) { ++ int result; ++ if (!error) { ++ error = yaffs_vfs_setattr(inode, attr); ++ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_vfs_setsize(inode, attr->ia_size); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ } ++ } ++ dev = yaffs_inode_to_obj(inode)->my_dev; ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "resize to %d(%x)", ++ (int)(attr->ia_size), ++ (int)(attr->ia_size)); ++ } ++ yaffs_gross_lock(dev); ++ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); ++ if (result == YAFFS_OK) { ++ error = 0; ++ } else { ++ error = -EPERM; ++ } ++ yaffs_gross_unlock(dev); ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_set_xattrib(obj, name, value, size, flags); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, ++ void *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_getxattr \"%s\" from object %d", ++ name, obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_get_xattrib(obj, name, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_remove_xattrib(obj, name); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_list_xattrib(obj, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr done returning %d", error); ++ ++ return error; ++} ++ ++ ++static const struct inode_operations yaffs_file_inode_operations = { ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++ ++static int yaffs_readlink(struct dentry *dentry, char __user * buffer, ++ int buflen) ++{ ++ unsigned char *alias; ++ int ret; ++ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) ++ return -ENOMEM; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) ++ ret = vfs_readlink(dentry, buffer, buflen, alias); ++#else ++ ret = readlink_copy(buffer, buflen, alias); ++#endif ++ kfree(alias); ++ return ret; ++} ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ void *ret; ++#else ++static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int ret ++#endif ++ unsigned char *alias; ++ int ret_int = 0; ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) { ++ ret_int = -ENOMEM; ++ goto out; ++ } ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ nd_set_link(nd, alias); ++ ret = alias; ++out: ++ if (ret_int) ++ ret = ERR_PTR(ret_int); ++ return ret; ++#else ++ ret = vfs_follow_link(nd, alias); ++ kfree(alias); ++out: ++ if (ret_int) ++ ret = ret_int; ++ return ret; ++#endif ++} ++ ++ ++#ifdef YAFFS_HAS_PUT_INODE ++ ++/* For now put inode is just for debugging ++ * Put inode is called when the inode **structure** is put. ++ */ ++static void yaffs_put_inode(struct inode *inode) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_put_inode: ino %d, count %d"), ++ (int)inode->i_ino, atomic_read(&inode->i_count); ++ ++} ++#endif ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) ++{ ++ kfree(alias); ++} ++#endif ++ ++static const struct inode_operations yaffs_symlink_inode_operations = { ++ .readlink = yaffs_readlink, ++ .follow_link = yaffs_follow_link, ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ .put_link = yaffs_put_link, ++#endif ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++#ifdef YAFFS_USE_OWN_IGET ++ ++static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) ++{ ++ struct inode *inode; ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); ++ ++ inode = iget_locked(sb, ino); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ yaffs_gross_unlock(dev); ++ ++ unlock_new_inode(inode); ++ return inode; ++} ++ ++#else ++ ++static void yaffs_read_inode(struct inode *inode) ++{ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_inode for %d", (int)inode->i_ino); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++} ++ ++#endif ++ ++ ++ ++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, ++ struct yaffs_obj *obj) ++{ ++ struct inode *inode; ++ ++ if (!sb) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL super_block!!"); ++ return NULL; ++ ++ } ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL object!!"); ++ return NULL; ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for object %d", obj->obj_id); ++ ++ inode = Y_IGET(sb, obj->obj_id); ++ if (IS_ERR(inode)) ++ return NULL; ++ ++ /* NB Side effect: iget calls back to yaffs_read_inode(). */ ++ /* iget also increments the inode's i_count */ ++ /* NB You can't be holding gross_lock or deadlock will happen! */ ++ ++ return inode; ++} ++ ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++#define YCRED(x) x ++#else ++#define YCRED(x) (x->cred) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) ++#define YPROC_uid(p) (YCRED(p)->fsuid) ++#define YPROC_gid(p) (YCRED(p)->fsgid) ++#define EXTRACT_gid(x) x ++#define EXTRACT_uid(x) x ++#define MAKE_gid(x) x ++#define MAKE_uid(x) x ++#else ++#define YPROC_uid(p) from_kuid(&init_user_ns, YCRED(p)->fsuid) ++#define YPROC_gid(p) from_kgid(&init_user_ns, YCRED(p)->fsgid) ++#define EXTRACT_gid(x) from_kgid(&init_user_ns, x) ++#define EXTRACT_uid(x) from_kuid(&init_user_ns, x) ++#define MAKE_gid(x) make_kgid(&init_user_ns, x) ++#define MAKE_uid(x) make_kuid(&init_user_ns, x) ++#endif ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t rdev) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t rdev) ++#else ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ int rdev) ++#endif ++{ ++ struct inode *inode; ++ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_dev *dev; ++ ++ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); ++ ++ int error = -ENOSPC; ++ uid_t uid = YPROC_uid(current); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); ++ ++ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) ++ mode |= S_ISGID; ++ ++ if (parent) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: parent object %d type %d", ++ parent->obj_id, parent->variant_type); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: could not get parent object"); ++ return -EPERM; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: making oject for %s, mode %x dev %x", ++ dentry->d_name.name, mode, rdev); ++ ++ dev = parent->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ switch (mode & S_IFMT) { ++ default: ++ /* Special (socket, fifo, device...) */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, old_encode_dev(rdev)); ++#else ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, rdev); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); ++ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, ++ gid); ++ break; ++ case S_IFDIR: /* directory */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); ++ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, ++ uid, gid); ++ break; ++ case S_IFLNK: /* symlink */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); ++ obj = NULL; /* Do we ever get here? */ ++ break; ++ } ++ ++ /* Can not call yaffs_get_inode() with gross lock held */ ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod created object %d count = %d", ++ obj->obj_id, atomic_read(&inode->i_count)); ++ error = 0; ++ yaffs_fill_inode_from_obj(dir, parent); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); ++ error = -ENOMEM; ++ } ++ ++ return error; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++#else ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ int ret_val; ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); ++ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); ++ return ret_val; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool dummy) ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ struct nameidata *n) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *n) ++#else ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); ++ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ unsigned int dummy) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *n) ++#else ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ ++ ++ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", ++ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); ++ ++ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); ++ ++ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ ++ ++ /* Can't hold gross lock when calling yaffs_get_inode() */ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_lookup found %d", obj->obj_id); ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); ++ ++ } ++ ++/* added NCB for 2.5/6 compatability - forces add even if inode is ++ * NULL which creates dentry hash */ ++ d_add(dentry, inode); ++ ++ return NULL; ++} ++ ++/* ++ * Create a link... ++ */ ++static int yaffs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ struct inode *inode = old_dentry->d_inode; ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *link = NULL; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ ++ link = ++ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ obj); ++ ++ if (link) { ++ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); ++ d_instantiate(dentry, old_dentry->d_inode); ++ atomic_inc(&old_dentry->d_inode->i_count); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_link link count %d i_count %d", ++ old_dentry->d_inode->i_nlink, ++ atomic_read(&old_dentry->d_inode->i_count)); ++ } ++ ++ yaffs_gross_unlock(dev); ++ ++ if (link) { ++ update_dir_time(dir); ++ return 0; ++ } ++ ++ return -EPERM; ++} ++ ++static int yaffs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ uid_t uid = YPROC_uid(current); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? EXTRACT_gid(dir->i_gid) : YPROC_gid(current); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); ++ ++ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ return -ENAMETOOLONG; ++ ++ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > ++ YAFFS_MAX_ALIAS_LENGTH) ++ return -ENAMETOOLONG; ++ ++ dev = yaffs_inode_to_obj(dir)->my_dev; ++ yaffs_gross_lock(dev); ++ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ S_IFLNK | S_IRWXUGO, uid, gid, symname); ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ struct inode *inode; ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); ++ return 0; ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); ++ } ++ ++ return -ENOMEM; ++} ++ ++/* ++ * The VFS layer already does all the dentry stuff for rename. ++ * ++ * NB: POSIX says you can rename an object over an old object of the same name ++ */ ++static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct yaffs_dev *dev; ++ int ret_val = YAFFS_FAIL; ++ struct yaffs_obj *target; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); ++ dev = yaffs_inode_to_obj(old_dir)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ /* Check if the target is an existing directory that is not empty. */ ++ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ ++ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && ++ !list_empty(&target->variant.dir_variant.children)) { ++ ++ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); ++ ++ ret_val = YAFFS_FAIL; ++ } else { ++ /* Now does unlinking internally using shadowing mechanism */ ++ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); ++ ++ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), ++ old_dentry->d_name.name, ++ yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ } ++ yaffs_gross_unlock(dev); ++ ++ if (ret_val == YAFFS_OK) { ++ if (target) ++ inode_dec_link_count(new_dentry->d_inode); ++ ++ update_dir_time(old_dir); ++ if (old_dir != new_dir) ++ update_dir_time(new_dir); ++ return 0; ++ } else { ++ return -ENOTEMPTY; ++ } ++} ++ ++ ++ ++ ++static int yaffs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int ret_val; ++ ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", ++ (int)(dir->i_ino), dentry->d_name.name); ++ obj = yaffs_inode_to_obj(dir); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ ret_val = yaffs_unlinker(obj, dentry->d_name.name); ++ ++ if (ret_val == YAFFS_OK) { ++ inode_dec_link_count(dentry->d_inode); ++ dir->i_version++; ++ yaffs_gross_unlock(dev); ++ update_dir_time(dir); ++ return 0; ++ } ++ yaffs_gross_unlock(dev); ++ return -ENOTEMPTY; ++} ++ ++ ++ ++static const struct inode_operations yaffs_dir_inode_operations = { ++ .create = yaffs_create, ++ .lookup = yaffs_lookup, ++ .link = yaffs_link, ++ .unlink = yaffs_unlink, ++ .symlink = yaffs_symlink, ++ .mkdir = yaffs_mkdir, ++ .rmdir = yaffs_unlink, ++ .mknod = yaffs_mknod, ++ .rename = yaffs_rename, ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++/*-----------------------------------------------------------------*/ ++/* Directory search context allows us to unlock access to yaffs during ++ * filldir without causing problems with the directory being modified. ++ * This is similar to the tried and tested mechanism used in yaffs direct. ++ * ++ * A search context iterates along a doubly linked list of siblings in the ++ * directory. If the iterating object is deleted then this would corrupt ++ * the list iteration, likely causing a crash. The search context avoids ++ * this by using the remove_obj_fn to move the search context to the ++ * next object before the object is deleted. ++ * ++ * Many readdirs (and thus seach conexts) may be alive simulateously so ++ * each struct yaffs_dev has a list of these. ++ * ++ * A seach context lives for the duration of a readdir. ++ * ++ * All these functions must be called while yaffs is locked. ++ */ ++ ++struct yaffs_search_context { ++ struct yaffs_dev *dev; ++ struct yaffs_obj *dir_obj; ++ struct yaffs_obj *next_return; ++ struct list_head others; ++}; ++ ++/* ++ * yaffs_new_search() creates a new search context, initialises it and ++ * adds it to the device's search context list. ++ * ++ * Called at start of readdir. ++ */ ++static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) ++{ ++ struct yaffs_dev *dev = dir->my_dev; ++ struct yaffs_search_context *sc = ++ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); ++ if (sc) { ++ sc->dir_obj = dir; ++ sc->dev = dev; ++ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else ++ sc->next_return = ++ list_entry(dir->variant.dir_variant.children.next, ++ struct yaffs_obj, siblings); ++ INIT_LIST_HEAD(&sc->others); ++ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); ++ } ++ return sc; ++} ++ ++/* ++ * yaffs_search_end() disposes of a search context and cleans up. ++ */ ++static void yaffs_search_end(struct yaffs_search_context *sc) ++{ ++ if (sc) { ++ list_del(&sc->others); ++ kfree(sc); ++ } ++} ++ ++/* ++ * yaffs_search_advance() moves a search context to the next object. ++ * Called when the search iterates or when an object removal causes ++ * the search context to be moved to the next object. ++ */ ++static void yaffs_search_advance(struct yaffs_search_context *sc) ++{ ++ if (!sc) ++ return; ++ ++ if (sc->next_return == NULL || ++ list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else { ++ struct list_head *next = sc->next_return->siblings.next; ++ ++ if (next == &sc->dir_obj->variant.dir_variant.children) ++ sc->next_return = NULL; /* end of list */ ++ else ++ sc->next_return = ++ list_entry(next, struct yaffs_obj, siblings); ++ } ++} ++ ++/* ++ * yaffs_remove_obj_callback() is called when an object is unlinked. ++ * We check open search contexts and advance any which are currently ++ * on the object being iterated. ++ */ ++static void yaffs_remove_obj_callback(struct yaffs_obj *obj) ++{ ++ ++ struct list_head *i; ++ struct yaffs_search_context *sc; ++ struct list_head *search_contexts = ++ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); ++ ++ /* Iterate through the directory search contexts. ++ * If any are currently on the object being removed, then advance ++ * the search context to the next object to prevent a hanging pointer. ++ */ ++ list_for_each(i, search_contexts) { ++ sc = list_entry(i, struct yaffs_search_context, others); ++ if (sc->next_return == obj) ++ yaffs_search_advance(sc); ++ } ++ ++} ++ ++ ++/*-----------------------------------------------------------------*/ ++ ++#ifdef YAFFS_USE_DIR_ITERATE ++static int yaffs_iterate(struct file *f, struct dir_context *dc) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ unsigned long curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ if (!dir_emit_dots(f, dc)) ++ return 0; ++ ++ curoffs = 1; ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= dc->pos) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!dir_emit(dc, ++ name, ++ strlen(name), ++ this_inode, ++ this_type)) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ dc->pos++; ++ f->f_pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++ ++#else ++ ++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ struct inode *inode = f->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ offset = f->f_pos; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: starting at %d", (int)offset); ++ ++ if (offset == 0) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry . ino %d", ++ (int)inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ if (offset == 1) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry .. ino %d", ++ (int)f->f_dentry->d_parent->d_inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, "..", 2, offset, ++ f->f_dentry->d_parent->d_inode->i_ino, ++ DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ if (f->f_version != inode->i_version) { ++ offset = 2; ++ f->f_pos = offset; ++ f->f_version = inode->i_version; ++ } ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= offset) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (filldir(dirent, ++ name, ++ strlen(name), ++ offset, this_inode, this_type) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ offset++; ++ f->f_pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++ ++#endif ++ ++static const struct file_operations yaffs_dir_operations = { ++ .read = generic_read_dir, ++#ifdef YAFFS_USE_DIR_ITERATE ++ .iterate = yaffs_iterate, ++#else ++ .readdir = yaffs_readdir, ++#endif ++ .fsync = yaffs_sync_object, ++ .llseek = generic_file_llseek, ++}; ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj) ++{ ++ if (inode && obj) { ++ ++ /* Check mode against the variant type and attempt to repair if broken. */ ++ u32 mode = obj->yst_mode; ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (!S_ISREG(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFREG; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ if (!S_ISLNK(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFLNK; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!S_ISDIR(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFDIR; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ default: ++ /* TODO? */ ++ break; ++ } ++ ++ inode->i_flags |= S_NOATIME; ++ ++ inode->i_ino = obj->obj_id; ++ inode->i_mode = obj->yst_mode; ++ inode->i_uid = MAKE_uid(obj->yst_uid); ++ inode->i_gid = MAKE_gid(obj->yst_gid); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++ inode->i_blksize = inode->i_sb->s_blocksize; ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++ inode->i_rdev = old_decode_dev(obj->yst_rdev); ++ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); ++ inode->i_atime.tv_nsec = 0; ++ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; ++ inode->i_mtime.tv_nsec = 0; ++ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; ++ inode->i_ctime.tv_nsec = 0; ++#else ++ inode->i_rdev = obj->yst_rdev; ++ inode->i_atime = obj->yst_atime; ++ inode->i_mtime = obj->yst_mtime; ++ inode->i_ctime = obj->yst_ctime; ++#endif ++ inode->i_size = yaffs_get_obj_length(obj); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ ++ set_nlink(inode, yaffs_get_obj_link_count(obj)); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", ++ inode->i_mode, obj->yst_uid, obj->yst_gid, ++ inode->i_size, atomic_read(&inode->i_count)); ++ ++ switch (obj->yst_mode & S_IFMT) { ++ default: /* fifo, device or socket */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ init_special_inode(inode, obj->yst_mode, ++ old_decode_dev(obj->yst_rdev)); ++#else ++ init_special_inode(inode, obj->yst_mode, ++ (dev_t) (obj->yst_rdev)); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ inode->i_op = &yaffs_file_inode_operations; ++ inode->i_fop = &yaffs_file_operations; ++ inode->i_mapping->a_ops = ++ &yaffs_file_address_operations; ++ break; ++ case S_IFDIR: /* directory */ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ break; ++ case S_IFLNK: /* symlink */ ++ inode->i_op = &yaffs_symlink_inode_operations; ++ break; ++ } ++ ++ yaffs_inode_to_obj_lv(inode) = obj; ++ ++ obj->my_inode = inode; ++ ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode invalid parameters"); ++ } ++ ++} ++ ++ ++ ++/* ++ * yaffs background thread functions . ++ * yaffs_bg_thread_fn() the thread function ++ * yaffs_bg_start() launches the background thread. ++ * yaffs_bg_stop() cleans up the background thread. ++ * ++ * NB: ++ * The thread should only run after the yaffs is initialised ++ * The thread should be stopped before yaffs is unmounted. ++ * The thread should not do any writing while the fs is in read only. ++ */ ++ ++static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) ++{ ++ unsigned erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned scattered = 0; /* Free chunks not in an erased block */ ++ ++ if (erased_chunks < dev->n_free_chunks) ++ scattered = (dev->n_free_chunks - erased_chunks); ++ ++ if (!context->bg_running) ++ return 0; ++ else if (scattered < (dev->param.chunks_per_block * 2)) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 2) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 4) ++ return 1; ++ else ++ return 2; ++} ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++ ++void yaffs_background_waker(unsigned long data) ++{ ++ wake_up_process((struct task_struct *)data); ++} ++ ++static int yaffs_bg_thread_fn(void *data) ++{ ++ struct yaffs_dev *dev = (struct yaffs_dev *)data; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned long now = jiffies; ++ unsigned long next_dir_update = now; ++ unsigned long next_gc = now; ++ unsigned long expires; ++ unsigned int urgency; ++ ++ int gc_result; ++ struct timer_list timer; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "yaffs_background starting for dev %p", (void *)dev); ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ set_freezable(); ++#endif ++ while (context->bg_running) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); ++ ++ if (kthread_should_stop()) ++ break; ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ if (try_to_freeze()) ++ continue; ++#endif ++ yaffs_gross_lock(dev); ++ ++ now = jiffies; ++ ++ if (time_after(now, next_dir_update) && yaffs_bg_enable) { ++ yaffs_update_dirty_dirs(dev); ++ next_dir_update = now + HZ; ++ } ++ ++ if (time_after(now, next_gc) && yaffs_bg_enable) { ++ if (!dev->is_checkpointed) { ++ urgency = yaffs_bg_gc_urgency(dev); ++ gc_result = yaffs_bg_gc(dev, urgency); ++ if (urgency > 1) ++ next_gc = now + HZ / 20 + 1; ++ else if (urgency > 0) ++ next_gc = now + HZ / 10 + 1; ++ else ++ next_gc = now + HZ * 2; ++ } else { ++ /* ++ * gc not running so set to next_dir_update ++ * to cut down on wake ups ++ */ ++ next_gc = next_dir_update; ++ } ++ } ++ yaffs_gross_unlock(dev); ++#if 1 ++ expires = next_dir_update; ++ if (time_before(next_gc, expires)) ++ expires = next_gc; ++ if (time_before(expires, now)) ++ expires = now + HZ; ++ ++ Y_INIT_TIMER(&timer); ++ timer.expires = expires + 1; ++ timer.data = (unsigned long)current; ++ timer.function = yaffs_background_waker; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_timer(&timer); ++ schedule(); ++ del_timer_sync(&timer); ++#else ++ msleep(10); ++#endif ++ } ++ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ int retval = 0; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ ++ if (dev->read_only) ++ return -1; ++ ++ context->bg_running = 1; ++ ++ context->bg_thread = kthread_run(yaffs_bg_thread_fn, ++ (void *)dev, "yaffs-bg-%d", ++ context->mount_id); ++ ++ if (IS_ERR(context->bg_thread)) { ++ retval = PTR_ERR(context->bg_thread); ++ context->bg_thread = NULL; ++ context->bg_running = 0; ++ } ++ return retval; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); ++ ++ ctxt->bg_running = 0; ++ ++ if (ctxt->bg_thread) { ++ kthread_stop(ctxt->bg_thread); ++ ctxt->bg_thread = NULL; ++ } ++} ++#else ++static int yaffs_bg_thread_fn(void *data) ++{ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ return 0; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++} ++#endif ++ ++ ++static void yaffs_flush_inodes(struct super_block *sb) ++{ ++ struct inode *iptr; ++ struct yaffs_obj *obj; ++ ++ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { ++ obj = yaffs_inode_to_obj(iptr); ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "flushing obj %d", ++ obj->obj_id); ++ yaffs_flush_file(obj, 1, 0, 0); ++ } ++ } ++} ++ ++static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ if (!dev) ++ return; ++ ++ yaffs_flush_inodes(sb); ++ yaffs_update_dirty_dirs(dev); ++ yaffs_flush_whole_cache(dev, 1); ++ if (do_checkpoint) ++ yaffs_checkpoint_save(dev); ++} ++ ++static LIST_HEAD(yaffs_context_list); ++struct mutex yaffs_context_lock; ++ ++static void yaffs_put_super(struct super_block *sb) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super"); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "Shutting down yaffs background thread"); ++ yaffs_bg_stop(dev); ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "yaffs background thread shut down"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_super(sb, 1); ++ ++ yaffs_deinitialise(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ mutex_lock(&yaffs_context_lock); ++ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); ++ mutex_unlock(&yaffs_context_lock); ++ ++ if (yaffs_dev_to_lc(dev)->spare_buffer) { ++ kfree(yaffs_dev_to_lc(dev)->spare_buffer); ++ yaffs_dev_to_lc(dev)->spare_buffer = NULL; ++ } ++ ++ kfree(dev); ++ ++ yaffs_put_mtd_device(mtd); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super done"); ++} ++ ++ ++static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) ++{ ++ return yaffs_gc_control; ++} ++ ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ ++static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, ++ uint32_t generation) ++{ ++ return Y_IGET(sb, ino); ++} ++ ++static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_parent(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++struct dentry *yaffs2_get_parent(struct dentry *dentry) ++{ ++ ++ struct super_block *sb = dentry->d_inode->i_sb; ++ struct dentry *parent = ERR_PTR(-ENOENT); ++ struct inode *inode; ++ unsigned long parent_ino; ++ struct yaffs_obj *d_obj; ++ struct yaffs_obj *parent_obj; ++ ++ d_obj = yaffs_inode_to_obj(dentry->d_inode); ++ ++ if (d_obj) { ++ parent_obj = d_obj->parent; ++ if (parent_obj) { ++ parent_ino = yaffs_get_obj_inode(parent_obj); ++ inode = Y_IGET(sb, parent_ino); ++ ++ if (IS_ERR(inode)) { ++ parent = ERR_CAST(inode); ++ } else { ++ parent = d_obtain_alias(inode); ++ if (!IS_ERR(parent)) { ++ parent = ERR_PTR(-ENOMEM); ++ iput(inode); ++ } ++ } ++ } ++ } ++ ++ return parent; ++} ++ ++/* Just declare a zero structure as a NULL value implies ++ * using the default functions of exportfs. ++ */ ++ ++static struct export_operations yaffs_export_ops = { ++ .fh_to_dentry = yaffs2_fh_to_dentry, ++ .fh_to_parent = yaffs2_fh_to_parent, ++ .get_parent = yaffs2_get_parent, ++}; ++ ++#endif ++ ++static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) ++{ ++ /* Clear the association between the inode and ++ * the struct yaffs_obj. ++ */ ++ obj->my_inode = NULL; ++ yaffs_inode_to_obj_lv(inode) = NULL; ++ ++ /* If the object freeing was deferred, then the real ++ * free happens now. ++ * This should fix the inode inconsistency problem. ++ */ ++ yaffs_handle_defered_free(obj); ++} ++ ++#ifdef YAFFS_HAS_EVICT_INODE ++/* yaffs_evict_inode combines into one operation what was previously done in ++ * yaffs_clear_inode() and yaffs_delete_inode() ++ * ++ */ ++static void yaffs_evict_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ int deleteme = 0; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_evict_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (!inode->i_nlink && !is_bad_inode(inode)) ++ deleteme = 1; ++ truncate_inode_pages(&inode->i_data, 0); ++ Y_CLEAR_INODE(inode); ++ ++ if (deleteme && obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++} ++#else ++ ++/* clear is called to tell the fs to release any per-inode data it holds. ++ * The object might still exist on disk and is just being thrown out of the cache ++ * or else the object has actually been deleted and we're being called via ++ * the chain ++ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() ++ */ ++ ++static void yaffs_clear_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_clear_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++ ++} ++ ++/* delete is called when the link count is zero and the inode ++ * is put (ie. nobody wants to know about it anymore, time to ++ * delete the file). ++ * NB Must call clear_inode() ++ */ ++static void yaffs_delete_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_delete_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++ truncate_inode_pages(&inode->i_data, 0); ++#endif ++ clear_inode(inode); ++} ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ struct super_block *sb = dentry->d_sb; ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#else ++static int yaffs_statfs(struct super_block *sb, struct statfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#endif ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); ++ ++ yaffs_gross_lock(dev); ++ ++ buf->f_type = YAFFS_MAGIC; ++ buf->f_bsize = sb->s_blocksize; ++ buf->f_namelen = 255; ++ ++ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { ++ /* Do this if chunk size is not a power of 2 */ ++ ++ uint64_t bytes_in_dev; ++ uint64_t bytes_free; ++ ++ bytes_in_dev = ++ ((uint64_t) ++ ((dev->param.end_block - dev->param.start_block + ++ 1))) * ((uint64_t) (dev->param.chunks_per_block * ++ dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ ++ buf->f_blocks = bytes_in_dev; ++ ++ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * ++ ((uint64_t) (dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_free, sb->s_blocksize); ++ ++ buf->f_bfree = bytes_free; ++ ++ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { ++ ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ } else { ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ } ++ ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_bavail = buf->f_bfree; ++ ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++ ++static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) ++{ ++ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); ++ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); ++ int do_checkpoint; ++ int dirty = yaffs_check_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", ++ gc_urgent, ++ dirty ? "dirty" : "clean", ++ request_checkpoint ? "checkpoint requested" : "no checkpoint", ++ oneshot_checkpoint ? " one-shot" : ""); ++ ++ yaffs_gross_lock(dev); ++ do_checkpoint = ((request_checkpoint && !gc_urgent) || ++ oneshot_checkpoint) && !dev->is_checkpointed; ++ ++ if (dirty || do_checkpoint) { ++ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); ++ yaffs_clear_super_dirty(dev); ++ if (oneshot_checkpoint) ++ yaffs_auto_checkpoint &= ~4; ++ } ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#ifdef YAFFS_HAS_WRITE_SUPER ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static void yaffs_write_super(struct super_block *sb) ++#else ++static int yaffs_write_super(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_write_super %s", ++ request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) ++ return 0; ++#endif ++} ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_sync_fs(struct super_block *sb, int wait) ++#else ++static int yaffs_sync_fs(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++ return 0; ++} ++ ++/* the function only is used to change dev->read_only when this file system ++ * is remounted. ++ */ ++static int yaffs_remount_fs(struct super_block *sb, int *flags, char *data) ++{ ++ int read_only = 0; ++ struct mtd_info *mtd; ++ struct yaffs_dev *dev = 0; ++ ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (!mtd) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device #%u doesn't appear to exist", ++ MINOR(sb->s_dev)); ++ return 1; ++ } ++ ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device is not NAND it's type %d", ++ mtd->type); ++ return 1; ++ } ++ ++ read_only = ((*flags & MS_RDONLY) != 0); ++ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { ++ read_only = 1; ++ printk(KERN_INFO ++ "yaffs: mtd is read only, setting superblock read only"); ++ *flags |= MS_RDONLY; ++ } ++ ++ dev = sb->s_fs_info; ++ dev->read_only = read_only; ++ ++ return 0; ++} ++ ++static const struct super_operations yaffs_super_ops = { ++ .statfs = yaffs_statfs, ++ ++#ifndef YAFFS_USE_OWN_IGET ++ .read_inode = yaffs_read_inode, ++#endif ++#ifdef YAFFS_HAS_PUT_INODE ++ .put_inode = yaffs_put_inode, ++#endif ++ .put_super = yaffs_put_super, ++#ifdef YAFFS_HAS_EVICT_INODE ++ .evict_inode = yaffs_evict_inode, ++#else ++ .delete_inode = yaffs_delete_inode, ++ .clear_inode = yaffs_clear_inode, ++#endif ++ .sync_fs = yaffs_sync_fs, ++#ifdef YAFFS_HAS_WRITE_SUPER ++ .write_super = yaffs_write_super, ++#endif ++ .remount_fs = yaffs_remount_fs, ++}; ++ ++struct yaffs_options { ++ int inband_tags; ++ int skip_checkpoint_read; ++ int skip_checkpoint_write; ++ int no_cache; ++ int tags_ecc_on; ++ int tags_ecc_overridden; ++ int lazy_loading_enabled; ++ int lazy_loading_overridden; ++ int empty_lost_and_found; ++ int empty_lost_and_found_overridden; ++ int disable_summary; ++}; ++ ++#define MAX_OPT_LEN 30 ++static int yaffs_parse_options(struct yaffs_options *options, ++ const char *options_str) ++{ ++ char cur_opt[MAX_OPT_LEN + 1]; ++ int p; ++ int error = 0; ++ ++ /* Parse through the options which is a comma seperated list */ ++ ++ while (options_str && *options_str && !error) { ++ memset(cur_opt, 0, MAX_OPT_LEN + 1); ++ p = 0; ++ ++ while (*options_str == ',') ++ options_str++; ++ ++ while (*options_str && *options_str != ',') { ++ if (p < MAX_OPT_LEN) { ++ cur_opt[p] = *options_str; ++ p++; ++ } ++ options_str++; ++ } ++ ++ if (!strcmp(cur_opt, "inband-tags")) { ++ options->inband_tags = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-off")) { ++ options->tags_ecc_on = 0; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-on")) { ++ options->tags_ecc_on = 1; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-off")) { ++ options->lazy_loading_enabled = 0; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-on")) { ++ options->lazy_loading_enabled = 1; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "disable-summary")) { ++ options->disable_summary = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { ++ options->empty_lost_and_found = 0; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { ++ options->empty_lost_and_found = 1; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "no-cache")) { ++ options->no_cache = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { ++ options->skip_checkpoint_read = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { ++ options->skip_checkpoint_write = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint")) { ++ options->skip_checkpoint_read = 1; ++ options->skip_checkpoint_write = 1; ++ } else { ++ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", ++ cur_opt); ++ error = 1; ++ } ++ } ++ ++ return error; ++} ++ ++ ++static struct dentry *yaffs_make_root(struct inode *inode) ++{ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++ struct dentry *root = d_alloc_root(inode); ++ ++ if (!root) ++ iput(inode); ++ ++ return root; ++#else ++ return d_make_root(inode); ++#endif ++} ++ ++ ++ ++ ++static struct super_block *yaffs_internal_read_super(int yaffs_version, ++ struct super_block *sb, ++ void *data, int silent) ++{ ++ int n_blocks; ++ struct inode *inode = NULL; ++ struct dentry *root; ++ struct yaffs_dev *dev = 0; ++ char devname_buf[BDEVNAME_SIZE + 1]; ++ struct mtd_info *mtd; ++ int err; ++ char *data_str = (char *)data; ++ struct yaffs_linux_context *context = NULL; ++ struct yaffs_param *param; ++ ++ int read_only = 0; ++ int inband_tags = 0; ++ ++ struct yaffs_options options; ++ ++ unsigned mount_id; ++ int found; ++ struct yaffs_linux_context *context_iterator; ++ struct list_head *l; ++ ++ if (!sb) { ++ printk(KERN_INFO "yaffs: sb is NULL\n"); ++ return NULL; ++ } ++ ++ sb->s_magic = YAFFS_MAGIC; ++ sb->s_op = &yaffs_super_ops; ++ sb->s_flags |= MS_NOATIME; ++ ++ read_only = ((sb->s_flags & MS_RDONLY) != 0); ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ sb->s_export_op = &yaffs_export_ops; ++#endif ++ ++ if (!sb->s_dev) ++ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); ++ else if (!yaffs_devname(sb, devname_buf)) ++ printk(KERN_INFO "yaffs: devname is NULL\n"); ++ else ++ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", ++ sb->s_dev, ++ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); ++ ++ if (!data_str) ++ data_str = ""; ++ ++ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); ++ ++ memset(&options, 0, sizeof(options)); ++ ++ if (yaffs_parse_options(&options, data_str)) { ++ /* Option parsing failed */ ++ return NULL; ++ } ++ ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: Using yaffs%d", yaffs_version); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", ++ MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ yaffs_devname(sb, devname_buf)); ++ ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (IS_ERR(mtd)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device %u either not valid or unavailable", ++ MINOR(sb->s_dev)); ++ return NULL; ++ } ++ ++ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); ++ yaffs_version = 2; ++ } ++ ++ /* Added NCB 26/5/2006 for completeness */ ++ if (yaffs_version == 2 && !options.inband_tags ++ && WRITE_SIZE(mtd) == 512) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); ++ yaffs_version = 1; ++ } ++ ++ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || ++ options.inband_tags) ++ inband_tags = 1; ++ ++ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) ++ return NULL; ++ ++ /* OK, so if we got here, we have an MTD that's NAND and looks ++ * like it has the right capabilities ++ * Set the struct yaffs_dev up for mtd ++ */ ++ ++ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { ++ read_only = 1; ++ printk(KERN_INFO ++ "yaffs: mtd is read only, setting superblock read only\n" ++ ); ++ sb->s_flags |= MS_RDONLY; ++ } ++ ++ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); ++ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); ++ ++ if (!dev || !context) { ++ kfree(dev); ++ kfree(context); ++ dev = NULL; ++ context = NULL; ++ ++ /* Deep shit could not allocate device structure */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." ++ ); ++ return NULL; ++ } ++ memset(dev, 0, sizeof(struct yaffs_dev)); ++ param = &(dev->param); ++ ++ memset(context, 0, sizeof(struct yaffs_linux_context)); ++ dev->os_context = context; ++ INIT_LIST_HEAD(&(context->context_list)); ++ context->dev = dev; ++ context->super = sb; ++ ++ dev->read_only = read_only; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ sb->s_fs_info = dev; ++#else ++ sb->u.generic_sbp = dev; ++#endif ++ ++ ++ dev->driver_context = mtd; ++ param->name = mtd->name; ++ ++ /* Set up the memory size parameters.... */ ++ ++ ++ param->n_reserved_blocks = 5; ++ param->n_caches = (options.no_cache) ? 0 : 10; ++ param->inband_tags = inband_tags; ++ ++ param->enable_xattr = 1; ++ if (options.lazy_loading_overridden) ++ param->disable_lazy_load = !options.lazy_loading_enabled; ++ ++ param->defered_dir_update = 1; ++ ++ if (options.tags_ecc_overridden) ++ param->no_tags_ecc = !options.tags_ecc_on; ++ ++ param->empty_lost_n_found = 1; ++ param->refresh_period = 500; ++ param->disable_summary = options.disable_summary; ++ ++ ++#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING ++ param->disable_bad_block_marking = 1; ++#endif ++ if (options.empty_lost_and_found_overridden) ++ param->empty_lost_n_found = options.empty_lost_and_found; ++ ++ /* ... and the functions. */ ++ if (yaffs_version == 2) { ++ param->is_yaffs2 = 1; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++ param->total_bytes_per_chunk = mtd->writesize; ++ param->chunks_per_block = mtd->erasesize / mtd->writesize; ++#else ++ param->total_bytes_per_chunk = mtd->oobblock; ++ param->chunks_per_block = mtd->erasesize / mtd->oobblock; ++#endif ++ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ } else { ++ param->is_yaffs2 = 0; ++ n_blocks = YCALCBLOCKS(mtd->size, ++ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); ++ ++ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; ++ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; ++ } ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ ++ yaffs_mtd_drv_install(dev); ++ ++ param->sb_dirty_fn = yaffs_set_super_dirty; ++ param->gc_control_fn = yaffs_gc_control_callback; ++ ++ yaffs_dev_to_lc(dev)->super = sb; ++ ++ param->use_nand_ecc = 1; ++ ++ param->skip_checkpt_rd = options.skip_checkpoint_read; ++ param->skip_checkpt_wr = options.skip_checkpoint_write; ++ ++ mutex_lock(&yaffs_context_lock); ++ /* Get a mount id */ ++ found = 0; ++ for (mount_id = 0; !found; mount_id++) { ++ found = 1; ++ list_for_each(l, &yaffs_context_list) { ++ context_iterator = ++ list_entry(l, struct yaffs_linux_context, ++ context_list); ++ if (context_iterator->mount_id == mount_id) ++ found = 0; ++ } ++ } ++ context->mount_id = mount_id; ++ ++ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), ++ &yaffs_context_list); ++ mutex_unlock(&yaffs_context_lock); ++ ++ /* Directory search handling... */ ++ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); ++ param->remove_obj_fn = yaffs_remove_obj_callback; ++ ++ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ ++ yaffs_gross_lock(dev); ++ ++ err = yaffs_guts_initialise(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: guts initialised %s", ++ (err == YAFFS_OK) ? "OK" : "FAILED"); ++ ++ if (err == YAFFS_OK) ++ yaffs_bg_start(dev); ++ ++ if (!context->bg_thread) ++ param->defered_dir_update = 0; ++ ++ sb->s_maxbytes = yaffs_max_file_size(dev); ++ ++ /* Release lock before yaffs_get_inode() */ ++ yaffs_gross_unlock(dev); ++ ++ /* Create root inode */ ++ if (err == YAFFS_OK) ++ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); ++ ++ if (!inode) ++ return NULL; ++ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); ++ ++ root = yaffs_make_root(inode); ++ ++ if (!root) ++ return NULL; ++ ++ sb->s_root = root; ++ if(!dev->is_checkpointed) ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); ++ return sb; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data, struct vfsmount *mnt) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs_mount, ++#else ++ .get_sb = yaffs_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs_read_super(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, void *data, ++ struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs2_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs2", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs2_mount, ++#else ++ .get_sb = yaffs2_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs2_read_super(struct super_block *sb, ++ void *data, int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++static struct proc_dir_entry *my_proc_entry; ++ ++static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) ++{ ++ struct yaffs_param *param = &dev->param; ++ int bs[10]; ++ ++ yaffs_count_blocks_by_state(dev,bs); ++ ++ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); ++ buf += sprintf(buf, "end_block............ %d\n", param->end_block); ++ buf += sprintf(buf, "total_bytes_per_chunk %d\n", ++ param->total_bytes_per_chunk); ++ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); ++ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); ++ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); ++ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); ++ buf += sprintf(buf, "empty_lost_n_found... %d\n", ++ param->empty_lost_n_found); ++ buf += sprintf(buf, "disable_lazy_load.... %d\n", ++ param->disable_lazy_load); ++ buf += sprintf(buf, "disable_bad_block_mrk %d\n", ++ param->disable_bad_block_marking); ++ buf += sprintf(buf, "refresh_period....... %d\n", ++ param->refresh_period); ++ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); ++ buf += sprintf(buf, "n_reserved_blocks.... %d\n", ++ param->n_reserved_blocks); ++ buf += sprintf(buf, "always_check_erased.. %d\n", ++ param->always_check_erased); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "block count by state\n"); ++ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", ++ bs[0], bs[1], bs[2], bs[3], bs[4]); ++ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", ++ bs[5], bs[6], bs[7], bs[8], bs[9]); ++ ++ return buf; ++} ++ ++static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) ++{ ++ buf += sprintf(buf, "max file size....... %lld\n", ++ (long long) yaffs_max_file_size(dev)); ++ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", ++ dev->data_bytes_per_chunk); ++ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); ++ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); ++ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); ++ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", ++ dev->blocks_in_checkpt); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); ++ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); ++ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); ++ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); ++ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); ++ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); ++ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); ++ buf += sprintf(buf, "passive_gc_count..... %u\n", ++ dev->passive_gc_count); ++ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", ++ dev->oldest_dirty_gc_count); ++ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); ++ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); ++ buf += sprintf(buf, "n_retried_writes..... %u\n", ++ dev->n_retried_writes); ++ buf += sprintf(buf, "n_retired_blocks..... %u\n", ++ dev->n_retired_blocks); ++ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); ++ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); ++ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", ++ dev->n_tags_ecc_fixed); ++ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", ++ dev->n_tags_ecc_unfixed); ++ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); ++ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); ++ buf += sprintf(buf, "n_unlinked_files..... %u\n", ++ dev->n_unlinked_files); ++ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); ++ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); ++ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); ++ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); ++ ++ return buf; ++} ++ ++static int yaffs_proc_read(char *page, ++ char **start, ++ off_t offset, int count, int *eof, void *data) ++{ ++ struct list_head *item; ++ char *buf = page; ++ int step = offset; ++ int n = 0; ++ ++ /* Get proc_file_read() to step 'offset' by one on each sucessive call. ++ * We use 'offset' (*ppos) to indicate where we are in dev_list. ++ * This also assumes the user has posted a read buffer large ++ * enough to hold the complete output; but that's life in /proc. ++ */ ++ ++ *(int *)start = 1; ++ ++ /* Print header first */ ++ if (step == 0) ++ buf += ++ sprintf(buf, ++ "Multi-version YAFFS\n"); ++ else if (step == 1) ++ buf += sprintf(buf, "\n"); ++ else { ++ step -= 2; ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (n < (step & ~1)) { ++ n += 2; ++ continue; ++ } ++ if ((step & 1) == 0) { ++ buf += ++ sprintf(buf, "\nDevice %d \"%s\"\n", n, ++ dev->param.name); ++ buf = yaffs_dump_dev_part0(buf, dev); ++ } else { ++ buf = yaffs_dump_dev_part1(buf, dev); ++ } ++ ++ break; ++ } ++ mutex_unlock(&yaffs_context_lock); ++ } ++ ++ return buf - page < count ? buf - page : count; ++} ++ ++/** ++ * Set the verbosity of the warnings and error messages. ++ * ++ * Note that the names can only be a..z or _ with the current code. ++ */ ++ ++static struct { ++ char *mask_name; ++ unsigned mask_bitfield; ++} mask_flags[] = { ++ {"allocate", YAFFS_TRACE_ALLOCATE}, ++ {"always", YAFFS_TRACE_ALWAYS}, ++ {"background", YAFFS_TRACE_BACKGROUND}, ++ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, ++ {"buffers", YAFFS_TRACE_BUFFERS}, ++ {"bug", YAFFS_TRACE_BUG}, ++ {"checkpt", YAFFS_TRACE_CHECKPOINT}, ++ {"deletion", YAFFS_TRACE_DELETION}, ++ {"erase", YAFFS_TRACE_ERASE}, ++ {"error", YAFFS_TRACE_ERROR}, ++ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, ++ {"gc", YAFFS_TRACE_GC}, ++ {"lock", YAFFS_TRACE_LOCK}, ++ {"mtd", YAFFS_TRACE_MTD}, ++ {"nandaccess", YAFFS_TRACE_NANDACCESS}, ++ {"os", YAFFS_TRACE_OS}, ++ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, ++ {"scan", YAFFS_TRACE_SCAN}, ++ {"mount", YAFFS_TRACE_MOUNT}, ++ {"tracing", YAFFS_TRACE_TRACING}, ++ {"sync", YAFFS_TRACE_SYNC}, ++ {"write", YAFFS_TRACE_WRITE}, ++ {"verify", YAFFS_TRACE_VERIFY}, ++ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, ++ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, ++ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, ++ {"all", 0xffffffff}, ++ {"none", 0}, ++ {NULL, 0}, ++}; ++ ++#define MAX_MASK_NAME_LENGTH 40 ++static int yaffs_proc_write_trace_options(struct file *file, const char *buf, ++ unsigned long count) ++{ ++ unsigned rg = 0, mask_bitfield; ++ char *end; ++ char *mask_name; ++ const char *x; ++ char substring[MAX_MASK_NAME_LENGTH + 1]; ++ int i; ++ int done = 0; ++ int add, len = 0; ++ int pos = 0; ++ ++ rg = yaffs_trace_mask; ++ ++ while (!done && (pos < count)) { ++ done = 1; ++ while ((pos < count) && isspace(buf[pos])) ++ pos++; ++ ++ switch (buf[pos]) { ++ case '+': ++ case '-': ++ case '=': ++ add = buf[pos]; ++ pos++; ++ break; ++ ++ default: ++ add = ' '; ++ break; ++ } ++ mask_name = NULL; ++ ++ mask_bitfield = simple_strtoul(buf + pos, &end, 0); ++ ++ if (end > buf + pos) { ++ mask_name = "numeral"; ++ len = end - (buf + pos); ++ pos += len; ++ done = 0; ++ } else { ++ for (x = buf + pos, i = 0; ++ (*x == '_' || (*x >= 'a' && *x <= 'z')) && ++ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) ++ substring[i] = *x; ++ substring[i] = '\0'; ++ ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ if (strcmp(substring, mask_flags[i].mask_name) ++ == 0) { ++ mask_name = mask_flags[i].mask_name; ++ mask_bitfield = ++ mask_flags[i].mask_bitfield; ++ done = 0; ++ break; ++ } ++ } ++ } ++ ++ if (mask_name != NULL) { ++ done = 0; ++ switch (add) { ++ case '-': ++ rg &= ~mask_bitfield; ++ break; ++ case '+': ++ rg |= mask_bitfield; ++ break; ++ case '=': ++ rg = mask_bitfield; ++ break; ++ default: ++ rg |= mask_bitfield; ++ break; ++ } ++ } ++ } ++ ++ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; ++ ++ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); ++ ++ if (rg & YAFFS_TRACE_ALWAYS) { ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ char flag; ++ flag = ((rg & mask_flags[i].mask_bitfield) == ++ mask_flags[i].mask_bitfield) ? '+' : '-'; ++ printk(KERN_DEBUG "%c%s\n", flag, ++ mask_flags[i].mask_name); ++ } ++ } ++ ++ return count; ++} ++ ++/* Debug strings are of the form: ++ * .bnnn print info on block n ++ * .cobjn,chunkn print nand chunk id for objn:chunkn ++ */ ++ ++static int yaffs_proc_debug_write(struct file *file, const char *buf, ++ unsigned long count) ++{ ++ ++ char str[100]; ++ char *p0; ++ char *p1; ++ long p1_val; ++ long p0_val; ++ char cmd; ++ struct list_head *item; ++ ++ memset(str, 0, sizeof(str)); ++ memcpy(str, buf, min((size_t)count, sizeof(str) -1)); ++ ++ cmd = str[1]; ++ ++ p0 = str + 2; ++ ++ p1 = p0; ++ ++ while (*p1 && *p1 != ',') { ++ p1++; ++ } ++ *p1 = '\0'; ++ p1++; ++ ++ p0_val = simple_strtol(p0, NULL, 0); ++ p1_val = simple_strtol(p1, NULL, 0); ++ ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (cmd == 'b') { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev,p0_val); ++ ++ if(bi) { ++ printk("Block %d: state %d, retire %d, use %d, seq %d\n", ++ (int)p0_val, bi->block_state, ++ bi->needs_retiring, bi->pages_in_use, ++ bi->seq_number); ++ } ++ } else if (cmd == 'c') { ++ struct yaffs_obj *obj; ++ int nand_chunk; ++ ++ obj = yaffs_find_by_number(dev, p0_val); ++ if (!obj) ++ printk("No obj %d\n", (int)p0_val); ++ else { ++ if(p1_val == 0) ++ nand_chunk = obj->hdr_chunk; ++ else ++ nand_chunk = ++ yaffs_find_chunk_in_file(obj, ++ p1_val, NULL); ++ printk("Nand chunk for %d:%d is %d\n", ++ (int)p0_val, (int)p1_val, nand_chunk); ++ } ++ } ++ } ++ ++ mutex_unlock(&yaffs_context_lock); ++ ++ return count; ++} ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) ++static int yaffs_proc_write(struct file *file, const char *buf, ++ unsigned long count, void *ppos) ++#else ++static ssize_t yaffs_proc_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++#endif ++{ ++ if (buf[0] == '.') ++ return yaffs_proc_debug_write(file, buf, count); ++ return yaffs_proc_write_trace_options(file, buf, count); ++} ++ ++/* Stuff to handle installation of file systems */ ++struct file_system_to_install { ++ struct file_system_type *fst; ++ int installed; ++}; ++ ++static struct file_system_to_install fs_to_install[] = { ++ {&yaffs_fs_type, 0}, ++ {&yaffs2_fs_type, 0}, ++ {NULL, 0} ++}; ++ ++ ++#ifdef YAFFS_NEW_PROCFS ++static int yaffs_proc_show(struct seq_file *m, void *v) ++{ ++ /* FIXME: Unify in a better way? */ ++ char buffer[512]; ++ char *start; ++ int len; ++ ++ len = yaffs_proc_read(buffer, &start, 0, sizeof(buffer), NULL, NULL); ++ seq_puts(m, buffer); ++ return 0; ++} ++ ++static int yaffs_proc_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, yaffs_proc_show, NULL); ++} ++ ++static struct file_operations procfs_ops = { ++ .owner = THIS_MODULE, ++ .open = yaffs_proc_open, ++ .read = seq_read, ++ .write = yaffs_proc_write, ++}; ++ ++static int yaffs_procfs_init(void) ++{ ++ /* Install the proc_fs entries */ ++ my_proc_entry = proc_create("yaffs", ++ S_IRUGO | S_IFREG, ++ YPROC_ROOT, ++ &procfs_ops); ++ ++ if (my_proc_entry) { ++ return 0; ++ } else { ++ return -ENOMEM; ++ } ++} ++ ++#else ++ ++ ++static int yaffs_procfs_init(void) ++{ ++ /* Install the proc_fs entries */ ++ my_proc_entry = create_proc_entry("yaffs", ++ S_IRUGO | S_IFREG, YPROC_ROOT); ++ ++ if (my_proc_entry) { ++ my_proc_entry->write_proc = yaffs_proc_write; ++ my_proc_entry->read_proc = yaffs_proc_read; ++ my_proc_entry->data = NULL; ++ return 0; ++ } else { ++ return -ENOMEM; ++ } ++} ++ ++#endif ++ ++ ++static int __init init_yaffs_fs(void) ++{ ++ int error = 0; ++ struct file_system_to_install *fsinst; ++ ++ mutex_init(&yaffs_context_lock); ++ ++ error = yaffs_procfs_init(); ++ if (error) ++ return error; ++ ++ /* Now add the file system entries */ ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst && !error) { ++ error = register_filesystem(fsinst->fst); ++ if (!error) ++ fsinst->installed = 1; ++ fsinst++; ++ } ++ ++ /* Any errors? uninstall */ ++ if (error) { ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++ } ++ ++ return error; ++} ++ ++static void __exit exit_yaffs_fs(void) ++{ ++ ++ struct file_system_to_install *fsinst; ++ ++ remove_proc_entry("yaffs", YPROC_ROOT); ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++} ++ ++module_init(init_yaffs_fs) ++ module_exit(exit_yaffs_fs) ++ ++ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); ++MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.18.14/fs/yaffs2/yaffs_yaffs1.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_yaffs1.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,422 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_yaffs1.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++#include "yaffs_attribs.h" ++ ++int yaffs1_scan(struct yaffs_dev *dev) ++{ ++ struct yaffs_ext_tags tags; ++ int blk; ++ int result; ++ int chunk; ++ int c; ++ int deleted; ++ enum yaffs_block_state state; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int alloc_failed = 0; ++ struct yaffs_shadow_fixer *shadow_fixers = NULL; ++ u8 *chunk_data; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs1_scan starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, state, seq_number); ++ ++ if (state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } ++ bi++; ++ } ++ ++ /* For each block.... */ ++ for (blk = dev->internal_start_block; ++ !alloc_failed && blk <= dev->internal_end_block; blk++) { ++ ++ cond_resched(); ++ ++ bi = yaffs_get_block_info(dev, blk); ++ state = bi->block_state; ++ ++ deleted = 0; ++ ++ /* For each chunk in each block that needs scanning.... */ ++ for (c = 0; ++ !alloc_failed && c < dev->param.chunks_per_block && ++ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { ++ /* Read the tags and decide what to do */ ++ chunk = blk * dev->param.chunks_per_block + c; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, ++ &tags); ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || ++ tags.is_deleted) { ++ /* YAFFS1 only... ++ * A deleted chunk ++ */ ++ deleted++; ++ dev->n_free_chunks++; ++ } else if (!tags.chunk_used) { ++ /* An unassigned chunk in the block ++ * This means that either the block is empty or ++ * this is the one being allocated from ++ */ ++ ++ if (c == 0) { ++ /* We're looking at the first chunk in ++ *the block so the block is unused */ ++ state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ /* this is the block being allocated */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, c); ++ state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = c; ++ dev->alloc_block_finder = blk; ++ ++ } ++ ++ dev->n_free_chunks += ++ (dev->param.chunks_per_block - c); ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ unsigned int endpos; ++ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ /* PutChunkIntoFile checks for a clash ++ * (two data chunks with the same chunk_id). ++ */ ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in) { ++ if (!yaffs_put_chunk_in_file ++ (in, tags.chunk_id, chunk, 1)) ++ alloc_failed = 1; ++ } ++ ++ endpos = ++ (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk + ++ tags.n_bytes; ++ if (in && ++ in->variant_type == ++ YAFFS_OBJECT_TYPE_FILE && ++ in->variant.file_variant.scanned_size < ++ endpos) { ++ in->variant.file_variant.scanned_size = ++ endpos; ++ if (!dev->param.use_header_file_size) { ++ in->variant. ++ file_variant.file_size = ++ in->variant. ++ file_variant.scanned_size; ++ } ++ ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Make the object ++ */ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ in = yaffs_find_by_number(dev, tags.obj_id); ++ if (in && in->variant_type != oh->type) { ++ /* This should not happen, but somehow ++ * Wev'e ended up with an obj_id that ++ * has been reused but not yet deleted, ++ * and worse still it has changed type. ++ * Delete the old object. ++ */ ++ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ oh->type); ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in && oh->shadows_obj > 0) { ++ ++ struct yaffs_shadow_fixer *fixer; ++ fixer = ++ kmalloc(sizeof ++ (struct yaffs_shadow_fixer), ++ GFP_NOFS); ++ if (fixer) { ++ fixer->next = shadow_fixers; ++ shadow_fixers = fixer; ++ fixer->obj_id = tags.obj_id; ++ fixer->shadowed_id = ++ oh->shadows_obj; ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Shadow fixer: %d shadows %d", ++ fixer->obj_id, ++ fixer->shadowed_id); ++ ++ } ++ ++ } ++ ++ if (in && in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate and need to ++ * resolve it. */ ++ ++ unsigned existing_serial = in->serial; ++ unsigned new_serial = ++ tags.serial_number; ++ ++ if (((existing_serial + 1) & 3) == ++ new_serial) { ++ /* Use new one - destroy the ++ * exisiting one */ ++ yaffs_chunk_del(dev, ++ in->hdr_chunk, ++ 1, __LINE__); ++ in->valid = 0; ++ } else { ++ /* Use existing - destroy ++ * this one. */ ++ yaffs_chunk_del(dev, chunk, 1, ++ __LINE__); ++ } ++ } ++ ++ if (in && !in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == ++ YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ } else if (in && !in->valid) { ++ /* we need to load this info */ ++ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ in->dirty = 0; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ parent = ++ yaffs_find_or_create_by_number ++ (dev, oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ if (!parent) ++ alloc_failed = 1; ++ if (parent && parent->variant_type == ++ YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant. ++ children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, a problem.... ++ * We're trying to use a ++ * non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (dev->param. ++ use_header_file_size) ++ in->variant. ++ file_variant.file_size ++ = yaffs_oh_to_size(oh); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant. ++ hardlink_variant.equiv_id = ++ oh->equiv_id; ++ list_add(&in->hard_links, ++ &hard_list); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant. ++ alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant. ++ symlink_variant.alias) ++ alloc_failed = 1; ++ break; ++ } ++ } ++ } ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, ++ * then the block is fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ /* If the block was partially allocated then ++ * treat it as fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ bi->block_state = state; ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ yaffs_block_became_dirty(dev, blk); ++ } ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We should now have scanned all the objects, now it's time to add ++ * these hardlinks. ++ */ ++ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ /* ++ * Fix up any shadowed objects. ++ * There should not be more than one of these. ++ */ ++ { ++ struct yaffs_shadow_fixer *fixer; ++ struct yaffs_obj *obj; ++ ++ while (shadow_fixers) { ++ fixer = shadow_fixers; ++ shadow_fixers = fixer->next; ++ /* Complete the rename transaction by deleting the ++ * shadowed object then setting the object header ++ to unshadowed. ++ */ ++ obj = yaffs_find_by_number(dev, fixer->shadowed_id); ++ if (obj) ++ yaffs_del_obj(obj); ++ ++ obj = yaffs_find_by_number(dev, fixer->obj_id); ++ ++ if (obj) ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ kfree(fixer); ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.18.14/fs/yaffs2/yaffs_yaffs1.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_yaffs1.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS1_H__ ++#define __YAFFS_YAFFS1_H__ ++ ++#include "yaffs_guts.h" ++int yaffs1_scan(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.18.14/fs/yaffs2/yaffs_yaffs2.c +--- linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_yaffs2.c 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,1532 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_checkptrw.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_verify.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* ++ * Checkpoints are really no benefit on very small partitions. ++ * ++ * To save space on small partitions don't bother with checkpoints unless ++ * the partition is at least this big. ++ */ ++#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 ++#define YAFFS_SMALL_HOLE_THRESHOLD 4 ++ ++/* ++ * Oldest Dirty Sequence Number handling. ++ */ ++ ++/* yaffs_calc_oldest_dirty_seq() ++ * yaffs2_find_oldest_dirty_seq() ++ * Calculate the oldest dirty sequence number if we don't know it. ++ */ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ int i; ++ unsigned seq; ++ unsigned block_no = 0; ++ struct yaffs_block_info *b; ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ /* Find the oldest dirty sequence number. */ ++ seq = dev->seq_number + 1; ++ b = dev->block_info; ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ if (b->block_state == YAFFS_BLOCK_STATE_FULL && ++ (b->pages_in_use - b->soft_del_pages) < ++ dev->param.chunks_per_block && ++ b->seq_number < seq) { ++ seq = b->seq_number; ++ block_no = i; ++ } ++ b++; ++ } ++ ++ if (block_no) { ++ dev->oldest_dirty_seq = seq; ++ dev->oldest_dirty_block = block_no; ++ } ++} ++ ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->oldest_dirty_seq) ++ yaffs_calc_oldest_dirty_seq(dev); ++} ++ ++/* ++ * yaffs_clear_oldest_dirty_seq() ++ * Called when a block is erased or marked bad. (ie. when its seq_number ++ * becomes invalid). If the value matches the oldest then we clear ++ * dev->oldest_dirty_seq to force its recomputation. ++ */ ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ } ++} ++ ++/* ++ * yaffs2_update_oldest_dirty_seq() ++ * Update the oldest dirty sequence number whenever we dirty a block. ++ * Only do this if the oldest_dirty_seq is actually being tracked. ++ */ ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (dev->oldest_dirty_seq) { ++ if (dev->oldest_dirty_seq > bi->seq_number) { ++ dev->oldest_dirty_seq = bi->seq_number; ++ dev->oldest_dirty_block = block_no; ++ } ++ } ++} ++ ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return 1; /* disqualification only applies to yaffs2. */ ++ ++ if (!bi->has_shrink_hdr) ++ return 1; /* can gc */ ++ ++ yaffs2_find_oldest_dirty_seq(dev); ++ ++ /* Can't do gc of this block if there are any blocks older than this ++ * one that have discarded pages. ++ */ ++ return (bi->seq_number <= dev->oldest_dirty_seq); ++} ++ ++/* ++ * yaffs2_find_refresh_block() ++ * periodically finds the oldest full block by sequence number for refreshing. ++ * Only for yaffs2. ++ */ ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) ++{ ++ u32 b; ++ u32 oldest = 0; ++ u32 oldest_seq = 0; ++ struct yaffs_block_info *bi; ++ ++ if (!dev->param.is_yaffs2) ++ return oldest; ++ ++ /* ++ * If refresh period < 10 then refreshing is disabled. ++ */ ++ if (dev->param.refresh_period < 10) ++ return oldest; ++ ++ /* ++ * Fix broken values. ++ */ ++ if (dev->refresh_skip > dev->param.refresh_period) ++ dev->refresh_skip = dev->param.refresh_period; ++ ++ if (dev->refresh_skip > 0) ++ return oldest; ++ ++ /* ++ * Refresh skip is now zero. ++ * We'll do a refresh this time around.... ++ * Update the refresh skip and find the oldest block. ++ */ ++ dev->refresh_skip = dev->param.refresh_period; ++ dev->refresh_count++; ++ bi = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ ++ if (oldest < 1 || bi->seq_number < oldest_seq) { ++ oldest = b; ++ oldest_seq = bi->seq_number; ++ } ++ } ++ bi++; ++ } ++ ++ if (oldest > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC refresh count %d selected block %d with seq_number %d", ++ dev->refresh_count, oldest, oldest_seq); ++ } ++ ++ return oldest; ++} ++ ++int yaffs2_checkpt_required(struct yaffs_dev *dev) ++{ ++ int nblocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ nblocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ return !dev->param.skip_checkpt_wr && ++ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); ++} ++ ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) ++{ ++ int retval; ++ int n_bytes = 0; ++ int n_blocks; ++ int dev_blocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { ++ /* Not a valid value so recalculate */ ++ dev_blocks = dev->param.end_block - dev->param.start_block + 1; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(struct yaffs_checkpt_dev); ++ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); ++ n_bytes += dev_blocks * dev->chunk_bit_stride; ++ n_bytes += ++ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * ++ dev->n_obj; ++ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(u32); /* checksum */ ++ ++ /* Round up and add 2 blocks to allow for some bad blocks, ++ * so add 3 */ ++ ++ n_blocks = ++ (n_bytes / ++ (dev->data_bytes_per_chunk * ++ dev->param.chunks_per_block)) + 3; ++ ++ dev->checkpoint_blocks_required = n_blocks; ++ } ++ ++ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; ++ if (retval < 0) ++ retval = 0; ++ return retval; ++} ++ ++/*--------------------- Checkpointing --------------------*/ ++ ++static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ ++ memset(&cp, 0, sizeof(cp)); ++ ++ cp.struct_type = sizeof(cp); ++ cp.magic = YAFFS_MAGIC; ++ cp.version = YAFFS_CHECKPOINT_VERSION; ++ cp.head = (head) ? 1 : 0; ++ ++ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ if (ok) ++ ok = (cp.struct_type == sizeof(cp)) && ++ (cp.magic == YAFFS_MAGIC) && ++ (cp.version == YAFFS_CHECKPOINT_VERSION) && ++ (cp.head == ((head) ? 1 : 0)); ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, ++ struct yaffs_dev *dev) ++{ ++ cp->n_erased_blocks = dev->n_erased_blocks; ++ cp->alloc_block = dev->alloc_block; ++ cp->alloc_page = dev->alloc_page; ++ cp->n_free_chunks = dev->n_free_chunks; ++ ++ cp->n_deleted_files = dev->n_deleted_files; ++ cp->n_unlinked_files = dev->n_unlinked_files; ++ cp->n_bg_deletions = dev->n_bg_deletions; ++ cp->seq_number = dev->seq_number; ++ ++} ++ ++static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, ++ struct yaffs_checkpt_dev *cp) ++{ ++ dev->n_erased_blocks = cp->n_erased_blocks; ++ dev->alloc_block = cp->alloc_block; ++ dev->alloc_page = cp->alloc_page; ++ dev->n_free_chunks = cp->n_free_chunks; ++ ++ dev->n_deleted_files = cp->n_deleted_files; ++ dev->n_unlinked_files = cp->n_unlinked_files; ++ dev->n_bg_deletions = cp->n_bg_deletions; ++ dev->seq_number = cp->seq_number; ++} ++ ++static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ int ok; ++ ++ /* Write device runtime values */ ++ yaffs2_dev_to_checkpt_dev(&cp, dev); ++ cp.struct_type = sizeof(cp); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ /* Write block info */ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); ++ if (!ok) ++ return 0; ++ ++ /* Write chunk bits */ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = ++ (dev->internal_end_block - dev->internal_start_block + 1); ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ if (cp.struct_type != sizeof(cp)) ++ return 0; ++ ++ yaffs_checkpt_dev_to_dev(dev, &cp); ++ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); ++ ++ if (!ok) ++ return 0; ++ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, ++ struct yaffs_obj *obj) ++{ ++ cp->obj_id = obj->obj_id; ++ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; ++ cp->hdr_chunk = obj->hdr_chunk; ++ cp->variant_type = obj->variant_type; ++ cp->deleted = obj->deleted; ++ cp->soft_del = obj->soft_del; ++ cp->unlinked = obj->unlinked; ++ cp->fake = obj->fake; ++ cp->rename_allowed = obj->rename_allowed; ++ cp->unlink_allowed = obj->unlink_allowed; ++ cp->serial = obj->serial; ++ cp->n_data_chunks = obj->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; ++} ++ ++static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, ++ struct yaffs_checkpt_obj *cp) ++{ ++ struct yaffs_obj *parent; ++ ++ if (obj->variant_type != cp->variant_type) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", ++ cp->obj_id, cp->variant_type, cp->hdr_chunk, ++ obj->variant_type); ++ return 0; ++ } ++ ++ obj->obj_id = cp->obj_id; ++ ++ if (cp->parent_id) ++ parent = yaffs_find_or_create_by_number(obj->my_dev, ++ cp->parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ else ++ parent = NULL; ++ ++ if (parent) { ++ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", ++ cp->obj_id, cp->parent_id, ++ cp->variant_type, cp->hdr_chunk, ++ parent->variant_type); ++ return 0; ++ } ++ yaffs_add_obj_to_dir(parent, obj); ++ } ++ ++ obj->hdr_chunk = cp->hdr_chunk; ++ obj->variant_type = cp->variant_type; ++ obj->deleted = cp->deleted; ++ obj->soft_del = cp->soft_del; ++ obj->unlinked = cp->unlinked; ++ obj->fake = cp->fake; ++ obj->rename_allowed = cp->rename_allowed; ++ obj->unlink_allowed = cp->unlink_allowed; ++ obj->serial = cp->serial; ++ obj->n_data_chunks = cp->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; ++ ++ if (obj->hdr_chunk > 0) ++ obj->lazy_loaded = 1; ++ return 1; ++} ++ ++static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, ++ struct yaffs_tnode *tn, u32 level, ++ int chunk_offset) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ int ok = 1; ++ u32 base_offset; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { ++ if (!tn->internal[i]) ++ continue; ++ ok = yaffs2_checkpt_tnode_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) + i); ++ } ++ return ok; ++ } ++ ++ /* Level 0 tnode */ ++ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; ++ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == ++ sizeof(base_offset)); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ ++ return ok; ++} ++ ++static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 end_marker = ~0; ++ int ok = 1; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return ok; ++ ++ ok = yaffs2_checkpt_tnode_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant.file_variant. ++ top_level, 0); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, ++ sizeof(end_marker)) == sizeof(end_marker)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 base_chunk; ++ int ok = 1; ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; ++ struct yaffs_tnode *tn; ++ int nread = 0; ++ ++ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == ++ sizeof(base_chunk)); ++ ++ while (ok && (~base_chunk)) { ++ nread++; ++ /* Read level 0 tnode */ ++ ++ tn = yaffs_get_tnode(dev); ++ if (tn) ++ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ else ++ ok = 0; ++ ++ if (tn && ok) ++ ok = yaffs_add_find_tnode_0(dev, ++ file_stuct_ptr, ++ base_chunk, tn) ? 1 : 0; ++ ++ if (ok) ++ ok = (yaffs2_checkpt_rd ++ (dev, &base_chunk, ++ sizeof(base_chunk)) == sizeof(base_chunk)); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read tnodes %d records, last %d. ok %d", ++ nread, base_chunk, ok); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int i; ++ int ok = 1; ++ struct list_head *lh; ++ ++ /* Iterate through the objects in each hash entry, ++ * dumping them to the checkpointing stream. ++ */ ++ ++ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ if (!obj->defered_free) { ++ yaffs2_obj_checkpt_obj(&cp, obj); ++ cp.struct_type = sizeof(cp); ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", ++ cp.obj_id, cp.parent_id, ++ cp.variant_type, cp.hdr_chunk, obj); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, ++ sizeof(cp)) == sizeof(cp)); ++ ++ if (ok && ++ obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) ++ ok = yaffs2_wr_checkpt_tnodes(obj); ++ } ++ } ++ } ++ ++ /* Dump end of list */ ++ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); ++ cp.struct_type = sizeof(cp); ++ ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int ok = 1; ++ int done = 0; ++ LIST_HEAD(hard_list); ++ ++ ++ while (ok && !done) { ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (cp.struct_type != sizeof(cp)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "struct size %d instead of %d ok %d", ++ cp.struct_type, (int)sizeof(cp), ok); ++ ok = 0; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read object %d parent %d type %d chunk %d ", ++ cp.obj_id, cp.parent_id, cp.variant_type, ++ cp.hdr_chunk); ++ ++ if (ok && cp.obj_id == ~0) { ++ done = 1; ++ } else if (ok) { ++ obj = ++ yaffs_find_or_create_by_number(dev, cp.obj_id, ++ cp.variant_type); ++ if (obj) { ++ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); ++ if (!ok) ++ break; ++ if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) { ++ ok = yaffs2_rd_checkpt_tnodes(obj); ++ } else if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_HARDLINK) { ++ list_add(&obj->hard_links, &hard_list); ++ } ++ } else { ++ ok = 0; ++ } ++ } ++ } ++ ++ if (ok) ++ yaffs_link_fixup(dev, &hard_list); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum); ++ ++ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == ++ sizeof(checkpt_sum)); ++ ++ if (!ok) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum0; ++ u32 checkpt_sum1; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); ++ ++ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == ++ sizeof(checkpt_sum1)); ++ ++ if (!ok) ++ return 0; ++ ++ if (checkpt_sum0 != checkpt_sum1) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!yaffs2_checkpt_required(dev)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint write"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 1); ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint device"); ++ ok = yaffs2_wr_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint objects"); ++ ok = yaffs2_wr_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) ++ ok = yaffs2_wr_checkpt_sum(dev); ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return dev->is_checkpointed; ++} ++ ++static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!dev->param.is_yaffs2) ++ ok = 0; ++ ++ if (ok && dev->param.skip_checkpt_rd) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint read"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint device"); ++ ok = yaffs2_rd_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint objects"); ++ ok = yaffs2_rd_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) { ++ ok = yaffs2_rd_checkpt_sum(dev); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint checksum %d", ok); ++ } ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return ok ? 1 : 0; ++} ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) ++{ ++ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { ++ dev->is_checkpointed = 0; ++ yaffs2_checkpt_invalidate_stream(dev); ++ } ++ if (dev->param.sb_dirty_fn) ++ dev->param.sb_dirty_fn(dev); ++} ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "save entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ ++ if (!dev->is_checkpointed) { ++ yaffs2_checkpt_invalidate(dev); ++ yaffs2_wr_checkpt_data(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, ++ "save exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return dev->is_checkpointed; ++} ++ ++int yaffs2_checkpt_restore(struct yaffs_dev *dev) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ retval = yaffs2_rd_checkpt_data(dev); ++ ++ if (dev->is_checkpointed) { ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return retval; ++} ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) ++{ ++ /* if new_size > old_file_size. ++ * We're going to be writing a hole. ++ * If the hole is small then write zeros otherwise write a start ++ * of hole marker. ++ */ ++ loff_t old_file_size; ++ loff_t increase; ++ int small_hole; ++ int result = YAFFS_OK; ++ struct yaffs_dev *dev = NULL; ++ u8 *local_buffer = NULL; ++ int small_increase_ok = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ dev = obj->my_dev; ++ ++ /* Bail out if not yaffs2 mode */ ++ if (!dev->param.is_yaffs2) ++ return YAFFS_OK; ++ ++ old_file_size = obj->variant.file_variant.file_size; ++ ++ if (new_size <= old_file_size) ++ return YAFFS_OK; ++ ++ increase = new_size - old_file_size; ++ ++ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && ++ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) ++ small_hole = 1; ++ else ++ small_hole = 0; ++ ++ if (small_hole) ++ local_buffer = yaffs_get_temp_buffer(dev); ++ ++ if (local_buffer) { ++ /* fill hole with zero bytes */ ++ loff_t pos = old_file_size; ++ int this_write; ++ int written; ++ memset(local_buffer, 0, dev->data_bytes_per_chunk); ++ small_increase_ok = 1; ++ ++ while (increase > 0 && small_increase_ok) { ++ this_write = increase; ++ if (this_write > dev->data_bytes_per_chunk) ++ this_write = dev->data_bytes_per_chunk; ++ written = ++ yaffs_do_file_wr(obj, local_buffer, pos, this_write, ++ 0); ++ if (written == this_write) { ++ pos += this_write; ++ increase -= this_write; ++ } else { ++ small_increase_ok = 0; ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ ++ /* If out of space then reverse any chunks we've added */ ++ if (!small_increase_ok) ++ yaffs_resize_file_down(obj, old_file_size); ++ } ++ ++ if (!small_increase_ok && ++ obj->parent && ++ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { ++ /* Write a hole start header with the old file size */ ++ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); ++ } ++ ++ return result; ++} ++ ++struct yaffs_block_index { ++ int seq; ++ int block; ++}; ++ ++static int yaffs2_ybicmp(const void *a, const void *b) ++{ ++ int aseq = ((struct yaffs_block_index *)a)->seq; ++ int bseq = ((struct yaffs_block_index *)b)->seq; ++ int ablock = ((struct yaffs_block_index *)a)->block; ++ int bblock = ((struct yaffs_block_index *)b)->block; ++ ++ if (aseq == bseq) ++ return ablock - bblock; ++ ++ return aseq - bseq; ++} ++ ++static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int blk, int chunk_in_block, ++ int *found_chunks, ++ u8 *chunk_data, ++ struct list_head *hard_list, ++ int summary_available) ++{ ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int equiv_id; ++ loff_t file_size; ++ int is_shrink; ++ int is_unlinked; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; ++ struct yaffs_file_var *file_var; ++ struct yaffs_hardlink_var *hl_var; ++ struct yaffs_symlink_var *sl_var; ++ ++ if (summary_available) { ++ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); ++ tags.seq_number = bi->seq_number; ++ } ++ ++ if (!summary_available || tags.obj_id == 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); ++ dev->tags_used++; ++ } else { ++ dev->summary_used++; ++ } ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (!tags.chunk_used) { ++ /* An unassigned chunk in the block. ++ * If there are used chunks after this one, then ++ * it is a chunk that was skipped due to failing ++ * the erased check. Just skip it so that it can ++ * be deleted. ++ * But, more typically, We get here when this is ++ * an unallocated chunk and his means that ++ * either the block is empty or this is the one ++ * being allocated from ++ */ ++ ++ if (*found_chunks) { ++ /* This is a chunk that was skipped due ++ * to failing the erased check */ ++ } else if (chunk_in_block == 0) { ++ /* We're looking at the first chunk in ++ * the block so the block is unused */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ if (dev->seq_number == bi->seq_number) { ++ /* Allocating from this block*/ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, chunk_in_block); ++ ++ bi->block_state = ++ YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = chunk_in_block; ++ dev->alloc_block_finder = blk; ++ } else { ++ /* This is a partially written block ++ * that is not the current ++ * allocation block. ++ */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Partially written block %d detected. gc will fix this.", ++ blk); ++ } ++ } ++ } ++ ++ dev->n_free_chunks++; ++ ++ } else if (tags.ecc_result == ++ YAFFS_ECC_RESULT_UNFIXED) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Unfixed ECC in chunk(%d:%d), chunk ignored", ++ blk, chunk_in_block); ++ dev->n_free_chunks++; ++ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || ++ tags.chunk_id > YAFFS_MAX_CHUNK_ID || ++ tags.obj_id == YAFFS_OBJECTID_SUMMARY || ++ (tags.chunk_id > 0 && ++ tags.n_bytes > dev->data_bytes_per_chunk) || ++ tags.seq_number != bi->seq_number) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", ++ blk, chunk_in_block, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ dev->n_free_chunks++; ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ loff_t endpos; ++ loff_t chunk_base = (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk; ++ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ if (!in) ++ /* Out of memory */ ++ alloc_failed = 1; ++ ++ if (in && ++ in->variant_type == YAFFS_OBJECT_TYPE_FILE && ++ chunk_base < in->variant.file_variant.shrink_size) { ++ /* This has not been invalidated by ++ * a resize */ ++ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, ++ chunk, -1)) ++ alloc_failed = 1; ++ ++ /* File size is calculated by looking at ++ * the data chunks if we have not ++ * seen an object header yet. ++ * Stop this practice once we find an ++ * object header. ++ */ ++ endpos = chunk_base + tags.n_bytes; ++ ++ if (!in->valid && ++ in->variant.file_variant.scanned_size < endpos) { ++ in->variant.file_variant. ++ scanned_size = endpos; ++ in->variant.file_variant. ++ file_size = endpos; ++ } ++ } else if (in) { ++ /* This chunk has been invalidated by a ++ * resize, or a past file deletion ++ * so delete the chunk*/ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Thus, we read in the object header and make ++ * the object ++ */ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ oh = NULL; ++ in = NULL; ++ ++ if (tags.extra_available) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ tags.extra_obj_type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ ++ if (!in || ++ (!in->valid && dev->param.disable_lazy_load) || ++ tags.extra_shadows || ++ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { ++ ++ /* If we don't have valid info then we ++ * need to read the chunk ++ * TODO In future we can probably defer ++ * reading the chunk and living with ++ * invalid data until needed. ++ */ ++ ++ result = yaffs_rd_chunk_tags_nand(dev, ++ chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ if (dev->param.inband_tags) { ++ /* Fix up the header if they got ++ * corrupted by inband tags */ ++ oh->shadows_obj = ++ oh->inband_shadowed_obj_id; ++ oh->is_shrink = ++ oh->inband_is_shrink; ++ } ++ ++ if (!in) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, oh->type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ } ++ ++ if (!in) { ++ /* TODO Hoosterman we have a problem! */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", ++ tags.obj_id, chunk); ++ return YAFFS_FAIL; ++ } ++ ++ if (in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate that will be ++ * discarded, but we first have to suck ++ * out resize info if it is a file. ++ */ ++ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && ++ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || ++ (tags.extra_available && ++ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ )) { ++ loff_t this_size = (oh) ? ++ yaffs_oh_to_size(oh) : ++ tags.extra_file_size; ++ u32 parent_obj_id = (oh) ? ++ oh->parent_obj_id : ++ tags.extra_parent_id; ++ ++ is_shrink = (oh) ? ++ oh->is_shrink : ++ tags.extra_is_shrink; ++ ++ /* If it is deleted (unlinked ++ * at start also means deleted) ++ * we treat the file size as ++ * being zeroed at this point. ++ */ ++ if (parent_obj_id == YAFFS_OBJECTID_DELETED || ++ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { ++ this_size = 0; ++ is_shrink = 1; ++ } ++ ++ if (is_shrink && ++ in->variant.file_variant.shrink_size > ++ this_size) ++ in->variant.file_variant.shrink_size = ++ this_size; ++ ++ if (is_shrink) ++ bi->has_shrink_hdr = 1; ++ } ++ /* Use existing - destroy this one. */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ ++ if (!in->valid && in->variant_type != ++ (oh ? oh->type : tags.extra_obj_type)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", ++ oh ? oh->type : tags.extra_obj_type, ++ in->variant_type, tags.obj_id, ++ chunk); ++ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); ++ } ++ ++ if (!in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ ++ if (oh) { ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->lazy_loaded = 0; ++ } else { ++ in->lazy_loaded = 1; ++ } ++ in->hdr_chunk = chunk; ++ ++ } else if (!in->valid) { ++ /* we need to load this info */ ++ in->valid = 1; ++ in->hdr_chunk = chunk; ++ if (oh) { ++ in->variant_type = oh->type; ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ ++ if (oh->shadows_obj > 0) ++ yaffs_handle_shadowed_obj(dev, ++ oh->shadows_obj, 1); ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ parent = yaffs_find_or_create_by_number(dev, ++ oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = yaffs_oh_to_size(oh); ++ is_shrink = oh->is_shrink; ++ equiv_id = oh->equiv_id; ++ } else { ++ in->variant_type = tags.extra_obj_type; ++ parent = yaffs_find_or_create_by_number(dev, ++ tags.extra_parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = tags.extra_file_size; ++ is_shrink = tags.extra_is_shrink; ++ equiv_id = tags.extra_equiv_id; ++ in->lazy_loaded = 1; ++ } ++ in->dirty = 0; ++ ++ if (!parent) ++ alloc_failed = 1; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ if (parent && ++ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant.children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, another problem.... ++ * Trying to use a non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ yaffs_add_obj_to_dir(parent, in); ++ ++ is_unlinked = (parent == dev->del_dir) || ++ (parent == dev->unlinked_dir); ++ ++ if (is_shrink) ++ /* Mark the block */ ++ bi->has_shrink_hdr = 1; ++ ++ /* Note re hardlinks. ++ * Since we might scan a hardlink before its equivalent ++ * object is scanned we put them all in a list. ++ * After scanning is complete, we should have all the ++ * objects, so we run through this list and fix up all ++ * the chains. ++ */ ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ file_var = &in->variant.file_variant; ++ if (file_var->scanned_size < file_size) { ++ /* This covers the case where the file ++ * size is greater than the data held. ++ * This will happen if the file is ++ * resized to be larger than its ++ * current data extents. ++ */ ++ file_var->file_size = file_size; ++ file_var->scanned_size = file_size; ++ } ++ ++ if (file_var->shrink_size > file_size) ++ file_var->shrink_size = file_size; ++ ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ hl_var = &in->variant.hardlink_variant; ++ if (!is_unlinked) { ++ hl_var->equiv_id = equiv_id; ++ list_add(&in->hard_links, hard_list); ++ } ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ sl_var = &in->variant.symlink_variant; ++ if (oh) { ++ sl_var->alias = ++ yaffs_clone_str(oh->alias); ++ if (!sl_var->alias) ++ alloc_failed = 1; ++ } ++ break; ++ } ++ } ++ } ++ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++int yaffs2_scan_backwards(struct yaffs_dev *dev) ++{ ++ int blk; ++ int block_iter; ++ int start_iter; ++ int end_iter; ++ int n_to_scan = 0; ++ enum yaffs_block_state state; ++ int c; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ u8 *chunk_data; ++ int found_chunks; ++ int alloc_failed = 0; ++ struct yaffs_block_index *block_index = NULL; ++ int alt_block_index = 0; ++ int summary_available; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ block_index = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); ++ ++ if (!block_index) { ++ block_index = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); ++ alt_block_index = 1; ++ } ++ ++ if (!block_index) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards() could not allocate block index!" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, bi->block_state, seq_number); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ dev->blocks_in_checkpt++; ++ ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } else if (bi->block_state == ++ YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* Determine the highest sequence number */ ++ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && ++ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { ++ block_index[n_to_scan].seq = seq_number; ++ block_index[n_to_scan].block = blk; ++ n_to_scan++; ++ if (seq_number >= dev->seq_number) ++ dev->seq_number = seq_number; ++ } else { ++ /* TODO: Nasty sequence number! */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Block scanning block %d has bad sequence number %d", ++ blk, seq_number); ++ } ++ } ++ bi++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); ++ ++ cond_resched(); ++ ++ /* Sort the blocks by sequence number */ ++ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), ++ yaffs2_ybicmp, NULL); ++ ++ cond_resched(); ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); ++ ++ /* Now scan the blocks looking at the data. */ ++ start_iter = 0; ++ end_iter = n_to_scan - 1; ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); ++ ++ /* For each block.... backwards */ ++ for (block_iter = end_iter; ++ !alloc_failed && block_iter >= start_iter; ++ block_iter--) { ++ /* Cooperative multitasking! This loop can run for so ++ long that watchdog timers expire. */ ++ cond_resched(); ++ ++ /* get the block to scan in the correct order */ ++ blk = block_index[block_iter].block; ++ bi = yaffs_get_block_info(dev, blk); ++ ++ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); ++ ++ /* For each chunk in each block that needs scanning.... */ ++ found_chunks = 0; ++ if (summary_available) ++ c = dev->chunks_per_summary - 1; ++ else ++ c = dev->param.chunks_per_block - 1; ++ ++ for (/* c is already initialised */; ++ !alloc_failed && c >= 0 && ++ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); ++ c--) { ++ /* Scan backwards... ++ * Read the tags and decide what to do ++ */ ++ if (yaffs2_scan_chunk(dev, bi, blk, c, ++ &found_chunks, chunk_data, ++ &hard_list, summary_available) == ++ YAFFS_FAIL) ++ alloc_failed = 1; ++ } ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, then the block ++ * is fully allocated. */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ yaffs_block_became_dirty(dev, blk); ++ } ++ } ++ ++ yaffs_skip_rest_of_block(dev); ++ ++ if (alt_block_index) ++ vfree(block_index); ++ else ++ kfree(block_index); ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We have scanned all the objects, now it's time to add these ++ * hardlinks. ++ */ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.18.14/fs/yaffs2/yaffs_yaffs2.h +--- linux-3.18.14.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yaffs_yaffs2.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS2_H__ ++#define __YAFFS_YAFFS2_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi); ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); ++int yaffs2_checkpt_required(struct yaffs_dev *dev); ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); ++int yaffs2_checkpt_save(struct yaffs_dev *dev); ++int yaffs2_checkpt_restore(struct yaffs_dev *dev); ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); ++int yaffs2_scan_backwards(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.18.14.orig/fs/yaffs2/yportenv.h linux-3.18.14/fs/yaffs2/yportenv.h +--- linux-3.18.14.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.14/fs/yaffs2/yportenv.h 2015-06-14 21:23:22.000000000 +0200 +@@ -0,0 +1,85 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YPORTENV_H__ ++#define __YPORTENV_H__ ++ ++/* ++ * Define the MTD version in terms of Linux Kernel versions ++ * This allows yaffs to be used independantly of the kernel ++ * as well as with it. ++ */ ++ ++#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) ++ ++#ifdef YAFFS_OUT_OF_TREE ++#include "moduleconfig.h" ++#endif ++ ++#include ++#define MTD_VERSION_CODE LINUX_VERSION_CODE ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* These type wrappings are used to support Unicode names in WinCE. */ ++#define YCHAR char ++#define YUCHAR unsigned char ++#define _Y(x) x ++ ++#define YAFFS_LOSTNFOUND_NAME "lost+found" ++#define YAFFS_LOSTNFOUND_PREFIX "obj" ++ ++ ++#define YAFFS_ROOT_MODE 0755 ++#define YAFFS_LOSTNFOUND_MODE 0700 ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define Y_CURRENT_TIME CURRENT_TIME.tv_sec ++#define Y_TIME_CONVERT(x) (x).tv_sec ++#else ++#define Y_CURRENT_TIME CURRENT_TIME ++#define Y_TIME_CONVERT(x) (x) ++#endif ++ ++#define compile_time_assertion(assertion) \ ++ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) ++ ++ ++#define yaffs_printf(msk, fmt, ...) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) ++ ++#define yaffs_trace(msk, fmt, ...) do { \ ++ if (yaffs_trace_mask & (msk)) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ ++} while (0) ++ ++ ++#endif diff --git a/target/linux/patches/3.2.68/sparc-aout.patch b/target/linux/patches/3.2.68/sparc-aout.patch deleted file mode 100644 index 6ad252479..000000000 --- a/target/linux/patches/3.2.68/sparc-aout.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff -Nur linux-3.2.66.orig/arch/sparc/boot/Makefile linux-3.2.66/arch/sparc/boot/Makefile ---- linux-3.2.66.orig/arch/sparc/boot/Makefile 2014-12-31 19:27:54.000000000 -0600 -+++ linux-3.2.66/arch/sparc/boot/Makefile 2015-02-17 13:21:38.269400789 -0600 -@@ -75,9 +75,6 @@ - $(call if_changed,gzip) - @echo ' kernel: $@ is ready' - --$(obj)/vmlinux.aout: vmlinux FORCE -- $(call if_changed,elftoaout) -- @echo ' kernel: $@ is ready' - else - - # The following lines make a readable image for U-Boot. -@@ -110,6 +107,10 @@ - - endif - -+$(obj)/vmlinux.aout: $(obj)/image FORCE -+ $(call if_changed,elftoaout) -+ @echo ' kernel: $@ is ready' -+ - $(obj)/tftpboot.img: $(obj)/image $(obj)/piggyback System.map $(ROOT_IMG) FORCE - $(call if_changed,elftoaout) - $(call if_changed,piggy) diff --git a/target/linux/patches/3.2.69/sparc-aout.patch b/target/linux/patches/3.2.69/sparc-aout.patch new file mode 100644 index 000000000..6ad252479 --- /dev/null +++ b/target/linux/patches/3.2.69/sparc-aout.patch @@ -0,0 +1,24 @@ +diff -Nur linux-3.2.66.orig/arch/sparc/boot/Makefile linux-3.2.66/arch/sparc/boot/Makefile +--- linux-3.2.66.orig/arch/sparc/boot/Makefile 2014-12-31 19:27:54.000000000 -0600 ++++ linux-3.2.66/arch/sparc/boot/Makefile 2015-02-17 13:21:38.269400789 -0600 +@@ -75,9 +75,6 @@ + $(call if_changed,gzip) + @echo ' kernel: $@ is ready' + +-$(obj)/vmlinux.aout: vmlinux FORCE +- $(call if_changed,elftoaout) +- @echo ' kernel: $@ is ready' + else + + # The following lines make a readable image for U-Boot. +@@ -110,6 +107,10 @@ + + endif + ++$(obj)/vmlinux.aout: $(obj)/image FORCE ++ $(call if_changed,elftoaout) ++ @echo ' kernel: $@ is ready' ++ + $(obj)/tftpboot.img: $(obj)/image $(obj)/piggyback System.map $(ROOT_IMG) FORCE + $(call if_changed,elftoaout) + $(call if_changed,piggy) diff --git a/target/linux/patches/3.4.107/aufs2.patch b/target/linux/patches/3.4.107/aufs2.patch deleted file mode 100644 index d40c9a3fe..000000000 --- a/target/linux/patches/3.4.107/aufs2.patch +++ /dev/null @@ -1,238 +0,0 @@ -diff -Nur linux-3.1.4.orig/include/linux/Kbuild linux-3.1.4/include/linux/Kbuild ---- linux-3.1.4.orig/include/linux/Kbuild 2011-11-28 23:48:14.000000000 +0100 -+++ linux-3.1.4/include/linux/Kbuild 2011-12-01 12:44:17.000000000 +0100 -@@ -65,6 +65,7 @@ - header-y += atmsap.h - header-y += atmsvc.h - header-y += audit.h -+header-y += aufs_type.h - header-y += auto_fs.h - header-y += auto_fs4.h - header-y += auxvec.h -diff -Nur linux-3.1.4.orig/include/linux/aufs_type.h linux-3.1.4/include/linux/aufs_type.h ---- linux-3.1.4.orig/include/linux/aufs_type.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.1.4/include/linux/aufs_type.h 2011-12-01 12:44:17.000000000 +0100 -@@ -0,0 +1,197 @@ -+/* -+ * Copyright (C) 2005-2011 Junjiro R. Okajima -+ * -+ * This program, aufs is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -+ */ -+ -+#ifndef __AUFS_TYPE_H__ -+#define __AUFS_TYPE_H__ -+ -+#include -+#include -+#include -+#include -+ -+#define AUFS_VERSION "2.1-standalone.tree-36-20110110" -+ -+/* todo? move this to linux-2.6.19/include/magic.h */ -+#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_BRANCH_MAX_127 -+typedef __s8 aufs_bindex_t; -+#define AUFS_BRANCH_MAX 127 -+#else -+typedef __s16 aufs_bindex_t; -+#ifdef CONFIG_AUFS_BRANCH_MAX_511 -+#define AUFS_BRANCH_MAX 511 -+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) -+#define AUFS_BRANCH_MAX 1023 -+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) -+#define AUFS_BRANCH_MAX 32767 -+#endif -+#endif -+ -+#ifdef __KERNEL__ -+#ifndef AUFS_BRANCH_MAX -+#error unknown CONFIG_AUFS_BRANCH_MAX value -+#endif -+#endif /* __KERNEL__ */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AUFS_NAME "aufs" -+#define AUFS_FSTYPE AUFS_NAME -+ -+#define AUFS_ROOT_INO 2 -+#define AUFS_FIRST_INO 11 -+ -+#define AUFS_WH_PFX ".wh." -+#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) -+#define AUFS_WH_TMP_LEN 4 -+/* a limit for rmdir/rename a dir */ -+#define AUFS_MAX_NAMELEN (NAME_MAX \ -+ - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ -+ - 1 /* dot */\ -+ - AUFS_WH_TMP_LEN) /* hex */ -+#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" -+#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME -+#define AUFS_XINO_TRUNC_INIT 64 /* blocks */ -+#define AUFS_XINO_TRUNC_STEP 4 /* blocks */ -+#define AUFS_DIRWH_DEF 3 -+#define AUFS_RDCACHE_DEF 10 /* seconds */ -+#define AUFS_RDCACHE_MAX 3600 /* seconds */ -+#define AUFS_RDBLK_DEF 512 /* bytes */ -+#define AUFS_RDHASH_DEF 32 -+#define AUFS_WKQ_NAME AUFS_NAME "d" -+#define AUFS_WKQ_PRE_NAME AUFS_WKQ_NAME "_pre" -+#define AUFS_MFS_DEF_SEC 30 /* seconds */ -+#define AUFS_MFS_MAX_SEC 3600 /* seconds */ -+#define AUFS_PLINK_WARN 100 /* number of plinks */ -+ -+/* pseudo-link maintenace under /proc */ -+#define AUFS_PLINK_MAINT_NAME "plink_maint" -+#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME -+#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME -+ -+#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ -+#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME -+ -+#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME -+#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" -+#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" -+ -+/* doubly whiteouted */ -+#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME -+#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME -+#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME -+ -+/* branch permission */ -+#define AUFS_BRPERM_RW "rw" -+#define AUFS_BRPERM_RO "ro" -+#define AUFS_BRPERM_RR "rr" -+#define AUFS_BRPERM_WH "wh" -+#define AUFS_BRPERM_NLWH "nolwh" -+#define AUFS_BRPERM_ROWH AUFS_BRPERM_RO "+" AUFS_BRPERM_WH -+#define AUFS_BRPERM_RRWH AUFS_BRPERM_RR "+" AUFS_BRPERM_WH -+#define AUFS_BRPERM_RWNLWH AUFS_BRPERM_RW "+" AUFS_BRPERM_NLWH -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* ioctl */ -+enum { -+ /* readdir in userspace */ -+ AuCtl_RDU, -+ AuCtl_RDU_INO, -+ -+ /* pathconf wrapper */ -+ AuCtl_WBR_FD -+}; -+ -+/* borrowed from linux/include/linux/kernel.h */ -+#ifndef ALIGN -+#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) -+#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) -+#endif -+ -+/* borrowed from linux/include/linux/compiler-gcc3.h */ -+#ifndef __aligned -+#define __aligned(x) __attribute__((aligned(x))) -+#define __packed __attribute__((packed)) -+#endif -+ -+struct au_rdu_cookie { -+ __u64 h_pos; -+ __s16 bindex; -+ __u8 flags; -+ __u8 pad; -+ __u32 generation; -+} __aligned(8); -+ -+struct au_rdu_ent { -+ __u64 ino; -+ __s16 bindex; -+ __u8 type; -+ __u8 nlen; -+ __u8 wh; -+ char name[0]; -+} __aligned(8); -+ -+static inline int au_rdu_len(int nlen) -+{ -+ /* include the terminating NULL */ -+ return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, -+ sizeof(__u64)); -+} -+ -+union au_rdu_ent_ul { -+ struct au_rdu_ent __user *e; -+ __u64 ul; -+}; -+ -+enum { -+ AufsCtlRduV_SZ, -+ AufsCtlRduV_End -+}; -+ -+struct aufs_rdu { -+ /* input */ -+ union { -+ __u64 sz; /* AuCtl_RDU */ -+ __u64 nent; /* AuCtl_RDU_INO */ -+ }; -+ union au_rdu_ent_ul ent; -+ __u16 verify[AufsCtlRduV_End]; -+ -+ /* input/output */ -+ __u32 blk; -+ -+ /* output */ -+ union au_rdu_ent_ul tail; -+ /* number of entries which were added in a single call */ -+ __u64 rent; -+ __u8 full; -+ __u8 shwh; -+ -+ struct au_rdu_cookie cookie; -+} __aligned(8); -+ -+#define AuCtlType 'A' -+#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) -+#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) -+#define AUFS_CTL_WBR_FD _IO(AuCtlType, AuCtl_WBR_FD) -+ -+#endif /* __AUFS_TYPE_H__ */ -diff -Nur linux-3.1.4.orig/include/linux/namei.h linux-3.1.4/include/linux/namei.h ---- linux-3.1.4.orig/include/linux/namei.h 2011-11-28 23:48:14.000000000 +0100 -+++ linux-3.1.4/include/linux/namei.h 2011-12-01 12:44:17.000000000 +0100 -@@ -85,6 +85,9 @@ - extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, - int (*open)(struct inode *, struct file *)); - -+extern struct dentry *lookup_hash(struct nameidata *nd); -+extern int __lookup_one_len(const char *name, struct qstr *this, -+ struct dentry *base, int len); - extern struct dentry *lookup_one_len(const char *, struct dentry *, int); - - extern int follow_down_one(struct path *); -diff -Nur linux-3.1.4.orig/include/linux/splice.h linux-3.1.4/include/linux/splice.h ---- linux-3.1.4.orig/include/linux/splice.h 2011-11-28 23:48:14.000000000 +0100 -+++ linux-3.1.4/include/linux/splice.h 2011-12-01 12:45:51.000000000 +0100 -@@ -91,4 +91,9 @@ - extern void spd_release_page(struct splice_pipe_desc *, unsigned int); - - extern const struct pipe_buf_operations page_cache_pipe_buf_ops; -+extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags); -+extern long do_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags); - #endif diff --git a/target/linux/patches/3.4.107/bsd-compatibility.patch b/target/linux/patches/3.4.107/bsd-compatibility.patch deleted file mode 100644 index 9e91a62de..000000000 --- a/target/linux/patches/3.4.107/bsd-compatibility.patch +++ /dev/null @@ -1,2512 +0,0 @@ -diff -Nur linux-2.6.36.orig/scripts/Makefile.lib linux-2.6.36/scripts/Makefile.lib ---- linux-2.6.36.orig/scripts/Makefile.lib 2010-10-20 22:30:22.000000000 +0200 -+++ linux-2.6.36/scripts/Makefile.lib 2010-11-28 18:34:22.000000000 +0100 -@@ -216,7 +216,12 @@ - size_append = printf $(shell \ - dec_size=0; \ - for F in $1; do \ -- fsize=$$(stat -c "%s" $$F); \ -+ if stat -qs .>/dev/null 2>&1; then \ -+ statcmd='stat -f %z'; \ -+ else \ -+ statcmd='stat -c %s'; \ -+ fi; \ -+ fsize=$$($$statcmd $$F); \ - dec_size=$$(expr $$dec_size + $$fsize); \ - done; \ - printf "%08x\n" $$dec_size | \ -diff -Nur linux-2.6.36.orig/scripts/mod/mk_elfconfig.c linux-2.6.36/scripts/mod/mk_elfconfig.c ---- linux-2.6.36.orig/scripts/mod/mk_elfconfig.c 2010-10-20 22:30:22.000000000 +0200 -+++ linux-2.6.36/scripts/mod/mk_elfconfig.c 2010-11-28 18:33:24.000000000 +0100 -@@ -1,7 +1,18 @@ - #include - #include - #include --#include -+ -+#define EI_NIDENT (16) -+#define ELFMAG "\177ELF" -+ -+#define SELFMAG 4 -+#define EI_CLASS 4 -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ - - int - main(int argc, char **argv) -diff -Nur linux-2.6.36.orig/scripts/mod/modpost.h linux-2.6.36/scripts/mod/modpost.h ---- linux-2.6.36.orig/scripts/mod/modpost.h 2010-10-20 22:30:22.000000000 +0200 -+++ linux-2.6.36/scripts/mod/modpost.h 2010-11-28 18:33:24.000000000 +0100 -@@ -7,7 +7,2453 @@ - #include - #include - #include --#include -+ -+ -+/* This file defines standard ELF types, structures, and macros. -+ Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. -+ This file is part of the GNU C Library. -+ -+ The GNU C Library is free software; you can redistribute it and/or -+ modify it under the terms of the GNU Lesser General Public -+ License as published by the Free Software Foundation; either -+ version 2.1 of the License, or (at your option) any later version. -+ -+ The GNU C Library is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ Lesser General Public License for more details. -+ -+ You should have received a copy of the GNU Lesser General Public -+ License along with the GNU C Library; if not, write to the Free -+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -+ 02111-1307 USA. */ -+ -+#ifndef _ELF_H -+#define _ELF_H 1 -+ -+__BEGIN_DECLS -+ -+/* Standard ELF types. */ -+ -+#include -+ -+/* Type for a 16-bit quantity. */ -+typedef uint16_t Elf32_Half; -+typedef uint16_t Elf64_Half; -+ -+/* Types for signed and unsigned 32-bit quantities. */ -+typedef uint32_t Elf32_Word; -+typedef int32_t Elf32_Sword; -+typedef uint32_t Elf64_Word; -+typedef int32_t Elf64_Sword; -+ -+/* Types for signed and unsigned 64-bit quantities. */ -+typedef uint64_t Elf32_Xword; -+typedef int64_t Elf32_Sxword; -+typedef uint64_t Elf64_Xword; -+typedef int64_t Elf64_Sxword; -+ -+/* Type of addresses. */ -+typedef uint32_t Elf32_Addr; -+typedef uint64_t Elf64_Addr; -+ -+/* Type of file offsets. */ -+typedef uint32_t Elf32_Off; -+typedef uint64_t Elf64_Off; -+ -+/* Type for section indices, which are 16-bit quantities. */ -+typedef uint16_t Elf32_Section; -+typedef uint16_t Elf64_Section; -+ -+/* Type for version symbol information. */ -+typedef Elf32_Half Elf32_Versym; -+typedef Elf64_Half Elf64_Versym; -+ -+ -+/* The ELF file header. This appears at the start of every ELF file. */ -+ -+#define EI_NIDENT (16) -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf32_Half e_type; /* Object file type */ -+ Elf32_Half e_machine; /* Architecture */ -+ Elf32_Word e_version; /* Object file version */ -+ Elf32_Addr e_entry; /* Entry point virtual address */ -+ Elf32_Off e_phoff; /* Program header table file offset */ -+ Elf32_Off e_shoff; /* Section header table file offset */ -+ Elf32_Word e_flags; /* Processor-specific flags */ -+ Elf32_Half e_ehsize; /* ELF header size in bytes */ -+ Elf32_Half e_phentsize; /* Program header table entry size */ -+ Elf32_Half e_phnum; /* Program header table entry count */ -+ Elf32_Half e_shentsize; /* Section header table entry size */ -+ Elf32_Half e_shnum; /* Section header table entry count */ -+ Elf32_Half e_shstrndx; /* Section header string table index */ -+} Elf32_Ehdr; -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf64_Half e_type; /* Object file type */ -+ Elf64_Half e_machine; /* Architecture */ -+ Elf64_Word e_version; /* Object file version */ -+ Elf64_Addr e_entry; /* Entry point virtual address */ -+ Elf64_Off e_phoff; /* Program header table file offset */ -+ Elf64_Off e_shoff; /* Section header table file offset */ -+ Elf64_Word e_flags; /* Processor-specific flags */ -+ Elf64_Half e_ehsize; /* ELF header size in bytes */ -+ Elf64_Half e_phentsize; /* Program header table entry size */ -+ Elf64_Half e_phnum; /* Program header table entry count */ -+ Elf64_Half e_shentsize; /* Section header table entry size */ -+ Elf64_Half e_shnum; /* Section header table entry count */ -+ Elf64_Half e_shstrndx; /* Section header string table index */ -+} Elf64_Ehdr; -+ -+/* Fields in the e_ident array. The EI_* macros are indices into the -+ array. The macros under each EI_* macro are the values the byte -+ may have. */ -+ -+#define EI_MAG0 0 /* File identification byte 0 index */ -+#define ELFMAG0 0x7f /* Magic number byte 0 */ -+ -+#define EI_MAG1 1 /* File identification byte 1 index */ -+#define ELFMAG1 'E' /* Magic number byte 1 */ -+ -+#define EI_MAG2 2 /* File identification byte 2 index */ -+#define ELFMAG2 'L' /* Magic number byte 2 */ -+ -+#define EI_MAG3 3 /* File identification byte 3 index */ -+#define ELFMAG3 'F' /* Magic number byte 3 */ -+ -+/* Conglomeration of the identification bytes, for easy testing as a word. */ -+#define ELFMAG "\177ELF" -+#define SELFMAG 4 -+ -+#define EI_CLASS 4 /* File class byte index */ -+#define ELFCLASSNONE 0 /* Invalid class */ -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+#define ELFCLASSNUM 3 -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATANONE 0 /* Invalid data encoding */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ -+#define ELFDATANUM 3 -+ -+#define EI_VERSION 6 /* File version byte index */ -+ /* Value must be EV_CURRENT */ -+ -+#define EI_OSABI 7 /* OS ABI identification */ -+#define ELFOSABI_NONE 0 /* UNIX System V ABI */ -+#define ELFOSABI_SYSV 0 /* Alias. */ -+#define ELFOSABI_HPUX 1 /* HP-UX */ -+#define ELFOSABI_NETBSD 2 /* NetBSD. */ -+#define ELFOSABI_LINUX 3 /* Linux. */ -+#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ -+#define ELFOSABI_AIX 7 /* IBM AIX. */ -+#define ELFOSABI_IRIX 8 /* SGI Irix. */ -+#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ -+#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ -+#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ -+#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ -+#define ELFOSABI_ARM 97 /* ARM */ -+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ -+ -+#define EI_ABIVERSION 8 /* ABI version */ -+ -+#define EI_PAD 9 /* Byte index of padding bytes */ -+ -+/* Legal values for e_type (object file type). */ -+ -+#define ET_NONE 0 /* No file type */ -+#define ET_REL 1 /* Relocatable file */ -+#define ET_EXEC 2 /* Executable file */ -+#define ET_DYN 3 /* Shared object file */ -+#define ET_CORE 4 /* Core file */ -+#define ET_NUM 5 /* Number of defined types */ -+#define ET_LOOS 0xfe00 /* OS-specific range start */ -+#define ET_HIOS 0xfeff /* OS-specific range end */ -+#define ET_LOPROC 0xff00 /* Processor-specific range start */ -+#define ET_HIPROC 0xffff /* Processor-specific range end */ -+ -+/* Legal values for e_machine (architecture). */ -+ -+#define EM_NONE 0 /* No machine */ -+#define EM_M32 1 /* AT&T WE 32100 */ -+#define EM_SPARC 2 /* SUN SPARC */ -+#define EM_386 3 /* Intel 80386 */ -+#define EM_68K 4 /* Motorola m68k family */ -+#define EM_88K 5 /* Motorola m88k family */ -+#define EM_860 7 /* Intel 80860 */ -+#define EM_MIPS 8 /* MIPS R3000 big-endian */ -+#define EM_S370 9 /* IBM System/370 */ -+#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ -+ -+#define EM_PARISC 15 /* HPPA */ -+#define EM_VPP500 17 /* Fujitsu VPP500 */ -+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ -+#define EM_960 19 /* Intel 80960 */ -+#define EM_PPC 20 /* PowerPC */ -+#define EM_PPC64 21 /* PowerPC 64-bit */ -+#define EM_S390 22 /* IBM S390 */ -+ -+#define EM_V800 36 /* NEC V800 series */ -+#define EM_FR20 37 /* Fujitsu FR20 */ -+#define EM_RH32 38 /* TRW RH-32 */ -+#define EM_RCE 39 /* Motorola RCE */ -+#define EM_ARM 40 /* ARM */ -+#define EM_FAKE_ALPHA 41 /* Digital Alpha */ -+#define EM_SH 42 /* Hitachi SH */ -+#define EM_SPARCV9 43 /* SPARC v9 64-bit */ -+#define EM_TRICORE 44 /* Siemens Tricore */ -+#define EM_ARC 45 /* Argonaut RISC Core */ -+#define EM_H8_300 46 /* Hitachi H8/300 */ -+#define EM_H8_300H 47 /* Hitachi H8/300H */ -+#define EM_H8S 48 /* Hitachi H8S */ -+#define EM_H8_500 49 /* Hitachi H8/500 */ -+#define EM_IA_64 50 /* Intel Merced */ -+#define EM_MIPS_X 51 /* Stanford MIPS-X */ -+#define EM_COLDFIRE 52 /* Motorola Coldfire */ -+#define EM_68HC12 53 /* Motorola M68HC12 */ -+#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -+#define EM_PCP 55 /* Siemens PCP */ -+#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -+#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -+#define EM_STARCORE 58 /* Motorola Start*Core processor */ -+#define EM_ME16 59 /* Toyota ME16 processor */ -+#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -+#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -+#define EM_X86_64 62 /* AMD x86-64 architecture */ -+#define EM_PDSP 63 /* Sony DSP Processor */ -+ -+#define EM_FX66 66 /* Siemens FX66 microcontroller */ -+#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -+#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -+#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -+#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -+#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -+#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -+#define EM_SVX 73 /* Silicon Graphics SVx */ -+#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ -+#define EM_VAX 75 /* Digital VAX */ -+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ -+#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ -+#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -+#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -+#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ -+#define EM_HUANY 81 /* Harvard University machine-independent object files */ -+#define EM_PRISM 82 /* SiTera Prism */ -+#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -+#define EM_FR30 84 /* Fujitsu FR30 */ -+#define EM_D10V 85 /* Mitsubishi D10V */ -+#define EM_D30V 86 /* Mitsubishi D30V */ -+#define EM_V850 87 /* NEC v850 */ -+#define EM_M32R 88 /* Mitsubishi M32R */ -+#define EM_MN10300 89 /* Matsushita MN10300 */ -+#define EM_MN10200 90 /* Matsushita MN10200 */ -+#define EM_PJ 91 /* picoJava */ -+#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ -+#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ -+#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ -+#define EM_NUM 95 -+ -+/* If it is necessary to assign new unofficial EM_* values, please -+ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the -+ chances of collision with official or non-GNU unofficial values. */ -+ -+#define EM_ALPHA 0x9026 -+ -+/* Legal values for e_version (version). */ -+ -+#define EV_NONE 0 /* Invalid ELF version */ -+#define EV_CURRENT 1 /* Current version */ -+#define EV_NUM 2 -+ -+/* Section header. */ -+ -+typedef struct -+{ -+ Elf32_Word sh_name; /* Section name (string tbl index) */ -+ Elf32_Word sh_type; /* Section type */ -+ Elf32_Word sh_flags; /* Section flags */ -+ Elf32_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf32_Off sh_offset; /* Section file offset */ -+ Elf32_Word sh_size; /* Section size in bytes */ -+ Elf32_Word sh_link; /* Link to another section */ -+ Elf32_Word sh_info; /* Additional section information */ -+ Elf32_Word sh_addralign; /* Section alignment */ -+ Elf32_Word sh_entsize; /* Entry size if section holds table */ -+} Elf32_Shdr; -+ -+typedef struct -+{ -+ Elf64_Word sh_name; /* Section name (string tbl index) */ -+ Elf64_Word sh_type; /* Section type */ -+ Elf64_Xword sh_flags; /* Section flags */ -+ Elf64_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf64_Off sh_offset; /* Section file offset */ -+ Elf64_Xword sh_size; /* Section size in bytes */ -+ Elf64_Word sh_link; /* Link to another section */ -+ Elf64_Word sh_info; /* Additional section information */ -+ Elf64_Xword sh_addralign; /* Section alignment */ -+ Elf64_Xword sh_entsize; /* Entry size if section holds table */ -+} Elf64_Shdr; -+ -+/* Special section indices. */ -+ -+#define SHN_UNDEF 0 /* Undefined section */ -+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ -+#define SHN_LOPROC 0xff00 /* Start of processor-specific */ -+#define SHN_HIPROC 0xff1f /* End of processor-specific */ -+#define SHN_LOOS 0xff20 /* Start of OS-specific */ -+#define SHN_HIOS 0xff3f /* End of OS-specific */ -+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ -+#define SHN_COMMON 0xfff2 /* Associated symbol is common */ -+#define SHN_XINDEX 0xffff /* Index is in extra table. */ -+#define SHN_HIRESERVE 0xffff /* End of reserved indices */ -+ -+/* Legal values for sh_type (section type). */ -+ -+#define SHT_NULL 0 /* Section header table entry unused */ -+#define SHT_PROGBITS 1 /* Program data */ -+#define SHT_SYMTAB 2 /* Symbol table */ -+#define SHT_STRTAB 3 /* String table */ -+#define SHT_RELA 4 /* Relocation entries with addends */ -+#define SHT_HASH 5 /* Symbol hash table */ -+#define SHT_DYNAMIC 6 /* Dynamic linking information */ -+#define SHT_NOTE 7 /* Notes */ -+#define SHT_NOBITS 8 /* Program space with no data (bss) */ -+#define SHT_REL 9 /* Relocation entries, no addends */ -+#define SHT_SHLIB 10 /* Reserved */ -+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ -+#define SHT_INIT_ARRAY 14 /* Array of constructors */ -+#define SHT_FINI_ARRAY 15 /* Array of destructors */ -+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ -+#define SHT_GROUP 17 /* Section group */ -+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ -+#define SHT_NUM 19 /* Number of defined types. */ -+#define SHT_LOOS 0x60000000 /* Start OS-specific */ -+#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ -+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ -+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ -+#define SHT_SUNW_move 0x6ffffffa -+#define SHT_SUNW_COMDAT 0x6ffffffb -+#define SHT_SUNW_syminfo 0x6ffffffc -+#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ -+#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ -+#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ -+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ -+#define SHT_HIOS 0x6fffffff /* End OS-specific type */ -+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define SHT_LOUSER 0x80000000 /* Start of application-specific */ -+#define SHT_HIUSER 0x8fffffff /* End of application-specific */ -+ -+/* Legal values for sh_flags (section flags). */ -+ -+#define SHF_WRITE (1 << 0) /* Writable */ -+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ -+#define SHF_EXECINSTR (1 << 2) /* Executable */ -+#define SHF_MERGE (1 << 4) /* Might be merged */ -+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ -+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ -+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ -+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling -+ required */ -+#define SHF_GROUP (1 << 9) /* Section is member of a group. */ -+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ -+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ -+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Section group handling. */ -+#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ -+ -+/* Symbol table entry. */ -+ -+typedef struct -+{ -+ Elf32_Word st_name; /* Symbol name (string tbl index) */ -+ Elf32_Addr st_value; /* Symbol value */ -+ Elf32_Word st_size; /* Symbol size */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf32_Section st_shndx; /* Section index */ -+} Elf32_Sym; -+ -+typedef struct -+{ -+ Elf64_Word st_name; /* Symbol name (string tbl index) */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf64_Section st_shndx; /* Section index */ -+ Elf64_Addr st_value; /* Symbol value */ -+ Elf64_Xword st_size; /* Symbol size */ -+} Elf64_Sym; -+ -+/* The syminfo section if available contains additional information about -+ every dynamic symbol. */ -+ -+typedef struct -+{ -+ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf32_Half si_flags; /* Per symbol flags */ -+} Elf32_Syminfo; -+ -+typedef struct -+{ -+ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf64_Half si_flags; /* Per symbol flags */ -+} Elf64_Syminfo; -+ -+/* Possible values for si_boundto. */ -+#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ -+#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ -+#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ -+ -+/* Possible bitmasks for si_flags. */ -+#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ -+#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ -+#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ -+#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy -+ loaded */ -+/* Syminfo version values. */ -+#define SYMINFO_NONE 0 -+#define SYMINFO_CURRENT 1 -+#define SYMINFO_NUM 2 -+ -+ -+/* How to extract and insert information held in the st_info field. */ -+ -+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -+#define ELF32_ST_TYPE(val) ((val) & 0xf) -+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -+ -+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ -+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) -+ -+/* Legal values for ST_BIND subfield of st_info (symbol binding). */ -+ -+#define STB_LOCAL 0 /* Local symbol */ -+#define STB_GLOBAL 1 /* Global symbol */ -+#define STB_WEAK 2 /* Weak symbol */ -+#define STB_NUM 3 /* Number of defined types. */ -+#define STB_LOOS 10 /* Start of OS-specific */ -+#define STB_HIOS 12 /* End of OS-specific */ -+#define STB_LOPROC 13 /* Start of processor-specific */ -+#define STB_HIPROC 15 /* End of processor-specific */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_NOTYPE 0 /* Symbol type is unspecified */ -+#define STT_OBJECT 1 /* Symbol is a data object */ -+#define STT_FUNC 2 /* Symbol is a code object */ -+#define STT_SECTION 3 /* Symbol associated with a section */ -+#define STT_FILE 4 /* Symbol's name is file name */ -+#define STT_COMMON 5 /* Symbol is a common data object */ -+#define STT_TLS 6 /* Symbol is thread-local data object*/ -+#define STT_NUM 7 /* Number of defined types. */ -+#define STT_LOOS 10 /* Start of OS-specific */ -+#define STT_HIOS 12 /* End of OS-specific */ -+#define STT_LOPROC 13 /* Start of processor-specific */ -+#define STT_HIPROC 15 /* End of processor-specific */ -+ -+ -+/* Symbol table indices are found in the hash buckets and chain table -+ of a symbol hash table section. This special index value indicates -+ the end of a chain, meaning no further symbols are found in that bucket. */ -+ -+#define STN_UNDEF 0 /* End of a chain. */ -+ -+ -+/* How to extract and insert information held in the st_other field. */ -+ -+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) -+ -+/* For ELF64 the definitions are the same. */ -+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) -+ -+/* Symbol visibility specification encoded in the st_other field. */ -+#define STV_DEFAULT 0 /* Default symbol visibility rules */ -+#define STV_INTERNAL 1 /* Processor specific hidden class */ -+#define STV_HIDDEN 2 /* Sym unavailable in other modules */ -+#define STV_PROTECTED 3 /* Not preemptible, not exported */ -+ -+ -+/* Relocation table entry without addend (in section of type SHT_REL). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+} Elf32_Rel; -+ -+/* I have seen two different definitions of the Elf64_Rel and -+ Elf64_Rela structures, so we'll leave them out until Novell (or -+ whoever) gets their act together. */ -+/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+} Elf64_Rel; -+ -+/* Relocation table entry with addend (in section of type SHT_RELA). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+ Elf32_Sword r_addend; /* Addend */ -+} Elf32_Rela; -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+ Elf64_Sxword r_addend; /* Addend */ -+} Elf64_Rela; -+ -+/* How to extract and insert information held in the r_info field. */ -+ -+#define ELF32_R_SYM(val) ((val) >> 8) -+#define ELF32_R_TYPE(val) ((val) & 0xff) -+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) -+ -+#define ELF64_R_SYM(i) ((i) >> 32) -+#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) -+ -+/* Program segment header. */ -+ -+typedef struct -+{ -+ Elf32_Word p_type; /* Segment type */ -+ Elf32_Off p_offset; /* Segment file offset */ -+ Elf32_Addr p_vaddr; /* Segment virtual address */ -+ Elf32_Addr p_paddr; /* Segment physical address */ -+ Elf32_Word p_filesz; /* Segment size in file */ -+ Elf32_Word p_memsz; /* Segment size in memory */ -+ Elf32_Word p_flags; /* Segment flags */ -+ Elf32_Word p_align; /* Segment alignment */ -+} Elf32_Phdr; -+ -+typedef struct -+{ -+ Elf64_Word p_type; /* Segment type */ -+ Elf64_Word p_flags; /* Segment flags */ -+ Elf64_Off p_offset; /* Segment file offset */ -+ Elf64_Addr p_vaddr; /* Segment virtual address */ -+ Elf64_Addr p_paddr; /* Segment physical address */ -+ Elf64_Xword p_filesz; /* Segment size in file */ -+ Elf64_Xword p_memsz; /* Segment size in memory */ -+ Elf64_Xword p_align; /* Segment alignment */ -+} Elf64_Phdr; -+ -+/* Legal values for p_type (segment type). */ -+ -+#define PT_NULL 0 /* Program header table entry unused */ -+#define PT_LOAD 1 /* Loadable program segment */ -+#define PT_DYNAMIC 2 /* Dynamic linking information */ -+#define PT_INTERP 3 /* Program interpreter */ -+#define PT_NOTE 4 /* Auxiliary information */ -+#define PT_SHLIB 5 /* Reserved */ -+#define PT_PHDR 6 /* Entry for header table itself */ -+#define PT_TLS 7 /* Thread-local storage segment */ -+#define PT_NUM 8 /* Number of defined types */ -+#define PT_LOOS 0x60000000 /* Start of OS-specific */ -+#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ -+#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ -+#define PT_LOSUNW 0x6ffffffa -+#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ -+#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ -+#define PT_HISUNW 0x6fffffff -+#define PT_HIOS 0x6fffffff /* End of OS-specific */ -+#define PT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define PT_HIPROC 0x7fffffff /* End of processor-specific */ -+ -+/* Legal values for p_flags (segment flags). */ -+ -+#define PF_X (1 << 0) /* Segment is executable */ -+#define PF_W (1 << 1) /* Segment is writable */ -+#define PF_R (1 << 2) /* Segment is readable */ -+#define PF_MASKOS 0x0ff00000 /* OS-specific */ -+#define PF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Legal values for note segment descriptor types for core files. */ -+ -+#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ -+#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ -+#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ -+#define NT_PRXREG 4 /* Contains copy of prxregset struct */ -+#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ -+#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ -+#define NT_AUXV 6 /* Contains copy of auxv array */ -+#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ -+#define NT_ASRS 8 /* Contains copy of asrset struct */ -+#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ -+#define NT_PSINFO 13 /* Contains copy of psinfo struct */ -+#define NT_PRCRED 14 /* Contains copy of prcred struct */ -+#define NT_UTSNAME 15 /* Contains copy of utsname struct */ -+#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ -+#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ -+#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ -+ -+/* Legal values for the note segment descriptor types for object files. */ -+ -+#define NT_VERSION 1 /* Contains a version string. */ -+ -+ -+/* Dynamic section entry. */ -+ -+typedef struct -+{ -+ Elf32_Sword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf32_Word d_val; /* Integer value */ -+ Elf32_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf32_Dyn; -+ -+typedef struct -+{ -+ Elf64_Sxword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf64_Xword d_val; /* Integer value */ -+ Elf64_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf64_Dyn; -+ -+/* Legal values for d_tag (dynamic entry type). */ -+ -+#define DT_NULL 0 /* Marks end of dynamic section */ -+#define DT_NEEDED 1 /* Name of needed library */ -+#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ -+#define DT_PLTGOT 3 /* Processor defined value */ -+#define DT_HASH 4 /* Address of symbol hash table */ -+#define DT_STRTAB 5 /* Address of string table */ -+#define DT_SYMTAB 6 /* Address of symbol table */ -+#define DT_RELA 7 /* Address of Rela relocs */ -+#define DT_RELASZ 8 /* Total size of Rela relocs */ -+#define DT_RELAENT 9 /* Size of one Rela reloc */ -+#define DT_STRSZ 10 /* Size of string table */ -+#define DT_SYMENT 11 /* Size of one symbol table entry */ -+#define DT_INIT 12 /* Address of init function */ -+#define DT_FINI 13 /* Address of termination function */ -+#define DT_SONAME 14 /* Name of shared object */ -+#define DT_RPATH 15 /* Library search path (deprecated) */ -+#define DT_SYMBOLIC 16 /* Start symbol search here */ -+#define DT_REL 17 /* Address of Rel relocs */ -+#define DT_RELSZ 18 /* Total size of Rel relocs */ -+#define DT_RELENT 19 /* Size of one Rel reloc */ -+#define DT_PLTREL 20 /* Type of reloc in PLT */ -+#define DT_DEBUG 21 /* For debugging; unspecified */ -+#define DT_TEXTREL 22 /* Reloc might modify .text */ -+#define DT_JMPREL 23 /* Address of PLT relocs */ -+#define DT_BIND_NOW 24 /* Process relocations of object */ -+#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ -+#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ -+#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ -+#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ -+#define DT_RUNPATH 29 /* Library search path */ -+#define DT_FLAGS 30 /* Flags for the object being loaded */ -+#define DT_ENCODING 32 /* Start of encoded range */ -+#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ -+#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ -+#define DT_NUM 34 /* Number used */ -+#define DT_LOOS 0x6000000d /* Start of OS-specific */ -+#define DT_HIOS 0x6ffff000 /* End of OS-specific */ -+#define DT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define DT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ -+ -+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the -+ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's -+ approach. */ -+#define DT_VALRNGLO 0x6ffffd00 -+#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ -+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ -+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ -+#define DT_CHECKSUM 0x6ffffdf8 -+#define DT_PLTPADSZ 0x6ffffdf9 -+#define DT_MOVEENT 0x6ffffdfa -+#define DT_MOVESZ 0x6ffffdfb -+#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ -+#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting -+ the following DT_* entry. */ -+#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ -+#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ -+#define DT_VALRNGHI 0x6ffffdff -+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ -+#define DT_VALNUM 12 -+ -+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the -+ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. -+ -+ If any adjustment is made to the ELF object after it has been -+ built these entries will need to be adjusted. */ -+#define DT_ADDRRNGLO 0x6ffffe00 -+#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ -+#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ -+#define DT_CONFIG 0x6ffffefa /* Configuration information. */ -+#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ -+#define DT_AUDIT 0x6ffffefc /* Object auditing. */ -+#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ -+#define DT_MOVETAB 0x6ffffefe /* Move table. */ -+#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ -+#define DT_ADDRRNGHI 0x6ffffeff -+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ -+#define DT_ADDRNUM 10 -+ -+/* The versioning entry types. The next are defined as part of the -+ GNU extension. */ -+#define DT_VERSYM 0x6ffffff0 -+ -+#define DT_RELACOUNT 0x6ffffff9 -+#define DT_RELCOUNT 0x6ffffffa -+ -+/* These were chosen by Sun. */ -+#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ -+#define DT_VERDEF 0x6ffffffc /* Address of version definition -+ table */ -+#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ -+#define DT_VERNEED 0x6ffffffe /* Address of table with needed -+ versions */ -+#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ -+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ -+#define DT_VERSIONTAGNUM 16 -+ -+/* Sun added these machine-independent extensions in the "processor-specific" -+ range. Be compatible. */ -+#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ -+#define DT_FILTER 0x7fffffff /* Shared object to get values from */ -+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -+#define DT_EXTRANUM 3 -+ -+/* Values of `d_un.d_val' in the DT_FLAGS entry. */ -+#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ -+#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ -+#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ -+#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ -+#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ -+ -+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 -+ entry in the dynamic section. */ -+#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ -+#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ -+#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ -+#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ -+#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ -+#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ -+#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ -+#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ -+#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ -+#define DF_1_TRANS 0x00000200 -+#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ -+#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ -+#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ -+#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ -+#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ -+#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ -+#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ -+ -+/* Flags for the feature selection in DT_FEATURE_1. */ -+#define DTF_1_PARINIT 0x00000001 -+#define DTF_1_CONFEXP 0x00000002 -+ -+/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ -+#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ -+#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not -+ generally available. */ -+ -+/* Version definition sections. */ -+ -+typedef struct -+{ -+ Elf32_Half vd_version; /* Version revision */ -+ Elf32_Half vd_flags; /* Version information */ -+ Elf32_Half vd_ndx; /* Version Index */ -+ Elf32_Half vd_cnt; /* Number of associated aux entries */ -+ Elf32_Word vd_hash; /* Version name hash value */ -+ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf32_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf32_Verdef; -+ -+typedef struct -+{ -+ Elf64_Half vd_version; /* Version revision */ -+ Elf64_Half vd_flags; /* Version information */ -+ Elf64_Half vd_ndx; /* Version Index */ -+ Elf64_Half vd_cnt; /* Number of associated aux entries */ -+ Elf64_Word vd_hash; /* Version name hash value */ -+ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf64_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf64_Verdef; -+ -+ -+/* Legal values for vd_version (version revision). */ -+#define VER_DEF_NONE 0 /* No version */ -+#define VER_DEF_CURRENT 1 /* Current version */ -+#define VER_DEF_NUM 2 /* Given version number */ -+ -+/* Legal values for vd_flags (version information flags). */ -+#define VER_FLG_BASE 0x1 /* Version definition of file itself */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+/* Versym symbol index values. */ -+#define VER_NDX_LOCAL 0 /* Symbol is local. */ -+#define VER_NDX_GLOBAL 1 /* Symbol is global. */ -+#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ -+#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ -+ -+/* Auxialiary version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vda_name; /* Version or dependency names */ -+ Elf32_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf32_Verdaux; -+ -+typedef struct -+{ -+ Elf64_Word vda_name; /* Version or dependency names */ -+ Elf64_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf64_Verdaux; -+ -+ -+/* Version dependency section. */ -+ -+typedef struct -+{ -+ Elf32_Half vn_version; /* Version of structure */ -+ Elf32_Half vn_cnt; /* Number of associated aux entries */ -+ Elf32_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf32_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf32_Verneed; -+ -+typedef struct -+{ -+ Elf64_Half vn_version; /* Version of structure */ -+ Elf64_Half vn_cnt; /* Number of associated aux entries */ -+ Elf64_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf64_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf64_Verneed; -+ -+ -+/* Legal values for vn_version (version revision). */ -+#define VER_NEED_NONE 0 /* No version */ -+#define VER_NEED_CURRENT 1 /* Current version */ -+#define VER_NEED_NUM 2 /* Given version number */ -+ -+/* Auxiliary needed version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vna_hash; /* Hash value of dependency name */ -+ Elf32_Half vna_flags; /* Dependency specific information */ -+ Elf32_Half vna_other; /* Unused */ -+ Elf32_Word vna_name; /* Dependency name string offset */ -+ Elf32_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf32_Vernaux; -+ -+typedef struct -+{ -+ Elf64_Word vna_hash; /* Hash value of dependency name */ -+ Elf64_Half vna_flags; /* Dependency specific information */ -+ Elf64_Half vna_other; /* Unused */ -+ Elf64_Word vna_name; /* Dependency name string offset */ -+ Elf64_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf64_Vernaux; -+ -+ -+/* Legal values for vna_flags. */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+ -+/* Auxiliary vector. */ -+ -+/* This vector is normally only used by the program interpreter. The -+ usual definition in an ABI supplement uses the name auxv_t. The -+ vector is not usually defined in a standard file, but it -+ can't hurt. We rename it to avoid conflicts. The sizes of these -+ types are an arrangement between the exec server and the program -+ interpreter, so we don't fully specify them here. */ -+ -+typedef struct -+{ -+ int a_type; /* Entry type */ -+ union -+ { -+ long int a_val; /* Integer value */ -+ void *a_ptr; /* Pointer value */ -+ void (*a_fcn) (void); /* Function pointer value */ -+ } a_un; -+} Elf32_auxv_t; -+ -+typedef struct -+{ -+ long int a_type; /* Entry type */ -+ union -+ { -+ long int a_val; /* Integer value */ -+ void *a_ptr; /* Pointer value */ -+ void (*a_fcn) (void); /* Function pointer value */ -+ } a_un; -+} Elf64_auxv_t; -+ -+/* Legal values for a_type (entry type). */ -+ -+#define AT_NULL 0 /* End of vector */ -+#define AT_IGNORE 1 /* Entry should be ignored */ -+#define AT_EXECFD 2 /* File descriptor of program */ -+#define AT_PHDR 3 /* Program headers for program */ -+#define AT_PHENT 4 /* Size of program header entry */ -+#define AT_PHNUM 5 /* Number of program headers */ -+#define AT_PAGESZ 6 /* System page size */ -+#define AT_BASE 7 /* Base address of interpreter */ -+#define AT_FLAGS 8 /* Flags */ -+#define AT_ENTRY 9 /* Entry point of program */ -+#define AT_NOTELF 10 /* Program is not ELF */ -+#define AT_UID 11 /* Real uid */ -+#define AT_EUID 12 /* Effective uid */ -+#define AT_GID 13 /* Real gid */ -+#define AT_EGID 14 /* Effective gid */ -+#define AT_CLKTCK 17 /* Frequency of times() */ -+ -+/* Some more special a_type values describing the hardware. */ -+#define AT_PLATFORM 15 /* String identifying platform. */ -+#define AT_HWCAP 16 /* Machine dependent hints about -+ processor capabilities. */ -+ -+/* This entry gives some information about the FPU initialization -+ performed by the kernel. */ -+#define AT_FPUCW 18 /* Used FPU control word. */ -+ -+/* Cache block sizes. */ -+#define AT_DCACHEBSIZE 19 /* Data cache block size. */ -+#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ -+#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ -+ -+/* A special ignored value for PPC, used by the kernel to control the -+ interpretation of the AUXV. Must be > 16. */ -+#define AT_IGNOREPPC 22 /* Entry should be ignored. */ -+ -+#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ -+ -+/* Pointer to the global system page used for system calls and other -+ nice things. */ -+#define AT_SYSINFO 32 -+#define AT_SYSINFO_EHDR 33 -+ -+ -+/* Note section contents. Each entry in the note section begins with -+ a header of a fixed form. */ -+ -+typedef struct -+{ -+ Elf32_Word n_namesz; /* Length of the note's name. */ -+ Elf32_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf32_Word n_type; /* Type of the note. */ -+} Elf32_Nhdr; -+ -+typedef struct -+{ -+ Elf64_Word n_namesz; /* Length of the note's name. */ -+ Elf64_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf64_Word n_type; /* Type of the note. */ -+} Elf64_Nhdr; -+ -+/* Known names of notes. */ -+ -+/* Solaris entries in the note section have this name. */ -+#define ELF_NOTE_SOLARIS "SUNW Solaris" -+ -+/* Note entries for GNU systems have this name. */ -+#define ELF_NOTE_GNU "GNU" -+ -+ -+/* Defined types of notes for Solaris. */ -+ -+/* Value of descriptor (one word) is desired pagesize for the binary. */ -+#define ELF_NOTE_PAGESIZE_HINT 1 -+ -+ -+/* Defined note types for GNU systems. */ -+ -+/* ABI information. The descriptor consists of words: -+ word 0: OS descriptor -+ word 1: major version of the ABI -+ word 2: minor version of the ABI -+ word 3: subminor version of the ABI -+*/ -+#define ELF_NOTE_ABI 1 -+ -+/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI -+ note section entry. */ -+#define ELF_NOTE_OS_LINUX 0 -+#define ELF_NOTE_OS_GNU 1 -+#define ELF_NOTE_OS_SOLARIS2 2 -+#define ELF_NOTE_OS_FREEBSD 3 -+ -+ -+/* Move records. */ -+typedef struct -+{ -+ Elf32_Xword m_value; /* Symbol value. */ -+ Elf32_Word m_info; /* Size and index. */ -+ Elf32_Word m_poffset; /* Symbol offset. */ -+ Elf32_Half m_repeat; /* Repeat count. */ -+ Elf32_Half m_stride; /* Stride info. */ -+} Elf32_Move; -+ -+typedef struct -+{ -+ Elf64_Xword m_value; /* Symbol value. */ -+ Elf64_Xword m_info; /* Size and index. */ -+ Elf64_Xword m_poffset; /* Symbol offset. */ -+ Elf64_Half m_repeat; /* Repeat count. */ -+ Elf64_Half m_stride; /* Stride info. */ -+} Elf64_Move; -+ -+/* Macro to construct move records. */ -+#define ELF32_M_SYM(info) ((info) >> 8) -+#define ELF32_M_SIZE(info) ((unsigned char) (info)) -+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) -+ -+#define ELF64_M_SYM(info) ELF32_M_SYM (info) -+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) -+ -+ -+/* Motorola 68k specific definitions. */ -+ -+/* Values for Elf32_Ehdr.e_flags. */ -+#define EF_CPU32 0x00810000 -+ -+/* m68k relocs. */ -+ -+#define R_68K_NONE 0 /* No reloc */ -+#define R_68K_32 1 /* Direct 32 bit */ -+#define R_68K_16 2 /* Direct 16 bit */ -+#define R_68K_8 3 /* Direct 8 bit */ -+#define R_68K_PC32 4 /* PC relative 32 bit */ -+#define R_68K_PC16 5 /* PC relative 16 bit */ -+#define R_68K_PC8 6 /* PC relative 8 bit */ -+#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ -+#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ -+#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ -+#define R_68K_GOT32O 10 /* 32 bit GOT offset */ -+#define R_68K_GOT16O 11 /* 16 bit GOT offset */ -+#define R_68K_GOT8O 12 /* 8 bit GOT offset */ -+#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ -+#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ -+#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ -+#define R_68K_PLT32O 16 /* 32 bit PLT offset */ -+#define R_68K_PLT16O 17 /* 16 bit PLT offset */ -+#define R_68K_PLT8O 18 /* 8 bit PLT offset */ -+#define R_68K_COPY 19 /* Copy symbol at runtime */ -+#define R_68K_GLOB_DAT 20 /* Create GOT entry */ -+#define R_68K_JMP_SLOT 21 /* Create PLT entry */ -+#define R_68K_RELATIVE 22 /* Adjust by program base */ -+/* Keep this the last entry. */ -+#define R_68K_NUM 23 -+ -+/* Intel 80386 specific definitions. */ -+ -+/* i386 relocs. */ -+ -+#define R_386_NONE 0 /* No reloc */ -+#define R_386_32 1 /* Direct 32 bit */ -+#define R_386_PC32 2 /* PC relative 32 bit */ -+#define R_386_GOT32 3 /* 32 bit GOT entry */ -+#define R_386_PLT32 4 /* 32 bit PLT address */ -+#define R_386_COPY 5 /* Copy symbol at runtime */ -+#define R_386_GLOB_DAT 6 /* Create GOT entry */ -+#define R_386_JMP_SLOT 7 /* Create PLT entry */ -+#define R_386_RELATIVE 8 /* Adjust by program base */ -+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ -+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ -+#define R_386_32PLT 11 -+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ -+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS -+ block offset */ -+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block -+ offset */ -+#define R_386_TLS_LE 17 /* Offset relative to static TLS -+ block */ -+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of -+ general dynamic thread local data */ -+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of -+ local dynamic thread local data -+ in LE code */ -+#define R_386_16 20 -+#define R_386_PC16 21 -+#define R_386_8 22 -+#define R_386_PC8 23 -+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic -+ thread local data */ -+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ -+#define R_386_TLS_GD_CALL 26 /* Relocation for call to -+ __tls_get_addr() */ -+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ -+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic -+ thread local data in LE code */ -+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ -+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to -+ __tls_get_addr() in LDM code */ -+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ -+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ -+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS -+ block offset */ -+#define R_386_TLS_LE_32 34 /* Negated offset relative to static -+ TLS block */ -+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ -+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ -+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ -+/* Keep this the last entry. */ -+#define R_386_NUM 38 -+ -+/* SUN SPARC specific definitions. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_REGISTER 13 /* Global register reserved to app. */ -+ -+/* Values for Elf64_Ehdr.e_flags. */ -+ -+#define EF_SPARCV9_MM 3 -+#define EF_SPARCV9_TSO 0 -+#define EF_SPARCV9_PSO 1 -+#define EF_SPARCV9_RMO 2 -+#define EF_SPARC_LEDATA 0x800000 /* little endian data */ -+#define EF_SPARC_EXT_MASK 0xFFFF00 -+#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ -+#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ -+#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ -+#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ -+ -+/* SPARC relocs. */ -+ -+#define R_SPARC_NONE 0 /* No reloc */ -+#define R_SPARC_8 1 /* Direct 8 bit */ -+#define R_SPARC_16 2 /* Direct 16 bit */ -+#define R_SPARC_32 3 /* Direct 32 bit */ -+#define R_SPARC_DISP8 4 /* PC relative 8 bit */ -+#define R_SPARC_DISP16 5 /* PC relative 16 bit */ -+#define R_SPARC_DISP32 6 /* PC relative 32 bit */ -+#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ -+#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ -+#define R_SPARC_HI22 9 /* High 22 bit */ -+#define R_SPARC_22 10 /* Direct 22 bit */ -+#define R_SPARC_13 11 /* Direct 13 bit */ -+#define R_SPARC_LO10 12 /* Truncated 10 bit */ -+#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ -+#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ -+#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ -+#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ -+#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ -+#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ -+#define R_SPARC_COPY 19 /* Copy symbol at runtime */ -+#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ -+#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ -+#define R_SPARC_RELATIVE 22 /* Adjust by program base */ -+#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ -+ -+/* Additional Sparc64 relocs. */ -+ -+#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ -+#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ -+#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ -+#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ -+#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ -+#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ -+#define R_SPARC_10 30 /* Direct 10 bit */ -+#define R_SPARC_11 31 /* Direct 11 bit */ -+#define R_SPARC_64 32 /* Direct 64 bit */ -+#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ -+#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ -+#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ -+#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ -+#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ -+#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ -+#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ -+#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ -+#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ -+#define R_SPARC_7 43 /* Direct 7 bit */ -+#define R_SPARC_5 44 /* Direct 5 bit */ -+#define R_SPARC_6 45 /* Direct 6 bit */ -+#define R_SPARC_DISP64 46 /* PC relative 64 bit */ -+#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ -+#define R_SPARC_HIX22 48 /* High 22 bit complemented */ -+#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ -+#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ -+#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ -+#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ -+#define R_SPARC_REGISTER 53 /* Global register usage */ -+#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ -+#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ -+#define R_SPARC_TLS_GD_HI22 56 -+#define R_SPARC_TLS_GD_LO10 57 -+#define R_SPARC_TLS_GD_ADD 58 -+#define R_SPARC_TLS_GD_CALL 59 -+#define R_SPARC_TLS_LDM_HI22 60 -+#define R_SPARC_TLS_LDM_LO10 61 -+#define R_SPARC_TLS_LDM_ADD 62 -+#define R_SPARC_TLS_LDM_CALL 63 -+#define R_SPARC_TLS_LDO_HIX22 64 -+#define R_SPARC_TLS_LDO_LOX10 65 -+#define R_SPARC_TLS_LDO_ADD 66 -+#define R_SPARC_TLS_IE_HI22 67 -+#define R_SPARC_TLS_IE_LO10 68 -+#define R_SPARC_TLS_IE_LD 69 -+#define R_SPARC_TLS_IE_LDX 70 -+#define R_SPARC_TLS_IE_ADD 71 -+#define R_SPARC_TLS_LE_HIX22 72 -+#define R_SPARC_TLS_LE_LOX10 73 -+#define R_SPARC_TLS_DTPMOD32 74 -+#define R_SPARC_TLS_DTPMOD64 75 -+#define R_SPARC_TLS_DTPOFF32 76 -+#define R_SPARC_TLS_DTPOFF64 77 -+#define R_SPARC_TLS_TPOFF32 78 -+#define R_SPARC_TLS_TPOFF64 79 -+/* Keep this the last entry. */ -+#define R_SPARC_NUM 80 -+ -+/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ -+ -+#define DT_SPARC_REGISTER 0x70000001 -+#define DT_SPARC_NUM 2 -+ -+/* Bits present in AT_HWCAP, primarily for Sparc32. */ -+ -+#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ -+#define HWCAP_SPARC_STBAR 2 -+#define HWCAP_SPARC_SWAP 4 -+#define HWCAP_SPARC_MULDIV 8 -+#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ -+#define HWCAP_SPARC_ULTRA3 32 -+ -+/* MIPS R3000 specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ -+#define EF_MIPS_PIC 2 /* Contains PIC code */ -+#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ -+#define EF_MIPS_XGOT 8 -+#define EF_MIPS_64BIT_WHIRL 16 -+#define EF_MIPS_ABI2 32 -+#define EF_MIPS_ABI_ON32 64 -+#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ -+ -+/* Legal values for MIPS architecture level. */ -+ -+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* The following are non-official names and should not be used. */ -+ -+#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* Special section indices. */ -+ -+#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ -+#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ -+#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ -+#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ -+#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ -+#define SHT_MIPS_MSYM 0x70000001 -+#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ -+#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ -+#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ -+#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ -+#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ -+#define SHT_MIPS_PACKAGE 0x70000007 -+#define SHT_MIPS_PACKSYM 0x70000008 -+#define SHT_MIPS_RELD 0x70000009 -+#define SHT_MIPS_IFACE 0x7000000b -+#define SHT_MIPS_CONTENT 0x7000000c -+#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ -+#define SHT_MIPS_SHDR 0x70000010 -+#define SHT_MIPS_FDESC 0x70000011 -+#define SHT_MIPS_EXTSYM 0x70000012 -+#define SHT_MIPS_DENSE 0x70000013 -+#define SHT_MIPS_PDESC 0x70000014 -+#define SHT_MIPS_LOCSYM 0x70000015 -+#define SHT_MIPS_AUXSYM 0x70000016 -+#define SHT_MIPS_OPTSYM 0x70000017 -+#define SHT_MIPS_LOCSTR 0x70000018 -+#define SHT_MIPS_LINE 0x70000019 -+#define SHT_MIPS_RFDESC 0x7000001a -+#define SHT_MIPS_DELTASYM 0x7000001b -+#define SHT_MIPS_DELTAINST 0x7000001c -+#define SHT_MIPS_DELTACLASS 0x7000001d -+#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ -+#define SHT_MIPS_DELTADECL 0x7000001f -+#define SHT_MIPS_SYMBOL_LIB 0x70000020 -+#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ -+#define SHT_MIPS_TRANSLATE 0x70000022 -+#define SHT_MIPS_PIXIE 0x70000023 -+#define SHT_MIPS_XLATE 0x70000024 -+#define SHT_MIPS_XLATE_DEBUG 0x70000025 -+#define SHT_MIPS_WHIRL 0x70000026 -+#define SHT_MIPS_EH_REGION 0x70000027 -+#define SHT_MIPS_XLATE_OLD 0x70000028 -+#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ -+#define SHF_MIPS_MERGE 0x20000000 -+#define SHF_MIPS_ADDR 0x40000000 -+#define SHF_MIPS_STRINGS 0x80000000 -+#define SHF_MIPS_NOSTRIP 0x08000000 -+#define SHF_MIPS_LOCAL 0x04000000 -+#define SHF_MIPS_NAMES 0x02000000 -+#define SHF_MIPS_NODUPE 0x01000000 -+ -+ -+/* Symbol tables. */ -+ -+/* MIPS specific values for `st_other'. */ -+#define STO_MIPS_DEFAULT 0x0 -+#define STO_MIPS_INTERNAL 0x1 -+#define STO_MIPS_HIDDEN 0x2 -+#define STO_MIPS_PROTECTED 0x3 -+#define STO_MIPS_SC_ALIGN_UNUSED 0xff -+ -+/* MIPS specific values for `st_info'. */ -+#define STB_MIPS_SPLIT_COMMON 13 -+ -+/* Entries found in sections of type SHT_MIPS_GPTAB. */ -+ -+typedef union -+{ -+ struct -+ { -+ Elf32_Word gt_current_g_value; /* -G value used for compilation */ -+ Elf32_Word gt_unused; /* Not used */ -+ } gt_header; /* First entry in section */ -+ struct -+ { -+ Elf32_Word gt_g_value; /* If this value were used for -G */ -+ Elf32_Word gt_bytes; /* This many bytes would be used */ -+ } gt_entry; /* Subsequent entries in section */ -+} Elf32_gptab; -+ -+/* Entry found in sections of type SHT_MIPS_REGINFO. */ -+ -+typedef struct -+{ -+ Elf32_Word ri_gprmask; /* General registers used */ -+ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ -+ Elf32_Sword ri_gp_value; /* $gp register value */ -+} Elf32_RegInfo; -+ -+/* Entries found in sections of type SHT_MIPS_OPTIONS. */ -+ -+typedef struct -+{ -+ unsigned char kind; /* Determines interpretation of the -+ variable part of descriptor. */ -+ unsigned char size; /* Size of descriptor, including header. */ -+ Elf32_Section section; /* Section header index of section affected, -+ 0 for global options. */ -+ Elf32_Word info; /* Kind-specific information. */ -+} Elf_Options; -+ -+/* Values for `kind' field in Elf_Options. */ -+ -+#define ODK_NULL 0 /* Undefined. */ -+#define ODK_REGINFO 1 /* Register usage information. */ -+#define ODK_EXCEPTIONS 2 /* Exception processing options. */ -+#define ODK_PAD 3 /* Section padding options. */ -+#define ODK_HWPATCH 4 /* Hardware workarounds performed */ -+#define ODK_FILL 5 /* record the fill value used by the linker. */ -+#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ -+#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ -+#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ -+ -+/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ -+ -+#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ -+#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ -+#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ -+#define OEX_SMM 0x20000 /* Force sequential memory mode? */ -+#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ -+#define OEX_PRECISEFP OEX_FPDBUG -+#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ -+ -+#define OEX_FPU_INVAL 0x10 -+#define OEX_FPU_DIV0 0x08 -+#define OEX_FPU_OFLO 0x04 -+#define OEX_FPU_UFLO 0x02 -+#define OEX_FPU_INEX 0x01 -+ -+/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ -+ -+#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ -+#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ -+#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ -+#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ -+ -+#define OPAD_PREFIX 0x1 -+#define OPAD_POSTFIX 0x2 -+#define OPAD_SYMBOL 0x4 -+ -+/* Entry found in `.options' section. */ -+ -+typedef struct -+{ -+ Elf32_Word hwp_flags1; /* Extra flags. */ -+ Elf32_Word hwp_flags2; /* Extra flags. */ -+} Elf_Options_Hw; -+ -+/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ -+ -+#define OHWA0_R4KEOP_CHECKED 0x00000001 -+#define OHWA1_R4KEOP_CLEAN 0x00000002 -+ -+/* MIPS relocs. */ -+ -+#define R_MIPS_NONE 0 /* No reloc */ -+#define R_MIPS_16 1 /* Direct 16 bit */ -+#define R_MIPS_32 2 /* Direct 32 bit */ -+#define R_MIPS_REL32 3 /* PC relative 32 bit */ -+#define R_MIPS_26 4 /* Direct 26 bit shifted */ -+#define R_MIPS_HI16 5 /* High 16 bit */ -+#define R_MIPS_LO16 6 /* Low 16 bit */ -+#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ -+#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ -+#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ -+#define R_MIPS_PC16 10 /* PC relative 16 bit */ -+#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ -+#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ -+ -+#define R_MIPS_SHIFT5 16 -+#define R_MIPS_SHIFT6 17 -+#define R_MIPS_64 18 -+#define R_MIPS_GOT_DISP 19 -+#define R_MIPS_GOT_PAGE 20 -+#define R_MIPS_GOT_OFST 21 -+#define R_MIPS_GOT_HI16 22 -+#define R_MIPS_GOT_LO16 23 -+#define R_MIPS_SUB 24 -+#define R_MIPS_INSERT_A 25 -+#define R_MIPS_INSERT_B 26 -+#define R_MIPS_DELETE 27 -+#define R_MIPS_HIGHER 28 -+#define R_MIPS_HIGHEST 29 -+#define R_MIPS_CALL_HI16 30 -+#define R_MIPS_CALL_LO16 31 -+#define R_MIPS_SCN_DISP 32 -+#define R_MIPS_REL16 33 -+#define R_MIPS_ADD_IMMEDIATE 34 -+#define R_MIPS_PJUMP 35 -+#define R_MIPS_RELGOT 36 -+#define R_MIPS_JALR 37 -+/* Keep this the last entry. */ -+#define R_MIPS_NUM 38 -+ -+/* Legal values for p_type field of Elf32_Phdr. */ -+ -+#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ -+#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ -+#define PT_MIPS_OPTIONS 0x70000002 -+ -+/* Special program header types. */ -+ -+#define PF_MIPS_LOCAL 0x10000000 -+ -+/* Legal values for d_tag field of Elf32_Dyn. */ -+ -+#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ -+#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ -+#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ -+#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ -+#define DT_MIPS_FLAGS 0x70000005 /* Flags */ -+#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ -+#define DT_MIPS_MSYM 0x70000007 -+#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ -+#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ -+#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ -+#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ -+#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ -+#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ -+#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ -+#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ -+#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ -+#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ -+#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ -+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in -+ DT_MIPS_DELTA_CLASS. */ -+#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ -+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in -+ DT_MIPS_DELTA_INSTANCE. */ -+#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ -+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in -+ DT_MIPS_DELTA_RELOC. */ -+#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta -+ relocations refer to. */ -+#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in -+ DT_MIPS_DELTA_SYM. */ -+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the -+ class declaration. */ -+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in -+ DT_MIPS_DELTA_CLASSSYM. */ -+#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ -+#define DT_MIPS_PIXIE_INIT 0x70000023 -+#define DT_MIPS_SYMBOL_LIB 0x70000024 -+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -+#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -+#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ -+#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ -+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -+#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ -+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve -+ function stored in GOT. */ -+#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added -+ by rld on dlopen() calls. */ -+#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ -+#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ -+#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ -+#define DT_MIPS_NUM 0x32 -+ -+/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ -+ -+#define RHF_NONE 0 /* No flags */ -+#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ -+#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ -+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ -+#define RHF_NO_MOVE (1 << 3) -+#define RHF_SGI_ONLY (1 << 4) -+#define RHF_GUARANTEE_INIT (1 << 5) -+#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -+#define RHF_GUARANTEE_START_INIT (1 << 7) -+#define RHF_PIXIE (1 << 8) -+#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -+#define RHF_REQUICKSTART (1 << 10) -+#define RHF_REQUICKSTARTED (1 << 11) -+#define RHF_CORD (1 << 12) -+#define RHF_NO_UNRES_UNDEF (1 << 13) -+#define RHF_RLD_ORDER_SAFE (1 << 14) -+ -+/* Entries found in sections of type SHT_MIPS_LIBLIST. */ -+ -+typedef struct -+{ -+ Elf32_Word l_name; /* Name (string table index) */ -+ Elf32_Word l_time_stamp; /* Timestamp */ -+ Elf32_Word l_checksum; /* Checksum */ -+ Elf32_Word l_version; /* Interface version */ -+ Elf32_Word l_flags; /* Flags */ -+} Elf32_Lib; -+ -+typedef struct -+{ -+ Elf64_Word l_name; /* Name (string table index) */ -+ Elf64_Word l_time_stamp; /* Timestamp */ -+ Elf64_Word l_checksum; /* Checksum */ -+ Elf64_Word l_version; /* Interface version */ -+ Elf64_Word l_flags; /* Flags */ -+} Elf64_Lib; -+ -+ -+/* Legal values for l_flags. */ -+ -+#define LL_NONE 0 -+#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ -+#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ -+#define LL_REQUIRE_MINOR (1 << 2) -+#define LL_EXPORTS (1 << 3) -+#define LL_DELAY_LOAD (1 << 4) -+#define LL_DELTA (1 << 5) -+ -+/* Entries found in sections of type SHT_MIPS_CONFLICT. */ -+ -+typedef Elf32_Addr Elf32_Conflict; -+ -+ -+/* HPPA specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ -+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ -+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ -+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ -+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch -+ prediction. */ -+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ -+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ -+ -+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ -+ -+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ -+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ -+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ -+ -+/* Additional section indeces. */ -+ -+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared -+ symbols in ANSI C. */ -+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ -+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ -+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ -+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ -+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ -+ -+#define STT_HP_OPAQUE (STT_LOOS + 0x1) -+#define STT_HP_STUB (STT_LOOS + 0x2) -+ -+/* HPPA relocs. */ -+ -+#define R_PARISC_NONE 0 /* No reloc. */ -+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ -+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ -+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ -+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ -+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ -+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ -+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ -+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ -+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ -+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ -+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ -+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ -+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ -+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ -+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ -+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ -+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ -+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ -+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ -+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ -+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ -+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ -+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ -+#define R_PARISC_FPTR64 64 /* 64 bits function address. */ -+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ -+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ -+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ -+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ -+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ -+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ -+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ -+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ -+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ -+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ -+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ -+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ -+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ -+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ -+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ -+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LORESERVE 128 -+#define R_PARISC_COPY 128 /* Copy relocation. */ -+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ -+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ -+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ -+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ -+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ -+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ -+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ -+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ -+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_HIRESERVE 255 -+ -+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PT_HP_TLS (PT_LOOS + 0x0) -+#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -+#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -+#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -+#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -+#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -+#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -+#define PT_HP_PARALLEL (PT_LOOS + 0x10) -+#define PT_HP_FASTBIND (PT_LOOS + 0x11) -+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -+#define PT_HP_STACK (PT_LOOS + 0x14) -+ -+#define PT_PARISC_ARCHEXT 0x70000000 -+#define PT_PARISC_UNWIND 0x70000001 -+ -+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PF_PARISC_SBP 0x08000000 -+ -+#define PF_HP_PAGE_SIZE 0x00100000 -+#define PF_HP_FAR_SHARED 0x00200000 -+#define PF_HP_NEAR_SHARED 0x00400000 -+#define PF_HP_CODE 0x01000000 -+#define PF_HP_MODIFY 0x02000000 -+#define PF_HP_LAZYSWAP 0x04000000 -+#define PF_HP_SBP 0x08000000 -+ -+ -+/* Alpha specific definitions. */ -+ -+/* Legal values for e_flags field of Elf64_Ehdr. */ -+ -+#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ -+#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ -+ -+/* Legal values for sh_type field of Elf64_Shdr. */ -+ -+/* These two are primerily concerned with ECOFF debugging info. */ -+#define SHT_ALPHA_DEBUG 0x70000001 -+#define SHT_ALPHA_REGINFO 0x70000002 -+ -+/* Legal values for sh_flags field of Elf64_Shdr. */ -+ -+#define SHF_ALPHA_GPREL 0x10000000 -+ -+/* Legal values for st_other field of Elf64_Sym. */ -+#define STO_ALPHA_NOPV 0x80 /* No PV required. */ -+#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ -+ -+/* Alpha relocs. */ -+ -+#define R_ALPHA_NONE 0 /* No reloc */ -+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ -+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ -+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ -+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ -+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ -+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ -+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ -+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ -+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ -+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ -+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ -+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ -+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ -+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ -+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ -+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ -+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ -+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ -+#define R_ALPHA_TLS_GD_HI 28 -+#define R_ALPHA_TLSGD 29 -+#define R_ALPHA_TLS_LDM 30 -+#define R_ALPHA_DTPMOD64 31 -+#define R_ALPHA_GOTDTPREL 32 -+#define R_ALPHA_DTPREL64 33 -+#define R_ALPHA_DTPRELHI 34 -+#define R_ALPHA_DTPRELLO 35 -+#define R_ALPHA_DTPREL16 36 -+#define R_ALPHA_GOTTPREL 37 -+#define R_ALPHA_TPREL64 38 -+#define R_ALPHA_TPRELHI 39 -+#define R_ALPHA_TPRELLO 40 -+#define R_ALPHA_TPREL16 41 -+/* Keep this the last entry. */ -+#define R_ALPHA_NUM 46 -+ -+/* Magic values of the LITUSE relocation addend. */ -+#define LITUSE_ALPHA_ADDR 0 -+#define LITUSE_ALPHA_BASE 1 -+#define LITUSE_ALPHA_BYTOFF 2 -+#define LITUSE_ALPHA_JSR 3 -+#define LITUSE_ALPHA_TLS_GD 4 -+#define LITUSE_ALPHA_TLS_LDM 5 -+ -+ -+/* PowerPC specific declarations */ -+ -+/* Values for Elf32/64_Ehdr.e_flags. */ -+#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ -+ -+/* Cygnus local bits below */ -+#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ -+#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib -+ flag */ -+ -+/* PowerPC relocations defined by the ABIs */ -+#define R_PPC_NONE 0 -+#define R_PPC_ADDR32 1 /* 32bit absolute address */ -+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -+#define R_PPC_ADDR16 3 /* 16bit absolute address */ -+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -+#define R_PPC_ADDR14_BRTAKEN 8 -+#define R_PPC_ADDR14_BRNTAKEN 9 -+#define R_PPC_REL24 10 /* PC relative 26 bit */ -+#define R_PPC_REL14 11 /* PC relative 16 bit */ -+#define R_PPC_REL14_BRTAKEN 12 -+#define R_PPC_REL14_BRNTAKEN 13 -+#define R_PPC_GOT16 14 -+#define R_PPC_GOT16_LO 15 -+#define R_PPC_GOT16_HI 16 -+#define R_PPC_GOT16_HA 17 -+#define R_PPC_PLTREL24 18 -+#define R_PPC_COPY 19 -+#define R_PPC_GLOB_DAT 20 -+#define R_PPC_JMP_SLOT 21 -+#define R_PPC_RELATIVE 22 -+#define R_PPC_LOCAL24PC 23 -+#define R_PPC_UADDR32 24 -+#define R_PPC_UADDR16 25 -+#define R_PPC_REL32 26 -+#define R_PPC_PLT32 27 -+#define R_PPC_PLTREL32 28 -+#define R_PPC_PLT16_LO 29 -+#define R_PPC_PLT16_HI 30 -+#define R_PPC_PLT16_HA 31 -+#define R_PPC_SDAREL16 32 -+#define R_PPC_SECTOFF 33 -+#define R_PPC_SECTOFF_LO 34 -+#define R_PPC_SECTOFF_HI 35 -+#define R_PPC_SECTOFF_HA 36 -+ -+/* PowerPC relocations defined for the TLS access ABI. */ -+#define R_PPC_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ -+#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ -+#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ -+#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ -+#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ -+#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ -+#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ -+#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ -+#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ -+ -+/* Keep this the last entry. */ -+#define R_PPC_NUM 95 -+ -+/* The remaining relocs are from the Embedded ELF ABI, and are not -+ in the SVR4 ELF ABI. */ -+#define R_PPC_EMB_NADDR32 101 -+#define R_PPC_EMB_NADDR16 102 -+#define R_PPC_EMB_NADDR16_LO 103 -+#define R_PPC_EMB_NADDR16_HI 104 -+#define R_PPC_EMB_NADDR16_HA 105 -+#define R_PPC_EMB_SDAI16 106 -+#define R_PPC_EMB_SDA2I16 107 -+#define R_PPC_EMB_SDA2REL 108 -+#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ -+#define R_PPC_EMB_MRKREF 110 -+#define R_PPC_EMB_RELSEC16 111 -+#define R_PPC_EMB_RELST_LO 112 -+#define R_PPC_EMB_RELST_HI 113 -+#define R_PPC_EMB_RELST_HA 114 -+#define R_PPC_EMB_BIT_FLD 115 -+#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ -+ -+/* Diab tool relocations. */ -+#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ -+#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ -+#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ -+#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ -+#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ -+#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ -+ -+/* This is a phony reloc to handle any old fashioned TOC16 references -+ that may still be in object files. */ -+#define R_PPC_TOC16 255 -+ -+ -+/* PowerPC64 relocations defined by the ABIs */ -+#define R_PPC64_NONE R_PPC_NONE -+#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ -+#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ -+#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ -+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ -+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ -+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ -+#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ -+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -+#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ -+#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ -+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -+#define R_PPC64_GOT16 R_PPC_GOT16 -+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA -+ -+#define R_PPC64_COPY R_PPC_COPY -+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -+#define R_PPC64_RELATIVE R_PPC_RELATIVE -+ -+#define R_PPC64_UADDR32 R_PPC_UADDR32 -+#define R_PPC64_UADDR16 R_PPC_UADDR16 -+#define R_PPC64_REL32 R_PPC_REL32 -+#define R_PPC64_PLT32 R_PPC_PLT32 -+#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA -+ -+#define R_PPC64_SECTOFF R_PPC_SECTOFF -+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -+#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ -+#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ -+#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ -+#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ -+#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ -+#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ -+#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ -+#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ -+#define R_PPC64_PLT64 45 /* doubleword64 L + A */ -+#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ -+#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ -+#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ -+#define R_PPC64_TOC 51 /* doubleword64 .TOC */ -+#define R_PPC64_PLTGOT16 52 /* half16* M + A */ -+#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ -+#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ -+#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ -+ -+#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ -+#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ -+#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ -+#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ -+#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ -+#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ -+#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ -+#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ -+#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ -+#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ -+#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ -+ -+/* PowerPC64 relocations defined for the TLS access ABI. */ -+#define R_PPC64_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ -+#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ -+#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ -+#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ -+#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ -+#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ -+#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ -+#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ -+#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ -+#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ -+#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ -+#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ -+#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ -+#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ -+#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ -+#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ -+#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ -+ -+/* Keep this the last entry. */ -+#define R_PPC64_NUM 107 -+ -+/* PowerPC64 specific values for the Dyn d_tag field. */ -+#define DT_PPC64_GLINK (DT_LOPROC + 0) -+#define DT_PPC64_NUM 1 -+ -+ -+/* ARM specific declarations */ -+ -+/* Processor specific flags for the ELF header e_flags field. */ -+#define EF_ARM_RELEXEC 0x01 -+#define EF_ARM_HASENTRY 0x02 -+#define EF_ARM_INTERWORK 0x04 -+#define EF_ARM_APCS_26 0x08 -+#define EF_ARM_APCS_FLOAT 0x10 -+#define EF_ARM_PIC 0x20 -+#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ -+#define EF_ARM_NEW_ABI 0x80 -+#define EF_ARM_OLD_ABI 0x100 -+ -+/* Other constants defined in the ARM ELF spec. version B-01. */ -+/* NB. These conflict with values defined above. */ -+#define EF_ARM_SYMSARESORTED 0x04 -+#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -+#define EF_ARM_MAPSYMSFIRST 0x10 -+#define EF_ARM_EABIMASK 0XFF000000 -+ -+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -+#define EF_ARM_EABI_UNKNOWN 0x00000000 -+#define EF_ARM_EABI_VER1 0x01000000 -+#define EF_ARM_EABI_VER2 0x02000000 -+ -+/* Additional symbol types for Thumb */ -+#define STT_ARM_TFUNC 0xd -+ -+/* ARM-specific values for sh_flags */ -+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ -+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined -+ in the input to a link step */ -+ -+/* ARM-specific program header flags */ -+#define PF_ARM_SB 0x10000000 /* Segment contains the location -+ addressed by the static base */ -+ -+/* ARM relocs. */ -+#define R_ARM_NONE 0 /* No reloc */ -+#define R_ARM_PC24 1 /* PC relative 26 bit branch */ -+#define R_ARM_ABS32 2 /* Direct 32 bit */ -+#define R_ARM_REL32 3 /* PC relative 32 bit */ -+#define R_ARM_PC13 4 -+#define R_ARM_ABS16 5 /* Direct 16 bit */ -+#define R_ARM_ABS12 6 /* Direct 12 bit */ -+#define R_ARM_THM_ABS5 7 -+#define R_ARM_ABS8 8 /* Direct 8 bit */ -+#define R_ARM_SBREL32 9 -+#define R_ARM_THM_PC22 10 -+#define R_ARM_THM_PC8 11 -+#define R_ARM_AMP_VCALL9 12 -+#define R_ARM_SWI24 13 -+#define R_ARM_THM_SWI8 14 -+#define R_ARM_XPC25 15 -+#define R_ARM_THM_XPC22 16 -+#define R_ARM_COPY 20 /* Copy symbol at runtime */ -+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ -+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ -+#define R_ARM_RELATIVE 23 /* Adjust by program base */ -+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ -+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ -+#define R_ARM_GOT32 26 /* 32 bit GOT entry */ -+#define R_ARM_PLT32 27 /* 32 bit PLT address */ -+#define R_ARM_ALU_PCREL_7_0 32 -+#define R_ARM_ALU_PCREL_15_8 33 -+#define R_ARM_ALU_PCREL_23_15 34 -+#define R_ARM_LDR_SBREL_11_0 35 -+#define R_ARM_ALU_SBREL_19_12 36 -+#define R_ARM_ALU_SBREL_27_20 37 -+#define R_ARM_GNU_VTENTRY 100 -+#define R_ARM_GNU_VTINHERIT 101 -+#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ -+#define R_ARM_THM_PC9 103 /* thumb conditional branch */ -+#define R_ARM_RXPC25 249 -+#define R_ARM_RSBREL32 250 -+#define R_ARM_THM_RPC22 251 -+#define R_ARM_RREL32 252 -+#define R_ARM_RABS22 253 -+#define R_ARM_RPC24 254 -+#define R_ARM_RBASE 255 -+/* Keep this the last entry. */ -+#define R_ARM_NUM 256 -+ -+/* IA-64 specific declarations. */ -+ -+/* Processor specific flags for the Ehdr e_flags field. */ -+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ -+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ -+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ -+ -+/* Processor specific values for the Phdr p_type field. */ -+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ -+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ -+ -+/* Processor specific flags for the Phdr p_flags field. */ -+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Shdr sh_type field. */ -+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ -+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ -+ -+/* Processor specific flags for the Shdr sh_flags field. */ -+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ -+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Dyn d_tag field. */ -+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -+#define DT_IA_64_NUM 1 -+ -+/* IA-64 relocations. */ -+#define R_IA64_NONE 0x00 /* none */ -+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ -+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ -+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ -+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ -+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ -+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ -+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ -+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ -+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ -+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ -+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ -+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ -+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ -+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ -+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ -+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ -+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ -+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ -+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ -+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ -+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ -+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ -+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ -+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ -+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ -+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ -+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ -+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ -+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ -+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ -+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ -+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ -+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ -+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ -+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ -+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ -+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ -+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ -+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ -+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ -+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ -+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ -+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ -+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ -+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ -+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ -+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ -+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ -+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ -+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ -+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ -+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ -+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ -+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ -+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ -+#define R_IA64_COPY 0x84 /* copy relocation */ -+#define R_IA64_SUB 0x85 /* Addend and symbol difference */ -+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ -+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ -+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ -+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ -+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ -+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ -+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ -+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ -+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ -+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ -+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ -+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ -+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ -+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ -+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ -+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ -+ -+/* SH specific declarations */ -+ -+/* SH relocs. */ -+#define R_SH_NONE 0 -+#define R_SH_DIR32 1 -+#define R_SH_REL32 2 -+#define R_SH_DIR8WPN 3 -+#define R_SH_IND12W 4 -+#define R_SH_DIR8WPL 5 -+#define R_SH_DIR8WPZ 6 -+#define R_SH_DIR8BP 7 -+#define R_SH_DIR8W 8 -+#define R_SH_DIR8L 9 -+#define R_SH_SWITCH16 25 -+#define R_SH_SWITCH32 26 -+#define R_SH_USES 27 -+#define R_SH_COUNT 28 -+#define R_SH_ALIGN 29 -+#define R_SH_CODE 30 -+#define R_SH_DATA 31 -+#define R_SH_LABEL 32 -+#define R_SH_SWITCH8 33 -+#define R_SH_GNU_VTINHERIT 34 -+#define R_SH_GNU_VTENTRY 35 -+#define R_SH_TLS_GD_32 144 -+#define R_SH_TLS_LD_32 145 -+#define R_SH_TLS_LDO_32 146 -+#define R_SH_TLS_IE_32 147 -+#define R_SH_TLS_LE_32 148 -+#define R_SH_TLS_DTPMOD32 149 -+#define R_SH_TLS_DTPOFF32 150 -+#define R_SH_TLS_TPOFF32 151 -+#define R_SH_GOT32 160 -+#define R_SH_PLT32 161 -+#define R_SH_COPY 162 -+#define R_SH_GLOB_DAT 163 -+#define R_SH_JMP_SLOT 164 -+#define R_SH_RELATIVE 165 -+#define R_SH_GOTOFF 166 -+#define R_SH_GOTPC 167 -+/* Keep this the last entry. */ -+#define R_SH_NUM 256 -+ -+/* Additional s390 relocs */ -+ -+#define R_390_NONE 0 /* No reloc. */ -+#define R_390_8 1 /* Direct 8 bit. */ -+#define R_390_12 2 /* Direct 12 bit. */ -+#define R_390_16 3 /* Direct 16 bit. */ -+#define R_390_32 4 /* Direct 32 bit. */ -+#define R_390_PC32 5 /* PC relative 32 bit. */ -+#define R_390_GOT12 6 /* 12 bit GOT offset. */ -+#define R_390_GOT32 7 /* 32 bit GOT offset. */ -+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ -+#define R_390_COPY 9 /* Copy symbol at runtime. */ -+#define R_390_GLOB_DAT 10 /* Create GOT entry. */ -+#define R_390_JMP_SLOT 11 /* Create PLT entry. */ -+#define R_390_RELATIVE 12 /* Adjust by program base. */ -+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ -+#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ -+#define R_390_GOT16 15 /* 16 bit GOT offset. */ -+#define R_390_PC16 16 /* PC relative 16 bit. */ -+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ -+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ -+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ -+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ -+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ -+#define R_390_64 22 /* Direct 64 bit. */ -+#define R_390_PC64 23 /* PC relative 64 bit. */ -+#define R_390_GOT64 24 /* 64 bit GOT offset. */ -+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ -+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ -+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ -+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ -+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ -+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ -+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ -+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ -+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ -+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ -+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ -+#define R_390_TLS_GDCALL 38 /* Tag for function call in general -+ dynamic TLS code. */ -+#define R_390_TLS_LDCALL 39 /* Tag for function call in local -+ dynamic TLS code. */ -+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ -+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ -+#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS -+ block. */ -+ -+/* Keep this the last entry. */ -+#define R_390_NUM 57 -+ -+/* CRIS relocations. */ -+#define R_CRIS_NONE 0 -+#define R_CRIS_8 1 -+#define R_CRIS_16 2 -+#define R_CRIS_32 3 -+#define R_CRIS_8_PCREL 4 -+#define R_CRIS_16_PCREL 5 -+#define R_CRIS_32_PCREL 6 -+#define R_CRIS_GNU_VTINHERIT 7 -+#define R_CRIS_GNU_VTENTRY 8 -+#define R_CRIS_COPY 9 -+#define R_CRIS_GLOB_DAT 10 -+#define R_CRIS_JUMP_SLOT 11 -+#define R_CRIS_RELATIVE 12 -+#define R_CRIS_16_GOT 13 -+#define R_CRIS_32_GOT 14 -+#define R_CRIS_16_GOTPLT 15 -+#define R_CRIS_32_GOTPLT 16 -+#define R_CRIS_32_GOTREL 17 -+#define R_CRIS_32_PLT_GOTREL 18 -+#define R_CRIS_32_PLT_PCREL 19 -+ -+#define R_CRIS_NUM 20 -+ -+/* AMD x86-64 relocations. */ -+#define R_X86_64_NONE 0 /* No reloc */ -+#define R_X86_64_64 1 /* Direct 64 bit */ -+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ -+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ -+#define R_X86_64_PLT32 4 /* 32 bit PLT address */ -+#define R_X86_64_COPY 5 /* Copy symbol at runtime */ -+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ -+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ -+#define R_X86_64_RELATIVE 8 /* Adjust by program base */ -+#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative -+ offset to GOT */ -+#define R_X86_64_32 10 /* Direct 32 bit zero extended */ -+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ -+#define R_X86_64_16 12 /* Direct 16 bit zero extended */ -+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ -+#define R_X86_64_8 14 /* Direct 8 bit sign extended */ -+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ -+#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ -+#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ -+#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ -+#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset -+ to two GOT entries for GD symbol */ -+#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset -+ to two GOT entries for LD symbol */ -+#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ -+#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset -+ to GOT entry for IE symbol */ -+#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ -+ -+#define R_X86_64_NUM 24 -+ -+__END_DECLS -+ -+#endif /* elf.h */ - - #include "elfconfig.h" - -@@ -195,3 +2641,4 @@ - void fatal(const char *fmt, ...); - void warn(const char *fmt, ...); - void merror(const char *fmt, ...); -+ -diff -Nur linux-2.6.36.orig/scripts/mod/sumversion.c linux-2.6.36/scripts/mod/sumversion.c ---- linux-2.6.36.orig/scripts/mod/sumversion.c 2010-10-20 22:30:22.000000000 +0200 -+++ linux-2.6.36/scripts/mod/sumversion.c 2010-11-28 18:33:24.000000000 +0100 -@@ -1,4 +1,4 @@ --#include -+/* #include */ - #ifdef __sun__ - #include - #else diff --git a/target/linux/patches/3.4.107/defaults.patch b/target/linux/patches/3.4.107/defaults.patch deleted file mode 100644 index 58aae610b..000000000 --- a/target/linux/patches/3.4.107/defaults.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff -Nur linux-3.0.4.orig/fs/Kconfig linux-3.0.4/fs/Kconfig ---- linux-3.0.4.orig/fs/Kconfig 2011-08-29 22:56:30.000000000 +0200 -+++ linux-3.0.4/fs/Kconfig 2011-10-15 22:08:44.000000000 +0200 -@@ -47,7 +47,7 @@ - def_bool n - - config EXPORTFS -- tristate -+ def_bool y - - config FILE_LOCKING - bool "Enable POSIX file locking API" if EXPERT -diff -Nur linux-3.0.4.orig/fs/notify/Kconfig linux-3.0.4/fs/notify/Kconfig ---- linux-3.0.4.orig/fs/notify/Kconfig 2011-08-29 22:56:30.000000000 +0200 -+++ linux-3.0.4/fs/notify/Kconfig 2011-10-15 22:02:00.000000000 +0200 -@@ -1,5 +1,5 @@ - config FSNOTIFY -- def_bool n -+ def_bool y - - source "fs/notify/dnotify/Kconfig" - source "fs/notify/inotify/Kconfig" diff --git a/target/linux/patches/3.4.107/gemalto.patch b/target/linux/patches/3.4.107/gemalto.patch deleted file mode 100644 index 65f7af1d7..000000000 --- a/target/linux/patches/3.4.107/gemalto.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c linux-2.6.36/drivers/serial/8250/serial_cs.c ---- linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c 2010-10-20 22:30:22.000000000 +0200 -+++ linux-2.6.36/drivers/tty/serial/8250/serial_cs.c 2010-12-13 23:03:40.000000000 +0100 -@@ -794,6 +794,7 @@ - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), -+ PCMCIA_DEVICE_MANF_CARD(0x0157, 0x0100), /* Gemalto SCR */ - PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ - PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ - PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), diff --git a/target/linux/patches/3.4.107/lemote-rfkill.patch b/target/linux/patches/3.4.107/lemote-rfkill.patch deleted file mode 100644 index a61488434..000000000 --- a/target/linux/patches/3.4.107/lemote-rfkill.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -Nur linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c ---- linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-19 00:15:34.000000000 +0100 -+++ linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-27 23:29:46.000000000 +0200 -@@ -22,6 +22,9 @@ - - static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) - { -+#ifdef CONFIG_LEMOTE_MACH2F -+ return 1; -+#else - u8 gpio; - - gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); -@@ -29,6 +32,7 @@ - gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); - - return gpio & priv->rfkill_mask; -+#endif - } - - void rtl8187_rfkill_init(struct ieee80211_hw *hw) diff --git a/target/linux/patches/3.4.107/linux-gcc-check.patch b/target/linux/patches/3.4.107/linux-gcc-check.patch deleted file mode 100644 index 7cc381845..000000000 --- a/target/linux/patches/3.4.107/linux-gcc-check.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff -Nur linux-2.6.32.orig/arch/mips/include/asm/sgidefs.h linux-2.6.32/arch/mips/include/asm/sgidefs.h ---- linux-2.6.32.orig/arch/mips/include/asm/sgidefs.h 2009-12-03 04:51:21.000000000 +0100 -+++ linux-2.6.32/arch/mips/include/asm/sgidefs.h 2010-02-14 11:49:21.000000000 +0100 -@@ -11,14 +11,6 @@ - #define __ASM_SGIDEFS_H - - /* -- * Using a Linux compiler for building Linux seems logic but not to -- * everybody. -- */ --#ifndef __linux__ --#error Use a Linux compiler or give up. --#endif -- --/* - * Definitions for the ISA levels - * - * With the introduction of MIPS32 / MIPS64 instruction sets definitions diff --git a/target/linux/patches/3.4.107/mips-error.patch b/target/linux/patches/3.4.107/mips-error.patch deleted file mode 100644 index 800abc80d..000000000 --- a/target/linux/patches/3.4.107/mips-error.patch +++ /dev/null @@ -1,254 +0,0 @@ -From c022630633624a75b3b58f43dd3c6cc896a56cff Mon Sep 17 00:00:00 2001 -From: Steven J. Hill -Date: Fri, 06 Jul 2012 19:56:01 +0000 -Subject: MIPS: Refactor 'clear_page' and 'copy_page' functions. - -Remove usage of the '__attribute__((alias("...")))' hack that aliased -to integer arrays containing micro-assembled instructions. This hack -breaks when building a microMIPS kernel. It also makes the code much -easier to understand. - -[ralf@linux-mips.org: Added back export of the clear_page and copy_page -symbols so certain modules will work again. Also fixed build with -CONFIG_SIBYTE_DMA_PAGEOPS enabled.] - -Signed-off-by: Steven J. Hill -Cc: linux-mips@linux-mips.org -Patchwork: https://patchwork.linux-mips.org/patch/3866/ -Acked-by: David Daney -Signed-off-by: Ralf Baechle ---- -diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c -index 57ba13e..3fc1691 100644 ---- a/arch/mips/kernel/mips_ksyms.c -+++ b/arch/mips/kernel/mips_ksyms.c -@@ -5,7 +5,7 @@ - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * -- * Copyright (C) 1996, 97, 98, 99, 2000, 01, 03, 04, 05 by Ralf Baechle -+ * Copyright (C) 1996, 97, 98, 99, 2000, 01, 03, 04, 05, 12 by Ralf Baechle - * Copyright (C) 1999, 2000, 01 Silicon Graphics, Inc. - */ - #include -@@ -35,6 +35,12 @@ EXPORT_SYMBOL(memmove); - EXPORT_SYMBOL(kernel_thread); - - /* -+ * Functions that operate on entire pages. Mostly used by memory management. -+ */ -+EXPORT_SYMBOL(clear_page); -+EXPORT_SYMBOL(copy_page); -+ -+/* - * Userspace access stuff. - */ - EXPORT_SYMBOL(__copy_user); -diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile -index 4aa2028..fd6203f 100644 ---- a/arch/mips/mm/Makefile -+++ b/arch/mips/mm/Makefile -@@ -3,8 +3,8 @@ - # - - obj-y += cache.o dma-default.o extable.o fault.o \ -- gup.o init.o mmap.o page.o tlbex.o \ -- tlbex-fault.o uasm.o -+ gup.o init.o mmap.o page.o page-funcs.o \ -+ tlbex.o tlbex-fault.o uasm.o - - obj-$(CONFIG_32BIT) += ioremap.o pgtable-32.o - obj-$(CONFIG_64BIT) += pgtable-64.o -diff --git a/arch/mips/mm/page-funcs.S b/arch/mips/mm/page-funcs.S -new file mode 100644 -index 0000000..48a6b38 ---- /dev/null -+++ b/arch/mips/mm/page-funcs.S -@@ -0,0 +1,50 @@ -+/* -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file "COPYING" in the main directory of this archive -+ * for more details. -+ * -+ * Micro-assembler generated clear_page/copy_page functions. -+ * -+ * Copyright (C) 2012 MIPS Technologies, Inc. -+ * Copyright (C) 2012 Ralf Baechle -+ */ -+#include -+#include -+ -+#ifdef CONFIG_SIBYTE_DMA_PAGEOPS -+#define cpu_clear_page_function_name clear_page_cpu -+#define cpu_copy_page_function_name copy_page_cpu -+#else -+#define cpu_clear_page_function_name clear_page -+#define cpu_copy_page_function_name copy_page -+#endif -+ -+/* -+ * Maximum sizes: -+ * -+ * R4000 128 bytes S-cache: 0x058 bytes -+ * R4600 v1.7: 0x05c bytes -+ * R4600 v2.0: 0x060 bytes -+ * With prefetching, 16 word strides 0x120 bytes -+ */ -+EXPORT(__clear_page_start) -+LEAF(cpu_clear_page_function_name) -+1: j 1b /* Dummy, will be replaced. */ -+ .space 288 -+END(cpu_clear_page_function_name) -+EXPORT(__clear_page_end) -+ -+/* -+ * Maximum sizes: -+ * -+ * R4000 128 bytes S-cache: 0x11c bytes -+ * R4600 v1.7: 0x080 bytes -+ * R4600 v2.0: 0x07c bytes -+ * With prefetching, 16 word strides 0x540 bytes -+ */ -+EXPORT(__copy_page_start) -+LEAF(cpu_copy_page_function_name) -+1: j 1b /* Dummy, will be replaced. */ -+ .space 1344 -+END(cpu_copy_page_function_name) -+EXPORT(__copy_page_end) -diff --git a/arch/mips/mm/page.c b/arch/mips/mm/page.c -index cc0b626..98f530e 100644 ---- a/arch/mips/mm/page.c -+++ b/arch/mips/mm/page.c -@@ -6,6 +6,7 @@ - * Copyright (C) 2003, 04, 05 Ralf Baechle (ralf@linux-mips.org) - * Copyright (C) 2007 Maciej W. Rozycki - * Copyright (C) 2008 Thiemo Seufer -+ * Copyright (C) 2012 MIPS Technologies, Inc. - */ - #include - #include -@@ -71,45 +72,6 @@ static struct uasm_reloc __cpuinitdata relocs[5]; - #define cpu_is_r4600_v1_x() ((read_c0_prid() & 0xfffffff0) == 0x00002010) - #define cpu_is_r4600_v2_x() ((read_c0_prid() & 0xfffffff0) == 0x00002020) - --/* -- * Maximum sizes: -- * -- * R4000 128 bytes S-cache: 0x058 bytes -- * R4600 v1.7: 0x05c bytes -- * R4600 v2.0: 0x060 bytes -- * With prefetching, 16 word strides 0x120 bytes -- */ -- --static u32 clear_page_array[0x120 / 4]; -- --#ifdef CONFIG_SIBYTE_DMA_PAGEOPS --void clear_page_cpu(void *page) __attribute__((alias("clear_page_array"))); --#else --void clear_page(void *page) __attribute__((alias("clear_page_array"))); --#endif -- --EXPORT_SYMBOL(clear_page); -- --/* -- * Maximum sizes: -- * -- * R4000 128 bytes S-cache: 0x11c bytes -- * R4600 v1.7: 0x080 bytes -- * R4600 v2.0: 0x07c bytes -- * With prefetching, 16 word strides 0x540 bytes -- */ --static u32 copy_page_array[0x540 / 4]; -- --#ifdef CONFIG_SIBYTE_DMA_PAGEOPS --void --copy_page_cpu(void *to, void *from) __attribute__((alias("copy_page_array"))); --#else --void copy_page(void *to, void *from) __attribute__((alias("copy_page_array"))); --#endif -- --EXPORT_SYMBOL(copy_page); -- -- - static int pref_bias_clear_store __cpuinitdata; - static int pref_bias_copy_load __cpuinitdata; - static int pref_bias_copy_store __cpuinitdata; -@@ -282,10 +244,15 @@ static inline void __cpuinit build_clear_pref(u32 **buf, int off) - } - } - -+extern u32 __clear_page_start; -+extern u32 __clear_page_end; -+extern u32 __copy_page_start; -+extern u32 __copy_page_end; -+ - void __cpuinit build_clear_page(void) - { - int off; -- u32 *buf = (u32 *)&clear_page_array; -+ u32 *buf = &__clear_page_start; - struct uasm_label *l = labels; - struct uasm_reloc *r = relocs; - int i; -@@ -356,17 +323,17 @@ void __cpuinit build_clear_page(void) - uasm_i_jr(&buf, RA); - uasm_i_nop(&buf); - -- BUG_ON(buf > clear_page_array + ARRAY_SIZE(clear_page_array)); -+ BUG_ON(buf > &__clear_page_end); - - uasm_resolve_relocs(relocs, labels); - - pr_debug("Synthesized clear page handler (%u instructions).\n", -- (u32)(buf - clear_page_array)); -+ (u32)(buf - &__clear_page_start)); - - pr_debug("\t.set push\n"); - pr_debug("\t.set noreorder\n"); -- for (i = 0; i < (buf - clear_page_array); i++) -- pr_debug("\t.word 0x%08x\n", clear_page_array[i]); -+ for (i = 0; i < (buf - &__clear_page_start); i++) -+ pr_debug("\t.word 0x%08x\n", (&__clear_page_start)[i]); - pr_debug("\t.set pop\n"); - } - -@@ -427,7 +394,7 @@ static inline void build_copy_store_pref(u32 **buf, int off) - void __cpuinit build_copy_page(void) - { - int off; -- u32 *buf = (u32 *)©_page_array; -+ u32 *buf = &__copy_page_start; - struct uasm_label *l = labels; - struct uasm_reloc *r = relocs; - int i; -@@ -595,21 +562,23 @@ void __cpuinit build_copy_page(void) - uasm_i_jr(&buf, RA); - uasm_i_nop(&buf); - -- BUG_ON(buf > copy_page_array + ARRAY_SIZE(copy_page_array)); -+ BUG_ON(buf > &__copy_page_end); - - uasm_resolve_relocs(relocs, labels); - - pr_debug("Synthesized copy page handler (%u instructions).\n", -- (u32)(buf - copy_page_array)); -+ (u32)(buf - &__copy_page_start)); - - pr_debug("\t.set push\n"); - pr_debug("\t.set noreorder\n"); -- for (i = 0; i < (buf - copy_page_array); i++) -- pr_debug("\t.word 0x%08x\n", copy_page_array[i]); -+ for (i = 0; i < (buf - &__copy_page_start); i++) -+ pr_debug("\t.word 0x%08x\n", (&__copy_page_start)[i]); - pr_debug("\t.set pop\n"); - } - - #ifdef CONFIG_SIBYTE_DMA_PAGEOPS -+extern void clear_page_cpu(void *page); -+extern void copy_page_cpu(void *to, void *from); - - /* - * Pad descriptors to cacheline, since each is exclusively owned by a --- -cgit v0.9.2 - diff --git a/target/linux/patches/3.4.107/mkpiggy.patch b/target/linux/patches/3.4.107/mkpiggy.patch deleted file mode 100644 index d4e815cd2..000000000 --- a/target/linux/patches/3.4.107/mkpiggy.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff -Nur linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c ---- linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c 2012-06-22 20:37:50.000000000 +0200 -+++ linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c 2012-07-03 09:48:02.000000000 +0200 -@@ -29,7 +29,14 @@ - #include - #include - #include --#include -+ -+static uint32_t getle32(const void *p) -+{ -+ const uint8_t *cp = p; -+ -+ return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + -+ ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); -+} - - int main(int argc, char *argv[]) - { -@@ -62,7 +69,7 @@ - } - - ilen = ftell(f); -- olen = get_unaligned_le32(&olen); -+ olen = getle32(&olen); - fclose(f); - - /* diff --git a/target/linux/patches/3.4.107/module-alloc-size-check.patch b/target/linux/patches/3.4.107/module-alloc-size-check.patch deleted file mode 100644 index a792ac60a..000000000 --- a/target/linux/patches/3.4.107/module-alloc-size-check.patch +++ /dev/null @@ -1,21 +0,0 @@ -https://dev.openwrt.org/browser/trunk/target/linux/generic/patches-3.3/340-module_alloc_size_check.patch?rev=30813 ---- a/kernel/module.c -+++ b/kernel/module.c -@@ -2322,12 +2322,15 @@ - - void * __weak module_alloc(unsigned long size) - { -- return size == 0 ? NULL : vmalloc_exec(size); -+ return vmalloc_exec(size); - } - - static void *module_alloc_update_bounds(unsigned long size) - { -- void *ret = module_alloc(size); -+ void *ret = NULL; -+ -+ if (size) -+ ret = module_alloc(size); - - if (ret) { - mutex_lock(&module_mutex); diff --git a/target/linux/patches/3.4.107/non-static.patch b/target/linux/patches/3.4.107/non-static.patch deleted file mode 100644 index a967703d0..000000000 --- a/target/linux/patches/3.4.107/non-static.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff -Nur linux-2.6.39-rc6.orig/fs/namei.c linux-2.6.39-rc6/fs/namei.c ---- linux-2.6.39-rc6.orig/fs/namei.c 2011-05-04 04:59:13.000000000 +0200 -+++ linux-2.6.39-rc6/fs/namei.c 2011-05-05 11:30:14.000000000 +0200 -@@ -1769,7 +1769,7 @@ - * needs parent already locked. Doesn't follow mounts. - * SMP-safe. - */ --static struct dentry *lookup_hash(struct nameidata *nd) -+struct dentry *lookup_hash(struct nameidata *nd) - { - return __lookup_hash(&nd->last, nd->path.dentry, nd); - } -diff -Nur linux-2.6.39-rc6.orig/fs/splice.c linux-2.6.39-rc6/fs/splice.c ---- linux-2.6.39-rc6.orig/fs/splice.c 2011-05-04 04:59:13.000000000 +0200 -+++ linux-2.6.39-rc6/fs/splice.c 2011-05-05 11:31:04.000000000 +0200 -@@ -1081,7 +1081,7 @@ - /* - * Attempt to initiate a splice from pipe to file. - */ --static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags) - { - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, -@@ -1109,7 +1109,7 @@ - /* - * Attempt to initiate a splice from a file to a pipe. - */ --static long do_splice_to(struct file *in, loff_t *ppos, -+long do_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) - { diff --git a/target/linux/patches/3.4.107/relocs.patch b/target/linux/patches/3.4.107/relocs.patch deleted file mode 100644 index 43c5bb580..000000000 --- a/target/linux/patches/3.4.107/relocs.patch +++ /dev/null @@ -1,3131 +0,0 @@ -diff -Nur linux-3.4.4.orig/arch/x86/tools/relocs.c linux-3.4.4/arch/x86/tools/relocs.c ---- linux-3.4.4.orig/arch/x86/tools/relocs.c 2012-06-22 20:37:50.000000000 +0200 -+++ linux-3.4.4/arch/x86/tools/relocs.c 2012-07-03 09:19:18.000000000 +0200 -@@ -5,12 +5,3123 @@ - #include - #include - #include --#include --#include -+//#include -+//#include - #define USE_BSD --#include -+//#include - #include --#include -+//#include -+ -+static inline void __put_unaligned_le16(int val, int *p) -+{ -+ *p++ = val; -+ *p++ = val >> 8; -+} -+ -+static inline void __put_unaligned_le32(int val, int *p) -+{ -+ __put_unaligned_le16(val >> 16, p + 2); -+ __put_unaligned_le16(val, p); -+} -+ -+static inline void __put_unaligned_le64(int val, int *p) -+{ -+ __put_unaligned_le32(val >> 32, p + 4); -+ __put_unaligned_le32(val, p); -+} -+ -+static inline void put_unaligned_le16(int val, void *p) -+{ -+ __put_unaligned_le16(val, p); -+} -+ -+static inline void put_unaligned_le32(int val, void *p) -+{ -+ __put_unaligned_le32(val, p); -+} -+ -+static inline void put_unaligned_le64(int val, void *p) -+{ -+ __put_unaligned_le64(val, p); -+} -+ -+/* Type for a 16-bit quantity. */ -+typedef uint16_t Elf32_Half; -+typedef uint16_t Elf64_Half; -+ -+/* Types for signed and unsigned 32-bit quantities. */ -+typedef uint32_t Elf32_Word; -+typedef int32_t Elf32_Sword; -+typedef uint32_t Elf64_Word; -+typedef int32_t Elf64_Sword; -+ -+/* Types for signed and unsigned 64-bit quantities. */ -+typedef uint64_t Elf32_Xword; -+typedef int64_t Elf32_Sxword; -+typedef uint64_t Elf64_Xword; -+typedef int64_t Elf64_Sxword; -+ -+/* Type of addresses. */ -+typedef uint32_t Elf32_Addr; -+typedef uint64_t Elf64_Addr; -+ -+/* Type of file offsets. */ -+typedef uint32_t Elf32_Off; -+typedef uint64_t Elf64_Off; -+ -+/* Type for section indices, which are 16-bit quantities. */ -+typedef uint16_t Elf32_Section; -+typedef uint16_t Elf64_Section; -+ -+/* Type for version symbol information. */ -+typedef Elf32_Half Elf32_Versym; -+typedef Elf64_Half Elf64_Versym; -+ -+ -+/* The ELF file header. This appears at the start of every ELF file. */ -+ -+#define EI_NIDENT (16) -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf32_Half e_type; /* Object file type */ -+ Elf32_Half e_machine; /* Architecture */ -+ Elf32_Word e_version; /* Object file version */ -+ Elf32_Addr e_entry; /* Entry point virtual address */ -+ Elf32_Off e_phoff; /* Program header table file offset */ -+ Elf32_Off e_shoff; /* Section header table file offset */ -+ Elf32_Word e_flags; /* Processor-specific flags */ -+ Elf32_Half e_ehsize; /* ELF header size in bytes */ -+ Elf32_Half e_phentsize; /* Program header table entry size */ -+ Elf32_Half e_phnum; /* Program header table entry count */ -+ Elf32_Half e_shentsize; /* Section header table entry size */ -+ Elf32_Half e_shnum; /* Section header table entry count */ -+ Elf32_Half e_shstrndx; /* Section header string table index */ -+} Elf32_Ehdr; -+ -+typedef struct -+{ -+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ -+ Elf64_Half e_type; /* Object file type */ -+ Elf64_Half e_machine; /* Architecture */ -+ Elf64_Word e_version; /* Object file version */ -+ Elf64_Addr e_entry; /* Entry point virtual address */ -+ Elf64_Off e_phoff; /* Program header table file offset */ -+ Elf64_Off e_shoff; /* Section header table file offset */ -+ Elf64_Word e_flags; /* Processor-specific flags */ -+ Elf64_Half e_ehsize; /* ELF header size in bytes */ -+ Elf64_Half e_phentsize; /* Program header table entry size */ -+ Elf64_Half e_phnum; /* Program header table entry count */ -+ Elf64_Half e_shentsize; /* Section header table entry size */ -+ Elf64_Half e_shnum; /* Section header table entry count */ -+ Elf64_Half e_shstrndx; /* Section header string table index */ -+} Elf64_Ehdr; -+ -+/* Fields in the e_ident array. The EI_* macros are indices into the -+ array. The macros under each EI_* macro are the values the byte -+ may have. */ -+ -+#define EI_MAG0 0 /* File identification byte 0 index */ -+#define ELFMAG0 0x7f /* Magic number byte 0 */ -+ -+#define EI_MAG1 1 /* File identification byte 1 index */ -+#define ELFMAG1 'E' /* Magic number byte 1 */ -+ -+#define EI_MAG2 2 /* File identification byte 2 index */ -+#define ELFMAG2 'L' /* Magic number byte 2 */ -+ -+#define EI_MAG3 3 /* File identification byte 3 index */ -+#define ELFMAG3 'F' /* Magic number byte 3 */ -+ -+/* Conglomeration of the identification bytes, for easy testing as a word. */ -+#define ELFMAG "\177ELF" -+#define SELFMAG 4 -+#if __BYTE_ORDER == __LITTLE_ENDIAN -+# define ELFMAG_U32 ((uint32_t)(ELFMAG0 + 0x100 * (ELFMAG1 + (0x100 * (ELFMAG2 + 0x100 * ELFMAG3))))) -+#elif __BYTE_ORDER == __BIG_ENDIAN -+# define ELFMAG_U32 ((uint32_t)((((ELFMAG0 * 0x100) + ELFMAG1) * 0x100 + ELFMAG2) * 0x100 + ELFMAG3)) -+#endif -+ -+#define EI_CLASS 4 /* File class byte index */ -+#define ELFCLASSNONE 0 /* Invalid class */ -+#define ELFCLASS32 1 /* 32-bit objects */ -+#define ELFCLASS64 2 /* 64-bit objects */ -+#define ELFCLASSNUM 3 -+ -+#define EI_DATA 5 /* Data encoding byte index */ -+#define ELFDATANONE 0 /* Invalid data encoding */ -+#define ELFDATA2LSB 1 /* 2's complement, little endian */ -+#define ELFDATA2MSB 2 /* 2's complement, big endian */ -+#define ELFDATANUM 3 -+ -+#define EI_VERSION 6 /* File version byte index */ -+ /* Value must be EV_CURRENT */ -+ -+#define EI_OSABI 7 /* OS ABI identification */ -+#define ELFOSABI_NONE 0 /* UNIX System V ABI */ -+#define ELFOSABI_SYSV 0 /* Alias. */ -+#define ELFOSABI_HPUX 1 /* HP-UX */ -+#define ELFOSABI_NETBSD 2 /* NetBSD. */ -+#define ELFOSABI_LINUX 3 /* Linux. */ -+#define ELFOSABI_HURD 4 /* GNU/Hurd */ -+#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ -+#define ELFOSABI_AIX 7 /* IBM AIX. */ -+#define ELFOSABI_IRIX 8 /* SGI Irix. */ -+#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ -+#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ -+#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ -+#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ -+#define ELFOSABI_OPENVMS 13 /* OpenVMS */ -+#define ELFOSABI_NSK 14 /* Hewlett-Packard Non-Stop Kernel */ -+#define ELFOSABI_AROS 15 /* Amiga Research OS */ -+#define ELFOSABI_ARM 97 /* ARM */ -+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ -+ -+#define EI_ABIVERSION 8 /* ABI version */ -+ -+#define EI_PAD 9 /* Byte index of padding bytes */ -+ -+/* Legal values for e_type (object file type). */ -+ -+#define ET_NONE 0 /* No file type */ -+#define ET_REL 1 /* Relocatable file */ -+#define ET_EXEC 2 /* Executable file */ -+#define ET_DYN 3 /* Shared object file */ -+#define ET_CORE 4 /* Core file */ -+#define ET_NUM 5 /* Number of defined types */ -+#define ET_LOOS 0xfe00 /* OS-specific range start */ -+#define ET_HIOS 0xfeff /* OS-specific range end */ -+#define ET_LOPROC 0xff00 /* Processor-specific range start */ -+#define ET_HIPROC 0xffff /* Processor-specific range end */ -+ -+/* Legal values for e_machine (architecture). */ -+ -+#define EM_NONE 0 /* No machine */ -+#define EM_M32 1 /* AT&T WE 32100 */ -+#define EM_SPARC 2 /* SUN SPARC */ -+#define EM_386 3 /* Intel 80386 */ -+#define EM_68K 4 /* Motorola m68k family */ -+#define EM_88K 5 /* Motorola m88k family */ -+#define EM_486 6 /* Intel 80486 *//* Reserved for future use */ -+#define EM_860 7 /* Intel 80860 */ -+#define EM_MIPS 8 /* MIPS R3000 big-endian */ -+#define EM_S370 9 /* IBM System/370 */ -+#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ -+ -+#define EM_PARISC 15 /* HPPA */ -+#define EM_VPP500 17 /* Fujitsu VPP500 */ -+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ -+#define EM_960 19 /* Intel 80960 */ -+#define EM_PPC 20 /* PowerPC */ -+#define EM_PPC64 21 /* PowerPC 64-bit */ -+#define EM_S390 22 /* IBM S390 */ -+ -+#define EM_V800 36 /* NEC V800 series */ -+#define EM_FR20 37 /* Fujitsu FR20 */ -+#define EM_RH32 38 /* TRW RH-32 */ -+#define EM_MCORE 39 /* Motorola M*Core */ /* May also be taken by Fujitsu MMA */ -+#define EM_RCE 39 /* Old name for MCore */ -+#define EM_ARM 40 /* ARM */ -+#define EM_FAKE_ALPHA 41 /* Digital Alpha */ -+#define EM_SH 42 /* Renesas SH */ -+#define EM_SPARCV9 43 /* SPARC v9 64-bit */ -+#define EM_TRICORE 44 /* Siemens Tricore */ -+#define EM_ARC 45 /* Argonaut RISC Core */ -+#define EM_H8_300 46 /* Renesas H8/300 */ -+#define EM_H8_300H 47 /* Renesas H8/300H */ -+#define EM_H8S 48 /* Renesas H8S */ -+#define EM_H8_500 49 /* Renesas H8/500 */ -+#define EM_IA_64 50 /* Intel Merced */ -+#define EM_MIPS_X 51 /* Stanford MIPS-X */ -+#define EM_COLDFIRE 52 /* Motorola Coldfire */ -+#define EM_68HC12 53 /* Motorola M68HC12 */ -+#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -+#define EM_PCP 55 /* Siemens PCP */ -+#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -+#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -+#define EM_STARCORE 58 /* Motorola Start*Core processor */ -+#define EM_ME16 59 /* Toyota ME16 processor */ -+#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -+#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -+#define EM_X86_64 62 /* AMD x86-64 architecture */ -+#define EM_PDSP 63 /* Sony DSP Processor */ -+ -+#define EM_FX66 66 /* Siemens FX66 microcontroller */ -+#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -+#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -+#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -+#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -+#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -+#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -+#define EM_SVX 73 /* Silicon Graphics SVx */ -+#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ -+#define EM_VAX 75 /* Digital VAX */ -+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ -+#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ -+#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -+#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -+#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ -+#define EM_HUANY 81 /* Harvard University machine-independent object files */ -+#define EM_PRISM 82 /* SiTera Prism */ -+#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -+#define EM_FR30 84 /* Fujitsu FR30 */ -+#define EM_D10V 85 /* Mitsubishi D10V */ -+#define EM_D30V 86 /* Mitsubishi D30V */ -+#define EM_V850 87 /* NEC v850 */ -+#define EM_M32R 88 /* Renesas M32R */ -+#define EM_MN10300 89 /* Matsushita MN10300 */ -+#define EM_MN10200 90 /* Matsushita MN10200 */ -+#define EM_PJ 91 /* picoJava */ -+#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ -+#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ -+#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ -+#define EM_IP2K 101 /* Ubicom IP2022 micro controller */ -+#define EM_CR 103 /* National Semiconductor CompactRISC */ -+#define EM_MSP430 105 /* TI msp430 micro controller */ -+#define EM_BLACKFIN 106 /* Analog Devices Blackfin */ -+#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */ -+#define EM_CRX 114 /* National Semiconductor CRX */ -+#define EM_NUM 95 -+#define EM_TI_C6000 140 -+ -+/* If it is necessary to assign new unofficial EM_* values, please pick large -+ random numbers (0x8523, 0xa7f2, etc.) to minimize the chances of collision -+ with official or non-GNU unofficial values. -+ -+ NOTE: Do not just increment the most recent number by one. -+ Somebody else somewhere will do exactly the same thing, and you -+ will have a collision. Instead, pick a random number. -+ -+ Normally, each entity or maintainer responsible for a machine with an -+ unofficial e_machine number should eventually ask registry@caldera.com for -+ an officially blessed number to be added to the list above. */ -+ -+/* picoJava */ -+#define EM_PJ_OLD 99 -+ -+/* Cygnus PowerPC ELF backend. Written in the absence of an ABI. */ -+#define EM_CYGNUS_POWERPC 0x9025 -+ -+/* Old version of Sparc v9, from before the ABI; this should be -+ removed shortly. */ -+#define EM_OLD_SPARCV9 11 -+ -+/* Old version of PowerPC, this should be removed shortly. */ -+#define EM_PPC_OLD 17 -+ -+/* (Deprecated) Temporary number for the OpenRISC processor. */ -+#define EM_OR32 0x8472 -+ -+/* Renesas M32C and M16C. */ -+#define EM_M32C 0xFEB0 -+ -+/* Cygnus M32R ELF backend. Written in the absence of an ABI. */ -+#define EM_CYGNUS_M32R 0x9041 -+ -+/* old S/390 backend magic number. Written in the absence of an ABI. */ -+#define EM_S390_OLD 0xa390 -+ -+/* D10V backend magic number. Written in the absence of an ABI. */ -+#define EM_CYGNUS_D10V 0x7650 -+ -+/* D30V backend magic number. Written in the absence of an ABI. */ -+#define EM_CYGNUS_D30V 0x7676 -+ -+/* V850 backend magic number. Written in the absense of an ABI. */ -+#define EM_CYGNUS_V850 0x9080 -+ -+/* mn10200 and mn10300 backend magic numbers. -+ Written in the absense of an ABI. */ -+#define EM_CYGNUS_MN10200 0xdead -+#define EM_CYGNUS_MN10300 0xbeef -+ -+/* FR30 magic number - no EABI available. */ -+#define EM_CYGNUS_FR30 0x3330 -+ -+/* AVR magic number -+ Written in the absense of an ABI. */ -+#define EM_AVR_OLD 0x1057 -+ -+/* OpenRISC magic number -+ Written in the absense of an ABI. */ -+#define EM_OPENRISC_OLD 0x3426 -+ -+/* DLX magic number -+ Written in the absense of an ABI. */ -+#define EM_DLX 0x5aa5 -+ -+#define EM_XSTORMY16 0xad45 -+ -+/* FRV magic number - no EABI available??. */ -+#define EM_CYGNUS_FRV 0x5441 -+ -+/* Ubicom IP2xxx; no ABI */ -+#define EM_IP2K_OLD 0x8217 -+ -+#define EM_MT 0x2530 /* Morpho MT; no ABI */ -+ -+/* MSP430 magic number -+ Written in the absense everything. */ -+#define EM_MSP430_OLD 0x1059 -+ -+/* Vitesse IQ2000. */ -+#define EM_IQ2000 0xFEBA -+ -+/* Old, unofficial value for Xtensa. */ -+#define EM_XTENSA_OLD 0xabc7 -+ -+/* Alpha backend magic number. Written in the absence of an ABI. */ -+#define EM_ALPHA 0x9026 -+ -+/* NIOS magic number - no EABI available. */ -+#define EM_NIOS32 0xFEBB -+ -+/* AVR32 magic number from ATMEL */ -+#define EM_AVR32 0x18ad -+ -+/* V850 backend magic number. Written in the absense of an ABI. */ -+#define EM_CYGNUS_V850 0x9080 -+ -+/* Legal values for e_version (version). */ -+ -+#define EV_NONE 0 /* Invalid ELF version */ -+#define EV_CURRENT 1 /* Current version */ -+#define EV_NUM 2 -+ -+/* Section header. */ -+ -+typedef struct -+{ -+ Elf32_Word sh_name; /* Section name (string tbl index) */ -+ Elf32_Word sh_type; /* Section type */ -+ Elf32_Word sh_flags; /* Section flags */ -+ Elf32_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf32_Off sh_offset; /* Section file offset */ -+ Elf32_Word sh_size; /* Section size in bytes */ -+ Elf32_Word sh_link; /* Link to another section */ -+ Elf32_Word sh_info; /* Additional section information */ -+ Elf32_Word sh_addralign; /* Section alignment */ -+ Elf32_Word sh_entsize; /* Entry size if section holds table */ -+} Elf32_Shdr; -+ -+typedef struct -+{ -+ Elf64_Word sh_name; /* Section name (string tbl index) */ -+ Elf64_Word sh_type; /* Section type */ -+ Elf64_Xword sh_flags; /* Section flags */ -+ Elf64_Addr sh_addr; /* Section virtual addr at execution */ -+ Elf64_Off sh_offset; /* Section file offset */ -+ Elf64_Xword sh_size; /* Section size in bytes */ -+ Elf64_Word sh_link; /* Link to another section */ -+ Elf64_Word sh_info; /* Additional section information */ -+ Elf64_Xword sh_addralign; /* Section alignment */ -+ Elf64_Xword sh_entsize; /* Entry size if section holds table */ -+} Elf64_Shdr; -+ -+/* Special section indices. */ -+ -+#define SHN_UNDEF 0 /* Undefined section */ -+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ -+#define SHN_LOPROC 0xff00 /* Start of processor-specific */ -+#define SHN_BEFORE 0xff00 /* Order section before all others -+ (Solaris). */ -+#define SHN_AFTER 0xff01 /* Order section after all others -+ (Solaris). */ -+#define SHN_HIPROC 0xff1f /* End of processor-specific */ -+#define SHN_LOOS 0xff20 /* Start of OS-specific */ -+#define SHN_HIOS 0xff3f /* End of OS-specific */ -+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ -+#define SHN_COMMON 0xfff2 /* Associated symbol is common */ -+#define SHN_XINDEX 0xffff /* Index is in extra table. */ -+#define SHN_HIRESERVE 0xffff /* End of reserved indices */ -+ -+/* Legal values for sh_type (section type). */ -+ -+#define SHT_NULL 0 /* Section header table entry unused */ -+#define SHT_PROGBITS 1 /* Program data */ -+#define SHT_SYMTAB 2 /* Symbol table */ -+#define SHT_STRTAB 3 /* String table */ -+#define SHT_RELA 4 /* Relocation entries with addends */ -+#define SHT_HASH 5 /* Symbol hash table */ -+#define SHT_DYNAMIC 6 /* Dynamic linking information */ -+#define SHT_NOTE 7 /* Notes */ -+#define SHT_NOBITS 8 /* Program space with no data (bss) */ -+#define SHT_REL 9 /* Relocation entries, no addends */ -+#define SHT_SHLIB 10 /* Reserved */ -+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ -+#define SHT_INIT_ARRAY 14 /* Array of constructors */ -+#define SHT_FINI_ARRAY 15 /* Array of destructors */ -+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ -+#define SHT_GROUP 17 /* Section group */ -+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ -+#define SHT_NUM 19 /* Number of defined types. */ -+#define SHT_LOOS 0x60000000 /* Start OS-specific */ -+#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ -+#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ -+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ -+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ -+#define SHT_SUNW_move 0x6ffffffa -+#define SHT_SUNW_COMDAT 0x6ffffffb -+#define SHT_SUNW_syminfo 0x6ffffffc -+#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ -+#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ -+#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ -+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ -+#define SHT_HIOS 0x6fffffff /* End OS-specific type */ -+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define SHT_LOUSER 0x80000000 /* Start of application-specific */ -+#define SHT_HIUSER 0x8fffffff /* End of application-specific */ -+ -+/* Legal values for sh_flags (section flags). */ -+ -+#define SHF_WRITE (1 << 0) /* Writable */ -+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ -+#define SHF_EXECINSTR (1 << 2) /* Executable */ -+#define SHF_MERGE (1 << 4) /* Might be merged */ -+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ -+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ -+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ -+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling -+ required */ -+#define SHF_GROUP (1 << 9) /* Section is member of a group. */ -+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ -+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ -+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ -+#define SHF_ORDERED (1 << 30) /* Special ordering requirement -+ (Solaris). */ -+#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless -+ referenced or allocated (Solaris).*/ -+ -+/* Section group handling. */ -+#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ -+ -+/* Symbol table entry. */ -+ -+typedef struct -+{ -+ Elf32_Word st_name; /* Symbol name (string tbl index) */ -+ Elf32_Addr st_value; /* Symbol value */ -+ Elf32_Word st_size; /* Symbol size */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf32_Section st_shndx; /* Section index */ -+} Elf32_Sym; -+ -+typedef struct -+{ -+ Elf64_Word st_name; /* Symbol name (string tbl index) */ -+ unsigned char st_info; /* Symbol type and binding */ -+ unsigned char st_other; /* Symbol visibility */ -+ Elf64_Section st_shndx; /* Section index */ -+ Elf64_Addr st_value; /* Symbol value */ -+ Elf64_Xword st_size; /* Symbol size */ -+} Elf64_Sym; -+ -+/* The syminfo section if available contains additional information about -+ every dynamic symbol. */ -+ -+typedef struct -+{ -+ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf32_Half si_flags; /* Per symbol flags */ -+} Elf32_Syminfo; -+ -+typedef struct -+{ -+ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ -+ Elf64_Half si_flags; /* Per symbol flags */ -+} Elf64_Syminfo; -+ -+/* Possible values for si_boundto. */ -+#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ -+#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ -+#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ -+ -+/* Possible bitmasks for si_flags. */ -+#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ -+#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ -+#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ -+#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy -+ loaded */ -+/* Syminfo version values. */ -+#define SYMINFO_NONE 0 -+#define SYMINFO_CURRENT 1 -+#define SYMINFO_NUM 2 -+ -+ -+/* How to extract and insert information held in the st_info field. */ -+ -+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) -+#define ELF32_ST_TYPE(val) ((val) & 0xf) -+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) -+ -+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ -+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) -+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) -+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) -+ -+/* Legal values for ST_BIND subfield of st_info (symbol binding). */ -+ -+#define STB_LOCAL 0 /* Local symbol */ -+#define STB_GLOBAL 1 /* Global symbol */ -+#define STB_WEAK 2 /* Weak symbol */ -+#define STB_NUM 3 /* Number of defined types. */ -+#define STB_LOOS 10 /* Start of OS-specific */ -+#define STB_HIOS 12 /* End of OS-specific */ -+#define STB_LOPROC 13 /* Start of processor-specific */ -+#define STB_HIPROC 15 /* End of processor-specific */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_NOTYPE 0 /* Symbol type is unspecified */ -+#define STT_OBJECT 1 /* Symbol is a data object */ -+#define STT_FUNC 2 /* Symbol is a code object */ -+#define STT_SECTION 3 /* Symbol associated with a section */ -+#define STT_FILE 4 /* Symbol's name is file name */ -+#define STT_COMMON 5 /* Symbol is a common data object */ -+#define STT_TLS 6 /* Symbol is thread-local data object*/ -+#define STT_NUM 7 /* Number of defined types. */ -+#define STT_LOOS 10 /* Start of OS-specific */ -+#define STT_HIOS 12 /* End of OS-specific */ -+#define STT_LOPROC 13 /* Start of processor-specific */ -+#define STT_HIPROC 15 /* End of processor-specific */ -+ -+ -+/* Symbol table indices are found in the hash buckets and chain table -+ of a symbol hash table section. This special index value indicates -+ the end of a chain, meaning no further symbols are found in that bucket. */ -+ -+#define STN_UNDEF 0 /* End of a chain. */ -+ -+ -+/* How to extract and insert information held in the st_other field. */ -+ -+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) -+ -+/* For ELF64 the definitions are the same. */ -+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) -+ -+/* Symbol visibility specification encoded in the st_other field. */ -+#define STV_DEFAULT 0 /* Default symbol visibility rules */ -+#define STV_INTERNAL 1 /* Processor specific hidden class */ -+#define STV_HIDDEN 2 /* Sym unavailable in other modules */ -+#define STV_PROTECTED 3 /* Not preemptible, not exported */ -+ -+ -+/* Relocation table entry without addend (in section of type SHT_REL). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+} Elf32_Rel; -+ -+/* I have seen two different definitions of the Elf64_Rel and -+ Elf64_Rela structures, so we'll leave them out until Novell (or -+ whoever) gets their act together. */ -+/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+} Elf64_Rel; -+ -+/* Relocation table entry with addend (in section of type SHT_RELA). */ -+ -+typedef struct -+{ -+ Elf32_Addr r_offset; /* Address */ -+ Elf32_Word r_info; /* Relocation type and symbol index */ -+ Elf32_Sword r_addend; /* Addend */ -+} Elf32_Rela; -+ -+typedef struct -+{ -+ Elf64_Addr r_offset; /* Address */ -+ Elf64_Xword r_info; /* Relocation type and symbol index */ -+ Elf64_Sxword r_addend; /* Addend */ -+} Elf64_Rela; -+ -+/* How to extract and insert information held in the r_info field. */ -+ -+#define ELF32_R_SYM(val) ((val) >> 8) -+#define ELF32_R_TYPE(val) ((val) & 0xff) -+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) -+ -+#define ELF64_R_SYM(i) ((i) >> 32) -+#define ELF64_R_TYPE(i) ((i) & 0xffffffff) -+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) -+ -+/* Program segment header. */ -+ -+typedef struct -+{ -+ Elf32_Word p_type; /* Segment type */ -+ Elf32_Off p_offset; /* Segment file offset */ -+ Elf32_Addr p_vaddr; /* Segment virtual address */ -+ Elf32_Addr p_paddr; /* Segment physical address */ -+ Elf32_Word p_filesz; /* Segment size in file */ -+ Elf32_Word p_memsz; /* Segment size in memory */ -+ Elf32_Word p_flags; /* Segment flags */ -+ Elf32_Word p_align; /* Segment alignment */ -+} Elf32_Phdr; -+ -+typedef struct -+{ -+ Elf64_Word p_type; /* Segment type */ -+ Elf64_Word p_flags; /* Segment flags */ -+ Elf64_Off p_offset; /* Segment file offset */ -+ Elf64_Addr p_vaddr; /* Segment virtual address */ -+ Elf64_Addr p_paddr; /* Segment physical address */ -+ Elf64_Xword p_filesz; /* Segment size in file */ -+ Elf64_Xword p_memsz; /* Segment size in memory */ -+ Elf64_Xword p_align; /* Segment alignment */ -+} Elf64_Phdr; -+ -+/* Legal values for p_type (segment type). */ -+ -+#define PT_NULL 0 /* Program header table entry unused */ -+#define PT_LOAD 1 /* Loadable program segment */ -+#define PT_DYNAMIC 2 /* Dynamic linking information */ -+#define PT_INTERP 3 /* Program interpreter */ -+#define PT_NOTE 4 /* Auxiliary information */ -+#define PT_SHLIB 5 /* Reserved */ -+#define PT_PHDR 6 /* Entry for header table itself */ -+#define PT_TLS 7 /* Thread-local storage segment */ -+#define PT_NUM 8 /* Number of defined types */ -+#define PT_LOOS 0x60000000 /* Start of OS-specific */ -+#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ -+#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ -+#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ -+#define PT_PAX_FLAGS 0x65041580 /* Indicates PaX flag markings */ -+#define PT_LOSUNW 0x6ffffffa -+#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ -+#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ -+#define PT_HISUNW 0x6fffffff -+#define PT_HIOS 0x6fffffff /* End of OS-specific */ -+#define PT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define PT_HIPROC 0x7fffffff /* End of processor-specific */ -+ -+/* Legal values for p_flags (segment flags). */ -+ -+#define PF_X (1 << 0) /* Segment is executable */ -+#define PF_W (1 << 1) /* Segment is writable */ -+#define PF_R (1 << 2) /* Segment is readable */ -+#define PF_PAGEEXEC (1 << 4) /* Enable PAGEEXEC */ -+#define PF_NOPAGEEXEC (1 << 5) /* Disable PAGEEXEC */ -+#define PF_SEGMEXEC (1 << 6) /* Enable SEGMEXEC */ -+#define PF_NOSEGMEXEC (1 << 7) /* Disable SEGMEXEC */ -+#define PF_MPROTECT (1 << 8) /* Enable MPROTECT */ -+#define PF_NOMPROTECT (1 << 9) /* Disable MPROTECT */ -+#define PF_RANDEXEC (1 << 10) /* Enable RANDEXEC */ -+#define PF_NORANDEXEC (1 << 11) /* Disable RANDEXEC */ -+#define PF_EMUTRAMP (1 << 12) /* Enable EMUTRAMP */ -+#define PF_NOEMUTRAMP (1 << 13) /* Disable EMUTRAMP */ -+#define PF_RANDMMAP (1 << 14) /* Enable RANDMMAP */ -+#define PF_NORANDMMAP (1 << 15) /* Disable RANDMMAP */ -+#define PF_MASKOS 0x0ff00000 /* OS-specific */ -+#define PF_MASKPROC 0xf0000000 /* Processor-specific */ -+ -+/* Legal values for note segment descriptor types for core files. */ -+ -+#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ -+#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ -+#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ -+#define NT_PRXREG 4 /* Contains copy of prxregset struct */ -+#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ -+#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ -+#define NT_AUXV 6 /* Contains copy of auxv array */ -+#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ -+#define NT_ASRS 8 /* Contains copy of asrset struct */ -+#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ -+#define NT_PSINFO 13 /* Contains copy of psinfo struct */ -+#define NT_PRCRED 14 /* Contains copy of prcred struct */ -+#define NT_UTSNAME 15 /* Contains copy of utsname struct */ -+#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ -+#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ -+#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ -+ -+/* Legal values for the note segment descriptor types for object files. */ -+ -+#define NT_VERSION 1 /* Contains a version string. */ -+ -+ -+/* Dynamic section entry. */ -+ -+typedef struct -+{ -+ Elf32_Sword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf32_Word d_val; /* Integer value */ -+ Elf32_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf32_Dyn; -+ -+typedef struct -+{ -+ Elf64_Sxword d_tag; /* Dynamic entry type */ -+ union -+ { -+ Elf64_Xword d_val; /* Integer value */ -+ Elf64_Addr d_ptr; /* Address value */ -+ } d_un; -+} Elf64_Dyn; -+ -+/* Legal values for d_tag (dynamic entry type). */ -+ -+#define DT_NULL 0 /* Marks end of dynamic section */ -+#define DT_NEEDED 1 /* Name of needed library */ -+#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ -+#define DT_PLTGOT 3 /* Processor defined value */ -+#define DT_HASH 4 /* Address of symbol hash table */ -+#define DT_STRTAB 5 /* Address of string table */ -+#define DT_SYMTAB 6 /* Address of symbol table */ -+#define DT_RELA 7 /* Address of Rela relocs */ -+#define DT_RELASZ 8 /* Total size of Rela relocs */ -+#define DT_RELAENT 9 /* Size of one Rela reloc */ -+#define DT_STRSZ 10 /* Size of string table */ -+#define DT_SYMENT 11 /* Size of one symbol table entry */ -+#define DT_INIT 12 /* Address of init function */ -+#define DT_FINI 13 /* Address of termination function */ -+#define DT_SONAME 14 /* Name of shared object */ -+#define DT_RPATH 15 /* Library search path (deprecated) */ -+#define DT_SYMBOLIC 16 /* Start symbol search here */ -+#define DT_REL 17 /* Address of Rel relocs */ -+#define DT_RELSZ 18 /* Total size of Rel relocs */ -+#define DT_RELENT 19 /* Size of one Rel reloc */ -+#define DT_PLTREL 20 /* Type of reloc in PLT */ -+#define DT_DEBUG 21 /* For debugging; unspecified */ -+#define DT_TEXTREL 22 /* Reloc might modify .text */ -+#define DT_JMPREL 23 /* Address of PLT relocs */ -+#define DT_BIND_NOW 24 /* Process relocations of object */ -+#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ -+#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ -+#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ -+#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ -+#define DT_RUNPATH 29 /* Library search path */ -+#define DT_FLAGS 30 /* Flags for the object being loaded */ -+#define DT_ENCODING 32 /* Start of encoded range */ -+#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ -+#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ -+#define DT_NUM 34 /* Number used */ -+#define DT_LOOS 0x6000000d /* Start of OS-specific */ -+#define DT_HIOS 0x6ffff000 /* End of OS-specific */ -+#define DT_LOPROC 0x70000000 /* Start of processor-specific */ -+#define DT_HIPROC 0x7fffffff /* End of processor-specific */ -+#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ -+ -+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the -+ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's -+ approach. */ -+#define DT_VALRNGLO 0x6ffffd00 -+#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ -+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ -+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ -+#define DT_CHECKSUM 0x6ffffdf8 -+#define DT_PLTPADSZ 0x6ffffdf9 -+#define DT_MOVEENT 0x6ffffdfa -+#define DT_MOVESZ 0x6ffffdfb -+#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ -+#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting -+ the following DT_* entry. */ -+#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ -+#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ -+#define DT_VALRNGHI 0x6ffffdff -+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ -+#define DT_VALNUM 12 -+ -+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the -+ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. -+ -+ If any adjustment is made to the ELF object after it has been -+ built these entries will need to be adjusted. */ -+#define DT_ADDRRNGLO 0x6ffffe00 -+#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ -+#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ -+#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ -+#define DT_CONFIG 0x6ffffefa /* Configuration information. */ -+#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ -+#define DT_AUDIT 0x6ffffefc /* Object auditing. */ -+#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ -+#define DT_MOVETAB 0x6ffffefe /* Move table. */ -+#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ -+#define DT_ADDRRNGHI 0x6ffffeff -+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ -+#define DT_ADDRNUM 10 -+ -+/* The versioning entry types. The next are defined as part of the -+ GNU extension. */ -+#define DT_VERSYM 0x6ffffff0 -+ -+#define DT_RELACOUNT 0x6ffffff9 -+#define DT_RELCOUNT 0x6ffffffa -+ -+/* These were chosen by Sun. */ -+#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ -+#define DT_VERDEF 0x6ffffffc /* Address of version definition -+ table */ -+#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ -+#define DT_VERNEED 0x6ffffffe /* Address of table with needed -+ versions */ -+#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ -+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ -+#define DT_VERSIONTAGNUM 16 -+ -+/* Sun added these machine-independent extensions in the "processor-specific" -+ range. Be compatible. */ -+#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ -+#define DT_FILTER 0x7fffffff /* Shared object to get values from */ -+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) -+#define DT_EXTRANUM 3 -+ -+/* Values of `d_un.d_val' in the DT_FLAGS entry. */ -+#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ -+#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ -+#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ -+#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ -+#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ -+ -+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 -+ entry in the dynamic section. */ -+#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ -+#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ -+#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ -+#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ -+#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ -+#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ -+#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ -+#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ -+#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ -+#define DF_1_TRANS 0x00000200 -+#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ -+#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ -+#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ -+#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ -+#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ -+#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ -+#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ -+ -+/* Flags for the feature selection in DT_FEATURE_1. */ -+#define DTF_1_PARINIT 0x00000001 -+#define DTF_1_CONFEXP 0x00000002 -+ -+/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ -+#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ -+#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not -+ generally available. */ -+ -+/* Version definition sections. */ -+ -+typedef struct -+{ -+ Elf32_Half vd_version; /* Version revision */ -+ Elf32_Half vd_flags; /* Version information */ -+ Elf32_Half vd_ndx; /* Version Index */ -+ Elf32_Half vd_cnt; /* Number of associated aux entries */ -+ Elf32_Word vd_hash; /* Version name hash value */ -+ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf32_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf32_Verdef; -+ -+typedef struct -+{ -+ Elf64_Half vd_version; /* Version revision */ -+ Elf64_Half vd_flags; /* Version information */ -+ Elf64_Half vd_ndx; /* Version Index */ -+ Elf64_Half vd_cnt; /* Number of associated aux entries */ -+ Elf64_Word vd_hash; /* Version name hash value */ -+ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ -+ Elf64_Word vd_next; /* Offset in bytes to next verdef -+ entry */ -+} Elf64_Verdef; -+ -+ -+/* Legal values for vd_version (version revision). */ -+#define VER_DEF_NONE 0 /* No version */ -+#define VER_DEF_CURRENT 1 /* Current version */ -+#define VER_DEF_NUM 2 /* Given version number */ -+ -+/* Legal values for vd_flags (version information flags). */ -+#define VER_FLG_BASE 0x1 /* Version definition of file itself */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+/* Versym symbol index values. */ -+#define VER_NDX_LOCAL 0 /* Symbol is local. */ -+#define VER_NDX_GLOBAL 1 /* Symbol is global. */ -+#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ -+#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ -+ -+/* Auxialiary version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vda_name; /* Version or dependency names */ -+ Elf32_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf32_Verdaux; -+ -+typedef struct -+{ -+ Elf64_Word vda_name; /* Version or dependency names */ -+ Elf64_Word vda_next; /* Offset in bytes to next verdaux -+ entry */ -+} Elf64_Verdaux; -+ -+ -+/* Version dependency section. */ -+ -+typedef struct -+{ -+ Elf32_Half vn_version; /* Version of structure */ -+ Elf32_Half vn_cnt; /* Number of associated aux entries */ -+ Elf32_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf32_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf32_Verneed; -+ -+typedef struct -+{ -+ Elf64_Half vn_version; /* Version of structure */ -+ Elf64_Half vn_cnt; /* Number of associated aux entries */ -+ Elf64_Word vn_file; /* Offset of filename for this -+ dependency */ -+ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ -+ Elf64_Word vn_next; /* Offset in bytes to next verneed -+ entry */ -+} Elf64_Verneed; -+ -+ -+/* Legal values for vn_version (version revision). */ -+#define VER_NEED_NONE 0 /* No version */ -+#define VER_NEED_CURRENT 1 /* Current version */ -+#define VER_NEED_NUM 2 /* Given version number */ -+ -+/* Auxiliary needed version information. */ -+ -+typedef struct -+{ -+ Elf32_Word vna_hash; /* Hash value of dependency name */ -+ Elf32_Half vna_flags; /* Dependency specific information */ -+ Elf32_Half vna_other; /* Unused */ -+ Elf32_Word vna_name; /* Dependency name string offset */ -+ Elf32_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf32_Vernaux; -+ -+typedef struct -+{ -+ Elf64_Word vna_hash; /* Hash value of dependency name */ -+ Elf64_Half vna_flags; /* Dependency specific information */ -+ Elf64_Half vna_other; /* Unused */ -+ Elf64_Word vna_name; /* Dependency name string offset */ -+ Elf64_Word vna_next; /* Offset in bytes to next vernaux -+ entry */ -+} Elf64_Vernaux; -+ -+ -+/* Legal values for vna_flags. */ -+#define VER_FLG_WEAK 0x2 /* Weak version identifier */ -+ -+ -+/* Auxiliary vector. */ -+ -+/* This vector is normally only used by the program interpreter. The -+ usual definition in an ABI supplement uses the name auxv_t. The -+ vector is not usually defined in a standard file, but it -+ can't hurt. We rename it to avoid conflicts. The sizes of these -+ types are an arrangement between the exec server and the program -+ interpreter, so we don't fully specify them here. */ -+ -+typedef struct -+{ -+ uint32_t a_type; /* Entry type */ -+ union -+ { -+ uint32_t a_val; /* Integer value */ -+ /* We use to have pointer elements added here. We cannot do that, -+ though, since it does not work when using 32-bit definitions -+ on 64-bit platforms and vice versa. */ -+ } a_un; -+} Elf32_auxv_t; -+ -+typedef struct -+{ -+ uint64_t a_type; /* Entry type */ -+ union -+ { -+ uint64_t a_val; /* Integer value */ -+ /* We use to have pointer elements added here. We cannot do that, -+ though, since it does not work when using 32-bit definitions -+ on 64-bit platforms and vice versa. */ -+ } a_un; -+} Elf64_auxv_t; -+ -+/* Legal values for a_type (entry type). */ -+ -+#define AT_NULL 0 /* End of vector */ -+#define AT_IGNORE 1 /* Entry should be ignored */ -+#define AT_EXECFD 2 /* File descriptor of program */ -+#define AT_PHDR 3 /* Program headers for program */ -+#define AT_PHENT 4 /* Size of program header entry */ -+#define AT_PHNUM 5 /* Number of program headers */ -+#define AT_PAGESZ 6 /* System page size */ -+#define AT_BASE 7 /* Base address of interpreter */ -+#define AT_FLAGS 8 /* Flags */ -+#define AT_ENTRY 9 /* Entry point of program */ -+#define AT_NOTELF 10 /* Program is not ELF */ -+#define AT_UID 11 /* Real uid */ -+#define AT_EUID 12 /* Effective uid */ -+#define AT_GID 13 /* Real gid */ -+#define AT_EGID 14 /* Effective gid */ -+#define AT_CLKTCK 17 /* Frequency of times() */ -+ -+/* Some more special a_type values describing the hardware. */ -+#define AT_PLATFORM 15 /* String identifying platform. */ -+#define AT_HWCAP 16 /* Machine dependent hints about -+ processor capabilities. */ -+ -+/* This entry gives some information about the FPU initialization -+ performed by the kernel. */ -+#define AT_FPUCW 18 /* Used FPU control word. */ -+ -+/* Cache block sizes. */ -+#define AT_DCACHEBSIZE 19 /* Data cache block size. */ -+#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ -+#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ -+ -+/* A special ignored value for PPC, used by the kernel to control the -+ interpretation of the AUXV. Must be > 16. */ -+#define AT_IGNOREPPC 22 /* Entry should be ignored. */ -+ -+#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ -+ -+/* Pointer to the global system page used for system calls and other -+ nice things. */ -+#define AT_SYSINFO 32 -+#define AT_SYSINFO_EHDR 33 -+ -+/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains -+ log2 of line size; mask those to get cache size. */ -+#define AT_L1I_CACHESHAPE 34 -+#define AT_L1D_CACHESHAPE 35 -+#define AT_L2_CACHESHAPE 36 -+#define AT_L3_CACHESHAPE 37 -+ -+/* Note section contents. Each entry in the note section begins with -+ a header of a fixed form. */ -+ -+typedef struct -+{ -+ Elf32_Word n_namesz; /* Length of the note's name. */ -+ Elf32_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf32_Word n_type; /* Type of the note. */ -+} Elf32_Nhdr; -+ -+typedef struct -+{ -+ Elf64_Word n_namesz; /* Length of the note's name. */ -+ Elf64_Word n_descsz; /* Length of the note's descriptor. */ -+ Elf64_Word n_type; /* Type of the note. */ -+} Elf64_Nhdr; -+ -+/* Known names of notes. */ -+ -+/* Solaris entries in the note section have this name. */ -+#define ELF_NOTE_SOLARIS "SUNW Solaris" -+ -+/* Note entries for GNU systems have this name. */ -+#define ELF_NOTE_GNU "GNU" -+ -+ -+/* Defined types of notes for Solaris. */ -+ -+/* Value of descriptor (one word) is desired pagesize for the binary. */ -+#define ELF_NOTE_PAGESIZE_HINT 1 -+ -+ -+/* Defined note types for GNU systems. */ -+ -+/* ABI information. The descriptor consists of words: -+ word 0: OS descriptor -+ word 1: major version of the ABI -+ word 2: minor version of the ABI -+ word 3: subminor version of the ABI -+*/ -+#define ELF_NOTE_ABI 1 -+ -+/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI -+ note section entry. */ -+#define ELF_NOTE_OS_LINUX 0 -+#define ELF_NOTE_OS_GNU 1 -+#define ELF_NOTE_OS_SOLARIS2 2 -+#define ELF_NOTE_OS_FREEBSD 3 -+ -+ -+/* Move records. */ -+typedef struct -+{ -+ Elf32_Xword m_value; /* Symbol value. */ -+ Elf32_Word m_info; /* Size and index. */ -+ Elf32_Word m_poffset; /* Symbol offset. */ -+ Elf32_Half m_repeat; /* Repeat count. */ -+ Elf32_Half m_stride; /* Stride info. */ -+} Elf32_Move; -+ -+typedef struct -+{ -+ Elf64_Xword m_value; /* Symbol value. */ -+ Elf64_Xword m_info; /* Size and index. */ -+ Elf64_Xword m_poffset; /* Symbol offset. */ -+ Elf64_Half m_repeat; /* Repeat count. */ -+ Elf64_Half m_stride; /* Stride info. */ -+} Elf64_Move; -+ -+/* Macro to construct move records. */ -+#define ELF32_M_SYM(info) ((info) >> 8) -+#define ELF32_M_SIZE(info) ((unsigned char) (info)) -+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) -+ -+#define ELF64_M_SYM(info) ELF32_M_SYM (info) -+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) -+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) -+ -+ -+/* Motorola 68k specific definitions. */ -+ -+/* Values for Elf32_Ehdr.e_flags. */ -+#define EF_CPU32 0x00810000 -+ -+/* m68k relocs. */ -+ -+#define R_68K_NONE 0 /* No reloc */ -+#define R_68K_32 1 /* Direct 32 bit */ -+#define R_68K_16 2 /* Direct 16 bit */ -+#define R_68K_8 3 /* Direct 8 bit */ -+#define R_68K_PC32 4 /* PC relative 32 bit */ -+#define R_68K_PC16 5 /* PC relative 16 bit */ -+#define R_68K_PC8 6 /* PC relative 8 bit */ -+#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ -+#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ -+#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ -+#define R_68K_GOT32O 10 /* 32 bit GOT offset */ -+#define R_68K_GOT16O 11 /* 16 bit GOT offset */ -+#define R_68K_GOT8O 12 /* 8 bit GOT offset */ -+#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ -+#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ -+#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ -+#define R_68K_PLT32O 16 /* 32 bit PLT offset */ -+#define R_68K_PLT16O 17 /* 16 bit PLT offset */ -+#define R_68K_PLT8O 18 /* 8 bit PLT offset */ -+#define R_68K_COPY 19 /* Copy symbol at runtime */ -+#define R_68K_GLOB_DAT 20 /* Create GOT entry */ -+#define R_68K_JMP_SLOT 21 /* Create PLT entry */ -+#define R_68K_RELATIVE 22 /* Adjust by program base */ -+/* Keep this the last entry. */ -+#define R_68K_NUM 23 -+ -+/* Intel 80386 specific definitions. */ -+ -+/* i386 relocs. */ -+ -+#define R_386_NONE 0 /* No reloc */ -+#define R_386_32 1 /* Direct 32 bit */ -+#define R_386_PC32 2 /* PC relative 32 bit */ -+#define R_386_GOT32 3 /* 32 bit GOT entry */ -+#define R_386_PLT32 4 /* 32 bit PLT address */ -+#define R_386_COPY 5 /* Copy symbol at runtime */ -+#define R_386_GLOB_DAT 6 /* Create GOT entry */ -+#define R_386_JMP_SLOT 7 /* Create PLT entry */ -+#define R_386_RELATIVE 8 /* Adjust by program base */ -+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ -+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ -+#define R_386_32PLT 11 -+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ -+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS -+ block offset */ -+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block -+ offset */ -+#define R_386_TLS_LE 17 /* Offset relative to static TLS -+ block */ -+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of -+ general dynamic thread local data */ -+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of -+ local dynamic thread local data -+ in LE code */ -+#define R_386_16 20 -+#define R_386_PC16 21 -+#define R_386_8 22 -+#define R_386_PC8 23 -+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic -+ thread local data */ -+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ -+#define R_386_TLS_GD_CALL 26 /* Relocation for call to -+ __tls_get_addr() */ -+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ -+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic -+ thread local data in LE code */ -+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ -+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to -+ __tls_get_addr() in LDM code */ -+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ -+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ -+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS -+ block offset */ -+#define R_386_TLS_LE_32 34 /* Negated offset relative to static -+ TLS block */ -+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ -+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ -+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ -+/* Keep this the last entry. */ -+#define R_386_NUM 38 -+ -+/* Blackfin specific definitions. */ -+#define R_BFIN_UNUSED0 0x00 -+#define R_BFIN_PCREL5M2 0x01 -+#define R_BFIN_UNUSED1 0x02 -+#define R_BFIN_PCREL10 0x03 -+#define R_BFIN_PCREL12_JUMP 0x04 -+#define R_BFIN_RIMM16 0x05 -+#define R_BFIN_LUIMM16 0x06 -+#define R_BFIN_HUIMM16 0x07 -+#define R_BFIN_PCREL12_JUMP_S 0x08 -+#define R_BFIN_PCREL24_JUMP_X 0x09 -+#define R_BFIN_PCREL24 0x0a -+#define R_BFIN_UNUSEDB 0x0b -+#define R_BFIN_UNUSEDC 0x0c -+#define R_BFIN_PCREL24_JUMP_L 0x0d -+#define R_BFIN_PCREL24_CALL_X 0x0e -+#define R_BFIN_var_eq_symb 0x0f -+#define R_BFIN_BYTE_DATA 0x10 -+#define R_BFIN_BYTE2_DATA 0x11 -+#define R_BFIN_BYTE4_DATA 0x12 -+#define R_BFIN_PCREL11 0x13 -+ -+#define R_BFIN_GOT17M4 0x14 -+#define R_BFIN_GOTHI 0x15 -+#define R_BFIN_GOTLO 0x16 -+#define R_BFIN_FUNCDESC 0x17 -+#define R_BFIN_FUNCDESC_GOT17M4 0x18 -+#define R_BFIN_FUNCDESC_GOTHI 0x19 -+#define R_BFIN_FUNCDESC_GOTLO 0x1a -+#define R_BFIN_FUNCDESC_VALUE 0x1b -+#define R_BFIN_FUNCDESC_GOTOFF17M4 0x1c -+#define R_BFIN_FUNCDESC_GOTOFFHI 0x1d -+#define R_BFIN_FUNCDESC_GOTOFFLO 0x1e -+#define R_BFIN_GOTOFF17M4 0x1f -+#define R_BFIN_GOTOFFHI 0x20 -+#define R_BFIN_GOTOFFLO 0x21 -+ -+#define EF_BFIN_PIC 0x00000001 /* -fpic */ -+#define EF_BFIN_FDPIC 0x00000002 /* -mfdpic */ -+#define EF_BFIN_CODE_IN_L1 0x00000010 /* --code-in-l1 */ -+#define EF_BFIN_DATA_IN_L1 0x00000020 /* --data-in-l1 */ -+ -+/* FR-V specific definitions. */ -+#define R_FRV_NONE 0 /* No reloc. */ -+#define R_FRV_32 1 /* Direct 32 bit. */ -+/* Canonical function descriptor address. */ -+#define R_FRV_FUNCDESC 14 -+/* Private function descriptor initialization. */ -+#define R_FRV_FUNCDESC_VALUE 18 -+ -+ /* gpr support */ -+#define EF_FRV_GPR_MASK 0x00000003 /* mask for # of gprs */ -+#define EF_FRV_GPR_32 0x00000001 /* -mgpr-32 */ -+#define EF_FRV_GPR_64 0x00000002 /* -mgpr-64 */ -+ -+ /* fpr support */ -+#define EF_FRV_FPR_MASK 0x0000000c /* mask for # of fprs */ -+#define EF_FRV_FPR_32 0x00000004 /* -mfpr-32 */ -+#define EF_FRV_FPR_64 0x00000008 /* -mfpr-64 */ -+#define EF_FRV_FPR_NONE 0x0000000c /* -msoft-float */ -+ -+#define EF_FRV_PIC 0x00000100 -+#define EF_FRV_FDPIC 0x00008000 -+ -+/* SUN SPARC specific definitions. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ -+ -+/* Values for Elf64_Ehdr.e_flags. */ -+ -+#define EF_SPARCV9_MM 3 -+#define EF_SPARCV9_TSO 0 -+#define EF_SPARCV9_PSO 1 -+#define EF_SPARCV9_RMO 2 -+#define EF_SPARC_LEDATA 0x800000 /* little endian data */ -+#define EF_SPARC_EXT_MASK 0xFFFF00 -+#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ -+#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ -+#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ -+#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ -+ -+/* SPARC relocs. */ -+ -+#define R_SPARC_NONE 0 /* No reloc */ -+#define R_SPARC_8 1 /* Direct 8 bit */ -+#define R_SPARC_16 2 /* Direct 16 bit */ -+#define R_SPARC_32 3 /* Direct 32 bit */ -+#define R_SPARC_DISP8 4 /* PC relative 8 bit */ -+#define R_SPARC_DISP16 5 /* PC relative 16 bit */ -+#define R_SPARC_DISP32 6 /* PC relative 32 bit */ -+#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ -+#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ -+#define R_SPARC_HI22 9 /* High 22 bit */ -+#define R_SPARC_22 10 /* Direct 22 bit */ -+#define R_SPARC_13 11 /* Direct 13 bit */ -+#define R_SPARC_LO10 12 /* Truncated 10 bit */ -+#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ -+#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ -+#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ -+#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ -+#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ -+#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ -+#define R_SPARC_COPY 19 /* Copy symbol at runtime */ -+#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ -+#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ -+#define R_SPARC_RELATIVE 22 /* Adjust by program base */ -+#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ -+ -+/* Additional Sparc64 relocs. */ -+ -+#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ -+#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ -+#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ -+#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ -+#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ -+#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ -+#define R_SPARC_10 30 /* Direct 10 bit */ -+#define R_SPARC_11 31 /* Direct 11 bit */ -+#define R_SPARC_64 32 /* Direct 64 bit */ -+#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ -+#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ -+#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ -+#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ -+#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ -+#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ -+#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ -+#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ -+#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ -+#define R_SPARC_7 43 /* Direct 7 bit */ -+#define R_SPARC_5 44 /* Direct 5 bit */ -+#define R_SPARC_6 45 /* Direct 6 bit */ -+#define R_SPARC_DISP64 46 /* PC relative 64 bit */ -+#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ -+#define R_SPARC_HIX22 48 /* High 22 bit complemented */ -+#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ -+#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ -+#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ -+#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ -+#define R_SPARC_REGISTER 53 /* Global register usage */ -+#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ -+#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ -+#define R_SPARC_TLS_GD_HI22 56 -+#define R_SPARC_TLS_GD_LO10 57 -+#define R_SPARC_TLS_GD_ADD 58 -+#define R_SPARC_TLS_GD_CALL 59 -+#define R_SPARC_TLS_LDM_HI22 60 -+#define R_SPARC_TLS_LDM_LO10 61 -+#define R_SPARC_TLS_LDM_ADD 62 -+#define R_SPARC_TLS_LDM_CALL 63 -+#define R_SPARC_TLS_LDO_HIX22 64 -+#define R_SPARC_TLS_LDO_LOX10 65 -+#define R_SPARC_TLS_LDO_ADD 66 -+#define R_SPARC_TLS_IE_HI22 67 -+#define R_SPARC_TLS_IE_LO10 68 -+#define R_SPARC_TLS_IE_LD 69 -+#define R_SPARC_TLS_IE_LDX 70 -+#define R_SPARC_TLS_IE_ADD 71 -+#define R_SPARC_TLS_LE_HIX22 72 -+#define R_SPARC_TLS_LE_LOX10 73 -+#define R_SPARC_TLS_DTPMOD32 74 -+#define R_SPARC_TLS_DTPMOD64 75 -+#define R_SPARC_TLS_DTPOFF32 76 -+#define R_SPARC_TLS_DTPOFF64 77 -+#define R_SPARC_TLS_TPOFF32 78 -+#define R_SPARC_TLS_TPOFF64 79 -+/* Keep this the last entry. */ -+#define R_SPARC_NUM 80 -+ -+/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ -+ -+#define DT_SPARC_REGISTER 0x70000001 -+#define DT_SPARC_NUM 2 -+ -+/* Bits present in AT_HWCAP, primarily for Sparc32. */ -+ -+#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ -+#define HWCAP_SPARC_STBAR 2 -+#define HWCAP_SPARC_SWAP 4 -+#define HWCAP_SPARC_MULDIV 8 -+#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ -+#define HWCAP_SPARC_ULTRA3 32 -+ -+/* MIPS R3000 specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ -+#define EF_MIPS_PIC 2 /* Contains PIC code */ -+#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ -+#define EF_MIPS_XGOT 8 -+#define EF_MIPS_64BIT_WHIRL 16 -+#define EF_MIPS_ABI2 32 -+#define EF_MIPS_ABI_ON32 64 -+#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ -+ -+/* Legal values for MIPS architecture level. */ -+ -+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* The following are non-official names and should not be used. */ -+ -+#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ -+#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ -+#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ -+#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ -+#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ -+#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ -+#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ -+ -+/* Special section indices. */ -+ -+#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ -+#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ -+#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ -+#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ -+#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ -+#define SHT_MIPS_MSYM 0x70000001 -+#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ -+#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ -+#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ -+#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ -+#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ -+#define SHT_MIPS_PACKAGE 0x70000007 -+#define SHT_MIPS_PACKSYM 0x70000008 -+#define SHT_MIPS_RELD 0x70000009 -+#define SHT_MIPS_IFACE 0x7000000b -+#define SHT_MIPS_CONTENT 0x7000000c -+#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ -+#define SHT_MIPS_SHDR 0x70000010 -+#define SHT_MIPS_FDESC 0x70000011 -+#define SHT_MIPS_EXTSYM 0x70000012 -+#define SHT_MIPS_DENSE 0x70000013 -+#define SHT_MIPS_PDESC 0x70000014 -+#define SHT_MIPS_LOCSYM 0x70000015 -+#define SHT_MIPS_AUXSYM 0x70000016 -+#define SHT_MIPS_OPTSYM 0x70000017 -+#define SHT_MIPS_LOCSTR 0x70000018 -+#define SHT_MIPS_LINE 0x70000019 -+#define SHT_MIPS_RFDESC 0x7000001a -+#define SHT_MIPS_DELTASYM 0x7000001b -+#define SHT_MIPS_DELTAINST 0x7000001c -+#define SHT_MIPS_DELTACLASS 0x7000001d -+#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ -+#define SHT_MIPS_DELTADECL 0x7000001f -+#define SHT_MIPS_SYMBOL_LIB 0x70000020 -+#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ -+#define SHT_MIPS_TRANSLATE 0x70000022 -+#define SHT_MIPS_PIXIE 0x70000023 -+#define SHT_MIPS_XLATE 0x70000024 -+#define SHT_MIPS_XLATE_DEBUG 0x70000025 -+#define SHT_MIPS_WHIRL 0x70000026 -+#define SHT_MIPS_EH_REGION 0x70000027 -+#define SHT_MIPS_XLATE_OLD 0x70000028 -+#define SHT_MIPS_PDR_EXCEPTION 0x70000029 -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ -+#define SHF_MIPS_MERGE 0x20000000 -+#define SHF_MIPS_ADDR 0x40000000 -+#define SHF_MIPS_STRINGS 0x80000000 -+#define SHF_MIPS_NOSTRIP 0x08000000 -+#define SHF_MIPS_LOCAL 0x04000000 -+#define SHF_MIPS_NAMES 0x02000000 -+#define SHF_MIPS_NODUPE 0x01000000 -+ -+ -+/* Symbol tables. */ -+ -+/* MIPS specific values for `st_other'. */ -+#define STO_MIPS_DEFAULT 0x0 -+#define STO_MIPS_INTERNAL 0x1 -+#define STO_MIPS_HIDDEN 0x2 -+#define STO_MIPS_PROTECTED 0x3 -+#define STO_MIPS_PLT 0x8 -+#define STO_MIPS_SC_ALIGN_UNUSED 0xff -+ -+/* MIPS specific values for `st_info'. */ -+#define STB_MIPS_SPLIT_COMMON 13 -+ -+/* Entries found in sections of type SHT_MIPS_GPTAB. */ -+ -+typedef union -+{ -+ struct -+ { -+ Elf32_Word gt_current_g_value; /* -G value used for compilation */ -+ Elf32_Word gt_unused; /* Not used */ -+ } gt_header; /* First entry in section */ -+ struct -+ { -+ Elf32_Word gt_g_value; /* If this value were used for -G */ -+ Elf32_Word gt_bytes; /* This many bytes would be used */ -+ } gt_entry; /* Subsequent entries in section */ -+} Elf32_gptab; -+ -+/* Entry found in sections of type SHT_MIPS_REGINFO. */ -+ -+typedef struct -+{ -+ Elf32_Word ri_gprmask; /* General registers used */ -+ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ -+ Elf32_Sword ri_gp_value; /* $gp register value */ -+} Elf32_RegInfo; -+ -+/* Entries found in sections of type SHT_MIPS_OPTIONS. */ -+ -+typedef struct -+{ -+ unsigned char kind; /* Determines interpretation of the -+ variable part of descriptor. */ -+ unsigned char size; /* Size of descriptor, including header. */ -+ Elf32_Section section; /* Section header index of section affected, -+ 0 for global options. */ -+ Elf32_Word info; /* Kind-specific information. */ -+} Elf_Options; -+ -+/* Values for `kind' field in Elf_Options. */ -+ -+#define ODK_NULL 0 /* Undefined. */ -+#define ODK_REGINFO 1 /* Register usage information. */ -+#define ODK_EXCEPTIONS 2 /* Exception processing options. */ -+#define ODK_PAD 3 /* Section padding options. */ -+#define ODK_HWPATCH 4 /* Hardware workarounds performed */ -+#define ODK_FILL 5 /* record the fill value used by the linker. */ -+#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ -+#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ -+#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ -+ -+/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ -+ -+#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ -+#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ -+#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ -+#define OEX_SMM 0x20000 /* Force sequential memory mode? */ -+#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ -+#define OEX_PRECISEFP OEX_FPDBUG -+#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ -+ -+#define OEX_FPU_INVAL 0x10 -+#define OEX_FPU_DIV0 0x08 -+#define OEX_FPU_OFLO 0x04 -+#define OEX_FPU_UFLO 0x02 -+#define OEX_FPU_INEX 0x01 -+ -+/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ -+ -+#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ -+#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ -+#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ -+#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ -+ -+#define OPAD_PREFIX 0x1 -+#define OPAD_POSTFIX 0x2 -+#define OPAD_SYMBOL 0x4 -+ -+/* Entry found in `.options' section. */ -+ -+typedef struct -+{ -+ Elf32_Word hwp_flags1; /* Extra flags. */ -+ Elf32_Word hwp_flags2; /* Extra flags. */ -+} Elf_Options_Hw; -+ -+/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ -+ -+#define OHWA0_R4KEOP_CHECKED 0x00000001 -+#define OHWA1_R4KEOP_CLEAN 0x00000002 -+ -+/* MIPS relocs. */ -+ -+#define R_MIPS_NONE 0 /* No reloc */ -+#define R_MIPS_16 1 /* Direct 16 bit */ -+#define R_MIPS_32 2 /* Direct 32 bit */ -+#define R_MIPS_REL32 3 /* PC relative 32 bit */ -+#define R_MIPS_26 4 /* Direct 26 bit shifted */ -+#define R_MIPS_HI16 5 /* High 16 bit */ -+#define R_MIPS_LO16 6 /* Low 16 bit */ -+#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ -+#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ -+#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ -+#define R_MIPS_PC16 10 /* PC relative 16 bit */ -+#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ -+#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ -+ -+#define R_MIPS_SHIFT5 16 -+#define R_MIPS_SHIFT6 17 -+#define R_MIPS_64 18 -+#define R_MIPS_GOT_DISP 19 -+#define R_MIPS_GOT_PAGE 20 -+#define R_MIPS_GOT_OFST 21 -+#define R_MIPS_GOT_HI16 22 -+#define R_MIPS_GOT_LO16 23 -+#define R_MIPS_SUB 24 -+#define R_MIPS_INSERT_A 25 -+#define R_MIPS_INSERT_B 26 -+#define R_MIPS_DELETE 27 -+#define R_MIPS_HIGHER 28 -+#define R_MIPS_HIGHEST 29 -+#define R_MIPS_CALL_HI16 30 -+#define R_MIPS_CALL_LO16 31 -+#define R_MIPS_SCN_DISP 32 -+#define R_MIPS_REL16 33 -+#define R_MIPS_ADD_IMMEDIATE 34 -+#define R_MIPS_PJUMP 35 -+#define R_MIPS_RELGOT 36 -+#define R_MIPS_JALR 37 -+#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ -+#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ -+#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ -+#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ -+#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ -+#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ -+#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ -+#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ -+#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ -+#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ -+#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ -+#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ -+#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ -+#define R_MIPS_GLOB_DAT 51 -+#define R_MIPS_COPY 126 -+#define R_MIPS_JUMP_SLOT 127 -+/* Keep this the last entry. */ -+#define R_MIPS_NUM 128 -+ -+/* Legal values for p_type field of Elf32_Phdr. */ -+ -+#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ -+#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ -+#define PT_MIPS_OPTIONS 0x70000002 -+ -+/* Special program header types. */ -+ -+#define PF_MIPS_LOCAL 0x10000000 -+ -+/* Legal values for d_tag field of Elf32_Dyn. */ -+ -+#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ -+#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ -+#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ -+#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ -+#define DT_MIPS_FLAGS 0x70000005 /* Flags */ -+#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ -+#define DT_MIPS_MSYM 0x70000007 -+#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ -+#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ -+#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ -+#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ -+#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ -+#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ -+#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ -+#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ -+#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ -+#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ -+#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ -+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in -+ DT_MIPS_DELTA_CLASS. */ -+#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ -+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in -+ DT_MIPS_DELTA_INSTANCE. */ -+#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ -+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in -+ DT_MIPS_DELTA_RELOC. */ -+#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta -+ relocations refer to. */ -+#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in -+ DT_MIPS_DELTA_SYM. */ -+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the -+ class declaration. */ -+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in -+ DT_MIPS_DELTA_CLASSSYM. */ -+#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ -+#define DT_MIPS_PIXIE_INIT 0x70000023 -+#define DT_MIPS_SYMBOL_LIB 0x70000024 -+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 -+#define DT_MIPS_LOCAL_GOTIDX 0x70000026 -+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 -+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 -+#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ -+#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ -+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b -+#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ -+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve -+ function stored in GOT. */ -+#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added -+ by rld on dlopen() calls. */ -+#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ -+#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ -+#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ -+/* The address of .got.plt in an executable using the new non-PIC ABI. */ -+#define DT_MIPS_PLTGOT 0x70000032 -+/* The base of the PLT in an executable using the new non-PIC ABI if that -+ PLT is writable. For a non-writable PLT, this is omitted or has a zero -+ value. */ -+#define DT_MIPS_RWPLT 0x70000034 -+#define DT_MIPS_NUM 0x35 -+ -+/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ -+ -+#define RHF_NONE 0 /* No flags */ -+#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ -+#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ -+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ -+#define RHF_NO_MOVE (1 << 3) -+#define RHF_SGI_ONLY (1 << 4) -+#define RHF_GUARANTEE_INIT (1 << 5) -+#define RHF_DELTA_C_PLUS_PLUS (1 << 6) -+#define RHF_GUARANTEE_START_INIT (1 << 7) -+#define RHF_PIXIE (1 << 8) -+#define RHF_DEFAULT_DELAY_LOAD (1 << 9) -+#define RHF_REQUICKSTART (1 << 10) -+#define RHF_REQUICKSTARTED (1 << 11) -+#define RHF_CORD (1 << 12) -+#define RHF_NO_UNRES_UNDEF (1 << 13) -+#define RHF_RLD_ORDER_SAFE (1 << 14) -+ -+/* Entries found in sections of type SHT_MIPS_LIBLIST. */ -+ -+typedef struct -+{ -+ Elf32_Word l_name; /* Name (string table index) */ -+ Elf32_Word l_time_stamp; /* Timestamp */ -+ Elf32_Word l_checksum; /* Checksum */ -+ Elf32_Word l_version; /* Interface version */ -+ Elf32_Word l_flags; /* Flags */ -+} Elf32_Lib; -+ -+typedef struct -+{ -+ Elf64_Word l_name; /* Name (string table index) */ -+ Elf64_Word l_time_stamp; /* Timestamp */ -+ Elf64_Word l_checksum; /* Checksum */ -+ Elf64_Word l_version; /* Interface version */ -+ Elf64_Word l_flags; /* Flags */ -+} Elf64_Lib; -+ -+ -+/* Legal values for l_flags. */ -+ -+#define LL_NONE 0 -+#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ -+#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ -+#define LL_REQUIRE_MINOR (1 << 2) -+#define LL_EXPORTS (1 << 3) -+#define LL_DELAY_LOAD (1 << 4) -+#define LL_DELTA (1 << 5) -+ -+/* Entries found in sections of type SHT_MIPS_CONFLICT. */ -+ -+typedef Elf32_Addr Elf32_Conflict; -+ -+ -+/* HPPA specific definitions. */ -+ -+/* Legal values for e_flags field of Elf32_Ehdr. */ -+ -+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ -+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ -+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ -+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ -+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch -+ prediction. */ -+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ -+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ -+ -+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ -+ -+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ -+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ -+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ -+ -+/* Additional section indeces. */ -+ -+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared -+ symbols in ANSI C. */ -+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ -+ -+/* Legal values for sh_type field of Elf32_Shdr. */ -+ -+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ -+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ -+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ -+ -+/* Legal values for sh_flags field of Elf32_Shdr. */ -+ -+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ -+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ -+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ -+ -+/* Legal values for ST_TYPE subfield of st_info (symbol type). */ -+ -+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ -+ -+#define STT_HP_OPAQUE (STT_LOOS + 0x1) -+#define STT_HP_STUB (STT_LOOS + 0x2) -+ -+/* HPPA relocs. */ -+ -+#define R_PARISC_NONE 0 /* No reloc. */ -+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ -+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ -+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ -+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ -+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ -+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ -+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ -+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ -+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ -+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ -+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ -+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ -+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ -+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ -+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ -+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ -+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ -+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ -+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ -+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ -+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ -+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ -+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ -+#define R_PARISC_FPTR64 64 /* 64 bits function address. */ -+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ -+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ -+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ -+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ -+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ -+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ -+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ -+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ -+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ -+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ -+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ -+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ -+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ -+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ -+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ -+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ -+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ -+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ -+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ -+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ -+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ -+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ -+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ -+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ -+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ -+#define R_PARISC_LORESERVE 128 -+#define R_PARISC_COPY 128 /* Copy relocation. */ -+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ -+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ -+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ -+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ -+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ -+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ -+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ -+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ -+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ -+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ -+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ -+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ -+#define R_PARISC_HIRESERVE 255 -+ -+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PT_HP_TLS (PT_LOOS + 0x0) -+#define PT_HP_CORE_NONE (PT_LOOS + 0x1) -+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) -+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) -+#define PT_HP_CORE_COMM (PT_LOOS + 0x4) -+#define PT_HP_CORE_PROC (PT_LOOS + 0x5) -+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) -+#define PT_HP_CORE_STACK (PT_LOOS + 0x7) -+#define PT_HP_CORE_SHM (PT_LOOS + 0x8) -+#define PT_HP_CORE_MMF (PT_LOOS + 0x9) -+#define PT_HP_PARALLEL (PT_LOOS + 0x10) -+#define PT_HP_FASTBIND (PT_LOOS + 0x11) -+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) -+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) -+#define PT_HP_STACK (PT_LOOS + 0x14) -+ -+#define PT_PARISC_ARCHEXT 0x70000000 -+#define PT_PARISC_UNWIND 0x70000001 -+ -+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ -+ -+#define PF_PARISC_SBP 0x08000000 -+ -+#define PF_HP_PAGE_SIZE 0x00100000 -+#define PF_HP_FAR_SHARED 0x00200000 -+#define PF_HP_NEAR_SHARED 0x00400000 -+#define PF_HP_CODE 0x01000000 -+#define PF_HP_MODIFY 0x02000000 -+#define PF_HP_LAZYSWAP 0x04000000 -+#define PF_HP_SBP 0x08000000 -+ -+ -+/* Alpha specific definitions. */ -+ -+/* Legal values for e_flags field of Elf64_Ehdr. */ -+ -+#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ -+#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ -+ -+/* Legal values for sh_type field of Elf64_Shdr. */ -+ -+/* These two are primerily concerned with ECOFF debugging info. */ -+#define SHT_ALPHA_DEBUG 0x70000001 -+#define SHT_ALPHA_REGINFO 0x70000002 -+ -+/* Legal values for sh_flags field of Elf64_Shdr. */ -+ -+#define SHF_ALPHA_GPREL 0x10000000 -+ -+/* Legal values for st_other field of Elf64_Sym. */ -+#define STO_ALPHA_NOPV 0x80 /* No PV required. */ -+#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ -+ -+/* Alpha relocs. */ -+ -+#define R_ALPHA_NONE 0 /* No reloc */ -+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ -+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ -+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ -+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ -+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ -+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ -+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ -+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ -+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ -+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ -+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ -+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ -+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ -+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ -+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ -+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ -+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ -+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ -+#define R_ALPHA_TLS_GD_HI 28 -+#define R_ALPHA_TLSGD 29 -+#define R_ALPHA_TLS_LDM 30 -+#define R_ALPHA_DTPMOD64 31 -+#define R_ALPHA_GOTDTPREL 32 -+#define R_ALPHA_DTPREL64 33 -+#define R_ALPHA_DTPRELHI 34 -+#define R_ALPHA_DTPRELLO 35 -+#define R_ALPHA_DTPREL16 36 -+#define R_ALPHA_GOTTPREL 37 -+#define R_ALPHA_TPREL64 38 -+#define R_ALPHA_TPRELHI 39 -+#define R_ALPHA_TPRELLO 40 -+#define R_ALPHA_TPREL16 41 -+/* Keep this the last entry. */ -+#define R_ALPHA_NUM 46 -+ -+/* Magic values of the LITUSE relocation addend. */ -+#define LITUSE_ALPHA_ADDR 0 -+#define LITUSE_ALPHA_BASE 1 -+#define LITUSE_ALPHA_BYTOFF 2 -+#define LITUSE_ALPHA_JSR 3 -+#define LITUSE_ALPHA_TLS_GD 4 -+#define LITUSE_ALPHA_TLS_LDM 5 -+ -+/* Legal values for d_tag of Elf64_Dyn. */ -+#define DT_ALPHA_PLTRO (DT_LOPROC + 0) -+#define DT_ALPHA_NUM 1 -+ -+/* PowerPC specific declarations */ -+ -+/* Values for Elf32/64_Ehdr.e_flags. */ -+#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ -+ -+/* Cygnus local bits below */ -+#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ -+#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib -+ flag */ -+ -+/* PowerPC relocations defined by the ABIs */ -+#define R_PPC_NONE 0 -+#define R_PPC_ADDR32 1 /* 32bit absolute address */ -+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -+#define R_PPC_ADDR16 3 /* 16bit absolute address */ -+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -+#define R_PPC_ADDR14_BRTAKEN 8 -+#define R_PPC_ADDR14_BRNTAKEN 9 -+#define R_PPC_REL24 10 /* PC relative 26 bit */ -+#define R_PPC_REL14 11 /* PC relative 16 bit */ -+#define R_PPC_REL14_BRTAKEN 12 -+#define R_PPC_REL14_BRNTAKEN 13 -+#define R_PPC_GOT16 14 -+#define R_PPC_GOT16_LO 15 -+#define R_PPC_GOT16_HI 16 -+#define R_PPC_GOT16_HA 17 -+#define R_PPC_PLTREL24 18 -+#define R_PPC_COPY 19 -+#define R_PPC_GLOB_DAT 20 -+#define R_PPC_JMP_SLOT 21 -+#define R_PPC_RELATIVE 22 -+#define R_PPC_LOCAL24PC 23 -+#define R_PPC_UADDR32 24 -+#define R_PPC_UADDR16 25 -+#define R_PPC_REL32 26 -+#define R_PPC_PLT32 27 -+#define R_PPC_PLTREL32 28 -+#define R_PPC_PLT16_LO 29 -+#define R_PPC_PLT16_HI 30 -+#define R_PPC_PLT16_HA 31 -+#define R_PPC_SDAREL16 32 -+#define R_PPC_SECTOFF 33 -+#define R_PPC_SECTOFF_LO 34 -+#define R_PPC_SECTOFF_HI 35 -+#define R_PPC_SECTOFF_HA 36 -+ -+/* PowerPC relocations defined for the TLS access ABI. */ -+#define R_PPC_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ -+#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ -+#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ -+#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ -+#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ -+#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ -+#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ -+#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ -+#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ -+ -+/* Keep this the last entry. */ -+#define R_PPC_NUM 95 -+ -+/* The remaining relocs are from the Embedded ELF ABI, and are not -+ in the SVR4 ELF ABI. */ -+#define R_PPC_EMB_NADDR32 101 -+#define R_PPC_EMB_NADDR16 102 -+#define R_PPC_EMB_NADDR16_LO 103 -+#define R_PPC_EMB_NADDR16_HI 104 -+#define R_PPC_EMB_NADDR16_HA 105 -+#define R_PPC_EMB_SDAI16 106 -+#define R_PPC_EMB_SDA2I16 107 -+#define R_PPC_EMB_SDA2REL 108 -+#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ -+#define R_PPC_EMB_MRKREF 110 -+#define R_PPC_EMB_RELSEC16 111 -+#define R_PPC_EMB_RELST_LO 112 -+#define R_PPC_EMB_RELST_HI 113 -+#define R_PPC_EMB_RELST_HA 114 -+#define R_PPC_EMB_BIT_FLD 115 -+#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ -+ -+/* Diab tool relocations. */ -+#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ -+#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ -+#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ -+#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ -+#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ -+#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ -+ -+/* GNU relocs used in PIC code sequences. */ -+#define R_PPC_REL16 249 /* word32 (sym+add-.) */ -+#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ -+#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ -+#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ -+ -+/* This is a phony reloc to handle any old fashioned TOC16 references -+ that may still be in object files. */ -+#define R_PPC_TOC16 255 -+ -+/* PowerPC specific values for the Dyn d_tag field. */ -+#define DT_PPC_GOT (DT_LOPROC + 0) -+#define DT_PPC_NUM 1 -+ -+/* PowerPC64 relocations defined by the ABIs */ -+#define R_PPC64_NONE R_PPC_NONE -+#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ -+#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ -+#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ -+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ -+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ -+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ -+#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ -+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN -+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN -+#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ -+#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ -+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN -+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN -+#define R_PPC64_GOT16 R_PPC_GOT16 -+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO -+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI -+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA -+ -+#define R_PPC64_COPY R_PPC_COPY -+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT -+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT -+#define R_PPC64_RELATIVE R_PPC_RELATIVE -+ -+#define R_PPC64_UADDR32 R_PPC_UADDR32 -+#define R_PPC64_UADDR16 R_PPC_UADDR16 -+#define R_PPC64_REL32 R_PPC_REL32 -+#define R_PPC64_PLT32 R_PPC_PLT32 -+#define R_PPC64_PLTREL32 R_PPC_PLTREL32 -+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO -+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI -+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA -+ -+#define R_PPC64_SECTOFF R_PPC_SECTOFF -+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO -+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI -+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA -+#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ -+#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ -+#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ -+#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ -+#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ -+#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ -+#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ -+#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ -+#define R_PPC64_PLT64 45 /* doubleword64 L + A */ -+#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ -+#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ -+#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ -+#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ -+#define R_PPC64_TOC 51 /* doubleword64 .TOC */ -+#define R_PPC64_PLTGOT16 52 /* half16* M + A */ -+#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ -+#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ -+#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ -+ -+#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ -+#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ -+#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ -+#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ -+#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ -+#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ -+#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ -+#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ -+#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ -+#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ -+#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ -+ -+/* PowerPC64 relocations defined for the TLS access ABI. */ -+#define R_PPC64_TLS 67 /* none (sym+add)@tls */ -+#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ -+#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ -+#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ -+#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ -+#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ -+#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ -+#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ -+#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ -+#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ -+#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ -+#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ -+#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ -+#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ -+#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ -+#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ -+#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ -+#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ -+#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ -+#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ -+#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ -+#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ -+#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ -+#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ -+#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ -+#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ -+#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ -+#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ -+#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ -+#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ -+#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ -+#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ -+#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ -+#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ -+#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ -+#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ -+ -+/* Keep this the last entry. */ -+#define R_PPC64_NUM 107 -+ -+/* PowerPC64 specific values for the Dyn d_tag field. */ -+#define DT_PPC64_GLINK (DT_LOPROC + 0) -+#define DT_PPC64_OPD (DT_LOPROC + 1) -+#define DT_PPC64_OPDSZ (DT_LOPROC + 2) -+#define DT_PPC64_NUM 3 -+ -+ -+/* ARM specific declarations */ -+ -+/* Processor specific flags for the ELF header e_flags field. */ -+#define EF_ARM_RELEXEC 0x01 -+#define EF_ARM_HASENTRY 0x02 -+#define EF_ARM_INTERWORK 0x04 -+#define EF_ARM_APCS_26 0x08 -+#define EF_ARM_APCS_FLOAT 0x10 -+#define EF_ARM_PIC 0x20 -+#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ -+#define EF_ARM_NEW_ABI 0x80 -+#define EF_ARM_OLD_ABI 0x100 -+ -+/* Other constants defined in the ARM ELF spec. version B-01. */ -+/* NB. These conflict with values defined above. */ -+#define EF_ARM_SYMSARESORTED 0x04 -+#define EF_ARM_DYNSYMSUSESEGIDX 0x08 -+#define EF_ARM_MAPSYMSFIRST 0x10 -+#define EF_ARM_EABIMASK 0XFF000000 -+ -+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) -+#define EF_ARM_EABI_UNKNOWN 0x00000000 -+#define EF_ARM_EABI_VER1 0x01000000 -+#define EF_ARM_EABI_VER2 0x02000000 -+ -+/* Additional symbol types for Thumb */ -+#define STT_ARM_TFUNC 0xd -+ -+/* ARM-specific values for sh_flags */ -+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ -+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined -+ in the input to a link step */ -+ -+/* ARM-specific program header flags */ -+#define PF_ARM_SB 0x10000000 /* Segment contains the location -+ addressed by the static base */ -+ -+/* Processor specific values for the Phdr p_type field. */ -+#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ -+ -+/* ARM relocs. */ -+ -+#define R_ARM_NONE 0 /* No reloc */ -+#define R_ARM_PC24 1 /* PC relative 26 bit branch */ -+#define R_ARM_ABS32 2 /* Direct 32 bit */ -+#define R_ARM_REL32 3 /* PC relative 32 bit */ -+#define R_ARM_PC13 4 -+#define R_ARM_ABS16 5 /* Direct 16 bit */ -+#define R_ARM_ABS12 6 /* Direct 12 bit */ -+#define R_ARM_THM_ABS5 7 -+#define R_ARM_ABS8 8 /* Direct 8 bit */ -+#define R_ARM_SBREL32 9 -+#define R_ARM_THM_PC22 10 -+#define R_ARM_THM_PC8 11 -+#define R_ARM_AMP_VCALL9 12 -+#define R_ARM_SWI24 13 -+#define R_ARM_THM_SWI8 14 -+#define R_ARM_XPC25 15 -+#define R_ARM_THM_XPC22 16 -+#define R_ARM_TLS_DTPMOD32 17 -+#define R_ARM_TLS_DTPOFF32 18 -+#define R_ARM_TLS_TPOFF32 19 -+#define R_ARM_COPY 20 /* Copy symbol at runtime */ -+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ -+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ -+#define R_ARM_RELATIVE 23 /* Adjust by program base */ -+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ -+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ -+#define R_ARM_GOT32 26 /* 32 bit GOT entry */ -+#define R_ARM_PLT32 27 /* 32 bit PLT address */ -+#define R_ARM_ALU_PCREL_7_0 32 -+#define R_ARM_ALU_PCREL_15_8 33 -+#define R_ARM_ALU_PCREL_23_15 34 -+#define R_ARM_LDR_SBREL_11_0 35 -+#define R_ARM_ALU_SBREL_19_12 36 -+#define R_ARM_ALU_SBREL_27_20 37 -+#define R_ARM_GNU_VTENTRY 100 -+#define R_ARM_GNU_VTINHERIT 101 -+#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ -+#define R_ARM_THM_PC9 103 /* thumb conditional branch */ -+#define R_ARM_TLS_GD32 104 -+#define R_ARM_TLS_LDM32 105 -+#define R_ARM_TLS_LDO32 106 -+#define R_ARM_TLS_IE32 107 -+#define R_ARM_TLS_LE32 108 -+#define R_ARM_TLS_LDO12 109 -+#define R_ARM_TLS_LE12 110 -+#define R_ARM_TLS_IE12GP 111 -+#define R_ARM_RXPC25 249 -+#define R_ARM_RSBREL32 250 -+#define R_ARM_THM_RPC22 251 -+#define R_ARM_RREL32 252 -+#define R_ARM_RABS22 253 -+#define R_ARM_RPC24 254 -+#define R_ARM_RBASE 255 -+/* Keep this the last entry. */ -+#define R_ARM_NUM 256 -+ -+/* IA-64 specific declarations. */ -+ -+/* Processor specific flags for the Ehdr e_flags field. */ -+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ -+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ -+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ -+ -+/* Processor specific values for the Phdr p_type field. */ -+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ -+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ -+#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) -+#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) -+#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) -+ -+/* Processor specific flags for the Phdr p_flags field. */ -+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Shdr sh_type field. */ -+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ -+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ -+ -+/* Processor specific flags for the Shdr sh_flags field. */ -+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ -+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ -+ -+/* Processor specific values for the Dyn d_tag field. */ -+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) -+#define DT_IA_64_NUM 1 -+ -+/* IA-64 relocations. */ -+#define R_IA64_NONE 0x00 /* none */ -+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ -+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ -+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ -+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ -+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ -+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ -+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ -+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ -+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ -+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ -+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ -+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ -+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ -+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ -+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ -+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ -+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ -+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ -+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ -+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ -+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ -+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ -+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ -+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ -+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ -+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ -+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ -+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ -+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ -+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ -+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ -+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ -+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ -+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ -+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ -+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ -+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ -+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ -+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ -+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ -+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ -+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ -+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ -+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ -+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ -+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ -+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ -+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ -+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ -+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ -+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ -+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ -+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ -+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ -+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ -+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ -+#define R_IA64_COPY 0x84 /* copy relocation */ -+#define R_IA64_SUB 0x85 /* Addend and symbol difference */ -+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ -+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ -+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ -+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ -+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ -+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ -+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ -+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ -+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ -+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ -+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ -+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ -+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ -+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ -+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ -+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ -+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ -+ -+/* SH specific declarations */ -+ -+/* SH specific values for `st_other'. */ -+ -+/* If set, this is a symbol pointing to SHmedia code, which will be branched -+ to, so need to add 1 to the symbol value. */ -+#define STO_SH5_ISA32 (1 << 2) -+ -+/* SH relocs. */ -+#define R_SH_NONE 0 -+#define R_SH_DIR32 1 -+#define R_SH_REL32 2 -+#define R_SH_DIR8WPN 3 -+#define R_SH_IND12W 4 -+#define R_SH_DIR8WPL 5 -+#define R_SH_DIR8WPZ 6 -+#define R_SH_DIR8BP 7 -+#define R_SH_DIR8W 8 -+#define R_SH_DIR8L 9 -+#define R_SH_SWITCH16 25 -+#define R_SH_SWITCH32 26 -+#define R_SH_USES 27 -+#define R_SH_COUNT 28 -+#define R_SH_ALIGN 29 -+#define R_SH_CODE 30 -+#define R_SH_DATA 31 -+#define R_SH_LABEL 32 -+#define R_SH_SWITCH8 33 -+#define R_SH_GNU_VTINHERIT 34 -+#define R_SH_GNU_VTENTRY 35 -+#define R_SH_TLS_GD_32 144 -+#define R_SH_TLS_LD_32 145 -+#define R_SH_TLS_LDO_32 146 -+#define R_SH_TLS_IE_32 147 -+#define R_SH_TLS_LE_32 148 -+#define R_SH_TLS_DTPMOD32 149 -+#define R_SH_TLS_DTPOFF32 150 -+#define R_SH_TLS_TPOFF32 151 -+#define R_SH_GOT32 160 -+#define R_SH_PLT32 161 -+#define R_SH_COPY 162 -+#define R_SH_GLOB_DAT 163 -+#define R_SH_JMP_SLOT 164 -+#define R_SH_RELATIVE 165 -+#define R_SH_GOTOFF 166 -+#define R_SH_GOTPC 167 -+#define R_SH_RELATIVE_LOW16 197 -+#define R_SH_RELATIVE_MEDLOW16 198 -+#define R_SH_IMM_LOW16 246 -+#define R_SH_IMM_LOW16_PCREL 247 -+#define R_SH_IMM_MEDLOW16 248 -+#define R_SH_IMM_MEDLOW16_PCREL 249 -+ -+/* Keep this the last entry. */ -+#define R_SH_NUM 256 -+ -+/* Additional s390 relocs */ -+ -+#define R_390_NONE 0 /* No reloc. */ -+#define R_390_8 1 /* Direct 8 bit. */ -+#define R_390_12 2 /* Direct 12 bit. */ -+#define R_390_16 3 /* Direct 16 bit. */ -+#define R_390_32 4 /* Direct 32 bit. */ -+#define R_390_PC32 5 /* PC relative 32 bit. */ -+#define R_390_GOT12 6 /* 12 bit GOT offset. */ -+#define R_390_GOT32 7 /* 32 bit GOT offset. */ -+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ -+#define R_390_COPY 9 /* Copy symbol at runtime. */ -+#define R_390_GLOB_DAT 10 /* Create GOT entry. */ -+#define R_390_JMP_SLOT 11 /* Create PLT entry. */ -+#define R_390_RELATIVE 12 /* Adjust by program base. */ -+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ -+#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ -+#define R_390_GOT16 15 /* 16 bit GOT offset. */ -+#define R_390_PC16 16 /* PC relative 16 bit. */ -+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ -+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ -+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ -+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ -+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ -+#define R_390_64 22 /* Direct 64 bit. */ -+#define R_390_PC64 23 /* PC relative 64 bit. */ -+#define R_390_GOT64 24 /* 64 bit GOT offset. */ -+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ -+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ -+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ -+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ -+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ -+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ -+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ -+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ -+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ -+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ -+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ -+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ -+#define R_390_TLS_GDCALL 38 /* Tag for function call in general -+ dynamic TLS code. */ -+#define R_390_TLS_LDCALL 39 /* Tag for function call in local -+ dynamic TLS code. */ -+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic -+ thread local data. */ -+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS -+ block offset. */ -+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic -+ thread local data in LE code. */ -+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for -+ negated static TLS block offset. */ -+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to -+ static TLS block. */ -+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS -+ block. */ -+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ -+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ -+#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS -+ block. */ -+#define R_390_20 57 /* Direct 20 bit. */ -+#define R_390_GOT20 58 /* 20 bit GOT offset. */ -+#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ -+#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS -+ block offset. */ -+/* Keep this the last entry. */ -+#define R_390_NUM 61 -+ -+ -+/* CRIS flags. */ -+#define EF_CRIS_VARIANT_MASK 0x0000000e -+#define EF_CRIS_VARIANT_ANY_V0_V10 0x00000000 -+#define EF_CRIS_VARIANT_V32 0x00000002 -+#define EF_CRIS_VARIANT_COMMON_V10_V32 0x00000004 -+ -+/* CRIS relocations. */ -+#define R_CRIS_NONE 0 -+#define R_CRIS_8 1 -+#define R_CRIS_16 2 -+#define R_CRIS_32 3 -+#define R_CRIS_8_PCREL 4 -+#define R_CRIS_16_PCREL 5 -+#define R_CRIS_32_PCREL 6 -+#define R_CRIS_GNU_VTINHERIT 7 -+#define R_CRIS_GNU_VTENTRY 8 -+#define R_CRIS_COPY 9 -+#define R_CRIS_GLOB_DAT 10 -+#define R_CRIS_JUMP_SLOT 11 -+#define R_CRIS_RELATIVE 12 -+#define R_CRIS_16_GOT 13 -+#define R_CRIS_32_GOT 14 -+#define R_CRIS_16_GOTPLT 15 -+#define R_CRIS_32_GOTPLT 16 -+#define R_CRIS_32_GOTREL 17 -+#define R_CRIS_32_PLT_GOTREL 18 -+#define R_CRIS_32_PLT_PCREL 19 -+ -+/* Keep this the last entry. */ -+#define R_CRIS_NUM 20 -+ -+ -+/* AMD x86-64 relocations. */ -+#define R_X86_64_NONE 0 /* No reloc */ -+#define R_X86_64_64 1 /* Direct 64 bit */ -+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ -+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ -+#define R_X86_64_PLT32 4 /* 32 bit PLT address */ -+#define R_X86_64_COPY 5 /* Copy symbol at runtime */ -+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ -+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ -+#define R_X86_64_RELATIVE 8 /* Adjust by program base */ -+#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative -+ offset to GOT */ -+#define R_X86_64_32 10 /* Direct 32 bit zero extended */ -+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ -+#define R_X86_64_16 12 /* Direct 16 bit zero extended */ -+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ -+#define R_X86_64_8 14 /* Direct 8 bit sign extended */ -+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ -+#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ -+#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ -+#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ -+#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset -+ to two GOT entries for GD symbol */ -+#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset -+ to two GOT entries for LD symbol */ -+#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ -+#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset -+ to GOT entry for IE symbol */ -+#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ -+ -+#define R_X86_64_NUM 24 -+ -+ -+/* AM33 relocations. */ -+#define R_MN10300_NONE 0 /* No reloc. */ -+#define R_MN10300_32 1 /* Direct 32 bit. */ -+#define R_MN10300_16 2 /* Direct 16 bit. */ -+#define R_MN10300_8 3 /* Direct 8 bit. */ -+#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ -+#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ -+#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ -+#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ -+#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ -+#define R_MN10300_24 9 /* Direct 24 bit. */ -+#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ -+#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ -+#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ -+#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ -+#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ -+#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ -+#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ -+#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ -+#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ -+#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ -+#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ -+#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ -+#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ -+#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ -+ -+#define R_MN10300_NUM 24 -+ -+ -+/* M32R relocs. */ -+#define R_M32R_NONE 0 /* No reloc. */ -+#define R_M32R_16 1 /* Direct 16 bit. */ -+#define R_M32R_32 2 /* Direct 32 bit. */ -+#define R_M32R_24 3 /* Direct 24 bit. */ -+#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ -+#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ -+#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ -+#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ -+#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ -+#define R_M32R_LO16 9 /* Low 16 bit. */ -+#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ -+#define R_M32R_GNU_VTINHERIT 11 -+#define R_M32R_GNU_VTENTRY 12 -+/* M32R relocs use SHT_RELA. */ -+#define R_M32R_16_RELA 33 /* Direct 16 bit. */ -+#define R_M32R_32_RELA 34 /* Direct 32 bit. */ -+#define R_M32R_24_RELA 35 /* Direct 24 bit. */ -+#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ -+#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ -+#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ -+#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ -+#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ -+#define R_M32R_LO16_RELA 41 /* Low 16 bit */ -+#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ -+#define R_M32R_RELA_GNU_VTINHERIT 43 -+#define R_M32R_RELA_GNU_VTENTRY 44 -+ -+#define R_M32R_GOT24 48 /* 24 bit GOT entry */ -+#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ -+#define R_M32R_COPY 50 /* Copy symbol at runtime */ -+#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ -+#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ -+#define R_M32R_RELATIVE 53 /* Adjust by program base */ -+#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ -+#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ -+#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned -+ low */ -+#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed -+ low */ -+#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ -+#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to -+ GOT with unsigned low */ -+#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to -+ GOT with signed low */ -+#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to -+ GOT */ -+#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT -+ with unsigned low */ -+#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT -+ with signed low */ -+#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ -+#define R_M32R_NUM 256 /* Keep this the last entry. */ -+ -+/* i960 Relocations */ -+#define R_960_NONE 0 -+#define R_960_12 1 -+#define R_960_32 2 -+#define R_960_IP24 3 -+#define R_960_SUB 4 -+#define R_960_OPTCALL 5 -+#define R_960_OPTCALLX 6 -+#define R_960_OPTCALLXA 7 -+/* Keep this the last entry. */ -+#define R_960_NUM 8 -+ -+ -+/* v850 relocations. */ -+#define R_V850_NONE 0 -+#define R_V850_9_PCREL 1 -+#define R_V850_22_PCREL 2 -+#define R_V850_HI16_S 3 -+#define R_V850_HI16 4 -+#define R_V850_LO16 5 -+#define R_V850_32 6 -+#define R_V850_16 7 -+#define R_V850_8 8 -+#define R_V850_SDA_16_16_OFFSET 9 /* For ld.b, st.b, set1, clr1, -+ not1, tst1, movea, movhi */ -+#define R_V850_SDA_15_16_OFFSET 10 /* For ld.w, ld.h, ld.hu, st.w, st.h */ -+#define R_V850_ZDA_16_16_OFFSET 11 /* For ld.b, st.b, set1, clr1, -+ not1, tst1, movea, movhi */ -+#define R_V850_ZDA_15_16_OFFSET 12 /* For ld.w, ld.h, ld.hu, st.w, st.h */ -+#define R_V850_TDA_6_8_OFFSET 13 /* For sst.w, sld.w */ -+#define R_V850_TDA_7_8_OFFSET 14 /* For sst.h, sld.h */ -+#define R_V850_TDA_7_7_OFFSET 15 /* For sst.b, sld.b */ -+#define R_V850_TDA_16_16_OFFSET 16 /* For set1, clr1, not1, tst1, -+ movea, movhi */ -+/* CYGNUS LOCAL v850e */ -+#define R_V850_TDA_4_5_OFFSET 17 /* For sld.hu */ -+#define R_V850_TDA_4_4_OFFSET 18 /* For sld.bu */ -+#define R_V850_SDA_16_16_SPLIT_OFFSET 19 /* For ld.bu */ -+#define R_V850_ZDA_16_16_SPLIT_OFFSET 20 /* For ld.bu */ -+#define R_V850_CALLT_6_7_OFFSET 21 /* For callt */ -+#define R_V850_CALLT_16_16_OFFSET 22 /* For callt */ -+/* END CYGNUS LOCAL */ -+#define R_V850_GNU_VTINHERIT 23 -+#define R_V850_GNU_VTENTRY 24 -+/* Keep this the last entry. */ -+#define R_V850_NUM 25 -+ -+/* Atmel AVR32 relocations. */ -+#define R_AVR32_NONE 0 -+#define R_AVR32_32 1 -+#define R_AVR32_16 2 -+#define R_AVR32_8 3 -+#define R_AVR32_32_PCREL 4 -+#define R_AVR32_16_PCREL 5 -+#define R_AVR32_8_PCREL 6 -+#define R_AVR32_DIFF32 7 -+#define R_AVR32_DIFF16 8 -+#define R_AVR32_DIFF8 9 -+#define R_AVR32_GOT32 10 -+#define R_AVR32_GOT16 11 -+#define R_AVR32_GOT8 12 -+#define R_AVR32_21S 13 -+#define R_AVR32_16U 14 -+#define R_AVR32_16S 15 -+#define R_AVR32_8S 16 -+#define R_AVR32_8S_EXT 17 -+#define R_AVR32_22H_PCREL 18 -+#define R_AVR32_18W_PCREL 19 -+#define R_AVR32_16B_PCREL 20 -+#define R_AVR32_16N_PCREL 21 -+#define R_AVR32_14UW_PCREL 22 -+#define R_AVR32_11H_PCREL 23 -+#define R_AVR32_10UW_PCREL 24 -+#define R_AVR32_9H_PCREL 25 -+#define R_AVR32_9UW_PCREL 26 -+#define R_AVR32_HI16 27 -+#define R_AVR32_LO16 28 -+#define R_AVR32_GOTPC 29 -+#define R_AVR32_GOTCALL 30 -+#define R_AVR32_LDA_GOT 31 -+#define R_AVR32_GOT21S 32 -+#define R_AVR32_GOT18SW 33 -+#define R_AVR32_GOT16S 34 -+#define R_AVR32_GOT7UW 35 -+#define R_AVR32_32_CPENT 36 -+#define R_AVR32_CPCALL 37 -+#define R_AVR32_16_CP 38 -+#define R_AVR32_9W_CP 39 -+#define R_AVR32_RELATIVE 40 -+#define R_AVR32_GLOB_DAT 41 -+#define R_AVR32_JMP_SLOT 42 -+#define R_AVR32_ALIGN 43 -+#define R_AVR32_NUM 44 -+ -+/* AVR32 dynamic tags */ -+#define DT_AVR32_GOTSZ 0x70000001 /* Total size of GOT in bytes */ -+ -+/* Renesas H8/300 Relocations */ -+#define R_H8_NONE 0 -+#define R_H8_DIR32 1 -+#define R_H8_DIR32_28 2 -+#define R_H8_DIR32_24 3 -+#define R_H8_DIR32_16 4 -+#define R_H8_DIR32U 6 -+#define R_H8_DIR32U_28 7 -+#define R_H8_DIR32U_24 8 -+#define R_H8_DIR32U_20 9 -+#define R_H8_DIR32U_16 10 -+#define R_H8_DIR24 11 -+#define R_H8_DIR24_20 12 -+#define R_H8_DIR24_16 13 -+#define R_H8_DIR24U 14 -+#define R_H8_DIR24U_20 15 -+#define R_H8_DIR24U_16 16 -+#define R_H8_DIR16 17 -+#define R_H8_DIR16U 18 -+#define R_H8_DIR16S_32 19 -+#define R_H8_DIR16S_28 20 -+#define R_H8_DIR16S_24 21 -+#define R_H8_DIR16S_20 22 -+#define R_H8_DIR16S 23 -+#define R_H8_DIR8 24 -+#define R_H8_DIR8U 25 -+#define R_H8_DIR8Z_32 26 -+#define R_H8_DIR8Z_28 27 -+#define R_H8_DIR8Z_24 28 -+#define R_H8_DIR8Z_20 29 -+#define R_H8_DIR8Z_16 30 -+#define R_H8_PCREL16 31 -+#define R_H8_PCREL8 32 -+#define R_H8_BPOS 33 -+#define R_H8_PCREL32 34 -+#define R_H8_GOT32O 35 -+#define R_H8_GOT16O 36 -+#define R_H8_DIR16A8 59 -+#define R_H8_DIR16R8 60 -+#define R_H8_DIR24A8 61 -+#define R_H8_DIR24R8 62 -+#define R_H8_DIR32A16 63 -+#define R_H8_ABS32 65 -+#define R_H8_ABS32A16 127 -+#define R_H8_NUM 128 -+ -+/* NIOS relocations. */ -+#define R_NIOS_NONE 0 -+#define R_NIOS_32 1 /* A 32 bit absolute relocation.*/ -+#define R_NIOS_LO16_LO5 2 /* A LO-16 5 bit absolute relocation. */ -+#define R_NIOS_LO16_HI11 3 /* A LO-16 top 11 bit absolute relocation. */ -+#define R_NIOS_HI16_LO5 4 /* A HI-16 5 bit absolute relocation. */ -+#define R_NIOS_HI16_HI11 5 /* A HI-16 top 11 bit absolute relocation. */ -+#define R_NIOS_PCREL6 6 /* A 6 bit relative relocation. */ -+#define R_NIOS_PCREL8 7 /* An 8 bit relative relocation. */ -+#define R_NIOS_PCREL11 8 /* An 11 bit relative relocation. */ -+#define R_NIOS_16 9 /* A 16 bit absolute relocation. */ -+#define R_NIOS_H_LO5 10 /* Low 5-bits of absolute relocation in halfwords. */ -+#define R_NIOS_H_HI11 11 /* Top 11 bits of 16-bit absolute relocation in halfwords. */ -+#define R_NIOS_H_XLO5 12 /* Low 5 bits of top 16-bits of 32-bit absolute relocation in halfwords. */ -+#define R_NIOS_H_XHI11 13 /* Top 11 bits of top 16-bits of 32-bit absolute relocation in halfwords. */ -+#define R_NIOS_H_16 14 /* Half-word @h value */ -+#define R_NIOS_H_32 15 /* Word @h value */ -+#define R_NIOS_GNU_VTINHERIT 200 /* GNU extension to record C++ vtable hierarchy */ -+#define R_NIOS_GNU_VTENTRY 201 /* GNU extension to record C++ vtable member usage */ -+/* Keep this the last entry. */ -+#define R_NIOS_NUM 202 -+ -+/* NIOS II relocations */ -+#define R_NIOS2_NONE 0 -+#define R_NIOS2_S16 1 -+#define R_NIOS2_U16 2 -+#define R_NIOS2_PCREL16 3 -+#define R_NIOS2_CALL26 4 -+#define R_NIOS2_IMM5 5 -+#define R_NIOS2_CACHE_OPX 6 -+#define R_NIOS2_IMM6 7 -+#define R_NIOS2_IMM8 8 -+#define R_NIOS2_HI16 9 -+#define R_NIOS2_LO16 10 -+#define R_NIOS2_HIADJ16 11 -+#define R_NIOS2_BFD_RELOC_32 12 -+#define R_NIOS2_BFD_RELOC_16 13 -+#define R_NIOS2_BFD_RELOC_8 14 -+#define R_NIOS2_GPREL 15 -+#define R_NIOS2_GNU_VTINHERIT 16 -+#define R_NIOS2_GNU_VTENTRY 17 -+#define R_NIOS2_UJMP 18 -+#define R_NIOS2_CJMP 19 -+#define R_NIOS2_CALLR 20 -+#define R_NIOS2_ALIGN 21 -+/* Keep this the last entry. */ -+#define R_NIOS2_NUM 22 -+ -+/* Xtensa-specific declarations */ -+ -+/* Xtensa values for the Dyn d_tag field. */ -+#define DT_XTENSA_GOT_LOC_OFF (DT_LOPROC + 0) -+#define DT_XTENSA_GOT_LOC_SZ (DT_LOPROC + 1) -+#define DT_XTENSA_NUM 2 -+ -+/* Xtensa relocations. */ -+#define R_XTENSA_NONE 0 -+#define R_XTENSA_32 1 -+#define R_XTENSA_RTLD 2 -+#define R_XTENSA_GLOB_DAT 3 -+#define R_XTENSA_JMP_SLOT 4 -+#define R_XTENSA_RELATIVE 5 -+#define R_XTENSA_PLT 6 -+#define R_XTENSA_OP0 8 -+#define R_XTENSA_OP1 9 -+#define R_XTENSA_OP2 10 -+#define R_XTENSA_ASM_EXPAND 11 -+#define R_XTENSA_ASM_SIMPLIFY 12 -+#define R_XTENSA_GNU_VTINHERIT 15 -+#define R_XTENSA_GNU_VTENTRY 16 -+#define R_XTENSA_DIFF8 17 -+#define R_XTENSA_DIFF16 18 -+#define R_XTENSA_DIFF32 19 -+#define R_XTENSA_SLOT0_OP 20 -+#define R_XTENSA_SLOT1_OP 21 -+#define R_XTENSA_SLOT2_OP 22 -+#define R_XTENSA_SLOT3_OP 23 -+#define R_XTENSA_SLOT4_OP 24 -+#define R_XTENSA_SLOT5_OP 25 -+#define R_XTENSA_SLOT6_OP 26 -+#define R_XTENSA_SLOT7_OP 27 -+#define R_XTENSA_SLOT8_OP 28 -+#define R_XTENSA_SLOT9_OP 29 -+#define R_XTENSA_SLOT10_OP 30 -+#define R_XTENSA_SLOT11_OP 31 -+#define R_XTENSA_SLOT12_OP 32 -+#define R_XTENSA_SLOT13_OP 33 -+#define R_XTENSA_SLOT14_OP 34 -+#define R_XTENSA_SLOT0_ALT 35 -+#define R_XTENSA_SLOT1_ALT 36 -+#define R_XTENSA_SLOT2_ALT 37 -+#define R_XTENSA_SLOT3_ALT 38 -+#define R_XTENSA_SLOT4_ALT 39 -+#define R_XTENSA_SLOT5_ALT 40 -+#define R_XTENSA_SLOT6_ALT 41 -+#define R_XTENSA_SLOT7_ALT 42 -+#define R_XTENSA_SLOT8_ALT 43 -+#define R_XTENSA_SLOT9_ALT 44 -+#define R_XTENSA_SLOT10_ALT 45 -+#define R_XTENSA_SLOT11_ALT 46 -+#define R_XTENSA_SLOT12_ALT 47 -+#define R_XTENSA_SLOT13_ALT 48 -+#define R_XTENSA_SLOT14_ALT 49 -+/* Keep this the last entry. */ -+#define R_XTENSA_NUM 50 -+ -+/* C6X specific relocs */ -+#define R_C6000_NONE 0 -+#define R_C6000_ABS32 1 -+#define R_C6000_ABS16 2 -+#define R_C6000_ABS8 3 -+#define R_C6000_PCR_S21 4 -+#define R_C6000_PCR_S12 5 -+#define R_C6000_PCR_S10 6 -+#define R_C6000_PCR_S7 7 -+#define R_C6000_ABS_S16 8 -+#define R_C6000_ABS_L16 9 -+#define R_C6000_ABS_H16 10 -+#define R_C6000_SBR_U15_B 11 -+#define R_C6000_SBR_U15_H 12 -+#define R_C6000_SBR_U15_W 13 -+#define R_C6000_SBR_S16 14 -+#define R_C6000_SBR_L16_B 15 -+#define R_C6000_SBR_L16_H 16 -+#define R_C6000_SBR_L16_W 17 -+#define R_C6000_SBR_H16_B 18 -+#define R_C6000_SBR_H16_H 19 -+#define R_C6000_SBR_H16_W 20 -+#define R_C6000_SBR_GOT_U15_W 21 -+#define R_C6000_SBR_GOT_L16_W 22 -+#define R_C6000_SBR_GOT_H16_W 23 -+#define R_C6000_DSBT_INDEX 24 -+#define R_C6000_PREL31 25 -+#define R_C6000_COPY 26 -+#define R_C6000_JUMP_SLOT 27 -+#define R_C6000_SBR_GOT32 28 -+#define R_C6000_PCR_H16 29 -+#define R_C6000_PCR_L16 30 -+#define R_C6000_ALIGN 253 -+#define R_C6000_FPHEAD 254 -+#define R_C6000_NOCMP 255 -+ -+/* C6x specific values for the Dyn d_tag field. */ -+#define DT_C6000_DSBT_BASE (DT_LOPROC + 0) -+#define DT_C6000_DSBT_SIZE (DT_LOPROC + 1) -+#define DT_C6000_PREEMPTMAP (DT_LOPROC + 2) -+#define DT_C6000_DSBT_INDEX (DT_LOPROC + 3) -+ -+#define DT_C6000_NUM 4 - - static void die(char *fmt, ...); - diff --git a/target/linux/patches/3.4.107/sparc-aout.patch b/target/linux/patches/3.4.107/sparc-aout.patch deleted file mode 100644 index 5cd74c2e7..000000000 --- a/target/linux/patches/3.4.107/sparc-aout.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff -Nur linux-3.4.103.orig/arch/sparc/boot/Makefile linux-3.4.103/arch/sparc/boot/Makefile ---- linux-3.4.103.orig/arch/sparc/boot/Makefile 2014-08-13 20:07:43.000000000 -0500 -+++ linux-3.4.103/arch/sparc/boot/Makefile 2015-02-16 00:26:16.934618259 -0600 -@@ -74,9 +74,6 @@ - $(call if_changed,gzip) - @echo ' kernel: $@ is ready' - --$(obj)/vmlinux.aout: vmlinux FORCE -- $(call if_changed,elftoaout) -- @echo ' kernel: $@ is ready' - else - - # The following lines make a readable image for U-Boot. -@@ -107,6 +104,10 @@ - - endif - -+$(obj)/vmlinux.aout: $(obj)/image FORCE -+ $(call if_changed,elftoaout) -+ @echo ' kernel: $@ is ready' -+ - $(obj)/tftpboot.img: $(obj)/image $(obj)/piggyback System.map $(ROOT_IMG) FORCE - $(call if_changed,elftoaout) - $(call if_changed,piggy) diff --git a/target/linux/patches/3.4.107/sparc-include.patch b/target/linux/patches/3.4.107/sparc-include.patch deleted file mode 100644 index 2f8ffd061..000000000 --- a/target/linux/patches/3.4.107/sparc-include.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-2.6.39-rc7.orig/arch/sparc/boot/btfixupprep.c linux-2.6.39-rc7/arch/sparc/boot/btfixupprep.c ---- linux-2.6.39-rc7.orig/arch/sparc/boot/btfixupprep.c 2011-05-10 04:33:54.000000000 +0200 -+++ linux-2.6.39-rc7/arch/sparc/boot/btfixupprep.c 2011-05-21 13:34:40.000000000 +0200 -@@ -25,7 +25,6 @@ - #include - #include - #include --#include - - #define MAXSYMS 1024 - diff --git a/target/linux/patches/3.4.107/startup.patch b/target/linux/patches/3.4.107/startup.patch deleted file mode 100644 index c26430bcb..000000000 --- a/target/linux/patches/3.4.107/startup.patch +++ /dev/null @@ -1,34 +0,0 @@ -diff -Nur linux-3.4.82.orig/init/initramfs.c linux-3.4.82/init/initramfs.c ---- linux-3.4.82.orig/init/initramfs.c 2014-02-22 19:33:35.000000000 +0100 -+++ linux-3.4.82/init/initramfs.c 2014-03-15 18:46:22.674928245 +0100 -@@ -606,6 +606,9 @@ - free_initrd(); - #endif - } -+#ifdef CONFIG_DEVTMPFS_MOUNT -+ devtmpfs_mount("dev"); -+#endif - return 0; - } - rootfs_initcall(populate_rootfs); -diff -Nur linux-3.4.82.orig/init/main.c linux-3.4.82/init/main.c ---- linux-3.4.82.orig/init/main.c 2014-02-22 19:33:35.000000000 +0100 -+++ linux-3.4.82/init/main.c 2014-03-15 18:46:22.674928245 +0100 -@@ -873,6 +873,8 @@ - if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - printk(KERN_WARNING "Warning: unable to open an initial console.\n"); - -+ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); -+ - (void) sys_dup(0); - (void) sys_dup(0); - /* -@@ -881,7 +883,7 @@ - */ - - if (!ramdisk_execute_command) -- ramdisk_execute_command = "/init"; -+ ramdisk_execute_command = "/sbin/init"; - - if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { - ramdisk_execute_command = NULL; diff --git a/target/linux/patches/3.4.107/usb-defaults-off.patch b/target/linux/patches/3.4.107/usb-defaults-off.patch deleted file mode 100644 index 31367108a..000000000 --- a/target/linux/patches/3.4.107/usb-defaults-off.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff -Nur linux-2.6.37.orig//drivers/usb/core/Kconfig linux-2.6.37/drivers/usb/core/Kconfig ---- linux-2.6.37.orig//drivers/usb/core/Kconfig 2011-01-05 01:50:19.000000000 +0100 -+++ linux-2.6.37/drivers/usb/core/Kconfig 2011-04-12 19:04:23.000000000 +0200 -@@ -59,7 +59,7 @@ - config USB_DEVICE_CLASS - bool "USB device class-devices (DEPRECATED)" - depends on USB -- default y -+ default n - ---help--- - Userspace access to USB devices is granted by device-nodes exported - directly from the usbdev in sysfs. Old versions of the driver -diff -Nur linux-2.6.37.orig//drivers/usb/host/Kconfig linux-2.6.37/drivers/usb/host/Kconfig ---- linux-2.6.37.orig//drivers/usb/host/Kconfig 2011-01-05 01:50:19.000000000 +0100 -+++ linux-2.6.37/drivers/usb/host/Kconfig 2011-04-12 19:04:48.000000000 +0200 -@@ -62,6 +62,7 @@ - config USB_EHCI_ROOT_HUB_TT - bool "Root Hub Transaction Translators" - depends on USB_EHCI_HCD -+ default n - ---help--- - Some EHCI chips have vendor-specific extensions to integrate - transaction translators, so that no OHCI or UHCI companion -@@ -74,7 +75,7 @@ - config USB_EHCI_TT_NEWSCHED - bool "Improved Transaction Translator scheduling" - depends on USB_EHCI_HCD -- default y -+ default n - ---help--- - This changes the periodic scheduling code to fill more of the low - and full speed bandwidth available from the Transaction Translator diff --git a/target/linux/patches/3.4.107/vga-cons-default-off.patch b/target/linux/patches/3.4.107/vga-cons-default-off.patch deleted file mode 100644 index 178aeeeb9..000000000 --- a/target/linux/patches/3.4.107/vga-cons-default-off.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-2.6.37.orig//drivers/video/console/Kconfig linux-2.6.37/drivers/video/console/Kconfig ---- linux-2.6.37.orig//drivers/video/console/Kconfig 2011-01-05 01:50:19.000000000 +0100 -+++ linux-2.6.37/drivers/video/console/Kconfig 2011-04-12 16:29:34.000000000 +0200 -@@ -7,7 +7,7 @@ - config VGA_CONSOLE - bool "VGA text console" if EMBEDDED || !X86 - depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) -- default y -+ default n - help - Saying Y here will allow you to use Linux in text mode through a - display that complies with the generic VGA standard. Virtually diff --git a/target/linux/patches/3.4.107/wlan-cf.patch b/target/linux/patches/3.4.107/wlan-cf.patch deleted file mode 100644 index fc20759e2..000000000 --- a/target/linux/patches/3.4.107/wlan-cf.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c ---- linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c 2011-05-19 06:06:34.000000000 +0200 -+++ linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c 2011-09-12 02:46:26.987984145 +0200 -@@ -623,6 +623,7 @@ - static struct pcmcia_device_id hostap_cs_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), - PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), -+ PCMCIA_DEVICE_MANF_CARD(0x0004, 0x2003), - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), - PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), - PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), diff --git a/target/linux/patches/3.4.107/yaffs2.patch b/target/linux/patches/3.4.107/yaffs2.patch deleted file mode 100644 index 44c95915f..000000000 --- a/target/linux/patches/3.4.107/yaffs2.patch +++ /dev/null @@ -1,16550 +0,0 @@ -diff -Nur linux-3.4.90.orig/fs/Kconfig linux-3.4.90/fs/Kconfig ---- linux-3.4.90.orig/fs/Kconfig 2014-05-13 14:11:45.000000000 +0200 -+++ linux-3.4.90/fs/Kconfig 2014-05-17 15:08:09.000000000 +0200 -@@ -203,6 +203,7 @@ - source "fs/befs/Kconfig" - source "fs/bfs/Kconfig" - source "fs/efs/Kconfig" -+source "fs/yaffs2/Kconfig" - source "fs/jffs2/Kconfig" - # UBIFS File system configuration - source "fs/ubifs/Kconfig" -diff -Nur linux-3.4.90.orig/fs/Makefile linux-3.4.90/fs/Makefile ---- linux-3.4.90.orig/fs/Makefile 2014-05-13 14:11:45.000000000 +0200 -+++ linux-3.4.90/fs/Makefile 2014-05-17 15:09:05.000000000 +0200 -@@ -97,6 +97,7 @@ - obj-$(CONFIG_UFS_FS) += ufs/ - obj-$(CONFIG_EFS_FS) += efs/ - obj-$(CONFIG_JFFS2_FS) += jffs2/ -+obj-$(CONFIG_YAFFS_FS) += yaffs2/ - obj-$(CONFIG_LOGFS) += logfs/ - obj-$(CONFIG_UBIFS_FS) += ubifs/ - obj-$(CONFIG_AFFS_FS) += affs/ -diff -Nur linux-3.4.90.orig/fs/yaffs2/Kconfig linux-3.4.90/fs/yaffs2/Kconfig ---- linux-3.4.90.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/Kconfig 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,171 @@ -+# -+# yaffs file system configurations -+# -+ -+config YAFFS_FS -+ tristate "yaffs2 file system support" -+ default n -+ depends on MTD_BLOCK -+ select YAFFS_YAFFS1 -+ select YAFFS_YAFFS2 -+ help -+ yaffs2, or Yet Another Flash File System, is a file system -+ optimised for NAND Flash chips. -+ -+ To compile the yaffs2 file system support as a module, choose M -+ here: the module will be called yaffs2. -+ -+ If unsure, say N. -+ -+ Further information on yaffs2 is available at -+ . -+ -+config YAFFS_YAFFS1 -+ bool "512 byte / page devices" -+ depends on YAFFS_FS -+ default y -+ help -+ Enable yaffs1 support -- yaffs for 512 byte / page devices -+ -+ Not needed for 2K-page devices. -+ -+ If unsure, say Y. -+ -+config YAFFS_9BYTE_TAGS -+ bool "Use older-style on-NAND data format with pageStatus byte" -+ depends on YAFFS_YAFFS1 -+ default n -+ help -+ -+ Older-style on-NAND data format has a "pageStatus" byte to record -+ chunk/page state. This byte is zero when the page is discarded. -+ Choose this option if you have existing on-NAND data using this -+ format that you need to continue to support. New data written -+ also uses the older-style format. Note: Use of this option -+ generally requires that MTD's oob layout be adjusted to use the -+ older-style format. See notes on tags formats and MTD versions -+ in yaffs_mtdif1.c. -+ -+ If unsure, say N. -+ -+config YAFFS_DOES_ECC -+ bool "Lets yaffs do its own ECC" -+ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS -+ default n -+ help -+ This enables yaffs to use its own ECC functions instead of using -+ the ones from the generic MTD-NAND driver. -+ -+ If unsure, say N. -+ -+config YAFFS_ECC_WRONG_ORDER -+ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" -+ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS -+ default n -+ help -+ This makes yaffs_ecc.c use the same ecc byte order as Steven -+ Hill's nand_ecc.c. If not set, then you get the same ecc byte -+ order as SmartMedia. -+ -+ If unsure, say N. -+ -+config YAFFS_YAFFS2 -+ bool "2048 byte (or larger) / page devices" -+ depends on YAFFS_FS -+ default y -+ help -+ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices -+ -+ If unsure, say Y. -+ -+config YAFFS_AUTO_YAFFS2 -+ bool "Autoselect yaffs2 format" -+ depends on YAFFS_YAFFS2 -+ default y -+ help -+ Without this, you need to explicitely use yaffs2 as the file -+ system type. With this, you can say "yaffs" and yaffs or yaffs2 -+ will be used depending on the device page size (yaffs on -+ 512-byte page devices, yaffs2 on 2K page devices). -+ -+ If unsure, say Y. -+ -+config YAFFS_DISABLE_TAGS_ECC -+ bool "Disable yaffs from doing ECC on tags by default" -+ depends on YAFFS_FS && YAFFS_YAFFS2 -+ default n -+ help -+ This defaults yaffs to using its own ECC calculations on tags instead of -+ just relying on the MTD. -+ This behavior can also be overridden with tags_ecc_on and -+ tags_ecc_off mount options. -+ -+ If unsure, say N. -+ -+config YAFFS_ALWAYS_CHECK_CHUNK_ERASED -+ bool "Force chunk erase check" -+ depends on YAFFS_FS -+ default n -+ help -+ Normally yaffs only checks chunks before writing until an erased -+ chunk is found. This helps to detect any partially written -+ chunks that might have happened due to power loss. -+ -+ Enabling this forces on the test that chunks are erased in flash -+ before writing to them. This takes more time but is potentially -+ a bit more secure. -+ -+ Suggest setting Y during development and ironing out driver -+ issues etc. Suggest setting to N if you want faster writing. -+ -+ If unsure, say Y. -+ -+config YAFFS_EMPTY_LOST_AND_FOUND -+ bool "Empty lost and found on boot" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is enabled then the contents of lost and found is -+ automatically dumped at mount. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BLOCK_REFRESHING -+ bool "Disable yaffs2 block refreshing" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is set, then block refreshing is disabled. -+ Block refreshing infrequently refreshes the oldest block in -+ a yaffs2 file system. This mechanism helps to refresh flash to -+ mitigate against data loss. This is particularly useful for MLC. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BACKGROUND -+ bool "Disable yaffs2 background processing" -+ depends on YAFFS_FS -+ default n -+ help -+ If this is set, then background processing is disabled. -+ Background processing makes many foreground activities faster. -+ -+ If unsure, say N. -+ -+config YAFFS_DISABLE_BAD_BLOCK_MARKING -+ bool "Disable yaffs2 bad block marking" -+ depends on YAFFS_FS -+ default n -+ help -+ Useful during early flash bring up to prevent problems causing -+ lots of bad block marking. -+ -+ If unsure, say N. -+ -+config YAFFS_XATTR -+ bool "Enable yaffs2 xattr support" -+ depends on YAFFS_FS -+ default y -+ help -+ If this is set then yaffs2 will provide xattr support. -+ If unsure, say Y. -diff -Nur linux-3.4.90.orig/fs/yaffs2/Makefile linux-3.4.90/fs/yaffs2/Makefile ---- linux-3.4.90.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/Makefile 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,18 @@ -+# -+# Makefile for the linux YAFFS filesystem routines. -+# -+ -+obj-$(CONFIG_YAFFS_FS) += yaffs.o -+ -+yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o -+yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o -+yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o -+yaffs-y += yaffs_mtdif.o -+yaffs-y += yaffs_nameval.o yaffs_attribs.o -+yaffs-y += yaffs_allocator.o -+yaffs-y += yaffs_yaffs1.o -+yaffs-y += yaffs_yaffs2.o -+yaffs-y += yaffs_bitmap.o -+yaffs-y += yaffs_summary.o -+yaffs-y += yaffs_verify.o -+ -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.c linux-3.4.90/fs/yaffs2/yaffs_allocator.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_allocator.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,357 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_allocator.h" -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yportenv.h" -+ -+/* -+ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks -+ * of approx 100 objects that are themn allocated singly. -+ * This is basically a simplified slab allocator. -+ * -+ * We don't use the Linux slab allocator because slab does not allow -+ * us to dump all the objects in one hit when we do a umount and tear -+ * down all the tnodes and objects. slab requires that we first free -+ * the individual objects. -+ * -+ * Once yaffs has been mainlined I shall try to motivate for a change -+ * to slab to provide the extra features we need here. -+ */ -+ -+struct yaffs_tnode_list { -+ struct yaffs_tnode_list *next; -+ struct yaffs_tnode *tnodes; -+}; -+ -+struct yaffs_obj_list { -+ struct yaffs_obj_list *next; -+ struct yaffs_obj *objects; -+}; -+ -+struct yaffs_allocator { -+ int n_tnodes_created; -+ struct yaffs_tnode *free_tnodes; -+ int n_free_tnodes; -+ struct yaffs_tnode_list *alloc_tnode_list; -+ -+ int n_obj_created; -+ struct list_head free_objs; -+ int n_free_objects; -+ -+ struct yaffs_obj_list *allocated_obj_list; -+}; -+ -+static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ struct yaffs_tnode_list *tmp; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ while (allocator->alloc_tnode_list) { -+ tmp = allocator->alloc_tnode_list->next; -+ -+ kfree(allocator->alloc_tnode_list->tnodes); -+ kfree(allocator->alloc_tnode_list); -+ allocator->alloc_tnode_list = tmp; -+ } -+ -+ allocator->free_tnodes = NULL; -+ allocator->n_free_tnodes = 0; -+ allocator->n_tnodes_created = 0; -+} -+ -+static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator->alloc_tnode_list = NULL; -+ allocator->free_tnodes = NULL; -+ allocator->n_free_tnodes = 0; -+ allocator->n_tnodes_created = 0; -+} -+ -+static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ int i; -+ struct yaffs_tnode *new_tnodes; -+ u8 *mem; -+ struct yaffs_tnode *curr; -+ struct yaffs_tnode *next; -+ struct yaffs_tnode_list *tnl; -+ -+ if (!allocator) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ if (n_tnodes < 1) -+ return YAFFS_OK; -+ -+ /* make these things */ -+ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); -+ mem = (u8 *) new_tnodes; -+ -+ if (!new_tnodes) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs: Could not allocate Tnodes"); -+ return YAFFS_FAIL; -+ } -+ -+ /* New hookup for wide tnodes */ -+ for (i = 0; i < n_tnodes - 1; i++) { -+ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; -+ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; -+ curr->internal[0] = next; -+ } -+ -+ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; -+ curr->internal[0] = allocator->free_tnodes; -+ allocator->free_tnodes = (struct yaffs_tnode *)mem; -+ -+ allocator->n_free_tnodes += n_tnodes; -+ allocator->n_tnodes_created += n_tnodes; -+ -+ /* Now add this bunch of tnodes to a list for freeing up. -+ * NB If we can't add this to the management list it isn't fatal -+ * but it just means we can't free this bunch of tnodes later. -+ */ -+ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); -+ if (!tnl) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Could not add tnodes to management list"); -+ return YAFFS_FAIL; -+ } else { -+ tnl->tnodes = new_tnodes; -+ tnl->next = allocator->alloc_tnode_list; -+ allocator->alloc_tnode_list = tnl; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); -+ -+ return YAFFS_OK; -+} -+ -+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = -+ (struct yaffs_allocator *)dev->allocator; -+ struct yaffs_tnode *tn = NULL; -+ -+ if (!allocator) { -+ BUG(); -+ return NULL; -+ } -+ -+ /* If there are none left make more */ -+ if (!allocator->free_tnodes) -+ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); -+ -+ if (allocator->free_tnodes) { -+ tn = allocator->free_tnodes; -+ allocator->free_tnodes = allocator->free_tnodes->internal[0]; -+ allocator->n_free_tnodes--; -+ } -+ -+ return tn; -+} -+ -+/* FreeTnode frees up a tnode and puts it back on the free list */ -+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ if (tn) { -+ tn->internal[0] = allocator->free_tnodes; -+ allocator->free_tnodes = tn; -+ allocator->n_free_tnodes++; -+ } -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+/*--------------- yaffs_obj alloaction ------------------------ -+ * -+ * Free yaffs_objs are stored in a list using obj->siblings. -+ * The blocks of allocated objects are stored in a linked list. -+ */ -+ -+static void yaffs_init_raw_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator->allocated_obj_list = NULL; -+ INIT_LIST_HEAD(&allocator->free_objs); -+ allocator->n_free_objects = 0; -+} -+ -+static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ struct yaffs_obj_list *tmp; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ while (allocator->allocated_obj_list) { -+ tmp = allocator->allocated_obj_list->next; -+ kfree(allocator->allocated_obj_list->objects); -+ kfree(allocator->allocated_obj_list); -+ allocator->allocated_obj_list = tmp; -+ } -+ -+ INIT_LIST_HEAD(&allocator->free_objs); -+ allocator->n_free_objects = 0; -+ allocator->n_obj_created = 0; -+} -+ -+static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) -+{ -+ struct yaffs_allocator *allocator = dev->allocator; -+ int i; -+ struct yaffs_obj *new_objs; -+ struct yaffs_obj_list *list; -+ -+ if (!allocator) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ if (n_obj < 1) -+ return YAFFS_OK; -+ -+ /* make these things */ -+ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); -+ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); -+ -+ if (!new_objs || !list) { -+ kfree(new_objs); -+ new_objs = NULL; -+ kfree(list); -+ list = NULL; -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, -+ "Could not allocate more objects"); -+ return YAFFS_FAIL; -+ } -+ -+ /* Hook them into the free list */ -+ for (i = 0; i < n_obj; i++) -+ list_add(&new_objs[i].siblings, &allocator->free_objs); -+ -+ allocator->n_free_objects += n_obj; -+ allocator->n_obj_created += n_obj; -+ -+ /* Now add this bunch of Objects to a list for freeing up. */ -+ -+ list->objects = new_objs; -+ list->next = allocator->allocated_obj_list; -+ allocator->allocated_obj_list = list; -+ -+ return YAFFS_OK; -+} -+ -+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj = NULL; -+ struct list_head *lh; -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return obj; -+ } -+ -+ /* If there are none left make more */ -+ if (list_empty(&allocator->free_objs)) -+ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); -+ -+ if (!list_empty(&allocator->free_objs)) { -+ lh = allocator->free_objs.next; -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ list_del_init(lh); -+ allocator->n_free_objects--; -+ } -+ -+ return obj; -+} -+ -+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) -+{ -+ -+ struct yaffs_allocator *allocator = dev->allocator; -+ -+ if (!allocator) { -+ BUG(); -+ return; -+ } -+ -+ /* Link into the free list. */ -+ list_add(&obj->siblings, &allocator->free_objs); -+ allocator->n_free_objects++; -+} -+ -+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ -+ if (!dev->allocator) { -+ BUG(); -+ return; -+ } -+ -+ yaffs_deinit_raw_tnodes(dev); -+ yaffs_deinit_raw_objs(dev); -+ kfree(dev->allocator); -+ dev->allocator = NULL; -+} -+ -+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_allocator *allocator; -+ -+ if (dev->allocator) { -+ BUG(); -+ return; -+ } -+ -+ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); -+ if (allocator) { -+ dev->allocator = allocator; -+ yaffs_init_raw_tnodes(dev); -+ yaffs_init_raw_objs(dev); -+ } -+} -+ -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.h linux-3.4.90/fs/yaffs2/yaffs_allocator.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_allocator.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,30 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_ALLOCATOR_H__ -+#define __YAFFS_ALLOCATOR_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); -+void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); -+ -+struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); -+void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); -+ -+struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); -+void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.c linux-3.4.90/fs/yaffs2/yaffs_attribs.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_attribs.c 2014-05-17 16:37:59.000000000 +0200 -@@ -0,0 +1,166 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_attribs.h" -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+static inline uid_t ia_uid_read(const struct iattr *iattr) -+{ -+ return from_kuid(&init_user_ns, iattr->ia_uid); -+} -+ -+static inline gid_t ia_gid_read(const struct iattr *iattr) -+{ -+ return from_kgid(&init_user_ns, iattr->ia_gid); -+} -+ -+static inline void ia_uid_write(struct iattr *iattr, uid_t uid) -+{ -+ iattr->ia_uid = make_kuid(&init_user_ns, uid); -+} -+ -+static inline void ia_gid_write(struct iattr *iattr, gid_t gid) -+{ -+ iattr->ia_gid = make_kgid(&init_user_ns, gid); -+} -+#else -+static inline uid_t ia_uid_read(const struct iattr *iattr) -+{ -+ return iattr->ia_uid; -+} -+ -+static inline gid_t ia_gid_read(const struct iattr *iattr) -+{ -+ return iattr->ia_gid; -+} -+ -+static inline void ia_uid_write(struct iattr *iattr, uid_t uid) -+{ -+ iattr->ia_uid = uid; -+} -+ -+static inline void ia_gid_write(struct iattr *iattr, gid_t gid) -+{ -+ iattr->ia_gid = gid; -+} -+#endif -+ -+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) -+{ -+ obj->yst_uid = oh->yst_uid; -+ obj->yst_gid = oh->yst_gid; -+ obj->yst_atime = oh->yst_atime; -+ obj->yst_mtime = oh->yst_mtime; -+ obj->yst_ctime = oh->yst_ctime; -+ obj->yst_rdev = oh->yst_rdev; -+} -+ -+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) -+{ -+ oh->yst_uid = obj->yst_uid; -+ oh->yst_gid = obj->yst_gid; -+ oh->yst_atime = obj->yst_atime; -+ oh->yst_mtime = obj->yst_mtime; -+ oh->yst_ctime = obj->yst_ctime; -+ oh->yst_rdev = obj->yst_rdev; -+ -+} -+ -+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) -+{ -+ obj->yst_mtime = Y_CURRENT_TIME; -+ if (do_a) -+ obj->yst_atime = obj->yst_mtime; -+ if (do_c) -+ obj->yst_ctime = obj->yst_mtime; -+} -+ -+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) -+{ -+ yaffs_load_current_time(obj, 1, 1); -+ obj->yst_rdev = rdev; -+ obj->yst_uid = uid; -+ obj->yst_gid = gid; -+} -+ -+static loff_t yaffs_get_file_size(struct yaffs_obj *obj) -+{ -+ YCHAR *alias = NULL; -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return obj->variant.file_variant.file_size; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ alias = obj->variant.symlink_variant.alias; -+ if (!alias) -+ return 0; -+ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); -+ default: -+ return 0; -+ } -+} -+ -+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) -+{ -+ unsigned int valid = attr->ia_valid; -+ -+ if (valid & ATTR_MODE) -+ obj->yst_mode = attr->ia_mode; -+ if (valid & ATTR_UID) -+ obj->yst_uid = ia_uid_read(attr); -+ if (valid & ATTR_GID) -+ obj->yst_gid = ia_gid_read(attr); -+ -+ if (valid & ATTR_ATIME) -+ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); -+ if (valid & ATTR_CTIME) -+ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); -+ if (valid & ATTR_MTIME) -+ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); -+ -+ if (valid & ATTR_SIZE) -+ yaffs_resize_file(obj, attr->ia_size); -+ -+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); -+ -+ return YAFFS_OK; -+ -+} -+ -+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) -+{ -+ unsigned int valid = 0; -+ -+ attr->ia_mode = obj->yst_mode; -+ valid |= ATTR_MODE; -+ ia_uid_write(attr, obj->yst_uid); -+ valid |= ATTR_UID; -+ ia_gid_write(attr, obj->yst_gid); -+ valid |= ATTR_GID; -+ -+ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; -+ valid |= ATTR_ATIME; -+ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; -+ valid |= ATTR_CTIME; -+ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; -+ valid |= ATTR_MTIME; -+ -+ attr->ia_size = yaffs_get_file_size(obj); -+ valid |= ATTR_SIZE; -+ -+ attr->ia_valid = valid; -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.h linux-3.4.90/fs/yaffs2/yaffs_attribs.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_attribs.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,28 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_ATTRIBS_H__ -+#define __YAFFS_ATTRIBS_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); -+void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); -+void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); -+void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); -+int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); -+int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.c linux-3.4.90/fs/yaffs2/yaffs_bitmap.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_bitmap.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,97 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_bitmap.h" -+#include "yaffs_trace.h" -+/* -+ * Chunk bitmap manipulations -+ */ -+ -+static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "BlockBits block %d is not valid", -+ blk); -+ BUG(); -+ } -+ return dev->chunk_bits + -+ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); -+} -+ -+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block || -+ chunk < 0 || chunk >= dev->param.chunks_per_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Chunk Id (%d:%d) invalid", -+ blk, chunk); -+ BUG(); -+ } -+} -+ -+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ memset(blk_bits, 0, dev->chunk_bit_stride); -+} -+ -+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); -+} -+ -+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ blk_bits[chunk / 8] |= (1 << (chunk & 7)); -+} -+ -+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ -+ yaffs_verify_chunk_bit_id(dev, blk, chunk); -+ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; -+} -+ -+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ int i; -+ -+ for (i = 0; i < dev->chunk_bit_stride; i++) { -+ if (*blk_bits) -+ return 1; -+ blk_bits++; -+ } -+ return 0; -+} -+ -+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) -+{ -+ u8 *blk_bits = yaffs_block_bits(dev, blk); -+ int i; -+ int n = 0; -+ -+ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) -+ n += hweight8(*blk_bits); -+ -+ return n; -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.h linux-3.4.90/fs/yaffs2/yaffs_bitmap.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_bitmap.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,33 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* -+ * Chunk bitmap manipulations -+ */ -+ -+#ifndef __YAFFS_BITMAP_H__ -+#define __YAFFS_BITMAP_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); -+void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); -+void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); -+int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); -+int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.4.90/fs/yaffs2/yaffs_checkptrw.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,474 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_checkptrw.h" -+#include "yaffs_getblockinfo.h" -+ -+struct yaffs_checkpt_chunk_hdr { -+ int version; -+ int seq; -+ u32 sum; -+ u32 xor; -+} ; -+ -+ -+static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) -+{ -+ return chunk - dev->chunk_offset; -+} -+ -+static int apply_block_offset(struct yaffs_dev *dev, int block) -+{ -+ return block - dev->block_offset; -+} -+ -+static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_chunk_hdr hdr; -+ -+ hdr.version = YAFFS_CHECKPOINT_VERSION; -+ hdr.seq = dev->checkpt_page_seq; -+ hdr.sum = dev->checkpt_sum; -+ hdr.xor = dev->checkpt_xor; -+ -+ dev->checkpt_byte_offs = sizeof(hdr); -+ -+ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); -+} -+ -+static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_chunk_hdr hdr; -+ -+ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); -+ -+ dev->checkpt_byte_offs = sizeof(hdr); -+ -+ return hdr.version == YAFFS_CHECKPOINT_VERSION && -+ hdr.seq == dev->checkpt_page_seq && -+ hdr.sum == dev->checkpt_sum && -+ hdr.xor == dev->checkpt_xor; -+} -+ -+static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) -+{ -+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpt blocks_avail = %d", blocks_avail); -+ -+ return (blocks_avail <= 0) ? 0 : 1; -+} -+ -+static int yaffs_checkpt_erase(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (!dev->drv.drv_erase_fn) -+ return 0; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checking blocks %d to %d", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); -+ int offset_i = apply_block_offset(dev, i); -+ int result; -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "erasing checkpt block %d", i); -+ -+ dev->n_erasures++; -+ -+ result = dev->drv.drv_erase_fn(dev, offset_i); -+ if(result) { -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += -+ dev->param.chunks_per_block; -+ } else { -+ dev->drv.drv_mark_bad_fn(dev, offset_i); -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ } -+ } -+ } -+ -+ dev->blocks_in_checkpt = 0; -+ -+ return 1; -+} -+ -+static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) -+{ -+ int i; -+ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "allocating checkpt block: erased %d reserved %d avail %d next %d ", -+ dev->n_erased_blocks, dev->param.n_reserved_blocks, -+ blocks_avail, dev->checkpt_next_block); -+ -+ if (dev->checkpt_next_block >= 0 && -+ dev->checkpt_next_block <= dev->internal_end_block && -+ blocks_avail > 0) { -+ -+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; -+ i++) { -+ struct yaffs_block_info *bi; -+ -+ bi = yaffs_get_block_info(dev, i); -+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ dev->checkpt_next_block = i + 1; -+ dev->checkpt_cur_block = i; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "allocating checkpt block %d", i); -+ return; -+ } -+ } -+ } -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); -+ -+ dev->checkpt_next_block = -1; -+ dev->checkpt_cur_block = -1; -+} -+ -+static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) -+{ -+ int i; -+ struct yaffs_ext_tags tags; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "find next checkpt block: start: blocks %d next %d", -+ dev->blocks_in_checkpt, dev->checkpt_next_block); -+ -+ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) -+ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; -+ i++) { -+ int chunk = i * dev->param.chunks_per_block; -+ enum yaffs_block_state state; -+ u32 seq; -+ -+ dev->tagger.read_chunk_tags_fn(dev, -+ apply_chunk_offset(dev, chunk), -+ NULL, &tags); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", -+ i, (int) state, -+ tags.obj_id, tags.seq_number, -+ tags.ecc_result); -+ -+ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ continue; -+ -+ dev->tagger.query_block_fn(dev, -+ apply_block_offset(dev, i), -+ &state, &seq); -+ if (state == YAFFS_BLOCK_STATE_DEAD) -+ continue; -+ -+ /* Right kind of block */ -+ dev->checkpt_next_block = tags.obj_id; -+ dev->checkpt_cur_block = i; -+ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; -+ dev->blocks_in_checkpt++; -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "found checkpt block %d", i); -+ return; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); -+ -+ dev->checkpt_next_block = -1; -+ dev->checkpt_cur_block = -1; -+} -+ -+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) -+{ -+ int i; -+ -+ dev->checkpt_open_write = writing; -+ -+ /* Got the functions we need? */ -+ if (!dev->tagger.write_chunk_tags_fn || -+ !dev->tagger.read_chunk_tags_fn || -+ !dev->drv.drv_erase_fn || -+ !dev->drv.drv_mark_bad_fn) -+ return 0; -+ -+ if (writing && !yaffs2_checkpt_space_ok(dev)) -+ return 0; -+ -+ if (!dev->checkpt_buffer) -+ dev->checkpt_buffer = -+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ dev->checkpt_page_seq = 0; -+ dev->checkpt_byte_count = 0; -+ dev->checkpt_sum = 0; -+ dev->checkpt_xor = 0; -+ dev->checkpt_cur_block = -1; -+ dev->checkpt_cur_chunk = -1; -+ dev->checkpt_next_block = dev->internal_start_block; -+ -+ if (writing) { -+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); -+ yaffs2_checkpt_init_chunk_hdr(dev); -+ return yaffs_checkpt_erase(dev); -+ } -+ -+ /* Opening for a read */ -+ /* Set to a value that will kick off a read */ -+ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; -+ /* A checkpoint block list of 1 checkpoint block per 16 block is -+ * (hopefully) going to be way more than we need */ -+ dev->blocks_in_checkpt = 0; -+ dev->checkpt_max_blocks = -+ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; -+ dev->checkpt_block_list = -+ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); -+ -+ if (!dev->checkpt_block_list) -+ return 0; -+ -+ for (i = 0; i < dev->checkpt_max_blocks; i++) -+ dev->checkpt_block_list[i] = -1; -+ -+ return 1; -+} -+ -+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) -+{ -+ u32 composite_sum; -+ -+ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); -+ *sum = composite_sum; -+ return 1; -+} -+ -+static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) -+{ -+ int chunk; -+ int offset_chunk; -+ struct yaffs_ext_tags tags; -+ -+ if (dev->checkpt_cur_block < 0) { -+ yaffs2_checkpt_find_erased_block(dev); -+ dev->checkpt_cur_chunk = 0; -+ } -+ -+ if (dev->checkpt_cur_block < 0) -+ return 0; -+ -+ tags.is_deleted = 0; -+ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ -+ tags.chunk_id = dev->checkpt_page_seq + 1; -+ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; -+ tags.n_bytes = dev->data_bytes_per_chunk; -+ if (dev->checkpt_cur_chunk == 0) { -+ /* First chunk we write for the block? Set block state to -+ checkpoint */ -+ struct yaffs_block_info *bi = -+ yaffs_get_block_info(dev, dev->checkpt_cur_block); -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ dev->blocks_in_checkpt++; -+ } -+ -+ chunk = -+ dev->checkpt_cur_block * dev->param.chunks_per_block + -+ dev->checkpt_cur_chunk; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", -+ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, -+ tags.obj_id, tags.chunk_id); -+ -+ offset_chunk = apply_chunk_offset(dev, chunk); -+ -+ dev->n_page_writes++; -+ -+ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, -+ dev->checkpt_buffer, &tags); -+ dev->checkpt_page_seq++; -+ dev->checkpt_cur_chunk++; -+ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { -+ dev->checkpt_cur_chunk = 0; -+ dev->checkpt_cur_block = -1; -+ } -+ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); -+ -+ yaffs2_checkpt_init_chunk_hdr(dev); -+ -+ -+ return 1; -+} -+ -+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) -+{ -+ int i = 0; -+ int ok = 1; -+ u8 *data_bytes = (u8 *) data; -+ -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ if (!dev->checkpt_open_write) -+ return -1; -+ -+ while (i < n_bytes && ok) { -+ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; -+ dev->checkpt_sum += *data_bytes; -+ dev->checkpt_xor ^= *data_bytes; -+ -+ dev->checkpt_byte_offs++; -+ i++; -+ data_bytes++; -+ dev->checkpt_byte_count++; -+ -+ if (dev->checkpt_byte_offs < 0 || -+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) -+ ok = yaffs2_checkpt_flush_buffer(dev); -+ } -+ -+ return i; -+} -+ -+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) -+{ -+ int i = 0; -+ int ok = 1; -+ struct yaffs_ext_tags tags; -+ int chunk; -+ int offset_chunk; -+ u8 *data_bytes = (u8 *) data; -+ -+ if (!dev->checkpt_buffer) -+ return 0; -+ -+ if (dev->checkpt_open_write) -+ return -1; -+ -+ while (i < n_bytes && ok) { -+ -+ if (dev->checkpt_byte_offs < 0 || -+ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { -+ -+ if (dev->checkpt_cur_block < 0) { -+ yaffs2_checkpt_find_block(dev); -+ dev->checkpt_cur_chunk = 0; -+ } -+ -+ if (dev->checkpt_cur_block < 0) { -+ ok = 0; -+ break; -+ } -+ -+ chunk = dev->checkpt_cur_block * -+ dev->param.chunks_per_block + -+ dev->checkpt_cur_chunk; -+ -+ offset_chunk = apply_chunk_offset(dev, chunk); -+ dev->n_page_reads++; -+ -+ /* read in the next chunk */ -+ dev->tagger.read_chunk_tags_fn(dev, -+ offset_chunk, -+ dev->checkpt_buffer, -+ &tags); -+ -+ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || -+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || -+ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) { -+ ok = 0; -+ break; -+ } -+ if(!yaffs2_checkpt_check_chunk_hdr(dev)) { -+ ok = 0; -+ break; -+ } -+ -+ dev->checkpt_page_seq++; -+ dev->checkpt_cur_chunk++; -+ -+ if (dev->checkpt_cur_chunk >= -+ dev->param.chunks_per_block) -+ dev->checkpt_cur_block = -1; -+ -+ } -+ -+ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; -+ dev->checkpt_sum += *data_bytes; -+ dev->checkpt_xor ^= *data_bytes; -+ dev->checkpt_byte_offs++; -+ i++; -+ data_bytes++; -+ dev->checkpt_byte_count++; -+ } -+ -+ return i; -+} -+ -+int yaffs_checkpt_close(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (dev->checkpt_open_write) { -+ if (dev->checkpt_byte_offs != -+ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) -+ yaffs2_checkpt_flush_buffer(dev); -+ } else if (dev->checkpt_block_list) { -+ for (i = 0; -+ i < dev->blocks_in_checkpt && -+ dev->checkpt_block_list[i] >= 0; i++) { -+ int blk = dev->checkpt_block_list[i]; -+ struct yaffs_block_info *bi = NULL; -+ -+ if (dev->internal_start_block <= blk && -+ blk <= dev->internal_end_block) -+ bi = yaffs_get_block_info(dev, blk); -+ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ } -+ kfree(dev->checkpt_block_list); -+ dev->checkpt_block_list = NULL; -+ } -+ -+ dev->n_free_chunks -= -+ dev->blocks_in_checkpt * dev->param.chunks_per_block; -+ dev->n_erased_blocks -= dev->blocks_in_checkpt; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", -+ dev->checkpt_byte_count); -+ -+ if (dev->checkpt_buffer) { -+ /* free the buffer */ -+ kfree(dev->checkpt_buffer); -+ dev->checkpt_buffer = NULL; -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ -+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) -+{ -+ /* Erase the checkpoint data */ -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "checkpoint invalidate of %d blocks", -+ dev->blocks_in_checkpt); -+ -+ return yaffs_checkpt_erase(dev); -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.4.90/fs/yaffs2/yaffs_checkptrw.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,33 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_CHECKPTRW_H__ -+#define __YAFFS_CHECKPTRW_H__ -+ -+#include "yaffs_guts.h" -+ -+int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); -+ -+int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); -+ -+int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); -+ -+int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); -+ -+int yaffs_checkpt_close(struct yaffs_dev *dev); -+ -+int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.c linux-3.4.90/fs/yaffs2/yaffs_ecc.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_ecc.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,281 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * This code implements the ECC algorithm used in SmartMedia. -+ * -+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. -+ * The two unused bit are set to 1. -+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two -+ * such ECC blocks are used on a 512-byte NAND page. -+ * -+ */ -+ -+#include "yportenv.h" -+ -+#include "yaffs_ecc.h" -+ -+/* Table generated by gen-ecc.c -+ * Using a table means we do not have to calculate p1..p4 and p1'..p4' -+ * for each byte of data. These are instead provided in a table in bits7..2. -+ * Bit 0 of each entry indicates whether the entry has an odd or even parity, -+ * and therefore this bytes influence on the line parity. -+ */ -+ -+static const unsigned char column_parity_table[] = { -+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, -+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, -+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, -+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, -+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, -+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, -+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, -+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, -+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, -+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, -+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, -+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, -+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, -+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, -+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, -+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, -+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, -+ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, -+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, -+ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, -+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, -+ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, -+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, -+ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, -+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, -+ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, -+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, -+ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, -+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, -+ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, -+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, -+ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, -+}; -+ -+ -+/* Calculate the ECC for a 256-byte block of data */ -+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) -+{ -+ unsigned int i; -+ unsigned char col_parity = 0; -+ unsigned char line_parity = 0; -+ unsigned char line_parity_prime = 0; -+ unsigned char t; -+ unsigned char b; -+ -+ for (i = 0; i < 256; i++) { -+ b = column_parity_table[*data++]; -+ col_parity ^= b; -+ -+ if (b & 0x01) { /* odd number of bits in the byte */ -+ line_parity ^= i; -+ line_parity_prime ^= ~i; -+ } -+ } -+ -+ ecc[2] = (~col_parity) | 0x03; -+ -+ t = 0; -+ if (line_parity & 0x80) -+ t |= 0x80; -+ if (line_parity_prime & 0x80) -+ t |= 0x40; -+ if (line_parity & 0x40) -+ t |= 0x20; -+ if (line_parity_prime & 0x40) -+ t |= 0x10; -+ if (line_parity & 0x20) -+ t |= 0x08; -+ if (line_parity_prime & 0x20) -+ t |= 0x04; -+ if (line_parity & 0x10) -+ t |= 0x02; -+ if (line_parity_prime & 0x10) -+ t |= 0x01; -+ ecc[1] = ~t; -+ -+ t = 0; -+ if (line_parity & 0x08) -+ t |= 0x80; -+ if (line_parity_prime & 0x08) -+ t |= 0x40; -+ if (line_parity & 0x04) -+ t |= 0x20; -+ if (line_parity_prime & 0x04) -+ t |= 0x10; -+ if (line_parity & 0x02) -+ t |= 0x08; -+ if (line_parity_prime & 0x02) -+ t |= 0x04; -+ if (line_parity & 0x01) -+ t |= 0x02; -+ if (line_parity_prime & 0x01) -+ t |= 0x01; -+ ecc[0] = ~t; -+ -+} -+ -+/* Correct the ECC on a 256 byte block of data */ -+ -+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, -+ const unsigned char *test_ecc) -+{ -+ unsigned char d0, d1, d2; /* deltas */ -+ -+ d0 = read_ecc[0] ^ test_ecc[0]; -+ d1 = read_ecc[1] ^ test_ecc[1]; -+ d2 = read_ecc[2] ^ test_ecc[2]; -+ -+ if ((d0 | d1 | d2) == 0) -+ return 0; /* no error */ -+ -+ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && -+ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && -+ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { -+ /* Single bit (recoverable) error in data */ -+ -+ unsigned byte; -+ unsigned bit; -+ -+ bit = byte = 0; -+ -+ if (d1 & 0x80) -+ byte |= 0x80; -+ if (d1 & 0x20) -+ byte |= 0x40; -+ if (d1 & 0x08) -+ byte |= 0x20; -+ if (d1 & 0x02) -+ byte |= 0x10; -+ if (d0 & 0x80) -+ byte |= 0x08; -+ if (d0 & 0x20) -+ byte |= 0x04; -+ if (d0 & 0x08) -+ byte |= 0x02; -+ if (d0 & 0x02) -+ byte |= 0x01; -+ -+ if (d2 & 0x80) -+ bit |= 0x04; -+ if (d2 & 0x20) -+ bit |= 0x02; -+ if (d2 & 0x08) -+ bit |= 0x01; -+ -+ data[byte] ^= (1 << bit); -+ -+ return 1; /* Corrected the error */ -+ } -+ -+ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { -+ /* Reccoverable error in ecc */ -+ -+ read_ecc[0] = test_ecc[0]; -+ read_ecc[1] = test_ecc[1]; -+ read_ecc[2] = test_ecc[2]; -+ -+ return 1; /* Corrected the error */ -+ } -+ -+ /* Unrecoverable error */ -+ -+ return -1; -+ -+} -+ -+/* -+ * ECCxxxOther does ECC calcs on arbitrary n bytes of data -+ */ -+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *ecc_other) -+{ -+ unsigned int i; -+ unsigned char col_parity = 0; -+ unsigned line_parity = 0; -+ unsigned line_parity_prime = 0; -+ unsigned char b; -+ -+ for (i = 0; i < n_bytes; i++) { -+ b = column_parity_table[*data++]; -+ col_parity ^= b; -+ -+ if (b & 0x01) { -+ /* odd number of bits in the byte */ -+ line_parity ^= i; -+ line_parity_prime ^= ~i; -+ } -+ -+ } -+ -+ ecc_other->col_parity = (col_parity >> 2) & 0x3f; -+ ecc_other->line_parity = line_parity; -+ ecc_other->line_parity_prime = line_parity_prime; -+} -+ -+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *read_ecc, -+ const struct yaffs_ecc_other *test_ecc) -+{ -+ unsigned char delta_col; /* column parity delta */ -+ unsigned delta_line; /* line parity delta */ -+ unsigned delta_line_prime; /* line parity delta */ -+ unsigned bit; -+ -+ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; -+ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; -+ delta_line_prime = -+ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; -+ -+ if ((delta_col | delta_line | delta_line_prime) == 0) -+ return 0; /* no error */ -+ -+ if (delta_line == ~delta_line_prime && -+ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { -+ /* Single bit (recoverable) error in data */ -+ -+ bit = 0; -+ -+ if (delta_col & 0x20) -+ bit |= 0x04; -+ if (delta_col & 0x08) -+ bit |= 0x02; -+ if (delta_col & 0x02) -+ bit |= 0x01; -+ -+ if (delta_line >= n_bytes) -+ return -1; -+ -+ data[delta_line] ^= (1 << bit); -+ -+ return 1; /* corrected */ -+ } -+ -+ if ((hweight32(delta_line) + -+ hweight32(delta_line_prime) + -+ hweight8(delta_col)) == 1) { -+ /* Reccoverable error in ecc */ -+ -+ *read_ecc = *test_ecc; -+ return 1; /* corrected */ -+ } -+ -+ /* Unrecoverable error */ -+ -+ return -1; -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.h linux-3.4.90/fs/yaffs2/yaffs_ecc.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_ecc.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,44 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* -+ * This code implements the ECC algorithm used in SmartMedia. -+ * -+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. -+ * The two unused bit are set to 1. -+ * The ECC can correct single bit errors in a 256-byte page of data. -+ * Thus, two such ECC blocks are used on a 512-byte NAND page. -+ * -+ */ -+ -+#ifndef __YAFFS_ECC_H__ -+#define __YAFFS_ECC_H__ -+ -+struct yaffs_ecc_other { -+ unsigned char col_parity; -+ unsigned line_parity; -+ unsigned line_parity_prime; -+}; -+ -+void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); -+int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, -+ const unsigned char *test_ecc); -+ -+void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *ecc); -+int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, -+ struct yaffs_ecc_other *read_ecc, -+ const struct yaffs_ecc_other *test_ecc); -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.4.90/fs/yaffs2/yaffs_getblockinfo.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,35 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_GETBLOCKINFO_H__ -+#define __YAFFS_GETBLOCKINFO_H__ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+ -+/* Function to manipulate block info */ -+static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev -+ *dev, int blk) -+{ -+ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs: get_block_info block %d is not valid", -+ blk); -+ BUG(); -+ } -+ return &dev->block_info[blk - dev->internal_start_block]; -+} -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_guts.c linux-3.4.90/fs/yaffs2/yaffs_guts.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_guts.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,5146 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yportenv.h" -+#include "yaffs_trace.h" -+ -+#include "yaffs_guts.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_tagscompat.h" -+#include "yaffs_tagsmarshall.h" -+#include "yaffs_nand.h" -+#include "yaffs_yaffs1.h" -+#include "yaffs_yaffs2.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_verify.h" -+#include "yaffs_nand.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_nameval.h" -+#include "yaffs_allocator.h" -+#include "yaffs_attribs.h" -+#include "yaffs_summary.h" -+ -+/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ -+#define YAFFS_GC_GOOD_ENOUGH 2 -+#define YAFFS_GC_PASSIVE_THRESHOLD 4 -+ -+#include "yaffs_ecc.h" -+ -+/* Forward declarations */ -+ -+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, -+ const u8 *buffer, int n_bytes, int use_reserve); -+ -+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, -+ int buffer_size); -+ -+/* Function to calculate chunk and offset */ -+ -+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, -+ int *chunk_out, u32 *offset_out) -+{ -+ int chunk; -+ u32 offset; -+ -+ chunk = (u32) (addr >> dev->chunk_shift); -+ -+ if (dev->chunk_div == 1) { -+ /* easy power of 2 case */ -+ offset = (u32) (addr & dev->chunk_mask); -+ } else { -+ /* Non power-of-2 case */ -+ -+ loff_t chunk_base; -+ -+ chunk /= dev->chunk_div; -+ -+ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; -+ offset = (u32) (addr - chunk_base); -+ } -+ -+ *chunk_out = chunk; -+ *offset_out = offset; -+} -+ -+/* Function to return the number of shifts for a power of 2 greater than or -+ * equal to the given number -+ * Note we don't try to cater for all possible numbers and this does not have to -+ * be hellishly efficient. -+ */ -+ -+static inline u32 calc_shifts_ceiling(u32 x) -+{ -+ int extra_bits; -+ int shifts; -+ -+ shifts = extra_bits = 0; -+ -+ while (x > 1) { -+ if (x & 1) -+ extra_bits++; -+ x >>= 1; -+ shifts++; -+ } -+ -+ if (extra_bits) -+ shifts++; -+ -+ return shifts; -+} -+ -+/* Function to return the number of shifts to get a 1 in bit 0 -+ */ -+ -+static inline u32 calc_shifts(u32 x) -+{ -+ u32 shifts; -+ -+ shifts = 0; -+ -+ if (!x) -+ return 0; -+ -+ while (!(x & 1)) { -+ x >>= 1; -+ shifts++; -+ } -+ -+ return shifts; -+} -+ -+/* -+ * Temporary buffer manipulations. -+ */ -+ -+static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) -+{ -+ int i; -+ u8 *buf = (u8 *) 1; -+ -+ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); -+ -+ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { -+ dev->temp_buffer[i].in_use = 0; -+ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ dev->temp_buffer[i].buffer = buf; -+ } -+ -+ return buf ? YAFFS_OK : YAFFS_FAIL; -+} -+ -+u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) -+{ -+ int i; -+ -+ dev->temp_in_use++; -+ if (dev->temp_in_use > dev->max_temp) -+ dev->max_temp = dev->temp_in_use; -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ if (dev->temp_buffer[i].in_use == 0) { -+ dev->temp_buffer[i].in_use = 1; -+ return dev->temp_buffer[i].buffer; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); -+ /* -+ * If we got here then we have to allocate an unmanaged one -+ * This is not good. -+ */ -+ -+ dev->unmanaged_buffer_allocs++; -+ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); -+ -+} -+ -+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) -+{ -+ int i; -+ -+ dev->temp_in_use--; -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { -+ if (dev->temp_buffer[i].buffer == buffer) { -+ dev->temp_buffer[i].in_use = 0; -+ return; -+ } -+ } -+ -+ if (buffer) { -+ /* assume it is an unmanaged one. */ -+ yaffs_trace(YAFFS_TRACE_BUFFERS, -+ "Releasing unmanaged temp buffer"); -+ kfree(buffer); -+ dev->unmanaged_buffer_deallocs++; -+ } -+ -+} -+ -+/* -+ * Functions for robustisizing TODO -+ * -+ */ -+ -+static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, -+ const struct yaffs_ext_tags *tags) -+{ -+ (void) dev; -+ (void) nand_chunk; -+ (void) data; -+ (void) tags; -+} -+ -+static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, -+ const struct yaffs_ext_tags *tags) -+{ -+ (void) dev; -+ (void) nand_chunk; -+ (void) tags; -+} -+ -+void yaffs_handle_chunk_error(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi) -+{ -+ if (!bi->gc_prioritise) { -+ bi->gc_prioritise = 1; -+ dev->has_pending_prioritised_gc = 1; -+ bi->chunk_error_strikes++; -+ -+ if (bi->chunk_error_strikes > 3) { -+ bi->needs_retiring = 1; /* Too many stikes, so retire */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Block struck out"); -+ -+ } -+ } -+} -+ -+static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, -+ int erased_ok) -+{ -+ int flash_block = nand_chunk / dev->param.chunks_per_block; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -+ -+ yaffs_handle_chunk_error(dev, bi); -+ -+ if (erased_ok) { -+ /* Was an actual write failure, -+ * so mark the block for retirement.*/ -+ bi->needs_retiring = 1; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Block %d needs retiring", flash_block); -+ } -+ -+ /* Delete the chunk */ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ yaffs_skip_rest_of_block(dev); -+} -+ -+/* -+ * Verification code -+ */ -+ -+/* -+ * Simple hash function. Needs to have a reasonable spread -+ */ -+ -+static inline int yaffs_hash_fn(int n) -+{ -+ if (n < 0) -+ n = -n; -+ return n % YAFFS_NOBJECT_BUCKETS; -+} -+ -+/* -+ * Access functions to useful fake objects. -+ * Note that root might have a presence in NAND if permissions are set. -+ */ -+ -+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) -+{ -+ return dev->root_dir; -+} -+ -+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) -+{ -+ return dev->lost_n_found; -+} -+ -+/* -+ * Erased NAND checking functions -+ */ -+ -+int yaffs_check_ff(u8 *buffer, int n_bytes) -+{ -+ /* Horrible, slow implementation */ -+ while (n_bytes--) { -+ if (*buffer != 0xff) -+ return 0; -+ buffer++; -+ } -+ return 1; -+} -+ -+static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) -+{ -+ int retval = YAFFS_OK; -+ u8 *data = yaffs_get_temp_buffer(dev); -+ struct yaffs_ext_tags tags; -+ int result; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); -+ -+ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) -+ retval = YAFFS_FAIL; -+ -+ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || -+ tags.chunk_used) { -+ yaffs_trace(YAFFS_TRACE_NANDACCESS, -+ "Chunk %d not erased", nand_chunk); -+ retval = YAFFS_FAIL; -+ } -+ -+ yaffs_release_temp_buffer(dev, data); -+ -+ return retval; -+ -+} -+ -+static int yaffs_verify_chunk_written(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, -+ struct yaffs_ext_tags *tags) -+{ -+ int retval = YAFFS_OK; -+ struct yaffs_ext_tags temp_tags; -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ int result; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); -+ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || -+ temp_tags.obj_id != tags->obj_id || -+ temp_tags.chunk_id != tags->chunk_id || -+ temp_tags.n_bytes != tags->n_bytes) -+ retval = YAFFS_FAIL; -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ return retval; -+} -+ -+ -+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) -+{ -+ int reserved_chunks; -+ int reserved_blocks = dev->param.n_reserved_blocks; -+ int checkpt_blocks; -+ -+ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); -+ -+ reserved_chunks = -+ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; -+ -+ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); -+} -+ -+static int yaffs_find_alloc_block(struct yaffs_dev *dev) -+{ -+ int i; -+ struct yaffs_block_info *bi; -+ -+ if (dev->n_erased_blocks < 1) { -+ /* Hoosterman we've got a problem. -+ * Can't get space to gc -+ */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: no more erased blocks"); -+ -+ return -1; -+ } -+ -+ /* Find an empty block. */ -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ dev->alloc_block_finder++; -+ if (dev->alloc_block_finder < dev->internal_start_block -+ || dev->alloc_block_finder > dev->internal_end_block) { -+ dev->alloc_block_finder = dev->internal_start_block; -+ } -+ -+ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->seq_number++; -+ bi->seq_number = dev->seq_number; -+ dev->n_erased_blocks--; -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, -+ "Allocated block %d, seq %d, %d left" , -+ dev->alloc_block_finder, dev->seq_number, -+ dev->n_erased_blocks); -+ return dev->alloc_block_finder; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs tragedy: no more erased blocks, but there should have been %d", -+ dev->n_erased_blocks); -+ -+ return -1; -+} -+ -+static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, -+ struct yaffs_block_info **block_ptr) -+{ -+ int ret_val; -+ struct yaffs_block_info *bi; -+ -+ if (dev->alloc_block < 0) { -+ /* Get next block to allocate off */ -+ dev->alloc_block = yaffs_find_alloc_block(dev); -+ dev->alloc_page = 0; -+ } -+ -+ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { -+ /* No space unless we're allowed to use the reserve. */ -+ return -1; -+ } -+ -+ if (dev->n_erased_blocks < dev->param.n_reserved_blocks -+ && dev->alloc_page == 0) -+ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); -+ -+ /* Next page please.... */ -+ if (dev->alloc_block >= 0) { -+ bi = yaffs_get_block_info(dev, dev->alloc_block); -+ -+ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + -+ dev->alloc_page; -+ bi->pages_in_use++; -+ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); -+ -+ dev->alloc_page++; -+ -+ dev->n_free_chunks--; -+ -+ /* If the block is full set the state to full */ -+ if (dev->alloc_page >= dev->param.chunks_per_block) { -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ -+ if (block_ptr) -+ *block_ptr = bi; -+ -+ return ret_val; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); -+ -+ return -1; -+} -+ -+static int yaffs_get_erased_chunks(struct yaffs_dev *dev) -+{ -+ int n; -+ -+ n = dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ if (dev->alloc_block > 0) -+ n += (dev->param.chunks_per_block - dev->alloc_page); -+ -+ return n; -+ -+} -+ -+/* -+ * yaffs_skip_rest_of_block() skips over the rest of the allocation block -+ * if we don't want to write to it. -+ */ -+void yaffs_skip_rest_of_block(struct yaffs_dev *dev) -+{ -+ struct yaffs_block_info *bi; -+ -+ if (dev->alloc_block > 0) { -+ bi = yaffs_get_block_info(dev, dev->alloc_block); -+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ } -+} -+ -+static int yaffs_write_new_chunk(struct yaffs_dev *dev, -+ const u8 *data, -+ struct yaffs_ext_tags *tags, int use_reserver) -+{ -+ int attempts = 0; -+ int write_ok = 0; -+ int chunk; -+ -+ yaffs2_checkpt_invalidate(dev); -+ -+ do { -+ struct yaffs_block_info *bi = 0; -+ int erased_ok = 0; -+ -+ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); -+ if (chunk < 0) { -+ /* no space */ -+ break; -+ } -+ -+ /* First check this chunk is erased, if it needs -+ * checking. The checking policy (unless forced -+ * always on) is as follows: -+ * -+ * Check the first page we try to write in a block. -+ * If the check passes then we don't need to check any -+ * more. If the check fails, we check again... -+ * If the block has been erased, we don't need to check. -+ * -+ * However, if the block has been prioritised for gc, -+ * then we think there might be something odd about -+ * this block and stop using it. -+ * -+ * Rationale: We should only ever see chunks that have -+ * not been erased if there was a partially written -+ * chunk due to power loss. This checking policy should -+ * catch that case with very few checks and thus save a -+ * lot of checks that are most likely not needed. -+ * -+ * Mods to the above -+ * If an erase check fails or the write fails we skip the -+ * rest of the block. -+ */ -+ -+ /* let's give it a try */ -+ attempts++; -+ -+ if (dev->param.always_check_erased) -+ bi->skip_erased_check = 0; -+ -+ if (!bi->skip_erased_check) { -+ erased_ok = yaffs_check_chunk_erased(dev, chunk); -+ if (erased_ok != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs chunk %d was not erased", -+ chunk); -+ -+ /* If not erased, delete this one, -+ * skip rest of block and -+ * try another chunk */ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ yaffs_skip_rest_of_block(dev); -+ continue; -+ } -+ } -+ -+ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); -+ -+ if (!bi->skip_erased_check) -+ write_ok = -+ yaffs_verify_chunk_written(dev, chunk, data, tags); -+ -+ if (write_ok != YAFFS_OK) { -+ /* Clean up aborted write, skip to next block and -+ * try another chunk */ -+ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); -+ continue; -+ } -+ -+ bi->skip_erased_check = 1; -+ -+ /* Copy the data into the robustification buffer */ -+ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); -+ -+ } while (write_ok != YAFFS_OK && -+ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); -+ -+ if (!write_ok) -+ chunk = -1; -+ -+ if (attempts > 1) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>> yaffs write required %d attempts", -+ attempts); -+ dev->n_retried_writes += (attempts - 1); -+ } -+ -+ return chunk; -+} -+ -+/* -+ * Block retiring for handling a broken block. -+ */ -+ -+static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); -+ -+ yaffs2_checkpt_invalidate(dev); -+ -+ yaffs2_clear_oldest_dirty_seq(dev, bi); -+ -+ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { -+ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Failed to mark bad and erase block %d", -+ flash_block); -+ } else { -+ struct yaffs_ext_tags tags; -+ int chunk_id = -+ flash_block * dev->param.chunks_per_block; -+ -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ memset(buffer, 0xff, dev->data_bytes_per_chunk); -+ memset(&tags, 0, sizeof(tags)); -+ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; -+ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - -+ dev->chunk_offset, -+ buffer, -+ &tags) != YAFFS_OK) -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Failed to write bad block marker to block %d", -+ flash_block); -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ } -+ -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ bi->gc_prioritise = 0; -+ bi->needs_retiring = 0; -+ -+ dev->n_retired_blocks++; -+} -+ -+/*---------------- Name handling functions ------------*/ -+ -+static u16 yaffs_calc_name_sum(const YCHAR *name) -+{ -+ u16 sum = 0; -+ u16 i = 1; -+ -+ if (!name) -+ return 0; -+ -+ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { -+ -+ /* 0x1f mask is case insensitive */ -+ sum += ((*name) & 0x1f) * i; -+ i++; -+ name++; -+ } -+ return sum; -+} -+ -+ -+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) -+{ -+ memset(obj->short_name, 0, sizeof(obj->short_name)); -+ -+ if (name && !name[0]) { -+ yaffs_fix_null_name(obj, obj->short_name, -+ YAFFS_SHORT_NAME_LENGTH); -+ name = obj->short_name; -+ } else if (name && -+ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= -+ YAFFS_SHORT_NAME_LENGTH) { -+ strcpy(obj->short_name, name); -+ } -+ -+ obj->sum = yaffs_calc_name_sum(name); -+} -+ -+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, -+ const struct yaffs_obj_hdr *oh) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; -+ memset(tmp_name, 0, sizeof(tmp_name)); -+ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, -+ YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_set_obj_name(obj, tmp_name); -+#else -+ yaffs_set_obj_name(obj, oh->name); -+#endif -+} -+ -+loff_t yaffs_max_file_size(struct yaffs_dev *dev) -+{ -+ if(sizeof(loff_t) < 8) -+ return YAFFS_MAX_FILE_SIZE_32; -+ else -+ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; -+} -+ -+/*-------------------- TNODES ------------------- -+ -+ * List of spare tnodes -+ * The list is hooked together using the first pointer -+ * in the tnode. -+ */ -+ -+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) -+{ -+ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); -+ -+ if (tn) { -+ memset(tn, 0, dev->tnode_size); -+ dev->n_tnodes++; -+ } -+ -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+ -+ return tn; -+} -+ -+/* FreeTnode frees up a tnode and puts it back on the free list */ -+static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) -+{ -+ yaffs_free_raw_tnode(dev, tn); -+ dev->n_tnodes--; -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ yaffs_deinit_raw_tnodes_and_objs(dev); -+ dev->n_obj = 0; -+ dev->n_tnodes = 0; -+} -+ -+static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos, unsigned val) -+{ -+ u32 *map = (u32 *) tn; -+ u32 bit_in_map; -+ u32 bit_in_word; -+ u32 word_in_map; -+ u32 mask; -+ -+ pos &= YAFFS_TNODES_LEVEL0_MASK; -+ val >>= dev->chunk_grp_bits; -+ -+ bit_in_map = pos * dev->tnode_width; -+ word_in_map = bit_in_map / 32; -+ bit_in_word = bit_in_map & (32 - 1); -+ -+ mask = dev->tnode_mask << bit_in_word; -+ -+ map[word_in_map] &= ~mask; -+ map[word_in_map] |= (mask & (val << bit_in_word)); -+ -+ if (dev->tnode_width > (32 - bit_in_word)) { -+ bit_in_word = (32 - bit_in_word); -+ word_in_map++; -+ mask = -+ dev->tnode_mask >> bit_in_word; -+ map[word_in_map] &= ~mask; -+ map[word_in_map] |= (mask & (val >> bit_in_word)); -+ } -+} -+ -+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos) -+{ -+ u32 *map = (u32 *) tn; -+ u32 bit_in_map; -+ u32 bit_in_word; -+ u32 word_in_map; -+ u32 val; -+ -+ pos &= YAFFS_TNODES_LEVEL0_MASK; -+ -+ bit_in_map = pos * dev->tnode_width; -+ word_in_map = bit_in_map / 32; -+ bit_in_word = bit_in_map & (32 - 1); -+ -+ val = map[word_in_map] >> bit_in_word; -+ -+ if (dev->tnode_width > (32 - bit_in_word)) { -+ bit_in_word = (32 - bit_in_word); -+ word_in_map++; -+ val |= (map[word_in_map] << bit_in_word); -+ } -+ -+ val &= dev->tnode_mask; -+ val <<= dev->chunk_grp_bits; -+ -+ return val; -+} -+ -+/* ------------------- End of individual tnode manipulation -----------------*/ -+ -+/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ -+ * The look up tree is represented by the top tnode and the number of top_level -+ * in the tree. 0 means only the level 0 tnode is in the tree. -+ */ -+ -+/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ -+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id) -+{ -+ struct yaffs_tnode *tn = file_struct->top; -+ u32 i; -+ int required_depth; -+ int level = file_struct->top_level; -+ -+ (void) dev; -+ -+ /* Check sane level and chunk Id */ -+ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) -+ return NULL; -+ -+ if (chunk_id > YAFFS_MAX_CHUNK_ID) -+ return NULL; -+ -+ /* First check we're tall enough (ie enough top_level) */ -+ -+ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (i) { -+ i >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ if (required_depth > file_struct->top_level) -+ return NULL; /* Not tall enough, so we can't find it */ -+ -+ /* Traverse down to level 0 */ -+ while (level > 0 && tn) { -+ tn = tn->internal[(chunk_id >> -+ (YAFFS_TNODES_LEVEL0_BITS + -+ (level - 1) * -+ YAFFS_TNODES_INTERNAL_BITS)) & -+ YAFFS_TNODES_INTERNAL_MASK]; -+ level--; -+ } -+ -+ return tn; -+} -+ -+/* add_find_tnode_0 finds the level 0 tnode if it exists, -+ * otherwise first expands the tree. -+ * This happens in two steps: -+ * 1. If the tree isn't tall enough, then make it taller. -+ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. -+ * -+ * Used when modifying the tree. -+ * -+ * If the tn argument is NULL, then a fresh tnode will be added otherwise the -+ * specified tn will be plugged into the ttree. -+ */ -+ -+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id, -+ struct yaffs_tnode *passed_tn) -+{ -+ int required_depth; -+ int i; -+ int l; -+ struct yaffs_tnode *tn; -+ u32 x; -+ -+ /* Check sane level and page Id */ -+ if (file_struct->top_level < 0 || -+ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) -+ return NULL; -+ -+ if (chunk_id > YAFFS_MAX_CHUNK_ID) -+ return NULL; -+ -+ /* First check we're tall enough (ie enough top_level) */ -+ -+ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (x) { -+ x >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ if (required_depth > file_struct->top_level) { -+ /* Not tall enough, gotta make the tree taller */ -+ for (i = file_struct->top_level; i < required_depth; i++) { -+ -+ tn = yaffs_get_tnode(dev); -+ -+ if (tn) { -+ tn->internal[0] = file_struct->top; -+ file_struct->top = tn; -+ file_struct->top_level++; -+ } else { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs: no more tnodes"); -+ return NULL; -+ } -+ } -+ } -+ -+ /* Traverse down to level 0, adding anything we need */ -+ -+ l = file_struct->top_level; -+ tn = file_struct->top; -+ -+ if (l > 0) { -+ while (l > 0 && tn) { -+ x = (chunk_id >> -+ (YAFFS_TNODES_LEVEL0_BITS + -+ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & -+ YAFFS_TNODES_INTERNAL_MASK; -+ -+ if ((l > 1) && !tn->internal[x]) { -+ /* Add missing non-level-zero tnode */ -+ tn->internal[x] = yaffs_get_tnode(dev); -+ if (!tn->internal[x]) -+ return NULL; -+ } else if (l == 1) { -+ /* Looking from level 1 at level 0 */ -+ if (passed_tn) { -+ /* If we already have one, release it */ -+ if (tn->internal[x]) -+ yaffs_free_tnode(dev, -+ tn->internal[x]); -+ tn->internal[x] = passed_tn; -+ -+ } else if (!tn->internal[x]) { -+ /* Don't have one, none passed in */ -+ tn->internal[x] = yaffs_get_tnode(dev); -+ if (!tn->internal[x]) -+ return NULL; -+ } -+ } -+ -+ tn = tn->internal[x]; -+ l--; -+ } -+ } else { -+ /* top is level 0 */ -+ if (passed_tn) { -+ memcpy(tn, passed_tn, -+ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); -+ yaffs_free_tnode(dev, passed_tn); -+ } -+ } -+ -+ return tn; -+} -+ -+static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, -+ int chunk_obj) -+{ -+ return (tags->chunk_id == chunk_obj && -+ tags->obj_id == obj_id && -+ !tags->is_deleted) ? 1 : 0; -+ -+} -+ -+static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, -+ struct yaffs_ext_tags *tags, int obj_id, -+ int inode_chunk) -+{ -+ int j; -+ -+ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { -+ if (yaffs_check_chunk_bit -+ (dev, the_chunk / dev->param.chunks_per_block, -+ the_chunk % dev->param.chunks_per_block)) { -+ -+ if (dev->chunk_grp_size == 1) -+ return the_chunk; -+ else { -+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, -+ tags); -+ if (yaffs_tags_match(tags, -+ obj_id, inode_chunk)) { -+ /* found it; */ -+ return the_chunk; -+ } -+ } -+ } -+ the_chunk++; -+ } -+ return -1; -+} -+ -+int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags) -+{ -+ /*Get the Tnode, then get the level 0 offset chunk offset */ -+ struct yaffs_tnode *tn; -+ int the_chunk = -1; -+ struct yaffs_ext_tags local_tags; -+ int ret_val = -1; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!tags) { -+ /* Passed a NULL, so use our own tags space */ -+ tags = &local_tags; -+ } -+ -+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -+ -+ if (!tn) -+ return ret_val; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, -+ inode_chunk); -+ return ret_val; -+} -+ -+static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags) -+{ -+ /* Get the Tnode, then get the level 0 offset chunk offset */ -+ struct yaffs_tnode *tn; -+ int the_chunk = -1; -+ struct yaffs_ext_tags local_tags; -+ struct yaffs_dev *dev = in->my_dev; -+ int ret_val = -1; -+ -+ if (!tags) { -+ /* Passed a NULL, so use our own tags space */ -+ tags = &local_tags; -+ } -+ -+ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); -+ -+ if (!tn) -+ return ret_val; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, -+ inode_chunk); -+ -+ /* Delete the entry in the filestructure (if found) */ -+ if (ret_val != -1) -+ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); -+ -+ return ret_val; -+} -+ -+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ int nand_chunk, int in_scan) -+{ -+ /* NB in_scan is zero unless scanning. -+ * For forward scanning, in_scan is > 0; -+ * for backward scanning in_scan is < 0 -+ * -+ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. -+ */ -+ -+ struct yaffs_tnode *tn; -+ struct yaffs_dev *dev = in->my_dev; -+ int existing_cunk; -+ struct yaffs_ext_tags existing_tags; -+ struct yaffs_ext_tags new_tags; -+ unsigned existing_serial, new_serial; -+ -+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { -+ /* Just ignore an attempt at putting a chunk into a non-file -+ * during scanning. -+ * If it is not during Scanning then something went wrong! -+ */ -+ if (!in_scan) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy:attempt to put data chunk into a non-file" -+ ); -+ BUG(); -+ } -+ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ return YAFFS_OK; -+ } -+ -+ tn = yaffs_add_find_tnode_0(dev, -+ &in->variant.file_variant, -+ inode_chunk, NULL); -+ if (!tn) -+ return YAFFS_FAIL; -+ -+ if (!nand_chunk) -+ /* Dummy insert, bail now */ -+ return YAFFS_OK; -+ -+ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); -+ -+ if (in_scan != 0) { -+ /* If we're scanning then we need to test for duplicates -+ * NB This does not need to be efficient since it should only -+ * happen when the power fails during a write, then only one -+ * chunk should ever be affected. -+ * -+ * Correction for YAFFS2: This could happen quite a lot and we -+ * need to think about efficiency! TODO -+ * Update: For backward scanning we don't need to re-read tags -+ * so this is quite cheap. -+ */ -+ -+ if (existing_cunk > 0) { -+ /* NB Right now existing chunk will not be real -+ * chunk_id if the chunk group size > 1 -+ * thus we have to do a FindChunkInFile to get the -+ * real chunk id. -+ * -+ * We have a duplicate now we need to decide which -+ * one to use: -+ * -+ * Backwards scanning YAFFS2: The old one is what -+ * we use, dump the new one. -+ * YAFFS1: Get both sets of tags and compare serial -+ * numbers. -+ */ -+ -+ if (in_scan > 0) { -+ /* Only do this for forward scanning */ -+ yaffs_rd_chunk_tags_nand(dev, -+ nand_chunk, -+ NULL, &new_tags); -+ -+ /* Do a proper find */ -+ existing_cunk = -+ yaffs_find_chunk_in_file(in, inode_chunk, -+ &existing_tags); -+ } -+ -+ if (existing_cunk <= 0) { -+ /*Hoosterman - how did this happen? */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: existing chunk < 0 in scan" -+ ); -+ -+ } -+ -+ /* NB The deleted flags should be false, otherwise -+ * the chunks will not be loaded during a scan -+ */ -+ -+ if (in_scan > 0) { -+ new_serial = new_tags.serial_number; -+ existing_serial = existing_tags.serial_number; -+ } -+ -+ if ((in_scan > 0) && -+ (existing_cunk <= 0 || -+ ((existing_serial + 1) & 3) == new_serial)) { -+ /* Forward scanning. -+ * Use new -+ * Delete the old one and drop through to -+ * update the tnode -+ */ -+ yaffs_chunk_del(dev, existing_cunk, 1, -+ __LINE__); -+ } else { -+ /* Backward scanning or we want to use the -+ * existing one -+ * Delete the new one and return early so that -+ * the tnode isn't changed -+ */ -+ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); -+ return YAFFS_OK; -+ } -+ } -+ -+ } -+ -+ if (existing_cunk == 0) -+ in->n_data_chunks++; -+ -+ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); -+ -+ return YAFFS_OK; -+} -+ -+static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) -+{ -+ struct yaffs_block_info *the_block; -+ unsigned block_no; -+ -+ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); -+ -+ block_no = chunk / dev->param.chunks_per_block; -+ the_block = yaffs_get_block_info(dev, block_no); -+ if (the_block) { -+ the_block->soft_del_pages++; -+ dev->n_free_chunks++; -+ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); -+ } -+} -+ -+/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all -+ * the chunks in the file. -+ * All soft deleting does is increment the block's softdelete count and pulls -+ * the chunk out of the tnode. -+ * Thus, essentially this is the same as DeleteWorker except that the chunks -+ * are soft deleted. -+ */ -+ -+static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, -+ u32 level, int chunk_offset) -+{ -+ int i; -+ int the_chunk; -+ int all_done = 1; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!tn) -+ return 1; -+ -+ if (level > 0) { -+ for (i = YAFFS_NTNODES_INTERNAL - 1; -+ all_done && i >= 0; -+ i--) { -+ if (tn->internal[i]) { -+ all_done = -+ yaffs_soft_del_worker(in, -+ tn->internal[i], -+ level - 1, -+ (chunk_offset << -+ YAFFS_TNODES_INTERNAL_BITS) -+ + i); -+ if (all_done) { -+ yaffs_free_tnode(dev, -+ tn->internal[i]); -+ tn->internal[i] = NULL; -+ } else { -+ /* Can this happen? */ -+ } -+ } -+ } -+ return (all_done) ? 1 : 0; -+ } -+ -+ /* level 0 */ -+ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { -+ the_chunk = yaffs_get_group_base(dev, tn, i); -+ if (the_chunk) { -+ yaffs_soft_del_chunk(dev, the_chunk); -+ yaffs_load_tnode_0(dev, tn, i, 0); -+ } -+ } -+ return 1; -+} -+ -+static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ struct yaffs_obj *parent; -+ -+ yaffs_verify_obj_in_dir(obj); -+ parent = obj->parent; -+ -+ yaffs_verify_dir(parent); -+ -+ if (dev && dev->param.remove_obj_fn) -+ dev->param.remove_obj_fn(obj); -+ -+ list_del_init(&obj->siblings); -+ obj->parent = NULL; -+ -+ yaffs_verify_dir(parent); -+} -+ -+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) -+{ -+ if (!directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: Trying to add an object to a null pointer directory" -+ ); -+ BUG(); -+ return; -+ } -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: Trying to add an object to a non-directory" -+ ); -+ BUG(); -+ } -+ -+ if (obj->siblings.prev == NULL) { -+ /* Not initialised */ -+ BUG(); -+ } -+ -+ yaffs_verify_dir(directory); -+ -+ yaffs_remove_obj_from_dir(obj); -+ -+ /* Now add it */ -+ list_add(&obj->siblings, &directory->variant.dir_variant.children); -+ obj->parent = directory; -+ -+ if (directory == obj->my_dev->unlinked_dir -+ || directory == obj->my_dev->del_dir) { -+ obj->unlinked = 1; -+ obj->my_dev->n_unlinked_files++; -+ obj->rename_allowed = 0; -+ } -+ -+ yaffs_verify_dir(directory); -+ yaffs_verify_obj_in_dir(obj); -+} -+ -+static int yaffs_change_obj_name(struct yaffs_obj *obj, -+ struct yaffs_obj *new_dir, -+ const YCHAR *new_name, int force, int shadows) -+{ -+ int unlink_op; -+ int del_op; -+ struct yaffs_obj *existing_target; -+ -+ if (new_dir == NULL) -+ new_dir = obj->parent; /* use the old directory */ -+ -+ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_change_obj_name: new_dir is not a directory" -+ ); -+ BUG(); -+ } -+ -+ unlink_op = (new_dir == obj->my_dev->unlinked_dir); -+ del_op = (new_dir == obj->my_dev->del_dir); -+ -+ existing_target = yaffs_find_by_name(new_dir, new_name); -+ -+ /* If the object is a file going into the unlinked directory, -+ * then it is OK to just stuff it in since duplicate names are OK. -+ * else only proceed if the new name does not exist and we're putting -+ * it into a directory. -+ */ -+ if (!(unlink_op || del_op || force || -+ shadows > 0 || !existing_target) || -+ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) -+ return YAFFS_FAIL; -+ -+ yaffs_set_obj_name(obj, new_name); -+ obj->dirty = 1; -+ yaffs_add_obj_to_dir(new_dir, obj); -+ -+ if (unlink_op) -+ obj->unlinked = 1; -+ -+ /* If it is a deletion then we mark it as a shrink for gc */ -+ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+/*------------------------ Short Operations Cache ------------------------------ -+ * In many situations where there is no high level buffering a lot of -+ * reads might be short sequential reads, and a lot of writes may be short -+ * sequential writes. eg. scanning/writing a jpeg file. -+ * In these cases, a short read/write cache can provide a huge perfomance -+ * benefit with dumb-as-a-rock code. -+ * In Linux, the page cache provides read buffering and the short op cache -+ * provides write buffering. -+ * -+ * There are a small number (~10) of cache chunks per device so that we don't -+ * need a very intelligent search. -+ */ -+ -+static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ struct yaffs_cache *cache; -+ int n_caches = obj->my_dev->param.n_caches; -+ -+ for (i = 0; i < n_caches; i++) { -+ cache = &dev->cache[i]; -+ if (cache->object == obj && cache->dirty) -+ return 1; -+ } -+ -+ return 0; -+} -+ -+static void yaffs_flush_file_cache(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int lowest = -99; /* Stop compiler whining. */ -+ int i; -+ struct yaffs_cache *cache; -+ int chunk_written = 0; -+ int n_caches = obj->my_dev->param.n_caches; -+ -+ if (n_caches < 1) -+ return; -+ do { -+ cache = NULL; -+ -+ /* Find the lowest dirty chunk for this object */ -+ for (i = 0; i < n_caches; i++) { -+ if (dev->cache[i].object == obj && -+ dev->cache[i].dirty) { -+ if (!cache || -+ dev->cache[i].chunk_id < lowest) { -+ cache = &dev->cache[i]; -+ lowest = cache->chunk_id; -+ } -+ } -+ } -+ -+ if (cache && !cache->locked) { -+ /* Write it out and free it up */ -+ chunk_written = -+ yaffs_wr_data_obj(cache->object, -+ cache->chunk_id, -+ cache->data, -+ cache->n_bytes, 1); -+ cache->dirty = 0; -+ cache->object = NULL; -+ } -+ } while (cache && chunk_written > 0); -+ -+ if (cache) -+ /* Hoosterman, disk full while writing cache out. */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: no space during cache write"); -+} -+ -+/*yaffs_flush_whole_cache(dev) -+ * -+ * -+ */ -+ -+void yaffs_flush_whole_cache(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ int n_caches = dev->param.n_caches; -+ int i; -+ -+ /* Find a dirty object in the cache and flush it... -+ * until there are no further dirty objects. -+ */ -+ do { -+ obj = NULL; -+ for (i = 0; i < n_caches && !obj; i++) { -+ if (dev->cache[i].object && dev->cache[i].dirty) -+ obj = dev->cache[i].object; -+ } -+ if (obj) -+ yaffs_flush_file_cache(obj); -+ } while (obj); -+ -+} -+ -+/* Grab us a cache chunk for use. -+ * First look for an empty one. -+ * Then look for the least recently used non-dirty one. -+ * Then look for the least recently used dirty one...., flush and look again. -+ */ -+static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ if (dev->param.n_caches > 0) { -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (!dev->cache[i].object) -+ return &dev->cache[i]; -+ } -+ } -+ return NULL; -+} -+ -+static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) -+{ -+ struct yaffs_cache *cache; -+ struct yaffs_obj *the_obj; -+ int usage; -+ int i; -+ int pushout; -+ -+ if (dev->param.n_caches < 1) -+ return NULL; -+ -+ /* Try find a non-dirty one... */ -+ -+ cache = yaffs_grab_chunk_worker(dev); -+ -+ if (!cache) { -+ /* They were all dirty, find the LRU object and flush -+ * its cache, then find again. -+ * NB what's here is not very accurate, -+ * we actually flush the object with the LRU chunk. -+ */ -+ -+ /* With locking we can't assume we can use entry zero, -+ * Set the_obj to a valid pointer for Coverity. */ -+ the_obj = dev->cache[0].object; -+ usage = -1; -+ cache = NULL; -+ pushout = -1; -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object && -+ !dev->cache[i].locked && -+ (dev->cache[i].last_use < usage || -+ !cache)) { -+ usage = dev->cache[i].last_use; -+ the_obj = dev->cache[i].object; -+ cache = &dev->cache[i]; -+ pushout = i; -+ } -+ } -+ -+ if (!cache || cache->dirty) { -+ /* Flush and try again */ -+ yaffs_flush_file_cache(the_obj); -+ cache = yaffs_grab_chunk_worker(dev); -+ } -+ } -+ return cache; -+} -+ -+/* Find a cached chunk */ -+static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, -+ int chunk_id) -+{ -+ struct yaffs_dev *dev = obj->my_dev; -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return NULL; -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object == obj && -+ dev->cache[i].chunk_id == chunk_id) { -+ dev->cache_hits++; -+ -+ return &dev->cache[i]; -+ } -+ } -+ return NULL; -+} -+ -+/* Mark the chunk for the least recently used algorithym */ -+static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, -+ int is_write) -+{ -+ int i; -+ -+ if (dev->param.n_caches < 1) -+ return; -+ -+ if (dev->cache_last_use < 0 || -+ dev->cache_last_use > 100000000) { -+ /* Reset the cache usages */ -+ for (i = 1; i < dev->param.n_caches; i++) -+ dev->cache[i].last_use = 0; -+ -+ dev->cache_last_use = 0; -+ } -+ dev->cache_last_use++; -+ cache->last_use = dev->cache_last_use; -+ -+ if (is_write) -+ cache->dirty = 1; -+} -+ -+/* Invalidate a single cache page. -+ * Do this when a whole page gets written, -+ * ie the short cache for this page is no longer valid. -+ */ -+static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) -+{ -+ struct yaffs_cache *cache; -+ -+ if (object->my_dev->param.n_caches > 0) { -+ cache = yaffs_find_chunk_cache(object, chunk_id); -+ -+ if (cache) -+ cache->object = NULL; -+ } -+} -+ -+/* Invalidate all the cache pages associated with this object -+ * Do this whenever ther file is deleted or resized. -+ */ -+static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) -+{ -+ int i; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (dev->param.n_caches > 0) { -+ /* Invalidate it. */ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].object == in) -+ dev->cache[i].object = NULL; -+ } -+ } -+} -+ -+static void yaffs_unhash_obj(struct yaffs_obj *obj) -+{ -+ int bucket; -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ /* If it is still linked into the bucket list, free from the list */ -+ if (!list_empty(&obj->hash_link)) { -+ list_del_init(&obj->hash_link); -+ bucket = yaffs_hash_fn(obj->obj_id); -+ dev->obj_bucket[bucket].count--; -+ } -+} -+ -+/* FreeObject frees up a Object and puts it back on the free list */ -+static void yaffs_free_obj(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ -+ if (!obj) { -+ BUG(); -+ return; -+ } -+ dev = obj->my_dev; -+ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", -+ obj, obj->my_inode); -+ if (obj->parent) -+ BUG(); -+ if (!list_empty(&obj->siblings)) -+ BUG(); -+ -+ if (obj->my_inode) { -+ /* We're still hooked up to a cached inode. -+ * Don't delete now, but mark for later deletion -+ */ -+ obj->defered_free = 1; -+ return; -+ } -+ -+ yaffs_unhash_obj(obj); -+ -+ yaffs_free_raw_obj(dev, obj); -+ dev->n_obj--; -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+} -+ -+void yaffs_handle_defered_free(struct yaffs_obj *obj) -+{ -+ if (obj->defered_free) -+ yaffs_free_obj(obj); -+} -+ -+static int yaffs_generic_obj_del(struct yaffs_obj *in) -+{ -+ /* Iinvalidate the file's data in the cache, without flushing. */ -+ yaffs_invalidate_whole_cache(in); -+ -+ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { -+ /* Move to unlinked directory so we have a deletion record */ -+ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, -+ 0); -+ } -+ -+ yaffs_remove_obj_from_dir(in); -+ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); -+ in->hdr_chunk = 0; -+ -+ yaffs_free_obj(in); -+ return YAFFS_OK; -+ -+} -+ -+static void yaffs_soft_del_file(struct yaffs_obj *obj) -+{ -+ if (!obj->deleted || -+ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || -+ obj->soft_del) -+ return; -+ -+ if (obj->n_data_chunks <= 0) { -+ /* Empty file with no duplicate object headers, -+ * just delete it immediately */ -+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); -+ obj->variant.file_variant.top = NULL; -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: Deleting empty file %d", -+ obj->obj_id); -+ yaffs_generic_obj_del(obj); -+ } else { -+ yaffs_soft_del_worker(obj, -+ obj->variant.file_variant.top, -+ obj->variant. -+ file_variant.top_level, 0); -+ obj->soft_del = 1; -+ } -+} -+ -+/* Pruning removes any part of the file structure tree that is beyond the -+ * bounds of the file (ie that does not point to chunks). -+ * -+ * A file should only get pruned when its size is reduced. -+ * -+ * Before pruning, the chunks must be pulled from the tree and the -+ * level 0 tnode entries must be zeroed out. -+ * Could also use this for file deletion, but that's probably better handled -+ * by a special case. -+ * -+ * This function is recursive. For levels > 0 the function is called again on -+ * any sub-tree. For level == 0 we just check if the sub-tree has data. -+ * If there is no data in a subtree then it is pruned. -+ */ -+ -+static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, -+ struct yaffs_tnode *tn, u32 level, -+ int del0) -+{ -+ int i; -+ int has_data; -+ -+ if (!tn) -+ return tn; -+ -+ has_data = 0; -+ -+ if (level > 0) { -+ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { -+ if (tn->internal[i]) { -+ tn->internal[i] = -+ yaffs_prune_worker(dev, -+ tn->internal[i], -+ level - 1, -+ (i == 0) ? del0 : 1); -+ } -+ -+ if (tn->internal[i]) -+ has_data++; -+ } -+ } else { -+ int tnode_size_u32 = dev->tnode_size / sizeof(u32); -+ u32 *map = (u32 *) tn; -+ -+ for (i = 0; !has_data && i < tnode_size_u32; i++) { -+ if (map[i]) -+ has_data++; -+ } -+ } -+ -+ if (has_data == 0 && del0) { -+ /* Free and return NULL */ -+ yaffs_free_tnode(dev, tn); -+ tn = NULL; -+ } -+ return tn; -+} -+ -+static int yaffs_prune_tree(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct) -+{ -+ int i; -+ int has_data; -+ int done = 0; -+ struct yaffs_tnode *tn; -+ -+ if (file_struct->top_level < 1) -+ return YAFFS_OK; -+ -+ file_struct->top = -+ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); -+ -+ /* Now we have a tree with all the non-zero branches NULL but -+ * the height is the same as it was. -+ * Let's see if we can trim internal tnodes to shorten the tree. -+ * We can do this if only the 0th element in the tnode is in use -+ * (ie all the non-zero are NULL) -+ */ -+ -+ while (file_struct->top_level && !done) { -+ tn = file_struct->top; -+ -+ has_data = 0; -+ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { -+ if (tn->internal[i]) -+ has_data++; -+ } -+ -+ if (!has_data) { -+ file_struct->top = tn->internal[0]; -+ file_struct->top_level--; -+ yaffs_free_tnode(dev, tn); -+ } else { -+ done = 1; -+ } -+ } -+ -+ return YAFFS_OK; -+} -+ -+/*-------------------- End of File Structure functions.-------------------*/ -+ -+/* alloc_empty_obj gets us a clean Object.*/ -+static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); -+ -+ if (!obj) -+ return obj; -+ -+ dev->n_obj++; -+ -+ /* Now sweeten it up... */ -+ -+ memset(obj, 0, sizeof(struct yaffs_obj)); -+ obj->being_created = 1; -+ -+ obj->my_dev = dev; -+ obj->hdr_chunk = 0; -+ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; -+ INIT_LIST_HEAD(&(obj->hard_links)); -+ INIT_LIST_HEAD(&(obj->hash_link)); -+ INIT_LIST_HEAD(&obj->siblings); -+ -+ /* Now make the directory sane */ -+ if (dev->root_dir) { -+ obj->parent = dev->root_dir; -+ list_add(&(obj->siblings), -+ &dev->root_dir->variant.dir_variant.children); -+ } -+ -+ /* Add it to the lost and found directory. -+ * NB Can't put root or lost-n-found in lost-n-found so -+ * check if lost-n-found exists first -+ */ -+ if (dev->lost_n_found) -+ yaffs_add_obj_to_dir(dev->lost_n_found, obj); -+ -+ obj->being_created = 0; -+ -+ dev->checkpoint_blocks_required = 0; /* force recalculation */ -+ -+ return obj; -+} -+ -+static int yaffs_find_nice_bucket(struct yaffs_dev *dev) -+{ -+ int i; -+ int l = 999; -+ int lowest = 999999; -+ -+ /* Search for the shortest list or one that -+ * isn't too long. -+ */ -+ -+ for (i = 0; i < 10 && lowest > 4; i++) { -+ dev->bucket_finder++; -+ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; -+ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { -+ lowest = dev->obj_bucket[dev->bucket_finder].count; -+ l = dev->bucket_finder; -+ } -+ } -+ -+ return l; -+} -+ -+static int yaffs_new_obj_id(struct yaffs_dev *dev) -+{ -+ int bucket = yaffs_find_nice_bucket(dev); -+ int found = 0; -+ struct list_head *i; -+ u32 n = (u32) bucket; -+ -+ /* Now find an object value that has not already been taken -+ * by scanning the list. -+ */ -+ -+ while (!found) { -+ found = 1; -+ n += YAFFS_NOBJECT_BUCKETS; -+ if (1 || dev->obj_bucket[bucket].count > 0) { -+ list_for_each(i, &dev->obj_bucket[bucket].list) { -+ /* If there is already one in the list */ -+ if (i && list_entry(i, struct yaffs_obj, -+ hash_link)->obj_id == n) { -+ found = 0; -+ } -+ } -+ } -+ } -+ return n; -+} -+ -+static void yaffs_hash_obj(struct yaffs_obj *in) -+{ -+ int bucket = yaffs_hash_fn(in->obj_id); -+ struct yaffs_dev *dev = in->my_dev; -+ -+ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); -+ dev->obj_bucket[bucket].count++; -+} -+ -+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) -+{ -+ int bucket = yaffs_hash_fn(number); -+ struct list_head *i; -+ struct yaffs_obj *in; -+ -+ list_for_each(i, &dev->obj_bucket[bucket].list) { -+ /* Look if it is in the list */ -+ in = list_entry(i, struct yaffs_obj, hash_link); -+ if (in->obj_id == number) { -+ /* Don't show if it is defered free */ -+ if (in->defered_free) -+ return NULL; -+ return in; -+ } -+ } -+ -+ return NULL; -+} -+ -+static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, -+ enum yaffs_obj_type type) -+{ -+ struct yaffs_obj *the_obj = NULL; -+ struct yaffs_tnode *tn = NULL; -+ -+ if (number < 0) -+ number = yaffs_new_obj_id(dev); -+ -+ if (type == YAFFS_OBJECT_TYPE_FILE) { -+ tn = yaffs_get_tnode(dev); -+ if (!tn) -+ return NULL; -+ } -+ -+ the_obj = yaffs_alloc_empty_obj(dev); -+ if (!the_obj) { -+ if (tn) -+ yaffs_free_tnode(dev, tn); -+ return NULL; -+ } -+ -+ the_obj->fake = 0; -+ the_obj->rename_allowed = 1; -+ the_obj->unlink_allowed = 1; -+ the_obj->obj_id = number; -+ yaffs_hash_obj(the_obj); -+ the_obj->variant_type = type; -+ yaffs_load_current_time(the_obj, 1, 1); -+ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ the_obj->variant.file_variant.file_size = 0; -+ the_obj->variant.file_variant.scanned_size = 0; -+ the_obj->variant.file_variant.shrink_size = -+ yaffs_max_file_size(dev); -+ the_obj->variant.file_variant.top_level = 0; -+ the_obj->variant.file_variant.top = tn; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); -+ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* No action required */ -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* todo this should not happen */ -+ break; -+ } -+ return the_obj; -+} -+ -+static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, -+ int number, u32 mode) -+{ -+ -+ struct yaffs_obj *obj = -+ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); -+ -+ if (!obj) -+ return NULL; -+ -+ obj->fake = 1; /* it is fake so it might not use NAND */ -+ obj->rename_allowed = 0; -+ obj->unlink_allowed = 0; -+ obj->deleted = 0; -+ obj->unlinked = 0; -+ obj->yst_mode = mode; -+ obj->my_dev = dev; -+ obj->hdr_chunk = 0; /* Not a valid chunk. */ -+ return obj; -+ -+} -+ -+ -+static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) -+{ -+ int i; -+ -+ dev->n_obj = 0; -+ dev->n_tnodes = 0; -+ yaffs_init_raw_tnodes_and_objs(dev); -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ INIT_LIST_HEAD(&dev->obj_bucket[i].list); -+ dev->obj_bucket[i].count = 0; -+ } -+} -+ -+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, -+ int number, -+ enum yaffs_obj_type type) -+{ -+ struct yaffs_obj *the_obj = NULL; -+ -+ if (number > 0) -+ the_obj = yaffs_find_by_number(dev, number); -+ -+ if (!the_obj) -+ the_obj = yaffs_new_obj(dev, number, type); -+ -+ return the_obj; -+ -+} -+ -+YCHAR *yaffs_clone_str(const YCHAR *str) -+{ -+ YCHAR *new_str = NULL; -+ int len; -+ -+ if (!str) -+ str = _Y(""); -+ -+ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); -+ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); -+ if (new_str) { -+ strncpy(new_str, str, len); -+ new_str[len] = 0; -+ } -+ return new_str; -+ -+} -+/* -+ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new -+ * link (ie. name) is created or deleted in the directory. -+ * -+ * ie. -+ * create dir/a : update dir's mtime/ctime -+ * rm dir/a: update dir's mtime/ctime -+ * modify dir/a: don't update dir's mtimme/ctime -+ * -+ * This can be handled immediately or defered. Defering helps reduce the number -+ * of updates when many files in a directory are changed within a brief period. -+ * -+ * If the directory updating is defered then yaffs_update_dirty_dirs must be -+ * called periodically. -+ */ -+ -+static void yaffs_update_parent(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ -+ if (!obj) -+ return; -+ dev = obj->my_dev; -+ obj->dirty = 1; -+ yaffs_load_current_time(obj, 0, 1); -+ if (dev->param.defered_dir_update) { -+ struct list_head *link = &obj->variant.dir_variant.dirty; -+ -+ if (list_empty(link)) { -+ list_add(link, &dev->dirty_dirs); -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "Added object %d to dirty directories", -+ obj->obj_id); -+ } -+ -+ } else { -+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); -+ } -+} -+ -+void yaffs_update_dirty_dirs(struct yaffs_dev *dev) -+{ -+ struct list_head *link; -+ struct yaffs_obj *obj; -+ struct yaffs_dir_var *d_s; -+ union yaffs_obj_var *o_v; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); -+ -+ while (!list_empty(&dev->dirty_dirs)) { -+ link = dev->dirty_dirs.next; -+ list_del_init(link); -+ -+ d_s = list_entry(link, struct yaffs_dir_var, dirty); -+ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); -+ obj = list_entry(o_v, struct yaffs_obj, variant); -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", -+ obj->obj_id); -+ -+ if (obj->dirty) -+ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); -+ } -+} -+ -+/* -+ * Mknod (create) a new object. -+ * equiv_obj only has meaning for a hard link; -+ * alias_str only has meaning for a symlink. -+ * rdev only has meaning for devices (a subset of special objects) -+ */ -+ -+static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, -+ struct yaffs_obj *parent, -+ const YCHAR *name, -+ u32 mode, -+ u32 uid, -+ u32 gid, -+ struct yaffs_obj *equiv_obj, -+ const YCHAR *alias_str, u32 rdev) -+{ -+ struct yaffs_obj *in; -+ YCHAR *str = NULL; -+ struct yaffs_dev *dev = parent->my_dev; -+ -+ /* Check if the entry exists. -+ * If it does then fail the call since we don't want a dup. */ -+ if (yaffs_find_by_name(parent, name)) -+ return NULL; -+ -+ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ str = yaffs_clone_str(alias_str); -+ if (!str) -+ return NULL; -+ } -+ -+ in = yaffs_new_obj(dev, -1, type); -+ -+ if (!in) { -+ kfree(str); -+ return NULL; -+ } -+ -+ in->hdr_chunk = 0; -+ in->valid = 1; -+ in->variant_type = type; -+ -+ in->yst_mode = mode; -+ -+ yaffs_attribs_init(in, gid, uid, rdev); -+ -+ in->n_data_chunks = 0; -+ -+ yaffs_set_obj_name(in, name); -+ in->dirty = 1; -+ -+ yaffs_add_obj_to_dir(parent, in); -+ -+ in->my_dev = parent->my_dev; -+ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ in->variant.symlink_variant.alias = str; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ in->variant.hardlink_variant.equiv_obj = equiv_obj; -+ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; -+ list_add(&in->hard_links, &equiv_obj->hard_links); -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* do nothing */ -+ break; -+ } -+ -+ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { -+ /* Could not create the object header, fail */ -+ yaffs_del_obj(in); -+ in = NULL; -+ } -+ -+ if (in) -+ yaffs_update_parent(parent); -+ -+ return in; -+} -+ -+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, -+ uid, gid, NULL, NULL, 0); -+} -+ -+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, -+ u32 mode, u32 uid, u32 gid) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, -+ mode, uid, gid, NULL, NULL, 0); -+} -+ -+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, u32 rdev) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, -+ uid, gid, NULL, NULL, rdev); -+} -+ -+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, const YCHAR *alias) -+{ -+ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, -+ uid, gid, NULL, alias, 0); -+} -+ -+/* yaffs_link_obj returns the object id of the equivalent object.*/ -+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, -+ struct yaffs_obj *equiv_obj) -+{ -+ /* Get the real object in case we were fed a hard link obj */ -+ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); -+ -+ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, -+ parent, name, 0, 0, 0, -+ equiv_obj, NULL, 0)) -+ return equiv_obj; -+ -+ return NULL; -+ -+} -+ -+ -+ -+/*---------------------- Block Management and Page Allocation -------------*/ -+ -+static void yaffs_deinit_blocks(struct yaffs_dev *dev) -+{ -+ if (dev->block_info_alt && dev->block_info) -+ vfree(dev->block_info); -+ else -+ kfree(dev->block_info); -+ -+ dev->block_info_alt = 0; -+ -+ dev->block_info = NULL; -+ -+ if (dev->chunk_bits_alt && dev->chunk_bits) -+ vfree(dev->chunk_bits); -+ else -+ kfree(dev->chunk_bits); -+ dev->chunk_bits_alt = 0; -+ dev->chunk_bits = NULL; -+} -+ -+static int yaffs_init_blocks(struct yaffs_dev *dev) -+{ -+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ -+ dev->block_info = NULL; -+ dev->chunk_bits = NULL; -+ dev->alloc_block = -1; /* force it to get a new one */ -+ -+ /* If the first allocation strategy fails, thry the alternate one */ -+ dev->block_info = -+ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); -+ if (!dev->block_info) { -+ dev->block_info = -+ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); -+ dev->block_info_alt = 1; -+ } else { -+ dev->block_info_alt = 0; -+ } -+ -+ if (!dev->block_info) -+ goto alloc_error; -+ -+ /* Set up dynamic blockinfo stuff. Round up bytes. */ -+ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; -+ dev->chunk_bits = -+ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); -+ if (!dev->chunk_bits) { -+ dev->chunk_bits = -+ vmalloc(dev->chunk_bit_stride * n_blocks); -+ dev->chunk_bits_alt = 1; -+ } else { -+ dev->chunk_bits_alt = 0; -+ } -+ if (!dev->chunk_bits) -+ goto alloc_error; -+ -+ -+ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); -+ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); -+ return YAFFS_OK; -+ -+alloc_error: -+ yaffs_deinit_blocks(dev); -+ return YAFFS_FAIL; -+} -+ -+ -+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); -+ int erased_ok = 0; -+ int i; -+ -+ /* If the block is still healthy erase it and mark as clean. -+ * If the block has had a data failure, then retire it. -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, -+ "yaffs_block_became_dirty block %d state %d %s", -+ block_no, bi->block_state, -+ (bi->needs_retiring) ? "needs retiring" : ""); -+ -+ yaffs2_clear_oldest_dirty_seq(dev, bi); -+ -+ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; -+ -+ /* If this is the block being garbage collected then stop gc'ing */ -+ if (block_no == dev->gc_block) -+ dev->gc_block = 0; -+ -+ /* If this block is currently the best candidate for gc -+ * then drop as a candidate */ -+ if (block_no == dev->gc_dirtiest) { -+ dev->gc_dirtiest = 0; -+ dev->gc_pages_in_use = 0; -+ } -+ -+ if (!bi->needs_retiring) { -+ yaffs2_checkpt_invalidate(dev); -+ erased_ok = yaffs_erase_block(dev, block_no); -+ if (!erased_ok) { -+ dev->n_erase_failures++; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Erasure failed %d", block_no); -+ } -+ } -+ -+ /* Verify erasure if needed */ -+ if (erased_ok && -+ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || -+ !yaffs_skip_verification(dev))) { -+ for (i = 0; i < dev->param.chunks_per_block; i++) { -+ if (!yaffs_check_chunk_erased(dev, -+ block_no * dev->param.chunks_per_block + i)) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ ">>Block %d erasure supposedly OK, but chunk %d not erased", -+ block_no, i); -+ } -+ } -+ } -+ -+ if (!erased_ok) { -+ /* We lost a block of free space */ -+ dev->n_free_chunks -= dev->param.chunks_per_block; -+ yaffs_retire_block(dev, block_no); -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>> Block %d retired", block_no); -+ return; -+ } -+ -+ /* Clean it up... */ -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ bi->seq_number = 0; -+ dev->n_erased_blocks++; -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ bi->has_shrink_hdr = 0; -+ bi->skip_erased_check = 1; /* Clean, so no need to check */ -+ bi->gc_prioritise = 0; -+ bi->has_summary = 0; -+ -+ yaffs_clear_chunk_bits(dev, block_no); -+ -+ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); -+} -+ -+static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, -+ int old_chunk, u8 *buffer) -+{ -+ int new_chunk; -+ int mark_flash = 1; -+ struct yaffs_ext_tags tags; -+ struct yaffs_obj *object; -+ int matching_chunk; -+ int ret_val = YAFFS_OK; -+ -+ memset(&tags, 0, sizeof(tags)); -+ yaffs_rd_chunk_tags_nand(dev, old_chunk, -+ buffer, &tags); -+ object = yaffs_find_by_number(dev, tags.obj_id); -+ -+ yaffs_trace(YAFFS_TRACE_GC_DETAIL, -+ "Collecting chunk in block %d, %d %d %d ", -+ dev->gc_chunk, tags.obj_id, -+ tags.chunk_id, tags.n_bytes); -+ -+ if (object && !yaffs_skip_verification(dev)) { -+ if (tags.chunk_id == 0) -+ matching_chunk = -+ object->hdr_chunk; -+ else if (object->soft_del) -+ /* Defeat the test */ -+ matching_chunk = old_chunk; -+ else -+ matching_chunk = -+ yaffs_find_chunk_in_file -+ (object, tags.chunk_id, -+ NULL); -+ -+ if (old_chunk != matching_chunk) -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "gc: page in gc mismatch: %d %d %d %d", -+ old_chunk, -+ matching_chunk, -+ tags.obj_id, -+ tags.chunk_id); -+ } -+ -+ if (!object) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "page %d in gc has no object: %d %d %d ", -+ old_chunk, -+ tags.obj_id, tags.chunk_id, -+ tags.n_bytes); -+ } -+ -+ if (object && -+ object->deleted && -+ object->soft_del && tags.chunk_id != 0) { -+ /* Data chunk in a soft deleted file, -+ * throw it away. -+ * It's a soft deleted data chunk, -+ * No need to copy this, just forget -+ * about it and fix up the object. -+ */ -+ -+ /* Free chunks already includes -+ * softdeleted chunks, how ever this -+ * chunk is going to soon be really -+ * deleted which will increment free -+ * chunks. We have to decrement free -+ * chunks so this works out properly. -+ */ -+ dev->n_free_chunks--; -+ bi->soft_del_pages--; -+ -+ object->n_data_chunks--; -+ if (object->n_data_chunks <= 0) { -+ /* remeber to clean up obj */ -+ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; -+ dev->n_clean_ups++; -+ } -+ mark_flash = 0; -+ } else if (object) { -+ /* It's either a data chunk in a live -+ * file or an ObjectHeader, so we're -+ * interested in it. -+ * NB Need to keep the ObjectHeaders of -+ * deleted files until the whole file -+ * has been deleted off -+ */ -+ tags.serial_number++; -+ dev->n_gc_copies++; -+ -+ if (tags.chunk_id == 0) { -+ /* It is an object Id, -+ * We need to nuke the -+ * shrinkheader flags since its -+ * work is done. -+ * Also need to clean up -+ * shadowing. -+ */ -+ struct yaffs_obj_hdr *oh; -+ oh = (struct yaffs_obj_hdr *) buffer; -+ -+ oh->is_shrink = 0; -+ tags.extra_is_shrink = 0; -+ oh->shadows_obj = 0; -+ oh->inband_shadowed_obj_id = 0; -+ tags.extra_shadows = 0; -+ -+ /* Update file size */ -+ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { -+ yaffs_oh_size_load(oh, -+ object->variant.file_variant.file_size); -+ tags.extra_file_size = -+ object->variant.file_variant.file_size; -+ } -+ -+ yaffs_verify_oh(object, oh, &tags, 1); -+ new_chunk = -+ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); -+ } else { -+ new_chunk = -+ yaffs_write_new_chunk(dev, buffer, &tags, 1); -+ } -+ -+ if (new_chunk < 0) { -+ ret_val = YAFFS_FAIL; -+ } else { -+ -+ /* Now fix up the Tnodes etc. */ -+ -+ if (tags.chunk_id == 0) { -+ /* It's a header */ -+ object->hdr_chunk = new_chunk; -+ object->serial = tags.serial_number; -+ } else { -+ /* It's a data chunk */ -+ yaffs_put_chunk_in_file(object, tags.chunk_id, -+ new_chunk, 0); -+ } -+ } -+ } -+ if (ret_val == YAFFS_OK) -+ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); -+ return ret_val; -+} -+ -+static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) -+{ -+ int old_chunk; -+ int ret_val = YAFFS_OK; -+ int i; -+ int is_checkpt_block; -+ int max_copies; -+ int chunks_before = yaffs_get_erased_chunks(dev); -+ int chunks_after; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); -+ -+ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "Collecting block %d, in use %d, shrink %d, whole_block %d", -+ block, bi->pages_in_use, bi->has_shrink_hdr, -+ whole_block); -+ -+ /*yaffs_verify_free_chunks(dev); */ -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) -+ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; -+ -+ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ -+ -+ dev->gc_disable = 1; -+ -+ yaffs_summary_gc(dev, block); -+ -+ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "Collecting block %d that has no chunks in use", -+ block); -+ yaffs_block_became_dirty(dev, block); -+ } else { -+ -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ yaffs_verify_blk(dev, bi, block); -+ -+ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; -+ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; -+ -+ for (/* init already done */ ; -+ ret_val == YAFFS_OK && -+ dev->gc_chunk < dev->param.chunks_per_block && -+ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && -+ max_copies > 0; -+ dev->gc_chunk++, old_chunk++) { -+ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { -+ /* Page is in use and might need to be copied */ -+ max_copies--; -+ ret_val = yaffs_gc_process_chunk(dev, bi, -+ old_chunk, buffer); -+ } -+ } -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ -+ yaffs_verify_collected_blk(dev, bi, block); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { -+ /* -+ * The gc did not complete. Set block state back to FULL -+ * because checkpointing does not restore gc. -+ */ -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ } else { -+ /* The gc completed. */ -+ /* Do any required cleanups */ -+ for (i = 0; i < dev->n_clean_ups; i++) { -+ /* Time to delete the file too */ -+ struct yaffs_obj *object = -+ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); -+ if (object) { -+ yaffs_free_tnode(dev, -+ object->variant.file_variant.top); -+ object->variant.file_variant.top = NULL; -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: About to finally delete object %d", -+ object->obj_id); -+ yaffs_generic_obj_del(object); -+ object->my_dev->n_deleted_files--; -+ } -+ -+ } -+ chunks_after = yaffs_get_erased_chunks(dev); -+ if (chunks_before >= chunks_after) -+ yaffs_trace(YAFFS_TRACE_GC, -+ "gc did not increase free chunks before %d after %d", -+ chunks_before, chunks_after); -+ dev->gc_block = 0; -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ -+ dev->gc_disable = 0; -+ -+ return ret_val; -+} -+ -+/* -+ * find_gc_block() selects the dirtiest block (or close enough) -+ * for garbage collection. -+ */ -+ -+static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, -+ int aggressive, int background) -+{ -+ int i; -+ int iterations; -+ unsigned selected = 0; -+ int prioritised = 0; -+ int prioritised_exist = 0; -+ struct yaffs_block_info *bi; -+ int threshold; -+ -+ /* First let's see if we need to grab a prioritised block */ -+ if (dev->has_pending_prioritised_gc && !aggressive) { -+ dev->gc_dirtiest = 0; -+ bi = dev->block_info; -+ for (i = dev->internal_start_block; -+ i <= dev->internal_end_block && !selected; i++) { -+ -+ if (bi->gc_prioritise) { -+ prioritised_exist = 1; -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && -+ yaffs_block_ok_for_gc(dev, bi)) { -+ selected = i; -+ prioritised = 1; -+ } -+ } -+ bi++; -+ } -+ -+ /* -+ * If there is a prioritised block and none was selected then -+ * this happened because there is at least one old dirty block -+ * gumming up the works. Let's gc the oldest dirty block. -+ */ -+ -+ if (prioritised_exist && -+ !selected && dev->oldest_dirty_block > 0) -+ selected = dev->oldest_dirty_block; -+ -+ if (!prioritised_exist) /* None found, so we can clear this */ -+ dev->has_pending_prioritised_gc = 0; -+ } -+ -+ /* If we're doing aggressive GC then we are happy to take a less-dirty -+ * block, and search harder. -+ * else (leasurely gc), then we only bother to do this if the -+ * block has only a few pages in use. -+ */ -+ -+ if (!selected) { -+ int pages_used; -+ int n_blocks = -+ dev->internal_end_block - dev->internal_start_block + 1; -+ if (aggressive) { -+ threshold = dev->param.chunks_per_block; -+ iterations = n_blocks; -+ } else { -+ int max_threshold; -+ -+ if (background) -+ max_threshold = dev->param.chunks_per_block / 2; -+ else -+ max_threshold = dev->param.chunks_per_block / 8; -+ -+ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) -+ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; -+ -+ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; -+ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) -+ threshold = YAFFS_GC_PASSIVE_THRESHOLD; -+ if (threshold > max_threshold) -+ threshold = max_threshold; -+ -+ iterations = n_blocks / 16 + 1; -+ if (iterations > 100) -+ iterations = 100; -+ } -+ -+ for (i = 0; -+ i < iterations && -+ (dev->gc_dirtiest < 1 || -+ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); -+ i++) { -+ dev->gc_block_finder++; -+ if (dev->gc_block_finder < dev->internal_start_block || -+ dev->gc_block_finder > dev->internal_end_block) -+ dev->gc_block_finder = -+ dev->internal_start_block; -+ -+ bi = yaffs_get_block_info(dev, dev->gc_block_finder); -+ -+ pages_used = bi->pages_in_use - bi->soft_del_pages; -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && -+ pages_used < dev->param.chunks_per_block && -+ (dev->gc_dirtiest < 1 || -+ pages_used < dev->gc_pages_in_use) && -+ yaffs_block_ok_for_gc(dev, bi)) { -+ dev->gc_dirtiest = dev->gc_block_finder; -+ dev->gc_pages_in_use = pages_used; -+ } -+ } -+ -+ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) -+ selected = dev->gc_dirtiest; -+ } -+ -+ /* -+ * If nothing has been selected for a while, try the oldest dirty -+ * because that's gumming up the works. -+ */ -+ -+ if (!selected && dev->param.is_yaffs2 && -+ dev->gc_not_done >= (background ? 10 : 20)) { -+ yaffs2_find_oldest_dirty_seq(dev); -+ if (dev->oldest_dirty_block > 0) { -+ selected = dev->oldest_dirty_block; -+ dev->gc_dirtiest = selected; -+ dev->oldest_dirty_gc_count++; -+ bi = yaffs_get_block_info(dev, selected); -+ dev->gc_pages_in_use = -+ bi->pages_in_use - bi->soft_del_pages; -+ } else { -+ dev->gc_not_done = 0; -+ } -+ } -+ -+ if (selected) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC Selected block %d with %d free, prioritised:%d", -+ selected, -+ dev->param.chunks_per_block - dev->gc_pages_in_use, -+ prioritised); -+ -+ dev->n_gc_blocks++; -+ if (background) -+ dev->bg_gcs++; -+ -+ dev->gc_dirtiest = 0; -+ dev->gc_pages_in_use = 0; -+ dev->gc_not_done = 0; -+ if (dev->refresh_skip > 0) -+ dev->refresh_skip--; -+ } else { -+ dev->gc_not_done++; -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", -+ dev->gc_block_finder, dev->gc_not_done, threshold, -+ dev->gc_dirtiest, dev->gc_pages_in_use, -+ dev->oldest_dirty_block, background ? " bg" : ""); -+ } -+ -+ return selected; -+} -+ -+/* New garbage collector -+ * If we're very low on erased blocks then we do aggressive garbage collection -+ * otherwise we do "leasurely" garbage collection. -+ * Aggressive gc looks further (whole array) and will accept less dirty blocks. -+ * Passive gc only inspects smaller areas and only accepts more dirty blocks. -+ * -+ * The idea is to help clear out space in a more spread-out manner. -+ * Dunno if it really does anything useful. -+ */ -+static int yaffs_check_gc(struct yaffs_dev *dev, int background) -+{ -+ int aggressive = 0; -+ int gc_ok = YAFFS_OK; -+ int max_tries = 0; -+ int min_erased; -+ int erased_chunks; -+ int checkpt_block_adjust; -+ -+ if (dev->param.gc_control_fn && -+ (dev->param.gc_control_fn(dev) & 1) == 0) -+ return YAFFS_OK; -+ -+ if (dev->gc_disable) -+ /* Bail out so we don't get recursive gc */ -+ return YAFFS_OK; -+ -+ /* This loop should pass the first time. -+ * Only loops here if the collection does not increase space. -+ */ -+ -+ do { -+ max_tries++; -+ -+ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); -+ -+ min_erased = -+ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; -+ erased_chunks = -+ dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ /* If we need a block soon then do aggressive gc. */ -+ if (dev->n_erased_blocks < min_erased) -+ aggressive = 1; -+ else { -+ if (!background -+ && erased_chunks > (dev->n_free_chunks / 4)) -+ break; -+ -+ if (dev->gc_skip > 20) -+ dev->gc_skip = 20; -+ if (erased_chunks < dev->n_free_chunks / 2 || -+ dev->gc_skip < 1 || background) -+ aggressive = 0; -+ else { -+ dev->gc_skip--; -+ break; -+ } -+ } -+ -+ dev->gc_skip = 5; -+ -+ /* If we don't already have a block being gc'd then see if we -+ * should start another */ -+ -+ if (dev->gc_block < 1 && !aggressive) { -+ dev->gc_block = yaffs2_find_refresh_block(dev); -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ if (dev->gc_block < 1) { -+ dev->gc_block = -+ yaffs_find_gc_block(dev, aggressive, background); -+ dev->gc_chunk = 0; -+ dev->n_clean_ups = 0; -+ } -+ -+ if (dev->gc_block > 0) { -+ dev->all_gcs++; -+ if (!aggressive) -+ dev->passive_gc_count++; -+ -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: GC n_erased_blocks %d aggressive %d", -+ dev->n_erased_blocks, aggressive); -+ -+ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); -+ } -+ -+ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && -+ dev->gc_block > 0) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", -+ dev->n_erased_blocks, max_tries, -+ dev->gc_block); -+ } -+ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && -+ (dev->gc_block > 0) && (max_tries < 2)); -+ -+ return aggressive ? gc_ok : YAFFS_OK; -+} -+ -+/* -+ * yaffs_bg_gc() -+ * Garbage collects. Intended to be called from a background thread. -+ * Returns non-zero if at least half the free chunks are erased. -+ */ -+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) -+{ -+ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); -+ -+ yaffs_check_gc(dev, 1); -+ return erased_chunks > dev->n_free_chunks / 2; -+} -+ -+/*-------------------- Data file manipulation -----------------*/ -+ -+static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) -+{ -+ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); -+ -+ if (nand_chunk >= 0) -+ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, -+ buffer, NULL); -+ else { -+ yaffs_trace(YAFFS_TRACE_NANDACCESS, -+ "Chunk %d not found zero instead", -+ nand_chunk); -+ /* get sane (zero) data if you read a hole */ -+ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); -+ return 0; -+ } -+ -+} -+ -+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, -+ int lyn) -+{ -+ int block; -+ int page; -+ struct yaffs_ext_tags tags; -+ struct yaffs_block_info *bi; -+ -+ if (chunk_id <= 0) -+ return; -+ -+ dev->n_deletions++; -+ block = chunk_id / dev->param.chunks_per_block; -+ page = chunk_id % dev->param.chunks_per_block; -+ -+ if (!yaffs_check_chunk_bit(dev, block, page)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Deleting invalid chunk %d", chunk_id); -+ -+ bi = yaffs_get_block_info(dev, block); -+ -+ yaffs2_update_oldest_dirty_seq(dev, block, bi); -+ -+ yaffs_trace(YAFFS_TRACE_DELETION, -+ "line %d delete of chunk %d", -+ lyn, chunk_id); -+ -+ if (!dev->param.is_yaffs2 && mark_flash && -+ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { -+ -+ memset(&tags, 0, sizeof(tags)); -+ tags.is_deleted = 1; -+ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); -+ yaffs_handle_chunk_update(dev, chunk_id, &tags); -+ } else { -+ dev->n_unmarked_deletions++; -+ } -+ -+ /* Pull out of the management area. -+ * If the whole block became dirty, this will kick off an erasure. -+ */ -+ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || -+ bi->block_state == YAFFS_BLOCK_STATE_FULL || -+ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { -+ dev->n_free_chunks++; -+ yaffs_clear_chunk_bit(dev, block, page); -+ bi->pages_in_use--; -+ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && -+ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ yaffs_block_became_dirty(dev, block); -+ } -+ } -+} -+ -+static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, -+ const u8 *buffer, int n_bytes, int use_reserve) -+{ -+ /* Find old chunk Need to do this to get serial number -+ * Write new one and patch into tree. -+ * Invalidate old tags. -+ */ -+ -+ int prev_chunk_id; -+ struct yaffs_ext_tags prev_tags; -+ int new_chunk_id; -+ struct yaffs_ext_tags new_tags; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ yaffs_check_gc(dev, 0); -+ -+ /* Get the previous chunk at this location in the file if it exists. -+ * If it does not exist then put a zero into the tree. This creates -+ * the tnode now, rather than later when it is harder to clean up. -+ */ -+ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); -+ if (prev_chunk_id < 1 && -+ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) -+ return 0; -+ -+ /* Set up new tags */ -+ memset(&new_tags, 0, sizeof(new_tags)); -+ -+ new_tags.chunk_id = inode_chunk; -+ new_tags.obj_id = in->obj_id; -+ new_tags.serial_number = -+ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; -+ new_tags.n_bytes = n_bytes; -+ -+ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Writing %d bytes to chunk!!!!!!!!!", -+ n_bytes); -+ BUG(); -+ } -+ -+ new_chunk_id = -+ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); -+ -+ if (new_chunk_id > 0) { -+ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); -+ -+ if (prev_chunk_id > 0) -+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -+ -+ yaffs_verify_file_sane(in); -+ } -+ return new_chunk_id; -+ -+} -+ -+ -+ -+static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, -+ const YCHAR *name, const void *value, int size, -+ int flags) -+{ -+ struct yaffs_xattr_mod xmod; -+ int result; -+ -+ xmod.set = set; -+ xmod.name = name; -+ xmod.data = value; -+ xmod.size = size; -+ xmod.flags = flags; -+ xmod.result = -ENOSPC; -+ -+ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); -+ -+ if (result > 0) -+ return xmod.result; -+ else -+ return -ENOSPC; -+} -+ -+static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, -+ struct yaffs_xattr_mod *xmod) -+{ -+ int retval = 0; -+ int x_offs = sizeof(struct yaffs_obj_hdr); -+ struct yaffs_dev *dev = obj->my_dev; -+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); -+ char *x_buffer = buffer + x_offs; -+ -+ if (xmod->set) -+ retval = -+ nval_set(x_buffer, x_size, xmod->name, xmod->data, -+ xmod->size, xmod->flags); -+ else -+ retval = nval_del(x_buffer, x_size, xmod->name); -+ -+ obj->has_xattr = nval_hasvalues(x_buffer, x_size); -+ obj->xattr_known = 1; -+ xmod->result = retval; -+ -+ return retval; -+} -+ -+static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, -+ void *value, int size) -+{ -+ char *buffer = NULL; -+ int result; -+ struct yaffs_ext_tags tags; -+ struct yaffs_dev *dev = obj->my_dev; -+ int x_offs = sizeof(struct yaffs_obj_hdr); -+ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); -+ char *x_buffer; -+ int retval = 0; -+ -+ if (obj->hdr_chunk < 1) -+ return -ENODATA; -+ -+ /* If we know that the object has no xattribs then don't do all the -+ * reading and parsing. -+ */ -+ if (obj->xattr_known && !obj->has_xattr) { -+ if (name) -+ return -ENODATA; -+ else -+ return 0; -+ } -+ -+ buffer = (char *)yaffs_get_temp_buffer(dev); -+ if (!buffer) -+ return -ENOMEM; -+ -+ result = -+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); -+ -+ if (result != YAFFS_OK) -+ retval = -ENOENT; -+ else { -+ x_buffer = buffer + x_offs; -+ -+ if (!obj->xattr_known) { -+ obj->has_xattr = nval_hasvalues(x_buffer, x_size); -+ obj->xattr_known = 1; -+ } -+ -+ if (name) -+ retval = nval_get(x_buffer, x_size, name, value, size); -+ else -+ retval = nval_list(x_buffer, x_size, value, size); -+ } -+ yaffs_release_temp_buffer(dev, (u8 *) buffer); -+ return retval; -+} -+ -+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, -+ const void *value, int size, int flags) -+{ -+ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); -+} -+ -+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) -+{ -+ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); -+} -+ -+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, -+ int size) -+{ -+ return yaffs_do_xattrib_fetch(obj, name, value, size); -+} -+ -+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) -+{ -+ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); -+} -+ -+static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) -+{ -+ u8 *buf; -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_dev *dev; -+ struct yaffs_ext_tags tags; -+ int result; -+ int alloc_failed = 0; -+ -+ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) -+ return; -+ -+ dev = in->my_dev; -+ in->lazy_loaded = 0; -+ buf = yaffs_get_temp_buffer(dev); -+ -+ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); -+ oh = (struct yaffs_obj_hdr *)buf; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ yaffs_set_obj_name_from_oh(in, oh); -+ -+ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ in->variant.symlink_variant.alias = -+ yaffs_clone_str(oh->alias); -+ if (!in->variant.symlink_variant.alias) -+ alloc_failed = 1; /* Not returned */ -+ } -+ yaffs_release_temp_buffer(dev, buf); -+} -+ -+static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, -+ const YCHAR *oh_name, int buff_size) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ if (dev->param.auto_unicode) { -+ if (*oh_name) { -+ /* It is an ASCII name, do an ASCII to -+ * unicode conversion */ -+ const char *ascii_oh_name = (const char *)oh_name; -+ int n = buff_size - 1; -+ while (n > 0 && *ascii_oh_name) { -+ *name = *ascii_oh_name; -+ name++; -+ ascii_oh_name++; -+ n--; -+ } -+ } else { -+ strncpy(name, oh_name + 1, buff_size - 1); -+ } -+ } else { -+#else -+ (void) dev; -+ { -+#endif -+ strncpy(name, oh_name, buff_size - 1); -+ } -+} -+ -+static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, -+ const YCHAR *name) -+{ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ -+ int is_ascii; -+ YCHAR *w; -+ -+ if (dev->param.auto_unicode) { -+ -+ is_ascii = 1; -+ w = name; -+ -+ /* Figure out if the name will fit in ascii character set */ -+ while (is_ascii && *w) { -+ if ((*w) & 0xff00) -+ is_ascii = 0; -+ w++; -+ } -+ -+ if (is_ascii) { -+ /* It is an ASCII name, so convert unicode to ascii */ -+ char *ascii_oh_name = (char *)oh_name; -+ int n = YAFFS_MAX_NAME_LENGTH - 1; -+ while (n > 0 && *name) { -+ *ascii_oh_name = *name; -+ name++; -+ ascii_oh_name++; -+ n--; -+ } -+ } else { -+ /* Unicode name, so save starting at the second YCHAR */ -+ *oh_name = 0; -+ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); -+ } -+ } else { -+#else -+ dev = dev; -+ { -+#endif -+ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); -+ } -+} -+ -+/* UpdateObjectHeader updates the header on NAND for an object. -+ * If name is not NULL, then that new name is used. -+ */ -+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, -+ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) -+{ -+ -+ struct yaffs_block_info *bi; -+ struct yaffs_dev *dev = in->my_dev; -+ int prev_chunk_id; -+ int ret_val = 0; -+ int result = 0; -+ int new_chunk_id; -+ struct yaffs_ext_tags new_tags; -+ struct yaffs_ext_tags old_tags; -+ const YCHAR *alias = NULL; -+ u8 *buffer = NULL; -+ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; -+ struct yaffs_obj_hdr *oh = NULL; -+ loff_t file_size = 0; -+ -+ strcpy(old_name, _Y("silly old name")); -+ -+ if (in->fake && in != dev->root_dir && !force && !xmod) -+ return ret_val; -+ -+ yaffs_check_gc(dev, 0); -+ yaffs_check_obj_details_loaded(in); -+ -+ buffer = yaffs_get_temp_buffer(in->my_dev); -+ oh = (struct yaffs_obj_hdr *)buffer; -+ -+ prev_chunk_id = in->hdr_chunk; -+ -+ if (prev_chunk_id > 0) { -+ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, -+ buffer, &old_tags); -+ -+ yaffs_verify_oh(in, oh, &old_tags, 0); -+ memcpy(old_name, oh->name, sizeof(oh->name)); -+ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); -+ } else { -+ memset(buffer, 0xff, dev->data_bytes_per_chunk); -+ } -+ -+ oh->type = in->variant_type; -+ oh->yst_mode = in->yst_mode; -+ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; -+ -+ yaffs_load_attribs_oh(oh, in); -+ -+ if (in->parent) -+ oh->parent_obj_id = in->parent->obj_id; -+ else -+ oh->parent_obj_id = 0; -+ -+ if (name && *name) { -+ memset(oh->name, 0, sizeof(oh->name)); -+ yaffs_load_oh_from_name(dev, oh->name, name); -+ } else if (prev_chunk_id > 0) { -+ memcpy(oh->name, old_name, sizeof(oh->name)); -+ } else { -+ memset(oh->name, 0, sizeof(oh->name)); -+ } -+ -+ oh->is_shrink = is_shrink; -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Should not happen */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && -+ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) -+ file_size = in->variant.file_variant.file_size; -+ yaffs_oh_size_load(oh, file_size); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ oh->equiv_id = in->variant.hardlink_variant.equiv_id; -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ alias = in->variant.symlink_variant.alias; -+ if (!alias) -+ alias = _Y("no alias"); -+ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); -+ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; -+ break; -+ } -+ -+ /* process any xattrib modifications */ -+ if (xmod) -+ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); -+ -+ /* Tags */ -+ memset(&new_tags, 0, sizeof(new_tags)); -+ in->serial++; -+ new_tags.chunk_id = 0; -+ new_tags.obj_id = in->obj_id; -+ new_tags.serial_number = in->serial; -+ -+ /* Add extra info for file header */ -+ new_tags.extra_available = 1; -+ new_tags.extra_parent_id = oh->parent_obj_id; -+ new_tags.extra_file_size = file_size; -+ new_tags.extra_is_shrink = oh->is_shrink; -+ new_tags.extra_equiv_id = oh->equiv_id; -+ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; -+ new_tags.extra_obj_type = in->variant_type; -+ yaffs_verify_oh(in, oh, &new_tags, 1); -+ -+ /* Create new chunk in NAND */ -+ new_chunk_id = -+ yaffs_write_new_chunk(dev, buffer, &new_tags, -+ (prev_chunk_id > 0) ? 1 : 0); -+ -+ if (buffer) -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ if (new_chunk_id < 0) -+ return new_chunk_id; -+ -+ in->hdr_chunk = new_chunk_id; -+ -+ if (prev_chunk_id > 0) -+ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); -+ -+ if (!yaffs_obj_cache_dirty(in)) -+ in->dirty = 0; -+ -+ /* If this was a shrink, then mark the block -+ * that the chunk lives on */ -+ if (is_shrink) { -+ bi = yaffs_get_block_info(in->my_dev, -+ new_chunk_id / -+ in->my_dev->param.chunks_per_block); -+ bi->has_shrink_hdr = 1; -+ } -+ -+ -+ return new_chunk_id; -+} -+ -+/*--------------------- File read/write ------------------------ -+ * Read and write have very similar structures. -+ * In general the read/write has three parts to it -+ * An incomplete chunk to start with (if the read/write is not chunk-aligned) -+ * Some complete chunks -+ * An incomplete chunk to end off with -+ * -+ * Curve-balls: the first chunk might also be the last chunk. -+ */ -+ -+int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) -+{ -+ int chunk; -+ u32 start; -+ int n_copy; -+ int n = n_bytes; -+ int n_done = 0; -+ struct yaffs_cache *cache; -+ struct yaffs_dev *dev; -+ -+ dev = in->my_dev; -+ -+ while (n > 0) { -+ yaffs_addr_to_chunk(dev, offset, &chunk, &start); -+ chunk++; -+ -+ /* OK now check for the curveball where the start and end are in -+ * the same chunk. -+ */ -+ if ((start + n) < dev->data_bytes_per_chunk) -+ n_copy = n; -+ else -+ n_copy = dev->data_bytes_per_chunk - start; -+ -+ cache = yaffs_find_chunk_cache(in, chunk); -+ -+ /* If the chunk is already in the cache or it is less than -+ * a whole chunk or we're using inband tags then use the cache -+ * (if there is caching) else bypass the cache. -+ */ -+ if (cache || n_copy != dev->data_bytes_per_chunk || -+ dev->param.inband_tags) { -+ if (dev->param.n_caches > 0) { -+ -+ /* If we can't find the data in the cache, -+ * then load it up. */ -+ -+ if (!cache) { -+ cache = -+ yaffs_grab_chunk_cache(in->my_dev); -+ cache->object = in; -+ cache->chunk_id = chunk; -+ cache->dirty = 0; -+ cache->locked = 0; -+ yaffs_rd_data_obj(in, chunk, -+ cache->data); -+ cache->n_bytes = 0; -+ } -+ -+ yaffs_use_cache(dev, cache, 0); -+ -+ cache->locked = 1; -+ -+ memcpy(buffer, &cache->data[start], n_copy); -+ -+ cache->locked = 0; -+ } else { -+ /* Read into the local buffer then copy.. */ -+ -+ u8 *local_buffer = -+ yaffs_get_temp_buffer(dev); -+ yaffs_rd_data_obj(in, chunk, local_buffer); -+ -+ memcpy(buffer, &local_buffer[start], n_copy); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ } else { -+ /* A full chunk. Read directly into the buffer. */ -+ yaffs_rd_data_obj(in, chunk, buffer); -+ } -+ n -= n_copy; -+ offset += n_copy; -+ buffer += n_copy; -+ n_done += n_copy; -+ } -+ return n_done; -+} -+ -+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_through) -+{ -+ -+ int chunk; -+ u32 start; -+ int n_copy; -+ int n = n_bytes; -+ int n_done = 0; -+ int n_writeback; -+ loff_t start_write = offset; -+ int chunk_written = 0; -+ u32 n_bytes_read; -+ loff_t chunk_start; -+ struct yaffs_dev *dev; -+ -+ dev = in->my_dev; -+ -+ while (n > 0 && chunk_written >= 0) { -+ yaffs_addr_to_chunk(dev, offset, &chunk, &start); -+ -+ if (((loff_t)chunk) * -+ dev->data_bytes_per_chunk + start != offset || -+ start >= dev->data_bytes_per_chunk) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "AddrToChunk of offset %lld gives chunk %d start %d", -+ offset, chunk, start); -+ } -+ chunk++; /* File pos to chunk in file offset */ -+ -+ /* OK now check for the curveball where the start and end are in -+ * the same chunk. -+ */ -+ -+ if ((start + n) < dev->data_bytes_per_chunk) { -+ n_copy = n; -+ -+ /* Now calculate how many bytes to write back.... -+ * If we're overwriting and not writing to then end of -+ * file then we need to write back as much as was there -+ * before. -+ */ -+ -+ chunk_start = (((loff_t)(chunk - 1)) * -+ dev->data_bytes_per_chunk); -+ -+ if (chunk_start > in->variant.file_variant.file_size) -+ n_bytes_read = 0; /* Past end of file */ -+ else -+ n_bytes_read = -+ in->variant.file_variant.file_size - -+ chunk_start; -+ -+ if (n_bytes_read > dev->data_bytes_per_chunk) -+ n_bytes_read = dev->data_bytes_per_chunk; -+ -+ n_writeback = -+ (n_bytes_read > -+ (start + n)) ? n_bytes_read : (start + n); -+ -+ if (n_writeback < 0 || -+ n_writeback > dev->data_bytes_per_chunk) -+ BUG(); -+ -+ } else { -+ n_copy = dev->data_bytes_per_chunk - start; -+ n_writeback = dev->data_bytes_per_chunk; -+ } -+ -+ if (n_copy != dev->data_bytes_per_chunk || -+ !dev->param.cache_bypass_aligned || -+ dev->param.inband_tags) { -+ /* An incomplete start or end chunk (or maybe both -+ * start and end chunk), or we're using inband tags, -+ * or we're forcing writes through the cache, -+ * so we want to use the cache buffers. -+ */ -+ if (dev->param.n_caches > 0) { -+ struct yaffs_cache *cache; -+ -+ /* If we can't find the data in the cache, then -+ * load the cache */ -+ cache = yaffs_find_chunk_cache(in, chunk); -+ -+ if (!cache && -+ yaffs_check_alloc_available(dev, 1)) { -+ cache = yaffs_grab_chunk_cache(dev); -+ cache->object = in; -+ cache->chunk_id = chunk; -+ cache->dirty = 0; -+ cache->locked = 0; -+ yaffs_rd_data_obj(in, chunk, -+ cache->data); -+ } else if (cache && -+ !cache->dirty && -+ !yaffs_check_alloc_available(dev, -+ 1)) { -+ /* Drop the cache if it was a read cache -+ * item and no space check has been made -+ * for it. -+ */ -+ cache = NULL; -+ } -+ -+ if (cache) { -+ yaffs_use_cache(dev, cache, 1); -+ cache->locked = 1; -+ -+ memcpy(&cache->data[start], buffer, -+ n_copy); -+ -+ cache->locked = 0; -+ cache->n_bytes = n_writeback; -+ -+ if (write_through) { -+ chunk_written = -+ yaffs_wr_data_obj -+ (cache->object, -+ cache->chunk_id, -+ cache->data, -+ cache->n_bytes, 1); -+ cache->dirty = 0; -+ } -+ } else { -+ chunk_written = -1; /* fail write */ -+ } -+ } else { -+ /* An incomplete start or end chunk (or maybe -+ * both start and end chunk). Read into the -+ * local buffer then copy over and write back. -+ */ -+ -+ u8 *local_buffer = yaffs_get_temp_buffer(dev); -+ -+ yaffs_rd_data_obj(in, chunk, local_buffer); -+ memcpy(&local_buffer[start], buffer, n_copy); -+ -+ chunk_written = -+ yaffs_wr_data_obj(in, chunk, -+ local_buffer, -+ n_writeback, 0); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ } else { -+ /* A full chunk. Write directly from the buffer. */ -+ -+ chunk_written = -+ yaffs_wr_data_obj(in, chunk, buffer, -+ dev->data_bytes_per_chunk, 0); -+ -+ /* Since we've overwritten the cached data, -+ * we better invalidate it. */ -+ yaffs_invalidate_chunk_cache(in, chunk); -+ } -+ -+ if (chunk_written >= 0) { -+ n -= n_copy; -+ offset += n_copy; -+ buffer += n_copy; -+ n_done += n_copy; -+ } -+ } -+ -+ /* Update file object */ -+ -+ if ((start_write + n_done) > in->variant.file_variant.file_size) -+ in->variant.file_variant.file_size = (start_write + n_done); -+ -+ in->dirty = 1; -+ return n_done; -+} -+ -+int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_through) -+{ -+ yaffs2_handle_hole(in, offset); -+ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); -+} -+ -+/* ---------------------- File resizing stuff ------------------ */ -+ -+static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) -+{ -+ -+ struct yaffs_dev *dev = in->my_dev; -+ loff_t old_size = in->variant.file_variant.file_size; -+ int i; -+ int chunk_id; -+ u32 dummy; -+ int last_del; -+ int start_del; -+ -+ if (old_size > 0) -+ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); -+ else -+ last_del = 0; -+ -+ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, -+ &start_del, &dummy); -+ last_del++; -+ start_del++; -+ -+ /* Delete backwards so that we don't end up with holes if -+ * power is lost part-way through the operation. -+ */ -+ for (i = last_del; i >= start_del; i--) { -+ /* NB this could be optimised somewhat, -+ * eg. could retrieve the tags and write them without -+ * using yaffs_chunk_del -+ */ -+ -+ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); -+ -+ if (chunk_id < 1) -+ continue; -+ -+ if (chunk_id < -+ (dev->internal_start_block * dev->param.chunks_per_block) || -+ chunk_id >= -+ ((dev->internal_end_block + 1) * -+ dev->param.chunks_per_block)) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Found daft chunk_id %d for %d", -+ chunk_id, i); -+ } else { -+ in->n_data_chunks--; -+ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); -+ } -+ } -+} -+ -+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) -+{ -+ int new_full; -+ u32 new_partial; -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); -+ -+ yaffs_prune_chunks(obj, new_size); -+ -+ if (new_partial != 0) { -+ int last_chunk = 1 + new_full; -+ u8 *local_buffer = yaffs_get_temp_buffer(dev); -+ -+ /* Rewrite the last chunk with its new size and zero pad */ -+ yaffs_rd_data_obj(obj, last_chunk, local_buffer); -+ memset(local_buffer + new_partial, 0, -+ dev->data_bytes_per_chunk - new_partial); -+ -+ yaffs_wr_data_obj(obj, last_chunk, local_buffer, -+ new_partial, 1); -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ } -+ -+ obj->variant.file_variant.file_size = new_size; -+ -+ yaffs_prune_tree(dev, &obj->variant.file_variant); -+} -+ -+int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) -+{ -+ struct yaffs_dev *dev = in->my_dev; -+ loff_t old_size = in->variant.file_variant.file_size; -+ -+ yaffs_flush_file_cache(in); -+ yaffs_invalidate_whole_cache(in); -+ -+ yaffs_check_gc(dev, 0); -+ -+ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return YAFFS_FAIL; -+ -+ if (new_size == old_size) -+ return YAFFS_OK; -+ -+ if (new_size > old_size) { -+ yaffs2_handle_hole(in, new_size); -+ in->variant.file_variant.file_size = new_size; -+ } else { -+ /* new_size < old_size */ -+ yaffs_resize_file_down(in, new_size); -+ } -+ -+ /* Write a new object header to reflect the resize. -+ * show we've shrunk the file, if need be -+ * Do this only if the file is not in the deleted directories -+ * and is not shadowed. -+ */ -+ if (in->parent && -+ !in->is_shadowed && -+ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && -+ in->parent->obj_id != YAFFS_OBJECTID_DELETED) -+ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); -+ -+ return YAFFS_OK; -+} -+ -+int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync) -+{ -+ if (!in->dirty) -+ return YAFFS_OK; -+ -+ yaffs_flush_file_cache(in); -+ -+ if (data_sync) -+ return YAFFS_OK; -+ -+ if (update_time) -+ yaffs_load_current_time(in, 0, 0); -+ -+ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? -+ YAFFS_OK : YAFFS_FAIL; -+} -+ -+ -+/* yaffs_del_file deletes the whole file data -+ * and the inode associated with the file. -+ * It does not delete the links associated with the file. -+ */ -+static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) -+{ -+ int ret_val; -+ int del_now = 0; -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (!in->my_inode) -+ del_now = 1; -+ -+ if (del_now) { -+ ret_val = -+ yaffs_change_obj_name(in, in->my_dev->del_dir, -+ _Y("deleted"), 0, 0); -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: immediate deletion of file %d", -+ in->obj_id); -+ in->deleted = 1; -+ in->my_dev->n_deleted_files++; -+ if (dev->param.disable_soft_del || dev->param.is_yaffs2) -+ yaffs_resize_file(in, 0); -+ yaffs_soft_del_file(in); -+ } else { -+ ret_val = -+ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, -+ _Y("unlinked"), 0, 0); -+ } -+ return ret_val; -+} -+ -+static int yaffs_del_file(struct yaffs_obj *in) -+{ -+ int ret_val = YAFFS_OK; -+ int deleted; /* Need to cache value on stack if in is freed */ -+ struct yaffs_dev *dev = in->my_dev; -+ -+ if (dev->param.disable_soft_del || dev->param.is_yaffs2) -+ yaffs_resize_file(in, 0); -+ -+ if (in->n_data_chunks > 0) { -+ /* Use soft deletion if there is data in the file. -+ * That won't be the case if it has been resized to zero. -+ */ -+ if (!in->unlinked) -+ ret_val = yaffs_unlink_file_if_needed(in); -+ -+ deleted = in->deleted; -+ -+ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { -+ in->deleted = 1; -+ deleted = 1; -+ in->my_dev->n_deleted_files++; -+ yaffs_soft_del_file(in); -+ } -+ return deleted ? YAFFS_OK : YAFFS_FAIL; -+ } else { -+ /* The file has no data chunks so we toss it immediately */ -+ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); -+ in->variant.file_variant.top = NULL; -+ yaffs_generic_obj_del(in); -+ -+ return YAFFS_OK; -+ } -+} -+ -+int yaffs_is_non_empty_dir(struct yaffs_obj *obj) -+{ -+ return (obj && -+ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && -+ !(list_empty(&obj->variant.dir_variant.children)); -+} -+ -+static int yaffs_del_dir(struct yaffs_obj *obj) -+{ -+ /* First check that the directory is empty. */ -+ if (yaffs_is_non_empty_dir(obj)) -+ return YAFFS_FAIL; -+ -+ return yaffs_generic_obj_del(obj); -+} -+ -+static int yaffs_del_symlink(struct yaffs_obj *in) -+{ -+ kfree(in->variant.symlink_variant.alias); -+ in->variant.symlink_variant.alias = NULL; -+ -+ return yaffs_generic_obj_del(in); -+} -+ -+static int yaffs_del_link(struct yaffs_obj *in) -+{ -+ /* remove this hardlink from the list associated with the equivalent -+ * object -+ */ -+ list_del_init(&in->hard_links); -+ return yaffs_generic_obj_del(in); -+} -+ -+int yaffs_del_obj(struct yaffs_obj *obj) -+{ -+ int ret_val = -1; -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ ret_val = yaffs_del_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ if (!list_empty(&obj->variant.dir_variant.dirty)) { -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "Remove object %d from dirty directories", -+ obj->obj_id); -+ list_del_init(&obj->variant.dir_variant.dirty); -+ } -+ return yaffs_del_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ ret_val = yaffs_del_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ ret_val = yaffs_del_link(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ ret_val = yaffs_generic_obj_del(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ ret_val = 0; -+ break; /* should not happen. */ -+ } -+ return ret_val; -+} -+ -+ -+static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, -+ struct yaffs_obj *to_dir) -+{ -+ struct yaffs_obj *obj; -+ struct list_head *lh; -+ struct list_head *n; -+ -+ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ yaffs_add_obj_to_dir(to_dir, obj); -+ } -+} -+ -+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, -+ enum yaffs_obj_type type) -+{ -+ /* Tear down the old variant */ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ /* Nuke file data */ -+ yaffs_resize_file(obj, 0); -+ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); -+ obj->variant.file_variant.top = NULL; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Put the children in lost and found. */ -+ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); -+ if (!list_empty(&obj->variant.dir_variant.dirty)) -+ list_del_init(&obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ /* Nuke symplink data */ -+ kfree(obj->variant.symlink_variant.alias); -+ obj->variant.symlink_variant.alias = NULL; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ list_del_init(&obj->hard_links); -+ break; -+ default: -+ break; -+ } -+ -+ memset(&obj->variant, 0, sizeof(obj->variant)); -+ -+ /*Set up new variant if the memset is not enough. */ -+ switch (type) { -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ INIT_LIST_HEAD(&obj->variant.dir_variant.children); -+ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ default: -+ break; -+ } -+ -+ obj->variant_type = type; -+ -+ return obj; -+ -+} -+ -+static int yaffs_unlink_worker(struct yaffs_obj *obj) -+{ -+ int del_now = 0; -+ -+ if (!obj) -+ return YAFFS_FAIL; -+ -+ if (!obj->my_inode) -+ del_now = 1; -+ -+ yaffs_update_parent(obj->parent); -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { -+ return yaffs_del_link(obj); -+ } else if (!list_empty(&obj->hard_links)) { -+ /* Curve ball: We're unlinking an object that has a hardlink. -+ * -+ * This problem arises because we are not strictly following -+ * The Linux link/inode model. -+ * -+ * We can't really delete the object. -+ * Instead, we do the following: -+ * - Select a hardlink. -+ * - Unhook it from the hard links -+ * - Move it from its parent directory so that the rename works. -+ * - Rename the object to the hardlink's name. -+ * - Delete the hardlink -+ */ -+ -+ struct yaffs_obj *hl; -+ struct yaffs_obj *parent; -+ int ret_val; -+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ hl = list_entry(obj->hard_links.next, struct yaffs_obj, -+ hard_links); -+ -+ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); -+ parent = hl->parent; -+ -+ list_del_init(&hl->hard_links); -+ -+ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); -+ -+ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); -+ -+ if (ret_val == YAFFS_OK) -+ ret_val = yaffs_generic_obj_del(hl); -+ -+ return ret_val; -+ -+ } else if (del_now) { -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return yaffs_del_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ list_del_init(&obj->variant.dir_variant.dirty); -+ return yaffs_del_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ return yaffs_del_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ return yaffs_generic_obj_del(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ default: -+ return YAFFS_FAIL; -+ } -+ } else if (yaffs_is_non_empty_dir(obj)) { -+ return YAFFS_FAIL; -+ } else { -+ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, -+ _Y("unlinked"), 0, 0); -+ } -+} -+ -+static int yaffs_unlink_obj(struct yaffs_obj *obj) -+{ -+ if (obj && obj->unlink_allowed) -+ return yaffs_unlink_worker(obj); -+ -+ return YAFFS_FAIL; -+} -+ -+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) -+{ -+ struct yaffs_obj *obj; -+ -+ obj = yaffs_find_by_name(dir, name); -+ return yaffs_unlink_obj(obj); -+} -+ -+/* Note: -+ * If old_name is NULL then we take old_dir as the object to be renamed. -+ */ -+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, -+ struct yaffs_obj *new_dir, const YCHAR *new_name) -+{ -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_obj *existing_target = NULL; -+ int force = 0; -+ int result; -+ struct yaffs_dev *dev; -+ -+ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ dev = old_dir->my_dev; -+ -+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE -+ /* Special case for case insemsitive systems. -+ * While look-up is case insensitive, the name isn't. -+ * Therefore we might want to change x.txt to X.txt -+ */ -+ if (old_dir == new_dir && -+ old_name && new_name && -+ strcmp(old_name, new_name) == 0) -+ force = 1; -+#endif -+ -+ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > -+ YAFFS_MAX_NAME_LENGTH) -+ /* ENAMETOOLONG */ -+ return YAFFS_FAIL; -+ -+ if (old_name) -+ obj = yaffs_find_by_name(old_dir, old_name); -+ else{ -+ obj = old_dir; -+ old_dir = obj->parent; -+ } -+ -+ if (obj && obj->rename_allowed) { -+ /* Now handle an existing target, if there is one */ -+ existing_target = yaffs_find_by_name(new_dir, new_name); -+ if (yaffs_is_non_empty_dir(existing_target)) { -+ return YAFFS_FAIL; /* ENOTEMPTY */ -+ } else if (existing_target && existing_target != obj) { -+ /* Nuke the target first, using shadowing, -+ * but only if it isn't the same object. -+ * -+ * Note we must disable gc here otherwise it can mess -+ * up the shadowing. -+ * -+ */ -+ dev->gc_disable = 1; -+ yaffs_change_obj_name(obj, new_dir, new_name, force, -+ existing_target->obj_id); -+ existing_target->is_shadowed = 1; -+ yaffs_unlink_obj(existing_target); -+ dev->gc_disable = 0; -+ } -+ -+ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); -+ -+ yaffs_update_parent(old_dir); -+ if (new_dir != old_dir) -+ yaffs_update_parent(new_dir); -+ -+ return result; -+ } -+ return YAFFS_FAIL; -+} -+ -+/*----------------------- Initialisation Scanning ---------------------- */ -+ -+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, -+ int backward_scanning) -+{ -+ struct yaffs_obj *obj; -+ -+ if (backward_scanning) { -+ /* Handle YAFFS2 case (backward scanning) -+ * If the shadowed object exists then ignore. -+ */ -+ obj = yaffs_find_by_number(dev, obj_id); -+ if (obj) -+ return; -+ } -+ -+ /* Let's create it (if it does not exist) assuming it is a file so that -+ * it can do shrinking etc. -+ * We put it in unlinked dir to be cleaned up after the scanning -+ */ -+ obj = -+ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); -+ if (!obj) -+ return; -+ obj->is_shadowed = 1; -+ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); -+ obj->variant.file_variant.shrink_size = 0; -+ obj->valid = 1; /* So that we don't read any other info. */ -+} -+ -+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) -+{ -+ struct list_head *lh; -+ struct list_head *save; -+ struct yaffs_obj *hl; -+ struct yaffs_obj *in; -+ -+ list_for_each_safe(lh, save, hard_list) { -+ hl = list_entry(lh, struct yaffs_obj, hard_links); -+ in = yaffs_find_by_number(dev, -+ hl->variant.hardlink_variant.equiv_id); -+ -+ if (in) { -+ /* Add the hardlink pointers */ -+ hl->variant.hardlink_variant.equiv_obj = in; -+ list_add(&hl->hard_links, &in->hard_links); -+ } else { -+ /* Todo Need to report/handle this better. -+ * Got a problem... hardlink to a non-existant object -+ */ -+ hl->variant.hardlink_variant.equiv_obj = NULL; -+ INIT_LIST_HEAD(&hl->hard_links); -+ } -+ } -+} -+ -+static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) -+{ -+ /* -+ * Sort out state of unlinked and deleted objects after scanning. -+ */ -+ struct list_head *i; -+ struct list_head *n; -+ struct yaffs_obj *l; -+ -+ if (dev->read_only) -+ return; -+ -+ /* Soft delete all the unlinked files */ -+ list_for_each_safe(i, n, -+ &dev->unlinked_dir->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ yaffs_del_obj(l); -+ } -+ -+ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ yaffs_del_obj(l); -+ } -+} -+ -+/* -+ * This code iterates through all the objects making sure that they are rooted. -+ * Any unrooted objects are re-rooted in lost+found. -+ * An object needs to be in one of: -+ * - Directly under deleted, unlinked -+ * - Directly or indirectly under root. -+ * -+ * Note: -+ * This code assumes that we don't ever change the current relationships -+ * between directories: -+ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL -+ * lost-n-found->parent == root_dir -+ * -+ * This fixes the problem where directories might have inadvertently been -+ * deleted leaving the object "hanging" without being rooted in the -+ * directory tree. -+ */ -+ -+static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) -+{ -+ return (obj == dev->del_dir || -+ obj == dev->unlinked_dir || obj == dev->root_dir); -+} -+ -+static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_obj *parent; -+ int i; -+ struct list_head *lh; -+ struct list_head *n; -+ int depth_limit; -+ int hanging; -+ -+ if (dev->read_only) -+ return; -+ -+ /* Iterate through the objects in each hash entry, -+ * looking at each object. -+ * Make sure it is rooted. -+ */ -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ parent = obj->parent; -+ -+ if (yaffs_has_null_parent(dev, obj)) { -+ /* These directories are not hanging */ -+ hanging = 0; -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ hanging = 1; -+ } else if (yaffs_has_null_parent(dev, parent)) { -+ hanging = 0; -+ } else { -+ /* -+ * Need to follow the parent chain to -+ * see if it is hanging. -+ */ -+ hanging = 0; -+ depth_limit = 100; -+ -+ while (parent != dev->root_dir && -+ parent->parent && -+ parent->parent->variant_type == -+ YAFFS_OBJECT_TYPE_DIRECTORY && -+ depth_limit > 0) { -+ parent = parent->parent; -+ depth_limit--; -+ } -+ if (parent != dev->root_dir) -+ hanging = 1; -+ } -+ if (hanging) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Hanging object %d moved to lost and found", -+ obj->obj_id); -+ yaffs_add_obj_to_dir(dev->lost_n_found, obj); -+ } -+ } -+ } -+} -+ -+/* -+ * Delete directory contents for cleaning up lost and found. -+ */ -+static void yaffs_del_dir_contents(struct yaffs_obj *dir) -+{ -+ struct yaffs_obj *obj; -+ struct list_head *lh; -+ struct list_head *n; -+ -+ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) -+ BUG(); -+ -+ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { -+ obj = list_entry(lh, struct yaffs_obj, siblings); -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) -+ yaffs_del_dir_contents(obj); -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Deleting lost_found object %d", -+ obj->obj_id); -+ yaffs_unlink_obj(obj); -+ } -+} -+ -+static void yaffs_empty_l_n_f(struct yaffs_dev *dev) -+{ -+ yaffs_del_dir_contents(dev->lost_n_found); -+} -+ -+ -+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, -+ const YCHAR *name) -+{ -+ int sum; -+ struct list_head *i; -+ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; -+ struct yaffs_obj *l; -+ -+ if (!name) -+ return NULL; -+ -+ if (!directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_find_by_name: null pointer directory" -+ ); -+ BUG(); -+ return NULL; -+ } -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "tragedy: yaffs_find_by_name: non-directory" -+ ); -+ BUG(); -+ } -+ -+ sum = yaffs_calc_name_sum(name); -+ -+ list_for_each(i, &directory->variant.dir_variant.children) { -+ l = list_entry(i, struct yaffs_obj, siblings); -+ -+ if (l->parent != directory) -+ BUG(); -+ -+ yaffs_check_obj_details_loaded(l); -+ -+ /* Special case for lost-n-found */ -+ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { -+ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) -+ return l; -+ } else if (l->sum == sum || l->hdr_chunk <= 0) { -+ /* LostnFound chunk called Objxxx -+ * Do a real check -+ */ -+ yaffs_get_obj_name(l, buffer, -+ YAFFS_MAX_NAME_LENGTH + 1); -+ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) -+ return l; -+ } -+ } -+ return NULL; -+} -+ -+/* GetEquivalentObject dereferences any hard links to get to the -+ * actual object. -+ */ -+ -+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) -+{ -+ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { -+ obj = obj->variant.hardlink_variant.equiv_obj; -+ yaffs_check_obj_details_loaded(obj); -+ } -+ return obj; -+} -+ -+/* -+ * A note or two on object names. -+ * * If the object name is missing, we then make one up in the form objnnn -+ * -+ * * ASCII names are stored in the object header's name field from byte zero -+ * * Unicode names are historically stored starting from byte zero. -+ * -+ * Then there are automatic Unicode names... -+ * The purpose of these is to save names in a way that can be read as -+ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII -+ * system to share files. -+ * -+ * These automatic unicode are stored slightly differently... -+ * - If the name can fit in the ASCII character space then they are saved as -+ * ascii names as per above. -+ * - If the name needs Unicode then the name is saved in Unicode -+ * starting at oh->name[1]. -+ -+ */ -+static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, -+ int buffer_size) -+{ -+ /* Create an object name if we could not find one. */ -+ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { -+ YCHAR local_name[20]; -+ YCHAR num_string[20]; -+ YCHAR *x = &num_string[19]; -+ unsigned v = obj->obj_id; -+ num_string[19] = 0; -+ while (v > 0) { -+ x--; -+ *x = '0' + (v % 10); -+ v /= 10; -+ } -+ /* make up a name */ -+ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); -+ strcat(local_name, x); -+ strncpy(name, local_name, buffer_size - 1); -+ } -+} -+ -+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) -+{ -+ memset(name, 0, buffer_size * sizeof(YCHAR)); -+ yaffs_check_obj_details_loaded(obj); -+ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { -+ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); -+ } else if (obj->short_name[0]) { -+ strcpy(name, obj->short_name); -+ } else if (obj->hdr_chunk > 0) { -+ int result; -+ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); -+ -+ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; -+ -+ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); -+ -+ if (obj->hdr_chunk > 0) { -+ result = yaffs_rd_chunk_tags_nand(obj->my_dev, -+ obj->hdr_chunk, -+ buffer, NULL); -+ } -+ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, -+ buffer_size); -+ -+ yaffs_release_temp_buffer(obj->my_dev, buffer); -+ } -+ -+ yaffs_fix_null_name(obj, name, buffer_size); -+ -+ return strnlen(name, YAFFS_MAX_NAME_LENGTH); -+} -+ -+loff_t yaffs_get_obj_length(struct yaffs_obj *obj) -+{ -+ /* Dereference any hard linking */ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ return obj->variant.file_variant.file_size; -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { -+ if (!obj->variant.symlink_variant.alias) -+ return 0; -+ return strnlen(obj->variant.symlink_variant.alias, -+ YAFFS_MAX_ALIAS_LENGTH); -+ } else { -+ /* Only a directory should drop through to here */ -+ return obj->my_dev->data_bytes_per_chunk; -+ } -+} -+ -+int yaffs_get_obj_link_count(struct yaffs_obj *obj) -+{ -+ int count = 0; -+ struct list_head *i; -+ -+ if (!obj->unlinked) -+ count++; /* the object itself */ -+ -+ list_for_each(i, &obj->hard_links) -+ count++; /* add the hard links; */ -+ -+ return count; -+} -+ -+int yaffs_get_obj_inode(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ return obj->obj_id; -+} -+ -+unsigned yaffs_get_obj_type(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ return DT_REG; -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ return DT_DIR; -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ return DT_LNK; -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ return DT_REG; -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ if (S_ISFIFO(obj->yst_mode)) -+ return DT_FIFO; -+ if (S_ISCHR(obj->yst_mode)) -+ return DT_CHR; -+ if (S_ISBLK(obj->yst_mode)) -+ return DT_BLK; -+ if (S_ISSOCK(obj->yst_mode)) -+ return DT_SOCK; -+ return DT_REG; -+ break; -+ default: -+ return DT_REG; -+ break; -+ } -+} -+ -+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) -+{ -+ obj = yaffs_get_equivalent_obj(obj); -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) -+ return yaffs_clone_str(obj->variant.symlink_variant.alias); -+ else -+ return yaffs_clone_str(_Y("")); -+} -+ -+/*--------------------------- Initialisation code -------------------------- */ -+ -+static int yaffs_check_dev_fns(struct yaffs_dev *dev) -+{ -+ struct yaffs_driver *drv = &dev->drv; -+ struct yaffs_tags_handler *tagger = &dev->tagger; -+ -+ /* Common functions, gotta have */ -+ if (!drv->drv_read_chunk_fn || -+ !drv->drv_write_chunk_fn || -+ !drv->drv_erase_fn) -+ return 0; -+ -+ if (dev->param.is_yaffs2 && -+ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) -+ return 0; -+ -+ /* Install the default tags marshalling functions if needed. */ -+ yaffs_tags_compat_install(dev); -+ yaffs_tags_marshall_install(dev); -+ -+ /* Check we now have the marshalling functions required. */ -+ if (!tagger->write_chunk_tags_fn || -+ !tagger->read_chunk_tags_fn || -+ !tagger->query_block_fn || -+ !tagger->mark_bad_fn) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs_create_initial_dir(struct yaffs_dev *dev) -+{ -+ /* Initialise the unlinked, deleted, root and lost+found directories */ -+ dev->lost_n_found = dev->root_dir = NULL; -+ dev->unlinked_dir = dev->del_dir = NULL; -+ dev->unlinked_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); -+ dev->del_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); -+ dev->root_dir = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, -+ YAFFS_ROOT_MODE | S_IFDIR); -+ dev->lost_n_found = -+ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, -+ YAFFS_LOSTNFOUND_MODE | S_IFDIR); -+ -+ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir -+ && dev->del_dir) { -+ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); -+ return YAFFS_OK; -+ } -+ return YAFFS_FAIL; -+} -+ -+/* Low level init. -+ * Typically only used by yaffs_guts_initialise, but also used by the -+ * Low level yaffs driver tests. -+ */ -+ -+int yaffs_guts_ll_init(struct yaffs_dev *dev) -+{ -+ -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); -+ -+ if (!dev) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Need a device" -+ ); -+ return YAFFS_FAIL; -+ } -+ -+ if (dev->ll_init) -+ return YAFFS_OK; -+ -+ dev->internal_start_block = dev->param.start_block; -+ dev->internal_end_block = dev->param.end_block; -+ dev->block_offset = 0; -+ dev->chunk_offset = 0; -+ dev->n_free_chunks = 0; -+ -+ dev->gc_block = 0; -+ -+ if (dev->param.start_block == 0) { -+ dev->internal_start_block = dev->param.start_block + 1; -+ dev->internal_end_block = dev->param.end_block + 1; -+ dev->block_offset = 1; -+ dev->chunk_offset = dev->param.chunks_per_block; -+ } -+ -+ /* Check geometry parameters. */ -+ -+ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && -+ dev->param.total_bytes_per_chunk < 1024) || -+ (!dev->param.is_yaffs2 && -+ dev->param.total_bytes_per_chunk < 512) || -+ (dev->param.inband_tags && !dev->param.is_yaffs2) || -+ dev->param.chunks_per_block < 2 || -+ dev->param.n_reserved_blocks < 2 || -+ dev->internal_start_block <= 0 || -+ dev->internal_end_block <= 0 || -+ dev->internal_end_block <= -+ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) -+ ) { -+ /* otherwise it is too small */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", -+ dev->param.total_bytes_per_chunk, -+ dev->param.is_yaffs2 ? "2" : "", -+ dev->param.inband_tags); -+ return YAFFS_FAIL; -+ } -+ -+ /* Sort out space for inband tags, if required */ -+ if (dev->param.inband_tags) -+ dev->data_bytes_per_chunk = -+ dev->param.total_bytes_per_chunk - -+ sizeof(struct yaffs_packed_tags2_tags_only); -+ else -+ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; -+ -+ /* Got the right mix of functions? */ -+ if (!yaffs_check_dev_fns(dev)) { -+ /* Function missing */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "device function(s) missing or wrong"); -+ -+ return YAFFS_FAIL; -+ } -+ -+ if (yaffs_init_nand(dev) != YAFFS_OK) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); -+ return YAFFS_FAIL; -+ } -+ -+ return YAFFS_OK; -+} -+ -+ -+int yaffs_guts_format_dev(struct yaffs_dev *dev) -+{ -+ int i; -+ enum yaffs_block_state state; -+ u32 dummy; -+ -+ if(yaffs_guts_ll_init(dev) != YAFFS_OK) -+ return YAFFS_FAIL; -+ -+ if(dev->is_mounted) -+ return YAFFS_FAIL; -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ yaffs_query_init_block_state(dev, i, &state, &dummy); -+ if (state != YAFFS_BLOCK_STATE_DEAD) -+ yaffs_erase_block(dev, i); -+ } -+ -+ return YAFFS_OK; -+} -+ -+ -+int yaffs_guts_initialise(struct yaffs_dev *dev) -+{ -+ int init_failed = 0; -+ unsigned x; -+ int bits; -+ -+ if(yaffs_guts_ll_init(dev) != YAFFS_OK) -+ return YAFFS_FAIL; -+ -+ if (dev->is_mounted) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); -+ return YAFFS_FAIL; -+ } -+ -+ dev->is_mounted = 1; -+ -+ /* OK now calculate a few things for the device */ -+ -+ /* -+ * Calculate all the chunk size manipulation numbers: -+ */ -+ x = dev->data_bytes_per_chunk; -+ /* We always use dev->chunk_shift and dev->chunk_div */ -+ dev->chunk_shift = calc_shifts(x); -+ x >>= dev->chunk_shift; -+ dev->chunk_div = x; -+ /* We only use chunk mask if chunk_div is 1 */ -+ dev->chunk_mask = (1 << dev->chunk_shift) - 1; -+ -+ /* -+ * Calculate chunk_grp_bits. -+ * We need to find the next power of 2 > than internal_end_block -+ */ -+ -+ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); -+ -+ bits = calc_shifts_ceiling(x); -+ -+ /* Set up tnode width if wide tnodes are enabled. */ -+ if (!dev->param.wide_tnodes_disabled) { -+ /* bits must be even so that we end up with 32-bit words */ -+ if (bits & 1) -+ bits++; -+ if (bits < 16) -+ dev->tnode_width = 16; -+ else -+ dev->tnode_width = bits; -+ } else { -+ dev->tnode_width = 16; -+ } -+ -+ dev->tnode_mask = (1 << dev->tnode_width) - 1; -+ -+ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), -+ * so if the bitwidth of the -+ * chunk range we're using is greater than 16 we need -+ * to figure out chunk shift and chunk_grp_size -+ */ -+ -+ if (bits <= dev->tnode_width) -+ dev->chunk_grp_bits = 0; -+ else -+ dev->chunk_grp_bits = bits - dev->tnode_width; -+ -+ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; -+ if (dev->tnode_size < sizeof(struct yaffs_tnode)) -+ dev->tnode_size = sizeof(struct yaffs_tnode); -+ -+ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; -+ -+ if (dev->param.chunks_per_block < dev->chunk_grp_size) { -+ /* We have a problem because the soft delete won't work if -+ * the chunk group size > chunks per block. -+ * This can be remedied by using larger "virtual blocks". -+ */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); -+ -+ return YAFFS_FAIL; -+ } -+ -+ /* Finished verifying the device, continue with initialisation */ -+ -+ /* More device initialisation */ -+ dev->all_gcs = 0; -+ dev->passive_gc_count = 0; -+ dev->oldest_dirty_gc_count = 0; -+ dev->bg_gcs = 0; -+ dev->gc_block_finder = 0; -+ dev->buffered_block = -1; -+ dev->doing_buffered_block_rewrite = 0; -+ dev->n_deleted_files = 0; -+ dev->n_bg_deletions = 0; -+ dev->n_unlinked_files = 0; -+ dev->n_ecc_fixed = 0; -+ dev->n_ecc_unfixed = 0; -+ dev->n_tags_ecc_fixed = 0; -+ dev->n_tags_ecc_unfixed = 0; -+ dev->n_erase_failures = 0; -+ dev->n_erased_blocks = 0; -+ dev->gc_disable = 0; -+ dev->has_pending_prioritised_gc = 1; -+ /* Assume the worst for now, will get fixed on first GC */ -+ INIT_LIST_HEAD(&dev->dirty_dirs); -+ dev->oldest_dirty_seq = 0; -+ dev->oldest_dirty_block = 0; -+ -+ /* Initialise temporary buffers and caches. */ -+ if (!yaffs_init_tmp_buffers(dev)) -+ init_failed = 1; -+ -+ dev->cache = NULL; -+ dev->gc_cleanup_list = NULL; -+ -+ if (!init_failed && dev->param.n_caches > 0) { -+ int i; -+ void *buf; -+ int cache_bytes = -+ dev->param.n_caches * sizeof(struct yaffs_cache); -+ -+ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) -+ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; -+ -+ dev->cache = kmalloc(cache_bytes, GFP_NOFS); -+ -+ buf = (u8 *) dev->cache; -+ -+ if (dev->cache) -+ memset(dev->cache, 0, cache_bytes); -+ -+ for (i = 0; i < dev->param.n_caches && buf; i++) { -+ dev->cache[i].object = NULL; -+ dev->cache[i].last_use = 0; -+ dev->cache[i].dirty = 0; -+ dev->cache[i].data = buf = -+ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); -+ } -+ if (!buf) -+ init_failed = 1; -+ -+ dev->cache_last_use = 0; -+ } -+ -+ dev->cache_hits = 0; -+ -+ if (!init_failed) { -+ dev->gc_cleanup_list = -+ kmalloc(dev->param.chunks_per_block * sizeof(u32), -+ GFP_NOFS); -+ if (!dev->gc_cleanup_list) -+ init_failed = 1; -+ } -+ -+ if (dev->param.is_yaffs2) -+ dev->param.use_header_file_size = 1; -+ -+ if (!init_failed && !yaffs_init_blocks(dev)) -+ init_failed = 1; -+ -+ yaffs_init_tnodes_and_objs(dev); -+ -+ if (!init_failed && !yaffs_create_initial_dir(dev)) -+ init_failed = 1; -+ -+ if (!init_failed && dev->param.is_yaffs2 && -+ !dev->param.disable_summary && -+ !yaffs_summary_init(dev)) -+ init_failed = 1; -+ -+ if (!init_failed) { -+ /* Now scan the flash. */ -+ if (dev->param.is_yaffs2) { -+ if (yaffs2_checkpt_restore(dev)) { -+ yaffs_check_obj_details_loaded(dev->root_dir); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | -+ YAFFS_TRACE_MOUNT, -+ "yaffs: restored from checkpoint" -+ ); -+ } else { -+ -+ /* Clean up the mess caused by an aborted -+ * checkpoint load then scan backwards. -+ */ -+ yaffs_deinit_blocks(dev); -+ -+ yaffs_deinit_tnodes_and_objs(dev); -+ -+ dev->n_erased_blocks = 0; -+ dev->n_free_chunks = 0; -+ dev->alloc_block = -1; -+ dev->alloc_page = -1; -+ dev->n_deleted_files = 0; -+ dev->n_unlinked_files = 0; -+ dev->n_bg_deletions = 0; -+ -+ if (!init_failed && !yaffs_init_blocks(dev)) -+ init_failed = 1; -+ -+ yaffs_init_tnodes_and_objs(dev); -+ -+ if (!init_failed -+ && !yaffs_create_initial_dir(dev)) -+ init_failed = 1; -+ -+ if (!init_failed && !yaffs2_scan_backwards(dev)) -+ init_failed = 1; -+ } -+ } else if (!yaffs1_scan(dev)) { -+ init_failed = 1; -+ } -+ -+ yaffs_strip_deleted_objs(dev); -+ yaffs_fix_hanging_objs(dev); -+ if (dev->param.empty_lost_n_found) -+ yaffs_empty_l_n_f(dev); -+ } -+ -+ if (init_failed) { -+ /* Clean up the mess */ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: yaffs_guts_initialise() aborted."); -+ -+ yaffs_deinitialise(dev); -+ return YAFFS_FAIL; -+ } -+ -+ /* Zero out stats */ -+ dev->n_page_reads = 0; -+ dev->n_page_writes = 0; -+ dev->n_erasures = 0; -+ dev->n_gc_copies = 0; -+ dev->n_retried_writes = 0; -+ -+ dev->n_retired_blocks = 0; -+ -+ yaffs_verify_free_chunks(dev); -+ yaffs_verify_blocks(dev); -+ -+ /* Clean up any aborted checkpoint data */ -+ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) -+ yaffs2_checkpt_invalidate(dev); -+ -+ yaffs_trace(YAFFS_TRACE_TRACING, -+ "yaffs: yaffs_guts_initialise() done."); -+ return YAFFS_OK; -+} -+ -+void yaffs_deinitialise(struct yaffs_dev *dev) -+{ -+ if (dev->is_mounted) { -+ int i; -+ -+ yaffs_deinit_blocks(dev); -+ yaffs_deinit_tnodes_and_objs(dev); -+ yaffs_summary_deinit(dev); -+ -+ if (dev->param.n_caches > 0 && dev->cache) { -+ -+ for (i = 0; i < dev->param.n_caches; i++) { -+ kfree(dev->cache[i].data); -+ dev->cache[i].data = NULL; -+ } -+ -+ kfree(dev->cache); -+ dev->cache = NULL; -+ } -+ -+ kfree(dev->gc_cleanup_list); -+ -+ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) -+ kfree(dev->temp_buffer[i].buffer); -+ -+ dev->is_mounted = 0; -+ -+ yaffs_deinit_nand(dev); -+ } -+} -+ -+int yaffs_count_free_chunks(struct yaffs_dev *dev) -+{ -+ int n_free = 0; -+ int b; -+ struct yaffs_block_info *blk; -+ -+ blk = dev->block_info; -+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { -+ switch (blk->block_state) { -+ case YAFFS_BLOCK_STATE_EMPTY: -+ case YAFFS_BLOCK_STATE_ALLOCATING: -+ case YAFFS_BLOCK_STATE_COLLECTING: -+ case YAFFS_BLOCK_STATE_FULL: -+ n_free += -+ (dev->param.chunks_per_block - blk->pages_in_use + -+ blk->soft_del_pages); -+ break; -+ default: -+ break; -+ } -+ blk++; -+ } -+ return n_free; -+} -+ -+int yaffs_get_n_free_chunks(struct yaffs_dev *dev) -+{ -+ /* This is what we report to the outside world */ -+ int n_free; -+ int n_dirty_caches; -+ int blocks_for_checkpt; -+ int i; -+ -+ n_free = dev->n_free_chunks; -+ n_free += dev->n_deleted_files; -+ -+ /* Now count and subtract the number of dirty chunks in the cache. */ -+ -+ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { -+ if (dev->cache[i].dirty) -+ n_dirty_caches++; -+ } -+ -+ n_free -= n_dirty_caches; -+ -+ n_free -= -+ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); -+ -+ /* Now figure checkpoint space and report that... */ -+ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); -+ -+ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); -+ -+ if (n_free < 0) -+ n_free = 0; -+ -+ return n_free; -+} -+ -+ -+ -+/* -+ * Marshalling functions to get loff_t file sizes into and out of -+ * object headers. -+ */ -+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) -+{ -+ oh->file_size_low = (fsize & 0xFFFFFFFF); -+ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); -+} -+ -+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) -+{ -+ loff_t retval; -+ -+ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) -+ retval = (((loff_t) oh->file_size_high) << 32) | -+ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); -+ else -+ retval = (loff_t) oh->file_size_low; -+ -+ return retval; -+} -+ -+ -+void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) -+{ -+ int i; -+ struct yaffs_block_info *bi; -+ int s; -+ -+ for(i = 0; i < 10; i++) -+ bs[i] = 0; -+ -+ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ bi = yaffs_get_block_info(dev, i); -+ s = bi->block_state; -+ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) -+ bs[0]++; -+ else -+ bs[s]++; -+ } -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_guts.h linux-3.4.90/fs/yaffs2/yaffs_guts.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_guts.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,1007 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_GUTS_H__ -+#define __YAFFS_GUTS_H__ -+ -+#include "yportenv.h" -+ -+#define YAFFS_OK 1 -+#define YAFFS_FAIL 0 -+ -+/* Give us a Y=0x59, -+ * Give us an A=0x41, -+ * Give us an FF=0xff -+ * Give us an S=0x53 -+ * And what have we got... -+ */ -+#define YAFFS_MAGIC 0x5941ff53 -+ -+/* -+ * Tnodes form a tree with the tnodes in "levels" -+ * Levels greater than 0 hold 8 slots which point to other tnodes. -+ * Those at level 0 hold 16 slots which point to chunks in NAND. -+ * -+ * A maximum level of 8 thust supports files of size up to: -+ * -+ * 2^(3*MAX_LEVEL+4) -+ * -+ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives -+ * a maximum file size of around 512Gbytees with 2k chunks. -+ */ -+#define YAFFS_NTNODES_LEVEL0 16 -+#define YAFFS_TNODES_LEVEL0_BITS 4 -+#define YAFFS_TNODES_LEVEL0_MASK 0xf -+ -+#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) -+#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) -+#define YAFFS_TNODES_INTERNAL_MASK 0x7 -+#define YAFFS_TNODES_MAX_LEVEL 8 -+#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ -+ YAFFS_TNODES_INTERNAL_BITS * \ -+ YAFFS_TNODES_MAX_LEVEL) -+#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) -+ -+#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff -+ -+/* Constants for YAFFS1 mode */ -+#define YAFFS_BYTES_PER_SPARE 16 -+#define YAFFS_BYTES_PER_CHUNK 512 -+#define YAFFS_CHUNK_SIZE_SHIFT 9 -+#define YAFFS_CHUNKS_PER_BLOCK 32 -+#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) -+ -+#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 -+#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 -+ -+ -+ -+#define YAFFS_ALLOCATION_NOBJECTS 100 -+#define YAFFS_ALLOCATION_NTNODES 100 -+#define YAFFS_ALLOCATION_NLINKS 100 -+ -+#define YAFFS_NOBJECT_BUCKETS 256 -+ -+#define YAFFS_OBJECT_SPACE 0x40000 -+#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) -+ -+/* Binary data version stamps */ -+#define YAFFS_SUMMARY_VERSION 1 -+#define YAFFS_CHECKPOINT_VERSION 7 -+ -+#ifdef CONFIG_YAFFS_UNICODE -+#define YAFFS_MAX_NAME_LENGTH 127 -+#define YAFFS_MAX_ALIAS_LENGTH 79 -+#else -+#define YAFFS_MAX_NAME_LENGTH 255 -+#define YAFFS_MAX_ALIAS_LENGTH 159 -+#endif -+ -+#define YAFFS_SHORT_NAME_LENGTH 15 -+ -+/* Some special object ids for pseudo objects */ -+#define YAFFS_OBJECTID_ROOT 1 -+#define YAFFS_OBJECTID_LOSTNFOUND 2 -+#define YAFFS_OBJECTID_UNLINKED 3 -+#define YAFFS_OBJECTID_DELETED 4 -+ -+/* Fake object Id for summary data */ -+#define YAFFS_OBJECTID_SUMMARY 0x10 -+ -+/* Pseudo object ids for checkpointing */ -+#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 -+#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 -+ -+#define YAFFS_MAX_SHORT_OP_CACHES 20 -+ -+#define YAFFS_N_TEMP_BUFFERS 6 -+ -+/* We limit the number attempts at sucessfully saving a chunk of data. -+ * Small-page devices have 32 pages per block; large-page devices have 64. -+ * Default to something in the order of 5 to 10 blocks worth of chunks. -+ */ -+#define YAFFS_WR_ATTEMPTS (5*64) -+ -+/* Sequence numbers are used in YAFFS2 to determine block allocation order. -+ * The range is limited slightly to help distinguish bad numbers from good. -+ * This also allows us to perhaps in the future use special numbers for -+ * special purposes. -+ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, -+ * and is a larger number than the lifetime of a 2GB device. -+ */ -+#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 -+#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 -+ -+/* Special sequence number for bad block that failed to be marked bad */ -+#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 -+ -+/* ChunkCache is used for short read/write operations.*/ -+struct yaffs_cache { -+ struct yaffs_obj *object; -+ int chunk_id; -+ int last_use; -+ int dirty; -+ int n_bytes; /* Only valid if the cache is dirty */ -+ int locked; /* Can't push out or flush while locked. */ -+ u8 *data; -+}; -+ -+/* yaffs1 tags structures in RAM -+ * NB This uses bitfield. Bitfields should not straddle a u32 boundary -+ * otherwise the structure size will get blown out. -+ */ -+ -+struct yaffs_tags { -+ u32 chunk_id:20; -+ u32 serial_number:2; -+ u32 n_bytes_lsb:10; -+ u32 obj_id:18; -+ u32 ecc:12; -+ u32 n_bytes_msb:2; -+}; -+ -+union yaffs_tags_union { -+ struct yaffs_tags as_tags; -+ u8 as_bytes[8]; -+}; -+ -+ -+/* Stuff used for extended tags in YAFFS2 */ -+ -+enum yaffs_ecc_result { -+ YAFFS_ECC_RESULT_UNKNOWN, -+ YAFFS_ECC_RESULT_NO_ERROR, -+ YAFFS_ECC_RESULT_FIXED, -+ YAFFS_ECC_RESULT_UNFIXED -+}; -+ -+enum yaffs_obj_type { -+ YAFFS_OBJECT_TYPE_UNKNOWN, -+ YAFFS_OBJECT_TYPE_FILE, -+ YAFFS_OBJECT_TYPE_SYMLINK, -+ YAFFS_OBJECT_TYPE_DIRECTORY, -+ YAFFS_OBJECT_TYPE_HARDLINK, -+ YAFFS_OBJECT_TYPE_SPECIAL -+}; -+ -+#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL -+ -+struct yaffs_ext_tags { -+ unsigned chunk_used; /* Status of the chunk: used or unused */ -+ unsigned obj_id; /* If 0 this is not used */ -+ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ -+ unsigned n_bytes; /* Only valid for data chunks */ -+ -+ /* The following stuff only has meaning when we read */ -+ enum yaffs_ecc_result ecc_result; -+ unsigned block_bad; -+ -+ /* YAFFS 1 stuff */ -+ unsigned is_deleted; /* The chunk is marked deleted */ -+ unsigned serial_number; /* Yaffs1 2-bit serial number */ -+ -+ /* YAFFS2 stuff */ -+ unsigned seq_number; /* The sequence number of this block */ -+ -+ /* Extra info if this is an object header (YAFFS2 only) */ -+ -+ unsigned extra_available; /* Extra info available if not zero */ -+ unsigned extra_parent_id; /* The parent object */ -+ unsigned extra_is_shrink; /* Is it a shrink header? */ -+ unsigned extra_shadows; /* Does this shadow another object? */ -+ -+ enum yaffs_obj_type extra_obj_type; /* What object type? */ -+ -+ loff_t extra_file_size; /* Length if it is a file */ -+ unsigned extra_equiv_id; /* Equivalent object for a hard link */ -+}; -+ -+/* Spare structure for YAFFS1 */ -+struct yaffs_spare { -+ u8 tb0; -+ u8 tb1; -+ u8 tb2; -+ u8 tb3; -+ u8 page_status; /* set to 0 to delete the chunk */ -+ u8 block_status; -+ u8 tb4; -+ u8 tb5; -+ u8 ecc1[3]; -+ u8 tb6; -+ u8 tb7; -+ u8 ecc2[3]; -+}; -+ -+/*Special structure for passing through to mtd */ -+struct yaffs_nand_spare { -+ struct yaffs_spare spare; -+ int eccres1; -+ int eccres2; -+}; -+ -+/* Block data in RAM */ -+ -+enum yaffs_block_state { -+ YAFFS_BLOCK_STATE_UNKNOWN = 0, -+ -+ YAFFS_BLOCK_STATE_SCANNING, -+ /* Being scanned */ -+ -+ YAFFS_BLOCK_STATE_NEEDS_SCAN, -+ /* The block might have something on it (ie it is allocating or full, -+ * perhaps empty) but it needs to be scanned to determine its true -+ * state. -+ * This state is only valid during scanning. -+ * NB We tolerate empty because the pre-scanner might be incapable of -+ * deciding -+ * However, if this state is returned on a YAFFS2 device, -+ * then we expect a sequence number -+ */ -+ -+ YAFFS_BLOCK_STATE_EMPTY, -+ /* This block is empty */ -+ -+ YAFFS_BLOCK_STATE_ALLOCATING, -+ /* This block is partially allocated. -+ * At least one page holds valid data. -+ * This is the one currently being used for page -+ * allocation. Should never be more than one of these. -+ * If a block is only partially allocated at mount it is treated as -+ * full. -+ */ -+ -+ YAFFS_BLOCK_STATE_FULL, -+ /* All the pages in this block have been allocated. -+ * If a block was only partially allocated when mounted we treat -+ * it as fully allocated. -+ */ -+ -+ YAFFS_BLOCK_STATE_DIRTY, -+ /* The block was full and now all chunks have been deleted. -+ * Erase me, reuse me. -+ */ -+ -+ YAFFS_BLOCK_STATE_CHECKPOINT, -+ /* This block is assigned to holding checkpoint data. */ -+ -+ YAFFS_BLOCK_STATE_COLLECTING, -+ /* This block is being garbage collected */ -+ -+ YAFFS_BLOCK_STATE_DEAD -+ /* This block has failed and is not in use */ -+}; -+ -+#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) -+ -+struct yaffs_block_info { -+ -+ s32 soft_del_pages:10; /* number of soft deleted pages */ -+ s32 pages_in_use:10; /* number of pages in use */ -+ u32 block_state:4; /* One of the above block states. */ -+ /* NB use unsigned because enum is sometimes -+ * an int */ -+ u32 needs_retiring:1; /* Data has failed on this block, */ -+ /*need to get valid data off and retire*/ -+ u32 skip_erased_check:1;/* Skip the erased check on this block */ -+ u32 gc_prioritise:1; /* An ECC check or blank check has failed. -+ Block should be prioritised for GC */ -+ u32 chunk_error_strikes:3; /* How many times we've had ecc etc -+ failures on this block and tried to reuse it */ -+ u32 has_summary:1; /* The block has a summary */ -+ -+ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ -+ u32 seq_number; /* block sequence number for yaffs2 */ -+ -+}; -+ -+/* -------------------------- Object structure -------------------------------*/ -+/* This is the object structure as stored on NAND */ -+ -+struct yaffs_obj_hdr { -+ enum yaffs_obj_type type; -+ -+ /* Apply to everything */ -+ int parent_obj_id; -+ u16 sum_no_longer_used; /* checksum of name. No longer used */ -+ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ /* The following apply to all object types except for hard links */ -+ u32 yst_mode; /* protection */ -+ -+ u32 yst_uid; -+ u32 yst_gid; -+ u32 yst_atime; -+ u32 yst_mtime; -+ u32 yst_ctime; -+ -+ /* File size applies to files only */ -+ u32 file_size_low; -+ -+ /* Equivalent object id applies to hard links only. */ -+ int equiv_id; -+ -+ /* Alias is for symlinks only. */ -+ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; -+ -+ u32 yst_rdev; /* stuff for block and char devices (major/min) */ -+ -+ u32 win_ctime[2]; -+ u32 win_atime[2]; -+ u32 win_mtime[2]; -+ -+ u32 inband_shadowed_obj_id; -+ u32 inband_is_shrink; -+ -+ u32 file_size_high; -+ u32 reserved[1]; -+ int shadows_obj; /* This object header shadows the -+ specified object if > 0 */ -+ -+ /* is_shrink applies to object headers written when wemake a hole. */ -+ u32 is_shrink; -+ -+}; -+ -+/*--------------------------- Tnode -------------------------- */ -+ -+struct yaffs_tnode { -+ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; -+}; -+ -+/*------------------------ Object -----------------------------*/ -+/* An object can be one of: -+ * - a directory (no data, has children links -+ * - a regular file (data.... not prunes :->). -+ * - a symlink [symbolic link] (the alias). -+ * - a hard link -+ */ -+ -+struct yaffs_file_var { -+ loff_t file_size; -+ loff_t scanned_size; -+ loff_t shrink_size; -+ int top_level; -+ struct yaffs_tnode *top; -+}; -+ -+struct yaffs_dir_var { -+ struct list_head children; /* list of child links */ -+ struct list_head dirty; /* Entry for list of dirty directories */ -+}; -+ -+struct yaffs_symlink_var { -+ YCHAR *alias; -+}; -+ -+struct yaffs_hardlink_var { -+ struct yaffs_obj *equiv_obj; -+ u32 equiv_id; -+}; -+ -+union yaffs_obj_var { -+ struct yaffs_file_var file_variant; -+ struct yaffs_dir_var dir_variant; -+ struct yaffs_symlink_var symlink_variant; -+ struct yaffs_hardlink_var hardlink_variant; -+}; -+ -+struct yaffs_obj { -+ u8 deleted:1; /* This should only apply to unlinked files. */ -+ u8 soft_del:1; /* it has also been soft deleted */ -+ u8 unlinked:1; /* An unlinked file.*/ -+ u8 fake:1; /* A fake object has no presence on NAND. */ -+ u8 rename_allowed:1; /* Some objects cannot be renamed. */ -+ u8 unlink_allowed:1; -+ u8 dirty:1; /* the object needs to be written to flash */ -+ u8 valid:1; /* When the file system is being loaded up, this -+ * object might be created before the data -+ * is available -+ * ie. file data chunks encountered before -+ * the header. -+ */ -+ u8 lazy_loaded:1; /* This object has been lazy loaded and -+ * is missing some detail */ -+ -+ u8 defered_free:1; /* Object is removed from NAND, but is -+ * still in the inode cache. -+ * Free of object is defered. -+ * until the inode is released. -+ */ -+ u8 being_created:1; /* This object is still being created -+ * so skip some verification checks. */ -+ u8 is_shadowed:1; /* This object is shadowed on the way -+ * to being renamed. */ -+ -+ u8 xattr_known:1; /* We know if this has object has xattribs -+ * or not. */ -+ u8 has_xattr:1; /* This object has xattribs. -+ * Only valid if xattr_known. */ -+ -+ u8 serial; /* serial number of chunk in NAND.*/ -+ u16 sum; /* sum of the name to speed searching */ -+ -+ struct yaffs_dev *my_dev; /* The device I'm on */ -+ -+ struct list_head hash_link; /* list of objects in hash bucket */ -+ -+ struct list_head hard_links; /* hard linked object chain*/ -+ -+ /* directory structure stuff */ -+ /* also used for linking up the free list */ -+ struct yaffs_obj *parent; -+ struct list_head siblings; -+ -+ /* Where's my object header in NAND? */ -+ int hdr_chunk; -+ -+ int n_data_chunks; /* Number of data chunks for this file. */ -+ -+ u32 obj_id; /* the object id value */ -+ -+ u32 yst_mode; -+ -+ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; -+ -+#ifdef CONFIG_YAFFS_WINCE -+ u32 win_ctime[2]; -+ u32 win_mtime[2]; -+ u32 win_atime[2]; -+#else -+ u32 yst_uid; -+ u32 yst_gid; -+ u32 yst_atime; -+ u32 yst_mtime; -+ u32 yst_ctime; -+#endif -+ -+ u32 yst_rdev; -+ -+ void *my_inode; -+ -+ enum yaffs_obj_type variant_type; -+ -+ union yaffs_obj_var variant; -+ -+}; -+ -+struct yaffs_obj_bucket { -+ struct list_head list; -+ int count; -+}; -+ -+/* yaffs_checkpt_obj holds the definition of an object as dumped -+ * by checkpointing. -+ */ -+ -+struct yaffs_checkpt_obj { -+ int struct_type; -+ u32 obj_id; -+ u32 parent_id; -+ int hdr_chunk; -+ enum yaffs_obj_type variant_type:3; -+ u8 deleted:1; -+ u8 soft_del:1; -+ u8 unlinked:1; -+ u8 fake:1; -+ u8 rename_allowed:1; -+ u8 unlink_allowed:1; -+ u8 serial; -+ int n_data_chunks; -+ loff_t size_or_equiv_obj; -+}; -+ -+/*--------------------- Temporary buffers ---------------- -+ * -+ * These are chunk-sized working buffers. Each device has a few. -+ */ -+ -+struct yaffs_buffer { -+ u8 *buffer; -+ int in_use; -+}; -+ -+/*----------------- Device ---------------------------------*/ -+ -+struct yaffs_param { -+ const YCHAR *name; -+ -+ /* -+ * Entry parameters set up way early. Yaffs sets up the rest. -+ * The structure should be zeroed out before use so that unused -+ * and default values are zero. -+ */ -+ -+ int inband_tags; /* Use unband tags */ -+ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to -+ be a power of 2 */ -+ int chunks_per_block; /* does not need to be a power of 2 */ -+ int spare_bytes_per_chunk; /* spare area size */ -+ int start_block; /* Start block we're allowed to use */ -+ int end_block; /* End block we're allowed to use */ -+ int n_reserved_blocks; /* Tuneable so that we can reduce -+ * reserved blocks on NOR and RAM. */ -+ -+ int n_caches; /* If <= 0, then short op caching is disabled, -+ * else the number of short op caches. -+ */ -+ int cache_bypass_aligned; /* If non-zero then bypass the cache for -+ * aligned writes. -+ */ -+ -+ int use_nand_ecc; /* Flag to decide whether or not to use -+ * NAND driver ECC on data (yaffs1) */ -+ int tags_9bytes; /* Use 9 byte tags */ -+ int no_tags_ecc; /* Flag to decide whether or not to do ECC -+ * on packed tags (yaffs2) */ -+ -+ int is_yaffs2; /* Use yaffs2 mode on this device */ -+ -+ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ -+ -+ int refresh_period; /* How often to check for a block refresh */ -+ -+ /* Checkpoint control. Can be set before or after initialisation */ -+ u8 skip_checkpt_rd; -+ u8 skip_checkpt_wr; -+ -+ int enable_xattr; /* Enable xattribs */ -+ -+ int max_objects; /* -+ * Set to limit the number of objects created. -+ * 0 = no limit. -+ */ -+ -+ /* The remove_obj_fn function must be supplied by OS flavours that -+ * need it. -+ * yaffs direct uses it to implement the faster readdir. -+ * Linux uses it to protect the directory during unlocking. -+ */ -+ void (*remove_obj_fn) (struct yaffs_obj *obj); -+ -+ /* Callback to mark the superblock dirty */ -+ void (*sb_dirty_fn) (struct yaffs_dev *dev); -+ -+ /* Callback to control garbage collection. */ -+ unsigned (*gc_control_fn) (struct yaffs_dev *dev); -+ -+ /* Debug control flags. Don't use unless you know what you're doing */ -+ int use_header_file_size; /* Flag to determine if we should use -+ * file sizes from the header */ -+ int disable_lazy_load; /* Disable lazy loading on this device */ -+ int wide_tnodes_disabled; /* Set to disable wide tnodes */ -+ int disable_soft_del; /* yaffs 1 only: Set to disable the use of -+ * softdeletion. */ -+ -+ int defered_dir_update; /* Set to defer directory updates */ -+ -+#ifdef CONFIG_YAFFS_AUTO_UNICODE -+ int auto_unicode; -+#endif -+ int always_check_erased; /* Force chunk erased check always on */ -+ -+ int disable_summary; -+ int disable_bad_block_marking; -+ -+}; -+ -+struct yaffs_driver { -+ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, int data_len, -+ const u8 *oob, int oob_len); -+ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, -+ u8 *data, int data_len, -+ u8 *oob, int oob_len, -+ enum yaffs_ecc_result *ecc_result); -+ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); -+ int (*drv_initialise_fn) (struct yaffs_dev *dev); -+ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); -+}; -+ -+struct yaffs_tags_handler { -+ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ const struct yaffs_ext_tags *tags); -+ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, -+ int nand_chunk, u8 *data, -+ struct yaffs_ext_tags *tags); -+ -+ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number); -+ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); -+}; -+ -+struct yaffs_dev { -+ struct yaffs_param param; -+ struct yaffs_driver drv; -+ struct yaffs_tags_handler tagger; -+ -+ /* Context storage. Holds extra OS specific data for this device */ -+ -+ void *os_context; -+ void *driver_context; -+ -+ struct list_head dev_list; -+ -+ int ll_init; -+ /* Runtime parameters. Set up by YAFFS. */ -+ int data_bytes_per_chunk; -+ -+ /* Non-wide tnode stuff */ -+ u16 chunk_grp_bits; /* Number of bits that need to be resolved if -+ * the tnodes are not wide enough. -+ */ -+ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ -+ -+ /* Stuff to support wide tnodes */ -+ u32 tnode_width; -+ u32 tnode_mask; -+ u32 tnode_size; -+ -+ /* Stuff for figuring out file offset to chunk conversions */ -+ u32 chunk_shift; /* Shift value */ -+ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ -+ u32 chunk_mask; /* Mask to use for power-of-2 case */ -+ -+ int is_mounted; -+ int read_only; -+ int is_checkpointed; -+ -+ /* Stuff to support block offsetting to support start block zero */ -+ int internal_start_block; -+ int internal_end_block; -+ int block_offset; -+ int chunk_offset; -+ -+ /* Runtime checkpointing stuff */ -+ int checkpt_page_seq; /* running sequence number of checkpt pages */ -+ int checkpt_byte_count; -+ int checkpt_byte_offs; -+ u8 *checkpt_buffer; -+ int checkpt_open_write; -+ int blocks_in_checkpt; -+ int checkpt_cur_chunk; -+ int checkpt_cur_block; -+ int checkpt_next_block; -+ int *checkpt_block_list; -+ int checkpt_max_blocks; -+ u32 checkpt_sum; -+ u32 checkpt_xor; -+ -+ int checkpoint_blocks_required; /* Number of blocks needed to store -+ * current checkpoint set */ -+ -+ /* Block Info */ -+ struct yaffs_block_info *block_info; -+ u8 *chunk_bits; /* bitmap of chunks in use */ -+ u8 block_info_alt:1; /* allocated using alternative alloc */ -+ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ -+ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. -+ * Must be consistent with chunks_per_block. -+ */ -+ -+ int n_erased_blocks; -+ int alloc_block; /* Current block being allocated off */ -+ u32 alloc_page; -+ int alloc_block_finder; /* Used to search for next allocation block */ -+ -+ /* Object and Tnode memory management */ -+ void *allocator; -+ int n_obj; -+ int n_tnodes; -+ -+ int n_hardlinks; -+ -+ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; -+ u32 bucket_finder; -+ -+ int n_free_chunks; -+ -+ /* Garbage collection control */ -+ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ -+ u32 n_clean_ups; -+ -+ unsigned has_pending_prioritised_gc; /* We think this device might -+ have pending prioritised gcs */ -+ unsigned gc_disable; -+ unsigned gc_block_finder; -+ unsigned gc_dirtiest; -+ unsigned gc_pages_in_use; -+ unsigned gc_not_done; -+ unsigned gc_block; -+ unsigned gc_chunk; -+ unsigned gc_skip; -+ struct yaffs_summary_tags *gc_sum_tags; -+ -+ /* Special directories */ -+ struct yaffs_obj *root_dir; -+ struct yaffs_obj *lost_n_found; -+ -+ int buffered_block; /* Which block is buffered here? */ -+ int doing_buffered_block_rewrite; -+ -+ struct yaffs_cache *cache; -+ int cache_last_use; -+ -+ /* Stuff for background deletion and unlinked files. */ -+ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted -+ files live. */ -+ struct yaffs_obj *del_dir; /* Directory where deleted objects are -+ sent to disappear. */ -+ struct yaffs_obj *unlinked_deletion; /* Current file being -+ background deleted. */ -+ int n_deleted_files; /* Count of files awaiting deletion; */ -+ int n_unlinked_files; /* Count of unlinked files. */ -+ int n_bg_deletions; /* Count of background deletions. */ -+ -+ /* Temporary buffer management */ -+ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; -+ int max_temp; -+ int temp_in_use; -+ int unmanaged_buffer_allocs; -+ int unmanaged_buffer_deallocs; -+ -+ /* yaffs2 runtime stuff */ -+ unsigned seq_number; /* Sequence number of currently -+ allocating block */ -+ unsigned oldest_dirty_seq; -+ unsigned oldest_dirty_block; -+ -+ /* Block refreshing */ -+ int refresh_skip; /* A skip down counter. -+ * Refresh happens when this gets to zero. */ -+ -+ /* Dirty directory handling */ -+ struct list_head dirty_dirs; /* List of dirty directories */ -+ -+ /* Summary */ -+ int chunks_per_summary; -+ struct yaffs_summary_tags *sum_tags; -+ -+ /* Statistics */ -+ u32 n_page_writes; -+ u32 n_page_reads; -+ u32 n_erasures; -+ u32 n_bad_queries; -+ u32 n_bad_markings; -+ u32 n_erase_failures; -+ u32 n_gc_copies; -+ u32 all_gcs; -+ u32 passive_gc_count; -+ u32 oldest_dirty_gc_count; -+ u32 n_gc_blocks; -+ u32 bg_gcs; -+ u32 n_retried_writes; -+ u32 n_retired_blocks; -+ u32 n_ecc_fixed; -+ u32 n_ecc_unfixed; -+ u32 n_tags_ecc_fixed; -+ u32 n_tags_ecc_unfixed; -+ u32 n_deletions; -+ u32 n_unmarked_deletions; -+ u32 refresh_count; -+ u32 cache_hits; -+ u32 tags_used; -+ u32 summary_used; -+ -+}; -+ -+/* The CheckpointDevice structure holds the device information that changes -+ *at runtime and must be preserved over unmount/mount cycles. -+ */ -+struct yaffs_checkpt_dev { -+ int struct_type; -+ int n_erased_blocks; -+ int alloc_block; /* Current block being allocated off */ -+ u32 alloc_page; -+ int n_free_chunks; -+ -+ int n_deleted_files; /* Count of files awaiting deletion; */ -+ int n_unlinked_files; /* Count of unlinked files. */ -+ int n_bg_deletions; /* Count of background deletions. */ -+ -+ /* yaffs2 runtime stuff */ -+ unsigned seq_number; /* Sequence number of currently -+ * allocating block */ -+ -+}; -+ -+struct yaffs_checkpt_validity { -+ int struct_type; -+ u32 magic; -+ u32 version; -+ u32 head; -+}; -+ -+struct yaffs_shadow_fixer { -+ int obj_id; -+ int shadowed_id; -+ struct yaffs_shadow_fixer *next; -+}; -+ -+/* Structure for doing xattr modifications */ -+struct yaffs_xattr_mod { -+ int set; /* If 0 then this is a deletion */ -+ const YCHAR *name; -+ const void *data; -+ int size; -+ int flags; -+ int result; -+}; -+ -+/*----------------------- YAFFS Functions -----------------------*/ -+ -+int yaffs_guts_initialise(struct yaffs_dev *dev); -+void yaffs_deinitialise(struct yaffs_dev *dev); -+ -+int yaffs_get_n_free_chunks(struct yaffs_dev *dev); -+ -+int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, -+ struct yaffs_obj *new_dir, const YCHAR * new_name); -+ -+int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); -+int yaffs_del_obj(struct yaffs_obj *obj); -+struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, -+ enum yaffs_obj_type type); -+ -+ -+int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); -+loff_t yaffs_get_obj_length(struct yaffs_obj *obj); -+int yaffs_get_obj_inode(struct yaffs_obj *obj); -+unsigned yaffs_get_obj_type(struct yaffs_obj *obj); -+int yaffs_get_obj_link_count(struct yaffs_obj *obj); -+ -+/* File operations */ -+int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, -+ int n_bytes); -+int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, -+ int n_bytes, int write_trhrough); -+int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); -+ -+struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid); -+ -+int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync); -+ -+/* Flushing and checkpointing */ -+void yaffs_flush_whole_cache(struct yaffs_dev *dev); -+ -+int yaffs_checkpoint_save(struct yaffs_dev *dev); -+int yaffs_checkpoint_restore(struct yaffs_dev *dev); -+ -+/* Directory operations */ -+struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, -+ u32 mode, u32 uid, u32 gid); -+struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, -+ const YCHAR *name); -+struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); -+ -+/* Link operations */ -+struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, -+ struct yaffs_obj *equiv_obj); -+ -+struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); -+ -+/* Symlink operations */ -+struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, const YCHAR *alias); -+YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); -+ -+/* Special inodes (fifos, sockets and devices) */ -+struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, -+ const YCHAR *name, u32 mode, u32 uid, -+ u32 gid, u32 rdev); -+ -+int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, -+ const void *value, int size, int flags); -+int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, -+ int size); -+int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); -+int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); -+ -+/* Special directories */ -+struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); -+struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); -+ -+void yaffs_handle_defered_free(struct yaffs_obj *obj); -+ -+void yaffs_update_dirty_dirs(struct yaffs_dev *dev); -+ -+int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); -+ -+/* Debug dump */ -+int yaffs_dump_obj(struct yaffs_obj *obj); -+ -+void yaffs_guts_test(struct yaffs_dev *dev); -+int yaffs_guts_ll_init(struct yaffs_dev *dev); -+ -+ -+/* A few useful functions to be used within the core files*/ -+void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, -+ int lyn); -+int yaffs_check_ff(u8 *buffer, int n_bytes); -+void yaffs_handle_chunk_error(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi); -+ -+u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); -+void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); -+ -+struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, -+ int number, -+ enum yaffs_obj_type type); -+int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ int nand_chunk, int in_scan); -+void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); -+void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, -+ const struct yaffs_obj_hdr *oh); -+void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); -+YCHAR *yaffs_clone_str(const YCHAR *str); -+void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); -+void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); -+int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, -+ int force, int is_shrink, int shadows, -+ struct yaffs_xattr_mod *xop); -+void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, -+ int backward_scanning); -+int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); -+struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); -+struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id, -+ struct yaffs_tnode *passed_tn); -+ -+int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, -+ int n_bytes, int write_trhrough); -+void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); -+void yaffs_skip_rest_of_block(struct yaffs_dev *dev); -+ -+int yaffs_count_free_chunks(struct yaffs_dev *dev); -+ -+struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, -+ struct yaffs_file_var *file_struct, -+ u32 chunk_id); -+ -+u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, -+ unsigned pos); -+ -+int yaffs_is_non_empty_dir(struct yaffs_obj *obj); -+ -+int yaffs_guts_format_dev(struct yaffs_dev *dev); -+ -+void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, -+ int *chunk_out, u32 *offset_out); -+/* -+ * Marshalling functions to get loff_t file sizes into aand out of -+ * object headers. -+ */ -+void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); -+loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); -+loff_t yaffs_max_file_size(struct yaffs_dev *dev); -+ -+/* -+ * Debug function to count number of blocks in each state -+ * NB Needs to be called with correct number of integers -+ */ -+ -+void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); -+ -+int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, -+ struct yaffs_ext_tags *tags); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_linux.h linux-3.4.90/fs/yaffs2/yaffs_linux.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_linux.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,48 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_LINUX_H__ -+#define __YAFFS_LINUX_H__ -+ -+#include "yportenv.h" -+ -+struct yaffs_linux_context { -+ struct list_head context_list; /* List of these we have mounted */ -+ struct yaffs_dev *dev; -+ struct super_block *super; -+ struct task_struct *bg_thread; /* Background thread for this device */ -+ int bg_running; -+ struct mutex gross_lock; /* Gross locking mutex*/ -+ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size -+ * at compile time so we have to allocate it. -+ */ -+ struct list_head search_contexts; -+ struct task_struct *readdir_process; -+ unsigned mount_id; -+ int dirty; -+}; -+ -+#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) -+#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+#define WRITE_SIZE_STR "writesize" -+#define WRITE_SIZE(mtd) ((mtd)->writesize) -+#else -+#define WRITE_SIZE_STR "oobblock" -+#define WRITE_SIZE(mtd) ((mtd)->oobblock) -+#endif -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.c linux-3.4.90/fs/yaffs2/yaffs_mtdif.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_mtdif.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,309 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yportenv.h" -+ -+#include "yaffs_mtdif.h" -+ -+#include "linux/mtd/mtd.h" -+#include "linux/types.h" -+#include "linux/time.h" -+#include "linux/major.h" -+#include "linux/mtd/nand.h" -+#include "linux/kernel.h" -+#include "linux/version.h" -+#include "linux/types.h" -+ -+#include "yaffs_trace.h" -+#include "yaffs_guts.h" -+#include "yaffs_linux.h" -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) -+#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) -+#define mtd_erase(m, ei) (m)->erase(m, ei) -+#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) -+#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) -+#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) -+#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) -+#endif -+ -+ -+ -+int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ u32 addr = -+ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * -+ dev->param.chunks_per_block; -+ struct erase_info ei; -+ int retval = 0; -+ -+ ei.mtd = mtd; -+ ei.addr = addr; -+ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; -+ ei.time = 1000; -+ ei.retries = 2; -+ ei.callback = NULL; -+ ei.priv = (u_long) dev; -+ -+ retval = mtd_erase(mtd, &ei); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+ -+static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, -+ const u8 *data, int data_len, -+ const u8 *oob, int oob_len) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ loff_t addr; -+ struct mtd_oob_ops ops; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", -+ dev, nand_chunk, data, data_len, oob, oob_len); -+ -+ if (!data || !data_len) { -+ data = NULL; -+ data_len = 0; -+ } -+ -+ if (!oob || !oob_len) { -+ oob = NULL; -+ oob_len = 0; -+ } -+ -+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; -+ memset(&ops, 0, sizeof(ops)); -+ ops.mode = MTD_OPS_AUTO_OOB; -+ ops.len = (data) ? data_len : 0; -+ ops.ooblen = oob_len; -+ ops.datbuf = (u8 *)data; -+ ops.oobbuf = (u8 *)oob; -+ -+ retval = mtd_write_oob(mtd, addr, &ops); -+ if (retval) { -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "write_oob failed, chunk %d, mtd error %d", -+ nand_chunk, retval); -+ } -+ return retval ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, -+ u8 *data, int data_len, -+ u8 *oob, int oob_len, -+ enum yaffs_ecc_result *ecc_result) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ loff_t addr; -+ struct mtd_oob_ops ops; -+ int retval; -+ -+ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; -+ memset(&ops, 0, sizeof(ops)); -+ ops.mode = MTD_OPS_AUTO_OOB; -+ ops.len = (data) ? data_len : 0; -+ ops.ooblen = oob_len; -+ ops.datbuf = data; -+ ops.oobbuf = oob; -+ -+#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) -+ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; -+ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. -+ */ -+ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; -+#endif -+ /* Read page and oob using MTD. -+ * Check status and determine ECC result. -+ */ -+ retval = mtd_read_oob(mtd, addr, &ops); -+ if (retval) -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "read_oob failed, chunk %d, mtd error %d", -+ nand_chunk, retval); -+ -+ switch (retval) { -+ case 0: -+ /* no error */ -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ break; -+ -+ case -EUCLEAN: -+ /* MTD's ECC fixed the data */ -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_FIXED; -+ dev->n_ecc_fixed++; -+ break; -+ -+ case -EBADMSG: -+ default: -+ /* MTD's ECC could not fix the data */ -+ dev->n_ecc_unfixed++; -+ if(ecc_result) -+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ return YAFFS_FAIL; -+ } -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ -+ loff_t addr; -+ struct erase_info ei; -+ int retval = 0; -+ u32 block_size; -+ -+ block_size = dev->param.total_bytes_per_chunk * -+ dev->param.chunks_per_block; -+ addr = ((loff_t) block_no) * block_size; -+ -+ ei.mtd = mtd; -+ ei.addr = addr; -+ ei.len = block_size; -+ ei.time = 1000; -+ ei.retries = 2; -+ ei.callback = NULL; -+ ei.priv = (u_long) dev; -+ -+ retval = mtd_erase(mtd, &ei); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); -+ -+ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); -+ return (retval) ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) -+{ -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); -+ -+ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); -+ return (retval) ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+static int yaffs_mtd_initialise(struct yaffs_dev *dev) -+{ -+ return YAFFS_OK; -+} -+ -+static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) -+{ -+ return YAFFS_OK; -+} -+ -+ -+void yaffs_mtd_drv_install(struct yaffs_dev *dev) -+{ -+ struct yaffs_driver *drv = &dev->drv; -+ -+ drv->drv_write_chunk_fn = yaffs_mtd_write; -+ drv->drv_read_chunk_fn = yaffs_mtd_read; -+ drv->drv_erase_fn = yaffs_mtd_erase; -+ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; -+ drv->drv_check_bad_fn = yaffs_mtd_check_bad; -+ drv->drv_initialise_fn = yaffs_mtd_initialise; -+ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; -+} -+ -+ -+struct mtd_info * yaffs_get_mtd_device(dev_t sdev) -+{ -+ struct mtd_info *mtd; -+ -+ mtd = yaffs_get_mtd_device(sdev); -+ -+ /* Check it's an mtd device..... */ -+ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) -+ return NULL; /* This isn't an mtd device */ -+ -+ /* Check it's NAND */ -+ if (mtd->type != MTD_NANDFLASH) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: MTD device is not NAND it's type %d", -+ mtd->type); -+ return NULL; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); -+ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); -+ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -+ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); -+#else -+ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); -+#endif -+ -+ return mtd; -+} -+ -+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) -+{ -+ if (yaffs_version == 2) { -+ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || -+ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && -+ !inband_tags) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device does not have the right page sizes" -+ ); -+ return -1; -+ } -+ } else { -+ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || -+ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "MTD device does not support have the right page sizes" -+ ); -+ return -1; -+ } -+ } -+ -+ return 0; -+} -+ -+ -+void yaffs_put_mtd_device(struct mtd_info *mtd) -+{ -+ if(mtd) -+ put_mtd_device(mtd); -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.h linux-3.4.90/fs/yaffs2/yaffs_mtdif.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_mtdif.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,25 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_MTDIF_H__ -+#define __YAFFS_MTDIF_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_mtd_drv_install(struct yaffs_dev *dev); -+struct mtd_info * yaffs_get_mtd_device(dev_t sdev); -+void yaffs_put_mtd_device(struct mtd_info *mtd); -+int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.c linux-3.4.90/fs/yaffs2/yaffs_nameval.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_nameval.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,208 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * This simple implementation of a name-value store assumes a small number of -+* values and fits into a small finite buffer. -+ * -+ * Each attribute is stored as a record: -+ * sizeof(int) bytes record size. -+ * strnlen+1 bytes name null terminated. -+ * nbytes value. -+ * ---------- -+ * total size stored in record size -+ * -+ * This code has not been tested with unicode yet. -+ */ -+ -+#include "yaffs_nameval.h" -+ -+#include "yportenv.h" -+ -+static int nval_find(const char *xb, int xb_size, const YCHAR *name, -+ int *exist_size) -+{ -+ int pos = 0; -+ int size; -+ -+ memcpy(&size, xb, sizeof(int)); -+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { -+ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), -+ name, size)) { -+ if (exist_size) -+ *exist_size = size; -+ return pos; -+ } -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ if (exist_size) -+ *exist_size = 0; -+ return -ENODATA; -+} -+ -+static int nval_used(const char *xb, int xb_size) -+{ -+ int pos = 0; -+ int size; -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ return pos; -+} -+ -+int nval_del(char *xb, int xb_size, const YCHAR *name) -+{ -+ int pos = nval_find(xb, xb_size, name, NULL); -+ int size; -+ -+ if (pos < 0 || pos >= xb_size) -+ return -ENODATA; -+ -+ /* Find size, shift rest over this record, -+ * then zero out the rest of buffer */ -+ memcpy(&size, xb + pos, sizeof(int)); -+ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); -+ memset(xb + (xb_size - size), 0, size); -+ return 0; -+} -+ -+int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, -+ int bsize, int flags) -+{ -+ int pos; -+ int namelen = strnlen(name, xb_size); -+ int reclen; -+ int size_exist = 0; -+ int space; -+ int start; -+ -+ pos = nval_find(xb, xb_size, name, &size_exist); -+ -+ if (flags & XATTR_CREATE && pos >= 0) -+ return -EEXIST; -+ if (flags & XATTR_REPLACE && pos < 0) -+ return -ENODATA; -+ -+ start = nval_used(xb, xb_size); -+ space = xb_size - start + size_exist; -+ -+ reclen = (sizeof(int) + namelen + 1 + bsize); -+ -+ if (reclen > space) -+ return -ENOSPC; -+ -+ if (pos >= 0) { -+ nval_del(xb, xb_size, name); -+ start = nval_used(xb, xb_size); -+ } -+ -+ pos = start; -+ -+ memcpy(xb + pos, &reclen, sizeof(int)); -+ pos += sizeof(int); -+ strncpy((YCHAR *) (xb + pos), name, reclen); -+ pos += (namelen + 1); -+ memcpy(xb + pos, buf, bsize); -+ return 0; -+} -+ -+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, -+ int bsize) -+{ -+ int pos = nval_find(xb, xb_size, name, NULL); -+ int size; -+ -+ if (pos >= 0 && pos < xb_size) { -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ pos += sizeof(int); /* advance past record length */ -+ size -= sizeof(int); -+ -+ /* Advance over name string */ -+ while (xb[pos] && size > 0 && pos < xb_size) { -+ pos++; -+ size--; -+ } -+ /*Advance over NUL */ -+ pos++; -+ size--; -+ -+ /* If bsize is zero then this is a size query. -+ * Return the size, but don't copy. -+ */ -+ if (!bsize) -+ return size; -+ -+ if (size <= bsize) { -+ memcpy(buf, xb + pos, size); -+ return size; -+ } -+ } -+ if (pos >= 0) -+ return -ERANGE; -+ -+ return -ENODATA; -+} -+ -+int nval_list(const char *xb, int xb_size, char *buf, int bsize) -+{ -+ int pos = 0; -+ int size; -+ int name_len; -+ int ncopied = 0; -+ int filled = 0; -+ -+ memcpy(&size, xb + pos, sizeof(int)); -+ while (size > sizeof(int) && -+ size <= xb_size && -+ (pos + size) < xb_size && -+ !filled) { -+ pos += sizeof(int); -+ size -= sizeof(int); -+ name_len = strnlen((YCHAR *) (xb + pos), size); -+ if (ncopied + name_len + 1 < bsize) { -+ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); -+ buf += name_len; -+ *buf = '\0'; -+ buf++; -+ if (sizeof(YCHAR) > 1) { -+ *buf = '\0'; -+ buf++; -+ } -+ ncopied += (name_len + 1); -+ } else { -+ filled = 1; -+ } -+ pos += size; -+ if (pos < xb_size - sizeof(int)) -+ memcpy(&size, xb + pos, sizeof(int)); -+ else -+ size = 0; -+ } -+ return ncopied; -+} -+ -+int nval_hasvalues(const char *xb, int xb_size) -+{ -+ return nval_used(xb, xb_size) > 0; -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.h linux-3.4.90/fs/yaffs2/yaffs_nameval.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_nameval.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,28 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __NAMEVAL_H__ -+#define __NAMEVAL_H__ -+ -+#include "yportenv.h" -+ -+int nval_del(char *xb, int xb_size, const YCHAR * name); -+int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, -+ int bsize, int flags); -+int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, -+ int bsize); -+int nval_list(const char *xb, int xb_size, char *buf, int bsize); -+int nval_hasvalues(const char *xb, int xb_size); -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nand.c linux-3.4.90/fs/yaffs2/yaffs_nand.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_nand.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,122 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_nand.h" -+#include "yaffs_tagscompat.h" -+ -+#include "yaffs_getblockinfo.h" -+#include "yaffs_summary.h" -+ -+static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) -+{ -+ return chunk - dev->chunk_offset; -+} -+ -+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, -+ u8 *buffer, struct yaffs_ext_tags *tags) -+{ -+ int result; -+ struct yaffs_ext_tags local_tags; -+ int flash_chunk = apply_chunk_offset(dev, nand_chunk); -+ -+ dev->n_page_reads++; -+ -+ /* If there are no tags provided use local tags. */ -+ if (!tags) -+ tags = &local_tags; -+ -+ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); -+ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { -+ -+ struct yaffs_block_info *bi; -+ bi = yaffs_get_block_info(dev, -+ nand_chunk / -+ dev->param.chunks_per_block); -+ yaffs_handle_chunk_error(dev, bi); -+ } -+ return result; -+} -+ -+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *buffer, struct yaffs_ext_tags *tags) -+{ -+ int result; -+ int flash_chunk = apply_chunk_offset(dev, nand_chunk); -+ -+ dev->n_page_writes++; -+ -+ if (!tags) { -+ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); -+ BUG(); -+ return YAFFS_FAIL; -+ } -+ -+ tags->seq_number = dev->seq_number; -+ tags->chunk_used = 1; -+ yaffs_trace(YAFFS_TRACE_WRITE, -+ "Writing chunk %d tags %d %d", -+ nand_chunk, tags->obj_id, tags->chunk_id); -+ -+ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, -+ buffer, tags); -+ -+ yaffs_summary_add(dev, tags, nand_chunk); -+ -+ return result; -+} -+ -+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ block_no -= dev->block_offset; -+ dev->n_bad_markings++; -+ -+ if (dev->param.disable_bad_block_marking) -+ return YAFFS_OK; -+ -+ return dev->tagger.mark_bad_fn(dev, block_no); -+} -+ -+ -+int yaffs_query_init_block_state(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ block_no -= dev->block_offset; -+ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); -+} -+ -+int yaffs_erase_block(struct yaffs_dev *dev, int block_no) -+{ -+ int result; -+ -+ block_no -= dev->block_offset; -+ dev->n_erasures++; -+ result = dev->drv.drv_erase_fn(dev, block_no); -+ return result; -+} -+ -+int yaffs_init_nand(struct yaffs_dev *dev) -+{ -+ if (dev->drv.drv_initialise_fn) -+ return dev->drv.drv_initialise_fn(dev); -+ return YAFFS_OK; -+} -+ -+int yaffs_deinit_nand(struct yaffs_dev *dev) -+{ -+ if (dev->drv.drv_deinitialise_fn) -+ return dev->drv.drv_deinitialise_fn(dev); -+ return YAFFS_OK; -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nand.h linux-3.4.90/fs/yaffs2/yaffs_nand.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_nand.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_NAND_H__ -+#define __YAFFS_NAND_H__ -+#include "yaffs_guts.h" -+ -+int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, -+ u8 *buffer, struct yaffs_ext_tags *tags); -+ -+int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *buffer, struct yaffs_ext_tags *tags); -+ -+int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); -+ -+int yaffs_query_init_block_state(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ unsigned *seq_number); -+ -+int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); -+ -+int yaffs_init_nand(struct yaffs_dev *dev); -+int yaffs_deinit_nand(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.4.90/fs/yaffs2/yaffs_packedtags1.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,56 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_packedtags1.h" -+#include "yportenv.h" -+ -+static const u8 all_ff[20] = { -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff, -+ 0xff, 0xff, 0xff, 0xff -+}; -+ -+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, -+ const struct yaffs_ext_tags *t) -+{ -+ pt->chunk_id = t->chunk_id; -+ pt->serial_number = t->serial_number; -+ pt->n_bytes = t->n_bytes; -+ pt->obj_id = t->obj_id; -+ pt->ecc = 0; -+ pt->deleted = (t->is_deleted) ? 0 : 1; -+ pt->unused_stuff = 0; -+ pt->should_be_ff = 0xffffffff; -+} -+ -+void yaffs_unpack_tags1(struct yaffs_ext_tags *t, -+ const struct yaffs_packed_tags1 *pt) -+{ -+ -+ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { -+ t->block_bad = 0; -+ if (pt->should_be_ff != 0xffffffff) -+ t->block_bad = 1; -+ t->chunk_used = 1; -+ t->obj_id = pt->obj_id; -+ t->chunk_id = pt->chunk_id; -+ t->n_bytes = pt->n_bytes; -+ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ t->is_deleted = (pt->deleted) ? 0 : 1; -+ t->serial_number = pt->serial_number; -+ } else { -+ memset(t, 0, sizeof(struct yaffs_ext_tags)); -+ } -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.4.90/fs/yaffs2/yaffs_packedtags1.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ -+ -+#ifndef __YAFFS_PACKEDTAGS1_H__ -+#define __YAFFS_PACKEDTAGS1_H__ -+ -+#include "yaffs_guts.h" -+ -+struct yaffs_packed_tags1 { -+ u32 chunk_id:20; -+ u32 serial_number:2; -+ u32 n_bytes:10; -+ u32 obj_id:18; -+ u32 ecc:12; -+ u32 deleted:1; -+ u32 unused_stuff:1; -+ unsigned should_be_ff; -+ -+}; -+ -+void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, -+ const struct yaffs_ext_tags *t); -+void yaffs_unpack_tags1(struct yaffs_ext_tags *t, -+ const struct yaffs_packed_tags1 *pt); -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.4.90/fs/yaffs2/yaffs_packedtags2.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,197 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_packedtags2.h" -+#include "yportenv.h" -+#include "yaffs_trace.h" -+ -+/* This code packs a set of extended tags into a binary structure for -+ * NAND storage -+ */ -+ -+/* Some of the information is "extra" struff which can be packed in to -+ * speed scanning -+ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. -+ */ -+ -+/* Extra flags applied to chunk_id */ -+ -+#define EXTRA_HEADER_INFO_FLAG 0x80000000 -+#define EXTRA_SHRINK_FLAG 0x40000000 -+#define EXTRA_SHADOWS_FLAG 0x20000000 -+#define EXTRA_SPARE_FLAGS 0x10000000 -+ -+#define ALL_EXTRA_FLAGS 0xf0000000 -+ -+/* Also, the top 4 bits of the object Id are set to the object type. */ -+#define EXTRA_OBJECT_TYPE_SHIFT (28) -+#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) -+ -+static void yaffs_dump_packed_tags2_tags_only( -+ const struct yaffs_packed_tags2_tags_only *ptt) -+{ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "packed tags obj %d chunk %d byte %d seq %d", -+ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); -+} -+ -+static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) -+{ -+ yaffs_dump_packed_tags2_tags_only(&pt->t); -+} -+ -+static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) -+{ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", -+ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, -+ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, -+ t->seq_number); -+ -+} -+ -+static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) -+{ -+ if (t->chunk_id != 0 || !t->extra_available) -+ return 0; -+ -+ /* Check if the file size is too long to store */ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && -+ (t->extra_file_size >> 31) != 0) -+ return 0; -+ return 1; -+} -+ -+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, -+ const struct yaffs_ext_tags *t) -+{ -+ ptt->chunk_id = t->chunk_id; -+ ptt->seq_number = t->seq_number; -+ ptt->n_bytes = t->n_bytes; -+ ptt->obj_id = t->obj_id; -+ -+ /* Only store extra tags for object headers. -+ * If it is a file then only store if the file size is short\ -+ * enough to fit. -+ */ -+ if (yaffs_check_tags_extra_packable(t)) { -+ /* Store the extra header info instead */ -+ /* We save the parent object in the chunk_id */ -+ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; -+ if (t->extra_is_shrink) -+ ptt->chunk_id |= EXTRA_SHRINK_FLAG; -+ if (t->extra_shadows) -+ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; -+ -+ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; -+ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); -+ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ ptt->n_bytes = t->extra_equiv_id; -+ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) -+ ptt->n_bytes = (unsigned) t->extra_file_size; -+ else -+ ptt->n_bytes = 0; -+ } -+ -+ yaffs_dump_packed_tags2_tags_only(ptt); -+ yaffs_dump_tags2(t); -+} -+ -+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, -+ const struct yaffs_ext_tags *t, int tags_ecc) -+{ -+ yaffs_pack_tags2_tags_only(&pt->t, t); -+ -+ if (tags_ecc) -+ yaffs_ecc_calc_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &pt->ecc); -+} -+ -+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, -+ struct yaffs_packed_tags2_tags_only *ptt) -+{ -+ memset(t, 0, sizeof(struct yaffs_ext_tags)); -+ -+ if (ptt->seq_number == 0xffffffff) -+ return; -+ -+ t->block_bad = 0; -+ t->chunk_used = 1; -+ t->obj_id = ptt->obj_id; -+ t->chunk_id = ptt->chunk_id; -+ t->n_bytes = ptt->n_bytes; -+ t->is_deleted = 0; -+ t->serial_number = 0; -+ t->seq_number = ptt->seq_number; -+ -+ /* Do extra header info stuff */ -+ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { -+ t->chunk_id = 0; -+ t->n_bytes = 0; -+ -+ t->extra_available = 1; -+ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); -+ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; -+ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; -+ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; -+ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; -+ -+ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ t->extra_equiv_id = ptt->n_bytes; -+ else -+ t->extra_file_size = ptt->n_bytes; -+ } -+ yaffs_dump_packed_tags2_tags_only(ptt); -+ yaffs_dump_tags2(t); -+} -+ -+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, -+ int tags_ecc) -+{ -+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ -+ if (pt->t.seq_number != 0xffffffff && tags_ecc) { -+ /* Chunk is in use and we need to do ECC */ -+ -+ struct yaffs_ecc_other ecc; -+ int result; -+ yaffs_ecc_calc_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &ecc); -+ result = -+ yaffs_ecc_correct_other((unsigned char *)&pt->t, -+ sizeof(struct yaffs_packed_tags2_tags_only), -+ &pt->ecc, &ecc); -+ switch (result) { -+ case 0: -+ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ break; -+ case 1: -+ ecc_result = YAFFS_ECC_RESULT_FIXED; -+ break; -+ case -1: -+ ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ break; -+ default: -+ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; -+ } -+ } -+ yaffs_unpack_tags2_tags_only(t, &pt->t); -+ -+ t->ecc_result = ecc_result; -+ -+ yaffs_dump_packed_tags2(pt); -+ yaffs_dump_tags2(t); -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.4.90/fs/yaffs2/yaffs_packedtags2.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,47 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ -+ -+#ifndef __YAFFS_PACKEDTAGS2_H__ -+#define __YAFFS_PACKEDTAGS2_H__ -+ -+#include "yaffs_guts.h" -+#include "yaffs_ecc.h" -+ -+struct yaffs_packed_tags2_tags_only { -+ unsigned seq_number; -+ unsigned obj_id; -+ unsigned chunk_id; -+ unsigned n_bytes; -+}; -+ -+struct yaffs_packed_tags2 { -+ struct yaffs_packed_tags2_tags_only t; -+ struct yaffs_ecc_other ecc; -+}; -+ -+/* Full packed tags with ECC, used for oob tags */ -+void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, -+ const struct yaffs_ext_tags *t, int tags_ecc); -+void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, -+ int tags_ecc); -+ -+/* Only the tags part (no ECC for use with inband tags */ -+void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, -+ const struct yaffs_ext_tags *t); -+void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, -+ struct yaffs_packed_tags2_tags_only *pt); -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_summary.c linux-3.4.90/fs/yaffs2/yaffs_summary.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_summary.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,312 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* Summaries write the useful part of the tags for the chunks in a block into an -+ * an array which is written to the last n chunks of the block. -+ * Reading the summaries gives all the tags for the block in one read. Much -+ * faster. -+ * -+ * Chunks holding summaries are marked with tags making it look like -+ * they are part of a fake file. -+ * -+ * The summary could also be used during gc. -+ * -+ */ -+ -+#include "yaffs_summary.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_nand.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_bitmap.h" -+ -+/* -+ * The summary is built up in an array of summary tags. -+ * This gets written to the last one or two (maybe more) chunks in a block. -+ * A summary header is written as the first part of each chunk of summary data. -+ * The summary header must match or the summary is rejected. -+ */ -+ -+/* Summary tags don't need the sequence number because that is redundant. */ -+struct yaffs_summary_tags { -+ unsigned obj_id; -+ unsigned chunk_id; -+ unsigned n_bytes; -+}; -+ -+/* Summary header */ -+struct yaffs_summary_header { -+ unsigned version; /* Must match current version */ -+ unsigned block; /* Must be this block */ -+ unsigned seq; /* Must be this sequence number */ -+ unsigned sum; /* Just add up all the bytes in the tags */ -+}; -+ -+ -+static void yaffs_summary_clear(struct yaffs_dev *dev) -+{ -+ if (!dev->sum_tags) -+ return; -+ memset(dev->sum_tags, 0, dev->chunks_per_summary * -+ sizeof(struct yaffs_summary_tags)); -+} -+ -+ -+void yaffs_summary_deinit(struct yaffs_dev *dev) -+{ -+ kfree(dev->sum_tags); -+ dev->sum_tags = NULL; -+ kfree(dev->gc_sum_tags); -+ dev->gc_sum_tags = NULL; -+ dev->chunks_per_summary = 0; -+} -+ -+int yaffs_summary_init(struct yaffs_dev *dev) -+{ -+ int sum_bytes; -+ int chunks_used; /* Number of chunks used by summary */ -+ int sum_tags_bytes; -+ -+ sum_bytes = dev->param.chunks_per_block * -+ sizeof(struct yaffs_summary_tags); -+ -+ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ -+ (dev->data_bytes_per_chunk - -+ sizeof(struct yaffs_summary_header)); -+ -+ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; -+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); -+ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); -+ if (!dev->sum_tags || !dev->gc_sum_tags) { -+ yaffs_summary_deinit(dev); -+ return YAFFS_FAIL; -+ } -+ -+ yaffs_summary_clear(dev); -+ -+ return YAFFS_OK; -+} -+ -+static unsigned yaffs_summary_sum(struct yaffs_dev *dev) -+{ -+ u8 *sum_buffer = (u8 *)dev->sum_tags; -+ int i; -+ unsigned sum = 0; -+ -+ i = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ while (i > 0) { -+ sum += *sum_buffer; -+ sum_buffer++; -+ i--; -+ } -+ -+ return sum; -+} -+ -+static int yaffs_summary_write(struct yaffs_dev *dev, int blk) -+{ -+ struct yaffs_ext_tags tags; -+ u8 *buffer; -+ u8 *sum_buffer = (u8 *)dev->sum_tags; -+ int n_bytes; -+ int chunk_in_nand; -+ int chunk_in_block; -+ int result; -+ int this_tx; -+ struct yaffs_summary_header hdr; -+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ -+ buffer = yaffs_get_temp_buffer(dev); -+ n_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); -+ tags.obj_id = YAFFS_OBJECTID_SUMMARY; -+ tags.chunk_id = 1; -+ chunk_in_block = dev->chunks_per_summary; -+ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + -+ dev->chunks_per_summary; -+ hdr.version = YAFFS_SUMMARY_VERSION; -+ hdr.block = blk; -+ hdr.seq = bi->seq_number; -+ hdr.sum = yaffs_summary_sum(dev); -+ -+ do { -+ this_tx = n_bytes; -+ if (this_tx > sum_bytes_per_chunk) -+ this_tx = sum_bytes_per_chunk; -+ memcpy(buffer, &hdr, sizeof(hdr)); -+ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); -+ tags.n_bytes = this_tx + sizeof(hdr); -+ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, -+ buffer, &tags); -+ -+ if (result != YAFFS_OK) -+ break; -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ dev->n_free_chunks--; -+ -+ n_bytes -= this_tx; -+ sum_buffer += this_tx; -+ chunk_in_nand++; -+ chunk_in_block++; -+ tags.chunk_id++; -+ } while (result == YAFFS_OK && n_bytes > 0); -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ -+ if (result == YAFFS_OK) -+ bi->has_summary = 1; -+ -+ -+ return result; -+} -+ -+int yaffs_summary_read(struct yaffs_dev *dev, -+ struct yaffs_summary_tags *st, -+ int blk) -+{ -+ struct yaffs_ext_tags tags; -+ u8 *buffer; -+ u8 *sum_buffer = (u8 *)st; -+ int n_bytes; -+ int chunk_id; -+ int chunk_in_nand; -+ int chunk_in_block; -+ int result; -+ int this_tx; -+ struct yaffs_summary_header hdr; -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); -+ int sum_tags_bytes; -+ -+ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * -+ dev->chunks_per_summary; -+ buffer = yaffs_get_temp_buffer(dev); -+ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; -+ chunk_in_block = dev->chunks_per_summary; -+ chunk_in_nand = blk * dev->param.chunks_per_block + -+ dev->chunks_per_summary; -+ chunk_id = 1; -+ do { -+ this_tx = n_bytes; -+ if (this_tx > sum_bytes_per_chunk) -+ this_tx = sum_bytes_per_chunk; -+ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, -+ buffer, &tags); -+ -+ if (tags.chunk_id != chunk_id || -+ tags.obj_id != YAFFS_OBJECTID_SUMMARY || -+ tags.chunk_used == 0 || -+ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || -+ tags.n_bytes != (this_tx + sizeof(hdr))) -+ result = YAFFS_FAIL; -+ if (result != YAFFS_OK) -+ break; -+ -+ if (st == dev->sum_tags) { -+ /* If we're scanning then update the block info */ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ } -+ memcpy(&hdr, buffer, sizeof(hdr)); -+ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); -+ n_bytes -= this_tx; -+ sum_buffer += this_tx; -+ chunk_in_nand++; -+ chunk_in_block++; -+ chunk_id++; -+ } while (result == YAFFS_OK && n_bytes > 0); -+ yaffs_release_temp_buffer(dev, buffer); -+ -+ if (result == YAFFS_OK) { -+ /* Verify header */ -+ if (hdr.version != YAFFS_SUMMARY_VERSION || -+ hdr.seq != bi->seq_number || -+ hdr.sum != yaffs_summary_sum(dev)) -+ result = YAFFS_FAIL; -+ } -+ -+ if (st == dev->sum_tags && result == YAFFS_OK) -+ bi->has_summary = 1; -+ -+ return result; -+} -+ -+int yaffs_summary_add(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_nand) -+{ -+ struct yaffs_packed_tags2_tags_only tags_only; -+ struct yaffs_summary_tags *sum_tags; -+ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; -+ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; -+ -+ if (!dev->sum_tags) -+ return YAFFS_OK; -+ -+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { -+ yaffs_pack_tags2_tags_only(&tags_only, tags); -+ sum_tags = &dev->sum_tags[chunk_in_block]; -+ sum_tags->chunk_id = tags_only.chunk_id; -+ sum_tags->n_bytes = tags_only.n_bytes; -+ sum_tags->obj_id = tags_only.obj_id; -+ -+ if (chunk_in_block == dev->chunks_per_summary - 1) { -+ /* Time to write out the summary */ -+ yaffs_summary_write(dev, block_in_nand); -+ yaffs_summary_clear(dev); -+ yaffs_skip_rest_of_block(dev); -+ } -+ } -+ return YAFFS_OK; -+} -+ -+int yaffs_summary_fetch(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block) -+{ -+ struct yaffs_packed_tags2_tags_only tags_only; -+ struct yaffs_summary_tags *sum_tags; -+ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { -+ sum_tags = &dev->sum_tags[chunk_in_block]; -+ tags_only.chunk_id = sum_tags->chunk_id; -+ tags_only.n_bytes = sum_tags->n_bytes; -+ tags_only.obj_id = sum_tags->obj_id; -+ yaffs_unpack_tags2_tags_only(tags, &tags_only); -+ return YAFFS_OK; -+ } -+ return YAFFS_FAIL; -+} -+ -+void yaffs_summary_gc(struct yaffs_dev *dev, int blk) -+{ -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); -+ int i; -+ -+ if (!bi->has_summary) -+ return; -+ -+ for (i = dev->chunks_per_summary; -+ i < dev->param.chunks_per_block; -+ i++) { -+ if (yaffs_check_chunk_bit(dev, blk, i)) { -+ yaffs_clear_chunk_bit(dev, blk, i); -+ bi->pages_in_use--; -+ dev->n_free_chunks++; -+ } -+ } -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_summary.h linux-3.4.90/fs/yaffs2/yaffs_summary.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_summary.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,37 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_SUMMARY_H__ -+#define __YAFFS_SUMMARY_H__ -+ -+#include "yaffs_packedtags2.h" -+ -+ -+int yaffs_summary_init(struct yaffs_dev *dev); -+void yaffs_summary_deinit(struct yaffs_dev *dev); -+ -+int yaffs_summary_add(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block); -+int yaffs_summary_fetch(struct yaffs_dev *dev, -+ struct yaffs_ext_tags *tags, -+ int chunk_in_block); -+int yaffs_summary_read(struct yaffs_dev *dev, -+ struct yaffs_summary_tags *st, -+ int blk); -+void yaffs_summary_gc(struct yaffs_dev *dev, int blk); -+ -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.4.90/fs/yaffs2/yaffs_tagscompat.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,381 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_tagscompat.h" -+#include "yaffs_ecc.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_trace.h" -+ -+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); -+ -+ -+/********** Tags ECC calculations *********/ -+ -+ -+void yaffs_calc_tags_ecc(struct yaffs_tags *tags) -+{ -+ /* Calculate an ecc */ -+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; -+ unsigned i, j; -+ unsigned ecc = 0; -+ unsigned bit = 0; -+ -+ tags->ecc = 0; -+ -+ for (i = 0; i < 8; i++) { -+ for (j = 1; j & 0xff; j <<= 1) { -+ bit++; -+ if (b[i] & j) -+ ecc ^= bit; -+ } -+ } -+ tags->ecc = ecc; -+} -+ -+int yaffs_check_tags_ecc(struct yaffs_tags *tags) -+{ -+ unsigned ecc = tags->ecc; -+ -+ yaffs_calc_tags_ecc(tags); -+ -+ ecc ^= tags->ecc; -+ -+ if (ecc && ecc <= 64) { -+ /* TODO: Handle the failure better. Retire? */ -+ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; -+ -+ ecc--; -+ -+ b[ecc / 8] ^= (1 << (ecc & 7)); -+ -+ /* Now recvalc the ecc */ -+ yaffs_calc_tags_ecc(tags); -+ -+ return 1; /* recovered error */ -+ } else if (ecc) { -+ /* Wierd ecc failure value */ -+ /* TODO Need to do somethiong here */ -+ return -1; /* unrecovered error */ -+ } -+ return 0; -+} -+ -+/********** Tags **********/ -+ -+static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, -+ struct yaffs_tags *tags_ptr) -+{ -+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; -+ -+ yaffs_calc_tags_ecc(tags_ptr); -+ -+ spare_ptr->tb0 = tu->as_bytes[0]; -+ spare_ptr->tb1 = tu->as_bytes[1]; -+ spare_ptr->tb2 = tu->as_bytes[2]; -+ spare_ptr->tb3 = tu->as_bytes[3]; -+ spare_ptr->tb4 = tu->as_bytes[4]; -+ spare_ptr->tb5 = tu->as_bytes[5]; -+ spare_ptr->tb6 = tu->as_bytes[6]; -+ spare_ptr->tb7 = tu->as_bytes[7]; -+} -+ -+static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, -+ struct yaffs_spare *spare_ptr, -+ struct yaffs_tags *tags_ptr) -+{ -+ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; -+ int result; -+ -+ tu->as_bytes[0] = spare_ptr->tb0; -+ tu->as_bytes[1] = spare_ptr->tb1; -+ tu->as_bytes[2] = spare_ptr->tb2; -+ tu->as_bytes[3] = spare_ptr->tb3; -+ tu->as_bytes[4] = spare_ptr->tb4; -+ tu->as_bytes[5] = spare_ptr->tb5; -+ tu->as_bytes[6] = spare_ptr->tb6; -+ tu->as_bytes[7] = spare_ptr->tb7; -+ -+ result = yaffs_check_tags_ecc(tags_ptr); -+ if (result > 0) -+ dev->n_tags_ecc_fixed++; -+ else if (result < 0) -+ dev->n_tags_ecc_unfixed++; -+} -+ -+static void yaffs_spare_init(struct yaffs_spare *spare) -+{ -+ memset(spare, 0xff, sizeof(struct yaffs_spare)); -+} -+ -+static int yaffs_wr_nand(struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ struct yaffs_spare *spare) -+{ -+ int data_size = dev->data_bytes_per_chunk; -+ -+ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *) spare, sizeof(*spare)); -+} -+ -+static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, -+ struct yaffs_spare *spare, -+ enum yaffs_ecc_result *ecc_result, -+ int correct_errors) -+{ -+ int ret_val; -+ struct yaffs_spare local_spare; -+ int data_size; -+ int spare_size; -+ int ecc_result1, ecc_result2; -+ u8 calc_ecc[3]; -+ -+ if (!spare) { -+ /* If we don't have a real spare, then we use a local one. */ -+ /* Need this for the calculation of the ecc */ -+ spare = &local_spare; -+ } -+ data_size = dev->data_bytes_per_chunk; -+ spare_size = sizeof(struct yaffs_spare); -+ -+ if (dev->param.use_nand_ecc) -+ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *) spare, spare_size, -+ ecc_result); -+ -+ -+ /* Handle the ECC at this level. */ -+ -+ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, data_size, -+ (u8 *)spare, spare_size, -+ NULL); -+ if (!data || !correct_errors) -+ return ret_val; -+ -+ /* Do ECC correction if needed. */ -+ yaffs_ecc_calc(data, calc_ecc); -+ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); -+ yaffs_ecc_calc(&data[256], calc_ecc); -+ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); -+ -+ if (ecc_result1 > 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error fix performed on chunk %d:0", -+ nand_chunk); -+ dev->n_ecc_fixed++; -+ } else if (ecc_result1 < 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error unfixed on chunk %d:0", -+ nand_chunk); -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (ecc_result2 > 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error fix performed on chunk %d:1", -+ nand_chunk); -+ dev->n_ecc_fixed++; -+ } else if (ecc_result2 < 0) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "**>>yaffs ecc error unfixed on chunk %d:1", -+ nand_chunk); -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (ecc_result1 || ecc_result2) { -+ /* We had a data problem on this page */ -+ yaffs_handle_rd_data_error(dev, nand_chunk); -+ } -+ -+ if (ecc_result1 < 0 || ecc_result2 < 0) -+ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ else if (ecc_result1 > 0 || ecc_result2 > 0) -+ *ecc_result = YAFFS_ECC_RESULT_FIXED; -+ else -+ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; -+ -+ return ret_val; -+} -+ -+/* -+ * Functions for robustisizing -+ */ -+ -+static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) -+{ -+ int flash_block = nand_chunk / dev->param.chunks_per_block; -+ -+ /* Mark the block for retirement */ -+ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> -+ needs_retiring = 1; -+ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, -+ "**>>Block %d marked for retirement", -+ flash_block); -+ -+ /* TODO: -+ * Just do a garbage collection on the affected block -+ * then retire the block -+ * NB recursion -+ */ -+} -+ -+static int yaffs_tags_compat_wr(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, const struct yaffs_ext_tags *ext_tags) -+{ -+ struct yaffs_spare spare; -+ struct yaffs_tags tags; -+ -+ yaffs_spare_init(&spare); -+ -+ if (ext_tags->is_deleted) -+ spare.page_status = 0; -+ else { -+ tags.obj_id = ext_tags->obj_id; -+ tags.chunk_id = ext_tags->chunk_id; -+ -+ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); -+ -+ if (dev->data_bytes_per_chunk >= 1024) -+ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; -+ else -+ tags.n_bytes_msb = 3; -+ -+ tags.serial_number = ext_tags->serial_number; -+ -+ if (!dev->param.use_nand_ecc && data) { -+ yaffs_ecc_calc(data, spare.ecc1); -+ yaffs_ecc_calc(&data[256], spare.ecc2); -+ } -+ -+ yaffs_load_tags_to_spare(&spare, &tags); -+ } -+ return yaffs_wr_nand(dev, nand_chunk, data, &spare); -+} -+ -+static int yaffs_tags_compat_rd(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, struct yaffs_ext_tags *ext_tags) -+{ -+ struct yaffs_spare spare; -+ struct yaffs_tags tags; -+ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; -+ static struct yaffs_spare spare_ff; -+ static int init; -+ int deleted; -+ -+ if (!init) { -+ memset(&spare_ff, 0xff, sizeof(spare_ff)); -+ init = 1; -+ } -+ -+ if (!yaffs_rd_chunk_nand(dev, nand_chunk, -+ data, &spare, &ecc_result, 1)) -+ return YAFFS_FAIL; -+ -+ /* ext_tags may be NULL */ -+ if (!ext_tags) -+ return YAFFS_OK; -+ -+ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; -+ -+ ext_tags->is_deleted = deleted; -+ ext_tags->ecc_result = ecc_result; -+ ext_tags->block_bad = 0; /* We're reading it */ -+ /* therefore it is not a bad block */ -+ ext_tags->chunk_used = -+ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; -+ -+ if (ext_tags->chunk_used) { -+ yaffs_get_tags_from_spare(dev, &spare, &tags); -+ ext_tags->obj_id = tags.obj_id; -+ ext_tags->chunk_id = tags.chunk_id; -+ ext_tags->n_bytes = tags.n_bytes_lsb; -+ -+ if (dev->data_bytes_per_chunk >= 1024) -+ ext_tags->n_bytes |= -+ (((unsigned)tags.n_bytes_msb) << 10); -+ -+ ext_tags->serial_number = tags.serial_number; -+ } -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) -+{ -+ struct yaffs_spare spare; -+ -+ memset(&spare, 0xff, sizeof(struct yaffs_spare)); -+ -+ spare.block_status = 'Y'; -+ -+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, -+ &spare); -+ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, -+ NULL, &spare); -+ -+ return YAFFS_OK; -+} -+ -+static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ struct yaffs_spare spare0, spare1; -+ static struct yaffs_spare spare_ff; -+ static int init; -+ enum yaffs_ecc_result dummy; -+ -+ if (!init) { -+ memset(&spare_ff, 0xff, sizeof(spare_ff)); -+ init = 1; -+ } -+ -+ *seq_number = 0; -+ -+ /* Look for bad block markers in the first two chunks */ -+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, -+ NULL, &spare0, &dummy, 0); -+ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, -+ NULL, &spare1, &dummy, 0); -+ -+ if (hweight8(spare0.block_status & spare1.block_status) < 7) -+ *state = YAFFS_BLOCK_STATE_DEAD; -+ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) -+ *state = YAFFS_BLOCK_STATE_EMPTY; -+ else -+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; -+ -+ return YAFFS_OK; -+} -+ -+void yaffs_tags_compat_install(struct yaffs_dev *dev) -+{ -+ if(dev->param.is_yaffs2) -+ return; -+ if(!dev->tagger.write_chunk_tags_fn) -+ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; -+ if(!dev->tagger.read_chunk_tags_fn) -+ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; -+ if(!dev->tagger.query_block_fn) -+ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; -+ if(!dev->tagger.mark_bad_fn) -+ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.4.90/fs/yaffs2/yaffs_tagscompat.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,44 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_TAGSCOMPAT_H__ -+#define __YAFFS_TAGSCOMPAT_H__ -+ -+ -+#include "yaffs_guts.h" -+ -+#if 0 -+ -+ -+int yaffs_tags_compat_wr(struct yaffs_dev *dev, -+ int nand_chunk, -+ const u8 *data, const struct yaffs_ext_tags *tags); -+int yaffs_tags_compat_rd(struct yaffs_dev *dev, -+ int nand_chunk, -+ u8 *data, struct yaffs_ext_tags *tags); -+int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); -+int yaffs_tags_compat_query_block(struct yaffs_dev *dev, -+ int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number); -+ -+#endif -+ -+ -+void yaffs_tags_compat_install(struct yaffs_dev *dev); -+void yaffs_calc_tags_ecc(struct yaffs_tags *tags); -+int yaffs_check_tags_ecc(struct yaffs_tags *tags); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,199 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yaffs_packedtags2.h" -+ -+static int yaffs_tags_marshall_write(struct yaffs_dev *dev, -+ int nand_chunk, const u8 *data, -+ const struct yaffs_ext_tags *tags) -+{ -+ struct yaffs_packed_tags2 pt; -+ int retval; -+ -+ int packed_tags_size = -+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); -+ void *packed_tags_ptr = -+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_tags_marshall_write chunk %d data %p tags %p", -+ nand_chunk, data, tags); -+ -+ /* For yaffs2 writing there must be both data and tags. -+ * If we're using inband tags, then the tags are stuffed into -+ * the end of the data buffer. -+ */ -+ if (!data || !tags) -+ BUG(); -+ else if (dev->param.inband_tags) { -+ struct yaffs_packed_tags2_tags_only *pt2tp; -+ pt2tp = -+ (struct yaffs_packed_tags2_tags_only *)(data + -+ dev-> -+ data_bytes_per_chunk); -+ yaffs_pack_tags2_tags_only(pt2tp, tags); -+ } else { -+ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); -+ } -+ -+ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ (dev->param.inband_tags) ? NULL : packed_tags_ptr, -+ (dev->param.inband_tags) ? 0 : packed_tags_size); -+ -+ return retval; -+} -+ -+static int yaffs_tags_marshall_read(struct yaffs_dev *dev, -+ int nand_chunk, u8 *data, -+ struct yaffs_ext_tags *tags) -+{ -+ int retval = 0; -+ int local_data = 0; -+ u8 spare_buffer[100]; -+ enum yaffs_ecc_result ecc_result; -+ -+ struct yaffs_packed_tags2 pt; -+ -+ int packed_tags_size = -+ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); -+ void *packed_tags_ptr = -+ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "yaffs_tags_marshall_read chunk %d data %p tags %p", -+ nand_chunk, data, tags); -+ -+ if (dev->param.inband_tags) { -+ if (!data) { -+ local_data = 1; -+ data = yaffs_get_temp_buffer(dev); -+ } -+ } -+ -+ if (dev->param.inband_tags || (data && !tags)) -+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ NULL, 0, -+ &ecc_result); -+ else if (tags) -+ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, -+ data, dev->param.total_bytes_per_chunk, -+ spare_buffer, packed_tags_size, -+ &ecc_result); -+ else -+ BUG(); -+ -+ -+ if (dev->param.inband_tags) { -+ if (tags) { -+ struct yaffs_packed_tags2_tags_only *pt2tp; -+ pt2tp = -+ (struct yaffs_packed_tags2_tags_only *) -+ &data[dev->data_bytes_per_chunk]; -+ yaffs_unpack_tags2_tags_only(tags, pt2tp); -+ } -+ } else if (tags) { -+ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); -+ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); -+ } -+ -+ if (local_data) -+ yaffs_release_temp_buffer(dev, data); -+ -+ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { -+ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; -+ dev->n_ecc_unfixed++; -+ } -+ -+ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { -+ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) -+ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; -+ dev->n_ecc_fixed++; -+ } -+ -+ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) -+ return YAFFS_OK; -+ else -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, -+ enum yaffs_block_state *state, -+ u32 *seq_number) -+{ -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", -+ block_no); -+ -+ retval = dev->drv.drv_check_bad_fn(dev, block_no); -+ -+ if (retval== YAFFS_FAIL) { -+ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); -+ -+ *state = YAFFS_BLOCK_STATE_DEAD; -+ *seq_number = 0; -+ } else { -+ struct yaffs_ext_tags t; -+ -+ yaffs_tags_marshall_read(dev, -+ block_no * dev->param.chunks_per_block, -+ NULL, &t); -+ -+ if (t.chunk_used) { -+ *seq_number = t.seq_number; -+ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; -+ } else { -+ *seq_number = 0; -+ *state = YAFFS_BLOCK_STATE_EMPTY; -+ } -+ } -+ -+ yaffs_trace(YAFFS_TRACE_MTD, -+ "block query returns seq %d state %d", -+ *seq_number, *state); -+ -+ if (retval == 0) -+ return YAFFS_OK; -+ else -+ return YAFFS_FAIL; -+} -+ -+static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) -+{ -+ return dev->drv.drv_mark_bad_fn(dev, block_no); -+ -+} -+ -+ -+void yaffs_tags_marshall_install(struct yaffs_dev *dev) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!dev->tagger.write_chunk_tags_fn) -+ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; -+ -+ if (!dev->tagger.read_chunk_tags_fn) -+ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; -+ -+ if (!dev->tagger.query_block_fn) -+ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; -+ -+ if (!dev->tagger.mark_bad_fn) -+ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; -+ -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,22 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_TAGSMARSHALL_H__ -+#define __YAFFS_TAGSMARSHALL_H__ -+ -+#include "yaffs_guts.h" -+void yaffs_tags_marshall_install(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_trace.h linux-3.4.90/fs/yaffs2/yaffs_trace.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_trace.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,57 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YTRACE_H__ -+#define __YTRACE_H__ -+ -+extern unsigned int yaffs_trace_mask; -+extern unsigned int yaffs_wr_attempts; -+ -+/* -+ * Tracing flags. -+ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. -+ */ -+ -+#define YAFFS_TRACE_OS 0x00000002 -+#define YAFFS_TRACE_ALLOCATE 0x00000004 -+#define YAFFS_TRACE_SCAN 0x00000008 -+#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 -+#define YAFFS_TRACE_ERASE 0x00000020 -+#define YAFFS_TRACE_GC 0x00000040 -+#define YAFFS_TRACE_WRITE 0x00000080 -+#define YAFFS_TRACE_TRACING 0x00000100 -+#define YAFFS_TRACE_DELETION 0x00000200 -+#define YAFFS_TRACE_BUFFERS 0x00000400 -+#define YAFFS_TRACE_NANDACCESS 0x00000800 -+#define YAFFS_TRACE_GC_DETAIL 0x00001000 -+#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 -+#define YAFFS_TRACE_MTD 0x00004000 -+#define YAFFS_TRACE_CHECKPOINT 0x00008000 -+ -+#define YAFFS_TRACE_VERIFY 0x00010000 -+#define YAFFS_TRACE_VERIFY_NAND 0x00020000 -+#define YAFFS_TRACE_VERIFY_FULL 0x00040000 -+#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 -+ -+#define YAFFS_TRACE_SYNC 0x00100000 -+#define YAFFS_TRACE_BACKGROUND 0x00200000 -+#define YAFFS_TRACE_LOCK 0x00400000 -+#define YAFFS_TRACE_MOUNT 0x00800000 -+ -+#define YAFFS_TRACE_ERROR 0x40000000 -+#define YAFFS_TRACE_BUG 0x80000000 -+#define YAFFS_TRACE_ALWAYS 0xf0000000 -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_verify.c linux-3.4.90/fs/yaffs2/yaffs_verify.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_verify.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,529 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_verify.h" -+#include "yaffs_trace.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_nand.h" -+ -+int yaffs_skip_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & -+ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); -+} -+ -+static int yaffs_skip_full_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); -+} -+ -+static int yaffs_skip_nand_verification(struct yaffs_dev *dev) -+{ -+ (void) dev; -+ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); -+} -+ -+static const char * const block_state_name[] = { -+ "Unknown", -+ "Needs scan", -+ "Scanning", -+ "Empty", -+ "Allocating", -+ "Full", -+ "Dirty", -+ "Checkpoint", -+ "Collecting", -+ "Dead" -+}; -+ -+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) -+{ -+ int actually_used; -+ int in_use; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Report illegal runtime states */ -+ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has undefined state %d", -+ n, bi->block_state); -+ -+ switch (bi->block_state) { -+ case YAFFS_BLOCK_STATE_UNKNOWN: -+ case YAFFS_BLOCK_STATE_SCANNING: -+ case YAFFS_BLOCK_STATE_NEEDS_SCAN: -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has bad run-state %s", -+ n, block_state_name[bi->block_state]); -+ } -+ -+ /* Check pages in use and soft deletions are legal */ -+ -+ actually_used = bi->pages_in_use - bi->soft_del_pages; -+ -+ if (bi->pages_in_use < 0 || -+ bi->pages_in_use > dev->param.chunks_per_block || -+ bi->soft_del_pages < 0 || -+ bi->soft_del_pages > dev->param.chunks_per_block || -+ actually_used < 0 || actually_used > dev->param.chunks_per_block) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has illegal values pages_in_used %d soft_del_pages %d", -+ n, bi->pages_in_use, bi->soft_del_pages); -+ -+ /* Check chunk bitmap legal */ -+ in_use = yaffs_count_chunk_bits(dev, n); -+ if (in_use != bi->pages_in_use) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", -+ n, bi->pages_in_use, in_use); -+} -+ -+void yaffs_verify_collected_blk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, int n) -+{ -+ yaffs_verify_blk(dev, bi, n); -+ -+ /* After collection the block should be in the erased state */ -+ -+ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && -+ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Block %d is in state %d after gc, should be erased", -+ n, bi->block_state); -+ } -+} -+ -+void yaffs_verify_blocks(struct yaffs_dev *dev) -+{ -+ int i; -+ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; -+ int illegal_states = 0; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ memset(state_count, 0, sizeof(state_count)); -+ -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); -+ yaffs_verify_blk(dev, bi, i); -+ -+ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) -+ state_count[bi->block_state]++; -+ else -+ illegal_states++; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); -+ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "%d blocks have illegal states", -+ illegal_states); -+ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Too many allocating blocks"); -+ -+ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "%s %d blocks", -+ block_state_name[i], state_count[i]); -+ -+ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Checkpoint block count wrong dev %d count %d", -+ dev->blocks_in_checkpt, -+ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); -+ -+ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Erased block count wrong dev %d count %d", -+ dev->n_erased_blocks, -+ state_count[YAFFS_BLOCK_STATE_EMPTY]); -+ -+ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Too many collecting blocks %d (max is 1)", -+ state_count[YAFFS_BLOCK_STATE_COLLECTING]); -+} -+ -+/* -+ * Verify the object header. oh must be valid, but obj and tags may be NULL in -+ * which case those tests will not be performed. -+ */ -+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, -+ struct yaffs_ext_tags *tags, int parent_check) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ if (!(tags && obj && oh)) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Verifying object header tags %p obj %p oh %p", -+ tags, obj, oh); -+ return; -+ } -+ -+ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || -+ oh->type > YAFFS_OBJECT_TYPE_MAX) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header type is illegal value 0x%x", -+ tags->obj_id, oh->type); -+ -+ if (tags->obj_id != obj->obj_id) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch obj_id %d", -+ tags->obj_id, obj->obj_id); -+ -+ /* -+ * Check that the object's parent ids match if parent_check requested. -+ * -+ * Tests do not apply to the root object. -+ */ -+ -+ if (parent_check && tags->obj_id > 1 && !obj->parent) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch parent_id %d obj->parent is NULL", -+ tags->obj_id, oh->parent_obj_id); -+ -+ if (parent_check && obj->parent && -+ oh->parent_obj_id != obj->parent->obj_id && -+ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || -+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header mismatch parent_id %d parent_obj_id %d", -+ tags->obj_id, oh->parent_obj_id, -+ obj->parent->obj_id); -+ -+ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header name is NULL", -+ obj->obj_id); -+ -+ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d header name is 0xff", -+ obj->obj_id); -+} -+ -+void yaffs_verify_file(struct yaffs_obj *obj) -+{ -+ u32 x; -+ int required_depth; -+ int actual_depth; -+ int last_chunk; -+ u32 offset_in_chunk; -+ u32 the_chunk; -+ -+ u32 i; -+ struct yaffs_dev *dev; -+ struct yaffs_ext_tags tags; -+ struct yaffs_tnode *tn; -+ u32 obj_id; -+ -+ if (!obj) -+ return; -+ -+ if (yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ dev = obj->my_dev; -+ obj_id = obj->obj_id; -+ -+ -+ /* Check file size is consistent with tnode depth */ -+ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, -+ &last_chunk, &offset_in_chunk); -+ last_chunk++; -+ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; -+ required_depth = 0; -+ while (x > 0) { -+ x >>= YAFFS_TNODES_INTERNAL_BITS; -+ required_depth++; -+ } -+ -+ actual_depth = obj->variant.file_variant.top_level; -+ -+ /* Check that the chunks in the tnode tree are all correct. -+ * We do this by scanning through the tnode tree and -+ * checking the tags for every chunk match. -+ */ -+ -+ if (yaffs_skip_nand_verification(dev)) -+ return; -+ -+ for (i = 1; i <= last_chunk; i++) { -+ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); -+ -+ if (!tn) -+ continue; -+ -+ the_chunk = yaffs_get_group_base(dev, tn, i); -+ if (the_chunk > 0) { -+ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, -+ &tags); -+ if (tags.obj_id != obj_id || tags.chunk_id != i) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", -+ obj_id, i, the_chunk, -+ tags.obj_id, tags.chunk_id); -+ } -+ } -+} -+ -+void yaffs_verify_link(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ /* Verify sane equivalent object */ -+} -+ -+void yaffs_verify_symlink(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ /* Verify symlink string */ -+} -+ -+void yaffs_verify_special(struct yaffs_obj *obj) -+{ -+ if (obj && yaffs_skip_verification(obj->my_dev)) -+ return; -+} -+ -+void yaffs_verify_obj(struct yaffs_obj *obj) -+{ -+ struct yaffs_dev *dev; -+ u32 chunk_min; -+ u32 chunk_max; -+ u32 chunk_id_ok; -+ u32 chunk_in_range; -+ u32 chunk_wrongly_deleted; -+ u32 chunk_valid; -+ -+ if (!obj) -+ return; -+ -+ if (obj->being_created) -+ return; -+ -+ dev = obj->my_dev; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Check sane object header chunk */ -+ -+ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; -+ chunk_max = -+ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; -+ -+ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && -+ ((unsigned)(obj->hdr_chunk)) <= chunk_max); -+ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); -+ chunk_valid = chunk_in_range && -+ yaffs_check_chunk_bit(dev, -+ obj->hdr_chunk / dev->param.chunks_per_block, -+ obj->hdr_chunk % dev->param.chunks_per_block); -+ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; -+ -+ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has chunk_id %d %s %s", -+ obj->obj_id, obj->hdr_chunk, -+ chunk_id_ok ? "" : ",out of range", -+ chunk_wrongly_deleted ? ",marked as deleted" : ""); -+ -+ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { -+ struct yaffs_ext_tags tags; -+ struct yaffs_obj_hdr *oh; -+ u8 *buffer = yaffs_get_temp_buffer(dev); -+ -+ oh = (struct yaffs_obj_hdr *)buffer; -+ -+ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); -+ -+ yaffs_verify_oh(obj, oh, &tags, 1); -+ -+ yaffs_release_temp_buffer(dev, buffer); -+ } -+ -+ /* Verify it has a parent */ -+ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has parent pointer %p which does not look like an object", -+ obj->obj_id, obj->parent); -+ } -+ -+ /* Verify parent is a directory */ -+ if (obj->parent && -+ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d's parent is not a directory (type %d)", -+ obj->obj_id, obj->parent->variant_type); -+ } -+ -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ yaffs_verify_file(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ yaffs_verify_symlink(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ yaffs_verify_dir(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ yaffs_verify_link(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ yaffs_verify_special(obj); -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ default: -+ yaffs_trace(YAFFS_TRACE_VERIFY, -+ "Obj %d has illegaltype %d", -+ obj->obj_id, obj->variant_type); -+ break; -+ } -+} -+ -+void yaffs_verify_objects(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ int i; -+ struct list_head *lh; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each(lh, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ yaffs_verify_obj(obj); -+ } -+ } -+} -+ -+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) -+{ -+ struct list_head *lh; -+ struct yaffs_obj *list_obj; -+ int count = 0; -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); -+ BUG(); -+ return; -+ } -+ -+ if (yaffs_skip_verification(obj->my_dev)) -+ return; -+ -+ if (!obj->parent) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); -+ BUG(); -+ return; -+ } -+ -+ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); -+ BUG(); -+ } -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ list_for_each(lh, &obj->parent->variant.dir_variant.children) { -+ list_obj = list_entry(lh, struct yaffs_obj, siblings); -+ yaffs_verify_obj(list_obj); -+ if (obj == list_obj) -+ count++; -+ } -+ -+ if (count != 1) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Object in directory %d times", -+ count); -+ BUG(); -+ } -+} -+ -+void yaffs_verify_dir(struct yaffs_obj *directory) -+{ -+ struct list_head *lh; -+ struct yaffs_obj *list_obj; -+ -+ if (!directory) { -+ BUG(); -+ return; -+ } -+ -+ if (yaffs_skip_full_verification(directory->my_dev)) -+ return; -+ -+ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Directory has wrong type: %d", -+ directory->variant_type); -+ BUG(); -+ } -+ -+ /* Iterate through the objects in each hash entry */ -+ -+ list_for_each(lh, &directory->variant.dir_variant.children) { -+ list_obj = list_entry(lh, struct yaffs_obj, siblings); -+ if (list_obj->parent != directory) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Object in directory list has wrong parent %p", -+ list_obj->parent); -+ BUG(); -+ } -+ yaffs_verify_obj_in_dir(list_obj); -+ } -+} -+ -+static int yaffs_free_verification_failures; -+ -+void yaffs_verify_free_chunks(struct yaffs_dev *dev) -+{ -+ int counted; -+ int difference; -+ -+ if (yaffs_skip_verification(dev)) -+ return; -+ -+ counted = yaffs_count_free_chunks(dev); -+ -+ difference = dev->n_free_chunks - counted; -+ -+ if (difference) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Freechunks verification failure %d %d %d", -+ dev->n_free_chunks, counted, difference); -+ yaffs_free_verification_failures++; -+ } -+} -+ -+int yaffs_verify_file_sane(struct yaffs_obj *in) -+{ -+ (void) in; -+ return YAFFS_OK; -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_verify.h linux-3.4.90/fs/yaffs2/yaffs_verify.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_verify.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,43 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_VERIFY_H__ -+#define __YAFFS_VERIFY_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, -+ int n); -+void yaffs_verify_collected_blk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, int n); -+void yaffs_verify_blocks(struct yaffs_dev *dev); -+ -+void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, -+ struct yaffs_ext_tags *tags, int parent_check); -+void yaffs_verify_file(struct yaffs_obj *obj); -+void yaffs_verify_link(struct yaffs_obj *obj); -+void yaffs_verify_symlink(struct yaffs_obj *obj); -+void yaffs_verify_special(struct yaffs_obj *obj); -+void yaffs_verify_obj(struct yaffs_obj *obj); -+void yaffs_verify_objects(struct yaffs_dev *dev); -+void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); -+void yaffs_verify_dir(struct yaffs_obj *directory); -+void yaffs_verify_free_chunks(struct yaffs_dev *dev); -+ -+int yaffs_verify_file_sane(struct yaffs_obj *obj); -+ -+int yaffs_skip_verification(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_vfs.c linux-3.4.90/fs/yaffs2/yaffs_vfs.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_vfs.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,3600 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * Acknowledgements: -+ * Luc van OostenRyck for numerous patches. -+ * Nick Bane for numerous patches. -+ * Nick Bane for 2.5/2.6 integration. -+ * Andras Toth for mknod rdev issue. -+ * Michael Fischer for finding the problem with inode inconsistency. -+ * Some code bodily lifted from JFFS -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+/* -+ * -+ * This is the file system front-end to YAFFS that hooks it up to -+ * the VFS. -+ * -+ * Special notes: -+ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with -+ * this superblock -+ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this -+ * superblock -+ * >> inode->u.generic_ip points to the associated struct yaffs_obj. -+ */ -+ -+/* -+ * There are two variants of the VFS glue code. This variant should compile -+ * for any version of Linux. -+ */ -+#include -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) -+#define YAFFS_COMPILE_BACKGROUND -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) -+#define YAFFS_COMPILE_FREEZER -+#endif -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) -+#define YAFFS_COMPILE_EXPORTFS -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) -+#define YAFFS_USE_SETATTR_COPY -+#define YAFFS_USE_TRUNCATE_SETSIZE -+#endif -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) -+#define YAFFS_HAS_EVICT_INODE -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) -+#define YAFFS_NEW_FOLLOW_LINK 1 -+#else -+#define YAFFS_NEW_FOLLOW_LINK 0 -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) -+#define YAFFS_HAS_WRITE_SUPER -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+#include -+#endif -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+#include -+#endif -+ -+#ifdef YAFFS_COMPILE_BACKGROUND -+#include -+#include -+#endif -+#ifdef YAFFS_COMPILE_FREEZER -+#include -+#endif -+ -+#include -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ -+#include -+ -+#define UnlockPage(p) unlock_page(p) -+#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) -+ -+/* FIXME: use sb->s_id instead ? */ -+#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) -+ -+#else -+ -+#include -+#define BDEVNAME_SIZE 0 -+#define yaffs_devname(sb, buf) kdevname(sb->s_dev) -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) -+/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ -+#define __user -+#endif -+ -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) -+#define YPROC_ROOT (&proc_root) -+#else -+#define YPROC_ROOT NULL -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) -+#define Y_INIT_TIMER(a) init_timer(a) -+#else -+#define Y_INIT_TIMER(a) init_timer_on_stack(a) -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) -+#define YAFFS_USE_WRITE_BEGIN_END 1 -+#else -+#define YAFFS_USE_WRITE_BEGIN_END 0 -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) -+#define YAFFS_SUPER_HAS_DIRTY -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) -+#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) -+static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) -+{ -+ uint64_t result = partition_size; -+ do_div(result, block_size); -+ return (uint32_t) result; -+} -+#else -+#define YCALCBLOCKS(s, b) ((s)/(b)) -+#endif -+ -+#include -+#include -+ -+#include "yportenv.h" -+#include "yaffs_trace.h" -+#include "yaffs_guts.h" -+#include "yaffs_attribs.h" -+ -+#include "yaffs_linux.h" -+ -+#include "yaffs_mtdif.h" -+#include "yaffs_packedtags2.h" -+#include "yaffs_getblockinfo.h" -+ -+unsigned int yaffs_trace_mask = -+ YAFFS_TRACE_BAD_BLOCKS | -+ YAFFS_TRACE_ALWAYS | -+ 0; -+ -+unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; -+unsigned int yaffs_auto_checkpoint = 1; -+unsigned int yaffs_gc_control = 1; -+unsigned int yaffs_bg_enable = 1; -+unsigned int yaffs_auto_select = 1; -+/* Module Parameters */ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+module_param(yaffs_trace_mask, uint, 0644); -+module_param(yaffs_wr_attempts, uint, 0644); -+module_param(yaffs_auto_checkpoint, uint, 0644); -+module_param(yaffs_gc_control, uint, 0644); -+module_param(yaffs_bg_enable, uint, 0644); -+#else -+MODULE_PARM(yaffs_trace_mask, "i"); -+MODULE_PARM(yaffs_wr_attempts, "i"); -+MODULE_PARM(yaffs_auto_checkpoint, "i"); -+MODULE_PARM(yaffs_gc_control, "i"); -+#endif -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) -+/* use iget and read_inode */ -+#define Y_IGET(sb, inum) iget((sb), (inum)) -+ -+#else -+/* Call local equivalent */ -+#define YAFFS_USE_OWN_IGET -+#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) -+ -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) -+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) -+#else -+#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) -+#endif -+ -+#define yaffs_inode_to_obj(iptr) \ -+ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) -+#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) -+#else -+#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+#define Y_CLEAR_INODE(i) clear_inode(i) -+#else -+#define Y_CLEAR_INODE(i) end_writeback(i) -+#endif -+ -+ -+#define update_dir_time(dir) do {\ -+ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ -+ } while (0) -+ -+static void yaffs_fill_inode_from_obj(struct inode *inode, -+ struct yaffs_obj *obj); -+ -+ -+static void yaffs_gross_lock(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); -+ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); -+} -+ -+static void yaffs_gross_unlock(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); -+ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); -+} -+ -+ -+static int yaffs_readpage_nolock(struct file *f, struct page *pg) -+{ -+ /* Lifted from jffs2 */ -+ -+ struct yaffs_obj *obj; -+ unsigned char *pg_buf; -+ int ret; -+ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readpage_nolock at %lld, size %08x", -+ (long long)pos, -+ (unsigned)PAGE_CACHE_SIZE); -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ BUG_ON(!PageLocked(pg)); -+#else -+ if (!PageLocked(pg)) -+ PAGE_BUG(pg); -+#endif -+ -+ pg_buf = kmap(pg); -+ /* FIXME: Can kmap fail? */ -+ -+ yaffs_gross_lock(dev); -+ -+ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (ret >= 0) -+ ret = 0; -+ -+ if (ret) { -+ ClearPageUptodate(pg); -+ SetPageError(pg); -+ } else { -+ SetPageUptodate(pg); -+ ClearPageError(pg); -+ } -+ -+ flush_dcache_page(pg); -+ kunmap(pg); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); -+ return ret; -+} -+ -+static int yaffs_readpage_unlock(struct file *f, struct page *pg) -+{ -+ int ret = yaffs_readpage_nolock(f, pg); -+ UnlockPage(pg); -+ return ret; -+} -+ -+static int yaffs_readpage(struct file *f, struct page *pg) -+{ -+ int ret; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); -+ ret = yaffs_readpage_unlock(f, pg); -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); -+ return ret; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) -+#define YCRED_FSUID() from_kuid(&init_user_ns, current_fsuid()) -+#define YCRED_FSGID() from_kgid(&init_user_ns, current_fsgid()) -+#else -+#define YCRED_FSUID() YCRED(current)->fsuid -+#define YCRED_FSGID() YCRED(current)->fsgid -+ -+static inline uid_t i_uid_read(const struct inode *inode) -+{ -+ return inode->i_uid; -+} -+ -+static inline gid_t i_gid_read(const struct inode *inode) -+{ -+ return inode->i_gid; -+} -+ -+static inline void i_uid_write(struct inode *inode, uid_t uid) -+{ -+ inode->i_uid = uid; -+} -+ -+static inline void i_gid_write(struct inode *inode, gid_t gid) -+{ -+ inode->i_gid = gid; -+} -+#endif -+ -+static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) -+{ -+ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); -+ -+ if (lc) -+ lc->dirty = val; -+ -+# ifdef YAFFS_SUPER_HAS_DIRTY -+ { -+ struct super_block *sb = lc->super; -+ -+ if (sb) -+ sb->s_dirt = val; -+ } -+#endif -+ -+} -+ -+static void yaffs_set_super_dirty(struct yaffs_dev *dev) -+{ -+ yaffs_set_super_dirty_val(dev, 1); -+} -+ -+static void yaffs_clear_super_dirty(struct yaffs_dev *dev) -+{ -+ yaffs_set_super_dirty_val(dev, 0); -+} -+ -+static int yaffs_check_super_dirty(struct yaffs_dev *dev) -+{ -+ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); -+ -+ if (lc && lc->dirty) -+ return 1; -+ -+# ifdef YAFFS_SUPER_HAS_DIRTY -+ { -+ struct super_block *sb = lc->super; -+ -+ if (sb && sb->s_dirt) -+ return 1; -+ } -+#endif -+ return 0; -+ -+} -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_writepage(struct page *page, struct writeback_control *wbc) -+#else -+static int yaffs_writepage(struct page *page) -+#endif -+{ -+ struct yaffs_dev *dev; -+ struct address_space *mapping = page->mapping; -+ struct inode *inode; -+ unsigned long end_index; -+ char *buffer; -+ struct yaffs_obj *obj; -+ int n_written = 0; -+ unsigned n_bytes; -+ loff_t i_size; -+ -+ if (!mapping) -+ BUG(); -+ inode = mapping->host; -+ if (!inode) -+ BUG(); -+ i_size = i_size_read(inode); -+ -+ end_index = i_size >> PAGE_CACHE_SHIFT; -+ -+ if (page->index < end_index) -+ n_bytes = PAGE_CACHE_SIZE; -+ else { -+ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); -+ -+ if (page->index > end_index || !n_bytes) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_writepage at %lld, inode size = %lld!!", -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, -+ inode->i_size); -+ yaffs_trace(YAFFS_TRACE_OS, -+ " -> don't care!!"); -+ -+ zero_user_segment(page, 0, PAGE_CACHE_SIZE); -+ set_page_writeback(page); -+ unlock_page(page); -+ end_page_writeback(page); -+ return 0; -+ } -+ } -+ -+ if (n_bytes != PAGE_CACHE_SIZE) -+ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); -+ -+ get_page(page); -+ -+ buffer = kmap(page); -+ -+ obj = yaffs_inode_to_obj(inode); -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_writepage at %lld, size %08x", -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "writepag0: obj = %lld, ino = %lld", -+ obj->variant.file_variant.file_size, inode->i_size); -+ -+ n_written = yaffs_wr_file(obj, buffer, -+ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); -+ -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "writepag1: obj = %lld, ino = %lld", -+ obj->variant.file_variant.file_size, inode->i_size); -+ -+ yaffs_gross_unlock(dev); -+ -+ kunmap(page); -+ set_page_writeback(page); -+ unlock_page(page); -+ end_page_writeback(page); -+ put_page(page); -+ -+ return (n_written == n_bytes) ? 0 : -ENOSPC; -+} -+ -+/* Space holding and freeing is done to ensure we have space available for write_begin/end */ -+/* For now we just assume few parallel writes and check against a small number. */ -+/* Todo: need to do this with a counter to handle parallel reads better */ -+ -+static ssize_t yaffs_hold_space(struct file *f) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ int n_free_chunks; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ n_free_chunks = yaffs_get_n_free_chunks(dev); -+ -+ yaffs_gross_unlock(dev); -+ -+ return (n_free_chunks > 20) ? 1 : 0; -+} -+ -+static void yaffs_release_space(struct file *f) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_gross_unlock(dev); -+} -+ -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+static int yaffs_write_begin(struct file *filp, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned flags, -+ struct page **pagep, void **fsdata) -+{ -+ struct page *pg = NULL; -+ pgoff_t index = pos >> PAGE_CACHE_SHIFT; -+ -+ int ret = 0; -+ int space_held = 0; -+ -+ /* Get a page */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) -+ pg = grab_cache_page_write_begin(mapping, index, flags); -+#else -+ pg = __grab_cache_page(mapping, index); -+#endif -+ -+ *pagep = pg; -+ if (!pg) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "start yaffs_write_begin index %d(%x) uptodate %d", -+ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); -+ -+ /* Get fs space */ -+ space_held = yaffs_hold_space(filp); -+ -+ if (!space_held) { -+ ret = -ENOSPC; -+ goto out; -+ } -+ -+ /* Update page if required */ -+ -+ if (!Page_Uptodate(pg)) -+ ret = yaffs_readpage_nolock(filp, pg); -+ -+ if (ret) -+ goto out; -+ -+ /* Happy path return */ -+ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); -+ -+ return 0; -+ -+out: -+ yaffs_trace(YAFFS_TRACE_OS, -+ "end yaffs_write_begin fail returning %d", ret); -+ if (space_held) -+ yaffs_release_space(filp); -+ if (pg) { -+ unlock_page(pg); -+ page_cache_release(pg); -+ } -+ return ret; -+} -+ -+#else -+ -+static int yaffs_prepare_write(struct file *f, struct page *pg, -+ unsigned offset, unsigned to) -+{ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); -+ -+ if (!Page_Uptodate(pg)) -+ return yaffs_readpage_nolock(f, pg); -+ return 0; -+} -+#endif -+ -+ -+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, -+ loff_t * pos) -+{ -+ struct yaffs_obj *obj; -+ int n_written; -+ loff_t ipos; -+ struct inode *inode; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write: hey obj is null!"); -+ return -EINVAL; -+ } -+ -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ inode = f->f_dentry->d_inode; -+ -+ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) -+ ipos = inode->i_size; -+ else -+ ipos = *pos; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", -+ (unsigned)n, (unsigned)n, obj->obj_id, ipos); -+ -+ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); -+ -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write: %d(%x) bytes written", -+ (unsigned)n, (unsigned)n); -+ -+ if (n_written > 0) { -+ ipos += n_written; -+ *pos = ipos; -+ if (ipos > inode->i_size) { -+ inode->i_size = ipos; -+ inode->i_blocks = (ipos + 511) >> 9; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_write size updated to %lld bytes, %d blocks", -+ ipos, (int)(inode->i_blocks)); -+ } -+ -+ } -+ yaffs_gross_unlock(dev); -+ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; -+} -+ -+ -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+static int yaffs_write_end(struct file *filp, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned copied, -+ struct page *pg, void *fsdadata) -+{ -+ int ret = 0; -+ void *addr, *kva; -+ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); -+ -+ kva = kmap(pg); -+ addr = kva + offset_into_page; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_write_end addr %p pos %lld n_bytes %d", -+ addr, pos, copied); -+ -+ ret = yaffs_file_write(filp, addr, copied, &pos); -+ -+ if (ret != copied) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_write_end not same size ret %d copied %d", -+ ret, copied); -+ SetPageError(pg); -+ } -+ -+ kunmap(pg); -+ -+ yaffs_release_space(filp); -+ unlock_page(pg); -+ page_cache_release(pg); -+ return ret; -+} -+#else -+ -+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, -+ unsigned to) -+{ -+ void *addr, *kva; -+ -+ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; -+ int n_bytes = to - offset; -+ int n_written; -+ -+ kva = kmap(pg); -+ addr = kva + offset; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write addr %p pos %lld n_bytes %d", -+ addr, pos, n_bytes); -+ -+ n_written = yaffs_file_write(f, addr, n_bytes, &pos); -+ -+ if (n_written != n_bytes) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write not same size n_written %d n_bytes %d", -+ n_written, n_bytes); -+ SetPageError(pg); -+ } -+ kunmap(pg); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_commit_write returning %d", -+ n_written == n_bytes ? 0 : n_written); -+ -+ return n_written == n_bytes ? 0 : n_written; -+} -+#endif -+ -+static struct address_space_operations yaffs_file_address_operations = { -+ .readpage = yaffs_readpage, -+ .writepage = yaffs_writepage, -+#if (YAFFS_USE_WRITE_BEGIN_END > 0) -+ .write_begin = yaffs_write_begin, -+ .write_end = yaffs_write_end, -+#else -+ .prepare_write = yaffs_prepare_write, -+ .commit_write = yaffs_commit_write, -+#endif -+}; -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_file_flush(struct file *file, fl_owner_t id) -+#else -+static int yaffs_file_flush(struct file *file) -+#endif -+{ -+ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); -+ -+ struct yaffs_dev *dev = obj->my_dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_file_flush object %d (%s)", -+ obj->obj_id, -+ obj->dirty ? "dirty" : "clean"); -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_flush_file(obj, 1, 0); -+ -+ yaffs_gross_unlock(dev); -+ -+ return 0; -+} -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) -+static int yaffs_sync_object(struct file *file, int datasync) -+#else -+static int yaffs_sync_object(struct file *file, struct dentry *dentry, -+ int datasync) -+#endif -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) -+ struct dentry *dentry = file->f_path.dentry; -+#endif -+ -+ obj = yaffs_dentry_to_obj(dentry); -+ -+ dev = obj->my_dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, -+ "yaffs_sync_object"); -+ yaffs_gross_lock(dev); -+ yaffs_flush_file(obj, 1, datasync); -+ yaffs_gross_unlock(dev); -+ return 0; -+} -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) -+static const struct file_operations yaffs_file_operations = { -+ .read = do_sync_read, -+ .write = do_sync_write, -+ .aio_read = generic_file_aio_read, -+ .aio_write = generic_file_aio_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+ .splice_read = generic_file_splice_read, -+ .splice_write = generic_file_splice_write, -+ .llseek = generic_file_llseek, -+}; -+ -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) -+ -+static const struct file_operations yaffs_file_operations = { -+ .read = do_sync_read, -+ .write = do_sync_write, -+ .aio_read = generic_file_aio_read, -+ .aio_write = generic_file_aio_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+ .sendfile = generic_file_sendfile, -+}; -+ -+#else -+ -+static const struct file_operations yaffs_file_operations = { -+ .read = generic_file_read, -+ .write = generic_file_write, -+ .mmap = generic_file_mmap, -+ .flush = yaffs_file_flush, -+ .fsync = yaffs_sync_object, -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ .sendfile = generic_file_sendfile, -+#endif -+}; -+#endif -+ -+ -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) -+static void zero_user_segment(struct page *page, unsigned start, unsigned end) -+{ -+ void *kaddr = kmap_atomic(page, KM_USER0); -+ memset(kaddr + start, 0, end - start); -+ kunmap_atomic(kaddr, KM_USER0); -+ flush_dcache_page(page); -+} -+#endif -+ -+ -+static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) -+{ -+#ifdef YAFFS_USE_TRUNCATE_SETSIZE -+ truncate_setsize(inode, newsize); -+ return 0; -+#else -+ truncate_inode_pages(&inode->i_data, newsize); -+ return 0; -+#endif -+ -+} -+ -+ -+static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) -+{ -+#ifdef YAFFS_USE_SETATTR_COPY -+ setattr_copy(inode, attr); -+ return 0; -+#else -+ return inode_setattr(inode, attr); -+#endif -+ -+} -+ -+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_setattr of object %d", -+ yaffs_inode_to_obj(inode)->obj_id); -+#if 0 -+ /* Fail if a requested resize >= 2GB */ -+ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) -+ error = -EINVAL; -+#endif -+ -+ if (error == 0) -+ error = inode_change_ok(inode, attr); -+ if (error == 0) { -+ int result; -+ if (!error) { -+ error = yaffs_vfs_setattr(inode, attr); -+ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); -+ if (attr->ia_valid & ATTR_SIZE) { -+ yaffs_vfs_setsize(inode, attr->ia_size); -+ inode->i_blocks = (inode->i_size + 511) >> 9; -+ } -+ } -+ dev = yaffs_inode_to_obj(inode)->my_dev; -+ if (attr->ia_valid & ATTR_SIZE) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "resize to %d(%x)", -+ (int)(attr->ia_size), -+ (int)(attr->ia_size)); -+ } -+ yaffs_gross_lock(dev); -+ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); -+ if (result == YAFFS_OK) { -+ error = 0; -+ } else { -+ error = -EPERM; -+ } -+ yaffs_gross_unlock(dev); -+ -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); -+ -+ return error; -+} -+ -+static int yaffs_setxattr(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ int result; -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ result = yaffs_set_xattrib(obj, name, value, size, flags); -+ if (result == YAFFS_OK) -+ error = 0; -+ else if (result < 0) -+ error = result; -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); -+ -+ return error; -+} -+ -+static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, -+ void *buff, size_t size) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_getxattr \"%s\" from object %d", -+ name, obj->obj_id); -+ -+ if (error == 0) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ error = yaffs_get_xattrib(obj, name, buff, size); -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); -+ -+ return error; -+} -+ -+static int yaffs_removexattr(struct dentry *dentry, const char *name) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_removexattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ int result; -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ result = yaffs_remove_xattrib(obj, name); -+ if (result == YAFFS_OK) -+ error = 0; -+ else if (result < 0) -+ error = result; -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_removexattr done returning %d", error); -+ -+ return error; -+} -+ -+static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) -+{ -+ struct inode *inode = dentry->d_inode; -+ int error = 0; -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_listxattr of object %d", obj->obj_id); -+ -+ if (error == 0) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ error = yaffs_list_xattrib(obj, buff, size); -+ yaffs_gross_unlock(dev); -+ -+ } -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_listxattr done returning %d", error); -+ -+ return error; -+} -+ -+ -+static const struct inode_operations yaffs_file_inode_operations = { -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+ -+static int yaffs_readlink(struct dentry *dentry, char __user * buffer, -+ int buflen) -+{ -+ unsigned char *alias; -+ int ret; -+ -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (!alias) -+ return -ENOMEM; -+ -+ ret = vfs_readlink(dentry, buffer, buflen, alias); -+ kfree(alias); -+ return ret; -+} -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ void *ret; -+#else -+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ int ret -+#endif -+ unsigned char *alias; -+ int ret_int = 0; -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); -+ yaffs_gross_unlock(dev); -+ -+ if (!alias) { -+ ret_int = -ENOMEM; -+ goto out; -+ } -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+ nd_set_link(nd, alias); -+ ret = alias; -+out: -+ if (ret_int) -+ ret = ERR_PTR(ret_int); -+ return ret; -+#else -+ ret = vfs_follow_link(nd, alias); -+ kfree(alias); -+out: -+ if (ret_int) -+ ret = ret_int; -+ return ret; -+#endif -+} -+ -+ -+#ifdef YAFFS_HAS_PUT_INODE -+ -+/* For now put inode is just for debugging -+ * Put inode is called when the inode **structure** is put. -+ */ -+static void yaffs_put_inode(struct inode *inode) -+{ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_put_inode: ino %d, count %d"), -+ (int)inode->i_ino, atomic_read(&inode->i_count); -+ -+} -+#endif -+ -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) -+{ -+ kfree(alias); -+} -+#endif -+ -+static const struct inode_operations yaffs_symlink_inode_operations = { -+ .readlink = yaffs_readlink, -+ .follow_link = yaffs_follow_link, -+#if (YAFFS_NEW_FOLLOW_LINK == 1) -+ .put_link = yaffs_put_link, -+#endif -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+#ifdef YAFFS_USE_OWN_IGET -+ -+static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) -+{ -+ struct inode *inode; -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); -+ -+ inode = iget_locked(sb, ino); -+ if (!inode) -+ return ERR_PTR(-ENOMEM); -+ if (!(inode->i_state & I_NEW)) -+ return inode; -+ -+ /* NB This is called as a side effect of other functions, but -+ * we had to release the lock to prevent deadlocks, so -+ * need to lock again. -+ */ -+ -+ yaffs_gross_lock(dev); -+ -+ obj = yaffs_find_by_number(dev, inode->i_ino); -+ -+ yaffs_fill_inode_from_obj(inode, obj); -+ -+ yaffs_gross_unlock(dev); -+ -+ unlock_new_inode(inode); -+ return inode; -+} -+ -+#else -+ -+static void yaffs_read_inode(struct inode *inode) -+{ -+ /* NB This is called as a side effect of other functions, but -+ * we had to release the lock to prevent deadlocks, so -+ * need to lock again. -+ */ -+ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_inode for %d", (int)inode->i_ino); -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_lock(dev); -+ -+ obj = yaffs_find_by_number(dev, inode->i_ino); -+ -+ yaffs_fill_inode_from_obj(inode, obj); -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_unlock(dev); -+} -+ -+#endif -+ -+ -+ -+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, -+ struct yaffs_obj *obj) -+{ -+ struct inode *inode; -+ -+ if (!sb) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for NULL super_block!!"); -+ return NULL; -+ -+ } -+ -+ if (!obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for NULL object!!"); -+ return NULL; -+ -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_get_inode for object %d", obj->obj_id); -+ -+ inode = Y_IGET(sb, obj->obj_id); -+ if (IS_ERR(inode)) -+ return NULL; -+ -+ /* NB Side effect: iget calls back to yaffs_read_inode(). */ -+ /* iget also increments the inode's i_count */ -+ /* NB You can't be holding gross_lock or deadlock will happen! */ -+ -+ return inode; -+} -+ -+ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) -+#define YCRED(x) x -+#else -+#define YCRED(x) (x->cred) -+#endif -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t rdev) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, -+ dev_t rdev) -+#else -+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, -+ int rdev) -+#endif -+{ -+ struct inode *inode; -+ -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_dev *dev; -+ -+ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); -+ -+ int error = -ENOSPC; -+ uid_t uid = YCRED_FSUID(); -+ gid_t gid = -+ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); -+ -+ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) -+ mode |= S_ISGID; -+ -+ if (parent) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: parent object %d type %d", -+ parent->obj_id, parent->variant_type); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: could not get parent object"); -+ return -EPERM; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod: making oject for %s, mode %x dev %x", -+ dentry->d_name.name, mode, rdev); -+ -+ dev = parent->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ switch (mode & S_IFMT) { -+ default: -+ /* Special (socket, fifo, device...) */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ obj = -+ yaffs_create_special(parent, dentry->d_name.name, mode, uid, -+ gid, old_encode_dev(rdev)); -+#else -+ obj = -+ yaffs_create_special(parent, dentry->d_name.name, mode, uid, -+ gid, rdev); -+#endif -+ break; -+ case S_IFREG: /* file */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); -+ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, -+ gid); -+ break; -+ case S_IFDIR: /* directory */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); -+ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, -+ uid, gid); -+ break; -+ case S_IFLNK: /* symlink */ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); -+ obj = NULL; /* Do we ever get here? */ -+ break; -+ } -+ -+ /* Can not call yaffs_get_inode() with gross lock held */ -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); -+ d_instantiate(dentry, inode); -+ update_dir_time(dir); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_mknod created object %d count = %d", -+ obj->obj_id, atomic_read(&inode->i_count)); -+ error = 0; -+ yaffs_fill_inode_from_obj(dir, parent); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); -+ error = -ENOMEM; -+ } -+ -+ return error; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -+#else -+static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) -+#endif -+{ -+ int ret_val; -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); -+ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); -+ return ret_val; -+} -+ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool dummy) -+#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ struct nameidata *n) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, -+ struct nameidata *n) -+#else -+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) -+#endif -+{ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); -+ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, -+ unsigned int dummy) -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, -+ struct nameidata *n) -+#else -+static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) -+#endif -+{ -+ struct yaffs_obj *obj; -+ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ -+ -+ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; -+ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_lock(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", -+ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); -+ -+ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); -+ -+ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ -+ -+ /* Can't hold gross lock when calling yaffs_get_inode() */ -+ if (current != yaffs_dev_to_lc(dev)->readdir_process) -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_lookup found %d", obj->obj_id); -+ -+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); -+ -+ } -+ -+/* added NCB for 2.5/6 compatability - forces add even if inode is -+ * NULL which creates dentry hash */ -+ d_add(dentry, inode); -+ -+ return NULL; -+} -+ -+/* -+ * Create a link... -+ */ -+static int yaffs_link(struct dentry *old_dentry, struct inode *dir, -+ struct dentry *dentry) -+{ -+ struct inode *inode = old_dentry->d_inode; -+ struct yaffs_obj *obj = NULL; -+ struct yaffs_obj *link = NULL; -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); -+ -+ obj = yaffs_inode_to_obj(inode); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ -+ link = -+ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, -+ obj); -+ -+ if (link) { -+ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); -+ d_instantiate(dentry, old_dentry->d_inode); -+ atomic_inc(&old_dentry->d_inode->i_count); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_link link count %d i_count %d", -+ old_dentry->d_inode->i_nlink, -+ atomic_read(&old_dentry->d_inode->i_count)); -+ } -+ -+ yaffs_gross_unlock(dev); -+ -+ if (link) { -+ update_dir_time(dir); -+ return 0; -+ } -+ -+ return -EPERM; -+} -+ -+static int yaffs_symlink(struct inode *dir, struct dentry *dentry, -+ const char *symname) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ uid_t uid = YCRED_FSUID(); -+ gid_t gid = -+ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); -+ -+ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > -+ YAFFS_MAX_NAME_LENGTH) -+ return -ENAMETOOLONG; -+ -+ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > -+ YAFFS_MAX_ALIAS_LENGTH) -+ return -ENAMETOOLONG; -+ -+ dev = yaffs_inode_to_obj(dir)->my_dev; -+ yaffs_gross_lock(dev); -+ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, -+ S_IFLNK | S_IRWXUGO, uid, gid, symname); -+ yaffs_gross_unlock(dev); -+ -+ if (obj) { -+ struct inode *inode; -+ -+ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); -+ d_instantiate(dentry, inode); -+ update_dir_time(dir); -+ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); -+ return 0; -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); -+ } -+ -+ return -ENOMEM; -+} -+ -+/* -+ * The VFS layer already does all the dentry stuff for rename. -+ * -+ * NB: POSIX says you can rename an object over an old object of the same name -+ */ -+static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, -+ struct inode *new_dir, struct dentry *new_dentry) -+{ -+ struct yaffs_dev *dev; -+ int ret_val = YAFFS_FAIL; -+ struct yaffs_obj *target; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); -+ dev = yaffs_inode_to_obj(old_dir)->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ /* Check if the target is an existing directory that is not empty. */ -+ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), -+ new_dentry->d_name.name); -+ -+ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && -+ !list_empty(&target->variant.dir_variant.children)) { -+ -+ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); -+ -+ ret_val = YAFFS_FAIL; -+ } else { -+ /* Now does unlinking internally using shadowing mechanism */ -+ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); -+ -+ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), -+ old_dentry->d_name.name, -+ yaffs_inode_to_obj(new_dir), -+ new_dentry->d_name.name); -+ } -+ yaffs_gross_unlock(dev); -+ -+ if (ret_val == YAFFS_OK) { -+ if (target) -+ inode_dec_link_count(new_dentry->d_inode); -+ -+ update_dir_time(old_dir); -+ if (old_dir != new_dir) -+ update_dir_time(new_dir); -+ return 0; -+ } else { -+ return -ENOTEMPTY; -+ } -+} -+ -+ -+ -+ -+static int yaffs_unlink(struct inode *dir, struct dentry *dentry) -+{ -+ int ret_val; -+ -+ struct yaffs_dev *dev; -+ struct yaffs_obj *obj; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", -+ (int)(dir->i_ino), dentry->d_name.name); -+ obj = yaffs_inode_to_obj(dir); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ ret_val = yaffs_unlinker(obj, dentry->d_name.name); -+ -+ if (ret_val == YAFFS_OK) { -+ inode_dec_link_count(dentry->d_inode); -+ dir->i_version++; -+ yaffs_gross_unlock(dev); -+ update_dir_time(dir); -+ return 0; -+ } -+ yaffs_gross_unlock(dev); -+ return -ENOTEMPTY; -+} -+ -+ -+ -+static const struct inode_operations yaffs_dir_inode_operations = { -+ .create = yaffs_create, -+ .lookup = yaffs_lookup, -+ .link = yaffs_link, -+ .unlink = yaffs_unlink, -+ .symlink = yaffs_symlink, -+ .mkdir = yaffs_mkdir, -+ .rmdir = yaffs_unlink, -+ .mknod = yaffs_mknod, -+ .rename = yaffs_rename, -+ .setattr = yaffs_setattr, -+ .setxattr = yaffs_setxattr, -+ .getxattr = yaffs_getxattr, -+ .listxattr = yaffs_listxattr, -+ .removexattr = yaffs_removexattr, -+}; -+ -+/*-----------------------------------------------------------------*/ -+/* Directory search context allows us to unlock access to yaffs during -+ * filldir without causing problems with the directory being modified. -+ * This is similar to the tried and tested mechanism used in yaffs direct. -+ * -+ * A search context iterates along a doubly linked list of siblings in the -+ * directory. If the iterating object is deleted then this would corrupt -+ * the list iteration, likely causing a crash. The search context avoids -+ * this by using the remove_obj_fn to move the search context to the -+ * next object before the object is deleted. -+ * -+ * Many readdirs (and thus seach conexts) may be alive simulateously so -+ * each struct yaffs_dev has a list of these. -+ * -+ * A seach context lives for the duration of a readdir. -+ * -+ * All these functions must be called while yaffs is locked. -+ */ -+ -+struct yaffs_search_context { -+ struct yaffs_dev *dev; -+ struct yaffs_obj *dir_obj; -+ struct yaffs_obj *next_return; -+ struct list_head others; -+}; -+ -+/* -+ * yaffs_new_search() creates a new search context, initialises it and -+ * adds it to the device's search context list. -+ * -+ * Called at start of readdir. -+ */ -+static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) -+{ -+ struct yaffs_dev *dev = dir->my_dev; -+ struct yaffs_search_context *sc = -+ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); -+ if (sc) { -+ sc->dir_obj = dir; -+ sc->dev = dev; -+ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) -+ sc->next_return = NULL; -+ else -+ sc->next_return = -+ list_entry(dir->variant.dir_variant.children.next, -+ struct yaffs_obj, siblings); -+ INIT_LIST_HEAD(&sc->others); -+ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); -+ } -+ return sc; -+} -+ -+/* -+ * yaffs_search_end() disposes of a search context and cleans up. -+ */ -+static void yaffs_search_end(struct yaffs_search_context *sc) -+{ -+ if (sc) { -+ list_del(&sc->others); -+ kfree(sc); -+ } -+} -+ -+/* -+ * yaffs_search_advance() moves a search context to the next object. -+ * Called when the search iterates or when an object removal causes -+ * the search context to be moved to the next object. -+ */ -+static void yaffs_search_advance(struct yaffs_search_context *sc) -+{ -+ if (!sc) -+ return; -+ -+ if (sc->next_return == NULL || -+ list_empty(&sc->dir_obj->variant.dir_variant.children)) -+ sc->next_return = NULL; -+ else { -+ struct list_head *next = sc->next_return->siblings.next; -+ -+ if (next == &sc->dir_obj->variant.dir_variant.children) -+ sc->next_return = NULL; /* end of list */ -+ else -+ sc->next_return = -+ list_entry(next, struct yaffs_obj, siblings); -+ } -+} -+ -+/* -+ * yaffs_remove_obj_callback() is called when an object is unlinked. -+ * We check open search contexts and advance any which are currently -+ * on the object being iterated. -+ */ -+static void yaffs_remove_obj_callback(struct yaffs_obj *obj) -+{ -+ -+ struct list_head *i; -+ struct yaffs_search_context *sc; -+ struct list_head *search_contexts = -+ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); -+ -+ /* Iterate through the directory search contexts. -+ * If any are currently on the object being removed, then advance -+ * the search context to the next object to prevent a hanging pointer. -+ */ -+ list_for_each(i, search_contexts) { -+ sc = list_entry(i, struct yaffs_search_context, others); -+ if (sc->next_return == obj) -+ yaffs_search_advance(sc); -+ } -+ -+} -+ -+ -+/*-----------------------------------------------------------------*/ -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) -+static int yaffs_readdir(struct file *file, struct dir_context *ctx) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ struct yaffs_search_context *sc; -+ struct inode *inode = file->f_dentry->d_inode; -+ unsigned long offset, curoffs; -+ struct yaffs_obj *l; -+ int ret_val = 0; -+ -+ char name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ obj = yaffs_dentry_to_obj(file->f_dentry); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_dev_to_lc(dev)->readdir_process = current; -+ -+ offset = ctx->pos; -+ -+ sc = yaffs_new_search(obj); -+ if (!sc) { -+ ret_val = -ENOMEM; -+ goto out; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: starting at %d", (int)offset); -+ -+ if (offset == 0) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry . ino %d", -+ (int)inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (!dir_emit_dot(file, ctx)) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ ctx->pos++; -+ } -+ if (offset == 1) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry .. ino %d", -+ (int)file->f_dentry->d_parent->d_inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (!dir_emit_dotdot(file, ctx)) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ ctx->pos++; -+ } -+ -+ curoffs = 1; -+ -+ /* If the directory has changed since the open or last call to -+ readdir, rewind to after the 2 canned entries. */ -+ if (file->f_version != inode->i_version) { -+ offset = 2; -+ ctx->pos = offset; -+ file->f_version = inode->i_version; -+ } -+ -+ while (sc->next_return) { -+ curoffs++; -+ l = sc->next_return; -+ if (curoffs >= offset) { -+ int this_inode = yaffs_get_obj_inode(l); -+ int this_type = yaffs_get_obj_type(l); -+ -+ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: %s inode %d", -+ name, yaffs_get_obj_inode(l)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (!dir_emit(ctx, name, strlen(name), -+ this_inode, this_type) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ -+ yaffs_gross_lock(dev); -+ -+ offset++; -+ ctx->pos++; -+ } -+ yaffs_search_advance(sc); -+ } -+ -+out: -+ yaffs_search_end(sc); -+ yaffs_dev_to_lc(dev)->readdir_process = NULL; -+ yaffs_gross_unlock(dev); -+ -+ return ret_val; -+} -+#else -+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ struct yaffs_search_context *sc; -+ struct inode *inode = f->f_dentry->d_inode; -+ unsigned long offset, curoffs; -+ struct yaffs_obj *l; -+ int ret_val = 0; -+ -+ char name[YAFFS_MAX_NAME_LENGTH + 1]; -+ -+ obj = yaffs_dentry_to_obj(f->f_dentry); -+ dev = obj->my_dev; -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_dev_to_lc(dev)->readdir_process = current; -+ -+ offset = f->f_pos; -+ -+ sc = yaffs_new_search(obj); -+ if (!sc) { -+ ret_val = -ENOMEM; -+ goto out; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: starting at %d", (int)offset); -+ -+ if (offset == 0) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry . ino %d", -+ (int)inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ f->f_pos++; -+ } -+ if (offset == 1) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: entry .. ino %d", -+ (int)f->f_dentry->d_parent->d_inode->i_ino); -+ yaffs_gross_unlock(dev); -+ if (filldir(dirent, "..", 2, offset, -+ f->f_dentry->d_parent->d_inode->i_ino, -+ DT_DIR) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ yaffs_gross_lock(dev); -+ offset++; -+ f->f_pos++; -+ } -+ -+ curoffs = 1; -+ -+ /* If the directory has changed since the open or last call to -+ readdir, rewind to after the 2 canned entries. */ -+ if (f->f_version != inode->i_version) { -+ offset = 2; -+ f->f_pos = offset; -+ f->f_version = inode->i_version; -+ } -+ -+ while (sc->next_return) { -+ curoffs++; -+ l = sc->next_return; -+ if (curoffs >= offset) { -+ int this_inode = yaffs_get_obj_inode(l); -+ int this_type = yaffs_get_obj_type(l); -+ -+ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_readdir: %s inode %d", -+ name, yaffs_get_obj_inode(l)); -+ -+ yaffs_gross_unlock(dev); -+ -+ if (filldir(dirent, -+ name, -+ strlen(name), -+ offset, this_inode, this_type) < 0) { -+ yaffs_gross_lock(dev); -+ goto out; -+ } -+ -+ yaffs_gross_lock(dev); -+ -+ offset++; -+ f->f_pos++; -+ } -+ yaffs_search_advance(sc); -+ } -+ -+out: -+ yaffs_search_end(sc); -+ yaffs_dev_to_lc(dev)->readdir_process = NULL; -+ yaffs_gross_unlock(dev); -+ -+ return ret_val; -+} -+#endif -+ -+static const struct file_operations yaffs_dir_operations = { -+ .read = generic_read_dir, -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) -+ .iterate = yaffs_readdir, -+#else -+ .readdir = yaffs_readdir, -+#endif -+ .fsync = yaffs_sync_object, -+ .llseek = generic_file_llseek, -+}; -+ -+static void yaffs_fill_inode_from_obj(struct inode *inode, -+ struct yaffs_obj *obj) -+{ -+ if (inode && obj) { -+ -+ /* Check mode against the variant type and attempt to repair if broken. */ -+ u32 mode = obj->yst_mode; -+ switch (obj->variant_type) { -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (!S_ISREG(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFREG; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ if (!S_ISLNK(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFLNK; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ if (!S_ISDIR(mode)) { -+ obj->yst_mode &= ~S_IFMT; -+ obj->yst_mode |= S_IFDIR; -+ } -+ -+ break; -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ default: -+ /* TODO? */ -+ break; -+ } -+ -+ inode->i_flags |= S_NOATIME; -+ -+ inode->i_ino = obj->obj_id; -+ inode->i_mode = obj->yst_mode; -+ i_uid_write(inode, obj->yst_uid); -+ i_gid_write(inode, obj->yst_gid); -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+ inode->i_blksize = inode->i_sb->s_blocksize; -+#endif -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ -+ inode->i_rdev = old_decode_dev(obj->yst_rdev); -+ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); -+ inode->i_atime.tv_nsec = 0; -+ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; -+ inode->i_mtime.tv_nsec = 0; -+ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; -+ inode->i_ctime.tv_nsec = 0; -+#else -+ inode->i_rdev = obj->yst_rdev; -+ inode->i_atime = obj->yst_atime; -+ inode->i_mtime = obj->yst_mtime; -+ inode->i_ctime = obj->yst_ctime; -+#endif -+ inode->i_size = yaffs_get_obj_length(obj); -+ inode->i_blocks = (inode->i_size + 511) >> 9; -+ -+ set_nlink(inode, yaffs_get_obj_link_count(obj)); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", -+ inode->i_mode, i_uid_read(inode), i_gid_read(inode), -+ inode->i_size, atomic_read(&inode->i_count)); -+ -+ switch (obj->yst_mode & S_IFMT) { -+ default: /* fifo, device or socket */ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ init_special_inode(inode, obj->yst_mode, -+ old_decode_dev(obj->yst_rdev)); -+#else -+ init_special_inode(inode, obj->yst_mode, -+ (dev_t) (obj->yst_rdev)); -+#endif -+ break; -+ case S_IFREG: /* file */ -+ inode->i_op = &yaffs_file_inode_operations; -+ inode->i_fop = &yaffs_file_operations; -+ inode->i_mapping->a_ops = -+ &yaffs_file_address_operations; -+ break; -+ case S_IFDIR: /* directory */ -+ inode->i_op = &yaffs_dir_inode_operations; -+ inode->i_fop = &yaffs_dir_operations; -+ break; -+ case S_IFLNK: /* symlink */ -+ inode->i_op = &yaffs_symlink_inode_operations; -+ break; -+ } -+ -+ yaffs_inode_to_obj_lv(inode) = obj; -+ -+ obj->my_inode = inode; -+ -+ } else { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_fill_inode invalid parameters"); -+ } -+ -+} -+ -+ -+ -+/* -+ * yaffs background thread functions . -+ * yaffs_bg_thread_fn() the thread function -+ * yaffs_bg_start() launches the background thread. -+ * yaffs_bg_stop() cleans up the background thread. -+ * -+ * NB: -+ * The thread should only run after the yaffs is initialised -+ * The thread should be stopped before yaffs is unmounted. -+ * The thread should not do any writing while the fs is in read only. -+ */ -+ -+static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) -+{ -+ unsigned erased_chunks = -+ dev->n_erased_blocks * dev->param.chunks_per_block; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ unsigned scattered = 0; /* Free chunks not in an erased block */ -+ -+ if (erased_chunks < dev->n_free_chunks) -+ scattered = (dev->n_free_chunks - erased_chunks); -+ -+ if (!context->bg_running) -+ return 0; -+ else if (scattered < (dev->param.chunks_per_block * 2)) -+ return 0; -+ else if (erased_chunks > dev->n_free_chunks / 2) -+ return 0; -+ else if (erased_chunks > dev->n_free_chunks / 4) -+ return 1; -+ else -+ return 2; -+} -+ -+#ifdef YAFFS_COMPILE_BACKGROUND -+ -+void yaffs_background_waker(unsigned long data) -+{ -+ wake_up_process((struct task_struct *)data); -+} -+ -+static int yaffs_bg_thread_fn(void *data) -+{ -+ struct yaffs_dev *dev = (struct yaffs_dev *)data; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ unsigned long now = jiffies; -+ unsigned long next_dir_update = now; -+ unsigned long next_gc = now; -+ unsigned long expires; -+ unsigned int urgency; -+ -+ int gc_result; -+ struct timer_list timer; -+ -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, -+ "yaffs_background starting for dev %p", (void *)dev); -+ -+#ifdef YAFFS_COMPILE_FREEZER -+ set_freezable(); -+#endif -+ while (context->bg_running) { -+ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); -+ -+ if (kthread_should_stop()) -+ break; -+ -+#ifdef YAFFS_COMPILE_FREEZER -+ if (try_to_freeze()) -+ continue; -+#endif -+ yaffs_gross_lock(dev); -+ -+ now = jiffies; -+ -+ if (time_after(now, next_dir_update) && yaffs_bg_enable) { -+ yaffs_update_dirty_dirs(dev); -+ next_dir_update = now + HZ; -+ } -+ -+ if (time_after(now, next_gc) && yaffs_bg_enable) { -+ if (!dev->is_checkpointed) { -+ urgency = yaffs_bg_gc_urgency(dev); -+ gc_result = yaffs_bg_gc(dev, urgency); -+ if (urgency > 1) -+ next_gc = now + HZ / 20 + 1; -+ else if (urgency > 0) -+ next_gc = now + HZ / 10 + 1; -+ else -+ next_gc = now + HZ * 2; -+ } else { -+ /* -+ * gc not running so set to next_dir_update -+ * to cut down on wake ups -+ */ -+ next_gc = next_dir_update; -+ } -+ } -+ yaffs_gross_unlock(dev); -+#if 1 -+ expires = next_dir_update; -+ if (time_before(next_gc, expires)) -+ expires = next_gc; -+ if (time_before(expires, now)) -+ expires = now + HZ; -+ -+ Y_INIT_TIMER(&timer); -+ timer.expires = expires + 1; -+ timer.data = (unsigned long)current; -+ timer.function = yaffs_background_waker; -+ -+ set_current_state(TASK_INTERRUPTIBLE); -+ add_timer(&timer); -+ schedule(); -+ del_timer_sync(&timer); -+#else -+ msleep(10); -+#endif -+ } -+ -+ return 0; -+} -+ -+static int yaffs_bg_start(struct yaffs_dev *dev) -+{ -+ int retval = 0; -+ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); -+ -+ if (dev->read_only) -+ return -1; -+ -+ context->bg_running = 1; -+ -+ context->bg_thread = kthread_run(yaffs_bg_thread_fn, -+ (void *)dev, "yaffs-bg-%d", -+ context->mount_id); -+ -+ if (IS_ERR(context->bg_thread)) { -+ retval = PTR_ERR(context->bg_thread); -+ context->bg_thread = NULL; -+ context->bg_running = 0; -+ } -+ return retval; -+} -+ -+static void yaffs_bg_stop(struct yaffs_dev *dev) -+{ -+ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); -+ -+ ctxt->bg_running = 0; -+ -+ if (ctxt->bg_thread) { -+ kthread_stop(ctxt->bg_thread); -+ ctxt->bg_thread = NULL; -+ } -+} -+#else -+static int yaffs_bg_thread_fn(void *data) -+{ -+ return 0; -+} -+ -+static int yaffs_bg_start(struct yaffs_dev *dev) -+{ -+ return 0; -+} -+ -+static void yaffs_bg_stop(struct yaffs_dev *dev) -+{ -+} -+#endif -+ -+ -+static void yaffs_flush_inodes(struct super_block *sb) -+{ -+ struct inode *iptr; -+ struct yaffs_obj *obj; -+ -+ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { -+ obj = yaffs_inode_to_obj(iptr); -+ if (obj) { -+ yaffs_trace(YAFFS_TRACE_OS, -+ "flushing obj %d", -+ obj->obj_id); -+ yaffs_flush_file(obj, 1, 0); -+ } -+ } -+} -+ -+static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ if (!dev) -+ return; -+ -+ yaffs_flush_inodes(sb); -+ yaffs_update_dirty_dirs(dev); -+ yaffs_flush_whole_cache(dev); -+ if (do_checkpoint) -+ yaffs_checkpoint_save(dev); -+} -+ -+static LIST_HEAD(yaffs_context_list); -+struct mutex yaffs_context_lock; -+ -+static void yaffs_put_super(struct super_block *sb) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, -+ "yaffs_put_super"); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, -+ "Shutting down yaffs background thread"); -+ yaffs_bg_stop(dev); -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, -+ "yaffs background thread shut down"); -+ -+ yaffs_gross_lock(dev); -+ -+ yaffs_flush_super(sb, 1); -+ -+ yaffs_deinitialise(dev); -+ -+ yaffs_gross_unlock(dev); -+ -+ mutex_lock(&yaffs_context_lock); -+ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); -+ mutex_unlock(&yaffs_context_lock); -+ -+ if (yaffs_dev_to_lc(dev)->spare_buffer) { -+ kfree(yaffs_dev_to_lc(dev)->spare_buffer); -+ yaffs_dev_to_lc(dev)->spare_buffer = NULL; -+ } -+ -+ kfree(dev); -+ -+ yaffs_put_mtd_device(mtd); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, -+ "yaffs_put_super done"); -+} -+ -+ -+static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) -+{ -+ return yaffs_gc_control; -+} -+ -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+ -+static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, -+ uint32_t generation) -+{ -+ return Y_IGET(sb, ino); -+} -+ -+static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, -+ struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, -+ yaffs2_nfs_get_inode); -+} -+ -+static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, -+ struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ return generic_fh_to_parent(sb, fid, fh_len, fh_type, -+ yaffs2_nfs_get_inode); -+} -+ -+struct dentry *yaffs2_get_parent(struct dentry *dentry) -+{ -+ -+ struct super_block *sb = dentry->d_inode->i_sb; -+ struct dentry *parent = ERR_PTR(-ENOENT); -+ struct inode *inode; -+ unsigned long parent_ino; -+ struct yaffs_obj *d_obj; -+ struct yaffs_obj *parent_obj; -+ -+ d_obj = yaffs_inode_to_obj(dentry->d_inode); -+ -+ if (d_obj) { -+ parent_obj = d_obj->parent; -+ if (parent_obj) { -+ parent_ino = yaffs_get_obj_inode(parent_obj); -+ inode = Y_IGET(sb, parent_ino); -+ -+ if (IS_ERR(inode)) { -+ parent = ERR_CAST(inode); -+ } else { -+ parent = d_obtain_alias(inode); -+ if (!IS_ERR(parent)) { -+ parent = ERR_PTR(-ENOMEM); -+ iput(inode); -+ } -+ } -+ } -+ } -+ -+ return parent; -+} -+ -+/* Just declare a zero structure as a NULL value implies -+ * using the default functions of exportfs. -+ */ -+ -+static struct export_operations yaffs_export_ops = { -+ .fh_to_dentry = yaffs2_fh_to_dentry, -+ .fh_to_parent = yaffs2_fh_to_parent, -+ .get_parent = yaffs2_get_parent, -+}; -+ -+#endif -+ -+static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) -+{ -+ /* Clear the association between the inode and -+ * the struct yaffs_obj. -+ */ -+ obj->my_inode = NULL; -+ yaffs_inode_to_obj_lv(inode) = NULL; -+ -+ /* If the object freeing was deferred, then the real -+ * free happens now. -+ * This should fix the inode inconsistency problem. -+ */ -+ yaffs_handle_defered_free(obj); -+} -+ -+#ifdef YAFFS_HAS_EVICT_INODE -+/* yaffs_evict_inode combines into one operation what was previously done in -+ * yaffs_clear_inode() and yaffs_delete_inode() -+ * -+ */ -+static void yaffs_evict_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ int deleteme = 0; -+ -+ obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_evict_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (!inode->i_nlink && !is_bad_inode(inode)) -+ deleteme = 1; -+ truncate_inode_pages(&inode->i_data, 0); -+ Y_CLEAR_INODE(inode); -+ -+ if (deleteme && obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_del_obj(obj); -+ yaffs_gross_unlock(dev); -+ } -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_unstitch_obj(inode, obj); -+ yaffs_gross_unlock(dev); -+ } -+} -+#else -+ -+/* clear is called to tell the fs to release any per-inode data it holds. -+ * The object might still exist on disk and is just being thrown out of the cache -+ * or else the object has actually been deleted and we're being called via -+ * the chain -+ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() -+ */ -+ -+static void yaffs_clear_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_dev *dev; -+ -+ obj = yaffs_inode_to_obj(inode); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_clear_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_unstitch_obj(inode, obj); -+ yaffs_gross_unlock(dev); -+ } -+ -+} -+ -+/* delete is called when the link count is zero and the inode -+ * is put (ie. nobody wants to know about it anymore, time to -+ * delete the file). -+ * NB Must call clear_inode() -+ */ -+static void yaffs_delete_inode(struct inode *inode) -+{ -+ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); -+ struct yaffs_dev *dev; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_delete_inode: ino %d, count %d %s", -+ (int)inode->i_ino, atomic_read(&inode->i_count), -+ obj ? "object exists" : "null object"); -+ -+ if (obj) { -+ dev = obj->my_dev; -+ yaffs_gross_lock(dev); -+ yaffs_del_obj(obj); -+ yaffs_gross_unlock(dev); -+ } -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) -+ truncate_inode_pages(&inode->i_data, 0); -+#endif -+ clear_inode(inode); -+} -+#endif -+ -+ -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; -+ struct super_block *sb = dentry->d_sb; -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+#else -+static int yaffs_statfs(struct super_block *sb, struct statfs *buf) -+{ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+#endif -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); -+ -+ yaffs_gross_lock(dev); -+ -+ buf->f_type = YAFFS_MAGIC; -+ buf->f_bsize = sb->s_blocksize; -+ buf->f_namelen = 255; -+ -+ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { -+ /* Do this if chunk size is not a power of 2 */ -+ -+ uint64_t bytes_in_dev; -+ uint64_t bytes_free; -+ -+ bytes_in_dev = -+ ((uint64_t) -+ ((dev->param.end_block - dev->param.start_block + -+ 1))) * ((uint64_t) (dev->param.chunks_per_block * -+ dev->data_bytes_per_chunk)); -+ -+ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ -+ buf->f_blocks = bytes_in_dev; -+ -+ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * -+ ((uint64_t) (dev->data_bytes_per_chunk)); -+ -+ do_div(bytes_free, sb->s_blocksize); -+ -+ buf->f_bfree = bytes_free; -+ -+ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { -+ -+ buf->f_blocks = -+ (dev->param.end_block - dev->param.start_block + 1) * -+ dev->param.chunks_per_block / -+ (sb->s_blocksize / dev->data_bytes_per_chunk); -+ buf->f_bfree = -+ yaffs_get_n_free_chunks(dev) / -+ (sb->s_blocksize / dev->data_bytes_per_chunk); -+ } else { -+ buf->f_blocks = -+ (dev->param.end_block - dev->param.start_block + 1) * -+ dev->param.chunks_per_block * -+ (dev->data_bytes_per_chunk / sb->s_blocksize); -+ -+ buf->f_bfree = -+ yaffs_get_n_free_chunks(dev) * -+ (dev->data_bytes_per_chunk / sb->s_blocksize); -+ } -+ -+ buf->f_files = 0; -+ buf->f_ffree = 0; -+ buf->f_bavail = buf->f_bfree; -+ -+ yaffs_gross_unlock(dev); -+ return 0; -+} -+ -+ -+ -+static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) -+{ -+ -+ struct yaffs_dev *dev = yaffs_super_to_dev(sb); -+ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); -+ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); -+ int do_checkpoint; -+ int dirty = yaffs_check_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, -+ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", -+ gc_urgent, -+ dirty ? "dirty" : "clean", -+ request_checkpoint ? "checkpoint requested" : "no checkpoint", -+ oneshot_checkpoint ? " one-shot" : ""); -+ -+ yaffs_gross_lock(dev); -+ do_checkpoint = ((request_checkpoint && !gc_urgent) || -+ oneshot_checkpoint) && !dev->is_checkpointed; -+ -+ if (dirty || do_checkpoint) { -+ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); -+ yaffs_clear_super_dirty(dev); -+ if (oneshot_checkpoint) -+ yaffs_auto_checkpoint &= ~4; -+ } -+ yaffs_gross_unlock(dev); -+ -+ return 0; -+} -+ -+ -+#ifdef YAFFS_HAS_WRITE_SUPER -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static void yaffs_write_super(struct super_block *sb) -+#else -+static int yaffs_write_super(struct super_block *sb) -+#endif -+{ -+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, -+ "yaffs_write_super %s", -+ request_checkpoint ? " checkpt" : ""); -+ -+ yaffs_do_sync_fs(sb, request_checkpoint); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) -+ return 0; -+#endif -+} -+#endif -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_sync_fs(struct super_block *sb, int wait) -+#else -+static int yaffs_sync_fs(struct super_block *sb) -+#endif -+{ -+ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); -+ -+ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, -+ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); -+ -+ yaffs_do_sync_fs(sb, request_checkpoint); -+ -+ return 0; -+} -+ -+ -+ -+static const struct super_operations yaffs_super_ops = { -+ .statfs = yaffs_statfs, -+ -+#ifndef YAFFS_USE_OWN_IGET -+ .read_inode = yaffs_read_inode, -+#endif -+#ifdef YAFFS_HAS_PUT_INODE -+ .put_inode = yaffs_put_inode, -+#endif -+ .put_super = yaffs_put_super, -+#ifdef YAFFS_HAS_EVICT_INODE -+ .evict_inode = yaffs_evict_inode, -+#else -+ .delete_inode = yaffs_delete_inode, -+ .clear_inode = yaffs_clear_inode, -+#endif -+ .sync_fs = yaffs_sync_fs, -+#ifdef YAFFS_HAS_WRITE_SUPER -+ .write_super = yaffs_write_super, -+#endif -+}; -+ -+struct yaffs_options { -+ int inband_tags; -+ int skip_checkpoint_read; -+ int skip_checkpoint_write; -+ int no_cache; -+ int tags_ecc_on; -+ int tags_ecc_overridden; -+ int lazy_loading_enabled; -+ int lazy_loading_overridden; -+ int empty_lost_and_found; -+ int empty_lost_and_found_overridden; -+ int disable_summary; -+}; -+ -+#define MAX_OPT_LEN 30 -+static int yaffs_parse_options(struct yaffs_options *options, -+ const char *options_str) -+{ -+ char cur_opt[MAX_OPT_LEN + 1]; -+ int p; -+ int error = 0; -+ -+ /* Parse through the options which is a comma seperated list */ -+ -+ while (options_str && *options_str && !error) { -+ memset(cur_opt, 0, MAX_OPT_LEN + 1); -+ p = 0; -+ -+ while (*options_str == ',') -+ options_str++; -+ -+ while (*options_str && *options_str != ',') { -+ if (p < MAX_OPT_LEN) { -+ cur_opt[p] = *options_str; -+ p++; -+ } -+ options_str++; -+ } -+ -+ if (!strcmp(cur_opt, "inband-tags")) { -+ options->inband_tags = 1; -+ } else if (!strcmp(cur_opt, "tags-ecc-off")) { -+ options->tags_ecc_on = 0; -+ options->tags_ecc_overridden = 1; -+ } else if (!strcmp(cur_opt, "tags-ecc-on")) { -+ options->tags_ecc_on = 1; -+ options->tags_ecc_overridden = 1; -+ } else if (!strcmp(cur_opt, "lazy-loading-off")) { -+ options->lazy_loading_enabled = 0; -+ options->lazy_loading_overridden = 1; -+ } else if (!strcmp(cur_opt, "lazy-loading-on")) { -+ options->lazy_loading_enabled = 1; -+ options->lazy_loading_overridden = 1; -+ } else if (!strcmp(cur_opt, "disable-summary")) { -+ options->disable_summary = 1; -+ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { -+ options->empty_lost_and_found = 0; -+ options->empty_lost_and_found_overridden = 1; -+ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { -+ options->empty_lost_and_found = 1; -+ options->empty_lost_and_found_overridden = 1; -+ } else if (!strcmp(cur_opt, "no-cache")) { -+ options->no_cache = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { -+ options->skip_checkpoint_read = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { -+ options->skip_checkpoint_write = 1; -+ } else if (!strcmp(cur_opt, "no-checkpoint")) { -+ options->skip_checkpoint_read = 1; -+ options->skip_checkpoint_write = 1; -+ } else { -+ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", -+ cur_opt); -+ error = 1; -+ } -+ } -+ -+ return error; -+} -+ -+ -+static struct dentry *yaffs_make_root(struct inode *inode) -+{ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) -+ struct dentry *root = d_alloc_root(inode); -+ -+ if (!root) -+ iput(inode); -+ -+ return root; -+#else -+ return d_make_root(inode); -+#endif -+} -+ -+ -+ -+ -+static struct super_block *yaffs_internal_read_super(int yaffs_version, -+ struct super_block *sb, -+ void *data, int silent) -+{ -+ int n_blocks; -+ struct inode *inode = NULL; -+ struct dentry *root; -+ struct yaffs_dev *dev = 0; -+ char devname_buf[BDEVNAME_SIZE + 1]; -+ struct mtd_info *mtd; -+ int err; -+ char *data_str = (char *)data; -+ struct yaffs_linux_context *context = NULL; -+ struct yaffs_param *param; -+ -+ int read_only = 0; -+ int inband_tags = 0; -+ -+ struct yaffs_options options; -+ -+ unsigned mount_id; -+ int found; -+ struct yaffs_linux_context *context_iterator; -+ struct list_head *l; -+ -+ if (!sb) { -+ printk(KERN_INFO "yaffs: sb is NULL\n"); -+ return NULL; -+ } -+ -+ sb->s_magic = YAFFS_MAGIC; -+ sb->s_op = &yaffs_super_ops; -+ sb->s_flags |= MS_NOATIME; -+ -+ read_only = ((sb->s_flags & MS_RDONLY) != 0); -+ -+#ifdef YAFFS_COMPILE_EXPORTFS -+ sb->s_export_op = &yaffs_export_ops; -+#endif -+ -+ if (!sb->s_dev) -+ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); -+ else if (!yaffs_devname(sb, devname_buf)) -+ printk(KERN_INFO "yaffs: devname is NULL\n"); -+ else -+ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", -+ sb->s_dev, -+ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); -+ -+ if (!data_str) -+ data_str = ""; -+ -+ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); -+ -+ memset(&options, 0, sizeof(options)); -+ -+ if (yaffs_parse_options(&options, data_str)) { -+ /* Option parsing failed */ -+ return NULL; -+ } -+ -+ sb->s_blocksize = PAGE_CACHE_SIZE; -+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: Using yaffs%d", yaffs_version); -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", -+ MAJOR(sb->s_dev), MINOR(sb->s_dev), -+ yaffs_devname(sb, devname_buf)); -+ -+ /* Get the device */ -+ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); -+ if (IS_ERR(mtd)) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs: MTD device %u either not valid or unavailable", -+ MINOR(sb->s_dev)); -+ return NULL; -+ } -+ -+ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); -+ yaffs_version = 2; -+ } -+ -+ /* Added NCB 26/5/2006 for completeness */ -+ if (yaffs_version == 2 && !options.inband_tags -+ && WRITE_SIZE(mtd) == 512) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); -+ yaffs_version = 1; -+ } -+ -+ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || -+ options.inband_tags) -+ inband_tags = 1; -+ -+ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) -+ return NULL; -+ -+ /* OK, so if we got here, we have an MTD that's NAND and looks -+ * like it has the right capabilities -+ * Set the struct yaffs_dev up for mtd -+ */ -+ -+ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { -+ read_only = 1; -+ printk(KERN_INFO -+ "yaffs: mtd is read only, setting superblock read only\n" -+ ); -+ sb->s_flags |= MS_RDONLY; -+ } -+ -+ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); -+ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); -+ -+ if (!dev || !context) { -+ kfree(dev); -+ kfree(context); -+ dev = NULL; -+ context = NULL; -+ -+ /* Deep shit could not allocate device structure */ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." -+ ); -+ return NULL; -+ } -+ memset(dev, 0, sizeof(struct yaffs_dev)); -+ param = &(dev->param); -+ -+ memset(context, 0, sizeof(struct yaffs_linux_context)); -+ dev->os_context = context; -+ INIT_LIST_HEAD(&(context->context_list)); -+ context->dev = dev; -+ context->super = sb; -+ -+ dev->read_only = read_only; -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+ sb->s_fs_info = dev; -+#else -+ sb->u.generic_sbp = dev; -+#endif -+ -+ -+ dev->driver_context = mtd; -+ param->name = mtd->name; -+ -+ /* Set up the memory size parameters.... */ -+ -+ -+ param->n_reserved_blocks = 5; -+ param->n_caches = (options.no_cache) ? 0 : 10; -+ param->inband_tags = inband_tags; -+ -+ param->enable_xattr = 1; -+ if (options.lazy_loading_overridden) -+ param->disable_lazy_load = !options.lazy_loading_enabled; -+ -+ param->defered_dir_update = 1; -+ -+ if (options.tags_ecc_overridden) -+ param->no_tags_ecc = !options.tags_ecc_on; -+ -+ param->empty_lost_n_found = 1; -+ param->refresh_period = 500; -+ param->disable_summary = options.disable_summary; -+ -+ -+#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING -+ param->disable_bad_block_marking = 1; -+#endif -+ if (options.empty_lost_and_found_overridden) -+ param->empty_lost_n_found = options.empty_lost_and_found; -+ -+ /* ... and the functions. */ -+ if (yaffs_version == 2) { -+ param->is_yaffs2 = 1; -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+ param->total_bytes_per_chunk = mtd->writesize; -+ param->chunks_per_block = mtd->erasesize / mtd->writesize; -+#else -+ param->total_bytes_per_chunk = mtd->oobblock; -+ param->chunks_per_block = mtd->erasesize / mtd->oobblock; -+#endif -+ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); -+ -+ param->start_block = 0; -+ param->end_block = n_blocks - 1; -+ } else { -+ param->is_yaffs2 = 0; -+ n_blocks = YCALCBLOCKS(mtd->size, -+ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); -+ -+ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; -+ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; -+ } -+ -+ param->start_block = 0; -+ param->end_block = n_blocks - 1; -+ -+ yaffs_mtd_drv_install(dev); -+ -+ param->sb_dirty_fn = yaffs_set_super_dirty; -+ param->gc_control_fn = yaffs_gc_control_callback; -+ -+ yaffs_dev_to_lc(dev)->super = sb; -+ -+ param->use_nand_ecc = 1; -+ -+ param->skip_checkpt_rd = options.skip_checkpoint_read; -+ param->skip_checkpt_wr = options.skip_checkpoint_write; -+ -+ mutex_lock(&yaffs_context_lock); -+ /* Get a mount id */ -+ found = 0; -+ for (mount_id = 0; !found; mount_id++) { -+ found = 1; -+ list_for_each(l, &yaffs_context_list) { -+ context_iterator = -+ list_entry(l, struct yaffs_linux_context, -+ context_list); -+ if (context_iterator->mount_id == mount_id) -+ found = 0; -+ } -+ } -+ context->mount_id = mount_id; -+ -+ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), -+ &yaffs_context_list); -+ mutex_unlock(&yaffs_context_lock); -+ -+ /* Directory search handling... */ -+ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); -+ param->remove_obj_fn = yaffs_remove_obj_callback; -+ -+ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); -+ -+ yaffs_gross_lock(dev); -+ -+ err = yaffs_guts_initialise(dev); -+ -+ yaffs_trace(YAFFS_TRACE_OS, -+ "yaffs_read_super: guts initialised %s", -+ (err == YAFFS_OK) ? "OK" : "FAILED"); -+ -+ if (err == YAFFS_OK) -+ yaffs_bg_start(dev); -+ -+ if (!context->bg_thread) -+ param->defered_dir_update = 0; -+ -+ sb->s_maxbytes = yaffs_max_file_size(dev); -+ -+ /* Release lock before yaffs_get_inode() */ -+ yaffs_gross_unlock(dev); -+ -+ /* Create root inode */ -+ if (err == YAFFS_OK) -+ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); -+ -+ if (!inode) -+ return NULL; -+ -+ inode->i_op = &yaffs_dir_inode_operations; -+ inode->i_fop = &yaffs_dir_operations; -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); -+ -+ root = yaffs_make_root(inode); -+ -+ if (!root) -+ return NULL; -+ -+ sb->s_root = root; -+ if(!dev->is_checkpointed) -+ yaffs_set_super_dirty(dev); -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs_read_super: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); -+ return sb; -+} -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data) -+{ -+ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); -+} -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data, struct vfsmount *mnt) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs_internal_read_super_mtd, mnt); -+} -+#else -+static struct super_block *yaffs_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs_internal_read_super_mtd); -+} -+#endif -+ -+static struct file_system_type yaffs_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "yaffs", -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+ .mount = yaffs_mount, -+#else -+ .get_sb = yaffs_read_super, -+#endif -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV, -+}; -+#else -+static struct super_block *yaffs_read_super(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(1, sb, data, silent); -+} -+ -+static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, -+ FS_REQUIRES_DEV); -+#endif -+ -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, -+ int silent) -+{ -+ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; -+} -+ -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data) -+{ -+ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); -+} -+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) -+static int yaffs2_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, void *data, -+ struct vfsmount *mnt) -+{ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs2_internal_read_super_mtd, mnt); -+} -+#else -+static struct super_block *yaffs2_read_super(struct file_system_type *fs, -+ int flags, const char *dev_name, -+ void *data) -+{ -+ -+ return get_sb_bdev(fs, flags, dev_name, data, -+ yaffs2_internal_read_super_mtd); -+} -+#endif -+ -+static struct file_system_type yaffs2_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "yaffs2", -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) -+ .mount = yaffs2_mount, -+#else -+ .get_sb = yaffs2_read_super, -+#endif -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV, -+}; -+#else -+static struct super_block *yaffs2_read_super(struct super_block *sb, -+ void *data, int silent) -+{ -+ return yaffs_internal_read_super(2, sb, data, silent); -+} -+ -+static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, -+ FS_REQUIRES_DEV); -+#endif -+ -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) -+static struct proc_dir_entry *my_proc_entry; -+ -+static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) -+{ -+ struct yaffs_param *param = &dev->param; -+ int bs[10]; -+ -+ yaffs_count_blocks_by_state(dev,bs); -+ -+ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); -+ buf += sprintf(buf, "end_block............ %d\n", param->end_block); -+ buf += sprintf(buf, "total_bytes_per_chunk %d\n", -+ param->total_bytes_per_chunk); -+ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); -+ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); -+ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); -+ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); -+ buf += sprintf(buf, "empty_lost_n_found... %d\n", -+ param->empty_lost_n_found); -+ buf += sprintf(buf, "disable_lazy_load.... %d\n", -+ param->disable_lazy_load); -+ buf += sprintf(buf, "disable_bad_block_mrk %d\n", -+ param->disable_bad_block_marking); -+ buf += sprintf(buf, "refresh_period....... %d\n", -+ param->refresh_period); -+ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); -+ buf += sprintf(buf, "n_reserved_blocks.... %d\n", -+ param->n_reserved_blocks); -+ buf += sprintf(buf, "always_check_erased.. %d\n", -+ param->always_check_erased); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "block count by state\n"); -+ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", -+ bs[0], bs[1], bs[2], bs[3], bs[4]); -+ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", -+ bs[5], bs[6], bs[7], bs[8], bs[9]); -+ -+ return buf; -+} -+ -+static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) -+{ -+ buf += sprintf(buf, "max file size....... %lld\n", -+ (long long) yaffs_max_file_size(dev)); -+ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", -+ dev->data_bytes_per_chunk); -+ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); -+ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); -+ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); -+ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", -+ dev->blocks_in_checkpt); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); -+ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); -+ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); -+ buf += sprintf(buf, "\n"); -+ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); -+ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); -+ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); -+ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); -+ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); -+ buf += sprintf(buf, "passive_gc_count..... %u\n", -+ dev->passive_gc_count); -+ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", -+ dev->oldest_dirty_gc_count); -+ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); -+ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); -+ buf += sprintf(buf, "n_retried_writes..... %u\n", -+ dev->n_retried_writes); -+ buf += sprintf(buf, "n_retired_blocks..... %u\n", -+ dev->n_retired_blocks); -+ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); -+ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); -+ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", -+ dev->n_tags_ecc_fixed); -+ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", -+ dev->n_tags_ecc_unfixed); -+ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); -+ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); -+ buf += sprintf(buf, "n_unlinked_files..... %u\n", -+ dev->n_unlinked_files); -+ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); -+ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); -+ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); -+ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); -+ -+ return buf; -+} -+ -+static int yaffs_proc_read(char *page, -+ char **start, -+ off_t offset, int count, int *eof, void *data) -+{ -+ struct list_head *item; -+ char *buf = page; -+ int step = offset; -+ int n = 0; -+ -+ /* Get proc_file_read() to step 'offset' by one on each sucessive call. -+ * We use 'offset' (*ppos) to indicate where we are in dev_list. -+ * This also assumes the user has posted a read buffer large -+ * enough to hold the complete output; but that's life in /proc. -+ */ -+ -+ *(int *)start = 1; -+ -+ /* Print header first */ -+ if (step == 0) -+ buf += -+ sprintf(buf, -+ "Multi-version YAFFS built:" __DATE__ " " __TIME__ -+ "\n"); -+ else if (step == 1) -+ buf += sprintf(buf, "\n"); -+ else { -+ step -= 2; -+ -+ mutex_lock(&yaffs_context_lock); -+ -+ /* Locate and print the Nth entry. Order N-squared but N is small. */ -+ list_for_each(item, &yaffs_context_list) { -+ struct yaffs_linux_context *dc = -+ list_entry(item, struct yaffs_linux_context, -+ context_list); -+ struct yaffs_dev *dev = dc->dev; -+ -+ if (n < (step & ~1)) { -+ n += 2; -+ continue; -+ } -+ if ((step & 1) == 0) { -+ buf += -+ sprintf(buf, "\nDevice %d \"%s\"\n", n, -+ dev->param.name); -+ buf = yaffs_dump_dev_part0(buf, dev); -+ } else { -+ buf = yaffs_dump_dev_part1(buf, dev); -+ } -+ -+ break; -+ } -+ mutex_unlock(&yaffs_context_lock); -+ } -+ -+ return buf - page < count ? buf - page : count; -+} -+ -+/** -+ * Set the verbosity of the warnings and error messages. -+ * -+ * Note that the names can only be a..z or _ with the current code. -+ */ -+ -+static struct { -+ char *mask_name; -+ unsigned mask_bitfield; -+} mask_flags[] = { -+ {"allocate", YAFFS_TRACE_ALLOCATE}, -+ {"always", YAFFS_TRACE_ALWAYS}, -+ {"background", YAFFS_TRACE_BACKGROUND}, -+ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, -+ {"buffers", YAFFS_TRACE_BUFFERS}, -+ {"bug", YAFFS_TRACE_BUG}, -+ {"checkpt", YAFFS_TRACE_CHECKPOINT}, -+ {"deletion", YAFFS_TRACE_DELETION}, -+ {"erase", YAFFS_TRACE_ERASE}, -+ {"error", YAFFS_TRACE_ERROR}, -+ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, -+ {"gc", YAFFS_TRACE_GC}, -+ {"lock", YAFFS_TRACE_LOCK}, -+ {"mtd", YAFFS_TRACE_MTD}, -+ {"nandaccess", YAFFS_TRACE_NANDACCESS}, -+ {"os", YAFFS_TRACE_OS}, -+ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, -+ {"scan", YAFFS_TRACE_SCAN}, -+ {"mount", YAFFS_TRACE_MOUNT}, -+ {"tracing", YAFFS_TRACE_TRACING}, -+ {"sync", YAFFS_TRACE_SYNC}, -+ {"write", YAFFS_TRACE_WRITE}, -+ {"verify", YAFFS_TRACE_VERIFY}, -+ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, -+ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, -+ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, -+ {"all", 0xffffffff}, -+ {"none", 0}, -+ {NULL, 0}, -+}; -+ -+#define MAX_MASK_NAME_LENGTH 40 -+static int yaffs_proc_write_trace_options(struct file *file, const char *buf, -+ unsigned long count, void *data) -+{ -+ unsigned rg = 0, mask_bitfield; -+ char *end; -+ char *mask_name; -+ const char *x; -+ char substring[MAX_MASK_NAME_LENGTH + 1]; -+ int i; -+ int done = 0; -+ int add, len = 0; -+ int pos = 0; -+ -+ rg = yaffs_trace_mask; -+ -+ while (!done && (pos < count)) { -+ done = 1; -+ while ((pos < count) && isspace(buf[pos])) -+ pos++; -+ -+ switch (buf[pos]) { -+ case '+': -+ case '-': -+ case '=': -+ add = buf[pos]; -+ pos++; -+ break; -+ -+ default: -+ add = ' '; -+ break; -+ } -+ mask_name = NULL; -+ -+ mask_bitfield = simple_strtoul(buf + pos, &end, 0); -+ -+ if (end > buf + pos) { -+ mask_name = "numeral"; -+ len = end - (buf + pos); -+ pos += len; -+ done = 0; -+ } else { -+ for (x = buf + pos, i = 0; -+ (*x == '_' || (*x >= 'a' && *x <= 'z')) && -+ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) -+ substring[i] = *x; -+ substring[i] = '\0'; -+ -+ for (i = 0; mask_flags[i].mask_name != NULL; i++) { -+ if (strcmp(substring, mask_flags[i].mask_name) -+ == 0) { -+ mask_name = mask_flags[i].mask_name; -+ mask_bitfield = -+ mask_flags[i].mask_bitfield; -+ done = 0; -+ break; -+ } -+ } -+ } -+ -+ if (mask_name != NULL) { -+ done = 0; -+ switch (add) { -+ case '-': -+ rg &= ~mask_bitfield; -+ break; -+ case '+': -+ rg |= mask_bitfield; -+ break; -+ case '=': -+ rg = mask_bitfield; -+ break; -+ default: -+ rg |= mask_bitfield; -+ break; -+ } -+ } -+ } -+ -+ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; -+ -+ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); -+ -+ if (rg & YAFFS_TRACE_ALWAYS) { -+ for (i = 0; mask_flags[i].mask_name != NULL; i++) { -+ char flag; -+ flag = ((rg & mask_flags[i].mask_bitfield) == -+ mask_flags[i].mask_bitfield) ? '+' : '-'; -+ printk(KERN_DEBUG "%c%s\n", flag, -+ mask_flags[i].mask_name); -+ } -+ } -+ -+ return count; -+} -+ -+/* Debug strings are of the form: -+ * .bnnn print info on block n -+ * .cobjn,chunkn print nand chunk id for objn:chunkn -+ */ -+ -+static int yaffs_proc_debug_write(struct file *file, const char *buf, -+ unsigned long count, void *data) -+{ -+ -+ char str[100]; -+ char *p0; -+ char *p1; -+ long p1_val; -+ long p0_val; -+ char cmd; -+ struct list_head *item; -+ -+ memset(str, 0, sizeof(str)); -+ memcpy(str, buf, min(count, sizeof(str) -1)); -+ -+ cmd = str[1]; -+ -+ p0 = str + 2; -+ -+ p1 = p0; -+ -+ while (*p1 && *p1 != ',') { -+ p1++; -+ } -+ *p1 = '\0'; -+ p1++; -+ -+ p0_val = simple_strtol(p0, NULL, 0); -+ p1_val = simple_strtol(p1, NULL, 0); -+ -+ -+ mutex_lock(&yaffs_context_lock); -+ -+ /* Locate and print the Nth entry. Order N-squared but N is small. */ -+ list_for_each(item, &yaffs_context_list) { -+ struct yaffs_linux_context *dc = -+ list_entry(item, struct yaffs_linux_context, -+ context_list); -+ struct yaffs_dev *dev = dc->dev; -+ -+ if (cmd == 'b') { -+ struct yaffs_block_info *bi; -+ -+ bi = yaffs_get_block_info(dev,p0_val); -+ -+ if(bi) { -+ printk("Block %d: state %d, retire %d, use %d, seq %d\n", -+ (int)p0_val, bi->block_state, -+ bi->needs_retiring, bi->pages_in_use, -+ bi->seq_number); -+ } -+ } else if (cmd == 'c') { -+ struct yaffs_obj *obj; -+ int nand_chunk; -+ -+ obj = yaffs_find_by_number(dev, p0_val); -+ if (!obj) -+ printk("No obj %d\n", (int)p0_val); -+ else { -+ if(p1_val == 0) -+ nand_chunk = obj->hdr_chunk; -+ else -+ nand_chunk = -+ yaffs_find_chunk_in_file(obj, -+ p1_val, NULL); -+ printk("Nand chunk for %d:%d is %d\n", -+ (int)p0_val, (int)p1_val, nand_chunk); -+ } -+ } -+ } -+ -+ mutex_unlock(&yaffs_context_lock); -+ -+ return count; -+} -+ -+static int yaffs_proc_write(struct file *file, const char *buf, -+ unsigned long count, void *data) -+{ -+ if (buf[0] == '.') -+ return yaffs_proc_debug_write(file, buf, count, data); -+ return yaffs_proc_write_trace_options(file, buf, count, data); -+} -+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) */ -+ -+/* Stuff to handle installation of file systems */ -+struct file_system_to_install { -+ struct file_system_type *fst; -+ int installed; -+}; -+ -+static struct file_system_to_install fs_to_install[] = { -+ {&yaffs_fs_type, 0}, -+ {&yaffs2_fs_type, 0}, -+ {NULL, 0} -+}; -+ -+static int __init init_yaffs_fs(void) -+{ -+ int error = 0; -+ struct file_system_to_install *fsinst; -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs built " __DATE__ " " __TIME__ " Installing."); -+ -+ mutex_init(&yaffs_context_lock); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) -+ /* Install the proc_fs entries */ -+ my_proc_entry = create_proc_entry("yaffs", -+ S_IRUGO | S_IFREG, YPROC_ROOT); -+ -+ if (my_proc_entry) { -+ my_proc_entry->write_proc = yaffs_proc_write; -+ my_proc_entry->read_proc = yaffs_proc_read; -+ my_proc_entry->data = NULL; -+ } else { -+ return -ENOMEM; -+ } -+#endif -+ -+ /* Now add the file system entries */ -+ -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst && !error) { -+ error = register_filesystem(fsinst->fst); -+ if (!error) -+ fsinst->installed = 1; -+ fsinst++; -+ } -+ -+ /* Any errors? uninstall */ -+ if (error) { -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst) { -+ if (fsinst->installed) { -+ unregister_filesystem(fsinst->fst); -+ fsinst->installed = 0; -+ } -+ fsinst++; -+ } -+ } -+ -+ return error; -+} -+ -+static void __exit exit_yaffs_fs(void) -+{ -+ -+ struct file_system_to_install *fsinst; -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "yaffs built " __DATE__ " " __TIME__ " removing."); -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) -+ remove_proc_entry("yaffs", YPROC_ROOT); -+#endif -+ -+ fsinst = fs_to_install; -+ -+ while (fsinst->fst) { -+ if (fsinst->installed) { -+ unregister_filesystem(fsinst->fst); -+ fsinst->installed = 0; -+ } -+ fsinst++; -+ } -+} -+ -+module_init(init_yaffs_fs) -+ module_exit(exit_yaffs_fs) -+ -+ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); -+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); -+MODULE_LICENSE("GPL"); -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.4.90/fs/yaffs2/yaffs_yaffs1.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,422 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_yaffs1.h" -+#include "yportenv.h" -+#include "yaffs_trace.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_nand.h" -+#include "yaffs_attribs.h" -+ -+int yaffs1_scan(struct yaffs_dev *dev) -+{ -+ struct yaffs_ext_tags tags; -+ int blk; -+ int result; -+ int chunk; -+ int c; -+ int deleted; -+ enum yaffs_block_state state; -+ LIST_HEAD(hard_list); -+ struct yaffs_block_info *bi; -+ u32 seq_number; -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_obj *in; -+ struct yaffs_obj *parent; -+ int alloc_failed = 0; -+ struct yaffs_shadow_fixer *shadow_fixers = NULL; -+ u8 *chunk_data; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs1_scan starts intstartblk %d intendblk %d...", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ chunk_data = yaffs_get_temp_buffer(dev); -+ -+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; -+ -+ /* Scan all the blocks to determine their state */ -+ bi = dev->block_info; -+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; -+ blk++) { -+ yaffs_clear_chunk_bits(dev, blk); -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ -+ yaffs_query_init_block_state(dev, blk, &state, &seq_number); -+ -+ bi->block_state = state; -+ bi->seq_number = seq_number; -+ -+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) -+ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, -+ "Block scanning block %d state %d seq %d", -+ blk, state, seq_number); -+ -+ if (state == YAFFS_BLOCK_STATE_DEAD) { -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, -+ "block %d is bad", blk); -+ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += dev->param.chunks_per_block; -+ } -+ bi++; -+ } -+ -+ /* For each block.... */ -+ for (blk = dev->internal_start_block; -+ !alloc_failed && blk <= dev->internal_end_block; blk++) { -+ -+ cond_resched(); -+ -+ bi = yaffs_get_block_info(dev, blk); -+ state = bi->block_state; -+ -+ deleted = 0; -+ -+ /* For each chunk in each block that needs scanning.... */ -+ for (c = 0; -+ !alloc_failed && c < dev->param.chunks_per_block && -+ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { -+ /* Read the tags and decide what to do */ -+ chunk = blk * dev->param.chunks_per_block + c; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, -+ &tags); -+ -+ /* Let's have a good look at this chunk... */ -+ -+ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || -+ tags.is_deleted) { -+ /* YAFFS1 only... -+ * A deleted chunk -+ */ -+ deleted++; -+ dev->n_free_chunks++; -+ } else if (!tags.chunk_used) { -+ /* An unassigned chunk in the block -+ * This means that either the block is empty or -+ * this is the one being allocated from -+ */ -+ -+ if (c == 0) { -+ /* We're looking at the first chunk in -+ *the block so the block is unused */ -+ state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ } else { -+ /* this is the block being allocated */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Allocating from %d %d", -+ blk, c); -+ state = YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->alloc_block = blk; -+ dev->alloc_page = c; -+ dev->alloc_block_finder = blk; -+ -+ } -+ -+ dev->n_free_chunks += -+ (dev->param.chunks_per_block - c); -+ } else if (tags.chunk_id > 0) { -+ /* chunk_id > 0 so it is a data chunk... */ -+ unsigned int endpos; -+ -+ yaffs_set_chunk_bit(dev, blk, c); -+ bi->pages_in_use++; -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ YAFFS_OBJECT_TYPE_FILE); -+ /* PutChunkIntoFile checks for a clash -+ * (two data chunks with the same chunk_id). -+ */ -+ -+ if (!in) -+ alloc_failed = 1; -+ -+ if (in) { -+ if (!yaffs_put_chunk_in_file -+ (in, tags.chunk_id, chunk, 1)) -+ alloc_failed = 1; -+ } -+ -+ endpos = -+ (tags.chunk_id - 1) * -+ dev->data_bytes_per_chunk + -+ tags.n_bytes; -+ if (in && -+ in->variant_type == -+ YAFFS_OBJECT_TYPE_FILE && -+ in->variant.file_variant.scanned_size < -+ endpos) { -+ in->variant.file_variant.scanned_size = -+ endpos; -+ if (!dev->param.use_header_file_size) { -+ in->variant. -+ file_variant.file_size = -+ in->variant. -+ file_variant.scanned_size; -+ } -+ -+ } -+ } else { -+ /* chunk_id == 0, so it is an ObjectHeader. -+ * Make the object -+ */ -+ yaffs_set_chunk_bit(dev, blk, c); -+ bi->pages_in_use++; -+ -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, -+ chunk_data, -+ NULL); -+ -+ oh = (struct yaffs_obj_hdr *)chunk_data; -+ -+ in = yaffs_find_by_number(dev, tags.obj_id); -+ if (in && in->variant_type != oh->type) { -+ /* This should not happen, but somehow -+ * Wev'e ended up with an obj_id that -+ * has been reused but not yet deleted, -+ * and worse still it has changed type. -+ * Delete the old object. -+ */ -+ -+ yaffs_del_obj(in); -+ in = NULL; -+ } -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ oh->type); -+ -+ if (!in) -+ alloc_failed = 1; -+ -+ if (in && oh->shadows_obj > 0) { -+ -+ struct yaffs_shadow_fixer *fixer; -+ fixer = -+ kmalloc(sizeof -+ (struct yaffs_shadow_fixer), -+ GFP_NOFS); -+ if (fixer) { -+ fixer->next = shadow_fixers; -+ shadow_fixers = fixer; -+ fixer->obj_id = tags.obj_id; -+ fixer->shadowed_id = -+ oh->shadows_obj; -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Shadow fixer: %d shadows %d", -+ fixer->obj_id, -+ fixer->shadowed_id); -+ -+ } -+ -+ } -+ -+ if (in && in->valid) { -+ /* We have already filled this one. -+ * We have a duplicate and need to -+ * resolve it. */ -+ -+ unsigned existing_serial = in->serial; -+ unsigned new_serial = -+ tags.serial_number; -+ -+ if (((existing_serial + 1) & 3) == -+ new_serial) { -+ /* Use new one - destroy the -+ * exisiting one */ -+ yaffs_chunk_del(dev, -+ in->hdr_chunk, -+ 1, __LINE__); -+ in->valid = 0; -+ } else { -+ /* Use existing - destroy -+ * this one. */ -+ yaffs_chunk_del(dev, chunk, 1, -+ __LINE__); -+ } -+ } -+ -+ if (in && !in->valid && -+ (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == -+ YAFFS_OBJECTID_LOSTNFOUND)) { -+ /* We only load some info, don't fiddle -+ * with directory structure */ -+ in->valid = 1; -+ in->variant_type = oh->type; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->hdr_chunk = chunk; -+ in->serial = tags.serial_number; -+ -+ } else if (in && !in->valid) { -+ /* we need to load this info */ -+ -+ in->valid = 1; -+ in->variant_type = oh->type; -+ -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->hdr_chunk = chunk; -+ in->serial = tags.serial_number; -+ -+ yaffs_set_obj_name_from_oh(in, oh); -+ in->dirty = 0; -+ -+ /* directory stuff... -+ * hook up to parent -+ */ -+ -+ parent = -+ yaffs_find_or_create_by_number -+ (dev, oh->parent_obj_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ if (!parent) -+ alloc_failed = 1; -+ if (parent && parent->variant_type == -+ YAFFS_OBJECT_TYPE_UNKNOWN) { -+ /* Set up as a directory */ -+ parent->variant_type = -+ YAFFS_OBJECT_TYPE_DIRECTORY; -+ INIT_LIST_HEAD(&parent-> -+ variant.dir_variant. -+ children); -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ /* Hoosterman, a problem.... -+ * We're trying to use a -+ * non-directory as a directory -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." -+ ); -+ parent = dev->lost_n_found; -+ } -+ -+ yaffs_add_obj_to_dir(parent, in); -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Todo got a problem */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ if (dev->param. -+ use_header_file_size) -+ in->variant. -+ file_variant.file_size -+ = yaffs_oh_to_size(oh); -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ in->variant. -+ hardlink_variant.equiv_id = -+ oh->equiv_id; -+ list_add(&in->hard_links, -+ &hard_list); -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ in->variant.symlink_variant. -+ alias = -+ yaffs_clone_str(oh->alias); -+ if (!in->variant. -+ symlink_variant.alias) -+ alloc_failed = 1; -+ break; -+ } -+ } -+ } -+ } -+ -+ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* If we got this far while scanning, -+ * then the block is fully allocated. */ -+ state = YAFFS_BLOCK_STATE_FULL; -+ } -+ -+ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ /* If the block was partially allocated then -+ * treat it as fully allocated. */ -+ state = YAFFS_BLOCK_STATE_FULL; -+ dev->alloc_block = -1; -+ } -+ -+ bi->block_state = state; -+ -+ /* Now let's see if it was dirty */ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state == YAFFS_BLOCK_STATE_FULL) -+ yaffs_block_became_dirty(dev, blk); -+ } -+ -+ /* Ok, we've done all the scanning. -+ * Fix up the hard link chains. -+ * We should now have scanned all the objects, now it's time to add -+ * these hardlinks. -+ */ -+ -+ yaffs_link_fixup(dev, &hard_list); -+ -+ /* -+ * Fix up any shadowed objects. -+ * There should not be more than one of these. -+ */ -+ { -+ struct yaffs_shadow_fixer *fixer; -+ struct yaffs_obj *obj; -+ -+ while (shadow_fixers) { -+ fixer = shadow_fixers; -+ shadow_fixers = fixer->next; -+ /* Complete the rename transaction by deleting the -+ * shadowed object then setting the object header -+ to unshadowed. -+ */ -+ obj = yaffs_find_by_number(dev, fixer->shadowed_id); -+ if (obj) -+ yaffs_del_obj(obj); -+ -+ obj = yaffs_find_by_number(dev, fixer->obj_id); -+ -+ if (obj) -+ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); -+ -+ kfree(fixer); -+ } -+ } -+ -+ yaffs_release_temp_buffer(dev, chunk_data); -+ -+ if (alloc_failed) -+ return YAFFS_FAIL; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.4.90/fs/yaffs2/yaffs_yaffs1.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,22 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_YAFFS1_H__ -+#define __YAFFS_YAFFS1_H__ -+ -+#include "yaffs_guts.h" -+int yaffs1_scan(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.4.90/fs/yaffs2/yaffs_yaffs2.c ---- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,1534 @@ -+/* -+ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include "yaffs_guts.h" -+#include "yaffs_trace.h" -+#include "yaffs_yaffs2.h" -+#include "yaffs_checkptrw.h" -+#include "yaffs_bitmap.h" -+#include "yaffs_nand.h" -+#include "yaffs_getblockinfo.h" -+#include "yaffs_verify.h" -+#include "yaffs_attribs.h" -+#include "yaffs_summary.h" -+ -+/* -+ * Checkpoints are really no benefit on very small partitions. -+ * -+ * To save space on small partitions don't bother with checkpoints unless -+ * the partition is at least this big. -+ */ -+#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 -+#define YAFFS_SMALL_HOLE_THRESHOLD 4 -+ -+/* -+ * Oldest Dirty Sequence Number handling. -+ */ -+ -+/* yaffs_calc_oldest_dirty_seq() -+ * yaffs2_find_oldest_dirty_seq() -+ * Calculate the oldest dirty sequence number if we don't know it. -+ */ -+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) -+{ -+ int i; -+ unsigned seq; -+ unsigned block_no = 0; -+ struct yaffs_block_info *b; -+ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ /* Find the oldest dirty sequence number. */ -+ seq = dev->seq_number + 1; -+ b = dev->block_info; -+ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { -+ if (b->block_state == YAFFS_BLOCK_STATE_FULL && -+ (b->pages_in_use - b->soft_del_pages) < -+ dev->param.chunks_per_block && -+ b->seq_number < seq) { -+ seq = b->seq_number; -+ block_no = i; -+ } -+ b++; -+ } -+ -+ if (block_no) { -+ dev->oldest_dirty_seq = seq; -+ dev->oldest_dirty_block = block_no; -+ } -+} -+ -+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!dev->oldest_dirty_seq) -+ yaffs_calc_oldest_dirty_seq(dev); -+} -+ -+/* -+ * yaffs_clear_oldest_dirty_seq() -+ * Called when a block is erased or marked bad. (ie. when its seq_number -+ * becomes invalid). If the value matches the oldest then we clear -+ * dev->oldest_dirty_seq to force its recomputation. -+ */ -+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi) -+{ -+ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { -+ dev->oldest_dirty_seq = 0; -+ dev->oldest_dirty_block = 0; -+ } -+} -+ -+/* -+ * yaffs2_update_oldest_dirty_seq() -+ * Update the oldest dirty sequence number whenever we dirty a block. -+ * Only do this if the oldest_dirty_seq is actually being tracked. -+ */ -+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, -+ struct yaffs_block_info *bi) -+{ -+ if (!dev->param.is_yaffs2) -+ return; -+ -+ if (dev->oldest_dirty_seq) { -+ if (dev->oldest_dirty_seq > bi->seq_number) { -+ dev->oldest_dirty_seq = bi->seq_number; -+ dev->oldest_dirty_block = block_no; -+ } -+ } -+} -+ -+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) -+{ -+ -+ if (!dev->param.is_yaffs2) -+ return 1; /* disqualification only applies to yaffs2. */ -+ -+ if (!bi->has_shrink_hdr) -+ return 1; /* can gc */ -+ -+ yaffs2_find_oldest_dirty_seq(dev); -+ -+ /* Can't do gc of this block if there are any blocks older than this -+ * one that have discarded pages. -+ */ -+ return (bi->seq_number <= dev->oldest_dirty_seq); -+} -+ -+/* -+ * yaffs2_find_refresh_block() -+ * periodically finds the oldest full block by sequence number for refreshing. -+ * Only for yaffs2. -+ */ -+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) -+{ -+ u32 b; -+ u32 oldest = 0; -+ u32 oldest_seq = 0; -+ struct yaffs_block_info *bi; -+ -+ if (!dev->param.is_yaffs2) -+ return oldest; -+ -+ /* -+ * If refresh period < 10 then refreshing is disabled. -+ */ -+ if (dev->param.refresh_period < 10) -+ return oldest; -+ -+ /* -+ * Fix broken values. -+ */ -+ if (dev->refresh_skip > dev->param.refresh_period) -+ dev->refresh_skip = dev->param.refresh_period; -+ -+ if (dev->refresh_skip > 0) -+ return oldest; -+ -+ /* -+ * Refresh skip is now zero. -+ * We'll do a refresh this time around.... -+ * Update the refresh skip and find the oldest block. -+ */ -+ dev->refresh_skip = dev->param.refresh_period; -+ dev->refresh_count++; -+ bi = dev->block_info; -+ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { -+ -+ if (oldest < 1 || bi->seq_number < oldest_seq) { -+ oldest = b; -+ oldest_seq = bi->seq_number; -+ } -+ } -+ bi++; -+ } -+ -+ if (oldest > 0) { -+ yaffs_trace(YAFFS_TRACE_GC, -+ "GC refresh count %d selected block %d with seq_number %d", -+ dev->refresh_count, oldest, oldest_seq); -+ } -+ -+ return oldest; -+} -+ -+int yaffs2_checkpt_required(struct yaffs_dev *dev) -+{ -+ int nblocks; -+ -+ if (!dev->param.is_yaffs2) -+ return 0; -+ -+ nblocks = dev->internal_end_block - dev->internal_start_block + 1; -+ -+ return !dev->param.skip_checkpt_wr && -+ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); -+} -+ -+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) -+{ -+ int retval; -+ int n_bytes = 0; -+ int n_blocks; -+ int dev_blocks; -+ -+ if (!dev->param.is_yaffs2) -+ return 0; -+ -+ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { -+ /* Not a valid value so recalculate */ -+ dev_blocks = dev->param.end_block - dev->param.start_block + 1; -+ n_bytes += sizeof(struct yaffs_checkpt_validity); -+ n_bytes += sizeof(struct yaffs_checkpt_dev); -+ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); -+ n_bytes += dev_blocks * dev->chunk_bit_stride; -+ n_bytes += -+ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * -+ dev->n_obj; -+ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; -+ n_bytes += sizeof(struct yaffs_checkpt_validity); -+ n_bytes += sizeof(u32); /* checksum */ -+ -+ /* Round up and add 2 blocks to allow for some bad blocks, -+ * so add 3 */ -+ -+ n_blocks = -+ (n_bytes / -+ (dev->data_bytes_per_chunk * -+ dev->param.chunks_per_block)) + 3; -+ -+ dev->checkpoint_blocks_required = n_blocks; -+ } -+ -+ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; -+ if (retval < 0) -+ retval = 0; -+ return retval; -+} -+ -+/*--------------------- Checkpointing --------------------*/ -+ -+static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) -+{ -+ struct yaffs_checkpt_validity cp; -+ -+ memset(&cp, 0, sizeof(cp)); -+ -+ cp.struct_type = sizeof(cp); -+ cp.magic = YAFFS_MAGIC; -+ cp.version = YAFFS_CHECKPOINT_VERSION; -+ cp.head = (head) ? 1 : 0; -+ -+ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) -+{ -+ struct yaffs_checkpt_validity cp; -+ int ok; -+ -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ -+ if (ok) -+ ok = (cp.struct_type == sizeof(cp)) && -+ (cp.magic == YAFFS_MAGIC) && -+ (cp.version == YAFFS_CHECKPOINT_VERSION) && -+ (cp.head == ((head) ? 1 : 0)); -+ return ok ? 1 : 0; -+} -+ -+static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, -+ struct yaffs_dev *dev) -+{ -+ cp->n_erased_blocks = dev->n_erased_blocks; -+ cp->alloc_block = dev->alloc_block; -+ cp->alloc_page = dev->alloc_page; -+ cp->n_free_chunks = dev->n_free_chunks; -+ -+ cp->n_deleted_files = dev->n_deleted_files; -+ cp->n_unlinked_files = dev->n_unlinked_files; -+ cp->n_bg_deletions = dev->n_bg_deletions; -+ cp->seq_number = dev->seq_number; -+ -+} -+ -+static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, -+ struct yaffs_checkpt_dev *cp) -+{ -+ dev->n_erased_blocks = cp->n_erased_blocks; -+ dev->alloc_block = cp->alloc_block; -+ dev->alloc_page = cp->alloc_page; -+ dev->n_free_chunks = cp->n_free_chunks; -+ -+ dev->n_deleted_files = cp->n_deleted_files; -+ dev->n_unlinked_files = cp->n_unlinked_files; -+ dev->n_bg_deletions = cp->n_bg_deletions; -+ dev->seq_number = cp->seq_number; -+} -+ -+static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_dev cp; -+ u32 n_bytes; -+ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ int ok; -+ -+ /* Write device runtime values */ -+ yaffs2_dev_to_checkpt_dev(&cp, dev); -+ cp.struct_type = sizeof(cp); -+ -+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (!ok) -+ return 0; -+ -+ /* Write block info */ -+ n_bytes = n_blocks * sizeof(struct yaffs_block_info); -+ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); -+ if (!ok) -+ return 0; -+ -+ /* Write chunk bits */ -+ n_bytes = n_blocks * dev->chunk_bit_stride; -+ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) -+{ -+ struct yaffs_checkpt_dev cp; -+ u32 n_bytes; -+ u32 n_blocks = -+ (dev->internal_end_block - dev->internal_start_block + 1); -+ int ok; -+ -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (!ok) -+ return 0; -+ -+ if (cp.struct_type != sizeof(cp)) -+ return 0; -+ -+ yaffs_checkpt_dev_to_dev(dev, &cp); -+ -+ n_bytes = n_blocks * sizeof(struct yaffs_block_info); -+ -+ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); -+ -+ if (!ok) -+ return 0; -+ -+ n_bytes = n_blocks * dev->chunk_bit_stride; -+ -+ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); -+ -+ return ok ? 1 : 0; -+} -+ -+static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, -+ struct yaffs_obj *obj) -+{ -+ cp->obj_id = obj->obj_id; -+ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; -+ cp->hdr_chunk = obj->hdr_chunk; -+ cp->variant_type = obj->variant_type; -+ cp->deleted = obj->deleted; -+ cp->soft_del = obj->soft_del; -+ cp->unlinked = obj->unlinked; -+ cp->fake = obj->fake; -+ cp->rename_allowed = obj->rename_allowed; -+ cp->unlink_allowed = obj->unlink_allowed; -+ cp->serial = obj->serial; -+ cp->n_data_chunks = obj->n_data_chunks; -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; -+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; -+} -+ -+static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, -+ struct yaffs_checkpt_obj *cp) -+{ -+ struct yaffs_obj *parent; -+ -+ if (obj->variant_type != cp->variant_type) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", -+ cp->obj_id, cp->variant_type, cp->hdr_chunk, -+ obj->variant_type); -+ return 0; -+ } -+ -+ obj->obj_id = cp->obj_id; -+ -+ if (cp->parent_id) -+ parent = yaffs_find_or_create_by_number(obj->my_dev, -+ cp->parent_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ else -+ parent = NULL; -+ -+ if (parent) { -+ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { -+ yaffs_trace(YAFFS_TRACE_ALWAYS, -+ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", -+ cp->obj_id, cp->parent_id, -+ cp->variant_type, cp->hdr_chunk, -+ parent->variant_type); -+ return 0; -+ } -+ yaffs_add_obj_to_dir(parent, obj); -+ } -+ -+ obj->hdr_chunk = cp->hdr_chunk; -+ obj->variant_type = cp->variant_type; -+ obj->deleted = cp->deleted; -+ obj->soft_del = cp->soft_del; -+ obj->unlinked = cp->unlinked; -+ obj->fake = cp->fake; -+ obj->rename_allowed = cp->rename_allowed; -+ obj->unlink_allowed = cp->unlink_allowed; -+ obj->serial = cp->serial; -+ obj->n_data_chunks = cp->n_data_chunks; -+ -+ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) -+ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; -+ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) -+ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; -+ -+ if (obj->hdr_chunk > 0) -+ obj->lazy_loaded = 1; -+ return 1; -+} -+ -+static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, -+ struct yaffs_tnode *tn, u32 level, -+ int chunk_offset) -+{ -+ int i; -+ struct yaffs_dev *dev = in->my_dev; -+ int ok = 1; -+ u32 base_offset; -+ -+ if (!tn) -+ return 1; -+ -+ if (level > 0) { -+ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { -+ if (!tn->internal[i]) -+ continue; -+ ok = yaffs2_checkpt_tnode_worker(in, -+ tn->internal[i], -+ level - 1, -+ (chunk_offset << -+ YAFFS_TNODES_INTERNAL_BITS) + i); -+ } -+ return ok; -+ } -+ -+ /* Level 0 tnode */ -+ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; -+ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == -+ sizeof(base_offset)); -+ if (ok) -+ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == -+ dev->tnode_size); -+ -+ return ok; -+} -+ -+static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) -+{ -+ u32 end_marker = ~0; -+ int ok = 1; -+ -+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return ok; -+ -+ ok = yaffs2_checkpt_tnode_worker(obj, -+ obj->variant.file_variant.top, -+ obj->variant.file_variant. -+ top_level, 0); -+ if (ok) -+ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, -+ sizeof(end_marker)) == sizeof(end_marker)); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) -+{ -+ u32 base_chunk; -+ int ok = 1; -+ struct yaffs_dev *dev = obj->my_dev; -+ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; -+ struct yaffs_tnode *tn; -+ int nread = 0; -+ -+ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == -+ sizeof(base_chunk)); -+ -+ while (ok && (~base_chunk)) { -+ nread++; -+ /* Read level 0 tnode */ -+ -+ tn = yaffs_get_tnode(dev); -+ if (tn) -+ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == -+ dev->tnode_size); -+ else -+ ok = 0; -+ -+ if (tn && ok) -+ ok = yaffs_add_find_tnode_0(dev, -+ file_stuct_ptr, -+ base_chunk, tn) ? 1 : 0; -+ -+ if (ok) -+ ok = (yaffs2_checkpt_rd -+ (dev, &base_chunk, -+ sizeof(base_chunk)) == sizeof(base_chunk)); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint read tnodes %d records, last %d. ok %d", -+ nread, base_chunk, ok); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_checkpt_obj cp; -+ int i; -+ int ok = 1; -+ struct list_head *lh; -+ -+ /* Iterate through the objects in each hash entry, -+ * dumping them to the checkpointing stream. -+ */ -+ -+ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { -+ list_for_each(lh, &dev->obj_bucket[i].list) { -+ obj = list_entry(lh, struct yaffs_obj, hash_link); -+ if (!obj->defered_free) { -+ yaffs2_obj_checkpt_obj(&cp, obj); -+ cp.struct_type = sizeof(cp); -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", -+ cp.obj_id, cp.parent_id, -+ cp.variant_type, cp.hdr_chunk, obj); -+ -+ ok = (yaffs2_checkpt_wr(dev, &cp, -+ sizeof(cp)) == sizeof(cp)); -+ -+ if (ok && -+ obj->variant_type == -+ YAFFS_OBJECT_TYPE_FILE) -+ ok = yaffs2_wr_checkpt_tnodes(obj); -+ } -+ } -+ } -+ -+ /* Dump end of list */ -+ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); -+ cp.struct_type = sizeof(cp); -+ -+ if (ok) -+ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) -+{ -+ struct yaffs_obj *obj; -+ struct yaffs_checkpt_obj cp; -+ int ok = 1; -+ int done = 0; -+ LIST_HEAD(hard_list); -+ -+ -+ while (ok && !done) { -+ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); -+ if (cp.struct_type != sizeof(cp)) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "struct size %d instead of %d ok %d", -+ cp.struct_type, (int)sizeof(cp), ok); -+ ok = 0; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "Checkpoint read object %d parent %d type %d chunk %d ", -+ cp.obj_id, cp.parent_id, cp.variant_type, -+ cp.hdr_chunk); -+ -+ if (ok && cp.obj_id == ~0) { -+ done = 1; -+ } else if (ok) { -+ obj = -+ yaffs_find_or_create_by_number(dev, cp.obj_id, -+ cp.variant_type); -+ if (obj) { -+ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); -+ if (!ok) -+ break; -+ if (obj->variant_type == -+ YAFFS_OBJECT_TYPE_FILE) { -+ ok = yaffs2_rd_checkpt_tnodes(obj); -+ } else if (obj->variant_type == -+ YAFFS_OBJECT_TYPE_HARDLINK) { -+ list_add(&obj->hard_links, &hard_list); -+ } -+ } else { -+ ok = 0; -+ } -+ } -+ } -+ -+ if (ok) -+ yaffs_link_fixup(dev, &hard_list); -+ -+ return ok ? 1 : 0; -+} -+ -+static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) -+{ -+ u32 checkpt_sum; -+ int ok; -+ -+ yaffs2_get_checkpt_sum(dev, &checkpt_sum); -+ -+ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == -+ sizeof(checkpt_sum)); -+ -+ if (!ok) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) -+{ -+ u32 checkpt_sum0; -+ u32 checkpt_sum1; -+ int ok; -+ -+ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); -+ -+ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == -+ sizeof(checkpt_sum1)); -+ -+ if (!ok) -+ return 0; -+ -+ if (checkpt_sum0 != checkpt_sum1) -+ return 0; -+ -+ return 1; -+} -+ -+static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) -+{ -+ int ok = 1; -+ -+ if (!yaffs2_checkpt_required(dev)) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "skipping checkpoint write"); -+ ok = 0; -+ } -+ -+ if (ok) -+ ok = yaffs2_checkpt_open(dev, 1); -+ -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint validity"); -+ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint device"); -+ ok = yaffs2_wr_checkpt_dev(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint objects"); -+ ok = yaffs2_wr_checkpt_objs(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "write checkpoint validity"); -+ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); -+ } -+ -+ if (ok) -+ ok = yaffs2_wr_checkpt_sum(dev); -+ -+ if (!yaffs_checkpt_close(dev)) -+ ok = 0; -+ -+ if (ok) -+ dev->is_checkpointed = 1; -+ else -+ dev->is_checkpointed = 0; -+ -+ return dev->is_checkpointed; -+} -+ -+static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) -+{ -+ int ok = 1; -+ -+ if (!dev->param.is_yaffs2) -+ ok = 0; -+ -+ if (ok && dev->param.skip_checkpt_rd) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "skipping checkpoint read"); -+ ok = 0; -+ } -+ -+ if (ok) -+ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ -+ -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint validity"); -+ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint device"); -+ ok = yaffs2_rd_checkpt_dev(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint objects"); -+ ok = yaffs2_rd_checkpt_objs(dev); -+ } -+ if (ok) { -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint validity"); -+ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); -+ } -+ -+ if (ok) { -+ ok = yaffs2_rd_checkpt_sum(dev); -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "read checkpoint checksum %d", ok); -+ } -+ -+ if (!yaffs_checkpt_close(dev)) -+ ok = 0; -+ -+ if (ok) -+ dev->is_checkpointed = 1; -+ else -+ dev->is_checkpointed = 0; -+ -+ return ok ? 1 : 0; -+} -+ -+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) -+{ -+ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { -+ dev->is_checkpointed = 0; -+ yaffs2_checkpt_invalidate_stream(dev); -+ } -+ if (dev->param.sb_dirty_fn) -+ dev->param.sb_dirty_fn(dev); -+} -+ -+int yaffs_checkpoint_save(struct yaffs_dev *dev) -+{ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "save entry: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ yaffs_verify_objects(dev); -+ yaffs_verify_blocks(dev); -+ yaffs_verify_free_chunks(dev); -+ -+ if (!dev->is_checkpointed) { -+ yaffs2_checkpt_invalidate(dev); -+ yaffs2_wr_checkpt_data(dev); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, -+ "save exit: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ return dev->is_checkpointed; -+} -+ -+int yaffs2_checkpt_restore(struct yaffs_dev *dev) -+{ -+ int retval; -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "restore entry: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ retval = yaffs2_rd_checkpt_data(dev); -+ -+ if (dev->is_checkpointed) { -+ yaffs_verify_objects(dev); -+ yaffs_verify_blocks(dev); -+ yaffs_verify_free_chunks(dev); -+ } -+ -+ yaffs_trace(YAFFS_TRACE_CHECKPOINT, -+ "restore exit: is_checkpointed %d", -+ dev->is_checkpointed); -+ -+ return retval; -+} -+ -+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) -+{ -+ /* if new_size > old_file_size. -+ * We're going to be writing a hole. -+ * If the hole is small then write zeros otherwise write a start -+ * of hole marker. -+ */ -+ loff_t old_file_size; -+ loff_t increase; -+ int small_hole; -+ int result = YAFFS_OK; -+ struct yaffs_dev *dev = NULL; -+ u8 *local_buffer = NULL; -+ int small_increase_ok = 0; -+ -+ if (!obj) -+ return YAFFS_FAIL; -+ -+ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) -+ return YAFFS_FAIL; -+ -+ dev = obj->my_dev; -+ -+ /* Bail out if not yaffs2 mode */ -+ if (!dev->param.is_yaffs2) -+ return YAFFS_OK; -+ -+ old_file_size = obj->variant.file_variant.file_size; -+ -+ if (new_size <= old_file_size) -+ return YAFFS_OK; -+ -+ increase = new_size - old_file_size; -+ -+ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && -+ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) -+ small_hole = 1; -+ else -+ small_hole = 0; -+ -+ if (small_hole) -+ local_buffer = yaffs_get_temp_buffer(dev); -+ -+ if (local_buffer) { -+ /* fill hole with zero bytes */ -+ loff_t pos = old_file_size; -+ int this_write; -+ int written; -+ memset(local_buffer, 0, dev->data_bytes_per_chunk); -+ small_increase_ok = 1; -+ -+ while (increase > 0 && small_increase_ok) { -+ this_write = increase; -+ if (this_write > dev->data_bytes_per_chunk) -+ this_write = dev->data_bytes_per_chunk; -+ written = -+ yaffs_do_file_wr(obj, local_buffer, pos, this_write, -+ 0); -+ if (written == this_write) { -+ pos += this_write; -+ increase -= this_write; -+ } else { -+ small_increase_ok = 0; -+ } -+ } -+ -+ yaffs_release_temp_buffer(dev, local_buffer); -+ -+ /* If out of space then reverse any chunks we've added */ -+ if (!small_increase_ok) -+ yaffs_resize_file_down(obj, old_file_size); -+ } -+ -+ if (!small_increase_ok && -+ obj->parent && -+ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && -+ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { -+ /* Write a hole start header with the old file size */ -+ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); -+ } -+ -+ return result; -+} -+ -+struct yaffs_block_index { -+ int seq; -+ int block; -+}; -+ -+static int yaffs2_ybicmp(const void *a, const void *b) -+{ -+ int aseq = ((struct yaffs_block_index *)a)->seq; -+ int bseq = ((struct yaffs_block_index *)b)->seq; -+ int ablock = ((struct yaffs_block_index *)a)->block; -+ int bblock = ((struct yaffs_block_index *)b)->block; -+ -+ if (aseq == bseq) -+ return ablock - bblock; -+ -+ return aseq - bseq; -+} -+ -+static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi, -+ int blk, int chunk_in_block, -+ int *found_chunks, -+ u8 *chunk_data, -+ struct list_head *hard_list, -+ int summary_available) -+{ -+ struct yaffs_obj_hdr *oh; -+ struct yaffs_obj *in; -+ struct yaffs_obj *parent; -+ int equiv_id; -+ loff_t file_size; -+ int is_shrink; -+ int is_unlinked; -+ struct yaffs_ext_tags tags; -+ int result; -+ int alloc_failed = 0; -+ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; -+ struct yaffs_file_var *file_var; -+ struct yaffs_hardlink_var *hl_var; -+ struct yaffs_symlink_var *sl_var; -+ -+ if (summary_available) { -+ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); -+ tags.seq_number = bi->seq_number; -+ } -+ -+ if (!summary_available || tags.obj_id == 0) { -+ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); -+ dev->tags_used++; -+ } else { -+ dev->summary_used++; -+ } -+ -+ /* Let's have a good look at this chunk... */ -+ -+ if (!tags.chunk_used) { -+ /* An unassigned chunk in the block. -+ * If there are used chunks after this one, then -+ * it is a chunk that was skipped due to failing -+ * the erased check. Just skip it so that it can -+ * be deleted. -+ * But, more typically, We get here when this is -+ * an unallocated chunk and his means that -+ * either the block is empty or this is the one -+ * being allocated from -+ */ -+ -+ if (*found_chunks) { -+ /* This is a chunk that was skipped due -+ * to failing the erased check */ -+ } else if (chunk_in_block == 0) { -+ /* We're looking at the first chunk in -+ * the block so the block is unused */ -+ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; -+ dev->n_erased_blocks++; -+ } else { -+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { -+ if (dev->seq_number == bi->seq_number) { -+ /* Allocating from this block*/ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Allocating from %d %d", -+ blk, chunk_in_block); -+ -+ bi->block_state = -+ YAFFS_BLOCK_STATE_ALLOCATING; -+ dev->alloc_block = blk; -+ dev->alloc_page = chunk_in_block; -+ dev->alloc_block_finder = blk; -+ } else { -+ /* This is a partially written block -+ * that is not the current -+ * allocation block. -+ */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Partially written block %d detected. gc will fix this.", -+ blk); -+ } -+ } -+ } -+ -+ dev->n_free_chunks++; -+ -+ } else if (tags.ecc_result == -+ YAFFS_ECC_RESULT_UNFIXED) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ " Unfixed ECC in chunk(%d:%d), chunk ignored", -+ blk, chunk_in_block); -+ dev->n_free_chunks++; -+ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || -+ tags.chunk_id > YAFFS_MAX_CHUNK_ID || -+ tags.obj_id == YAFFS_OBJECTID_SUMMARY || -+ (tags.chunk_id > 0 && -+ tags.n_bytes > dev->data_bytes_per_chunk) || -+ tags.seq_number != bi->seq_number) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", -+ blk, chunk_in_block, tags.obj_id, -+ tags.chunk_id, tags.n_bytes); -+ dev->n_free_chunks++; -+ } else if (tags.chunk_id > 0) { -+ /* chunk_id > 0 so it is a data chunk... */ -+ loff_t endpos; -+ loff_t chunk_base = (tags.chunk_id - 1) * -+ dev->data_bytes_per_chunk; -+ -+ *found_chunks = 1; -+ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ YAFFS_OBJECT_TYPE_FILE); -+ if (!in) -+ /* Out of memory */ -+ alloc_failed = 1; -+ -+ if (in && -+ in->variant_type == YAFFS_OBJECT_TYPE_FILE && -+ chunk_base < in->variant.file_variant.shrink_size) { -+ /* This has not been invalidated by -+ * a resize */ -+ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, -+ chunk, -1)) -+ alloc_failed = 1; -+ -+ /* File size is calculated by looking at -+ * the data chunks if we have not -+ * seen an object header yet. -+ * Stop this practice once we find an -+ * object header. -+ */ -+ endpos = chunk_base + tags.n_bytes; -+ -+ if (!in->valid && -+ in->variant.file_variant.scanned_size < endpos) { -+ in->variant.file_variant. -+ scanned_size = endpos; -+ in->variant.file_variant. -+ file_size = endpos; -+ } -+ } else if (in) { -+ /* This chunk has been invalidated by a -+ * resize, or a past file deletion -+ * so delete the chunk*/ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ } -+ } else { -+ /* chunk_id == 0, so it is an ObjectHeader. -+ * Thus, we read in the object header and make -+ * the object -+ */ -+ *found_chunks = 1; -+ -+ yaffs_set_chunk_bit(dev, blk, chunk_in_block); -+ bi->pages_in_use++; -+ -+ oh = NULL; -+ in = NULL; -+ -+ if (tags.extra_available) { -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, -+ tags.extra_obj_type); -+ if (!in) -+ alloc_failed = 1; -+ } -+ -+ if (!in || -+ (!in->valid && dev->param.disable_lazy_load) || -+ tags.extra_shadows || -+ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { -+ -+ /* If we don't have valid info then we -+ * need to read the chunk -+ * TODO In future we can probably defer -+ * reading the chunk and living with -+ * invalid data until needed. -+ */ -+ -+ result = yaffs_rd_chunk_tags_nand(dev, -+ chunk, -+ chunk_data, -+ NULL); -+ -+ oh = (struct yaffs_obj_hdr *)chunk_data; -+ -+ if (dev->param.inband_tags) { -+ /* Fix up the header if they got -+ * corrupted by inband tags */ -+ oh->shadows_obj = -+ oh->inband_shadowed_obj_id; -+ oh->is_shrink = -+ oh->inband_is_shrink; -+ } -+ -+ if (!in) { -+ in = yaffs_find_or_create_by_number(dev, -+ tags.obj_id, oh->type); -+ if (!in) -+ alloc_failed = 1; -+ } -+ } -+ -+ if (!in) { -+ /* TODO Hoosterman we have a problem! */ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", -+ tags.obj_id, chunk); -+ return YAFFS_FAIL; -+ } -+ -+ if (in->valid) { -+ /* We have already filled this one. -+ * We have a duplicate that will be -+ * discarded, but we first have to suck -+ * out resize info if it is a file. -+ */ -+ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && -+ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || -+ (tags.extra_available && -+ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) -+ )) { -+ loff_t this_size = (oh) ? -+ yaffs_oh_to_size(oh) : -+ tags.extra_file_size; -+ u32 parent_obj_id = (oh) ? -+ oh->parent_obj_id : -+ tags.extra_parent_id; -+ -+ is_shrink = (oh) ? -+ oh->is_shrink : -+ tags.extra_is_shrink; -+ -+ /* If it is deleted (unlinked -+ * at start also means deleted) -+ * we treat the file size as -+ * being zeroed at this point. -+ */ -+ if (parent_obj_id == YAFFS_OBJECTID_DELETED || -+ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { -+ this_size = 0; -+ is_shrink = 1; -+ } -+ -+ if (is_shrink && -+ in->variant.file_variant.shrink_size > -+ this_size) -+ in->variant.file_variant.shrink_size = -+ this_size; -+ -+ if (is_shrink) -+ bi->has_shrink_hdr = 1; -+ } -+ /* Use existing - destroy this one. */ -+ yaffs_chunk_del(dev, chunk, 1, __LINE__); -+ } -+ -+ if (!in->valid && in->variant_type != -+ (oh ? oh->type : tags.extra_obj_type)) { -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", -+ oh ? oh->type : tags.extra_obj_type, -+ in->variant_type, tags.obj_id, -+ chunk); -+ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); -+ } -+ -+ if (!in->valid && -+ (tags.obj_id == YAFFS_OBJECTID_ROOT || -+ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { -+ /* We only load some info, don't fiddle -+ * with directory structure */ -+ in->valid = 1; -+ -+ if (oh) { -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ in->lazy_loaded = 0; -+ } else { -+ in->lazy_loaded = 1; -+ } -+ in->hdr_chunk = chunk; -+ -+ } else if (!in->valid) { -+ /* we need to load this info */ -+ in->valid = 1; -+ in->hdr_chunk = chunk; -+ if (oh) { -+ in->variant_type = oh->type; -+ in->yst_mode = oh->yst_mode; -+ yaffs_load_attribs(in, oh); -+ -+ if (oh->shadows_obj > 0) -+ yaffs_handle_shadowed_obj(dev, -+ oh->shadows_obj, 1); -+ -+ yaffs_set_obj_name_from_oh(in, oh); -+ parent = yaffs_find_or_create_by_number(dev, -+ oh->parent_obj_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ file_size = yaffs_oh_to_size(oh); -+ is_shrink = oh->is_shrink; -+ equiv_id = oh->equiv_id; -+ } else { -+ in->variant_type = tags.extra_obj_type; -+ parent = yaffs_find_or_create_by_number(dev, -+ tags.extra_parent_id, -+ YAFFS_OBJECT_TYPE_DIRECTORY); -+ file_size = tags.extra_file_size; -+ is_shrink = tags.extra_is_shrink; -+ equiv_id = tags.extra_equiv_id; -+ in->lazy_loaded = 1; -+ } -+ in->dirty = 0; -+ -+ if (!parent) -+ alloc_failed = 1; -+ -+ /* directory stuff... -+ * hook up to parent -+ */ -+ -+ if (parent && -+ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { -+ /* Set up as a directory */ -+ parent->variant_type = -+ YAFFS_OBJECT_TYPE_DIRECTORY; -+ INIT_LIST_HEAD(&parent-> -+ variant.dir_variant.children); -+ } else if (!parent || -+ parent->variant_type != -+ YAFFS_OBJECT_TYPE_DIRECTORY) { -+ /* Hoosterman, another problem.... -+ * Trying to use a non-directory as a directory -+ */ -+ -+ yaffs_trace(YAFFS_TRACE_ERROR, -+ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." -+ ); -+ parent = dev->lost_n_found; -+ } -+ yaffs_add_obj_to_dir(parent, in); -+ -+ is_unlinked = (parent == dev->del_dir) || -+ (parent == dev->unlinked_dir); -+ -+ if (is_shrink) -+ /* Mark the block */ -+ bi->has_shrink_hdr = 1; -+ -+ /* Note re hardlinks. -+ * Since we might scan a hardlink before its equivalent -+ * object is scanned we put them all in a list. -+ * After scanning is complete, we should have all the -+ * objects, so we run through this list and fix up all -+ * the chains. -+ */ -+ -+ switch (in->variant_type) { -+ case YAFFS_OBJECT_TYPE_UNKNOWN: -+ /* Todo got a problem */ -+ break; -+ case YAFFS_OBJECT_TYPE_FILE: -+ file_var = &in->variant.file_variant; -+ if (file_var->scanned_size < file_size) { -+ /* This covers the case where the file -+ * size is greater than the data held. -+ * This will happen if the file is -+ * resized to be larger than its -+ * current data extents. -+ */ -+ file_var->file_size = file_size; -+ file_var->scanned_size = file_size; -+ } -+ -+ if (file_var->shrink_size > file_size) -+ file_var->shrink_size = file_size; -+ -+ break; -+ case YAFFS_OBJECT_TYPE_HARDLINK: -+ hl_var = &in->variant.hardlink_variant; -+ if (!is_unlinked) { -+ hl_var->equiv_id = equiv_id; -+ list_add(&in->hard_links, hard_list); -+ } -+ break; -+ case YAFFS_OBJECT_TYPE_DIRECTORY: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SPECIAL: -+ /* Do nothing */ -+ break; -+ case YAFFS_OBJECT_TYPE_SYMLINK: -+ sl_var = &in->variant.symlink_variant; -+ if (oh) { -+ sl_var->alias = -+ yaffs_clone_str(oh->alias); -+ if (!sl_var->alias) -+ alloc_failed = 1; -+ } -+ break; -+ } -+ } -+ } -+ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; -+} -+ -+int yaffs2_scan_backwards(struct yaffs_dev *dev) -+{ -+ int blk; -+ int block_iter; -+ int start_iter; -+ int end_iter; -+ int n_to_scan = 0; -+ enum yaffs_block_state state; -+ int c; -+ int deleted; -+ LIST_HEAD(hard_list); -+ struct yaffs_block_info *bi; -+ u32 seq_number; -+ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; -+ u8 *chunk_data; -+ int found_chunks; -+ int alloc_failed = 0; -+ struct yaffs_block_index *block_index = NULL; -+ int alt_block_index = 0; -+ int summary_available; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", -+ dev->internal_start_block, dev->internal_end_block); -+ -+ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; -+ -+ block_index = -+ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); -+ -+ if (!block_index) { -+ block_index = -+ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); -+ alt_block_index = 1; -+ } -+ -+ if (!block_index) { -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "yaffs2_scan_backwards() could not allocate block index!" -+ ); -+ return YAFFS_FAIL; -+ } -+ -+ dev->blocks_in_checkpt = 0; -+ -+ chunk_data = yaffs_get_temp_buffer(dev); -+ -+ /* Scan all the blocks to determine their state */ -+ bi = dev->block_info; -+ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; -+ blk++) { -+ yaffs_clear_chunk_bits(dev, blk); -+ bi->pages_in_use = 0; -+ bi->soft_del_pages = 0; -+ -+ yaffs_query_init_block_state(dev, blk, &state, &seq_number); -+ -+ bi->block_state = state; -+ bi->seq_number = seq_number; -+ -+ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) -+ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; -+ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) -+ bi->block_state = YAFFS_BLOCK_STATE_DEAD; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, -+ "Block scanning block %d state %d seq %d", -+ blk, bi->block_state, seq_number); -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { -+ dev->blocks_in_checkpt++; -+ -+ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { -+ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, -+ "block %d is bad", blk); -+ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); -+ dev->n_erased_blocks++; -+ dev->n_free_chunks += dev->param.chunks_per_block; -+ } else if (bi->block_state == -+ YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* Determine the highest sequence number */ -+ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && -+ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { -+ block_index[n_to_scan].seq = seq_number; -+ block_index[n_to_scan].block = blk; -+ n_to_scan++; -+ if (seq_number >= dev->seq_number) -+ dev->seq_number = seq_number; -+ } else { -+ /* TODO: Nasty sequence number! */ -+ yaffs_trace(YAFFS_TRACE_SCAN, -+ "Block scanning block %d has bad sequence number %d", -+ blk, seq_number); -+ } -+ } -+ bi++; -+ } -+ -+ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); -+ -+ cond_resched(); -+ -+ /* Sort the blocks by sequence number */ -+ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), -+ yaffs2_ybicmp, NULL); -+ -+ cond_resched(); -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); -+ -+ /* Now scan the blocks looking at the data. */ -+ start_iter = 0; -+ end_iter = n_to_scan - 1; -+ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); -+ -+ /* For each block.... backwards */ -+ for (block_iter = end_iter; -+ !alloc_failed && block_iter >= start_iter; -+ block_iter--) { -+ /* Cooperative multitasking! This loop can run for so -+ long that watchdog timers expire. */ -+ cond_resched(); -+ -+ /* get the block to scan in the correct order */ -+ blk = block_index[block_iter].block; -+ bi = yaffs_get_block_info(dev, blk); -+ deleted = 0; -+ -+ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); -+ -+ /* For each chunk in each block that needs scanning.... */ -+ found_chunks = 0; -+ if (summary_available) -+ c = dev->chunks_per_summary - 1; -+ else -+ c = dev->param.chunks_per_block - 1; -+ -+ for (/* c is already initialised */; -+ !alloc_failed && c >= 0 && -+ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || -+ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); -+ c--) { -+ /* Scan backwards... -+ * Read the tags and decide what to do -+ */ -+ if (yaffs2_scan_chunk(dev, bi, blk, c, -+ &found_chunks, chunk_data, -+ &hard_list, summary_available) == -+ YAFFS_FAIL) -+ alloc_failed = 1; -+ } -+ -+ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { -+ /* If we got this far while scanning, then the block -+ * is fully allocated. */ -+ bi->block_state = YAFFS_BLOCK_STATE_FULL; -+ } -+ -+ /* Now let's see if it was dirty */ -+ if (bi->pages_in_use == 0 && -+ !bi->has_shrink_hdr && -+ bi->block_state == YAFFS_BLOCK_STATE_FULL) { -+ yaffs_block_became_dirty(dev, blk); -+ } -+ } -+ -+ yaffs_skip_rest_of_block(dev); -+ -+ if (alt_block_index) -+ vfree(block_index); -+ else -+ kfree(block_index); -+ -+ /* Ok, we've done all the scanning. -+ * Fix up the hard link chains. -+ * We have scanned all the objects, now it's time to add these -+ * hardlinks. -+ */ -+ yaffs_link_fixup(dev, &hard_list); -+ -+ yaffs_release_temp_buffer(dev, chunk_data); -+ -+ if (alloc_failed) -+ return YAFFS_FAIL; -+ -+ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); -+ -+ return YAFFS_OK; -+} -diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.4.90/fs/yaffs2/yaffs_yaffs2.h ---- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YAFFS_YAFFS2_H__ -+#define __YAFFS_YAFFS2_H__ -+ -+#include "yaffs_guts.h" -+ -+void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); -+void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); -+void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, -+ struct yaffs_block_info *bi); -+void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, -+ struct yaffs_block_info *bi); -+int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); -+u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); -+int yaffs2_checkpt_required(struct yaffs_dev *dev); -+int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); -+ -+void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); -+int yaffs2_checkpt_save(struct yaffs_dev *dev); -+int yaffs2_checkpt_restore(struct yaffs_dev *dev); -+ -+int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); -+int yaffs2_scan_backwards(struct yaffs_dev *dev); -+ -+#endif -diff -Nur linux-3.4.90.orig/fs/yaffs2/yportenv.h linux-3.4.90/fs/yaffs2/yportenv.h ---- linux-3.4.90.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-3.4.90/fs/yaffs2/yportenv.h 2014-05-17 15:08:09.000000000 +0200 -@@ -0,0 +1,85 @@ -+/* -+ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -+ * -+ * Copyright (C) 2002-2011 Aleph One Ltd. -+ * for Toby Churchill Ltd and Brightstar Engineering -+ * -+ * Created by Charles Manning -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License version 2.1 as -+ * published by the Free Software Foundation. -+ * -+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. -+ */ -+ -+#ifndef __YPORTENV_H__ -+#define __YPORTENV_H__ -+ -+/* -+ * Define the MTD version in terms of Linux Kernel versions -+ * This allows yaffs to be used independantly of the kernel -+ * as well as with it. -+ */ -+ -+#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) -+ -+#ifdef YAFFS_OUT_OF_TREE -+#include "moduleconfig.h" -+#endif -+ -+#include -+#define MTD_VERSION_CODE LINUX_VERSION_CODE -+ -+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* These type wrappings are used to support Unicode names in WinCE. */ -+#define YCHAR char -+#define YUCHAR unsigned char -+#define _Y(x) x -+ -+#define YAFFS_LOSTNFOUND_NAME "lost+found" -+#define YAFFS_LOSTNFOUND_PREFIX "obj" -+ -+ -+#define YAFFS_ROOT_MODE 0755 -+#define YAFFS_LOSTNFOUND_MODE 0700 -+ -+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) -+#define Y_CURRENT_TIME CURRENT_TIME.tv_sec -+#define Y_TIME_CONVERT(x) (x).tv_sec -+#else -+#define Y_CURRENT_TIME CURRENT_TIME -+#define Y_TIME_CONVERT(x) (x) -+#endif -+ -+#define compile_time_assertion(assertion) \ -+ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) -+ -+ -+#define yaffs_printf(msk, fmt, ...) \ -+ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) -+ -+#define yaffs_trace(msk, fmt, ...) do { \ -+ if (yaffs_trace_mask & (msk)) \ -+ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ -+} while (0) -+ -+ -+#endif diff --git a/target/linux/patches/3.4.107/zlib-inflate.patch b/target/linux/patches/3.4.107/zlib-inflate.patch deleted file mode 100644 index 58e1f6d21..000000000 --- a/target/linux/patches/3.4.107/zlib-inflate.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-2.6.37.orig/lib/Kconfig linux-2.6.37/lib/Kconfig ---- linux-2.6.37.orig/lib/Kconfig 2011-01-05 01:50:19.000000000 +0100 -+++ linux-2.6.37/lib/Kconfig 2011-03-01 20:10:29.833370667 +0100 -@@ -95,7 +95,7 @@ - # compression support is select'ed if needed - # - config ZLIB_INFLATE -- tristate -+ boolean - - config ZLIB_DEFLATE - tristate diff --git a/target/linux/patches/3.4.108/aufs2.patch b/target/linux/patches/3.4.108/aufs2.patch new file mode 100644 index 000000000..d40c9a3fe --- /dev/null +++ b/target/linux/patches/3.4.108/aufs2.patch @@ -0,0 +1,238 @@ +diff -Nur linux-3.1.4.orig/include/linux/Kbuild linux-3.1.4/include/linux/Kbuild +--- linux-3.1.4.orig/include/linux/Kbuild 2011-11-28 23:48:14.000000000 +0100 ++++ linux-3.1.4/include/linux/Kbuild 2011-12-01 12:44:17.000000000 +0100 +@@ -65,6 +65,7 @@ + header-y += atmsap.h + header-y += atmsvc.h + header-y += audit.h ++header-y += aufs_type.h + header-y += auto_fs.h + header-y += auto_fs4.h + header-y += auxvec.h +diff -Nur linux-3.1.4.orig/include/linux/aufs_type.h linux-3.1.4/include/linux/aufs_type.h +--- linux-3.1.4.orig/include/linux/aufs_type.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.1.4/include/linux/aufs_type.h 2011-12-01 12:44:17.000000000 +0100 +@@ -0,0 +1,197 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#ifndef __AUFS_TYPE_H__ ++#define __AUFS_TYPE_H__ ++ ++#include ++#include ++#include ++#include ++ ++#define AUFS_VERSION "2.1-standalone.tree-36-20110110" ++ ++/* todo? move this to linux-2.6.19/include/magic.h */ ++#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_BRANCH_MAX_127 ++typedef __s8 aufs_bindex_t; ++#define AUFS_BRANCH_MAX 127 ++#else ++typedef __s16 aufs_bindex_t; ++#ifdef CONFIG_AUFS_BRANCH_MAX_511 ++#define AUFS_BRANCH_MAX 511 ++#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) ++#define AUFS_BRANCH_MAX 1023 ++#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) ++#define AUFS_BRANCH_MAX 32767 ++#endif ++#endif ++ ++#ifdef __KERNEL__ ++#ifndef AUFS_BRANCH_MAX ++#error unknown CONFIG_AUFS_BRANCH_MAX value ++#endif ++#endif /* __KERNEL__ */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AUFS_NAME "aufs" ++#define AUFS_FSTYPE AUFS_NAME ++ ++#define AUFS_ROOT_INO 2 ++#define AUFS_FIRST_INO 11 ++ ++#define AUFS_WH_PFX ".wh." ++#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) ++#define AUFS_WH_TMP_LEN 4 ++/* a limit for rmdir/rename a dir */ ++#define AUFS_MAX_NAMELEN (NAME_MAX \ ++ - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ ++ - 1 /* dot */\ ++ - AUFS_WH_TMP_LEN) /* hex */ ++#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" ++#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME ++#define AUFS_XINO_TRUNC_INIT 64 /* blocks */ ++#define AUFS_XINO_TRUNC_STEP 4 /* blocks */ ++#define AUFS_DIRWH_DEF 3 ++#define AUFS_RDCACHE_DEF 10 /* seconds */ ++#define AUFS_RDCACHE_MAX 3600 /* seconds */ ++#define AUFS_RDBLK_DEF 512 /* bytes */ ++#define AUFS_RDHASH_DEF 32 ++#define AUFS_WKQ_NAME AUFS_NAME "d" ++#define AUFS_WKQ_PRE_NAME AUFS_WKQ_NAME "_pre" ++#define AUFS_MFS_DEF_SEC 30 /* seconds */ ++#define AUFS_MFS_MAX_SEC 3600 /* seconds */ ++#define AUFS_PLINK_WARN 100 /* number of plinks */ ++ ++/* pseudo-link maintenace under /proc */ ++#define AUFS_PLINK_MAINT_NAME "plink_maint" ++#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME ++#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME ++ ++#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ ++#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME ++ ++#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME ++#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" ++#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" ++ ++/* doubly whiteouted */ ++#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME ++#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME ++#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME ++ ++/* branch permission */ ++#define AUFS_BRPERM_RW "rw" ++#define AUFS_BRPERM_RO "ro" ++#define AUFS_BRPERM_RR "rr" ++#define AUFS_BRPERM_WH "wh" ++#define AUFS_BRPERM_NLWH "nolwh" ++#define AUFS_BRPERM_ROWH AUFS_BRPERM_RO "+" AUFS_BRPERM_WH ++#define AUFS_BRPERM_RRWH AUFS_BRPERM_RR "+" AUFS_BRPERM_WH ++#define AUFS_BRPERM_RWNLWH AUFS_BRPERM_RW "+" AUFS_BRPERM_NLWH ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ioctl */ ++enum { ++ /* readdir in userspace */ ++ AuCtl_RDU, ++ AuCtl_RDU_INO, ++ ++ /* pathconf wrapper */ ++ AuCtl_WBR_FD ++}; ++ ++/* borrowed from linux/include/linux/kernel.h */ ++#ifndef ALIGN ++#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) ++#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) ++#endif ++ ++/* borrowed from linux/include/linux/compiler-gcc3.h */ ++#ifndef __aligned ++#define __aligned(x) __attribute__((aligned(x))) ++#define __packed __attribute__((packed)) ++#endif ++ ++struct au_rdu_cookie { ++ __u64 h_pos; ++ __s16 bindex; ++ __u8 flags; ++ __u8 pad; ++ __u32 generation; ++} __aligned(8); ++ ++struct au_rdu_ent { ++ __u64 ino; ++ __s16 bindex; ++ __u8 type; ++ __u8 nlen; ++ __u8 wh; ++ char name[0]; ++} __aligned(8); ++ ++static inline int au_rdu_len(int nlen) ++{ ++ /* include the terminating NULL */ ++ return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, ++ sizeof(__u64)); ++} ++ ++union au_rdu_ent_ul { ++ struct au_rdu_ent __user *e; ++ __u64 ul; ++}; ++ ++enum { ++ AufsCtlRduV_SZ, ++ AufsCtlRduV_End ++}; ++ ++struct aufs_rdu { ++ /* input */ ++ union { ++ __u64 sz; /* AuCtl_RDU */ ++ __u64 nent; /* AuCtl_RDU_INO */ ++ }; ++ union au_rdu_ent_ul ent; ++ __u16 verify[AufsCtlRduV_End]; ++ ++ /* input/output */ ++ __u32 blk; ++ ++ /* output */ ++ union au_rdu_ent_ul tail; ++ /* number of entries which were added in a single call */ ++ __u64 rent; ++ __u8 full; ++ __u8 shwh; ++ ++ struct au_rdu_cookie cookie; ++} __aligned(8); ++ ++#define AuCtlType 'A' ++#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) ++#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) ++#define AUFS_CTL_WBR_FD _IO(AuCtlType, AuCtl_WBR_FD) ++ ++#endif /* __AUFS_TYPE_H__ */ +diff -Nur linux-3.1.4.orig/include/linux/namei.h linux-3.1.4/include/linux/namei.h +--- linux-3.1.4.orig/include/linux/namei.h 2011-11-28 23:48:14.000000000 +0100 ++++ linux-3.1.4/include/linux/namei.h 2011-12-01 12:44:17.000000000 +0100 +@@ -85,6 +85,9 @@ + extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, + int (*open)(struct inode *, struct file *)); + ++extern struct dentry *lookup_hash(struct nameidata *nd); ++extern int __lookup_one_len(const char *name, struct qstr *this, ++ struct dentry *base, int len); + extern struct dentry *lookup_one_len(const char *, struct dentry *, int); + + extern int follow_down_one(struct path *); +diff -Nur linux-3.1.4.orig/include/linux/splice.h linux-3.1.4/include/linux/splice.h +--- linux-3.1.4.orig/include/linux/splice.h 2011-11-28 23:48:14.000000000 +0100 ++++ linux-3.1.4/include/linux/splice.h 2011-12-01 12:45:51.000000000 +0100 +@@ -91,4 +91,9 @@ + extern void spd_release_page(struct splice_pipe_desc *, unsigned int); + + extern const struct pipe_buf_operations page_cache_pipe_buf_ops; ++extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags); ++extern long do_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags); + #endif diff --git a/target/linux/patches/3.4.108/bsd-compatibility.patch b/target/linux/patches/3.4.108/bsd-compatibility.patch new file mode 100644 index 000000000..9e91a62de --- /dev/null +++ b/target/linux/patches/3.4.108/bsd-compatibility.patch @@ -0,0 +1,2512 @@ +diff -Nur linux-2.6.36.orig/scripts/Makefile.lib linux-2.6.36/scripts/Makefile.lib +--- linux-2.6.36.orig/scripts/Makefile.lib 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/scripts/Makefile.lib 2010-11-28 18:34:22.000000000 +0100 +@@ -216,7 +216,12 @@ + size_append = printf $(shell \ + dec_size=0; \ + for F in $1; do \ +- fsize=$$(stat -c "%s" $$F); \ ++ if stat -qs .>/dev/null 2>&1; then \ ++ statcmd='stat -f %z'; \ ++ else \ ++ statcmd='stat -c %s'; \ ++ fi; \ ++ fsize=$$($$statcmd $$F); \ + dec_size=$$(expr $$dec_size + $$fsize); \ + done; \ + printf "%08x\n" $$dec_size | \ +diff -Nur linux-2.6.36.orig/scripts/mod/mk_elfconfig.c linux-2.6.36/scripts/mod/mk_elfconfig.c +--- linux-2.6.36.orig/scripts/mod/mk_elfconfig.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/scripts/mod/mk_elfconfig.c 2010-11-28 18:33:24.000000000 +0100 +@@ -1,7 +1,18 @@ + #include + #include + #include +-#include ++ ++#define EI_NIDENT (16) ++#define ELFMAG "\177ELF" ++ ++#define SELFMAG 4 ++#define EI_CLASS 4 ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ + + int + main(int argc, char **argv) +diff -Nur linux-2.6.36.orig/scripts/mod/modpost.h linux-2.6.36/scripts/mod/modpost.h +--- linux-2.6.36.orig/scripts/mod/modpost.h 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/scripts/mod/modpost.h 2010-11-28 18:33:24.000000000 +0100 +@@ -7,7 +7,2453 @@ + #include + #include + #include +-#include ++ ++ ++/* This file defines standard ELF types, structures, and macros. ++ Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++#ifndef _ELF_H ++#define _ELF_H 1 ++ ++__BEGIN_DECLS ++ ++/* Standard ELF types. */ ++ ++#include ++ ++/* Type for a 16-bit quantity. */ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++/* Types for signed and unsigned 32-bit quantities. */ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++/* Types for signed and unsigned 64-bit quantities. */ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++/* Type of addresses. */ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++/* Type of file offsets. */ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++/* Type for section indices, which are 16-bit quantities. */ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++/* Type for version symbol information. */ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++ ++/* The ELF file header. This appears at the start of every ELF file. */ ++ ++#define EI_NIDENT (16) ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf32_Half e_type; /* Object file type */ ++ Elf32_Half e_machine; /* Architecture */ ++ Elf32_Word e_version; /* Object file version */ ++ Elf32_Addr e_entry; /* Entry point virtual address */ ++ Elf32_Off e_phoff; /* Program header table file offset */ ++ Elf32_Off e_shoff; /* Section header table file offset */ ++ Elf32_Word e_flags; /* Processor-specific flags */ ++ Elf32_Half e_ehsize; /* ELF header size in bytes */ ++ Elf32_Half e_phentsize; /* Program header table entry size */ ++ Elf32_Half e_phnum; /* Program header table entry count */ ++ Elf32_Half e_shentsize; /* Section header table entry size */ ++ Elf32_Half e_shnum; /* Section header table entry count */ ++ Elf32_Half e_shstrndx; /* Section header string table index */ ++} Elf32_Ehdr; ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf64_Half e_type; /* Object file type */ ++ Elf64_Half e_machine; /* Architecture */ ++ Elf64_Word e_version; /* Object file version */ ++ Elf64_Addr e_entry; /* Entry point virtual address */ ++ Elf64_Off e_phoff; /* Program header table file offset */ ++ Elf64_Off e_shoff; /* Section header table file offset */ ++ Elf64_Word e_flags; /* Processor-specific flags */ ++ Elf64_Half e_ehsize; /* ELF header size in bytes */ ++ Elf64_Half e_phentsize; /* Program header table entry size */ ++ Elf64_Half e_phnum; /* Program header table entry count */ ++ Elf64_Half e_shentsize; /* Section header table entry size */ ++ Elf64_Half e_shnum; /* Section header table entry count */ ++ Elf64_Half e_shstrndx; /* Section header string table index */ ++} Elf64_Ehdr; ++ ++/* Fields in the e_ident array. The EI_* macros are indices into the ++ array. The macros under each EI_* macro are the values the byte ++ may have. */ ++ ++#define EI_MAG0 0 /* File identification byte 0 index */ ++#define ELFMAG0 0x7f /* Magic number byte 0 */ ++ ++#define EI_MAG1 1 /* File identification byte 1 index */ ++#define ELFMAG1 'E' /* Magic number byte 1 */ ++ ++#define EI_MAG2 2 /* File identification byte 2 index */ ++#define ELFMAG2 'L' /* Magic number byte 2 */ ++ ++#define EI_MAG3 3 /* File identification byte 3 index */ ++#define ELFMAG3 'F' /* Magic number byte 3 */ ++ ++/* Conglomeration of the identification bytes, for easy testing as a word. */ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++ ++#define EI_CLASS 4 /* File class byte index */ ++#define ELFCLASSNONE 0 /* Invalid class */ ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATANONE 0 /* Invalid data encoding */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 /* File version byte index */ ++ /* Value must be EV_CURRENT */ ++ ++#define EI_OSABI 7 /* OS ABI identification */ ++#define ELFOSABI_NONE 0 /* UNIX System V ABI */ ++#define ELFOSABI_SYSV 0 /* Alias. */ ++#define ELFOSABI_HPUX 1 /* HP-UX */ ++#define ELFOSABI_NETBSD 2 /* NetBSD. */ ++#define ELFOSABI_LINUX 3 /* Linux. */ ++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ ++#define ELFOSABI_AIX 7 /* IBM AIX. */ ++#define ELFOSABI_IRIX 8 /* SGI Irix. */ ++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ ++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ ++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ ++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ ++#define ELFOSABI_ARM 97 /* ARM */ ++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ ++ ++#define EI_ABIVERSION 8 /* ABI version */ ++ ++#define EI_PAD 9 /* Byte index of padding bytes */ ++ ++/* Legal values for e_type (object file type). */ ++ ++#define ET_NONE 0 /* No file type */ ++#define ET_REL 1 /* Relocatable file */ ++#define ET_EXEC 2 /* Executable file */ ++#define ET_DYN 3 /* Shared object file */ ++#define ET_CORE 4 /* Core file */ ++#define ET_NUM 5 /* Number of defined types */ ++#define ET_LOOS 0xfe00 /* OS-specific range start */ ++#define ET_HIOS 0xfeff /* OS-specific range end */ ++#define ET_LOPROC 0xff00 /* Processor-specific range start */ ++#define ET_HIPROC 0xffff /* Processor-specific range end */ ++ ++/* Legal values for e_machine (architecture). */ ++ ++#define EM_NONE 0 /* No machine */ ++#define EM_M32 1 /* AT&T WE 32100 */ ++#define EM_SPARC 2 /* SUN SPARC */ ++#define EM_386 3 /* Intel 80386 */ ++#define EM_68K 4 /* Motorola m68k family */ ++#define EM_88K 5 /* Motorola m88k family */ ++#define EM_860 7 /* Intel 80860 */ ++#define EM_MIPS 8 /* MIPS R3000 big-endian */ ++#define EM_S370 9 /* IBM System/370 */ ++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ ++ ++#define EM_PARISC 15 /* HPPA */ ++#define EM_VPP500 17 /* Fujitsu VPP500 */ ++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ ++#define EM_960 19 /* Intel 80960 */ ++#define EM_PPC 20 /* PowerPC */ ++#define EM_PPC64 21 /* PowerPC 64-bit */ ++#define EM_S390 22 /* IBM S390 */ ++ ++#define EM_V800 36 /* NEC V800 series */ ++#define EM_FR20 37 /* Fujitsu FR20 */ ++#define EM_RH32 38 /* TRW RH-32 */ ++#define EM_RCE 39 /* Motorola RCE */ ++#define EM_ARM 40 /* ARM */ ++#define EM_FAKE_ALPHA 41 /* Digital Alpha */ ++#define EM_SH 42 /* Hitachi SH */ ++#define EM_SPARCV9 43 /* SPARC v9 64-bit */ ++#define EM_TRICORE 44 /* Siemens Tricore */ ++#define EM_ARC 45 /* Argonaut RISC Core */ ++#define EM_H8_300 46 /* Hitachi H8/300 */ ++#define EM_H8_300H 47 /* Hitachi H8/300H */ ++#define EM_H8S 48 /* Hitachi H8S */ ++#define EM_H8_500 49 /* Hitachi H8/500 */ ++#define EM_IA_64 50 /* Intel Merced */ ++#define EM_MIPS_X 51 /* Stanford MIPS-X */ ++#define EM_COLDFIRE 52 /* Motorola Coldfire */ ++#define EM_68HC12 53 /* Motorola M68HC12 */ ++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ ++#define EM_PCP 55 /* Siemens PCP */ ++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ ++#define EM_NDR1 57 /* Denso NDR1 microprocessor */ ++#define EM_STARCORE 58 /* Motorola Start*Core processor */ ++#define EM_ME16 59 /* Toyota ME16 processor */ ++#define EM_ST100 60 /* STMicroelectronic ST100 processor */ ++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ ++#define EM_X86_64 62 /* AMD x86-64 architecture */ ++#define EM_PDSP 63 /* Sony DSP Processor */ ++ ++#define EM_FX66 66 /* Siemens FX66 microcontroller */ ++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ ++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ ++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ ++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ ++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ ++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ ++#define EM_SVX 73 /* Silicon Graphics SVx */ ++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ ++#define EM_VAX 75 /* Digital VAX */ ++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ ++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ ++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ ++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ ++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ ++#define EM_HUANY 81 /* Harvard University machine-independent object files */ ++#define EM_PRISM 82 /* SiTera Prism */ ++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ ++#define EM_FR30 84 /* Fujitsu FR30 */ ++#define EM_D10V 85 /* Mitsubishi D10V */ ++#define EM_D30V 86 /* Mitsubishi D30V */ ++#define EM_V850 87 /* NEC v850 */ ++#define EM_M32R 88 /* Mitsubishi M32R */ ++#define EM_MN10300 89 /* Matsushita MN10300 */ ++#define EM_MN10200 90 /* Matsushita MN10200 */ ++#define EM_PJ 91 /* picoJava */ ++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ ++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ ++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ ++#define EM_NUM 95 ++ ++/* If it is necessary to assign new unofficial EM_* values, please ++ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the ++ chances of collision with official or non-GNU unofficial values. */ ++ ++#define EM_ALPHA 0x9026 ++ ++/* Legal values for e_version (version). */ ++ ++#define EV_NONE 0 /* Invalid ELF version */ ++#define EV_CURRENT 1 /* Current version */ ++#define EV_NUM 2 ++ ++/* Section header. */ ++ ++typedef struct ++{ ++ Elf32_Word sh_name; /* Section name (string tbl index) */ ++ Elf32_Word sh_type; /* Section type */ ++ Elf32_Word sh_flags; /* Section flags */ ++ Elf32_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf32_Off sh_offset; /* Section file offset */ ++ Elf32_Word sh_size; /* Section size in bytes */ ++ Elf32_Word sh_link; /* Link to another section */ ++ Elf32_Word sh_info; /* Additional section information */ ++ Elf32_Word sh_addralign; /* Section alignment */ ++ Elf32_Word sh_entsize; /* Entry size if section holds table */ ++} Elf32_Shdr; ++ ++typedef struct ++{ ++ Elf64_Word sh_name; /* Section name (string tbl index) */ ++ Elf64_Word sh_type; /* Section type */ ++ Elf64_Xword sh_flags; /* Section flags */ ++ Elf64_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf64_Off sh_offset; /* Section file offset */ ++ Elf64_Xword sh_size; /* Section size in bytes */ ++ Elf64_Word sh_link; /* Link to another section */ ++ Elf64_Word sh_info; /* Additional section information */ ++ Elf64_Xword sh_addralign; /* Section alignment */ ++ Elf64_Xword sh_entsize; /* Entry size if section holds table */ ++} Elf64_Shdr; ++ ++/* Special section indices. */ ++ ++#define SHN_UNDEF 0 /* Undefined section */ ++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ ++#define SHN_LOPROC 0xff00 /* Start of processor-specific */ ++#define SHN_HIPROC 0xff1f /* End of processor-specific */ ++#define SHN_LOOS 0xff20 /* Start of OS-specific */ ++#define SHN_HIOS 0xff3f /* End of OS-specific */ ++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ ++#define SHN_COMMON 0xfff2 /* Associated symbol is common */ ++#define SHN_XINDEX 0xffff /* Index is in extra table. */ ++#define SHN_HIRESERVE 0xffff /* End of reserved indices */ ++ ++/* Legal values for sh_type (section type). */ ++ ++#define SHT_NULL 0 /* Section header table entry unused */ ++#define SHT_PROGBITS 1 /* Program data */ ++#define SHT_SYMTAB 2 /* Symbol table */ ++#define SHT_STRTAB 3 /* String table */ ++#define SHT_RELA 4 /* Relocation entries with addends */ ++#define SHT_HASH 5 /* Symbol hash table */ ++#define SHT_DYNAMIC 6 /* Dynamic linking information */ ++#define SHT_NOTE 7 /* Notes */ ++#define SHT_NOBITS 8 /* Program space with no data (bss) */ ++#define SHT_REL 9 /* Relocation entries, no addends */ ++#define SHT_SHLIB 10 /* Reserved */ ++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ ++#define SHT_INIT_ARRAY 14 /* Array of constructors */ ++#define SHT_FINI_ARRAY 15 /* Array of destructors */ ++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ ++#define SHT_GROUP 17 /* Section group */ ++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ ++#define SHT_NUM 19 /* Number of defined types. */ ++#define SHT_LOOS 0x60000000 /* Start OS-specific */ ++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ ++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ ++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ ++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ ++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ ++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ ++#define SHT_HIOS 0x6fffffff /* End OS-specific type */ ++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define SHT_LOUSER 0x80000000 /* Start of application-specific */ ++#define SHT_HIUSER 0x8fffffff /* End of application-specific */ ++ ++/* Legal values for sh_flags (section flags). */ ++ ++#define SHF_WRITE (1 << 0) /* Writable */ ++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ ++#define SHF_EXECINSTR (1 << 2) /* Executable */ ++#define SHF_MERGE (1 << 4) /* Might be merged */ ++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ ++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ ++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ ++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling ++ required */ ++#define SHF_GROUP (1 << 9) /* Section is member of a group. */ ++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ ++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ ++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Section group handling. */ ++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ ++ ++/* Symbol table entry. */ ++ ++typedef struct ++{ ++ Elf32_Word st_name; /* Symbol name (string tbl index) */ ++ Elf32_Addr st_value; /* Symbol value */ ++ Elf32_Word st_size; /* Symbol size */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf32_Section st_shndx; /* Section index */ ++} Elf32_Sym; ++ ++typedef struct ++{ ++ Elf64_Word st_name; /* Symbol name (string tbl index) */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf64_Section st_shndx; /* Section index */ ++ Elf64_Addr st_value; /* Symbol value */ ++ Elf64_Xword st_size; /* Symbol size */ ++} Elf64_Sym; ++ ++/* The syminfo section if available contains additional information about ++ every dynamic symbol. */ ++ ++typedef struct ++{ ++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf32_Half si_flags; /* Per symbol flags */ ++} Elf32_Syminfo; ++ ++typedef struct ++{ ++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf64_Half si_flags; /* Per symbol flags */ ++} Elf64_Syminfo; ++ ++/* Possible values for si_boundto. */ ++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ ++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ ++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ ++ ++/* Possible bitmasks for si_flags. */ ++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ ++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ ++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ ++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy ++ loaded */ ++/* Syminfo version values. */ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++ ++/* How to extract and insert information held in the st_info field. */ ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++/* Legal values for ST_BIND subfield of st_info (symbol binding). */ ++ ++#define STB_LOCAL 0 /* Local symbol */ ++#define STB_GLOBAL 1 /* Global symbol */ ++#define STB_WEAK 2 /* Weak symbol */ ++#define STB_NUM 3 /* Number of defined types. */ ++#define STB_LOOS 10 /* Start of OS-specific */ ++#define STB_HIOS 12 /* End of OS-specific */ ++#define STB_LOPROC 13 /* Start of processor-specific */ ++#define STB_HIPROC 15 /* End of processor-specific */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_NOTYPE 0 /* Symbol type is unspecified */ ++#define STT_OBJECT 1 /* Symbol is a data object */ ++#define STT_FUNC 2 /* Symbol is a code object */ ++#define STT_SECTION 3 /* Symbol associated with a section */ ++#define STT_FILE 4 /* Symbol's name is file name */ ++#define STT_COMMON 5 /* Symbol is a common data object */ ++#define STT_TLS 6 /* Symbol is thread-local data object*/ ++#define STT_NUM 7 /* Number of defined types. */ ++#define STT_LOOS 10 /* Start of OS-specific */ ++#define STT_HIOS 12 /* End of OS-specific */ ++#define STT_LOPROC 13 /* Start of processor-specific */ ++#define STT_HIPROC 15 /* End of processor-specific */ ++ ++ ++/* Symbol table indices are found in the hash buckets and chain table ++ of a symbol hash table section. This special index value indicates ++ the end of a chain, meaning no further symbols are found in that bucket. */ ++ ++#define STN_UNDEF 0 /* End of a chain. */ ++ ++ ++/* How to extract and insert information held in the st_other field. */ ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++ ++/* For ELF64 the definitions are the same. */ ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++/* Symbol visibility specification encoded in the st_other field. */ ++#define STV_DEFAULT 0 /* Default symbol visibility rules */ ++#define STV_INTERNAL 1 /* Processor specific hidden class */ ++#define STV_HIDDEN 2 /* Sym unavailable in other modules */ ++#define STV_PROTECTED 3 /* Not preemptible, not exported */ ++ ++ ++/* Relocation table entry without addend (in section of type SHT_REL). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++} Elf32_Rel; ++ ++/* I have seen two different definitions of the Elf64_Rel and ++ Elf64_Rela structures, so we'll leave them out until Novell (or ++ whoever) gets their act together. */ ++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++} Elf64_Rel; ++ ++/* Relocation table entry with addend (in section of type SHT_RELA). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++ Elf32_Sword r_addend; /* Addend */ ++} Elf32_Rela; ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++ Elf64_Sxword r_addend; /* Addend */ ++} Elf64_Rela; ++ ++/* How to extract and insert information held in the r_info field. */ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++/* Program segment header. */ ++ ++typedef struct ++{ ++ Elf32_Word p_type; /* Segment type */ ++ Elf32_Off p_offset; /* Segment file offset */ ++ Elf32_Addr p_vaddr; /* Segment virtual address */ ++ Elf32_Addr p_paddr; /* Segment physical address */ ++ Elf32_Word p_filesz; /* Segment size in file */ ++ Elf32_Word p_memsz; /* Segment size in memory */ ++ Elf32_Word p_flags; /* Segment flags */ ++ Elf32_Word p_align; /* Segment alignment */ ++} Elf32_Phdr; ++ ++typedef struct ++{ ++ Elf64_Word p_type; /* Segment type */ ++ Elf64_Word p_flags; /* Segment flags */ ++ Elf64_Off p_offset; /* Segment file offset */ ++ Elf64_Addr p_vaddr; /* Segment virtual address */ ++ Elf64_Addr p_paddr; /* Segment physical address */ ++ Elf64_Xword p_filesz; /* Segment size in file */ ++ Elf64_Xword p_memsz; /* Segment size in memory */ ++ Elf64_Xword p_align; /* Segment alignment */ ++} Elf64_Phdr; ++ ++/* Legal values for p_type (segment type). */ ++ ++#define PT_NULL 0 /* Program header table entry unused */ ++#define PT_LOAD 1 /* Loadable program segment */ ++#define PT_DYNAMIC 2 /* Dynamic linking information */ ++#define PT_INTERP 3 /* Program interpreter */ ++#define PT_NOTE 4 /* Auxiliary information */ ++#define PT_SHLIB 5 /* Reserved */ ++#define PT_PHDR 6 /* Entry for header table itself */ ++#define PT_TLS 7 /* Thread-local storage segment */ ++#define PT_NUM 8 /* Number of defined types */ ++#define PT_LOOS 0x60000000 /* Start of OS-specific */ ++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ ++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ ++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff /* End of OS-specific */ ++#define PT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define PT_HIPROC 0x7fffffff /* End of processor-specific */ ++ ++/* Legal values for p_flags (segment flags). */ ++ ++#define PF_X (1 << 0) /* Segment is executable */ ++#define PF_W (1 << 1) /* Segment is writable */ ++#define PF_R (1 << 2) /* Segment is readable */ ++#define PF_MASKOS 0x0ff00000 /* OS-specific */ ++#define PF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Legal values for note segment descriptor types for core files. */ ++ ++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ ++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ ++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ ++#define NT_PRXREG 4 /* Contains copy of prxregset struct */ ++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ ++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ ++#define NT_AUXV 6 /* Contains copy of auxv array */ ++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ ++#define NT_ASRS 8 /* Contains copy of asrset struct */ ++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ ++#define NT_PSINFO 13 /* Contains copy of psinfo struct */ ++#define NT_PRCRED 14 /* Contains copy of prcred struct */ ++#define NT_UTSNAME 15 /* Contains copy of utsname struct */ ++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ ++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ ++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ ++ ++/* Legal values for the note segment descriptor types for object files. */ ++ ++#define NT_VERSION 1 /* Contains a version string. */ ++ ++ ++/* Dynamic section entry. */ ++ ++typedef struct ++{ ++ Elf32_Sword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf32_Word d_val; /* Integer value */ ++ Elf32_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct ++{ ++ Elf64_Sxword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf64_Xword d_val; /* Integer value */ ++ Elf64_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf64_Dyn; ++ ++/* Legal values for d_tag (dynamic entry type). */ ++ ++#define DT_NULL 0 /* Marks end of dynamic section */ ++#define DT_NEEDED 1 /* Name of needed library */ ++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ ++#define DT_PLTGOT 3 /* Processor defined value */ ++#define DT_HASH 4 /* Address of symbol hash table */ ++#define DT_STRTAB 5 /* Address of string table */ ++#define DT_SYMTAB 6 /* Address of symbol table */ ++#define DT_RELA 7 /* Address of Rela relocs */ ++#define DT_RELASZ 8 /* Total size of Rela relocs */ ++#define DT_RELAENT 9 /* Size of one Rela reloc */ ++#define DT_STRSZ 10 /* Size of string table */ ++#define DT_SYMENT 11 /* Size of one symbol table entry */ ++#define DT_INIT 12 /* Address of init function */ ++#define DT_FINI 13 /* Address of termination function */ ++#define DT_SONAME 14 /* Name of shared object */ ++#define DT_RPATH 15 /* Library search path (deprecated) */ ++#define DT_SYMBOLIC 16 /* Start symbol search here */ ++#define DT_REL 17 /* Address of Rel relocs */ ++#define DT_RELSZ 18 /* Total size of Rel relocs */ ++#define DT_RELENT 19 /* Size of one Rel reloc */ ++#define DT_PLTREL 20 /* Type of reloc in PLT */ ++#define DT_DEBUG 21 /* For debugging; unspecified */ ++#define DT_TEXTREL 22 /* Reloc might modify .text */ ++#define DT_JMPREL 23 /* Address of PLT relocs */ ++#define DT_BIND_NOW 24 /* Process relocations of object */ ++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ ++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ ++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ ++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ ++#define DT_RUNPATH 29 /* Library search path */ ++#define DT_FLAGS 30 /* Flags for the object being loaded */ ++#define DT_ENCODING 32 /* Start of encoded range */ ++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ ++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ ++#define DT_NUM 34 /* Number used */ ++#define DT_LOOS 0x6000000d /* Start of OS-specific */ ++#define DT_HIOS 0x6ffff000 /* End of OS-specific */ ++#define DT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define DT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ ++ ++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the ++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's ++ approach. */ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ ++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting ++ the following DT_* entry. */ ++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ ++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ ++#define DT_VALNUM 12 ++ ++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the ++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. ++ ++ If any adjustment is made to the ELF object after it has been ++ built these entries will need to be adjusted. */ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ ++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ ++#define DT_CONFIG 0x6ffffefa /* Configuration information. */ ++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ ++#define DT_AUDIT 0x6ffffefc /* Object auditing. */ ++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ ++#define DT_MOVETAB 0x6ffffefe /* Move table. */ ++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ ++#define DT_ADDRNUM 10 ++ ++/* The versioning entry types. The next are defined as part of the ++ GNU extension. */ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++/* These were chosen by Sun. */ ++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ ++#define DT_VERDEF 0x6ffffffc /* Address of version definition ++ table */ ++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ ++#define DT_VERNEED 0x6ffffffe /* Address of table with needed ++ versions */ ++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ ++#define DT_VERSIONTAGNUM 16 ++ ++/* Sun added these machine-independent extensions in the "processor-specific" ++ range. Be compatible. */ ++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ ++#define DT_FILTER 0x7fffffff /* Shared object to get values from */ ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++/* Values of `d_un.d_val' in the DT_FLAGS entry. */ ++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ ++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ ++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ ++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ ++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ ++ ++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 ++ entry in the dynamic section. */ ++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ ++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ ++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ ++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ ++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ ++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ ++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ ++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ ++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ ++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ ++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ ++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ ++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ ++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ ++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ ++ ++/* Flags for the feature selection in DT_FEATURE_1. */ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ ++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ ++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not ++ generally available. */ ++ ++/* Version definition sections. */ ++ ++typedef struct ++{ ++ Elf32_Half vd_version; /* Version revision */ ++ Elf32_Half vd_flags; /* Version information */ ++ Elf32_Half vd_ndx; /* Version Index */ ++ Elf32_Half vd_cnt; /* Number of associated aux entries */ ++ Elf32_Word vd_hash; /* Version name hash value */ ++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf32_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf32_Verdef; ++ ++typedef struct ++{ ++ Elf64_Half vd_version; /* Version revision */ ++ Elf64_Half vd_flags; /* Version information */ ++ Elf64_Half vd_ndx; /* Version Index */ ++ Elf64_Half vd_cnt; /* Number of associated aux entries */ ++ Elf64_Word vd_hash; /* Version name hash value */ ++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf64_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf64_Verdef; ++ ++ ++/* Legal values for vd_version (version revision). */ ++#define VER_DEF_NONE 0 /* No version */ ++#define VER_DEF_CURRENT 1 /* Current version */ ++#define VER_DEF_NUM 2 /* Given version number */ ++ ++/* Legal values for vd_flags (version information flags). */ ++#define VER_FLG_BASE 0x1 /* Version definition of file itself */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++/* Versym symbol index values. */ ++#define VER_NDX_LOCAL 0 /* Symbol is local. */ ++#define VER_NDX_GLOBAL 1 /* Symbol is global. */ ++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ ++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ ++ ++/* Auxialiary version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vda_name; /* Version or dependency names */ ++ Elf32_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf32_Verdaux; ++ ++typedef struct ++{ ++ Elf64_Word vda_name; /* Version or dependency names */ ++ Elf64_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf64_Verdaux; ++ ++ ++/* Version dependency section. */ ++ ++typedef struct ++{ ++ Elf32_Half vn_version; /* Version of structure */ ++ Elf32_Half vn_cnt; /* Number of associated aux entries */ ++ Elf32_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf32_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf32_Verneed; ++ ++typedef struct ++{ ++ Elf64_Half vn_version; /* Version of structure */ ++ Elf64_Half vn_cnt; /* Number of associated aux entries */ ++ Elf64_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf64_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf64_Verneed; ++ ++ ++/* Legal values for vn_version (version revision). */ ++#define VER_NEED_NONE 0 /* No version */ ++#define VER_NEED_CURRENT 1 /* Current version */ ++#define VER_NEED_NUM 2 /* Given version number */ ++ ++/* Auxiliary needed version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vna_hash; /* Hash value of dependency name */ ++ Elf32_Half vna_flags; /* Dependency specific information */ ++ Elf32_Half vna_other; /* Unused */ ++ Elf32_Word vna_name; /* Dependency name string offset */ ++ Elf32_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf32_Vernaux; ++ ++typedef struct ++{ ++ Elf64_Word vna_hash; /* Hash value of dependency name */ ++ Elf64_Half vna_flags; /* Dependency specific information */ ++ Elf64_Half vna_other; /* Unused */ ++ Elf64_Word vna_name; /* Dependency name string offset */ ++ Elf64_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf64_Vernaux; ++ ++ ++/* Legal values for vna_flags. */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++ ++/* Auxiliary vector. */ ++ ++/* This vector is normally only used by the program interpreter. The ++ usual definition in an ABI supplement uses the name auxv_t. The ++ vector is not usually defined in a standard file, but it ++ can't hurt. We rename it to avoid conflicts. The sizes of these ++ types are an arrangement between the exec server and the program ++ interpreter, so we don't fully specify them here. */ ++ ++typedef struct ++{ ++ int a_type; /* Entry type */ ++ union ++ { ++ long int a_val; /* Integer value */ ++ void *a_ptr; /* Pointer value */ ++ void (*a_fcn) (void); /* Function pointer value */ ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct ++{ ++ long int a_type; /* Entry type */ ++ union ++ { ++ long int a_val; /* Integer value */ ++ void *a_ptr; /* Pointer value */ ++ void (*a_fcn) (void); /* Function pointer value */ ++ } a_un; ++} Elf64_auxv_t; ++ ++/* Legal values for a_type (entry type). */ ++ ++#define AT_NULL 0 /* End of vector */ ++#define AT_IGNORE 1 /* Entry should be ignored */ ++#define AT_EXECFD 2 /* File descriptor of program */ ++#define AT_PHDR 3 /* Program headers for program */ ++#define AT_PHENT 4 /* Size of program header entry */ ++#define AT_PHNUM 5 /* Number of program headers */ ++#define AT_PAGESZ 6 /* System page size */ ++#define AT_BASE 7 /* Base address of interpreter */ ++#define AT_FLAGS 8 /* Flags */ ++#define AT_ENTRY 9 /* Entry point of program */ ++#define AT_NOTELF 10 /* Program is not ELF */ ++#define AT_UID 11 /* Real uid */ ++#define AT_EUID 12 /* Effective uid */ ++#define AT_GID 13 /* Real gid */ ++#define AT_EGID 14 /* Effective gid */ ++#define AT_CLKTCK 17 /* Frequency of times() */ ++ ++/* Some more special a_type values describing the hardware. */ ++#define AT_PLATFORM 15 /* String identifying platform. */ ++#define AT_HWCAP 16 /* Machine dependent hints about ++ processor capabilities. */ ++ ++/* This entry gives some information about the FPU initialization ++ performed by the kernel. */ ++#define AT_FPUCW 18 /* Used FPU control word. */ ++ ++/* Cache block sizes. */ ++#define AT_DCACHEBSIZE 19 /* Data cache block size. */ ++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ ++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ ++ ++/* A special ignored value for PPC, used by the kernel to control the ++ interpretation of the AUXV. Must be > 16. */ ++#define AT_IGNOREPPC 22 /* Entry should be ignored. */ ++ ++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ ++ ++/* Pointer to the global system page used for system calls and other ++ nice things. */ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++ ++/* Note section contents. Each entry in the note section begins with ++ a header of a fixed form. */ ++ ++typedef struct ++{ ++ Elf32_Word n_namesz; /* Length of the note's name. */ ++ Elf32_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf32_Word n_type; /* Type of the note. */ ++} Elf32_Nhdr; ++ ++typedef struct ++{ ++ Elf64_Word n_namesz; /* Length of the note's name. */ ++ Elf64_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf64_Word n_type; /* Type of the note. */ ++} Elf64_Nhdr; ++ ++/* Known names of notes. */ ++ ++/* Solaris entries in the note section have this name. */ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++/* Note entries for GNU systems have this name. */ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++/* Defined types of notes for Solaris. */ ++ ++/* Value of descriptor (one word) is desired pagesize for the binary. */ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++/* Defined note types for GNU systems. */ ++ ++/* ABI information. The descriptor consists of words: ++ word 0: OS descriptor ++ word 1: major version of the ABI ++ word 2: minor version of the ABI ++ word 3: subminor version of the ABI ++*/ ++#define ELF_NOTE_ABI 1 ++ ++/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI ++ note section entry. */ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++ ++/* Move records. */ ++typedef struct ++{ ++ Elf32_Xword m_value; /* Symbol value. */ ++ Elf32_Word m_info; /* Size and index. */ ++ Elf32_Word m_poffset; /* Symbol offset. */ ++ Elf32_Half m_repeat; /* Repeat count. */ ++ Elf32_Half m_stride; /* Stride info. */ ++} Elf32_Move; ++ ++typedef struct ++{ ++ Elf64_Xword m_value; /* Symbol value. */ ++ Elf64_Xword m_info; /* Size and index. */ ++ Elf64_Xword m_poffset; /* Symbol offset. */ ++ Elf64_Half m_repeat; /* Repeat count. */ ++ Elf64_Half m_stride; /* Stride info. */ ++} Elf64_Move; ++ ++/* Macro to construct move records. */ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++ ++/* Motorola 68k specific definitions. */ ++ ++/* Values for Elf32_Ehdr.e_flags. */ ++#define EF_CPU32 0x00810000 ++ ++/* m68k relocs. */ ++ ++#define R_68K_NONE 0 /* No reloc */ ++#define R_68K_32 1 /* Direct 32 bit */ ++#define R_68K_16 2 /* Direct 16 bit */ ++#define R_68K_8 3 /* Direct 8 bit */ ++#define R_68K_PC32 4 /* PC relative 32 bit */ ++#define R_68K_PC16 5 /* PC relative 16 bit */ ++#define R_68K_PC8 6 /* PC relative 8 bit */ ++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ ++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ ++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ ++#define R_68K_GOT32O 10 /* 32 bit GOT offset */ ++#define R_68K_GOT16O 11 /* 16 bit GOT offset */ ++#define R_68K_GOT8O 12 /* 8 bit GOT offset */ ++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ ++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ ++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ ++#define R_68K_PLT32O 16 /* 32 bit PLT offset */ ++#define R_68K_PLT16O 17 /* 16 bit PLT offset */ ++#define R_68K_PLT8O 18 /* 8 bit PLT offset */ ++#define R_68K_COPY 19 /* Copy symbol at runtime */ ++#define R_68K_GLOB_DAT 20 /* Create GOT entry */ ++#define R_68K_JMP_SLOT 21 /* Create PLT entry */ ++#define R_68K_RELATIVE 22 /* Adjust by program base */ ++/* Keep this the last entry. */ ++#define R_68K_NUM 23 ++ ++/* Intel 80386 specific definitions. */ ++ ++/* i386 relocs. */ ++ ++#define R_386_NONE 0 /* No reloc */ ++#define R_386_32 1 /* Direct 32 bit */ ++#define R_386_PC32 2 /* PC relative 32 bit */ ++#define R_386_GOT32 3 /* 32 bit GOT entry */ ++#define R_386_PLT32 4 /* 32 bit PLT address */ ++#define R_386_COPY 5 /* Copy symbol at runtime */ ++#define R_386_GLOB_DAT 6 /* Create GOT entry */ ++#define R_386_JMP_SLOT 7 /* Create PLT entry */ ++#define R_386_RELATIVE 8 /* Adjust by program base */ ++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ ++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ ++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS ++ block offset */ ++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block ++ offset */ ++#define R_386_TLS_LE 17 /* Offset relative to static TLS ++ block */ ++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of ++ general dynamic thread local data */ ++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of ++ local dynamic thread local data ++ in LE code */ ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic ++ thread local data */ ++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ ++#define R_386_TLS_GD_CALL 26 /* Relocation for call to ++ __tls_get_addr() */ ++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ ++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic ++ thread local data in LE code */ ++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ ++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to ++ __tls_get_addr() in LDM code */ ++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ ++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ ++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS ++ block offset */ ++#define R_386_TLS_LE_32 34 /* Negated offset relative to static ++ TLS block */ ++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ ++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ ++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ ++/* Keep this the last entry. */ ++#define R_386_NUM 38 ++ ++/* SUN SPARC specific definitions. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_REGISTER 13 /* Global register reserved to app. */ ++ ++/* Values for Elf64_Ehdr.e_flags. */ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 /* little endian data */ ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ ++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ ++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ ++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ ++ ++/* SPARC relocs. */ ++ ++#define R_SPARC_NONE 0 /* No reloc */ ++#define R_SPARC_8 1 /* Direct 8 bit */ ++#define R_SPARC_16 2 /* Direct 16 bit */ ++#define R_SPARC_32 3 /* Direct 32 bit */ ++#define R_SPARC_DISP8 4 /* PC relative 8 bit */ ++#define R_SPARC_DISP16 5 /* PC relative 16 bit */ ++#define R_SPARC_DISP32 6 /* PC relative 32 bit */ ++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ ++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ ++#define R_SPARC_HI22 9 /* High 22 bit */ ++#define R_SPARC_22 10 /* Direct 22 bit */ ++#define R_SPARC_13 11 /* Direct 13 bit */ ++#define R_SPARC_LO10 12 /* Truncated 10 bit */ ++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ ++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ ++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ ++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ ++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ ++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ ++#define R_SPARC_COPY 19 /* Copy symbol at runtime */ ++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ ++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ ++#define R_SPARC_RELATIVE 22 /* Adjust by program base */ ++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ ++ ++/* Additional Sparc64 relocs. */ ++ ++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ ++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ ++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ ++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ ++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ ++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ ++#define R_SPARC_10 30 /* Direct 10 bit */ ++#define R_SPARC_11 31 /* Direct 11 bit */ ++#define R_SPARC_64 32 /* Direct 64 bit */ ++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ ++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ ++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ ++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ ++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ ++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ ++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ ++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ ++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ ++#define R_SPARC_7 43 /* Direct 7 bit */ ++#define R_SPARC_5 44 /* Direct 5 bit */ ++#define R_SPARC_6 45 /* Direct 6 bit */ ++#define R_SPARC_DISP64 46 /* PC relative 64 bit */ ++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ ++#define R_SPARC_HIX22 48 /* High 22 bit complemented */ ++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ ++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ ++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ ++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ ++#define R_SPARC_REGISTER 53 /* Global register usage */ ++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ ++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++/* Keep this the last entry. */ ++#define R_SPARC_NUM 80 ++ ++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++/* Bits present in AT_HWCAP, primarily for Sparc32. */ ++ ++#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ ++#define HWCAP_SPARC_STBAR 2 ++#define HWCAP_SPARC_SWAP 4 ++#define HWCAP_SPARC_MULDIV 8 ++#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ ++#define HWCAP_SPARC_ULTRA3 32 ++ ++/* MIPS R3000 specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ ++#define EF_MIPS_PIC 2 /* Contains PIC code */ ++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ ++ ++/* Legal values for MIPS architecture level. */ ++ ++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* The following are non-official names and should not be used. */ ++ ++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* Special section indices. */ ++ ++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ ++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ ++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ ++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ ++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ ++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ ++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ ++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ ++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++/* Symbol tables. */ ++ ++/* MIPS specific values for `st_other'. */ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++/* MIPS specific values for `st_info'. */ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++/* Entries found in sections of type SHT_MIPS_GPTAB. */ ++ ++typedef union ++{ ++ struct ++ { ++ Elf32_Word gt_current_g_value; /* -G value used for compilation */ ++ Elf32_Word gt_unused; /* Not used */ ++ } gt_header; /* First entry in section */ ++ struct ++ { ++ Elf32_Word gt_g_value; /* If this value were used for -G */ ++ Elf32_Word gt_bytes; /* This many bytes would be used */ ++ } gt_entry; /* Subsequent entries in section */ ++} Elf32_gptab; ++ ++/* Entry found in sections of type SHT_MIPS_REGINFO. */ ++ ++typedef struct ++{ ++ Elf32_Word ri_gprmask; /* General registers used */ ++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ ++ Elf32_Sword ri_gp_value; /* $gp register value */ ++} Elf32_RegInfo; ++ ++/* Entries found in sections of type SHT_MIPS_OPTIONS. */ ++ ++typedef struct ++{ ++ unsigned char kind; /* Determines interpretation of the ++ variable part of descriptor. */ ++ unsigned char size; /* Size of descriptor, including header. */ ++ Elf32_Section section; /* Section header index of section affected, ++ 0 for global options. */ ++ Elf32_Word info; /* Kind-specific information. */ ++} Elf_Options; ++ ++/* Values for `kind' field in Elf_Options. */ ++ ++#define ODK_NULL 0 /* Undefined. */ ++#define ODK_REGINFO 1 /* Register usage information. */ ++#define ODK_EXCEPTIONS 2 /* Exception processing options. */ ++#define ODK_PAD 3 /* Section padding options. */ ++#define ODK_HWPATCH 4 /* Hardware workarounds performed */ ++#define ODK_FILL 5 /* record the fill value used by the linker. */ ++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ ++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ ++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ ++ ++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ ++ ++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ ++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ ++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ ++#define OEX_SMM 0x20000 /* Force sequential memory mode? */ ++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ ++ ++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ ++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ ++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ ++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++/* Entry found in `.options' section. */ ++ ++typedef struct ++{ ++ Elf32_Word hwp_flags1; /* Extra flags. */ ++ Elf32_Word hwp_flags2; /* Extra flags. */ ++} Elf_Options_Hw; ++ ++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++/* MIPS relocs. */ ++ ++#define R_MIPS_NONE 0 /* No reloc */ ++#define R_MIPS_16 1 /* Direct 16 bit */ ++#define R_MIPS_32 2 /* Direct 32 bit */ ++#define R_MIPS_REL32 3 /* PC relative 32 bit */ ++#define R_MIPS_26 4 /* Direct 26 bit shifted */ ++#define R_MIPS_HI16 5 /* High 16 bit */ ++#define R_MIPS_LO16 6 /* Low 16 bit */ ++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ ++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ ++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ ++#define R_MIPS_PC16 10 /* PC relative 16 bit */ ++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ ++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++/* Keep this the last entry. */ ++#define R_MIPS_NUM 38 ++ ++/* Legal values for p_type field of Elf32_Phdr. */ ++ ++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ ++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++/* Special program header types. */ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++/* Legal values for d_tag field of Elf32_Dyn. */ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ ++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ ++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ ++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ ++#define DT_MIPS_FLAGS 0x70000005 /* Flags */ ++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ ++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ ++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ ++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ ++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ ++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ ++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ ++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ ++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ ++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in ++ DT_MIPS_DELTA_CLASS. */ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in ++ DT_MIPS_DELTA_INSTANCE. */ ++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in ++ DT_MIPS_DELTA_RELOC. */ ++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta ++ relocations refer to. */ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in ++ DT_MIPS_DELTA_SYM. */ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the ++ class declaration. */ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in ++ DT_MIPS_DELTA_CLASSSYM. */ ++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ ++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve ++ function stored in GOT. */ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added ++ by rld on dlopen() calls. */ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ ++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ ++#define DT_MIPS_NUM 0x32 ++ ++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ ++ ++#define RHF_NONE 0 /* No flags */ ++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ ++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++/* Entries found in sections of type SHT_MIPS_LIBLIST. */ ++ ++typedef struct ++{ ++ Elf32_Word l_name; /* Name (string table index) */ ++ Elf32_Word l_time_stamp; /* Timestamp */ ++ Elf32_Word l_checksum; /* Checksum */ ++ Elf32_Word l_version; /* Interface version */ ++ Elf32_Word l_flags; /* Flags */ ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; /* Name (string table index) */ ++ Elf64_Word l_time_stamp; /* Timestamp */ ++ Elf64_Word l_checksum; /* Checksum */ ++ Elf64_Word l_version; /* Interface version */ ++ Elf64_Word l_flags; /* Flags */ ++} Elf64_Lib; ++ ++ ++/* Legal values for l_flags. */ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ ++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++/* Entries found in sections of type SHT_MIPS_CONFLICT. */ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++/* HPPA specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ ++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ ++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ ++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ ++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch ++ prediction. */ ++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ ++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ ++ ++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ ++ ++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ ++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ ++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ ++ ++/* Additional section indeces. */ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared ++ symbols in ANSI C. */ ++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ ++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ ++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ ++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ ++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++/* HPPA relocs. */ ++ ++#define R_PARISC_NONE 0 /* No reloc. */ ++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ ++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ ++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ ++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ ++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ ++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ ++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ ++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ ++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ ++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ ++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ ++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ ++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ ++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ ++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ ++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ ++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ ++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ ++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ ++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ ++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ ++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ ++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ ++#define R_PARISC_FPTR64 64 /* 64 bits function address. */ ++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ ++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ ++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ ++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ ++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ ++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ ++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ ++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ ++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ ++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ ++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ ++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ ++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ ++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ ++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ ++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 /* Copy relocation. */ ++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ ++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ ++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ ++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ ++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ ++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ ++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ ++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ ++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_HIRESERVE 255 ++ ++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++/* Alpha specific definitions. */ ++ ++/* Legal values for e_flags field of Elf64_Ehdr. */ ++ ++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ ++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ ++ ++/* Legal values for sh_type field of Elf64_Shdr. */ ++ ++/* These two are primerily concerned with ECOFF debugging info. */ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++/* Legal values for sh_flags field of Elf64_Shdr. */ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++/* Legal values for st_other field of Elf64_Sym. */ ++#define STO_ALPHA_NOPV 0x80 /* No PV required. */ ++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ ++ ++/* Alpha relocs. */ ++ ++#define R_ALPHA_NONE 0 /* No reloc */ ++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ ++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ ++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ ++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ ++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ ++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ ++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ ++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ ++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ ++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ ++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ ++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ ++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ ++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ ++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ ++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ ++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ ++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++/* Keep this the last entry. */ ++#define R_ALPHA_NUM 46 ++ ++/* Magic values of the LITUSE relocation addend. */ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++ ++/* PowerPC specific declarations */ ++ ++/* Values for Elf32/64_Ehdr.e_flags. */ ++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ ++ ++/* Cygnus local bits below */ ++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib ++ flag */ ++ ++/* PowerPC relocations defined by the ABIs */ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 /* 32bit absolute address */ ++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ ++#define R_PPC_ADDR16 3 /* 16bit absolute address */ ++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ ++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ ++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ ++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 /* PC relative 26 bit */ ++#define R_PPC_REL14 11 /* PC relative 16 bit */ ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++/* PowerPC relocations defined for the TLS access ABI. */ ++#define R_PPC_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ ++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ ++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ ++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ ++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ ++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ ++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ ++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ ++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ ++ ++/* Keep this the last entry. */ ++#define R_PPC_NUM 95 ++ ++/* The remaining relocs are from the Embedded ELF ABI, and are not ++ in the SVR4 ELF ABI. */ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ ++ ++/* Diab tool relocations. */ ++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ ++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ ++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ ++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ ++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ ++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ ++ ++/* This is a phony reloc to handle any old fashioned TOC16 references ++ that may still be in object files. */ ++#define R_PPC_TOC16 255 ++ ++ ++/* PowerPC64 relocations defined by the ABIs */ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ ++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ ++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ ++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ ++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ ++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ ++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ ++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ ++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ ++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ ++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ ++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ ++#define R_PPC64_PLT64 45 /* doubleword64 L + A */ ++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ ++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ ++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ ++#define R_PPC64_TOC 51 /* doubleword64 .TOC */ ++#define R_PPC64_PLTGOT16 52 /* half16* M + A */ ++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ ++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ ++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ ++ ++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ ++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ ++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ ++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ ++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ ++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ ++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ ++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ ++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ ++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ ++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ ++ ++/* PowerPC64 relocations defined for the TLS access ABI. */ ++#define R_PPC64_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ ++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ ++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ ++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ ++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ ++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ ++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ ++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ ++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ ++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ ++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ ++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ ++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ ++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ ++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ ++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ ++ ++/* Keep this the last entry. */ ++#define R_PPC64_NUM 107 ++ ++/* PowerPC64 specific values for the Dyn d_tag field. */ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_NUM 1 ++ ++ ++/* ARM specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++ ++/* Other constants defined in the ARM ELF spec. version B-01. */ ++/* NB. These conflict with values defined above. */ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++ ++/* Additional symbol types for Thumb */ ++#define STT_ARM_TFUNC 0xd ++ ++/* ARM-specific values for sh_flags */ ++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ ++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined ++ in the input to a link step */ ++ ++/* ARM-specific program header flags */ ++#define PF_ARM_SB 0x10000000 /* Segment contains the location ++ addressed by the static base */ ++ ++/* ARM relocs. */ ++#define R_ARM_NONE 0 /* No reloc */ ++#define R_ARM_PC24 1 /* PC relative 26 bit branch */ ++#define R_ARM_ABS32 2 /* Direct 32 bit */ ++#define R_ARM_REL32 3 /* PC relative 32 bit */ ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 /* Direct 16 bit */ ++#define R_ARM_ABS12 6 /* Direct 12 bit */ ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 /* Direct 8 bit */ ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_SWI24 13 ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_COPY 20 /* Copy symbol at runtime */ ++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ ++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ ++#define R_ARM_RELATIVE 23 /* Adjust by program base */ ++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ ++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ ++#define R_ARM_GOT32 26 /* 32 bit GOT entry */ ++#define R_ARM_PLT32 27 /* 32 bit PLT address */ ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ ++#define R_ARM_THM_PC9 103 /* thumb conditional branch */ ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++/* Keep this the last entry. */ ++#define R_ARM_NUM 256 ++ ++/* IA-64 specific declarations. */ ++ ++/* Processor specific flags for the Ehdr e_flags field. */ ++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ ++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ ++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ ++ ++/* Processor specific flags for the Phdr p_flags field. */ ++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ ++ ++/* Processor specific flags for the Shdr sh_flags field. */ ++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ ++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Dyn d_tag field. */ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++/* IA-64 relocations. */ ++#define R_IA64_NONE 0x00 /* none */ ++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ ++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ ++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ ++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ ++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ ++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ ++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ ++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ ++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ ++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ ++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ ++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ ++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ ++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ ++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ ++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ ++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ ++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ ++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ ++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ ++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ ++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ ++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ ++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ ++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ ++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ ++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ ++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ ++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ ++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ ++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ ++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ ++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ ++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ ++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ ++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ ++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ ++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ ++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ ++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ ++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ ++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ ++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ ++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ ++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ ++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ ++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ ++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ ++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ ++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ ++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ ++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ ++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ ++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ ++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ ++#define R_IA64_COPY 0x84 /* copy relocation */ ++#define R_IA64_SUB 0x85 /* Addend and symbol difference */ ++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ ++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ ++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ ++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ ++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ ++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ ++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ ++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ ++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ ++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ ++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ ++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ ++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ ++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ ++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ ++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ ++ ++/* SH specific declarations */ ++ ++/* SH relocs. */ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++/* Keep this the last entry. */ ++#define R_SH_NUM 256 ++ ++/* Additional s390 relocs */ ++ ++#define R_390_NONE 0 /* No reloc. */ ++#define R_390_8 1 /* Direct 8 bit. */ ++#define R_390_12 2 /* Direct 12 bit. */ ++#define R_390_16 3 /* Direct 16 bit. */ ++#define R_390_32 4 /* Direct 32 bit. */ ++#define R_390_PC32 5 /* PC relative 32 bit. */ ++#define R_390_GOT12 6 /* 12 bit GOT offset. */ ++#define R_390_GOT32 7 /* 32 bit GOT offset. */ ++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ ++#define R_390_COPY 9 /* Copy symbol at runtime. */ ++#define R_390_GLOB_DAT 10 /* Create GOT entry. */ ++#define R_390_JMP_SLOT 11 /* Create PLT entry. */ ++#define R_390_RELATIVE 12 /* Adjust by program base. */ ++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ ++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ ++#define R_390_GOT16 15 /* 16 bit GOT offset. */ ++#define R_390_PC16 16 /* PC relative 16 bit. */ ++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ ++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ ++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ ++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ ++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ ++#define R_390_64 22 /* Direct 64 bit. */ ++#define R_390_PC64 23 /* PC relative 64 bit. */ ++#define R_390_GOT64 24 /* 64 bit GOT offset. */ ++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ ++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ ++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ ++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ ++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ ++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ ++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ ++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ ++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ ++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ ++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ ++#define R_390_TLS_GDCALL 38 /* Tag for function call in general ++ dynamic TLS code. */ ++#define R_390_TLS_LDCALL 39 /* Tag for function call in local ++ dynamic TLS code. */ ++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ ++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ ++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS ++ block. */ ++ ++/* Keep this the last entry. */ ++#define R_390_NUM 57 ++ ++/* CRIS relocations. */ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++#define R_CRIS_NUM 20 ++ ++/* AMD x86-64 relocations. */ ++#define R_X86_64_NONE 0 /* No reloc */ ++#define R_X86_64_64 1 /* Direct 64 bit */ ++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ ++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ ++#define R_X86_64_PLT32 4 /* 32 bit PLT address */ ++#define R_X86_64_COPY 5 /* Copy symbol at runtime */ ++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ ++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ ++#define R_X86_64_RELATIVE 8 /* Adjust by program base */ ++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative ++ offset to GOT */ ++#define R_X86_64_32 10 /* Direct 32 bit zero extended */ ++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ ++#define R_X86_64_16 12 /* Direct 16 bit zero extended */ ++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ ++#define R_X86_64_8 14 /* Direct 8 bit sign extended */ ++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ ++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ ++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ ++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ ++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset ++ to two GOT entries for GD symbol */ ++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset ++ to two GOT entries for LD symbol */ ++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ ++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset ++ to GOT entry for IE symbol */ ++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ ++ ++#define R_X86_64_NUM 24 ++ ++__END_DECLS ++ ++#endif /* elf.h */ + + #include "elfconfig.h" + +@@ -195,3 +2641,4 @@ + void fatal(const char *fmt, ...); + void warn(const char *fmt, ...); + void merror(const char *fmt, ...); ++ +diff -Nur linux-2.6.36.orig/scripts/mod/sumversion.c linux-2.6.36/scripts/mod/sumversion.c +--- linux-2.6.36.orig/scripts/mod/sumversion.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/scripts/mod/sumversion.c 2010-11-28 18:33:24.000000000 +0100 +@@ -1,4 +1,4 @@ +-#include ++/* #include */ + #ifdef __sun__ + #include + #else diff --git a/target/linux/patches/3.4.108/defaults.patch b/target/linux/patches/3.4.108/defaults.patch new file mode 100644 index 000000000..58aae610b --- /dev/null +++ b/target/linux/patches/3.4.108/defaults.patch @@ -0,0 +1,22 @@ +diff -Nur linux-3.0.4.orig/fs/Kconfig linux-3.0.4/fs/Kconfig +--- linux-3.0.4.orig/fs/Kconfig 2011-08-29 22:56:30.000000000 +0200 ++++ linux-3.0.4/fs/Kconfig 2011-10-15 22:08:44.000000000 +0200 +@@ -47,7 +47,7 @@ + def_bool n + + config EXPORTFS +- tristate ++ def_bool y + + config FILE_LOCKING + bool "Enable POSIX file locking API" if EXPERT +diff -Nur linux-3.0.4.orig/fs/notify/Kconfig linux-3.0.4/fs/notify/Kconfig +--- linux-3.0.4.orig/fs/notify/Kconfig 2011-08-29 22:56:30.000000000 +0200 ++++ linux-3.0.4/fs/notify/Kconfig 2011-10-15 22:02:00.000000000 +0200 +@@ -1,5 +1,5 @@ + config FSNOTIFY +- def_bool n ++ def_bool y + + source "fs/notify/dnotify/Kconfig" + source "fs/notify/inotify/Kconfig" diff --git a/target/linux/patches/3.4.108/gemalto.patch b/target/linux/patches/3.4.108/gemalto.patch new file mode 100644 index 000000000..65f7af1d7 --- /dev/null +++ b/target/linux/patches/3.4.108/gemalto.patch @@ -0,0 +1,11 @@ +diff -Nur linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c linux-2.6.36/drivers/serial/8250/serial_cs.c +--- linux-2.6.36.orig/drivers/tty/serial/8250/serial_cs.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/drivers/tty/serial/8250/serial_cs.c 2010-12-13 23:03:40.000000000 +0100 +@@ -794,6 +794,7 @@ + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), ++ PCMCIA_DEVICE_MANF_CARD(0x0157, 0x0100), /* Gemalto SCR */ + PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ + PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ + PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), diff --git a/target/linux/patches/3.4.108/lemote-rfkill.patch b/target/linux/patches/3.4.108/lemote-rfkill.patch new file mode 100644 index 000000000..a61488434 --- /dev/null +++ b/target/linux/patches/3.4.108/lemote-rfkill.patch @@ -0,0 +1,21 @@ +diff -Nur linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c +--- linux-3.3.orig/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-19 00:15:34.000000000 +0100 ++++ linux-3.3/drivers/net/wireless/rtl818x/rtl8187/rfkill.c 2012-03-27 23:29:46.000000000 +0200 +@@ -22,6 +22,9 @@ + + static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) + { ++#ifdef CONFIG_LEMOTE_MACH2F ++ return 1; ++#else + u8 gpio; + + gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); +@@ -29,6 +32,7 @@ + gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); + + return gpio & priv->rfkill_mask; ++#endif + } + + void rtl8187_rfkill_init(struct ieee80211_hw *hw) diff --git a/target/linux/patches/3.4.108/linux-gcc-check.patch b/target/linux/patches/3.4.108/linux-gcc-check.patch new file mode 100644 index 000000000..7cc381845 --- /dev/null +++ b/target/linux/patches/3.4.108/linux-gcc-check.patch @@ -0,0 +1,18 @@ +diff -Nur linux-2.6.32.orig/arch/mips/include/asm/sgidefs.h linux-2.6.32/arch/mips/include/asm/sgidefs.h +--- linux-2.6.32.orig/arch/mips/include/asm/sgidefs.h 2009-12-03 04:51:21.000000000 +0100 ++++ linux-2.6.32/arch/mips/include/asm/sgidefs.h 2010-02-14 11:49:21.000000000 +0100 +@@ -11,14 +11,6 @@ + #define __ASM_SGIDEFS_H + + /* +- * Using a Linux compiler for building Linux seems logic but not to +- * everybody. +- */ +-#ifndef __linux__ +-#error Use a Linux compiler or give up. +-#endif +- +-/* + * Definitions for the ISA levels + * + * With the introduction of MIPS32 / MIPS64 instruction sets definitions diff --git a/target/linux/patches/3.4.108/mips-error.patch b/target/linux/patches/3.4.108/mips-error.patch new file mode 100644 index 000000000..800abc80d --- /dev/null +++ b/target/linux/patches/3.4.108/mips-error.patch @@ -0,0 +1,254 @@ +From c022630633624a75b3b58f43dd3c6cc896a56cff Mon Sep 17 00:00:00 2001 +From: Steven J. Hill +Date: Fri, 06 Jul 2012 19:56:01 +0000 +Subject: MIPS: Refactor 'clear_page' and 'copy_page' functions. + +Remove usage of the '__attribute__((alias("...")))' hack that aliased +to integer arrays containing micro-assembled instructions. This hack +breaks when building a microMIPS kernel. It also makes the code much +easier to understand. + +[ralf@linux-mips.org: Added back export of the clear_page and copy_page +symbols so certain modules will work again. Also fixed build with +CONFIG_SIBYTE_DMA_PAGEOPS enabled.] + +Signed-off-by: Steven J. Hill +Cc: linux-mips@linux-mips.org +Patchwork: https://patchwork.linux-mips.org/patch/3866/ +Acked-by: David Daney +Signed-off-by: Ralf Baechle +--- +diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c +index 57ba13e..3fc1691 100644 +--- a/arch/mips/kernel/mips_ksyms.c ++++ b/arch/mips/kernel/mips_ksyms.c +@@ -5,7 +5,7 @@ + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * +- * Copyright (C) 1996, 97, 98, 99, 2000, 01, 03, 04, 05 by Ralf Baechle ++ * Copyright (C) 1996, 97, 98, 99, 2000, 01, 03, 04, 05, 12 by Ralf Baechle + * Copyright (C) 1999, 2000, 01 Silicon Graphics, Inc. + */ + #include +@@ -35,6 +35,12 @@ EXPORT_SYMBOL(memmove); + EXPORT_SYMBOL(kernel_thread); + + /* ++ * Functions that operate on entire pages. Mostly used by memory management. ++ */ ++EXPORT_SYMBOL(clear_page); ++EXPORT_SYMBOL(copy_page); ++ ++/* + * Userspace access stuff. + */ + EXPORT_SYMBOL(__copy_user); +diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile +index 4aa2028..fd6203f 100644 +--- a/arch/mips/mm/Makefile ++++ b/arch/mips/mm/Makefile +@@ -3,8 +3,8 @@ + # + + obj-y += cache.o dma-default.o extable.o fault.o \ +- gup.o init.o mmap.o page.o tlbex.o \ +- tlbex-fault.o uasm.o ++ gup.o init.o mmap.o page.o page-funcs.o \ ++ tlbex.o tlbex-fault.o uasm.o + + obj-$(CONFIG_32BIT) += ioremap.o pgtable-32.o + obj-$(CONFIG_64BIT) += pgtable-64.o +diff --git a/arch/mips/mm/page-funcs.S b/arch/mips/mm/page-funcs.S +new file mode 100644 +index 0000000..48a6b38 +--- /dev/null ++++ b/arch/mips/mm/page-funcs.S +@@ -0,0 +1,50 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Micro-assembler generated clear_page/copy_page functions. ++ * ++ * Copyright (C) 2012 MIPS Technologies, Inc. ++ * Copyright (C) 2012 Ralf Baechle ++ */ ++#include ++#include ++ ++#ifdef CONFIG_SIBYTE_DMA_PAGEOPS ++#define cpu_clear_page_function_name clear_page_cpu ++#define cpu_copy_page_function_name copy_page_cpu ++#else ++#define cpu_clear_page_function_name clear_page ++#define cpu_copy_page_function_name copy_page ++#endif ++ ++/* ++ * Maximum sizes: ++ * ++ * R4000 128 bytes S-cache: 0x058 bytes ++ * R4600 v1.7: 0x05c bytes ++ * R4600 v2.0: 0x060 bytes ++ * With prefetching, 16 word strides 0x120 bytes ++ */ ++EXPORT(__clear_page_start) ++LEAF(cpu_clear_page_function_name) ++1: j 1b /* Dummy, will be replaced. */ ++ .space 288 ++END(cpu_clear_page_function_name) ++EXPORT(__clear_page_end) ++ ++/* ++ * Maximum sizes: ++ * ++ * R4000 128 bytes S-cache: 0x11c bytes ++ * R4600 v1.7: 0x080 bytes ++ * R4600 v2.0: 0x07c bytes ++ * With prefetching, 16 word strides 0x540 bytes ++ */ ++EXPORT(__copy_page_start) ++LEAF(cpu_copy_page_function_name) ++1: j 1b /* Dummy, will be replaced. */ ++ .space 1344 ++END(cpu_copy_page_function_name) ++EXPORT(__copy_page_end) +diff --git a/arch/mips/mm/page.c b/arch/mips/mm/page.c +index cc0b626..98f530e 100644 +--- a/arch/mips/mm/page.c ++++ b/arch/mips/mm/page.c +@@ -6,6 +6,7 @@ + * Copyright (C) 2003, 04, 05 Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2007 Maciej W. Rozycki + * Copyright (C) 2008 Thiemo Seufer ++ * Copyright (C) 2012 MIPS Technologies, Inc. + */ + #include + #include +@@ -71,45 +72,6 @@ static struct uasm_reloc __cpuinitdata relocs[5]; + #define cpu_is_r4600_v1_x() ((read_c0_prid() & 0xfffffff0) == 0x00002010) + #define cpu_is_r4600_v2_x() ((read_c0_prid() & 0xfffffff0) == 0x00002020) + +-/* +- * Maximum sizes: +- * +- * R4000 128 bytes S-cache: 0x058 bytes +- * R4600 v1.7: 0x05c bytes +- * R4600 v2.0: 0x060 bytes +- * With prefetching, 16 word strides 0x120 bytes +- */ +- +-static u32 clear_page_array[0x120 / 4]; +- +-#ifdef CONFIG_SIBYTE_DMA_PAGEOPS +-void clear_page_cpu(void *page) __attribute__((alias("clear_page_array"))); +-#else +-void clear_page(void *page) __attribute__((alias("clear_page_array"))); +-#endif +- +-EXPORT_SYMBOL(clear_page); +- +-/* +- * Maximum sizes: +- * +- * R4000 128 bytes S-cache: 0x11c bytes +- * R4600 v1.7: 0x080 bytes +- * R4600 v2.0: 0x07c bytes +- * With prefetching, 16 word strides 0x540 bytes +- */ +-static u32 copy_page_array[0x540 / 4]; +- +-#ifdef CONFIG_SIBYTE_DMA_PAGEOPS +-void +-copy_page_cpu(void *to, void *from) __attribute__((alias("copy_page_array"))); +-#else +-void copy_page(void *to, void *from) __attribute__((alias("copy_page_array"))); +-#endif +- +-EXPORT_SYMBOL(copy_page); +- +- + static int pref_bias_clear_store __cpuinitdata; + static int pref_bias_copy_load __cpuinitdata; + static int pref_bias_copy_store __cpuinitdata; +@@ -282,10 +244,15 @@ static inline void __cpuinit build_clear_pref(u32 **buf, int off) + } + } + ++extern u32 __clear_page_start; ++extern u32 __clear_page_end; ++extern u32 __copy_page_start; ++extern u32 __copy_page_end; ++ + void __cpuinit build_clear_page(void) + { + int off; +- u32 *buf = (u32 *)&clear_page_array; ++ u32 *buf = &__clear_page_start; + struct uasm_label *l = labels; + struct uasm_reloc *r = relocs; + int i; +@@ -356,17 +323,17 @@ void __cpuinit build_clear_page(void) + uasm_i_jr(&buf, RA); + uasm_i_nop(&buf); + +- BUG_ON(buf > clear_page_array + ARRAY_SIZE(clear_page_array)); ++ BUG_ON(buf > &__clear_page_end); + + uasm_resolve_relocs(relocs, labels); + + pr_debug("Synthesized clear page handler (%u instructions).\n", +- (u32)(buf - clear_page_array)); ++ (u32)(buf - &__clear_page_start)); + + pr_debug("\t.set push\n"); + pr_debug("\t.set noreorder\n"); +- for (i = 0; i < (buf - clear_page_array); i++) +- pr_debug("\t.word 0x%08x\n", clear_page_array[i]); ++ for (i = 0; i < (buf - &__clear_page_start); i++) ++ pr_debug("\t.word 0x%08x\n", (&__clear_page_start)[i]); + pr_debug("\t.set pop\n"); + } + +@@ -427,7 +394,7 @@ static inline void build_copy_store_pref(u32 **buf, int off) + void __cpuinit build_copy_page(void) + { + int off; +- u32 *buf = (u32 *)©_page_array; ++ u32 *buf = &__copy_page_start; + struct uasm_label *l = labels; + struct uasm_reloc *r = relocs; + int i; +@@ -595,21 +562,23 @@ void __cpuinit build_copy_page(void) + uasm_i_jr(&buf, RA); + uasm_i_nop(&buf); + +- BUG_ON(buf > copy_page_array + ARRAY_SIZE(copy_page_array)); ++ BUG_ON(buf > &__copy_page_end); + + uasm_resolve_relocs(relocs, labels); + + pr_debug("Synthesized copy page handler (%u instructions).\n", +- (u32)(buf - copy_page_array)); ++ (u32)(buf - &__copy_page_start)); + + pr_debug("\t.set push\n"); + pr_debug("\t.set noreorder\n"); +- for (i = 0; i < (buf - copy_page_array); i++) +- pr_debug("\t.word 0x%08x\n", copy_page_array[i]); ++ for (i = 0; i < (buf - &__copy_page_start); i++) ++ pr_debug("\t.word 0x%08x\n", (&__copy_page_start)[i]); + pr_debug("\t.set pop\n"); + } + + #ifdef CONFIG_SIBYTE_DMA_PAGEOPS ++extern void clear_page_cpu(void *page); ++extern void copy_page_cpu(void *to, void *from); + + /* + * Pad descriptors to cacheline, since each is exclusively owned by a +-- +cgit v0.9.2 + diff --git a/target/linux/patches/3.4.108/mkpiggy.patch b/target/linux/patches/3.4.108/mkpiggy.patch new file mode 100644 index 000000000..d4e815cd2 --- /dev/null +++ b/target/linux/patches/3.4.108/mkpiggy.patch @@ -0,0 +1,28 @@ +diff -Nur linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c +--- linux-3.4.4.orig/arch/x86/boot/compressed/mkpiggy.c 2012-06-22 20:37:50.000000000 +0200 ++++ linux-3.4.4/arch/x86/boot/compressed/mkpiggy.c 2012-07-03 09:48:02.000000000 +0200 +@@ -29,7 +29,14 @@ + #include + #include + #include +-#include ++ ++static uint32_t getle32(const void *p) ++{ ++ const uint8_t *cp = p; ++ ++ return (uint32_t)cp[0] + ((uint32_t)cp[1] << 8) + ++ ((uint32_t)cp[2] << 16) + ((uint32_t)cp[3] << 24); ++} + + int main(int argc, char *argv[]) + { +@@ -62,7 +69,7 @@ + } + + ilen = ftell(f); +- olen = get_unaligned_le32(&olen); ++ olen = getle32(&olen); + fclose(f); + + /* diff --git a/target/linux/patches/3.4.108/module-alloc-size-check.patch b/target/linux/patches/3.4.108/module-alloc-size-check.patch new file mode 100644 index 000000000..a792ac60a --- /dev/null +++ b/target/linux/patches/3.4.108/module-alloc-size-check.patch @@ -0,0 +1,21 @@ +https://dev.openwrt.org/browser/trunk/target/linux/generic/patches-3.3/340-module_alloc_size_check.patch?rev=30813 +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -2322,12 +2322,15 @@ + + void * __weak module_alloc(unsigned long size) + { +- return size == 0 ? NULL : vmalloc_exec(size); ++ return vmalloc_exec(size); + } + + static void *module_alloc_update_bounds(unsigned long size) + { +- void *ret = module_alloc(size); ++ void *ret = NULL; ++ ++ if (size) ++ ret = module_alloc(size); + + if (ret) { + mutex_lock(&module_mutex); diff --git a/target/linux/patches/3.4.108/non-static.patch b/target/linux/patches/3.4.108/non-static.patch new file mode 100644 index 000000000..a967703d0 --- /dev/null +++ b/target/linux/patches/3.4.108/non-static.patch @@ -0,0 +1,33 @@ +diff -Nur linux-2.6.39-rc6.orig/fs/namei.c linux-2.6.39-rc6/fs/namei.c +--- linux-2.6.39-rc6.orig/fs/namei.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6/fs/namei.c 2011-05-05 11:30:14.000000000 +0200 +@@ -1769,7 +1769,7 @@ + * needs parent already locked. Doesn't follow mounts. + * SMP-safe. + */ +-static struct dentry *lookup_hash(struct nameidata *nd) ++struct dentry *lookup_hash(struct nameidata *nd) + { + return __lookup_hash(&nd->last, nd->path.dentry, nd); + } +diff -Nur linux-2.6.39-rc6.orig/fs/splice.c linux-2.6.39-rc6/fs/splice.c +--- linux-2.6.39-rc6.orig/fs/splice.c 2011-05-04 04:59:13.000000000 +0200 ++++ linux-2.6.39-rc6/fs/splice.c 2011-05-05 11:31:04.000000000 +0200 +@@ -1081,7 +1081,7 @@ + /* + * Attempt to initiate a splice from pipe to file. + */ +-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) + { + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, +@@ -1109,7 +1109,7 @@ + /* + * Attempt to initiate a splice from a file to a pipe. + */ +-static long do_splice_to(struct file *in, loff_t *ppos, ++long do_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) + { diff --git a/target/linux/patches/3.4.108/relocs.patch b/target/linux/patches/3.4.108/relocs.patch new file mode 100644 index 000000000..43c5bb580 --- /dev/null +++ b/target/linux/patches/3.4.108/relocs.patch @@ -0,0 +1,3131 @@ +diff -Nur linux-3.4.4.orig/arch/x86/tools/relocs.c linux-3.4.4/arch/x86/tools/relocs.c +--- linux-3.4.4.orig/arch/x86/tools/relocs.c 2012-06-22 20:37:50.000000000 +0200 ++++ linux-3.4.4/arch/x86/tools/relocs.c 2012-07-03 09:19:18.000000000 +0200 +@@ -5,12 +5,3123 @@ + #include + #include + #include +-#include +-#include ++//#include ++//#include + #define USE_BSD +-#include ++//#include + #include +-#include ++//#include ++ ++static inline void __put_unaligned_le16(int val, int *p) ++{ ++ *p++ = val; ++ *p++ = val >> 8; ++} ++ ++static inline void __put_unaligned_le32(int val, int *p) ++{ ++ __put_unaligned_le16(val >> 16, p + 2); ++ __put_unaligned_le16(val, p); ++} ++ ++static inline void __put_unaligned_le64(int val, int *p) ++{ ++ __put_unaligned_le32(val >> 32, p + 4); ++ __put_unaligned_le32(val, p); ++} ++ ++static inline void put_unaligned_le16(int val, void *p) ++{ ++ __put_unaligned_le16(val, p); ++} ++ ++static inline void put_unaligned_le32(int val, void *p) ++{ ++ __put_unaligned_le32(val, p); ++} ++ ++static inline void put_unaligned_le64(int val, void *p) ++{ ++ __put_unaligned_le64(val, p); ++} ++ ++/* Type for a 16-bit quantity. */ ++typedef uint16_t Elf32_Half; ++typedef uint16_t Elf64_Half; ++ ++/* Types for signed and unsigned 32-bit quantities. */ ++typedef uint32_t Elf32_Word; ++typedef int32_t Elf32_Sword; ++typedef uint32_t Elf64_Word; ++typedef int32_t Elf64_Sword; ++ ++/* Types for signed and unsigned 64-bit quantities. */ ++typedef uint64_t Elf32_Xword; ++typedef int64_t Elf32_Sxword; ++typedef uint64_t Elf64_Xword; ++typedef int64_t Elf64_Sxword; ++ ++/* Type of addresses. */ ++typedef uint32_t Elf32_Addr; ++typedef uint64_t Elf64_Addr; ++ ++/* Type of file offsets. */ ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Off; ++ ++/* Type for section indices, which are 16-bit quantities. */ ++typedef uint16_t Elf32_Section; ++typedef uint16_t Elf64_Section; ++ ++/* Type for version symbol information. */ ++typedef Elf32_Half Elf32_Versym; ++typedef Elf64_Half Elf64_Versym; ++ ++ ++/* The ELF file header. This appears at the start of every ELF file. */ ++ ++#define EI_NIDENT (16) ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf32_Half e_type; /* Object file type */ ++ Elf32_Half e_machine; /* Architecture */ ++ Elf32_Word e_version; /* Object file version */ ++ Elf32_Addr e_entry; /* Entry point virtual address */ ++ Elf32_Off e_phoff; /* Program header table file offset */ ++ Elf32_Off e_shoff; /* Section header table file offset */ ++ Elf32_Word e_flags; /* Processor-specific flags */ ++ Elf32_Half e_ehsize; /* ELF header size in bytes */ ++ Elf32_Half e_phentsize; /* Program header table entry size */ ++ Elf32_Half e_phnum; /* Program header table entry count */ ++ Elf32_Half e_shentsize; /* Section header table entry size */ ++ Elf32_Half e_shnum; /* Section header table entry count */ ++ Elf32_Half e_shstrndx; /* Section header string table index */ ++} Elf32_Ehdr; ++ ++typedef struct ++{ ++ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ ++ Elf64_Half e_type; /* Object file type */ ++ Elf64_Half e_machine; /* Architecture */ ++ Elf64_Word e_version; /* Object file version */ ++ Elf64_Addr e_entry; /* Entry point virtual address */ ++ Elf64_Off e_phoff; /* Program header table file offset */ ++ Elf64_Off e_shoff; /* Section header table file offset */ ++ Elf64_Word e_flags; /* Processor-specific flags */ ++ Elf64_Half e_ehsize; /* ELF header size in bytes */ ++ Elf64_Half e_phentsize; /* Program header table entry size */ ++ Elf64_Half e_phnum; /* Program header table entry count */ ++ Elf64_Half e_shentsize; /* Section header table entry size */ ++ Elf64_Half e_shnum; /* Section header table entry count */ ++ Elf64_Half e_shstrndx; /* Section header string table index */ ++} Elf64_Ehdr; ++ ++/* Fields in the e_ident array. The EI_* macros are indices into the ++ array. The macros under each EI_* macro are the values the byte ++ may have. */ ++ ++#define EI_MAG0 0 /* File identification byte 0 index */ ++#define ELFMAG0 0x7f /* Magic number byte 0 */ ++ ++#define EI_MAG1 1 /* File identification byte 1 index */ ++#define ELFMAG1 'E' /* Magic number byte 1 */ ++ ++#define EI_MAG2 2 /* File identification byte 2 index */ ++#define ELFMAG2 'L' /* Magic number byte 2 */ ++ ++#define EI_MAG3 3 /* File identification byte 3 index */ ++#define ELFMAG3 'F' /* Magic number byte 3 */ ++ ++/* Conglomeration of the identification bytes, for easy testing as a word. */ ++#define ELFMAG "\177ELF" ++#define SELFMAG 4 ++#if __BYTE_ORDER == __LITTLE_ENDIAN ++# define ELFMAG_U32 ((uint32_t)(ELFMAG0 + 0x100 * (ELFMAG1 + (0x100 * (ELFMAG2 + 0x100 * ELFMAG3))))) ++#elif __BYTE_ORDER == __BIG_ENDIAN ++# define ELFMAG_U32 ((uint32_t)((((ELFMAG0 * 0x100) + ELFMAG1) * 0x100 + ELFMAG2) * 0x100 + ELFMAG3)) ++#endif ++ ++#define EI_CLASS 4 /* File class byte index */ ++#define ELFCLASSNONE 0 /* Invalid class */ ++#define ELFCLASS32 1 /* 32-bit objects */ ++#define ELFCLASS64 2 /* 64-bit objects */ ++#define ELFCLASSNUM 3 ++ ++#define EI_DATA 5 /* Data encoding byte index */ ++#define ELFDATANONE 0 /* Invalid data encoding */ ++#define ELFDATA2LSB 1 /* 2's complement, little endian */ ++#define ELFDATA2MSB 2 /* 2's complement, big endian */ ++#define ELFDATANUM 3 ++ ++#define EI_VERSION 6 /* File version byte index */ ++ /* Value must be EV_CURRENT */ ++ ++#define EI_OSABI 7 /* OS ABI identification */ ++#define ELFOSABI_NONE 0 /* UNIX System V ABI */ ++#define ELFOSABI_SYSV 0 /* Alias. */ ++#define ELFOSABI_HPUX 1 /* HP-UX */ ++#define ELFOSABI_NETBSD 2 /* NetBSD. */ ++#define ELFOSABI_LINUX 3 /* Linux. */ ++#define ELFOSABI_HURD 4 /* GNU/Hurd */ ++#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ ++#define ELFOSABI_AIX 7 /* IBM AIX. */ ++#define ELFOSABI_IRIX 8 /* SGI Irix. */ ++#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ ++#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ ++#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ ++#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ ++#define ELFOSABI_OPENVMS 13 /* OpenVMS */ ++#define ELFOSABI_NSK 14 /* Hewlett-Packard Non-Stop Kernel */ ++#define ELFOSABI_AROS 15 /* Amiga Research OS */ ++#define ELFOSABI_ARM 97 /* ARM */ ++#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ ++ ++#define EI_ABIVERSION 8 /* ABI version */ ++ ++#define EI_PAD 9 /* Byte index of padding bytes */ ++ ++/* Legal values for e_type (object file type). */ ++ ++#define ET_NONE 0 /* No file type */ ++#define ET_REL 1 /* Relocatable file */ ++#define ET_EXEC 2 /* Executable file */ ++#define ET_DYN 3 /* Shared object file */ ++#define ET_CORE 4 /* Core file */ ++#define ET_NUM 5 /* Number of defined types */ ++#define ET_LOOS 0xfe00 /* OS-specific range start */ ++#define ET_HIOS 0xfeff /* OS-specific range end */ ++#define ET_LOPROC 0xff00 /* Processor-specific range start */ ++#define ET_HIPROC 0xffff /* Processor-specific range end */ ++ ++/* Legal values for e_machine (architecture). */ ++ ++#define EM_NONE 0 /* No machine */ ++#define EM_M32 1 /* AT&T WE 32100 */ ++#define EM_SPARC 2 /* SUN SPARC */ ++#define EM_386 3 /* Intel 80386 */ ++#define EM_68K 4 /* Motorola m68k family */ ++#define EM_88K 5 /* Motorola m88k family */ ++#define EM_486 6 /* Intel 80486 *//* Reserved for future use */ ++#define EM_860 7 /* Intel 80860 */ ++#define EM_MIPS 8 /* MIPS R3000 big-endian */ ++#define EM_S370 9 /* IBM System/370 */ ++#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ ++ ++#define EM_PARISC 15 /* HPPA */ ++#define EM_VPP500 17 /* Fujitsu VPP500 */ ++#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ ++#define EM_960 19 /* Intel 80960 */ ++#define EM_PPC 20 /* PowerPC */ ++#define EM_PPC64 21 /* PowerPC 64-bit */ ++#define EM_S390 22 /* IBM S390 */ ++ ++#define EM_V800 36 /* NEC V800 series */ ++#define EM_FR20 37 /* Fujitsu FR20 */ ++#define EM_RH32 38 /* TRW RH-32 */ ++#define EM_MCORE 39 /* Motorola M*Core */ /* May also be taken by Fujitsu MMA */ ++#define EM_RCE 39 /* Old name for MCore */ ++#define EM_ARM 40 /* ARM */ ++#define EM_FAKE_ALPHA 41 /* Digital Alpha */ ++#define EM_SH 42 /* Renesas SH */ ++#define EM_SPARCV9 43 /* SPARC v9 64-bit */ ++#define EM_TRICORE 44 /* Siemens Tricore */ ++#define EM_ARC 45 /* Argonaut RISC Core */ ++#define EM_H8_300 46 /* Renesas H8/300 */ ++#define EM_H8_300H 47 /* Renesas H8/300H */ ++#define EM_H8S 48 /* Renesas H8S */ ++#define EM_H8_500 49 /* Renesas H8/500 */ ++#define EM_IA_64 50 /* Intel Merced */ ++#define EM_MIPS_X 51 /* Stanford MIPS-X */ ++#define EM_COLDFIRE 52 /* Motorola Coldfire */ ++#define EM_68HC12 53 /* Motorola M68HC12 */ ++#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ ++#define EM_PCP 55 /* Siemens PCP */ ++#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ ++#define EM_NDR1 57 /* Denso NDR1 microprocessor */ ++#define EM_STARCORE 58 /* Motorola Start*Core processor */ ++#define EM_ME16 59 /* Toyota ME16 processor */ ++#define EM_ST100 60 /* STMicroelectronic ST100 processor */ ++#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ ++#define EM_X86_64 62 /* AMD x86-64 architecture */ ++#define EM_PDSP 63 /* Sony DSP Processor */ ++ ++#define EM_FX66 66 /* Siemens FX66 microcontroller */ ++#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ ++#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ ++#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ ++#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ ++#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ ++#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ ++#define EM_SVX 73 /* Silicon Graphics SVx */ ++#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ ++#define EM_VAX 75 /* Digital VAX */ ++#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ ++#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ ++#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ ++#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ ++#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ ++#define EM_HUANY 81 /* Harvard University machine-independent object files */ ++#define EM_PRISM 82 /* SiTera Prism */ ++#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ ++#define EM_FR30 84 /* Fujitsu FR30 */ ++#define EM_D10V 85 /* Mitsubishi D10V */ ++#define EM_D30V 86 /* Mitsubishi D30V */ ++#define EM_V850 87 /* NEC v850 */ ++#define EM_M32R 88 /* Renesas M32R */ ++#define EM_MN10300 89 /* Matsushita MN10300 */ ++#define EM_MN10200 90 /* Matsushita MN10200 */ ++#define EM_PJ 91 /* picoJava */ ++#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ ++#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ ++#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ ++#define EM_IP2K 101 /* Ubicom IP2022 micro controller */ ++#define EM_CR 103 /* National Semiconductor CompactRISC */ ++#define EM_MSP430 105 /* TI msp430 micro controller */ ++#define EM_BLACKFIN 106 /* Analog Devices Blackfin */ ++#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */ ++#define EM_CRX 114 /* National Semiconductor CRX */ ++#define EM_NUM 95 ++#define EM_TI_C6000 140 ++ ++/* If it is necessary to assign new unofficial EM_* values, please pick large ++ random numbers (0x8523, 0xa7f2, etc.) to minimize the chances of collision ++ with official or non-GNU unofficial values. ++ ++ NOTE: Do not just increment the most recent number by one. ++ Somebody else somewhere will do exactly the same thing, and you ++ will have a collision. Instead, pick a random number. ++ ++ Normally, each entity or maintainer responsible for a machine with an ++ unofficial e_machine number should eventually ask registry@caldera.com for ++ an officially blessed number to be added to the list above. */ ++ ++/* picoJava */ ++#define EM_PJ_OLD 99 ++ ++/* Cygnus PowerPC ELF backend. Written in the absence of an ABI. */ ++#define EM_CYGNUS_POWERPC 0x9025 ++ ++/* Old version of Sparc v9, from before the ABI; this should be ++ removed shortly. */ ++#define EM_OLD_SPARCV9 11 ++ ++/* Old version of PowerPC, this should be removed shortly. */ ++#define EM_PPC_OLD 17 ++ ++/* (Deprecated) Temporary number for the OpenRISC processor. */ ++#define EM_OR32 0x8472 ++ ++/* Renesas M32C and M16C. */ ++#define EM_M32C 0xFEB0 ++ ++/* Cygnus M32R ELF backend. Written in the absence of an ABI. */ ++#define EM_CYGNUS_M32R 0x9041 ++ ++/* old S/390 backend magic number. Written in the absence of an ABI. */ ++#define EM_S390_OLD 0xa390 ++ ++/* D10V backend magic number. Written in the absence of an ABI. */ ++#define EM_CYGNUS_D10V 0x7650 ++ ++/* D30V backend magic number. Written in the absence of an ABI. */ ++#define EM_CYGNUS_D30V 0x7676 ++ ++/* V850 backend magic number. Written in the absense of an ABI. */ ++#define EM_CYGNUS_V850 0x9080 ++ ++/* mn10200 and mn10300 backend magic numbers. ++ Written in the absense of an ABI. */ ++#define EM_CYGNUS_MN10200 0xdead ++#define EM_CYGNUS_MN10300 0xbeef ++ ++/* FR30 magic number - no EABI available. */ ++#define EM_CYGNUS_FR30 0x3330 ++ ++/* AVR magic number ++ Written in the absense of an ABI. */ ++#define EM_AVR_OLD 0x1057 ++ ++/* OpenRISC magic number ++ Written in the absense of an ABI. */ ++#define EM_OPENRISC_OLD 0x3426 ++ ++/* DLX magic number ++ Written in the absense of an ABI. */ ++#define EM_DLX 0x5aa5 ++ ++#define EM_XSTORMY16 0xad45 ++ ++/* FRV magic number - no EABI available??. */ ++#define EM_CYGNUS_FRV 0x5441 ++ ++/* Ubicom IP2xxx; no ABI */ ++#define EM_IP2K_OLD 0x8217 ++ ++#define EM_MT 0x2530 /* Morpho MT; no ABI */ ++ ++/* MSP430 magic number ++ Written in the absense everything. */ ++#define EM_MSP430_OLD 0x1059 ++ ++/* Vitesse IQ2000. */ ++#define EM_IQ2000 0xFEBA ++ ++/* Old, unofficial value for Xtensa. */ ++#define EM_XTENSA_OLD 0xabc7 ++ ++/* Alpha backend magic number. Written in the absence of an ABI. */ ++#define EM_ALPHA 0x9026 ++ ++/* NIOS magic number - no EABI available. */ ++#define EM_NIOS32 0xFEBB ++ ++/* AVR32 magic number from ATMEL */ ++#define EM_AVR32 0x18ad ++ ++/* V850 backend magic number. Written in the absense of an ABI. */ ++#define EM_CYGNUS_V850 0x9080 ++ ++/* Legal values for e_version (version). */ ++ ++#define EV_NONE 0 /* Invalid ELF version */ ++#define EV_CURRENT 1 /* Current version */ ++#define EV_NUM 2 ++ ++/* Section header. */ ++ ++typedef struct ++{ ++ Elf32_Word sh_name; /* Section name (string tbl index) */ ++ Elf32_Word sh_type; /* Section type */ ++ Elf32_Word sh_flags; /* Section flags */ ++ Elf32_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf32_Off sh_offset; /* Section file offset */ ++ Elf32_Word sh_size; /* Section size in bytes */ ++ Elf32_Word sh_link; /* Link to another section */ ++ Elf32_Word sh_info; /* Additional section information */ ++ Elf32_Word sh_addralign; /* Section alignment */ ++ Elf32_Word sh_entsize; /* Entry size if section holds table */ ++} Elf32_Shdr; ++ ++typedef struct ++{ ++ Elf64_Word sh_name; /* Section name (string tbl index) */ ++ Elf64_Word sh_type; /* Section type */ ++ Elf64_Xword sh_flags; /* Section flags */ ++ Elf64_Addr sh_addr; /* Section virtual addr at execution */ ++ Elf64_Off sh_offset; /* Section file offset */ ++ Elf64_Xword sh_size; /* Section size in bytes */ ++ Elf64_Word sh_link; /* Link to another section */ ++ Elf64_Word sh_info; /* Additional section information */ ++ Elf64_Xword sh_addralign; /* Section alignment */ ++ Elf64_Xword sh_entsize; /* Entry size if section holds table */ ++} Elf64_Shdr; ++ ++/* Special section indices. */ ++ ++#define SHN_UNDEF 0 /* Undefined section */ ++#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ ++#define SHN_LOPROC 0xff00 /* Start of processor-specific */ ++#define SHN_BEFORE 0xff00 /* Order section before all others ++ (Solaris). */ ++#define SHN_AFTER 0xff01 /* Order section after all others ++ (Solaris). */ ++#define SHN_HIPROC 0xff1f /* End of processor-specific */ ++#define SHN_LOOS 0xff20 /* Start of OS-specific */ ++#define SHN_HIOS 0xff3f /* End of OS-specific */ ++#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ ++#define SHN_COMMON 0xfff2 /* Associated symbol is common */ ++#define SHN_XINDEX 0xffff /* Index is in extra table. */ ++#define SHN_HIRESERVE 0xffff /* End of reserved indices */ ++ ++/* Legal values for sh_type (section type). */ ++ ++#define SHT_NULL 0 /* Section header table entry unused */ ++#define SHT_PROGBITS 1 /* Program data */ ++#define SHT_SYMTAB 2 /* Symbol table */ ++#define SHT_STRTAB 3 /* String table */ ++#define SHT_RELA 4 /* Relocation entries with addends */ ++#define SHT_HASH 5 /* Symbol hash table */ ++#define SHT_DYNAMIC 6 /* Dynamic linking information */ ++#define SHT_NOTE 7 /* Notes */ ++#define SHT_NOBITS 8 /* Program space with no data (bss) */ ++#define SHT_REL 9 /* Relocation entries, no addends */ ++#define SHT_SHLIB 10 /* Reserved */ ++#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ ++#define SHT_INIT_ARRAY 14 /* Array of constructors */ ++#define SHT_FINI_ARRAY 15 /* Array of destructors */ ++#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ ++#define SHT_GROUP 17 /* Section group */ ++#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ ++#define SHT_NUM 19 /* Number of defined types. */ ++#define SHT_LOOS 0x60000000 /* Start OS-specific */ ++#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ ++#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ ++#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ ++#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ ++#define SHT_SUNW_move 0x6ffffffa ++#define SHT_SUNW_COMDAT 0x6ffffffb ++#define SHT_SUNW_syminfo 0x6ffffffc ++#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ ++#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ ++#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ ++#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ ++#define SHT_HIOS 0x6fffffff /* End OS-specific type */ ++#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define SHT_LOUSER 0x80000000 /* Start of application-specific */ ++#define SHT_HIUSER 0x8fffffff /* End of application-specific */ ++ ++/* Legal values for sh_flags (section flags). */ ++ ++#define SHF_WRITE (1 << 0) /* Writable */ ++#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ ++#define SHF_EXECINSTR (1 << 2) /* Executable */ ++#define SHF_MERGE (1 << 4) /* Might be merged */ ++#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ ++#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ ++#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ ++#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling ++ required */ ++#define SHF_GROUP (1 << 9) /* Section is member of a group. */ ++#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ ++#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ ++#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ ++#define SHF_ORDERED (1 << 30) /* Special ordering requirement ++ (Solaris). */ ++#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless ++ referenced or allocated (Solaris).*/ ++ ++/* Section group handling. */ ++#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ ++ ++/* Symbol table entry. */ ++ ++typedef struct ++{ ++ Elf32_Word st_name; /* Symbol name (string tbl index) */ ++ Elf32_Addr st_value; /* Symbol value */ ++ Elf32_Word st_size; /* Symbol size */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf32_Section st_shndx; /* Section index */ ++} Elf32_Sym; ++ ++typedef struct ++{ ++ Elf64_Word st_name; /* Symbol name (string tbl index) */ ++ unsigned char st_info; /* Symbol type and binding */ ++ unsigned char st_other; /* Symbol visibility */ ++ Elf64_Section st_shndx; /* Section index */ ++ Elf64_Addr st_value; /* Symbol value */ ++ Elf64_Xword st_size; /* Symbol size */ ++} Elf64_Sym; ++ ++/* The syminfo section if available contains additional information about ++ every dynamic symbol. */ ++ ++typedef struct ++{ ++ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf32_Half si_flags; /* Per symbol flags */ ++} Elf32_Syminfo; ++ ++typedef struct ++{ ++ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ ++ Elf64_Half si_flags; /* Per symbol flags */ ++} Elf64_Syminfo; ++ ++/* Possible values for si_boundto. */ ++#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ ++#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ ++#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ ++ ++/* Possible bitmasks for si_flags. */ ++#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ ++#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ ++#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ ++#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy ++ loaded */ ++/* Syminfo version values. */ ++#define SYMINFO_NONE 0 ++#define SYMINFO_CURRENT 1 ++#define SYMINFO_NUM 2 ++ ++ ++/* How to extract and insert information held in the st_info field. */ ++ ++#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) ++#define ELF32_ST_TYPE(val) ((val) & 0xf) ++#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) ++ ++/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ ++#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) ++#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) ++#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) ++ ++/* Legal values for ST_BIND subfield of st_info (symbol binding). */ ++ ++#define STB_LOCAL 0 /* Local symbol */ ++#define STB_GLOBAL 1 /* Global symbol */ ++#define STB_WEAK 2 /* Weak symbol */ ++#define STB_NUM 3 /* Number of defined types. */ ++#define STB_LOOS 10 /* Start of OS-specific */ ++#define STB_HIOS 12 /* End of OS-specific */ ++#define STB_LOPROC 13 /* Start of processor-specific */ ++#define STB_HIPROC 15 /* End of processor-specific */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_NOTYPE 0 /* Symbol type is unspecified */ ++#define STT_OBJECT 1 /* Symbol is a data object */ ++#define STT_FUNC 2 /* Symbol is a code object */ ++#define STT_SECTION 3 /* Symbol associated with a section */ ++#define STT_FILE 4 /* Symbol's name is file name */ ++#define STT_COMMON 5 /* Symbol is a common data object */ ++#define STT_TLS 6 /* Symbol is thread-local data object*/ ++#define STT_NUM 7 /* Number of defined types. */ ++#define STT_LOOS 10 /* Start of OS-specific */ ++#define STT_HIOS 12 /* End of OS-specific */ ++#define STT_LOPROC 13 /* Start of processor-specific */ ++#define STT_HIPROC 15 /* End of processor-specific */ ++ ++ ++/* Symbol table indices are found in the hash buckets and chain table ++ of a symbol hash table section. This special index value indicates ++ the end of a chain, meaning no further symbols are found in that bucket. */ ++ ++#define STN_UNDEF 0 /* End of a chain. */ ++ ++ ++/* How to extract and insert information held in the st_other field. */ ++ ++#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) ++ ++/* For ELF64 the definitions are the same. */ ++#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) ++ ++/* Symbol visibility specification encoded in the st_other field. */ ++#define STV_DEFAULT 0 /* Default symbol visibility rules */ ++#define STV_INTERNAL 1 /* Processor specific hidden class */ ++#define STV_HIDDEN 2 /* Sym unavailable in other modules */ ++#define STV_PROTECTED 3 /* Not preemptible, not exported */ ++ ++ ++/* Relocation table entry without addend (in section of type SHT_REL). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++} Elf32_Rel; ++ ++/* I have seen two different definitions of the Elf64_Rel and ++ Elf64_Rela structures, so we'll leave them out until Novell (or ++ whoever) gets their act together. */ ++/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++} Elf64_Rel; ++ ++/* Relocation table entry with addend (in section of type SHT_RELA). */ ++ ++typedef struct ++{ ++ Elf32_Addr r_offset; /* Address */ ++ Elf32_Word r_info; /* Relocation type and symbol index */ ++ Elf32_Sword r_addend; /* Addend */ ++} Elf32_Rela; ++ ++typedef struct ++{ ++ Elf64_Addr r_offset; /* Address */ ++ Elf64_Xword r_info; /* Relocation type and symbol index */ ++ Elf64_Sxword r_addend; /* Addend */ ++} Elf64_Rela; ++ ++/* How to extract and insert information held in the r_info field. */ ++ ++#define ELF32_R_SYM(val) ((val) >> 8) ++#define ELF32_R_TYPE(val) ((val) & 0xff) ++#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) ++ ++#define ELF64_R_SYM(i) ((i) >> 32) ++#define ELF64_R_TYPE(i) ((i) & 0xffffffff) ++#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) ++ ++/* Program segment header. */ ++ ++typedef struct ++{ ++ Elf32_Word p_type; /* Segment type */ ++ Elf32_Off p_offset; /* Segment file offset */ ++ Elf32_Addr p_vaddr; /* Segment virtual address */ ++ Elf32_Addr p_paddr; /* Segment physical address */ ++ Elf32_Word p_filesz; /* Segment size in file */ ++ Elf32_Word p_memsz; /* Segment size in memory */ ++ Elf32_Word p_flags; /* Segment flags */ ++ Elf32_Word p_align; /* Segment alignment */ ++} Elf32_Phdr; ++ ++typedef struct ++{ ++ Elf64_Word p_type; /* Segment type */ ++ Elf64_Word p_flags; /* Segment flags */ ++ Elf64_Off p_offset; /* Segment file offset */ ++ Elf64_Addr p_vaddr; /* Segment virtual address */ ++ Elf64_Addr p_paddr; /* Segment physical address */ ++ Elf64_Xword p_filesz; /* Segment size in file */ ++ Elf64_Xword p_memsz; /* Segment size in memory */ ++ Elf64_Xword p_align; /* Segment alignment */ ++} Elf64_Phdr; ++ ++/* Legal values for p_type (segment type). */ ++ ++#define PT_NULL 0 /* Program header table entry unused */ ++#define PT_LOAD 1 /* Loadable program segment */ ++#define PT_DYNAMIC 2 /* Dynamic linking information */ ++#define PT_INTERP 3 /* Program interpreter */ ++#define PT_NOTE 4 /* Auxiliary information */ ++#define PT_SHLIB 5 /* Reserved */ ++#define PT_PHDR 6 /* Entry for header table itself */ ++#define PT_TLS 7 /* Thread-local storage segment */ ++#define PT_NUM 8 /* Number of defined types */ ++#define PT_LOOS 0x60000000 /* Start of OS-specific */ ++#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ ++#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ ++#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ ++#define PT_PAX_FLAGS 0x65041580 /* Indicates PaX flag markings */ ++#define PT_LOSUNW 0x6ffffffa ++#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ ++#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ ++#define PT_HISUNW 0x6fffffff ++#define PT_HIOS 0x6fffffff /* End of OS-specific */ ++#define PT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define PT_HIPROC 0x7fffffff /* End of processor-specific */ ++ ++/* Legal values for p_flags (segment flags). */ ++ ++#define PF_X (1 << 0) /* Segment is executable */ ++#define PF_W (1 << 1) /* Segment is writable */ ++#define PF_R (1 << 2) /* Segment is readable */ ++#define PF_PAGEEXEC (1 << 4) /* Enable PAGEEXEC */ ++#define PF_NOPAGEEXEC (1 << 5) /* Disable PAGEEXEC */ ++#define PF_SEGMEXEC (1 << 6) /* Enable SEGMEXEC */ ++#define PF_NOSEGMEXEC (1 << 7) /* Disable SEGMEXEC */ ++#define PF_MPROTECT (1 << 8) /* Enable MPROTECT */ ++#define PF_NOMPROTECT (1 << 9) /* Disable MPROTECT */ ++#define PF_RANDEXEC (1 << 10) /* Enable RANDEXEC */ ++#define PF_NORANDEXEC (1 << 11) /* Disable RANDEXEC */ ++#define PF_EMUTRAMP (1 << 12) /* Enable EMUTRAMP */ ++#define PF_NOEMUTRAMP (1 << 13) /* Disable EMUTRAMP */ ++#define PF_RANDMMAP (1 << 14) /* Enable RANDMMAP */ ++#define PF_NORANDMMAP (1 << 15) /* Disable RANDMMAP */ ++#define PF_MASKOS 0x0ff00000 /* OS-specific */ ++#define PF_MASKPROC 0xf0000000 /* Processor-specific */ ++ ++/* Legal values for note segment descriptor types for core files. */ ++ ++#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ ++#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ ++#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ ++#define NT_PRXREG 4 /* Contains copy of prxregset struct */ ++#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ ++#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ ++#define NT_AUXV 6 /* Contains copy of auxv array */ ++#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ ++#define NT_ASRS 8 /* Contains copy of asrset struct */ ++#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ ++#define NT_PSINFO 13 /* Contains copy of psinfo struct */ ++#define NT_PRCRED 14 /* Contains copy of prcred struct */ ++#define NT_UTSNAME 15 /* Contains copy of utsname struct */ ++#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ ++#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ ++#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ ++ ++/* Legal values for the note segment descriptor types for object files. */ ++ ++#define NT_VERSION 1 /* Contains a version string. */ ++ ++ ++/* Dynamic section entry. */ ++ ++typedef struct ++{ ++ Elf32_Sword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf32_Word d_val; /* Integer value */ ++ Elf32_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf32_Dyn; ++ ++typedef struct ++{ ++ Elf64_Sxword d_tag; /* Dynamic entry type */ ++ union ++ { ++ Elf64_Xword d_val; /* Integer value */ ++ Elf64_Addr d_ptr; /* Address value */ ++ } d_un; ++} Elf64_Dyn; ++ ++/* Legal values for d_tag (dynamic entry type). */ ++ ++#define DT_NULL 0 /* Marks end of dynamic section */ ++#define DT_NEEDED 1 /* Name of needed library */ ++#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ ++#define DT_PLTGOT 3 /* Processor defined value */ ++#define DT_HASH 4 /* Address of symbol hash table */ ++#define DT_STRTAB 5 /* Address of string table */ ++#define DT_SYMTAB 6 /* Address of symbol table */ ++#define DT_RELA 7 /* Address of Rela relocs */ ++#define DT_RELASZ 8 /* Total size of Rela relocs */ ++#define DT_RELAENT 9 /* Size of one Rela reloc */ ++#define DT_STRSZ 10 /* Size of string table */ ++#define DT_SYMENT 11 /* Size of one symbol table entry */ ++#define DT_INIT 12 /* Address of init function */ ++#define DT_FINI 13 /* Address of termination function */ ++#define DT_SONAME 14 /* Name of shared object */ ++#define DT_RPATH 15 /* Library search path (deprecated) */ ++#define DT_SYMBOLIC 16 /* Start symbol search here */ ++#define DT_REL 17 /* Address of Rel relocs */ ++#define DT_RELSZ 18 /* Total size of Rel relocs */ ++#define DT_RELENT 19 /* Size of one Rel reloc */ ++#define DT_PLTREL 20 /* Type of reloc in PLT */ ++#define DT_DEBUG 21 /* For debugging; unspecified */ ++#define DT_TEXTREL 22 /* Reloc might modify .text */ ++#define DT_JMPREL 23 /* Address of PLT relocs */ ++#define DT_BIND_NOW 24 /* Process relocations of object */ ++#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ ++#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ ++#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ ++#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ ++#define DT_RUNPATH 29 /* Library search path */ ++#define DT_FLAGS 30 /* Flags for the object being loaded */ ++#define DT_ENCODING 32 /* Start of encoded range */ ++#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ ++#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ ++#define DT_NUM 34 /* Number used */ ++#define DT_LOOS 0x6000000d /* Start of OS-specific */ ++#define DT_HIOS 0x6ffff000 /* End of OS-specific */ ++#define DT_LOPROC 0x70000000 /* Start of processor-specific */ ++#define DT_HIPROC 0x7fffffff /* End of processor-specific */ ++#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ ++ ++/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the ++ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's ++ approach. */ ++#define DT_VALRNGLO 0x6ffffd00 ++#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ ++#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ ++#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ ++#define DT_CHECKSUM 0x6ffffdf8 ++#define DT_PLTPADSZ 0x6ffffdf9 ++#define DT_MOVEENT 0x6ffffdfa ++#define DT_MOVESZ 0x6ffffdfb ++#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ ++#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting ++ the following DT_* entry. */ ++#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ ++#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ ++#define DT_VALRNGHI 0x6ffffdff ++#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ ++#define DT_VALNUM 12 ++ ++/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the ++ Dyn.d_un.d_ptr field of the Elf*_Dyn structure. ++ ++ If any adjustment is made to the ELF object after it has been ++ built these entries will need to be adjusted. */ ++#define DT_ADDRRNGLO 0x6ffffe00 ++#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ ++#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ ++#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ ++#define DT_CONFIG 0x6ffffefa /* Configuration information. */ ++#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ ++#define DT_AUDIT 0x6ffffefc /* Object auditing. */ ++#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ ++#define DT_MOVETAB 0x6ffffefe /* Move table. */ ++#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ ++#define DT_ADDRRNGHI 0x6ffffeff ++#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ ++#define DT_ADDRNUM 10 ++ ++/* The versioning entry types. The next are defined as part of the ++ GNU extension. */ ++#define DT_VERSYM 0x6ffffff0 ++ ++#define DT_RELACOUNT 0x6ffffff9 ++#define DT_RELCOUNT 0x6ffffffa ++ ++/* These were chosen by Sun. */ ++#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ ++#define DT_VERDEF 0x6ffffffc /* Address of version definition ++ table */ ++#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ ++#define DT_VERNEED 0x6ffffffe /* Address of table with needed ++ versions */ ++#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ ++#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ ++#define DT_VERSIONTAGNUM 16 ++ ++/* Sun added these machine-independent extensions in the "processor-specific" ++ range. Be compatible. */ ++#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ ++#define DT_FILTER 0x7fffffff /* Shared object to get values from */ ++#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) ++#define DT_EXTRANUM 3 ++ ++/* Values of `d_un.d_val' in the DT_FLAGS entry. */ ++#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ ++#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ ++#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ ++#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ ++#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ ++ ++/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 ++ entry in the dynamic section. */ ++#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ ++#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ ++#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ ++#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ ++#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ ++#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ ++#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ ++#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ ++#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ ++#define DF_1_TRANS 0x00000200 ++#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ ++#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ ++#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ ++#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ ++#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ ++#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ ++#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ ++ ++/* Flags for the feature selection in DT_FEATURE_1. */ ++#define DTF_1_PARINIT 0x00000001 ++#define DTF_1_CONFEXP 0x00000002 ++ ++/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ ++#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ ++#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not ++ generally available. */ ++ ++/* Version definition sections. */ ++ ++typedef struct ++{ ++ Elf32_Half vd_version; /* Version revision */ ++ Elf32_Half vd_flags; /* Version information */ ++ Elf32_Half vd_ndx; /* Version Index */ ++ Elf32_Half vd_cnt; /* Number of associated aux entries */ ++ Elf32_Word vd_hash; /* Version name hash value */ ++ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf32_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf32_Verdef; ++ ++typedef struct ++{ ++ Elf64_Half vd_version; /* Version revision */ ++ Elf64_Half vd_flags; /* Version information */ ++ Elf64_Half vd_ndx; /* Version Index */ ++ Elf64_Half vd_cnt; /* Number of associated aux entries */ ++ Elf64_Word vd_hash; /* Version name hash value */ ++ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ ++ Elf64_Word vd_next; /* Offset in bytes to next verdef ++ entry */ ++} Elf64_Verdef; ++ ++ ++/* Legal values for vd_version (version revision). */ ++#define VER_DEF_NONE 0 /* No version */ ++#define VER_DEF_CURRENT 1 /* Current version */ ++#define VER_DEF_NUM 2 /* Given version number */ ++ ++/* Legal values for vd_flags (version information flags). */ ++#define VER_FLG_BASE 0x1 /* Version definition of file itself */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++/* Versym symbol index values. */ ++#define VER_NDX_LOCAL 0 /* Symbol is local. */ ++#define VER_NDX_GLOBAL 1 /* Symbol is global. */ ++#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ ++#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ ++ ++/* Auxialiary version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vda_name; /* Version or dependency names */ ++ Elf32_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf32_Verdaux; ++ ++typedef struct ++{ ++ Elf64_Word vda_name; /* Version or dependency names */ ++ Elf64_Word vda_next; /* Offset in bytes to next verdaux ++ entry */ ++} Elf64_Verdaux; ++ ++ ++/* Version dependency section. */ ++ ++typedef struct ++{ ++ Elf32_Half vn_version; /* Version of structure */ ++ Elf32_Half vn_cnt; /* Number of associated aux entries */ ++ Elf32_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf32_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf32_Verneed; ++ ++typedef struct ++{ ++ Elf64_Half vn_version; /* Version of structure */ ++ Elf64_Half vn_cnt; /* Number of associated aux entries */ ++ Elf64_Word vn_file; /* Offset of filename for this ++ dependency */ ++ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ ++ Elf64_Word vn_next; /* Offset in bytes to next verneed ++ entry */ ++} Elf64_Verneed; ++ ++ ++/* Legal values for vn_version (version revision). */ ++#define VER_NEED_NONE 0 /* No version */ ++#define VER_NEED_CURRENT 1 /* Current version */ ++#define VER_NEED_NUM 2 /* Given version number */ ++ ++/* Auxiliary needed version information. */ ++ ++typedef struct ++{ ++ Elf32_Word vna_hash; /* Hash value of dependency name */ ++ Elf32_Half vna_flags; /* Dependency specific information */ ++ Elf32_Half vna_other; /* Unused */ ++ Elf32_Word vna_name; /* Dependency name string offset */ ++ Elf32_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf32_Vernaux; ++ ++typedef struct ++{ ++ Elf64_Word vna_hash; /* Hash value of dependency name */ ++ Elf64_Half vna_flags; /* Dependency specific information */ ++ Elf64_Half vna_other; /* Unused */ ++ Elf64_Word vna_name; /* Dependency name string offset */ ++ Elf64_Word vna_next; /* Offset in bytes to next vernaux ++ entry */ ++} Elf64_Vernaux; ++ ++ ++/* Legal values for vna_flags. */ ++#define VER_FLG_WEAK 0x2 /* Weak version identifier */ ++ ++ ++/* Auxiliary vector. */ ++ ++/* This vector is normally only used by the program interpreter. The ++ usual definition in an ABI supplement uses the name auxv_t. The ++ vector is not usually defined in a standard file, but it ++ can't hurt. We rename it to avoid conflicts. The sizes of these ++ types are an arrangement between the exec server and the program ++ interpreter, so we don't fully specify them here. */ ++ ++typedef struct ++{ ++ uint32_t a_type; /* Entry type */ ++ union ++ { ++ uint32_t a_val; /* Integer value */ ++ /* We use to have pointer elements added here. We cannot do that, ++ though, since it does not work when using 32-bit definitions ++ on 64-bit platforms and vice versa. */ ++ } a_un; ++} Elf32_auxv_t; ++ ++typedef struct ++{ ++ uint64_t a_type; /* Entry type */ ++ union ++ { ++ uint64_t a_val; /* Integer value */ ++ /* We use to have pointer elements added here. We cannot do that, ++ though, since it does not work when using 32-bit definitions ++ on 64-bit platforms and vice versa. */ ++ } a_un; ++} Elf64_auxv_t; ++ ++/* Legal values for a_type (entry type). */ ++ ++#define AT_NULL 0 /* End of vector */ ++#define AT_IGNORE 1 /* Entry should be ignored */ ++#define AT_EXECFD 2 /* File descriptor of program */ ++#define AT_PHDR 3 /* Program headers for program */ ++#define AT_PHENT 4 /* Size of program header entry */ ++#define AT_PHNUM 5 /* Number of program headers */ ++#define AT_PAGESZ 6 /* System page size */ ++#define AT_BASE 7 /* Base address of interpreter */ ++#define AT_FLAGS 8 /* Flags */ ++#define AT_ENTRY 9 /* Entry point of program */ ++#define AT_NOTELF 10 /* Program is not ELF */ ++#define AT_UID 11 /* Real uid */ ++#define AT_EUID 12 /* Effective uid */ ++#define AT_GID 13 /* Real gid */ ++#define AT_EGID 14 /* Effective gid */ ++#define AT_CLKTCK 17 /* Frequency of times() */ ++ ++/* Some more special a_type values describing the hardware. */ ++#define AT_PLATFORM 15 /* String identifying platform. */ ++#define AT_HWCAP 16 /* Machine dependent hints about ++ processor capabilities. */ ++ ++/* This entry gives some information about the FPU initialization ++ performed by the kernel. */ ++#define AT_FPUCW 18 /* Used FPU control word. */ ++ ++/* Cache block sizes. */ ++#define AT_DCACHEBSIZE 19 /* Data cache block size. */ ++#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ ++#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ ++ ++/* A special ignored value for PPC, used by the kernel to control the ++ interpretation of the AUXV. Must be > 16. */ ++#define AT_IGNOREPPC 22 /* Entry should be ignored. */ ++ ++#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ ++ ++/* Pointer to the global system page used for system calls and other ++ nice things. */ ++#define AT_SYSINFO 32 ++#define AT_SYSINFO_EHDR 33 ++ ++/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains ++ log2 of line size; mask those to get cache size. */ ++#define AT_L1I_CACHESHAPE 34 ++#define AT_L1D_CACHESHAPE 35 ++#define AT_L2_CACHESHAPE 36 ++#define AT_L3_CACHESHAPE 37 ++ ++/* Note section contents. Each entry in the note section begins with ++ a header of a fixed form. */ ++ ++typedef struct ++{ ++ Elf32_Word n_namesz; /* Length of the note's name. */ ++ Elf32_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf32_Word n_type; /* Type of the note. */ ++} Elf32_Nhdr; ++ ++typedef struct ++{ ++ Elf64_Word n_namesz; /* Length of the note's name. */ ++ Elf64_Word n_descsz; /* Length of the note's descriptor. */ ++ Elf64_Word n_type; /* Type of the note. */ ++} Elf64_Nhdr; ++ ++/* Known names of notes. */ ++ ++/* Solaris entries in the note section have this name. */ ++#define ELF_NOTE_SOLARIS "SUNW Solaris" ++ ++/* Note entries for GNU systems have this name. */ ++#define ELF_NOTE_GNU "GNU" ++ ++ ++/* Defined types of notes for Solaris. */ ++ ++/* Value of descriptor (one word) is desired pagesize for the binary. */ ++#define ELF_NOTE_PAGESIZE_HINT 1 ++ ++ ++/* Defined note types for GNU systems. */ ++ ++/* ABI information. The descriptor consists of words: ++ word 0: OS descriptor ++ word 1: major version of the ABI ++ word 2: minor version of the ABI ++ word 3: subminor version of the ABI ++*/ ++#define ELF_NOTE_ABI 1 ++ ++/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI ++ note section entry. */ ++#define ELF_NOTE_OS_LINUX 0 ++#define ELF_NOTE_OS_GNU 1 ++#define ELF_NOTE_OS_SOLARIS2 2 ++#define ELF_NOTE_OS_FREEBSD 3 ++ ++ ++/* Move records. */ ++typedef struct ++{ ++ Elf32_Xword m_value; /* Symbol value. */ ++ Elf32_Word m_info; /* Size and index. */ ++ Elf32_Word m_poffset; /* Symbol offset. */ ++ Elf32_Half m_repeat; /* Repeat count. */ ++ Elf32_Half m_stride; /* Stride info. */ ++} Elf32_Move; ++ ++typedef struct ++{ ++ Elf64_Xword m_value; /* Symbol value. */ ++ Elf64_Xword m_info; /* Size and index. */ ++ Elf64_Xword m_poffset; /* Symbol offset. */ ++ Elf64_Half m_repeat; /* Repeat count. */ ++ Elf64_Half m_stride; /* Stride info. */ ++} Elf64_Move; ++ ++/* Macro to construct move records. */ ++#define ELF32_M_SYM(info) ((info) >> 8) ++#define ELF32_M_SIZE(info) ((unsigned char) (info)) ++#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) ++ ++#define ELF64_M_SYM(info) ELF32_M_SYM (info) ++#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) ++#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) ++ ++ ++/* Motorola 68k specific definitions. */ ++ ++/* Values for Elf32_Ehdr.e_flags. */ ++#define EF_CPU32 0x00810000 ++ ++/* m68k relocs. */ ++ ++#define R_68K_NONE 0 /* No reloc */ ++#define R_68K_32 1 /* Direct 32 bit */ ++#define R_68K_16 2 /* Direct 16 bit */ ++#define R_68K_8 3 /* Direct 8 bit */ ++#define R_68K_PC32 4 /* PC relative 32 bit */ ++#define R_68K_PC16 5 /* PC relative 16 bit */ ++#define R_68K_PC8 6 /* PC relative 8 bit */ ++#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ ++#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ ++#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ ++#define R_68K_GOT32O 10 /* 32 bit GOT offset */ ++#define R_68K_GOT16O 11 /* 16 bit GOT offset */ ++#define R_68K_GOT8O 12 /* 8 bit GOT offset */ ++#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ ++#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ ++#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ ++#define R_68K_PLT32O 16 /* 32 bit PLT offset */ ++#define R_68K_PLT16O 17 /* 16 bit PLT offset */ ++#define R_68K_PLT8O 18 /* 8 bit PLT offset */ ++#define R_68K_COPY 19 /* Copy symbol at runtime */ ++#define R_68K_GLOB_DAT 20 /* Create GOT entry */ ++#define R_68K_JMP_SLOT 21 /* Create PLT entry */ ++#define R_68K_RELATIVE 22 /* Adjust by program base */ ++/* Keep this the last entry. */ ++#define R_68K_NUM 23 ++ ++/* Intel 80386 specific definitions. */ ++ ++/* i386 relocs. */ ++ ++#define R_386_NONE 0 /* No reloc */ ++#define R_386_32 1 /* Direct 32 bit */ ++#define R_386_PC32 2 /* PC relative 32 bit */ ++#define R_386_GOT32 3 /* 32 bit GOT entry */ ++#define R_386_PLT32 4 /* 32 bit PLT address */ ++#define R_386_COPY 5 /* Copy symbol at runtime */ ++#define R_386_GLOB_DAT 6 /* Create GOT entry */ ++#define R_386_JMP_SLOT 7 /* Create PLT entry */ ++#define R_386_RELATIVE 8 /* Adjust by program base */ ++#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ ++#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ ++#define R_386_32PLT 11 ++#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ ++#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS ++ block offset */ ++#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block ++ offset */ ++#define R_386_TLS_LE 17 /* Offset relative to static TLS ++ block */ ++#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of ++ general dynamic thread local data */ ++#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of ++ local dynamic thread local data ++ in LE code */ ++#define R_386_16 20 ++#define R_386_PC16 21 ++#define R_386_8 22 ++#define R_386_PC8 23 ++#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic ++ thread local data */ ++#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ ++#define R_386_TLS_GD_CALL 26 /* Relocation for call to ++ __tls_get_addr() */ ++#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ ++#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic ++ thread local data in LE code */ ++#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ ++#define R_386_TLS_LDM_CALL 30 /* Relocation for call to ++ __tls_get_addr() in LDM code */ ++#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ ++#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ ++#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS ++ block offset */ ++#define R_386_TLS_LE_32 34 /* Negated offset relative to static ++ TLS block */ ++#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ ++#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ ++#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ ++/* Keep this the last entry. */ ++#define R_386_NUM 38 ++ ++/* Blackfin specific definitions. */ ++#define R_BFIN_UNUSED0 0x00 ++#define R_BFIN_PCREL5M2 0x01 ++#define R_BFIN_UNUSED1 0x02 ++#define R_BFIN_PCREL10 0x03 ++#define R_BFIN_PCREL12_JUMP 0x04 ++#define R_BFIN_RIMM16 0x05 ++#define R_BFIN_LUIMM16 0x06 ++#define R_BFIN_HUIMM16 0x07 ++#define R_BFIN_PCREL12_JUMP_S 0x08 ++#define R_BFIN_PCREL24_JUMP_X 0x09 ++#define R_BFIN_PCREL24 0x0a ++#define R_BFIN_UNUSEDB 0x0b ++#define R_BFIN_UNUSEDC 0x0c ++#define R_BFIN_PCREL24_JUMP_L 0x0d ++#define R_BFIN_PCREL24_CALL_X 0x0e ++#define R_BFIN_var_eq_symb 0x0f ++#define R_BFIN_BYTE_DATA 0x10 ++#define R_BFIN_BYTE2_DATA 0x11 ++#define R_BFIN_BYTE4_DATA 0x12 ++#define R_BFIN_PCREL11 0x13 ++ ++#define R_BFIN_GOT17M4 0x14 ++#define R_BFIN_GOTHI 0x15 ++#define R_BFIN_GOTLO 0x16 ++#define R_BFIN_FUNCDESC 0x17 ++#define R_BFIN_FUNCDESC_GOT17M4 0x18 ++#define R_BFIN_FUNCDESC_GOTHI 0x19 ++#define R_BFIN_FUNCDESC_GOTLO 0x1a ++#define R_BFIN_FUNCDESC_VALUE 0x1b ++#define R_BFIN_FUNCDESC_GOTOFF17M4 0x1c ++#define R_BFIN_FUNCDESC_GOTOFFHI 0x1d ++#define R_BFIN_FUNCDESC_GOTOFFLO 0x1e ++#define R_BFIN_GOTOFF17M4 0x1f ++#define R_BFIN_GOTOFFHI 0x20 ++#define R_BFIN_GOTOFFLO 0x21 ++ ++#define EF_BFIN_PIC 0x00000001 /* -fpic */ ++#define EF_BFIN_FDPIC 0x00000002 /* -mfdpic */ ++#define EF_BFIN_CODE_IN_L1 0x00000010 /* --code-in-l1 */ ++#define EF_BFIN_DATA_IN_L1 0x00000020 /* --data-in-l1 */ ++ ++/* FR-V specific definitions. */ ++#define R_FRV_NONE 0 /* No reloc. */ ++#define R_FRV_32 1 /* Direct 32 bit. */ ++/* Canonical function descriptor address. */ ++#define R_FRV_FUNCDESC 14 ++/* Private function descriptor initialization. */ ++#define R_FRV_FUNCDESC_VALUE 18 ++ ++ /* gpr support */ ++#define EF_FRV_GPR_MASK 0x00000003 /* mask for # of gprs */ ++#define EF_FRV_GPR_32 0x00000001 /* -mgpr-32 */ ++#define EF_FRV_GPR_64 0x00000002 /* -mgpr-64 */ ++ ++ /* fpr support */ ++#define EF_FRV_FPR_MASK 0x0000000c /* mask for # of fprs */ ++#define EF_FRV_FPR_32 0x00000004 /* -mfpr-32 */ ++#define EF_FRV_FPR_64 0x00000008 /* -mfpr-64 */ ++#define EF_FRV_FPR_NONE 0x0000000c /* -msoft-float */ ++ ++#define EF_FRV_PIC 0x00000100 ++#define EF_FRV_FDPIC 0x00008000 ++ ++/* SUN SPARC specific definitions. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ ++ ++/* Values for Elf64_Ehdr.e_flags. */ ++ ++#define EF_SPARCV9_MM 3 ++#define EF_SPARCV9_TSO 0 ++#define EF_SPARCV9_PSO 1 ++#define EF_SPARCV9_RMO 2 ++#define EF_SPARC_LEDATA 0x800000 /* little endian data */ ++#define EF_SPARC_EXT_MASK 0xFFFF00 ++#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ ++#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ ++#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ ++#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ ++ ++/* SPARC relocs. */ ++ ++#define R_SPARC_NONE 0 /* No reloc */ ++#define R_SPARC_8 1 /* Direct 8 bit */ ++#define R_SPARC_16 2 /* Direct 16 bit */ ++#define R_SPARC_32 3 /* Direct 32 bit */ ++#define R_SPARC_DISP8 4 /* PC relative 8 bit */ ++#define R_SPARC_DISP16 5 /* PC relative 16 bit */ ++#define R_SPARC_DISP32 6 /* PC relative 32 bit */ ++#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ ++#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ ++#define R_SPARC_HI22 9 /* High 22 bit */ ++#define R_SPARC_22 10 /* Direct 22 bit */ ++#define R_SPARC_13 11 /* Direct 13 bit */ ++#define R_SPARC_LO10 12 /* Truncated 10 bit */ ++#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ ++#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ ++#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ ++#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ ++#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ ++#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ ++#define R_SPARC_COPY 19 /* Copy symbol at runtime */ ++#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ ++#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ ++#define R_SPARC_RELATIVE 22 /* Adjust by program base */ ++#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ ++ ++/* Additional Sparc64 relocs. */ ++ ++#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ ++#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ ++#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ ++#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ ++#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ ++#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ ++#define R_SPARC_10 30 /* Direct 10 bit */ ++#define R_SPARC_11 31 /* Direct 11 bit */ ++#define R_SPARC_64 32 /* Direct 64 bit */ ++#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ ++#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ ++#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ ++#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ ++#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ ++#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ ++#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ ++#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ ++#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ ++#define R_SPARC_7 43 /* Direct 7 bit */ ++#define R_SPARC_5 44 /* Direct 5 bit */ ++#define R_SPARC_6 45 /* Direct 6 bit */ ++#define R_SPARC_DISP64 46 /* PC relative 64 bit */ ++#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ ++#define R_SPARC_HIX22 48 /* High 22 bit complemented */ ++#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ ++#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ ++#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ ++#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ ++#define R_SPARC_REGISTER 53 /* Global register usage */ ++#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ ++#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ ++#define R_SPARC_TLS_GD_HI22 56 ++#define R_SPARC_TLS_GD_LO10 57 ++#define R_SPARC_TLS_GD_ADD 58 ++#define R_SPARC_TLS_GD_CALL 59 ++#define R_SPARC_TLS_LDM_HI22 60 ++#define R_SPARC_TLS_LDM_LO10 61 ++#define R_SPARC_TLS_LDM_ADD 62 ++#define R_SPARC_TLS_LDM_CALL 63 ++#define R_SPARC_TLS_LDO_HIX22 64 ++#define R_SPARC_TLS_LDO_LOX10 65 ++#define R_SPARC_TLS_LDO_ADD 66 ++#define R_SPARC_TLS_IE_HI22 67 ++#define R_SPARC_TLS_IE_LO10 68 ++#define R_SPARC_TLS_IE_LD 69 ++#define R_SPARC_TLS_IE_LDX 70 ++#define R_SPARC_TLS_IE_ADD 71 ++#define R_SPARC_TLS_LE_HIX22 72 ++#define R_SPARC_TLS_LE_LOX10 73 ++#define R_SPARC_TLS_DTPMOD32 74 ++#define R_SPARC_TLS_DTPMOD64 75 ++#define R_SPARC_TLS_DTPOFF32 76 ++#define R_SPARC_TLS_DTPOFF64 77 ++#define R_SPARC_TLS_TPOFF32 78 ++#define R_SPARC_TLS_TPOFF64 79 ++/* Keep this the last entry. */ ++#define R_SPARC_NUM 80 ++ ++/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ ++ ++#define DT_SPARC_REGISTER 0x70000001 ++#define DT_SPARC_NUM 2 ++ ++/* Bits present in AT_HWCAP, primarily for Sparc32. */ ++ ++#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ ++#define HWCAP_SPARC_STBAR 2 ++#define HWCAP_SPARC_SWAP 4 ++#define HWCAP_SPARC_MULDIV 8 ++#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ ++#define HWCAP_SPARC_ULTRA3 32 ++ ++/* MIPS R3000 specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ ++#define EF_MIPS_PIC 2 /* Contains PIC code */ ++#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ ++#define EF_MIPS_XGOT 8 ++#define EF_MIPS_64BIT_WHIRL 16 ++#define EF_MIPS_ABI2 32 ++#define EF_MIPS_ABI_ON32 64 ++#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ ++ ++/* Legal values for MIPS architecture level. */ ++ ++#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* The following are non-official names and should not be used. */ ++ ++#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ ++#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ ++#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ ++#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ ++#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ ++#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ ++#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ ++ ++/* Special section indices. */ ++ ++#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ ++#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ ++#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ ++#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ ++#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ ++#define SHT_MIPS_MSYM 0x70000001 ++#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ ++#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ ++#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ ++#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ ++#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ ++#define SHT_MIPS_PACKAGE 0x70000007 ++#define SHT_MIPS_PACKSYM 0x70000008 ++#define SHT_MIPS_RELD 0x70000009 ++#define SHT_MIPS_IFACE 0x7000000b ++#define SHT_MIPS_CONTENT 0x7000000c ++#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ ++#define SHT_MIPS_SHDR 0x70000010 ++#define SHT_MIPS_FDESC 0x70000011 ++#define SHT_MIPS_EXTSYM 0x70000012 ++#define SHT_MIPS_DENSE 0x70000013 ++#define SHT_MIPS_PDESC 0x70000014 ++#define SHT_MIPS_LOCSYM 0x70000015 ++#define SHT_MIPS_AUXSYM 0x70000016 ++#define SHT_MIPS_OPTSYM 0x70000017 ++#define SHT_MIPS_LOCSTR 0x70000018 ++#define SHT_MIPS_LINE 0x70000019 ++#define SHT_MIPS_RFDESC 0x7000001a ++#define SHT_MIPS_DELTASYM 0x7000001b ++#define SHT_MIPS_DELTAINST 0x7000001c ++#define SHT_MIPS_DELTACLASS 0x7000001d ++#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ ++#define SHT_MIPS_DELTADECL 0x7000001f ++#define SHT_MIPS_SYMBOL_LIB 0x70000020 ++#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ ++#define SHT_MIPS_TRANSLATE 0x70000022 ++#define SHT_MIPS_PIXIE 0x70000023 ++#define SHT_MIPS_XLATE 0x70000024 ++#define SHT_MIPS_XLATE_DEBUG 0x70000025 ++#define SHT_MIPS_WHIRL 0x70000026 ++#define SHT_MIPS_EH_REGION 0x70000027 ++#define SHT_MIPS_XLATE_OLD 0x70000028 ++#define SHT_MIPS_PDR_EXCEPTION 0x70000029 ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ ++#define SHF_MIPS_MERGE 0x20000000 ++#define SHF_MIPS_ADDR 0x40000000 ++#define SHF_MIPS_STRINGS 0x80000000 ++#define SHF_MIPS_NOSTRIP 0x08000000 ++#define SHF_MIPS_LOCAL 0x04000000 ++#define SHF_MIPS_NAMES 0x02000000 ++#define SHF_MIPS_NODUPE 0x01000000 ++ ++ ++/* Symbol tables. */ ++ ++/* MIPS specific values for `st_other'. */ ++#define STO_MIPS_DEFAULT 0x0 ++#define STO_MIPS_INTERNAL 0x1 ++#define STO_MIPS_HIDDEN 0x2 ++#define STO_MIPS_PROTECTED 0x3 ++#define STO_MIPS_PLT 0x8 ++#define STO_MIPS_SC_ALIGN_UNUSED 0xff ++ ++/* MIPS specific values for `st_info'. */ ++#define STB_MIPS_SPLIT_COMMON 13 ++ ++/* Entries found in sections of type SHT_MIPS_GPTAB. */ ++ ++typedef union ++{ ++ struct ++ { ++ Elf32_Word gt_current_g_value; /* -G value used for compilation */ ++ Elf32_Word gt_unused; /* Not used */ ++ } gt_header; /* First entry in section */ ++ struct ++ { ++ Elf32_Word gt_g_value; /* If this value were used for -G */ ++ Elf32_Word gt_bytes; /* This many bytes would be used */ ++ } gt_entry; /* Subsequent entries in section */ ++} Elf32_gptab; ++ ++/* Entry found in sections of type SHT_MIPS_REGINFO. */ ++ ++typedef struct ++{ ++ Elf32_Word ri_gprmask; /* General registers used */ ++ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ ++ Elf32_Sword ri_gp_value; /* $gp register value */ ++} Elf32_RegInfo; ++ ++/* Entries found in sections of type SHT_MIPS_OPTIONS. */ ++ ++typedef struct ++{ ++ unsigned char kind; /* Determines interpretation of the ++ variable part of descriptor. */ ++ unsigned char size; /* Size of descriptor, including header. */ ++ Elf32_Section section; /* Section header index of section affected, ++ 0 for global options. */ ++ Elf32_Word info; /* Kind-specific information. */ ++} Elf_Options; ++ ++/* Values for `kind' field in Elf_Options. */ ++ ++#define ODK_NULL 0 /* Undefined. */ ++#define ODK_REGINFO 1 /* Register usage information. */ ++#define ODK_EXCEPTIONS 2 /* Exception processing options. */ ++#define ODK_PAD 3 /* Section padding options. */ ++#define ODK_HWPATCH 4 /* Hardware workarounds performed */ ++#define ODK_FILL 5 /* record the fill value used by the linker. */ ++#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ ++#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ ++#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ ++ ++/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ ++ ++#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ ++#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ ++#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ ++#define OEX_SMM 0x20000 /* Force sequential memory mode? */ ++#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ ++#define OEX_PRECISEFP OEX_FPDBUG ++#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ ++ ++#define OEX_FPU_INVAL 0x10 ++#define OEX_FPU_DIV0 0x08 ++#define OEX_FPU_OFLO 0x04 ++#define OEX_FPU_UFLO 0x02 ++#define OEX_FPU_INEX 0x01 ++ ++/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ ++ ++#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ ++#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ ++#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ ++#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ ++ ++#define OPAD_PREFIX 0x1 ++#define OPAD_POSTFIX 0x2 ++#define OPAD_SYMBOL 0x4 ++ ++/* Entry found in `.options' section. */ ++ ++typedef struct ++{ ++ Elf32_Word hwp_flags1; /* Extra flags. */ ++ Elf32_Word hwp_flags2; /* Extra flags. */ ++} Elf_Options_Hw; ++ ++/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ ++ ++#define OHWA0_R4KEOP_CHECKED 0x00000001 ++#define OHWA1_R4KEOP_CLEAN 0x00000002 ++ ++/* MIPS relocs. */ ++ ++#define R_MIPS_NONE 0 /* No reloc */ ++#define R_MIPS_16 1 /* Direct 16 bit */ ++#define R_MIPS_32 2 /* Direct 32 bit */ ++#define R_MIPS_REL32 3 /* PC relative 32 bit */ ++#define R_MIPS_26 4 /* Direct 26 bit shifted */ ++#define R_MIPS_HI16 5 /* High 16 bit */ ++#define R_MIPS_LO16 6 /* Low 16 bit */ ++#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ ++#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ ++#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ ++#define R_MIPS_PC16 10 /* PC relative 16 bit */ ++#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ ++#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ ++ ++#define R_MIPS_SHIFT5 16 ++#define R_MIPS_SHIFT6 17 ++#define R_MIPS_64 18 ++#define R_MIPS_GOT_DISP 19 ++#define R_MIPS_GOT_PAGE 20 ++#define R_MIPS_GOT_OFST 21 ++#define R_MIPS_GOT_HI16 22 ++#define R_MIPS_GOT_LO16 23 ++#define R_MIPS_SUB 24 ++#define R_MIPS_INSERT_A 25 ++#define R_MIPS_INSERT_B 26 ++#define R_MIPS_DELETE 27 ++#define R_MIPS_HIGHER 28 ++#define R_MIPS_HIGHEST 29 ++#define R_MIPS_CALL_HI16 30 ++#define R_MIPS_CALL_LO16 31 ++#define R_MIPS_SCN_DISP 32 ++#define R_MIPS_REL16 33 ++#define R_MIPS_ADD_IMMEDIATE 34 ++#define R_MIPS_PJUMP 35 ++#define R_MIPS_RELGOT 36 ++#define R_MIPS_JALR 37 ++#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ ++#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ ++#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ ++#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ ++#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ ++#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ ++#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ ++#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ ++#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ ++#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ ++#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ ++#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ ++#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ ++#define R_MIPS_GLOB_DAT 51 ++#define R_MIPS_COPY 126 ++#define R_MIPS_JUMP_SLOT 127 ++/* Keep this the last entry. */ ++#define R_MIPS_NUM 128 ++ ++/* Legal values for p_type field of Elf32_Phdr. */ ++ ++#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ ++#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ ++#define PT_MIPS_OPTIONS 0x70000002 ++ ++/* Special program header types. */ ++ ++#define PF_MIPS_LOCAL 0x10000000 ++ ++/* Legal values for d_tag field of Elf32_Dyn. */ ++ ++#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ ++#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ ++#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ ++#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ ++#define DT_MIPS_FLAGS 0x70000005 /* Flags */ ++#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ ++#define DT_MIPS_MSYM 0x70000007 ++#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ ++#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ ++#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ ++#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ ++#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ ++#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ ++#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ ++#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ ++#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ ++#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ ++#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ ++#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in ++ DT_MIPS_DELTA_CLASS. */ ++#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ ++#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in ++ DT_MIPS_DELTA_INSTANCE. */ ++#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ ++#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in ++ DT_MIPS_DELTA_RELOC. */ ++#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta ++ relocations refer to. */ ++#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in ++ DT_MIPS_DELTA_SYM. */ ++#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the ++ class declaration. */ ++#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in ++ DT_MIPS_DELTA_CLASSSYM. */ ++#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ ++#define DT_MIPS_PIXIE_INIT 0x70000023 ++#define DT_MIPS_SYMBOL_LIB 0x70000024 ++#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 ++#define DT_MIPS_LOCAL_GOTIDX 0x70000026 ++#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 ++#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 ++#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ ++#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ ++#define DT_MIPS_DYNSTR_ALIGN 0x7000002b ++#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ ++#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve ++ function stored in GOT. */ ++#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added ++ by rld on dlopen() calls. */ ++#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ ++#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ ++#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ ++/* The address of .got.plt in an executable using the new non-PIC ABI. */ ++#define DT_MIPS_PLTGOT 0x70000032 ++/* The base of the PLT in an executable using the new non-PIC ABI if that ++ PLT is writable. For a non-writable PLT, this is omitted or has a zero ++ value. */ ++#define DT_MIPS_RWPLT 0x70000034 ++#define DT_MIPS_NUM 0x35 ++ ++/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ ++ ++#define RHF_NONE 0 /* No flags */ ++#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ ++#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ ++#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ ++#define RHF_NO_MOVE (1 << 3) ++#define RHF_SGI_ONLY (1 << 4) ++#define RHF_GUARANTEE_INIT (1 << 5) ++#define RHF_DELTA_C_PLUS_PLUS (1 << 6) ++#define RHF_GUARANTEE_START_INIT (1 << 7) ++#define RHF_PIXIE (1 << 8) ++#define RHF_DEFAULT_DELAY_LOAD (1 << 9) ++#define RHF_REQUICKSTART (1 << 10) ++#define RHF_REQUICKSTARTED (1 << 11) ++#define RHF_CORD (1 << 12) ++#define RHF_NO_UNRES_UNDEF (1 << 13) ++#define RHF_RLD_ORDER_SAFE (1 << 14) ++ ++/* Entries found in sections of type SHT_MIPS_LIBLIST. */ ++ ++typedef struct ++{ ++ Elf32_Word l_name; /* Name (string table index) */ ++ Elf32_Word l_time_stamp; /* Timestamp */ ++ Elf32_Word l_checksum; /* Checksum */ ++ Elf32_Word l_version; /* Interface version */ ++ Elf32_Word l_flags; /* Flags */ ++} Elf32_Lib; ++ ++typedef struct ++{ ++ Elf64_Word l_name; /* Name (string table index) */ ++ Elf64_Word l_time_stamp; /* Timestamp */ ++ Elf64_Word l_checksum; /* Checksum */ ++ Elf64_Word l_version; /* Interface version */ ++ Elf64_Word l_flags; /* Flags */ ++} Elf64_Lib; ++ ++ ++/* Legal values for l_flags. */ ++ ++#define LL_NONE 0 ++#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ ++#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ ++#define LL_REQUIRE_MINOR (1 << 2) ++#define LL_EXPORTS (1 << 3) ++#define LL_DELAY_LOAD (1 << 4) ++#define LL_DELTA (1 << 5) ++ ++/* Entries found in sections of type SHT_MIPS_CONFLICT. */ ++ ++typedef Elf32_Addr Elf32_Conflict; ++ ++ ++/* HPPA specific definitions. */ ++ ++/* Legal values for e_flags field of Elf32_Ehdr. */ ++ ++#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ ++#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ ++#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ ++#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ ++#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch ++ prediction. */ ++#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ ++#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ ++ ++/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ ++ ++#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ ++#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ ++#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ ++ ++/* Additional section indeces. */ ++ ++#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared ++ symbols in ANSI C. */ ++#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ ++ ++/* Legal values for sh_type field of Elf32_Shdr. */ ++ ++#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ ++#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ ++#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ ++ ++/* Legal values for sh_flags field of Elf32_Shdr. */ ++ ++#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ ++#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ ++#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ ++ ++/* Legal values for ST_TYPE subfield of st_info (symbol type). */ ++ ++#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ ++ ++#define STT_HP_OPAQUE (STT_LOOS + 0x1) ++#define STT_HP_STUB (STT_LOOS + 0x2) ++ ++/* HPPA relocs. */ ++ ++#define R_PARISC_NONE 0 /* No reloc. */ ++#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ ++#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ ++#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ ++#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ ++#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ ++#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ ++#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ ++#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ ++#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ ++#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ ++#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ ++#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ ++#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ ++#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ ++#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ ++#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ ++#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ ++#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ ++#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ ++#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ ++#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ ++#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ ++#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ ++#define R_PARISC_FPTR64 64 /* 64 bits function address. */ ++#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ ++#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ ++#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ ++#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ ++#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ ++#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ ++#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ ++#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ ++#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ ++#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ ++#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ ++#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ ++#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ ++#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ ++#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ ++#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ ++#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ ++#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ ++#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ ++#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ ++#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ ++#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ ++#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ ++#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ ++#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ ++#define R_PARISC_LORESERVE 128 ++#define R_PARISC_COPY 128 /* Copy relocation. */ ++#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ ++#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ ++#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ ++#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ ++#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ ++#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ ++#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ ++#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ ++#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ ++#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ ++#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ ++#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ ++#define R_PARISC_HIRESERVE 255 ++ ++/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PT_HP_TLS (PT_LOOS + 0x0) ++#define PT_HP_CORE_NONE (PT_LOOS + 0x1) ++#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) ++#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) ++#define PT_HP_CORE_COMM (PT_LOOS + 0x4) ++#define PT_HP_CORE_PROC (PT_LOOS + 0x5) ++#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) ++#define PT_HP_CORE_STACK (PT_LOOS + 0x7) ++#define PT_HP_CORE_SHM (PT_LOOS + 0x8) ++#define PT_HP_CORE_MMF (PT_LOOS + 0x9) ++#define PT_HP_PARALLEL (PT_LOOS + 0x10) ++#define PT_HP_FASTBIND (PT_LOOS + 0x11) ++#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) ++#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) ++#define PT_HP_STACK (PT_LOOS + 0x14) ++ ++#define PT_PARISC_ARCHEXT 0x70000000 ++#define PT_PARISC_UNWIND 0x70000001 ++ ++/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ ++ ++#define PF_PARISC_SBP 0x08000000 ++ ++#define PF_HP_PAGE_SIZE 0x00100000 ++#define PF_HP_FAR_SHARED 0x00200000 ++#define PF_HP_NEAR_SHARED 0x00400000 ++#define PF_HP_CODE 0x01000000 ++#define PF_HP_MODIFY 0x02000000 ++#define PF_HP_LAZYSWAP 0x04000000 ++#define PF_HP_SBP 0x08000000 ++ ++ ++/* Alpha specific definitions. */ ++ ++/* Legal values for e_flags field of Elf64_Ehdr. */ ++ ++#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ ++#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ ++ ++/* Legal values for sh_type field of Elf64_Shdr. */ ++ ++/* These two are primerily concerned with ECOFF debugging info. */ ++#define SHT_ALPHA_DEBUG 0x70000001 ++#define SHT_ALPHA_REGINFO 0x70000002 ++ ++/* Legal values for sh_flags field of Elf64_Shdr. */ ++ ++#define SHF_ALPHA_GPREL 0x10000000 ++ ++/* Legal values for st_other field of Elf64_Sym. */ ++#define STO_ALPHA_NOPV 0x80 /* No PV required. */ ++#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ ++ ++/* Alpha relocs. */ ++ ++#define R_ALPHA_NONE 0 /* No reloc */ ++#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ ++#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ ++#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ ++#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ ++#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ ++#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ ++#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ ++#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ ++#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ ++#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ ++#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ ++#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ ++#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ ++#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ ++#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ ++#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ ++#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ ++#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ ++#define R_ALPHA_TLS_GD_HI 28 ++#define R_ALPHA_TLSGD 29 ++#define R_ALPHA_TLS_LDM 30 ++#define R_ALPHA_DTPMOD64 31 ++#define R_ALPHA_GOTDTPREL 32 ++#define R_ALPHA_DTPREL64 33 ++#define R_ALPHA_DTPRELHI 34 ++#define R_ALPHA_DTPRELLO 35 ++#define R_ALPHA_DTPREL16 36 ++#define R_ALPHA_GOTTPREL 37 ++#define R_ALPHA_TPREL64 38 ++#define R_ALPHA_TPRELHI 39 ++#define R_ALPHA_TPRELLO 40 ++#define R_ALPHA_TPREL16 41 ++/* Keep this the last entry. */ ++#define R_ALPHA_NUM 46 ++ ++/* Magic values of the LITUSE relocation addend. */ ++#define LITUSE_ALPHA_ADDR 0 ++#define LITUSE_ALPHA_BASE 1 ++#define LITUSE_ALPHA_BYTOFF 2 ++#define LITUSE_ALPHA_JSR 3 ++#define LITUSE_ALPHA_TLS_GD 4 ++#define LITUSE_ALPHA_TLS_LDM 5 ++ ++/* Legal values for d_tag of Elf64_Dyn. */ ++#define DT_ALPHA_PLTRO (DT_LOPROC + 0) ++#define DT_ALPHA_NUM 1 ++ ++/* PowerPC specific declarations */ ++ ++/* Values for Elf32/64_Ehdr.e_flags. */ ++#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ ++ ++/* Cygnus local bits below */ ++#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ ++#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib ++ flag */ ++ ++/* PowerPC relocations defined by the ABIs */ ++#define R_PPC_NONE 0 ++#define R_PPC_ADDR32 1 /* 32bit absolute address */ ++#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ ++#define R_PPC_ADDR16 3 /* 16bit absolute address */ ++#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ ++#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ ++#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ ++#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ ++#define R_PPC_ADDR14_BRTAKEN 8 ++#define R_PPC_ADDR14_BRNTAKEN 9 ++#define R_PPC_REL24 10 /* PC relative 26 bit */ ++#define R_PPC_REL14 11 /* PC relative 16 bit */ ++#define R_PPC_REL14_BRTAKEN 12 ++#define R_PPC_REL14_BRNTAKEN 13 ++#define R_PPC_GOT16 14 ++#define R_PPC_GOT16_LO 15 ++#define R_PPC_GOT16_HI 16 ++#define R_PPC_GOT16_HA 17 ++#define R_PPC_PLTREL24 18 ++#define R_PPC_COPY 19 ++#define R_PPC_GLOB_DAT 20 ++#define R_PPC_JMP_SLOT 21 ++#define R_PPC_RELATIVE 22 ++#define R_PPC_LOCAL24PC 23 ++#define R_PPC_UADDR32 24 ++#define R_PPC_UADDR16 25 ++#define R_PPC_REL32 26 ++#define R_PPC_PLT32 27 ++#define R_PPC_PLTREL32 28 ++#define R_PPC_PLT16_LO 29 ++#define R_PPC_PLT16_HI 30 ++#define R_PPC_PLT16_HA 31 ++#define R_PPC_SDAREL16 32 ++#define R_PPC_SECTOFF 33 ++#define R_PPC_SECTOFF_LO 34 ++#define R_PPC_SECTOFF_HI 35 ++#define R_PPC_SECTOFF_HA 36 ++ ++/* PowerPC relocations defined for the TLS access ABI. */ ++#define R_PPC_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ ++#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ ++#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ ++#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ ++#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ ++#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ ++#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ ++#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ ++#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ ++ ++/* Keep this the last entry. */ ++#define R_PPC_NUM 95 ++ ++/* The remaining relocs are from the Embedded ELF ABI, and are not ++ in the SVR4 ELF ABI. */ ++#define R_PPC_EMB_NADDR32 101 ++#define R_PPC_EMB_NADDR16 102 ++#define R_PPC_EMB_NADDR16_LO 103 ++#define R_PPC_EMB_NADDR16_HI 104 ++#define R_PPC_EMB_NADDR16_HA 105 ++#define R_PPC_EMB_SDAI16 106 ++#define R_PPC_EMB_SDA2I16 107 ++#define R_PPC_EMB_SDA2REL 108 ++#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ ++#define R_PPC_EMB_MRKREF 110 ++#define R_PPC_EMB_RELSEC16 111 ++#define R_PPC_EMB_RELST_LO 112 ++#define R_PPC_EMB_RELST_HI 113 ++#define R_PPC_EMB_RELST_HA 114 ++#define R_PPC_EMB_BIT_FLD 115 ++#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ ++ ++/* Diab tool relocations. */ ++#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ ++#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ ++#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ ++#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ ++#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ ++#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ ++ ++/* GNU relocs used in PIC code sequences. */ ++#define R_PPC_REL16 249 /* word32 (sym+add-.) */ ++#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ ++#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ ++#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ ++ ++/* This is a phony reloc to handle any old fashioned TOC16 references ++ that may still be in object files. */ ++#define R_PPC_TOC16 255 ++ ++/* PowerPC specific values for the Dyn d_tag field. */ ++#define DT_PPC_GOT (DT_LOPROC + 0) ++#define DT_PPC_NUM 1 ++ ++/* PowerPC64 relocations defined by the ABIs */ ++#define R_PPC64_NONE R_PPC_NONE ++#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ ++#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ ++#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ ++#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ ++#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ ++#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ ++#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ ++#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN ++#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN ++#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ ++#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ ++#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN ++#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN ++#define R_PPC64_GOT16 R_PPC_GOT16 ++#define R_PPC64_GOT16_LO R_PPC_GOT16_LO ++#define R_PPC64_GOT16_HI R_PPC_GOT16_HI ++#define R_PPC64_GOT16_HA R_PPC_GOT16_HA ++ ++#define R_PPC64_COPY R_PPC_COPY ++#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT ++#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT ++#define R_PPC64_RELATIVE R_PPC_RELATIVE ++ ++#define R_PPC64_UADDR32 R_PPC_UADDR32 ++#define R_PPC64_UADDR16 R_PPC_UADDR16 ++#define R_PPC64_REL32 R_PPC_REL32 ++#define R_PPC64_PLT32 R_PPC_PLT32 ++#define R_PPC64_PLTREL32 R_PPC_PLTREL32 ++#define R_PPC64_PLT16_LO R_PPC_PLT16_LO ++#define R_PPC64_PLT16_HI R_PPC_PLT16_HI ++#define R_PPC64_PLT16_HA R_PPC_PLT16_HA ++ ++#define R_PPC64_SECTOFF R_PPC_SECTOFF ++#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO ++#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI ++#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA ++#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ ++#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ ++#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ ++#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ ++#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ ++#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ ++#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ ++#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ ++#define R_PPC64_PLT64 45 /* doubleword64 L + A */ ++#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ ++#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ ++#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ ++#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ ++#define R_PPC64_TOC 51 /* doubleword64 .TOC */ ++#define R_PPC64_PLTGOT16 52 /* half16* M + A */ ++#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ ++#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ ++#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ ++ ++#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ ++#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ ++#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ ++#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ ++#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ ++#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ ++#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ ++#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ ++#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ ++#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ ++#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ ++ ++/* PowerPC64 relocations defined for the TLS access ABI. */ ++#define R_PPC64_TLS 67 /* none (sym+add)@tls */ ++#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ ++#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ ++#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ ++#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ ++#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ ++#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ ++#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ ++#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ ++#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ ++#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ ++#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ ++#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ ++#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ ++#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ ++#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ ++#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ ++#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ ++#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ ++#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ ++#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ ++#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ ++#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ ++#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ ++#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ ++#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ ++#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ ++#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ ++#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ ++#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ ++#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ ++#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ ++#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ ++#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ ++#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ ++#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ ++ ++/* Keep this the last entry. */ ++#define R_PPC64_NUM 107 ++ ++/* PowerPC64 specific values for the Dyn d_tag field. */ ++#define DT_PPC64_GLINK (DT_LOPROC + 0) ++#define DT_PPC64_OPD (DT_LOPROC + 1) ++#define DT_PPC64_OPDSZ (DT_LOPROC + 2) ++#define DT_PPC64_NUM 3 ++ ++ ++/* ARM specific declarations */ ++ ++/* Processor specific flags for the ELF header e_flags field. */ ++#define EF_ARM_RELEXEC 0x01 ++#define EF_ARM_HASENTRY 0x02 ++#define EF_ARM_INTERWORK 0x04 ++#define EF_ARM_APCS_26 0x08 ++#define EF_ARM_APCS_FLOAT 0x10 ++#define EF_ARM_PIC 0x20 ++#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ ++#define EF_ARM_NEW_ABI 0x80 ++#define EF_ARM_OLD_ABI 0x100 ++ ++/* Other constants defined in the ARM ELF spec. version B-01. */ ++/* NB. These conflict with values defined above. */ ++#define EF_ARM_SYMSARESORTED 0x04 ++#define EF_ARM_DYNSYMSUSESEGIDX 0x08 ++#define EF_ARM_MAPSYMSFIRST 0x10 ++#define EF_ARM_EABIMASK 0XFF000000 ++ ++#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) ++#define EF_ARM_EABI_UNKNOWN 0x00000000 ++#define EF_ARM_EABI_VER1 0x01000000 ++#define EF_ARM_EABI_VER2 0x02000000 ++ ++/* Additional symbol types for Thumb */ ++#define STT_ARM_TFUNC 0xd ++ ++/* ARM-specific values for sh_flags */ ++#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ ++#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined ++ in the input to a link step */ ++ ++/* ARM-specific program header flags */ ++#define PF_ARM_SB 0x10000000 /* Segment contains the location ++ addressed by the static base */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ ++ ++/* ARM relocs. */ ++ ++#define R_ARM_NONE 0 /* No reloc */ ++#define R_ARM_PC24 1 /* PC relative 26 bit branch */ ++#define R_ARM_ABS32 2 /* Direct 32 bit */ ++#define R_ARM_REL32 3 /* PC relative 32 bit */ ++#define R_ARM_PC13 4 ++#define R_ARM_ABS16 5 /* Direct 16 bit */ ++#define R_ARM_ABS12 6 /* Direct 12 bit */ ++#define R_ARM_THM_ABS5 7 ++#define R_ARM_ABS8 8 /* Direct 8 bit */ ++#define R_ARM_SBREL32 9 ++#define R_ARM_THM_PC22 10 ++#define R_ARM_THM_PC8 11 ++#define R_ARM_AMP_VCALL9 12 ++#define R_ARM_SWI24 13 ++#define R_ARM_THM_SWI8 14 ++#define R_ARM_XPC25 15 ++#define R_ARM_THM_XPC22 16 ++#define R_ARM_TLS_DTPMOD32 17 ++#define R_ARM_TLS_DTPOFF32 18 ++#define R_ARM_TLS_TPOFF32 19 ++#define R_ARM_COPY 20 /* Copy symbol at runtime */ ++#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ ++#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ ++#define R_ARM_RELATIVE 23 /* Adjust by program base */ ++#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ ++#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ ++#define R_ARM_GOT32 26 /* 32 bit GOT entry */ ++#define R_ARM_PLT32 27 /* 32 bit PLT address */ ++#define R_ARM_ALU_PCREL_7_0 32 ++#define R_ARM_ALU_PCREL_15_8 33 ++#define R_ARM_ALU_PCREL_23_15 34 ++#define R_ARM_LDR_SBREL_11_0 35 ++#define R_ARM_ALU_SBREL_19_12 36 ++#define R_ARM_ALU_SBREL_27_20 37 ++#define R_ARM_GNU_VTENTRY 100 ++#define R_ARM_GNU_VTINHERIT 101 ++#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ ++#define R_ARM_THM_PC9 103 /* thumb conditional branch */ ++#define R_ARM_TLS_GD32 104 ++#define R_ARM_TLS_LDM32 105 ++#define R_ARM_TLS_LDO32 106 ++#define R_ARM_TLS_IE32 107 ++#define R_ARM_TLS_LE32 108 ++#define R_ARM_TLS_LDO12 109 ++#define R_ARM_TLS_LE12 110 ++#define R_ARM_TLS_IE12GP 111 ++#define R_ARM_RXPC25 249 ++#define R_ARM_RSBREL32 250 ++#define R_ARM_THM_RPC22 251 ++#define R_ARM_RREL32 252 ++#define R_ARM_RABS22 253 ++#define R_ARM_RPC24 254 ++#define R_ARM_RBASE 255 ++/* Keep this the last entry. */ ++#define R_ARM_NUM 256 ++ ++/* IA-64 specific declarations. */ ++ ++/* Processor specific flags for the Ehdr e_flags field. */ ++#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ ++#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ ++#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ ++ ++/* Processor specific values for the Phdr p_type field. */ ++#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ ++#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ ++#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) ++#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) ++#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) ++ ++/* Processor specific flags for the Phdr p_flags field. */ ++#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Shdr sh_type field. */ ++#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ ++#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ ++ ++/* Processor specific flags for the Shdr sh_flags field. */ ++#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ ++#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ ++ ++/* Processor specific values for the Dyn d_tag field. */ ++#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) ++#define DT_IA_64_NUM 1 ++ ++/* IA-64 relocations. */ ++#define R_IA64_NONE 0x00 /* none */ ++#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ ++#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ ++#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ ++#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ ++#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ ++#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ ++#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ ++#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ ++#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ ++#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ ++#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ ++#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ ++#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ ++#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ ++#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ ++#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ ++#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ ++#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ ++#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ ++#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ ++#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ ++#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ ++#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ ++#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ ++#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ ++#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ ++#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ ++#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ ++#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ ++#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ ++#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ ++#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ ++#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ ++#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ ++#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ ++#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ ++#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ ++#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ ++#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ ++#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ ++#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ ++#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ ++#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ ++#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ ++#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ ++#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ ++#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ ++#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ ++#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ ++#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ ++#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ ++#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ ++#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ ++#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ ++#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ ++#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ ++#define R_IA64_COPY 0x84 /* copy relocation */ ++#define R_IA64_SUB 0x85 /* Addend and symbol difference */ ++#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ ++#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ ++#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ ++#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ ++#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ ++#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ ++#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ ++#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ ++#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ ++#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ ++#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ ++#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ ++#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ ++#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ ++#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ ++#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ ++#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ ++ ++/* SH specific declarations */ ++ ++/* SH specific values for `st_other'. */ ++ ++/* If set, this is a symbol pointing to SHmedia code, which will be branched ++ to, so need to add 1 to the symbol value. */ ++#define STO_SH5_ISA32 (1 << 2) ++ ++/* SH relocs. */ ++#define R_SH_NONE 0 ++#define R_SH_DIR32 1 ++#define R_SH_REL32 2 ++#define R_SH_DIR8WPN 3 ++#define R_SH_IND12W 4 ++#define R_SH_DIR8WPL 5 ++#define R_SH_DIR8WPZ 6 ++#define R_SH_DIR8BP 7 ++#define R_SH_DIR8W 8 ++#define R_SH_DIR8L 9 ++#define R_SH_SWITCH16 25 ++#define R_SH_SWITCH32 26 ++#define R_SH_USES 27 ++#define R_SH_COUNT 28 ++#define R_SH_ALIGN 29 ++#define R_SH_CODE 30 ++#define R_SH_DATA 31 ++#define R_SH_LABEL 32 ++#define R_SH_SWITCH8 33 ++#define R_SH_GNU_VTINHERIT 34 ++#define R_SH_GNU_VTENTRY 35 ++#define R_SH_TLS_GD_32 144 ++#define R_SH_TLS_LD_32 145 ++#define R_SH_TLS_LDO_32 146 ++#define R_SH_TLS_IE_32 147 ++#define R_SH_TLS_LE_32 148 ++#define R_SH_TLS_DTPMOD32 149 ++#define R_SH_TLS_DTPOFF32 150 ++#define R_SH_TLS_TPOFF32 151 ++#define R_SH_GOT32 160 ++#define R_SH_PLT32 161 ++#define R_SH_COPY 162 ++#define R_SH_GLOB_DAT 163 ++#define R_SH_JMP_SLOT 164 ++#define R_SH_RELATIVE 165 ++#define R_SH_GOTOFF 166 ++#define R_SH_GOTPC 167 ++#define R_SH_RELATIVE_LOW16 197 ++#define R_SH_RELATIVE_MEDLOW16 198 ++#define R_SH_IMM_LOW16 246 ++#define R_SH_IMM_LOW16_PCREL 247 ++#define R_SH_IMM_MEDLOW16 248 ++#define R_SH_IMM_MEDLOW16_PCREL 249 ++ ++/* Keep this the last entry. */ ++#define R_SH_NUM 256 ++ ++/* Additional s390 relocs */ ++ ++#define R_390_NONE 0 /* No reloc. */ ++#define R_390_8 1 /* Direct 8 bit. */ ++#define R_390_12 2 /* Direct 12 bit. */ ++#define R_390_16 3 /* Direct 16 bit. */ ++#define R_390_32 4 /* Direct 32 bit. */ ++#define R_390_PC32 5 /* PC relative 32 bit. */ ++#define R_390_GOT12 6 /* 12 bit GOT offset. */ ++#define R_390_GOT32 7 /* 32 bit GOT offset. */ ++#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ ++#define R_390_COPY 9 /* Copy symbol at runtime. */ ++#define R_390_GLOB_DAT 10 /* Create GOT entry. */ ++#define R_390_JMP_SLOT 11 /* Create PLT entry. */ ++#define R_390_RELATIVE 12 /* Adjust by program base. */ ++#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ ++#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ ++#define R_390_GOT16 15 /* 16 bit GOT offset. */ ++#define R_390_PC16 16 /* PC relative 16 bit. */ ++#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ ++#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ ++#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ ++#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ ++#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ ++#define R_390_64 22 /* Direct 64 bit. */ ++#define R_390_PC64 23 /* PC relative 64 bit. */ ++#define R_390_GOT64 24 /* 64 bit GOT offset. */ ++#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ ++#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ ++#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ ++#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ ++#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ ++#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ ++#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ ++#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ ++#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ ++#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ ++#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ ++#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ ++#define R_390_TLS_GDCALL 38 /* Tag for function call in general ++ dynamic TLS code. */ ++#define R_390_TLS_LDCALL 39 /* Tag for function call in local ++ dynamic TLS code. */ ++#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic ++ thread local data. */ ++#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS ++ block offset. */ ++#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic ++ thread local data in LE code. */ ++#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for ++ negated static TLS block offset. */ ++#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to ++ static TLS block. */ ++#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS ++ block. */ ++#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ ++#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ ++#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS ++ block. */ ++#define R_390_20 57 /* Direct 20 bit. */ ++#define R_390_GOT20 58 /* 20 bit GOT offset. */ ++#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ ++#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS ++ block offset. */ ++/* Keep this the last entry. */ ++#define R_390_NUM 61 ++ ++ ++/* CRIS flags. */ ++#define EF_CRIS_VARIANT_MASK 0x0000000e ++#define EF_CRIS_VARIANT_ANY_V0_V10 0x00000000 ++#define EF_CRIS_VARIANT_V32 0x00000002 ++#define EF_CRIS_VARIANT_COMMON_V10_V32 0x00000004 ++ ++/* CRIS relocations. */ ++#define R_CRIS_NONE 0 ++#define R_CRIS_8 1 ++#define R_CRIS_16 2 ++#define R_CRIS_32 3 ++#define R_CRIS_8_PCREL 4 ++#define R_CRIS_16_PCREL 5 ++#define R_CRIS_32_PCREL 6 ++#define R_CRIS_GNU_VTINHERIT 7 ++#define R_CRIS_GNU_VTENTRY 8 ++#define R_CRIS_COPY 9 ++#define R_CRIS_GLOB_DAT 10 ++#define R_CRIS_JUMP_SLOT 11 ++#define R_CRIS_RELATIVE 12 ++#define R_CRIS_16_GOT 13 ++#define R_CRIS_32_GOT 14 ++#define R_CRIS_16_GOTPLT 15 ++#define R_CRIS_32_GOTPLT 16 ++#define R_CRIS_32_GOTREL 17 ++#define R_CRIS_32_PLT_GOTREL 18 ++#define R_CRIS_32_PLT_PCREL 19 ++ ++/* Keep this the last entry. */ ++#define R_CRIS_NUM 20 ++ ++ ++/* AMD x86-64 relocations. */ ++#define R_X86_64_NONE 0 /* No reloc */ ++#define R_X86_64_64 1 /* Direct 64 bit */ ++#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ ++#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ ++#define R_X86_64_PLT32 4 /* 32 bit PLT address */ ++#define R_X86_64_COPY 5 /* Copy symbol at runtime */ ++#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ ++#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ ++#define R_X86_64_RELATIVE 8 /* Adjust by program base */ ++#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative ++ offset to GOT */ ++#define R_X86_64_32 10 /* Direct 32 bit zero extended */ ++#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ ++#define R_X86_64_16 12 /* Direct 16 bit zero extended */ ++#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ ++#define R_X86_64_8 14 /* Direct 8 bit sign extended */ ++#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ ++#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ ++#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ ++#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ ++#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset ++ to two GOT entries for GD symbol */ ++#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset ++ to two GOT entries for LD symbol */ ++#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ ++#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset ++ to GOT entry for IE symbol */ ++#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ ++ ++#define R_X86_64_NUM 24 ++ ++ ++/* AM33 relocations. */ ++#define R_MN10300_NONE 0 /* No reloc. */ ++#define R_MN10300_32 1 /* Direct 32 bit. */ ++#define R_MN10300_16 2 /* Direct 16 bit. */ ++#define R_MN10300_8 3 /* Direct 8 bit. */ ++#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ ++#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ ++#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ ++#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ ++#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ ++#define R_MN10300_24 9 /* Direct 24 bit. */ ++#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ ++#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ ++#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ ++#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ ++#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ ++#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ ++#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ ++#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ ++#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ ++#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ ++#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ ++#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ ++#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ ++#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ ++ ++#define R_MN10300_NUM 24 ++ ++ ++/* M32R relocs. */ ++#define R_M32R_NONE 0 /* No reloc. */ ++#define R_M32R_16 1 /* Direct 16 bit. */ ++#define R_M32R_32 2 /* Direct 32 bit. */ ++#define R_M32R_24 3 /* Direct 24 bit. */ ++#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ ++#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ ++#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ ++#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ ++#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ ++#define R_M32R_LO16 9 /* Low 16 bit. */ ++#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ ++#define R_M32R_GNU_VTINHERIT 11 ++#define R_M32R_GNU_VTENTRY 12 ++/* M32R relocs use SHT_RELA. */ ++#define R_M32R_16_RELA 33 /* Direct 16 bit. */ ++#define R_M32R_32_RELA 34 /* Direct 32 bit. */ ++#define R_M32R_24_RELA 35 /* Direct 24 bit. */ ++#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ ++#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ ++#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ ++#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ ++#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ ++#define R_M32R_LO16_RELA 41 /* Low 16 bit */ ++#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ ++#define R_M32R_RELA_GNU_VTINHERIT 43 ++#define R_M32R_RELA_GNU_VTENTRY 44 ++ ++#define R_M32R_GOT24 48 /* 24 bit GOT entry */ ++#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ ++#define R_M32R_COPY 50 /* Copy symbol at runtime */ ++#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ ++#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ ++#define R_M32R_RELATIVE 53 /* Adjust by program base */ ++#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ ++#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ ++#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned ++ low */ ++#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed ++ low */ ++#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ ++#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to ++ GOT with unsigned low */ ++#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to ++ GOT with signed low */ ++#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to ++ GOT */ ++#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT ++ with unsigned low */ ++#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT ++ with signed low */ ++#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ ++#define R_M32R_NUM 256 /* Keep this the last entry. */ ++ ++/* i960 Relocations */ ++#define R_960_NONE 0 ++#define R_960_12 1 ++#define R_960_32 2 ++#define R_960_IP24 3 ++#define R_960_SUB 4 ++#define R_960_OPTCALL 5 ++#define R_960_OPTCALLX 6 ++#define R_960_OPTCALLXA 7 ++/* Keep this the last entry. */ ++#define R_960_NUM 8 ++ ++ ++/* v850 relocations. */ ++#define R_V850_NONE 0 ++#define R_V850_9_PCREL 1 ++#define R_V850_22_PCREL 2 ++#define R_V850_HI16_S 3 ++#define R_V850_HI16 4 ++#define R_V850_LO16 5 ++#define R_V850_32 6 ++#define R_V850_16 7 ++#define R_V850_8 8 ++#define R_V850_SDA_16_16_OFFSET 9 /* For ld.b, st.b, set1, clr1, ++ not1, tst1, movea, movhi */ ++#define R_V850_SDA_15_16_OFFSET 10 /* For ld.w, ld.h, ld.hu, st.w, st.h */ ++#define R_V850_ZDA_16_16_OFFSET 11 /* For ld.b, st.b, set1, clr1, ++ not1, tst1, movea, movhi */ ++#define R_V850_ZDA_15_16_OFFSET 12 /* For ld.w, ld.h, ld.hu, st.w, st.h */ ++#define R_V850_TDA_6_8_OFFSET 13 /* For sst.w, sld.w */ ++#define R_V850_TDA_7_8_OFFSET 14 /* For sst.h, sld.h */ ++#define R_V850_TDA_7_7_OFFSET 15 /* For sst.b, sld.b */ ++#define R_V850_TDA_16_16_OFFSET 16 /* For set1, clr1, not1, tst1, ++ movea, movhi */ ++/* CYGNUS LOCAL v850e */ ++#define R_V850_TDA_4_5_OFFSET 17 /* For sld.hu */ ++#define R_V850_TDA_4_4_OFFSET 18 /* For sld.bu */ ++#define R_V850_SDA_16_16_SPLIT_OFFSET 19 /* For ld.bu */ ++#define R_V850_ZDA_16_16_SPLIT_OFFSET 20 /* For ld.bu */ ++#define R_V850_CALLT_6_7_OFFSET 21 /* For callt */ ++#define R_V850_CALLT_16_16_OFFSET 22 /* For callt */ ++/* END CYGNUS LOCAL */ ++#define R_V850_GNU_VTINHERIT 23 ++#define R_V850_GNU_VTENTRY 24 ++/* Keep this the last entry. */ ++#define R_V850_NUM 25 ++ ++/* Atmel AVR32 relocations. */ ++#define R_AVR32_NONE 0 ++#define R_AVR32_32 1 ++#define R_AVR32_16 2 ++#define R_AVR32_8 3 ++#define R_AVR32_32_PCREL 4 ++#define R_AVR32_16_PCREL 5 ++#define R_AVR32_8_PCREL 6 ++#define R_AVR32_DIFF32 7 ++#define R_AVR32_DIFF16 8 ++#define R_AVR32_DIFF8 9 ++#define R_AVR32_GOT32 10 ++#define R_AVR32_GOT16 11 ++#define R_AVR32_GOT8 12 ++#define R_AVR32_21S 13 ++#define R_AVR32_16U 14 ++#define R_AVR32_16S 15 ++#define R_AVR32_8S 16 ++#define R_AVR32_8S_EXT 17 ++#define R_AVR32_22H_PCREL 18 ++#define R_AVR32_18W_PCREL 19 ++#define R_AVR32_16B_PCREL 20 ++#define R_AVR32_16N_PCREL 21 ++#define R_AVR32_14UW_PCREL 22 ++#define R_AVR32_11H_PCREL 23 ++#define R_AVR32_10UW_PCREL 24 ++#define R_AVR32_9H_PCREL 25 ++#define R_AVR32_9UW_PCREL 26 ++#define R_AVR32_HI16 27 ++#define R_AVR32_LO16 28 ++#define R_AVR32_GOTPC 29 ++#define R_AVR32_GOTCALL 30 ++#define R_AVR32_LDA_GOT 31 ++#define R_AVR32_GOT21S 32 ++#define R_AVR32_GOT18SW 33 ++#define R_AVR32_GOT16S 34 ++#define R_AVR32_GOT7UW 35 ++#define R_AVR32_32_CPENT 36 ++#define R_AVR32_CPCALL 37 ++#define R_AVR32_16_CP 38 ++#define R_AVR32_9W_CP 39 ++#define R_AVR32_RELATIVE 40 ++#define R_AVR32_GLOB_DAT 41 ++#define R_AVR32_JMP_SLOT 42 ++#define R_AVR32_ALIGN 43 ++#define R_AVR32_NUM 44 ++ ++/* AVR32 dynamic tags */ ++#define DT_AVR32_GOTSZ 0x70000001 /* Total size of GOT in bytes */ ++ ++/* Renesas H8/300 Relocations */ ++#define R_H8_NONE 0 ++#define R_H8_DIR32 1 ++#define R_H8_DIR32_28 2 ++#define R_H8_DIR32_24 3 ++#define R_H8_DIR32_16 4 ++#define R_H8_DIR32U 6 ++#define R_H8_DIR32U_28 7 ++#define R_H8_DIR32U_24 8 ++#define R_H8_DIR32U_20 9 ++#define R_H8_DIR32U_16 10 ++#define R_H8_DIR24 11 ++#define R_H8_DIR24_20 12 ++#define R_H8_DIR24_16 13 ++#define R_H8_DIR24U 14 ++#define R_H8_DIR24U_20 15 ++#define R_H8_DIR24U_16 16 ++#define R_H8_DIR16 17 ++#define R_H8_DIR16U 18 ++#define R_H8_DIR16S_32 19 ++#define R_H8_DIR16S_28 20 ++#define R_H8_DIR16S_24 21 ++#define R_H8_DIR16S_20 22 ++#define R_H8_DIR16S 23 ++#define R_H8_DIR8 24 ++#define R_H8_DIR8U 25 ++#define R_H8_DIR8Z_32 26 ++#define R_H8_DIR8Z_28 27 ++#define R_H8_DIR8Z_24 28 ++#define R_H8_DIR8Z_20 29 ++#define R_H8_DIR8Z_16 30 ++#define R_H8_PCREL16 31 ++#define R_H8_PCREL8 32 ++#define R_H8_BPOS 33 ++#define R_H8_PCREL32 34 ++#define R_H8_GOT32O 35 ++#define R_H8_GOT16O 36 ++#define R_H8_DIR16A8 59 ++#define R_H8_DIR16R8 60 ++#define R_H8_DIR24A8 61 ++#define R_H8_DIR24R8 62 ++#define R_H8_DIR32A16 63 ++#define R_H8_ABS32 65 ++#define R_H8_ABS32A16 127 ++#define R_H8_NUM 128 ++ ++/* NIOS relocations. */ ++#define R_NIOS_NONE 0 ++#define R_NIOS_32 1 /* A 32 bit absolute relocation.*/ ++#define R_NIOS_LO16_LO5 2 /* A LO-16 5 bit absolute relocation. */ ++#define R_NIOS_LO16_HI11 3 /* A LO-16 top 11 bit absolute relocation. */ ++#define R_NIOS_HI16_LO5 4 /* A HI-16 5 bit absolute relocation. */ ++#define R_NIOS_HI16_HI11 5 /* A HI-16 top 11 bit absolute relocation. */ ++#define R_NIOS_PCREL6 6 /* A 6 bit relative relocation. */ ++#define R_NIOS_PCREL8 7 /* An 8 bit relative relocation. */ ++#define R_NIOS_PCREL11 8 /* An 11 bit relative relocation. */ ++#define R_NIOS_16 9 /* A 16 bit absolute relocation. */ ++#define R_NIOS_H_LO5 10 /* Low 5-bits of absolute relocation in halfwords. */ ++#define R_NIOS_H_HI11 11 /* Top 11 bits of 16-bit absolute relocation in halfwords. */ ++#define R_NIOS_H_XLO5 12 /* Low 5 bits of top 16-bits of 32-bit absolute relocation in halfwords. */ ++#define R_NIOS_H_XHI11 13 /* Top 11 bits of top 16-bits of 32-bit absolute relocation in halfwords. */ ++#define R_NIOS_H_16 14 /* Half-word @h value */ ++#define R_NIOS_H_32 15 /* Word @h value */ ++#define R_NIOS_GNU_VTINHERIT 200 /* GNU extension to record C++ vtable hierarchy */ ++#define R_NIOS_GNU_VTENTRY 201 /* GNU extension to record C++ vtable member usage */ ++/* Keep this the last entry. */ ++#define R_NIOS_NUM 202 ++ ++/* NIOS II relocations */ ++#define R_NIOS2_NONE 0 ++#define R_NIOS2_S16 1 ++#define R_NIOS2_U16 2 ++#define R_NIOS2_PCREL16 3 ++#define R_NIOS2_CALL26 4 ++#define R_NIOS2_IMM5 5 ++#define R_NIOS2_CACHE_OPX 6 ++#define R_NIOS2_IMM6 7 ++#define R_NIOS2_IMM8 8 ++#define R_NIOS2_HI16 9 ++#define R_NIOS2_LO16 10 ++#define R_NIOS2_HIADJ16 11 ++#define R_NIOS2_BFD_RELOC_32 12 ++#define R_NIOS2_BFD_RELOC_16 13 ++#define R_NIOS2_BFD_RELOC_8 14 ++#define R_NIOS2_GPREL 15 ++#define R_NIOS2_GNU_VTINHERIT 16 ++#define R_NIOS2_GNU_VTENTRY 17 ++#define R_NIOS2_UJMP 18 ++#define R_NIOS2_CJMP 19 ++#define R_NIOS2_CALLR 20 ++#define R_NIOS2_ALIGN 21 ++/* Keep this the last entry. */ ++#define R_NIOS2_NUM 22 ++ ++/* Xtensa-specific declarations */ ++ ++/* Xtensa values for the Dyn d_tag field. */ ++#define DT_XTENSA_GOT_LOC_OFF (DT_LOPROC + 0) ++#define DT_XTENSA_GOT_LOC_SZ (DT_LOPROC + 1) ++#define DT_XTENSA_NUM 2 ++ ++/* Xtensa relocations. */ ++#define R_XTENSA_NONE 0 ++#define R_XTENSA_32 1 ++#define R_XTENSA_RTLD 2 ++#define R_XTENSA_GLOB_DAT 3 ++#define R_XTENSA_JMP_SLOT 4 ++#define R_XTENSA_RELATIVE 5 ++#define R_XTENSA_PLT 6 ++#define R_XTENSA_OP0 8 ++#define R_XTENSA_OP1 9 ++#define R_XTENSA_OP2 10 ++#define R_XTENSA_ASM_EXPAND 11 ++#define R_XTENSA_ASM_SIMPLIFY 12 ++#define R_XTENSA_GNU_VTINHERIT 15 ++#define R_XTENSA_GNU_VTENTRY 16 ++#define R_XTENSA_DIFF8 17 ++#define R_XTENSA_DIFF16 18 ++#define R_XTENSA_DIFF32 19 ++#define R_XTENSA_SLOT0_OP 20 ++#define R_XTENSA_SLOT1_OP 21 ++#define R_XTENSA_SLOT2_OP 22 ++#define R_XTENSA_SLOT3_OP 23 ++#define R_XTENSA_SLOT4_OP 24 ++#define R_XTENSA_SLOT5_OP 25 ++#define R_XTENSA_SLOT6_OP 26 ++#define R_XTENSA_SLOT7_OP 27 ++#define R_XTENSA_SLOT8_OP 28 ++#define R_XTENSA_SLOT9_OP 29 ++#define R_XTENSA_SLOT10_OP 30 ++#define R_XTENSA_SLOT11_OP 31 ++#define R_XTENSA_SLOT12_OP 32 ++#define R_XTENSA_SLOT13_OP 33 ++#define R_XTENSA_SLOT14_OP 34 ++#define R_XTENSA_SLOT0_ALT 35 ++#define R_XTENSA_SLOT1_ALT 36 ++#define R_XTENSA_SLOT2_ALT 37 ++#define R_XTENSA_SLOT3_ALT 38 ++#define R_XTENSA_SLOT4_ALT 39 ++#define R_XTENSA_SLOT5_ALT 40 ++#define R_XTENSA_SLOT6_ALT 41 ++#define R_XTENSA_SLOT7_ALT 42 ++#define R_XTENSA_SLOT8_ALT 43 ++#define R_XTENSA_SLOT9_ALT 44 ++#define R_XTENSA_SLOT10_ALT 45 ++#define R_XTENSA_SLOT11_ALT 46 ++#define R_XTENSA_SLOT12_ALT 47 ++#define R_XTENSA_SLOT13_ALT 48 ++#define R_XTENSA_SLOT14_ALT 49 ++/* Keep this the last entry. */ ++#define R_XTENSA_NUM 50 ++ ++/* C6X specific relocs */ ++#define R_C6000_NONE 0 ++#define R_C6000_ABS32 1 ++#define R_C6000_ABS16 2 ++#define R_C6000_ABS8 3 ++#define R_C6000_PCR_S21 4 ++#define R_C6000_PCR_S12 5 ++#define R_C6000_PCR_S10 6 ++#define R_C6000_PCR_S7 7 ++#define R_C6000_ABS_S16 8 ++#define R_C6000_ABS_L16 9 ++#define R_C6000_ABS_H16 10 ++#define R_C6000_SBR_U15_B 11 ++#define R_C6000_SBR_U15_H 12 ++#define R_C6000_SBR_U15_W 13 ++#define R_C6000_SBR_S16 14 ++#define R_C6000_SBR_L16_B 15 ++#define R_C6000_SBR_L16_H 16 ++#define R_C6000_SBR_L16_W 17 ++#define R_C6000_SBR_H16_B 18 ++#define R_C6000_SBR_H16_H 19 ++#define R_C6000_SBR_H16_W 20 ++#define R_C6000_SBR_GOT_U15_W 21 ++#define R_C6000_SBR_GOT_L16_W 22 ++#define R_C6000_SBR_GOT_H16_W 23 ++#define R_C6000_DSBT_INDEX 24 ++#define R_C6000_PREL31 25 ++#define R_C6000_COPY 26 ++#define R_C6000_JUMP_SLOT 27 ++#define R_C6000_SBR_GOT32 28 ++#define R_C6000_PCR_H16 29 ++#define R_C6000_PCR_L16 30 ++#define R_C6000_ALIGN 253 ++#define R_C6000_FPHEAD 254 ++#define R_C6000_NOCMP 255 ++ ++/* C6x specific values for the Dyn d_tag field. */ ++#define DT_C6000_DSBT_BASE (DT_LOPROC + 0) ++#define DT_C6000_DSBT_SIZE (DT_LOPROC + 1) ++#define DT_C6000_PREEMPTMAP (DT_LOPROC + 2) ++#define DT_C6000_DSBT_INDEX (DT_LOPROC + 3) ++ ++#define DT_C6000_NUM 4 + + static void die(char *fmt, ...); + diff --git a/target/linux/patches/3.4.108/sparc-aout.patch b/target/linux/patches/3.4.108/sparc-aout.patch new file mode 100644 index 000000000..5cd74c2e7 --- /dev/null +++ b/target/linux/patches/3.4.108/sparc-aout.patch @@ -0,0 +1,24 @@ +diff -Nur linux-3.4.103.orig/arch/sparc/boot/Makefile linux-3.4.103/arch/sparc/boot/Makefile +--- linux-3.4.103.orig/arch/sparc/boot/Makefile 2014-08-13 20:07:43.000000000 -0500 ++++ linux-3.4.103/arch/sparc/boot/Makefile 2015-02-16 00:26:16.934618259 -0600 +@@ -74,9 +74,6 @@ + $(call if_changed,gzip) + @echo ' kernel: $@ is ready' + +-$(obj)/vmlinux.aout: vmlinux FORCE +- $(call if_changed,elftoaout) +- @echo ' kernel: $@ is ready' + else + + # The following lines make a readable image for U-Boot. +@@ -107,6 +104,10 @@ + + endif + ++$(obj)/vmlinux.aout: $(obj)/image FORCE ++ $(call if_changed,elftoaout) ++ @echo ' kernel: $@ is ready' ++ + $(obj)/tftpboot.img: $(obj)/image $(obj)/piggyback System.map $(ROOT_IMG) FORCE + $(call if_changed,elftoaout) + $(call if_changed,piggy) diff --git a/target/linux/patches/3.4.108/sparc-include.patch b/target/linux/patches/3.4.108/sparc-include.patch new file mode 100644 index 000000000..2f8ffd061 --- /dev/null +++ b/target/linux/patches/3.4.108/sparc-include.patch @@ -0,0 +1,11 @@ +diff -Nur linux-2.6.39-rc7.orig/arch/sparc/boot/btfixupprep.c linux-2.6.39-rc7/arch/sparc/boot/btfixupprep.c +--- linux-2.6.39-rc7.orig/arch/sparc/boot/btfixupprep.c 2011-05-10 04:33:54.000000000 +0200 ++++ linux-2.6.39-rc7/arch/sparc/boot/btfixupprep.c 2011-05-21 13:34:40.000000000 +0200 +@@ -25,7 +25,6 @@ + #include + #include + #include +-#include + + #define MAXSYMS 1024 + diff --git a/target/linux/patches/3.4.108/startup.patch b/target/linux/patches/3.4.108/startup.patch new file mode 100644 index 000000000..c26430bcb --- /dev/null +++ b/target/linux/patches/3.4.108/startup.patch @@ -0,0 +1,34 @@ +diff -Nur linux-3.4.82.orig/init/initramfs.c linux-3.4.82/init/initramfs.c +--- linux-3.4.82.orig/init/initramfs.c 2014-02-22 19:33:35.000000000 +0100 ++++ linux-3.4.82/init/initramfs.c 2014-03-15 18:46:22.674928245 +0100 +@@ -606,6 +606,9 @@ + free_initrd(); + #endif + } ++#ifdef CONFIG_DEVTMPFS_MOUNT ++ devtmpfs_mount("dev"); ++#endif + return 0; + } + rootfs_initcall(populate_rootfs); +diff -Nur linux-3.4.82.orig/init/main.c linux-3.4.82/init/main.c +--- linux-3.4.82.orig/init/main.c 2014-02-22 19:33:35.000000000 +0100 ++++ linux-3.4.82/init/main.c 2014-03-15 18:46:22.674928245 +0100 +@@ -873,6 +873,8 @@ + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + printk(KERN_WARNING "Warning: unable to open an initial console.\n"); + ++ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); ++ + (void) sys_dup(0); + (void) sys_dup(0); + /* +@@ -881,7 +883,7 @@ + */ + + if (!ramdisk_execute_command) +- ramdisk_execute_command = "/init"; ++ ramdisk_execute_command = "/sbin/init"; + + if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { + ramdisk_execute_command = NULL; diff --git a/target/linux/patches/3.4.108/usb-defaults-off.patch b/target/linux/patches/3.4.108/usb-defaults-off.patch new file mode 100644 index 000000000..31367108a --- /dev/null +++ b/target/linux/patches/3.4.108/usb-defaults-off.patch @@ -0,0 +1,32 @@ +diff -Nur linux-2.6.37.orig//drivers/usb/core/Kconfig linux-2.6.37/drivers/usb/core/Kconfig +--- linux-2.6.37.orig//drivers/usb/core/Kconfig 2011-01-05 01:50:19.000000000 +0100 ++++ linux-2.6.37/drivers/usb/core/Kconfig 2011-04-12 19:04:23.000000000 +0200 +@@ -59,7 +59,7 @@ + config USB_DEVICE_CLASS + bool "USB device class-devices (DEPRECATED)" + depends on USB +- default y ++ default n + ---help--- + Userspace access to USB devices is granted by device-nodes exported + directly from the usbdev in sysfs. Old versions of the driver +diff -Nur linux-2.6.37.orig//drivers/usb/host/Kconfig linux-2.6.37/drivers/usb/host/Kconfig +--- linux-2.6.37.orig//drivers/usb/host/Kconfig 2011-01-05 01:50:19.000000000 +0100 ++++ linux-2.6.37/drivers/usb/host/Kconfig 2011-04-12 19:04:48.000000000 +0200 +@@ -62,6 +62,7 @@ + config USB_EHCI_ROOT_HUB_TT + bool "Root Hub Transaction Translators" + depends on USB_EHCI_HCD ++ default n + ---help--- + Some EHCI chips have vendor-specific extensions to integrate + transaction translators, so that no OHCI or UHCI companion +@@ -74,7 +75,7 @@ + config USB_EHCI_TT_NEWSCHED + bool "Improved Transaction Translator scheduling" + depends on USB_EHCI_HCD +- default y ++ default n + ---help--- + This changes the periodic scheduling code to fill more of the low + and full speed bandwidth available from the Transaction Translator diff --git a/target/linux/patches/3.4.108/vga-cons-default-off.patch b/target/linux/patches/3.4.108/vga-cons-default-off.patch new file mode 100644 index 000000000..178aeeeb9 --- /dev/null +++ b/target/linux/patches/3.4.108/vga-cons-default-off.patch @@ -0,0 +1,12 @@ +diff -Nur linux-2.6.37.orig//drivers/video/console/Kconfig linux-2.6.37/drivers/video/console/Kconfig +--- linux-2.6.37.orig//drivers/video/console/Kconfig 2011-01-05 01:50:19.000000000 +0100 ++++ linux-2.6.37/drivers/video/console/Kconfig 2011-04-12 16:29:34.000000000 +0200 +@@ -7,7 +7,7 @@ + config VGA_CONSOLE + bool "VGA text console" if EMBEDDED || !X86 + depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) +- default y ++ default n + help + Saying Y here will allow you to use Linux in text mode through a + display that complies with the generic VGA standard. Virtually diff --git a/target/linux/patches/3.4.108/wlan-cf.patch b/target/linux/patches/3.4.108/wlan-cf.patch new file mode 100644 index 000000000..fc20759e2 --- /dev/null +++ b/target/linux/patches/3.4.108/wlan-cf.patch @@ -0,0 +1,11 @@ +diff -Nur linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c +--- linux-2.6.39.orig/drivers/net/wireless/hostap/hostap_cs.c 2011-05-19 06:06:34.000000000 +0200 ++++ linux-2.6.39/drivers/net/wireless/hostap/hostap_cs.c 2011-09-12 02:46:26.987984145 +0200 +@@ -623,6 +623,7 @@ + static struct pcmcia_device_id hostap_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), ++ PCMCIA_DEVICE_MANF_CARD(0x0004, 0x2003), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), + PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), + PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), diff --git a/target/linux/patches/3.4.108/yaffs2.patch b/target/linux/patches/3.4.108/yaffs2.patch new file mode 100644 index 000000000..44c95915f --- /dev/null +++ b/target/linux/patches/3.4.108/yaffs2.patch @@ -0,0 +1,16550 @@ +diff -Nur linux-3.4.90.orig/fs/Kconfig linux-3.4.90/fs/Kconfig +--- linux-3.4.90.orig/fs/Kconfig 2014-05-13 14:11:45.000000000 +0200 ++++ linux-3.4.90/fs/Kconfig 2014-05-17 15:08:09.000000000 +0200 +@@ -203,6 +203,7 @@ + source "fs/befs/Kconfig" + source "fs/bfs/Kconfig" + source "fs/efs/Kconfig" ++source "fs/yaffs2/Kconfig" + source "fs/jffs2/Kconfig" + # UBIFS File system configuration + source "fs/ubifs/Kconfig" +diff -Nur linux-3.4.90.orig/fs/Makefile linux-3.4.90/fs/Makefile +--- linux-3.4.90.orig/fs/Makefile 2014-05-13 14:11:45.000000000 +0200 ++++ linux-3.4.90/fs/Makefile 2014-05-17 15:09:05.000000000 +0200 +@@ -97,6 +97,7 @@ + obj-$(CONFIG_UFS_FS) += ufs/ + obj-$(CONFIG_EFS_FS) += efs/ + obj-$(CONFIG_JFFS2_FS) += jffs2/ ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ + obj-$(CONFIG_LOGFS) += logfs/ + obj-$(CONFIG_UBIFS_FS) += ubifs/ + obj-$(CONFIG_AFFS_FS) += affs/ +diff -Nur linux-3.4.90.orig/fs/yaffs2/Kconfig linux-3.4.90/fs/yaffs2/Kconfig +--- linux-3.4.90.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/Kconfig 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,171 @@ ++# ++# yaffs file system configurations ++# ++ ++config YAFFS_FS ++ tristate "yaffs2 file system support" ++ default n ++ depends on MTD_BLOCK ++ select YAFFS_YAFFS1 ++ select YAFFS_YAFFS2 ++ help ++ yaffs2, or Yet Another Flash File System, is a file system ++ optimised for NAND Flash chips. ++ ++ To compile the yaffs2 file system support as a module, choose M ++ here: the module will be called yaffs2. ++ ++ If unsure, say N. ++ ++ Further information on yaffs2 is available at ++ . ++ ++config YAFFS_YAFFS1 ++ bool "512 byte / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs1 support -- yaffs for 512 byte / page devices ++ ++ Not needed for 2K-page devices. ++ ++ If unsure, say Y. ++ ++config YAFFS_9BYTE_TAGS ++ bool "Use older-style on-NAND data format with pageStatus byte" ++ depends on YAFFS_YAFFS1 ++ default n ++ help ++ ++ Older-style on-NAND data format has a "pageStatus" byte to record ++ chunk/page state. This byte is zero when the page is discarded. ++ Choose this option if you have existing on-NAND data using this ++ format that you need to continue to support. New data written ++ also uses the older-style format. Note: Use of this option ++ generally requires that MTD's oob layout be adjusted to use the ++ older-style format. See notes on tags formats and MTD versions ++ in yaffs_mtdif1.c. ++ ++ If unsure, say N. ++ ++config YAFFS_DOES_ECC ++ bool "Lets yaffs do its own ECC" ++ depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This enables yaffs to use its own ECC functions instead of using ++ the ones from the generic MTD-NAND driver. ++ ++ If unsure, say N. ++ ++config YAFFS_ECC_WRONG_ORDER ++ bool "Use the same ecc byte order as Steven Hill's nand_ecc.c" ++ depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS ++ default n ++ help ++ This makes yaffs_ecc.c use the same ecc byte order as Steven ++ Hill's nand_ecc.c. If not set, then you get the same ecc byte ++ order as SmartMedia. ++ ++ If unsure, say N. ++ ++config YAFFS_YAFFS2 ++ bool "2048 byte (or larger) / page devices" ++ depends on YAFFS_FS ++ default y ++ help ++ Enable yaffs2 support -- yaffs for >= 2K bytes per page devices ++ ++ If unsure, say Y. ++ ++config YAFFS_AUTO_YAFFS2 ++ bool "Autoselect yaffs2 format" ++ depends on YAFFS_YAFFS2 ++ default y ++ help ++ Without this, you need to explicitely use yaffs2 as the file ++ system type. With this, you can say "yaffs" and yaffs or yaffs2 ++ will be used depending on the device page size (yaffs on ++ 512-byte page devices, yaffs2 on 2K page devices). ++ ++ If unsure, say Y. ++ ++config YAFFS_DISABLE_TAGS_ECC ++ bool "Disable yaffs from doing ECC on tags by default" ++ depends on YAFFS_FS && YAFFS_YAFFS2 ++ default n ++ help ++ This defaults yaffs to using its own ECC calculations on tags instead of ++ just relying on the MTD. ++ This behavior can also be overridden with tags_ecc_on and ++ tags_ecc_off mount options. ++ ++ If unsure, say N. ++ ++config YAFFS_ALWAYS_CHECK_CHUNK_ERASED ++ bool "Force chunk erase check" ++ depends on YAFFS_FS ++ default n ++ help ++ Normally yaffs only checks chunks before writing until an erased ++ chunk is found. This helps to detect any partially written ++ chunks that might have happened due to power loss. ++ ++ Enabling this forces on the test that chunks are erased in flash ++ before writing to them. This takes more time but is potentially ++ a bit more secure. ++ ++ Suggest setting Y during development and ironing out driver ++ issues etc. Suggest setting to N if you want faster writing. ++ ++ If unsure, say Y. ++ ++config YAFFS_EMPTY_LOST_AND_FOUND ++ bool "Empty lost and found on boot" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is enabled then the contents of lost and found is ++ automatically dumped at mount. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BLOCK_REFRESHING ++ bool "Disable yaffs2 block refreshing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then block refreshing is disabled. ++ Block refreshing infrequently refreshes the oldest block in ++ a yaffs2 file system. This mechanism helps to refresh flash to ++ mitigate against data loss. This is particularly useful for MLC. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BACKGROUND ++ bool "Disable yaffs2 background processing" ++ depends on YAFFS_FS ++ default n ++ help ++ If this is set, then background processing is disabled. ++ Background processing makes many foreground activities faster. ++ ++ If unsure, say N. ++ ++config YAFFS_DISABLE_BAD_BLOCK_MARKING ++ bool "Disable yaffs2 bad block marking" ++ depends on YAFFS_FS ++ default n ++ help ++ Useful during early flash bring up to prevent problems causing ++ lots of bad block marking. ++ ++ If unsure, say N. ++ ++config YAFFS_XATTR ++ bool "Enable yaffs2 xattr support" ++ depends on YAFFS_FS ++ default y ++ help ++ If this is set then yaffs2 will provide xattr support. ++ If unsure, say Y. +diff -Nur linux-3.4.90.orig/fs/yaffs2/Makefile linux-3.4.90/fs/yaffs2/Makefile +--- linux-3.4.90.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/Makefile 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,18 @@ ++# ++# Makefile for the linux YAFFS filesystem routines. ++# ++ ++obj-$(CONFIG_YAFFS_FS) += yaffs.o ++ ++yaffs-y := yaffs_ecc.o yaffs_vfs.o yaffs_guts.o yaffs_checkptrw.o ++yaffs-y += yaffs_packedtags1.o yaffs_packedtags2.o yaffs_nand.o ++yaffs-y += yaffs_tagscompat.o yaffs_tagsmarshall.o ++yaffs-y += yaffs_mtdif.o ++yaffs-y += yaffs_nameval.o yaffs_attribs.o ++yaffs-y += yaffs_allocator.o ++yaffs-y += yaffs_yaffs1.o ++yaffs-y += yaffs_yaffs2.o ++yaffs-y += yaffs_bitmap.o ++yaffs-y += yaffs_summary.o ++yaffs-y += yaffs_verify.o ++ +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.c linux-3.4.90/fs/yaffs2/yaffs_allocator.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_allocator.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,357 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_allocator.h" ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yportenv.h" ++ ++/* ++ * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks ++ * of approx 100 objects that are themn allocated singly. ++ * This is basically a simplified slab allocator. ++ * ++ * We don't use the Linux slab allocator because slab does not allow ++ * us to dump all the objects in one hit when we do a umount and tear ++ * down all the tnodes and objects. slab requires that we first free ++ * the individual objects. ++ * ++ * Once yaffs has been mainlined I shall try to motivate for a change ++ * to slab to provide the extra features we need here. ++ */ ++ ++struct yaffs_tnode_list { ++ struct yaffs_tnode_list *next; ++ struct yaffs_tnode *tnodes; ++}; ++ ++struct yaffs_obj_list { ++ struct yaffs_obj_list *next; ++ struct yaffs_obj *objects; ++}; ++ ++struct yaffs_allocator { ++ int n_tnodes_created; ++ struct yaffs_tnode *free_tnodes; ++ int n_free_tnodes; ++ struct yaffs_tnode_list *alloc_tnode_list; ++ ++ int n_obj_created; ++ struct list_head free_objs; ++ int n_free_objects; ++ ++ struct yaffs_obj_list *allocated_obj_list; ++}; ++ ++static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->alloc_tnode_list) { ++ tmp = allocator->alloc_tnode_list->next; ++ ++ kfree(allocator->alloc_tnode_list->tnodes); ++ kfree(allocator->alloc_tnode_list); ++ allocator->alloc_tnode_list = tmp; ++ } ++ ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static void yaffs_init_raw_tnodes(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->alloc_tnode_list = NULL; ++ allocator->free_tnodes = NULL; ++ allocator->n_free_tnodes = 0; ++ allocator->n_tnodes_created = 0; ++} ++ ++static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ int i; ++ struct yaffs_tnode *new_tnodes; ++ u8 *mem; ++ struct yaffs_tnode *curr; ++ struct yaffs_tnode *next; ++ struct yaffs_tnode_list *tnl; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_tnodes < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS); ++ mem = (u8 *) new_tnodes; ++ ++ if (!new_tnodes) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: Could not allocate Tnodes"); ++ return YAFFS_FAIL; ++ } ++ ++ /* New hookup for wide tnodes */ ++ for (i = 0; i < n_tnodes - 1; i++) { ++ curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size]; ++ next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size]; ++ curr->internal[0] = next; ++ } ++ ++ curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size]; ++ curr->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = (struct yaffs_tnode *)mem; ++ ++ allocator->n_free_tnodes += n_tnodes; ++ allocator->n_tnodes_created += n_tnodes; ++ ++ /* Now add this bunch of tnodes to a list for freeing up. ++ * NB If we can't add this to the management list it isn't fatal ++ * but it just means we can't free this bunch of tnodes later. ++ */ ++ tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS); ++ if (!tnl) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Could not add tnodes to management list"); ++ return YAFFS_FAIL; ++ } else { ++ tnl->tnodes = new_tnodes; ++ tnl->next = allocator->alloc_tnode_list; ++ allocator->alloc_tnode_list = tnl; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added"); ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = ++ (struct yaffs_allocator *)dev->allocator; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (!allocator) { ++ BUG(); ++ return NULL; ++ } ++ ++ /* If there are none left make more */ ++ if (!allocator->free_tnodes) ++ yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES); ++ ++ if (allocator->free_tnodes) { ++ tn = allocator->free_tnodes; ++ allocator->free_tnodes = allocator->free_tnodes->internal[0]; ++ allocator->n_free_tnodes--; ++ } ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ if (tn) { ++ tn->internal[0] = allocator->free_tnodes; ++ allocator->free_tnodes = tn; ++ allocator->n_free_tnodes++; ++ } ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++/*--------------- yaffs_obj alloaction ------------------------ ++ * ++ * Free yaffs_objs are stored in a list using obj->siblings. ++ * The blocks of allocated objects are stored in a linked list. ++ */ ++ ++static void yaffs_init_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator->allocated_obj_list = NULL; ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++} ++ ++static void yaffs_deinit_raw_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ struct yaffs_obj_list *tmp; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ while (allocator->allocated_obj_list) { ++ tmp = allocator->allocated_obj_list->next; ++ kfree(allocator->allocated_obj_list->objects); ++ kfree(allocator->allocated_obj_list); ++ allocator->allocated_obj_list = tmp; ++ } ++ ++ INIT_LIST_HEAD(&allocator->free_objs); ++ allocator->n_free_objects = 0; ++ allocator->n_obj_created = 0; ++} ++ ++static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj) ++{ ++ struct yaffs_allocator *allocator = dev->allocator; ++ int i; ++ struct yaffs_obj *new_objs; ++ struct yaffs_obj_list *list; ++ ++ if (!allocator) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ if (n_obj < 1) ++ return YAFFS_OK; ++ ++ /* make these things */ ++ new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS); ++ list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS); ++ ++ if (!new_objs || !list) { ++ kfree(new_objs); ++ new_objs = NULL; ++ kfree(list); ++ list = NULL; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Could not allocate more objects"); ++ return YAFFS_FAIL; ++ } ++ ++ /* Hook them into the free list */ ++ for (i = 0; i < n_obj; i++) ++ list_add(&new_objs[i].siblings, &allocator->free_objs); ++ ++ allocator->n_free_objects += n_obj; ++ allocator->n_obj_created += n_obj; ++ ++ /* Now add this bunch of Objects to a list for freeing up. */ ++ ++ list->objects = new_objs; ++ list->next = allocator->allocated_obj_list; ++ allocator->allocated_obj_list = list; ++ ++ return YAFFS_OK; ++} ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct list_head *lh; ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return obj; ++ } ++ ++ /* If there are none left make more */ ++ if (list_empty(&allocator->free_objs)) ++ yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS); ++ ++ if (!list_empty(&allocator->free_objs)) { ++ lh = allocator->free_objs.next; ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ list_del_init(lh); ++ allocator->n_free_objects--; ++ } ++ ++ return obj; ++} ++ ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ ++ struct yaffs_allocator *allocator = dev->allocator; ++ ++ if (!allocator) { ++ BUG(); ++ return; ++ } ++ ++ /* Link into the free list. */ ++ list_add(&obj->siblings, &allocator->free_objs); ++ allocator->n_free_objects++; ++} ++ ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ ++ if (!dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ yaffs_deinit_raw_tnodes(dev); ++ yaffs_deinit_raw_objs(dev); ++ kfree(dev->allocator); ++ dev->allocator = NULL; ++} ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_allocator *allocator; ++ ++ if (dev->allocator) { ++ BUG(); ++ return; ++ } ++ ++ allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS); ++ if (allocator) { ++ dev->allocator = allocator; ++ yaffs_init_raw_tnodes(dev); ++ yaffs_init_raw_objs(dev); ++ } ++} ++ +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.h linux-3.4.90/fs/yaffs2/yaffs_allocator.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_allocator.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,30 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ALLOCATOR_H__ ++#define __YAFFS_ALLOCATOR_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev); ++void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev); ++void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn); ++ ++struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev); ++void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.c linux-3.4.90/fs/yaffs2/yaffs_attribs.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_attribs.c 2014-05-17 16:37:59.000000000 +0200 +@@ -0,0 +1,166 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++static inline uid_t ia_uid_read(const struct iattr *iattr) ++{ ++ return from_kuid(&init_user_ns, iattr->ia_uid); ++} ++ ++static inline gid_t ia_gid_read(const struct iattr *iattr) ++{ ++ return from_kgid(&init_user_ns, iattr->ia_gid); ++} ++ ++static inline void ia_uid_write(struct iattr *iattr, uid_t uid) ++{ ++ iattr->ia_uid = make_kuid(&init_user_ns, uid); ++} ++ ++static inline void ia_gid_write(struct iattr *iattr, gid_t gid) ++{ ++ iattr->ia_gid = make_kgid(&init_user_ns, gid); ++} ++#else ++static inline uid_t ia_uid_read(const struct iattr *iattr) ++{ ++ return iattr->ia_uid; ++} ++ ++static inline gid_t ia_gid_read(const struct iattr *iattr) ++{ ++ return iattr->ia_gid; ++} ++ ++static inline void ia_uid_write(struct iattr *iattr, uid_t uid) ++{ ++ iattr->ia_uid = uid; ++} ++ ++static inline void ia_gid_write(struct iattr *iattr, gid_t gid) ++{ ++ iattr->ia_gid = gid; ++} ++#endif ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh) ++{ ++ obj->yst_uid = oh->yst_uid; ++ obj->yst_gid = oh->yst_gid; ++ obj->yst_atime = oh->yst_atime; ++ obj->yst_mtime = oh->yst_mtime; ++ obj->yst_ctime = oh->yst_ctime; ++ obj->yst_rdev = oh->yst_rdev; ++} ++ ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj) ++{ ++ oh->yst_uid = obj->yst_uid; ++ oh->yst_gid = obj->yst_gid; ++ oh->yst_atime = obj->yst_atime; ++ oh->yst_mtime = obj->yst_mtime; ++ oh->yst_ctime = obj->yst_ctime; ++ oh->yst_rdev = obj->yst_rdev; ++ ++} ++ ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c) ++{ ++ obj->yst_mtime = Y_CURRENT_TIME; ++ if (do_a) ++ obj->yst_atime = obj->yst_mtime; ++ if (do_c) ++ obj->yst_ctime = obj->yst_mtime; ++} ++ ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev) ++{ ++ yaffs_load_current_time(obj, 1, 1); ++ obj->yst_rdev = rdev; ++ obj->yst_uid = uid; ++ obj->yst_gid = gid; ++} ++ ++static loff_t yaffs_get_file_size(struct yaffs_obj *obj) ++{ ++ YCHAR *alias = NULL; ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return obj->variant.file_variant.file_size; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = obj->variant.symlink_variant.alias; ++ if (!alias) ++ return 0; ++ return strnlen(alias, YAFFS_MAX_ALIAS_LENGTH); ++ default: ++ return 0; ++ } ++} ++ ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = attr->ia_valid; ++ ++ if (valid & ATTR_MODE) ++ obj->yst_mode = attr->ia_mode; ++ if (valid & ATTR_UID) ++ obj->yst_uid = ia_uid_read(attr); ++ if (valid & ATTR_GID) ++ obj->yst_gid = ia_gid_read(attr); ++ ++ if (valid & ATTR_ATIME) ++ obj->yst_atime = Y_TIME_CONVERT(attr->ia_atime); ++ if (valid & ATTR_CTIME) ++ obj->yst_ctime = Y_TIME_CONVERT(attr->ia_ctime); ++ if (valid & ATTR_MTIME) ++ obj->yst_mtime = Y_TIME_CONVERT(attr->ia_mtime); ++ ++ if (valid & ATTR_SIZE) ++ yaffs_resize_file(obj, attr->ia_size); ++ ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ return YAFFS_OK; ++ ++} ++ ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr) ++{ ++ unsigned int valid = 0; ++ ++ attr->ia_mode = obj->yst_mode; ++ valid |= ATTR_MODE; ++ ia_uid_write(attr, obj->yst_uid); ++ valid |= ATTR_UID; ++ ia_gid_write(attr, obj->yst_gid); ++ valid |= ATTR_GID; ++ ++ Y_TIME_CONVERT(attr->ia_atime) = obj->yst_atime; ++ valid |= ATTR_ATIME; ++ Y_TIME_CONVERT(attr->ia_ctime) = obj->yst_ctime; ++ valid |= ATTR_CTIME; ++ Y_TIME_CONVERT(attr->ia_mtime) = obj->yst_mtime; ++ valid |= ATTR_MTIME; ++ ++ attr->ia_size = yaffs_get_file_size(obj); ++ valid |= ATTR_SIZE; ++ ++ attr->ia_valid = valid; ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.h linux-3.4.90/fs/yaffs2/yaffs_attribs.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_attribs.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_ATTRIBS_H__ ++#define __YAFFS_ATTRIBS_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_load_attribs(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh); ++void yaffs_load_attribs_oh(struct yaffs_obj_hdr *oh, struct yaffs_obj *obj); ++void yaffs_attribs_init(struct yaffs_obj *obj, u32 gid, u32 uid, u32 rdev); ++void yaffs_load_current_time(struct yaffs_obj *obj, int do_a, int do_c); ++int yaffs_set_attribs(struct yaffs_obj *obj, struct iattr *attr); ++int yaffs_get_attribs(struct yaffs_obj *obj, struct iattr *attr); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.c linux-3.4.90/fs/yaffs2/yaffs_bitmap.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_bitmap.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,97 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_bitmap.h" ++#include "yaffs_trace.h" ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++static inline u8 *yaffs_block_bits(struct yaffs_dev *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "BlockBits block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return dev->chunk_bits + ++ (dev->chunk_bit_stride * (blk - dev->internal_start_block)); ++} ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block || ++ chunk < 0 || chunk >= dev->param.chunks_per_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Chunk Id (%d:%d) invalid", ++ blk, chunk); ++ BUG(); ++ } ++} ++ ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ memset(blk_bits, 0, dev->chunk_bit_stride); ++} ++ ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] &= ~(1 << (chunk & 7)); ++} ++ ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ blk_bits[chunk / 8] |= (1 << (chunk & 7)); ++} ++ ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ ++ yaffs_verify_chunk_bit_id(dev, blk, chunk); ++ return (blk_bits[chunk / 8] & (1 << (chunk & 7))) ? 1 : 0; ++} ++ ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++) { ++ if (*blk_bits) ++ return 1; ++ blk_bits++; ++ } ++ return 0; ++} ++ ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk) ++{ ++ u8 *blk_bits = yaffs_block_bits(dev, blk); ++ int i; ++ int n = 0; ++ ++ for (i = 0; i < dev->chunk_bit_stride; i++, blk_bits++) ++ n += hweight8(*blk_bits); ++ ++ return n; ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.h linux-3.4.90/fs/yaffs2/yaffs_bitmap.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_bitmap.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * Chunk bitmap manipulations ++ */ ++ ++#ifndef __YAFFS_BITMAP_H__ ++#define __YAFFS_BITMAP_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_chunk_bit_id(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_clear_chunk_bits(struct yaffs_dev *dev, int blk); ++void yaffs_clear_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++void yaffs_set_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_check_chunk_bit(struct yaffs_dev *dev, int blk, int chunk); ++int yaffs_still_some_chunks(struct yaffs_dev *dev, int blk); ++int yaffs_count_chunk_bits(struct yaffs_dev *dev, int blk); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.c linux-3.4.90/fs/yaffs2/yaffs_checkptrw.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_checkptrw.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,474 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_checkptrw.h" ++#include "yaffs_getblockinfo.h" ++ ++struct yaffs_checkpt_chunk_hdr { ++ int version; ++ int seq; ++ u32 sum; ++ u32 xor; ++} ; ++ ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++static int apply_block_offset(struct yaffs_dev *dev, int block) ++{ ++ return block - dev->block_offset; ++} ++ ++static void yaffs2_checkpt_init_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ hdr.version = YAFFS_CHECKPOINT_VERSION; ++ hdr.seq = dev->checkpt_page_seq; ++ hdr.sum = dev->checkpt_sum; ++ hdr.xor = dev->checkpt_xor; ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ memcpy(dev->checkpt_buffer, &hdr, sizeof(hdr)); ++} ++ ++static int yaffs2_checkpt_check_chunk_hdr(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_chunk_hdr hdr; ++ ++ memcpy(&hdr, dev->checkpt_buffer, sizeof(hdr)); ++ ++ dev->checkpt_byte_offs = sizeof(hdr); ++ ++ return hdr.version == YAFFS_CHECKPOINT_VERSION && ++ hdr.seq == dev->checkpt_page_seq && ++ hdr.sum == dev->checkpt_sum && ++ hdr.xor == dev->checkpt_xor; ++} ++ ++static int yaffs2_checkpt_space_ok(struct yaffs_dev *dev) ++{ ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpt blocks_avail = %d", blocks_avail); ++ ++ return (blocks_avail <= 0) ? 0 : 1; ++} ++ ++static int yaffs_checkpt_erase(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (!dev->drv.drv_erase_fn) ++ return 0; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checking blocks %d to %d", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ int offset_i = apply_block_offset(dev, i); ++ int result; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "erasing checkpt block %d", i); ++ ++ dev->n_erasures++; ++ ++ result = dev->drv.drv_erase_fn(dev, offset_i); ++ if(result) { ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += ++ dev->param.chunks_per_block; ++ } else { ++ dev->drv.drv_mark_bad_fn(dev, offset_i); ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ } ++ } ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ return 1; ++} ++ ++static void yaffs2_checkpt_find_erased_block(struct yaffs_dev *dev) ++{ ++ int i; ++ int blocks_avail = dev->n_erased_blocks - dev->param.n_reserved_blocks; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block: erased %d reserved %d avail %d next %d ", ++ dev->n_erased_blocks, dev->param.n_reserved_blocks, ++ blocks_avail, dev->checkpt_next_block); ++ ++ if (dev->checkpt_next_block >= 0 && ++ dev->checkpt_next_block <= dev->internal_end_block && ++ blocks_avail > 0) { ++ ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev, i); ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ dev->checkpt_next_block = i + 1; ++ dev->checkpt_cur_block = i; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "allocating checkpt block %d", i); ++ return; ++ } ++ } ++ } ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "out of checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++static void yaffs2_checkpt_find_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_ext_tags tags; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: start: blocks %d next %d", ++ dev->blocks_in_checkpt, dev->checkpt_next_block); ++ ++ if (dev->blocks_in_checkpt < dev->checkpt_max_blocks) ++ for (i = dev->checkpt_next_block; i <= dev->internal_end_block; ++ i++) { ++ int chunk = i * dev->param.chunks_per_block; ++ enum yaffs_block_state state; ++ u32 seq; ++ ++ dev->tagger.read_chunk_tags_fn(dev, ++ apply_chunk_offset(dev, chunk), ++ NULL, &tags); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "find next checkpt block: search: block %d state %d oid %d seq %d eccr %d", ++ i, (int) state, ++ tags.obj_id, tags.seq_number, ++ tags.ecc_result); ++ ++ if (tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ continue; ++ ++ dev->tagger.query_block_fn(dev, ++ apply_block_offset(dev, i), ++ &state, &seq); ++ if (state == YAFFS_BLOCK_STATE_DEAD) ++ continue; ++ ++ /* Right kind of block */ ++ dev->checkpt_next_block = tags.obj_id; ++ dev->checkpt_cur_block = i; ++ dev->checkpt_block_list[dev->blocks_in_checkpt] = i; ++ dev->blocks_in_checkpt++; ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "found checkpt block %d", i); ++ return; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "found no more checkpt blocks"); ++ ++ dev->checkpt_next_block = -1; ++ dev->checkpt_cur_block = -1; ++} ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing) ++{ ++ int i; ++ ++ dev->checkpt_open_write = writing; ++ ++ /* Got the functions we need? */ ++ if (!dev->tagger.write_chunk_tags_fn || ++ !dev->tagger.read_chunk_tags_fn || ++ !dev->drv.drv_erase_fn || ++ !dev->drv.drv_mark_bad_fn) ++ return 0; ++ ++ if (writing && !yaffs2_checkpt_space_ok(dev)) ++ return 0; ++ ++ if (!dev->checkpt_buffer) ++ dev->checkpt_buffer = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ dev->checkpt_page_seq = 0; ++ dev->checkpt_byte_count = 0; ++ dev->checkpt_sum = 0; ++ dev->checkpt_xor = 0; ++ dev->checkpt_cur_block = -1; ++ dev->checkpt_cur_chunk = -1; ++ dev->checkpt_next_block = dev->internal_start_block; ++ ++ if (writing) { ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ return yaffs_checkpt_erase(dev); ++ } ++ ++ /* Opening for a read */ ++ /* Set to a value that will kick off a read */ ++ dev->checkpt_byte_offs = dev->data_bytes_per_chunk; ++ /* A checkpoint block list of 1 checkpoint block per 16 block is ++ * (hopefully) going to be way more than we need */ ++ dev->blocks_in_checkpt = 0; ++ dev->checkpt_max_blocks = ++ (dev->internal_end_block - dev->internal_start_block) / 16 + 2; ++ dev->checkpt_block_list = ++ kmalloc(sizeof(int) * dev->checkpt_max_blocks, GFP_NOFS); ++ ++ if (!dev->checkpt_block_list) ++ return 0; ++ ++ for (i = 0; i < dev->checkpt_max_blocks; i++) ++ dev->checkpt_block_list[i] = -1; ++ ++ return 1; ++} ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum) ++{ ++ u32 composite_sum; ++ ++ composite_sum = (dev->checkpt_sum << 8) | (dev->checkpt_xor & 0xff); ++ *sum = composite_sum; ++ return 1; ++} ++ ++static int yaffs2_checkpt_flush_buffer(struct yaffs_dev *dev) ++{ ++ int chunk; ++ int offset_chunk; ++ struct yaffs_ext_tags tags; ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_erased_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) ++ return 0; ++ ++ tags.is_deleted = 0; ++ tags.obj_id = dev->checkpt_next_block; /* Hint to next place to look */ ++ tags.chunk_id = dev->checkpt_page_seq + 1; ++ tags.seq_number = YAFFS_SEQUENCE_CHECKPOINT_DATA; ++ tags.n_bytes = dev->data_bytes_per_chunk; ++ if (dev->checkpt_cur_chunk == 0) { ++ /* First chunk we write for the block? Set block state to ++ checkpoint */ ++ struct yaffs_block_info *bi = ++ yaffs_get_block_info(dev, dev->checkpt_cur_block); ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ dev->blocks_in_checkpt++; ++ } ++ ++ chunk = ++ dev->checkpt_cur_block * dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint wite buffer nand %d(%d:%d) objid %d chId %d", ++ chunk, dev->checkpt_cur_block, dev->checkpt_cur_chunk, ++ tags.obj_id, tags.chunk_id); ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ ++ dev->n_page_writes++; ++ ++ dev->tagger.write_chunk_tags_fn(dev, offset_chunk, ++ dev->checkpt_buffer, &tags); ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ if (dev->checkpt_cur_chunk >= dev->param.chunks_per_block) { ++ dev->checkpt_cur_chunk = 0; ++ dev->checkpt_cur_block = -1; ++ } ++ memset(dev->checkpt_buffer, 0, dev->data_bytes_per_chunk); ++ ++ yaffs2_checkpt_init_chunk_hdr(dev); ++ ++ ++ return 1; ++} ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (!dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ dev->checkpt_buffer[dev->checkpt_byte_offs] = *data_bytes; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) ++ ok = yaffs2_checkpt_flush_buffer(dev); ++ } ++ ++ return i; ++} ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes) ++{ ++ int i = 0; ++ int ok = 1; ++ struct yaffs_ext_tags tags; ++ int chunk; ++ int offset_chunk; ++ u8 *data_bytes = (u8 *) data; ++ ++ if (!dev->checkpt_buffer) ++ return 0; ++ ++ if (dev->checkpt_open_write) ++ return -1; ++ ++ while (i < n_bytes && ok) { ++ ++ if (dev->checkpt_byte_offs < 0 || ++ dev->checkpt_byte_offs >= dev->data_bytes_per_chunk) { ++ ++ if (dev->checkpt_cur_block < 0) { ++ yaffs2_checkpt_find_block(dev); ++ dev->checkpt_cur_chunk = 0; ++ } ++ ++ if (dev->checkpt_cur_block < 0) { ++ ok = 0; ++ break; ++ } ++ ++ chunk = dev->checkpt_cur_block * ++ dev->param.chunks_per_block + ++ dev->checkpt_cur_chunk; ++ ++ offset_chunk = apply_chunk_offset(dev, chunk); ++ dev->n_page_reads++; ++ ++ /* read in the next chunk */ ++ dev->tagger.read_chunk_tags_fn(dev, ++ offset_chunk, ++ dev->checkpt_buffer, ++ &tags); ++ ++ if (tags.chunk_id != (dev->checkpt_page_seq + 1) || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.seq_number != YAFFS_SEQUENCE_CHECKPOINT_DATA) { ++ ok = 0; ++ break; ++ } ++ if(!yaffs2_checkpt_check_chunk_hdr(dev)) { ++ ok = 0; ++ break; ++ } ++ ++ dev->checkpt_page_seq++; ++ dev->checkpt_cur_chunk++; ++ ++ if (dev->checkpt_cur_chunk >= ++ dev->param.chunks_per_block) ++ dev->checkpt_cur_block = -1; ++ ++ } ++ ++ *data_bytes = dev->checkpt_buffer[dev->checkpt_byte_offs]; ++ dev->checkpt_sum += *data_bytes; ++ dev->checkpt_xor ^= *data_bytes; ++ dev->checkpt_byte_offs++; ++ i++; ++ data_bytes++; ++ dev->checkpt_byte_count++; ++ } ++ ++ return i; ++} ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->checkpt_open_write) { ++ if (dev->checkpt_byte_offs != ++ sizeof(sizeof(struct yaffs_checkpt_chunk_hdr))) ++ yaffs2_checkpt_flush_buffer(dev); ++ } else if (dev->checkpt_block_list) { ++ for (i = 0; ++ i < dev->blocks_in_checkpt && ++ dev->checkpt_block_list[i] >= 0; i++) { ++ int blk = dev->checkpt_block_list[i]; ++ struct yaffs_block_info *bi = NULL; ++ ++ if (dev->internal_start_block <= blk && ++ blk <= dev->internal_end_block) ++ bi = yaffs_get_block_info(dev, blk); ++ if (bi && bi->block_state == YAFFS_BLOCK_STATE_EMPTY) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ } ++ kfree(dev->checkpt_block_list); ++ dev->checkpt_block_list = NULL; ++ } ++ ++ dev->n_free_chunks -= ++ dev->blocks_in_checkpt * dev->param.chunks_per_block; ++ dev->n_erased_blocks -= dev->blocks_in_checkpt; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, "checkpoint byte count %d", ++ dev->checkpt_byte_count); ++ ++ if (dev->checkpt_buffer) { ++ /* free the buffer */ ++ kfree(dev->checkpt_buffer); ++ dev->checkpt_buffer = NULL; ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev) ++{ ++ /* Erase the checkpoint data */ ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "checkpoint invalidate of %d blocks", ++ dev->blocks_in_checkpt); ++ ++ return yaffs_checkpt_erase(dev); ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.h linux-3.4.90/fs/yaffs2/yaffs_checkptrw.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_checkptrw.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,33 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_CHECKPTRW_H__ ++#define __YAFFS_CHECKPTRW_H__ ++ ++#include "yaffs_guts.h" ++ ++int yaffs2_checkpt_open(struct yaffs_dev *dev, int writing); ++ ++int yaffs2_checkpt_wr(struct yaffs_dev *dev, const void *data, int n_bytes); ++ ++int yaffs2_checkpt_rd(struct yaffs_dev *dev, void *data, int n_bytes); ++ ++int yaffs2_get_checkpt_sum(struct yaffs_dev *dev, u32 * sum); ++ ++int yaffs_checkpt_close(struct yaffs_dev *dev); ++ ++int yaffs2_checkpt_invalidate_stream(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.c linux-3.4.90/fs/yaffs2/yaffs_ecc.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_ecc.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,281 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two ++ * such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_ecc.h" ++ ++/* Table generated by gen-ecc.c ++ * Using a table means we do not have to calculate p1..p4 and p1'..p4' ++ * for each byte of data. These are instead provided in a table in bits7..2. ++ * Bit 0 of each entry indicates whether the entry has an odd or even parity, ++ * and therefore this bytes influence on the line parity. ++ */ ++ ++static const unsigned char column_parity_table[] = { ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, ++ 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9, ++ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, ++ 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c, ++ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, ++ 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30, ++ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, ++ 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5, ++ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, ++ 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c, ++ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, ++ 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99, ++ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, ++ 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95, ++ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, ++ 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00, ++}; ++ ++ ++/* Calculate the ECC for a 256-byte block of data */ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned char line_parity = 0; ++ unsigned char line_parity_prime = 0; ++ unsigned char t; ++ unsigned char b; ++ ++ for (i = 0; i < 256; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ } ++ ++ ecc[2] = (~col_parity) | 0x03; ++ ++ t = 0; ++ if (line_parity & 0x80) ++ t |= 0x80; ++ if (line_parity_prime & 0x80) ++ t |= 0x40; ++ if (line_parity & 0x40) ++ t |= 0x20; ++ if (line_parity_prime & 0x40) ++ t |= 0x10; ++ if (line_parity & 0x20) ++ t |= 0x08; ++ if (line_parity_prime & 0x20) ++ t |= 0x04; ++ if (line_parity & 0x10) ++ t |= 0x02; ++ if (line_parity_prime & 0x10) ++ t |= 0x01; ++ ecc[1] = ~t; ++ ++ t = 0; ++ if (line_parity & 0x08) ++ t |= 0x80; ++ if (line_parity_prime & 0x08) ++ t |= 0x40; ++ if (line_parity & 0x04) ++ t |= 0x20; ++ if (line_parity_prime & 0x04) ++ t |= 0x10; ++ if (line_parity & 0x02) ++ t |= 0x08; ++ if (line_parity_prime & 0x02) ++ t |= 0x04; ++ if (line_parity & 0x01) ++ t |= 0x02; ++ if (line_parity_prime & 0x01) ++ t |= 0x01; ++ ecc[0] = ~t; ++ ++} ++ ++/* Correct the ECC on a 256 byte block of data */ ++ ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc) ++{ ++ unsigned char d0, d1, d2; /* deltas */ ++ ++ d0 = read_ecc[0] ^ test_ecc[0]; ++ d1 = read_ecc[1] ^ test_ecc[1]; ++ d2 = read_ecc[2] ^ test_ecc[2]; ++ ++ if ((d0 | d1 | d2) == 0) ++ return 0; /* no error */ ++ ++ if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 && ++ ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 && ++ ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) { ++ /* Single bit (recoverable) error in data */ ++ ++ unsigned byte; ++ unsigned bit; ++ ++ bit = byte = 0; ++ ++ if (d1 & 0x80) ++ byte |= 0x80; ++ if (d1 & 0x20) ++ byte |= 0x40; ++ if (d1 & 0x08) ++ byte |= 0x20; ++ if (d1 & 0x02) ++ byte |= 0x10; ++ if (d0 & 0x80) ++ byte |= 0x08; ++ if (d0 & 0x20) ++ byte |= 0x04; ++ if (d0 & 0x08) ++ byte |= 0x02; ++ if (d0 & 0x02) ++ byte |= 0x01; ++ ++ if (d2 & 0x80) ++ bit |= 0x04; ++ if (d2 & 0x20) ++ bit |= 0x02; ++ if (d2 & 0x08) ++ bit |= 0x01; ++ ++ data[byte] ^= (1 << bit); ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ read_ecc[0] = test_ecc[0]; ++ read_ecc[1] = test_ecc[1]; ++ read_ecc[2] = test_ecc[2]; ++ ++ return 1; /* Corrected the error */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++ ++} ++ ++/* ++ * ECCxxxOther does ECC calcs on arbitrary n bytes of data ++ */ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc_other) ++{ ++ unsigned int i; ++ unsigned char col_parity = 0; ++ unsigned line_parity = 0; ++ unsigned line_parity_prime = 0; ++ unsigned char b; ++ ++ for (i = 0; i < n_bytes; i++) { ++ b = column_parity_table[*data++]; ++ col_parity ^= b; ++ ++ if (b & 0x01) { ++ /* odd number of bits in the byte */ ++ line_parity ^= i; ++ line_parity_prime ^= ~i; ++ } ++ ++ } ++ ++ ecc_other->col_parity = (col_parity >> 2) & 0x3f; ++ ecc_other->line_parity = line_parity; ++ ecc_other->line_parity_prime = line_parity_prime; ++} ++ ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc) ++{ ++ unsigned char delta_col; /* column parity delta */ ++ unsigned delta_line; /* line parity delta */ ++ unsigned delta_line_prime; /* line parity delta */ ++ unsigned bit; ++ ++ delta_col = read_ecc->col_parity ^ test_ecc->col_parity; ++ delta_line = read_ecc->line_parity ^ test_ecc->line_parity; ++ delta_line_prime = ++ read_ecc->line_parity_prime ^ test_ecc->line_parity_prime; ++ ++ if ((delta_col | delta_line | delta_line_prime) == 0) ++ return 0; /* no error */ ++ ++ if (delta_line == ~delta_line_prime && ++ (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) { ++ /* Single bit (recoverable) error in data */ ++ ++ bit = 0; ++ ++ if (delta_col & 0x20) ++ bit |= 0x04; ++ if (delta_col & 0x08) ++ bit |= 0x02; ++ if (delta_col & 0x02) ++ bit |= 0x01; ++ ++ if (delta_line >= n_bytes) ++ return -1; ++ ++ data[delta_line] ^= (1 << bit); ++ ++ return 1; /* corrected */ ++ } ++ ++ if ((hweight32(delta_line) + ++ hweight32(delta_line_prime) + ++ hweight8(delta_col)) == 1) { ++ /* Reccoverable error in ecc */ ++ ++ *read_ecc = *test_ecc; ++ return 1; /* corrected */ ++ } ++ ++ /* Unrecoverable error */ ++ ++ return -1; ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.h linux-3.4.90/fs/yaffs2/yaffs_ecc.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_ecc.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* ++ * This code implements the ECC algorithm used in SmartMedia. ++ * ++ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. ++ * The two unused bit are set to 1. ++ * The ECC can correct single bit errors in a 256-byte page of data. ++ * Thus, two such ECC blocks are used on a 512-byte NAND page. ++ * ++ */ ++ ++#ifndef __YAFFS_ECC_H__ ++#define __YAFFS_ECC_H__ ++ ++struct yaffs_ecc_other { ++ unsigned char col_parity; ++ unsigned line_parity; ++ unsigned line_parity_prime; ++}; ++ ++void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc); ++int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc, ++ const unsigned char *test_ecc); ++ ++void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *ecc); ++int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes, ++ struct yaffs_ecc_other *read_ecc, ++ const struct yaffs_ecc_other *test_ecc); ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_getblockinfo.h linux-3.4.90/fs/yaffs2/yaffs_getblockinfo.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_getblockinfo.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,35 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GETBLOCKINFO_H__ ++#define __YAFFS_GETBLOCKINFO_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++ ++/* Function to manipulate block info */ ++static inline struct yaffs_block_info *yaffs_get_block_info(struct yaffs_dev ++ *dev, int blk) ++{ ++ if (blk < dev->internal_start_block || blk > dev->internal_end_block) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs: get_block_info block %d is not valid", ++ blk); ++ BUG(); ++ } ++ return &dev->block_info[blk - dev->internal_start_block]; ++} ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_guts.c linux-3.4.90/fs/yaffs2/yaffs_guts.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_guts.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,5146 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++#include "yaffs_guts.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_tagsmarshall.h" ++#include "yaffs_nand.h" ++#include "yaffs_yaffs1.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_verify.h" ++#include "yaffs_nand.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nameval.h" ++#include "yaffs_allocator.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* Note YAFFS_GC_GOOD_ENOUGH must be <= YAFFS_GC_PASSIVE_THRESHOLD */ ++#define YAFFS_GC_GOOD_ENOUGH 2 ++#define YAFFS_GC_PASSIVE_THRESHOLD 4 ++ ++#include "yaffs_ecc.h" ++ ++/* Forward declarations */ ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve); ++ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size); ++ ++/* Function to calculate chunk and offset */ ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out) ++{ ++ int chunk; ++ u32 offset; ++ ++ chunk = (u32) (addr >> dev->chunk_shift); ++ ++ if (dev->chunk_div == 1) { ++ /* easy power of 2 case */ ++ offset = (u32) (addr & dev->chunk_mask); ++ } else { ++ /* Non power-of-2 case */ ++ ++ loff_t chunk_base; ++ ++ chunk /= dev->chunk_div; ++ ++ chunk_base = ((loff_t) chunk) * dev->data_bytes_per_chunk; ++ offset = (u32) (addr - chunk_base); ++ } ++ ++ *chunk_out = chunk; ++ *offset_out = offset; ++} ++ ++/* Function to return the number of shifts for a power of 2 greater than or ++ * equal to the given number ++ * Note we don't try to cater for all possible numbers and this does not have to ++ * be hellishly efficient. ++ */ ++ ++static inline u32 calc_shifts_ceiling(u32 x) ++{ ++ int extra_bits; ++ int shifts; ++ ++ shifts = extra_bits = 0; ++ ++ while (x > 1) { ++ if (x & 1) ++ extra_bits++; ++ x >>= 1; ++ shifts++; ++ } ++ ++ if (extra_bits) ++ shifts++; ++ ++ return shifts; ++} ++ ++/* Function to return the number of shifts to get a 1 in bit 0 ++ */ ++ ++static inline u32 calc_shifts(u32 x) ++{ ++ u32 shifts; ++ ++ shifts = 0; ++ ++ if (!x) ++ return 0; ++ ++ while (!(x & 1)) { ++ x >>= 1; ++ shifts++; ++ } ++ ++ return shifts; ++} ++ ++/* ++ * Temporary buffer manipulations. ++ */ ++ ++static int yaffs_init_tmp_buffers(struct yaffs_dev *dev) ++{ ++ int i; ++ u8 *buf = (u8 *) 1; ++ ++ memset(dev->temp_buffer, 0, sizeof(dev->temp_buffer)); ++ ++ for (i = 0; buf && i < YAFFS_N_TEMP_BUFFERS; i++) { ++ dev->temp_buffer[i].in_use = 0; ++ buf = kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ dev->temp_buffer[i].buffer = buf; ++ } ++ ++ return buf ? YAFFS_OK : YAFFS_FAIL; ++} ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev * dev) ++{ ++ int i; ++ ++ dev->temp_in_use++; ++ if (dev->temp_in_use > dev->max_temp) ++ dev->max_temp = dev->temp_in_use; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].in_use == 0) { ++ dev->temp_buffer[i].in_use = 1; ++ return dev->temp_buffer[i].buffer; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, "Out of temp buffers"); ++ /* ++ * If we got here then we have to allocate an unmanaged one ++ * This is not good. ++ */ ++ ++ dev->unmanaged_buffer_allocs++; ++ return kmalloc(dev->data_bytes_per_chunk, GFP_NOFS); ++ ++} ++ ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer) ++{ ++ int i; ++ ++ dev->temp_in_use--; ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) { ++ if (dev->temp_buffer[i].buffer == buffer) { ++ dev->temp_buffer[i].in_use = 0; ++ return; ++ } ++ } ++ ++ if (buffer) { ++ /* assume it is an unmanaged one. */ ++ yaffs_trace(YAFFS_TRACE_BUFFERS, ++ "Releasing unmanaged temp buffer"); ++ kfree(buffer); ++ dev->unmanaged_buffer_deallocs++; ++ } ++ ++} ++ ++/* ++ * Functions for robustisizing TODO ++ * ++ */ ++ ++static void yaffs_handle_chunk_wr_ok(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) data; ++ (void) tags; ++} ++ ++static void yaffs_handle_chunk_update(struct yaffs_dev *dev, int nand_chunk, ++ const struct yaffs_ext_tags *tags) ++{ ++ (void) dev; ++ (void) nand_chunk; ++ (void) tags; ++} ++ ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ if (!bi->gc_prioritise) { ++ bi->gc_prioritise = 1; ++ dev->has_pending_prioritised_gc = 1; ++ bi->chunk_error_strikes++; ++ ++ if (bi->chunk_error_strikes > 3) { ++ bi->needs_retiring = 1; /* Too many stikes, so retire */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Block struck out"); ++ ++ } ++ } ++} ++ ++static void yaffs_handle_chunk_wr_error(struct yaffs_dev *dev, int nand_chunk, ++ int erased_ok) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs_handle_chunk_error(dev, bi); ++ ++ if (erased_ok) { ++ /* Was an actual write failure, ++ * so mark the block for retirement.*/ ++ bi->needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d needs retiring", flash_block); ++ } ++ ++ /* Delete the chunk */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++} ++ ++/* ++ * Verification code ++ */ ++ ++/* ++ * Simple hash function. Needs to have a reasonable spread ++ */ ++ ++static inline int yaffs_hash_fn(int n) ++{ ++ if (n < 0) ++ n = -n; ++ return n % YAFFS_NOBJECT_BUCKETS; ++} ++ ++/* ++ * Access functions to useful fake objects. ++ * Note that root might have a presence in NAND if permissions are set. ++ */ ++ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev) ++{ ++ return dev->root_dir; ++} ++ ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev) ++{ ++ return dev->lost_n_found; ++} ++ ++/* ++ * Erased NAND checking functions ++ */ ++ ++int yaffs_check_ff(u8 *buffer, int n_bytes) ++{ ++ /* Horrible, slow implementation */ ++ while (n_bytes--) { ++ if (*buffer != 0xff) ++ return 0; ++ buffer++; ++ } ++ return 1; ++} ++ ++static int yaffs_check_chunk_erased(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int retval = YAFFS_OK; ++ u8 *data = yaffs_get_temp_buffer(dev); ++ struct yaffs_ext_tags tags; ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, data, &tags); ++ ++ if (tags.ecc_result > YAFFS_ECC_RESULT_NO_ERROR) ++ retval = YAFFS_FAIL; ++ ++ if (!yaffs_check_ff(data, dev->data_bytes_per_chunk) || ++ tags.chunk_used) { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not erased", nand_chunk); ++ retval = YAFFS_FAIL; ++ } ++ ++ yaffs_release_temp_buffer(dev, data); ++ ++ return retval; ++ ++} ++ ++static int yaffs_verify_chunk_written(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = YAFFS_OK; ++ struct yaffs_ext_tags temp_tags; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ int result; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, nand_chunk, buffer, &temp_tags); ++ if (memcmp(buffer, data, dev->data_bytes_per_chunk) || ++ temp_tags.obj_id != tags->obj_id || ++ temp_tags.chunk_id != tags->chunk_id || ++ temp_tags.n_bytes != tags->n_bytes) ++ retval = YAFFS_FAIL; ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ return retval; ++} ++ ++ ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks) ++{ ++ int reserved_chunks; ++ int reserved_blocks = dev->param.n_reserved_blocks; ++ int checkpt_blocks; ++ ++ checkpt_blocks = yaffs_calc_checkpt_blocks_required(dev); ++ ++ reserved_chunks = ++ (reserved_blocks + checkpt_blocks) * dev->param.chunks_per_block; ++ ++ return (dev->n_free_chunks > (reserved_chunks + n_chunks)); ++} ++ ++static int yaffs_find_alloc_block(struct yaffs_dev *dev) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ ++ if (dev->n_erased_blocks < 1) { ++ /* Hoosterman we've got a problem. ++ * Can't get space to gc ++ */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no more erased blocks"); ++ ++ return -1; ++ } ++ ++ /* Find an empty block. */ ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ dev->alloc_block_finder++; ++ if (dev->alloc_block_finder < dev->internal_start_block ++ || dev->alloc_block_finder > dev->internal_end_block) { ++ dev->alloc_block_finder = dev->internal_start_block; ++ } ++ ++ bi = yaffs_get_block_info(dev, dev->alloc_block_finder); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ bi->block_state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->seq_number++; ++ bi->seq_number = dev->seq_number; ++ dev->n_erased_blocks--; ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, ++ "Allocated block %d, seq %d, %d left" , ++ dev->alloc_block_finder, dev->seq_number, ++ dev->n_erased_blocks); ++ return dev->alloc_block_finder; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs tragedy: no more erased blocks, but there should have been %d", ++ dev->n_erased_blocks); ++ ++ return -1; ++} ++ ++static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver, ++ struct yaffs_block_info **block_ptr) ++{ ++ int ret_val; ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block < 0) { ++ /* Get next block to allocate off */ ++ dev->alloc_block = yaffs_find_alloc_block(dev); ++ dev->alloc_page = 0; ++ } ++ ++ if (!use_reserver && !yaffs_check_alloc_available(dev, 1)) { ++ /* No space unless we're allowed to use the reserve. */ ++ return -1; ++ } ++ ++ if (dev->n_erased_blocks < dev->param.n_reserved_blocks ++ && dev->alloc_page == 0) ++ yaffs_trace(YAFFS_TRACE_ALLOCATE, "Allocating reserve"); ++ ++ /* Next page please.... */ ++ if (dev->alloc_block >= 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ ++ ret_val = (dev->alloc_block * dev->param.chunks_per_block) + ++ dev->alloc_page; ++ bi->pages_in_use++; ++ yaffs_set_chunk_bit(dev, dev->alloc_block, dev->alloc_page); ++ ++ dev->alloc_page++; ++ ++ dev->n_free_chunks--; ++ ++ /* If the block is full set the state to full */ ++ if (dev->alloc_page >= dev->param.chunks_per_block) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ if (block_ptr) ++ *block_ptr = bi; ++ ++ return ret_val; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!"); ++ ++ return -1; ++} ++ ++static int yaffs_get_erased_chunks(struct yaffs_dev *dev) ++{ ++ int n; ++ ++ n = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ if (dev->alloc_block > 0) ++ n += (dev->param.chunks_per_block - dev->alloc_page); ++ ++ return n; ++ ++} ++ ++/* ++ * yaffs_skip_rest_of_block() skips over the rest of the allocation block ++ * if we don't want to write to it. ++ */ ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev) ++{ ++ struct yaffs_block_info *bi; ++ ++ if (dev->alloc_block > 0) { ++ bi = yaffs_get_block_info(dev, dev->alloc_block); ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ } ++} ++ ++static int yaffs_write_new_chunk(struct yaffs_dev *dev, ++ const u8 *data, ++ struct yaffs_ext_tags *tags, int use_reserver) ++{ ++ int attempts = 0; ++ int write_ok = 0; ++ int chunk; ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ do { ++ struct yaffs_block_info *bi = 0; ++ int erased_ok = 0; ++ ++ chunk = yaffs_alloc_chunk(dev, use_reserver, &bi); ++ if (chunk < 0) { ++ /* no space */ ++ break; ++ } ++ ++ /* First check this chunk is erased, if it needs ++ * checking. The checking policy (unless forced ++ * always on) is as follows: ++ * ++ * Check the first page we try to write in a block. ++ * If the check passes then we don't need to check any ++ * more. If the check fails, we check again... ++ * If the block has been erased, we don't need to check. ++ * ++ * However, if the block has been prioritised for gc, ++ * then we think there might be something odd about ++ * this block and stop using it. ++ * ++ * Rationale: We should only ever see chunks that have ++ * not been erased if there was a partially written ++ * chunk due to power loss. This checking policy should ++ * catch that case with very few checks and thus save a ++ * lot of checks that are most likely not needed. ++ * ++ * Mods to the above ++ * If an erase check fails or the write fails we skip the ++ * rest of the block. ++ */ ++ ++ /* let's give it a try */ ++ attempts++; ++ ++ if (dev->param.always_check_erased) ++ bi->skip_erased_check = 0; ++ ++ if (!bi->skip_erased_check) { ++ erased_ok = yaffs_check_chunk_erased(dev, chunk); ++ if (erased_ok != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs chunk %d was not erased", ++ chunk); ++ ++ /* If not erased, delete this one, ++ * skip rest of block and ++ * try another chunk */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ yaffs_skip_rest_of_block(dev); ++ continue; ++ } ++ } ++ ++ write_ok = yaffs_wr_chunk_tags_nand(dev, chunk, data, tags); ++ ++ if (!bi->skip_erased_check) ++ write_ok = ++ yaffs_verify_chunk_written(dev, chunk, data, tags); ++ ++ if (write_ok != YAFFS_OK) { ++ /* Clean up aborted write, skip to next block and ++ * try another chunk */ ++ yaffs_handle_chunk_wr_error(dev, chunk, erased_ok); ++ continue; ++ } ++ ++ bi->skip_erased_check = 1; ++ ++ /* Copy the data into the robustification buffer */ ++ yaffs_handle_chunk_wr_ok(dev, chunk, data, tags); ++ ++ } while (write_ok != YAFFS_OK && ++ (yaffs_wr_attempts <= 0 || attempts <= yaffs_wr_attempts)); ++ ++ if (!write_ok) ++ chunk = -1; ++ ++ if (attempts > 1) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>> yaffs write required %d attempts", ++ attempts); ++ dev->n_retried_writes += (attempts - 1); ++ } ++ ++ return chunk; ++} ++ ++/* ++ * Block retiring for handling a broken block. ++ */ ++ ++static void yaffs_retire_block(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, flash_block); ++ ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ if (yaffs_mark_bad(dev, flash_block) != YAFFS_OK) { ++ if (yaffs_erase_block(dev, flash_block) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to mark bad and erase block %d", ++ flash_block); ++ } else { ++ struct yaffs_ext_tags tags; ++ int chunk_id = ++ flash_block * dev->param.chunks_per_block; ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ memset(&tags, 0, sizeof(tags)); ++ tags.seq_number = YAFFS_SEQUENCE_BAD_BLOCK; ++ if (dev->tagger.write_chunk_tags_fn(dev, chunk_id - ++ dev->chunk_offset, ++ buffer, ++ &tags) != YAFFS_OK) ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Failed to write bad block marker to block %d", ++ flash_block); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ } ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ bi->gc_prioritise = 0; ++ bi->needs_retiring = 0; ++ ++ dev->n_retired_blocks++; ++} ++ ++/*---------------- Name handling functions ------------*/ ++ ++static u16 yaffs_calc_name_sum(const YCHAR *name) ++{ ++ u16 sum = 0; ++ u16 i = 1; ++ ++ if (!name) ++ return 0; ++ ++ while ((*name) && i < (YAFFS_MAX_NAME_LENGTH / 2)) { ++ ++ /* 0x1f mask is case insensitive */ ++ sum += ((*name) & 0x1f) * i; ++ i++; ++ name++; ++ } ++ return sum; ++} ++ ++ ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ memset(obj->short_name, 0, sizeof(obj->short_name)); ++ ++ if (name && !name[0]) { ++ yaffs_fix_null_name(obj, obj->short_name, ++ YAFFS_SHORT_NAME_LENGTH); ++ name = obj->short_name; ++ } else if (name && ++ strnlen(name, YAFFS_SHORT_NAME_LENGTH + 1) <= ++ YAFFS_SHORT_NAME_LENGTH) { ++ strcpy(obj->short_name, name); ++ } ++ ++ obj->sum = yaffs_calc_name_sum(name); ++} ++ ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ YCHAR tmp_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ memset(tmp_name, 0, sizeof(tmp_name)); ++ yaffs_load_name_from_oh(obj->my_dev, tmp_name, oh->name, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_set_obj_name(obj, tmp_name); ++#else ++ yaffs_set_obj_name(obj, oh->name); ++#endif ++} ++ ++loff_t yaffs_max_file_size(struct yaffs_dev *dev) ++{ ++ if(sizeof(loff_t) < 8) ++ return YAFFS_MAX_FILE_SIZE_32; ++ else ++ return ((loff_t) YAFFS_MAX_CHUNK_ID) * dev->data_bytes_per_chunk; ++} ++ ++/*-------------------- TNODES ------------------- ++ ++ * List of spare tnodes ++ * The list is hooked together using the first pointer ++ * in the tnode. ++ */ ++ ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) ++{ ++ struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); ++ ++ if (tn) { ++ memset(tn, 0, dev->tnode_size); ++ dev->n_tnodes++; ++ } ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return tn; ++} ++ ++/* FreeTnode frees up a tnode and puts it back on the free list */ ++static void yaffs_free_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn) ++{ ++ yaffs_free_raw_tnode(dev, tn); ++ dev->n_tnodes--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++static void yaffs_deinit_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ yaffs_deinit_raw_tnodes_and_objs(dev); ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++} ++ ++static void yaffs_load_tnode_0(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos, unsigned val) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 mask; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ val >>= dev->chunk_grp_bits; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ mask = dev->tnode_mask << bit_in_word; ++ ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val << bit_in_word)); ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ mask = ++ dev->tnode_mask >> bit_in_word; ++ map[word_in_map] &= ~mask; ++ map[word_in_map] |= (mask & (val >> bit_in_word)); ++ } ++} ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos) ++{ ++ u32 *map = (u32 *) tn; ++ u32 bit_in_map; ++ u32 bit_in_word; ++ u32 word_in_map; ++ u32 val; ++ ++ pos &= YAFFS_TNODES_LEVEL0_MASK; ++ ++ bit_in_map = pos * dev->tnode_width; ++ word_in_map = bit_in_map / 32; ++ bit_in_word = bit_in_map & (32 - 1); ++ ++ val = map[word_in_map] >> bit_in_word; ++ ++ if (dev->tnode_width > (32 - bit_in_word)) { ++ bit_in_word = (32 - bit_in_word); ++ word_in_map++; ++ val |= (map[word_in_map] << bit_in_word); ++ } ++ ++ val &= dev->tnode_mask; ++ val <<= dev->chunk_grp_bits; ++ ++ return val; ++} ++ ++/* ------------------- End of individual tnode manipulation -----------------*/ ++ ++/* ---------Functions to manipulate the look-up tree (made up of tnodes) ------ ++ * The look up tree is represented by the top tnode and the number of top_level ++ * in the tree. 0 means only the level 0 tnode is in the tree. ++ */ ++ ++/* FindLevel0Tnode finds the level 0 tnode, if one exists. */ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id) ++{ ++ struct yaffs_tnode *tn = file_struct->top; ++ u32 i; ++ int required_depth; ++ int level = file_struct->top_level; ++ ++ (void) dev; ++ ++ /* Check sane level and chunk Id */ ++ if (level < 0 || level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ i = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (i) { ++ i >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) ++ return NULL; /* Not tall enough, so we can't find it */ ++ ++ /* Traverse down to level 0 */ ++ while (level > 0 && tn) { ++ tn = tn->internal[(chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (level - 1) * ++ YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK]; ++ level--; ++ } ++ ++ return tn; ++} ++ ++/* add_find_tnode_0 finds the level 0 tnode if it exists, ++ * otherwise first expands the tree. ++ * This happens in two steps: ++ * 1. If the tree isn't tall enough, then make it taller. ++ * 2. Scan down the tree towards the level 0 tnode adding tnodes if required. ++ * ++ * Used when modifying the tree. ++ * ++ * If the tn argument is NULL, then a fresh tnode will be added otherwise the ++ * specified tn will be plugged into the ttree. ++ */ ++ ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn) ++{ ++ int required_depth; ++ int i; ++ int l; ++ struct yaffs_tnode *tn; ++ u32 x; ++ ++ /* Check sane level and page Id */ ++ if (file_struct->top_level < 0 || ++ file_struct->top_level > YAFFS_TNODES_MAX_LEVEL) ++ return NULL; ++ ++ if (chunk_id > YAFFS_MAX_CHUNK_ID) ++ return NULL; ++ ++ /* First check we're tall enough (ie enough top_level) */ ++ ++ x = chunk_id >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ if (required_depth > file_struct->top_level) { ++ /* Not tall enough, gotta make the tree taller */ ++ for (i = file_struct->top_level; i < required_depth; i++) { ++ ++ tn = yaffs_get_tnode(dev); ++ ++ if (tn) { ++ tn->internal[0] = file_struct->top; ++ file_struct->top = tn; ++ file_struct->top_level++; ++ } else { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs: no more tnodes"); ++ return NULL; ++ } ++ } ++ } ++ ++ /* Traverse down to level 0, adding anything we need */ ++ ++ l = file_struct->top_level; ++ tn = file_struct->top; ++ ++ if (l > 0) { ++ while (l > 0 && tn) { ++ x = (chunk_id >> ++ (YAFFS_TNODES_LEVEL0_BITS + ++ (l - 1) * YAFFS_TNODES_INTERNAL_BITS)) & ++ YAFFS_TNODES_INTERNAL_MASK; ++ ++ if ((l > 1) && !tn->internal[x]) { ++ /* Add missing non-level-zero tnode */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } else if (l == 1) { ++ /* Looking from level 1 at level 0 */ ++ if (passed_tn) { ++ /* If we already have one, release it */ ++ if (tn->internal[x]) ++ yaffs_free_tnode(dev, ++ tn->internal[x]); ++ tn->internal[x] = passed_tn; ++ ++ } else if (!tn->internal[x]) { ++ /* Don't have one, none passed in */ ++ tn->internal[x] = yaffs_get_tnode(dev); ++ if (!tn->internal[x]) ++ return NULL; ++ } ++ } ++ ++ tn = tn->internal[x]; ++ l--; ++ } ++ } else { ++ /* top is level 0 */ ++ if (passed_tn) { ++ memcpy(tn, passed_tn, ++ (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8); ++ yaffs_free_tnode(dev, passed_tn); ++ } ++ } ++ ++ return tn; ++} ++ ++static int yaffs_tags_match(const struct yaffs_ext_tags *tags, int obj_id, ++ int chunk_obj) ++{ ++ return (tags->chunk_id == chunk_obj && ++ tags->obj_id == obj_id && ++ !tags->is_deleted) ? 1 : 0; ++ ++} ++ ++static int yaffs_find_chunk_in_group(struct yaffs_dev *dev, int the_chunk, ++ struct yaffs_ext_tags *tags, int obj_id, ++ int inode_chunk) ++{ ++ int j; ++ ++ for (j = 0; the_chunk && j < dev->chunk_grp_size; j++) { ++ if (yaffs_check_chunk_bit ++ (dev, the_chunk / dev->param.chunks_per_block, ++ the_chunk % dev->param.chunks_per_block)) { ++ ++ if (dev->chunk_grp_size == 1) ++ return the_chunk; ++ else { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ tags); ++ if (yaffs_tags_match(tags, ++ obj_id, inode_chunk)) { ++ /* found it; */ ++ return the_chunk; ++ } ++ } ++ } ++ the_chunk++; ++ } ++ return -1; ++} ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /*Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ int ret_val = -1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ return ret_val; ++} ++ ++static int yaffs_find_del_file_chunk(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags) ++{ ++ /* Get the Tnode, then get the level 0 offset chunk offset */ ++ struct yaffs_tnode *tn; ++ int the_chunk = -1; ++ struct yaffs_ext_tags local_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ int ret_val = -1; ++ ++ if (!tags) { ++ /* Passed a NULL, so use our own tags space */ ++ tags = &local_tags; ++ } ++ ++ tn = yaffs_find_tnode_0(dev, &in->variant.file_variant, inode_chunk); ++ ++ if (!tn) ++ return ret_val; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ ret_val = yaffs_find_chunk_in_group(dev, the_chunk, tags, in->obj_id, ++ inode_chunk); ++ ++ /* Delete the entry in the filestructure (if found) */ ++ if (ret_val != -1) ++ yaffs_load_tnode_0(dev, tn, inode_chunk, 0); ++ ++ return ret_val; ++} ++ ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan) ++{ ++ /* NB in_scan is zero unless scanning. ++ * For forward scanning, in_scan is > 0; ++ * for backward scanning in_scan is < 0 ++ * ++ * nand_chunk = 0 is a dummy insert to make sure the tnodes are there. ++ */ ++ ++ struct yaffs_tnode *tn; ++ struct yaffs_dev *dev = in->my_dev; ++ int existing_cunk; ++ struct yaffs_ext_tags existing_tags; ++ struct yaffs_ext_tags new_tags; ++ unsigned existing_serial, new_serial; ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) { ++ /* Just ignore an attempt at putting a chunk into a non-file ++ * during scanning. ++ * If it is not during Scanning then something went wrong! ++ */ ++ if (!in_scan) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy:attempt to put data chunk into a non-file" ++ ); ++ BUG(); ++ } ++ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ ++ tn = yaffs_add_find_tnode_0(dev, ++ &in->variant.file_variant, ++ inode_chunk, NULL); ++ if (!tn) ++ return YAFFS_FAIL; ++ ++ if (!nand_chunk) ++ /* Dummy insert, bail now */ ++ return YAFFS_OK; ++ ++ existing_cunk = yaffs_get_group_base(dev, tn, inode_chunk); ++ ++ if (in_scan != 0) { ++ /* If we're scanning then we need to test for duplicates ++ * NB This does not need to be efficient since it should only ++ * happen when the power fails during a write, then only one ++ * chunk should ever be affected. ++ * ++ * Correction for YAFFS2: This could happen quite a lot and we ++ * need to think about efficiency! TODO ++ * Update: For backward scanning we don't need to re-read tags ++ * so this is quite cheap. ++ */ ++ ++ if (existing_cunk > 0) { ++ /* NB Right now existing chunk will not be real ++ * chunk_id if the chunk group size > 1 ++ * thus we have to do a FindChunkInFile to get the ++ * real chunk id. ++ * ++ * We have a duplicate now we need to decide which ++ * one to use: ++ * ++ * Backwards scanning YAFFS2: The old one is what ++ * we use, dump the new one. ++ * YAFFS1: Get both sets of tags and compare serial ++ * numbers. ++ */ ++ ++ if (in_scan > 0) { ++ /* Only do this for forward scanning */ ++ yaffs_rd_chunk_tags_nand(dev, ++ nand_chunk, ++ NULL, &new_tags); ++ ++ /* Do a proper find */ ++ existing_cunk = ++ yaffs_find_chunk_in_file(in, inode_chunk, ++ &existing_tags); ++ } ++ ++ if (existing_cunk <= 0) { ++ /*Hoosterman - how did this happen? */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: existing chunk < 0 in scan" ++ ); ++ ++ } ++ ++ /* NB The deleted flags should be false, otherwise ++ * the chunks will not be loaded during a scan ++ */ ++ ++ if (in_scan > 0) { ++ new_serial = new_tags.serial_number; ++ existing_serial = existing_tags.serial_number; ++ } ++ ++ if ((in_scan > 0) && ++ (existing_cunk <= 0 || ++ ((existing_serial + 1) & 3) == new_serial)) { ++ /* Forward scanning. ++ * Use new ++ * Delete the old one and drop through to ++ * update the tnode ++ */ ++ yaffs_chunk_del(dev, existing_cunk, 1, ++ __LINE__); ++ } else { ++ /* Backward scanning or we want to use the ++ * existing one ++ * Delete the new one and return early so that ++ * the tnode isn't changed ++ */ ++ yaffs_chunk_del(dev, nand_chunk, 1, __LINE__); ++ return YAFFS_OK; ++ } ++ } ++ ++ } ++ ++ if (existing_cunk == 0) ++ in->n_data_chunks++; ++ ++ yaffs_load_tnode_0(dev, tn, inode_chunk, nand_chunk); ++ ++ return YAFFS_OK; ++} ++ ++static void yaffs_soft_del_chunk(struct yaffs_dev *dev, int chunk) ++{ ++ struct yaffs_block_info *the_block; ++ unsigned block_no; ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, "soft delete chunk %d", chunk); ++ ++ block_no = chunk / dev->param.chunks_per_block; ++ the_block = yaffs_get_block_info(dev, block_no); ++ if (the_block) { ++ the_block->soft_del_pages++; ++ dev->n_free_chunks++; ++ yaffs2_update_oldest_dirty_seq(dev, block_no, the_block); ++ } ++} ++ ++/* SoftDeleteWorker scans backwards through the tnode tree and soft deletes all ++ * the chunks in the file. ++ * All soft deleting does is increment the block's softdelete count and pulls ++ * the chunk out of the tnode. ++ * Thus, essentially this is the same as DeleteWorker except that the chunks ++ * are soft deleted. ++ */ ++ ++static int yaffs_soft_del_worker(struct yaffs_obj *in, struct yaffs_tnode *tn, ++ u32 level, int chunk_offset) ++{ ++ int i; ++ int the_chunk; ++ int all_done = 1; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = YAFFS_NTNODES_INTERNAL - 1; ++ all_done && i >= 0; ++ i--) { ++ if (tn->internal[i]) { ++ all_done = ++ yaffs_soft_del_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) ++ + i); ++ if (all_done) { ++ yaffs_free_tnode(dev, ++ tn->internal[i]); ++ tn->internal[i] = NULL; ++ } else { ++ /* Can this happen? */ ++ } ++ } ++ } ++ return (all_done) ? 1 : 0; ++ } ++ ++ /* level 0 */ ++ for (i = YAFFS_NTNODES_LEVEL0 - 1; i >= 0; i--) { ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk) { ++ yaffs_soft_del_chunk(dev, the_chunk); ++ yaffs_load_tnode_0(dev, tn, i, 0); ++ } ++ } ++ return 1; ++} ++ ++static void yaffs_remove_obj_from_dir(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_obj *parent; ++ ++ yaffs_verify_obj_in_dir(obj); ++ parent = obj->parent; ++ ++ yaffs_verify_dir(parent); ++ ++ if (dev && dev->param.remove_obj_fn) ++ dev->param.remove_obj_fn(obj); ++ ++ list_del_init(&obj->siblings); ++ obj->parent = NULL; ++ ++ yaffs_verify_dir(parent); ++} ++ ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj) ++{ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a null pointer directory" ++ ); ++ BUG(); ++ return; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: Trying to add an object to a non-directory" ++ ); ++ BUG(); ++ } ++ ++ if (obj->siblings.prev == NULL) { ++ /* Not initialised */ ++ BUG(); ++ } ++ ++ yaffs_verify_dir(directory); ++ ++ yaffs_remove_obj_from_dir(obj); ++ ++ /* Now add it */ ++ list_add(&obj->siblings, &directory->variant.dir_variant.children); ++ obj->parent = directory; ++ ++ if (directory == obj->my_dev->unlinked_dir ++ || directory == obj->my_dev->del_dir) { ++ obj->unlinked = 1; ++ obj->my_dev->n_unlinked_files++; ++ obj->rename_allowed = 0; ++ } ++ ++ yaffs_verify_dir(directory); ++ yaffs_verify_obj_in_dir(obj); ++} ++ ++static int yaffs_change_obj_name(struct yaffs_obj *obj, ++ struct yaffs_obj *new_dir, ++ const YCHAR *new_name, int force, int shadows) ++{ ++ int unlink_op; ++ int del_op; ++ struct yaffs_obj *existing_target; ++ ++ if (new_dir == NULL) ++ new_dir = obj->parent; /* use the old directory */ ++ ++ if (new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_change_obj_name: new_dir is not a directory" ++ ); ++ BUG(); ++ } ++ ++ unlink_op = (new_dir == obj->my_dev->unlinked_dir); ++ del_op = (new_dir == obj->my_dev->del_dir); ++ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ ++ /* If the object is a file going into the unlinked directory, ++ * then it is OK to just stuff it in since duplicate names are OK. ++ * else only proceed if the new name does not exist and we're putting ++ * it into a directory. ++ */ ++ if (!(unlink_op || del_op || force || ++ shadows > 0 || !existing_target) || ++ new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ return YAFFS_FAIL; ++ ++ yaffs_set_obj_name(obj, new_name); ++ obj->dirty = 1; ++ yaffs_add_obj_to_dir(new_dir, obj); ++ ++ if (unlink_op) ++ obj->unlinked = 1; ++ ++ /* If it is a deletion then we mark it as a shrink for gc */ ++ if (yaffs_update_oh(obj, new_name, 0, del_op, shadows, NULL) >= 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++/*------------------------ Short Operations Cache ------------------------------ ++ * In many situations where there is no high level buffering a lot of ++ * reads might be short sequential reads, and a lot of writes may be short ++ * sequential writes. eg. scanning/writing a jpeg file. ++ * In these cases, a short read/write cache can provide a huge perfomance ++ * benefit with dumb-as-a-rock code. ++ * In Linux, the page cache provides read buffering and the short op cache ++ * provides write buffering. ++ * ++ * There are a small number (~10) of cache chunks per device so that we don't ++ * need a very intelligent search. ++ */ ++ ++static int yaffs_obj_cache_dirty(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ struct yaffs_cache *cache; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ for (i = 0; i < n_caches; i++) { ++ cache = &dev->cache[i]; ++ if (cache->object == obj && cache->dirty) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static void yaffs_flush_file_cache(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int lowest = -99; /* Stop compiler whining. */ ++ int i; ++ struct yaffs_cache *cache; ++ int chunk_written = 0; ++ int n_caches = obj->my_dev->param.n_caches; ++ ++ if (n_caches < 1) ++ return; ++ do { ++ cache = NULL; ++ ++ /* Find the lowest dirty chunk for this object */ ++ for (i = 0; i < n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].dirty) { ++ if (!cache || ++ dev->cache[i].chunk_id < lowest) { ++ cache = &dev->cache[i]; ++ lowest = cache->chunk_id; ++ } ++ } ++ } ++ ++ if (cache && !cache->locked) { ++ /* Write it out and free it up */ ++ chunk_written = ++ yaffs_wr_data_obj(cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ cache->object = NULL; ++ } ++ } while (cache && chunk_written > 0); ++ ++ if (cache) ++ /* Hoosterman, disk full while writing cache out. */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: no space during cache write"); ++} ++ ++/*yaffs_flush_whole_cache(dev) ++ * ++ * ++ */ ++ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int n_caches = dev->param.n_caches; ++ int i; ++ ++ /* Find a dirty object in the cache and flush it... ++ * until there are no further dirty objects. ++ */ ++ do { ++ obj = NULL; ++ for (i = 0; i < n_caches && !obj; i++) { ++ if (dev->cache[i].object && dev->cache[i].dirty) ++ obj = dev->cache[i].object; ++ } ++ if (obj) ++ yaffs_flush_file_cache(obj); ++ } while (obj); ++ ++} ++ ++/* Grab us a cache chunk for use. ++ * First look for an empty one. ++ * Then look for the least recently used non-dirty one. ++ * Then look for the least recently used dirty one...., flush and look again. ++ */ ++static struct yaffs_cache *yaffs_grab_chunk_worker(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ if (dev->param.n_caches > 0) { ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (!dev->cache[i].object) ++ return &dev->cache[i]; ++ } ++ } ++ return NULL; ++} ++ ++static struct yaffs_cache *yaffs_grab_chunk_cache(struct yaffs_dev *dev) ++{ ++ struct yaffs_cache *cache; ++ struct yaffs_obj *the_obj; ++ int usage; ++ int i; ++ int pushout; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ /* Try find a non-dirty one... */ ++ ++ cache = yaffs_grab_chunk_worker(dev); ++ ++ if (!cache) { ++ /* They were all dirty, find the LRU object and flush ++ * its cache, then find again. ++ * NB what's here is not very accurate, ++ * we actually flush the object with the LRU chunk. ++ */ ++ ++ /* With locking we can't assume we can use entry zero, ++ * Set the_obj to a valid pointer for Coverity. */ ++ the_obj = dev->cache[0].object; ++ usage = -1; ++ cache = NULL; ++ pushout = -1; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object && ++ !dev->cache[i].locked && ++ (dev->cache[i].last_use < usage || ++ !cache)) { ++ usage = dev->cache[i].last_use; ++ the_obj = dev->cache[i].object; ++ cache = &dev->cache[i]; ++ pushout = i; ++ } ++ } ++ ++ if (!cache || cache->dirty) { ++ /* Flush and try again */ ++ yaffs_flush_file_cache(the_obj); ++ cache = yaffs_grab_chunk_worker(dev); ++ } ++ } ++ return cache; ++} ++ ++/* Find a cached chunk */ ++static struct yaffs_cache *yaffs_find_chunk_cache(const struct yaffs_obj *obj, ++ int chunk_id) ++{ ++ struct yaffs_dev *dev = obj->my_dev; ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return NULL; ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == obj && ++ dev->cache[i].chunk_id == chunk_id) { ++ dev->cache_hits++; ++ ++ return &dev->cache[i]; ++ } ++ } ++ return NULL; ++} ++ ++/* Mark the chunk for the least recently used algorithym */ ++static void yaffs_use_cache(struct yaffs_dev *dev, struct yaffs_cache *cache, ++ int is_write) ++{ ++ int i; ++ ++ if (dev->param.n_caches < 1) ++ return; ++ ++ if (dev->cache_last_use < 0 || ++ dev->cache_last_use > 100000000) { ++ /* Reset the cache usages */ ++ for (i = 1; i < dev->param.n_caches; i++) ++ dev->cache[i].last_use = 0; ++ ++ dev->cache_last_use = 0; ++ } ++ dev->cache_last_use++; ++ cache->last_use = dev->cache_last_use; ++ ++ if (is_write) ++ cache->dirty = 1; ++} ++ ++/* Invalidate a single cache page. ++ * Do this when a whole page gets written, ++ * ie the short cache for this page is no longer valid. ++ */ ++static void yaffs_invalidate_chunk_cache(struct yaffs_obj *object, int chunk_id) ++{ ++ struct yaffs_cache *cache; ++ ++ if (object->my_dev->param.n_caches > 0) { ++ cache = yaffs_find_chunk_cache(object, chunk_id); ++ ++ if (cache) ++ cache->object = NULL; ++ } ++} ++ ++/* Invalidate all the cache pages associated with this object ++ * Do this whenever ther file is deleted or resized. ++ */ ++static void yaffs_invalidate_whole_cache(struct yaffs_obj *in) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.n_caches > 0) { ++ /* Invalidate it. */ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].object == in) ++ dev->cache[i].object = NULL; ++ } ++ } ++} ++ ++static void yaffs_unhash_obj(struct yaffs_obj *obj) ++{ ++ int bucket; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ /* If it is still linked into the bucket list, free from the list */ ++ if (!list_empty(&obj->hash_link)) { ++ list_del_init(&obj->hash_link); ++ bucket = yaffs_hash_fn(obj->obj_id); ++ dev->obj_bucket[bucket].count--; ++ } ++} ++ ++/* FreeObject frees up a Object and puts it back on the free list */ ++static void yaffs_free_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) { ++ BUG(); ++ return; ++ } ++ dev = obj->my_dev; ++ yaffs_trace(YAFFS_TRACE_OS, "FreeObject %p inode %p", ++ obj, obj->my_inode); ++ if (obj->parent) ++ BUG(); ++ if (!list_empty(&obj->siblings)) ++ BUG(); ++ ++ if (obj->my_inode) { ++ /* We're still hooked up to a cached inode. ++ * Don't delete now, but mark for later deletion ++ */ ++ obj->defered_free = 1; ++ return; ++ } ++ ++ yaffs_unhash_obj(obj); ++ ++ yaffs_free_raw_obj(dev, obj); ++ dev->n_obj--; ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++} ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj) ++{ ++ if (obj->defered_free) ++ yaffs_free_obj(obj); ++} ++ ++static int yaffs_generic_obj_del(struct yaffs_obj *in) ++{ ++ /* Iinvalidate the file's data in the cache, without flushing. */ ++ yaffs_invalidate_whole_cache(in); ++ ++ if (in->my_dev->param.is_yaffs2 && in->parent != in->my_dev->del_dir) { ++ /* Move to unlinked directory so we have a deletion record */ ++ yaffs_change_obj_name(in, in->my_dev->del_dir, _Y("deleted"), 0, ++ 0); ++ } ++ ++ yaffs_remove_obj_from_dir(in); ++ yaffs_chunk_del(in->my_dev, in->hdr_chunk, 1, __LINE__); ++ in->hdr_chunk = 0; ++ ++ yaffs_free_obj(in); ++ return YAFFS_OK; ++ ++} ++ ++static void yaffs_soft_del_file(struct yaffs_obj *obj) ++{ ++ if (!obj->deleted || ++ obj->variant_type != YAFFS_OBJECT_TYPE_FILE || ++ obj->soft_del) ++ return; ++ ++ if (obj->n_data_chunks <= 0) { ++ /* Empty file with no duplicate object headers, ++ * just delete it immediately */ ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: Deleting empty file %d", ++ obj->obj_id); ++ yaffs_generic_obj_del(obj); ++ } else { ++ yaffs_soft_del_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant. ++ file_variant.top_level, 0); ++ obj->soft_del = 1; ++ } ++} ++ ++/* Pruning removes any part of the file structure tree that is beyond the ++ * bounds of the file (ie that does not point to chunks). ++ * ++ * A file should only get pruned when its size is reduced. ++ * ++ * Before pruning, the chunks must be pulled from the tree and the ++ * level 0 tnode entries must be zeroed out. ++ * Could also use this for file deletion, but that's probably better handled ++ * by a special case. ++ * ++ * This function is recursive. For levels > 0 the function is called again on ++ * any sub-tree. For level == 0 we just check if the sub-tree has data. ++ * If there is no data in a subtree then it is pruned. ++ */ ++ ++static struct yaffs_tnode *yaffs_prune_worker(struct yaffs_dev *dev, ++ struct yaffs_tnode *tn, u32 level, ++ int del0) ++{ ++ int i; ++ int has_data; ++ ++ if (!tn) ++ return tn; ++ ++ has_data = 0; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) { ++ tn->internal[i] = ++ yaffs_prune_worker(dev, ++ tn->internal[i], ++ level - 1, ++ (i == 0) ? del0 : 1); ++ } ++ ++ if (tn->internal[i]) ++ has_data++; ++ } ++ } else { ++ int tnode_size_u32 = dev->tnode_size / sizeof(u32); ++ u32 *map = (u32 *) tn; ++ ++ for (i = 0; !has_data && i < tnode_size_u32; i++) { ++ if (map[i]) ++ has_data++; ++ } ++ } ++ ++ if (has_data == 0 && del0) { ++ /* Free and return NULL */ ++ yaffs_free_tnode(dev, tn); ++ tn = NULL; ++ } ++ return tn; ++} ++ ++static int yaffs_prune_tree(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct) ++{ ++ int i; ++ int has_data; ++ int done = 0; ++ struct yaffs_tnode *tn; ++ ++ if (file_struct->top_level < 1) ++ return YAFFS_OK; ++ ++ file_struct->top = ++ yaffs_prune_worker(dev, file_struct->top, file_struct->top_level, 0); ++ ++ /* Now we have a tree with all the non-zero branches NULL but ++ * the height is the same as it was. ++ * Let's see if we can trim internal tnodes to shorten the tree. ++ * We can do this if only the 0th element in the tnode is in use ++ * (ie all the non-zero are NULL) ++ */ ++ ++ while (file_struct->top_level && !done) { ++ tn = file_struct->top; ++ ++ has_data = 0; ++ for (i = 1; i < YAFFS_NTNODES_INTERNAL; i++) { ++ if (tn->internal[i]) ++ has_data++; ++ } ++ ++ if (!has_data) { ++ file_struct->top = tn->internal[0]; ++ file_struct->top_level--; ++ yaffs_free_tnode(dev, tn); ++ } else { ++ done = 1; ++ } ++ } ++ ++ return YAFFS_OK; ++} ++ ++/*-------------------- End of File Structure functions.-------------------*/ ++ ++/* alloc_empty_obj gets us a clean Object.*/ ++static struct yaffs_obj *yaffs_alloc_empty_obj(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj = yaffs_alloc_raw_obj(dev); ++ ++ if (!obj) ++ return obj; ++ ++ dev->n_obj++; ++ ++ /* Now sweeten it up... */ ++ ++ memset(obj, 0, sizeof(struct yaffs_obj)); ++ obj->being_created = 1; ++ ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; ++ obj->variant_type = YAFFS_OBJECT_TYPE_UNKNOWN; ++ INIT_LIST_HEAD(&(obj->hard_links)); ++ INIT_LIST_HEAD(&(obj->hash_link)); ++ INIT_LIST_HEAD(&obj->siblings); ++ ++ /* Now make the directory sane */ ++ if (dev->root_dir) { ++ obj->parent = dev->root_dir; ++ list_add(&(obj->siblings), ++ &dev->root_dir->variant.dir_variant.children); ++ } ++ ++ /* Add it to the lost and found directory. ++ * NB Can't put root or lost-n-found in lost-n-found so ++ * check if lost-n-found exists first ++ */ ++ if (dev->lost_n_found) ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ ++ obj->being_created = 0; ++ ++ dev->checkpoint_blocks_required = 0; /* force recalculation */ ++ ++ return obj; ++} ++ ++static int yaffs_find_nice_bucket(struct yaffs_dev *dev) ++{ ++ int i; ++ int l = 999; ++ int lowest = 999999; ++ ++ /* Search for the shortest list or one that ++ * isn't too long. ++ */ ++ ++ for (i = 0; i < 10 && lowest > 4; i++) { ++ dev->bucket_finder++; ++ dev->bucket_finder %= YAFFS_NOBJECT_BUCKETS; ++ if (dev->obj_bucket[dev->bucket_finder].count < lowest) { ++ lowest = dev->obj_bucket[dev->bucket_finder].count; ++ l = dev->bucket_finder; ++ } ++ } ++ ++ return l; ++} ++ ++static int yaffs_new_obj_id(struct yaffs_dev *dev) ++{ ++ int bucket = yaffs_find_nice_bucket(dev); ++ int found = 0; ++ struct list_head *i; ++ u32 n = (u32) bucket; ++ ++ /* Now find an object value that has not already been taken ++ * by scanning the list. ++ */ ++ ++ while (!found) { ++ found = 1; ++ n += YAFFS_NOBJECT_BUCKETS; ++ if (1 || dev->obj_bucket[bucket].count > 0) { ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* If there is already one in the list */ ++ if (i && list_entry(i, struct yaffs_obj, ++ hash_link)->obj_id == n) { ++ found = 0; ++ } ++ } ++ } ++ } ++ return n; ++} ++ ++static void yaffs_hash_obj(struct yaffs_obj *in) ++{ ++ int bucket = yaffs_hash_fn(in->obj_id); ++ struct yaffs_dev *dev = in->my_dev; ++ ++ list_add(&in->hash_link, &dev->obj_bucket[bucket].list); ++ dev->obj_bucket[bucket].count++; ++} ++ ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number) ++{ ++ int bucket = yaffs_hash_fn(number); ++ struct list_head *i; ++ struct yaffs_obj *in; ++ ++ list_for_each(i, &dev->obj_bucket[bucket].list) { ++ /* Look if it is in the list */ ++ in = list_entry(i, struct yaffs_obj, hash_link); ++ if (in->obj_id == number) { ++ /* Don't show if it is defered free */ ++ if (in->defered_free) ++ return NULL; ++ return in; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct yaffs_obj *yaffs_new_obj(struct yaffs_dev *dev, int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ struct yaffs_tnode *tn = NULL; ++ ++ if (number < 0) ++ number = yaffs_new_obj_id(dev); ++ ++ if (type == YAFFS_OBJECT_TYPE_FILE) { ++ tn = yaffs_get_tnode(dev); ++ if (!tn) ++ return NULL; ++ } ++ ++ the_obj = yaffs_alloc_empty_obj(dev); ++ if (!the_obj) { ++ if (tn) ++ yaffs_free_tnode(dev, tn); ++ return NULL; ++ } ++ ++ the_obj->fake = 0; ++ the_obj->rename_allowed = 1; ++ the_obj->unlink_allowed = 1; ++ the_obj->obj_id = number; ++ yaffs_hash_obj(the_obj); ++ the_obj->variant_type = type; ++ yaffs_load_current_time(the_obj, 1, 1); ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ the_obj->variant.file_variant.file_size = 0; ++ the_obj->variant.file_variant.scanned_size = 0; ++ the_obj->variant.file_variant.shrink_size = ++ yaffs_max_file_size(dev); ++ the_obj->variant.file_variant.top_level = 0; ++ the_obj->variant.file_variant.top = tn; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&the_obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* No action required */ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* todo this should not happen */ ++ break; ++ } ++ return the_obj; ++} ++ ++static struct yaffs_obj *yaffs_create_fake_dir(struct yaffs_dev *dev, ++ int number, u32 mode) ++{ ++ ++ struct yaffs_obj *obj = ++ yaffs_new_obj(dev, number, YAFFS_OBJECT_TYPE_DIRECTORY); ++ ++ if (!obj) ++ return NULL; ++ ++ obj->fake = 1; /* it is fake so it might not use NAND */ ++ obj->rename_allowed = 0; ++ obj->unlink_allowed = 0; ++ obj->deleted = 0; ++ obj->unlinked = 0; ++ obj->yst_mode = mode; ++ obj->my_dev = dev; ++ obj->hdr_chunk = 0; /* Not a valid chunk. */ ++ return obj; ++ ++} ++ ++ ++static void yaffs_init_tnodes_and_objs(struct yaffs_dev *dev) ++{ ++ int i; ++ ++ dev->n_obj = 0; ++ dev->n_tnodes = 0; ++ yaffs_init_raw_tnodes_and_objs(dev); ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ INIT_LIST_HEAD(&dev->obj_bucket[i].list); ++ dev->obj_bucket[i].count = 0; ++ } ++} ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type) ++{ ++ struct yaffs_obj *the_obj = NULL; ++ ++ if (number > 0) ++ the_obj = yaffs_find_by_number(dev, number); ++ ++ if (!the_obj) ++ the_obj = yaffs_new_obj(dev, number, type); ++ ++ return the_obj; ++ ++} ++ ++YCHAR *yaffs_clone_str(const YCHAR *str) ++{ ++ YCHAR *new_str = NULL; ++ int len; ++ ++ if (!str) ++ str = _Y(""); ++ ++ len = strnlen(str, YAFFS_MAX_ALIAS_LENGTH); ++ new_str = kmalloc((len + 1) * sizeof(YCHAR), GFP_NOFS); ++ if (new_str) { ++ strncpy(new_str, str, len); ++ new_str[len] = 0; ++ } ++ return new_str; ++ ++} ++/* ++ *yaffs_update_parent() handles fixing a directories mtime and ctime when a new ++ * link (ie. name) is created or deleted in the directory. ++ * ++ * ie. ++ * create dir/a : update dir's mtime/ctime ++ * rm dir/a: update dir's mtime/ctime ++ * modify dir/a: don't update dir's mtimme/ctime ++ * ++ * This can be handled immediately or defered. Defering helps reduce the number ++ * of updates when many files in a directory are changed within a brief period. ++ * ++ * If the directory updating is defered then yaffs_update_dirty_dirs must be ++ * called periodically. ++ */ ++ ++static void yaffs_update_parent(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ ++ if (!obj) ++ return; ++ dev = obj->my_dev; ++ obj->dirty = 1; ++ yaffs_load_current_time(obj, 0, 1); ++ if (dev->param.defered_dir_update) { ++ struct list_head *link = &obj->variant.dir_variant.dirty; ++ ++ if (list_empty(link)) { ++ list_add(link, &dev->dirty_dirs); ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Added object %d to dirty directories", ++ obj->obj_id); ++ } ++ ++ } else { ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev) ++{ ++ struct list_head *link; ++ struct yaffs_obj *obj; ++ struct yaffs_dir_var *d_s; ++ union yaffs_obj_var *o_v; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update dirty directories"); ++ ++ while (!list_empty(&dev->dirty_dirs)) { ++ link = dev->dirty_dirs.next; ++ list_del_init(link); ++ ++ d_s = list_entry(link, struct yaffs_dir_var, dirty); ++ o_v = list_entry(d_s, union yaffs_obj_var, dir_variant); ++ obj = list_entry(o_v, struct yaffs_obj, variant); ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Update directory %d", ++ obj->obj_id); ++ ++ if (obj->dirty) ++ yaffs_update_oh(obj, NULL, 0, 0, 0, NULL); ++ } ++} ++ ++/* ++ * Mknod (create) a new object. ++ * equiv_obj only has meaning for a hard link; ++ * alias_str only has meaning for a symlink. ++ * rdev only has meaning for devices (a subset of special objects) ++ */ ++ ++static struct yaffs_obj *yaffs_create_obj(enum yaffs_obj_type type, ++ struct yaffs_obj *parent, ++ const YCHAR *name, ++ u32 mode, ++ u32 uid, ++ u32 gid, ++ struct yaffs_obj *equiv_obj, ++ const YCHAR *alias_str, u32 rdev) ++{ ++ struct yaffs_obj *in; ++ YCHAR *str = NULL; ++ struct yaffs_dev *dev = parent->my_dev; ++ ++ /* Check if the entry exists. ++ * If it does then fail the call since we don't want a dup. */ ++ if (yaffs_find_by_name(parent, name)) ++ return NULL; ++ ++ if (type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ str = yaffs_clone_str(alias_str); ++ if (!str) ++ return NULL; ++ } ++ ++ in = yaffs_new_obj(dev, -1, type); ++ ++ if (!in) { ++ kfree(str); ++ return NULL; ++ } ++ ++ in->hdr_chunk = 0; ++ in->valid = 1; ++ in->variant_type = type; ++ ++ in->yst_mode = mode; ++ ++ yaffs_attribs_init(in, gid, uid, rdev); ++ ++ in->n_data_chunks = 0; ++ ++ yaffs_set_obj_name(in, name); ++ in->dirty = 1; ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ in->my_dev = parent->my_dev; ++ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant.alias = str; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant.hardlink_variant.equiv_obj = equiv_obj; ++ in->variant.hardlink_variant.equiv_id = equiv_obj->obj_id; ++ list_add(&in->hard_links, &equiv_obj->hard_links); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* do nothing */ ++ break; ++ } ++ ++ if (yaffs_update_oh(in, name, 0, 0, 0, NULL) < 0) { ++ /* Could not create the object header, fail */ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ if (in) ++ yaffs_update_parent(parent); ++ ++ return in; ++} ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_FILE, parent, name, mode, ++ uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_DIRECTORY, parent, name, ++ mode, uid, gid, NULL, NULL, 0); ++} ++ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SPECIAL, parent, name, mode, ++ uid, gid, NULL, NULL, rdev); ++} ++ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias) ++{ ++ return yaffs_create_obj(YAFFS_OBJECT_TYPE_SYMLINK, parent, name, mode, ++ uid, gid, NULL, alias, 0); ++} ++ ++/* yaffs_link_obj returns the object id of the equivalent object.*/ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR * name, ++ struct yaffs_obj *equiv_obj) ++{ ++ /* Get the real object in case we were fed a hard link obj */ ++ equiv_obj = yaffs_get_equivalent_obj(equiv_obj); ++ ++ if (yaffs_create_obj(YAFFS_OBJECT_TYPE_HARDLINK, ++ parent, name, 0, 0, 0, ++ equiv_obj, NULL, 0)) ++ return equiv_obj; ++ ++ return NULL; ++ ++} ++ ++ ++ ++/*---------------------- Block Management and Page Allocation -------------*/ ++ ++static void yaffs_deinit_blocks(struct yaffs_dev *dev) ++{ ++ if (dev->block_info_alt && dev->block_info) ++ vfree(dev->block_info); ++ else ++ kfree(dev->block_info); ++ ++ dev->block_info_alt = 0; ++ ++ dev->block_info = NULL; ++ ++ if (dev->chunk_bits_alt && dev->chunk_bits) ++ vfree(dev->chunk_bits); ++ else ++ kfree(dev->chunk_bits); ++ dev->chunk_bits_alt = 0; ++ dev->chunk_bits = NULL; ++} ++ ++static int yaffs_init_blocks(struct yaffs_dev *dev) ++{ ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ dev->block_info = NULL; ++ dev->chunk_bits = NULL; ++ dev->alloc_block = -1; /* force it to get a new one */ ++ ++ /* If the first allocation strategy fails, thry the alternate one */ ++ dev->block_info = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_info), GFP_NOFS); ++ if (!dev->block_info) { ++ dev->block_info = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_info)); ++ dev->block_info_alt = 1; ++ } else { ++ dev->block_info_alt = 0; ++ } ++ ++ if (!dev->block_info) ++ goto alloc_error; ++ ++ /* Set up dynamic blockinfo stuff. Round up bytes. */ ++ dev->chunk_bit_stride = (dev->param.chunks_per_block + 7) / 8; ++ dev->chunk_bits = ++ kmalloc(dev->chunk_bit_stride * n_blocks, GFP_NOFS); ++ if (!dev->chunk_bits) { ++ dev->chunk_bits = ++ vmalloc(dev->chunk_bit_stride * n_blocks); ++ dev->chunk_bits_alt = 1; ++ } else { ++ dev->chunk_bits_alt = 0; ++ } ++ if (!dev->chunk_bits) ++ goto alloc_error; ++ ++ ++ memset(dev->block_info, 0, n_blocks * sizeof(struct yaffs_block_info)); ++ memset(dev->chunk_bits, 0, dev->chunk_bit_stride * n_blocks); ++ return YAFFS_OK; ++ ++alloc_error: ++ yaffs_deinit_blocks(dev); ++ return YAFFS_FAIL; ++} ++ ++ ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block_no); ++ int erased_ok = 0; ++ int i; ++ ++ /* If the block is still healthy erase it and mark as clean. ++ * If the block has had a data failure, then retire it. ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_GC | YAFFS_TRACE_ERASE, ++ "yaffs_block_became_dirty block %d state %d %s", ++ block_no, bi->block_state, ++ (bi->needs_retiring) ? "needs retiring" : ""); ++ ++ yaffs2_clear_oldest_dirty_seq(dev, bi); ++ ++ bi->block_state = YAFFS_BLOCK_STATE_DIRTY; ++ ++ /* If this is the block being garbage collected then stop gc'ing */ ++ if (block_no == dev->gc_block) ++ dev->gc_block = 0; ++ ++ /* If this block is currently the best candidate for gc ++ * then drop as a candidate */ ++ if (block_no == dev->gc_dirtiest) { ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ } ++ ++ if (!bi->needs_retiring) { ++ yaffs2_checkpt_invalidate(dev); ++ erased_ok = yaffs_erase_block(dev, block_no); ++ if (!erased_ok) { ++ dev->n_erase_failures++; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Erasure failed %d", block_no); ++ } ++ } ++ ++ /* Verify erasure if needed */ ++ if (erased_ok && ++ ((yaffs_trace_mask & YAFFS_TRACE_ERASE) || ++ !yaffs_skip_verification(dev))) { ++ for (i = 0; i < dev->param.chunks_per_block; i++) { ++ if (!yaffs_check_chunk_erased(dev, ++ block_no * dev->param.chunks_per_block + i)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ ">>Block %d erasure supposedly OK, but chunk %d not erased", ++ block_no, i); ++ } ++ } ++ } ++ ++ if (!erased_ok) { ++ /* We lost a block of free space */ ++ dev->n_free_chunks -= dev->param.chunks_per_block; ++ yaffs_retire_block(dev, block_no); ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>> Block %d retired", block_no); ++ return; ++ } ++ ++ /* Clean it up... */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ bi->seq_number = 0; ++ dev->n_erased_blocks++; ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ bi->has_shrink_hdr = 0; ++ bi->skip_erased_check = 1; /* Clean, so no need to check */ ++ bi->gc_prioritise = 0; ++ bi->has_summary = 0; ++ ++ yaffs_clear_chunk_bits(dev, block_no); ++ ++ yaffs_trace(YAFFS_TRACE_ERASE, "Erased block %d", block_no); ++} ++ ++static inline int yaffs_gc_process_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int old_chunk, u8 *buffer) ++{ ++ int new_chunk; ++ int mark_flash = 1; ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj *object; ++ int matching_chunk; ++ int ret_val = YAFFS_OK; ++ ++ memset(&tags, 0, sizeof(tags)); ++ yaffs_rd_chunk_tags_nand(dev, old_chunk, ++ buffer, &tags); ++ object = yaffs_find_by_number(dev, tags.obj_id); ++ ++ yaffs_trace(YAFFS_TRACE_GC_DETAIL, ++ "Collecting chunk in block %d, %d %d %d ", ++ dev->gc_chunk, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ ++ if (object && !yaffs_skip_verification(dev)) { ++ if (tags.chunk_id == 0) ++ matching_chunk = ++ object->hdr_chunk; ++ else if (object->soft_del) ++ /* Defeat the test */ ++ matching_chunk = old_chunk; ++ else ++ matching_chunk = ++ yaffs_find_chunk_in_file ++ (object, tags.chunk_id, ++ NULL); ++ ++ if (old_chunk != matching_chunk) ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "gc: page in gc mismatch: %d %d %d %d", ++ old_chunk, ++ matching_chunk, ++ tags.obj_id, ++ tags.chunk_id); ++ } ++ ++ if (!object) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "page %d in gc has no object: %d %d %d ", ++ old_chunk, ++ tags.obj_id, tags.chunk_id, ++ tags.n_bytes); ++ } ++ ++ if (object && ++ object->deleted && ++ object->soft_del && tags.chunk_id != 0) { ++ /* Data chunk in a soft deleted file, ++ * throw it away. ++ * It's a soft deleted data chunk, ++ * No need to copy this, just forget ++ * about it and fix up the object. ++ */ ++ ++ /* Free chunks already includes ++ * softdeleted chunks, how ever this ++ * chunk is going to soon be really ++ * deleted which will increment free ++ * chunks. We have to decrement free ++ * chunks so this works out properly. ++ */ ++ dev->n_free_chunks--; ++ bi->soft_del_pages--; ++ ++ object->n_data_chunks--; ++ if (object->n_data_chunks <= 0) { ++ /* remeber to clean up obj */ ++ dev->gc_cleanup_list[dev->n_clean_ups] = tags.obj_id; ++ dev->n_clean_ups++; ++ } ++ mark_flash = 0; ++ } else if (object) { ++ /* It's either a data chunk in a live ++ * file or an ObjectHeader, so we're ++ * interested in it. ++ * NB Need to keep the ObjectHeaders of ++ * deleted files until the whole file ++ * has been deleted off ++ */ ++ tags.serial_number++; ++ dev->n_gc_copies++; ++ ++ if (tags.chunk_id == 0) { ++ /* It is an object Id, ++ * We need to nuke the ++ * shrinkheader flags since its ++ * work is done. ++ * Also need to clean up ++ * shadowing. ++ */ ++ struct yaffs_obj_hdr *oh; ++ oh = (struct yaffs_obj_hdr *) buffer; ++ ++ oh->is_shrink = 0; ++ tags.extra_is_shrink = 0; ++ oh->shadows_obj = 0; ++ oh->inband_shadowed_obj_id = 0; ++ tags.extra_shadows = 0; ++ ++ /* Update file size */ ++ if (object->variant_type == YAFFS_OBJECT_TYPE_FILE) { ++ yaffs_oh_size_load(oh, ++ object->variant.file_variant.file_size); ++ tags.extra_file_size = ++ object->variant.file_variant.file_size; ++ } ++ ++ yaffs_verify_oh(object, oh, &tags, 1); ++ new_chunk = ++ yaffs_write_new_chunk(dev, (u8 *) oh, &tags, 1); ++ } else { ++ new_chunk = ++ yaffs_write_new_chunk(dev, buffer, &tags, 1); ++ } ++ ++ if (new_chunk < 0) { ++ ret_val = YAFFS_FAIL; ++ } else { ++ ++ /* Now fix up the Tnodes etc. */ ++ ++ if (tags.chunk_id == 0) { ++ /* It's a header */ ++ object->hdr_chunk = new_chunk; ++ object->serial = tags.serial_number; ++ } else { ++ /* It's a data chunk */ ++ yaffs_put_chunk_in_file(object, tags.chunk_id, ++ new_chunk, 0); ++ } ++ } ++ } ++ if (ret_val == YAFFS_OK) ++ yaffs_chunk_del(dev, old_chunk, mark_flash, __LINE__); ++ return ret_val; ++} ++ ++static int yaffs_gc_block(struct yaffs_dev *dev, int block, int whole_block) ++{ ++ int old_chunk; ++ int ret_val = YAFFS_OK; ++ int i; ++ int is_checkpt_block; ++ int max_copies; ++ int chunks_before = yaffs_get_erased_chunks(dev); ++ int chunks_after; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, block); ++ ++ is_checkpt_block = (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d, in use %d, shrink %d, whole_block %d", ++ block, bi->pages_in_use, bi->has_shrink_hdr, ++ whole_block); ++ ++ /*yaffs_verify_free_chunks(dev); */ ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ bi->block_state = YAFFS_BLOCK_STATE_COLLECTING; ++ ++ bi->has_shrink_hdr = 0; /* clear the flag so that the block can erase */ ++ ++ dev->gc_disable = 1; ++ ++ yaffs_summary_gc(dev, block); ++ ++ if (is_checkpt_block || !yaffs_still_some_chunks(dev, block)) { ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "Collecting block %d that has no chunks in use", ++ block); ++ yaffs_block_became_dirty(dev, block); ++ } else { ++ ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_verify_blk(dev, bi, block); ++ ++ max_copies = (whole_block) ? dev->param.chunks_per_block : 5; ++ old_chunk = block * dev->param.chunks_per_block + dev->gc_chunk; ++ ++ for (/* init already done */ ; ++ ret_val == YAFFS_OK && ++ dev->gc_chunk < dev->param.chunks_per_block && ++ (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) && ++ max_copies > 0; ++ dev->gc_chunk++, old_chunk++) { ++ if (yaffs_check_chunk_bit(dev, block, dev->gc_chunk)) { ++ /* Page is in use and might need to be copied */ ++ max_copies--; ++ ret_val = yaffs_gc_process_chunk(dev, bi, ++ old_chunk, buffer); ++ } ++ } ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ yaffs_verify_collected_blk(dev, bi, block); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ /* ++ * The gc did not complete. Set block state back to FULL ++ * because checkpointing does not restore gc. ++ */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } else { ++ /* The gc completed. */ ++ /* Do any required cleanups */ ++ for (i = 0; i < dev->n_clean_ups; i++) { ++ /* Time to delete the file too */ ++ struct yaffs_obj *object = ++ yaffs_find_by_number(dev, dev->gc_cleanup_list[i]); ++ if (object) { ++ yaffs_free_tnode(dev, ++ object->variant.file_variant.top); ++ object->variant.file_variant.top = NULL; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: About to finally delete object %d", ++ object->obj_id); ++ yaffs_generic_obj_del(object); ++ object->my_dev->n_deleted_files--; ++ } ++ ++ } ++ chunks_after = yaffs_get_erased_chunks(dev); ++ if (chunks_before >= chunks_after) ++ yaffs_trace(YAFFS_TRACE_GC, ++ "gc did not increase free chunks before %d after %d", ++ chunks_before, chunks_after); ++ dev->gc_block = 0; ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ dev->gc_disable = 0; ++ ++ return ret_val; ++} ++ ++/* ++ * find_gc_block() selects the dirtiest block (or close enough) ++ * for garbage collection. ++ */ ++ ++static unsigned yaffs_find_gc_block(struct yaffs_dev *dev, ++ int aggressive, int background) ++{ ++ int i; ++ int iterations; ++ unsigned selected = 0; ++ int prioritised = 0; ++ int prioritised_exist = 0; ++ struct yaffs_block_info *bi; ++ int threshold; ++ ++ /* First let's see if we need to grab a prioritised block */ ++ if (dev->has_pending_prioritised_gc && !aggressive) { ++ dev->gc_dirtiest = 0; ++ bi = dev->block_info; ++ for (i = dev->internal_start_block; ++ i <= dev->internal_end_block && !selected; i++) { ++ ++ if (bi->gc_prioritise) { ++ prioritised_exist = 1; ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ selected = i; ++ prioritised = 1; ++ } ++ } ++ bi++; ++ } ++ ++ /* ++ * If there is a prioritised block and none was selected then ++ * this happened because there is at least one old dirty block ++ * gumming up the works. Let's gc the oldest dirty block. ++ */ ++ ++ if (prioritised_exist && ++ !selected && dev->oldest_dirty_block > 0) ++ selected = dev->oldest_dirty_block; ++ ++ if (!prioritised_exist) /* None found, so we can clear this */ ++ dev->has_pending_prioritised_gc = 0; ++ } ++ ++ /* If we're doing aggressive GC then we are happy to take a less-dirty ++ * block, and search harder. ++ * else (leasurely gc), then we only bother to do this if the ++ * block has only a few pages in use. ++ */ ++ ++ if (!selected) { ++ int pages_used; ++ int n_blocks = ++ dev->internal_end_block - dev->internal_start_block + 1; ++ if (aggressive) { ++ threshold = dev->param.chunks_per_block; ++ iterations = n_blocks; ++ } else { ++ int max_threshold; ++ ++ if (background) ++ max_threshold = dev->param.chunks_per_block / 2; ++ else ++ max_threshold = dev->param.chunks_per_block / 8; ++ ++ if (max_threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ max_threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ ++ threshold = background ? (dev->gc_not_done + 2) * 2 : 0; ++ if (threshold < YAFFS_GC_PASSIVE_THRESHOLD) ++ threshold = YAFFS_GC_PASSIVE_THRESHOLD; ++ if (threshold > max_threshold) ++ threshold = max_threshold; ++ ++ iterations = n_blocks / 16 + 1; ++ if (iterations > 100) ++ iterations = 100; ++ } ++ ++ for (i = 0; ++ i < iterations && ++ (dev->gc_dirtiest < 1 || ++ dev->gc_pages_in_use > YAFFS_GC_GOOD_ENOUGH); ++ i++) { ++ dev->gc_block_finder++; ++ if (dev->gc_block_finder < dev->internal_start_block || ++ dev->gc_block_finder > dev->internal_end_block) ++ dev->gc_block_finder = ++ dev->internal_start_block; ++ ++ bi = yaffs_get_block_info(dev, dev->gc_block_finder); ++ ++ pages_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL && ++ pages_used < dev->param.chunks_per_block && ++ (dev->gc_dirtiest < 1 || ++ pages_used < dev->gc_pages_in_use) && ++ yaffs_block_ok_for_gc(dev, bi)) { ++ dev->gc_dirtiest = dev->gc_block_finder; ++ dev->gc_pages_in_use = pages_used; ++ } ++ } ++ ++ if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold) ++ selected = dev->gc_dirtiest; ++ } ++ ++ /* ++ * If nothing has been selected for a while, try the oldest dirty ++ * because that's gumming up the works. ++ */ ++ ++ if (!selected && dev->param.is_yaffs2 && ++ dev->gc_not_done >= (background ? 10 : 20)) { ++ yaffs2_find_oldest_dirty_seq(dev); ++ if (dev->oldest_dirty_block > 0) { ++ selected = dev->oldest_dirty_block; ++ dev->gc_dirtiest = selected; ++ dev->oldest_dirty_gc_count++; ++ bi = yaffs_get_block_info(dev, selected); ++ dev->gc_pages_in_use = ++ bi->pages_in_use - bi->soft_del_pages; ++ } else { ++ dev->gc_not_done = 0; ++ } ++ } ++ ++ if (selected) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC Selected block %d with %d free, prioritised:%d", ++ selected, ++ dev->param.chunks_per_block - dev->gc_pages_in_use, ++ prioritised); ++ ++ dev->n_gc_blocks++; ++ if (background) ++ dev->bg_gcs++; ++ ++ dev->gc_dirtiest = 0; ++ dev->gc_pages_in_use = 0; ++ dev->gc_not_done = 0; ++ if (dev->refresh_skip > 0) ++ dev->refresh_skip--; ++ } else { ++ dev->gc_not_done++; ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC none: finder %d skip %d threshold %d dirtiest %d using %d oldest %d%s", ++ dev->gc_block_finder, dev->gc_not_done, threshold, ++ dev->gc_dirtiest, dev->gc_pages_in_use, ++ dev->oldest_dirty_block, background ? " bg" : ""); ++ } ++ ++ return selected; ++} ++ ++/* New garbage collector ++ * If we're very low on erased blocks then we do aggressive garbage collection ++ * otherwise we do "leasurely" garbage collection. ++ * Aggressive gc looks further (whole array) and will accept less dirty blocks. ++ * Passive gc only inspects smaller areas and only accepts more dirty blocks. ++ * ++ * The idea is to help clear out space in a more spread-out manner. ++ * Dunno if it really does anything useful. ++ */ ++static int yaffs_check_gc(struct yaffs_dev *dev, int background) ++{ ++ int aggressive = 0; ++ int gc_ok = YAFFS_OK; ++ int max_tries = 0; ++ int min_erased; ++ int erased_chunks; ++ int checkpt_block_adjust; ++ ++ if (dev->param.gc_control_fn && ++ (dev->param.gc_control_fn(dev) & 1) == 0) ++ return YAFFS_OK; ++ ++ if (dev->gc_disable) ++ /* Bail out so we don't get recursive gc */ ++ return YAFFS_OK; ++ ++ /* This loop should pass the first time. ++ * Only loops here if the collection does not increase space. ++ */ ++ ++ do { ++ max_tries++; ++ ++ checkpt_block_adjust = yaffs_calc_checkpt_blocks_required(dev); ++ ++ min_erased = ++ dev->param.n_reserved_blocks + checkpt_block_adjust + 1; ++ erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ /* If we need a block soon then do aggressive gc. */ ++ if (dev->n_erased_blocks < min_erased) ++ aggressive = 1; ++ else { ++ if (!background ++ && erased_chunks > (dev->n_free_chunks / 4)) ++ break; ++ ++ if (dev->gc_skip > 20) ++ dev->gc_skip = 20; ++ if (erased_chunks < dev->n_free_chunks / 2 || ++ dev->gc_skip < 1 || background) ++ aggressive = 0; ++ else { ++ dev->gc_skip--; ++ break; ++ } ++ } ++ ++ dev->gc_skip = 5; ++ ++ /* If we don't already have a block being gc'd then see if we ++ * should start another */ ++ ++ if (dev->gc_block < 1 && !aggressive) { ++ dev->gc_block = yaffs2_find_refresh_block(dev); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ if (dev->gc_block < 1) { ++ dev->gc_block = ++ yaffs_find_gc_block(dev, aggressive, background); ++ dev->gc_chunk = 0; ++ dev->n_clean_ups = 0; ++ } ++ ++ if (dev->gc_block > 0) { ++ dev->all_gcs++; ++ if (!aggressive) ++ dev->passive_gc_count++; ++ ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC n_erased_blocks %d aggressive %d", ++ dev->n_erased_blocks, aggressive); ++ ++ gc_ok = yaffs_gc_block(dev, dev->gc_block, aggressive); ++ } ++ ++ if (dev->n_erased_blocks < (dev->param.n_reserved_blocks) && ++ dev->gc_block > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "yaffs: GC !!!no reclaim!!! n_erased_blocks %d after try %d block %d", ++ dev->n_erased_blocks, max_tries, ++ dev->gc_block); ++ } ++ } while ((dev->n_erased_blocks < dev->param.n_reserved_blocks) && ++ (dev->gc_block > 0) && (max_tries < 2)); ++ ++ return aggressive ? gc_ok : YAFFS_OK; ++} ++ ++/* ++ * yaffs_bg_gc() ++ * Garbage collects. Intended to be called from a background thread. ++ * Returns non-zero if at least half the free chunks are erased. ++ */ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency) ++{ ++ int erased_chunks = dev->n_erased_blocks * dev->param.chunks_per_block; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "Background gc %u", urgency); ++ ++ yaffs_check_gc(dev, 1); ++ return erased_chunks > dev->n_free_chunks / 2; ++} ++ ++/*-------------------- Data file manipulation -----------------*/ ++ ++static int yaffs_rd_data_obj(struct yaffs_obj *in, int inode_chunk, u8 * buffer) ++{ ++ int nand_chunk = yaffs_find_chunk_in_file(in, inode_chunk, NULL); ++ ++ if (nand_chunk >= 0) ++ return yaffs_rd_chunk_tags_nand(in->my_dev, nand_chunk, ++ buffer, NULL); ++ else { ++ yaffs_trace(YAFFS_TRACE_NANDACCESS, ++ "Chunk %d not found zero instead", ++ nand_chunk); ++ /* get sane (zero) data if you read a hole */ ++ memset(buffer, 0, in->my_dev->data_bytes_per_chunk); ++ return 0; ++ } ++ ++} ++ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn) ++{ ++ int block; ++ int page; ++ struct yaffs_ext_tags tags; ++ struct yaffs_block_info *bi; ++ ++ if (chunk_id <= 0) ++ return; ++ ++ dev->n_deletions++; ++ block = chunk_id / dev->param.chunks_per_block; ++ page = chunk_id % dev->param.chunks_per_block; ++ ++ if (!yaffs_check_chunk_bit(dev, block, page)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Deleting invalid chunk %d", chunk_id); ++ ++ bi = yaffs_get_block_info(dev, block); ++ ++ yaffs2_update_oldest_dirty_seq(dev, block, bi); ++ ++ yaffs_trace(YAFFS_TRACE_DELETION, ++ "line %d delete of chunk %d", ++ lyn, chunk_id); ++ ++ if (!dev->param.is_yaffs2 && mark_flash && ++ bi->block_state != YAFFS_BLOCK_STATE_COLLECTING) { ++ ++ memset(&tags, 0, sizeof(tags)); ++ tags.is_deleted = 1; ++ yaffs_wr_chunk_tags_nand(dev, chunk_id, NULL, &tags); ++ yaffs_handle_chunk_update(dev, chunk_id, &tags); ++ } else { ++ dev->n_unmarked_deletions++; ++ } ++ ++ /* Pull out of the management area. ++ * If the whole block became dirty, this will kick off an erasure. ++ */ ++ if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING || ++ bi->block_state == YAFFS_BLOCK_STATE_FULL || ++ bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) { ++ dev->n_free_chunks++; ++ yaffs_clear_chunk_bit(dev, block, page); ++ bi->pages_in_use--; ++ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state != YAFFS_BLOCK_STATE_ALLOCATING && ++ bi->block_state != YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ yaffs_block_became_dirty(dev, block); ++ } ++ } ++} ++ ++static int yaffs_wr_data_obj(struct yaffs_obj *in, int inode_chunk, ++ const u8 *buffer, int n_bytes, int use_reserve) ++{ ++ /* Find old chunk Need to do this to get serial number ++ * Write new one and patch into tree. ++ * Invalidate old tags. ++ */ ++ ++ int prev_chunk_id; ++ struct yaffs_ext_tags prev_tags; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ yaffs_check_gc(dev, 0); ++ ++ /* Get the previous chunk at this location in the file if it exists. ++ * If it does not exist then put a zero into the tree. This creates ++ * the tnode now, rather than later when it is harder to clean up. ++ */ ++ prev_chunk_id = yaffs_find_chunk_in_file(in, inode_chunk, &prev_tags); ++ if (prev_chunk_id < 1 && ++ !yaffs_put_chunk_in_file(in, inode_chunk, 0, 0)) ++ return 0; ++ ++ /* Set up new tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ ++ new_tags.chunk_id = inode_chunk; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = ++ (prev_chunk_id > 0) ? prev_tags.serial_number + 1 : 1; ++ new_tags.n_bytes = n_bytes; ++ ++ if (n_bytes < 1 || n_bytes > dev->param.total_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Writing %d bytes to chunk!!!!!!!!!", ++ n_bytes); ++ BUG(); ++ } ++ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, use_reserve); ++ ++ if (new_chunk_id > 0) { ++ yaffs_put_chunk_in_file(in, inode_chunk, new_chunk_id, 0); ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ yaffs_verify_file_sane(in); ++ } ++ return new_chunk_id; ++ ++} ++ ++ ++ ++static int yaffs_do_xattrib_mod(struct yaffs_obj *obj, int set, ++ const YCHAR *name, const void *value, int size, ++ int flags) ++{ ++ struct yaffs_xattr_mod xmod; ++ int result; ++ ++ xmod.set = set; ++ xmod.name = name; ++ xmod.data = value; ++ xmod.size = size; ++ xmod.flags = flags; ++ xmod.result = -ENOSPC; ++ ++ result = yaffs_update_oh(obj, NULL, 0, 0, 0, &xmod); ++ ++ if (result > 0) ++ return xmod.result; ++ else ++ return -ENOSPC; ++} ++ ++static int yaffs_apply_xattrib_mod(struct yaffs_obj *obj, char *buffer, ++ struct yaffs_xattr_mod *xmod) ++{ ++ int retval = 0; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer = buffer + x_offs; ++ ++ if (xmod->set) ++ retval = ++ nval_set(x_buffer, x_size, xmod->name, xmod->data, ++ xmod->size, xmod->flags); ++ else ++ retval = nval_del(x_buffer, x_size, xmod->name); ++ ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ xmod->result = retval; ++ ++ return retval; ++} ++ ++static int yaffs_do_xattrib_fetch(struct yaffs_obj *obj, const YCHAR *name, ++ void *value, int size) ++{ ++ char *buffer = NULL; ++ int result; ++ struct yaffs_ext_tags tags; ++ struct yaffs_dev *dev = obj->my_dev; ++ int x_offs = sizeof(struct yaffs_obj_hdr); ++ int x_size = dev->data_bytes_per_chunk - sizeof(struct yaffs_obj_hdr); ++ char *x_buffer; ++ int retval = 0; ++ ++ if (obj->hdr_chunk < 1) ++ return -ENODATA; ++ ++ /* If we know that the object has no xattribs then don't do all the ++ * reading and parsing. ++ */ ++ if (obj->xattr_known && !obj->has_xattr) { ++ if (name) ++ return -ENODATA; ++ else ++ return 0; ++ } ++ ++ buffer = (char *)yaffs_get_temp_buffer(dev); ++ if (!buffer) ++ return -ENOMEM; ++ ++ result = ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, (u8 *) buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ retval = -ENOENT; ++ else { ++ x_buffer = buffer + x_offs; ++ ++ if (!obj->xattr_known) { ++ obj->has_xattr = nval_hasvalues(x_buffer, x_size); ++ obj->xattr_known = 1; ++ } ++ ++ if (name) ++ retval = nval_get(x_buffer, x_size, name, value, size); ++ else ++ retval = nval_list(x_buffer, x_size, value, size); ++ } ++ yaffs_release_temp_buffer(dev, (u8 *) buffer); ++ return retval; ++} ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR * name, ++ const void *value, int size, int flags) ++{ ++ return yaffs_do_xattrib_mod(obj, 1, name, value, size, flags); ++} ++ ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR * name) ++{ ++ return yaffs_do_xattrib_mod(obj, 0, name, NULL, 0, 0); ++} ++ ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR * name, void *value, ++ int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, name, value, size); ++} ++ ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size) ++{ ++ return yaffs_do_xattrib_fetch(obj, NULL, buffer, size); ++} ++ ++static void yaffs_check_obj_details_loaded(struct yaffs_obj *in) ++{ ++ u8 *buf; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ ++ if (!in || !in->lazy_loaded || in->hdr_chunk < 1) ++ return; ++ ++ dev = in->my_dev; ++ in->lazy_loaded = 0; ++ buf = yaffs_get_temp_buffer(dev); ++ ++ result = yaffs_rd_chunk_tags_nand(dev, in->hdr_chunk, buf, &tags); ++ oh = (struct yaffs_obj_hdr *)buf; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ yaffs_set_obj_name_from_oh(in, oh); ++ ++ if (in->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ in->variant.symlink_variant.alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant.symlink_variant.alias) ++ alloc_failed = 1; /* Not returned */ ++ } ++ yaffs_release_temp_buffer(dev, buf); ++} ++ ++static void yaffs_load_name_from_oh(struct yaffs_dev *dev, YCHAR *name, ++ const YCHAR *oh_name, int buff_size) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ if (dev->param.auto_unicode) { ++ if (*oh_name) { ++ /* It is an ASCII name, do an ASCII to ++ * unicode conversion */ ++ const char *ascii_oh_name = (const char *)oh_name; ++ int n = buff_size - 1; ++ while (n > 0 && *ascii_oh_name) { ++ *name = *ascii_oh_name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ strncpy(name, oh_name + 1, buff_size - 1); ++ } ++ } else { ++#else ++ (void) dev; ++ { ++#endif ++ strncpy(name, oh_name, buff_size - 1); ++ } ++} ++ ++static void yaffs_load_oh_from_name(struct yaffs_dev *dev, YCHAR *oh_name, ++ const YCHAR *name) ++{ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ ++ int is_ascii; ++ YCHAR *w; ++ ++ if (dev->param.auto_unicode) { ++ ++ is_ascii = 1; ++ w = name; ++ ++ /* Figure out if the name will fit in ascii character set */ ++ while (is_ascii && *w) { ++ if ((*w) & 0xff00) ++ is_ascii = 0; ++ w++; ++ } ++ ++ if (is_ascii) { ++ /* It is an ASCII name, so convert unicode to ascii */ ++ char *ascii_oh_name = (char *)oh_name; ++ int n = YAFFS_MAX_NAME_LENGTH - 1; ++ while (n > 0 && *name) { ++ *ascii_oh_name = *name; ++ name++; ++ ascii_oh_name++; ++ n--; ++ } ++ } else { ++ /* Unicode name, so save starting at the second YCHAR */ ++ *oh_name = 0; ++ strncpy(oh_name + 1, name, YAFFS_MAX_NAME_LENGTH - 2); ++ } ++ } else { ++#else ++ dev = dev; ++ { ++#endif ++ strncpy(oh_name, name, YAFFS_MAX_NAME_LENGTH - 1); ++ } ++} ++ ++/* UpdateObjectHeader updates the header on NAND for an object. ++ * If name is not NULL, then that new name is used. ++ */ ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, int force, ++ int is_shrink, int shadows, struct yaffs_xattr_mod *xmod) ++{ ++ ++ struct yaffs_block_info *bi; ++ struct yaffs_dev *dev = in->my_dev; ++ int prev_chunk_id; ++ int ret_val = 0; ++ int result = 0; ++ int new_chunk_id; ++ struct yaffs_ext_tags new_tags; ++ struct yaffs_ext_tags old_tags; ++ const YCHAR *alias = NULL; ++ u8 *buffer = NULL; ++ YCHAR old_name[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj_hdr *oh = NULL; ++ loff_t file_size = 0; ++ ++ strcpy(old_name, _Y("silly old name")); ++ ++ if (in->fake && in != dev->root_dir && !force && !xmod) ++ return ret_val; ++ ++ yaffs_check_gc(dev, 0); ++ yaffs_check_obj_details_loaded(in); ++ ++ buffer = yaffs_get_temp_buffer(in->my_dev); ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ prev_chunk_id = in->hdr_chunk; ++ ++ if (prev_chunk_id > 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, prev_chunk_id, ++ buffer, &old_tags); ++ ++ yaffs_verify_oh(in, oh, &old_tags, 0); ++ memcpy(old_name, oh->name, sizeof(oh->name)); ++ memset(buffer, 0xff, sizeof(struct yaffs_obj_hdr)); ++ } else { ++ memset(buffer, 0xff, dev->data_bytes_per_chunk); ++ } ++ ++ oh->type = in->variant_type; ++ oh->yst_mode = in->yst_mode; ++ oh->shadows_obj = oh->inband_shadowed_obj_id = shadows; ++ ++ yaffs_load_attribs_oh(oh, in); ++ ++ if (in->parent) ++ oh->parent_obj_id = in->parent->obj_id; ++ else ++ oh->parent_obj_id = 0; ++ ++ if (name && *name) { ++ memset(oh->name, 0, sizeof(oh->name)); ++ yaffs_load_oh_from_name(dev, oh->name, name); ++ } else if (prev_chunk_id > 0) { ++ memcpy(oh->name, old_name, sizeof(oh->name)); ++ } else { ++ memset(oh->name, 0, sizeof(oh->name)); ++ } ++ ++ oh->is_shrink = is_shrink; ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Should not happen */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (oh->parent_obj_id != YAFFS_OBJECTID_DELETED && ++ oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED) ++ file_size = in->variant.file_variant.file_size; ++ yaffs_oh_size_load(oh, file_size); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ oh->equiv_id = in->variant.hardlink_variant.equiv_id; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ alias = in->variant.symlink_variant.alias; ++ if (!alias) ++ alias = _Y("no alias"); ++ strncpy(oh->alias, alias, YAFFS_MAX_ALIAS_LENGTH); ++ oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0; ++ break; ++ } ++ ++ /* process any xattrib modifications */ ++ if (xmod) ++ yaffs_apply_xattrib_mod(in, (char *)buffer, xmod); ++ ++ /* Tags */ ++ memset(&new_tags, 0, sizeof(new_tags)); ++ in->serial++; ++ new_tags.chunk_id = 0; ++ new_tags.obj_id = in->obj_id; ++ new_tags.serial_number = in->serial; ++ ++ /* Add extra info for file header */ ++ new_tags.extra_available = 1; ++ new_tags.extra_parent_id = oh->parent_obj_id; ++ new_tags.extra_file_size = file_size; ++ new_tags.extra_is_shrink = oh->is_shrink; ++ new_tags.extra_equiv_id = oh->equiv_id; ++ new_tags.extra_shadows = (oh->shadows_obj > 0) ? 1 : 0; ++ new_tags.extra_obj_type = in->variant_type; ++ yaffs_verify_oh(in, oh, &new_tags, 1); ++ ++ /* Create new chunk in NAND */ ++ new_chunk_id = ++ yaffs_write_new_chunk(dev, buffer, &new_tags, ++ (prev_chunk_id > 0) ? 1 : 0); ++ ++ if (buffer) ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (new_chunk_id < 0) ++ return new_chunk_id; ++ ++ in->hdr_chunk = new_chunk_id; ++ ++ if (prev_chunk_id > 0) ++ yaffs_chunk_del(dev, prev_chunk_id, 1, __LINE__); ++ ++ if (!yaffs_obj_cache_dirty(in)) ++ in->dirty = 0; ++ ++ /* If this was a shrink, then mark the block ++ * that the chunk lives on */ ++ if (is_shrink) { ++ bi = yaffs_get_block_info(in->my_dev, ++ new_chunk_id / ++ in->my_dev->param.chunks_per_block); ++ bi->has_shrink_hdr = 1; ++ } ++ ++ ++ return new_chunk_id; ++} ++ ++/*--------------------- File read/write ------------------------ ++ * Read and write have very similar structures. ++ * In general the read/write has three parts to it ++ * An incomplete chunk to start with (if the read/write is not chunk-aligned) ++ * Some complete chunks ++ * An incomplete chunk to end off with ++ * ++ * Curve-balls: the first chunk might also be the last chunk. ++ */ ++ ++int yaffs_file_rd(struct yaffs_obj *in, u8 * buffer, loff_t offset, int n_bytes) ++{ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ struct yaffs_cache *cache; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ chunk++; ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ if ((start + n) < dev->data_bytes_per_chunk) ++ n_copy = n; ++ else ++ n_copy = dev->data_bytes_per_chunk - start; ++ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ /* If the chunk is already in the cache or it is less than ++ * a whole chunk or we're using inband tags then use the cache ++ * (if there is caching) else bypass the cache. ++ */ ++ if (cache || n_copy != dev->data_bytes_per_chunk || ++ dev->param.inband_tags) { ++ if (dev->param.n_caches > 0) { ++ ++ /* If we can't find the data in the cache, ++ * then load it up. */ ++ ++ if (!cache) { ++ cache = ++ yaffs_grab_chunk_cache(in->my_dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ cache->n_bytes = 0; ++ } ++ ++ yaffs_use_cache(dev, cache, 0); ++ ++ cache->locked = 1; ++ ++ memcpy(buffer, &cache->data[start], n_copy); ++ ++ cache->locked = 0; ++ } else { ++ /* Read into the local buffer then copy.. */ ++ ++ u8 *local_buffer = ++ yaffs_get_temp_buffer(dev); ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ ++ memcpy(buffer, &local_buffer[start], n_copy); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Read directly into the buffer. */ ++ yaffs_rd_data_obj(in, chunk, buffer); ++ } ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ return n_done; ++} ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ ++ int chunk; ++ u32 start; ++ int n_copy; ++ int n = n_bytes; ++ int n_done = 0; ++ int n_writeback; ++ loff_t start_write = offset; ++ int chunk_written = 0; ++ u32 n_bytes_read; ++ loff_t chunk_start; ++ struct yaffs_dev *dev; ++ ++ dev = in->my_dev; ++ ++ while (n > 0 && chunk_written >= 0) { ++ yaffs_addr_to_chunk(dev, offset, &chunk, &start); ++ ++ if (((loff_t)chunk) * ++ dev->data_bytes_per_chunk + start != offset || ++ start >= dev->data_bytes_per_chunk) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "AddrToChunk of offset %lld gives chunk %d start %d", ++ offset, chunk, start); ++ } ++ chunk++; /* File pos to chunk in file offset */ ++ ++ /* OK now check for the curveball where the start and end are in ++ * the same chunk. ++ */ ++ ++ if ((start + n) < dev->data_bytes_per_chunk) { ++ n_copy = n; ++ ++ /* Now calculate how many bytes to write back.... ++ * If we're overwriting and not writing to then end of ++ * file then we need to write back as much as was there ++ * before. ++ */ ++ ++ chunk_start = (((loff_t)(chunk - 1)) * ++ dev->data_bytes_per_chunk); ++ ++ if (chunk_start > in->variant.file_variant.file_size) ++ n_bytes_read = 0; /* Past end of file */ ++ else ++ n_bytes_read = ++ in->variant.file_variant.file_size - ++ chunk_start; ++ ++ if (n_bytes_read > dev->data_bytes_per_chunk) ++ n_bytes_read = dev->data_bytes_per_chunk; ++ ++ n_writeback = ++ (n_bytes_read > ++ (start + n)) ? n_bytes_read : (start + n); ++ ++ if (n_writeback < 0 || ++ n_writeback > dev->data_bytes_per_chunk) ++ BUG(); ++ ++ } else { ++ n_copy = dev->data_bytes_per_chunk - start; ++ n_writeback = dev->data_bytes_per_chunk; ++ } ++ ++ if (n_copy != dev->data_bytes_per_chunk || ++ !dev->param.cache_bypass_aligned || ++ dev->param.inband_tags) { ++ /* An incomplete start or end chunk (or maybe both ++ * start and end chunk), or we're using inband tags, ++ * or we're forcing writes through the cache, ++ * so we want to use the cache buffers. ++ */ ++ if (dev->param.n_caches > 0) { ++ struct yaffs_cache *cache; ++ ++ /* If we can't find the data in the cache, then ++ * load the cache */ ++ cache = yaffs_find_chunk_cache(in, chunk); ++ ++ if (!cache && ++ yaffs_check_alloc_available(dev, 1)) { ++ cache = yaffs_grab_chunk_cache(dev); ++ cache->object = in; ++ cache->chunk_id = chunk; ++ cache->dirty = 0; ++ cache->locked = 0; ++ yaffs_rd_data_obj(in, chunk, ++ cache->data); ++ } else if (cache && ++ !cache->dirty && ++ !yaffs_check_alloc_available(dev, ++ 1)) { ++ /* Drop the cache if it was a read cache ++ * item and no space check has been made ++ * for it. ++ */ ++ cache = NULL; ++ } ++ ++ if (cache) { ++ yaffs_use_cache(dev, cache, 1); ++ cache->locked = 1; ++ ++ memcpy(&cache->data[start], buffer, ++ n_copy); ++ ++ cache->locked = 0; ++ cache->n_bytes = n_writeback; ++ ++ if (write_through) { ++ chunk_written = ++ yaffs_wr_data_obj ++ (cache->object, ++ cache->chunk_id, ++ cache->data, ++ cache->n_bytes, 1); ++ cache->dirty = 0; ++ } ++ } else { ++ chunk_written = -1; /* fail write */ ++ } ++ } else { ++ /* An incomplete start or end chunk (or maybe ++ * both start and end chunk). Read into the ++ * local buffer then copy over and write back. ++ */ ++ ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ yaffs_rd_data_obj(in, chunk, local_buffer); ++ memcpy(&local_buffer[start], buffer, n_copy); ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, ++ local_buffer, ++ n_writeback, 0); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ } else { ++ /* A full chunk. Write directly from the buffer. */ ++ ++ chunk_written = ++ yaffs_wr_data_obj(in, chunk, buffer, ++ dev->data_bytes_per_chunk, 0); ++ ++ /* Since we've overwritten the cached data, ++ * we better invalidate it. */ ++ yaffs_invalidate_chunk_cache(in, chunk); ++ } ++ ++ if (chunk_written >= 0) { ++ n -= n_copy; ++ offset += n_copy; ++ buffer += n_copy; ++ n_done += n_copy; ++ } ++ } ++ ++ /* Update file object */ ++ ++ if ((start_write + n_done) > in->variant.file_variant.file_size) ++ in->variant.file_variant.file_size = (start_write + n_done); ++ ++ in->dirty = 1; ++ return n_done; ++} ++ ++int yaffs_wr_file(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_through) ++{ ++ yaffs2_handle_hole(in, offset); ++ return yaffs_do_file_wr(in, buffer, offset, n_bytes, write_through); ++} ++ ++/* ---------------------- File resizing stuff ------------------ */ ++ ++static void yaffs_prune_chunks(struct yaffs_obj *in, loff_t new_size) ++{ ++ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ int i; ++ int chunk_id; ++ u32 dummy; ++ int last_del; ++ int start_del; ++ ++ if (old_size > 0) ++ yaffs_addr_to_chunk(dev, old_size - 1, &last_del, &dummy); ++ else ++ last_del = 0; ++ ++ yaffs_addr_to_chunk(dev, new_size + dev->data_bytes_per_chunk - 1, ++ &start_del, &dummy); ++ last_del++; ++ start_del++; ++ ++ /* Delete backwards so that we don't end up with holes if ++ * power is lost part-way through the operation. ++ */ ++ for (i = last_del; i >= start_del; i--) { ++ /* NB this could be optimised somewhat, ++ * eg. could retrieve the tags and write them without ++ * using yaffs_chunk_del ++ */ ++ ++ chunk_id = yaffs_find_del_file_chunk(in, i, NULL); ++ ++ if (chunk_id < 1) ++ continue; ++ ++ if (chunk_id < ++ (dev->internal_start_block * dev->param.chunks_per_block) || ++ chunk_id >= ++ ((dev->internal_end_block + 1) * ++ dev->param.chunks_per_block)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Found daft chunk_id %d for %d", ++ chunk_id, i); ++ } else { ++ in->n_data_chunks--; ++ yaffs_chunk_del(dev, chunk_id, 1, __LINE__); ++ } ++ } ++} ++ ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size) ++{ ++ int new_full; ++ u32 new_partial; ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_addr_to_chunk(dev, new_size, &new_full, &new_partial); ++ ++ yaffs_prune_chunks(obj, new_size); ++ ++ if (new_partial != 0) { ++ int last_chunk = 1 + new_full; ++ u8 *local_buffer = yaffs_get_temp_buffer(dev); ++ ++ /* Rewrite the last chunk with its new size and zero pad */ ++ yaffs_rd_data_obj(obj, last_chunk, local_buffer); ++ memset(local_buffer + new_partial, 0, ++ dev->data_bytes_per_chunk - new_partial); ++ ++ yaffs_wr_data_obj(obj, last_chunk, local_buffer, ++ new_partial, 1); ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ } ++ ++ obj->variant.file_variant.file_size = new_size; ++ ++ yaffs_prune_tree(dev, &obj->variant.file_variant); ++} ++ ++int yaffs_resize_file(struct yaffs_obj *in, loff_t new_size) ++{ ++ struct yaffs_dev *dev = in->my_dev; ++ loff_t old_size = in->variant.file_variant.file_size; ++ ++ yaffs_flush_file_cache(in); ++ yaffs_invalidate_whole_cache(in); ++ ++ yaffs_check_gc(dev, 0); ++ ++ if (in->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ if (new_size == old_size) ++ return YAFFS_OK; ++ ++ if (new_size > old_size) { ++ yaffs2_handle_hole(in, new_size); ++ in->variant.file_variant.file_size = new_size; ++ } else { ++ /* new_size < old_size */ ++ yaffs_resize_file_down(in, new_size); ++ } ++ ++ /* Write a new object header to reflect the resize. ++ * show we've shrunk the file, if need be ++ * Do this only if the file is not in the deleted directories ++ * and is not shadowed. ++ */ ++ if (in->parent && ++ !in->is_shadowed && ++ in->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ in->parent->obj_id != YAFFS_OBJECTID_DELETED) ++ yaffs_update_oh(in, NULL, 0, 0, 0, NULL); ++ ++ return YAFFS_OK; ++} ++ ++int yaffs_flush_file(struct yaffs_obj *in, int update_time, int data_sync) ++{ ++ if (!in->dirty) ++ return YAFFS_OK; ++ ++ yaffs_flush_file_cache(in); ++ ++ if (data_sync) ++ return YAFFS_OK; ++ ++ if (update_time) ++ yaffs_load_current_time(in, 0, 0); ++ ++ return (yaffs_update_oh(in, NULL, 0, 0, 0, NULL) >= 0) ? ++ YAFFS_OK : YAFFS_FAIL; ++} ++ ++ ++/* yaffs_del_file deletes the whole file data ++ * and the inode associated with the file. ++ * It does not delete the links associated with the file. ++ */ ++static int yaffs_unlink_file_if_needed(struct yaffs_obj *in) ++{ ++ int ret_val; ++ int del_now = 0; ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (!in->my_inode) ++ del_now = 1; ++ ++ if (del_now) { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->del_dir, ++ _Y("deleted"), 0, 0); ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: immediate deletion of file %d", ++ in->obj_id); ++ in->deleted = 1; ++ in->my_dev->n_deleted_files++; ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ yaffs_soft_del_file(in); ++ } else { ++ ret_val = ++ yaffs_change_obj_name(in, in->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++ return ret_val; ++} ++ ++static int yaffs_del_file(struct yaffs_obj *in) ++{ ++ int ret_val = YAFFS_OK; ++ int deleted; /* Need to cache value on stack if in is freed */ ++ struct yaffs_dev *dev = in->my_dev; ++ ++ if (dev->param.disable_soft_del || dev->param.is_yaffs2) ++ yaffs_resize_file(in, 0); ++ ++ if (in->n_data_chunks > 0) { ++ /* Use soft deletion if there is data in the file. ++ * That won't be the case if it has been resized to zero. ++ */ ++ if (!in->unlinked) ++ ret_val = yaffs_unlink_file_if_needed(in); ++ ++ deleted = in->deleted; ++ ++ if (ret_val == YAFFS_OK && in->unlinked && !in->deleted) { ++ in->deleted = 1; ++ deleted = 1; ++ in->my_dev->n_deleted_files++; ++ yaffs_soft_del_file(in); ++ } ++ return deleted ? YAFFS_OK : YAFFS_FAIL; ++ } else { ++ /* The file has no data chunks so we toss it immediately */ ++ yaffs_free_tnode(in->my_dev, in->variant.file_variant.top); ++ in->variant.file_variant.top = NULL; ++ yaffs_generic_obj_del(in); ++ ++ return YAFFS_OK; ++ } ++} ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj) ++{ ++ return (obj && ++ obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) && ++ !(list_empty(&obj->variant.dir_variant.children)); ++} ++ ++static int yaffs_del_dir(struct yaffs_obj *obj) ++{ ++ /* First check that the directory is empty. */ ++ if (yaffs_is_non_empty_dir(obj)) ++ return YAFFS_FAIL; ++ ++ return yaffs_generic_obj_del(obj); ++} ++ ++static int yaffs_del_symlink(struct yaffs_obj *in) ++{ ++ kfree(in->variant.symlink_variant.alias); ++ in->variant.symlink_variant.alias = NULL; ++ ++ return yaffs_generic_obj_del(in); ++} ++ ++static int yaffs_del_link(struct yaffs_obj *in) ++{ ++ /* remove this hardlink from the list associated with the equivalent ++ * object ++ */ ++ list_del_init(&in->hard_links); ++ return yaffs_generic_obj_del(in); ++} ++ ++int yaffs_del_obj(struct yaffs_obj *obj) ++{ ++ int ret_val = -1; ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ ret_val = yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!list_empty(&obj->variant.dir_variant.dirty)) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "Remove object %d from dirty directories", ++ obj->obj_id); ++ list_del_init(&obj->variant.dir_variant.dirty); ++ } ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ ret_val = yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ ret_val = yaffs_del_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ ret_val = yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ ret_val = 0; ++ break; /* should not happen. */ ++ } ++ return ret_val; ++} ++ ++ ++static void yaffs_empty_dir_to_dir(struct yaffs_obj *from_dir, ++ struct yaffs_obj *to_dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ list_for_each_safe(lh, n, &from_dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_add_obj_to_dir(to_dir, obj); ++ } ++} ++ ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type) ++{ ++ /* Tear down the old variant */ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ /* Nuke file data */ ++ yaffs_resize_file(obj, 0); ++ yaffs_free_tnode(obj->my_dev, obj->variant.file_variant.top); ++ obj->variant.file_variant.top = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Put the children in lost and found. */ ++ yaffs_empty_dir_to_dir(obj, obj->my_dev->lost_n_found); ++ if (!list_empty(&obj->variant.dir_variant.dirty)) ++ list_del_init(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ /* Nuke symplink data */ ++ kfree(obj->variant.symlink_variant.alias); ++ obj->variant.symlink_variant.alias = NULL; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ list_del_init(&obj->hard_links); ++ break; ++ default: ++ break; ++ } ++ ++ memset(&obj->variant, 0, sizeof(obj->variant)); ++ ++ /*Set up new variant if the memset is not enough. */ ++ switch (type) { ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ INIT_LIST_HEAD(&obj->variant.dir_variant.children); ++ INIT_LIST_HEAD(&obj->variant.dir_variant.dirty); ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ default: ++ break; ++ } ++ ++ obj->variant_type = type; ++ ++ return obj; ++ ++} ++ ++static int yaffs_unlink_worker(struct yaffs_obj *obj) ++{ ++ int del_now = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (!obj->my_inode) ++ del_now = 1; ++ ++ yaffs_update_parent(obj->parent); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ return yaffs_del_link(obj); ++ } else if (!list_empty(&obj->hard_links)) { ++ /* Curve ball: We're unlinking an object that has a hardlink. ++ * ++ * This problem arises because we are not strictly following ++ * The Linux link/inode model. ++ * ++ * We can't really delete the object. ++ * Instead, we do the following: ++ * - Select a hardlink. ++ * - Unhook it from the hard links ++ * - Move it from its parent directory so that the rename works. ++ * - Rename the object to the hardlink's name. ++ * - Delete the hardlink ++ */ ++ ++ struct yaffs_obj *hl; ++ struct yaffs_obj *parent; ++ int ret_val; ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ hl = list_entry(obj->hard_links.next, struct yaffs_obj, ++ hard_links); ++ ++ yaffs_get_obj_name(hl, name, YAFFS_MAX_NAME_LENGTH + 1); ++ parent = hl->parent; ++ ++ list_del_init(&hl->hard_links); ++ ++ yaffs_add_obj_to_dir(obj->my_dev->unlinked_dir, hl); ++ ++ ret_val = yaffs_change_obj_name(obj, parent, name, 0, 0); ++ ++ if (ret_val == YAFFS_OK) ++ ret_val = yaffs_generic_obj_del(hl); ++ ++ return ret_val; ++ ++ } else if (del_now) { ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return yaffs_del_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ list_del_init(&obj->variant.dir_variant.dirty); ++ return yaffs_del_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return yaffs_del_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ return yaffs_generic_obj_del(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ return YAFFS_FAIL; ++ } ++ } else if (yaffs_is_non_empty_dir(obj)) { ++ return YAFFS_FAIL; ++ } else { ++ return yaffs_change_obj_name(obj, obj->my_dev->unlinked_dir, ++ _Y("unlinked"), 0, 0); ++ } ++} ++ ++static int yaffs_unlink_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->unlink_allowed) ++ return yaffs_unlink_worker(obj); ++ ++ return YAFFS_FAIL; ++} ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR *name) ++{ ++ struct yaffs_obj *obj; ++ ++ obj = yaffs_find_by_name(dir, name); ++ return yaffs_unlink_obj(obj); ++} ++ ++/* Note: ++ * If old_name is NULL then we take old_dir as the object to be renamed. ++ */ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR *old_name, ++ struct yaffs_obj *new_dir, const YCHAR *new_name) ++{ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *existing_target = NULL; ++ int force = 0; ++ int result; ++ struct yaffs_dev *dev; ++ ++ if (!old_dir || old_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ if (!new_dir || new_dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ dev = old_dir->my_dev; ++ ++#ifdef CONFIG_YAFFS_CASE_INSENSITIVE ++ /* Special case for case insemsitive systems. ++ * While look-up is case insensitive, the name isn't. ++ * Therefore we might want to change x.txt to X.txt ++ */ ++ if (old_dir == new_dir && ++ old_name && new_name && ++ strcmp(old_name, new_name) == 0) ++ force = 1; ++#endif ++ ++ if (strnlen(new_name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ /* ENAMETOOLONG */ ++ return YAFFS_FAIL; ++ ++ if (old_name) ++ obj = yaffs_find_by_name(old_dir, old_name); ++ else{ ++ obj = old_dir; ++ old_dir = obj->parent; ++ } ++ ++ if (obj && obj->rename_allowed) { ++ /* Now handle an existing target, if there is one */ ++ existing_target = yaffs_find_by_name(new_dir, new_name); ++ if (yaffs_is_non_empty_dir(existing_target)) { ++ return YAFFS_FAIL; /* ENOTEMPTY */ ++ } else if (existing_target && existing_target != obj) { ++ /* Nuke the target first, using shadowing, ++ * but only if it isn't the same object. ++ * ++ * Note we must disable gc here otherwise it can mess ++ * up the shadowing. ++ * ++ */ ++ dev->gc_disable = 1; ++ yaffs_change_obj_name(obj, new_dir, new_name, force, ++ existing_target->obj_id); ++ existing_target->is_shadowed = 1; ++ yaffs_unlink_obj(existing_target); ++ dev->gc_disable = 0; ++ } ++ ++ result = yaffs_change_obj_name(obj, new_dir, new_name, 1, 0); ++ ++ yaffs_update_parent(old_dir); ++ if (new_dir != old_dir) ++ yaffs_update_parent(new_dir); ++ ++ return result; ++ } ++ return YAFFS_FAIL; ++} ++ ++/*----------------------- Initialisation Scanning ---------------------- */ ++ ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning) ++{ ++ struct yaffs_obj *obj; ++ ++ if (backward_scanning) { ++ /* Handle YAFFS2 case (backward scanning) ++ * If the shadowed object exists then ignore. ++ */ ++ obj = yaffs_find_by_number(dev, obj_id); ++ if (obj) ++ return; ++ } ++ ++ /* Let's create it (if it does not exist) assuming it is a file so that ++ * it can do shrinking etc. ++ * We put it in unlinked dir to be cleaned up after the scanning ++ */ ++ obj = ++ yaffs_find_or_create_by_number(dev, obj_id, YAFFS_OBJECT_TYPE_FILE); ++ if (!obj) ++ return; ++ obj->is_shadowed = 1; ++ yaffs_add_obj_to_dir(dev->unlinked_dir, obj); ++ obj->variant.file_variant.shrink_size = 0; ++ obj->valid = 1; /* So that we don't read any other info. */ ++} ++ ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list) ++{ ++ struct list_head *lh; ++ struct list_head *save; ++ struct yaffs_obj *hl; ++ struct yaffs_obj *in; ++ ++ list_for_each_safe(lh, save, hard_list) { ++ hl = list_entry(lh, struct yaffs_obj, hard_links); ++ in = yaffs_find_by_number(dev, ++ hl->variant.hardlink_variant.equiv_id); ++ ++ if (in) { ++ /* Add the hardlink pointers */ ++ hl->variant.hardlink_variant.equiv_obj = in; ++ list_add(&hl->hard_links, &in->hard_links); ++ } else { ++ /* Todo Need to report/handle this better. ++ * Got a problem... hardlink to a non-existant object ++ */ ++ hl->variant.hardlink_variant.equiv_obj = NULL; ++ INIT_LIST_HEAD(&hl->hard_links); ++ } ++ } ++} ++ ++static void yaffs_strip_deleted_objs(struct yaffs_dev *dev) ++{ ++ /* ++ * Sort out state of unlinked and deleted objects after scanning. ++ */ ++ struct list_head *i; ++ struct list_head *n; ++ struct yaffs_obj *l; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Soft delete all the unlinked files */ ++ list_for_each_safe(i, n, ++ &dev->unlinked_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++ ++ list_for_each_safe(i, n, &dev->del_dir->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ yaffs_del_obj(l); ++ } ++} ++ ++/* ++ * This code iterates through all the objects making sure that they are rooted. ++ * Any unrooted objects are re-rooted in lost+found. ++ * An object needs to be in one of: ++ * - Directly under deleted, unlinked ++ * - Directly or indirectly under root. ++ * ++ * Note: ++ * This code assumes that we don't ever change the current relationships ++ * between directories: ++ * root_dir->parent == unlinked_dir->parent == del_dir->parent == NULL ++ * lost-n-found->parent == root_dir ++ * ++ * This fixes the problem where directories might have inadvertently been ++ * deleted leaving the object "hanging" without being rooted in the ++ * directory tree. ++ */ ++ ++static int yaffs_has_null_parent(struct yaffs_dev *dev, struct yaffs_obj *obj) ++{ ++ return (obj == dev->del_dir || ++ obj == dev->unlinked_dir || obj == dev->root_dir); ++} ++ ++static void yaffs_fix_hanging_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_obj *parent; ++ int i; ++ struct list_head *lh; ++ struct list_head *n; ++ int depth_limit; ++ int hanging; ++ ++ if (dev->read_only) ++ return; ++ ++ /* Iterate through the objects in each hash entry, ++ * looking at each object. ++ * Make sure it is rooted. ++ */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each_safe(lh, n, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ parent = obj->parent; ++ ++ if (yaffs_has_null_parent(dev, obj)) { ++ /* These directories are not hanging */ ++ hanging = 0; ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ hanging = 1; ++ } else if (yaffs_has_null_parent(dev, parent)) { ++ hanging = 0; ++ } else { ++ /* ++ * Need to follow the parent chain to ++ * see if it is hanging. ++ */ ++ hanging = 0; ++ depth_limit = 100; ++ ++ while (parent != dev->root_dir && ++ parent->parent && ++ parent->parent->variant_type == ++ YAFFS_OBJECT_TYPE_DIRECTORY && ++ depth_limit > 0) { ++ parent = parent->parent; ++ depth_limit--; ++ } ++ if (parent != dev->root_dir) ++ hanging = 1; ++ } ++ if (hanging) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Hanging object %d moved to lost and found", ++ obj->obj_id); ++ yaffs_add_obj_to_dir(dev->lost_n_found, obj); ++ } ++ } ++ } ++} ++ ++/* ++ * Delete directory contents for cleaning up lost and found. ++ */ ++static void yaffs_del_dir_contents(struct yaffs_obj *dir) ++{ ++ struct yaffs_obj *obj; ++ struct list_head *lh; ++ struct list_head *n; ++ ++ if (dir->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) ++ BUG(); ++ ++ list_for_each_safe(lh, n, &dir->variant.dir_variant.children) { ++ obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY) ++ yaffs_del_dir_contents(obj); ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Deleting lost_found object %d", ++ obj->obj_id); ++ yaffs_unlink_obj(obj); ++ } ++} ++ ++static void yaffs_empty_l_n_f(struct yaffs_dev *dev) ++{ ++ yaffs_del_dir_contents(dev->lost_n_found); ++} ++ ++ ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *directory, ++ const YCHAR *name) ++{ ++ int sum; ++ struct list_head *i; ++ YCHAR buffer[YAFFS_MAX_NAME_LENGTH + 1]; ++ struct yaffs_obj *l; ++ ++ if (!name) ++ return NULL; ++ ++ if (!directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: null pointer directory" ++ ); ++ BUG(); ++ return NULL; ++ } ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "tragedy: yaffs_find_by_name: non-directory" ++ ); ++ BUG(); ++ } ++ ++ sum = yaffs_calc_name_sum(name); ++ ++ list_for_each(i, &directory->variant.dir_variant.children) { ++ l = list_entry(i, struct yaffs_obj, siblings); ++ ++ if (l->parent != directory) ++ BUG(); ++ ++ yaffs_check_obj_details_loaded(l); ++ ++ /* Special case for lost-n-found */ ++ if (l->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ if (!strcmp(name, YAFFS_LOSTNFOUND_NAME)) ++ return l; ++ } else if (l->sum == sum || l->hdr_chunk <= 0) { ++ /* LostnFound chunk called Objxxx ++ * Do a real check ++ */ ++ yaffs_get_obj_name(l, buffer, ++ YAFFS_MAX_NAME_LENGTH + 1); ++ if (!strncmp(name, buffer, YAFFS_MAX_NAME_LENGTH)) ++ return l; ++ } ++ } ++ return NULL; ++} ++ ++/* GetEquivalentObject dereferences any hard links to get to the ++ * actual object. ++ */ ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj) ++{ ++ if (obj && obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) { ++ obj = obj->variant.hardlink_variant.equiv_obj; ++ yaffs_check_obj_details_loaded(obj); ++ } ++ return obj; ++} ++ ++/* ++ * A note or two on object names. ++ * * If the object name is missing, we then make one up in the form objnnn ++ * ++ * * ASCII names are stored in the object header's name field from byte zero ++ * * Unicode names are historically stored starting from byte zero. ++ * ++ * Then there are automatic Unicode names... ++ * The purpose of these is to save names in a way that can be read as ++ * ASCII or Unicode names as appropriate, thus allowing a Unicode and ASCII ++ * system to share files. ++ * ++ * These automatic unicode are stored slightly differently... ++ * - If the name can fit in the ASCII character space then they are saved as ++ * ascii names as per above. ++ * - If the name needs Unicode then the name is saved in Unicode ++ * starting at oh->name[1]. ++ ++ */ ++static void yaffs_fix_null_name(struct yaffs_obj *obj, YCHAR *name, ++ int buffer_size) ++{ ++ /* Create an object name if we could not find one. */ ++ if (strnlen(name, YAFFS_MAX_NAME_LENGTH) == 0) { ++ YCHAR local_name[20]; ++ YCHAR num_string[20]; ++ YCHAR *x = &num_string[19]; ++ unsigned v = obj->obj_id; ++ num_string[19] = 0; ++ while (v > 0) { ++ x--; ++ *x = '0' + (v % 10); ++ v /= 10; ++ } ++ /* make up a name */ ++ strcpy(local_name, YAFFS_LOSTNFOUND_PREFIX); ++ strcat(local_name, x); ++ strncpy(name, local_name, buffer_size - 1); ++ } ++} ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR *name, int buffer_size) ++{ ++ memset(name, 0, buffer_size * sizeof(YCHAR)); ++ yaffs_check_obj_details_loaded(obj); ++ if (obj->obj_id == YAFFS_OBJECTID_LOSTNFOUND) { ++ strncpy(name, YAFFS_LOSTNFOUND_NAME, buffer_size - 1); ++ } else if (obj->short_name[0]) { ++ strcpy(name, obj->short_name); ++ } else if (obj->hdr_chunk > 0) { ++ int result; ++ u8 *buffer = yaffs_get_temp_buffer(obj->my_dev); ++ ++ struct yaffs_obj_hdr *oh = (struct yaffs_obj_hdr *)buffer; ++ ++ memset(buffer, 0, obj->my_dev->data_bytes_per_chunk); ++ ++ if (obj->hdr_chunk > 0) { ++ result = yaffs_rd_chunk_tags_nand(obj->my_dev, ++ obj->hdr_chunk, ++ buffer, NULL); ++ } ++ yaffs_load_name_from_oh(obj->my_dev, name, oh->name, ++ buffer_size); ++ ++ yaffs_release_temp_buffer(obj->my_dev, buffer); ++ } ++ ++ yaffs_fix_null_name(obj, name, buffer_size); ++ ++ return strnlen(name, YAFFS_MAX_NAME_LENGTH); ++} ++ ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj) ++{ ++ /* Dereference any hard linking */ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ return obj->variant.file_variant.file_size; ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) { ++ if (!obj->variant.symlink_variant.alias) ++ return 0; ++ return strnlen(obj->variant.symlink_variant.alias, ++ YAFFS_MAX_ALIAS_LENGTH); ++ } else { ++ /* Only a directory should drop through to here */ ++ return obj->my_dev->data_bytes_per_chunk; ++ } ++} ++ ++int yaffs_get_obj_link_count(struct yaffs_obj *obj) ++{ ++ int count = 0; ++ struct list_head *i; ++ ++ if (!obj->unlinked) ++ count++; /* the object itself */ ++ ++ list_for_each(i, &obj->hard_links) ++ count++; /* add the hard links; */ ++ ++ return count; ++} ++ ++int yaffs_get_obj_inode(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ return obj->obj_id; ++} ++ ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ return DT_DIR; ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ return DT_LNK; ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ return DT_REG; ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ if (S_ISFIFO(obj->yst_mode)) ++ return DT_FIFO; ++ if (S_ISCHR(obj->yst_mode)) ++ return DT_CHR; ++ if (S_ISBLK(obj->yst_mode)) ++ return DT_BLK; ++ if (S_ISSOCK(obj->yst_mode)) ++ return DT_SOCK; ++ return DT_REG; ++ break; ++ default: ++ return DT_REG; ++ break; ++ } ++} ++ ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj) ++{ ++ obj = yaffs_get_equivalent_obj(obj); ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_SYMLINK) ++ return yaffs_clone_str(obj->variant.symlink_variant.alias); ++ else ++ return yaffs_clone_str(_Y("")); ++} ++ ++/*--------------------------- Initialisation code -------------------------- */ ++ ++static int yaffs_check_dev_fns(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ struct yaffs_tags_handler *tagger = &dev->tagger; ++ ++ /* Common functions, gotta have */ ++ if (!drv->drv_read_chunk_fn || ++ !drv->drv_write_chunk_fn || ++ !drv->drv_erase_fn) ++ return 0; ++ ++ if (dev->param.is_yaffs2 && ++ (!drv->drv_mark_bad_fn || !drv->drv_check_bad_fn)) ++ return 0; ++ ++ /* Install the default tags marshalling functions if needed. */ ++ yaffs_tags_compat_install(dev); ++ yaffs_tags_marshall_install(dev); ++ ++ /* Check we now have the marshalling functions required. */ ++ if (!tagger->write_chunk_tags_fn || ++ !tagger->read_chunk_tags_fn || ++ !tagger->query_block_fn || ++ !tagger->mark_bad_fn) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs_create_initial_dir(struct yaffs_dev *dev) ++{ ++ /* Initialise the unlinked, deleted, root and lost+found directories */ ++ dev->lost_n_found = dev->root_dir = NULL; ++ dev->unlinked_dir = dev->del_dir = NULL; ++ dev->unlinked_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_UNLINKED, S_IFDIR); ++ dev->del_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_DELETED, S_IFDIR); ++ dev->root_dir = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_ROOT, ++ YAFFS_ROOT_MODE | S_IFDIR); ++ dev->lost_n_found = ++ yaffs_create_fake_dir(dev, YAFFS_OBJECTID_LOSTNFOUND, ++ YAFFS_LOSTNFOUND_MODE | S_IFDIR); ++ ++ if (dev->lost_n_found && dev->root_dir && dev->unlinked_dir ++ && dev->del_dir) { ++ yaffs_add_obj_to_dir(dev->root_dir, dev->lost_n_found); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++/* Low level init. ++ * Typically only used by yaffs_guts_initialise, but also used by the ++ * Low level yaffs driver tests. ++ */ ++ ++int yaffs_guts_ll_init(struct yaffs_dev *dev) ++{ ++ ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, "yaffs: yaffs_ll_init()"); ++ ++ if (!dev) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Need a device" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ if (dev->ll_init) ++ return YAFFS_OK; ++ ++ dev->internal_start_block = dev->param.start_block; ++ dev->internal_end_block = dev->param.end_block; ++ dev->block_offset = 0; ++ dev->chunk_offset = 0; ++ dev->n_free_chunks = 0; ++ ++ dev->gc_block = 0; ++ ++ if (dev->param.start_block == 0) { ++ dev->internal_start_block = dev->param.start_block + 1; ++ dev->internal_end_block = dev->param.end_block + 1; ++ dev->block_offset = 1; ++ dev->chunk_offset = dev->param.chunks_per_block; ++ } ++ ++ /* Check geometry parameters. */ ++ ++ if ((!dev->param.inband_tags && dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 1024) || ++ (!dev->param.is_yaffs2 && ++ dev->param.total_bytes_per_chunk < 512) || ++ (dev->param.inband_tags && !dev->param.is_yaffs2) || ++ dev->param.chunks_per_block < 2 || ++ dev->param.n_reserved_blocks < 2 || ++ dev->internal_start_block <= 0 || ++ dev->internal_end_block <= 0 || ++ dev->internal_end_block <= ++ (dev->internal_start_block + dev->param.n_reserved_blocks + 2) ++ ) { ++ /* otherwise it is too small */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "NAND geometry problems: chunk size %d, type is yaffs%s, inband_tags %d ", ++ dev->param.total_bytes_per_chunk, ++ dev->param.is_yaffs2 ? "2" : "", ++ dev->param.inband_tags); ++ return YAFFS_FAIL; ++ } ++ ++ /* Sort out space for inband tags, if required */ ++ if (dev->param.inband_tags) ++ dev->data_bytes_per_chunk = ++ dev->param.total_bytes_per_chunk - ++ sizeof(struct yaffs_packed_tags2_tags_only); ++ else ++ dev->data_bytes_per_chunk = dev->param.total_bytes_per_chunk; ++ ++ /* Got the right mix of functions? */ ++ if (!yaffs_check_dev_fns(dev)) { ++ /* Function missing */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "device function(s) missing or wrong"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ if (yaffs_init_nand(dev) != YAFFS_OK) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "InitialiseNAND failed"); ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev) ++{ ++ int i; ++ enum yaffs_block_state state; ++ u32 dummy; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if(dev->is_mounted) ++ return YAFFS_FAIL; ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ yaffs_query_init_block_state(dev, i, &state, &dummy); ++ if (state != YAFFS_BLOCK_STATE_DEAD) ++ yaffs_erase_block(dev, i); ++ } ++ ++ return YAFFS_OK; ++} ++ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev) ++{ ++ int init_failed = 0; ++ unsigned x; ++ int bits; ++ ++ if(yaffs_guts_ll_init(dev) != YAFFS_OK) ++ return YAFFS_FAIL; ++ ++ if (dev->is_mounted) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "device already mounted"); ++ return YAFFS_FAIL; ++ } ++ ++ dev->is_mounted = 1; ++ ++ /* OK now calculate a few things for the device */ ++ ++ /* ++ * Calculate all the chunk size manipulation numbers: ++ */ ++ x = dev->data_bytes_per_chunk; ++ /* We always use dev->chunk_shift and dev->chunk_div */ ++ dev->chunk_shift = calc_shifts(x); ++ x >>= dev->chunk_shift; ++ dev->chunk_div = x; ++ /* We only use chunk mask if chunk_div is 1 */ ++ dev->chunk_mask = (1 << dev->chunk_shift) - 1; ++ ++ /* ++ * Calculate chunk_grp_bits. ++ * We need to find the next power of 2 > than internal_end_block ++ */ ++ ++ x = dev->param.chunks_per_block * (dev->internal_end_block + 1); ++ ++ bits = calc_shifts_ceiling(x); ++ ++ /* Set up tnode width if wide tnodes are enabled. */ ++ if (!dev->param.wide_tnodes_disabled) { ++ /* bits must be even so that we end up with 32-bit words */ ++ if (bits & 1) ++ bits++; ++ if (bits < 16) ++ dev->tnode_width = 16; ++ else ++ dev->tnode_width = bits; ++ } else { ++ dev->tnode_width = 16; ++ } ++ ++ dev->tnode_mask = (1 << dev->tnode_width) - 1; ++ ++ /* Level0 Tnodes are 16 bits or wider (if wide tnodes are enabled), ++ * so if the bitwidth of the ++ * chunk range we're using is greater than 16 we need ++ * to figure out chunk shift and chunk_grp_size ++ */ ++ ++ if (bits <= dev->tnode_width) ++ dev->chunk_grp_bits = 0; ++ else ++ dev->chunk_grp_bits = bits - dev->tnode_width; ++ ++ dev->tnode_size = (dev->tnode_width * YAFFS_NTNODES_LEVEL0) / 8; ++ if (dev->tnode_size < sizeof(struct yaffs_tnode)) ++ dev->tnode_size = sizeof(struct yaffs_tnode); ++ ++ dev->chunk_grp_size = 1 << dev->chunk_grp_bits; ++ ++ if (dev->param.chunks_per_block < dev->chunk_grp_size) { ++ /* We have a problem because the soft delete won't work if ++ * the chunk group size > chunks per block. ++ * This can be remedied by using larger "virtual blocks". ++ */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "chunk group too large"); ++ ++ return YAFFS_FAIL; ++ } ++ ++ /* Finished verifying the device, continue with initialisation */ ++ ++ /* More device initialisation */ ++ dev->all_gcs = 0; ++ dev->passive_gc_count = 0; ++ dev->oldest_dirty_gc_count = 0; ++ dev->bg_gcs = 0; ++ dev->gc_block_finder = 0; ++ dev->buffered_block = -1; ++ dev->doing_buffered_block_rewrite = 0; ++ dev->n_deleted_files = 0; ++ dev->n_bg_deletions = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_ecc_fixed = 0; ++ dev->n_ecc_unfixed = 0; ++ dev->n_tags_ecc_fixed = 0; ++ dev->n_tags_ecc_unfixed = 0; ++ dev->n_erase_failures = 0; ++ dev->n_erased_blocks = 0; ++ dev->gc_disable = 0; ++ dev->has_pending_prioritised_gc = 1; ++ /* Assume the worst for now, will get fixed on first GC */ ++ INIT_LIST_HEAD(&dev->dirty_dirs); ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ ++ /* Initialise temporary buffers and caches. */ ++ if (!yaffs_init_tmp_buffers(dev)) ++ init_failed = 1; ++ ++ dev->cache = NULL; ++ dev->gc_cleanup_list = NULL; ++ ++ if (!init_failed && dev->param.n_caches > 0) { ++ int i; ++ void *buf; ++ int cache_bytes = ++ dev->param.n_caches * sizeof(struct yaffs_cache); ++ ++ if (dev->param.n_caches > YAFFS_MAX_SHORT_OP_CACHES) ++ dev->param.n_caches = YAFFS_MAX_SHORT_OP_CACHES; ++ ++ dev->cache = kmalloc(cache_bytes, GFP_NOFS); ++ ++ buf = (u8 *) dev->cache; ++ ++ if (dev->cache) ++ memset(dev->cache, 0, cache_bytes); ++ ++ for (i = 0; i < dev->param.n_caches && buf; i++) { ++ dev->cache[i].object = NULL; ++ dev->cache[i].last_use = 0; ++ dev->cache[i].dirty = 0; ++ dev->cache[i].data = buf = ++ kmalloc(dev->param.total_bytes_per_chunk, GFP_NOFS); ++ } ++ if (!buf) ++ init_failed = 1; ++ ++ dev->cache_last_use = 0; ++ } ++ ++ dev->cache_hits = 0; ++ ++ if (!init_failed) { ++ dev->gc_cleanup_list = ++ kmalloc(dev->param.chunks_per_block * sizeof(u32), ++ GFP_NOFS); ++ if (!dev->gc_cleanup_list) ++ init_failed = 1; ++ } ++ ++ if (dev->param.is_yaffs2) ++ dev->param.use_header_file_size = 1; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && dev->param.is_yaffs2 && ++ !dev->param.disable_summary && ++ !yaffs_summary_init(dev)) ++ init_failed = 1; ++ ++ if (!init_failed) { ++ /* Now scan the flash. */ ++ if (dev->param.is_yaffs2) { ++ if (yaffs2_checkpt_restore(dev)) { ++ yaffs_check_obj_details_loaded(dev->root_dir); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | ++ YAFFS_TRACE_MOUNT, ++ "yaffs: restored from checkpoint" ++ ); ++ } else { ++ ++ /* Clean up the mess caused by an aborted ++ * checkpoint load then scan backwards. ++ */ ++ yaffs_deinit_blocks(dev); ++ ++ yaffs_deinit_tnodes_and_objs(dev); ++ ++ dev->n_erased_blocks = 0; ++ dev->n_free_chunks = 0; ++ dev->alloc_block = -1; ++ dev->alloc_page = -1; ++ dev->n_deleted_files = 0; ++ dev->n_unlinked_files = 0; ++ dev->n_bg_deletions = 0; ++ ++ if (!init_failed && !yaffs_init_blocks(dev)) ++ init_failed = 1; ++ ++ yaffs_init_tnodes_and_objs(dev); ++ ++ if (!init_failed ++ && !yaffs_create_initial_dir(dev)) ++ init_failed = 1; ++ ++ if (!init_failed && !yaffs2_scan_backwards(dev)) ++ init_failed = 1; ++ } ++ } else if (!yaffs1_scan(dev)) { ++ init_failed = 1; ++ } ++ ++ yaffs_strip_deleted_objs(dev); ++ yaffs_fix_hanging_objs(dev); ++ if (dev->param.empty_lost_n_found) ++ yaffs_empty_l_n_f(dev); ++ } ++ ++ if (init_failed) { ++ /* Clean up the mess */ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() aborted."); ++ ++ yaffs_deinitialise(dev); ++ return YAFFS_FAIL; ++ } ++ ++ /* Zero out stats */ ++ dev->n_page_reads = 0; ++ dev->n_page_writes = 0; ++ dev->n_erasures = 0; ++ dev->n_gc_copies = 0; ++ dev->n_retried_writes = 0; ++ ++ dev->n_retired_blocks = 0; ++ ++ yaffs_verify_free_chunks(dev); ++ yaffs_verify_blocks(dev); ++ ++ /* Clean up any aborted checkpoint data */ ++ if (!dev->is_checkpointed && dev->blocks_in_checkpt > 0) ++ yaffs2_checkpt_invalidate(dev); ++ ++ yaffs_trace(YAFFS_TRACE_TRACING, ++ "yaffs: yaffs_guts_initialise() done."); ++ return YAFFS_OK; ++} ++ ++void yaffs_deinitialise(struct yaffs_dev *dev) ++{ ++ if (dev->is_mounted) { ++ int i; ++ ++ yaffs_deinit_blocks(dev); ++ yaffs_deinit_tnodes_and_objs(dev); ++ yaffs_summary_deinit(dev); ++ ++ if (dev->param.n_caches > 0 && dev->cache) { ++ ++ for (i = 0; i < dev->param.n_caches; i++) { ++ kfree(dev->cache[i].data); ++ dev->cache[i].data = NULL; ++ } ++ ++ kfree(dev->cache); ++ dev->cache = NULL; ++ } ++ ++ kfree(dev->gc_cleanup_list); ++ ++ for (i = 0; i < YAFFS_N_TEMP_BUFFERS; i++) ++ kfree(dev->temp_buffer[i].buffer); ++ ++ dev->is_mounted = 0; ++ ++ yaffs_deinit_nand(dev); ++ } ++} ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev) ++{ ++ int n_free = 0; ++ int b; ++ struct yaffs_block_info *blk; ++ ++ blk = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ switch (blk->block_state) { ++ case YAFFS_BLOCK_STATE_EMPTY: ++ case YAFFS_BLOCK_STATE_ALLOCATING: ++ case YAFFS_BLOCK_STATE_COLLECTING: ++ case YAFFS_BLOCK_STATE_FULL: ++ n_free += ++ (dev->param.chunks_per_block - blk->pages_in_use + ++ blk->soft_del_pages); ++ break; ++ default: ++ break; ++ } ++ blk++; ++ } ++ return n_free; ++} ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev) ++{ ++ /* This is what we report to the outside world */ ++ int n_free; ++ int n_dirty_caches; ++ int blocks_for_checkpt; ++ int i; ++ ++ n_free = dev->n_free_chunks; ++ n_free += dev->n_deleted_files; ++ ++ /* Now count and subtract the number of dirty chunks in the cache. */ ++ ++ for (n_dirty_caches = 0, i = 0; i < dev->param.n_caches; i++) { ++ if (dev->cache[i].dirty) ++ n_dirty_caches++; ++ } ++ ++ n_free -= n_dirty_caches; ++ ++ n_free -= ++ ((dev->param.n_reserved_blocks + 1) * dev->param.chunks_per_block); ++ ++ /* Now figure checkpoint space and report that... */ ++ blocks_for_checkpt = yaffs_calc_checkpt_blocks_required(dev); ++ ++ n_free -= (blocks_for_checkpt * dev->param.chunks_per_block); ++ ++ if (n_free < 0) ++ n_free = 0; ++ ++ return n_free; ++} ++ ++ ++ ++/* ++ * Marshalling functions to get loff_t file sizes into and out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize) ++{ ++ oh->file_size_low = (fsize & 0xFFFFFFFF); ++ oh->file_size_high = ((fsize >> 32) & 0xFFFFFFFF); ++} ++ ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh) ++{ ++ loff_t retval; ++ ++ if (sizeof(loff_t) >= 8 && ~(oh->file_size_high)) ++ retval = (((loff_t) oh->file_size_high) << 32) | ++ (((loff_t) oh->file_size_low) & 0xFFFFFFFF); ++ else ++ retval = (loff_t) oh->file_size_low; ++ ++ return retval; ++} ++ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]) ++{ ++ int i; ++ struct yaffs_block_info *bi; ++ int s; ++ ++ for(i = 0; i < 10; i++) ++ bs[i] = 0; ++ ++ for(i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ bi = yaffs_get_block_info(dev, i); ++ s = bi->block_state; ++ if(s > YAFFS_BLOCK_STATE_DEAD || s < YAFFS_BLOCK_STATE_UNKNOWN) ++ bs[0]++; ++ else ++ bs[s]++; ++ } ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_guts.h linux-3.4.90/fs/yaffs2/yaffs_guts.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_guts.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,1007 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_GUTS_H__ ++#define __YAFFS_GUTS_H__ ++ ++#include "yportenv.h" ++ ++#define YAFFS_OK 1 ++#define YAFFS_FAIL 0 ++ ++/* Give us a Y=0x59, ++ * Give us an A=0x41, ++ * Give us an FF=0xff ++ * Give us an S=0x53 ++ * And what have we got... ++ */ ++#define YAFFS_MAGIC 0x5941ff53 ++ ++/* ++ * Tnodes form a tree with the tnodes in "levels" ++ * Levels greater than 0 hold 8 slots which point to other tnodes. ++ * Those at level 0 hold 16 slots which point to chunks in NAND. ++ * ++ * A maximum level of 8 thust supports files of size up to: ++ * ++ * 2^(3*MAX_LEVEL+4) ++ * ++ * Thus a max level of 8 supports files with up to 2^^28 chunks which gives ++ * a maximum file size of around 512Gbytees with 2k chunks. ++ */ ++#define YAFFS_NTNODES_LEVEL0 16 ++#define YAFFS_TNODES_LEVEL0_BITS 4 ++#define YAFFS_TNODES_LEVEL0_MASK 0xf ++ ++#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2) ++#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1) ++#define YAFFS_TNODES_INTERNAL_MASK 0x7 ++#define YAFFS_TNODES_MAX_LEVEL 8 ++#define YAFFS_TNODES_MAX_BITS (YAFFS_TNODES_LEVEL0_BITS + \ ++ YAFFS_TNODES_INTERNAL_BITS * \ ++ YAFFS_TNODES_MAX_LEVEL) ++#define YAFFS_MAX_CHUNK_ID ((1 << YAFFS_TNODES_MAX_BITS) - 1) ++ ++#define YAFFS_MAX_FILE_SIZE_32 0x7fffffff ++ ++/* Constants for YAFFS1 mode */ ++#define YAFFS_BYTES_PER_SPARE 16 ++#define YAFFS_BYTES_PER_CHUNK 512 ++#define YAFFS_CHUNK_SIZE_SHIFT 9 ++#define YAFFS_CHUNKS_PER_BLOCK 32 ++#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK) ++ ++#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024 ++#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32 ++ ++ ++ ++#define YAFFS_ALLOCATION_NOBJECTS 100 ++#define YAFFS_ALLOCATION_NTNODES 100 ++#define YAFFS_ALLOCATION_NLINKS 100 ++ ++#define YAFFS_NOBJECT_BUCKETS 256 ++ ++#define YAFFS_OBJECT_SPACE 0x40000 ++#define YAFFS_MAX_OBJECT_ID (YAFFS_OBJECT_SPACE - 1) ++ ++/* Binary data version stamps */ ++#define YAFFS_SUMMARY_VERSION 1 ++#define YAFFS_CHECKPOINT_VERSION 7 ++ ++#ifdef CONFIG_YAFFS_UNICODE ++#define YAFFS_MAX_NAME_LENGTH 127 ++#define YAFFS_MAX_ALIAS_LENGTH 79 ++#else ++#define YAFFS_MAX_NAME_LENGTH 255 ++#define YAFFS_MAX_ALIAS_LENGTH 159 ++#endif ++ ++#define YAFFS_SHORT_NAME_LENGTH 15 ++ ++/* Some special object ids for pseudo objects */ ++#define YAFFS_OBJECTID_ROOT 1 ++#define YAFFS_OBJECTID_LOSTNFOUND 2 ++#define YAFFS_OBJECTID_UNLINKED 3 ++#define YAFFS_OBJECTID_DELETED 4 ++ ++/* Fake object Id for summary data */ ++#define YAFFS_OBJECTID_SUMMARY 0x10 ++ ++/* Pseudo object ids for checkpointing */ ++#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20 ++#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21 ++ ++#define YAFFS_MAX_SHORT_OP_CACHES 20 ++ ++#define YAFFS_N_TEMP_BUFFERS 6 ++ ++/* We limit the number attempts at sucessfully saving a chunk of data. ++ * Small-page devices have 32 pages per block; large-page devices have 64. ++ * Default to something in the order of 5 to 10 blocks worth of chunks. ++ */ ++#define YAFFS_WR_ATTEMPTS (5*64) ++ ++/* Sequence numbers are used in YAFFS2 to determine block allocation order. ++ * The range is limited slightly to help distinguish bad numbers from good. ++ * This also allows us to perhaps in the future use special numbers for ++ * special purposes. ++ * EFFFFF00 allows the allocation of 8 blocks/second (~1Mbytes) for 15 years, ++ * and is a larger number than the lifetime of a 2GB device. ++ */ ++#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000 ++#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xefffff00 ++ ++/* Special sequence number for bad block that failed to be marked bad */ ++#define YAFFS_SEQUENCE_BAD_BLOCK 0xffff0000 ++ ++/* ChunkCache is used for short read/write operations.*/ ++struct yaffs_cache { ++ struct yaffs_obj *object; ++ int chunk_id; ++ int last_use; ++ int dirty; ++ int n_bytes; /* Only valid if the cache is dirty */ ++ int locked; /* Can't push out or flush while locked. */ ++ u8 *data; ++}; ++ ++/* yaffs1 tags structures in RAM ++ * NB This uses bitfield. Bitfields should not straddle a u32 boundary ++ * otherwise the structure size will get blown out. ++ */ ++ ++struct yaffs_tags { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes_lsb:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 n_bytes_msb:2; ++}; ++ ++union yaffs_tags_union { ++ struct yaffs_tags as_tags; ++ u8 as_bytes[8]; ++}; ++ ++ ++/* Stuff used for extended tags in YAFFS2 */ ++ ++enum yaffs_ecc_result { ++ YAFFS_ECC_RESULT_UNKNOWN, ++ YAFFS_ECC_RESULT_NO_ERROR, ++ YAFFS_ECC_RESULT_FIXED, ++ YAFFS_ECC_RESULT_UNFIXED ++}; ++ ++enum yaffs_obj_type { ++ YAFFS_OBJECT_TYPE_UNKNOWN, ++ YAFFS_OBJECT_TYPE_FILE, ++ YAFFS_OBJECT_TYPE_SYMLINK, ++ YAFFS_OBJECT_TYPE_DIRECTORY, ++ YAFFS_OBJECT_TYPE_HARDLINK, ++ YAFFS_OBJECT_TYPE_SPECIAL ++}; ++ ++#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL ++ ++struct yaffs_ext_tags { ++ unsigned chunk_used; /* Status of the chunk: used or unused */ ++ unsigned obj_id; /* If 0 this is not used */ ++ unsigned chunk_id; /* If 0 this is a header, else a data chunk */ ++ unsigned n_bytes; /* Only valid for data chunks */ ++ ++ /* The following stuff only has meaning when we read */ ++ enum yaffs_ecc_result ecc_result; ++ unsigned block_bad; ++ ++ /* YAFFS 1 stuff */ ++ unsigned is_deleted; /* The chunk is marked deleted */ ++ unsigned serial_number; /* Yaffs1 2-bit serial number */ ++ ++ /* YAFFS2 stuff */ ++ unsigned seq_number; /* The sequence number of this block */ ++ ++ /* Extra info if this is an object header (YAFFS2 only) */ ++ ++ unsigned extra_available; /* Extra info available if not zero */ ++ unsigned extra_parent_id; /* The parent object */ ++ unsigned extra_is_shrink; /* Is it a shrink header? */ ++ unsigned extra_shadows; /* Does this shadow another object? */ ++ ++ enum yaffs_obj_type extra_obj_type; /* What object type? */ ++ ++ loff_t extra_file_size; /* Length if it is a file */ ++ unsigned extra_equiv_id; /* Equivalent object for a hard link */ ++}; ++ ++/* Spare structure for YAFFS1 */ ++struct yaffs_spare { ++ u8 tb0; ++ u8 tb1; ++ u8 tb2; ++ u8 tb3; ++ u8 page_status; /* set to 0 to delete the chunk */ ++ u8 block_status; ++ u8 tb4; ++ u8 tb5; ++ u8 ecc1[3]; ++ u8 tb6; ++ u8 tb7; ++ u8 ecc2[3]; ++}; ++ ++/*Special structure for passing through to mtd */ ++struct yaffs_nand_spare { ++ struct yaffs_spare spare; ++ int eccres1; ++ int eccres2; ++}; ++ ++/* Block data in RAM */ ++ ++enum yaffs_block_state { ++ YAFFS_BLOCK_STATE_UNKNOWN = 0, ++ ++ YAFFS_BLOCK_STATE_SCANNING, ++ /* Being scanned */ ++ ++ YAFFS_BLOCK_STATE_NEEDS_SCAN, ++ /* The block might have something on it (ie it is allocating or full, ++ * perhaps empty) but it needs to be scanned to determine its true ++ * state. ++ * This state is only valid during scanning. ++ * NB We tolerate empty because the pre-scanner might be incapable of ++ * deciding ++ * However, if this state is returned on a YAFFS2 device, ++ * then we expect a sequence number ++ */ ++ ++ YAFFS_BLOCK_STATE_EMPTY, ++ /* This block is empty */ ++ ++ YAFFS_BLOCK_STATE_ALLOCATING, ++ /* This block is partially allocated. ++ * At least one page holds valid data. ++ * This is the one currently being used for page ++ * allocation. Should never be more than one of these. ++ * If a block is only partially allocated at mount it is treated as ++ * full. ++ */ ++ ++ YAFFS_BLOCK_STATE_FULL, ++ /* All the pages in this block have been allocated. ++ * If a block was only partially allocated when mounted we treat ++ * it as fully allocated. ++ */ ++ ++ YAFFS_BLOCK_STATE_DIRTY, ++ /* The block was full and now all chunks have been deleted. ++ * Erase me, reuse me. ++ */ ++ ++ YAFFS_BLOCK_STATE_CHECKPOINT, ++ /* This block is assigned to holding checkpoint data. */ ++ ++ YAFFS_BLOCK_STATE_COLLECTING, ++ /* This block is being garbage collected */ ++ ++ YAFFS_BLOCK_STATE_DEAD ++ /* This block has failed and is not in use */ ++}; ++ ++#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1) ++ ++struct yaffs_block_info { ++ ++ s32 soft_del_pages:10; /* number of soft deleted pages */ ++ s32 pages_in_use:10; /* number of pages in use */ ++ u32 block_state:4; /* One of the above block states. */ ++ /* NB use unsigned because enum is sometimes ++ * an int */ ++ u32 needs_retiring:1; /* Data has failed on this block, */ ++ /*need to get valid data off and retire*/ ++ u32 skip_erased_check:1;/* Skip the erased check on this block */ ++ u32 gc_prioritise:1; /* An ECC check or blank check has failed. ++ Block should be prioritised for GC */ ++ u32 chunk_error_strikes:3; /* How many times we've had ecc etc ++ failures on this block and tried to reuse it */ ++ u32 has_summary:1; /* The block has a summary */ ++ ++ u32 has_shrink_hdr:1; /* This block has at least one shrink header */ ++ u32 seq_number; /* block sequence number for yaffs2 */ ++ ++}; ++ ++/* -------------------------- Object structure -------------------------------*/ ++/* This is the object structure as stored on NAND */ ++ ++struct yaffs_obj_hdr { ++ enum yaffs_obj_type type; ++ ++ /* Apply to everything */ ++ int parent_obj_id; ++ u16 sum_no_longer_used; /* checksum of name. No longer used */ ++ YCHAR name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ /* The following apply to all object types except for hard links */ ++ u32 yst_mode; /* protection */ ++ ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++ ++ /* File size applies to files only */ ++ u32 file_size_low; ++ ++ /* Equivalent object id applies to hard links only. */ ++ int equiv_id; ++ ++ /* Alias is for symlinks only. */ ++ YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1]; ++ ++ u32 yst_rdev; /* stuff for block and char devices (major/min) */ ++ ++ u32 win_ctime[2]; ++ u32 win_atime[2]; ++ u32 win_mtime[2]; ++ ++ u32 inband_shadowed_obj_id; ++ u32 inband_is_shrink; ++ ++ u32 file_size_high; ++ u32 reserved[1]; ++ int shadows_obj; /* This object header shadows the ++ specified object if > 0 */ ++ ++ /* is_shrink applies to object headers written when wemake a hole. */ ++ u32 is_shrink; ++ ++}; ++ ++/*--------------------------- Tnode -------------------------- */ ++ ++struct yaffs_tnode { ++ struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL]; ++}; ++ ++/*------------------------ Object -----------------------------*/ ++/* An object can be one of: ++ * - a directory (no data, has children links ++ * - a regular file (data.... not prunes :->). ++ * - a symlink [symbolic link] (the alias). ++ * - a hard link ++ */ ++ ++struct yaffs_file_var { ++ loff_t file_size; ++ loff_t scanned_size; ++ loff_t shrink_size; ++ int top_level; ++ struct yaffs_tnode *top; ++}; ++ ++struct yaffs_dir_var { ++ struct list_head children; /* list of child links */ ++ struct list_head dirty; /* Entry for list of dirty directories */ ++}; ++ ++struct yaffs_symlink_var { ++ YCHAR *alias; ++}; ++ ++struct yaffs_hardlink_var { ++ struct yaffs_obj *equiv_obj; ++ u32 equiv_id; ++}; ++ ++union yaffs_obj_var { ++ struct yaffs_file_var file_variant; ++ struct yaffs_dir_var dir_variant; ++ struct yaffs_symlink_var symlink_variant; ++ struct yaffs_hardlink_var hardlink_variant; ++}; ++ ++struct yaffs_obj { ++ u8 deleted:1; /* This should only apply to unlinked files. */ ++ u8 soft_del:1; /* it has also been soft deleted */ ++ u8 unlinked:1; /* An unlinked file.*/ ++ u8 fake:1; /* A fake object has no presence on NAND. */ ++ u8 rename_allowed:1; /* Some objects cannot be renamed. */ ++ u8 unlink_allowed:1; ++ u8 dirty:1; /* the object needs to be written to flash */ ++ u8 valid:1; /* When the file system is being loaded up, this ++ * object might be created before the data ++ * is available ++ * ie. file data chunks encountered before ++ * the header. ++ */ ++ u8 lazy_loaded:1; /* This object has been lazy loaded and ++ * is missing some detail */ ++ ++ u8 defered_free:1; /* Object is removed from NAND, but is ++ * still in the inode cache. ++ * Free of object is defered. ++ * until the inode is released. ++ */ ++ u8 being_created:1; /* This object is still being created ++ * so skip some verification checks. */ ++ u8 is_shadowed:1; /* This object is shadowed on the way ++ * to being renamed. */ ++ ++ u8 xattr_known:1; /* We know if this has object has xattribs ++ * or not. */ ++ u8 has_xattr:1; /* This object has xattribs. ++ * Only valid if xattr_known. */ ++ ++ u8 serial; /* serial number of chunk in NAND.*/ ++ u16 sum; /* sum of the name to speed searching */ ++ ++ struct yaffs_dev *my_dev; /* The device I'm on */ ++ ++ struct list_head hash_link; /* list of objects in hash bucket */ ++ ++ struct list_head hard_links; /* hard linked object chain*/ ++ ++ /* directory structure stuff */ ++ /* also used for linking up the free list */ ++ struct yaffs_obj *parent; ++ struct list_head siblings; ++ ++ /* Where's my object header in NAND? */ ++ int hdr_chunk; ++ ++ int n_data_chunks; /* Number of data chunks for this file. */ ++ ++ u32 obj_id; /* the object id value */ ++ ++ u32 yst_mode; ++ ++ YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1]; ++ ++#ifdef CONFIG_YAFFS_WINCE ++ u32 win_ctime[2]; ++ u32 win_mtime[2]; ++ u32 win_atime[2]; ++#else ++ u32 yst_uid; ++ u32 yst_gid; ++ u32 yst_atime; ++ u32 yst_mtime; ++ u32 yst_ctime; ++#endif ++ ++ u32 yst_rdev; ++ ++ void *my_inode; ++ ++ enum yaffs_obj_type variant_type; ++ ++ union yaffs_obj_var variant; ++ ++}; ++ ++struct yaffs_obj_bucket { ++ struct list_head list; ++ int count; ++}; ++ ++/* yaffs_checkpt_obj holds the definition of an object as dumped ++ * by checkpointing. ++ */ ++ ++struct yaffs_checkpt_obj { ++ int struct_type; ++ u32 obj_id; ++ u32 parent_id; ++ int hdr_chunk; ++ enum yaffs_obj_type variant_type:3; ++ u8 deleted:1; ++ u8 soft_del:1; ++ u8 unlinked:1; ++ u8 fake:1; ++ u8 rename_allowed:1; ++ u8 unlink_allowed:1; ++ u8 serial; ++ int n_data_chunks; ++ loff_t size_or_equiv_obj; ++}; ++ ++/*--------------------- Temporary buffers ---------------- ++ * ++ * These are chunk-sized working buffers. Each device has a few. ++ */ ++ ++struct yaffs_buffer { ++ u8 *buffer; ++ int in_use; ++}; ++ ++/*----------------- Device ---------------------------------*/ ++ ++struct yaffs_param { ++ const YCHAR *name; ++ ++ /* ++ * Entry parameters set up way early. Yaffs sets up the rest. ++ * The structure should be zeroed out before use so that unused ++ * and default values are zero. ++ */ ++ ++ int inband_tags; /* Use unband tags */ ++ u32 total_bytes_per_chunk; /* Should be >= 512, does not need to ++ be a power of 2 */ ++ int chunks_per_block; /* does not need to be a power of 2 */ ++ int spare_bytes_per_chunk; /* spare area size */ ++ int start_block; /* Start block we're allowed to use */ ++ int end_block; /* End block we're allowed to use */ ++ int n_reserved_blocks; /* Tuneable so that we can reduce ++ * reserved blocks on NOR and RAM. */ ++ ++ int n_caches; /* If <= 0, then short op caching is disabled, ++ * else the number of short op caches. ++ */ ++ int cache_bypass_aligned; /* If non-zero then bypass the cache for ++ * aligned writes. ++ */ ++ ++ int use_nand_ecc; /* Flag to decide whether or not to use ++ * NAND driver ECC on data (yaffs1) */ ++ int tags_9bytes; /* Use 9 byte tags */ ++ int no_tags_ecc; /* Flag to decide whether or not to do ECC ++ * on packed tags (yaffs2) */ ++ ++ int is_yaffs2; /* Use yaffs2 mode on this device */ ++ ++ int empty_lost_n_found; /* Auto-empty lost+found directory on mount */ ++ ++ int refresh_period; /* How often to check for a block refresh */ ++ ++ /* Checkpoint control. Can be set before or after initialisation */ ++ u8 skip_checkpt_rd; ++ u8 skip_checkpt_wr; ++ ++ int enable_xattr; /* Enable xattribs */ ++ ++ int max_objects; /* ++ * Set to limit the number of objects created. ++ * 0 = no limit. ++ */ ++ ++ /* The remove_obj_fn function must be supplied by OS flavours that ++ * need it. ++ * yaffs direct uses it to implement the faster readdir. ++ * Linux uses it to protect the directory during unlocking. ++ */ ++ void (*remove_obj_fn) (struct yaffs_obj *obj); ++ ++ /* Callback to mark the superblock dirty */ ++ void (*sb_dirty_fn) (struct yaffs_dev *dev); ++ ++ /* Callback to control garbage collection. */ ++ unsigned (*gc_control_fn) (struct yaffs_dev *dev); ++ ++ /* Debug control flags. Don't use unless you know what you're doing */ ++ int use_header_file_size; /* Flag to determine if we should use ++ * file sizes from the header */ ++ int disable_lazy_load; /* Disable lazy loading on this device */ ++ int wide_tnodes_disabled; /* Set to disable wide tnodes */ ++ int disable_soft_del; /* yaffs 1 only: Set to disable the use of ++ * softdeletion. */ ++ ++ int defered_dir_update; /* Set to defer directory updates */ ++ ++#ifdef CONFIG_YAFFS_AUTO_UNICODE ++ int auto_unicode; ++#endif ++ int always_check_erased; /* Force chunk erased check always on */ ++ ++ int disable_summary; ++ int disable_bad_block_marking; ++ ++}; ++ ++struct yaffs_driver { ++ int (*drv_write_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len); ++ int (*drv_read_chunk_fn) (struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result); ++ int (*drv_erase_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_check_bad_fn) (struct yaffs_dev *dev, int block_no); ++ int (*drv_initialise_fn) (struct yaffs_dev *dev); ++ int (*drv_deinitialise_fn) (struct yaffs_dev *dev); ++}; ++ ++struct yaffs_tags_handler { ++ int (*write_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags); ++ int (*read_chunk_tags_fn) (struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags); ++ ++ int (*query_block_fn) (struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ int (*mark_bad_fn) (struct yaffs_dev *dev, int block_no); ++}; ++ ++struct yaffs_dev { ++ struct yaffs_param param; ++ struct yaffs_driver drv; ++ struct yaffs_tags_handler tagger; ++ ++ /* Context storage. Holds extra OS specific data for this device */ ++ ++ void *os_context; ++ void *driver_context; ++ ++ struct list_head dev_list; ++ ++ int ll_init; ++ /* Runtime parameters. Set up by YAFFS. */ ++ int data_bytes_per_chunk; ++ ++ /* Non-wide tnode stuff */ ++ u16 chunk_grp_bits; /* Number of bits that need to be resolved if ++ * the tnodes are not wide enough. ++ */ ++ u16 chunk_grp_size; /* == 2^^chunk_grp_bits */ ++ ++ /* Stuff to support wide tnodes */ ++ u32 tnode_width; ++ u32 tnode_mask; ++ u32 tnode_size; ++ ++ /* Stuff for figuring out file offset to chunk conversions */ ++ u32 chunk_shift; /* Shift value */ ++ u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */ ++ u32 chunk_mask; /* Mask to use for power-of-2 case */ ++ ++ int is_mounted; ++ int read_only; ++ int is_checkpointed; ++ ++ /* Stuff to support block offsetting to support start block zero */ ++ int internal_start_block; ++ int internal_end_block; ++ int block_offset; ++ int chunk_offset; ++ ++ /* Runtime checkpointing stuff */ ++ int checkpt_page_seq; /* running sequence number of checkpt pages */ ++ int checkpt_byte_count; ++ int checkpt_byte_offs; ++ u8 *checkpt_buffer; ++ int checkpt_open_write; ++ int blocks_in_checkpt; ++ int checkpt_cur_chunk; ++ int checkpt_cur_block; ++ int checkpt_next_block; ++ int *checkpt_block_list; ++ int checkpt_max_blocks; ++ u32 checkpt_sum; ++ u32 checkpt_xor; ++ ++ int checkpoint_blocks_required; /* Number of blocks needed to store ++ * current checkpoint set */ ++ ++ /* Block Info */ ++ struct yaffs_block_info *block_info; ++ u8 *chunk_bits; /* bitmap of chunks in use */ ++ u8 block_info_alt:1; /* allocated using alternative alloc */ ++ u8 chunk_bits_alt:1; /* allocated using alternative alloc */ ++ int chunk_bit_stride; /* Number of bytes of chunk_bits per block. ++ * Must be consistent with chunks_per_block. ++ */ ++ ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int alloc_block_finder; /* Used to search for next allocation block */ ++ ++ /* Object and Tnode memory management */ ++ void *allocator; ++ int n_obj; ++ int n_tnodes; ++ ++ int n_hardlinks; ++ ++ struct yaffs_obj_bucket obj_bucket[YAFFS_NOBJECT_BUCKETS]; ++ u32 bucket_finder; ++ ++ int n_free_chunks; ++ ++ /* Garbage collection control */ ++ u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */ ++ u32 n_clean_ups; ++ ++ unsigned has_pending_prioritised_gc; /* We think this device might ++ have pending prioritised gcs */ ++ unsigned gc_disable; ++ unsigned gc_block_finder; ++ unsigned gc_dirtiest; ++ unsigned gc_pages_in_use; ++ unsigned gc_not_done; ++ unsigned gc_block; ++ unsigned gc_chunk; ++ unsigned gc_skip; ++ struct yaffs_summary_tags *gc_sum_tags; ++ ++ /* Special directories */ ++ struct yaffs_obj *root_dir; ++ struct yaffs_obj *lost_n_found; ++ ++ int buffered_block; /* Which block is buffered here? */ ++ int doing_buffered_block_rewrite; ++ ++ struct yaffs_cache *cache; ++ int cache_last_use; ++ ++ /* Stuff for background deletion and unlinked files. */ ++ struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted ++ files live. */ ++ struct yaffs_obj *del_dir; /* Directory where deleted objects are ++ sent to disappear. */ ++ struct yaffs_obj *unlinked_deletion; /* Current file being ++ background deleted. */ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* Temporary buffer management */ ++ struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS]; ++ int max_temp; ++ int temp_in_use; ++ int unmanaged_buffer_allocs; ++ int unmanaged_buffer_deallocs; ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ allocating block */ ++ unsigned oldest_dirty_seq; ++ unsigned oldest_dirty_block; ++ ++ /* Block refreshing */ ++ int refresh_skip; /* A skip down counter. ++ * Refresh happens when this gets to zero. */ ++ ++ /* Dirty directory handling */ ++ struct list_head dirty_dirs; /* List of dirty directories */ ++ ++ /* Summary */ ++ int chunks_per_summary; ++ struct yaffs_summary_tags *sum_tags; ++ ++ /* Statistics */ ++ u32 n_page_writes; ++ u32 n_page_reads; ++ u32 n_erasures; ++ u32 n_bad_queries; ++ u32 n_bad_markings; ++ u32 n_erase_failures; ++ u32 n_gc_copies; ++ u32 all_gcs; ++ u32 passive_gc_count; ++ u32 oldest_dirty_gc_count; ++ u32 n_gc_blocks; ++ u32 bg_gcs; ++ u32 n_retried_writes; ++ u32 n_retired_blocks; ++ u32 n_ecc_fixed; ++ u32 n_ecc_unfixed; ++ u32 n_tags_ecc_fixed; ++ u32 n_tags_ecc_unfixed; ++ u32 n_deletions; ++ u32 n_unmarked_deletions; ++ u32 refresh_count; ++ u32 cache_hits; ++ u32 tags_used; ++ u32 summary_used; ++ ++}; ++ ++/* The CheckpointDevice structure holds the device information that changes ++ *at runtime and must be preserved over unmount/mount cycles. ++ */ ++struct yaffs_checkpt_dev { ++ int struct_type; ++ int n_erased_blocks; ++ int alloc_block; /* Current block being allocated off */ ++ u32 alloc_page; ++ int n_free_chunks; ++ ++ int n_deleted_files; /* Count of files awaiting deletion; */ ++ int n_unlinked_files; /* Count of unlinked files. */ ++ int n_bg_deletions; /* Count of background deletions. */ ++ ++ /* yaffs2 runtime stuff */ ++ unsigned seq_number; /* Sequence number of currently ++ * allocating block */ ++ ++}; ++ ++struct yaffs_checkpt_validity { ++ int struct_type; ++ u32 magic; ++ u32 version; ++ u32 head; ++}; ++ ++struct yaffs_shadow_fixer { ++ int obj_id; ++ int shadowed_id; ++ struct yaffs_shadow_fixer *next; ++}; ++ ++/* Structure for doing xattr modifications */ ++struct yaffs_xattr_mod { ++ int set; /* If 0 then this is a deletion */ ++ const YCHAR *name; ++ const void *data; ++ int size; ++ int flags; ++ int result; ++}; ++ ++/*----------------------- YAFFS Functions -----------------------*/ ++ ++int yaffs_guts_initialise(struct yaffs_dev *dev); ++void yaffs_deinitialise(struct yaffs_dev *dev); ++ ++int yaffs_get_n_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_rename_obj(struct yaffs_obj *old_dir, const YCHAR * old_name, ++ struct yaffs_obj *new_dir, const YCHAR * new_name); ++ ++int yaffs_unlinker(struct yaffs_obj *dir, const YCHAR * name); ++int yaffs_del_obj(struct yaffs_obj *obj); ++struct yaffs_obj *yaffs_retype_obj(struct yaffs_obj *obj, ++ enum yaffs_obj_type type); ++ ++ ++int yaffs_get_obj_name(struct yaffs_obj *obj, YCHAR * name, int buffer_size); ++loff_t yaffs_get_obj_length(struct yaffs_obj *obj); ++int yaffs_get_obj_inode(struct yaffs_obj *obj); ++unsigned yaffs_get_obj_type(struct yaffs_obj *obj); ++int yaffs_get_obj_link_count(struct yaffs_obj *obj); ++ ++/* File operations */ ++int yaffs_file_rd(struct yaffs_obj *obj, u8 * buffer, loff_t offset, ++ int n_bytes); ++int yaffs_wr_file(struct yaffs_obj *obj, const u8 * buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++int yaffs_resize_file(struct yaffs_obj *obj, loff_t new_size); ++ ++struct yaffs_obj *yaffs_create_file(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid); ++ ++int yaffs_flush_file(struct yaffs_obj *obj, int update_time, int data_sync); ++ ++/* Flushing and checkpointing */ ++void yaffs_flush_whole_cache(struct yaffs_dev *dev); ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev); ++int yaffs_checkpoint_restore(struct yaffs_dev *dev); ++ ++/* Directory operations */ ++struct yaffs_obj *yaffs_create_dir(struct yaffs_obj *parent, const YCHAR *name, ++ u32 mode, u32 uid, u32 gid); ++struct yaffs_obj *yaffs_find_by_name(struct yaffs_obj *the_dir, ++ const YCHAR *name); ++struct yaffs_obj *yaffs_find_by_number(struct yaffs_dev *dev, u32 number); ++ ++/* Link operations */ ++struct yaffs_obj *yaffs_link_obj(struct yaffs_obj *parent, const YCHAR *name, ++ struct yaffs_obj *equiv_obj); ++ ++struct yaffs_obj *yaffs_get_equivalent_obj(struct yaffs_obj *obj); ++ ++/* Symlink operations */ ++struct yaffs_obj *yaffs_create_symlink(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, const YCHAR *alias); ++YCHAR *yaffs_get_symlink_alias(struct yaffs_obj *obj); ++ ++/* Special inodes (fifos, sockets and devices) */ ++struct yaffs_obj *yaffs_create_special(struct yaffs_obj *parent, ++ const YCHAR *name, u32 mode, u32 uid, ++ u32 gid, u32 rdev); ++ ++int yaffs_set_xattrib(struct yaffs_obj *obj, const YCHAR *name, ++ const void *value, int size, int flags); ++int yaffs_get_xattrib(struct yaffs_obj *obj, const YCHAR *name, void *value, ++ int size); ++int yaffs_list_xattrib(struct yaffs_obj *obj, char *buffer, int size); ++int yaffs_remove_xattrib(struct yaffs_obj *obj, const YCHAR *name); ++ ++/* Special directories */ ++struct yaffs_obj *yaffs_root(struct yaffs_dev *dev); ++struct yaffs_obj *yaffs_lost_n_found(struct yaffs_dev *dev); ++ ++void yaffs_handle_defered_free(struct yaffs_obj *obj); ++ ++void yaffs_update_dirty_dirs(struct yaffs_dev *dev); ++ ++int yaffs_bg_gc(struct yaffs_dev *dev, unsigned urgency); ++ ++/* Debug dump */ ++int yaffs_dump_obj(struct yaffs_obj *obj); ++ ++void yaffs_guts_test(struct yaffs_dev *dev); ++int yaffs_guts_ll_init(struct yaffs_dev *dev); ++ ++ ++/* A few useful functions to be used within the core files*/ ++void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, ++ int lyn); ++int yaffs_check_ff(u8 *buffer, int n_bytes); ++void yaffs_handle_chunk_error(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++ ++u8 *yaffs_get_temp_buffer(struct yaffs_dev *dev); ++void yaffs_release_temp_buffer(struct yaffs_dev *dev, u8 *buffer); ++ ++struct yaffs_obj *yaffs_find_or_create_by_number(struct yaffs_dev *dev, ++ int number, ++ enum yaffs_obj_type type); ++int yaffs_put_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ int nand_chunk, int in_scan); ++void yaffs_set_obj_name(struct yaffs_obj *obj, const YCHAR *name); ++void yaffs_set_obj_name_from_oh(struct yaffs_obj *obj, ++ const struct yaffs_obj_hdr *oh); ++void yaffs_add_obj_to_dir(struct yaffs_obj *directory, struct yaffs_obj *obj); ++YCHAR *yaffs_clone_str(const YCHAR *str); ++void yaffs_link_fixup(struct yaffs_dev *dev, struct list_head *hard_list); ++void yaffs_block_became_dirty(struct yaffs_dev *dev, int block_no); ++int yaffs_update_oh(struct yaffs_obj *in, const YCHAR *name, ++ int force, int is_shrink, int shadows, ++ struct yaffs_xattr_mod *xop); ++void yaffs_handle_shadowed_obj(struct yaffs_dev *dev, int obj_id, ++ int backward_scanning); ++int yaffs_check_alloc_available(struct yaffs_dev *dev, int n_chunks); ++struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev); ++struct yaffs_tnode *yaffs_add_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id, ++ struct yaffs_tnode *passed_tn); ++ ++int yaffs_do_file_wr(struct yaffs_obj *in, const u8 *buffer, loff_t offset, ++ int n_bytes, int write_trhrough); ++void yaffs_resize_file_down(struct yaffs_obj *obj, loff_t new_size); ++void yaffs_skip_rest_of_block(struct yaffs_dev *dev); ++ ++int yaffs_count_free_chunks(struct yaffs_dev *dev); ++ ++struct yaffs_tnode *yaffs_find_tnode_0(struct yaffs_dev *dev, ++ struct yaffs_file_var *file_struct, ++ u32 chunk_id); ++ ++u32 yaffs_get_group_base(struct yaffs_dev *dev, struct yaffs_tnode *tn, ++ unsigned pos); ++ ++int yaffs_is_non_empty_dir(struct yaffs_obj *obj); ++ ++int yaffs_guts_format_dev(struct yaffs_dev *dev); ++ ++void yaffs_addr_to_chunk(struct yaffs_dev *dev, loff_t addr, ++ int *chunk_out, u32 *offset_out); ++/* ++ * Marshalling functions to get loff_t file sizes into aand out of ++ * object headers. ++ */ ++void yaffs_oh_size_load(struct yaffs_obj_hdr *oh, loff_t fsize); ++loff_t yaffs_oh_to_size(struct yaffs_obj_hdr *oh); ++loff_t yaffs_max_file_size(struct yaffs_dev *dev); ++ ++/* ++ * Debug function to count number of blocks in each state ++ * NB Needs to be called with correct number of integers ++ */ ++ ++void yaffs_count_blocks_by_state(struct yaffs_dev *dev, int bs[10]); ++ ++int yaffs_find_chunk_in_file(struct yaffs_obj *in, int inode_chunk, ++ struct yaffs_ext_tags *tags); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_linux.h linux-3.4.90/fs/yaffs2/yaffs_linux.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_linux.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,48 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_LINUX_H__ ++#define __YAFFS_LINUX_H__ ++ ++#include "yportenv.h" ++ ++struct yaffs_linux_context { ++ struct list_head context_list; /* List of these we have mounted */ ++ struct yaffs_dev *dev; ++ struct super_block *super; ++ struct task_struct *bg_thread; /* Background thread for this device */ ++ int bg_running; ++ struct mutex gross_lock; /* Gross locking mutex*/ ++ u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size ++ * at compile time so we have to allocate it. ++ */ ++ struct list_head search_contexts; ++ struct task_struct *readdir_process; ++ unsigned mount_id; ++ int dirty; ++}; ++ ++#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context)) ++#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context)) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++#define WRITE_SIZE_STR "writesize" ++#define WRITE_SIZE(mtd) ((mtd)->writesize) ++#else ++#define WRITE_SIZE_STR "oobblock" ++#define WRITE_SIZE(mtd) ((mtd)->oobblock) ++#endif ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.c linux-3.4.90/fs/yaffs2/yaffs_mtdif.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_mtdif.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,309 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yportenv.h" ++ ++#include "yaffs_mtdif.h" ++ ++#include "linux/mtd/mtd.h" ++#include "linux/types.h" ++#include "linux/time.h" ++#include "linux/major.h" ++#include "linux/mtd/nand.h" ++#include "linux/kernel.h" ++#include "linux/version.h" ++#include "linux/types.h" ++ ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_linux.h" ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++#define mtd_erase(m, ei) (m)->erase(m, ei) ++#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) ++#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) ++#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) ++#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) ++#endif ++ ++ ++ ++int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ u32 addr = ++ ((loff_t) block_no) * dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ struct erase_info ei; ++ int retval = 0; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++ ++static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, ++ const u8 *data, int data_len, ++ const u8 *oob, int oob_len) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", ++ dev, nand_chunk, data, data_len, oob, oob_len); ++ ++ if (!data || !data_len) { ++ data = NULL; ++ data_len = 0; ++ } ++ ++ if (!oob || !oob_len) { ++ oob = NULL; ++ oob_len = 0; ++ } ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = (u8 *)data; ++ ops.oobbuf = (u8 *)oob; ++ ++ retval = mtd_write_oob(mtd, addr, &ops); ++ if (retval) { ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "write_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ } ++ return retval ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, ++ u8 *data, int data_len, ++ u8 *oob, int oob_len, ++ enum yaffs_ecc_result *ecc_result) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ loff_t addr; ++ struct mtd_oob_ops ops; ++ int retval; ++ ++ addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; ++ memset(&ops, 0, sizeof(ops)); ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.len = (data) ? data_len : 0; ++ ops.ooblen = oob_len; ++ ops.datbuf = data; ++ ops.oobbuf = oob; ++ ++#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) ++ /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; ++ * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. ++ */ ++ ops.len = (ops.datbuf) ? ops.len : ops.ooblen; ++#endif ++ /* Read page and oob using MTD. ++ * Check status and determine ECC result. ++ */ ++ retval = mtd_read_oob(mtd, addr, &ops); ++ if (retval) ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "read_oob failed, chunk %d, mtd error %d", ++ nand_chunk, retval); ++ ++ switch (retval) { ++ case 0: ++ /* no error */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ ++ case -EUCLEAN: ++ /* MTD's ECC fixed the data */ ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ break; ++ ++ case -EBADMSG: ++ default: ++ /* MTD's ECC could not fix the data */ ++ dev->n_ecc_unfixed++; ++ if(ecc_result) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ return YAFFS_FAIL; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ loff_t addr; ++ struct erase_info ei; ++ int retval = 0; ++ u32 block_size; ++ ++ block_size = dev->param.total_bytes_per_chunk * ++ dev->param.chunks_per_block; ++ addr = ((loff_t) block_no) * block_size; ++ ++ ei.mtd = mtd; ++ ei.addr = addr; ++ ei.len = block_size; ++ ei.time = 1000; ++ ei.retries = 2; ++ ei.callback = NULL; ++ ei.priv = (u_long) dev; ++ ++ retval = mtd_erase(mtd, &ei); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); ++ ++ retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) ++{ ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); ++ ++ retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); ++ return (retval) ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++static int yaffs_mtd_initialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) ++{ ++ return YAFFS_OK; ++} ++ ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev) ++{ ++ struct yaffs_driver *drv = &dev->drv; ++ ++ drv->drv_write_chunk_fn = yaffs_mtd_write; ++ drv->drv_read_chunk_fn = yaffs_mtd_read; ++ drv->drv_erase_fn = yaffs_mtd_erase; ++ drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; ++ drv->drv_check_bad_fn = yaffs_mtd_check_bad; ++ drv->drv_initialise_fn = yaffs_mtd_initialise; ++ drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; ++} ++ ++ ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev) ++{ ++ struct mtd_info *mtd; ++ ++ mtd = yaffs_get_mtd_device(sdev); ++ ++ /* Check it's an mtd device..... */ ++ if (MAJOR(sdev) != MTD_BLOCK_MAJOR) ++ return NULL; /* This isn't an mtd device */ ++ ++ /* Check it's NAND */ ++ if (mtd->type != MTD_NANDFLASH) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device is not NAND it's type %d", ++ mtd->type); ++ return NULL; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); ++ yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); ++ yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++ yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); ++#else ++ yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); ++#endif ++ ++ return mtd; ++} ++ ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) ++{ ++ if (yaffs_version == 2) { ++ if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || ++ mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && ++ !inband_tags) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not have the right page sizes" ++ ); ++ return -1; ++ } ++ } else { ++ if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || ++ mtd->oobsize != YAFFS_BYTES_PER_SPARE) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "MTD device does not support have the right page sizes" ++ ); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++void yaffs_put_mtd_device(struct mtd_info *mtd) ++{ ++ if(mtd) ++ put_mtd_device(mtd); ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.h linux-3.4.90/fs/yaffs2/yaffs_mtdif.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_mtdif.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,25 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_MTDIF_H__ ++#define __YAFFS_MTDIF_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_mtd_drv_install(struct yaffs_dev *dev); ++struct mtd_info * yaffs_get_mtd_device(dev_t sdev); ++void yaffs_put_mtd_device(struct mtd_info *mtd); ++int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags); ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.c linux-3.4.90/fs/yaffs2/yaffs_nameval.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_nameval.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,208 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * This simple implementation of a name-value store assumes a small number of ++* values and fits into a small finite buffer. ++ * ++ * Each attribute is stored as a record: ++ * sizeof(int) bytes record size. ++ * strnlen+1 bytes name null terminated. ++ * nbytes value. ++ * ---------- ++ * total size stored in record size ++ * ++ * This code has not been tested with unicode yet. ++ */ ++ ++#include "yaffs_nameval.h" ++ ++#include "yportenv.h" ++ ++static int nval_find(const char *xb, int xb_size, const YCHAR *name, ++ int *exist_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ if (!strncmp((YCHAR *) (xb + pos + sizeof(int)), ++ name, size)) { ++ if (exist_size) ++ *exist_size = size; ++ return pos; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ if (exist_size) ++ *exist_size = 0; ++ return -ENODATA; ++} ++ ++static int nval_used(const char *xb, int xb_size) ++{ ++ int pos = 0; ++ int size; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > 0 && (size < xb_size) && (pos + size < xb_size)) { ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return pos; ++} ++ ++int nval_del(char *xb, int xb_size, const YCHAR *name) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos < 0 || pos >= xb_size) ++ return -ENODATA; ++ ++ /* Find size, shift rest over this record, ++ * then zero out the rest of buffer */ ++ memcpy(&size, xb + pos, sizeof(int)); ++ memcpy(xb + pos, xb + pos + size, xb_size - (pos + size)); ++ memset(xb + (xb_size - size), 0, size); ++ return 0; ++} ++ ++int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf, ++ int bsize, int flags) ++{ ++ int pos; ++ int namelen = strnlen(name, xb_size); ++ int reclen; ++ int size_exist = 0; ++ int space; ++ int start; ++ ++ pos = nval_find(xb, xb_size, name, &size_exist); ++ ++ if (flags & XATTR_CREATE && pos >= 0) ++ return -EEXIST; ++ if (flags & XATTR_REPLACE && pos < 0) ++ return -ENODATA; ++ ++ start = nval_used(xb, xb_size); ++ space = xb_size - start + size_exist; ++ ++ reclen = (sizeof(int) + namelen + 1 + bsize); ++ ++ if (reclen > space) ++ return -ENOSPC; ++ ++ if (pos >= 0) { ++ nval_del(xb, xb_size, name); ++ start = nval_used(xb, xb_size); ++ } ++ ++ pos = start; ++ ++ memcpy(xb + pos, &reclen, sizeof(int)); ++ pos += sizeof(int); ++ strncpy((YCHAR *) (xb + pos), name, reclen); ++ pos += (namelen + 1); ++ memcpy(xb + pos, buf, bsize); ++ return 0; ++} ++ ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize) ++{ ++ int pos = nval_find(xb, xb_size, name, NULL); ++ int size; ++ ++ if (pos >= 0 && pos < xb_size) { ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ pos += sizeof(int); /* advance past record length */ ++ size -= sizeof(int); ++ ++ /* Advance over name string */ ++ while (xb[pos] && size > 0 && pos < xb_size) { ++ pos++; ++ size--; ++ } ++ /*Advance over NUL */ ++ pos++; ++ size--; ++ ++ /* If bsize is zero then this is a size query. ++ * Return the size, but don't copy. ++ */ ++ if (!bsize) ++ return size; ++ ++ if (size <= bsize) { ++ memcpy(buf, xb + pos, size); ++ return size; ++ } ++ } ++ if (pos >= 0) ++ return -ERANGE; ++ ++ return -ENODATA; ++} ++ ++int nval_list(const char *xb, int xb_size, char *buf, int bsize) ++{ ++ int pos = 0; ++ int size; ++ int name_len; ++ int ncopied = 0; ++ int filled = 0; ++ ++ memcpy(&size, xb + pos, sizeof(int)); ++ while (size > sizeof(int) && ++ size <= xb_size && ++ (pos + size) < xb_size && ++ !filled) { ++ pos += sizeof(int); ++ size -= sizeof(int); ++ name_len = strnlen((YCHAR *) (xb + pos), size); ++ if (ncopied + name_len + 1 < bsize) { ++ memcpy(buf, xb + pos, name_len * sizeof(YCHAR)); ++ buf += name_len; ++ *buf = '\0'; ++ buf++; ++ if (sizeof(YCHAR) > 1) { ++ *buf = '\0'; ++ buf++; ++ } ++ ncopied += (name_len + 1); ++ } else { ++ filled = 1; ++ } ++ pos += size; ++ if (pos < xb_size - sizeof(int)) ++ memcpy(&size, xb + pos, sizeof(int)); ++ else ++ size = 0; ++ } ++ return ncopied; ++} ++ ++int nval_hasvalues(const char *xb, int xb_size) ++{ ++ return nval_used(xb, xb_size) > 0; ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.h linux-3.4.90/fs/yaffs2/yaffs_nameval.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_nameval.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,28 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __NAMEVAL_H__ ++#define __NAMEVAL_H__ ++ ++#include "yportenv.h" ++ ++int nval_del(char *xb, int xb_size, const YCHAR * name); ++int nval_set(char *xb, int xb_size, const YCHAR * name, const char *buf, ++ int bsize, int flags); ++int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf, ++ int bsize); ++int nval_list(const char *xb, int xb_size, char *buf, int bsize); ++int nval_hasvalues(const char *xb, int xb_size); ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nand.c linux-3.4.90/fs/yaffs2/yaffs_nand.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_nand.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,122 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_nand.h" ++#include "yaffs_tagscompat.h" ++ ++#include "yaffs_getblockinfo.h" ++#include "yaffs_summary.h" ++ ++static int apply_chunk_offset(struct yaffs_dev *dev, int chunk) ++{ ++ return chunk - dev->chunk_offset; ++} ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ struct yaffs_ext_tags local_tags; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_reads++; ++ ++ /* If there are no tags provided use local tags. */ ++ if (!tags) ++ tags = &local_tags; ++ ++ result = dev->tagger.read_chunk_tags_fn(dev, flash_chunk, buffer, tags); ++ if (tags && tags->ecc_result > YAFFS_ECC_RESULT_NO_ERROR) { ++ ++ struct yaffs_block_info *bi; ++ bi = yaffs_get_block_info(dev, ++ nand_chunk / ++ dev->param.chunks_per_block); ++ yaffs_handle_chunk_error(dev, bi); ++ } ++ return result; ++} ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags) ++{ ++ int result; ++ int flash_chunk = apply_chunk_offset(dev, nand_chunk); ++ ++ dev->n_page_writes++; ++ ++ if (!tags) { ++ yaffs_trace(YAFFS_TRACE_ERROR, "Writing with no tags"); ++ BUG(); ++ return YAFFS_FAIL; ++ } ++ ++ tags->seq_number = dev->seq_number; ++ tags->chunk_used = 1; ++ yaffs_trace(YAFFS_TRACE_WRITE, ++ "Writing chunk %d tags %d %d", ++ nand_chunk, tags->obj_id, tags->chunk_id); ++ ++ result = dev->tagger.write_chunk_tags_fn(dev, flash_chunk, ++ buffer, tags); ++ ++ yaffs_summary_add(dev, tags, nand_chunk); ++ ++ return result; ++} ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ block_no -= dev->block_offset; ++ dev->n_bad_markings++; ++ ++ if (dev->param.disable_bad_block_marking) ++ return YAFFS_OK; ++ ++ return dev->tagger.mark_bad_fn(dev, block_no); ++} ++ ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ block_no -= dev->block_offset; ++ return dev->tagger.query_block_fn(dev, block_no, state, seq_number); ++} ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int block_no) ++{ ++ int result; ++ ++ block_no -= dev->block_offset; ++ dev->n_erasures++; ++ result = dev->drv.drv_erase_fn(dev, block_no); ++ return result; ++} ++ ++int yaffs_init_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_initialise_fn) ++ return dev->drv.drv_initialise_fn(dev); ++ return YAFFS_OK; ++} ++ ++int yaffs_deinit_nand(struct yaffs_dev *dev) ++{ ++ if (dev->drv.drv_deinitialise_fn) ++ return dev->drv.drv_deinitialise_fn(dev); ++ return YAFFS_OK; ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_nand.h linux-3.4.90/fs/yaffs2/yaffs_nand.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_nand.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_NAND_H__ ++#define __YAFFS_NAND_H__ ++#include "yaffs_guts.h" ++ ++int yaffs_rd_chunk_tags_nand(struct yaffs_dev *dev, int nand_chunk, ++ u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_wr_chunk_tags_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *buffer, struct yaffs_ext_tags *tags); ++ ++int yaffs_mark_bad(struct yaffs_dev *dev, int block_no); ++ ++int yaffs_query_init_block_state(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ unsigned *seq_number); ++ ++int yaffs_erase_block(struct yaffs_dev *dev, int flash_block); ++ ++int yaffs_init_nand(struct yaffs_dev *dev); ++int yaffs_deinit_nand(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.c linux-3.4.90/fs/yaffs2/yaffs_packedtags1.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_packedtags1.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,56 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags1.h" ++#include "yportenv.h" ++ ++static const u8 all_ff[20] = { ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t) ++{ ++ pt->chunk_id = t->chunk_id; ++ pt->serial_number = t->serial_number; ++ pt->n_bytes = t->n_bytes; ++ pt->obj_id = t->obj_id; ++ pt->ecc = 0; ++ pt->deleted = (t->is_deleted) ? 0 : 1; ++ pt->unused_stuff = 0; ++ pt->should_be_ff = 0xffffffff; ++} ++ ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt) ++{ ++ ++ if (memcmp(all_ff, pt, sizeof(struct yaffs_packed_tags1))) { ++ t->block_bad = 0; ++ if (pt->should_be_ff != 0xffffffff) ++ t->block_bad = 1; ++ t->chunk_used = 1; ++ t->obj_id = pt->obj_id; ++ t->chunk_id = pt->chunk_id; ++ t->n_bytes = pt->n_bytes; ++ t->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ t->is_deleted = (pt->deleted) ? 0 : 1; ++ t->serial_number = pt->serial_number; ++ } else { ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ } ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.h linux-3.4.90/fs/yaffs2/yaffs_packedtags1.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_packedtags1.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS1_H__ ++#define __YAFFS_PACKEDTAGS1_H__ ++ ++#include "yaffs_guts.h" ++ ++struct yaffs_packed_tags1 { ++ u32 chunk_id:20; ++ u32 serial_number:2; ++ u32 n_bytes:10; ++ u32 obj_id:18; ++ u32 ecc:12; ++ u32 deleted:1; ++ u32 unused_stuff:1; ++ unsigned should_be_ff; ++ ++}; ++ ++void yaffs_pack_tags1(struct yaffs_packed_tags1 *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags1(struct yaffs_ext_tags *t, ++ const struct yaffs_packed_tags1 *pt); ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.c linux-3.4.90/fs/yaffs2/yaffs_packedtags2.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_packedtags2.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,197 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_packedtags2.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++ ++/* This code packs a set of extended tags into a binary structure for ++ * NAND storage ++ */ ++ ++/* Some of the information is "extra" struff which can be packed in to ++ * speed scanning ++ * This is defined by having the EXTRA_HEADER_INFO_FLAG set. ++ */ ++ ++/* Extra flags applied to chunk_id */ ++ ++#define EXTRA_HEADER_INFO_FLAG 0x80000000 ++#define EXTRA_SHRINK_FLAG 0x40000000 ++#define EXTRA_SHADOWS_FLAG 0x20000000 ++#define EXTRA_SPARE_FLAGS 0x10000000 ++ ++#define ALL_EXTRA_FLAGS 0xf0000000 ++ ++/* Also, the top 4 bits of the object Id are set to the object type. */ ++#define EXTRA_OBJECT_TYPE_SHIFT (28) ++#define EXTRA_OBJECT_TYPE_MASK ((0x0f) << EXTRA_OBJECT_TYPE_SHIFT) ++ ++static void yaffs_dump_packed_tags2_tags_only( ++ const struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "packed tags obj %d chunk %d byte %d seq %d", ++ ptt->obj_id, ptt->chunk_id, ptt->n_bytes, ptt->seq_number); ++} ++ ++static void yaffs_dump_packed_tags2(const struct yaffs_packed_tags2 *pt) ++{ ++ yaffs_dump_packed_tags2_tags_only(&pt->t); ++} ++ ++static void yaffs_dump_tags2(const struct yaffs_ext_tags *t) ++{ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte %d del %d ser %d seq %d", ++ t->ecc_result, t->block_bad, t->chunk_used, t->obj_id, ++ t->chunk_id, t->n_bytes, t->is_deleted, t->serial_number, ++ t->seq_number); ++ ++} ++ ++static int yaffs_check_tags_extra_packable(const struct yaffs_ext_tags *t) ++{ ++ if (t->chunk_id != 0 || !t->extra_available) ++ return 0; ++ ++ /* Check if the file size is too long to store */ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE && ++ (t->extra_file_size >> 31) != 0) ++ return 0; ++ return 1; ++} ++ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *ptt, ++ const struct yaffs_ext_tags *t) ++{ ++ ptt->chunk_id = t->chunk_id; ++ ptt->seq_number = t->seq_number; ++ ptt->n_bytes = t->n_bytes; ++ ptt->obj_id = t->obj_id; ++ ++ /* Only store extra tags for object headers. ++ * If it is a file then only store if the file size is short\ ++ * enough to fit. ++ */ ++ if (yaffs_check_tags_extra_packable(t)) { ++ /* Store the extra header info instead */ ++ /* We save the parent object in the chunk_id */ ++ ptt->chunk_id = EXTRA_HEADER_INFO_FLAG | t->extra_parent_id; ++ if (t->extra_is_shrink) ++ ptt->chunk_id |= EXTRA_SHRINK_FLAG; ++ if (t->extra_shadows) ++ ptt->chunk_id |= EXTRA_SHADOWS_FLAG; ++ ++ ptt->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ptt->obj_id |= (t->extra_obj_type << EXTRA_OBJECT_TYPE_SHIFT); ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ ptt->n_bytes = t->extra_equiv_id; ++ else if (t->extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ ptt->n_bytes = (unsigned) t->extra_file_size; ++ else ++ ptt->n_bytes = 0; ++ } ++ ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc) ++{ ++ yaffs_pack_tags2_tags_only(&pt->t, t); ++ ++ if (tags_ecc) ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc); ++} ++ ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *ptt) ++{ ++ memset(t, 0, sizeof(struct yaffs_ext_tags)); ++ ++ if (ptt->seq_number == 0xffffffff) ++ return; ++ ++ t->block_bad = 0; ++ t->chunk_used = 1; ++ t->obj_id = ptt->obj_id; ++ t->chunk_id = ptt->chunk_id; ++ t->n_bytes = ptt->n_bytes; ++ t->is_deleted = 0; ++ t->serial_number = 0; ++ t->seq_number = ptt->seq_number; ++ ++ /* Do extra header info stuff */ ++ if (ptt->chunk_id & EXTRA_HEADER_INFO_FLAG) { ++ t->chunk_id = 0; ++ t->n_bytes = 0; ++ ++ t->extra_available = 1; ++ t->extra_parent_id = ptt->chunk_id & (~(ALL_EXTRA_FLAGS)); ++ t->extra_is_shrink = ptt->chunk_id & EXTRA_SHRINK_FLAG ? 1 : 0; ++ t->extra_shadows = ptt->chunk_id & EXTRA_SHADOWS_FLAG ? 1 : 0; ++ t->extra_obj_type = ptt->obj_id >> EXTRA_OBJECT_TYPE_SHIFT; ++ t->obj_id &= ~EXTRA_OBJECT_TYPE_MASK; ++ ++ if (t->extra_obj_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ t->extra_equiv_id = ptt->n_bytes; ++ else ++ t->extra_file_size = ptt->n_bytes; ++ } ++ yaffs_dump_packed_tags2_tags_only(ptt); ++ yaffs_dump_tags2(t); ++} ++ ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc) ++{ ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ if (pt->t.seq_number != 0xffffffff && tags_ecc) { ++ /* Chunk is in use and we need to do ECC */ ++ ++ struct yaffs_ecc_other ecc; ++ int result; ++ yaffs_ecc_calc_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &ecc); ++ result = ++ yaffs_ecc_correct_other((unsigned char *)&pt->t, ++ sizeof(struct yaffs_packed_tags2_tags_only), ++ &pt->ecc, &ecc); ++ switch (result) { ++ case 0: ++ ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ break; ++ case 1: ++ ecc_result = YAFFS_ECC_RESULT_FIXED; ++ break; ++ case -1: ++ ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ break; ++ default: ++ ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ } ++ } ++ yaffs_unpack_tags2_tags_only(t, &pt->t); ++ ++ t->ecc_result = ecc_result; ++ ++ yaffs_dump_packed_tags2(pt); ++ yaffs_dump_tags2(t); ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.h linux-3.4.90/fs/yaffs2/yaffs_packedtags2.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_packedtags2.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,47 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++/* This is used to pack YAFFS2 tags, not YAFFS1tags. */ ++ ++#ifndef __YAFFS_PACKEDTAGS2_H__ ++#define __YAFFS_PACKEDTAGS2_H__ ++ ++#include "yaffs_guts.h" ++#include "yaffs_ecc.h" ++ ++struct yaffs_packed_tags2_tags_only { ++ unsigned seq_number; ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++struct yaffs_packed_tags2 { ++ struct yaffs_packed_tags2_tags_only t; ++ struct yaffs_ecc_other ecc; ++}; ++ ++/* Full packed tags with ECC, used for oob tags */ ++void yaffs_pack_tags2(struct yaffs_packed_tags2 *pt, ++ const struct yaffs_ext_tags *t, int tags_ecc); ++void yaffs_unpack_tags2(struct yaffs_ext_tags *t, struct yaffs_packed_tags2 *pt, ++ int tags_ecc); ++ ++/* Only the tags part (no ECC for use with inband tags */ ++void yaffs_pack_tags2_tags_only(struct yaffs_packed_tags2_tags_only *pt, ++ const struct yaffs_ext_tags *t); ++void yaffs_unpack_tags2_tags_only(struct yaffs_ext_tags *t, ++ struct yaffs_packed_tags2_tags_only *pt); ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_summary.c linux-3.4.90/fs/yaffs2/yaffs_summary.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_summary.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_summary.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,312 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Summaries write the useful part of the tags for the chunks in a block into an ++ * an array which is written to the last n chunks of the block. ++ * Reading the summaries gives all the tags for the block in one read. Much ++ * faster. ++ * ++ * Chunks holding summaries are marked with tags making it look like ++ * they are part of a fake file. ++ * ++ * The summary could also be used during gc. ++ * ++ */ ++ ++#include "yaffs_summary.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_bitmap.h" ++ ++/* ++ * The summary is built up in an array of summary tags. ++ * This gets written to the last one or two (maybe more) chunks in a block. ++ * A summary header is written as the first part of each chunk of summary data. ++ * The summary header must match or the summary is rejected. ++ */ ++ ++/* Summary tags don't need the sequence number because that is redundant. */ ++struct yaffs_summary_tags { ++ unsigned obj_id; ++ unsigned chunk_id; ++ unsigned n_bytes; ++}; ++ ++/* Summary header */ ++struct yaffs_summary_header { ++ unsigned version; /* Must match current version */ ++ unsigned block; /* Must be this block */ ++ unsigned seq; /* Must be this sequence number */ ++ unsigned sum; /* Just add up all the bytes in the tags */ ++}; ++ ++ ++static void yaffs_summary_clear(struct yaffs_dev *dev) ++{ ++ if (!dev->sum_tags) ++ return; ++ memset(dev->sum_tags, 0, dev->chunks_per_summary * ++ sizeof(struct yaffs_summary_tags)); ++} ++ ++ ++void yaffs_summary_deinit(struct yaffs_dev *dev) ++{ ++ kfree(dev->sum_tags); ++ dev->sum_tags = NULL; ++ kfree(dev->gc_sum_tags); ++ dev->gc_sum_tags = NULL; ++ dev->chunks_per_summary = 0; ++} ++ ++int yaffs_summary_init(struct yaffs_dev *dev) ++{ ++ int sum_bytes; ++ int chunks_used; /* Number of chunks used by summary */ ++ int sum_tags_bytes; ++ ++ sum_bytes = dev->param.chunks_per_block * ++ sizeof(struct yaffs_summary_tags); ++ ++ chunks_used = (sum_bytes + dev->data_bytes_per_chunk - 1)/ ++ (dev->data_bytes_per_chunk - ++ sizeof(struct yaffs_summary_header)); ++ ++ dev->chunks_per_summary = dev->param.chunks_per_block - chunks_used; ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ dev->sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ dev->gc_sum_tags = kmalloc(sum_tags_bytes, GFP_NOFS); ++ if (!dev->sum_tags || !dev->gc_sum_tags) { ++ yaffs_summary_deinit(dev); ++ return YAFFS_FAIL; ++ } ++ ++ yaffs_summary_clear(dev); ++ ++ return YAFFS_OK; ++} ++ ++static unsigned yaffs_summary_sum(struct yaffs_dev *dev) ++{ ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int i; ++ unsigned sum = 0; ++ ++ i = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ while (i > 0) { ++ sum += *sum_buffer; ++ sum_buffer++; ++ i--; ++ } ++ ++ return sum; ++} ++ ++static int yaffs_summary_write(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)dev->sum_tags; ++ int n_bytes; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ memset(&tags, 0, sizeof(struct yaffs_ext_tags)); ++ tags.obj_id = YAFFS_OBJECTID_SUMMARY; ++ tags.chunk_id = 1; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = dev->alloc_block * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ hdr.version = YAFFS_SUMMARY_VERSION; ++ hdr.block = blk; ++ hdr.seq = bi->seq_number; ++ hdr.sum = yaffs_summary_sum(dev); ++ ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ memcpy(buffer, &hdr, sizeof(hdr)); ++ memcpy(buffer + sizeof(hdr), sum_buffer, this_tx); ++ tags.n_bytes = this_tx + sizeof(hdr); ++ result = yaffs_wr_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (result != YAFFS_OK) ++ break; ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ dev->n_free_chunks--; ++ ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ tags.chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ ++ if (result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ ++ return result; ++} ++ ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk) ++{ ++ struct yaffs_ext_tags tags; ++ u8 *buffer; ++ u8 *sum_buffer = (u8 *)st; ++ int n_bytes; ++ int chunk_id; ++ int chunk_in_nand; ++ int chunk_in_block; ++ int result; ++ int this_tx; ++ struct yaffs_summary_header hdr; ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int sum_bytes_per_chunk = dev->data_bytes_per_chunk - sizeof(hdr); ++ int sum_tags_bytes; ++ ++ sum_tags_bytes = sizeof(struct yaffs_summary_tags) * ++ dev->chunks_per_summary; ++ buffer = yaffs_get_temp_buffer(dev); ++ n_bytes = sizeof(struct yaffs_summary_tags) * dev->chunks_per_summary; ++ chunk_in_block = dev->chunks_per_summary; ++ chunk_in_nand = blk * dev->param.chunks_per_block + ++ dev->chunks_per_summary; ++ chunk_id = 1; ++ do { ++ this_tx = n_bytes; ++ if (this_tx > sum_bytes_per_chunk) ++ this_tx = sum_bytes_per_chunk; ++ result = yaffs_rd_chunk_tags_nand(dev, chunk_in_nand, ++ buffer, &tags); ++ ++ if (tags.chunk_id != chunk_id || ++ tags.obj_id != YAFFS_OBJECTID_SUMMARY || ++ tags.chunk_used == 0 || ++ tags.ecc_result > YAFFS_ECC_RESULT_FIXED || ++ tags.n_bytes != (this_tx + sizeof(hdr))) ++ result = YAFFS_FAIL; ++ if (result != YAFFS_OK) ++ break; ++ ++ if (st == dev->sum_tags) { ++ /* If we're scanning then update the block info */ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ } ++ memcpy(&hdr, buffer, sizeof(hdr)); ++ memcpy(sum_buffer, buffer + sizeof(hdr), this_tx); ++ n_bytes -= this_tx; ++ sum_buffer += this_tx; ++ chunk_in_nand++; ++ chunk_in_block++; ++ chunk_id++; ++ } while (result == YAFFS_OK && n_bytes > 0); ++ yaffs_release_temp_buffer(dev, buffer); ++ ++ if (result == YAFFS_OK) { ++ /* Verify header */ ++ if (hdr.version != YAFFS_SUMMARY_VERSION || ++ hdr.seq != bi->seq_number || ++ hdr.sum != yaffs_summary_sum(dev)) ++ result = YAFFS_FAIL; ++ } ++ ++ if (st == dev->sum_tags && result == YAFFS_OK) ++ bi->has_summary = 1; ++ ++ return result; ++} ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_nand) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ int block_in_nand = chunk_in_nand / dev->param.chunks_per_block; ++ int chunk_in_block = chunk_in_nand % dev->param.chunks_per_block; ++ ++ if (!dev->sum_tags) ++ return YAFFS_OK; ++ ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ yaffs_pack_tags2_tags_only(&tags_only, tags); ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ sum_tags->chunk_id = tags_only.chunk_id; ++ sum_tags->n_bytes = tags_only.n_bytes; ++ sum_tags->obj_id = tags_only.obj_id; ++ ++ if (chunk_in_block == dev->chunks_per_summary - 1) { ++ /* Time to write out the summary */ ++ yaffs_summary_write(dev, block_in_nand); ++ yaffs_summary_clear(dev); ++ yaffs_skip_rest_of_block(dev); ++ } ++ } ++ return YAFFS_OK; ++} ++ ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block) ++{ ++ struct yaffs_packed_tags2_tags_only tags_only; ++ struct yaffs_summary_tags *sum_tags; ++ if (chunk_in_block >= 0 && chunk_in_block < dev->chunks_per_summary) { ++ sum_tags = &dev->sum_tags[chunk_in_block]; ++ tags_only.chunk_id = sum_tags->chunk_id; ++ tags_only.n_bytes = sum_tags->n_bytes; ++ tags_only.obj_id = sum_tags->obj_id; ++ yaffs_unpack_tags2_tags_only(tags, &tags_only); ++ return YAFFS_OK; ++ } ++ return YAFFS_FAIL; ++} ++ ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk) ++{ ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, blk); ++ int i; ++ ++ if (!bi->has_summary) ++ return; ++ ++ for (i = dev->chunks_per_summary; ++ i < dev->param.chunks_per_block; ++ i++) { ++ if (yaffs_check_chunk_bit(dev, blk, i)) { ++ yaffs_clear_chunk_bit(dev, blk, i); ++ bi->pages_in_use--; ++ dev->n_free_chunks++; ++ } ++ } ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_summary.h linux-3.4.90/fs/yaffs2/yaffs_summary.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_summary.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_summary.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,37 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_SUMMARY_H__ ++#define __YAFFS_SUMMARY_H__ ++ ++#include "yaffs_packedtags2.h" ++ ++ ++int yaffs_summary_init(struct yaffs_dev *dev); ++void yaffs_summary_deinit(struct yaffs_dev *dev); ++ ++int yaffs_summary_add(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_fetch(struct yaffs_dev *dev, ++ struct yaffs_ext_tags *tags, ++ int chunk_in_block); ++int yaffs_summary_read(struct yaffs_dev *dev, ++ struct yaffs_summary_tags *st, ++ int blk); ++void yaffs_summary_gc(struct yaffs_dev *dev, int blk); ++ ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.c linux-3.4.90/fs/yaffs2/yaffs_tagscompat.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_tagscompat.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,381 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_tagscompat.h" ++#include "yaffs_ecc.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_trace.h" ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk); ++ ++ ++/********** Tags ECC calculations *********/ ++ ++ ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags) ++{ ++ /* Calculate an ecc */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ unsigned i, j; ++ unsigned ecc = 0; ++ unsigned bit = 0; ++ ++ tags->ecc = 0; ++ ++ for (i = 0; i < 8; i++) { ++ for (j = 1; j & 0xff; j <<= 1) { ++ bit++; ++ if (b[i] & j) ++ ecc ^= bit; ++ } ++ } ++ tags->ecc = ecc; ++} ++ ++int yaffs_check_tags_ecc(struct yaffs_tags *tags) ++{ ++ unsigned ecc = tags->ecc; ++ ++ yaffs_calc_tags_ecc(tags); ++ ++ ecc ^= tags->ecc; ++ ++ if (ecc && ecc <= 64) { ++ /* TODO: Handle the failure better. Retire? */ ++ unsigned char *b = ((union yaffs_tags_union *)tags)->as_bytes; ++ ++ ecc--; ++ ++ b[ecc / 8] ^= (1 << (ecc & 7)); ++ ++ /* Now recvalc the ecc */ ++ yaffs_calc_tags_ecc(tags); ++ ++ return 1; /* recovered error */ ++ } else if (ecc) { ++ /* Wierd ecc failure value */ ++ /* TODO Need to do somethiong here */ ++ return -1; /* unrecovered error */ ++ } ++ return 0; ++} ++ ++/********** Tags **********/ ++ ++static void yaffs_load_tags_to_spare(struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ ++ yaffs_calc_tags_ecc(tags_ptr); ++ ++ spare_ptr->tb0 = tu->as_bytes[0]; ++ spare_ptr->tb1 = tu->as_bytes[1]; ++ spare_ptr->tb2 = tu->as_bytes[2]; ++ spare_ptr->tb3 = tu->as_bytes[3]; ++ spare_ptr->tb4 = tu->as_bytes[4]; ++ spare_ptr->tb5 = tu->as_bytes[5]; ++ spare_ptr->tb6 = tu->as_bytes[6]; ++ spare_ptr->tb7 = tu->as_bytes[7]; ++} ++ ++static void yaffs_get_tags_from_spare(struct yaffs_dev *dev, ++ struct yaffs_spare *spare_ptr, ++ struct yaffs_tags *tags_ptr) ++{ ++ union yaffs_tags_union *tu = (union yaffs_tags_union *)tags_ptr; ++ int result; ++ ++ tu->as_bytes[0] = spare_ptr->tb0; ++ tu->as_bytes[1] = spare_ptr->tb1; ++ tu->as_bytes[2] = spare_ptr->tb2; ++ tu->as_bytes[3] = spare_ptr->tb3; ++ tu->as_bytes[4] = spare_ptr->tb4; ++ tu->as_bytes[5] = spare_ptr->tb5; ++ tu->as_bytes[6] = spare_ptr->tb6; ++ tu->as_bytes[7] = spare_ptr->tb7; ++ ++ result = yaffs_check_tags_ecc(tags_ptr); ++ if (result > 0) ++ dev->n_tags_ecc_fixed++; ++ else if (result < 0) ++ dev->n_tags_ecc_unfixed++; ++} ++ ++static void yaffs_spare_init(struct yaffs_spare *spare) ++{ ++ memset(spare, 0xff, sizeof(struct yaffs_spare)); ++} ++ ++static int yaffs_wr_nand(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ struct yaffs_spare *spare) ++{ ++ int data_size = dev->data_bytes_per_chunk; ++ ++ return dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, sizeof(*spare)); ++} ++ ++static int yaffs_rd_chunk_nand(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, ++ struct yaffs_spare *spare, ++ enum yaffs_ecc_result *ecc_result, ++ int correct_errors) ++{ ++ int ret_val; ++ struct yaffs_spare local_spare; ++ int data_size; ++ int spare_size; ++ int ecc_result1, ecc_result2; ++ u8 calc_ecc[3]; ++ ++ if (!spare) { ++ /* If we don't have a real spare, then we use a local one. */ ++ /* Need this for the calculation of the ecc */ ++ spare = &local_spare; ++ } ++ data_size = dev->data_bytes_per_chunk; ++ spare_size = sizeof(struct yaffs_spare); ++ ++ if (dev->param.use_nand_ecc) ++ return dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *) spare, spare_size, ++ ecc_result); ++ ++ ++ /* Handle the ECC at this level. */ ++ ++ ret_val = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, data_size, ++ (u8 *)spare, spare_size, ++ NULL); ++ if (!data || !correct_errors) ++ return ret_val; ++ ++ /* Do ECC correction if needed. */ ++ yaffs_ecc_calc(data, calc_ecc); ++ ecc_result1 = yaffs_ecc_correct(data, spare->ecc1, calc_ecc); ++ yaffs_ecc_calc(&data[256], calc_ecc); ++ ecc_result2 = yaffs_ecc_correct(&data[256], spare->ecc2, calc_ecc); ++ ++ if (ecc_result1 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result1 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:0", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result2 > 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error fix performed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_fixed++; ++ } else if (ecc_result2 < 0) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "**>>yaffs ecc error unfixed on chunk %d:1", ++ nand_chunk); ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (ecc_result1 || ecc_result2) { ++ /* We had a data problem on this page */ ++ yaffs_handle_rd_data_error(dev, nand_chunk); ++ } ++ ++ if (ecc_result1 < 0 || ecc_result2 < 0) ++ *ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ else if (ecc_result1 > 0 || ecc_result2 > 0) ++ *ecc_result = YAFFS_ECC_RESULT_FIXED; ++ else ++ *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; ++ ++ return ret_val; ++} ++ ++/* ++ * Functions for robustisizing ++ */ ++ ++static void yaffs_handle_rd_data_error(struct yaffs_dev *dev, int nand_chunk) ++{ ++ int flash_block = nand_chunk / dev->param.chunks_per_block; ++ ++ /* Mark the block for retirement */ ++ yaffs_get_block_info(dev, flash_block + dev->block_offset)-> ++ needs_retiring = 1; ++ yaffs_trace(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS, ++ "**>>Block %d marked for retirement", ++ flash_block); ++ ++ /* TODO: ++ * Just do a garbage collection on the affected block ++ * then retire the block ++ * NB recursion ++ */ ++} ++ ++static int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ ++ yaffs_spare_init(&spare); ++ ++ if (ext_tags->is_deleted) ++ spare.page_status = 0; ++ else { ++ tags.obj_id = ext_tags->obj_id; ++ tags.chunk_id = ext_tags->chunk_id; ++ ++ tags.n_bytes_lsb = ext_tags->n_bytes & (1024 - 1); ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ tags.n_bytes_msb = (ext_tags->n_bytes >> 10) & 3; ++ else ++ tags.n_bytes_msb = 3; ++ ++ tags.serial_number = ext_tags->serial_number; ++ ++ if (!dev->param.use_nand_ecc && data) { ++ yaffs_ecc_calc(data, spare.ecc1); ++ yaffs_ecc_calc(&data[256], spare.ecc2); ++ } ++ ++ yaffs_load_tags_to_spare(&spare, &tags); ++ } ++ return yaffs_wr_nand(dev, nand_chunk, data, &spare); ++} ++ ++static int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *ext_tags) ++{ ++ struct yaffs_spare spare; ++ struct yaffs_tags tags; ++ enum yaffs_ecc_result ecc_result = YAFFS_ECC_RESULT_UNKNOWN; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ int deleted; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ if (!yaffs_rd_chunk_nand(dev, nand_chunk, ++ data, &spare, &ecc_result, 1)) ++ return YAFFS_FAIL; ++ ++ /* ext_tags may be NULL */ ++ if (!ext_tags) ++ return YAFFS_OK; ++ ++ deleted = (hweight8(spare.page_status) < 7) ? 1 : 0; ++ ++ ext_tags->is_deleted = deleted; ++ ext_tags->ecc_result = ecc_result; ++ ext_tags->block_bad = 0; /* We're reading it */ ++ /* therefore it is not a bad block */ ++ ext_tags->chunk_used = ++ memcmp(&spare_ff, &spare, sizeof(spare_ff)) ? 1 : 0; ++ ++ if (ext_tags->chunk_used) { ++ yaffs_get_tags_from_spare(dev, &spare, &tags); ++ ext_tags->obj_id = tags.obj_id; ++ ext_tags->chunk_id = tags.chunk_id; ++ ext_tags->n_bytes = tags.n_bytes_lsb; ++ ++ if (dev->data_bytes_per_chunk >= 1024) ++ ext_tags->n_bytes |= ++ (((unsigned)tags.n_bytes_msb) << 10); ++ ++ ext_tags->serial_number = tags.serial_number; ++ } ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int flash_block) ++{ ++ struct yaffs_spare spare; ++ ++ memset(&spare, 0xff, sizeof(struct yaffs_spare)); ++ ++ spare.block_status = 'Y'; ++ ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block, NULL, ++ &spare); ++ yaffs_wr_nand(dev, flash_block * dev->param.chunks_per_block + 1, ++ NULL, &spare); ++ ++ return YAFFS_OK; ++} ++ ++static int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ struct yaffs_spare spare0, spare1; ++ static struct yaffs_spare spare_ff; ++ static int init; ++ enum yaffs_ecc_result dummy; ++ ++ if (!init) { ++ memset(&spare_ff, 0xff, sizeof(spare_ff)); ++ init = 1; ++ } ++ ++ *seq_number = 0; ++ ++ /* Look for bad block markers in the first two chunks */ ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block, ++ NULL, &spare0, &dummy, 0); ++ yaffs_rd_chunk_nand(dev, block_no * dev->param.chunks_per_block + 1, ++ NULL, &spare1, &dummy, 0); ++ ++ if (hweight8(spare0.block_status & spare1.block_status) < 7) ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ else if (memcmp(&spare_ff, &spare0, sizeof(spare_ff)) == 0) ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ else ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ ++ return YAFFS_OK; ++} ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev) ++{ ++ if(dev->param.is_yaffs2) ++ return; ++ if(!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_compat_wr; ++ if(!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_compat_rd; ++ if(!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_compat_query_block; ++ if(!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_compat_mark_bad; ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.h linux-3.4.90/fs/yaffs2/yaffs_tagscompat.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_tagscompat.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,44 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSCOMPAT_H__ ++#define __YAFFS_TAGSCOMPAT_H__ ++ ++ ++#include "yaffs_guts.h" ++ ++#if 0 ++ ++ ++int yaffs_tags_compat_wr(struct yaffs_dev *dev, ++ int nand_chunk, ++ const u8 *data, const struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_rd(struct yaffs_dev *dev, ++ int nand_chunk, ++ u8 *data, struct yaffs_ext_tags *tags); ++int yaffs_tags_compat_mark_bad(struct yaffs_dev *dev, int block_no); ++int yaffs_tags_compat_query_block(struct yaffs_dev *dev, ++ int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number); ++ ++#endif ++ ++ ++void yaffs_tags_compat_install(struct yaffs_dev *dev); ++void yaffs_calc_tags_ecc(struct yaffs_tags *tags); ++int yaffs_check_tags_ecc(struct yaffs_tags *tags); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.c linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,199 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_packedtags2.h" ++ ++static int yaffs_tags_marshall_write(struct yaffs_dev *dev, ++ int nand_chunk, const u8 *data, ++ const struct yaffs_ext_tags *tags) ++{ ++ struct yaffs_packed_tags2 pt; ++ int retval; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_write chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ /* For yaffs2 writing there must be both data and tags. ++ * If we're using inband tags, then the tags are stuffed into ++ * the end of the data buffer. ++ */ ++ if (!data || !tags) ++ BUG(); ++ else if (dev->param.inband_tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *)(data + ++ dev-> ++ data_bytes_per_chunk); ++ yaffs_pack_tags2_tags_only(pt2tp, tags); ++ } else { ++ yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); ++ } ++ ++ retval = dev->drv.drv_write_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ (dev->param.inband_tags) ? NULL : packed_tags_ptr, ++ (dev->param.inband_tags) ? 0 : packed_tags_size); ++ ++ return retval; ++} ++ ++static int yaffs_tags_marshall_read(struct yaffs_dev *dev, ++ int nand_chunk, u8 *data, ++ struct yaffs_ext_tags *tags) ++{ ++ int retval = 0; ++ int local_data = 0; ++ u8 spare_buffer[100]; ++ enum yaffs_ecc_result ecc_result; ++ ++ struct yaffs_packed_tags2 pt; ++ ++ int packed_tags_size = ++ dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt); ++ void *packed_tags_ptr = ++ dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "yaffs_tags_marshall_read chunk %d data %p tags %p", ++ nand_chunk, data, tags); ++ ++ if (dev->param.inband_tags) { ++ if (!data) { ++ local_data = 1; ++ data = yaffs_get_temp_buffer(dev); ++ } ++ } ++ ++ if (dev->param.inband_tags || (data && !tags)) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ NULL, 0, ++ &ecc_result); ++ else if (tags) ++ retval = dev->drv.drv_read_chunk_fn(dev, nand_chunk, ++ data, dev->param.total_bytes_per_chunk, ++ spare_buffer, packed_tags_size, ++ &ecc_result); ++ else ++ BUG(); ++ ++ ++ if (dev->param.inband_tags) { ++ if (tags) { ++ struct yaffs_packed_tags2_tags_only *pt2tp; ++ pt2tp = ++ (struct yaffs_packed_tags2_tags_only *) ++ &data[dev->data_bytes_per_chunk]; ++ yaffs_unpack_tags2_tags_only(tags, pt2tp); ++ } ++ } else if (tags) { ++ memcpy(packed_tags_ptr, spare_buffer, packed_tags_size); ++ yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); ++ } ++ ++ if (local_data) ++ yaffs_release_temp_buffer(dev, data); ++ ++ if (tags && ecc_result == YAFFS_ECC_RESULT_UNFIXED) { ++ tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; ++ dev->n_ecc_unfixed++; ++ } ++ ++ if (tags && ecc_result == -YAFFS_ECC_RESULT_FIXED) { ++ if (tags->ecc_result <= YAFFS_ECC_RESULT_NO_ERROR) ++ tags->ecc_result = YAFFS_ECC_RESULT_FIXED; ++ dev->n_ecc_fixed++; ++ } ++ ++ if (ecc_result < YAFFS_ECC_RESULT_UNFIXED) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_query_block(struct yaffs_dev *dev, int block_no, ++ enum yaffs_block_state *state, ++ u32 *seq_number) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_MTD, "yaffs_tags_marshall_query_block %d", ++ block_no); ++ ++ retval = dev->drv.drv_check_bad_fn(dev, block_no); ++ ++ if (retval== YAFFS_FAIL) { ++ yaffs_trace(YAFFS_TRACE_MTD, "block is bad"); ++ ++ *state = YAFFS_BLOCK_STATE_DEAD; ++ *seq_number = 0; ++ } else { ++ struct yaffs_ext_tags t; ++ ++ yaffs_tags_marshall_read(dev, ++ block_no * dev->param.chunks_per_block, ++ NULL, &t); ++ ++ if (t.chunk_used) { ++ *seq_number = t.seq_number; ++ *state = YAFFS_BLOCK_STATE_NEEDS_SCAN; ++ } else { ++ *seq_number = 0; ++ *state = YAFFS_BLOCK_STATE_EMPTY; ++ } ++ } ++ ++ yaffs_trace(YAFFS_TRACE_MTD, ++ "block query returns seq %d state %d", ++ *seq_number, *state); ++ ++ if (retval == 0) ++ return YAFFS_OK; ++ else ++ return YAFFS_FAIL; ++} ++ ++static int yaffs_tags_marshall_mark_bad(struct yaffs_dev *dev, int block_no) ++{ ++ return dev->drv.drv_mark_bad_fn(dev, block_no); ++ ++} ++ ++ ++void yaffs_tags_marshall_install(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->tagger.write_chunk_tags_fn) ++ dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; ++ ++ if (!dev->tagger.read_chunk_tags_fn) ++ dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; ++ ++ if (!dev->tagger.query_block_fn) ++ dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; ++ ++ if (!dev->tagger.mark_bad_fn) ++ dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; ++ ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.h linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_tagsmarshall.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_tagsmarshall.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_TAGSMARSHALL_H__ ++#define __YAFFS_TAGSMARSHALL_H__ ++ ++#include "yaffs_guts.h" ++void yaffs_tags_marshall_install(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_trace.h linux-3.4.90/fs/yaffs2/yaffs_trace.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_trace.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,57 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YTRACE_H__ ++#define __YTRACE_H__ ++ ++extern unsigned int yaffs_trace_mask; ++extern unsigned int yaffs_wr_attempts; ++ ++/* ++ * Tracing flags. ++ * The flags masked in YAFFS_TRACE_ALWAYS are always traced. ++ */ ++ ++#define YAFFS_TRACE_OS 0x00000002 ++#define YAFFS_TRACE_ALLOCATE 0x00000004 ++#define YAFFS_TRACE_SCAN 0x00000008 ++#define YAFFS_TRACE_BAD_BLOCKS 0x00000010 ++#define YAFFS_TRACE_ERASE 0x00000020 ++#define YAFFS_TRACE_GC 0x00000040 ++#define YAFFS_TRACE_WRITE 0x00000080 ++#define YAFFS_TRACE_TRACING 0x00000100 ++#define YAFFS_TRACE_DELETION 0x00000200 ++#define YAFFS_TRACE_BUFFERS 0x00000400 ++#define YAFFS_TRACE_NANDACCESS 0x00000800 ++#define YAFFS_TRACE_GC_DETAIL 0x00001000 ++#define YAFFS_TRACE_SCAN_DEBUG 0x00002000 ++#define YAFFS_TRACE_MTD 0x00004000 ++#define YAFFS_TRACE_CHECKPOINT 0x00008000 ++ ++#define YAFFS_TRACE_VERIFY 0x00010000 ++#define YAFFS_TRACE_VERIFY_NAND 0x00020000 ++#define YAFFS_TRACE_VERIFY_FULL 0x00040000 ++#define YAFFS_TRACE_VERIFY_ALL 0x000f0000 ++ ++#define YAFFS_TRACE_SYNC 0x00100000 ++#define YAFFS_TRACE_BACKGROUND 0x00200000 ++#define YAFFS_TRACE_LOCK 0x00400000 ++#define YAFFS_TRACE_MOUNT 0x00800000 ++ ++#define YAFFS_TRACE_ERROR 0x40000000 ++#define YAFFS_TRACE_BUG 0x80000000 ++#define YAFFS_TRACE_ALWAYS 0xf0000000 ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_verify.c linux-3.4.90/fs/yaffs2/yaffs_verify.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_verify.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,529 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_verify.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++ ++int yaffs_skip_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & ++ (YAFFS_TRACE_VERIFY | YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_full_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_FULL)); ++} ++ ++static int yaffs_skip_nand_verification(struct yaffs_dev *dev) ++{ ++ (void) dev; ++ return !(yaffs_trace_mask & (YAFFS_TRACE_VERIFY_NAND)); ++} ++ ++static const char * const block_state_name[] = { ++ "Unknown", ++ "Needs scan", ++ "Scanning", ++ "Empty", ++ "Allocating", ++ "Full", ++ "Dirty", ++ "Checkpoint", ++ "Collecting", ++ "Dead" ++}; ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, int n) ++{ ++ int actually_used; ++ int in_use; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Report illegal runtime states */ ++ if (bi->block_state >= YAFFS_NUMBER_OF_BLOCK_STATES) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has undefined state %d", ++ n, bi->block_state); ++ ++ switch (bi->block_state) { ++ case YAFFS_BLOCK_STATE_UNKNOWN: ++ case YAFFS_BLOCK_STATE_SCANNING: ++ case YAFFS_BLOCK_STATE_NEEDS_SCAN: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has bad run-state %s", ++ n, block_state_name[bi->block_state]); ++ } ++ ++ /* Check pages in use and soft deletions are legal */ ++ ++ actually_used = bi->pages_in_use - bi->soft_del_pages; ++ ++ if (bi->pages_in_use < 0 || ++ bi->pages_in_use > dev->param.chunks_per_block || ++ bi->soft_del_pages < 0 || ++ bi->soft_del_pages > dev->param.chunks_per_block || ++ actually_used < 0 || actually_used > dev->param.chunks_per_block) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has illegal values pages_in_used %d soft_del_pages %d", ++ n, bi->pages_in_use, bi->soft_del_pages); ++ ++ /* Check chunk bitmap legal */ ++ in_use = yaffs_count_chunk_bits(dev, n); ++ if (in_use != bi->pages_in_use) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Block %d has inconsistent values pages_in_use %d counted chunk bits %d", ++ n, bi->pages_in_use, in_use); ++} ++ ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n) ++{ ++ yaffs_verify_blk(dev, bi, n); ++ ++ /* After collection the block should be in the erased state */ ++ ++ if (bi->block_state != YAFFS_BLOCK_STATE_COLLECTING && ++ bi->block_state != YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Block %d is in state %d after gc, should be erased", ++ n, bi->block_state); ++ } ++} ++ ++void yaffs_verify_blocks(struct yaffs_dev *dev) ++{ ++ int i; ++ int state_count[YAFFS_NUMBER_OF_BLOCK_STATES]; ++ int illegal_states = 0; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ memset(state_count, 0, sizeof(state_count)); ++ ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ struct yaffs_block_info *bi = yaffs_get_block_info(dev, i); ++ yaffs_verify_blk(dev, bi, i); ++ ++ if (bi->block_state < YAFFS_NUMBER_OF_BLOCK_STATES) ++ state_count[bi->block_state]++; ++ else ++ illegal_states++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, "Block summary"); ++ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%d blocks have illegal states", ++ illegal_states); ++ if (state_count[YAFFS_BLOCK_STATE_ALLOCATING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many allocating blocks"); ++ ++ for (i = 0; i < YAFFS_NUMBER_OF_BLOCK_STATES; i++) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "%s %d blocks", ++ block_state_name[i], state_count[i]); ++ ++ if (dev->blocks_in_checkpt != state_count[YAFFS_BLOCK_STATE_CHECKPOINT]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Checkpoint block count wrong dev %d count %d", ++ dev->blocks_in_checkpt, ++ state_count[YAFFS_BLOCK_STATE_CHECKPOINT]); ++ ++ if (dev->n_erased_blocks != state_count[YAFFS_BLOCK_STATE_EMPTY]) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Erased block count wrong dev %d count %d", ++ dev->n_erased_blocks, ++ state_count[YAFFS_BLOCK_STATE_EMPTY]); ++ ++ if (state_count[YAFFS_BLOCK_STATE_COLLECTING] > 1) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Too many collecting blocks %d (max is 1)", ++ state_count[YAFFS_BLOCK_STATE_COLLECTING]); ++} ++ ++/* ++ * Verify the object header. oh must be valid, but obj and tags may be NULL in ++ * which case those tests will not be performed. ++ */ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!(tags && obj && oh)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Verifying object header tags %p obj %p oh %p", ++ tags, obj, oh); ++ return; ++ } ++ ++ if (oh->type <= YAFFS_OBJECT_TYPE_UNKNOWN || ++ oh->type > YAFFS_OBJECT_TYPE_MAX) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header type is illegal value 0x%x", ++ tags->obj_id, oh->type); ++ ++ if (tags->obj_id != obj->obj_id) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch obj_id %d", ++ tags->obj_id, obj->obj_id); ++ ++ /* ++ * Check that the object's parent ids match if parent_check requested. ++ * ++ * Tests do not apply to the root object. ++ */ ++ ++ if (parent_check && tags->obj_id > 1 && !obj->parent) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d obj->parent is NULL", ++ tags->obj_id, oh->parent_obj_id); ++ ++ if (parent_check && obj->parent && ++ oh->parent_obj_id != obj->parent->obj_id && ++ (oh->parent_obj_id != YAFFS_OBJECTID_UNLINKED || ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header mismatch parent_id %d parent_obj_id %d", ++ tags->obj_id, oh->parent_obj_id, ++ obj->parent->obj_id); ++ ++ if (tags->obj_id > 1 && oh->name[0] == 0) /* Null name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is NULL", ++ obj->obj_id); ++ ++ if (tags->obj_id > 1 && ((u8) (oh->name[0])) == 0xff) /* Junk name */ ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d header name is 0xff", ++ obj->obj_id); ++} ++ ++void yaffs_verify_file(struct yaffs_obj *obj) ++{ ++ u32 x; ++ int required_depth; ++ int actual_depth; ++ int last_chunk; ++ u32 offset_in_chunk; ++ u32 the_chunk; ++ ++ u32 i; ++ struct yaffs_dev *dev; ++ struct yaffs_ext_tags tags; ++ struct yaffs_tnode *tn; ++ u32 obj_id; ++ ++ if (!obj) ++ return; ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ dev = obj->my_dev; ++ obj_id = obj->obj_id; ++ ++ ++ /* Check file size is consistent with tnode depth */ ++ yaffs_addr_to_chunk(dev, obj->variant.file_variant.file_size, ++ &last_chunk, &offset_in_chunk); ++ last_chunk++; ++ x = last_chunk >> YAFFS_TNODES_LEVEL0_BITS; ++ required_depth = 0; ++ while (x > 0) { ++ x >>= YAFFS_TNODES_INTERNAL_BITS; ++ required_depth++; ++ } ++ ++ actual_depth = obj->variant.file_variant.top_level; ++ ++ /* Check that the chunks in the tnode tree are all correct. ++ * We do this by scanning through the tnode tree and ++ * checking the tags for every chunk match. ++ */ ++ ++ if (yaffs_skip_nand_verification(dev)) ++ return; ++ ++ for (i = 1; i <= last_chunk; i++) { ++ tn = yaffs_find_tnode_0(dev, &obj->variant.file_variant, i); ++ ++ if (!tn) ++ continue; ++ ++ the_chunk = yaffs_get_group_base(dev, tn, i); ++ if (the_chunk > 0) { ++ yaffs_rd_chunk_tags_nand(dev, the_chunk, NULL, ++ &tags); ++ if (tags.obj_id != obj_id || tags.chunk_id != i) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Object %d chunk_id %d NAND mismatch chunk %d tags (%d:%d)", ++ obj_id, i, the_chunk, ++ tags.obj_id, tags.chunk_id); ++ } ++ } ++} ++ ++void yaffs_verify_link(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify sane equivalent object */ ++} ++ ++void yaffs_verify_symlink(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ /* Verify symlink string */ ++} ++ ++void yaffs_verify_special(struct yaffs_obj *obj) ++{ ++ if (obj && yaffs_skip_verification(obj->my_dev)) ++ return; ++} ++ ++void yaffs_verify_obj(struct yaffs_obj *obj) ++{ ++ struct yaffs_dev *dev; ++ u32 chunk_min; ++ u32 chunk_max; ++ u32 chunk_id_ok; ++ u32 chunk_in_range; ++ u32 chunk_wrongly_deleted; ++ u32 chunk_valid; ++ ++ if (!obj) ++ return; ++ ++ if (obj->being_created) ++ return; ++ ++ dev = obj->my_dev; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Check sane object header chunk */ ++ ++ chunk_min = dev->internal_start_block * dev->param.chunks_per_block; ++ chunk_max = ++ (dev->internal_end_block + 1) * dev->param.chunks_per_block - 1; ++ ++ chunk_in_range = (((unsigned)(obj->hdr_chunk)) >= chunk_min && ++ ((unsigned)(obj->hdr_chunk)) <= chunk_max); ++ chunk_id_ok = chunk_in_range || (obj->hdr_chunk == 0); ++ chunk_valid = chunk_in_range && ++ yaffs_check_chunk_bit(dev, ++ obj->hdr_chunk / dev->param.chunks_per_block, ++ obj->hdr_chunk % dev->param.chunks_per_block); ++ chunk_wrongly_deleted = chunk_in_range && !chunk_valid; ++ ++ if (!obj->fake && (!chunk_id_ok || chunk_wrongly_deleted)) ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has chunk_id %d %s %s", ++ obj->obj_id, obj->hdr_chunk, ++ chunk_id_ok ? "" : ",out of range", ++ chunk_wrongly_deleted ? ",marked as deleted" : ""); ++ ++ if (chunk_valid && !yaffs_skip_nand_verification(dev)) { ++ struct yaffs_ext_tags tags; ++ struct yaffs_obj_hdr *oh; ++ u8 *buffer = yaffs_get_temp_buffer(dev); ++ ++ oh = (struct yaffs_obj_hdr *)buffer; ++ ++ yaffs_rd_chunk_tags_nand(dev, obj->hdr_chunk, buffer, &tags); ++ ++ yaffs_verify_oh(obj, oh, &tags, 1); ++ ++ yaffs_release_temp_buffer(dev, buffer); ++ } ++ ++ /* Verify it has a parent */ ++ if (obj && !obj->fake && (!obj->parent || obj->parent->my_dev != dev)) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has parent pointer %p which does not look like an object", ++ obj->obj_id, obj->parent); ++ } ++ ++ /* Verify parent is a directory */ ++ if (obj->parent && ++ obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d's parent is not a directory (type %d)", ++ obj->obj_id, obj->parent->variant_type); ++ } ++ ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ yaffs_verify_file(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ yaffs_verify_symlink(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ yaffs_verify_dir(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ yaffs_verify_link(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ yaffs_verify_special(obj); ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ default: ++ yaffs_trace(YAFFS_TRACE_VERIFY, ++ "Obj %d has illegaltype %d", ++ obj->obj_id, obj->variant_type); ++ break; ++ } ++} ++ ++void yaffs_verify_objects(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ int i; ++ struct list_head *lh; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ for (i = 0; i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ yaffs_verify_obj(obj); ++ } ++ } ++} ++ ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ int count = 0; ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "No object to verify"); ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_verification(obj->my_dev)) ++ return; ++ ++ if (!obj->parent) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Object does not have parent"); ++ BUG(); ++ return; ++ } ++ ++ if (obj->parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "Parent is not directory"); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &obj->parent->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ yaffs_verify_obj(list_obj); ++ if (obj == list_obj) ++ count++; ++ } ++ ++ if (count != 1) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory %d times", ++ count); ++ BUG(); ++ } ++} ++ ++void yaffs_verify_dir(struct yaffs_obj *directory) ++{ ++ struct list_head *lh; ++ struct yaffs_obj *list_obj; ++ ++ if (!directory) { ++ BUG(); ++ return; ++ } ++ ++ if (yaffs_skip_full_verification(directory->my_dev)) ++ return; ++ ++ if (directory->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Directory has wrong type: %d", ++ directory->variant_type); ++ BUG(); ++ } ++ ++ /* Iterate through the objects in each hash entry */ ++ ++ list_for_each(lh, &directory->variant.dir_variant.children) { ++ list_obj = list_entry(lh, struct yaffs_obj, siblings); ++ if (list_obj->parent != directory) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Object in directory list has wrong parent %p", ++ list_obj->parent); ++ BUG(); ++ } ++ yaffs_verify_obj_in_dir(list_obj); ++ } ++} ++ ++static int yaffs_free_verification_failures; ++ ++void yaffs_verify_free_chunks(struct yaffs_dev *dev) ++{ ++ int counted; ++ int difference; ++ ++ if (yaffs_skip_verification(dev)) ++ return; ++ ++ counted = yaffs_count_free_chunks(dev); ++ ++ difference = dev->n_free_chunks - counted; ++ ++ if (difference) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Freechunks verification failure %d %d %d", ++ dev->n_free_chunks, counted, difference); ++ yaffs_free_verification_failures++; ++ } ++} ++ ++int yaffs_verify_file_sane(struct yaffs_obj *in) ++{ ++ (void) in; ++ return YAFFS_OK; ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_verify.h linux-3.4.90/fs/yaffs2/yaffs_verify.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_verify.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,43 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_VERIFY_H__ ++#define __YAFFS_VERIFY_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_verify_blk(struct yaffs_dev *dev, struct yaffs_block_info *bi, ++ int n); ++void yaffs_verify_collected_blk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, int n); ++void yaffs_verify_blocks(struct yaffs_dev *dev); ++ ++void yaffs_verify_oh(struct yaffs_obj *obj, struct yaffs_obj_hdr *oh, ++ struct yaffs_ext_tags *tags, int parent_check); ++void yaffs_verify_file(struct yaffs_obj *obj); ++void yaffs_verify_link(struct yaffs_obj *obj); ++void yaffs_verify_symlink(struct yaffs_obj *obj); ++void yaffs_verify_special(struct yaffs_obj *obj); ++void yaffs_verify_obj(struct yaffs_obj *obj); ++void yaffs_verify_objects(struct yaffs_dev *dev); ++void yaffs_verify_obj_in_dir(struct yaffs_obj *obj); ++void yaffs_verify_dir(struct yaffs_obj *directory); ++void yaffs_verify_free_chunks(struct yaffs_dev *dev); ++ ++int yaffs_verify_file_sane(struct yaffs_obj *obj); ++ ++int yaffs_skip_verification(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_vfs.c linux-3.4.90/fs/yaffs2/yaffs_vfs.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_vfs.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,3600 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * Acknowledgements: ++ * Luc van OostenRyck for numerous patches. ++ * Nick Bane for numerous patches. ++ * Nick Bane for 2.5/2.6 integration. ++ * Andras Toth for mknod rdev issue. ++ * Michael Fischer for finding the problem with inode inconsistency. ++ * Some code bodily lifted from JFFS ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * ++ * This is the file system front-end to YAFFS that hooks it up to ++ * the VFS. ++ * ++ * Special notes: ++ * >> 2.4: sb->u.generic_sbp points to the struct yaffs_dev associated with ++ * this superblock ++ * >> 2.6: sb->s_fs_info points to the struct yaffs_dev associated with this ++ * superblock ++ * >> inode->u.generic_ip points to the associated struct yaffs_obj. ++ */ ++ ++/* ++ * There are two variants of the VFS glue code. This variant should compile ++ * for any version of Linux. ++ */ ++#include ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)) ++#define YAFFS_COMPILE_BACKGROUND ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)) ++#define YAFFS_COMPILE_FREEZER ++#endif ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) ++#define YAFFS_COMPILE_EXPORTFS ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_USE_SETATTR_COPY ++#define YAFFS_USE_TRUNCATE_SETSIZE ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) ++#define YAFFS_HAS_EVICT_INODE ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++#define YAFFS_NEW_FOLLOW_LINK 1 ++#else ++#define YAFFS_NEW_FOLLOW_LINK 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_HAS_WRITE_SUPER ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++#include ++#endif ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++#include ++#endif ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++#include ++#include ++#endif ++#ifdef YAFFS_COMPILE_FREEZER ++#include ++#endif ++ ++#include ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++#include ++ ++#define UnlockPage(p) unlock_page(p) ++#define Page_Uptodate(page) test_bit(PG_uptodate, &(page)->flags) ++ ++/* FIXME: use sb->s_id instead ? */ ++#define yaffs_devname(sb, buf) bdevname(sb->s_bdev, buf) ++ ++#else ++ ++#include ++#define BDEVNAME_SIZE 0 ++#define yaffs_devname(sb, buf) kdevname(sb->s_dev) ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)) ++/* added NCB 26/5/2006 for 2.4.25-vrs2-tcl1 kernel */ ++#define __user ++#endif ++ ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define YPROC_ROOT (&proc_root) ++#else ++#define YPROC_ROOT NULL ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)) ++#define Y_INIT_TIMER(a) init_timer(a) ++#else ++#define Y_INIT_TIMER(a) init_timer_on_stack(a) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) ++#define YAFFS_USE_WRITE_BEGIN_END 1 ++#else ++#define YAFFS_USE_WRITE_BEGIN_END 0 ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)) ++#define YAFFS_SUPER_HAS_DIRTY ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) ++#define set_nlink(inode, count) do { (inode)->i_nlink = (count); } while(0) ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 28)) ++static uint32_t YCALCBLOCKS(uint64_t partition_size, uint32_t block_size) ++{ ++ uint64_t result = partition_size; ++ do_div(result, block_size); ++ return (uint32_t) result; ++} ++#else ++#define YCALCBLOCKS(s, b) ((s)/(b)) ++#endif ++ ++#include ++#include ++ ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_guts.h" ++#include "yaffs_attribs.h" ++ ++#include "yaffs_linux.h" ++ ++#include "yaffs_mtdif.h" ++#include "yaffs_packedtags2.h" ++#include "yaffs_getblockinfo.h" ++ ++unsigned int yaffs_trace_mask = ++ YAFFS_TRACE_BAD_BLOCKS | ++ YAFFS_TRACE_ALWAYS | ++ 0; ++ ++unsigned int yaffs_wr_attempts = YAFFS_WR_ATTEMPTS; ++unsigned int yaffs_auto_checkpoint = 1; ++unsigned int yaffs_gc_control = 1; ++unsigned int yaffs_bg_enable = 1; ++unsigned int yaffs_auto_select = 1; ++/* Module Parameters */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++module_param(yaffs_trace_mask, uint, 0644); ++module_param(yaffs_wr_attempts, uint, 0644); ++module_param(yaffs_auto_checkpoint, uint, 0644); ++module_param(yaffs_gc_control, uint, 0644); ++module_param(yaffs_bg_enable, uint, 0644); ++#else ++MODULE_PARM(yaffs_trace_mask, "i"); ++MODULE_PARM(yaffs_wr_attempts, "i"); ++MODULE_PARM(yaffs_auto_checkpoint, "i"); ++MODULE_PARM(yaffs_gc_control, "i"); ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++/* use iget and read_inode */ ++#define Y_IGET(sb, inum) iget((sb), (inum)) ++ ++#else ++/* Call local equivalent */ ++#define YAFFS_USE_OWN_IGET ++#define Y_IGET(sb, inum) yaffs_iget((sb), (inum)) ++ ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->i_private) ++#else ++#define yaffs_inode_to_obj_lv(iptr) ((iptr)->u.generic_ip) ++#endif ++ ++#define yaffs_inode_to_obj(iptr) \ ++ ((struct yaffs_obj *)(yaffs_inode_to_obj_lv(iptr))) ++#define yaffs_dentry_to_obj(dptr) yaffs_inode_to_obj((dptr)->d_inode) ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->s_fs_info) ++#else ++#define yaffs_super_to_dev(sb) ((struct yaffs_dev *)sb->u.generic_sbp) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++#define Y_CLEAR_INODE(i) clear_inode(i) ++#else ++#define Y_CLEAR_INODE(i) end_writeback(i) ++#endif ++ ++ ++#define update_dir_time(dir) do {\ ++ (dir)->i_ctime = (dir)->i_mtime = CURRENT_TIME; \ ++ } while (0) ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj); ++ ++ ++static void yaffs_gross_lock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locking %p", current); ++ mutex_lock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs locked %p", current); ++} ++ ++static void yaffs_gross_unlock(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_LOCK, "yaffs unlocking %p", current); ++ mutex_unlock(&(yaffs_dev_to_lc(dev)->gross_lock)); ++} ++ ++ ++static int yaffs_readpage_nolock(struct file *f, struct page *pg) ++{ ++ /* Lifted from jffs2 */ ++ ++ struct yaffs_obj *obj; ++ unsigned char *pg_buf; ++ int ret; ++ loff_t pos = ((loff_t) pg->index) << PAGE_CACHE_SHIFT; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readpage_nolock at %lld, size %08x", ++ (long long)pos, ++ (unsigned)PAGE_CACHE_SIZE); ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ BUG_ON(!PageLocked(pg)); ++#else ++ if (!PageLocked(pg)) ++ PAGE_BUG(pg); ++#endif ++ ++ pg_buf = kmap(pg); ++ /* FIXME: Can kmap fail? */ ++ ++ yaffs_gross_lock(dev); ++ ++ ret = yaffs_file_rd(obj, pg_buf, pos, PAGE_CACHE_SIZE); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (ret >= 0) ++ ret = 0; ++ ++ if (ret) { ++ ClearPageUptodate(pg); ++ SetPageError(pg); ++ } else { ++ SetPageUptodate(pg); ++ ClearPageError(pg); ++ } ++ ++ flush_dcache_page(pg); ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage_nolock done"); ++ return ret; ++} ++ ++static int yaffs_readpage_unlock(struct file *f, struct page *pg) ++{ ++ int ret = yaffs_readpage_nolock(f, pg); ++ UnlockPage(pg); ++ return ret; ++} ++ ++static int yaffs_readpage(struct file *f, struct page *pg) ++{ ++ int ret; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage"); ++ ret = yaffs_readpage_unlock(f, pg); ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_readpage done"); ++ return ret; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) ++#define YCRED_FSUID() from_kuid(&init_user_ns, current_fsuid()) ++#define YCRED_FSGID() from_kgid(&init_user_ns, current_fsgid()) ++#else ++#define YCRED_FSUID() YCRED(current)->fsuid ++#define YCRED_FSGID() YCRED(current)->fsgid ++ ++static inline uid_t i_uid_read(const struct inode *inode) ++{ ++ return inode->i_uid; ++} ++ ++static inline gid_t i_gid_read(const struct inode *inode) ++{ ++ return inode->i_gid; ++} ++ ++static inline void i_uid_write(struct inode *inode, uid_t uid) ++{ ++ inode->i_uid = uid; ++} ++ ++static inline void i_gid_write(struct inode *inode, gid_t gid) ++{ ++ inode->i_gid = gid; ++} ++#endif ++ ++static void yaffs_set_super_dirty_val(struct yaffs_dev *dev, int val) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc) ++ lc->dirty = val; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb) ++ sb->s_dirt = val; ++ } ++#endif ++ ++} ++ ++static void yaffs_set_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 1); ++} ++ ++static void yaffs_clear_super_dirty(struct yaffs_dev *dev) ++{ ++ yaffs_set_super_dirty_val(dev, 0); ++} ++ ++static int yaffs_check_super_dirty(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *lc = yaffs_dev_to_lc(dev); ++ ++ if (lc && lc->dirty) ++ return 1; ++ ++# ifdef YAFFS_SUPER_HAS_DIRTY ++ { ++ struct super_block *sb = lc->super; ++ ++ if (sb && sb->s_dirt) ++ return 1; ++ } ++#endif ++ return 0; ++ ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_writepage(struct page *page, struct writeback_control *wbc) ++#else ++static int yaffs_writepage(struct page *page) ++#endif ++{ ++ struct yaffs_dev *dev; ++ struct address_space *mapping = page->mapping; ++ struct inode *inode; ++ unsigned long end_index; ++ char *buffer; ++ struct yaffs_obj *obj; ++ int n_written = 0; ++ unsigned n_bytes; ++ loff_t i_size; ++ ++ if (!mapping) ++ BUG(); ++ inode = mapping->host; ++ if (!inode) ++ BUG(); ++ i_size = i_size_read(inode); ++ ++ end_index = i_size >> PAGE_CACHE_SHIFT; ++ ++ if (page->index < end_index) ++ n_bytes = PAGE_CACHE_SIZE; ++ else { ++ n_bytes = i_size & (PAGE_CACHE_SIZE - 1); ++ ++ if (page->index > end_index || !n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, inode size = %lld!!", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, ++ inode->i_size); ++ yaffs_trace(YAFFS_TRACE_OS, ++ " -> don't care!!"); ++ ++ zero_user_segment(page, 0, PAGE_CACHE_SIZE); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ return 0; ++ } ++ } ++ ++ if (n_bytes != PAGE_CACHE_SIZE) ++ zero_user_segment(page, n_bytes, PAGE_CACHE_SIZE); ++ ++ get_page(page); ++ ++ buffer = kmap(page); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_writepage at %lld, size %08x", ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag0: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ n_written = yaffs_wr_file(obj, buffer, ++ ((loff_t)page->index) << PAGE_CACHE_SHIFT, n_bytes, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "writepag1: obj = %lld, ino = %lld", ++ obj->variant.file_variant.file_size, inode->i_size); ++ ++ yaffs_gross_unlock(dev); ++ ++ kunmap(page); ++ set_page_writeback(page); ++ unlock_page(page); ++ end_page_writeback(page); ++ put_page(page); ++ ++ return (n_written == n_bytes) ? 0 : -ENOSPC; ++} ++ ++/* Space holding and freeing is done to ensure we have space available for write_begin/end */ ++/* For now we just assume few parallel writes and check against a small number. */ ++/* Todo: need to do this with a counter to handle parallel reads better */ ++ ++static ssize_t yaffs_hold_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ int n_free_chunks; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ n_free_chunks = yaffs_get_n_free_chunks(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ return (n_free_chunks > 20) ? 1 : 0; ++} ++ ++static void yaffs_release_space(struct file *f) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_gross_unlock(dev); ++} ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_begin(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ ++ struct page *pg = NULL; ++ pgoff_t index = pos >> PAGE_CACHE_SHIFT; ++ ++ int ret = 0; ++ int space_held = 0; ++ ++ /* Get a page */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) ++ pg = grab_cache_page_write_begin(mapping, index, flags); ++#else ++ pg = __grab_cache_page(mapping, index); ++#endif ++ ++ *pagep = pg; ++ if (!pg) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "start yaffs_write_begin index %d(%x) uptodate %d", ++ (int)index, (int)index, Page_Uptodate(pg) ? 1 : 0); ++ ++ /* Get fs space */ ++ space_held = yaffs_hold_space(filp); ++ ++ if (!space_held) { ++ ret = -ENOSPC; ++ goto out; ++ } ++ ++ /* Update page if required */ ++ ++ if (!Page_Uptodate(pg)) ++ ret = yaffs_readpage_nolock(filp, pg); ++ ++ if (ret) ++ goto out; ++ ++ /* Happy path return */ ++ yaffs_trace(YAFFS_TRACE_OS, "end yaffs_write_begin - ok"); ++ ++ return 0; ++ ++out: ++ yaffs_trace(YAFFS_TRACE_OS, ++ "end yaffs_write_begin fail returning %d", ret); ++ if (space_held) ++ yaffs_release_space(filp); ++ if (pg) { ++ unlock_page(pg); ++ page_cache_release(pg); ++ } ++ return ret; ++} ++ ++#else ++ ++static int yaffs_prepare_write(struct file *f, struct page *pg, ++ unsigned offset, unsigned to) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_prepair_write"); ++ ++ if (!Page_Uptodate(pg)) ++ return yaffs_readpage_nolock(f, pg); ++ return 0; ++} ++#endif ++ ++ ++static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, ++ loff_t * pos) ++{ ++ struct yaffs_obj *obj; ++ int n_written; ++ loff_t ipos; ++ struct inode *inode; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: hey obj is null!"); ++ return -EINVAL; ++ } ++ ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ inode = f->f_dentry->d_inode; ++ ++ if (!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND) ++ ipos = inode->i_size; ++ else ++ ipos = *pos; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write about to write writing %u(%x) bytes to object %d at %lld", ++ (unsigned)n, (unsigned)n, obj->obj_id, ipos); ++ ++ n_written = yaffs_wr_file(obj, buf, ipos, n, 0); ++ ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write: %d(%x) bytes written", ++ (unsigned)n, (unsigned)n); ++ ++ if (n_written > 0) { ++ ipos += n_written; ++ *pos = ipos; ++ if (ipos > inode->i_size) { ++ inode->i_size = ipos; ++ inode->i_blocks = (ipos + 511) >> 9; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_write size updated to %lld bytes, %d blocks", ++ ipos, (int)(inode->i_blocks)); ++ } ++ ++ } ++ yaffs_gross_unlock(dev); ++ return (n_written == 0) && (n > 0) ? -ENOSPC : n_written; ++} ++ ++ ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++static int yaffs_write_end(struct file *filp, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *pg, void *fsdadata) ++{ ++ int ret = 0; ++ void *addr, *kva; ++ uint32_t offset_into_page = pos & (PAGE_CACHE_SIZE - 1); ++ ++ kva = kmap(pg); ++ addr = kva + offset_into_page; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end addr %p pos %lld n_bytes %d", ++ addr, pos, copied); ++ ++ ret = yaffs_file_write(filp, addr, copied, &pos); ++ ++ if (ret != copied) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_write_end not same size ret %d copied %d", ++ ret, copied); ++ SetPageError(pg); ++ } ++ ++ kunmap(pg); ++ ++ yaffs_release_space(filp); ++ unlock_page(pg); ++ page_cache_release(pg); ++ return ret; ++} ++#else ++ ++static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, ++ unsigned to) ++{ ++ void *addr, *kva; ++ ++ loff_t pos = (((loff_t) pg->index) << PAGE_CACHE_SHIFT) + offset; ++ int n_bytes = to - offset; ++ int n_written; ++ ++ kva = kmap(pg); ++ addr = kva + offset; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write addr %p pos %lld n_bytes %d", ++ addr, pos, n_bytes); ++ ++ n_written = yaffs_file_write(f, addr, n_bytes, &pos); ++ ++ if (n_written != n_bytes) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write not same size n_written %d n_bytes %d", ++ n_written, n_bytes); ++ SetPageError(pg); ++ } ++ kunmap(pg); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_commit_write returning %d", ++ n_written == n_bytes ? 0 : n_written); ++ ++ return n_written == n_bytes ? 0 : n_written; ++} ++#endif ++ ++static struct address_space_operations yaffs_file_address_operations = { ++ .readpage = yaffs_readpage, ++ .writepage = yaffs_writepage, ++#if (YAFFS_USE_WRITE_BEGIN_END > 0) ++ .write_begin = yaffs_write_begin, ++ .write_end = yaffs_write_end, ++#else ++ .prepare_write = yaffs_prepare_write, ++ .commit_write = yaffs_commit_write, ++#endif ++}; ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_file_flush(struct file *file, fl_owner_t id) ++#else ++static int yaffs_file_flush(struct file *file) ++#endif ++{ ++ struct yaffs_obj *obj = yaffs_dentry_to_obj(file->f_dentry); ++ ++ struct yaffs_dev *dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_file_flush object %d (%s)", ++ obj->obj_id, ++ obj->dirty ? "dirty" : "clean"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_file(obj, 1, 0); ++ ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static int yaffs_sync_object(struct file *file, loff_t start, loff_t end, int datasync) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++static int yaffs_sync_object(struct file *file, int datasync) ++#else ++static int yaffs_sync_object(struct file *file, struct dentry *dentry, ++ int datasync) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 34)) ++ struct dentry *dentry = file->f_path.dentry; ++#endif ++ ++ obj = yaffs_dentry_to_obj(dentry); ++ ++ dev = obj->my_dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_object"); ++ yaffs_gross_lock(dev); ++ yaffs_flush_file(obj, 1, datasync); ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .splice_read = generic_file_splice_read, ++ .splice_write = generic_file_splice_write, ++ .llseek = generic_file_llseek, ++}; ++ ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)) ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = do_sync_read, ++ .write = do_sync_write, ++ .aio_read = generic_file_aio_read, ++ .aio_write = generic_file_aio_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++ .sendfile = generic_file_sendfile, ++}; ++ ++#else ++ ++static const struct file_operations yaffs_file_operations = { ++ .read = generic_file_read, ++ .write = generic_file_write, ++ .mmap = generic_file_mmap, ++ .flush = yaffs_file_flush, ++ .fsync = yaffs_sync_object, ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ .sendfile = generic_file_sendfile, ++#endif ++}; ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) ++static void zero_user_segment(struct page *page, unsigned start, unsigned end) ++{ ++ void *kaddr = kmap_atomic(page, KM_USER0); ++ memset(kaddr + start, 0, end - start); ++ kunmap_atomic(kaddr, KM_USER0); ++ flush_dcache_page(page); ++} ++#endif ++ ++ ++static int yaffs_vfs_setsize(struct inode *inode, loff_t newsize) ++{ ++#ifdef YAFFS_USE_TRUNCATE_SETSIZE ++ truncate_setsize(inode, newsize); ++ return 0; ++#else ++ truncate_inode_pages(&inode->i_data, newsize); ++ return 0; ++#endif ++ ++} ++ ++ ++static int yaffs_vfs_setattr(struct inode *inode, struct iattr *attr) ++{ ++#ifdef YAFFS_USE_SETATTR_COPY ++ setattr_copy(inode, attr); ++ return 0; ++#else ++ return inode_setattr(inode, attr); ++#endif ++ ++} ++ ++static int yaffs_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_setattr of object %d", ++ yaffs_inode_to_obj(inode)->obj_id); ++#if 0 ++ /* Fail if a requested resize >= 2GB */ ++ if (attr->ia_valid & ATTR_SIZE && (attr->ia_size >> 31)) ++ error = -EINVAL; ++#endif ++ ++ if (error == 0) ++ error = inode_change_ok(inode, attr); ++ if (error == 0) { ++ int result; ++ if (!error) { ++ error = yaffs_vfs_setattr(inode, attr); ++ yaffs_trace(YAFFS_TRACE_OS, "inode_setattr called"); ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_vfs_setsize(inode, attr->ia_size); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ } ++ } ++ dev = yaffs_inode_to_obj(inode)->my_dev; ++ if (attr->ia_valid & ATTR_SIZE) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "resize to %d(%x)", ++ (int)(attr->ia_size), ++ (int)(attr->ia_size)); ++ } ++ yaffs_gross_lock(dev); ++ result = yaffs_set_attribs(yaffs_inode_to_obj(inode), attr); ++ if (result == YAFFS_OK) { ++ error = 0; ++ } else { ++ error = -EPERM; ++ } ++ yaffs_gross_unlock(dev); ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_set_xattrib(obj, name, value, size, flags); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_setxattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_getxattr(struct dentry * dentry, const char *name, ++ void *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_getxattr \"%s\" from object %d", ++ name, obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_get_xattrib(obj, name, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_getxattr done returning %d", error); ++ ++ return error; ++} ++ ++static int yaffs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ int result; ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ result = yaffs_remove_xattrib(obj, name); ++ if (result == YAFFS_OK) ++ error = 0; ++ else if (result < 0) ++ error = result; ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_removexattr done returning %d", error); ++ ++ return error; ++} ++ ++static ssize_t yaffs_listxattr(struct dentry * dentry, char *buff, size_t size) ++{ ++ struct inode *inode = dentry->d_inode; ++ int error = 0; ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr of object %d", obj->obj_id); ++ ++ if (error == 0) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ error = yaffs_list_xattrib(obj, buff, size); ++ yaffs_gross_unlock(dev); ++ ++ } ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_listxattr done returning %d", error); ++ ++ return error; ++} ++ ++ ++static const struct inode_operations yaffs_file_inode_operations = { ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++ ++static int yaffs_readlink(struct dentry *dentry, char __user * buffer, ++ int buflen) ++{ ++ unsigned char *alias; ++ int ret; ++ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) ++ return -ENOMEM; ++ ++ ret = vfs_readlink(dentry, buffer, buflen, alias); ++ kfree(alias); ++ return ret; ++} ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++static void *yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ void *ret; ++#else ++static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int ret ++#endif ++ unsigned char *alias; ++ int ret_int = 0; ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ alias = yaffs_get_symlink_alias(yaffs_dentry_to_obj(dentry)); ++ yaffs_gross_unlock(dev); ++ ++ if (!alias) { ++ ret_int = -ENOMEM; ++ goto out; ++ } ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ nd_set_link(nd, alias); ++ ret = alias; ++out: ++ if (ret_int) ++ ret = ERR_PTR(ret_int); ++ return ret; ++#else ++ ret = vfs_follow_link(nd, alias); ++ kfree(alias); ++out: ++ if (ret_int) ++ ret = ret_int; ++ return ret; ++#endif ++} ++ ++ ++#ifdef YAFFS_HAS_PUT_INODE ++ ++/* For now put inode is just for debugging ++ * Put inode is called when the inode **structure** is put. ++ */ ++static void yaffs_put_inode(struct inode *inode) ++{ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_put_inode: ino %d, count %d"), ++ (int)inode->i_ino, atomic_read(&inode->i_count); ++ ++} ++#endif ++ ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++void yaffs_put_link(struct dentry *dentry, struct nameidata *nd, void *alias) ++{ ++ kfree(alias); ++} ++#endif ++ ++static const struct inode_operations yaffs_symlink_inode_operations = { ++ .readlink = yaffs_readlink, ++ .follow_link = yaffs_follow_link, ++#if (YAFFS_NEW_FOLLOW_LINK == 1) ++ .put_link = yaffs_put_link, ++#endif ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++#ifdef YAFFS_USE_OWN_IGET ++ ++static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino) ++{ ++ struct inode *inode; ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_iget for %lu", ino); ++ ++ inode = iget_locked(sb, ino); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ yaffs_gross_unlock(dev); ++ ++ unlock_new_inode(inode); ++ return inode; ++} ++ ++#else ++ ++static void yaffs_read_inode(struct inode *inode) ++{ ++ /* NB This is called as a side effect of other functions, but ++ * we had to release the lock to prevent deadlocks, so ++ * need to lock again. ++ */ ++ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev = yaffs_super_to_dev(inode->i_sb); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_inode for %d", (int)inode->i_ino); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ obj = yaffs_find_by_number(dev, inode->i_ino); ++ ++ yaffs_fill_inode_from_obj(inode, obj); ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++} ++ ++#endif ++ ++ ++ ++struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev, ++ struct yaffs_obj *obj) ++{ ++ struct inode *inode; ++ ++ if (!sb) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL super_block!!"); ++ return NULL; ++ ++ } ++ ++ if (!obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for NULL object!!"); ++ return NULL; ++ ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_get_inode for object %d", obj->obj_id); ++ ++ inode = Y_IGET(sb, obj->obj_id); ++ if (IS_ERR(inode)) ++ return NULL; ++ ++ /* NB Side effect: iget calls back to yaffs_read_inode(). */ ++ /* iget also increments the inode's i_count */ ++ /* NB You can't be holding gross_lock or deadlock will happen! */ ++ ++ return inode; ++} ++ ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) ++#define YCRED(x) x ++#else ++#define YCRED(x) (x->cred) ++#endif ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t rdev) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t rdev) ++#else ++static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ int rdev) ++#endif ++{ ++ struct inode *inode; ++ ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_dev *dev; ++ ++ struct yaffs_obj *parent = yaffs_inode_to_obj(dir); ++ ++ int error = -ENOSPC; ++ uid_t uid = YCRED_FSUID(); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); ++ ++ if ((dir->i_mode & S_ISGID) && S_ISDIR(mode)) ++ mode |= S_ISGID; ++ ++ if (parent) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: parent object %d type %d", ++ parent->obj_id, parent->variant_type); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: could not get parent object"); ++ return -EPERM; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod: making oject for %s, mode %x dev %x", ++ dentry->d_name.name, mode, rdev); ++ ++ dev = parent->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ switch (mode & S_IFMT) { ++ default: ++ /* Special (socket, fifo, device...) */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making special"); ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, old_encode_dev(rdev)); ++#else ++ obj = ++ yaffs_create_special(parent, dentry->d_name.name, mode, uid, ++ gid, rdev); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making file"); ++ obj = yaffs_create_file(parent, dentry->d_name.name, mode, uid, ++ gid); ++ break; ++ case S_IFDIR: /* directory */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making directory"); ++ obj = yaffs_create_dir(parent, dentry->d_name.name, mode, ++ uid, gid); ++ break; ++ case S_IFLNK: /* symlink */ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod: making symlink"); ++ obj = NULL; /* Do we ever get here? */ ++ break; ++ } ++ ++ /* Can not call yaffs_get_inode() with gross lock held */ ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_mknod created object %d count = %d", ++ obj->obj_id, atomic_read(&inode->i_count)); ++ error = 0; ++ yaffs_fill_inode_from_obj(dir, parent); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mknod failed making object"); ++ error = -ENOMEM; ++ } ++ ++ return error; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++#else ++static int yaffs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ int ret_val; ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_mkdir"); ++ ret_val = yaffs_mknod(dir, dentry, mode | S_IFDIR, 0); ++ return ret_val; ++} ++ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool dummy) ++#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ struct nameidata *n) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *n) ++#else ++static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode) ++#endif ++{ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_create"); ++ return yaffs_mknod(dir, dentry, mode | S_IFREG, 0); ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ unsigned int dummy) ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *n) ++#else ++static struct dentry *yaffs_lookup(struct inode *dir, struct dentry *dentry) ++#endif ++{ ++ struct yaffs_obj *obj; ++ struct inode *inode = NULL; /* NCB 2.5/2.6 needs NULL here */ ++ ++ struct yaffs_dev *dev = yaffs_inode_to_obj(dir)->my_dev; ++ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_lock(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup for %d:%s", ++ yaffs_inode_to_obj(dir)->obj_id, dentry->d_name.name); ++ ++ obj = yaffs_find_by_name(yaffs_inode_to_obj(dir), dentry->d_name.name); ++ ++ obj = yaffs_get_equivalent_obj(obj); /* in case it was a hardlink */ ++ ++ /* Can't hold gross lock when calling yaffs_get_inode() */ ++ if (current != yaffs_dev_to_lc(dev)->readdir_process) ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_lookup found %d", obj->obj_id); ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_lookup not found"); ++ ++ } ++ ++/* added NCB for 2.5/6 compatability - forces add even if inode is ++ * NULL which creates dentry hash */ ++ d_add(dentry, inode); ++ ++ return NULL; ++} ++ ++/* ++ * Create a link... ++ */ ++static int yaffs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ struct inode *inode = old_dentry->d_inode; ++ struct yaffs_obj *obj = NULL; ++ struct yaffs_obj *link = NULL; ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_link"); ++ ++ obj = yaffs_inode_to_obj(inode); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ if (!S_ISDIR(inode->i_mode)) /* Don't link directories */ ++ link = ++ yaffs_link_obj(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ obj); ++ ++ if (link) { ++ set_nlink(old_dentry->d_inode, yaffs_get_obj_link_count(obj)); ++ d_instantiate(dentry, old_dentry->d_inode); ++ atomic_inc(&old_dentry->d_inode->i_count); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_link link count %d i_count %d", ++ old_dentry->d_inode->i_nlink, ++ atomic_read(&old_dentry->d_inode->i_count)); ++ } ++ ++ yaffs_gross_unlock(dev); ++ ++ if (link) { ++ update_dir_time(dir); ++ return 0; ++ } ++ ++ return -EPERM; ++} ++ ++static int yaffs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ uid_t uid = YCRED_FSUID(); ++ gid_t gid = ++ (dir->i_mode & S_ISGID) ? i_gid_read(dir) : YCRED_FSGID(); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_symlink"); ++ ++ if (strnlen(dentry->d_name.name, YAFFS_MAX_NAME_LENGTH + 1) > ++ YAFFS_MAX_NAME_LENGTH) ++ return -ENAMETOOLONG; ++ ++ if (strnlen(symname, YAFFS_MAX_ALIAS_LENGTH + 1) > ++ YAFFS_MAX_ALIAS_LENGTH) ++ return -ENAMETOOLONG; ++ ++ dev = yaffs_inode_to_obj(dir)->my_dev; ++ yaffs_gross_lock(dev); ++ obj = yaffs_create_symlink(yaffs_inode_to_obj(dir), dentry->d_name.name, ++ S_IFLNK | S_IRWXUGO, uid, gid, symname); ++ yaffs_gross_unlock(dev); ++ ++ if (obj) { ++ struct inode *inode; ++ ++ inode = yaffs_get_inode(dir->i_sb, obj->yst_mode, 0, obj); ++ d_instantiate(dentry, inode); ++ update_dir_time(dir); ++ yaffs_trace(YAFFS_TRACE_OS, "symlink created OK"); ++ return 0; ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, "symlink not created"); ++ } ++ ++ return -ENOMEM; ++} ++ ++/* ++ * The VFS layer already does all the dentry stuff for rename. ++ * ++ * NB: POSIX says you can rename an object over an old object of the same name ++ */ ++static int yaffs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct yaffs_dev *dev; ++ int ret_val = YAFFS_FAIL; ++ struct yaffs_obj *target; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_rename"); ++ dev = yaffs_inode_to_obj(old_dir)->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ /* Check if the target is an existing directory that is not empty. */ ++ target = yaffs_find_by_name(yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ ++ if (target && target->variant_type == YAFFS_OBJECT_TYPE_DIRECTORY && ++ !list_empty(&target->variant.dir_variant.children)) { ++ ++ yaffs_trace(YAFFS_TRACE_OS, "target is non-empty dir"); ++ ++ ret_val = YAFFS_FAIL; ++ } else { ++ /* Now does unlinking internally using shadowing mechanism */ ++ yaffs_trace(YAFFS_TRACE_OS, "calling yaffs_rename_obj"); ++ ++ ret_val = yaffs_rename_obj(yaffs_inode_to_obj(old_dir), ++ old_dentry->d_name.name, ++ yaffs_inode_to_obj(new_dir), ++ new_dentry->d_name.name); ++ } ++ yaffs_gross_unlock(dev); ++ ++ if (ret_val == YAFFS_OK) { ++ if (target) ++ inode_dec_link_count(new_dentry->d_inode); ++ ++ update_dir_time(old_dir); ++ if (old_dir != new_dir) ++ update_dir_time(new_dir); ++ return 0; ++ } else { ++ return -ENOTEMPTY; ++ } ++} ++ ++ ++ ++ ++static int yaffs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int ret_val; ++ ++ struct yaffs_dev *dev; ++ struct yaffs_obj *obj; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_unlink %d:%s", ++ (int)(dir->i_ino), dentry->d_name.name); ++ obj = yaffs_inode_to_obj(dir); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ ret_val = yaffs_unlinker(obj, dentry->d_name.name); ++ ++ if (ret_val == YAFFS_OK) { ++ inode_dec_link_count(dentry->d_inode); ++ dir->i_version++; ++ yaffs_gross_unlock(dev); ++ update_dir_time(dir); ++ return 0; ++ } ++ yaffs_gross_unlock(dev); ++ return -ENOTEMPTY; ++} ++ ++ ++ ++static const struct inode_operations yaffs_dir_inode_operations = { ++ .create = yaffs_create, ++ .lookup = yaffs_lookup, ++ .link = yaffs_link, ++ .unlink = yaffs_unlink, ++ .symlink = yaffs_symlink, ++ .mkdir = yaffs_mkdir, ++ .rmdir = yaffs_unlink, ++ .mknod = yaffs_mknod, ++ .rename = yaffs_rename, ++ .setattr = yaffs_setattr, ++ .setxattr = yaffs_setxattr, ++ .getxattr = yaffs_getxattr, ++ .listxattr = yaffs_listxattr, ++ .removexattr = yaffs_removexattr, ++}; ++ ++/*-----------------------------------------------------------------*/ ++/* Directory search context allows us to unlock access to yaffs during ++ * filldir without causing problems with the directory being modified. ++ * This is similar to the tried and tested mechanism used in yaffs direct. ++ * ++ * A search context iterates along a doubly linked list of siblings in the ++ * directory. If the iterating object is deleted then this would corrupt ++ * the list iteration, likely causing a crash. The search context avoids ++ * this by using the remove_obj_fn to move the search context to the ++ * next object before the object is deleted. ++ * ++ * Many readdirs (and thus seach conexts) may be alive simulateously so ++ * each struct yaffs_dev has a list of these. ++ * ++ * A seach context lives for the duration of a readdir. ++ * ++ * All these functions must be called while yaffs is locked. ++ */ ++ ++struct yaffs_search_context { ++ struct yaffs_dev *dev; ++ struct yaffs_obj *dir_obj; ++ struct yaffs_obj *next_return; ++ struct list_head others; ++}; ++ ++/* ++ * yaffs_new_search() creates a new search context, initialises it and ++ * adds it to the device's search context list. ++ * ++ * Called at start of readdir. ++ */ ++static struct yaffs_search_context *yaffs_new_search(struct yaffs_obj *dir) ++{ ++ struct yaffs_dev *dev = dir->my_dev; ++ struct yaffs_search_context *sc = ++ kmalloc(sizeof(struct yaffs_search_context), GFP_NOFS); ++ if (sc) { ++ sc->dir_obj = dir; ++ sc->dev = dev; ++ if (list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else ++ sc->next_return = ++ list_entry(dir->variant.dir_variant.children.next, ++ struct yaffs_obj, siblings); ++ INIT_LIST_HEAD(&sc->others); ++ list_add(&sc->others, &(yaffs_dev_to_lc(dev)->search_contexts)); ++ } ++ return sc; ++} ++ ++/* ++ * yaffs_search_end() disposes of a search context and cleans up. ++ */ ++static void yaffs_search_end(struct yaffs_search_context *sc) ++{ ++ if (sc) { ++ list_del(&sc->others); ++ kfree(sc); ++ } ++} ++ ++/* ++ * yaffs_search_advance() moves a search context to the next object. ++ * Called when the search iterates or when an object removal causes ++ * the search context to be moved to the next object. ++ */ ++static void yaffs_search_advance(struct yaffs_search_context *sc) ++{ ++ if (!sc) ++ return; ++ ++ if (sc->next_return == NULL || ++ list_empty(&sc->dir_obj->variant.dir_variant.children)) ++ sc->next_return = NULL; ++ else { ++ struct list_head *next = sc->next_return->siblings.next; ++ ++ if (next == &sc->dir_obj->variant.dir_variant.children) ++ sc->next_return = NULL; /* end of list */ ++ else ++ sc->next_return = ++ list_entry(next, struct yaffs_obj, siblings); ++ } ++} ++ ++/* ++ * yaffs_remove_obj_callback() is called when an object is unlinked. ++ * We check open search contexts and advance any which are currently ++ * on the object being iterated. ++ */ ++static void yaffs_remove_obj_callback(struct yaffs_obj *obj) ++{ ++ ++ struct list_head *i; ++ struct yaffs_search_context *sc; ++ struct list_head *search_contexts = ++ &(yaffs_dev_to_lc(obj->my_dev)->search_contexts); ++ ++ /* Iterate through the directory search contexts. ++ * If any are currently on the object being removed, then advance ++ * the search context to the next object to prevent a hanging pointer. ++ */ ++ list_for_each(i, search_contexts) { ++ sc = list_entry(i, struct yaffs_search_context, others); ++ if (sc->next_return == obj) ++ yaffs_search_advance(sc); ++ } ++ ++} ++ ++ ++/*-----------------------------------------------------------------*/ ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++static int yaffs_readdir(struct file *file, struct dir_context *ctx) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ struct inode *inode = file->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(file->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ offset = ctx->pos; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: starting at %d", (int)offset); ++ ++ if (offset == 0) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry . ino %d", ++ (int)inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (!dir_emit_dot(file, ctx)) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ ctx->pos++; ++ } ++ if (offset == 1) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry .. ino %d", ++ (int)file->f_dentry->d_parent->d_inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (!dir_emit_dotdot(file, ctx)) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ ctx->pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ if (file->f_version != inode->i_version) { ++ offset = 2; ++ ctx->pos = offset; ++ file->f_version = inode->i_version; ++ } ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= offset) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (!dir_emit(ctx, name, strlen(name), ++ this_inode, this_type) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ offset++; ++ ctx->pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++#else ++static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ struct yaffs_search_context *sc; ++ struct inode *inode = f->f_dentry->d_inode; ++ unsigned long offset, curoffs; ++ struct yaffs_obj *l; ++ int ret_val = 0; ++ ++ char name[YAFFS_MAX_NAME_LENGTH + 1]; ++ ++ obj = yaffs_dentry_to_obj(f->f_dentry); ++ dev = obj->my_dev; ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_dev_to_lc(dev)->readdir_process = current; ++ ++ offset = f->f_pos; ++ ++ sc = yaffs_new_search(obj); ++ if (!sc) { ++ ret_val = -ENOMEM; ++ goto out; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: starting at %d", (int)offset); ++ ++ if (offset == 0) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry . ino %d", ++ (int)inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, ".", 1, offset, inode->i_ino, DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ if (offset == 1) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: entry .. ino %d", ++ (int)f->f_dentry->d_parent->d_inode->i_ino); ++ yaffs_gross_unlock(dev); ++ if (filldir(dirent, "..", 2, offset, ++ f->f_dentry->d_parent->d_inode->i_ino, ++ DT_DIR) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ yaffs_gross_lock(dev); ++ offset++; ++ f->f_pos++; ++ } ++ ++ curoffs = 1; ++ ++ /* If the directory has changed since the open or last call to ++ readdir, rewind to after the 2 canned entries. */ ++ if (f->f_version != inode->i_version) { ++ offset = 2; ++ f->f_pos = offset; ++ f->f_version = inode->i_version; ++ } ++ ++ while (sc->next_return) { ++ curoffs++; ++ l = sc->next_return; ++ if (curoffs >= offset) { ++ int this_inode = yaffs_get_obj_inode(l); ++ int this_type = yaffs_get_obj_type(l); ++ ++ yaffs_get_obj_name(l, name, YAFFS_MAX_NAME_LENGTH + 1); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_readdir: %s inode %d", ++ name, yaffs_get_obj_inode(l)); ++ ++ yaffs_gross_unlock(dev); ++ ++ if (filldir(dirent, ++ name, ++ strlen(name), ++ offset, this_inode, this_type) < 0) { ++ yaffs_gross_lock(dev); ++ goto out; ++ } ++ ++ yaffs_gross_lock(dev); ++ ++ offset++; ++ f->f_pos++; ++ } ++ yaffs_search_advance(sc); ++ } ++ ++out: ++ yaffs_search_end(sc); ++ yaffs_dev_to_lc(dev)->readdir_process = NULL; ++ yaffs_gross_unlock(dev); ++ ++ return ret_val; ++} ++#endif ++ ++static const struct file_operations yaffs_dir_operations = { ++ .read = generic_read_dir, ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) ++ .iterate = yaffs_readdir, ++#else ++ .readdir = yaffs_readdir, ++#endif ++ .fsync = yaffs_sync_object, ++ .llseek = generic_file_llseek, ++}; ++ ++static void yaffs_fill_inode_from_obj(struct inode *inode, ++ struct yaffs_obj *obj) ++{ ++ if (inode && obj) { ++ ++ /* Check mode against the variant type and attempt to repair if broken. */ ++ u32 mode = obj->yst_mode; ++ switch (obj->variant_type) { ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (!S_ISREG(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFREG; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ if (!S_ISLNK(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFLNK; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ if (!S_ISDIR(mode)) { ++ obj->yst_mode &= ~S_IFMT; ++ obj->yst_mode |= S_IFDIR; ++ } ++ ++ break; ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ default: ++ /* TODO? */ ++ break; ++ } ++ ++ inode->i_flags |= S_NOATIME; ++ ++ inode->i_ino = obj->obj_id; ++ inode->i_mode = obj->yst_mode; ++ i_uid_write(inode, obj->yst_uid); ++ i_gid_write(inode, obj->yst_gid); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++ inode->i_blksize = inode->i_sb->s_blocksize; ++#endif ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ ++ inode->i_rdev = old_decode_dev(obj->yst_rdev); ++ inode->i_atime.tv_sec = (time_t) (obj->yst_atime); ++ inode->i_atime.tv_nsec = 0; ++ inode->i_mtime.tv_sec = (time_t) obj->yst_mtime; ++ inode->i_mtime.tv_nsec = 0; ++ inode->i_ctime.tv_sec = (time_t) obj->yst_ctime; ++ inode->i_ctime.tv_nsec = 0; ++#else ++ inode->i_rdev = obj->yst_rdev; ++ inode->i_atime = obj->yst_atime; ++ inode->i_mtime = obj->yst_mtime; ++ inode->i_ctime = obj->yst_ctime; ++#endif ++ inode->i_size = yaffs_get_obj_length(obj); ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ ++ set_nlink(inode, yaffs_get_obj_link_count(obj)); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode mode %x uid %d gid %d size %lld count %d", ++ inode->i_mode, i_uid_read(inode), i_gid_read(inode), ++ inode->i_size, atomic_read(&inode->i_count)); ++ ++ switch (obj->yst_mode & S_IFMT) { ++ default: /* fifo, device or socket */ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ init_special_inode(inode, obj->yst_mode, ++ old_decode_dev(obj->yst_rdev)); ++#else ++ init_special_inode(inode, obj->yst_mode, ++ (dev_t) (obj->yst_rdev)); ++#endif ++ break; ++ case S_IFREG: /* file */ ++ inode->i_op = &yaffs_file_inode_operations; ++ inode->i_fop = &yaffs_file_operations; ++ inode->i_mapping->a_ops = ++ &yaffs_file_address_operations; ++ break; ++ case S_IFDIR: /* directory */ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ break; ++ case S_IFLNK: /* symlink */ ++ inode->i_op = &yaffs_symlink_inode_operations; ++ break; ++ } ++ ++ yaffs_inode_to_obj_lv(inode) = obj; ++ ++ obj->my_inode = inode; ++ ++ } else { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_fill_inode invalid parameters"); ++ } ++ ++} ++ ++ ++ ++/* ++ * yaffs background thread functions . ++ * yaffs_bg_thread_fn() the thread function ++ * yaffs_bg_start() launches the background thread. ++ * yaffs_bg_stop() cleans up the background thread. ++ * ++ * NB: ++ * The thread should only run after the yaffs is initialised ++ * The thread should be stopped before yaffs is unmounted. ++ * The thread should not do any writing while the fs is in read only. ++ */ ++ ++static unsigned yaffs_bg_gc_urgency(struct yaffs_dev *dev) ++{ ++ unsigned erased_chunks = ++ dev->n_erased_blocks * dev->param.chunks_per_block; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned scattered = 0; /* Free chunks not in an erased block */ ++ ++ if (erased_chunks < dev->n_free_chunks) ++ scattered = (dev->n_free_chunks - erased_chunks); ++ ++ if (!context->bg_running) ++ return 0; ++ else if (scattered < (dev->param.chunks_per_block * 2)) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 2) ++ return 0; ++ else if (erased_chunks > dev->n_free_chunks / 4) ++ return 1; ++ else ++ return 2; ++} ++ ++#ifdef YAFFS_COMPILE_BACKGROUND ++ ++void yaffs_background_waker(unsigned long data) ++{ ++ wake_up_process((struct task_struct *)data); ++} ++ ++static int yaffs_bg_thread_fn(void *data) ++{ ++ struct yaffs_dev *dev = (struct yaffs_dev *)data; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ unsigned long now = jiffies; ++ unsigned long next_dir_update = now; ++ unsigned long next_gc = now; ++ unsigned long expires; ++ unsigned int urgency; ++ ++ int gc_result; ++ struct timer_list timer; ++ ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, ++ "yaffs_background starting for dev %p", (void *)dev); ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ set_freezable(); ++#endif ++ while (context->bg_running) { ++ yaffs_trace(YAFFS_TRACE_BACKGROUND, "yaffs_background"); ++ ++ if (kthread_should_stop()) ++ break; ++ ++#ifdef YAFFS_COMPILE_FREEZER ++ if (try_to_freeze()) ++ continue; ++#endif ++ yaffs_gross_lock(dev); ++ ++ now = jiffies; ++ ++ if (time_after(now, next_dir_update) && yaffs_bg_enable) { ++ yaffs_update_dirty_dirs(dev); ++ next_dir_update = now + HZ; ++ } ++ ++ if (time_after(now, next_gc) && yaffs_bg_enable) { ++ if (!dev->is_checkpointed) { ++ urgency = yaffs_bg_gc_urgency(dev); ++ gc_result = yaffs_bg_gc(dev, urgency); ++ if (urgency > 1) ++ next_gc = now + HZ / 20 + 1; ++ else if (urgency > 0) ++ next_gc = now + HZ / 10 + 1; ++ else ++ next_gc = now + HZ * 2; ++ } else { ++ /* ++ * gc not running so set to next_dir_update ++ * to cut down on wake ups ++ */ ++ next_gc = next_dir_update; ++ } ++ } ++ yaffs_gross_unlock(dev); ++#if 1 ++ expires = next_dir_update; ++ if (time_before(next_gc, expires)) ++ expires = next_gc; ++ if (time_before(expires, now)) ++ expires = now + HZ; ++ ++ Y_INIT_TIMER(&timer); ++ timer.expires = expires + 1; ++ timer.data = (unsigned long)current; ++ timer.function = yaffs_background_waker; ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ add_timer(&timer); ++ schedule(); ++ del_timer_sync(&timer); ++#else ++ msleep(10); ++#endif ++ } ++ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ int retval = 0; ++ struct yaffs_linux_context *context = yaffs_dev_to_lc(dev); ++ ++ if (dev->read_only) ++ return -1; ++ ++ context->bg_running = 1; ++ ++ context->bg_thread = kthread_run(yaffs_bg_thread_fn, ++ (void *)dev, "yaffs-bg-%d", ++ context->mount_id); ++ ++ if (IS_ERR(context->bg_thread)) { ++ retval = PTR_ERR(context->bg_thread); ++ context->bg_thread = NULL; ++ context->bg_running = 0; ++ } ++ return retval; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++ struct yaffs_linux_context *ctxt = yaffs_dev_to_lc(dev); ++ ++ ctxt->bg_running = 0; ++ ++ if (ctxt->bg_thread) { ++ kthread_stop(ctxt->bg_thread); ++ ctxt->bg_thread = NULL; ++ } ++} ++#else ++static int yaffs_bg_thread_fn(void *data) ++{ ++ return 0; ++} ++ ++static int yaffs_bg_start(struct yaffs_dev *dev) ++{ ++ return 0; ++} ++ ++static void yaffs_bg_stop(struct yaffs_dev *dev) ++{ ++} ++#endif ++ ++ ++static void yaffs_flush_inodes(struct super_block *sb) ++{ ++ struct inode *iptr; ++ struct yaffs_obj *obj; ++ ++ list_for_each_entry(iptr, &sb->s_inodes, i_sb_list) { ++ obj = yaffs_inode_to_obj(iptr); ++ if (obj) { ++ yaffs_trace(YAFFS_TRACE_OS, ++ "flushing obj %d", ++ obj->obj_id); ++ yaffs_flush_file(obj, 1, 0); ++ } ++ } ++} ++ ++static void yaffs_flush_super(struct super_block *sb, int do_checkpoint) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ if (!dev) ++ return; ++ ++ yaffs_flush_inodes(sb); ++ yaffs_update_dirty_dirs(dev); ++ yaffs_flush_whole_cache(dev); ++ if (do_checkpoint) ++ yaffs_checkpoint_save(dev); ++} ++ ++static LIST_HEAD(yaffs_context_list); ++struct mutex yaffs_context_lock; ++ ++static void yaffs_put_super(struct super_block *sb) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ struct mtd_info *mtd = yaffs_dev_to_mtd(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super"); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "Shutting down yaffs background thread"); ++ yaffs_bg_stop(dev); ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_BACKGROUND, ++ "yaffs background thread shut down"); ++ ++ yaffs_gross_lock(dev); ++ ++ yaffs_flush_super(sb, 1); ++ ++ yaffs_deinitialise(dev); ++ ++ yaffs_gross_unlock(dev); ++ ++ mutex_lock(&yaffs_context_lock); ++ list_del_init(&(yaffs_dev_to_lc(dev)->context_list)); ++ mutex_unlock(&yaffs_context_lock); ++ ++ if (yaffs_dev_to_lc(dev)->spare_buffer) { ++ kfree(yaffs_dev_to_lc(dev)->spare_buffer); ++ yaffs_dev_to_lc(dev)->spare_buffer = NULL; ++ } ++ ++ kfree(dev); ++ ++ yaffs_put_mtd_device(mtd); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_ALWAYS, ++ "yaffs_put_super done"); ++} ++ ++ ++static unsigned yaffs_gc_control_callback(struct yaffs_dev *dev) ++{ ++ return yaffs_gc_control; ++} ++ ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ ++static struct inode *yaffs2_nfs_get_inode(struct super_block *sb, uint64_t ino, ++ uint32_t generation) ++{ ++ return Y_IGET(sb, ino); ++} ++ ++static struct dentry *yaffs2_fh_to_dentry(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++static struct dentry *yaffs2_fh_to_parent(struct super_block *sb, ++ struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ return generic_fh_to_parent(sb, fid, fh_len, fh_type, ++ yaffs2_nfs_get_inode); ++} ++ ++struct dentry *yaffs2_get_parent(struct dentry *dentry) ++{ ++ ++ struct super_block *sb = dentry->d_inode->i_sb; ++ struct dentry *parent = ERR_PTR(-ENOENT); ++ struct inode *inode; ++ unsigned long parent_ino; ++ struct yaffs_obj *d_obj; ++ struct yaffs_obj *parent_obj; ++ ++ d_obj = yaffs_inode_to_obj(dentry->d_inode); ++ ++ if (d_obj) { ++ parent_obj = d_obj->parent; ++ if (parent_obj) { ++ parent_ino = yaffs_get_obj_inode(parent_obj); ++ inode = Y_IGET(sb, parent_ino); ++ ++ if (IS_ERR(inode)) { ++ parent = ERR_CAST(inode); ++ } else { ++ parent = d_obtain_alias(inode); ++ if (!IS_ERR(parent)) { ++ parent = ERR_PTR(-ENOMEM); ++ iput(inode); ++ } ++ } ++ } ++ } ++ ++ return parent; ++} ++ ++/* Just declare a zero structure as a NULL value implies ++ * using the default functions of exportfs. ++ */ ++ ++static struct export_operations yaffs_export_ops = { ++ .fh_to_dentry = yaffs2_fh_to_dentry, ++ .fh_to_parent = yaffs2_fh_to_parent, ++ .get_parent = yaffs2_get_parent, ++}; ++ ++#endif ++ ++static void yaffs_unstitch_obj(struct inode *inode, struct yaffs_obj *obj) ++{ ++ /* Clear the association between the inode and ++ * the struct yaffs_obj. ++ */ ++ obj->my_inode = NULL; ++ yaffs_inode_to_obj_lv(inode) = NULL; ++ ++ /* If the object freeing was deferred, then the real ++ * free happens now. ++ * This should fix the inode inconsistency problem. ++ */ ++ yaffs_handle_defered_free(obj); ++} ++ ++#ifdef YAFFS_HAS_EVICT_INODE ++/* yaffs_evict_inode combines into one operation what was previously done in ++ * yaffs_clear_inode() and yaffs_delete_inode() ++ * ++ */ ++static void yaffs_evict_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ int deleteme = 0; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_evict_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (!inode->i_nlink && !is_bad_inode(inode)) ++ deleteme = 1; ++ truncate_inode_pages(&inode->i_data, 0); ++ Y_CLEAR_INODE(inode); ++ ++ if (deleteme && obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++} ++#else ++ ++/* clear is called to tell the fs to release any per-inode data it holds. ++ * The object might still exist on disk and is just being thrown out of the cache ++ * or else the object has actually been deleted and we're being called via ++ * the chain ++ * yaffs_delete_inode() -> clear_inode()->yaffs_clear_inode() ++ */ ++ ++static void yaffs_clear_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_dev *dev; ++ ++ obj = yaffs_inode_to_obj(inode); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_clear_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_unstitch_obj(inode, obj); ++ yaffs_gross_unlock(dev); ++ } ++ ++} ++ ++/* delete is called when the link count is zero and the inode ++ * is put (ie. nobody wants to know about it anymore, time to ++ * delete the file). ++ * NB Must call clear_inode() ++ */ ++static void yaffs_delete_inode(struct inode *inode) ++{ ++ struct yaffs_obj *obj = yaffs_inode_to_obj(inode); ++ struct yaffs_dev *dev; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_delete_inode: ino %d, count %d %s", ++ (int)inode->i_ino, atomic_read(&inode->i_count), ++ obj ? "object exists" : "null object"); ++ ++ if (obj) { ++ dev = obj->my_dev; ++ yaffs_gross_lock(dev); ++ yaffs_del_obj(obj); ++ yaffs_gross_unlock(dev); ++ } ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)) ++ truncate_inode_pages(&inode->i_data, 0); ++#endif ++ clear_inode(inode); ++} ++#endif ++ ++ ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_dentry_to_obj(dentry)->my_dev; ++ struct super_block *sb = dentry->d_sb; ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#else ++static int yaffs_statfs(struct super_block *sb, struct statfs *buf) ++{ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++#endif ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_statfs"); ++ ++ yaffs_gross_lock(dev); ++ ++ buf->f_type = YAFFS_MAGIC; ++ buf->f_bsize = sb->s_blocksize; ++ buf->f_namelen = 255; ++ ++ if (dev->data_bytes_per_chunk & (dev->data_bytes_per_chunk - 1)) { ++ /* Do this if chunk size is not a power of 2 */ ++ ++ uint64_t bytes_in_dev; ++ uint64_t bytes_free; ++ ++ bytes_in_dev = ++ ((uint64_t) ++ ((dev->param.end_block - dev->param.start_block + ++ 1))) * ((uint64_t) (dev->param.chunks_per_block * ++ dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_in_dev, sb->s_blocksize); /* bytes_in_dev becomes the number of blocks */ ++ buf->f_blocks = bytes_in_dev; ++ ++ bytes_free = ((uint64_t) (yaffs_get_n_free_chunks(dev))) * ++ ((uint64_t) (dev->data_bytes_per_chunk)); ++ ++ do_div(bytes_free, sb->s_blocksize); ++ ++ buf->f_bfree = bytes_free; ++ ++ } else if (sb->s_blocksize > dev->data_bytes_per_chunk) { ++ ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) / ++ (sb->s_blocksize / dev->data_bytes_per_chunk); ++ } else { ++ buf->f_blocks = ++ (dev->param.end_block - dev->param.start_block + 1) * ++ dev->param.chunks_per_block * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ ++ buf->f_bfree = ++ yaffs_get_n_free_chunks(dev) * ++ (dev->data_bytes_per_chunk / sb->s_blocksize); ++ } ++ ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_bavail = buf->f_bfree; ++ ++ yaffs_gross_unlock(dev); ++ return 0; ++} ++ ++ ++ ++static int yaffs_do_sync_fs(struct super_block *sb, int request_checkpoint) ++{ ++ ++ struct yaffs_dev *dev = yaffs_super_to_dev(sb); ++ unsigned int oneshot_checkpoint = (yaffs_auto_checkpoint & 4); ++ unsigned gc_urgent = yaffs_bg_gc_urgency(dev); ++ int do_checkpoint; ++ int dirty = yaffs_check_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_do_sync_fs: gc-urgency %d %s %s%s", ++ gc_urgent, ++ dirty ? "dirty" : "clean", ++ request_checkpoint ? "checkpoint requested" : "no checkpoint", ++ oneshot_checkpoint ? " one-shot" : ""); ++ ++ yaffs_gross_lock(dev); ++ do_checkpoint = ((request_checkpoint && !gc_urgent) || ++ oneshot_checkpoint) && !dev->is_checkpointed; ++ ++ if (dirty || do_checkpoint) { ++ yaffs_flush_super(sb, !dev->is_checkpointed && do_checkpoint); ++ yaffs_clear_super_dirty(dev); ++ if (oneshot_checkpoint) ++ yaffs_auto_checkpoint &= ~4; ++ } ++ yaffs_gross_unlock(dev); ++ ++ return 0; ++} ++ ++ ++#ifdef YAFFS_HAS_WRITE_SUPER ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static void yaffs_write_super(struct super_block *sb) ++#else ++static int yaffs_write_super(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 2); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC | YAFFS_TRACE_BACKGROUND, ++ "yaffs_write_super %s", ++ request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)) ++ return 0; ++#endif ++} ++#endif ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_sync_fs(struct super_block *sb, int wait) ++#else ++static int yaffs_sync_fs(struct super_block *sb) ++#endif ++{ ++ unsigned request_checkpoint = (yaffs_auto_checkpoint >= 1); ++ ++ yaffs_trace(YAFFS_TRACE_OS | YAFFS_TRACE_SYNC, ++ "yaffs_sync_fs%s", request_checkpoint ? " checkpt" : ""); ++ ++ yaffs_do_sync_fs(sb, request_checkpoint); ++ ++ return 0; ++} ++ ++ ++ ++static const struct super_operations yaffs_super_ops = { ++ .statfs = yaffs_statfs, ++ ++#ifndef YAFFS_USE_OWN_IGET ++ .read_inode = yaffs_read_inode, ++#endif ++#ifdef YAFFS_HAS_PUT_INODE ++ .put_inode = yaffs_put_inode, ++#endif ++ .put_super = yaffs_put_super, ++#ifdef YAFFS_HAS_EVICT_INODE ++ .evict_inode = yaffs_evict_inode, ++#else ++ .delete_inode = yaffs_delete_inode, ++ .clear_inode = yaffs_clear_inode, ++#endif ++ .sync_fs = yaffs_sync_fs, ++#ifdef YAFFS_HAS_WRITE_SUPER ++ .write_super = yaffs_write_super, ++#endif ++}; ++ ++struct yaffs_options { ++ int inband_tags; ++ int skip_checkpoint_read; ++ int skip_checkpoint_write; ++ int no_cache; ++ int tags_ecc_on; ++ int tags_ecc_overridden; ++ int lazy_loading_enabled; ++ int lazy_loading_overridden; ++ int empty_lost_and_found; ++ int empty_lost_and_found_overridden; ++ int disable_summary; ++}; ++ ++#define MAX_OPT_LEN 30 ++static int yaffs_parse_options(struct yaffs_options *options, ++ const char *options_str) ++{ ++ char cur_opt[MAX_OPT_LEN + 1]; ++ int p; ++ int error = 0; ++ ++ /* Parse through the options which is a comma seperated list */ ++ ++ while (options_str && *options_str && !error) { ++ memset(cur_opt, 0, MAX_OPT_LEN + 1); ++ p = 0; ++ ++ while (*options_str == ',') ++ options_str++; ++ ++ while (*options_str && *options_str != ',') { ++ if (p < MAX_OPT_LEN) { ++ cur_opt[p] = *options_str; ++ p++; ++ } ++ options_str++; ++ } ++ ++ if (!strcmp(cur_opt, "inband-tags")) { ++ options->inband_tags = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-off")) { ++ options->tags_ecc_on = 0; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "tags-ecc-on")) { ++ options->tags_ecc_on = 1; ++ options->tags_ecc_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-off")) { ++ options->lazy_loading_enabled = 0; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "lazy-loading-on")) { ++ options->lazy_loading_enabled = 1; ++ options->lazy_loading_overridden = 1; ++ } else if (!strcmp(cur_opt, "disable-summary")) { ++ options->disable_summary = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-off")) { ++ options->empty_lost_and_found = 0; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "empty-lost-and-found-on")) { ++ options->empty_lost_and_found = 1; ++ options->empty_lost_and_found_overridden = 1; ++ } else if (!strcmp(cur_opt, "no-cache")) { ++ options->no_cache = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-read")) { ++ options->skip_checkpoint_read = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint-write")) { ++ options->skip_checkpoint_write = 1; ++ } else if (!strcmp(cur_opt, "no-checkpoint")) { ++ options->skip_checkpoint_read = 1; ++ options->skip_checkpoint_write = 1; ++ } else { ++ printk(KERN_INFO "yaffs: Bad mount option \"%s\"\n", ++ cur_opt); ++ error = 1; ++ } ++ } ++ ++ return error; ++} ++ ++ ++static struct dentry *yaffs_make_root(struct inode *inode) ++{ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) ++ struct dentry *root = d_alloc_root(inode); ++ ++ if (!root) ++ iput(inode); ++ ++ return root; ++#else ++ return d_make_root(inode); ++#endif ++} ++ ++ ++ ++ ++static struct super_block *yaffs_internal_read_super(int yaffs_version, ++ struct super_block *sb, ++ void *data, int silent) ++{ ++ int n_blocks; ++ struct inode *inode = NULL; ++ struct dentry *root; ++ struct yaffs_dev *dev = 0; ++ char devname_buf[BDEVNAME_SIZE + 1]; ++ struct mtd_info *mtd; ++ int err; ++ char *data_str = (char *)data; ++ struct yaffs_linux_context *context = NULL; ++ struct yaffs_param *param; ++ ++ int read_only = 0; ++ int inband_tags = 0; ++ ++ struct yaffs_options options; ++ ++ unsigned mount_id; ++ int found; ++ struct yaffs_linux_context *context_iterator; ++ struct list_head *l; ++ ++ if (!sb) { ++ printk(KERN_INFO "yaffs: sb is NULL\n"); ++ return NULL; ++ } ++ ++ sb->s_magic = YAFFS_MAGIC; ++ sb->s_op = &yaffs_super_ops; ++ sb->s_flags |= MS_NOATIME; ++ ++ read_only = ((sb->s_flags & MS_RDONLY) != 0); ++ ++#ifdef YAFFS_COMPILE_EXPORTFS ++ sb->s_export_op = &yaffs_export_ops; ++#endif ++ ++ if (!sb->s_dev) ++ printk(KERN_INFO "yaffs: sb->s_dev is NULL\n"); ++ else if (!yaffs_devname(sb, devname_buf)) ++ printk(KERN_INFO "yaffs: devname is NULL\n"); ++ else ++ printk(KERN_INFO "yaffs: dev is %d name is \"%s\" %s\n", ++ sb->s_dev, ++ yaffs_devname(sb, devname_buf), read_only ? "ro" : "rw"); ++ ++ if (!data_str) ++ data_str = ""; ++ ++ printk(KERN_INFO "yaffs: passed flags \"%s\"\n", data_str); ++ ++ memset(&options, 0, sizeof(options)); ++ ++ if (yaffs_parse_options(&options, data_str)) { ++ /* Option parsing failed */ ++ return NULL; ++ } ++ ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: Using yaffs%d", yaffs_version); ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: block size %d", (int)(sb->s_blocksize)); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: Attempting MTD mount of %u.%u,\"%s\"", ++ MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ yaffs_devname(sb, devname_buf)); ++ ++ /* Get the device */ ++ mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); ++ if (IS_ERR(mtd)) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs: MTD device %u either not valid or unavailable", ++ MINOR(sb->s_dev)); ++ return NULL; ++ } ++ ++ if (yaffs_auto_select && yaffs_version == 1 && WRITE_SIZE(mtd) >= 2048) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs2"); ++ yaffs_version = 2; ++ } ++ ++ /* Added NCB 26/5/2006 for completeness */ ++ if (yaffs_version == 2 && !options.inband_tags ++ && WRITE_SIZE(mtd) == 512) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "auto selecting yaffs1"); ++ yaffs_version = 1; ++ } ++ ++ if (mtd->oobavail < sizeof(struct yaffs_packed_tags2) || ++ options.inband_tags) ++ inband_tags = 1; ++ ++ if(yaffs_verify_mtd(mtd, yaffs_version, inband_tags) < 0) ++ return NULL; ++ ++ /* OK, so if we got here, we have an MTD that's NAND and looks ++ * like it has the right capabilities ++ * Set the struct yaffs_dev up for mtd ++ */ ++ ++ if (!read_only && !(mtd->flags & MTD_WRITEABLE)) { ++ read_only = 1; ++ printk(KERN_INFO ++ "yaffs: mtd is read only, setting superblock read only\n" ++ ); ++ sb->s_flags |= MS_RDONLY; ++ } ++ ++ dev = kmalloc(sizeof(struct yaffs_dev), GFP_KERNEL); ++ context = kmalloc(sizeof(struct yaffs_linux_context), GFP_KERNEL); ++ ++ if (!dev || !context) { ++ kfree(dev); ++ kfree(context); ++ dev = NULL; ++ context = NULL; ++ ++ /* Deep shit could not allocate device structure */ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: Failed trying to allocate struct yaffs_dev." ++ ); ++ return NULL; ++ } ++ memset(dev, 0, sizeof(struct yaffs_dev)); ++ param = &(dev->param); ++ ++ memset(context, 0, sizeof(struct yaffs_linux_context)); ++ dev->os_context = context; ++ INIT_LIST_HEAD(&(context->context_list)); ++ context->dev = dev; ++ context->super = sb; ++ ++ dev->read_only = read_only; ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++ sb->s_fs_info = dev; ++#else ++ sb->u.generic_sbp = dev; ++#endif ++ ++ ++ dev->driver_context = mtd; ++ param->name = mtd->name; ++ ++ /* Set up the memory size parameters.... */ ++ ++ ++ param->n_reserved_blocks = 5; ++ param->n_caches = (options.no_cache) ? 0 : 10; ++ param->inband_tags = inband_tags; ++ ++ param->enable_xattr = 1; ++ if (options.lazy_loading_overridden) ++ param->disable_lazy_load = !options.lazy_loading_enabled; ++ ++ param->defered_dir_update = 1; ++ ++ if (options.tags_ecc_overridden) ++ param->no_tags_ecc = !options.tags_ecc_on; ++ ++ param->empty_lost_n_found = 1; ++ param->refresh_period = 500; ++ param->disable_summary = options.disable_summary; ++ ++ ++#ifdef CONFIG_YAFFS_DISABLE_BAD_BLOCK_MARKING ++ param->disable_bad_block_marking = 1; ++#endif ++ if (options.empty_lost_and_found_overridden) ++ param->empty_lost_n_found = options.empty_lost_and_found; ++ ++ /* ... and the functions. */ ++ if (yaffs_version == 2) { ++ param->is_yaffs2 = 1; ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++ param->total_bytes_per_chunk = mtd->writesize; ++ param->chunks_per_block = mtd->erasesize / mtd->writesize; ++#else ++ param->total_bytes_per_chunk = mtd->oobblock; ++ param->chunks_per_block = mtd->erasesize / mtd->oobblock; ++#endif ++ n_blocks = YCALCBLOCKS(mtd->size, mtd->erasesize); ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ } else { ++ param->is_yaffs2 = 0; ++ n_blocks = YCALCBLOCKS(mtd->size, ++ YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK); ++ ++ param->chunks_per_block = YAFFS_CHUNKS_PER_BLOCK; ++ param->total_bytes_per_chunk = YAFFS_BYTES_PER_CHUNK; ++ } ++ ++ param->start_block = 0; ++ param->end_block = n_blocks - 1; ++ ++ yaffs_mtd_drv_install(dev); ++ ++ param->sb_dirty_fn = yaffs_set_super_dirty; ++ param->gc_control_fn = yaffs_gc_control_callback; ++ ++ yaffs_dev_to_lc(dev)->super = sb; ++ ++ param->use_nand_ecc = 1; ++ ++ param->skip_checkpt_rd = options.skip_checkpoint_read; ++ param->skip_checkpt_wr = options.skip_checkpoint_write; ++ ++ mutex_lock(&yaffs_context_lock); ++ /* Get a mount id */ ++ found = 0; ++ for (mount_id = 0; !found; mount_id++) { ++ found = 1; ++ list_for_each(l, &yaffs_context_list) { ++ context_iterator = ++ list_entry(l, struct yaffs_linux_context, ++ context_list); ++ if (context_iterator->mount_id == mount_id) ++ found = 0; ++ } ++ } ++ context->mount_id = mount_id; ++ ++ list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), ++ &yaffs_context_list); ++ mutex_unlock(&yaffs_context_lock); ++ ++ /* Directory search handling... */ ++ INIT_LIST_HEAD(&(yaffs_dev_to_lc(dev)->search_contexts)); ++ param->remove_obj_fn = yaffs_remove_obj_callback; ++ ++ mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); ++ ++ yaffs_gross_lock(dev); ++ ++ err = yaffs_guts_initialise(dev); ++ ++ yaffs_trace(YAFFS_TRACE_OS, ++ "yaffs_read_super: guts initialised %s", ++ (err == YAFFS_OK) ? "OK" : "FAILED"); ++ ++ if (err == YAFFS_OK) ++ yaffs_bg_start(dev); ++ ++ if (!context->bg_thread) ++ param->defered_dir_update = 0; ++ ++ sb->s_maxbytes = yaffs_max_file_size(dev); ++ ++ /* Release lock before yaffs_get_inode() */ ++ yaffs_gross_unlock(dev); ++ ++ /* Create root inode */ ++ if (err == YAFFS_OK) ++ inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0, yaffs_root(dev)); ++ ++ if (!inode) ++ return NULL; ++ ++ inode->i_op = &yaffs_dir_inode_operations; ++ inode->i_fop = &yaffs_dir_operations; ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: got root inode"); ++ ++ root = yaffs_make_root(inode); ++ ++ if (!root) ++ return NULL; ++ ++ sb->s_root = root; ++ if(!dev->is_checkpointed) ++ yaffs_set_super_dirty(dev); ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs_read_super: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_trace(YAFFS_TRACE_OS, "yaffs_read_super: done"); ++ return sb; ++} ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data, struct vfsmount *mnt) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs_mount, ++#else ++ .get_sb = yaffs_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs_read_super(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(1, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++static int yaffs2_internal_read_super_mtd(struct super_block *sb, void *data, ++ int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent) ? 0 : -EINVAL; ++} ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++static struct dentry *yaffs2_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data) ++{ ++ return mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd); ++} ++#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17)) ++static int yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, void *data, ++ struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd, mnt); ++} ++#else ++static struct super_block *yaffs2_read_super(struct file_system_type *fs, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ ++ return get_sb_bdev(fs, flags, dev_name, data, ++ yaffs2_internal_read_super_mtd); ++} ++#endif ++ ++static struct file_system_type yaffs2_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "yaffs2", ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) ++ .mount = yaffs2_mount, ++#else ++ .get_sb = yaffs2_read_super, ++#endif ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV, ++}; ++#else ++static struct super_block *yaffs2_read_super(struct super_block *sb, ++ void *data, int silent) ++{ ++ return yaffs_internal_read_super(2, sb, data, silent); ++} ++ ++static DECLARE_FSTYPE(yaffs2_fs_type, "yaffs2", yaffs2_read_super, ++ FS_REQUIRES_DEV); ++#endif ++ ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) ++static struct proc_dir_entry *my_proc_entry; ++ ++static char *yaffs_dump_dev_part0(char *buf, struct yaffs_dev *dev) ++{ ++ struct yaffs_param *param = &dev->param; ++ int bs[10]; ++ ++ yaffs_count_blocks_by_state(dev,bs); ++ ++ buf += sprintf(buf, "start_block.......... %d\n", param->start_block); ++ buf += sprintf(buf, "end_block............ %d\n", param->end_block); ++ buf += sprintf(buf, "total_bytes_per_chunk %d\n", ++ param->total_bytes_per_chunk); ++ buf += sprintf(buf, "use_nand_ecc......... %d\n", param->use_nand_ecc); ++ buf += sprintf(buf, "no_tags_ecc.......... %d\n", param->no_tags_ecc); ++ buf += sprintf(buf, "is_yaffs2............ %d\n", param->is_yaffs2); ++ buf += sprintf(buf, "inband_tags.......... %d\n", param->inband_tags); ++ buf += sprintf(buf, "empty_lost_n_found... %d\n", ++ param->empty_lost_n_found); ++ buf += sprintf(buf, "disable_lazy_load.... %d\n", ++ param->disable_lazy_load); ++ buf += sprintf(buf, "disable_bad_block_mrk %d\n", ++ param->disable_bad_block_marking); ++ buf += sprintf(buf, "refresh_period....... %d\n", ++ param->refresh_period); ++ buf += sprintf(buf, "n_caches............. %d\n", param->n_caches); ++ buf += sprintf(buf, "n_reserved_blocks.... %d\n", ++ param->n_reserved_blocks); ++ buf += sprintf(buf, "always_check_erased.. %d\n", ++ param->always_check_erased); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "block count by state\n"); ++ buf += sprintf(buf, "0:%d 1:%d 2:%d 3:%d 4:%d\n", ++ bs[0], bs[1], bs[2], bs[3], bs[4]); ++ buf += sprintf(buf, "5:%d 6:%d 7:%d 8:%d 9:%d\n", ++ bs[5], bs[6], bs[7], bs[8], bs[9]); ++ ++ return buf; ++} ++ ++static char *yaffs_dump_dev_part1(char *buf, struct yaffs_dev *dev) ++{ ++ buf += sprintf(buf, "max file size....... %lld\n", ++ (long long) yaffs_max_file_size(dev)); ++ buf += sprintf(buf, "data_bytes_per_chunk. %d\n", ++ dev->data_bytes_per_chunk); ++ buf += sprintf(buf, "chunk_grp_bits....... %d\n", dev->chunk_grp_bits); ++ buf += sprintf(buf, "chunk_grp_size....... %d\n", dev->chunk_grp_size); ++ buf += sprintf(buf, "n_erased_blocks...... %d\n", dev->n_erased_blocks); ++ buf += sprintf(buf, "blocks_in_checkpt.... %d\n", ++ dev->blocks_in_checkpt); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_tnodes............. %d\n", dev->n_tnodes); ++ buf += sprintf(buf, "n_obj................ %d\n", dev->n_obj); ++ buf += sprintf(buf, "n_free_chunks........ %d\n", dev->n_free_chunks); ++ buf += sprintf(buf, "\n"); ++ buf += sprintf(buf, "n_page_writes........ %u\n", dev->n_page_writes); ++ buf += sprintf(buf, "n_page_reads......... %u\n", dev->n_page_reads); ++ buf += sprintf(buf, "n_erasures........... %u\n", dev->n_erasures); ++ buf += sprintf(buf, "n_gc_copies.......... %u\n", dev->n_gc_copies); ++ buf += sprintf(buf, "all_gcs.............. %u\n", dev->all_gcs); ++ buf += sprintf(buf, "passive_gc_count..... %u\n", ++ dev->passive_gc_count); ++ buf += sprintf(buf, "oldest_dirty_gc_count %u\n", ++ dev->oldest_dirty_gc_count); ++ buf += sprintf(buf, "n_gc_blocks.......... %u\n", dev->n_gc_blocks); ++ buf += sprintf(buf, "bg_gcs............... %u\n", dev->bg_gcs); ++ buf += sprintf(buf, "n_retried_writes..... %u\n", ++ dev->n_retried_writes); ++ buf += sprintf(buf, "n_retired_blocks..... %u\n", ++ dev->n_retired_blocks); ++ buf += sprintf(buf, "n_ecc_fixed.......... %u\n", dev->n_ecc_fixed); ++ buf += sprintf(buf, "n_ecc_unfixed........ %u\n", dev->n_ecc_unfixed); ++ buf += sprintf(buf, "n_tags_ecc_fixed..... %u\n", ++ dev->n_tags_ecc_fixed); ++ buf += sprintf(buf, "n_tags_ecc_unfixed... %u\n", ++ dev->n_tags_ecc_unfixed); ++ buf += sprintf(buf, "cache_hits........... %u\n", dev->cache_hits); ++ buf += sprintf(buf, "n_deleted_files...... %u\n", dev->n_deleted_files); ++ buf += sprintf(buf, "n_unlinked_files..... %u\n", ++ dev->n_unlinked_files); ++ buf += sprintf(buf, "refresh_count........ %u\n", dev->refresh_count); ++ buf += sprintf(buf, "n_bg_deletions....... %u\n", dev->n_bg_deletions); ++ buf += sprintf(buf, "tags_used............ %u\n", dev->tags_used); ++ buf += sprintf(buf, "summary_used......... %u\n", dev->summary_used); ++ ++ return buf; ++} ++ ++static int yaffs_proc_read(char *page, ++ char **start, ++ off_t offset, int count, int *eof, void *data) ++{ ++ struct list_head *item; ++ char *buf = page; ++ int step = offset; ++ int n = 0; ++ ++ /* Get proc_file_read() to step 'offset' by one on each sucessive call. ++ * We use 'offset' (*ppos) to indicate where we are in dev_list. ++ * This also assumes the user has posted a read buffer large ++ * enough to hold the complete output; but that's life in /proc. ++ */ ++ ++ *(int *)start = 1; ++ ++ /* Print header first */ ++ if (step == 0) ++ buf += ++ sprintf(buf, ++ "Multi-version YAFFS built:" __DATE__ " " __TIME__ ++ "\n"); ++ else if (step == 1) ++ buf += sprintf(buf, "\n"); ++ else { ++ step -= 2; ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (n < (step & ~1)) { ++ n += 2; ++ continue; ++ } ++ if ((step & 1) == 0) { ++ buf += ++ sprintf(buf, "\nDevice %d \"%s\"\n", n, ++ dev->param.name); ++ buf = yaffs_dump_dev_part0(buf, dev); ++ } else { ++ buf = yaffs_dump_dev_part1(buf, dev); ++ } ++ ++ break; ++ } ++ mutex_unlock(&yaffs_context_lock); ++ } ++ ++ return buf - page < count ? buf - page : count; ++} ++ ++/** ++ * Set the verbosity of the warnings and error messages. ++ * ++ * Note that the names can only be a..z or _ with the current code. ++ */ ++ ++static struct { ++ char *mask_name; ++ unsigned mask_bitfield; ++} mask_flags[] = { ++ {"allocate", YAFFS_TRACE_ALLOCATE}, ++ {"always", YAFFS_TRACE_ALWAYS}, ++ {"background", YAFFS_TRACE_BACKGROUND}, ++ {"bad_blocks", YAFFS_TRACE_BAD_BLOCKS}, ++ {"buffers", YAFFS_TRACE_BUFFERS}, ++ {"bug", YAFFS_TRACE_BUG}, ++ {"checkpt", YAFFS_TRACE_CHECKPOINT}, ++ {"deletion", YAFFS_TRACE_DELETION}, ++ {"erase", YAFFS_TRACE_ERASE}, ++ {"error", YAFFS_TRACE_ERROR}, ++ {"gc_detail", YAFFS_TRACE_GC_DETAIL}, ++ {"gc", YAFFS_TRACE_GC}, ++ {"lock", YAFFS_TRACE_LOCK}, ++ {"mtd", YAFFS_TRACE_MTD}, ++ {"nandaccess", YAFFS_TRACE_NANDACCESS}, ++ {"os", YAFFS_TRACE_OS}, ++ {"scan_debug", YAFFS_TRACE_SCAN_DEBUG}, ++ {"scan", YAFFS_TRACE_SCAN}, ++ {"mount", YAFFS_TRACE_MOUNT}, ++ {"tracing", YAFFS_TRACE_TRACING}, ++ {"sync", YAFFS_TRACE_SYNC}, ++ {"write", YAFFS_TRACE_WRITE}, ++ {"verify", YAFFS_TRACE_VERIFY}, ++ {"verify_nand", YAFFS_TRACE_VERIFY_NAND}, ++ {"verify_full", YAFFS_TRACE_VERIFY_FULL}, ++ {"verify_all", YAFFS_TRACE_VERIFY_ALL}, ++ {"all", 0xffffffff}, ++ {"none", 0}, ++ {NULL, 0}, ++}; ++ ++#define MAX_MASK_NAME_LENGTH 40 ++static int yaffs_proc_write_trace_options(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ unsigned rg = 0, mask_bitfield; ++ char *end; ++ char *mask_name; ++ const char *x; ++ char substring[MAX_MASK_NAME_LENGTH + 1]; ++ int i; ++ int done = 0; ++ int add, len = 0; ++ int pos = 0; ++ ++ rg = yaffs_trace_mask; ++ ++ while (!done && (pos < count)) { ++ done = 1; ++ while ((pos < count) && isspace(buf[pos])) ++ pos++; ++ ++ switch (buf[pos]) { ++ case '+': ++ case '-': ++ case '=': ++ add = buf[pos]; ++ pos++; ++ break; ++ ++ default: ++ add = ' '; ++ break; ++ } ++ mask_name = NULL; ++ ++ mask_bitfield = simple_strtoul(buf + pos, &end, 0); ++ ++ if (end > buf + pos) { ++ mask_name = "numeral"; ++ len = end - (buf + pos); ++ pos += len; ++ done = 0; ++ } else { ++ for (x = buf + pos, i = 0; ++ (*x == '_' || (*x >= 'a' && *x <= 'z')) && ++ i < MAX_MASK_NAME_LENGTH; x++, i++, pos++) ++ substring[i] = *x; ++ substring[i] = '\0'; ++ ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ if (strcmp(substring, mask_flags[i].mask_name) ++ == 0) { ++ mask_name = mask_flags[i].mask_name; ++ mask_bitfield = ++ mask_flags[i].mask_bitfield; ++ done = 0; ++ break; ++ } ++ } ++ } ++ ++ if (mask_name != NULL) { ++ done = 0; ++ switch (add) { ++ case '-': ++ rg &= ~mask_bitfield; ++ break; ++ case '+': ++ rg |= mask_bitfield; ++ break; ++ case '=': ++ rg = mask_bitfield; ++ break; ++ default: ++ rg |= mask_bitfield; ++ break; ++ } ++ } ++ } ++ ++ yaffs_trace_mask = rg | YAFFS_TRACE_ALWAYS; ++ ++ printk(KERN_DEBUG "new trace = 0x%08X\n", yaffs_trace_mask); ++ ++ if (rg & YAFFS_TRACE_ALWAYS) { ++ for (i = 0; mask_flags[i].mask_name != NULL; i++) { ++ char flag; ++ flag = ((rg & mask_flags[i].mask_bitfield) == ++ mask_flags[i].mask_bitfield) ? '+' : '-'; ++ printk(KERN_DEBUG "%c%s\n", flag, ++ mask_flags[i].mask_name); ++ } ++ } ++ ++ return count; ++} ++ ++/* Debug strings are of the form: ++ * .bnnn print info on block n ++ * .cobjn,chunkn print nand chunk id for objn:chunkn ++ */ ++ ++static int yaffs_proc_debug_write(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ ++ char str[100]; ++ char *p0; ++ char *p1; ++ long p1_val; ++ long p0_val; ++ char cmd; ++ struct list_head *item; ++ ++ memset(str, 0, sizeof(str)); ++ memcpy(str, buf, min(count, sizeof(str) -1)); ++ ++ cmd = str[1]; ++ ++ p0 = str + 2; ++ ++ p1 = p0; ++ ++ while (*p1 && *p1 != ',') { ++ p1++; ++ } ++ *p1 = '\0'; ++ p1++; ++ ++ p0_val = simple_strtol(p0, NULL, 0); ++ p1_val = simple_strtol(p1, NULL, 0); ++ ++ ++ mutex_lock(&yaffs_context_lock); ++ ++ /* Locate and print the Nth entry. Order N-squared but N is small. */ ++ list_for_each(item, &yaffs_context_list) { ++ struct yaffs_linux_context *dc = ++ list_entry(item, struct yaffs_linux_context, ++ context_list); ++ struct yaffs_dev *dev = dc->dev; ++ ++ if (cmd == 'b') { ++ struct yaffs_block_info *bi; ++ ++ bi = yaffs_get_block_info(dev,p0_val); ++ ++ if(bi) { ++ printk("Block %d: state %d, retire %d, use %d, seq %d\n", ++ (int)p0_val, bi->block_state, ++ bi->needs_retiring, bi->pages_in_use, ++ bi->seq_number); ++ } ++ } else if (cmd == 'c') { ++ struct yaffs_obj *obj; ++ int nand_chunk; ++ ++ obj = yaffs_find_by_number(dev, p0_val); ++ if (!obj) ++ printk("No obj %d\n", (int)p0_val); ++ else { ++ if(p1_val == 0) ++ nand_chunk = obj->hdr_chunk; ++ else ++ nand_chunk = ++ yaffs_find_chunk_in_file(obj, ++ p1_val, NULL); ++ printk("Nand chunk for %d:%d is %d\n", ++ (int)p0_val, (int)p1_val, nand_chunk); ++ } ++ } ++ } ++ ++ mutex_unlock(&yaffs_context_lock); ++ ++ return count; ++} ++ ++static int yaffs_proc_write(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ if (buf[0] == '.') ++ return yaffs_proc_debug_write(file, buf, count, data); ++ return yaffs_proc_write_trace_options(file, buf, count, data); ++} ++#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) */ ++ ++/* Stuff to handle installation of file systems */ ++struct file_system_to_install { ++ struct file_system_type *fst; ++ int installed; ++}; ++ ++static struct file_system_to_install fs_to_install[] = { ++ {&yaffs_fs_type, 0}, ++ {&yaffs2_fs_type, 0}, ++ {NULL, 0} ++}; ++ ++static int __init init_yaffs_fs(void) ++{ ++ int error = 0; ++ struct file_system_to_install *fsinst; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs built " __DATE__ " " __TIME__ " Installing."); ++ ++ mutex_init(&yaffs_context_lock); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) ++ /* Install the proc_fs entries */ ++ my_proc_entry = create_proc_entry("yaffs", ++ S_IRUGO | S_IFREG, YPROC_ROOT); ++ ++ if (my_proc_entry) { ++ my_proc_entry->write_proc = yaffs_proc_write; ++ my_proc_entry->read_proc = yaffs_proc_read; ++ my_proc_entry->data = NULL; ++ } else { ++ return -ENOMEM; ++ } ++#endif ++ ++ /* Now add the file system entries */ ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst && !error) { ++ error = register_filesystem(fsinst->fst); ++ if (!error) ++ fsinst->installed = 1; ++ fsinst++; ++ } ++ ++ /* Any errors? uninstall */ ++ if (error) { ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++ } ++ ++ return error; ++} ++ ++static void __exit exit_yaffs_fs(void) ++{ ++ ++ struct file_system_to_install *fsinst; ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "yaffs built " __DATE__ " " __TIME__ " removing."); ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)) ++ remove_proc_entry("yaffs", YPROC_ROOT); ++#endif ++ ++ fsinst = fs_to_install; ++ ++ while (fsinst->fst) { ++ if (fsinst->installed) { ++ unregister_filesystem(fsinst->fst); ++ fsinst->installed = 0; ++ } ++ fsinst++; ++ } ++} ++ ++module_init(init_yaffs_fs) ++ module_exit(exit_yaffs_fs) ++ ++ MODULE_DESCRIPTION("YAFFS2 - a NAND specific flash file system"); ++MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002-2011"); ++MODULE_LICENSE("GPL"); +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.c linux-3.4.90/fs/yaffs2/yaffs_yaffs1.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_yaffs1.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,422 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_yaffs1.h" ++#include "yportenv.h" ++#include "yaffs_trace.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_nand.h" ++#include "yaffs_attribs.h" ++ ++int yaffs1_scan(struct yaffs_dev *dev) ++{ ++ struct yaffs_ext_tags tags; ++ int blk; ++ int result; ++ int chunk; ++ int c; ++ int deleted; ++ enum yaffs_block_state state; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int alloc_failed = 0; ++ struct yaffs_shadow_fixer *shadow_fixers = NULL; ++ u8 *chunk_data; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs1_scan starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, state, seq_number); ++ ++ if (state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } ++ bi++; ++ } ++ ++ /* For each block.... */ ++ for (blk = dev->internal_start_block; ++ !alloc_failed && blk <= dev->internal_end_block; blk++) { ++ ++ cond_resched(); ++ ++ bi = yaffs_get_block_info(dev, blk); ++ state = bi->block_state; ++ ++ deleted = 0; ++ ++ /* For each chunk in each block that needs scanning.... */ ++ for (c = 0; ++ !alloc_failed && c < dev->param.chunks_per_block && ++ state == YAFFS_BLOCK_STATE_NEEDS_SCAN; c++) { ++ /* Read the tags and decide what to do */ ++ chunk = blk * dev->param.chunks_per_block + c; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, ++ &tags); ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (tags.ecc_result == YAFFS_ECC_RESULT_UNFIXED || ++ tags.is_deleted) { ++ /* YAFFS1 only... ++ * A deleted chunk ++ */ ++ deleted++; ++ dev->n_free_chunks++; ++ } else if (!tags.chunk_used) { ++ /* An unassigned chunk in the block ++ * This means that either the block is empty or ++ * this is the one being allocated from ++ */ ++ ++ if (c == 0) { ++ /* We're looking at the first chunk in ++ *the block so the block is unused */ ++ state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ /* this is the block being allocated */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, c); ++ state = YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = c; ++ dev->alloc_block_finder = blk; ++ ++ } ++ ++ dev->n_free_chunks += ++ (dev->param.chunks_per_block - c); ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ unsigned int endpos; ++ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ /* PutChunkIntoFile checks for a clash ++ * (two data chunks with the same chunk_id). ++ */ ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in) { ++ if (!yaffs_put_chunk_in_file ++ (in, tags.chunk_id, chunk, 1)) ++ alloc_failed = 1; ++ } ++ ++ endpos = ++ (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk + ++ tags.n_bytes; ++ if (in && ++ in->variant_type == ++ YAFFS_OBJECT_TYPE_FILE && ++ in->variant.file_variant.scanned_size < ++ endpos) { ++ in->variant.file_variant.scanned_size = ++ endpos; ++ if (!dev->param.use_header_file_size) { ++ in->variant. ++ file_variant.file_size = ++ in->variant. ++ file_variant.scanned_size; ++ } ++ ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Make the object ++ */ ++ yaffs_set_chunk_bit(dev, blk, c); ++ bi->pages_in_use++; ++ ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ in = yaffs_find_by_number(dev, tags.obj_id); ++ if (in && in->variant_type != oh->type) { ++ /* This should not happen, but somehow ++ * Wev'e ended up with an obj_id that ++ * has been reused but not yet deleted, ++ * and worse still it has changed type. ++ * Delete the old object. ++ */ ++ ++ yaffs_del_obj(in); ++ in = NULL; ++ } ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ oh->type); ++ ++ if (!in) ++ alloc_failed = 1; ++ ++ if (in && oh->shadows_obj > 0) { ++ ++ struct yaffs_shadow_fixer *fixer; ++ fixer = ++ kmalloc(sizeof ++ (struct yaffs_shadow_fixer), ++ GFP_NOFS); ++ if (fixer) { ++ fixer->next = shadow_fixers; ++ shadow_fixers = fixer; ++ fixer->obj_id = tags.obj_id; ++ fixer->shadowed_id = ++ oh->shadows_obj; ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Shadow fixer: %d shadows %d", ++ fixer->obj_id, ++ fixer->shadowed_id); ++ ++ } ++ ++ } ++ ++ if (in && in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate and need to ++ * resolve it. */ ++ ++ unsigned existing_serial = in->serial; ++ unsigned new_serial = ++ tags.serial_number; ++ ++ if (((existing_serial + 1) & 3) == ++ new_serial) { ++ /* Use new one - destroy the ++ * exisiting one */ ++ yaffs_chunk_del(dev, ++ in->hdr_chunk, ++ 1, __LINE__); ++ in->valid = 0; ++ } else { ++ /* Use existing - destroy ++ * this one. */ ++ yaffs_chunk_del(dev, chunk, 1, ++ __LINE__); ++ } ++ } ++ ++ if (in && !in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == ++ YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ } else if (in && !in->valid) { ++ /* we need to load this info */ ++ ++ in->valid = 1; ++ in->variant_type = oh->type; ++ ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->hdr_chunk = chunk; ++ in->serial = tags.serial_number; ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ in->dirty = 0; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ parent = ++ yaffs_find_or_create_by_number ++ (dev, oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ if (!parent) ++ alloc_failed = 1; ++ if (parent && parent->variant_type == ++ YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant. ++ children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, a problem.... ++ * We're trying to use a ++ * non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ ++ yaffs_add_obj_to_dir(parent, in); ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ if (dev->param. ++ use_header_file_size) ++ in->variant. ++ file_variant.file_size ++ = yaffs_oh_to_size(oh); ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ in->variant. ++ hardlink_variant.equiv_id = ++ oh->equiv_id; ++ list_add(&in->hard_links, ++ &hard_list); ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ in->variant.symlink_variant. ++ alias = ++ yaffs_clone_str(oh->alias); ++ if (!in->variant. ++ symlink_variant.alias) ++ alloc_failed = 1; ++ break; ++ } ++ } ++ } ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, ++ * then the block is fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ if (state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ /* If the block was partially allocated then ++ * treat it as fully allocated. */ ++ state = YAFFS_BLOCK_STATE_FULL; ++ dev->alloc_block = -1; ++ } ++ ++ bi->block_state = state; ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) ++ yaffs_block_became_dirty(dev, blk); ++ } ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We should now have scanned all the objects, now it's time to add ++ * these hardlinks. ++ */ ++ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ /* ++ * Fix up any shadowed objects. ++ * There should not be more than one of these. ++ */ ++ { ++ struct yaffs_shadow_fixer *fixer; ++ struct yaffs_obj *obj; ++ ++ while (shadow_fixers) { ++ fixer = shadow_fixers; ++ shadow_fixers = fixer->next; ++ /* Complete the rename transaction by deleting the ++ * shadowed object then setting the object header ++ to unshadowed. ++ */ ++ obj = yaffs_find_by_number(dev, fixer->shadowed_id); ++ if (obj) ++ yaffs_del_obj(obj); ++ ++ obj = yaffs_find_by_number(dev, fixer->obj_id); ++ ++ if (obj) ++ yaffs_update_oh(obj, NULL, 1, 0, 0, NULL); ++ ++ kfree(fixer); ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs1_scan ends"); ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.h linux-3.4.90/fs/yaffs2/yaffs_yaffs1.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_yaffs1.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,22 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS1_H__ ++#define __YAFFS_YAFFS1_H__ ++ ++#include "yaffs_guts.h" ++int yaffs1_scan(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.c linux-3.4.90/fs/yaffs2/yaffs_yaffs2.c +--- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_yaffs2.c 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,1534 @@ ++/* ++ * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "yaffs_guts.h" ++#include "yaffs_trace.h" ++#include "yaffs_yaffs2.h" ++#include "yaffs_checkptrw.h" ++#include "yaffs_bitmap.h" ++#include "yaffs_nand.h" ++#include "yaffs_getblockinfo.h" ++#include "yaffs_verify.h" ++#include "yaffs_attribs.h" ++#include "yaffs_summary.h" ++ ++/* ++ * Checkpoints are really no benefit on very small partitions. ++ * ++ * To save space on small partitions don't bother with checkpoints unless ++ * the partition is at least this big. ++ */ ++#define YAFFS_CHECKPOINT_MIN_BLOCKS 60 ++#define YAFFS_SMALL_HOLE_THRESHOLD 4 ++ ++/* ++ * Oldest Dirty Sequence Number handling. ++ */ ++ ++/* yaffs_calc_oldest_dirty_seq() ++ * yaffs2_find_oldest_dirty_seq() ++ * Calculate the oldest dirty sequence number if we don't know it. ++ */ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ int i; ++ unsigned seq; ++ unsigned block_no = 0; ++ struct yaffs_block_info *b; ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ /* Find the oldest dirty sequence number. */ ++ seq = dev->seq_number + 1; ++ b = dev->block_info; ++ for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) { ++ if (b->block_state == YAFFS_BLOCK_STATE_FULL && ++ (b->pages_in_use - b->soft_del_pages) < ++ dev->param.chunks_per_block && ++ b->seq_number < seq) { ++ seq = b->seq_number; ++ block_no = i; ++ } ++ b++; ++ } ++ ++ if (block_no) { ++ dev->oldest_dirty_seq = seq; ++ dev->oldest_dirty_block = block_no; ++ } ++} ++ ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!dev->oldest_dirty_seq) ++ yaffs_calc_oldest_dirty_seq(dev); ++} ++ ++/* ++ * yaffs_clear_oldest_dirty_seq() ++ * Called when a block is erased or marked bad. (ie. when its seq_number ++ * becomes invalid). If the value matches the oldest then we clear ++ * dev->oldest_dirty_seq to force its recomputation. ++ */ ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (!bi || bi->seq_number == dev->oldest_dirty_seq) { ++ dev->oldest_dirty_seq = 0; ++ dev->oldest_dirty_block = 0; ++ } ++} ++ ++/* ++ * yaffs2_update_oldest_dirty_seq() ++ * Update the oldest dirty sequence number whenever we dirty a block. ++ * Only do this if the oldest_dirty_seq is actually being tracked. ++ */ ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi) ++{ ++ if (!dev->param.is_yaffs2) ++ return; ++ ++ if (dev->oldest_dirty_seq) { ++ if (dev->oldest_dirty_seq > bi->seq_number) { ++ dev->oldest_dirty_seq = bi->seq_number; ++ dev->oldest_dirty_block = block_no; ++ } ++ } ++} ++ ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi) ++{ ++ ++ if (!dev->param.is_yaffs2) ++ return 1; /* disqualification only applies to yaffs2. */ ++ ++ if (!bi->has_shrink_hdr) ++ return 1; /* can gc */ ++ ++ yaffs2_find_oldest_dirty_seq(dev); ++ ++ /* Can't do gc of this block if there are any blocks older than this ++ * one that have discarded pages. ++ */ ++ return (bi->seq_number <= dev->oldest_dirty_seq); ++} ++ ++/* ++ * yaffs2_find_refresh_block() ++ * periodically finds the oldest full block by sequence number for refreshing. ++ * Only for yaffs2. ++ */ ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev) ++{ ++ u32 b; ++ u32 oldest = 0; ++ u32 oldest_seq = 0; ++ struct yaffs_block_info *bi; ++ ++ if (!dev->param.is_yaffs2) ++ return oldest; ++ ++ /* ++ * If refresh period < 10 then refreshing is disabled. ++ */ ++ if (dev->param.refresh_period < 10) ++ return oldest; ++ ++ /* ++ * Fix broken values. ++ */ ++ if (dev->refresh_skip > dev->param.refresh_period) ++ dev->refresh_skip = dev->param.refresh_period; ++ ++ if (dev->refresh_skip > 0) ++ return oldest; ++ ++ /* ++ * Refresh skip is now zero. ++ * We'll do a refresh this time around.... ++ * Update the refresh skip and find the oldest block. ++ */ ++ dev->refresh_skip = dev->param.refresh_period; ++ dev->refresh_count++; ++ bi = dev->block_info; ++ for (b = dev->internal_start_block; b <= dev->internal_end_block; b++) { ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ ++ if (oldest < 1 || bi->seq_number < oldest_seq) { ++ oldest = b; ++ oldest_seq = bi->seq_number; ++ } ++ } ++ bi++; ++ } ++ ++ if (oldest > 0) { ++ yaffs_trace(YAFFS_TRACE_GC, ++ "GC refresh count %d selected block %d with seq_number %d", ++ dev->refresh_count, oldest, oldest_seq); ++ } ++ ++ return oldest; ++} ++ ++int yaffs2_checkpt_required(struct yaffs_dev *dev) ++{ ++ int nblocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ nblocks = dev->internal_end_block - dev->internal_start_block + 1; ++ ++ return !dev->param.skip_checkpt_wr && ++ !dev->read_only && (nblocks >= YAFFS_CHECKPOINT_MIN_BLOCKS); ++} ++ ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev) ++{ ++ int retval; ++ int n_bytes = 0; ++ int n_blocks; ++ int dev_blocks; ++ ++ if (!dev->param.is_yaffs2) ++ return 0; ++ ++ if (!dev->checkpoint_blocks_required && yaffs2_checkpt_required(dev)) { ++ /* Not a valid value so recalculate */ ++ dev_blocks = dev->param.end_block - dev->param.start_block + 1; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(struct yaffs_checkpt_dev); ++ n_bytes += dev_blocks * sizeof(struct yaffs_block_info); ++ n_bytes += dev_blocks * dev->chunk_bit_stride; ++ n_bytes += ++ (sizeof(struct yaffs_checkpt_obj) + sizeof(u32)) * ++ dev->n_obj; ++ n_bytes += (dev->tnode_size + sizeof(u32)) * dev->n_tnodes; ++ n_bytes += sizeof(struct yaffs_checkpt_validity); ++ n_bytes += sizeof(u32); /* checksum */ ++ ++ /* Round up and add 2 blocks to allow for some bad blocks, ++ * so add 3 */ ++ ++ n_blocks = ++ (n_bytes / ++ (dev->data_bytes_per_chunk * ++ dev->param.chunks_per_block)) + 3; ++ ++ dev->checkpoint_blocks_required = n_blocks; ++ } ++ ++ retval = dev->checkpoint_blocks_required - dev->blocks_in_checkpt; ++ if (retval < 0) ++ retval = 0; ++ return retval; ++} ++ ++/*--------------------- Checkpointing --------------------*/ ++ ++static int yaffs2_wr_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ ++ memset(&cp, 0, sizeof(cp)); ++ ++ cp.struct_type = sizeof(cp); ++ cp.magic = YAFFS_MAGIC; ++ cp.version = YAFFS_CHECKPOINT_VERSION; ++ cp.head = (head) ? 1 : 0; ++ ++ return (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)) ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_validity_marker(struct yaffs_dev *dev, int head) ++{ ++ struct yaffs_checkpt_validity cp; ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ if (ok) ++ ok = (cp.struct_type == sizeof(cp)) && ++ (cp.magic == YAFFS_MAGIC) && ++ (cp.version == YAFFS_CHECKPOINT_VERSION) && ++ (cp.head == ((head) ? 1 : 0)); ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_dev_to_checkpt_dev(struct yaffs_checkpt_dev *cp, ++ struct yaffs_dev *dev) ++{ ++ cp->n_erased_blocks = dev->n_erased_blocks; ++ cp->alloc_block = dev->alloc_block; ++ cp->alloc_page = dev->alloc_page; ++ cp->n_free_chunks = dev->n_free_chunks; ++ ++ cp->n_deleted_files = dev->n_deleted_files; ++ cp->n_unlinked_files = dev->n_unlinked_files; ++ cp->n_bg_deletions = dev->n_bg_deletions; ++ cp->seq_number = dev->seq_number; ++ ++} ++ ++static void yaffs_checkpt_dev_to_dev(struct yaffs_dev *dev, ++ struct yaffs_checkpt_dev *cp) ++{ ++ dev->n_erased_blocks = cp->n_erased_blocks; ++ dev->alloc_block = cp->alloc_block; ++ dev->alloc_page = cp->alloc_page; ++ dev->n_free_chunks = cp->n_free_chunks; ++ ++ dev->n_deleted_files = cp->n_deleted_files; ++ dev->n_unlinked_files = cp->n_unlinked_files; ++ dev->n_bg_deletions = cp->n_bg_deletions; ++ dev->seq_number = cp->seq_number; ++} ++ ++static int yaffs2_wr_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ int ok; ++ ++ /* Write device runtime values */ ++ yaffs2_dev_to_checkpt_dev(&cp, dev); ++ cp.struct_type = sizeof(cp); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ /* Write block info */ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ok = (yaffs2_checkpt_wr(dev, dev->block_info, n_bytes) == n_bytes); ++ if (!ok) ++ return 0; ++ ++ /* Write chunk bits */ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ok = (yaffs2_checkpt_wr(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_dev(struct yaffs_dev *dev) ++{ ++ struct yaffs_checkpt_dev cp; ++ u32 n_bytes; ++ u32 n_blocks = ++ (dev->internal_end_block - dev->internal_start_block + 1); ++ int ok; ++ ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (!ok) ++ return 0; ++ ++ if (cp.struct_type != sizeof(cp)) ++ return 0; ++ ++ yaffs_checkpt_dev_to_dev(dev, &cp); ++ ++ n_bytes = n_blocks * sizeof(struct yaffs_block_info); ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->block_info, n_bytes) == n_bytes); ++ ++ if (!ok) ++ return 0; ++ ++ n_bytes = n_blocks * dev->chunk_bit_stride; ++ ++ ok = (yaffs2_checkpt_rd(dev, dev->chunk_bits, n_bytes) == n_bytes); ++ ++ return ok ? 1 : 0; ++} ++ ++static void yaffs2_obj_checkpt_obj(struct yaffs_checkpt_obj *cp, ++ struct yaffs_obj *obj) ++{ ++ cp->obj_id = obj->obj_id; ++ cp->parent_id = (obj->parent) ? obj->parent->obj_id : 0; ++ cp->hdr_chunk = obj->hdr_chunk; ++ cp->variant_type = obj->variant_type; ++ cp->deleted = obj->deleted; ++ cp->soft_del = obj->soft_del; ++ cp->unlinked = obj->unlinked; ++ cp->fake = obj->fake; ++ cp->rename_allowed = obj->rename_allowed; ++ cp->unlink_allowed = obj->unlink_allowed; ++ cp->serial = obj->serial; ++ cp->n_data_chunks = obj->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ cp->size_or_equiv_obj = obj->variant.file_variant.file_size; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ cp->size_or_equiv_obj = obj->variant.hardlink_variant.equiv_id; ++} ++ ++static int yaffs2_checkpt_obj_to_obj(struct yaffs_obj *obj, ++ struct yaffs_checkpt_obj *cp) ++{ ++ struct yaffs_obj *parent; ++ ++ if (obj->variant_type != cp->variant_type) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "Checkpoint read object %d type %d chunk %d does not match existing object type %d", ++ cp->obj_id, cp->variant_type, cp->hdr_chunk, ++ obj->variant_type); ++ return 0; ++ } ++ ++ obj->obj_id = cp->obj_id; ++ ++ if (cp->parent_id) ++ parent = yaffs_find_or_create_by_number(obj->my_dev, ++ cp->parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ else ++ parent = NULL; ++ ++ if (parent) { ++ if (parent->variant_type != YAFFS_OBJECT_TYPE_DIRECTORY) { ++ yaffs_trace(YAFFS_TRACE_ALWAYS, ++ "Checkpoint read object %d parent %d type %d chunk %d Parent type, %d, not directory", ++ cp->obj_id, cp->parent_id, ++ cp->variant_type, cp->hdr_chunk, ++ parent->variant_type); ++ return 0; ++ } ++ yaffs_add_obj_to_dir(parent, obj); ++ } ++ ++ obj->hdr_chunk = cp->hdr_chunk; ++ obj->variant_type = cp->variant_type; ++ obj->deleted = cp->deleted; ++ obj->soft_del = cp->soft_del; ++ obj->unlinked = cp->unlinked; ++ obj->fake = cp->fake; ++ obj->rename_allowed = cp->rename_allowed; ++ obj->unlink_allowed = cp->unlink_allowed; ++ obj->serial = cp->serial; ++ obj->n_data_chunks = cp->n_data_chunks; ++ ++ if (obj->variant_type == YAFFS_OBJECT_TYPE_FILE) ++ obj->variant.file_variant.file_size = cp->size_or_equiv_obj; ++ else if (obj->variant_type == YAFFS_OBJECT_TYPE_HARDLINK) ++ obj->variant.hardlink_variant.equiv_id = cp->size_or_equiv_obj; ++ ++ if (obj->hdr_chunk > 0) ++ obj->lazy_loaded = 1; ++ return 1; ++} ++ ++static int yaffs2_checkpt_tnode_worker(struct yaffs_obj *in, ++ struct yaffs_tnode *tn, u32 level, ++ int chunk_offset) ++{ ++ int i; ++ struct yaffs_dev *dev = in->my_dev; ++ int ok = 1; ++ u32 base_offset; ++ ++ if (!tn) ++ return 1; ++ ++ if (level > 0) { ++ for (i = 0; i < YAFFS_NTNODES_INTERNAL && ok; i++) { ++ if (!tn->internal[i]) ++ continue; ++ ok = yaffs2_checkpt_tnode_worker(in, ++ tn->internal[i], ++ level - 1, ++ (chunk_offset << ++ YAFFS_TNODES_INTERNAL_BITS) + i); ++ } ++ return ok; ++ } ++ ++ /* Level 0 tnode */ ++ base_offset = chunk_offset << YAFFS_TNODES_LEVEL0_BITS; ++ ok = (yaffs2_checkpt_wr(dev, &base_offset, sizeof(base_offset)) == ++ sizeof(base_offset)); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ ++ return ok; ++} ++ ++static int yaffs2_wr_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 end_marker = ~0; ++ int ok = 1; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return ok; ++ ++ ok = yaffs2_checkpt_tnode_worker(obj, ++ obj->variant.file_variant.top, ++ obj->variant.file_variant. ++ top_level, 0); ++ if (ok) ++ ok = (yaffs2_checkpt_wr(obj->my_dev, &end_marker, ++ sizeof(end_marker)) == sizeof(end_marker)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_tnodes(struct yaffs_obj *obj) ++{ ++ u32 base_chunk; ++ int ok = 1; ++ struct yaffs_dev *dev = obj->my_dev; ++ struct yaffs_file_var *file_stuct_ptr = &obj->variant.file_variant; ++ struct yaffs_tnode *tn; ++ int nread = 0; ++ ++ ok = (yaffs2_checkpt_rd(dev, &base_chunk, sizeof(base_chunk)) == ++ sizeof(base_chunk)); ++ ++ while (ok && (~base_chunk)) { ++ nread++; ++ /* Read level 0 tnode */ ++ ++ tn = yaffs_get_tnode(dev); ++ if (tn) ++ ok = (yaffs2_checkpt_rd(dev, tn, dev->tnode_size) == ++ dev->tnode_size); ++ else ++ ok = 0; ++ ++ if (tn && ok) ++ ok = yaffs_add_find_tnode_0(dev, ++ file_stuct_ptr, ++ base_chunk, tn) ? 1 : 0; ++ ++ if (ok) ++ ok = (yaffs2_checkpt_rd ++ (dev, &base_chunk, ++ sizeof(base_chunk)) == sizeof(base_chunk)); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read tnodes %d records, last %d. ok %d", ++ nread, base_chunk, ok); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int i; ++ int ok = 1; ++ struct list_head *lh; ++ ++ /* Iterate through the objects in each hash entry, ++ * dumping them to the checkpointing stream. ++ */ ++ ++ for (i = 0; ok && i < YAFFS_NOBJECT_BUCKETS; i++) { ++ list_for_each(lh, &dev->obj_bucket[i].list) { ++ obj = list_entry(lh, struct yaffs_obj, hash_link); ++ if (!obj->defered_free) { ++ yaffs2_obj_checkpt_obj(&cp, obj); ++ cp.struct_type = sizeof(cp); ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint write object %d parent %d type %d chunk %d obj addr %p", ++ cp.obj_id, cp.parent_id, ++ cp.variant_type, cp.hdr_chunk, obj); ++ ++ ok = (yaffs2_checkpt_wr(dev, &cp, ++ sizeof(cp)) == sizeof(cp)); ++ ++ if (ok && ++ obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) ++ ok = yaffs2_wr_checkpt_tnodes(obj); ++ } ++ } ++ } ++ ++ /* Dump end of list */ ++ memset(&cp, 0xff, sizeof(struct yaffs_checkpt_obj)); ++ cp.struct_type = sizeof(cp); ++ ++ if (ok) ++ ok = (yaffs2_checkpt_wr(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_rd_checkpt_objs(struct yaffs_dev *dev) ++{ ++ struct yaffs_obj *obj; ++ struct yaffs_checkpt_obj cp; ++ int ok = 1; ++ int done = 0; ++ LIST_HEAD(hard_list); ++ ++ ++ while (ok && !done) { ++ ok = (yaffs2_checkpt_rd(dev, &cp, sizeof(cp)) == sizeof(cp)); ++ if (cp.struct_type != sizeof(cp)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "struct size %d instead of %d ok %d", ++ cp.struct_type, (int)sizeof(cp), ok); ++ ok = 0; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "Checkpoint read object %d parent %d type %d chunk %d ", ++ cp.obj_id, cp.parent_id, cp.variant_type, ++ cp.hdr_chunk); ++ ++ if (ok && cp.obj_id == ~0) { ++ done = 1; ++ } else if (ok) { ++ obj = ++ yaffs_find_or_create_by_number(dev, cp.obj_id, ++ cp.variant_type); ++ if (obj) { ++ ok = yaffs2_checkpt_obj_to_obj(obj, &cp); ++ if (!ok) ++ break; ++ if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_FILE) { ++ ok = yaffs2_rd_checkpt_tnodes(obj); ++ } else if (obj->variant_type == ++ YAFFS_OBJECT_TYPE_HARDLINK) { ++ list_add(&obj->hard_links, &hard_list); ++ } ++ } else { ++ ok = 0; ++ } ++ } ++ } ++ ++ if (ok) ++ yaffs_link_fixup(dev, &hard_list); ++ ++ return ok ? 1 : 0; ++} ++ ++static int yaffs2_wr_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum); ++ ++ ok = (yaffs2_checkpt_wr(dev, &checkpt_sum, sizeof(checkpt_sum)) == ++ sizeof(checkpt_sum)); ++ ++ if (!ok) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_rd_checkpt_sum(struct yaffs_dev *dev) ++{ ++ u32 checkpt_sum0; ++ u32 checkpt_sum1; ++ int ok; ++ ++ yaffs2_get_checkpt_sum(dev, &checkpt_sum0); ++ ++ ok = (yaffs2_checkpt_rd(dev, &checkpt_sum1, sizeof(checkpt_sum1)) == ++ sizeof(checkpt_sum1)); ++ ++ if (!ok) ++ return 0; ++ ++ if (checkpt_sum0 != checkpt_sum1) ++ return 0; ++ ++ return 1; ++} ++ ++static int yaffs2_wr_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!yaffs2_checkpt_required(dev)) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint write"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 1); ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint device"); ++ ok = yaffs2_wr_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint objects"); ++ ok = yaffs2_wr_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "write checkpoint validity"); ++ ok = yaffs2_wr_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) ++ ok = yaffs2_wr_checkpt_sum(dev); ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return dev->is_checkpointed; ++} ++ ++static int yaffs2_rd_checkpt_data(struct yaffs_dev *dev) ++{ ++ int ok = 1; ++ ++ if (!dev->param.is_yaffs2) ++ ok = 0; ++ ++ if (ok && dev->param.skip_checkpt_rd) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "skipping checkpoint read"); ++ ok = 0; ++ } ++ ++ if (ok) ++ ok = yaffs2_checkpt_open(dev, 0); /* open for read */ ++ ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 1); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint device"); ++ ok = yaffs2_rd_checkpt_dev(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint objects"); ++ ok = yaffs2_rd_checkpt_objs(dev); ++ } ++ if (ok) { ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint validity"); ++ ok = yaffs2_rd_checkpt_validity_marker(dev, 0); ++ } ++ ++ if (ok) { ++ ok = yaffs2_rd_checkpt_sum(dev); ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "read checkpoint checksum %d", ok); ++ } ++ ++ if (!yaffs_checkpt_close(dev)) ++ ok = 0; ++ ++ if (ok) ++ dev->is_checkpointed = 1; ++ else ++ dev->is_checkpointed = 0; ++ ++ return ok ? 1 : 0; ++} ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev) ++{ ++ if (dev->is_checkpointed || dev->blocks_in_checkpt > 0) { ++ dev->is_checkpointed = 0; ++ yaffs2_checkpt_invalidate_stream(dev); ++ } ++ if (dev->param.sb_dirty_fn) ++ dev->param.sb_dirty_fn(dev); ++} ++ ++int yaffs_checkpoint_save(struct yaffs_dev *dev) ++{ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "save entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ ++ if (!dev->is_checkpointed) { ++ yaffs2_checkpt_invalidate(dev); ++ yaffs2_wr_checkpt_data(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT | YAFFS_TRACE_MOUNT, ++ "save exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return dev->is_checkpointed; ++} ++ ++int yaffs2_checkpt_restore(struct yaffs_dev *dev) ++{ ++ int retval; ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore entry: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ retval = yaffs2_rd_checkpt_data(dev); ++ ++ if (dev->is_checkpointed) { ++ yaffs_verify_objects(dev); ++ yaffs_verify_blocks(dev); ++ yaffs_verify_free_chunks(dev); ++ } ++ ++ yaffs_trace(YAFFS_TRACE_CHECKPOINT, ++ "restore exit: is_checkpointed %d", ++ dev->is_checkpointed); ++ ++ return retval; ++} ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size) ++{ ++ /* if new_size > old_file_size. ++ * We're going to be writing a hole. ++ * If the hole is small then write zeros otherwise write a start ++ * of hole marker. ++ */ ++ loff_t old_file_size; ++ loff_t increase; ++ int small_hole; ++ int result = YAFFS_OK; ++ struct yaffs_dev *dev = NULL; ++ u8 *local_buffer = NULL; ++ int small_increase_ok = 0; ++ ++ if (!obj) ++ return YAFFS_FAIL; ++ ++ if (obj->variant_type != YAFFS_OBJECT_TYPE_FILE) ++ return YAFFS_FAIL; ++ ++ dev = obj->my_dev; ++ ++ /* Bail out if not yaffs2 mode */ ++ if (!dev->param.is_yaffs2) ++ return YAFFS_OK; ++ ++ old_file_size = obj->variant.file_variant.file_size; ++ ++ if (new_size <= old_file_size) ++ return YAFFS_OK; ++ ++ increase = new_size - old_file_size; ++ ++ if (increase < YAFFS_SMALL_HOLE_THRESHOLD * dev->data_bytes_per_chunk && ++ yaffs_check_alloc_available(dev, YAFFS_SMALL_HOLE_THRESHOLD + 1)) ++ small_hole = 1; ++ else ++ small_hole = 0; ++ ++ if (small_hole) ++ local_buffer = yaffs_get_temp_buffer(dev); ++ ++ if (local_buffer) { ++ /* fill hole with zero bytes */ ++ loff_t pos = old_file_size; ++ int this_write; ++ int written; ++ memset(local_buffer, 0, dev->data_bytes_per_chunk); ++ small_increase_ok = 1; ++ ++ while (increase > 0 && small_increase_ok) { ++ this_write = increase; ++ if (this_write > dev->data_bytes_per_chunk) ++ this_write = dev->data_bytes_per_chunk; ++ written = ++ yaffs_do_file_wr(obj, local_buffer, pos, this_write, ++ 0); ++ if (written == this_write) { ++ pos += this_write; ++ increase -= this_write; ++ } else { ++ small_increase_ok = 0; ++ } ++ } ++ ++ yaffs_release_temp_buffer(dev, local_buffer); ++ ++ /* If out of space then reverse any chunks we've added */ ++ if (!small_increase_ok) ++ yaffs_resize_file_down(obj, old_file_size); ++ } ++ ++ if (!small_increase_ok && ++ obj->parent && ++ obj->parent->obj_id != YAFFS_OBJECTID_UNLINKED && ++ obj->parent->obj_id != YAFFS_OBJECTID_DELETED) { ++ /* Write a hole start header with the old file size */ ++ yaffs_update_oh(obj, NULL, 0, 1, 0, NULL); ++ } ++ ++ return result; ++} ++ ++struct yaffs_block_index { ++ int seq; ++ int block; ++}; ++ ++static int yaffs2_ybicmp(const void *a, const void *b) ++{ ++ int aseq = ((struct yaffs_block_index *)a)->seq; ++ int bseq = ((struct yaffs_block_index *)b)->seq; ++ int ablock = ((struct yaffs_block_index *)a)->block; ++ int bblock = ((struct yaffs_block_index *)b)->block; ++ ++ if (aseq == bseq) ++ return ablock - bblock; ++ ++ return aseq - bseq; ++} ++ ++static inline int yaffs2_scan_chunk(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi, ++ int blk, int chunk_in_block, ++ int *found_chunks, ++ u8 *chunk_data, ++ struct list_head *hard_list, ++ int summary_available) ++{ ++ struct yaffs_obj_hdr *oh; ++ struct yaffs_obj *in; ++ struct yaffs_obj *parent; ++ int equiv_id; ++ loff_t file_size; ++ int is_shrink; ++ int is_unlinked; ++ struct yaffs_ext_tags tags; ++ int result; ++ int alloc_failed = 0; ++ int chunk = blk * dev->param.chunks_per_block + chunk_in_block; ++ struct yaffs_file_var *file_var; ++ struct yaffs_hardlink_var *hl_var; ++ struct yaffs_symlink_var *sl_var; ++ ++ if (summary_available) { ++ result = yaffs_summary_fetch(dev, &tags, chunk_in_block); ++ tags.seq_number = bi->seq_number; ++ } ++ ++ if (!summary_available || tags.obj_id == 0) { ++ result = yaffs_rd_chunk_tags_nand(dev, chunk, NULL, &tags); ++ dev->tags_used++; ++ } else { ++ dev->summary_used++; ++ } ++ ++ /* Let's have a good look at this chunk... */ ++ ++ if (!tags.chunk_used) { ++ /* An unassigned chunk in the block. ++ * If there are used chunks after this one, then ++ * it is a chunk that was skipped due to failing ++ * the erased check. Just skip it so that it can ++ * be deleted. ++ * But, more typically, We get here when this is ++ * an unallocated chunk and his means that ++ * either the block is empty or this is the one ++ * being allocated from ++ */ ++ ++ if (*found_chunks) { ++ /* This is a chunk that was skipped due ++ * to failing the erased check */ ++ } else if (chunk_in_block == 0) { ++ /* We're looking at the first chunk in ++ * the block so the block is unused */ ++ bi->block_state = YAFFS_BLOCK_STATE_EMPTY; ++ dev->n_erased_blocks++; ++ } else { ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING) { ++ if (dev->seq_number == bi->seq_number) { ++ /* Allocating from this block*/ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Allocating from %d %d", ++ blk, chunk_in_block); ++ ++ bi->block_state = ++ YAFFS_BLOCK_STATE_ALLOCATING; ++ dev->alloc_block = blk; ++ dev->alloc_page = chunk_in_block; ++ dev->alloc_block_finder = blk; ++ } else { ++ /* This is a partially written block ++ * that is not the current ++ * allocation block. ++ */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Partially written block %d detected. gc will fix this.", ++ blk); ++ } ++ } ++ } ++ ++ dev->n_free_chunks++; ++ ++ } else if (tags.ecc_result == ++ YAFFS_ECC_RESULT_UNFIXED) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ " Unfixed ECC in chunk(%d:%d), chunk ignored", ++ blk, chunk_in_block); ++ dev->n_free_chunks++; ++ } else if (tags.obj_id > YAFFS_MAX_OBJECT_ID || ++ tags.chunk_id > YAFFS_MAX_CHUNK_ID || ++ tags.obj_id == YAFFS_OBJECTID_SUMMARY || ++ (tags.chunk_id > 0 && ++ tags.n_bytes > dev->data_bytes_per_chunk) || ++ tags.seq_number != bi->seq_number) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Chunk (%d:%d) with bad tags:obj = %d, chunk_id = %d, n_bytes = %d, ignored", ++ blk, chunk_in_block, tags.obj_id, ++ tags.chunk_id, tags.n_bytes); ++ dev->n_free_chunks++; ++ } else if (tags.chunk_id > 0) { ++ /* chunk_id > 0 so it is a data chunk... */ ++ loff_t endpos; ++ loff_t chunk_base = (tags.chunk_id - 1) * ++ dev->data_bytes_per_chunk; ++ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ YAFFS_OBJECT_TYPE_FILE); ++ if (!in) ++ /* Out of memory */ ++ alloc_failed = 1; ++ ++ if (in && ++ in->variant_type == YAFFS_OBJECT_TYPE_FILE && ++ chunk_base < in->variant.file_variant.shrink_size) { ++ /* This has not been invalidated by ++ * a resize */ ++ if (!yaffs_put_chunk_in_file(in, tags.chunk_id, ++ chunk, -1)) ++ alloc_failed = 1; ++ ++ /* File size is calculated by looking at ++ * the data chunks if we have not ++ * seen an object header yet. ++ * Stop this practice once we find an ++ * object header. ++ */ ++ endpos = chunk_base + tags.n_bytes; ++ ++ if (!in->valid && ++ in->variant.file_variant.scanned_size < endpos) { ++ in->variant.file_variant. ++ scanned_size = endpos; ++ in->variant.file_variant. ++ file_size = endpos; ++ } ++ } else if (in) { ++ /* This chunk has been invalidated by a ++ * resize, or a past file deletion ++ * so delete the chunk*/ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ } else { ++ /* chunk_id == 0, so it is an ObjectHeader. ++ * Thus, we read in the object header and make ++ * the object ++ */ ++ *found_chunks = 1; ++ ++ yaffs_set_chunk_bit(dev, blk, chunk_in_block); ++ bi->pages_in_use++; ++ ++ oh = NULL; ++ in = NULL; ++ ++ if (tags.extra_available) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, ++ tags.extra_obj_type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ ++ if (!in || ++ (!in->valid && dev->param.disable_lazy_load) || ++ tags.extra_shadows || ++ (!in->valid && (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND))) { ++ ++ /* If we don't have valid info then we ++ * need to read the chunk ++ * TODO In future we can probably defer ++ * reading the chunk and living with ++ * invalid data until needed. ++ */ ++ ++ result = yaffs_rd_chunk_tags_nand(dev, ++ chunk, ++ chunk_data, ++ NULL); ++ ++ oh = (struct yaffs_obj_hdr *)chunk_data; ++ ++ if (dev->param.inband_tags) { ++ /* Fix up the header if they got ++ * corrupted by inband tags */ ++ oh->shadows_obj = ++ oh->inband_shadowed_obj_id; ++ oh->is_shrink = ++ oh->inband_is_shrink; ++ } ++ ++ if (!in) { ++ in = yaffs_find_or_create_by_number(dev, ++ tags.obj_id, oh->type); ++ if (!in) ++ alloc_failed = 1; ++ } ++ } ++ ++ if (!in) { ++ /* TODO Hoosterman we have a problem! */ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Could not make object for object %d at chunk %d during scan", ++ tags.obj_id, chunk); ++ return YAFFS_FAIL; ++ } ++ ++ if (in->valid) { ++ /* We have already filled this one. ++ * We have a duplicate that will be ++ * discarded, but we first have to suck ++ * out resize info if it is a file. ++ */ ++ if ((in->variant_type == YAFFS_OBJECT_TYPE_FILE) && ++ ((oh && oh->type == YAFFS_OBJECT_TYPE_FILE) || ++ (tags.extra_available && ++ tags.extra_obj_type == YAFFS_OBJECT_TYPE_FILE) ++ )) { ++ loff_t this_size = (oh) ? ++ yaffs_oh_to_size(oh) : ++ tags.extra_file_size; ++ u32 parent_obj_id = (oh) ? ++ oh->parent_obj_id : ++ tags.extra_parent_id; ++ ++ is_shrink = (oh) ? ++ oh->is_shrink : ++ tags.extra_is_shrink; ++ ++ /* If it is deleted (unlinked ++ * at start also means deleted) ++ * we treat the file size as ++ * being zeroed at this point. ++ */ ++ if (parent_obj_id == YAFFS_OBJECTID_DELETED || ++ parent_obj_id == YAFFS_OBJECTID_UNLINKED) { ++ this_size = 0; ++ is_shrink = 1; ++ } ++ ++ if (is_shrink && ++ in->variant.file_variant.shrink_size > ++ this_size) ++ in->variant.file_variant.shrink_size = ++ this_size; ++ ++ if (is_shrink) ++ bi->has_shrink_hdr = 1; ++ } ++ /* Use existing - destroy this one. */ ++ yaffs_chunk_del(dev, chunk, 1, __LINE__); ++ } ++ ++ if (!in->valid && in->variant_type != ++ (oh ? oh->type : tags.extra_obj_type)) { ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: Bad type, %d != %d, for object %d at chunk %d during scan", ++ oh ? oh->type : tags.extra_obj_type, ++ in->variant_type, tags.obj_id, ++ chunk); ++ in = yaffs_retype_obj(in, oh ? oh->type : tags.extra_obj_type); ++ } ++ ++ if (!in->valid && ++ (tags.obj_id == YAFFS_OBJECTID_ROOT || ++ tags.obj_id == YAFFS_OBJECTID_LOSTNFOUND)) { ++ /* We only load some info, don't fiddle ++ * with directory structure */ ++ in->valid = 1; ++ ++ if (oh) { ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ in->lazy_loaded = 0; ++ } else { ++ in->lazy_loaded = 1; ++ } ++ in->hdr_chunk = chunk; ++ ++ } else if (!in->valid) { ++ /* we need to load this info */ ++ in->valid = 1; ++ in->hdr_chunk = chunk; ++ if (oh) { ++ in->variant_type = oh->type; ++ in->yst_mode = oh->yst_mode; ++ yaffs_load_attribs(in, oh); ++ ++ if (oh->shadows_obj > 0) ++ yaffs_handle_shadowed_obj(dev, ++ oh->shadows_obj, 1); ++ ++ yaffs_set_obj_name_from_oh(in, oh); ++ parent = yaffs_find_or_create_by_number(dev, ++ oh->parent_obj_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = yaffs_oh_to_size(oh); ++ is_shrink = oh->is_shrink; ++ equiv_id = oh->equiv_id; ++ } else { ++ in->variant_type = tags.extra_obj_type; ++ parent = yaffs_find_or_create_by_number(dev, ++ tags.extra_parent_id, ++ YAFFS_OBJECT_TYPE_DIRECTORY); ++ file_size = tags.extra_file_size; ++ is_shrink = tags.extra_is_shrink; ++ equiv_id = tags.extra_equiv_id; ++ in->lazy_loaded = 1; ++ } ++ in->dirty = 0; ++ ++ if (!parent) ++ alloc_failed = 1; ++ ++ /* directory stuff... ++ * hook up to parent ++ */ ++ ++ if (parent && ++ parent->variant_type == YAFFS_OBJECT_TYPE_UNKNOWN) { ++ /* Set up as a directory */ ++ parent->variant_type = ++ YAFFS_OBJECT_TYPE_DIRECTORY; ++ INIT_LIST_HEAD(&parent-> ++ variant.dir_variant.children); ++ } else if (!parent || ++ parent->variant_type != ++ YAFFS_OBJECT_TYPE_DIRECTORY) { ++ /* Hoosterman, another problem.... ++ * Trying to use a non-directory as a directory ++ */ ++ ++ yaffs_trace(YAFFS_TRACE_ERROR, ++ "yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." ++ ); ++ parent = dev->lost_n_found; ++ } ++ yaffs_add_obj_to_dir(parent, in); ++ ++ is_unlinked = (parent == dev->del_dir) || ++ (parent == dev->unlinked_dir); ++ ++ if (is_shrink) ++ /* Mark the block */ ++ bi->has_shrink_hdr = 1; ++ ++ /* Note re hardlinks. ++ * Since we might scan a hardlink before its equivalent ++ * object is scanned we put them all in a list. ++ * After scanning is complete, we should have all the ++ * objects, so we run through this list and fix up all ++ * the chains. ++ */ ++ ++ switch (in->variant_type) { ++ case YAFFS_OBJECT_TYPE_UNKNOWN: ++ /* Todo got a problem */ ++ break; ++ case YAFFS_OBJECT_TYPE_FILE: ++ file_var = &in->variant.file_variant; ++ if (file_var->scanned_size < file_size) { ++ /* This covers the case where the file ++ * size is greater than the data held. ++ * This will happen if the file is ++ * resized to be larger than its ++ * current data extents. ++ */ ++ file_var->file_size = file_size; ++ file_var->scanned_size = file_size; ++ } ++ ++ if (file_var->shrink_size > file_size) ++ file_var->shrink_size = file_size; ++ ++ break; ++ case YAFFS_OBJECT_TYPE_HARDLINK: ++ hl_var = &in->variant.hardlink_variant; ++ if (!is_unlinked) { ++ hl_var->equiv_id = equiv_id; ++ list_add(&in->hard_links, hard_list); ++ } ++ break; ++ case YAFFS_OBJECT_TYPE_DIRECTORY: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SPECIAL: ++ /* Do nothing */ ++ break; ++ case YAFFS_OBJECT_TYPE_SYMLINK: ++ sl_var = &in->variant.symlink_variant; ++ if (oh) { ++ sl_var->alias = ++ yaffs_clone_str(oh->alias); ++ if (!sl_var->alias) ++ alloc_failed = 1; ++ } ++ break; ++ } ++ } ++ } ++ return alloc_failed ? YAFFS_FAIL : YAFFS_OK; ++} ++ ++int yaffs2_scan_backwards(struct yaffs_dev *dev) ++{ ++ int blk; ++ int block_iter; ++ int start_iter; ++ int end_iter; ++ int n_to_scan = 0; ++ enum yaffs_block_state state; ++ int c; ++ int deleted; ++ LIST_HEAD(hard_list); ++ struct yaffs_block_info *bi; ++ u32 seq_number; ++ int n_blocks = dev->internal_end_block - dev->internal_start_block + 1; ++ u8 *chunk_data; ++ int found_chunks; ++ int alloc_failed = 0; ++ struct yaffs_block_index *block_index = NULL; ++ int alt_block_index = 0; ++ int summary_available; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards starts intstartblk %d intendblk %d...", ++ dev->internal_start_block, dev->internal_end_block); ++ ++ dev->seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER; ++ ++ block_index = ++ kmalloc(n_blocks * sizeof(struct yaffs_block_index), GFP_NOFS); ++ ++ if (!block_index) { ++ block_index = ++ vmalloc(n_blocks * sizeof(struct yaffs_block_index)); ++ alt_block_index = 1; ++ } ++ ++ if (!block_index) { ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "yaffs2_scan_backwards() could not allocate block index!" ++ ); ++ return YAFFS_FAIL; ++ } ++ ++ dev->blocks_in_checkpt = 0; ++ ++ chunk_data = yaffs_get_temp_buffer(dev); ++ ++ /* Scan all the blocks to determine their state */ ++ bi = dev->block_info; ++ for (blk = dev->internal_start_block; blk <= dev->internal_end_block; ++ blk++) { ++ yaffs_clear_chunk_bits(dev, blk); ++ bi->pages_in_use = 0; ++ bi->soft_del_pages = 0; ++ ++ yaffs_query_init_block_state(dev, blk, &state, &seq_number); ++ ++ bi->block_state = state; ++ bi->seq_number = seq_number; ++ ++ if (bi->seq_number == YAFFS_SEQUENCE_CHECKPOINT_DATA) ++ bi->block_state = YAFFS_BLOCK_STATE_CHECKPOINT; ++ if (bi->seq_number == YAFFS_SEQUENCE_BAD_BLOCK) ++ bi->block_state = YAFFS_BLOCK_STATE_DEAD; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, ++ "Block scanning block %d state %d seq %d", ++ blk, bi->block_state, seq_number); ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_CHECKPOINT) { ++ dev->blocks_in_checkpt++; ++ ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_DEAD) { ++ yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, ++ "block %d is bad", blk); ++ } else if (bi->block_state == YAFFS_BLOCK_STATE_EMPTY) { ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "Block empty "); ++ dev->n_erased_blocks++; ++ dev->n_free_chunks += dev->param.chunks_per_block; ++ } else if (bi->block_state == ++ YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* Determine the highest sequence number */ ++ if (seq_number >= YAFFS_LOWEST_SEQUENCE_NUMBER && ++ seq_number < YAFFS_HIGHEST_SEQUENCE_NUMBER) { ++ block_index[n_to_scan].seq = seq_number; ++ block_index[n_to_scan].block = blk; ++ n_to_scan++; ++ if (seq_number >= dev->seq_number) ++ dev->seq_number = seq_number; ++ } else { ++ /* TODO: Nasty sequence number! */ ++ yaffs_trace(YAFFS_TRACE_SCAN, ++ "Block scanning block %d has bad sequence number %d", ++ blk, seq_number); ++ } ++ } ++ bi++; ++ } ++ ++ yaffs_trace(YAFFS_TRACE_ALWAYS, "%d blocks to be sorted...", n_to_scan); ++ ++ cond_resched(); ++ ++ /* Sort the blocks by sequence number */ ++ sort(block_index, n_to_scan, sizeof(struct yaffs_block_index), ++ yaffs2_ybicmp, NULL); ++ ++ cond_resched(); ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "...done"); ++ ++ /* Now scan the blocks looking at the data. */ ++ start_iter = 0; ++ end_iter = n_to_scan - 1; ++ yaffs_trace(YAFFS_TRACE_SCAN_DEBUG, "%d blocks to scan", n_to_scan); ++ ++ /* For each block.... backwards */ ++ for (block_iter = end_iter; ++ !alloc_failed && block_iter >= start_iter; ++ block_iter--) { ++ /* Cooperative multitasking! This loop can run for so ++ long that watchdog timers expire. */ ++ cond_resched(); ++ ++ /* get the block to scan in the correct order */ ++ blk = block_index[block_iter].block; ++ bi = yaffs_get_block_info(dev, blk); ++ deleted = 0; ++ ++ summary_available = yaffs_summary_read(dev, dev->sum_tags, blk); ++ ++ /* For each chunk in each block that needs scanning.... */ ++ found_chunks = 0; ++ if (summary_available) ++ c = dev->chunks_per_summary - 1; ++ else ++ c = dev->param.chunks_per_block - 1; ++ ++ for (/* c is already initialised */; ++ !alloc_failed && c >= 0 && ++ (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN || ++ bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING); ++ c--) { ++ /* Scan backwards... ++ * Read the tags and decide what to do ++ */ ++ if (yaffs2_scan_chunk(dev, bi, blk, c, ++ &found_chunks, chunk_data, ++ &hard_list, summary_available) == ++ YAFFS_FAIL) ++ alloc_failed = 1; ++ } ++ ++ if (bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN) { ++ /* If we got this far while scanning, then the block ++ * is fully allocated. */ ++ bi->block_state = YAFFS_BLOCK_STATE_FULL; ++ } ++ ++ /* Now let's see if it was dirty */ ++ if (bi->pages_in_use == 0 && ++ !bi->has_shrink_hdr && ++ bi->block_state == YAFFS_BLOCK_STATE_FULL) { ++ yaffs_block_became_dirty(dev, blk); ++ } ++ } ++ ++ yaffs_skip_rest_of_block(dev); ++ ++ if (alt_block_index) ++ vfree(block_index); ++ else ++ kfree(block_index); ++ ++ /* Ok, we've done all the scanning. ++ * Fix up the hard link chains. ++ * We have scanned all the objects, now it's time to add these ++ * hardlinks. ++ */ ++ yaffs_link_fixup(dev, &hard_list); ++ ++ yaffs_release_temp_buffer(dev, chunk_data); ++ ++ if (alloc_failed) ++ return YAFFS_FAIL; ++ ++ yaffs_trace(YAFFS_TRACE_SCAN, "yaffs2_scan_backwards ends"); ++ ++ return YAFFS_OK; ++} +diff -Nur linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.h linux-3.4.90/fs/yaffs2/yaffs_yaffs2.h +--- linux-3.4.90.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yaffs_yaffs2.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YAFFS_YAFFS2_H__ ++#define __YAFFS_YAFFS2_H__ ++ ++#include "yaffs_guts.h" ++ ++void yaffs_calc_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_find_oldest_dirty_seq(struct yaffs_dev *dev); ++void yaffs2_clear_oldest_dirty_seq(struct yaffs_dev *dev, ++ struct yaffs_block_info *bi); ++void yaffs2_update_oldest_dirty_seq(struct yaffs_dev *dev, unsigned block_no, ++ struct yaffs_block_info *bi); ++int yaffs_block_ok_for_gc(struct yaffs_dev *dev, struct yaffs_block_info *bi); ++u32 yaffs2_find_refresh_block(struct yaffs_dev *dev); ++int yaffs2_checkpt_required(struct yaffs_dev *dev); ++int yaffs_calc_checkpt_blocks_required(struct yaffs_dev *dev); ++ ++void yaffs2_checkpt_invalidate(struct yaffs_dev *dev); ++int yaffs2_checkpt_save(struct yaffs_dev *dev); ++int yaffs2_checkpt_restore(struct yaffs_dev *dev); ++ ++int yaffs2_handle_hole(struct yaffs_obj *obj, loff_t new_size); ++int yaffs2_scan_backwards(struct yaffs_dev *dev); ++ ++#endif +diff -Nur linux-3.4.90.orig/fs/yaffs2/yportenv.h linux-3.4.90/fs/yaffs2/yportenv.h +--- linux-3.4.90.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.4.90/fs/yaffs2/yportenv.h 2014-05-17 15:08:09.000000000 +0200 +@@ -0,0 +1,85 @@ ++/* ++ * YAFFS: Yet another Flash File System . A NAND-flash specific file system. ++ * ++ * Copyright (C) 2002-2011 Aleph One Ltd. ++ * for Toby Churchill Ltd and Brightstar Engineering ++ * ++ * Created by Charles Manning ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License version 2.1 as ++ * published by the Free Software Foundation. ++ * ++ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL. ++ */ ++ ++#ifndef __YPORTENV_H__ ++#define __YPORTENV_H__ ++ ++/* ++ * Define the MTD version in terms of Linux Kernel versions ++ * This allows yaffs to be used independantly of the kernel ++ * as well as with it. ++ */ ++ ++#define MTD_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) ++ ++#ifdef YAFFS_OUT_OF_TREE ++#include "moduleconfig.h" ++#endif ++ ++#include ++#define MTD_VERSION_CODE LINUX_VERSION_CODE ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* These type wrappings are used to support Unicode names in WinCE. */ ++#define YCHAR char ++#define YUCHAR unsigned char ++#define _Y(x) x ++ ++#define YAFFS_LOSTNFOUND_NAME "lost+found" ++#define YAFFS_LOSTNFOUND_PREFIX "obj" ++ ++ ++#define YAFFS_ROOT_MODE 0755 ++#define YAFFS_LOSTNFOUND_MODE 0700 ++ ++#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)) ++#define Y_CURRENT_TIME CURRENT_TIME.tv_sec ++#define Y_TIME_CONVERT(x) (x).tv_sec ++#else ++#define Y_CURRENT_TIME CURRENT_TIME ++#define Y_TIME_CONVERT(x) (x) ++#endif ++ ++#define compile_time_assertion(assertion) \ ++ ({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; }) ++ ++ ++#define yaffs_printf(msk, fmt, ...) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__) ++ ++#define yaffs_trace(msk, fmt, ...) do { \ ++ if (yaffs_trace_mask & (msk)) \ ++ printk(KERN_DEBUG "yaffs: " fmt "\n", ##__VA_ARGS__); \ ++} while (0) ++ ++ ++#endif diff --git a/target/linux/patches/3.4.108/zlib-inflate.patch b/target/linux/patches/3.4.108/zlib-inflate.patch new file mode 100644 index 000000000..58e1f6d21 --- /dev/null +++ b/target/linux/patches/3.4.108/zlib-inflate.patch @@ -0,0 +1,12 @@ +diff -Nur linux-2.6.37.orig/lib/Kconfig linux-2.6.37/lib/Kconfig +--- linux-2.6.37.orig/lib/Kconfig 2011-01-05 01:50:19.000000000 +0100 ++++ linux-2.6.37/lib/Kconfig 2011-03-01 20:10:29.833370667 +0100 +@@ -95,7 +95,7 @@ + # compression support is select'ed if needed + # + config ZLIB_INFLATE +- tristate ++ boolean + + config ZLIB_DEFLATE + tristate diff --git a/target/linux/patches/4.0.5/aufs4.patch b/target/linux/patches/4.0.5/aufs4.patch deleted file mode 100644 index 73b035010..000000000 --- a/target/linux/patches/4.0.5/aufs4.patch +++ /dev/null @@ -1,33819 +0,0 @@ -diff -Nur linux-4.0.4.orig/Documentation/ABI/testing/debugfs-aufs linux-4.0.4/Documentation/ABI/testing/debugfs-aufs ---- linux-4.0.4.orig/Documentation/ABI/testing/debugfs-aufs 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/ABI/testing/debugfs-aufs 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,50 @@ -+What: /debug/aufs/si_/ -+Date: March 2009 -+Contact: J. R. Okajima -+Description: -+ Under /debug/aufs, a directory named si_ is created -+ per aufs mount, where is a unique id generated -+ internally. -+ -+What: /debug/aufs/si_/plink -+Date: Apr 2013 -+Contact: J. R. Okajima -+Description: -+ It has three lines and shows the information about the -+ pseudo-link. The first line is a single number -+ representing a number of buckets. The second line is a -+ number of pseudo-links per buckets (separated by a -+ blank). The last line is a single number representing a -+ total number of psedo-links. -+ When the aufs mount option 'noplink' is specified, it -+ will show "1\n0\n0\n". -+ -+What: /debug/aufs/si_/xib -+Date: March 2009 -+Contact: J. R. Okajima -+Description: -+ It shows the consumed blocks by xib (External Inode Number -+ Bitmap), its block size and file size. -+ When the aufs mount option 'noxino' is specified, it -+ will be empty. About XINO files, see the aufs manual. -+ -+What: /debug/aufs/si_/xino0, xino1 ... xinoN -+Date: March 2009 -+Contact: J. R. Okajima -+Description: -+ It shows the consumed blocks by xino (External Inode Number -+ Translation Table), its link count, block size and file -+ size. -+ When the aufs mount option 'noxino' is specified, it -+ will be empty. About XINO files, see the aufs manual. -+ -+What: /debug/aufs/si_/xigen -+Date: March 2009 -+Contact: J. R. Okajima -+Description: -+ It shows the consumed blocks by xigen (External Inode -+ Generation Table), its block size and file size. -+ If CONFIG_AUFS_EXPORT is disabled, this entry will not -+ be created. -+ When the aufs mount option 'noxino' is specified, it -+ will be empty. About XINO files, see the aufs manual. -diff -Nur linux-4.0.4.orig/Documentation/ABI/testing/sysfs-aufs linux-4.0.4/Documentation/ABI/testing/sysfs-aufs ---- linux-4.0.4.orig/Documentation/ABI/testing/sysfs-aufs 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/ABI/testing/sysfs-aufs 2015-05-30 22:11:29.000000000 +0200 -@@ -0,0 +1,31 @@ -+What: /sys/fs/aufs/si_/ -+Date: March 2009 -+Contact: J. R. Okajima -+Description: -+ Under /sys/fs/aufs, a directory named si_ is created -+ per aufs mount, where is a unique id generated -+ internally. -+ -+What: /sys/fs/aufs/si_/br0, br1 ... brN -+Date: March 2009 -+Contact: J. R. Okajima -+Description: -+ It shows the abolute path of a member directory (which -+ is called branch) in aufs, and its permission. -+ -+What: /sys/fs/aufs/si_/brid0, brid1 ... bridN -+Date: July 2013 -+Contact: J. R. Okajima -+Description: -+ It shows the id of a member directory (which is called -+ branch) in aufs. -+ -+What: /sys/fs/aufs/si_/xi_path -+Date: March 2009 -+Contact: J. R. Okajima -+Description: -+ It shows the abolute path of XINO (External Inode Number -+ Bitmap, Translation Table and Generation Table) file -+ even if it is the default path. -+ When the aufs mount option 'noxino' is specified, it -+ will be empty. About XINO files, see the aufs manual. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/01intro.txt linux-4.0.4/Documentation/filesystems/aufs/design/01intro.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/01intro.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/01intro.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,157 @@ -+ -+# Copyright (C) 2005-2015 Junjiro R. Okajima -+ -+Introduction -+---------------------------------------- -+ -+aufs [ei ju: ef es] | [a u f s] -+1. abbrev. for "advanced multi-layered unification filesystem". -+2. abbrev. for "another unionfs". -+3. abbrev. for "auf das" in German which means "on the" in English. -+ Ex. "Butter aufs Brot"(G) means "butter onto bread"(E). -+ But "Filesystem aufs Filesystem" is hard to understand. -+ -+AUFS is a filesystem with features: -+- multi layered stackable unification filesystem, the member directory -+ is called as a branch. -+- branch permission and attribute, 'readonly', 'real-readonly', -+ 'readwrite', 'whiteout-able', 'link-able whiteout', etc. and their -+ combination. -+- internal "file copy-on-write". -+- logical deletion, whiteout. -+- dynamic branch manipulation, adding, deleting and changing permission. -+- allow bypassing aufs, user's direct branch access. -+- external inode number translation table and bitmap which maintains the -+ persistent aufs inode number. -+- seekable directory, including NFS readdir. -+- file mapping, mmap and sharing pages. -+- pseudo-link, hardlink over branches. -+- loopback mounted filesystem as a branch. -+- several policies to select one among multiple writable branches. -+- revert a single systemcall when an error occurs in aufs. -+- and more... -+ -+ -+Multi Layered Stackable Unification Filesystem -+---------------------------------------------------------------------- -+Most people already knows what it is. -+It is a filesystem which unifies several directories and provides a -+merged single directory. When users access a file, the access will be -+passed/re-directed/converted (sorry, I am not sure which English word is -+correct) to the real file on the member filesystem. The member -+filesystem is called 'lower filesystem' or 'branch' and has a mode -+'readonly' and 'readwrite.' And the deletion for a file on the lower -+readonly branch is handled by creating 'whiteout' on the upper writable -+branch. -+ -+On LKML, there have been discussions about UnionMount (Jan Blunck, -+Bharata B Rao and Valerie Aurora) and Unionfs (Erez Zadok). They took -+different approaches to implement the merged-view. -+The former tries putting it into VFS, and the latter implements as a -+separate filesystem. -+(If I misunderstand about these implementations, please let me know and -+I shall correct it. Because it is a long time ago when I read their -+source files last time). -+ -+UnionMount's approach will be able to small, but may be hard to share -+branches between several UnionMount since the whiteout in it is -+implemented in the inode on branch filesystem and always -+shared. According to Bharata's post, readdir does not seems to be -+finished yet. -+There are several missing features known in this implementations such as -+- for users, the inode number may change silently. eg. copy-up. -+- link(2) may break by copy-up. -+- read(2) may get an obsoleted filedata (fstat(2) too). -+- fcntl(F_SETLK) may be broken by copy-up. -+- unnecessary copy-up may happen, for example mmap(MAP_PRIVATE) after -+ open(O_RDWR). -+ -+In linux-3.18, "overlay" filesystem (formerly known as "overlayfs") was -+merged into mainline. This is another implementation of UnionMount as a -+separated filesystem. All the limitations and known problems which -+UnionMount are equally inherited to "overlay" filesystem. -+ -+Unionfs has a longer history. When I started implementing a stackable -+filesystem (Aug 2005), it already existed. It has virtual super_block, -+inode, dentry and file objects and they have an array pointing lower -+same kind objects. After contributing many patches for Unionfs, I -+re-started my project AUFS (Jun 2006). -+ -+In AUFS, the structure of filesystem resembles to Unionfs, but I -+implemented my own ideas, approaches and enhancements and it became -+totally different one. -+ -+Comparing DM snapshot and fs based implementation -+- the number of bytes to be copied between devices is much smaller. -+- the type of filesystem must be one and only. -+- the fs must be writable, no readonly fs, even for the lower original -+ device. so the compression fs will not be usable. but if we use -+ loopback mount, we may address this issue. -+ for instance, -+ mount /cdrom/squashfs.img /sq -+ losetup /sq/ext2.img -+ losetup /somewhere/cow -+ dmsetup "snapshot /dev/loop0 /dev/loop1 ..." -+- it will be difficult (or needs more operations) to extract the -+ difference between the original device and COW. -+- DM snapshot-merge may help a lot when users try merging. in the -+ fs-layer union, users will use rsync(1). -+ -+You may want to read my old paper "Filesystems in LiveCD" -+(http://aufs.sourceforge.net/aufs2/report/sq/sq.pdf). -+ -+ -+Several characters/aspects/persona of aufs -+---------------------------------------------------------------------- -+ -+Aufs has several characters, aspects or persona. -+1. a filesystem, callee of VFS helper -+2. sub-VFS, caller of VFS helper for branches -+3. a virtual filesystem which maintains persistent inode number -+4. reader/writer of files on branches such like an application -+ -+1. Callee of VFS Helper -+As an ordinary linux filesystem, aufs is a callee of VFS. For instance, -+unlink(2) from an application reaches sys_unlink() kernel function and -+then vfs_unlink() is called. vfs_unlink() is one of VFS helper and it -+calls filesystem specific unlink operation. Actually aufs implements the -+unlink operation but it behaves like a redirector. -+ -+2. Caller of VFS Helper for Branches -+aufs_unlink() passes the unlink request to the branch filesystem as if -+it were called from VFS. So the called unlink operation of the branch -+filesystem acts as usual. As a caller of VFS helper, aufs should handle -+every necessary pre/post operation for the branch filesystem. -+- acquire the lock for the parent dir on a branch -+- lookup in a branch -+- revalidate dentry on a branch -+- mnt_want_write() for a branch -+- vfs_unlink() for a branch -+- mnt_drop_write() for a branch -+- release the lock on a branch -+ -+3. Persistent Inode Number -+One of the most important issue for a filesystem is to maintain inode -+numbers. This is particularly important to support exporting a -+filesystem via NFS. Aufs is a virtual filesystem which doesn't have a -+backend block device for its own. But some storage is necessary to -+keep and maintain the inode numbers. It may be a large space and may not -+suit to keep in memory. Aufs rents some space from its first writable -+branch filesystem (by default) and creates file(s) on it. These files -+are created by aufs internally and removed soon (currently) keeping -+opened. -+Note: Because these files are removed, they are totally gone after -+ unmounting aufs. It means the inode numbers are not persistent -+ across unmount or reboot. I have a plan to make them really -+ persistent which will be important for aufs on NFS server. -+ -+4. Read/Write Files Internally (copy-on-write) -+Because a branch can be readonly, when you write a file on it, aufs will -+"copy-up" it to the upper writable branch internally. And then write the -+originally requested thing to the file. Generally kernel doesn't -+open/read/write file actively. In aufs, even a single write may cause a -+internal "file copy". This behaviour is very similar to cp(1) command. -+ -+Some people may think it is better to pass such work to user space -+helper, instead of doing in kernel space. Actually I am still thinking -+about it. But currently I have implemented it in kernel space. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/02struct.txt linux-4.0.4/Documentation/filesystems/aufs/design/02struct.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/02struct.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/02struct.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,245 @@ -+ -+# Copyright (C) 2005-2015 Junjiro R. Okajima -+ -+Basic Aufs Internal Structure -+ -+Superblock/Inode/Dentry/File Objects -+---------------------------------------------------------------------- -+As like an ordinary filesystem, aufs has its own -+superblock/inode/dentry/file objects. All these objects have a -+dynamically allocated array and store the same kind of pointers to the -+lower filesystem, branch. -+For example, when you build a union with one readwrite branch and one -+readonly, mounted /au, /rw and /ro respectively. -+- /au = /rw + /ro -+- /ro/fileA exists but /rw/fileA -+ -+Aufs lookup operation finds /ro/fileA and gets dentry for that. These -+pointers are stored in a aufs dentry. The array in aufs dentry will be, -+- [0] = NULL (because /rw/fileA doesn't exist) -+- [1] = /ro/fileA -+ -+This style of an array is essentially same to the aufs -+superblock/inode/dentry/file objects. -+ -+Because aufs supports manipulating branches, ie. add/delete/change -+branches dynamically, these objects has its own generation. When -+branches are changed, the generation in aufs superblock is -+incremented. And a generation in other object are compared when it is -+accessed. When a generation in other objects are obsoleted, aufs -+refreshes the internal array. -+ -+ -+Superblock -+---------------------------------------------------------------------- -+Additionally aufs superblock has some data for policies to select one -+among multiple writable branches, XIB files, pseudo-links and kobject. -+See below in detail. -+About the policies which supports copy-down a directory, see -+wbr_policy.txt too. -+ -+ -+Branch and XINO(External Inode Number Translation Table) -+---------------------------------------------------------------------- -+Every branch has its own xino (external inode number translation table) -+file. The xino file is created and unlinked by aufs internally. When two -+members of a union exist on the same filesystem, they share the single -+xino file. -+The struct of a xino file is simple, just a sequence of aufs inode -+numbers which is indexed by the lower inode number. -+In the above sample, assume the inode number of /ro/fileA is i111 and -+aufs assigns the inode number i999 for fileA. Then aufs writes 999 as -+4(8) bytes at 111 * 4(8) bytes offset in the xino file. -+ -+When the inode numbers are not contiguous, the xino file will be sparse -+which has a hole in it and doesn't consume as much disk space as it -+might appear. If your branch filesystem consumes disk space for such -+holes, then you should specify 'xino=' option at mounting aufs. -+ -+Aufs has a mount option to free the disk blocks for such holes in XINO -+files on tmpfs or ramdisk. But it is not so effective actually. If you -+meet a problem of disk shortage due to XINO files, then you should try -+"tmpfs-ino.patch" (and "vfs-ino.patch" too) in aufs4-standalone.git. -+The patch localizes the assignment inumbers per tmpfs-mount and avoid -+the holes in XINO files. -+ -+Also a writable branch has three kinds of "whiteout bases". All these -+are existed when the branch is joined to aufs, and their names are -+whiteout-ed doubly, so that users will never see their names in aufs -+hierarchy. -+1. a regular file which will be hardlinked to all whiteouts. -+2. a directory to store a pseudo-link. -+3. a directory to store an "orphan"-ed file temporary. -+ -+1. Whiteout Base -+ When you remove a file on a readonly branch, aufs handles it as a -+ logical deletion and creates a whiteout on the upper writable branch -+ as a hardlink of this file in order not to consume inode on the -+ writable branch. -+2. Pseudo-link Dir -+ See below, Pseudo-link. -+3. Step-Parent Dir -+ When "fileC" exists on the lower readonly branch only and it is -+ opened and removed with its parent dir, and then user writes -+ something into it, then aufs copies-up fileC to this -+ directory. Because there is no other dir to store fileC. After -+ creating a file under this dir, the file is unlinked. -+ -+Because aufs supports manipulating branches, ie. add/delete/change -+dynamically, a branch has its own id. When the branch order changes, -+aufs finds the new index by searching the branch id. -+ -+ -+Pseudo-link -+---------------------------------------------------------------------- -+Assume "fileA" exists on the lower readonly branch only and it is -+hardlinked to "fileB" on the branch. When you write something to fileA, -+aufs copies-up it to the upper writable branch. Additionally aufs -+creates a hardlink under the Pseudo-link Directory of the writable -+branch. The inode of a pseudo-link is kept in aufs super_block as a -+simple list. If fileB is read after unlinking fileA, aufs returns -+filedata from the pseudo-link instead of the lower readonly -+branch. Because the pseudo-link is based upon the inode, to keep the -+inode number by xino (see above) is essentially necessary. -+ -+All the hardlinks under the Pseudo-link Directory of the writable branch -+should be restored in a proper location later. Aufs provides a utility -+to do this. The userspace helpers executed at remounting and unmounting -+aufs by default. -+During this utility is running, it puts aufs into the pseudo-link -+maintenance mode. In this mode, only the process which began the -+maintenance mode (and its child processes) is allowed to operate in -+aufs. Some other processes which are not related to the pseudo-link will -+be allowed to run too, but the rest have to return an error or wait -+until the maintenance mode ends. If a process already acquires an inode -+mutex (in VFS), it has to return an error. -+ -+ -+XIB(external inode number bitmap) -+---------------------------------------------------------------------- -+Addition to the xino file per a branch, aufs has an external inode number -+bitmap in a superblock object. It is also an internal file such like a -+xino file. -+It is a simple bitmap to mark whether the aufs inode number is in-use or -+not. -+To reduce the file I/O, aufs prepares a single memory page to cache xib. -+ -+As well as XINO files, aufs has a feature to truncate/refresh XIB to -+reduce the number of consumed disk blocks for these files. -+ -+ -+Virtual or Vertical Dir, and Readdir in Userspace -+---------------------------------------------------------------------- -+In order to support multiple layers (branches), aufs readdir operation -+constructs a virtual dir block on memory. For readdir, aufs calls -+vfs_readdir() internally for each dir on branches, merges their entries -+with eliminating the whiteout-ed ones, and sets it to file (dir) -+object. So the file object has its entry list until it is closed. The -+entry list will be updated when the file position is zero and becomes -+obsoleted. This decision is made in aufs automatically. -+ -+The dynamically allocated memory block for the name of entries has a -+unit of 512 bytes (by default) and stores the names contiguously (no -+padding). Another block for each entry is handled by kmem_cache too. -+During building dir blocks, aufs creates hash list and judging whether -+the entry is whiteouted by its upper branch or already listed. -+The merged result is cached in the corresponding inode object and -+maintained by a customizable life-time option. -+ -+Some people may call it can be a security hole or invite DoS attack -+since the opened and once readdir-ed dir (file object) holds its entry -+list and becomes a pressure for system memory. But I'd say it is similar -+to files under /proc or /sys. The virtual files in them also holds a -+memory page (generally) while they are opened. When an idea to reduce -+memory for them is introduced, it will be applied to aufs too. -+For those who really hate this situation, I've developed readdir(3) -+library which operates this merging in userspace. You just need to set -+LD_PRELOAD environment variable, and aufs will not consume no memory in -+kernel space for readdir(3). -+ -+ -+Workqueue -+---------------------------------------------------------------------- -+Aufs sometimes requires privilege access to a branch. For instance, -+in copy-up/down operation. When a user process is going to make changes -+to a file which exists in the lower readonly branch only, and the mode -+of one of ancestor directories may not be writable by a user -+process. Here aufs copy-up the file with its ancestors and they may -+require privilege to set its owner/group/mode/etc. -+This is a typical case of a application character of aufs (see -+Introduction). -+ -+Aufs uses workqueue synchronously for this case. It creates its own -+workqueue. The workqueue is a kernel thread and has privilege. Aufs -+passes the request to call mkdir or write (for example), and wait for -+its completion. This approach solves a problem of a signal handler -+simply. -+If aufs didn't adopt the workqueue and changed the privilege of the -+process, then the process may receive the unexpected SIGXFSZ or other -+signals. -+ -+Also aufs uses the system global workqueue ("events" kernel thread) too -+for asynchronous tasks, such like handling inotify/fsnotify, re-creating a -+whiteout base and etc. This is unrelated to a privilege. -+Most of aufs operation tries acquiring a rw_semaphore for aufs -+superblock at the beginning, at the same time waits for the completion -+of all queued asynchronous tasks. -+ -+ -+Whiteout -+---------------------------------------------------------------------- -+The whiteout in aufs is very similar to Unionfs's. That is represented -+by its filename. UnionMount takes an approach of a file mode, but I am -+afraid several utilities (find(1) or something) will have to support it. -+ -+Basically the whiteout represents "logical deletion" which stops aufs to -+lookup further, but also it represents "dir is opaque" which also stop -+further lookup. -+ -+In aufs, rmdir(2) and rename(2) for dir uses whiteout alternatively. -+In order to make several functions in a single systemcall to be -+revertible, aufs adopts an approach to rename a directory to a temporary -+unique whiteouted name. -+For example, in rename(2) dir where the target dir already existed, aufs -+renames the target dir to a temporary unique whiteouted name before the -+actual rename on a branch, and then handles other actions (make it opaque, -+update the attributes, etc). If an error happens in these actions, aufs -+simply renames the whiteouted name back and returns an error. If all are -+succeeded, aufs registers a function to remove the whiteouted unique -+temporary name completely and asynchronously to the system global -+workqueue. -+ -+ -+Copy-up -+---------------------------------------------------------------------- -+It is a well-known feature or concept. -+When user modifies a file on a readonly branch, aufs operate "copy-up" -+internally and makes change to the new file on the upper writable branch. -+When the trigger systemcall does not update the timestamps of the parent -+dir, aufs reverts it after copy-up. -+ -+ -+Move-down (aufs3.9 and later) -+---------------------------------------------------------------------- -+"Copy-up" is one of the essential feature in aufs. It copies a file from -+the lower readonly branch to the upper writable branch when a user -+changes something about the file. -+"Move-down" is an opposite action of copy-up. Basically this action is -+ran manually instead of automatically and internally. -+For desgin and implementation, aufs has to consider these issues. -+- whiteout for the file may exist on the lower branch. -+- ancestor directories may not exist on the lower branch. -+- diropq for the ancestor directories may exist on the upper branch. -+- free space on the lower branch will reduce. -+- another access to the file may happen during moving-down, including -+ UDBA (see "Revalidate Dentry and UDBA"). -+- the file should not be hard-linked nor pseudo-linked. they should be -+ handled by auplink utility later. -+ -+Sometimes users want to move-down a file from the upper writable branch -+to the lower readonly or writable branch. For instance, -+- the free space of the upper writable branch is going to run out. -+- create a new intermediate branch between the upper and lower branch. -+- etc. -+ -+For this purpose, use "aumvdown" command in aufs-util.git. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/03atomic_open.txt linux-4.0.4/Documentation/filesystems/aufs/design/03atomic_open.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/03atomic_open.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/03atomic_open.txt 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,72 @@ -+ -+# Copyright (C) 2015 Junjiro R. Okajima -+ -+Support for a branch who has its ->atomic_open() -+---------------------------------------------------------------------- -+The filesystems who implement its ->atomic_open() are not majority. For -+example NFSv4 does, and aufs should call NFSv4 ->atomic_open, -+particularly for open(O_CREAT|O_EXCL, 0400) case. Other than -+->atomic_open(), NFSv4 returns an error for this open(2). While I am not -+sure whether all filesystems who have ->atomic_open() behave like this, -+but NFSv4 surely returns the error. -+ -+In order to support ->atomic_open() for aufs, there are a few -+approaches. -+ -+A. Introduce aufs_atomic_open() -+ - calls one of VFS:do_last(), lookup_open() or atomic_open() for -+ branch fs. -+B. Introduce aufs_atomic_open() calling create, open and chmod. this is -+ an aufs user Pip Cet's approach -+ - calls aufs_create(), VFS finish_open() and notify_change(). -+ - pass fake-mode to finish_open(), and then correct the mode by -+ notify_change(). -+C. Extend aufs_open() to call branch fs's ->atomic_open() -+ - no aufs_atomic_open(). -+ - aufs_lookup() registers the TID to an aufs internal object. -+ - aufs_create() does nothing when the matching TID is registered, but -+ registers the mode. -+ - aufs_open() calls branch fs's ->atomic_open() when the matching -+ TID is registered. -+D. Extend aufs_open() to re-try branch fs's ->open() with superuser's -+ credential -+ - no aufs_atomic_open(). -+ - aufs_create() registers the TID to an internal object. this info -+ represents "this process created this file just now." -+ - when aufs gets EACCES from branch fs's ->open(), then confirm the -+ registered TID and re-try open() with superuser's credential. -+ -+Pros and cons for each approach. -+ -+A. -+ - straightforward but highly depends upon VFS internal. -+ - the atomic behavaiour is kept. -+ - some of parameters such as nameidata are hard to reproduce for -+ branch fs. -+ - large overhead. -+B. -+ - easy to implement. -+ - the atomic behavaiour is lost. -+C. -+ - the atomic behavaiour is kept. -+ - dirty and tricky. -+ - VFS checks whether the file is created correctly after calling -+ ->create(), which means this approach doesn't work. -+D. -+ - easy to implement. -+ - the atomic behavaiour is lost. -+ - to open a file with superuser's credential and give it to a user -+ process is a bad idea, since the file object keeps the credential -+ in it. It may affect LSM or something. This approach doesn't work -+ either. -+ -+The approach A is ideal, but it hard to implement. So here is a -+variation of A, which is to be implemented. -+ -+A-1. Introduce aufs_atomic_open() -+ - calls branch fs ->atomic_open() if exists. otherwise calls -+ vfs_create() and finish_open(). -+ - the demerit is that the several checks after branch fs -+ ->atomic_open() are lost. in the ordinary case, the checks are -+ done by VFS:do_last(), lookup_open() and atomic_open(). some can -+ be implemented in aufs, but not all I am afraid. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/03lookup.txt linux-4.0.4/Documentation/filesystems/aufs/design/03lookup.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/03lookup.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/03lookup.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,100 @@ -+ -+# Copyright (C) 2005-2015 Junjiro R. Okajima -+ -+Lookup in a Branch -+---------------------------------------------------------------------- -+Since aufs has a character of sub-VFS (see Introduction), it operates -+lookup for branches as VFS does. It may be a heavy work. But almost all -+lookup operation in aufs is the simplest case, ie. lookup only an entry -+directly connected to its parent. Digging down the directory hierarchy -+is unnecessary. VFS has a function lookup_one_len() for that use, and -+aufs calls it. -+ -+When a branch is a remote filesystem, aufs basically relies upon its -+->d_revalidate(), also aufs forces the hardest revalidate tests for -+them. -+For d_revalidate, aufs implements three levels of revalidate tests. See -+"Revalidate Dentry and UDBA" in detail. -+ -+ -+Test Only the Highest One for the Directory Permission (dirperm1 option) -+---------------------------------------------------------------------- -+Let's try case study. -+- aufs has two branches, upper readwrite and lower readonly. -+ /au = /rw + /ro -+- "dirA" exists under /ro, but /rw. and its mode is 0700. -+- user invoked "chmod a+rx /au/dirA" -+- the internal copy-up is activated and "/rw/dirA" is created and its -+ permission bits are set to world readable. -+- then "/au/dirA" becomes world readable? -+ -+In this case, /ro/dirA is still 0700 since it exists in readonly branch, -+or it may be a natively readonly filesystem. If aufs respects the lower -+branch, it should not respond readdir request from other users. But user -+allowed it by chmod. Should really aufs rejects showing the entries -+under /ro/dirA? -+ -+To be honest, I don't have a good solution for this case. So aufs -+implements 'dirperm1' and 'nodirperm1' mount options, and leave it to -+users. -+When dirperm1 is specified, aufs checks only the highest one for the -+directory permission, and shows the entries. Otherwise, as usual, checks -+every dir existing on all branches and rejects the request. -+ -+As a side effect, dirperm1 option improves the performance of aufs -+because the number of permission check is reduced when the number of -+branch is many. -+ -+ -+Revalidate Dentry and UDBA (User's Direct Branch Access) -+---------------------------------------------------------------------- -+Generally VFS helpers re-validate a dentry as a part of lookup. -+0. digging down the directory hierarchy. -+1. lock the parent dir by its i_mutex. -+2. lookup the final (child) entry. -+3. revalidate it. -+4. call the actual operation (create, unlink, etc.) -+5. unlock the parent dir -+ -+If the filesystem implements its ->d_revalidate() (step 3), then it is -+called. Actually aufs implements it and checks the dentry on a branch is -+still valid. -+But it is not enough. Because aufs has to release the lock for the -+parent dir on a branch at the end of ->lookup() (step 2) and -+->d_revalidate() (step 3) while the i_mutex of the aufs dir is still -+held by VFS. -+If the file on a branch is changed directly, eg. bypassing aufs, after -+aufs released the lock, then the subsequent operation may cause -+something unpleasant result. -+ -+This situation is a result of VFS architecture, ->lookup() and -+->d_revalidate() is separated. But I never say it is wrong. It is a good -+design from VFS's point of view. It is just not suitable for sub-VFS -+character in aufs. -+ -+Aufs supports such case by three level of revalidation which is -+selectable by user. -+1. Simple Revalidate -+ Addition to the native flow in VFS's, confirm the child-parent -+ relationship on the branch just after locking the parent dir on the -+ branch in the "actual operation" (step 4). When this validation -+ fails, aufs returns EBUSY. ->d_revalidate() (step 3) in aufs still -+ checks the validation of the dentry on branches. -+2. Monitor Changes Internally by Inotify/Fsnotify -+ Addition to above, in the "actual operation" (step 4) aufs re-lookup -+ the dentry on the branch, and returns EBUSY if it finds different -+ dentry. -+ Additionally, aufs sets the inotify/fsnotify watch for every dir on branches -+ during it is in cache. When the event is notified, aufs registers a -+ function to kernel 'events' thread by schedule_work(). And the -+ function sets some special status to the cached aufs dentry and inode -+ private data. If they are not cached, then aufs has nothing to -+ do. When the same file is accessed through aufs (step 0-3) later, -+ aufs will detect the status and refresh all necessary data. -+ In this mode, aufs has to ignore the event which is fired by aufs -+ itself. -+3. No Extra Validation -+ This is the simplest test and doesn't add any additional revalidation -+ test, and skip the revalidation in step 4. It is useful and improves -+ aufs performance when system surely hide the aufs branches from user, -+ by over-mounting something (or another method). -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/04branch.txt linux-4.0.4/Documentation/filesystems/aufs/design/04branch.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/04branch.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/04branch.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,61 @@ -+ -+# Copyright (C) 2005-2015 Junjiro R. Okajima -+ -+Branch Manipulation -+ -+Since aufs supports dynamic branch manipulation, ie. add/remove a branch -+and changing its permission/attribute, there are a lot of works to do. -+ -+ -+Add a Branch -+---------------------------------------------------------------------- -+o Confirm the adding dir exists outside of aufs, including loopback -+ mount, and its various attributes. -+o Initialize the xino file and whiteout bases if necessary. -+ See struct.txt. -+ -+o Check the owner/group/mode of the directory -+ When the owner/group/mode of the adding directory differs from the -+ existing branch, aufs issues a warning because it may impose a -+ security risk. -+ For example, when a upper writable branch has a world writable empty -+ top directory, a malicious user can create any files on the writable -+ branch directly, like copy-up and modify manually. If something like -+ /etc/{passwd,shadow} exists on the lower readonly branch but the upper -+ writable branch, and the writable branch is world-writable, then a -+ malicious guy may create /etc/passwd on the writable branch directly -+ and the infected file will be valid in aufs. -+ I am afraid it can be a security issue, but aufs can do nothing except -+ producing a warning. -+ -+ -+Delete a Branch -+---------------------------------------------------------------------- -+o Confirm the deleting branch is not busy -+ To be general, there is one merit to adopt "remount" interface to -+ manipulate branches. It is to discard caches. At deleting a branch, -+ aufs checks the still cached (and connected) dentries and inodes. If -+ there are any, then they are all in-use. An inode without its -+ corresponding dentry can be alive alone (for example, inotify/fsnotify case). -+ -+ For the cached one, aufs checks whether the same named entry exists on -+ other branches. -+ If the cached one is a directory, because aufs provides a merged view -+ to users, as long as one dir is left on any branch aufs can show the -+ dir to users. In this case, the branch can be removed from aufs. -+ Otherwise aufs rejects deleting the branch. -+ -+ If any file on the deleting branch is opened by aufs, then aufs -+ rejects deleting. -+ -+ -+Modify the Permission of a Branch -+---------------------------------------------------------------------- -+o Re-initialize or remove the xino file and whiteout bases if necessary. -+ See struct.txt. -+ -+o rw --> ro: Confirm the modifying branch is not busy -+ Aufs rejects the request if any of these conditions are true. -+ - a file on the branch is mmap-ed. -+ - a regular file on the branch is opened for write and there is no -+ same named entry on the upper branch. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/05wbr_policy.txt linux-4.0.4/Documentation/filesystems/aufs/design/05wbr_policy.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/05wbr_policy.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/05wbr_policy.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,51 @@ -+ -+# Copyright (C) 2005-2015 Junjiro R. Okajima -+ -+Policies to Select One among Multiple Writable Branches -+---------------------------------------------------------------------- -+When the number of writable branch is more than one, aufs has to decide -+the target branch for file creation or copy-up. By default, the highest -+writable branch which has the parent (or ancestor) dir of the target -+file is chosen (top-down-parent policy). -+By user's request, aufs implements some other policies to select the -+writable branch, for file creation several policies, round-robin, -+most-free-space, and other policies. For copy-up, top-down-parent, -+bottom-up-parent, bottom-up and others. -+ -+As expected, the round-robin policy selects the branch in circular. When -+you have two writable branches and creates 10 new files, 5 files will be -+created for each branch. mkdir(2) systemcall is an exception. When you -+create 10 new directories, all will be created on the same branch. -+And the most-free-space policy selects the one which has most free -+space among the writable branches. The amount of free space will be -+checked by aufs internally, and users can specify its time interval. -+ -+The policies for copy-up is more simple, -+top-down-parent is equivalent to the same named on in create policy, -+bottom-up-parent selects the writable branch where the parent dir -+exists and the nearest upper one from the copyup-source, -+bottom-up selects the nearest upper writable branch from the -+copyup-source, regardless the existence of the parent dir. -+ -+There are some rules or exceptions to apply these policies. -+- If there is a readonly branch above the policy-selected branch and -+ the parent dir is marked as opaque (a variation of whiteout), or the -+ target (creating) file is whiteout-ed on the upper readonly branch, -+ then the result of the policy is ignored and the target file will be -+ created on the nearest upper writable branch than the readonly branch. -+- If there is a writable branch above the policy-selected branch and -+ the parent dir is marked as opaque or the target file is whiteouted -+ on the branch, then the result of the policy is ignored and the target -+ file will be created on the highest one among the upper writable -+ branches who has diropq or whiteout. In case of whiteout, aufs removes -+ it as usual. -+- link(2) and rename(2) systemcalls are exceptions in every policy. -+ They try selecting the branch where the source exists as possible -+ since copyup a large file will take long time. If it can't be, -+ ie. the branch where the source exists is readonly, then they will -+ follow the copyup policy. -+- There is an exception for rename(2) when the target exists. -+ If the rename target exists, aufs compares the index of the branches -+ where the source and the target exists and selects the higher -+ one. If the selected branch is readonly, then aufs follows the -+ copyup policy. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/06fhsm.txt linux-4.0.4/Documentation/filesystems/aufs/design/06fhsm.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/06fhsm.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/06fhsm.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,105 @@ -+ -+# Copyright (C) 2011-2015 Junjiro R. Okajima -+ -+File-based Hierarchical Storage Management (FHSM) -+---------------------------------------------------------------------- -+Hierarchical Storage Management (or HSM) is a well-known feature in the -+storage world. Aufs provides this feature as file-based with multiple -+writable branches, based upon the principle of "Colder, the Lower". -+Here the word "colder" means that the less used files, and "lower" means -+that the position in the order of the stacked branches vertically. -+These multiple writable branches are prioritized, ie. the topmost one -+should be the fastest drive and be used heavily. -+ -+o Characters in aufs FHSM story -+- aufs itself and a new branch attribute. -+- a new ioctl interface to move-down and to establish a connection with -+ the daemon ("move-down" is a converse of "copy-up"). -+- userspace tool and daemon. -+ -+The userspace daemon establishes a connection with aufs and waits for -+the notification. The notified information is very similar to struct -+statfs containing the number of consumed blocks and inodes. -+When the consumed blocks/inodes of a branch exceeds the user-specified -+upper watermark, the daemon activates its move-down process until the -+consumed blocks/inodes reaches the user-specified lower watermark. -+ -+The actual move-down is done by aufs based upon the request from -+user-space since we need to maintain the inode number and the internal -+pointer arrays in aufs. -+ -+Currently aufs FHSM handles the regular files only. Additionally they -+must not be hard-linked nor pseudo-linked. -+ -+ -+o Cowork of aufs and the user-space daemon -+ During the userspace daemon established the connection, aufs sends a -+ small notification to it whenever aufs writes something into the -+ writable branch. But it may cost high since aufs issues statfs(2) -+ internally. So user can specify a new option to cache the -+ info. Actually the notification is controlled by these factors. -+ + the specified cache time. -+ + classified as "force" by aufs internally. -+ Until the specified time expires, aufs doesn't send the info -+ except the forced cases. When aufs decide forcing, the info is always -+ notified to userspace. -+ For example, the number of free inodes is generally large enough and -+ the shortage of it happens rarely. So aufs doesn't force the -+ notification when creating a new file, directory and others. This is -+ the typical case which aufs doesn't force. -+ When aufs writes the actual filedata and the files consumes any of new -+ blocks, the aufs forces notifying. -+ -+ -+o Interfaces in aufs -+- New branch attribute. -+ + fhsm -+ Specifies that the branch is managed by FHSM feature. In other word, -+ participant in the FHSM. -+ When nofhsm is set to the branch, it will not be the source/target -+ branch of the move-down operation. This attribute is set -+ independently from coo and moo attributes, and if you want full -+ FHSM, you should specify them as well. -+- New mount option. -+ + fhsm_sec -+ Specifies a second to suppress many less important info to be -+ notified. -+- New ioctl. -+ + AUFS_CTL_FHSM_FD -+ create a new file descriptor which userspace can read the notification -+ (a subset of struct statfs) from aufs. -+- Module parameter 'brs' -+ It has to be set to 1. Otherwise the new mount option 'fhsm' will not -+ be set. -+- mount helpers /sbin/mount.aufs and /sbin/umount.aufs -+ When there are two or more branches with fhsm attributes, -+ /sbin/mount.aufs invokes the user-space daemon and /sbin/umount.aufs -+ terminates it. As a result of remounting and branch-manipulation, the -+ number of branches with fhsm attribute can be one. In this case, -+ /sbin/mount.aufs will terminate the user-space daemon. -+ -+ -+Finally the operation is done as these steps in kernel-space. -+- make sure that, -+ + no one else is using the file. -+ + the file is not hard-linked. -+ + the file is not pseudo-linked. -+ + the file is a regular file. -+ + the parent dir is not opaqued. -+- find the target writable branch. -+- make sure the file is not whiteout-ed by the upper (than the target) -+ branch. -+- make the parent dir on the target branch. -+- mutex lock the inode on the branch. -+- unlink the whiteout on the target branch (if exists). -+- lookup and create the whiteout-ed temporary name on the target branch. -+- copy the file as the whiteout-ed temporary name on the target branch. -+- rename the whiteout-ed temporary name to the original name. -+- unlink the file on the source branch. -+- maintain the internal pointer array and the external inode number -+ table (XINO). -+- maintain the timestamps and other attributes of the parent dir and the -+ file. -+ -+And of course, in every step, an error may happen. So the operation -+should restore the original file state after an error happens. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/06mmap.txt linux-4.0.4/Documentation/filesystems/aufs/design/06mmap.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/06mmap.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/06mmap.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,33 @@ -+ -+# Copyright (C) 2005-2015 Junjiro R. Okajima -+ -+mmap(2) -- File Memory Mapping -+---------------------------------------------------------------------- -+In aufs, the file-mapped pages are handled by a branch fs directly, no -+interaction with aufs. It means aufs_mmap() calls the branch fs's -+->mmap(). -+This approach is simple and good, but there is one problem. -+Under /proc, several entries show the mmapped files by its path (with -+device and inode number), and the printed path will be the path on the -+branch fs's instead of virtual aufs's. -+This is not a problem in most cases, but some utilities lsof(1) (and its -+user) may expect the path on aufs. -+ -+To address this issue, aufs adds a new member called vm_prfile in struct -+vm_area_struct (and struct vm_region). The original vm_file points to -+the file on the branch fs in order to handle everything correctly as -+usual. The new vm_prfile points to a virtual file in aufs, and the -+show-functions in procfs refers to vm_prfile if it is set. -+Also we need to maintain several other places where touching vm_file -+such like -+- fork()/clone() copies vma and the reference count of vm_file is -+ incremented. -+- merging vma maintains the ref count too. -+ -+This is not a good approach. It just fakes the printed path. But it -+leaves all behaviour around f_mapping unchanged. This is surely an -+advantage. -+Actually aufs had adopted another complicated approach which calls -+generic_file_mmap() and handles struct vm_operations_struct. In this -+approach, aufs met a hard problem and I could not solve it without -+switching the approach. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/06xattr.txt linux-4.0.4/Documentation/filesystems/aufs/design/06xattr.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/06xattr.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/06xattr.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,81 @@ -+ -+# Copyright (C) 2014-2015 Junjiro R. Okajima -+ -+Listing XATTR/EA and getting the value -+---------------------------------------------------------------------- -+For the inode standard attributes (owner, group, timestamps, etc.), aufs -+shows the values from the topmost existing file. This behaviour is good -+for the non-dir entries since the bahaviour exactly matches the shown -+information. But for the directories, aufs considers all the same named -+entries on the lower branches. Which means, if one of the lower entry -+rejects readdir call, then aufs returns an error even if the topmost -+entry allows it. This behaviour is necessary to respect the branch fs's -+security, but can make users confused since the user-visible standard -+attributes don't match the behaviour. -+To address this issue, aufs has a mount option called dirperm1 which -+checks the permission for the topmost entry only, and ignores the lower -+entry's permission. -+ -+A similar issue can happen around XATTR. -+getxattr(2) and listxattr(2) families behave as if dirperm1 option is -+always set. Otherwise these very unpleasant situation would happen. -+- listxattr(2) may return the duplicated entries. -+- users may not be able to remove or reset the XATTR forever, -+ -+ -+XATTR/EA support in the internal (copy,move)-(up,down) -+---------------------------------------------------------------------- -+Generally the extended attributes of inode are categorized as these. -+- "security" for LSM and capability. -+- "system" for posix ACL, 'acl' mount option is required for the branch -+ fs generally. -+- "trusted" for userspace, CAP_SYS_ADMIN is required. -+- "user" for userspace, 'user_xattr' mount option is required for the -+ branch fs generally. -+ -+Moreover there are some other categories. Aufs handles these rather -+unpopular categories as the ordinary ones, ie. there is no special -+condition nor exception. -+ -+In copy-up, the support for XATTR on the dst branch may differ from the -+src branch. In this case, the copy-up operation will get an error and -+the original user operation which triggered the copy-up will fail. It -+can happen that even all copy-up will fail. -+When both of src and dst branches support XATTR and if an error occurs -+during copying XATTR, then the copy-up should fail obviously. That is a -+good reason and aufs should return an error to userspace. But when only -+the src branch support that XATTR, aufs should not return an error. -+For example, the src branch supports ACL but the dst branch doesn't -+because the dst branch may natively un-support it or temporary -+un-support it due to "noacl" mount option. Of course, the dst branch fs -+may NOT return an error even if the XATTR is not supported. It is -+totally up to the branch fs. -+ -+Anyway when the aufs internal copy-up gets an error from the dst branch -+fs, then aufs tries removing the just copied entry and returns the error -+to the userspace. The worst case of this situation will be all copy-up -+will fail. -+ -+For the copy-up operation, there two basic approaches. -+- copy the specified XATTR only (by category above), and return the -+ error unconditionally if it happens. -+- copy all XATTR, and ignore the error on the specified category only. -+ -+In order to support XATTR and to implement the correct behaviour, aufs -+chooses the latter approach and introduces some new branch attributes, -+"icexsec", "icexsys", "icextr", "icexusr", and "icexoth". -+They correspond to the XATTR namespaces (see above). Additionally, to be -+convenient, "icex" is also provided which means all "icex*" attributes -+are set (here the word "icex" stands for "ignore copy-error on XATTR"). -+ -+The meaning of these attributes is to ignore the error from setting -+XATTR on that branch. -+Note that aufs tries copying all XATTR unconditionally, and ignores the -+error from the dst branch according to the specified attributes. -+ -+Some XATTR may have its default value. The default value may come from -+the parent dir or the environment. If the default value is set at the -+file creating-time, it will be overwritten by copy-up. -+Some contradiction may happen I am afraid. -+Do we need another attribute to stop copying XATTR? I am unsure. For -+now, aufs implements the branch attributes to ignore the error. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/07export.txt linux-4.0.4/Documentation/filesystems/aufs/design/07export.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/07export.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/07export.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,45 @@ -+ -+# Copyright (C) 2005-2015 Junjiro R. Okajima -+ -+Export Aufs via NFS -+---------------------------------------------------------------------- -+Here is an approach. -+- like xino/xib, add a new file 'xigen' which stores aufs inode -+ generation. -+- iget_locked(): initialize aufs inode generation for a new inode, and -+ store it in xigen file. -+- destroy_inode(): increment aufs inode generation and store it in xigen -+ file. it is necessary even if it is not unlinked, because any data of -+ inode may be changed by UDBA. -+- encode_fh(): for a root dir, simply return FILEID_ROOT. otherwise -+ build file handle by -+ + branch id (4 bytes) -+ + superblock generation (4 bytes) -+ + inode number (4 or 8 bytes) -+ + parent dir inode number (4 or 8 bytes) -+ + inode generation (4 bytes)) -+ + return value of exportfs_encode_fh() for the parent on a branch (4 -+ bytes) -+ + file handle for a branch (by exportfs_encode_fh()) -+- fh_to_dentry(): -+ + find the index of a branch from its id in handle, and check it is -+ still exist in aufs. -+ + 1st level: get the inode number from handle and search it in cache. -+ + 2nd level: if not found in cache, get the parent inode number from -+ the handle and search it in cache. and then open the found parent -+ dir, find the matching inode number by vfs_readdir() and get its -+ name, and call lookup_one_len() for the target dentry. -+ + 3rd level: if the parent dir is not cached, call -+ exportfs_decode_fh() for a branch and get the parent on a branch, -+ build a pathname of it, convert it a pathname in aufs, call -+ path_lookup(). now aufs gets a parent dir dentry, then handle it as -+ the 2nd level. -+ + to open the dir, aufs needs struct vfsmount. aufs keeps vfsmount -+ for every branch, but not itself. to get this, (currently) aufs -+ searches in current->nsproxy->mnt_ns list. it may not be a good -+ idea, but I didn't get other approach. -+ + test the generation of the gotten inode. -+- every inode operation: they may get EBUSY due to UDBA. in this case, -+ convert it into ESTALE for NFSD. -+- readdir(): call lockdep_on/off() because filldir in NFSD calls -+ lookup_one_len(), vfs_getattr(), encode_fh() and others. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/08shwh.txt linux-4.0.4/Documentation/filesystems/aufs/design/08shwh.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/08shwh.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/08shwh.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,39 @@ -+ -+# Copyright (C) 2005-2015 Junjiro R. Okajima -+ -+Show Whiteout Mode (shwh) -+---------------------------------------------------------------------- -+Generally aufs hides the name of whiteouts. But in some cases, to show -+them is very useful for users. For instance, creating a new middle layer -+(branch) by merging existing layers. -+ -+(borrowing aufs1 HOW-TO from a user, Michael Towers) -+When you have three branches, -+- Bottom: 'system', squashfs (underlying base system), read-only -+- Middle: 'mods', squashfs, read-only -+- Top: 'overlay', ram (tmpfs), read-write -+ -+The top layer is loaded at boot time and saved at shutdown, to preserve -+the changes made to the system during the session. -+When larger changes have been made, or smaller changes have accumulated, -+the size of the saved top layer data grows. At this point, it would be -+nice to be able to merge the two overlay branches ('mods' and 'overlay') -+and rewrite the 'mods' squashfs, clearing the top layer and thus -+restoring save and load speed. -+ -+This merging is simplified by the use of another aufs mount, of just the -+two overlay branches using the 'shwh' option. -+# mount -t aufs -o ro,shwh,br:/livesys/overlay=ro+wh:/livesys/mods=rr+wh \ -+ aufs /livesys/merge_union -+ -+A merged view of these two branches is then available at -+/livesys/merge_union, and the new feature is that the whiteouts are -+visible! -+Note that in 'shwh' mode the aufs mount must be 'ro', which will disable -+writing to all branches. Also the default mode for all branches is 'ro'. -+It is now possible to save the combined contents of the two overlay -+branches to a new squashfs, e.g.: -+# mksquashfs /livesys/merge_union /path/to/newmods.squash -+ -+This new squashfs archive can be stored on the boot device and the -+initramfs will use it to replace the old one at the next boot. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/10dynop.txt linux-4.0.4/Documentation/filesystems/aufs/design/10dynop.txt ---- linux-4.0.4.orig/Documentation/filesystems/aufs/design/10dynop.txt 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/design/10dynop.txt 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,34 @@ -+ -+# Copyright (C) 2010-2015 Junjiro R. Okajima -+ -+Dynamically customizable FS operations -+---------------------------------------------------------------------- -+Generally FS operations (struct inode_operations, struct -+address_space_operations, struct file_operations, etc.) are defined as -+"static const", but it never means that FS have only one set of -+operation. Some FS have multiple sets of them. For instance, ext2 has -+three sets, one for XIP, for NOBH, and for normal. -+Since aufs overrides and redirects these operations, sometimes aufs has -+to change its behaviour according to the branch FS type. More importantly -+VFS acts differently if a function (member in the struct) is set or -+not. It means aufs should have several sets of operations and select one -+among them according to the branch FS definition. -+ -+In order to solve this problem and not to affect the behaviour of VFS, -+aufs defines these operations dynamically. For instance, aufs defines -+dummy direct_IO function for struct address_space_operations, but it may -+not be set to the address_space_operations actually. When the branch FS -+doesn't have it, aufs doesn't set it to its address_space_operations -+while the function definition itself is still alive. So the behaviour -+itself will not change, and it will return an error when direct_IO is -+not set. -+ -+The lifetime of these dynamically generated operation object is -+maintained by aufs branch object. When the branch is removed from aufs, -+the reference counter of the object is decremented. When it reaches -+zero, the dynamically generated operation object will be freed. -+ -+This approach is designed to support AIO (io_submit), Direct I/O and -+XIP (DAX) mainly. -+Currently this approach is applied to address_space_operations for -+regular files only. -diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/README linux-4.0.4/Documentation/filesystems/aufs/README ---- linux-4.0.4.orig/Documentation/filesystems/aufs/README 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/Documentation/filesystems/aufs/README 2015-05-30 22:11:29.000000000 +0200 -@@ -0,0 +1,383 @@ -+ -+Aufs4 -- advanced multi layered unification filesystem version 4.x -+http://aufs.sf.net -+Junjiro R. Okajima -+ -+ -+0. Introduction -+---------------------------------------- -+In the early days, aufs was entirely re-designed and re-implemented -+Unionfs Version 1.x series. Adding many original ideas, approaches, -+improvements and implementations, it becomes totally different from -+Unionfs while keeping the basic features. -+Recently, Unionfs Version 2.x series begin taking some of the same -+approaches to aufs1's. -+Unionfs is being developed by Professor Erez Zadok at Stony Brook -+University and his team. -+ -+Aufs4 supports linux-4.0 and later, and for linux-3.x series try aufs3. -+If you want older kernel version support, try aufs2-2.6.git or -+aufs2-standalone.git repository, aufs1 from CVS on SourceForge. -+ -+Note: it becomes clear that "Aufs was rejected. Let's give it up." -+ According to Christoph Hellwig, linux rejects all union-type -+ filesystems but UnionMount. -+ -+ -+PS. Al Viro seems have a plan to merge aufs as well as overlayfs and -+ UnionMount, and he pointed out an issue around a directory mutex -+ lock and aufs addressed it. But it is still unsure whether aufs will -+ be merged (or any other union solution). -+ -+ -+ -+1. Features -+---------------------------------------- -+- unite several directories into a single virtual filesystem. The member -+ directory is called as a branch. -+- you can specify the permission flags to the branch, which are 'readonly', -+ 'readwrite' and 'whiteout-able.' -+- by upper writable branch, internal copyup and whiteout, files/dirs on -+ readonly branch are modifiable logically. -+- dynamic branch manipulation, add, del. -+- etc... -+ -+Also there are many enhancements in aufs, such as: -+- test only the highest one for the directory permission (dirperm1) -+- copyup on open (coo=) -+- 'move' policy for copy-up between two writable branches, after -+ checking free space. -+- xattr, acl -+- readdir(3) in userspace. -+- keep inode number by external inode number table -+- keep the timestamps of file/dir in internal copyup operation -+- seekable directory, supporting NFS readdir. -+- whiteout is hardlinked in order to reduce the consumption of inodes -+ on branch -+- do not copyup, nor create a whiteout when it is unnecessary -+- revert a single systemcall when an error occurs in aufs -+- remount interface instead of ioctl -+- maintain /etc/mtab by an external command, /sbin/mount.aufs. -+- loopback mounted filesystem as a branch -+- kernel thread for removing the dir who has a plenty of whiteouts -+- support copyup sparse file (a file which has a 'hole' in it) -+- default permission flags for branches -+- selectable permission flags for ro branch, whether whiteout can -+ exist or not -+- export via NFS. -+- support /fs/aufs and /aufs. -+- support multiple writable branches, some policies to select one -+ among multiple writable branches. -+- a new semantics for link(2) and rename(2) to support multiple -+ writable branches. -+- no glibc changes are required. -+- pseudo hardlink (hardlink over branches) -+- allow a direct access manually to a file on branch, e.g. bypassing aufs. -+ including NFS or remote filesystem branch. -+- userspace wrapper for pathconf(3)/fpathconf(3) with _PC_LINK_MAX. -+- and more... -+ -+Currently these features are dropped temporary from aufs4. -+See design/08plan.txt in detail. -+- nested mount, i.e. aufs as readonly no-whiteout branch of another aufs -+ (robr) -+- statistics of aufs thread (/sys/fs/aufs/stat) -+ -+Features or just an idea in the future (see also design/*.txt), -+- reorder the branch index without del/re-add. -+- permanent xino files for NFSD -+- an option for refreshing the opened files after add/del branches -+- light version, without branch manipulation. (unnecessary?) -+- copyup in userspace -+- inotify in userspace -+- readv/writev -+ -+ -+2. Download -+---------------------------------------- -+There are three GIT trees for aufs4, aufs4-linux.git, -+aufs4-standalone.git, and aufs-util.git. Note that there is no "4" in -+"aufs-util.git." -+While the aufs-util is always necessary, you need either of aufs4-linux -+or aufs4-standalone. -+ -+The aufs4-linux tree includes the whole linux mainline GIT tree, -+git://git.kernel.org/.../torvalds/linux.git. -+And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot -+build aufs4 as an external kernel module. -+Several extra patches are not included in this tree. Only -+aufs4-standalone tree contains them. They are describe in the later -+section "Configuration and Compilation." -+ -+On the other hand, the aufs4-standalone tree has only aufs source files -+and necessary patches, and you can select CONFIG_AUFS_FS=m. -+But you need to apply all aufs patches manually. -+ -+You will find GIT branches whose name is in form of "aufs4.x" where "x" -+represents the linux kernel version, "linux-4.x". For instance, -+"aufs4.0" is for linux-4.0. For latest "linux-4.x-rcN", use -+"aufs4.x-rcN" branch. -+ -+o aufs4-linux tree -+$ git clone --reference /your/linux/git/tree \ -+ git://github.com/sfjro/aufs4-linux.git aufs4-linux.git -+- if you don't have linux GIT tree, then remove "--reference ..." -+$ cd aufs4-linux.git -+$ git checkout origin/aufs4.0 -+ -+Or You may want to directly git-pull aufs into your linux GIT tree, and -+leave the patch-work to GIT. -+$ cd /your/linux/git/tree -+$ git remote add aufs4 git://github.com/sfjro/aufs4-linux.git -+$ git fetch aufs4 -+$ git checkout -b my4.0 v4.0 -+$ (add your local change...) -+$ git pull aufs4 aufs4.0 -+- now you have v4.0 + your_changes + aufs4.0 in you my4.0 branch. -+- you may need to solve some conflicts between your_changes and -+ aufs4.0. in this case, git-rerere is recommended so that you can -+ solve the similar conflicts automatically when you upgrade to 4.1 or -+ later in the future. -+ -+o aufs4-standalone tree -+$ git clone git://github.com/sfjro/aufs4-standalone.git aufs4-standalone.git -+$ cd aufs4-standalone.git -+$ git checkout origin/aufs4.0 -+ -+o aufs-util tree -+$ git clone git://git.code.sf.net/p/aufs/aufs-util aufs-util.git -+- note that the public aufs-util.git is on SourceForge instead of -+ GitHUB. -+$ cd aufs-util.git -+$ git checkout origin/aufs4.0 -+ -+Note: The 4.x-rcN branch is to be used with `rc' kernel versions ONLY. -+The minor version number, 'x' in '4.x', of aufs may not always -+follow the minor version number of the kernel. -+Because changes in the kernel that cause the use of a new -+minor version number do not always require changes to aufs-util. -+ -+Since aufs-util has its own minor version number, you may not be -+able to find a GIT branch in aufs-util for your kernel's -+exact minor version number. -+In this case, you should git-checkout the branch for the -+nearest lower number. -+ -+For (an unreleased) example: -+If you are using "linux-4.10" and the "aufs4.10" branch -+does not exist in aufs-util repository, then "aufs4.9", "aufs4.8" -+or something numerically smaller is the branch for your kernel. -+ -+Also you can view all branches by -+ $ git branch -a -+ -+ -+3. Configuration and Compilation -+---------------------------------------- -+Make sure you have git-checkout'ed the correct branch. -+ -+For aufs4-linux tree, -+- enable CONFIG_AUFS_FS. -+- set other aufs configurations if necessary. -+ -+For aufs4-standalone tree, -+There are several ways to build. -+ -+1. -+- apply ./aufs4-kbuild.patch to your kernel source files. -+- apply ./aufs4-base.patch too. -+- apply ./aufs4-mmap.patch too. -+- apply ./aufs4-standalone.patch too, if you have a plan to set -+ CONFIG_AUFS_FS=m. otherwise you don't need ./aufs4-standalone.patch. -+- copy ./{Documentation,fs,include/uapi/linux/aufs_type.h} files to your -+ kernel source tree. Never copy $PWD/include/uapi/linux/Kbuild. -+- enable CONFIG_AUFS_FS, you can select either -+ =m or =y. -+- and build your kernel as usual. -+- install the built kernel. -+ Note: Since linux-3.9, every filesystem module requires an alias -+ "fs-". You should make sure that "fs-aufs" is listed in your -+ modules.aliases file if you set CONFIG_AUFS_FS=m. -+- install the header files too by "make headers_install" to the -+ directory where you specify. By default, it is $PWD/usr. -+ "make help" shows a brief note for headers_install. -+- and reboot your system. -+ -+2. -+- module only (CONFIG_AUFS_FS=m). -+- apply ./aufs4-base.patch to your kernel source files. -+- apply ./aufs4-mmap.patch too. -+- apply ./aufs4-standalone.patch too. -+- build your kernel, don't forget "make headers_install", and reboot. -+- edit ./config.mk and set other aufs configurations if necessary. -+ Note: You should read $PWD/fs/aufs/Kconfig carefully which describes -+ every aufs configurations. -+- build the module by simple "make". -+ Note: Since linux-3.9, every filesystem module requires an alias -+ "fs-". You should make sure that "fs-aufs" is listed in your -+ modules.aliases file. -+- you can specify ${KDIR} make variable which points to your kernel -+ source tree. -+- install the files -+ + run "make install" to install the aufs module, or copy the built -+ $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply). -+ + run "make install_headers" (instead of headers_install) to install -+ the modified aufs header file (you can specify DESTDIR which is -+ available in aufs standalone version's Makefile only), or copy -+ $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever -+ you like manually. By default, the target directory is $PWD/usr. -+- no need to apply aufs4-kbuild.patch, nor copying source files to your -+ kernel source tree. -+ -+Note: The header file aufs_type.h is necessary to build aufs-util -+ as well as "make headers_install" in the kernel source tree. -+ headers_install is subject to be forgotten, but it is essentially -+ necessary, not only for building aufs-util. -+ You may not meet problems without headers_install in some older -+ version though. -+ -+And then, -+- read README in aufs-util, build and install it -+- note that your distribution may contain an obsoleted version of -+ aufs_type.h in /usr/include/linux or something. When you build aufs -+ utilities, make sure that your compiler refers the correct aufs header -+ file which is built by "make headers_install." -+- if you want to use readdir(3) in userspace or pathconf(3) wrapper, -+ then run "make install_ulib" too. And refer to the aufs manual in -+ detail. -+ -+There several other patches in aufs4-standalone.git. They are all -+optional. When you meet some problems, they will help you. -+- aufs4-loopback.patch -+ Supports a nested loopback mount in a branch-fs. This patch is -+ unnecessary until aufs produces a message like "you may want to try -+ another patch for loopback file". -+- vfs-ino.patch -+ Modifies a system global kernel internal function get_next_ino() in -+ order to stop assigning 0 for an inode-number. Not directly related to -+ aufs, but recommended generally. -+- tmpfs-idr.patch -+ Keeps the tmpfs inode number as the lowest value. Effective to reduce -+ the size of aufs XINO files for tmpfs branch. Also it prevents the -+ duplication of inode number, which is important for backup tools and -+ other utilities. When you find aufs XINO files for tmpfs branch -+ growing too much, try this patch. -+ -+ -+4. Usage -+---------------------------------------- -+At first, make sure aufs-util are installed, and please read the aufs -+manual, aufs.5 in aufs-util.git tree. -+$ man -l aufs.5 -+ -+And then, -+$ mkdir /tmp/rw /tmp/aufs -+# mount -t aufs -o br=/tmp/rw:${HOME} none /tmp/aufs -+ -+Here is another example. The result is equivalent. -+# mount -t aufs -o br=/tmp/rw=rw:${HOME}=ro none /tmp/aufs -+ Or -+# mount -t aufs -o br:/tmp/rw none /tmp/aufs -+# mount -o remount,append:${HOME} /tmp/aufs -+ -+Then, you can see whole tree of your home dir through /tmp/aufs. If -+you modify a file under /tmp/aufs, the one on your home directory is -+not affected, instead the same named file will be newly created under -+/tmp/rw. And all of your modification to a file will be applied to -+the one under /tmp/rw. This is called the file based Copy on Write -+(COW) method. -+Aufs mount options are described in aufs.5. -+If you run chroot or something and make your aufs as a root directory, -+then you need to customize the shutdown script. See the aufs manual in -+detail. -+ -+Additionally, there are some sample usages of aufs which are a -+diskless system with network booting, and LiveCD over NFS. -+See sample dir in CVS tree on SourceForge. -+ -+ -+5. Contact -+---------------------------------------- -+When you have any problems or strange behaviour in aufs, please let me -+know with: -+- /proc/mounts (instead of the output of mount(8)) -+- /sys/module/aufs/* -+- /sys/fs/aufs/* (if you have them) -+- /debug/aufs/* (if you have them) -+- linux kernel version -+ if your kernel is not plain, for example modified by distributor, -+ the url where i can download its source is necessary too. -+- aufs version which was printed at loading the module or booting the -+ system, instead of the date you downloaded. -+- configuration (define/undefine CONFIG_AUFS_xxx) -+- kernel configuration or /proc/config.gz (if you have it) -+- behaviour which you think to be incorrect -+- actual operation, reproducible one is better -+- mailto: aufs-users at lists.sourceforge.net -+ -+Usually, I don't watch the Public Areas(Bugs, Support Requests, Patches, -+and Feature Requests) on SourceForge. Please join and write to -+aufs-users ML. -+ -+ -+6. Acknowledgements -+---------------------------------------- -+Thanks to everyone who have tried and are using aufs, whoever -+have reported a bug or any feedback. -+ -+Especially donators: -+Tomas Matejicek(slax.org) made a donation (much more than once). -+ Since Apr 2010, Tomas M (the author of Slax and Linux Live -+ scripts) is making "doubling" donations. -+ Unfortunately I cannot list all of the donators, but I really -+ appreciate. -+ It ends Aug 2010, but the ordinary donation URL is still available. -+ -+Dai Itasaka made a donation (2007/8). -+Chuck Smith made a donation (2008/4, 10 and 12). -+Henk Schoneveld made a donation (2008/9). -+Chih-Wei Huang, ASUS, CTC donated Eee PC 4G (2008/10). -+Francois Dupoux made a donation (2008/11). -+Bruno Cesar Ribas and Luis Carlos Erpen de Bona, C3SL serves public -+ aufs2 GIT tree (2009/2). -+William Grant made a donation (2009/3). -+Patrick Lane made a donation (2009/4). -+The Mail Archive (mail-archive.com) made donations (2009/5). -+Nippy Networks (Ed Wildgoose) made a donation (2009/7). -+New Dream Network, LLC (www.dreamhost.com) made a donation (2009/11). -+Pavel Pronskiy made a donation (2011/2). -+Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy -+ Networks (Ed Wildgoose) made a donation for hardware (2011/3). -+Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and -+11). -+Sam Liddicott made a donation (2011/9). -+Era Scarecrow made a donation (2013/4). -+Bor Ratajc made a donation (2013/4). -+Alessandro Gorreta made a donation (2013/4). -+POIRETTE Marc made a donation (2013/4). -+Alessandro Gorreta made a donation (2013/4). -+lauri kasvandik made a donation (2013/5). -+"pemasu from Finland" made a donation (2013/7). -+The Parted Magic Project made a donation (2013/9 and 11). -+Pavel Barta made a donation (2013/10). -+Nikolay Pertsev made a donation (2014/5). -+James B made a donation (2014/7). -+Stefano Di Biase made a donation (2014/8). -+Daniel Epellei made a donation (2015/1). -+ -+Thank you very much. -+Donations are always, including future donations, very important and -+helpful for me to keep on developing aufs. -+ -+ -+7. -+---------------------------------------- -+If you are an experienced user, no explanation is needed. Aufs is -+just a linux filesystem. -+ -+ -+Enjoy! -+ -+# Local variables: ; -+# mode: text; -+# End: ; -diff -Nur linux-4.0.4.orig/drivers/block/loop.c linux-4.0.4/drivers/block/loop.c ---- linux-4.0.4.orig/drivers/block/loop.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/drivers/block/loop.c 2015-05-30 22:11:30.000000000 +0200 -@@ -592,6 +592,24 @@ - return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; - } - -+/* -+ * for AUFS -+ * no get/put for file. -+ */ -+struct file *loop_backing_file(struct super_block *sb) -+{ -+ struct file *ret; -+ struct loop_device *l; -+ -+ ret = NULL; -+ if (MAJOR(sb->s_dev) == LOOP_MAJOR) { -+ l = sb->s_bdev->bd_disk->private_data; -+ ret = l->lo_backing_file; -+ } -+ return ret; -+} -+EXPORT_SYMBOL(loop_backing_file); -+ - /* loop sysfs attributes */ - - static ssize_t loop_attr_show(struct device *dev, char *page, -diff -Nur linux-4.0.4.orig/fs/aufs/aufs.h linux-4.0.4/fs/aufs/aufs.h ---- linux-4.0.4.orig/fs/aufs/aufs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/aufs.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,46 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * all header files -+ */ -+ -+#ifndef __AUFS_H__ -+#define __AUFS_H__ -+ -+#ifdef __KERNEL__ -+ -+#define AuStub(type, name, body, ...) \ -+ static inline type name(__VA_ARGS__) { body; } -+ -+#define AuStubVoid(name, ...) \ -+ AuStub(void, name, , __VA_ARGS__) -+#define AuStubInt0(name, ...) \ -+ AuStub(int, name, return 0, __VA_ARGS__) -+ -+#include "debug.h" -+ -+#include "branch.h" -+#include "cpup.h" -+#include "dcsub.h" -+#include "dbgaufs.h" -+#include "dentry.h" -+#include "dir.h" -+#include "dynop.h" -+#include "file.h" -+#include "fstype.h" -+#include "inode.h" -+#include "loop.h" -+#include "module.h" -+#include "opts.h" -+#include "rwsem.h" -+#include "spl.h" -+#include "super.h" -+#include "sysaufs.h" -+#include "vfsub.h" -+#include "whout.h" -+#include "wkq.h" -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/branch.c linux-4.0.4/fs/aufs/branch.c ---- linux-4.0.4.orig/fs/aufs/branch.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/branch.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,1393 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * branch management -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+/* -+ * free a single branch -+ */ -+static void au_br_do_free(struct au_branch *br) -+{ -+ int i; -+ struct au_wbr *wbr; -+ struct au_dykey **key; -+ -+ au_hnotify_fin_br(br); -+ -+ if (br->br_xino.xi_file) -+ fput(br->br_xino.xi_file); -+ mutex_destroy(&br->br_xino.xi_nondir_mtx); -+ -+ AuDebugOn(atomic_read(&br->br_count)); -+ -+ wbr = br->br_wbr; -+ if (wbr) { -+ for (i = 0; i < AuBrWh_Last; i++) -+ dput(wbr->wbr_wh[i]); -+ AuDebugOn(atomic_read(&wbr->wbr_wh_running)); -+ AuRwDestroy(&wbr->wbr_wh_rwsem); -+ } -+ -+ if (br->br_fhsm) { -+ au_br_fhsm_fin(br->br_fhsm); -+ kfree(br->br_fhsm); -+ } -+ -+ key = br->br_dykey; -+ for (i = 0; i < AuBrDynOp; i++, key++) -+ if (*key) -+ au_dy_put(*key); -+ else -+ break; -+ -+ /* recursive lock, s_umount of branch's */ -+ lockdep_off(); -+ path_put(&br->br_path); -+ lockdep_on(); -+ kfree(wbr); -+ kfree(br); -+} -+ -+/* -+ * frees all branches -+ */ -+void au_br_free(struct au_sbinfo *sbinfo) -+{ -+ aufs_bindex_t bmax; -+ struct au_branch **br; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ bmax = sbinfo->si_bend + 1; -+ br = sbinfo->si_branch; -+ while (bmax--) -+ au_br_do_free(*br++); -+} -+ -+/* -+ * find the index of a branch which is specified by @br_id. -+ */ -+int au_br_index(struct super_block *sb, aufs_bindex_t br_id) -+{ -+ aufs_bindex_t bindex, bend; -+ -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (au_sbr_id(sb, bindex) == br_id) -+ return bindex; -+ return -1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * add a branch -+ */ -+ -+static int test_overlap(struct super_block *sb, struct dentry *h_adding, -+ struct dentry *h_root) -+{ -+ if (unlikely(h_adding == h_root -+ || au_test_loopback_overlap(sb, h_adding))) -+ return 1; -+ if (h_adding->d_sb != h_root->d_sb) -+ return 0; -+ return au_test_subdir(h_adding, h_root) -+ || au_test_subdir(h_root, h_adding); -+} -+ -+/* -+ * returns a newly allocated branch. @new_nbranch is a number of branches -+ * after adding a branch. -+ */ -+static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, -+ int perm) -+{ -+ struct au_branch *add_branch; -+ struct dentry *root; -+ int err; -+ -+ err = -ENOMEM; -+ root = sb->s_root; -+ add_branch = kmalloc(sizeof(*add_branch), GFP_NOFS); -+ if (unlikely(!add_branch)) -+ goto out; -+ -+ err = au_hnotify_init_br(add_branch, perm); -+ if (unlikely(err)) -+ goto out_br; -+ -+ add_branch->br_wbr = NULL; -+ if (au_br_writable(perm)) { -+ /* may be freed separately at changing the branch permission */ -+ add_branch->br_wbr = kmalloc(sizeof(*add_branch->br_wbr), -+ GFP_NOFS); -+ if (unlikely(!add_branch->br_wbr)) -+ goto out_hnotify; -+ } -+ -+ add_branch->br_fhsm = NULL; -+ if (au_br_fhsm(perm)) { -+ err = au_fhsm_br_alloc(add_branch); -+ if (unlikely(err)) -+ goto out_wbr; -+ } -+ -+ err = au_sbr_realloc(au_sbi(sb), new_nbranch); -+ if (!err) -+ err = au_di_realloc(au_di(root), new_nbranch); -+ if (!err) -+ err = au_ii_realloc(au_ii(root->d_inode), new_nbranch); -+ if (!err) -+ return add_branch; /* success */ -+ -+out_wbr: -+ kfree(add_branch->br_wbr); -+out_hnotify: -+ au_hnotify_fin_br(add_branch); -+out_br: -+ kfree(add_branch); -+out: -+ return ERR_PTR(err); -+} -+ -+/* -+ * test if the branch permission is legal or not. -+ */ -+static int test_br(struct inode *inode, int brperm, char *path) -+{ -+ int err; -+ -+ err = (au_br_writable(brperm) && IS_RDONLY(inode)); -+ if (!err) -+ goto out; -+ -+ err = -EINVAL; -+ pr_err("write permission for readonly mount or inode, %s\n", path); -+ -+out: -+ return err; -+} -+ -+/* -+ * returns: -+ * 0: success, the caller will add it -+ * plus: success, it is already unified, the caller should ignore it -+ * minus: error -+ */ -+static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct dentry *root; -+ struct inode *inode, *h_inode; -+ -+ root = sb->s_root; -+ bend = au_sbend(sb); -+ if (unlikely(bend >= 0 -+ && au_find_dbindex(root, add->path.dentry) >= 0)) { -+ err = 1; -+ if (!remount) { -+ err = -EINVAL; -+ pr_err("%s duplicated\n", add->pathname); -+ } -+ goto out; -+ } -+ -+ err = -ENOSPC; /* -E2BIG; */ -+ if (unlikely(AUFS_BRANCH_MAX <= add->bindex -+ || AUFS_BRANCH_MAX - 1 <= bend)) { -+ pr_err("number of branches exceeded %s\n", add->pathname); -+ goto out; -+ } -+ -+ err = -EDOM; -+ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { -+ pr_err("bad index %d\n", add->bindex); -+ goto out; -+ } -+ -+ inode = add->path.dentry->d_inode; -+ err = -ENOENT; -+ if (unlikely(!inode->i_nlink)) { -+ pr_err("no existence %s\n", add->pathname); -+ goto out; -+ } -+ -+ err = -EINVAL; -+ if (unlikely(inode->i_sb == sb)) { -+ pr_err("%s must be outside\n", add->pathname); -+ goto out; -+ } -+ -+ if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) { -+ pr_err("unsupported filesystem, %s (%s)\n", -+ add->pathname, au_sbtype(inode->i_sb)); -+ goto out; -+ } -+ -+ if (unlikely(inode->i_sb->s_stack_depth)) { -+ pr_err("already stacked, %s (%s)\n", -+ add->pathname, au_sbtype(inode->i_sb)); -+ goto out; -+ } -+ -+ err = test_br(add->path.dentry->d_inode, add->perm, add->pathname); -+ if (unlikely(err)) -+ goto out; -+ -+ if (bend < 0) -+ return 0; /* success */ -+ -+ err = -EINVAL; -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (unlikely(test_overlap(sb, add->path.dentry, -+ au_h_dptr(root, bindex)))) { -+ pr_err("%s is overlapped\n", add->pathname); -+ goto out; -+ } -+ -+ err = 0; -+ if (au_opt_test(au_mntflags(sb), WARN_PERM)) { -+ h_inode = au_h_dptr(root, 0)->d_inode; -+ if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO) -+ || !uid_eq(h_inode->i_uid, inode->i_uid) -+ || !gid_eq(h_inode->i_gid, inode->i_gid)) -+ pr_warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", -+ add->pathname, -+ i_uid_read(inode), i_gid_read(inode), -+ (inode->i_mode & S_IALLUGO), -+ i_uid_read(h_inode), i_gid_read(h_inode), -+ (h_inode->i_mode & S_IALLUGO)); -+ } -+ -+out: -+ return err; -+} -+ -+/* -+ * initialize or clean the whiteouts for an adding branch -+ */ -+static int au_br_init_wh(struct super_block *sb, struct au_branch *br, -+ int new_perm) -+{ -+ int err, old_perm; -+ aufs_bindex_t bindex; -+ struct mutex *h_mtx; -+ struct au_wbr *wbr; -+ struct au_hinode *hdir; -+ -+ err = vfsub_mnt_want_write(au_br_mnt(br)); -+ if (unlikely(err)) -+ goto out; -+ -+ wbr = br->br_wbr; -+ old_perm = br->br_perm; -+ br->br_perm = new_perm; -+ hdir = NULL; -+ h_mtx = NULL; -+ bindex = au_br_index(sb, br->br_id); -+ if (0 <= bindex) { -+ hdir = au_hi(sb->s_root->d_inode, bindex); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ } else { -+ h_mtx = &au_br_dentry(br)->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_PARENT); -+ } -+ if (!wbr) -+ err = au_wh_init(br, sb); -+ else { -+ wbr_wh_write_lock(wbr); -+ err = au_wh_init(br, sb); -+ wbr_wh_write_unlock(wbr); -+ } -+ if (hdir) -+ au_hn_imtx_unlock(hdir); -+ else -+ mutex_unlock(h_mtx); -+ vfsub_mnt_drop_write(au_br_mnt(br)); -+ br->br_perm = old_perm; -+ -+ if (!err && wbr && !au_br_writable(new_perm)) { -+ kfree(wbr); -+ br->br_wbr = NULL; -+ } -+ -+out: -+ return err; -+} -+ -+static int au_wbr_init(struct au_branch *br, struct super_block *sb, -+ int perm) -+{ -+ int err; -+ struct kstatfs kst; -+ struct au_wbr *wbr; -+ -+ wbr = br->br_wbr; -+ au_rw_init(&wbr->wbr_wh_rwsem); -+ memset(wbr->wbr_wh, 0, sizeof(wbr->wbr_wh)); -+ atomic_set(&wbr->wbr_wh_running, 0); -+ wbr->wbr_bytes = 0; -+ -+ /* -+ * a limit for rmdir/rename a dir -+ * cf. AUFS_MAX_NAMELEN in include/uapi/linux/aufs_type.h -+ */ -+ err = vfs_statfs(&br->br_path, &kst); -+ if (unlikely(err)) -+ goto out; -+ err = -EINVAL; -+ if (kst.f_namelen >= NAME_MAX) -+ err = au_br_init_wh(sb, br, perm); -+ else -+ pr_err("%pd(%s), unsupported namelen %ld\n", -+ au_br_dentry(br), -+ au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); -+ -+out: -+ return err; -+} -+ -+/* initialize a new branch */ -+static int au_br_init(struct au_branch *br, struct super_block *sb, -+ struct au_opt_add *add) -+{ -+ int err; -+ -+ err = 0; -+ memset(&br->br_xino, 0, sizeof(br->br_xino)); -+ mutex_init(&br->br_xino.xi_nondir_mtx); -+ br->br_perm = add->perm; -+ br->br_path = add->path; /* set first, path_get() later */ -+ spin_lock_init(&br->br_dykey_lock); -+ memset(br->br_dykey, 0, sizeof(br->br_dykey)); -+ atomic_set(&br->br_count, 0); -+ atomic_set(&br->br_xino_running, 0); -+ br->br_id = au_new_br_id(sb); -+ AuDebugOn(br->br_id < 0); -+ -+ if (au_br_writable(add->perm)) { -+ err = au_wbr_init(br, sb, add->perm); -+ if (unlikely(err)) -+ goto out_err; -+ } -+ -+ if (au_opt_test(au_mntflags(sb), XINO)) { -+ err = au_xino_br(sb, br, add->path.dentry->d_inode->i_ino, -+ au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1); -+ if (unlikely(err)) { -+ AuDebugOn(br->br_xino.xi_file); -+ goto out_err; -+ } -+ } -+ -+ sysaufs_br_init(br); -+ path_get(&br->br_path); -+ goto out; /* success */ -+ -+out_err: -+ memset(&br->br_path, 0, sizeof(br->br_path)); -+out: -+ return err; -+} -+ -+static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, -+ struct au_branch *br, aufs_bindex_t bend, -+ aufs_bindex_t amount) -+{ -+ struct au_branch **brp; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ brp = sbinfo->si_branch + bindex; -+ memmove(brp + 1, brp, sizeof(*brp) * amount); -+ *brp = br; -+ sbinfo->si_bend++; -+ if (unlikely(bend < 0)) -+ sbinfo->si_bend = 0; -+} -+ -+static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, -+ aufs_bindex_t bend, aufs_bindex_t amount) -+{ -+ struct au_hdentry *hdp; -+ -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ hdp = dinfo->di_hdentry + bindex; -+ memmove(hdp + 1, hdp, sizeof(*hdp) * amount); -+ au_h_dentry_init(hdp); -+ dinfo->di_bend++; -+ if (unlikely(bend < 0)) -+ dinfo->di_bstart = 0; -+} -+ -+static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, -+ aufs_bindex_t bend, aufs_bindex_t amount) -+{ -+ struct au_hinode *hip; -+ -+ AuRwMustWriteLock(&iinfo->ii_rwsem); -+ -+ hip = iinfo->ii_hinode + bindex; -+ memmove(hip + 1, hip, sizeof(*hip) * amount); -+ hip->hi_inode = NULL; -+ au_hn_init(hip); -+ iinfo->ii_bend++; -+ if (unlikely(bend < 0)) -+ iinfo->ii_bstart = 0; -+} -+ -+static void au_br_do_add(struct super_block *sb, struct au_branch *br, -+ aufs_bindex_t bindex) -+{ -+ struct dentry *root, *h_dentry; -+ struct inode *root_inode; -+ aufs_bindex_t bend, amount; -+ -+ root = sb->s_root; -+ root_inode = root->d_inode; -+ bend = au_sbend(sb); -+ amount = bend + 1 - bindex; -+ h_dentry = au_br_dentry(br); -+ au_sbilist_lock(); -+ au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); -+ au_br_do_add_hdp(au_di(root), bindex, bend, amount); -+ au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); -+ au_set_h_dptr(root, bindex, dget(h_dentry)); -+ au_set_h_iptr(root_inode, bindex, au_igrab(h_dentry->d_inode), -+ /*flags*/0); -+ au_sbilist_unlock(); -+} -+ -+int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) -+{ -+ int err; -+ aufs_bindex_t bend, add_bindex; -+ struct dentry *root, *h_dentry; -+ struct inode *root_inode; -+ struct au_branch *add_branch; -+ -+ root = sb->s_root; -+ root_inode = root->d_inode; -+ IMustLock(root_inode); -+ err = test_add(sb, add, remount); -+ if (unlikely(err < 0)) -+ goto out; -+ if (err) { -+ err = 0; -+ goto out; /* success */ -+ } -+ -+ bend = au_sbend(sb); -+ add_branch = au_br_alloc(sb, bend + 2, add->perm); -+ err = PTR_ERR(add_branch); -+ if (IS_ERR(add_branch)) -+ goto out; -+ -+ err = au_br_init(add_branch, sb, add); -+ if (unlikely(err)) { -+ au_br_do_free(add_branch); -+ goto out; -+ } -+ -+ add_bindex = add->bindex; -+ if (!remount) -+ au_br_do_add(sb, add_branch, add_bindex); -+ else { -+ sysaufs_brs_del(sb, add_bindex); -+ au_br_do_add(sb, add_branch, add_bindex); -+ sysaufs_brs_add(sb, add_bindex); -+ } -+ -+ h_dentry = add->path.dentry; -+ if (!add_bindex) { -+ au_cpup_attr_all(root_inode, /*force*/1); -+ sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; -+ } else -+ au_add_nlink(root_inode, h_dentry->d_inode); -+ -+ /* -+ * this test/set prevents aufs from handling unnecesary notify events -+ * of xino files, in case of re-adding a writable branch which was -+ * once detached from aufs. -+ */ -+ if (au_xino_brid(sb) < 0 -+ && au_br_writable(add_branch->br_perm) -+ && !au_test_fs_bad_xino(h_dentry->d_sb) -+ && add_branch->br_xino.xi_file -+ && add_branch->br_xino.xi_file->f_path.dentry->d_parent == h_dentry) -+ au_xino_brid_set(sb, add_branch->br_id); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static unsigned long long au_farray_cb(void *a, -+ unsigned long long max __maybe_unused, -+ void *arg) -+{ -+ unsigned long long n; -+ struct file **p, *f; -+ struct au_sphlhead *files; -+ struct au_finfo *finfo; -+ struct super_block *sb = arg; -+ -+ n = 0; -+ p = a; -+ files = &au_sbi(sb)->si_files; -+ spin_lock(&files->spin); -+ hlist_for_each_entry(finfo, &files->head, fi_hlist) { -+ f = finfo->fi_file; -+ if (file_count(f) -+ && !special_file(file_inode(f)->i_mode)) { -+ get_file(f); -+ *p++ = f; -+ n++; -+ AuDebugOn(n > max); -+ } -+ } -+ spin_unlock(&files->spin); -+ -+ return n; -+} -+ -+static struct file **au_farray_alloc(struct super_block *sb, -+ unsigned long long *max) -+{ -+ *max = atomic_long_read(&au_sbi(sb)->si_nfiles); -+ return au_array_alloc(max, au_farray_cb, sb); -+} -+ -+static void au_farray_free(struct file **a, unsigned long long max) -+{ -+ unsigned long long ull; -+ -+ for (ull = 0; ull < max; ull++) -+ if (a[ull]) -+ fput(a[ull]); -+ au_array_free(a); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * delete a branch -+ */ -+ -+/* to show the line number, do not make it inlined function */ -+#define AuVerbose(do_info, fmt, ...) do { \ -+ if (do_info) \ -+ pr_info(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+static int au_test_ibusy(struct inode *inode, aufs_bindex_t bstart, -+ aufs_bindex_t bend) -+{ -+ return (inode && !S_ISDIR(inode->i_mode)) || bstart == bend; -+} -+ -+static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t bstart, -+ aufs_bindex_t bend) -+{ -+ return au_test_ibusy(dentry->d_inode, bstart, bend); -+} -+ -+/* -+ * test if the branch is deletable or not. -+ */ -+static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, -+ unsigned int sigen, const unsigned int verbose) -+{ -+ int err, i, j, ndentry; -+ aufs_bindex_t bstart, bend; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry *d; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, root, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ for (i = 0; !err && i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ ndentry = dpage->ndentry; -+ for (j = 0; !err && j < ndentry; j++) { -+ d = dpage->dentries[j]; -+ AuDebugOn(au_dcount(d) <= 0); -+ if (!au_digen_test(d, sigen)) { -+ di_read_lock_child(d, AuLock_IR); -+ if (unlikely(au_dbrange_test(d))) { -+ di_read_unlock(d, AuLock_IR); -+ continue; -+ } -+ } else { -+ di_write_lock_child(d); -+ if (unlikely(au_dbrange_test(d))) { -+ di_write_unlock(d); -+ continue; -+ } -+ err = au_reval_dpath(d, sigen); -+ if (!err) -+ di_downgrade_lock(d, AuLock_IR); -+ else { -+ di_write_unlock(d); -+ break; -+ } -+ } -+ -+ /* AuDbgDentry(d); */ -+ bstart = au_dbstart(d); -+ bend = au_dbend(d); -+ if (bstart <= bindex -+ && bindex <= bend -+ && au_h_dptr(d, bindex) -+ && au_test_dbusy(d, bstart, bend)) { -+ err = -EBUSY; -+ AuVerbose(verbose, "busy %pd\n", d); -+ AuDbgDentry(d); -+ } -+ di_read_unlock(d, AuLock_IR); -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, -+ unsigned int sigen, const unsigned int verbose) -+{ -+ int err; -+ unsigned long long max, ull; -+ struct inode *i, **array; -+ aufs_bindex_t bstart, bend; -+ -+ array = au_iarray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ err = 0; -+ AuDbg("b%d\n", bindex); -+ for (ull = 0; !err && ull < max; ull++) { -+ i = array[ull]; -+ if (unlikely(!i)) -+ break; -+ if (i->i_ino == AUFS_ROOT_INO) -+ continue; -+ -+ /* AuDbgInode(i); */ -+ if (au_iigen(i, NULL) == sigen) -+ ii_read_lock_child(i); -+ else { -+ ii_write_lock_child(i); -+ err = au_refresh_hinode_self(i); -+ au_iigen_dec(i); -+ if (!err) -+ ii_downgrade_lock(i); -+ else { -+ ii_write_unlock(i); -+ break; -+ } -+ } -+ -+ bstart = au_ibstart(i); -+ bend = au_ibend(i); -+ if (bstart <= bindex -+ && bindex <= bend -+ && au_h_iptr(i, bindex) -+ && au_test_ibusy(i, bstart, bend)) { -+ err = -EBUSY; -+ AuVerbose(verbose, "busy i%lu\n", i->i_ino); -+ AuDbgInode(i); -+ } -+ ii_read_unlock(i); -+ } -+ au_iarray_free(array, max); -+ -+out: -+ return err; -+} -+ -+static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, -+ const unsigned int verbose) -+{ -+ int err; -+ unsigned int sigen; -+ -+ sigen = au_sigen(root->d_sb); -+ DiMustNoWaiters(root); -+ IiMustNoWaiters(root->d_inode); -+ di_write_unlock(root); -+ err = test_dentry_busy(root, bindex, sigen, verbose); -+ if (!err) -+ err = test_inode_busy(root->d_sb, bindex, sigen, verbose); -+ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ -+ -+ return err; -+} -+ -+static int test_dir_busy(struct file *file, aufs_bindex_t br_id, -+ struct file **to_free, int *idx) -+{ -+ int err; -+ unsigned char matched, root; -+ aufs_bindex_t bindex, bend; -+ struct au_fidir *fidir; -+ struct au_hfile *hfile; -+ -+ err = 0; -+ root = IS_ROOT(file->f_path.dentry); -+ if (root) { -+ get_file(file); -+ to_free[*idx] = file; -+ (*idx)++; -+ goto out; -+ } -+ -+ matched = 0; -+ fidir = au_fi(file)->fi_hdir; -+ AuDebugOn(!fidir); -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); bindex <= bend; bindex++) { -+ hfile = fidir->fd_hfile + bindex; -+ if (!hfile->hf_file) -+ continue; -+ -+ if (hfile->hf_br->br_id == br_id) { -+ matched = 1; -+ break; -+ } -+ } -+ if (matched) -+ err = -EBUSY; -+ -+out: -+ return err; -+} -+ -+static int test_file_busy(struct super_block *sb, aufs_bindex_t br_id, -+ struct file **to_free, int opened) -+{ -+ int err, idx; -+ unsigned long long ull, max; -+ aufs_bindex_t bstart; -+ struct file *file, **array; -+ struct dentry *root; -+ struct au_hfile *hfile; -+ -+ array = au_farray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ err = 0; -+ idx = 0; -+ root = sb->s_root; -+ di_write_unlock(root); -+ for (ull = 0; ull < max; ull++) { -+ file = array[ull]; -+ if (unlikely(!file)) -+ break; -+ -+ /* AuDbg("%pD\n", file); */ -+ fi_read_lock(file); -+ bstart = au_fbstart(file); -+ if (!d_is_dir(file->f_path.dentry)) { -+ hfile = &au_fi(file)->fi_htop; -+ if (hfile->hf_br->br_id == br_id) -+ err = -EBUSY; -+ } else -+ err = test_dir_busy(file, br_id, to_free, &idx); -+ fi_read_unlock(file); -+ if (unlikely(err)) -+ break; -+ } -+ di_write_lock_child(root); -+ au_farray_free(array, max); -+ AuDebugOn(idx > opened); -+ -+out: -+ return err; -+} -+ -+static void br_del_file(struct file **to_free, unsigned long long opened, -+ aufs_bindex_t br_id) -+{ -+ unsigned long long ull; -+ aufs_bindex_t bindex, bstart, bend, bfound; -+ struct file *file; -+ struct au_fidir *fidir; -+ struct au_hfile *hfile; -+ -+ for (ull = 0; ull < opened; ull++) { -+ file = to_free[ull]; -+ if (unlikely(!file)) -+ break; -+ -+ /* AuDbg("%pD\n", file); */ -+ AuDebugOn(!d_is_dir(file->f_path.dentry)); -+ bfound = -1; -+ fidir = au_fi(file)->fi_hdir; -+ AuDebugOn(!fidir); -+ fi_write_lock(file); -+ bstart = au_fbstart(file); -+ bend = au_fbend_dir(file); -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ hfile = fidir->fd_hfile + bindex; -+ if (!hfile->hf_file) -+ continue; -+ -+ if (hfile->hf_br->br_id == br_id) { -+ bfound = bindex; -+ break; -+ } -+ } -+ AuDebugOn(bfound < 0); -+ au_set_h_fptr(file, bfound, NULL); -+ if (bfound == bstart) { -+ for (bstart++; bstart <= bend; bstart++) -+ if (au_hf_dir(file, bstart)) { -+ au_set_fbstart(file, bstart); -+ break; -+ } -+ } -+ fi_write_unlock(file); -+ } -+} -+ -+static void au_br_do_del_brp(struct au_sbinfo *sbinfo, -+ const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) -+{ -+ struct au_branch **brp, **p; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ brp = sbinfo->si_branch + bindex; -+ if (bindex < bend) -+ memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex)); -+ sbinfo->si_branch[0 + bend] = NULL; -+ sbinfo->si_bend--; -+ -+ p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, AuGFP_SBILIST); -+ if (p) -+ sbinfo->si_branch = p; -+ /* harmless error */ -+} -+ -+static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) -+{ -+ struct au_hdentry *hdp, *p; -+ -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ hdp = dinfo->di_hdentry; -+ if (bindex < bend) -+ memmove(hdp + bindex, hdp + bindex + 1, -+ sizeof(*hdp) * (bend - bindex)); -+ hdp[0 + bend].hd_dentry = NULL; -+ dinfo->di_bend--; -+ -+ p = krealloc(hdp, sizeof(*p) * bend, AuGFP_SBILIST); -+ if (p) -+ dinfo->di_hdentry = p; -+ /* harmless error */ -+} -+ -+static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, -+ const aufs_bindex_t bend) -+{ -+ struct au_hinode *hip, *p; -+ -+ AuRwMustWriteLock(&iinfo->ii_rwsem); -+ -+ hip = iinfo->ii_hinode + bindex; -+ if (bindex < bend) -+ memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex)); -+ iinfo->ii_hinode[0 + bend].hi_inode = NULL; -+ au_hn_init(iinfo->ii_hinode + bend); -+ iinfo->ii_bend--; -+ -+ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, AuGFP_SBILIST); -+ if (p) -+ iinfo->ii_hinode = p; -+ /* harmless error */ -+} -+ -+static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, -+ struct au_branch *br) -+{ -+ aufs_bindex_t bend; -+ struct au_sbinfo *sbinfo; -+ struct dentry *root, *h_root; -+ struct inode *inode, *h_inode; -+ struct au_hinode *hinode; -+ -+ SiMustWriteLock(sb); -+ -+ root = sb->s_root; -+ inode = root->d_inode; -+ sbinfo = au_sbi(sb); -+ bend = sbinfo->si_bend; -+ -+ h_root = au_h_dptr(root, bindex); -+ hinode = au_hi(inode, bindex); -+ h_inode = au_igrab(hinode->hi_inode); -+ au_hiput(hinode); -+ -+ au_sbilist_lock(); -+ au_br_do_del_brp(sbinfo, bindex, bend); -+ au_br_do_del_hdp(au_di(root), bindex, bend); -+ au_br_do_del_hip(au_ii(inode), bindex, bend); -+ au_sbilist_unlock(); -+ -+ dput(h_root); -+ iput(h_inode); -+ au_br_do_free(br); -+} -+ -+static unsigned long long empty_cb(void *array, unsigned long long max, -+ void *arg) -+{ -+ return max; -+} -+ -+int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) -+{ -+ int err, rerr, i; -+ unsigned long long opened; -+ unsigned int mnt_flags; -+ aufs_bindex_t bindex, bend, br_id; -+ unsigned char do_wh, verbose; -+ struct au_branch *br; -+ struct au_wbr *wbr; -+ struct dentry *root; -+ struct file **to_free; -+ -+ err = 0; -+ opened = 0; -+ to_free = NULL; -+ root = sb->s_root; -+ bindex = au_find_dbindex(root, del->h_path.dentry); -+ if (bindex < 0) { -+ if (remount) -+ goto out; /* success */ -+ err = -ENOENT; -+ pr_err("%s no such branch\n", del->pathname); -+ goto out; -+ } -+ AuDbg("bindex b%d\n", bindex); -+ -+ err = -EBUSY; -+ mnt_flags = au_mntflags(sb); -+ verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ bend = au_sbend(sb); -+ if (unlikely(!bend)) { -+ AuVerbose(verbose, "no more branches left\n"); -+ goto out; -+ } -+ br = au_sbr(sb, bindex); -+ AuDebugOn(!path_equal(&br->br_path, &del->h_path)); -+ -+ br_id = br->br_id; -+ opened = atomic_read(&br->br_count); -+ if (unlikely(opened)) { -+ to_free = au_array_alloc(&opened, empty_cb, NULL); -+ err = PTR_ERR(to_free); -+ if (IS_ERR(to_free)) -+ goto out; -+ -+ err = test_file_busy(sb, br_id, to_free, opened); -+ if (unlikely(err)) { -+ AuVerbose(verbose, "%llu file(s) opened\n", opened); -+ goto out; -+ } -+ } -+ -+ wbr = br->br_wbr; -+ do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph); -+ if (do_wh) { -+ /* instead of WbrWhMustWriteLock(wbr) */ -+ SiMustWriteLock(sb); -+ for (i = 0; i < AuBrWh_Last; i++) { -+ dput(wbr->wbr_wh[i]); -+ wbr->wbr_wh[i] = NULL; -+ } -+ } -+ -+ err = test_children_busy(root, bindex, verbose); -+ if (unlikely(err)) { -+ if (do_wh) -+ goto out_wh; -+ goto out; -+ } -+ -+ err = 0; -+ if (to_free) { -+ /* -+ * now we confirmed the branch is deletable. -+ * let's free the remaining opened dirs on the branch. -+ */ -+ di_write_unlock(root); -+ br_del_file(to_free, opened, br_id); -+ di_write_lock_child(root); -+ } -+ -+ if (!remount) -+ au_br_do_del(sb, bindex, br); -+ else { -+ sysaufs_brs_del(sb, bindex); -+ au_br_do_del(sb, bindex, br); -+ sysaufs_brs_add(sb, bindex); -+ } -+ -+ if (!bindex) { -+ au_cpup_attr_all(root->d_inode, /*force*/1); -+ sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes; -+ } else -+ au_sub_nlink(root->d_inode, del->h_path.dentry->d_inode); -+ if (au_opt_test(mnt_flags, PLINK)) -+ au_plink_half_refresh(sb, br_id); -+ -+ if (au_xino_brid(sb) == br_id) -+ au_xino_brid_set(sb, -1); -+ goto out; /* success */ -+ -+out_wh: -+ /* revert */ -+ rerr = au_br_init_wh(sb, br, br->br_perm); -+ if (rerr) -+ pr_warn("failed re-creating base whiteout, %s. (%d)\n", -+ del->pathname, rerr); -+out: -+ if (to_free) -+ au_farray_free(to_free, opened); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) -+{ -+ int err; -+ aufs_bindex_t bstart, bend; -+ struct aufs_ibusy ibusy; -+ struct inode *inode, *h_inode; -+ -+ err = -EPERM; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = copy_from_user(&ibusy, arg, sizeof(ibusy)); -+ if (!err) -+ err = !access_ok(VERIFY_WRITE, &arg->h_ino, sizeof(arg->h_ino)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ -+ err = -EINVAL; -+ si_read_lock(sb, AuLock_FLUSH); -+ if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbend(sb))) -+ goto out_unlock; -+ -+ err = 0; -+ ibusy.h_ino = 0; /* invalid */ -+ inode = ilookup(sb, ibusy.ino); -+ if (!inode -+ || inode->i_ino == AUFS_ROOT_INO -+ || is_bad_inode(inode)) -+ goto out_unlock; -+ -+ ii_read_lock_child(inode); -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart <= ibusy.bindex && ibusy.bindex <= bend) { -+ h_inode = au_h_iptr(inode, ibusy.bindex); -+ if (h_inode && au_test_ibusy(inode, bstart, bend)) -+ ibusy.h_ino = h_inode->i_ino; -+ } -+ ii_read_unlock(inode); -+ iput(inode); -+ -+out_unlock: -+ si_read_unlock(sb); -+ if (!err) { -+ err = __put_user(ibusy.h_ino, &arg->h_ino); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ } -+ } -+out: -+ return err; -+} -+ -+long au_ibusy_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_ibusy(file->f_path.dentry->d_sb, (void __user *)arg); -+} -+ -+#ifdef CONFIG_COMPAT -+long au_ibusy_compat_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_ibusy(file->f_path.dentry->d_sb, compat_ptr(arg)); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * change a branch permission -+ */ -+ -+static void au_warn_ima(void) -+{ -+#ifdef CONFIG_IMA -+ /* since it doesn't support mark_files_ro() */ -+ AuWarn1("RW -> RO makes IMA to produce wrong message\n"); -+#endif -+} -+ -+static int do_need_sigen_inc(int a, int b) -+{ -+ return au_br_whable(a) && !au_br_whable(b); -+} -+ -+static int need_sigen_inc(int old, int new) -+{ -+ return do_need_sigen_inc(old, new) -+ || do_need_sigen_inc(new, old); -+} -+ -+static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ int err, do_warn; -+ unsigned int mnt_flags; -+ unsigned long long ull, max; -+ aufs_bindex_t br_id; -+ unsigned char verbose, writer; -+ struct file *file, *hf, **array; -+ struct au_hfile *hfile; -+ -+ mnt_flags = au_mntflags(sb); -+ verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ -+ array = au_farray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ do_warn = 0; -+ br_id = au_sbr_id(sb, bindex); -+ for (ull = 0; ull < max; ull++) { -+ file = array[ull]; -+ if (unlikely(!file)) -+ break; -+ -+ /* AuDbg("%pD\n", file); */ -+ fi_read_lock(file); -+ if (unlikely(au_test_mmapped(file))) { -+ err = -EBUSY; -+ AuVerbose(verbose, "mmapped %pD\n", file); -+ AuDbgFile(file); -+ FiMustNoWaiters(file); -+ fi_read_unlock(file); -+ goto out_array; -+ } -+ -+ hfile = &au_fi(file)->fi_htop; -+ hf = hfile->hf_file; -+ if (!d_is_reg(file->f_path.dentry) -+ || !(file->f_mode & FMODE_WRITE) -+ || hfile->hf_br->br_id != br_id -+ || !(hf->f_mode & FMODE_WRITE)) -+ array[ull] = NULL; -+ else { -+ do_warn = 1; -+ get_file(file); -+ } -+ -+ FiMustNoWaiters(file); -+ fi_read_unlock(file); -+ fput(file); -+ } -+ -+ err = 0; -+ if (do_warn) -+ au_warn_ima(); -+ -+ for (ull = 0; ull < max; ull++) { -+ file = array[ull]; -+ if (!file) -+ continue; -+ -+ /* todo: already flushed? */ -+ /* -+ * fs/super.c:mark_files_ro() is gone, but aufs keeps its -+ * approach which resets f_mode and calls mnt_drop_write() and -+ * file_release_write() for each file, because the branch -+ * attribute in aufs world is totally different from the native -+ * fs rw/ro mode. -+ */ -+ /* fi_read_lock(file); */ -+ hfile = &au_fi(file)->fi_htop; -+ hf = hfile->hf_file; -+ /* fi_read_unlock(file); */ -+ spin_lock(&hf->f_lock); -+ writer = !!(hf->f_mode & FMODE_WRITER); -+ hf->f_mode &= ~(FMODE_WRITE | FMODE_WRITER); -+ spin_unlock(&hf->f_lock); -+ if (writer) { -+ put_write_access(file_inode(hf)); -+ __mnt_drop_write(hf->f_path.mnt); -+ } -+ } -+ -+out_array: -+ au_farray_free(array, max); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, -+ int *do_refresh) -+{ -+ int err, rerr; -+ aufs_bindex_t bindex; -+ struct dentry *root; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ root = sb->s_root; -+ bindex = au_find_dbindex(root, mod->h_root); -+ if (bindex < 0) { -+ if (remount) -+ return 0; /* success */ -+ err = -ENOENT; -+ pr_err("%s no such branch\n", mod->path); -+ goto out; -+ } -+ AuDbg("bindex b%d\n", bindex); -+ -+ err = test_br(mod->h_root->d_inode, mod->perm, mod->path); -+ if (unlikely(err)) -+ goto out; -+ -+ br = au_sbr(sb, bindex); -+ AuDebugOn(mod->h_root != au_br_dentry(br)); -+ if (br->br_perm == mod->perm) -+ return 0; /* success */ -+ -+ /* pre-allocate for non-fhsm --> fhsm */ -+ bf = NULL; -+ if (!au_br_fhsm(br->br_perm) && au_br_fhsm(mod->perm)) { -+ err = au_fhsm_br_alloc(br); -+ if (unlikely(err)) -+ goto out; -+ bf = br->br_fhsm; -+ br->br_fhsm = NULL; -+ } -+ -+ if (au_br_writable(br->br_perm)) { -+ /* remove whiteout base */ -+ err = au_br_init_wh(sb, br, mod->perm); -+ if (unlikely(err)) -+ goto out_bf; -+ -+ if (!au_br_writable(mod->perm)) { -+ /* rw --> ro, file might be mmapped */ -+ DiMustNoWaiters(root); -+ IiMustNoWaiters(root->d_inode); -+ di_write_unlock(root); -+ err = au_br_mod_files_ro(sb, bindex); -+ /* aufs_write_lock() calls ..._child() */ -+ di_write_lock_child(root); -+ -+ if (unlikely(err)) { -+ rerr = -ENOMEM; -+ br->br_wbr = kmalloc(sizeof(*br->br_wbr), -+ GFP_NOFS); -+ if (br->br_wbr) -+ rerr = au_wbr_init(br, sb, br->br_perm); -+ if (unlikely(rerr)) { -+ AuIOErr("nested error %d (%d)\n", -+ rerr, err); -+ br->br_perm = mod->perm; -+ } -+ } -+ } -+ } else if (au_br_writable(mod->perm)) { -+ /* ro --> rw */ -+ err = -ENOMEM; -+ br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS); -+ if (br->br_wbr) { -+ err = au_wbr_init(br, sb, mod->perm); -+ if (unlikely(err)) { -+ kfree(br->br_wbr); -+ br->br_wbr = NULL; -+ } -+ } -+ } -+ if (unlikely(err)) -+ goto out_bf; -+ -+ if (au_br_fhsm(br->br_perm)) { -+ if (!au_br_fhsm(mod->perm)) { -+ /* fhsm --> non-fhsm */ -+ au_br_fhsm_fin(br->br_fhsm); -+ kfree(br->br_fhsm); -+ br->br_fhsm = NULL; -+ } -+ } else if (au_br_fhsm(mod->perm)) -+ /* non-fhsm --> fhsm */ -+ br->br_fhsm = bf; -+ -+ *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); -+ br->br_perm = mod->perm; -+ goto out; /* success */ -+ -+out_bf: -+ kfree(bf); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs) -+{ -+ int err; -+ struct kstatfs kstfs; -+ -+ err = vfs_statfs(&br->br_path, &kstfs); -+ if (!err) { -+ stfs->f_blocks = kstfs.f_blocks; -+ stfs->f_bavail = kstfs.f_bavail; -+ stfs->f_files = kstfs.f_files; -+ stfs->f_ffree = kstfs.f_ffree; -+ } -+ -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/branch.h linux-4.0.4/fs/aufs/branch.h ---- linux-4.0.4.orig/fs/aufs/branch.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/branch.h 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,266 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * branch filesystems and xino for them -+ */ -+ -+#ifndef __AUFS_BRANCH_H__ -+#define __AUFS_BRANCH_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "dynop.h" -+#include "rwsem.h" -+#include "super.h" -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* a xino file */ -+struct au_xino_file { -+ struct file *xi_file; -+ struct mutex xi_nondir_mtx; -+ -+ /* todo: make xino files an array to support huge inode number */ -+ -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *xi_dbgaufs; -+#endif -+}; -+ -+/* File-based Hierarchical Storage Management */ -+struct au_br_fhsm { -+#ifdef CONFIG_AUFS_FHSM -+ struct mutex bf_lock; -+ unsigned long bf_jiffy; -+ struct aufs_stfs bf_stfs; -+ int bf_readable; -+#endif -+}; -+ -+/* members for writable branch only */ -+enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last}; -+struct au_wbr { -+ struct au_rwsem wbr_wh_rwsem; -+ struct dentry *wbr_wh[AuBrWh_Last]; -+ atomic_t wbr_wh_running; -+#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */ -+#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */ -+#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */ -+ -+ /* mfs mode */ -+ unsigned long long wbr_bytes; -+}; -+ -+/* ext2 has 3 types of operations at least, ext3 has 4 */ -+#define AuBrDynOp (AuDyLast * 4) -+ -+#ifdef CONFIG_AUFS_HFSNOTIFY -+/* support for asynchronous destruction */ -+struct au_br_hfsnotify { -+ struct fsnotify_group *hfsn_group; -+}; -+#endif -+ -+/* sysfs entries */ -+struct au_brsysfs { -+ char name[16]; -+ struct attribute attr; -+}; -+ -+enum { -+ AuBrSysfs_BR, -+ AuBrSysfs_BRID, -+ AuBrSysfs_Last -+}; -+ -+/* protected by superblock rwsem */ -+struct au_branch { -+ struct au_xino_file br_xino; -+ -+ aufs_bindex_t br_id; -+ -+ int br_perm; -+ struct path br_path; -+ spinlock_t br_dykey_lock; -+ struct au_dykey *br_dykey[AuBrDynOp]; -+ atomic_t br_count; -+ -+ struct au_wbr *br_wbr; -+ struct au_br_fhsm *br_fhsm; -+ -+ /* xino truncation */ -+ atomic_t br_xino_running; -+ -+#ifdef CONFIG_AUFS_HFSNOTIFY -+ struct au_br_hfsnotify *br_hfsn; -+#endif -+ -+#ifdef CONFIG_SYSFS -+ /* entries under sysfs per mount-point */ -+ struct au_brsysfs br_sysfs[AuBrSysfs_Last]; -+#endif -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct vfsmount *au_br_mnt(struct au_branch *br) -+{ -+ return br->br_path.mnt; -+} -+ -+static inline struct dentry *au_br_dentry(struct au_branch *br) -+{ -+ return br->br_path.dentry; -+} -+ -+static inline struct super_block *au_br_sb(struct au_branch *br) -+{ -+ return au_br_mnt(br)->mnt_sb; -+} -+ -+static inline int au_br_rdonly(struct au_branch *br) -+{ -+ return ((au_br_sb(br)->s_flags & MS_RDONLY) -+ || !au_br_writable(br->br_perm)) -+ ? -EROFS : 0; -+} -+ -+static inline int au_br_hnotifyable(int brperm __maybe_unused) -+{ -+#ifdef CONFIG_AUFS_HNOTIFY -+ return !(brperm & AuBrPerm_RR); -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_br_test_oflag(int oflag, struct au_branch *br) -+{ -+ int err, exec_flag; -+ -+ err = 0; -+ exec_flag = oflag & __FMODE_EXEC; -+ if (unlikely(exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC))) -+ err = -EACCES; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* branch.c */ -+struct au_sbinfo; -+void au_br_free(struct au_sbinfo *sinfo); -+int au_br_index(struct super_block *sb, aufs_bindex_t br_id); -+struct au_opt_add; -+int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); -+struct au_opt_del; -+int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); -+long au_ibusy_ioctl(struct file *file, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long au_ibusy_compat_ioctl(struct file *file, unsigned long arg); -+#endif -+struct au_opt_mod; -+int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, -+ int *do_refresh); -+struct aufs_stfs; -+int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs); -+ -+/* xino.c */ -+static const loff_t au_loff_max = LLONG_MAX; -+ -+int au_xib_trunc(struct super_block *sb); -+ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size, -+ loff_t *pos); -+ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, -+ loff_t *pos); -+struct file *au_xino_create2(struct file *base_file, struct file *copy_src); -+struct file *au_xino_create(struct super_block *sb, char *fname, int silent); -+ino_t au_xino_new_ino(struct super_block *sb); -+void au_xino_delete_inode(struct inode *inode, const int unlinked); -+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t ino); -+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t *ino); -+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino, -+ struct file *base_file, int do_test); -+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex); -+ -+struct au_opt_xino; -+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount); -+void au_xino_clr(struct super_block *sb); -+struct file *au_xino_def(struct super_block *sb); -+int au_xino_path(struct seq_file *seq, struct file *file); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* Superblock to branch */ -+static inline -+aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_sbr(sb, bindex)->br_id; -+} -+ -+static inline -+struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_br_mnt(au_sbr(sb, bindex)); -+} -+ -+static inline -+struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_br_sb(au_sbr(sb, bindex)); -+} -+ -+static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ atomic_dec(&au_sbr(sb, bindex)->br_count); -+} -+ -+static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_sbr(sb, bindex)->br_perm; -+} -+ -+static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ return au_br_whable(au_sbr_perm(sb, bindex)); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * wbr_wh_read_lock, wbr_wh_write_lock -+ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem); -+ -+#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&wbr->wbr_wh_rwsem) -+#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&wbr->wbr_wh_rwsem) -+#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&wbr->wbr_wh_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_FHSM -+static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm) -+{ -+ mutex_init(&brfhsm->bf_lock); -+ brfhsm->bf_jiffy = 0; -+ brfhsm->bf_readable = 0; -+} -+ -+static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm) -+{ -+ mutex_destroy(&brfhsm->bf_lock); -+} -+#else -+AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm) -+AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm) -+#endif -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_BRANCH_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/cpup.c linux-4.0.4/fs/aufs/cpup.c ---- linux-4.0.4.orig/fs/aufs/cpup.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/cpup.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,1295 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * copy-up functions, see wbr_policy.c for copy-down -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) -+{ -+ const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE -+ | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT; -+ -+ BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); -+ -+ dst->i_flags |= iflags & ~mask; -+ if (au_test_fs_notime(dst->i_sb)) -+ dst->i_flags |= S_NOATIME | S_NOCMTIME; -+} -+ -+void au_cpup_attr_timesizes(struct inode *inode) -+{ -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ fsstack_copy_attr_times(inode, h_inode); -+ fsstack_copy_inode_size(inode, h_inode); -+} -+ -+void au_cpup_attr_nlink(struct inode *inode, int force) -+{ -+ struct inode *h_inode; -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend; -+ -+ sb = inode->i_sb; -+ bindex = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bindex); -+ if (!force -+ && !S_ISDIR(h_inode->i_mode) -+ && au_opt_test(au_mntflags(sb), PLINK) -+ && au_plink_test(inode)) -+ return; -+ -+ /* -+ * 0 can happen in revalidating. -+ * h_inode->i_mutex may not be held here, but it is harmless since once -+ * i_nlink reaches 0, it will never become positive except O_TMPFILE -+ * case. -+ * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause -+ * the incorrect link count. -+ */ -+ set_nlink(inode, h_inode->i_nlink); -+ -+ /* -+ * fewer nlink makes find(1) noisy, but larger nlink doesn't. -+ * it may includes whplink directory. -+ */ -+ if (S_ISDIR(h_inode->i_mode)) { -+ bend = au_ibend(inode); -+ for (bindex++; bindex <= bend; bindex++) { -+ h_inode = au_h_iptr(inode, bindex); -+ if (h_inode) -+ au_add_nlink(inode, h_inode); -+ } -+ } -+} -+ -+void au_cpup_attr_changeable(struct inode *inode) -+{ -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ inode->i_mode = h_inode->i_mode; -+ inode->i_uid = h_inode->i_uid; -+ inode->i_gid = h_inode->i_gid; -+ au_cpup_attr_timesizes(inode); -+ au_cpup_attr_flags(inode, h_inode->i_flags); -+} -+ -+void au_cpup_igen(struct inode *inode, struct inode *h_inode) -+{ -+ struct au_iinfo *iinfo = au_ii(inode); -+ -+ IiMustWriteLock(inode); -+ -+ iinfo->ii_higen = h_inode->i_generation; -+ iinfo->ii_hsb1 = h_inode->i_sb; -+} -+ -+void au_cpup_attr_all(struct inode *inode, int force) -+{ -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ au_cpup_attr_changeable(inode); -+ if (inode->i_nlink > 0) -+ au_cpup_attr_nlink(inode, force); -+ inode->i_rdev = h_inode->i_rdev; -+ inode->i_blkbits = h_inode->i_blkbits; -+ au_cpup_igen(inode, h_inode); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */ -+ -+/* keep the timestamps of the parent dir when cpup */ -+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, -+ struct path *h_path) -+{ -+ struct inode *h_inode; -+ -+ dt->dt_dentry = dentry; -+ dt->dt_h_path = *h_path; -+ h_inode = h_path->dentry->d_inode; -+ dt->dt_atime = h_inode->i_atime; -+ dt->dt_mtime = h_inode->i_mtime; -+ /* smp_mb(); */ -+} -+ -+void au_dtime_revert(struct au_dtime *dt) -+{ -+ struct iattr attr; -+ int err; -+ -+ attr.ia_atime = dt->dt_atime; -+ attr.ia_mtime = dt->dt_mtime; -+ attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET -+ | ATTR_ATIME | ATTR_ATIME_SET; -+ -+ /* no delegation since this is a directory */ -+ err = vfsub_notify_change(&dt->dt_h_path, &attr, /*delegated*/NULL); -+ if (unlikely(err)) -+ pr_warn("restoring timestamps failed(%d). ignored\n", err); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* internal use only */ -+struct au_cpup_reg_attr { -+ int valid; -+ struct kstat st; -+ unsigned int iflags; /* inode->i_flags */ -+}; -+ -+static noinline_for_stack -+int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, -+ struct au_cpup_reg_attr *h_src_attr) -+{ -+ int err, sbits, icex; -+ unsigned int mnt_flags; -+ unsigned char verbose; -+ struct iattr ia; -+ struct path h_path; -+ struct inode *h_isrc, *h_idst; -+ struct kstat *h_st; -+ struct au_branch *br; -+ -+ h_path.dentry = au_h_dptr(dst, bindex); -+ h_idst = h_path.dentry->d_inode; -+ br = au_sbr(dst->d_sb, bindex); -+ h_path.mnt = au_br_mnt(br); -+ h_isrc = h_src->d_inode; -+ ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID -+ | ATTR_ATIME | ATTR_MTIME -+ | ATTR_ATIME_SET | ATTR_MTIME_SET; -+ if (h_src_attr && h_src_attr->valid) { -+ h_st = &h_src_attr->st; -+ ia.ia_uid = h_st->uid; -+ ia.ia_gid = h_st->gid; -+ ia.ia_atime = h_st->atime; -+ ia.ia_mtime = h_st->mtime; -+ if (h_idst->i_mode != h_st->mode -+ && !S_ISLNK(h_idst->i_mode)) { -+ ia.ia_valid |= ATTR_MODE; -+ ia.ia_mode = h_st->mode; -+ } -+ sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_idst, h_src_attr->iflags); -+ } else { -+ ia.ia_uid = h_isrc->i_uid; -+ ia.ia_gid = h_isrc->i_gid; -+ ia.ia_atime = h_isrc->i_atime; -+ ia.ia_mtime = h_isrc->i_mtime; -+ if (h_idst->i_mode != h_isrc->i_mode -+ && !S_ISLNK(h_idst->i_mode)) { -+ ia.ia_valid |= ATTR_MODE; -+ ia.ia_mode = h_isrc->i_mode; -+ } -+ sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_idst, h_isrc->i_flags); -+ } -+ /* no delegation since it is just created */ -+ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); -+ -+ /* is this nfs only? */ -+ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { -+ ia.ia_valid = ATTR_FORCE | ATTR_MODE; -+ ia.ia_mode = h_isrc->i_mode; -+ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); -+ } -+ -+ icex = br->br_perm & AuBrAttr_ICEX; -+ if (!err) { -+ mnt_flags = au_mntflags(dst->d_sb); -+ verbose = !!au_opt_test(mnt_flags, VERBOSE); -+ err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, -+ char *buf, unsigned long blksize) -+{ -+ int err; -+ size_t sz, rbytes, wbytes; -+ unsigned char all_zero; -+ char *p, *zp; -+ struct mutex *h_mtx; -+ /* reduce stack usage */ -+ struct iattr *ia; -+ -+ zp = page_address(ZERO_PAGE(0)); -+ if (unlikely(!zp)) -+ return -ENOMEM; /* possible? */ -+ -+ err = 0; -+ all_zero = 0; -+ while (len) { -+ AuDbg("len %lld\n", len); -+ sz = blksize; -+ if (len < blksize) -+ sz = len; -+ -+ rbytes = 0; -+ /* todo: signal_pending? */ -+ while (!rbytes || err == -EAGAIN || err == -EINTR) { -+ rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); -+ err = rbytes; -+ } -+ if (unlikely(err < 0)) -+ break; -+ -+ all_zero = 0; -+ if (len >= rbytes && rbytes == blksize) -+ all_zero = !memcmp(buf, zp, rbytes); -+ if (!all_zero) { -+ wbytes = rbytes; -+ p = buf; -+ while (wbytes) { -+ size_t b; -+ -+ b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); -+ err = b; -+ /* todo: signal_pending? */ -+ if (unlikely(err == -EAGAIN || err == -EINTR)) -+ continue; -+ if (unlikely(err < 0)) -+ break; -+ wbytes -= b; -+ p += b; -+ } -+ if (unlikely(err < 0)) -+ break; -+ } else { -+ loff_t res; -+ -+ AuLabel(hole); -+ res = vfsub_llseek(dst, rbytes, SEEK_CUR); -+ err = res; -+ if (unlikely(res < 0)) -+ break; -+ } -+ len -= rbytes; -+ err = 0; -+ } -+ -+ /* the last block may be a hole */ -+ if (!err && all_zero) { -+ AuLabel(last hole); -+ -+ err = 1; -+ if (au_test_nfs(dst->f_path.dentry->d_sb)) { -+ /* nfs requires this step to make last hole */ -+ /* is this only nfs? */ -+ do { -+ /* todo: signal_pending? */ -+ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); -+ } while (err == -EAGAIN || err == -EINTR); -+ if (err == 1) -+ dst->f_pos--; -+ } -+ -+ if (err == 1) { -+ ia = (void *)buf; -+ ia->ia_size = dst->f_pos; -+ ia->ia_valid = ATTR_SIZE | ATTR_FILE; -+ ia->ia_file = dst; -+ h_mtx = &file_inode(dst)->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); -+ /* no delegation since it is just created */ -+ err = vfsub_notify_change(&dst->f_path, ia, -+ /*delegated*/NULL); -+ mutex_unlock(h_mtx); -+ } -+ } -+ -+ return err; -+} -+ -+int au_copy_file(struct file *dst, struct file *src, loff_t len) -+{ -+ int err; -+ unsigned long blksize; -+ unsigned char do_kfree; -+ char *buf; -+ -+ err = -ENOMEM; -+ blksize = dst->f_path.dentry->d_sb->s_blocksize; -+ if (!blksize || PAGE_SIZE < blksize) -+ blksize = PAGE_SIZE; -+ AuDbg("blksize %lu\n", blksize); -+ do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *)); -+ if (do_kfree) -+ buf = kmalloc(blksize, GFP_NOFS); -+ else -+ buf = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!buf)) -+ goto out; -+ -+ if (len > (1 << 22)) -+ AuDbg("copying a large file %lld\n", (long long)len); -+ -+ src->f_pos = 0; -+ dst->f_pos = 0; -+ err = au_do_copy_file(dst, src, len, buf, blksize); -+ if (do_kfree) -+ kfree(buf); -+ else -+ free_page((unsigned long)buf); -+ -+out: -+ return err; -+} -+ -+/* -+ * to support a sparse file which is opened with O_APPEND, -+ * we need to close the file. -+ */ -+static int au_cp_regular(struct au_cp_generic *cpg) -+{ -+ int err, i; -+ enum { SRC, DST }; -+ struct { -+ aufs_bindex_t bindex; -+ unsigned int flags; -+ struct dentry *dentry; -+ int force_wr; -+ struct file *file; -+ void *label; -+ } *f, file[] = { -+ { -+ .bindex = cpg->bsrc, -+ .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, -+ .label = &&out -+ }, -+ { -+ .bindex = cpg->bdst, -+ .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, -+ .force_wr = !!au_ftest_cpup(cpg->flags, RWDST), -+ .label = &&out_src -+ } -+ }; -+ struct super_block *sb; -+ -+ /* bsrc branch can be ro/rw. */ -+ sb = cpg->dentry->d_sb; -+ f = file; -+ for (i = 0; i < 2; i++, f++) { -+ f->dentry = au_h_dptr(cpg->dentry, f->bindex); -+ f->file = au_h_open(cpg->dentry, f->bindex, f->flags, -+ /*file*/NULL, f->force_wr); -+ err = PTR_ERR(f->file); -+ if (IS_ERR(f->file)) -+ goto *f->label; -+ } -+ -+ /* try stopping to update while we copyup */ -+ IMustLock(file[SRC].dentry->d_inode); -+ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len); -+ -+ fput(file[DST].file); -+ au_sbr_put(sb, file[DST].bindex); -+ -+out_src: -+ fput(file[SRC].file); -+ au_sbr_put(sb, file[SRC].bindex); -+out: -+ return err; -+} -+ -+static int au_do_cpup_regular(struct au_cp_generic *cpg, -+ struct au_cpup_reg_attr *h_src_attr) -+{ -+ int err, rerr; -+ loff_t l; -+ struct path h_path; -+ struct inode *h_src_inode, *h_dst_inode; -+ -+ err = 0; -+ h_src_inode = au_h_iptr(cpg->dentry->d_inode, cpg->bsrc); -+ l = i_size_read(h_src_inode); -+ if (cpg->len == -1 || l < cpg->len) -+ cpg->len = l; -+ if (cpg->len) { -+ /* try stopping to update while we are referencing */ -+ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); -+ au_pin_hdir_unlock(cpg->pin); -+ -+ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc); -+ h_path.mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc); -+ h_src_attr->iflags = h_src_inode->i_flags; -+ err = vfs_getattr(&h_path, &h_src_attr->st); -+ if (unlikely(err)) { -+ mutex_unlock(&h_src_inode->i_mutex); -+ goto out; -+ } -+ h_src_attr->valid = 1; -+ err = au_cp_regular(cpg); -+ mutex_unlock(&h_src_inode->i_mutex); -+ rerr = au_pin_hdir_relock(cpg->pin); -+ if (!err && rerr) -+ err = rerr; -+ } -+ if (!err && (h_src_inode->i_state & I_LINKABLE)) { -+ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bdst); -+ h_dst_inode = h_path.dentry->d_inode; -+ spin_lock(&h_dst_inode->i_lock); -+ h_dst_inode->i_state |= I_LINKABLE; -+ spin_unlock(&h_dst_inode->i_lock); -+ } -+ -+out: -+ return err; -+} -+ -+static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src, -+ struct inode *h_dir) -+{ -+ int err, symlen; -+ mm_segment_t old_fs; -+ union { -+ char *k; -+ char __user *u; -+ } sym; -+ -+ err = -ENOSYS; -+ if (unlikely(!h_src->d_inode->i_op->readlink)) -+ goto out; -+ -+ err = -ENOMEM; -+ sym.k = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!sym.k)) -+ goto out; -+ -+ /* unnecessary to support mmap_sem since symlink is not mmap-able */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ symlen = h_src->d_inode->i_op->readlink(h_src, sym.u, PATH_MAX); -+ err = symlen; -+ set_fs(old_fs); -+ -+ if (symlen > 0) { -+ sym.k[symlen] = 0; -+ err = vfsub_symlink(h_dir, h_path, sym.k); -+ } -+ free_page((unsigned long)sym.k); -+ -+out: -+ return err; -+} -+ -+static noinline_for_stack -+int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent, -+ struct au_cpup_reg_attr *h_src_attr) -+{ -+ int err; -+ umode_t mode; -+ unsigned int mnt_flags; -+ unsigned char isdir, isreg, force; -+ const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME); -+ struct au_dtime dt; -+ struct path h_path; -+ struct dentry *h_src, *h_dst, *h_parent; -+ struct inode *h_inode, *h_dir; -+ struct super_block *sb; -+ -+ /* bsrc branch can be ro/rw. */ -+ h_src = au_h_dptr(cpg->dentry, cpg->bsrc); -+ h_inode = h_src->d_inode; -+ AuDebugOn(h_inode != au_h_iptr(cpg->dentry->d_inode, cpg->bsrc)); -+ -+ /* try stopping to be referenced while we are creating */ -+ h_dst = au_h_dptr(cpg->dentry, cpg->bdst); -+ if (au_ftest_cpup(cpg->flags, RENAME)) -+ AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, -+ AUFS_WH_PFX_LEN)); -+ h_parent = h_dst->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ AuDebugOn(h_parent != h_dst->d_parent); -+ -+ sb = cpg->dentry->d_sb; -+ h_path.mnt = au_sbr_mnt(sb, cpg->bdst); -+ if (do_dt) { -+ h_path.dentry = h_parent; -+ au_dtime_store(&dt, dst_parent, &h_path); -+ } -+ h_path.dentry = h_dst; -+ -+ isreg = 0; -+ isdir = 0; -+ mode = h_inode->i_mode; -+ switch (mode & S_IFMT) { -+ case S_IFREG: -+ isreg = 1; -+ err = vfsub_create(h_dir, &h_path, mode | S_IWUSR, -+ /*want_excl*/true); -+ if (!err) -+ err = au_do_cpup_regular(cpg, h_src_attr); -+ break; -+ case S_IFDIR: -+ isdir = 1; -+ err = vfsub_mkdir(h_dir, &h_path, mode); -+ if (!err) { -+ /* -+ * strange behaviour from the users view, -+ * particularry setattr case -+ */ -+ if (au_ibstart(dst_parent->d_inode) == cpg->bdst) -+ au_cpup_attr_nlink(dst_parent->d_inode, -+ /*force*/1); -+ au_cpup_attr_nlink(cpg->dentry->d_inode, /*force*/1); -+ } -+ break; -+ case S_IFLNK: -+ err = au_do_cpup_symlink(&h_path, h_src, h_dir); -+ break; -+ case S_IFCHR: -+ case S_IFBLK: -+ AuDebugOn(!capable(CAP_MKNOD)); -+ /*FALLTHROUGH*/ -+ case S_IFIFO: -+ case S_IFSOCK: -+ err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev); -+ break; -+ default: -+ AuIOErr("Unknown inode type 0%o\n", mode); -+ err = -EIO; -+ } -+ -+ mnt_flags = au_mntflags(sb); -+ if (!au_opt_test(mnt_flags, UDBA_NONE) -+ && !isdir -+ && au_opt_test(mnt_flags, XINO) -+ && (h_inode->i_nlink == 1 -+ || (h_inode->i_state & I_LINKABLE)) -+ /* todo: unnecessary? */ -+ /* && cpg->dentry->d_inode->i_nlink == 1 */ -+ && cpg->bdst < cpg->bsrc -+ && !au_ftest_cpup(cpg->flags, KEEPLINO)) -+ au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0); -+ /* ignore this error */ -+ -+ if (!err) { -+ force = 0; -+ if (isreg) { -+ force = !!cpg->len; -+ if (cpg->len == -1) -+ force = !!i_size_read(h_inode); -+ } -+ au_fhsm_wrote(sb, cpg->bdst, force); -+ } -+ -+ if (do_dt) -+ au_dtime_revert(&dt); -+ return err; -+} -+ -+static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path) -+{ -+ int err; -+ struct dentry *dentry, *h_dentry, *h_parent, *parent; -+ struct inode *h_dir; -+ aufs_bindex_t bdst; -+ -+ dentry = cpg->dentry; -+ bdst = cpg->bdst; -+ h_dentry = au_h_dptr(dentry, bdst); -+ if (!au_ftest_cpup(cpg->flags, OVERWRITE)) { -+ dget(h_dentry); -+ au_set_h_dptr(dentry, bdst, NULL); -+ err = au_lkup_neg(dentry, bdst, /*wh*/0); -+ if (!err) -+ h_path->dentry = dget(au_h_dptr(dentry, bdst)); -+ au_set_h_dptr(dentry, bdst, h_dentry); -+ } else { -+ err = 0; -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bdst); -+ dput(parent); -+ h_path->dentry = vfsub_lkup_one(&dentry->d_name, h_parent); -+ if (IS_ERR(h_path->dentry)) -+ err = PTR_ERR(h_path->dentry); -+ } -+ if (unlikely(err)) -+ goto out; -+ -+ h_parent = h_dentry->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ AuDbg("%pd %pd\n", h_dentry, h_path->dentry); -+ /* no delegation since it is just created */ -+ err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL); -+ dput(h_path->dentry); -+ -+out: -+ return err; -+} -+ -+/* -+ * copyup the @dentry from @bsrc to @bdst. -+ * the caller must set the both of lower dentries. -+ * @len is for truncating when it is -1 copyup the entire file. -+ * in link/rename cases, @dst_parent may be different from the real one. -+ * basic->bsrc can be larger than basic->bdst. -+ */ -+static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) -+{ -+ int err, rerr; -+ aufs_bindex_t old_ibstart; -+ unsigned char isdir, plink; -+ struct dentry *h_src, *h_dst, *h_parent; -+ struct inode *dst_inode, *h_dir, *inode, *delegated; -+ struct super_block *sb; -+ struct au_branch *br; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct path h_path; -+ struct au_cpup_reg_attr h_src_attr; -+ } *a; -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ a->h_src_attr.valid = 0; -+ -+ sb = cpg->dentry->d_sb; -+ br = au_sbr(sb, cpg->bdst); -+ a->h_path.mnt = au_br_mnt(br); -+ h_dst = au_h_dptr(cpg->dentry, cpg->bdst); -+ h_parent = h_dst->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ -+ h_src = au_h_dptr(cpg->dentry, cpg->bsrc); -+ inode = cpg->dentry->d_inode; -+ -+ if (!dst_parent) -+ dst_parent = dget_parent(cpg->dentry); -+ else -+ dget(dst_parent); -+ -+ plink = !!au_opt_test(au_mntflags(sb), PLINK); -+ dst_inode = au_h_iptr(inode, cpg->bdst); -+ if (dst_inode) { -+ if (unlikely(!plink)) { -+ err = -EIO; -+ AuIOErr("hi%lu(i%lu) exists on b%d " -+ "but plink is disabled\n", -+ dst_inode->i_ino, inode->i_ino, cpg->bdst); -+ goto out_parent; -+ } -+ -+ if (dst_inode->i_nlink) { -+ const int do_dt = au_ftest_cpup(cpg->flags, DTIME); -+ -+ h_src = au_plink_lkup(inode, cpg->bdst); -+ err = PTR_ERR(h_src); -+ if (IS_ERR(h_src)) -+ goto out_parent; -+ if (unlikely(!h_src->d_inode)) { -+ err = -EIO; -+ AuIOErr("i%lu exists on a upper branch " -+ "but not pseudo-linked\n", -+ inode->i_ino); -+ dput(h_src); -+ goto out_parent; -+ } -+ -+ if (do_dt) { -+ a->h_path.dentry = h_parent; -+ au_dtime_store(&a->dt, dst_parent, &a->h_path); -+ } -+ -+ a->h_path.dentry = h_dst; -+ delegated = NULL; -+ err = vfsub_link(h_src, h_dir, &a->h_path, &delegated); -+ if (!err && au_ftest_cpup(cpg->flags, RENAME)) -+ err = au_do_ren_after_cpup(cpg, &a->h_path); -+ if (do_dt) -+ au_dtime_revert(&a->dt); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ dput(h_src); -+ goto out_parent; -+ } else -+ /* todo: cpup_wh_file? */ -+ /* udba work */ -+ au_update_ibrange(inode, /*do_put_zero*/1); -+ } -+ -+ isdir = S_ISDIR(inode->i_mode); -+ old_ibstart = au_ibstart(inode); -+ err = cpup_entry(cpg, dst_parent, &a->h_src_attr); -+ if (unlikely(err)) -+ goto out_rev; -+ dst_inode = h_dst->d_inode; -+ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); -+ /* todo: necessary? */ -+ /* au_pin_hdir_unlock(cpg->pin); */ -+ -+ err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr); -+ if (unlikely(err)) { -+ /* todo: necessary? */ -+ /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ -+ mutex_unlock(&dst_inode->i_mutex); -+ goto out_rev; -+ } -+ -+ if (cpg->bdst < old_ibstart) { -+ if (S_ISREG(inode->i_mode)) { -+ err = au_dy_iaop(inode, cpg->bdst, dst_inode); -+ if (unlikely(err)) { -+ /* ignore an error */ -+ /* au_pin_hdir_relock(cpg->pin); */ -+ mutex_unlock(&dst_inode->i_mutex); -+ goto out_rev; -+ } -+ } -+ au_set_ibstart(inode, cpg->bdst); -+ } else -+ au_set_ibend(inode, cpg->bdst); -+ au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), -+ au_hi_flags(inode, isdir)); -+ -+ /* todo: necessary? */ -+ /* err = au_pin_hdir_relock(cpg->pin); */ -+ mutex_unlock(&dst_inode->i_mutex); -+ if (unlikely(err)) -+ goto out_rev; -+ -+ if (!isdir -+ && (h_src->d_inode->i_nlink > 1 -+ || h_src->d_inode->i_state & I_LINKABLE) -+ && plink) -+ au_plink_append(inode, cpg->bdst, h_dst); -+ -+ if (au_ftest_cpup(cpg->flags, RENAME)) { -+ a->h_path.dentry = h_dst; -+ err = au_do_ren_after_cpup(cpg, &a->h_path); -+ } -+ if (!err) -+ goto out_parent; /* success */ -+ -+ /* revert */ -+out_rev: -+ a->h_path.dentry = h_parent; -+ au_dtime_store(&a->dt, dst_parent, &a->h_path); -+ a->h_path.dentry = h_dst; -+ rerr = 0; -+ if (h_dst->d_inode) { -+ if (!isdir) { -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(h_dir, &a->h_path, -+ /*delegated*/NULL, /*force*/0); -+ } else -+ rerr = vfsub_rmdir(h_dir, &a->h_path); -+ } -+ au_dtime_revert(&a->dt); -+ if (rerr) { -+ AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); -+ err = -EIO; -+ } -+out_parent: -+ dput(dst_parent); -+ kfree(a); -+out: -+ return err; -+} -+ -+#if 0 /* reserved */ -+struct au_cpup_single_args { -+ int *errp; -+ struct au_cp_generic *cpg; -+ struct dentry *dst_parent; -+}; -+ -+static void au_call_cpup_single(void *args) -+{ -+ struct au_cpup_single_args *a = args; -+ -+ au_pin_hdir_acquire_nest(a->cpg->pin); -+ *a->errp = au_cpup_single(a->cpg, a->dst_parent); -+ au_pin_hdir_release(a->cpg->pin); -+} -+#endif -+ -+/* -+ * prevent SIGXFSZ in copy-up. -+ * testing CAP_MKNOD is for generic fs, -+ * but CAP_FSETID is for xfs only, currently. -+ */ -+static int au_cpup_sio_test(struct au_pin *pin, umode_t mode) -+{ -+ int do_sio; -+ struct super_block *sb; -+ struct inode *h_dir; -+ -+ do_sio = 0; -+ sb = au_pinned_parent(pin)->d_sb; -+ if (!au_wkq_test() -+ && (!au_sbi(sb)->si_plink_maint_pid -+ || au_plink_maint(sb, AuLock_NOPLM))) { -+ switch (mode & S_IFMT) { -+ case S_IFREG: -+ /* no condition about RLIMIT_FSIZE and the file size */ -+ do_sio = 1; -+ break; -+ case S_IFCHR: -+ case S_IFBLK: -+ do_sio = !capable(CAP_MKNOD); -+ break; -+ } -+ if (!do_sio) -+ do_sio = ((mode & (S_ISUID | S_ISGID)) -+ && !capable(CAP_FSETID)); -+ /* this workaround may be removed in the future */ -+ if (!do_sio) { -+ h_dir = au_pinned_h_dir(pin); -+ do_sio = h_dir->i_mode & S_ISVTX; -+ } -+ } -+ -+ return do_sio; -+} -+ -+#if 0 /* reserved */ -+int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) -+{ -+ int err, wkq_err; -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc); -+ if (!au_cpup_sio_test(pin, h_dentry->d_inode->i_mode)) -+ err = au_cpup_single(cpg, dst_parent); -+ else { -+ struct au_cpup_single_args args = { -+ .errp = &err, -+ .cpg = cpg, -+ .dst_parent = dst_parent -+ }; -+ wkq_err = au_wkq_wait(au_call_cpup_single, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -+#endif -+ -+/* -+ * copyup the @dentry from the first active lower branch to @bdst, -+ * using au_cpup_single(). -+ */ -+static int au_cpup_simple(struct au_cp_generic *cpg) -+{ -+ int err; -+ unsigned int flags_orig; -+ struct dentry *dentry; -+ -+ AuDebugOn(cpg->bsrc < 0); -+ -+ dentry = cpg->dentry; -+ DiMustWriteLock(dentry); -+ -+ err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1); -+ if (!err) { -+ flags_orig = cpg->flags; -+ au_fset_cpup(cpg->flags, RENAME); -+ err = au_cpup_single(cpg, NULL); -+ cpg->flags = flags_orig; -+ if (!err) -+ return 0; /* success */ -+ -+ /* revert */ -+ au_set_h_dptr(dentry, cpg->bdst, NULL); -+ au_set_dbstart(dentry, cpg->bsrc); -+ } -+ -+ return err; -+} -+ -+struct au_cpup_simple_args { -+ int *errp; -+ struct au_cp_generic *cpg; -+}; -+ -+static void au_call_cpup_simple(void *args) -+{ -+ struct au_cpup_simple_args *a = args; -+ -+ au_pin_hdir_acquire_nest(a->cpg->pin); -+ *a->errp = au_cpup_simple(a->cpg); -+ au_pin_hdir_release(a->cpg->pin); -+} -+ -+static int au_do_sio_cpup_simple(struct au_cp_generic *cpg) -+{ -+ int err, wkq_err; -+ struct dentry *dentry, *parent; -+ struct file *h_file; -+ struct inode *h_dir; -+ -+ dentry = cpg->dentry; -+ h_file = NULL; -+ if (au_ftest_cpup(cpg->flags, HOPEN)) { -+ AuDebugOn(cpg->bsrc < 0); -+ h_file = au_h_open_pre(dentry, cpg->bsrc, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ } -+ -+ parent = dget_parent(dentry); -+ h_dir = au_h_iptr(parent->d_inode, cpg->bdst); -+ if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) -+ && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode)) -+ err = au_cpup_simple(cpg); -+ else { -+ struct au_cpup_simple_args args = { -+ .errp = &err, -+ .cpg = cpg -+ }; -+ wkq_err = au_wkq_wait(au_call_cpup_simple, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ dput(parent); -+ if (h_file) -+ au_h_open_post(dentry, cpg->bsrc, h_file); -+ -+out: -+ return err; -+} -+ -+int au_sio_cpup_simple(struct au_cp_generic *cpg) -+{ -+ aufs_bindex_t bsrc, bend; -+ struct dentry *dentry, *h_dentry; -+ -+ if (cpg->bsrc < 0) { -+ dentry = cpg->dentry; -+ bend = au_dbend(dentry); -+ for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) { -+ h_dentry = au_h_dptr(dentry, bsrc); -+ if (h_dentry) { -+ AuDebugOn(!h_dentry->d_inode); -+ break; -+ } -+ } -+ AuDebugOn(bsrc > bend); -+ cpg->bsrc = bsrc; -+ } -+ AuDebugOn(cpg->bsrc <= cpg->bdst); -+ return au_do_sio_cpup_simple(cpg); -+} -+ -+int au_sio_cpdown_simple(struct au_cp_generic *cpg) -+{ -+ AuDebugOn(cpg->bdst <= cpg->bsrc); -+ return au_do_sio_cpup_simple(cpg); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * copyup the deleted file for writing. -+ */ -+static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry, -+ struct file *file) -+{ -+ int err; -+ unsigned int flags_orig; -+ aufs_bindex_t bsrc_orig; -+ struct dentry *h_d_dst, *h_d_start; -+ struct au_dinfo *dinfo; -+ struct au_hdentry *hdp; -+ -+ dinfo = au_di(cpg->dentry); -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ bsrc_orig = cpg->bsrc; -+ cpg->bsrc = dinfo->di_bstart; -+ hdp = dinfo->di_hdentry; -+ h_d_dst = hdp[0 + cpg->bdst].hd_dentry; -+ dinfo->di_bstart = cpg->bdst; -+ hdp[0 + cpg->bdst].hd_dentry = wh_dentry; -+ h_d_start = NULL; -+ if (file) { -+ h_d_start = hdp[0 + cpg->bsrc].hd_dentry; -+ hdp[0 + cpg->bsrc].hd_dentry = au_hf_top(file)->f_path.dentry; -+ } -+ flags_orig = cpg->flags; -+ cpg->flags = !AuCpup_DTIME; -+ err = au_cpup_single(cpg, /*h_parent*/NULL); -+ cpg->flags = flags_orig; -+ if (file) { -+ if (!err) -+ err = au_reopen_nondir(file); -+ hdp[0 + cpg->bsrc].hd_dentry = h_d_start; -+ } -+ hdp[0 + cpg->bdst].hd_dentry = h_d_dst; -+ dinfo->di_bstart = cpg->bsrc; -+ cpg->bsrc = bsrc_orig; -+ -+ return err; -+} -+ -+static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file) -+{ -+ int err; -+ aufs_bindex_t bdst; -+ struct au_dtime dt; -+ struct dentry *dentry, *parent, *h_parent, *wh_dentry; -+ struct au_branch *br; -+ struct path h_path; -+ -+ dentry = cpg->dentry; -+ bdst = cpg->bdst; -+ br = au_sbr(dentry->d_sb, bdst); -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bdst); -+ wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out; -+ -+ h_path.dentry = h_parent; -+ h_path.mnt = au_br_mnt(br); -+ au_dtime_store(&dt, parent, &h_path); -+ err = au_do_cpup_wh(cpg, wh_dentry, file); -+ if (unlikely(err)) -+ goto out_wh; -+ -+ dget(wh_dentry); -+ h_path.dentry = wh_dentry; -+ if (!d_is_dir(wh_dentry)) { -+ /* no delegation since it is just created */ -+ err = vfsub_unlink(h_parent->d_inode, &h_path, -+ /*delegated*/NULL, /*force*/0); -+ } else -+ err = vfsub_rmdir(h_parent->d_inode, &h_path); -+ if (unlikely(err)) { -+ AuIOErr("failed remove copied-up tmp file %pd(%d)\n", -+ wh_dentry, err); -+ err = -EIO; -+ } -+ au_dtime_revert(&dt); -+ au_set_hi_wh(dentry->d_inode, bdst, wh_dentry); -+ -+out_wh: -+ dput(wh_dentry); -+out: -+ dput(parent); -+ return err; -+} -+ -+struct au_cpup_wh_args { -+ int *errp; -+ struct au_cp_generic *cpg; -+ struct file *file; -+}; -+ -+static void au_call_cpup_wh(void *args) -+{ -+ struct au_cpup_wh_args *a = args; -+ -+ au_pin_hdir_acquire_nest(a->cpg->pin); -+ *a->errp = au_cpup_wh(a->cpg, a->file); -+ au_pin_hdir_release(a->cpg->pin); -+} -+ -+int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file) -+{ -+ int err, wkq_err; -+ aufs_bindex_t bdst; -+ struct dentry *dentry, *parent, *h_orph, *h_parent; -+ struct inode *dir, *h_dir, *h_tmpdir; -+ struct au_wbr *wbr; -+ struct au_pin wh_pin, *pin_orig; -+ -+ dentry = cpg->dentry; -+ bdst = cpg->bdst; -+ parent = dget_parent(dentry); -+ dir = parent->d_inode; -+ h_orph = NULL; -+ h_parent = NULL; -+ h_dir = au_igrab(au_h_iptr(dir, bdst)); -+ h_tmpdir = h_dir; -+ pin_orig = NULL; -+ if (!h_dir->i_nlink) { -+ wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; -+ h_orph = wbr->wbr_orph; -+ -+ h_parent = dget(au_h_dptr(parent, bdst)); -+ au_set_h_dptr(parent, bdst, dget(h_orph)); -+ h_tmpdir = h_orph->d_inode; -+ au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); -+ -+ mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); -+ /* todo: au_h_open_pre()? */ -+ -+ pin_orig = cpg->pin; -+ au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, -+ AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED); -+ cpg->pin = &wh_pin; -+ } -+ -+ if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) -+ && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode)) -+ err = au_cpup_wh(cpg, file); -+ else { -+ struct au_cpup_wh_args args = { -+ .errp = &err, -+ .cpg = cpg, -+ .file = file -+ }; -+ wkq_err = au_wkq_wait(au_call_cpup_wh, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ if (h_orph) { -+ mutex_unlock(&h_tmpdir->i_mutex); -+ /* todo: au_h_open_post()? */ -+ au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); -+ au_set_h_dptr(parent, bdst, h_parent); -+ AuDebugOn(!pin_orig); -+ cpg->pin = pin_orig; -+ } -+ iput(h_dir); -+ dput(parent); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * generic routine for both of copy-up and copy-down. -+ */ -+/* cf. revalidate function in file.c */ -+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, -+ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent, void *arg), -+ void *arg) -+{ -+ int err; -+ struct au_pin pin; -+ struct dentry *d, *parent, *h_parent, *real_parent; -+ -+ err = 0; -+ parent = dget_parent(dentry); -+ if (IS_ROOT(parent)) -+ goto out; -+ -+ au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2, -+ au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); -+ -+ /* do not use au_dpage */ -+ real_parent = parent; -+ while (1) { -+ dput(parent); -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bdst); -+ if (h_parent) -+ goto out; /* success */ -+ -+ /* find top dir which is necessary to cpup */ -+ do { -+ d = parent; -+ dput(parent); -+ parent = dget_parent(d); -+ di_read_lock_parent3(parent, !AuLock_IR); -+ h_parent = au_h_dptr(parent, bdst); -+ di_read_unlock(parent, !AuLock_IR); -+ } while (!h_parent); -+ -+ if (d != real_parent) -+ di_write_lock_child3(d); -+ -+ /* somebody else might create while we were sleeping */ -+ if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) { -+ if (au_h_dptr(d, bdst)) -+ au_update_dbstart(d); -+ -+ au_pin_set_dentry(&pin, d); -+ err = au_do_pin(&pin); -+ if (!err) { -+ err = cp(d, bdst, &pin, h_parent, arg); -+ au_unpin(&pin); -+ } -+ } -+ -+ if (d != real_parent) -+ di_write_unlock(d); -+ if (unlikely(err)) -+ break; -+ } -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent __maybe_unused, -+ void *arg __maybe_unused) -+{ -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = bdst, -+ .bsrc = -1, -+ .len = 0, -+ .pin = pin, -+ .flags = AuCpup_DTIME -+ }; -+ return au_sio_cpup_simple(&cpg); -+} -+ -+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) -+{ -+ return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL); -+} -+ -+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) -+{ -+ int err; -+ struct dentry *parent; -+ struct inode *dir; -+ -+ parent = dget_parent(dentry); -+ dir = parent->d_inode; -+ err = 0; -+ if (au_h_iptr(dir, bdst)) -+ goto out; -+ -+ di_read_unlock(parent, AuLock_IR); -+ di_write_lock_parent(parent); -+ /* someone else might change our inode while we were sleeping */ -+ if (!au_h_iptr(dir, bdst)) -+ err = au_cpup_dirs(dentry, bdst); -+ di_downgrade_lock(parent, AuLock_IR); -+ -+out: -+ dput(parent); -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/cpup.h linux-4.0.4/fs/aufs/cpup.h ---- linux-4.0.4.orig/fs/aufs/cpup.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/cpup.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,81 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * copy-up/down functions -+ */ -+ -+#ifndef __AUFS_CPUP_H__ -+#define __AUFS_CPUP_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+struct inode; -+struct file; -+struct au_pin; -+ -+void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); -+void au_cpup_attr_timesizes(struct inode *inode); -+void au_cpup_attr_nlink(struct inode *inode, int force); -+void au_cpup_attr_changeable(struct inode *inode); -+void au_cpup_igen(struct inode *inode, struct inode *h_inode); -+void au_cpup_attr_all(struct inode *inode, int force); -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_cp_generic { -+ struct dentry *dentry; -+ aufs_bindex_t bdst, bsrc; -+ loff_t len; -+ struct au_pin *pin; -+ unsigned int flags; -+}; -+ -+/* cpup flags */ -+#define AuCpup_DTIME 1 /* do dtime_store/revert */ -+#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, -+ for link(2) */ -+#define AuCpup_RENAME (1 << 2) /* rename after cpup */ -+#define AuCpup_HOPEN (1 << 3) /* call h_open_pre/post() in -+ cpup */ -+#define AuCpup_OVERWRITE (1 << 4) /* allow overwriting the -+ existing entry */ -+#define AuCpup_RWDST (1 << 5) /* force write target even if -+ the branch is marked as RO */ -+ -+#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) -+#define au_fset_cpup(flags, name) \ -+ do { (flags) |= AuCpup_##name; } while (0) -+#define au_fclr_cpup(flags, name) \ -+ do { (flags) &= ~AuCpup_##name; } while (0) -+ -+int au_copy_file(struct file *dst, struct file *src, loff_t len); -+int au_sio_cpup_simple(struct au_cp_generic *cpg); -+int au_sio_cpdown_simple(struct au_cp_generic *cpg); -+int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file); -+ -+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, -+ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent, void *arg), -+ void *arg); -+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); -+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* keep timestamps when copyup */ -+struct au_dtime { -+ struct dentry *dt_dentry; -+ struct path dt_h_path; -+ struct timespec dt_atime, dt_mtime; -+}; -+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, -+ struct path *h_path); -+void au_dtime_revert(struct au_dtime *dt); -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_CPUP_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/dbgaufs.c linux-4.0.4/fs/aufs/dbgaufs.c ---- linux-4.0.4.orig/fs/aufs/dbgaufs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dbgaufs.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,419 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * debugfs interface -+ */ -+ -+#include -+#include "aufs.h" -+ -+#ifndef CONFIG_SYSFS -+#error DEBUG_FS depends upon SYSFS -+#endif -+ -+static struct dentry *dbgaufs; -+static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH; -+ -+/* 20 is max digits length of ulong 64 */ -+struct dbgaufs_arg { -+ int n; -+ char a[20 * 4]; -+}; -+ -+/* -+ * common function for all XINO files -+ */ -+static int dbgaufs_xi_release(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ kfree(file->private_data); -+ return 0; -+} -+ -+static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt) -+{ -+ int err; -+ struct kstat st; -+ struct dbgaufs_arg *p; -+ -+ err = -ENOMEM; -+ p = kmalloc(sizeof(*p), GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ -+ err = 0; -+ p->n = 0; -+ file->private_data = p; -+ if (!xf) -+ goto out; -+ -+ err = vfs_getattr(&xf->f_path, &st); -+ if (!err) { -+ if (do_fcnt) -+ p->n = snprintf -+ (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n", -+ (long)file_count(xf), st.blocks, st.blksize, -+ (long long)st.size); -+ else -+ p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n", -+ st.blocks, st.blksize, -+ (long long)st.size); -+ AuDebugOn(p->n >= sizeof(p->a)); -+ } else { -+ p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); -+ err = 0; -+ } -+ -+out: -+ return err; -+ -+} -+ -+static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct dbgaufs_arg *p; -+ -+ p = file->private_data; -+ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dbgaufs_plink_arg { -+ int n; -+ char a[]; -+}; -+ -+static int dbgaufs_plink_release(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ free_page((unsigned long)file->private_data); -+ return 0; -+} -+ -+static int dbgaufs_plink_open(struct inode *inode, struct file *file) -+{ -+ int err, i, limit; -+ unsigned long n, sum; -+ struct dbgaufs_plink_arg *p; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ struct au_sphlhead *sphl; -+ -+ err = -ENOMEM; -+ p = (void *)get_zeroed_page(GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ -+ err = -EFBIG; -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ if (au_opt_test(au_mntflags(sb), PLINK)) { -+ limit = PAGE_SIZE - sizeof(p->n); -+ -+ /* the number of buckets */ -+ n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); -+ p->n += n; -+ limit -= n; -+ -+ sum = 0; -+ for (i = 0, sphl = sbinfo->si_plink; -+ i < AuPlink_NHASH; -+ i++, sphl++) { -+ n = au_sphl_count(sphl); -+ sum += n; -+ -+ n = snprintf(p->a + p->n, limit, "%lu ", n); -+ p->n += n; -+ limit -= n; -+ if (unlikely(limit <= 0)) -+ goto out_free; -+ } -+ p->a[p->n - 1] = '\n'; -+ -+ /* the sum of plinks */ -+ n = snprintf(p->a + p->n, limit, "%lu\n", sum); -+ p->n += n; -+ limit -= n; -+ if (unlikely(limit <= 0)) -+ goto out_free; -+ } else { -+#define str "1\n0\n0\n" -+ p->n = sizeof(str) - 1; -+ strcpy(p->a, str); -+#undef str -+ } -+ si_read_unlock(sb); -+ -+ err = 0; -+ file->private_data = p; -+ goto out; /* success */ -+ -+out_free: -+ free_page((unsigned long)p); -+out: -+ return err; -+} -+ -+static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct dbgaufs_plink_arg *p; -+ -+ p = file->private_data; -+ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); -+} -+ -+static const struct file_operations dbgaufs_plink_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_plink_open, -+ .release = dbgaufs_plink_release, -+ .read = dbgaufs_plink_read -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int dbgaufs_xib_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); -+ si_read_unlock(sb); -+ return err; -+} -+ -+static const struct file_operations dbgaufs_xib_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_xib_open, -+ .release = dbgaufs_xi_release, -+ .read = dbgaufs_xi_read -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define DbgaufsXi_PREFIX "xi" -+ -+static int dbgaufs_xino_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ long l; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ struct file *xf; -+ struct qstr *name; -+ -+ err = -ENOENT; -+ xf = NULL; -+ name = &file->f_path.dentry->d_name; -+ if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) -+ || memcmp(name->name, DbgaufsXi_PREFIX, -+ sizeof(DbgaufsXi_PREFIX) - 1))) -+ goto out; -+ err = kstrtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); -+ if (unlikely(err)) -+ goto out; -+ -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ if (l <= au_sbend(sb)) { -+ xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; -+ err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); -+ } else -+ err = -ENOENT; -+ si_read_unlock(sb); -+ -+out: -+ return err; -+} -+ -+static const struct file_operations dbgaufs_xino_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_xino_open, -+ .release = dbgaufs_xi_release, -+ .read = dbgaufs_xi_read -+}; -+ -+void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ aufs_bindex_t bend; -+ struct au_branch *br; -+ struct au_xino_file *xi; -+ -+ if (!au_sbi(sb)->si_dbgaufs) -+ return; -+ -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ xi = &br->br_xino; -+ debugfs_remove(xi->xi_dbgaufs); -+ xi->xi_dbgaufs = NULL; -+ } -+} -+ -+void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ struct au_sbinfo *sbinfo; -+ struct dentry *parent; -+ struct au_branch *br; -+ struct au_xino_file *xi; -+ aufs_bindex_t bend; -+ char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ -+ -+ sbinfo = au_sbi(sb); -+ parent = sbinfo->si_dbgaufs; -+ if (!parent) -+ return; -+ -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); -+ br = au_sbr(sb, bindex); -+ xi = &br->br_xino; -+ AuDebugOn(xi->xi_dbgaufs); -+ xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, -+ sbinfo, &dbgaufs_xino_fop); -+ /* ignore an error */ -+ if (unlikely(!xi->xi_dbgaufs)) -+ AuWarn1("failed %s under debugfs\n", name); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_EXPORT -+static int dbgaufs_xigen_open(struct inode *inode, struct file *file) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ -+ sbinfo = inode->i_private; -+ sb = sbinfo->si_sb; -+ si_noflush_read_lock(sb); -+ err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); -+ si_read_unlock(sb); -+ return err; -+} -+ -+static const struct file_operations dbgaufs_xigen_fop = { -+ .owner = THIS_MODULE, -+ .open = dbgaufs_xigen_open, -+ .release = dbgaufs_xi_release, -+ .read = dbgaufs_xi_read -+}; -+ -+static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) -+{ -+ int err; -+ -+ /* -+ * This function is a dynamic '__init' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+ -+ err = -EIO; -+ sbinfo->si_dbgaufs_xigen = debugfs_create_file -+ ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, -+ &dbgaufs_xigen_fop); -+ if (sbinfo->si_dbgaufs_xigen) -+ err = 0; -+ -+ return err; -+} -+#else -+static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) -+{ -+ return 0; -+} -+#endif /* CONFIG_AUFS_EXPORT */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+void dbgaufs_si_fin(struct au_sbinfo *sbinfo) -+{ -+ /* -+ * This function is a dynamic '__fin' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+ -+ debugfs_remove_recursive(sbinfo->si_dbgaufs); -+ sbinfo->si_dbgaufs = NULL; -+ kobject_put(&sbinfo->si_kobj); -+} -+ -+int dbgaufs_si_init(struct au_sbinfo *sbinfo) -+{ -+ int err; -+ char name[SysaufsSiNameLen]; -+ -+ /* -+ * This function is a dynamic '__init' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+ -+ err = -ENOENT; -+ if (!dbgaufs) { -+ AuErr1("/debug/aufs is uninitialized\n"); -+ goto out; -+ } -+ -+ err = -EIO; -+ sysaufs_name(sbinfo, name); -+ sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); -+ if (unlikely(!sbinfo->si_dbgaufs)) -+ goto out; -+ kobject_get(&sbinfo->si_kobj); -+ -+ sbinfo->si_dbgaufs_xib = debugfs_create_file -+ ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, -+ &dbgaufs_xib_fop); -+ if (unlikely(!sbinfo->si_dbgaufs_xib)) -+ goto out_dir; -+ -+ sbinfo->si_dbgaufs_plink = debugfs_create_file -+ ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, -+ &dbgaufs_plink_fop); -+ if (unlikely(!sbinfo->si_dbgaufs_plink)) -+ goto out_dir; -+ -+ err = dbgaufs_xigen_init(sbinfo); -+ if (!err) -+ goto out; /* success */ -+ -+out_dir: -+ dbgaufs_si_fin(sbinfo); -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void dbgaufs_fin(void) -+{ -+ debugfs_remove(dbgaufs); -+} -+ -+int __init dbgaufs_init(void) -+{ -+ int err; -+ -+ err = -EIO; -+ dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); -+ if (dbgaufs) -+ err = 0; -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/dbgaufs.h linux-4.0.4/fs/aufs/dbgaufs.h ---- linux-4.0.4.orig/fs/aufs/dbgaufs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dbgaufs.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,35 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * debugfs interface -+ */ -+ -+#ifndef __DBGAUFS_H__ -+#define __DBGAUFS_H__ -+ -+#ifdef __KERNEL__ -+ -+struct super_block; -+struct au_sbinfo; -+ -+#ifdef CONFIG_DEBUG_FS -+/* dbgaufs.c */ -+void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); -+void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); -+void dbgaufs_si_fin(struct au_sbinfo *sbinfo); -+int dbgaufs_si_init(struct au_sbinfo *sbinfo); -+void dbgaufs_fin(void); -+int __init dbgaufs_init(void); -+#else -+AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo) -+AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo) -+AuStubVoid(dbgaufs_fin, void) -+AuStubInt0(__init dbgaufs_init, void) -+#endif /* CONFIG_DEBUG_FS */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __DBGAUFS_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/dcsub.c linux-4.0.4/fs/aufs/dcsub.c ---- linux-4.0.4.orig/fs/aufs/dcsub.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dcsub.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,211 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * sub-routines for dentry cache -+ */ -+ -+#include "aufs.h" -+ -+static void au_dpage_free(struct au_dpage *dpage) -+{ -+ int i; -+ struct dentry **p; -+ -+ p = dpage->dentries; -+ for (i = 0; i < dpage->ndentry; i++) -+ dput(*p++); -+ free_page((unsigned long)dpage->dentries); -+} -+ -+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) -+{ -+ int err; -+ void *p; -+ -+ err = -ENOMEM; -+ dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp); -+ if (unlikely(!dpages->dpages)) -+ goto out; -+ -+ p = (void *)__get_free_page(gfp); -+ if (unlikely(!p)) -+ goto out_dpages; -+ -+ dpages->dpages[0].ndentry = 0; -+ dpages->dpages[0].dentries = p; -+ dpages->ndpage = 1; -+ return 0; /* success */ -+ -+out_dpages: -+ kfree(dpages->dpages); -+out: -+ return err; -+} -+ -+void au_dpages_free(struct au_dcsub_pages *dpages) -+{ -+ int i; -+ struct au_dpage *p; -+ -+ p = dpages->dpages; -+ for (i = 0; i < dpages->ndpage; i++) -+ au_dpage_free(p++); -+ kfree(dpages->dpages); -+} -+ -+static int au_dpages_append(struct au_dcsub_pages *dpages, -+ struct dentry *dentry, gfp_t gfp) -+{ -+ int err, sz; -+ struct au_dpage *dpage; -+ void *p; -+ -+ dpage = dpages->dpages + dpages->ndpage - 1; -+ sz = PAGE_SIZE / sizeof(dentry); -+ if (unlikely(dpage->ndentry >= sz)) { -+ AuLabel(new dpage); -+ err = -ENOMEM; -+ sz = dpages->ndpage * sizeof(*dpages->dpages); -+ p = au_kzrealloc(dpages->dpages, sz, -+ sz + sizeof(*dpages->dpages), gfp); -+ if (unlikely(!p)) -+ goto out; -+ -+ dpages->dpages = p; -+ dpage = dpages->dpages + dpages->ndpage; -+ p = (void *)__get_free_page(gfp); -+ if (unlikely(!p)) -+ goto out; -+ -+ dpage->ndentry = 0; -+ dpage->dentries = p; -+ dpages->ndpage++; -+ } -+ -+ AuDebugOn(au_dcount(dentry) <= 0); -+ dpage->dentries[dpage->ndentry++] = dget_dlock(dentry); -+ return 0; /* success */ -+ -+out: -+ return err; -+} -+ -+/* todo: BAD approach */ -+/* copied from linux/fs/dcache.c */ -+enum d_walk_ret { -+ D_WALK_CONTINUE, -+ D_WALK_QUIT, -+ D_WALK_NORETRY, -+ D_WALK_SKIP, -+}; -+ -+extern void d_walk(struct dentry *parent, void *data, -+ enum d_walk_ret (*enter)(void *, struct dentry *), -+ void (*finish)(void *)); -+ -+struct ac_dpages_arg { -+ int err; -+ struct au_dcsub_pages *dpages; -+ struct super_block *sb; -+ au_dpages_test test; -+ void *arg; -+}; -+ -+static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry) -+{ -+ enum d_walk_ret ret; -+ struct ac_dpages_arg *arg = _arg; -+ -+ ret = D_WALK_CONTINUE; -+ if (dentry->d_sb == arg->sb -+ && !IS_ROOT(dentry) -+ && au_dcount(dentry) > 0 -+ && au_di(dentry) -+ && (!arg->test || arg->test(dentry, arg->arg))) { -+ arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC); -+ if (unlikely(arg->err)) -+ ret = D_WALK_QUIT; -+ } -+ -+ return ret; -+} -+ -+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, -+ au_dpages_test test, void *arg) -+{ -+ struct ac_dpages_arg args = { -+ .err = 0, -+ .dpages = dpages, -+ .sb = root->d_sb, -+ .test = test, -+ .arg = arg -+ }; -+ -+ d_walk(root, &args, au_call_dpages_append, NULL); -+ -+ return args.err; -+} -+ -+int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, -+ int do_include, au_dpages_test test, void *arg) -+{ -+ int err; -+ -+ err = 0; -+ write_seqlock(&rename_lock); -+ spin_lock(&dentry->d_lock); -+ if (do_include -+ && au_dcount(dentry) > 0 -+ && (!test || test(dentry, arg))) -+ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); -+ spin_unlock(&dentry->d_lock); -+ if (unlikely(err)) -+ goto out; -+ -+ /* -+ * RCU for vfsmount is unnecessary since this is a traverse in a single -+ * mount -+ */ -+ while (!IS_ROOT(dentry)) { -+ dentry = dentry->d_parent; /* rename_lock is locked */ -+ spin_lock(&dentry->d_lock); -+ if (au_dcount(dentry) > 0 -+ && (!test || test(dentry, arg))) -+ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); -+ spin_unlock(&dentry->d_lock); -+ if (unlikely(err)) -+ break; -+ } -+ -+out: -+ write_sequnlock(&rename_lock); -+ return err; -+} -+ -+static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg) -+{ -+ return au_di(dentry) && dentry->d_sb == arg; -+} -+ -+int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, -+ struct dentry *dentry, int do_include) -+{ -+ return au_dcsub_pages_rev(dpages, dentry, do_include, -+ au_dcsub_dpages_aufs, dentry->d_sb); -+} -+ -+int au_test_subdir(struct dentry *d1, struct dentry *d2) -+{ -+ struct path path[2] = { -+ { -+ .dentry = d1 -+ }, -+ { -+ .dentry = d2 -+ } -+ }; -+ -+ return path_is_under(path + 0, path + 1); -+} -diff -Nur linux-4.0.4.orig/fs/aufs/dcsub.h linux-4.0.4/fs/aufs/dcsub.h ---- linux-4.0.4.orig/fs/aufs/dcsub.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dcsub.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,119 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * sub-routines for dentry cache -+ */ -+ -+#ifndef __AUFS_DCSUB_H__ -+#define __AUFS_DCSUB_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+ -+struct au_dpage { -+ int ndentry; -+ struct dentry **dentries; -+}; -+ -+struct au_dcsub_pages { -+ int ndpage; -+ struct au_dpage *dpages; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dcsub.c */ -+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp); -+void au_dpages_free(struct au_dcsub_pages *dpages); -+typedef int (*au_dpages_test)(struct dentry *dentry, void *arg); -+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, -+ au_dpages_test test, void *arg); -+int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, -+ int do_include, au_dpages_test test, void *arg); -+int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, -+ struct dentry *dentry, int do_include); -+int au_test_subdir(struct dentry *d1, struct dentry *d2); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * todo: in linux-3.13, several similar (but faster) helpers are added to -+ * include/linux/dcache.h. Try them (in the future). -+ */ -+ -+static inline int au_d_hashed_positive(struct dentry *d) -+{ -+ int err; -+ struct inode *inode = d->d_inode; -+ -+ err = 0; -+ if (unlikely(d_unhashed(d) || !inode || !inode->i_nlink)) -+ err = -ENOENT; -+ return err; -+} -+ -+static inline int au_d_linkable(struct dentry *d) -+{ -+ int err; -+ struct inode *inode = d->d_inode; -+ -+ err = au_d_hashed_positive(d); -+ if (err -+ && inode -+ && (inode->i_state & I_LINKABLE)) -+ err = 0; -+ return err; -+} -+ -+static inline int au_d_alive(struct dentry *d) -+{ -+ int err; -+ struct inode *inode; -+ -+ err = 0; -+ if (!IS_ROOT(d)) -+ err = au_d_hashed_positive(d); -+ else { -+ inode = d->d_inode; -+ if (unlikely(d_unlinked(d) || !inode || !inode->i_nlink)) -+ err = -ENOENT; -+ } -+ return err; -+} -+ -+static inline int au_alive_dir(struct dentry *d) -+{ -+ int err; -+ -+ err = au_d_alive(d); -+ if (unlikely(err || IS_DEADDIR(d->d_inode))) -+ err = -ENOENT; -+ return err; -+} -+ -+static inline int au_qstreq(struct qstr *a, struct qstr *b) -+{ -+ return a->len == b->len -+ && !memcmp(a->name, b->name, a->len); -+} -+ -+/* -+ * by the commit -+ * 360f547 2015-01-25 dcache: let the dentry count go down to zero without -+ * taking d_lock -+ * the type of d_lockref.count became int, but the inlined function d_count() -+ * still returns unsigned int. -+ * I don't know why. Maybe it is for every d_count() users? -+ * Anyway au_dcount() lives on. -+ */ -+static inline int au_dcount(struct dentry *d) -+{ -+ return (int)d_count(d); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DCSUB_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/debug.c linux-4.0.4/fs/aufs/debug.c ---- linux-4.0.4.orig/fs/aufs/debug.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/debug.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,425 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * debug print functions -+ */ -+ -+#include "aufs.h" -+ -+/* Returns 0, or -errno. arg is in kp->arg. */ -+static int param_atomic_t_set(const char *val, const struct kernel_param *kp) -+{ -+ int err, n; -+ -+ err = kstrtoint(val, 0, &n); -+ if (!err) { -+ if (n > 0) -+ au_debug_on(); -+ else -+ au_debug_off(); -+ } -+ return err; -+} -+ -+/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ -+static int param_atomic_t_get(char *buffer, const struct kernel_param *kp) -+{ -+ atomic_t *a; -+ -+ a = kp->arg; -+ return sprintf(buffer, "%d", atomic_read(a)); -+} -+ -+static struct kernel_param_ops param_ops_atomic_t = { -+ .set = param_atomic_t_set, -+ .get = param_atomic_t_get -+ /* void (*free)(void *arg) */ -+}; -+ -+atomic_t aufs_debug = ATOMIC_INIT(0); -+MODULE_PARM_DESC(debug, "debug print"); -+module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP); -+ -+DEFINE_MUTEX(au_dbg_mtx); /* just to serialize the dbg msgs */ -+char *au_plevel = KERN_DEBUG; -+#define dpri(fmt, ...) do { \ -+ if ((au_plevel \ -+ && strcmp(au_plevel, KERN_DEBUG)) \ -+ || au_debug_test()) \ -+ printk("%s" fmt, au_plevel, ##__VA_ARGS__); \ -+} while (0) -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_dpri_whlist(struct au_nhash *whlist) -+{ -+ unsigned long ul, n; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ -+ n = whlist->nh_num; -+ head = whlist->nh_head; -+ for (ul = 0; ul < n; ul++) { -+ hlist_for_each_entry(pos, head, wh_hash) -+ dpri("b%d, %.*s, %d\n", -+ pos->wh_bindex, -+ pos->wh_str.len, pos->wh_str.name, -+ pos->wh_str.len); -+ head++; -+ } -+} -+ -+void au_dpri_vdir(struct au_vdir *vdir) -+{ -+ unsigned long ul; -+ union au_vdir_deblk_p p; -+ unsigned char *o; -+ -+ if (!vdir || IS_ERR(vdir)) { -+ dpri("err %ld\n", PTR_ERR(vdir)); -+ return; -+ } -+ -+ dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n", -+ vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, -+ vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); -+ for (ul = 0; ul < vdir->vd_nblk; ul++) { -+ p.deblk = vdir->vd_deblk[ul]; -+ o = p.deblk; -+ dpri("[%lu]: %p\n", ul, o); -+ } -+} -+ -+static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn, -+ struct dentry *wh) -+{ -+ char *n = NULL; -+ int l = 0; -+ -+ if (!inode || IS_ERR(inode)) { -+ dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); -+ return -1; -+ } -+ -+ /* the type of i_blocks depends upon CONFIG_LBDAF */ -+ BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) -+ && sizeof(inode->i_blocks) != sizeof(u64)); -+ if (wh) { -+ n = (void *)wh->d_name.name; -+ l = wh->d_name.len; -+ } -+ -+ dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," -+ " hn %d, ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x%s%.*s\n", -+ bindex, inode, -+ inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", -+ atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, -+ i_size_read(inode), (unsigned long long)inode->i_blocks, -+ hn, (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff, -+ inode->i_mapping ? inode->i_mapping->nrpages : 0, -+ inode->i_state, inode->i_flags, inode->i_version, -+ inode->i_generation, -+ l ? ", wh " : "", l, n); -+ return 0; -+} -+ -+void au_dpri_inode(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ aufs_bindex_t bindex; -+ int err, hn; -+ -+ err = do_pri_inode(-1, inode, -1, NULL); -+ if (err || !au_test_aufs(inode->i_sb)) -+ return; -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ dpri("i-1: bstart %d, bend %d, gen %d\n", -+ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL)); -+ if (iinfo->ii_bstart < 0) -+ return; -+ hn = 0; -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) { -+ hn = !!au_hn(iinfo->ii_hinode + bindex); -+ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, hn, -+ iinfo->ii_hinode[0 + bindex].hi_whdentry); -+ } -+} -+ -+void au_dpri_dalias(struct inode *inode) -+{ -+ struct dentry *d; -+ -+ spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) -+ au_dpri_dentry(d); -+ spin_unlock(&inode->i_lock); -+} -+ -+static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) -+{ -+ struct dentry *wh = NULL; -+ int hn; -+ struct au_iinfo *iinfo; -+ -+ if (!dentry || IS_ERR(dentry)) { -+ dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); -+ return -1; -+ } -+ /* do not call dget_parent() here */ -+ /* note: access d_xxx without d_lock */ -+ dpri("d%d: %p, %pd2?, %s, cnt %d, flags 0x%x, %shashed\n", -+ bindex, dentry, dentry, -+ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", -+ au_dcount(dentry), dentry->d_flags, -+ d_unhashed(dentry) ? "un" : ""); -+ hn = -1; -+ if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) { -+ iinfo = au_ii(dentry->d_inode); -+ if (iinfo) { -+ hn = !!au_hn(iinfo->ii_hinode + bindex); -+ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; -+ } -+ } -+ do_pri_inode(bindex, dentry->d_inode, hn, wh); -+ return 0; -+} -+ -+void au_dpri_dentry(struct dentry *dentry) -+{ -+ struct au_dinfo *dinfo; -+ aufs_bindex_t bindex; -+ int err; -+ struct au_hdentry *hdp; -+ -+ err = do_pri_dentry(-1, dentry); -+ if (err || !au_test_aufs(dentry->d_sb)) -+ return; -+ -+ dinfo = au_di(dentry); -+ if (!dinfo) -+ return; -+ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", -+ dinfo->di_bstart, dinfo->di_bend, -+ dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry), -+ dinfo->di_tmpfile); -+ if (dinfo->di_bstart < 0) -+ return; -+ hdp = dinfo->di_hdentry; -+ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) -+ do_pri_dentry(bindex, hdp[0 + bindex].hd_dentry); -+} -+ -+static int do_pri_file(aufs_bindex_t bindex, struct file *file) -+{ -+ char a[32]; -+ -+ if (!file || IS_ERR(file)) { -+ dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); -+ return -1; -+ } -+ a[0] = 0; -+ if (bindex < 0 -+ && !IS_ERR_OR_NULL(file->f_path.dentry) -+ && au_test_aufs(file->f_path.dentry->d_sb) -+ && au_fi(file)) -+ snprintf(a, sizeof(a), ", gen %d, mmapped %d", -+ au_figen(file), atomic_read(&au_fi(file)->fi_mmapped)); -+ dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n", -+ bindex, file->f_mode, file->f_flags, (long)file_count(file), -+ file->f_version, file->f_pos, a); -+ if (!IS_ERR_OR_NULL(file->f_path.dentry)) -+ do_pri_dentry(bindex, file->f_path.dentry); -+ return 0; -+} -+ -+void au_dpri_file(struct file *file) -+{ -+ struct au_finfo *finfo; -+ struct au_fidir *fidir; -+ struct au_hfile *hfile; -+ aufs_bindex_t bindex; -+ int err; -+ -+ err = do_pri_file(-1, file); -+ if (err -+ || IS_ERR_OR_NULL(file->f_path.dentry) -+ || !au_test_aufs(file->f_path.dentry->d_sb)) -+ return; -+ -+ finfo = au_fi(file); -+ if (!finfo) -+ return; -+ if (finfo->fi_btop < 0) -+ return; -+ fidir = finfo->fi_hdir; -+ if (!fidir) -+ do_pri_file(finfo->fi_btop, finfo->fi_htop.hf_file); -+ else -+ for (bindex = finfo->fi_btop; -+ bindex >= 0 && bindex <= fidir->fd_bbot; -+ bindex++) { -+ hfile = fidir->fd_hfile + bindex; -+ do_pri_file(bindex, hfile ? hfile->hf_file : NULL); -+ } -+} -+ -+static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) -+{ -+ struct vfsmount *mnt; -+ struct super_block *sb; -+ -+ if (!br || IS_ERR(br)) -+ goto out; -+ mnt = au_br_mnt(br); -+ if (!mnt || IS_ERR(mnt)) -+ goto out; -+ sb = mnt->mnt_sb; -+ if (!sb || IS_ERR(sb)) -+ goto out; -+ -+ dpri("s%d: {perm 0x%x, id %d, cnt %d, wbr %p}, " -+ "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " -+ "xino %d\n", -+ bindex, br->br_perm, br->br_id, atomic_read(&br->br_count), -+ br->br_wbr, au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), -+ sb->s_flags, sb->s_count, -+ atomic_read(&sb->s_active), !!br->br_xino.xi_file); -+ return 0; -+ -+out: -+ dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); -+ return -1; -+} -+ -+void au_dpri_sb(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ aufs_bindex_t bindex; -+ int err; -+ /* to reuduce stack size */ -+ struct { -+ struct vfsmount mnt; -+ struct au_branch fake; -+ } *a; -+ -+ /* this function can be called from magic sysrq */ -+ a = kzalloc(sizeof(*a), GFP_ATOMIC); -+ if (unlikely(!a)) { -+ dpri("no memory\n"); -+ return; -+ } -+ -+ a->mnt.mnt_sb = sb; -+ a->fake.br_perm = 0; -+ a->fake.br_path.mnt = &a->mnt; -+ a->fake.br_xino.xi_file = NULL; -+ atomic_set(&a->fake.br_count, 0); -+ smp_mb(); /* atomic_set */ -+ err = do_pri_br(-1, &a->fake); -+ kfree(a); -+ dpri("dev 0x%x\n", sb->s_dev); -+ if (err || !au_test_aufs(sb)) -+ return; -+ -+ sbinfo = au_sbi(sb); -+ if (!sbinfo) -+ return; -+ dpri("nw %d, gen %u, kobj %d\n", -+ atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, -+ atomic_read(&sbinfo->si_kobj.kref.refcount)); -+ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) -+ do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) -+{ -+ struct inode *h_inode, *inode = dentry->d_inode; -+ struct dentry *h_dentry; -+ aufs_bindex_t bindex, bend, bi; -+ -+ if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) -+ return; -+ -+ bend = au_dbend(dentry); -+ bi = au_ibend(inode); -+ if (bi < bend) -+ bend = bi; -+ bindex = au_dbstart(dentry); -+ bi = au_ibstart(inode); -+ if (bi > bindex) -+ bindex = bi; -+ -+ for (; bindex <= bend; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ h_inode = au_h_iptr(inode, bindex); -+ if (unlikely(h_inode != h_dentry->d_inode)) { -+ au_debug_on(); -+ AuDbg("b%d, %s:%d\n", bindex, func, line); -+ AuDbgDentry(dentry); -+ AuDbgInode(inode); -+ au_debug_off(); -+ BUG(); -+ } -+ } -+} -+ -+void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) -+{ -+ int err, i, j; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ AuDebugOn(err); -+ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); -+ AuDebugOn(err); -+ for (i = dpages.ndpage - 1; !err && i >= 0; i--) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ for (j = dpage->ndentry - 1; !err && j >= 0; j--) -+ AuDebugOn(au_digen_test(dentries[j], sigen)); -+ } -+ au_dpages_free(&dpages); -+} -+ -+void au_dbg_verify_kthread(void) -+{ -+ if (au_wkq_test()) { -+ au_dbg_blocked(); -+ /* -+ * It may be recursive, but udba=notify between two aufs mounts, -+ * where a single ro branch is shared, is not a problem. -+ */ -+ /* WARN_ON(1); */ -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int __init au_debug_init(void) -+{ -+ aufs_bindex_t bindex; -+ struct au_vdir_destr destr; -+ -+ bindex = -1; -+ AuDebugOn(bindex >= 0); -+ -+ destr.len = -1; -+ AuDebugOn(destr.len < NAME_MAX); -+ -+#ifdef CONFIG_4KSTACKS -+ pr_warn("CONFIG_4KSTACKS is defined.\n"); -+#endif -+ -+ return 0; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/debug.h linux-4.0.4/fs/aufs/debug.h ---- linux-4.0.4.orig/fs/aufs/debug.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/debug.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,212 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * debug print functions -+ */ -+ -+#ifndef __AUFS_DEBUG_H__ -+#define __AUFS_DEBUG_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include -+ -+#ifdef CONFIG_AUFS_DEBUG -+#define AuDebugOn(a) BUG_ON(a) -+ -+/* module parameter */ -+extern atomic_t aufs_debug; -+static inline void au_debug_on(void) -+{ -+ atomic_inc(&aufs_debug); -+} -+static inline void au_debug_off(void) -+{ -+ atomic_dec_if_positive(&aufs_debug); -+} -+ -+static inline int au_debug_test(void) -+{ -+ return atomic_read(&aufs_debug) > 0; -+} -+#else -+#define AuDebugOn(a) do {} while (0) -+AuStubVoid(au_debug_on, void) -+AuStubVoid(au_debug_off, void) -+AuStubInt0(au_debug_test, void) -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* debug print */ -+ -+#define AuDbg(fmt, ...) do { \ -+ if (au_debug_test()) \ -+ pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \ -+} while (0) -+#define AuLabel(l) AuDbg(#l "\n") -+#define AuIOErr(fmt, ...) pr_err("I/O Error, " fmt, ##__VA_ARGS__) -+#define AuWarn1(fmt, ...) do { \ -+ static unsigned char _c; \ -+ if (!_c++) \ -+ pr_warn(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+#define AuErr1(fmt, ...) do { \ -+ static unsigned char _c; \ -+ if (!_c++) \ -+ pr_err(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+#define AuIOErr1(fmt, ...) do { \ -+ static unsigned char _c; \ -+ if (!_c++) \ -+ AuIOErr(fmt, ##__VA_ARGS__); \ -+} while (0) -+ -+#define AuUnsupportMsg "This operation is not supported." \ -+ " Please report this application to aufs-users ML." -+#define AuUnsupport(fmt, ...) do { \ -+ pr_err(AuUnsupportMsg "\n" fmt, ##__VA_ARGS__); \ -+ dump_stack(); \ -+} while (0) -+ -+#define AuTraceErr(e) do { \ -+ if (unlikely((e) < 0)) \ -+ AuDbg("err %d\n", (int)(e)); \ -+} while (0) -+ -+#define AuTraceErrPtr(p) do { \ -+ if (IS_ERR(p)) \ -+ AuDbg("err %ld\n", PTR_ERR(p)); \ -+} while (0) -+ -+/* dirty macros for debug print, use with "%.*s" and caution */ -+#define AuLNPair(qstr) (qstr)->len, (qstr)->name -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dentry; -+#ifdef CONFIG_AUFS_DEBUG -+extern struct mutex au_dbg_mtx; -+extern char *au_plevel; -+struct au_nhash; -+void au_dpri_whlist(struct au_nhash *whlist); -+struct au_vdir; -+void au_dpri_vdir(struct au_vdir *vdir); -+struct inode; -+void au_dpri_inode(struct inode *inode); -+void au_dpri_dalias(struct inode *inode); -+void au_dpri_dentry(struct dentry *dentry); -+struct file; -+void au_dpri_file(struct file *filp); -+struct super_block; -+void au_dpri_sb(struct super_block *sb); -+ -+#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__) -+void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line); -+void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); -+void au_dbg_verify_kthread(void); -+ -+int __init au_debug_init(void); -+ -+#define AuDbgWhlist(w) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#w "\n"); \ -+ au_dpri_whlist(w); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgVdir(v) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#v "\n"); \ -+ au_dpri_vdir(v); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgInode(i) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#i "\n"); \ -+ au_dpri_inode(i); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgDAlias(i) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#i "\n"); \ -+ au_dpri_dalias(i); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgDentry(d) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#d "\n"); \ -+ au_dpri_dentry(d); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgFile(f) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#f "\n"); \ -+ au_dpri_file(f); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgSb(sb) do { \ -+ mutex_lock(&au_dbg_mtx); \ -+ AuDbg(#sb "\n"); \ -+ au_dpri_sb(sb); \ -+ mutex_unlock(&au_dbg_mtx); \ -+} while (0) -+ -+#define AuDbgSym(addr) do { \ -+ char sym[KSYM_SYMBOL_LEN]; \ -+ sprint_symbol(sym, (unsigned long)addr); \ -+ AuDbg("%s\n", sym); \ -+} while (0) -+#else -+AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry) -+AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen) -+AuStubVoid(au_dbg_verify_kthread, void) -+AuStubInt0(__init au_debug_init, void) -+ -+#define AuDbgWhlist(w) do {} while (0) -+#define AuDbgVdir(v) do {} while (0) -+#define AuDbgInode(i) do {} while (0) -+#define AuDbgDAlias(i) do {} while (0) -+#define AuDbgDentry(d) do {} while (0) -+#define AuDbgFile(f) do {} while (0) -+#define AuDbgSb(sb) do {} while (0) -+#define AuDbgSym(addr) do {} while (0) -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_MAGIC_SYSRQ -+int __init au_sysrq_init(void); -+void au_sysrq_fin(void); -+ -+#ifdef CONFIG_HW_CONSOLE -+#define au_dbg_blocked() do { \ -+ WARN_ON(1); \ -+ handle_sysrq('w'); \ -+} while (0) -+#else -+AuStubVoid(au_dbg_blocked, void) -+#endif -+ -+#else -+AuStubInt0(__init au_sysrq_init, void) -+AuStubVoid(au_sysrq_fin, void) -+AuStubVoid(au_dbg_blocked, void) -+#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DEBUG_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/dentry.c linux-4.0.4/fs/aufs/dentry.c ---- linux-4.0.4.orig/fs/aufs/dentry.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dentry.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,1084 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * lookup and dentry operations -+ */ -+ -+#include -+#include "aufs.h" -+ -+#define AuLkup_ALLOW_NEG 1 -+#define AuLkup_IGNORE_PERM (1 << 1) -+#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) -+#define au_fset_lkup(flags, name) \ -+ do { (flags) |= AuLkup_##name; } while (0) -+#define au_fclr_lkup(flags, name) \ -+ do { (flags) &= ~AuLkup_##name; } while (0) -+ -+struct au_do_lookup_args { -+ unsigned int flags; -+ mode_t type; -+}; -+ -+/* -+ * returns positive/negative dentry, NULL or an error. -+ * NULL means whiteout-ed or not-found. -+ */ -+static struct dentry* -+au_do_lookup(struct dentry *h_parent, struct dentry *dentry, -+ aufs_bindex_t bindex, struct qstr *wh_name, -+ struct au_do_lookup_args *args) -+{ -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ struct au_branch *br; -+ int wh_found, opq; -+ unsigned char wh_able; -+ const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); -+ const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, -+ IGNORE_PERM); -+ -+ wh_found = 0; -+ br = au_sbr(dentry->d_sb, bindex); -+ wh_able = !!au_br_whable(br->br_perm); -+ if (wh_able) -+ wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0); -+ h_dentry = ERR_PTR(wh_found); -+ if (!wh_found) -+ goto real_lookup; -+ if (unlikely(wh_found < 0)) -+ goto out; -+ -+ /* We found a whiteout */ -+ /* au_set_dbend(dentry, bindex); */ -+ au_set_dbwh(dentry, bindex); -+ if (!allow_neg) -+ return NULL; /* success */ -+ -+real_lookup: -+ if (!ignore_perm) -+ h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent); -+ else -+ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); -+ if (IS_ERR(h_dentry)) { -+ if (PTR_ERR(h_dentry) == -ENAMETOOLONG -+ && !allow_neg) -+ h_dentry = NULL; -+ goto out; -+ } -+ -+ h_inode = h_dentry->d_inode; -+ if (!h_inode) { -+ if (!allow_neg) -+ goto out_neg; -+ } else if (wh_found -+ || (args->type && args->type != (h_inode->i_mode & S_IFMT))) -+ goto out_neg; -+ -+ if (au_dbend(dentry) <= bindex) -+ au_set_dbend(dentry, bindex); -+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) -+ au_set_dbstart(dentry, bindex); -+ au_set_h_dptr(dentry, bindex, h_dentry); -+ -+ if (!d_is_dir(h_dentry) -+ || !wh_able -+ || (d_is_positive(dentry) && !d_is_dir(dentry))) -+ goto out; /* success */ -+ -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ opq = au_diropq_test(h_dentry); -+ mutex_unlock(&h_inode->i_mutex); -+ if (opq > 0) -+ au_set_dbdiropq(dentry, bindex); -+ else if (unlikely(opq < 0)) { -+ au_set_h_dptr(dentry, bindex, NULL); -+ h_dentry = ERR_PTR(opq); -+ } -+ goto out; -+ -+out_neg: -+ dput(h_dentry); -+ h_dentry = NULL; -+out: -+ return h_dentry; -+} -+ -+static int au_test_shwh(struct super_block *sb, const struct qstr *name) -+{ -+ if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) -+ && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) -+ return -EPERM; -+ return 0; -+} -+ -+/* -+ * returns the number of lower positive dentries, -+ * otherwise an error. -+ * can be called at unlinking with @type is zero. -+ */ -+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) -+{ -+ int npositive, err; -+ aufs_bindex_t bindex, btail, bdiropq; -+ unsigned char isdir, dirperm1; -+ struct qstr whname; -+ struct au_do_lookup_args args = { -+ .flags = 0, -+ .type = type -+ }; -+ const struct qstr *name = &dentry->d_name; -+ struct dentry *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ err = au_test_shwh(sb, name); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_wh_name_alloc(&whname, name); -+ if (unlikely(err)) -+ goto out; -+ -+ inode = dentry->d_inode; -+ isdir = !!d_is_dir(dentry); -+ if (!type) -+ au_fset_lkup(args.flags, ALLOW_NEG); -+ dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); -+ -+ npositive = 0; -+ parent = dget_parent(dentry); -+ btail = au_dbtaildir(parent); -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ struct dentry *h_parent, *h_dentry; -+ struct inode *h_inode, *h_dir; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry) { -+ if (h_dentry->d_inode) -+ npositive++; -+ if (type != S_IFDIR) -+ break; -+ continue; -+ } -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !d_is_dir(h_parent)) -+ continue; -+ -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, -+ &args); -+ mutex_unlock(&h_dir->i_mutex); -+ err = PTR_ERR(h_dentry); -+ if (IS_ERR(h_dentry)) -+ goto out_parent; -+ if (h_dentry) -+ au_fclr_lkup(args.flags, ALLOW_NEG); -+ if (dirperm1) -+ au_fset_lkup(args.flags, IGNORE_PERM); -+ -+ if (au_dbwh(dentry) >= 0) -+ break; -+ if (!h_dentry) -+ continue; -+ h_inode = h_dentry->d_inode; -+ if (!h_inode) -+ continue; -+ npositive++; -+ if (!args.type) -+ args.type = h_inode->i_mode & S_IFMT; -+ if (args.type != S_IFDIR) -+ break; -+ else if (isdir) { -+ /* the type of lower may be different */ -+ bdiropq = au_dbdiropq(dentry); -+ if (bdiropq >= 0 && bdiropq <= bindex) -+ break; -+ } -+ } -+ -+ if (npositive) { -+ AuLabel(positive); -+ au_update_dbstart(dentry); -+ } -+ err = npositive; -+ if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) -+ && au_dbstart(dentry) < 0)) { -+ err = -EIO; -+ AuIOErr("both of real entry and whiteout found, %pd, err %d\n", -+ dentry, err); -+ } -+ -+out_parent: -+ dput(parent); -+ kfree(whname.name); -+out: -+ return err; -+} -+ -+struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent) -+{ -+ struct dentry *dentry; -+ int wkq_err; -+ -+ if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC)) -+ dentry = vfsub_lkup_one(name, parent); -+ else { -+ struct vfsub_lkup_one_args args = { -+ .errp = &dentry, -+ .name = name, -+ .parent = parent -+ }; -+ -+ wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); -+ if (unlikely(wkq_err)) -+ dentry = ERR_PTR(wkq_err); -+ } -+ -+ return dentry; -+} -+ -+/* -+ * lookup @dentry on @bindex which should be negative. -+ */ -+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) -+{ -+ int err; -+ struct dentry *parent, *h_parent, *h_dentry; -+ struct au_branch *br; -+ -+ parent = dget_parent(dentry); -+ h_parent = au_h_dptr(parent, bindex); -+ br = au_sbr(dentry->d_sb, bindex); -+ if (wh) -+ h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); -+ else -+ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); -+ err = PTR_ERR(h_dentry); -+ if (IS_ERR(h_dentry)) -+ goto out; -+ if (unlikely(h_dentry->d_inode)) { -+ err = -EIO; -+ AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); -+ dput(h_dentry); -+ goto out; -+ } -+ -+ err = 0; -+ if (bindex < au_dbstart(dentry)) -+ au_set_dbstart(dentry, bindex); -+ if (au_dbend(dentry) < bindex) -+ au_set_dbend(dentry, bindex); -+ au_set_h_dptr(dentry, bindex, h_dentry); -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* subset of struct inode */ -+struct au_iattr { -+ unsigned long i_ino; -+ /* unsigned int i_nlink; */ -+ kuid_t i_uid; -+ kgid_t i_gid; -+ u64 i_version; -+/* -+ loff_t i_size; -+ blkcnt_t i_blocks; -+*/ -+ umode_t i_mode; -+}; -+ -+static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) -+{ -+ ia->i_ino = h_inode->i_ino; -+ /* ia->i_nlink = h_inode->i_nlink; */ -+ ia->i_uid = h_inode->i_uid; -+ ia->i_gid = h_inode->i_gid; -+ ia->i_version = h_inode->i_version; -+/* -+ ia->i_size = h_inode->i_size; -+ ia->i_blocks = h_inode->i_blocks; -+*/ -+ ia->i_mode = (h_inode->i_mode & S_IFMT); -+} -+ -+static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) -+{ -+ return ia->i_ino != h_inode->i_ino -+ /* || ia->i_nlink != h_inode->i_nlink */ -+ || !uid_eq(ia->i_uid, h_inode->i_uid) -+ || !gid_eq(ia->i_gid, h_inode->i_gid) -+ || ia->i_version != h_inode->i_version -+/* -+ || ia->i_size != h_inode->i_size -+ || ia->i_blocks != h_inode->i_blocks -+*/ -+ || ia->i_mode != (h_inode->i_mode & S_IFMT); -+} -+ -+static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, -+ struct au_branch *br) -+{ -+ int err; -+ struct au_iattr ia; -+ struct inode *h_inode; -+ struct dentry *h_d; -+ struct super_block *h_sb; -+ -+ err = 0; -+ memset(&ia, -1, sizeof(ia)); -+ h_sb = h_dentry->d_sb; -+ h_inode = h_dentry->d_inode; -+ if (h_inode) -+ au_iattr_save(&ia, h_inode); -+ else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) -+ /* nfs d_revalidate may return 0 for negative dentry */ -+ /* fuse d_revalidate always return 0 for negative dentry */ -+ goto out; -+ -+ /* main purpose is namei.c:cached_lookup() and d_revalidate */ -+ h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent); -+ err = PTR_ERR(h_d); -+ if (IS_ERR(h_d)) -+ goto out; -+ -+ err = 0; -+ if (unlikely(h_d != h_dentry -+ || h_d->d_inode != h_inode -+ || (h_inode && au_iattr_test(&ia, h_inode)))) -+ err = au_busy_or_stale(); -+ dput(h_d); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, -+ struct dentry *h_parent, struct au_branch *br) -+{ -+ int err; -+ -+ err = 0; -+ if (udba == AuOpt_UDBA_REVAL -+ && !au_test_fs_remote(h_dentry->d_sb)) { -+ IMustLock(h_dir); -+ err = (h_dentry->d_parent->d_inode != h_dir); -+ } else if (udba != AuOpt_UDBA_NONE) -+ err = au_h_verify_dentry(h_dentry, h_parent, br); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) -+{ -+ int err; -+ aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; -+ struct au_hdentry tmp, *p, *q; -+ struct au_dinfo *dinfo; -+ struct super_block *sb; -+ -+ DiMustWriteLock(dentry); -+ -+ sb = dentry->d_sb; -+ dinfo = au_di(dentry); -+ bend = dinfo->di_bend; -+ bwh = dinfo->di_bwh; -+ bdiropq = dinfo->di_bdiropq; -+ p = dinfo->di_hdentry + dinfo->di_bstart; -+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { -+ if (!p->hd_dentry) -+ continue; -+ -+ new_bindex = au_br_index(sb, p->hd_id); -+ if (new_bindex == bindex) -+ continue; -+ -+ if (dinfo->di_bwh == bindex) -+ bwh = new_bindex; -+ if (dinfo->di_bdiropq == bindex) -+ bdiropq = new_bindex; -+ if (new_bindex < 0) { -+ au_hdput(p); -+ p->hd_dentry = NULL; -+ continue; -+ } -+ -+ /* swap two lower dentries, and loop again */ -+ q = dinfo->di_hdentry + new_bindex; -+ tmp = *q; -+ *q = *p; -+ *p = tmp; -+ if (tmp.hd_dentry) { -+ bindex--; -+ p--; -+ } -+ } -+ -+ dinfo->di_bwh = -1; -+ if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) -+ dinfo->di_bwh = bwh; -+ -+ dinfo->di_bdiropq = -1; -+ if (bdiropq >= 0 -+ && bdiropq <= au_sbend(sb) -+ && au_sbr_whable(sb, bdiropq)) -+ dinfo->di_bdiropq = bdiropq; -+ -+ err = -EIO; -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ bend = au_dbend(parent); -+ p = dinfo->di_hdentry; -+ for (bindex = 0; bindex <= bend; bindex++, p++) -+ if (p->hd_dentry) { -+ dinfo->di_bstart = bindex; -+ break; -+ } -+ -+ if (dinfo->di_bstart >= 0) { -+ p = dinfo->di_hdentry + bend; -+ for (bindex = bend; bindex >= 0; bindex--, p--) -+ if (p->hd_dentry) { -+ dinfo->di_bend = bindex; -+ err = 0; -+ break; -+ } -+ } -+ -+ return err; -+} -+ -+static void au_do_hide(struct dentry *dentry) -+{ -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ if (inode) { -+ if (!S_ISDIR(inode->i_mode)) { -+ if (inode->i_nlink && !d_unhashed(dentry)) -+ drop_nlink(inode); -+ } else { -+ clear_nlink(inode); -+ /* stop next lookup */ -+ inode->i_flags |= S_DEAD; -+ } -+ smp_mb(); /* necessary? */ -+ } -+ d_drop(dentry); -+} -+ -+static int au_hide_children(struct dentry *parent) -+{ -+ int err, i, j, ndentry; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry *dentry; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, parent, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ /* in reverse order */ -+ for (i = dpages.ndpage - 1; i >= 0; i--) { -+ dpage = dpages.dpages + i; -+ ndentry = dpage->ndentry; -+ for (j = ndentry - 1; j >= 0; j--) { -+ dentry = dpage->dentries[j]; -+ if (dentry != parent) -+ au_do_hide(dentry); -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static void au_hide(struct dentry *dentry) -+{ -+ int err; -+ -+ AuDbgDentry(dentry); -+ if (d_is_dir(dentry)) { -+ /* shrink_dcache_parent(dentry); */ -+ err = au_hide_children(dentry); -+ if (unlikely(err)) -+ AuIOErr("%pd, failed hiding children, ignored %d\n", -+ dentry, err); -+ } -+ au_do_hide(dentry); -+} -+ -+/* -+ * By adding a dirty branch, a cached dentry may be affected in various ways. -+ * -+ * a dirty branch is added -+ * - on the top of layers -+ * - in the middle of layers -+ * - to the bottom of layers -+ * -+ * on the added branch there exists -+ * - a whiteout -+ * - a diropq -+ * - a same named entry -+ * + exist -+ * * negative --> positive -+ * * positive --> positive -+ * - type is unchanged -+ * - type is changed -+ * + doesn't exist -+ * * negative --> negative -+ * * positive --> negative (rejected by au_br_del() for non-dir case) -+ * - none -+ */ -+static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, -+ struct au_dinfo *tmp) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ struct { -+ struct dentry *dentry; -+ struct inode *inode; -+ mode_t mode; -+ } orig_h, tmp_h; -+ struct au_hdentry *hd; -+ struct inode *inode, *h_inode; -+ struct dentry *h_dentry; -+ -+ err = 0; -+ AuDebugOn(dinfo->di_bstart < 0); -+ orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; -+ orig_h.inode = orig_h.dentry->d_inode; -+ orig_h.mode = 0; -+ if (orig_h.inode) -+ orig_h.mode = orig_h.inode->i_mode & S_IFMT; -+ memset(&tmp_h, 0, sizeof(tmp_h)); -+ if (tmp->di_bstart >= 0) { -+ tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; -+ tmp_h.inode = tmp_h.dentry->d_inode; -+ if (tmp_h.inode) -+ tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; -+ } -+ -+ inode = dentry->d_inode; -+ if (!orig_h.inode) { -+ AuDbg("nagative originally\n"); -+ if (inode) { -+ au_hide(dentry); -+ goto out; -+ } -+ AuDebugOn(inode); -+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); -+ AuDebugOn(dinfo->di_bdiropq != -1); -+ -+ if (!tmp_h.inode) { -+ AuDbg("negative --> negative\n"); -+ /* should have only one negative lower */ -+ if (tmp->di_bstart >= 0 -+ && tmp->di_bstart < dinfo->di_bstart) { -+ AuDebugOn(tmp->di_bstart != tmp->di_bend); -+ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); -+ au_set_h_dptr(dentry, dinfo->di_bstart, NULL); -+ au_di_cp(dinfo, tmp); -+ hd = tmp->di_hdentry + tmp->di_bstart; -+ au_set_h_dptr(dentry, tmp->di_bstart, -+ dget(hd->hd_dentry)); -+ } -+ au_dbg_verify_dinode(dentry); -+ } else { -+ AuDbg("negative --> positive\n"); -+ /* -+ * similar to the behaviour of creating with bypassing -+ * aufs. -+ * unhash it in order to force an error in the -+ * succeeding create operation. -+ * we should not set S_DEAD here. -+ */ -+ d_drop(dentry); -+ /* au_di_swap(tmp, dinfo); */ -+ au_dbg_verify_dinode(dentry); -+ } -+ } else { -+ AuDbg("positive originally\n"); -+ /* inode may be NULL */ -+ AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); -+ if (!tmp_h.inode) { -+ AuDbg("positive --> negative\n"); -+ /* or bypassing aufs */ -+ au_hide(dentry); -+ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) -+ dinfo->di_bwh = tmp->di_bwh; -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ } else if (orig_h.mode == tmp_h.mode) { -+ AuDbg("positive --> positive, same type\n"); -+ if (!S_ISDIR(orig_h.mode) -+ && dinfo->di_bstart > tmp->di_bstart) { -+ /* -+ * similar to the behaviour of removing and -+ * creating. -+ */ -+ au_hide(dentry); -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ } else { -+ /* fill empty slots */ -+ if (dinfo->di_bstart > tmp->di_bstart) -+ dinfo->di_bstart = tmp->di_bstart; -+ if (dinfo->di_bend < tmp->di_bend) -+ dinfo->di_bend = tmp->di_bend; -+ dinfo->di_bwh = tmp->di_bwh; -+ dinfo->di_bdiropq = tmp->di_bdiropq; -+ hd = tmp->di_hdentry; -+ bend = dinfo->di_bend; -+ for (bindex = tmp->di_bstart; bindex <= bend; -+ bindex++) { -+ if (au_h_dptr(dentry, bindex)) -+ continue; -+ h_dentry = hd[bindex].hd_dentry; -+ if (!h_dentry) -+ continue; -+ h_inode = h_dentry->d_inode; -+ AuDebugOn(!h_inode); -+ AuDebugOn(orig_h.mode -+ != (h_inode->i_mode -+ & S_IFMT)); -+ au_set_h_dptr(dentry, bindex, -+ dget(h_dentry)); -+ } -+ err = au_refresh_hinode(inode, dentry); -+ au_dbg_verify_dinode(dentry); -+ } -+ } else { -+ AuDbg("positive --> positive, different type\n"); -+ /* similar to the behaviour of removing and creating */ -+ au_hide(dentry); -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ } -+ } -+ -+out: -+ return err; -+} -+ -+int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) -+{ -+ int err, ebrange; -+ unsigned int sigen; -+ struct au_dinfo *dinfo, *tmp; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ DiMustWriteLock(dentry); -+ AuDebugOn(IS_ROOT(dentry)); -+ AuDebugOn(!parent->d_inode); -+ -+ sb = dentry->d_sb; -+ inode = dentry->d_inode; -+ sigen = au_sigen(sb); -+ err = au_digen_test(parent, sigen); -+ if (unlikely(err)) -+ goto out; -+ -+ dinfo = au_di(dentry); -+ err = au_di_realloc(dinfo, au_sbend(sb) + 1); -+ if (unlikely(err)) -+ goto out; -+ ebrange = au_dbrange_test(dentry); -+ if (!ebrange) -+ ebrange = au_do_refresh_hdentry(dentry, parent); -+ -+ if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { -+ AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); -+ if (inode) -+ err = au_refresh_hinode_self(inode); -+ au_dbg_verify_dinode(dentry); -+ if (!err) -+ goto out_dgen; /* success */ -+ goto out; -+ } -+ -+ /* temporary dinfo */ -+ AuDbgDentry(dentry); -+ err = -ENOMEM; -+ tmp = au_di_alloc(sb, AuLsc_DI_TMP); -+ if (unlikely(!tmp)) -+ goto out; -+ au_di_swap(tmp, dinfo); -+ /* returns the number of positive dentries */ -+ /* -+ * if current working dir is removed, it returns an error. -+ * but the dentry is legal. -+ */ -+ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); -+ AuDbgDentry(dentry); -+ au_di_swap(tmp, dinfo); -+ if (err == -ENOENT) -+ err = 0; -+ if (err >= 0) { -+ /* compare/refresh by dinfo */ -+ AuDbgDentry(dentry); -+ err = au_refresh_by_dinfo(dentry, dinfo, tmp); -+ au_dbg_verify_dinode(dentry); -+ AuTraceErr(err); -+ } -+ au_rw_write_unlock(&tmp->di_rwsem); -+ au_di_free(tmp); -+ if (unlikely(err)) -+ goto out; -+ -+out_dgen: -+ au_update_digen(dentry); -+out: -+ if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { -+ AuIOErr("failed refreshing %pd, %d\n", dentry, err); -+ AuDbgDentry(dentry); -+ } -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags, -+ struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ int err, valid; -+ -+ err = 0; -+ if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE)) -+ goto out; -+ -+ AuDbg("b%d\n", bindex); -+ /* -+ * gave up supporting LOOKUP_CREATE/OPEN for lower fs, -+ * due to whiteout and branch permission. -+ */ -+ flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE -+ | LOOKUP_FOLLOW | LOOKUP_EXCL); -+ /* it may return tri-state */ -+ valid = h_dentry->d_op->d_revalidate(h_dentry, flags); -+ -+ if (unlikely(valid < 0)) -+ err = valid; -+ else if (!valid) -+ err = -EINVAL; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* todo: remove this */ -+static int h_d_revalidate(struct dentry *dentry, struct inode *inode, -+ unsigned int flags, int do_udba) -+{ -+ int err; -+ umode_t mode, h_mode; -+ aufs_bindex_t bindex, btail, bstart, ibs, ibe; -+ unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; -+ struct inode *h_inode, *h_cached_inode; -+ struct dentry *h_dentry; -+ struct qstr *name, *h_name; -+ -+ err = 0; -+ plus = 0; -+ mode = 0; -+ ibs = -1; -+ ibe = -1; -+ unhashed = !!d_unhashed(dentry); -+ is_root = !!IS_ROOT(dentry); -+ name = &dentry->d_name; -+ tmpfile = au_di(dentry)->di_tmpfile; -+ -+ /* -+ * Theoretically, REVAL test should be unnecessary in case of -+ * {FS,I}NOTIFY. -+ * But {fs,i}notify doesn't fire some necessary events, -+ * IN_ATTRIB for atime/nlink/pageio -+ * Let's do REVAL test too. -+ */ -+ if (do_udba && inode) { -+ mode = (inode->i_mode & S_IFMT); -+ plus = (inode->i_nlink > 0); -+ ibs = au_ibstart(inode); -+ ibe = au_ibend(inode); -+ } -+ -+ bstart = au_dbstart(dentry); -+ btail = bstart; -+ if (inode && S_ISDIR(inode->i_mode)) -+ btail = au_dbtaildir(dentry); -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ -+ AuDbg("b%d, %pd\n", bindex, h_dentry); -+ h_nfs = !!au_test_nfs(h_dentry->d_sb); -+ spin_lock(&h_dentry->d_lock); -+ h_name = &h_dentry->d_name; -+ if (unlikely(do_udba -+ && !is_root -+ && ((!h_nfs -+ && (unhashed != !!d_unhashed(h_dentry) -+ || (!tmpfile -+ && !au_qstreq(name, h_name)) -+ )) -+ || (h_nfs -+ && !(flags & LOOKUP_OPEN) -+ && (h_dentry->d_flags -+ & DCACHE_NFSFS_RENAMED))) -+ )) { -+ int h_unhashed; -+ -+ h_unhashed = d_unhashed(h_dentry); -+ spin_unlock(&h_dentry->d_lock); -+ AuDbg("unhash 0x%x 0x%x, %pd %pd\n", -+ unhashed, h_unhashed, dentry, h_dentry); -+ goto err; -+ } -+ spin_unlock(&h_dentry->d_lock); -+ -+ err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); -+ if (unlikely(err)) -+ /* do not goto err, to keep the errno */ -+ break; -+ -+ /* todo: plink too? */ -+ if (!do_udba) -+ continue; -+ -+ /* UDBA tests */ -+ h_inode = h_dentry->d_inode; -+ if (unlikely(!!inode != !!h_inode)) -+ goto err; -+ -+ h_plus = plus; -+ h_mode = mode; -+ h_cached_inode = h_inode; -+ if (h_inode) { -+ h_mode = (h_inode->i_mode & S_IFMT); -+ h_plus = (h_inode->i_nlink > 0); -+ } -+ if (inode && ibs <= bindex && bindex <= ibe) -+ h_cached_inode = au_h_iptr(inode, bindex); -+ -+ if (!h_nfs) { -+ if (unlikely(plus != h_plus && !tmpfile)) -+ goto err; -+ } else { -+ if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) -+ && !is_root -+ && !IS_ROOT(h_dentry) -+ && unhashed != d_unhashed(h_dentry))) -+ goto err; -+ } -+ if (unlikely(mode != h_mode -+ || h_cached_inode != h_inode)) -+ goto err; -+ continue; -+ -+err: -+ err = -EINVAL; -+ break; -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* todo: consolidate with do_refresh() and au_reval_for_attr() */ -+static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ struct dentry *parent; -+ -+ if (!au_digen_test(dentry, sigen)) -+ return 0; -+ -+ parent = dget_parent(dentry); -+ di_read_lock_parent(parent, AuLock_IR); -+ AuDebugOn(au_digen_test(parent, sigen)); -+ au_dbg_verify_gen(parent, sigen); -+ err = au_refresh_dentry(dentry, parent); -+ di_read_unlock(parent, AuLock_IR); -+ dput(parent); -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_reval_dpath(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ struct dentry *d, *parent; -+ struct inode *inode; -+ -+ if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) -+ return simple_reval_dpath(dentry, sigen); -+ -+ /* slow loop, keep it simple and stupid */ -+ /* cf: au_cpup_dirs() */ -+ err = 0; -+ parent = NULL; -+ while (au_digen_test(dentry, sigen)) { -+ d = dentry; -+ while (1) { -+ dput(parent); -+ parent = dget_parent(d); -+ if (!au_digen_test(parent, sigen)) -+ break; -+ d = parent; -+ } -+ -+ inode = d->d_inode; -+ if (d != dentry) -+ di_write_lock_child2(d); -+ -+ /* someone might update our dentry while we were sleeping */ -+ if (au_digen_test(d, sigen)) { -+ /* -+ * todo: consolidate with simple_reval_dpath(), -+ * do_refresh() and au_reval_for_attr(). -+ */ -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_refresh_dentry(d, parent); -+ di_read_unlock(parent, AuLock_IR); -+ } -+ -+ if (d != dentry) -+ di_write_unlock(d); -+ dput(parent); -+ if (unlikely(err)) -+ break; -+ } -+ -+ return err; -+} -+ -+/* -+ * if valid returns 1, otherwise 0. -+ */ -+static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) -+{ -+ int valid, err; -+ unsigned int sigen; -+ unsigned char do_udba; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ /* todo: support rcu-walk? */ -+ if (flags & LOOKUP_RCU) -+ return -ECHILD; -+ -+ valid = 0; -+ if (unlikely(!au_di(dentry))) -+ goto out; -+ -+ valid = 1; -+ sb = dentry->d_sb; -+ /* -+ * todo: very ugly -+ * i_mutex of parent dir may be held, -+ * but we should not return 'invalid' due to busy. -+ */ -+ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); -+ if (unlikely(err)) { -+ valid = err; -+ AuTraceErr(err); -+ goto out; -+ } -+ inode = dentry->d_inode; -+ if (unlikely(inode && is_bad_inode(inode))) { -+ err = -EINVAL; -+ AuTraceErr(err); -+ goto out_dgrade; -+ } -+ if (unlikely(au_dbrange_test(dentry))) { -+ err = -EINVAL; -+ AuTraceErr(err); -+ goto out_dgrade; -+ } -+ -+ sigen = au_sigen(sb); -+ if (au_digen_test(dentry, sigen)) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_dpath(dentry, sigen); -+ if (unlikely(err)) { -+ AuTraceErr(err); -+ goto out_dgrade; -+ } -+ } -+ di_downgrade_lock(dentry, AuLock_IR); -+ -+ err = -EINVAL; -+ if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) -+ && inode -+ && !(inode->i_state && I_LINKABLE) -+ && (IS_DEADDIR(inode) || !inode->i_nlink)) -+ goto out_inval; -+ -+ do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); -+ if (do_udba && inode) { -+ aufs_bindex_t bstart = au_ibstart(inode); -+ struct inode *h_inode; -+ -+ if (bstart >= 0) { -+ h_inode = au_h_iptr(inode, bstart); -+ if (h_inode && au_test_higen(inode, h_inode)) -+ goto out_inval; -+ } -+ } -+ -+ err = h_d_revalidate(dentry, inode, flags, do_udba); -+ if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { -+ err = -EIO; -+ AuDbg("both of real entry and whiteout found, %p, err %d\n", -+ dentry, err); -+ } -+ goto out_inval; -+ -+out_dgrade: -+ di_downgrade_lock(dentry, AuLock_IR); -+out_inval: -+ aufs_read_unlock(dentry, AuLock_IR); -+ AuTraceErr(err); -+ valid = !err; -+out: -+ if (!valid) { -+ AuDbg("%pd invalid, %d\n", dentry, valid); -+ d_drop(dentry); -+ } -+ return valid; -+} -+ -+static void aufs_d_release(struct dentry *dentry) -+{ -+ if (au_di(dentry)) { -+ au_di_fin(dentry); -+ au_hn_di_reinit(dentry); -+ } -+} -+ -+const struct dentry_operations aufs_dop = { -+ .d_revalidate = aufs_d_revalidate, -+ .d_weak_revalidate = aufs_d_revalidate, -+ .d_release = aufs_d_release -+}; -diff -Nur linux-4.0.4.orig/fs/aufs/dentry.h linux-4.0.4/fs/aufs/dentry.h ---- linux-4.0.4.orig/fs/aufs/dentry.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dentry.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,220 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * lookup and dentry operations -+ */ -+ -+#ifndef __AUFS_DENTRY_H__ -+#define __AUFS_DENTRY_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "rwsem.h" -+ -+struct au_hdentry { -+ struct dentry *hd_dentry; -+ aufs_bindex_t hd_id; -+}; -+ -+struct au_dinfo { -+ atomic_t di_generation; -+ -+ struct au_rwsem di_rwsem; -+ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; -+ unsigned char di_tmpfile; /* to allow the different name */ -+ struct au_hdentry *di_hdentry; -+} ____cacheline_aligned_in_smp; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dentry.c */ -+extern const struct dentry_operations aufs_dop; -+struct au_branch; -+struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent); -+int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, -+ struct dentry *h_parent, struct au_branch *br); -+ -+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type); -+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); -+int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); -+int au_reval_dpath(struct dentry *dentry, unsigned int sigen); -+ -+/* dinfo.c */ -+void au_di_init_once(void *_di); -+struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); -+void au_di_free(struct au_dinfo *dinfo); -+void au_di_swap(struct au_dinfo *a, struct au_dinfo *b); -+void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); -+int au_di_init(struct dentry *dentry); -+void au_di_fin(struct dentry *dentry); -+int au_di_realloc(struct au_dinfo *dinfo, int nbr); -+ -+void di_read_lock(struct dentry *d, int flags, unsigned int lsc); -+void di_read_unlock(struct dentry *d, int flags); -+void di_downgrade_lock(struct dentry *d, int flags); -+void di_write_lock(struct dentry *d, unsigned int lsc); -+void di_write_unlock(struct dentry *d); -+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); -+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); -+void di_write_unlock2(struct dentry *d1, struct dentry *d2); -+ -+struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); -+struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex); -+aufs_bindex_t au_dbtail(struct dentry *dentry); -+aufs_bindex_t au_dbtaildir(struct dentry *dentry); -+ -+void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_dentry); -+int au_digen_test(struct dentry *dentry, unsigned int sigen); -+int au_dbrange_test(struct dentry *dentry); -+void au_update_digen(struct dentry *dentry); -+void au_update_dbrange(struct dentry *dentry, int do_put_zero); -+void au_update_dbstart(struct dentry *dentry); -+void au_update_dbend(struct dentry *dentry); -+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_dinfo *au_di(struct dentry *dentry) -+{ -+ return dentry->d_fsdata; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock subclass for dinfo */ -+enum { -+ AuLsc_DI_CHILD, /* child first */ -+ AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */ -+ AuLsc_DI_CHILD3, /* copyup dirs */ -+ AuLsc_DI_PARENT, -+ AuLsc_DI_PARENT2, -+ AuLsc_DI_PARENT3, -+ AuLsc_DI_TMP /* temp for replacing dinfo */ -+}; -+ -+/* -+ * di_read_lock_child, di_write_lock_child, -+ * di_read_lock_child2, di_write_lock_child2, -+ * di_read_lock_child3, di_write_lock_child3, -+ * di_read_lock_parent, di_write_lock_parent, -+ * di_read_lock_parent2, di_write_lock_parent2, -+ * di_read_lock_parent3, di_write_lock_parent3, -+ */ -+#define AuReadLockFunc(name, lsc) \ -+static inline void di_read_lock_##name(struct dentry *d, int flags) \ -+{ di_read_lock(d, flags, AuLsc_DI_##lsc); } -+ -+#define AuWriteLockFunc(name, lsc) \ -+static inline void di_write_lock_##name(struct dentry *d) \ -+{ di_write_lock(d, AuLsc_DI_##lsc); } -+ -+#define AuRWLockFuncs(name, lsc) \ -+ AuReadLockFunc(name, lsc) \ -+ AuWriteLockFunc(name, lsc) -+ -+AuRWLockFuncs(child, CHILD); -+AuRWLockFuncs(child2, CHILD2); -+AuRWLockFuncs(child3, CHILD3); -+AuRWLockFuncs(parent, PARENT); -+AuRWLockFuncs(parent2, PARENT2); -+AuRWLockFuncs(parent3, PARENT3); -+ -+#undef AuReadLockFunc -+#undef AuWriteLockFunc -+#undef AuRWLockFuncs -+ -+#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) -+#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem) -+#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: memory barrier? */ -+static inline unsigned int au_digen(struct dentry *d) -+{ -+ return atomic_read(&au_di(d)->di_generation); -+} -+ -+static inline void au_h_dentry_init(struct au_hdentry *hdentry) -+{ -+ hdentry->hd_dentry = NULL; -+} -+ -+static inline void au_hdput(struct au_hdentry *hd) -+{ -+ if (hd) -+ dput(hd->hd_dentry); -+} -+ -+static inline aufs_bindex_t au_dbstart(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bstart; -+} -+ -+static inline aufs_bindex_t au_dbend(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bend; -+} -+ -+static inline aufs_bindex_t au_dbwh(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bwh; -+} -+ -+static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) -+{ -+ DiMustAnyLock(dentry); -+ return au_di(dentry)->di_bdiropq; -+} -+ -+/* todo: hard/soft set? */ -+static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ au_di(dentry)->di_bstart = bindex; -+} -+ -+static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ au_di(dentry)->di_bend = bindex; -+} -+ -+static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ /* dbwh can be outside of bstart - bend range */ -+ au_di(dentry)->di_bwh = bindex; -+} -+ -+static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ DiMustWriteLock(dentry); -+ au_di(dentry)->di_bdiropq = bindex; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_HNOTIFY -+static inline void au_digen_dec(struct dentry *d) -+{ -+ atomic_dec(&au_di(d)->di_generation); -+} -+ -+static inline void au_hn_di_reinit(struct dentry *dentry) -+{ -+ dentry->d_fsdata = NULL; -+} -+#else -+AuStubVoid(au_hn_di_reinit, struct dentry *dentry __maybe_unused) -+#endif /* CONFIG_AUFS_HNOTIFY */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DENTRY_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/dinfo.c linux-4.0.4/fs/aufs/dinfo.c ---- linux-4.0.4.orig/fs/aufs/dinfo.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dinfo.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,531 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * dentry private data -+ */ -+ -+#include "aufs.h" -+ -+void au_di_init_once(void *_dinfo) -+{ -+ struct au_dinfo *dinfo = _dinfo; -+ static struct lock_class_key aufs_di; -+ -+ au_rw_init(&dinfo->di_rwsem); -+ au_rw_class(&dinfo->di_rwsem, &aufs_di); -+} -+ -+struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) -+{ -+ struct au_dinfo *dinfo; -+ int nbr, i; -+ -+ dinfo = au_cache_alloc_dinfo(); -+ if (unlikely(!dinfo)) -+ goto out; -+ -+ nbr = au_sbend(sb) + 1; -+ if (nbr <= 0) -+ nbr = 1; -+ dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); -+ if (dinfo->di_hdentry) { -+ au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ dinfo->di_bwh = -1; -+ dinfo->di_bdiropq = -1; -+ dinfo->di_tmpfile = 0; -+ for (i = 0; i < nbr; i++) -+ dinfo->di_hdentry[i].hd_id = -1; -+ goto out; -+ } -+ -+ au_cache_free_dinfo(dinfo); -+ dinfo = NULL; -+ -+out: -+ return dinfo; -+} -+ -+void au_di_free(struct au_dinfo *dinfo) -+{ -+ struct au_hdentry *p; -+ aufs_bindex_t bend, bindex; -+ -+ /* dentry may not be revalidated */ -+ bindex = dinfo->di_bstart; -+ if (bindex >= 0) { -+ bend = dinfo->di_bend; -+ p = dinfo->di_hdentry + bindex; -+ while (bindex++ <= bend) -+ au_hdput(p++); -+ } -+ kfree(dinfo->di_hdentry); -+ au_cache_free_dinfo(dinfo); -+} -+ -+void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) -+{ -+ struct au_hdentry *p; -+ aufs_bindex_t bi; -+ -+ AuRwMustWriteLock(&a->di_rwsem); -+ AuRwMustWriteLock(&b->di_rwsem); -+ -+#define DiSwap(v, name) \ -+ do { \ -+ v = a->di_##name; \ -+ a->di_##name = b->di_##name; \ -+ b->di_##name = v; \ -+ } while (0) -+ -+ DiSwap(p, hdentry); -+ DiSwap(bi, bstart); -+ DiSwap(bi, bend); -+ DiSwap(bi, bwh); -+ DiSwap(bi, bdiropq); -+ /* smp_mb(); */ -+ -+#undef DiSwap -+} -+ -+void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) -+{ -+ AuRwMustWriteLock(&dst->di_rwsem); -+ AuRwMustWriteLock(&src->di_rwsem); -+ -+ dst->di_bstart = src->di_bstart; -+ dst->di_bend = src->di_bend; -+ dst->di_bwh = src->di_bwh; -+ dst->di_bdiropq = src->di_bdiropq; -+ /* smp_mb(); */ -+} -+ -+int au_di_init(struct dentry *dentry) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_dinfo *dinfo; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); -+ if (dinfo) { -+ atomic_set(&dinfo->di_generation, au_sigen(sb)); -+ /* smp_mb(); */ /* atomic_set */ -+ dentry->d_fsdata = dinfo; -+ } else -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+void au_di_fin(struct dentry *dentry) -+{ -+ struct au_dinfo *dinfo; -+ -+ dinfo = au_di(dentry); -+ AuRwDestroy(&dinfo->di_rwsem); -+ au_di_free(dinfo); -+} -+ -+int au_di_realloc(struct au_dinfo *dinfo, int nbr) -+{ -+ int err, sz; -+ struct au_hdentry *hdp; -+ -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ err = -ENOMEM; -+ sz = sizeof(*hdp) * (dinfo->di_bend + 1); -+ if (!sz) -+ sz = sizeof(*hdp); -+ hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); -+ if (hdp) { -+ dinfo->di_hdentry = hdp; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void do_ii_write_lock(struct inode *inode, unsigned int lsc) -+{ -+ switch (lsc) { -+ case AuLsc_DI_CHILD: -+ ii_write_lock_child(inode); -+ break; -+ case AuLsc_DI_CHILD2: -+ ii_write_lock_child2(inode); -+ break; -+ case AuLsc_DI_CHILD3: -+ ii_write_lock_child3(inode); -+ break; -+ case AuLsc_DI_PARENT: -+ ii_write_lock_parent(inode); -+ break; -+ case AuLsc_DI_PARENT2: -+ ii_write_lock_parent2(inode); -+ break; -+ case AuLsc_DI_PARENT3: -+ ii_write_lock_parent3(inode); -+ break; -+ default: -+ BUG(); -+ } -+} -+ -+static void do_ii_read_lock(struct inode *inode, unsigned int lsc) -+{ -+ switch (lsc) { -+ case AuLsc_DI_CHILD: -+ ii_read_lock_child(inode); -+ break; -+ case AuLsc_DI_CHILD2: -+ ii_read_lock_child2(inode); -+ break; -+ case AuLsc_DI_CHILD3: -+ ii_read_lock_child3(inode); -+ break; -+ case AuLsc_DI_PARENT: -+ ii_read_lock_parent(inode); -+ break; -+ case AuLsc_DI_PARENT2: -+ ii_read_lock_parent2(inode); -+ break; -+ case AuLsc_DI_PARENT3: -+ ii_read_lock_parent3(inode); -+ break; -+ default: -+ BUG(); -+ } -+} -+ -+void di_read_lock(struct dentry *d, int flags, unsigned int lsc) -+{ -+ au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); -+ if (d->d_inode) { -+ if (au_ftest_lock(flags, IW)) -+ do_ii_write_lock(d->d_inode, lsc); -+ else if (au_ftest_lock(flags, IR)) -+ do_ii_read_lock(d->d_inode, lsc); -+ } -+} -+ -+void di_read_unlock(struct dentry *d, int flags) -+{ -+ if (d->d_inode) { -+ if (au_ftest_lock(flags, IW)) { -+ au_dbg_verify_dinode(d); -+ ii_write_unlock(d->d_inode); -+ } else if (au_ftest_lock(flags, IR)) { -+ au_dbg_verify_dinode(d); -+ ii_read_unlock(d->d_inode); -+ } -+ } -+ au_rw_read_unlock(&au_di(d)->di_rwsem); -+} -+ -+void di_downgrade_lock(struct dentry *d, int flags) -+{ -+ if (d->d_inode && au_ftest_lock(flags, IR)) -+ ii_downgrade_lock(d->d_inode); -+ au_rw_dgrade_lock(&au_di(d)->di_rwsem); -+} -+ -+void di_write_lock(struct dentry *d, unsigned int lsc) -+{ -+ au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); -+ if (d->d_inode) -+ do_ii_write_lock(d->d_inode, lsc); -+} -+ -+void di_write_unlock(struct dentry *d) -+{ -+ au_dbg_verify_dinode(d); -+ if (d->d_inode) -+ ii_write_unlock(d->d_inode); -+ au_rw_write_unlock(&au_di(d)->di_rwsem); -+} -+ -+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) -+{ -+ AuDebugOn(d1 == d2 -+ || d1->d_inode == d2->d_inode -+ || d1->d_sb != d2->d_sb); -+ -+ if (isdir && au_test_subdir(d1, d2)) { -+ di_write_lock_child(d1); -+ di_write_lock_child2(d2); -+ } else { -+ /* there should be no races */ -+ di_write_lock_child(d2); -+ di_write_lock_child2(d1); -+ } -+} -+ -+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) -+{ -+ AuDebugOn(d1 == d2 -+ || d1->d_inode == d2->d_inode -+ || d1->d_sb != d2->d_sb); -+ -+ if (isdir && au_test_subdir(d1, d2)) { -+ di_write_lock_parent(d1); -+ di_write_lock_parent2(d2); -+ } else { -+ /* there should be no races */ -+ di_write_lock_parent(d2); -+ di_write_lock_parent2(d1); -+ } -+} -+ -+void di_write_unlock2(struct dentry *d1, struct dentry *d2) -+{ -+ di_write_unlock(d1); -+ if (d1->d_inode == d2->d_inode) -+ au_rw_write_unlock(&au_di(d2)->di_rwsem); -+ else -+ di_write_unlock(d2); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ struct dentry *d; -+ -+ DiMustAnyLock(dentry); -+ -+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) -+ return NULL; -+ AuDebugOn(bindex < 0); -+ d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; -+ AuDebugOn(d && au_dcount(d) <= 0); -+ return d; -+} -+ -+/* -+ * extended version of au_h_dptr(). -+ * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or -+ * error. -+ */ -+struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ struct dentry *h_dentry; -+ struct inode *inode, *h_inode; -+ -+ inode = dentry->d_inode; -+ AuDebugOn(!inode); -+ -+ h_dentry = NULL; -+ if (au_dbstart(dentry) <= bindex -+ && bindex <= au_dbend(dentry)) -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && !au_d_linkable(h_dentry)) { -+ dget(h_dentry); -+ goto out; /* success */ -+ } -+ -+ AuDebugOn(bindex < au_ibstart(inode)); -+ AuDebugOn(au_ibend(inode) < bindex); -+ h_inode = au_h_iptr(inode, bindex); -+ h_dentry = d_find_alias(h_inode); -+ if (h_dentry) { -+ if (!IS_ERR(h_dentry)) { -+ if (!au_d_linkable(h_dentry)) -+ goto out; /* success */ -+ dput(h_dentry); -+ } else -+ goto out; -+ } -+ -+ if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { -+ h_dentry = au_plink_lkup(inode, bindex); -+ AuDebugOn(!h_dentry); -+ if (!IS_ERR(h_dentry)) { -+ if (!au_d_hashed_positive(h_dentry)) -+ goto out; /* success */ -+ dput(h_dentry); -+ h_dentry = NULL; -+ } -+ } -+ -+out: -+ AuDbgDentry(h_dentry); -+ return h_dentry; -+} -+ -+aufs_bindex_t au_dbtail(struct dentry *dentry) -+{ -+ aufs_bindex_t bend, bwh; -+ -+ bend = au_dbend(dentry); -+ if (0 <= bend) { -+ bwh = au_dbwh(dentry); -+ if (!bwh) -+ return bwh; -+ if (0 < bwh && bwh < bend) -+ return bwh - 1; -+ } -+ return bend; -+} -+ -+aufs_bindex_t au_dbtaildir(struct dentry *dentry) -+{ -+ aufs_bindex_t bend, bopq; -+ -+ bend = au_dbtail(dentry); -+ if (0 <= bend) { -+ bopq = au_dbdiropq(dentry); -+ if (0 <= bopq && bopq < bend) -+ bend = bopq; -+ } -+ return bend; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_dentry) -+{ -+ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; -+ struct au_branch *br; -+ -+ DiMustWriteLock(dentry); -+ -+ au_hdput(hd); -+ hd->hd_dentry = h_dentry; -+ if (h_dentry) { -+ br = au_sbr(dentry->d_sb, bindex); -+ hd->hd_id = br->br_id; -+ } -+} -+ -+int au_dbrange_test(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bstart, bend; -+ -+ err = 0; -+ bstart = au_dbstart(dentry); -+ bend = au_dbend(dentry); -+ if (bstart >= 0) -+ AuDebugOn(bend < 0 && bstart > bend); -+ else { -+ err = -EIO; -+ AuDebugOn(bend >= 0); -+ } -+ -+ return err; -+} -+ -+int au_digen_test(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ -+ err = 0; -+ if (unlikely(au_digen(dentry) != sigen -+ || au_iigen_test(dentry->d_inode, sigen))) -+ err = -EIO; -+ -+ return err; -+} -+ -+void au_update_digen(struct dentry *dentry) -+{ -+ atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); -+ /* smp_mb(); */ /* atomic_set */ -+} -+ -+void au_update_dbrange(struct dentry *dentry, int do_put_zero) -+{ -+ struct au_dinfo *dinfo; -+ struct dentry *h_d; -+ struct au_hdentry *hdp; -+ -+ DiMustWriteLock(dentry); -+ -+ dinfo = au_di(dentry); -+ if (!dinfo || dinfo->di_bstart < 0) -+ return; -+ -+ hdp = dinfo->di_hdentry; -+ if (do_put_zero) { -+ aufs_bindex_t bindex, bend; -+ -+ bend = dinfo->di_bend; -+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { -+ h_d = hdp[0 + bindex].hd_dentry; -+ if (h_d && !h_d->d_inode) -+ au_set_h_dptr(dentry, bindex, NULL); -+ } -+ } -+ -+ dinfo->di_bstart = -1; -+ while (++dinfo->di_bstart <= dinfo->di_bend) -+ if (hdp[0 + dinfo->di_bstart].hd_dentry) -+ break; -+ if (dinfo->di_bstart > dinfo->di_bend) { -+ dinfo->di_bstart = -1; -+ dinfo->di_bend = -1; -+ return; -+ } -+ -+ dinfo->di_bend++; -+ while (0 <= --dinfo->di_bend) -+ if (hdp[0 + dinfo->di_bend].hd_dentry) -+ break; -+ AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); -+} -+ -+void au_update_dbstart(struct dentry *dentry) -+{ -+ aufs_bindex_t bindex, bend; -+ struct dentry *h_dentry; -+ -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ if (h_dentry->d_inode) { -+ au_set_dbstart(dentry, bindex); -+ return; -+ } -+ au_set_h_dptr(dentry, bindex, NULL); -+ } -+} -+ -+void au_update_dbend(struct dentry *dentry) -+{ -+ aufs_bindex_t bindex, bstart; -+ struct dentry *h_dentry; -+ -+ bstart = au_dbstart(dentry); -+ for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ if (h_dentry->d_inode) { -+ au_set_dbend(dentry, bindex); -+ return; -+ } -+ au_set_h_dptr(dentry, bindex, NULL); -+ } -+} -+ -+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) -+{ -+ aufs_bindex_t bindex, bend; -+ -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) -+ if (au_h_dptr(dentry, bindex) == h_dentry) -+ return bindex; -+ return -1; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/dir.c linux-4.0.4/fs/aufs/dir.c ---- linux-4.0.4.orig/fs/aufs/dir.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dir.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,738 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * directory operations -+ */ -+ -+#include -+#include "aufs.h" -+ -+void au_add_nlink(struct inode *dir, struct inode *h_dir) -+{ -+ unsigned int nlink; -+ -+ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); -+ -+ nlink = dir->i_nlink; -+ nlink += h_dir->i_nlink - 2; -+ if (h_dir->i_nlink < 2) -+ nlink += 2; -+ smp_mb(); /* for i_nlink */ -+ /* 0 can happen in revaliding */ -+ set_nlink(dir, nlink); -+} -+ -+void au_sub_nlink(struct inode *dir, struct inode *h_dir) -+{ -+ unsigned int nlink; -+ -+ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); -+ -+ nlink = dir->i_nlink; -+ nlink -= h_dir->i_nlink - 2; -+ if (h_dir->i_nlink < 2) -+ nlink -= 2; -+ smp_mb(); /* for i_nlink */ -+ /* nlink == 0 means the branch-fs is broken */ -+ set_nlink(dir, nlink); -+} -+ -+loff_t au_dir_size(struct file *file, struct dentry *dentry) -+{ -+ loff_t sz; -+ aufs_bindex_t bindex, bend; -+ struct file *h_file; -+ struct dentry *h_dentry; -+ -+ sz = 0; -+ if (file) { -+ AuDebugOn(!d_is_dir(file->f_path.dentry)); -+ -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); -+ bindex <= bend && sz < KMALLOC_MAX_SIZE; -+ bindex++) { -+ h_file = au_hf_dir(file, bindex); -+ if (h_file && file_inode(h_file)) -+ sz += vfsub_f_size_read(h_file); -+ } -+ } else { -+ AuDebugOn(!dentry); -+ AuDebugOn(!d_is_dir(dentry)); -+ -+ bend = au_dbtaildir(dentry); -+ for (bindex = au_dbstart(dentry); -+ bindex <= bend && sz < KMALLOC_MAX_SIZE; -+ bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && h_dentry->d_inode) -+ sz += i_size_read(h_dentry->d_inode); -+ } -+ } -+ if (sz < KMALLOC_MAX_SIZE) -+ sz = roundup_pow_of_two(sz); -+ if (sz > KMALLOC_MAX_SIZE) -+ sz = KMALLOC_MAX_SIZE; -+ else if (sz < NAME_MAX) { -+ BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); -+ sz = AUFS_RDBLK_DEF; -+ } -+ return sz; -+} -+ -+struct au_dir_ts_arg { -+ struct dentry *dentry; -+ aufs_bindex_t brid; -+}; -+ -+static void au_do_dir_ts(void *arg) -+{ -+ struct au_dir_ts_arg *a = arg; -+ struct au_dtime dt; -+ struct path h_path; -+ struct inode *dir, *h_dir; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_hinode *hdir; -+ int err; -+ aufs_bindex_t bstart, bindex; -+ -+ sb = a->dentry->d_sb; -+ dir = a->dentry->d_inode; -+ if (!dir) -+ goto out; -+ /* no dir->i_mutex lock */ -+ aufs_read_lock(a->dentry, AuLock_DW | AuLock_DIR); /* noflush */ -+ -+ bstart = au_ibstart(dir); -+ bindex = au_br_index(sb, a->brid); -+ if (bindex < bstart) -+ goto out_unlock; -+ -+ br = au_sbr(sb, bindex); -+ h_path.dentry = au_h_dptr(a->dentry, bindex); -+ if (!h_path.dentry) -+ goto out_unlock; -+ h_path.mnt = au_br_mnt(br); -+ au_dtime_store(&dt, a->dentry, &h_path); -+ -+ br = au_sbr(sb, bstart); -+ if (!au_br_writable(br->br_perm)) -+ goto out_unlock; -+ h_path.dentry = au_h_dptr(a->dentry, bstart); -+ h_path.mnt = au_br_mnt(br); -+ err = vfsub_mnt_want_write(h_path.mnt); -+ if (err) -+ goto out_unlock; -+ hdir = au_hi(dir, bstart); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ h_dir = au_h_iptr(dir, bstart); -+ if (h_dir->i_nlink -+ && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { -+ dt.dt_h_path = h_path; -+ au_dtime_revert(&dt); -+ } -+ au_hn_imtx_unlock(hdir); -+ vfsub_mnt_drop_write(h_path.mnt); -+ au_cpup_attr_timesizes(dir); -+ -+out_unlock: -+ aufs_read_unlock(a->dentry, AuLock_DW); -+out: -+ dput(a->dentry); -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ kfree(arg); -+} -+ -+void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) -+{ -+ int perm, wkq_err; -+ aufs_bindex_t bstart; -+ struct au_dir_ts_arg *arg; -+ struct dentry *dentry; -+ struct super_block *sb; -+ -+ IMustLock(dir); -+ -+ dentry = d_find_any_alias(dir); -+ AuDebugOn(!dentry); -+ sb = dentry->d_sb; -+ bstart = au_ibstart(dir); -+ if (bstart == bindex) { -+ au_cpup_attr_timesizes(dir); -+ goto out; -+ } -+ -+ perm = au_sbr_perm(sb, bstart); -+ if (!au_br_writable(perm)) -+ goto out; -+ -+ arg = kmalloc(sizeof(*arg), GFP_NOFS); -+ if (!arg) -+ goto out; -+ -+ arg->dentry = dget(dentry); /* will be dput-ted by au_do_dir_ts() */ -+ arg->brid = au_sbr_id(sb, bindex); -+ wkq_err = au_wkq_nowait(au_do_dir_ts, arg, sb, /*flags*/0); -+ if (unlikely(wkq_err)) { -+ pr_err("wkq %d\n", wkq_err); -+ dput(dentry); -+ kfree(arg); -+ } -+ -+out: -+ dput(dentry); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int reopen_dir(struct file *file) -+{ -+ int err; -+ unsigned int flags; -+ aufs_bindex_t bindex, btail, bstart; -+ struct dentry *dentry, *h_dentry; -+ struct file *h_file; -+ -+ /* open all lower dirs */ -+ dentry = file->f_path.dentry; -+ bstart = au_dbstart(dentry); -+ for (bindex = au_fbstart(file); bindex < bstart; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ au_set_fbstart(file, bstart); -+ -+ btail = au_dbtaildir(dentry); -+ for (bindex = au_fbend_dir(file); btail < bindex; bindex--) -+ au_set_h_fptr(file, bindex, NULL); -+ au_set_fbend_dir(file, btail); -+ -+ flags = vfsub_file_flags(file); -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ h_file = au_hf_dir(file, bindex); -+ if (h_file) -+ continue; -+ -+ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; /* close all? */ -+ au_set_h_fptr(file, bindex, h_file); -+ } -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ err = 0; -+ -+out: -+ return err; -+} -+ -+static int do_open_dir(struct file *file, int flags, struct file *h_file) -+{ -+ int err; -+ aufs_bindex_t bindex, btail; -+ struct dentry *dentry, *h_dentry; -+ -+ FiMustWriteLock(file); -+ AuDebugOn(h_file); -+ -+ err = 0; -+ dentry = file->f_path.dentry; -+ file->f_version = dentry->d_inode->i_version; -+ bindex = au_dbstart(dentry); -+ au_set_fbstart(file, bindex); -+ btail = au_dbtaildir(dentry); -+ au_set_fbend_dir(file, btail); -+ for (; !err && bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!h_dentry) -+ continue; -+ -+ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ if (IS_ERR(h_file)) { -+ err = PTR_ERR(h_file); -+ break; -+ } -+ au_set_h_fptr(file, bindex, h_file); -+ } -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ if (!err) -+ return 0; /* success */ -+ -+ /* close all */ -+ for (bindex = au_fbstart(file); bindex <= btail; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ au_set_fbstart(file, -1); -+ au_set_fbend_dir(file, -1); -+ -+ return err; -+} -+ -+static int aufs_open_dir(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_fidir *fidir; -+ -+ err = -ENOMEM; -+ sb = file->f_path.dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ fidir = au_fidir_alloc(sb); -+ if (fidir) { -+ struct au_do_open_args args = { -+ .open = do_open_dir, -+ .fidir = fidir -+ }; -+ err = au_do_open(file, &args); -+ if (unlikely(err)) -+ kfree(fidir); -+ } -+ si_read_unlock(sb); -+ return err; -+} -+ -+static int aufs_release_dir(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ struct au_vdir *vdir_cache; -+ struct au_finfo *finfo; -+ struct au_fidir *fidir; -+ aufs_bindex_t bindex, bend; -+ -+ finfo = au_fi(file); -+ fidir = finfo->fi_hdir; -+ if (fidir) { -+ au_sphl_del(&finfo->fi_hlist, -+ &au_sbi(file->f_path.dentry->d_sb)->si_files); -+ vdir_cache = fidir->fd_vdir_cache; /* lock-free */ -+ if (vdir_cache) -+ au_vdir_free(vdir_cache); -+ -+ bindex = finfo->fi_btop; -+ if (bindex >= 0) { -+ /* -+ * calls fput() instead of filp_close(), -+ * since no dnotify or lock for the lower file. -+ */ -+ bend = fidir->fd_bbot; -+ for (; bindex <= bend; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ } -+ kfree(fidir); -+ finfo->fi_hdir = NULL; -+ } -+ au_finfo_fin(file); -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_flush_dir(struct file *file, fl_owner_t id) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ struct file *h_file; -+ -+ err = 0; -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { -+ h_file = au_hf_dir(file, bindex); -+ if (h_file) -+ err = vfsub_flush(h_file, id); -+ } -+ return err; -+} -+ -+static int aufs_flush_dir(struct file *file, fl_owner_t id) -+{ -+ return au_do_flush(file, id, au_do_flush_dir); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { -+ struct path h_path; -+ -+ if (au_test_ro(sb, bindex, inode)) -+ continue; -+ h_path.dentry = au_h_dptr(dentry, bindex); -+ if (!h_path.dentry) -+ continue; -+ -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_fsync(NULL, &h_path, datasync); -+ } -+ -+ return err; -+} -+ -+static int au_do_fsync_dir(struct file *file, int datasync) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct file *h_file; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ -+ sb = file->f_path.dentry->d_sb; -+ inode = file_inode(file); -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { -+ h_file = au_hf_dir(file, bindex); -+ if (!h_file || au_test_ro(sb, bindex, inode)) -+ continue; -+ -+ err = vfsub_fsync(h_file, &h_file->f_path, datasync); -+ } -+ -+out: -+ return err; -+} -+ -+/* -+ * @file may be NULL -+ */ -+static int aufs_fsync_dir(struct file *file, loff_t start, loff_t end, -+ int datasync) -+{ -+ int err; -+ struct dentry *dentry; -+ struct super_block *sb; -+ struct mutex *mtx; -+ -+ err = 0; -+ dentry = file->f_path.dentry; -+ mtx = &dentry->d_inode->i_mutex; -+ mutex_lock(mtx); -+ sb = dentry->d_sb; -+ si_noflush_read_lock(sb); -+ if (file) -+ err = au_do_fsync_dir(file, datasync); -+ else { -+ di_write_lock_child(dentry); -+ err = au_do_fsync_dir_no_file(dentry, datasync); -+ } -+ au_cpup_attr_timesizes(dentry->d_inode); -+ di_write_unlock(dentry); -+ if (file) -+ fi_write_unlock(file); -+ -+ si_read_unlock(sb); -+ mutex_unlock(mtx); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_iterate(struct file *file, struct dir_context *ctx) -+{ -+ int err; -+ struct dentry *dentry; -+ struct inode *inode, *h_inode; -+ struct super_block *sb; -+ -+ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); -+ -+ dentry = file->f_path.dentry; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ -+ sb = dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ err = au_alive_dir(dentry); -+ if (!err) -+ err = au_vdir_init(file); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ if (!au_test_nfsd()) { -+ err = au_vdir_fill_de(file, ctx); -+ fsstack_copy_attr_atime(inode, h_inode); -+ } else { -+ /* -+ * nfsd filldir may call lookup_one_len(), vfs_getattr(), -+ * encode_fh() and others. -+ */ -+ atomic_inc(&h_inode->i_count); -+ di_read_unlock(dentry, AuLock_IR); -+ si_read_unlock(sb); -+ err = au_vdir_fill_de(file, ctx); -+ fsstack_copy_attr_atime(inode, h_inode); -+ fi_write_unlock(file); -+ iput(h_inode); -+ -+ AuTraceErr(err); -+ return err; -+ } -+ -+out_unlock: -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AuTestEmpty_WHONLY 1 -+#define AuTestEmpty_CALLED (1 << 1) -+#define AuTestEmpty_SHWH (1 << 2) -+#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) -+#define au_fset_testempty(flags, name) \ -+ do { (flags) |= AuTestEmpty_##name; } while (0) -+#define au_fclr_testempty(flags, name) \ -+ do { (flags) &= ~AuTestEmpty_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_SHWH -+#undef AuTestEmpty_SHWH -+#define AuTestEmpty_SHWH 0 -+#endif -+ -+struct test_empty_arg { -+ struct dir_context ctx; -+ struct au_nhash *whlist; -+ unsigned int flags; -+ int err; -+ aufs_bindex_t bindex; -+}; -+ -+static int test_empty_cb(struct dir_context *ctx, const char *__name, -+ int namelen, loff_t offset __maybe_unused, u64 ino, -+ unsigned int d_type) -+{ -+ struct test_empty_arg *arg = container_of(ctx, struct test_empty_arg, -+ ctx); -+ char *name = (void *)__name; -+ -+ arg->err = 0; -+ au_fset_testempty(arg->flags, CALLED); -+ /* smp_mb(); */ -+ if (name[0] == '.' -+ && (namelen == 1 || (name[1] == '.' && namelen == 2))) -+ goto out; /* success */ -+ -+ if (namelen <= AUFS_WH_PFX_LEN -+ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { -+ if (au_ftest_testempty(arg->flags, WHONLY) -+ && !au_nhash_test_known_wh(arg->whlist, name, namelen)) -+ arg->err = -ENOTEMPTY; -+ goto out; -+ } -+ -+ name += AUFS_WH_PFX_LEN; -+ namelen -= AUFS_WH_PFX_LEN; -+ if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) -+ arg->err = au_nhash_append_wh -+ (arg->whlist, name, namelen, ino, d_type, arg->bindex, -+ au_ftest_testempty(arg->flags, SHWH)); -+ -+out: -+ /* smp_mb(); */ -+ AuTraceErr(arg->err); -+ return arg->err; -+} -+ -+static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) -+{ -+ int err; -+ struct file *h_file; -+ -+ h_file = au_h_open(dentry, arg->bindex, -+ O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, -+ /*file*/NULL, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out; -+ -+ err = 0; -+ if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) -+ && !file_inode(h_file)->i_nlink) -+ goto out_put; -+ -+ do { -+ arg->err = 0; -+ au_fclr_testempty(arg->flags, CALLED); -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(h_file, &arg->ctx); -+ if (err >= 0) -+ err = arg->err; -+ } while (!err && au_ftest_testempty(arg->flags, CALLED)); -+ -+out_put: -+ fput(h_file); -+ au_sbr_put(dentry->d_sb, arg->bindex); -+out: -+ return err; -+} -+ -+struct do_test_empty_args { -+ int *errp; -+ struct dentry *dentry; -+ struct test_empty_arg *arg; -+}; -+ -+static void call_do_test_empty(void *args) -+{ -+ struct do_test_empty_args *a = args; -+ *a->errp = do_test_empty(a->dentry, a->arg); -+} -+ -+static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) -+{ -+ int err, wkq_err; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ -+ h_dentry = au_h_dptr(dentry, arg->bindex); -+ h_inode = h_dentry->d_inode; -+ /* todo: i_mode changes anytime? */ -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); -+ mutex_unlock(&h_inode->i_mutex); -+ if (!err) -+ err = do_test_empty(dentry, arg); -+ else { -+ struct do_test_empty_args args = { -+ .errp = &err, -+ .dentry = dentry, -+ .arg = arg -+ }; -+ unsigned int flags = arg->flags; -+ -+ wkq_err = au_wkq_wait(call_do_test_empty, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ arg->flags = flags; -+ } -+ -+ return err; -+} -+ -+int au_test_empty_lower(struct dentry *dentry) -+{ -+ int err; -+ unsigned int rdhash; -+ aufs_bindex_t bindex, bstart, btail; -+ struct au_nhash whlist; -+ struct test_empty_arg arg = { -+ .ctx = { -+ .actor = test_empty_cb -+ } -+ }; -+ int (*test_empty)(struct dentry *dentry, struct test_empty_arg *arg); -+ -+ SiMustAnyLock(dentry->d_sb); -+ -+ rdhash = au_sbi(dentry->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); -+ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ -+ arg.flags = 0; -+ arg.whlist = &whlist; -+ bstart = au_dbstart(dentry); -+ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) -+ au_fset_testempty(arg.flags, SHWH); -+ test_empty = do_test_empty; -+ if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) -+ test_empty = sio_test_empty; -+ arg.bindex = bstart; -+ err = test_empty(dentry, &arg); -+ if (unlikely(err)) -+ goto out_whlist; -+ -+ au_fset_testempty(arg.flags, WHONLY); -+ btail = au_dbtaildir(dentry); -+ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && h_dentry->d_inode) { -+ arg.bindex = bindex; -+ err = test_empty(dentry, &arg); -+ } -+ } -+ -+out_whlist: -+ au_nhash_wh_free(&whlist); -+out: -+ return err; -+} -+ -+int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) -+{ -+ int err; -+ struct test_empty_arg arg = { -+ .ctx = { -+ .actor = test_empty_cb -+ } -+ }; -+ aufs_bindex_t bindex, btail; -+ -+ err = 0; -+ arg.whlist = whlist; -+ arg.flags = AuTestEmpty_WHONLY; -+ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) -+ au_fset_testempty(arg.flags, SHWH); -+ btail = au_dbtaildir(dentry); -+ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry && h_dentry->d_inode) { -+ arg.bindex = bindex; -+ err = sio_test_empty(dentry, &arg); -+ } -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+const struct file_operations aufs_dir_fop = { -+ .owner = THIS_MODULE, -+ .llseek = default_llseek, -+ .read = generic_read_dir, -+ .iterate = aufs_iterate, -+ .unlocked_ioctl = aufs_ioctl_dir, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = aufs_compat_ioctl_dir, -+#endif -+ .open = aufs_open_dir, -+ .release = aufs_release_dir, -+ .flush = aufs_flush_dir, -+ .fsync = aufs_fsync_dir -+}; -diff -Nur linux-4.0.4.orig/fs/aufs/dir.h linux-4.0.4/fs/aufs/dir.h ---- linux-4.0.4.orig/fs/aufs/dir.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dir.h 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,118 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * directory operations -+ */ -+ -+#ifndef __AUFS_DIR_H__ -+#define __AUFS_DIR_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* need to be faster and smaller */ -+ -+struct au_nhash { -+ unsigned int nh_num; -+ struct hlist_head *nh_head; -+}; -+ -+struct au_vdir_destr { -+ unsigned char len; -+ unsigned char name[0]; -+} __packed; -+ -+struct au_vdir_dehstr { -+ struct hlist_node hash; -+ struct au_vdir_destr *str; -+} ____cacheline_aligned_in_smp; -+ -+struct au_vdir_de { -+ ino_t de_ino; -+ unsigned char de_type; -+ /* caution: packed */ -+ struct au_vdir_destr de_str; -+} __packed; -+ -+struct au_vdir_wh { -+ struct hlist_node wh_hash; -+#ifdef CONFIG_AUFS_SHWH -+ ino_t wh_ino; -+ aufs_bindex_t wh_bindex; -+ unsigned char wh_type; -+#else -+ aufs_bindex_t wh_bindex; -+#endif -+ /* caution: packed */ -+ struct au_vdir_destr wh_str; -+} __packed; -+ -+union au_vdir_deblk_p { -+ unsigned char *deblk; -+ struct au_vdir_de *de; -+}; -+ -+struct au_vdir { -+ unsigned char **vd_deblk; -+ unsigned long vd_nblk; -+ struct { -+ unsigned long ul; -+ union au_vdir_deblk_p p; -+ } vd_last; -+ -+ unsigned long vd_version; -+ unsigned int vd_deblk_sz; -+ unsigned long vd_jiffy; -+} ____cacheline_aligned_in_smp; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dir.c */ -+extern const struct file_operations aufs_dir_fop; -+void au_add_nlink(struct inode *dir, struct inode *h_dir); -+void au_sub_nlink(struct inode *dir, struct inode *h_dir); -+loff_t au_dir_size(struct file *file, struct dentry *dentry); -+void au_dir_ts(struct inode *dir, aufs_bindex_t bsrc); -+int au_test_empty_lower(struct dentry *dentry); -+int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); -+ -+/* vdir.c */ -+unsigned int au_rdhash_est(loff_t sz); -+int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp); -+void au_nhash_wh_free(struct au_nhash *whlist); -+int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, -+ int limit); -+int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen); -+int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, -+ unsigned int d_type, aufs_bindex_t bindex, -+ unsigned char shwh); -+void au_vdir_free(struct au_vdir *vdir); -+int au_vdir_init(struct file *file); -+int au_vdir_fill_de(struct file *file, struct dir_context *ctx); -+ -+/* ioctl.c */ -+long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg); -+ -+#ifdef CONFIG_AUFS_RDU -+/* rdu.c */ -+long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg); -+#endif -+#else -+AuStub(long, au_rdu_ioctl, return -EINVAL, struct file *file, -+ unsigned int cmd, unsigned long arg) -+#ifdef CONFIG_COMPAT -+AuStub(long, au_rdu_compat_ioctl, return -EINVAL, struct file *file, -+ unsigned int cmd, unsigned long arg) -+#endif -+#endif -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DIR_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/dynop.c linux-4.0.4/fs/aufs/dynop.c ---- linux-4.0.4.orig/fs/aufs/dynop.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dynop.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,356 @@ -+/* -+ * Copyright (C) 2010-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * dynamically customizable operations for regular files -+ */ -+ -+#include "aufs.h" -+ -+#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) -+ -+/* -+ * How large will these lists be? -+ * Usually just a few elements, 20-30 at most for each, I guess. -+ */ -+static struct au_splhead dynop[AuDyLast]; -+ -+static struct au_dykey *dy_gfind_get(struct au_splhead *spl, const void *h_op) -+{ -+ struct au_dykey *key, *tmp; -+ struct list_head *head; -+ -+ key = NULL; -+ head = &spl->head; -+ rcu_read_lock(); -+ list_for_each_entry_rcu(tmp, head, dk_list) -+ if (tmp->dk_op.dy_hop == h_op) { -+ key = tmp; -+ kref_get(&key->dk_kref); -+ break; -+ } -+ rcu_read_unlock(); -+ -+ return key; -+} -+ -+static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) -+{ -+ struct au_dykey **k, *found; -+ const void *h_op = key->dk_op.dy_hop; -+ int i; -+ -+ found = NULL; -+ k = br->br_dykey; -+ for (i = 0; i < AuBrDynOp; i++) -+ if (k[i]) { -+ if (k[i]->dk_op.dy_hop == h_op) { -+ found = k[i]; -+ break; -+ } -+ } else -+ break; -+ if (!found) { -+ spin_lock(&br->br_dykey_lock); -+ for (; i < AuBrDynOp; i++) -+ if (k[i]) { -+ if (k[i]->dk_op.dy_hop == h_op) { -+ found = k[i]; -+ break; -+ } -+ } else { -+ k[i] = key; -+ break; -+ } -+ spin_unlock(&br->br_dykey_lock); -+ BUG_ON(i == AuBrDynOp); /* expand the array */ -+ } -+ -+ return found; -+} -+ -+/* kref_get() if @key is already added */ -+static struct au_dykey *dy_gadd(struct au_splhead *spl, struct au_dykey *key) -+{ -+ struct au_dykey *tmp, *found; -+ struct list_head *head; -+ const void *h_op = key->dk_op.dy_hop; -+ -+ found = NULL; -+ head = &spl->head; -+ spin_lock(&spl->spin); -+ list_for_each_entry(tmp, head, dk_list) -+ if (tmp->dk_op.dy_hop == h_op) { -+ kref_get(&tmp->dk_kref); -+ found = tmp; -+ break; -+ } -+ if (!found) -+ list_add_rcu(&key->dk_list, head); -+ spin_unlock(&spl->spin); -+ -+ if (!found) -+ DyPrSym(key); -+ return found; -+} -+ -+static void dy_free_rcu(struct rcu_head *rcu) -+{ -+ struct au_dykey *key; -+ -+ key = container_of(rcu, struct au_dykey, dk_rcu); -+ DyPrSym(key); -+ kfree(key); -+} -+ -+static void dy_free(struct kref *kref) -+{ -+ struct au_dykey *key; -+ struct au_splhead *spl; -+ -+ key = container_of(kref, struct au_dykey, dk_kref); -+ spl = dynop + key->dk_op.dy_type; -+ au_spl_del_rcu(&key->dk_list, spl); -+ call_rcu(&key->dk_rcu, dy_free_rcu); -+} -+ -+void au_dy_put(struct au_dykey *key) -+{ -+ kref_put(&key->dk_kref, dy_free); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) -+ -+#ifdef CONFIG_AUFS_DEBUG -+#define DyDbgDeclare(cnt) unsigned int cnt = 0 -+#define DyDbgInc(cnt) do { cnt++; } while (0) -+#else -+#define DyDbgDeclare(cnt) do {} while (0) -+#define DyDbgInc(cnt) do {} while (0) -+#endif -+ -+#define DySet(func, dst, src, h_op, h_sb) do { \ -+ DyDbgInc(cnt); \ -+ if (h_op->func) { \ -+ if (src.func) \ -+ dst.func = src.func; \ -+ else \ -+ AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ -+ } \ -+} while (0) -+ -+#define DySetForce(func, dst, src) do { \ -+ AuDebugOn(!src.func); \ -+ DyDbgInc(cnt); \ -+ dst.func = src.func; \ -+} while (0) -+ -+#define DySetAop(func) \ -+ DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) -+#define DySetAopForce(func) \ -+ DySetForce(func, dyaop->da_op, aufs_aop) -+ -+static void dy_aop(struct au_dykey *key, const void *h_op, -+ struct super_block *h_sb __maybe_unused) -+{ -+ struct au_dyaop *dyaop = (void *)key; -+ const struct address_space_operations *h_aop = h_op; -+ DyDbgDeclare(cnt); -+ -+ AuDbg("%s\n", au_sbtype(h_sb)); -+ -+ DySetAop(writepage); -+ DySetAopForce(readpage); /* force */ -+ DySetAop(writepages); -+ DySetAop(set_page_dirty); -+ DySetAop(readpages); -+ DySetAop(write_begin); -+ DySetAop(write_end); -+ DySetAop(bmap); -+ DySetAop(invalidatepage); -+ DySetAop(releasepage); -+ DySetAop(freepage); -+ /* this one will be changed according to an aufs mount option */ -+ DySetAop(direct_IO); -+ DySetAop(migratepage); -+ DySetAop(launder_page); -+ DySetAop(is_partially_uptodate); -+ DySetAop(is_dirty_writeback); -+ DySetAop(error_remove_page); -+ DySetAop(swap_activate); -+ DySetAop(swap_deactivate); -+ -+ DyDbgSize(cnt, *h_aop); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void dy_bug(struct kref *kref) -+{ -+ BUG(); -+} -+ -+static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) -+{ -+ struct au_dykey *key, *old; -+ struct au_splhead *spl; -+ struct op { -+ unsigned int sz; -+ void (*set)(struct au_dykey *key, const void *h_op, -+ struct super_block *h_sb __maybe_unused); -+ }; -+ static const struct op a[] = { -+ [AuDy_AOP] = { -+ .sz = sizeof(struct au_dyaop), -+ .set = dy_aop -+ } -+ }; -+ const struct op *p; -+ -+ spl = dynop + op->dy_type; -+ key = dy_gfind_get(spl, op->dy_hop); -+ if (key) -+ goto out_add; /* success */ -+ -+ p = a + op->dy_type; -+ key = kzalloc(p->sz, GFP_NOFS); -+ if (unlikely(!key)) { -+ key = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ key->dk_op.dy_hop = op->dy_hop; -+ kref_init(&key->dk_kref); -+ p->set(key, op->dy_hop, au_br_sb(br)); -+ old = dy_gadd(spl, key); -+ if (old) { -+ kfree(key); -+ key = old; -+ } -+ -+out_add: -+ old = dy_bradd(br, key); -+ if (old) -+ /* its ref-count should never be zero here */ -+ kref_put(&key->dk_kref, dy_bug); -+out: -+ return key; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * Aufs prohibits O_DIRECT by defaut even if the branch supports it. -+ * This behaviour is necessary to return an error from open(O_DIRECT) instead -+ * of the succeeding I/O. The dio mount option enables O_DIRECT and makes -+ * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. -+ * See the aufs manual in detail. -+ */ -+static void dy_adx(struct au_dyaop *dyaop, int do_dx) -+{ -+ if (!do_dx) -+ dyaop->da_op.direct_IO = NULL; -+ else -+ dyaop->da_op.direct_IO = aufs_aop.direct_IO; -+} -+ -+static struct au_dyaop *dy_aget(struct au_branch *br, -+ const struct address_space_operations *h_aop, -+ int do_dx) -+{ -+ struct au_dyaop *dyaop; -+ struct au_dynop op; -+ -+ op.dy_type = AuDy_AOP; -+ op.dy_haop = h_aop; -+ dyaop = (void *)dy_get(&op, br); -+ if (IS_ERR(dyaop)) -+ goto out; -+ dy_adx(dyaop, do_dx); -+ -+out: -+ return dyaop; -+} -+ -+int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode) -+{ -+ int err, do_dx; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_dyaop *dyaop; -+ -+ AuDebugOn(!S_ISREG(h_inode->i_mode)); -+ IiMustWriteLock(inode); -+ -+ sb = inode->i_sb; -+ br = au_sbr(sb, bindex); -+ do_dx = !!au_opt_test(au_mntflags(sb), DIO); -+ dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx); -+ err = PTR_ERR(dyaop); -+ if (IS_ERR(dyaop)) -+ /* unnecessary to call dy_fput() */ -+ goto out; -+ -+ err = 0; -+ inode->i_mapping->a_ops = &dyaop->da_op; -+ -+out: -+ return err; -+} -+ -+/* -+ * Is it safe to replace a_ops during the inode/file is in operation? -+ * Yes, I hope so. -+ */ -+int au_dy_irefresh(struct inode *inode) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ struct inode *h_inode; -+ -+ err = 0; -+ if (S_ISREG(inode->i_mode)) { -+ bstart = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bstart); -+ err = au_dy_iaop(inode, bstart, h_inode); -+ } -+ return err; -+} -+ -+void au_dy_arefresh(int do_dx) -+{ -+ struct au_splhead *spl; -+ struct list_head *head; -+ struct au_dykey *key; -+ -+ spl = dynop + AuDy_AOP; -+ head = &spl->head; -+ spin_lock(&spl->spin); -+ list_for_each_entry(key, head, dk_list) -+ dy_adx((void *)key, do_dx); -+ spin_unlock(&spl->spin); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void __init au_dy_init(void) -+{ -+ int i; -+ -+ /* make sure that 'struct au_dykey *' can be any type */ -+ BUILD_BUG_ON(offsetof(struct au_dyaop, da_key)); -+ -+ for (i = 0; i < AuDyLast; i++) -+ au_spl_init(dynop + i); -+} -+ -+void au_dy_fin(void) -+{ -+ int i; -+ -+ for (i = 0; i < AuDyLast; i++) -+ WARN_ON(!list_empty(&dynop[i].head)); -+} -diff -Nur linux-4.0.4.orig/fs/aufs/dynop.h linux-4.0.4/fs/aufs/dynop.h ---- linux-4.0.4.orig/fs/aufs/dynop.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/dynop.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,61 @@ -+/* -+ * Copyright (C) 2010-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * dynamically customizable operations (for regular files only) -+ */ -+ -+#ifndef __AUFS_DYNOP_H__ -+#define __AUFS_DYNOP_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+ -+enum {AuDy_AOP, AuDyLast}; -+ -+struct au_dynop { -+ int dy_type; -+ union { -+ const void *dy_hop; -+ const struct address_space_operations *dy_haop; -+ }; -+}; -+ -+struct au_dykey { -+ union { -+ struct list_head dk_list; -+ struct rcu_head dk_rcu; -+ }; -+ struct au_dynop dk_op; -+ -+ /* -+ * during I am in the branch local array, kref is gotten. when the -+ * branch is removed, kref is put. -+ */ -+ struct kref dk_kref; -+}; -+ -+/* stop unioning since their sizes are very different from each other */ -+struct au_dyaop { -+ struct au_dykey da_key; -+ struct address_space_operations da_op; /* not const */ -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* dynop.c */ -+struct au_branch; -+void au_dy_put(struct au_dykey *key); -+int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode); -+int au_dy_irefresh(struct inode *inode); -+void au_dy_arefresh(int do_dio); -+ -+void __init au_dy_init(void); -+void au_dy_fin(void); -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_DYNOP_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/export.c linux-4.0.4/fs/aufs/export.c ---- linux-4.0.4.orig/fs/aufs/export.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/export.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,818 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * export via nfs -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "../fs/mount.h" -+#include "aufs.h" -+ -+union conv { -+#ifdef CONFIG_AUFS_INO_T_64 -+ __u32 a[2]; -+#else -+ __u32 a[1]; -+#endif -+ ino_t ino; -+}; -+ -+static ino_t decode_ino(__u32 *a) -+{ -+ union conv u; -+ -+ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); -+ u.a[0] = a[0]; -+#ifdef CONFIG_AUFS_INO_T_64 -+ u.a[1] = a[1]; -+#endif -+ return u.ino; -+} -+ -+static void encode_ino(__u32 *a, ino_t ino) -+{ -+ union conv u; -+ -+ u.ino = ino; -+ a[0] = u.a[0]; -+#ifdef CONFIG_AUFS_INO_T_64 -+ a[1] = u.a[1]; -+#endif -+} -+ -+/* NFS file handle */ -+enum { -+ Fh_br_id, -+ Fh_sigen, -+#ifdef CONFIG_AUFS_INO_T_64 -+ /* support 64bit inode number */ -+ Fh_ino1, -+ Fh_ino2, -+ Fh_dir_ino1, -+ Fh_dir_ino2, -+#else -+ Fh_ino1, -+ Fh_dir_ino1, -+#endif -+ Fh_igen, -+ Fh_h_type, -+ Fh_tail, -+ -+ Fh_ino = Fh_ino1, -+ Fh_dir_ino = Fh_dir_ino1 -+}; -+ -+static int au_test_anon(struct dentry *dentry) -+{ -+ /* note: read d_flags without d_lock */ -+ return !!(dentry->d_flags & DCACHE_DISCONNECTED); -+} -+ -+int au_test_nfsd(void) -+{ -+ int ret; -+ struct task_struct *tsk = current; -+ char comm[sizeof(tsk->comm)]; -+ -+ ret = 0; -+ if (tsk->flags & PF_KTHREAD) { -+ get_task_comm(comm, tsk); -+ ret = !strcmp(comm, "nfsd"); -+ } -+ -+ return ret; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* inode generation external table */ -+ -+void au_xigen_inc(struct inode *inode) -+{ -+ loff_t pos; -+ ssize_t sz; -+ __u32 igen; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+ sb = inode->i_sb; -+ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); -+ -+ sbinfo = au_sbi(sb); -+ pos = inode->i_ino; -+ pos *= sizeof(igen); -+ igen = inode->i_generation + 1; -+ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, -+ sizeof(igen), &pos); -+ if (sz == sizeof(igen)) -+ return; /* success */ -+ -+ if (unlikely(sz >= 0)) -+ AuIOErr("xigen error (%zd)\n", sz); -+} -+ -+int au_xigen_new(struct inode *inode) -+{ -+ int err; -+ loff_t pos; -+ ssize_t sz; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ -+ err = 0; -+ /* todo: dirty, at mount time */ -+ if (inode->i_ino == AUFS_ROOT_INO) -+ goto out; -+ sb = inode->i_sb; -+ SiMustAnyLock(sb); -+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) -+ goto out; -+ -+ err = -EFBIG; -+ pos = inode->i_ino; -+ if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { -+ AuIOErr1("too large i%lld\n", pos); -+ goto out; -+ } -+ pos *= sizeof(inode->i_generation); -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ file = sbinfo->si_xigen; -+ BUG_ON(!file); -+ -+ if (vfsub_f_size_read(file) -+ < pos + sizeof(inode->i_generation)) { -+ inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); -+ sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, -+ sizeof(inode->i_generation), &pos); -+ } else -+ sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, -+ sizeof(inode->i_generation), &pos); -+ if (sz == sizeof(inode->i_generation)) -+ goto out; /* success */ -+ -+ err = sz; -+ if (unlikely(sz >= 0)) { -+ err = -EIO; -+ AuIOErr("xigen error (%zd)\n", sz); -+ } -+ -+out: -+ return err; -+} -+ -+int au_xigen_set(struct super_block *sb, struct file *base) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ file = au_xino_create2(base, sbinfo->si_xigen); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ err = 0; -+ if (sbinfo->si_xigen) -+ fput(sbinfo->si_xigen); -+ sbinfo->si_xigen = file; -+ -+out: -+ return err; -+} -+ -+void au_xigen_clr(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ if (sbinfo->si_xigen) { -+ fput(sbinfo->si_xigen); -+ sbinfo->si_xigen = NULL; -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, -+ ino_t dir_ino) -+{ -+ struct dentry *dentry, *d; -+ struct inode *inode; -+ unsigned int sigen; -+ -+ dentry = NULL; -+ inode = ilookup(sb, ino); -+ if (!inode) -+ goto out; -+ -+ dentry = ERR_PTR(-ESTALE); -+ sigen = au_sigen(sb); -+ if (unlikely(is_bad_inode(inode) -+ || IS_DEADDIR(inode) -+ || sigen != au_iigen(inode, NULL))) -+ goto out_iput; -+ -+ dentry = NULL; -+ if (!dir_ino || S_ISDIR(inode->i_mode)) -+ dentry = d_find_alias(inode); -+ else { -+ spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { -+ spin_lock(&d->d_lock); -+ if (!au_test_anon(d) -+ && d->d_parent->d_inode->i_ino == dir_ino) { -+ dentry = dget_dlock(d); -+ spin_unlock(&d->d_lock); -+ break; -+ } -+ spin_unlock(&d->d_lock); -+ } -+ spin_unlock(&inode->i_lock); -+ } -+ if (unlikely(dentry && au_digen_test(dentry, sigen))) { -+ /* need to refresh */ -+ dput(dentry); -+ dentry = NULL; -+ } -+ -+out_iput: -+ iput(inode); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: dirty? */ -+/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ -+ -+struct au_compare_mnt_args { -+ /* input */ -+ struct super_block *sb; -+ -+ /* output */ -+ struct vfsmount *mnt; -+}; -+ -+static int au_compare_mnt(struct vfsmount *mnt, void *arg) -+{ -+ struct au_compare_mnt_args *a = arg; -+ -+ if (mnt->mnt_sb != a->sb) -+ return 0; -+ a->mnt = mntget(mnt); -+ return 1; -+} -+ -+static struct vfsmount *au_mnt_get(struct super_block *sb) -+{ -+ int err; -+ struct path root; -+ struct au_compare_mnt_args args = { -+ .sb = sb -+ }; -+ -+ get_fs_root(current->fs, &root); -+ rcu_read_lock(); -+ err = iterate_mounts(au_compare_mnt, &args, root.mnt); -+ rcu_read_unlock(); -+ path_put(&root); -+ AuDebugOn(!err); -+ AuDebugOn(!args.mnt); -+ return args.mnt; -+} -+ -+struct au_nfsd_si_lock { -+ unsigned int sigen; -+ aufs_bindex_t bindex, br_id; -+ unsigned char force_lock; -+}; -+ -+static int si_nfsd_read_lock(struct super_block *sb, -+ struct au_nfsd_si_lock *nsi_lock) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ -+ si_read_lock(sb, AuLock_FLUSH); -+ -+ /* branch id may be wrapped around */ -+ err = 0; -+ bindex = au_br_index(sb, nsi_lock->br_id); -+ if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) -+ goto out; /* success */ -+ -+ err = -ESTALE; -+ bindex = -1; -+ if (!nsi_lock->force_lock) -+ si_read_unlock(sb); -+ -+out: -+ nsi_lock->bindex = bindex; -+ return err; -+} -+ -+struct find_name_by_ino { -+ struct dir_context ctx; -+ int called, found; -+ ino_t ino; -+ char *name; -+ int namelen; -+}; -+ -+static int -+find_name_by_ino(struct dir_context *ctx, const char *name, int namelen, -+ loff_t offset, u64 ino, unsigned int d_type) -+{ -+ struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino, -+ ctx); -+ -+ a->called++; -+ if (a->ino != ino) -+ return 0; -+ -+ memcpy(a->name, name, namelen); -+ a->namelen = namelen; -+ a->found = 1; -+ return 1; -+} -+ -+static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, -+ struct au_nfsd_si_lock *nsi_lock) -+{ -+ struct dentry *dentry, *parent; -+ struct file *file; -+ struct inode *dir; -+ struct find_name_by_ino arg = { -+ .ctx = { -+ .actor = find_name_by_ino -+ } -+ }; -+ int err; -+ -+ parent = path->dentry; -+ if (nsi_lock) -+ si_read_unlock(parent->d_sb); -+ file = vfsub_dentry_open(path, au_dir_roflags); -+ dentry = (void *)file; -+ if (IS_ERR(file)) -+ goto out; -+ -+ dentry = ERR_PTR(-ENOMEM); -+ arg.name = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!arg.name)) -+ goto out_file; -+ arg.ino = ino; -+ arg.found = 0; -+ do { -+ arg.called = 0; -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(file, &arg.ctx); -+ } while (!err && !arg.found && arg.called); -+ dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_name; -+ /* instead of ENOENT */ -+ dentry = ERR_PTR(-ESTALE); -+ if (!arg.found) -+ goto out_name; -+ -+ /* do not call vfsub_lkup_one() */ -+ dir = parent->d_inode; -+ mutex_lock(&dir->i_mutex); -+ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); -+ mutex_unlock(&dir->i_mutex); -+ AuTraceErrPtr(dentry); -+ if (IS_ERR(dentry)) -+ goto out_name; -+ AuDebugOn(au_test_anon(dentry)); -+ if (unlikely(!dentry->d_inode)) { -+ dput(dentry); -+ dentry = ERR_PTR(-ENOENT); -+ } -+ -+out_name: -+ free_page((unsigned long)arg.name); -+out_file: -+ fput(file); -+out: -+ if (unlikely(nsi_lock -+ && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) -+ if (!IS_ERR(dentry)) { -+ dput(dentry); -+ dentry = ERR_PTR(-ESTALE); -+ } -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, -+ ino_t dir_ino, -+ struct au_nfsd_si_lock *nsi_lock) -+{ -+ struct dentry *dentry; -+ struct path path; -+ -+ if (dir_ino != AUFS_ROOT_INO) { -+ path.dentry = decode_by_ino(sb, dir_ino, 0); -+ dentry = path.dentry; -+ if (!path.dentry || IS_ERR(path.dentry)) -+ goto out; -+ AuDebugOn(au_test_anon(path.dentry)); -+ } else -+ path.dentry = dget(sb->s_root); -+ -+ path.mnt = au_mnt_get(sb); -+ dentry = au_lkup_by_ino(&path, ino, nsi_lock); -+ path_put(&path); -+ -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int h_acceptable(void *expv, struct dentry *dentry) -+{ -+ return 1; -+} -+ -+static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, -+ char *buf, int len, struct super_block *sb) -+{ -+ char *p; -+ int n; -+ struct path path; -+ -+ p = d_path(h_rootpath, buf, len); -+ if (IS_ERR(p)) -+ goto out; -+ n = strlen(p); -+ -+ path.mnt = h_rootpath->mnt; -+ path.dentry = h_parent; -+ p = d_path(&path, buf, len); -+ if (IS_ERR(p)) -+ goto out; -+ if (n != 1) -+ p += n; -+ -+ path.mnt = au_mnt_get(sb); -+ path.dentry = sb->s_root; -+ p = d_path(&path, buf, len - strlen(p)); -+ mntput(path.mnt); -+ if (IS_ERR(p)) -+ goto out; -+ if (n != 1) -+ p[strlen(p)] = '/'; -+ -+out: -+ AuTraceErrPtr(p); -+ return p; -+} -+ -+static -+struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, -+ int fh_len, struct au_nfsd_si_lock *nsi_lock) -+{ -+ struct dentry *dentry, *h_parent, *root; -+ struct super_block *h_sb; -+ char *pathname, *p; -+ struct vfsmount *h_mnt; -+ struct au_branch *br; -+ int err; -+ struct path path; -+ -+ br = au_sbr(sb, nsi_lock->bindex); -+ h_mnt = au_br_mnt(br); -+ h_sb = h_mnt->mnt_sb; -+ /* todo: call lower fh_to_dentry()? fh_to_parent()? */ -+ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), -+ fh_len - Fh_tail, fh[Fh_h_type], -+ h_acceptable, /*context*/NULL); -+ dentry = h_parent; -+ if (unlikely(!h_parent || IS_ERR(h_parent))) { -+ AuWarn1("%s decode_fh failed, %ld\n", -+ au_sbtype(h_sb), PTR_ERR(h_parent)); -+ goto out; -+ } -+ dentry = NULL; -+ if (unlikely(au_test_anon(h_parent))) { -+ AuWarn1("%s decode_fh returned a disconnected dentry\n", -+ au_sbtype(h_sb)); -+ goto out_h_parent; -+ } -+ -+ dentry = ERR_PTR(-ENOMEM); -+ pathname = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!pathname)) -+ goto out_h_parent; -+ -+ root = sb->s_root; -+ path.mnt = h_mnt; -+ di_read_lock_parent(root, !AuLock_IR); -+ path.dentry = au_h_dptr(root, nsi_lock->bindex); -+ di_read_unlock(root, !AuLock_IR); -+ p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); -+ dentry = (void *)p; -+ if (IS_ERR(p)) -+ goto out_pathname; -+ -+ si_read_unlock(sb); -+ err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); -+ dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_relock; -+ -+ dentry = ERR_PTR(-ENOENT); -+ AuDebugOn(au_test_anon(path.dentry)); -+ if (unlikely(!path.dentry->d_inode)) -+ goto out_path; -+ -+ if (ino != path.dentry->d_inode->i_ino) -+ dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); -+ else -+ dentry = dget(path.dentry); -+ -+out_path: -+ path_put(&path); -+out_relock: -+ if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) -+ if (!IS_ERR(dentry)) { -+ dput(dentry); -+ dentry = ERR_PTR(-ESTALE); -+ } -+out_pathname: -+ free_page((unsigned long)pathname); -+out_h_parent: -+ dput(h_parent); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry * -+aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, -+ int fh_type) -+{ -+ struct dentry *dentry; -+ __u32 *fh = fid->raw; -+ struct au_branch *br; -+ ino_t ino, dir_ino; -+ struct au_nfsd_si_lock nsi_lock = { -+ .force_lock = 0 -+ }; -+ -+ dentry = ERR_PTR(-ESTALE); -+ /* it should never happen, but the file handle is unreliable */ -+ if (unlikely(fh_len < Fh_tail)) -+ goto out; -+ nsi_lock.sigen = fh[Fh_sigen]; -+ nsi_lock.br_id = fh[Fh_br_id]; -+ -+ /* branch id may be wrapped around */ -+ br = NULL; -+ if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) -+ goto out; -+ nsi_lock.force_lock = 1; -+ -+ /* is this inode still cached? */ -+ ino = decode_ino(fh + Fh_ino); -+ /* it should never happen */ -+ if (unlikely(ino == AUFS_ROOT_INO)) -+ goto out; -+ -+ dir_ino = decode_ino(fh + Fh_dir_ino); -+ dentry = decode_by_ino(sb, ino, dir_ino); -+ if (IS_ERR(dentry)) -+ goto out_unlock; -+ if (dentry) -+ goto accept; -+ -+ /* is the parent dir cached? */ -+ br = au_sbr(sb, nsi_lock.bindex); -+ atomic_inc(&br->br_count); -+ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); -+ if (IS_ERR(dentry)) -+ goto out_unlock; -+ if (dentry) -+ goto accept; -+ -+ /* lookup path */ -+ dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); -+ if (IS_ERR(dentry)) -+ goto out_unlock; -+ if (unlikely(!dentry)) -+ /* todo?: make it ESTALE */ -+ goto out_unlock; -+ -+accept: -+ if (!au_digen_test(dentry, au_sigen(sb)) -+ && dentry->d_inode->i_generation == fh[Fh_igen]) -+ goto out_unlock; /* success */ -+ -+ dput(dentry); -+ dentry = ERR_PTR(-ESTALE); -+out_unlock: -+ if (br) -+ atomic_dec(&br->br_count); -+ si_read_unlock(sb); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+#if 0 /* reserved for future use */ -+/* support subtreecheck option */ -+static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, -+ int fh_len, int fh_type) -+{ -+ struct dentry *parent; -+ __u32 *fh = fid->raw; -+ ino_t dir_ino; -+ -+ dir_ino = decode_ino(fh + Fh_dir_ino); -+ parent = decode_by_ino(sb, dir_ino, 0); -+ if (IS_ERR(parent)) -+ goto out; -+ if (!parent) -+ parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), -+ dir_ino, fh, fh_len); -+ -+out: -+ AuTraceErrPtr(parent); -+ return parent; -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, -+ struct inode *dir) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct super_block *sb, *h_sb; -+ struct dentry *dentry, *parent, *h_parent; -+ struct inode *h_dir; -+ struct au_branch *br; -+ -+ err = -ENOSPC; -+ if (unlikely(*max_len <= Fh_tail)) { -+ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); -+ goto out; -+ } -+ -+ err = FILEID_ROOT; -+ if (inode->i_ino == AUFS_ROOT_INO) { -+ AuDebugOn(inode->i_ino != AUFS_ROOT_INO); -+ goto out; -+ } -+ -+ h_parent = NULL; -+ sb = inode->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH); -+ if (unlikely(err)) -+ goto out; -+ -+#ifdef CONFIG_AUFS_DEBUG -+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) -+ AuWarn1("NFS-exporting requires xino\n"); -+#endif -+ err = -EIO; -+ parent = NULL; -+ ii_read_lock_child(inode); -+ bindex = au_ibstart(inode); -+ if (!dir) { -+ dentry = d_find_any_alias(inode); -+ if (unlikely(!dentry)) -+ goto out_unlock; -+ AuDebugOn(au_test_anon(dentry)); -+ parent = dget_parent(dentry); -+ dput(dentry); -+ if (unlikely(!parent)) -+ goto out_unlock; -+ dir = parent->d_inode; -+ } -+ -+ ii_read_lock_parent(dir); -+ h_dir = au_h_iptr(dir, bindex); -+ ii_read_unlock(dir); -+ if (unlikely(!h_dir)) -+ goto out_parent; -+ h_parent = d_find_any_alias(h_dir); -+ if (unlikely(!h_parent)) -+ goto out_hparent; -+ -+ err = -EPERM; -+ br = au_sbr(sb, bindex); -+ h_sb = au_br_sb(br); -+ if (unlikely(!h_sb->s_export_op)) { -+ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); -+ goto out_hparent; -+ } -+ -+ fh[Fh_br_id] = br->br_id; -+ fh[Fh_sigen] = au_sigen(sb); -+ encode_ino(fh + Fh_ino, inode->i_ino); -+ encode_ino(fh + Fh_dir_ino, dir->i_ino); -+ fh[Fh_igen] = inode->i_generation; -+ -+ *max_len -= Fh_tail; -+ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), -+ max_len, -+ /*connectable or subtreecheck*/0); -+ err = fh[Fh_h_type]; -+ *max_len += Fh_tail; -+ /* todo: macros? */ -+ if (err != FILEID_INVALID) -+ err = 99; -+ else -+ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); -+ -+out_hparent: -+ dput(h_parent); -+out_parent: -+ dput(parent); -+out_unlock: -+ ii_read_unlock(inode); -+ si_read_unlock(sb); -+out: -+ if (unlikely(err < 0)) -+ err = FILEID_INVALID; -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_commit_metadata(struct inode *inode) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct super_block *sb; -+ struct inode *h_inode; -+ int (*f)(struct inode *inode); -+ -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ ii_write_lock_child(inode); -+ bindex = au_ibstart(inode); -+ AuDebugOn(bindex < 0); -+ h_inode = au_h_iptr(inode, bindex); -+ -+ f = h_inode->i_sb->s_export_op->commit_metadata; -+ if (f) -+ err = f(h_inode); -+ else { -+ struct writeback_control wbc = { -+ .sync_mode = WB_SYNC_ALL, -+ .nr_to_write = 0 /* metadata only */ -+ }; -+ -+ err = sync_inode(h_inode, &wbc); -+ } -+ -+ au_cpup_attr_timesizes(inode); -+ ii_write_unlock(inode); -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct export_operations aufs_export_op = { -+ .fh_to_dentry = aufs_fh_to_dentry, -+ /* .fh_to_parent = aufs_fh_to_parent, */ -+ .encode_fh = aufs_encode_fh, -+ .commit_metadata = aufs_commit_metadata -+}; -+ -+void au_export_init(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ __u32 u; -+ -+ sb->s_export_op = &aufs_export_op; -+ sbinfo = au_sbi(sb); -+ sbinfo->si_xigen = NULL; -+ get_random_bytes(&u, sizeof(u)); -+ BUILD_BUG_ON(sizeof(u) != sizeof(int)); -+ atomic_set(&sbinfo->si_xigen_next, u); -+} -diff -Nur linux-4.0.4.orig/fs/aufs/fhsm.c linux-4.0.4/fs/aufs/fhsm.c ---- linux-4.0.4.orig/fs/aufs/fhsm.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/fhsm.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,412 @@ -+/* -+ * Copyright (C) 2011-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * File-based Hierarchy Storage Management -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+static aufs_bindex_t au_fhsm_bottom(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ AuDebugOn(!fhsm); -+ return fhsm->fhsm_bottom; -+} -+ -+void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ AuDebugOn(!fhsm); -+ fhsm->fhsm_bottom = bindex; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_fhsm_test_jiffy(struct au_sbinfo *sbinfo, struct au_branch *br) -+{ -+ struct au_br_fhsm *bf; -+ -+ bf = br->br_fhsm; -+ MtxMustLock(&bf->bf_lock); -+ -+ return !bf->bf_readable -+ || time_after(jiffies, -+ bf->bf_jiffy + sbinfo->si_fhsm.fhsm_expire); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_fhsm_notify(struct super_block *sb, int val) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ if (au_fhsm_pid(fhsm) -+ && atomic_read(&fhsm->fhsm_readable) != -1) { -+ atomic_set(&fhsm->fhsm_readable, val); -+ if (val) -+ wake_up(&fhsm->fhsm_wqh); -+ } -+} -+ -+static int au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex, -+ struct aufs_stfs *rstfs, int do_lock, int do_notify) -+{ -+ int err; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ br = au_sbr(sb, bindex); -+ AuDebugOn(au_br_rdonly(br)); -+ bf = br->br_fhsm; -+ AuDebugOn(!bf); -+ -+ if (do_lock) -+ mutex_lock(&bf->bf_lock); -+ else -+ MtxMustLock(&bf->bf_lock); -+ -+ /* sb->s_root for NFS is unreliable */ -+ err = au_br_stfs(br, &bf->bf_stfs); -+ if (unlikely(err)) { -+ AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err); -+ goto out; -+ } -+ -+ bf->bf_jiffy = jiffies; -+ bf->bf_readable = 1; -+ if (do_notify) -+ au_fhsm_notify(sb, /*val*/1); -+ if (rstfs) -+ *rstfs = bf->bf_stfs; -+ -+out: -+ if (do_lock) -+ mutex_unlock(&bf->bf_lock); -+ au_fhsm_notify(sb, /*val*/1); -+ -+ return err; -+} -+ -+void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ AuDbg("b%d, force %d\n", bindex, force); -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ if (!au_ftest_si(sbinfo, FHSM) -+ || fhsm->fhsm_bottom == bindex) -+ return; -+ -+ br = au_sbr(sb, bindex); -+ bf = br->br_fhsm; -+ AuDebugOn(!bf); -+ mutex_lock(&bf->bf_lock); -+ if (force -+ || au_fhsm_pid(fhsm) -+ || au_fhsm_test_jiffy(sbinfo, br)) -+ err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0, -+ /*do_notify*/1); -+ mutex_unlock(&bf->bf_lock); -+} -+ -+void au_fhsm_wrote_all(struct super_block *sb, int force) -+{ -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ -+ /* exclude the bottom */ -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; bindex < bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm)) -+ au_fhsm_wrote(sb, bindex, force); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static unsigned int au_fhsm_poll(struct file *file, -+ struct poll_table_struct *wait) -+{ -+ unsigned int mask; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ mask = 0; -+ sbinfo = file->private_data; -+ fhsm = &sbinfo->si_fhsm; -+ poll_wait(file, &fhsm->fhsm_wqh, wait); -+ if (atomic_read(&fhsm->fhsm_readable)) -+ mask = POLLIN /* | POLLRDNORM */; -+ -+ AuTraceErr((int)mask); -+ return mask; -+} -+ -+static int au_fhsm_do_read_one(struct aufs_stbr __user *stbr, -+ struct aufs_stfs *stfs, __s16 brid) -+{ -+ int err; -+ -+ err = copy_to_user(&stbr->stfs, stfs, sizeof(*stfs)); -+ if (!err) -+ err = __put_user(brid, &stbr->brid); -+ if (unlikely(err)) -+ err = -EFAULT; -+ -+ return err; -+} -+ -+static ssize_t au_fhsm_do_read(struct super_block *sb, -+ struct aufs_stbr __user *stbr, size_t count) -+{ -+ ssize_t err; -+ int nstbr; -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ struct au_br_fhsm *bf; -+ -+ /* except the bottom branch */ -+ err = 0; -+ nstbr = 0; -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; !err && bindex < bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!au_br_fhsm(br->br_perm)) -+ continue; -+ -+ bf = br->br_fhsm; -+ mutex_lock(&bf->bf_lock); -+ if (bf->bf_readable) { -+ err = -EFAULT; -+ if (count >= sizeof(*stbr)) -+ err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs, -+ br->br_id); -+ if (!err) { -+ bf->bf_readable = 0; -+ count -= sizeof(*stbr); -+ nstbr++; -+ } -+ } -+ mutex_unlock(&bf->bf_lock); -+ } -+ if (!err) -+ err = sizeof(*stbr) * nstbr; -+ -+ return err; -+} -+ -+static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count, -+ loff_t *pos) -+{ -+ ssize_t err; -+ int readable; -+ aufs_bindex_t nfhsm, bindex, bend; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ struct au_branch *br; -+ struct super_block *sb; -+ -+ err = 0; -+ sbinfo = file->private_data; -+ fhsm = &sbinfo->si_fhsm; -+need_data: -+ spin_lock_irq(&fhsm->fhsm_wqh.lock); -+ if (!atomic_read(&fhsm->fhsm_readable)) { -+ if (vfsub_file_flags(file) & O_NONBLOCK) -+ err = -EAGAIN; -+ else -+ err = wait_event_interruptible_locked_irq -+ (fhsm->fhsm_wqh, -+ atomic_read(&fhsm->fhsm_readable)); -+ } -+ spin_unlock_irq(&fhsm->fhsm_wqh.lock); -+ if (unlikely(err)) -+ goto out; -+ -+ /* sb may already be dead */ -+ au_rw_read_lock(&sbinfo->si_rwsem); -+ readable = atomic_read(&fhsm->fhsm_readable); -+ if (readable > 0) { -+ sb = sbinfo->si_sb; -+ AuDebugOn(!sb); -+ /* exclude the bottom branch */ -+ nfhsm = 0; -+ bend = au_fhsm_bottom(sb); -+ for (bindex = 0; bindex < bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm)) -+ nfhsm++; -+ } -+ err = -EMSGSIZE; -+ if (nfhsm * sizeof(struct aufs_stbr) <= count) { -+ atomic_set(&fhsm->fhsm_readable, 0); -+ err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf, -+ count); -+ } -+ } -+ au_rw_read_unlock(&sbinfo->si_rwsem); -+ if (!readable) -+ goto need_data; -+ -+out: -+ return err; -+} -+ -+static int au_fhsm_release(struct inode *inode, struct file *file) -+{ -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ /* sb may already be dead */ -+ sbinfo = file->private_data; -+ fhsm = &sbinfo->si_fhsm; -+ spin_lock(&fhsm->fhsm_spin); -+ fhsm->fhsm_pid = 0; -+ spin_unlock(&fhsm->fhsm_spin); -+ kobject_put(&sbinfo->si_kobj); -+ -+ return 0; -+} -+ -+static const struct file_operations au_fhsm_fops = { -+ .owner = THIS_MODULE, -+ .llseek = noop_llseek, -+ .read = au_fhsm_read, -+ .poll = au_fhsm_poll, -+ .release = au_fhsm_release -+}; -+ -+int au_fhsm_fd(struct super_block *sb, int oflags) -+{ -+ int err, fd; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ -+ err = -EPERM; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = -EINVAL; -+ if (unlikely(oflags & ~(O_CLOEXEC | O_NONBLOCK))) -+ goto out; -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ spin_lock(&fhsm->fhsm_spin); -+ if (!fhsm->fhsm_pid) -+ fhsm->fhsm_pid = current->pid; -+ else -+ err = -EBUSY; -+ spin_unlock(&fhsm->fhsm_spin); -+ if (unlikely(err)) -+ goto out; -+ -+ oflags |= O_RDONLY; -+ /* oflags |= FMODE_NONOTIFY; */ -+ fd = anon_inode_getfd("[aufs_fhsm]", &au_fhsm_fops, sbinfo, oflags); -+ err = fd; -+ if (unlikely(fd < 0)) -+ goto out_pid; -+ -+ /* succeed reglardless 'fhsm' status */ -+ kobject_get(&sbinfo->si_kobj); -+ si_noflush_read_lock(sb); -+ if (au_ftest_si(sbinfo, FHSM)) -+ au_fhsm_wrote_all(sb, /*force*/0); -+ si_read_unlock(sb); -+ goto out; /* success */ -+ -+out_pid: -+ spin_lock(&fhsm->fhsm_spin); -+ fhsm->fhsm_pid = 0; -+ spin_unlock(&fhsm->fhsm_spin); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_fhsm_br_alloc(struct au_branch *br) -+{ -+ int err; -+ -+ err = 0; -+ br->br_fhsm = kmalloc(sizeof(*br->br_fhsm), GFP_NOFS); -+ if (br->br_fhsm) -+ au_br_fhsm_init(br->br_fhsm); -+ else -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_fhsm_fin(struct super_block *sb) -+{ -+ au_fhsm_notify(sb, /*val*/-1); -+} -+ -+void au_fhsm_init(struct au_sbinfo *sbinfo) -+{ -+ struct au_fhsm *fhsm; -+ -+ fhsm = &sbinfo->si_fhsm; -+ spin_lock_init(&fhsm->fhsm_spin); -+ init_waitqueue_head(&fhsm->fhsm_wqh); -+ atomic_set(&fhsm->fhsm_readable, 0); -+ fhsm->fhsm_expire -+ = msecs_to_jiffies(AUFS_FHSM_CACHE_DEF_SEC * MSEC_PER_SEC); -+ fhsm->fhsm_bottom = -1; -+} -+ -+void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec) -+{ -+ sbinfo->si_fhsm.fhsm_expire -+ = msecs_to_jiffies(sec * MSEC_PER_SEC); -+} -+ -+void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo) -+{ -+ unsigned int u; -+ -+ if (!au_ftest_si(sbinfo, FHSM)) -+ return; -+ -+ u = jiffies_to_msecs(sbinfo->si_fhsm.fhsm_expire) / MSEC_PER_SEC; -+ if (u != AUFS_FHSM_CACHE_DEF_SEC) -+ seq_printf(seq, ",fhsm_sec=%u", u); -+} -diff -Nur linux-4.0.4.orig/fs/aufs/file.c linux-4.0.4/fs/aufs/file.c ---- linux-4.0.4.orig/fs/aufs/file.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/file.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,831 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * handling file/dir, and address_space operation -+ */ -+ -+#ifdef CONFIG_AUFS_DEBUG -+#include -+#endif -+#include -+#include "aufs.h" -+ -+/* drop flags for writing */ -+unsigned int au_file_roflags(unsigned int flags) -+{ -+ flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); -+ flags |= O_RDONLY | O_NOATIME; -+ return flags; -+} -+ -+/* common functions to regular file and dir */ -+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, -+ struct file *file, int force_wr) -+{ -+ struct file *h_file; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct path h_path; -+ int err; -+ -+ /* a race condition can happen between open and unlink/rmdir */ -+ h_file = ERR_PTR(-ENOENT); -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (au_test_nfsd() && !h_dentry) -+ goto out; -+ h_inode = h_dentry->d_inode; -+ if (au_test_nfsd() && !h_inode) -+ goto out; -+ spin_lock(&h_dentry->d_lock); -+ err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) -+ || !h_inode -+ /* || !dentry->d_inode->i_nlink */ -+ ; -+ spin_unlock(&h_dentry->d_lock); -+ if (unlikely(err)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, bindex); -+ err = au_br_test_oflag(flags, br); -+ h_file = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ /* drop flags for writing */ -+ if (au_test_ro(sb, bindex, dentry->d_inode)) { -+ if (force_wr && !(flags & O_WRONLY)) -+ force_wr = 0; -+ flags = au_file_roflags(flags); -+ if (force_wr) { -+ h_file = ERR_PTR(-EROFS); -+ flags = au_file_roflags(flags); -+ if (unlikely(vfsub_native_ro(h_inode) -+ || IS_APPEND(h_inode))) -+ goto out; -+ flags &= ~O_ACCMODE; -+ flags |= O_WRONLY; -+ } -+ } -+ flags &= ~O_CREAT; -+ atomic_inc(&br->br_count); -+ h_path.dentry = h_dentry; -+ h_path.mnt = au_br_mnt(br); -+ h_file = vfsub_dentry_open(&h_path, flags); -+ if (IS_ERR(h_file)) -+ goto out_br; -+ -+ if (flags & __FMODE_EXEC) { -+ err = deny_write_access(h_file); -+ if (unlikely(err)) { -+ fput(h_file); -+ h_file = ERR_PTR(err); -+ goto out_br; -+ } -+ } -+ fsnotify_open(h_file); -+ goto out; /* success */ -+ -+out_br: -+ atomic_dec(&br->br_count); -+out: -+ return h_file; -+} -+ -+static int au_cmoo(struct dentry *dentry) -+{ -+ int err, cmoo; -+ unsigned int udba; -+ struct path h_path; -+ struct au_pin pin; -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = -1, -+ .bsrc = -1, -+ .len = -1, -+ .pin = &pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ struct inode *delegated; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ struct au_fhsm *fhsm; -+ pid_t pid; -+ struct au_branch *br; -+ struct dentry *parent; -+ struct au_hinode *hdir; -+ -+ DiMustWriteLock(dentry); -+ IiMustWriteLock(dentry->d_inode); -+ -+ err = 0; -+ if (IS_ROOT(dentry)) -+ goto out; -+ cpg.bsrc = au_dbstart(dentry); -+ if (!cpg.bsrc) -+ goto out; -+ -+ sb = dentry->d_sb; -+ sbinfo = au_sbi(sb); -+ fhsm = &sbinfo->si_fhsm; -+ pid = au_fhsm_pid(fhsm); -+ if (pid -+ && (current->pid == pid -+ || current->real_parent->pid == pid)) -+ goto out; -+ -+ br = au_sbr(sb, cpg.bsrc); -+ cmoo = au_br_cmoo(br->br_perm); -+ if (!cmoo) -+ goto out; -+ if (!d_is_reg(dentry)) -+ cmoo &= AuBrAttr_COO_ALL; -+ if (!cmoo) -+ goto out; -+ -+ parent = dget_parent(dentry); -+ di_write_lock_parent(parent); -+ err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1); -+ cpg.bdst = err; -+ if (unlikely(err < 0)) { -+ err = 0; /* there is no upper writable branch */ -+ goto out_dgrade; -+ } -+ AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst); -+ -+ /* do not respect the coo attrib for the target branch */ -+ err = au_cpup_dirs(dentry, cpg.bdst); -+ if (unlikely(err)) -+ goto out_dgrade; -+ -+ di_downgrade_lock(parent, AuLock_IR); -+ udba = au_opt_udba(sb); -+ err = au_pin(&pin, dentry, cpg.bdst, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&pin); -+ if (unlikely(err)) -+ goto out_parent; -+ if (!(cmoo & AuBrWAttr_MOO)) -+ goto out_parent; /* success */ -+ -+ err = au_pin(&pin, dentry, cpg.bsrc, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ h_path.mnt = au_br_mnt(br); -+ h_path.dentry = au_h_dptr(dentry, cpg.bsrc); -+ hdir = au_hi(parent->d_inode, cpg.bsrc); -+ delegated = NULL; -+ err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1); -+ au_unpin(&pin); -+ /* todo: keep h_dentry or not? */ -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) { -+ pr_err("unlink %pd after coo failed (%d), ignored\n", -+ dentry, err); -+ err = 0; -+ } -+ goto out_parent; /* success */ -+ -+out_dgrade: -+ di_downgrade_lock(parent, AuLock_IR); -+out_parent: -+ di_read_unlock(parent, AuLock_IR); -+ dput(parent); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_do_open(struct file *file, struct au_do_open_args *args) -+{ -+ int err, no_lock = args->no_lock; -+ struct dentry *dentry; -+ struct au_finfo *finfo; -+ -+ if (!no_lock) -+ err = au_finfo_init(file, args->fidir); -+ else { -+ lockdep_off(); -+ err = au_finfo_init(file, args->fidir); -+ lockdep_on(); -+ } -+ if (unlikely(err)) -+ goto out; -+ -+ dentry = file->f_path.dentry; -+ AuDebugOn(IS_ERR_OR_NULL(dentry)); -+ if (!no_lock) { -+ di_write_lock_child(dentry); -+ err = au_cmoo(dentry); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (!err) -+ err = args->open(file, vfsub_file_flags(file), NULL); -+ di_read_unlock(dentry, AuLock_IR); -+ } else { -+ err = au_cmoo(dentry); -+ if (!err) -+ err = args->open(file, vfsub_file_flags(file), -+ args->h_file); -+ if (!err && au_fbstart(file) != au_dbstart(dentry)) -+ /* -+ * cmoo happens after h_file was opened. -+ * need to refresh file later. -+ */ -+ atomic_dec(&au_fi(file)->fi_generation); -+ } -+ -+ finfo = au_fi(file); -+ if (!err) { -+ finfo->fi_file = file; -+ au_sphl_add(&finfo->fi_hlist, -+ &au_sbi(file->f_path.dentry->d_sb)->si_files); -+ } -+ if (!no_lock) -+ fi_write_unlock(file); -+ else { -+ lockdep_off(); -+ fi_write_unlock(file); -+ lockdep_on(); -+ } -+ if (unlikely(err)) { -+ finfo->fi_hdir = NULL; -+ au_finfo_fin(file); -+ } -+ -+out: -+ return err; -+} -+ -+int au_reopen_nondir(struct file *file) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ struct dentry *dentry; -+ struct file *h_file, *h_file_tmp; -+ -+ dentry = file->f_path.dentry; -+ bstart = au_dbstart(dentry); -+ h_file_tmp = NULL; -+ if (au_fbstart(file) == bstart) { -+ h_file = au_hf_top(file); -+ if (file->f_mode == h_file->f_mode) -+ return 0; /* success */ -+ h_file_tmp = h_file; -+ get_file(h_file_tmp); -+ au_set_h_fptr(file, bstart, NULL); -+ } -+ AuDebugOn(au_fi(file)->fi_hdir); -+ /* -+ * it can happen -+ * file exists on both of rw and ro -+ * open --> dbstart and fbstart are both 0 -+ * prepend a branch as rw, "rw" become ro -+ * remove rw/file -+ * delete the top branch, "rw" becomes rw again -+ * --> dbstart is 1, fbstart is still 0 -+ * write --> fbstart is 0 but dbstart is 1 -+ */ -+ /* AuDebugOn(au_fbstart(file) < bstart); */ -+ -+ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, -+ file, /*force_wr*/0); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) { -+ if (h_file_tmp) { -+ atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); -+ au_set_h_fptr(file, bstart, h_file_tmp); -+ h_file_tmp = NULL; -+ } -+ goto out; /* todo: close all? */ -+ } -+ -+ err = 0; -+ au_set_fbstart(file, bstart); -+ au_set_h_fptr(file, bstart, h_file); -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ -+out: -+ if (h_file_tmp) -+ fput(h_file_tmp); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, -+ struct dentry *hi_wh) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ struct au_dinfo *dinfo; -+ struct dentry *h_dentry; -+ struct au_hdentry *hdp; -+ -+ dinfo = au_di(file->f_path.dentry); -+ AuRwMustWriteLock(&dinfo->di_rwsem); -+ -+ bstart = dinfo->di_bstart; -+ dinfo->di_bstart = btgt; -+ hdp = dinfo->di_hdentry; -+ h_dentry = hdp[0 + btgt].hd_dentry; -+ hdp[0 + btgt].hd_dentry = hi_wh; -+ err = au_reopen_nondir(file); -+ hdp[0 + btgt].hd_dentry = h_dentry; -+ dinfo->di_bstart = bstart; -+ -+ return err; -+} -+ -+static int au_ready_to_write_wh(struct file *file, loff_t len, -+ aufs_bindex_t bcpup, struct au_pin *pin) -+{ -+ int err; -+ struct inode *inode, *h_inode; -+ struct dentry *h_dentry, *hi_wh; -+ struct au_cp_generic cpg = { -+ .dentry = file->f_path.dentry, -+ .bdst = bcpup, -+ .bsrc = -1, -+ .len = len, -+ .pin = pin -+ }; -+ -+ au_update_dbstart(cpg.dentry); -+ inode = cpg.dentry->d_inode; -+ h_inode = NULL; -+ if (au_dbstart(cpg.dentry) <= bcpup -+ && au_dbend(cpg.dentry) >= bcpup) { -+ h_dentry = au_h_dptr(cpg.dentry, bcpup); -+ if (h_dentry) -+ h_inode = h_dentry->d_inode; -+ } -+ hi_wh = au_hi_wh(inode, bcpup); -+ if (!hi_wh && !h_inode) -+ err = au_sio_cpup_wh(&cpg, file); -+ else -+ /* already copied-up after unlink */ -+ err = au_reopen_wh(file, bcpup, hi_wh); -+ -+ if (!err -+ && (inode->i_nlink > 1 -+ || (inode->i_state & I_LINKABLE)) -+ && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK)) -+ au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); -+ -+ return err; -+} -+ -+/* -+ * prepare the @file for writing. -+ */ -+int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) -+{ -+ int err; -+ aufs_bindex_t dbstart; -+ struct dentry *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ struct file *h_file; -+ struct au_cp_generic cpg = { -+ .dentry = file->f_path.dentry, -+ .bdst = -1, -+ .bsrc = -1, -+ .len = len, -+ .pin = pin, -+ .flags = AuCpup_DTIME -+ }; -+ -+ sb = cpg.dentry->d_sb; -+ inode = cpg.dentry->d_inode; -+ cpg.bsrc = au_fbstart(file); -+ err = au_test_ro(sb, cpg.bsrc, inode); -+ if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { -+ err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, -+ /*flags*/0); -+ goto out; -+ } -+ -+ /* need to cpup or reopen */ -+ parent = dget_parent(cpg.dentry); -+ di_write_lock_parent(parent); -+ err = AuWbrCopyup(au_sbi(sb), cpg.dentry); -+ cpg.bdst = err; -+ if (unlikely(err < 0)) -+ goto out_dgrade; -+ err = 0; -+ -+ if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { -+ err = au_cpup_dirs(cpg.dentry, cpg.bdst); -+ if (unlikely(err)) -+ goto out_dgrade; -+ } -+ -+ err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_dgrade; -+ -+ dbstart = au_dbstart(cpg.dentry); -+ if (dbstart <= cpg.bdst) -+ cpg.bsrc = cpg.bdst; -+ -+ if (dbstart <= cpg.bdst /* just reopen */ -+ || !d_unhashed(cpg.dentry) /* copyup and reopen */ -+ ) { -+ h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ di_downgrade_lock(parent, AuLock_IR); -+ if (dbstart > cpg.bdst) -+ err = au_sio_cpup_simple(&cpg); -+ if (!err) -+ err = au_reopen_nondir(file); -+ au_h_open_post(cpg.dentry, cpg.bsrc, h_file); -+ } -+ } else { /* copyup as wh and reopen */ -+ /* -+ * since writable hfsplus branch is not supported, -+ * h_open_pre/post() are unnecessary. -+ */ -+ err = au_ready_to_write_wh(file, len, cpg.bdst, pin); -+ di_downgrade_lock(parent, AuLock_IR); -+ } -+ -+ if (!err) { -+ au_pin_set_parent_lflag(pin, /*lflag*/0); -+ goto out_dput; /* success */ -+ } -+ au_unpin(pin); -+ goto out_unlock; -+ -+out_dgrade: -+ di_downgrade_lock(parent, AuLock_IR); -+out_unlock: -+ di_read_unlock(parent, AuLock_IR); -+out_dput: -+ dput(parent); -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_do_flush(struct file *file, fl_owner_t id, -+ int (*flush)(struct file *file, fl_owner_t id)) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *inode; -+ -+ inode = file_inode(file); -+ sb = inode->i_sb; -+ si_noflush_read_lock(sb); -+ fi_read_lock(file); -+ ii_read_lock_child(inode); -+ -+ err = flush(file, id); -+ au_cpup_attr_timesizes(inode); -+ -+ ii_read_unlock(inode); -+ fi_read_unlock(file); -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_file_refresh_by_inode(struct file *file, int *need_reopen) -+{ -+ int err; -+ struct au_pin pin; -+ struct au_finfo *finfo; -+ struct dentry *parent, *hi_wh; -+ struct inode *inode; -+ struct super_block *sb; -+ struct au_cp_generic cpg = { -+ .dentry = file->f_path.dentry, -+ .bdst = -1, -+ .bsrc = -1, -+ .len = -1, -+ .pin = &pin, -+ .flags = AuCpup_DTIME -+ }; -+ -+ FiMustWriteLock(file); -+ -+ err = 0; -+ finfo = au_fi(file); -+ sb = cpg.dentry->d_sb; -+ inode = cpg.dentry->d_inode; -+ cpg.bdst = au_ibstart(inode); -+ if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) -+ goto out; -+ -+ parent = dget_parent(cpg.dentry); -+ if (au_test_ro(sb, cpg.bdst, inode)) { -+ di_read_lock_parent(parent, !AuLock_IR); -+ err = AuWbrCopyup(au_sbi(sb), cpg.dentry); -+ cpg.bdst = err; -+ di_read_unlock(parent, !AuLock_IR); -+ if (unlikely(err < 0)) -+ goto out_parent; -+ err = 0; -+ } -+ -+ di_read_lock_parent(parent, AuLock_IR); -+ hi_wh = au_hi_wh(inode, cpg.bdst); -+ if (!S_ISDIR(inode->i_mode) -+ && au_opt_test(au_mntflags(sb), PLINK) -+ && au_plink_test(inode) -+ && !d_unhashed(cpg.dentry) -+ && cpg.bdst < au_dbstart(cpg.dentry)) { -+ err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ /* always superio. */ -+ err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (!err) { -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&pin); -+ } -+ } else if (hi_wh) { -+ /* already copied-up after unlink */ -+ err = au_reopen_wh(file, cpg.bdst, hi_wh); -+ *need_reopen = 0; -+ } -+ -+out_unlock: -+ di_read_unlock(parent, AuLock_IR); -+out_parent: -+ dput(parent); -+out: -+ return err; -+} -+ -+static void au_do_refresh_dir(struct file *file) -+{ -+ aufs_bindex_t bindex, bend, new_bindex, brid; -+ struct au_hfile *p, tmp, *q; -+ struct au_finfo *finfo; -+ struct super_block *sb; -+ struct au_fidir *fidir; -+ -+ FiMustWriteLock(file); -+ -+ sb = file->f_path.dentry->d_sb; -+ finfo = au_fi(file); -+ fidir = finfo->fi_hdir; -+ AuDebugOn(!fidir); -+ p = fidir->fd_hfile + finfo->fi_btop; -+ brid = p->hf_br->br_id; -+ bend = fidir->fd_bbot; -+ for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { -+ if (!p->hf_file) -+ continue; -+ -+ new_bindex = au_br_index(sb, p->hf_br->br_id); -+ if (new_bindex == bindex) -+ continue; -+ if (new_bindex < 0) { -+ au_set_h_fptr(file, bindex, NULL); -+ continue; -+ } -+ -+ /* swap two lower inode, and loop again */ -+ q = fidir->fd_hfile + new_bindex; -+ tmp = *q; -+ *q = *p; -+ *p = tmp; -+ if (tmp.hf_file) { -+ bindex--; -+ p--; -+ } -+ } -+ -+ p = fidir->fd_hfile; -+ if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) { -+ bend = au_sbend(sb); -+ for (finfo->fi_btop = 0; finfo->fi_btop <= bend; -+ finfo->fi_btop++, p++) -+ if (p->hf_file) { -+ if (file_inode(p->hf_file)) -+ break; -+ au_hfput(p, file); -+ } -+ } else { -+ bend = au_br_index(sb, brid); -+ for (finfo->fi_btop = 0; finfo->fi_btop < bend; -+ finfo->fi_btop++, p++) -+ if (p->hf_file) -+ au_hfput(p, file); -+ bend = au_sbend(sb); -+ } -+ -+ p = fidir->fd_hfile + bend; -+ for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; -+ fidir->fd_bbot--, p--) -+ if (p->hf_file) { -+ if (file_inode(p->hf_file)) -+ break; -+ au_hfput(p, file); -+ } -+ AuDebugOn(fidir->fd_bbot < finfo->fi_btop); -+} -+ -+/* -+ * after branch manipulating, refresh the file. -+ */ -+static int refresh_file(struct file *file, int (*reopen)(struct file *file)) -+{ -+ int err, need_reopen; -+ aufs_bindex_t bend, bindex; -+ struct dentry *dentry; -+ struct au_finfo *finfo; -+ struct au_hfile *hfile; -+ -+ dentry = file->f_path.dentry; -+ finfo = au_fi(file); -+ if (!finfo->fi_hdir) { -+ hfile = &finfo->fi_htop; -+ AuDebugOn(!hfile->hf_file); -+ bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); -+ AuDebugOn(bindex < 0); -+ if (bindex != finfo->fi_btop) -+ au_set_fbstart(file, bindex); -+ } else { -+ err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); -+ if (unlikely(err)) -+ goto out; -+ au_do_refresh_dir(file); -+ } -+ -+ err = 0; -+ need_reopen = 1; -+ if (!au_test_mmapped(file)) -+ err = au_file_refresh_by_inode(file, &need_reopen); -+ if (!err && need_reopen && !d_unlinked(dentry)) -+ err = reopen(file); -+ if (!err) { -+ au_update_figen(file); -+ goto out; /* success */ -+ } -+ -+ /* error, close all lower files */ -+ if (finfo->fi_hdir) { -+ bend = au_fbend_dir(file); -+ for (bindex = au_fbstart(file); bindex <= bend; bindex++) -+ au_set_h_fptr(file, bindex, NULL); -+ } -+ -+out: -+ return err; -+} -+ -+/* common function to regular file and dir */ -+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), -+ int wlock) -+{ -+ int err; -+ unsigned int sigen, figen; -+ aufs_bindex_t bstart; -+ unsigned char pseudo_link; -+ struct dentry *dentry; -+ struct inode *inode; -+ -+ err = 0; -+ dentry = file->f_path.dentry; -+ inode = dentry->d_inode; -+ sigen = au_sigen(dentry->d_sb); -+ fi_write_lock(file); -+ figen = au_figen(file); -+ di_write_lock_child(dentry); -+ bstart = au_dbstart(dentry); -+ pseudo_link = (bstart != au_ibstart(inode)); -+ if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { -+ if (!wlock) { -+ di_downgrade_lock(dentry, AuLock_IR); -+ fi_downgrade_lock(file); -+ } -+ goto out; /* success */ -+ } -+ -+ AuDbg("sigen %d, figen %d\n", sigen, figen); -+ if (au_digen_test(dentry, sigen)) { -+ err = au_reval_dpath(dentry, sigen); -+ AuDebugOn(!err && au_digen_test(dentry, sigen)); -+ } -+ -+ if (!err) -+ err = refresh_file(file, reopen); -+ if (!err) { -+ if (!wlock) { -+ di_downgrade_lock(dentry, AuLock_IR); -+ fi_downgrade_lock(file); -+ } -+ } else { -+ di_write_unlock(dentry); -+ fi_write_unlock(file); -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* cf. aufs_nopage() */ -+/* for madvise(2) */ -+static int aufs_readpage(struct file *file __maybe_unused, struct page *page) -+{ -+ unlock_page(page); -+ return 0; -+} -+ -+/* it will never be called, but necessary to support O_DIRECT */ -+static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb, -+ struct iov_iter *iter, loff_t offset) -+{ BUG(); return 0; } -+ -+/* they will never be called. */ -+#ifdef CONFIG_AUFS_DEBUG -+static int aufs_write_begin(struct file *file, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned flags, -+ struct page **pagep, void **fsdata) -+{ AuUnsupport(); return 0; } -+static int aufs_write_end(struct file *file, struct address_space *mapping, -+ loff_t pos, unsigned len, unsigned copied, -+ struct page *page, void *fsdata) -+{ AuUnsupport(); return 0; } -+static int aufs_writepage(struct page *page, struct writeback_control *wbc) -+{ AuUnsupport(); return 0; } -+ -+static int aufs_set_page_dirty(struct page *page) -+{ AuUnsupport(); return 0; } -+static void aufs_invalidatepage(struct page *page, unsigned int offset, -+ unsigned int length) -+{ AuUnsupport(); } -+static int aufs_releasepage(struct page *page, gfp_t gfp) -+{ AuUnsupport(); return 0; } -+static int aufs_migratepage(struct address_space *mapping, struct page *newpage, -+ struct page *page, enum migrate_mode mode) -+{ AuUnsupport(); return 0; } -+static int aufs_launder_page(struct page *page) -+{ AuUnsupport(); return 0; } -+static int aufs_is_partially_uptodate(struct page *page, -+ unsigned long from, -+ unsigned long count) -+{ AuUnsupport(); return 0; } -+static void aufs_is_dirty_writeback(struct page *page, bool *dirty, -+ bool *writeback) -+{ AuUnsupport(); } -+static int aufs_error_remove_page(struct address_space *mapping, -+ struct page *page) -+{ AuUnsupport(); return 0; } -+static int aufs_swap_activate(struct swap_info_struct *sis, struct file *file, -+ sector_t *span) -+{ AuUnsupport(); return 0; } -+static void aufs_swap_deactivate(struct file *file) -+{ AuUnsupport(); } -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+const struct address_space_operations aufs_aop = { -+ .readpage = aufs_readpage, -+ .direct_IO = aufs_direct_IO, -+#ifdef CONFIG_AUFS_DEBUG -+ .writepage = aufs_writepage, -+ /* no writepages, because of writepage */ -+ .set_page_dirty = aufs_set_page_dirty, -+ /* no readpages, because of readpage */ -+ .write_begin = aufs_write_begin, -+ .write_end = aufs_write_end, -+ /* no bmap, no block device */ -+ .invalidatepage = aufs_invalidatepage, -+ .releasepage = aufs_releasepage, -+ .migratepage = aufs_migratepage, -+ .launder_page = aufs_launder_page, -+ .is_partially_uptodate = aufs_is_partially_uptodate, -+ .is_dirty_writeback = aufs_is_dirty_writeback, -+ .error_remove_page = aufs_error_remove_page, -+ .swap_activate = aufs_swap_activate, -+ .swap_deactivate = aufs_swap_deactivate -+#endif /* CONFIG_AUFS_DEBUG */ -+}; -diff -Nur linux-4.0.4.orig/fs/aufs/file.h linux-4.0.4/fs/aufs/file.h ---- linux-4.0.4.orig/fs/aufs/file.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/file.h 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,277 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * file operations -+ */ -+ -+#ifndef __AUFS_FILE_H__ -+#define __AUFS_FILE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include "rwsem.h" -+ -+struct au_branch; -+struct au_hfile { -+ struct file *hf_file; -+ struct au_branch *hf_br; -+}; -+ -+struct au_vdir; -+struct au_fidir { -+ aufs_bindex_t fd_bbot; -+ aufs_bindex_t fd_nent; -+ struct au_vdir *fd_vdir_cache; -+ struct au_hfile fd_hfile[]; -+}; -+ -+static inline int au_fidir_sz(int nent) -+{ -+ AuDebugOn(nent < 0); -+ return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent; -+} -+ -+struct au_finfo { -+ atomic_t fi_generation; -+ -+ struct au_rwsem fi_rwsem; -+ aufs_bindex_t fi_btop; -+ -+ /* do not union them */ -+ struct { /* for non-dir */ -+ struct au_hfile fi_htop; -+ atomic_t fi_mmapped; -+ }; -+ struct au_fidir *fi_hdir; /* for dir only */ -+ -+ struct hlist_node fi_hlist; -+ struct file *fi_file; /* very ugly */ -+} ____cacheline_aligned_in_smp; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* file.c */ -+extern const struct address_space_operations aufs_aop; -+unsigned int au_file_roflags(unsigned int flags); -+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, -+ struct file *file, int force_wr); -+struct au_do_open_args { -+ int no_lock; -+ int (*open)(struct file *file, int flags, -+ struct file *h_file); -+ struct au_fidir *fidir; -+ struct file *h_file; -+}; -+int au_do_open(struct file *file, struct au_do_open_args *args); -+int au_reopen_nondir(struct file *file); -+struct au_pin; -+int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin); -+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), -+ int wlock); -+int au_do_flush(struct file *file, fl_owner_t id, -+ int (*flush)(struct file *file, fl_owner_t id)); -+ -+/* poll.c */ -+#ifdef CONFIG_AUFS_POLL -+unsigned int aufs_poll(struct file *file, poll_table *wait); -+#endif -+ -+#ifdef CONFIG_AUFS_BR_HFSPLUS -+/* hfsplus.c */ -+struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, -+ int force_wr); -+void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, -+ struct file *h_file); -+#else -+AuStub(struct file *, au_h_open_pre, return NULL, struct dentry *dentry, -+ aufs_bindex_t bindex, int force_wr) -+AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex, -+ struct file *h_file); -+#endif -+ -+/* f_op.c */ -+extern const struct file_operations aufs_file_fop; -+int au_do_open_nondir(struct file *file, int flags, struct file *h_file); -+int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file); -+ -+/* finfo.c */ -+void au_hfput(struct au_hfile *hf, struct file *file); -+void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, -+ struct file *h_file); -+ -+void au_update_figen(struct file *file); -+struct au_fidir *au_fidir_alloc(struct super_block *sb); -+int au_fidir_realloc(struct au_finfo *finfo, int nbr); -+ -+void au_fi_init_once(void *_fi); -+void au_finfo_fin(struct file *file); -+int au_finfo_init(struct file *file, struct au_fidir *fidir); -+ -+/* ioctl.c */ -+long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, -+ unsigned long arg); -+long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, -+ unsigned long arg); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_finfo *au_fi(struct file *file) -+{ -+ return file->private_data; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * fi_read_lock, fi_write_lock, -+ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem); -+ -+#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) -+#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem) -+#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: hard/soft set? */ -+static inline aufs_bindex_t au_fbstart(struct file *file) -+{ -+ FiMustAnyLock(file); -+ return au_fi(file)->fi_btop; -+} -+ -+static inline aufs_bindex_t au_fbend_dir(struct file *file) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_hdir->fd_bbot; -+} -+ -+static inline struct au_vdir *au_fvdir_cache(struct file *file) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_hdir->fd_vdir_cache; -+} -+ -+static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) -+{ -+ FiMustWriteLock(file); -+ au_fi(file)->fi_btop = bindex; -+} -+ -+static inline void au_set_fbend_dir(struct file *file, aufs_bindex_t bindex) -+{ -+ FiMustWriteLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ au_fi(file)->fi_hdir->fd_bbot = bindex; -+} -+ -+static inline void au_set_fvdir_cache(struct file *file, -+ struct au_vdir *vdir_cache) -+{ -+ FiMustWriteLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache; -+} -+ -+static inline struct file *au_hf_top(struct file *file) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_htop.hf_file; -+} -+ -+static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex) -+{ -+ FiMustAnyLock(file); -+ AuDebugOn(!au_fi(file)->fi_hdir); -+ return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file; -+} -+ -+/* todo: memory barrier? */ -+static inline unsigned int au_figen(struct file *f) -+{ -+ return atomic_read(&au_fi(f)->fi_generation); -+} -+ -+static inline void au_set_mmapped(struct file *f) -+{ -+ if (atomic_inc_return(&au_fi(f)->fi_mmapped)) -+ return; -+ pr_warn("fi_mmapped wrapped around\n"); -+ while (!atomic_inc_return(&au_fi(f)->fi_mmapped)) -+ ; -+} -+ -+static inline void au_unset_mmapped(struct file *f) -+{ -+ atomic_dec(&au_fi(f)->fi_mmapped); -+} -+ -+static inline int au_test_mmapped(struct file *f) -+{ -+ return atomic_read(&au_fi(f)->fi_mmapped); -+} -+ -+/* customize vma->vm_file */ -+ -+static inline void au_do_vm_file_reset(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ struct file *f; -+ -+ f = vma->vm_file; -+ get_file(file); -+ vma->vm_file = file; -+ fput(f); -+} -+ -+#ifdef CONFIG_MMU -+#define AuDbgVmRegion(file, vma) do {} while (0) -+ -+static inline void au_vm_file_reset(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ au_do_vm_file_reset(vma, file); -+} -+#else -+#define AuDbgVmRegion(file, vma) \ -+ AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file)) -+ -+static inline void au_vm_file_reset(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ struct file *f; -+ -+ au_do_vm_file_reset(vma, file); -+ f = vma->vm_region->vm_file; -+ get_file(file); -+ vma->vm_region->vm_file = file; -+ fput(f); -+} -+#endif /* CONFIG_MMU */ -+ -+/* handle vma->vm_prfile */ -+static inline void au_vm_prfile_set(struct vm_area_struct *vma, -+ struct file *file) -+{ -+ get_file(file); -+ vma->vm_prfile = file; -+#ifndef CONFIG_MMU -+ get_file(file); -+ vma->vm_region->vm_prfile = file; -+#endif -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_FILE_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/finfo.c linux-4.0.4/fs/aufs/finfo.c ---- linux-4.0.4.orig/fs/aufs/finfo.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/finfo.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,144 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * file private data -+ */ -+ -+#include "aufs.h" -+ -+void au_hfput(struct au_hfile *hf, struct file *file) -+{ -+ /* todo: direct access f_flags */ -+ if (vfsub_file_flags(file) & __FMODE_EXEC) -+ allow_write_access(hf->hf_file); -+ fput(hf->hf_file); -+ hf->hf_file = NULL; -+ atomic_dec(&hf->hf_br->br_count); -+ hf->hf_br = NULL; -+} -+ -+void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) -+{ -+ struct au_finfo *finfo = au_fi(file); -+ struct au_hfile *hf; -+ struct au_fidir *fidir; -+ -+ fidir = finfo->fi_hdir; -+ if (!fidir) { -+ AuDebugOn(finfo->fi_btop != bindex); -+ hf = &finfo->fi_htop; -+ } else -+ hf = fidir->fd_hfile + bindex; -+ -+ if (hf && hf->hf_file) -+ au_hfput(hf, file); -+ if (val) { -+ FiMustWriteLock(file); -+ AuDebugOn(IS_ERR_OR_NULL(file->f_path.dentry)); -+ hf->hf_file = val; -+ hf->hf_br = au_sbr(file->f_path.dentry->d_sb, bindex); -+ } -+} -+ -+void au_update_figen(struct file *file) -+{ -+ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_path.dentry)); -+ /* smp_mb(); */ /* atomic_set */ -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_fidir *au_fidir_alloc(struct super_block *sb) -+{ -+ struct au_fidir *fidir; -+ int nbr; -+ -+ nbr = au_sbend(sb) + 1; -+ if (nbr < 2) -+ nbr = 2; /* initial allocate for 2 branches */ -+ fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); -+ if (fidir) { -+ fidir->fd_bbot = -1; -+ fidir->fd_nent = nbr; -+ fidir->fd_vdir_cache = NULL; -+ } -+ -+ return fidir; -+} -+ -+int au_fidir_realloc(struct au_finfo *finfo, int nbr) -+{ -+ int err; -+ struct au_fidir *fidir, *p; -+ -+ AuRwMustWriteLock(&finfo->fi_rwsem); -+ fidir = finfo->fi_hdir; -+ AuDebugOn(!fidir); -+ -+ err = -ENOMEM; -+ p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr), -+ GFP_NOFS); -+ if (p) { -+ p->fd_nent = nbr; -+ finfo->fi_hdir = p; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_finfo_fin(struct file *file) -+{ -+ struct au_finfo *finfo; -+ -+ au_nfiles_dec(file->f_path.dentry->d_sb); -+ -+ finfo = au_fi(file); -+ AuDebugOn(finfo->fi_hdir); -+ AuRwDestroy(&finfo->fi_rwsem); -+ au_cache_free_finfo(finfo); -+} -+ -+void au_fi_init_once(void *_finfo) -+{ -+ struct au_finfo *finfo = _finfo; -+ static struct lock_class_key aufs_fi; -+ -+ au_rw_init(&finfo->fi_rwsem); -+ au_rw_class(&finfo->fi_rwsem, &aufs_fi); -+} -+ -+int au_finfo_init(struct file *file, struct au_fidir *fidir) -+{ -+ int err; -+ struct au_finfo *finfo; -+ struct dentry *dentry; -+ -+ err = -ENOMEM; -+ dentry = file->f_path.dentry; -+ finfo = au_cache_alloc_finfo(); -+ if (unlikely(!finfo)) -+ goto out; -+ -+ err = 0; -+ au_nfiles_inc(dentry->d_sb); -+ /* verbose coding for lock class name */ -+ if (!fidir) -+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO); -+ else -+ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO); -+ au_rw_write_lock(&finfo->fi_rwsem); -+ finfo->fi_btop = -1; -+ finfo->fi_hdir = fidir; -+ atomic_set(&finfo->fi_generation, au_digen(dentry)); -+ /* smp_mb(); */ /* atomic_set */ -+ -+ file->private_data = finfo; -+ -+out: -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/f_op.c linux-4.0.4/fs/aufs/f_op.c ---- linux-4.0.4.orig/fs/aufs/f_op.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/f_op.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,807 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * file and vm operations -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+int au_do_open_nondir(struct file *file, int flags, struct file *h_file) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct dentry *dentry; -+ struct au_finfo *finfo; -+ struct inode *h_inode; -+ -+ FiMustWriteLock(file); -+ -+ err = 0; -+ dentry = file->f_path.dentry; -+ AuDebugOn(IS_ERR_OR_NULL(dentry)); -+ finfo = au_fi(file); -+ memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); -+ atomic_set(&finfo->fi_mmapped, 0); -+ bindex = au_dbstart(dentry); -+ if (!h_file) -+ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); -+ else -+ get_file(h_file); -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ if ((flags & __O_TMPFILE) -+ && !(flags & O_EXCL)) { -+ h_inode = file_inode(h_file); -+ spin_lock(&h_inode->i_lock); -+ h_inode->i_state |= I_LINKABLE; -+ spin_unlock(&h_inode->i_lock); -+ } -+ au_set_fbstart(file, bindex); -+ au_set_h_fptr(file, bindex, h_file); -+ au_update_figen(file); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ } -+ -+ return err; -+} -+ -+static int aufs_open_nondir(struct inode *inode __maybe_unused, -+ struct file *file) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_do_open_args args = { -+ .open = au_do_open_nondir -+ }; -+ -+ AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n", -+ file, vfsub_file_flags(file), file->f_mode); -+ -+ sb = file->f_path.dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ err = au_do_open(file, &args); -+ si_read_unlock(sb); -+ return err; -+} -+ -+int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) -+{ -+ struct au_finfo *finfo; -+ aufs_bindex_t bindex; -+ -+ finfo = au_fi(file); -+ au_sphl_del(&finfo->fi_hlist, -+ &au_sbi(file->f_path.dentry->d_sb)->si_files); -+ bindex = finfo->fi_btop; -+ if (bindex >= 0) -+ au_set_h_fptr(file, bindex, NULL); -+ -+ au_finfo_fin(file); -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_do_flush_nondir(struct file *file, fl_owner_t id) -+{ -+ int err; -+ struct file *h_file; -+ -+ err = 0; -+ h_file = au_hf_top(file); -+ if (h_file) -+ err = vfsub_flush(h_file, id); -+ return err; -+} -+ -+static int aufs_flush_nondir(struct file *file, fl_owner_t id) -+{ -+ return au_do_flush(file, id, au_do_flush_nondir); -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * read and write functions acquire [fdi]_rwsem once, but release before -+ * mmap_sem. This is because to stop a race condition between mmap(2). -+ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping -+ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in -+ * read functions after [fdi]_rwsem are released, but it should be harmless. -+ */ -+ -+static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ struct dentry *dentry; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ dentry = file->f_path.dentry; -+ sb = dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); -+ if (unlikely(err)) -+ goto out; -+ -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_read_unlock(file); -+ -+ /* filedata may be obsoleted by concurrent copyup, but no problem */ -+ err = vfsub_read_u(h_file, buf, count, ppos); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(dentry->d_inode, file_inode(h_file)); -+ fput(h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* -+ * todo: very ugly -+ * it locks both of i_mutex and si_rwsem for read in safe. -+ * if the plink maintenance mode continues forever (that is the problem), -+ * may loop forever. -+ */ -+static void au_mtx_and_read_lock(struct inode *inode) -+{ -+ int err; -+ struct super_block *sb = inode->i_sb; -+ -+ while (1) { -+ mutex_lock(&inode->i_mutex); -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (!err) -+ break; -+ mutex_unlock(&inode->i_mutex); -+ si_read_lock(sb, AuLock_NOPLMW); -+ si_read_unlock(sb); -+ } -+} -+ -+static ssize_t aufs_write(struct file *file, const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ ssize_t err; -+ blkcnt_t blks; -+ aufs_bindex_t bstart; -+ struct au_pin pin; -+ struct dentry *dentry; -+ struct inode *inode, *h_inode; -+ struct super_block *sb; -+ struct file *h_file; -+ char __user *buf = (char __user *)ubuf; -+ -+ dentry = file->f_path.dentry; -+ sb = dentry->d_sb; -+ inode = dentry->d_inode; -+ au_mtx_and_read_lock(inode); -+ -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_ready_to_write(file, -1, &pin); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (unlikely(err)) { -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ goto out; -+ } -+ -+ bstart = au_fbstart(file); -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ h_inode = file_inode(h_file); -+ blks = h_inode->i_blocks; -+ au_unpin(&pin); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ -+ err = vfsub_write_u(h_file, buf, count, ppos); -+ ii_write_lock_child(inode); -+ au_cpup_attr_timesizes(inode); -+ inode->i_mode = file_inode(h_file)->i_mode; -+ AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); -+ if (err > 0) -+ au_fhsm_wrote(sb, bstart, /*force*/h_inode->i_blocks > blks); -+ ii_write_unlock(inode); -+ fput(h_file); -+ -+out: -+ si_read_unlock(sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio, -+ struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct file *file; -+ ssize_t (*iter)(struct kiocb *, struct iov_iter *); -+ ssize_t (*aio)(struct kiocb *, const struct iovec *, unsigned long, -+ loff_t); -+ -+ err = security_file_permission(h_file, rw); -+ if (unlikely(err)) -+ goto out; -+ -+ err = -ENOSYS; -+ iter = NULL; -+ aio = NULL; -+ if (rw == MAY_READ) { -+ iter = h_file->f_op->read_iter; -+ aio = h_file->f_op->aio_read; -+ } else if (rw == MAY_WRITE) { -+ iter = h_file->f_op->write_iter; -+ aio = h_file->f_op->aio_write; -+ } -+ -+ file = kio->ki_filp; -+ kio->ki_filp = h_file; -+ if (iter) { -+ lockdep_off(); -+ err = iter(kio, iov_iter); -+ lockdep_on(); -+ } else if (aio) { -+ lockdep_off(); -+ err = aio(kio, iov_iter->iov, iov_iter->nr_segs, kio->ki_pos); -+ lockdep_on(); -+ } else -+ /* currently there is no such fs */ -+ WARN_ON_ONCE(1); -+ kio->ki_filp = file; -+ -+out: -+ return err; -+} -+ -+static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ struct file *file, *h_file; -+ struct dentry *dentry; -+ struct super_block *sb; -+ -+ file = kio->ki_filp; -+ dentry = file->f_path.dentry; -+ sb = dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); -+ if (unlikely(err)) -+ goto out; -+ -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_read_unlock(file); -+ -+ err = au_do_iter(h_file, MAY_READ, kio, iov_iter); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(dentry->d_inode, file_inode(h_file)); -+ fput(h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter) -+{ -+ ssize_t err; -+ blkcnt_t blks; -+ aufs_bindex_t bstart; -+ struct au_pin pin; -+ struct dentry *dentry; -+ struct inode *inode, *h_inode; -+ struct file *file, *h_file; -+ struct super_block *sb; -+ -+ file = kio->ki_filp; -+ dentry = file->f_path.dentry; -+ sb = dentry->d_sb; -+ inode = dentry->d_inode; -+ au_mtx_and_read_lock(inode); -+ -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_ready_to_write(file, -1, &pin); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (unlikely(err)) { -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ goto out; -+ } -+ -+ bstart = au_fbstart(file); -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ h_inode = file_inode(h_file); -+ blks = h_inode->i_blocks; -+ au_unpin(&pin); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ -+ err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter); -+ ii_write_lock_child(inode); -+ au_cpup_attr_timesizes(inode); -+ inode->i_mode = file_inode(h_file)->i_mode; -+ AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); -+ if (err > 0) -+ au_fhsm_wrote(sb, bstart, /*force*/h_inode->i_blocks > blks); -+ ii_write_unlock(inode); -+ fput(h_file); -+ -+out: -+ si_read_unlock(sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags) -+{ -+ ssize_t err; -+ struct file *h_file; -+ struct dentry *dentry; -+ struct super_block *sb; -+ -+ dentry = file->f_path.dentry; -+ sb = dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); -+ if (unlikely(err)) -+ goto out; -+ -+ err = -EINVAL; -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ if (au_test_loopback_kthread()) { -+ au_warn_loopback(h_file->f_path.dentry->d_sb); -+ if (file->f_mapping != h_file->f_mapping) { -+ file->f_mapping = h_file->f_mapping; -+ smp_mb(); /* unnecessary? */ -+ } -+ } -+ di_read_unlock(dentry, AuLock_IR); -+ fi_read_unlock(file); -+ -+ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); -+ /* todo: necessasry? */ -+ /* file->f_ra = h_file->f_ra; */ -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(dentry->d_inode, file_inode(h_file)); -+ fput(h_file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+static ssize_t -+aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, -+ size_t len, unsigned int flags) -+{ -+ ssize_t err; -+ blkcnt_t blks; -+ aufs_bindex_t bstart; -+ struct au_pin pin; -+ struct dentry *dentry; -+ struct inode *inode, *h_inode; -+ struct super_block *sb; -+ struct file *h_file; -+ -+ dentry = file->f_path.dentry; -+ sb = dentry->d_sb; -+ inode = dentry->d_inode; -+ au_mtx_and_read_lock(inode); -+ -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_ready_to_write(file, -1, &pin); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (unlikely(err)) { -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ goto out; -+ } -+ -+ bstart = au_fbstart(file); -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ h_inode = file_inode(h_file); -+ blks = h_inode->i_blocks; -+ au_unpin(&pin); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ -+ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); -+ ii_write_lock_child(inode); -+ au_cpup_attr_timesizes(inode); -+ inode->i_mode = file_inode(h_file)->i_mode; -+ AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); -+ if (err > 0) -+ au_fhsm_wrote(sb, bstart, /*force*/h_inode->i_blocks > blks); -+ ii_write_unlock(inode); -+ fput(h_file); -+ -+out: -+ si_read_unlock(sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+static long aufs_fallocate(struct file *file, int mode, loff_t offset, -+ loff_t len) -+{ -+ long err; -+ struct au_pin pin; -+ struct dentry *dentry; -+ struct super_block *sb; -+ struct inode *inode; -+ struct file *h_file; -+ -+ dentry = file->f_path.dentry; -+ sb = dentry->d_sb; -+ inode = dentry->d_inode; -+ au_mtx_and_read_lock(inode); -+ -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_ready_to_write(file, -1, &pin); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (unlikely(err)) { -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ goto out; -+ } -+ -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ au_unpin(&pin); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ -+ lockdep_off(); -+ err = vfs_fallocate(h_file, mode, offset, len); -+ lockdep_on(); -+ ii_write_lock_child(inode); -+ au_cpup_attr_timesizes(inode); -+ inode->i_mode = file_inode(h_file)->i_mode; -+ ii_write_unlock(inode); -+ fput(h_file); -+ -+out: -+ si_read_unlock(sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * The locking order around current->mmap_sem. -+ * - in most and regular cases -+ * file I/O syscall -- aufs_read() or something -+ * -- si_rwsem for read -- mmap_sem -+ * (Note that [fdi]i_rwsem are released before mmap_sem). -+ * - in mmap case -+ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem -+ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for -+ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in -+ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. -+ * It means that when aufs acquires si_rwsem for write, the process should never -+ * acquire mmap_sem. -+ * -+ * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a -+ * problem either since any directory is not able to be mmap-ed. -+ * The similar scenario is applied to aufs_readlink() too. -+ */ -+ -+#if 0 /* stop calling security_file_mmap() */ -+/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ -+#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) -+ -+static unsigned long au_arch_prot_conv(unsigned long flags) -+{ -+ /* currently ppc64 only */ -+#ifdef CONFIG_PPC64 -+ /* cf. linux/arch/powerpc/include/asm/mman.h */ -+ AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); -+ return AuConv_VM_PROT(flags, SAO); -+#else -+ AuDebugOn(arch_calc_vm_prot_bits(-1)); -+ return 0; -+#endif -+} -+ -+static unsigned long au_prot_conv(unsigned long flags) -+{ -+ return AuConv_VM_PROT(flags, READ) -+ | AuConv_VM_PROT(flags, WRITE) -+ | AuConv_VM_PROT(flags, EXEC) -+ | au_arch_prot_conv(flags); -+} -+ -+/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ -+#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) -+ -+static unsigned long au_flag_conv(unsigned long flags) -+{ -+ return AuConv_VM_MAP(flags, GROWSDOWN) -+ | AuConv_VM_MAP(flags, DENYWRITE) -+ | AuConv_VM_MAP(flags, LOCKED); -+} -+#endif -+ -+static int aufs_mmap(struct file *file, struct vm_area_struct *vma) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ const unsigned char wlock -+ = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); -+ struct dentry *dentry; -+ struct super_block *sb; -+ struct file *h_file; -+ struct au_branch *br; -+ struct au_pin pin; -+ -+ AuDbgVmRegion(file, vma); -+ -+ dentry = file->f_path.dentry; -+ sb = dentry->d_sb; -+ lockdep_off(); -+ si_read_lock(sb, AuLock_NOPLMW); -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ -+ if (wlock) { -+ err = au_ready_to_write(file, -1, &pin); -+ di_write_unlock(dentry); -+ if (unlikely(err)) { -+ fi_write_unlock(file); -+ goto out; -+ } -+ au_unpin(&pin); -+ } else -+ di_write_unlock(dentry); -+ -+ bstart = au_fbstart(file); -+ br = au_sbr(sb, bstart); -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ au_set_mmapped(file); -+ fi_write_unlock(file); -+ lockdep_on(); -+ -+ au_vm_file_reset(vma, h_file); -+ /* -+ * we cannot call security_mmap_file() here since it may acquire -+ * mmap_sem or i_mutex. -+ * -+ * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), -+ * au_flag_conv(vma->vm_flags)); -+ */ -+ if (!err) -+ err = h_file->f_op->mmap(h_file, vma); -+ if (unlikely(err)) -+ goto out_reset; -+ -+ au_vm_prfile_set(vma, file); -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(file_inode(file), file_inode(h_file)); -+ goto out_fput; /* success */ -+ -+out_reset: -+ au_unset_mmapped(file); -+ au_vm_file_reset(vma, file); -+out_fput: -+ fput(h_file); -+ lockdep_off(); -+out: -+ si_read_unlock(sb); -+ lockdep_on(); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, -+ int datasync) -+{ -+ int err; -+ struct au_pin pin; -+ struct dentry *dentry; -+ struct inode *inode; -+ struct file *h_file; -+ struct super_block *sb; -+ -+ dentry = file->f_path.dentry; -+ inode = dentry->d_inode; -+ sb = dentry->d_sb; -+ mutex_lock(&inode->i_mutex); -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ -+ err = 0; /* -EBADF; */ /* posix? */ -+ if (unlikely(!(file->f_mode & FMODE_WRITE))) -+ goto out_si; -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out_si; -+ -+ err = au_ready_to_write(file, -1, &pin); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (unlikely(err)) -+ goto out_unlock; -+ au_unpin(&pin); -+ -+ err = -EINVAL; -+ h_file = au_hf_top(file); -+ err = vfsub_fsync(h_file, &h_file->f_path, datasync); -+ au_cpup_attr_timesizes(inode); -+ -+out_unlock: -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+out_si: -+ si_read_unlock(sb); -+out: -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+/* no one supports this operation, currently */ -+#if 0 -+static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync) -+{ -+ int err; -+ struct au_pin pin; -+ struct dentry *dentry; -+ struct inode *inode; -+ struct file *file, *h_file; -+ -+ file = kio->ki_filp; -+ dentry = file->f_path.dentry; -+ inode = dentry->d_inode; -+ au_mtx_and_read_lock(inode); -+ -+ err = 0; /* -EBADF; */ /* posix? */ -+ if (unlikely(!(file->f_mode & FMODE_WRITE))) -+ goto out; -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_ready_to_write(file, -1, &pin); -+ di_downgrade_lock(dentry, AuLock_IR); -+ if (unlikely(err)) -+ goto out_unlock; -+ au_unpin(&pin); -+ -+ err = -ENOSYS; -+ h_file = au_hf_top(file); -+ if (h_file->f_op->aio_fsync) { -+ struct mutex *h_mtx; -+ -+ h_mtx = &file_inode(h_file)->i_mutex; -+ if (!is_sync_kiocb(kio)) { -+ get_file(h_file); -+ fput(file); -+ } -+ kio->ki_filp = h_file; -+ err = h_file->f_op->aio_fsync(kio, datasync); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ if (!err) -+ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); -+ /*ignore*/ -+ au_cpup_attr_timesizes(inode); -+ mutex_unlock(h_mtx); -+ } -+ -+out_unlock: -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+out: -+ si_read_unlock(inode->sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+#endif -+ -+static int aufs_fasync(int fd, struct file *file, int flag) -+{ -+ int err; -+ struct file *h_file; -+ struct dentry *dentry; -+ struct super_block *sb; -+ -+ dentry = file->f_path.dentry; -+ sb = dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); -+ if (unlikely(err)) -+ goto out; -+ -+ h_file = au_hf_top(file); -+ if (h_file->f_op->fasync) -+ err = h_file->f_op->fasync(fd, h_file, flag); -+ -+ di_read_unlock(dentry, AuLock_IR); -+ fi_read_unlock(file); -+ -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* no one supports this operation, currently */ -+#if 0 -+static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, -+ size_t len, loff_t *pos, int more) -+{ -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+const struct file_operations aufs_file_fop = { -+ .owner = THIS_MODULE, -+ -+ .llseek = default_llseek, -+ -+ .read = aufs_read, -+ .write = aufs_write, -+ .read_iter = aufs_read_iter, -+ .write_iter = aufs_write_iter, -+ -+#ifdef CONFIG_AUFS_POLL -+ .poll = aufs_poll, -+#endif -+ .unlocked_ioctl = aufs_ioctl_nondir, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = aufs_compat_ioctl_nondir, -+#endif -+ .mmap = aufs_mmap, -+ .open = aufs_open_nondir, -+ .flush = aufs_flush_nondir, -+ .release = aufs_release_nondir, -+ .fsync = aufs_fsync_nondir, -+ /* .aio_fsync = aufs_aio_fsync_nondir, */ -+ .fasync = aufs_fasync, -+ /* .sendpage = aufs_sendpage, */ -+ .splice_write = aufs_splice_write, -+ .splice_read = aufs_splice_read, -+#if 0 -+ .aio_splice_write = aufs_aio_splice_write, -+ .aio_splice_read = aufs_aio_splice_read, -+#endif -+ .fallocate = aufs_fallocate -+}; -diff -Nur linux-4.0.4.orig/fs/aufs/fstype.h linux-4.0.4/fs/aufs/fstype.h ---- linux-4.0.4.orig/fs/aufs/fstype.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/fstype.h 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,387 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * judging filesystem type -+ */ -+ -+#ifndef __AUFS_FSTYPE_H__ -+#define __AUFS_FSTYPE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include -+ -+static inline int au_test_aufs(struct super_block *sb) -+{ -+ return sb->s_magic == AUFS_SUPER_MAGIC; -+} -+ -+static inline const char *au_sbtype(struct super_block *sb) -+{ -+ return sb->s_type->name; -+} -+ -+static inline int au_test_iso9660(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE) -+ return sb->s_magic == ISOFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_romfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE) -+ return sb->s_magic == ROMFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_cramfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) -+ return sb->s_magic == CRAMFS_MAGIC; -+#endif -+ return 0; -+} -+ -+static inline int au_test_nfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE) -+ return sb->s_magic == NFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_fuse(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE) -+ return sb->s_magic == FUSE_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_xfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE) -+ return sb->s_magic == XFS_SB_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_tmpfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_TMPFS -+ return sb->s_magic == TMPFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE) -+ return !strcmp(au_sbtype(sb), "ecryptfs"); -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_ramfs(struct super_block *sb) -+{ -+ return sb->s_magic == RAMFS_MAGIC; -+} -+ -+static inline int au_test_ubifs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE) -+ return sb->s_magic == UBIFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_procfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_PROC_FS -+ return sb->s_magic == PROC_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_sysfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_SYSFS -+ return sb->s_magic == SYSFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_configfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE) -+ return sb->s_magic == CONFIGFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_minix(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE) -+ return sb->s_magic == MINIX3_SUPER_MAGIC -+ || sb->s_magic == MINIX2_SUPER_MAGIC -+ || sb->s_magic == MINIX2_SUPER_MAGIC2 -+ || sb->s_magic == MINIX_SUPER_MAGIC -+ || sb->s_magic == MINIX_SUPER_MAGIC2; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_fat(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE) -+ return sb->s_magic == MSDOS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_msdos(struct super_block *sb) -+{ -+ return au_test_fat(sb); -+} -+ -+static inline int au_test_vfat(struct super_block *sb) -+{ -+ return au_test_fat(sb); -+} -+ -+static inline int au_test_securityfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_SECURITYFS -+ return sb->s_magic == SECURITYFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_squashfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE) -+ return sb->s_magic == SQUASHFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_btrfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE) -+ return sb->s_magic == BTRFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_xenfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE) -+ return sb->s_magic == XENFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_debugfs(struct super_block *sb __maybe_unused) -+{ -+#ifdef CONFIG_DEBUG_FS -+ return sb->s_magic == DEBUGFS_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_nilfs(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_NILFS) || defined(CONFIG_NILFS_MODULE) -+ return sb->s_magic == NILFS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+static inline int au_test_hfsplus(struct super_block *sb __maybe_unused) -+{ -+#if defined(CONFIG_HFSPLUS_FS) || defined(CONFIG_HFSPLUS_FS_MODULE) -+ return sb->s_magic == HFSPLUS_SUPER_MAGIC; -+#else -+ return 0; -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * they can't be an aufs branch. -+ */ -+static inline int au_test_fs_unsuppoted(struct super_block *sb) -+{ -+ return -+#ifndef CONFIG_AUFS_BR_RAMFS -+ au_test_ramfs(sb) || -+#endif -+ au_test_procfs(sb) -+ || au_test_sysfs(sb) -+ || au_test_configfs(sb) -+ || au_test_debugfs(sb) -+ || au_test_securityfs(sb) -+ || au_test_xenfs(sb) -+ || au_test_ecryptfs(sb) -+ /* || !strcmp(au_sbtype(sb), "unionfs") */ -+ || au_test_aufs(sb); /* will be supported in next version */ -+} -+ -+static inline int au_test_fs_remote(struct super_block *sb) -+{ -+ return !au_test_tmpfs(sb) -+#ifdef CONFIG_AUFS_BR_RAMFS -+ && !au_test_ramfs(sb) -+#endif -+ && !(sb->s_type->fs_flags & FS_REQUIRES_DEV); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * Note: these functions (below) are created after reading ->getattr() in all -+ * filesystems under linux/fs. it means we have to do so in every update... -+ */ -+ -+/* -+ * some filesystems require getattr to refresh the inode attributes before -+ * referencing. -+ * in most cases, we can rely on the inode attribute in NFS (or every remote fs) -+ * and leave the work for d_revalidate() -+ */ -+static inline int au_test_fs_refresh_iattr(struct super_block *sb) -+{ -+ return au_test_nfs(sb) -+ || au_test_fuse(sb) -+ /* || au_test_btrfs(sb) */ /* untested */ -+ ; -+} -+ -+/* -+ * filesystems which don't maintain i_size or i_blocks. -+ */ -+static inline int au_test_fs_bad_iattr_size(struct super_block *sb) -+{ -+ return au_test_xfs(sb) -+ || au_test_btrfs(sb) -+ || au_test_ubifs(sb) -+ || au_test_hfsplus(sb) /* maintained, but incorrect */ -+ /* || au_test_minix(sb) */ /* untested */ -+ ; -+} -+ -+/* -+ * filesystems which don't store the correct value in some of their inode -+ * attributes. -+ */ -+static inline int au_test_fs_bad_iattr(struct super_block *sb) -+{ -+ return au_test_fs_bad_iattr_size(sb) -+ || au_test_fat(sb) -+ || au_test_msdos(sb) -+ || au_test_vfat(sb); -+} -+ -+/* they don't check i_nlink in link(2) */ -+static inline int au_test_fs_no_limit_nlink(struct super_block *sb) -+{ -+ return au_test_tmpfs(sb) -+#ifdef CONFIG_AUFS_BR_RAMFS -+ || au_test_ramfs(sb) -+#endif -+ || au_test_ubifs(sb) -+ || au_test_hfsplus(sb); -+} -+ -+/* -+ * filesystems which sets S_NOATIME and S_NOCMTIME. -+ */ -+static inline int au_test_fs_notime(struct super_block *sb) -+{ -+ return au_test_nfs(sb) -+ || au_test_fuse(sb) -+ || au_test_ubifs(sb) -+ ; -+} -+ -+/* temporary support for i#1 in cramfs */ -+static inline int au_test_fs_unique_ino(struct inode *inode) -+{ -+ if (au_test_cramfs(inode->i_sb)) -+ return inode->i_ino != 1; -+ return 1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * the filesystem where the xino files placed must support i/o after unlink and -+ * maintain i_size and i_blocks. -+ */ -+static inline int au_test_fs_bad_xino(struct super_block *sb) -+{ -+ return au_test_fs_remote(sb) -+ || au_test_fs_bad_iattr_size(sb) -+ /* don't want unnecessary work for xino */ -+ || au_test_aufs(sb) -+ || au_test_ecryptfs(sb) -+ || au_test_nilfs(sb); -+} -+ -+static inline int au_test_fs_trunc_xino(struct super_block *sb) -+{ -+ return au_test_tmpfs(sb) -+ || au_test_ramfs(sb); -+} -+ -+/* -+ * test if the @sb is real-readonly. -+ */ -+static inline int au_test_fs_rr(struct super_block *sb) -+{ -+ return au_test_squashfs(sb) -+ || au_test_iso9660(sb) -+ || au_test_cramfs(sb) -+ || au_test_romfs(sb); -+} -+ -+/* -+ * test if the @inode is nfs with 'noacl' option -+ * NFS always sets MS_POSIXACL regardless its mount option 'noacl.' -+ */ -+static inline int au_test_nfs_noacl(struct inode *inode) -+{ -+ return au_test_nfs(inode->i_sb) -+ /* && IS_POSIXACL(inode) */ -+ && !nfs_server_capable(inode, NFS_CAP_ACLS); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_FSTYPE_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/hfsnotify.c linux-4.0.4/fs/aufs/hfsnotify.c ---- linux-4.0.4.orig/fs/aufs/hfsnotify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/hfsnotify.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,275 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * fsnotify for the lower directories -+ */ -+ -+#include "aufs.h" -+ -+/* FS_IN_IGNORED is unnecessary */ -+static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE -+ | FS_CREATE | FS_EVENT_ON_CHILD); -+static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq); -+static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0); -+ -+static void au_hfsn_free_mark(struct fsnotify_mark *mark) -+{ -+ struct au_hnotify *hn = container_of(mark, struct au_hnotify, -+ hn_mark); -+ AuDbg("here\n"); -+ au_cache_free_hnotify(hn); -+ smp_mb__before_atomic(); -+ if (atomic64_dec_and_test(&au_hfsn_ifree)) -+ wake_up(&au_hfsn_wq); -+} -+ -+static int au_hfsn_alloc(struct au_hinode *hinode) -+{ -+ int err; -+ struct au_hnotify *hn; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct fsnotify_mark *mark; -+ aufs_bindex_t bindex; -+ -+ hn = hinode->hi_notify; -+ sb = hn->hn_aufs_inode->i_sb; -+ bindex = au_br_index(sb, hinode->hi_id); -+ br = au_sbr(sb, bindex); -+ AuDebugOn(!br->br_hfsn); -+ -+ mark = &hn->hn_mark; -+ fsnotify_init_mark(mark, au_hfsn_free_mark); -+ mark->mask = AuHfsnMask; -+ /* -+ * by udba rename or rmdir, aufs assign a new inode to the known -+ * h_inode, so specify 1 to allow dups. -+ */ -+ lockdep_off(); -+ err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode, -+ /*mnt*/NULL, /*allow_dups*/1); -+ /* even if err */ -+ fsnotify_put_mark(mark); -+ lockdep_on(); -+ -+ return err; -+} -+ -+static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn) -+{ -+ struct fsnotify_mark *mark; -+ unsigned long long ull; -+ struct fsnotify_group *group; -+ -+ ull = atomic64_inc_return(&au_hfsn_ifree); -+ BUG_ON(!ull); -+ -+ mark = &hn->hn_mark; -+ spin_lock(&mark->lock); -+ group = mark->group; -+ fsnotify_get_group(group); -+ spin_unlock(&mark->lock); -+ lockdep_off(); -+ fsnotify_destroy_mark(mark, group); -+ fsnotify_put_group(group); -+ lockdep_on(); -+ -+ /* free hn by myself */ -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) -+{ -+ struct fsnotify_mark *mark; -+ -+ mark = &hinode->hi_notify->hn_mark; -+ spin_lock(&mark->lock); -+ if (do_set) { -+ AuDebugOn(mark->mask & AuHfsnMask); -+ mark->mask |= AuHfsnMask; -+ } else { -+ AuDebugOn(!(mark->mask & AuHfsnMask)); -+ mark->mask &= ~AuHfsnMask; -+ } -+ spin_unlock(&mark->lock); -+ /* fsnotify_recalc_inode_mask(hinode->hi_inode); */ -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* #define AuDbgHnotify */ -+#ifdef AuDbgHnotify -+static char *au_hfsn_name(u32 mask) -+{ -+#ifdef CONFIG_AUFS_DEBUG -+#define test_ret(flag) \ -+ do { \ -+ if (mask & flag) \ -+ return #flag; \ -+ } while (0) -+ test_ret(FS_ACCESS); -+ test_ret(FS_MODIFY); -+ test_ret(FS_ATTRIB); -+ test_ret(FS_CLOSE_WRITE); -+ test_ret(FS_CLOSE_NOWRITE); -+ test_ret(FS_OPEN); -+ test_ret(FS_MOVED_FROM); -+ test_ret(FS_MOVED_TO); -+ test_ret(FS_CREATE); -+ test_ret(FS_DELETE); -+ test_ret(FS_DELETE_SELF); -+ test_ret(FS_MOVE_SELF); -+ test_ret(FS_UNMOUNT); -+ test_ret(FS_Q_OVERFLOW); -+ test_ret(FS_IN_IGNORED); -+ test_ret(FS_ISDIR); -+ test_ret(FS_IN_ONESHOT); -+ test_ret(FS_EVENT_ON_CHILD); -+ return ""; -+#undef test_ret -+#else -+ return "??"; -+#endif -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_free_group(struct fsnotify_group *group) -+{ -+ struct au_br_hfsnotify *hfsn = group->private; -+ -+ AuDbg("here\n"); -+ kfree(hfsn); -+} -+ -+static int au_hfsn_handle_event(struct fsnotify_group *group, -+ struct inode *inode, -+ struct fsnotify_mark *inode_mark, -+ struct fsnotify_mark *vfsmount_mark, -+ u32 mask, void *data, int data_type, -+ const unsigned char *file_name, u32 cookie) -+{ -+ int err; -+ struct au_hnotify *hnotify; -+ struct inode *h_dir, *h_inode; -+ struct qstr h_child_qstr = QSTR_INIT(file_name, strlen(file_name)); -+ -+ AuDebugOn(data_type != FSNOTIFY_EVENT_INODE); -+ -+ err = 0; -+ /* if FS_UNMOUNT happens, there must be another bug */ -+ AuDebugOn(mask & FS_UNMOUNT); -+ if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) -+ goto out; -+ -+ h_dir = inode; -+ h_inode = NULL; -+#ifdef AuDbgHnotify -+ au_debug_on(); -+ if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 -+ || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { -+ AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", -+ h_dir->i_ino, mask, au_hfsn_name(mask), -+ AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); -+ /* WARN_ON(1); */ -+ } -+ au_debug_off(); -+#endif -+ -+ AuDebugOn(!inode_mark); -+ hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); -+ err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode); -+ -+out: -+ return err; -+} -+ -+static struct fsnotify_ops au_hfsn_ops = { -+ .handle_event = au_hfsn_handle_event, -+ .free_group_priv = au_hfsn_free_group -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_fin_br(struct au_branch *br) -+{ -+ struct au_br_hfsnotify *hfsn; -+ -+ hfsn = br->br_hfsn; -+ if (hfsn) { -+ lockdep_off(); -+ fsnotify_put_group(hfsn->hfsn_group); -+ lockdep_on(); -+ } -+} -+ -+static int au_hfsn_init_br(struct au_branch *br, int perm) -+{ -+ int err; -+ struct fsnotify_group *group; -+ struct au_br_hfsnotify *hfsn; -+ -+ err = 0; -+ br->br_hfsn = NULL; -+ if (!au_br_hnotifyable(perm)) -+ goto out; -+ -+ err = -ENOMEM; -+ hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS); -+ if (unlikely(!hfsn)) -+ goto out; -+ -+ err = 0; -+ group = fsnotify_alloc_group(&au_hfsn_ops); -+ if (IS_ERR(group)) { -+ err = PTR_ERR(group); -+ pr_err("fsnotify_alloc_group() failed, %d\n", err); -+ goto out_hfsn; -+ } -+ -+ group->private = hfsn; -+ hfsn->hfsn_group = group; -+ br->br_hfsn = hfsn; -+ goto out; /* success */ -+ -+out_hfsn: -+ kfree(hfsn); -+out: -+ return err; -+} -+ -+static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) -+{ -+ int err; -+ -+ err = 0; -+ if (!br->br_hfsn) -+ err = au_hfsn_init_br(br, perm); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_hfsn_fin(void) -+{ -+ AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree)); -+ wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree)); -+} -+ -+const struct au_hnotify_op au_hnotify_op = { -+ .ctl = au_hfsn_ctl, -+ .alloc = au_hfsn_alloc, -+ .free = au_hfsn_free, -+ -+ .fin = au_hfsn_fin, -+ -+ .reset_br = au_hfsn_reset_br, -+ .fin_br = au_hfsn_fin_br, -+ .init_br = au_hfsn_init_br -+}; -diff -Nur linux-4.0.4.orig/fs/aufs/hfsplus.c linux-4.0.4/fs/aufs/hfsplus.c ---- linux-4.0.4.orig/fs/aufs/hfsplus.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/hfsplus.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,43 @@ -+/* -+ * Copyright (C) 2010-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * special support for filesystems which aqucires an inode mutex -+ * at final closing a file, eg, hfsplus. -+ * -+ * This trick is very simple and stupid, just to open the file before really -+ * neceeary open to tell hfsplus that this is not the final closing. -+ * The caller should call au_h_open_pre() after acquiring the inode mutex, -+ * and au_h_open_post() after releasing it. -+ */ -+ -+#include "aufs.h" -+ -+struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, -+ int force_wr) -+{ -+ struct file *h_file; -+ struct dentry *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ AuDebugOn(!h_dentry); -+ AuDebugOn(!h_dentry->d_inode); -+ -+ h_file = NULL; -+ if (au_test_hfsplus(h_dentry->d_sb) -+ && d_is_reg(h_dentry)) -+ h_file = au_h_open(dentry, bindex, -+ O_RDONLY | O_NOATIME | O_LARGEFILE, -+ /*file*/NULL, force_wr); -+ return h_file; -+} -+ -+void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, -+ struct file *h_file) -+{ -+ if (h_file) { -+ fput(h_file); -+ au_sbr_put(dentry->d_sb, bindex); -+ } -+} -diff -Nur linux-4.0.4.orig/fs/aufs/hnotify.c linux-4.0.4/fs/aufs/hnotify.c ---- linux-4.0.4.orig/fs/aufs/hnotify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/hnotify.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,701 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * abstraction to notify the direct changes on lower directories -+ */ -+ -+#include "aufs.h" -+ -+int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) -+{ -+ int err; -+ struct au_hnotify *hn; -+ -+ err = -ENOMEM; -+ hn = au_cache_alloc_hnotify(); -+ if (hn) { -+ hn->hn_aufs_inode = inode; -+ hinode->hi_notify = hn; -+ err = au_hnotify_op.alloc(hinode); -+ AuTraceErr(err); -+ if (unlikely(err)) { -+ hinode->hi_notify = NULL; -+ au_cache_free_hnotify(hn); -+ /* -+ * The upper dir was removed by udba, but the same named -+ * dir left. In this case, aufs assignes a new inode -+ * number and set the monitor again. -+ * For the lower dir, the old monitnor is still left. -+ */ -+ if (err == -EEXIST) -+ err = 0; -+ } -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+void au_hn_free(struct au_hinode *hinode) -+{ -+ struct au_hnotify *hn; -+ -+ hn = hinode->hi_notify; -+ if (hn) { -+ hinode->hi_notify = NULL; -+ if (au_hnotify_op.free(hinode, hn)) -+ au_cache_free_hnotify(hn); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_hn_ctl(struct au_hinode *hinode, int do_set) -+{ -+ if (hinode->hi_notify) -+ au_hnotify_op.ctl(hinode, do_set); -+} -+ -+void au_hn_reset(struct inode *inode, unsigned int flags) -+{ -+ aufs_bindex_t bindex, bend; -+ struct inode *hi; -+ struct dentry *iwhdentry; -+ -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { -+ hi = au_h_iptr(inode, bindex); -+ if (!hi) -+ continue; -+ -+ /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */ -+ iwhdentry = au_hi_wh(inode, bindex); -+ if (iwhdentry) -+ dget(iwhdentry); -+ au_igrab(hi); -+ au_set_h_iptr(inode, bindex, NULL, 0); -+ au_set_h_iptr(inode, bindex, au_igrab(hi), -+ flags & ~AuHi_XINO); -+ iput(hi); -+ dput(iwhdentry); -+ /* mutex_unlock(&hi->i_mutex); */ -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int hn_xino(struct inode *inode, struct inode *h_inode) -+{ -+ int err; -+ aufs_bindex_t bindex, bend, bfound, bstart; -+ struct inode *h_i; -+ -+ err = 0; -+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { -+ pr_warn("branch root dir was changed\n"); -+ goto out; -+ } -+ -+ bfound = -1; -+ bend = au_ibend(inode); -+ bstart = au_ibstart(inode); -+#if 0 /* reserved for future use */ -+ if (bindex == bend) { -+ /* keep this ino in rename case */ -+ goto out; -+ } -+#endif -+ for (bindex = bstart; bindex <= bend; bindex++) -+ if (au_h_iptr(inode, bindex) == h_inode) { -+ bfound = bindex; -+ break; -+ } -+ if (bfound < 0) -+ goto out; -+ -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ h_i = au_h_iptr(inode, bindex); -+ if (!h_i) -+ continue; -+ -+ err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0); -+ /* ignore this error */ -+ /* bad action? */ -+ } -+ -+ /* children inode number will be broken */ -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int hn_gen_tree(struct dentry *dentry) -+{ -+ int err, i, j, ndentry; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, dentry, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) { -+ struct dentry *d; -+ -+ d = dentries[j]; -+ if (IS_ROOT(d)) -+ continue; -+ -+ au_digen_dec(d); -+ if (d->d_inode) -+ /* todo: reset children xino? -+ cached children only? */ -+ au_iigen_dec(d->d_inode); -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+ -+#if 0 -+ /* discard children */ -+ dentry_unhash(dentry); -+ dput(dentry); -+#endif -+out: -+ return err; -+} -+ -+/* -+ * return 0 if processed. -+ */ -+static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, -+ const unsigned int isdir) -+{ -+ int err; -+ struct dentry *d; -+ struct qstr *dname; -+ -+ err = 1; -+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { -+ pr_warn("branch root dir was changed\n"); -+ err = 0; -+ goto out; -+ } -+ -+ if (!isdir) { -+ AuDebugOn(!name); -+ au_iigen_dec(inode); -+ spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { -+ spin_lock(&d->d_lock); -+ dname = &d->d_name; -+ if (dname->len != nlen -+ && memcmp(dname->name, name, nlen)) { -+ spin_unlock(&d->d_lock); -+ continue; -+ } -+ err = 0; -+ au_digen_dec(d); -+ spin_unlock(&d->d_lock); -+ break; -+ } -+ spin_unlock(&inode->i_lock); -+ } else { -+ au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); -+ d = d_find_any_alias(inode); -+ if (!d) { -+ au_iigen_dec(inode); -+ goto out; -+ } -+ -+ spin_lock(&d->d_lock); -+ dname = &d->d_name; -+ if (dname->len == nlen && !memcmp(dname->name, name, nlen)) { -+ spin_unlock(&d->d_lock); -+ err = hn_gen_tree(d); -+ spin_lock(&d->d_lock); -+ } -+ spin_unlock(&d->d_lock); -+ dput(d); -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) -+{ -+ int err; -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ if (IS_ROOT(dentry) -+ /* || (inode && inode->i_ino == AUFS_ROOT_INO) */ -+ ) { -+ pr_warn("branch root dir was changed\n"); -+ return 0; -+ } -+ -+ err = 0; -+ if (!isdir) { -+ au_digen_dec(dentry); -+ if (inode) -+ au_iigen_dec(inode); -+ } else { -+ au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); -+ if (inode) -+ err = hn_gen_tree(dentry); -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* hnotify job flags */ -+#define AuHnJob_XINO0 1 -+#define AuHnJob_GEN (1 << 1) -+#define AuHnJob_DIRENT (1 << 2) -+#define AuHnJob_ISDIR (1 << 3) -+#define AuHnJob_TRYXINO0 (1 << 4) -+#define AuHnJob_MNTPNT (1 << 5) -+#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name) -+#define au_fset_hnjob(flags, name) \ -+ do { (flags) |= AuHnJob_##name; } while (0) -+#define au_fclr_hnjob(flags, name) \ -+ do { (flags) &= ~AuHnJob_##name; } while (0) -+ -+enum { -+ AuHn_CHILD, -+ AuHn_PARENT, -+ AuHnLast -+}; -+ -+struct au_hnotify_args { -+ struct inode *h_dir, *dir, *h_child_inode; -+ u32 mask; -+ unsigned int flags[AuHnLast]; -+ unsigned int h_child_nlen; -+ char h_child_name[]; -+}; -+ -+struct hn_job_args { -+ unsigned int flags; -+ struct inode *inode, *h_inode, *dir, *h_dir; -+ struct dentry *dentry; -+ char *h_name; -+ int h_nlen; -+}; -+ -+static int hn_job(struct hn_job_args *a) -+{ -+ const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR); -+ int e; -+ -+ /* reset xino */ -+ if (au_ftest_hnjob(a->flags, XINO0) && a->inode) -+ hn_xino(a->inode, a->h_inode); /* ignore this error */ -+ -+ if (au_ftest_hnjob(a->flags, TRYXINO0) -+ && a->inode -+ && a->h_inode) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ if (!a->h_inode->i_nlink -+ && !(a->h_inode->i_state & I_LINKABLE)) -+ hn_xino(a->inode, a->h_inode); /* ignore this error */ -+ mutex_unlock(&a->h_inode->i_mutex); -+ } -+ -+ /* make the generation obsolete */ -+ if (au_ftest_hnjob(a->flags, GEN)) { -+ e = -1; -+ if (a->inode) -+ e = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode, -+ isdir); -+ if (e && a->dentry) -+ hn_gen_by_name(a->dentry, isdir); -+ /* ignore this error */ -+ } -+ -+ /* make dir entries obsolete */ -+ if (au_ftest_hnjob(a->flags, DIRENT) && a->inode) { -+ struct au_vdir *vdir; -+ -+ vdir = au_ivdir(a->inode); -+ if (vdir) -+ vdir->vd_jiffy = 0; -+ /* IMustLock(a->inode); */ -+ /* a->inode->i_version++; */ -+ } -+ -+ /* can do nothing but warn */ -+ if (au_ftest_hnjob(a->flags, MNTPNT) -+ && a->dentry -+ && d_mountpoint(a->dentry)) -+ pr_warn("mount-point %pd is removed or renamed\n", a->dentry); -+ -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, -+ struct inode *dir) -+{ -+ struct dentry *dentry, *d, *parent; -+ struct qstr *dname; -+ -+ parent = d_find_any_alias(dir); -+ if (!parent) -+ return NULL; -+ -+ dentry = NULL; -+ spin_lock(&parent->d_lock); -+ list_for_each_entry(d, &parent->d_subdirs, d_child) { -+ /* AuDbg("%pd\n", d); */ -+ spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); -+ dname = &d->d_name; -+ if (dname->len != nlen || memcmp(dname->name, name, nlen)) -+ goto cont_unlock; -+ if (au_di(d)) -+ au_digen_dec(d); -+ else -+ goto cont_unlock; -+ if (au_dcount(d) > 0) { -+ dentry = dget_dlock(d); -+ spin_unlock(&d->d_lock); -+ break; -+ } -+ -+cont_unlock: -+ spin_unlock(&d->d_lock); -+ } -+ spin_unlock(&parent->d_lock); -+ dput(parent); -+ -+ if (dentry) -+ di_write_lock_child(dentry); -+ -+ return dentry; -+} -+ -+static struct inode *lookup_wlock_by_ino(struct super_block *sb, -+ aufs_bindex_t bindex, ino_t h_ino) -+{ -+ struct inode *inode; -+ ino_t ino; -+ int err; -+ -+ inode = NULL; -+ err = au_xino_read(sb, bindex, h_ino, &ino); -+ if (!err && ino) -+ inode = ilookup(sb, ino); -+ if (!inode) -+ goto out; -+ -+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { -+ pr_warn("wrong root branch\n"); -+ iput(inode); -+ inode = NULL; -+ goto out; -+ } -+ -+ ii_write_lock_child(inode); -+ -+out: -+ return inode; -+} -+ -+static void au_hn_bh(void *_args) -+{ -+ struct au_hnotify_args *a = _args; -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend, bfound; -+ unsigned char xino, try_iput; -+ int err; -+ struct inode *inode; -+ ino_t h_ino; -+ struct hn_job_args args; -+ struct dentry *dentry; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(!_args); -+ AuDebugOn(!a->h_dir); -+ AuDebugOn(!a->dir); -+ AuDebugOn(!a->mask); -+ AuDbg("mask 0x%x, i%lu, hi%lu, hci%lu\n", -+ a->mask, a->dir->i_ino, a->h_dir->i_ino, -+ a->h_child_inode ? a->h_child_inode->i_ino : 0); -+ -+ inode = NULL; -+ dentry = NULL; -+ /* -+ * do not lock a->dir->i_mutex here -+ * because of d_revalidate() may cause a deadlock. -+ */ -+ sb = a->dir->i_sb; -+ AuDebugOn(!sb); -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!sbinfo); -+ si_write_lock(sb, AuLock_NOPLMW); -+ -+ ii_read_lock_parent(a->dir); -+ bfound = -1; -+ bend = au_ibend(a->dir); -+ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) -+ if (au_h_iptr(a->dir, bindex) == a->h_dir) { -+ bfound = bindex; -+ break; -+ } -+ ii_read_unlock(a->dir); -+ if (unlikely(bfound < 0)) -+ goto out; -+ -+ xino = !!au_opt_test(au_mntflags(sb), XINO); -+ h_ino = 0; -+ if (a->h_child_inode) -+ h_ino = a->h_child_inode->i_ino; -+ -+ if (a->h_child_nlen -+ && (au_ftest_hnjob(a->flags[AuHn_CHILD], GEN) -+ || au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT))) -+ dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, -+ a->dir); -+ try_iput = 0; -+ if (dentry) -+ inode = dentry->d_inode; -+ if (xino && !inode && h_ino -+ && (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0) -+ || au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0) -+ || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { -+ inode = lookup_wlock_by_ino(sb, bfound, h_ino); -+ try_iput = 1; -+ } -+ -+ args.flags = a->flags[AuHn_CHILD]; -+ args.dentry = dentry; -+ args.inode = inode; -+ args.h_inode = a->h_child_inode; -+ args.dir = a->dir; -+ args.h_dir = a->h_dir; -+ args.h_name = a->h_child_name; -+ args.h_nlen = a->h_child_nlen; -+ err = hn_job(&args); -+ if (dentry) { -+ if (au_di(dentry)) -+ di_write_unlock(dentry); -+ dput(dentry); -+ } -+ if (inode && try_iput) { -+ ii_write_unlock(inode); -+ iput(inode); -+ } -+ -+ ii_write_lock_parent(a->dir); -+ args.flags = a->flags[AuHn_PARENT]; -+ args.dentry = NULL; -+ args.inode = a->dir; -+ args.h_inode = a->h_dir; -+ args.dir = NULL; -+ args.h_dir = NULL; -+ args.h_name = NULL; -+ args.h_nlen = 0; -+ err = hn_job(&args); -+ ii_write_unlock(a->dir); -+ -+out: -+ iput(a->h_child_inode); -+ iput(a->h_dir); -+ iput(a->dir); -+ si_write_unlock(sb); -+ au_nwt_done(&sbinfo->si_nowait); -+ kfree(a); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, -+ struct qstr *h_child_qstr, struct inode *h_child_inode) -+{ -+ int err, len; -+ unsigned int flags[AuHnLast], f; -+ unsigned char isdir, isroot, wh; -+ struct inode *dir; -+ struct au_hnotify_args *args; -+ char *p, *h_child_name; -+ -+ err = 0; -+ AuDebugOn(!hnotify || !hnotify->hn_aufs_inode); -+ dir = igrab(hnotify->hn_aufs_inode); -+ if (!dir) -+ goto out; -+ -+ isroot = (dir->i_ino == AUFS_ROOT_INO); -+ wh = 0; -+ h_child_name = (void *)h_child_qstr->name; -+ len = h_child_qstr->len; -+ if (h_child_name) { -+ if (len > AUFS_WH_PFX_LEN -+ && !memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { -+ h_child_name += AUFS_WH_PFX_LEN; -+ len -= AUFS_WH_PFX_LEN; -+ wh = 1; -+ } -+ } -+ -+ isdir = 0; -+ if (h_child_inode) -+ isdir = !!S_ISDIR(h_child_inode->i_mode); -+ flags[AuHn_PARENT] = AuHnJob_ISDIR; -+ flags[AuHn_CHILD] = 0; -+ if (isdir) -+ flags[AuHn_CHILD] = AuHnJob_ISDIR; -+ au_fset_hnjob(flags[AuHn_PARENT], DIRENT); -+ au_fset_hnjob(flags[AuHn_CHILD], GEN); -+ switch (mask & FS_EVENTS_POSS_ON_CHILD) { -+ case FS_MOVED_FROM: -+ case FS_MOVED_TO: -+ au_fset_hnjob(flags[AuHn_CHILD], XINO0); -+ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); -+ /*FALLTHROUGH*/ -+ case FS_CREATE: -+ AuDebugOn(!h_child_name); -+ break; -+ -+ case FS_DELETE: -+ /* -+ * aufs never be able to get this child inode. -+ * revalidation should be in d_revalidate() -+ * by checking i_nlink, i_generation or d_unhashed(). -+ */ -+ AuDebugOn(!h_child_name); -+ au_fset_hnjob(flags[AuHn_CHILD], TRYXINO0); -+ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); -+ break; -+ -+ default: -+ AuDebugOn(1); -+ } -+ -+ if (wh) -+ h_child_inode = NULL; -+ -+ err = -ENOMEM; -+ /* iput() and kfree() will be called in au_hnotify() */ -+ args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); -+ if (unlikely(!args)) { -+ AuErr1("no memory\n"); -+ iput(dir); -+ goto out; -+ } -+ args->flags[AuHn_PARENT] = flags[AuHn_PARENT]; -+ args->flags[AuHn_CHILD] = flags[AuHn_CHILD]; -+ args->mask = mask; -+ args->dir = dir; -+ args->h_dir = igrab(h_dir); -+ if (h_child_inode) -+ h_child_inode = igrab(h_child_inode); /* can be NULL */ -+ args->h_child_inode = h_child_inode; -+ args->h_child_nlen = len; -+ if (len) { -+ p = (void *)args; -+ p += sizeof(*args); -+ memcpy(p, h_child_name, len); -+ p[len] = 0; -+ } -+ -+ /* NFS fires the event for silly-renamed one from kworker */ -+ f = 0; -+ if (!dir->i_nlink -+ || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE))) -+ f = AuWkq_NEST; -+ err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f); -+ if (unlikely(err)) { -+ pr_err("wkq %d\n", err); -+ iput(args->h_child_inode); -+ iput(args->h_dir); -+ iput(args->dir); -+ kfree(args); -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) -+{ -+ int err; -+ -+ AuDebugOn(!(udba & AuOptMask_UDBA)); -+ -+ err = 0; -+ if (au_hnotify_op.reset_br) -+ err = au_hnotify_op.reset_br(udba, br, perm); -+ -+ return err; -+} -+ -+int au_hnotify_init_br(struct au_branch *br, int perm) -+{ -+ int err; -+ -+ err = 0; -+ if (au_hnotify_op.init_br) -+ err = au_hnotify_op.init_br(br, perm); -+ -+ return err; -+} -+ -+void au_hnotify_fin_br(struct au_branch *br) -+{ -+ if (au_hnotify_op.fin_br) -+ au_hnotify_op.fin_br(br); -+} -+ -+static void au_hn_destroy_cache(void) -+{ -+ kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]); -+ au_cachep[AuCache_HNOTIFY] = NULL; -+} -+ -+int __init au_hnotify_init(void) -+{ -+ int err; -+ -+ err = -ENOMEM; -+ au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify); -+ if (au_cachep[AuCache_HNOTIFY]) { -+ err = 0; -+ if (au_hnotify_op.init) -+ err = au_hnotify_op.init(); -+ if (unlikely(err)) -+ au_hn_destroy_cache(); -+ } -+ AuTraceErr(err); -+ return err; -+} -+ -+void au_hnotify_fin(void) -+{ -+ if (au_hnotify_op.fin) -+ au_hnotify_op.fin(); -+ /* cf. au_cache_fin() */ -+ if (au_cachep[AuCache_HNOTIFY]) -+ au_hn_destroy_cache(); -+} -diff -Nur linux-4.0.4.orig/fs/aufs/iinfo.c linux-4.0.4/fs/aufs/iinfo.c ---- linux-4.0.4.orig/fs/aufs/iinfo.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/iinfo.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,264 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode private data -+ */ -+ -+#include "aufs.h" -+ -+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) -+{ -+ struct inode *h_inode; -+ -+ IiMustAnyLock(inode); -+ -+ h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; -+ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); -+ return h_inode; -+} -+ -+/* todo: hard/soft set? */ -+void au_hiput(struct au_hinode *hinode) -+{ -+ au_hn_free(hinode); -+ dput(hinode->hi_whdentry); -+ iput(hinode->hi_inode); -+} -+ -+unsigned int au_hi_flags(struct inode *inode, int isdir) -+{ -+ unsigned int flags; -+ const unsigned int mnt_flags = au_mntflags(inode->i_sb); -+ -+ flags = 0; -+ if (au_opt_test(mnt_flags, XINO)) -+ au_fset_hi(flags, XINO); -+ if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY)) -+ au_fset_hi(flags, HNOTIFY); -+ return flags; -+} -+ -+void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode, unsigned int flags) -+{ -+ struct au_hinode *hinode; -+ struct inode *hi; -+ struct au_iinfo *iinfo = au_ii(inode); -+ -+ IiMustWriteLock(inode); -+ -+ hinode = iinfo->ii_hinode + bindex; -+ hi = hinode->hi_inode; -+ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); -+ -+ if (hi) -+ au_hiput(hinode); -+ hinode->hi_inode = h_inode; -+ if (h_inode) { -+ int err; -+ struct super_block *sb = inode->i_sb; -+ struct au_branch *br; -+ -+ AuDebugOn(inode->i_mode -+ && (h_inode->i_mode & S_IFMT) -+ != (inode->i_mode & S_IFMT)); -+ if (bindex == iinfo->ii_bstart) -+ au_cpup_igen(inode, h_inode); -+ br = au_sbr(sb, bindex); -+ hinode->hi_id = br->br_id; -+ if (au_ftest_hi(flags, XINO)) { -+ err = au_xino_write(sb, bindex, h_inode->i_ino, -+ inode->i_ino); -+ if (unlikely(err)) -+ AuIOErr1("failed au_xino_write() %d\n", err); -+ } -+ -+ if (au_ftest_hi(flags, HNOTIFY) -+ && au_br_hnotifyable(br->br_perm)) { -+ err = au_hn_alloc(hinode, inode); -+ if (unlikely(err)) -+ AuIOErr1("au_hn_alloc() %d\n", err); -+ } -+ } -+} -+ -+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_wh) -+{ -+ struct au_hinode *hinode; -+ -+ IiMustWriteLock(inode); -+ -+ hinode = au_ii(inode)->ii_hinode + bindex; -+ AuDebugOn(hinode->hi_whdentry); -+ hinode->hi_whdentry = h_wh; -+} -+ -+void au_update_iigen(struct inode *inode, int half) -+{ -+ struct au_iinfo *iinfo; -+ struct au_iigen *iigen; -+ unsigned int sigen; -+ -+ sigen = au_sigen(inode->i_sb); -+ iinfo = au_ii(inode); -+ iigen = &iinfo->ii_generation; -+ spin_lock(&iinfo->ii_genspin); -+ iigen->ig_generation = sigen; -+ if (half) -+ au_ig_fset(iigen->ig_flags, HALF_REFRESHED); -+ else -+ au_ig_fclr(iigen->ig_flags, HALF_REFRESHED); -+ spin_unlock(&iinfo->ii_genspin); -+} -+ -+/* it may be called at remount time, too */ -+void au_update_ibrange(struct inode *inode, int do_put_zero) -+{ -+ struct au_iinfo *iinfo; -+ aufs_bindex_t bindex, bend; -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ -+ IiMustWriteLock(inode); -+ -+ if (do_put_zero && iinfo->ii_bstart >= 0) { -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; -+ bindex++) { -+ struct inode *h_i; -+ -+ h_i = iinfo->ii_hinode[0 + bindex].hi_inode; -+ if (h_i -+ && !h_i->i_nlink -+ && !(h_i->i_state & I_LINKABLE)) -+ au_set_h_iptr(inode, bindex, NULL, 0); -+ } -+ } -+ -+ iinfo->ii_bstart = -1; -+ iinfo->ii_bend = -1; -+ bend = au_sbend(inode->i_sb); -+ for (bindex = 0; bindex <= bend; bindex++) -+ if (iinfo->ii_hinode[0 + bindex].hi_inode) { -+ iinfo->ii_bstart = bindex; -+ break; -+ } -+ if (iinfo->ii_bstart >= 0) -+ for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--) -+ if (iinfo->ii_hinode[0 + bindex].hi_inode) { -+ iinfo->ii_bend = bindex; -+ break; -+ } -+ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_icntnr_init_once(void *_c) -+{ -+ struct au_icntnr *c = _c; -+ struct au_iinfo *iinfo = &c->iinfo; -+ static struct lock_class_key aufs_ii; -+ -+ spin_lock_init(&iinfo->ii_genspin); -+ au_rw_init(&iinfo->ii_rwsem); -+ au_rw_class(&iinfo->ii_rwsem, &aufs_ii); -+ inode_init_once(&c->vfs_inode); -+} -+ -+int au_iinfo_init(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ struct super_block *sb; -+ int nbr, i; -+ -+ sb = inode->i_sb; -+ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); -+ nbr = au_sbend(sb) + 1; -+ if (unlikely(nbr <= 0)) -+ nbr = 1; -+ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); -+ if (iinfo->ii_hinode) { -+ au_ninodes_inc(sb); -+ for (i = 0; i < nbr; i++) -+ iinfo->ii_hinode[i].hi_id = -1; -+ -+ iinfo->ii_generation.ig_generation = au_sigen(sb); -+ iinfo->ii_bstart = -1; -+ iinfo->ii_bend = -1; -+ iinfo->ii_vdir = NULL; -+ return 0; -+ } -+ return -ENOMEM; -+} -+ -+int au_ii_realloc(struct au_iinfo *iinfo, int nbr) -+{ -+ int err, sz; -+ struct au_hinode *hip; -+ -+ AuRwMustWriteLock(&iinfo->ii_rwsem); -+ -+ err = -ENOMEM; -+ sz = sizeof(*hip) * (iinfo->ii_bend + 1); -+ if (!sz) -+ sz = sizeof(*hip); -+ hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS); -+ if (hip) { -+ iinfo->ii_hinode = hip; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+void au_iinfo_fin(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ struct au_hinode *hi; -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend; -+ const unsigned char unlinked = !inode->i_nlink; -+ -+ iinfo = au_ii(inode); -+ /* bad_inode case */ -+ if (!iinfo) -+ return; -+ -+ sb = inode->i_sb; -+ au_ninodes_dec(sb); -+ if (si_pid_test(sb)) -+ au_xino_delete_inode(inode, unlinked); -+ else { -+ /* -+ * it is safe to hide the dependency between sbinfo and -+ * sb->s_umount. -+ */ -+ lockdep_off(); -+ si_noflush_read_lock(sb); -+ au_xino_delete_inode(inode, unlinked); -+ si_read_unlock(sb); -+ lockdep_on(); -+ } -+ -+ if (iinfo->ii_vdir) -+ au_vdir_free(iinfo->ii_vdir); -+ -+ bindex = iinfo->ii_bstart; -+ if (bindex >= 0) { -+ hi = iinfo->ii_hinode + bindex; -+ bend = iinfo->ii_bend; -+ while (bindex++ <= bend) { -+ if (hi->hi_inode) -+ au_hiput(hi); -+ hi++; -+ } -+ } -+ kfree(iinfo->ii_hinode); -+ iinfo->ii_hinode = NULL; -+ AuRwDestroy(&iinfo->ii_rwsem); -+} -diff -Nur linux-4.0.4.orig/fs/aufs/inode.c linux-4.0.4/fs/aufs/inode.c ---- linux-4.0.4.orig/fs/aufs/inode.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/inode.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,482 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode functions -+ */ -+ -+#include "aufs.h" -+ -+struct inode *au_igrab(struct inode *inode) -+{ -+ if (inode) { -+ AuDebugOn(!atomic_read(&inode->i_count)); -+ ihold(inode); -+ } -+ return inode; -+} -+ -+static void au_refresh_hinode_attr(struct inode *inode, int do_version) -+{ -+ au_cpup_attr_all(inode, /*force*/0); -+ au_update_iigen(inode, /*half*/1); -+ if (do_version) -+ inode->i_version++; -+} -+ -+static int au_ii_refresh(struct inode *inode, int *update) -+{ -+ int err, e; -+ umode_t type; -+ aufs_bindex_t bindex, new_bindex; -+ struct super_block *sb; -+ struct au_iinfo *iinfo; -+ struct au_hinode *p, *q, tmp; -+ -+ IiMustWriteLock(inode); -+ -+ *update = 0; -+ sb = inode->i_sb; -+ type = inode->i_mode & S_IFMT; -+ iinfo = au_ii(inode); -+ err = au_ii_realloc(iinfo, au_sbend(sb) + 1); -+ if (unlikely(err)) -+ goto out; -+ -+ AuDebugOn(iinfo->ii_bstart < 0); -+ p = iinfo->ii_hinode + iinfo->ii_bstart; -+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; -+ bindex++, p++) { -+ if (!p->hi_inode) -+ continue; -+ -+ AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT)); -+ new_bindex = au_br_index(sb, p->hi_id); -+ if (new_bindex == bindex) -+ continue; -+ -+ if (new_bindex < 0) { -+ *update = 1; -+ au_hiput(p); -+ p->hi_inode = NULL; -+ continue; -+ } -+ -+ if (new_bindex < iinfo->ii_bstart) -+ iinfo->ii_bstart = new_bindex; -+ if (iinfo->ii_bend < new_bindex) -+ iinfo->ii_bend = new_bindex; -+ /* swap two lower inode, and loop again */ -+ q = iinfo->ii_hinode + new_bindex; -+ tmp = *q; -+ *q = *p; -+ *p = tmp; -+ if (tmp.hi_inode) { -+ bindex--; -+ p--; -+ } -+ } -+ au_update_ibrange(inode, /*do_put_zero*/0); -+ e = au_dy_irefresh(inode); -+ if (unlikely(e && !err)) -+ err = e; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_refresh_hinode_self(struct inode *inode) -+{ -+ int err, update; -+ -+ err = au_ii_refresh(inode, &update); -+ if (!err) -+ au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_refresh_hinode(struct inode *inode, struct dentry *dentry) -+{ -+ int err, e, update; -+ unsigned int flags; -+ umode_t mode; -+ aufs_bindex_t bindex, bend; -+ unsigned char isdir; -+ struct au_hinode *p; -+ struct au_iinfo *iinfo; -+ -+ err = au_ii_refresh(inode, &update); -+ if (unlikely(err)) -+ goto out; -+ -+ update = 0; -+ iinfo = au_ii(inode); -+ p = iinfo->ii_hinode + iinfo->ii_bstart; -+ mode = (inode->i_mode & S_IFMT); -+ isdir = S_ISDIR(mode); -+ flags = au_hi_flags(inode, isdir); -+ bend = au_dbend(dentry); -+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { -+ struct inode *h_i; -+ struct dentry *h_d; -+ -+ h_d = au_h_dptr(dentry, bindex); -+ if (!h_d || !h_d->d_inode) -+ continue; -+ -+ AuDebugOn(mode != (h_d->d_inode->i_mode & S_IFMT)); -+ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { -+ h_i = au_h_iptr(inode, bindex); -+ if (h_i) { -+ if (h_i == h_d->d_inode) -+ continue; -+ err = -EIO; -+ break; -+ } -+ } -+ if (bindex < iinfo->ii_bstart) -+ iinfo->ii_bstart = bindex; -+ if (iinfo->ii_bend < bindex) -+ iinfo->ii_bend = bindex; -+ au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags); -+ update = 1; -+ } -+ au_update_ibrange(inode, /*do_put_zero*/0); -+ e = au_dy_irefresh(inode); -+ if (unlikely(e && !err)) -+ err = e; -+ if (!err) -+ au_refresh_hinode_attr(inode, update && isdir); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int set_inode(struct inode *inode, struct dentry *dentry) -+{ -+ int err; -+ unsigned int flags; -+ umode_t mode; -+ aufs_bindex_t bindex, bstart, btail; -+ unsigned char isdir; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ struct au_iinfo *iinfo; -+ -+ IiMustWriteLock(inode); -+ -+ err = 0; -+ isdir = 0; -+ bstart = au_dbstart(dentry); -+ h_inode = au_h_dptr(dentry, bstart)->d_inode; -+ mode = h_inode->i_mode; -+ switch (mode & S_IFMT) { -+ case S_IFREG: -+ btail = au_dbtail(dentry); -+ inode->i_op = &aufs_iop; -+ inode->i_fop = &aufs_file_fop; -+ err = au_dy_iaop(inode, bstart, h_inode); -+ if (unlikely(err)) -+ goto out; -+ break; -+ case S_IFDIR: -+ isdir = 1; -+ btail = au_dbtaildir(dentry); -+ inode->i_op = &aufs_dir_iop; -+ inode->i_fop = &aufs_dir_fop; -+ break; -+ case S_IFLNK: -+ btail = au_dbtail(dentry); -+ inode->i_op = &aufs_symlink_iop; -+ break; -+ case S_IFBLK: -+ case S_IFCHR: -+ case S_IFIFO: -+ case S_IFSOCK: -+ btail = au_dbtail(dentry); -+ inode->i_op = &aufs_iop; -+ init_special_inode(inode, mode, h_inode->i_rdev); -+ break; -+ default: -+ AuIOErr("Unknown file type 0%o\n", mode); -+ err = -EIO; -+ goto out; -+ } -+ -+ /* do not set hnotify for whiteouted dirs (SHWH mode) */ -+ flags = au_hi_flags(inode, isdir); -+ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) -+ && au_ftest_hi(flags, HNOTIFY) -+ && dentry->d_name.len > AUFS_WH_PFX_LEN -+ && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) -+ au_fclr_hi(flags, HNOTIFY); -+ iinfo = au_ii(inode); -+ iinfo->ii_bstart = bstart; -+ iinfo->ii_bend = btail; -+ for (bindex = bstart; bindex <= btail; bindex++) { -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (h_dentry) -+ au_set_h_iptr(inode, bindex, -+ au_igrab(h_dentry->d_inode), flags); -+ } -+ au_cpup_attr_all(inode, /*force*/1); -+ /* -+ * to force calling aufs_get_acl() every time, -+ * do not call cache_no_acl() for aufs inode. -+ */ -+ -+out: -+ return err; -+} -+ -+/* -+ * successful returns with iinfo write_locked -+ * minus: errno -+ * zero: success, matched -+ * plus: no error, but unmatched -+ */ -+static int reval_inode(struct inode *inode, struct dentry *dentry) -+{ -+ int err; -+ unsigned int gen; -+ struct au_iigen iigen; -+ aufs_bindex_t bindex, bend; -+ struct inode *h_inode, *h_dinode; -+ -+ /* -+ * before this function, if aufs got any iinfo lock, it must be only -+ * one, the parent dir. -+ * it can happen by UDBA and the obsoleted inode number. -+ */ -+ err = -EIO; -+ if (unlikely(inode->i_ino == parent_ino(dentry))) -+ goto out; -+ -+ err = 1; -+ ii_write_lock_new_child(inode); -+ h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { -+ h_inode = au_h_iptr(inode, bindex); -+ if (!h_inode || h_inode != h_dinode) -+ continue; -+ -+ err = 0; -+ gen = au_iigen(inode, &iigen); -+ if (gen == au_digen(dentry) -+ && !au_ig_ftest(iigen.ig_flags, HALF_REFRESHED)) -+ break; -+ -+ /* fully refresh inode using dentry */ -+ err = au_refresh_hinode(inode, dentry); -+ if (!err) -+ au_update_iigen(inode, /*half*/0); -+ break; -+ } -+ -+ if (unlikely(err)) -+ ii_write_unlock(inode); -+out: -+ return err; -+} -+ -+int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ unsigned int d_type, ino_t *ino) -+{ -+ int err; -+ struct mutex *mtx; -+ -+ /* prevent hardlinked inode number from race condition */ -+ mtx = NULL; -+ if (d_type != DT_DIR) { -+ mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx; -+ mutex_lock(mtx); -+ } -+ err = au_xino_read(sb, bindex, h_ino, ino); -+ if (unlikely(err)) -+ goto out; -+ -+ if (!*ino) { -+ err = -EIO; -+ *ino = au_xino_new_ino(sb); -+ if (unlikely(!*ino)) -+ goto out; -+ err = au_xino_write(sb, bindex, h_ino, *ino); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+out: -+ if (mtx) -+ mutex_unlock(mtx); -+ return err; -+} -+ -+/* successful returns with iinfo write_locked */ -+/* todo: return with unlocked? */ -+struct inode *au_new_inode(struct dentry *dentry, int must_new) -+{ -+ struct inode *inode; -+ struct dentry *h_dentry; -+ struct super_block *sb; -+ struct mutex *mtx; -+ ino_t h_ino, ino; -+ int err; -+ aufs_bindex_t bstart; -+ -+ sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ h_dentry = au_h_dptr(dentry, bstart); -+ h_ino = h_dentry->d_inode->i_ino; -+ -+ /* -+ * stop 'race'-ing between hardlinks under different -+ * parents. -+ */ -+ mtx = NULL; -+ if (!d_is_dir(h_dentry)) -+ mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; -+ -+new_ino: -+ if (mtx) -+ mutex_lock(mtx); -+ err = au_xino_read(sb, bstart, h_ino, &ino); -+ inode = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ if (!ino) { -+ ino = au_xino_new_ino(sb); -+ if (unlikely(!ino)) { -+ inode = ERR_PTR(-EIO); -+ goto out; -+ } -+ } -+ -+ AuDbg("i%lu\n", (unsigned long)ino); -+ inode = au_iget_locked(sb, ino); -+ err = PTR_ERR(inode); -+ if (IS_ERR(inode)) -+ goto out; -+ -+ AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); -+ if (inode->i_state & I_NEW) { -+ /* verbose coding for lock class name */ -+ if (unlikely(d_is_symlink(h_dentry))) -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcSymlink_IIINFO); -+ else if (unlikely(d_is_dir(h_dentry))) -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcDir_IIINFO); -+ else /* likely */ -+ au_rw_class(&au_ii(inode)->ii_rwsem, -+ au_lc_key + AuLcNonDir_IIINFO); -+ -+ ii_write_lock_new_child(inode); -+ err = set_inode(inode, dentry); -+ if (!err) { -+ unlock_new_inode(inode); -+ goto out; /* success */ -+ } -+ -+ /* -+ * iget_failed() calls iput(), but we need to call -+ * ii_write_unlock() after iget_failed(). so dirty hack for -+ * i_count. -+ */ -+ atomic_inc(&inode->i_count); -+ iget_failed(inode); -+ ii_write_unlock(inode); -+ au_xino_write(sb, bstart, h_ino, /*ino*/0); -+ /* ignore this error */ -+ goto out_iput; -+ } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { -+ /* -+ * horrible race condition between lookup, readdir and copyup -+ * (or something). -+ */ -+ if (mtx) -+ mutex_unlock(mtx); -+ err = reval_inode(inode, dentry); -+ if (unlikely(err < 0)) { -+ mtx = NULL; -+ goto out_iput; -+ } -+ -+ if (!err) { -+ mtx = NULL; -+ goto out; /* success */ -+ } else if (mtx) -+ mutex_lock(mtx); -+ } -+ -+ if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode))) -+ AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," -+ " b%d, %s, %pd, hi%lu, i%lu.\n", -+ bstart, au_sbtype(h_dentry->d_sb), dentry, -+ (unsigned long)h_ino, (unsigned long)ino); -+ ino = 0; -+ err = au_xino_write(sb, bstart, h_ino, /*ino*/0); -+ if (!err) { -+ iput(inode); -+ if (mtx) -+ mutex_unlock(mtx); -+ goto new_ino; -+ } -+ -+out_iput: -+ iput(inode); -+ inode = ERR_PTR(err); -+out: -+ if (mtx) -+ mutex_unlock(mtx); -+ return inode; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, -+ struct inode *inode) -+{ -+ int err; -+ struct inode *hi; -+ -+ err = au_br_rdonly(au_sbr(sb, bindex)); -+ -+ /* pseudo-link after flushed may happen out of bounds */ -+ if (!err -+ && inode -+ && au_ibstart(inode) <= bindex -+ && bindex <= au_ibend(inode)) { -+ /* -+ * permission check is unnecessary since vfsub routine -+ * will be called later -+ */ -+ hi = au_h_iptr(inode, bindex); -+ if (hi) -+ err = IS_IMMUTABLE(hi) ? -EROFS : 0; -+ } -+ -+ return err; -+} -+ -+int au_test_h_perm(struct inode *h_inode, int mask) -+{ -+ if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) -+ return 0; -+ return inode_permission(h_inode, mask); -+} -+ -+int au_test_h_perm_sio(struct inode *h_inode, int mask) -+{ -+ if (au_test_nfs(h_inode->i_sb) -+ && (mask & MAY_WRITE) -+ && S_ISDIR(h_inode->i_mode)) -+ mask |= MAY_READ; /* force permission check */ -+ return au_test_h_perm(h_inode, mask); -+} -diff -Nur linux-4.0.4.orig/fs/aufs/inode.h linux-4.0.4/fs/aufs/inode.h ---- linux-4.0.4.orig/fs/aufs/inode.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/inode.h 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,660 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode operations -+ */ -+ -+#ifndef __AUFS_INODE_H__ -+#define __AUFS_INODE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "rwsem.h" -+ -+struct vfsmount; -+ -+struct au_hnotify { -+#ifdef CONFIG_AUFS_HNOTIFY -+#ifdef CONFIG_AUFS_HFSNOTIFY -+ /* never use fsnotify_add_vfsmount_mark() */ -+ struct fsnotify_mark hn_mark; -+#endif -+ struct inode *hn_aufs_inode; /* no get/put */ -+#endif -+} ____cacheline_aligned_in_smp; -+ -+struct au_hinode { -+ struct inode *hi_inode; -+ aufs_bindex_t hi_id; -+#ifdef CONFIG_AUFS_HNOTIFY -+ struct au_hnotify *hi_notify; -+#endif -+ -+ /* reference to the copied-up whiteout with get/put */ -+ struct dentry *hi_whdentry; -+}; -+ -+/* ig_flags */ -+#define AuIG_HALF_REFRESHED 1 -+#define au_ig_ftest(flags, name) ((flags) & AuIG_##name) -+#define au_ig_fset(flags, name) \ -+ do { (flags) |= AuIG_##name; } while (0) -+#define au_ig_fclr(flags, name) \ -+ do { (flags) &= ~AuIG_##name; } while (0) -+ -+struct au_iigen { -+ __u32 ig_generation, ig_flags; -+}; -+ -+struct au_vdir; -+struct au_iinfo { -+ spinlock_t ii_genspin; -+ struct au_iigen ii_generation; -+ struct super_block *ii_hsb1; /* no get/put */ -+ -+ struct au_rwsem ii_rwsem; -+ aufs_bindex_t ii_bstart, ii_bend; -+ __u32 ii_higen; -+ struct au_hinode *ii_hinode; -+ struct au_vdir *ii_vdir; -+}; -+ -+struct au_icntnr { -+ struct au_iinfo iinfo; -+ struct inode vfs_inode; -+} ____cacheline_aligned_in_smp; -+ -+/* au_pin flags */ -+#define AuPin_DI_LOCKED 1 -+#define AuPin_MNT_WRITE (1 << 1) -+#define au_ftest_pin(flags, name) ((flags) & AuPin_##name) -+#define au_fset_pin(flags, name) \ -+ do { (flags) |= AuPin_##name; } while (0) -+#define au_fclr_pin(flags, name) \ -+ do { (flags) &= ~AuPin_##name; } while (0) -+ -+struct au_pin { -+ /* input */ -+ struct dentry *dentry; -+ unsigned int udba; -+ unsigned char lsc_di, lsc_hi, flags; -+ aufs_bindex_t bindex; -+ -+ /* output */ -+ struct dentry *parent; -+ struct au_hinode *hdir; -+ struct vfsmount *h_mnt; -+ -+ /* temporary unlock/relock for copyup */ -+ struct dentry *h_dentry, *h_parent; -+ struct au_branch *br; -+ struct task_struct *task; -+}; -+ -+void au_pin_hdir_unlock(struct au_pin *p); -+int au_pin_hdir_lock(struct au_pin *p); -+int au_pin_hdir_relock(struct au_pin *p); -+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task); -+void au_pin_hdir_acquire_nest(struct au_pin *p); -+void au_pin_hdir_release(struct au_pin *p); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_iinfo *au_ii(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ -+ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); -+ if (iinfo->ii_hinode) -+ return iinfo; -+ return NULL; /* debugging bad_inode case */ -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* inode.c */ -+struct inode *au_igrab(struct inode *inode); -+int au_refresh_hinode_self(struct inode *inode); -+int au_refresh_hinode(struct inode *inode, struct dentry *dentry); -+int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ unsigned int d_type, ino_t *ino); -+struct inode *au_new_inode(struct dentry *dentry, int must_new); -+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, -+ struct inode *inode); -+int au_test_h_perm(struct inode *h_inode, int mask); -+int au_test_h_perm_sio(struct inode *h_inode, int mask); -+ -+static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, -+ ino_t h_ino, unsigned int d_type, ino_t *ino) -+{ -+#ifdef CONFIG_AUFS_SHWH -+ return au_ino(sb, bindex, h_ino, d_type, ino); -+#else -+ return 0; -+#endif -+} -+ -+/* i_op.c */ -+extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop; -+ -+/* au_wr_dir flags */ -+#define AuWrDir_ADD_ENTRY 1 -+#define AuWrDir_ISDIR (1 << 1) -+#define AuWrDir_TMPFILE (1 << 2) -+#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) -+#define au_fset_wrdir(flags, name) \ -+ do { (flags) |= AuWrDir_##name; } while (0) -+#define au_fclr_wrdir(flags, name) \ -+ do { (flags) &= ~AuWrDir_##name; } while (0) -+ -+struct au_wr_dir_args { -+ aufs_bindex_t force_btgt; -+ unsigned char flags; -+}; -+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, -+ struct au_wr_dir_args *args); -+ -+struct dentry *au_pinned_h_parent(struct au_pin *pin); -+void au_pin_init(struct au_pin *pin, struct dentry *dentry, -+ aufs_bindex_t bindex, int lsc_di, int lsc_hi, -+ unsigned int udba, unsigned char flags); -+int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int udba, unsigned char flags) __must_check; -+int au_do_pin(struct au_pin *pin) __must_check; -+void au_unpin(struct au_pin *pin); -+int au_reval_for_attr(struct dentry *dentry, unsigned int sigen); -+ -+#define AuIcpup_DID_CPUP 1 -+#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name) -+#define au_fset_icpup(flags, name) \ -+ do { (flags) |= AuIcpup_##name; } while (0) -+#define au_fclr_icpup(flags, name) \ -+ do { (flags) &= ~AuIcpup_##name; } while (0) -+ -+struct au_icpup_args { -+ unsigned char flags; -+ unsigned char pin_flags; -+ aufs_bindex_t btgt; -+ unsigned int udba; -+ struct au_pin pin; -+ struct path h_path; -+ struct inode *h_inode; -+}; -+ -+int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, -+ struct au_icpup_args *a); -+ -+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path); -+ -+/* i_op_add.c */ -+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir); -+int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t dev); -+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); -+int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool want_excl); -+struct vfsub_aopen_args; -+int au_aopen_or_create(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args); -+int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode); -+int aufs_link(struct dentry *src_dentry, struct inode *dir, -+ struct dentry *dentry); -+int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); -+ -+/* i_op_del.c */ -+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup); -+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir); -+int aufs_unlink(struct inode *dir, struct dentry *dentry); -+int aufs_rmdir(struct inode *dir, struct dentry *dentry); -+ -+/* i_op_ren.c */ -+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt); -+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, -+ struct inode *dir, struct dentry *dentry); -+ -+/* iinfo.c */ -+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); -+void au_hiput(struct au_hinode *hinode); -+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_wh); -+unsigned int au_hi_flags(struct inode *inode, int isdir); -+ -+/* hinode flags */ -+#define AuHi_XINO 1 -+#define AuHi_HNOTIFY (1 << 1) -+#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) -+#define au_fset_hi(flags, name) \ -+ do { (flags) |= AuHi_##name; } while (0) -+#define au_fclr_hi(flags, name) \ -+ do { (flags) &= ~AuHi_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_HNOTIFY -+#undef AuHi_HNOTIFY -+#define AuHi_HNOTIFY 0 -+#endif -+ -+void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, -+ struct inode *h_inode, unsigned int flags); -+ -+void au_update_iigen(struct inode *inode, int half); -+void au_update_ibrange(struct inode *inode, int do_put_zero); -+ -+void au_icntnr_init_once(void *_c); -+int au_iinfo_init(struct inode *inode); -+void au_iinfo_fin(struct inode *inode); -+int au_ii_realloc(struct au_iinfo *iinfo, int nbr); -+ -+#ifdef CONFIG_PROC_FS -+/* plink.c */ -+int au_plink_maint(struct super_block *sb, int flags); -+struct au_sbinfo; -+void au_plink_maint_leave(struct au_sbinfo *sbinfo); -+int au_plink_maint_enter(struct super_block *sb); -+#ifdef CONFIG_AUFS_DEBUG -+void au_plink_list(struct super_block *sb); -+#else -+AuStubVoid(au_plink_list, struct super_block *sb) -+#endif -+int au_plink_test(struct inode *inode); -+struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex); -+void au_plink_append(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_dentry); -+void au_plink_put(struct super_block *sb, int verbose); -+void au_plink_clean(struct super_block *sb, int verbose); -+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); -+#else -+AuStubInt0(au_plink_maint, struct super_block *sb, int flags); -+AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo); -+AuStubInt0(au_plink_maint_enter, struct super_block *sb); -+AuStubVoid(au_plink_list, struct super_block *sb); -+AuStubInt0(au_plink_test, struct inode *inode); -+AuStub(struct dentry *, au_plink_lkup, return NULL, -+ struct inode *inode, aufs_bindex_t bindex); -+AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_dentry); -+AuStubVoid(au_plink_put, struct super_block *sb, int verbose); -+AuStubVoid(au_plink_clean, struct super_block *sb, int verbose); -+AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id); -+#endif /* CONFIG_PROC_FS */ -+ -+#ifdef CONFIG_AUFS_XATTR -+/* xattr.c */ -+int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, -+ unsigned int verbose); -+ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size); -+ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, -+ size_t size); -+int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, -+ size_t size, int flags); -+int aufs_removexattr(struct dentry *dentry, const char *name); -+ -+/* void au_xattr_init(struct super_block *sb); */ -+#else -+AuStubInt0(au_cpup_xattr, struct dentry *h_dst, struct dentry *h_src, -+ int ignore_flags, unsigned int verbose); -+/* AuStubVoid(au_xattr_init, struct super_block *sb); */ -+#endif -+ -+#ifdef CONFIG_FS_POSIX_ACL -+struct posix_acl *aufs_get_acl(struct inode *inode, int type); -+int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type); -+#endif -+ -+#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) -+enum { -+ AU_XATTR_SET, -+ AU_XATTR_REMOVE, -+ AU_ACL_SET -+}; -+ -+struct au_srxattr { -+ int type; -+ union { -+ struct { -+ const char *name; -+ const void *value; -+ size_t size; -+ int flags; -+ } set; -+ struct { -+ const char *name; -+ } remove; -+ struct { -+ struct posix_acl *acl; -+ int type; -+ } acl_set; -+ } u; -+}; -+ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock subclass for iinfo */ -+enum { -+ AuLsc_II_CHILD, /* child first */ -+ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */ -+ AuLsc_II_CHILD3, /* copyup dirs */ -+ AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */ -+ AuLsc_II_PARENT2, -+ AuLsc_II_PARENT3, /* copyup dirs */ -+ AuLsc_II_NEW_CHILD -+}; -+ -+/* -+ * ii_read_lock_child, ii_write_lock_child, -+ * ii_read_lock_child2, ii_write_lock_child2, -+ * ii_read_lock_child3, ii_write_lock_child3, -+ * ii_read_lock_parent, ii_write_lock_parent, -+ * ii_read_lock_parent2, ii_write_lock_parent2, -+ * ii_read_lock_parent3, ii_write_lock_parent3, -+ * ii_read_lock_new_child, ii_write_lock_new_child, -+ */ -+#define AuReadLockFunc(name, lsc) \ -+static inline void ii_read_lock_##name(struct inode *i) \ -+{ \ -+ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ -+} -+ -+#define AuWriteLockFunc(name, lsc) \ -+static inline void ii_write_lock_##name(struct inode *i) \ -+{ \ -+ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ -+} -+ -+#define AuRWLockFuncs(name, lsc) \ -+ AuReadLockFunc(name, lsc) \ -+ AuWriteLockFunc(name, lsc) -+ -+AuRWLockFuncs(child, CHILD); -+AuRWLockFuncs(child2, CHILD2); -+AuRWLockFuncs(child3, CHILD3); -+AuRWLockFuncs(parent, PARENT); -+AuRWLockFuncs(parent2, PARENT2); -+AuRWLockFuncs(parent3, PARENT3); -+AuRWLockFuncs(new_child, NEW_CHILD); -+ -+#undef AuReadLockFunc -+#undef AuWriteLockFunc -+#undef AuRWLockFuncs -+ -+/* -+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock -+ */ -+AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem); -+ -+#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) -+#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem) -+#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem) -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void au_icntnr_init(struct au_icntnr *c) -+{ -+#ifdef CONFIG_AUFS_DEBUG -+ c->vfs_inode.i_mode = 0; -+#endif -+} -+ -+static inline unsigned int au_iigen(struct inode *inode, struct au_iigen *iigen) -+{ -+ unsigned int gen; -+ struct au_iinfo *iinfo; -+ -+ iinfo = au_ii(inode); -+ spin_lock(&iinfo->ii_genspin); -+ if (iigen) -+ *iigen = iinfo->ii_generation; -+ gen = iinfo->ii_generation.ig_generation; -+ spin_unlock(&iinfo->ii_genspin); -+ -+ return gen; -+} -+ -+/* tiny test for inode number */ -+/* tmpfs generation is too rough */ -+static inline int au_test_higen(struct inode *inode, struct inode *h_inode) -+{ -+ struct au_iinfo *iinfo; -+ -+ iinfo = au_ii(inode); -+ AuRwMustAnyLock(&iinfo->ii_rwsem); -+ return !(iinfo->ii_hsb1 == h_inode->i_sb -+ && iinfo->ii_higen == h_inode->i_generation); -+} -+ -+static inline void au_iigen_dec(struct inode *inode) -+{ -+ struct au_iinfo *iinfo; -+ -+ iinfo = au_ii(inode); -+ spin_lock(&iinfo->ii_genspin); -+ iinfo->ii_generation.ig_generation--; -+ spin_unlock(&iinfo->ii_genspin); -+} -+ -+static inline int au_iigen_test(struct inode *inode, unsigned int sigen) -+{ -+ int err; -+ -+ err = 0; -+ if (unlikely(inode && au_iigen(inode, NULL) != sigen)) -+ err = -EIO; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline aufs_bindex_t au_ii_br_id(struct inode *inode, -+ aufs_bindex_t bindex) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode[0 + bindex].hi_id; -+} -+ -+static inline aufs_bindex_t au_ibstart(struct inode *inode) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_bstart; -+} -+ -+static inline aufs_bindex_t au_ibend(struct inode *inode) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_bend; -+} -+ -+static inline struct au_vdir *au_ivdir(struct inode *inode) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_vdir; -+} -+ -+static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry; -+} -+ -+static inline void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustWriteLock(inode); -+ au_ii(inode)->ii_bstart = bindex; -+} -+ -+static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustWriteLock(inode); -+ au_ii(inode)->ii_bend = bindex; -+} -+ -+static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) -+{ -+ IiMustWriteLock(inode); -+ au_ii(inode)->ii_vdir = vdir; -+} -+ -+static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) -+{ -+ IiMustAnyLock(inode); -+ return au_ii(inode)->ii_hinode + bindex; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct dentry *au_pinned_parent(struct au_pin *pin) -+{ -+ if (pin) -+ return pin->parent; -+ return NULL; -+} -+ -+static inline struct inode *au_pinned_h_dir(struct au_pin *pin) -+{ -+ if (pin && pin->hdir) -+ return pin->hdir->hi_inode; -+ return NULL; -+} -+ -+static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin) -+{ -+ if (pin) -+ return pin->hdir; -+ return NULL; -+} -+ -+static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry) -+{ -+ if (pin) -+ pin->dentry = dentry; -+} -+ -+static inline void au_pin_set_parent_lflag(struct au_pin *pin, -+ unsigned char lflag) -+{ -+ if (pin) { -+ if (lflag) -+ au_fset_pin(pin->flags, DI_LOCKED); -+ else -+ au_fclr_pin(pin->flags, DI_LOCKED); -+ } -+} -+ -+#if 0 /* reserved */ -+static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) -+{ -+ if (pin) { -+ dput(pin->parent); -+ pin->parent = dget(parent); -+ } -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_branch; -+#ifdef CONFIG_AUFS_HNOTIFY -+struct au_hnotify_op { -+ void (*ctl)(struct au_hinode *hinode, int do_set); -+ int (*alloc)(struct au_hinode *hinode); -+ -+ /* -+ * if it returns true, the the caller should free hinode->hi_notify, -+ * otherwise ->free() frees it. -+ */ -+ int (*free)(struct au_hinode *hinode, -+ struct au_hnotify *hn) __must_check; -+ -+ void (*fin)(void); -+ int (*init)(void); -+ -+ int (*reset_br)(unsigned int udba, struct au_branch *br, int perm); -+ void (*fin_br)(struct au_branch *br); -+ int (*init_br)(struct au_branch *br, int perm); -+}; -+ -+/* hnotify.c */ -+int au_hn_alloc(struct au_hinode *hinode, struct inode *inode); -+void au_hn_free(struct au_hinode *hinode); -+void au_hn_ctl(struct au_hinode *hinode, int do_set); -+void au_hn_reset(struct inode *inode, unsigned int flags); -+int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, -+ struct qstr *h_child_qstr, struct inode *h_child_inode); -+int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm); -+int au_hnotify_init_br(struct au_branch *br, int perm); -+void au_hnotify_fin_br(struct au_branch *br); -+int __init au_hnotify_init(void); -+void au_hnotify_fin(void); -+ -+/* hfsnotify.c */ -+extern const struct au_hnotify_op au_hnotify_op; -+ -+static inline -+void au_hn_init(struct au_hinode *hinode) -+{ -+ hinode->hi_notify = NULL; -+} -+ -+static inline struct au_hnotify *au_hn(struct au_hinode *hinode) -+{ -+ return hinode->hi_notify; -+} -+ -+#else -+AuStub(int, au_hn_alloc, return -EOPNOTSUPP, -+ struct au_hinode *hinode __maybe_unused, -+ struct inode *inode __maybe_unused) -+AuStub(struct au_hnotify *, au_hn, return NULL, struct au_hinode *hinode) -+AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused) -+AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused, -+ int do_set __maybe_unused) -+AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused, -+ unsigned int flags __maybe_unused) -+AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused, -+ struct au_branch *br __maybe_unused, -+ int perm __maybe_unused) -+AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused, -+ int perm __maybe_unused) -+AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused) -+AuStubInt0(__init au_hnotify_init, void) -+AuStubVoid(au_hnotify_fin, void) -+AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused) -+#endif /* CONFIG_AUFS_HNOTIFY */ -+ -+static inline void au_hn_suspend(struct au_hinode *hdir) -+{ -+ au_hn_ctl(hdir, /*do_set*/0); -+} -+ -+static inline void au_hn_resume(struct au_hinode *hdir) -+{ -+ au_hn_ctl(hdir, /*do_set*/1); -+} -+ -+static inline void au_hn_imtx_lock(struct au_hinode *hdir) -+{ -+ mutex_lock(&hdir->hi_inode->i_mutex); -+ au_hn_suspend(hdir); -+} -+ -+static inline void au_hn_imtx_lock_nested(struct au_hinode *hdir, -+ unsigned int sc __maybe_unused) -+{ -+ mutex_lock_nested(&hdir->hi_inode->i_mutex, sc); -+ au_hn_suspend(hdir); -+} -+ -+static inline void au_hn_imtx_unlock(struct au_hinode *hdir) -+{ -+ au_hn_resume(hdir); -+ mutex_unlock(&hdir->hi_inode->i_mutex); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_INODE_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/ioctl.c linux-4.0.4/fs/aufs/ioctl.c ---- linux-4.0.4.orig/fs/aufs/ioctl.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/ioctl.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,206 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * ioctl -+ * plink-management and readdir in userspace. -+ * assist the pathconf(3) wrapper library. -+ * move-down -+ * File-based Hierarchical Storage Management. -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) -+{ -+ int err, fd; -+ aufs_bindex_t wbi, bindex, bend; -+ struct file *h_file; -+ struct super_block *sb; -+ struct dentry *root; -+ struct au_branch *br; -+ struct aufs_wbr_fd wbrfd = { -+ .oflags = au_dir_roflags, -+ .brid = -1 -+ }; -+ const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY -+ | O_NOATIME | O_CLOEXEC; -+ -+ AuDebugOn(wbrfd.oflags & ~valid); -+ -+ if (arg) { -+ err = copy_from_user(&wbrfd, arg, sizeof(wbrfd)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ goto out; -+ } -+ -+ err = -EINVAL; -+ AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid); -+ wbrfd.oflags |= au_dir_roflags; -+ AuDbg("0%o\n", wbrfd.oflags); -+ if (unlikely(wbrfd.oflags & ~valid)) -+ goto out; -+ } -+ -+ fd = get_unused_fd_flags(0); -+ err = fd; -+ if (unlikely(fd < 0)) -+ goto out; -+ -+ h_file = ERR_PTR(-EINVAL); -+ wbi = 0; -+ br = NULL; -+ sb = path->dentry->d_sb; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_IR); -+ bend = au_sbend(sb); -+ if (wbrfd.brid >= 0) { -+ wbi = au_br_index(sb, wbrfd.brid); -+ if (unlikely(wbi < 0 || wbi > bend)) -+ goto out_unlock; -+ } -+ -+ h_file = ERR_PTR(-ENOENT); -+ br = au_sbr(sb, wbi); -+ if (!au_br_writable(br->br_perm)) { -+ if (arg) -+ goto out_unlock; -+ -+ bindex = wbi + 1; -+ wbi = -1; -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_writable(br->br_perm)) { -+ wbi = bindex; -+ br = au_sbr(sb, wbi); -+ break; -+ } -+ } -+ } -+ AuDbg("wbi %d\n", wbi); -+ if (wbi >= 0) -+ h_file = au_h_open(root, wbi, wbrfd.oflags, NULL, -+ /*force_wr*/0); -+ -+out_unlock: -+ aufs_read_unlock(root, AuLock_IR); -+ err = PTR_ERR(h_file); -+ if (IS_ERR(h_file)) -+ goto out_fd; -+ -+ atomic_dec(&br->br_count); /* cf. au_h_open() */ -+ fd_install(fd, h_file); -+ err = fd; -+ goto out; /* success */ -+ -+out_fd: -+ put_unused_fd(fd); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err; -+ struct dentry *dentry; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_ioctl(file, cmd, arg); -+ break; -+ -+ case AUFS_CTL_WBR_FD: -+ err = au_wbr_fd(&file->f_path, (void __user *)arg); -+ break; -+ -+ case AUFS_CTL_IBUSY: -+ err = au_ibusy_ioctl(file, arg); -+ break; -+ -+ case AUFS_CTL_BRINFO: -+ err = au_brinfo_ioctl(file, arg); -+ break; -+ -+ case AUFS_CTL_FHSM_FD: -+ dentry = file->f_path.dentry; -+ if (IS_ROOT(dentry)) -+ err = au_fhsm_fd(dentry->d_sb, arg); -+ else -+ err = -ENOTTY; -+ break; -+ -+ default: -+ /* do not call the lower */ -+ AuDbg("0x%x\n", cmd); -+ err = -ENOTTY; -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err; -+ -+ switch (cmd) { -+ case AUFS_CTL_MVDOWN: -+ err = au_mvdown(file->f_path.dentry, (void __user *)arg); -+ break; -+ -+ case AUFS_CTL_WBR_FD: -+ err = au_wbr_fd(&file->f_path, (void __user *)arg); -+ break; -+ -+ default: -+ /* do not call the lower */ -+ AuDbg("0x%x\n", cmd); -+ err = -ENOTTY; -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+#ifdef CONFIG_COMPAT -+long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ long err; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_compat_ioctl(file, cmd, arg); -+ break; -+ -+ case AUFS_CTL_IBUSY: -+ err = au_ibusy_compat_ioctl(file, arg); -+ break; -+ -+ case AUFS_CTL_BRINFO: -+ err = au_brinfo_compat_ioctl(file, arg); -+ break; -+ -+ default: -+ err = aufs_ioctl_dir(file, cmd, arg); -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); -+} -+#endif -diff -Nur linux-4.0.4.orig/fs/aufs/i_op_add.c linux-4.0.4/fs/aufs/i_op_add.c ---- linux-4.0.4.orig/fs/aufs/i_op_add.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/i_op_add.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,917 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode operations (add entry) -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * final procedure of adding a new entry, except link(2). -+ * remove whiteout, instantiate, copyup the parent dir's times and size -+ * and update version. -+ * if it failed, re-create the removed whiteout. -+ */ -+static int epilog(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct dentry *dentry) -+{ -+ int err, rerr; -+ aufs_bindex_t bwh; -+ struct path h_path; -+ struct super_block *sb; -+ struct inode *inode, *h_dir; -+ struct dentry *wh; -+ -+ bwh = -1; -+ sb = dir->i_sb; -+ if (wh_dentry) { -+ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ -+ IMustLock(h_dir); -+ AuDebugOn(au_h_iptr(dir, bindex) != h_dir); -+ bwh = au_dbwh(dentry); -+ h_path.dentry = wh_dentry; -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, -+ dentry); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+ inode = au_new_inode(dentry, /*must_new*/1); -+ if (!IS_ERR(inode)) { -+ d_instantiate(dentry, inode); -+ dir = dentry->d_parent->d_inode; /* dir inode is locked */ -+ IMustLock(dir); -+ au_dir_ts(dir, bindex); -+ dir->i_version++; -+ au_fhsm_wrote(sb, bindex, /*force*/0); -+ return 0; /* success */ -+ } -+ -+ err = PTR_ERR(inode); -+ if (!wh_dentry) -+ goto out; -+ -+ /* revert */ -+ /* dir inode is locked */ -+ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); -+ rerr = PTR_ERR(wh); -+ if (IS_ERR(wh)) { -+ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } else -+ dput(wh); -+ -+out: -+ return err; -+} -+ -+static int au_d_may_add(struct dentry *dentry) -+{ -+ int err; -+ -+ err = 0; -+ if (unlikely(d_unhashed(dentry))) -+ err = -ENOENT; -+ if (unlikely(dentry->d_inode)) -+ err = -EEXIST; -+ return err; -+} -+ -+/* -+ * simple tests for the adding inode operations. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir) -+{ -+ int err; -+ umode_t h_mode; -+ struct dentry *h_dentry; -+ struct inode *h_inode; -+ -+ err = -ENAMETOOLONG; -+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ goto out; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ h_inode = h_dentry->d_inode; -+ if (!dentry->d_inode) { -+ err = -EEXIST; -+ if (unlikely(h_inode)) -+ goto out; -+ } else { -+ /* rename(2) case */ -+ err = -EIO; -+ if (unlikely(!h_inode || !h_inode->i_nlink)) -+ goto out; -+ -+ h_mode = h_inode->i_mode; -+ if (!isdir) { -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(h_mode))) -+ goto out; -+ } else if (unlikely(!S_ISDIR(h_mode))) { -+ err = -ENOTDIR; -+ goto out; -+ } -+ } -+ -+ err = 0; -+ /* expected parent dir is locked */ -+ if (unlikely(h_parent != h_dentry->d_parent)) -+ err = -EIO; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * initial procedure of adding a new entry. -+ * prepare writable branch and the parent dir, lock it, -+ * and lookup whiteout for the new entry. -+ */ -+static struct dentry* -+lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, -+ struct dentry *src_dentry, struct au_pin *pin, -+ struct au_wr_dir_args *wr_dir_args) -+{ -+ struct dentry *wh_dentry, *h_parent; -+ struct super_block *sb; -+ struct au_branch *br; -+ int err; -+ unsigned int udba; -+ aufs_bindex_t bcpup; -+ -+ AuDbg("%pd\n", dentry); -+ -+ err = au_wr_dir(dentry, src_dentry, wr_dir_args); -+ bcpup = err; -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ udba = au_opt_udba(sb); -+ err = au_pin(pin, dentry, bcpup, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ h_parent = au_pinned_h_parent(pin); -+ if (udba != AuOpt_UDBA_NONE -+ && au_dbstart(dentry) == bcpup) -+ err = au_may_add(dentry, bcpup, h_parent, -+ au_ftest_wrdir(wr_dir_args->flags, ISDIR)); -+ else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ err = -ENAMETOOLONG; -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ br = au_sbr(sb, bcpup); -+ if (dt) { -+ struct path tmp = { -+ .dentry = h_parent, -+ .mnt = au_br_mnt(br) -+ }; -+ au_dtime_store(dt, au_pinned_parent(pin), &tmp); -+ } -+ -+ wh_dentry = NULL; -+ if (bcpup != au_dbwh(dentry)) -+ goto out; /* success */ -+ -+ /* -+ * ENAMETOOLONG here means that if we allowed create such name, then it -+ * would not be able to removed in the future. So we don't allow such -+ * name here and we don't handle ENAMETOOLONG differently here. -+ */ -+ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); -+ -+out_unpin: -+ if (IS_ERR(wh_dentry)) -+ au_unpin(pin); -+out: -+ return wh_dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+enum { Mknod, Symlink, Creat }; -+struct simple_arg { -+ int type; -+ union { -+ struct { -+ umode_t mode; -+ bool want_excl; -+ bool try_aopen; -+ struct vfsub_aopen_args *aopen; -+ } c; -+ struct { -+ const char *symname; -+ } s; -+ struct { -+ umode_t mode; -+ dev_t dev; -+ } m; -+ } u; -+}; -+ -+static int add_simple(struct inode *dir, struct dentry *dentry, -+ struct simple_arg *arg) -+{ -+ int err, rerr; -+ aufs_bindex_t bstart; -+ unsigned char created; -+ const unsigned char try_aopen -+ = (arg->type == Creat && arg->u.c.try_aopen); -+ struct dentry *wh_dentry, *parent; -+ struct inode *h_dir; -+ struct super_block *sb; -+ struct au_branch *br; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ struct path h_path; -+ struct au_wr_dir_args wr_dir_args; -+ } *a; -+ -+ AuDbg("%pd\n", dentry); -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ a->wr_dir_args.force_btgt = -1; -+ a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ if (!try_aopen) { -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ } -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ if (!try_aopen) -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, -+ &a->pin, &a->wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ bstart = au_dbstart(dentry); -+ sb = dentry->d_sb; -+ br = au_sbr(sb, bstart); -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ a->h_path.mnt = au_br_mnt(br); -+ h_dir = au_pinned_h_dir(&a->pin); -+ switch (arg->type) { -+ case Creat: -+ err = 0; -+ if (!try_aopen || !h_dir->i_op->atomic_open) -+ err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, -+ arg->u.c.want_excl); -+ else -+ err = vfsub_atomic_open(h_dir, a->h_path.dentry, -+ arg->u.c.aopen, br); -+ break; -+ case Symlink: -+ err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); -+ break; -+ case Mknod: -+ err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, -+ arg->u.m.dev); -+ break; -+ default: -+ BUG(); -+ } -+ created = !err; -+ if (!err) -+ err = epilog(dir, bstart, wh_dentry, dentry); -+ -+ /* revert */ -+ if (unlikely(created && err && a->h_path.dentry->d_inode)) { -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, -+ /*force*/0); -+ if (rerr) { -+ AuIOErr("%pd revert failure(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&a->dt); -+ } -+ -+ if (!err && try_aopen && !h_dir->i_op->atomic_open) -+ *arg->u.c.aopen->opened |= FILE_CREATED; -+ -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ -+out_parent: -+ if (!try_aopen) -+ di_write_unlock(parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ if (!try_aopen) -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -+ -+int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, -+ dev_t dev) -+{ -+ struct simple_arg arg = { -+ .type = Mknod, -+ .u.m = { -+ .mode = mode, -+ .dev = dev -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) -+{ -+ struct simple_arg arg = { -+ .type = Symlink, -+ .u.s.symname = symname -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, -+ bool want_excl) -+{ -+ struct simple_arg arg = { -+ .type = Creat, -+ .u.c = { -+ .mode = mode, -+ .want_excl = want_excl -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int au_aopen_or_create(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *aopen_args) -+{ -+ struct simple_arg arg = { -+ .type = Creat, -+ .u.c = { -+ .mode = aopen_args->create_mode, -+ .want_excl = aopen_args->open_flag & O_EXCL, -+ .try_aopen = true, -+ .aopen = aopen_args -+ } -+ }; -+ return add_simple(dir, dentry, &arg); -+} -+ -+int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct super_block *sb; -+ struct dentry *parent, *h_parent, *h_dentry; -+ struct inode *h_dir, *inode; -+ struct vfsmount *h_mnt; -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = AuWrDir_TMPFILE -+ }; -+ -+ /* copy-up may happen */ -+ mutex_lock(&dir->i_mutex); -+ -+ sb = dir->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_di_init(dentry); -+ if (unlikely(err)) -+ goto out_si; -+ -+ err = -EBUSY; -+ parent = d_find_any_alias(dir); -+ AuDebugOn(!parent); -+ di_write_lock_parent(parent); -+ if (unlikely(parent->d_inode != dir)) -+ goto out_parent; -+ -+ err = au_digen_test(parent, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ bindex = au_dbstart(parent); -+ au_set_dbstart(dentry, bindex); -+ au_set_dbend(dentry, bindex); -+ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); -+ bindex = err; -+ if (unlikely(err < 0)) -+ goto out_parent; -+ -+ err = -EOPNOTSUPP; -+ h_dir = au_h_iptr(dir, bindex); -+ if (unlikely(!h_dir->i_op->tmpfile)) -+ goto out_parent; -+ -+ h_mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_mnt_want_write(h_mnt); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ h_parent = au_h_dptr(parent, bindex); -+ err = inode_permission(h_parent->d_inode, MAY_WRITE | MAY_EXEC); -+ if (unlikely(err)) -+ goto out_mnt; -+ -+ err = -ENOMEM; -+ h_dentry = d_alloc(h_parent, &dentry->d_name); -+ if (unlikely(!h_dentry)) -+ goto out_mnt; -+ -+ err = h_dir->i_op->tmpfile(h_dir, h_dentry, mode); -+ if (unlikely(err)) -+ goto out_dentry; -+ -+ au_set_dbstart(dentry, bindex); -+ au_set_dbend(dentry, bindex); -+ au_set_h_dptr(dentry, bindex, dget(h_dentry)); -+ inode = au_new_inode(dentry, /*must_new*/1); -+ if (IS_ERR(inode)) { -+ err = PTR_ERR(inode); -+ au_set_h_dptr(dentry, bindex, NULL); -+ au_set_dbstart(dentry, -1); -+ au_set_dbend(dentry, -1); -+ } else { -+ if (!inode->i_nlink) -+ set_nlink(inode, 1); -+ d_tmpfile(dentry, inode); -+ au_di(dentry)->di_tmpfile = 1; -+ -+ /* update without i_mutex */ -+ if (au_ibstart(dir) == au_dbstart(dentry)) -+ au_cpup_attr_timesizes(dir); -+ } -+ -+out_dentry: -+ dput(h_dentry); -+out_mnt: -+ vfsub_mnt_drop_write(h_mnt); -+out_parent: -+ di_write_unlock(parent); -+ dput(parent); -+ di_write_unlock(dentry); -+ if (!err) -+#if 0 -+ /* verbose coding for lock class name */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+#else -+ ; -+#endif -+ else { -+ au_di_fin(dentry); -+ dentry->d_fsdata = NULL; -+ } -+out_si: -+ si_read_unlock(sb); -+out: -+ mutex_unlock(&dir->i_mutex); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_link_args { -+ aufs_bindex_t bdst, bsrc; -+ struct au_pin pin; -+ struct path h_path; -+ struct dentry *src_parent, *parent; -+}; -+ -+static int au_cpup_before_link(struct dentry *src_dentry, -+ struct au_link_args *a) -+{ -+ int err; -+ struct dentry *h_src_dentry; -+ struct au_cp_generic cpg = { -+ .dentry = src_dentry, -+ .bdst = a->bdst, -+ .bsrc = a->bsrc, -+ .len = -1, -+ .pin = &a->pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ -+ }; -+ -+ di_read_lock_parent(a->src_parent, AuLock_IR); -+ err = au_test_and_cpup_dirs(src_dentry, a->bdst); -+ if (unlikely(err)) -+ goto out; -+ -+ h_src_dentry = au_h_dptr(src_dentry, a->bsrc); -+ err = au_pin(&a->pin, src_dentry, a->bdst, -+ au_opt_udba(src_dentry->d_sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&a->pin); -+ -+out: -+ di_read_unlock(a->src_parent, AuLock_IR); -+ return err; -+} -+ -+static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, -+ struct au_link_args *a) -+{ -+ int err; -+ unsigned char plink; -+ aufs_bindex_t bend; -+ struct dentry *h_src_dentry; -+ struct inode *h_inode, *inode, *delegated; -+ struct super_block *sb; -+ struct file *h_file; -+ -+ plink = 0; -+ h_inode = NULL; -+ sb = src_dentry->d_sb; -+ inode = src_dentry->d_inode; -+ if (au_ibstart(inode) <= a->bdst) -+ h_inode = au_h_iptr(inode, a->bdst); -+ if (!h_inode || !h_inode->i_nlink) { -+ /* copyup src_dentry as the name of dentry. */ -+ bend = au_dbend(dentry); -+ if (bend < a->bsrc) -+ au_set_dbend(dentry, a->bsrc); -+ au_set_h_dptr(dentry, a->bsrc, -+ dget(au_h_dptr(src_dentry, a->bsrc))); -+ dget(a->h_path.dentry); -+ au_set_h_dptr(dentry, a->bdst, NULL); -+ AuDbg("temporary d_inode...\n"); -+ spin_lock(&dentry->d_lock); -+ dentry->d_inode = src_dentry->d_inode; /* tmp */ -+ spin_unlock(&dentry->d_lock); -+ h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0); -+ if (IS_ERR(h_file)) -+ err = PTR_ERR(h_file); -+ else { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->bdst, -+ .bsrc = -1, -+ .len = -1, -+ .pin = &a->pin, -+ .flags = AuCpup_KEEPLINO -+ }; -+ err = au_sio_cpup_simple(&cpg); -+ au_h_open_post(dentry, a->bsrc, h_file); -+ if (!err) { -+ dput(a->h_path.dentry); -+ a->h_path.dentry = au_h_dptr(dentry, a->bdst); -+ } else -+ au_set_h_dptr(dentry, a->bdst, -+ a->h_path.dentry); -+ } -+ spin_lock(&dentry->d_lock); -+ dentry->d_inode = NULL; /* restore */ -+ spin_unlock(&dentry->d_lock); -+ AuDbg("temporary d_inode...done\n"); -+ au_set_h_dptr(dentry, a->bsrc, NULL); -+ au_set_dbend(dentry, bend); -+ } else { -+ /* the inode of src_dentry already exists on a.bdst branch */ -+ h_src_dentry = d_find_alias(h_inode); -+ if (!h_src_dentry && au_plink_test(inode)) { -+ plink = 1; -+ h_src_dentry = au_plink_lkup(inode, a->bdst); -+ err = PTR_ERR(h_src_dentry); -+ if (IS_ERR(h_src_dentry)) -+ goto out; -+ -+ if (unlikely(!h_src_dentry->d_inode)) { -+ dput(h_src_dentry); -+ h_src_dentry = NULL; -+ } -+ -+ } -+ if (h_src_dentry) { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ dput(h_src_dentry); -+ } else { -+ AuIOErr("no dentry found for hi%lu on b%d\n", -+ h_inode->i_ino, a->bdst); -+ err = -EIO; -+ } -+ } -+ -+ if (!err && !plink) -+ au_plink_append(inode, a->bdst, a->h_path.dentry); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int aufs_link(struct dentry *src_dentry, struct inode *dir, -+ struct dentry *dentry) -+{ -+ int err, rerr; -+ struct au_dtime dt; -+ struct au_link_args *a; -+ struct dentry *wh_dentry, *h_src_dentry; -+ struct inode *inode, *delegated; -+ struct super_block *sb; -+ struct au_wr_dir_args wr_dir_args = { -+ /* .force_btgt = -1, */ -+ .flags = AuWrDir_ADD_ENTRY -+ }; -+ -+ IMustLock(dir); -+ inode = src_dentry->d_inode; -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ a->parent = dentry->d_parent; /* dir inode is locked */ -+ err = aufs_read_and_write_lock2(dentry, src_dentry, -+ AuLock_NOPLM | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_kfree; -+ err = au_d_linkable(src_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ a->src_parent = dget_parent(src_dentry); -+ wr_dir_args.force_btgt = au_ibstart(inode); -+ -+ di_write_lock_parent(a->parent); -+ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, -+ &wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ a->bdst = au_dbstart(dentry); -+ a->h_path.dentry = au_h_dptr(dentry, a->bdst); -+ a->h_path.mnt = au_sbr_mnt(sb, a->bdst); -+ a->bsrc = au_ibstart(inode); -+ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); -+ if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) -+ h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); -+ if (!h_src_dentry) { -+ a->bsrc = au_dbstart(src_dentry); -+ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); -+ AuDebugOn(!h_src_dentry); -+ } else if (IS_ERR(h_src_dentry)) { -+ err = PTR_ERR(h_src_dentry); -+ goto out_parent; -+ } -+ -+ if (au_opt_test(au_mntflags(sb), PLINK)) { -+ if (a->bdst < a->bsrc -+ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) -+ err = au_cpup_or_link(src_dentry, dentry, a); -+ else { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ dput(h_src_dentry); -+ } else { -+ /* -+ * copyup src_dentry to the branch we process, -+ * and then link(2) to it. -+ */ -+ dput(h_src_dentry); -+ if (a->bdst < a->bsrc -+ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { -+ au_unpin(&a->pin); -+ di_write_unlock(a->parent); -+ err = au_cpup_before_link(src_dentry, a); -+ di_write_lock_parent(a->parent); -+ if (!err) -+ err = au_pin(&a->pin, dentry, a->bdst, -+ au_opt_udba(sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (unlikely(err)) -+ goto out_wh; -+ } -+ if (!err) { -+ h_src_dentry = au_h_dptr(src_dentry, a->bdst); -+ err = -ENOENT; -+ if (h_src_dentry && h_src_dentry->d_inode) { -+ delegated = NULL; -+ err = vfsub_link(h_src_dentry, -+ au_pinned_h_dir(&a->pin), -+ &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry" -+ " for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ } -+ } -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ if (wh_dentry) { -+ a->h_path.dentry = wh_dentry; -+ err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, -+ dentry); -+ if (unlikely(err)) -+ goto out_revert; -+ } -+ -+ au_dir_ts(dir, a->bdst); -+ dir->i_version++; -+ inc_nlink(inode); -+ inode->i_ctime = dir->i_ctime; -+ d_instantiate(dentry, au_igrab(inode)); -+ if (d_unhashed(a->h_path.dentry)) -+ /* some filesystem calls d_drop() */ -+ d_drop(dentry); -+ /* some filesystems consume an inode even hardlink */ -+ au_fhsm_wrote(sb, a->bdst, /*force*/0); -+ goto out_unpin; /* success */ -+ -+out_revert: -+ /* no delegation since it is just created */ -+ rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, -+ /*delegated*/NULL, /*force*/0); -+ if (unlikely(rerr)) { -+ AuIOErr("%pd reverting failed(%d, %d)\n", dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&dt); -+out_unpin: -+ au_unpin(&a->pin); -+out_wh: -+ dput(wh_dentry); -+out_parent: -+ di_write_unlock(a->parent); -+ dput(a->src_parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ aufs_read_and_write_unlock2(dentry, src_dentry); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -+{ -+ int err, rerr; -+ aufs_bindex_t bindex; -+ unsigned char diropq; -+ struct path h_path; -+ struct dentry *wh_dentry, *parent, *opq_dentry; -+ struct mutex *h_mtx; -+ struct super_block *sb; -+ struct { -+ struct au_pin pin; -+ struct au_dtime dt; -+ } *a; /* reduce the stack usage */ -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR -+ }; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_d_may_add(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, -+ &a->pin, &wr_dir_args); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ sb = dentry->d_sb; -+ bindex = au_dbstart(dentry); -+ h_path.dentry = au_h_dptr(dentry, bindex); -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); -+ if (unlikely(err)) -+ goto out_unpin; -+ -+ /* make the dir opaque */ -+ diropq = 0; -+ h_mtx = &h_path.dentry->d_inode->i_mutex; -+ if (wh_dentry -+ || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ opq_dentry = au_diropq_create(dentry, bindex); -+ mutex_unlock(h_mtx); -+ err = PTR_ERR(opq_dentry); -+ if (IS_ERR(opq_dentry)) -+ goto out_dir; -+ dput(opq_dentry); -+ diropq = 1; -+ } -+ -+ err = epilog(dir, bindex, wh_dentry, dentry); -+ if (!err) { -+ inc_nlink(dir); -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ if (diropq) { -+ AuLabel(revert opq); -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(dentry, bindex); -+ mutex_unlock(h_mtx); -+ if (rerr) { -+ AuIOErr("%pd reverting diropq failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ } -+ -+out_dir: -+ AuLabel(revert dir); -+ rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); -+ if (rerr) { -+ AuIOErr("%pd reverting dir failed(%d, %d)\n", -+ dentry, err, rerr); -+ err = -EIO; -+ } -+ au_dtime_revert(&a->dt); -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+out_parent: -+ di_write_unlock(parent); -+out_unlock: -+ if (unlikely(err)) { -+ au_update_dbstart(dentry); -+ d_drop(dentry); -+ } -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/i_op.c linux-4.0.4/fs/aufs/i_op.c ---- linux-4.0.4.orig/fs/aufs/i_op.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/i_op.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,1432 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode operations (except add/del/rename) -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+static int h_permission(struct inode *h_inode, int mask, -+ struct vfsmount *h_mnt, int brperm) -+{ -+ int err; -+ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); -+ -+ err = -EACCES; -+ if ((write_mask && IS_IMMUTABLE(h_inode)) -+ || ((mask & MAY_EXEC) -+ && S_ISREG(h_inode->i_mode) -+ && ((h_mnt->mnt_flags & MNT_NOEXEC) -+ || !(h_inode->i_mode & S_IXUGO)))) -+ goto out; -+ -+ /* -+ * - skip the lower fs test in the case of write to ro branch. -+ * - nfs dir permission write check is optimized, but a policy for -+ * link/rename requires a real check. -+ * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' -+ * in this case, generic_permission() returns -EOPNOTSUPP. -+ */ -+ if ((write_mask && !au_br_writable(brperm)) -+ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) -+ && write_mask && !(mask & MAY_READ)) -+ || !h_inode->i_op->permission) { -+ /* AuLabel(generic_permission); */ -+ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ -+ err = generic_permission(h_inode, mask); -+ if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) -+ err = h_inode->i_op->permission(h_inode, mask); -+ AuTraceErr(err); -+ } else { -+ /* AuLabel(h_inode->permission); */ -+ err = h_inode->i_op->permission(h_inode, mask); -+ AuTraceErr(err); -+ } -+ -+ if (!err) -+ err = devcgroup_inode_permission(h_inode, mask); -+ if (!err) -+ err = security_inode_permission(h_inode, mask); -+ -+#if 0 -+ if (!err) { -+ /* todo: do we need to call ima_path_check()? */ -+ struct path h_path = { -+ .dentry = -+ .mnt = h_mnt -+ }; -+ err = ima_path_check(&h_path, -+ mask & (MAY_READ | MAY_WRITE | MAY_EXEC), -+ IMA_COUNT_LEAVE); -+ } -+#endif -+ -+out: -+ return err; -+} -+ -+static int aufs_permission(struct inode *inode, int mask) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ const unsigned char isdir = !!S_ISDIR(inode->i_mode), -+ write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); -+ struct inode *h_inode; -+ struct super_block *sb; -+ struct au_branch *br; -+ -+ /* todo: support rcu-walk? */ -+ if (mask & MAY_NOT_BLOCK) -+ return -ECHILD; -+ -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_read_lock_child(inode); -+#if 0 -+ err = au_iigen_test(inode, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out; -+#endif -+ -+ if (!isdir -+ || write_mask -+ || au_opt_test(au_mntflags(sb), DIRPERM1)) { -+ err = au_busy_or_stale(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ if (unlikely(!h_inode -+ || (h_inode->i_mode & S_IFMT) -+ != (inode->i_mode & S_IFMT))) -+ goto out; -+ -+ err = 0; -+ bindex = au_ibstart(inode); -+ br = au_sbr(sb, bindex); -+ err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); -+ if (write_mask -+ && !err -+ && !special_file(h_inode->i_mode)) { -+ /* test whether the upper writable branch exists */ -+ err = -EROFS; -+ for (; bindex >= 0; bindex--) -+ if (!au_br_rdonly(au_sbr(sb, bindex))) { -+ err = 0; -+ break; -+ } -+ } -+ goto out; -+ } -+ -+ /* non-write to dir */ -+ err = 0; -+ bend = au_ibend(inode); -+ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { -+ h_inode = au_h_iptr(inode, bindex); -+ if (h_inode) { -+ err = au_busy_or_stale(); -+ if (unlikely(!S_ISDIR(h_inode->i_mode))) -+ break; -+ -+ br = au_sbr(sb, bindex); -+ err = h_permission(h_inode, mask, au_br_mnt(br), -+ br->br_perm); -+ } -+ } -+ -+out: -+ ii_read_unlock(inode); -+ si_read_unlock(sb); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, -+ unsigned int flags) -+{ -+ struct dentry *ret, *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ int err, npositive; -+ -+ IMustLock(dir); -+ -+ /* todo: support rcu-walk? */ -+ ret = ERR_PTR(-ECHILD); -+ if (flags & LOOKUP_RCU) -+ goto out; -+ -+ ret = ERR_PTR(-ENAMETOOLONG); -+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ goto out; -+ -+ sb = dir->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ ret = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_di_init(dentry); -+ ret = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_si; -+ -+ inode = NULL; -+ npositive = 0; /* suppress a warning */ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_alive_dir(parent); -+ if (!err) -+ err = au_digen_test(parent, au_sigen(sb)); -+ if (!err) { -+ npositive = au_lkup_dentry(dentry, au_dbstart(parent), -+ /*type*/0); -+ err = npositive; -+ } -+ di_read_unlock(parent, AuLock_IR); -+ ret = ERR_PTR(err); -+ if (unlikely(err < 0)) -+ goto out_unlock; -+ -+ if (npositive) { -+ inode = au_new_inode(dentry, /*must_new*/0); -+ if (IS_ERR(inode)) { -+ ret = (void *)inode; -+ inode = NULL; -+ goto out_unlock; -+ } -+ } -+ -+ if (inode) -+ atomic_inc(&inode->i_count); -+ ret = d_splice_alias(inode, dentry); -+#if 0 -+ if (unlikely(d_need_lookup(dentry))) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_NEED_LOOKUP; -+ spin_unlock(&dentry->d_lock); -+ } else -+#endif -+ if (inode) { -+ if (!IS_ERR(ret)) { -+ iput(inode); -+ if (ret && ret != dentry) -+ ii_write_unlock(inode); -+ } else { -+ ii_write_unlock(inode); -+ iput(inode); -+ inode = NULL; -+ } -+ } -+ -+out_unlock: -+ di_write_unlock(dentry); -+ if (inode) { -+ /* verbose coding for lock class name */ -+ if (unlikely(S_ISLNK(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcSymlink_DIINFO); -+ else if (unlikely(S_ISDIR(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcDir_DIINFO); -+ else /* likely */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+ } -+out_si: -+ si_read_unlock(sb); -+out: -+ return ret; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct aopen_node { -+ struct hlist_node hlist; -+ struct file *file, *h_file; -+}; -+ -+static int au_do_aopen(struct inode *inode, struct file *file) -+{ -+ struct au_sphlhead *aopen; -+ struct aopen_node *node; -+ struct au_do_open_args args = { -+ .no_lock = 1, -+ .open = au_do_open_nondir -+ }; -+ -+ aopen = &au_sbi(inode->i_sb)->si_aopen; -+ spin_lock(&aopen->spin); -+ hlist_for_each_entry(node, &aopen->head, hlist) -+ if (node->file == file) { -+ args.h_file = node->h_file; -+ break; -+ } -+ spin_unlock(&aopen->spin); -+ /* AuDebugOn(!args.h_file); */ -+ -+ return au_do_open(file, &args); -+} -+ -+static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct file *file, unsigned int open_flag, -+ umode_t create_mode, int *opened) -+{ -+ int err, h_opened = *opened; -+ struct dentry *parent; -+ struct dentry *d; -+ struct au_sphlhead *aopen; -+ struct vfsub_aopen_args args = { -+ .open_flag = open_flag, -+ .create_mode = create_mode, -+ .opened = &h_opened -+ }; -+ struct aopen_node aopen_node = { -+ .file = file -+ }; -+ -+ IMustLock(dir); -+ AuDbg("open_flag 0x%x\n", open_flag); -+ AuDbgDentry(dentry); -+ -+ err = 0; -+ if (!au_di(dentry)) { -+ d = aufs_lookup(dir, dentry, /*flags*/0); -+ if (IS_ERR(d)) { -+ err = PTR_ERR(d); -+ goto out; -+ } else if (d) { -+ /* -+ * obsoleted dentry found. -+ * another error will be returned later. -+ */ -+ d_drop(d); -+ dput(d); -+ AuDbgDentry(d); -+ } -+ AuDbgDentry(dentry); -+ } -+ -+ if (d_is_positive(dentry) -+ || d_unhashed(dentry) -+ || d_unlinked(dentry) -+ || !(open_flag & O_CREAT)) -+ goto out_no_open; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); -+ if (unlikely(err)) -+ goto out; -+ -+ parent = dentry->d_parent; /* dir is locked */ -+ di_write_lock_parent(parent); -+ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ AuDbgDentry(dentry); -+ if (d_is_positive(dentry)) -+ goto out_unlock; -+ -+ args.file = get_empty_filp(); -+ err = PTR_ERR(args.file); -+ if (IS_ERR(args.file)) -+ goto out_unlock; -+ -+ args.file->f_flags = file->f_flags; -+ err = au_aopen_or_create(dir, dentry, &args); -+ AuTraceErr(err); -+ AuDbgFile(args.file); -+ if (unlikely(err < 0)) { -+ if (h_opened & FILE_OPENED) -+ fput(args.file); -+ else -+ put_filp(args.file); -+ goto out_unlock; -+ } -+ -+ /* some filesystems don't set FILE_CREATED while succeeded? */ -+ *opened |= FILE_CREATED; -+ if (h_opened & FILE_OPENED) -+ aopen_node.h_file = args.file; -+ else { -+ put_filp(args.file); -+ args.file = NULL; -+ } -+ aopen = &au_sbi(dir->i_sb)->si_aopen; -+ au_sphl_add(&aopen_node.hlist, aopen); -+ err = finish_open(file, dentry, au_do_aopen, opened); -+ au_sphl_del(&aopen_node.hlist, aopen); -+ AuTraceErr(err); -+ AuDbgFile(file); -+ if (aopen_node.h_file) -+ fput(aopen_node.h_file); -+ -+out_unlock: -+ di_write_unlock(parent); -+ aufs_read_unlock(dentry, AuLock_DW); -+ AuDbgDentry(dentry); -+ if (unlikely(err)) -+ goto out; -+out_no_open: -+ if (!err && !(*opened & FILE_CREATED)) { -+ AuLabel(out_no_open); -+ dget(dentry); -+ err = finish_no_open(file, dentry); -+ } -+out: -+ AuDbg("%pd%s%s\n", dentry, -+ (*opened & FILE_CREATED) ? " created" : "", -+ (*opened & FILE_OPENED) ? " opened" : ""); -+ AuTraceErr(err); -+ return err; -+} -+ -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, -+ const unsigned char add_entry, aufs_bindex_t bcpup, -+ aufs_bindex_t bstart) -+{ -+ int err; -+ struct dentry *h_parent; -+ struct inode *h_dir; -+ -+ if (add_entry) -+ IMustLock(parent->d_inode); -+ else -+ di_write_lock_parent(parent); -+ -+ err = 0; -+ if (!au_h_dptr(parent, bcpup)) { -+ if (bstart > bcpup) -+ err = au_cpup_dirs(dentry, bcpup); -+ else if (bstart < bcpup) -+ err = au_cpdown_dirs(dentry, bcpup); -+ else -+ BUG(); -+ } -+ if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { -+ h_parent = au_h_dptr(parent, bcpup); -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ err = au_lkup_neg(dentry, bcpup, /*wh*/0); -+ /* todo: no unlock here */ -+ mutex_unlock(&h_dir->i_mutex); -+ -+ AuDbg("bcpup %d\n", bcpup); -+ if (!err) { -+ if (!dentry->d_inode) -+ au_set_h_dptr(dentry, bstart, NULL); -+ au_update_dbrange(dentry, /*do_put_zero*/0); -+ } -+ } -+ -+ if (!add_entry) -+ di_write_unlock(parent); -+ if (!err) -+ err = bcpup; /* success */ -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * decide the branch and the parent dir where we will create a new entry. -+ * returns new bindex or an error. -+ * copyup the parent dir if needed. -+ */ -+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, -+ struct au_wr_dir_args *args) -+{ -+ int err; -+ unsigned int flags; -+ aufs_bindex_t bcpup, bstart, src_bstart; -+ const unsigned char add_entry -+ = au_ftest_wrdir(args->flags, ADD_ENTRY) -+ | au_ftest_wrdir(args->flags, TMPFILE); -+ struct super_block *sb; -+ struct dentry *parent; -+ struct au_sbinfo *sbinfo; -+ -+ sb = dentry->d_sb; -+ sbinfo = au_sbi(sb); -+ parent = dget_parent(dentry); -+ bstart = au_dbstart(dentry); -+ bcpup = bstart; -+ if (args->force_btgt < 0) { -+ if (src_dentry) { -+ src_bstart = au_dbstart(src_dentry); -+ if (src_bstart < bstart) -+ bcpup = src_bstart; -+ } else if (add_entry) { -+ flags = 0; -+ if (au_ftest_wrdir(args->flags, ISDIR)) -+ au_fset_wbr(flags, DIR); -+ err = AuWbrCreate(sbinfo, dentry, flags); -+ bcpup = err; -+ } -+ -+ if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) { -+ if (add_entry) -+ err = AuWbrCopyup(sbinfo, dentry); -+ else { -+ if (!IS_ROOT(dentry)) { -+ di_read_lock_parent(parent, !AuLock_IR); -+ err = AuWbrCopyup(sbinfo, dentry); -+ di_read_unlock(parent, !AuLock_IR); -+ } else -+ err = AuWbrCopyup(sbinfo, dentry); -+ } -+ bcpup = err; -+ if (unlikely(err < 0)) -+ goto out; -+ } -+ } else { -+ bcpup = args->force_btgt; -+ AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode)); -+ } -+ -+ AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); -+ err = bcpup; -+ if (bcpup == bstart) -+ goto out; /* success */ -+ -+ /* copyup the new parent into the branch we process */ -+ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); -+ if (err >= 0) { -+ if (!dentry->d_inode) { -+ au_set_h_dptr(dentry, bstart, NULL); -+ au_set_dbstart(dentry, bcpup); -+ au_set_dbend(dentry, bcpup); -+ } -+ AuDebugOn(add_entry -+ && !au_ftest_wrdir(args->flags, TMPFILE) -+ && !au_h_dptr(dentry, bcpup)); -+ } -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_pin_hdir_unlock(struct au_pin *p) -+{ -+ if (p->hdir) -+ au_hn_imtx_unlock(p->hdir); -+} -+ -+int au_pin_hdir_lock(struct au_pin *p) -+{ -+ int err; -+ -+ err = 0; -+ if (!p->hdir) -+ goto out; -+ -+ /* even if an error happens later, keep this lock */ -+ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); -+ -+ err = -EBUSY; -+ if (unlikely(p->hdir->hi_inode != p->h_parent->d_inode)) -+ goto out; -+ -+ err = 0; -+ if (p->h_dentry) -+ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, -+ p->h_parent, p->br); -+ -+out: -+ return err; -+} -+ -+int au_pin_hdir_relock(struct au_pin *p) -+{ -+ int err, i; -+ struct inode *h_i; -+ struct dentry *h_d[] = { -+ p->h_dentry, -+ p->h_parent -+ }; -+ -+ err = au_pin_hdir_lock(p); -+ if (unlikely(err)) -+ goto out; -+ -+ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { -+ if (!h_d[i]) -+ continue; -+ h_i = h_d[i]->d_inode; -+ if (h_i) -+ err = !h_i->i_nlink; -+ } -+ -+out: -+ return err; -+} -+ -+void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) -+{ -+#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) -+ p->hdir->hi_inode->i_mutex.owner = task; -+#endif -+} -+ -+void au_pin_hdir_acquire_nest(struct au_pin *p) -+{ -+ if (p->hdir) { -+ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, -+ p->lsc_hi, 0, NULL, _RET_IP_); -+ au_pin_hdir_set_owner(p, current); -+ } -+} -+ -+void au_pin_hdir_release(struct au_pin *p) -+{ -+ if (p->hdir) { -+ au_pin_hdir_set_owner(p, p->task); -+ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); -+ } -+} -+ -+struct dentry *au_pinned_h_parent(struct au_pin *pin) -+{ -+ if (pin && pin->parent) -+ return au_h_dptr(pin->parent, pin->bindex); -+ return NULL; -+} -+ -+void au_unpin(struct au_pin *p) -+{ -+ if (p->hdir) -+ au_pin_hdir_unlock(p); -+ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) -+ vfsub_mnt_drop_write(p->h_mnt); -+ if (!p->hdir) -+ return; -+ -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ iput(p->hdir->hi_inode); -+ dput(p->parent); -+ p->parent = NULL; -+ p->hdir = NULL; -+ p->h_mnt = NULL; -+ /* do not clear p->task */ -+} -+ -+int au_do_pin(struct au_pin *p) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *h_dir; -+ -+ err = 0; -+ sb = p->dentry->d_sb; -+ p->br = au_sbr(sb, p->bindex); -+ if (IS_ROOT(p->dentry)) { -+ if (au_ftest_pin(p->flags, MNT_WRITE)) { -+ p->h_mnt = au_br_mnt(p->br); -+ err = vfsub_mnt_want_write(p->h_mnt); -+ if (unlikely(err)) { -+ au_fclr_pin(p->flags, MNT_WRITE); -+ goto out_err; -+ } -+ } -+ goto out; -+ } -+ -+ p->h_dentry = NULL; -+ if (p->bindex <= au_dbend(p->dentry)) -+ p->h_dentry = au_h_dptr(p->dentry, p->bindex); -+ -+ p->parent = dget_parent(p->dentry); -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_lock(p->parent, AuLock_IR, p->lsc_di); -+ -+ h_dir = NULL; -+ p->h_parent = au_h_dptr(p->parent, p->bindex); -+ p->hdir = au_hi(p->parent->d_inode, p->bindex); -+ if (p->hdir) -+ h_dir = p->hdir->hi_inode; -+ -+ /* -+ * udba case, or -+ * if DI_LOCKED is not set, then p->parent may be different -+ * and h_parent can be NULL. -+ */ -+ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { -+ err = -EBUSY; -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ dput(p->parent); -+ p->parent = NULL; -+ goto out_err; -+ } -+ -+ if (au_ftest_pin(p->flags, MNT_WRITE)) { -+ p->h_mnt = au_br_mnt(p->br); -+ err = vfsub_mnt_want_write(p->h_mnt); -+ if (unlikely(err)) { -+ au_fclr_pin(p->flags, MNT_WRITE); -+ if (!au_ftest_pin(p->flags, DI_LOCKED)) -+ di_read_unlock(p->parent, AuLock_IR); -+ dput(p->parent); -+ p->parent = NULL; -+ goto out_err; -+ } -+ } -+ -+ au_igrab(h_dir); -+ err = au_pin_hdir_lock(p); -+ if (!err) -+ goto out; /* success */ -+ -+ au_unpin(p); -+ -+out_err: -+ pr_err("err %d\n", err); -+ err = au_busy_or_stale(); -+out: -+ return err; -+} -+ -+void au_pin_init(struct au_pin *p, struct dentry *dentry, -+ aufs_bindex_t bindex, int lsc_di, int lsc_hi, -+ unsigned int udba, unsigned char flags) -+{ -+ p->dentry = dentry; -+ p->udba = udba; -+ p->lsc_di = lsc_di; -+ p->lsc_hi = lsc_hi; -+ p->flags = flags; -+ p->bindex = bindex; -+ -+ p->parent = NULL; -+ p->hdir = NULL; -+ p->h_mnt = NULL; -+ -+ p->h_dentry = NULL; -+ p->h_parent = NULL; -+ p->br = NULL; -+ p->task = current; -+} -+ -+int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int udba, unsigned char flags) -+{ -+ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, -+ udba, flags); -+ return au_do_pin(pin); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * ->setattr() and ->getattr() are called in various cases. -+ * chmod, stat: dentry is revalidated. -+ * fchmod, fstat: file and dentry are not revalidated, additionally they may be -+ * unhashed. -+ * for ->setattr(), ia->ia_file is passed from ftruncate only. -+ */ -+/* todo: consolidate with do_refresh() and simple_reval_dpath() */ -+int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) -+{ -+ int err; -+ struct inode *inode; -+ struct dentry *parent; -+ -+ err = 0; -+ inode = dentry->d_inode; -+ if (au_digen_test(dentry, sigen)) { -+ parent = dget_parent(dentry); -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_refresh_dentry(dentry, parent); -+ di_read_unlock(parent, AuLock_IR); -+ dput(parent); -+ } -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, -+ struct au_icpup_args *a) -+{ -+ int err; -+ loff_t sz; -+ aufs_bindex_t bstart, ibstart; -+ struct dentry *hi_wh, *parent; -+ struct inode *inode; -+ struct au_wr_dir_args wr_dir_args = { -+ .force_btgt = -1, -+ .flags = 0 -+ }; -+ -+ if (d_is_dir(dentry)) -+ au_fset_wrdir(wr_dir_args.flags, ISDIR); -+ /* plink or hi_wh() case */ -+ bstart = au_dbstart(dentry); -+ inode = dentry->d_inode; -+ ibstart = au_ibstart(inode); -+ if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode)) -+ wr_dir_args.force_btgt = ibstart; -+ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); -+ if (unlikely(err < 0)) -+ goto out; -+ a->btgt = err; -+ if (err != bstart) -+ au_fset_icpup(a->flags, DID_CPUP); -+ -+ err = 0; -+ a->pin_flags = AuPin_MNT_WRITE; -+ parent = NULL; -+ if (!IS_ROOT(dentry)) { -+ au_fset_pin(a->pin_flags, DI_LOCKED); -+ parent = dget_parent(dentry); -+ di_write_lock_parent(parent); -+ } -+ -+ err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ a->h_inode = a->h_path.dentry->d_inode; -+ sz = -1; -+ if (ia && (ia->ia_valid & ATTR_SIZE)) { -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ if (ia->ia_size < i_size_read(a->h_inode)) -+ sz = ia->ia_size; -+ mutex_unlock(&a->h_inode->i_mutex); -+ } -+ -+ hi_wh = NULL; -+ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { -+ hi_wh = au_hi_wh(inode, a->btgt); -+ if (!hi_wh) { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->btgt, -+ .bsrc = -1, -+ .len = sz, -+ .pin = &a->pin -+ }; -+ err = au_sio_cpup_wh(&cpg, /*file*/NULL); -+ if (unlikely(err)) -+ goto out_unlock; -+ hi_wh = au_hi_wh(inode, a->btgt); -+ /* todo: revalidate hi_wh? */ -+ } -+ } -+ -+ if (parent) { -+ au_pin_set_parent_lflag(&a->pin, /*lflag*/0); -+ di_downgrade_lock(parent, AuLock_IR); -+ dput(parent); -+ parent = NULL; -+ } -+ if (!au_ftest_icpup(a->flags, DID_CPUP)) -+ goto out; /* success */ -+ -+ if (!d_unhashed(dentry)) { -+ struct au_cp_generic cpg = { -+ .dentry = dentry, -+ .bdst = a->btgt, -+ .bsrc = bstart, -+ .len = sz, -+ .pin = &a->pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ err = au_sio_cpup_simple(&cpg); -+ if (!err) -+ a->h_path.dentry = au_h_dptr(dentry, a->btgt); -+ } else if (!hi_wh) -+ a->h_path.dentry = au_h_dptr(dentry, a->btgt); -+ else -+ a->h_path.dentry = hi_wh; /* do not dget here */ -+ -+out_unlock: -+ a->h_inode = a->h_path.dentry->d_inode; -+ if (!err) -+ goto out; /* success */ -+ au_unpin(&a->pin); -+out_parent: -+ if (parent) { -+ di_write_unlock(parent); -+ dput(parent); -+ } -+out: -+ if (!err) -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ return err; -+} -+ -+static int aufs_setattr(struct dentry *dentry, struct iattr *ia) -+{ -+ int err; -+ struct inode *inode, *delegated; -+ struct super_block *sb; -+ struct file *file; -+ struct au_icpup_args *a; -+ -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) -+ ia->ia_valid &= ~ATTR_MODE; -+ -+ file = NULL; -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_kfree; -+ -+ if (ia->ia_valid & ATTR_FILE) { -+ /* currently ftruncate(2) only */ -+ AuDebugOn(!d_is_reg(dentry)); -+ file = ia->ia_file; -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out_si; -+ ia->ia_file = au_hf_top(file); -+ a->udba = AuOpt_UDBA_NONE; -+ } else { -+ /* fchmod() doesn't pass ia_file */ -+ a->udba = au_opt_udba(sb); -+ di_write_lock_child(dentry); -+ /* no d_unlinked(), to set UDBA_NONE for root */ -+ if (d_unhashed(dentry)) -+ a->udba = AuOpt_UDBA_NONE; -+ if (a->udba != AuOpt_UDBA_NONE) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_for_attr(dentry, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out_dentry; -+ } -+ } -+ -+ err = au_pin_and_icpup(dentry, ia, a); -+ if (unlikely(err < 0)) -+ goto out_dentry; -+ if (au_ftest_icpup(a->flags, DID_CPUP)) { -+ ia->ia_file = NULL; -+ ia->ia_valid &= ~ATTR_FILE; -+ } -+ -+ a->h_path.mnt = au_sbr_mnt(sb, a->btgt); -+ if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) -+ == (ATTR_MODE | ATTR_CTIME)) { -+ err = security_path_chmod(&a->h_path, ia->ia_mode); -+ if (unlikely(err)) -+ goto out_unlock; -+ } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) -+ && (ia->ia_valid & ATTR_CTIME)) { -+ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); -+ if (unlikely(err)) -+ goto out_unlock; -+ } -+ -+ if (ia->ia_valid & ATTR_SIZE) { -+ struct file *f; -+ -+ if (ia->ia_size < i_size_read(inode)) -+ /* unmap only */ -+ truncate_setsize(inode, ia->ia_size); -+ -+ f = NULL; -+ if (ia->ia_valid & ATTR_FILE) -+ f = ia->ia_file; -+ mutex_unlock(&a->h_inode->i_mutex); -+ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); -+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); -+ } else { -+ delegated = NULL; -+ while (1) { -+ err = vfsub_notify_change(&a->h_path, ia, &delegated); -+ if (delegated) { -+ err = break_deleg_wait(&delegated); -+ if (!err) -+ continue; -+ } -+ break; -+ } -+ } -+ if (!err) -+ au_cpup_attr_changeable(inode); -+ -+out_unlock: -+ mutex_unlock(&a->h_inode->i_mutex); -+ au_unpin(&a->pin); -+ if (unlikely(err)) -+ au_update_dbstart(dentry); -+out_dentry: -+ di_write_unlock(dentry); -+ if (file) { -+ fi_write_unlock(file); -+ ia->ia_file = file; -+ ia->ia_valid |= ATTR_FILE; -+ } -+out_si: -+ si_read_unlock(sb); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) -+static int au_h_path_to_set_attr(struct dentry *dentry, -+ struct au_icpup_args *a, struct path *h_path) -+{ -+ int err; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ a->udba = au_opt_udba(sb); -+ /* no d_unlinked(), to set UDBA_NONE for root */ -+ if (d_unhashed(dentry)) -+ a->udba = AuOpt_UDBA_NONE; -+ if (a->udba != AuOpt_UDBA_NONE) { -+ AuDebugOn(IS_ROOT(dentry)); -+ err = au_reval_for_attr(dentry, au_sigen(sb)); -+ if (unlikely(err)) -+ goto out; -+ } -+ err = au_pin_and_icpup(dentry, /*ia*/NULL, a); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ h_path->dentry = a->h_path.dentry; -+ h_path->mnt = au_sbr_mnt(sb, a->btgt); -+ -+out: -+ return err; -+} -+ -+ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg) -+{ -+ int err; -+ struct path h_path; -+ struct super_block *sb; -+ struct au_icpup_args *a; -+ struct inode *inode, *h_inode; -+ -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ -+ err = -ENOMEM; -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_kfree; -+ -+ h_path.dentry = NULL; /* silence gcc */ -+ di_write_lock_child(dentry); -+ err = au_h_path_to_set_attr(dentry, a, &h_path); -+ if (unlikely(err)) -+ goto out_di; -+ -+ mutex_unlock(&a->h_inode->i_mutex); -+ switch (arg->type) { -+ case AU_XATTR_SET: -+ err = vfsub_setxattr(h_path.dentry, -+ arg->u.set.name, arg->u.set.value, -+ arg->u.set.size, arg->u.set.flags); -+ break; -+ case AU_XATTR_REMOVE: -+ err = vfsub_removexattr(h_path.dentry, arg->u.remove.name); -+ break; -+ case AU_ACL_SET: -+ err = -EOPNOTSUPP; -+ h_inode = h_path.dentry->d_inode; -+ if (h_inode->i_op->set_acl) -+ err = h_inode->i_op->set_acl(h_inode, -+ arg->u.acl_set.acl, -+ arg->u.acl_set.type); -+ break; -+ } -+ if (!err) -+ au_cpup_attr_timesizes(inode); -+ -+ au_unpin(&a->pin); -+ if (unlikely(err)) -+ au_update_dbstart(dentry); -+ -+out_di: -+ di_write_unlock(dentry); -+ si_read_unlock(sb); -+out_kfree: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+#endif -+ -+static void au_refresh_iattr(struct inode *inode, struct kstat *st, -+ unsigned int nlink) -+{ -+ unsigned int n; -+ -+ inode->i_mode = st->mode; -+ /* don't i_[ug]id_write() here */ -+ inode->i_uid = st->uid; -+ inode->i_gid = st->gid; -+ inode->i_atime = st->atime; -+ inode->i_mtime = st->mtime; -+ inode->i_ctime = st->ctime; -+ -+ au_cpup_attr_nlink(inode, /*force*/0); -+ if (S_ISDIR(inode->i_mode)) { -+ n = inode->i_nlink; -+ n -= nlink; -+ n += st->nlink; -+ smp_mb(); /* for i_nlink */ -+ /* 0 can happen */ -+ set_nlink(inode, n); -+ } -+ -+ spin_lock(&inode->i_lock); -+ inode->i_blocks = st->blocks; -+ i_size_write(inode, st->size); -+ spin_unlock(&inode->i_lock); -+} -+ -+/* -+ * common routine for aufs_getattr() and aufs_getxattr(). -+ * returns zero or negative (an error). -+ * @dentry will be read-locked in success. -+ */ -+int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path) -+{ -+ int err; -+ unsigned int mnt_flags, sigen; -+ unsigned char udba_none; -+ aufs_bindex_t bindex; -+ struct super_block *sb, *h_sb; -+ struct inode *inode; -+ -+ h_path->mnt = NULL; -+ h_path->dentry = NULL; -+ -+ err = 0; -+ sb = dentry->d_sb; -+ mnt_flags = au_mntflags(sb); -+ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); -+ -+ /* support fstat(2) */ -+ if (!d_unlinked(dentry) && !udba_none) { -+ sigen = au_sigen(sb); -+ err = au_digen_test(dentry, sigen); -+ if (!err) { -+ di_read_lock_child(dentry, AuLock_IR); -+ err = au_dbrange_test(dentry); -+ if (unlikely(err)) { -+ di_read_unlock(dentry, AuLock_IR); -+ goto out; -+ } -+ } else { -+ AuDebugOn(IS_ROOT(dentry)); -+ di_write_lock_child(dentry); -+ err = au_dbrange_test(dentry); -+ if (!err) -+ err = au_reval_for_attr(dentry, sigen); -+ if (!err) -+ di_downgrade_lock(dentry, AuLock_IR); -+ else { -+ di_write_unlock(dentry); -+ goto out; -+ } -+ } -+ } else -+ di_read_lock_child(dentry, AuLock_IR); -+ -+ inode = dentry->d_inode; -+ bindex = au_ibstart(inode); -+ h_path->mnt = au_sbr_mnt(sb, bindex); -+ h_sb = h_path->mnt->mnt_sb; -+ if (!force -+ && !au_test_fs_bad_iattr(h_sb) -+ && udba_none) -+ goto out; /* success */ -+ -+ if (au_dbstart(dentry) == bindex) -+ h_path->dentry = au_h_dptr(dentry, bindex); -+ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { -+ h_path->dentry = au_plink_lkup(inode, bindex); -+ if (IS_ERR(h_path->dentry)) -+ /* pretending success */ -+ h_path->dentry = NULL; -+ else -+ dput(h_path->dentry); -+ } -+ -+out: -+ return err; -+} -+ -+static int aufs_getattr(struct vfsmount *mnt __maybe_unused, -+ struct dentry *dentry, struct kstat *st) -+{ -+ int err; -+ unsigned char positive; -+ struct path h_path; -+ struct inode *inode; -+ struct super_block *sb; -+ -+ inode = dentry->d_inode; -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ err = au_h_path_getattr(dentry, /*force*/0, &h_path); -+ if (unlikely(err)) -+ goto out_si; -+ if (unlikely(!h_path.dentry)) -+ /* illegally overlapped or something */ -+ goto out_fill; /* pretending success */ -+ -+ positive = !!h_path.dentry->d_inode; -+ if (positive) -+ err = vfs_getattr(&h_path, st); -+ if (!err) { -+ if (positive) -+ au_refresh_iattr(inode, st, -+ h_path.dentry->d_inode->i_nlink); -+ goto out_fill; /* success */ -+ } -+ AuTraceErr(err); -+ goto out_di; -+ -+out_fill: -+ generic_fillattr(inode, st); -+out_di: -+ di_read_unlock(dentry, AuLock_IR); -+out_si: -+ si_read_unlock(sb); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int h_readlink(struct dentry *dentry, int bindex, char __user *buf, -+ int bufsiz) -+{ -+ int err; -+ struct super_block *sb; -+ struct dentry *h_dentry; -+ -+ err = -EINVAL; -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (unlikely(!h_dentry->d_inode->i_op->readlink)) -+ goto out; -+ -+ err = security_inode_readlink(h_dentry); -+ if (unlikely(err)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ if (!au_test_ro(sb, bindex, dentry->d_inode)) { -+ vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry); -+ fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode); -+ } -+ err = h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz); -+ -+out: -+ return err; -+} -+ -+static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) -+{ -+ int err; -+ -+ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); -+ if (unlikely(err)) -+ goto out; -+ err = au_d_hashed_positive(dentry); -+ if (!err) -+ err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); -+ aufs_read_unlock(dentry, AuLock_IR); -+ -+out: -+ return err; -+} -+ -+static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) -+{ -+ int err; -+ mm_segment_t old_fs; -+ union { -+ char *k; -+ char __user *u; -+ } buf; -+ -+ err = -ENOMEM; -+ buf.k = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!buf.k)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_name; -+ -+ err = au_d_hashed_positive(dentry); -+ if (!err) { -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = h_readlink(dentry, au_dbstart(dentry), buf.u, PATH_MAX); -+ set_fs(old_fs); -+ } -+ aufs_read_unlock(dentry, AuLock_IR); -+ -+ if (err >= 0) { -+ buf.k[err] = 0; -+ /* will be freed by put_link */ -+ nd_set_link(nd, buf.k); -+ return NULL; /* success */ -+ } -+ -+out_name: -+ free_page((unsigned long)buf.k); -+out: -+ AuTraceErr(err); -+ return ERR_PTR(err); -+} -+ -+static void aufs_put_link(struct dentry *dentry __maybe_unused, -+ struct nameidata *nd, void *cookie __maybe_unused) -+{ -+ char *p; -+ -+ p = nd_get_link(nd); -+ if (!IS_ERR_OR_NULL(p)) -+ free_page((unsigned long)p); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *h_inode; -+ -+ sb = inode->i_sb; -+ /* mmap_sem might be acquired already, cf. aufs_mmap() */ -+ lockdep_off(); -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_write_lock_child(inode); -+ lockdep_on(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ err = vfsub_update_time(h_inode, ts, flags); -+ lockdep_off(); -+ if (!err) -+ au_cpup_attr_timesizes(inode); -+ ii_write_unlock(inode); -+ si_read_unlock(sb); -+ lockdep_on(); -+ -+ if (!err && (flags & S_VERSION)) -+ inode_inc_iversion(inode); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct inode_operations aufs_symlink_iop = { -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, /* unsupport for symlink? */ -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .readlink = aufs_readlink, -+ .follow_link = aufs_follow_link, -+ .put_link = aufs_put_link, -+ -+ /* .update_time = aufs_update_time */ -+}; -+ -+struct inode_operations aufs_dir_iop = { -+ .create = aufs_create, -+ .lookup = aufs_lookup, -+ .link = aufs_link, -+ .unlink = aufs_unlink, -+ .symlink = aufs_symlink, -+ .mkdir = aufs_mkdir, -+ .rmdir = aufs_rmdir, -+ .mknod = aufs_mknod, -+ .rename = aufs_rename, -+ -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .update_time = aufs_update_time, -+ .atomic_open = aufs_atomic_open, -+ .tmpfile = aufs_tmpfile -+}; -+ -+struct inode_operations aufs_iop = { -+ .permission = aufs_permission, -+#ifdef CONFIG_FS_POSIX_ACL -+ .get_acl = aufs_get_acl, -+ .set_acl = aufs_set_acl, -+#endif -+ -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+#ifdef CONFIG_AUFS_XATTR -+ .setxattr = aufs_setxattr, -+ .getxattr = aufs_getxattr, -+ .listxattr = aufs_listxattr, -+ .removexattr = aufs_removexattr, -+#endif -+ -+ .update_time = aufs_update_time -+}; -diff -Nur linux-4.0.4.orig/fs/aufs/i_op_del.c linux-4.0.4/fs/aufs/i_op_del.c ---- linux-4.0.4.orig/fs/aufs/i_op_del.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/i_op_del.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,493 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode operations (del entry) -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * decide if a new whiteout for @dentry is necessary or not. -+ * when it is necessary, prepare the parent dir for the upper branch whose -+ * branch index is @bcpup for creation. the actual creation of the whiteout will -+ * be done by caller. -+ * return value: -+ * 0: wh is unnecessary -+ * plus: wh is necessary -+ * minus: error -+ */ -+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) -+{ -+ int need_wh, err; -+ aufs_bindex_t bstart; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ if (*bcpup < 0) { -+ *bcpup = bstart; -+ if (au_test_ro(sb, bstart, dentry->d_inode)) { -+ err = AuWbrCopyup(au_sbi(sb), dentry); -+ *bcpup = err; -+ if (unlikely(err < 0)) -+ goto out; -+ } -+ } else -+ AuDebugOn(bstart < *bcpup -+ || au_test_ro(sb, *bcpup, dentry->d_inode)); -+ AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); -+ -+ if (*bcpup != bstart) { -+ err = au_cpup_dirs(dentry, *bcpup); -+ if (unlikely(err)) -+ goto out; -+ need_wh = 1; -+ } else { -+ struct au_dinfo *dinfo, *tmp; -+ -+ need_wh = -ENOMEM; -+ dinfo = au_di(dentry); -+ tmp = au_di_alloc(sb, AuLsc_DI_TMP); -+ if (tmp) { -+ au_di_cp(tmp, dinfo); -+ au_di_swap(tmp, dinfo); -+ /* returns the number of positive dentries */ -+ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0); -+ au_di_swap(tmp, dinfo); -+ au_rw_write_unlock(&tmp->di_rwsem); -+ au_di_free(tmp); -+ } -+ } -+ AuDbg("need_wh %d\n", need_wh); -+ err = need_wh; -+ -+out: -+ return err; -+} -+ -+/* -+ * simple tests for the del-entry operations. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent, int isdir) -+{ -+ int err; -+ umode_t h_mode; -+ struct dentry *h_dentry, *h_latest; -+ struct inode *h_inode; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ h_inode = h_dentry->d_inode; -+ if (dentry->d_inode) { -+ err = -ENOENT; -+ if (unlikely(!h_inode || !h_inode->i_nlink)) -+ goto out; -+ -+ h_mode = h_inode->i_mode; -+ if (!isdir) { -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(h_mode))) -+ goto out; -+ } else if (unlikely(!S_ISDIR(h_mode))) { -+ err = -ENOTDIR; -+ goto out; -+ } -+ } else { -+ /* rename(2) case */ -+ err = -EIO; -+ if (unlikely(h_inode)) -+ goto out; -+ } -+ -+ err = -ENOENT; -+ /* expected parent dir is locked */ -+ if (unlikely(h_parent != h_dentry->d_parent)) -+ goto out; -+ err = 0; -+ -+ /* -+ * rmdir a dir may break the consistency on some filesystem. -+ * let's try heavy test. -+ */ -+ err = -EACCES; -+ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) -+ && au_test_h_perm(h_parent->d_inode, -+ MAY_EXEC | MAY_WRITE))) -+ goto out; -+ -+ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); -+ err = -EIO; -+ if (IS_ERR(h_latest)) -+ goto out; -+ if (h_latest == h_dentry) -+ err = 0; -+ dput(h_latest); -+ -+out: -+ return err; -+} -+ -+/* -+ * decide the branch where we operate for @dentry. the branch index will be set -+ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent -+ * dir for reverting. -+ * when a new whiteout is necessary, create it. -+ */ -+static struct dentry* -+lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, -+ struct au_dtime *dt, struct au_pin *pin) -+{ -+ struct dentry *wh_dentry; -+ struct super_block *sb; -+ struct path h_path; -+ int err, need_wh; -+ unsigned int udba; -+ aufs_bindex_t bcpup; -+ -+ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); -+ wh_dentry = ERR_PTR(need_wh); -+ if (unlikely(need_wh < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ udba = au_opt_udba(sb); -+ bcpup = *rbcpup; -+ err = au_pin(pin, dentry, bcpup, udba, -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ h_path.dentry = au_pinned_h_parent(pin); -+ if (udba != AuOpt_UDBA_NONE -+ && au_dbstart(dentry) == bcpup) { -+ err = au_may_del(dentry, bcpup, h_path.dentry, isdir); -+ wh_dentry = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_unpin; -+ } -+ -+ h_path.mnt = au_sbr_mnt(sb, bcpup); -+ au_dtime_store(dt, au_pinned_parent(pin), &h_path); -+ wh_dentry = NULL; -+ if (!need_wh) -+ goto out; /* success, no need to create whiteout */ -+ -+ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_unpin; -+ -+ /* returns with the parent is locked and wh_dentry is dget-ed */ -+ goto out; /* success */ -+ -+out_unpin: -+ au_unpin(pin); -+out: -+ return wh_dentry; -+} -+ -+/* -+ * when removing a dir, rename it to a unique temporary whiteout-ed name first -+ * in order to be revertible and save time for removing many child whiteouts -+ * under the dir. -+ * returns 1 when there are too many child whiteout and caller should remove -+ * them asynchronously. returns 0 when the number of children is enough small to -+ * remove now or the branch fs is a remote fs. -+ * otherwise return an error. -+ */ -+static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, -+ struct au_nhash *whlist, struct inode *dir) -+{ -+ int rmdir_later, err, dirwh; -+ struct dentry *h_dentry; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ SiMustAnyLock(sb); -+ h_dentry = au_h_dptr(dentry, bindex); -+ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); -+ if (unlikely(err)) -+ goto out; -+ -+ /* stop monitoring */ -+ au_hn_free(au_hi(dentry->d_inode, bindex)); -+ -+ if (!au_test_fs_remote(h_dentry->d_sb)) { -+ dirwh = au_sbi(sb)->si_dirwh; -+ rmdir_later = (dirwh <= 1); -+ if (!rmdir_later) -+ rmdir_later = au_nhash_test_longer_wh(whlist, bindex, -+ dirwh); -+ if (rmdir_later) -+ return rmdir_later; -+ } -+ -+ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); -+ if (unlikely(err)) { -+ AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", -+ h_dentry, bindex, err); -+ err = 0; -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * final procedure for deleting a entry. -+ * maintain dentry and iattr. -+ */ -+static void epilog(struct inode *dir, struct dentry *dentry, -+ aufs_bindex_t bindex) -+{ -+ struct inode *inode; -+ -+ inode = dentry->d_inode; -+ d_drop(dentry); -+ inode->i_ctime = dir->i_ctime; -+ -+ au_dir_ts(dir, bindex); -+ dir->i_version++; -+} -+ -+/* -+ * when an error happened, remove the created whiteout and revert everything. -+ */ -+static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, -+ aufs_bindex_t bwh, struct dentry *wh_dentry, -+ struct dentry *dentry, struct au_dtime *dt) -+{ -+ int rerr; -+ struct path h_path = { -+ .dentry = wh_dentry, -+ .mnt = au_sbr_mnt(dir->i_sb, bindex) -+ }; -+ -+ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); -+ if (!rerr) { -+ au_set_dbwh(dentry, bwh); -+ au_dtime_revert(dt); -+ return 0; -+ } -+ -+ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); -+ return -EIO; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int aufs_unlink(struct inode *dir, struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bwh, bindex, bstart; -+ struct inode *inode, *h_dir, *delegated; -+ struct dentry *parent, *wh_dentry; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ struct path h_path; -+ } *a; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_d_hashed_positive(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ err = -EISDIR; -+ if (unlikely(d_is_dir(dentry))) -+ goto out_unlock; /* possible? */ -+ -+ bstart = au_dbstart(dentry); -+ bwh = au_dbwh(dentry); -+ bindex = -1; -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, -+ &a->pin); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); -+ a->h_path.dentry = au_h_dptr(dentry, bstart); -+ dget(a->h_path.dentry); -+ if (bindex == bstart) { -+ h_dir = au_pinned_h_dir(&a->pin); -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ } else { -+ /* dir inode is locked */ -+ h_dir = wh_dentry->d_parent->d_inode; -+ IMustLock(h_dir); -+ err = 0; -+ } -+ -+ if (!err) { -+ vfsub_drop_nlink(inode); -+ epilog(dir, dentry, bindex); -+ -+ /* update target timestamps */ -+ if (bindex == bstart) { -+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); -+ /*ignore*/ -+ inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; -+ } else -+ /* todo: this timestamp may be reverted later */ -+ inode->i_ctime = h_dir->i_ctime; -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ if (wh_dentry) { -+ int rerr; -+ -+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, -+ &a->dt); -+ if (rerr) -+ err = rerr; -+ } -+ -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ dput(a->h_path.dentry); -+out_parent: -+ di_write_unlock(parent); -+out_unlock: -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ return err; -+} -+ -+int aufs_rmdir(struct inode *dir, struct dentry *dentry) -+{ -+ int err, rmdir_later; -+ aufs_bindex_t bwh, bindex, bstart; -+ struct inode *inode; -+ struct dentry *parent, *wh_dentry, *h_dentry; -+ struct au_whtmp_rmdir *args; -+ /* to reuduce stack size */ -+ struct { -+ struct au_dtime dt; -+ struct au_pin pin; -+ } *a; -+ -+ IMustLock(dir); -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); -+ if (unlikely(err)) -+ goto out_free; -+ err = au_alive_dir(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ inode = dentry->d_inode; -+ IMustLock(inode); -+ err = -ENOTDIR; -+ if (unlikely(!d_is_dir(dentry))) -+ goto out_unlock; /* possible? */ -+ -+ err = -ENOMEM; -+ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); -+ if (unlikely(!args)) -+ goto out_unlock; -+ -+ parent = dentry->d_parent; /* dir inode is locked */ -+ di_write_lock_parent(parent); -+ err = au_test_empty(dentry, &args->whlist); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ bstart = au_dbstart(dentry); -+ bwh = au_dbwh(dentry); -+ bindex = -1; -+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, -+ &a->pin); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) -+ goto out_parent; -+ -+ h_dentry = au_h_dptr(dentry, bstart); -+ dget(h_dentry); -+ rmdir_later = 0; -+ if (bindex == bstart) { -+ err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); -+ if (err > 0) { -+ rmdir_later = err; -+ err = 0; -+ } -+ } else { -+ /* stop monitoring */ -+ au_hn_free(au_hi(inode, bstart)); -+ -+ /* dir inode is locked */ -+ IMustLock(wh_dentry->d_parent->d_inode); -+ err = 0; -+ } -+ -+ if (!err) { -+ vfsub_dead_dir(inode); -+ au_set_dbdiropq(dentry, -1); -+ epilog(dir, dentry, bindex); -+ -+ if (rmdir_later) { -+ au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); -+ args = NULL; -+ } -+ -+ goto out_unpin; /* success */ -+ } -+ -+ /* revert */ -+ AuLabel(revert); -+ if (wh_dentry) { -+ int rerr; -+ -+ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, -+ &a->dt); -+ if (rerr) -+ err = rerr; -+ } -+ -+out_unpin: -+ au_unpin(&a->pin); -+ dput(wh_dentry); -+ dput(h_dentry); -+out_parent: -+ di_write_unlock(parent); -+ if (args) -+ au_whtmp_rmdir_free(args); -+out_unlock: -+ aufs_read_unlock(dentry, AuLock_DW); -+out_free: -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/i_op_ren.c linux-4.0.4/fs/aufs/i_op_ren.c ---- linux-4.0.4.orig/fs/aufs/i_op_ren.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/i_op_ren.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,1002 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * inode operation (rename entry) -+ * todo: this is crazy monster -+ */ -+ -+#include "aufs.h" -+ -+enum { AuSRC, AuDST, AuSrcDst }; -+enum { AuPARENT, AuCHILD, AuParentChild }; -+ -+#define AuRen_ISDIR 1 -+#define AuRen_ISSAMEDIR (1 << 1) -+#define AuRen_WHSRC (1 << 2) -+#define AuRen_WHDST (1 << 3) -+#define AuRen_MNT_WRITE (1 << 4) -+#define AuRen_DT_DSTDIR (1 << 5) -+#define AuRen_DIROPQ (1 << 6) -+#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) -+#define au_fset_ren(flags, name) \ -+ do { (flags) |= AuRen_##name; } while (0) -+#define au_fclr_ren(flags, name) \ -+ do { (flags) &= ~AuRen_##name; } while (0) -+ -+struct au_ren_args { -+ struct { -+ struct dentry *dentry, *h_dentry, *parent, *h_parent, -+ *wh_dentry; -+ struct inode *dir, *inode; -+ struct au_hinode *hdir; -+ struct au_dtime dt[AuParentChild]; -+ aufs_bindex_t bstart; -+ } sd[AuSrcDst]; -+ -+#define src_dentry sd[AuSRC].dentry -+#define src_dir sd[AuSRC].dir -+#define src_inode sd[AuSRC].inode -+#define src_h_dentry sd[AuSRC].h_dentry -+#define src_parent sd[AuSRC].parent -+#define src_h_parent sd[AuSRC].h_parent -+#define src_wh_dentry sd[AuSRC].wh_dentry -+#define src_hdir sd[AuSRC].hdir -+#define src_h_dir sd[AuSRC].hdir->hi_inode -+#define src_dt sd[AuSRC].dt -+#define src_bstart sd[AuSRC].bstart -+ -+#define dst_dentry sd[AuDST].dentry -+#define dst_dir sd[AuDST].dir -+#define dst_inode sd[AuDST].inode -+#define dst_h_dentry sd[AuDST].h_dentry -+#define dst_parent sd[AuDST].parent -+#define dst_h_parent sd[AuDST].h_parent -+#define dst_wh_dentry sd[AuDST].wh_dentry -+#define dst_hdir sd[AuDST].hdir -+#define dst_h_dir sd[AuDST].hdir->hi_inode -+#define dst_dt sd[AuDST].dt -+#define dst_bstart sd[AuDST].bstart -+ -+ struct dentry *h_trap; -+ struct au_branch *br; -+ struct au_hinode *src_hinode; -+ struct path h_path; -+ struct au_nhash whlist; -+ aufs_bindex_t btgt, src_bwh, src_bdiropq; -+ -+ unsigned int flags; -+ -+ struct au_whtmp_rmdir *thargs; -+ struct dentry *h_dst; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * functions for reverting. -+ * when an error happened in a single rename systemcall, we should revert -+ * everything as if nothing happend. -+ * we don't need to revert the copied-up/down the parent dir since they are -+ * harmless. -+ */ -+ -+#define RevertFailure(fmt, ...) do { \ -+ AuIOErr("revert failure: " fmt " (%d, %d)\n", \ -+ ##__VA_ARGS__, err, rerr); \ -+ err = -EIO; \ -+} while (0) -+ -+static void au_ren_rev_diropq(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ -+ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(a->src_dentry, a->btgt); -+ au_hn_imtx_unlock(a->src_hinode); -+ au_set_dbdiropq(a->src_dentry, a->src_bdiropq); -+ if (rerr) -+ RevertFailure("remove diropq %pd", a->src_dentry); -+} -+ -+static void au_ren_rev_rename(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ struct inode *delegated; -+ -+ a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, -+ a->src_h_parent); -+ rerr = PTR_ERR(a->h_path.dentry); -+ if (IS_ERR(a->h_path.dentry)) { -+ RevertFailure("lkup one %pd", a->src_dentry); -+ return; -+ } -+ -+ delegated = NULL; -+ rerr = vfsub_rename(a->dst_h_dir, -+ au_h_dptr(a->src_dentry, a->btgt), -+ a->src_h_dir, &a->h_path, &delegated); -+ if (unlikely(rerr == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ -+ if (rerr) -+ RevertFailure("rename %pd", a->src_dentry); -+} -+ -+static void au_ren_rev_whtmp(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ struct inode *delegated; -+ -+ a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, -+ a->dst_h_parent); -+ rerr = PTR_ERR(a->h_path.dentry); -+ if (IS_ERR(a->h_path.dentry)) { -+ RevertFailure("lkup one %pd", a->dst_dentry); -+ return; -+ } -+ if (a->h_path.dentry->d_inode) { -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ return; -+ } -+ -+ delegated = NULL; -+ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, -+ &delegated); -+ if (unlikely(rerr == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ d_drop(a->h_path.dentry); -+ dput(a->h_path.dentry); -+ if (!rerr) -+ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); -+ else -+ RevertFailure("rename %pd", a->h_dst); -+} -+ -+static void au_ren_rev_whsrc(int err, struct au_ren_args *a) -+{ -+ int rerr; -+ -+ a->h_path.dentry = a->src_wh_dentry; -+ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); -+ au_set_dbwh(a->src_dentry, a->src_bwh); -+ if (rerr) -+ RevertFailure("unlink %pd", a->src_wh_dentry); -+} -+#undef RevertFailure -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * when we have to copyup the renaming entry, do it with the rename-target name -+ * in order to minimize the cost (the later actual rename is unnecessary). -+ * otherwise rename it on the target branch. -+ */ -+static int au_ren_or_cpup(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *d; -+ struct inode *delegated; -+ -+ d = a->src_dentry; -+ if (au_dbstart(d) == a->btgt) { -+ a->h_path.dentry = a->dst_h_dentry; -+ if (au_ftest_ren(a->flags, DIROPQ) -+ && au_dbdiropq(d) == a->btgt) -+ au_fclr_ren(a->flags, DIROPQ); -+ AuDebugOn(au_dbstart(d) != a->btgt); -+ delegated = NULL; -+ err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), -+ a->dst_h_dir, &a->h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ } else -+ BUG(); -+ -+ if (!err && a->h_dst) -+ /* it will be set to dinfo later */ -+ dget(a->h_dst); -+ -+ return err; -+} -+ -+/* cf. aufs_rmdir() */ -+static int au_ren_del_whtmp(struct au_ren_args *a) -+{ -+ int err; -+ struct inode *dir; -+ -+ dir = a->dst_dir; -+ SiMustAnyLock(dir->i_sb); -+ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, -+ au_sbi(dir->i_sb)->si_dirwh) -+ || au_test_fs_remote(a->h_dst->d_sb)) { -+ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); -+ if (unlikely(err)) -+ pr_warn("failed removing whtmp dir %pd (%d), " -+ "ignored.\n", a->h_dst, err); -+ } else { -+ au_nhash_wh_free(&a->thargs->whlist); -+ a->thargs->whlist = a->whlist; -+ a->whlist.nh_num = 0; -+ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); -+ dput(a->h_dst); -+ a->thargs = NULL; -+ } -+ -+ return 0; -+} -+ -+/* make it 'opaque' dir. */ -+static int au_ren_diropq(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *diropq; -+ -+ err = 0; -+ a->src_bdiropq = au_dbdiropq(a->src_dentry); -+ a->src_hinode = au_hi(a->src_inode, a->btgt); -+ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); -+ diropq = au_diropq_create(a->src_dentry, a->btgt); -+ au_hn_imtx_unlock(a->src_hinode); -+ if (IS_ERR(diropq)) -+ err = PTR_ERR(diropq); -+ else -+ dput(diropq); -+ -+ return err; -+} -+ -+static int do_rename(struct au_ren_args *a) -+{ -+ int err; -+ struct dentry *d, *h_d; -+ -+ /* prepare workqueue args for asynchronous rmdir */ -+ h_d = a->dst_h_dentry; -+ if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) { -+ err = -ENOMEM; -+ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); -+ if (unlikely(!a->thargs)) -+ goto out; -+ a->h_dst = dget(h_d); -+ } -+ -+ /* create whiteout for src_dentry */ -+ if (au_ftest_ren(a->flags, WHSRC)) { -+ a->src_bwh = au_dbwh(a->src_dentry); -+ AuDebugOn(a->src_bwh >= 0); -+ a->src_wh_dentry -+ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); -+ err = PTR_ERR(a->src_wh_dentry); -+ if (IS_ERR(a->src_wh_dentry)) -+ goto out_thargs; -+ } -+ -+ /* lookup whiteout for dentry */ -+ if (au_ftest_ren(a->flags, WHDST)) { -+ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, -+ a->br); -+ err = PTR_ERR(h_d); -+ if (IS_ERR(h_d)) -+ goto out_whsrc; -+ if (!h_d->d_inode) -+ dput(h_d); -+ else -+ a->dst_wh_dentry = h_d; -+ } -+ -+ /* rename dentry to tmpwh */ -+ if (a->thargs) { -+ err = au_whtmp_ren(a->dst_h_dentry, a->br); -+ if (unlikely(err)) -+ goto out_whdst; -+ -+ d = a->dst_dentry; -+ au_set_h_dptr(d, a->btgt, NULL); -+ err = au_lkup_neg(d, a->btgt, /*wh*/0); -+ if (unlikely(err)) -+ goto out_whtmp; -+ a->dst_h_dentry = au_h_dptr(d, a->btgt); -+ } -+ -+ BUG_ON(a->dst_h_dentry->d_inode && a->src_bstart != a->btgt); -+ -+ /* rename by vfs_rename or cpup */ -+ d = a->dst_dentry; -+ if (au_ftest_ren(a->flags, ISDIR) -+ && (a->dst_wh_dentry -+ || au_dbdiropq(d) == a->btgt -+ /* hide the lower to keep xino */ -+ || a->btgt < au_dbend(d) -+ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) -+ au_fset_ren(a->flags, DIROPQ); -+ err = au_ren_or_cpup(a); -+ if (unlikely(err)) -+ /* leave the copied-up one */ -+ goto out_whtmp; -+ -+ /* make dir opaque */ -+ if (au_ftest_ren(a->flags, DIROPQ)) { -+ err = au_ren_diropq(a); -+ if (unlikely(err)) -+ goto out_rename; -+ } -+ -+ /* update target timestamps */ -+ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); -+ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); -+ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ -+ a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; -+ -+ /* remove whiteout for dentry */ -+ if (a->dst_wh_dentry) { -+ a->h_path.dentry = a->dst_wh_dentry; -+ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, -+ a->dst_dentry); -+ if (unlikely(err)) -+ goto out_diropq; -+ } -+ -+ /* remove whtmp */ -+ if (a->thargs) -+ au_ren_del_whtmp(a); /* ignore this error */ -+ -+ au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); -+ err = 0; -+ goto out_success; -+ -+out_diropq: -+ if (au_ftest_ren(a->flags, DIROPQ)) -+ au_ren_rev_diropq(err, a); -+out_rename: -+ au_ren_rev_rename(err, a); -+ dput(a->h_dst); -+out_whtmp: -+ if (a->thargs) -+ au_ren_rev_whtmp(err, a); -+out_whdst: -+ dput(a->dst_wh_dentry); -+ a->dst_wh_dentry = NULL; -+out_whsrc: -+ if (a->src_wh_dentry) -+ au_ren_rev_whsrc(err, a); -+out_success: -+ dput(a->src_wh_dentry); -+ dput(a->dst_wh_dentry); -+out_thargs: -+ if (a->thargs) { -+ dput(a->h_dst); -+ au_whtmp_rmdir_free(a->thargs); -+ a->thargs = NULL; -+ } -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * test if @dentry dir can be rename destination or not. -+ * success means, it is a logically empty dir. -+ */ -+static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) -+{ -+ return au_test_empty(dentry, whlist); -+} -+ -+/* -+ * test if @dentry dir can be rename source or not. -+ * if it can, return 0 and @children is filled. -+ * success means, -+ * - it is a logically empty dir. -+ * - or, it exists on writable branch and has no children including whiteouts -+ * on the lower branch. -+ */ -+static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) -+{ -+ int err; -+ unsigned int rdhash; -+ aufs_bindex_t bstart; -+ -+ bstart = au_dbstart(dentry); -+ if (bstart != btgt) { -+ struct au_nhash whlist; -+ -+ SiMustAnyLock(dentry->d_sb); -+ rdhash = au_sbi(dentry->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, -+ dentry)); -+ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_test_empty(dentry, &whlist); -+ au_nhash_wh_free(&whlist); -+ goto out; -+ } -+ -+ if (bstart == au_dbtaildir(dentry)) -+ return 0; /* success */ -+ -+ err = au_test_empty_lower(dentry); -+ -+out: -+ if (err == -ENOTEMPTY) { -+ AuWarn1("renaming dir who has child(ren) on multiple branches," -+ " is not supported\n"); -+ err = -EXDEV; -+ } -+ return err; -+} -+ -+/* side effect: sets whlist and h_dentry */ -+static int au_ren_may_dir(struct au_ren_args *a) -+{ -+ int err; -+ unsigned int rdhash; -+ struct dentry *d; -+ -+ d = a->dst_dentry; -+ SiMustAnyLock(d->d_sb); -+ -+ err = 0; -+ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { -+ rdhash = au_sbi(d->d_sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); -+ err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ -+ au_set_dbstart(d, a->dst_bstart); -+ err = may_rename_dstdir(d, &a->whlist); -+ au_set_dbstart(d, a->btgt); -+ } -+ a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); -+ if (unlikely(err)) -+ goto out; -+ -+ d = a->src_dentry; -+ a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); -+ if (au_ftest_ren(a->flags, ISDIR)) { -+ err = may_rename_srcdir(d, a->btgt); -+ if (unlikely(err)) { -+ au_nhash_wh_free(&a->whlist); -+ a->whlist.nh_num = 0; -+ } -+ } -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * simple tests for rename. -+ * following the checks in vfs, plus the parent-child relationship. -+ */ -+static int au_may_ren(struct au_ren_args *a) -+{ -+ int err, isdir; -+ struct inode *h_inode; -+ -+ if (a->src_bstart == a->btgt) { -+ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, -+ au_ftest_ren(a->flags, ISDIR)); -+ if (unlikely(err)) -+ goto out; -+ err = -EINVAL; -+ if (unlikely(a->src_h_dentry == a->h_trap)) -+ goto out; -+ } -+ -+ err = 0; -+ if (a->dst_bstart != a->btgt) -+ goto out; -+ -+ err = -ENOTEMPTY; -+ if (unlikely(a->dst_h_dentry == a->h_trap)) -+ goto out; -+ -+ err = -EIO; -+ h_inode = a->dst_h_dentry->d_inode; -+ isdir = !!au_ftest_ren(a->flags, ISDIR); -+ if (!a->dst_dentry->d_inode) { -+ if (unlikely(h_inode)) -+ goto out; -+ err = au_may_add(a->dst_dentry, a->btgt, a->dst_h_parent, -+ isdir); -+ } else { -+ if (unlikely(!h_inode || !h_inode->i_nlink)) -+ goto out; -+ err = au_may_del(a->dst_dentry, a->btgt, a->dst_h_parent, -+ isdir); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+out: -+ if (unlikely(err == -ENOENT || err == -EEXIST)) -+ err = -EIO; -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * locking order -+ * (VFS) -+ * - src_dir and dir by lock_rename() -+ * - inode if exitsts -+ * (aufs) -+ * - lock all -+ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, -+ * + si_read_lock -+ * + di_write_lock2_child() -+ * + di_write_lock_child() -+ * + ii_write_lock_child() -+ * + di_write_lock_child2() -+ * + ii_write_lock_child2() -+ * + src_parent and parent -+ * + di_write_lock_parent() -+ * + ii_write_lock_parent() -+ * + di_write_lock_parent2() -+ * + ii_write_lock_parent2() -+ * + lower src_dir and dir by vfsub_lock_rename() -+ * + verify the every relationships between child and parent. if any -+ * of them failed, unlock all and return -EBUSY. -+ */ -+static void au_ren_unlock(struct au_ren_args *a) -+{ -+ vfsub_unlock_rename(a->src_h_parent, a->src_hdir, -+ a->dst_h_parent, a->dst_hdir); -+ if (au_ftest_ren(a->flags, MNT_WRITE)) -+ vfsub_mnt_drop_write(au_br_mnt(a->br)); -+} -+ -+static int au_ren_lock(struct au_ren_args *a) -+{ -+ int err; -+ unsigned int udba; -+ -+ err = 0; -+ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); -+ a->src_hdir = au_hi(a->src_dir, a->btgt); -+ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); -+ a->dst_hdir = au_hi(a->dst_dir, a->btgt); -+ -+ err = vfsub_mnt_want_write(au_br_mnt(a->br)); -+ if (unlikely(err)) -+ goto out; -+ au_fset_ren(a->flags, MNT_WRITE); -+ a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, -+ a->dst_h_parent, a->dst_hdir); -+ udba = au_opt_udba(a->src_dentry->d_sb); -+ if (unlikely(a->src_hdir->hi_inode != a->src_h_parent->d_inode -+ || a->dst_hdir->hi_inode != a->dst_h_parent->d_inode)) -+ err = au_busy_or_stale(); -+ if (!err && au_dbstart(a->src_dentry) == a->btgt) -+ err = au_h_verify(a->src_h_dentry, udba, -+ a->src_h_parent->d_inode, a->src_h_parent, -+ a->br); -+ if (!err && au_dbstart(a->dst_dentry) == a->btgt) -+ err = au_h_verify(a->dst_h_dentry, udba, -+ a->dst_h_parent->d_inode, a->dst_h_parent, -+ a->br); -+ if (!err) -+ goto out; /* success */ -+ -+ err = au_busy_or_stale(); -+ au_ren_unlock(a); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_ren_refresh_dir(struct au_ren_args *a) -+{ -+ struct inode *dir; -+ -+ dir = a->dst_dir; -+ dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) { -+ /* is this updating defined in POSIX? */ -+ au_cpup_attr_timesizes(a->src_inode); -+ au_cpup_attr_nlink(dir, /*force*/1); -+ } -+ -+ au_dir_ts(dir, a->btgt); -+ -+ if (au_ftest_ren(a->flags, ISSAMEDIR)) -+ return; -+ -+ dir = a->src_dir; -+ dir->i_version++; -+ if (au_ftest_ren(a->flags, ISDIR)) -+ au_cpup_attr_nlink(dir, /*force*/1); -+ au_dir_ts(dir, a->btgt); -+} -+ -+static void au_ren_refresh(struct au_ren_args *a) -+{ -+ aufs_bindex_t bend, bindex; -+ struct dentry *d, *h_d; -+ struct inode *i, *h_i; -+ struct super_block *sb; -+ -+ d = a->dst_dentry; -+ d_drop(d); -+ if (a->h_dst) -+ /* already dget-ed by au_ren_or_cpup() */ -+ au_set_h_dptr(d, a->btgt, a->h_dst); -+ -+ i = a->dst_inode; -+ if (i) { -+ if (!au_ftest_ren(a->flags, ISDIR)) -+ vfsub_drop_nlink(i); -+ else { -+ vfsub_dead_dir(i); -+ au_cpup_attr_timesizes(i); -+ } -+ au_update_dbrange(d, /*do_put_zero*/1); -+ } else { -+ bend = a->btgt; -+ for (bindex = au_dbstart(d); bindex < bend; bindex++) -+ au_set_h_dptr(d, bindex, NULL); -+ bend = au_dbend(d); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) -+ au_set_h_dptr(d, bindex, NULL); -+ au_update_dbrange(d, /*do_put_zero*/0); -+ } -+ -+ d = a->src_dentry; -+ au_set_dbwh(d, -1); -+ bend = au_dbend(d); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { -+ h_d = au_h_dptr(d, bindex); -+ if (h_d) -+ au_set_h_dptr(d, bindex, NULL); -+ } -+ au_set_dbend(d, a->btgt); -+ -+ sb = d->d_sb; -+ i = a->src_inode; -+ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) -+ return; /* success */ -+ -+ bend = au_ibend(i); -+ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { -+ h_i = au_h_iptr(i, bindex); -+ if (h_i) { -+ au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); -+ /* ignore this error */ -+ au_set_h_iptr(i, bindex, NULL, 0); -+ } -+ } -+ au_set_ibend(i, a->btgt); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* mainly for link(2) and rename(2) */ -+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) -+{ -+ aufs_bindex_t bdiropq, bwh; -+ struct dentry *parent; -+ struct au_branch *br; -+ -+ parent = dentry->d_parent; -+ IMustLock(parent->d_inode); /* dir is locked */ -+ -+ bdiropq = au_dbdiropq(parent); -+ bwh = au_dbwh(dentry); -+ br = au_sbr(dentry->d_sb, btgt); -+ if (au_br_rdonly(br) -+ || (0 <= bdiropq && bdiropq < btgt) -+ || (0 <= bwh && bwh < btgt)) -+ btgt = -1; -+ -+ AuDbg("btgt %d\n", btgt); -+ return btgt; -+} -+ -+/* sets src_bstart, dst_bstart and btgt */ -+static int au_ren_wbr(struct au_ren_args *a) -+{ -+ int err; -+ struct au_wr_dir_args wr_dir_args = { -+ /* .force_btgt = -1, */ -+ .flags = AuWrDir_ADD_ENTRY -+ }; -+ -+ a->src_bstart = au_dbstart(a->src_dentry); -+ a->dst_bstart = au_dbstart(a->dst_dentry); -+ if (au_ftest_ren(a->flags, ISDIR)) -+ au_fset_wrdir(wr_dir_args.flags, ISDIR); -+ wr_dir_args.force_btgt = a->src_bstart; -+ if (a->dst_inode && a->dst_bstart < a->src_bstart) -+ wr_dir_args.force_btgt = a->dst_bstart; -+ wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); -+ err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); -+ a->btgt = err; -+ -+ return err; -+} -+ -+static void au_ren_dt(struct au_ren_args *a) -+{ -+ a->h_path.dentry = a->src_h_parent; -+ au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); -+ if (!au_ftest_ren(a->flags, ISSAMEDIR)) { -+ a->h_path.dentry = a->dst_h_parent; -+ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); -+ } -+ -+ au_fclr_ren(a->flags, DT_DSTDIR); -+ if (!au_ftest_ren(a->flags, ISDIR)) -+ return; -+ -+ a->h_path.dentry = a->src_h_dentry; -+ au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); -+ if (a->dst_h_dentry->d_inode) { -+ au_fset_ren(a->flags, DT_DSTDIR); -+ a->h_path.dentry = a->dst_h_dentry; -+ au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); -+ } -+} -+ -+static void au_ren_rev_dt(int err, struct au_ren_args *a) -+{ -+ struct dentry *h_d; -+ struct mutex *h_mtx; -+ -+ au_dtime_revert(a->src_dt + AuPARENT); -+ if (!au_ftest_ren(a->flags, ISSAMEDIR)) -+ au_dtime_revert(a->dst_dt + AuPARENT); -+ -+ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { -+ h_d = a->src_dt[AuCHILD].dt_h_path.dentry; -+ h_mtx = &h_d->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ au_dtime_revert(a->src_dt + AuCHILD); -+ mutex_unlock(h_mtx); -+ -+ if (au_ftest_ren(a->flags, DT_DSTDIR)) { -+ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; -+ h_mtx = &h_d->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); -+ au_dtime_revert(a->dst_dt + AuCHILD); -+ mutex_unlock(h_mtx); -+ } -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, -+ struct inode *_dst_dir, struct dentry *_dst_dentry) -+{ -+ int err, flags; -+ /* reduce stack space */ -+ struct au_ren_args *a; -+ -+ AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry); -+ IMustLock(_src_dir); -+ IMustLock(_dst_dir); -+ -+ err = -ENOMEM; -+ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); -+ a = kzalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ a->src_dir = _src_dir; -+ a->src_dentry = _src_dentry; -+ a->src_inode = a->src_dentry->d_inode; -+ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ -+ a->dst_dir = _dst_dir; -+ a->dst_dentry = _dst_dentry; -+ a->dst_inode = a->dst_dentry->d_inode; -+ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ -+ if (a->dst_inode) { -+ IMustLock(a->dst_inode); -+ au_igrab(a->dst_inode); -+ } -+ -+ err = -ENOTDIR; -+ flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; -+ if (d_is_dir(a->src_dentry)) { -+ au_fset_ren(a->flags, ISDIR); -+ if (unlikely(d_is_positive(a->dst_dentry) -+ && !d_is_dir(a->dst_dentry))) -+ goto out_free; -+ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, -+ AuLock_DIR | flags); -+ } else -+ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, -+ flags); -+ if (unlikely(err)) -+ goto out_free; -+ -+ err = au_d_hashed_positive(a->src_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ err = -ENOENT; -+ if (a->dst_inode) { -+ /* -+ * If it is a dir, VFS unhash dst_dentry before this -+ * function. It means we cannot rely upon d_unhashed(). -+ */ -+ if (unlikely(!a->dst_inode->i_nlink)) -+ goto out_unlock; -+ if (!S_ISDIR(a->dst_inode->i_mode)) { -+ err = au_d_hashed_positive(a->dst_dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ } else if (unlikely(IS_DEADDIR(a->dst_inode))) -+ goto out_unlock; -+ } else if (unlikely(d_unhashed(a->dst_dentry))) -+ goto out_unlock; -+ -+ /* -+ * is it possible? -+ * yes, it happend (in linux-3.3-rcN) but I don't know why. -+ * there may exist a problem somewhere else. -+ */ -+ err = -EINVAL; -+ if (unlikely(a->dst_parent->d_inode == a->src_dentry->d_inode)) -+ goto out_unlock; -+ -+ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ -+ di_write_lock_parent(a->dst_parent); -+ -+ /* which branch we process */ -+ err = au_ren_wbr(a); -+ if (unlikely(err < 0)) -+ goto out_parent; -+ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); -+ a->h_path.mnt = au_br_mnt(a->br); -+ -+ /* are they available to be renamed */ -+ err = au_ren_may_dir(a); -+ if (unlikely(err)) -+ goto out_children; -+ -+ /* prepare the writable parent dir on the same branch */ -+ if (a->dst_bstart == a->btgt) { -+ au_fset_ren(a->flags, WHDST); -+ } else { -+ err = au_cpup_dirs(a->dst_dentry, a->btgt); -+ if (unlikely(err)) -+ goto out_children; -+ } -+ -+ if (a->src_dir != a->dst_dir) { -+ /* -+ * this temporary unlock is safe, -+ * because both dir->i_mutex are locked. -+ */ -+ di_write_unlock(a->dst_parent); -+ di_write_lock_parent(a->src_parent); -+ err = au_wr_dir_need_wh(a->src_dentry, -+ au_ftest_ren(a->flags, ISDIR), -+ &a->btgt); -+ di_write_unlock(a->src_parent); -+ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); -+ au_fclr_ren(a->flags, ISSAMEDIR); -+ } else -+ err = au_wr_dir_need_wh(a->src_dentry, -+ au_ftest_ren(a->flags, ISDIR), -+ &a->btgt); -+ if (unlikely(err < 0)) -+ goto out_children; -+ if (err) -+ au_fset_ren(a->flags, WHSRC); -+ -+ /* cpup src */ -+ if (a->src_bstart != a->btgt) { -+ struct au_pin pin; -+ -+ err = au_pin(&pin, a->src_dentry, a->btgt, -+ au_opt_udba(a->src_dentry->d_sb), -+ AuPin_DI_LOCKED | AuPin_MNT_WRITE); -+ if (!err) { -+ struct au_cp_generic cpg = { -+ .dentry = a->src_dentry, -+ .bdst = a->btgt, -+ .bsrc = a->src_bstart, -+ .len = -1, -+ .pin = &pin, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); -+ err = au_sio_cpup_simple(&cpg); -+ au_unpin(&pin); -+ } -+ if (unlikely(err)) -+ goto out_children; -+ a->src_bstart = a->btgt; -+ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); -+ au_fset_ren(a->flags, WHSRC); -+ } -+ -+ /* lock them all */ -+ err = au_ren_lock(a); -+ if (unlikely(err)) -+ /* leave the copied-up one */ -+ goto out_children; -+ -+ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) -+ err = au_may_ren(a); -+ else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ err = -ENAMETOOLONG; -+ if (unlikely(err)) -+ goto out_hdir; -+ -+ /* store timestamps to be revertible */ -+ au_ren_dt(a); -+ -+ /* here we go */ -+ err = do_rename(a); -+ if (unlikely(err)) -+ goto out_dt; -+ -+ /* update dir attributes */ -+ au_ren_refresh_dir(a); -+ -+ /* dput/iput all lower dentries */ -+ au_ren_refresh(a); -+ -+ goto out_hdir; /* success */ -+ -+out_dt: -+ au_ren_rev_dt(err, a); -+out_hdir: -+ au_ren_unlock(a); -+out_children: -+ au_nhash_wh_free(&a->whlist); -+ if (err && a->dst_inode && a->dst_bstart != a->btgt) { -+ AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); -+ au_set_h_dptr(a->dst_dentry, a->btgt, NULL); -+ au_set_dbstart(a->dst_dentry, a->dst_bstart); -+ } -+out_parent: -+ if (!err) -+ d_move(a->src_dentry, a->dst_dentry); -+ else { -+ au_update_dbstart(a->dst_dentry); -+ if (!a->dst_inode) -+ d_drop(a->dst_dentry); -+ } -+ if (au_ftest_ren(a->flags, ISSAMEDIR)) -+ di_write_unlock(a->dst_parent); -+ else -+ di_write_unlock2(a->src_parent, a->dst_parent); -+out_unlock: -+ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); -+out_free: -+ iput(a->dst_inode); -+ if (a->thargs) -+ au_whtmp_rmdir_free(a->thargs); -+ kfree(a); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/Kconfig linux-4.0.4/fs/aufs/Kconfig ---- linux-4.0.4.orig/fs/aufs/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/Kconfig 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,185 @@ -+config AUFS_FS -+ bool "Aufs (Advanced multi layered unification filesystem) support" -+ help -+ Aufs is a stackable unification filesystem such as Unionfs, -+ which unifies several directories and provides a merged single -+ directory. -+ In the early days, aufs was entirely re-designed and -+ re-implemented Unionfs Version 1.x series. Introducing many -+ original ideas, approaches and improvements, it becomes totally -+ different from Unionfs while keeping the basic features. -+ -+if AUFS_FS -+choice -+ prompt "Maximum number of branches" -+ default AUFS_BRANCH_MAX_127 -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_127 -+ bool "127" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_511 -+ bool "511" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_1023 -+ bool "1023" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+config AUFS_BRANCH_MAX_32767 -+ bool "32767" -+ help -+ Specifies the maximum number of branches (or member directories) -+ in a single aufs. The larger value consumes more system -+ resources and has a minor impact to performance. -+endchoice -+ -+config AUFS_SBILIST -+ bool -+ depends on AUFS_MAGIC_SYSRQ || PROC_FS -+ default y -+ help -+ Automatic configuration for internal use. -+ When aufs supports Magic SysRq or /proc, enabled automatically. -+ -+config AUFS_HNOTIFY -+ bool "Detect direct branch access (bypassing aufs)" -+ help -+ If you want to modify files on branches directly, eg. bypassing aufs, -+ and want aufs to detect the changes of them fully, then enable this -+ option and use 'udba=notify' mount option. -+ Currently there is only one available configuration, "fsnotify". -+ It will have a negative impact to the performance. -+ See detail in aufs.5. -+ -+choice -+ prompt "method" if AUFS_HNOTIFY -+ default AUFS_HFSNOTIFY -+config AUFS_HFSNOTIFY -+ bool "fsnotify" -+ select FSNOTIFY -+endchoice -+ -+config AUFS_EXPORT -+ bool "NFS-exportable aufs" -+ depends on EXPORTFS = y -+ help -+ If you want to export your mounted aufs via NFS, then enable this -+ option. There are several requirements for this configuration. -+ See detail in aufs.5. -+ -+config AUFS_INO_T_64 -+ bool -+ depends on AUFS_EXPORT -+ depends on 64BIT && !(ALPHA || S390) -+ default y -+ help -+ Automatic configuration for internal use. -+ /* typedef unsigned long/int __kernel_ino_t */ -+ /* alpha and s390x are int */ -+ -+config AUFS_XATTR -+ bool "support for XATTR/EA (including Security Labels)" -+ help -+ If your branch fs supports XATTR/EA and you want to make them -+ available in aufs too, then enable this opsion and specify the -+ branch attributes for EA. -+ See detail in aufs.5. -+ -+config AUFS_FHSM -+ bool "File-based Hierarchical Storage Management" -+ help -+ Hierarchical Storage Management (or HSM) is a well-known feature -+ in the storage world. Aufs provides this feature as file-based. -+ with multiple branches. -+ These multiple branches are prioritized, ie. the topmost one -+ should be the fastest drive and be used heavily. -+ -+config AUFS_RDU -+ bool "Readdir in userspace" -+ help -+ Aufs has two methods to provide a merged view for a directory, -+ by a user-space library and by kernel-space natively. The latter -+ is always enabled but sometimes large and slow. -+ If you enable this option, install the library in aufs2-util -+ package, and set some environment variables for your readdir(3), -+ then the work will be handled in user-space which generally -+ shows better performance in most cases. -+ See detail in aufs.5. -+ -+config AUFS_SHWH -+ bool "Show whiteouts" -+ help -+ If you want to make the whiteouts in aufs visible, then enable -+ this option and specify 'shwh' mount option. Although it may -+ sounds like philosophy or something, but in technically it -+ simply shows the name of whiteout with keeping its behaviour. -+ -+config AUFS_BR_RAMFS -+ bool "Ramfs (initramfs/rootfs) as an aufs branch" -+ help -+ If you want to use ramfs as an aufs branch fs, then enable this -+ option. Generally tmpfs is recommended. -+ Aufs prohibited them to be a branch fs by default, because -+ initramfs becomes unusable after switch_root or something -+ generally. If you sets initramfs as an aufs branch and boot your -+ system by switch_root, you will meet a problem easily since the -+ files in initramfs may be inaccessible. -+ Unless you are going to use ramfs as an aufs branch fs without -+ switch_root or something, leave it N. -+ -+config AUFS_BR_FUSE -+ bool "Fuse fs as an aufs branch" -+ depends on FUSE_FS -+ select AUFS_POLL -+ help -+ If you want to use fuse-based userspace filesystem as an aufs -+ branch fs, then enable this option. -+ It implements the internal poll(2) operation which is -+ implemented by fuse only (curretnly). -+ -+config AUFS_POLL -+ bool -+ help -+ Automatic configuration for internal use. -+ -+config AUFS_BR_HFSPLUS -+ bool "Hfsplus as an aufs branch" -+ depends on HFSPLUS_FS -+ default y -+ help -+ If you want to use hfsplus fs as an aufs branch fs, then enable -+ this option. This option introduces a small overhead at -+ copying-up a file on hfsplus. -+ -+config AUFS_BDEV_LOOP -+ bool -+ depends on BLK_DEV_LOOP -+ default y -+ help -+ Automatic configuration for internal use. -+ Convert =[ym] into =y. -+ -+config AUFS_DEBUG -+ bool "Debug aufs" -+ help -+ Enable this to compile aufs internal debug code. -+ It will have a negative impact to the performance. -+ -+config AUFS_MAGIC_SYSRQ -+ bool -+ depends on AUFS_DEBUG && MAGIC_SYSRQ -+ default y -+ help -+ Automatic configuration for internal use. -+ When aufs supports Magic SysRq, enabled automatically. -+endif -diff -Nur linux-4.0.4.orig/fs/aufs/loop.c linux-4.0.4/fs/aufs/loop.c ---- linux-4.0.4.orig/fs/aufs/loop.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/loop.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,132 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * support for loopback block device as a branch -+ */ -+ -+#include "aufs.h" -+ -+/* added into drivers/block/loop.c */ -+static struct file *(*backing_file_func)(struct super_block *sb); -+ -+/* -+ * test if two lower dentries have overlapping branches. -+ */ -+int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding) -+{ -+ struct super_block *h_sb; -+ struct file *backing_file; -+ -+ if (unlikely(!backing_file_func)) { -+ /* don't load "loop" module here */ -+ backing_file_func = symbol_get(loop_backing_file); -+ if (unlikely(!backing_file_func)) -+ /* "loop" module is not loaded */ -+ return 0; -+ } -+ -+ h_sb = h_adding->d_sb; -+ backing_file = backing_file_func(h_sb); -+ if (!backing_file) -+ return 0; -+ -+ h_adding = backing_file->f_path.dentry; -+ /* -+ * h_adding can be local NFS. -+ * in this case aufs cannot detect the loop. -+ */ -+ if (unlikely(h_adding->d_sb == sb)) -+ return 1; -+ return !!au_test_subdir(h_adding, sb->s_root); -+} -+ -+/* true if a kernel thread named 'loop[0-9].*' accesses a file */ -+int au_test_loopback_kthread(void) -+{ -+ int ret; -+ struct task_struct *tsk = current; -+ char c, comm[sizeof(tsk->comm)]; -+ -+ ret = 0; -+ if (tsk->flags & PF_KTHREAD) { -+ get_task_comm(comm, tsk); -+ c = comm[4]; -+ ret = ('0' <= c && c <= '9' -+ && !strncmp(comm, "loop", 4)); -+ } -+ -+ return ret; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define au_warn_loopback_step 16 -+static int au_warn_loopback_nelem = au_warn_loopback_step; -+static unsigned long *au_warn_loopback_array; -+ -+void au_warn_loopback(struct super_block *h_sb) -+{ -+ int i, new_nelem; -+ unsigned long *a, magic; -+ static DEFINE_SPINLOCK(spin); -+ -+ magic = h_sb->s_magic; -+ spin_lock(&spin); -+ a = au_warn_loopback_array; -+ for (i = 0; i < au_warn_loopback_nelem && *a; i++) -+ if (a[i] == magic) { -+ spin_unlock(&spin); -+ return; -+ } -+ -+ /* h_sb is new to us, print it */ -+ if (i < au_warn_loopback_nelem) { -+ a[i] = magic; -+ goto pr; -+ } -+ -+ /* expand the array */ -+ new_nelem = au_warn_loopback_nelem + au_warn_loopback_step; -+ a = au_kzrealloc(au_warn_loopback_array, -+ au_warn_loopback_nelem * sizeof(unsigned long), -+ new_nelem * sizeof(unsigned long), GFP_ATOMIC); -+ if (a) { -+ au_warn_loopback_nelem = new_nelem; -+ au_warn_loopback_array = a; -+ a[i] = magic; -+ goto pr; -+ } -+ -+ spin_unlock(&spin); -+ AuWarn1("realloc failed, ignored\n"); -+ return; -+ -+pr: -+ spin_unlock(&spin); -+ pr_warn("you may want to try another patch for loopback file " -+ "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic); -+} -+ -+int au_loopback_init(void) -+{ -+ int err; -+ struct super_block *sb __maybe_unused; -+ -+ AuDebugOn(sizeof(sb->s_magic) != sizeof(unsigned long)); -+ -+ err = 0; -+ au_warn_loopback_array = kcalloc(au_warn_loopback_step, -+ sizeof(unsigned long), GFP_NOFS); -+ if (unlikely(!au_warn_loopback_array)) -+ err = -ENOMEM; -+ -+ return err; -+} -+ -+void au_loopback_fin(void) -+{ -+ symbol_put(loop_backing_file); -+ kfree(au_warn_loopback_array); -+} -diff -Nur linux-4.0.4.orig/fs/aufs/loop.h linux-4.0.4/fs/aufs/loop.h ---- linux-4.0.4.orig/fs/aufs/loop.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/loop.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,39 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * support for loopback mount as a branch -+ */ -+ -+#ifndef __AUFS_LOOP_H__ -+#define __AUFS_LOOP_H__ -+ -+#ifdef __KERNEL__ -+ -+struct dentry; -+struct super_block; -+ -+#ifdef CONFIG_AUFS_BDEV_LOOP -+/* drivers/block/loop.c */ -+struct file *loop_backing_file(struct super_block *sb); -+ -+/* loop.c */ -+int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding); -+int au_test_loopback_kthread(void); -+void au_warn_loopback(struct super_block *h_sb); -+ -+int au_loopback_init(void); -+void au_loopback_fin(void); -+#else -+AuStubInt0(au_test_loopback_overlap, struct super_block *sb, -+ struct dentry *h_adding) -+AuStubInt0(au_test_loopback_kthread, void) -+AuStubVoid(au_warn_loopback, struct super_block *h_sb) -+ -+AuStubInt0(au_loopback_init, void) -+AuStubVoid(au_loopback_fin, void) -+#endif /* BLK_DEV_LOOP */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_LOOP_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/magic.mk linux-4.0.4/fs/aufs/magic.mk ---- linux-4.0.4.orig/fs/aufs/magic.mk 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/magic.mk 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,30 @@ -+ -+# defined in ${srctree}/fs/fuse/inode.c -+# tristate -+ifdef CONFIG_FUSE_FS -+ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546 -+endif -+ -+# defined in ${srctree}/fs/xfs/xfs_sb.h -+# tristate -+ifdef CONFIG_XFS_FS -+ccflags-y += -DXFS_SB_MAGIC=0x58465342 -+endif -+ -+# defined in ${srctree}/fs/configfs/mount.c -+# tristate -+ifdef CONFIG_CONFIGFS_FS -+ccflags-y += -DCONFIGFS_MAGIC=0x62656570 -+endif -+ -+# defined in ${srctree}/fs/ubifs/ubifs.h -+# tristate -+ifdef CONFIG_UBIFS_FS -+ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905 -+endif -+ -+# defined in ${srctree}/fs/hfsplus/hfsplus_raw.h -+# tristate -+ifdef CONFIG_HFSPLUS_FS -+ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b -+endif -diff -Nur linux-4.0.4.orig/fs/aufs/Makefile linux-4.0.4/fs/aufs/Makefile ---- linux-4.0.4.orig/fs/aufs/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/Makefile 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,36 @@ -+ -+include ${srctree}/${src}/magic.mk -+ -+# cf. include/linux/kernel.h -+# enable pr_debug -+ccflags-y += -DDEBUG -+# sparse requires the full pathname -+ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h -+ -+obj-$(CONFIG_AUFS_FS) += aufs.o -+aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ -+ wkq.o vfsub.o dcsub.o \ -+ cpup.o whout.o wbr_policy.o \ -+ dinfo.o dentry.o \ -+ dynop.o \ -+ finfo.o file.o f_op.o \ -+ dir.o vdir.o \ -+ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ -+ mvdown.o ioctl.o -+ -+# all are boolean -+aufs-$(CONFIG_PROC_FS) += procfs.o plink.o -+aufs-$(CONFIG_SYSFS) += sysfs.o -+aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o -+aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o -+aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o -+aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o -+aufs-$(CONFIG_AUFS_EXPORT) += export.o -+aufs-$(CONFIG_AUFS_XATTR) += xattr.o -+aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o -+aufs-$(CONFIG_AUFS_FHSM) += fhsm.o -+aufs-$(CONFIG_AUFS_POLL) += poll.o -+aufs-$(CONFIG_AUFS_RDU) += rdu.o -+aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o -+aufs-$(CONFIG_AUFS_DEBUG) += debug.o -+aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o -diff -Nur linux-4.0.4.orig/fs/aufs/module.c linux-4.0.4/fs/aufs/module.c ---- linux-4.0.4.orig/fs/aufs/module.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/module.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,196 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * module global variables and operations -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp) -+{ -+ if (new_sz <= nused) -+ return p; -+ -+ p = krealloc(p, new_sz, gfp); -+ if (p) -+ memset(p + nused, 0, new_sz - nused); -+ return p; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * aufs caches -+ */ -+struct kmem_cache *au_cachep[AuCache_Last]; -+static int __init au_cache_init(void) -+{ -+ au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); -+ if (au_cachep[AuCache_DINFO]) -+ /* SLAB_DESTROY_BY_RCU */ -+ au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, -+ au_icntnr_init_once); -+ if (au_cachep[AuCache_ICNTNR]) -+ au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo, -+ au_fi_init_once); -+ if (au_cachep[AuCache_FINFO]) -+ au_cachep[AuCache_VDIR] = AuCache(au_vdir); -+ if (au_cachep[AuCache_VDIR]) -+ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); -+ if (au_cachep[AuCache_DEHSTR]) -+ return 0; -+ -+ return -ENOMEM; -+} -+ -+static void au_cache_fin(void) -+{ -+ int i; -+ -+ /* -+ * Make sure all delayed rcu free inodes are flushed before we -+ * destroy cache. -+ */ -+ rcu_barrier(); -+ -+ /* excluding AuCache_HNOTIFY */ -+ BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last); -+ for (i = 0; i < AuCache_HNOTIFY; i++) -+ if (au_cachep[i]) { -+ kmem_cache_destroy(au_cachep[i]); -+ au_cachep[i] = NULL; -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_dir_roflags; -+ -+#ifdef CONFIG_AUFS_SBILIST -+/* -+ * iterate_supers_type() doesn't protect us from -+ * remounting (branch management) -+ */ -+struct au_splhead au_sbilist; -+#endif -+ -+struct lock_class_key au_lc_key[AuLcKey_Last]; -+ -+/* -+ * functions for module interface. -+ */ -+MODULE_LICENSE("GPL"); -+/* MODULE_LICENSE("GPL v2"); */ -+MODULE_AUTHOR("Junjiro R. Okajima "); -+MODULE_DESCRIPTION(AUFS_NAME -+ " -- Advanced multi layered unification filesystem"); -+MODULE_VERSION(AUFS_VERSION); -+ -+/* this module parameter has no meaning when SYSFS is disabled */ -+int sysaufs_brs = 1; -+MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); -+module_param_named(brs, sysaufs_brs, int, S_IRUGO); -+ -+/* this module parameter has no meaning when USER_NS is disabled */ -+static bool au_userns; -+MODULE_PARM_DESC(allow_userns, "allow unprivileged to mount under userns"); -+module_param_named(allow_userns, au_userns, bool, S_IRUGO); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ -+ -+int au_seq_path(struct seq_file *seq, struct path *path) -+{ -+ return seq_path(seq, path, au_esc_chars); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int __init aufs_init(void) -+{ -+ int err, i; -+ char *p; -+ -+ p = au_esc_chars; -+ for (i = 1; i <= ' '; i++) -+ *p++ = i; -+ *p++ = '\\'; -+ *p++ = '\x7f'; -+ *p = 0; -+ -+ au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); -+ -+ au_sbilist_init(); -+ sysaufs_brs_init(); -+ au_debug_init(); -+ au_dy_init(); -+ err = sysaufs_init(); -+ if (unlikely(err)) -+ goto out; -+ err = au_procfs_init(); -+ if (unlikely(err)) -+ goto out_sysaufs; -+ err = au_wkq_init(); -+ if (unlikely(err)) -+ goto out_procfs; -+ err = au_loopback_init(); -+ if (unlikely(err)) -+ goto out_wkq; -+ err = au_hnotify_init(); -+ if (unlikely(err)) -+ goto out_loopback; -+ err = au_sysrq_init(); -+ if (unlikely(err)) -+ goto out_hin; -+ err = au_cache_init(); -+ if (unlikely(err)) -+ goto out_sysrq; -+ -+ aufs_fs_type.fs_flags |= au_userns ? FS_USERNS_MOUNT : 0; -+ err = register_filesystem(&aufs_fs_type); -+ if (unlikely(err)) -+ goto out_cache; -+ -+ /* since we define pr_fmt, call printk directly */ -+ printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n"); -+ goto out; /* success */ -+ -+out_cache: -+ au_cache_fin(); -+out_sysrq: -+ au_sysrq_fin(); -+out_hin: -+ au_hnotify_fin(); -+out_loopback: -+ au_loopback_fin(); -+out_wkq: -+ au_wkq_fin(); -+out_procfs: -+ au_procfs_fin(); -+out_sysaufs: -+ sysaufs_fin(); -+ au_dy_fin(); -+out: -+ return err; -+} -+ -+static void __exit aufs_exit(void) -+{ -+ unregister_filesystem(&aufs_fs_type); -+ au_cache_fin(); -+ au_sysrq_fin(); -+ au_hnotify_fin(); -+ au_loopback_fin(); -+ au_wkq_fin(); -+ au_procfs_fin(); -+ sysaufs_fin(); -+ au_dy_fin(); -+} -+ -+module_init(aufs_init); -+module_exit(aufs_exit); -diff -Nur linux-4.0.4.orig/fs/aufs/module.h linux-4.0.4/fs/aufs/module.h ---- linux-4.0.4.orig/fs/aufs/module.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/module.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,91 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * module initialization and module-global -+ */ -+ -+#ifndef __AUFS_MODULE_H__ -+#define __AUFS_MODULE_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+struct path; -+struct seq_file; -+ -+/* module parameters */ -+extern int sysaufs_brs; -+ -+/* ---------------------------------------------------------------------- */ -+ -+extern int au_dir_roflags; -+ -+enum { -+ AuLcNonDir_FIINFO, -+ AuLcNonDir_DIINFO, -+ AuLcNonDir_IIINFO, -+ -+ AuLcDir_FIINFO, -+ AuLcDir_DIINFO, -+ AuLcDir_IIINFO, -+ -+ AuLcSymlink_DIINFO, -+ AuLcSymlink_IIINFO, -+ -+ AuLcKey_Last -+}; -+extern struct lock_class_key au_lc_key[AuLcKey_Last]; -+ -+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); -+int au_seq_path(struct seq_file *seq, struct path *path); -+ -+#ifdef CONFIG_PROC_FS -+/* procfs.c */ -+int __init au_procfs_init(void); -+void au_procfs_fin(void); -+#else -+AuStubInt0(au_procfs_init, void); -+AuStubVoid(au_procfs_fin, void); -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* kmem cache */ -+enum { -+ AuCache_DINFO, -+ AuCache_ICNTNR, -+ AuCache_FINFO, -+ AuCache_VDIR, -+ AuCache_DEHSTR, -+ AuCache_HNOTIFY, /* must be last */ -+ AuCache_Last -+}; -+ -+#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) -+#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) -+#define AuCacheCtor(type, ctor) \ -+ kmem_cache_create(#type, sizeof(struct type), \ -+ __alignof__(struct type), AuCacheFlags, ctor) -+ -+extern struct kmem_cache *au_cachep[]; -+ -+#define AuCacheFuncs(name, index) \ -+static inline struct au_##name *au_cache_alloc_##name(void) \ -+{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \ -+static inline void au_cache_free_##name(struct au_##name *p) \ -+{ kmem_cache_free(au_cachep[AuCache_##index], p); } -+ -+AuCacheFuncs(dinfo, DINFO); -+AuCacheFuncs(icntnr, ICNTNR); -+AuCacheFuncs(finfo, FINFO); -+AuCacheFuncs(vdir, VDIR); -+AuCacheFuncs(vdir_dehstr, DEHSTR); -+#ifdef CONFIG_AUFS_HNOTIFY -+AuCacheFuncs(hnotify, HNOTIFY); -+#endif -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_MODULE_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/mvdown.c linux-4.0.4/fs/aufs/mvdown.c ---- linux-4.0.4.orig/fs/aufs/mvdown.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/mvdown.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,681 @@ -+/* -+ * Copyright (C) 2011-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * move-down, opposite of copy-up -+ */ -+ -+#include "aufs.h" -+ -+struct au_mvd_args { -+ struct { -+ struct super_block *h_sb; -+ struct dentry *h_parent; -+ struct au_hinode *hdir; -+ struct inode *h_dir, *h_inode; -+ struct au_pin pin; -+ } info[AUFS_MVDOWN_NARRAY]; -+ -+ struct aufs_mvdown mvdown; -+ struct dentry *dentry, *parent; -+ struct inode *inode, *dir; -+ struct super_block *sb; -+ aufs_bindex_t bopq, bwh, bfound; -+ unsigned char rename_lock; -+}; -+ -+#define mvd_errno mvdown.au_errno -+#define mvd_bsrc mvdown.stbr[AUFS_MVDOWN_UPPER].bindex -+#define mvd_src_brid mvdown.stbr[AUFS_MVDOWN_UPPER].brid -+#define mvd_bdst mvdown.stbr[AUFS_MVDOWN_LOWER].bindex -+#define mvd_dst_brid mvdown.stbr[AUFS_MVDOWN_LOWER].brid -+ -+#define mvd_h_src_sb info[AUFS_MVDOWN_UPPER].h_sb -+#define mvd_h_src_parent info[AUFS_MVDOWN_UPPER].h_parent -+#define mvd_hdir_src info[AUFS_MVDOWN_UPPER].hdir -+#define mvd_h_src_dir info[AUFS_MVDOWN_UPPER].h_dir -+#define mvd_h_src_inode info[AUFS_MVDOWN_UPPER].h_inode -+#define mvd_pin_src info[AUFS_MVDOWN_UPPER].pin -+ -+#define mvd_h_dst_sb info[AUFS_MVDOWN_LOWER].h_sb -+#define mvd_h_dst_parent info[AUFS_MVDOWN_LOWER].h_parent -+#define mvd_hdir_dst info[AUFS_MVDOWN_LOWER].hdir -+#define mvd_h_dst_dir info[AUFS_MVDOWN_LOWER].h_dir -+#define mvd_h_dst_inode info[AUFS_MVDOWN_LOWER].h_inode -+#define mvd_pin_dst info[AUFS_MVDOWN_LOWER].pin -+ -+#define AU_MVD_PR(flag, ...) do { \ -+ if (flag) \ -+ pr_err(__VA_ARGS__); \ -+ } while (0) -+ -+static int find_lower_writable(struct au_mvd_args *a) -+{ -+ struct super_block *sb; -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ -+ sb = a->sb; -+ bindex = a->mvd_bsrc; -+ bend = au_sbend(sb); -+ if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) -+ for (bindex++; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm) -+ && (!(au_br_sb(br)->s_flags & MS_RDONLY))) -+ return bindex; -+ } -+ else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) -+ for (bindex++; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!au_br_rdonly(br)) -+ return bindex; -+ } -+ else -+ for (bindex++; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!(au_br_sb(br)->s_flags & MS_RDONLY)) { -+ if (au_br_rdonly(br)) -+ a->mvdown.flags -+ |= AUFS_MVDOWN_ROLOWER_R; -+ return bindex; -+ } -+ } -+ -+ return -1; -+} -+ -+/* make the parent dir on bdst */ -+static int au_do_mkdir(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ -+ err = 0; -+ a->mvd_hdir_src = au_hi(a->dir, a->mvd_bsrc); -+ a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); -+ a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); -+ a->mvd_h_dst_parent = NULL; -+ if (au_dbend(a->parent) >= a->mvd_bdst) -+ a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); -+ if (!a->mvd_h_dst_parent) { -+ err = au_cpdown_dirs(a->dentry, a->mvd_bdst); -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "cpdown_dirs failed\n"); -+ goto out; -+ } -+ a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* lock them all */ -+static int au_do_lock(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct dentry *h_trap; -+ -+ a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc); -+ a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst); -+ err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst, -+ au_opt_udba(a->sb), -+ AuPin_MNT_WRITE | AuPin_DI_LOCKED); -+ AuTraceErr(err); -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "pin_dst failed\n"); -+ goto out; -+ } -+ -+ if (a->mvd_h_src_sb != a->mvd_h_dst_sb) { -+ a->rename_lock = 0; -+ au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, -+ AuLsc_DI_PARENT, AuLsc_I_PARENT3, -+ au_opt_udba(a->sb), -+ AuPin_MNT_WRITE | AuPin_DI_LOCKED); -+ err = au_do_pin(&a->mvd_pin_src); -+ AuTraceErr(err); -+ a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode; -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "pin_src failed\n"); -+ goto out_dst; -+ } -+ goto out; /* success */ -+ } -+ -+ a->rename_lock = 1; -+ au_pin_hdir_unlock(&a->mvd_pin_dst); -+ err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, -+ au_opt_udba(a->sb), -+ AuPin_MNT_WRITE | AuPin_DI_LOCKED); -+ AuTraceErr(err); -+ a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode; -+ if (unlikely(err)) { -+ AU_MVD_PR(dmsg, "pin_src failed\n"); -+ au_pin_hdir_lock(&a->mvd_pin_dst); -+ goto out_dst; -+ } -+ au_pin_hdir_unlock(&a->mvd_pin_src); -+ h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, -+ a->mvd_h_dst_parent, a->mvd_hdir_dst); -+ if (h_trap) { -+ err = (h_trap != a->mvd_h_src_parent); -+ if (err) -+ err = (h_trap != a->mvd_h_dst_parent); -+ } -+ BUG_ON(err); /* it should never happen */ -+ if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) { -+ err = -EBUSY; -+ AuTraceErr(err); -+ vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, -+ a->mvd_h_dst_parent, a->mvd_hdir_dst); -+ au_pin_hdir_lock(&a->mvd_pin_src); -+ au_unpin(&a->mvd_pin_src); -+ au_pin_hdir_lock(&a->mvd_pin_dst); -+ goto out_dst; -+ } -+ goto out; /* success */ -+ -+out_dst: -+ au_unpin(&a->mvd_pin_dst); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ if (!a->rename_lock) -+ au_unpin(&a->mvd_pin_src); -+ else { -+ vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, -+ a->mvd_h_dst_parent, a->mvd_hdir_dst); -+ au_pin_hdir_lock(&a->mvd_pin_src); -+ au_unpin(&a->mvd_pin_src); -+ au_pin_hdir_lock(&a->mvd_pin_dst); -+ } -+ au_unpin(&a->mvd_pin_dst); -+} -+ -+/* copy-down the file */ -+static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct au_cp_generic cpg = { -+ .dentry = a->dentry, -+ .bdst = a->mvd_bdst, -+ .bsrc = a->mvd_bsrc, -+ .len = -1, -+ .pin = &a->mvd_pin_dst, -+ .flags = AuCpup_DTIME | AuCpup_HOPEN -+ }; -+ -+ AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); -+ if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) -+ au_fset_cpup(cpg.flags, OVERWRITE); -+ if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) -+ au_fset_cpup(cpg.flags, RWDST); -+ err = au_sio_cpdown_simple(&cpg); -+ if (unlikely(err)) -+ AU_MVD_PR(dmsg, "cpdown failed\n"); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * unlink the whiteout on bdst if exist which may be created by UDBA while we -+ * were sleeping -+ */ -+static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct path h_path; -+ struct au_branch *br; -+ struct inode *delegated; -+ -+ br = au_sbr(a->sb, a->mvd_bdst); -+ h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) { -+ AU_MVD_PR(dmsg, "wh_lkup failed\n"); -+ goto out; -+ } -+ -+ err = 0; -+ if (h_path.dentry->d_inode) { -+ h_path.mnt = au_br_mnt(br); -+ delegated = NULL; -+ err = vfsub_unlink(a->mvd_h_dst_parent->d_inode, &h_path, -+ &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) -+ AU_MVD_PR(dmsg, "wh_unlink failed\n"); -+ } -+ dput(h_path.dentry); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* -+ * unlink the topmost h_dentry -+ */ -+static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct path h_path; -+ struct inode *delegated; -+ -+ h_path.mnt = au_sbr_mnt(a->sb, a->mvd_bsrc); -+ h_path.dentry = au_h_dptr(a->dentry, a->mvd_bsrc); -+ delegated = NULL; -+ err = vfsub_unlink(a->mvd_h_src_dir, &h_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) -+ AU_MVD_PR(dmsg, "unlink failed\n"); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+/* Since mvdown succeeded, we ignore an error of this function */ -+static void au_do_stfs(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct au_branch *br; -+ -+ a->mvdown.flags |= AUFS_MVDOWN_STFS_FAILED; -+ br = au_sbr(a->sb, a->mvd_bsrc); -+ err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_UPPER].stfs); -+ if (!err) { -+ br = au_sbr(a->sb, a->mvd_bdst); -+ a->mvdown.stbr[AUFS_MVDOWN_LOWER].brid = br->br_id; -+ err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_LOWER].stfs); -+ } -+ if (!err) -+ a->mvdown.flags &= ~AUFS_MVDOWN_STFS_FAILED; -+ else -+ AU_MVD_PR(dmsg, "statfs failed (%d), ignored\n", err); -+} -+ -+/* -+ * copy-down the file and unlink the bsrc file. -+ * - unlink the bdst whout if exist -+ * - copy-down the file (with whtmp name and rename) -+ * - unlink the bsrc file -+ */ -+static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ -+ err = au_do_mkdir(dmsg, a); -+ if (!err) -+ err = au_do_lock(dmsg, a); -+ if (unlikely(err)) -+ goto out; -+ -+ /* -+ * do not revert the activities we made on bdst since they should be -+ * harmless in aufs. -+ */ -+ -+ err = au_do_cpdown(dmsg, a); -+ if (!err) -+ err = au_do_unlink_wh(dmsg, a); -+ if (!err && !(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) -+ err = au_do_unlink(dmsg, a); -+ if (unlikely(err)) -+ goto out_unlock; -+ -+ AuDbg("%pd2, 0x%x, %d --> %d\n", -+ a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst); -+ if (find_lower_writable(a) < 0) -+ a->mvdown.flags |= AUFS_MVDOWN_BOTTOM; -+ -+ if (a->mvdown.flags & AUFS_MVDOWN_STFS) -+ au_do_stfs(dmsg, a); -+ -+ /* maintain internal array */ -+ if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { -+ au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); -+ au_set_dbstart(a->dentry, a->mvd_bdst); -+ au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); -+ au_set_ibstart(a->inode, a->mvd_bdst); -+ } -+ if (au_dbend(a->dentry) < a->mvd_bdst) -+ au_set_dbend(a->dentry, a->mvd_bdst); -+ if (au_ibend(a->inode) < a->mvd_bdst) -+ au_set_ibend(a->inode, a->mvd_bdst); -+ -+out_unlock: -+ au_do_unlock(dmsg, a); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* make sure the file is idle */ -+static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err, plinked; -+ -+ err = 0; -+ plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); -+ if (au_dbstart(a->dentry) == a->mvd_bsrc -+ && au_dcount(a->dentry) == 1 -+ && atomic_read(&a->inode->i_count) == 1 -+ /* && a->mvd_h_src_inode->i_nlink == 1 */ -+ && (!plinked || !au_plink_test(a->inode)) -+ && a->inode->i_nlink == 1) -+ goto out; -+ -+ err = -EBUSY; -+ AU_MVD_PR(dmsg, -+ "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", -+ a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry), -+ atomic_read(&a->inode->i_count), a->inode->i_nlink, -+ a->mvd_h_src_inode->i_nlink, -+ plinked, plinked ? au_plink_test(a->inode) : 0); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* make sure the parent dir is fine */ -+static int au_mvd_args_parent(const unsigned char dmsg, -+ struct au_mvd_args *a) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ -+ err = 0; -+ if (unlikely(au_alive_dir(a->parent))) { -+ err = -ENOENT; -+ AU_MVD_PR(dmsg, "parent dir is dead\n"); -+ goto out; -+ } -+ -+ a->bopq = au_dbdiropq(a->parent); -+ bindex = au_wbr_nonopq(a->dentry, a->mvd_bdst); -+ AuDbg("b%d\n", bindex); -+ if (unlikely((bindex >= 0 && bindex < a->mvd_bdst) -+ || (a->bopq != -1 && a->bopq < a->mvd_bdst))) { -+ err = -EINVAL; -+ a->mvd_errno = EAU_MVDOWN_OPAQUE; -+ AU_MVD_PR(dmsg, "ancestor is opaque b%d, b%d\n", -+ a->bopq, a->mvd_bdst); -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_mvd_args_intermediate(const unsigned char dmsg, -+ struct au_mvd_args *a) -+{ -+ int err; -+ struct au_dinfo *dinfo, *tmp; -+ -+ /* lookup the next lower positive entry */ -+ err = -ENOMEM; -+ tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); -+ if (unlikely(!tmp)) -+ goto out; -+ -+ a->bfound = -1; -+ a->bwh = -1; -+ dinfo = au_di(a->dentry); -+ au_di_cp(tmp, dinfo); -+ au_di_swap(tmp, dinfo); -+ -+ /* returns the number of positive dentries */ -+ err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, /*type*/0); -+ if (!err) -+ a->bwh = au_dbwh(a->dentry); -+ else if (err > 0) -+ a->bfound = au_dbstart(a->dentry); -+ -+ au_di_swap(tmp, dinfo); -+ au_rw_write_unlock(&tmp->di_rwsem); -+ au_di_free(tmp); -+ if (unlikely(err < 0)) -+ AU_MVD_PR(dmsg, "failed look-up lower\n"); -+ -+ /* -+ * here, we have these cases. -+ * bfound == -1 -+ * no positive dentry under bsrc. there are more sub-cases. -+ * bwh < 0 -+ * there no whiteout, we can safely move-down. -+ * bwh <= bsrc -+ * impossible -+ * bsrc < bwh && bwh < bdst -+ * there is a whiteout on RO branch. cannot proceed. -+ * bwh == bdst -+ * there is a whiteout on the RW target branch. it should -+ * be removed. -+ * bdst < bwh -+ * there is a whiteout somewhere unrelated branch. -+ * -1 < bfound && bfound <= bsrc -+ * impossible. -+ * bfound < bdst -+ * found, but it is on RO branch between bsrc and bdst. cannot -+ * proceed. -+ * bfound == bdst -+ * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return -+ * error. -+ * bdst < bfound -+ * found, after we create the file on bdst, it will be hidden. -+ */ -+ -+ AuDebugOn(a->bfound == -1 -+ && a->bwh != -1 -+ && a->bwh <= a->mvd_bsrc); -+ AuDebugOn(-1 < a->bfound -+ && a->bfound <= a->mvd_bsrc); -+ -+ err = -EINVAL; -+ if (a->bfound == -1 -+ && a->mvd_bsrc < a->bwh -+ && a->bwh != -1 -+ && a->bwh < a->mvd_bdst) { -+ a->mvd_errno = EAU_MVDOWN_WHITEOUT; -+ AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", -+ a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); -+ goto out; -+ } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { -+ a->mvd_errno = EAU_MVDOWN_UPPER; -+ AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", -+ a->mvd_bdst, a->bfound); -+ goto out; -+ } -+ -+ err = 0; /* success */ -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_mvd_args_exist(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ -+ err = 0; -+ if (!(a->mvdown.flags & AUFS_MVDOWN_OWLOWER) -+ && a->bfound == a->mvd_bdst) -+ err = -EEXIST; -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) -+{ -+ int err; -+ struct au_branch *br; -+ -+ err = -EISDIR; -+ if (unlikely(S_ISDIR(a->inode->i_mode))) -+ goto out; -+ -+ err = -EINVAL; -+ if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) -+ a->mvd_bsrc = au_ibstart(a->inode); -+ else { -+ a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); -+ if (unlikely(a->mvd_bsrc < 0 -+ || (a->mvd_bsrc < au_dbstart(a->dentry) -+ || au_dbend(a->dentry) < a->mvd_bsrc -+ || !au_h_dptr(a->dentry, a->mvd_bsrc)) -+ || (a->mvd_bsrc < au_ibstart(a->inode) -+ || au_ibend(a->inode) < a->mvd_bsrc -+ || !au_h_iptr(a->inode, a->mvd_bsrc)))) { -+ a->mvd_errno = EAU_MVDOWN_NOUPPER; -+ AU_MVD_PR(dmsg, "no upper\n"); -+ goto out; -+ } -+ } -+ if (unlikely(a->mvd_bsrc == au_sbend(a->sb))) { -+ a->mvd_errno = EAU_MVDOWN_BOTTOM; -+ AU_MVD_PR(dmsg, "on the bottom\n"); -+ goto out; -+ } -+ a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); -+ br = au_sbr(a->sb, a->mvd_bsrc); -+ err = au_br_rdonly(br); -+ if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { -+ if (unlikely(err)) -+ goto out; -+ } else if (!(vfsub_native_ro(a->mvd_h_src_inode) -+ || IS_APPEND(a->mvd_h_src_inode))) { -+ if (err) -+ a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; -+ /* go on */ -+ } else -+ goto out; -+ -+ err = -EINVAL; -+ if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { -+ a->mvd_bdst = find_lower_writable(a); -+ if (unlikely(a->mvd_bdst < 0)) { -+ a->mvd_errno = EAU_MVDOWN_BOTTOM; -+ AU_MVD_PR(dmsg, "no writable lower branch\n"); -+ goto out; -+ } -+ } else { -+ a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); -+ if (unlikely(a->mvd_bdst < 0 -+ || au_sbend(a->sb) < a->mvd_bdst)) { -+ a->mvd_errno = EAU_MVDOWN_NOLOWERBR; -+ AU_MVD_PR(dmsg, "no lower brid\n"); -+ goto out; -+ } -+ } -+ -+ err = au_mvd_args_busy(dmsg, a); -+ if (!err) -+ err = au_mvd_args_parent(dmsg, a); -+ if (!err) -+ err = au_mvd_args_intermediate(dmsg, a); -+ if (!err) -+ err = au_mvd_args_exist(dmsg, a); -+ if (!err) -+ AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg) -+{ -+ int err, e; -+ unsigned char dmsg; -+ struct au_mvd_args *args; -+ -+ err = -EPERM; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = -ENOMEM; -+ args = kmalloc(sizeof(*args), GFP_NOFS); -+ if (unlikely(!args)) -+ goto out; -+ -+ err = copy_from_user(&args->mvdown, uarg, sizeof(args->mvdown)); -+ if (!err) -+ err = !access_ok(VERIFY_WRITE, uarg, sizeof(*uarg)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out_free; -+ } -+ AuDbg("flags 0x%x\n", args->mvdown.flags); -+ args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R); -+ args->mvdown.au_errno = 0; -+ args->dentry = dentry; -+ args->inode = dentry->d_inode; -+ args->sb = dentry->d_sb; -+ -+ err = -ENOENT; -+ dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); -+ args->parent = dget_parent(dentry); -+ args->dir = args->parent->d_inode; -+ mutex_lock_nested(&args->dir->i_mutex, I_MUTEX_PARENT); -+ dput(args->parent); -+ if (unlikely(args->parent != dentry->d_parent)) { -+ AU_MVD_PR(dmsg, "parent dir is moved\n"); -+ goto out_dir; -+ } -+ -+ mutex_lock_nested(&args->inode->i_mutex, I_MUTEX_CHILD); -+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH); -+ if (unlikely(err)) -+ goto out_inode; -+ -+ di_write_lock_parent(args->parent); -+ err = au_mvd_args(dmsg, args); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ err = au_do_mvdown(dmsg, args); -+ if (unlikely(err)) -+ goto out_parent; -+ -+ au_cpup_attr_timesizes(args->dir); -+ au_cpup_attr_timesizes(args->inode); -+ au_cpup_igen(args->inode, au_h_iptr(args->inode, args->mvd_bdst)); -+ /* au_digen_dec(dentry); */ -+ -+out_parent: -+ di_write_unlock(args->parent); -+ aufs_read_unlock(dentry, AuLock_DW); -+out_inode: -+ mutex_unlock(&args->inode->i_mutex); -+out_dir: -+ mutex_unlock(&args->dir->i_mutex); -+out_free: -+ e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); -+ if (unlikely(e)) -+ err = -EFAULT; -+ kfree(args); -+out: -+ AuTraceErr(err); -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/opts.c linux-4.0.4/fs/aufs/opts.c ---- linux-4.0.4.orig/fs/aufs/opts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/opts.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,1822 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * mount options/flags -+ */ -+ -+#include -+#include /* a distribution requires */ -+#include -+#include "aufs.h" -+ -+/* ---------------------------------------------------------------------- */ -+ -+enum { -+ Opt_br, -+ Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend, -+ Opt_idel, Opt_imod, -+ Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, -+ Opt_rdblk_def, Opt_rdhash_def, -+ Opt_xino, Opt_noxino, -+ Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino, -+ Opt_trunc_xino_path, Opt_itrunc_xino, -+ Opt_trunc_xib, Opt_notrunc_xib, -+ Opt_shwh, Opt_noshwh, -+ Opt_plink, Opt_noplink, Opt_list_plink, -+ Opt_udba, -+ Opt_dio, Opt_nodio, -+ Opt_diropq_a, Opt_diropq_w, -+ Opt_warn_perm, Opt_nowarn_perm, -+ Opt_wbr_copyup, Opt_wbr_create, -+ Opt_fhsm_sec, -+ Opt_verbose, Opt_noverbose, -+ Opt_sum, Opt_nosum, Opt_wsum, -+ Opt_dirperm1, Opt_nodirperm1, -+ Opt_acl, Opt_noacl, -+ Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err -+}; -+ -+static match_table_t options = { -+ {Opt_br, "br=%s"}, -+ {Opt_br, "br:%s"}, -+ -+ {Opt_add, "add=%d:%s"}, -+ {Opt_add, "add:%d:%s"}, -+ {Opt_add, "ins=%d:%s"}, -+ {Opt_add, "ins:%d:%s"}, -+ {Opt_append, "append=%s"}, -+ {Opt_append, "append:%s"}, -+ {Opt_prepend, "prepend=%s"}, -+ {Opt_prepend, "prepend:%s"}, -+ -+ {Opt_del, "del=%s"}, -+ {Opt_del, "del:%s"}, -+ /* {Opt_idel, "idel:%d"}, */ -+ {Opt_mod, "mod=%s"}, -+ {Opt_mod, "mod:%s"}, -+ /* {Opt_imod, "imod:%d:%s"}, */ -+ -+ {Opt_dirwh, "dirwh=%d"}, -+ -+ {Opt_xino, "xino=%s"}, -+ {Opt_noxino, "noxino"}, -+ {Opt_trunc_xino, "trunc_xino"}, -+ {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"}, -+ {Opt_notrunc_xino, "notrunc_xino"}, -+ {Opt_trunc_xino_path, "trunc_xino=%s"}, -+ {Opt_itrunc_xino, "itrunc_xino=%d"}, -+ /* {Opt_zxino, "zxino=%s"}, */ -+ {Opt_trunc_xib, "trunc_xib"}, -+ {Opt_notrunc_xib, "notrunc_xib"}, -+ -+#ifdef CONFIG_PROC_FS -+ {Opt_plink, "plink"}, -+#else -+ {Opt_ignore_silent, "plink"}, -+#endif -+ -+ {Opt_noplink, "noplink"}, -+ -+#ifdef CONFIG_AUFS_DEBUG -+ {Opt_list_plink, "list_plink"}, -+#endif -+ -+ {Opt_udba, "udba=%s"}, -+ -+ {Opt_dio, "dio"}, -+ {Opt_nodio, "nodio"}, -+ -+#ifdef CONFIG_AUFS_FHSM -+ {Opt_fhsm_sec, "fhsm_sec=%d"}, -+#else -+ {Opt_ignore_silent, "fhsm_sec=%d"}, -+#endif -+ -+ {Opt_diropq_a, "diropq=always"}, -+ {Opt_diropq_a, "diropq=a"}, -+ {Opt_diropq_w, "diropq=whiteouted"}, -+ {Opt_diropq_w, "diropq=w"}, -+ -+ {Opt_warn_perm, "warn_perm"}, -+ {Opt_nowarn_perm, "nowarn_perm"}, -+ -+ /* keep them temporary */ -+ {Opt_ignore_silent, "nodlgt"}, -+ {Opt_ignore_silent, "clean_plink"}, -+ -+#ifdef CONFIG_AUFS_SHWH -+ {Opt_shwh, "shwh"}, -+#endif -+ {Opt_noshwh, "noshwh"}, -+ -+ {Opt_dirperm1, "dirperm1"}, -+ {Opt_nodirperm1, "nodirperm1"}, -+ -+ {Opt_verbose, "verbose"}, -+ {Opt_verbose, "v"}, -+ {Opt_noverbose, "noverbose"}, -+ {Opt_noverbose, "quiet"}, -+ {Opt_noverbose, "q"}, -+ {Opt_noverbose, "silent"}, -+ -+ {Opt_sum, "sum"}, -+ {Opt_nosum, "nosum"}, -+ {Opt_wsum, "wsum"}, -+ -+ {Opt_rdcache, "rdcache=%d"}, -+ {Opt_rdblk, "rdblk=%d"}, -+ {Opt_rdblk_def, "rdblk=def"}, -+ {Opt_rdhash, "rdhash=%d"}, -+ {Opt_rdhash_def, "rdhash=def"}, -+ -+ {Opt_wbr_create, "create=%s"}, -+ {Opt_wbr_create, "create_policy=%s"}, -+ {Opt_wbr_copyup, "cpup=%s"}, -+ {Opt_wbr_copyup, "copyup=%s"}, -+ {Opt_wbr_copyup, "copyup_policy=%s"}, -+ -+ /* generic VFS flag */ -+#ifdef CONFIG_FS_POSIX_ACL -+ {Opt_acl, "acl"}, -+ {Opt_noacl, "noacl"}, -+#else -+ {Opt_ignore_silent, "acl"}, -+ {Opt_ignore_silent, "noacl"}, -+#endif -+ -+ /* internal use for the scripts */ -+ {Opt_ignore_silent, "si=%s"}, -+ -+ {Opt_br, "dirs=%s"}, -+ {Opt_ignore, "debug=%d"}, -+ {Opt_ignore, "delete=whiteout"}, -+ {Opt_ignore, "delete=all"}, -+ {Opt_ignore, "imap=%s"}, -+ -+ /* temporary workaround, due to old mount(8)? */ -+ {Opt_ignore_silent, "relatime"}, -+ -+ {Opt_err, NULL} -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static const char *au_parser_pattern(int val, match_table_t tbl) -+{ -+ struct match_token *p; -+ -+ p = tbl; -+ while (p->pattern) { -+ if (p->token == val) -+ return p->pattern; -+ p++; -+ } -+ BUG(); -+ return "??"; -+} -+ -+static const char *au_optstr(int *val, match_table_t tbl) -+{ -+ struct match_token *p; -+ int v; -+ -+ v = *val; -+ if (!v) -+ goto out; -+ p = tbl; -+ while (p->pattern) { -+ if (p->token -+ && (v & p->token) == p->token) { -+ *val &= ~p->token; -+ return p->pattern; -+ } -+ p++; -+ } -+ -+out: -+ return NULL; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static match_table_t brperm = { -+ {AuBrPerm_RO, AUFS_BRPERM_RO}, -+ {AuBrPerm_RR, AUFS_BRPERM_RR}, -+ {AuBrPerm_RW, AUFS_BRPERM_RW}, -+ {0, NULL} -+}; -+ -+static match_table_t brattr = { -+ /* general */ -+ {AuBrAttr_COO_REG, AUFS_BRATTR_COO_REG}, -+ {AuBrAttr_COO_ALL, AUFS_BRATTR_COO_ALL}, -+ /* 'unpin' attrib is meaningless since linux-3.18-rc1 */ -+ {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, -+#ifdef CONFIG_AUFS_FHSM -+ {AuBrAttr_FHSM, AUFS_BRATTR_FHSM}, -+#endif -+#ifdef CONFIG_AUFS_XATTR -+ {AuBrAttr_ICEX, AUFS_BRATTR_ICEX}, -+ {AuBrAttr_ICEX_SEC, AUFS_BRATTR_ICEX_SEC}, -+ {AuBrAttr_ICEX_SYS, AUFS_BRATTR_ICEX_SYS}, -+ {AuBrAttr_ICEX_TR, AUFS_BRATTR_ICEX_TR}, -+ {AuBrAttr_ICEX_USR, AUFS_BRATTR_ICEX_USR}, -+ {AuBrAttr_ICEX_OTH, AUFS_BRATTR_ICEX_OTH}, -+#endif -+ -+ /* ro/rr branch */ -+ {AuBrRAttr_WH, AUFS_BRRATTR_WH}, -+ -+ /* rw branch */ -+ {AuBrWAttr_MOO, AUFS_BRWATTR_MOO}, -+ {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, -+ -+ {0, NULL} -+}; -+ -+static int br_attr_val(char *str, match_table_t table, substring_t args[]) -+{ -+ int attr, v; -+ char *p; -+ -+ attr = 0; -+ do { -+ p = strchr(str, '+'); -+ if (p) -+ *p = 0; -+ v = match_token(str, table, args); -+ if (v) { -+ if (v & AuBrAttr_CMOO_Mask) -+ attr &= ~AuBrAttr_CMOO_Mask; -+ attr |= v; -+ } else { -+ if (p) -+ *p = '+'; -+ pr_warn("ignored branch attribute %s\n", str); -+ break; -+ } -+ if (p) -+ str = p + 1; -+ } while (p); -+ -+ return attr; -+} -+ -+static int au_do_optstr_br_attr(au_br_perm_str_t *str, int perm) -+{ -+ int sz; -+ const char *p; -+ char *q; -+ -+ q = str->a; -+ *q = 0; -+ p = au_optstr(&perm, brattr); -+ if (p) { -+ sz = strlen(p); -+ memcpy(q, p, sz + 1); -+ q += sz; -+ } else -+ goto out; -+ -+ do { -+ p = au_optstr(&perm, brattr); -+ if (p) { -+ *q++ = '+'; -+ sz = strlen(p); -+ memcpy(q, p, sz + 1); -+ q += sz; -+ } -+ } while (p); -+ -+out: -+ return q - str->a; -+} -+ -+static int noinline_for_stack br_perm_val(char *perm) -+{ -+ int val, bad, sz; -+ char *p; -+ substring_t args[MAX_OPT_ARGS]; -+ au_br_perm_str_t attr; -+ -+ p = strchr(perm, '+'); -+ if (p) -+ *p = 0; -+ val = match_token(perm, brperm, args); -+ if (!val) { -+ if (p) -+ *p = '+'; -+ pr_warn("ignored branch permission %s\n", perm); -+ val = AuBrPerm_RO; -+ goto out; -+ } -+ if (!p) -+ goto out; -+ -+ val |= br_attr_val(p + 1, brattr, args); -+ -+ bad = 0; -+ switch (val & AuBrPerm_Mask) { -+ case AuBrPerm_RO: -+ case AuBrPerm_RR: -+ bad = val & AuBrWAttr_Mask; -+ val &= ~AuBrWAttr_Mask; -+ break; -+ case AuBrPerm_RW: -+ bad = val & AuBrRAttr_Mask; -+ val &= ~AuBrRAttr_Mask; -+ break; -+ } -+ -+ /* -+ * 'unpin' attrib becomes meaningless since linux-3.18-rc1, but aufs -+ * does not treat it as an error, just warning. -+ * this is a tiny guard for the user operation. -+ */ -+ if (val & AuBrAttr_UNPIN) { -+ bad |= AuBrAttr_UNPIN; -+ val &= ~AuBrAttr_UNPIN; -+ } -+ -+ if (unlikely(bad)) { -+ sz = au_do_optstr_br_attr(&attr, bad); -+ AuDebugOn(!sz); -+ pr_warn("ignored branch attribute %s\n", attr.a); -+ } -+ -+out: -+ return val; -+} -+ -+void au_optstr_br_perm(au_br_perm_str_t *str, int perm) -+{ -+ au_br_perm_str_t attr; -+ const char *p; -+ char *q; -+ int sz; -+ -+ q = str->a; -+ p = au_optstr(&perm, brperm); -+ AuDebugOn(!p || !*p); -+ sz = strlen(p); -+ memcpy(q, p, sz + 1); -+ q += sz; -+ -+ sz = au_do_optstr_br_attr(&attr, perm); -+ if (sz) { -+ *q++ = '+'; -+ memcpy(q, attr.a, sz + 1); -+ } -+ -+ AuDebugOn(strlen(str->a) >= sizeof(str->a)); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static match_table_t udbalevel = { -+ {AuOpt_UDBA_REVAL, "reval"}, -+ {AuOpt_UDBA_NONE, "none"}, -+#ifdef CONFIG_AUFS_HNOTIFY -+ {AuOpt_UDBA_HNOTIFY, "notify"}, /* abstraction */ -+#ifdef CONFIG_AUFS_HFSNOTIFY -+ {AuOpt_UDBA_HNOTIFY, "fsnotify"}, -+#endif -+#endif -+ {-1, NULL} -+}; -+ -+static int noinline_for_stack udba_val(char *str) -+{ -+ substring_t args[MAX_OPT_ARGS]; -+ -+ return match_token(str, udbalevel, args); -+} -+ -+const char *au_optstr_udba(int udba) -+{ -+ return au_parser_pattern(udba, udbalevel); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static match_table_t au_wbr_create_policy = { -+ {AuWbrCreate_TDP, "tdp"}, -+ {AuWbrCreate_TDP, "top-down-parent"}, -+ {AuWbrCreate_RR, "rr"}, -+ {AuWbrCreate_RR, "round-robin"}, -+ {AuWbrCreate_MFS, "mfs"}, -+ {AuWbrCreate_MFS, "most-free-space"}, -+ {AuWbrCreate_MFSV, "mfs:%d"}, -+ {AuWbrCreate_MFSV, "most-free-space:%d"}, -+ -+ {AuWbrCreate_MFSRR, "mfsrr:%d"}, -+ {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, -+ {AuWbrCreate_PMFS, "pmfs"}, -+ {AuWbrCreate_PMFSV, "pmfs:%d"}, -+ {AuWbrCreate_PMFSRR, "pmfsrr:%d"}, -+ {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"}, -+ -+ {-1, NULL} -+}; -+ -+/* -+ * cf. linux/lib/parser.c and cmdline.c -+ * gave up calling memparse() since it uses simple_strtoull() instead of -+ * kstrto...(). -+ */ -+static int noinline_for_stack -+au_match_ull(substring_t *s, unsigned long long *result) -+{ -+ int err; -+ unsigned int len; -+ char a[32]; -+ -+ err = -ERANGE; -+ len = s->to - s->from; -+ if (len + 1 <= sizeof(a)) { -+ memcpy(a, s->from, len); -+ a[len] = '\0'; -+ err = kstrtoull(a, 0, result); -+ } -+ return err; -+} -+ -+static int au_wbr_mfs_wmark(substring_t *arg, char *str, -+ struct au_opt_wbr_create *create) -+{ -+ int err; -+ unsigned long long ull; -+ -+ err = 0; -+ if (!au_match_ull(arg, &ull)) -+ create->mfsrr_watermark = ull; -+ else { -+ pr_err("bad integer in %s\n", str); -+ err = -EINVAL; -+ } -+ -+ return err; -+} -+ -+static int au_wbr_mfs_sec(substring_t *arg, char *str, -+ struct au_opt_wbr_create *create) -+{ -+ int n, err; -+ -+ err = 0; -+ if (!match_int(arg, &n) && 0 <= n && n <= AUFS_MFS_MAX_SEC) -+ create->mfs_second = n; -+ else { -+ pr_err("bad integer in %s\n", str); -+ err = -EINVAL; -+ } -+ -+ return err; -+} -+ -+static int noinline_for_stack -+au_wbr_create_val(char *str, struct au_opt_wbr_create *create) -+{ -+ int err, e; -+ substring_t args[MAX_OPT_ARGS]; -+ -+ err = match_token(str, au_wbr_create_policy, args); -+ create->wbr_create = err; -+ switch (err) { -+ case AuWbrCreate_MFSRRV: -+ case AuWbrCreate_PMFSRRV: -+ e = au_wbr_mfs_wmark(&args[0], str, create); -+ if (!e) -+ e = au_wbr_mfs_sec(&args[1], str, create); -+ if (unlikely(e)) -+ err = e; -+ break; -+ case AuWbrCreate_MFSRR: -+ case AuWbrCreate_PMFSRR: -+ e = au_wbr_mfs_wmark(&args[0], str, create); -+ if (unlikely(e)) { -+ err = e; -+ break; -+ } -+ /*FALLTHROUGH*/ -+ case AuWbrCreate_MFS: -+ case AuWbrCreate_PMFS: -+ create->mfs_second = AUFS_MFS_DEF_SEC; -+ break; -+ case AuWbrCreate_MFSV: -+ case AuWbrCreate_PMFSV: -+ e = au_wbr_mfs_sec(&args[0], str, create); -+ if (unlikely(e)) -+ err = e; -+ break; -+ } -+ -+ return err; -+} -+ -+const char *au_optstr_wbr_create(int wbr_create) -+{ -+ return au_parser_pattern(wbr_create, au_wbr_create_policy); -+} -+ -+static match_table_t au_wbr_copyup_policy = { -+ {AuWbrCopyup_TDP, "tdp"}, -+ {AuWbrCopyup_TDP, "top-down-parent"}, -+ {AuWbrCopyup_BUP, "bup"}, -+ {AuWbrCopyup_BUP, "bottom-up-parent"}, -+ {AuWbrCopyup_BU, "bu"}, -+ {AuWbrCopyup_BU, "bottom-up"}, -+ {-1, NULL} -+}; -+ -+static int noinline_for_stack au_wbr_copyup_val(char *str) -+{ -+ substring_t args[MAX_OPT_ARGS]; -+ -+ return match_token(str, au_wbr_copyup_policy, args); -+} -+ -+const char *au_optstr_wbr_copyup(int wbr_copyup) -+{ -+ return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; -+ -+static void dump_opts(struct au_opts *opts) -+{ -+#ifdef CONFIG_AUFS_DEBUG -+ /* reduce stack space */ -+ union { -+ struct au_opt_add *add; -+ struct au_opt_del *del; -+ struct au_opt_mod *mod; -+ struct au_opt_xino *xino; -+ struct au_opt_xino_itrunc *xino_itrunc; -+ struct au_opt_wbr_create *create; -+ } u; -+ struct au_opt *opt; -+ -+ opt = opts->opt; -+ while (opt->type != Opt_tail) { -+ switch (opt->type) { -+ case Opt_add: -+ u.add = &opt->add; -+ AuDbg("add {b%d, %s, 0x%x, %p}\n", -+ u.add->bindex, u.add->pathname, u.add->perm, -+ u.add->path.dentry); -+ break; -+ case Opt_del: -+ case Opt_idel: -+ u.del = &opt->del; -+ AuDbg("del {%s, %p}\n", -+ u.del->pathname, u.del->h_path.dentry); -+ break; -+ case Opt_mod: -+ case Opt_imod: -+ u.mod = &opt->mod; -+ AuDbg("mod {%s, 0x%x, %p}\n", -+ u.mod->path, u.mod->perm, u.mod->h_root); -+ break; -+ case Opt_append: -+ u.add = &opt->add; -+ AuDbg("append {b%d, %s, 0x%x, %p}\n", -+ u.add->bindex, u.add->pathname, u.add->perm, -+ u.add->path.dentry); -+ break; -+ case Opt_prepend: -+ u.add = &opt->add; -+ AuDbg("prepend {b%d, %s, 0x%x, %p}\n", -+ u.add->bindex, u.add->pathname, u.add->perm, -+ u.add->path.dentry); -+ break; -+ case Opt_dirwh: -+ AuDbg("dirwh %d\n", opt->dirwh); -+ break; -+ case Opt_rdcache: -+ AuDbg("rdcache %d\n", opt->rdcache); -+ break; -+ case Opt_rdblk: -+ AuDbg("rdblk %u\n", opt->rdblk); -+ break; -+ case Opt_rdblk_def: -+ AuDbg("rdblk_def\n"); -+ break; -+ case Opt_rdhash: -+ AuDbg("rdhash %u\n", opt->rdhash); -+ break; -+ case Opt_rdhash_def: -+ AuDbg("rdhash_def\n"); -+ break; -+ case Opt_xino: -+ u.xino = &opt->xino; -+ AuDbg("xino {%s %pD}\n", u.xino->path, u.xino->file); -+ break; -+ case Opt_trunc_xino: -+ AuLabel(trunc_xino); -+ break; -+ case Opt_notrunc_xino: -+ AuLabel(notrunc_xino); -+ break; -+ case Opt_trunc_xino_path: -+ case Opt_itrunc_xino: -+ u.xino_itrunc = &opt->xino_itrunc; -+ AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); -+ break; -+ case Opt_noxino: -+ AuLabel(noxino); -+ break; -+ case Opt_trunc_xib: -+ AuLabel(trunc_xib); -+ break; -+ case Opt_notrunc_xib: -+ AuLabel(notrunc_xib); -+ break; -+ case Opt_shwh: -+ AuLabel(shwh); -+ break; -+ case Opt_noshwh: -+ AuLabel(noshwh); -+ break; -+ case Opt_dirperm1: -+ AuLabel(dirperm1); -+ break; -+ case Opt_nodirperm1: -+ AuLabel(nodirperm1); -+ break; -+ case Opt_plink: -+ AuLabel(plink); -+ break; -+ case Opt_noplink: -+ AuLabel(noplink); -+ break; -+ case Opt_list_plink: -+ AuLabel(list_plink); -+ break; -+ case Opt_udba: -+ AuDbg("udba %d, %s\n", -+ opt->udba, au_optstr_udba(opt->udba)); -+ break; -+ case Opt_dio: -+ AuLabel(dio); -+ break; -+ case Opt_nodio: -+ AuLabel(nodio); -+ break; -+ case Opt_diropq_a: -+ AuLabel(diropq_a); -+ break; -+ case Opt_diropq_w: -+ AuLabel(diropq_w); -+ break; -+ case Opt_warn_perm: -+ AuLabel(warn_perm); -+ break; -+ case Opt_nowarn_perm: -+ AuLabel(nowarn_perm); -+ break; -+ case Opt_verbose: -+ AuLabel(verbose); -+ break; -+ case Opt_noverbose: -+ AuLabel(noverbose); -+ break; -+ case Opt_sum: -+ AuLabel(sum); -+ break; -+ case Opt_nosum: -+ AuLabel(nosum); -+ break; -+ case Opt_wsum: -+ AuLabel(wsum); -+ break; -+ case Opt_wbr_create: -+ u.create = &opt->wbr_create; -+ AuDbg("create %d, %s\n", u.create->wbr_create, -+ au_optstr_wbr_create(u.create->wbr_create)); -+ switch (u.create->wbr_create) { -+ case AuWbrCreate_MFSV: -+ case AuWbrCreate_PMFSV: -+ AuDbg("%d sec\n", u.create->mfs_second); -+ break; -+ case AuWbrCreate_MFSRR: -+ AuDbg("%llu watermark\n", -+ u.create->mfsrr_watermark); -+ break; -+ case AuWbrCreate_MFSRRV: -+ case AuWbrCreate_PMFSRRV: -+ AuDbg("%llu watermark, %d sec\n", -+ u.create->mfsrr_watermark, -+ u.create->mfs_second); -+ break; -+ } -+ break; -+ case Opt_wbr_copyup: -+ AuDbg("copyup %d, %s\n", opt->wbr_copyup, -+ au_optstr_wbr_copyup(opt->wbr_copyup)); -+ break; -+ case Opt_fhsm_sec: -+ AuDbg("fhsm_sec %u\n", opt->fhsm_second); -+ break; -+ case Opt_acl: -+ AuLabel(acl); -+ break; -+ case Opt_noacl: -+ AuLabel(noacl); -+ break; -+ default: -+ BUG(); -+ } -+ opt++; -+ } -+#endif -+} -+ -+void au_opts_free(struct au_opts *opts) -+{ -+ struct au_opt *opt; -+ -+ opt = opts->opt; -+ while (opt->type != Opt_tail) { -+ switch (opt->type) { -+ case Opt_add: -+ case Opt_append: -+ case Opt_prepend: -+ path_put(&opt->add.path); -+ break; -+ case Opt_del: -+ case Opt_idel: -+ path_put(&opt->del.h_path); -+ break; -+ case Opt_mod: -+ case Opt_imod: -+ dput(opt->mod.h_root); -+ break; -+ case Opt_xino: -+ fput(opt->xino.file); -+ break; -+ } -+ opt++; -+ } -+} -+ -+static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, -+ aufs_bindex_t bindex) -+{ -+ int err; -+ struct au_opt_add *add = &opt->add; -+ char *p; -+ -+ add->bindex = bindex; -+ add->perm = AuBrPerm_RO; -+ add->pathname = opt_str; -+ p = strchr(opt_str, '='); -+ if (p) { -+ *p++ = 0; -+ if (*p) -+ add->perm = br_perm_val(p); -+ } -+ -+ err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); -+ if (!err) { -+ if (!p) { -+ add->perm = AuBrPerm_RO; -+ if (au_test_fs_rr(add->path.dentry->d_sb)) -+ add->perm = AuBrPerm_RR; -+ else if (!bindex && !(sb_flags & MS_RDONLY)) -+ add->perm = AuBrPerm_RW; -+ } -+ opt->type = Opt_add; -+ goto out; -+ } -+ pr_err("lookup failed %s (%d)\n", add->pathname, err); -+ err = -EINVAL; -+ -+out: -+ return err; -+} -+ -+static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) -+{ -+ int err; -+ -+ del->pathname = args[0].from; -+ AuDbg("del path %s\n", del->pathname); -+ -+ err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); -+ if (unlikely(err)) -+ pr_err("lookup failed %s (%d)\n", del->pathname, err); -+ -+ return err; -+} -+ -+#if 0 /* reserved for future use */ -+static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, -+ struct au_opt_del *del, substring_t args[]) -+{ -+ int err; -+ struct dentry *root; -+ -+ err = -EINVAL; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_FLUSH); -+ if (bindex < 0 || au_sbend(sb) < bindex) { -+ pr_err("out of bounds, %d\n", bindex); -+ goto out; -+ } -+ -+ err = 0; -+ del->h_path.dentry = dget(au_h_dptr(root, bindex)); -+ del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); -+ -+out: -+ aufs_read_unlock(root, !AuLock_IR); -+ return err; -+} -+#endif -+ -+static int noinline_for_stack -+au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) -+{ -+ int err; -+ struct path path; -+ char *p; -+ -+ err = -EINVAL; -+ mod->path = args[0].from; -+ p = strchr(mod->path, '='); -+ if (unlikely(!p)) { -+ pr_err("no permssion %s\n", args[0].from); -+ goto out; -+ } -+ -+ *p++ = 0; -+ err = vfsub_kern_path(mod->path, lkup_dirflags, &path); -+ if (unlikely(err)) { -+ pr_err("lookup failed %s (%d)\n", mod->path, err); -+ goto out; -+ } -+ -+ mod->perm = br_perm_val(p); -+ AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); -+ mod->h_root = dget(path.dentry); -+ path_put(&path); -+ -+out: -+ return err; -+} -+ -+#if 0 /* reserved for future use */ -+static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex, -+ struct au_opt_mod *mod, substring_t args[]) -+{ -+ int err; -+ struct dentry *root; -+ -+ err = -EINVAL; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_FLUSH); -+ if (bindex < 0 || au_sbend(sb) < bindex) { -+ pr_err("out of bounds, %d\n", bindex); -+ goto out; -+ } -+ -+ err = 0; -+ mod->perm = br_perm_val(args[1].from); -+ AuDbg("mod path %s, perm 0x%x, %s\n", -+ mod->path, mod->perm, args[1].from); -+ mod->h_root = dget(au_h_dptr(root, bindex)); -+ -+out: -+ aufs_read_unlock(root, !AuLock_IR); -+ return err; -+} -+#endif -+ -+static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino, -+ substring_t args[]) -+{ -+ int err; -+ struct file *file; -+ -+ file = au_xino_create(sb, args[0].from, /*silent*/0); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ -+ err = -EINVAL; -+ if (unlikely(file->f_path.dentry->d_sb == sb)) { -+ fput(file); -+ pr_err("%s must be outside\n", args[0].from); -+ goto out; -+ } -+ -+ err = 0; -+ xino->file = file; -+ xino->path = args[0].from; -+ -+out: -+ return err; -+} -+ -+static int noinline_for_stack -+au_opts_parse_xino_itrunc_path(struct super_block *sb, -+ struct au_opt_xino_itrunc *xino_itrunc, -+ substring_t args[]) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct path path; -+ struct dentry *root; -+ -+ err = vfsub_kern_path(args[0].from, lkup_dirflags, &path); -+ if (unlikely(err)) { -+ pr_err("lookup failed %s (%d)\n", args[0].from, err); -+ goto out; -+ } -+ -+ xino_itrunc->bindex = -1; -+ root = sb->s_root; -+ aufs_read_lock(root, AuLock_FLUSH); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ if (au_h_dptr(root, bindex) == path.dentry) { -+ xino_itrunc->bindex = bindex; -+ break; -+ } -+ } -+ aufs_read_unlock(root, !AuLock_IR); -+ path_put(&path); -+ -+ if (unlikely(xino_itrunc->bindex < 0)) { -+ pr_err("no such branch %s\n", args[0].from); -+ err = -EINVAL; -+ } -+ -+out: -+ return err; -+} -+ -+/* called without aufs lock */ -+int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) -+{ -+ int err, n, token; -+ aufs_bindex_t bindex; -+ unsigned char skipped; -+ struct dentry *root; -+ struct au_opt *opt, *opt_tail; -+ char *opt_str; -+ /* reduce the stack space */ -+ union { -+ struct au_opt_xino_itrunc *xino_itrunc; -+ struct au_opt_wbr_create *create; -+ } u; -+ struct { -+ substring_t args[MAX_OPT_ARGS]; -+ } *a; -+ -+ err = -ENOMEM; -+ a = kmalloc(sizeof(*a), GFP_NOFS); -+ if (unlikely(!a)) -+ goto out; -+ -+ root = sb->s_root; -+ err = 0; -+ bindex = 0; -+ opt = opts->opt; -+ opt_tail = opt + opts->max_opt - 1; -+ opt->type = Opt_tail; -+ while (!err && (opt_str = strsep(&str, ",")) && *opt_str) { -+ err = -EINVAL; -+ skipped = 0; -+ token = match_token(opt_str, options, a->args); -+ switch (token) { -+ case Opt_br: -+ err = 0; -+ while (!err && (opt_str = strsep(&a->args[0].from, ":")) -+ && *opt_str) { -+ err = opt_add(opt, opt_str, opts->sb_flags, -+ bindex++); -+ if (unlikely(!err && ++opt > opt_tail)) { -+ err = -E2BIG; -+ break; -+ } -+ opt->type = Opt_tail; -+ skipped = 1; -+ } -+ break; -+ case Opt_add: -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ bindex = n; -+ err = opt_add(opt, a->args[1].from, opts->sb_flags, -+ bindex); -+ if (!err) -+ opt->type = token; -+ break; -+ case Opt_append: -+ err = opt_add(opt, a->args[0].from, opts->sb_flags, -+ /*dummy bindex*/1); -+ if (!err) -+ opt->type = token; -+ break; -+ case Opt_prepend: -+ err = opt_add(opt, a->args[0].from, opts->sb_flags, -+ /*bindex*/0); -+ if (!err) -+ opt->type = token; -+ break; -+ case Opt_del: -+ err = au_opts_parse_del(&opt->del, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#if 0 /* reserved for future use */ -+ case Opt_idel: -+ del->pathname = "(indexed)"; -+ if (unlikely(match_int(&args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ err = au_opts_parse_idel(sb, n, &opt->del, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#endif -+ case Opt_mod: -+ err = au_opts_parse_mod(&opt->mod, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#ifdef IMOD /* reserved for future use */ -+ case Opt_imod: -+ u.mod->path = "(indexed)"; -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ err = au_opts_parse_imod(sb, n, &opt->mod, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+#endif -+ case Opt_xino: -+ err = au_opts_parse_xino(sb, &opt->xino, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+ -+ case Opt_trunc_xino_path: -+ err = au_opts_parse_xino_itrunc_path -+ (sb, &opt->xino_itrunc, a->args); -+ if (!err) -+ opt->type = token; -+ break; -+ -+ case Opt_itrunc_xino: -+ u.xino_itrunc = &opt->xino_itrunc; -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ u.xino_itrunc->bindex = n; -+ aufs_read_lock(root, AuLock_FLUSH); -+ if (n < 0 || au_sbend(sb) < n) { -+ pr_err("out of bounds, %d\n", n); -+ aufs_read_unlock(root, !AuLock_IR); -+ break; -+ } -+ aufs_read_unlock(root, !AuLock_IR); -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_dirwh: -+ if (unlikely(match_int(&a->args[0], &opt->dirwh))) -+ break; -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_rdcache: -+ if (unlikely(match_int(&a->args[0], &n))) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ if (unlikely(n > AUFS_RDCACHE_MAX)) { -+ pr_err("rdcache must be smaller than %d\n", -+ AUFS_RDCACHE_MAX); -+ break; -+ } -+ opt->rdcache = n; -+ err = 0; -+ opt->type = token; -+ break; -+ case Opt_rdblk: -+ if (unlikely(match_int(&a->args[0], &n) -+ || n < 0 -+ || n > KMALLOC_MAX_SIZE)) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ if (unlikely(n && n < NAME_MAX)) { -+ pr_err("rdblk must be larger than %d\n", -+ NAME_MAX); -+ break; -+ } -+ opt->rdblk = n; -+ err = 0; -+ opt->type = token; -+ break; -+ case Opt_rdhash: -+ if (unlikely(match_int(&a->args[0], &n) -+ || n < 0 -+ || n * sizeof(struct hlist_head) -+ > KMALLOC_MAX_SIZE)) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ opt->rdhash = n; -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_trunc_xino: -+ case Opt_notrunc_xino: -+ case Opt_noxino: -+ case Opt_trunc_xib: -+ case Opt_notrunc_xib: -+ case Opt_shwh: -+ case Opt_noshwh: -+ case Opt_dirperm1: -+ case Opt_nodirperm1: -+ case Opt_plink: -+ case Opt_noplink: -+ case Opt_list_plink: -+ case Opt_dio: -+ case Opt_nodio: -+ case Opt_diropq_a: -+ case Opt_diropq_w: -+ case Opt_warn_perm: -+ case Opt_nowarn_perm: -+ case Opt_verbose: -+ case Opt_noverbose: -+ case Opt_sum: -+ case Opt_nosum: -+ case Opt_wsum: -+ case Opt_rdblk_def: -+ case Opt_rdhash_def: -+ case Opt_acl: -+ case Opt_noacl: -+ err = 0; -+ opt->type = token; -+ break; -+ -+ case Opt_udba: -+ opt->udba = udba_val(a->args[0].from); -+ if (opt->udba >= 0) { -+ err = 0; -+ opt->type = token; -+ } else -+ pr_err("wrong value, %s\n", opt_str); -+ break; -+ -+ case Opt_wbr_create: -+ u.create = &opt->wbr_create; -+ u.create->wbr_create -+ = au_wbr_create_val(a->args[0].from, u.create); -+ if (u.create->wbr_create >= 0) { -+ err = 0; -+ opt->type = token; -+ } else -+ pr_err("wrong value, %s\n", opt_str); -+ break; -+ case Opt_wbr_copyup: -+ opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from); -+ if (opt->wbr_copyup >= 0) { -+ err = 0; -+ opt->type = token; -+ } else -+ pr_err("wrong value, %s\n", opt_str); -+ break; -+ -+ case Opt_fhsm_sec: -+ if (unlikely(match_int(&a->args[0], &n) -+ || n < 0)) { -+ pr_err("bad integer in %s\n", opt_str); -+ break; -+ } -+ if (sysaufs_brs) { -+ opt->fhsm_second = n; -+ opt->type = token; -+ } else -+ pr_warn("ignored %s\n", opt_str); -+ err = 0; -+ break; -+ -+ case Opt_ignore: -+ pr_warn("ignored %s\n", opt_str); -+ /*FALLTHROUGH*/ -+ case Opt_ignore_silent: -+ skipped = 1; -+ err = 0; -+ break; -+ case Opt_err: -+ pr_err("unknown option %s\n", opt_str); -+ break; -+ } -+ -+ if (!err && !skipped) { -+ if (unlikely(++opt > opt_tail)) { -+ err = -E2BIG; -+ opt--; -+ opt->type = Opt_tail; -+ break; -+ } -+ opt->type = Opt_tail; -+ } -+ } -+ -+ kfree(a); -+ dump_opts(opts); -+ if (unlikely(err)) -+ au_opts_free(opts); -+ -+out: -+ return err; -+} -+ -+static int au_opt_wbr_create(struct super_block *sb, -+ struct au_opt_wbr_create *create) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 1; /* handled */ -+ sbinfo = au_sbi(sb); -+ if (sbinfo->si_wbr_create_ops->fin) { -+ err = sbinfo->si_wbr_create_ops->fin(sb); -+ if (!err) -+ err = 1; -+ } -+ -+ sbinfo->si_wbr_create = create->wbr_create; -+ sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create; -+ switch (create->wbr_create) { -+ case AuWbrCreate_MFSRRV: -+ case AuWbrCreate_MFSRR: -+ case AuWbrCreate_PMFSRR: -+ case AuWbrCreate_PMFSRRV: -+ sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; -+ /*FALLTHROUGH*/ -+ case AuWbrCreate_MFS: -+ case AuWbrCreate_MFSV: -+ case AuWbrCreate_PMFS: -+ case AuWbrCreate_PMFSV: -+ sbinfo->si_wbr_mfs.mfs_expire -+ = msecs_to_jiffies(create->mfs_second * MSEC_PER_SEC); -+ break; -+ } -+ -+ if (sbinfo->si_wbr_create_ops->init) -+ sbinfo->si_wbr_create_ops->init(sb); /* ignore */ -+ -+ return err; -+} -+ -+/* -+ * returns, -+ * plus: processed without an error -+ * zero: unprocessed -+ */ -+static int au_opt_simple(struct super_block *sb, struct au_opt *opt, -+ struct au_opts *opts) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 1; /* handled */ -+ sbinfo = au_sbi(sb); -+ switch (opt->type) { -+ case Opt_udba: -+ sbinfo->si_mntflags &= ~AuOptMask_UDBA; -+ sbinfo->si_mntflags |= opt->udba; -+ opts->given_udba |= opt->udba; -+ break; -+ -+ case Opt_plink: -+ au_opt_set(sbinfo->si_mntflags, PLINK); -+ break; -+ case Opt_noplink: -+ if (au_opt_test(sbinfo->si_mntflags, PLINK)) -+ au_plink_put(sb, /*verbose*/1); -+ au_opt_clr(sbinfo->si_mntflags, PLINK); -+ break; -+ case Opt_list_plink: -+ if (au_opt_test(sbinfo->si_mntflags, PLINK)) -+ au_plink_list(sb); -+ break; -+ -+ case Opt_dio: -+ au_opt_set(sbinfo->si_mntflags, DIO); -+ au_fset_opts(opts->flags, REFRESH_DYAOP); -+ break; -+ case Opt_nodio: -+ au_opt_clr(sbinfo->si_mntflags, DIO); -+ au_fset_opts(opts->flags, REFRESH_DYAOP); -+ break; -+ -+ case Opt_fhsm_sec: -+ au_fhsm_set(sbinfo, opt->fhsm_second); -+ break; -+ -+ case Opt_diropq_a: -+ au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); -+ break; -+ case Opt_diropq_w: -+ au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); -+ break; -+ -+ case Opt_warn_perm: -+ au_opt_set(sbinfo->si_mntflags, WARN_PERM); -+ break; -+ case Opt_nowarn_perm: -+ au_opt_clr(sbinfo->si_mntflags, WARN_PERM); -+ break; -+ -+ case Opt_verbose: -+ au_opt_set(sbinfo->si_mntflags, VERBOSE); -+ break; -+ case Opt_noverbose: -+ au_opt_clr(sbinfo->si_mntflags, VERBOSE); -+ break; -+ -+ case Opt_sum: -+ au_opt_set(sbinfo->si_mntflags, SUM); -+ break; -+ case Opt_wsum: -+ au_opt_clr(sbinfo->si_mntflags, SUM); -+ au_opt_set(sbinfo->si_mntflags, SUM_W); -+ case Opt_nosum: -+ au_opt_clr(sbinfo->si_mntflags, SUM); -+ au_opt_clr(sbinfo->si_mntflags, SUM_W); -+ break; -+ -+ case Opt_wbr_create: -+ err = au_opt_wbr_create(sb, &opt->wbr_create); -+ break; -+ case Opt_wbr_copyup: -+ sbinfo->si_wbr_copyup = opt->wbr_copyup; -+ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; -+ break; -+ -+ case Opt_dirwh: -+ sbinfo->si_dirwh = opt->dirwh; -+ break; -+ -+ case Opt_rdcache: -+ sbinfo->si_rdcache -+ = msecs_to_jiffies(opt->rdcache * MSEC_PER_SEC); -+ break; -+ case Opt_rdblk: -+ sbinfo->si_rdblk = opt->rdblk; -+ break; -+ case Opt_rdblk_def: -+ sbinfo->si_rdblk = AUFS_RDBLK_DEF; -+ break; -+ case Opt_rdhash: -+ sbinfo->si_rdhash = opt->rdhash; -+ break; -+ case Opt_rdhash_def: -+ sbinfo->si_rdhash = AUFS_RDHASH_DEF; -+ break; -+ -+ case Opt_shwh: -+ au_opt_set(sbinfo->si_mntflags, SHWH); -+ break; -+ case Opt_noshwh: -+ au_opt_clr(sbinfo->si_mntflags, SHWH); -+ break; -+ -+ case Opt_dirperm1: -+ au_opt_set(sbinfo->si_mntflags, DIRPERM1); -+ break; -+ case Opt_nodirperm1: -+ au_opt_clr(sbinfo->si_mntflags, DIRPERM1); -+ break; -+ -+ case Opt_trunc_xino: -+ au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); -+ break; -+ case Opt_notrunc_xino: -+ au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); -+ break; -+ -+ case Opt_trunc_xino_path: -+ case Opt_itrunc_xino: -+ err = au_xino_trunc(sb, opt->xino_itrunc.bindex); -+ if (!err) -+ err = 1; -+ break; -+ -+ case Opt_trunc_xib: -+ au_fset_opts(opts->flags, TRUNC_XIB); -+ break; -+ case Opt_notrunc_xib: -+ au_fclr_opts(opts->flags, TRUNC_XIB); -+ break; -+ -+ case Opt_acl: -+ sb->s_flags |= MS_POSIXACL; -+ break; -+ case Opt_noacl: -+ sb->s_flags &= ~MS_POSIXACL; -+ break; -+ -+ default: -+ err = 0; -+ break; -+ } -+ -+ return err; -+} -+ -+/* -+ * returns tri-state. -+ * plus: processed without an error -+ * zero: unprocessed -+ * minus: error -+ */ -+static int au_opt_br(struct super_block *sb, struct au_opt *opt, -+ struct au_opts *opts) -+{ -+ int err, do_refresh; -+ -+ err = 0; -+ switch (opt->type) { -+ case Opt_append: -+ opt->add.bindex = au_sbend(sb) + 1; -+ if (opt->add.bindex < 0) -+ opt->add.bindex = 0; -+ goto add; -+ case Opt_prepend: -+ opt->add.bindex = 0; -+ add: /* indented label */ -+ case Opt_add: -+ err = au_br_add(sb, &opt->add, -+ au_ftest_opts(opts->flags, REMOUNT)); -+ if (!err) { -+ err = 1; -+ au_fset_opts(opts->flags, REFRESH); -+ } -+ break; -+ -+ case Opt_del: -+ case Opt_idel: -+ err = au_br_del(sb, &opt->del, -+ au_ftest_opts(opts->flags, REMOUNT)); -+ if (!err) { -+ err = 1; -+ au_fset_opts(opts->flags, TRUNC_XIB); -+ au_fset_opts(opts->flags, REFRESH); -+ } -+ break; -+ -+ case Opt_mod: -+ case Opt_imod: -+ err = au_br_mod(sb, &opt->mod, -+ au_ftest_opts(opts->flags, REMOUNT), -+ &do_refresh); -+ if (!err) { -+ err = 1; -+ if (do_refresh) -+ au_fset_opts(opts->flags, REFRESH); -+ } -+ break; -+ } -+ -+ return err; -+} -+ -+static int au_opt_xino(struct super_block *sb, struct au_opt *opt, -+ struct au_opt_xino **opt_xino, -+ struct au_opts *opts) -+{ -+ int err; -+ aufs_bindex_t bend, bindex; -+ struct dentry *root, *parent, *h_root; -+ -+ err = 0; -+ switch (opt->type) { -+ case Opt_xino: -+ err = au_xino_set(sb, &opt->xino, -+ !!au_ftest_opts(opts->flags, REMOUNT)); -+ if (unlikely(err)) -+ break; -+ -+ *opt_xino = &opt->xino; -+ au_xino_brid_set(sb, -1); -+ -+ /* safe d_parent access */ -+ parent = opt->xino.file->f_path.dentry->d_parent; -+ root = sb->s_root; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ h_root = au_h_dptr(root, bindex); -+ if (h_root == parent) { -+ au_xino_brid_set(sb, au_sbr_id(sb, bindex)); -+ break; -+ } -+ } -+ break; -+ -+ case Opt_noxino: -+ au_xino_clr(sb); -+ au_xino_brid_set(sb, -1); -+ *opt_xino = (void *)-1; -+ break; -+ } -+ -+ return err; -+} -+ -+int au_opts_verify(struct super_block *sb, unsigned long sb_flags, -+ unsigned int pending) -+{ -+ int err, fhsm; -+ aufs_bindex_t bindex, bend; -+ unsigned char do_plink, skip, do_free; -+ struct au_branch *br; -+ struct au_wbr *wbr; -+ struct dentry *root; -+ struct inode *dir, *h_dir; -+ struct au_sbinfo *sbinfo; -+ struct au_hinode *hdir; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); -+ -+ if (!(sb_flags & MS_RDONLY)) { -+ if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) -+ pr_warn("first branch should be rw\n"); -+ if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) -+ pr_warn("shwh should be used with ro\n"); -+ } -+ -+ if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) -+ && !au_opt_test(sbinfo->si_mntflags, XINO)) -+ pr_warn("udba=*notify requires xino\n"); -+ -+ if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) -+ pr_warn("dirperm1 breaks the protection" -+ " by the permission bits on the lower branch\n"); -+ -+ err = 0; -+ fhsm = 0; -+ root = sb->s_root; -+ dir = root->d_inode; -+ do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); -+ bend = au_sbend(sb); -+ for (bindex = 0; !err && bindex <= bend; bindex++) { -+ skip = 0; -+ h_dir = au_h_iptr(dir, bindex); -+ br = au_sbr(sb, bindex); -+ -+ if ((br->br_perm & AuBrAttr_ICEX) -+ && !h_dir->i_op->listxattr) -+ br->br_perm &= ~AuBrAttr_ICEX; -+#if 0 -+ if ((br->br_perm & AuBrAttr_ICEX_SEC) -+ && (au_br_sb(br)->s_flags & MS_NOSEC)) -+ br->br_perm &= ~AuBrAttr_ICEX_SEC; -+#endif -+ -+ do_free = 0; -+ wbr = br->br_wbr; -+ if (wbr) -+ wbr_wh_read_lock(wbr); -+ -+ if (!au_br_writable(br->br_perm)) { -+ do_free = !!wbr; -+ skip = (!wbr -+ || (!wbr->wbr_whbase -+ && !wbr->wbr_plink -+ && !wbr->wbr_orph)); -+ } else if (!au_br_wh_linkable(br->br_perm)) { -+ /* skip = (!br->br_whbase && !br->br_orph); */ -+ skip = (!wbr || !wbr->wbr_whbase); -+ if (skip && wbr) { -+ if (do_plink) -+ skip = !!wbr->wbr_plink; -+ else -+ skip = !wbr->wbr_plink; -+ } -+ } else { -+ /* skip = (br->br_whbase && br->br_ohph); */ -+ skip = (wbr && wbr->wbr_whbase); -+ if (skip) { -+ if (do_plink) -+ skip = !!wbr->wbr_plink; -+ else -+ skip = !wbr->wbr_plink; -+ } -+ } -+ if (wbr) -+ wbr_wh_read_unlock(wbr); -+ -+ if (au_br_fhsm(br->br_perm)) { -+ fhsm++; -+ AuDebugOn(!br->br_fhsm); -+ } -+ -+ if (skip) -+ continue; -+ -+ hdir = au_hi(dir, bindex); -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ if (wbr) -+ wbr_wh_write_lock(wbr); -+ err = au_wh_init(br, sb); -+ if (wbr) -+ wbr_wh_write_unlock(wbr); -+ au_hn_imtx_unlock(hdir); -+ -+ if (!err && do_free) { -+ kfree(wbr); -+ br->br_wbr = NULL; -+ } -+ } -+ -+ if (fhsm >= 2) { -+ au_fset_si(sbinfo, FHSM); -+ for (bindex = bend; bindex >= 0; bindex--) { -+ br = au_sbr(sb, bindex); -+ if (au_br_fhsm(br->br_perm)) { -+ au_fhsm_set_bottom(sb, bindex); -+ break; -+ } -+ } -+ } else { -+ au_fclr_si(sbinfo, FHSM); -+ au_fhsm_set_bottom(sb, -1); -+ } -+ -+ return err; -+} -+ -+int au_opts_mount(struct super_block *sb, struct au_opts *opts) -+{ -+ int err; -+ unsigned int tmp; -+ aufs_bindex_t bindex, bend; -+ struct au_opt *opt; -+ struct au_opt_xino *opt_xino, xino; -+ struct au_sbinfo *sbinfo; -+ struct au_branch *br; -+ struct inode *dir; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ opt_xino = NULL; -+ opt = opts->opt; -+ while (err >= 0 && opt->type != Opt_tail) -+ err = au_opt_simple(sb, opt++, opts); -+ if (err > 0) -+ err = 0; -+ else if (unlikely(err < 0)) -+ goto out; -+ -+ /* disable xino and udba temporary */ -+ sbinfo = au_sbi(sb); -+ tmp = sbinfo->si_mntflags; -+ au_opt_clr(sbinfo->si_mntflags, XINO); -+ au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); -+ -+ opt = opts->opt; -+ while (err >= 0 && opt->type != Opt_tail) -+ err = au_opt_br(sb, opt++, opts); -+ if (err > 0) -+ err = 0; -+ else if (unlikely(err < 0)) -+ goto out; -+ -+ bend = au_sbend(sb); -+ if (unlikely(bend < 0)) { -+ err = -EINVAL; -+ pr_err("no branches\n"); -+ goto out; -+ } -+ -+ if (au_opt_test(tmp, XINO)) -+ au_opt_set(sbinfo->si_mntflags, XINO); -+ opt = opts->opt; -+ while (!err && opt->type != Opt_tail) -+ err = au_opt_xino(sb, opt++, &opt_xino, opts); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_opts_verify(sb, sb->s_flags, tmp); -+ if (unlikely(err)) -+ goto out; -+ -+ /* restore xino */ -+ if (au_opt_test(tmp, XINO) && !opt_xino) { -+ xino.file = au_xino_def(sb); -+ err = PTR_ERR(xino.file); -+ if (IS_ERR(xino.file)) -+ goto out; -+ -+ err = au_xino_set(sb, &xino, /*remount*/0); -+ fput(xino.file); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+ /* restore udba */ -+ tmp &= AuOptMask_UDBA; -+ sbinfo->si_mntflags &= ~AuOptMask_UDBA; -+ sbinfo->si_mntflags |= tmp; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ err = au_hnotify_reset_br(tmp, br, br->br_perm); -+ if (unlikely(err)) -+ AuIOErr("hnotify failed on br %d, %d, ignored\n", -+ bindex, err); -+ /* go on even if err */ -+ } -+ if (au_opt_test(tmp, UDBA_HNOTIFY)) { -+ dir = sb->s_root->d_inode; -+ au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); -+ } -+ -+out: -+ return err; -+} -+ -+int au_opts_remount(struct super_block *sb, struct au_opts *opts) -+{ -+ int err, rerr; -+ struct inode *dir; -+ struct au_opt_xino *opt_xino; -+ struct au_opt *opt; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ dir = sb->s_root->d_inode; -+ sbinfo = au_sbi(sb); -+ err = 0; -+ opt_xino = NULL; -+ opt = opts->opt; -+ while (err >= 0 && opt->type != Opt_tail) { -+ err = au_opt_simple(sb, opt, opts); -+ if (!err) -+ err = au_opt_br(sb, opt, opts); -+ if (!err) -+ err = au_opt_xino(sb, opt, &opt_xino, opts); -+ opt++; -+ } -+ if (err > 0) -+ err = 0; -+ AuTraceErr(err); -+ /* go on even err */ -+ -+ rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); -+ if (unlikely(rerr && !err)) -+ err = rerr; -+ -+ if (au_ftest_opts(opts->flags, TRUNC_XIB)) { -+ rerr = au_xib_trunc(sb); -+ if (unlikely(rerr && !err)) -+ err = rerr; -+ } -+ -+ /* will be handled by the caller */ -+ if (!au_ftest_opts(opts->flags, REFRESH) -+ && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO))) -+ au_fset_opts(opts->flags, REFRESH); -+ -+ AuDbg("status 0x%x\n", opts->flags); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+unsigned int au_opt_udba(struct super_block *sb) -+{ -+ return au_mntflags(sb) & AuOptMask_UDBA; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/opts.h linux-4.0.4/fs/aufs/opts.h ---- linux-4.0.4.orig/fs/aufs/opts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/opts.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,197 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * mount options/flags -+ */ -+ -+#ifndef __AUFS_OPTS_H__ -+#define __AUFS_OPTS_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+ -+struct file; -+struct super_block; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* mount flags */ -+#define AuOpt_XINO 1 /* external inode number bitmap -+ and translation table */ -+#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */ -+#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */ -+#define AuOpt_UDBA_REVAL (1 << 3) -+#define AuOpt_UDBA_HNOTIFY (1 << 4) -+#define AuOpt_SHWH (1 << 5) /* show whiteout */ -+#define AuOpt_PLINK (1 << 6) /* pseudo-link */ -+#define AuOpt_DIRPERM1 (1 << 7) /* ignore the lower dir's perm -+ bits */ -+#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */ -+#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */ -+#define AuOpt_SUM_W (1 << 11) /* unimplemented */ -+#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */ -+#define AuOpt_VERBOSE (1 << 13) /* busy inode when del-branch */ -+#define AuOpt_DIO (1 << 14) /* direct io */ -+ -+#ifndef CONFIG_AUFS_HNOTIFY -+#undef AuOpt_UDBA_HNOTIFY -+#define AuOpt_UDBA_HNOTIFY 0 -+#endif -+#ifndef CONFIG_AUFS_SHWH -+#undef AuOpt_SHWH -+#define AuOpt_SHWH 0 -+#endif -+ -+#define AuOpt_Def (AuOpt_XINO \ -+ | AuOpt_UDBA_REVAL \ -+ | AuOpt_PLINK \ -+ /* | AuOpt_DIRPERM1 */ \ -+ | AuOpt_WARN_PERM) -+#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ -+ | AuOpt_UDBA_REVAL \ -+ | AuOpt_UDBA_HNOTIFY) -+ -+#define au_opt_test(flags, name) (flags & AuOpt_##name) -+#define au_opt_set(flags, name) do { \ -+ BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \ -+ ((flags) |= AuOpt_##name); \ -+} while (0) -+#define au_opt_set_udba(flags, name) do { \ -+ (flags) &= ~AuOptMask_UDBA; \ -+ ((flags) |= AuOpt_##name); \ -+} while (0) -+#define au_opt_clr(flags, name) do { \ -+ ((flags) &= ~AuOpt_##name); \ -+} while (0) -+ -+static inline unsigned int au_opts_plink(unsigned int mntflags) -+{ -+#ifdef CONFIG_PROC_FS -+ return mntflags; -+#else -+ return mntflags & ~AuOpt_PLINK; -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policies to select one among multiple writable branches */ -+enum { -+ AuWbrCreate_TDP, /* top down parent */ -+ AuWbrCreate_RR, /* round robin */ -+ AuWbrCreate_MFS, /* most free space */ -+ AuWbrCreate_MFSV, /* mfs with seconds */ -+ AuWbrCreate_MFSRR, /* mfs then rr */ -+ AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ -+ AuWbrCreate_PMFS, /* parent and mfs */ -+ AuWbrCreate_PMFSV, /* parent and mfs with seconds */ -+ AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */ -+ AuWbrCreate_PMFSRRV, /* plus seconds */ -+ -+ AuWbrCreate_Def = AuWbrCreate_TDP -+}; -+ -+enum { -+ AuWbrCopyup_TDP, /* top down parent */ -+ AuWbrCopyup_BUP, /* bottom up parent */ -+ AuWbrCopyup_BU, /* bottom up */ -+ -+ AuWbrCopyup_Def = AuWbrCopyup_TDP -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_opt_add { -+ aufs_bindex_t bindex; -+ char *pathname; -+ int perm; -+ struct path path; -+}; -+ -+struct au_opt_del { -+ char *pathname; -+ struct path h_path; -+}; -+ -+struct au_opt_mod { -+ char *path; -+ int perm; -+ struct dentry *h_root; -+}; -+ -+struct au_opt_xino { -+ char *path; -+ struct file *file; -+}; -+ -+struct au_opt_xino_itrunc { -+ aufs_bindex_t bindex; -+}; -+ -+struct au_opt_wbr_create { -+ int wbr_create; -+ int mfs_second; -+ unsigned long long mfsrr_watermark; -+}; -+ -+struct au_opt { -+ int type; -+ union { -+ struct au_opt_xino xino; -+ struct au_opt_xino_itrunc xino_itrunc; -+ struct au_opt_add add; -+ struct au_opt_del del; -+ struct au_opt_mod mod; -+ int dirwh; -+ int rdcache; -+ unsigned int rdblk; -+ unsigned int rdhash; -+ int udba; -+ struct au_opt_wbr_create wbr_create; -+ int wbr_copyup; -+ unsigned int fhsm_second; -+ }; -+}; -+ -+/* opts flags */ -+#define AuOpts_REMOUNT 1 -+#define AuOpts_REFRESH (1 << 1) -+#define AuOpts_TRUNC_XIB (1 << 2) -+#define AuOpts_REFRESH_DYAOP (1 << 3) -+#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) -+#define au_fset_opts(flags, name) \ -+ do { (flags) |= AuOpts_##name; } while (0) -+#define au_fclr_opts(flags, name) \ -+ do { (flags) &= ~AuOpts_##name; } while (0) -+ -+struct au_opts { -+ struct au_opt *opt; -+ int max_opt; -+ -+ unsigned int given_udba; -+ unsigned int flags; -+ unsigned long sb_flags; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* opts.c */ -+void au_optstr_br_perm(au_br_perm_str_t *str, int perm); -+const char *au_optstr_udba(int udba); -+const char *au_optstr_wbr_copyup(int wbr_copyup); -+const char *au_optstr_wbr_create(int wbr_create); -+ -+void au_opts_free(struct au_opts *opts); -+int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts); -+int au_opts_verify(struct super_block *sb, unsigned long sb_flags, -+ unsigned int pending); -+int au_opts_mount(struct super_block *sb, struct au_opts *opts); -+int au_opts_remount(struct super_block *sb, struct au_opts *opts); -+ -+unsigned int au_opt_udba(struct super_block *sb); -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_OPTS_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/plink.c linux-4.0.4/fs/aufs/plink.c ---- linux-4.0.4.orig/fs/aufs/plink.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/plink.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,519 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * pseudo-link -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * the pseudo-link maintenance mode. -+ * during a user process maintains the pseudo-links, -+ * prohibit adding a new plink and branch manipulation. -+ * -+ * Flags -+ * NOPLM: -+ * For entry functions which will handle plink, and i_mutex is already held -+ * in VFS. -+ * They cannot wait and should return an error at once. -+ * Callers has to check the error. -+ * NOPLMW: -+ * For entry functions which will handle plink, but i_mutex is not held -+ * in VFS. -+ * They can wait the plink maintenance mode to finish. -+ * -+ * They behave like F_SETLK and F_SETLKW. -+ * If the caller never handle plink, then both flags are unnecessary. -+ */ -+ -+int au_plink_maint(struct super_block *sb, int flags) -+{ -+ int err; -+ pid_t pid, ppid; -+ struct au_sbinfo *sbi; -+ -+ SiMustAnyLock(sb); -+ -+ err = 0; -+ if (!au_opt_test(au_mntflags(sb), PLINK)) -+ goto out; -+ -+ sbi = au_sbi(sb); -+ pid = sbi->si_plink_maint_pid; -+ if (!pid || pid == current->pid) -+ goto out; -+ -+ /* todo: it highly depends upon /sbin/mount.aufs */ -+ rcu_read_lock(); -+ ppid = task_pid_vnr(rcu_dereference(current->real_parent)); -+ rcu_read_unlock(); -+ if (pid == ppid) -+ goto out; -+ -+ if (au_ftest_lock(flags, NOPLMW)) { -+ /* if there is no i_mutex lock in VFS, we don't need to wait */ -+ /* AuDebugOn(!lockdep_depth(current)); */ -+ while (sbi->si_plink_maint_pid) { -+ si_read_unlock(sb); -+ /* gave up wake_up_bit() */ -+ wait_event(sbi->si_plink_wq, !sbi->si_plink_maint_pid); -+ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&sbi->si_nowait); -+ si_noflush_read_lock(sb); -+ } -+ } else if (au_ftest_lock(flags, NOPLM)) { -+ AuDbg("ppid %d, pid %d\n", ppid, pid); -+ err = -EAGAIN; -+ } -+ -+out: -+ return err; -+} -+ -+void au_plink_maint_leave(struct au_sbinfo *sbinfo) -+{ -+ spin_lock(&sbinfo->si_plink_maint_lock); -+ sbinfo->si_plink_maint_pid = 0; -+ spin_unlock(&sbinfo->si_plink_maint_lock); -+ wake_up_all(&sbinfo->si_plink_wq); -+} -+ -+int au_plink_maint_enter(struct super_block *sb) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ /* make sure i am the only one in this fs */ -+ si_write_lock(sb, AuLock_FLUSH); -+ if (au_opt_test(au_mntflags(sb), PLINK)) { -+ spin_lock(&sbinfo->si_plink_maint_lock); -+ if (!sbinfo->si_plink_maint_pid) -+ sbinfo->si_plink_maint_pid = current->pid; -+ else -+ err = -EBUSY; -+ spin_unlock(&sbinfo->si_plink_maint_lock); -+ } -+ si_write_unlock(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_DEBUG -+void au_plink_list(struct super_block *sb) -+{ -+ int i; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct pseudo_link *plink; -+ -+ SiMustAnyLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ rcu_read_lock(); -+ hlist_for_each_entry_rcu(plink, plink_hlist, hlist) -+ AuDbg("%lu\n", plink->inode->i_ino); -+ rcu_read_unlock(); -+ } -+} -+#endif -+ -+/* is the inode pseudo-linked? */ -+int au_plink_test(struct inode *inode) -+{ -+ int found, i; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct pseudo_link *plink; -+ -+ sbinfo = au_sbi(inode->i_sb); -+ AuRwMustAnyLock(&sbinfo->si_rwsem); -+ AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); -+ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); -+ -+ found = 0; -+ i = au_plink_hash(inode->i_ino); -+ plink_hlist = &sbinfo->si_plink[i].head; -+ rcu_read_lock(); -+ hlist_for_each_entry_rcu(plink, plink_hlist, hlist) -+ if (plink->inode == inode) { -+ found = 1; -+ break; -+ } -+ rcu_read_unlock(); -+ return found; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * generate a name for plink. -+ * the file will be stored under AUFS_WH_PLINKDIR. -+ */ -+/* 20 is max digits length of ulong 64 */ -+#define PLINK_NAME_LEN ((20 + 1) * 2) -+ -+static int plink_name(char *name, int len, struct inode *inode, -+ aufs_bindex_t bindex) -+{ -+ int rlen; -+ struct inode *h_inode; -+ -+ h_inode = au_h_iptr(inode, bindex); -+ rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); -+ return rlen; -+} -+ -+struct au_do_plink_lkup_args { -+ struct dentry **errp; -+ struct qstr *tgtname; -+ struct dentry *h_parent; -+ struct au_branch *br; -+}; -+ -+static struct dentry *au_do_plink_lkup(struct qstr *tgtname, -+ struct dentry *h_parent, -+ struct au_branch *br) -+{ -+ struct dentry *h_dentry; -+ struct mutex *h_mtx; -+ -+ h_mtx = &h_parent->d_inode->i_mutex; -+ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); -+ h_dentry = vfsub_lkup_one(tgtname, h_parent); -+ mutex_unlock(h_mtx); -+ return h_dentry; -+} -+ -+static void au_call_do_plink_lkup(void *args) -+{ -+ struct au_do_plink_lkup_args *a = args; -+ *a->errp = au_do_plink_lkup(a->tgtname, a->h_parent, a->br); -+} -+ -+/* lookup the plink-ed @inode under the branch at @bindex */ -+struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) -+{ -+ struct dentry *h_dentry, *h_parent; -+ struct au_branch *br; -+ struct inode *h_dir; -+ int wkq_err; -+ char a[PLINK_NAME_LEN]; -+ struct qstr tgtname = QSTR_INIT(a, 0); -+ -+ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); -+ -+ br = au_sbr(inode->i_sb, bindex); -+ h_parent = br->br_wbr->wbr_plink; -+ h_dir = h_parent->d_inode; -+ tgtname.len = plink_name(a, sizeof(a), inode, bindex); -+ -+ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { -+ struct au_do_plink_lkup_args args = { -+ .errp = &h_dentry, -+ .tgtname = &tgtname, -+ .h_parent = h_parent, -+ .br = br -+ }; -+ -+ wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args); -+ if (unlikely(wkq_err)) -+ h_dentry = ERR_PTR(wkq_err); -+ } else -+ h_dentry = au_do_plink_lkup(&tgtname, h_parent, br); -+ -+ return h_dentry; -+} -+ -+/* create a pseudo-link */ -+static int do_whplink(struct qstr *tgt, struct dentry *h_parent, -+ struct dentry *h_dentry, struct au_branch *br) -+{ -+ int err; -+ struct path h_path = { -+ .mnt = au_br_mnt(br) -+ }; -+ struct inode *h_dir, *delegated; -+ -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); -+again: -+ h_path.dentry = vfsub_lkup_one(tgt, h_parent); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) -+ goto out; -+ -+ err = 0; -+ /* wh.plink dir is not monitored */ -+ /* todo: is it really safe? */ -+ if (h_path.dentry->d_inode -+ && h_path.dentry->d_inode != h_dentry->d_inode) { -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, &h_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ dput(h_path.dentry); -+ h_path.dentry = NULL; -+ if (!err) -+ goto again; -+ } -+ if (!err && !h_path.dentry->d_inode) { -+ delegated = NULL; -+ err = vfsub_link(h_dentry, h_dir, &h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ } -+ dput(h_path.dentry); -+ -+out: -+ mutex_unlock(&h_dir->i_mutex); -+ return err; -+} -+ -+struct do_whplink_args { -+ int *errp; -+ struct qstr *tgt; -+ struct dentry *h_parent; -+ struct dentry *h_dentry; -+ struct au_branch *br; -+}; -+ -+static void call_do_whplink(void *args) -+{ -+ struct do_whplink_args *a = args; -+ *a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br); -+} -+ -+static int whplink(struct dentry *h_dentry, struct inode *inode, -+ aufs_bindex_t bindex, struct au_branch *br) -+{ -+ int err, wkq_err; -+ struct au_wbr *wbr; -+ struct dentry *h_parent; -+ struct inode *h_dir; -+ char a[PLINK_NAME_LEN]; -+ struct qstr tgtname = QSTR_INIT(a, 0); -+ -+ wbr = au_sbr(inode->i_sb, bindex)->br_wbr; -+ h_parent = wbr->wbr_plink; -+ h_dir = h_parent->d_inode; -+ tgtname.len = plink_name(a, sizeof(a), inode, bindex); -+ -+ /* always superio. */ -+ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { -+ struct do_whplink_args args = { -+ .errp = &err, -+ .tgt = &tgtname, -+ .h_parent = h_parent, -+ .h_dentry = h_dentry, -+ .br = br -+ }; -+ wkq_err = au_wkq_wait(call_do_whplink, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } else -+ err = do_whplink(&tgtname, h_parent, h_dentry, br); -+ -+ return err; -+} -+ -+/* free a single plink */ -+static void do_put_plink(struct pseudo_link *plink, int do_del) -+{ -+ if (do_del) -+ hlist_del(&plink->hlist); -+ iput(plink->inode); -+ kfree(plink); -+} -+ -+static void do_put_plink_rcu(struct rcu_head *rcu) -+{ -+ struct pseudo_link *plink; -+ -+ plink = container_of(rcu, struct pseudo_link, rcu); -+ iput(plink->inode); -+ kfree(plink); -+} -+ -+/* -+ * create a new pseudo-link for @h_dentry on @bindex. -+ * the linked inode is held in aufs @inode. -+ */ -+void au_plink_append(struct inode *inode, aufs_bindex_t bindex, -+ struct dentry *h_dentry) -+{ -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct pseudo_link *plink, *tmp; -+ struct au_sphlhead *sphl; -+ int found, err, cnt, i; -+ -+ sb = inode->i_sb; -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ found = au_plink_test(inode); -+ if (found) -+ return; -+ -+ i = au_plink_hash(inode->i_ino); -+ sphl = sbinfo->si_plink + i; -+ plink_hlist = &sphl->head; -+ tmp = kmalloc(sizeof(*plink), GFP_NOFS); -+ if (tmp) -+ tmp->inode = au_igrab(inode); -+ else { -+ err = -ENOMEM; -+ goto out; -+ } -+ -+ spin_lock(&sphl->spin); -+ hlist_for_each_entry(plink, plink_hlist, hlist) { -+ if (plink->inode == inode) { -+ found = 1; -+ break; -+ } -+ } -+ if (!found) -+ hlist_add_head_rcu(&tmp->hlist, plink_hlist); -+ spin_unlock(&sphl->spin); -+ if (!found) { -+ cnt = au_sphl_count(sphl); -+#define msg "unexpectedly unblanced or too many pseudo-links" -+ if (cnt > AUFS_PLINK_WARN) -+ AuWarn1(msg ", %d\n", cnt); -+#undef msg -+ err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); -+ } else { -+ do_put_plink(tmp, 0); -+ return; -+ } -+ -+out: -+ if (unlikely(err)) { -+ pr_warn("err %d, damaged pseudo link.\n", err); -+ if (tmp) { -+ au_sphl_del_rcu(&tmp->hlist, sphl); -+ call_rcu(&tmp->rcu, do_put_plink_rcu); -+ } -+ } -+} -+ -+/* free all plinks */ -+void au_plink_put(struct super_block *sb, int verbose) -+{ -+ int i, warned; -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct hlist_node *tmp; -+ struct pseudo_link *plink; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ /* no spin_lock since sbinfo is write-locked */ -+ warned = 0; -+ for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ if (!warned && verbose && !hlist_empty(plink_hlist)) { -+ pr_warn("pseudo-link is not flushed"); -+ warned = 1; -+ } -+ hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) -+ do_put_plink(plink, 0); -+ INIT_HLIST_HEAD(plink_hlist); -+ } -+} -+ -+void au_plink_clean(struct super_block *sb, int verbose) -+{ -+ struct dentry *root; -+ -+ root = sb->s_root; -+ aufs_write_lock(root); -+ if (au_opt_test(au_mntflags(sb), PLINK)) -+ au_plink_put(sb, verbose); -+ aufs_write_unlock(root); -+} -+ -+static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) -+{ -+ int do_put; -+ aufs_bindex_t bstart, bend, bindex; -+ -+ do_put = 0; -+ bstart = au_ibstart(inode); -+ bend = au_ibend(inode); -+ if (bstart >= 0) { -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ if (!au_h_iptr(inode, bindex) -+ || au_ii_br_id(inode, bindex) != br_id) -+ continue; -+ au_set_h_iptr(inode, bindex, NULL, 0); -+ do_put = 1; -+ break; -+ } -+ if (do_put) -+ for (bindex = bstart; bindex <= bend; bindex++) -+ if (au_h_iptr(inode, bindex)) { -+ do_put = 0; -+ break; -+ } -+ } else -+ do_put = 1; -+ -+ return do_put; -+} -+ -+/* free the plinks on a branch specified by @br_id */ -+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) -+{ -+ struct au_sbinfo *sbinfo; -+ struct hlist_head *plink_hlist; -+ struct hlist_node *tmp; -+ struct pseudo_link *plink; -+ struct inode *inode; -+ int i, do_put; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); -+ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); -+ -+ /* no spin_lock since sbinfo is write-locked */ -+ for (i = 0; i < AuPlink_NHASH; i++) { -+ plink_hlist = &sbinfo->si_plink[i].head; -+ hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) { -+ inode = au_igrab(plink->inode); -+ ii_write_lock_child(inode); -+ do_put = au_plink_do_half_refresh(inode, br_id); -+ if (do_put) -+ do_put_plink(plink, 1); -+ ii_write_unlock(inode); -+ iput(inode); -+ } -+ } -+} -diff -Nur linux-4.0.4.orig/fs/aufs/poll.c linux-4.0.4/fs/aufs/poll.c ---- linux-4.0.4.orig/fs/aufs/poll.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/poll.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,42 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * poll operation -+ * There is only one filesystem which implements ->poll operation, currently. -+ */ -+ -+#include "aufs.h" -+ -+unsigned int aufs_poll(struct file *file, poll_table *wait) -+{ -+ unsigned int mask; -+ int err; -+ struct file *h_file; -+ struct dentry *dentry; -+ struct super_block *sb; -+ -+ /* We should pretend an error happened. */ -+ mask = POLLERR /* | POLLIN | POLLOUT */; -+ dentry = file->f_path.dentry; -+ sb = dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); -+ if (unlikely(err)) -+ goto out; -+ -+ /* it is not an error if h_file has no operation */ -+ mask = DEFAULT_POLLMASK; -+ h_file = au_hf_top(file); -+ if (h_file->f_op->poll) -+ mask = h_file->f_op->poll(h_file, wait); -+ -+ di_read_unlock(dentry, AuLock_IR); -+ fi_read_unlock(file); -+ -+out: -+ si_read_unlock(sb); -+ AuTraceErr((int)mask); -+ return mask; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/posix_acl.c linux-4.0.4/fs/aufs/posix_acl.c ---- linux-4.0.4.orig/fs/aufs/posix_acl.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/posix_acl.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,86 @@ -+/* -+ * Copyright (C) 2014-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * posix acl operations -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+struct posix_acl *aufs_get_acl(struct inode *inode, int type) -+{ -+ struct posix_acl *acl; -+ int err; -+ aufs_bindex_t bindex; -+ struct inode *h_inode; -+ struct super_block *sb; -+ -+ acl = NULL; -+ sb = inode->i_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_read_lock_child(inode); -+ if (!(sb->s_flags & MS_POSIXACL)) -+ goto out; -+ -+ bindex = au_ibstart(inode); -+ h_inode = au_h_iptr(inode, bindex); -+ if (unlikely(!h_inode -+ || ((h_inode->i_mode & S_IFMT) -+ != (inode->i_mode & S_IFMT)))) { -+ err = au_busy_or_stale(); -+ acl = ERR_PTR(err); -+ goto out; -+ } -+ -+ /* always topmost only */ -+ acl = get_acl(h_inode, type); -+ -+out: -+ ii_read_unlock(inode); -+ si_read_unlock(sb); -+ -+ AuTraceErrPtr(acl); -+ return acl; -+} -+ -+int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type) -+{ -+ int err; -+ ssize_t ssz; -+ struct dentry *dentry; -+ struct au_srxattr arg = { -+ .type = AU_ACL_SET, -+ .u.acl_set = { -+ .acl = acl, -+ .type = type -+ }, -+ }; -+ -+ mutex_lock(&inode->i_mutex); -+ if (inode->i_ino == AUFS_ROOT_INO) -+ dentry = dget(inode->i_sb->s_root); -+ else { -+ dentry = d_find_alias(inode); -+ if (!dentry) -+ dentry = d_find_any_alias(inode); -+ if (!dentry) { -+ pr_warn("cannot handle this inode, " -+ "please report to aufs-users ML\n"); -+ err = -ENOENT; -+ goto out; -+ } -+ } -+ -+ ssz = au_srxattr(dentry, &arg); -+ dput(dentry); -+ err = ssz; -+ if (ssz >= 0) -+ err = 0; -+ -+out: -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/procfs.c linux-4.0.4/fs/aufs/procfs.c ---- linux-4.0.4.orig/fs/aufs/procfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/procfs.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,156 @@ -+/* -+ * Copyright (C) 2010-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * procfs interfaces -+ */ -+ -+#include -+#include "aufs.h" -+ -+static int au_procfs_plm_release(struct inode *inode, struct file *file) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = file->private_data; -+ if (sbinfo) { -+ au_plink_maint_leave(sbinfo); -+ kobject_put(&sbinfo->si_kobj); -+ } -+ -+ return 0; -+} -+ -+static void au_procfs_plm_write_clean(struct file *file) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = file->private_data; -+ if (sbinfo) -+ au_plink_clean(sbinfo->si_sb, /*verbose*/0); -+} -+ -+static int au_procfs_plm_write_si(struct file *file, unsigned long id) -+{ -+ int err; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+ err = -EBUSY; -+ if (unlikely(file->private_data)) -+ goto out; -+ -+ sb = NULL; -+ /* don't use au_sbilist_lock() here */ -+ spin_lock(&au_sbilist.spin); -+ list_for_each_entry(sbinfo, &au_sbilist.head, si_list) -+ if (id == sysaufs_si_id(sbinfo)) { -+ kobject_get(&sbinfo->si_kobj); -+ sb = sbinfo->si_sb; -+ break; -+ } -+ spin_unlock(&au_sbilist.spin); -+ -+ err = -EINVAL; -+ if (unlikely(!sb)) -+ goto out; -+ -+ err = au_plink_maint_enter(sb); -+ if (!err) -+ /* keep kobject_get() */ -+ file->private_data = sbinfo; -+ else -+ kobject_put(&sbinfo->si_kobj); -+out: -+ return err; -+} -+ -+/* -+ * Accept a valid "si=xxxx" only. -+ * Once it is accepted successfully, accept "clean" too. -+ */ -+static ssize_t au_procfs_plm_write(struct file *file, const char __user *ubuf, -+ size_t count, loff_t *ppos) -+{ -+ ssize_t err; -+ unsigned long id; -+ /* last newline is allowed */ -+ char buf[3 + sizeof(unsigned long) * 2 + 1]; -+ -+ err = -EACCES; -+ if (unlikely(!capable(CAP_SYS_ADMIN))) -+ goto out; -+ -+ err = -EINVAL; -+ if (unlikely(count > sizeof(buf))) -+ goto out; -+ -+ err = copy_from_user(buf, ubuf, count); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ goto out; -+ } -+ buf[count] = 0; -+ -+ err = -EINVAL; -+ if (!strcmp("clean", buf)) { -+ au_procfs_plm_write_clean(file); -+ goto out_success; -+ } else if (unlikely(strncmp("si=", buf, 3))) -+ goto out; -+ -+ err = kstrtoul(buf + 3, 16, &id); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_procfs_plm_write_si(file, id); -+ if (unlikely(err)) -+ goto out; -+ -+out_success: -+ err = count; /* success */ -+out: -+ return err; -+} -+ -+static const struct file_operations au_procfs_plm_fop = { -+ .write = au_procfs_plm_write, -+ .release = au_procfs_plm_release, -+ .owner = THIS_MODULE -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct proc_dir_entry *au_procfs_dir; -+ -+void au_procfs_fin(void) -+{ -+ remove_proc_entry(AUFS_PLINK_MAINT_NAME, au_procfs_dir); -+ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); -+} -+ -+int __init au_procfs_init(void) -+{ -+ int err; -+ struct proc_dir_entry *entry; -+ -+ err = -ENOMEM; -+ au_procfs_dir = proc_mkdir(AUFS_PLINK_MAINT_DIR, NULL); -+ if (unlikely(!au_procfs_dir)) -+ goto out; -+ -+ entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | S_IWUSR, -+ au_procfs_dir, &au_procfs_plm_fop); -+ if (unlikely(!entry)) -+ goto out_dir; -+ -+ err = 0; -+ goto out; /* success */ -+ -+ -+out_dir: -+ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); -+out: -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/rdu.c linux-4.0.4/fs/aufs/rdu.c ---- linux-4.0.4.orig/fs/aufs/rdu.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/rdu.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,375 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * readdir in userspace. -+ */ -+ -+#include -+#include -+#include -+#include "aufs.h" -+ -+/* bits for struct aufs_rdu.flags */ -+#define AuRdu_CALLED 1 -+#define AuRdu_CONT (1 << 1) -+#define AuRdu_FULL (1 << 2) -+#define au_ftest_rdu(flags, name) ((flags) & AuRdu_##name) -+#define au_fset_rdu(flags, name) \ -+ do { (flags) |= AuRdu_##name; } while (0) -+#define au_fclr_rdu(flags, name) \ -+ do { (flags) &= ~AuRdu_##name; } while (0) -+ -+struct au_rdu_arg { -+ struct dir_context ctx; -+ struct aufs_rdu *rdu; -+ union au_rdu_ent_ul ent; -+ unsigned long end; -+ -+ struct super_block *sb; -+ int err; -+}; -+ -+static int au_rdu_fill(struct dir_context *ctx, const char *name, int nlen, -+ loff_t offset, u64 h_ino, unsigned int d_type) -+{ -+ int err, len; -+ struct au_rdu_arg *arg = container_of(ctx, struct au_rdu_arg, ctx); -+ struct aufs_rdu *rdu = arg->rdu; -+ struct au_rdu_ent ent; -+ -+ err = 0; -+ arg->err = 0; -+ au_fset_rdu(rdu->cookie.flags, CALLED); -+ len = au_rdu_len(nlen); -+ if (arg->ent.ul + len < arg->end) { -+ ent.ino = h_ino; -+ ent.bindex = rdu->cookie.bindex; -+ ent.type = d_type; -+ ent.nlen = nlen; -+ if (unlikely(nlen > AUFS_MAX_NAMELEN)) -+ ent.type = DT_UNKNOWN; -+ -+ /* unnecessary to support mmap_sem since this is a dir */ -+ err = -EFAULT; -+ if (copy_to_user(arg->ent.e, &ent, sizeof(ent))) -+ goto out; -+ if (copy_to_user(arg->ent.e->name, name, nlen)) -+ goto out; -+ /* the terminating NULL */ -+ if (__put_user(0, arg->ent.e->name + nlen)) -+ goto out; -+ err = 0; -+ /* AuDbg("%p, %.*s\n", arg->ent.p, nlen, name); */ -+ arg->ent.ul += len; -+ rdu->rent++; -+ } else { -+ err = -EFAULT; -+ au_fset_rdu(rdu->cookie.flags, FULL); -+ rdu->full = 1; -+ rdu->tail = arg->ent; -+ } -+ -+out: -+ /* AuTraceErr(err); */ -+ return err; -+} -+ -+static int au_rdu_do(struct file *h_file, struct au_rdu_arg *arg) -+{ -+ int err; -+ loff_t offset; -+ struct au_rdu_cookie *cookie = &arg->rdu->cookie; -+ -+ /* we don't have to care (FMODE_32BITHASH | FMODE_64BITHASH) for ext4 */ -+ offset = vfsub_llseek(h_file, cookie->h_pos, SEEK_SET); -+ err = offset; -+ if (unlikely(offset != cookie->h_pos)) -+ goto out; -+ -+ err = 0; -+ do { -+ arg->err = 0; -+ au_fclr_rdu(cookie->flags, CALLED); -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(h_file, &arg->ctx); -+ if (err >= 0) -+ err = arg->err; -+ } while (!err -+ && au_ftest_rdu(cookie->flags, CALLED) -+ && !au_ftest_rdu(cookie->flags, FULL)); -+ cookie->h_pos = h_file->f_pos; -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_rdu(struct file *file, struct aufs_rdu *rdu) -+{ -+ int err; -+ aufs_bindex_t bend; -+ struct au_rdu_arg arg = { -+ .ctx = { -+ .actor = au_rdu_fill -+ } -+ }; -+ struct dentry *dentry; -+ struct inode *inode; -+ struct file *h_file; -+ struct au_rdu_cookie *cookie = &rdu->cookie; -+ -+ err = !access_ok(VERIFY_WRITE, rdu->ent.e, rdu->sz); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ rdu->rent = 0; -+ rdu->tail = rdu->ent; -+ rdu->full = 0; -+ arg.rdu = rdu; -+ arg.ent = rdu->ent; -+ arg.end = arg.ent.ul; -+ arg.end += rdu->sz; -+ -+ err = -ENOTDIR; -+ if (unlikely(!file->f_op->iterate)) -+ goto out; -+ -+ err = security_file_permission(file, MAY_READ); -+ AuTraceErr(err); -+ if (unlikely(err)) -+ goto out; -+ -+ dentry = file->f_path.dentry; -+ inode = dentry->d_inode; -+#if 1 -+ mutex_lock(&inode->i_mutex); -+#else -+ err = mutex_lock_killable(&inode->i_mutex); -+ AuTraceErr(err); -+ if (unlikely(err)) -+ goto out; -+#endif -+ -+ arg.sb = inode->i_sb; -+ err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_mtx; -+ err = au_alive_dir(dentry); -+ if (unlikely(err)) -+ goto out_si; -+ /* todo: reval? */ -+ fi_read_lock(file); -+ -+ err = -EAGAIN; -+ if (unlikely(au_ftest_rdu(cookie->flags, CONT) -+ && cookie->generation != au_figen(file))) -+ goto out_unlock; -+ -+ err = 0; -+ if (!rdu->blk) { -+ rdu->blk = au_sbi(arg.sb)->si_rdblk; -+ if (!rdu->blk) -+ rdu->blk = au_dir_size(file, /*dentry*/NULL); -+ } -+ bend = au_fbstart(file); -+ if (cookie->bindex < bend) -+ cookie->bindex = bend; -+ bend = au_fbend_dir(file); -+ /* AuDbg("b%d, b%d\n", cookie->bindex, bend); */ -+ for (; !err && cookie->bindex <= bend; -+ cookie->bindex++, cookie->h_pos = 0) { -+ h_file = au_hf_dir(file, cookie->bindex); -+ if (!h_file) -+ continue; -+ -+ au_fclr_rdu(cookie->flags, FULL); -+ err = au_rdu_do(h_file, &arg); -+ AuTraceErr(err); -+ if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err)) -+ break; -+ } -+ AuDbg("rent %llu\n", rdu->rent); -+ -+ if (!err && !au_ftest_rdu(cookie->flags, CONT)) { -+ rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH); -+ au_fset_rdu(cookie->flags, CONT); -+ cookie->generation = au_figen(file); -+ } -+ -+ ii_read_lock_child(inode); -+ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); -+ ii_read_unlock(inode); -+ -+out_unlock: -+ fi_read_unlock(file); -+out_si: -+ si_read_unlock(arg.sb); -+out_mtx: -+ mutex_unlock(&inode->i_mutex); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu) -+{ -+ int err; -+ ino_t ino; -+ unsigned long long nent; -+ union au_rdu_ent_ul *u; -+ struct au_rdu_ent ent; -+ struct super_block *sb; -+ -+ err = 0; -+ nent = rdu->nent; -+ u = &rdu->ent; -+ sb = file->f_path.dentry->d_sb; -+ si_read_lock(sb, AuLock_FLUSH); -+ while (nent-- > 0) { -+ /* unnecessary to support mmap_sem since this is a dir */ -+ err = copy_from_user(&ent, u->e, sizeof(ent)); -+ if (!err) -+ err = !access_ok(VERIFY_WRITE, &u->e->ino, sizeof(ino)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ break; -+ } -+ -+ /* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */ -+ if (!ent.wh) -+ err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino); -+ else -+ err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type, -+ &ino); -+ if (unlikely(err)) { -+ AuTraceErr(err); -+ break; -+ } -+ -+ err = __put_user(ino, &u->e->ino); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ break; -+ } -+ u->ul += au_rdu_len(ent.nlen); -+ } -+ si_read_unlock(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_rdu_verify(struct aufs_rdu *rdu) -+{ -+ AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | " -+ "%llu, b%d, 0x%x, g%u}\n", -+ rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ], -+ rdu->blk, -+ rdu->rent, rdu->shwh, rdu->full, -+ rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags, -+ rdu->cookie.generation); -+ -+ if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu)) -+ return 0; -+ -+ AuDbg("%u:%u\n", -+ rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu)); -+ return -EINVAL; -+} -+ -+long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err, e; -+ struct aufs_rdu rdu; -+ void __user *p = (void __user *)arg; -+ -+ err = copy_from_user(&rdu, p, sizeof(rdu)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ err = au_rdu_verify(&rdu); -+ if (unlikely(err)) -+ goto out; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ err = au_rdu(file, &rdu); -+ if (unlikely(err)) -+ break; -+ -+ e = copy_to_user(p, &rdu, sizeof(rdu)); -+ if (unlikely(e)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ } -+ break; -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_ino(file, &rdu); -+ break; -+ -+ default: -+ /* err = -ENOTTY; */ -+ err = -EINVAL; -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+#ifdef CONFIG_COMPAT -+long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ long err, e; -+ struct aufs_rdu rdu; -+ void __user *p = compat_ptr(arg); -+ -+ /* todo: get_user()? */ -+ err = copy_from_user(&rdu, p, sizeof(rdu)); -+ if (unlikely(err)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ goto out; -+ } -+ rdu.ent.e = compat_ptr(rdu.ent.ul); -+ err = au_rdu_verify(&rdu); -+ if (unlikely(err)) -+ goto out; -+ -+ switch (cmd) { -+ case AUFS_CTL_RDU: -+ err = au_rdu(file, &rdu); -+ if (unlikely(err)) -+ break; -+ -+ rdu.ent.ul = ptr_to_compat(rdu.ent.e); -+ rdu.tail.ul = ptr_to_compat(rdu.tail.e); -+ e = copy_to_user(p, &rdu, sizeof(rdu)); -+ if (unlikely(e)) { -+ err = -EFAULT; -+ AuTraceErr(err); -+ } -+ break; -+ case AUFS_CTL_RDU_INO: -+ err = au_rdu_ino(file, &rdu); -+ break; -+ -+ default: -+ /* err = -ENOTTY; */ -+ err = -EINVAL; -+ } -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+#endif -diff -Nur linux-4.0.4.orig/fs/aufs/rwsem.h linux-4.0.4/fs/aufs/rwsem.h ---- linux-4.0.4.orig/fs/aufs/rwsem.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/rwsem.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,178 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * simple read-write semaphore wrappers -+ */ -+ -+#ifndef __AUFS_RWSEM_H__ -+#define __AUFS_RWSEM_H__ -+ -+#ifdef __KERNEL__ -+ -+#include "debug.h" -+ -+struct au_rwsem { -+ struct rw_semaphore rwsem; -+#ifdef CONFIG_AUFS_DEBUG -+ /* just for debugging, not almighty counter */ -+ atomic_t rcnt, wcnt; -+#endif -+}; -+ -+#ifdef CONFIG_AUFS_DEBUG -+#define AuDbgCntInit(rw) do { \ -+ atomic_set(&(rw)->rcnt, 0); \ -+ atomic_set(&(rw)->wcnt, 0); \ -+ smp_mb(); /* atomic set */ \ -+} while (0) -+ -+#define AuDbgRcntInc(rw) atomic_inc(&(rw)->rcnt) -+#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0) -+#define AuDbgWcntInc(rw) atomic_inc(&(rw)->wcnt) -+#define AuDbgWcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0) -+#else -+#define AuDbgCntInit(rw) do {} while (0) -+#define AuDbgRcntInc(rw) do {} while (0) -+#define AuDbgRcntDec(rw) do {} while (0) -+#define AuDbgWcntInc(rw) do {} while (0) -+#define AuDbgWcntDec(rw) do {} while (0) -+#endif /* CONFIG_AUFS_DEBUG */ -+ -+/* to debug easier, do not make them inlined functions */ -+#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list)) -+/* rwsem_is_locked() is unusable */ -+#define AuRwMustReadLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0) -+#define AuRwMustWriteLock(rw) AuDebugOn(atomic_read(&(rw)->wcnt) <= 0) -+#define AuRwMustAnyLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \ -+ && atomic_read(&(rw)->wcnt) <= 0) -+#define AuRwDestroy(rw) AuDebugOn(atomic_read(&(rw)->rcnt) \ -+ || atomic_read(&(rw)->wcnt)) -+ -+#define au_rw_class(rw, key) lockdep_set_class(&(rw)->rwsem, key) -+ -+static inline void au_rw_init(struct au_rwsem *rw) -+{ -+ AuDbgCntInit(rw); -+ init_rwsem(&rw->rwsem); -+} -+ -+static inline void au_rw_init_wlock(struct au_rwsem *rw) -+{ -+ au_rw_init(rw); -+ down_write(&rw->rwsem); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_init_wlock_nested(struct au_rwsem *rw, -+ unsigned int lsc) -+{ -+ au_rw_init(rw); -+ down_write_nested(&rw->rwsem, lsc); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_read_lock(struct au_rwsem *rw) -+{ -+ down_read(&rw->rwsem); -+ AuDbgRcntInc(rw); -+} -+ -+static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc) -+{ -+ down_read_nested(&rw->rwsem, lsc); -+ AuDbgRcntInc(rw); -+} -+ -+static inline void au_rw_read_unlock(struct au_rwsem *rw) -+{ -+ AuRwMustReadLock(rw); -+ AuDbgRcntDec(rw); -+ up_read(&rw->rwsem); -+} -+ -+static inline void au_rw_dgrade_lock(struct au_rwsem *rw) -+{ -+ AuRwMustWriteLock(rw); -+ AuDbgRcntInc(rw); -+ AuDbgWcntDec(rw); -+ downgrade_write(&rw->rwsem); -+} -+ -+static inline void au_rw_write_lock(struct au_rwsem *rw) -+{ -+ down_write(&rw->rwsem); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_write_lock_nested(struct au_rwsem *rw, -+ unsigned int lsc) -+{ -+ down_write_nested(&rw->rwsem, lsc); -+ AuDbgWcntInc(rw); -+} -+ -+static inline void au_rw_write_unlock(struct au_rwsem *rw) -+{ -+ AuRwMustWriteLock(rw); -+ AuDbgWcntDec(rw); -+ up_write(&rw->rwsem); -+} -+ -+/* why is not _nested version defined */ -+static inline int au_rw_read_trylock(struct au_rwsem *rw) -+{ -+ int ret; -+ -+ ret = down_read_trylock(&rw->rwsem); -+ if (ret) -+ AuDbgRcntInc(rw); -+ return ret; -+} -+ -+static inline int au_rw_write_trylock(struct au_rwsem *rw) -+{ -+ int ret; -+ -+ ret = down_write_trylock(&rw->rwsem); -+ if (ret) -+ AuDbgWcntInc(rw); -+ return ret; -+} -+ -+#undef AuDbgCntInit -+#undef AuDbgRcntInc -+#undef AuDbgRcntDec -+#undef AuDbgWcntInc -+#undef AuDbgWcntDec -+ -+#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ -+static inline void prefix##_read_lock(param) \ -+{ au_rw_read_lock(rwsem); } \ -+static inline void prefix##_write_lock(param) \ -+{ au_rw_write_lock(rwsem); } \ -+static inline int prefix##_read_trylock(param) \ -+{ return au_rw_read_trylock(rwsem); } \ -+static inline int prefix##_write_trylock(param) \ -+{ return au_rw_write_trylock(rwsem); } -+/* why is not _nested version defined */ -+/* static inline void prefix##_read_trylock_nested(param, lsc) -+{ au_rw_read_trylock_nested(rwsem, lsc)); } -+static inline void prefix##_write_trylock_nestd(param, lsc) -+{ au_rw_write_trylock_nested(rwsem, lsc); } */ -+ -+#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \ -+static inline void prefix##_read_unlock(param) \ -+{ au_rw_read_unlock(rwsem); } \ -+static inline void prefix##_write_unlock(param) \ -+{ au_rw_write_unlock(rwsem); } \ -+static inline void prefix##_downgrade_lock(param) \ -+{ au_rw_dgrade_lock(rwsem); } -+ -+#define AuSimpleRwsemFuncs(prefix, param, rwsem) \ -+ AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ -+ AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_RWSEM_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/sbinfo.c linux-4.0.4/fs/aufs/sbinfo.c ---- linux-4.0.4.orig/fs/aufs/sbinfo.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/sbinfo.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,341 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * superblock private data -+ */ -+ -+#include "aufs.h" -+ -+/* -+ * they are necessary regardless sysfs is disabled. -+ */ -+void au_si_free(struct kobject *kobj) -+{ -+ int i; -+ struct au_sbinfo *sbinfo; -+ char *locked __maybe_unused; /* debug only */ -+ -+ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); -+ for (i = 0; i < AuPlink_NHASH; i++) -+ AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); -+ AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); -+ -+ au_rw_write_lock(&sbinfo->si_rwsem); -+ au_br_free(sbinfo); -+ au_rw_write_unlock(&sbinfo->si_rwsem); -+ -+ AuDebugOn(radix_tree_gang_lookup -+ (&sbinfo->au_si_pid.tree, (void **)&locked, -+ /*first_index*/PID_MAX_DEFAULT - 1, -+ /*max_items*/sizeof(locked)/sizeof(*locked))); -+ -+ kfree(sbinfo->si_branch); -+ kfree(sbinfo->au_si_pid.bitmap); -+ mutex_destroy(&sbinfo->si_xib_mtx); -+ AuRwDestroy(&sbinfo->si_rwsem); -+ -+ kfree(sbinfo); -+} -+ -+int au_si_alloc(struct super_block *sb) -+{ -+ int err, i; -+ struct au_sbinfo *sbinfo; -+ static struct lock_class_key aufs_si; -+ -+ err = -ENOMEM; -+ sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); -+ if (unlikely(!sbinfo)) -+ goto out; -+ -+ BUILD_BUG_ON(sizeof(unsigned long) != -+ sizeof(*sbinfo->au_si_pid.bitmap)); -+ sbinfo->au_si_pid.bitmap = kcalloc(BITS_TO_LONGS(PID_MAX_DEFAULT), -+ sizeof(*sbinfo->au_si_pid.bitmap), -+ GFP_NOFS); -+ if (unlikely(!sbinfo->au_si_pid.bitmap)) -+ goto out_sbinfo; -+ -+ /* will be reallocated separately */ -+ sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); -+ if (unlikely(!sbinfo->si_branch)) -+ goto out_pidmap; -+ -+ err = sysaufs_si_init(sbinfo); -+ if (unlikely(err)) -+ goto out_br; -+ -+ au_nwt_init(&sbinfo->si_nowait); -+ au_rw_init_wlock(&sbinfo->si_rwsem); -+ au_rw_class(&sbinfo->si_rwsem, &aufs_si); -+ spin_lock_init(&sbinfo->au_si_pid.tree_lock); -+ INIT_RADIX_TREE(&sbinfo->au_si_pid.tree, GFP_ATOMIC | __GFP_NOFAIL); -+ -+ atomic_long_set(&sbinfo->si_ninodes, 0); -+ atomic_long_set(&sbinfo->si_nfiles, 0); -+ -+ sbinfo->si_bend = -1; -+ sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; -+ -+ sbinfo->si_wbr_copyup = AuWbrCopyup_Def; -+ sbinfo->si_wbr_create = AuWbrCreate_Def; -+ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup; -+ sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create; -+ -+ au_fhsm_init(sbinfo); -+ -+ sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); -+ -+ sbinfo->si_xino_jiffy = jiffies; -+ sbinfo->si_xino_expire -+ = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); -+ mutex_init(&sbinfo->si_xib_mtx); -+ sbinfo->si_xino_brid = -1; -+ /* leave si_xib_last_pindex and si_xib_next_bit */ -+ -+ au_sphl_init(&sbinfo->si_aopen); -+ -+ sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC); -+ sbinfo->si_rdblk = AUFS_RDBLK_DEF; -+ sbinfo->si_rdhash = AUFS_RDHASH_DEF; -+ sbinfo->si_dirwh = AUFS_DIRWH_DEF; -+ -+ for (i = 0; i < AuPlink_NHASH; i++) -+ au_sphl_init(sbinfo->si_plink + i); -+ init_waitqueue_head(&sbinfo->si_plink_wq); -+ spin_lock_init(&sbinfo->si_plink_maint_lock); -+ -+ au_sphl_init(&sbinfo->si_files); -+ -+ /* leave other members for sysaufs and si_mnt. */ -+ sbinfo->si_sb = sb; -+ sb->s_fs_info = sbinfo; -+ si_pid_set(sb); -+ return 0; /* success */ -+ -+out_br: -+ kfree(sbinfo->si_branch); -+out_pidmap: -+ kfree(sbinfo->au_si_pid.bitmap); -+out_sbinfo: -+ kfree(sbinfo); -+out: -+ return err; -+} -+ -+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr) -+{ -+ int err, sz; -+ struct au_branch **brp; -+ -+ AuRwMustWriteLock(&sbinfo->si_rwsem); -+ -+ err = -ENOMEM; -+ sz = sizeof(*brp) * (sbinfo->si_bend + 1); -+ if (unlikely(!sz)) -+ sz = sizeof(*brp); -+ brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS); -+ if (brp) { -+ sbinfo->si_branch = brp; -+ err = 0; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+unsigned int au_sigen_inc(struct super_block *sb) -+{ -+ unsigned int gen; -+ -+ SiMustWriteLock(sb); -+ -+ gen = ++au_sbi(sb)->si_generation; -+ au_update_digen(sb->s_root); -+ au_update_iigen(sb->s_root->d_inode, /*half*/0); -+ sb->s_root->d_inode->i_version++; -+ return gen; -+} -+ -+aufs_bindex_t au_new_br_id(struct super_block *sb) -+{ -+ aufs_bindex_t br_id; -+ int i; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ for (i = 0; i <= AUFS_BRANCH_MAX; i++) { -+ br_id = ++sbinfo->si_last_br_id; -+ AuDebugOn(br_id < 0); -+ if (br_id && au_br_index(sb, br_id) < 0) -+ return br_id; -+ } -+ -+ return -1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* it is ok that new 'nwt' tasks are appended while we are sleeping */ -+int si_read_lock(struct super_block *sb, int flags) -+{ -+ int err; -+ -+ err = 0; -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ -+ si_noflush_read_lock(sb); -+ err = au_plink_maint(sb, flags); -+ if (unlikely(err)) -+ si_read_unlock(sb); -+ -+ return err; -+} -+ -+int si_write_lock(struct super_block *sb, int flags) -+{ -+ int err; -+ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ -+ si_noflush_write_lock(sb); -+ err = au_plink_maint(sb, flags); -+ if (unlikely(err)) -+ si_write_unlock(sb); -+ -+ return err; -+} -+ -+/* dentry and super_block lock. call at entry point */ -+int aufs_read_lock(struct dentry *dentry, int flags) -+{ -+ int err; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, flags); -+ if (unlikely(err)) -+ goto out; -+ -+ if (au_ftest_lock(flags, DW)) -+ di_write_lock_child(dentry); -+ else -+ di_read_lock_child(dentry, flags); -+ -+ if (au_ftest_lock(flags, GEN)) { -+ err = au_digen_test(dentry, au_sigen(sb)); -+ AuDebugOn(!err && au_dbrange_test(dentry)); -+ if (unlikely(err)) -+ aufs_read_unlock(dentry, flags); -+ } -+ -+out: -+ return err; -+} -+ -+void aufs_read_unlock(struct dentry *dentry, int flags) -+{ -+ if (au_ftest_lock(flags, DW)) -+ di_write_unlock(dentry); -+ else -+ di_read_unlock(dentry, flags); -+ si_read_unlock(dentry->d_sb); -+} -+ -+void aufs_write_lock(struct dentry *dentry) -+{ -+ si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW); -+ di_write_lock_child(dentry); -+} -+ -+void aufs_write_unlock(struct dentry *dentry) -+{ -+ di_write_unlock(dentry); -+ si_write_unlock(dentry->d_sb); -+} -+ -+int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) -+{ -+ int err; -+ unsigned int sigen; -+ struct super_block *sb; -+ -+ sb = d1->d_sb; -+ err = si_read_lock(sb, flags); -+ if (unlikely(err)) -+ goto out; -+ -+ di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); -+ -+ if (au_ftest_lock(flags, GEN)) { -+ sigen = au_sigen(sb); -+ err = au_digen_test(d1, sigen); -+ AuDebugOn(!err && au_dbrange_test(d1)); -+ if (!err) { -+ err = au_digen_test(d2, sigen); -+ AuDebugOn(!err && au_dbrange_test(d2)); -+ } -+ if (unlikely(err)) -+ aufs_read_and_write_unlock2(d1, d2); -+ } -+ -+out: -+ return err; -+} -+ -+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) -+{ -+ di_write_unlock2(d1, d2); -+ si_read_unlock(d1->d_sb); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int si_pid_test_slow(struct super_block *sb) -+{ -+ void *p; -+ -+ rcu_read_lock(); -+ p = radix_tree_lookup(&au_sbi(sb)->au_si_pid.tree, current->pid); -+ rcu_read_unlock(); -+ -+ return (long)!!p; -+} -+ -+void si_pid_set_slow(struct super_block *sb) -+{ -+ int err; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(si_pid_test_slow(sb)); -+ -+ sbinfo = au_sbi(sb); -+ err = radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); -+ AuDebugOn(err); -+ spin_lock(&sbinfo->au_si_pid.tree_lock); -+ err = radix_tree_insert(&sbinfo->au_si_pid.tree, current->pid, -+ /*any valid ptr*/sb); -+ spin_unlock(&sbinfo->au_si_pid.tree_lock); -+ AuDebugOn(err); -+ radix_tree_preload_end(); -+} -+ -+void si_pid_clr_slow(struct super_block *sb) -+{ -+ void *p; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(!si_pid_test_slow(sb)); -+ -+ sbinfo = au_sbi(sb); -+ spin_lock(&sbinfo->au_si_pid.tree_lock); -+ p = radix_tree_delete(&sbinfo->au_si_pid.tree, current->pid); -+ spin_unlock(&sbinfo->au_si_pid.tree_lock); -+} -diff -Nur linux-4.0.4.orig/fs/aufs/spl.h linux-4.0.4/fs/aufs/spl.h ---- linux-4.0.4.orig/fs/aufs/spl.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/spl.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,98 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * simple list protected by a spinlock -+ */ -+ -+#ifndef __AUFS_SPL_H__ -+#define __AUFS_SPL_H__ -+ -+#ifdef __KERNEL__ -+ -+struct au_splhead { -+ spinlock_t spin; -+ struct list_head head; -+}; -+ -+static inline void au_spl_init(struct au_splhead *spl) -+{ -+ spin_lock_init(&spl->spin); -+ INIT_LIST_HEAD(&spl->head); -+} -+ -+static inline void au_spl_add(struct list_head *list, struct au_splhead *spl) -+{ -+ spin_lock(&spl->spin); -+ list_add(list, &spl->head); -+ spin_unlock(&spl->spin); -+} -+ -+static inline void au_spl_del(struct list_head *list, struct au_splhead *spl) -+{ -+ spin_lock(&spl->spin); -+ list_del(list); -+ spin_unlock(&spl->spin); -+} -+ -+static inline void au_spl_del_rcu(struct list_head *list, -+ struct au_splhead *spl) -+{ -+ spin_lock(&spl->spin); -+ list_del_rcu(list); -+ spin_unlock(&spl->spin); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_sphlhead { -+ spinlock_t spin; -+ struct hlist_head head; -+}; -+ -+static inline void au_sphl_init(struct au_sphlhead *sphl) -+{ -+ spin_lock_init(&sphl->spin); -+ INIT_HLIST_HEAD(&sphl->head); -+} -+ -+static inline void au_sphl_add(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_add_head(hlist, &sphl->head); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline void au_sphl_del(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_del(hlist); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline void au_sphl_del_rcu(struct hlist_node *hlist, -+ struct au_sphlhead *sphl) -+{ -+ spin_lock(&sphl->spin); -+ hlist_del_rcu(hlist); -+ spin_unlock(&sphl->spin); -+} -+ -+static inline unsigned long au_sphl_count(struct au_sphlhead *sphl) -+{ -+ unsigned long cnt; -+ struct hlist_node *pos; -+ -+ cnt = 0; -+ spin_lock(&sphl->spin); -+ hlist_for_each(pos, &sphl->head) -+ cnt++; -+ spin_unlock(&sphl->spin); -+ return cnt; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_SPL_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/super.c linux-4.0.4/fs/aufs/super.c ---- linux-4.0.4.orig/fs/aufs/super.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/super.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,993 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * mount and super_block operations -+ */ -+ -+#include -+#include -+#include -+#include -+#include "aufs.h" -+ -+/* -+ * super_operations -+ */ -+static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) -+{ -+ struct au_icntnr *c; -+ -+ c = au_cache_alloc_icntnr(); -+ if (c) { -+ au_icntnr_init(c); -+ c->vfs_inode.i_version = 1; /* sigen(sb); */ -+ c->iinfo.ii_hinode = NULL; -+ return &c->vfs_inode; -+ } -+ return NULL; -+} -+ -+static void aufs_destroy_inode_cb(struct rcu_head *head) -+{ -+ struct inode *inode = container_of(head, struct inode, i_rcu); -+ -+ INIT_HLIST_HEAD(&inode->i_dentry); -+ au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); -+} -+ -+static void aufs_destroy_inode(struct inode *inode) -+{ -+ au_iinfo_fin(inode); -+ call_rcu(&inode->i_rcu, aufs_destroy_inode_cb); -+} -+ -+struct inode *au_iget_locked(struct super_block *sb, ino_t ino) -+{ -+ struct inode *inode; -+ int err; -+ -+ inode = iget_locked(sb, ino); -+ if (unlikely(!inode)) { -+ inode = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ if (!(inode->i_state & I_NEW)) -+ goto out; -+ -+ err = au_xigen_new(inode); -+ if (!err) -+ err = au_iinfo_init(inode); -+ if (!err) -+ inode->i_version++; -+ else { -+ iget_failed(inode); -+ inode = ERR_PTR(err); -+ } -+ -+out: -+ /* never return NULL */ -+ AuDebugOn(!inode); -+ AuTraceErrPtr(inode); -+ return inode; -+} -+ -+/* lock free root dinfo */ -+static int au_show_brs(struct seq_file *seq, struct super_block *sb) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ struct path path; -+ struct au_hdentry *hdp; -+ struct au_branch *br; -+ au_br_perm_str_t perm; -+ -+ err = 0; -+ bend = au_sbend(sb); -+ hdp = au_di(sb->s_root)->di_hdentry; -+ for (bindex = 0; !err && bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ path.mnt = au_br_mnt(br); -+ path.dentry = hdp[bindex].hd_dentry; -+ err = au_seq_path(seq, &path); -+ if (err > 0) { -+ au_optstr_br_perm(&perm, br->br_perm); -+ err = seq_printf(seq, "=%s", perm.a); -+ if (err == -1) -+ err = -E2BIG; -+ } -+ if (!err && bindex != bend) -+ err = seq_putc(seq, ':'); -+ } -+ -+ return err; -+} -+ -+static void au_show_wbr_create(struct seq_file *m, int v, -+ struct au_sbinfo *sbinfo) -+{ -+ const char *pat; -+ -+ AuRwMustAnyLock(&sbinfo->si_rwsem); -+ -+ seq_puts(m, ",create="); -+ pat = au_optstr_wbr_create(v); -+ switch (v) { -+ case AuWbrCreate_TDP: -+ case AuWbrCreate_RR: -+ case AuWbrCreate_MFS: -+ case AuWbrCreate_PMFS: -+ seq_puts(m, pat); -+ break; -+ case AuWbrCreate_MFSV: -+ seq_printf(m, /*pat*/"mfs:%lu", -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ case AuWbrCreate_PMFSV: -+ seq_printf(m, /*pat*/"pmfs:%lu", -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ case AuWbrCreate_MFSRR: -+ seq_printf(m, /*pat*/"mfsrr:%llu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark); -+ break; -+ case AuWbrCreate_MFSRRV: -+ seq_printf(m, /*pat*/"mfsrr:%llu:%lu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark, -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ case AuWbrCreate_PMFSRR: -+ seq_printf(m, /*pat*/"pmfsrr:%llu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark); -+ break; -+ case AuWbrCreate_PMFSRRV: -+ seq_printf(m, /*pat*/"pmfsrr:%llu:%lu", -+ sbinfo->si_wbr_mfs.mfsrr_watermark, -+ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) -+ / MSEC_PER_SEC); -+ break; -+ } -+} -+ -+static int au_show_xino(struct seq_file *seq, struct super_block *sb) -+{ -+#ifdef CONFIG_SYSFS -+ return 0; -+#else -+ int err; -+ const int len = sizeof(AUFS_XINO_FNAME) - 1; -+ aufs_bindex_t bindex, brid; -+ struct qstr *name; -+ struct file *f; -+ struct dentry *d, *h_root; -+ struct au_hdentry *hdp; -+ -+ AuRwMustAnyLock(&sbinfo->si_rwsem); -+ -+ err = 0; -+ f = au_sbi(sb)->si_xib; -+ if (!f) -+ goto out; -+ -+ /* stop printing the default xino path on the first writable branch */ -+ h_root = NULL; -+ brid = au_xino_brid(sb); -+ if (brid >= 0) { -+ bindex = au_br_index(sb, brid); -+ hdp = au_di(sb->s_root)->di_hdentry; -+ h_root = hdp[0 + bindex].hd_dentry; -+ } -+ d = f->f_path.dentry; -+ name = &d->d_name; -+ /* safe ->d_parent because the file is unlinked */ -+ if (d->d_parent == h_root -+ && name->len == len -+ && !memcmp(name->name, AUFS_XINO_FNAME, len)) -+ goto out; -+ -+ seq_puts(seq, ",xino="); -+ err = au_xino_path(seq, f); -+ -+out: -+ return err; -+#endif -+} -+ -+/* seq_file will re-call me in case of too long string */ -+static int aufs_show_options(struct seq_file *m, struct dentry *dentry) -+{ -+ int err; -+ unsigned int mnt_flags, v; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+#define AuBool(name, str) do { \ -+ v = au_opt_test(mnt_flags, name); \ -+ if (v != au_opt_test(AuOpt_Def, name)) \ -+ seq_printf(m, ",%s" #str, v ? "" : "no"); \ -+} while (0) -+ -+#define AuStr(name, str) do { \ -+ v = mnt_flags & AuOptMask_##name; \ -+ if (v != (AuOpt_Def & AuOptMask_##name)) \ -+ seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ -+} while (0) -+ -+#define AuUInt(name, str, val) do { \ -+ if (val != AUFS_##name##_DEF) \ -+ seq_printf(m, "," #str "=%u", val); \ -+} while (0) -+ -+ sb = dentry->d_sb; -+ if (sb->s_flags & MS_POSIXACL) -+ seq_puts(m, ",acl"); -+ -+ /* lock free root dinfo */ -+ si_noflush_read_lock(sb); -+ sbinfo = au_sbi(sb); -+ seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo)); -+ -+ mnt_flags = au_mntflags(sb); -+ if (au_opt_test(mnt_flags, XINO)) { -+ err = au_show_xino(m, sb); -+ if (unlikely(err)) -+ goto out; -+ } else -+ seq_puts(m, ",noxino"); -+ -+ AuBool(TRUNC_XINO, trunc_xino); -+ AuStr(UDBA, udba); -+ AuBool(SHWH, shwh); -+ AuBool(PLINK, plink); -+ AuBool(DIO, dio); -+ AuBool(DIRPERM1, dirperm1); -+ -+ v = sbinfo->si_wbr_create; -+ if (v != AuWbrCreate_Def) -+ au_show_wbr_create(m, v, sbinfo); -+ -+ v = sbinfo->si_wbr_copyup; -+ if (v != AuWbrCopyup_Def) -+ seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); -+ -+ v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); -+ if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) -+ seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); -+ -+ AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); -+ -+ v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; -+ AuUInt(RDCACHE, rdcache, v); -+ -+ AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); -+ AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); -+ -+ au_fhsm_show(m, sbinfo); -+ -+ AuBool(SUM, sum); -+ /* AuBool(SUM_W, wsum); */ -+ AuBool(WARN_PERM, warn_perm); -+ AuBool(VERBOSE, verbose); -+ -+out: -+ /* be sure to print "br:" last */ -+ if (!sysaufs_brs) { -+ seq_puts(m, ",br:"); -+ au_show_brs(m, sb); -+ } -+ si_read_unlock(sb); -+ return 0; -+ -+#undef AuBool -+#undef AuStr -+#undef AuUInt -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* sum mode which returns the summation for statfs(2) */ -+ -+static u64 au_add_till_max(u64 a, u64 b) -+{ -+ u64 old; -+ -+ old = a; -+ a += b; -+ if (old <= a) -+ return a; -+ return ULLONG_MAX; -+} -+ -+static u64 au_mul_till_max(u64 a, long mul) -+{ -+ u64 old; -+ -+ old = a; -+ a *= mul; -+ if (old <= a) -+ return a; -+ return ULLONG_MAX; -+} -+ -+static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf) -+{ -+ int err; -+ long bsize, factor; -+ u64 blocks, bfree, bavail, files, ffree; -+ aufs_bindex_t bend, bindex, i; -+ unsigned char shared; -+ struct path h_path; -+ struct super_block *h_sb; -+ -+ err = 0; -+ bsize = LONG_MAX; -+ files = 0; -+ ffree = 0; -+ blocks = 0; -+ bfree = 0; -+ bavail = 0; -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ h_path.mnt = au_sbr_mnt(sb, bindex); -+ h_sb = h_path.mnt->mnt_sb; -+ shared = 0; -+ for (i = 0; !shared && i < bindex; i++) -+ shared = (au_sbr_sb(sb, i) == h_sb); -+ if (shared) -+ continue; -+ -+ /* sb->s_root for NFS is unreliable */ -+ h_path.dentry = h_path.mnt->mnt_root; -+ err = vfs_statfs(&h_path, buf); -+ if (unlikely(err)) -+ goto out; -+ -+ if (bsize > buf->f_bsize) { -+ /* -+ * we will reduce bsize, so we have to expand blocks -+ * etc. to match them again -+ */ -+ factor = (bsize / buf->f_bsize); -+ blocks = au_mul_till_max(blocks, factor); -+ bfree = au_mul_till_max(bfree, factor); -+ bavail = au_mul_till_max(bavail, factor); -+ bsize = buf->f_bsize; -+ } -+ -+ factor = (buf->f_bsize / bsize); -+ blocks = au_add_till_max(blocks, -+ au_mul_till_max(buf->f_blocks, factor)); -+ bfree = au_add_till_max(bfree, -+ au_mul_till_max(buf->f_bfree, factor)); -+ bavail = au_add_till_max(bavail, -+ au_mul_till_max(buf->f_bavail, factor)); -+ files = au_add_till_max(files, buf->f_files); -+ ffree = au_add_till_max(ffree, buf->f_ffree); -+ } -+ -+ buf->f_bsize = bsize; -+ buf->f_blocks = blocks; -+ buf->f_bfree = bfree; -+ buf->f_bavail = bavail; -+ buf->f_files = files; -+ buf->f_ffree = ffree; -+ buf->f_frsize = 0; -+ -+out: -+ return err; -+} -+ -+static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) -+{ -+ int err; -+ struct path h_path; -+ struct super_block *sb; -+ -+ /* lock free root dinfo */ -+ sb = dentry->d_sb; -+ si_noflush_read_lock(sb); -+ if (!au_opt_test(au_mntflags(sb), SUM)) { -+ /* sb->s_root for NFS is unreliable */ -+ h_path.mnt = au_sbr_mnt(sb, 0); -+ h_path.dentry = h_path.mnt->mnt_root; -+ err = vfs_statfs(&h_path, buf); -+ } else -+ err = au_statfs_sum(sb, buf); -+ si_read_unlock(sb); -+ -+ if (!err) { -+ buf->f_type = AUFS_SUPER_MAGIC; -+ buf->f_namelen = AUFS_MAX_NAMELEN; -+ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); -+ } -+ /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_sync_fs(struct super_block *sb, int wait) -+{ -+ int err, e; -+ aufs_bindex_t bend, bindex; -+ struct au_branch *br; -+ struct super_block *h_sb; -+ -+ err = 0; -+ si_noflush_read_lock(sb); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!au_br_writable(br->br_perm)) -+ continue; -+ -+ h_sb = au_sbr_sb(sb, bindex); -+ if (h_sb->s_op->sync_fs) { -+ e = h_sb->s_op->sync_fs(h_sb, wait); -+ if (unlikely(e && !err)) -+ err = e; -+ /* go on even if an error happens */ -+ } -+ } -+ si_read_unlock(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* final actions when unmounting a file system */ -+static void aufs_put_super(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = au_sbi(sb); -+ if (!sbinfo) -+ return; -+ -+ dbgaufs_si_fin(sbinfo); -+ kobject_put(&sbinfo->si_kobj); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_array_free(void *array) -+{ -+ if (array) { -+ if (!is_vmalloc_addr(array)) -+ kfree(array); -+ else -+ vfree(array); -+ } -+} -+ -+void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg) -+{ -+ void *array; -+ unsigned long long n, sz; -+ -+ array = NULL; -+ n = 0; -+ if (!*hint) -+ goto out; -+ -+ if (*hint > ULLONG_MAX / sizeof(array)) { -+ array = ERR_PTR(-EMFILE); -+ pr_err("hint %llu\n", *hint); -+ goto out; -+ } -+ -+ sz = sizeof(array) * *hint; -+ array = kzalloc(sz, GFP_NOFS); -+ if (unlikely(!array)) -+ array = vzalloc(sz); -+ if (unlikely(!array)) { -+ array = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ n = cb(array, *hint, arg); -+ AuDebugOn(n > *hint); -+ -+out: -+ *hint = n; -+ return array; -+} -+ -+static unsigned long long au_iarray_cb(void *a, -+ unsigned long long max __maybe_unused, -+ void *arg) -+{ -+ unsigned long long n; -+ struct inode **p, *inode; -+ struct list_head *head; -+ -+ n = 0; -+ p = a; -+ head = arg; -+ spin_lock(&inode_sb_list_lock); -+ list_for_each_entry(inode, head, i_sb_list) { -+ if (!is_bad_inode(inode) -+ && au_ii(inode)->ii_bstart >= 0) { -+ spin_lock(&inode->i_lock); -+ if (atomic_read(&inode->i_count)) { -+ au_igrab(inode); -+ *p++ = inode; -+ n++; -+ AuDebugOn(n > max); -+ } -+ spin_unlock(&inode->i_lock); -+ } -+ } -+ spin_unlock(&inode_sb_list_lock); -+ -+ return n; -+} -+ -+struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) -+{ -+ *max = atomic_long_read(&au_sbi(sb)->si_ninodes); -+ return au_array_alloc(max, au_iarray_cb, &sb->s_inodes); -+} -+ -+void au_iarray_free(struct inode **a, unsigned long long max) -+{ -+ unsigned long long ull; -+ -+ for (ull = 0; ull < max; ull++) -+ iput(a[ull]); -+ au_array_free(a); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * refresh dentry and inode at remount time. -+ */ -+/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */ -+static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags, -+ struct dentry *parent) -+{ -+ int err; -+ -+ di_write_lock_child(dentry); -+ di_read_lock_parent(parent, AuLock_IR); -+ err = au_refresh_dentry(dentry, parent); -+ if (!err && dir_flags) -+ au_hn_reset(dentry->d_inode, dir_flags); -+ di_read_unlock(parent, AuLock_IR); -+ di_write_unlock(dentry); -+ -+ return err; -+} -+ -+static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen, -+ struct au_sbinfo *sbinfo, -+ const unsigned int dir_flags) -+{ -+ int err; -+ struct dentry *parent; -+ struct inode *inode; -+ -+ err = 0; -+ parent = dget_parent(dentry); -+ if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) { -+ inode = dentry->d_inode; -+ if (inode) { -+ if (!S_ISDIR(inode->i_mode)) -+ err = au_do_refresh(dentry, /*dir_flags*/0, -+ parent); -+ else { -+ err = au_do_refresh(dentry, dir_flags, parent); -+ if (unlikely(err)) -+ au_fset_si(sbinfo, FAILED_REFRESH_DIR); -+ } -+ } else -+ err = au_do_refresh(dentry, /*dir_flags*/0, parent); -+ AuDbgDentry(dentry); -+ } -+ dput(parent); -+ -+ AuTraceErr(err); -+ return err; -+} -+ -+static int au_refresh_d(struct super_block *sb) -+{ -+ int err, i, j, ndentry, e; -+ unsigned int sigen; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries, *d; -+ struct au_sbinfo *sbinfo; -+ struct dentry *root = sb->s_root; -+ const unsigned int dir_flags = au_hi_flags(root->d_inode, /*isdir*/1); -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_dcsub_pages(&dpages, root, NULL, NULL); -+ if (unlikely(err)) -+ goto out_dpages; -+ -+ sigen = au_sigen(sb); -+ sbinfo = au_sbi(sb); -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) { -+ d = dentries[j]; -+ e = au_do_refresh_d(d, sigen, sbinfo, dir_flags); -+ if (unlikely(e && !err)) -+ err = e; -+ /* go on even err */ -+ } -+ } -+ -+out_dpages: -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static int au_refresh_i(struct super_block *sb) -+{ -+ int err, e; -+ unsigned int sigen; -+ unsigned long long max, ull; -+ struct inode *inode, **array; -+ -+ array = au_iarray_alloc(sb, &max); -+ err = PTR_ERR(array); -+ if (IS_ERR(array)) -+ goto out; -+ -+ err = 0; -+ sigen = au_sigen(sb); -+ for (ull = 0; ull < max; ull++) { -+ inode = array[ull]; -+ if (unlikely(!inode)) -+ break; -+ if (au_iigen(inode, NULL) != sigen) { -+ ii_write_lock_child(inode); -+ e = au_refresh_hinode_self(inode); -+ ii_write_unlock(inode); -+ if (unlikely(e)) { -+ pr_err("error %d, i%lu\n", e, inode->i_ino); -+ if (!err) -+ err = e; -+ /* go on even if err */ -+ } -+ } -+ } -+ -+ au_iarray_free(array, max); -+ -+out: -+ return err; -+} -+ -+static void au_remount_refresh(struct super_block *sb) -+{ -+ int err, e; -+ unsigned int udba; -+ aufs_bindex_t bindex, bend; -+ struct dentry *root; -+ struct inode *inode; -+ struct au_branch *br; -+ -+ au_sigen_inc(sb); -+ au_fclr_si(au_sbi(sb), FAILED_REFRESH_DIR); -+ -+ root = sb->s_root; -+ DiMustNoWaiters(root); -+ inode = root->d_inode; -+ IiMustNoWaiters(inode); -+ -+ udba = au_opt_udba(sb); -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ err = au_hnotify_reset_br(udba, br, br->br_perm); -+ if (unlikely(err)) -+ AuIOErr("hnotify failed on br %d, %d, ignored\n", -+ bindex, err); -+ /* go on even if err */ -+ } -+ au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); -+ -+ di_write_unlock(root); -+ err = au_refresh_d(sb); -+ e = au_refresh_i(sb); -+ if (unlikely(e && !err)) -+ err = e; -+ /* aufs_write_lock() calls ..._child() */ -+ di_write_lock_child(root); -+ -+ au_cpup_attr_all(inode, /*force*/1); -+ -+ if (unlikely(err)) -+ AuIOErr("refresh failed, ignored, %d\n", err); -+} -+ -+/* stop extra interpretation of errno in mount(8), and strange error messages */ -+static int cvt_err(int err) -+{ -+ AuTraceErr(err); -+ -+ switch (err) { -+ case -ENOENT: -+ case -ENOTDIR: -+ case -EEXIST: -+ case -EIO: -+ err = -EINVAL; -+ } -+ return err; -+} -+ -+static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) -+{ -+ int err, do_dx; -+ unsigned int mntflags; -+ struct au_opts opts; -+ struct dentry *root; -+ struct inode *inode; -+ struct au_sbinfo *sbinfo; -+ -+ err = 0; -+ root = sb->s_root; -+ if (!data || !*data) { -+ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (!err) { -+ di_write_lock_child(root); -+ err = au_opts_verify(sb, *flags, /*pending*/0); -+ aufs_write_unlock(root); -+ } -+ goto out; -+ } -+ -+ err = -ENOMEM; -+ memset(&opts, 0, sizeof(opts)); -+ opts.opt = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!opts.opt)) -+ goto out; -+ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); -+ opts.flags = AuOpts_REMOUNT; -+ opts.sb_flags = *flags; -+ -+ /* parse it before aufs lock */ -+ err = au_opts_parse(sb, data, &opts); -+ if (unlikely(err)) -+ goto out_opts; -+ -+ sbinfo = au_sbi(sb); -+ inode = root->d_inode; -+ mutex_lock(&inode->i_mutex); -+ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out_mtx; -+ di_write_lock_child(root); -+ -+ /* au_opts_remount() may return an error */ -+ err = au_opts_remount(sb, &opts); -+ au_opts_free(&opts); -+ -+ if (au_ftest_opts(opts.flags, REFRESH)) -+ au_remount_refresh(sb); -+ -+ if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) { -+ mntflags = au_mntflags(sb); -+ do_dx = !!au_opt_test(mntflags, DIO); -+ au_dy_arefresh(do_dx); -+ } -+ -+ au_fhsm_wrote_all(sb, /*force*/1); /* ?? */ -+ aufs_write_unlock(root); -+ -+out_mtx: -+ mutex_unlock(&inode->i_mutex); -+out_opts: -+ free_page((unsigned long)opts.opt); -+out: -+ err = cvt_err(err); -+ AuTraceErr(err); -+ return err; -+} -+ -+static const struct super_operations aufs_sop = { -+ .alloc_inode = aufs_alloc_inode, -+ .destroy_inode = aufs_destroy_inode, -+ /* always deleting, no clearing */ -+ .drop_inode = generic_delete_inode, -+ .show_options = aufs_show_options, -+ .statfs = aufs_statfs, -+ .put_super = aufs_put_super, -+ .sync_fs = aufs_sync_fs, -+ .remount_fs = aufs_remount_fs -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int alloc_root(struct super_block *sb) -+{ -+ int err; -+ struct inode *inode; -+ struct dentry *root; -+ -+ err = -ENOMEM; -+ inode = au_iget_locked(sb, AUFS_ROOT_INO); -+ err = PTR_ERR(inode); -+ if (IS_ERR(inode)) -+ goto out; -+ -+ inode->i_op = &aufs_dir_iop; -+ inode->i_fop = &aufs_dir_fop; -+ inode->i_mode = S_IFDIR; -+ set_nlink(inode, 2); -+ unlock_new_inode(inode); -+ -+ root = d_make_root(inode); -+ if (unlikely(!root)) -+ goto out; -+ err = PTR_ERR(root); -+ if (IS_ERR(root)) -+ goto out; -+ -+ err = au_di_init(root); -+ if (!err) { -+ sb->s_root = root; -+ return 0; /* success */ -+ } -+ dput(root); -+ -+out: -+ return err; -+} -+ -+static int aufs_fill_super(struct super_block *sb, void *raw_data, -+ int silent __maybe_unused) -+{ -+ int err; -+ struct au_opts opts; -+ struct dentry *root; -+ struct inode *inode; -+ char *arg = raw_data; -+ -+ if (unlikely(!arg || !*arg)) { -+ err = -EINVAL; -+ pr_err("no arg\n"); -+ goto out; -+ } -+ -+ err = -ENOMEM; -+ memset(&opts, 0, sizeof(opts)); -+ opts.opt = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!opts.opt)) -+ goto out; -+ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); -+ opts.sb_flags = sb->s_flags; -+ -+ err = au_si_alloc(sb); -+ if (unlikely(err)) -+ goto out_opts; -+ -+ /* all timestamps always follow the ones on the branch */ -+ sb->s_flags |= MS_NOATIME | MS_NODIRATIME; -+ sb->s_op = &aufs_sop; -+ sb->s_d_op = &aufs_dop; -+ sb->s_magic = AUFS_SUPER_MAGIC; -+ sb->s_maxbytes = 0; -+ sb->s_stack_depth = 1; -+ au_export_init(sb); -+ /* au_xattr_init(sb); */ -+ -+ err = alloc_root(sb); -+ if (unlikely(err)) { -+ si_write_unlock(sb); -+ goto out_info; -+ } -+ root = sb->s_root; -+ inode = root->d_inode; -+ -+ /* -+ * actually we can parse options regardless aufs lock here. -+ * but at remount time, parsing must be done before aufs lock. -+ * so we follow the same rule. -+ */ -+ ii_write_lock_parent(inode); -+ aufs_write_unlock(root); -+ err = au_opts_parse(sb, arg, &opts); -+ if (unlikely(err)) -+ goto out_root; -+ -+ /* lock vfs_inode first, then aufs. */ -+ mutex_lock(&inode->i_mutex); -+ aufs_write_lock(root); -+ err = au_opts_mount(sb, &opts); -+ au_opts_free(&opts); -+ aufs_write_unlock(root); -+ mutex_unlock(&inode->i_mutex); -+ if (!err) -+ goto out_opts; /* success */ -+ -+out_root: -+ dput(root); -+ sb->s_root = NULL; -+out_info: -+ dbgaufs_si_fin(au_sbi(sb)); -+ kobject_put(&au_sbi(sb)->si_kobj); -+ sb->s_fs_info = NULL; -+out_opts: -+ free_page((unsigned long)opts.opt); -+out: -+ AuTraceErr(err); -+ err = cvt_err(err); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct dentry *aufs_mount(struct file_system_type *fs_type, int flags, -+ const char *dev_name __maybe_unused, -+ void *raw_data) -+{ -+ struct dentry *root; -+ struct super_block *sb; -+ -+ /* all timestamps always follow the ones on the branch */ -+ /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */ -+ root = mount_nodev(fs_type, flags, raw_data, aufs_fill_super); -+ if (IS_ERR(root)) -+ goto out; -+ -+ sb = root->d_sb; -+ si_write_lock(sb, !AuLock_FLUSH); -+ sysaufs_brs_add(sb, 0); -+ si_write_unlock(sb); -+ au_sbilist_add(sb); -+ -+out: -+ return root; -+} -+ -+static void aufs_kill_sb(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ sbinfo = au_sbi(sb); -+ if (sbinfo) { -+ au_sbilist_del(sb); -+ aufs_write_lock(sb->s_root); -+ au_fhsm_fin(sb); -+ if (sbinfo->si_wbr_create_ops->fin) -+ sbinfo->si_wbr_create_ops->fin(sb); -+ if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) { -+ au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE); -+ au_remount_refresh(sb); -+ } -+ if (au_opt_test(sbinfo->si_mntflags, PLINK)) -+ au_plink_put(sb, /*verbose*/1); -+ au_xino_clr(sb); -+ sbinfo->si_sb = NULL; -+ aufs_write_unlock(sb->s_root); -+ au_nwt_flush(&sbinfo->si_nowait); -+ } -+ kill_anon_super(sb); -+} -+ -+struct file_system_type aufs_fs_type = { -+ .name = AUFS_FSTYPE, -+ /* a race between rename and others */ -+ .fs_flags = FS_RENAME_DOES_D_MOVE, -+ .mount = aufs_mount, -+ .kill_sb = aufs_kill_sb, -+ /* no need to __module_get() and module_put(). */ -+ .owner = THIS_MODULE, -+}; -diff -Nur linux-4.0.4.orig/fs/aufs/super.h linux-4.0.4/fs/aufs/super.h ---- linux-4.0.4.orig/fs/aufs/super.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/super.h 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,626 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * super_block operations -+ */ -+ -+#ifndef __AUFS_SUPER_H__ -+#define __AUFS_SUPER_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include "rwsem.h" -+#include "spl.h" -+#include "wkq.h" -+ -+typedef ssize_t (*au_readf_t)(struct file *, char __user *, size_t, loff_t *); -+typedef ssize_t (*au_writef_t)(struct file *, const char __user *, size_t, -+ loff_t *); -+ -+/* policies to select one among multiple writable branches */ -+struct au_wbr_copyup_operations { -+ int (*copyup)(struct dentry *dentry); -+}; -+ -+#define AuWbr_DIR 1 /* target is a dir */ -+#define AuWbr_PARENT (1 << 1) /* always require a parent */ -+ -+#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name) -+#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; } -+#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; } -+ -+struct au_wbr_create_operations { -+ int (*create)(struct dentry *dentry, unsigned int flags); -+ int (*init)(struct super_block *sb); -+ int (*fin)(struct super_block *sb); -+}; -+ -+struct au_wbr_mfs { -+ struct mutex mfs_lock; /* protect this structure */ -+ unsigned long mfs_jiffy; -+ unsigned long mfs_expire; -+ aufs_bindex_t mfs_bindex; -+ -+ unsigned long long mfsrr_bytes; -+ unsigned long long mfsrr_watermark; -+}; -+ -+struct pseudo_link { -+ union { -+ struct hlist_node hlist; -+ struct rcu_head rcu; -+ }; -+ struct inode *inode; -+}; -+ -+#define AuPlink_NHASH 100 -+static inline int au_plink_hash(ino_t ino) -+{ -+ return ino % AuPlink_NHASH; -+} -+ -+/* File-based Hierarchical Storage Management */ -+struct au_fhsm { -+#ifdef CONFIG_AUFS_FHSM -+ /* allow only one process who can receive the notification */ -+ spinlock_t fhsm_spin; -+ pid_t fhsm_pid; -+ wait_queue_head_t fhsm_wqh; -+ atomic_t fhsm_readable; -+ -+ /* these are protected by si_rwsem */ -+ unsigned long fhsm_expire; -+ aufs_bindex_t fhsm_bottom; -+#endif -+}; -+ -+struct au_branch; -+struct au_sbinfo { -+ /* nowait tasks in the system-wide workqueue */ -+ struct au_nowait_tasks si_nowait; -+ -+ /* -+ * tried sb->s_umount, but failed due to the dependecy between i_mutex. -+ * rwsem for au_sbinfo is necessary. -+ */ -+ struct au_rwsem si_rwsem; -+ -+ /* prevent recursive locking in deleting inode */ -+ struct { -+ unsigned long *bitmap; -+ spinlock_t tree_lock; -+ struct radix_tree_root tree; -+ } au_si_pid; -+ -+ /* -+ * dirty approach to protect sb->sb_inodes and ->s_files (gone) from -+ * remount. -+ */ -+ atomic_long_t si_ninodes, si_nfiles; -+ -+ /* branch management */ -+ unsigned int si_generation; -+ -+ /* see AuSi_ flags */ -+ unsigned char au_si_status; -+ -+ aufs_bindex_t si_bend; -+ -+ /* dirty trick to keep br_id plus */ -+ unsigned int si_last_br_id : -+ sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1; -+ struct au_branch **si_branch; -+ -+ /* policy to select a writable branch */ -+ unsigned char si_wbr_copyup; -+ unsigned char si_wbr_create; -+ struct au_wbr_copyup_operations *si_wbr_copyup_ops; -+ struct au_wbr_create_operations *si_wbr_create_ops; -+ -+ /* round robin */ -+ atomic_t si_wbr_rr_next; -+ -+ /* most free space */ -+ struct au_wbr_mfs si_wbr_mfs; -+ -+ /* File-based Hierarchical Storage Management */ -+ struct au_fhsm si_fhsm; -+ -+ /* mount flags */ -+ /* include/asm-ia64/siginfo.h defines a macro named si_flags */ -+ unsigned int si_mntflags; -+ -+ /* external inode number (bitmap and translation table) */ -+ au_readf_t si_xread; -+ au_writef_t si_xwrite; -+ struct file *si_xib; -+ struct mutex si_xib_mtx; /* protect xib members */ -+ unsigned long *si_xib_buf; -+ unsigned long si_xib_last_pindex; -+ int si_xib_next_bit; -+ aufs_bindex_t si_xino_brid; -+ unsigned long si_xino_jiffy; -+ unsigned long si_xino_expire; -+ /* reserved for future use */ -+ /* unsigned long long si_xib_limit; */ /* Max xib file size */ -+ -+#ifdef CONFIG_AUFS_EXPORT -+ /* i_generation */ -+ struct file *si_xigen; -+ atomic_t si_xigen_next; -+#endif -+ -+ /* dirty trick to suppoer atomic_open */ -+ struct au_sphlhead si_aopen; -+ -+ /* vdir parameters */ -+ unsigned long si_rdcache; /* max cache time in jiffies */ -+ unsigned int si_rdblk; /* deblk size */ -+ unsigned int si_rdhash; /* hash size */ -+ -+ /* -+ * If the number of whiteouts are larger than si_dirwh, leave all of -+ * them after au_whtmp_ren to reduce the cost of rmdir(2). -+ * future fsck.aufs or kernel thread will remove them later. -+ * Otherwise, remove all whiteouts and the dir in rmdir(2). -+ */ -+ unsigned int si_dirwh; -+ -+ /* pseudo_link list */ -+ struct au_sphlhead si_plink[AuPlink_NHASH]; -+ wait_queue_head_t si_plink_wq; -+ spinlock_t si_plink_maint_lock; -+ pid_t si_plink_maint_pid; -+ -+ /* file list */ -+ struct au_sphlhead si_files; -+ -+ /* -+ * sysfs and lifetime management. -+ * this is not a small structure and it may be a waste of memory in case -+ * of sysfs is disabled, particulary when many aufs-es are mounted. -+ * but using sysfs is majority. -+ */ -+ struct kobject si_kobj; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *si_dbgaufs; -+ struct dentry *si_dbgaufs_plink; -+ struct dentry *si_dbgaufs_xib; -+#ifdef CONFIG_AUFS_EXPORT -+ struct dentry *si_dbgaufs_xigen; -+#endif -+#endif -+ -+#ifdef CONFIG_AUFS_SBILIST -+ struct list_head si_list; -+#endif -+ -+ /* dirty, necessary for unmounting, sysfs and sysrq */ -+ struct super_block *si_sb; -+}; -+ -+/* sbinfo status flags */ -+/* -+ * set true when refresh_dirs() failed at remount time. -+ * then try refreshing dirs at access time again. -+ * if it is false, refreshing dirs at access time is unnecesary -+ */ -+#define AuSi_FAILED_REFRESH_DIR 1 -+ -+#define AuSi_FHSM (1 << 1) /* fhsm is active now */ -+ -+#ifndef CONFIG_AUFS_FHSM -+#undef AuSi_FHSM -+#define AuSi_FHSM 0 -+#endif -+ -+static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, -+ unsigned int flag) -+{ -+ AuRwMustAnyLock(&sbi->si_rwsem); -+ return sbi->au_si_status & flag; -+} -+#define au_ftest_si(sbinfo, name) au_do_ftest_si(sbinfo, AuSi_##name) -+#define au_fset_si(sbinfo, name) do { \ -+ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ -+ (sbinfo)->au_si_status |= AuSi_##name; \ -+} while (0) -+#define au_fclr_si(sbinfo, name) do { \ -+ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ -+ (sbinfo)->au_si_status &= ~AuSi_##name; \ -+} while (0) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policy to select one among writable branches */ -+#define AuWbrCopyup(sbinfo, ...) \ -+ ((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__)) -+#define AuWbrCreate(sbinfo, ...) \ -+ ((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__)) -+ -+/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ -+#define AuLock_DW 1 /* write-lock dentry */ -+#define AuLock_IR (1 << 1) /* read-lock inode */ -+#define AuLock_IW (1 << 2) /* write-lock inode */ -+#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ -+#define AuLock_DIR (1 << 4) /* target is a dir */ -+#define AuLock_NOPLM (1 << 5) /* return err in plm mode */ -+#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */ -+#define AuLock_GEN (1 << 7) /* test digen/iigen */ -+#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) -+#define au_fset_lock(flags, name) \ -+ do { (flags) |= AuLock_##name; } while (0) -+#define au_fclr_lock(flags, name) \ -+ do { (flags) &= ~AuLock_##name; } while (0) -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* super.c */ -+extern struct file_system_type aufs_fs_type; -+struct inode *au_iget_locked(struct super_block *sb, ino_t ino); -+typedef unsigned long long (*au_arraycb_t)(void *array, unsigned long long max, -+ void *arg); -+void au_array_free(void *array); -+void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg); -+struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max); -+void au_iarray_free(struct inode **a, unsigned long long max); -+ -+/* sbinfo.c */ -+void au_si_free(struct kobject *kobj); -+int au_si_alloc(struct super_block *sb); -+int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr); -+ -+unsigned int au_sigen_inc(struct super_block *sb); -+aufs_bindex_t au_new_br_id(struct super_block *sb); -+ -+int si_read_lock(struct super_block *sb, int flags); -+int si_write_lock(struct super_block *sb, int flags); -+int aufs_read_lock(struct dentry *dentry, int flags); -+void aufs_read_unlock(struct dentry *dentry, int flags); -+void aufs_write_lock(struct dentry *dentry); -+void aufs_write_unlock(struct dentry *dentry); -+int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); -+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); -+ -+int si_pid_test_slow(struct super_block *sb); -+void si_pid_set_slow(struct super_block *sb); -+void si_pid_clr_slow(struct super_block *sb); -+ -+/* wbr_policy.c */ -+extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; -+extern struct au_wbr_create_operations au_wbr_create_ops[]; -+int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); -+int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex); -+int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart); -+ -+/* mvdown.c */ -+int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg); -+ -+#ifdef CONFIG_AUFS_FHSM -+/* fhsm.c */ -+ -+static inline pid_t au_fhsm_pid(struct au_fhsm *fhsm) -+{ -+ pid_t pid; -+ -+ spin_lock(&fhsm->fhsm_spin); -+ pid = fhsm->fhsm_pid; -+ spin_unlock(&fhsm->fhsm_spin); -+ -+ return pid; -+} -+ -+void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force); -+void au_fhsm_wrote_all(struct super_block *sb, int force); -+int au_fhsm_fd(struct super_block *sb, int oflags); -+int au_fhsm_br_alloc(struct au_branch *br); -+void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex); -+void au_fhsm_fin(struct super_block *sb); -+void au_fhsm_init(struct au_sbinfo *sbinfo); -+void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec); -+void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo); -+#else -+AuStubVoid(au_fhsm_wrote, struct super_block *sb, aufs_bindex_t bindex, -+ int force) -+AuStubVoid(au_fhsm_wrote_all, struct super_block *sb, int force) -+AuStub(int, au_fhsm_fd, return -EOPNOTSUPP, struct super_block *sb, int oflags) -+AuStub(pid_t, au_fhsm_pid, return 0, struct au_fhsm *fhsm) -+AuStubInt0(au_fhsm_br_alloc, struct au_branch *br) -+AuStubVoid(au_fhsm_set_bottom, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(au_fhsm_fin, struct super_block *sb) -+AuStubVoid(au_fhsm_init, struct au_sbinfo *sbinfo) -+AuStubVoid(au_fhsm_set, struct au_sbinfo *sbinfo, unsigned int sec) -+AuStubVoid(au_fhsm_show, struct seq_file *seq, struct au_sbinfo *sbinfo) -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct au_sbinfo *au_sbi(struct super_block *sb) -+{ -+ return sb->s_fs_info; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_EXPORT -+int au_test_nfsd(void); -+void au_export_init(struct super_block *sb); -+void au_xigen_inc(struct inode *inode); -+int au_xigen_new(struct inode *inode); -+int au_xigen_set(struct super_block *sb, struct file *base); -+void au_xigen_clr(struct super_block *sb); -+ -+static inline int au_busy_or_stale(void) -+{ -+ if (!au_test_nfsd()) -+ return -EBUSY; -+ return -ESTALE; -+} -+#else -+AuStubInt0(au_test_nfsd, void) -+AuStubVoid(au_export_init, struct super_block *sb) -+AuStubVoid(au_xigen_inc, struct inode *inode) -+AuStubInt0(au_xigen_new, struct inode *inode) -+AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base) -+AuStubVoid(au_xigen_clr, struct super_block *sb) -+AuStub(int, au_busy_or_stale, return -EBUSY, void) -+#endif /* CONFIG_AUFS_EXPORT */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_SBILIST -+/* module.c */ -+extern struct au_splhead au_sbilist; -+ -+static inline void au_sbilist_init(void) -+{ -+ au_spl_init(&au_sbilist); -+} -+ -+static inline void au_sbilist_add(struct super_block *sb) -+{ -+ au_spl_add(&au_sbi(sb)->si_list, &au_sbilist); -+} -+ -+static inline void au_sbilist_del(struct super_block *sb) -+{ -+ au_spl_del(&au_sbi(sb)->si_list, &au_sbilist); -+} -+ -+#ifdef CONFIG_AUFS_MAGIC_SYSRQ -+static inline void au_sbilist_lock(void) -+{ -+ spin_lock(&au_sbilist.spin); -+} -+ -+static inline void au_sbilist_unlock(void) -+{ -+ spin_unlock(&au_sbilist.spin); -+} -+#define AuGFP_SBILIST GFP_ATOMIC -+#else -+AuStubVoid(au_sbilist_lock, void) -+AuStubVoid(au_sbilist_unlock, void) -+#define AuGFP_SBILIST GFP_NOFS -+#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ -+#else -+AuStubVoid(au_sbilist_init, void) -+AuStubVoid(au_sbilist_add, struct super_block *sb) -+AuStubVoid(au_sbilist_del, struct super_block *sb) -+AuStubVoid(au_sbilist_lock, void) -+AuStubVoid(au_sbilist_unlock, void) -+#define AuGFP_SBILIST GFP_NOFS -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) -+{ -+ /* -+ * This function is a dynamic '__init' function actually, -+ * so the tiny check for si_rwsem is unnecessary. -+ */ -+ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ -+#ifdef CONFIG_DEBUG_FS -+ sbinfo->si_dbgaufs = NULL; -+ sbinfo->si_dbgaufs_plink = NULL; -+ sbinfo->si_dbgaufs_xib = NULL; -+#ifdef CONFIG_AUFS_EXPORT -+ sbinfo->si_dbgaufs_xigen = NULL; -+#endif -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline pid_t si_pid_bit(void) -+{ -+ /* the origin of pid is 1, but the bitmap's is 0 */ -+ return current->pid - 1; -+} -+ -+static inline int si_pid_test(struct super_block *sb) -+{ -+ pid_t bit; -+ -+ bit = si_pid_bit(); -+ if (bit < PID_MAX_DEFAULT) -+ return test_bit(bit, au_sbi(sb)->au_si_pid.bitmap); -+ return si_pid_test_slow(sb); -+} -+ -+static inline void si_pid_set(struct super_block *sb) -+{ -+ pid_t bit; -+ -+ bit = si_pid_bit(); -+ if (bit < PID_MAX_DEFAULT) { -+ AuDebugOn(test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); -+ set_bit(bit, au_sbi(sb)->au_si_pid.bitmap); -+ /* smp_mb(); */ -+ } else -+ si_pid_set_slow(sb); -+} -+ -+static inline void si_pid_clr(struct super_block *sb) -+{ -+ pid_t bit; -+ -+ bit = si_pid_bit(); -+ if (bit < PID_MAX_DEFAULT) { -+ AuDebugOn(!test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); -+ clear_bit(bit, au_sbi(sb)->au_si_pid.bitmap); -+ /* smp_mb(); */ -+ } else -+ si_pid_clr_slow(sb); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock superblock. mainly for entry point functions */ -+/* -+ * __si_read_lock, __si_write_lock, -+ * __si_read_unlock, __si_write_unlock, __si_downgrade_lock -+ */ -+AuSimpleRwsemFuncs(__si, struct super_block *sb, &au_sbi(sb)->si_rwsem); -+ -+#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem) -+#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) -+#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem) -+ -+static inline void si_noflush_read_lock(struct super_block *sb) -+{ -+ __si_read_lock(sb); -+ si_pid_set(sb); -+} -+ -+static inline int si_noflush_read_trylock(struct super_block *sb) -+{ -+ int locked; -+ -+ locked = __si_read_trylock(sb); -+ if (locked) -+ si_pid_set(sb); -+ return locked; -+} -+ -+static inline void si_noflush_write_lock(struct super_block *sb) -+{ -+ __si_write_lock(sb); -+ si_pid_set(sb); -+} -+ -+static inline int si_noflush_write_trylock(struct super_block *sb) -+{ -+ int locked; -+ -+ locked = __si_write_trylock(sb); -+ if (locked) -+ si_pid_set(sb); -+ return locked; -+} -+ -+#if 0 /* reserved */ -+static inline int si_read_trylock(struct super_block *sb, int flags) -+{ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ return si_noflush_read_trylock(sb); -+} -+#endif -+ -+static inline void si_read_unlock(struct super_block *sb) -+{ -+ si_pid_clr(sb); -+ __si_read_unlock(sb); -+} -+ -+#if 0 /* reserved */ -+static inline int si_write_trylock(struct super_block *sb, int flags) -+{ -+ if (au_ftest_lock(flags, FLUSH)) -+ au_nwt_flush(&au_sbi(sb)->si_nowait); -+ return si_noflush_write_trylock(sb); -+} -+#endif -+ -+static inline void si_write_unlock(struct super_block *sb) -+{ -+ si_pid_clr(sb); -+ __si_write_unlock(sb); -+} -+ -+#if 0 /* reserved */ -+static inline void si_downgrade_lock(struct super_block *sb) -+{ -+ __si_downgrade_lock(sb); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline aufs_bindex_t au_sbend(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_bend; -+} -+ -+static inline unsigned int au_mntflags(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_mntflags; -+} -+ -+static inline unsigned int au_sigen(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_generation; -+} -+ -+static inline void au_ninodes_inc(struct super_block *sb) -+{ -+ atomic_long_inc(&au_sbi(sb)->si_ninodes); -+} -+ -+static inline void au_ninodes_dec(struct super_block *sb) -+{ -+ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_ninodes)); -+ atomic_long_dec(&au_sbi(sb)->si_ninodes); -+} -+ -+static inline void au_nfiles_inc(struct super_block *sb) -+{ -+ atomic_long_inc(&au_sbi(sb)->si_nfiles); -+} -+ -+static inline void au_nfiles_dec(struct super_block *sb) -+{ -+ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_nfiles)); -+ atomic_long_dec(&au_sbi(sb)->si_nfiles); -+} -+ -+static inline struct au_branch *au_sbr(struct super_block *sb, -+ aufs_bindex_t bindex) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_branch[0 + bindex]; -+} -+ -+static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid) -+{ -+ SiMustWriteLock(sb); -+ au_sbi(sb)->si_xino_brid = brid; -+} -+ -+static inline aufs_bindex_t au_xino_brid(struct super_block *sb) -+{ -+ SiMustAnyLock(sb); -+ return au_sbi(sb)->si_xino_brid; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_SUPER_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/sysaufs.c linux-4.0.4/fs/aufs/sysaufs.c ---- linux-4.0.4.orig/fs/aufs/sysaufs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/sysaufs.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,91 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * sysfs interface and lifetime management -+ * they are necessary regardless sysfs is disabled. -+ */ -+ -+#include -+#include "aufs.h" -+ -+unsigned long sysaufs_si_mask; -+struct kset *sysaufs_kset; -+ -+#define AuSiAttr(_name) { \ -+ .attr = { .name = __stringify(_name), .mode = 0444 }, \ -+ .show = sysaufs_si_##_name, \ -+} -+ -+static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path); -+struct attribute *sysaufs_si_attrs[] = { -+ &sysaufs_si_attr_xi_path.attr, -+ NULL, -+}; -+ -+static const struct sysfs_ops au_sbi_ops = { -+ .show = sysaufs_si_show -+}; -+ -+static struct kobj_type au_sbi_ktype = { -+ .release = au_si_free, -+ .sysfs_ops = &au_sbi_ops, -+ .default_attrs = sysaufs_si_attrs -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+int sysaufs_si_init(struct au_sbinfo *sbinfo) -+{ -+ int err; -+ -+ sbinfo->si_kobj.kset = sysaufs_kset; -+ /* cf. sysaufs_name() */ -+ err = kobject_init_and_add -+ (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL, -+ SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); -+ -+ dbgaufs_si_null(sbinfo); -+ if (!err) { -+ err = dbgaufs_si_init(sbinfo); -+ if (unlikely(err)) -+ kobject_put(&sbinfo->si_kobj); -+ } -+ return err; -+} -+ -+void sysaufs_fin(void) -+{ -+ dbgaufs_fin(); -+ sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group); -+ kset_unregister(sysaufs_kset); -+} -+ -+int __init sysaufs_init(void) -+{ -+ int err; -+ -+ do { -+ get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask)); -+ } while (!sysaufs_si_mask); -+ -+ err = -EINVAL; -+ sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj); -+ if (unlikely(!sysaufs_kset)) -+ goto out; -+ err = PTR_ERR(sysaufs_kset); -+ if (IS_ERR(sysaufs_kset)) -+ goto out; -+ err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group); -+ if (unlikely(err)) { -+ kset_unregister(sysaufs_kset); -+ goto out; -+ } -+ -+ err = dbgaufs_init(); -+ if (unlikely(err)) -+ sysaufs_fin(); -+out: -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/sysaufs.h linux-4.0.4/fs/aufs/sysaufs.h ---- linux-4.0.4.orig/fs/aufs/sysaufs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/sysaufs.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,88 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * sysfs interface and mount lifetime management -+ */ -+ -+#ifndef __SYSAUFS_H__ -+#define __SYSAUFS_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include "module.h" -+ -+struct super_block; -+struct au_sbinfo; -+ -+struct sysaufs_si_attr { -+ struct attribute attr; -+ int (*show)(struct seq_file *seq, struct super_block *sb); -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* sysaufs.c */ -+extern unsigned long sysaufs_si_mask; -+extern struct kset *sysaufs_kset; -+extern struct attribute *sysaufs_si_attrs[]; -+int sysaufs_si_init(struct au_sbinfo *sbinfo); -+int __init sysaufs_init(void); -+void sysaufs_fin(void); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* some people doesn't like to show a pointer in kernel */ -+static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo) -+{ -+ return sysaufs_si_mask ^ (unsigned long)sbinfo; -+} -+ -+#define SysaufsSiNamePrefix "si_" -+#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16) -+static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name) -+{ -+ snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx", -+ sysaufs_si_id(sbinfo)); -+} -+ -+struct au_branch; -+#ifdef CONFIG_SYSFS -+/* sysfs.c */ -+extern struct attribute_group *sysaufs_attr_group; -+ -+int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb); -+ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, -+ char *buf); -+long au_brinfo_ioctl(struct file *file, unsigned long arg); -+#ifdef CONFIG_COMPAT -+long au_brinfo_compat_ioctl(struct file *file, unsigned long arg); -+#endif -+ -+void sysaufs_br_init(struct au_branch *br); -+void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); -+void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); -+ -+#define sysaufs_brs_init() do {} while (0) -+ -+#else -+#define sysaufs_attr_group NULL -+ -+AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb) -+AuStub(ssize_t, sysaufs_si_show, return 0, struct kobject *kobj, -+ struct attribute *attr, char *buf) -+AuStubVoid(sysaufs_br_init, struct au_branch *br) -+AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) -+AuStubVoid(sysaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) -+ -+static inline void sysaufs_brs_init(void) -+{ -+ sysaufs_brs = 0; -+} -+ -+#endif /* CONFIG_SYSFS */ -+ -+#endif /* __KERNEL__ */ -+#endif /* __SYSAUFS_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/sysfs.c linux-4.0.4/fs/aufs/sysfs.c ---- linux-4.0.4.orig/fs/aufs/sysfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/sysfs.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,336 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * sysfs interface -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+static struct attribute *au_attr[] = { -+ NULL, /* need to NULL terminate the list of attributes */ -+}; -+ -+static struct attribute_group sysaufs_attr_group_body = { -+ .attrs = au_attr -+}; -+ -+struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; -+ -+/* ---------------------------------------------------------------------- */ -+ -+int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) -+{ -+ int err; -+ -+ SiMustAnyLock(sb); -+ -+ err = 0; -+ if (au_opt_test(au_mntflags(sb), XINO)) { -+ err = au_xino_path(seq, au_sbi(sb)->si_xib); -+ seq_putc(seq, '\n'); -+ } -+ return err; -+} -+ -+/* -+ * the lifetime of branch is independent from the entry under sysfs. -+ * sysfs handles the lifetime of the entry, and never call ->show() after it is -+ * unlinked. -+ */ -+static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, -+ aufs_bindex_t bindex, int idx) -+{ -+ int err; -+ struct path path; -+ struct dentry *root; -+ struct au_branch *br; -+ au_br_perm_str_t perm; -+ -+ AuDbg("b%d\n", bindex); -+ -+ err = 0; -+ root = sb->s_root; -+ di_read_lock_parent(root, !AuLock_IR); -+ br = au_sbr(sb, bindex); -+ -+ switch (idx) { -+ case AuBrSysfs_BR: -+ path.mnt = au_br_mnt(br); -+ path.dentry = au_h_dptr(root, bindex); -+ au_seq_path(seq, &path); -+ au_optstr_br_perm(&perm, br->br_perm); -+ err = seq_printf(seq, "=%s\n", perm.a); -+ break; -+ case AuBrSysfs_BRID: -+ err = seq_printf(seq, "%d\n", br->br_id); -+ break; -+ } -+ di_read_unlock(root, !AuLock_IR); -+ if (err == -1) -+ err = -E2BIG; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static struct seq_file *au_seq(char *p, ssize_t len) -+{ -+ struct seq_file *seq; -+ -+ seq = kzalloc(sizeof(*seq), GFP_NOFS); -+ if (seq) { -+ /* mutex_init(&seq.lock); */ -+ seq->buf = p; -+ seq->size = len; -+ return seq; /* success */ -+ } -+ -+ seq = ERR_PTR(-ENOMEM); -+ return seq; -+} -+ -+#define SysaufsBr_PREFIX "br" -+#define SysaufsBrid_PREFIX "brid" -+ -+/* todo: file size may exceed PAGE_SIZE */ -+ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, -+ char *buf) -+{ -+ ssize_t err; -+ int idx; -+ long l; -+ aufs_bindex_t bend; -+ struct au_sbinfo *sbinfo; -+ struct super_block *sb; -+ struct seq_file *seq; -+ char *name; -+ struct attribute **cattr; -+ -+ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); -+ sb = sbinfo->si_sb; -+ -+ /* -+ * prevent a race condition between sysfs and aufs. -+ * for instance, sysfs_file_read() calls sysfs_get_active_two() which -+ * prohibits maintaining the sysfs entries. -+ * hew we acquire read lock after sysfs_get_active_two(). -+ * on the other hand, the remount process may maintain the sysfs/aufs -+ * entries after acquiring write lock. -+ * it can cause a deadlock. -+ * simply we gave up processing read here. -+ */ -+ err = -EBUSY; -+ if (unlikely(!si_noflush_read_trylock(sb))) -+ goto out; -+ -+ seq = au_seq(buf, PAGE_SIZE); -+ err = PTR_ERR(seq); -+ if (IS_ERR(seq)) -+ goto out_unlock; -+ -+ name = (void *)attr->name; -+ cattr = sysaufs_si_attrs; -+ while (*cattr) { -+ if (!strcmp(name, (*cattr)->name)) { -+ err = container_of(*cattr, struct sysaufs_si_attr, attr) -+ ->show(seq, sb); -+ goto out_seq; -+ } -+ cattr++; -+ } -+ -+ if (!strncmp(name, SysaufsBrid_PREFIX, -+ sizeof(SysaufsBrid_PREFIX) - 1)) { -+ idx = AuBrSysfs_BRID; -+ name += sizeof(SysaufsBrid_PREFIX) - 1; -+ } else if (!strncmp(name, SysaufsBr_PREFIX, -+ sizeof(SysaufsBr_PREFIX) - 1)) { -+ idx = AuBrSysfs_BR; -+ name += sizeof(SysaufsBr_PREFIX) - 1; -+ } else -+ BUG(); -+ -+ err = kstrtol(name, 10, &l); -+ if (!err) { -+ bend = au_sbend(sb); -+ if (l <= bend) -+ err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); -+ else -+ err = -ENOENT; -+ } -+ -+out_seq: -+ if (!err) { -+ err = seq->count; -+ /* sysfs limit */ -+ if (unlikely(err == PAGE_SIZE)) -+ err = -EFBIG; -+ } -+ kfree(seq); -+out_unlock: -+ si_read_unlock(sb); -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg) -+{ -+ int err; -+ int16_t brid; -+ aufs_bindex_t bindex, bend; -+ size_t sz; -+ char *buf; -+ struct seq_file *seq; -+ struct au_branch *br; -+ -+ si_read_lock(sb, AuLock_FLUSH); -+ bend = au_sbend(sb); -+ err = bend + 1; -+ if (!arg) -+ goto out; -+ -+ err = -ENOMEM; -+ buf = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!buf)) -+ goto out; -+ -+ seq = au_seq(buf, PAGE_SIZE); -+ err = PTR_ERR(seq); -+ if (IS_ERR(seq)) -+ goto out_buf; -+ -+ sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); -+ for (bindex = 0; bindex <= bend; bindex++, arg++) { -+ err = !access_ok(VERIFY_WRITE, arg, sizeof(*arg)); -+ if (unlikely(err)) -+ break; -+ -+ br = au_sbr(sb, bindex); -+ brid = br->br_id; -+ BUILD_BUG_ON(sizeof(brid) != sizeof(arg->id)); -+ err = __put_user(brid, &arg->id); -+ if (unlikely(err)) -+ break; -+ -+ BUILD_BUG_ON(sizeof(br->br_perm) != sizeof(arg->perm)); -+ err = __put_user(br->br_perm, &arg->perm); -+ if (unlikely(err)) -+ break; -+ -+ au_seq_path(seq, &br->br_path); -+ err = seq_putc(seq, '\0'); -+ if (!err && seq->count <= sz) { -+ err = copy_to_user(arg->path, seq->buf, seq->count); -+ seq->count = 0; -+ if (unlikely(err)) -+ break; -+ } else { -+ err = -E2BIG; -+ goto out_seq; -+ } -+ } -+ if (unlikely(err)) -+ err = -EFAULT; -+ -+out_seq: -+ kfree(seq); -+out_buf: -+ free_page((unsigned long)buf); -+out: -+ si_read_unlock(sb); -+ return err; -+} -+ -+long au_brinfo_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_brinfo(file->f_path.dentry->d_sb, (void __user *)arg); -+} -+ -+#ifdef CONFIG_COMPAT -+long au_brinfo_compat_ioctl(struct file *file, unsigned long arg) -+{ -+ return au_brinfo(file->f_path.dentry->d_sb, compat_ptr(arg)); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+void sysaufs_br_init(struct au_branch *br) -+{ -+ int i; -+ struct au_brsysfs *br_sysfs; -+ struct attribute *attr; -+ -+ br_sysfs = br->br_sysfs; -+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { -+ attr = &br_sysfs->attr; -+ sysfs_attr_init(attr); -+ attr->name = br_sysfs->name; -+ attr->mode = S_IRUGO; -+ br_sysfs++; -+ } -+} -+ -+void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ struct au_branch *br; -+ struct kobject *kobj; -+ struct au_brsysfs *br_sysfs; -+ int i; -+ aufs_bindex_t bend; -+ -+ dbgaufs_brs_del(sb, bindex); -+ -+ if (!sysaufs_brs) -+ return; -+ -+ kobj = &au_sbi(sb)->si_kobj; -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ br_sysfs = br->br_sysfs; -+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { -+ sysfs_remove_file(kobj, &br_sysfs->attr); -+ br_sysfs++; -+ } -+ } -+} -+ -+void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ int err, i; -+ aufs_bindex_t bend; -+ struct kobject *kobj; -+ struct au_branch *br; -+ struct au_brsysfs *br_sysfs; -+ -+ dbgaufs_brs_add(sb, bindex); -+ -+ if (!sysaufs_brs) -+ return; -+ -+ kobj = &au_sbi(sb)->si_kobj; -+ bend = au_sbend(sb); -+ for (; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ br_sysfs = br->br_sysfs; -+ snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), -+ SysaufsBr_PREFIX "%d", bindex); -+ snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name), -+ SysaufsBrid_PREFIX "%d", bindex); -+ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { -+ err = sysfs_create_file(kobj, &br_sysfs->attr); -+ if (unlikely(err)) -+ pr_warn("failed %s under sysfs(%d)\n", -+ br_sysfs->name, err); -+ br_sysfs++; -+ } -+ } -+} -diff -Nur linux-4.0.4.orig/fs/aufs/sysrq.c linux-4.0.4/fs/aufs/sysrq.c ---- linux-4.0.4.orig/fs/aufs/sysrq.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/sysrq.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,144 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * magic sysrq hanlder -+ */ -+ -+/* #include */ -+#include -+#include "aufs.h" -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void sysrq_sb(struct super_block *sb) -+{ -+ char *plevel; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ struct au_sphlhead *files; -+ struct au_finfo *finfo; -+ -+ plevel = au_plevel; -+ au_plevel = KERN_WARNING; -+ -+ /* since we define pr_fmt, call printk directly */ -+#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str) -+ -+ sbinfo = au_sbi(sb); -+ printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); -+ pr("superblock\n"); -+ au_dpri_sb(sb); -+ -+#if 0 -+ pr("root dentry\n"); -+ au_dpri_dentry(sb->s_root); -+ pr("root inode\n"); -+ au_dpri_inode(sb->s_root->d_inode); -+#endif -+ -+#if 0 -+ do { -+ int err, i, j, ndentry; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ -+ err = au_dpages_init(&dpages, GFP_ATOMIC); -+ if (unlikely(err)) -+ break; -+ err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL); -+ if (!err) -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) -+ au_dpri_dentry(dpage->dentries[j]); -+ } -+ au_dpages_free(&dpages); -+ } while (0); -+#endif -+ -+#if 1 -+ { -+ struct inode *i; -+ -+ pr("isolated inode\n"); -+ spin_lock(&inode_sb_list_lock); -+ list_for_each_entry(i, &sb->s_inodes, i_sb_list) { -+ spin_lock(&i->i_lock); -+ if (1 || hlist_empty(&i->i_dentry)) -+ au_dpri_inode(i); -+ spin_unlock(&i->i_lock); -+ } -+ spin_unlock(&inode_sb_list_lock); -+ } -+#endif -+ pr("files\n"); -+ files = &au_sbi(sb)->si_files; -+ spin_lock(&files->spin); -+ hlist_for_each_entry(finfo, &files->head, fi_hlist) { -+ umode_t mode; -+ -+ file = finfo->fi_file; -+ mode = file_inode(file)->i_mode; -+ if (!special_file(mode)) -+ au_dpri_file(file); -+ } -+ spin_unlock(&files->spin); -+ pr("done\n"); -+ -+#undef pr -+ au_plevel = plevel; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* module parameter */ -+static char *aufs_sysrq_key = "a"; -+module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO); -+MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); -+ -+static void au_sysrq(int key __maybe_unused) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ lockdep_off(); -+ au_sbilist_lock(); -+ list_for_each_entry(sbinfo, &au_sbilist.head, si_list) -+ sysrq_sb(sbinfo->si_sb); -+ au_sbilist_unlock(); -+ lockdep_on(); -+} -+ -+static struct sysrq_key_op au_sysrq_op = { -+ .handler = au_sysrq, -+ .help_msg = "Aufs", -+ .action_msg = "Aufs", -+ .enable_mask = SYSRQ_ENABLE_DUMP -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+int __init au_sysrq_init(void) -+{ -+ int err; -+ char key; -+ -+ err = -1; -+ key = *aufs_sysrq_key; -+ if ('a' <= key && key <= 'z') -+ err = register_sysrq_key(key, &au_sysrq_op); -+ if (unlikely(err)) -+ pr_err("err %d, sysrq=%c\n", err, key); -+ return err; -+} -+ -+void au_sysrq_fin(void) -+{ -+ int err; -+ -+ err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op); -+ if (unlikely(err)) -+ pr_err("err %d (ignored)\n", err); -+} -diff -Nur linux-4.0.4.orig/fs/aufs/vdir.c linux-4.0.4/fs/aufs/vdir.c ---- linux-4.0.4.orig/fs/aufs/vdir.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/vdir.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,876 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * virtual or vertical directory -+ */ -+ -+#include "aufs.h" -+ -+static unsigned int calc_size(int nlen) -+{ -+ return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); -+} -+ -+static int set_deblk_end(union au_vdir_deblk_p *p, -+ union au_vdir_deblk_p *deblk_end) -+{ -+ if (calc_size(0) <= deblk_end->deblk - p->deblk) { -+ p->de->de_str.len = 0; -+ /* smp_mb(); */ -+ return 0; -+ } -+ return -1; /* error */ -+} -+ -+/* returns true or false */ -+static int is_deblk_end(union au_vdir_deblk_p *p, -+ union au_vdir_deblk_p *deblk_end) -+{ -+ if (calc_size(0) <= deblk_end->deblk - p->deblk) -+ return !p->de->de_str.len; -+ return 1; -+} -+ -+static unsigned char *last_deblk(struct au_vdir *vdir) -+{ -+ return vdir->vd_deblk[vdir->vd_nblk - 1]; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* estimate the apropriate size for name hash table */ -+unsigned int au_rdhash_est(loff_t sz) -+{ -+ unsigned int n; -+ -+ n = UINT_MAX; -+ sz >>= 10; -+ if (sz < n) -+ n = sz; -+ if (sz < AUFS_RDHASH_DEF) -+ n = AUFS_RDHASH_DEF; -+ /* pr_info("n %u\n", n); */ -+ return n; -+} -+ -+/* -+ * the allocated memory has to be freed by -+ * au_nhash_wh_free() or au_nhash_de_free(). -+ */ -+int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) -+{ -+ struct hlist_head *head; -+ unsigned int u; -+ size_t sz; -+ -+ sz = sizeof(*nhash->nh_head) * num_hash; -+ head = kmalloc(sz, gfp); -+ if (head) { -+ nhash->nh_num = num_hash; -+ nhash->nh_head = head; -+ for (u = 0; u < num_hash; u++) -+ INIT_HLIST_HEAD(head++); -+ return 0; /* success */ -+ } -+ -+ return -ENOMEM; -+} -+ -+static void nhash_count(struct hlist_head *head) -+{ -+#if 0 -+ unsigned long n; -+ struct hlist_node *pos; -+ -+ n = 0; -+ hlist_for_each(pos, head) -+ n++; -+ pr_info("%lu\n", n); -+#endif -+} -+ -+static void au_nhash_wh_do_free(struct hlist_head *head) -+{ -+ struct au_vdir_wh *pos; -+ struct hlist_node *node; -+ -+ hlist_for_each_entry_safe(pos, node, head, wh_hash) -+ kfree(pos); -+} -+ -+static void au_nhash_de_do_free(struct hlist_head *head) -+{ -+ struct au_vdir_dehstr *pos; -+ struct hlist_node *node; -+ -+ hlist_for_each_entry_safe(pos, node, head, hash) -+ au_cache_free_vdir_dehstr(pos); -+} -+ -+static void au_nhash_do_free(struct au_nhash *nhash, -+ void (*free)(struct hlist_head *head)) -+{ -+ unsigned int n; -+ struct hlist_head *head; -+ -+ n = nhash->nh_num; -+ if (!n) -+ return; -+ -+ head = nhash->nh_head; -+ while (n-- > 0) { -+ nhash_count(head); -+ free(head++); -+ } -+ kfree(nhash->nh_head); -+} -+ -+void au_nhash_wh_free(struct au_nhash *whlist) -+{ -+ au_nhash_do_free(whlist, au_nhash_wh_do_free); -+} -+ -+static void au_nhash_de_free(struct au_nhash *delist) -+{ -+ au_nhash_do_free(delist, au_nhash_de_do_free); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, -+ int limit) -+{ -+ int num; -+ unsigned int u, n; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ -+ num = 0; -+ n = whlist->nh_num; -+ head = whlist->nh_head; -+ for (u = 0; u < n; u++, head++) -+ hlist_for_each_entry(pos, head, wh_hash) -+ if (pos->wh_bindex == btgt && ++num > limit) -+ return 1; -+ return 0; -+} -+ -+static struct hlist_head *au_name_hash(struct au_nhash *nhash, -+ unsigned char *name, -+ unsigned int len) -+{ -+ unsigned int v; -+ /* const unsigned int magic_bit = 12; */ -+ -+ AuDebugOn(!nhash->nh_num || !nhash->nh_head); -+ -+ v = 0; -+ while (len--) -+ v += *name++; -+ /* v = hash_long(v, magic_bit); */ -+ v %= nhash->nh_num; -+ return nhash->nh_head + v; -+} -+ -+static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, -+ int nlen) -+{ -+ return str->len == nlen && !memcmp(str->name, name, nlen); -+} -+ -+/* returns found or not */ -+int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) -+{ -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ struct au_vdir_destr *str; -+ -+ head = au_name_hash(whlist, name, nlen); -+ hlist_for_each_entry(pos, head, wh_hash) { -+ str = &pos->wh_str; -+ AuDbg("%.*s\n", str->len, str->name); -+ if (au_nhash_test_name(str, name, nlen)) -+ return 1; -+ } -+ return 0; -+} -+ -+/* returns found(true) or not */ -+static int test_known(struct au_nhash *delist, char *name, int nlen) -+{ -+ struct hlist_head *head; -+ struct au_vdir_dehstr *pos; -+ struct au_vdir_destr *str; -+ -+ head = au_name_hash(delist, name, nlen); -+ hlist_for_each_entry(pos, head, hash) { -+ str = pos->str; -+ AuDbg("%.*s\n", str->len, str->name); -+ if (au_nhash_test_name(str, name, nlen)) -+ return 1; -+ } -+ return 0; -+} -+ -+static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, -+ unsigned char d_type) -+{ -+#ifdef CONFIG_AUFS_SHWH -+ wh->wh_ino = ino; -+ wh->wh_type = d_type; -+#endif -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, -+ unsigned int d_type, aufs_bindex_t bindex, -+ unsigned char shwh) -+{ -+ int err; -+ struct au_vdir_destr *str; -+ struct au_vdir_wh *wh; -+ -+ AuDbg("%.*s\n", nlen, name); -+ AuDebugOn(!whlist->nh_num || !whlist->nh_head); -+ -+ err = -ENOMEM; -+ wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); -+ if (unlikely(!wh)) -+ goto out; -+ -+ err = 0; -+ wh->wh_bindex = bindex; -+ if (shwh) -+ au_shwh_init_wh(wh, ino, d_type); -+ str = &wh->wh_str; -+ str->len = nlen; -+ memcpy(str->name, name, nlen); -+ hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); -+ /* smp_mb(); */ -+ -+out: -+ return err; -+} -+ -+static int append_deblk(struct au_vdir *vdir) -+{ -+ int err; -+ unsigned long ul; -+ const unsigned int deblk_sz = vdir->vd_deblk_sz; -+ union au_vdir_deblk_p p, deblk_end; -+ unsigned char **o; -+ -+ err = -ENOMEM; -+ o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), -+ GFP_NOFS); -+ if (unlikely(!o)) -+ goto out; -+ -+ vdir->vd_deblk = o; -+ p.deblk = kmalloc(deblk_sz, GFP_NOFS); -+ if (p.deblk) { -+ ul = vdir->vd_nblk++; -+ vdir->vd_deblk[ul] = p.deblk; -+ vdir->vd_last.ul = ul; -+ vdir->vd_last.p.deblk = p.deblk; -+ deblk_end.deblk = p.deblk + deblk_sz; -+ err = set_deblk_end(&p, &deblk_end); -+ } -+ -+out: -+ return err; -+} -+ -+static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, -+ unsigned int d_type, struct au_nhash *delist) -+{ -+ int err; -+ unsigned int sz; -+ const unsigned int deblk_sz = vdir->vd_deblk_sz; -+ union au_vdir_deblk_p p, *room, deblk_end; -+ struct au_vdir_dehstr *dehstr; -+ -+ p.deblk = last_deblk(vdir); -+ deblk_end.deblk = p.deblk + deblk_sz; -+ room = &vdir->vd_last.p; -+ AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk -+ || !is_deblk_end(room, &deblk_end)); -+ -+ sz = calc_size(nlen); -+ if (unlikely(sz > deblk_end.deblk - room->deblk)) { -+ err = append_deblk(vdir); -+ if (unlikely(err)) -+ goto out; -+ -+ p.deblk = last_deblk(vdir); -+ deblk_end.deblk = p.deblk + deblk_sz; -+ /* smp_mb(); */ -+ AuDebugOn(room->deblk != p.deblk); -+ } -+ -+ err = -ENOMEM; -+ dehstr = au_cache_alloc_vdir_dehstr(); -+ if (unlikely(!dehstr)) -+ goto out; -+ -+ dehstr->str = &room->de->de_str; -+ hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); -+ room->de->de_ino = ino; -+ room->de->de_type = d_type; -+ room->de->de_str.len = nlen; -+ memcpy(room->de->de_str.name, name, nlen); -+ -+ err = 0; -+ room->deblk += sz; -+ if (unlikely(set_deblk_end(room, &deblk_end))) -+ err = append_deblk(vdir); -+ /* smp_mb(); */ -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_vdir_free(struct au_vdir *vdir) -+{ -+ unsigned char **deblk; -+ -+ deblk = vdir->vd_deblk; -+ while (vdir->vd_nblk--) -+ kfree(*deblk++); -+ kfree(vdir->vd_deblk); -+ au_cache_free_vdir(vdir); -+} -+ -+static struct au_vdir *alloc_vdir(struct file *file) -+{ -+ struct au_vdir *vdir; -+ struct super_block *sb; -+ int err; -+ -+ sb = file->f_path.dentry->d_sb; -+ SiMustAnyLock(sb); -+ -+ err = -ENOMEM; -+ vdir = au_cache_alloc_vdir(); -+ if (unlikely(!vdir)) -+ goto out; -+ -+ vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); -+ if (unlikely(!vdir->vd_deblk)) -+ goto out_free; -+ -+ vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; -+ if (!vdir->vd_deblk_sz) { -+ /* estimate the apropriate size for deblk */ -+ vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); -+ /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ -+ } -+ vdir->vd_nblk = 0; -+ vdir->vd_version = 0; -+ vdir->vd_jiffy = 0; -+ err = append_deblk(vdir); -+ if (!err) -+ return vdir; /* success */ -+ -+ kfree(vdir->vd_deblk); -+ -+out_free: -+ au_cache_free_vdir(vdir); -+out: -+ vdir = ERR_PTR(err); -+ return vdir; -+} -+ -+static int reinit_vdir(struct au_vdir *vdir) -+{ -+ int err; -+ union au_vdir_deblk_p p, deblk_end; -+ -+ while (vdir->vd_nblk > 1) { -+ kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); -+ /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ -+ vdir->vd_nblk--; -+ } -+ p.deblk = vdir->vd_deblk[0]; -+ deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; -+ err = set_deblk_end(&p, &deblk_end); -+ /* keep vd_dblk_sz */ -+ vdir->vd_last.ul = 0; -+ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; -+ vdir->vd_version = 0; -+ vdir->vd_jiffy = 0; -+ /* smp_mb(); */ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AuFillVdir_CALLED 1 -+#define AuFillVdir_WHABLE (1 << 1) -+#define AuFillVdir_SHWH (1 << 2) -+#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) -+#define au_fset_fillvdir(flags, name) \ -+ do { (flags) |= AuFillVdir_##name; } while (0) -+#define au_fclr_fillvdir(flags, name) \ -+ do { (flags) &= ~AuFillVdir_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_SHWH -+#undef AuFillVdir_SHWH -+#define AuFillVdir_SHWH 0 -+#endif -+ -+struct fillvdir_arg { -+ struct dir_context ctx; -+ struct file *file; -+ struct au_vdir *vdir; -+ struct au_nhash delist; -+ struct au_nhash whlist; -+ aufs_bindex_t bindex; -+ unsigned int flags; -+ int err; -+}; -+ -+static int fillvdir(struct dir_context *ctx, const char *__name, int nlen, -+ loff_t offset __maybe_unused, u64 h_ino, -+ unsigned int d_type) -+{ -+ struct fillvdir_arg *arg = container_of(ctx, struct fillvdir_arg, ctx); -+ char *name = (void *)__name; -+ struct super_block *sb; -+ ino_t ino; -+ const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); -+ -+ arg->err = 0; -+ sb = arg->file->f_path.dentry->d_sb; -+ au_fset_fillvdir(arg->flags, CALLED); -+ /* smp_mb(); */ -+ if (nlen <= AUFS_WH_PFX_LEN -+ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { -+ if (test_known(&arg->delist, name, nlen) -+ || au_nhash_test_known_wh(&arg->whlist, name, nlen)) -+ goto out; /* already exists or whiteouted */ -+ -+ sb = arg->file->f_path.dentry->d_sb; -+ arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); -+ if (!arg->err) { -+ if (unlikely(nlen > AUFS_MAX_NAMELEN)) -+ d_type = DT_UNKNOWN; -+ arg->err = append_de(arg->vdir, name, nlen, ino, -+ d_type, &arg->delist); -+ } -+ } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { -+ name += AUFS_WH_PFX_LEN; -+ nlen -= AUFS_WH_PFX_LEN; -+ if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) -+ goto out; /* already whiteouted */ -+ -+ if (shwh) -+ arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, -+ &ino); -+ if (!arg->err) { -+ if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) -+ d_type = DT_UNKNOWN; -+ arg->err = au_nhash_append_wh -+ (&arg->whlist, name, nlen, ino, d_type, -+ arg->bindex, shwh); -+ } -+ } -+ -+out: -+ if (!arg->err) -+ arg->vdir->vd_jiffy = jiffies; -+ /* smp_mb(); */ -+ AuTraceErr(arg->err); -+ return arg->err; -+} -+ -+static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, -+ struct au_nhash *whlist, struct au_nhash *delist) -+{ -+#ifdef CONFIG_AUFS_SHWH -+ int err; -+ unsigned int nh, u; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ struct hlist_node *n; -+ char *p, *o; -+ struct au_vdir_destr *destr; -+ -+ AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); -+ -+ err = -ENOMEM; -+ o = p = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ -+ err = 0; -+ nh = whlist->nh_num; -+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); -+ p += AUFS_WH_PFX_LEN; -+ for (u = 0; u < nh; u++) { -+ head = whlist->nh_head + u; -+ hlist_for_each_entry_safe(pos, n, head, wh_hash) { -+ destr = &pos->wh_str; -+ memcpy(p, destr->name, destr->len); -+ err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, -+ pos->wh_ino, pos->wh_type, delist); -+ if (unlikely(err)) -+ break; -+ } -+ } -+ -+ free_page((unsigned long)o); -+ -+out: -+ AuTraceErr(err); -+ return err; -+#else -+ return 0; -+#endif -+} -+ -+static int au_do_read_vdir(struct fillvdir_arg *arg) -+{ -+ int err; -+ unsigned int rdhash; -+ loff_t offset; -+ aufs_bindex_t bend, bindex, bstart; -+ unsigned char shwh; -+ struct file *hf, *file; -+ struct super_block *sb; -+ -+ file = arg->file; -+ sb = file->f_path.dentry->d_sb; -+ SiMustAnyLock(sb); -+ -+ rdhash = au_sbi(sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); -+ err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); -+ if (unlikely(err)) -+ goto out_delist; -+ -+ err = 0; -+ arg->flags = 0; -+ shwh = 0; -+ if (au_opt_test(au_mntflags(sb), SHWH)) { -+ shwh = 1; -+ au_fset_fillvdir(arg->flags, SHWH); -+ } -+ bstart = au_fbstart(file); -+ bend = au_fbend_dir(file); -+ for (bindex = bstart; !err && bindex <= bend; bindex++) { -+ hf = au_hf_dir(file, bindex); -+ if (!hf) -+ continue; -+ -+ offset = vfsub_llseek(hf, 0, SEEK_SET); -+ err = offset; -+ if (unlikely(offset)) -+ break; -+ -+ arg->bindex = bindex; -+ au_fclr_fillvdir(arg->flags, WHABLE); -+ if (shwh -+ || (bindex != bend -+ && au_br_whable(au_sbr_perm(sb, bindex)))) -+ au_fset_fillvdir(arg->flags, WHABLE); -+ do { -+ arg->err = 0; -+ au_fclr_fillvdir(arg->flags, CALLED); -+ /* smp_mb(); */ -+ err = vfsub_iterate_dir(hf, &arg->ctx); -+ if (err >= 0) -+ err = arg->err; -+ } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); -+ -+ /* -+ * dir_relax() may be good for concurrency, but aufs should not -+ * use it since it will cause a lockdep problem. -+ */ -+ } -+ -+ if (!err && shwh) -+ err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); -+ -+ au_nhash_wh_free(&arg->whlist); -+ -+out_delist: -+ au_nhash_de_free(&arg->delist); -+out: -+ return err; -+} -+ -+static int read_vdir(struct file *file, int may_read) -+{ -+ int err; -+ unsigned long expire; -+ unsigned char do_read; -+ struct fillvdir_arg arg = { -+ .ctx = { -+ .actor = fillvdir -+ } -+ }; -+ struct inode *inode; -+ struct au_vdir *vdir, *allocated; -+ -+ err = 0; -+ inode = file_inode(file); -+ IMustLock(inode); -+ SiMustAnyLock(inode->i_sb); -+ -+ allocated = NULL; -+ do_read = 0; -+ expire = au_sbi(inode->i_sb)->si_rdcache; -+ vdir = au_ivdir(inode); -+ if (!vdir) { -+ do_read = 1; -+ vdir = alloc_vdir(file); -+ err = PTR_ERR(vdir); -+ if (IS_ERR(vdir)) -+ goto out; -+ err = 0; -+ allocated = vdir; -+ } else if (may_read -+ && (inode->i_version != vdir->vd_version -+ || time_after(jiffies, vdir->vd_jiffy + expire))) { -+ do_read = 1; -+ err = reinit_vdir(vdir); -+ if (unlikely(err)) -+ goto out; -+ } -+ -+ if (!do_read) -+ return 0; /* success */ -+ -+ arg.file = file; -+ arg.vdir = vdir; -+ err = au_do_read_vdir(&arg); -+ if (!err) { -+ /* file->f_pos = 0; */ /* todo: ctx->pos? */ -+ vdir->vd_version = inode->i_version; -+ vdir->vd_last.ul = 0; -+ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; -+ if (allocated) -+ au_set_ivdir(inode, allocated); -+ } else if (allocated) -+ au_vdir_free(allocated); -+ -+out: -+ return err; -+} -+ -+static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) -+{ -+ int err, rerr; -+ unsigned long ul, n; -+ const unsigned int deblk_sz = src->vd_deblk_sz; -+ -+ AuDebugOn(tgt->vd_nblk != 1); -+ -+ err = -ENOMEM; -+ if (tgt->vd_nblk < src->vd_nblk) { -+ unsigned char **p; -+ -+ p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, -+ GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ tgt->vd_deblk = p; -+ } -+ -+ if (tgt->vd_deblk_sz != deblk_sz) { -+ unsigned char *p; -+ -+ tgt->vd_deblk_sz = deblk_sz; -+ p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS); -+ if (unlikely(!p)) -+ goto out; -+ tgt->vd_deblk[0] = p; -+ } -+ memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); -+ tgt->vd_version = src->vd_version; -+ tgt->vd_jiffy = src->vd_jiffy; -+ -+ n = src->vd_nblk; -+ for (ul = 1; ul < n; ul++) { -+ tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, -+ GFP_NOFS); -+ if (unlikely(!tgt->vd_deblk[ul])) -+ goto out; -+ tgt->vd_nblk++; -+ } -+ tgt->vd_nblk = n; -+ tgt->vd_last.ul = tgt->vd_last.ul; -+ tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; -+ tgt->vd_last.p.deblk += src->vd_last.p.deblk -+ - src->vd_deblk[src->vd_last.ul]; -+ /* smp_mb(); */ -+ return 0; /* success */ -+ -+out: -+ rerr = reinit_vdir(tgt); -+ BUG_ON(rerr); -+ return err; -+} -+ -+int au_vdir_init(struct file *file) -+{ -+ int err; -+ struct inode *inode; -+ struct au_vdir *vdir_cache, *allocated; -+ -+ /* test file->f_pos here instead of ctx->pos */ -+ err = read_vdir(file, !file->f_pos); -+ if (unlikely(err)) -+ goto out; -+ -+ allocated = NULL; -+ vdir_cache = au_fvdir_cache(file); -+ if (!vdir_cache) { -+ vdir_cache = alloc_vdir(file); -+ err = PTR_ERR(vdir_cache); -+ if (IS_ERR(vdir_cache)) -+ goto out; -+ allocated = vdir_cache; -+ } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { -+ /* test file->f_pos here instead of ctx->pos */ -+ err = reinit_vdir(vdir_cache); -+ if (unlikely(err)) -+ goto out; -+ } else -+ return 0; /* success */ -+ -+ inode = file_inode(file); -+ err = copy_vdir(vdir_cache, au_ivdir(inode)); -+ if (!err) { -+ file->f_version = inode->i_version; -+ if (allocated) -+ au_set_fvdir_cache(file, allocated); -+ } else if (allocated) -+ au_vdir_free(allocated); -+ -+out: -+ return err; -+} -+ -+static loff_t calc_offset(struct au_vdir *vdir) -+{ -+ loff_t offset; -+ union au_vdir_deblk_p p; -+ -+ p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; -+ offset = vdir->vd_last.p.deblk - p.deblk; -+ offset += vdir->vd_deblk_sz * vdir->vd_last.ul; -+ return offset; -+} -+ -+/* returns true or false */ -+static int seek_vdir(struct file *file, struct dir_context *ctx) -+{ -+ int valid; -+ unsigned int deblk_sz; -+ unsigned long ul, n; -+ loff_t offset; -+ union au_vdir_deblk_p p, deblk_end; -+ struct au_vdir *vdir_cache; -+ -+ valid = 1; -+ vdir_cache = au_fvdir_cache(file); -+ offset = calc_offset(vdir_cache); -+ AuDbg("offset %lld\n", offset); -+ if (ctx->pos == offset) -+ goto out; -+ -+ vdir_cache->vd_last.ul = 0; -+ vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; -+ if (!ctx->pos) -+ goto out; -+ -+ valid = 0; -+ deblk_sz = vdir_cache->vd_deblk_sz; -+ ul = div64_u64(ctx->pos, deblk_sz); -+ AuDbg("ul %lu\n", ul); -+ if (ul >= vdir_cache->vd_nblk) -+ goto out; -+ -+ n = vdir_cache->vd_nblk; -+ for (; ul < n; ul++) { -+ p.deblk = vdir_cache->vd_deblk[ul]; -+ deblk_end.deblk = p.deblk + deblk_sz; -+ offset = ul; -+ offset *= deblk_sz; -+ while (!is_deblk_end(&p, &deblk_end) && offset < ctx->pos) { -+ unsigned int l; -+ -+ l = calc_size(p.de->de_str.len); -+ offset += l; -+ p.deblk += l; -+ } -+ if (!is_deblk_end(&p, &deblk_end)) { -+ valid = 1; -+ vdir_cache->vd_last.ul = ul; -+ vdir_cache->vd_last.p = p; -+ break; -+ } -+ } -+ -+out: -+ /* smp_mb(); */ -+ AuTraceErr(!valid); -+ return valid; -+} -+ -+int au_vdir_fill_de(struct file *file, struct dir_context *ctx) -+{ -+ unsigned int l, deblk_sz; -+ union au_vdir_deblk_p deblk_end; -+ struct au_vdir *vdir_cache; -+ struct au_vdir_de *de; -+ -+ vdir_cache = au_fvdir_cache(file); -+ if (!seek_vdir(file, ctx)) -+ return 0; -+ -+ deblk_sz = vdir_cache->vd_deblk_sz; -+ while (1) { -+ deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; -+ deblk_end.deblk += deblk_sz; -+ while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { -+ de = vdir_cache->vd_last.p.de; -+ AuDbg("%.*s, off%lld, i%lu, dt%d\n", -+ de->de_str.len, de->de_str.name, ctx->pos, -+ (unsigned long)de->de_ino, de->de_type); -+ if (unlikely(!dir_emit(ctx, de->de_str.name, -+ de->de_str.len, de->de_ino, -+ de->de_type))) { -+ /* todo: ignore the error caused by udba? */ -+ /* return err; */ -+ return 0; -+ } -+ -+ l = calc_size(de->de_str.len); -+ vdir_cache->vd_last.p.deblk += l; -+ ctx->pos += l; -+ } -+ if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { -+ vdir_cache->vd_last.ul++; -+ vdir_cache->vd_last.p.deblk -+ = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; -+ ctx->pos = deblk_sz * vdir_cache->vd_last.ul; -+ continue; -+ } -+ break; -+ } -+ -+ /* smp_mb(); */ -+ return 0; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/vfsub.c linux-4.0.4/fs/aufs/vfsub.c ---- linux-4.0.4.orig/fs/aufs/vfsub.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/vfsub.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,833 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * sub-routines for VFS -+ */ -+ -+#include -+#include -+#include -+#include "aufs.h" -+ -+int vfsub_update_h_iattr(struct path *h_path, int *did) -+{ -+ int err; -+ struct kstat st; -+ struct super_block *h_sb; -+ -+ /* for remote fs, leave work for its getattr or d_revalidate */ -+ /* for bad i_attr fs, handle them in aufs_getattr() */ -+ /* still some fs may acquire i_mutex. we need to skip them */ -+ err = 0; -+ if (!did) -+ did = &err; -+ h_sb = h_path->dentry->d_sb; -+ *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); -+ if (*did) -+ err = vfs_getattr(h_path, &st); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct file *vfsub_dentry_open(struct path *path, int flags) -+{ -+ struct file *file; -+ -+ file = dentry_open(path, flags /* | __FMODE_NONOTIFY */, -+ current_cred()); -+ if (!IS_ERR_OR_NULL(file) -+ && (file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) -+ i_readcount_inc(path->dentry->d_inode); -+ -+ return file; -+} -+ -+struct file *vfsub_filp_open(const char *path, int oflags, int mode) -+{ -+ struct file *file; -+ -+ lockdep_off(); -+ file = filp_open(path, -+ oflags /* | __FMODE_NONOTIFY */, -+ mode); -+ lockdep_on(); -+ if (IS_ERR(file)) -+ goto out; -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ -+out: -+ return file; -+} -+ -+/* -+ * Ideally this function should call VFS:do_last() in order to keep all its -+ * checkings. But it is very hard for aufs to regenerate several VFS internal -+ * structure such as nameidata. This is a second (or third) best approach. -+ * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). -+ */ -+int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args, struct au_branch *br) -+{ -+ int err; -+ struct file *file = args->file; -+ /* copied from linux/fs/namei.c:atomic_open() */ -+ struct dentry *const DENTRY_NOT_SET = (void *)-1UL; -+ -+ IMustLock(dir); -+ AuDebugOn(!dir->i_op->atomic_open); -+ -+ err = au_br_test_oflag(args->open_flag, br); -+ if (unlikely(err)) -+ goto out; -+ -+ args->file->f_path.dentry = DENTRY_NOT_SET; -+ args->file->f_path.mnt = au_br_mnt(br); -+ err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, -+ args->create_mode, args->opened); -+ if (err >= 0) { -+ /* some filesystems don't set FILE_CREATED while succeeded? */ -+ if (*args->opened & FILE_CREATED) -+ fsnotify_create(dir, dentry); -+ } else -+ goto out; -+ -+ -+ if (!err) { -+ /* todo: call VFS:may_open() here */ -+ err = open_check_o_direct(file); -+ /* todo: ima_file_check() too? */ -+ if (!err && (args->open_flag & __FMODE_EXEC)) -+ err = deny_write_access(file); -+ if (unlikely(err)) -+ /* note that the file is created and still opened */ -+ goto out; -+ } -+ -+ atomic_inc(&br->br_count); -+ fsnotify_open(file); -+ -+out: -+ return err; -+} -+ -+int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) -+{ -+ int err; -+ -+ err = kern_path(name, flags, path); -+ if (!err && path->dentry->d_inode) -+ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, -+ int len) -+{ -+ struct path path = { -+ .mnt = NULL -+ }; -+ -+ /* VFS checks it too, but by WARN_ON_ONCE() */ -+ IMustLock(parent->d_inode); -+ -+ path.dentry = lookup_one_len(name, parent, len); -+ if (IS_ERR(path.dentry)) -+ goto out; -+ if (path.dentry->d_inode) -+ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ -+ -+out: -+ AuTraceErrPtr(path.dentry); -+ return path.dentry; -+} -+ -+void vfsub_call_lkup_one(void *args) -+{ -+ struct vfsub_lkup_one_args *a = args; -+ *a->errp = vfsub_lkup_one(a->name, a->parent); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2) -+{ -+ struct dentry *d; -+ -+ lockdep_off(); -+ d = lock_rename(d1, d2); -+ lockdep_on(); -+ au_hn_suspend(hdir1); -+ if (hdir1 != hdir2) -+ au_hn_suspend(hdir2); -+ -+ return d; -+} -+ -+void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2) -+{ -+ au_hn_resume(hdir1); -+ if (hdir1 != hdir2) -+ au_hn_resume(hdir2); -+ lockdep_off(); -+ unlock_rename(d1, d2); -+ lockdep_on(); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_mknod(path, d, mode, 0); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_create(dir, path->dentry, mode, want_excl); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_symlink(struct inode *dir, struct path *path, const char *symname) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_symlink(path, d, symname); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_symlink(dir, path->dentry, symname); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_mknod(path, d, mode, new_encode_dev(dev)); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_mknod(dir, path->dentry, mode, dev); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+static int au_test_nlink(struct inode *inode) -+{ -+ const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ -+ -+ if (!au_test_fs_no_limit_nlink(inode->i_sb) -+ || inode->i_nlink < link_max) -+ return 0; -+ return -EMLINK; -+} -+ -+int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path, -+ struct inode **delegated_inode) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ err = au_test_nlink(src_dentry->d_inode); -+ if (unlikely(err)) -+ return err; -+ -+ /* we don't call may_linkat() */ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_link(src_dentry, path, d); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_link(src_dentry, dir, path->dentry, delegated_inode); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ /* fuse has different memory inode for the same inumber */ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ tmp.dentry = src_dentry; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, -+ struct inode *dir, struct path *path, -+ struct inode **delegated_inode) -+{ -+ int err; -+ struct path tmp = { -+ .mnt = path->mnt -+ }; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ IMustLock(src_dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ tmp.dentry = src_dentry->d_parent; -+ err = security_path_rename(&tmp, src_dentry, path, d, /*flags*/0); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_rename(src_dir, src_dentry, dir, path->dentry, -+ delegated_inode, /*flags*/0); -+ lockdep_on(); -+ if (!err) { -+ int did; -+ -+ tmp.dentry = d->d_parent; -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = src_dentry; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ tmp.dentry = src_dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_mkdir(struct inode *dir, struct path *path, int mode) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_mkdir(path, d, mode); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_mkdir(dir, path->dentry, mode); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = *path; -+ int did; -+ -+ vfsub_update_h_iattr(&tmp, &did); -+ if (did) { -+ tmp.dentry = path->dentry->d_parent; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); -+ } -+ /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+int vfsub_rmdir(struct inode *dir, struct path *path) -+{ -+ int err; -+ struct dentry *d; -+ -+ IMustLock(dir); -+ -+ d = path->dentry; -+ path->dentry = d->d_parent; -+ err = security_path_rmdir(path, d); -+ path->dentry = d; -+ if (unlikely(err)) -+ goto out; -+ -+ lockdep_off(); -+ err = vfs_rmdir(dir, path->dentry); -+ lockdep_on(); -+ if (!err) { -+ struct path tmp = { -+ .dentry = path->dentry->d_parent, -+ .mnt = path->mnt -+ }; -+ -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* todo: support mmap_sem? */ -+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ -+ lockdep_off(); -+ err = vfs_read(file, ubuf, count, ppos); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+/* todo: kernel_read()? */ -+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ char __user *u; -+ } buf; -+ -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = vfsub_read_u(file, buf.u, count, ppos); -+ set_fs(oldfs); -+ return err; -+} -+ -+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, -+ loff_t *ppos) -+{ -+ ssize_t err; -+ -+ lockdep_off(); -+ err = vfs_write(file, ubuf, count, ppos); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ const char __user *u; -+ } buf; -+ -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = vfsub_write_u(file, buf.u, count, ppos); -+ set_fs(oldfs); -+ return err; -+} -+ -+int vfsub_flush(struct file *file, fl_owner_t id) -+{ -+ int err; -+ -+ err = 0; -+ if (file->f_op->flush) { -+ if (!au_test_nfs(file->f_path.dentry->d_sb)) -+ err = file->f_op->flush(file, id); -+ else { -+ lockdep_off(); -+ err = file->f_op->flush(file, id); -+ lockdep_on(); -+ } -+ if (!err) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); -+ /*ignore*/ -+ } -+ return err; -+} -+ -+int vfsub_iterate_dir(struct file *file, struct dir_context *ctx) -+{ -+ int err; -+ -+ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); -+ -+ lockdep_off(); -+ err = iterate_dir(file, ctx); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+long vfsub_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags) -+{ -+ long err; -+ -+ lockdep_off(); -+ err = do_splice_to(in, ppos, pipe, len, flags); -+ lockdep_on(); -+ file_accessed(in); -+ if (err >= 0) -+ vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags) -+{ -+ long err; -+ -+ lockdep_off(); -+ err = do_splice_from(pipe, out, ppos, len, flags); -+ lockdep_on(); -+ if (err >= 0) -+ vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/ -+ return err; -+} -+ -+int vfsub_fsync(struct file *file, struct path *path, int datasync) -+{ -+ int err; -+ -+ /* file can be NULL */ -+ lockdep_off(); -+ err = vfs_fsync(file, datasync); -+ lockdep_on(); -+ if (!err) { -+ if (!path) { -+ AuDebugOn(!file); -+ path = &file->f_path; -+ } -+ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ -+ } -+ return err; -+} -+ -+/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */ -+int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, -+ struct file *h_file) -+{ -+ int err; -+ struct inode *h_inode; -+ struct super_block *h_sb; -+ -+ if (!h_file) { -+ err = vfsub_truncate(h_path, length); -+ goto out; -+ } -+ -+ h_inode = h_path->dentry->d_inode; -+ h_sb = h_inode->i_sb; -+ lockdep_off(); -+ sb_start_write(h_sb); -+ lockdep_on(); -+ err = locks_verify_truncate(h_inode, h_file, length); -+ if (!err) -+ err = security_path_truncate(h_path); -+ if (!err) { -+ lockdep_off(); -+ err = do_truncate(h_path->dentry, length, attr, h_file); -+ lockdep_on(); -+ } -+ lockdep_off(); -+ sb_end_write(h_sb); -+ lockdep_on(); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_vfsub_mkdir_args { -+ int *errp; -+ struct inode *dir; -+ struct path *path; -+ int mode; -+}; -+ -+static void au_call_vfsub_mkdir(void *args) -+{ -+ struct au_vfsub_mkdir_args *a = args; -+ *a->errp = vfsub_mkdir(a->dir, a->path, a->mode); -+} -+ -+int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode) -+{ -+ int err, do_sio, wkq_err; -+ -+ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); -+ if (!do_sio) { -+ lockdep_off(); -+ err = vfsub_mkdir(dir, path, mode); -+ lockdep_on(); -+ } else { -+ struct au_vfsub_mkdir_args args = { -+ .errp = &err, -+ .dir = dir, -+ .path = path, -+ .mode = mode -+ }; -+ wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -+ -+struct au_vfsub_rmdir_args { -+ int *errp; -+ struct inode *dir; -+ struct path *path; -+}; -+ -+static void au_call_vfsub_rmdir(void *args) -+{ -+ struct au_vfsub_rmdir_args *a = args; -+ *a->errp = vfsub_rmdir(a->dir, a->path); -+} -+ -+int vfsub_sio_rmdir(struct inode *dir, struct path *path) -+{ -+ int err, do_sio, wkq_err; -+ -+ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); -+ if (!do_sio) { -+ lockdep_off(); -+ err = vfsub_rmdir(dir, path); -+ lockdep_on(); -+ } else { -+ struct au_vfsub_rmdir_args args = { -+ .errp = &err, -+ .dir = dir, -+ .path = path -+ }; -+ wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct notify_change_args { -+ int *errp; -+ struct path *path; -+ struct iattr *ia; -+ struct inode **delegated_inode; -+}; -+ -+static void call_notify_change(void *args) -+{ -+ struct notify_change_args *a = args; -+ struct inode *h_inode; -+ -+ h_inode = a->path->dentry->d_inode; -+ IMustLock(h_inode); -+ -+ *a->errp = -EPERM; -+ if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { -+ lockdep_off(); -+ *a->errp = notify_change(a->path->dentry, a->ia, -+ a->delegated_inode); -+ lockdep_on(); -+ if (!*a->errp) -+ vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/ -+ } -+ AuTraceErr(*a->errp); -+} -+ -+int vfsub_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode) -+{ -+ int err; -+ struct notify_change_args args = { -+ .errp = &err, -+ .path = path, -+ .ia = ia, -+ .delegated_inode = delegated_inode -+ }; -+ -+ call_notify_change(&args); -+ -+ return err; -+} -+ -+int vfsub_sio_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode) -+{ -+ int err, wkq_err; -+ struct notify_change_args args = { -+ .errp = &err, -+ .path = path, -+ .ia = ia, -+ .delegated_inode = delegated_inode -+ }; -+ -+ wkq_err = au_wkq_wait(call_notify_change, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct unlink_args { -+ int *errp; -+ struct inode *dir; -+ struct path *path; -+ struct inode **delegated_inode; -+}; -+ -+static void call_unlink(void *args) -+{ -+ struct unlink_args *a = args; -+ struct dentry *d = a->path->dentry; -+ struct inode *h_inode; -+ const int stop_sillyrename = (au_test_nfs(d->d_sb) -+ && au_dcount(d) == 1); -+ -+ IMustLock(a->dir); -+ -+ a->path->dentry = d->d_parent; -+ *a->errp = security_path_unlink(a->path, d); -+ a->path->dentry = d; -+ if (unlikely(*a->errp)) -+ return; -+ -+ if (!stop_sillyrename) -+ dget(d); -+ h_inode = d->d_inode; -+ if (h_inode) -+ ihold(h_inode); -+ -+ lockdep_off(); -+ *a->errp = vfs_unlink(a->dir, d, a->delegated_inode); -+ lockdep_on(); -+ if (!*a->errp) { -+ struct path tmp = { -+ .dentry = d->d_parent, -+ .mnt = a->path->mnt -+ }; -+ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ -+ } -+ -+ if (!stop_sillyrename) -+ dput(d); -+ if (h_inode) -+ iput(h_inode); -+ -+ AuTraceErr(*a->errp); -+} -+ -+/* -+ * @dir: must be locked. -+ * @dentry: target dentry. -+ */ -+int vfsub_unlink(struct inode *dir, struct path *path, -+ struct inode **delegated_inode, int force) -+{ -+ int err; -+ struct unlink_args args = { -+ .errp = &err, -+ .dir = dir, -+ .path = path, -+ .delegated_inode = delegated_inode -+ }; -+ -+ if (!force) -+ call_unlink(&args); -+ else { -+ int wkq_err; -+ -+ wkq_err = au_wkq_wait(call_unlink, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/vfsub.h linux-4.0.4/fs/aufs/vfsub.h ---- linux-4.0.4.orig/fs/aufs/vfsub.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/vfsub.h 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,273 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * sub-routines for VFS -+ */ -+ -+#ifndef __AUFS_VFSUB_H__ -+#define __AUFS_VFSUB_H__ -+ -+#ifdef __KERNEL__ -+ -+#include -+#include -+#include -+#include "debug.h" -+ -+/* copied from linux/fs/internal.h */ -+/* todo: BAD approach!! */ -+extern void __mnt_drop_write(struct vfsmount *); -+extern spinlock_t inode_sb_list_lock; -+extern int open_check_o_direct(struct file *f); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* lock subclass for lower inode */ -+/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ -+/* reduce? gave up. */ -+enum { -+ AuLsc_I_Begin = I_MUTEX_PARENT2, /* 5 */ -+ AuLsc_I_PARENT, /* lower inode, parent first */ -+ AuLsc_I_PARENT2, /* copyup dirs */ -+ AuLsc_I_PARENT3, /* copyup wh */ -+ AuLsc_I_CHILD, -+ AuLsc_I_CHILD2, -+ AuLsc_I_End -+}; -+ -+/* to debug easier, do not make them inlined functions */ -+#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) -+#define IMustLock(i) MtxMustLock(&(i)->i_mutex) -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline void vfsub_drop_nlink(struct inode *inode) -+{ -+ AuDebugOn(!inode->i_nlink); -+ drop_nlink(inode); -+} -+ -+static inline void vfsub_dead_dir(struct inode *inode) -+{ -+ AuDebugOn(!S_ISDIR(inode->i_mode)); -+ inode->i_flags |= S_DEAD; -+ clear_nlink(inode); -+} -+ -+static inline int vfsub_native_ro(struct inode *inode) -+{ -+ return (inode->i_sb->s_flags & MS_RDONLY) -+ || IS_RDONLY(inode) -+ /* || IS_APPEND(inode) */ -+ || IS_IMMUTABLE(inode); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_update_h_iattr(struct path *h_path, int *did); -+struct file *vfsub_dentry_open(struct path *path, int flags); -+struct file *vfsub_filp_open(const char *path, int oflags, int mode); -+struct vfsub_aopen_args { -+ struct file *file; -+ unsigned int open_flag; -+ umode_t create_mode; -+ int *opened; -+}; -+struct au_branch; -+int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, -+ struct vfsub_aopen_args *args, struct au_branch *br); -+int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); -+ -+struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, -+ int len); -+ -+struct vfsub_lkup_one_args { -+ struct dentry **errp; -+ struct qstr *name; -+ struct dentry *parent; -+}; -+ -+static inline struct dentry *vfsub_lkup_one(struct qstr *name, -+ struct dentry *parent) -+{ -+ return vfsub_lookup_one_len(name->name, parent, name->len); -+} -+ -+void vfsub_call_lkup_one(void *args); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline int vfsub_mnt_want_write(struct vfsmount *mnt) -+{ -+ int err; -+ -+ lockdep_off(); -+ err = mnt_want_write(mnt); -+ lockdep_on(); -+ return err; -+} -+ -+static inline void vfsub_mnt_drop_write(struct vfsmount *mnt) -+{ -+ lockdep_off(); -+ mnt_drop_write(mnt); -+ lockdep_on(); -+} -+ -+#if 0 /* reserved */ -+static inline void vfsub_mnt_drop_write_file(struct file *file) -+{ -+ lockdep_off(); -+ mnt_drop_write_file(file); -+ lockdep_on(); -+} -+#endif -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_hinode; -+struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2); -+void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, -+ struct dentry *d2, struct au_hinode *hdir2); -+ -+int vfsub_create(struct inode *dir, struct path *path, int mode, -+ bool want_excl); -+int vfsub_symlink(struct inode *dir, struct path *path, -+ const char *symname); -+int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev); -+int vfsub_link(struct dentry *src_dentry, struct inode *dir, -+ struct path *path, struct inode **delegated_inode); -+int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, -+ struct inode *hdir, struct path *path, -+ struct inode **delegated_inode); -+int vfsub_mkdir(struct inode *dir, struct path *path, int mode); -+int vfsub_rmdir(struct inode *dir, struct path *path); -+ -+/* ---------------------------------------------------------------------- */ -+ -+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, -+ loff_t *ppos); -+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, -+ loff_t *ppos); -+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, -+ loff_t *ppos); -+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, -+ loff_t *ppos); -+int vfsub_flush(struct file *file, fl_owner_t id); -+int vfsub_iterate_dir(struct file *file, struct dir_context *ctx); -+ -+static inline loff_t vfsub_f_size_read(struct file *file) -+{ -+ return i_size_read(file_inode(file)); -+} -+ -+static inline unsigned int vfsub_file_flags(struct file *file) -+{ -+ unsigned int flags; -+ -+ spin_lock(&file->f_lock); -+ flags = file->f_flags; -+ spin_unlock(&file->f_lock); -+ -+ return flags; -+} -+ -+#if 0 /* reserved */ -+static inline void vfsub_file_accessed(struct file *h_file) -+{ -+ file_accessed(h_file); -+ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/ -+} -+#endif -+ -+static inline void vfsub_touch_atime(struct vfsmount *h_mnt, -+ struct dentry *h_dentry) -+{ -+ struct path h_path = { -+ .dentry = h_dentry, -+ .mnt = h_mnt -+ }; -+ touch_atime(&h_path); -+ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ -+} -+ -+static inline int vfsub_update_time(struct inode *h_inode, struct timespec *ts, -+ int flags) -+{ -+ return generic_update_time(h_inode, ts, flags); -+ /* no vfsub_update_h_iattr() since we don't have struct path */ -+} -+ -+long vfsub_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags); -+long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags); -+ -+static inline long vfsub_truncate(struct path *path, loff_t length) -+{ -+ long err; -+ -+ lockdep_off(); -+ err = vfs_truncate(path, length); -+ lockdep_on(); -+ return err; -+} -+ -+int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, -+ struct file *h_file); -+int vfsub_fsync(struct file *file, struct path *path, int datasync); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) -+{ -+ loff_t err; -+ -+ lockdep_off(); -+ err = vfs_llseek(file, offset, origin); -+ lockdep_on(); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode); -+int vfsub_sio_rmdir(struct inode *dir, struct path *path); -+int vfsub_sio_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode); -+int vfsub_notify_change(struct path *path, struct iattr *ia, -+ struct inode **delegated_inode); -+int vfsub_unlink(struct inode *dir, struct path *path, -+ struct inode **delegated_inode, int force); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline int vfsub_setxattr(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags) -+{ -+ int err; -+ -+ lockdep_off(); -+ err = vfs_setxattr(dentry, name, value, size, flags); -+ lockdep_on(); -+ -+ return err; -+} -+ -+static inline int vfsub_removexattr(struct dentry *dentry, const char *name) -+{ -+ int err; -+ -+ lockdep_off(); -+ err = vfs_removexattr(dentry, name); -+ lockdep_on(); -+ -+ return err; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_VFSUB_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/wbr_policy.c linux-4.0.4/fs/aufs/wbr_policy.c ---- linux-4.0.4.orig/fs/aufs/wbr_policy.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/wbr_policy.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,752 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * policies for selecting one among multiple writable branches -+ */ -+ -+#include -+#include "aufs.h" -+ -+/* subset of cpup_attr() */ -+static noinline_for_stack -+int au_cpdown_attr(struct path *h_path, struct dentry *h_src) -+{ -+ int err, sbits; -+ struct iattr ia; -+ struct inode *h_isrc; -+ -+ h_isrc = h_src->d_inode; -+ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; -+ ia.ia_mode = h_isrc->i_mode; -+ ia.ia_uid = h_isrc->i_uid; -+ ia.ia_gid = h_isrc->i_gid; -+ sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); -+ au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc->i_flags); -+ /* no delegation since it is just created */ -+ err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); -+ -+ /* is this nfs only? */ -+ if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { -+ ia.ia_valid = ATTR_FORCE | ATTR_MODE; -+ ia.ia_mode = h_isrc->i_mode; -+ err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); -+ } -+ -+ return err; -+} -+ -+#define AuCpdown_PARENT_OPQ 1 -+#define AuCpdown_WHED (1 << 1) -+#define AuCpdown_MADE_DIR (1 << 2) -+#define AuCpdown_DIROPQ (1 << 3) -+#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name) -+#define au_fset_cpdown(flags, name) \ -+ do { (flags) |= AuCpdown_##name; } while (0) -+#define au_fclr_cpdown(flags, name) \ -+ do { (flags) &= ~AuCpdown_##name; } while (0) -+ -+static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, -+ unsigned int *flags) -+{ -+ int err; -+ struct dentry *opq_dentry; -+ -+ opq_dentry = au_diropq_create(dentry, bdst); -+ err = PTR_ERR(opq_dentry); -+ if (IS_ERR(opq_dentry)) -+ goto out; -+ dput(opq_dentry); -+ au_fset_cpdown(*flags, DIROPQ); -+ -+out: -+ return err; -+} -+ -+static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, -+ struct inode *dir, aufs_bindex_t bdst) -+{ -+ int err; -+ struct path h_path; -+ struct au_branch *br; -+ -+ br = au_sbr(dentry->d_sb, bdst); -+ h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) -+ goto out; -+ -+ err = 0; -+ if (h_path.dentry->d_inode) { -+ h_path.mnt = au_br_mnt(br); -+ err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, -+ dentry); -+ } -+ dput(h_path.dentry); -+ -+out: -+ return err; -+} -+ -+static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, -+ struct au_pin *pin, -+ struct dentry *h_parent, void *arg) -+{ -+ int err, rerr; -+ aufs_bindex_t bopq, bstart; -+ struct path h_path; -+ struct dentry *parent; -+ struct inode *h_dir, *h_inode, *inode, *dir; -+ unsigned int *flags = arg; -+ -+ bstart = au_dbstart(dentry); -+ /* dentry is di-locked */ -+ parent = dget_parent(dentry); -+ dir = parent->d_inode; -+ h_dir = h_parent->d_inode; -+ AuDebugOn(h_dir != au_h_iptr(dir, bdst)); -+ IMustLock(h_dir); -+ -+ err = au_lkup_neg(dentry, bdst, /*wh*/0); -+ if (unlikely(err < 0)) -+ goto out; -+ h_path.dentry = au_h_dptr(dentry, bdst); -+ h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); -+ err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, -+ S_IRWXU | S_IRUGO | S_IXUGO); -+ if (unlikely(err)) -+ goto out_put; -+ au_fset_cpdown(*flags, MADE_DIR); -+ -+ bopq = au_dbdiropq(dentry); -+ au_fclr_cpdown(*flags, WHED); -+ au_fclr_cpdown(*flags, DIROPQ); -+ if (au_dbwh(dentry) == bdst) -+ au_fset_cpdown(*flags, WHED); -+ if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) -+ au_fset_cpdown(*flags, PARENT_OPQ); -+ h_inode = h_path.dentry->d_inode; -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ if (au_ftest_cpdown(*flags, WHED)) { -+ err = au_cpdown_dir_opq(dentry, bdst, flags); -+ if (unlikely(err)) { -+ mutex_unlock(&h_inode->i_mutex); -+ goto out_dir; -+ } -+ } -+ -+ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); -+ mutex_unlock(&h_inode->i_mutex); -+ if (unlikely(err)) -+ goto out_opq; -+ -+ if (au_ftest_cpdown(*flags, WHED)) { -+ err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); -+ if (unlikely(err)) -+ goto out_opq; -+ } -+ -+ inode = dentry->d_inode; -+ if (au_ibend(inode) < bdst) -+ au_set_ibend(inode, bdst); -+ au_set_h_iptr(inode, bdst, au_igrab(h_inode), -+ au_hi_flags(inode, /*isdir*/1)); -+ au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0); -+ goto out; /* success */ -+ -+ /* revert */ -+out_opq: -+ if (au_ftest_cpdown(*flags, DIROPQ)) { -+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); -+ rerr = au_diropq_remove(dentry, bdst); -+ mutex_unlock(&h_inode->i_mutex); -+ if (unlikely(rerr)) { -+ AuIOErr("failed removing diropq for %pd b%d (%d)\n", -+ dentry, bdst, rerr); -+ err = -EIO; -+ goto out; -+ } -+ } -+out_dir: -+ if (au_ftest_cpdown(*flags, MADE_DIR)) { -+ rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); -+ if (unlikely(rerr)) { -+ AuIOErr("failed removing %pd b%d (%d)\n", -+ dentry, bdst, rerr); -+ err = -EIO; -+ } -+ } -+out_put: -+ au_set_h_dptr(dentry, bdst, NULL); -+ if (au_dbend(dentry) == bdst) -+ au_update_dbend(dentry); -+out: -+ dput(parent); -+ return err; -+} -+ -+int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) -+{ -+ int err; -+ unsigned int flags; -+ -+ flags = 0; -+ err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policies for create */ -+ -+int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ int err, i, j, ndentry; -+ aufs_bindex_t bopq; -+ struct au_dcsub_pages dpages; -+ struct au_dpage *dpage; -+ struct dentry **dentries, *parent, *d; -+ -+ err = au_dpages_init(&dpages, GFP_NOFS); -+ if (unlikely(err)) -+ goto out; -+ parent = dget_parent(dentry); -+ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0); -+ if (unlikely(err)) -+ goto out_free; -+ -+ err = bindex; -+ for (i = 0; i < dpages.ndpage; i++) { -+ dpage = dpages.dpages + i; -+ dentries = dpage->dentries; -+ ndentry = dpage->ndentry; -+ for (j = 0; j < ndentry; j++) { -+ d = dentries[j]; -+ di_read_lock_parent2(d, !AuLock_IR); -+ bopq = au_dbdiropq(d); -+ di_read_unlock(d, !AuLock_IR); -+ if (bopq >= 0 && bopq < err) -+ err = bopq; -+ } -+ } -+ -+out_free: -+ dput(parent); -+ au_dpages_free(&dpages); -+out: -+ return err; -+} -+ -+static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ for (; bindex >= 0; bindex--) -+ if (!au_br_rdonly(au_sbr(sb, bindex))) -+ return bindex; -+ return -EROFS; -+} -+ -+/* top down parent */ -+static int au_wbr_create_tdp(struct dentry *dentry, -+ unsigned int flags __maybe_unused) -+{ -+ int err; -+ aufs_bindex_t bstart, bindex; -+ struct super_block *sb; -+ struct dentry *parent, *h_parent; -+ -+ sb = dentry->d_sb; -+ bstart = au_dbstart(dentry); -+ err = bstart; -+ if (!au_br_rdonly(au_sbr(sb, bstart))) -+ goto out; -+ -+ err = -EROFS; -+ parent = dget_parent(dentry); -+ for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ -+ if (!au_br_rdonly(au_sbr(sb, bindex))) { -+ err = bindex; -+ break; -+ } -+ } -+ dput(parent); -+ -+ /* bottom up here */ -+ if (unlikely(err < 0)) { -+ err = au_wbr_bu(sb, bstart - 1); -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ } -+ -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* an exception for the policy other than tdp */ -+static int au_wbr_create_exp(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bwh, bdiropq; -+ struct dentry *parent; -+ -+ err = -1; -+ bwh = au_dbwh(dentry); -+ parent = dget_parent(dentry); -+ bdiropq = au_dbdiropq(parent); -+ if (bwh >= 0) { -+ if (bdiropq >= 0) -+ err = min(bdiropq, bwh); -+ else -+ err = bwh; -+ AuDbg("%d\n", err); -+ } else if (bdiropq >= 0) { -+ err = bdiropq; -+ AuDbg("%d\n", err); -+ } -+ dput(parent); -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+ if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) -+ err = -1; -+ -+ AuDbg("%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* round robin */ -+static int au_wbr_create_init_rr(struct super_block *sb) -+{ -+ int err; -+ -+ err = au_wbr_bu(sb, au_sbend(sb)); -+ atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ -+ /* smp_mb(); */ -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags) -+{ -+ int err, nbr; -+ unsigned int u; -+ aufs_bindex_t bindex, bend; -+ struct super_block *sb; -+ atomic_t *next; -+ -+ err = au_wbr_create_exp(dentry); -+ if (err >= 0) -+ goto out; -+ -+ sb = dentry->d_sb; -+ next = &au_sbi(sb)->si_wbr_rr_next; -+ bend = au_sbend(sb); -+ nbr = bend + 1; -+ for (bindex = 0; bindex <= bend; bindex++) { -+ if (!au_ftest_wbr(flags, DIR)) { -+ err = atomic_dec_return(next) + 1; -+ /* modulo for 0 is meaningless */ -+ if (unlikely(!err)) -+ err = atomic_dec_return(next) + 1; -+ } else -+ err = atomic_read(next); -+ AuDbg("%d\n", err); -+ u = err; -+ err = u % nbr; -+ AuDbg("%d\n", err); -+ if (!au_br_rdonly(au_sbr(sb, err))) -+ break; -+ err = -EROFS; -+ } -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+out: -+ AuDbg("%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* most free space */ -+static void au_mfs(struct dentry *dentry, struct dentry *parent) -+{ -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_wbr_mfs *mfs; -+ struct dentry *h_parent; -+ aufs_bindex_t bindex, bend; -+ int err; -+ unsigned long long b, bavail; -+ struct path h_path; -+ /* reduce the stack usage */ -+ struct kstatfs *st; -+ -+ st = kmalloc(sizeof(*st), GFP_NOFS); -+ if (unlikely(!st)) { -+ AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); -+ return; -+ } -+ -+ bavail = 0; -+ sb = dentry->d_sb; -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ MtxMustLock(&mfs->mfs_lock); -+ mfs->mfs_bindex = -EROFS; -+ mfs->mfsrr_bytes = 0; -+ if (!parent) { -+ bindex = 0; -+ bend = au_sbend(sb); -+ } else { -+ bindex = au_dbstart(parent); -+ bend = au_dbtaildir(parent); -+ } -+ -+ for (; bindex <= bend; bindex++) { -+ if (parent) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ } -+ br = au_sbr(sb, bindex); -+ if (au_br_rdonly(br)) -+ continue; -+ -+ /* sb->s_root for NFS is unreliable */ -+ h_path.mnt = au_br_mnt(br); -+ h_path.dentry = h_path.mnt->mnt_root; -+ err = vfs_statfs(&h_path, st); -+ if (unlikely(err)) { -+ AuWarn1("failed statfs, b%d, %d\n", bindex, err); -+ continue; -+ } -+ -+ /* when the available size is equal, select the lower one */ -+ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) -+ || sizeof(b) < sizeof(st->f_bsize)); -+ b = st->f_bavail * st->f_bsize; -+ br->br_wbr->wbr_bytes = b; -+ if (b >= bavail) { -+ bavail = b; -+ mfs->mfs_bindex = bindex; -+ mfs->mfs_jiffy = jiffies; -+ } -+ } -+ -+ mfs->mfsrr_bytes = bavail; -+ AuDbg("b%d\n", mfs->mfs_bindex); -+ kfree(st); -+} -+ -+static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) -+{ -+ int err; -+ struct dentry *parent; -+ struct super_block *sb; -+ struct au_wbr_mfs *mfs; -+ -+ err = au_wbr_create_exp(dentry); -+ if (err >= 0) -+ goto out; -+ -+ sb = dentry->d_sb; -+ parent = NULL; -+ if (au_ftest_wbr(flags, PARENT)) -+ parent = dget_parent(dentry); -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ mutex_lock(&mfs->mfs_lock); -+ if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) -+ || mfs->mfs_bindex < 0 -+ || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) -+ au_mfs(dentry, parent); -+ mutex_unlock(&mfs->mfs_lock); -+ err = mfs->mfs_bindex; -+ dput(parent); -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_create_init_mfs(struct super_block *sb) -+{ -+ struct au_wbr_mfs *mfs; -+ -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ mutex_init(&mfs->mfs_lock); -+ mfs->mfs_jiffy = 0; -+ mfs->mfs_bindex = -EROFS; -+ -+ return 0; -+} -+ -+static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused) -+{ -+ mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); -+ return 0; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* most free space and then round robin */ -+static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags) -+{ -+ int err; -+ struct au_wbr_mfs *mfs; -+ -+ err = au_wbr_create_mfs(dentry, flags); -+ if (err >= 0) { -+ mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; -+ mutex_lock(&mfs->mfs_lock); -+ if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) -+ err = au_wbr_create_rr(dentry, flags); -+ mutex_unlock(&mfs->mfs_lock); -+ } -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_create_init_mfsrr(struct super_block *sb) -+{ -+ int err; -+ -+ au_wbr_create_init_mfs(sb); /* ignore */ -+ err = au_wbr_create_init_rr(sb); -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* top down parent and most free space */ -+static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags) -+{ -+ int err, e2; -+ unsigned long long b; -+ aufs_bindex_t bindex, bstart, bend; -+ struct super_block *sb; -+ struct dentry *parent, *h_parent; -+ struct au_branch *br; -+ -+ err = au_wbr_create_tdp(dentry, flags); -+ if (unlikely(err < 0)) -+ goto out; -+ parent = dget_parent(dentry); -+ bstart = au_dbstart(parent); -+ bend = au_dbtaildir(parent); -+ if (bstart == bend) -+ goto out_parent; /* success */ -+ -+ e2 = au_wbr_create_mfs(dentry, flags); -+ if (e2 < 0) -+ goto out_parent; /* success */ -+ -+ /* when the available size is equal, select upper one */ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, err); -+ b = br->br_wbr->wbr_bytes; -+ AuDbg("b%d, %llu\n", err, b); -+ -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ -+ br = au_sbr(sb, bindex); -+ if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) { -+ b = br->br_wbr->wbr_bytes; -+ err = bindex; -+ AuDbg("b%d, %llu\n", err, b); -+ } -+ } -+ -+ if (err >= 0) -+ err = au_wbr_nonopq(dentry, err); -+ -+out_parent: -+ dput(parent); -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * - top down parent -+ * - most free space with parent -+ * - most free space round-robin regardless parent -+ */ -+static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags) -+{ -+ int err; -+ unsigned long long watermark; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct au_wbr_mfs *mfs; -+ -+ err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, err); -+ mfs = &au_sbi(sb)->si_wbr_mfs; -+ mutex_lock(&mfs->mfs_lock); -+ watermark = mfs->mfsrr_watermark; -+ mutex_unlock(&mfs->mfs_lock); -+ if (br->br_wbr->wbr_bytes < watermark) -+ /* regardless the parent dir */ -+ err = au_wbr_create_mfsrr(dentry, flags); -+ -+out: -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* policies for copyup */ -+ -+/* top down parent */ -+static int au_wbr_copyup_tdp(struct dentry *dentry) -+{ -+ return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0); -+} -+ -+/* bottom up parent */ -+static int au_wbr_copyup_bup(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bindex, bstart; -+ struct dentry *parent, *h_parent; -+ struct super_block *sb; -+ -+ err = -EROFS; -+ sb = dentry->d_sb; -+ parent = dget_parent(dentry); -+ bstart = au_dbstart(parent); -+ for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { -+ h_parent = au_h_dptr(parent, bindex); -+ if (!h_parent || !h_parent->d_inode) -+ continue; -+ -+ if (!au_br_rdonly(au_sbr(sb, bindex))) { -+ err = bindex; -+ break; -+ } -+ } -+ dput(parent); -+ -+ /* bottom up here */ -+ if (unlikely(err < 0)) -+ err = au_wbr_bu(sb, bstart - 1); -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+/* bottom up */ -+int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart) -+{ -+ int err; -+ -+ err = au_wbr_bu(dentry->d_sb, bstart); -+ AuDbg("b%d\n", err); -+ if (err > bstart) -+ err = au_wbr_nonopq(dentry, err); -+ -+ AuDbg("b%d\n", err); -+ return err; -+} -+ -+static int au_wbr_copyup_bu(struct dentry *dentry) -+{ -+ int err; -+ aufs_bindex_t bstart; -+ -+ bstart = au_dbstart(dentry); -+ err = au_wbr_do_copyup_bu(dentry, bstart); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_wbr_copyup_operations au_wbr_copyup_ops[] = { -+ [AuWbrCopyup_TDP] = { -+ .copyup = au_wbr_copyup_tdp -+ }, -+ [AuWbrCopyup_BUP] = { -+ .copyup = au_wbr_copyup_bup -+ }, -+ [AuWbrCopyup_BU] = { -+ .copyup = au_wbr_copyup_bu -+ } -+}; -+ -+struct au_wbr_create_operations au_wbr_create_ops[] = { -+ [AuWbrCreate_TDP] = { -+ .create = au_wbr_create_tdp -+ }, -+ [AuWbrCreate_RR] = { -+ .create = au_wbr_create_rr, -+ .init = au_wbr_create_init_rr -+ }, -+ [AuWbrCreate_MFS] = { -+ .create = au_wbr_create_mfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_MFSV] = { -+ .create = au_wbr_create_mfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_MFSRR] = { -+ .create = au_wbr_create_mfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_MFSRRV] = { -+ .create = au_wbr_create_mfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFS] = { -+ .create = au_wbr_create_pmfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFSV] = { -+ .create = au_wbr_create_pmfs, -+ .init = au_wbr_create_init_mfs, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFSRR] = { -+ .create = au_wbr_create_pmfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ }, -+ [AuWbrCreate_PMFSRRV] = { -+ .create = au_wbr_create_pmfsrr, -+ .init = au_wbr_create_init_mfsrr, -+ .fin = au_wbr_create_fin_mfs -+ } -+}; -diff -Nur linux-4.0.4.orig/fs/aufs/whout.c linux-4.0.4/fs/aufs/whout.c ---- linux-4.0.4.orig/fs/aufs/whout.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/whout.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,1051 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * whiteout for logical deletion and opaque directory -+ */ -+ -+#include "aufs.h" -+ -+#define WH_MASK S_IRUGO -+ -+/* -+ * If a directory contains this file, then it is opaque. We start with the -+ * .wh. flag so that it is blocked by lookup. -+ */ -+static struct qstr diropq_name = QSTR_INIT(AUFS_WH_DIROPQ, -+ sizeof(AUFS_WH_DIROPQ) - 1); -+ -+/* -+ * generate whiteout name, which is NOT terminated by NULL. -+ * @name: original d_name.name -+ * @len: original d_name.len -+ * @wh: whiteout qstr -+ * returns zero when succeeds, otherwise error. -+ * succeeded value as wh->name should be freed by kfree(). -+ */ -+int au_wh_name_alloc(struct qstr *wh, const struct qstr *name) -+{ -+ char *p; -+ -+ if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN)) -+ return -ENAMETOOLONG; -+ -+ wh->len = name->len + AUFS_WH_PFX_LEN; -+ p = kmalloc(wh->len, GFP_NOFS); -+ wh->name = p; -+ if (p) { -+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); -+ memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len); -+ /* smp_mb(); */ -+ return 0; -+ } -+ return -ENOMEM; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * test if the @wh_name exists under @h_parent. -+ * @try_sio specifies the necessary of super-io. -+ */ -+int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio) -+{ -+ int err; -+ struct dentry *wh_dentry; -+ -+ if (!try_sio) -+ wh_dentry = vfsub_lkup_one(wh_name, h_parent); -+ else -+ wh_dentry = au_sio_lkup_one(wh_name, h_parent); -+ err = PTR_ERR(wh_dentry); -+ if (IS_ERR(wh_dentry)) { -+ if (err == -ENAMETOOLONG) -+ err = 0; -+ goto out; -+ } -+ -+ err = 0; -+ if (!wh_dentry->d_inode) -+ goto out_wh; /* success */ -+ -+ err = 1; -+ if (d_is_reg(wh_dentry)) -+ goto out_wh; /* success */ -+ -+ err = -EIO; -+ AuIOErr("%pd Invalid whiteout entry type 0%o.\n", -+ wh_dentry, wh_dentry->d_inode->i_mode); -+ -+out_wh: -+ dput(wh_dentry); -+out: -+ return err; -+} -+ -+/* -+ * test if the @h_dentry sets opaque or not. -+ */ -+int au_diropq_test(struct dentry *h_dentry) -+{ -+ int err; -+ struct inode *h_dir; -+ -+ h_dir = h_dentry->d_inode; -+ err = au_wh_test(h_dentry, &diropq_name, -+ au_test_h_perm_sio(h_dir, MAY_EXEC)); -+ return err; -+} -+ -+/* -+ * returns a negative dentry whose name is unique and temporary. -+ */ -+struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, -+ struct qstr *prefix) -+{ -+ struct dentry *dentry; -+ int i; -+ char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1], -+ *name, *p; -+ /* strict atomic_t is unnecessary here */ -+ static unsigned short cnt; -+ struct qstr qs; -+ -+ BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); -+ -+ name = defname; -+ qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1; -+ if (unlikely(prefix->len > DNAME_INLINE_LEN)) { -+ dentry = ERR_PTR(-ENAMETOOLONG); -+ if (unlikely(qs.len > NAME_MAX)) -+ goto out; -+ dentry = ERR_PTR(-ENOMEM); -+ name = kmalloc(qs.len + 1, GFP_NOFS); -+ if (unlikely(!name)) -+ goto out; -+ } -+ -+ /* doubly whiteout-ed */ -+ memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); -+ p = name + AUFS_WH_PFX_LEN * 2; -+ memcpy(p, prefix->name, prefix->len); -+ p += prefix->len; -+ *p++ = '.'; -+ AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN); -+ -+ qs.name = name; -+ for (i = 0; i < 3; i++) { -+ sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); -+ dentry = au_sio_lkup_one(&qs, h_parent); -+ if (IS_ERR(dentry) || !dentry->d_inode) -+ goto out_name; -+ dput(dentry); -+ } -+ /* pr_warn("could not get random name\n"); */ -+ dentry = ERR_PTR(-EEXIST); -+ AuDbg("%.*s\n", AuLNPair(&qs)); -+ BUG(); -+ -+out_name: -+ if (name != defname) -+ kfree(name); -+out: -+ AuTraceErrPtr(dentry); -+ return dentry; -+} -+ -+/* -+ * rename the @h_dentry on @br to the whiteouted temporary name. -+ */ -+int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) -+{ -+ int err; -+ struct path h_path = { -+ .mnt = au_br_mnt(br) -+ }; -+ struct inode *h_dir, *delegated; -+ struct dentry *h_parent; -+ -+ h_parent = h_dentry->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ -+ h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name); -+ err = PTR_ERR(h_path.dentry); -+ if (IS_ERR(h_path.dentry)) -+ goto out; -+ -+ /* under the same dir, no need to lock_rename() */ -+ delegated = NULL; -+ err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated); -+ AuTraceErr(err); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal rename\n"); -+ iput(delegated); -+ } -+ dput(h_path.dentry); -+ -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * functions for removing a whiteout -+ */ -+ -+static int do_unlink_wh(struct inode *h_dir, struct path *h_path) -+{ -+ int err, force; -+ struct inode *delegated; -+ -+ /* -+ * forces superio when the dir has a sticky bit. -+ * this may be a violation of unix fs semantics. -+ */ -+ force = (h_dir->i_mode & S_ISVTX) -+ && !uid_eq(current_fsuid(), h_path->dentry->d_inode->i_uid); -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, h_path, &delegated, force); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ return err; -+} -+ -+int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, -+ struct dentry *dentry) -+{ -+ int err; -+ -+ err = do_unlink_wh(h_dir, h_path); -+ if (!err && dentry) -+ au_set_dbwh(dentry, -1); -+ -+ return err; -+} -+ -+static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh, -+ struct au_branch *br) -+{ -+ int err; -+ struct path h_path = { -+ .mnt = au_br_mnt(br) -+ }; -+ -+ err = 0; -+ h_path.dentry = vfsub_lkup_one(wh, h_parent); -+ if (IS_ERR(h_path.dentry)) -+ err = PTR_ERR(h_path.dentry); -+ else { -+ if (h_path.dentry->d_inode -+ && d_is_reg(h_path.dentry)) -+ err = do_unlink_wh(h_parent->d_inode, &h_path); -+ dput(h_path.dentry); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * initialize/clean whiteout for a branch -+ */ -+ -+static void au_wh_clean(struct inode *h_dir, struct path *whpath, -+ const int isdir) -+{ -+ int err; -+ struct inode *delegated; -+ -+ if (!whpath->dentry->d_inode) -+ return; -+ -+ if (isdir) -+ err = vfsub_rmdir(h_dir, whpath); -+ else { -+ delegated = NULL; -+ err = vfsub_unlink(h_dir, whpath, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ } -+ if (unlikely(err)) -+ pr_warn("failed removing %pd (%d), ignored.\n", -+ whpath->dentry, err); -+} -+ -+static int test_linkable(struct dentry *h_root) -+{ -+ struct inode *h_dir = h_root->d_inode; -+ -+ if (h_dir->i_op->link) -+ return 0; -+ -+ pr_err("%pd (%s) doesn't support link(2), use noplink and rw+nolwh\n", -+ h_root, au_sbtype(h_root->d_sb)); -+ return -ENOSYS; -+} -+ -+/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ -+static int au_whdir(struct inode *h_dir, struct path *path) -+{ -+ int err; -+ -+ err = -EEXIST; -+ if (!path->dentry->d_inode) { -+ int mode = S_IRWXU; -+ -+ if (au_test_nfs(path->dentry->d_sb)) -+ mode |= S_IXUGO; -+ err = vfsub_mkdir(h_dir, path, mode); -+ } else if (d_is_dir(path->dentry)) -+ err = 0; -+ else -+ pr_err("unknown %pd exists\n", path->dentry); -+ -+ return err; -+} -+ -+struct au_wh_base { -+ const struct qstr *name; -+ struct dentry *dentry; -+}; -+ -+static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[], -+ struct path *h_path) -+{ -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/0); -+ h_path->dentry = base[AuBrWh_PLINK].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+ h_path->dentry = base[AuBrWh_ORPH].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+} -+ -+/* -+ * returns tri-state, -+ * minus: error, caller should print the message -+ * zero: succuess -+ * plus: error, caller should NOT print the message -+ */ -+static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, -+ int do_plink, struct au_wh_base base[], -+ struct path *h_path) -+{ -+ int err; -+ struct inode *h_dir; -+ -+ h_dir = h_root->d_inode; -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ au_wh_clean(h_dir, h_path, /*isdir*/0); -+ h_path->dentry = base[AuBrWh_PLINK].dentry; -+ if (do_plink) { -+ err = test_linkable(h_root); -+ if (unlikely(err)) { -+ err = 1; -+ goto out; -+ } -+ -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); -+ } else -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+ h_path->dentry = base[AuBrWh_ORPH].dentry; -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); -+ -+out: -+ return err; -+} -+ -+/* -+ * for the moment, aufs supports the branch filesystem which does not support -+ * link(2). testing on FAT which does not support i_op->setattr() fully either, -+ * copyup failed. finally, such filesystem will not be used as the writable -+ * branch. -+ * -+ * returns tri-state, see above. -+ */ -+static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, -+ int do_plink, struct au_wh_base base[], -+ struct path *h_path) -+{ -+ int err; -+ struct inode *h_dir; -+ -+ WbrWhMustWriteLock(wbr); -+ -+ err = test_linkable(h_root); -+ if (unlikely(err)) { -+ err = 1; -+ goto out; -+ } -+ -+ /* -+ * todo: should this create be done in /sbin/mount.aufs helper? -+ */ -+ err = -EEXIST; -+ h_dir = h_root->d_inode; -+ if (!base[AuBrWh_BASE].dentry->d_inode) { -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); -+ } else if (d_is_reg(base[AuBrWh_BASE].dentry)) -+ err = 0; -+ else -+ pr_err("unknown %pd2 exists\n", base[AuBrWh_BASE].dentry); -+ if (unlikely(err)) -+ goto out; -+ -+ h_path->dentry = base[AuBrWh_PLINK].dentry; -+ if (do_plink) { -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); -+ } else -+ au_wh_clean(h_dir, h_path, /*isdir*/1); -+ wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); -+ -+ h_path->dentry = base[AuBrWh_ORPH].dentry; -+ err = au_whdir(h_dir, h_path); -+ if (unlikely(err)) -+ goto out; -+ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); -+ -+out: -+ return err; -+} -+ -+/* -+ * initialize the whiteout base file/dir for @br. -+ */ -+int au_wh_init(struct au_branch *br, struct super_block *sb) -+{ -+ int err, i; -+ const unsigned char do_plink -+ = !!au_opt_test(au_mntflags(sb), PLINK); -+ struct inode *h_dir; -+ struct path path = br->br_path; -+ struct dentry *h_root = path.dentry; -+ struct au_wbr *wbr = br->br_wbr; -+ static const struct qstr base_name[] = { -+ [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, -+ sizeof(AUFS_BASE_NAME) - 1), -+ [AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME, -+ sizeof(AUFS_PLINKDIR_NAME) - 1), -+ [AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME, -+ sizeof(AUFS_ORPHDIR_NAME) - 1) -+ }; -+ struct au_wh_base base[] = { -+ [AuBrWh_BASE] = { -+ .name = base_name + AuBrWh_BASE, -+ .dentry = NULL -+ }, -+ [AuBrWh_PLINK] = { -+ .name = base_name + AuBrWh_PLINK, -+ .dentry = NULL -+ }, -+ [AuBrWh_ORPH] = { -+ .name = base_name + AuBrWh_ORPH, -+ .dentry = NULL -+ } -+ }; -+ -+ if (wbr) -+ WbrWhMustWriteLock(wbr); -+ -+ for (i = 0; i < AuBrWh_Last; i++) { -+ /* doubly whiteouted */ -+ struct dentry *d; -+ -+ d = au_wh_lkup(h_root, (void *)base[i].name, br); -+ err = PTR_ERR(d); -+ if (IS_ERR(d)) -+ goto out; -+ -+ base[i].dentry = d; -+ AuDebugOn(wbr -+ && wbr->wbr_wh[i] -+ && wbr->wbr_wh[i] != base[i].dentry); -+ } -+ -+ if (wbr) -+ for (i = 0; i < AuBrWh_Last; i++) { -+ dput(wbr->wbr_wh[i]); -+ wbr->wbr_wh[i] = NULL; -+ } -+ -+ err = 0; -+ if (!au_br_writable(br->br_perm)) { -+ h_dir = h_root->d_inode; -+ au_wh_init_ro(h_dir, base, &path); -+ } else if (!au_br_wh_linkable(br->br_perm)) { -+ err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path); -+ if (err > 0) -+ goto out; -+ else if (err) -+ goto out_err; -+ } else { -+ err = au_wh_init_rw(h_root, wbr, do_plink, base, &path); -+ if (err > 0) -+ goto out; -+ else if (err) -+ goto out_err; -+ } -+ goto out; /* success */ -+ -+out_err: -+ pr_err("an error(%d) on the writable branch %pd(%s)\n", -+ err, h_root, au_sbtype(h_root->d_sb)); -+out: -+ for (i = 0; i < AuBrWh_Last; i++) -+ dput(base[i].dentry); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * whiteouts are all hard-linked usually. -+ * when its link count reaches a ceiling, we create a new whiteout base -+ * asynchronously. -+ */ -+ -+struct reinit_br_wh { -+ struct super_block *sb; -+ struct au_branch *br; -+}; -+ -+static void reinit_br_wh(void *arg) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct path h_path; -+ struct reinit_br_wh *a = arg; -+ struct au_wbr *wbr; -+ struct inode *dir, *delegated; -+ struct dentry *h_root; -+ struct au_hinode *hdir; -+ -+ err = 0; -+ wbr = a->br->br_wbr; -+ /* big aufs lock */ -+ si_noflush_write_lock(a->sb); -+ if (!au_br_writable(a->br->br_perm)) -+ goto out; -+ bindex = au_br_index(a->sb, a->br->br_id); -+ if (unlikely(bindex < 0)) -+ goto out; -+ -+ di_read_lock_parent(a->sb->s_root, AuLock_IR); -+ dir = a->sb->s_root->d_inode; -+ hdir = au_hi(dir, bindex); -+ h_root = au_h_dptr(a->sb->s_root, bindex); -+ AuDebugOn(h_root != au_br_dentry(a->br)); -+ -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ wbr_wh_write_lock(wbr); -+ err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, -+ h_root, a->br); -+ if (!err) { -+ h_path.dentry = wbr->wbr_whbase; -+ h_path.mnt = au_br_mnt(a->br); -+ delegated = NULL; -+ err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, -+ /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ } else { -+ pr_warn("%pd is moved, ignored\n", wbr->wbr_whbase); -+ err = 0; -+ } -+ dput(wbr->wbr_whbase); -+ wbr->wbr_whbase = NULL; -+ if (!err) -+ err = au_wh_init(a->br, a->sb); -+ wbr_wh_write_unlock(wbr); -+ au_hn_imtx_unlock(hdir); -+ di_read_unlock(a->sb->s_root, AuLock_IR); -+ if (!err) -+ au_fhsm_wrote(a->sb, bindex, /*force*/0); -+ -+out: -+ if (wbr) -+ atomic_dec(&wbr->wbr_wh_running); -+ atomic_dec(&a->br->br_count); -+ si_write_unlock(a->sb); -+ au_nwt_done(&au_sbi(a->sb)->si_nowait); -+ kfree(arg); -+ if (unlikely(err)) -+ AuIOErr("err %d\n", err); -+} -+ -+static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) -+{ -+ int do_dec, wkq_err; -+ struct reinit_br_wh *arg; -+ -+ do_dec = 1; -+ if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1) -+ goto out; -+ -+ /* ignore ENOMEM */ -+ arg = kmalloc(sizeof(*arg), GFP_NOFS); -+ if (arg) { -+ /* -+ * dec(wh_running), kfree(arg) and dec(br_count) -+ * in reinit function -+ */ -+ arg->sb = sb; -+ arg->br = br; -+ atomic_inc(&br->br_count); -+ wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0); -+ if (unlikely(wkq_err)) { -+ atomic_dec(&br->br_wbr->wbr_wh_running); -+ atomic_dec(&br->br_count); -+ kfree(arg); -+ } -+ do_dec = 0; -+ } -+ -+out: -+ if (do_dec) -+ atomic_dec(&br->br_wbr->wbr_wh_running); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create the whiteout @wh. -+ */ -+static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, -+ struct dentry *wh) -+{ -+ int err; -+ struct path h_path = { -+ .dentry = wh -+ }; -+ struct au_branch *br; -+ struct au_wbr *wbr; -+ struct dentry *h_parent; -+ struct inode *h_dir, *delegated; -+ -+ h_parent = wh->d_parent; /* dir inode is locked */ -+ h_dir = h_parent->d_inode; -+ IMustLock(h_dir); -+ -+ br = au_sbr(sb, bindex); -+ h_path.mnt = au_br_mnt(br); -+ wbr = br->br_wbr; -+ wbr_wh_read_lock(wbr); -+ if (wbr->wbr_whbase) { -+ delegated = NULL; -+ err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path, &delegated); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal link\n"); -+ iput(delegated); -+ } -+ if (!err || err != -EMLINK) -+ goto out; -+ -+ /* link count full. re-initialize br_whbase. */ -+ kick_reinit_br_wh(sb, br); -+ } -+ -+ /* return this error in this context */ -+ err = vfsub_create(h_dir, &h_path, WH_MASK, /*want_excl*/true); -+ if (!err) -+ au_fhsm_wrote(sb, bindex, /*force*/0); -+ -+out: -+ wbr_wh_read_unlock(wbr); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create or remove the diropq. -+ */ -+static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int flags) -+{ -+ struct dentry *opq_dentry, *h_dentry; -+ struct super_block *sb; -+ struct au_branch *br; -+ int err; -+ -+ sb = dentry->d_sb; -+ br = au_sbr(sb, bindex); -+ h_dentry = au_h_dptr(dentry, bindex); -+ opq_dentry = vfsub_lkup_one(&diropq_name, h_dentry); -+ if (IS_ERR(opq_dentry)) -+ goto out; -+ -+ if (au_ftest_diropq(flags, CREATE)) { -+ err = link_or_create_wh(sb, bindex, opq_dentry); -+ if (!err) { -+ au_set_dbdiropq(dentry, bindex); -+ goto out; /* success */ -+ } -+ } else { -+ struct path tmp = { -+ .dentry = opq_dentry, -+ .mnt = au_br_mnt(br) -+ }; -+ err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp); -+ if (!err) -+ au_set_dbdiropq(dentry, -1); -+ } -+ dput(opq_dentry); -+ opq_dentry = ERR_PTR(err); -+ -+out: -+ return opq_dentry; -+} -+ -+struct do_diropq_args { -+ struct dentry **errp; -+ struct dentry *dentry; -+ aufs_bindex_t bindex; -+ unsigned int flags; -+}; -+ -+static void call_do_diropq(void *args) -+{ -+ struct do_diropq_args *a = args; -+ *a->errp = do_diropq(a->dentry, a->bindex, a->flags); -+} -+ -+struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int flags) -+{ -+ struct dentry *diropq, *h_dentry; -+ -+ h_dentry = au_h_dptr(dentry, bindex); -+ if (!au_test_h_perm_sio(h_dentry->d_inode, MAY_EXEC | MAY_WRITE)) -+ diropq = do_diropq(dentry, bindex, flags); -+ else { -+ int wkq_err; -+ struct do_diropq_args args = { -+ .errp = &diropq, -+ .dentry = dentry, -+ .bindex = bindex, -+ .flags = flags -+ }; -+ -+ wkq_err = au_wkq_wait(call_do_diropq, &args); -+ if (unlikely(wkq_err)) -+ diropq = ERR_PTR(wkq_err); -+ } -+ -+ return diropq; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * lookup whiteout dentry. -+ * @h_parent: lower parent dentry which must exist and be locked -+ * @base_name: name of dentry which will be whiteouted -+ * returns dentry for whiteout. -+ */ -+struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, -+ struct au_branch *br) -+{ -+ int err; -+ struct qstr wh_name; -+ struct dentry *wh_dentry; -+ -+ err = au_wh_name_alloc(&wh_name, base_name); -+ wh_dentry = ERR_PTR(err); -+ if (!err) { -+ wh_dentry = vfsub_lkup_one(&wh_name, h_parent); -+ kfree(wh_name.name); -+ } -+ return wh_dentry; -+} -+ -+/* -+ * link/create a whiteout for @dentry on @bindex. -+ */ -+struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent) -+{ -+ struct dentry *wh_dentry; -+ struct super_block *sb; -+ int err; -+ -+ sb = dentry->d_sb; -+ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); -+ if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) { -+ err = link_or_create_wh(sb, bindex, wh_dentry); -+ if (!err) { -+ au_set_dbwh(dentry, bindex); -+ au_fhsm_wrote(sb, bindex, /*force*/0); -+ } else { -+ dput(wh_dentry); -+ wh_dentry = ERR_PTR(err); -+ } -+ } -+ -+ return wh_dentry; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* Delete all whiteouts in this directory on branch bindex. */ -+static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist, -+ aufs_bindex_t bindex, struct au_branch *br) -+{ -+ int err; -+ unsigned long ul, n; -+ struct qstr wh_name; -+ char *p; -+ struct hlist_head *head; -+ struct au_vdir_wh *pos; -+ struct au_vdir_destr *str; -+ -+ err = -ENOMEM; -+ p = (void *)__get_free_page(GFP_NOFS); -+ wh_name.name = p; -+ if (unlikely(!wh_name.name)) -+ goto out; -+ -+ err = 0; -+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); -+ p += AUFS_WH_PFX_LEN; -+ n = whlist->nh_num; -+ head = whlist->nh_head; -+ for (ul = 0; !err && ul < n; ul++, head++) { -+ hlist_for_each_entry(pos, head, wh_hash) { -+ if (pos->wh_bindex != bindex) -+ continue; -+ -+ str = &pos->wh_str; -+ if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) { -+ memcpy(p, str->name, str->len); -+ wh_name.len = AUFS_WH_PFX_LEN + str->len; -+ err = unlink_wh_name(h_dentry, &wh_name, br); -+ if (!err) -+ continue; -+ break; -+ } -+ AuIOErr("whiteout name too long %.*s\n", -+ str->len, str->name); -+ err = -EIO; -+ break; -+ } -+ } -+ free_page((unsigned long)wh_name.name); -+ -+out: -+ return err; -+} -+ -+struct del_wh_children_args { -+ int *errp; -+ struct dentry *h_dentry; -+ struct au_nhash *whlist; -+ aufs_bindex_t bindex; -+ struct au_branch *br; -+}; -+ -+static void call_del_wh_children(void *args) -+{ -+ struct del_wh_children_args *a = args; -+ *a->errp = del_wh_children(a->h_dentry, a->whlist, a->bindex, a->br); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) -+{ -+ struct au_whtmp_rmdir *whtmp; -+ int err; -+ unsigned int rdhash; -+ -+ SiMustAnyLock(sb); -+ -+ whtmp = kmalloc(sizeof(*whtmp), gfp); -+ if (unlikely(!whtmp)) { -+ whtmp = ERR_PTR(-ENOMEM); -+ goto out; -+ } -+ -+ whtmp->dir = NULL; -+ whtmp->br = NULL; -+ whtmp->wh_dentry = NULL; -+ /* no estimation for dir size */ -+ rdhash = au_sbi(sb)->si_rdhash; -+ if (!rdhash) -+ rdhash = AUFS_RDHASH_DEF; -+ err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp); -+ if (unlikely(err)) { -+ kfree(whtmp); -+ whtmp = ERR_PTR(err); -+ } -+ -+out: -+ return whtmp; -+} -+ -+void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) -+{ -+ if (whtmp->br) -+ atomic_dec(&whtmp->br->br_count); -+ dput(whtmp->wh_dentry); -+ iput(whtmp->dir); -+ au_nhash_wh_free(&whtmp->whlist); -+ kfree(whtmp); -+} -+ -+/* -+ * rmdir the whiteouted temporary named dir @h_dentry. -+ * @whlist: whiteouted children. -+ */ -+int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_nhash *whlist) -+{ -+ int err; -+ unsigned int h_nlink; -+ struct path h_tmp; -+ struct inode *wh_inode, *h_dir; -+ struct au_branch *br; -+ -+ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ -+ IMustLock(h_dir); -+ -+ br = au_sbr(dir->i_sb, bindex); -+ wh_inode = wh_dentry->d_inode; -+ mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD); -+ -+ /* -+ * someone else might change some whiteouts while we were sleeping. -+ * it means this whlist may have an obsoleted entry. -+ */ -+ if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE)) -+ err = del_wh_children(wh_dentry, whlist, bindex, br); -+ else { -+ int wkq_err; -+ struct del_wh_children_args args = { -+ .errp = &err, -+ .h_dentry = wh_dentry, -+ .whlist = whlist, -+ .bindex = bindex, -+ .br = br -+ }; -+ -+ wkq_err = au_wkq_wait(call_del_wh_children, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ mutex_unlock(&wh_inode->i_mutex); -+ -+ if (!err) { -+ h_tmp.dentry = wh_dentry; -+ h_tmp.mnt = au_br_mnt(br); -+ h_nlink = h_dir->i_nlink; -+ err = vfsub_rmdir(h_dir, &h_tmp); -+ /* some fs doesn't change the parent nlink in some cases */ -+ h_nlink -= h_dir->i_nlink; -+ } -+ -+ if (!err) { -+ if (au_ibstart(dir) == bindex) { -+ /* todo: dir->i_mutex is necessary */ -+ au_cpup_attr_timesizes(dir); -+ if (h_nlink) -+ vfsub_drop_nlink(dir); -+ } -+ return 0; /* success */ -+ } -+ -+ pr_warn("failed removing %pd(%d), ignored\n", wh_dentry, err); -+ return err; -+} -+ -+static void call_rmdir_whtmp(void *args) -+{ -+ int err; -+ aufs_bindex_t bindex; -+ struct au_whtmp_rmdir *a = args; -+ struct super_block *sb; -+ struct dentry *h_parent; -+ struct inode *h_dir; -+ struct au_hinode *hdir; -+ -+ /* rmdir by nfsd may cause deadlock with this i_mutex */ -+ /* mutex_lock(&a->dir->i_mutex); */ -+ err = -EROFS; -+ sb = a->dir->i_sb; -+ si_read_lock(sb, !AuLock_FLUSH); -+ if (!au_br_writable(a->br->br_perm)) -+ goto out; -+ bindex = au_br_index(sb, a->br->br_id); -+ if (unlikely(bindex < 0)) -+ goto out; -+ -+ err = -EIO; -+ ii_write_lock_parent(a->dir); -+ h_parent = dget_parent(a->wh_dentry); -+ h_dir = h_parent->d_inode; -+ hdir = au_hi(a->dir, bindex); -+ err = vfsub_mnt_want_write(au_br_mnt(a->br)); -+ if (unlikely(err)) -+ goto out_mnt; -+ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); -+ err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, -+ a->br); -+ if (!err) -+ err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); -+ au_hn_imtx_unlock(hdir); -+ vfsub_mnt_drop_write(au_br_mnt(a->br)); -+ -+out_mnt: -+ dput(h_parent); -+ ii_write_unlock(a->dir); -+out: -+ /* mutex_unlock(&a->dir->i_mutex); */ -+ au_whtmp_rmdir_free(a); -+ si_read_unlock(sb); -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ if (unlikely(err)) -+ AuIOErr("err %d\n", err); -+} -+ -+void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_whtmp_rmdir *args) -+{ -+ int wkq_err; -+ struct super_block *sb; -+ -+ IMustLock(dir); -+ -+ /* all post-process will be done in do_rmdir_whtmp(). */ -+ sb = dir->i_sb; -+ args->dir = au_igrab(dir); -+ args->br = au_sbr(sb, bindex); -+ atomic_inc(&args->br->br_count); -+ args->wh_dentry = dget(wh_dentry); -+ wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb, /*flags*/0); -+ if (unlikely(wkq_err)) { -+ pr_warn("rmdir error %pd (%d), ignored\n", wh_dentry, wkq_err); -+ au_whtmp_rmdir_free(args); -+ } -+} -diff -Nur linux-4.0.4.orig/fs/aufs/whout.h linux-4.0.4/fs/aufs/whout.h ---- linux-4.0.4.orig/fs/aufs/whout.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/whout.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,72 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * whiteout for logical deletion and opaque directory -+ */ -+ -+#ifndef __AUFS_WHOUT_H__ -+#define __AUFS_WHOUT_H__ -+ -+#ifdef __KERNEL__ -+ -+#include "dir.h" -+ -+/* whout.c */ -+int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); -+int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio); -+int au_diropq_test(struct dentry *h_dentry); -+struct au_branch; -+struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, -+ struct qstr *prefix); -+int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); -+int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, -+ struct dentry *dentry); -+int au_wh_init(struct au_branch *br, struct super_block *sb); -+ -+/* diropq flags */ -+#define AuDiropq_CREATE 1 -+#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) -+#define au_fset_diropq(flags, name) \ -+ do { (flags) |= AuDiropq_##name; } while (0) -+#define au_fclr_diropq(flags, name) \ -+ do { (flags) &= ~AuDiropq_##name; } while (0) -+ -+struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, -+ unsigned int flags); -+struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, -+ struct au_branch *br); -+struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, -+ struct dentry *h_parent); -+ -+/* real rmdir for the whiteout-ed dir */ -+struct au_whtmp_rmdir { -+ struct inode *dir; -+ struct au_branch *br; -+ struct dentry *wh_dentry; -+ struct au_nhash whlist; -+}; -+ -+struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp); -+void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp); -+int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_nhash *whlist); -+void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, -+ struct dentry *wh_dentry, struct au_whtmp_rmdir *args); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline struct dentry *au_diropq_create(struct dentry *dentry, -+ aufs_bindex_t bindex) -+{ -+ return au_diropq_sio(dentry, bindex, AuDiropq_CREATE); -+} -+ -+static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex) -+{ -+ return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE)); -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_WHOUT_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/wkq.c linux-4.0.4/fs/aufs/wkq.c ---- linux-4.0.4.orig/fs/aufs/wkq.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/wkq.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,200 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * workqueue for asynchronous/super-io operations -+ * todo: try new dredential scheme -+ */ -+ -+#include -+#include "aufs.h" -+ -+/* internal workqueue named AUFS_WKQ_NAME */ -+ -+static struct workqueue_struct *au_wkq; -+ -+struct au_wkinfo { -+ struct work_struct wk; -+ struct kobject *kobj; -+ -+ unsigned int flags; /* see wkq.h */ -+ -+ au_wkq_func_t func; -+ void *args; -+ -+ struct completion *comp; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void wkq_func(struct work_struct *wk) -+{ -+ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); -+ -+ AuDebugOn(!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)); -+ AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY); -+ -+ wkinfo->func(wkinfo->args); -+ if (au_ftest_wkq(wkinfo->flags, WAIT)) -+ complete(wkinfo->comp); -+ else { -+ kobject_put(wkinfo->kobj); -+ module_put(THIS_MODULE); /* todo: ?? */ -+ kfree(wkinfo); -+ } -+} -+ -+/* -+ * Since struct completion is large, try allocating it dynamically. -+ */ -+#if 1 /* defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) */ -+#define AuWkqCompDeclare(name) struct completion *comp = NULL -+ -+static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) -+{ -+ *comp = kmalloc(sizeof(**comp), GFP_NOFS); -+ if (*comp) { -+ init_completion(*comp); -+ wkinfo->comp = *comp; -+ return 0; -+ } -+ return -ENOMEM; -+} -+ -+static void au_wkq_comp_free(struct completion *comp) -+{ -+ kfree(comp); -+} -+ -+#else -+ -+/* no braces */ -+#define AuWkqCompDeclare(name) \ -+ DECLARE_COMPLETION_ONSTACK(_ ## name); \ -+ struct completion *comp = &_ ## name -+ -+static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) -+{ -+ wkinfo->comp = *comp; -+ return 0; -+} -+ -+static void au_wkq_comp_free(struct completion *comp __maybe_unused) -+{ -+ /* empty */ -+} -+#endif /* 4KSTACKS */ -+ -+static void au_wkq_run(struct au_wkinfo *wkinfo) -+{ -+ if (au_ftest_wkq(wkinfo->flags, NEST)) { -+ if (au_wkq_test()) { -+ AuWarn1("wkq from wkq, unless silly-rename on NFS," -+ " due to a dead dir by UDBA?\n"); -+ AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT)); -+ } -+ } else -+ au_dbg_verify_kthread(); -+ -+ if (au_ftest_wkq(wkinfo->flags, WAIT)) { -+ INIT_WORK_ONSTACK(&wkinfo->wk, wkq_func); -+ queue_work(au_wkq, &wkinfo->wk); -+ } else { -+ INIT_WORK(&wkinfo->wk, wkq_func); -+ schedule_work(&wkinfo->wk); -+ } -+} -+ -+/* -+ * Be careful. It is easy to make deadlock happen. -+ * processA: lock, wkq and wait -+ * processB: wkq and wait, lock in wkq -+ * --> deadlock -+ */ -+int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args) -+{ -+ int err; -+ AuWkqCompDeclare(comp); -+ struct au_wkinfo wkinfo = { -+ .flags = flags, -+ .func = func, -+ .args = args -+ }; -+ -+ err = au_wkq_comp_alloc(&wkinfo, &comp); -+ if (!err) { -+ au_wkq_run(&wkinfo); -+ /* no timeout, no interrupt */ -+ wait_for_completion(wkinfo.comp); -+ au_wkq_comp_free(comp); -+ destroy_work_on_stack(&wkinfo.wk); -+ } -+ -+ return err; -+ -+} -+ -+/* -+ * Note: dget/dput() in func for aufs dentries are not supported. It will be a -+ * problem in a concurrent umounting. -+ */ -+int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, -+ unsigned int flags) -+{ -+ int err; -+ struct au_wkinfo *wkinfo; -+ -+ atomic_inc(&au_sbi(sb)->si_nowait.nw_len); -+ -+ /* -+ * wkq_func() must free this wkinfo. -+ * it highly depends upon the implementation of workqueue. -+ */ -+ err = 0; -+ wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS); -+ if (wkinfo) { -+ wkinfo->kobj = &au_sbi(sb)->si_kobj; -+ wkinfo->flags = flags & ~AuWkq_WAIT; -+ wkinfo->func = func; -+ wkinfo->args = args; -+ wkinfo->comp = NULL; -+ kobject_get(wkinfo->kobj); -+ __module_get(THIS_MODULE); /* todo: ?? */ -+ -+ au_wkq_run(wkinfo); -+ } else { -+ err = -ENOMEM; -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+void au_nwt_init(struct au_nowait_tasks *nwt) -+{ -+ atomic_set(&nwt->nw_len, 0); -+ /* smp_mb(); */ /* atomic_set */ -+ init_waitqueue_head(&nwt->nw_wq); -+} -+ -+void au_wkq_fin(void) -+{ -+ destroy_workqueue(au_wkq); -+} -+ -+int __init au_wkq_init(void) -+{ -+ int err; -+ -+ err = 0; -+ au_wkq = alloc_workqueue(AUFS_WKQ_NAME, 0, WQ_DFL_ACTIVE); -+ if (IS_ERR(au_wkq)) -+ err = PTR_ERR(au_wkq); -+ else if (!au_wkq) -+ err = -ENOMEM; -+ -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/aufs/wkq.h linux-4.0.4/fs/aufs/wkq.h ---- linux-4.0.4.orig/fs/aufs/wkq.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/wkq.h 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,78 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * workqueue for asynchronous/super-io operations -+ * todo: try new credentials management scheme -+ */ -+ -+#ifndef __AUFS_WKQ_H__ -+#define __AUFS_WKQ_H__ -+ -+#ifdef __KERNEL__ -+ -+struct super_block; -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * in the next operation, wait for the 'nowait' tasks in system-wide workqueue -+ */ -+struct au_nowait_tasks { -+ atomic_t nw_len; -+ wait_queue_head_t nw_wq; -+}; -+ -+/* ---------------------------------------------------------------------- */ -+ -+typedef void (*au_wkq_func_t)(void *args); -+ -+/* wkq flags */ -+#define AuWkq_WAIT 1 -+#define AuWkq_NEST (1 << 1) -+#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) -+#define au_fset_wkq(flags, name) \ -+ do { (flags) |= AuWkq_##name; } while (0) -+#define au_fclr_wkq(flags, name) \ -+ do { (flags) &= ~AuWkq_##name; } while (0) -+ -+#ifndef CONFIG_AUFS_HNOTIFY -+#undef AuWkq_NEST -+#define AuWkq_NEST 0 -+#endif -+ -+/* wkq.c */ -+int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args); -+int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, -+ unsigned int flags); -+void au_nwt_init(struct au_nowait_tasks *nwt); -+int __init au_wkq_init(void); -+void au_wkq_fin(void); -+ -+/* ---------------------------------------------------------------------- */ -+ -+static inline int au_wkq_test(void) -+{ -+ return current->flags & PF_WQ_WORKER; -+} -+ -+static inline int au_wkq_wait(au_wkq_func_t func, void *args) -+{ -+ return au_wkq_do_wait(AuWkq_WAIT, func, args); -+} -+ -+static inline void au_nwt_done(struct au_nowait_tasks *nwt) -+{ -+ if (atomic_dec_and_test(&nwt->nw_len)) -+ wake_up_all(&nwt->nw_wq); -+} -+ -+static inline int au_nwt_flush(struct au_nowait_tasks *nwt) -+{ -+ wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); -+ return 0; -+} -+ -+#endif /* __KERNEL__ */ -+#endif /* __AUFS_WKQ_H__ */ -diff -Nur linux-4.0.4.orig/fs/aufs/xattr.c linux-4.0.4/fs/aufs/xattr.c ---- linux-4.0.4.orig/fs/aufs/xattr.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/xattr.c 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,331 @@ -+/* -+ * Copyright (C) 2014-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * handling xattr functions -+ */ -+ -+#include -+#include "aufs.h" -+ -+static int au_xattr_ignore(int err, char *name, unsigned int ignore_flags) -+{ -+ if (!ignore_flags) -+ goto out; -+ switch (err) { -+ case -ENOMEM: -+ case -EDQUOT: -+ goto out; -+ } -+ -+ if ((ignore_flags & AuBrAttr_ICEX) == AuBrAttr_ICEX) { -+ err = 0; -+ goto out; -+ } -+ -+#define cmp(brattr, prefix) do { \ -+ if (!strncmp(name, XATTR_##prefix##_PREFIX, \ -+ XATTR_##prefix##_PREFIX_LEN)) { \ -+ if (ignore_flags & AuBrAttr_ICEX_##brattr) \ -+ err = 0; \ -+ goto out; \ -+ } \ -+ } while (0) -+ -+ cmp(SEC, SECURITY); -+ cmp(SYS, SYSTEM); -+ cmp(TR, TRUSTED); -+ cmp(USR, USER); -+#undef cmp -+ -+ if (ignore_flags & AuBrAttr_ICEX_OTH) -+ err = 0; -+ -+out: -+ return err; -+} -+ -+static const int au_xattr_out_of_list = AuBrAttr_ICEX_OTH << 1; -+ -+static int au_do_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, -+ char *name, char **buf, unsigned int ignore_flags, -+ unsigned int verbose) -+{ -+ int err; -+ ssize_t ssz; -+ struct inode *h_idst; -+ -+ ssz = vfs_getxattr_alloc(h_src, name, buf, 0, GFP_NOFS); -+ err = ssz; -+ if (unlikely(err <= 0)) { -+ if (err == -ENODATA -+ || (err == -EOPNOTSUPP -+ && ((ignore_flags & au_xattr_out_of_list) -+ || (au_test_nfs_noacl(h_src->d_inode) -+ && (!strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) -+ || !strcmp(name, -+ XATTR_NAME_POSIX_ACL_DEFAULT)))) -+ )) -+ err = 0; -+ if (err && (verbose || au_debug_test())) -+ pr_err("%s, err %d\n", name, err); -+ goto out; -+ } -+ -+ /* unlock it temporary */ -+ h_idst = h_dst->d_inode; -+ mutex_unlock(&h_idst->i_mutex); -+ err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0); -+ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); -+ if (unlikely(err)) { -+ if (verbose || au_debug_test()) -+ pr_err("%s, err %d\n", name, err); -+ err = au_xattr_ignore(err, name, ignore_flags); -+ } -+ -+out: -+ return err; -+} -+ -+int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, -+ unsigned int verbose) -+{ -+ int err, unlocked, acl_access, acl_default; -+ ssize_t ssz; -+ struct inode *h_isrc, *h_idst; -+ char *value, *p, *o, *e; -+ -+ /* try stopping to update the source inode while we are referencing */ -+ /* there should not be the parent-child relationship between them */ -+ h_isrc = h_src->d_inode; -+ h_idst = h_dst->d_inode; -+ mutex_unlock(&h_idst->i_mutex); -+ mutex_lock_nested(&h_isrc->i_mutex, AuLsc_I_CHILD); -+ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); -+ unlocked = 0; -+ -+ /* some filesystems don't list POSIX ACL, for example tmpfs */ -+ ssz = vfs_listxattr(h_src, NULL, 0); -+ err = ssz; -+ if (unlikely(err < 0)) { -+ AuTraceErr(err); -+ if (err == -ENODATA -+ || err == -EOPNOTSUPP) -+ err = 0; /* ignore */ -+ goto out; -+ } -+ -+ err = 0; -+ p = NULL; -+ o = NULL; -+ if (ssz) { -+ err = -ENOMEM; -+ p = kmalloc(ssz, GFP_NOFS); -+ o = p; -+ if (unlikely(!p)) -+ goto out; -+ err = vfs_listxattr(h_src, p, ssz); -+ } -+ mutex_unlock(&h_isrc->i_mutex); -+ unlocked = 1; -+ AuDbg("err %d, ssz %zd\n", err, ssz); -+ if (unlikely(err < 0)) -+ goto out_free; -+ -+ err = 0; -+ e = p + ssz; -+ value = NULL; -+ acl_access = 0; -+ acl_default = 0; -+ while (!err && p < e) { -+ acl_access |= !strncmp(p, XATTR_NAME_POSIX_ACL_ACCESS, -+ sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1); -+ acl_default |= !strncmp(p, XATTR_NAME_POSIX_ACL_DEFAULT, -+ sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) -+ - 1); -+ err = au_do_cpup_xattr(h_dst, h_src, p, &value, ignore_flags, -+ verbose); -+ p += strlen(p) + 1; -+ } -+ AuTraceErr(err); -+ ignore_flags |= au_xattr_out_of_list; -+ if (!err && !acl_access) { -+ err = au_do_cpup_xattr(h_dst, h_src, -+ XATTR_NAME_POSIX_ACL_ACCESS, &value, -+ ignore_flags, verbose); -+ AuTraceErr(err); -+ } -+ if (!err && !acl_default) { -+ err = au_do_cpup_xattr(h_dst, h_src, -+ XATTR_NAME_POSIX_ACL_DEFAULT, &value, -+ ignore_flags, verbose); -+ AuTraceErr(err); -+ } -+ -+ kfree(value); -+ -+out_free: -+ kfree(o); -+out: -+ if (!unlocked) -+ mutex_unlock(&h_isrc->i_mutex); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+enum { -+ AU_XATTR_LIST, -+ AU_XATTR_GET -+}; -+ -+struct au_lgxattr { -+ int type; -+ union { -+ struct { -+ char *list; -+ size_t size; -+ } list; -+ struct { -+ const char *name; -+ void *value; -+ size_t size; -+ } get; -+ } u; -+}; -+ -+static ssize_t au_lgxattr(struct dentry *dentry, struct au_lgxattr *arg) -+{ -+ ssize_t err; -+ struct path h_path; -+ struct super_block *sb; -+ -+ sb = dentry->d_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ if (unlikely(err)) -+ goto out; -+ err = au_h_path_getattr(dentry, /*force*/1, &h_path); -+ if (unlikely(err)) -+ goto out_si; -+ if (unlikely(!h_path.dentry)) -+ /* illegally overlapped or something */ -+ goto out_di; /* pretending success */ -+ -+ /* always topmost entry only */ -+ switch (arg->type) { -+ case AU_XATTR_LIST: -+ err = vfs_listxattr(h_path.dentry, -+ arg->u.list.list, arg->u.list.size); -+ break; -+ case AU_XATTR_GET: -+ err = vfs_getxattr(h_path.dentry, -+ arg->u.get.name, arg->u.get.value, -+ arg->u.get.size); -+ break; -+ } -+ -+out_di: -+ di_read_unlock(dentry, AuLock_IR); -+out_si: -+ si_read_unlock(sb); -+out: -+ AuTraceErr(err); -+ return err; -+} -+ -+ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size) -+{ -+ struct au_lgxattr arg = { -+ .type = AU_XATTR_LIST, -+ .u.list = { -+ .list = list, -+ .size = size -+ }, -+ }; -+ -+ return au_lgxattr(dentry, &arg); -+} -+ -+ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, -+ size_t size) -+{ -+ struct au_lgxattr arg = { -+ .type = AU_XATTR_GET, -+ .u.get = { -+ .name = name, -+ .value = value, -+ .size = size -+ }, -+ }; -+ -+ return au_lgxattr(dentry, &arg); -+} -+ -+int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, -+ size_t size, int flags) -+{ -+ struct au_srxattr arg = { -+ .type = AU_XATTR_SET, -+ .u.set = { -+ .name = name, -+ .value = value, -+ .size = size, -+ .flags = flags -+ }, -+ }; -+ -+ return au_srxattr(dentry, &arg); -+} -+ -+int aufs_removexattr(struct dentry *dentry, const char *name) -+{ -+ struct au_srxattr arg = { -+ .type = AU_XATTR_REMOVE, -+ .u.remove = { -+ .name = name -+ }, -+ }; -+ -+ return au_srxattr(dentry, &arg); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+#if 0 -+static size_t au_xattr_list(struct dentry *dentry, char *list, size_t list_size, -+ const char *name, size_t name_len, int type) -+{ -+ return aufs_listxattr(dentry, list, list_size); -+} -+ -+static int au_xattr_get(struct dentry *dentry, const char *name, void *buffer, -+ size_t size, int type) -+{ -+ return aufs_getxattr(dentry, name, buffer, size); -+} -+ -+static int au_xattr_set(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags, int type) -+{ -+ return aufs_setxattr(dentry, name, value, size, flags); -+} -+ -+static const struct xattr_handler au_xattr_handler = { -+ /* no prefix, no flags */ -+ .list = au_xattr_list, -+ .get = au_xattr_get, -+ .set = au_xattr_set -+ /* why no remove? */ -+}; -+ -+static const struct xattr_handler *au_xattr_handlers[] = { -+ &au_xattr_handler -+}; -+ -+void au_xattr_init(struct super_block *sb) -+{ -+ /* sb->s_xattr = au_xattr_handlers; */ -+} -+#endif -diff -Nur linux-4.0.4.orig/fs/aufs/xino.c linux-4.0.4/fs/aufs/xino.c ---- linux-4.0.4.orig/fs/aufs/xino.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/fs/aufs/xino.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,1305 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+/* -+ * external inode number translation table and bitmap -+ */ -+ -+#include -+#include -+#include "aufs.h" -+ -+/* todo: unnecessary to support mmap_sem since kernel-space? */ -+ssize_t xino_fread(au_readf_t func, struct file *file, void *kbuf, size_t size, -+ loff_t *pos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ char __user *u; -+ } buf; -+ -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ do { -+ /* todo: signal_pending? */ -+ err = func(file, buf.u, size, pos); -+ } while (err == -EAGAIN || err == -EINTR); -+ set_fs(oldfs); -+ -+#if 0 /* reserved for future use */ -+ if (err > 0) -+ fsnotify_access(file->f_path.dentry); -+#endif -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static ssize_t do_xino_fwrite(au_writef_t func, struct file *file, void *kbuf, -+ size_t size, loff_t *pos) -+{ -+ ssize_t err; -+ mm_segment_t oldfs; -+ union { -+ void *k; -+ const char __user *u; -+ } buf; -+ -+ buf.k = kbuf; -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ do { -+ /* todo: signal_pending? */ -+ err = func(file, buf.u, size, pos); -+ } while (err == -EAGAIN || err == -EINTR); -+ set_fs(oldfs); -+ -+#if 0 /* reserved for future use */ -+ if (err > 0) -+ fsnotify_modify(file->f_path.dentry); -+#endif -+ -+ return err; -+} -+ -+struct do_xino_fwrite_args { -+ ssize_t *errp; -+ au_writef_t func; -+ struct file *file; -+ void *buf; -+ size_t size; -+ loff_t *pos; -+}; -+ -+static void call_do_xino_fwrite(void *args) -+{ -+ struct do_xino_fwrite_args *a = args; -+ *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); -+} -+ -+ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, -+ loff_t *pos) -+{ -+ ssize_t err; -+ -+ /* todo: signal block and no wkq? */ -+ if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { -+ lockdep_off(); -+ err = do_xino_fwrite(func, file, buf, size, pos); -+ lockdep_on(); -+ } else { -+ /* -+ * it breaks RLIMIT_FSIZE and normal user's limit, -+ * users should care about quota and real 'filesystem full.' -+ */ -+ int wkq_err; -+ struct do_xino_fwrite_args args = { -+ .errp = &err, -+ .func = func, -+ .file = file, -+ .buf = buf, -+ .size = size, -+ .pos = pos -+ }; -+ -+ wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); -+ if (unlikely(wkq_err)) -+ err = wkq_err; -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create a new xinofile at the same place/path as @base_file. -+ */ -+struct file *au_xino_create2(struct file *base_file, struct file *copy_src) -+{ -+ struct file *file; -+ struct dentry *base, *parent; -+ struct inode *dir, *delegated; -+ struct qstr *name; -+ struct path path; -+ int err; -+ -+ base = base_file->f_path.dentry; -+ parent = base->d_parent; /* dir inode is locked */ -+ dir = parent->d_inode; -+ IMustLock(dir); -+ -+ file = ERR_PTR(-EINVAL); -+ name = &base->d_name; -+ path.dentry = vfsub_lookup_one_len(name->name, parent, name->len); -+ if (IS_ERR(path.dentry)) { -+ file = (void *)path.dentry; -+ pr_err("%pd lookup err %ld\n", -+ base, PTR_ERR(path.dentry)); -+ goto out; -+ } -+ -+ /* no need to mnt_want_write() since we call dentry_open() later */ -+ err = vfs_create(dir, path.dentry, S_IRUGO | S_IWUGO, NULL); -+ if (unlikely(err)) { -+ file = ERR_PTR(err); -+ pr_err("%pd create err %d\n", base, err); -+ goto out_dput; -+ } -+ -+ path.mnt = base_file->f_path.mnt; -+ file = vfsub_dentry_open(&path, -+ O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE -+ /* | __FMODE_NONOTIFY */); -+ if (IS_ERR(file)) { -+ pr_err("%pd open err %ld\n", base, PTR_ERR(file)); -+ goto out_dput; -+ } -+ -+ delegated = NULL; -+ err = vfsub_unlink(dir, &file->f_path, &delegated, /*force*/0); -+ if (unlikely(err == -EWOULDBLOCK)) { -+ pr_warn("cannot retry for NFSv4 delegation" -+ " for an internal unlink\n"); -+ iput(delegated); -+ } -+ if (unlikely(err)) { -+ pr_err("%pd unlink err %d\n", base, err); -+ goto out_fput; -+ } -+ -+ if (copy_src) { -+ /* no one can touch copy_src xino */ -+ err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); -+ if (unlikely(err)) { -+ pr_err("%pd copy err %d\n", base, err); -+ goto out_fput; -+ } -+ } -+ goto out_dput; /* success */ -+ -+out_fput: -+ fput(file); -+ file = ERR_PTR(err); -+out_dput: -+ dput(path.dentry); -+out: -+ return file; -+} -+ -+struct au_xino_lock_dir { -+ struct au_hinode *hdir; -+ struct dentry *parent; -+ struct mutex *mtx; -+}; -+ -+static void au_xino_lock_dir(struct super_block *sb, struct file *xino, -+ struct au_xino_lock_dir *ldir) -+{ -+ aufs_bindex_t brid, bindex; -+ -+ ldir->hdir = NULL; -+ bindex = -1; -+ brid = au_xino_brid(sb); -+ if (brid >= 0) -+ bindex = au_br_index(sb, brid); -+ if (bindex >= 0) { -+ ldir->hdir = au_hi(sb->s_root->d_inode, bindex); -+ au_hn_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); -+ } else { -+ ldir->parent = dget_parent(xino->f_path.dentry); -+ ldir->mtx = &ldir->parent->d_inode->i_mutex; -+ mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); -+ } -+} -+ -+static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) -+{ -+ if (ldir->hdir) -+ au_hn_imtx_unlock(ldir->hdir); -+ else { -+ mutex_unlock(ldir->mtx); -+ dput(ldir->parent); -+ } -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* trucate xino files asynchronously */ -+ -+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) -+{ -+ int err; -+ unsigned long jiffy; -+ blkcnt_t blocks; -+ aufs_bindex_t bi, bend; -+ struct kstatfs *st; -+ struct au_branch *br; -+ struct file *new_xino, *file; -+ struct super_block *h_sb; -+ struct au_xino_lock_dir ldir; -+ -+ err = -ENOMEM; -+ st = kzalloc(sizeof(*st), GFP_NOFS); -+ if (unlikely(!st)) -+ goto out; -+ -+ err = -EINVAL; -+ bend = au_sbend(sb); -+ if (unlikely(bindex < 0 || bend < bindex)) -+ goto out_st; -+ br = au_sbr(sb, bindex); -+ file = br->br_xino.xi_file; -+ if (!file) -+ goto out_st; -+ -+ err = vfs_statfs(&file->f_path, st); -+ if (unlikely(err)) -+ AuErr1("statfs err %d, ignored\n", err); -+ jiffy = jiffies; -+ blocks = file_inode(file)->i_blocks; -+ pr_info("begin truncating xino(b%d), ib%llu, %llu/%llu free blks\n", -+ bindex, (u64)blocks, st->f_bfree, st->f_blocks); -+ -+ au_xino_lock_dir(sb, file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ new_xino = au_xino_create2(file, file); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(new_xino); -+ if (IS_ERR(new_xino)) { -+ pr_err("err %d, ignored\n", err); -+ goto out_st; -+ } -+ err = 0; -+ fput(file); -+ br->br_xino.xi_file = new_xino; -+ -+ h_sb = au_br_sb(br); -+ for (bi = 0; bi <= bend; bi++) { -+ if (unlikely(bi == bindex)) -+ continue; -+ br = au_sbr(sb, bi); -+ if (au_br_sb(br) != h_sb) -+ continue; -+ -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = new_xino; -+ get_file(new_xino); -+ } -+ -+ err = vfs_statfs(&new_xino->f_path, st); -+ if (!err) { -+ pr_info("end truncating xino(b%d), ib%llu, %llu/%llu free blks\n", -+ bindex, (u64)file_inode(new_xino)->i_blocks, -+ st->f_bfree, st->f_blocks); -+ if (file_inode(new_xino)->i_blocks < blocks) -+ au_sbi(sb)->si_xino_jiffy = jiffy; -+ } else -+ AuErr1("statfs err %d, ignored\n", err); -+ -+out_st: -+ kfree(st); -+out: -+ return err; -+} -+ -+struct xino_do_trunc_args { -+ struct super_block *sb; -+ struct au_branch *br; -+}; -+ -+static void xino_do_trunc(void *_args) -+{ -+ struct xino_do_trunc_args *args = _args; -+ struct super_block *sb; -+ struct au_branch *br; -+ struct inode *dir; -+ int err; -+ aufs_bindex_t bindex; -+ -+ err = 0; -+ sb = args->sb; -+ dir = sb->s_root->d_inode; -+ br = args->br; -+ -+ si_noflush_write_lock(sb); -+ ii_read_lock_parent(dir); -+ bindex = au_br_index(sb, br->br_id); -+ err = au_xino_trunc(sb, bindex); -+ ii_read_unlock(dir); -+ if (unlikely(err)) -+ pr_warn("err b%d, (%d)\n", bindex, err); -+ atomic_dec(&br->br_xino_running); -+ atomic_dec(&br->br_count); -+ si_write_unlock(sb); -+ au_nwt_done(&au_sbi(sb)->si_nowait); -+ kfree(args); -+} -+ -+static int xino_trunc_test(struct super_block *sb, struct au_branch *br) -+{ -+ int err; -+ struct kstatfs st; -+ struct au_sbinfo *sbinfo; -+ -+ /* todo: si_xino_expire and the ratio should be customizable */ -+ sbinfo = au_sbi(sb); -+ if (time_before(jiffies, -+ sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) -+ return 0; -+ -+ /* truncation border */ -+ err = vfs_statfs(&br->br_xino.xi_file->f_path, &st); -+ if (unlikely(err)) { -+ AuErr1("statfs err %d, ignored\n", err); -+ return 0; -+ } -+ if (div64_u64(st.f_bfree * 100, st.f_blocks) >= AUFS_XINO_DEF_TRUNC) -+ return 0; -+ -+ return 1; -+} -+ -+static void xino_try_trunc(struct super_block *sb, struct au_branch *br) -+{ -+ struct xino_do_trunc_args *args; -+ int wkq_err; -+ -+ if (!xino_trunc_test(sb, br)) -+ return; -+ -+ if (atomic_inc_return(&br->br_xino_running) > 1) -+ goto out; -+ -+ /* lock and kfree() will be called in trunc_xino() */ -+ args = kmalloc(sizeof(*args), GFP_NOFS); -+ if (unlikely(!args)) { -+ AuErr1("no memory\n"); -+ goto out_args; -+ } -+ -+ atomic_inc(&br->br_count); -+ args->sb = sb; -+ args->br = br; -+ wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); -+ if (!wkq_err) -+ return; /* success */ -+ -+ pr_err("wkq %d\n", wkq_err); -+ atomic_dec(&br->br_count); -+ -+out_args: -+ kfree(args); -+out: -+ atomic_dec(&br->br_xino_running); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int au_xino_do_write(au_writef_t write, struct file *file, -+ ino_t h_ino, ino_t ino) -+{ -+ loff_t pos; -+ ssize_t sz; -+ -+ pos = h_ino; -+ if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) { -+ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); -+ return -EFBIG; -+ } -+ pos *= sizeof(ino); -+ sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos); -+ if (sz == sizeof(ino)) -+ return 0; /* success */ -+ -+ AuIOErr("write failed (%zd)\n", sz); -+ return -EIO; -+} -+ -+/* -+ * write @ino to the xinofile for the specified branch{@sb, @bindex} -+ * at the position of @h_ino. -+ * even if @ino is zero, it is written to the xinofile and means no entry. -+ * if the size of the xino file on a specific filesystem exceeds the watermark, -+ * try truncating it. -+ */ -+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t ino) -+{ -+ int err; -+ unsigned int mnt_flags; -+ struct au_branch *br; -+ -+ BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max) -+ || ((loff_t)-1) > 0); -+ SiMustAnyLock(sb); -+ -+ mnt_flags = au_mntflags(sb); -+ if (!au_opt_test(mnt_flags, XINO)) -+ return 0; -+ -+ br = au_sbr(sb, bindex); -+ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, -+ h_ino, ino); -+ if (!err) { -+ if (au_opt_test(mnt_flags, TRUNC_XINO) -+ && au_test_fs_trunc_xino(au_br_sb(br))) -+ xino_try_trunc(sb, br); -+ return 0; /* success */ -+ } -+ -+ AuIOErr("write failed (%d)\n", err); -+ return -EIO; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* aufs inode number bitmap */ -+ -+static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; -+static ino_t xib_calc_ino(unsigned long pindex, int bit) -+{ -+ ino_t ino; -+ -+ AuDebugOn(bit < 0 || page_bits <= bit); -+ ino = AUFS_FIRST_INO + pindex * page_bits + bit; -+ return ino; -+} -+ -+static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) -+{ -+ AuDebugOn(ino < AUFS_FIRST_INO); -+ ino -= AUFS_FIRST_INO; -+ *pindex = ino / page_bits; -+ *bit = ino % page_bits; -+} -+ -+static int xib_pindex(struct super_block *sb, unsigned long pindex) -+{ -+ int err; -+ loff_t pos; -+ ssize_t sz; -+ struct au_sbinfo *sbinfo; -+ struct file *xib; -+ unsigned long *p; -+ -+ sbinfo = au_sbi(sb); -+ MtxMustLock(&sbinfo->si_xib_mtx); -+ AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE -+ || !au_opt_test(sbinfo->si_mntflags, XINO)); -+ -+ if (pindex == sbinfo->si_xib_last_pindex) -+ return 0; -+ -+ xib = sbinfo->si_xib; -+ p = sbinfo->si_xib_buf; -+ pos = sbinfo->si_xib_last_pindex; -+ pos *= PAGE_SIZE; -+ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); -+ if (unlikely(sz != PAGE_SIZE)) -+ goto out; -+ -+ pos = pindex; -+ pos *= PAGE_SIZE; -+ if (vfsub_f_size_read(xib) >= pos + PAGE_SIZE) -+ sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); -+ else { -+ memset(p, 0, PAGE_SIZE); -+ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); -+ } -+ if (sz == PAGE_SIZE) { -+ sbinfo->si_xib_last_pindex = pindex; -+ return 0; /* success */ -+ } -+ -+out: -+ AuIOErr1("write failed (%zd)\n", sz); -+ err = sz; -+ if (sz >= 0) -+ err = -EIO; -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static void au_xib_clear_bit(struct inode *inode) -+{ -+ int err, bit; -+ unsigned long pindex; -+ struct super_block *sb; -+ struct au_sbinfo *sbinfo; -+ -+ AuDebugOn(inode->i_nlink); -+ -+ sb = inode->i_sb; -+ xib_calc_bit(inode->i_ino, &pindex, &bit); -+ AuDebugOn(page_bits <= bit); -+ sbinfo = au_sbi(sb); -+ mutex_lock(&sbinfo->si_xib_mtx); -+ err = xib_pindex(sb, pindex); -+ if (!err) { -+ clear_bit(bit, sbinfo->si_xib_buf); -+ sbinfo->si_xib_next_bit = bit; -+ } -+ mutex_unlock(&sbinfo->si_xib_mtx); -+} -+ -+/* for s_op->delete_inode() */ -+void au_xino_delete_inode(struct inode *inode, const int unlinked) -+{ -+ int err; -+ unsigned int mnt_flags; -+ aufs_bindex_t bindex, bend, bi; -+ unsigned char try_trunc; -+ struct au_iinfo *iinfo; -+ struct super_block *sb; -+ struct au_hinode *hi; -+ struct inode *h_inode; -+ struct au_branch *br; -+ au_writef_t xwrite; -+ -+ sb = inode->i_sb; -+ mnt_flags = au_mntflags(sb); -+ if (!au_opt_test(mnt_flags, XINO) -+ || inode->i_ino == AUFS_ROOT_INO) -+ return; -+ -+ if (unlinked) { -+ au_xigen_inc(inode); -+ au_xib_clear_bit(inode); -+ } -+ -+ iinfo = au_ii(inode); -+ if (!iinfo) -+ return; -+ -+ bindex = iinfo->ii_bstart; -+ if (bindex < 0) -+ return; -+ -+ xwrite = au_sbi(sb)->si_xwrite; -+ try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); -+ hi = iinfo->ii_hinode + bindex; -+ bend = iinfo->ii_bend; -+ for (; bindex <= bend; bindex++, hi++) { -+ h_inode = hi->hi_inode; -+ if (!h_inode -+ || (!unlinked && h_inode->i_nlink)) -+ continue; -+ -+ /* inode may not be revalidated */ -+ bi = au_br_index(sb, hi->hi_id); -+ if (bi < 0) -+ continue; -+ -+ br = au_sbr(sb, bi); -+ err = au_xino_do_write(xwrite, br->br_xino.xi_file, -+ h_inode->i_ino, /*ino*/0); -+ if (!err && try_trunc -+ && au_test_fs_trunc_xino(au_br_sb(br))) -+ xino_try_trunc(sb, br); -+ } -+} -+ -+/* get an unused inode number from bitmap */ -+ino_t au_xino_new_ino(struct super_block *sb) -+{ -+ ino_t ino; -+ unsigned long *p, pindex, ul, pend; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ int free_bit, err; -+ -+ if (!au_opt_test(au_mntflags(sb), XINO)) -+ return iunique(sb, AUFS_FIRST_INO); -+ -+ sbinfo = au_sbi(sb); -+ mutex_lock(&sbinfo->si_xib_mtx); -+ p = sbinfo->si_xib_buf; -+ free_bit = sbinfo->si_xib_next_bit; -+ if (free_bit < page_bits && !test_bit(free_bit, p)) -+ goto out; /* success */ -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ -+ pindex = sbinfo->si_xib_last_pindex; -+ for (ul = pindex - 1; ul < ULONG_MAX; ul--) { -+ err = xib_pindex(sb, ul); -+ if (unlikely(err)) -+ goto out_err; -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ } -+ -+ file = sbinfo->si_xib; -+ pend = vfsub_f_size_read(file) / PAGE_SIZE; -+ for (ul = pindex + 1; ul <= pend; ul++) { -+ err = xib_pindex(sb, ul); -+ if (unlikely(err)) -+ goto out_err; -+ free_bit = find_first_zero_bit(p, page_bits); -+ if (free_bit < page_bits) -+ goto out; /* success */ -+ } -+ BUG(); -+ -+out: -+ set_bit(free_bit, p); -+ sbinfo->si_xib_next_bit = free_bit + 1; -+ pindex = sbinfo->si_xib_last_pindex; -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ ino = xib_calc_ino(pindex, free_bit); -+ AuDbg("i%lu\n", (unsigned long)ino); -+ return ino; -+out_err: -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ AuDbg("i0\n"); -+ return 0; -+} -+ -+/* -+ * read @ino from xinofile for the specified branch{@sb, @bindex} -+ * at the position of @h_ino. -+ * if @ino does not exist and @do_new is true, get new one. -+ */ -+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, -+ ino_t *ino) -+{ -+ int err; -+ ssize_t sz; -+ loff_t pos; -+ struct file *file; -+ struct au_sbinfo *sbinfo; -+ -+ *ino = 0; -+ if (!au_opt_test(au_mntflags(sb), XINO)) -+ return 0; /* no xino */ -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ pos = h_ino; -+ if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) { -+ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); -+ return -EFBIG; -+ } -+ pos *= sizeof(*ino); -+ -+ file = au_sbr(sb, bindex)->br_xino.xi_file; -+ if (vfsub_f_size_read(file) < pos + sizeof(*ino)) -+ return 0; /* no ino */ -+ -+ sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); -+ if (sz == sizeof(*ino)) -+ return 0; /* success */ -+ -+ err = sz; -+ if (unlikely(sz >= 0)) { -+ err = -EIO; -+ AuIOErr("xino read error (%zd)\n", sz); -+ } -+ -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* create and set a new xino file */ -+ -+struct file *au_xino_create(struct super_block *sb, char *fname, int silent) -+{ -+ struct file *file; -+ struct dentry *h_parent, *d; -+ struct inode *h_dir; -+ int err; -+ -+ /* -+ * at mount-time, and the xino file is the default path, -+ * hnotify is disabled so we have no notify events to ignore. -+ * when a user specified the xino, we cannot get au_hdir to be ignored. -+ */ -+ file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE -+ /* | __FMODE_NONOTIFY */, -+ S_IRUGO | S_IWUGO); -+ if (IS_ERR(file)) { -+ if (!silent) -+ pr_err("open %s(%ld)\n", fname, PTR_ERR(file)); -+ return file; -+ } -+ -+ /* keep file count */ -+ h_parent = dget_parent(file->f_path.dentry); -+ h_dir = h_parent->d_inode; -+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); -+ /* mnt_want_write() is unnecessary here */ -+ /* no delegation since it is just created */ -+ err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, /*force*/0); -+ mutex_unlock(&h_dir->i_mutex); -+ dput(h_parent); -+ if (unlikely(err)) { -+ if (!silent) -+ pr_err("unlink %s(%d)\n", fname, err); -+ goto out; -+ } -+ -+ err = -EINVAL; -+ d = file->f_path.dentry; -+ if (unlikely(sb == d->d_sb)) { -+ if (!silent) -+ pr_err("%s must be outside\n", fname); -+ goto out; -+ } -+ if (unlikely(au_test_fs_bad_xino(d->d_sb))) { -+ if (!silent) -+ pr_err("xino doesn't support %s(%s)\n", -+ fname, au_sbtype(d->d_sb)); -+ goto out; -+ } -+ return file; /* success */ -+ -+out: -+ fput(file); -+ file = ERR_PTR(err); -+ return file; -+} -+ -+/* -+ * find another branch who is on the same filesystem of the specified -+ * branch{@btgt}. search until @bend. -+ */ -+static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, -+ aufs_bindex_t bend) -+{ -+ aufs_bindex_t bindex; -+ struct super_block *tgt_sb = au_sbr_sb(sb, btgt); -+ -+ for (bindex = 0; bindex < btgt; bindex++) -+ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) -+ return bindex; -+ for (bindex++; bindex <= bend; bindex++) -+ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) -+ return bindex; -+ return -1; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * initialize the xinofile for the specified branch @br -+ * at the place/path where @base_file indicates. -+ * test whether another branch is on the same filesystem or not, -+ * if @do_test is true. -+ */ -+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, -+ struct file *base_file, int do_test) -+{ -+ int err; -+ ino_t ino; -+ aufs_bindex_t bend, bindex; -+ struct au_branch *shared_br, *b; -+ struct file *file; -+ struct super_block *tgt_sb; -+ -+ shared_br = NULL; -+ bend = au_sbend(sb); -+ if (do_test) { -+ tgt_sb = au_br_sb(br); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ b = au_sbr(sb, bindex); -+ if (tgt_sb == au_br_sb(b)) { -+ shared_br = b; -+ break; -+ } -+ } -+ } -+ -+ if (!shared_br || !shared_br->br_xino.xi_file) { -+ struct au_xino_lock_dir ldir; -+ -+ au_xino_lock_dir(sb, base_file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ file = au_xino_create2(base_file, NULL); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ br->br_xino.xi_file = file; -+ } else { -+ br->br_xino.xi_file = shared_br->br_xino.xi_file; -+ get_file(br->br_xino.xi_file); -+ } -+ -+ ino = AUFS_ROOT_INO; -+ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, -+ h_ino, ino); -+ if (unlikely(err)) { -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = NULL; -+ } -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* trucate a xino bitmap file */ -+ -+/* todo: slow */ -+static int do_xib_restore(struct super_block *sb, struct file *file, void *page) -+{ -+ int err, bit; -+ ssize_t sz; -+ unsigned long pindex; -+ loff_t pos, pend; -+ struct au_sbinfo *sbinfo; -+ au_readf_t func; -+ ino_t *ino; -+ unsigned long *p; -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ MtxMustLock(&sbinfo->si_xib_mtx); -+ p = sbinfo->si_xib_buf; -+ func = sbinfo->si_xread; -+ pend = vfsub_f_size_read(file); -+ pos = 0; -+ while (pos < pend) { -+ sz = xino_fread(func, file, page, PAGE_SIZE, &pos); -+ err = sz; -+ if (unlikely(sz <= 0)) -+ goto out; -+ -+ err = 0; -+ for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { -+ if (unlikely(*ino < AUFS_FIRST_INO)) -+ continue; -+ -+ xib_calc_bit(*ino, &pindex, &bit); -+ AuDebugOn(page_bits <= bit); -+ err = xib_pindex(sb, pindex); -+ if (!err) -+ set_bit(bit, p); -+ else -+ goto out; -+ } -+ } -+ -+out: -+ return err; -+} -+ -+static int xib_restore(struct super_block *sb) -+{ -+ int err; -+ aufs_bindex_t bindex, bend; -+ void *page; -+ -+ err = -ENOMEM; -+ page = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!page)) -+ goto out; -+ -+ err = 0; -+ bend = au_sbend(sb); -+ for (bindex = 0; !err && bindex <= bend; bindex++) -+ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) -+ err = do_xib_restore -+ (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); -+ else -+ AuDbg("b%d\n", bindex); -+ free_page((unsigned long)page); -+ -+out: -+ return err; -+} -+ -+int au_xib_trunc(struct super_block *sb) -+{ -+ int err; -+ ssize_t sz; -+ loff_t pos; -+ struct au_xino_lock_dir ldir; -+ struct au_sbinfo *sbinfo; -+ unsigned long *p; -+ struct file *file; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ if (!au_opt_test(sbinfo->si_mntflags, XINO)) -+ goto out; -+ -+ file = sbinfo->si_xib; -+ if (vfsub_f_size_read(file) <= PAGE_SIZE) -+ goto out; -+ -+ au_xino_lock_dir(sb, file, &ldir); -+ /* mnt_want_write() is unnecessary here */ -+ file = au_xino_create2(sbinfo->si_xib, NULL); -+ au_xino_unlock_dir(&ldir); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = file; -+ -+ p = sbinfo->si_xib_buf; -+ memset(p, 0, PAGE_SIZE); -+ pos = 0; -+ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos); -+ if (unlikely(sz != PAGE_SIZE)) { -+ err = sz; -+ AuIOErr("err %d\n", err); -+ if (sz >= 0) -+ err = -EIO; -+ goto out; -+ } -+ -+ mutex_lock(&sbinfo->si_xib_mtx); -+ /* mnt_want_write() is unnecessary here */ -+ err = xib_restore(sb); -+ mutex_unlock(&sbinfo->si_xib_mtx); -+ -+out: -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * xino mount option handlers -+ */ -+static au_readf_t find_readf(struct file *h_file) -+{ -+ const struct file_operations *fop = h_file->f_op; -+ -+ if (fop->read) -+ return fop->read; -+ if (fop->aio_read) -+ return do_sync_read; -+ if (fop->read_iter) -+ return new_sync_read; -+ return ERR_PTR(-ENOSYS); -+} -+ -+static au_writef_t find_writef(struct file *h_file) -+{ -+ const struct file_operations *fop = h_file->f_op; -+ -+ if (fop->write) -+ return fop->write; -+ if (fop->aio_write) -+ return do_sync_write; -+ if (fop->write_iter) -+ return new_sync_write; -+ return ERR_PTR(-ENOSYS); -+} -+ -+/* xino bitmap */ -+static void xino_clear_xib(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ sbinfo->si_xread = NULL; -+ sbinfo->si_xwrite = NULL; -+ if (sbinfo->si_xib) -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = NULL; -+ free_page((unsigned long)sbinfo->si_xib_buf); -+ sbinfo->si_xib_buf = NULL; -+} -+ -+static int au_xino_set_xib(struct super_block *sb, struct file *base) -+{ -+ int err; -+ loff_t pos; -+ struct au_sbinfo *sbinfo; -+ struct file *file; -+ -+ SiMustWriteLock(sb); -+ -+ sbinfo = au_sbi(sb); -+ file = au_xino_create2(base, sbinfo->si_xib); -+ err = PTR_ERR(file); -+ if (IS_ERR(file)) -+ goto out; -+ if (sbinfo->si_xib) -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = file; -+ sbinfo->si_xread = find_readf(file); -+ sbinfo->si_xwrite = find_writef(file); -+ -+ err = -ENOMEM; -+ if (!sbinfo->si_xib_buf) -+ sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); -+ if (unlikely(!sbinfo->si_xib_buf)) -+ goto out_unset; -+ -+ sbinfo->si_xib_last_pindex = 0; -+ sbinfo->si_xib_next_bit = 0; -+ if (vfsub_f_size_read(file) < PAGE_SIZE) { -+ pos = 0; -+ err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf, -+ PAGE_SIZE, &pos); -+ if (unlikely(err != PAGE_SIZE)) -+ goto out_free; -+ } -+ err = 0; -+ goto out; /* success */ -+ -+out_free: -+ free_page((unsigned long)sbinfo->si_xib_buf); -+ sbinfo->si_xib_buf = NULL; -+ if (err >= 0) -+ err = -EIO; -+out_unset: -+ fput(sbinfo->si_xib); -+ sbinfo->si_xib = NULL; -+ sbinfo->si_xread = NULL; -+ sbinfo->si_xwrite = NULL; -+out: -+ return err; -+} -+ -+/* xino for each branch */ -+static void xino_clear_br(struct super_block *sb) -+{ -+ aufs_bindex_t bindex, bend; -+ struct au_branch *br; -+ -+ bend = au_sbend(sb); -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (!br || !br->br_xino.xi_file) -+ continue; -+ -+ fput(br->br_xino.xi_file); -+ br->br_xino.xi_file = NULL; -+ } -+} -+ -+static int au_xino_set_br(struct super_block *sb, struct file *base) -+{ -+ int err; -+ ino_t ino; -+ aufs_bindex_t bindex, bend, bshared; -+ struct { -+ struct file *old, *new; -+ } *fpair, *p; -+ struct au_branch *br; -+ struct inode *inode; -+ au_writef_t writef; -+ -+ SiMustWriteLock(sb); -+ -+ err = -ENOMEM; -+ bend = au_sbend(sb); -+ fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); -+ if (unlikely(!fpair)) -+ goto out; -+ -+ inode = sb->s_root->d_inode; -+ ino = AUFS_ROOT_INO; -+ writef = au_sbi(sb)->si_xwrite; -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { -+ br = au_sbr(sb, bindex); -+ bshared = is_sb_shared(sb, bindex, bindex - 1); -+ if (bshared >= 0) { -+ /* shared xino */ -+ *p = fpair[bshared]; -+ get_file(p->new); -+ } -+ -+ if (!p->new) { -+ /* new xino */ -+ p->old = br->br_xino.xi_file; -+ p->new = au_xino_create2(base, br->br_xino.xi_file); -+ err = PTR_ERR(p->new); -+ if (IS_ERR(p->new)) { -+ p->new = NULL; -+ goto out_pair; -+ } -+ } -+ -+ err = au_xino_do_write(writef, p->new, -+ au_h_iptr(inode, bindex)->i_ino, ino); -+ if (unlikely(err)) -+ goto out_pair; -+ } -+ -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { -+ br = au_sbr(sb, bindex); -+ if (br->br_xino.xi_file) -+ fput(br->br_xino.xi_file); -+ get_file(p->new); -+ br->br_xino.xi_file = p->new; -+ } -+ -+out_pair: -+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) -+ if (p->new) -+ fput(p->new); -+ else -+ break; -+ kfree(fpair); -+out: -+ return err; -+} -+ -+void au_xino_clr(struct super_block *sb) -+{ -+ struct au_sbinfo *sbinfo; -+ -+ au_xigen_clr(sb); -+ xino_clear_xib(sb); -+ xino_clear_br(sb); -+ sbinfo = au_sbi(sb); -+ /* lvalue, do not call au_mntflags() */ -+ au_opt_clr(sbinfo->si_mntflags, XINO); -+} -+ -+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) -+{ -+ int err, skip; -+ struct dentry *parent, *cur_parent; -+ struct qstr *dname, *cur_name; -+ struct file *cur_xino; -+ struct inode *dir; -+ struct au_sbinfo *sbinfo; -+ -+ SiMustWriteLock(sb); -+ -+ err = 0; -+ sbinfo = au_sbi(sb); -+ parent = dget_parent(xino->file->f_path.dentry); -+ if (remount) { -+ skip = 0; -+ dname = &xino->file->f_path.dentry->d_name; -+ cur_xino = sbinfo->si_xib; -+ if (cur_xino) { -+ cur_parent = dget_parent(cur_xino->f_path.dentry); -+ cur_name = &cur_xino->f_path.dentry->d_name; -+ skip = (cur_parent == parent -+ && au_qstreq(dname, cur_name)); -+ dput(cur_parent); -+ } -+ if (skip) -+ goto out; -+ } -+ -+ au_opt_set(sbinfo->si_mntflags, XINO); -+ dir = parent->d_inode; -+ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); -+ /* mnt_want_write() is unnecessary here */ -+ err = au_xino_set_xib(sb, xino->file); -+ if (!err) -+ err = au_xigen_set(sb, xino->file); -+ if (!err) -+ err = au_xino_set_br(sb, xino->file); -+ mutex_unlock(&dir->i_mutex); -+ if (!err) -+ goto out; /* success */ -+ -+ /* reset all */ -+ AuIOErr("failed creating xino(%d).\n", err); -+ au_xigen_clr(sb); -+ xino_clear_xib(sb); -+ -+out: -+ dput(parent); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * create a xinofile at the default place/path. -+ */ -+struct file *au_xino_def(struct super_block *sb) -+{ -+ struct file *file; -+ char *page, *p; -+ struct au_branch *br; -+ struct super_block *h_sb; -+ struct path path; -+ aufs_bindex_t bend, bindex, bwr; -+ -+ br = NULL; -+ bend = au_sbend(sb); -+ bwr = -1; -+ for (bindex = 0; bindex <= bend; bindex++) { -+ br = au_sbr(sb, bindex); -+ if (au_br_writable(br->br_perm) -+ && !au_test_fs_bad_xino(au_br_sb(br))) { -+ bwr = bindex; -+ break; -+ } -+ } -+ -+ if (bwr >= 0) { -+ file = ERR_PTR(-ENOMEM); -+ page = (void *)__get_free_page(GFP_NOFS); -+ if (unlikely(!page)) -+ goto out; -+ path.mnt = au_br_mnt(br); -+ path.dentry = au_h_dptr(sb->s_root, bwr); -+ p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); -+ file = (void *)p; -+ if (!IS_ERR(p)) { -+ strcat(p, "/" AUFS_XINO_FNAME); -+ AuDbg("%s\n", p); -+ file = au_xino_create(sb, p, /*silent*/0); -+ if (!IS_ERR(file)) -+ au_xino_brid_set(sb, br->br_id); -+ } -+ free_page((unsigned long)page); -+ } else { -+ file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0); -+ if (IS_ERR(file)) -+ goto out; -+ h_sb = file->f_path.dentry->d_sb; -+ if (unlikely(au_test_fs_bad_xino(h_sb))) { -+ pr_err("xino doesn't support %s(%s)\n", -+ AUFS_XINO_DEFPATH, au_sbtype(h_sb)); -+ fput(file); -+ file = ERR_PTR(-EINVAL); -+ } -+ if (!IS_ERR(file)) -+ au_xino_brid_set(sb, -1); -+ } -+ -+out: -+ return file; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+int au_xino_path(struct seq_file *seq, struct file *file) -+{ -+ int err; -+ -+ err = au_seq_path(seq, &file->f_path); -+ if (unlikely(err < 0)) -+ goto out; -+ -+ err = 0; -+#define Deleted "\\040(deleted)" -+ seq->count -= sizeof(Deleted) - 1; -+ AuDebugOn(memcmp(seq->buf + seq->count, Deleted, -+ sizeof(Deleted) - 1)); -+#undef Deleted -+ -+out: -+ return err; -+} -diff -Nur linux-4.0.4.orig/fs/buffer.c linux-4.0.4/fs/buffer.c ---- linux-4.0.4.orig/fs/buffer.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/fs/buffer.c 2015-05-30 22:11:30.000000000 +0200 -@@ -2450,7 +2450,7 @@ - * Update file times before taking page lock. We may end up failing the - * fault so this update may be superfluous but who really cares... - */ -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - - ret = __block_page_mkwrite(vma, vmf, get_block); - sb_end_pagefault(sb); -diff -Nur linux-4.0.4.orig/fs/dcache.c linux-4.0.4/fs/dcache.c ---- linux-4.0.4.orig/fs/dcache.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/fs/dcache.c 2015-05-30 22:11:30.000000000 +0200 -@@ -1130,7 +1130,7 @@ - * - * The @enter() and @finish() callbacks are called with d_lock held. - */ --static void d_walk(struct dentry *parent, void *data, -+void d_walk(struct dentry *parent, void *data, - enum d_walk_ret (*enter)(void *, struct dentry *), - void (*finish)(void *)) - { -diff -Nur linux-4.0.4.orig/fs/Kconfig linux-4.0.4/fs/Kconfig ---- linux-4.0.4.orig/fs/Kconfig 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/fs/Kconfig 2015-05-30 22:11:31.000000000 +0200 -@@ -218,6 +218,7 @@ - source "fs/ufs/Kconfig" - source "fs/exofs/Kconfig" - source "fs/f2fs/Kconfig" -+source "fs/aufs/Kconfig" - - endif # MISC_FILESYSTEMS - -diff -Nur linux-4.0.4.orig/fs/Makefile linux-4.0.4/fs/Makefile ---- linux-4.0.4.orig/fs/Makefile 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/fs/Makefile 2015-05-30 22:11:31.000000000 +0200 -@@ -126,3 +126,4 @@ - obj-$(CONFIG_CEPH_FS) += ceph/ - obj-$(CONFIG_PSTORE) += pstore/ - obj-$(CONFIG_EFIVAR_FS) += efivarfs/ -+obj-$(CONFIG_AUFS_FS) += aufs/ -diff -Nur linux-4.0.4.orig/fs/proc/base.c linux-4.0.4/fs/proc/base.c ---- linux-4.0.4.orig/fs/proc/base.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/fs/proc/base.c 2015-05-30 22:11:30.000000000 +0200 -@@ -1735,7 +1735,7 @@ - down_read(&mm->mmap_sem); - vma = find_exact_vma(mm, vm_start, vm_end); - if (vma && vma->vm_file) { -- *path = vma->vm_file->f_path; -+ *path = vma_pr_or_file(vma)->f_path; - path_get(path); - rc = 0; - } -diff -Nur linux-4.0.4.orig/fs/proc/nommu.c linux-4.0.4/fs/proc/nommu.c ---- linux-4.0.4.orig/fs/proc/nommu.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/fs/proc/nommu.c 2015-05-30 22:11:30.000000000 +0200 -@@ -45,7 +45,10 @@ - file = region->vm_file; - - if (file) { -- struct inode *inode = file_inode(region->vm_file); -+ struct inode *inode; -+ -+ file = vmr_pr_or_file(region); -+ inode = file_inode(file); - dev = inode->i_sb->s_dev; - ino = inode->i_ino; - } -diff -Nur linux-4.0.4.orig/fs/proc/task_mmu.c linux-4.0.4/fs/proc/task_mmu.c ---- linux-4.0.4.orig/fs/proc/task_mmu.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/fs/proc/task_mmu.c 2015-05-30 22:11:30.000000000 +0200 -@@ -279,7 +279,10 @@ - const char *name = NULL; - - if (file) { -- struct inode *inode = file_inode(vma->vm_file); -+ struct inode *inode; -+ -+ file = vma_pr_or_file(vma); -+ inode = file_inode(file); - dev = inode->i_sb->s_dev; - ino = inode->i_ino; - pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; -@@ -1479,7 +1482,7 @@ - struct proc_maps_private *proc_priv = &numa_priv->proc_maps; - struct vm_area_struct *vma = v; - struct numa_maps *md = &numa_priv->md; -- struct file *file = vma->vm_file; -+ struct file *file = vma_pr_or_file(vma); - struct mm_struct *mm = vma->vm_mm; - struct mm_walk walk = { - .hugetlb_entry = gather_hugetlb_stats, -diff -Nur linux-4.0.4.orig/fs/proc/task_nommu.c linux-4.0.4/fs/proc/task_nommu.c ---- linux-4.0.4.orig/fs/proc/task_nommu.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/fs/proc/task_nommu.c 2015-05-30 22:11:30.000000000 +0200 -@@ -160,7 +160,10 @@ - file = vma->vm_file; - - if (file) { -- struct inode *inode = file_inode(vma->vm_file); -+ struct inode *inode; -+ -+ file = vma_pr_or_file(file); -+ inode = file_inode(file); - dev = inode->i_sb->s_dev; - ino = inode->i_ino; - pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; -diff -Nur linux-4.0.4.orig/fs/splice.c linux-4.0.4/fs/splice.c ---- linux-4.0.4.orig/fs/splice.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/fs/splice.c 2015-05-30 22:11:30.000000000 +0200 -@@ -1099,8 +1099,8 @@ - /* - * Attempt to initiate a splice from pipe to file. - */ --static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -- loff_t *ppos, size_t len, unsigned int flags) -+long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags) - { - ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, - loff_t *, size_t, unsigned int); -@@ -1116,9 +1116,9 @@ - /* - * Attempt to initiate a splice from a file to a pipe. - */ --static long do_splice_to(struct file *in, loff_t *ppos, -- struct pipe_inode_info *pipe, size_t len, -- unsigned int flags) -+long do_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags) - { - ssize_t (*splice_read)(struct file *, loff_t *, - struct pipe_inode_info *, size_t, unsigned int); -diff -Nur linux-4.0.4.orig/include/linux/file.h linux-4.0.4/include/linux/file.h ---- linux-4.0.4.orig/include/linux/file.h 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/include/linux/file.h 2015-05-30 22:11:31.000000000 +0200 -@@ -19,6 +19,7 @@ - struct path; - extern struct file *alloc_file(struct path *, fmode_t mode, - const struct file_operations *fop); -+extern struct file *get_empty_filp(void); - - static inline void fput_light(struct file *file, int fput_needed) - { -diff -Nur linux-4.0.4.orig/include/linux/mm.h linux-4.0.4/include/linux/mm.h ---- linux-4.0.4.orig/include/linux/mm.h 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/include/linux/mm.h 2015-05-30 22:11:30.000000000 +0200 -@@ -1254,6 +1254,28 @@ - } - #endif - -+#ifdef CONFIG_MMU -+extern void vma_do_file_update_time(struct vm_area_struct *, const char[], int); -+extern struct file *vma_do_pr_or_file(struct vm_area_struct *, const char[], -+ int); -+extern void vma_do_get_file(struct vm_area_struct *, const char[], int); -+extern void vma_do_fput(struct vm_area_struct *, const char[], int); -+ -+#define vma_file_update_time(vma) vma_do_file_update_time(vma, __func__, \ -+ __LINE__) -+#define vma_pr_or_file(vma) vma_do_pr_or_file(vma, __func__, \ -+ __LINE__) -+#define vma_get_file(vma) vma_do_get_file(vma, __func__, __LINE__) -+#define vma_fput(vma) vma_do_fput(vma, __func__, __LINE__) -+#else -+extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int); -+extern void vmr_do_fput(struct vm_region *, const char[], int); -+ -+#define vmr_pr_or_file(region) vmr_do_pr_or_file(region, __func__, \ -+ __LINE__) -+#define vmr_fput(region) vmr_do_fput(region, __func__, __LINE__) -+#endif /* CONFIG_MMU */ -+ - extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); - extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, - void *buf, int len, int write); -diff -Nur linux-4.0.4.orig/include/linux/mm_types.h linux-4.0.4/include/linux/mm_types.h ---- linux-4.0.4.orig/include/linux/mm_types.h 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/include/linux/mm_types.h 2015-05-30 22:11:30.000000000 +0200 -@@ -241,6 +241,7 @@ - unsigned long vm_top; /* region allocated to here */ - unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ - struct file *vm_file; /* the backing file or NULL */ -+ struct file *vm_prfile; /* the virtual backing file or NULL */ - - int vm_usage; /* region usage count (access under nommu_region_sem) */ - bool vm_icache_flushed : 1; /* true if the icache has been flushed for -@@ -305,6 +306,7 @@ - unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE - units, *not* PAGE_CACHE_SIZE */ - struct file * vm_file; /* File we map to (can be NULL). */ -+ struct file *vm_prfile; /* shadow of vm_file */ - void * vm_private_data; /* was vm_pte (shared mem) */ - - #ifndef CONFIG_MMU -diff -Nur linux-4.0.4.orig/include/linux/splice.h linux-4.0.4/include/linux/splice.h ---- linux-4.0.4.orig/include/linux/splice.h 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/include/linux/splice.h 2015-05-30 22:11:30.000000000 +0200 -@@ -83,4 +83,10 @@ - extern void spd_release_page(struct splice_pipe_desc *, unsigned int); - - extern const struct pipe_buf_operations page_cache_pipe_buf_ops; -+ -+extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, -+ loff_t *ppos, size_t len, unsigned int flags); -+extern long do_splice_to(struct file *in, loff_t *ppos, -+ struct pipe_inode_info *pipe, size_t len, -+ unsigned int flags); - #endif -diff -Nur linux-4.0.4.orig/include/uapi/linux/aufs_type.h linux-4.0.4/include/uapi/linux/aufs_type.h ---- linux-4.0.4.orig/include/uapi/linux/aufs_type.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/include/uapi/linux/aufs_type.h 2015-05-30 22:11:31.000000000 +0200 -@@ -0,0 +1,406 @@ -+/* -+ * Copyright (C) 2005-2015 Junjiro R. Okajima -+ */ -+ -+#ifndef __AUFS_TYPE_H__ -+#define __AUFS_TYPE_H__ -+ -+#define AUFS_NAME "aufs" -+ -+#ifdef __KERNEL__ -+/* -+ * define it before including all other headers. -+ * sched.h may use pr_* macros before defining "current", so define the -+ * no-current version first, and re-define later. -+ */ -+#define pr_fmt(fmt) AUFS_NAME " %s:%d: " fmt, __func__, __LINE__ -+#include -+#undef pr_fmt -+#define pr_fmt(fmt) \ -+ AUFS_NAME " %s:%d:%.*s[%d]: " fmt, __func__, __LINE__, \ -+ (int)sizeof(current->comm), current->comm, current->pid -+#else -+#include -+#include -+#endif /* __KERNEL__ */ -+ -+#include -+ -+#define AUFS_VERSION "4.0" -+ -+/* todo? move this to linux-2.6.19/include/magic.h */ -+#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') -+ -+/* ---------------------------------------------------------------------- */ -+ -+#ifdef CONFIG_AUFS_BRANCH_MAX_127 -+typedef int8_t aufs_bindex_t; -+#define AUFS_BRANCH_MAX 127 -+#else -+typedef int16_t aufs_bindex_t; -+#ifdef CONFIG_AUFS_BRANCH_MAX_511 -+#define AUFS_BRANCH_MAX 511 -+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) -+#define AUFS_BRANCH_MAX 1023 -+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) -+#define AUFS_BRANCH_MAX 32767 -+#endif -+#endif -+ -+#ifdef __KERNEL__ -+#ifndef AUFS_BRANCH_MAX -+#error unknown CONFIG_AUFS_BRANCH_MAX value -+#endif -+#endif /* __KERNEL__ */ -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AUFS_FSTYPE AUFS_NAME -+ -+#define AUFS_ROOT_INO 2 -+#define AUFS_FIRST_INO 11 -+ -+#define AUFS_WH_PFX ".wh." -+#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) -+#define AUFS_WH_TMP_LEN 4 -+/* a limit for rmdir/rename a dir and copyup */ -+#define AUFS_MAX_NAMELEN (NAME_MAX \ -+ - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ -+ - 1 /* dot */\ -+ - AUFS_WH_TMP_LEN) /* hex */ -+#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" -+#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME -+#define AUFS_XINO_DEF_SEC 30 /* seconds */ -+#define AUFS_XINO_DEF_TRUNC 45 /* percentage */ -+#define AUFS_DIRWH_DEF 3 -+#define AUFS_RDCACHE_DEF 10 /* seconds */ -+#define AUFS_RDCACHE_MAX 3600 /* seconds */ -+#define AUFS_RDBLK_DEF 512 /* bytes */ -+#define AUFS_RDHASH_DEF 32 -+#define AUFS_WKQ_NAME AUFS_NAME "d" -+#define AUFS_MFS_DEF_SEC 30 /* seconds */ -+#define AUFS_MFS_MAX_SEC 3600 /* seconds */ -+#define AUFS_FHSM_CACHE_DEF_SEC 30 /* seconds */ -+#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */ -+ -+/* pseudo-link maintenace under /proc */ -+#define AUFS_PLINK_MAINT_NAME "plink_maint" -+#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME -+#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME -+ -+#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ -+#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME -+ -+#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME -+#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" -+#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" -+ -+/* doubly whiteouted */ -+#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME -+#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME -+#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME -+ -+/* branch permissions and attributes */ -+#define AUFS_BRPERM_RW "rw" -+#define AUFS_BRPERM_RO "ro" -+#define AUFS_BRPERM_RR "rr" -+#define AUFS_BRATTR_COO_REG "coo_reg" -+#define AUFS_BRATTR_COO_ALL "coo_all" -+#define AUFS_BRATTR_FHSM "fhsm" -+#define AUFS_BRATTR_UNPIN "unpin" -+#define AUFS_BRATTR_ICEX "icex" -+#define AUFS_BRATTR_ICEX_SEC "icexsec" -+#define AUFS_BRATTR_ICEX_SYS "icexsys" -+#define AUFS_BRATTR_ICEX_TR "icextr" -+#define AUFS_BRATTR_ICEX_USR "icexusr" -+#define AUFS_BRATTR_ICEX_OTH "icexoth" -+#define AUFS_BRRATTR_WH "wh" -+#define AUFS_BRWATTR_NLWH "nolwh" -+#define AUFS_BRWATTR_MOO "moo" -+ -+#define AuBrPerm_RW 1 /* writable, hardlinkable wh */ -+#define AuBrPerm_RO (1 << 1) /* readonly */ -+#define AuBrPerm_RR (1 << 2) /* natively readonly */ -+#define AuBrPerm_Mask (AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR) -+ -+#define AuBrAttr_COO_REG (1 << 3) /* copy-up on open */ -+#define AuBrAttr_COO_ALL (1 << 4) -+#define AuBrAttr_COO_Mask (AuBrAttr_COO_REG | AuBrAttr_COO_ALL) -+ -+#define AuBrAttr_FHSM (1 << 5) /* file-based hsm */ -+#define AuBrAttr_UNPIN (1 << 6) /* rename-able top dir of -+ branch. meaningless since -+ linux-3.18-rc1 */ -+ -+/* ignore error in copying XATTR */ -+#define AuBrAttr_ICEX_SEC (1 << 7) -+#define AuBrAttr_ICEX_SYS (1 << 8) -+#define AuBrAttr_ICEX_TR (1 << 9) -+#define AuBrAttr_ICEX_USR (1 << 10) -+#define AuBrAttr_ICEX_OTH (1 << 11) -+#define AuBrAttr_ICEX (AuBrAttr_ICEX_SEC \ -+ | AuBrAttr_ICEX_SYS \ -+ | AuBrAttr_ICEX_TR \ -+ | AuBrAttr_ICEX_USR \ -+ | AuBrAttr_ICEX_OTH) -+ -+#define AuBrRAttr_WH (1 << 12) /* whiteout-able */ -+#define AuBrRAttr_Mask AuBrRAttr_WH -+ -+#define AuBrWAttr_NoLinkWH (1 << 13) /* un-hardlinkable whiteouts */ -+#define AuBrWAttr_MOO (1 << 14) /* move-up on open */ -+#define AuBrWAttr_Mask (AuBrWAttr_NoLinkWH | AuBrWAttr_MOO) -+ -+#define AuBrAttr_CMOO_Mask (AuBrAttr_COO_Mask | AuBrWAttr_MOO) -+ -+/* #warning test userspace */ -+#ifdef __KERNEL__ -+#ifndef CONFIG_AUFS_FHSM -+#undef AuBrAttr_FHSM -+#define AuBrAttr_FHSM 0 -+#endif -+#ifndef CONFIG_AUFS_XATTR -+#undef AuBrAttr_ICEX -+#define AuBrAttr_ICEX 0 -+#undef AuBrAttr_ICEX_SEC -+#define AuBrAttr_ICEX_SEC 0 -+#undef AuBrAttr_ICEX_SYS -+#define AuBrAttr_ICEX_SYS 0 -+#undef AuBrAttr_ICEX_TR -+#define AuBrAttr_ICEX_TR 0 -+#undef AuBrAttr_ICEX_USR -+#define AuBrAttr_ICEX_USR 0 -+#undef AuBrAttr_ICEX_OTH -+#define AuBrAttr_ICEX_OTH 0 -+#endif -+#endif -+ -+/* the longest combination */ -+/* AUFS_BRATTR_ICEX and AUFS_BRATTR_ICEX_TR don't affect here */ -+#define AuBrPermStrSz sizeof(AUFS_BRPERM_RW \ -+ "+" AUFS_BRATTR_COO_REG \ -+ "+" AUFS_BRATTR_FHSM \ -+ "+" AUFS_BRATTR_UNPIN \ -+ "+" AUFS_BRATTR_ICEX_SEC \ -+ "+" AUFS_BRATTR_ICEX_SYS \ -+ "+" AUFS_BRATTR_ICEX_USR \ -+ "+" AUFS_BRATTR_ICEX_OTH \ -+ "+" AUFS_BRWATTR_NLWH) -+ -+typedef struct { -+ char a[AuBrPermStrSz]; -+} au_br_perm_str_t; -+ -+static inline int au_br_writable(int brperm) -+{ -+ return brperm & AuBrPerm_RW; -+} -+ -+static inline int au_br_whable(int brperm) -+{ -+ return brperm & (AuBrPerm_RW | AuBrRAttr_WH); -+} -+ -+static inline int au_br_wh_linkable(int brperm) -+{ -+ return !(brperm & AuBrWAttr_NoLinkWH); -+} -+ -+static inline int au_br_cmoo(int brperm) -+{ -+ return brperm & AuBrAttr_CMOO_Mask; -+} -+ -+static inline int au_br_fhsm(int brperm) -+{ -+ return brperm & AuBrAttr_FHSM; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* ioctl */ -+enum { -+ /* readdir in userspace */ -+ AuCtl_RDU, -+ AuCtl_RDU_INO, -+ -+ AuCtl_WBR_FD, /* pathconf wrapper */ -+ AuCtl_IBUSY, /* busy inode */ -+ AuCtl_MVDOWN, /* move-down */ -+ AuCtl_BR, /* info about branches */ -+ AuCtl_FHSM_FD /* connection for fhsm */ -+}; -+ -+/* borrowed from linux/include/linux/kernel.h */ -+#ifndef ALIGN -+#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) -+#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) -+#endif -+ -+/* borrowed from linux/include/linux/compiler-gcc3.h */ -+#ifndef __aligned -+#define __aligned(x) __attribute__((aligned(x))) -+#endif -+ -+#ifdef __KERNEL__ -+#ifndef __packed -+#define __packed __attribute__((packed)) -+#endif -+#endif -+ -+struct au_rdu_cookie { -+ uint64_t h_pos; -+ int16_t bindex; -+ uint8_t flags; -+ uint8_t pad; -+ uint32_t generation; -+} __aligned(8); -+ -+struct au_rdu_ent { -+ uint64_t ino; -+ int16_t bindex; -+ uint8_t type; -+ uint8_t nlen; -+ uint8_t wh; -+ char name[0]; -+} __aligned(8); -+ -+static inline int au_rdu_len(int nlen) -+{ -+ /* include the terminating NULL */ -+ return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, -+ sizeof(uint64_t)); -+} -+ -+union au_rdu_ent_ul { -+ struct au_rdu_ent __user *e; -+ uint64_t ul; -+}; -+ -+enum { -+ AufsCtlRduV_SZ, -+ AufsCtlRduV_End -+}; -+ -+struct aufs_rdu { -+ /* input */ -+ union { -+ uint64_t sz; /* AuCtl_RDU */ -+ uint64_t nent; /* AuCtl_RDU_INO */ -+ }; -+ union au_rdu_ent_ul ent; -+ uint16_t verify[AufsCtlRduV_End]; -+ -+ /* input/output */ -+ uint32_t blk; -+ -+ /* output */ -+ union au_rdu_ent_ul tail; -+ /* number of entries which were added in a single call */ -+ uint64_t rent; -+ uint8_t full; -+ uint8_t shwh; -+ -+ struct au_rdu_cookie cookie; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct aufs_wbr_fd { -+ uint32_t oflags; -+ int16_t brid; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+struct aufs_ibusy { -+ uint64_t ino, h_ino; -+ int16_t bindex; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* error code for move-down */ -+/* the actual message strings are implemented in aufs-util.git */ -+enum { -+ EAU_MVDOWN_OPAQUE = 1, -+ EAU_MVDOWN_WHITEOUT, -+ EAU_MVDOWN_UPPER, -+ EAU_MVDOWN_BOTTOM, -+ EAU_MVDOWN_NOUPPER, -+ EAU_MVDOWN_NOLOWERBR, -+ EAU_Last -+}; -+ -+/* flags for move-down */ -+#define AUFS_MVDOWN_DMSG 1 -+#define AUFS_MVDOWN_OWLOWER (1 << 1) /* overwrite lower */ -+#define AUFS_MVDOWN_KUPPER (1 << 2) /* keep upper */ -+#define AUFS_MVDOWN_ROLOWER (1 << 3) /* do even if lower is RO */ -+#define AUFS_MVDOWN_ROLOWER_R (1 << 4) /* did on lower RO */ -+#define AUFS_MVDOWN_ROUPPER (1 << 5) /* do even if upper is RO */ -+#define AUFS_MVDOWN_ROUPPER_R (1 << 6) /* did on upper RO */ -+#define AUFS_MVDOWN_BRID_UPPER (1 << 7) /* upper brid */ -+#define AUFS_MVDOWN_BRID_LOWER (1 << 8) /* lower brid */ -+#define AUFS_MVDOWN_FHSM_LOWER (1 << 9) /* find fhsm attr for lower */ -+#define AUFS_MVDOWN_STFS (1 << 10) /* req. stfs */ -+#define AUFS_MVDOWN_STFS_FAILED (1 << 11) /* output: stfs is unusable */ -+#define AUFS_MVDOWN_BOTTOM (1 << 12) /* output: no more lowers */ -+ -+/* index for move-down */ -+enum { -+ AUFS_MVDOWN_UPPER, -+ AUFS_MVDOWN_LOWER, -+ AUFS_MVDOWN_NARRAY -+}; -+ -+/* -+ * additional info of move-down -+ * number of free blocks and inodes. -+ * subset of struct kstatfs, but smaller and always 64bit. -+ */ -+struct aufs_stfs { -+ uint64_t f_blocks; -+ uint64_t f_bavail; -+ uint64_t f_files; -+ uint64_t f_ffree; -+}; -+ -+struct aufs_stbr { -+ int16_t brid; /* optional input */ -+ int16_t bindex; /* output */ -+ struct aufs_stfs stfs; /* output when AUFS_MVDOWN_STFS set */ -+} __aligned(8); -+ -+struct aufs_mvdown { -+ uint32_t flags; /* input/output */ -+ struct aufs_stbr stbr[AUFS_MVDOWN_NARRAY]; /* input/output */ -+ int8_t au_errno; /* output */ -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+union aufs_brinfo { -+ /* PATH_MAX may differ between kernel-space and user-space */ -+ char _spacer[4096]; -+ struct { -+ int16_t id; -+ int perm; -+ char path[0]; -+ }; -+} __aligned(8); -+ -+/* ---------------------------------------------------------------------- */ -+ -+#define AuCtlType 'A' -+#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) -+#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) -+#define AUFS_CTL_WBR_FD _IOW(AuCtlType, AuCtl_WBR_FD, \ -+ struct aufs_wbr_fd) -+#define AUFS_CTL_IBUSY _IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy) -+#define AUFS_CTL_MVDOWN _IOWR(AuCtlType, AuCtl_MVDOWN, \ -+ struct aufs_mvdown) -+#define AUFS_CTL_BRINFO _IOW(AuCtlType, AuCtl_BR, union aufs_brinfo) -+#define AUFS_CTL_FHSM_FD _IOW(AuCtlType, AuCtl_FHSM_FD, int) -+ -+#endif /* __AUFS_TYPE_H__ */ -diff -Nur linux-4.0.4.orig/include/uapi/linux/Kbuild linux-4.0.4/include/uapi/linux/Kbuild ---- linux-4.0.4.orig/include/uapi/linux/Kbuild 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/include/uapi/linux/Kbuild 2015-05-30 22:11:31.000000000 +0200 -@@ -58,6 +58,7 @@ - header-y += atm_tcp.h - header-y += atm_zatm.h - header-y += audit.h -+header-y += aufs_type.h - header-y += auto_fs4.h - header-y += auto_fs.h - header-y += auxvec.h -diff -Nur linux-4.0.4.orig/kernel/fork.c linux-4.0.4/kernel/fork.c ---- linux-4.0.4.orig/kernel/fork.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/kernel/fork.c 2015-05-30 22:11:30.000000000 +0200 -@@ -430,7 +430,7 @@ - struct inode *inode = file_inode(file); - struct address_space *mapping = file->f_mapping; - -- get_file(file); -+ vma_get_file(tmp); - if (tmp->vm_flags & VM_DENYWRITE) - atomic_dec(&inode->i_writecount); - i_mmap_lock_write(mapping); -diff -Nur linux-4.0.4.orig/MAINTAINERS linux-4.0.4/MAINTAINERS ---- linux-4.0.4.orig/MAINTAINERS 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/MAINTAINERS 2015-05-30 22:11:31.000000000 +0200 -@@ -1864,6 +1864,19 @@ - F: include/uapi/linux/audit.h - F: kernel/audit* - -+AUFS (advanced multi layered unification filesystem) FILESYSTEM -+M: "J. R. Okajima" -+L: linux-unionfs@vger.kernel.org -+L: aufs-users@lists.sourceforge.net (members only) -+W: http://aufs.sourceforge.net -+T: git://github.com/sfjro/aufs4-linux.git -+S: Supported -+F: Documentation/filesystems/aufs/ -+F: Documentation/ABI/testing/debugfs-aufs -+F: Documentation/ABI/testing/sysfs-aufs -+F: fs/aufs/ -+F: include/uapi/linux/aufs_type.h -+ - AUXILIARY DISPLAY DRIVERS - M: Miguel Ojeda Sandonis - W: http://miguelojeda.es/auxdisplay.htm -diff -Nur linux-4.0.4.orig/mm/filemap.c linux-4.0.4/mm/filemap.c ---- linux-4.0.4.orig/mm/filemap.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/mm/filemap.c 2015-05-30 22:11:30.000000000 +0200 -@@ -2064,7 +2064,7 @@ - int ret = VM_FAULT_LOCKED; - - sb_start_pagefault(inode->i_sb); -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - lock_page(page); - if (page->mapping != inode->i_mapping) { - unlock_page(page); -diff -Nur linux-4.0.4.orig/mm/madvise.c linux-4.0.4/mm/madvise.c ---- linux-4.0.4.orig/mm/madvise.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/mm/madvise.c 2015-05-30 22:11:30.000000000 +0200 -@@ -320,12 +320,12 @@ - * vma's reference to the file) can go away as soon as we drop - * mmap_sem. - */ -- get_file(f); -+ vma_get_file(vma); - up_read(¤t->mm->mmap_sem); - error = vfs_fallocate(f, - FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, - offset, end - start); -- fput(f); -+ vma_fput(vma); - down_read(¤t->mm->mmap_sem); - return error; - } -diff -Nur linux-4.0.4.orig/mm/Makefile linux-4.0.4/mm/Makefile ---- linux-4.0.4.orig/mm/Makefile 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/mm/Makefile 2015-05-30 22:11:30.000000000 +0200 -@@ -21,7 +21,7 @@ - mm_init.o mmu_context.o percpu.o slab_common.o \ - compaction.o vmacache.o \ - interval_tree.o list_lru.o workingset.o \ -- debug.o $(mmu-y) -+ prfile.o debug.o $(mmu-y) - - obj-y += init-mm.o - -diff -Nur linux-4.0.4.orig/mm/memory.c linux-4.0.4/mm/memory.c ---- linux-4.0.4.orig/mm/memory.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/mm/memory.c 2015-05-30 22:11:30.000000000 +0200 -@@ -2132,7 +2132,7 @@ - } - - if (!page_mkwrite) -- file_update_time(vma->vm_file); -+ vma_file_update_time(vma); - } - - return ret; -diff -Nur linux-4.0.4.orig/mm/mmap.c linux-4.0.4/mm/mmap.c ---- linux-4.0.4.orig/mm/mmap.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/mm/mmap.c 2015-05-30 22:11:30.000000000 +0200 -@@ -274,7 +274,7 @@ - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - if (vma->vm_file) -- fput(vma->vm_file); -+ vma_fput(vma); - mpol_put(vma_policy(vma)); - kmem_cache_free(vm_area_cachep, vma); - return next; -@@ -886,7 +886,7 @@ - if (remove_next) { - if (file) { - uprobe_munmap(next, next->vm_start, next->vm_end); -- fput(file); -+ vma_fput(vma); - } - if (next->anon_vma) - anon_vma_merge(vma, next); -@@ -1671,8 +1671,8 @@ - return addr; - - unmap_and_free_vma: -+ vma_fput(vma); - vma->vm_file = NULL; -- fput(file); - - /* Undo any partial mapping done by a device driver. */ - unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); -@@ -2473,7 +2473,7 @@ - goto out_free_mpol; - - if (new->vm_file) -- get_file(new->vm_file); -+ vma_get_file(new); - - if (new->vm_ops && new->vm_ops->open) - new->vm_ops->open(new); -@@ -2492,7 +2492,7 @@ - if (new->vm_ops && new->vm_ops->close) - new->vm_ops->close(new); - if (new->vm_file) -- fput(new->vm_file); -+ vma_fput(new); - unlink_anon_vmas(new); - out_free_mpol: - mpol_put(vma_policy(new)); -@@ -2635,7 +2635,6 @@ - struct vm_area_struct *vma; - unsigned long populate = 0; - unsigned long ret = -EINVAL; -- struct file *file; - - pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. " - "See Documentation/vm/remap_file_pages.txt.\n", -@@ -2679,10 +2678,10 @@ - munlock_vma_pages_range(vma, start, start + size); - } - -- file = get_file(vma->vm_file); -+ vma_get_file(vma); - ret = do_mmap_pgoff(vma->vm_file, start, size, - prot, flags, pgoff, &populate); -- fput(file); -+ vma_fput(vma); - out: - up_write(&mm->mmap_sem); - if (populate) -@@ -2950,7 +2949,7 @@ - if (anon_vma_clone(new_vma, vma)) - goto out_free_mempol; - if (new_vma->vm_file) -- get_file(new_vma->vm_file); -+ vma_get_file(new_vma); - if (new_vma->vm_ops && new_vma->vm_ops->open) - new_vma->vm_ops->open(new_vma); - vma_link(mm, new_vma, prev, rb_link, rb_parent); -diff -Nur linux-4.0.4.orig/mm/msync.c linux-4.0.4/mm/msync.c ---- linux-4.0.4.orig/mm/msync.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/mm/msync.c 2015-05-30 22:11:30.000000000 +0200 -@@ -84,10 +84,10 @@ - start = vma->vm_end; - if ((flags & MS_SYNC) && file && - (vma->vm_flags & VM_SHARED)) { -- get_file(file); -+ vma_get_file(vma); - up_read(&mm->mmap_sem); - error = vfs_fsync_range(file, fstart, fend, 1); -- fput(file); -+ vma_fput(vma); - if (error || start >= end) - goto out; - down_read(&mm->mmap_sem); -diff -Nur linux-4.0.4.orig/mm/nommu.c linux-4.0.4/mm/nommu.c ---- linux-4.0.4.orig/mm/nommu.c 2015-05-17 18:55:21.000000000 +0200 -+++ linux-4.0.4/mm/nommu.c 2015-05-30 22:11:30.000000000 +0200 -@@ -693,7 +693,7 @@ - up_write(&nommu_region_sem); - - if (region->vm_file) -- fput(region->vm_file); -+ vmr_fput(region); - - /* IO memory and memory shared directly out of the pagecache - * from ramfs/tmpfs mustn't be released here */ -@@ -858,7 +858,7 @@ - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - if (vma->vm_file) -- fput(vma->vm_file); -+ vma_fput(vma); - put_nommu_region(vma->vm_region); - kmem_cache_free(vm_area_cachep, vma); - } -@@ -1398,7 +1398,7 @@ - goto error_just_free; - } - } -- fput(region->vm_file); -+ vmr_fput(region); - kmem_cache_free(vm_region_jar, region); - region = pregion; - result = start; -@@ -1474,10 +1474,10 @@ - up_write(&nommu_region_sem); - error: - if (region->vm_file) -- fput(region->vm_file); -+ vmr_fput(region); - kmem_cache_free(vm_region_jar, region); - if (vma->vm_file) -- fput(vma->vm_file); -+ vma_fput(vma); - kmem_cache_free(vm_area_cachep, vma); - kleave(" = %d", ret); - return ret; -diff -Nur linux-4.0.4.orig/mm/prfile.c linux-4.0.4/mm/prfile.c ---- linux-4.0.4.orig/mm/prfile.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-4.0.4/mm/prfile.c 2015-05-30 22:11:30.000000000 +0200 -@@ -0,0 +1,86 @@ -+/* -+ * Mainly for aufs which mmap(2) diffrent file and wants to print different path -+ * in /proc/PID/maps. -+ * Call these functions via macros defined in linux/mm.h. -+ * -+ * See Documentation/filesystems/aufs/design/06mmap.txt -+ * -+ * Copyright (c) 2014 Junjro R. Okajima -+ * Copyright (c) 2014 Ian Campbell -+ */ -+ -+#include -+#include -+#include -+ -+/* #define PRFILE_TRACE */ -+static inline void prfile_trace(struct file *f, struct file *pr, -+ const char func[], int line, const char func2[]) -+{ -+#ifdef PRFILE_TRACE -+ if (pr) -+ pr_info("%s:%d: %s, %p\n", func, line, func2, -+ f ? (char *)f->f_path.dentry->d_name.name : "(null)"); -+#endif -+} -+ -+#ifdef CONFIG_MMU -+void vma_do_file_update_time(struct vm_area_struct *vma, const char func[], -+ int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ file_update_time(f); -+ if (f && pr) -+ file_update_time(pr); -+} -+ -+struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[], -+ int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ return (f && pr) ? pr : f; -+} -+ -+void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ get_file(f); -+ if (f && pr) -+ get_file(pr); -+} -+ -+void vma_do_fput(struct vm_area_struct *vma, const char func[], int line) -+{ -+ struct file *f = vma->vm_file, *pr = vma->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ fput(f); -+ if (f && pr) -+ fput(pr); -+} -+#else -+struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[], -+ int line) -+{ -+ struct file *f = region->vm_file, *pr = region->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ return (f && pr) ? pr : f; -+} -+ -+void vmr_do_fput(struct vm_region *region, const char func[], int line) -+{ -+ struct file *f = region->vm_file, *pr = region->vm_prfile; -+ -+ prfile_trace(f, pr, func, line, __func__); -+ fput(f); -+ if (f && pr) -+ fput(pr); -+} -+#endif /* CONFIG_MMU */ diff --git a/target/linux/patches/4.0.5/cleankernel.patch b/target/linux/patches/4.0.5/cleankernel.patch deleted file mode 100644 index d8c055dc3..000000000 --- a/target/linux/patches/4.0.5/cleankernel.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff -Nur linux-3.11.5.orig/scripts/Makefile.headersinst linux-3.11.5/scripts/Makefile.headersinst ---- linux-3.11.5.orig/scripts/Makefile.headersinst 2013-10-14 03:14:45.000000000 +0200 -+++ linux-3.11.5/scripts/Makefile.headersinst 2013-10-15 16:33:10.000000000 +0200 -@@ -107,7 +107,6 @@ - - targets += $(install-file) - $(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE -- $(if $(unwanted),$(call cmd,remove),) - $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) - $(call if_changed,install) - diff --git a/target/linux/patches/4.0.5/cris-header.patch b/target/linux/patches/4.0.5/cris-header.patch deleted file mode 100644 index 2b5a88461..000000000 --- a/target/linux/patches/4.0.5/cris-header.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild ---- linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 -+++ linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild 2014-09-26 19:24:50.000000000 +0200 -@@ -1 +1,2 @@ - # CRISv10 arch -+header-y += ptrace.h -diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild ---- linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 -+++ linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild 2014-09-26 19:24:31.000000000 +0200 -@@ -1 +1,2 @@ - # CRISv32 arch -+header-y += ptrace.h diff --git a/target/linux/patches/4.0.5/initramfs-nosizelimit.patch b/target/linux/patches/4.0.5/initramfs-nosizelimit.patch deleted file mode 100644 index 40d2f6bd8..000000000 --- a/target/linux/patches/4.0.5/initramfs-nosizelimit.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 9a18df7a71bfa620b1278777d64783a359d7eb4e Mon Sep 17 00:00:00 2001 -From: Thorsten Glaser -Date: Sun, 4 May 2014 01:37:54 +0200 -Subject: [PATCH] mount tmpfs-as-rootfs (initramfs) with -o - nr_blocks=0,nr_inodes=0 - -I would have preferred to write this patch to be able to pass -rootflags=nr_blocks=0,nr_inodes=0 on the kernel command line, -and then hand these rootflags over to the initramfs (tmpfs) -mount in the same way the kernel hands them over to the block -device rootfs mount. But at least the Debian/m68k initrd also -parses $rootflags from the environment and adds it to the call -to the user-space mount for the eventual root device, which -would make the kernel command line rootflags option be used in -both places (tmpfs and e.g. ext4) which is guaranteed to error -out in at least one of them. - -This change is intended to aid people in a setup where the -initrd is the final root filesystem, i.e. not mounted over. -This is especially useful in automated tests running on qemu -for boards with constrained memory (e.g. 64 MiB on sh4). - -Considering that the initramfs is normally emptied out then -overmounted, this change is probably safe for setups where -initramfs just hosts early userspace, too, since the tmpfs -backing it is not accessible any more later on, AFAICT. - -Signed-off-by: Thorsten Glaser ---- - init/do_mounts.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/init/do_mounts.c b/init/do_mounts.c -index 82f2288..55a4cfe 100644 ---- a/init/do_mounts.c -+++ b/init/do_mounts.c -@@ -594,6 +594,7 @@ out: - } - - static bool is_tmpfs; -+static char tmpfs_rootflags[] = "nr_blocks=0,nr_inodes=0"; - static struct dentry *rootfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) - { -@@ -606,6 +607,9 @@ static struct dentry *rootfs_mount(struct file_system_type *fs_type, - if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) - fill = shmem_fill_super; - -+ if (is_tmpfs) -+ data = tmpfs_rootflags; -+ - return mount_nodev(fs_type, flags, data, fill); - } - --- -2.0.0.rc0 - diff --git a/target/linux/patches/4.0.5/startup.patch b/target/linux/patches/4.0.5/startup.patch deleted file mode 100644 index d396b75e4..000000000 --- a/target/linux/patches/4.0.5/startup.patch +++ /dev/null @@ -1,37 +0,0 @@ -diff -Nur linux-3.13.3.orig/init/main.c linux-3.13.3/init/main.c ---- linux-3.13.3.orig/init/main.c 2014-02-13 23:00:14.000000000 +0100 -+++ linux-3.13.3/init/main.c 2014-02-17 11:35:14.000000000 +0100 -@@ -916,6 +917,8 @@ - if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - pr_err("Warning: unable to open an initial console.\n"); - -+ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); -+ - (void) sys_dup(0); - (void) sys_dup(0); - /* -diff -Nur linux-3.13.6.orig/init/initramfs.c linux-3.13.6/init/initramfs.c ---- linux-3.13.6.orig/init/initramfs.c 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/init/initramfs.c 2014-03-15 12:11:31.882731916 +0100 -@@ -622,6 +622,9 @@ - */ - load_default_modules(); - } -+#ifdef CONFIG_DEVTMPFS_MOUNT -+ devtmpfs_mount("dev"); -+#endif - return 0; - } - rootfs_initcall(populate_rootfs); -diff -Nur linux-3.13.6.orig/init/main.c linux-3.13.6/init/main.c ---- linux-3.13.6.orig/init/main.c 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/init/main.c 2014-03-15 12:13:16.459024452 +0100 -@@ -924,7 +924,7 @@ - */ - - if (!ramdisk_execute_command) -- ramdisk_execute_command = "/init"; -+ ramdisk_execute_command = "/sbin/init"; - - if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { - ramdisk_execute_command = NULL; diff --git a/target/linux/patches/4.0.6/aufs4.patch b/target/linux/patches/4.0.6/aufs4.patch new file mode 100644 index 000000000..73b035010 --- /dev/null +++ b/target/linux/patches/4.0.6/aufs4.patch @@ -0,0 +1,33819 @@ +diff -Nur linux-4.0.4.orig/Documentation/ABI/testing/debugfs-aufs linux-4.0.4/Documentation/ABI/testing/debugfs-aufs +--- linux-4.0.4.orig/Documentation/ABI/testing/debugfs-aufs 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/ABI/testing/debugfs-aufs 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,50 @@ ++What: /debug/aufs/si_/ ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ Under /debug/aufs, a directory named si_ is created ++ per aufs mount, where is a unique id generated ++ internally. ++ ++What: /debug/aufs/si_/plink ++Date: Apr 2013 ++Contact: J. R. Okajima ++Description: ++ It has three lines and shows the information about the ++ pseudo-link. The first line is a single number ++ representing a number of buckets. The second line is a ++ number of pseudo-links per buckets (separated by a ++ blank). The last line is a single number representing a ++ total number of psedo-links. ++ When the aufs mount option 'noplink' is specified, it ++ will show "1\n0\n0\n". ++ ++What: /debug/aufs/si_/xib ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the consumed blocks by xib (External Inode Number ++ Bitmap), its block size and file size. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see the aufs manual. ++ ++What: /debug/aufs/si_/xino0, xino1 ... xinoN ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the consumed blocks by xino (External Inode Number ++ Translation Table), its link count, block size and file ++ size. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see the aufs manual. ++ ++What: /debug/aufs/si_/xigen ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the consumed blocks by xigen (External Inode ++ Generation Table), its block size and file size. ++ If CONFIG_AUFS_EXPORT is disabled, this entry will not ++ be created. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see the aufs manual. +diff -Nur linux-4.0.4.orig/Documentation/ABI/testing/sysfs-aufs linux-4.0.4/Documentation/ABI/testing/sysfs-aufs +--- linux-4.0.4.orig/Documentation/ABI/testing/sysfs-aufs 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/ABI/testing/sysfs-aufs 2015-05-30 22:11:29.000000000 +0200 +@@ -0,0 +1,31 @@ ++What: /sys/fs/aufs/si_/ ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ Under /sys/fs/aufs, a directory named si_ is created ++ per aufs mount, where is a unique id generated ++ internally. ++ ++What: /sys/fs/aufs/si_/br0, br1 ... brN ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the abolute path of a member directory (which ++ is called branch) in aufs, and its permission. ++ ++What: /sys/fs/aufs/si_/brid0, brid1 ... bridN ++Date: July 2013 ++Contact: J. R. Okajima ++Description: ++ It shows the id of a member directory (which is called ++ branch) in aufs. ++ ++What: /sys/fs/aufs/si_/xi_path ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the abolute path of XINO (External Inode Number ++ Bitmap, Translation Table and Generation Table) file ++ even if it is the default path. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see the aufs manual. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/01intro.txt linux-4.0.4/Documentation/filesystems/aufs/design/01intro.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/01intro.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/01intro.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,157 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Introduction ++---------------------------------------- ++ ++aufs [ei ju: ef es] | [a u f s] ++1. abbrev. for "advanced multi-layered unification filesystem". ++2. abbrev. for "another unionfs". ++3. abbrev. for "auf das" in German which means "on the" in English. ++ Ex. "Butter aufs Brot"(G) means "butter onto bread"(E). ++ But "Filesystem aufs Filesystem" is hard to understand. ++ ++AUFS is a filesystem with features: ++- multi layered stackable unification filesystem, the member directory ++ is called as a branch. ++- branch permission and attribute, 'readonly', 'real-readonly', ++ 'readwrite', 'whiteout-able', 'link-able whiteout', etc. and their ++ combination. ++- internal "file copy-on-write". ++- logical deletion, whiteout. ++- dynamic branch manipulation, adding, deleting and changing permission. ++- allow bypassing aufs, user's direct branch access. ++- external inode number translation table and bitmap which maintains the ++ persistent aufs inode number. ++- seekable directory, including NFS readdir. ++- file mapping, mmap and sharing pages. ++- pseudo-link, hardlink over branches. ++- loopback mounted filesystem as a branch. ++- several policies to select one among multiple writable branches. ++- revert a single systemcall when an error occurs in aufs. ++- and more... ++ ++ ++Multi Layered Stackable Unification Filesystem ++---------------------------------------------------------------------- ++Most people already knows what it is. ++It is a filesystem which unifies several directories and provides a ++merged single directory. When users access a file, the access will be ++passed/re-directed/converted (sorry, I am not sure which English word is ++correct) to the real file on the member filesystem. The member ++filesystem is called 'lower filesystem' or 'branch' and has a mode ++'readonly' and 'readwrite.' And the deletion for a file on the lower ++readonly branch is handled by creating 'whiteout' on the upper writable ++branch. ++ ++On LKML, there have been discussions about UnionMount (Jan Blunck, ++Bharata B Rao and Valerie Aurora) and Unionfs (Erez Zadok). They took ++different approaches to implement the merged-view. ++The former tries putting it into VFS, and the latter implements as a ++separate filesystem. ++(If I misunderstand about these implementations, please let me know and ++I shall correct it. Because it is a long time ago when I read their ++source files last time). ++ ++UnionMount's approach will be able to small, but may be hard to share ++branches between several UnionMount since the whiteout in it is ++implemented in the inode on branch filesystem and always ++shared. According to Bharata's post, readdir does not seems to be ++finished yet. ++There are several missing features known in this implementations such as ++- for users, the inode number may change silently. eg. copy-up. ++- link(2) may break by copy-up. ++- read(2) may get an obsoleted filedata (fstat(2) too). ++- fcntl(F_SETLK) may be broken by copy-up. ++- unnecessary copy-up may happen, for example mmap(MAP_PRIVATE) after ++ open(O_RDWR). ++ ++In linux-3.18, "overlay" filesystem (formerly known as "overlayfs") was ++merged into mainline. This is another implementation of UnionMount as a ++separated filesystem. All the limitations and known problems which ++UnionMount are equally inherited to "overlay" filesystem. ++ ++Unionfs has a longer history. When I started implementing a stackable ++filesystem (Aug 2005), it already existed. It has virtual super_block, ++inode, dentry and file objects and they have an array pointing lower ++same kind objects. After contributing many patches for Unionfs, I ++re-started my project AUFS (Jun 2006). ++ ++In AUFS, the structure of filesystem resembles to Unionfs, but I ++implemented my own ideas, approaches and enhancements and it became ++totally different one. ++ ++Comparing DM snapshot and fs based implementation ++- the number of bytes to be copied between devices is much smaller. ++- the type of filesystem must be one and only. ++- the fs must be writable, no readonly fs, even for the lower original ++ device. so the compression fs will not be usable. but if we use ++ loopback mount, we may address this issue. ++ for instance, ++ mount /cdrom/squashfs.img /sq ++ losetup /sq/ext2.img ++ losetup /somewhere/cow ++ dmsetup "snapshot /dev/loop0 /dev/loop1 ..." ++- it will be difficult (or needs more operations) to extract the ++ difference between the original device and COW. ++- DM snapshot-merge may help a lot when users try merging. in the ++ fs-layer union, users will use rsync(1). ++ ++You may want to read my old paper "Filesystems in LiveCD" ++(http://aufs.sourceforge.net/aufs2/report/sq/sq.pdf). ++ ++ ++Several characters/aspects/persona of aufs ++---------------------------------------------------------------------- ++ ++Aufs has several characters, aspects or persona. ++1. a filesystem, callee of VFS helper ++2. sub-VFS, caller of VFS helper for branches ++3. a virtual filesystem which maintains persistent inode number ++4. reader/writer of files on branches such like an application ++ ++1. Callee of VFS Helper ++As an ordinary linux filesystem, aufs is a callee of VFS. For instance, ++unlink(2) from an application reaches sys_unlink() kernel function and ++then vfs_unlink() is called. vfs_unlink() is one of VFS helper and it ++calls filesystem specific unlink operation. Actually aufs implements the ++unlink operation but it behaves like a redirector. ++ ++2. Caller of VFS Helper for Branches ++aufs_unlink() passes the unlink request to the branch filesystem as if ++it were called from VFS. So the called unlink operation of the branch ++filesystem acts as usual. As a caller of VFS helper, aufs should handle ++every necessary pre/post operation for the branch filesystem. ++- acquire the lock for the parent dir on a branch ++- lookup in a branch ++- revalidate dentry on a branch ++- mnt_want_write() for a branch ++- vfs_unlink() for a branch ++- mnt_drop_write() for a branch ++- release the lock on a branch ++ ++3. Persistent Inode Number ++One of the most important issue for a filesystem is to maintain inode ++numbers. This is particularly important to support exporting a ++filesystem via NFS. Aufs is a virtual filesystem which doesn't have a ++backend block device for its own. But some storage is necessary to ++keep and maintain the inode numbers. It may be a large space and may not ++suit to keep in memory. Aufs rents some space from its first writable ++branch filesystem (by default) and creates file(s) on it. These files ++are created by aufs internally and removed soon (currently) keeping ++opened. ++Note: Because these files are removed, they are totally gone after ++ unmounting aufs. It means the inode numbers are not persistent ++ across unmount or reboot. I have a plan to make them really ++ persistent which will be important for aufs on NFS server. ++ ++4. Read/Write Files Internally (copy-on-write) ++Because a branch can be readonly, when you write a file on it, aufs will ++"copy-up" it to the upper writable branch internally. And then write the ++originally requested thing to the file. Generally kernel doesn't ++open/read/write file actively. In aufs, even a single write may cause a ++internal "file copy". This behaviour is very similar to cp(1) command. ++ ++Some people may think it is better to pass such work to user space ++helper, instead of doing in kernel space. Actually I am still thinking ++about it. But currently I have implemented it in kernel space. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/02struct.txt linux-4.0.4/Documentation/filesystems/aufs/design/02struct.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/02struct.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/02struct.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,245 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Basic Aufs Internal Structure ++ ++Superblock/Inode/Dentry/File Objects ++---------------------------------------------------------------------- ++As like an ordinary filesystem, aufs has its own ++superblock/inode/dentry/file objects. All these objects have a ++dynamically allocated array and store the same kind of pointers to the ++lower filesystem, branch. ++For example, when you build a union with one readwrite branch and one ++readonly, mounted /au, /rw and /ro respectively. ++- /au = /rw + /ro ++- /ro/fileA exists but /rw/fileA ++ ++Aufs lookup operation finds /ro/fileA and gets dentry for that. These ++pointers are stored in a aufs dentry. The array in aufs dentry will be, ++- [0] = NULL (because /rw/fileA doesn't exist) ++- [1] = /ro/fileA ++ ++This style of an array is essentially same to the aufs ++superblock/inode/dentry/file objects. ++ ++Because aufs supports manipulating branches, ie. add/delete/change ++branches dynamically, these objects has its own generation. When ++branches are changed, the generation in aufs superblock is ++incremented. And a generation in other object are compared when it is ++accessed. When a generation in other objects are obsoleted, aufs ++refreshes the internal array. ++ ++ ++Superblock ++---------------------------------------------------------------------- ++Additionally aufs superblock has some data for policies to select one ++among multiple writable branches, XIB files, pseudo-links and kobject. ++See below in detail. ++About the policies which supports copy-down a directory, see ++wbr_policy.txt too. ++ ++ ++Branch and XINO(External Inode Number Translation Table) ++---------------------------------------------------------------------- ++Every branch has its own xino (external inode number translation table) ++file. The xino file is created and unlinked by aufs internally. When two ++members of a union exist on the same filesystem, they share the single ++xino file. ++The struct of a xino file is simple, just a sequence of aufs inode ++numbers which is indexed by the lower inode number. ++In the above sample, assume the inode number of /ro/fileA is i111 and ++aufs assigns the inode number i999 for fileA. Then aufs writes 999 as ++4(8) bytes at 111 * 4(8) bytes offset in the xino file. ++ ++When the inode numbers are not contiguous, the xino file will be sparse ++which has a hole in it and doesn't consume as much disk space as it ++might appear. If your branch filesystem consumes disk space for such ++holes, then you should specify 'xino=' option at mounting aufs. ++ ++Aufs has a mount option to free the disk blocks for such holes in XINO ++files on tmpfs or ramdisk. But it is not so effective actually. If you ++meet a problem of disk shortage due to XINO files, then you should try ++"tmpfs-ino.patch" (and "vfs-ino.patch" too) in aufs4-standalone.git. ++The patch localizes the assignment inumbers per tmpfs-mount and avoid ++the holes in XINO files. ++ ++Also a writable branch has three kinds of "whiteout bases". All these ++are existed when the branch is joined to aufs, and their names are ++whiteout-ed doubly, so that users will never see their names in aufs ++hierarchy. ++1. a regular file which will be hardlinked to all whiteouts. ++2. a directory to store a pseudo-link. ++3. a directory to store an "orphan"-ed file temporary. ++ ++1. Whiteout Base ++ When you remove a file on a readonly branch, aufs handles it as a ++ logical deletion and creates a whiteout on the upper writable branch ++ as a hardlink of this file in order not to consume inode on the ++ writable branch. ++2. Pseudo-link Dir ++ See below, Pseudo-link. ++3. Step-Parent Dir ++ When "fileC" exists on the lower readonly branch only and it is ++ opened and removed with its parent dir, and then user writes ++ something into it, then aufs copies-up fileC to this ++ directory. Because there is no other dir to store fileC. After ++ creating a file under this dir, the file is unlinked. ++ ++Because aufs supports manipulating branches, ie. add/delete/change ++dynamically, a branch has its own id. When the branch order changes, ++aufs finds the new index by searching the branch id. ++ ++ ++Pseudo-link ++---------------------------------------------------------------------- ++Assume "fileA" exists on the lower readonly branch only and it is ++hardlinked to "fileB" on the branch. When you write something to fileA, ++aufs copies-up it to the upper writable branch. Additionally aufs ++creates a hardlink under the Pseudo-link Directory of the writable ++branch. The inode of a pseudo-link is kept in aufs super_block as a ++simple list. If fileB is read after unlinking fileA, aufs returns ++filedata from the pseudo-link instead of the lower readonly ++branch. Because the pseudo-link is based upon the inode, to keep the ++inode number by xino (see above) is essentially necessary. ++ ++All the hardlinks under the Pseudo-link Directory of the writable branch ++should be restored in a proper location later. Aufs provides a utility ++to do this. The userspace helpers executed at remounting and unmounting ++aufs by default. ++During this utility is running, it puts aufs into the pseudo-link ++maintenance mode. In this mode, only the process which began the ++maintenance mode (and its child processes) is allowed to operate in ++aufs. Some other processes which are not related to the pseudo-link will ++be allowed to run too, but the rest have to return an error or wait ++until the maintenance mode ends. If a process already acquires an inode ++mutex (in VFS), it has to return an error. ++ ++ ++XIB(external inode number bitmap) ++---------------------------------------------------------------------- ++Addition to the xino file per a branch, aufs has an external inode number ++bitmap in a superblock object. It is also an internal file such like a ++xino file. ++It is a simple bitmap to mark whether the aufs inode number is in-use or ++not. ++To reduce the file I/O, aufs prepares a single memory page to cache xib. ++ ++As well as XINO files, aufs has a feature to truncate/refresh XIB to ++reduce the number of consumed disk blocks for these files. ++ ++ ++Virtual or Vertical Dir, and Readdir in Userspace ++---------------------------------------------------------------------- ++In order to support multiple layers (branches), aufs readdir operation ++constructs a virtual dir block on memory. For readdir, aufs calls ++vfs_readdir() internally for each dir on branches, merges their entries ++with eliminating the whiteout-ed ones, and sets it to file (dir) ++object. So the file object has its entry list until it is closed. The ++entry list will be updated when the file position is zero and becomes ++obsoleted. This decision is made in aufs automatically. ++ ++The dynamically allocated memory block for the name of entries has a ++unit of 512 bytes (by default) and stores the names contiguously (no ++padding). Another block for each entry is handled by kmem_cache too. ++During building dir blocks, aufs creates hash list and judging whether ++the entry is whiteouted by its upper branch or already listed. ++The merged result is cached in the corresponding inode object and ++maintained by a customizable life-time option. ++ ++Some people may call it can be a security hole or invite DoS attack ++since the opened and once readdir-ed dir (file object) holds its entry ++list and becomes a pressure for system memory. But I'd say it is similar ++to files under /proc or /sys. The virtual files in them also holds a ++memory page (generally) while they are opened. When an idea to reduce ++memory for them is introduced, it will be applied to aufs too. ++For those who really hate this situation, I've developed readdir(3) ++library which operates this merging in userspace. You just need to set ++LD_PRELOAD environment variable, and aufs will not consume no memory in ++kernel space for readdir(3). ++ ++ ++Workqueue ++---------------------------------------------------------------------- ++Aufs sometimes requires privilege access to a branch. For instance, ++in copy-up/down operation. When a user process is going to make changes ++to a file which exists in the lower readonly branch only, and the mode ++of one of ancestor directories may not be writable by a user ++process. Here aufs copy-up the file with its ancestors and they may ++require privilege to set its owner/group/mode/etc. ++This is a typical case of a application character of aufs (see ++Introduction). ++ ++Aufs uses workqueue synchronously for this case. It creates its own ++workqueue. The workqueue is a kernel thread and has privilege. Aufs ++passes the request to call mkdir or write (for example), and wait for ++its completion. This approach solves a problem of a signal handler ++simply. ++If aufs didn't adopt the workqueue and changed the privilege of the ++process, then the process may receive the unexpected SIGXFSZ or other ++signals. ++ ++Also aufs uses the system global workqueue ("events" kernel thread) too ++for asynchronous tasks, such like handling inotify/fsnotify, re-creating a ++whiteout base and etc. This is unrelated to a privilege. ++Most of aufs operation tries acquiring a rw_semaphore for aufs ++superblock at the beginning, at the same time waits for the completion ++of all queued asynchronous tasks. ++ ++ ++Whiteout ++---------------------------------------------------------------------- ++The whiteout in aufs is very similar to Unionfs's. That is represented ++by its filename. UnionMount takes an approach of a file mode, but I am ++afraid several utilities (find(1) or something) will have to support it. ++ ++Basically the whiteout represents "logical deletion" which stops aufs to ++lookup further, but also it represents "dir is opaque" which also stop ++further lookup. ++ ++In aufs, rmdir(2) and rename(2) for dir uses whiteout alternatively. ++In order to make several functions in a single systemcall to be ++revertible, aufs adopts an approach to rename a directory to a temporary ++unique whiteouted name. ++For example, in rename(2) dir where the target dir already existed, aufs ++renames the target dir to a temporary unique whiteouted name before the ++actual rename on a branch, and then handles other actions (make it opaque, ++update the attributes, etc). If an error happens in these actions, aufs ++simply renames the whiteouted name back and returns an error. If all are ++succeeded, aufs registers a function to remove the whiteouted unique ++temporary name completely and asynchronously to the system global ++workqueue. ++ ++ ++Copy-up ++---------------------------------------------------------------------- ++It is a well-known feature or concept. ++When user modifies a file on a readonly branch, aufs operate "copy-up" ++internally and makes change to the new file on the upper writable branch. ++When the trigger systemcall does not update the timestamps of the parent ++dir, aufs reverts it after copy-up. ++ ++ ++Move-down (aufs3.9 and later) ++---------------------------------------------------------------------- ++"Copy-up" is one of the essential feature in aufs. It copies a file from ++the lower readonly branch to the upper writable branch when a user ++changes something about the file. ++"Move-down" is an opposite action of copy-up. Basically this action is ++ran manually instead of automatically and internally. ++For desgin and implementation, aufs has to consider these issues. ++- whiteout for the file may exist on the lower branch. ++- ancestor directories may not exist on the lower branch. ++- diropq for the ancestor directories may exist on the upper branch. ++- free space on the lower branch will reduce. ++- another access to the file may happen during moving-down, including ++ UDBA (see "Revalidate Dentry and UDBA"). ++- the file should not be hard-linked nor pseudo-linked. they should be ++ handled by auplink utility later. ++ ++Sometimes users want to move-down a file from the upper writable branch ++to the lower readonly or writable branch. For instance, ++- the free space of the upper writable branch is going to run out. ++- create a new intermediate branch between the upper and lower branch. ++- etc. ++ ++For this purpose, use "aumvdown" command in aufs-util.git. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/03atomic_open.txt linux-4.0.4/Documentation/filesystems/aufs/design/03atomic_open.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/03atomic_open.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/03atomic_open.txt 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,72 @@ ++ ++# Copyright (C) 2015 Junjiro R. Okajima ++ ++Support for a branch who has its ->atomic_open() ++---------------------------------------------------------------------- ++The filesystems who implement its ->atomic_open() are not majority. For ++example NFSv4 does, and aufs should call NFSv4 ->atomic_open, ++particularly for open(O_CREAT|O_EXCL, 0400) case. Other than ++->atomic_open(), NFSv4 returns an error for this open(2). While I am not ++sure whether all filesystems who have ->atomic_open() behave like this, ++but NFSv4 surely returns the error. ++ ++In order to support ->atomic_open() for aufs, there are a few ++approaches. ++ ++A. Introduce aufs_atomic_open() ++ - calls one of VFS:do_last(), lookup_open() or atomic_open() for ++ branch fs. ++B. Introduce aufs_atomic_open() calling create, open and chmod. this is ++ an aufs user Pip Cet's approach ++ - calls aufs_create(), VFS finish_open() and notify_change(). ++ - pass fake-mode to finish_open(), and then correct the mode by ++ notify_change(). ++C. Extend aufs_open() to call branch fs's ->atomic_open() ++ - no aufs_atomic_open(). ++ - aufs_lookup() registers the TID to an aufs internal object. ++ - aufs_create() does nothing when the matching TID is registered, but ++ registers the mode. ++ - aufs_open() calls branch fs's ->atomic_open() when the matching ++ TID is registered. ++D. Extend aufs_open() to re-try branch fs's ->open() with superuser's ++ credential ++ - no aufs_atomic_open(). ++ - aufs_create() registers the TID to an internal object. this info ++ represents "this process created this file just now." ++ - when aufs gets EACCES from branch fs's ->open(), then confirm the ++ registered TID and re-try open() with superuser's credential. ++ ++Pros and cons for each approach. ++ ++A. ++ - straightforward but highly depends upon VFS internal. ++ - the atomic behavaiour is kept. ++ - some of parameters such as nameidata are hard to reproduce for ++ branch fs. ++ - large overhead. ++B. ++ - easy to implement. ++ - the atomic behavaiour is lost. ++C. ++ - the atomic behavaiour is kept. ++ - dirty and tricky. ++ - VFS checks whether the file is created correctly after calling ++ ->create(), which means this approach doesn't work. ++D. ++ - easy to implement. ++ - the atomic behavaiour is lost. ++ - to open a file with superuser's credential and give it to a user ++ process is a bad idea, since the file object keeps the credential ++ in it. It may affect LSM or something. This approach doesn't work ++ either. ++ ++The approach A is ideal, but it hard to implement. So here is a ++variation of A, which is to be implemented. ++ ++A-1. Introduce aufs_atomic_open() ++ - calls branch fs ->atomic_open() if exists. otherwise calls ++ vfs_create() and finish_open(). ++ - the demerit is that the several checks after branch fs ++ ->atomic_open() are lost. in the ordinary case, the checks are ++ done by VFS:do_last(), lookup_open() and atomic_open(). some can ++ be implemented in aufs, but not all I am afraid. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/03lookup.txt linux-4.0.4/Documentation/filesystems/aufs/design/03lookup.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/03lookup.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/03lookup.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,100 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Lookup in a Branch ++---------------------------------------------------------------------- ++Since aufs has a character of sub-VFS (see Introduction), it operates ++lookup for branches as VFS does. It may be a heavy work. But almost all ++lookup operation in aufs is the simplest case, ie. lookup only an entry ++directly connected to its parent. Digging down the directory hierarchy ++is unnecessary. VFS has a function lookup_one_len() for that use, and ++aufs calls it. ++ ++When a branch is a remote filesystem, aufs basically relies upon its ++->d_revalidate(), also aufs forces the hardest revalidate tests for ++them. ++For d_revalidate, aufs implements three levels of revalidate tests. See ++"Revalidate Dentry and UDBA" in detail. ++ ++ ++Test Only the Highest One for the Directory Permission (dirperm1 option) ++---------------------------------------------------------------------- ++Let's try case study. ++- aufs has two branches, upper readwrite and lower readonly. ++ /au = /rw + /ro ++- "dirA" exists under /ro, but /rw. and its mode is 0700. ++- user invoked "chmod a+rx /au/dirA" ++- the internal copy-up is activated and "/rw/dirA" is created and its ++ permission bits are set to world readable. ++- then "/au/dirA" becomes world readable? ++ ++In this case, /ro/dirA is still 0700 since it exists in readonly branch, ++or it may be a natively readonly filesystem. If aufs respects the lower ++branch, it should not respond readdir request from other users. But user ++allowed it by chmod. Should really aufs rejects showing the entries ++under /ro/dirA? ++ ++To be honest, I don't have a good solution for this case. So aufs ++implements 'dirperm1' and 'nodirperm1' mount options, and leave it to ++users. ++When dirperm1 is specified, aufs checks only the highest one for the ++directory permission, and shows the entries. Otherwise, as usual, checks ++every dir existing on all branches and rejects the request. ++ ++As a side effect, dirperm1 option improves the performance of aufs ++because the number of permission check is reduced when the number of ++branch is many. ++ ++ ++Revalidate Dentry and UDBA (User's Direct Branch Access) ++---------------------------------------------------------------------- ++Generally VFS helpers re-validate a dentry as a part of lookup. ++0. digging down the directory hierarchy. ++1. lock the parent dir by its i_mutex. ++2. lookup the final (child) entry. ++3. revalidate it. ++4. call the actual operation (create, unlink, etc.) ++5. unlock the parent dir ++ ++If the filesystem implements its ->d_revalidate() (step 3), then it is ++called. Actually aufs implements it and checks the dentry on a branch is ++still valid. ++But it is not enough. Because aufs has to release the lock for the ++parent dir on a branch at the end of ->lookup() (step 2) and ++->d_revalidate() (step 3) while the i_mutex of the aufs dir is still ++held by VFS. ++If the file on a branch is changed directly, eg. bypassing aufs, after ++aufs released the lock, then the subsequent operation may cause ++something unpleasant result. ++ ++This situation is a result of VFS architecture, ->lookup() and ++->d_revalidate() is separated. But I never say it is wrong. It is a good ++design from VFS's point of view. It is just not suitable for sub-VFS ++character in aufs. ++ ++Aufs supports such case by three level of revalidation which is ++selectable by user. ++1. Simple Revalidate ++ Addition to the native flow in VFS's, confirm the child-parent ++ relationship on the branch just after locking the parent dir on the ++ branch in the "actual operation" (step 4). When this validation ++ fails, aufs returns EBUSY. ->d_revalidate() (step 3) in aufs still ++ checks the validation of the dentry on branches. ++2. Monitor Changes Internally by Inotify/Fsnotify ++ Addition to above, in the "actual operation" (step 4) aufs re-lookup ++ the dentry on the branch, and returns EBUSY if it finds different ++ dentry. ++ Additionally, aufs sets the inotify/fsnotify watch for every dir on branches ++ during it is in cache. When the event is notified, aufs registers a ++ function to kernel 'events' thread by schedule_work(). And the ++ function sets some special status to the cached aufs dentry and inode ++ private data. If they are not cached, then aufs has nothing to ++ do. When the same file is accessed through aufs (step 0-3) later, ++ aufs will detect the status and refresh all necessary data. ++ In this mode, aufs has to ignore the event which is fired by aufs ++ itself. ++3. No Extra Validation ++ This is the simplest test and doesn't add any additional revalidation ++ test, and skip the revalidation in step 4. It is useful and improves ++ aufs performance when system surely hide the aufs branches from user, ++ by over-mounting something (or another method). +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/04branch.txt linux-4.0.4/Documentation/filesystems/aufs/design/04branch.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/04branch.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/04branch.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,61 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Branch Manipulation ++ ++Since aufs supports dynamic branch manipulation, ie. add/remove a branch ++and changing its permission/attribute, there are a lot of works to do. ++ ++ ++Add a Branch ++---------------------------------------------------------------------- ++o Confirm the adding dir exists outside of aufs, including loopback ++ mount, and its various attributes. ++o Initialize the xino file and whiteout bases if necessary. ++ See struct.txt. ++ ++o Check the owner/group/mode of the directory ++ When the owner/group/mode of the adding directory differs from the ++ existing branch, aufs issues a warning because it may impose a ++ security risk. ++ For example, when a upper writable branch has a world writable empty ++ top directory, a malicious user can create any files on the writable ++ branch directly, like copy-up and modify manually. If something like ++ /etc/{passwd,shadow} exists on the lower readonly branch but the upper ++ writable branch, and the writable branch is world-writable, then a ++ malicious guy may create /etc/passwd on the writable branch directly ++ and the infected file will be valid in aufs. ++ I am afraid it can be a security issue, but aufs can do nothing except ++ producing a warning. ++ ++ ++Delete a Branch ++---------------------------------------------------------------------- ++o Confirm the deleting branch is not busy ++ To be general, there is one merit to adopt "remount" interface to ++ manipulate branches. It is to discard caches. At deleting a branch, ++ aufs checks the still cached (and connected) dentries and inodes. If ++ there are any, then they are all in-use. An inode without its ++ corresponding dentry can be alive alone (for example, inotify/fsnotify case). ++ ++ For the cached one, aufs checks whether the same named entry exists on ++ other branches. ++ If the cached one is a directory, because aufs provides a merged view ++ to users, as long as one dir is left on any branch aufs can show the ++ dir to users. In this case, the branch can be removed from aufs. ++ Otherwise aufs rejects deleting the branch. ++ ++ If any file on the deleting branch is opened by aufs, then aufs ++ rejects deleting. ++ ++ ++Modify the Permission of a Branch ++---------------------------------------------------------------------- ++o Re-initialize or remove the xino file and whiteout bases if necessary. ++ See struct.txt. ++ ++o rw --> ro: Confirm the modifying branch is not busy ++ Aufs rejects the request if any of these conditions are true. ++ - a file on the branch is mmap-ed. ++ - a regular file on the branch is opened for write and there is no ++ same named entry on the upper branch. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/05wbr_policy.txt linux-4.0.4/Documentation/filesystems/aufs/design/05wbr_policy.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/05wbr_policy.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/05wbr_policy.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,51 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Policies to Select One among Multiple Writable Branches ++---------------------------------------------------------------------- ++When the number of writable branch is more than one, aufs has to decide ++the target branch for file creation or copy-up. By default, the highest ++writable branch which has the parent (or ancestor) dir of the target ++file is chosen (top-down-parent policy). ++By user's request, aufs implements some other policies to select the ++writable branch, for file creation several policies, round-robin, ++most-free-space, and other policies. For copy-up, top-down-parent, ++bottom-up-parent, bottom-up and others. ++ ++As expected, the round-robin policy selects the branch in circular. When ++you have two writable branches and creates 10 new files, 5 files will be ++created for each branch. mkdir(2) systemcall is an exception. When you ++create 10 new directories, all will be created on the same branch. ++And the most-free-space policy selects the one which has most free ++space among the writable branches. The amount of free space will be ++checked by aufs internally, and users can specify its time interval. ++ ++The policies for copy-up is more simple, ++top-down-parent is equivalent to the same named on in create policy, ++bottom-up-parent selects the writable branch where the parent dir ++exists and the nearest upper one from the copyup-source, ++bottom-up selects the nearest upper writable branch from the ++copyup-source, regardless the existence of the parent dir. ++ ++There are some rules or exceptions to apply these policies. ++- If there is a readonly branch above the policy-selected branch and ++ the parent dir is marked as opaque (a variation of whiteout), or the ++ target (creating) file is whiteout-ed on the upper readonly branch, ++ then the result of the policy is ignored and the target file will be ++ created on the nearest upper writable branch than the readonly branch. ++- If there is a writable branch above the policy-selected branch and ++ the parent dir is marked as opaque or the target file is whiteouted ++ on the branch, then the result of the policy is ignored and the target ++ file will be created on the highest one among the upper writable ++ branches who has diropq or whiteout. In case of whiteout, aufs removes ++ it as usual. ++- link(2) and rename(2) systemcalls are exceptions in every policy. ++ They try selecting the branch where the source exists as possible ++ since copyup a large file will take long time. If it can't be, ++ ie. the branch where the source exists is readonly, then they will ++ follow the copyup policy. ++- There is an exception for rename(2) when the target exists. ++ If the rename target exists, aufs compares the index of the branches ++ where the source and the target exists and selects the higher ++ one. If the selected branch is readonly, then aufs follows the ++ copyup policy. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/06fhsm.txt linux-4.0.4/Documentation/filesystems/aufs/design/06fhsm.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/06fhsm.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/06fhsm.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,105 @@ ++ ++# Copyright (C) 2011-2015 Junjiro R. Okajima ++ ++File-based Hierarchical Storage Management (FHSM) ++---------------------------------------------------------------------- ++Hierarchical Storage Management (or HSM) is a well-known feature in the ++storage world. Aufs provides this feature as file-based with multiple ++writable branches, based upon the principle of "Colder, the Lower". ++Here the word "colder" means that the less used files, and "lower" means ++that the position in the order of the stacked branches vertically. ++These multiple writable branches are prioritized, ie. the topmost one ++should be the fastest drive and be used heavily. ++ ++o Characters in aufs FHSM story ++- aufs itself and a new branch attribute. ++- a new ioctl interface to move-down and to establish a connection with ++ the daemon ("move-down" is a converse of "copy-up"). ++- userspace tool and daemon. ++ ++The userspace daemon establishes a connection with aufs and waits for ++the notification. The notified information is very similar to struct ++statfs containing the number of consumed blocks and inodes. ++When the consumed blocks/inodes of a branch exceeds the user-specified ++upper watermark, the daemon activates its move-down process until the ++consumed blocks/inodes reaches the user-specified lower watermark. ++ ++The actual move-down is done by aufs based upon the request from ++user-space since we need to maintain the inode number and the internal ++pointer arrays in aufs. ++ ++Currently aufs FHSM handles the regular files only. Additionally they ++must not be hard-linked nor pseudo-linked. ++ ++ ++o Cowork of aufs and the user-space daemon ++ During the userspace daemon established the connection, aufs sends a ++ small notification to it whenever aufs writes something into the ++ writable branch. But it may cost high since aufs issues statfs(2) ++ internally. So user can specify a new option to cache the ++ info. Actually the notification is controlled by these factors. ++ + the specified cache time. ++ + classified as "force" by aufs internally. ++ Until the specified time expires, aufs doesn't send the info ++ except the forced cases. When aufs decide forcing, the info is always ++ notified to userspace. ++ For example, the number of free inodes is generally large enough and ++ the shortage of it happens rarely. So aufs doesn't force the ++ notification when creating a new file, directory and others. This is ++ the typical case which aufs doesn't force. ++ When aufs writes the actual filedata and the files consumes any of new ++ blocks, the aufs forces notifying. ++ ++ ++o Interfaces in aufs ++- New branch attribute. ++ + fhsm ++ Specifies that the branch is managed by FHSM feature. In other word, ++ participant in the FHSM. ++ When nofhsm is set to the branch, it will not be the source/target ++ branch of the move-down operation. This attribute is set ++ independently from coo and moo attributes, and if you want full ++ FHSM, you should specify them as well. ++- New mount option. ++ + fhsm_sec ++ Specifies a second to suppress many less important info to be ++ notified. ++- New ioctl. ++ + AUFS_CTL_FHSM_FD ++ create a new file descriptor which userspace can read the notification ++ (a subset of struct statfs) from aufs. ++- Module parameter 'brs' ++ It has to be set to 1. Otherwise the new mount option 'fhsm' will not ++ be set. ++- mount helpers /sbin/mount.aufs and /sbin/umount.aufs ++ When there are two or more branches with fhsm attributes, ++ /sbin/mount.aufs invokes the user-space daemon and /sbin/umount.aufs ++ terminates it. As a result of remounting and branch-manipulation, the ++ number of branches with fhsm attribute can be one. In this case, ++ /sbin/mount.aufs will terminate the user-space daemon. ++ ++ ++Finally the operation is done as these steps in kernel-space. ++- make sure that, ++ + no one else is using the file. ++ + the file is not hard-linked. ++ + the file is not pseudo-linked. ++ + the file is a regular file. ++ + the parent dir is not opaqued. ++- find the target writable branch. ++- make sure the file is not whiteout-ed by the upper (than the target) ++ branch. ++- make the parent dir on the target branch. ++- mutex lock the inode on the branch. ++- unlink the whiteout on the target branch (if exists). ++- lookup and create the whiteout-ed temporary name on the target branch. ++- copy the file as the whiteout-ed temporary name on the target branch. ++- rename the whiteout-ed temporary name to the original name. ++- unlink the file on the source branch. ++- maintain the internal pointer array and the external inode number ++ table (XINO). ++- maintain the timestamps and other attributes of the parent dir and the ++ file. ++ ++And of course, in every step, an error may happen. So the operation ++should restore the original file state after an error happens. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/06mmap.txt linux-4.0.4/Documentation/filesystems/aufs/design/06mmap.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/06mmap.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/06mmap.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,33 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++mmap(2) -- File Memory Mapping ++---------------------------------------------------------------------- ++In aufs, the file-mapped pages are handled by a branch fs directly, no ++interaction with aufs. It means aufs_mmap() calls the branch fs's ++->mmap(). ++This approach is simple and good, but there is one problem. ++Under /proc, several entries show the mmapped files by its path (with ++device and inode number), and the printed path will be the path on the ++branch fs's instead of virtual aufs's. ++This is not a problem in most cases, but some utilities lsof(1) (and its ++user) may expect the path on aufs. ++ ++To address this issue, aufs adds a new member called vm_prfile in struct ++vm_area_struct (and struct vm_region). The original vm_file points to ++the file on the branch fs in order to handle everything correctly as ++usual. The new vm_prfile points to a virtual file in aufs, and the ++show-functions in procfs refers to vm_prfile if it is set. ++Also we need to maintain several other places where touching vm_file ++such like ++- fork()/clone() copies vma and the reference count of vm_file is ++ incremented. ++- merging vma maintains the ref count too. ++ ++This is not a good approach. It just fakes the printed path. But it ++leaves all behaviour around f_mapping unchanged. This is surely an ++advantage. ++Actually aufs had adopted another complicated approach which calls ++generic_file_mmap() and handles struct vm_operations_struct. In this ++approach, aufs met a hard problem and I could not solve it without ++switching the approach. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/06xattr.txt linux-4.0.4/Documentation/filesystems/aufs/design/06xattr.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/06xattr.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/06xattr.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,81 @@ ++ ++# Copyright (C) 2014-2015 Junjiro R. Okajima ++ ++Listing XATTR/EA and getting the value ++---------------------------------------------------------------------- ++For the inode standard attributes (owner, group, timestamps, etc.), aufs ++shows the values from the topmost existing file. This behaviour is good ++for the non-dir entries since the bahaviour exactly matches the shown ++information. But for the directories, aufs considers all the same named ++entries on the lower branches. Which means, if one of the lower entry ++rejects readdir call, then aufs returns an error even if the topmost ++entry allows it. This behaviour is necessary to respect the branch fs's ++security, but can make users confused since the user-visible standard ++attributes don't match the behaviour. ++To address this issue, aufs has a mount option called dirperm1 which ++checks the permission for the topmost entry only, and ignores the lower ++entry's permission. ++ ++A similar issue can happen around XATTR. ++getxattr(2) and listxattr(2) families behave as if dirperm1 option is ++always set. Otherwise these very unpleasant situation would happen. ++- listxattr(2) may return the duplicated entries. ++- users may not be able to remove or reset the XATTR forever, ++ ++ ++XATTR/EA support in the internal (copy,move)-(up,down) ++---------------------------------------------------------------------- ++Generally the extended attributes of inode are categorized as these. ++- "security" for LSM and capability. ++- "system" for posix ACL, 'acl' mount option is required for the branch ++ fs generally. ++- "trusted" for userspace, CAP_SYS_ADMIN is required. ++- "user" for userspace, 'user_xattr' mount option is required for the ++ branch fs generally. ++ ++Moreover there are some other categories. Aufs handles these rather ++unpopular categories as the ordinary ones, ie. there is no special ++condition nor exception. ++ ++In copy-up, the support for XATTR on the dst branch may differ from the ++src branch. In this case, the copy-up operation will get an error and ++the original user operation which triggered the copy-up will fail. It ++can happen that even all copy-up will fail. ++When both of src and dst branches support XATTR and if an error occurs ++during copying XATTR, then the copy-up should fail obviously. That is a ++good reason and aufs should return an error to userspace. But when only ++the src branch support that XATTR, aufs should not return an error. ++For example, the src branch supports ACL but the dst branch doesn't ++because the dst branch may natively un-support it or temporary ++un-support it due to "noacl" mount option. Of course, the dst branch fs ++may NOT return an error even if the XATTR is not supported. It is ++totally up to the branch fs. ++ ++Anyway when the aufs internal copy-up gets an error from the dst branch ++fs, then aufs tries removing the just copied entry and returns the error ++to the userspace. The worst case of this situation will be all copy-up ++will fail. ++ ++For the copy-up operation, there two basic approaches. ++- copy the specified XATTR only (by category above), and return the ++ error unconditionally if it happens. ++- copy all XATTR, and ignore the error on the specified category only. ++ ++In order to support XATTR and to implement the correct behaviour, aufs ++chooses the latter approach and introduces some new branch attributes, ++"icexsec", "icexsys", "icextr", "icexusr", and "icexoth". ++They correspond to the XATTR namespaces (see above). Additionally, to be ++convenient, "icex" is also provided which means all "icex*" attributes ++are set (here the word "icex" stands for "ignore copy-error on XATTR"). ++ ++The meaning of these attributes is to ignore the error from setting ++XATTR on that branch. ++Note that aufs tries copying all XATTR unconditionally, and ignores the ++error from the dst branch according to the specified attributes. ++ ++Some XATTR may have its default value. The default value may come from ++the parent dir or the environment. If the default value is set at the ++file creating-time, it will be overwritten by copy-up. ++Some contradiction may happen I am afraid. ++Do we need another attribute to stop copying XATTR? I am unsure. For ++now, aufs implements the branch attributes to ignore the error. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/07export.txt linux-4.0.4/Documentation/filesystems/aufs/design/07export.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/07export.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/07export.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,45 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Export Aufs via NFS ++---------------------------------------------------------------------- ++Here is an approach. ++- like xino/xib, add a new file 'xigen' which stores aufs inode ++ generation. ++- iget_locked(): initialize aufs inode generation for a new inode, and ++ store it in xigen file. ++- destroy_inode(): increment aufs inode generation and store it in xigen ++ file. it is necessary even if it is not unlinked, because any data of ++ inode may be changed by UDBA. ++- encode_fh(): for a root dir, simply return FILEID_ROOT. otherwise ++ build file handle by ++ + branch id (4 bytes) ++ + superblock generation (4 bytes) ++ + inode number (4 or 8 bytes) ++ + parent dir inode number (4 or 8 bytes) ++ + inode generation (4 bytes)) ++ + return value of exportfs_encode_fh() for the parent on a branch (4 ++ bytes) ++ + file handle for a branch (by exportfs_encode_fh()) ++- fh_to_dentry(): ++ + find the index of a branch from its id in handle, and check it is ++ still exist in aufs. ++ + 1st level: get the inode number from handle and search it in cache. ++ + 2nd level: if not found in cache, get the parent inode number from ++ the handle and search it in cache. and then open the found parent ++ dir, find the matching inode number by vfs_readdir() and get its ++ name, and call lookup_one_len() for the target dentry. ++ + 3rd level: if the parent dir is not cached, call ++ exportfs_decode_fh() for a branch and get the parent on a branch, ++ build a pathname of it, convert it a pathname in aufs, call ++ path_lookup(). now aufs gets a parent dir dentry, then handle it as ++ the 2nd level. ++ + to open the dir, aufs needs struct vfsmount. aufs keeps vfsmount ++ for every branch, but not itself. to get this, (currently) aufs ++ searches in current->nsproxy->mnt_ns list. it may not be a good ++ idea, but I didn't get other approach. ++ + test the generation of the gotten inode. ++- every inode operation: they may get EBUSY due to UDBA. in this case, ++ convert it into ESTALE for NFSD. ++- readdir(): call lockdep_on/off() because filldir in NFSD calls ++ lookup_one_len(), vfs_getattr(), encode_fh() and others. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/08shwh.txt linux-4.0.4/Documentation/filesystems/aufs/design/08shwh.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/08shwh.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/08shwh.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,39 @@ ++ ++# Copyright (C) 2005-2015 Junjiro R. Okajima ++ ++Show Whiteout Mode (shwh) ++---------------------------------------------------------------------- ++Generally aufs hides the name of whiteouts. But in some cases, to show ++them is very useful for users. For instance, creating a new middle layer ++(branch) by merging existing layers. ++ ++(borrowing aufs1 HOW-TO from a user, Michael Towers) ++When you have three branches, ++- Bottom: 'system', squashfs (underlying base system), read-only ++- Middle: 'mods', squashfs, read-only ++- Top: 'overlay', ram (tmpfs), read-write ++ ++The top layer is loaded at boot time and saved at shutdown, to preserve ++the changes made to the system during the session. ++When larger changes have been made, or smaller changes have accumulated, ++the size of the saved top layer data grows. At this point, it would be ++nice to be able to merge the two overlay branches ('mods' and 'overlay') ++and rewrite the 'mods' squashfs, clearing the top layer and thus ++restoring save and load speed. ++ ++This merging is simplified by the use of another aufs mount, of just the ++two overlay branches using the 'shwh' option. ++# mount -t aufs -o ro,shwh,br:/livesys/overlay=ro+wh:/livesys/mods=rr+wh \ ++ aufs /livesys/merge_union ++ ++A merged view of these two branches is then available at ++/livesys/merge_union, and the new feature is that the whiteouts are ++visible! ++Note that in 'shwh' mode the aufs mount must be 'ro', which will disable ++writing to all branches. Also the default mode for all branches is 'ro'. ++It is now possible to save the combined contents of the two overlay ++branches to a new squashfs, e.g.: ++# mksquashfs /livesys/merge_union /path/to/newmods.squash ++ ++This new squashfs archive can be stored on the boot device and the ++initramfs will use it to replace the old one at the next boot. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/design/10dynop.txt linux-4.0.4/Documentation/filesystems/aufs/design/10dynop.txt +--- linux-4.0.4.orig/Documentation/filesystems/aufs/design/10dynop.txt 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/design/10dynop.txt 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,34 @@ ++ ++# Copyright (C) 2010-2015 Junjiro R. Okajima ++ ++Dynamically customizable FS operations ++---------------------------------------------------------------------- ++Generally FS operations (struct inode_operations, struct ++address_space_operations, struct file_operations, etc.) are defined as ++"static const", but it never means that FS have only one set of ++operation. Some FS have multiple sets of them. For instance, ext2 has ++three sets, one for XIP, for NOBH, and for normal. ++Since aufs overrides and redirects these operations, sometimes aufs has ++to change its behaviour according to the branch FS type. More importantly ++VFS acts differently if a function (member in the struct) is set or ++not. It means aufs should have several sets of operations and select one ++among them according to the branch FS definition. ++ ++In order to solve this problem and not to affect the behaviour of VFS, ++aufs defines these operations dynamically. For instance, aufs defines ++dummy direct_IO function for struct address_space_operations, but it may ++not be set to the address_space_operations actually. When the branch FS ++doesn't have it, aufs doesn't set it to its address_space_operations ++while the function definition itself is still alive. So the behaviour ++itself will not change, and it will return an error when direct_IO is ++not set. ++ ++The lifetime of these dynamically generated operation object is ++maintained by aufs branch object. When the branch is removed from aufs, ++the reference counter of the object is decremented. When it reaches ++zero, the dynamically generated operation object will be freed. ++ ++This approach is designed to support AIO (io_submit), Direct I/O and ++XIP (DAX) mainly. ++Currently this approach is applied to address_space_operations for ++regular files only. +diff -Nur linux-4.0.4.orig/Documentation/filesystems/aufs/README linux-4.0.4/Documentation/filesystems/aufs/README +--- linux-4.0.4.orig/Documentation/filesystems/aufs/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/Documentation/filesystems/aufs/README 2015-05-30 22:11:29.000000000 +0200 +@@ -0,0 +1,383 @@ ++ ++Aufs4 -- advanced multi layered unification filesystem version 4.x ++http://aufs.sf.net ++Junjiro R. Okajima ++ ++ ++0. Introduction ++---------------------------------------- ++In the early days, aufs was entirely re-designed and re-implemented ++Unionfs Version 1.x series. Adding many original ideas, approaches, ++improvements and implementations, it becomes totally different from ++Unionfs while keeping the basic features. ++Recently, Unionfs Version 2.x series begin taking some of the same ++approaches to aufs1's. ++Unionfs is being developed by Professor Erez Zadok at Stony Brook ++University and his team. ++ ++Aufs4 supports linux-4.0 and later, and for linux-3.x series try aufs3. ++If you want older kernel version support, try aufs2-2.6.git or ++aufs2-standalone.git repository, aufs1 from CVS on SourceForge. ++ ++Note: it becomes clear that "Aufs was rejected. Let's give it up." ++ According to Christoph Hellwig, linux rejects all union-type ++ filesystems but UnionMount. ++ ++ ++PS. Al Viro seems have a plan to merge aufs as well as overlayfs and ++ UnionMount, and he pointed out an issue around a directory mutex ++ lock and aufs addressed it. But it is still unsure whether aufs will ++ be merged (or any other union solution). ++ ++ ++ ++1. Features ++---------------------------------------- ++- unite several directories into a single virtual filesystem. The member ++ directory is called as a branch. ++- you can specify the permission flags to the branch, which are 'readonly', ++ 'readwrite' and 'whiteout-able.' ++- by upper writable branch, internal copyup and whiteout, files/dirs on ++ readonly branch are modifiable logically. ++- dynamic branch manipulation, add, del. ++- etc... ++ ++Also there are many enhancements in aufs, such as: ++- test only the highest one for the directory permission (dirperm1) ++- copyup on open (coo=) ++- 'move' policy for copy-up between two writable branches, after ++ checking free space. ++- xattr, acl ++- readdir(3) in userspace. ++- keep inode number by external inode number table ++- keep the timestamps of file/dir in internal copyup operation ++- seekable directory, supporting NFS readdir. ++- whiteout is hardlinked in order to reduce the consumption of inodes ++ on branch ++- do not copyup, nor create a whiteout when it is unnecessary ++- revert a single systemcall when an error occurs in aufs ++- remount interface instead of ioctl ++- maintain /etc/mtab by an external command, /sbin/mount.aufs. ++- loopback mounted filesystem as a branch ++- kernel thread for removing the dir who has a plenty of whiteouts ++- support copyup sparse file (a file which has a 'hole' in it) ++- default permission flags for branches ++- selectable permission flags for ro branch, whether whiteout can ++ exist or not ++- export via NFS. ++- support /fs/aufs and /aufs. ++- support multiple writable branches, some policies to select one ++ among multiple writable branches. ++- a new semantics for link(2) and rename(2) to support multiple ++ writable branches. ++- no glibc changes are required. ++- pseudo hardlink (hardlink over branches) ++- allow a direct access manually to a file on branch, e.g. bypassing aufs. ++ including NFS or remote filesystem branch. ++- userspace wrapper for pathconf(3)/fpathconf(3) with _PC_LINK_MAX. ++- and more... ++ ++Currently these features are dropped temporary from aufs4. ++See design/08plan.txt in detail. ++- nested mount, i.e. aufs as readonly no-whiteout branch of another aufs ++ (robr) ++- statistics of aufs thread (/sys/fs/aufs/stat) ++ ++Features or just an idea in the future (see also design/*.txt), ++- reorder the branch index without del/re-add. ++- permanent xino files for NFSD ++- an option for refreshing the opened files after add/del branches ++- light version, without branch manipulation. (unnecessary?) ++- copyup in userspace ++- inotify in userspace ++- readv/writev ++ ++ ++2. Download ++---------------------------------------- ++There are three GIT trees for aufs4, aufs4-linux.git, ++aufs4-standalone.git, and aufs-util.git. Note that there is no "4" in ++"aufs-util.git." ++While the aufs-util is always necessary, you need either of aufs4-linux ++or aufs4-standalone. ++ ++The aufs4-linux tree includes the whole linux mainline GIT tree, ++git://git.kernel.org/.../torvalds/linux.git. ++And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot ++build aufs4 as an external kernel module. ++Several extra patches are not included in this tree. Only ++aufs4-standalone tree contains them. They are describe in the later ++section "Configuration and Compilation." ++ ++On the other hand, the aufs4-standalone tree has only aufs source files ++and necessary patches, and you can select CONFIG_AUFS_FS=m. ++But you need to apply all aufs patches manually. ++ ++You will find GIT branches whose name is in form of "aufs4.x" where "x" ++represents the linux kernel version, "linux-4.x". For instance, ++"aufs4.0" is for linux-4.0. For latest "linux-4.x-rcN", use ++"aufs4.x-rcN" branch. ++ ++o aufs4-linux tree ++$ git clone --reference /your/linux/git/tree \ ++ git://github.com/sfjro/aufs4-linux.git aufs4-linux.git ++- if you don't have linux GIT tree, then remove "--reference ..." ++$ cd aufs4-linux.git ++$ git checkout origin/aufs4.0 ++ ++Or You may want to directly git-pull aufs into your linux GIT tree, and ++leave the patch-work to GIT. ++$ cd /your/linux/git/tree ++$ git remote add aufs4 git://github.com/sfjro/aufs4-linux.git ++$ git fetch aufs4 ++$ git checkout -b my4.0 v4.0 ++$ (add your local change...) ++$ git pull aufs4 aufs4.0 ++- now you have v4.0 + your_changes + aufs4.0 in you my4.0 branch. ++- you may need to solve some conflicts between your_changes and ++ aufs4.0. in this case, git-rerere is recommended so that you can ++ solve the similar conflicts automatically when you upgrade to 4.1 or ++ later in the future. ++ ++o aufs4-standalone tree ++$ git clone git://github.com/sfjro/aufs4-standalone.git aufs4-standalone.git ++$ cd aufs4-standalone.git ++$ git checkout origin/aufs4.0 ++ ++o aufs-util tree ++$ git clone git://git.code.sf.net/p/aufs/aufs-util aufs-util.git ++- note that the public aufs-util.git is on SourceForge instead of ++ GitHUB. ++$ cd aufs-util.git ++$ git checkout origin/aufs4.0 ++ ++Note: The 4.x-rcN branch is to be used with `rc' kernel versions ONLY. ++The minor version number, 'x' in '4.x', of aufs may not always ++follow the minor version number of the kernel. ++Because changes in the kernel that cause the use of a new ++minor version number do not always require changes to aufs-util. ++ ++Since aufs-util has its own minor version number, you may not be ++able to find a GIT branch in aufs-util for your kernel's ++exact minor version number. ++In this case, you should git-checkout the branch for the ++nearest lower number. ++ ++For (an unreleased) example: ++If you are using "linux-4.10" and the "aufs4.10" branch ++does not exist in aufs-util repository, then "aufs4.9", "aufs4.8" ++or something numerically smaller is the branch for your kernel. ++ ++Also you can view all branches by ++ $ git branch -a ++ ++ ++3. Configuration and Compilation ++---------------------------------------- ++Make sure you have git-checkout'ed the correct branch. ++ ++For aufs4-linux tree, ++- enable CONFIG_AUFS_FS. ++- set other aufs configurations if necessary. ++ ++For aufs4-standalone tree, ++There are several ways to build. ++ ++1. ++- apply ./aufs4-kbuild.patch to your kernel source files. ++- apply ./aufs4-base.patch too. ++- apply ./aufs4-mmap.patch too. ++- apply ./aufs4-standalone.patch too, if you have a plan to set ++ CONFIG_AUFS_FS=m. otherwise you don't need ./aufs4-standalone.patch. ++- copy ./{Documentation,fs,include/uapi/linux/aufs_type.h} files to your ++ kernel source tree. Never copy $PWD/include/uapi/linux/Kbuild. ++- enable CONFIG_AUFS_FS, you can select either ++ =m or =y. ++- and build your kernel as usual. ++- install the built kernel. ++ Note: Since linux-3.9, every filesystem module requires an alias ++ "fs-". You should make sure that "fs-aufs" is listed in your ++ modules.aliases file if you set CONFIG_AUFS_FS=m. ++- install the header files too by "make headers_install" to the ++ directory where you specify. By default, it is $PWD/usr. ++ "make help" shows a brief note for headers_install. ++- and reboot your system. ++ ++2. ++- module only (CONFIG_AUFS_FS=m). ++- apply ./aufs4-base.patch to your kernel source files. ++- apply ./aufs4-mmap.patch too. ++- apply ./aufs4-standalone.patch too. ++- build your kernel, don't forget "make headers_install", and reboot. ++- edit ./config.mk and set other aufs configurations if necessary. ++ Note: You should read $PWD/fs/aufs/Kconfig carefully which describes ++ every aufs configurations. ++- build the module by simple "make". ++ Note: Since linux-3.9, every filesystem module requires an alias ++ "fs-". You should make sure that "fs-aufs" is listed in your ++ modules.aliases file. ++- you can specify ${KDIR} make variable which points to your kernel ++ source tree. ++- install the files ++ + run "make install" to install the aufs module, or copy the built ++ $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply). ++ + run "make install_headers" (instead of headers_install) to install ++ the modified aufs header file (you can specify DESTDIR which is ++ available in aufs standalone version's Makefile only), or copy ++ $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever ++ you like manually. By default, the target directory is $PWD/usr. ++- no need to apply aufs4-kbuild.patch, nor copying source files to your ++ kernel source tree. ++ ++Note: The header file aufs_type.h is necessary to build aufs-util ++ as well as "make headers_install" in the kernel source tree. ++ headers_install is subject to be forgotten, but it is essentially ++ necessary, not only for building aufs-util. ++ You may not meet problems without headers_install in some older ++ version though. ++ ++And then, ++- read README in aufs-util, build and install it ++- note that your distribution may contain an obsoleted version of ++ aufs_type.h in /usr/include/linux or something. When you build aufs ++ utilities, make sure that your compiler refers the correct aufs header ++ file which is built by "make headers_install." ++- if you want to use readdir(3) in userspace or pathconf(3) wrapper, ++ then run "make install_ulib" too. And refer to the aufs manual in ++ detail. ++ ++There several other patches in aufs4-standalone.git. They are all ++optional. When you meet some problems, they will help you. ++- aufs4-loopback.patch ++ Supports a nested loopback mount in a branch-fs. This patch is ++ unnecessary until aufs produces a message like "you may want to try ++ another patch for loopback file". ++- vfs-ino.patch ++ Modifies a system global kernel internal function get_next_ino() in ++ order to stop assigning 0 for an inode-number. Not directly related to ++ aufs, but recommended generally. ++- tmpfs-idr.patch ++ Keeps the tmpfs inode number as the lowest value. Effective to reduce ++ the size of aufs XINO files for tmpfs branch. Also it prevents the ++ duplication of inode number, which is important for backup tools and ++ other utilities. When you find aufs XINO files for tmpfs branch ++ growing too much, try this patch. ++ ++ ++4. Usage ++---------------------------------------- ++At first, make sure aufs-util are installed, and please read the aufs ++manual, aufs.5 in aufs-util.git tree. ++$ man -l aufs.5 ++ ++And then, ++$ mkdir /tmp/rw /tmp/aufs ++# mount -t aufs -o br=/tmp/rw:${HOME} none /tmp/aufs ++ ++Here is another example. The result is equivalent. ++# mount -t aufs -o br=/tmp/rw=rw:${HOME}=ro none /tmp/aufs ++ Or ++# mount -t aufs -o br:/tmp/rw none /tmp/aufs ++# mount -o remount,append:${HOME} /tmp/aufs ++ ++Then, you can see whole tree of your home dir through /tmp/aufs. If ++you modify a file under /tmp/aufs, the one on your home directory is ++not affected, instead the same named file will be newly created under ++/tmp/rw. And all of your modification to a file will be applied to ++the one under /tmp/rw. This is called the file based Copy on Write ++(COW) method. ++Aufs mount options are described in aufs.5. ++If you run chroot or something and make your aufs as a root directory, ++then you need to customize the shutdown script. See the aufs manual in ++detail. ++ ++Additionally, there are some sample usages of aufs which are a ++diskless system with network booting, and LiveCD over NFS. ++See sample dir in CVS tree on SourceForge. ++ ++ ++5. Contact ++---------------------------------------- ++When you have any problems or strange behaviour in aufs, please let me ++know with: ++- /proc/mounts (instead of the output of mount(8)) ++- /sys/module/aufs/* ++- /sys/fs/aufs/* (if you have them) ++- /debug/aufs/* (if you have them) ++- linux kernel version ++ if your kernel is not plain, for example modified by distributor, ++ the url where i can download its source is necessary too. ++- aufs version which was printed at loading the module or booting the ++ system, instead of the date you downloaded. ++- configuration (define/undefine CONFIG_AUFS_xxx) ++- kernel configuration or /proc/config.gz (if you have it) ++- behaviour which you think to be incorrect ++- actual operation, reproducible one is better ++- mailto: aufs-users at lists.sourceforge.net ++ ++Usually, I don't watch the Public Areas(Bugs, Support Requests, Patches, ++and Feature Requests) on SourceForge. Please join and write to ++aufs-users ML. ++ ++ ++6. Acknowledgements ++---------------------------------------- ++Thanks to everyone who have tried and are using aufs, whoever ++have reported a bug or any feedback. ++ ++Especially donators: ++Tomas Matejicek(slax.org) made a donation (much more than once). ++ Since Apr 2010, Tomas M (the author of Slax and Linux Live ++ scripts) is making "doubling" donations. ++ Unfortunately I cannot list all of the donators, but I really ++ appreciate. ++ It ends Aug 2010, but the ordinary donation URL is still available. ++ ++Dai Itasaka made a donation (2007/8). ++Chuck Smith made a donation (2008/4, 10 and 12). ++Henk Schoneveld made a donation (2008/9). ++Chih-Wei Huang, ASUS, CTC donated Eee PC 4G (2008/10). ++Francois Dupoux made a donation (2008/11). ++Bruno Cesar Ribas and Luis Carlos Erpen de Bona, C3SL serves public ++ aufs2 GIT tree (2009/2). ++William Grant made a donation (2009/3). ++Patrick Lane made a donation (2009/4). ++The Mail Archive (mail-archive.com) made donations (2009/5). ++Nippy Networks (Ed Wildgoose) made a donation (2009/7). ++New Dream Network, LLC (www.dreamhost.com) made a donation (2009/11). ++Pavel Pronskiy made a donation (2011/2). ++Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy ++ Networks (Ed Wildgoose) made a donation for hardware (2011/3). ++Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and ++11). ++Sam Liddicott made a donation (2011/9). ++Era Scarecrow made a donation (2013/4). ++Bor Ratajc made a donation (2013/4). ++Alessandro Gorreta made a donation (2013/4). ++POIRETTE Marc made a donation (2013/4). ++Alessandro Gorreta made a donation (2013/4). ++lauri kasvandik made a donation (2013/5). ++"pemasu from Finland" made a donation (2013/7). ++The Parted Magic Project made a donation (2013/9 and 11). ++Pavel Barta made a donation (2013/10). ++Nikolay Pertsev made a donation (2014/5). ++James B made a donation (2014/7). ++Stefano Di Biase made a donation (2014/8). ++Daniel Epellei made a donation (2015/1). ++ ++Thank you very much. ++Donations are always, including future donations, very important and ++helpful for me to keep on developing aufs. ++ ++ ++7. ++---------------------------------------- ++If you are an experienced user, no explanation is needed. Aufs is ++just a linux filesystem. ++ ++ ++Enjoy! ++ ++# Local variables: ; ++# mode: text; ++# End: ; +diff -Nur linux-4.0.4.orig/drivers/block/loop.c linux-4.0.4/drivers/block/loop.c +--- linux-4.0.4.orig/drivers/block/loop.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/drivers/block/loop.c 2015-05-30 22:11:30.000000000 +0200 +@@ -592,6 +592,24 @@ + return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR; + } + ++/* ++ * for AUFS ++ * no get/put for file. ++ */ ++struct file *loop_backing_file(struct super_block *sb) ++{ ++ struct file *ret; ++ struct loop_device *l; ++ ++ ret = NULL; ++ if (MAJOR(sb->s_dev) == LOOP_MAJOR) { ++ l = sb->s_bdev->bd_disk->private_data; ++ ret = l->lo_backing_file; ++ } ++ return ret; ++} ++EXPORT_SYMBOL(loop_backing_file); ++ + /* loop sysfs attributes */ + + static ssize_t loop_attr_show(struct device *dev, char *page, +diff -Nur linux-4.0.4.orig/fs/aufs/aufs.h linux-4.0.4/fs/aufs/aufs.h +--- linux-4.0.4.orig/fs/aufs/aufs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/aufs.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,46 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * all header files ++ */ ++ ++#ifndef __AUFS_H__ ++#define __AUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++#define AuStub(type, name, body, ...) \ ++ static inline type name(__VA_ARGS__) { body; } ++ ++#define AuStubVoid(name, ...) \ ++ AuStub(void, name, , __VA_ARGS__) ++#define AuStubInt0(name, ...) \ ++ AuStub(int, name, return 0, __VA_ARGS__) ++ ++#include "debug.h" ++ ++#include "branch.h" ++#include "cpup.h" ++#include "dcsub.h" ++#include "dbgaufs.h" ++#include "dentry.h" ++#include "dir.h" ++#include "dynop.h" ++#include "file.h" ++#include "fstype.h" ++#include "inode.h" ++#include "loop.h" ++#include "module.h" ++#include "opts.h" ++#include "rwsem.h" ++#include "spl.h" ++#include "super.h" ++#include "sysaufs.h" ++#include "vfsub.h" ++#include "whout.h" ++#include "wkq.h" ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/branch.c linux-4.0.4/fs/aufs/branch.c +--- linux-4.0.4.orig/fs/aufs/branch.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/branch.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,1393 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * branch management ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++/* ++ * free a single branch ++ */ ++static void au_br_do_free(struct au_branch *br) ++{ ++ int i; ++ struct au_wbr *wbr; ++ struct au_dykey **key; ++ ++ au_hnotify_fin_br(br); ++ ++ if (br->br_xino.xi_file) ++ fput(br->br_xino.xi_file); ++ mutex_destroy(&br->br_xino.xi_nondir_mtx); ++ ++ AuDebugOn(atomic_read(&br->br_count)); ++ ++ wbr = br->br_wbr; ++ if (wbr) { ++ for (i = 0; i < AuBrWh_Last; i++) ++ dput(wbr->wbr_wh[i]); ++ AuDebugOn(atomic_read(&wbr->wbr_wh_running)); ++ AuRwDestroy(&wbr->wbr_wh_rwsem); ++ } ++ ++ if (br->br_fhsm) { ++ au_br_fhsm_fin(br->br_fhsm); ++ kfree(br->br_fhsm); ++ } ++ ++ key = br->br_dykey; ++ for (i = 0; i < AuBrDynOp; i++, key++) ++ if (*key) ++ au_dy_put(*key); ++ else ++ break; ++ ++ /* recursive lock, s_umount of branch's */ ++ lockdep_off(); ++ path_put(&br->br_path); ++ lockdep_on(); ++ kfree(wbr); ++ kfree(br); ++} ++ ++/* ++ * frees all branches ++ */ ++void au_br_free(struct au_sbinfo *sbinfo) ++{ ++ aufs_bindex_t bmax; ++ struct au_branch **br; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ bmax = sbinfo->si_bend + 1; ++ br = sbinfo->si_branch; ++ while (bmax--) ++ au_br_do_free(*br++); ++} ++ ++/* ++ * find the index of a branch which is specified by @br_id. ++ */ ++int au_br_index(struct super_block *sb, aufs_bindex_t br_id) ++{ ++ aufs_bindex_t bindex, bend; ++ ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (au_sbr_id(sb, bindex) == br_id) ++ return bindex; ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * add a branch ++ */ ++ ++static int test_overlap(struct super_block *sb, struct dentry *h_adding, ++ struct dentry *h_root) ++{ ++ if (unlikely(h_adding == h_root ++ || au_test_loopback_overlap(sb, h_adding))) ++ return 1; ++ if (h_adding->d_sb != h_root->d_sb) ++ return 0; ++ return au_test_subdir(h_adding, h_root) ++ || au_test_subdir(h_root, h_adding); ++} ++ ++/* ++ * returns a newly allocated branch. @new_nbranch is a number of branches ++ * after adding a branch. ++ */ ++static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, ++ int perm) ++{ ++ struct au_branch *add_branch; ++ struct dentry *root; ++ int err; ++ ++ err = -ENOMEM; ++ root = sb->s_root; ++ add_branch = kmalloc(sizeof(*add_branch), GFP_NOFS); ++ if (unlikely(!add_branch)) ++ goto out; ++ ++ err = au_hnotify_init_br(add_branch, perm); ++ if (unlikely(err)) ++ goto out_br; ++ ++ add_branch->br_wbr = NULL; ++ if (au_br_writable(perm)) { ++ /* may be freed separately at changing the branch permission */ ++ add_branch->br_wbr = kmalloc(sizeof(*add_branch->br_wbr), ++ GFP_NOFS); ++ if (unlikely(!add_branch->br_wbr)) ++ goto out_hnotify; ++ } ++ ++ add_branch->br_fhsm = NULL; ++ if (au_br_fhsm(perm)) { ++ err = au_fhsm_br_alloc(add_branch); ++ if (unlikely(err)) ++ goto out_wbr; ++ } ++ ++ err = au_sbr_realloc(au_sbi(sb), new_nbranch); ++ if (!err) ++ err = au_di_realloc(au_di(root), new_nbranch); ++ if (!err) ++ err = au_ii_realloc(au_ii(root->d_inode), new_nbranch); ++ if (!err) ++ return add_branch; /* success */ ++ ++out_wbr: ++ kfree(add_branch->br_wbr); ++out_hnotify: ++ au_hnotify_fin_br(add_branch); ++out_br: ++ kfree(add_branch); ++out: ++ return ERR_PTR(err); ++} ++ ++/* ++ * test if the branch permission is legal or not. ++ */ ++static int test_br(struct inode *inode, int brperm, char *path) ++{ ++ int err; ++ ++ err = (au_br_writable(brperm) && IS_RDONLY(inode)); ++ if (!err) ++ goto out; ++ ++ err = -EINVAL; ++ pr_err("write permission for readonly mount or inode, %s\n", path); ++ ++out: ++ return err; ++} ++ ++/* ++ * returns: ++ * 0: success, the caller will add it ++ * plus: success, it is already unified, the caller should ignore it ++ * minus: error ++ */ ++static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct dentry *root; ++ struct inode *inode, *h_inode; ++ ++ root = sb->s_root; ++ bend = au_sbend(sb); ++ if (unlikely(bend >= 0 ++ && au_find_dbindex(root, add->path.dentry) >= 0)) { ++ err = 1; ++ if (!remount) { ++ err = -EINVAL; ++ pr_err("%s duplicated\n", add->pathname); ++ } ++ goto out; ++ } ++ ++ err = -ENOSPC; /* -E2BIG; */ ++ if (unlikely(AUFS_BRANCH_MAX <= add->bindex ++ || AUFS_BRANCH_MAX - 1 <= bend)) { ++ pr_err("number of branches exceeded %s\n", add->pathname); ++ goto out; ++ } ++ ++ err = -EDOM; ++ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { ++ pr_err("bad index %d\n", add->bindex); ++ goto out; ++ } ++ ++ inode = add->path.dentry->d_inode; ++ err = -ENOENT; ++ if (unlikely(!inode->i_nlink)) { ++ pr_err("no existence %s\n", add->pathname); ++ goto out; ++ } ++ ++ err = -EINVAL; ++ if (unlikely(inode->i_sb == sb)) { ++ pr_err("%s must be outside\n", add->pathname); ++ goto out; ++ } ++ ++ if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) { ++ pr_err("unsupported filesystem, %s (%s)\n", ++ add->pathname, au_sbtype(inode->i_sb)); ++ goto out; ++ } ++ ++ if (unlikely(inode->i_sb->s_stack_depth)) { ++ pr_err("already stacked, %s (%s)\n", ++ add->pathname, au_sbtype(inode->i_sb)); ++ goto out; ++ } ++ ++ err = test_br(add->path.dentry->d_inode, add->perm, add->pathname); ++ if (unlikely(err)) ++ goto out; ++ ++ if (bend < 0) ++ return 0; /* success */ ++ ++ err = -EINVAL; ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (unlikely(test_overlap(sb, add->path.dentry, ++ au_h_dptr(root, bindex)))) { ++ pr_err("%s is overlapped\n", add->pathname); ++ goto out; ++ } ++ ++ err = 0; ++ if (au_opt_test(au_mntflags(sb), WARN_PERM)) { ++ h_inode = au_h_dptr(root, 0)->d_inode; ++ if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO) ++ || !uid_eq(h_inode->i_uid, inode->i_uid) ++ || !gid_eq(h_inode->i_gid, inode->i_gid)) ++ pr_warn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", ++ add->pathname, ++ i_uid_read(inode), i_gid_read(inode), ++ (inode->i_mode & S_IALLUGO), ++ i_uid_read(h_inode), i_gid_read(h_inode), ++ (h_inode->i_mode & S_IALLUGO)); ++ } ++ ++out: ++ return err; ++} ++ ++/* ++ * initialize or clean the whiteouts for an adding branch ++ */ ++static int au_br_init_wh(struct super_block *sb, struct au_branch *br, ++ int new_perm) ++{ ++ int err, old_perm; ++ aufs_bindex_t bindex; ++ struct mutex *h_mtx; ++ struct au_wbr *wbr; ++ struct au_hinode *hdir; ++ ++ err = vfsub_mnt_want_write(au_br_mnt(br)); ++ if (unlikely(err)) ++ goto out; ++ ++ wbr = br->br_wbr; ++ old_perm = br->br_perm; ++ br->br_perm = new_perm; ++ hdir = NULL; ++ h_mtx = NULL; ++ bindex = au_br_index(sb, br->br_id); ++ if (0 <= bindex) { ++ hdir = au_hi(sb->s_root->d_inode, bindex); ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ } else { ++ h_mtx = &au_br_dentry(br)->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_PARENT); ++ } ++ if (!wbr) ++ err = au_wh_init(br, sb); ++ else { ++ wbr_wh_write_lock(wbr); ++ err = au_wh_init(br, sb); ++ wbr_wh_write_unlock(wbr); ++ } ++ if (hdir) ++ au_hn_imtx_unlock(hdir); ++ else ++ mutex_unlock(h_mtx); ++ vfsub_mnt_drop_write(au_br_mnt(br)); ++ br->br_perm = old_perm; ++ ++ if (!err && wbr && !au_br_writable(new_perm)) { ++ kfree(wbr); ++ br->br_wbr = NULL; ++ } ++ ++out: ++ return err; ++} ++ ++static int au_wbr_init(struct au_branch *br, struct super_block *sb, ++ int perm) ++{ ++ int err; ++ struct kstatfs kst; ++ struct au_wbr *wbr; ++ ++ wbr = br->br_wbr; ++ au_rw_init(&wbr->wbr_wh_rwsem); ++ memset(wbr->wbr_wh, 0, sizeof(wbr->wbr_wh)); ++ atomic_set(&wbr->wbr_wh_running, 0); ++ wbr->wbr_bytes = 0; ++ ++ /* ++ * a limit for rmdir/rename a dir ++ * cf. AUFS_MAX_NAMELEN in include/uapi/linux/aufs_type.h ++ */ ++ err = vfs_statfs(&br->br_path, &kst); ++ if (unlikely(err)) ++ goto out; ++ err = -EINVAL; ++ if (kst.f_namelen >= NAME_MAX) ++ err = au_br_init_wh(sb, br, perm); ++ else ++ pr_err("%pd(%s), unsupported namelen %ld\n", ++ au_br_dentry(br), ++ au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); ++ ++out: ++ return err; ++} ++ ++/* initialize a new branch */ ++static int au_br_init(struct au_branch *br, struct super_block *sb, ++ struct au_opt_add *add) ++{ ++ int err; ++ ++ err = 0; ++ memset(&br->br_xino, 0, sizeof(br->br_xino)); ++ mutex_init(&br->br_xino.xi_nondir_mtx); ++ br->br_perm = add->perm; ++ br->br_path = add->path; /* set first, path_get() later */ ++ spin_lock_init(&br->br_dykey_lock); ++ memset(br->br_dykey, 0, sizeof(br->br_dykey)); ++ atomic_set(&br->br_count, 0); ++ atomic_set(&br->br_xino_running, 0); ++ br->br_id = au_new_br_id(sb); ++ AuDebugOn(br->br_id < 0); ++ ++ if (au_br_writable(add->perm)) { ++ err = au_wbr_init(br, sb, add->perm); ++ if (unlikely(err)) ++ goto out_err; ++ } ++ ++ if (au_opt_test(au_mntflags(sb), XINO)) { ++ err = au_xino_br(sb, br, add->path.dentry->d_inode->i_ino, ++ au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1); ++ if (unlikely(err)) { ++ AuDebugOn(br->br_xino.xi_file); ++ goto out_err; ++ } ++ } ++ ++ sysaufs_br_init(br); ++ path_get(&br->br_path); ++ goto out; /* success */ ++ ++out_err: ++ memset(&br->br_path, 0, sizeof(br->br_path)); ++out: ++ return err; ++} ++ ++static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, ++ struct au_branch *br, aufs_bindex_t bend, ++ aufs_bindex_t amount) ++{ ++ struct au_branch **brp; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ brp = sbinfo->si_branch + bindex; ++ memmove(brp + 1, brp, sizeof(*brp) * amount); ++ *brp = br; ++ sbinfo->si_bend++; ++ if (unlikely(bend < 0)) ++ sbinfo->si_bend = 0; ++} ++ ++static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, ++ aufs_bindex_t bend, aufs_bindex_t amount) ++{ ++ struct au_hdentry *hdp; ++ ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ hdp = dinfo->di_hdentry + bindex; ++ memmove(hdp + 1, hdp, sizeof(*hdp) * amount); ++ au_h_dentry_init(hdp); ++ dinfo->di_bend++; ++ if (unlikely(bend < 0)) ++ dinfo->di_bstart = 0; ++} ++ ++static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, ++ aufs_bindex_t bend, aufs_bindex_t amount) ++{ ++ struct au_hinode *hip; ++ ++ AuRwMustWriteLock(&iinfo->ii_rwsem); ++ ++ hip = iinfo->ii_hinode + bindex; ++ memmove(hip + 1, hip, sizeof(*hip) * amount); ++ hip->hi_inode = NULL; ++ au_hn_init(hip); ++ iinfo->ii_bend++; ++ if (unlikely(bend < 0)) ++ iinfo->ii_bstart = 0; ++} ++ ++static void au_br_do_add(struct super_block *sb, struct au_branch *br, ++ aufs_bindex_t bindex) ++{ ++ struct dentry *root, *h_dentry; ++ struct inode *root_inode; ++ aufs_bindex_t bend, amount; ++ ++ root = sb->s_root; ++ root_inode = root->d_inode; ++ bend = au_sbend(sb); ++ amount = bend + 1 - bindex; ++ h_dentry = au_br_dentry(br); ++ au_sbilist_lock(); ++ au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); ++ au_br_do_add_hdp(au_di(root), bindex, bend, amount); ++ au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); ++ au_set_h_dptr(root, bindex, dget(h_dentry)); ++ au_set_h_iptr(root_inode, bindex, au_igrab(h_dentry->d_inode), ++ /*flags*/0); ++ au_sbilist_unlock(); ++} ++ ++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) ++{ ++ int err; ++ aufs_bindex_t bend, add_bindex; ++ struct dentry *root, *h_dentry; ++ struct inode *root_inode; ++ struct au_branch *add_branch; ++ ++ root = sb->s_root; ++ root_inode = root->d_inode; ++ IMustLock(root_inode); ++ err = test_add(sb, add, remount); ++ if (unlikely(err < 0)) ++ goto out; ++ if (err) { ++ err = 0; ++ goto out; /* success */ ++ } ++ ++ bend = au_sbend(sb); ++ add_branch = au_br_alloc(sb, bend + 2, add->perm); ++ err = PTR_ERR(add_branch); ++ if (IS_ERR(add_branch)) ++ goto out; ++ ++ err = au_br_init(add_branch, sb, add); ++ if (unlikely(err)) { ++ au_br_do_free(add_branch); ++ goto out; ++ } ++ ++ add_bindex = add->bindex; ++ if (!remount) ++ au_br_do_add(sb, add_branch, add_bindex); ++ else { ++ sysaufs_brs_del(sb, add_bindex); ++ au_br_do_add(sb, add_branch, add_bindex); ++ sysaufs_brs_add(sb, add_bindex); ++ } ++ ++ h_dentry = add->path.dentry; ++ if (!add_bindex) { ++ au_cpup_attr_all(root_inode, /*force*/1); ++ sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; ++ } else ++ au_add_nlink(root_inode, h_dentry->d_inode); ++ ++ /* ++ * this test/set prevents aufs from handling unnecesary notify events ++ * of xino files, in case of re-adding a writable branch which was ++ * once detached from aufs. ++ */ ++ if (au_xino_brid(sb) < 0 ++ && au_br_writable(add_branch->br_perm) ++ && !au_test_fs_bad_xino(h_dentry->d_sb) ++ && add_branch->br_xino.xi_file ++ && add_branch->br_xino.xi_file->f_path.dentry->d_parent == h_dentry) ++ au_xino_brid_set(sb, add_branch->br_id); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static unsigned long long au_farray_cb(void *a, ++ unsigned long long max __maybe_unused, ++ void *arg) ++{ ++ unsigned long long n; ++ struct file **p, *f; ++ struct au_sphlhead *files; ++ struct au_finfo *finfo; ++ struct super_block *sb = arg; ++ ++ n = 0; ++ p = a; ++ files = &au_sbi(sb)->si_files; ++ spin_lock(&files->spin); ++ hlist_for_each_entry(finfo, &files->head, fi_hlist) { ++ f = finfo->fi_file; ++ if (file_count(f) ++ && !special_file(file_inode(f)->i_mode)) { ++ get_file(f); ++ *p++ = f; ++ n++; ++ AuDebugOn(n > max); ++ } ++ } ++ spin_unlock(&files->spin); ++ ++ return n; ++} ++ ++static struct file **au_farray_alloc(struct super_block *sb, ++ unsigned long long *max) ++{ ++ *max = atomic_long_read(&au_sbi(sb)->si_nfiles); ++ return au_array_alloc(max, au_farray_cb, sb); ++} ++ ++static void au_farray_free(struct file **a, unsigned long long max) ++{ ++ unsigned long long ull; ++ ++ for (ull = 0; ull < max; ull++) ++ if (a[ull]) ++ fput(a[ull]); ++ au_array_free(a); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * delete a branch ++ */ ++ ++/* to show the line number, do not make it inlined function */ ++#define AuVerbose(do_info, fmt, ...) do { \ ++ if (do_info) \ ++ pr_info(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++static int au_test_ibusy(struct inode *inode, aufs_bindex_t bstart, ++ aufs_bindex_t bend) ++{ ++ return (inode && !S_ISDIR(inode->i_mode)) || bstart == bend; ++} ++ ++static int au_test_dbusy(struct dentry *dentry, aufs_bindex_t bstart, ++ aufs_bindex_t bend) ++{ ++ return au_test_ibusy(dentry->d_inode, bstart, bend); ++} ++ ++/* ++ * test if the branch is deletable or not. ++ */ ++static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, ++ unsigned int sigen, const unsigned int verbose) ++{ ++ int err, i, j, ndentry; ++ aufs_bindex_t bstart, bend; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry *d; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, root, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ for (i = 0; !err && i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ ndentry = dpage->ndentry; ++ for (j = 0; !err && j < ndentry; j++) { ++ d = dpage->dentries[j]; ++ AuDebugOn(au_dcount(d) <= 0); ++ if (!au_digen_test(d, sigen)) { ++ di_read_lock_child(d, AuLock_IR); ++ if (unlikely(au_dbrange_test(d))) { ++ di_read_unlock(d, AuLock_IR); ++ continue; ++ } ++ } else { ++ di_write_lock_child(d); ++ if (unlikely(au_dbrange_test(d))) { ++ di_write_unlock(d); ++ continue; ++ } ++ err = au_reval_dpath(d, sigen); ++ if (!err) ++ di_downgrade_lock(d, AuLock_IR); ++ else { ++ di_write_unlock(d); ++ break; ++ } ++ } ++ ++ /* AuDbgDentry(d); */ ++ bstart = au_dbstart(d); ++ bend = au_dbend(d); ++ if (bstart <= bindex ++ && bindex <= bend ++ && au_h_dptr(d, bindex) ++ && au_test_dbusy(d, bstart, bend)) { ++ err = -EBUSY; ++ AuVerbose(verbose, "busy %pd\n", d); ++ AuDbgDentry(d); ++ } ++ di_read_unlock(d, AuLock_IR); ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, ++ unsigned int sigen, const unsigned int verbose) ++{ ++ int err; ++ unsigned long long max, ull; ++ struct inode *i, **array; ++ aufs_bindex_t bstart, bend; ++ ++ array = au_iarray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ err = 0; ++ AuDbg("b%d\n", bindex); ++ for (ull = 0; !err && ull < max; ull++) { ++ i = array[ull]; ++ if (unlikely(!i)) ++ break; ++ if (i->i_ino == AUFS_ROOT_INO) ++ continue; ++ ++ /* AuDbgInode(i); */ ++ if (au_iigen(i, NULL) == sigen) ++ ii_read_lock_child(i); ++ else { ++ ii_write_lock_child(i); ++ err = au_refresh_hinode_self(i); ++ au_iigen_dec(i); ++ if (!err) ++ ii_downgrade_lock(i); ++ else { ++ ii_write_unlock(i); ++ break; ++ } ++ } ++ ++ bstart = au_ibstart(i); ++ bend = au_ibend(i); ++ if (bstart <= bindex ++ && bindex <= bend ++ && au_h_iptr(i, bindex) ++ && au_test_ibusy(i, bstart, bend)) { ++ err = -EBUSY; ++ AuVerbose(verbose, "busy i%lu\n", i->i_ino); ++ AuDbgInode(i); ++ } ++ ii_read_unlock(i); ++ } ++ au_iarray_free(array, max); ++ ++out: ++ return err; ++} ++ ++static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, ++ const unsigned int verbose) ++{ ++ int err; ++ unsigned int sigen; ++ ++ sigen = au_sigen(root->d_sb); ++ DiMustNoWaiters(root); ++ IiMustNoWaiters(root->d_inode); ++ di_write_unlock(root); ++ err = test_dentry_busy(root, bindex, sigen, verbose); ++ if (!err) ++ err = test_inode_busy(root->d_sb, bindex, sigen, verbose); ++ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ ++ ++ return err; ++} ++ ++static int test_dir_busy(struct file *file, aufs_bindex_t br_id, ++ struct file **to_free, int *idx) ++{ ++ int err; ++ unsigned char matched, root; ++ aufs_bindex_t bindex, bend; ++ struct au_fidir *fidir; ++ struct au_hfile *hfile; ++ ++ err = 0; ++ root = IS_ROOT(file->f_path.dentry); ++ if (root) { ++ get_file(file); ++ to_free[*idx] = file; ++ (*idx)++; ++ goto out; ++ } ++ ++ matched = 0; ++ fidir = au_fi(file)->fi_hdir; ++ AuDebugOn(!fidir); ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); bindex <= bend; bindex++) { ++ hfile = fidir->fd_hfile + bindex; ++ if (!hfile->hf_file) ++ continue; ++ ++ if (hfile->hf_br->br_id == br_id) { ++ matched = 1; ++ break; ++ } ++ } ++ if (matched) ++ err = -EBUSY; ++ ++out: ++ return err; ++} ++ ++static int test_file_busy(struct super_block *sb, aufs_bindex_t br_id, ++ struct file **to_free, int opened) ++{ ++ int err, idx; ++ unsigned long long ull, max; ++ aufs_bindex_t bstart; ++ struct file *file, **array; ++ struct dentry *root; ++ struct au_hfile *hfile; ++ ++ array = au_farray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ err = 0; ++ idx = 0; ++ root = sb->s_root; ++ di_write_unlock(root); ++ for (ull = 0; ull < max; ull++) { ++ file = array[ull]; ++ if (unlikely(!file)) ++ break; ++ ++ /* AuDbg("%pD\n", file); */ ++ fi_read_lock(file); ++ bstart = au_fbstart(file); ++ if (!d_is_dir(file->f_path.dentry)) { ++ hfile = &au_fi(file)->fi_htop; ++ if (hfile->hf_br->br_id == br_id) ++ err = -EBUSY; ++ } else ++ err = test_dir_busy(file, br_id, to_free, &idx); ++ fi_read_unlock(file); ++ if (unlikely(err)) ++ break; ++ } ++ di_write_lock_child(root); ++ au_farray_free(array, max); ++ AuDebugOn(idx > opened); ++ ++out: ++ return err; ++} ++ ++static void br_del_file(struct file **to_free, unsigned long long opened, ++ aufs_bindex_t br_id) ++{ ++ unsigned long long ull; ++ aufs_bindex_t bindex, bstart, bend, bfound; ++ struct file *file; ++ struct au_fidir *fidir; ++ struct au_hfile *hfile; ++ ++ for (ull = 0; ull < opened; ull++) { ++ file = to_free[ull]; ++ if (unlikely(!file)) ++ break; ++ ++ /* AuDbg("%pD\n", file); */ ++ AuDebugOn(!d_is_dir(file->f_path.dentry)); ++ bfound = -1; ++ fidir = au_fi(file)->fi_hdir; ++ AuDebugOn(!fidir); ++ fi_write_lock(file); ++ bstart = au_fbstart(file); ++ bend = au_fbend_dir(file); ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ hfile = fidir->fd_hfile + bindex; ++ if (!hfile->hf_file) ++ continue; ++ ++ if (hfile->hf_br->br_id == br_id) { ++ bfound = bindex; ++ break; ++ } ++ } ++ AuDebugOn(bfound < 0); ++ au_set_h_fptr(file, bfound, NULL); ++ if (bfound == bstart) { ++ for (bstart++; bstart <= bend; bstart++) ++ if (au_hf_dir(file, bstart)) { ++ au_set_fbstart(file, bstart); ++ break; ++ } ++ } ++ fi_write_unlock(file); ++ } ++} ++ ++static void au_br_do_del_brp(struct au_sbinfo *sbinfo, ++ const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_branch **brp, **p; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ brp = sbinfo->si_branch + bindex; ++ if (bindex < bend) ++ memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex)); ++ sbinfo->si_branch[0 + bend] = NULL; ++ sbinfo->si_bend--; ++ ++ p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, AuGFP_SBILIST); ++ if (p) ++ sbinfo->si_branch = p; ++ /* harmless error */ ++} ++ ++static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_hdentry *hdp, *p; ++ ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ hdp = dinfo->di_hdentry; ++ if (bindex < bend) ++ memmove(hdp + bindex, hdp + bindex + 1, ++ sizeof(*hdp) * (bend - bindex)); ++ hdp[0 + bend].hd_dentry = NULL; ++ dinfo->di_bend--; ++ ++ p = krealloc(hdp, sizeof(*p) * bend, AuGFP_SBILIST); ++ if (p) ++ dinfo->di_hdentry = p; ++ /* harmless error */ ++} ++ ++static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_hinode *hip, *p; ++ ++ AuRwMustWriteLock(&iinfo->ii_rwsem); ++ ++ hip = iinfo->ii_hinode + bindex; ++ if (bindex < bend) ++ memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex)); ++ iinfo->ii_hinode[0 + bend].hi_inode = NULL; ++ au_hn_init(iinfo->ii_hinode + bend); ++ iinfo->ii_bend--; ++ ++ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, AuGFP_SBILIST); ++ if (p) ++ iinfo->ii_hinode = p; ++ /* harmless error */ ++} ++ ++static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_branch *br) ++{ ++ aufs_bindex_t bend; ++ struct au_sbinfo *sbinfo; ++ struct dentry *root, *h_root; ++ struct inode *inode, *h_inode; ++ struct au_hinode *hinode; ++ ++ SiMustWriteLock(sb); ++ ++ root = sb->s_root; ++ inode = root->d_inode; ++ sbinfo = au_sbi(sb); ++ bend = sbinfo->si_bend; ++ ++ h_root = au_h_dptr(root, bindex); ++ hinode = au_hi(inode, bindex); ++ h_inode = au_igrab(hinode->hi_inode); ++ au_hiput(hinode); ++ ++ au_sbilist_lock(); ++ au_br_do_del_brp(sbinfo, bindex, bend); ++ au_br_do_del_hdp(au_di(root), bindex, bend); ++ au_br_do_del_hip(au_ii(inode), bindex, bend); ++ au_sbilist_unlock(); ++ ++ dput(h_root); ++ iput(h_inode); ++ au_br_do_free(br); ++} ++ ++static unsigned long long empty_cb(void *array, unsigned long long max, ++ void *arg) ++{ ++ return max; ++} ++ ++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) ++{ ++ int err, rerr, i; ++ unsigned long long opened; ++ unsigned int mnt_flags; ++ aufs_bindex_t bindex, bend, br_id; ++ unsigned char do_wh, verbose; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ struct dentry *root; ++ struct file **to_free; ++ ++ err = 0; ++ opened = 0; ++ to_free = NULL; ++ root = sb->s_root; ++ bindex = au_find_dbindex(root, del->h_path.dentry); ++ if (bindex < 0) { ++ if (remount) ++ goto out; /* success */ ++ err = -ENOENT; ++ pr_err("%s no such branch\n", del->pathname); ++ goto out; ++ } ++ AuDbg("bindex b%d\n", bindex); ++ ++ err = -EBUSY; ++ mnt_flags = au_mntflags(sb); ++ verbose = !!au_opt_test(mnt_flags, VERBOSE); ++ bend = au_sbend(sb); ++ if (unlikely(!bend)) { ++ AuVerbose(verbose, "no more branches left\n"); ++ goto out; ++ } ++ br = au_sbr(sb, bindex); ++ AuDebugOn(!path_equal(&br->br_path, &del->h_path)); ++ ++ br_id = br->br_id; ++ opened = atomic_read(&br->br_count); ++ if (unlikely(opened)) { ++ to_free = au_array_alloc(&opened, empty_cb, NULL); ++ err = PTR_ERR(to_free); ++ if (IS_ERR(to_free)) ++ goto out; ++ ++ err = test_file_busy(sb, br_id, to_free, opened); ++ if (unlikely(err)) { ++ AuVerbose(verbose, "%llu file(s) opened\n", opened); ++ goto out; ++ } ++ } ++ ++ wbr = br->br_wbr; ++ do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph); ++ if (do_wh) { ++ /* instead of WbrWhMustWriteLock(wbr) */ ++ SiMustWriteLock(sb); ++ for (i = 0; i < AuBrWh_Last; i++) { ++ dput(wbr->wbr_wh[i]); ++ wbr->wbr_wh[i] = NULL; ++ } ++ } ++ ++ err = test_children_busy(root, bindex, verbose); ++ if (unlikely(err)) { ++ if (do_wh) ++ goto out_wh; ++ goto out; ++ } ++ ++ err = 0; ++ if (to_free) { ++ /* ++ * now we confirmed the branch is deletable. ++ * let's free the remaining opened dirs on the branch. ++ */ ++ di_write_unlock(root); ++ br_del_file(to_free, opened, br_id); ++ di_write_lock_child(root); ++ } ++ ++ if (!remount) ++ au_br_do_del(sb, bindex, br); ++ else { ++ sysaufs_brs_del(sb, bindex); ++ au_br_do_del(sb, bindex, br); ++ sysaufs_brs_add(sb, bindex); ++ } ++ ++ if (!bindex) { ++ au_cpup_attr_all(root->d_inode, /*force*/1); ++ sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes; ++ } else ++ au_sub_nlink(root->d_inode, del->h_path.dentry->d_inode); ++ if (au_opt_test(mnt_flags, PLINK)) ++ au_plink_half_refresh(sb, br_id); ++ ++ if (au_xino_brid(sb) == br_id) ++ au_xino_brid_set(sb, -1); ++ goto out; /* success */ ++ ++out_wh: ++ /* revert */ ++ rerr = au_br_init_wh(sb, br, br->br_perm); ++ if (rerr) ++ pr_warn("failed re-creating base whiteout, %s. (%d)\n", ++ del->pathname, rerr); ++out: ++ if (to_free) ++ au_farray_free(to_free, opened); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_ibusy(struct super_block *sb, struct aufs_ibusy __user *arg) ++{ ++ int err; ++ aufs_bindex_t bstart, bend; ++ struct aufs_ibusy ibusy; ++ struct inode *inode, *h_inode; ++ ++ err = -EPERM; ++ if (unlikely(!capable(CAP_SYS_ADMIN))) ++ goto out; ++ ++ err = copy_from_user(&ibusy, arg, sizeof(ibusy)); ++ if (!err) ++ err = !access_ok(VERIFY_WRITE, &arg->h_ino, sizeof(arg->h_ino)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ ++ err = -EINVAL; ++ si_read_lock(sb, AuLock_FLUSH); ++ if (unlikely(ibusy.bindex < 0 || ibusy.bindex > au_sbend(sb))) ++ goto out_unlock; ++ ++ err = 0; ++ ibusy.h_ino = 0; /* invalid */ ++ inode = ilookup(sb, ibusy.ino); ++ if (!inode ++ || inode->i_ino == AUFS_ROOT_INO ++ || is_bad_inode(inode)) ++ goto out_unlock; ++ ++ ii_read_lock_child(inode); ++ bstart = au_ibstart(inode); ++ bend = au_ibend(inode); ++ if (bstart <= ibusy.bindex && ibusy.bindex <= bend) { ++ h_inode = au_h_iptr(inode, ibusy.bindex); ++ if (h_inode && au_test_ibusy(inode, bstart, bend)) ++ ibusy.h_ino = h_inode->i_ino; ++ } ++ ii_read_unlock(inode); ++ iput(inode); ++ ++out_unlock: ++ si_read_unlock(sb); ++ if (!err) { ++ err = __put_user(ibusy.h_ino, &arg->h_ino); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ } ++ } ++out: ++ return err; ++} ++ ++long au_ibusy_ioctl(struct file *file, unsigned long arg) ++{ ++ return au_ibusy(file->f_path.dentry->d_sb, (void __user *)arg); ++} ++ ++#ifdef CONFIG_COMPAT ++long au_ibusy_compat_ioctl(struct file *file, unsigned long arg) ++{ ++ return au_ibusy(file->f_path.dentry->d_sb, compat_ptr(arg)); ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * change a branch permission ++ */ ++ ++static void au_warn_ima(void) ++{ ++#ifdef CONFIG_IMA ++ /* since it doesn't support mark_files_ro() */ ++ AuWarn1("RW -> RO makes IMA to produce wrong message\n"); ++#endif ++} ++ ++static int do_need_sigen_inc(int a, int b) ++{ ++ return au_br_whable(a) && !au_br_whable(b); ++} ++ ++static int need_sigen_inc(int old, int new) ++{ ++ return do_need_sigen_inc(old, new) ++ || do_need_sigen_inc(new, old); ++} ++ ++static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err, do_warn; ++ unsigned int mnt_flags; ++ unsigned long long ull, max; ++ aufs_bindex_t br_id; ++ unsigned char verbose, writer; ++ struct file *file, *hf, **array; ++ struct au_hfile *hfile; ++ ++ mnt_flags = au_mntflags(sb); ++ verbose = !!au_opt_test(mnt_flags, VERBOSE); ++ ++ array = au_farray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ do_warn = 0; ++ br_id = au_sbr_id(sb, bindex); ++ for (ull = 0; ull < max; ull++) { ++ file = array[ull]; ++ if (unlikely(!file)) ++ break; ++ ++ /* AuDbg("%pD\n", file); */ ++ fi_read_lock(file); ++ if (unlikely(au_test_mmapped(file))) { ++ err = -EBUSY; ++ AuVerbose(verbose, "mmapped %pD\n", file); ++ AuDbgFile(file); ++ FiMustNoWaiters(file); ++ fi_read_unlock(file); ++ goto out_array; ++ } ++ ++ hfile = &au_fi(file)->fi_htop; ++ hf = hfile->hf_file; ++ if (!d_is_reg(file->f_path.dentry) ++ || !(file->f_mode & FMODE_WRITE) ++ || hfile->hf_br->br_id != br_id ++ || !(hf->f_mode & FMODE_WRITE)) ++ array[ull] = NULL; ++ else { ++ do_warn = 1; ++ get_file(file); ++ } ++ ++ FiMustNoWaiters(file); ++ fi_read_unlock(file); ++ fput(file); ++ } ++ ++ err = 0; ++ if (do_warn) ++ au_warn_ima(); ++ ++ for (ull = 0; ull < max; ull++) { ++ file = array[ull]; ++ if (!file) ++ continue; ++ ++ /* todo: already flushed? */ ++ /* ++ * fs/super.c:mark_files_ro() is gone, but aufs keeps its ++ * approach which resets f_mode and calls mnt_drop_write() and ++ * file_release_write() for each file, because the branch ++ * attribute in aufs world is totally different from the native ++ * fs rw/ro mode. ++ */ ++ /* fi_read_lock(file); */ ++ hfile = &au_fi(file)->fi_htop; ++ hf = hfile->hf_file; ++ /* fi_read_unlock(file); */ ++ spin_lock(&hf->f_lock); ++ writer = !!(hf->f_mode & FMODE_WRITER); ++ hf->f_mode &= ~(FMODE_WRITE | FMODE_WRITER); ++ spin_unlock(&hf->f_lock); ++ if (writer) { ++ put_write_access(file_inode(hf)); ++ __mnt_drop_write(hf->f_path.mnt); ++ } ++ } ++ ++out_array: ++ au_farray_free(array, max); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, ++ int *do_refresh) ++{ ++ int err, rerr; ++ aufs_bindex_t bindex; ++ struct dentry *root; ++ struct au_branch *br; ++ struct au_br_fhsm *bf; ++ ++ root = sb->s_root; ++ bindex = au_find_dbindex(root, mod->h_root); ++ if (bindex < 0) { ++ if (remount) ++ return 0; /* success */ ++ err = -ENOENT; ++ pr_err("%s no such branch\n", mod->path); ++ goto out; ++ } ++ AuDbg("bindex b%d\n", bindex); ++ ++ err = test_br(mod->h_root->d_inode, mod->perm, mod->path); ++ if (unlikely(err)) ++ goto out; ++ ++ br = au_sbr(sb, bindex); ++ AuDebugOn(mod->h_root != au_br_dentry(br)); ++ if (br->br_perm == mod->perm) ++ return 0; /* success */ ++ ++ /* pre-allocate for non-fhsm --> fhsm */ ++ bf = NULL; ++ if (!au_br_fhsm(br->br_perm) && au_br_fhsm(mod->perm)) { ++ err = au_fhsm_br_alloc(br); ++ if (unlikely(err)) ++ goto out; ++ bf = br->br_fhsm; ++ br->br_fhsm = NULL; ++ } ++ ++ if (au_br_writable(br->br_perm)) { ++ /* remove whiteout base */ ++ err = au_br_init_wh(sb, br, mod->perm); ++ if (unlikely(err)) ++ goto out_bf; ++ ++ if (!au_br_writable(mod->perm)) { ++ /* rw --> ro, file might be mmapped */ ++ DiMustNoWaiters(root); ++ IiMustNoWaiters(root->d_inode); ++ di_write_unlock(root); ++ err = au_br_mod_files_ro(sb, bindex); ++ /* aufs_write_lock() calls ..._child() */ ++ di_write_lock_child(root); ++ ++ if (unlikely(err)) { ++ rerr = -ENOMEM; ++ br->br_wbr = kmalloc(sizeof(*br->br_wbr), ++ GFP_NOFS); ++ if (br->br_wbr) ++ rerr = au_wbr_init(br, sb, br->br_perm); ++ if (unlikely(rerr)) { ++ AuIOErr("nested error %d (%d)\n", ++ rerr, err); ++ br->br_perm = mod->perm; ++ } ++ } ++ } ++ } else if (au_br_writable(mod->perm)) { ++ /* ro --> rw */ ++ err = -ENOMEM; ++ br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS); ++ if (br->br_wbr) { ++ err = au_wbr_init(br, sb, mod->perm); ++ if (unlikely(err)) { ++ kfree(br->br_wbr); ++ br->br_wbr = NULL; ++ } ++ } ++ } ++ if (unlikely(err)) ++ goto out_bf; ++ ++ if (au_br_fhsm(br->br_perm)) { ++ if (!au_br_fhsm(mod->perm)) { ++ /* fhsm --> non-fhsm */ ++ au_br_fhsm_fin(br->br_fhsm); ++ kfree(br->br_fhsm); ++ br->br_fhsm = NULL; ++ } ++ } else if (au_br_fhsm(mod->perm)) ++ /* non-fhsm --> fhsm */ ++ br->br_fhsm = bf; ++ ++ *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); ++ br->br_perm = mod->perm; ++ goto out; /* success */ ++ ++out_bf: ++ kfree(bf); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs) ++{ ++ int err; ++ struct kstatfs kstfs; ++ ++ err = vfs_statfs(&br->br_path, &kstfs); ++ if (!err) { ++ stfs->f_blocks = kstfs.f_blocks; ++ stfs->f_bavail = kstfs.f_bavail; ++ stfs->f_files = kstfs.f_files; ++ stfs->f_ffree = kstfs.f_ffree; ++ } ++ ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/branch.h linux-4.0.4/fs/aufs/branch.h +--- linux-4.0.4.orig/fs/aufs/branch.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/branch.h 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,266 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * branch filesystems and xino for them ++ */ ++ ++#ifndef __AUFS_BRANCH_H__ ++#define __AUFS_BRANCH_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "dynop.h" ++#include "rwsem.h" ++#include "super.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* a xino file */ ++struct au_xino_file { ++ struct file *xi_file; ++ struct mutex xi_nondir_mtx; ++ ++ /* todo: make xino files an array to support huge inode number */ ++ ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *xi_dbgaufs; ++#endif ++}; ++ ++/* File-based Hierarchical Storage Management */ ++struct au_br_fhsm { ++#ifdef CONFIG_AUFS_FHSM ++ struct mutex bf_lock; ++ unsigned long bf_jiffy; ++ struct aufs_stfs bf_stfs; ++ int bf_readable; ++#endif ++}; ++ ++/* members for writable branch only */ ++enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last}; ++struct au_wbr { ++ struct au_rwsem wbr_wh_rwsem; ++ struct dentry *wbr_wh[AuBrWh_Last]; ++ atomic_t wbr_wh_running; ++#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */ ++#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */ ++#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */ ++ ++ /* mfs mode */ ++ unsigned long long wbr_bytes; ++}; ++ ++/* ext2 has 3 types of operations at least, ext3 has 4 */ ++#define AuBrDynOp (AuDyLast * 4) ++ ++#ifdef CONFIG_AUFS_HFSNOTIFY ++/* support for asynchronous destruction */ ++struct au_br_hfsnotify { ++ struct fsnotify_group *hfsn_group; ++}; ++#endif ++ ++/* sysfs entries */ ++struct au_brsysfs { ++ char name[16]; ++ struct attribute attr; ++}; ++ ++enum { ++ AuBrSysfs_BR, ++ AuBrSysfs_BRID, ++ AuBrSysfs_Last ++}; ++ ++/* protected by superblock rwsem */ ++struct au_branch { ++ struct au_xino_file br_xino; ++ ++ aufs_bindex_t br_id; ++ ++ int br_perm; ++ struct path br_path; ++ spinlock_t br_dykey_lock; ++ struct au_dykey *br_dykey[AuBrDynOp]; ++ atomic_t br_count; ++ ++ struct au_wbr *br_wbr; ++ struct au_br_fhsm *br_fhsm; ++ ++ /* xino truncation */ ++ atomic_t br_xino_running; ++ ++#ifdef CONFIG_AUFS_HFSNOTIFY ++ struct au_br_hfsnotify *br_hfsn; ++#endif ++ ++#ifdef CONFIG_SYSFS ++ /* entries under sysfs per mount-point */ ++ struct au_brsysfs br_sysfs[AuBrSysfs_Last]; ++#endif ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct vfsmount *au_br_mnt(struct au_branch *br) ++{ ++ return br->br_path.mnt; ++} ++ ++static inline struct dentry *au_br_dentry(struct au_branch *br) ++{ ++ return br->br_path.dentry; ++} ++ ++static inline struct super_block *au_br_sb(struct au_branch *br) ++{ ++ return au_br_mnt(br)->mnt_sb; ++} ++ ++static inline int au_br_rdonly(struct au_branch *br) ++{ ++ return ((au_br_sb(br)->s_flags & MS_RDONLY) ++ || !au_br_writable(br->br_perm)) ++ ? -EROFS : 0; ++} ++ ++static inline int au_br_hnotifyable(int brperm __maybe_unused) ++{ ++#ifdef CONFIG_AUFS_HNOTIFY ++ return !(brperm & AuBrPerm_RR); ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_br_test_oflag(int oflag, struct au_branch *br) ++{ ++ int err, exec_flag; ++ ++ err = 0; ++ exec_flag = oflag & __FMODE_EXEC; ++ if (unlikely(exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC))) ++ err = -EACCES; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* branch.c */ ++struct au_sbinfo; ++void au_br_free(struct au_sbinfo *sinfo); ++int au_br_index(struct super_block *sb, aufs_bindex_t br_id); ++struct au_opt_add; ++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); ++struct au_opt_del; ++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); ++long au_ibusy_ioctl(struct file *file, unsigned long arg); ++#ifdef CONFIG_COMPAT ++long au_ibusy_compat_ioctl(struct file *file, unsigned long arg); ++#endif ++struct au_opt_mod; ++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, ++ int *do_refresh); ++struct aufs_stfs; ++int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs); ++ ++/* xino.c */ ++static const loff_t au_loff_max = LLONG_MAX; ++ ++int au_xib_trunc(struct super_block *sb); ++ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos); ++ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos); ++struct file *au_xino_create2(struct file *base_file, struct file *copy_src); ++struct file *au_xino_create(struct super_block *sb, char *fname, int silent); ++ino_t au_xino_new_ino(struct super_block *sb); ++void au_xino_delete_inode(struct inode *inode, const int unlinked); ++int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino); ++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t *ino); ++int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino, ++ struct file *base_file, int do_test); ++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex); ++ ++struct au_opt_xino; ++int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount); ++void au_xino_clr(struct super_block *sb); ++struct file *au_xino_def(struct super_block *sb); ++int au_xino_path(struct seq_file *seq, struct file *file); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Superblock to branch */ ++static inline ++aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr(sb, bindex)->br_id; ++} ++ ++static inline ++struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_br_mnt(au_sbr(sb, bindex)); ++} ++ ++static inline ++struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_br_sb(au_sbr(sb, bindex)); ++} ++ ++static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ atomic_dec(&au_sbr(sb, bindex)->br_count); ++} ++ ++static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr(sb, bindex)->br_perm; ++} ++ ++static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_br_whable(au_sbr_perm(sb, bindex)); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * wbr_wh_read_lock, wbr_wh_write_lock ++ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem); ++ ++#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&wbr->wbr_wh_rwsem) ++#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&wbr->wbr_wh_rwsem) ++#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&wbr->wbr_wh_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_FHSM ++static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm) ++{ ++ mutex_init(&brfhsm->bf_lock); ++ brfhsm->bf_jiffy = 0; ++ brfhsm->bf_readable = 0; ++} ++ ++static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm) ++{ ++ mutex_destroy(&brfhsm->bf_lock); ++} ++#else ++AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm) ++AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm) ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_BRANCH_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/cpup.c linux-4.0.4/fs/aufs/cpup.c +--- linux-4.0.4.orig/fs/aufs/cpup.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/cpup.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,1295 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * copy-up functions, see wbr_policy.c for copy-down ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) ++{ ++ const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE ++ | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT; ++ ++ BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); ++ ++ dst->i_flags |= iflags & ~mask; ++ if (au_test_fs_notime(dst->i_sb)) ++ dst->i_flags |= S_NOATIME | S_NOCMTIME; ++} ++ ++void au_cpup_attr_timesizes(struct inode *inode) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ fsstack_copy_attr_times(inode, h_inode); ++ fsstack_copy_inode_size(inode, h_inode); ++} ++ ++void au_cpup_attr_nlink(struct inode *inode, int force) ++{ ++ struct inode *h_inode; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend; ++ ++ sb = inode->i_sb; ++ bindex = au_ibstart(inode); ++ h_inode = au_h_iptr(inode, bindex); ++ if (!force ++ && !S_ISDIR(h_inode->i_mode) ++ && au_opt_test(au_mntflags(sb), PLINK) ++ && au_plink_test(inode)) ++ return; ++ ++ /* ++ * 0 can happen in revalidating. ++ * h_inode->i_mutex may not be held here, but it is harmless since once ++ * i_nlink reaches 0, it will never become positive except O_TMPFILE ++ * case. ++ * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause ++ * the incorrect link count. ++ */ ++ set_nlink(inode, h_inode->i_nlink); ++ ++ /* ++ * fewer nlink makes find(1) noisy, but larger nlink doesn't. ++ * it may includes whplink directory. ++ */ ++ if (S_ISDIR(h_inode->i_mode)) { ++ bend = au_ibend(inode); ++ for (bindex++; bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode) ++ au_add_nlink(inode, h_inode); ++ } ++ } ++} ++ ++void au_cpup_attr_changeable(struct inode *inode) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ inode->i_mode = h_inode->i_mode; ++ inode->i_uid = h_inode->i_uid; ++ inode->i_gid = h_inode->i_gid; ++ au_cpup_attr_timesizes(inode); ++ au_cpup_attr_flags(inode, h_inode->i_flags); ++} ++ ++void au_cpup_igen(struct inode *inode, struct inode *h_inode) ++{ ++ struct au_iinfo *iinfo = au_ii(inode); ++ ++ IiMustWriteLock(inode); ++ ++ iinfo->ii_higen = h_inode->i_generation; ++ iinfo->ii_hsb1 = h_inode->i_sb; ++} ++ ++void au_cpup_attr_all(struct inode *inode, int force) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ au_cpup_attr_changeable(inode); ++ if (inode->i_nlink > 0) ++ au_cpup_attr_nlink(inode, force); ++ inode->i_rdev = h_inode->i_rdev; ++ inode->i_blkbits = h_inode->i_blkbits; ++ au_cpup_igen(inode, h_inode); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */ ++ ++/* keep the timestamps of the parent dir when cpup */ ++void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, ++ struct path *h_path) ++{ ++ struct inode *h_inode; ++ ++ dt->dt_dentry = dentry; ++ dt->dt_h_path = *h_path; ++ h_inode = h_path->dentry->d_inode; ++ dt->dt_atime = h_inode->i_atime; ++ dt->dt_mtime = h_inode->i_mtime; ++ /* smp_mb(); */ ++} ++ ++void au_dtime_revert(struct au_dtime *dt) ++{ ++ struct iattr attr; ++ int err; ++ ++ attr.ia_atime = dt->dt_atime; ++ attr.ia_mtime = dt->dt_mtime; ++ attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET ++ | ATTR_ATIME | ATTR_ATIME_SET; ++ ++ /* no delegation since this is a directory */ ++ err = vfsub_notify_change(&dt->dt_h_path, &attr, /*delegated*/NULL); ++ if (unlikely(err)) ++ pr_warn("restoring timestamps failed(%d). ignored\n", err); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* internal use only */ ++struct au_cpup_reg_attr { ++ int valid; ++ struct kstat st; ++ unsigned int iflags; /* inode->i_flags */ ++}; ++ ++static noinline_for_stack ++int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, ++ struct au_cpup_reg_attr *h_src_attr) ++{ ++ int err, sbits, icex; ++ unsigned int mnt_flags; ++ unsigned char verbose; ++ struct iattr ia; ++ struct path h_path; ++ struct inode *h_isrc, *h_idst; ++ struct kstat *h_st; ++ struct au_branch *br; ++ ++ h_path.dentry = au_h_dptr(dst, bindex); ++ h_idst = h_path.dentry->d_inode; ++ br = au_sbr(dst->d_sb, bindex); ++ h_path.mnt = au_br_mnt(br); ++ h_isrc = h_src->d_inode; ++ ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID ++ | ATTR_ATIME | ATTR_MTIME ++ | ATTR_ATIME_SET | ATTR_MTIME_SET; ++ if (h_src_attr && h_src_attr->valid) { ++ h_st = &h_src_attr->st; ++ ia.ia_uid = h_st->uid; ++ ia.ia_gid = h_st->gid; ++ ia.ia_atime = h_st->atime; ++ ia.ia_mtime = h_st->mtime; ++ if (h_idst->i_mode != h_st->mode ++ && !S_ISLNK(h_idst->i_mode)) { ++ ia.ia_valid |= ATTR_MODE; ++ ia.ia_mode = h_st->mode; ++ } ++ sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_idst, h_src_attr->iflags); ++ } else { ++ ia.ia_uid = h_isrc->i_uid; ++ ia.ia_gid = h_isrc->i_gid; ++ ia.ia_atime = h_isrc->i_atime; ++ ia.ia_mtime = h_isrc->i_mtime; ++ if (h_idst->i_mode != h_isrc->i_mode ++ && !S_ISLNK(h_idst->i_mode)) { ++ ia.ia_valid |= ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ } ++ sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_idst, h_isrc->i_flags); ++ } ++ /* no delegation since it is just created */ ++ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); ++ ++ /* is this nfs only? */ ++ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL); ++ } ++ ++ icex = br->br_perm & AuBrAttr_ICEX; ++ if (!err) { ++ mnt_flags = au_mntflags(dst->d_sb); ++ verbose = !!au_opt_test(mnt_flags, VERBOSE); ++ err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, ++ char *buf, unsigned long blksize) ++{ ++ int err; ++ size_t sz, rbytes, wbytes; ++ unsigned char all_zero; ++ char *p, *zp; ++ struct mutex *h_mtx; ++ /* reduce stack usage */ ++ struct iattr *ia; ++ ++ zp = page_address(ZERO_PAGE(0)); ++ if (unlikely(!zp)) ++ return -ENOMEM; /* possible? */ ++ ++ err = 0; ++ all_zero = 0; ++ while (len) { ++ AuDbg("len %lld\n", len); ++ sz = blksize; ++ if (len < blksize) ++ sz = len; ++ ++ rbytes = 0; ++ /* todo: signal_pending? */ ++ while (!rbytes || err == -EAGAIN || err == -EINTR) { ++ rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); ++ err = rbytes; ++ } ++ if (unlikely(err < 0)) ++ break; ++ ++ all_zero = 0; ++ if (len >= rbytes && rbytes == blksize) ++ all_zero = !memcmp(buf, zp, rbytes); ++ if (!all_zero) { ++ wbytes = rbytes; ++ p = buf; ++ while (wbytes) { ++ size_t b; ++ ++ b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); ++ err = b; ++ /* todo: signal_pending? */ ++ if (unlikely(err == -EAGAIN || err == -EINTR)) ++ continue; ++ if (unlikely(err < 0)) ++ break; ++ wbytes -= b; ++ p += b; ++ } ++ if (unlikely(err < 0)) ++ break; ++ } else { ++ loff_t res; ++ ++ AuLabel(hole); ++ res = vfsub_llseek(dst, rbytes, SEEK_CUR); ++ err = res; ++ if (unlikely(res < 0)) ++ break; ++ } ++ len -= rbytes; ++ err = 0; ++ } ++ ++ /* the last block may be a hole */ ++ if (!err && all_zero) { ++ AuLabel(last hole); ++ ++ err = 1; ++ if (au_test_nfs(dst->f_path.dentry->d_sb)) { ++ /* nfs requires this step to make last hole */ ++ /* is this only nfs? */ ++ do { ++ /* todo: signal_pending? */ ++ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ if (err == 1) ++ dst->f_pos--; ++ } ++ ++ if (err == 1) { ++ ia = (void *)buf; ++ ia->ia_size = dst->f_pos; ++ ia->ia_valid = ATTR_SIZE | ATTR_FILE; ++ ia->ia_file = dst; ++ h_mtx = &file_inode(dst)->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ /* no delegation since it is just created */ ++ err = vfsub_notify_change(&dst->f_path, ia, ++ /*delegated*/NULL); ++ mutex_unlock(h_mtx); ++ } ++ } ++ ++ return err; ++} ++ ++int au_copy_file(struct file *dst, struct file *src, loff_t len) ++{ ++ int err; ++ unsigned long blksize; ++ unsigned char do_kfree; ++ char *buf; ++ ++ err = -ENOMEM; ++ blksize = dst->f_path.dentry->d_sb->s_blocksize; ++ if (!blksize || PAGE_SIZE < blksize) ++ blksize = PAGE_SIZE; ++ AuDbg("blksize %lu\n", blksize); ++ do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *)); ++ if (do_kfree) ++ buf = kmalloc(blksize, GFP_NOFS); ++ else ++ buf = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!buf)) ++ goto out; ++ ++ if (len > (1 << 22)) ++ AuDbg("copying a large file %lld\n", (long long)len); ++ ++ src->f_pos = 0; ++ dst->f_pos = 0; ++ err = au_do_copy_file(dst, src, len, buf, blksize); ++ if (do_kfree) ++ kfree(buf); ++ else ++ free_page((unsigned long)buf); ++ ++out: ++ return err; ++} ++ ++/* ++ * to support a sparse file which is opened with O_APPEND, ++ * we need to close the file. ++ */ ++static int au_cp_regular(struct au_cp_generic *cpg) ++{ ++ int err, i; ++ enum { SRC, DST }; ++ struct { ++ aufs_bindex_t bindex; ++ unsigned int flags; ++ struct dentry *dentry; ++ int force_wr; ++ struct file *file; ++ void *label; ++ } *f, file[] = { ++ { ++ .bindex = cpg->bsrc, ++ .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, ++ .label = &&out ++ }, ++ { ++ .bindex = cpg->bdst, ++ .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, ++ .force_wr = !!au_ftest_cpup(cpg->flags, RWDST), ++ .label = &&out_src ++ } ++ }; ++ struct super_block *sb; ++ ++ /* bsrc branch can be ro/rw. */ ++ sb = cpg->dentry->d_sb; ++ f = file; ++ for (i = 0; i < 2; i++, f++) { ++ f->dentry = au_h_dptr(cpg->dentry, f->bindex); ++ f->file = au_h_open(cpg->dentry, f->bindex, f->flags, ++ /*file*/NULL, f->force_wr); ++ err = PTR_ERR(f->file); ++ if (IS_ERR(f->file)) ++ goto *f->label; ++ } ++ ++ /* try stopping to update while we copyup */ ++ IMustLock(file[SRC].dentry->d_inode); ++ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len); ++ ++ fput(file[DST].file); ++ au_sbr_put(sb, file[DST].bindex); ++ ++out_src: ++ fput(file[SRC].file); ++ au_sbr_put(sb, file[SRC].bindex); ++out: ++ return err; ++} ++ ++static int au_do_cpup_regular(struct au_cp_generic *cpg, ++ struct au_cpup_reg_attr *h_src_attr) ++{ ++ int err, rerr; ++ loff_t l; ++ struct path h_path; ++ struct inode *h_src_inode, *h_dst_inode; ++ ++ err = 0; ++ h_src_inode = au_h_iptr(cpg->dentry->d_inode, cpg->bsrc); ++ l = i_size_read(h_src_inode); ++ if (cpg->len == -1 || l < cpg->len) ++ cpg->len = l; ++ if (cpg->len) { ++ /* try stopping to update while we are referencing */ ++ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); ++ au_pin_hdir_unlock(cpg->pin); ++ ++ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc); ++ h_path.mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc); ++ h_src_attr->iflags = h_src_inode->i_flags; ++ err = vfs_getattr(&h_path, &h_src_attr->st); ++ if (unlikely(err)) { ++ mutex_unlock(&h_src_inode->i_mutex); ++ goto out; ++ } ++ h_src_attr->valid = 1; ++ err = au_cp_regular(cpg); ++ mutex_unlock(&h_src_inode->i_mutex); ++ rerr = au_pin_hdir_relock(cpg->pin); ++ if (!err && rerr) ++ err = rerr; ++ } ++ if (!err && (h_src_inode->i_state & I_LINKABLE)) { ++ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bdst); ++ h_dst_inode = h_path.dentry->d_inode; ++ spin_lock(&h_dst_inode->i_lock); ++ h_dst_inode->i_state |= I_LINKABLE; ++ spin_unlock(&h_dst_inode->i_lock); ++ } ++ ++out: ++ return err; ++} ++ ++static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src, ++ struct inode *h_dir) ++{ ++ int err, symlen; ++ mm_segment_t old_fs; ++ union { ++ char *k; ++ char __user *u; ++ } sym; ++ ++ err = -ENOSYS; ++ if (unlikely(!h_src->d_inode->i_op->readlink)) ++ goto out; ++ ++ err = -ENOMEM; ++ sym.k = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!sym.k)) ++ goto out; ++ ++ /* unnecessary to support mmap_sem since symlink is not mmap-able */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ symlen = h_src->d_inode->i_op->readlink(h_src, sym.u, PATH_MAX); ++ err = symlen; ++ set_fs(old_fs); ++ ++ if (symlen > 0) { ++ sym.k[symlen] = 0; ++ err = vfsub_symlink(h_dir, h_path, sym.k); ++ } ++ free_page((unsigned long)sym.k); ++ ++out: ++ return err; ++} ++ ++static noinline_for_stack ++int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent, ++ struct au_cpup_reg_attr *h_src_attr) ++{ ++ int err; ++ umode_t mode; ++ unsigned int mnt_flags; ++ unsigned char isdir, isreg, force; ++ const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME); ++ struct au_dtime dt; ++ struct path h_path; ++ struct dentry *h_src, *h_dst, *h_parent; ++ struct inode *h_inode, *h_dir; ++ struct super_block *sb; ++ ++ /* bsrc branch can be ro/rw. */ ++ h_src = au_h_dptr(cpg->dentry, cpg->bsrc); ++ h_inode = h_src->d_inode; ++ AuDebugOn(h_inode != au_h_iptr(cpg->dentry->d_inode, cpg->bsrc)); ++ ++ /* try stopping to be referenced while we are creating */ ++ h_dst = au_h_dptr(cpg->dentry, cpg->bdst); ++ if (au_ftest_cpup(cpg->flags, RENAME)) ++ AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, ++ AUFS_WH_PFX_LEN)); ++ h_parent = h_dst->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ AuDebugOn(h_parent != h_dst->d_parent); ++ ++ sb = cpg->dentry->d_sb; ++ h_path.mnt = au_sbr_mnt(sb, cpg->bdst); ++ if (do_dt) { ++ h_path.dentry = h_parent; ++ au_dtime_store(&dt, dst_parent, &h_path); ++ } ++ h_path.dentry = h_dst; ++ ++ isreg = 0; ++ isdir = 0; ++ mode = h_inode->i_mode; ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ isreg = 1; ++ err = vfsub_create(h_dir, &h_path, mode | S_IWUSR, ++ /*want_excl*/true); ++ if (!err) ++ err = au_do_cpup_regular(cpg, h_src_attr); ++ break; ++ case S_IFDIR: ++ isdir = 1; ++ err = vfsub_mkdir(h_dir, &h_path, mode); ++ if (!err) { ++ /* ++ * strange behaviour from the users view, ++ * particularry setattr case ++ */ ++ if (au_ibstart(dst_parent->d_inode) == cpg->bdst) ++ au_cpup_attr_nlink(dst_parent->d_inode, ++ /*force*/1); ++ au_cpup_attr_nlink(cpg->dentry->d_inode, /*force*/1); ++ } ++ break; ++ case S_IFLNK: ++ err = au_do_cpup_symlink(&h_path, h_src, h_dir); ++ break; ++ case S_IFCHR: ++ case S_IFBLK: ++ AuDebugOn(!capable(CAP_MKNOD)); ++ /*FALLTHROUGH*/ ++ case S_IFIFO: ++ case S_IFSOCK: ++ err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev); ++ break; ++ default: ++ AuIOErr("Unknown inode type 0%o\n", mode); ++ err = -EIO; ++ } ++ ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, UDBA_NONE) ++ && !isdir ++ && au_opt_test(mnt_flags, XINO) ++ && (h_inode->i_nlink == 1 ++ || (h_inode->i_state & I_LINKABLE)) ++ /* todo: unnecessary? */ ++ /* && cpg->dentry->d_inode->i_nlink == 1 */ ++ && cpg->bdst < cpg->bsrc ++ && !au_ftest_cpup(cpg->flags, KEEPLINO)) ++ au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0); ++ /* ignore this error */ ++ ++ if (!err) { ++ force = 0; ++ if (isreg) { ++ force = !!cpg->len; ++ if (cpg->len == -1) ++ force = !!i_size_read(h_inode); ++ } ++ au_fhsm_wrote(sb, cpg->bdst, force); ++ } ++ ++ if (do_dt) ++ au_dtime_revert(&dt); ++ return err; ++} ++ ++static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path) ++{ ++ int err; ++ struct dentry *dentry, *h_dentry, *h_parent, *parent; ++ struct inode *h_dir; ++ aufs_bindex_t bdst; ++ ++ dentry = cpg->dentry; ++ bdst = cpg->bdst; ++ h_dentry = au_h_dptr(dentry, bdst); ++ if (!au_ftest_cpup(cpg->flags, OVERWRITE)) { ++ dget(h_dentry); ++ au_set_h_dptr(dentry, bdst, NULL); ++ err = au_lkup_neg(dentry, bdst, /*wh*/0); ++ if (!err) ++ h_path->dentry = dget(au_h_dptr(dentry, bdst)); ++ au_set_h_dptr(dentry, bdst, h_dentry); ++ } else { ++ err = 0; ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bdst); ++ dput(parent); ++ h_path->dentry = vfsub_lkup_one(&dentry->d_name, h_parent); ++ if (IS_ERR(h_path->dentry)) ++ err = PTR_ERR(h_path->dentry); ++ } ++ if (unlikely(err)) ++ goto out; ++ ++ h_parent = h_dentry->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ AuDbg("%pd %pd\n", h_dentry, h_path->dentry); ++ /* no delegation since it is just created */ ++ err = vfsub_rename(h_dir, h_dentry, h_dir, h_path, /*delegated*/NULL); ++ dput(h_path->dentry); ++ ++out: ++ return err; ++} ++ ++/* ++ * copyup the @dentry from @bsrc to @bdst. ++ * the caller must set the both of lower dentries. ++ * @len is for truncating when it is -1 copyup the entire file. ++ * in link/rename cases, @dst_parent may be different from the real one. ++ * basic->bsrc can be larger than basic->bdst. ++ */ ++static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) ++{ ++ int err, rerr; ++ aufs_bindex_t old_ibstart; ++ unsigned char isdir, plink; ++ struct dentry *h_src, *h_dst, *h_parent; ++ struct inode *dst_inode, *h_dir, *inode, *delegated; ++ struct super_block *sb; ++ struct au_branch *br; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct path h_path; ++ struct au_cpup_reg_attr h_src_attr; ++ } *a; ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ a->h_src_attr.valid = 0; ++ ++ sb = cpg->dentry->d_sb; ++ br = au_sbr(sb, cpg->bdst); ++ a->h_path.mnt = au_br_mnt(br); ++ h_dst = au_h_dptr(cpg->dentry, cpg->bdst); ++ h_parent = h_dst->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ ++ h_src = au_h_dptr(cpg->dentry, cpg->bsrc); ++ inode = cpg->dentry->d_inode; ++ ++ if (!dst_parent) ++ dst_parent = dget_parent(cpg->dentry); ++ else ++ dget(dst_parent); ++ ++ plink = !!au_opt_test(au_mntflags(sb), PLINK); ++ dst_inode = au_h_iptr(inode, cpg->bdst); ++ if (dst_inode) { ++ if (unlikely(!plink)) { ++ err = -EIO; ++ AuIOErr("hi%lu(i%lu) exists on b%d " ++ "but plink is disabled\n", ++ dst_inode->i_ino, inode->i_ino, cpg->bdst); ++ goto out_parent; ++ } ++ ++ if (dst_inode->i_nlink) { ++ const int do_dt = au_ftest_cpup(cpg->flags, DTIME); ++ ++ h_src = au_plink_lkup(inode, cpg->bdst); ++ err = PTR_ERR(h_src); ++ if (IS_ERR(h_src)) ++ goto out_parent; ++ if (unlikely(!h_src->d_inode)) { ++ err = -EIO; ++ AuIOErr("i%lu exists on a upper branch " ++ "but not pseudo-linked\n", ++ inode->i_ino); ++ dput(h_src); ++ goto out_parent; ++ } ++ ++ if (do_dt) { ++ a->h_path.dentry = h_parent; ++ au_dtime_store(&a->dt, dst_parent, &a->h_path); ++ } ++ ++ a->h_path.dentry = h_dst; ++ delegated = NULL; ++ err = vfsub_link(h_src, h_dir, &a->h_path, &delegated); ++ if (!err && au_ftest_cpup(cpg->flags, RENAME)) ++ err = au_do_ren_after_cpup(cpg, &a->h_path); ++ if (do_dt) ++ au_dtime_revert(&a->dt); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ dput(h_src); ++ goto out_parent; ++ } else ++ /* todo: cpup_wh_file? */ ++ /* udba work */ ++ au_update_ibrange(inode, /*do_put_zero*/1); ++ } ++ ++ isdir = S_ISDIR(inode->i_mode); ++ old_ibstart = au_ibstart(inode); ++ err = cpup_entry(cpg, dst_parent, &a->h_src_attr); ++ if (unlikely(err)) ++ goto out_rev; ++ dst_inode = h_dst->d_inode; ++ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); ++ /* todo: necessary? */ ++ /* au_pin_hdir_unlock(cpg->pin); */ ++ ++ err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr); ++ if (unlikely(err)) { ++ /* todo: necessary? */ ++ /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ ++ mutex_unlock(&dst_inode->i_mutex); ++ goto out_rev; ++ } ++ ++ if (cpg->bdst < old_ibstart) { ++ if (S_ISREG(inode->i_mode)) { ++ err = au_dy_iaop(inode, cpg->bdst, dst_inode); ++ if (unlikely(err)) { ++ /* ignore an error */ ++ /* au_pin_hdir_relock(cpg->pin); */ ++ mutex_unlock(&dst_inode->i_mutex); ++ goto out_rev; ++ } ++ } ++ au_set_ibstart(inode, cpg->bdst); ++ } else ++ au_set_ibend(inode, cpg->bdst); ++ au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), ++ au_hi_flags(inode, isdir)); ++ ++ /* todo: necessary? */ ++ /* err = au_pin_hdir_relock(cpg->pin); */ ++ mutex_unlock(&dst_inode->i_mutex); ++ if (unlikely(err)) ++ goto out_rev; ++ ++ if (!isdir ++ && (h_src->d_inode->i_nlink > 1 ++ || h_src->d_inode->i_state & I_LINKABLE) ++ && plink) ++ au_plink_append(inode, cpg->bdst, h_dst); ++ ++ if (au_ftest_cpup(cpg->flags, RENAME)) { ++ a->h_path.dentry = h_dst; ++ err = au_do_ren_after_cpup(cpg, &a->h_path); ++ } ++ if (!err) ++ goto out_parent; /* success */ ++ ++ /* revert */ ++out_rev: ++ a->h_path.dentry = h_parent; ++ au_dtime_store(&a->dt, dst_parent, &a->h_path); ++ a->h_path.dentry = h_dst; ++ rerr = 0; ++ if (h_dst->d_inode) { ++ if (!isdir) { ++ /* no delegation since it is just created */ ++ rerr = vfsub_unlink(h_dir, &a->h_path, ++ /*delegated*/NULL, /*force*/0); ++ } else ++ rerr = vfsub_rmdir(h_dir, &a->h_path); ++ } ++ au_dtime_revert(&a->dt); ++ if (rerr) { ++ AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); ++ err = -EIO; ++ } ++out_parent: ++ dput(dst_parent); ++ kfree(a); ++out: ++ return err; ++} ++ ++#if 0 /* reserved */ ++struct au_cpup_single_args { ++ int *errp; ++ struct au_cp_generic *cpg; ++ struct dentry *dst_parent; ++}; ++ ++static void au_call_cpup_single(void *args) ++{ ++ struct au_cpup_single_args *a = args; ++ ++ au_pin_hdir_acquire_nest(a->cpg->pin); ++ *a->errp = au_cpup_single(a->cpg, a->dst_parent); ++ au_pin_hdir_release(a->cpg->pin); ++} ++#endif ++ ++/* ++ * prevent SIGXFSZ in copy-up. ++ * testing CAP_MKNOD is for generic fs, ++ * but CAP_FSETID is for xfs only, currently. ++ */ ++static int au_cpup_sio_test(struct au_pin *pin, umode_t mode) ++{ ++ int do_sio; ++ struct super_block *sb; ++ struct inode *h_dir; ++ ++ do_sio = 0; ++ sb = au_pinned_parent(pin)->d_sb; ++ if (!au_wkq_test() ++ && (!au_sbi(sb)->si_plink_maint_pid ++ || au_plink_maint(sb, AuLock_NOPLM))) { ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ /* no condition about RLIMIT_FSIZE and the file size */ ++ do_sio = 1; ++ break; ++ case S_IFCHR: ++ case S_IFBLK: ++ do_sio = !capable(CAP_MKNOD); ++ break; ++ } ++ if (!do_sio) ++ do_sio = ((mode & (S_ISUID | S_ISGID)) ++ && !capable(CAP_FSETID)); ++ /* this workaround may be removed in the future */ ++ if (!do_sio) { ++ h_dir = au_pinned_h_dir(pin); ++ do_sio = h_dir->i_mode & S_ISVTX; ++ } ++ } ++ ++ return do_sio; ++} ++ ++#if 0 /* reserved */ ++int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) ++{ ++ int err, wkq_err; ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc); ++ if (!au_cpup_sio_test(pin, h_dentry->d_inode->i_mode)) ++ err = au_cpup_single(cpg, dst_parent); ++ else { ++ struct au_cpup_single_args args = { ++ .errp = &err, ++ .cpg = cpg, ++ .dst_parent = dst_parent ++ }; ++ wkq_err = au_wkq_wait(au_call_cpup_single, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++#endif ++ ++/* ++ * copyup the @dentry from the first active lower branch to @bdst, ++ * using au_cpup_single(). ++ */ ++static int au_cpup_simple(struct au_cp_generic *cpg) ++{ ++ int err; ++ unsigned int flags_orig; ++ struct dentry *dentry; ++ ++ AuDebugOn(cpg->bsrc < 0); ++ ++ dentry = cpg->dentry; ++ DiMustWriteLock(dentry); ++ ++ err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1); ++ if (!err) { ++ flags_orig = cpg->flags; ++ au_fset_cpup(cpg->flags, RENAME); ++ err = au_cpup_single(cpg, NULL); ++ cpg->flags = flags_orig; ++ if (!err) ++ return 0; /* success */ ++ ++ /* revert */ ++ au_set_h_dptr(dentry, cpg->bdst, NULL); ++ au_set_dbstart(dentry, cpg->bsrc); ++ } ++ ++ return err; ++} ++ ++struct au_cpup_simple_args { ++ int *errp; ++ struct au_cp_generic *cpg; ++}; ++ ++static void au_call_cpup_simple(void *args) ++{ ++ struct au_cpup_simple_args *a = args; ++ ++ au_pin_hdir_acquire_nest(a->cpg->pin); ++ *a->errp = au_cpup_simple(a->cpg); ++ au_pin_hdir_release(a->cpg->pin); ++} ++ ++static int au_do_sio_cpup_simple(struct au_cp_generic *cpg) ++{ ++ int err, wkq_err; ++ struct dentry *dentry, *parent; ++ struct file *h_file; ++ struct inode *h_dir; ++ ++ dentry = cpg->dentry; ++ h_file = NULL; ++ if (au_ftest_cpup(cpg->flags, HOPEN)) { ++ AuDebugOn(cpg->bsrc < 0); ++ h_file = au_h_open_pre(dentry, cpg->bsrc, /*force_wr*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ } ++ ++ parent = dget_parent(dentry); ++ h_dir = au_h_iptr(parent->d_inode, cpg->bdst); ++ if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) ++ && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode)) ++ err = au_cpup_simple(cpg); ++ else { ++ struct au_cpup_simple_args args = { ++ .errp = &err, ++ .cpg = cpg ++ }; ++ wkq_err = au_wkq_wait(au_call_cpup_simple, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ dput(parent); ++ if (h_file) ++ au_h_open_post(dentry, cpg->bsrc, h_file); ++ ++out: ++ return err; ++} ++ ++int au_sio_cpup_simple(struct au_cp_generic *cpg) ++{ ++ aufs_bindex_t bsrc, bend; ++ struct dentry *dentry, *h_dentry; ++ ++ if (cpg->bsrc < 0) { ++ dentry = cpg->dentry; ++ bend = au_dbend(dentry); ++ for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) { ++ h_dentry = au_h_dptr(dentry, bsrc); ++ if (h_dentry) { ++ AuDebugOn(!h_dentry->d_inode); ++ break; ++ } ++ } ++ AuDebugOn(bsrc > bend); ++ cpg->bsrc = bsrc; ++ } ++ AuDebugOn(cpg->bsrc <= cpg->bdst); ++ return au_do_sio_cpup_simple(cpg); ++} ++ ++int au_sio_cpdown_simple(struct au_cp_generic *cpg) ++{ ++ AuDebugOn(cpg->bdst <= cpg->bsrc); ++ return au_do_sio_cpup_simple(cpg); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * copyup the deleted file for writing. ++ */ ++static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry, ++ struct file *file) ++{ ++ int err; ++ unsigned int flags_orig; ++ aufs_bindex_t bsrc_orig; ++ struct dentry *h_d_dst, *h_d_start; ++ struct au_dinfo *dinfo; ++ struct au_hdentry *hdp; ++ ++ dinfo = au_di(cpg->dentry); ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ bsrc_orig = cpg->bsrc; ++ cpg->bsrc = dinfo->di_bstart; ++ hdp = dinfo->di_hdentry; ++ h_d_dst = hdp[0 + cpg->bdst].hd_dentry; ++ dinfo->di_bstart = cpg->bdst; ++ hdp[0 + cpg->bdst].hd_dentry = wh_dentry; ++ h_d_start = NULL; ++ if (file) { ++ h_d_start = hdp[0 + cpg->bsrc].hd_dentry; ++ hdp[0 + cpg->bsrc].hd_dentry = au_hf_top(file)->f_path.dentry; ++ } ++ flags_orig = cpg->flags; ++ cpg->flags = !AuCpup_DTIME; ++ err = au_cpup_single(cpg, /*h_parent*/NULL); ++ cpg->flags = flags_orig; ++ if (file) { ++ if (!err) ++ err = au_reopen_nondir(file); ++ hdp[0 + cpg->bsrc].hd_dentry = h_d_start; ++ } ++ hdp[0 + cpg->bdst].hd_dentry = h_d_dst; ++ dinfo->di_bstart = cpg->bsrc; ++ cpg->bsrc = bsrc_orig; ++ ++ return err; ++} ++ ++static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file) ++{ ++ int err; ++ aufs_bindex_t bdst; ++ struct au_dtime dt; ++ struct dentry *dentry, *parent, *h_parent, *wh_dentry; ++ struct au_branch *br; ++ struct path h_path; ++ ++ dentry = cpg->dentry; ++ bdst = cpg->bdst; ++ br = au_sbr(dentry->d_sb, bdst); ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bdst); ++ wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out; ++ ++ h_path.dentry = h_parent; ++ h_path.mnt = au_br_mnt(br); ++ au_dtime_store(&dt, parent, &h_path); ++ err = au_do_cpup_wh(cpg, wh_dentry, file); ++ if (unlikely(err)) ++ goto out_wh; ++ ++ dget(wh_dentry); ++ h_path.dentry = wh_dentry; ++ if (!d_is_dir(wh_dentry)) { ++ /* no delegation since it is just created */ ++ err = vfsub_unlink(h_parent->d_inode, &h_path, ++ /*delegated*/NULL, /*force*/0); ++ } else ++ err = vfsub_rmdir(h_parent->d_inode, &h_path); ++ if (unlikely(err)) { ++ AuIOErr("failed remove copied-up tmp file %pd(%d)\n", ++ wh_dentry, err); ++ err = -EIO; ++ } ++ au_dtime_revert(&dt); ++ au_set_hi_wh(dentry->d_inode, bdst, wh_dentry); ++ ++out_wh: ++ dput(wh_dentry); ++out: ++ dput(parent); ++ return err; ++} ++ ++struct au_cpup_wh_args { ++ int *errp; ++ struct au_cp_generic *cpg; ++ struct file *file; ++}; ++ ++static void au_call_cpup_wh(void *args) ++{ ++ struct au_cpup_wh_args *a = args; ++ ++ au_pin_hdir_acquire_nest(a->cpg->pin); ++ *a->errp = au_cpup_wh(a->cpg, a->file); ++ au_pin_hdir_release(a->cpg->pin); ++} ++ ++int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file) ++{ ++ int err, wkq_err; ++ aufs_bindex_t bdst; ++ struct dentry *dentry, *parent, *h_orph, *h_parent; ++ struct inode *dir, *h_dir, *h_tmpdir; ++ struct au_wbr *wbr; ++ struct au_pin wh_pin, *pin_orig; ++ ++ dentry = cpg->dentry; ++ bdst = cpg->bdst; ++ parent = dget_parent(dentry); ++ dir = parent->d_inode; ++ h_orph = NULL; ++ h_parent = NULL; ++ h_dir = au_igrab(au_h_iptr(dir, bdst)); ++ h_tmpdir = h_dir; ++ pin_orig = NULL; ++ if (!h_dir->i_nlink) { ++ wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; ++ h_orph = wbr->wbr_orph; ++ ++ h_parent = dget(au_h_dptr(parent, bdst)); ++ au_set_h_dptr(parent, bdst, dget(h_orph)); ++ h_tmpdir = h_orph->d_inode; ++ au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); ++ ++ mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); ++ /* todo: au_h_open_pre()? */ ++ ++ pin_orig = cpg->pin; ++ au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, ++ AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED); ++ cpg->pin = &wh_pin; ++ } ++ ++ if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) ++ && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode)) ++ err = au_cpup_wh(cpg, file); ++ else { ++ struct au_cpup_wh_args args = { ++ .errp = &err, ++ .cpg = cpg, ++ .file = file ++ }; ++ wkq_err = au_wkq_wait(au_call_cpup_wh, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ if (h_orph) { ++ mutex_unlock(&h_tmpdir->i_mutex); ++ /* todo: au_h_open_post()? */ ++ au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); ++ au_set_h_dptr(parent, bdst, h_parent); ++ AuDebugOn(!pin_orig); ++ cpg->pin = pin_orig; ++ } ++ iput(h_dir); ++ dput(parent); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * generic routine for both of copy-up and copy-down. ++ */ ++/* cf. revalidate function in file.c */ ++int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, ++ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, ++ struct dentry *h_parent, void *arg), ++ void *arg) ++{ ++ int err; ++ struct au_pin pin; ++ struct dentry *d, *parent, *h_parent, *real_parent; ++ ++ err = 0; ++ parent = dget_parent(dentry); ++ if (IS_ROOT(parent)) ++ goto out; ++ ++ au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2, ++ au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); ++ ++ /* do not use au_dpage */ ++ real_parent = parent; ++ while (1) { ++ dput(parent); ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bdst); ++ if (h_parent) ++ goto out; /* success */ ++ ++ /* find top dir which is necessary to cpup */ ++ do { ++ d = parent; ++ dput(parent); ++ parent = dget_parent(d); ++ di_read_lock_parent3(parent, !AuLock_IR); ++ h_parent = au_h_dptr(parent, bdst); ++ di_read_unlock(parent, !AuLock_IR); ++ } while (!h_parent); ++ ++ if (d != real_parent) ++ di_write_lock_child3(d); ++ ++ /* somebody else might create while we were sleeping */ ++ if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) { ++ if (au_h_dptr(d, bdst)) ++ au_update_dbstart(d); ++ ++ au_pin_set_dentry(&pin, d); ++ err = au_do_pin(&pin); ++ if (!err) { ++ err = cp(d, bdst, &pin, h_parent, arg); ++ au_unpin(&pin); ++ } ++ } ++ ++ if (d != real_parent) ++ di_write_unlock(d); ++ if (unlikely(err)) ++ break; ++ } ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, ++ struct dentry *h_parent __maybe_unused, ++ void *arg __maybe_unused) ++{ ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = bdst, ++ .bsrc = -1, ++ .len = 0, ++ .pin = pin, ++ .flags = AuCpup_DTIME ++ }; ++ return au_sio_cpup_simple(&cpg); ++} ++ ++int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL); ++} ++ ++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ int err; ++ struct dentry *parent; ++ struct inode *dir; ++ ++ parent = dget_parent(dentry); ++ dir = parent->d_inode; ++ err = 0; ++ if (au_h_iptr(dir, bdst)) ++ goto out; ++ ++ di_read_unlock(parent, AuLock_IR); ++ di_write_lock_parent(parent); ++ /* someone else might change our inode while we were sleeping */ ++ if (!au_h_iptr(dir, bdst)) ++ err = au_cpup_dirs(dentry, bdst); ++ di_downgrade_lock(parent, AuLock_IR); ++ ++out: ++ dput(parent); ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/cpup.h linux-4.0.4/fs/aufs/cpup.h +--- linux-4.0.4.orig/fs/aufs/cpup.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/cpup.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,81 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * copy-up/down functions ++ */ ++ ++#ifndef __AUFS_CPUP_H__ ++#define __AUFS_CPUP_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++struct inode; ++struct file; ++struct au_pin; ++ ++void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); ++void au_cpup_attr_timesizes(struct inode *inode); ++void au_cpup_attr_nlink(struct inode *inode, int force); ++void au_cpup_attr_changeable(struct inode *inode); ++void au_cpup_igen(struct inode *inode, struct inode *h_inode); ++void au_cpup_attr_all(struct inode *inode, int force); ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_cp_generic { ++ struct dentry *dentry; ++ aufs_bindex_t bdst, bsrc; ++ loff_t len; ++ struct au_pin *pin; ++ unsigned int flags; ++}; ++ ++/* cpup flags */ ++#define AuCpup_DTIME 1 /* do dtime_store/revert */ ++#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, ++ for link(2) */ ++#define AuCpup_RENAME (1 << 2) /* rename after cpup */ ++#define AuCpup_HOPEN (1 << 3) /* call h_open_pre/post() in ++ cpup */ ++#define AuCpup_OVERWRITE (1 << 4) /* allow overwriting the ++ existing entry */ ++#define AuCpup_RWDST (1 << 5) /* force write target even if ++ the branch is marked as RO */ ++ ++#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) ++#define au_fset_cpup(flags, name) \ ++ do { (flags) |= AuCpup_##name; } while (0) ++#define au_fclr_cpup(flags, name) \ ++ do { (flags) &= ~AuCpup_##name; } while (0) ++ ++int au_copy_file(struct file *dst, struct file *src, loff_t len); ++int au_sio_cpup_simple(struct au_cp_generic *cpg); ++int au_sio_cpdown_simple(struct au_cp_generic *cpg); ++int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file); ++ ++int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, ++ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, ++ struct dentry *h_parent, void *arg), ++ void *arg); ++int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); ++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* keep timestamps when copyup */ ++struct au_dtime { ++ struct dentry *dt_dentry; ++ struct path dt_h_path; ++ struct timespec dt_atime, dt_mtime; ++}; ++void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, ++ struct path *h_path); ++void au_dtime_revert(struct au_dtime *dt); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_CPUP_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/dbgaufs.c linux-4.0.4/fs/aufs/dbgaufs.c +--- linux-4.0.4.orig/fs/aufs/dbgaufs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dbgaufs.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,419 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * debugfs interface ++ */ ++ ++#include ++#include "aufs.h" ++ ++#ifndef CONFIG_SYSFS ++#error DEBUG_FS depends upon SYSFS ++#endif ++ ++static struct dentry *dbgaufs; ++static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH; ++ ++/* 20 is max digits length of ulong 64 */ ++struct dbgaufs_arg { ++ int n; ++ char a[20 * 4]; ++}; ++ ++/* ++ * common function for all XINO files ++ */ ++static int dbgaufs_xi_release(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ kfree(file->private_data); ++ return 0; ++} ++ ++static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt) ++{ ++ int err; ++ struct kstat st; ++ struct dbgaufs_arg *p; ++ ++ err = -ENOMEM; ++ p = kmalloc(sizeof(*p), GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = 0; ++ p->n = 0; ++ file->private_data = p; ++ if (!xf) ++ goto out; ++ ++ err = vfs_getattr(&xf->f_path, &st); ++ if (!err) { ++ if (do_fcnt) ++ p->n = snprintf ++ (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n", ++ (long)file_count(xf), st.blocks, st.blksize, ++ (long long)st.size); ++ else ++ p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n", ++ st.blocks, st.blksize, ++ (long long)st.size); ++ AuDebugOn(p->n >= sizeof(p->a)); ++ } else { ++ p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); ++ err = 0; ++ } ++ ++out: ++ return err; ++ ++} ++ ++static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dbgaufs_arg *p; ++ ++ p = file->private_data; ++ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dbgaufs_plink_arg { ++ int n; ++ char a[]; ++}; ++ ++static int dbgaufs_plink_release(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ free_page((unsigned long)file->private_data); ++ return 0; ++} ++ ++static int dbgaufs_plink_open(struct inode *inode, struct file *file) ++{ ++ int err, i, limit; ++ unsigned long n, sum; ++ struct dbgaufs_plink_arg *p; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct au_sphlhead *sphl; ++ ++ err = -ENOMEM; ++ p = (void *)get_zeroed_page(GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = -EFBIG; ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ limit = PAGE_SIZE - sizeof(p->n); ++ ++ /* the number of buckets */ ++ n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); ++ p->n += n; ++ limit -= n; ++ ++ sum = 0; ++ for (i = 0, sphl = sbinfo->si_plink; ++ i < AuPlink_NHASH; ++ i++, sphl++) { ++ n = au_sphl_count(sphl); ++ sum += n; ++ ++ n = snprintf(p->a + p->n, limit, "%lu ", n); ++ p->n += n; ++ limit -= n; ++ if (unlikely(limit <= 0)) ++ goto out_free; ++ } ++ p->a[p->n - 1] = '\n'; ++ ++ /* the sum of plinks */ ++ n = snprintf(p->a + p->n, limit, "%lu\n", sum); ++ p->n += n; ++ limit -= n; ++ if (unlikely(limit <= 0)) ++ goto out_free; ++ } else { ++#define str "1\n0\n0\n" ++ p->n = sizeof(str) - 1; ++ strcpy(p->a, str); ++#undef str ++ } ++ si_read_unlock(sb); ++ ++ err = 0; ++ file->private_data = p; ++ goto out; /* success */ ++ ++out_free: ++ free_page((unsigned long)p); ++out: ++ return err; ++} ++ ++static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dbgaufs_plink_arg *p; ++ ++ p = file->private_data; ++ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); ++} ++ ++static const struct file_operations dbgaufs_plink_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_plink_open, ++ .release = dbgaufs_plink_release, ++ .read = dbgaufs_plink_read ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int dbgaufs_xib_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); ++ si_read_unlock(sb); ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xib_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_xib_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define DbgaufsXi_PREFIX "xi" ++ ++static int dbgaufs_xino_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ long l; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct file *xf; ++ struct qstr *name; ++ ++ err = -ENOENT; ++ xf = NULL; ++ name = &file->f_path.dentry->d_name; ++ if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) ++ || memcmp(name->name, DbgaufsXi_PREFIX, ++ sizeof(DbgaufsXi_PREFIX) - 1))) ++ goto out; ++ err = kstrtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); ++ if (unlikely(err)) ++ goto out; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ if (l <= au_sbend(sb)) { ++ xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; ++ err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); ++ } else ++ err = -ENOENT; ++ si_read_unlock(sb); ++ ++out: ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xino_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_xino_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ aufs_bindex_t bend; ++ struct au_branch *br; ++ struct au_xino_file *xi; ++ ++ if (!au_sbi(sb)->si_dbgaufs) ++ return; ++ ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ xi = &br->br_xino; ++ debugfs_remove(xi->xi_dbgaufs); ++ xi->xi_dbgaufs = NULL; ++ } ++} ++ ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ struct au_sbinfo *sbinfo; ++ struct dentry *parent; ++ struct au_branch *br; ++ struct au_xino_file *xi; ++ aufs_bindex_t bend; ++ char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ ++ ++ sbinfo = au_sbi(sb); ++ parent = sbinfo->si_dbgaufs; ++ if (!parent) ++ return; ++ ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); ++ br = au_sbr(sb, bindex); ++ xi = &br->br_xino; ++ AuDebugOn(xi->xi_dbgaufs); ++ xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, ++ sbinfo, &dbgaufs_xino_fop); ++ /* ignore an error */ ++ if (unlikely(!xi->xi_dbgaufs)) ++ AuWarn1("failed %s under debugfs\n", name); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++static int dbgaufs_xigen_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); ++ si_read_unlock(sb); ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xigen_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_xigen_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ ++ /* ++ * This function is a dynamic '__init' function actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++ ++ err = -EIO; ++ sbinfo->si_dbgaufs_xigen = debugfs_create_file ++ ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_xigen_fop); ++ if (sbinfo->si_dbgaufs_xigen) ++ err = 0; ++ ++ return err; ++} ++#else ++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) ++{ ++ return 0; ++} ++#endif /* CONFIG_AUFS_EXPORT */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++void dbgaufs_si_fin(struct au_sbinfo *sbinfo) ++{ ++ /* ++ * This function is a dynamic '__fin' function actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++ ++ debugfs_remove_recursive(sbinfo->si_dbgaufs); ++ sbinfo->si_dbgaufs = NULL; ++ kobject_put(&sbinfo->si_kobj); ++} ++ ++int dbgaufs_si_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ char name[SysaufsSiNameLen]; ++ ++ /* ++ * This function is a dynamic '__init' function actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++ ++ err = -ENOENT; ++ if (!dbgaufs) { ++ AuErr1("/debug/aufs is uninitialized\n"); ++ goto out; ++ } ++ ++ err = -EIO; ++ sysaufs_name(sbinfo, name); ++ sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); ++ if (unlikely(!sbinfo->si_dbgaufs)) ++ goto out; ++ kobject_get(&sbinfo->si_kobj); ++ ++ sbinfo->si_dbgaufs_xib = debugfs_create_file ++ ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_xib_fop); ++ if (unlikely(!sbinfo->si_dbgaufs_xib)) ++ goto out_dir; ++ ++ sbinfo->si_dbgaufs_plink = debugfs_create_file ++ ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_plink_fop); ++ if (unlikely(!sbinfo->si_dbgaufs_plink)) ++ goto out_dir; ++ ++ err = dbgaufs_xigen_init(sbinfo); ++ if (!err) ++ goto out; /* success */ ++ ++out_dir: ++ dbgaufs_si_fin(sbinfo); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void dbgaufs_fin(void) ++{ ++ debugfs_remove(dbgaufs); ++} ++ ++int __init dbgaufs_init(void) ++{ ++ int err; ++ ++ err = -EIO; ++ dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); ++ if (dbgaufs) ++ err = 0; ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/dbgaufs.h linux-4.0.4/fs/aufs/dbgaufs.h +--- linux-4.0.4.orig/fs/aufs/dbgaufs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dbgaufs.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * debugfs interface ++ */ ++ ++#ifndef __DBGAUFS_H__ ++#define __DBGAUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++struct super_block; ++struct au_sbinfo; ++ ++#ifdef CONFIG_DEBUG_FS ++/* dbgaufs.c */ ++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); ++void dbgaufs_si_fin(struct au_sbinfo *sbinfo); ++int dbgaufs_si_init(struct au_sbinfo *sbinfo); ++void dbgaufs_fin(void); ++int __init dbgaufs_init(void); ++#else ++AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo) ++AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo) ++AuStubVoid(dbgaufs_fin, void) ++AuStubInt0(__init dbgaufs_init, void) ++#endif /* CONFIG_DEBUG_FS */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __DBGAUFS_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/dcsub.c linux-4.0.4/fs/aufs/dcsub.c +--- linux-4.0.4.orig/fs/aufs/dcsub.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dcsub.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,211 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sub-routines for dentry cache ++ */ ++ ++#include "aufs.h" ++ ++static void au_dpage_free(struct au_dpage *dpage) ++{ ++ int i; ++ struct dentry **p; ++ ++ p = dpage->dentries; ++ for (i = 0; i < dpage->ndentry; i++) ++ dput(*p++); ++ free_page((unsigned long)dpage->dentries); ++} ++ ++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) ++{ ++ int err; ++ void *p; ++ ++ err = -ENOMEM; ++ dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp); ++ if (unlikely(!dpages->dpages)) ++ goto out; ++ ++ p = (void *)__get_free_page(gfp); ++ if (unlikely(!p)) ++ goto out_dpages; ++ ++ dpages->dpages[0].ndentry = 0; ++ dpages->dpages[0].dentries = p; ++ dpages->ndpage = 1; ++ return 0; /* success */ ++ ++out_dpages: ++ kfree(dpages->dpages); ++out: ++ return err; ++} ++ ++void au_dpages_free(struct au_dcsub_pages *dpages) ++{ ++ int i; ++ struct au_dpage *p; ++ ++ p = dpages->dpages; ++ for (i = 0; i < dpages->ndpage; i++) ++ au_dpage_free(p++); ++ kfree(dpages->dpages); ++} ++ ++static int au_dpages_append(struct au_dcsub_pages *dpages, ++ struct dentry *dentry, gfp_t gfp) ++{ ++ int err, sz; ++ struct au_dpage *dpage; ++ void *p; ++ ++ dpage = dpages->dpages + dpages->ndpage - 1; ++ sz = PAGE_SIZE / sizeof(dentry); ++ if (unlikely(dpage->ndentry >= sz)) { ++ AuLabel(new dpage); ++ err = -ENOMEM; ++ sz = dpages->ndpage * sizeof(*dpages->dpages); ++ p = au_kzrealloc(dpages->dpages, sz, ++ sz + sizeof(*dpages->dpages), gfp); ++ if (unlikely(!p)) ++ goto out; ++ ++ dpages->dpages = p; ++ dpage = dpages->dpages + dpages->ndpage; ++ p = (void *)__get_free_page(gfp); ++ if (unlikely(!p)) ++ goto out; ++ ++ dpage->ndentry = 0; ++ dpage->dentries = p; ++ dpages->ndpage++; ++ } ++ ++ AuDebugOn(au_dcount(dentry) <= 0); ++ dpage->dentries[dpage->ndentry++] = dget_dlock(dentry); ++ return 0; /* success */ ++ ++out: ++ return err; ++} ++ ++/* todo: BAD approach */ ++/* copied from linux/fs/dcache.c */ ++enum d_walk_ret { ++ D_WALK_CONTINUE, ++ D_WALK_QUIT, ++ D_WALK_NORETRY, ++ D_WALK_SKIP, ++}; ++ ++extern void d_walk(struct dentry *parent, void *data, ++ enum d_walk_ret (*enter)(void *, struct dentry *), ++ void (*finish)(void *)); ++ ++struct ac_dpages_arg { ++ int err; ++ struct au_dcsub_pages *dpages; ++ struct super_block *sb; ++ au_dpages_test test; ++ void *arg; ++}; ++ ++static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry) ++{ ++ enum d_walk_ret ret; ++ struct ac_dpages_arg *arg = _arg; ++ ++ ret = D_WALK_CONTINUE; ++ if (dentry->d_sb == arg->sb ++ && !IS_ROOT(dentry) ++ && au_dcount(dentry) > 0 ++ && au_di(dentry) ++ && (!arg->test || arg->test(dentry, arg->arg))) { ++ arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC); ++ if (unlikely(arg->err)) ++ ret = D_WALK_QUIT; ++ } ++ ++ return ret; ++} ++ ++int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, ++ au_dpages_test test, void *arg) ++{ ++ struct ac_dpages_arg args = { ++ .err = 0, ++ .dpages = dpages, ++ .sb = root->d_sb, ++ .test = test, ++ .arg = arg ++ }; ++ ++ d_walk(root, &args, au_call_dpages_append, NULL); ++ ++ return args.err; ++} ++ ++int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, ++ int do_include, au_dpages_test test, void *arg) ++{ ++ int err; ++ ++ err = 0; ++ write_seqlock(&rename_lock); ++ spin_lock(&dentry->d_lock); ++ if (do_include ++ && au_dcount(dentry) > 0 ++ && (!test || test(dentry, arg))) ++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); ++ spin_unlock(&dentry->d_lock); ++ if (unlikely(err)) ++ goto out; ++ ++ /* ++ * RCU for vfsmount is unnecessary since this is a traverse in a single ++ * mount ++ */ ++ while (!IS_ROOT(dentry)) { ++ dentry = dentry->d_parent; /* rename_lock is locked */ ++ spin_lock(&dentry->d_lock); ++ if (au_dcount(dentry) > 0 ++ && (!test || test(dentry, arg))) ++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); ++ spin_unlock(&dentry->d_lock); ++ if (unlikely(err)) ++ break; ++ } ++ ++out: ++ write_sequnlock(&rename_lock); ++ return err; ++} ++ ++static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg) ++{ ++ return au_di(dentry) && dentry->d_sb == arg; ++} ++ ++int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, ++ struct dentry *dentry, int do_include) ++{ ++ return au_dcsub_pages_rev(dpages, dentry, do_include, ++ au_dcsub_dpages_aufs, dentry->d_sb); ++} ++ ++int au_test_subdir(struct dentry *d1, struct dentry *d2) ++{ ++ struct path path[2] = { ++ { ++ .dentry = d1 ++ }, ++ { ++ .dentry = d2 ++ } ++ }; ++ ++ return path_is_under(path + 0, path + 1); ++} +diff -Nur linux-4.0.4.orig/fs/aufs/dcsub.h linux-4.0.4/fs/aufs/dcsub.h +--- linux-4.0.4.orig/fs/aufs/dcsub.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dcsub.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,119 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sub-routines for dentry cache ++ */ ++ ++#ifndef __AUFS_DCSUB_H__ ++#define __AUFS_DCSUB_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++struct au_dpage { ++ int ndentry; ++ struct dentry **dentries; ++}; ++ ++struct au_dcsub_pages { ++ int ndpage; ++ struct au_dpage *dpages; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dcsub.c */ ++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp); ++void au_dpages_free(struct au_dcsub_pages *dpages); ++typedef int (*au_dpages_test)(struct dentry *dentry, void *arg); ++int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, ++ au_dpages_test test, void *arg); ++int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, ++ int do_include, au_dpages_test test, void *arg); ++int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, ++ struct dentry *dentry, int do_include); ++int au_test_subdir(struct dentry *d1, struct dentry *d2); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * todo: in linux-3.13, several similar (but faster) helpers are added to ++ * include/linux/dcache.h. Try them (in the future). ++ */ ++ ++static inline int au_d_hashed_positive(struct dentry *d) ++{ ++ int err; ++ struct inode *inode = d->d_inode; ++ ++ err = 0; ++ if (unlikely(d_unhashed(d) || !inode || !inode->i_nlink)) ++ err = -ENOENT; ++ return err; ++} ++ ++static inline int au_d_linkable(struct dentry *d) ++{ ++ int err; ++ struct inode *inode = d->d_inode; ++ ++ err = au_d_hashed_positive(d); ++ if (err ++ && inode ++ && (inode->i_state & I_LINKABLE)) ++ err = 0; ++ return err; ++} ++ ++static inline int au_d_alive(struct dentry *d) ++{ ++ int err; ++ struct inode *inode; ++ ++ err = 0; ++ if (!IS_ROOT(d)) ++ err = au_d_hashed_positive(d); ++ else { ++ inode = d->d_inode; ++ if (unlikely(d_unlinked(d) || !inode || !inode->i_nlink)) ++ err = -ENOENT; ++ } ++ return err; ++} ++ ++static inline int au_alive_dir(struct dentry *d) ++{ ++ int err; ++ ++ err = au_d_alive(d); ++ if (unlikely(err || IS_DEADDIR(d->d_inode))) ++ err = -ENOENT; ++ return err; ++} ++ ++static inline int au_qstreq(struct qstr *a, struct qstr *b) ++{ ++ return a->len == b->len ++ && !memcmp(a->name, b->name, a->len); ++} ++ ++/* ++ * by the commit ++ * 360f547 2015-01-25 dcache: let the dentry count go down to zero without ++ * taking d_lock ++ * the type of d_lockref.count became int, but the inlined function d_count() ++ * still returns unsigned int. ++ * I don't know why. Maybe it is for every d_count() users? ++ * Anyway au_dcount() lives on. ++ */ ++static inline int au_dcount(struct dentry *d) ++{ ++ return (int)d_count(d); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DCSUB_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/debug.c linux-4.0.4/fs/aufs/debug.c +--- linux-4.0.4.orig/fs/aufs/debug.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/debug.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,425 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * debug print functions ++ */ ++ ++#include "aufs.h" ++ ++/* Returns 0, or -errno. arg is in kp->arg. */ ++static int param_atomic_t_set(const char *val, const struct kernel_param *kp) ++{ ++ int err, n; ++ ++ err = kstrtoint(val, 0, &n); ++ if (!err) { ++ if (n > 0) ++ au_debug_on(); ++ else ++ au_debug_off(); ++ } ++ return err; ++} ++ ++/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ ++static int param_atomic_t_get(char *buffer, const struct kernel_param *kp) ++{ ++ atomic_t *a; ++ ++ a = kp->arg; ++ return sprintf(buffer, "%d", atomic_read(a)); ++} ++ ++static struct kernel_param_ops param_ops_atomic_t = { ++ .set = param_atomic_t_set, ++ .get = param_atomic_t_get ++ /* void (*free)(void *arg) */ ++}; ++ ++atomic_t aufs_debug = ATOMIC_INIT(0); ++MODULE_PARM_DESC(debug, "debug print"); ++module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP); ++ ++DEFINE_MUTEX(au_dbg_mtx); /* just to serialize the dbg msgs */ ++char *au_plevel = KERN_DEBUG; ++#define dpri(fmt, ...) do { \ ++ if ((au_plevel \ ++ && strcmp(au_plevel, KERN_DEBUG)) \ ++ || au_debug_test()) \ ++ printk("%s" fmt, au_plevel, ##__VA_ARGS__); \ ++} while (0) ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_dpri_whlist(struct au_nhash *whlist) ++{ ++ unsigned long ul, n; ++ struct hlist_head *head; ++ struct au_vdir_wh *pos; ++ ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (ul = 0; ul < n; ul++) { ++ hlist_for_each_entry(pos, head, wh_hash) ++ dpri("b%d, %.*s, %d\n", ++ pos->wh_bindex, ++ pos->wh_str.len, pos->wh_str.name, ++ pos->wh_str.len); ++ head++; ++ } ++} ++ ++void au_dpri_vdir(struct au_vdir *vdir) ++{ ++ unsigned long ul; ++ union au_vdir_deblk_p p; ++ unsigned char *o; ++ ++ if (!vdir || IS_ERR(vdir)) { ++ dpri("err %ld\n", PTR_ERR(vdir)); ++ return; ++ } ++ ++ dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n", ++ vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, ++ vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); ++ for (ul = 0; ul < vdir->vd_nblk; ul++) { ++ p.deblk = vdir->vd_deblk[ul]; ++ o = p.deblk; ++ dpri("[%lu]: %p\n", ul, o); ++ } ++} ++ ++static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn, ++ struct dentry *wh) ++{ ++ char *n = NULL; ++ int l = 0; ++ ++ if (!inode || IS_ERR(inode)) { ++ dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); ++ return -1; ++ } ++ ++ /* the type of i_blocks depends upon CONFIG_LBDAF */ ++ BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) ++ && sizeof(inode->i_blocks) != sizeof(u64)); ++ if (wh) { ++ n = (void *)wh->d_name.name; ++ l = wh->d_name.len; ++ } ++ ++ dpri("i%d: %p, i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," ++ " hn %d, ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, g %x%s%.*s\n", ++ bindex, inode, ++ inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", ++ atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, ++ i_size_read(inode), (unsigned long long)inode->i_blocks, ++ hn, (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff, ++ inode->i_mapping ? inode->i_mapping->nrpages : 0, ++ inode->i_state, inode->i_flags, inode->i_version, ++ inode->i_generation, ++ l ? ", wh " : "", l, n); ++ return 0; ++} ++ ++void au_dpri_inode(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ aufs_bindex_t bindex; ++ int err, hn; ++ ++ err = do_pri_inode(-1, inode, -1, NULL); ++ if (err || !au_test_aufs(inode->i_sb)) ++ return; ++ ++ iinfo = au_ii(inode); ++ if (!iinfo) ++ return; ++ dpri("i-1: bstart %d, bend %d, gen %d\n", ++ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL)); ++ if (iinfo->ii_bstart < 0) ++ return; ++ hn = 0; ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) { ++ hn = !!au_hn(iinfo->ii_hinode + bindex); ++ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, hn, ++ iinfo->ii_hinode[0 + bindex].hi_whdentry); ++ } ++} ++ ++void au_dpri_dalias(struct inode *inode) ++{ ++ struct dentry *d; ++ ++ spin_lock(&inode->i_lock); ++ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) ++ au_dpri_dentry(d); ++ spin_unlock(&inode->i_lock); ++} ++ ++static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) ++{ ++ struct dentry *wh = NULL; ++ int hn; ++ struct au_iinfo *iinfo; ++ ++ if (!dentry || IS_ERR(dentry)) { ++ dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); ++ return -1; ++ } ++ /* do not call dget_parent() here */ ++ /* note: access d_xxx without d_lock */ ++ dpri("d%d: %p, %pd2?, %s, cnt %d, flags 0x%x, %shashed\n", ++ bindex, dentry, dentry, ++ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", ++ au_dcount(dentry), dentry->d_flags, ++ d_unhashed(dentry) ? "un" : ""); ++ hn = -1; ++ if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) { ++ iinfo = au_ii(dentry->d_inode); ++ if (iinfo) { ++ hn = !!au_hn(iinfo->ii_hinode + bindex); ++ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; ++ } ++ } ++ do_pri_inode(bindex, dentry->d_inode, hn, wh); ++ return 0; ++} ++ ++void au_dpri_dentry(struct dentry *dentry) ++{ ++ struct au_dinfo *dinfo; ++ aufs_bindex_t bindex; ++ int err; ++ struct au_hdentry *hdp; ++ ++ err = do_pri_dentry(-1, dentry); ++ if (err || !au_test_aufs(dentry->d_sb)) ++ return; ++ ++ dinfo = au_di(dentry); ++ if (!dinfo) ++ return; ++ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d, tmp %d\n", ++ dinfo->di_bstart, dinfo->di_bend, ++ dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry), ++ dinfo->di_tmpfile); ++ if (dinfo->di_bstart < 0) ++ return; ++ hdp = dinfo->di_hdentry; ++ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) ++ do_pri_dentry(bindex, hdp[0 + bindex].hd_dentry); ++} ++ ++static int do_pri_file(aufs_bindex_t bindex, struct file *file) ++{ ++ char a[32]; ++ ++ if (!file || IS_ERR(file)) { ++ dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); ++ return -1; ++ } ++ a[0] = 0; ++ if (bindex < 0 ++ && !IS_ERR_OR_NULL(file->f_path.dentry) ++ && au_test_aufs(file->f_path.dentry->d_sb) ++ && au_fi(file)) ++ snprintf(a, sizeof(a), ", gen %d, mmapped %d", ++ au_figen(file), atomic_read(&au_fi(file)->fi_mmapped)); ++ dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n", ++ bindex, file->f_mode, file->f_flags, (long)file_count(file), ++ file->f_version, file->f_pos, a); ++ if (!IS_ERR_OR_NULL(file->f_path.dentry)) ++ do_pri_dentry(bindex, file->f_path.dentry); ++ return 0; ++} ++ ++void au_dpri_file(struct file *file) ++{ ++ struct au_finfo *finfo; ++ struct au_fidir *fidir; ++ struct au_hfile *hfile; ++ aufs_bindex_t bindex; ++ int err; ++ ++ err = do_pri_file(-1, file); ++ if (err ++ || IS_ERR_OR_NULL(file->f_path.dentry) ++ || !au_test_aufs(file->f_path.dentry->d_sb)) ++ return; ++ ++ finfo = au_fi(file); ++ if (!finfo) ++ return; ++ if (finfo->fi_btop < 0) ++ return; ++ fidir = finfo->fi_hdir; ++ if (!fidir) ++ do_pri_file(finfo->fi_btop, finfo->fi_htop.hf_file); ++ else ++ for (bindex = finfo->fi_btop; ++ bindex >= 0 && bindex <= fidir->fd_bbot; ++ bindex++) { ++ hfile = fidir->fd_hfile + bindex; ++ do_pri_file(bindex, hfile ? hfile->hf_file : NULL); ++ } ++} ++ ++static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) ++{ ++ struct vfsmount *mnt; ++ struct super_block *sb; ++ ++ if (!br || IS_ERR(br)) ++ goto out; ++ mnt = au_br_mnt(br); ++ if (!mnt || IS_ERR(mnt)) ++ goto out; ++ sb = mnt->mnt_sb; ++ if (!sb || IS_ERR(sb)) ++ goto out; ++ ++ dpri("s%d: {perm 0x%x, id %d, cnt %d, wbr %p}, " ++ "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " ++ "xino %d\n", ++ bindex, br->br_perm, br->br_id, atomic_read(&br->br_count), ++ br->br_wbr, au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ sb->s_flags, sb->s_count, ++ atomic_read(&sb->s_active), !!br->br_xino.xi_file); ++ return 0; ++ ++out: ++ dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); ++ return -1; ++} ++ ++void au_dpri_sb(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ aufs_bindex_t bindex; ++ int err; ++ /* to reuduce stack size */ ++ struct { ++ struct vfsmount mnt; ++ struct au_branch fake; ++ } *a; ++ ++ /* this function can be called from magic sysrq */ ++ a = kzalloc(sizeof(*a), GFP_ATOMIC); ++ if (unlikely(!a)) { ++ dpri("no memory\n"); ++ return; ++ } ++ ++ a->mnt.mnt_sb = sb; ++ a->fake.br_perm = 0; ++ a->fake.br_path.mnt = &a->mnt; ++ a->fake.br_xino.xi_file = NULL; ++ atomic_set(&a->fake.br_count, 0); ++ smp_mb(); /* atomic_set */ ++ err = do_pri_br(-1, &a->fake); ++ kfree(a); ++ dpri("dev 0x%x\n", sb->s_dev); ++ if (err || !au_test_aufs(sb)) ++ return; ++ ++ sbinfo = au_sbi(sb); ++ if (!sbinfo) ++ return; ++ dpri("nw %d, gen %u, kobj %d\n", ++ atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, ++ atomic_read(&sbinfo->si_kobj.kref.refcount)); ++ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) ++ do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) ++{ ++ struct inode *h_inode, *inode = dentry->d_inode; ++ struct dentry *h_dentry; ++ aufs_bindex_t bindex, bend, bi; ++ ++ if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) ++ return; ++ ++ bend = au_dbend(dentry); ++ bi = au_ibend(inode); ++ if (bi < bend) ++ bend = bi; ++ bindex = au_dbstart(dentry); ++ bi = au_ibstart(inode); ++ if (bi > bindex) ++ bindex = bi; ++ ++ for (; bindex <= bend; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ h_inode = au_h_iptr(inode, bindex); ++ if (unlikely(h_inode != h_dentry->d_inode)) { ++ au_debug_on(); ++ AuDbg("b%d, %s:%d\n", bindex, func, line); ++ AuDbgDentry(dentry); ++ AuDbgInode(inode); ++ au_debug_off(); ++ BUG(); ++ } ++ } ++} ++ ++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) ++{ ++ int err, i, j; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ AuDebugOn(err); ++ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); ++ AuDebugOn(err); ++ for (i = dpages.ndpage - 1; !err && i >= 0; i--) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ for (j = dpage->ndentry - 1; !err && j >= 0; j--) ++ AuDebugOn(au_digen_test(dentries[j], sigen)); ++ } ++ au_dpages_free(&dpages); ++} ++ ++void au_dbg_verify_kthread(void) ++{ ++ if (au_wkq_test()) { ++ au_dbg_blocked(); ++ /* ++ * It may be recursive, but udba=notify between two aufs mounts, ++ * where a single ro branch is shared, is not a problem. ++ */ ++ /* WARN_ON(1); */ ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int __init au_debug_init(void) ++{ ++ aufs_bindex_t bindex; ++ struct au_vdir_destr destr; ++ ++ bindex = -1; ++ AuDebugOn(bindex >= 0); ++ ++ destr.len = -1; ++ AuDebugOn(destr.len < NAME_MAX); ++ ++#ifdef CONFIG_4KSTACKS ++ pr_warn("CONFIG_4KSTACKS is defined.\n"); ++#endif ++ ++ return 0; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/debug.h linux-4.0.4/fs/aufs/debug.h +--- linux-4.0.4.orig/fs/aufs/debug.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/debug.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,212 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * debug print functions ++ */ ++ ++#ifndef __AUFS_DEBUG_H__ ++#define __AUFS_DEBUG_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define AuDebugOn(a) BUG_ON(a) ++ ++/* module parameter */ ++extern atomic_t aufs_debug; ++static inline void au_debug_on(void) ++{ ++ atomic_inc(&aufs_debug); ++} ++static inline void au_debug_off(void) ++{ ++ atomic_dec_if_positive(&aufs_debug); ++} ++ ++static inline int au_debug_test(void) ++{ ++ return atomic_read(&aufs_debug) > 0; ++} ++#else ++#define AuDebugOn(a) do {} while (0) ++AuStubVoid(au_debug_on, void) ++AuStubVoid(au_debug_off, void) ++AuStubInt0(au_debug_test, void) ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* debug print */ ++ ++#define AuDbg(fmt, ...) do { \ ++ if (au_debug_test()) \ ++ pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \ ++} while (0) ++#define AuLabel(l) AuDbg(#l "\n") ++#define AuIOErr(fmt, ...) pr_err("I/O Error, " fmt, ##__VA_ARGS__) ++#define AuWarn1(fmt, ...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ pr_warn(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define AuErr1(fmt, ...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ pr_err(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define AuIOErr1(fmt, ...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ AuIOErr(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define AuUnsupportMsg "This operation is not supported." \ ++ " Please report this application to aufs-users ML." ++#define AuUnsupport(fmt, ...) do { \ ++ pr_err(AuUnsupportMsg "\n" fmt, ##__VA_ARGS__); \ ++ dump_stack(); \ ++} while (0) ++ ++#define AuTraceErr(e) do { \ ++ if (unlikely((e) < 0)) \ ++ AuDbg("err %d\n", (int)(e)); \ ++} while (0) ++ ++#define AuTraceErrPtr(p) do { \ ++ if (IS_ERR(p)) \ ++ AuDbg("err %ld\n", PTR_ERR(p)); \ ++} while (0) ++ ++/* dirty macros for debug print, use with "%.*s" and caution */ ++#define AuLNPair(qstr) (qstr)->len, (qstr)->name ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry; ++#ifdef CONFIG_AUFS_DEBUG ++extern struct mutex au_dbg_mtx; ++extern char *au_plevel; ++struct au_nhash; ++void au_dpri_whlist(struct au_nhash *whlist); ++struct au_vdir; ++void au_dpri_vdir(struct au_vdir *vdir); ++struct inode; ++void au_dpri_inode(struct inode *inode); ++void au_dpri_dalias(struct inode *inode); ++void au_dpri_dentry(struct dentry *dentry); ++struct file; ++void au_dpri_file(struct file *filp); ++struct super_block; ++void au_dpri_sb(struct super_block *sb); ++ ++#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__) ++void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line); ++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); ++void au_dbg_verify_kthread(void); ++ ++int __init au_debug_init(void); ++ ++#define AuDbgWhlist(w) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#w "\n"); \ ++ au_dpri_whlist(w); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgVdir(v) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#v "\n"); \ ++ au_dpri_vdir(v); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgInode(i) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#i "\n"); \ ++ au_dpri_inode(i); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgDAlias(i) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#i "\n"); \ ++ au_dpri_dalias(i); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgDentry(d) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#d "\n"); \ ++ au_dpri_dentry(d); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgFile(f) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#f "\n"); \ ++ au_dpri_file(f); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgSb(sb) do { \ ++ mutex_lock(&au_dbg_mtx); \ ++ AuDbg(#sb "\n"); \ ++ au_dpri_sb(sb); \ ++ mutex_unlock(&au_dbg_mtx); \ ++} while (0) ++ ++#define AuDbgSym(addr) do { \ ++ char sym[KSYM_SYMBOL_LEN]; \ ++ sprint_symbol(sym, (unsigned long)addr); \ ++ AuDbg("%s\n", sym); \ ++} while (0) ++#else ++AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry) ++AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen) ++AuStubVoid(au_dbg_verify_kthread, void) ++AuStubInt0(__init au_debug_init, void) ++ ++#define AuDbgWhlist(w) do {} while (0) ++#define AuDbgVdir(v) do {} while (0) ++#define AuDbgInode(i) do {} while (0) ++#define AuDbgDAlias(i) do {} while (0) ++#define AuDbgDentry(d) do {} while (0) ++#define AuDbgFile(f) do {} while (0) ++#define AuDbgSb(sb) do {} while (0) ++#define AuDbgSym(addr) do {} while (0) ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_MAGIC_SYSRQ ++int __init au_sysrq_init(void); ++void au_sysrq_fin(void); ++ ++#ifdef CONFIG_HW_CONSOLE ++#define au_dbg_blocked() do { \ ++ WARN_ON(1); \ ++ handle_sysrq('w'); \ ++} while (0) ++#else ++AuStubVoid(au_dbg_blocked, void) ++#endif ++ ++#else ++AuStubInt0(__init au_sysrq_init, void) ++AuStubVoid(au_sysrq_fin, void) ++AuStubVoid(au_dbg_blocked, void) ++#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DEBUG_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/dentry.c linux-4.0.4/fs/aufs/dentry.c +--- linux-4.0.4.orig/fs/aufs/dentry.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dentry.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,1084 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * lookup and dentry operations ++ */ ++ ++#include ++#include "aufs.h" ++ ++#define AuLkup_ALLOW_NEG 1 ++#define AuLkup_IGNORE_PERM (1 << 1) ++#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) ++#define au_fset_lkup(flags, name) \ ++ do { (flags) |= AuLkup_##name; } while (0) ++#define au_fclr_lkup(flags, name) \ ++ do { (flags) &= ~AuLkup_##name; } while (0) ++ ++struct au_do_lookup_args { ++ unsigned int flags; ++ mode_t type; ++}; ++ ++/* ++ * returns positive/negative dentry, NULL or an error. ++ * NULL means whiteout-ed or not-found. ++ */ ++static struct dentry* ++au_do_lookup(struct dentry *h_parent, struct dentry *dentry, ++ aufs_bindex_t bindex, struct qstr *wh_name, ++ struct au_do_lookup_args *args) ++{ ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct au_branch *br; ++ int wh_found, opq; ++ unsigned char wh_able; ++ const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); ++ const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, ++ IGNORE_PERM); ++ ++ wh_found = 0; ++ br = au_sbr(dentry->d_sb, bindex); ++ wh_able = !!au_br_whable(br->br_perm); ++ if (wh_able) ++ wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0); ++ h_dentry = ERR_PTR(wh_found); ++ if (!wh_found) ++ goto real_lookup; ++ if (unlikely(wh_found < 0)) ++ goto out; ++ ++ /* We found a whiteout */ ++ /* au_set_dbend(dentry, bindex); */ ++ au_set_dbwh(dentry, bindex); ++ if (!allow_neg) ++ return NULL; /* success */ ++ ++real_lookup: ++ if (!ignore_perm) ++ h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent); ++ else ++ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); ++ if (IS_ERR(h_dentry)) { ++ if (PTR_ERR(h_dentry) == -ENAMETOOLONG ++ && !allow_neg) ++ h_dentry = NULL; ++ goto out; ++ } ++ ++ h_inode = h_dentry->d_inode; ++ if (!h_inode) { ++ if (!allow_neg) ++ goto out_neg; ++ } else if (wh_found ++ || (args->type && args->type != (h_inode->i_mode & S_IFMT))) ++ goto out_neg; ++ ++ if (au_dbend(dentry) <= bindex) ++ au_set_dbend(dentry, bindex); ++ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) ++ au_set_dbstart(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, h_dentry); ++ ++ if (!d_is_dir(h_dentry) ++ || !wh_able ++ || (d_is_positive(dentry) && !d_is_dir(dentry))) ++ goto out; /* success */ ++ ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ opq = au_diropq_test(h_dentry); ++ mutex_unlock(&h_inode->i_mutex); ++ if (opq > 0) ++ au_set_dbdiropq(dentry, bindex); ++ else if (unlikely(opq < 0)) { ++ au_set_h_dptr(dentry, bindex, NULL); ++ h_dentry = ERR_PTR(opq); ++ } ++ goto out; ++ ++out_neg: ++ dput(h_dentry); ++ h_dentry = NULL; ++out: ++ return h_dentry; ++} ++ ++static int au_test_shwh(struct super_block *sb, const struct qstr *name) ++{ ++ if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) ++ && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) ++ return -EPERM; ++ return 0; ++} ++ ++/* ++ * returns the number of lower positive dentries, ++ * otherwise an error. ++ * can be called at unlinking with @type is zero. ++ */ ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) ++{ ++ int npositive, err; ++ aufs_bindex_t bindex, btail, bdiropq; ++ unsigned char isdir, dirperm1; ++ struct qstr whname; ++ struct au_do_lookup_args args = { ++ .flags = 0, ++ .type = type ++ }; ++ const struct qstr *name = &dentry->d_name; ++ struct dentry *parent; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ err = au_test_shwh(sb, name); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_wh_name_alloc(&whname, name); ++ if (unlikely(err)) ++ goto out; ++ ++ inode = dentry->d_inode; ++ isdir = !!d_is_dir(dentry); ++ if (!type) ++ au_fset_lkup(args.flags, ALLOW_NEG); ++ dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1); ++ ++ npositive = 0; ++ parent = dget_parent(dentry); ++ btail = au_dbtaildir(parent); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ struct dentry *h_parent, *h_dentry; ++ struct inode *h_inode, *h_dir; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry) { ++ if (h_dentry->d_inode) ++ npositive++; ++ if (type != S_IFDIR) ++ break; ++ continue; ++ } ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !d_is_dir(h_parent)) ++ continue; ++ ++ h_dir = h_parent->d_inode; ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, ++ &args); ++ mutex_unlock(&h_dir->i_mutex); ++ err = PTR_ERR(h_dentry); ++ if (IS_ERR(h_dentry)) ++ goto out_parent; ++ if (h_dentry) ++ au_fclr_lkup(args.flags, ALLOW_NEG); ++ if (dirperm1) ++ au_fset_lkup(args.flags, IGNORE_PERM); ++ ++ if (au_dbwh(dentry) >= 0) ++ break; ++ if (!h_dentry) ++ continue; ++ h_inode = h_dentry->d_inode; ++ if (!h_inode) ++ continue; ++ npositive++; ++ if (!args.type) ++ args.type = h_inode->i_mode & S_IFMT; ++ if (args.type != S_IFDIR) ++ break; ++ else if (isdir) { ++ /* the type of lower may be different */ ++ bdiropq = au_dbdiropq(dentry); ++ if (bdiropq >= 0 && bdiropq <= bindex) ++ break; ++ } ++ } ++ ++ if (npositive) { ++ AuLabel(positive); ++ au_update_dbstart(dentry); ++ } ++ err = npositive; ++ if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE) ++ && au_dbstart(dentry) < 0)) { ++ err = -EIO; ++ AuIOErr("both of real entry and whiteout found, %pd, err %d\n", ++ dentry, err); ++ } ++ ++out_parent: ++ dput(parent); ++ kfree(whname.name); ++out: ++ return err; ++} ++ ++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent) ++{ ++ struct dentry *dentry; ++ int wkq_err; ++ ++ if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC)) ++ dentry = vfsub_lkup_one(name, parent); ++ else { ++ struct vfsub_lkup_one_args args = { ++ .errp = &dentry, ++ .name = name, ++ .parent = parent ++ }; ++ ++ wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args); ++ if (unlikely(wkq_err)) ++ dentry = ERR_PTR(wkq_err); ++ } ++ ++ return dentry; ++} ++ ++/* ++ * lookup @dentry on @bindex which should be negative. ++ */ ++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) ++{ ++ int err; ++ struct dentry *parent, *h_parent, *h_dentry; ++ struct au_branch *br; ++ ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bindex); ++ br = au_sbr(dentry->d_sb, bindex); ++ if (wh) ++ h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); ++ else ++ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); ++ err = PTR_ERR(h_dentry); ++ if (IS_ERR(h_dentry)) ++ goto out; ++ if (unlikely(h_dentry->d_inode)) { ++ err = -EIO; ++ AuIOErr("%pd should be negative on b%d.\n", h_dentry, bindex); ++ dput(h_dentry); ++ goto out; ++ } ++ ++ err = 0; ++ if (bindex < au_dbstart(dentry)) ++ au_set_dbstart(dentry, bindex); ++ if (au_dbend(dentry) < bindex) ++ au_set_dbend(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, h_dentry); ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* subset of struct inode */ ++struct au_iattr { ++ unsigned long i_ino; ++ /* unsigned int i_nlink; */ ++ kuid_t i_uid; ++ kgid_t i_gid; ++ u64 i_version; ++/* ++ loff_t i_size; ++ blkcnt_t i_blocks; ++*/ ++ umode_t i_mode; ++}; ++ ++static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) ++{ ++ ia->i_ino = h_inode->i_ino; ++ /* ia->i_nlink = h_inode->i_nlink; */ ++ ia->i_uid = h_inode->i_uid; ++ ia->i_gid = h_inode->i_gid; ++ ia->i_version = h_inode->i_version; ++/* ++ ia->i_size = h_inode->i_size; ++ ia->i_blocks = h_inode->i_blocks; ++*/ ++ ia->i_mode = (h_inode->i_mode & S_IFMT); ++} ++ ++static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) ++{ ++ return ia->i_ino != h_inode->i_ino ++ /* || ia->i_nlink != h_inode->i_nlink */ ++ || !uid_eq(ia->i_uid, h_inode->i_uid) ++ || !gid_eq(ia->i_gid, h_inode->i_gid) ++ || ia->i_version != h_inode->i_version ++/* ++ || ia->i_size != h_inode->i_size ++ || ia->i_blocks != h_inode->i_blocks ++*/ ++ || ia->i_mode != (h_inode->i_mode & S_IFMT); ++} ++ ++static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, ++ struct au_branch *br) ++{ ++ int err; ++ struct au_iattr ia; ++ struct inode *h_inode; ++ struct dentry *h_d; ++ struct super_block *h_sb; ++ ++ err = 0; ++ memset(&ia, -1, sizeof(ia)); ++ h_sb = h_dentry->d_sb; ++ h_inode = h_dentry->d_inode; ++ if (h_inode) ++ au_iattr_save(&ia, h_inode); ++ else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) ++ /* nfs d_revalidate may return 0 for negative dentry */ ++ /* fuse d_revalidate always return 0 for negative dentry */ ++ goto out; ++ ++ /* main purpose is namei.c:cached_lookup() and d_revalidate */ ++ h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent); ++ err = PTR_ERR(h_d); ++ if (IS_ERR(h_d)) ++ goto out; ++ ++ err = 0; ++ if (unlikely(h_d != h_dentry ++ || h_d->d_inode != h_inode ++ || (h_inode && au_iattr_test(&ia, h_inode)))) ++ err = au_busy_or_stale(); ++ dput(h_d); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, ++ struct dentry *h_parent, struct au_branch *br) ++{ ++ int err; ++ ++ err = 0; ++ if (udba == AuOpt_UDBA_REVAL ++ && !au_test_fs_remote(h_dentry->d_sb)) { ++ IMustLock(h_dir); ++ err = (h_dentry->d_parent->d_inode != h_dir); ++ } else if (udba != AuOpt_UDBA_NONE) ++ err = au_h_verify_dentry(h_dentry, h_parent, br); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) ++{ ++ int err; ++ aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; ++ struct au_hdentry tmp, *p, *q; ++ struct au_dinfo *dinfo; ++ struct super_block *sb; ++ ++ DiMustWriteLock(dentry); ++ ++ sb = dentry->d_sb; ++ dinfo = au_di(dentry); ++ bend = dinfo->di_bend; ++ bwh = dinfo->di_bwh; ++ bdiropq = dinfo->di_bdiropq; ++ p = dinfo->di_hdentry + dinfo->di_bstart; ++ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { ++ if (!p->hd_dentry) ++ continue; ++ ++ new_bindex = au_br_index(sb, p->hd_id); ++ if (new_bindex == bindex) ++ continue; ++ ++ if (dinfo->di_bwh == bindex) ++ bwh = new_bindex; ++ if (dinfo->di_bdiropq == bindex) ++ bdiropq = new_bindex; ++ if (new_bindex < 0) { ++ au_hdput(p); ++ p->hd_dentry = NULL; ++ continue; ++ } ++ ++ /* swap two lower dentries, and loop again */ ++ q = dinfo->di_hdentry + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hd_dentry) { ++ bindex--; ++ p--; ++ } ++ } ++ ++ dinfo->di_bwh = -1; ++ if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) ++ dinfo->di_bwh = bwh; ++ ++ dinfo->di_bdiropq = -1; ++ if (bdiropq >= 0 ++ && bdiropq <= au_sbend(sb) ++ && au_sbr_whable(sb, bdiropq)) ++ dinfo->di_bdiropq = bdiropq; ++ ++ err = -EIO; ++ dinfo->di_bstart = -1; ++ dinfo->di_bend = -1; ++ bend = au_dbend(parent); ++ p = dinfo->di_hdentry; ++ for (bindex = 0; bindex <= bend; bindex++, p++) ++ if (p->hd_dentry) { ++ dinfo->di_bstart = bindex; ++ break; ++ } ++ ++ if (dinfo->di_bstart >= 0) { ++ p = dinfo->di_hdentry + bend; ++ for (bindex = bend; bindex >= 0; bindex--, p--) ++ if (p->hd_dentry) { ++ dinfo->di_bend = bindex; ++ err = 0; ++ break; ++ } ++ } ++ ++ return err; ++} ++ ++static void au_do_hide(struct dentry *dentry) ++{ ++ struct inode *inode; ++ ++ inode = dentry->d_inode; ++ if (inode) { ++ if (!S_ISDIR(inode->i_mode)) { ++ if (inode->i_nlink && !d_unhashed(dentry)) ++ drop_nlink(inode); ++ } else { ++ clear_nlink(inode); ++ /* stop next lookup */ ++ inode->i_flags |= S_DEAD; ++ } ++ smp_mb(); /* necessary? */ ++ } ++ d_drop(dentry); ++} ++ ++static int au_hide_children(struct dentry *parent) ++{ ++ int err, i, j, ndentry; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry *dentry; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, parent, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ /* in reverse order */ ++ for (i = dpages.ndpage - 1; i >= 0; i--) { ++ dpage = dpages.dpages + i; ++ ndentry = dpage->ndentry; ++ for (j = ndentry - 1; j >= 0; j--) { ++ dentry = dpage->dentries[j]; ++ if (dentry != parent) ++ au_do_hide(dentry); ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static void au_hide(struct dentry *dentry) ++{ ++ int err; ++ ++ AuDbgDentry(dentry); ++ if (d_is_dir(dentry)) { ++ /* shrink_dcache_parent(dentry); */ ++ err = au_hide_children(dentry); ++ if (unlikely(err)) ++ AuIOErr("%pd, failed hiding children, ignored %d\n", ++ dentry, err); ++ } ++ au_do_hide(dentry); ++} ++ ++/* ++ * By adding a dirty branch, a cached dentry may be affected in various ways. ++ * ++ * a dirty branch is added ++ * - on the top of layers ++ * - in the middle of layers ++ * - to the bottom of layers ++ * ++ * on the added branch there exists ++ * - a whiteout ++ * - a diropq ++ * - a same named entry ++ * + exist ++ * * negative --> positive ++ * * positive --> positive ++ * - type is unchanged ++ * - type is changed ++ * + doesn't exist ++ * * negative --> negative ++ * * positive --> negative (rejected by au_br_del() for non-dir case) ++ * - none ++ */ ++static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, ++ struct au_dinfo *tmp) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct { ++ struct dentry *dentry; ++ struct inode *inode; ++ mode_t mode; ++ } orig_h, tmp_h; ++ struct au_hdentry *hd; ++ struct inode *inode, *h_inode; ++ struct dentry *h_dentry; ++ ++ err = 0; ++ AuDebugOn(dinfo->di_bstart < 0); ++ orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; ++ orig_h.inode = orig_h.dentry->d_inode; ++ orig_h.mode = 0; ++ if (orig_h.inode) ++ orig_h.mode = orig_h.inode->i_mode & S_IFMT; ++ memset(&tmp_h, 0, sizeof(tmp_h)); ++ if (tmp->di_bstart >= 0) { ++ tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; ++ tmp_h.inode = tmp_h.dentry->d_inode; ++ if (tmp_h.inode) ++ tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; ++ } ++ ++ inode = dentry->d_inode; ++ if (!orig_h.inode) { ++ AuDbg("nagative originally\n"); ++ if (inode) { ++ au_hide(dentry); ++ goto out; ++ } ++ AuDebugOn(inode); ++ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); ++ AuDebugOn(dinfo->di_bdiropq != -1); ++ ++ if (!tmp_h.inode) { ++ AuDbg("negative --> negative\n"); ++ /* should have only one negative lower */ ++ if (tmp->di_bstart >= 0 ++ && tmp->di_bstart < dinfo->di_bstart) { ++ AuDebugOn(tmp->di_bstart != tmp->di_bend); ++ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); ++ au_set_h_dptr(dentry, dinfo->di_bstart, NULL); ++ au_di_cp(dinfo, tmp); ++ hd = tmp->di_hdentry + tmp->di_bstart; ++ au_set_h_dptr(dentry, tmp->di_bstart, ++ dget(hd->hd_dentry)); ++ } ++ au_dbg_verify_dinode(dentry); ++ } else { ++ AuDbg("negative --> positive\n"); ++ /* ++ * similar to the behaviour of creating with bypassing ++ * aufs. ++ * unhash it in order to force an error in the ++ * succeeding create operation. ++ * we should not set S_DEAD here. ++ */ ++ d_drop(dentry); ++ /* au_di_swap(tmp, dinfo); */ ++ au_dbg_verify_dinode(dentry); ++ } ++ } else { ++ AuDbg("positive originally\n"); ++ /* inode may be NULL */ ++ AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); ++ if (!tmp_h.inode) { ++ AuDbg("positive --> negative\n"); ++ /* or bypassing aufs */ ++ au_hide(dentry); ++ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) ++ dinfo->di_bwh = tmp->di_bwh; ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ } else if (orig_h.mode == tmp_h.mode) { ++ AuDbg("positive --> positive, same type\n"); ++ if (!S_ISDIR(orig_h.mode) ++ && dinfo->di_bstart > tmp->di_bstart) { ++ /* ++ * similar to the behaviour of removing and ++ * creating. ++ */ ++ au_hide(dentry); ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ } else { ++ /* fill empty slots */ ++ if (dinfo->di_bstart > tmp->di_bstart) ++ dinfo->di_bstart = tmp->di_bstart; ++ if (dinfo->di_bend < tmp->di_bend) ++ dinfo->di_bend = tmp->di_bend; ++ dinfo->di_bwh = tmp->di_bwh; ++ dinfo->di_bdiropq = tmp->di_bdiropq; ++ hd = tmp->di_hdentry; ++ bend = dinfo->di_bend; ++ for (bindex = tmp->di_bstart; bindex <= bend; ++ bindex++) { ++ if (au_h_dptr(dentry, bindex)) ++ continue; ++ h_dentry = hd[bindex].hd_dentry; ++ if (!h_dentry) ++ continue; ++ h_inode = h_dentry->d_inode; ++ AuDebugOn(!h_inode); ++ AuDebugOn(orig_h.mode ++ != (h_inode->i_mode ++ & S_IFMT)); ++ au_set_h_dptr(dentry, bindex, ++ dget(h_dentry)); ++ } ++ err = au_refresh_hinode(inode, dentry); ++ au_dbg_verify_dinode(dentry); ++ } ++ } else { ++ AuDbg("positive --> positive, different type\n"); ++ /* similar to the behaviour of removing and creating */ ++ au_hide(dentry); ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ } ++ } ++ ++out: ++ return err; ++} ++ ++int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) ++{ ++ int err, ebrange; ++ unsigned int sigen; ++ struct au_dinfo *dinfo, *tmp; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ DiMustWriteLock(dentry); ++ AuDebugOn(IS_ROOT(dentry)); ++ AuDebugOn(!parent->d_inode); ++ ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ sigen = au_sigen(sb); ++ err = au_digen_test(parent, sigen); ++ if (unlikely(err)) ++ goto out; ++ ++ dinfo = au_di(dentry); ++ err = au_di_realloc(dinfo, au_sbend(sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ ebrange = au_dbrange_test(dentry); ++ if (!ebrange) ++ ebrange = au_do_refresh_hdentry(dentry, parent); ++ ++ if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { ++ AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ if (!err) ++ goto out_dgen; /* success */ ++ goto out; ++ } ++ ++ /* temporary dinfo */ ++ AuDbgDentry(dentry); ++ err = -ENOMEM; ++ tmp = au_di_alloc(sb, AuLsc_DI_TMP); ++ if (unlikely(!tmp)) ++ goto out; ++ au_di_swap(tmp, dinfo); ++ /* returns the number of positive dentries */ ++ /* ++ * if current working dir is removed, it returns an error. ++ * but the dentry is legal. ++ */ ++ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); ++ AuDbgDentry(dentry); ++ au_di_swap(tmp, dinfo); ++ if (err == -ENOENT) ++ err = 0; ++ if (err >= 0) { ++ /* compare/refresh by dinfo */ ++ AuDbgDentry(dentry); ++ err = au_refresh_by_dinfo(dentry, dinfo, tmp); ++ au_dbg_verify_dinode(dentry); ++ AuTraceErr(err); ++ } ++ au_rw_write_unlock(&tmp->di_rwsem); ++ au_di_free(tmp); ++ if (unlikely(err)) ++ goto out; ++ ++out_dgen: ++ au_update_digen(dentry); ++out: ++ if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { ++ AuIOErr("failed refreshing %pd, %d\n", dentry, err); ++ AuDbgDentry(dentry); ++ } ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_do_h_d_reval(struct dentry *h_dentry, unsigned int flags, ++ struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ int err, valid; ++ ++ err = 0; ++ if (!(h_dentry->d_flags & DCACHE_OP_REVALIDATE)) ++ goto out; ++ ++ AuDbg("b%d\n", bindex); ++ /* ++ * gave up supporting LOOKUP_CREATE/OPEN for lower fs, ++ * due to whiteout and branch permission. ++ */ ++ flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE ++ | LOOKUP_FOLLOW | LOOKUP_EXCL); ++ /* it may return tri-state */ ++ valid = h_dentry->d_op->d_revalidate(h_dentry, flags); ++ ++ if (unlikely(valid < 0)) ++ err = valid; ++ else if (!valid) ++ err = -EINVAL; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* todo: remove this */ ++static int h_d_revalidate(struct dentry *dentry, struct inode *inode, ++ unsigned int flags, int do_udba) ++{ ++ int err; ++ umode_t mode, h_mode; ++ aufs_bindex_t bindex, btail, bstart, ibs, ibe; ++ unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile; ++ struct inode *h_inode, *h_cached_inode; ++ struct dentry *h_dentry; ++ struct qstr *name, *h_name; ++ ++ err = 0; ++ plus = 0; ++ mode = 0; ++ ibs = -1; ++ ibe = -1; ++ unhashed = !!d_unhashed(dentry); ++ is_root = !!IS_ROOT(dentry); ++ name = &dentry->d_name; ++ tmpfile = au_di(dentry)->di_tmpfile; ++ ++ /* ++ * Theoretically, REVAL test should be unnecessary in case of ++ * {FS,I}NOTIFY. ++ * But {fs,i}notify doesn't fire some necessary events, ++ * IN_ATTRIB for atime/nlink/pageio ++ * Let's do REVAL test too. ++ */ ++ if (do_udba && inode) { ++ mode = (inode->i_mode & S_IFMT); ++ plus = (inode->i_nlink > 0); ++ ibs = au_ibstart(inode); ++ ibe = au_ibend(inode); ++ } ++ ++ bstart = au_dbstart(dentry); ++ btail = bstart; ++ if (inode && S_ISDIR(inode->i_mode)) ++ btail = au_dbtaildir(dentry); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ ++ AuDbg("b%d, %pd\n", bindex, h_dentry); ++ h_nfs = !!au_test_nfs(h_dentry->d_sb); ++ spin_lock(&h_dentry->d_lock); ++ h_name = &h_dentry->d_name; ++ if (unlikely(do_udba ++ && !is_root ++ && ((!h_nfs ++ && (unhashed != !!d_unhashed(h_dentry) ++ || (!tmpfile ++ && !au_qstreq(name, h_name)) ++ )) ++ || (h_nfs ++ && !(flags & LOOKUP_OPEN) ++ && (h_dentry->d_flags ++ & DCACHE_NFSFS_RENAMED))) ++ )) { ++ int h_unhashed; ++ ++ h_unhashed = d_unhashed(h_dentry); ++ spin_unlock(&h_dentry->d_lock); ++ AuDbg("unhash 0x%x 0x%x, %pd %pd\n", ++ unhashed, h_unhashed, dentry, h_dentry); ++ goto err; ++ } ++ spin_unlock(&h_dentry->d_lock); ++ ++ err = au_do_h_d_reval(h_dentry, flags, dentry, bindex); ++ if (unlikely(err)) ++ /* do not goto err, to keep the errno */ ++ break; ++ ++ /* todo: plink too? */ ++ if (!do_udba) ++ continue; ++ ++ /* UDBA tests */ ++ h_inode = h_dentry->d_inode; ++ if (unlikely(!!inode != !!h_inode)) ++ goto err; ++ ++ h_plus = plus; ++ h_mode = mode; ++ h_cached_inode = h_inode; ++ if (h_inode) { ++ h_mode = (h_inode->i_mode & S_IFMT); ++ h_plus = (h_inode->i_nlink > 0); ++ } ++ if (inode && ibs <= bindex && bindex <= ibe) ++ h_cached_inode = au_h_iptr(inode, bindex); ++ ++ if (!h_nfs) { ++ if (unlikely(plus != h_plus && !tmpfile)) ++ goto err; ++ } else { ++ if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED) ++ && !is_root ++ && !IS_ROOT(h_dentry) ++ && unhashed != d_unhashed(h_dentry))) ++ goto err; ++ } ++ if (unlikely(mode != h_mode ++ || h_cached_inode != h_inode)) ++ goto err; ++ continue; ++ ++err: ++ err = -EINVAL; ++ break; ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* todo: consolidate with do_refresh() and au_reval_for_attr() */ ++static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct dentry *parent; ++ ++ if (!au_digen_test(dentry, sigen)) ++ return 0; ++ ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ AuDebugOn(au_digen_test(parent, sigen)); ++ au_dbg_verify_gen(parent, sigen); ++ err = au_refresh_dentry(dentry, parent); ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_reval_dpath(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct dentry *d, *parent; ++ struct inode *inode; ++ ++ if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) ++ return simple_reval_dpath(dentry, sigen); ++ ++ /* slow loop, keep it simple and stupid */ ++ /* cf: au_cpup_dirs() */ ++ err = 0; ++ parent = NULL; ++ while (au_digen_test(dentry, sigen)) { ++ d = dentry; ++ while (1) { ++ dput(parent); ++ parent = dget_parent(d); ++ if (!au_digen_test(parent, sigen)) ++ break; ++ d = parent; ++ } ++ ++ inode = d->d_inode; ++ if (d != dentry) ++ di_write_lock_child2(d); ++ ++ /* someone might update our dentry while we were sleeping */ ++ if (au_digen_test(d, sigen)) { ++ /* ++ * todo: consolidate with simple_reval_dpath(), ++ * do_refresh() and au_reval_for_attr(). ++ */ ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_refresh_dentry(d, parent); ++ di_read_unlock(parent, AuLock_IR); ++ } ++ ++ if (d != dentry) ++ di_write_unlock(d); ++ dput(parent); ++ if (unlikely(err)) ++ break; ++ } ++ ++ return err; ++} ++ ++/* ++ * if valid returns 1, otherwise 0. ++ */ ++static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) ++{ ++ int valid, err; ++ unsigned int sigen; ++ unsigned char do_udba; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ /* todo: support rcu-walk? */ ++ if (flags & LOOKUP_RCU) ++ return -ECHILD; ++ ++ valid = 0; ++ if (unlikely(!au_di(dentry))) ++ goto out; ++ ++ valid = 1; ++ sb = dentry->d_sb; ++ /* ++ * todo: very ugly ++ * i_mutex of parent dir may be held, ++ * but we should not return 'invalid' due to busy. ++ */ ++ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); ++ if (unlikely(err)) { ++ valid = err; ++ AuTraceErr(err); ++ goto out; ++ } ++ inode = dentry->d_inode; ++ if (unlikely(inode && is_bad_inode(inode))) { ++ err = -EINVAL; ++ AuTraceErr(err); ++ goto out_dgrade; ++ } ++ if (unlikely(au_dbrange_test(dentry))) { ++ err = -EINVAL; ++ AuTraceErr(err); ++ goto out_dgrade; ++ } ++ ++ sigen = au_sigen(sb); ++ if (au_digen_test(dentry, sigen)) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_reval_dpath(dentry, sigen); ++ if (unlikely(err)) { ++ AuTraceErr(err); ++ goto out_dgrade; ++ } ++ } ++ di_downgrade_lock(dentry, AuLock_IR); ++ ++ err = -EINVAL; ++ if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) ++ && inode ++ && !(inode->i_state && I_LINKABLE) ++ && (IS_DEADDIR(inode) || !inode->i_nlink)) ++ goto out_inval; ++ ++ do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); ++ if (do_udba && inode) { ++ aufs_bindex_t bstart = au_ibstart(inode); ++ struct inode *h_inode; ++ ++ if (bstart >= 0) { ++ h_inode = au_h_iptr(inode, bstart); ++ if (h_inode && au_test_higen(inode, h_inode)) ++ goto out_inval; ++ } ++ } ++ ++ err = h_d_revalidate(dentry, inode, flags, do_udba); ++ if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { ++ err = -EIO; ++ AuDbg("both of real entry and whiteout found, %p, err %d\n", ++ dentry, err); ++ } ++ goto out_inval; ++ ++out_dgrade: ++ di_downgrade_lock(dentry, AuLock_IR); ++out_inval: ++ aufs_read_unlock(dentry, AuLock_IR); ++ AuTraceErr(err); ++ valid = !err; ++out: ++ if (!valid) { ++ AuDbg("%pd invalid, %d\n", dentry, valid); ++ d_drop(dentry); ++ } ++ return valid; ++} ++ ++static void aufs_d_release(struct dentry *dentry) ++{ ++ if (au_di(dentry)) { ++ au_di_fin(dentry); ++ au_hn_di_reinit(dentry); ++ } ++} ++ ++const struct dentry_operations aufs_dop = { ++ .d_revalidate = aufs_d_revalidate, ++ .d_weak_revalidate = aufs_d_revalidate, ++ .d_release = aufs_d_release ++}; +diff -Nur linux-4.0.4.orig/fs/aufs/dentry.h linux-4.0.4/fs/aufs/dentry.h +--- linux-4.0.4.orig/fs/aufs/dentry.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dentry.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,220 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * lookup and dentry operations ++ */ ++ ++#ifndef __AUFS_DENTRY_H__ ++#define __AUFS_DENTRY_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "rwsem.h" ++ ++struct au_hdentry { ++ struct dentry *hd_dentry; ++ aufs_bindex_t hd_id; ++}; ++ ++struct au_dinfo { ++ atomic_t di_generation; ++ ++ struct au_rwsem di_rwsem; ++ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; ++ unsigned char di_tmpfile; /* to allow the different name */ ++ struct au_hdentry *di_hdentry; ++} ____cacheline_aligned_in_smp; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dentry.c */ ++extern const struct dentry_operations aufs_dop; ++struct au_branch; ++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent); ++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, ++ struct dentry *h_parent, struct au_branch *br); ++ ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type); ++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); ++int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); ++int au_reval_dpath(struct dentry *dentry, unsigned int sigen); ++ ++/* dinfo.c */ ++void au_di_init_once(void *_di); ++struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); ++void au_di_free(struct au_dinfo *dinfo); ++void au_di_swap(struct au_dinfo *a, struct au_dinfo *b); ++void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); ++int au_di_init(struct dentry *dentry); ++void au_di_fin(struct dentry *dentry); ++int au_di_realloc(struct au_dinfo *dinfo, int nbr); ++ ++void di_read_lock(struct dentry *d, int flags, unsigned int lsc); ++void di_read_unlock(struct dentry *d, int flags); ++void di_downgrade_lock(struct dentry *d, int flags); ++void di_write_lock(struct dentry *d, unsigned int lsc); ++void di_write_unlock(struct dentry *d); ++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); ++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); ++void di_write_unlock2(struct dentry *d1, struct dentry *d2); ++ ++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); ++struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex); ++aufs_bindex_t au_dbtail(struct dentry *dentry); ++aufs_bindex_t au_dbtaildir(struct dentry *dentry); ++ ++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++int au_digen_test(struct dentry *dentry, unsigned int sigen); ++int au_dbrange_test(struct dentry *dentry); ++void au_update_digen(struct dentry *dentry); ++void au_update_dbrange(struct dentry *dentry, int do_put_zero); ++void au_update_dbstart(struct dentry *dentry); ++void au_update_dbend(struct dentry *dentry); ++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_dinfo *au_di(struct dentry *dentry) ++{ ++ return dentry->d_fsdata; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for dinfo */ ++enum { ++ AuLsc_DI_CHILD, /* child first */ ++ AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */ ++ AuLsc_DI_CHILD3, /* copyup dirs */ ++ AuLsc_DI_PARENT, ++ AuLsc_DI_PARENT2, ++ AuLsc_DI_PARENT3, ++ AuLsc_DI_TMP /* temp for replacing dinfo */ ++}; ++ ++/* ++ * di_read_lock_child, di_write_lock_child, ++ * di_read_lock_child2, di_write_lock_child2, ++ * di_read_lock_child3, di_write_lock_child3, ++ * di_read_lock_parent, di_write_lock_parent, ++ * di_read_lock_parent2, di_write_lock_parent2, ++ * di_read_lock_parent3, di_write_lock_parent3, ++ */ ++#define AuReadLockFunc(name, lsc) \ ++static inline void di_read_lock_##name(struct dentry *d, int flags) \ ++{ di_read_lock(d, flags, AuLsc_DI_##lsc); } ++ ++#define AuWriteLockFunc(name, lsc) \ ++static inline void di_write_lock_##name(struct dentry *d) \ ++{ di_write_lock(d, AuLsc_DI_##lsc); } ++ ++#define AuRWLockFuncs(name, lsc) \ ++ AuReadLockFunc(name, lsc) \ ++ AuWriteLockFunc(name, lsc) ++ ++AuRWLockFuncs(child, CHILD); ++AuRWLockFuncs(child2, CHILD2); ++AuRWLockFuncs(child3, CHILD3); ++AuRWLockFuncs(parent, PARENT); ++AuRWLockFuncs(parent2, PARENT2); ++AuRWLockFuncs(parent3, PARENT3); ++ ++#undef AuReadLockFunc ++#undef AuWriteLockFunc ++#undef AuRWLockFuncs ++ ++#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) ++#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem) ++#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: memory barrier? */ ++static inline unsigned int au_digen(struct dentry *d) ++{ ++ return atomic_read(&au_di(d)->di_generation); ++} ++ ++static inline void au_h_dentry_init(struct au_hdentry *hdentry) ++{ ++ hdentry->hd_dentry = NULL; ++} ++ ++static inline void au_hdput(struct au_hdentry *hd) ++{ ++ if (hd) ++ dput(hd->hd_dentry); ++} ++ ++static inline aufs_bindex_t au_dbstart(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bstart; ++} ++ ++static inline aufs_bindex_t au_dbend(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bend; ++} ++ ++static inline aufs_bindex_t au_dbwh(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bwh; ++} ++ ++static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bdiropq; ++} ++ ++/* todo: hard/soft set? */ ++static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ au_di(dentry)->di_bstart = bindex; ++} ++ ++static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ au_di(dentry)->di_bend = bindex; ++} ++ ++static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ /* dbwh can be outside of bstart - bend range */ ++ au_di(dentry)->di_bwh = bindex; ++} ++ ++static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ au_di(dentry)->di_bdiropq = bindex; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_HNOTIFY ++static inline void au_digen_dec(struct dentry *d) ++{ ++ atomic_dec(&au_di(d)->di_generation); ++} ++ ++static inline void au_hn_di_reinit(struct dentry *dentry) ++{ ++ dentry->d_fsdata = NULL; ++} ++#else ++AuStubVoid(au_hn_di_reinit, struct dentry *dentry __maybe_unused) ++#endif /* CONFIG_AUFS_HNOTIFY */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DENTRY_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/dinfo.c linux-4.0.4/fs/aufs/dinfo.c +--- linux-4.0.4.orig/fs/aufs/dinfo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dinfo.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,531 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * dentry private data ++ */ ++ ++#include "aufs.h" ++ ++void au_di_init_once(void *_dinfo) ++{ ++ struct au_dinfo *dinfo = _dinfo; ++ static struct lock_class_key aufs_di; ++ ++ au_rw_init(&dinfo->di_rwsem); ++ au_rw_class(&dinfo->di_rwsem, &aufs_di); ++} ++ ++struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) ++{ ++ struct au_dinfo *dinfo; ++ int nbr, i; ++ ++ dinfo = au_cache_alloc_dinfo(); ++ if (unlikely(!dinfo)) ++ goto out; ++ ++ nbr = au_sbend(sb) + 1; ++ if (nbr <= 0) ++ nbr = 1; ++ dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); ++ if (dinfo->di_hdentry) { ++ au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); ++ dinfo->di_bstart = -1; ++ dinfo->di_bend = -1; ++ dinfo->di_bwh = -1; ++ dinfo->di_bdiropq = -1; ++ dinfo->di_tmpfile = 0; ++ for (i = 0; i < nbr; i++) ++ dinfo->di_hdentry[i].hd_id = -1; ++ goto out; ++ } ++ ++ au_cache_free_dinfo(dinfo); ++ dinfo = NULL; ++ ++out: ++ return dinfo; ++} ++ ++void au_di_free(struct au_dinfo *dinfo) ++{ ++ struct au_hdentry *p; ++ aufs_bindex_t bend, bindex; ++ ++ /* dentry may not be revalidated */ ++ bindex = dinfo->di_bstart; ++ if (bindex >= 0) { ++ bend = dinfo->di_bend; ++ p = dinfo->di_hdentry + bindex; ++ while (bindex++ <= bend) ++ au_hdput(p++); ++ } ++ kfree(dinfo->di_hdentry); ++ au_cache_free_dinfo(dinfo); ++} ++ ++void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) ++{ ++ struct au_hdentry *p; ++ aufs_bindex_t bi; ++ ++ AuRwMustWriteLock(&a->di_rwsem); ++ AuRwMustWriteLock(&b->di_rwsem); ++ ++#define DiSwap(v, name) \ ++ do { \ ++ v = a->di_##name; \ ++ a->di_##name = b->di_##name; \ ++ b->di_##name = v; \ ++ } while (0) ++ ++ DiSwap(p, hdentry); ++ DiSwap(bi, bstart); ++ DiSwap(bi, bend); ++ DiSwap(bi, bwh); ++ DiSwap(bi, bdiropq); ++ /* smp_mb(); */ ++ ++#undef DiSwap ++} ++ ++void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) ++{ ++ AuRwMustWriteLock(&dst->di_rwsem); ++ AuRwMustWriteLock(&src->di_rwsem); ++ ++ dst->di_bstart = src->di_bstart; ++ dst->di_bend = src->di_bend; ++ dst->di_bwh = src->di_bwh; ++ dst->di_bdiropq = src->di_bdiropq; ++ /* smp_mb(); */ ++} ++ ++int au_di_init(struct dentry *dentry) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_dinfo *dinfo; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); ++ if (dinfo) { ++ atomic_set(&dinfo->di_generation, au_sigen(sb)); ++ /* smp_mb(); */ /* atomic_set */ ++ dentry->d_fsdata = dinfo; ++ } else ++ err = -ENOMEM; ++ ++ return err; ++} ++ ++void au_di_fin(struct dentry *dentry) ++{ ++ struct au_dinfo *dinfo; ++ ++ dinfo = au_di(dentry); ++ AuRwDestroy(&dinfo->di_rwsem); ++ au_di_free(dinfo); ++} ++ ++int au_di_realloc(struct au_dinfo *dinfo, int nbr) ++{ ++ int err, sz; ++ struct au_hdentry *hdp; ++ ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ err = -ENOMEM; ++ sz = sizeof(*hdp) * (dinfo->di_bend + 1); ++ if (!sz) ++ sz = sizeof(*hdp); ++ hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); ++ if (hdp) { ++ dinfo->di_hdentry = hdp; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void do_ii_write_lock(struct inode *inode, unsigned int lsc) ++{ ++ switch (lsc) { ++ case AuLsc_DI_CHILD: ++ ii_write_lock_child(inode); ++ break; ++ case AuLsc_DI_CHILD2: ++ ii_write_lock_child2(inode); ++ break; ++ case AuLsc_DI_CHILD3: ++ ii_write_lock_child3(inode); ++ break; ++ case AuLsc_DI_PARENT: ++ ii_write_lock_parent(inode); ++ break; ++ case AuLsc_DI_PARENT2: ++ ii_write_lock_parent2(inode); ++ break; ++ case AuLsc_DI_PARENT3: ++ ii_write_lock_parent3(inode); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static void do_ii_read_lock(struct inode *inode, unsigned int lsc) ++{ ++ switch (lsc) { ++ case AuLsc_DI_CHILD: ++ ii_read_lock_child(inode); ++ break; ++ case AuLsc_DI_CHILD2: ++ ii_read_lock_child2(inode); ++ break; ++ case AuLsc_DI_CHILD3: ++ ii_read_lock_child3(inode); ++ break; ++ case AuLsc_DI_PARENT: ++ ii_read_lock_parent(inode); ++ break; ++ case AuLsc_DI_PARENT2: ++ ii_read_lock_parent2(inode); ++ break; ++ case AuLsc_DI_PARENT3: ++ ii_read_lock_parent3(inode); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++void di_read_lock(struct dentry *d, int flags, unsigned int lsc) ++{ ++ au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); ++ if (d->d_inode) { ++ if (au_ftest_lock(flags, IW)) ++ do_ii_write_lock(d->d_inode, lsc); ++ else if (au_ftest_lock(flags, IR)) ++ do_ii_read_lock(d->d_inode, lsc); ++ } ++} ++ ++void di_read_unlock(struct dentry *d, int flags) ++{ ++ if (d->d_inode) { ++ if (au_ftest_lock(flags, IW)) { ++ au_dbg_verify_dinode(d); ++ ii_write_unlock(d->d_inode); ++ } else if (au_ftest_lock(flags, IR)) { ++ au_dbg_verify_dinode(d); ++ ii_read_unlock(d->d_inode); ++ } ++ } ++ au_rw_read_unlock(&au_di(d)->di_rwsem); ++} ++ ++void di_downgrade_lock(struct dentry *d, int flags) ++{ ++ if (d->d_inode && au_ftest_lock(flags, IR)) ++ ii_downgrade_lock(d->d_inode); ++ au_rw_dgrade_lock(&au_di(d)->di_rwsem); ++} ++ ++void di_write_lock(struct dentry *d, unsigned int lsc) ++{ ++ au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); ++ if (d->d_inode) ++ do_ii_write_lock(d->d_inode, lsc); ++} ++ ++void di_write_unlock(struct dentry *d) ++{ ++ au_dbg_verify_dinode(d); ++ if (d->d_inode) ++ ii_write_unlock(d->d_inode); ++ au_rw_write_unlock(&au_di(d)->di_rwsem); ++} ++ ++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) ++{ ++ AuDebugOn(d1 == d2 ++ || d1->d_inode == d2->d_inode ++ || d1->d_sb != d2->d_sb); ++ ++ if (isdir && au_test_subdir(d1, d2)) { ++ di_write_lock_child(d1); ++ di_write_lock_child2(d2); ++ } else { ++ /* there should be no races */ ++ di_write_lock_child(d2); ++ di_write_lock_child2(d1); ++ } ++} ++ ++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) ++{ ++ AuDebugOn(d1 == d2 ++ || d1->d_inode == d2->d_inode ++ || d1->d_sb != d2->d_sb); ++ ++ if (isdir && au_test_subdir(d1, d2)) { ++ di_write_lock_parent(d1); ++ di_write_lock_parent2(d2); ++ } else { ++ /* there should be no races */ ++ di_write_lock_parent(d2); ++ di_write_lock_parent2(d1); ++ } ++} ++ ++void di_write_unlock2(struct dentry *d1, struct dentry *d2) ++{ ++ di_write_unlock(d1); ++ if (d1->d_inode == d2->d_inode) ++ au_rw_write_unlock(&au_di(d2)->di_rwsem); ++ else ++ di_write_unlock(d2); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ struct dentry *d; ++ ++ DiMustAnyLock(dentry); ++ ++ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) ++ return NULL; ++ AuDebugOn(bindex < 0); ++ d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; ++ AuDebugOn(d && au_dcount(d) <= 0); ++ return d; ++} ++ ++/* ++ * extended version of au_h_dptr(). ++ * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or ++ * error. ++ */ ++struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ struct dentry *h_dentry; ++ struct inode *inode, *h_inode; ++ ++ inode = dentry->d_inode; ++ AuDebugOn(!inode); ++ ++ h_dentry = NULL; ++ if (au_dbstart(dentry) <= bindex ++ && bindex <= au_dbend(dentry)) ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && !au_d_linkable(h_dentry)) { ++ dget(h_dentry); ++ goto out; /* success */ ++ } ++ ++ AuDebugOn(bindex < au_ibstart(inode)); ++ AuDebugOn(au_ibend(inode) < bindex); ++ h_inode = au_h_iptr(inode, bindex); ++ h_dentry = d_find_alias(h_inode); ++ if (h_dentry) { ++ if (!IS_ERR(h_dentry)) { ++ if (!au_d_linkable(h_dentry)) ++ goto out; /* success */ ++ dput(h_dentry); ++ } else ++ goto out; ++ } ++ ++ if (au_opt_test(au_mntflags(dentry->d_sb), PLINK)) { ++ h_dentry = au_plink_lkup(inode, bindex); ++ AuDebugOn(!h_dentry); ++ if (!IS_ERR(h_dentry)) { ++ if (!au_d_hashed_positive(h_dentry)) ++ goto out; /* success */ ++ dput(h_dentry); ++ h_dentry = NULL; ++ } ++ } ++ ++out: ++ AuDbgDentry(h_dentry); ++ return h_dentry; ++} ++ ++aufs_bindex_t au_dbtail(struct dentry *dentry) ++{ ++ aufs_bindex_t bend, bwh; ++ ++ bend = au_dbend(dentry); ++ if (0 <= bend) { ++ bwh = au_dbwh(dentry); ++ if (!bwh) ++ return bwh; ++ if (0 < bwh && bwh < bend) ++ return bwh - 1; ++ } ++ return bend; ++} ++ ++aufs_bindex_t au_dbtaildir(struct dentry *dentry) ++{ ++ aufs_bindex_t bend, bopq; ++ ++ bend = au_dbtail(dentry); ++ if (0 <= bend) { ++ bopq = au_dbdiropq(dentry); ++ if (0 <= bopq && bopq < bend) ++ bend = bopq; ++ } ++ return bend; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_dentry) ++{ ++ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; ++ struct au_branch *br; ++ ++ DiMustWriteLock(dentry); ++ ++ au_hdput(hd); ++ hd->hd_dentry = h_dentry; ++ if (h_dentry) { ++ br = au_sbr(dentry->d_sb, bindex); ++ hd->hd_id = br->br_id; ++ } ++} ++ ++int au_dbrange_test(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bstart, bend; ++ ++ err = 0; ++ bstart = au_dbstart(dentry); ++ bend = au_dbend(dentry); ++ if (bstart >= 0) ++ AuDebugOn(bend < 0 && bstart > bend); ++ else { ++ err = -EIO; ++ AuDebugOn(bend >= 0); ++ } ++ ++ return err; ++} ++ ++int au_digen_test(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(au_digen(dentry) != sigen ++ || au_iigen_test(dentry->d_inode, sigen))) ++ err = -EIO; ++ ++ return err; ++} ++ ++void au_update_digen(struct dentry *dentry) ++{ ++ atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); ++ /* smp_mb(); */ /* atomic_set */ ++} ++ ++void au_update_dbrange(struct dentry *dentry, int do_put_zero) ++{ ++ struct au_dinfo *dinfo; ++ struct dentry *h_d; ++ struct au_hdentry *hdp; ++ ++ DiMustWriteLock(dentry); ++ ++ dinfo = au_di(dentry); ++ if (!dinfo || dinfo->di_bstart < 0) ++ return; ++ ++ hdp = dinfo->di_hdentry; ++ if (do_put_zero) { ++ aufs_bindex_t bindex, bend; ++ ++ bend = dinfo->di_bend; ++ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { ++ h_d = hdp[0 + bindex].hd_dentry; ++ if (h_d && !h_d->d_inode) ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++ } ++ ++ dinfo->di_bstart = -1; ++ while (++dinfo->di_bstart <= dinfo->di_bend) ++ if (hdp[0 + dinfo->di_bstart].hd_dentry) ++ break; ++ if (dinfo->di_bstart > dinfo->di_bend) { ++ dinfo->di_bstart = -1; ++ dinfo->di_bend = -1; ++ return; ++ } ++ ++ dinfo->di_bend++; ++ while (0 <= --dinfo->di_bend) ++ if (hdp[0 + dinfo->di_bend].hd_dentry) ++ break; ++ AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); ++} ++ ++void au_update_dbstart(struct dentry *dentry) ++{ ++ aufs_bindex_t bindex, bend; ++ struct dentry *h_dentry; ++ ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ if (h_dentry->d_inode) { ++ au_set_dbstart(dentry, bindex); ++ return; ++ } ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++} ++ ++void au_update_dbend(struct dentry *dentry) ++{ ++ aufs_bindex_t bindex, bstart; ++ struct dentry *h_dentry; ++ ++ bstart = au_dbstart(dentry); ++ for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ if (h_dentry->d_inode) { ++ au_set_dbend(dentry, bindex); ++ return; ++ } ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++} ++ ++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) ++{ ++ aufs_bindex_t bindex, bend; ++ ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) ++ if (au_h_dptr(dentry, bindex) == h_dentry) ++ return bindex; ++ return -1; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/dir.c linux-4.0.4/fs/aufs/dir.c +--- linux-4.0.4.orig/fs/aufs/dir.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dir.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,738 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * directory operations ++ */ ++ ++#include ++#include "aufs.h" ++ ++void au_add_nlink(struct inode *dir, struct inode *h_dir) ++{ ++ unsigned int nlink; ++ ++ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); ++ ++ nlink = dir->i_nlink; ++ nlink += h_dir->i_nlink - 2; ++ if (h_dir->i_nlink < 2) ++ nlink += 2; ++ smp_mb(); /* for i_nlink */ ++ /* 0 can happen in revaliding */ ++ set_nlink(dir, nlink); ++} ++ ++void au_sub_nlink(struct inode *dir, struct inode *h_dir) ++{ ++ unsigned int nlink; ++ ++ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); ++ ++ nlink = dir->i_nlink; ++ nlink -= h_dir->i_nlink - 2; ++ if (h_dir->i_nlink < 2) ++ nlink -= 2; ++ smp_mb(); /* for i_nlink */ ++ /* nlink == 0 means the branch-fs is broken */ ++ set_nlink(dir, nlink); ++} ++ ++loff_t au_dir_size(struct file *file, struct dentry *dentry) ++{ ++ loff_t sz; ++ aufs_bindex_t bindex, bend; ++ struct file *h_file; ++ struct dentry *h_dentry; ++ ++ sz = 0; ++ if (file) { ++ AuDebugOn(!d_is_dir(file->f_path.dentry)); ++ ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); ++ bindex <= bend && sz < KMALLOC_MAX_SIZE; ++ bindex++) { ++ h_file = au_hf_dir(file, bindex); ++ if (h_file && file_inode(h_file)) ++ sz += vfsub_f_size_read(h_file); ++ } ++ } else { ++ AuDebugOn(!dentry); ++ AuDebugOn(!d_is_dir(dentry)); ++ ++ bend = au_dbtaildir(dentry); ++ for (bindex = au_dbstart(dentry); ++ bindex <= bend && sz < KMALLOC_MAX_SIZE; ++ bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && h_dentry->d_inode) ++ sz += i_size_read(h_dentry->d_inode); ++ } ++ } ++ if (sz < KMALLOC_MAX_SIZE) ++ sz = roundup_pow_of_two(sz); ++ if (sz > KMALLOC_MAX_SIZE) ++ sz = KMALLOC_MAX_SIZE; ++ else if (sz < NAME_MAX) { ++ BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); ++ sz = AUFS_RDBLK_DEF; ++ } ++ return sz; ++} ++ ++struct au_dir_ts_arg { ++ struct dentry *dentry; ++ aufs_bindex_t brid; ++}; ++ ++static void au_do_dir_ts(void *arg) ++{ ++ struct au_dir_ts_arg *a = arg; ++ struct au_dtime dt; ++ struct path h_path; ++ struct inode *dir, *h_dir; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_hinode *hdir; ++ int err; ++ aufs_bindex_t bstart, bindex; ++ ++ sb = a->dentry->d_sb; ++ dir = a->dentry->d_inode; ++ if (!dir) ++ goto out; ++ /* no dir->i_mutex lock */ ++ aufs_read_lock(a->dentry, AuLock_DW | AuLock_DIR); /* noflush */ ++ ++ bstart = au_ibstart(dir); ++ bindex = au_br_index(sb, a->brid); ++ if (bindex < bstart) ++ goto out_unlock; ++ ++ br = au_sbr(sb, bindex); ++ h_path.dentry = au_h_dptr(a->dentry, bindex); ++ if (!h_path.dentry) ++ goto out_unlock; ++ h_path.mnt = au_br_mnt(br); ++ au_dtime_store(&dt, a->dentry, &h_path); ++ ++ br = au_sbr(sb, bstart); ++ if (!au_br_writable(br->br_perm)) ++ goto out_unlock; ++ h_path.dentry = au_h_dptr(a->dentry, bstart); ++ h_path.mnt = au_br_mnt(br); ++ err = vfsub_mnt_want_write(h_path.mnt); ++ if (err) ++ goto out_unlock; ++ hdir = au_hi(dir, bstart); ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ h_dir = au_h_iptr(dir, bstart); ++ if (h_dir->i_nlink ++ && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) { ++ dt.dt_h_path = h_path; ++ au_dtime_revert(&dt); ++ } ++ au_hn_imtx_unlock(hdir); ++ vfsub_mnt_drop_write(h_path.mnt); ++ au_cpup_attr_timesizes(dir); ++ ++out_unlock: ++ aufs_read_unlock(a->dentry, AuLock_DW); ++out: ++ dput(a->dentry); ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ kfree(arg); ++} ++ ++void au_dir_ts(struct inode *dir, aufs_bindex_t bindex) ++{ ++ int perm, wkq_err; ++ aufs_bindex_t bstart; ++ struct au_dir_ts_arg *arg; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ IMustLock(dir); ++ ++ dentry = d_find_any_alias(dir); ++ AuDebugOn(!dentry); ++ sb = dentry->d_sb; ++ bstart = au_ibstart(dir); ++ if (bstart == bindex) { ++ au_cpup_attr_timesizes(dir); ++ goto out; ++ } ++ ++ perm = au_sbr_perm(sb, bstart); ++ if (!au_br_writable(perm)) ++ goto out; ++ ++ arg = kmalloc(sizeof(*arg), GFP_NOFS); ++ if (!arg) ++ goto out; ++ ++ arg->dentry = dget(dentry); /* will be dput-ted by au_do_dir_ts() */ ++ arg->brid = au_sbr_id(sb, bindex); ++ wkq_err = au_wkq_nowait(au_do_dir_ts, arg, sb, /*flags*/0); ++ if (unlikely(wkq_err)) { ++ pr_err("wkq %d\n", wkq_err); ++ dput(dentry); ++ kfree(arg); ++ } ++ ++out: ++ dput(dentry); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int reopen_dir(struct file *file) ++{ ++ int err; ++ unsigned int flags; ++ aufs_bindex_t bindex, btail, bstart; ++ struct dentry *dentry, *h_dentry; ++ struct file *h_file; ++ ++ /* open all lower dirs */ ++ dentry = file->f_path.dentry; ++ bstart = au_dbstart(dentry); ++ for (bindex = au_fbstart(file); bindex < bstart; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbstart(file, bstart); ++ ++ btail = au_dbtaildir(dentry); ++ for (bindex = au_fbend_dir(file); btail < bindex; bindex--) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbend_dir(file, btail); ++ ++ flags = vfsub_file_flags(file); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ h_file = au_hf_dir(file, bindex); ++ if (h_file) ++ continue; ++ ++ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; /* close all? */ ++ au_set_h_fptr(file, bindex, h_file); ++ } ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ err = 0; ++ ++out: ++ return err; ++} ++ ++static int do_open_dir(struct file *file, int flags, struct file *h_file) ++{ ++ int err; ++ aufs_bindex_t bindex, btail; ++ struct dentry *dentry, *h_dentry; ++ ++ FiMustWriteLock(file); ++ AuDebugOn(h_file); ++ ++ err = 0; ++ dentry = file->f_path.dentry; ++ file->f_version = dentry->d_inode->i_version; ++ bindex = au_dbstart(dentry); ++ au_set_fbstart(file, bindex); ++ btail = au_dbtaildir(dentry); ++ au_set_fbend_dir(file, btail); ++ for (; !err && bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ ++ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ break; ++ } ++ au_set_h_fptr(file, bindex, h_file); ++ } ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ if (!err) ++ return 0; /* success */ ++ ++ /* close all */ ++ for (bindex = au_fbstart(file); bindex <= btail; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbstart(file, -1); ++ au_set_fbend_dir(file, -1); ++ ++ return err; ++} ++ ++static int aufs_open_dir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_fidir *fidir; ++ ++ err = -ENOMEM; ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ fidir = au_fidir_alloc(sb); ++ if (fidir) { ++ struct au_do_open_args args = { ++ .open = do_open_dir, ++ .fidir = fidir ++ }; ++ err = au_do_open(file, &args); ++ if (unlikely(err)) ++ kfree(fidir); ++ } ++ si_read_unlock(sb); ++ return err; ++} ++ ++static int aufs_release_dir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ struct au_vdir *vdir_cache; ++ struct au_finfo *finfo; ++ struct au_fidir *fidir; ++ aufs_bindex_t bindex, bend; ++ ++ finfo = au_fi(file); ++ fidir = finfo->fi_hdir; ++ if (fidir) { ++ au_sphl_del(&finfo->fi_hlist, ++ &au_sbi(file->f_path.dentry->d_sb)->si_files); ++ vdir_cache = fidir->fd_vdir_cache; /* lock-free */ ++ if (vdir_cache) ++ au_vdir_free(vdir_cache); ++ ++ bindex = finfo->fi_btop; ++ if (bindex >= 0) { ++ /* ++ * calls fput() instead of filp_close(), ++ * since no dnotify or lock for the lower file. ++ */ ++ bend = fidir->fd_bbot; ++ for (; bindex <= bend; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ } ++ kfree(fidir); ++ finfo->fi_hdir = NULL; ++ } ++ au_finfo_fin(file); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_flush_dir(struct file *file, fl_owner_t id) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct file *h_file; ++ ++ err = 0; ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ h_file = au_hf_dir(file, bindex); ++ if (h_file) ++ err = vfsub_flush(h_file, id); ++ } ++ return err; ++} ++ ++static int aufs_flush_dir(struct file *file, fl_owner_t id) ++{ ++ return au_do_flush(file, id, au_do_flush_dir); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { ++ struct path h_path; ++ ++ if (au_test_ro(sb, bindex, inode)) ++ continue; ++ h_path.dentry = au_h_dptr(dentry, bindex); ++ if (!h_path.dentry) ++ continue; ++ ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_fsync(NULL, &h_path, datasync); ++ } ++ ++ return err; ++} ++ ++static int au_do_fsync_dir(struct file *file, int datasync) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct file *h_file; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = file->f_path.dentry->d_sb; ++ inode = file_inode(file); ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ h_file = au_hf_dir(file, bindex); ++ if (!h_file || au_test_ro(sb, bindex, inode)) ++ continue; ++ ++ err = vfsub_fsync(h_file, &h_file->f_path, datasync); ++ } ++ ++out: ++ return err; ++} ++ ++/* ++ * @file may be NULL ++ */ ++static int aufs_fsync_dir(struct file *file, loff_t start, loff_t end, ++ int datasync) ++{ ++ int err; ++ struct dentry *dentry; ++ struct super_block *sb; ++ struct mutex *mtx; ++ ++ err = 0; ++ dentry = file->f_path.dentry; ++ mtx = &dentry->d_inode->i_mutex; ++ mutex_lock(mtx); ++ sb = dentry->d_sb; ++ si_noflush_read_lock(sb); ++ if (file) ++ err = au_do_fsync_dir(file, datasync); ++ else { ++ di_write_lock_child(dentry); ++ err = au_do_fsync_dir_no_file(dentry, datasync); ++ } ++ au_cpup_attr_timesizes(dentry->d_inode); ++ di_write_unlock(dentry); ++ if (file) ++ fi_write_unlock(file); ++ ++ si_read_unlock(sb); ++ mutex_unlock(mtx); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_iterate(struct file *file, struct dir_context *ctx) ++{ ++ int err; ++ struct dentry *dentry; ++ struct inode *inode, *h_inode; ++ struct super_block *sb; ++ ++ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); ++ ++ dentry = file->f_path.dentry; ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ err = au_alive_dir(dentry); ++ if (!err) ++ err = au_vdir_init(file); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ if (!au_test_nfsd()) { ++ err = au_vdir_fill_de(file, ctx); ++ fsstack_copy_attr_atime(inode, h_inode); ++ } else { ++ /* ++ * nfsd filldir may call lookup_one_len(), vfs_getattr(), ++ * encode_fh() and others. ++ */ ++ atomic_inc(&h_inode->i_count); ++ di_read_unlock(dentry, AuLock_IR); ++ si_read_unlock(sb); ++ err = au_vdir_fill_de(file, ctx); ++ fsstack_copy_attr_atime(inode, h_inode); ++ fi_write_unlock(file); ++ iput(h_inode); ++ ++ AuTraceErr(err); ++ return err; ++ } ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AuTestEmpty_WHONLY 1 ++#define AuTestEmpty_CALLED (1 << 1) ++#define AuTestEmpty_SHWH (1 << 2) ++#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) ++#define au_fset_testempty(flags, name) \ ++ do { (flags) |= AuTestEmpty_##name; } while (0) ++#define au_fclr_testempty(flags, name) \ ++ do { (flags) &= ~AuTestEmpty_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_SHWH ++#undef AuTestEmpty_SHWH ++#define AuTestEmpty_SHWH 0 ++#endif ++ ++struct test_empty_arg { ++ struct dir_context ctx; ++ struct au_nhash *whlist; ++ unsigned int flags; ++ int err; ++ aufs_bindex_t bindex; ++}; ++ ++static int test_empty_cb(struct dir_context *ctx, const char *__name, ++ int namelen, loff_t offset __maybe_unused, u64 ino, ++ unsigned int d_type) ++{ ++ struct test_empty_arg *arg = container_of(ctx, struct test_empty_arg, ++ ctx); ++ char *name = (void *)__name; ++ ++ arg->err = 0; ++ au_fset_testempty(arg->flags, CALLED); ++ /* smp_mb(); */ ++ if (name[0] == '.' ++ && (namelen == 1 || (name[1] == '.' && namelen == 2))) ++ goto out; /* success */ ++ ++ if (namelen <= AUFS_WH_PFX_LEN ++ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ if (au_ftest_testempty(arg->flags, WHONLY) ++ && !au_nhash_test_known_wh(arg->whlist, name, namelen)) ++ arg->err = -ENOTEMPTY; ++ goto out; ++ } ++ ++ name += AUFS_WH_PFX_LEN; ++ namelen -= AUFS_WH_PFX_LEN; ++ if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) ++ arg->err = au_nhash_append_wh ++ (arg->whlist, name, namelen, ino, d_type, arg->bindex, ++ au_ftest_testempty(arg->flags, SHWH)); ++ ++out: ++ /* smp_mb(); */ ++ AuTraceErr(arg->err); ++ return arg->err; ++} ++ ++static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) ++{ ++ int err; ++ struct file *h_file; ++ ++ h_file = au_h_open(dentry, arg->bindex, ++ O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, ++ /*file*/NULL, /*force_wr*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = 0; ++ if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) ++ && !file_inode(h_file)->i_nlink) ++ goto out_put; ++ ++ do { ++ arg->err = 0; ++ au_fclr_testempty(arg->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_iterate_dir(h_file, &arg->ctx); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err && au_ftest_testempty(arg->flags, CALLED)); ++ ++out_put: ++ fput(h_file); ++ au_sbr_put(dentry->d_sb, arg->bindex); ++out: ++ return err; ++} ++ ++struct do_test_empty_args { ++ int *errp; ++ struct dentry *dentry; ++ struct test_empty_arg *arg; ++}; ++ ++static void call_do_test_empty(void *args) ++{ ++ struct do_test_empty_args *a = args; ++ *a->errp = do_test_empty(a->dentry, a->arg); ++} ++ ++static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) ++{ ++ int err, wkq_err; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ ++ h_dentry = au_h_dptr(dentry, arg->bindex); ++ h_inode = h_dentry->d_inode; ++ /* todo: i_mode changes anytime? */ ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); ++ mutex_unlock(&h_inode->i_mutex); ++ if (!err) ++ err = do_test_empty(dentry, arg); ++ else { ++ struct do_test_empty_args args = { ++ .errp = &err, ++ .dentry = dentry, ++ .arg = arg ++ }; ++ unsigned int flags = arg->flags; ++ ++ wkq_err = au_wkq_wait(call_do_test_empty, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ arg->flags = flags; ++ } ++ ++ return err; ++} ++ ++int au_test_empty_lower(struct dentry *dentry) ++{ ++ int err; ++ unsigned int rdhash; ++ aufs_bindex_t bindex, bstart, btail; ++ struct au_nhash whlist; ++ struct test_empty_arg arg = { ++ .ctx = { ++ .actor = test_empty_cb ++ } ++ }; ++ int (*test_empty)(struct dentry *dentry, struct test_empty_arg *arg); ++ ++ SiMustAnyLock(dentry->d_sb); ++ ++ rdhash = au_sbi(dentry->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); ++ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ ++ arg.flags = 0; ++ arg.whlist = &whlist; ++ bstart = au_dbstart(dentry); ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) ++ au_fset_testempty(arg.flags, SHWH); ++ test_empty = do_test_empty; ++ if (au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)) ++ test_empty = sio_test_empty; ++ arg.bindex = bstart; ++ err = test_empty(dentry, &arg); ++ if (unlikely(err)) ++ goto out_whlist; ++ ++ au_fset_testempty(arg.flags, WHONLY); ++ btail = au_dbtaildir(dentry); ++ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && h_dentry->d_inode) { ++ arg.bindex = bindex; ++ err = test_empty(dentry, &arg); ++ } ++ } ++ ++out_whlist: ++ au_nhash_wh_free(&whlist); ++out: ++ return err; ++} ++ ++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) ++{ ++ int err; ++ struct test_empty_arg arg = { ++ .ctx = { ++ .actor = test_empty_cb ++ } ++ }; ++ aufs_bindex_t bindex, btail; ++ ++ err = 0; ++ arg.whlist = whlist; ++ arg.flags = AuTestEmpty_WHONLY; ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) ++ au_fset_testempty(arg.flags, SHWH); ++ btail = au_dbtaildir(dentry); ++ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && h_dentry->d_inode) { ++ arg.bindex = bindex; ++ err = sio_test_empty(dentry, &arg); ++ } ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++const struct file_operations aufs_dir_fop = { ++ .owner = THIS_MODULE, ++ .llseek = default_llseek, ++ .read = generic_read_dir, ++ .iterate = aufs_iterate, ++ .unlocked_ioctl = aufs_ioctl_dir, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = aufs_compat_ioctl_dir, ++#endif ++ .open = aufs_open_dir, ++ .release = aufs_release_dir, ++ .flush = aufs_flush_dir, ++ .fsync = aufs_fsync_dir ++}; +diff -Nur linux-4.0.4.orig/fs/aufs/dir.h linux-4.0.4/fs/aufs/dir.h +--- linux-4.0.4.orig/fs/aufs/dir.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dir.h 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,118 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * directory operations ++ */ ++ ++#ifndef __AUFS_DIR_H__ ++#define __AUFS_DIR_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* need to be faster and smaller */ ++ ++struct au_nhash { ++ unsigned int nh_num; ++ struct hlist_head *nh_head; ++}; ++ ++struct au_vdir_destr { ++ unsigned char len; ++ unsigned char name[0]; ++} __packed; ++ ++struct au_vdir_dehstr { ++ struct hlist_node hash; ++ struct au_vdir_destr *str; ++} ____cacheline_aligned_in_smp; ++ ++struct au_vdir_de { ++ ino_t de_ino; ++ unsigned char de_type; ++ /* caution: packed */ ++ struct au_vdir_destr de_str; ++} __packed; ++ ++struct au_vdir_wh { ++ struct hlist_node wh_hash; ++#ifdef CONFIG_AUFS_SHWH ++ ino_t wh_ino; ++ aufs_bindex_t wh_bindex; ++ unsigned char wh_type; ++#else ++ aufs_bindex_t wh_bindex; ++#endif ++ /* caution: packed */ ++ struct au_vdir_destr wh_str; ++} __packed; ++ ++union au_vdir_deblk_p { ++ unsigned char *deblk; ++ struct au_vdir_de *de; ++}; ++ ++struct au_vdir { ++ unsigned char **vd_deblk; ++ unsigned long vd_nblk; ++ struct { ++ unsigned long ul; ++ union au_vdir_deblk_p p; ++ } vd_last; ++ ++ unsigned long vd_version; ++ unsigned int vd_deblk_sz; ++ unsigned long vd_jiffy; ++} ____cacheline_aligned_in_smp; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dir.c */ ++extern const struct file_operations aufs_dir_fop; ++void au_add_nlink(struct inode *dir, struct inode *h_dir); ++void au_sub_nlink(struct inode *dir, struct inode *h_dir); ++loff_t au_dir_size(struct file *file, struct dentry *dentry); ++void au_dir_ts(struct inode *dir, aufs_bindex_t bsrc); ++int au_test_empty_lower(struct dentry *dentry); ++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); ++ ++/* vdir.c */ ++unsigned int au_rdhash_est(loff_t sz); ++int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp); ++void au_nhash_wh_free(struct au_nhash *whlist); ++int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, ++ int limit); ++int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen); ++int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, ++ unsigned int d_type, aufs_bindex_t bindex, ++ unsigned char shwh); ++void au_vdir_free(struct au_vdir *vdir); ++int au_vdir_init(struct file *file); ++int au_vdir_fill_de(struct file *file, struct dir_context *ctx); ++ ++/* ioctl.c */ ++long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg); ++ ++#ifdef CONFIG_AUFS_RDU ++/* rdu.c */ ++long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); ++#ifdef CONFIG_COMPAT ++long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg); ++#endif ++#else ++AuStub(long, au_rdu_ioctl, return -EINVAL, struct file *file, ++ unsigned int cmd, unsigned long arg) ++#ifdef CONFIG_COMPAT ++AuStub(long, au_rdu_compat_ioctl, return -EINVAL, struct file *file, ++ unsigned int cmd, unsigned long arg) ++#endif ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DIR_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/dynop.c linux-4.0.4/fs/aufs/dynop.c +--- linux-4.0.4.orig/fs/aufs/dynop.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dynop.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,356 @@ ++/* ++ * Copyright (C) 2010-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * dynamically customizable operations for regular files ++ */ ++ ++#include "aufs.h" ++ ++#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) ++ ++/* ++ * How large will these lists be? ++ * Usually just a few elements, 20-30 at most for each, I guess. ++ */ ++static struct au_splhead dynop[AuDyLast]; ++ ++static struct au_dykey *dy_gfind_get(struct au_splhead *spl, const void *h_op) ++{ ++ struct au_dykey *key, *tmp; ++ struct list_head *head; ++ ++ key = NULL; ++ head = &spl->head; ++ rcu_read_lock(); ++ list_for_each_entry_rcu(tmp, head, dk_list) ++ if (tmp->dk_op.dy_hop == h_op) { ++ key = tmp; ++ kref_get(&key->dk_kref); ++ break; ++ } ++ rcu_read_unlock(); ++ ++ return key; ++} ++ ++static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) ++{ ++ struct au_dykey **k, *found; ++ const void *h_op = key->dk_op.dy_hop; ++ int i; ++ ++ found = NULL; ++ k = br->br_dykey; ++ for (i = 0; i < AuBrDynOp; i++) ++ if (k[i]) { ++ if (k[i]->dk_op.dy_hop == h_op) { ++ found = k[i]; ++ break; ++ } ++ } else ++ break; ++ if (!found) { ++ spin_lock(&br->br_dykey_lock); ++ for (; i < AuBrDynOp; i++) ++ if (k[i]) { ++ if (k[i]->dk_op.dy_hop == h_op) { ++ found = k[i]; ++ break; ++ } ++ } else { ++ k[i] = key; ++ break; ++ } ++ spin_unlock(&br->br_dykey_lock); ++ BUG_ON(i == AuBrDynOp); /* expand the array */ ++ } ++ ++ return found; ++} ++ ++/* kref_get() if @key is already added */ ++static struct au_dykey *dy_gadd(struct au_splhead *spl, struct au_dykey *key) ++{ ++ struct au_dykey *tmp, *found; ++ struct list_head *head; ++ const void *h_op = key->dk_op.dy_hop; ++ ++ found = NULL; ++ head = &spl->head; ++ spin_lock(&spl->spin); ++ list_for_each_entry(tmp, head, dk_list) ++ if (tmp->dk_op.dy_hop == h_op) { ++ kref_get(&tmp->dk_kref); ++ found = tmp; ++ break; ++ } ++ if (!found) ++ list_add_rcu(&key->dk_list, head); ++ spin_unlock(&spl->spin); ++ ++ if (!found) ++ DyPrSym(key); ++ return found; ++} ++ ++static void dy_free_rcu(struct rcu_head *rcu) ++{ ++ struct au_dykey *key; ++ ++ key = container_of(rcu, struct au_dykey, dk_rcu); ++ DyPrSym(key); ++ kfree(key); ++} ++ ++static void dy_free(struct kref *kref) ++{ ++ struct au_dykey *key; ++ struct au_splhead *spl; ++ ++ key = container_of(kref, struct au_dykey, dk_kref); ++ spl = dynop + key->dk_op.dy_type; ++ au_spl_del_rcu(&key->dk_list, spl); ++ call_rcu(&key->dk_rcu, dy_free_rcu); ++} ++ ++void au_dy_put(struct au_dykey *key) ++{ ++ kref_put(&key->dk_kref, dy_free); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define DyDbgDeclare(cnt) unsigned int cnt = 0 ++#define DyDbgInc(cnt) do { cnt++; } while (0) ++#else ++#define DyDbgDeclare(cnt) do {} while (0) ++#define DyDbgInc(cnt) do {} while (0) ++#endif ++ ++#define DySet(func, dst, src, h_op, h_sb) do { \ ++ DyDbgInc(cnt); \ ++ if (h_op->func) { \ ++ if (src.func) \ ++ dst.func = src.func; \ ++ else \ ++ AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ ++ } \ ++} while (0) ++ ++#define DySetForce(func, dst, src) do { \ ++ AuDebugOn(!src.func); \ ++ DyDbgInc(cnt); \ ++ dst.func = src.func; \ ++} while (0) ++ ++#define DySetAop(func) \ ++ DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) ++#define DySetAopForce(func) \ ++ DySetForce(func, dyaop->da_op, aufs_aop) ++ ++static void dy_aop(struct au_dykey *key, const void *h_op, ++ struct super_block *h_sb __maybe_unused) ++{ ++ struct au_dyaop *dyaop = (void *)key; ++ const struct address_space_operations *h_aop = h_op; ++ DyDbgDeclare(cnt); ++ ++ AuDbg("%s\n", au_sbtype(h_sb)); ++ ++ DySetAop(writepage); ++ DySetAopForce(readpage); /* force */ ++ DySetAop(writepages); ++ DySetAop(set_page_dirty); ++ DySetAop(readpages); ++ DySetAop(write_begin); ++ DySetAop(write_end); ++ DySetAop(bmap); ++ DySetAop(invalidatepage); ++ DySetAop(releasepage); ++ DySetAop(freepage); ++ /* this one will be changed according to an aufs mount option */ ++ DySetAop(direct_IO); ++ DySetAop(migratepage); ++ DySetAop(launder_page); ++ DySetAop(is_partially_uptodate); ++ DySetAop(is_dirty_writeback); ++ DySetAop(error_remove_page); ++ DySetAop(swap_activate); ++ DySetAop(swap_deactivate); ++ ++ DyDbgSize(cnt, *h_aop); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void dy_bug(struct kref *kref) ++{ ++ BUG(); ++} ++ ++static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) ++{ ++ struct au_dykey *key, *old; ++ struct au_splhead *spl; ++ struct op { ++ unsigned int sz; ++ void (*set)(struct au_dykey *key, const void *h_op, ++ struct super_block *h_sb __maybe_unused); ++ }; ++ static const struct op a[] = { ++ [AuDy_AOP] = { ++ .sz = sizeof(struct au_dyaop), ++ .set = dy_aop ++ } ++ }; ++ const struct op *p; ++ ++ spl = dynop + op->dy_type; ++ key = dy_gfind_get(spl, op->dy_hop); ++ if (key) ++ goto out_add; /* success */ ++ ++ p = a + op->dy_type; ++ key = kzalloc(p->sz, GFP_NOFS); ++ if (unlikely(!key)) { ++ key = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ key->dk_op.dy_hop = op->dy_hop; ++ kref_init(&key->dk_kref); ++ p->set(key, op->dy_hop, au_br_sb(br)); ++ old = dy_gadd(spl, key); ++ if (old) { ++ kfree(key); ++ key = old; ++ } ++ ++out_add: ++ old = dy_bradd(br, key); ++ if (old) ++ /* its ref-count should never be zero here */ ++ kref_put(&key->dk_kref, dy_bug); ++out: ++ return key; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * Aufs prohibits O_DIRECT by defaut even if the branch supports it. ++ * This behaviour is necessary to return an error from open(O_DIRECT) instead ++ * of the succeeding I/O. The dio mount option enables O_DIRECT and makes ++ * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. ++ * See the aufs manual in detail. ++ */ ++static void dy_adx(struct au_dyaop *dyaop, int do_dx) ++{ ++ if (!do_dx) ++ dyaop->da_op.direct_IO = NULL; ++ else ++ dyaop->da_op.direct_IO = aufs_aop.direct_IO; ++} ++ ++static struct au_dyaop *dy_aget(struct au_branch *br, ++ const struct address_space_operations *h_aop, ++ int do_dx) ++{ ++ struct au_dyaop *dyaop; ++ struct au_dynop op; ++ ++ op.dy_type = AuDy_AOP; ++ op.dy_haop = h_aop; ++ dyaop = (void *)dy_get(&op, br); ++ if (IS_ERR(dyaop)) ++ goto out; ++ dy_adx(dyaop, do_dx); ++ ++out: ++ return dyaop; ++} ++ ++int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode) ++{ ++ int err, do_dx; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_dyaop *dyaop; ++ ++ AuDebugOn(!S_ISREG(h_inode->i_mode)); ++ IiMustWriteLock(inode); ++ ++ sb = inode->i_sb; ++ br = au_sbr(sb, bindex); ++ do_dx = !!au_opt_test(au_mntflags(sb), DIO); ++ dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx); ++ err = PTR_ERR(dyaop); ++ if (IS_ERR(dyaop)) ++ /* unnecessary to call dy_fput() */ ++ goto out; ++ ++ err = 0; ++ inode->i_mapping->a_ops = &dyaop->da_op; ++ ++out: ++ return err; ++} ++ ++/* ++ * Is it safe to replace a_ops during the inode/file is in operation? ++ * Yes, I hope so. ++ */ ++int au_dy_irefresh(struct inode *inode) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct inode *h_inode; ++ ++ err = 0; ++ if (S_ISREG(inode->i_mode)) { ++ bstart = au_ibstart(inode); ++ h_inode = au_h_iptr(inode, bstart); ++ err = au_dy_iaop(inode, bstart, h_inode); ++ } ++ return err; ++} ++ ++void au_dy_arefresh(int do_dx) ++{ ++ struct au_splhead *spl; ++ struct list_head *head; ++ struct au_dykey *key; ++ ++ spl = dynop + AuDy_AOP; ++ head = &spl->head; ++ spin_lock(&spl->spin); ++ list_for_each_entry(key, head, dk_list) ++ dy_adx((void *)key, do_dx); ++ spin_unlock(&spl->spin); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void __init au_dy_init(void) ++{ ++ int i; ++ ++ /* make sure that 'struct au_dykey *' can be any type */ ++ BUILD_BUG_ON(offsetof(struct au_dyaop, da_key)); ++ ++ for (i = 0; i < AuDyLast; i++) ++ au_spl_init(dynop + i); ++} ++ ++void au_dy_fin(void) ++{ ++ int i; ++ ++ for (i = 0; i < AuDyLast; i++) ++ WARN_ON(!list_empty(&dynop[i].head)); ++} +diff -Nur linux-4.0.4.orig/fs/aufs/dynop.h linux-4.0.4/fs/aufs/dynop.h +--- linux-4.0.4.orig/fs/aufs/dynop.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/dynop.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (C) 2010-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * dynamically customizable operations (for regular files only) ++ */ ++ ++#ifndef __AUFS_DYNOP_H__ ++#define __AUFS_DYNOP_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++enum {AuDy_AOP, AuDyLast}; ++ ++struct au_dynop { ++ int dy_type; ++ union { ++ const void *dy_hop; ++ const struct address_space_operations *dy_haop; ++ }; ++}; ++ ++struct au_dykey { ++ union { ++ struct list_head dk_list; ++ struct rcu_head dk_rcu; ++ }; ++ struct au_dynop dk_op; ++ ++ /* ++ * during I am in the branch local array, kref is gotten. when the ++ * branch is removed, kref is put. ++ */ ++ struct kref dk_kref; ++}; ++ ++/* stop unioning since their sizes are very different from each other */ ++struct au_dyaop { ++ struct au_dykey da_key; ++ struct address_space_operations da_op; /* not const */ ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dynop.c */ ++struct au_branch; ++void au_dy_put(struct au_dykey *key); ++int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode); ++int au_dy_irefresh(struct inode *inode); ++void au_dy_arefresh(int do_dio); ++ ++void __init au_dy_init(void); ++void au_dy_fin(void); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DYNOP_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/export.c linux-4.0.4/fs/aufs/export.c +--- linux-4.0.4.orig/fs/aufs/export.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/export.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,818 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * export via nfs ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "../fs/mount.h" ++#include "aufs.h" ++ ++union conv { ++#ifdef CONFIG_AUFS_INO_T_64 ++ __u32 a[2]; ++#else ++ __u32 a[1]; ++#endif ++ ino_t ino; ++}; ++ ++static ino_t decode_ino(__u32 *a) ++{ ++ union conv u; ++ ++ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); ++ u.a[0] = a[0]; ++#ifdef CONFIG_AUFS_INO_T_64 ++ u.a[1] = a[1]; ++#endif ++ return u.ino; ++} ++ ++static void encode_ino(__u32 *a, ino_t ino) ++{ ++ union conv u; ++ ++ u.ino = ino; ++ a[0] = u.a[0]; ++#ifdef CONFIG_AUFS_INO_T_64 ++ a[1] = u.a[1]; ++#endif ++} ++ ++/* NFS file handle */ ++enum { ++ Fh_br_id, ++ Fh_sigen, ++#ifdef CONFIG_AUFS_INO_T_64 ++ /* support 64bit inode number */ ++ Fh_ino1, ++ Fh_ino2, ++ Fh_dir_ino1, ++ Fh_dir_ino2, ++#else ++ Fh_ino1, ++ Fh_dir_ino1, ++#endif ++ Fh_igen, ++ Fh_h_type, ++ Fh_tail, ++ ++ Fh_ino = Fh_ino1, ++ Fh_dir_ino = Fh_dir_ino1 ++}; ++ ++static int au_test_anon(struct dentry *dentry) ++{ ++ /* note: read d_flags without d_lock */ ++ return !!(dentry->d_flags & DCACHE_DISCONNECTED); ++} ++ ++int au_test_nfsd(void) ++{ ++ int ret; ++ struct task_struct *tsk = current; ++ char comm[sizeof(tsk->comm)]; ++ ++ ret = 0; ++ if (tsk->flags & PF_KTHREAD) { ++ get_task_comm(comm, tsk); ++ ret = !strcmp(comm, "nfsd"); ++ } ++ ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* inode generation external table */ ++ ++void au_xigen_inc(struct inode *inode) ++{ ++ loff_t pos; ++ ssize_t sz; ++ __u32 igen; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ sb = inode->i_sb; ++ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); ++ ++ sbinfo = au_sbi(sb); ++ pos = inode->i_ino; ++ pos *= sizeof(igen); ++ igen = inode->i_generation + 1; ++ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, ++ sizeof(igen), &pos); ++ if (sz == sizeof(igen)) ++ return; /* success */ ++ ++ if (unlikely(sz >= 0)) ++ AuIOErr("xigen error (%zd)\n", sz); ++} ++ ++int au_xigen_new(struct inode *inode) ++{ ++ int err; ++ loff_t pos; ++ ssize_t sz; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ err = 0; ++ /* todo: dirty, at mount time */ ++ if (inode->i_ino == AUFS_ROOT_INO) ++ goto out; ++ sb = inode->i_sb; ++ SiMustAnyLock(sb); ++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) ++ goto out; ++ ++ err = -EFBIG; ++ pos = inode->i_ino; ++ if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { ++ AuIOErr1("too large i%lld\n", pos); ++ goto out; ++ } ++ pos *= sizeof(inode->i_generation); ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ file = sbinfo->si_xigen; ++ BUG_ON(!file); ++ ++ if (vfsub_f_size_read(file) ++ < pos + sizeof(inode->i_generation)) { ++ inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); ++ sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, ++ sizeof(inode->i_generation), &pos); ++ } else ++ sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, ++ sizeof(inode->i_generation), &pos); ++ if (sz == sizeof(inode->i_generation)) ++ goto out; /* success */ ++ ++ err = sz; ++ if (unlikely(sz >= 0)) { ++ err = -EIO; ++ AuIOErr("xigen error (%zd)\n", sz); ++ } ++ ++out: ++ return err; ++} ++ ++int au_xigen_set(struct super_block *sb, struct file *base) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ file = au_xino_create2(base, sbinfo->si_xigen); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ err = 0; ++ if (sbinfo->si_xigen) ++ fput(sbinfo->si_xigen); ++ sbinfo->si_xigen = file; ++ ++out: ++ return err; ++} ++ ++void au_xigen_clr(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ if (sbinfo->si_xigen) { ++ fput(sbinfo->si_xigen); ++ sbinfo->si_xigen = NULL; ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, ++ ino_t dir_ino) ++{ ++ struct dentry *dentry, *d; ++ struct inode *inode; ++ unsigned int sigen; ++ ++ dentry = NULL; ++ inode = ilookup(sb, ino); ++ if (!inode) ++ goto out; ++ ++ dentry = ERR_PTR(-ESTALE); ++ sigen = au_sigen(sb); ++ if (unlikely(is_bad_inode(inode) ++ || IS_DEADDIR(inode) ++ || sigen != au_iigen(inode, NULL))) ++ goto out_iput; ++ ++ dentry = NULL; ++ if (!dir_ino || S_ISDIR(inode->i_mode)) ++ dentry = d_find_alias(inode); ++ else { ++ spin_lock(&inode->i_lock); ++ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { ++ spin_lock(&d->d_lock); ++ if (!au_test_anon(d) ++ && d->d_parent->d_inode->i_ino == dir_ino) { ++ dentry = dget_dlock(d); ++ spin_unlock(&d->d_lock); ++ break; ++ } ++ spin_unlock(&d->d_lock); ++ } ++ spin_unlock(&inode->i_lock); ++ } ++ if (unlikely(dentry && au_digen_test(dentry, sigen))) { ++ /* need to refresh */ ++ dput(dentry); ++ dentry = NULL; ++ } ++ ++out_iput: ++ iput(inode); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: dirty? */ ++/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ ++ ++struct au_compare_mnt_args { ++ /* input */ ++ struct super_block *sb; ++ ++ /* output */ ++ struct vfsmount *mnt; ++}; ++ ++static int au_compare_mnt(struct vfsmount *mnt, void *arg) ++{ ++ struct au_compare_mnt_args *a = arg; ++ ++ if (mnt->mnt_sb != a->sb) ++ return 0; ++ a->mnt = mntget(mnt); ++ return 1; ++} ++ ++static struct vfsmount *au_mnt_get(struct super_block *sb) ++{ ++ int err; ++ struct path root; ++ struct au_compare_mnt_args args = { ++ .sb = sb ++ }; ++ ++ get_fs_root(current->fs, &root); ++ rcu_read_lock(); ++ err = iterate_mounts(au_compare_mnt, &args, root.mnt); ++ rcu_read_unlock(); ++ path_put(&root); ++ AuDebugOn(!err); ++ AuDebugOn(!args.mnt); ++ return args.mnt; ++} ++ ++struct au_nfsd_si_lock { ++ unsigned int sigen; ++ aufs_bindex_t bindex, br_id; ++ unsigned char force_lock; ++}; ++ ++static int si_nfsd_read_lock(struct super_block *sb, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ ++ si_read_lock(sb, AuLock_FLUSH); ++ ++ /* branch id may be wrapped around */ ++ err = 0; ++ bindex = au_br_index(sb, nsi_lock->br_id); ++ if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) ++ goto out; /* success */ ++ ++ err = -ESTALE; ++ bindex = -1; ++ if (!nsi_lock->force_lock) ++ si_read_unlock(sb); ++ ++out: ++ nsi_lock->bindex = bindex; ++ return err; ++} ++ ++struct find_name_by_ino { ++ struct dir_context ctx; ++ int called, found; ++ ino_t ino; ++ char *name; ++ int namelen; ++}; ++ ++static int ++find_name_by_ino(struct dir_context *ctx, const char *name, int namelen, ++ loff_t offset, u64 ino, unsigned int d_type) ++{ ++ struct find_name_by_ino *a = container_of(ctx, struct find_name_by_ino, ++ ctx); ++ ++ a->called++; ++ if (a->ino != ino) ++ return 0; ++ ++ memcpy(a->name, name, namelen); ++ a->namelen = namelen; ++ a->found = 1; ++ return 1; ++} ++ ++static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry, *parent; ++ struct file *file; ++ struct inode *dir; ++ struct find_name_by_ino arg = { ++ .ctx = { ++ .actor = find_name_by_ino ++ } ++ }; ++ int err; ++ ++ parent = path->dentry; ++ if (nsi_lock) ++ si_read_unlock(parent->d_sb); ++ file = vfsub_dentry_open(path, au_dir_roflags); ++ dentry = (void *)file; ++ if (IS_ERR(file)) ++ goto out; ++ ++ dentry = ERR_PTR(-ENOMEM); ++ arg.name = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!arg.name)) ++ goto out_file; ++ arg.ino = ino; ++ arg.found = 0; ++ do { ++ arg.called = 0; ++ /* smp_mb(); */ ++ err = vfsub_iterate_dir(file, &arg.ctx); ++ } while (!err && !arg.found && arg.called); ++ dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_name; ++ /* instead of ENOENT */ ++ dentry = ERR_PTR(-ESTALE); ++ if (!arg.found) ++ goto out_name; ++ ++ /* do not call vfsub_lkup_one() */ ++ dir = parent->d_inode; ++ mutex_lock(&dir->i_mutex); ++ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); ++ mutex_unlock(&dir->i_mutex); ++ AuTraceErrPtr(dentry); ++ if (IS_ERR(dentry)) ++ goto out_name; ++ AuDebugOn(au_test_anon(dentry)); ++ if (unlikely(!dentry->d_inode)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ENOENT); ++ } ++ ++out_name: ++ free_page((unsigned long)arg.name); ++out_file: ++ fput(file); ++out: ++ if (unlikely(nsi_lock ++ && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) ++ if (!IS_ERR(dentry)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ } ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, ++ ino_t dir_ino, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry; ++ struct path path; ++ ++ if (dir_ino != AUFS_ROOT_INO) { ++ path.dentry = decode_by_ino(sb, dir_ino, 0); ++ dentry = path.dentry; ++ if (!path.dentry || IS_ERR(path.dentry)) ++ goto out; ++ AuDebugOn(au_test_anon(path.dentry)); ++ } else ++ path.dentry = dget(sb->s_root); ++ ++ path.mnt = au_mnt_get(sb); ++ dentry = au_lkup_by_ino(&path, ino, nsi_lock); ++ path_put(&path); ++ ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int h_acceptable(void *expv, struct dentry *dentry) ++{ ++ return 1; ++} ++ ++static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, ++ char *buf, int len, struct super_block *sb) ++{ ++ char *p; ++ int n; ++ struct path path; ++ ++ p = d_path(h_rootpath, buf, len); ++ if (IS_ERR(p)) ++ goto out; ++ n = strlen(p); ++ ++ path.mnt = h_rootpath->mnt; ++ path.dentry = h_parent; ++ p = d_path(&path, buf, len); ++ if (IS_ERR(p)) ++ goto out; ++ if (n != 1) ++ p += n; ++ ++ path.mnt = au_mnt_get(sb); ++ path.dentry = sb->s_root; ++ p = d_path(&path, buf, len - strlen(p)); ++ mntput(path.mnt); ++ if (IS_ERR(p)) ++ goto out; ++ if (n != 1) ++ p[strlen(p)] = '/'; ++ ++out: ++ AuTraceErrPtr(p); ++ return p; ++} ++ ++static ++struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, ++ int fh_len, struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry, *h_parent, *root; ++ struct super_block *h_sb; ++ char *pathname, *p; ++ struct vfsmount *h_mnt; ++ struct au_branch *br; ++ int err; ++ struct path path; ++ ++ br = au_sbr(sb, nsi_lock->bindex); ++ h_mnt = au_br_mnt(br); ++ h_sb = h_mnt->mnt_sb; ++ /* todo: call lower fh_to_dentry()? fh_to_parent()? */ ++ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), ++ fh_len - Fh_tail, fh[Fh_h_type], ++ h_acceptable, /*context*/NULL); ++ dentry = h_parent; ++ if (unlikely(!h_parent || IS_ERR(h_parent))) { ++ AuWarn1("%s decode_fh failed, %ld\n", ++ au_sbtype(h_sb), PTR_ERR(h_parent)); ++ goto out; ++ } ++ dentry = NULL; ++ if (unlikely(au_test_anon(h_parent))) { ++ AuWarn1("%s decode_fh returned a disconnected dentry\n", ++ au_sbtype(h_sb)); ++ goto out_h_parent; ++ } ++ ++ dentry = ERR_PTR(-ENOMEM); ++ pathname = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!pathname)) ++ goto out_h_parent; ++ ++ root = sb->s_root; ++ path.mnt = h_mnt; ++ di_read_lock_parent(root, !AuLock_IR); ++ path.dentry = au_h_dptr(root, nsi_lock->bindex); ++ di_read_unlock(root, !AuLock_IR); ++ p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); ++ dentry = (void *)p; ++ if (IS_ERR(p)) ++ goto out_pathname; ++ ++ si_read_unlock(sb); ++ err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); ++ dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_relock; ++ ++ dentry = ERR_PTR(-ENOENT); ++ AuDebugOn(au_test_anon(path.dentry)); ++ if (unlikely(!path.dentry->d_inode)) ++ goto out_path; ++ ++ if (ino != path.dentry->d_inode->i_ino) ++ dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); ++ else ++ dentry = dget(path.dentry); ++ ++out_path: ++ path_put(&path); ++out_relock: ++ if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) ++ if (!IS_ERR(dentry)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ } ++out_pathname: ++ free_page((unsigned long)pathname); ++out_h_parent: ++ dput(h_parent); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry * ++aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ struct dentry *dentry; ++ __u32 *fh = fid->raw; ++ struct au_branch *br; ++ ino_t ino, dir_ino; ++ struct au_nfsd_si_lock nsi_lock = { ++ .force_lock = 0 ++ }; ++ ++ dentry = ERR_PTR(-ESTALE); ++ /* it should never happen, but the file handle is unreliable */ ++ if (unlikely(fh_len < Fh_tail)) ++ goto out; ++ nsi_lock.sigen = fh[Fh_sigen]; ++ nsi_lock.br_id = fh[Fh_br_id]; ++ ++ /* branch id may be wrapped around */ ++ br = NULL; ++ if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) ++ goto out; ++ nsi_lock.force_lock = 1; ++ ++ /* is this inode still cached? */ ++ ino = decode_ino(fh + Fh_ino); ++ /* it should never happen */ ++ if (unlikely(ino == AUFS_ROOT_INO)) ++ goto out; ++ ++ dir_ino = decode_ino(fh + Fh_dir_ino); ++ dentry = decode_by_ino(sb, ino, dir_ino); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (dentry) ++ goto accept; ++ ++ /* is the parent dir cached? */ ++ br = au_sbr(sb, nsi_lock.bindex); ++ atomic_inc(&br->br_count); ++ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (dentry) ++ goto accept; ++ ++ /* lookup path */ ++ dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (unlikely(!dentry)) ++ /* todo?: make it ESTALE */ ++ goto out_unlock; ++ ++accept: ++ if (!au_digen_test(dentry, au_sigen(sb)) ++ && dentry->d_inode->i_generation == fh[Fh_igen]) ++ goto out_unlock; /* success */ ++ ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++out_unlock: ++ if (br) ++ atomic_dec(&br->br_count); ++ si_read_unlock(sb); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++#if 0 /* reserved for future use */ ++/* support subtreecheck option */ ++static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, ++ int fh_len, int fh_type) ++{ ++ struct dentry *parent; ++ __u32 *fh = fid->raw; ++ ino_t dir_ino; ++ ++ dir_ino = decode_ino(fh + Fh_dir_ino); ++ parent = decode_by_ino(sb, dir_ino, 0); ++ if (IS_ERR(parent)) ++ goto out; ++ if (!parent) ++ parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), ++ dir_ino, fh, fh_len); ++ ++out: ++ AuTraceErrPtr(parent); ++ return parent; ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, ++ struct inode *dir) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct super_block *sb, *h_sb; ++ struct dentry *dentry, *parent, *h_parent; ++ struct inode *h_dir; ++ struct au_branch *br; ++ ++ err = -ENOSPC; ++ if (unlikely(*max_len <= Fh_tail)) { ++ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); ++ goto out; ++ } ++ ++ err = FILEID_ROOT; ++ if (inode->i_ino == AUFS_ROOT_INO) { ++ AuDebugOn(inode->i_ino != AUFS_ROOT_INO); ++ goto out; ++ } ++ ++ h_parent = NULL; ++ sb = inode->i_sb; ++ err = si_read_lock(sb, AuLock_FLUSH); ++ if (unlikely(err)) ++ goto out; ++ ++#ifdef CONFIG_AUFS_DEBUG ++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) ++ AuWarn1("NFS-exporting requires xino\n"); ++#endif ++ err = -EIO; ++ parent = NULL; ++ ii_read_lock_child(inode); ++ bindex = au_ibstart(inode); ++ if (!dir) { ++ dentry = d_find_any_alias(inode); ++ if (unlikely(!dentry)) ++ goto out_unlock; ++ AuDebugOn(au_test_anon(dentry)); ++ parent = dget_parent(dentry); ++ dput(dentry); ++ if (unlikely(!parent)) ++ goto out_unlock; ++ dir = parent->d_inode; ++ } ++ ++ ii_read_lock_parent(dir); ++ h_dir = au_h_iptr(dir, bindex); ++ ii_read_unlock(dir); ++ if (unlikely(!h_dir)) ++ goto out_parent; ++ h_parent = d_find_any_alias(h_dir); ++ if (unlikely(!h_parent)) ++ goto out_hparent; ++ ++ err = -EPERM; ++ br = au_sbr(sb, bindex); ++ h_sb = au_br_sb(br); ++ if (unlikely(!h_sb->s_export_op)) { ++ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); ++ goto out_hparent; ++ } ++ ++ fh[Fh_br_id] = br->br_id; ++ fh[Fh_sigen] = au_sigen(sb); ++ encode_ino(fh + Fh_ino, inode->i_ino); ++ encode_ino(fh + Fh_dir_ino, dir->i_ino); ++ fh[Fh_igen] = inode->i_generation; ++ ++ *max_len -= Fh_tail; ++ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), ++ max_len, ++ /*connectable or subtreecheck*/0); ++ err = fh[Fh_h_type]; ++ *max_len += Fh_tail; ++ /* todo: macros? */ ++ if (err != FILEID_INVALID) ++ err = 99; ++ else ++ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); ++ ++out_hparent: ++ dput(h_parent); ++out_parent: ++ dput(parent); ++out_unlock: ++ ii_read_unlock(inode); ++ si_read_unlock(sb); ++out: ++ if (unlikely(err < 0)) ++ err = FILEID_INVALID; ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_commit_metadata(struct inode *inode) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct super_block *sb; ++ struct inode *h_inode; ++ int (*f)(struct inode *inode); ++ ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ii_write_lock_child(inode); ++ bindex = au_ibstart(inode); ++ AuDebugOn(bindex < 0); ++ h_inode = au_h_iptr(inode, bindex); ++ ++ f = h_inode->i_sb->s_export_op->commit_metadata; ++ if (f) ++ err = f(h_inode); ++ else { ++ struct writeback_control wbc = { ++ .sync_mode = WB_SYNC_ALL, ++ .nr_to_write = 0 /* metadata only */ ++ }; ++ ++ err = sync_inode(h_inode, &wbc); ++ } ++ ++ au_cpup_attr_timesizes(inode); ++ ii_write_unlock(inode); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct export_operations aufs_export_op = { ++ .fh_to_dentry = aufs_fh_to_dentry, ++ /* .fh_to_parent = aufs_fh_to_parent, */ ++ .encode_fh = aufs_encode_fh, ++ .commit_metadata = aufs_commit_metadata ++}; ++ ++void au_export_init(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ __u32 u; ++ ++ sb->s_export_op = &aufs_export_op; ++ sbinfo = au_sbi(sb); ++ sbinfo->si_xigen = NULL; ++ get_random_bytes(&u, sizeof(u)); ++ BUILD_BUG_ON(sizeof(u) != sizeof(int)); ++ atomic_set(&sbinfo->si_xigen_next, u); ++} +diff -Nur linux-4.0.4.orig/fs/aufs/fhsm.c linux-4.0.4/fs/aufs/fhsm.c +--- linux-4.0.4.orig/fs/aufs/fhsm.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/fhsm.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,412 @@ ++/* ++ * Copyright (C) 2011-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * File-based Hierarchy Storage Management ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++static aufs_bindex_t au_fhsm_bottom(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ AuDebugOn(!fhsm); ++ return fhsm->fhsm_bottom; ++} ++ ++void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ AuDebugOn(!fhsm); ++ fhsm->fhsm_bottom = bindex; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_fhsm_test_jiffy(struct au_sbinfo *sbinfo, struct au_branch *br) ++{ ++ struct au_br_fhsm *bf; ++ ++ bf = br->br_fhsm; ++ MtxMustLock(&bf->bf_lock); ++ ++ return !bf->bf_readable ++ || time_after(jiffies, ++ bf->bf_jiffy + sbinfo->si_fhsm.fhsm_expire); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_fhsm_notify(struct super_block *sb, int val) ++{ ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ if (au_fhsm_pid(fhsm) ++ && atomic_read(&fhsm->fhsm_readable) != -1) { ++ atomic_set(&fhsm->fhsm_readable, val); ++ if (val) ++ wake_up(&fhsm->fhsm_wqh); ++ } ++} ++ ++static int au_fhsm_stfs(struct super_block *sb, aufs_bindex_t bindex, ++ struct aufs_stfs *rstfs, int do_lock, int do_notify) ++{ ++ int err; ++ struct au_branch *br; ++ struct au_br_fhsm *bf; ++ ++ br = au_sbr(sb, bindex); ++ AuDebugOn(au_br_rdonly(br)); ++ bf = br->br_fhsm; ++ AuDebugOn(!bf); ++ ++ if (do_lock) ++ mutex_lock(&bf->bf_lock); ++ else ++ MtxMustLock(&bf->bf_lock); ++ ++ /* sb->s_root for NFS is unreliable */ ++ err = au_br_stfs(br, &bf->bf_stfs); ++ if (unlikely(err)) { ++ AuErr1("FHSM failed (%d), b%d, ignored.\n", bindex, err); ++ goto out; ++ } ++ ++ bf->bf_jiffy = jiffies; ++ bf->bf_readable = 1; ++ if (do_notify) ++ au_fhsm_notify(sb, /*val*/1); ++ if (rstfs) ++ *rstfs = bf->bf_stfs; ++ ++out: ++ if (do_lock) ++ mutex_unlock(&bf->bf_lock); ++ au_fhsm_notify(sb, /*val*/1); ++ ++ return err; ++} ++ ++void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ struct au_branch *br; ++ struct au_br_fhsm *bf; ++ ++ AuDbg("b%d, force %d\n", bindex, force); ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ if (!au_ftest_si(sbinfo, FHSM) ++ || fhsm->fhsm_bottom == bindex) ++ return; ++ ++ br = au_sbr(sb, bindex); ++ bf = br->br_fhsm; ++ AuDebugOn(!bf); ++ mutex_lock(&bf->bf_lock); ++ if (force ++ || au_fhsm_pid(fhsm) ++ || au_fhsm_test_jiffy(sbinfo, br)) ++ err = au_fhsm_stfs(sb, bindex, /*rstfs*/NULL, /*do_lock*/0, ++ /*do_notify*/1); ++ mutex_unlock(&bf->bf_lock); ++} ++ ++void au_fhsm_wrote_all(struct super_block *sb, int force) ++{ ++ aufs_bindex_t bindex, bend; ++ struct au_branch *br; ++ ++ /* exclude the bottom */ ++ bend = au_fhsm_bottom(sb); ++ for (bindex = 0; bindex < bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_fhsm(br->br_perm)) ++ au_fhsm_wrote(sb, bindex, force); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static unsigned int au_fhsm_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ unsigned int mask; ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ mask = 0; ++ sbinfo = file->private_data; ++ fhsm = &sbinfo->si_fhsm; ++ poll_wait(file, &fhsm->fhsm_wqh, wait); ++ if (atomic_read(&fhsm->fhsm_readable)) ++ mask = POLLIN /* | POLLRDNORM */; ++ ++ AuTraceErr((int)mask); ++ return mask; ++} ++ ++static int au_fhsm_do_read_one(struct aufs_stbr __user *stbr, ++ struct aufs_stfs *stfs, __s16 brid) ++{ ++ int err; ++ ++ err = copy_to_user(&stbr->stfs, stfs, sizeof(*stfs)); ++ if (!err) ++ err = __put_user(brid, &stbr->brid); ++ if (unlikely(err)) ++ err = -EFAULT; ++ ++ return err; ++} ++ ++static ssize_t au_fhsm_do_read(struct super_block *sb, ++ struct aufs_stbr __user *stbr, size_t count) ++{ ++ ssize_t err; ++ int nstbr; ++ aufs_bindex_t bindex, bend; ++ struct au_branch *br; ++ struct au_br_fhsm *bf; ++ ++ /* except the bottom branch */ ++ err = 0; ++ nstbr = 0; ++ bend = au_fhsm_bottom(sb); ++ for (bindex = 0; !err && bindex < bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!au_br_fhsm(br->br_perm)) ++ continue; ++ ++ bf = br->br_fhsm; ++ mutex_lock(&bf->bf_lock); ++ if (bf->bf_readable) { ++ err = -EFAULT; ++ if (count >= sizeof(*stbr)) ++ err = au_fhsm_do_read_one(stbr++, &bf->bf_stfs, ++ br->br_id); ++ if (!err) { ++ bf->bf_readable = 0; ++ count -= sizeof(*stbr); ++ nstbr++; ++ } ++ } ++ mutex_unlock(&bf->bf_lock); ++ } ++ if (!err) ++ err = sizeof(*stbr) * nstbr; ++ ++ return err; ++} ++ ++static ssize_t au_fhsm_read(struct file *file, char __user *buf, size_t count, ++ loff_t *pos) ++{ ++ ssize_t err; ++ int readable; ++ aufs_bindex_t nfhsm, bindex, bend; ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ struct au_branch *br; ++ struct super_block *sb; ++ ++ err = 0; ++ sbinfo = file->private_data; ++ fhsm = &sbinfo->si_fhsm; ++need_data: ++ spin_lock_irq(&fhsm->fhsm_wqh.lock); ++ if (!atomic_read(&fhsm->fhsm_readable)) { ++ if (vfsub_file_flags(file) & O_NONBLOCK) ++ err = -EAGAIN; ++ else ++ err = wait_event_interruptible_locked_irq ++ (fhsm->fhsm_wqh, ++ atomic_read(&fhsm->fhsm_readable)); ++ } ++ spin_unlock_irq(&fhsm->fhsm_wqh.lock); ++ if (unlikely(err)) ++ goto out; ++ ++ /* sb may already be dead */ ++ au_rw_read_lock(&sbinfo->si_rwsem); ++ readable = atomic_read(&fhsm->fhsm_readable); ++ if (readable > 0) { ++ sb = sbinfo->si_sb; ++ AuDebugOn(!sb); ++ /* exclude the bottom branch */ ++ nfhsm = 0; ++ bend = au_fhsm_bottom(sb); ++ for (bindex = 0; bindex < bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_fhsm(br->br_perm)) ++ nfhsm++; ++ } ++ err = -EMSGSIZE; ++ if (nfhsm * sizeof(struct aufs_stbr) <= count) { ++ atomic_set(&fhsm->fhsm_readable, 0); ++ err = au_fhsm_do_read(sbinfo->si_sb, (void __user *)buf, ++ count); ++ } ++ } ++ au_rw_read_unlock(&sbinfo->si_rwsem); ++ if (!readable) ++ goto need_data; ++ ++out: ++ return err; ++} ++ ++static int au_fhsm_release(struct inode *inode, struct file *file) ++{ ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ /* sb may already be dead */ ++ sbinfo = file->private_data; ++ fhsm = &sbinfo->si_fhsm; ++ spin_lock(&fhsm->fhsm_spin); ++ fhsm->fhsm_pid = 0; ++ spin_unlock(&fhsm->fhsm_spin); ++ kobject_put(&sbinfo->si_kobj); ++ ++ return 0; ++} ++ ++static const struct file_operations au_fhsm_fops = { ++ .owner = THIS_MODULE, ++ .llseek = noop_llseek, ++ .read = au_fhsm_read, ++ .poll = au_fhsm_poll, ++ .release = au_fhsm_release ++}; ++ ++int au_fhsm_fd(struct super_block *sb, int oflags) ++{ ++ int err, fd; ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ ++ err = -EPERM; ++ if (unlikely(!capable(CAP_SYS_ADMIN))) ++ goto out; ++ ++ err = -EINVAL; ++ if (unlikely(oflags & ~(O_CLOEXEC | O_NONBLOCK))) ++ goto out; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ spin_lock(&fhsm->fhsm_spin); ++ if (!fhsm->fhsm_pid) ++ fhsm->fhsm_pid = current->pid; ++ else ++ err = -EBUSY; ++ spin_unlock(&fhsm->fhsm_spin); ++ if (unlikely(err)) ++ goto out; ++ ++ oflags |= O_RDONLY; ++ /* oflags |= FMODE_NONOTIFY; */ ++ fd = anon_inode_getfd("[aufs_fhsm]", &au_fhsm_fops, sbinfo, oflags); ++ err = fd; ++ if (unlikely(fd < 0)) ++ goto out_pid; ++ ++ /* succeed reglardless 'fhsm' status */ ++ kobject_get(&sbinfo->si_kobj); ++ si_noflush_read_lock(sb); ++ if (au_ftest_si(sbinfo, FHSM)) ++ au_fhsm_wrote_all(sb, /*force*/0); ++ si_read_unlock(sb); ++ goto out; /* success */ ++ ++out_pid: ++ spin_lock(&fhsm->fhsm_spin); ++ fhsm->fhsm_pid = 0; ++ spin_unlock(&fhsm->fhsm_spin); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_fhsm_br_alloc(struct au_branch *br) ++{ ++ int err; ++ ++ err = 0; ++ br->br_fhsm = kmalloc(sizeof(*br->br_fhsm), GFP_NOFS); ++ if (br->br_fhsm) ++ au_br_fhsm_init(br->br_fhsm); ++ else ++ err = -ENOMEM; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_fhsm_fin(struct super_block *sb) ++{ ++ au_fhsm_notify(sb, /*val*/-1); ++} ++ ++void au_fhsm_init(struct au_sbinfo *sbinfo) ++{ ++ struct au_fhsm *fhsm; ++ ++ fhsm = &sbinfo->si_fhsm; ++ spin_lock_init(&fhsm->fhsm_spin); ++ init_waitqueue_head(&fhsm->fhsm_wqh); ++ atomic_set(&fhsm->fhsm_readable, 0); ++ fhsm->fhsm_expire ++ = msecs_to_jiffies(AUFS_FHSM_CACHE_DEF_SEC * MSEC_PER_SEC); ++ fhsm->fhsm_bottom = -1; ++} ++ ++void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec) ++{ ++ sbinfo->si_fhsm.fhsm_expire ++ = msecs_to_jiffies(sec * MSEC_PER_SEC); ++} ++ ++void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo) ++{ ++ unsigned int u; ++ ++ if (!au_ftest_si(sbinfo, FHSM)) ++ return; ++ ++ u = jiffies_to_msecs(sbinfo->si_fhsm.fhsm_expire) / MSEC_PER_SEC; ++ if (u != AUFS_FHSM_CACHE_DEF_SEC) ++ seq_printf(seq, ",fhsm_sec=%u", u); ++} +diff -Nur linux-4.0.4.orig/fs/aufs/file.c linux-4.0.4/fs/aufs/file.c +--- linux-4.0.4.orig/fs/aufs/file.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/file.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,831 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * handling file/dir, and address_space operation ++ */ ++ ++#ifdef CONFIG_AUFS_DEBUG ++#include ++#endif ++#include ++#include "aufs.h" ++ ++/* drop flags for writing */ ++unsigned int au_file_roflags(unsigned int flags) ++{ ++ flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); ++ flags |= O_RDONLY | O_NOATIME; ++ return flags; ++} ++ ++/* common functions to regular file and dir */ ++struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, ++ struct file *file, int force_wr) ++{ ++ struct file *h_file; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct path h_path; ++ int err; ++ ++ /* a race condition can happen between open and unlink/rmdir */ ++ h_file = ERR_PTR(-ENOENT); ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (au_test_nfsd() && !h_dentry) ++ goto out; ++ h_inode = h_dentry->d_inode; ++ if (au_test_nfsd() && !h_inode) ++ goto out; ++ spin_lock(&h_dentry->d_lock); ++ err = (!d_unhashed(dentry) && d_unlinked(h_dentry)) ++ || !h_inode ++ /* || !dentry->d_inode->i_nlink */ ++ ; ++ spin_unlock(&h_dentry->d_lock); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, bindex); ++ err = au_br_test_oflag(flags, br); ++ h_file = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ /* drop flags for writing */ ++ if (au_test_ro(sb, bindex, dentry->d_inode)) { ++ if (force_wr && !(flags & O_WRONLY)) ++ force_wr = 0; ++ flags = au_file_roflags(flags); ++ if (force_wr) { ++ h_file = ERR_PTR(-EROFS); ++ flags = au_file_roflags(flags); ++ if (unlikely(vfsub_native_ro(h_inode) ++ || IS_APPEND(h_inode))) ++ goto out; ++ flags &= ~O_ACCMODE; ++ flags |= O_WRONLY; ++ } ++ } ++ flags &= ~O_CREAT; ++ atomic_inc(&br->br_count); ++ h_path.dentry = h_dentry; ++ h_path.mnt = au_br_mnt(br); ++ h_file = vfsub_dentry_open(&h_path, flags); ++ if (IS_ERR(h_file)) ++ goto out_br; ++ ++ if (flags & __FMODE_EXEC) { ++ err = deny_write_access(h_file); ++ if (unlikely(err)) { ++ fput(h_file); ++ h_file = ERR_PTR(err); ++ goto out_br; ++ } ++ } ++ fsnotify_open(h_file); ++ goto out; /* success */ ++ ++out_br: ++ atomic_dec(&br->br_count); ++out: ++ return h_file; ++} ++ ++static int au_cmoo(struct dentry *dentry) ++{ ++ int err, cmoo; ++ unsigned int udba; ++ struct path h_path; ++ struct au_pin pin; ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = -1, ++ .bsrc = -1, ++ .len = -1, ++ .pin = &pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ struct inode *delegated; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ struct au_fhsm *fhsm; ++ pid_t pid; ++ struct au_branch *br; ++ struct dentry *parent; ++ struct au_hinode *hdir; ++ ++ DiMustWriteLock(dentry); ++ IiMustWriteLock(dentry->d_inode); ++ ++ err = 0; ++ if (IS_ROOT(dentry)) ++ goto out; ++ cpg.bsrc = au_dbstart(dentry); ++ if (!cpg.bsrc) ++ goto out; ++ ++ sb = dentry->d_sb; ++ sbinfo = au_sbi(sb); ++ fhsm = &sbinfo->si_fhsm; ++ pid = au_fhsm_pid(fhsm); ++ if (pid ++ && (current->pid == pid ++ || current->real_parent->pid == pid)) ++ goto out; ++ ++ br = au_sbr(sb, cpg.bsrc); ++ cmoo = au_br_cmoo(br->br_perm); ++ if (!cmoo) ++ goto out; ++ if (!d_is_reg(dentry)) ++ cmoo &= AuBrAttr_COO_ALL; ++ if (!cmoo) ++ goto out; ++ ++ parent = dget_parent(dentry); ++ di_write_lock_parent(parent); ++ err = au_wbr_do_copyup_bu(dentry, cpg.bsrc - 1); ++ cpg.bdst = err; ++ if (unlikely(err < 0)) { ++ err = 0; /* there is no upper writable branch */ ++ goto out_dgrade; ++ } ++ AuDbg("bsrc %d, bdst %d\n", cpg.bsrc, cpg.bdst); ++ ++ /* do not respect the coo attrib for the target branch */ ++ err = au_cpup_dirs(dentry, cpg.bdst); ++ if (unlikely(err)) ++ goto out_dgrade; ++ ++ di_downgrade_lock(parent, AuLock_IR); ++ udba = au_opt_udba(sb); ++ err = au_pin(&pin, dentry, cpg.bdst, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&pin); ++ if (unlikely(err)) ++ goto out_parent; ++ if (!(cmoo & AuBrWAttr_MOO)) ++ goto out_parent; /* success */ ++ ++ err = au_pin(&pin, dentry, cpg.bsrc, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ h_path.mnt = au_br_mnt(br); ++ h_path.dentry = au_h_dptr(dentry, cpg.bsrc); ++ hdir = au_hi(parent->d_inode, cpg.bsrc); ++ delegated = NULL; ++ err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, /*force*/1); ++ au_unpin(&pin); ++ /* todo: keep h_dentry or not? */ ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ if (unlikely(err)) { ++ pr_err("unlink %pd after coo failed (%d), ignored\n", ++ dentry, err); ++ err = 0; ++ } ++ goto out_parent; /* success */ ++ ++out_dgrade: ++ di_downgrade_lock(parent, AuLock_IR); ++out_parent: ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_do_open(struct file *file, struct au_do_open_args *args) ++{ ++ int err, no_lock = args->no_lock; ++ struct dentry *dentry; ++ struct au_finfo *finfo; ++ ++ if (!no_lock) ++ err = au_finfo_init(file, args->fidir); ++ else { ++ lockdep_off(); ++ err = au_finfo_init(file, args->fidir); ++ lockdep_on(); ++ } ++ if (unlikely(err)) ++ goto out; ++ ++ dentry = file->f_path.dentry; ++ AuDebugOn(IS_ERR_OR_NULL(dentry)); ++ if (!no_lock) { ++ di_write_lock_child(dentry); ++ err = au_cmoo(dentry); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (!err) ++ err = args->open(file, vfsub_file_flags(file), NULL); ++ di_read_unlock(dentry, AuLock_IR); ++ } else { ++ err = au_cmoo(dentry); ++ if (!err) ++ err = args->open(file, vfsub_file_flags(file), ++ args->h_file); ++ if (!err && au_fbstart(file) != au_dbstart(dentry)) ++ /* ++ * cmoo happens after h_file was opened. ++ * need to refresh file later. ++ */ ++ atomic_dec(&au_fi(file)->fi_generation); ++ } ++ ++ finfo = au_fi(file); ++ if (!err) { ++ finfo->fi_file = file; ++ au_sphl_add(&finfo->fi_hlist, ++ &au_sbi(file->f_path.dentry->d_sb)->si_files); ++ } ++ if (!no_lock) ++ fi_write_unlock(file); ++ else { ++ lockdep_off(); ++ fi_write_unlock(file); ++ lockdep_on(); ++ } ++ if (unlikely(err)) { ++ finfo->fi_hdir = NULL; ++ au_finfo_fin(file); ++ } ++ ++out: ++ return err; ++} ++ ++int au_reopen_nondir(struct file *file) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct dentry *dentry; ++ struct file *h_file, *h_file_tmp; ++ ++ dentry = file->f_path.dentry; ++ bstart = au_dbstart(dentry); ++ h_file_tmp = NULL; ++ if (au_fbstart(file) == bstart) { ++ h_file = au_hf_top(file); ++ if (file->f_mode == h_file->f_mode) ++ return 0; /* success */ ++ h_file_tmp = h_file; ++ get_file(h_file_tmp); ++ au_set_h_fptr(file, bstart, NULL); ++ } ++ AuDebugOn(au_fi(file)->fi_hdir); ++ /* ++ * it can happen ++ * file exists on both of rw and ro ++ * open --> dbstart and fbstart are both 0 ++ * prepend a branch as rw, "rw" become ro ++ * remove rw/file ++ * delete the top branch, "rw" becomes rw again ++ * --> dbstart is 1, fbstart is still 0 ++ * write --> fbstart is 0 but dbstart is 1 ++ */ ++ /* AuDebugOn(au_fbstart(file) < bstart); */ ++ ++ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, ++ file, /*force_wr*/0); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) { ++ if (h_file_tmp) { ++ atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); ++ au_set_h_fptr(file, bstart, h_file_tmp); ++ h_file_tmp = NULL; ++ } ++ goto out; /* todo: close all? */ ++ } ++ ++ err = 0; ++ au_set_fbstart(file, bstart); ++ au_set_h_fptr(file, bstart, h_file); ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ ++out: ++ if (h_file_tmp) ++ fput(h_file_tmp); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, ++ struct dentry *hi_wh) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct au_dinfo *dinfo; ++ struct dentry *h_dentry; ++ struct au_hdentry *hdp; ++ ++ dinfo = au_di(file->f_path.dentry); ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ bstart = dinfo->di_bstart; ++ dinfo->di_bstart = btgt; ++ hdp = dinfo->di_hdentry; ++ h_dentry = hdp[0 + btgt].hd_dentry; ++ hdp[0 + btgt].hd_dentry = hi_wh; ++ err = au_reopen_nondir(file); ++ hdp[0 + btgt].hd_dentry = h_dentry; ++ dinfo->di_bstart = bstart; ++ ++ return err; ++} ++ ++static int au_ready_to_write_wh(struct file *file, loff_t len, ++ aufs_bindex_t bcpup, struct au_pin *pin) ++{ ++ int err; ++ struct inode *inode, *h_inode; ++ struct dentry *h_dentry, *hi_wh; ++ struct au_cp_generic cpg = { ++ .dentry = file->f_path.dentry, ++ .bdst = bcpup, ++ .bsrc = -1, ++ .len = len, ++ .pin = pin ++ }; ++ ++ au_update_dbstart(cpg.dentry); ++ inode = cpg.dentry->d_inode; ++ h_inode = NULL; ++ if (au_dbstart(cpg.dentry) <= bcpup ++ && au_dbend(cpg.dentry) >= bcpup) { ++ h_dentry = au_h_dptr(cpg.dentry, bcpup); ++ if (h_dentry) ++ h_inode = h_dentry->d_inode; ++ } ++ hi_wh = au_hi_wh(inode, bcpup); ++ if (!hi_wh && !h_inode) ++ err = au_sio_cpup_wh(&cpg, file); ++ else ++ /* already copied-up after unlink */ ++ err = au_reopen_wh(file, bcpup, hi_wh); ++ ++ if (!err ++ && (inode->i_nlink > 1 ++ || (inode->i_state & I_LINKABLE)) ++ && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK)) ++ au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); ++ ++ return err; ++} ++ ++/* ++ * prepare the @file for writing. ++ */ ++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) ++{ ++ int err; ++ aufs_bindex_t dbstart; ++ struct dentry *parent; ++ struct inode *inode; ++ struct super_block *sb; ++ struct file *h_file; ++ struct au_cp_generic cpg = { ++ .dentry = file->f_path.dentry, ++ .bdst = -1, ++ .bsrc = -1, ++ .len = len, ++ .pin = pin, ++ .flags = AuCpup_DTIME ++ }; ++ ++ sb = cpg.dentry->d_sb; ++ inode = cpg.dentry->d_inode; ++ cpg.bsrc = au_fbstart(file); ++ err = au_test_ro(sb, cpg.bsrc, inode); ++ if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { ++ err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, ++ /*flags*/0); ++ goto out; ++ } ++ ++ /* need to cpup or reopen */ ++ parent = dget_parent(cpg.dentry); ++ di_write_lock_parent(parent); ++ err = AuWbrCopyup(au_sbi(sb), cpg.dentry); ++ cpg.bdst = err; ++ if (unlikely(err < 0)) ++ goto out_dgrade; ++ err = 0; ++ ++ if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { ++ err = au_cpup_dirs(cpg.dentry, cpg.bdst); ++ if (unlikely(err)) ++ goto out_dgrade; ++ } ++ ++ err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_dgrade; ++ ++ dbstart = au_dbstart(cpg.dentry); ++ if (dbstart <= cpg.bdst) ++ cpg.bsrc = cpg.bdst; ++ ++ if (dbstart <= cpg.bdst /* just reopen */ ++ || !d_unhashed(cpg.dentry) /* copyup and reopen */ ++ ) { ++ h_file = au_h_open_pre(cpg.dentry, cpg.bsrc, /*force_wr*/0); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ di_downgrade_lock(parent, AuLock_IR); ++ if (dbstart > cpg.bdst) ++ err = au_sio_cpup_simple(&cpg); ++ if (!err) ++ err = au_reopen_nondir(file); ++ au_h_open_post(cpg.dentry, cpg.bsrc, h_file); ++ } ++ } else { /* copyup as wh and reopen */ ++ /* ++ * since writable hfsplus branch is not supported, ++ * h_open_pre/post() are unnecessary. ++ */ ++ err = au_ready_to_write_wh(file, len, cpg.bdst, pin); ++ di_downgrade_lock(parent, AuLock_IR); ++ } ++ ++ if (!err) { ++ au_pin_set_parent_lflag(pin, /*lflag*/0); ++ goto out_dput; /* success */ ++ } ++ au_unpin(pin); ++ goto out_unlock; ++ ++out_dgrade: ++ di_downgrade_lock(parent, AuLock_IR); ++out_unlock: ++ di_read_unlock(parent, AuLock_IR); ++out_dput: ++ dput(parent); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_do_flush(struct file *file, fl_owner_t id, ++ int (*flush)(struct file *file, fl_owner_t id)) ++{ ++ int err; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ inode = file_inode(file); ++ sb = inode->i_sb; ++ si_noflush_read_lock(sb); ++ fi_read_lock(file); ++ ii_read_lock_child(inode); ++ ++ err = flush(file, id); ++ au_cpup_attr_timesizes(inode); ++ ++ ii_read_unlock(inode); ++ fi_read_unlock(file); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_file_refresh_by_inode(struct file *file, int *need_reopen) ++{ ++ int err; ++ struct au_pin pin; ++ struct au_finfo *finfo; ++ struct dentry *parent, *hi_wh; ++ struct inode *inode; ++ struct super_block *sb; ++ struct au_cp_generic cpg = { ++ .dentry = file->f_path.dentry, ++ .bdst = -1, ++ .bsrc = -1, ++ .len = -1, ++ .pin = &pin, ++ .flags = AuCpup_DTIME ++ }; ++ ++ FiMustWriteLock(file); ++ ++ err = 0; ++ finfo = au_fi(file); ++ sb = cpg.dentry->d_sb; ++ inode = cpg.dentry->d_inode; ++ cpg.bdst = au_ibstart(inode); ++ if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) ++ goto out; ++ ++ parent = dget_parent(cpg.dentry); ++ if (au_test_ro(sb, cpg.bdst, inode)) { ++ di_read_lock_parent(parent, !AuLock_IR); ++ err = AuWbrCopyup(au_sbi(sb), cpg.dentry); ++ cpg.bdst = err; ++ di_read_unlock(parent, !AuLock_IR); ++ if (unlikely(err < 0)) ++ goto out_parent; ++ err = 0; ++ } ++ ++ di_read_lock_parent(parent, AuLock_IR); ++ hi_wh = au_hi_wh(inode, cpg.bdst); ++ if (!S_ISDIR(inode->i_mode) ++ && au_opt_test(au_mntflags(sb), PLINK) ++ && au_plink_test(inode) ++ && !d_unhashed(cpg.dentry) ++ && cpg.bdst < au_dbstart(cpg.dentry)) { ++ err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ /* always superio. */ ++ err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (!err) { ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&pin); ++ } ++ } else if (hi_wh) { ++ /* already copied-up after unlink */ ++ err = au_reopen_wh(file, cpg.bdst, hi_wh); ++ *need_reopen = 0; ++ } ++ ++out_unlock: ++ di_read_unlock(parent, AuLock_IR); ++out_parent: ++ dput(parent); ++out: ++ return err; ++} ++ ++static void au_do_refresh_dir(struct file *file) ++{ ++ aufs_bindex_t bindex, bend, new_bindex, brid; ++ struct au_hfile *p, tmp, *q; ++ struct au_finfo *finfo; ++ struct super_block *sb; ++ struct au_fidir *fidir; ++ ++ FiMustWriteLock(file); ++ ++ sb = file->f_path.dentry->d_sb; ++ finfo = au_fi(file); ++ fidir = finfo->fi_hdir; ++ AuDebugOn(!fidir); ++ p = fidir->fd_hfile + finfo->fi_btop; ++ brid = p->hf_br->br_id; ++ bend = fidir->fd_bbot; ++ for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { ++ if (!p->hf_file) ++ continue; ++ ++ new_bindex = au_br_index(sb, p->hf_br->br_id); ++ if (new_bindex == bindex) ++ continue; ++ if (new_bindex < 0) { ++ au_set_h_fptr(file, bindex, NULL); ++ continue; ++ } ++ ++ /* swap two lower inode, and loop again */ ++ q = fidir->fd_hfile + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hf_file) { ++ bindex--; ++ p--; ++ } ++ } ++ ++ p = fidir->fd_hfile; ++ if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) { ++ bend = au_sbend(sb); ++ for (finfo->fi_btop = 0; finfo->fi_btop <= bend; ++ finfo->fi_btop++, p++) ++ if (p->hf_file) { ++ if (file_inode(p->hf_file)) ++ break; ++ au_hfput(p, file); ++ } ++ } else { ++ bend = au_br_index(sb, brid); ++ for (finfo->fi_btop = 0; finfo->fi_btop < bend; ++ finfo->fi_btop++, p++) ++ if (p->hf_file) ++ au_hfput(p, file); ++ bend = au_sbend(sb); ++ } ++ ++ p = fidir->fd_hfile + bend; ++ for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; ++ fidir->fd_bbot--, p--) ++ if (p->hf_file) { ++ if (file_inode(p->hf_file)) ++ break; ++ au_hfput(p, file); ++ } ++ AuDebugOn(fidir->fd_bbot < finfo->fi_btop); ++} ++ ++/* ++ * after branch manipulating, refresh the file. ++ */ ++static int refresh_file(struct file *file, int (*reopen)(struct file *file)) ++{ ++ int err, need_reopen; ++ aufs_bindex_t bend, bindex; ++ struct dentry *dentry; ++ struct au_finfo *finfo; ++ struct au_hfile *hfile; ++ ++ dentry = file->f_path.dentry; ++ finfo = au_fi(file); ++ if (!finfo->fi_hdir) { ++ hfile = &finfo->fi_htop; ++ AuDebugOn(!hfile->hf_file); ++ bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); ++ AuDebugOn(bindex < 0); ++ if (bindex != finfo->fi_btop) ++ au_set_fbstart(file, bindex); ++ } else { ++ err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ au_do_refresh_dir(file); ++ } ++ ++ err = 0; ++ need_reopen = 1; ++ if (!au_test_mmapped(file)) ++ err = au_file_refresh_by_inode(file, &need_reopen); ++ if (!err && need_reopen && !d_unlinked(dentry)) ++ err = reopen(file); ++ if (!err) { ++ au_update_figen(file); ++ goto out; /* success */ ++ } ++ ++ /* error, close all lower files */ ++ if (finfo->fi_hdir) { ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); bindex <= bend; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ } ++ ++out: ++ return err; ++} ++ ++/* common function to regular file and dir */ ++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), ++ int wlock) ++{ ++ int err; ++ unsigned int sigen, figen; ++ aufs_bindex_t bstart; ++ unsigned char pseudo_link; ++ struct dentry *dentry; ++ struct inode *inode; ++ ++ err = 0; ++ dentry = file->f_path.dentry; ++ inode = dentry->d_inode; ++ sigen = au_sigen(dentry->d_sb); ++ fi_write_lock(file); ++ figen = au_figen(file); ++ di_write_lock_child(dentry); ++ bstart = au_dbstart(dentry); ++ pseudo_link = (bstart != au_ibstart(inode)); ++ if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { ++ if (!wlock) { ++ di_downgrade_lock(dentry, AuLock_IR); ++ fi_downgrade_lock(file); ++ } ++ goto out; /* success */ ++ } ++ ++ AuDbg("sigen %d, figen %d\n", sigen, figen); ++ if (au_digen_test(dentry, sigen)) { ++ err = au_reval_dpath(dentry, sigen); ++ AuDebugOn(!err && au_digen_test(dentry, sigen)); ++ } ++ ++ if (!err) ++ err = refresh_file(file, reopen); ++ if (!err) { ++ if (!wlock) { ++ di_downgrade_lock(dentry, AuLock_IR); ++ fi_downgrade_lock(file); ++ } ++ } else { ++ di_write_unlock(dentry); ++ fi_write_unlock(file); ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* cf. aufs_nopage() */ ++/* for madvise(2) */ ++static int aufs_readpage(struct file *file __maybe_unused, struct page *page) ++{ ++ unlock_page(page); ++ return 0; ++} ++ ++/* it will never be called, but necessary to support O_DIRECT */ ++static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb, ++ struct iov_iter *iter, loff_t offset) ++{ BUG(); return 0; } ++ ++/* they will never be called. */ ++#ifdef CONFIG_AUFS_DEBUG ++static int aufs_write_begin(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ AuUnsupport(); return 0; } ++static int aufs_write_end(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *page, void *fsdata) ++{ AuUnsupport(); return 0; } ++static int aufs_writepage(struct page *page, struct writeback_control *wbc) ++{ AuUnsupport(); return 0; } ++ ++static int aufs_set_page_dirty(struct page *page) ++{ AuUnsupport(); return 0; } ++static void aufs_invalidatepage(struct page *page, unsigned int offset, ++ unsigned int length) ++{ AuUnsupport(); } ++static int aufs_releasepage(struct page *page, gfp_t gfp) ++{ AuUnsupport(); return 0; } ++static int aufs_migratepage(struct address_space *mapping, struct page *newpage, ++ struct page *page, enum migrate_mode mode) ++{ AuUnsupport(); return 0; } ++static int aufs_launder_page(struct page *page) ++{ AuUnsupport(); return 0; } ++static int aufs_is_partially_uptodate(struct page *page, ++ unsigned long from, ++ unsigned long count) ++{ AuUnsupport(); return 0; } ++static void aufs_is_dirty_writeback(struct page *page, bool *dirty, ++ bool *writeback) ++{ AuUnsupport(); } ++static int aufs_error_remove_page(struct address_space *mapping, ++ struct page *page) ++{ AuUnsupport(); return 0; } ++static int aufs_swap_activate(struct swap_info_struct *sis, struct file *file, ++ sector_t *span) ++{ AuUnsupport(); return 0; } ++static void aufs_swap_deactivate(struct file *file) ++{ AuUnsupport(); } ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++const struct address_space_operations aufs_aop = { ++ .readpage = aufs_readpage, ++ .direct_IO = aufs_direct_IO, ++#ifdef CONFIG_AUFS_DEBUG ++ .writepage = aufs_writepage, ++ /* no writepages, because of writepage */ ++ .set_page_dirty = aufs_set_page_dirty, ++ /* no readpages, because of readpage */ ++ .write_begin = aufs_write_begin, ++ .write_end = aufs_write_end, ++ /* no bmap, no block device */ ++ .invalidatepage = aufs_invalidatepage, ++ .releasepage = aufs_releasepage, ++ .migratepage = aufs_migratepage, ++ .launder_page = aufs_launder_page, ++ .is_partially_uptodate = aufs_is_partially_uptodate, ++ .is_dirty_writeback = aufs_is_dirty_writeback, ++ .error_remove_page = aufs_error_remove_page, ++ .swap_activate = aufs_swap_activate, ++ .swap_deactivate = aufs_swap_deactivate ++#endif /* CONFIG_AUFS_DEBUG */ ++}; +diff -Nur linux-4.0.4.orig/fs/aufs/file.h linux-4.0.4/fs/aufs/file.h +--- linux-4.0.4.orig/fs/aufs/file.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/file.h 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,277 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * file operations ++ */ ++ ++#ifndef __AUFS_FILE_H__ ++#define __AUFS_FILE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include "rwsem.h" ++ ++struct au_branch; ++struct au_hfile { ++ struct file *hf_file; ++ struct au_branch *hf_br; ++}; ++ ++struct au_vdir; ++struct au_fidir { ++ aufs_bindex_t fd_bbot; ++ aufs_bindex_t fd_nent; ++ struct au_vdir *fd_vdir_cache; ++ struct au_hfile fd_hfile[]; ++}; ++ ++static inline int au_fidir_sz(int nent) ++{ ++ AuDebugOn(nent < 0); ++ return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent; ++} ++ ++struct au_finfo { ++ atomic_t fi_generation; ++ ++ struct au_rwsem fi_rwsem; ++ aufs_bindex_t fi_btop; ++ ++ /* do not union them */ ++ struct { /* for non-dir */ ++ struct au_hfile fi_htop; ++ atomic_t fi_mmapped; ++ }; ++ struct au_fidir *fi_hdir; /* for dir only */ ++ ++ struct hlist_node fi_hlist; ++ struct file *fi_file; /* very ugly */ ++} ____cacheline_aligned_in_smp; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* file.c */ ++extern const struct address_space_operations aufs_aop; ++unsigned int au_file_roflags(unsigned int flags); ++struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, ++ struct file *file, int force_wr); ++struct au_do_open_args { ++ int no_lock; ++ int (*open)(struct file *file, int flags, ++ struct file *h_file); ++ struct au_fidir *fidir; ++ struct file *h_file; ++}; ++int au_do_open(struct file *file, struct au_do_open_args *args); ++int au_reopen_nondir(struct file *file); ++struct au_pin; ++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin); ++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), ++ int wlock); ++int au_do_flush(struct file *file, fl_owner_t id, ++ int (*flush)(struct file *file, fl_owner_t id)); ++ ++/* poll.c */ ++#ifdef CONFIG_AUFS_POLL ++unsigned int aufs_poll(struct file *file, poll_table *wait); ++#endif ++ ++#ifdef CONFIG_AUFS_BR_HFSPLUS ++/* hfsplus.c */ ++struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, ++ int force_wr); ++void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, ++ struct file *h_file); ++#else ++AuStub(struct file *, au_h_open_pre, return NULL, struct dentry *dentry, ++ aufs_bindex_t bindex, int force_wr) ++AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex, ++ struct file *h_file); ++#endif ++ ++/* f_op.c */ ++extern const struct file_operations aufs_file_fop; ++int au_do_open_nondir(struct file *file, int flags, struct file *h_file); ++int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file); ++ ++/* finfo.c */ ++void au_hfput(struct au_hfile *hf, struct file *file); ++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, ++ struct file *h_file); ++ ++void au_update_figen(struct file *file); ++struct au_fidir *au_fidir_alloc(struct super_block *sb); ++int au_fidir_realloc(struct au_finfo *finfo, int nbr); ++ ++void au_fi_init_once(void *_fi); ++void au_finfo_fin(struct file *file); ++int au_finfo_init(struct file *file, struct au_fidir *fidir); ++ ++/* ioctl.c */ ++long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg); ++#ifdef CONFIG_COMPAT ++long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, ++ unsigned long arg); ++long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, ++ unsigned long arg); ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_finfo *au_fi(struct file *file) ++{ ++ return file->private_data; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * fi_read_lock, fi_write_lock, ++ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem); ++ ++#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) ++#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem) ++#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: hard/soft set? */ ++static inline aufs_bindex_t au_fbstart(struct file *file) ++{ ++ FiMustAnyLock(file); ++ return au_fi(file)->fi_btop; ++} ++ ++static inline aufs_bindex_t au_fbend_dir(struct file *file) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_hdir->fd_bbot; ++} ++ ++static inline struct au_vdir *au_fvdir_cache(struct file *file) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_hdir->fd_vdir_cache; ++} ++ ++static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) ++{ ++ FiMustWriteLock(file); ++ au_fi(file)->fi_btop = bindex; ++} ++ ++static inline void au_set_fbend_dir(struct file *file, aufs_bindex_t bindex) ++{ ++ FiMustWriteLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ au_fi(file)->fi_hdir->fd_bbot = bindex; ++} ++ ++static inline void au_set_fvdir_cache(struct file *file, ++ struct au_vdir *vdir_cache) ++{ ++ FiMustWriteLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache; ++} ++ ++static inline struct file *au_hf_top(struct file *file) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_htop.hf_file; ++} ++ ++static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file; ++} ++ ++/* todo: memory barrier? */ ++static inline unsigned int au_figen(struct file *f) ++{ ++ return atomic_read(&au_fi(f)->fi_generation); ++} ++ ++static inline void au_set_mmapped(struct file *f) ++{ ++ if (atomic_inc_return(&au_fi(f)->fi_mmapped)) ++ return; ++ pr_warn("fi_mmapped wrapped around\n"); ++ while (!atomic_inc_return(&au_fi(f)->fi_mmapped)) ++ ; ++} ++ ++static inline void au_unset_mmapped(struct file *f) ++{ ++ atomic_dec(&au_fi(f)->fi_mmapped); ++} ++ ++static inline int au_test_mmapped(struct file *f) ++{ ++ return atomic_read(&au_fi(f)->fi_mmapped); ++} ++ ++/* customize vma->vm_file */ ++ ++static inline void au_do_vm_file_reset(struct vm_area_struct *vma, ++ struct file *file) ++{ ++ struct file *f; ++ ++ f = vma->vm_file; ++ get_file(file); ++ vma->vm_file = file; ++ fput(f); ++} ++ ++#ifdef CONFIG_MMU ++#define AuDbgVmRegion(file, vma) do {} while (0) ++ ++static inline void au_vm_file_reset(struct vm_area_struct *vma, ++ struct file *file) ++{ ++ au_do_vm_file_reset(vma, file); ++} ++#else ++#define AuDbgVmRegion(file, vma) \ ++ AuDebugOn((vma)->vm_region && (vma)->vm_region->vm_file != (file)) ++ ++static inline void au_vm_file_reset(struct vm_area_struct *vma, ++ struct file *file) ++{ ++ struct file *f; ++ ++ au_do_vm_file_reset(vma, file); ++ f = vma->vm_region->vm_file; ++ get_file(file); ++ vma->vm_region->vm_file = file; ++ fput(f); ++} ++#endif /* CONFIG_MMU */ ++ ++/* handle vma->vm_prfile */ ++static inline void au_vm_prfile_set(struct vm_area_struct *vma, ++ struct file *file) ++{ ++ get_file(file); ++ vma->vm_prfile = file; ++#ifndef CONFIG_MMU ++ get_file(file); ++ vma->vm_region->vm_prfile = file; ++#endif ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_FILE_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/finfo.c linux-4.0.4/fs/aufs/finfo.c +--- linux-4.0.4.orig/fs/aufs/finfo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/finfo.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,144 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * file private data ++ */ ++ ++#include "aufs.h" ++ ++void au_hfput(struct au_hfile *hf, struct file *file) ++{ ++ /* todo: direct access f_flags */ ++ if (vfsub_file_flags(file) & __FMODE_EXEC) ++ allow_write_access(hf->hf_file); ++ fput(hf->hf_file); ++ hf->hf_file = NULL; ++ atomic_dec(&hf->hf_br->br_count); ++ hf->hf_br = NULL; ++} ++ ++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) ++{ ++ struct au_finfo *finfo = au_fi(file); ++ struct au_hfile *hf; ++ struct au_fidir *fidir; ++ ++ fidir = finfo->fi_hdir; ++ if (!fidir) { ++ AuDebugOn(finfo->fi_btop != bindex); ++ hf = &finfo->fi_htop; ++ } else ++ hf = fidir->fd_hfile + bindex; ++ ++ if (hf && hf->hf_file) ++ au_hfput(hf, file); ++ if (val) { ++ FiMustWriteLock(file); ++ AuDebugOn(IS_ERR_OR_NULL(file->f_path.dentry)); ++ hf->hf_file = val; ++ hf->hf_br = au_sbr(file->f_path.dentry->d_sb, bindex); ++ } ++} ++ ++void au_update_figen(struct file *file) ++{ ++ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_path.dentry)); ++ /* smp_mb(); */ /* atomic_set */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_fidir *au_fidir_alloc(struct super_block *sb) ++{ ++ struct au_fidir *fidir; ++ int nbr; ++ ++ nbr = au_sbend(sb) + 1; ++ if (nbr < 2) ++ nbr = 2; /* initial allocate for 2 branches */ ++ fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); ++ if (fidir) { ++ fidir->fd_bbot = -1; ++ fidir->fd_nent = nbr; ++ fidir->fd_vdir_cache = NULL; ++ } ++ ++ return fidir; ++} ++ ++int au_fidir_realloc(struct au_finfo *finfo, int nbr) ++{ ++ int err; ++ struct au_fidir *fidir, *p; ++ ++ AuRwMustWriteLock(&finfo->fi_rwsem); ++ fidir = finfo->fi_hdir; ++ AuDebugOn(!fidir); ++ ++ err = -ENOMEM; ++ p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr), ++ GFP_NOFS); ++ if (p) { ++ p->fd_nent = nbr; ++ finfo->fi_hdir = p; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_finfo_fin(struct file *file) ++{ ++ struct au_finfo *finfo; ++ ++ au_nfiles_dec(file->f_path.dentry->d_sb); ++ ++ finfo = au_fi(file); ++ AuDebugOn(finfo->fi_hdir); ++ AuRwDestroy(&finfo->fi_rwsem); ++ au_cache_free_finfo(finfo); ++} ++ ++void au_fi_init_once(void *_finfo) ++{ ++ struct au_finfo *finfo = _finfo; ++ static struct lock_class_key aufs_fi; ++ ++ au_rw_init(&finfo->fi_rwsem); ++ au_rw_class(&finfo->fi_rwsem, &aufs_fi); ++} ++ ++int au_finfo_init(struct file *file, struct au_fidir *fidir) ++{ ++ int err; ++ struct au_finfo *finfo; ++ struct dentry *dentry; ++ ++ err = -ENOMEM; ++ dentry = file->f_path.dentry; ++ finfo = au_cache_alloc_finfo(); ++ if (unlikely(!finfo)) ++ goto out; ++ ++ err = 0; ++ au_nfiles_inc(dentry->d_sb); ++ /* verbose coding for lock class name */ ++ if (!fidir) ++ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO); ++ else ++ au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO); ++ au_rw_write_lock(&finfo->fi_rwsem); ++ finfo->fi_btop = -1; ++ finfo->fi_hdir = fidir; ++ atomic_set(&finfo->fi_generation, au_digen(dentry)); ++ /* smp_mb(); */ /* atomic_set */ ++ ++ file->private_data = finfo; ++ ++out: ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/f_op.c linux-4.0.4/fs/aufs/f_op.c +--- linux-4.0.4.orig/fs/aufs/f_op.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/f_op.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,807 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * file and vm operations ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++int au_do_open_nondir(struct file *file, int flags, struct file *h_file) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct dentry *dentry; ++ struct au_finfo *finfo; ++ struct inode *h_inode; ++ ++ FiMustWriteLock(file); ++ ++ err = 0; ++ dentry = file->f_path.dentry; ++ AuDebugOn(IS_ERR_OR_NULL(dentry)); ++ finfo = au_fi(file); ++ memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); ++ atomic_set(&finfo->fi_mmapped, 0); ++ bindex = au_dbstart(dentry); ++ if (!h_file) ++ h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0); ++ else ++ get_file(h_file); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ if ((flags & __O_TMPFILE) ++ && !(flags & O_EXCL)) { ++ h_inode = file_inode(h_file); ++ spin_lock(&h_inode->i_lock); ++ h_inode->i_state |= I_LINKABLE; ++ spin_unlock(&h_inode->i_lock); ++ } ++ au_set_fbstart(file, bindex); ++ au_set_h_fptr(file, bindex, h_file); ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ } ++ ++ return err; ++} ++ ++static int aufs_open_nondir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_do_open_args args = { ++ .open = au_do_open_nondir ++ }; ++ ++ AuDbg("%pD, f_flags 0x%x, f_mode 0x%x\n", ++ file, vfsub_file_flags(file), file->f_mode); ++ ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_do_open(file, &args); ++ si_read_unlock(sb); ++ return err; ++} ++ ++int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) ++{ ++ struct au_finfo *finfo; ++ aufs_bindex_t bindex; ++ ++ finfo = au_fi(file); ++ au_sphl_del(&finfo->fi_hlist, ++ &au_sbi(file->f_path.dentry->d_sb)->si_files); ++ bindex = finfo->fi_btop; ++ if (bindex >= 0) ++ au_set_h_fptr(file, bindex, NULL); ++ ++ au_finfo_fin(file); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_flush_nondir(struct file *file, fl_owner_t id) ++{ ++ int err; ++ struct file *h_file; ++ ++ err = 0; ++ h_file = au_hf_top(file); ++ if (h_file) ++ err = vfsub_flush(h_file, id); ++ return err; ++} ++ ++static int aufs_flush_nondir(struct file *file, fl_owner_t id) ++{ ++ return au_do_flush(file, id, au_do_flush_nondir); ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * read and write functions acquire [fdi]_rwsem once, but release before ++ * mmap_sem. This is because to stop a race condition between mmap(2). ++ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping ++ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in ++ * read functions after [fdi]_rwsem are released, but it should be harmless. ++ */ ++ ++static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ struct dentry *dentry; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++ /* filedata may be obsoleted by concurrent copyup, but no problem */ ++ err = vfsub_read_u(h_file, buf, count, ppos); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(dentry->d_inode, file_inode(h_file)); ++ fput(h_file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ++ * todo: very ugly ++ * it locks both of i_mutex and si_rwsem for read in safe. ++ * if the plink maintenance mode continues forever (that is the problem), ++ * may loop forever. ++ */ ++static void au_mtx_and_read_lock(struct inode *inode) ++{ ++ int err; ++ struct super_block *sb = inode->i_sb; ++ ++ while (1) { ++ mutex_lock(&inode->i_mutex); ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (!err) ++ break; ++ mutex_unlock(&inode->i_mutex); ++ si_read_lock(sb, AuLock_NOPLMW); ++ si_read_unlock(sb); ++ } ++} ++ ++static ssize_t aufs_write(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ blkcnt_t blks; ++ aufs_bindex_t bstart; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode, *h_inode; ++ struct super_block *sb; ++ struct file *h_file; ++ char __user *buf = (char __user *)ubuf; ++ ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ au_mtx_and_read_lock(inode); ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) { ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ goto out; ++ } ++ ++ bstart = au_fbstart(file); ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ h_inode = file_inode(h_file); ++ blks = h_inode->i_blocks; ++ au_unpin(&pin); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ ++ err = vfsub_write_u(h_file, buf, count, ppos); ++ ii_write_lock_child(inode); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = file_inode(h_file)->i_mode; ++ AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); ++ if (err > 0) ++ au_fhsm_wrote(sb, bstart, /*force*/h_inode->i_blocks > blks); ++ ii_write_unlock(inode); ++ fput(h_file); ++ ++out: ++ si_read_unlock(sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++static ssize_t au_do_iter(struct file *h_file, int rw, struct kiocb *kio, ++ struct iov_iter *iov_iter) ++{ ++ ssize_t err; ++ struct file *file; ++ ssize_t (*iter)(struct kiocb *, struct iov_iter *); ++ ssize_t (*aio)(struct kiocb *, const struct iovec *, unsigned long, ++ loff_t); ++ ++ err = security_file_permission(h_file, rw); ++ if (unlikely(err)) ++ goto out; ++ ++ err = -ENOSYS; ++ iter = NULL; ++ aio = NULL; ++ if (rw == MAY_READ) { ++ iter = h_file->f_op->read_iter; ++ aio = h_file->f_op->aio_read; ++ } else if (rw == MAY_WRITE) { ++ iter = h_file->f_op->write_iter; ++ aio = h_file->f_op->aio_write; ++ } ++ ++ file = kio->ki_filp; ++ kio->ki_filp = h_file; ++ if (iter) { ++ lockdep_off(); ++ err = iter(kio, iov_iter); ++ lockdep_on(); ++ } else if (aio) { ++ lockdep_off(); ++ err = aio(kio, iov_iter->iov, iov_iter->nr_segs, kio->ki_pos); ++ lockdep_on(); ++ } else ++ /* currently there is no such fs */ ++ WARN_ON_ONCE(1); ++ kio->ki_filp = file; ++ ++out: ++ return err; ++} ++ ++static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter) ++{ ++ ssize_t err; ++ struct file *file, *h_file; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ file = kio->ki_filp; ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++ err = au_do_iter(h_file, MAY_READ, kio, iov_iter); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(dentry->d_inode, file_inode(h_file)); ++ fput(h_file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static ssize_t aufs_write_iter(struct kiocb *kio, struct iov_iter *iov_iter) ++{ ++ ssize_t err; ++ blkcnt_t blks; ++ aufs_bindex_t bstart; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode, *h_inode; ++ struct file *file, *h_file; ++ struct super_block *sb; ++ ++ file = kio->ki_filp; ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ au_mtx_and_read_lock(inode); ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) { ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ goto out; ++ } ++ ++ bstart = au_fbstart(file); ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ h_inode = file_inode(h_file); ++ blks = h_inode->i_blocks; ++ au_unpin(&pin); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ ++ err = au_do_iter(h_file, MAY_WRITE, kio, iov_iter); ++ ii_write_lock_child(inode); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = file_inode(h_file)->i_mode; ++ AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); ++ if (err > 0) ++ au_fhsm_wrote(sb, bstart, /*force*/h_inode->i_blocks > blks); ++ ii_write_unlock(inode); ++ fput(h_file); ++ ++out: ++ si_read_unlock(sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ ssize_t err; ++ struct file *h_file; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ err = -EINVAL; ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ if (au_test_loopback_kthread()) { ++ au_warn_loopback(h_file->f_path.dentry->d_sb); ++ if (file->f_mapping != h_file->f_mapping) { ++ file->f_mapping = h_file->f_mapping; ++ smp_mb(); /* unnecessary? */ ++ } ++ } ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); ++ /* todo: necessasry? */ ++ /* file->f_ra = h_file->f_ra; */ ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(dentry->d_inode, file_inode(h_file)); ++ fput(h_file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static ssize_t ++aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, ++ size_t len, unsigned int flags) ++{ ++ ssize_t err; ++ blkcnt_t blks; ++ aufs_bindex_t bstart; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode, *h_inode; ++ struct super_block *sb; ++ struct file *h_file; ++ ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ au_mtx_and_read_lock(inode); ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) { ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ goto out; ++ } ++ ++ bstart = au_fbstart(file); ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ h_inode = file_inode(h_file); ++ blks = h_inode->i_blocks; ++ au_unpin(&pin); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ ++ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); ++ ii_write_lock_child(inode); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = file_inode(h_file)->i_mode; ++ AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); ++ if (err > 0) ++ au_fhsm_wrote(sb, bstart, /*force*/h_inode->i_blocks > blks); ++ ii_write_unlock(inode); ++ fput(h_file); ++ ++out: ++ si_read_unlock(sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++static long aufs_fallocate(struct file *file, int mode, loff_t offset, ++ loff_t len) ++{ ++ long err; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct super_block *sb; ++ struct inode *inode; ++ struct file *h_file; ++ ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ au_mtx_and_read_lock(inode); ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) { ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ goto out; ++ } ++ ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ au_unpin(&pin); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ ++ lockdep_off(); ++ err = vfs_fallocate(h_file, mode, offset, len); ++ lockdep_on(); ++ ii_write_lock_child(inode); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = file_inode(h_file)->i_mode; ++ ii_write_unlock(inode); ++ fput(h_file); ++ ++out: ++ si_read_unlock(sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * The locking order around current->mmap_sem. ++ * - in most and regular cases ++ * file I/O syscall -- aufs_read() or something ++ * -- si_rwsem for read -- mmap_sem ++ * (Note that [fdi]i_rwsem are released before mmap_sem). ++ * - in mmap case ++ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem ++ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for ++ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in ++ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. ++ * It means that when aufs acquires si_rwsem for write, the process should never ++ * acquire mmap_sem. ++ * ++ * Actually aufs_iterate() holds [fdi]i_rwsem before mmap_sem, but this is not a ++ * problem either since any directory is not able to be mmap-ed. ++ * The similar scenario is applied to aufs_readlink() too. ++ */ ++ ++#if 0 /* stop calling security_file_mmap() */ ++/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ ++#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) ++ ++static unsigned long au_arch_prot_conv(unsigned long flags) ++{ ++ /* currently ppc64 only */ ++#ifdef CONFIG_PPC64 ++ /* cf. linux/arch/powerpc/include/asm/mman.h */ ++ AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); ++ return AuConv_VM_PROT(flags, SAO); ++#else ++ AuDebugOn(arch_calc_vm_prot_bits(-1)); ++ return 0; ++#endif ++} ++ ++static unsigned long au_prot_conv(unsigned long flags) ++{ ++ return AuConv_VM_PROT(flags, READ) ++ | AuConv_VM_PROT(flags, WRITE) ++ | AuConv_VM_PROT(flags, EXEC) ++ | au_arch_prot_conv(flags); ++} ++ ++/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ ++#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) ++ ++static unsigned long au_flag_conv(unsigned long flags) ++{ ++ return AuConv_VM_MAP(flags, GROWSDOWN) ++ | AuConv_VM_MAP(flags, DENYWRITE) ++ | AuConv_VM_MAP(flags, LOCKED); ++} ++#endif ++ ++static int aufs_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ const unsigned char wlock ++ = (file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); ++ struct dentry *dentry; ++ struct super_block *sb; ++ struct file *h_file; ++ struct au_branch *br; ++ struct au_pin pin; ++ ++ AuDbgVmRegion(file, vma); ++ ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ lockdep_off(); ++ si_read_lock(sb, AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ if (wlock) { ++ err = au_ready_to_write(file, -1, &pin); ++ di_write_unlock(dentry); ++ if (unlikely(err)) { ++ fi_write_unlock(file); ++ goto out; ++ } ++ au_unpin(&pin); ++ } else ++ di_write_unlock(dentry); ++ ++ bstart = au_fbstart(file); ++ br = au_sbr(sb, bstart); ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ au_set_mmapped(file); ++ fi_write_unlock(file); ++ lockdep_on(); ++ ++ au_vm_file_reset(vma, h_file); ++ /* ++ * we cannot call security_mmap_file() here since it may acquire ++ * mmap_sem or i_mutex. ++ * ++ * err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), ++ * au_flag_conv(vma->vm_flags)); ++ */ ++ if (!err) ++ err = h_file->f_op->mmap(h_file, vma); ++ if (unlikely(err)) ++ goto out_reset; ++ ++ au_vm_prfile_set(vma, file); ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(file_inode(file), file_inode(h_file)); ++ goto out_fput; /* success */ ++ ++out_reset: ++ au_unset_mmapped(file); ++ au_vm_file_reset(vma, file); ++out_fput: ++ fput(h_file); ++ lockdep_off(); ++out: ++ si_read_unlock(sb); ++ lockdep_on(); ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, ++ int datasync) ++{ ++ int err; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ dentry = file->f_path.dentry; ++ inode = dentry->d_inode; ++ sb = dentry->d_sb; ++ mutex_lock(&inode->i_mutex); ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ ++ err = 0; /* -EBADF; */ /* posix? */ ++ if (unlikely(!(file->f_mode & FMODE_WRITE))) ++ goto out_si; ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out_si; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ au_unpin(&pin); ++ ++ err = -EINVAL; ++ h_file = au_hf_top(file); ++ err = vfsub_fsync(h_file, &h_file->f_path, datasync); ++ au_cpup_attr_timesizes(inode); ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out_si: ++ si_read_unlock(sb); ++out: ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++/* no one supports this operation, currently */ ++#if 0 ++static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync) ++{ ++ int err; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct file *file, *h_file; ++ ++ file = kio->ki_filp; ++ dentry = file->f_path.dentry; ++ inode = dentry->d_inode; ++ au_mtx_and_read_lock(inode); ++ ++ err = 0; /* -EBADF; */ /* posix? */ ++ if (unlikely(!(file->f_mode & FMODE_WRITE))) ++ goto out; ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ au_unpin(&pin); ++ ++ err = -ENOSYS; ++ h_file = au_hf_top(file); ++ if (h_file->f_op->aio_fsync) { ++ struct mutex *h_mtx; ++ ++ h_mtx = &file_inode(h_file)->i_mutex; ++ if (!is_sync_kiocb(kio)) { ++ get_file(h_file); ++ fput(file); ++ } ++ kio->ki_filp = h_file; ++ err = h_file->f_op->aio_fsync(kio, datasync); ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ if (!err) ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); ++ /*ignore*/ ++ au_cpup_attr_timesizes(inode); ++ mutex_unlock(h_mtx); ++ } ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out: ++ si_read_unlock(inode->sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++#endif ++ ++static int aufs_fasync(int fd, struct file *file, int flag) ++{ ++ int err; ++ struct file *h_file; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ h_file = au_hf_top(file); ++ if (h_file->f_op->fasync) ++ err = h_file->f_op->fasync(fd, h_file, flag); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* no one supports this operation, currently */ ++#if 0 ++static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, ++ size_t len, loff_t *pos, int more) ++{ ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++const struct file_operations aufs_file_fop = { ++ .owner = THIS_MODULE, ++ ++ .llseek = default_llseek, ++ ++ .read = aufs_read, ++ .write = aufs_write, ++ .read_iter = aufs_read_iter, ++ .write_iter = aufs_write_iter, ++ ++#ifdef CONFIG_AUFS_POLL ++ .poll = aufs_poll, ++#endif ++ .unlocked_ioctl = aufs_ioctl_nondir, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = aufs_compat_ioctl_nondir, ++#endif ++ .mmap = aufs_mmap, ++ .open = aufs_open_nondir, ++ .flush = aufs_flush_nondir, ++ .release = aufs_release_nondir, ++ .fsync = aufs_fsync_nondir, ++ /* .aio_fsync = aufs_aio_fsync_nondir, */ ++ .fasync = aufs_fasync, ++ /* .sendpage = aufs_sendpage, */ ++ .splice_write = aufs_splice_write, ++ .splice_read = aufs_splice_read, ++#if 0 ++ .aio_splice_write = aufs_aio_splice_write, ++ .aio_splice_read = aufs_aio_splice_read, ++#endif ++ .fallocate = aufs_fallocate ++}; +diff -Nur linux-4.0.4.orig/fs/aufs/fstype.h linux-4.0.4/fs/aufs/fstype.h +--- linux-4.0.4.orig/fs/aufs/fstype.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/fstype.h 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,387 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * judging filesystem type ++ */ ++ ++#ifndef __AUFS_FSTYPE_H__ ++#define __AUFS_FSTYPE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include ++ ++static inline int au_test_aufs(struct super_block *sb) ++{ ++ return sb->s_magic == AUFS_SUPER_MAGIC; ++} ++ ++static inline const char *au_sbtype(struct super_block *sb) ++{ ++ return sb->s_type->name; ++} ++ ++static inline int au_test_iso9660(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE) ++ return sb->s_magic == ISOFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_romfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE) ++ return sb->s_magic == ROMFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_cramfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) ++ return sb->s_magic == CRAMFS_MAGIC; ++#endif ++ return 0; ++} ++ ++static inline int au_test_nfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE) ++ return sb->s_magic == NFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_fuse(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE) ++ return sb->s_magic == FUSE_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_xfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE) ++ return sb->s_magic == XFS_SB_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_tmpfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_TMPFS ++ return sb->s_magic == TMPFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE) ++ return !strcmp(au_sbtype(sb), "ecryptfs"); ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ramfs(struct super_block *sb) ++{ ++ return sb->s_magic == RAMFS_MAGIC; ++} ++ ++static inline int au_test_ubifs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE) ++ return sb->s_magic == UBIFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_procfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_PROC_FS ++ return sb->s_magic == PROC_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_sysfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_SYSFS ++ return sb->s_magic == SYSFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_configfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE) ++ return sb->s_magic == CONFIGFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_minix(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE) ++ return sb->s_magic == MINIX3_SUPER_MAGIC ++ || sb->s_magic == MINIX2_SUPER_MAGIC ++ || sb->s_magic == MINIX2_SUPER_MAGIC2 ++ || sb->s_magic == MINIX_SUPER_MAGIC ++ || sb->s_magic == MINIX_SUPER_MAGIC2; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_fat(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE) ++ return sb->s_magic == MSDOS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_msdos(struct super_block *sb) ++{ ++ return au_test_fat(sb); ++} ++ ++static inline int au_test_vfat(struct super_block *sb) ++{ ++ return au_test_fat(sb); ++} ++ ++static inline int au_test_securityfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_SECURITYFS ++ return sb->s_magic == SECURITYFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_squashfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE) ++ return sb->s_magic == SQUASHFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_btrfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE) ++ return sb->s_magic == BTRFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_xenfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE) ++ return sb->s_magic == XENFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_debugfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_DEBUG_FS ++ return sb->s_magic == DEBUGFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_nilfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_NILFS) || defined(CONFIG_NILFS_MODULE) ++ return sb->s_magic == NILFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_hfsplus(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_HFSPLUS_FS) || defined(CONFIG_HFSPLUS_FS_MODULE) ++ return sb->s_magic == HFSPLUS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * they can't be an aufs branch. ++ */ ++static inline int au_test_fs_unsuppoted(struct super_block *sb) ++{ ++ return ++#ifndef CONFIG_AUFS_BR_RAMFS ++ au_test_ramfs(sb) || ++#endif ++ au_test_procfs(sb) ++ || au_test_sysfs(sb) ++ || au_test_configfs(sb) ++ || au_test_debugfs(sb) ++ || au_test_securityfs(sb) ++ || au_test_xenfs(sb) ++ || au_test_ecryptfs(sb) ++ /* || !strcmp(au_sbtype(sb), "unionfs") */ ++ || au_test_aufs(sb); /* will be supported in next version */ ++} ++ ++static inline int au_test_fs_remote(struct super_block *sb) ++{ ++ return !au_test_tmpfs(sb) ++#ifdef CONFIG_AUFS_BR_RAMFS ++ && !au_test_ramfs(sb) ++#endif ++ && !(sb->s_type->fs_flags & FS_REQUIRES_DEV); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * Note: these functions (below) are created after reading ->getattr() in all ++ * filesystems under linux/fs. it means we have to do so in every update... ++ */ ++ ++/* ++ * some filesystems require getattr to refresh the inode attributes before ++ * referencing. ++ * in most cases, we can rely on the inode attribute in NFS (or every remote fs) ++ * and leave the work for d_revalidate() ++ */ ++static inline int au_test_fs_refresh_iattr(struct super_block *sb) ++{ ++ return au_test_nfs(sb) ++ || au_test_fuse(sb) ++ /* || au_test_btrfs(sb) */ /* untested */ ++ ; ++} ++ ++/* ++ * filesystems which don't maintain i_size or i_blocks. ++ */ ++static inline int au_test_fs_bad_iattr_size(struct super_block *sb) ++{ ++ return au_test_xfs(sb) ++ || au_test_btrfs(sb) ++ || au_test_ubifs(sb) ++ || au_test_hfsplus(sb) /* maintained, but incorrect */ ++ /* || au_test_minix(sb) */ /* untested */ ++ ; ++} ++ ++/* ++ * filesystems which don't store the correct value in some of their inode ++ * attributes. ++ */ ++static inline int au_test_fs_bad_iattr(struct super_block *sb) ++{ ++ return au_test_fs_bad_iattr_size(sb) ++ || au_test_fat(sb) ++ || au_test_msdos(sb) ++ || au_test_vfat(sb); ++} ++ ++/* they don't check i_nlink in link(2) */ ++static inline int au_test_fs_no_limit_nlink(struct super_block *sb) ++{ ++ return au_test_tmpfs(sb) ++#ifdef CONFIG_AUFS_BR_RAMFS ++ || au_test_ramfs(sb) ++#endif ++ || au_test_ubifs(sb) ++ || au_test_hfsplus(sb); ++} ++ ++/* ++ * filesystems which sets S_NOATIME and S_NOCMTIME. ++ */ ++static inline int au_test_fs_notime(struct super_block *sb) ++{ ++ return au_test_nfs(sb) ++ || au_test_fuse(sb) ++ || au_test_ubifs(sb) ++ ; ++} ++ ++/* temporary support for i#1 in cramfs */ ++static inline int au_test_fs_unique_ino(struct inode *inode) ++{ ++ if (au_test_cramfs(inode->i_sb)) ++ return inode->i_ino != 1; ++ return 1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * the filesystem where the xino files placed must support i/o after unlink and ++ * maintain i_size and i_blocks. ++ */ ++static inline int au_test_fs_bad_xino(struct super_block *sb) ++{ ++ return au_test_fs_remote(sb) ++ || au_test_fs_bad_iattr_size(sb) ++ /* don't want unnecessary work for xino */ ++ || au_test_aufs(sb) ++ || au_test_ecryptfs(sb) ++ || au_test_nilfs(sb); ++} ++ ++static inline int au_test_fs_trunc_xino(struct super_block *sb) ++{ ++ return au_test_tmpfs(sb) ++ || au_test_ramfs(sb); ++} ++ ++/* ++ * test if the @sb is real-readonly. ++ */ ++static inline int au_test_fs_rr(struct super_block *sb) ++{ ++ return au_test_squashfs(sb) ++ || au_test_iso9660(sb) ++ || au_test_cramfs(sb) ++ || au_test_romfs(sb); ++} ++ ++/* ++ * test if the @inode is nfs with 'noacl' option ++ * NFS always sets MS_POSIXACL regardless its mount option 'noacl.' ++ */ ++static inline int au_test_nfs_noacl(struct inode *inode) ++{ ++ return au_test_nfs(inode->i_sb) ++ /* && IS_POSIXACL(inode) */ ++ && !nfs_server_capable(inode, NFS_CAP_ACLS); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_FSTYPE_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/hfsnotify.c linux-4.0.4/fs/aufs/hfsnotify.c +--- linux-4.0.4.orig/fs/aufs/hfsnotify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/hfsnotify.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,275 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * fsnotify for the lower directories ++ */ ++ ++#include "aufs.h" ++ ++/* FS_IN_IGNORED is unnecessary */ ++static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE ++ | FS_CREATE | FS_EVENT_ON_CHILD); ++static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq); ++static __cacheline_aligned_in_smp atomic64_t au_hfsn_ifree = ATOMIC64_INIT(0); ++ ++static void au_hfsn_free_mark(struct fsnotify_mark *mark) ++{ ++ struct au_hnotify *hn = container_of(mark, struct au_hnotify, ++ hn_mark); ++ AuDbg("here\n"); ++ au_cache_free_hnotify(hn); ++ smp_mb__before_atomic(); ++ if (atomic64_dec_and_test(&au_hfsn_ifree)) ++ wake_up(&au_hfsn_wq); ++} ++ ++static int au_hfsn_alloc(struct au_hinode *hinode) ++{ ++ int err; ++ struct au_hnotify *hn; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct fsnotify_mark *mark; ++ aufs_bindex_t bindex; ++ ++ hn = hinode->hi_notify; ++ sb = hn->hn_aufs_inode->i_sb; ++ bindex = au_br_index(sb, hinode->hi_id); ++ br = au_sbr(sb, bindex); ++ AuDebugOn(!br->br_hfsn); ++ ++ mark = &hn->hn_mark; ++ fsnotify_init_mark(mark, au_hfsn_free_mark); ++ mark->mask = AuHfsnMask; ++ /* ++ * by udba rename or rmdir, aufs assign a new inode to the known ++ * h_inode, so specify 1 to allow dups. ++ */ ++ lockdep_off(); ++ err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode, ++ /*mnt*/NULL, /*allow_dups*/1); ++ /* even if err */ ++ fsnotify_put_mark(mark); ++ lockdep_on(); ++ ++ return err; ++} ++ ++static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn) ++{ ++ struct fsnotify_mark *mark; ++ unsigned long long ull; ++ struct fsnotify_group *group; ++ ++ ull = atomic64_inc_return(&au_hfsn_ifree); ++ BUG_ON(!ull); ++ ++ mark = &hn->hn_mark; ++ spin_lock(&mark->lock); ++ group = mark->group; ++ fsnotify_get_group(group); ++ spin_unlock(&mark->lock); ++ lockdep_off(); ++ fsnotify_destroy_mark(mark, group); ++ fsnotify_put_group(group); ++ lockdep_on(); ++ ++ /* free hn by myself */ ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) ++{ ++ struct fsnotify_mark *mark; ++ ++ mark = &hinode->hi_notify->hn_mark; ++ spin_lock(&mark->lock); ++ if (do_set) { ++ AuDebugOn(mark->mask & AuHfsnMask); ++ mark->mask |= AuHfsnMask; ++ } else { ++ AuDebugOn(!(mark->mask & AuHfsnMask)); ++ mark->mask &= ~AuHfsnMask; ++ } ++ spin_unlock(&mark->lock); ++ /* fsnotify_recalc_inode_mask(hinode->hi_inode); */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* #define AuDbgHnotify */ ++#ifdef AuDbgHnotify ++static char *au_hfsn_name(u32 mask) ++{ ++#ifdef CONFIG_AUFS_DEBUG ++#define test_ret(flag) \ ++ do { \ ++ if (mask & flag) \ ++ return #flag; \ ++ } while (0) ++ test_ret(FS_ACCESS); ++ test_ret(FS_MODIFY); ++ test_ret(FS_ATTRIB); ++ test_ret(FS_CLOSE_WRITE); ++ test_ret(FS_CLOSE_NOWRITE); ++ test_ret(FS_OPEN); ++ test_ret(FS_MOVED_FROM); ++ test_ret(FS_MOVED_TO); ++ test_ret(FS_CREATE); ++ test_ret(FS_DELETE); ++ test_ret(FS_DELETE_SELF); ++ test_ret(FS_MOVE_SELF); ++ test_ret(FS_UNMOUNT); ++ test_ret(FS_Q_OVERFLOW); ++ test_ret(FS_IN_IGNORED); ++ test_ret(FS_ISDIR); ++ test_ret(FS_IN_ONESHOT); ++ test_ret(FS_EVENT_ON_CHILD); ++ return ""; ++#undef test_ret ++#else ++ return "??"; ++#endif ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hfsn_free_group(struct fsnotify_group *group) ++{ ++ struct au_br_hfsnotify *hfsn = group->private; ++ ++ AuDbg("here\n"); ++ kfree(hfsn); ++} ++ ++static int au_hfsn_handle_event(struct fsnotify_group *group, ++ struct inode *inode, ++ struct fsnotify_mark *inode_mark, ++ struct fsnotify_mark *vfsmount_mark, ++ u32 mask, void *data, int data_type, ++ const unsigned char *file_name, u32 cookie) ++{ ++ int err; ++ struct au_hnotify *hnotify; ++ struct inode *h_dir, *h_inode; ++ struct qstr h_child_qstr = QSTR_INIT(file_name, strlen(file_name)); ++ ++ AuDebugOn(data_type != FSNOTIFY_EVENT_INODE); ++ ++ err = 0; ++ /* if FS_UNMOUNT happens, there must be another bug */ ++ AuDebugOn(mask & FS_UNMOUNT); ++ if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) ++ goto out; ++ ++ h_dir = inode; ++ h_inode = NULL; ++#ifdef AuDbgHnotify ++ au_debug_on(); ++ if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 ++ || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { ++ AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", ++ h_dir->i_ino, mask, au_hfsn_name(mask), ++ AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); ++ /* WARN_ON(1); */ ++ } ++ au_debug_off(); ++#endif ++ ++ AuDebugOn(!inode_mark); ++ hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); ++ err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode); ++ ++out: ++ return err; ++} ++ ++static struct fsnotify_ops au_hfsn_ops = { ++ .handle_event = au_hfsn_handle_event, ++ .free_group_priv = au_hfsn_free_group ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hfsn_fin_br(struct au_branch *br) ++{ ++ struct au_br_hfsnotify *hfsn; ++ ++ hfsn = br->br_hfsn; ++ if (hfsn) { ++ lockdep_off(); ++ fsnotify_put_group(hfsn->hfsn_group); ++ lockdep_on(); ++ } ++} ++ ++static int au_hfsn_init_br(struct au_branch *br, int perm) ++{ ++ int err; ++ struct fsnotify_group *group; ++ struct au_br_hfsnotify *hfsn; ++ ++ err = 0; ++ br->br_hfsn = NULL; ++ if (!au_br_hnotifyable(perm)) ++ goto out; ++ ++ err = -ENOMEM; ++ hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS); ++ if (unlikely(!hfsn)) ++ goto out; ++ ++ err = 0; ++ group = fsnotify_alloc_group(&au_hfsn_ops); ++ if (IS_ERR(group)) { ++ err = PTR_ERR(group); ++ pr_err("fsnotify_alloc_group() failed, %d\n", err); ++ goto out_hfsn; ++ } ++ ++ group->private = hfsn; ++ hfsn->hfsn_group = group; ++ br->br_hfsn = hfsn; ++ goto out; /* success */ ++ ++out_hfsn: ++ kfree(hfsn); ++out: ++ return err; ++} ++ ++static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) ++{ ++ int err; ++ ++ err = 0; ++ if (!br->br_hfsn) ++ err = au_hfsn_init_br(br, perm); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hfsn_fin(void) ++{ ++ AuDbg("au_hfsn_ifree %lld\n", (long long)atomic64_read(&au_hfsn_ifree)); ++ wait_event(au_hfsn_wq, !atomic64_read(&au_hfsn_ifree)); ++} ++ ++const struct au_hnotify_op au_hnotify_op = { ++ .ctl = au_hfsn_ctl, ++ .alloc = au_hfsn_alloc, ++ .free = au_hfsn_free, ++ ++ .fin = au_hfsn_fin, ++ ++ .reset_br = au_hfsn_reset_br, ++ .fin_br = au_hfsn_fin_br, ++ .init_br = au_hfsn_init_br ++}; +diff -Nur linux-4.0.4.orig/fs/aufs/hfsplus.c linux-4.0.4/fs/aufs/hfsplus.c +--- linux-4.0.4.orig/fs/aufs/hfsplus.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/hfsplus.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (C) 2010-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * special support for filesystems which aqucires an inode mutex ++ * at final closing a file, eg, hfsplus. ++ * ++ * This trick is very simple and stupid, just to open the file before really ++ * neceeary open to tell hfsplus that this is not the final closing. ++ * The caller should call au_h_open_pre() after acquiring the inode mutex, ++ * and au_h_open_post() after releasing it. ++ */ ++ ++#include "aufs.h" ++ ++struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex, ++ int force_wr) ++{ ++ struct file *h_file; ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ AuDebugOn(!h_dentry); ++ AuDebugOn(!h_dentry->d_inode); ++ ++ h_file = NULL; ++ if (au_test_hfsplus(h_dentry->d_sb) ++ && d_is_reg(h_dentry)) ++ h_file = au_h_open(dentry, bindex, ++ O_RDONLY | O_NOATIME | O_LARGEFILE, ++ /*file*/NULL, force_wr); ++ return h_file; ++} ++ ++void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, ++ struct file *h_file) ++{ ++ if (h_file) { ++ fput(h_file); ++ au_sbr_put(dentry->d_sb, bindex); ++ } ++} +diff -Nur linux-4.0.4.orig/fs/aufs/hnotify.c linux-4.0.4/fs/aufs/hnotify.c +--- linux-4.0.4.orig/fs/aufs/hnotify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/hnotify.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,701 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * abstraction to notify the direct changes on lower directories ++ */ ++ ++#include "aufs.h" ++ ++int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) ++{ ++ int err; ++ struct au_hnotify *hn; ++ ++ err = -ENOMEM; ++ hn = au_cache_alloc_hnotify(); ++ if (hn) { ++ hn->hn_aufs_inode = inode; ++ hinode->hi_notify = hn; ++ err = au_hnotify_op.alloc(hinode); ++ AuTraceErr(err); ++ if (unlikely(err)) { ++ hinode->hi_notify = NULL; ++ au_cache_free_hnotify(hn); ++ /* ++ * The upper dir was removed by udba, but the same named ++ * dir left. In this case, aufs assignes a new inode ++ * number and set the monitor again. ++ * For the lower dir, the old monitnor is still left. ++ */ ++ if (err == -EEXIST) ++ err = 0; ++ } ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++void au_hn_free(struct au_hinode *hinode) ++{ ++ struct au_hnotify *hn; ++ ++ hn = hinode->hi_notify; ++ if (hn) { ++ hinode->hi_notify = NULL; ++ if (au_hnotify_op.free(hinode, hn)) ++ au_cache_free_hnotify(hn); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_hn_ctl(struct au_hinode *hinode, int do_set) ++{ ++ if (hinode->hi_notify) ++ au_hnotify_op.ctl(hinode, do_set); ++} ++ ++void au_hn_reset(struct inode *inode, unsigned int flags) ++{ ++ aufs_bindex_t bindex, bend; ++ struct inode *hi; ++ struct dentry *iwhdentry; ++ ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ hi = au_h_iptr(inode, bindex); ++ if (!hi) ++ continue; ++ ++ /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */ ++ iwhdentry = au_hi_wh(inode, bindex); ++ if (iwhdentry) ++ dget(iwhdentry); ++ au_igrab(hi); ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ au_set_h_iptr(inode, bindex, au_igrab(hi), ++ flags & ~AuHi_XINO); ++ iput(hi); ++ dput(iwhdentry); ++ /* mutex_unlock(&hi->i_mutex); */ ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int hn_xino(struct inode *inode, struct inode *h_inode) ++{ ++ int err; ++ aufs_bindex_t bindex, bend, bfound, bstart; ++ struct inode *h_i; ++ ++ err = 0; ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ pr_warn("branch root dir was changed\n"); ++ goto out; ++ } ++ ++ bfound = -1; ++ bend = au_ibend(inode); ++ bstart = au_ibstart(inode); ++#if 0 /* reserved for future use */ ++ if (bindex == bend) { ++ /* keep this ino in rename case */ ++ goto out; ++ } ++#endif ++ for (bindex = bstart; bindex <= bend; bindex++) ++ if (au_h_iptr(inode, bindex) == h_inode) { ++ bfound = bindex; ++ break; ++ } ++ if (bfound < 0) ++ goto out; ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ h_i = au_h_iptr(inode, bindex); ++ if (!h_i) ++ continue; ++ ++ err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0); ++ /* ignore this error */ ++ /* bad action? */ ++ } ++ ++ /* children inode number will be broken */ ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int hn_gen_tree(struct dentry *dentry) ++{ ++ int err, i, j, ndentry; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, dentry, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) { ++ struct dentry *d; ++ ++ d = dentries[j]; ++ if (IS_ROOT(d)) ++ continue; ++ ++ au_digen_dec(d); ++ if (d->d_inode) ++ /* todo: reset children xino? ++ cached children only? */ ++ au_iigen_dec(d->d_inode); ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++ ++#if 0 ++ /* discard children */ ++ dentry_unhash(dentry); ++ dput(dentry); ++#endif ++out: ++ return err; ++} ++ ++/* ++ * return 0 if processed. ++ */ ++static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, ++ const unsigned int isdir) ++{ ++ int err; ++ struct dentry *d; ++ struct qstr *dname; ++ ++ err = 1; ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ pr_warn("branch root dir was changed\n"); ++ err = 0; ++ goto out; ++ } ++ ++ if (!isdir) { ++ AuDebugOn(!name); ++ au_iigen_dec(inode); ++ spin_lock(&inode->i_lock); ++ hlist_for_each_entry(d, &inode->i_dentry, d_u.d_alias) { ++ spin_lock(&d->d_lock); ++ dname = &d->d_name; ++ if (dname->len != nlen ++ && memcmp(dname->name, name, nlen)) { ++ spin_unlock(&d->d_lock); ++ continue; ++ } ++ err = 0; ++ au_digen_dec(d); ++ spin_unlock(&d->d_lock); ++ break; ++ } ++ spin_unlock(&inode->i_lock); ++ } else { ++ au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); ++ d = d_find_any_alias(inode); ++ if (!d) { ++ au_iigen_dec(inode); ++ goto out; ++ } ++ ++ spin_lock(&d->d_lock); ++ dname = &d->d_name; ++ if (dname->len == nlen && !memcmp(dname->name, name, nlen)) { ++ spin_unlock(&d->d_lock); ++ err = hn_gen_tree(d); ++ spin_lock(&d->d_lock); ++ } ++ spin_unlock(&d->d_lock); ++ dput(d); ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) ++{ ++ int err; ++ struct inode *inode; ++ ++ inode = dentry->d_inode; ++ if (IS_ROOT(dentry) ++ /* || (inode && inode->i_ino == AUFS_ROOT_INO) */ ++ ) { ++ pr_warn("branch root dir was changed\n"); ++ return 0; ++ } ++ ++ err = 0; ++ if (!isdir) { ++ au_digen_dec(dentry); ++ if (inode) ++ au_iigen_dec(inode); ++ } else { ++ au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); ++ if (inode) ++ err = hn_gen_tree(dentry); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* hnotify job flags */ ++#define AuHnJob_XINO0 1 ++#define AuHnJob_GEN (1 << 1) ++#define AuHnJob_DIRENT (1 << 2) ++#define AuHnJob_ISDIR (1 << 3) ++#define AuHnJob_TRYXINO0 (1 << 4) ++#define AuHnJob_MNTPNT (1 << 5) ++#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name) ++#define au_fset_hnjob(flags, name) \ ++ do { (flags) |= AuHnJob_##name; } while (0) ++#define au_fclr_hnjob(flags, name) \ ++ do { (flags) &= ~AuHnJob_##name; } while (0) ++ ++enum { ++ AuHn_CHILD, ++ AuHn_PARENT, ++ AuHnLast ++}; ++ ++struct au_hnotify_args { ++ struct inode *h_dir, *dir, *h_child_inode; ++ u32 mask; ++ unsigned int flags[AuHnLast]; ++ unsigned int h_child_nlen; ++ char h_child_name[]; ++}; ++ ++struct hn_job_args { ++ unsigned int flags; ++ struct inode *inode, *h_inode, *dir, *h_dir; ++ struct dentry *dentry; ++ char *h_name; ++ int h_nlen; ++}; ++ ++static int hn_job(struct hn_job_args *a) ++{ ++ const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR); ++ int e; ++ ++ /* reset xino */ ++ if (au_ftest_hnjob(a->flags, XINO0) && a->inode) ++ hn_xino(a->inode, a->h_inode); /* ignore this error */ ++ ++ if (au_ftest_hnjob(a->flags, TRYXINO0) ++ && a->inode ++ && a->h_inode) { ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ if (!a->h_inode->i_nlink ++ && !(a->h_inode->i_state & I_LINKABLE)) ++ hn_xino(a->inode, a->h_inode); /* ignore this error */ ++ mutex_unlock(&a->h_inode->i_mutex); ++ } ++ ++ /* make the generation obsolete */ ++ if (au_ftest_hnjob(a->flags, GEN)) { ++ e = -1; ++ if (a->inode) ++ e = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode, ++ isdir); ++ if (e && a->dentry) ++ hn_gen_by_name(a->dentry, isdir); ++ /* ignore this error */ ++ } ++ ++ /* make dir entries obsolete */ ++ if (au_ftest_hnjob(a->flags, DIRENT) && a->inode) { ++ struct au_vdir *vdir; ++ ++ vdir = au_ivdir(a->inode); ++ if (vdir) ++ vdir->vd_jiffy = 0; ++ /* IMustLock(a->inode); */ ++ /* a->inode->i_version++; */ ++ } ++ ++ /* can do nothing but warn */ ++ if (au_ftest_hnjob(a->flags, MNTPNT) ++ && a->dentry ++ && d_mountpoint(a->dentry)) ++ pr_warn("mount-point %pd is removed or renamed\n", a->dentry); ++ ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, ++ struct inode *dir) ++{ ++ struct dentry *dentry, *d, *parent; ++ struct qstr *dname; ++ ++ parent = d_find_any_alias(dir); ++ if (!parent) ++ return NULL; ++ ++ dentry = NULL; ++ spin_lock(&parent->d_lock); ++ list_for_each_entry(d, &parent->d_subdirs, d_child) { ++ /* AuDbg("%pd\n", d); */ ++ spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED); ++ dname = &d->d_name; ++ if (dname->len != nlen || memcmp(dname->name, name, nlen)) ++ goto cont_unlock; ++ if (au_di(d)) ++ au_digen_dec(d); ++ else ++ goto cont_unlock; ++ if (au_dcount(d) > 0) { ++ dentry = dget_dlock(d); ++ spin_unlock(&d->d_lock); ++ break; ++ } ++ ++cont_unlock: ++ spin_unlock(&d->d_lock); ++ } ++ spin_unlock(&parent->d_lock); ++ dput(parent); ++ ++ if (dentry) ++ di_write_lock_child(dentry); ++ ++ return dentry; ++} ++ ++static struct inode *lookup_wlock_by_ino(struct super_block *sb, ++ aufs_bindex_t bindex, ino_t h_ino) ++{ ++ struct inode *inode; ++ ino_t ino; ++ int err; ++ ++ inode = NULL; ++ err = au_xino_read(sb, bindex, h_ino, &ino); ++ if (!err && ino) ++ inode = ilookup(sb, ino); ++ if (!inode) ++ goto out; ++ ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ pr_warn("wrong root branch\n"); ++ iput(inode); ++ inode = NULL; ++ goto out; ++ } ++ ++ ii_write_lock_child(inode); ++ ++out: ++ return inode; ++} ++ ++static void au_hn_bh(void *_args) ++{ ++ struct au_hnotify_args *a = _args; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend, bfound; ++ unsigned char xino, try_iput; ++ int err; ++ struct inode *inode; ++ ino_t h_ino; ++ struct hn_job_args args; ++ struct dentry *dentry; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(!_args); ++ AuDebugOn(!a->h_dir); ++ AuDebugOn(!a->dir); ++ AuDebugOn(!a->mask); ++ AuDbg("mask 0x%x, i%lu, hi%lu, hci%lu\n", ++ a->mask, a->dir->i_ino, a->h_dir->i_ino, ++ a->h_child_inode ? a->h_child_inode->i_ino : 0); ++ ++ inode = NULL; ++ dentry = NULL; ++ /* ++ * do not lock a->dir->i_mutex here ++ * because of d_revalidate() may cause a deadlock. ++ */ ++ sb = a->dir->i_sb; ++ AuDebugOn(!sb); ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!sbinfo); ++ si_write_lock(sb, AuLock_NOPLMW); ++ ++ ii_read_lock_parent(a->dir); ++ bfound = -1; ++ bend = au_ibend(a->dir); ++ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) ++ if (au_h_iptr(a->dir, bindex) == a->h_dir) { ++ bfound = bindex; ++ break; ++ } ++ ii_read_unlock(a->dir); ++ if (unlikely(bfound < 0)) ++ goto out; ++ ++ xino = !!au_opt_test(au_mntflags(sb), XINO); ++ h_ino = 0; ++ if (a->h_child_inode) ++ h_ino = a->h_child_inode->i_ino; ++ ++ if (a->h_child_nlen ++ && (au_ftest_hnjob(a->flags[AuHn_CHILD], GEN) ++ || au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT))) ++ dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, ++ a->dir); ++ try_iput = 0; ++ if (dentry) ++ inode = dentry->d_inode; ++ if (xino && !inode && h_ino ++ && (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0) ++ || au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0) ++ || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { ++ inode = lookup_wlock_by_ino(sb, bfound, h_ino); ++ try_iput = 1; ++ } ++ ++ args.flags = a->flags[AuHn_CHILD]; ++ args.dentry = dentry; ++ args.inode = inode; ++ args.h_inode = a->h_child_inode; ++ args.dir = a->dir; ++ args.h_dir = a->h_dir; ++ args.h_name = a->h_child_name; ++ args.h_nlen = a->h_child_nlen; ++ err = hn_job(&args); ++ if (dentry) { ++ if (au_di(dentry)) ++ di_write_unlock(dentry); ++ dput(dentry); ++ } ++ if (inode && try_iput) { ++ ii_write_unlock(inode); ++ iput(inode); ++ } ++ ++ ii_write_lock_parent(a->dir); ++ args.flags = a->flags[AuHn_PARENT]; ++ args.dentry = NULL; ++ args.inode = a->dir; ++ args.h_inode = a->h_dir; ++ args.dir = NULL; ++ args.h_dir = NULL; ++ args.h_name = NULL; ++ args.h_nlen = 0; ++ err = hn_job(&args); ++ ii_write_unlock(a->dir); ++ ++out: ++ iput(a->h_child_inode); ++ iput(a->h_dir); ++ iput(a->dir); ++ si_write_unlock(sb); ++ au_nwt_done(&sbinfo->si_nowait); ++ kfree(a); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, ++ struct qstr *h_child_qstr, struct inode *h_child_inode) ++{ ++ int err, len; ++ unsigned int flags[AuHnLast], f; ++ unsigned char isdir, isroot, wh; ++ struct inode *dir; ++ struct au_hnotify_args *args; ++ char *p, *h_child_name; ++ ++ err = 0; ++ AuDebugOn(!hnotify || !hnotify->hn_aufs_inode); ++ dir = igrab(hnotify->hn_aufs_inode); ++ if (!dir) ++ goto out; ++ ++ isroot = (dir->i_ino == AUFS_ROOT_INO); ++ wh = 0; ++ h_child_name = (void *)h_child_qstr->name; ++ len = h_child_qstr->len; ++ if (h_child_name) { ++ if (len > AUFS_WH_PFX_LEN ++ && !memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ h_child_name += AUFS_WH_PFX_LEN; ++ len -= AUFS_WH_PFX_LEN; ++ wh = 1; ++ } ++ } ++ ++ isdir = 0; ++ if (h_child_inode) ++ isdir = !!S_ISDIR(h_child_inode->i_mode); ++ flags[AuHn_PARENT] = AuHnJob_ISDIR; ++ flags[AuHn_CHILD] = 0; ++ if (isdir) ++ flags[AuHn_CHILD] = AuHnJob_ISDIR; ++ au_fset_hnjob(flags[AuHn_PARENT], DIRENT); ++ au_fset_hnjob(flags[AuHn_CHILD], GEN); ++ switch (mask & FS_EVENTS_POSS_ON_CHILD) { ++ case FS_MOVED_FROM: ++ case FS_MOVED_TO: ++ au_fset_hnjob(flags[AuHn_CHILD], XINO0); ++ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); ++ /*FALLTHROUGH*/ ++ case FS_CREATE: ++ AuDebugOn(!h_child_name); ++ break; ++ ++ case FS_DELETE: ++ /* ++ * aufs never be able to get this child inode. ++ * revalidation should be in d_revalidate() ++ * by checking i_nlink, i_generation or d_unhashed(). ++ */ ++ AuDebugOn(!h_child_name); ++ au_fset_hnjob(flags[AuHn_CHILD], TRYXINO0); ++ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); ++ break; ++ ++ default: ++ AuDebugOn(1); ++ } ++ ++ if (wh) ++ h_child_inode = NULL; ++ ++ err = -ENOMEM; ++ /* iput() and kfree() will be called in au_hnotify() */ ++ args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); ++ if (unlikely(!args)) { ++ AuErr1("no memory\n"); ++ iput(dir); ++ goto out; ++ } ++ args->flags[AuHn_PARENT] = flags[AuHn_PARENT]; ++ args->flags[AuHn_CHILD] = flags[AuHn_CHILD]; ++ args->mask = mask; ++ args->dir = dir; ++ args->h_dir = igrab(h_dir); ++ if (h_child_inode) ++ h_child_inode = igrab(h_child_inode); /* can be NULL */ ++ args->h_child_inode = h_child_inode; ++ args->h_child_nlen = len; ++ if (len) { ++ p = (void *)args; ++ p += sizeof(*args); ++ memcpy(p, h_child_name, len); ++ p[len] = 0; ++ } ++ ++ /* NFS fires the event for silly-renamed one from kworker */ ++ f = 0; ++ if (!dir->i_nlink ++ || (au_test_nfs(h_dir->i_sb) && (mask & FS_DELETE))) ++ f = AuWkq_NEST; ++ err = au_wkq_nowait(au_hn_bh, args, dir->i_sb, f); ++ if (unlikely(err)) { ++ pr_err("wkq %d\n", err); ++ iput(args->h_child_inode); ++ iput(args->h_dir); ++ iput(args->dir); ++ kfree(args); ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) ++{ ++ int err; ++ ++ AuDebugOn(!(udba & AuOptMask_UDBA)); ++ ++ err = 0; ++ if (au_hnotify_op.reset_br) ++ err = au_hnotify_op.reset_br(udba, br, perm); ++ ++ return err; ++} ++ ++int au_hnotify_init_br(struct au_branch *br, int perm) ++{ ++ int err; ++ ++ err = 0; ++ if (au_hnotify_op.init_br) ++ err = au_hnotify_op.init_br(br, perm); ++ ++ return err; ++} ++ ++void au_hnotify_fin_br(struct au_branch *br) ++{ ++ if (au_hnotify_op.fin_br) ++ au_hnotify_op.fin_br(br); ++} ++ ++static void au_hn_destroy_cache(void) ++{ ++ kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]); ++ au_cachep[AuCache_HNOTIFY] = NULL; ++} ++ ++int __init au_hnotify_init(void) ++{ ++ int err; ++ ++ err = -ENOMEM; ++ au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify); ++ if (au_cachep[AuCache_HNOTIFY]) { ++ err = 0; ++ if (au_hnotify_op.init) ++ err = au_hnotify_op.init(); ++ if (unlikely(err)) ++ au_hn_destroy_cache(); ++ } ++ AuTraceErr(err); ++ return err; ++} ++ ++void au_hnotify_fin(void) ++{ ++ if (au_hnotify_op.fin) ++ au_hnotify_op.fin(); ++ /* cf. au_cache_fin() */ ++ if (au_cachep[AuCache_HNOTIFY]) ++ au_hn_destroy_cache(); ++} +diff -Nur linux-4.0.4.orig/fs/aufs/iinfo.c linux-4.0.4/fs/aufs/iinfo.c +--- linux-4.0.4.orig/fs/aufs/iinfo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/iinfo.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,264 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode private data ++ */ ++ ++#include "aufs.h" ++ ++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) ++{ ++ struct inode *h_inode; ++ ++ IiMustAnyLock(inode); ++ ++ h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; ++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); ++ return h_inode; ++} ++ ++/* todo: hard/soft set? */ ++void au_hiput(struct au_hinode *hinode) ++{ ++ au_hn_free(hinode); ++ dput(hinode->hi_whdentry); ++ iput(hinode->hi_inode); ++} ++ ++unsigned int au_hi_flags(struct inode *inode, int isdir) ++{ ++ unsigned int flags; ++ const unsigned int mnt_flags = au_mntflags(inode->i_sb); ++ ++ flags = 0; ++ if (au_opt_test(mnt_flags, XINO)) ++ au_fset_hi(flags, XINO); ++ if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY)) ++ au_fset_hi(flags, HNOTIFY); ++ return flags; ++} ++ ++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode, unsigned int flags) ++{ ++ struct au_hinode *hinode; ++ struct inode *hi; ++ struct au_iinfo *iinfo = au_ii(inode); ++ ++ IiMustWriteLock(inode); ++ ++ hinode = iinfo->ii_hinode + bindex; ++ hi = hinode->hi_inode; ++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); ++ ++ if (hi) ++ au_hiput(hinode); ++ hinode->hi_inode = h_inode; ++ if (h_inode) { ++ int err; ++ struct super_block *sb = inode->i_sb; ++ struct au_branch *br; ++ ++ AuDebugOn(inode->i_mode ++ && (h_inode->i_mode & S_IFMT) ++ != (inode->i_mode & S_IFMT)); ++ if (bindex == iinfo->ii_bstart) ++ au_cpup_igen(inode, h_inode); ++ br = au_sbr(sb, bindex); ++ hinode->hi_id = br->br_id; ++ if (au_ftest_hi(flags, XINO)) { ++ err = au_xino_write(sb, bindex, h_inode->i_ino, ++ inode->i_ino); ++ if (unlikely(err)) ++ AuIOErr1("failed au_xino_write() %d\n", err); ++ } ++ ++ if (au_ftest_hi(flags, HNOTIFY) ++ && au_br_hnotifyable(br->br_perm)) { ++ err = au_hn_alloc(hinode, inode); ++ if (unlikely(err)) ++ AuIOErr1("au_hn_alloc() %d\n", err); ++ } ++ } ++} ++ ++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_wh) ++{ ++ struct au_hinode *hinode; ++ ++ IiMustWriteLock(inode); ++ ++ hinode = au_ii(inode)->ii_hinode + bindex; ++ AuDebugOn(hinode->hi_whdentry); ++ hinode->hi_whdentry = h_wh; ++} ++ ++void au_update_iigen(struct inode *inode, int half) ++{ ++ struct au_iinfo *iinfo; ++ struct au_iigen *iigen; ++ unsigned int sigen; ++ ++ sigen = au_sigen(inode->i_sb); ++ iinfo = au_ii(inode); ++ iigen = &iinfo->ii_generation; ++ spin_lock(&iinfo->ii_genspin); ++ iigen->ig_generation = sigen; ++ if (half) ++ au_ig_fset(iigen->ig_flags, HALF_REFRESHED); ++ else ++ au_ig_fclr(iigen->ig_flags, HALF_REFRESHED); ++ spin_unlock(&iinfo->ii_genspin); ++} ++ ++/* it may be called at remount time, too */ ++void au_update_ibrange(struct inode *inode, int do_put_zero) ++{ ++ struct au_iinfo *iinfo; ++ aufs_bindex_t bindex, bend; ++ ++ iinfo = au_ii(inode); ++ if (!iinfo) ++ return; ++ ++ IiMustWriteLock(inode); ++ ++ if (do_put_zero && iinfo->ii_bstart >= 0) { ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ bindex++) { ++ struct inode *h_i; ++ ++ h_i = iinfo->ii_hinode[0 + bindex].hi_inode; ++ if (h_i ++ && !h_i->i_nlink ++ && !(h_i->i_state & I_LINKABLE)) ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ } ++ } ++ ++ iinfo->ii_bstart = -1; ++ iinfo->ii_bend = -1; ++ bend = au_sbend(inode->i_sb); ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (iinfo->ii_hinode[0 + bindex].hi_inode) { ++ iinfo->ii_bstart = bindex; ++ break; ++ } ++ if (iinfo->ii_bstart >= 0) ++ for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--) ++ if (iinfo->ii_hinode[0 + bindex].hi_inode) { ++ iinfo->ii_bend = bindex; ++ break; ++ } ++ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_icntnr_init_once(void *_c) ++{ ++ struct au_icntnr *c = _c; ++ struct au_iinfo *iinfo = &c->iinfo; ++ static struct lock_class_key aufs_ii; ++ ++ spin_lock_init(&iinfo->ii_genspin); ++ au_rw_init(&iinfo->ii_rwsem); ++ au_rw_class(&iinfo->ii_rwsem, &aufs_ii); ++ inode_init_once(&c->vfs_inode); ++} ++ ++int au_iinfo_init(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ struct super_block *sb; ++ int nbr, i; ++ ++ sb = inode->i_sb; ++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); ++ nbr = au_sbend(sb) + 1; ++ if (unlikely(nbr <= 0)) ++ nbr = 1; ++ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); ++ if (iinfo->ii_hinode) { ++ au_ninodes_inc(sb); ++ for (i = 0; i < nbr; i++) ++ iinfo->ii_hinode[i].hi_id = -1; ++ ++ iinfo->ii_generation.ig_generation = au_sigen(sb); ++ iinfo->ii_bstart = -1; ++ iinfo->ii_bend = -1; ++ iinfo->ii_vdir = NULL; ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++int au_ii_realloc(struct au_iinfo *iinfo, int nbr) ++{ ++ int err, sz; ++ struct au_hinode *hip; ++ ++ AuRwMustWriteLock(&iinfo->ii_rwsem); ++ ++ err = -ENOMEM; ++ sz = sizeof(*hip) * (iinfo->ii_bend + 1); ++ if (!sz) ++ sz = sizeof(*hip); ++ hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS); ++ if (hip) { ++ iinfo->ii_hinode = hip; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++void au_iinfo_fin(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ struct au_hinode *hi; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend; ++ const unsigned char unlinked = !inode->i_nlink; ++ ++ iinfo = au_ii(inode); ++ /* bad_inode case */ ++ if (!iinfo) ++ return; ++ ++ sb = inode->i_sb; ++ au_ninodes_dec(sb); ++ if (si_pid_test(sb)) ++ au_xino_delete_inode(inode, unlinked); ++ else { ++ /* ++ * it is safe to hide the dependency between sbinfo and ++ * sb->s_umount. ++ */ ++ lockdep_off(); ++ si_noflush_read_lock(sb); ++ au_xino_delete_inode(inode, unlinked); ++ si_read_unlock(sb); ++ lockdep_on(); ++ } ++ ++ if (iinfo->ii_vdir) ++ au_vdir_free(iinfo->ii_vdir); ++ ++ bindex = iinfo->ii_bstart; ++ if (bindex >= 0) { ++ hi = iinfo->ii_hinode + bindex; ++ bend = iinfo->ii_bend; ++ while (bindex++ <= bend) { ++ if (hi->hi_inode) ++ au_hiput(hi); ++ hi++; ++ } ++ } ++ kfree(iinfo->ii_hinode); ++ iinfo->ii_hinode = NULL; ++ AuRwDestroy(&iinfo->ii_rwsem); ++} +diff -Nur linux-4.0.4.orig/fs/aufs/inode.c linux-4.0.4/fs/aufs/inode.c +--- linux-4.0.4.orig/fs/aufs/inode.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/inode.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,482 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode functions ++ */ ++ ++#include "aufs.h" ++ ++struct inode *au_igrab(struct inode *inode) ++{ ++ if (inode) { ++ AuDebugOn(!atomic_read(&inode->i_count)); ++ ihold(inode); ++ } ++ return inode; ++} ++ ++static void au_refresh_hinode_attr(struct inode *inode, int do_version) ++{ ++ au_cpup_attr_all(inode, /*force*/0); ++ au_update_iigen(inode, /*half*/1); ++ if (do_version) ++ inode->i_version++; ++} ++ ++static int au_ii_refresh(struct inode *inode, int *update) ++{ ++ int err, e; ++ umode_t type; ++ aufs_bindex_t bindex, new_bindex; ++ struct super_block *sb; ++ struct au_iinfo *iinfo; ++ struct au_hinode *p, *q, tmp; ++ ++ IiMustWriteLock(inode); ++ ++ *update = 0; ++ sb = inode->i_sb; ++ type = inode->i_mode & S_IFMT; ++ iinfo = au_ii(inode); ++ err = au_ii_realloc(iinfo, au_sbend(sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ ++ AuDebugOn(iinfo->ii_bstart < 0); ++ p = iinfo->ii_hinode + iinfo->ii_bstart; ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ bindex++, p++) { ++ if (!p->hi_inode) ++ continue; ++ ++ AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT)); ++ new_bindex = au_br_index(sb, p->hi_id); ++ if (new_bindex == bindex) ++ continue; ++ ++ if (new_bindex < 0) { ++ *update = 1; ++ au_hiput(p); ++ p->hi_inode = NULL; ++ continue; ++ } ++ ++ if (new_bindex < iinfo->ii_bstart) ++ iinfo->ii_bstart = new_bindex; ++ if (iinfo->ii_bend < new_bindex) ++ iinfo->ii_bend = new_bindex; ++ /* swap two lower inode, and loop again */ ++ q = iinfo->ii_hinode + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hi_inode) { ++ bindex--; ++ p--; ++ } ++ } ++ au_update_ibrange(inode, /*do_put_zero*/0); ++ e = au_dy_irefresh(inode); ++ if (unlikely(e && !err)) ++ err = e; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_refresh_hinode_self(struct inode *inode) ++{ ++ int err, update; ++ ++ err = au_ii_refresh(inode, &update); ++ if (!err) ++ au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_refresh_hinode(struct inode *inode, struct dentry *dentry) ++{ ++ int err, e, update; ++ unsigned int flags; ++ umode_t mode; ++ aufs_bindex_t bindex, bend; ++ unsigned char isdir; ++ struct au_hinode *p; ++ struct au_iinfo *iinfo; ++ ++ err = au_ii_refresh(inode, &update); ++ if (unlikely(err)) ++ goto out; ++ ++ update = 0; ++ iinfo = au_ii(inode); ++ p = iinfo->ii_hinode + iinfo->ii_bstart; ++ mode = (inode->i_mode & S_IFMT); ++ isdir = S_ISDIR(mode); ++ flags = au_hi_flags(inode, isdir); ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ struct inode *h_i; ++ struct dentry *h_d; ++ ++ h_d = au_h_dptr(dentry, bindex); ++ if (!h_d || !h_d->d_inode) ++ continue; ++ ++ AuDebugOn(mode != (h_d->d_inode->i_mode & S_IFMT)); ++ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { ++ h_i = au_h_iptr(inode, bindex); ++ if (h_i) { ++ if (h_i == h_d->d_inode) ++ continue; ++ err = -EIO; ++ break; ++ } ++ } ++ if (bindex < iinfo->ii_bstart) ++ iinfo->ii_bstart = bindex; ++ if (iinfo->ii_bend < bindex) ++ iinfo->ii_bend = bindex; ++ au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags); ++ update = 1; ++ } ++ au_update_ibrange(inode, /*do_put_zero*/0); ++ e = au_dy_irefresh(inode); ++ if (unlikely(e && !err)) ++ err = e; ++ if (!err) ++ au_refresh_hinode_attr(inode, update && isdir); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int set_inode(struct inode *inode, struct dentry *dentry) ++{ ++ int err; ++ unsigned int flags; ++ umode_t mode; ++ aufs_bindex_t bindex, bstart, btail; ++ unsigned char isdir; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct au_iinfo *iinfo; ++ ++ IiMustWriteLock(inode); ++ ++ err = 0; ++ isdir = 0; ++ bstart = au_dbstart(dentry); ++ h_inode = au_h_dptr(dentry, bstart)->d_inode; ++ mode = h_inode->i_mode; ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_iop; ++ inode->i_fop = &aufs_file_fop; ++ err = au_dy_iaop(inode, bstart, h_inode); ++ if (unlikely(err)) ++ goto out; ++ break; ++ case S_IFDIR: ++ isdir = 1; ++ btail = au_dbtaildir(dentry); ++ inode->i_op = &aufs_dir_iop; ++ inode->i_fop = &aufs_dir_fop; ++ break; ++ case S_IFLNK: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_symlink_iop; ++ break; ++ case S_IFBLK: ++ case S_IFCHR: ++ case S_IFIFO: ++ case S_IFSOCK: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_iop; ++ init_special_inode(inode, mode, h_inode->i_rdev); ++ break; ++ default: ++ AuIOErr("Unknown file type 0%o\n", mode); ++ err = -EIO; ++ goto out; ++ } ++ ++ /* do not set hnotify for whiteouted dirs (SHWH mode) */ ++ flags = au_hi_flags(inode, isdir); ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) ++ && au_ftest_hi(flags, HNOTIFY) ++ && dentry->d_name.len > AUFS_WH_PFX_LEN ++ && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) ++ au_fclr_hi(flags, HNOTIFY); ++ iinfo = au_ii(inode); ++ iinfo->ii_bstart = bstart; ++ iinfo->ii_bend = btail; ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry) ++ au_set_h_iptr(inode, bindex, ++ au_igrab(h_dentry->d_inode), flags); ++ } ++ au_cpup_attr_all(inode, /*force*/1); ++ /* ++ * to force calling aufs_get_acl() every time, ++ * do not call cache_no_acl() for aufs inode. ++ */ ++ ++out: ++ return err; ++} ++ ++/* ++ * successful returns with iinfo write_locked ++ * minus: errno ++ * zero: success, matched ++ * plus: no error, but unmatched ++ */ ++static int reval_inode(struct inode *inode, struct dentry *dentry) ++{ ++ int err; ++ unsigned int gen; ++ struct au_iigen iigen; ++ aufs_bindex_t bindex, bend; ++ struct inode *h_inode, *h_dinode; ++ ++ /* ++ * before this function, if aufs got any iinfo lock, it must be only ++ * one, the parent dir. ++ * it can happen by UDBA and the obsoleted inode number. ++ */ ++ err = -EIO; ++ if (unlikely(inode->i_ino == parent_ino(dentry))) ++ goto out; ++ ++ err = 1; ++ ii_write_lock_new_child(inode); ++ h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (!h_inode || h_inode != h_dinode) ++ continue; ++ ++ err = 0; ++ gen = au_iigen(inode, &iigen); ++ if (gen == au_digen(dentry) ++ && !au_ig_ftest(iigen.ig_flags, HALF_REFRESHED)) ++ break; ++ ++ /* fully refresh inode using dentry */ ++ err = au_refresh_hinode(inode, dentry); ++ if (!err) ++ au_update_iigen(inode, /*half*/0); ++ break; ++ } ++ ++ if (unlikely(err)) ++ ii_write_unlock(inode); ++out: ++ return err; ++} ++ ++int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ unsigned int d_type, ino_t *ino) ++{ ++ int err; ++ struct mutex *mtx; ++ ++ /* prevent hardlinked inode number from race condition */ ++ mtx = NULL; ++ if (d_type != DT_DIR) { ++ mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx; ++ mutex_lock(mtx); ++ } ++ err = au_xino_read(sb, bindex, h_ino, ino); ++ if (unlikely(err)) ++ goto out; ++ ++ if (!*ino) { ++ err = -EIO; ++ *ino = au_xino_new_ino(sb); ++ if (unlikely(!*ino)) ++ goto out; ++ err = au_xino_write(sb, bindex, h_ino, *ino); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++out: ++ if (mtx) ++ mutex_unlock(mtx); ++ return err; ++} ++ ++/* successful returns with iinfo write_locked */ ++/* todo: return with unlocked? */ ++struct inode *au_new_inode(struct dentry *dentry, int must_new) ++{ ++ struct inode *inode; ++ struct dentry *h_dentry; ++ struct super_block *sb; ++ struct mutex *mtx; ++ ino_t h_ino, ino; ++ int err; ++ aufs_bindex_t bstart; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ h_dentry = au_h_dptr(dentry, bstart); ++ h_ino = h_dentry->d_inode->i_ino; ++ ++ /* ++ * stop 'race'-ing between hardlinks under different ++ * parents. ++ */ ++ mtx = NULL; ++ if (!d_is_dir(h_dentry)) ++ mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; ++ ++new_ino: ++ if (mtx) ++ mutex_lock(mtx); ++ err = au_xino_read(sb, bstart, h_ino, &ino); ++ inode = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ if (!ino) { ++ ino = au_xino_new_ino(sb); ++ if (unlikely(!ino)) { ++ inode = ERR_PTR(-EIO); ++ goto out; ++ } ++ } ++ ++ AuDbg("i%lu\n", (unsigned long)ino); ++ inode = au_iget_locked(sb, ino); ++ err = PTR_ERR(inode); ++ if (IS_ERR(inode)) ++ goto out; ++ ++ AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); ++ if (inode->i_state & I_NEW) { ++ /* verbose coding for lock class name */ ++ if (unlikely(d_is_symlink(h_dentry))) ++ au_rw_class(&au_ii(inode)->ii_rwsem, ++ au_lc_key + AuLcSymlink_IIINFO); ++ else if (unlikely(d_is_dir(h_dentry))) ++ au_rw_class(&au_ii(inode)->ii_rwsem, ++ au_lc_key + AuLcDir_IIINFO); ++ else /* likely */ ++ au_rw_class(&au_ii(inode)->ii_rwsem, ++ au_lc_key + AuLcNonDir_IIINFO); ++ ++ ii_write_lock_new_child(inode); ++ err = set_inode(inode, dentry); ++ if (!err) { ++ unlock_new_inode(inode); ++ goto out; /* success */ ++ } ++ ++ /* ++ * iget_failed() calls iput(), but we need to call ++ * ii_write_unlock() after iget_failed(). so dirty hack for ++ * i_count. ++ */ ++ atomic_inc(&inode->i_count); ++ iget_failed(inode); ++ ii_write_unlock(inode); ++ au_xino_write(sb, bstart, h_ino, /*ino*/0); ++ /* ignore this error */ ++ goto out_iput; ++ } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { ++ /* ++ * horrible race condition between lookup, readdir and copyup ++ * (or something). ++ */ ++ if (mtx) ++ mutex_unlock(mtx); ++ err = reval_inode(inode, dentry); ++ if (unlikely(err < 0)) { ++ mtx = NULL; ++ goto out_iput; ++ } ++ ++ if (!err) { ++ mtx = NULL; ++ goto out; /* success */ ++ } else if (mtx) ++ mutex_lock(mtx); ++ } ++ ++ if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode))) ++ AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir," ++ " b%d, %s, %pd, hi%lu, i%lu.\n", ++ bstart, au_sbtype(h_dentry->d_sb), dentry, ++ (unsigned long)h_ino, (unsigned long)ino); ++ ino = 0; ++ err = au_xino_write(sb, bstart, h_ino, /*ino*/0); ++ if (!err) { ++ iput(inode); ++ if (mtx) ++ mutex_unlock(mtx); ++ goto new_ino; ++ } ++ ++out_iput: ++ iput(inode); ++ inode = ERR_PTR(err); ++out: ++ if (mtx) ++ mutex_unlock(mtx); ++ return inode; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, ++ struct inode *inode) ++{ ++ int err; ++ struct inode *hi; ++ ++ err = au_br_rdonly(au_sbr(sb, bindex)); ++ ++ /* pseudo-link after flushed may happen out of bounds */ ++ if (!err ++ && inode ++ && au_ibstart(inode) <= bindex ++ && bindex <= au_ibend(inode)) { ++ /* ++ * permission check is unnecessary since vfsub routine ++ * will be called later ++ */ ++ hi = au_h_iptr(inode, bindex); ++ if (hi) ++ err = IS_IMMUTABLE(hi) ? -EROFS : 0; ++ } ++ ++ return err; ++} ++ ++int au_test_h_perm(struct inode *h_inode, int mask) ++{ ++ if (uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) ++ return 0; ++ return inode_permission(h_inode, mask); ++} ++ ++int au_test_h_perm_sio(struct inode *h_inode, int mask) ++{ ++ if (au_test_nfs(h_inode->i_sb) ++ && (mask & MAY_WRITE) ++ && S_ISDIR(h_inode->i_mode)) ++ mask |= MAY_READ; /* force permission check */ ++ return au_test_h_perm(h_inode, mask); ++} +diff -Nur linux-4.0.4.orig/fs/aufs/inode.h linux-4.0.4/fs/aufs/inode.h +--- linux-4.0.4.orig/fs/aufs/inode.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/inode.h 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,660 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations ++ */ ++ ++#ifndef __AUFS_INODE_H__ ++#define __AUFS_INODE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "rwsem.h" ++ ++struct vfsmount; ++ ++struct au_hnotify { ++#ifdef CONFIG_AUFS_HNOTIFY ++#ifdef CONFIG_AUFS_HFSNOTIFY ++ /* never use fsnotify_add_vfsmount_mark() */ ++ struct fsnotify_mark hn_mark; ++#endif ++ struct inode *hn_aufs_inode; /* no get/put */ ++#endif ++} ____cacheline_aligned_in_smp; ++ ++struct au_hinode { ++ struct inode *hi_inode; ++ aufs_bindex_t hi_id; ++#ifdef CONFIG_AUFS_HNOTIFY ++ struct au_hnotify *hi_notify; ++#endif ++ ++ /* reference to the copied-up whiteout with get/put */ ++ struct dentry *hi_whdentry; ++}; ++ ++/* ig_flags */ ++#define AuIG_HALF_REFRESHED 1 ++#define au_ig_ftest(flags, name) ((flags) & AuIG_##name) ++#define au_ig_fset(flags, name) \ ++ do { (flags) |= AuIG_##name; } while (0) ++#define au_ig_fclr(flags, name) \ ++ do { (flags) &= ~AuIG_##name; } while (0) ++ ++struct au_iigen { ++ __u32 ig_generation, ig_flags; ++}; ++ ++struct au_vdir; ++struct au_iinfo { ++ spinlock_t ii_genspin; ++ struct au_iigen ii_generation; ++ struct super_block *ii_hsb1; /* no get/put */ ++ ++ struct au_rwsem ii_rwsem; ++ aufs_bindex_t ii_bstart, ii_bend; ++ __u32 ii_higen; ++ struct au_hinode *ii_hinode; ++ struct au_vdir *ii_vdir; ++}; ++ ++struct au_icntnr { ++ struct au_iinfo iinfo; ++ struct inode vfs_inode; ++} ____cacheline_aligned_in_smp; ++ ++/* au_pin flags */ ++#define AuPin_DI_LOCKED 1 ++#define AuPin_MNT_WRITE (1 << 1) ++#define au_ftest_pin(flags, name) ((flags) & AuPin_##name) ++#define au_fset_pin(flags, name) \ ++ do { (flags) |= AuPin_##name; } while (0) ++#define au_fclr_pin(flags, name) \ ++ do { (flags) &= ~AuPin_##name; } while (0) ++ ++struct au_pin { ++ /* input */ ++ struct dentry *dentry; ++ unsigned int udba; ++ unsigned char lsc_di, lsc_hi, flags; ++ aufs_bindex_t bindex; ++ ++ /* output */ ++ struct dentry *parent; ++ struct au_hinode *hdir; ++ struct vfsmount *h_mnt; ++ ++ /* temporary unlock/relock for copyup */ ++ struct dentry *h_dentry, *h_parent; ++ struct au_branch *br; ++ struct task_struct *task; ++}; ++ ++void au_pin_hdir_unlock(struct au_pin *p); ++int au_pin_hdir_lock(struct au_pin *p); ++int au_pin_hdir_relock(struct au_pin *p); ++void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task); ++void au_pin_hdir_acquire_nest(struct au_pin *p); ++void au_pin_hdir_release(struct au_pin *p); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_iinfo *au_ii(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); ++ if (iinfo->ii_hinode) ++ return iinfo; ++ return NULL; /* debugging bad_inode case */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* inode.c */ ++struct inode *au_igrab(struct inode *inode); ++int au_refresh_hinode_self(struct inode *inode); ++int au_refresh_hinode(struct inode *inode, struct dentry *dentry); ++int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ unsigned int d_type, ino_t *ino); ++struct inode *au_new_inode(struct dentry *dentry, int must_new); ++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, ++ struct inode *inode); ++int au_test_h_perm(struct inode *h_inode, int mask); ++int au_test_h_perm_sio(struct inode *h_inode, int mask); ++ ++static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, ++ ino_t h_ino, unsigned int d_type, ino_t *ino) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ return au_ino(sb, bindex, h_ino, d_type, ino); ++#else ++ return 0; ++#endif ++} ++ ++/* i_op.c */ ++extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop; ++ ++/* au_wr_dir flags */ ++#define AuWrDir_ADD_ENTRY 1 ++#define AuWrDir_ISDIR (1 << 1) ++#define AuWrDir_TMPFILE (1 << 2) ++#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) ++#define au_fset_wrdir(flags, name) \ ++ do { (flags) |= AuWrDir_##name; } while (0) ++#define au_fclr_wrdir(flags, name) \ ++ do { (flags) &= ~AuWrDir_##name; } while (0) ++ ++struct au_wr_dir_args { ++ aufs_bindex_t force_btgt; ++ unsigned char flags; ++}; ++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, ++ struct au_wr_dir_args *args); ++ ++struct dentry *au_pinned_h_parent(struct au_pin *pin); ++void au_pin_init(struct au_pin *pin, struct dentry *dentry, ++ aufs_bindex_t bindex, int lsc_di, int lsc_hi, ++ unsigned int udba, unsigned char flags); ++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int udba, unsigned char flags) __must_check; ++int au_do_pin(struct au_pin *pin) __must_check; ++void au_unpin(struct au_pin *pin); ++int au_reval_for_attr(struct dentry *dentry, unsigned int sigen); ++ ++#define AuIcpup_DID_CPUP 1 ++#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name) ++#define au_fset_icpup(flags, name) \ ++ do { (flags) |= AuIcpup_##name; } while (0) ++#define au_fclr_icpup(flags, name) \ ++ do { (flags) &= ~AuIcpup_##name; } while (0) ++ ++struct au_icpup_args { ++ unsigned char flags; ++ unsigned char pin_flags; ++ aufs_bindex_t btgt; ++ unsigned int udba; ++ struct au_pin pin; ++ struct path h_path; ++ struct inode *h_inode; ++}; ++ ++int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, ++ struct au_icpup_args *a); ++ ++int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path); ++ ++/* i_op_add.c */ ++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir); ++int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t dev); ++int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); ++int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool want_excl); ++struct vfsub_aopen_args; ++int au_aopen_or_create(struct inode *dir, struct dentry *dentry, ++ struct vfsub_aopen_args *args); ++int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode); ++int aufs_link(struct dentry *src_dentry, struct inode *dir, ++ struct dentry *dentry); ++int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); ++ ++/* i_op_del.c */ ++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup); ++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir); ++int aufs_unlink(struct inode *dir, struct dentry *dentry); ++int aufs_rmdir(struct inode *dir, struct dentry *dentry); ++ ++/* i_op_ren.c */ ++int au_wbr(struct dentry *dentry, aufs_bindex_t btgt); ++int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, ++ struct inode *dir, struct dentry *dentry); ++ ++/* iinfo.c */ ++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); ++void au_hiput(struct au_hinode *hinode); ++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_wh); ++unsigned int au_hi_flags(struct inode *inode, int isdir); ++ ++/* hinode flags */ ++#define AuHi_XINO 1 ++#define AuHi_HNOTIFY (1 << 1) ++#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) ++#define au_fset_hi(flags, name) \ ++ do { (flags) |= AuHi_##name; } while (0) ++#define au_fclr_hi(flags, name) \ ++ do { (flags) &= ~AuHi_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_HNOTIFY ++#undef AuHi_HNOTIFY ++#define AuHi_HNOTIFY 0 ++#endif ++ ++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode, unsigned int flags); ++ ++void au_update_iigen(struct inode *inode, int half); ++void au_update_ibrange(struct inode *inode, int do_put_zero); ++ ++void au_icntnr_init_once(void *_c); ++int au_iinfo_init(struct inode *inode); ++void au_iinfo_fin(struct inode *inode); ++int au_ii_realloc(struct au_iinfo *iinfo, int nbr); ++ ++#ifdef CONFIG_PROC_FS ++/* plink.c */ ++int au_plink_maint(struct super_block *sb, int flags); ++struct au_sbinfo; ++void au_plink_maint_leave(struct au_sbinfo *sbinfo); ++int au_plink_maint_enter(struct super_block *sb); ++#ifdef CONFIG_AUFS_DEBUG ++void au_plink_list(struct super_block *sb); ++#else ++AuStubVoid(au_plink_list, struct super_block *sb) ++#endif ++int au_plink_test(struct inode *inode); ++struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex); ++void au_plink_append(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++void au_plink_put(struct super_block *sb, int verbose); ++void au_plink_clean(struct super_block *sb, int verbose); ++void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); ++#else ++AuStubInt0(au_plink_maint, struct super_block *sb, int flags); ++AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo); ++AuStubInt0(au_plink_maint_enter, struct super_block *sb); ++AuStubVoid(au_plink_list, struct super_block *sb); ++AuStubInt0(au_plink_test, struct inode *inode); ++AuStub(struct dentry *, au_plink_lkup, return NULL, ++ struct inode *inode, aufs_bindex_t bindex); ++AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++AuStubVoid(au_plink_put, struct super_block *sb, int verbose); ++AuStubVoid(au_plink_clean, struct super_block *sb, int verbose); ++AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id); ++#endif /* CONFIG_PROC_FS */ ++ ++#ifdef CONFIG_AUFS_XATTR ++/* xattr.c */ ++int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, ++ unsigned int verbose); ++ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size); ++ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, ++ size_t size); ++int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, ++ size_t size, int flags); ++int aufs_removexattr(struct dentry *dentry, const char *name); ++ ++/* void au_xattr_init(struct super_block *sb); */ ++#else ++AuStubInt0(au_cpup_xattr, struct dentry *h_dst, struct dentry *h_src, ++ int ignore_flags, unsigned int verbose); ++/* AuStubVoid(au_xattr_init, struct super_block *sb); */ ++#endif ++ ++#ifdef CONFIG_FS_POSIX_ACL ++struct posix_acl *aufs_get_acl(struct inode *inode, int type); ++int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type); ++#endif ++ ++#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) ++enum { ++ AU_XATTR_SET, ++ AU_XATTR_REMOVE, ++ AU_ACL_SET ++}; ++ ++struct au_srxattr { ++ int type; ++ union { ++ struct { ++ const char *name; ++ const void *value; ++ size_t size; ++ int flags; ++ } set; ++ struct { ++ const char *name; ++ } remove; ++ struct { ++ struct posix_acl *acl; ++ int type; ++ } acl_set; ++ } u; ++}; ++ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg); ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for iinfo */ ++enum { ++ AuLsc_II_CHILD, /* child first */ ++ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */ ++ AuLsc_II_CHILD3, /* copyup dirs */ ++ AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */ ++ AuLsc_II_PARENT2, ++ AuLsc_II_PARENT3, /* copyup dirs */ ++ AuLsc_II_NEW_CHILD ++}; ++ ++/* ++ * ii_read_lock_child, ii_write_lock_child, ++ * ii_read_lock_child2, ii_write_lock_child2, ++ * ii_read_lock_child3, ii_write_lock_child3, ++ * ii_read_lock_parent, ii_write_lock_parent, ++ * ii_read_lock_parent2, ii_write_lock_parent2, ++ * ii_read_lock_parent3, ii_write_lock_parent3, ++ * ii_read_lock_new_child, ii_write_lock_new_child, ++ */ ++#define AuReadLockFunc(name, lsc) \ ++static inline void ii_read_lock_##name(struct inode *i) \ ++{ \ ++ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ ++} ++ ++#define AuWriteLockFunc(name, lsc) \ ++static inline void ii_write_lock_##name(struct inode *i) \ ++{ \ ++ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ ++} ++ ++#define AuRWLockFuncs(name, lsc) \ ++ AuReadLockFunc(name, lsc) \ ++ AuWriteLockFunc(name, lsc) ++ ++AuRWLockFuncs(child, CHILD); ++AuRWLockFuncs(child2, CHILD2); ++AuRWLockFuncs(child3, CHILD3); ++AuRWLockFuncs(parent, PARENT); ++AuRWLockFuncs(parent2, PARENT2); ++AuRWLockFuncs(parent3, PARENT3); ++AuRWLockFuncs(new_child, NEW_CHILD); ++ ++#undef AuReadLockFunc ++#undef AuWriteLockFunc ++#undef AuRWLockFuncs ++ ++/* ++ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock ++ */ ++AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem); ++ ++#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) ++#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem) ++#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void au_icntnr_init(struct au_icntnr *c) ++{ ++#ifdef CONFIG_AUFS_DEBUG ++ c->vfs_inode.i_mode = 0; ++#endif ++} ++ ++static inline unsigned int au_iigen(struct inode *inode, struct au_iigen *iigen) ++{ ++ unsigned int gen; ++ struct au_iinfo *iinfo; ++ ++ iinfo = au_ii(inode); ++ spin_lock(&iinfo->ii_genspin); ++ if (iigen) ++ *iigen = iinfo->ii_generation; ++ gen = iinfo->ii_generation.ig_generation; ++ spin_unlock(&iinfo->ii_genspin); ++ ++ return gen; ++} ++ ++/* tiny test for inode number */ ++/* tmpfs generation is too rough */ ++static inline int au_test_higen(struct inode *inode, struct inode *h_inode) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = au_ii(inode); ++ AuRwMustAnyLock(&iinfo->ii_rwsem); ++ return !(iinfo->ii_hsb1 == h_inode->i_sb ++ && iinfo->ii_higen == h_inode->i_generation); ++} ++ ++static inline void au_iigen_dec(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = au_ii(inode); ++ spin_lock(&iinfo->ii_genspin); ++ iinfo->ii_generation.ig_generation--; ++ spin_unlock(&iinfo->ii_genspin); ++} ++ ++static inline int au_iigen_test(struct inode *inode, unsigned int sigen) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(inode && au_iigen(inode, NULL) != sigen)) ++ err = -EIO; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline aufs_bindex_t au_ii_br_id(struct inode *inode, ++ aufs_bindex_t bindex) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_hinode[0 + bindex].hi_id; ++} ++ ++static inline aufs_bindex_t au_ibstart(struct inode *inode) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_bstart; ++} ++ ++static inline aufs_bindex_t au_ibend(struct inode *inode) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_bend; ++} ++ ++static inline struct au_vdir *au_ivdir(struct inode *inode) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_vdir; ++} ++ ++static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry; ++} ++ ++static inline void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustWriteLock(inode); ++ au_ii(inode)->ii_bstart = bindex; ++} ++ ++static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustWriteLock(inode); ++ au_ii(inode)->ii_bend = bindex; ++} ++ ++static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) ++{ ++ IiMustWriteLock(inode); ++ au_ii(inode)->ii_vdir = vdir; ++} ++ ++static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_hinode + bindex; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct dentry *au_pinned_parent(struct au_pin *pin) ++{ ++ if (pin) ++ return pin->parent; ++ return NULL; ++} ++ ++static inline struct inode *au_pinned_h_dir(struct au_pin *pin) ++{ ++ if (pin && pin->hdir) ++ return pin->hdir->hi_inode; ++ return NULL; ++} ++ ++static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin) ++{ ++ if (pin) ++ return pin->hdir; ++ return NULL; ++} ++ ++static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry) ++{ ++ if (pin) ++ pin->dentry = dentry; ++} ++ ++static inline void au_pin_set_parent_lflag(struct au_pin *pin, ++ unsigned char lflag) ++{ ++ if (pin) { ++ if (lflag) ++ au_fset_pin(pin->flags, DI_LOCKED); ++ else ++ au_fclr_pin(pin->flags, DI_LOCKED); ++ } ++} ++ ++#if 0 /* reserved */ ++static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) ++{ ++ if (pin) { ++ dput(pin->parent); ++ pin->parent = dget(parent); ++ } ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_branch; ++#ifdef CONFIG_AUFS_HNOTIFY ++struct au_hnotify_op { ++ void (*ctl)(struct au_hinode *hinode, int do_set); ++ int (*alloc)(struct au_hinode *hinode); ++ ++ /* ++ * if it returns true, the the caller should free hinode->hi_notify, ++ * otherwise ->free() frees it. ++ */ ++ int (*free)(struct au_hinode *hinode, ++ struct au_hnotify *hn) __must_check; ++ ++ void (*fin)(void); ++ int (*init)(void); ++ ++ int (*reset_br)(unsigned int udba, struct au_branch *br, int perm); ++ void (*fin_br)(struct au_branch *br); ++ int (*init_br)(struct au_branch *br, int perm); ++}; ++ ++/* hnotify.c */ ++int au_hn_alloc(struct au_hinode *hinode, struct inode *inode); ++void au_hn_free(struct au_hinode *hinode); ++void au_hn_ctl(struct au_hinode *hinode, int do_set); ++void au_hn_reset(struct inode *inode, unsigned int flags); ++int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, ++ struct qstr *h_child_qstr, struct inode *h_child_inode); ++int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm); ++int au_hnotify_init_br(struct au_branch *br, int perm); ++void au_hnotify_fin_br(struct au_branch *br); ++int __init au_hnotify_init(void); ++void au_hnotify_fin(void); ++ ++/* hfsnotify.c */ ++extern const struct au_hnotify_op au_hnotify_op; ++ ++static inline ++void au_hn_init(struct au_hinode *hinode) ++{ ++ hinode->hi_notify = NULL; ++} ++ ++static inline struct au_hnotify *au_hn(struct au_hinode *hinode) ++{ ++ return hinode->hi_notify; ++} ++ ++#else ++AuStub(int, au_hn_alloc, return -EOPNOTSUPP, ++ struct au_hinode *hinode __maybe_unused, ++ struct inode *inode __maybe_unused) ++AuStub(struct au_hnotify *, au_hn, return NULL, struct au_hinode *hinode) ++AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused) ++AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused, ++ int do_set __maybe_unused) ++AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused, ++ unsigned int flags __maybe_unused) ++AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused, ++ struct au_branch *br __maybe_unused, ++ int perm __maybe_unused) ++AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused, ++ int perm __maybe_unused) ++AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused) ++AuStubInt0(__init au_hnotify_init, void) ++AuStubVoid(au_hnotify_fin, void) ++AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused) ++#endif /* CONFIG_AUFS_HNOTIFY */ ++ ++static inline void au_hn_suspend(struct au_hinode *hdir) ++{ ++ au_hn_ctl(hdir, /*do_set*/0); ++} ++ ++static inline void au_hn_resume(struct au_hinode *hdir) ++{ ++ au_hn_ctl(hdir, /*do_set*/1); ++} ++ ++static inline void au_hn_imtx_lock(struct au_hinode *hdir) ++{ ++ mutex_lock(&hdir->hi_inode->i_mutex); ++ au_hn_suspend(hdir); ++} ++ ++static inline void au_hn_imtx_lock_nested(struct au_hinode *hdir, ++ unsigned int sc __maybe_unused) ++{ ++ mutex_lock_nested(&hdir->hi_inode->i_mutex, sc); ++ au_hn_suspend(hdir); ++} ++ ++static inline void au_hn_imtx_unlock(struct au_hinode *hdir) ++{ ++ au_hn_resume(hdir); ++ mutex_unlock(&hdir->hi_inode->i_mutex); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_INODE_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/ioctl.c linux-4.0.4/fs/aufs/ioctl.c +--- linux-4.0.4.orig/fs/aufs/ioctl.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/ioctl.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,206 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * ioctl ++ * plink-management and readdir in userspace. ++ * assist the pathconf(3) wrapper library. ++ * move-down ++ * File-based Hierarchical Storage Management. ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) ++{ ++ int err, fd; ++ aufs_bindex_t wbi, bindex, bend; ++ struct file *h_file; ++ struct super_block *sb; ++ struct dentry *root; ++ struct au_branch *br; ++ struct aufs_wbr_fd wbrfd = { ++ .oflags = au_dir_roflags, ++ .brid = -1 ++ }; ++ const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY ++ | O_NOATIME | O_CLOEXEC; ++ ++ AuDebugOn(wbrfd.oflags & ~valid); ++ ++ if (arg) { ++ err = copy_from_user(&wbrfd, arg, sizeof(wbrfd)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ goto out; ++ } ++ ++ err = -EINVAL; ++ AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid); ++ wbrfd.oflags |= au_dir_roflags; ++ AuDbg("0%o\n", wbrfd.oflags); ++ if (unlikely(wbrfd.oflags & ~valid)) ++ goto out; ++ } ++ ++ fd = get_unused_fd_flags(0); ++ err = fd; ++ if (unlikely(fd < 0)) ++ goto out; ++ ++ h_file = ERR_PTR(-EINVAL); ++ wbi = 0; ++ br = NULL; ++ sb = path->dentry->d_sb; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_IR); ++ bend = au_sbend(sb); ++ if (wbrfd.brid >= 0) { ++ wbi = au_br_index(sb, wbrfd.brid); ++ if (unlikely(wbi < 0 || wbi > bend)) ++ goto out_unlock; ++ } ++ ++ h_file = ERR_PTR(-ENOENT); ++ br = au_sbr(sb, wbi); ++ if (!au_br_writable(br->br_perm)) { ++ if (arg) ++ goto out_unlock; ++ ++ bindex = wbi + 1; ++ wbi = -1; ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_writable(br->br_perm)) { ++ wbi = bindex; ++ br = au_sbr(sb, wbi); ++ break; ++ } ++ } ++ } ++ AuDbg("wbi %d\n", wbi); ++ if (wbi >= 0) ++ h_file = au_h_open(root, wbi, wbrfd.oflags, NULL, ++ /*force_wr*/0); ++ ++out_unlock: ++ aufs_read_unlock(root, AuLock_IR); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out_fd; ++ ++ atomic_dec(&br->br_count); /* cf. au_h_open() */ ++ fd_install(fd, h_file); ++ err = fd; ++ goto out; /* success */ ++ ++out_fd: ++ put_unused_fd(fd); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err; ++ struct dentry *dentry; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_ioctl(file, cmd, arg); ++ break; ++ ++ case AUFS_CTL_WBR_FD: ++ err = au_wbr_fd(&file->f_path, (void __user *)arg); ++ break; ++ ++ case AUFS_CTL_IBUSY: ++ err = au_ibusy_ioctl(file, arg); ++ break; ++ ++ case AUFS_CTL_BRINFO: ++ err = au_brinfo_ioctl(file, arg); ++ break; ++ ++ case AUFS_CTL_FHSM_FD: ++ dentry = file->f_path.dentry; ++ if (IS_ROOT(dentry)) ++ err = au_fhsm_fd(dentry->d_sb, arg); ++ else ++ err = -ENOTTY; ++ break; ++ ++ default: ++ /* do not call the lower */ ++ AuDbg("0x%x\n", cmd); ++ err = -ENOTTY; ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err; ++ ++ switch (cmd) { ++ case AUFS_CTL_MVDOWN: ++ err = au_mvdown(file->f_path.dentry, (void __user *)arg); ++ break; ++ ++ case AUFS_CTL_WBR_FD: ++ err = au_wbr_fd(&file->f_path, (void __user *)arg); ++ break; ++ ++ default: ++ /* do not call the lower */ ++ AuDbg("0x%x\n", cmd); ++ err = -ENOTTY; ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++#ifdef CONFIG_COMPAT ++long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ long err; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_compat_ioctl(file, cmd, arg); ++ break; ++ ++ case AUFS_CTL_IBUSY: ++ err = au_ibusy_compat_ioctl(file, arg); ++ break; ++ ++ case AUFS_CTL_BRINFO: ++ err = au_brinfo_compat_ioctl(file, arg); ++ break; ++ ++ default: ++ err = aufs_ioctl_dir(file, cmd, arg); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); ++} ++#endif +diff -Nur linux-4.0.4.orig/fs/aufs/i_op_add.c linux-4.0.4/fs/aufs/i_op_add.c +--- linux-4.0.4.orig/fs/aufs/i_op_add.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/i_op_add.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,917 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations (add entry) ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * final procedure of adding a new entry, except link(2). ++ * remove whiteout, instantiate, copyup the parent dir's times and size ++ * and update version. ++ * if it failed, re-create the removed whiteout. ++ */ ++static int epilog(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct dentry *dentry) ++{ ++ int err, rerr; ++ aufs_bindex_t bwh; ++ struct path h_path; ++ struct super_block *sb; ++ struct inode *inode, *h_dir; ++ struct dentry *wh; ++ ++ bwh = -1; ++ sb = dir->i_sb; ++ if (wh_dentry) { ++ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ ++ IMustLock(h_dir); ++ AuDebugOn(au_h_iptr(dir, bindex) != h_dir); ++ bwh = au_dbwh(dentry); ++ h_path.dentry = wh_dentry; ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, ++ dentry); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ inode = au_new_inode(dentry, /*must_new*/1); ++ if (!IS_ERR(inode)) { ++ d_instantiate(dentry, inode); ++ dir = dentry->d_parent->d_inode; /* dir inode is locked */ ++ IMustLock(dir); ++ au_dir_ts(dir, bindex); ++ dir->i_version++; ++ au_fhsm_wrote(sb, bindex, /*force*/0); ++ return 0; /* success */ ++ } ++ ++ err = PTR_ERR(inode); ++ if (!wh_dentry) ++ goto out; ++ ++ /* revert */ ++ /* dir inode is locked */ ++ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); ++ rerr = PTR_ERR(wh); ++ if (IS_ERR(wh)) { ++ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } else ++ dput(wh); ++ ++out: ++ return err; ++} ++ ++static int au_d_may_add(struct dentry *dentry) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(d_unhashed(dentry))) ++ err = -ENOENT; ++ if (unlikely(dentry->d_inode)) ++ err = -EEXIST; ++ return err; ++} ++ ++/* ++ * simple tests for the adding inode operations. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir) ++{ ++ int err; ++ umode_t h_mode; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ ++ err = -ENAMETOOLONG; ++ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ goto out; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ h_inode = h_dentry->d_inode; ++ if (!dentry->d_inode) { ++ err = -EEXIST; ++ if (unlikely(h_inode)) ++ goto out; ++ } else { ++ /* rename(2) case */ ++ err = -EIO; ++ if (unlikely(!h_inode || !h_inode->i_nlink)) ++ goto out; ++ ++ h_mode = h_inode->i_mode; ++ if (!isdir) { ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(h_mode))) ++ goto out; ++ } else if (unlikely(!S_ISDIR(h_mode))) { ++ err = -ENOTDIR; ++ goto out; ++ } ++ } ++ ++ err = 0; ++ /* expected parent dir is locked */ ++ if (unlikely(h_parent != h_dentry->d_parent)) ++ err = -EIO; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * initial procedure of adding a new entry. ++ * prepare writable branch and the parent dir, lock it, ++ * and lookup whiteout for the new entry. ++ */ ++static struct dentry* ++lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, ++ struct dentry *src_dentry, struct au_pin *pin, ++ struct au_wr_dir_args *wr_dir_args) ++{ ++ struct dentry *wh_dentry, *h_parent; ++ struct super_block *sb; ++ struct au_branch *br; ++ int err; ++ unsigned int udba; ++ aufs_bindex_t bcpup; ++ ++ AuDbg("%pd\n", dentry); ++ ++ err = au_wr_dir(dentry, src_dentry, wr_dir_args); ++ bcpup = err; ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ udba = au_opt_udba(sb); ++ err = au_pin(pin, dentry, bcpup, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ h_parent = au_pinned_h_parent(pin); ++ if (udba != AuOpt_UDBA_NONE ++ && au_dbstart(dentry) == bcpup) ++ err = au_may_add(dentry, bcpup, h_parent, ++ au_ftest_wrdir(wr_dir_args->flags, ISDIR)); ++ else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ err = -ENAMETOOLONG; ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ br = au_sbr(sb, bcpup); ++ if (dt) { ++ struct path tmp = { ++ .dentry = h_parent, ++ .mnt = au_br_mnt(br) ++ }; ++ au_dtime_store(dt, au_pinned_parent(pin), &tmp); ++ } ++ ++ wh_dentry = NULL; ++ if (bcpup != au_dbwh(dentry)) ++ goto out; /* success */ ++ ++ /* ++ * ENAMETOOLONG here means that if we allowed create such name, then it ++ * would not be able to removed in the future. So we don't allow such ++ * name here and we don't handle ENAMETOOLONG differently here. ++ */ ++ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); ++ ++out_unpin: ++ if (IS_ERR(wh_dentry)) ++ au_unpin(pin); ++out: ++ return wh_dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++enum { Mknod, Symlink, Creat }; ++struct simple_arg { ++ int type; ++ union { ++ struct { ++ umode_t mode; ++ bool want_excl; ++ bool try_aopen; ++ struct vfsub_aopen_args *aopen; ++ } c; ++ struct { ++ const char *symname; ++ } s; ++ struct { ++ umode_t mode; ++ dev_t dev; ++ } m; ++ } u; ++}; ++ ++static int add_simple(struct inode *dir, struct dentry *dentry, ++ struct simple_arg *arg) ++{ ++ int err, rerr; ++ aufs_bindex_t bstart; ++ unsigned char created; ++ const unsigned char try_aopen ++ = (arg->type == Creat && arg->u.c.try_aopen); ++ struct dentry *wh_dentry, *parent; ++ struct inode *h_dir; ++ struct super_block *sb; ++ struct au_branch *br; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct path h_path; ++ struct au_wr_dir_args wr_dir_args; ++ } *a; ++ ++ AuDbg("%pd\n", dentry); ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ a->wr_dir_args.force_btgt = -1; ++ a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ if (!try_aopen) { ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ } ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ if (!try_aopen) ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, ++ &a->pin, &a->wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ bstart = au_dbstart(dentry); ++ sb = dentry->d_sb; ++ br = au_sbr(sb, bstart); ++ a->h_path.dentry = au_h_dptr(dentry, bstart); ++ a->h_path.mnt = au_br_mnt(br); ++ h_dir = au_pinned_h_dir(&a->pin); ++ switch (arg->type) { ++ case Creat: ++ err = 0; ++ if (!try_aopen || !h_dir->i_op->atomic_open) ++ err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, ++ arg->u.c.want_excl); ++ else ++ err = vfsub_atomic_open(h_dir, a->h_path.dentry, ++ arg->u.c.aopen, br); ++ break; ++ case Symlink: ++ err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); ++ break; ++ case Mknod: ++ err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, ++ arg->u.m.dev); ++ break; ++ default: ++ BUG(); ++ } ++ created = !err; ++ if (!err) ++ err = epilog(dir, bstart, wh_dentry, dentry); ++ ++ /* revert */ ++ if (unlikely(created && err && a->h_path.dentry->d_inode)) { ++ /* no delegation since it is just created */ ++ rerr = vfsub_unlink(h_dir, &a->h_path, /*delegated*/NULL, ++ /*force*/0); ++ if (rerr) { ++ AuIOErr("%pd revert failure(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&a->dt); ++ } ++ ++ if (!err && try_aopen && !h_dir->i_op->atomic_open) ++ *arg->u.c.aopen->opened |= FILE_CREATED; ++ ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++ ++out_parent: ++ if (!try_aopen) ++ di_write_unlock(parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ if (!try_aopen) ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ return err; ++} ++ ++int aufs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, ++ dev_t dev) ++{ ++ struct simple_arg arg = { ++ .type = Mknod, ++ .u.m = { ++ .mode = mode, ++ .dev = dev ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) ++{ ++ struct simple_arg arg = { ++ .type = Symlink, ++ .u.s.symname = symname ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ++ bool want_excl) ++{ ++ struct simple_arg arg = { ++ .type = Creat, ++ .u.c = { ++ .mode = mode, ++ .want_excl = want_excl ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int au_aopen_or_create(struct inode *dir, struct dentry *dentry, ++ struct vfsub_aopen_args *aopen_args) ++{ ++ struct simple_arg arg = { ++ .type = Creat, ++ .u.c = { ++ .mode = aopen_args->create_mode, ++ .want_excl = aopen_args->open_flag & O_EXCL, ++ .try_aopen = true, ++ .aopen = aopen_args ++ } ++ }; ++ return add_simple(dir, dentry, &arg); ++} ++ ++int aufs_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent, *h_dentry; ++ struct inode *h_dir, *inode; ++ struct vfsmount *h_mnt; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = AuWrDir_TMPFILE ++ }; ++ ++ /* copy-up may happen */ ++ mutex_lock(&dir->i_mutex); ++ ++ sb = dir->i_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_di_init(dentry); ++ if (unlikely(err)) ++ goto out_si; ++ ++ err = -EBUSY; ++ parent = d_find_any_alias(dir); ++ AuDebugOn(!parent); ++ di_write_lock_parent(parent); ++ if (unlikely(parent->d_inode != dir)) ++ goto out_parent; ++ ++ err = au_digen_test(parent, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ bindex = au_dbstart(parent); ++ au_set_dbstart(dentry, bindex); ++ au_set_dbend(dentry, bindex); ++ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); ++ bindex = err; ++ if (unlikely(err < 0)) ++ goto out_parent; ++ ++ err = -EOPNOTSUPP; ++ h_dir = au_h_iptr(dir, bindex); ++ if (unlikely(!h_dir->i_op->tmpfile)) ++ goto out_parent; ++ ++ h_mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_mnt_want_write(h_mnt); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ h_parent = au_h_dptr(parent, bindex); ++ err = inode_permission(h_parent->d_inode, MAY_WRITE | MAY_EXEC); ++ if (unlikely(err)) ++ goto out_mnt; ++ ++ err = -ENOMEM; ++ h_dentry = d_alloc(h_parent, &dentry->d_name); ++ if (unlikely(!h_dentry)) ++ goto out_mnt; ++ ++ err = h_dir->i_op->tmpfile(h_dir, h_dentry, mode); ++ if (unlikely(err)) ++ goto out_dentry; ++ ++ au_set_dbstart(dentry, bindex); ++ au_set_dbend(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, dget(h_dentry)); ++ inode = au_new_inode(dentry, /*must_new*/1); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ au_set_h_dptr(dentry, bindex, NULL); ++ au_set_dbstart(dentry, -1); ++ au_set_dbend(dentry, -1); ++ } else { ++ if (!inode->i_nlink) ++ set_nlink(inode, 1); ++ d_tmpfile(dentry, inode); ++ au_di(dentry)->di_tmpfile = 1; ++ ++ /* update without i_mutex */ ++ if (au_ibstart(dir) == au_dbstart(dentry)) ++ au_cpup_attr_timesizes(dir); ++ } ++ ++out_dentry: ++ dput(h_dentry); ++out_mnt: ++ vfsub_mnt_drop_write(h_mnt); ++out_parent: ++ di_write_unlock(parent); ++ dput(parent); ++ di_write_unlock(dentry); ++ if (!err) ++#if 0 ++ /* verbose coding for lock class name */ ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcNonDir_DIINFO); ++#else ++ ; ++#endif ++ else { ++ au_di_fin(dentry); ++ dentry->d_fsdata = NULL; ++ } ++out_si: ++ si_read_unlock(sb); ++out: ++ mutex_unlock(&dir->i_mutex); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_link_args { ++ aufs_bindex_t bdst, bsrc; ++ struct au_pin pin; ++ struct path h_path; ++ struct dentry *src_parent, *parent; ++}; ++ ++static int au_cpup_before_link(struct dentry *src_dentry, ++ struct au_link_args *a) ++{ ++ int err; ++ struct dentry *h_src_dentry; ++ struct au_cp_generic cpg = { ++ .dentry = src_dentry, ++ .bdst = a->bdst, ++ .bsrc = a->bsrc, ++ .len = -1, ++ .pin = &a->pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ ++ }; ++ ++ di_read_lock_parent(a->src_parent, AuLock_IR); ++ err = au_test_and_cpup_dirs(src_dentry, a->bdst); ++ if (unlikely(err)) ++ goto out; ++ ++ h_src_dentry = au_h_dptr(src_dentry, a->bsrc); ++ err = au_pin(&a->pin, src_dentry, a->bdst, ++ au_opt_udba(src_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&a->pin); ++ ++out: ++ di_read_unlock(a->src_parent, AuLock_IR); ++ return err; ++} ++ ++static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, ++ struct au_link_args *a) ++{ ++ int err; ++ unsigned char plink; ++ aufs_bindex_t bend; ++ struct dentry *h_src_dentry; ++ struct inode *h_inode, *inode, *delegated; ++ struct super_block *sb; ++ struct file *h_file; ++ ++ plink = 0; ++ h_inode = NULL; ++ sb = src_dentry->d_sb; ++ inode = src_dentry->d_inode; ++ if (au_ibstart(inode) <= a->bdst) ++ h_inode = au_h_iptr(inode, a->bdst); ++ if (!h_inode || !h_inode->i_nlink) { ++ /* copyup src_dentry as the name of dentry. */ ++ bend = au_dbend(dentry); ++ if (bend < a->bsrc) ++ au_set_dbend(dentry, a->bsrc); ++ au_set_h_dptr(dentry, a->bsrc, ++ dget(au_h_dptr(src_dentry, a->bsrc))); ++ dget(a->h_path.dentry); ++ au_set_h_dptr(dentry, a->bdst, NULL); ++ AuDbg("temporary d_inode...\n"); ++ spin_lock(&dentry->d_lock); ++ dentry->d_inode = src_dentry->d_inode; /* tmp */ ++ spin_unlock(&dentry->d_lock); ++ h_file = au_h_open_pre(dentry, a->bsrc, /*force_wr*/0); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = a->bdst, ++ .bsrc = -1, ++ .len = -1, ++ .pin = &a->pin, ++ .flags = AuCpup_KEEPLINO ++ }; ++ err = au_sio_cpup_simple(&cpg); ++ au_h_open_post(dentry, a->bsrc, h_file); ++ if (!err) { ++ dput(a->h_path.dentry); ++ a->h_path.dentry = au_h_dptr(dentry, a->bdst); ++ } else ++ au_set_h_dptr(dentry, a->bdst, ++ a->h_path.dentry); ++ } ++ spin_lock(&dentry->d_lock); ++ dentry->d_inode = NULL; /* restore */ ++ spin_unlock(&dentry->d_lock); ++ AuDbg("temporary d_inode...done\n"); ++ au_set_h_dptr(dentry, a->bsrc, NULL); ++ au_set_dbend(dentry, bend); ++ } else { ++ /* the inode of src_dentry already exists on a.bdst branch */ ++ h_src_dentry = d_find_alias(h_inode); ++ if (!h_src_dentry && au_plink_test(inode)) { ++ plink = 1; ++ h_src_dentry = au_plink_lkup(inode, a->bdst); ++ err = PTR_ERR(h_src_dentry); ++ if (IS_ERR(h_src_dentry)) ++ goto out; ++ ++ if (unlikely(!h_src_dentry->d_inode)) { ++ dput(h_src_dentry); ++ h_src_dentry = NULL; ++ } ++ ++ } ++ if (h_src_dentry) { ++ delegated = NULL; ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ dput(h_src_dentry); ++ } else { ++ AuIOErr("no dentry found for hi%lu on b%d\n", ++ h_inode->i_ino, a->bdst); ++ err = -EIO; ++ } ++ } ++ ++ if (!err && !plink) ++ au_plink_append(inode, a->bdst, a->h_path.dentry); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int aufs_link(struct dentry *src_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ int err, rerr; ++ struct au_dtime dt; ++ struct au_link_args *a; ++ struct dentry *wh_dentry, *h_src_dentry; ++ struct inode *inode, *delegated; ++ struct super_block *sb; ++ struct au_wr_dir_args wr_dir_args = { ++ /* .force_btgt = -1, */ ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ IMustLock(dir); ++ inode = src_dentry->d_inode; ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ a->parent = dentry->d_parent; /* dir inode is locked */ ++ err = aufs_read_and_write_lock2(dentry, src_dentry, ++ AuLock_NOPLM | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_kfree; ++ err = au_d_linkable(src_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ a->src_parent = dget_parent(src_dentry); ++ wr_dir_args.force_btgt = au_ibstart(inode); ++ ++ di_write_lock_parent(a->parent); ++ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, ++ &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ a->bdst = au_dbstart(dentry); ++ a->h_path.dentry = au_h_dptr(dentry, a->bdst); ++ a->h_path.mnt = au_sbr_mnt(sb, a->bdst); ++ a->bsrc = au_ibstart(inode); ++ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); ++ if (!h_src_dentry && au_di(src_dentry)->di_tmpfile) ++ h_src_dentry = dget(au_hi_wh(inode, a->bsrc)); ++ if (!h_src_dentry) { ++ a->bsrc = au_dbstart(src_dentry); ++ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); ++ AuDebugOn(!h_src_dentry); ++ } else if (IS_ERR(h_src_dentry)) { ++ err = PTR_ERR(h_src_dentry); ++ goto out_parent; ++ } ++ ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ if (a->bdst < a->bsrc ++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) ++ err = au_cpup_or_link(src_dentry, dentry, a); ++ else { ++ delegated = NULL; ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ } ++ dput(h_src_dentry); ++ } else { ++ /* ++ * copyup src_dentry to the branch we process, ++ * and then link(2) to it. ++ */ ++ dput(h_src_dentry); ++ if (a->bdst < a->bsrc ++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { ++ au_unpin(&a->pin); ++ di_write_unlock(a->parent); ++ err = au_cpup_before_link(src_dentry, a); ++ di_write_lock_parent(a->parent); ++ if (!err) ++ err = au_pin(&a->pin, dentry, a->bdst, ++ au_opt_udba(sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_wh; ++ } ++ if (!err) { ++ h_src_dentry = au_h_dptr(src_dentry, a->bdst); ++ err = -ENOENT; ++ if (h_src_dentry && h_src_dentry->d_inode) { ++ delegated = NULL; ++ err = vfsub_link(h_src_dentry, ++ au_pinned_h_dir(&a->pin), ++ &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry" ++ " for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ } ++ } ++ } ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ if (wh_dentry) { ++ a->h_path.dentry = wh_dentry; ++ err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, ++ dentry); ++ if (unlikely(err)) ++ goto out_revert; ++ } ++ ++ au_dir_ts(dir, a->bdst); ++ dir->i_version++; ++ inc_nlink(inode); ++ inode->i_ctime = dir->i_ctime; ++ d_instantiate(dentry, au_igrab(inode)); ++ if (d_unhashed(a->h_path.dentry)) ++ /* some filesystem calls d_drop() */ ++ d_drop(dentry); ++ /* some filesystems consume an inode even hardlink */ ++ au_fhsm_wrote(sb, a->bdst, /*force*/0); ++ goto out_unpin; /* success */ ++ ++out_revert: ++ /* no delegation since it is just created */ ++ rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, ++ /*delegated*/NULL, /*force*/0); ++ if (unlikely(rerr)) { ++ AuIOErr("%pd reverting failed(%d, %d)\n", dentry, err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&dt); ++out_unpin: ++ au_unpin(&a->pin); ++out_wh: ++ dput(wh_dentry); ++out_parent: ++ di_write_unlock(a->parent); ++ dput(a->src_parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ aufs_read_and_write_unlock2(dentry, src_dentry); ++out_kfree: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ++{ ++ int err, rerr; ++ aufs_bindex_t bindex; ++ unsigned char diropq; ++ struct path h_path; ++ struct dentry *wh_dentry, *parent, *opq_dentry; ++ struct mutex *h_mtx; ++ struct super_block *sb; ++ struct { ++ struct au_pin pin; ++ struct au_dtime dt; ++ } *a; /* reduce the stack usage */ ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR ++ }; ++ ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, ++ &a->pin, &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ sb = dentry->d_sb; ++ bindex = au_dbstart(dentry); ++ h_path.dentry = au_h_dptr(dentry, bindex); ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ /* make the dir opaque */ ++ diropq = 0; ++ h_mtx = &h_path.dentry->d_inode->i_mutex; ++ if (wh_dentry ++ || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ opq_dentry = au_diropq_create(dentry, bindex); ++ mutex_unlock(h_mtx); ++ err = PTR_ERR(opq_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out_dir; ++ dput(opq_dentry); ++ diropq = 1; ++ } ++ ++ err = epilog(dir, bindex, wh_dentry, dentry); ++ if (!err) { ++ inc_nlink(dir); ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ if (diropq) { ++ AuLabel(revert opq); ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(dentry, bindex); ++ mutex_unlock(h_mtx); ++ if (rerr) { ++ AuIOErr("%pd reverting diropq failed(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } ++ } ++ ++out_dir: ++ AuLabel(revert dir); ++ rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); ++ if (rerr) { ++ AuIOErr("%pd reverting dir failed(%d, %d)\n", ++ dentry, err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&a->dt); ++out_unpin: ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++out_parent: ++ di_write_unlock(parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/i_op.c linux-4.0.4/fs/aufs/i_op.c +--- linux-4.0.4.orig/fs/aufs/i_op.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/i_op.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,1432 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations (except add/del/rename) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++static int h_permission(struct inode *h_inode, int mask, ++ struct vfsmount *h_mnt, int brperm) ++{ ++ int err; ++ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); ++ ++ err = -EACCES; ++ if ((write_mask && IS_IMMUTABLE(h_inode)) ++ || ((mask & MAY_EXEC) ++ && S_ISREG(h_inode->i_mode) ++ && ((h_mnt->mnt_flags & MNT_NOEXEC) ++ || !(h_inode->i_mode & S_IXUGO)))) ++ goto out; ++ ++ /* ++ * - skip the lower fs test in the case of write to ro branch. ++ * - nfs dir permission write check is optimized, but a policy for ++ * link/rename requires a real check. ++ * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' ++ * in this case, generic_permission() returns -EOPNOTSUPP. ++ */ ++ if ((write_mask && !au_br_writable(brperm)) ++ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) ++ && write_mask && !(mask & MAY_READ)) ++ || !h_inode->i_op->permission) { ++ /* AuLabel(generic_permission); */ ++ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ ++ err = generic_permission(h_inode, mask); ++ if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) ++ err = h_inode->i_op->permission(h_inode, mask); ++ AuTraceErr(err); ++ } else { ++ /* AuLabel(h_inode->permission); */ ++ err = h_inode->i_op->permission(h_inode, mask); ++ AuTraceErr(err); ++ } ++ ++ if (!err) ++ err = devcgroup_inode_permission(h_inode, mask); ++ if (!err) ++ err = security_inode_permission(h_inode, mask); ++ ++#if 0 ++ if (!err) { ++ /* todo: do we need to call ima_path_check()? */ ++ struct path h_path = { ++ .dentry = ++ .mnt = h_mnt ++ }; ++ err = ima_path_check(&h_path, ++ mask & (MAY_READ | MAY_WRITE | MAY_EXEC), ++ IMA_COUNT_LEAVE); ++ } ++#endif ++ ++out: ++ return err; ++} ++ ++static int aufs_permission(struct inode *inode, int mask) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ const unsigned char isdir = !!S_ISDIR(inode->i_mode), ++ write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); ++ struct inode *h_inode; ++ struct super_block *sb; ++ struct au_branch *br; ++ ++ /* todo: support rcu-walk? */ ++ if (mask & MAY_NOT_BLOCK) ++ return -ECHILD; ++ ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_read_lock_child(inode); ++#if 0 ++ err = au_iigen_test(inode, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out; ++#endif ++ ++ if (!isdir ++ || write_mask ++ || au_opt_test(au_mntflags(sb), DIRPERM1)) { ++ err = au_busy_or_stale(); ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ if (unlikely(!h_inode ++ || (h_inode->i_mode & S_IFMT) ++ != (inode->i_mode & S_IFMT))) ++ goto out; ++ ++ err = 0; ++ bindex = au_ibstart(inode); ++ br = au_sbr(sb, bindex); ++ err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); ++ if (write_mask ++ && !err ++ && !special_file(h_inode->i_mode)) { ++ /* test whether the upper writable branch exists */ ++ err = -EROFS; ++ for (; bindex >= 0; bindex--) ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = 0; ++ break; ++ } ++ } ++ goto out; ++ } ++ ++ /* non-write to dir */ ++ err = 0; ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode) { ++ err = au_busy_or_stale(); ++ if (unlikely(!S_ISDIR(h_inode->i_mode))) ++ break; ++ ++ br = au_sbr(sb, bindex); ++ err = h_permission(h_inode, mask, au_br_mnt(br), ++ br->br_perm); ++ } ++ } ++ ++out: ++ ii_read_unlock(inode); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, ++ unsigned int flags) ++{ ++ struct dentry *ret, *parent; ++ struct inode *inode; ++ struct super_block *sb; ++ int err, npositive; ++ ++ IMustLock(dir); ++ ++ /* todo: support rcu-walk? */ ++ ret = ERR_PTR(-ECHILD); ++ if (flags & LOOKUP_RCU) ++ goto out; ++ ++ ret = ERR_PTR(-ENAMETOOLONG); ++ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ goto out; ++ ++ sb = dir->i_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_di_init(dentry); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_si; ++ ++ inode = NULL; ++ npositive = 0; /* suppress a warning */ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_alive_dir(parent); ++ if (!err) ++ err = au_digen_test(parent, au_sigen(sb)); ++ if (!err) { ++ npositive = au_lkup_dentry(dentry, au_dbstart(parent), ++ /*type*/0); ++ err = npositive; ++ } ++ di_read_unlock(parent, AuLock_IR); ++ ret = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ ++ if (npositive) { ++ inode = au_new_inode(dentry, /*must_new*/0); ++ if (IS_ERR(inode)) { ++ ret = (void *)inode; ++ inode = NULL; ++ goto out_unlock; ++ } ++ } ++ ++ if (inode) ++ atomic_inc(&inode->i_count); ++ ret = d_splice_alias(inode, dentry); ++#if 0 ++ if (unlikely(d_need_lookup(dentry))) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_NEED_LOOKUP; ++ spin_unlock(&dentry->d_lock); ++ } else ++#endif ++ if (inode) { ++ if (!IS_ERR(ret)) { ++ iput(inode); ++ if (ret && ret != dentry) ++ ii_write_unlock(inode); ++ } else { ++ ii_write_unlock(inode); ++ iput(inode); ++ inode = NULL; ++ } ++ } ++ ++out_unlock: ++ di_write_unlock(dentry); ++ if (inode) { ++ /* verbose coding for lock class name */ ++ if (unlikely(S_ISLNK(inode->i_mode))) ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcSymlink_DIINFO); ++ else if (unlikely(S_ISDIR(inode->i_mode))) ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcDir_DIINFO); ++ else /* likely */ ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcNonDir_DIINFO); ++ } ++out_si: ++ si_read_unlock(sb); ++out: ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct aopen_node { ++ struct hlist_node hlist; ++ struct file *file, *h_file; ++}; ++ ++static int au_do_aopen(struct inode *inode, struct file *file) ++{ ++ struct au_sphlhead *aopen; ++ struct aopen_node *node; ++ struct au_do_open_args args = { ++ .no_lock = 1, ++ .open = au_do_open_nondir ++ }; ++ ++ aopen = &au_sbi(inode->i_sb)->si_aopen; ++ spin_lock(&aopen->spin); ++ hlist_for_each_entry(node, &aopen->head, hlist) ++ if (node->file == file) { ++ args.h_file = node->h_file; ++ break; ++ } ++ spin_unlock(&aopen->spin); ++ /* AuDebugOn(!args.h_file); */ ++ ++ return au_do_open(file, &args); ++} ++ ++static int aufs_atomic_open(struct inode *dir, struct dentry *dentry, ++ struct file *file, unsigned int open_flag, ++ umode_t create_mode, int *opened) ++{ ++ int err, h_opened = *opened; ++ struct dentry *parent; ++ struct dentry *d; ++ struct au_sphlhead *aopen; ++ struct vfsub_aopen_args args = { ++ .open_flag = open_flag, ++ .create_mode = create_mode, ++ .opened = &h_opened ++ }; ++ struct aopen_node aopen_node = { ++ .file = file ++ }; ++ ++ IMustLock(dir); ++ AuDbg("open_flag 0x%x\n", open_flag); ++ AuDbgDentry(dentry); ++ ++ err = 0; ++ if (!au_di(dentry)) { ++ d = aufs_lookup(dir, dentry, /*flags*/0); ++ if (IS_ERR(d)) { ++ err = PTR_ERR(d); ++ goto out; ++ } else if (d) { ++ /* ++ * obsoleted dentry found. ++ * another error will be returned later. ++ */ ++ d_drop(d); ++ dput(d); ++ AuDbgDentry(d); ++ } ++ AuDbgDentry(dentry); ++ } ++ ++ if (d_is_positive(dentry) ++ || d_unhashed(dentry) ++ || d_unlinked(dentry) ++ || !(open_flag & O_CREAT)) ++ goto out_no_open; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ ++ parent = dentry->d_parent; /* dir is locked */ ++ di_write_lock_parent(parent); ++ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ AuDbgDentry(dentry); ++ if (d_is_positive(dentry)) ++ goto out_unlock; ++ ++ args.file = get_empty_filp(); ++ err = PTR_ERR(args.file); ++ if (IS_ERR(args.file)) ++ goto out_unlock; ++ ++ args.file->f_flags = file->f_flags; ++ err = au_aopen_or_create(dir, dentry, &args); ++ AuTraceErr(err); ++ AuDbgFile(args.file); ++ if (unlikely(err < 0)) { ++ if (h_opened & FILE_OPENED) ++ fput(args.file); ++ else ++ put_filp(args.file); ++ goto out_unlock; ++ } ++ ++ /* some filesystems don't set FILE_CREATED while succeeded? */ ++ *opened |= FILE_CREATED; ++ if (h_opened & FILE_OPENED) ++ aopen_node.h_file = args.file; ++ else { ++ put_filp(args.file); ++ args.file = NULL; ++ } ++ aopen = &au_sbi(dir->i_sb)->si_aopen; ++ au_sphl_add(&aopen_node.hlist, aopen); ++ err = finish_open(file, dentry, au_do_aopen, opened); ++ au_sphl_del(&aopen_node.hlist, aopen); ++ AuTraceErr(err); ++ AuDbgFile(file); ++ if (aopen_node.h_file) ++ fput(aopen_node.h_file); ++ ++out_unlock: ++ di_write_unlock(parent); ++ aufs_read_unlock(dentry, AuLock_DW); ++ AuDbgDentry(dentry); ++ if (unlikely(err)) ++ goto out; ++out_no_open: ++ if (!err && !(*opened & FILE_CREATED)) { ++ AuLabel(out_no_open); ++ dget(dentry); ++ err = finish_no_open(file, dentry); ++ } ++out: ++ AuDbg("%pd%s%s\n", dentry, ++ (*opened & FILE_CREATED) ? " created" : "", ++ (*opened & FILE_OPENED) ? " opened" : ""); ++ AuTraceErr(err); ++ return err; ++} ++ ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, ++ const unsigned char add_entry, aufs_bindex_t bcpup, ++ aufs_bindex_t bstart) ++{ ++ int err; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ ++ if (add_entry) ++ IMustLock(parent->d_inode); ++ else ++ di_write_lock_parent(parent); ++ ++ err = 0; ++ if (!au_h_dptr(parent, bcpup)) { ++ if (bstart > bcpup) ++ err = au_cpup_dirs(dentry, bcpup); ++ else if (bstart < bcpup) ++ err = au_cpdown_dirs(dentry, bcpup); ++ else ++ BUG(); ++ } ++ if (!err && add_entry && !au_ftest_wrdir(add_entry, TMPFILE)) { ++ h_parent = au_h_dptr(parent, bcpup); ++ h_dir = h_parent->d_inode; ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ err = au_lkup_neg(dentry, bcpup, /*wh*/0); ++ /* todo: no unlock here */ ++ mutex_unlock(&h_dir->i_mutex); ++ ++ AuDbg("bcpup %d\n", bcpup); ++ if (!err) { ++ if (!dentry->d_inode) ++ au_set_h_dptr(dentry, bstart, NULL); ++ au_update_dbrange(dentry, /*do_put_zero*/0); ++ } ++ } ++ ++ if (!add_entry) ++ di_write_unlock(parent); ++ if (!err) ++ err = bcpup; /* success */ ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * decide the branch and the parent dir where we will create a new entry. ++ * returns new bindex or an error. ++ * copyup the parent dir if needed. ++ */ ++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, ++ struct au_wr_dir_args *args) ++{ ++ int err; ++ unsigned int flags; ++ aufs_bindex_t bcpup, bstart, src_bstart; ++ const unsigned char add_entry ++ = au_ftest_wrdir(args->flags, ADD_ENTRY) ++ | au_ftest_wrdir(args->flags, TMPFILE); ++ struct super_block *sb; ++ struct dentry *parent; ++ struct au_sbinfo *sbinfo; ++ ++ sb = dentry->d_sb; ++ sbinfo = au_sbi(sb); ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(dentry); ++ bcpup = bstart; ++ if (args->force_btgt < 0) { ++ if (src_dentry) { ++ src_bstart = au_dbstart(src_dentry); ++ if (src_bstart < bstart) ++ bcpup = src_bstart; ++ } else if (add_entry) { ++ flags = 0; ++ if (au_ftest_wrdir(args->flags, ISDIR)) ++ au_fset_wbr(flags, DIR); ++ err = AuWbrCreate(sbinfo, dentry, flags); ++ bcpup = err; ++ } ++ ++ if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) { ++ if (add_entry) ++ err = AuWbrCopyup(sbinfo, dentry); ++ else { ++ if (!IS_ROOT(dentry)) { ++ di_read_lock_parent(parent, !AuLock_IR); ++ err = AuWbrCopyup(sbinfo, dentry); ++ di_read_unlock(parent, !AuLock_IR); ++ } else ++ err = AuWbrCopyup(sbinfo, dentry); ++ } ++ bcpup = err; ++ if (unlikely(err < 0)) ++ goto out; ++ } ++ } else { ++ bcpup = args->force_btgt; ++ AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode)); ++ } ++ ++ AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); ++ err = bcpup; ++ if (bcpup == bstart) ++ goto out; /* success */ ++ ++ /* copyup the new parent into the branch we process */ ++ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); ++ if (err >= 0) { ++ if (!dentry->d_inode) { ++ au_set_h_dptr(dentry, bstart, NULL); ++ au_set_dbstart(dentry, bcpup); ++ au_set_dbend(dentry, bcpup); ++ } ++ AuDebugOn(add_entry ++ && !au_ftest_wrdir(args->flags, TMPFILE) ++ && !au_h_dptr(dentry, bcpup)); ++ } ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_pin_hdir_unlock(struct au_pin *p) ++{ ++ if (p->hdir) ++ au_hn_imtx_unlock(p->hdir); ++} ++ ++int au_pin_hdir_lock(struct au_pin *p) ++{ ++ int err; ++ ++ err = 0; ++ if (!p->hdir) ++ goto out; ++ ++ /* even if an error happens later, keep this lock */ ++ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); ++ ++ err = -EBUSY; ++ if (unlikely(p->hdir->hi_inode != p->h_parent->d_inode)) ++ goto out; ++ ++ err = 0; ++ if (p->h_dentry) ++ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, ++ p->h_parent, p->br); ++ ++out: ++ return err; ++} ++ ++int au_pin_hdir_relock(struct au_pin *p) ++{ ++ int err, i; ++ struct inode *h_i; ++ struct dentry *h_d[] = { ++ p->h_dentry, ++ p->h_parent ++ }; ++ ++ err = au_pin_hdir_lock(p); ++ if (unlikely(err)) ++ goto out; ++ ++ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { ++ if (!h_d[i]) ++ continue; ++ h_i = h_d[i]->d_inode; ++ if (h_i) ++ err = !h_i->i_nlink; ++ } ++ ++out: ++ return err; ++} ++ ++void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) ++{ ++#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) ++ p->hdir->hi_inode->i_mutex.owner = task; ++#endif ++} ++ ++void au_pin_hdir_acquire_nest(struct au_pin *p) ++{ ++ if (p->hdir) { ++ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, ++ p->lsc_hi, 0, NULL, _RET_IP_); ++ au_pin_hdir_set_owner(p, current); ++ } ++} ++ ++void au_pin_hdir_release(struct au_pin *p) ++{ ++ if (p->hdir) { ++ au_pin_hdir_set_owner(p, p->task); ++ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); ++ } ++} ++ ++struct dentry *au_pinned_h_parent(struct au_pin *pin) ++{ ++ if (pin && pin->parent) ++ return au_h_dptr(pin->parent, pin->bindex); ++ return NULL; ++} ++ ++void au_unpin(struct au_pin *p) ++{ ++ if (p->hdir) ++ au_pin_hdir_unlock(p); ++ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) ++ vfsub_mnt_drop_write(p->h_mnt); ++ if (!p->hdir) ++ return; ++ ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ iput(p->hdir->hi_inode); ++ dput(p->parent); ++ p->parent = NULL; ++ p->hdir = NULL; ++ p->h_mnt = NULL; ++ /* do not clear p->task */ ++} ++ ++int au_do_pin(struct au_pin *p) ++{ ++ int err; ++ struct super_block *sb; ++ struct inode *h_dir; ++ ++ err = 0; ++ sb = p->dentry->d_sb; ++ p->br = au_sbr(sb, p->bindex); ++ if (IS_ROOT(p->dentry)) { ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = au_br_mnt(p->br); ++ err = vfsub_mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ goto out_err; ++ } ++ } ++ goto out; ++ } ++ ++ p->h_dentry = NULL; ++ if (p->bindex <= au_dbend(p->dentry)) ++ p->h_dentry = au_h_dptr(p->dentry, p->bindex); ++ ++ p->parent = dget_parent(p->dentry); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_lock(p->parent, AuLock_IR, p->lsc_di); ++ ++ h_dir = NULL; ++ p->h_parent = au_h_dptr(p->parent, p->bindex); ++ p->hdir = au_hi(p->parent->d_inode, p->bindex); ++ if (p->hdir) ++ h_dir = p->hdir->hi_inode; ++ ++ /* ++ * udba case, or ++ * if DI_LOCKED is not set, then p->parent may be different ++ * and h_parent can be NULL. ++ */ ++ if (unlikely(!p->hdir || !h_dir || !p->h_parent)) { ++ err = -EBUSY; ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ dput(p->parent); ++ p->parent = NULL; ++ goto out_err; ++ } ++ ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = au_br_mnt(p->br); ++ err = vfsub_mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ dput(p->parent); ++ p->parent = NULL; ++ goto out_err; ++ } ++ } ++ ++ au_igrab(h_dir); ++ err = au_pin_hdir_lock(p); ++ if (!err) ++ goto out; /* success */ ++ ++ au_unpin(p); ++ ++out_err: ++ pr_err("err %d\n", err); ++ err = au_busy_or_stale(); ++out: ++ return err; ++} ++ ++void au_pin_init(struct au_pin *p, struct dentry *dentry, ++ aufs_bindex_t bindex, int lsc_di, int lsc_hi, ++ unsigned int udba, unsigned char flags) ++{ ++ p->dentry = dentry; ++ p->udba = udba; ++ p->lsc_di = lsc_di; ++ p->lsc_hi = lsc_hi; ++ p->flags = flags; ++ p->bindex = bindex; ++ ++ p->parent = NULL; ++ p->hdir = NULL; ++ p->h_mnt = NULL; ++ ++ p->h_dentry = NULL; ++ p->h_parent = NULL; ++ p->br = NULL; ++ p->task = current; ++} ++ ++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int udba, unsigned char flags) ++{ ++ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, ++ udba, flags); ++ return au_do_pin(pin); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * ->setattr() and ->getattr() are called in various cases. ++ * chmod, stat: dentry is revalidated. ++ * fchmod, fstat: file and dentry are not revalidated, additionally they may be ++ * unhashed. ++ * for ->setattr(), ia->ia_file is passed from ftruncate only. ++ */ ++/* todo: consolidate with do_refresh() and simple_reval_dpath() */ ++int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct inode *inode; ++ struct dentry *parent; ++ ++ err = 0; ++ inode = dentry->d_inode; ++ if (au_digen_test(dentry, sigen)) { ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_refresh_dentry(dentry, parent); ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, ++ struct au_icpup_args *a) ++{ ++ int err; ++ loff_t sz; ++ aufs_bindex_t bstart, ibstart; ++ struct dentry *hi_wh, *parent; ++ struct inode *inode; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = 0 ++ }; ++ ++ if (d_is_dir(dentry)) ++ au_fset_wrdir(wr_dir_args.flags, ISDIR); ++ /* plink or hi_wh() case */ ++ bstart = au_dbstart(dentry); ++ inode = dentry->d_inode; ++ ibstart = au_ibstart(inode); ++ if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode)) ++ wr_dir_args.force_btgt = ibstart; ++ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); ++ if (unlikely(err < 0)) ++ goto out; ++ a->btgt = err; ++ if (err != bstart) ++ au_fset_icpup(a->flags, DID_CPUP); ++ ++ err = 0; ++ a->pin_flags = AuPin_MNT_WRITE; ++ parent = NULL; ++ if (!IS_ROOT(dentry)) { ++ au_fset_pin(a->pin_flags, DI_LOCKED); ++ parent = dget_parent(dentry); ++ di_write_lock_parent(parent); ++ } ++ ++ err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ a->h_path.dentry = au_h_dptr(dentry, bstart); ++ a->h_inode = a->h_path.dentry->d_inode; ++ sz = -1; ++ if (ia && (ia->ia_valid & ATTR_SIZE)) { ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ if (ia->ia_size < i_size_read(a->h_inode)) ++ sz = ia->ia_size; ++ mutex_unlock(&a->h_inode->i_mutex); ++ } ++ ++ hi_wh = NULL; ++ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { ++ hi_wh = au_hi_wh(inode, a->btgt); ++ if (!hi_wh) { ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = a->btgt, ++ .bsrc = -1, ++ .len = sz, ++ .pin = &a->pin ++ }; ++ err = au_sio_cpup_wh(&cpg, /*file*/NULL); ++ if (unlikely(err)) ++ goto out_unlock; ++ hi_wh = au_hi_wh(inode, a->btgt); ++ /* todo: revalidate hi_wh? */ ++ } ++ } ++ ++ if (parent) { ++ au_pin_set_parent_lflag(&a->pin, /*lflag*/0); ++ di_downgrade_lock(parent, AuLock_IR); ++ dput(parent); ++ parent = NULL; ++ } ++ if (!au_ftest_icpup(a->flags, DID_CPUP)) ++ goto out; /* success */ ++ ++ if (!d_unhashed(dentry)) { ++ struct au_cp_generic cpg = { ++ .dentry = dentry, ++ .bdst = a->btgt, ++ .bsrc = bstart, ++ .len = sz, ++ .pin = &a->pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ err = au_sio_cpup_simple(&cpg); ++ if (!err) ++ a->h_path.dentry = au_h_dptr(dentry, a->btgt); ++ } else if (!hi_wh) ++ a->h_path.dentry = au_h_dptr(dentry, a->btgt); ++ else ++ a->h_path.dentry = hi_wh; /* do not dget here */ ++ ++out_unlock: ++ a->h_inode = a->h_path.dentry->d_inode; ++ if (!err) ++ goto out; /* success */ ++ au_unpin(&a->pin); ++out_parent: ++ if (parent) { ++ di_write_unlock(parent); ++ dput(parent); ++ } ++out: ++ if (!err) ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ return err; ++} ++ ++static int aufs_setattr(struct dentry *dentry, struct iattr *ia) ++{ ++ int err; ++ struct inode *inode, *delegated; ++ struct super_block *sb; ++ struct file *file; ++ struct au_icpup_args *a; ++ ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) ++ ia->ia_valid &= ~ATTR_MODE; ++ ++ file = NULL; ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_kfree; ++ ++ if (ia->ia_valid & ATTR_FILE) { ++ /* currently ftruncate(2) only */ ++ AuDebugOn(!d_is_reg(dentry)); ++ file = ia->ia_file; ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out_si; ++ ia->ia_file = au_hf_top(file); ++ a->udba = AuOpt_UDBA_NONE; ++ } else { ++ /* fchmod() doesn't pass ia_file */ ++ a->udba = au_opt_udba(sb); ++ di_write_lock_child(dentry); ++ /* no d_unlinked(), to set UDBA_NONE for root */ ++ if (d_unhashed(dentry)) ++ a->udba = AuOpt_UDBA_NONE; ++ if (a->udba != AuOpt_UDBA_NONE) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_reval_for_attr(dentry, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out_dentry; ++ } ++ } ++ ++ err = au_pin_and_icpup(dentry, ia, a); ++ if (unlikely(err < 0)) ++ goto out_dentry; ++ if (au_ftest_icpup(a->flags, DID_CPUP)) { ++ ia->ia_file = NULL; ++ ia->ia_valid &= ~ATTR_FILE; ++ } ++ ++ a->h_path.mnt = au_sbr_mnt(sb, a->btgt); ++ if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) ++ == (ATTR_MODE | ATTR_CTIME)) { ++ err = security_path_chmod(&a->h_path, ia->ia_mode); ++ if (unlikely(err)) ++ goto out_unlock; ++ } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) ++ && (ia->ia_valid & ATTR_CTIME)) { ++ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); ++ if (unlikely(err)) ++ goto out_unlock; ++ } ++ ++ if (ia->ia_valid & ATTR_SIZE) { ++ struct file *f; ++ ++ if (ia->ia_size < i_size_read(inode)) ++ /* unmap only */ ++ truncate_setsize(inode, ia->ia_size); ++ ++ f = NULL; ++ if (ia->ia_valid & ATTR_FILE) ++ f = ia->ia_file; ++ mutex_unlock(&a->h_inode->i_mutex); ++ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ } else { ++ delegated = NULL; ++ while (1) { ++ err = vfsub_notify_change(&a->h_path, ia, &delegated); ++ if (delegated) { ++ err = break_deleg_wait(&delegated); ++ if (!err) ++ continue; ++ } ++ break; ++ } ++ } ++ if (!err) ++ au_cpup_attr_changeable(inode); ++ ++out_unlock: ++ mutex_unlock(&a->h_inode->i_mutex); ++ au_unpin(&a->pin); ++ if (unlikely(err)) ++ au_update_dbstart(dentry); ++out_dentry: ++ di_write_unlock(dentry); ++ if (file) { ++ fi_write_unlock(file); ++ ia->ia_file = file; ++ ia->ia_valid |= ATTR_FILE; ++ } ++out_si: ++ si_read_unlock(sb); ++out_kfree: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++#if IS_ENABLED(CONFIG_AUFS_XATTR) || IS_ENABLED(CONFIG_FS_POSIX_ACL) ++static int au_h_path_to_set_attr(struct dentry *dentry, ++ struct au_icpup_args *a, struct path *h_path) ++{ ++ int err; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ a->udba = au_opt_udba(sb); ++ /* no d_unlinked(), to set UDBA_NONE for root */ ++ if (d_unhashed(dentry)) ++ a->udba = AuOpt_UDBA_NONE; ++ if (a->udba != AuOpt_UDBA_NONE) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_reval_for_attr(dentry, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out; ++ } ++ err = au_pin_and_icpup(dentry, /*ia*/NULL, a); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ h_path->dentry = a->h_path.dentry; ++ h_path->mnt = au_sbr_mnt(sb, a->btgt); ++ ++out: ++ return err; ++} ++ ++ssize_t au_srxattr(struct dentry *dentry, struct au_srxattr *arg) ++{ ++ int err; ++ struct path h_path; ++ struct super_block *sb; ++ struct au_icpup_args *a; ++ struct inode *inode, *h_inode; ++ ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_kfree; ++ ++ h_path.dentry = NULL; /* silence gcc */ ++ di_write_lock_child(dentry); ++ err = au_h_path_to_set_attr(dentry, a, &h_path); ++ if (unlikely(err)) ++ goto out_di; ++ ++ mutex_unlock(&a->h_inode->i_mutex); ++ switch (arg->type) { ++ case AU_XATTR_SET: ++ err = vfsub_setxattr(h_path.dentry, ++ arg->u.set.name, arg->u.set.value, ++ arg->u.set.size, arg->u.set.flags); ++ break; ++ case AU_XATTR_REMOVE: ++ err = vfsub_removexattr(h_path.dentry, arg->u.remove.name); ++ break; ++ case AU_ACL_SET: ++ err = -EOPNOTSUPP; ++ h_inode = h_path.dentry->d_inode; ++ if (h_inode->i_op->set_acl) ++ err = h_inode->i_op->set_acl(h_inode, ++ arg->u.acl_set.acl, ++ arg->u.acl_set.type); ++ break; ++ } ++ if (!err) ++ au_cpup_attr_timesizes(inode); ++ ++ au_unpin(&a->pin); ++ if (unlikely(err)) ++ au_update_dbstart(dentry); ++ ++out_di: ++ di_write_unlock(dentry); ++ si_read_unlock(sb); ++out_kfree: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++#endif ++ ++static void au_refresh_iattr(struct inode *inode, struct kstat *st, ++ unsigned int nlink) ++{ ++ unsigned int n; ++ ++ inode->i_mode = st->mode; ++ /* don't i_[ug]id_write() here */ ++ inode->i_uid = st->uid; ++ inode->i_gid = st->gid; ++ inode->i_atime = st->atime; ++ inode->i_mtime = st->mtime; ++ inode->i_ctime = st->ctime; ++ ++ au_cpup_attr_nlink(inode, /*force*/0); ++ if (S_ISDIR(inode->i_mode)) { ++ n = inode->i_nlink; ++ n -= nlink; ++ n += st->nlink; ++ smp_mb(); /* for i_nlink */ ++ /* 0 can happen */ ++ set_nlink(inode, n); ++ } ++ ++ spin_lock(&inode->i_lock); ++ inode->i_blocks = st->blocks; ++ i_size_write(inode, st->size); ++ spin_unlock(&inode->i_lock); ++} ++ ++/* ++ * common routine for aufs_getattr() and aufs_getxattr(). ++ * returns zero or negative (an error). ++ * @dentry will be read-locked in success. ++ */ ++int au_h_path_getattr(struct dentry *dentry, int force, struct path *h_path) ++{ ++ int err; ++ unsigned int mnt_flags, sigen; ++ unsigned char udba_none; ++ aufs_bindex_t bindex; ++ struct super_block *sb, *h_sb; ++ struct inode *inode; ++ ++ h_path->mnt = NULL; ++ h_path->dentry = NULL; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ mnt_flags = au_mntflags(sb); ++ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); ++ ++ /* support fstat(2) */ ++ if (!d_unlinked(dentry) && !udba_none) { ++ sigen = au_sigen(sb); ++ err = au_digen_test(dentry, sigen); ++ if (!err) { ++ di_read_lock_child(dentry, AuLock_IR); ++ err = au_dbrange_test(dentry); ++ if (unlikely(err)) { ++ di_read_unlock(dentry, AuLock_IR); ++ goto out; ++ } ++ } else { ++ AuDebugOn(IS_ROOT(dentry)); ++ di_write_lock_child(dentry); ++ err = au_dbrange_test(dentry); ++ if (!err) ++ err = au_reval_for_attr(dentry, sigen); ++ if (!err) ++ di_downgrade_lock(dentry, AuLock_IR); ++ else { ++ di_write_unlock(dentry); ++ goto out; ++ } ++ } ++ } else ++ di_read_lock_child(dentry, AuLock_IR); ++ ++ inode = dentry->d_inode; ++ bindex = au_ibstart(inode); ++ h_path->mnt = au_sbr_mnt(sb, bindex); ++ h_sb = h_path->mnt->mnt_sb; ++ if (!force ++ && !au_test_fs_bad_iattr(h_sb) ++ && udba_none) ++ goto out; /* success */ ++ ++ if (au_dbstart(dentry) == bindex) ++ h_path->dentry = au_h_dptr(dentry, bindex); ++ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { ++ h_path->dentry = au_plink_lkup(inode, bindex); ++ if (IS_ERR(h_path->dentry)) ++ /* pretending success */ ++ h_path->dentry = NULL; ++ else ++ dput(h_path->dentry); ++ } ++ ++out: ++ return err; ++} ++ ++static int aufs_getattr(struct vfsmount *mnt __maybe_unused, ++ struct dentry *dentry, struct kstat *st) ++{ ++ int err; ++ unsigned char positive; ++ struct path h_path; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ inode = dentry->d_inode; ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ err = au_h_path_getattr(dentry, /*force*/0, &h_path); ++ if (unlikely(err)) ++ goto out_si; ++ if (unlikely(!h_path.dentry)) ++ /* illegally overlapped or something */ ++ goto out_fill; /* pretending success */ ++ ++ positive = !!h_path.dentry->d_inode; ++ if (positive) ++ err = vfs_getattr(&h_path, st); ++ if (!err) { ++ if (positive) ++ au_refresh_iattr(inode, st, ++ h_path.dentry->d_inode->i_nlink); ++ goto out_fill; /* success */ ++ } ++ AuTraceErr(err); ++ goto out_di; ++ ++out_fill: ++ generic_fillattr(inode, st); ++out_di: ++ di_read_unlock(dentry, AuLock_IR); ++out_si: ++ si_read_unlock(sb); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int h_readlink(struct dentry *dentry, int bindex, char __user *buf, ++ int bufsiz) ++{ ++ int err; ++ struct super_block *sb; ++ struct dentry *h_dentry; ++ ++ err = -EINVAL; ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (unlikely(!h_dentry->d_inode->i_op->readlink)) ++ goto out; ++ ++ err = security_inode_readlink(h_dentry); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ if (!au_test_ro(sb, bindex, dentry->d_inode)) { ++ vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry); ++ fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode); ++ } ++ err = h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz); ++ ++out: ++ return err; ++} ++ ++static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) ++{ ++ int err; ++ ++ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ err = au_d_hashed_positive(dentry); ++ if (!err) ++ err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); ++ aufs_read_unlock(dentry, AuLock_IR); ++ ++out: ++ return err; ++} ++ ++static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int err; ++ mm_segment_t old_fs; ++ union { ++ char *k; ++ char __user *u; ++ } buf; ++ ++ err = -ENOMEM; ++ buf.k = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!buf.k)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_name; ++ ++ err = au_d_hashed_positive(dentry); ++ if (!err) { ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = h_readlink(dentry, au_dbstart(dentry), buf.u, PATH_MAX); ++ set_fs(old_fs); ++ } ++ aufs_read_unlock(dentry, AuLock_IR); ++ ++ if (err >= 0) { ++ buf.k[err] = 0; ++ /* will be freed by put_link */ ++ nd_set_link(nd, buf.k); ++ return NULL; /* success */ ++ } ++ ++out_name: ++ free_page((unsigned long)buf.k); ++out: ++ AuTraceErr(err); ++ return ERR_PTR(err); ++} ++ ++static void aufs_put_link(struct dentry *dentry __maybe_unused, ++ struct nameidata *nd, void *cookie __maybe_unused) ++{ ++ char *p; ++ ++ p = nd_get_link(nd); ++ if (!IS_ERR_OR_NULL(p)) ++ free_page((unsigned long)p); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) ++{ ++ int err; ++ struct super_block *sb; ++ struct inode *h_inode; ++ ++ sb = inode->i_sb; ++ /* mmap_sem might be acquired already, cf. aufs_mmap() */ ++ lockdep_off(); ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_write_lock_child(inode); ++ lockdep_on(); ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ err = vfsub_update_time(h_inode, ts, flags); ++ lockdep_off(); ++ if (!err) ++ au_cpup_attr_timesizes(inode); ++ ii_write_unlock(inode); ++ si_read_unlock(sb); ++ lockdep_on(); ++ ++ if (!err && (flags & S_VERSION)) ++ inode_inc_iversion(inode); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct inode_operations aufs_symlink_iop = { ++ .permission = aufs_permission, ++#ifdef CONFIG_FS_POSIX_ACL ++ .get_acl = aufs_get_acl, ++ .set_acl = aufs_set_acl, /* unsupport for symlink? */ ++#endif ++ ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ ++#ifdef CONFIG_AUFS_XATTR ++ .setxattr = aufs_setxattr, ++ .getxattr = aufs_getxattr, ++ .listxattr = aufs_listxattr, ++ .removexattr = aufs_removexattr, ++#endif ++ ++ .readlink = aufs_readlink, ++ .follow_link = aufs_follow_link, ++ .put_link = aufs_put_link, ++ ++ /* .update_time = aufs_update_time */ ++}; ++ ++struct inode_operations aufs_dir_iop = { ++ .create = aufs_create, ++ .lookup = aufs_lookup, ++ .link = aufs_link, ++ .unlink = aufs_unlink, ++ .symlink = aufs_symlink, ++ .mkdir = aufs_mkdir, ++ .rmdir = aufs_rmdir, ++ .mknod = aufs_mknod, ++ .rename = aufs_rename, ++ ++ .permission = aufs_permission, ++#ifdef CONFIG_FS_POSIX_ACL ++ .get_acl = aufs_get_acl, ++ .set_acl = aufs_set_acl, ++#endif ++ ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ ++#ifdef CONFIG_AUFS_XATTR ++ .setxattr = aufs_setxattr, ++ .getxattr = aufs_getxattr, ++ .listxattr = aufs_listxattr, ++ .removexattr = aufs_removexattr, ++#endif ++ ++ .update_time = aufs_update_time, ++ .atomic_open = aufs_atomic_open, ++ .tmpfile = aufs_tmpfile ++}; ++ ++struct inode_operations aufs_iop = { ++ .permission = aufs_permission, ++#ifdef CONFIG_FS_POSIX_ACL ++ .get_acl = aufs_get_acl, ++ .set_acl = aufs_set_acl, ++#endif ++ ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ ++#ifdef CONFIG_AUFS_XATTR ++ .setxattr = aufs_setxattr, ++ .getxattr = aufs_getxattr, ++ .listxattr = aufs_listxattr, ++ .removexattr = aufs_removexattr, ++#endif ++ ++ .update_time = aufs_update_time ++}; +diff -Nur linux-4.0.4.orig/fs/aufs/i_op_del.c linux-4.0.4/fs/aufs/i_op_del.c +--- linux-4.0.4.orig/fs/aufs/i_op_del.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/i_op_del.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,493 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operations (del entry) ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * decide if a new whiteout for @dentry is necessary or not. ++ * when it is necessary, prepare the parent dir for the upper branch whose ++ * branch index is @bcpup for creation. the actual creation of the whiteout will ++ * be done by caller. ++ * return value: ++ * 0: wh is unnecessary ++ * plus: wh is necessary ++ * minus: error ++ */ ++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) ++{ ++ int need_wh, err; ++ aufs_bindex_t bstart; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ if (*bcpup < 0) { ++ *bcpup = bstart; ++ if (au_test_ro(sb, bstart, dentry->d_inode)) { ++ err = AuWbrCopyup(au_sbi(sb), dentry); ++ *bcpup = err; ++ if (unlikely(err < 0)) ++ goto out; ++ } ++ } else ++ AuDebugOn(bstart < *bcpup ++ || au_test_ro(sb, *bcpup, dentry->d_inode)); ++ AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); ++ ++ if (*bcpup != bstart) { ++ err = au_cpup_dirs(dentry, *bcpup); ++ if (unlikely(err)) ++ goto out; ++ need_wh = 1; ++ } else { ++ struct au_dinfo *dinfo, *tmp; ++ ++ need_wh = -ENOMEM; ++ dinfo = au_di(dentry); ++ tmp = au_di_alloc(sb, AuLsc_DI_TMP); ++ if (tmp) { ++ au_di_cp(tmp, dinfo); ++ au_di_swap(tmp, dinfo); ++ /* returns the number of positive dentries */ ++ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0); ++ au_di_swap(tmp, dinfo); ++ au_rw_write_unlock(&tmp->di_rwsem); ++ au_di_free(tmp); ++ } ++ } ++ AuDbg("need_wh %d\n", need_wh); ++ err = need_wh; ++ ++out: ++ return err; ++} ++ ++/* ++ * simple tests for the del-entry operations. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir) ++{ ++ int err; ++ umode_t h_mode; ++ struct dentry *h_dentry, *h_latest; ++ struct inode *h_inode; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ h_inode = h_dentry->d_inode; ++ if (dentry->d_inode) { ++ err = -ENOENT; ++ if (unlikely(!h_inode || !h_inode->i_nlink)) ++ goto out; ++ ++ h_mode = h_inode->i_mode; ++ if (!isdir) { ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(h_mode))) ++ goto out; ++ } else if (unlikely(!S_ISDIR(h_mode))) { ++ err = -ENOTDIR; ++ goto out; ++ } ++ } else { ++ /* rename(2) case */ ++ err = -EIO; ++ if (unlikely(h_inode)) ++ goto out; ++ } ++ ++ err = -ENOENT; ++ /* expected parent dir is locked */ ++ if (unlikely(h_parent != h_dentry->d_parent)) ++ goto out; ++ err = 0; ++ ++ /* ++ * rmdir a dir may break the consistency on some filesystem. ++ * let's try heavy test. ++ */ ++ err = -EACCES; ++ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) ++ && au_test_h_perm(h_parent->d_inode, ++ MAY_EXEC | MAY_WRITE))) ++ goto out; ++ ++ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); ++ err = -EIO; ++ if (IS_ERR(h_latest)) ++ goto out; ++ if (h_latest == h_dentry) ++ err = 0; ++ dput(h_latest); ++ ++out: ++ return err; ++} ++ ++/* ++ * decide the branch where we operate for @dentry. the branch index will be set ++ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent ++ * dir for reverting. ++ * when a new whiteout is necessary, create it. ++ */ ++static struct dentry* ++lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, ++ struct au_dtime *dt, struct au_pin *pin) ++{ ++ struct dentry *wh_dentry; ++ struct super_block *sb; ++ struct path h_path; ++ int err, need_wh; ++ unsigned int udba; ++ aufs_bindex_t bcpup; ++ ++ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); ++ wh_dentry = ERR_PTR(need_wh); ++ if (unlikely(need_wh < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ udba = au_opt_udba(sb); ++ bcpup = *rbcpup; ++ err = au_pin(pin, dentry, bcpup, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ h_path.dentry = au_pinned_h_parent(pin); ++ if (udba != AuOpt_UDBA_NONE ++ && au_dbstart(dentry) == bcpup) { ++ err = au_may_del(dentry, bcpup, h_path.dentry, isdir); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_unpin; ++ } ++ ++ h_path.mnt = au_sbr_mnt(sb, bcpup); ++ au_dtime_store(dt, au_pinned_parent(pin), &h_path); ++ wh_dentry = NULL; ++ if (!need_wh) ++ goto out; /* success, no need to create whiteout */ ++ ++ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_unpin; ++ ++ /* returns with the parent is locked and wh_dentry is dget-ed */ ++ goto out; /* success */ ++ ++out_unpin: ++ au_unpin(pin); ++out: ++ return wh_dentry; ++} ++ ++/* ++ * when removing a dir, rename it to a unique temporary whiteout-ed name first ++ * in order to be revertible and save time for removing many child whiteouts ++ * under the dir. ++ * returns 1 when there are too many child whiteout and caller should remove ++ * them asynchronously. returns 0 when the number of children is enough small to ++ * remove now or the branch fs is a remote fs. ++ * otherwise return an error. ++ */ ++static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, ++ struct au_nhash *whlist, struct inode *dir) ++{ ++ int rmdir_later, err, dirwh; ++ struct dentry *h_dentry; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ SiMustAnyLock(sb); ++ h_dentry = au_h_dptr(dentry, bindex); ++ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); ++ if (unlikely(err)) ++ goto out; ++ ++ /* stop monitoring */ ++ au_hn_free(au_hi(dentry->d_inode, bindex)); ++ ++ if (!au_test_fs_remote(h_dentry->d_sb)) { ++ dirwh = au_sbi(sb)->si_dirwh; ++ rmdir_later = (dirwh <= 1); ++ if (!rmdir_later) ++ rmdir_later = au_nhash_test_longer_wh(whlist, bindex, ++ dirwh); ++ if (rmdir_later) ++ return rmdir_later; ++ } ++ ++ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); ++ if (unlikely(err)) { ++ AuIOErr("rmdir %pd, b%d failed, %d. ignored\n", ++ h_dentry, bindex, err); ++ err = 0; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * final procedure for deleting a entry. ++ * maintain dentry and iattr. ++ */ ++static void epilog(struct inode *dir, struct dentry *dentry, ++ aufs_bindex_t bindex) ++{ ++ struct inode *inode; ++ ++ inode = dentry->d_inode; ++ d_drop(dentry); ++ inode->i_ctime = dir->i_ctime; ++ ++ au_dir_ts(dir, bindex); ++ dir->i_version++; ++} ++ ++/* ++ * when an error happened, remove the created whiteout and revert everything. ++ */ ++static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, ++ aufs_bindex_t bwh, struct dentry *wh_dentry, ++ struct dentry *dentry, struct au_dtime *dt) ++{ ++ int rerr; ++ struct path h_path = { ++ .dentry = wh_dentry, ++ .mnt = au_sbr_mnt(dir->i_sb, bindex) ++ }; ++ ++ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); ++ if (!rerr) { ++ au_set_dbwh(dentry, bwh); ++ au_dtime_revert(dt); ++ return 0; ++ } ++ ++ AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr); ++ return -EIO; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int aufs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bwh, bindex, bstart; ++ struct inode *inode, *h_dir, *delegated; ++ struct dentry *parent, *wh_dentry; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct path h_path; ++ } *a; ++ ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ err = au_d_hashed_positive(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ err = -EISDIR; ++ if (unlikely(d_is_dir(dentry))) ++ goto out_unlock; /* possible? */ ++ ++ bstart = au_dbstart(dentry); ++ bwh = au_dbwh(dentry); ++ bindex = -1; ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, ++ &a->pin); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); ++ a->h_path.dentry = au_h_dptr(dentry, bstart); ++ dget(a->h_path.dentry); ++ if (bindex == bstart) { ++ h_dir = au_pinned_h_dir(&a->pin); ++ delegated = NULL; ++ err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ } else { ++ /* dir inode is locked */ ++ h_dir = wh_dentry->d_parent->d_inode; ++ IMustLock(h_dir); ++ err = 0; ++ } ++ ++ if (!err) { ++ vfsub_drop_nlink(inode); ++ epilog(dir, dentry, bindex); ++ ++ /* update target timestamps */ ++ if (bindex == bstart) { ++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); ++ /*ignore*/ ++ inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; ++ } else ++ /* todo: this timestamp may be reverted later */ ++ inode->i_ctime = h_dir->i_ctime; ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ if (wh_dentry) { ++ int rerr; ++ ++ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, ++ &a->dt); ++ if (rerr) ++ err = rerr; ++ } ++ ++out_unpin: ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++ dput(a->h_path.dentry); ++out_parent: ++ di_write_unlock(parent); ++out_unlock: ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ return err; ++} ++ ++int aufs_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ int err, rmdir_later; ++ aufs_bindex_t bwh, bindex, bstart; ++ struct inode *inode; ++ struct dentry *parent, *wh_dentry, *h_dentry; ++ struct au_whtmp_rmdir *args; ++ /* to reuduce stack size */ ++ struct { ++ struct au_dtime dt; ++ struct au_pin pin; ++ } *a; ++ ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ err = au_alive_dir(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ err = -ENOTDIR; ++ if (unlikely(!d_is_dir(dentry))) ++ goto out_unlock; /* possible? */ ++ ++ err = -ENOMEM; ++ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); ++ if (unlikely(!args)) ++ goto out_unlock; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ err = au_test_empty(dentry, &args->whlist); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ bstart = au_dbstart(dentry); ++ bwh = au_dbwh(dentry); ++ bindex = -1; ++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, ++ &a->pin); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ h_dentry = au_h_dptr(dentry, bstart); ++ dget(h_dentry); ++ rmdir_later = 0; ++ if (bindex == bstart) { ++ err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); ++ if (err > 0) { ++ rmdir_later = err; ++ err = 0; ++ } ++ } else { ++ /* stop monitoring */ ++ au_hn_free(au_hi(inode, bstart)); ++ ++ /* dir inode is locked */ ++ IMustLock(wh_dentry->d_parent->d_inode); ++ err = 0; ++ } ++ ++ if (!err) { ++ vfsub_dead_dir(inode); ++ au_set_dbdiropq(dentry, -1); ++ epilog(dir, dentry, bindex); ++ ++ if (rmdir_later) { ++ au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); ++ args = NULL; ++ } ++ ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ AuLabel(revert); ++ if (wh_dentry) { ++ int rerr; ++ ++ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, ++ &a->dt); ++ if (rerr) ++ err = rerr; ++ } ++ ++out_unpin: ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++ dput(h_dentry); ++out_parent: ++ di_write_unlock(parent); ++ if (args) ++ au_whtmp_rmdir_free(args); ++out_unlock: ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/i_op_ren.c linux-4.0.4/fs/aufs/i_op_ren.c +--- linux-4.0.4.orig/fs/aufs/i_op_ren.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/i_op_ren.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,1002 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * inode operation (rename entry) ++ * todo: this is crazy monster ++ */ ++ ++#include "aufs.h" ++ ++enum { AuSRC, AuDST, AuSrcDst }; ++enum { AuPARENT, AuCHILD, AuParentChild }; ++ ++#define AuRen_ISDIR 1 ++#define AuRen_ISSAMEDIR (1 << 1) ++#define AuRen_WHSRC (1 << 2) ++#define AuRen_WHDST (1 << 3) ++#define AuRen_MNT_WRITE (1 << 4) ++#define AuRen_DT_DSTDIR (1 << 5) ++#define AuRen_DIROPQ (1 << 6) ++#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) ++#define au_fset_ren(flags, name) \ ++ do { (flags) |= AuRen_##name; } while (0) ++#define au_fclr_ren(flags, name) \ ++ do { (flags) &= ~AuRen_##name; } while (0) ++ ++struct au_ren_args { ++ struct { ++ struct dentry *dentry, *h_dentry, *parent, *h_parent, ++ *wh_dentry; ++ struct inode *dir, *inode; ++ struct au_hinode *hdir; ++ struct au_dtime dt[AuParentChild]; ++ aufs_bindex_t bstart; ++ } sd[AuSrcDst]; ++ ++#define src_dentry sd[AuSRC].dentry ++#define src_dir sd[AuSRC].dir ++#define src_inode sd[AuSRC].inode ++#define src_h_dentry sd[AuSRC].h_dentry ++#define src_parent sd[AuSRC].parent ++#define src_h_parent sd[AuSRC].h_parent ++#define src_wh_dentry sd[AuSRC].wh_dentry ++#define src_hdir sd[AuSRC].hdir ++#define src_h_dir sd[AuSRC].hdir->hi_inode ++#define src_dt sd[AuSRC].dt ++#define src_bstart sd[AuSRC].bstart ++ ++#define dst_dentry sd[AuDST].dentry ++#define dst_dir sd[AuDST].dir ++#define dst_inode sd[AuDST].inode ++#define dst_h_dentry sd[AuDST].h_dentry ++#define dst_parent sd[AuDST].parent ++#define dst_h_parent sd[AuDST].h_parent ++#define dst_wh_dentry sd[AuDST].wh_dentry ++#define dst_hdir sd[AuDST].hdir ++#define dst_h_dir sd[AuDST].hdir->hi_inode ++#define dst_dt sd[AuDST].dt ++#define dst_bstart sd[AuDST].bstart ++ ++ struct dentry *h_trap; ++ struct au_branch *br; ++ struct au_hinode *src_hinode; ++ struct path h_path; ++ struct au_nhash whlist; ++ aufs_bindex_t btgt, src_bwh, src_bdiropq; ++ ++ unsigned int flags; ++ ++ struct au_whtmp_rmdir *thargs; ++ struct dentry *h_dst; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * functions for reverting. ++ * when an error happened in a single rename systemcall, we should revert ++ * everything as if nothing happend. ++ * we don't need to revert the copied-up/down the parent dir since they are ++ * harmless. ++ */ ++ ++#define RevertFailure(fmt, ...) do { \ ++ AuIOErr("revert failure: " fmt " (%d, %d)\n", \ ++ ##__VA_ARGS__, err, rerr); \ ++ err = -EIO; \ ++} while (0) ++ ++static void au_ren_rev_diropq(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(a->src_dentry, a->btgt); ++ au_hn_imtx_unlock(a->src_hinode); ++ au_set_dbdiropq(a->src_dentry, a->src_bdiropq); ++ if (rerr) ++ RevertFailure("remove diropq %pd", a->src_dentry); ++} ++ ++static void au_ren_rev_rename(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ struct inode *delegated; ++ ++ a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, ++ a->src_h_parent); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("lkup one %pd", a->src_dentry); ++ return; ++ } ++ ++ delegated = NULL; ++ rerr = vfsub_rename(a->dst_h_dir, ++ au_h_dptr(a->src_dentry, a->btgt), ++ a->src_h_dir, &a->h_path, &delegated); ++ if (unlikely(rerr == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ ++ if (rerr) ++ RevertFailure("rename %pd", a->src_dentry); ++} ++ ++static void au_ren_rev_whtmp(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ struct inode *delegated; ++ ++ a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, ++ a->dst_h_parent); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("lkup one %pd", a->dst_dentry); ++ return; ++ } ++ if (a->h_path.dentry->d_inode) { ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ return; ++ } ++ ++ delegated = NULL; ++ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path, ++ &delegated); ++ if (unlikely(rerr == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ if (!rerr) ++ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); ++ else ++ RevertFailure("rename %pd", a->h_dst); ++} ++ ++static void au_ren_rev_whsrc(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = a->src_wh_dentry; ++ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); ++ au_set_dbwh(a->src_dentry, a->src_bwh); ++ if (rerr) ++ RevertFailure("unlink %pd", a->src_wh_dentry); ++} ++#undef RevertFailure ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * when we have to copyup the renaming entry, do it with the rename-target name ++ * in order to minimize the cost (the later actual rename is unnecessary). ++ * otherwise rename it on the target branch. ++ */ ++static int au_ren_or_cpup(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d; ++ struct inode *delegated; ++ ++ d = a->src_dentry; ++ if (au_dbstart(d) == a->btgt) { ++ a->h_path.dentry = a->dst_h_dentry; ++ if (au_ftest_ren(a->flags, DIROPQ) ++ && au_dbdiropq(d) == a->btgt) ++ au_fclr_ren(a->flags, DIROPQ); ++ AuDebugOn(au_dbstart(d) != a->btgt); ++ delegated = NULL; ++ err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), ++ a->dst_h_dir, &a->h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ } else ++ BUG(); ++ ++ if (!err && a->h_dst) ++ /* it will be set to dinfo later */ ++ dget(a->h_dst); ++ ++ return err; ++} ++ ++/* cf. aufs_rmdir() */ ++static int au_ren_del_whtmp(struct au_ren_args *a) ++{ ++ int err; ++ struct inode *dir; ++ ++ dir = a->dst_dir; ++ SiMustAnyLock(dir->i_sb); ++ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, ++ au_sbi(dir->i_sb)->si_dirwh) ++ || au_test_fs_remote(a->h_dst->d_sb)) { ++ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); ++ if (unlikely(err)) ++ pr_warn("failed removing whtmp dir %pd (%d), " ++ "ignored.\n", a->h_dst, err); ++ } else { ++ au_nhash_wh_free(&a->thargs->whlist); ++ a->thargs->whlist = a->whlist; ++ a->whlist.nh_num = 0; ++ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); ++ dput(a->h_dst); ++ a->thargs = NULL; ++ } ++ ++ return 0; ++} ++ ++/* make it 'opaque' dir. */ ++static int au_ren_diropq(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *diropq; ++ ++ err = 0; ++ a->src_bdiropq = au_dbdiropq(a->src_dentry); ++ a->src_hinode = au_hi(a->src_inode, a->btgt); ++ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); ++ diropq = au_diropq_create(a->src_dentry, a->btgt); ++ au_hn_imtx_unlock(a->src_hinode); ++ if (IS_ERR(diropq)) ++ err = PTR_ERR(diropq); ++ else ++ dput(diropq); ++ ++ return err; ++} ++ ++static int do_rename(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d, *h_d; ++ ++ /* prepare workqueue args for asynchronous rmdir */ ++ h_d = a->dst_h_dentry; ++ if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) { ++ err = -ENOMEM; ++ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); ++ if (unlikely(!a->thargs)) ++ goto out; ++ a->h_dst = dget(h_d); ++ } ++ ++ /* create whiteout for src_dentry */ ++ if (au_ftest_ren(a->flags, WHSRC)) { ++ a->src_bwh = au_dbwh(a->src_dentry); ++ AuDebugOn(a->src_bwh >= 0); ++ a->src_wh_dentry ++ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); ++ err = PTR_ERR(a->src_wh_dentry); ++ if (IS_ERR(a->src_wh_dentry)) ++ goto out_thargs; ++ } ++ ++ /* lookup whiteout for dentry */ ++ if (au_ftest_ren(a->flags, WHDST)) { ++ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, ++ a->br); ++ err = PTR_ERR(h_d); ++ if (IS_ERR(h_d)) ++ goto out_whsrc; ++ if (!h_d->d_inode) ++ dput(h_d); ++ else ++ a->dst_wh_dentry = h_d; ++ } ++ ++ /* rename dentry to tmpwh */ ++ if (a->thargs) { ++ err = au_whtmp_ren(a->dst_h_dentry, a->br); ++ if (unlikely(err)) ++ goto out_whdst; ++ ++ d = a->dst_dentry; ++ au_set_h_dptr(d, a->btgt, NULL); ++ err = au_lkup_neg(d, a->btgt, /*wh*/0); ++ if (unlikely(err)) ++ goto out_whtmp; ++ a->dst_h_dentry = au_h_dptr(d, a->btgt); ++ } ++ ++ BUG_ON(a->dst_h_dentry->d_inode && a->src_bstart != a->btgt); ++ ++ /* rename by vfs_rename or cpup */ ++ d = a->dst_dentry; ++ if (au_ftest_ren(a->flags, ISDIR) ++ && (a->dst_wh_dentry ++ || au_dbdiropq(d) == a->btgt ++ /* hide the lower to keep xino */ ++ || a->btgt < au_dbend(d) ++ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) ++ au_fset_ren(a->flags, DIROPQ); ++ err = au_ren_or_cpup(a); ++ if (unlikely(err)) ++ /* leave the copied-up one */ ++ goto out_whtmp; ++ ++ /* make dir opaque */ ++ if (au_ftest_ren(a->flags, DIROPQ)) { ++ err = au_ren_diropq(a); ++ if (unlikely(err)) ++ goto out_rename; ++ } ++ ++ /* update target timestamps */ ++ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); ++ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); ++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ ++ a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; ++ ++ /* remove whiteout for dentry */ ++ if (a->dst_wh_dentry) { ++ a->h_path.dentry = a->dst_wh_dentry; ++ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, ++ a->dst_dentry); ++ if (unlikely(err)) ++ goto out_diropq; ++ } ++ ++ /* remove whtmp */ ++ if (a->thargs) ++ au_ren_del_whtmp(a); /* ignore this error */ ++ ++ au_fhsm_wrote(a->src_dentry->d_sb, a->btgt, /*force*/0); ++ err = 0; ++ goto out_success; ++ ++out_diropq: ++ if (au_ftest_ren(a->flags, DIROPQ)) ++ au_ren_rev_diropq(err, a); ++out_rename: ++ au_ren_rev_rename(err, a); ++ dput(a->h_dst); ++out_whtmp: ++ if (a->thargs) ++ au_ren_rev_whtmp(err, a); ++out_whdst: ++ dput(a->dst_wh_dentry); ++ a->dst_wh_dentry = NULL; ++out_whsrc: ++ if (a->src_wh_dentry) ++ au_ren_rev_whsrc(err, a); ++out_success: ++ dput(a->src_wh_dentry); ++ dput(a->dst_wh_dentry); ++out_thargs: ++ if (a->thargs) { ++ dput(a->h_dst); ++ au_whtmp_rmdir_free(a->thargs); ++ a->thargs = NULL; ++ } ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * test if @dentry dir can be rename destination or not. ++ * success means, it is a logically empty dir. ++ */ ++static int may_rename_dstdir(struct dentry *dentry, struct au_nhash *whlist) ++{ ++ return au_test_empty(dentry, whlist); ++} ++ ++/* ++ * test if @dentry dir can be rename source or not. ++ * if it can, return 0 and @children is filled. ++ * success means, ++ * - it is a logically empty dir. ++ * - or, it exists on writable branch and has no children including whiteouts ++ * on the lower branch. ++ */ ++static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) ++{ ++ int err; ++ unsigned int rdhash; ++ aufs_bindex_t bstart; ++ ++ bstart = au_dbstart(dentry); ++ if (bstart != btgt) { ++ struct au_nhash whlist; ++ ++ SiMustAnyLock(dentry->d_sb); ++ rdhash = au_sbi(dentry->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, ++ dentry)); ++ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_test_empty(dentry, &whlist); ++ au_nhash_wh_free(&whlist); ++ goto out; ++ } ++ ++ if (bstart == au_dbtaildir(dentry)) ++ return 0; /* success */ ++ ++ err = au_test_empty_lower(dentry); ++ ++out: ++ if (err == -ENOTEMPTY) { ++ AuWarn1("renaming dir who has child(ren) on multiple branches," ++ " is not supported\n"); ++ err = -EXDEV; ++ } ++ return err; ++} ++ ++/* side effect: sets whlist and h_dentry */ ++static int au_ren_may_dir(struct au_ren_args *a) ++{ ++ int err; ++ unsigned int rdhash; ++ struct dentry *d; ++ ++ d = a->dst_dentry; ++ SiMustAnyLock(d->d_sb); ++ ++ err = 0; ++ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { ++ rdhash = au_sbi(d->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); ++ err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ ++ au_set_dbstart(d, a->dst_bstart); ++ err = may_rename_dstdir(d, &a->whlist); ++ au_set_dbstart(d, a->btgt); ++ } ++ a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); ++ if (unlikely(err)) ++ goto out; ++ ++ d = a->src_dentry; ++ a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); ++ if (au_ftest_ren(a->flags, ISDIR)) { ++ err = may_rename_srcdir(d, a->btgt); ++ if (unlikely(err)) { ++ au_nhash_wh_free(&a->whlist); ++ a->whlist.nh_num = 0; ++ } ++ } ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * simple tests for rename. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++static int au_may_ren(struct au_ren_args *a) ++{ ++ int err, isdir; ++ struct inode *h_inode; ++ ++ if (a->src_bstart == a->btgt) { ++ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, ++ au_ftest_ren(a->flags, ISDIR)); ++ if (unlikely(err)) ++ goto out; ++ err = -EINVAL; ++ if (unlikely(a->src_h_dentry == a->h_trap)) ++ goto out; ++ } ++ ++ err = 0; ++ if (a->dst_bstart != a->btgt) ++ goto out; ++ ++ err = -ENOTEMPTY; ++ if (unlikely(a->dst_h_dentry == a->h_trap)) ++ goto out; ++ ++ err = -EIO; ++ h_inode = a->dst_h_dentry->d_inode; ++ isdir = !!au_ftest_ren(a->flags, ISDIR); ++ if (!a->dst_dentry->d_inode) { ++ if (unlikely(h_inode)) ++ goto out; ++ err = au_may_add(a->dst_dentry, a->btgt, a->dst_h_parent, ++ isdir); ++ } else { ++ if (unlikely(!h_inode || !h_inode->i_nlink)) ++ goto out; ++ err = au_may_del(a->dst_dentry, a->btgt, a->dst_h_parent, ++ isdir); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++out: ++ if (unlikely(err == -ENOENT || err == -EEXIST)) ++ err = -EIO; ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * locking order ++ * (VFS) ++ * - src_dir and dir by lock_rename() ++ * - inode if exitsts ++ * (aufs) ++ * - lock all ++ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, ++ * + si_read_lock ++ * + di_write_lock2_child() ++ * + di_write_lock_child() ++ * + ii_write_lock_child() ++ * + di_write_lock_child2() ++ * + ii_write_lock_child2() ++ * + src_parent and parent ++ * + di_write_lock_parent() ++ * + ii_write_lock_parent() ++ * + di_write_lock_parent2() ++ * + ii_write_lock_parent2() ++ * + lower src_dir and dir by vfsub_lock_rename() ++ * + verify the every relationships between child and parent. if any ++ * of them failed, unlock all and return -EBUSY. ++ */ ++static void au_ren_unlock(struct au_ren_args *a) ++{ ++ vfsub_unlock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++ if (au_ftest_ren(a->flags, MNT_WRITE)) ++ vfsub_mnt_drop_write(au_br_mnt(a->br)); ++} ++ ++static int au_ren_lock(struct au_ren_args *a) ++{ ++ int err; ++ unsigned int udba; ++ ++ err = 0; ++ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); ++ a->src_hdir = au_hi(a->src_dir, a->btgt); ++ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); ++ a->dst_hdir = au_hi(a->dst_dir, a->btgt); ++ ++ err = vfsub_mnt_want_write(au_br_mnt(a->br)); ++ if (unlikely(err)) ++ goto out; ++ au_fset_ren(a->flags, MNT_WRITE); ++ a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++ udba = au_opt_udba(a->src_dentry->d_sb); ++ if (unlikely(a->src_hdir->hi_inode != a->src_h_parent->d_inode ++ || a->dst_hdir->hi_inode != a->dst_h_parent->d_inode)) ++ err = au_busy_or_stale(); ++ if (!err && au_dbstart(a->src_dentry) == a->btgt) ++ err = au_h_verify(a->src_h_dentry, udba, ++ a->src_h_parent->d_inode, a->src_h_parent, ++ a->br); ++ if (!err && au_dbstart(a->dst_dentry) == a->btgt) ++ err = au_h_verify(a->dst_h_dentry, udba, ++ a->dst_h_parent->d_inode, a->dst_h_parent, ++ a->br); ++ if (!err) ++ goto out; /* success */ ++ ++ err = au_busy_or_stale(); ++ au_ren_unlock(a); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_ren_refresh_dir(struct au_ren_args *a) ++{ ++ struct inode *dir; ++ ++ dir = a->dst_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->flags, ISDIR)) { ++ /* is this updating defined in POSIX? */ ++ au_cpup_attr_timesizes(a->src_inode); ++ au_cpup_attr_nlink(dir, /*force*/1); ++ } ++ ++ au_dir_ts(dir, a->btgt); ++ ++ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ return; ++ ++ dir = a->src_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->flags, ISDIR)) ++ au_cpup_attr_nlink(dir, /*force*/1); ++ au_dir_ts(dir, a->btgt); ++} ++ ++static void au_ren_refresh(struct au_ren_args *a) ++{ ++ aufs_bindex_t bend, bindex; ++ struct dentry *d, *h_d; ++ struct inode *i, *h_i; ++ struct super_block *sb; ++ ++ d = a->dst_dentry; ++ d_drop(d); ++ if (a->h_dst) ++ /* already dget-ed by au_ren_or_cpup() */ ++ au_set_h_dptr(d, a->btgt, a->h_dst); ++ ++ i = a->dst_inode; ++ if (i) { ++ if (!au_ftest_ren(a->flags, ISDIR)) ++ vfsub_drop_nlink(i); ++ else { ++ vfsub_dead_dir(i); ++ au_cpup_attr_timesizes(i); ++ } ++ au_update_dbrange(d, /*do_put_zero*/1); ++ } else { ++ bend = a->btgt; ++ for (bindex = au_dbstart(d); bindex < bend; bindex++) ++ au_set_h_dptr(d, bindex, NULL); ++ bend = au_dbend(d); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) ++ au_set_h_dptr(d, bindex, NULL); ++ au_update_dbrange(d, /*do_put_zero*/0); ++ } ++ ++ d = a->src_dentry; ++ au_set_dbwh(d, -1); ++ bend = au_dbend(d); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { ++ h_d = au_h_dptr(d, bindex); ++ if (h_d) ++ au_set_h_dptr(d, bindex, NULL); ++ } ++ au_set_dbend(d, a->btgt); ++ ++ sb = d->d_sb; ++ i = a->src_inode; ++ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) ++ return; /* success */ ++ ++ bend = au_ibend(i); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { ++ h_i = au_h_iptr(i, bindex); ++ if (h_i) { ++ au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); ++ /* ignore this error */ ++ au_set_h_iptr(i, bindex, NULL, 0); ++ } ++ } ++ au_set_ibend(i, a->btgt); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* mainly for link(2) and rename(2) */ ++int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) ++{ ++ aufs_bindex_t bdiropq, bwh; ++ struct dentry *parent; ++ struct au_branch *br; ++ ++ parent = dentry->d_parent; ++ IMustLock(parent->d_inode); /* dir is locked */ ++ ++ bdiropq = au_dbdiropq(parent); ++ bwh = au_dbwh(dentry); ++ br = au_sbr(dentry->d_sb, btgt); ++ if (au_br_rdonly(br) ++ || (0 <= bdiropq && bdiropq < btgt) ++ || (0 <= bwh && bwh < btgt)) ++ btgt = -1; ++ ++ AuDbg("btgt %d\n", btgt); ++ return btgt; ++} ++ ++/* sets src_bstart, dst_bstart and btgt */ ++static int au_ren_wbr(struct au_ren_args *a) ++{ ++ int err; ++ struct au_wr_dir_args wr_dir_args = { ++ /* .force_btgt = -1, */ ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ a->src_bstart = au_dbstart(a->src_dentry); ++ a->dst_bstart = au_dbstart(a->dst_dentry); ++ if (au_ftest_ren(a->flags, ISDIR)) ++ au_fset_wrdir(wr_dir_args.flags, ISDIR); ++ wr_dir_args.force_btgt = a->src_bstart; ++ if (a->dst_inode && a->dst_bstart < a->src_bstart) ++ wr_dir_args.force_btgt = a->dst_bstart; ++ wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); ++ err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); ++ a->btgt = err; ++ ++ return err; ++} ++ ++static void au_ren_dt(struct au_ren_args *a) ++{ ++ a->h_path.dentry = a->src_h_parent; ++ au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); ++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) { ++ a->h_path.dentry = a->dst_h_parent; ++ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); ++ } ++ ++ au_fclr_ren(a->flags, DT_DSTDIR); ++ if (!au_ftest_ren(a->flags, ISDIR)) ++ return; ++ ++ a->h_path.dentry = a->src_h_dentry; ++ au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); ++ if (a->dst_h_dentry->d_inode) { ++ au_fset_ren(a->flags, DT_DSTDIR); ++ a->h_path.dentry = a->dst_h_dentry; ++ au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); ++ } ++} ++ ++static void au_ren_rev_dt(int err, struct au_ren_args *a) ++{ ++ struct dentry *h_d; ++ struct mutex *h_mtx; ++ ++ au_dtime_revert(a->src_dt + AuPARENT); ++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) ++ au_dtime_revert(a->dst_dt + AuPARENT); ++ ++ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { ++ h_d = a->src_dt[AuCHILD].dt_h_path.dentry; ++ h_mtx = &h_d->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ au_dtime_revert(a->src_dt + AuCHILD); ++ mutex_unlock(h_mtx); ++ ++ if (au_ftest_ren(a->flags, DT_DSTDIR)) { ++ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; ++ h_mtx = &h_d->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ au_dtime_revert(a->dst_dt + AuCHILD); ++ mutex_unlock(h_mtx); ++ } ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, ++ struct inode *_dst_dir, struct dentry *_dst_dentry) ++{ ++ int err, flags; ++ /* reduce stack space */ ++ struct au_ren_args *a; ++ ++ AuDbg("%pd, %pd\n", _src_dentry, _dst_dentry); ++ IMustLock(_src_dir); ++ IMustLock(_dst_dir); ++ ++ err = -ENOMEM; ++ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ a->src_dir = _src_dir; ++ a->src_dentry = _src_dentry; ++ a->src_inode = a->src_dentry->d_inode; ++ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ ++ a->dst_dir = _dst_dir; ++ a->dst_dentry = _dst_dentry; ++ a->dst_inode = a->dst_dentry->d_inode; ++ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ ++ if (a->dst_inode) { ++ IMustLock(a->dst_inode); ++ au_igrab(a->dst_inode); ++ } ++ ++ err = -ENOTDIR; ++ flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; ++ if (d_is_dir(a->src_dentry)) { ++ au_fset_ren(a->flags, ISDIR); ++ if (unlikely(d_is_positive(a->dst_dentry) ++ && !d_is_dir(a->dst_dentry))) ++ goto out_free; ++ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, ++ AuLock_DIR | flags); ++ } else ++ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, ++ flags); ++ if (unlikely(err)) ++ goto out_free; ++ ++ err = au_d_hashed_positive(a->src_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ err = -ENOENT; ++ if (a->dst_inode) { ++ /* ++ * If it is a dir, VFS unhash dst_dentry before this ++ * function. It means we cannot rely upon d_unhashed(). ++ */ ++ if (unlikely(!a->dst_inode->i_nlink)) ++ goto out_unlock; ++ if (!S_ISDIR(a->dst_inode->i_mode)) { ++ err = au_d_hashed_positive(a->dst_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ } else if (unlikely(IS_DEADDIR(a->dst_inode))) ++ goto out_unlock; ++ } else if (unlikely(d_unhashed(a->dst_dentry))) ++ goto out_unlock; ++ ++ /* ++ * is it possible? ++ * yes, it happend (in linux-3.3-rcN) but I don't know why. ++ * there may exist a problem somewhere else. ++ */ ++ err = -EINVAL; ++ if (unlikely(a->dst_parent->d_inode == a->src_dentry->d_inode)) ++ goto out_unlock; ++ ++ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ ++ di_write_lock_parent(a->dst_parent); ++ ++ /* which branch we process */ ++ err = au_ren_wbr(a); ++ if (unlikely(err < 0)) ++ goto out_parent; ++ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); ++ a->h_path.mnt = au_br_mnt(a->br); ++ ++ /* are they available to be renamed */ ++ err = au_ren_may_dir(a); ++ if (unlikely(err)) ++ goto out_children; ++ ++ /* prepare the writable parent dir on the same branch */ ++ if (a->dst_bstart == a->btgt) { ++ au_fset_ren(a->flags, WHDST); ++ } else { ++ err = au_cpup_dirs(a->dst_dentry, a->btgt); ++ if (unlikely(err)) ++ goto out_children; ++ } ++ ++ if (a->src_dir != a->dst_dir) { ++ /* ++ * this temporary unlock is safe, ++ * because both dir->i_mutex are locked. ++ */ ++ di_write_unlock(a->dst_parent); ++ di_write_lock_parent(a->src_parent); ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->flags, ISDIR), ++ &a->btgt); ++ di_write_unlock(a->src_parent); ++ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); ++ au_fclr_ren(a->flags, ISSAMEDIR); ++ } else ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->flags, ISDIR), ++ &a->btgt); ++ if (unlikely(err < 0)) ++ goto out_children; ++ if (err) ++ au_fset_ren(a->flags, WHSRC); ++ ++ /* cpup src */ ++ if (a->src_bstart != a->btgt) { ++ struct au_pin pin; ++ ++ err = au_pin(&pin, a->src_dentry, a->btgt, ++ au_opt_udba(a->src_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (!err) { ++ struct au_cp_generic cpg = { ++ .dentry = a->src_dentry, ++ .bdst = a->btgt, ++ .bsrc = a->src_bstart, ++ .len = -1, ++ .pin = &pin, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); ++ err = au_sio_cpup_simple(&cpg); ++ au_unpin(&pin); ++ } ++ if (unlikely(err)) ++ goto out_children; ++ a->src_bstart = a->btgt; ++ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); ++ au_fset_ren(a->flags, WHSRC); ++ } ++ ++ /* lock them all */ ++ err = au_ren_lock(a); ++ if (unlikely(err)) ++ /* leave the copied-up one */ ++ goto out_children; ++ ++ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) ++ err = au_may_ren(a); ++ else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ err = -ENAMETOOLONG; ++ if (unlikely(err)) ++ goto out_hdir; ++ ++ /* store timestamps to be revertible */ ++ au_ren_dt(a); ++ ++ /* here we go */ ++ err = do_rename(a); ++ if (unlikely(err)) ++ goto out_dt; ++ ++ /* update dir attributes */ ++ au_ren_refresh_dir(a); ++ ++ /* dput/iput all lower dentries */ ++ au_ren_refresh(a); ++ ++ goto out_hdir; /* success */ ++ ++out_dt: ++ au_ren_rev_dt(err, a); ++out_hdir: ++ au_ren_unlock(a); ++out_children: ++ au_nhash_wh_free(&a->whlist); ++ if (err && a->dst_inode && a->dst_bstart != a->btgt) { ++ AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); ++ au_set_h_dptr(a->dst_dentry, a->btgt, NULL); ++ au_set_dbstart(a->dst_dentry, a->dst_bstart); ++ } ++out_parent: ++ if (!err) ++ d_move(a->src_dentry, a->dst_dentry); ++ else { ++ au_update_dbstart(a->dst_dentry); ++ if (!a->dst_inode) ++ d_drop(a->dst_dentry); ++ } ++ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ di_write_unlock(a->dst_parent); ++ else ++ di_write_unlock2(a->src_parent, a->dst_parent); ++out_unlock: ++ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); ++out_free: ++ iput(a->dst_inode); ++ if (a->thargs) ++ au_whtmp_rmdir_free(a->thargs); ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/Kconfig linux-4.0.4/fs/aufs/Kconfig +--- linux-4.0.4.orig/fs/aufs/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/Kconfig 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,185 @@ ++config AUFS_FS ++ bool "Aufs (Advanced multi layered unification filesystem) support" ++ help ++ Aufs is a stackable unification filesystem such as Unionfs, ++ which unifies several directories and provides a merged single ++ directory. ++ In the early days, aufs was entirely re-designed and ++ re-implemented Unionfs Version 1.x series. Introducing many ++ original ideas, approaches and improvements, it becomes totally ++ different from Unionfs while keeping the basic features. ++ ++if AUFS_FS ++choice ++ prompt "Maximum number of branches" ++ default AUFS_BRANCH_MAX_127 ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_127 ++ bool "127" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_511 ++ bool "511" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_1023 ++ bool "1023" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_32767 ++ bool "32767" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++endchoice ++ ++config AUFS_SBILIST ++ bool ++ depends on AUFS_MAGIC_SYSRQ || PROC_FS ++ default y ++ help ++ Automatic configuration for internal use. ++ When aufs supports Magic SysRq or /proc, enabled automatically. ++ ++config AUFS_HNOTIFY ++ bool "Detect direct branch access (bypassing aufs)" ++ help ++ If you want to modify files on branches directly, eg. bypassing aufs, ++ and want aufs to detect the changes of them fully, then enable this ++ option and use 'udba=notify' mount option. ++ Currently there is only one available configuration, "fsnotify". ++ It will have a negative impact to the performance. ++ See detail in aufs.5. ++ ++choice ++ prompt "method" if AUFS_HNOTIFY ++ default AUFS_HFSNOTIFY ++config AUFS_HFSNOTIFY ++ bool "fsnotify" ++ select FSNOTIFY ++endchoice ++ ++config AUFS_EXPORT ++ bool "NFS-exportable aufs" ++ depends on EXPORTFS = y ++ help ++ If you want to export your mounted aufs via NFS, then enable this ++ option. There are several requirements for this configuration. ++ See detail in aufs.5. ++ ++config AUFS_INO_T_64 ++ bool ++ depends on AUFS_EXPORT ++ depends on 64BIT && !(ALPHA || S390) ++ default y ++ help ++ Automatic configuration for internal use. ++ /* typedef unsigned long/int __kernel_ino_t */ ++ /* alpha and s390x are int */ ++ ++config AUFS_XATTR ++ bool "support for XATTR/EA (including Security Labels)" ++ help ++ If your branch fs supports XATTR/EA and you want to make them ++ available in aufs too, then enable this opsion and specify the ++ branch attributes for EA. ++ See detail in aufs.5. ++ ++config AUFS_FHSM ++ bool "File-based Hierarchical Storage Management" ++ help ++ Hierarchical Storage Management (or HSM) is a well-known feature ++ in the storage world. Aufs provides this feature as file-based. ++ with multiple branches. ++ These multiple branches are prioritized, ie. the topmost one ++ should be the fastest drive and be used heavily. ++ ++config AUFS_RDU ++ bool "Readdir in userspace" ++ help ++ Aufs has two methods to provide a merged view for a directory, ++ by a user-space library and by kernel-space natively. The latter ++ is always enabled but sometimes large and slow. ++ If you enable this option, install the library in aufs2-util ++ package, and set some environment variables for your readdir(3), ++ then the work will be handled in user-space which generally ++ shows better performance in most cases. ++ See detail in aufs.5. ++ ++config AUFS_SHWH ++ bool "Show whiteouts" ++ help ++ If you want to make the whiteouts in aufs visible, then enable ++ this option and specify 'shwh' mount option. Although it may ++ sounds like philosophy or something, but in technically it ++ simply shows the name of whiteout with keeping its behaviour. ++ ++config AUFS_BR_RAMFS ++ bool "Ramfs (initramfs/rootfs) as an aufs branch" ++ help ++ If you want to use ramfs as an aufs branch fs, then enable this ++ option. Generally tmpfs is recommended. ++ Aufs prohibited them to be a branch fs by default, because ++ initramfs becomes unusable after switch_root or something ++ generally. If you sets initramfs as an aufs branch and boot your ++ system by switch_root, you will meet a problem easily since the ++ files in initramfs may be inaccessible. ++ Unless you are going to use ramfs as an aufs branch fs without ++ switch_root or something, leave it N. ++ ++config AUFS_BR_FUSE ++ bool "Fuse fs as an aufs branch" ++ depends on FUSE_FS ++ select AUFS_POLL ++ help ++ If you want to use fuse-based userspace filesystem as an aufs ++ branch fs, then enable this option. ++ It implements the internal poll(2) operation which is ++ implemented by fuse only (curretnly). ++ ++config AUFS_POLL ++ bool ++ help ++ Automatic configuration for internal use. ++ ++config AUFS_BR_HFSPLUS ++ bool "Hfsplus as an aufs branch" ++ depends on HFSPLUS_FS ++ default y ++ help ++ If you want to use hfsplus fs as an aufs branch fs, then enable ++ this option. This option introduces a small overhead at ++ copying-up a file on hfsplus. ++ ++config AUFS_BDEV_LOOP ++ bool ++ depends on BLK_DEV_LOOP ++ default y ++ help ++ Automatic configuration for internal use. ++ Convert =[ym] into =y. ++ ++config AUFS_DEBUG ++ bool "Debug aufs" ++ help ++ Enable this to compile aufs internal debug code. ++ It will have a negative impact to the performance. ++ ++config AUFS_MAGIC_SYSRQ ++ bool ++ depends on AUFS_DEBUG && MAGIC_SYSRQ ++ default y ++ help ++ Automatic configuration for internal use. ++ When aufs supports Magic SysRq, enabled automatically. ++endif +diff -Nur linux-4.0.4.orig/fs/aufs/loop.c linux-4.0.4/fs/aufs/loop.c +--- linux-4.0.4.orig/fs/aufs/loop.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/loop.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,132 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * support for loopback block device as a branch ++ */ ++ ++#include "aufs.h" ++ ++/* added into drivers/block/loop.c */ ++static struct file *(*backing_file_func)(struct super_block *sb); ++ ++/* ++ * test if two lower dentries have overlapping branches. ++ */ ++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding) ++{ ++ struct super_block *h_sb; ++ struct file *backing_file; ++ ++ if (unlikely(!backing_file_func)) { ++ /* don't load "loop" module here */ ++ backing_file_func = symbol_get(loop_backing_file); ++ if (unlikely(!backing_file_func)) ++ /* "loop" module is not loaded */ ++ return 0; ++ } ++ ++ h_sb = h_adding->d_sb; ++ backing_file = backing_file_func(h_sb); ++ if (!backing_file) ++ return 0; ++ ++ h_adding = backing_file->f_path.dentry; ++ /* ++ * h_adding can be local NFS. ++ * in this case aufs cannot detect the loop. ++ */ ++ if (unlikely(h_adding->d_sb == sb)) ++ return 1; ++ return !!au_test_subdir(h_adding, sb->s_root); ++} ++ ++/* true if a kernel thread named 'loop[0-9].*' accesses a file */ ++int au_test_loopback_kthread(void) ++{ ++ int ret; ++ struct task_struct *tsk = current; ++ char c, comm[sizeof(tsk->comm)]; ++ ++ ret = 0; ++ if (tsk->flags & PF_KTHREAD) { ++ get_task_comm(comm, tsk); ++ c = comm[4]; ++ ret = ('0' <= c && c <= '9' ++ && !strncmp(comm, "loop", 4)); ++ } ++ ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define au_warn_loopback_step 16 ++static int au_warn_loopback_nelem = au_warn_loopback_step; ++static unsigned long *au_warn_loopback_array; ++ ++void au_warn_loopback(struct super_block *h_sb) ++{ ++ int i, new_nelem; ++ unsigned long *a, magic; ++ static DEFINE_SPINLOCK(spin); ++ ++ magic = h_sb->s_magic; ++ spin_lock(&spin); ++ a = au_warn_loopback_array; ++ for (i = 0; i < au_warn_loopback_nelem && *a; i++) ++ if (a[i] == magic) { ++ spin_unlock(&spin); ++ return; ++ } ++ ++ /* h_sb is new to us, print it */ ++ if (i < au_warn_loopback_nelem) { ++ a[i] = magic; ++ goto pr; ++ } ++ ++ /* expand the array */ ++ new_nelem = au_warn_loopback_nelem + au_warn_loopback_step; ++ a = au_kzrealloc(au_warn_loopback_array, ++ au_warn_loopback_nelem * sizeof(unsigned long), ++ new_nelem * sizeof(unsigned long), GFP_ATOMIC); ++ if (a) { ++ au_warn_loopback_nelem = new_nelem; ++ au_warn_loopback_array = a; ++ a[i] = magic; ++ goto pr; ++ } ++ ++ spin_unlock(&spin); ++ AuWarn1("realloc failed, ignored\n"); ++ return; ++ ++pr: ++ spin_unlock(&spin); ++ pr_warn("you may want to try another patch for loopback file " ++ "on %s(0x%lx) branch\n", au_sbtype(h_sb), magic); ++} ++ ++int au_loopback_init(void) ++{ ++ int err; ++ struct super_block *sb __maybe_unused; ++ ++ AuDebugOn(sizeof(sb->s_magic) != sizeof(unsigned long)); ++ ++ err = 0; ++ au_warn_loopback_array = kcalloc(au_warn_loopback_step, ++ sizeof(unsigned long), GFP_NOFS); ++ if (unlikely(!au_warn_loopback_array)) ++ err = -ENOMEM; ++ ++ return err; ++} ++ ++void au_loopback_fin(void) ++{ ++ symbol_put(loop_backing_file); ++ kfree(au_warn_loopback_array); ++} +diff -Nur linux-4.0.4.orig/fs/aufs/loop.h linux-4.0.4/fs/aufs/loop.h +--- linux-4.0.4.orig/fs/aufs/loop.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/loop.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * support for loopback mount as a branch ++ */ ++ ++#ifndef __AUFS_LOOP_H__ ++#define __AUFS_LOOP_H__ ++ ++#ifdef __KERNEL__ ++ ++struct dentry; ++struct super_block; ++ ++#ifdef CONFIG_AUFS_BDEV_LOOP ++/* drivers/block/loop.c */ ++struct file *loop_backing_file(struct super_block *sb); ++ ++/* loop.c */ ++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding); ++int au_test_loopback_kthread(void); ++void au_warn_loopback(struct super_block *h_sb); ++ ++int au_loopback_init(void); ++void au_loopback_fin(void); ++#else ++AuStubInt0(au_test_loopback_overlap, struct super_block *sb, ++ struct dentry *h_adding) ++AuStubInt0(au_test_loopback_kthread, void) ++AuStubVoid(au_warn_loopback, struct super_block *h_sb) ++ ++AuStubInt0(au_loopback_init, void) ++AuStubVoid(au_loopback_fin, void) ++#endif /* BLK_DEV_LOOP */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_LOOP_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/magic.mk linux-4.0.4/fs/aufs/magic.mk +--- linux-4.0.4.orig/fs/aufs/magic.mk 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/magic.mk 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,30 @@ ++ ++# defined in ${srctree}/fs/fuse/inode.c ++# tristate ++ifdef CONFIG_FUSE_FS ++ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546 ++endif ++ ++# defined in ${srctree}/fs/xfs/xfs_sb.h ++# tristate ++ifdef CONFIG_XFS_FS ++ccflags-y += -DXFS_SB_MAGIC=0x58465342 ++endif ++ ++# defined in ${srctree}/fs/configfs/mount.c ++# tristate ++ifdef CONFIG_CONFIGFS_FS ++ccflags-y += -DCONFIGFS_MAGIC=0x62656570 ++endif ++ ++# defined in ${srctree}/fs/ubifs/ubifs.h ++# tristate ++ifdef CONFIG_UBIFS_FS ++ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905 ++endif ++ ++# defined in ${srctree}/fs/hfsplus/hfsplus_raw.h ++# tristate ++ifdef CONFIG_HFSPLUS_FS ++ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b ++endif +diff -Nur linux-4.0.4.orig/fs/aufs/Makefile linux-4.0.4/fs/aufs/Makefile +--- linux-4.0.4.orig/fs/aufs/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/Makefile 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,36 @@ ++ ++include ${srctree}/${src}/magic.mk ++ ++# cf. include/linux/kernel.h ++# enable pr_debug ++ccflags-y += -DDEBUG ++# sparse requires the full pathname ++ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h ++ ++obj-$(CONFIG_AUFS_FS) += aufs.o ++aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ ++ wkq.o vfsub.o dcsub.o \ ++ cpup.o whout.o wbr_policy.o \ ++ dinfo.o dentry.o \ ++ dynop.o \ ++ finfo.o file.o f_op.o \ ++ dir.o vdir.o \ ++ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ ++ mvdown.o ioctl.o ++ ++# all are boolean ++aufs-$(CONFIG_PROC_FS) += procfs.o plink.o ++aufs-$(CONFIG_SYSFS) += sysfs.o ++aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o ++aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o ++aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o ++aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o ++aufs-$(CONFIG_AUFS_EXPORT) += export.o ++aufs-$(CONFIG_AUFS_XATTR) += xattr.o ++aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o ++aufs-$(CONFIG_AUFS_FHSM) += fhsm.o ++aufs-$(CONFIG_AUFS_POLL) += poll.o ++aufs-$(CONFIG_AUFS_RDU) += rdu.o ++aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o ++aufs-$(CONFIG_AUFS_DEBUG) += debug.o ++aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o +diff -Nur linux-4.0.4.orig/fs/aufs/module.c linux-4.0.4/fs/aufs/module.c +--- linux-4.0.4.orig/fs/aufs/module.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/module.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,196 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * module global variables and operations ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp) ++{ ++ if (new_sz <= nused) ++ return p; ++ ++ p = krealloc(p, new_sz, gfp); ++ if (p) ++ memset(p + nused, 0, new_sz - nused); ++ return p; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * aufs caches ++ */ ++struct kmem_cache *au_cachep[AuCache_Last]; ++static int __init au_cache_init(void) ++{ ++ au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); ++ if (au_cachep[AuCache_DINFO]) ++ /* SLAB_DESTROY_BY_RCU */ ++ au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, ++ au_icntnr_init_once); ++ if (au_cachep[AuCache_ICNTNR]) ++ au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo, ++ au_fi_init_once); ++ if (au_cachep[AuCache_FINFO]) ++ au_cachep[AuCache_VDIR] = AuCache(au_vdir); ++ if (au_cachep[AuCache_VDIR]) ++ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); ++ if (au_cachep[AuCache_DEHSTR]) ++ return 0; ++ ++ return -ENOMEM; ++} ++ ++static void au_cache_fin(void) ++{ ++ int i; ++ ++ /* ++ * Make sure all delayed rcu free inodes are flushed before we ++ * destroy cache. ++ */ ++ rcu_barrier(); ++ ++ /* excluding AuCache_HNOTIFY */ ++ BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last); ++ for (i = 0; i < AuCache_HNOTIFY; i++) ++ if (au_cachep[i]) { ++ kmem_cache_destroy(au_cachep[i]); ++ au_cachep[i] = NULL; ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_dir_roflags; ++ ++#ifdef CONFIG_AUFS_SBILIST ++/* ++ * iterate_supers_type() doesn't protect us from ++ * remounting (branch management) ++ */ ++struct au_splhead au_sbilist; ++#endif ++ ++struct lock_class_key au_lc_key[AuLcKey_Last]; ++ ++/* ++ * functions for module interface. ++ */ ++MODULE_LICENSE("GPL"); ++/* MODULE_LICENSE("GPL v2"); */ ++MODULE_AUTHOR("Junjiro R. Okajima "); ++MODULE_DESCRIPTION(AUFS_NAME ++ " -- Advanced multi layered unification filesystem"); ++MODULE_VERSION(AUFS_VERSION); ++ ++/* this module parameter has no meaning when SYSFS is disabled */ ++int sysaufs_brs = 1; ++MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); ++module_param_named(brs, sysaufs_brs, int, S_IRUGO); ++ ++/* this module parameter has no meaning when USER_NS is disabled */ ++static bool au_userns; ++MODULE_PARM_DESC(allow_userns, "allow unprivileged to mount under userns"); ++module_param_named(allow_userns, au_userns, bool, S_IRUGO); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ ++ ++int au_seq_path(struct seq_file *seq, struct path *path) ++{ ++ return seq_path(seq, path, au_esc_chars); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int __init aufs_init(void) ++{ ++ int err, i; ++ char *p; ++ ++ p = au_esc_chars; ++ for (i = 1; i <= ' '; i++) ++ *p++ = i; ++ *p++ = '\\'; ++ *p++ = '\x7f'; ++ *p = 0; ++ ++ au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); ++ ++ au_sbilist_init(); ++ sysaufs_brs_init(); ++ au_debug_init(); ++ au_dy_init(); ++ err = sysaufs_init(); ++ if (unlikely(err)) ++ goto out; ++ err = au_procfs_init(); ++ if (unlikely(err)) ++ goto out_sysaufs; ++ err = au_wkq_init(); ++ if (unlikely(err)) ++ goto out_procfs; ++ err = au_loopback_init(); ++ if (unlikely(err)) ++ goto out_wkq; ++ err = au_hnotify_init(); ++ if (unlikely(err)) ++ goto out_loopback; ++ err = au_sysrq_init(); ++ if (unlikely(err)) ++ goto out_hin; ++ err = au_cache_init(); ++ if (unlikely(err)) ++ goto out_sysrq; ++ ++ aufs_fs_type.fs_flags |= au_userns ? FS_USERNS_MOUNT : 0; ++ err = register_filesystem(&aufs_fs_type); ++ if (unlikely(err)) ++ goto out_cache; ++ ++ /* since we define pr_fmt, call printk directly */ ++ printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n"); ++ goto out; /* success */ ++ ++out_cache: ++ au_cache_fin(); ++out_sysrq: ++ au_sysrq_fin(); ++out_hin: ++ au_hnotify_fin(); ++out_loopback: ++ au_loopback_fin(); ++out_wkq: ++ au_wkq_fin(); ++out_procfs: ++ au_procfs_fin(); ++out_sysaufs: ++ sysaufs_fin(); ++ au_dy_fin(); ++out: ++ return err; ++} ++ ++static void __exit aufs_exit(void) ++{ ++ unregister_filesystem(&aufs_fs_type); ++ au_cache_fin(); ++ au_sysrq_fin(); ++ au_hnotify_fin(); ++ au_loopback_fin(); ++ au_wkq_fin(); ++ au_procfs_fin(); ++ sysaufs_fin(); ++ au_dy_fin(); ++} ++ ++module_init(aufs_init); ++module_exit(aufs_exit); +diff -Nur linux-4.0.4.orig/fs/aufs/module.h linux-4.0.4/fs/aufs/module.h +--- linux-4.0.4.orig/fs/aufs/module.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/module.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,91 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * module initialization and module-global ++ */ ++ ++#ifndef __AUFS_MODULE_H__ ++#define __AUFS_MODULE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++struct path; ++struct seq_file; ++ ++/* module parameters */ ++extern int sysaufs_brs; ++ ++/* ---------------------------------------------------------------------- */ ++ ++extern int au_dir_roflags; ++ ++enum { ++ AuLcNonDir_FIINFO, ++ AuLcNonDir_DIINFO, ++ AuLcNonDir_IIINFO, ++ ++ AuLcDir_FIINFO, ++ AuLcDir_DIINFO, ++ AuLcDir_IIINFO, ++ ++ AuLcSymlink_DIINFO, ++ AuLcSymlink_IIINFO, ++ ++ AuLcKey_Last ++}; ++extern struct lock_class_key au_lc_key[AuLcKey_Last]; ++ ++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); ++int au_seq_path(struct seq_file *seq, struct path *path); ++ ++#ifdef CONFIG_PROC_FS ++/* procfs.c */ ++int __init au_procfs_init(void); ++void au_procfs_fin(void); ++#else ++AuStubInt0(au_procfs_init, void); ++AuStubVoid(au_procfs_fin, void); ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* kmem cache */ ++enum { ++ AuCache_DINFO, ++ AuCache_ICNTNR, ++ AuCache_FINFO, ++ AuCache_VDIR, ++ AuCache_DEHSTR, ++ AuCache_HNOTIFY, /* must be last */ ++ AuCache_Last ++}; ++ ++#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) ++#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) ++#define AuCacheCtor(type, ctor) \ ++ kmem_cache_create(#type, sizeof(struct type), \ ++ __alignof__(struct type), AuCacheFlags, ctor) ++ ++extern struct kmem_cache *au_cachep[]; ++ ++#define AuCacheFuncs(name, index) \ ++static inline struct au_##name *au_cache_alloc_##name(void) \ ++{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \ ++static inline void au_cache_free_##name(struct au_##name *p) \ ++{ kmem_cache_free(au_cachep[AuCache_##index], p); } ++ ++AuCacheFuncs(dinfo, DINFO); ++AuCacheFuncs(icntnr, ICNTNR); ++AuCacheFuncs(finfo, FINFO); ++AuCacheFuncs(vdir, VDIR); ++AuCacheFuncs(vdir_dehstr, DEHSTR); ++#ifdef CONFIG_AUFS_HNOTIFY ++AuCacheFuncs(hnotify, HNOTIFY); ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_MODULE_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/mvdown.c linux-4.0.4/fs/aufs/mvdown.c +--- linux-4.0.4.orig/fs/aufs/mvdown.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/mvdown.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,681 @@ ++/* ++ * Copyright (C) 2011-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * move-down, opposite of copy-up ++ */ ++ ++#include "aufs.h" ++ ++struct au_mvd_args { ++ struct { ++ struct super_block *h_sb; ++ struct dentry *h_parent; ++ struct au_hinode *hdir; ++ struct inode *h_dir, *h_inode; ++ struct au_pin pin; ++ } info[AUFS_MVDOWN_NARRAY]; ++ ++ struct aufs_mvdown mvdown; ++ struct dentry *dentry, *parent; ++ struct inode *inode, *dir; ++ struct super_block *sb; ++ aufs_bindex_t bopq, bwh, bfound; ++ unsigned char rename_lock; ++}; ++ ++#define mvd_errno mvdown.au_errno ++#define mvd_bsrc mvdown.stbr[AUFS_MVDOWN_UPPER].bindex ++#define mvd_src_brid mvdown.stbr[AUFS_MVDOWN_UPPER].brid ++#define mvd_bdst mvdown.stbr[AUFS_MVDOWN_LOWER].bindex ++#define mvd_dst_brid mvdown.stbr[AUFS_MVDOWN_LOWER].brid ++ ++#define mvd_h_src_sb info[AUFS_MVDOWN_UPPER].h_sb ++#define mvd_h_src_parent info[AUFS_MVDOWN_UPPER].h_parent ++#define mvd_hdir_src info[AUFS_MVDOWN_UPPER].hdir ++#define mvd_h_src_dir info[AUFS_MVDOWN_UPPER].h_dir ++#define mvd_h_src_inode info[AUFS_MVDOWN_UPPER].h_inode ++#define mvd_pin_src info[AUFS_MVDOWN_UPPER].pin ++ ++#define mvd_h_dst_sb info[AUFS_MVDOWN_LOWER].h_sb ++#define mvd_h_dst_parent info[AUFS_MVDOWN_LOWER].h_parent ++#define mvd_hdir_dst info[AUFS_MVDOWN_LOWER].hdir ++#define mvd_h_dst_dir info[AUFS_MVDOWN_LOWER].h_dir ++#define mvd_h_dst_inode info[AUFS_MVDOWN_LOWER].h_inode ++#define mvd_pin_dst info[AUFS_MVDOWN_LOWER].pin ++ ++#define AU_MVD_PR(flag, ...) do { \ ++ if (flag) \ ++ pr_err(__VA_ARGS__); \ ++ } while (0) ++ ++static int find_lower_writable(struct au_mvd_args *a) ++{ ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend; ++ struct au_branch *br; ++ ++ sb = a->sb; ++ bindex = a->mvd_bsrc; ++ bend = au_sbend(sb); ++ if (a->mvdown.flags & AUFS_MVDOWN_FHSM_LOWER) ++ for (bindex++; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_fhsm(br->br_perm) ++ && (!(au_br_sb(br)->s_flags & MS_RDONLY))) ++ return bindex; ++ } ++ else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER)) ++ for (bindex++; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!au_br_rdonly(br)) ++ return bindex; ++ } ++ else ++ for (bindex++; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!(au_br_sb(br)->s_flags & MS_RDONLY)) { ++ if (au_br_rdonly(br)) ++ a->mvdown.flags ++ |= AUFS_MVDOWN_ROLOWER_R; ++ return bindex; ++ } ++ } ++ ++ return -1; ++} ++ ++/* make the parent dir on bdst */ ++static int au_do_mkdir(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ ++ err = 0; ++ a->mvd_hdir_src = au_hi(a->dir, a->mvd_bsrc); ++ a->mvd_hdir_dst = au_hi(a->dir, a->mvd_bdst); ++ a->mvd_h_src_parent = au_h_dptr(a->parent, a->mvd_bsrc); ++ a->mvd_h_dst_parent = NULL; ++ if (au_dbend(a->parent) >= a->mvd_bdst) ++ a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); ++ if (!a->mvd_h_dst_parent) { ++ err = au_cpdown_dirs(a->dentry, a->mvd_bdst); ++ if (unlikely(err)) { ++ AU_MVD_PR(dmsg, "cpdown_dirs failed\n"); ++ goto out; ++ } ++ a->mvd_h_dst_parent = au_h_dptr(a->parent, a->mvd_bdst); ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* lock them all */ ++static int au_do_lock(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct dentry *h_trap; ++ ++ a->mvd_h_src_sb = au_sbr_sb(a->sb, a->mvd_bsrc); ++ a->mvd_h_dst_sb = au_sbr_sb(a->sb, a->mvd_bdst); ++ err = au_pin(&a->mvd_pin_dst, a->dentry, a->mvd_bdst, ++ au_opt_udba(a->sb), ++ AuPin_MNT_WRITE | AuPin_DI_LOCKED); ++ AuTraceErr(err); ++ if (unlikely(err)) { ++ AU_MVD_PR(dmsg, "pin_dst failed\n"); ++ goto out; ++ } ++ ++ if (a->mvd_h_src_sb != a->mvd_h_dst_sb) { ++ a->rename_lock = 0; ++ au_pin_init(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, ++ AuLsc_DI_PARENT, AuLsc_I_PARENT3, ++ au_opt_udba(a->sb), ++ AuPin_MNT_WRITE | AuPin_DI_LOCKED); ++ err = au_do_pin(&a->mvd_pin_src); ++ AuTraceErr(err); ++ a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode; ++ if (unlikely(err)) { ++ AU_MVD_PR(dmsg, "pin_src failed\n"); ++ goto out_dst; ++ } ++ goto out; /* success */ ++ } ++ ++ a->rename_lock = 1; ++ au_pin_hdir_unlock(&a->mvd_pin_dst); ++ err = au_pin(&a->mvd_pin_src, a->dentry, a->mvd_bsrc, ++ au_opt_udba(a->sb), ++ AuPin_MNT_WRITE | AuPin_DI_LOCKED); ++ AuTraceErr(err); ++ a->mvd_h_src_dir = a->mvd_h_src_parent->d_inode; ++ if (unlikely(err)) { ++ AU_MVD_PR(dmsg, "pin_src failed\n"); ++ au_pin_hdir_lock(&a->mvd_pin_dst); ++ goto out_dst; ++ } ++ au_pin_hdir_unlock(&a->mvd_pin_src); ++ h_trap = vfsub_lock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, ++ a->mvd_h_dst_parent, a->mvd_hdir_dst); ++ if (h_trap) { ++ err = (h_trap != a->mvd_h_src_parent); ++ if (err) ++ err = (h_trap != a->mvd_h_dst_parent); ++ } ++ BUG_ON(err); /* it should never happen */ ++ if (unlikely(a->mvd_h_src_dir != au_pinned_h_dir(&a->mvd_pin_src))) { ++ err = -EBUSY; ++ AuTraceErr(err); ++ vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, ++ a->mvd_h_dst_parent, a->mvd_hdir_dst); ++ au_pin_hdir_lock(&a->mvd_pin_src); ++ au_unpin(&a->mvd_pin_src); ++ au_pin_hdir_lock(&a->mvd_pin_dst); ++ goto out_dst; ++ } ++ goto out; /* success */ ++ ++out_dst: ++ au_unpin(&a->mvd_pin_dst); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static void au_do_unlock(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ if (!a->rename_lock) ++ au_unpin(&a->mvd_pin_src); ++ else { ++ vfsub_unlock_rename(a->mvd_h_src_parent, a->mvd_hdir_src, ++ a->mvd_h_dst_parent, a->mvd_hdir_dst); ++ au_pin_hdir_lock(&a->mvd_pin_src); ++ au_unpin(&a->mvd_pin_src); ++ au_pin_hdir_lock(&a->mvd_pin_dst); ++ } ++ au_unpin(&a->mvd_pin_dst); ++} ++ ++/* copy-down the file */ ++static int au_do_cpdown(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct au_cp_generic cpg = { ++ .dentry = a->dentry, ++ .bdst = a->mvd_bdst, ++ .bsrc = a->mvd_bsrc, ++ .len = -1, ++ .pin = &a->mvd_pin_dst, ++ .flags = AuCpup_DTIME | AuCpup_HOPEN ++ }; ++ ++ AuDbg("b%d, b%d\n", cpg.bsrc, cpg.bdst); ++ if (a->mvdown.flags & AUFS_MVDOWN_OWLOWER) ++ au_fset_cpup(cpg.flags, OVERWRITE); ++ if (a->mvdown.flags & AUFS_MVDOWN_ROLOWER) ++ au_fset_cpup(cpg.flags, RWDST); ++ err = au_sio_cpdown_simple(&cpg); ++ if (unlikely(err)) ++ AU_MVD_PR(dmsg, "cpdown failed\n"); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * unlink the whiteout on bdst if exist which may be created by UDBA while we ++ * were sleeping ++ */ ++static int au_do_unlink_wh(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct path h_path; ++ struct au_branch *br; ++ struct inode *delegated; ++ ++ br = au_sbr(a->sb, a->mvd_bdst); ++ h_path.dentry = au_wh_lkup(a->mvd_h_dst_parent, &a->dentry->d_name, br); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) { ++ AU_MVD_PR(dmsg, "wh_lkup failed\n"); ++ goto out; ++ } ++ ++ err = 0; ++ if (h_path.dentry->d_inode) { ++ h_path.mnt = au_br_mnt(br); ++ delegated = NULL; ++ err = vfsub_unlink(a->mvd_h_dst_parent->d_inode, &h_path, ++ &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ if (unlikely(err)) ++ AU_MVD_PR(dmsg, "wh_unlink failed\n"); ++ } ++ dput(h_path.dentry); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * unlink the topmost h_dentry ++ */ ++static int au_do_unlink(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct path h_path; ++ struct inode *delegated; ++ ++ h_path.mnt = au_sbr_mnt(a->sb, a->mvd_bsrc); ++ h_path.dentry = au_h_dptr(a->dentry, a->mvd_bsrc); ++ delegated = NULL; ++ err = vfsub_unlink(a->mvd_h_src_dir, &h_path, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ if (unlikely(err)) ++ AU_MVD_PR(dmsg, "unlink failed\n"); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* Since mvdown succeeded, we ignore an error of this function */ ++static void au_do_stfs(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct au_branch *br; ++ ++ a->mvdown.flags |= AUFS_MVDOWN_STFS_FAILED; ++ br = au_sbr(a->sb, a->mvd_bsrc); ++ err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_UPPER].stfs); ++ if (!err) { ++ br = au_sbr(a->sb, a->mvd_bdst); ++ a->mvdown.stbr[AUFS_MVDOWN_LOWER].brid = br->br_id; ++ err = au_br_stfs(br, &a->mvdown.stbr[AUFS_MVDOWN_LOWER].stfs); ++ } ++ if (!err) ++ a->mvdown.flags &= ~AUFS_MVDOWN_STFS_FAILED; ++ else ++ AU_MVD_PR(dmsg, "statfs failed (%d), ignored\n", err); ++} ++ ++/* ++ * copy-down the file and unlink the bsrc file. ++ * - unlink the bdst whout if exist ++ * - copy-down the file (with whtmp name and rename) ++ * - unlink the bsrc file ++ */ ++static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ ++ err = au_do_mkdir(dmsg, a); ++ if (!err) ++ err = au_do_lock(dmsg, a); ++ if (unlikely(err)) ++ goto out; ++ ++ /* ++ * do not revert the activities we made on bdst since they should be ++ * harmless in aufs. ++ */ ++ ++ err = au_do_cpdown(dmsg, a); ++ if (!err) ++ err = au_do_unlink_wh(dmsg, a); ++ if (!err && !(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) ++ err = au_do_unlink(dmsg, a); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ AuDbg("%pd2, 0x%x, %d --> %d\n", ++ a->dentry, a->mvdown.flags, a->mvd_bsrc, a->mvd_bdst); ++ if (find_lower_writable(a) < 0) ++ a->mvdown.flags |= AUFS_MVDOWN_BOTTOM; ++ ++ if (a->mvdown.flags & AUFS_MVDOWN_STFS) ++ au_do_stfs(dmsg, a); ++ ++ /* maintain internal array */ ++ if (!(a->mvdown.flags & AUFS_MVDOWN_KUPPER)) { ++ au_set_h_dptr(a->dentry, a->mvd_bsrc, NULL); ++ au_set_dbstart(a->dentry, a->mvd_bdst); ++ au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0); ++ au_set_ibstart(a->inode, a->mvd_bdst); ++ } ++ if (au_dbend(a->dentry) < a->mvd_bdst) ++ au_set_dbend(a->dentry, a->mvd_bdst); ++ if (au_ibend(a->inode) < a->mvd_bdst) ++ au_set_ibend(a->inode, a->mvd_bdst); ++ ++out_unlock: ++ au_do_unlock(dmsg, a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* make sure the file is idle */ ++static int au_mvd_args_busy(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err, plinked; ++ ++ err = 0; ++ plinked = !!au_opt_test(au_mntflags(a->sb), PLINK); ++ if (au_dbstart(a->dentry) == a->mvd_bsrc ++ && au_dcount(a->dentry) == 1 ++ && atomic_read(&a->inode->i_count) == 1 ++ /* && a->mvd_h_src_inode->i_nlink == 1 */ ++ && (!plinked || !au_plink_test(a->inode)) ++ && a->inode->i_nlink == 1) ++ goto out; ++ ++ err = -EBUSY; ++ AU_MVD_PR(dmsg, ++ "b%d, d{b%d, c%d?}, i{c%d?, l%u}, hi{l%u}, p{%d, %d}\n", ++ a->mvd_bsrc, au_dbstart(a->dentry), au_dcount(a->dentry), ++ atomic_read(&a->inode->i_count), a->inode->i_nlink, ++ a->mvd_h_src_inode->i_nlink, ++ plinked, plinked ? au_plink_test(a->inode) : 0); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* make sure the parent dir is fine */ ++static int au_mvd_args_parent(const unsigned char dmsg, ++ struct au_mvd_args *a) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ ++ err = 0; ++ if (unlikely(au_alive_dir(a->parent))) { ++ err = -ENOENT; ++ AU_MVD_PR(dmsg, "parent dir is dead\n"); ++ goto out; ++ } ++ ++ a->bopq = au_dbdiropq(a->parent); ++ bindex = au_wbr_nonopq(a->dentry, a->mvd_bdst); ++ AuDbg("b%d\n", bindex); ++ if (unlikely((bindex >= 0 && bindex < a->mvd_bdst) ++ || (a->bopq != -1 && a->bopq < a->mvd_bdst))) { ++ err = -EINVAL; ++ a->mvd_errno = EAU_MVDOWN_OPAQUE; ++ AU_MVD_PR(dmsg, "ancestor is opaque b%d, b%d\n", ++ a->bopq, a->mvd_bdst); ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_mvd_args_intermediate(const unsigned char dmsg, ++ struct au_mvd_args *a) ++{ ++ int err; ++ struct au_dinfo *dinfo, *tmp; ++ ++ /* lookup the next lower positive entry */ ++ err = -ENOMEM; ++ tmp = au_di_alloc(a->sb, AuLsc_DI_TMP); ++ if (unlikely(!tmp)) ++ goto out; ++ ++ a->bfound = -1; ++ a->bwh = -1; ++ dinfo = au_di(a->dentry); ++ au_di_cp(tmp, dinfo); ++ au_di_swap(tmp, dinfo); ++ ++ /* returns the number of positive dentries */ ++ err = au_lkup_dentry(a->dentry, a->mvd_bsrc + 1, /*type*/0); ++ if (!err) ++ a->bwh = au_dbwh(a->dentry); ++ else if (err > 0) ++ a->bfound = au_dbstart(a->dentry); ++ ++ au_di_swap(tmp, dinfo); ++ au_rw_write_unlock(&tmp->di_rwsem); ++ au_di_free(tmp); ++ if (unlikely(err < 0)) ++ AU_MVD_PR(dmsg, "failed look-up lower\n"); ++ ++ /* ++ * here, we have these cases. ++ * bfound == -1 ++ * no positive dentry under bsrc. there are more sub-cases. ++ * bwh < 0 ++ * there no whiteout, we can safely move-down. ++ * bwh <= bsrc ++ * impossible ++ * bsrc < bwh && bwh < bdst ++ * there is a whiteout on RO branch. cannot proceed. ++ * bwh == bdst ++ * there is a whiteout on the RW target branch. it should ++ * be removed. ++ * bdst < bwh ++ * there is a whiteout somewhere unrelated branch. ++ * -1 < bfound && bfound <= bsrc ++ * impossible. ++ * bfound < bdst ++ * found, but it is on RO branch between bsrc and bdst. cannot ++ * proceed. ++ * bfound == bdst ++ * found, replace it if AUFS_MVDOWN_FORCE is set. otherwise return ++ * error. ++ * bdst < bfound ++ * found, after we create the file on bdst, it will be hidden. ++ */ ++ ++ AuDebugOn(a->bfound == -1 ++ && a->bwh != -1 ++ && a->bwh <= a->mvd_bsrc); ++ AuDebugOn(-1 < a->bfound ++ && a->bfound <= a->mvd_bsrc); ++ ++ err = -EINVAL; ++ if (a->bfound == -1 ++ && a->mvd_bsrc < a->bwh ++ && a->bwh != -1 ++ && a->bwh < a->mvd_bdst) { ++ a->mvd_errno = EAU_MVDOWN_WHITEOUT; ++ AU_MVD_PR(dmsg, "bsrc %d, bdst %d, bfound %d, bwh %d\n", ++ a->mvd_bsrc, a->mvd_bdst, a->bfound, a->bwh); ++ goto out; ++ } else if (a->bfound != -1 && a->bfound < a->mvd_bdst) { ++ a->mvd_errno = EAU_MVDOWN_UPPER; ++ AU_MVD_PR(dmsg, "bdst %d, bfound %d\n", ++ a->mvd_bdst, a->bfound); ++ goto out; ++ } ++ ++ err = 0; /* success */ ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_mvd_args_exist(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ ++ err = 0; ++ if (!(a->mvdown.flags & AUFS_MVDOWN_OWLOWER) ++ && a->bfound == a->mvd_bdst) ++ err = -EEXIST; ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_mvd_args(const unsigned char dmsg, struct au_mvd_args *a) ++{ ++ int err; ++ struct au_branch *br; ++ ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(a->inode->i_mode))) ++ goto out; ++ ++ err = -EINVAL; ++ if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_UPPER)) ++ a->mvd_bsrc = au_ibstart(a->inode); ++ else { ++ a->mvd_bsrc = au_br_index(a->sb, a->mvd_src_brid); ++ if (unlikely(a->mvd_bsrc < 0 ++ || (a->mvd_bsrc < au_dbstart(a->dentry) ++ || au_dbend(a->dentry) < a->mvd_bsrc ++ || !au_h_dptr(a->dentry, a->mvd_bsrc)) ++ || (a->mvd_bsrc < au_ibstart(a->inode) ++ || au_ibend(a->inode) < a->mvd_bsrc ++ || !au_h_iptr(a->inode, a->mvd_bsrc)))) { ++ a->mvd_errno = EAU_MVDOWN_NOUPPER; ++ AU_MVD_PR(dmsg, "no upper\n"); ++ goto out; ++ } ++ } ++ if (unlikely(a->mvd_bsrc == au_sbend(a->sb))) { ++ a->mvd_errno = EAU_MVDOWN_BOTTOM; ++ AU_MVD_PR(dmsg, "on the bottom\n"); ++ goto out; ++ } ++ a->mvd_h_src_inode = au_h_iptr(a->inode, a->mvd_bsrc); ++ br = au_sbr(a->sb, a->mvd_bsrc); ++ err = au_br_rdonly(br); ++ if (!(a->mvdown.flags & AUFS_MVDOWN_ROUPPER)) { ++ if (unlikely(err)) ++ goto out; ++ } else if (!(vfsub_native_ro(a->mvd_h_src_inode) ++ || IS_APPEND(a->mvd_h_src_inode))) { ++ if (err) ++ a->mvdown.flags |= AUFS_MVDOWN_ROUPPER_R; ++ /* go on */ ++ } else ++ goto out; ++ ++ err = -EINVAL; ++ if (!(a->mvdown.flags & AUFS_MVDOWN_BRID_LOWER)) { ++ a->mvd_bdst = find_lower_writable(a); ++ if (unlikely(a->mvd_bdst < 0)) { ++ a->mvd_errno = EAU_MVDOWN_BOTTOM; ++ AU_MVD_PR(dmsg, "no writable lower branch\n"); ++ goto out; ++ } ++ } else { ++ a->mvd_bdst = au_br_index(a->sb, a->mvd_dst_brid); ++ if (unlikely(a->mvd_bdst < 0 ++ || au_sbend(a->sb) < a->mvd_bdst)) { ++ a->mvd_errno = EAU_MVDOWN_NOLOWERBR; ++ AU_MVD_PR(dmsg, "no lower brid\n"); ++ goto out; ++ } ++ } ++ ++ err = au_mvd_args_busy(dmsg, a); ++ if (!err) ++ err = au_mvd_args_parent(dmsg, a); ++ if (!err) ++ err = au_mvd_args_intermediate(dmsg, a); ++ if (!err) ++ err = au_mvd_args_exist(dmsg, a); ++ if (!err) ++ AuDbg("b%d, b%d\n", a->mvd_bsrc, a->mvd_bdst); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg) ++{ ++ int err, e; ++ unsigned char dmsg; ++ struct au_mvd_args *args; ++ ++ err = -EPERM; ++ if (unlikely(!capable(CAP_SYS_ADMIN))) ++ goto out; ++ ++ err = -ENOMEM; ++ args = kmalloc(sizeof(*args), GFP_NOFS); ++ if (unlikely(!args)) ++ goto out; ++ ++ err = copy_from_user(&args->mvdown, uarg, sizeof(args->mvdown)); ++ if (!err) ++ err = !access_ok(VERIFY_WRITE, uarg, sizeof(*uarg)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out_free; ++ } ++ AuDbg("flags 0x%x\n", args->mvdown.flags); ++ args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R); ++ args->mvdown.au_errno = 0; ++ args->dentry = dentry; ++ args->inode = dentry->d_inode; ++ args->sb = dentry->d_sb; ++ ++ err = -ENOENT; ++ dmsg = !!(args->mvdown.flags & AUFS_MVDOWN_DMSG); ++ args->parent = dget_parent(dentry); ++ args->dir = args->parent->d_inode; ++ mutex_lock_nested(&args->dir->i_mutex, I_MUTEX_PARENT); ++ dput(args->parent); ++ if (unlikely(args->parent != dentry->d_parent)) { ++ AU_MVD_PR(dmsg, "parent dir is moved\n"); ++ goto out_dir; ++ } ++ ++ mutex_lock_nested(&args->inode->i_mutex, I_MUTEX_CHILD); ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH); ++ if (unlikely(err)) ++ goto out_inode; ++ ++ di_write_lock_parent(args->parent); ++ err = au_mvd_args(dmsg, args); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ err = au_do_mvdown(dmsg, args); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ au_cpup_attr_timesizes(args->dir); ++ au_cpup_attr_timesizes(args->inode); ++ au_cpup_igen(args->inode, au_h_iptr(args->inode, args->mvd_bdst)); ++ /* au_digen_dec(dentry); */ ++ ++out_parent: ++ di_write_unlock(args->parent); ++ aufs_read_unlock(dentry, AuLock_DW); ++out_inode: ++ mutex_unlock(&args->inode->i_mutex); ++out_dir: ++ mutex_unlock(&args->dir->i_mutex); ++out_free: ++ e = copy_to_user(uarg, &args->mvdown, sizeof(args->mvdown)); ++ if (unlikely(e)) ++ err = -EFAULT; ++ kfree(args); ++out: ++ AuTraceErr(err); ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/opts.c linux-4.0.4/fs/aufs/opts.c +--- linux-4.0.4.orig/fs/aufs/opts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/opts.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,1822 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * mount options/flags ++ */ ++ ++#include ++#include /* a distribution requires */ ++#include ++#include "aufs.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++enum { ++ Opt_br, ++ Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend, ++ Opt_idel, Opt_imod, ++ Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, ++ Opt_rdblk_def, Opt_rdhash_def, ++ Opt_xino, Opt_noxino, ++ Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino, ++ Opt_trunc_xino_path, Opt_itrunc_xino, ++ Opt_trunc_xib, Opt_notrunc_xib, ++ Opt_shwh, Opt_noshwh, ++ Opt_plink, Opt_noplink, Opt_list_plink, ++ Opt_udba, ++ Opt_dio, Opt_nodio, ++ Opt_diropq_a, Opt_diropq_w, ++ Opt_warn_perm, Opt_nowarn_perm, ++ Opt_wbr_copyup, Opt_wbr_create, ++ Opt_fhsm_sec, ++ Opt_verbose, Opt_noverbose, ++ Opt_sum, Opt_nosum, Opt_wsum, ++ Opt_dirperm1, Opt_nodirperm1, ++ Opt_acl, Opt_noacl, ++ Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err ++}; ++ ++static match_table_t options = { ++ {Opt_br, "br=%s"}, ++ {Opt_br, "br:%s"}, ++ ++ {Opt_add, "add=%d:%s"}, ++ {Opt_add, "add:%d:%s"}, ++ {Opt_add, "ins=%d:%s"}, ++ {Opt_add, "ins:%d:%s"}, ++ {Opt_append, "append=%s"}, ++ {Opt_append, "append:%s"}, ++ {Opt_prepend, "prepend=%s"}, ++ {Opt_prepend, "prepend:%s"}, ++ ++ {Opt_del, "del=%s"}, ++ {Opt_del, "del:%s"}, ++ /* {Opt_idel, "idel:%d"}, */ ++ {Opt_mod, "mod=%s"}, ++ {Opt_mod, "mod:%s"}, ++ /* {Opt_imod, "imod:%d:%s"}, */ ++ ++ {Opt_dirwh, "dirwh=%d"}, ++ ++ {Opt_xino, "xino=%s"}, ++ {Opt_noxino, "noxino"}, ++ {Opt_trunc_xino, "trunc_xino"}, ++ {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"}, ++ {Opt_notrunc_xino, "notrunc_xino"}, ++ {Opt_trunc_xino_path, "trunc_xino=%s"}, ++ {Opt_itrunc_xino, "itrunc_xino=%d"}, ++ /* {Opt_zxino, "zxino=%s"}, */ ++ {Opt_trunc_xib, "trunc_xib"}, ++ {Opt_notrunc_xib, "notrunc_xib"}, ++ ++#ifdef CONFIG_PROC_FS ++ {Opt_plink, "plink"}, ++#else ++ {Opt_ignore_silent, "plink"}, ++#endif ++ ++ {Opt_noplink, "noplink"}, ++ ++#ifdef CONFIG_AUFS_DEBUG ++ {Opt_list_plink, "list_plink"}, ++#endif ++ ++ {Opt_udba, "udba=%s"}, ++ ++ {Opt_dio, "dio"}, ++ {Opt_nodio, "nodio"}, ++ ++#ifdef CONFIG_AUFS_FHSM ++ {Opt_fhsm_sec, "fhsm_sec=%d"}, ++#else ++ {Opt_ignore_silent, "fhsm_sec=%d"}, ++#endif ++ ++ {Opt_diropq_a, "diropq=always"}, ++ {Opt_diropq_a, "diropq=a"}, ++ {Opt_diropq_w, "diropq=whiteouted"}, ++ {Opt_diropq_w, "diropq=w"}, ++ ++ {Opt_warn_perm, "warn_perm"}, ++ {Opt_nowarn_perm, "nowarn_perm"}, ++ ++ /* keep them temporary */ ++ {Opt_ignore_silent, "nodlgt"}, ++ {Opt_ignore_silent, "clean_plink"}, ++ ++#ifdef CONFIG_AUFS_SHWH ++ {Opt_shwh, "shwh"}, ++#endif ++ {Opt_noshwh, "noshwh"}, ++ ++ {Opt_dirperm1, "dirperm1"}, ++ {Opt_nodirperm1, "nodirperm1"}, ++ ++ {Opt_verbose, "verbose"}, ++ {Opt_verbose, "v"}, ++ {Opt_noverbose, "noverbose"}, ++ {Opt_noverbose, "quiet"}, ++ {Opt_noverbose, "q"}, ++ {Opt_noverbose, "silent"}, ++ ++ {Opt_sum, "sum"}, ++ {Opt_nosum, "nosum"}, ++ {Opt_wsum, "wsum"}, ++ ++ {Opt_rdcache, "rdcache=%d"}, ++ {Opt_rdblk, "rdblk=%d"}, ++ {Opt_rdblk_def, "rdblk=def"}, ++ {Opt_rdhash, "rdhash=%d"}, ++ {Opt_rdhash_def, "rdhash=def"}, ++ ++ {Opt_wbr_create, "create=%s"}, ++ {Opt_wbr_create, "create_policy=%s"}, ++ {Opt_wbr_copyup, "cpup=%s"}, ++ {Opt_wbr_copyup, "copyup=%s"}, ++ {Opt_wbr_copyup, "copyup_policy=%s"}, ++ ++ /* generic VFS flag */ ++#ifdef CONFIG_FS_POSIX_ACL ++ {Opt_acl, "acl"}, ++ {Opt_noacl, "noacl"}, ++#else ++ {Opt_ignore_silent, "acl"}, ++ {Opt_ignore_silent, "noacl"}, ++#endif ++ ++ /* internal use for the scripts */ ++ {Opt_ignore_silent, "si=%s"}, ++ ++ {Opt_br, "dirs=%s"}, ++ {Opt_ignore, "debug=%d"}, ++ {Opt_ignore, "delete=whiteout"}, ++ {Opt_ignore, "delete=all"}, ++ {Opt_ignore, "imap=%s"}, ++ ++ /* temporary workaround, due to old mount(8)? */ ++ {Opt_ignore_silent, "relatime"}, ++ ++ {Opt_err, NULL} ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static const char *au_parser_pattern(int val, match_table_t tbl) ++{ ++ struct match_token *p; ++ ++ p = tbl; ++ while (p->pattern) { ++ if (p->token == val) ++ return p->pattern; ++ p++; ++ } ++ BUG(); ++ return "??"; ++} ++ ++static const char *au_optstr(int *val, match_table_t tbl) ++{ ++ struct match_token *p; ++ int v; ++ ++ v = *val; ++ if (!v) ++ goto out; ++ p = tbl; ++ while (p->pattern) { ++ if (p->token ++ && (v & p->token) == p->token) { ++ *val &= ~p->token; ++ return p->pattern; ++ } ++ p++; ++ } ++ ++out: ++ return NULL; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t brperm = { ++ {AuBrPerm_RO, AUFS_BRPERM_RO}, ++ {AuBrPerm_RR, AUFS_BRPERM_RR}, ++ {AuBrPerm_RW, AUFS_BRPERM_RW}, ++ {0, NULL} ++}; ++ ++static match_table_t brattr = { ++ /* general */ ++ {AuBrAttr_COO_REG, AUFS_BRATTR_COO_REG}, ++ {AuBrAttr_COO_ALL, AUFS_BRATTR_COO_ALL}, ++ /* 'unpin' attrib is meaningless since linux-3.18-rc1 */ ++ {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, ++#ifdef CONFIG_AUFS_FHSM ++ {AuBrAttr_FHSM, AUFS_BRATTR_FHSM}, ++#endif ++#ifdef CONFIG_AUFS_XATTR ++ {AuBrAttr_ICEX, AUFS_BRATTR_ICEX}, ++ {AuBrAttr_ICEX_SEC, AUFS_BRATTR_ICEX_SEC}, ++ {AuBrAttr_ICEX_SYS, AUFS_BRATTR_ICEX_SYS}, ++ {AuBrAttr_ICEX_TR, AUFS_BRATTR_ICEX_TR}, ++ {AuBrAttr_ICEX_USR, AUFS_BRATTR_ICEX_USR}, ++ {AuBrAttr_ICEX_OTH, AUFS_BRATTR_ICEX_OTH}, ++#endif ++ ++ /* ro/rr branch */ ++ {AuBrRAttr_WH, AUFS_BRRATTR_WH}, ++ ++ /* rw branch */ ++ {AuBrWAttr_MOO, AUFS_BRWATTR_MOO}, ++ {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, ++ ++ {0, NULL} ++}; ++ ++static int br_attr_val(char *str, match_table_t table, substring_t args[]) ++{ ++ int attr, v; ++ char *p; ++ ++ attr = 0; ++ do { ++ p = strchr(str, '+'); ++ if (p) ++ *p = 0; ++ v = match_token(str, table, args); ++ if (v) { ++ if (v & AuBrAttr_CMOO_Mask) ++ attr &= ~AuBrAttr_CMOO_Mask; ++ attr |= v; ++ } else { ++ if (p) ++ *p = '+'; ++ pr_warn("ignored branch attribute %s\n", str); ++ break; ++ } ++ if (p) ++ str = p + 1; ++ } while (p); ++ ++ return attr; ++} ++ ++static int au_do_optstr_br_attr(au_br_perm_str_t *str, int perm) ++{ ++ int sz; ++ const char *p; ++ char *q; ++ ++ q = str->a; ++ *q = 0; ++ p = au_optstr(&perm, brattr); ++ if (p) { ++ sz = strlen(p); ++ memcpy(q, p, sz + 1); ++ q += sz; ++ } else ++ goto out; ++ ++ do { ++ p = au_optstr(&perm, brattr); ++ if (p) { ++ *q++ = '+'; ++ sz = strlen(p); ++ memcpy(q, p, sz + 1); ++ q += sz; ++ } ++ } while (p); ++ ++out: ++ return q - str->a; ++} ++ ++static int noinline_for_stack br_perm_val(char *perm) ++{ ++ int val, bad, sz; ++ char *p; ++ substring_t args[MAX_OPT_ARGS]; ++ au_br_perm_str_t attr; ++ ++ p = strchr(perm, '+'); ++ if (p) ++ *p = 0; ++ val = match_token(perm, brperm, args); ++ if (!val) { ++ if (p) ++ *p = '+'; ++ pr_warn("ignored branch permission %s\n", perm); ++ val = AuBrPerm_RO; ++ goto out; ++ } ++ if (!p) ++ goto out; ++ ++ val |= br_attr_val(p + 1, brattr, args); ++ ++ bad = 0; ++ switch (val & AuBrPerm_Mask) { ++ case AuBrPerm_RO: ++ case AuBrPerm_RR: ++ bad = val & AuBrWAttr_Mask; ++ val &= ~AuBrWAttr_Mask; ++ break; ++ case AuBrPerm_RW: ++ bad = val & AuBrRAttr_Mask; ++ val &= ~AuBrRAttr_Mask; ++ break; ++ } ++ ++ /* ++ * 'unpin' attrib becomes meaningless since linux-3.18-rc1, but aufs ++ * does not treat it as an error, just warning. ++ * this is a tiny guard for the user operation. ++ */ ++ if (val & AuBrAttr_UNPIN) { ++ bad |= AuBrAttr_UNPIN; ++ val &= ~AuBrAttr_UNPIN; ++ } ++ ++ if (unlikely(bad)) { ++ sz = au_do_optstr_br_attr(&attr, bad); ++ AuDebugOn(!sz); ++ pr_warn("ignored branch attribute %s\n", attr.a); ++ } ++ ++out: ++ return val; ++} ++ ++void au_optstr_br_perm(au_br_perm_str_t *str, int perm) ++{ ++ au_br_perm_str_t attr; ++ const char *p; ++ char *q; ++ int sz; ++ ++ q = str->a; ++ p = au_optstr(&perm, brperm); ++ AuDebugOn(!p || !*p); ++ sz = strlen(p); ++ memcpy(q, p, sz + 1); ++ q += sz; ++ ++ sz = au_do_optstr_br_attr(&attr, perm); ++ if (sz) { ++ *q++ = '+'; ++ memcpy(q, attr.a, sz + 1); ++ } ++ ++ AuDebugOn(strlen(str->a) >= sizeof(str->a)); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t udbalevel = { ++ {AuOpt_UDBA_REVAL, "reval"}, ++ {AuOpt_UDBA_NONE, "none"}, ++#ifdef CONFIG_AUFS_HNOTIFY ++ {AuOpt_UDBA_HNOTIFY, "notify"}, /* abstraction */ ++#ifdef CONFIG_AUFS_HFSNOTIFY ++ {AuOpt_UDBA_HNOTIFY, "fsnotify"}, ++#endif ++#endif ++ {-1, NULL} ++}; ++ ++static int noinline_for_stack udba_val(char *str) ++{ ++ substring_t args[MAX_OPT_ARGS]; ++ ++ return match_token(str, udbalevel, args); ++} ++ ++const char *au_optstr_udba(int udba) ++{ ++ return au_parser_pattern(udba, udbalevel); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t au_wbr_create_policy = { ++ {AuWbrCreate_TDP, "tdp"}, ++ {AuWbrCreate_TDP, "top-down-parent"}, ++ {AuWbrCreate_RR, "rr"}, ++ {AuWbrCreate_RR, "round-robin"}, ++ {AuWbrCreate_MFS, "mfs"}, ++ {AuWbrCreate_MFS, "most-free-space"}, ++ {AuWbrCreate_MFSV, "mfs:%d"}, ++ {AuWbrCreate_MFSV, "most-free-space:%d"}, ++ ++ {AuWbrCreate_MFSRR, "mfsrr:%d"}, ++ {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, ++ {AuWbrCreate_PMFS, "pmfs"}, ++ {AuWbrCreate_PMFSV, "pmfs:%d"}, ++ {AuWbrCreate_PMFSRR, "pmfsrr:%d"}, ++ {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"}, ++ ++ {-1, NULL} ++}; ++ ++/* ++ * cf. linux/lib/parser.c and cmdline.c ++ * gave up calling memparse() since it uses simple_strtoull() instead of ++ * kstrto...(). ++ */ ++static int noinline_for_stack ++au_match_ull(substring_t *s, unsigned long long *result) ++{ ++ int err; ++ unsigned int len; ++ char a[32]; ++ ++ err = -ERANGE; ++ len = s->to - s->from; ++ if (len + 1 <= sizeof(a)) { ++ memcpy(a, s->from, len); ++ a[len] = '\0'; ++ err = kstrtoull(a, 0, result); ++ } ++ return err; ++} ++ ++static int au_wbr_mfs_wmark(substring_t *arg, char *str, ++ struct au_opt_wbr_create *create) ++{ ++ int err; ++ unsigned long long ull; ++ ++ err = 0; ++ if (!au_match_ull(arg, &ull)) ++ create->mfsrr_watermark = ull; ++ else { ++ pr_err("bad integer in %s\n", str); ++ err = -EINVAL; ++ } ++ ++ return err; ++} ++ ++static int au_wbr_mfs_sec(substring_t *arg, char *str, ++ struct au_opt_wbr_create *create) ++{ ++ int n, err; ++ ++ err = 0; ++ if (!match_int(arg, &n) && 0 <= n && n <= AUFS_MFS_MAX_SEC) ++ create->mfs_second = n; ++ else { ++ pr_err("bad integer in %s\n", str); ++ err = -EINVAL; ++ } ++ ++ return err; ++} ++ ++static int noinline_for_stack ++au_wbr_create_val(char *str, struct au_opt_wbr_create *create) ++{ ++ int err, e; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ err = match_token(str, au_wbr_create_policy, args); ++ create->wbr_create = err; ++ switch (err) { ++ case AuWbrCreate_MFSRRV: ++ case AuWbrCreate_PMFSRRV: ++ e = au_wbr_mfs_wmark(&args[0], str, create); ++ if (!e) ++ e = au_wbr_mfs_sec(&args[1], str, create); ++ if (unlikely(e)) ++ err = e; ++ break; ++ case AuWbrCreate_MFSRR: ++ case AuWbrCreate_PMFSRR: ++ e = au_wbr_mfs_wmark(&args[0], str, create); ++ if (unlikely(e)) { ++ err = e; ++ break; ++ } ++ /*FALLTHROUGH*/ ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_PMFS: ++ create->mfs_second = AUFS_MFS_DEF_SEC; ++ break; ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFSV: ++ e = au_wbr_mfs_sec(&args[0], str, create); ++ if (unlikely(e)) ++ err = e; ++ break; ++ } ++ ++ return err; ++} ++ ++const char *au_optstr_wbr_create(int wbr_create) ++{ ++ return au_parser_pattern(wbr_create, au_wbr_create_policy); ++} ++ ++static match_table_t au_wbr_copyup_policy = { ++ {AuWbrCopyup_TDP, "tdp"}, ++ {AuWbrCopyup_TDP, "top-down-parent"}, ++ {AuWbrCopyup_BUP, "bup"}, ++ {AuWbrCopyup_BUP, "bottom-up-parent"}, ++ {AuWbrCopyup_BU, "bu"}, ++ {AuWbrCopyup_BU, "bottom-up"}, ++ {-1, NULL} ++}; ++ ++static int noinline_for_stack au_wbr_copyup_val(char *str) ++{ ++ substring_t args[MAX_OPT_ARGS]; ++ ++ return match_token(str, au_wbr_copyup_policy, args); ++} ++ ++const char *au_optstr_wbr_copyup(int wbr_copyup) ++{ ++ return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; ++ ++static void dump_opts(struct au_opts *opts) ++{ ++#ifdef CONFIG_AUFS_DEBUG ++ /* reduce stack space */ ++ union { ++ struct au_opt_add *add; ++ struct au_opt_del *del; ++ struct au_opt_mod *mod; ++ struct au_opt_xino *xino; ++ struct au_opt_xino_itrunc *xino_itrunc; ++ struct au_opt_wbr_create *create; ++ } u; ++ struct au_opt *opt; ++ ++ opt = opts->opt; ++ while (opt->type != Opt_tail) { ++ switch (opt->type) { ++ case Opt_add: ++ u.add = &opt->add; ++ AuDbg("add {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_del: ++ case Opt_idel: ++ u.del = &opt->del; ++ AuDbg("del {%s, %p}\n", ++ u.del->pathname, u.del->h_path.dentry); ++ break; ++ case Opt_mod: ++ case Opt_imod: ++ u.mod = &opt->mod; ++ AuDbg("mod {%s, 0x%x, %p}\n", ++ u.mod->path, u.mod->perm, u.mod->h_root); ++ break; ++ case Opt_append: ++ u.add = &opt->add; ++ AuDbg("append {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_prepend: ++ u.add = &opt->add; ++ AuDbg("prepend {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_dirwh: ++ AuDbg("dirwh %d\n", opt->dirwh); ++ break; ++ case Opt_rdcache: ++ AuDbg("rdcache %d\n", opt->rdcache); ++ break; ++ case Opt_rdblk: ++ AuDbg("rdblk %u\n", opt->rdblk); ++ break; ++ case Opt_rdblk_def: ++ AuDbg("rdblk_def\n"); ++ break; ++ case Opt_rdhash: ++ AuDbg("rdhash %u\n", opt->rdhash); ++ break; ++ case Opt_rdhash_def: ++ AuDbg("rdhash_def\n"); ++ break; ++ case Opt_xino: ++ u.xino = &opt->xino; ++ AuDbg("xino {%s %pD}\n", u.xino->path, u.xino->file); ++ break; ++ case Opt_trunc_xino: ++ AuLabel(trunc_xino); ++ break; ++ case Opt_notrunc_xino: ++ AuLabel(notrunc_xino); ++ break; ++ case Opt_trunc_xino_path: ++ case Opt_itrunc_xino: ++ u.xino_itrunc = &opt->xino_itrunc; ++ AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); ++ break; ++ case Opt_noxino: ++ AuLabel(noxino); ++ break; ++ case Opt_trunc_xib: ++ AuLabel(trunc_xib); ++ break; ++ case Opt_notrunc_xib: ++ AuLabel(notrunc_xib); ++ break; ++ case Opt_shwh: ++ AuLabel(shwh); ++ break; ++ case Opt_noshwh: ++ AuLabel(noshwh); ++ break; ++ case Opt_dirperm1: ++ AuLabel(dirperm1); ++ break; ++ case Opt_nodirperm1: ++ AuLabel(nodirperm1); ++ break; ++ case Opt_plink: ++ AuLabel(plink); ++ break; ++ case Opt_noplink: ++ AuLabel(noplink); ++ break; ++ case Opt_list_plink: ++ AuLabel(list_plink); ++ break; ++ case Opt_udba: ++ AuDbg("udba %d, %s\n", ++ opt->udba, au_optstr_udba(opt->udba)); ++ break; ++ case Opt_dio: ++ AuLabel(dio); ++ break; ++ case Opt_nodio: ++ AuLabel(nodio); ++ break; ++ case Opt_diropq_a: ++ AuLabel(diropq_a); ++ break; ++ case Opt_diropq_w: ++ AuLabel(diropq_w); ++ break; ++ case Opt_warn_perm: ++ AuLabel(warn_perm); ++ break; ++ case Opt_nowarn_perm: ++ AuLabel(nowarn_perm); ++ break; ++ case Opt_verbose: ++ AuLabel(verbose); ++ break; ++ case Opt_noverbose: ++ AuLabel(noverbose); ++ break; ++ case Opt_sum: ++ AuLabel(sum); ++ break; ++ case Opt_nosum: ++ AuLabel(nosum); ++ break; ++ case Opt_wsum: ++ AuLabel(wsum); ++ break; ++ case Opt_wbr_create: ++ u.create = &opt->wbr_create; ++ AuDbg("create %d, %s\n", u.create->wbr_create, ++ au_optstr_wbr_create(u.create->wbr_create)); ++ switch (u.create->wbr_create) { ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFSV: ++ AuDbg("%d sec\n", u.create->mfs_second); ++ break; ++ case AuWbrCreate_MFSRR: ++ AuDbg("%llu watermark\n", ++ u.create->mfsrr_watermark); ++ break; ++ case AuWbrCreate_MFSRRV: ++ case AuWbrCreate_PMFSRRV: ++ AuDbg("%llu watermark, %d sec\n", ++ u.create->mfsrr_watermark, ++ u.create->mfs_second); ++ break; ++ } ++ break; ++ case Opt_wbr_copyup: ++ AuDbg("copyup %d, %s\n", opt->wbr_copyup, ++ au_optstr_wbr_copyup(opt->wbr_copyup)); ++ break; ++ case Opt_fhsm_sec: ++ AuDbg("fhsm_sec %u\n", opt->fhsm_second); ++ break; ++ case Opt_acl: ++ AuLabel(acl); ++ break; ++ case Opt_noacl: ++ AuLabel(noacl); ++ break; ++ default: ++ BUG(); ++ } ++ opt++; ++ } ++#endif ++} ++ ++void au_opts_free(struct au_opts *opts) ++{ ++ struct au_opt *opt; ++ ++ opt = opts->opt; ++ while (opt->type != Opt_tail) { ++ switch (opt->type) { ++ case Opt_add: ++ case Opt_append: ++ case Opt_prepend: ++ path_put(&opt->add.path); ++ break; ++ case Opt_del: ++ case Opt_idel: ++ path_put(&opt->del.h_path); ++ break; ++ case Opt_mod: ++ case Opt_imod: ++ dput(opt->mod.h_root); ++ break; ++ case Opt_xino: ++ fput(opt->xino.file); ++ break; ++ } ++ opt++; ++ } ++} ++ ++static int opt_add(struct au_opt *opt, char *opt_str, unsigned long sb_flags, ++ aufs_bindex_t bindex) ++{ ++ int err; ++ struct au_opt_add *add = &opt->add; ++ char *p; ++ ++ add->bindex = bindex; ++ add->perm = AuBrPerm_RO; ++ add->pathname = opt_str; ++ p = strchr(opt_str, '='); ++ if (p) { ++ *p++ = 0; ++ if (*p) ++ add->perm = br_perm_val(p); ++ } ++ ++ err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); ++ if (!err) { ++ if (!p) { ++ add->perm = AuBrPerm_RO; ++ if (au_test_fs_rr(add->path.dentry->d_sb)) ++ add->perm = AuBrPerm_RR; ++ else if (!bindex && !(sb_flags & MS_RDONLY)) ++ add->perm = AuBrPerm_RW; ++ } ++ opt->type = Opt_add; ++ goto out; ++ } ++ pr_err("lookup failed %s (%d)\n", add->pathname, err); ++ err = -EINVAL; ++ ++out: ++ return err; ++} ++ ++static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) ++{ ++ int err; ++ ++ del->pathname = args[0].from; ++ AuDbg("del path %s\n", del->pathname); ++ ++ err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); ++ if (unlikely(err)) ++ pr_err("lookup failed %s (%d)\n", del->pathname, err); ++ ++ return err; ++} ++ ++#if 0 /* reserved for future use */ ++static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_opt_del *del, substring_t args[]) ++{ ++ int err; ++ struct dentry *root; ++ ++ err = -EINVAL; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (bindex < 0 || au_sbend(sb) < bindex) { ++ pr_err("out of bounds, %d\n", bindex); ++ goto out; ++ } ++ ++ err = 0; ++ del->h_path.dentry = dget(au_h_dptr(root, bindex)); ++ del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); ++ ++out: ++ aufs_read_unlock(root, !AuLock_IR); ++ return err; ++} ++#endif ++ ++static int noinline_for_stack ++au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) ++{ ++ int err; ++ struct path path; ++ char *p; ++ ++ err = -EINVAL; ++ mod->path = args[0].from; ++ p = strchr(mod->path, '='); ++ if (unlikely(!p)) { ++ pr_err("no permssion %s\n", args[0].from); ++ goto out; ++ } ++ ++ *p++ = 0; ++ err = vfsub_kern_path(mod->path, lkup_dirflags, &path); ++ if (unlikely(err)) { ++ pr_err("lookup failed %s (%d)\n", mod->path, err); ++ goto out; ++ } ++ ++ mod->perm = br_perm_val(p); ++ AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); ++ mod->h_root = dget(path.dentry); ++ path_put(&path); ++ ++out: ++ return err; ++} ++ ++#if 0 /* reserved for future use */ ++static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_opt_mod *mod, substring_t args[]) ++{ ++ int err; ++ struct dentry *root; ++ ++ err = -EINVAL; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (bindex < 0 || au_sbend(sb) < bindex) { ++ pr_err("out of bounds, %d\n", bindex); ++ goto out; ++ } ++ ++ err = 0; ++ mod->perm = br_perm_val(args[1].from); ++ AuDbg("mod path %s, perm 0x%x, %s\n", ++ mod->path, mod->perm, args[1].from); ++ mod->h_root = dget(au_h_dptr(root, bindex)); ++ ++out: ++ aufs_read_unlock(root, !AuLock_IR); ++ return err; ++} ++#endif ++ ++static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino, ++ substring_t args[]) ++{ ++ int err; ++ struct file *file; ++ ++ file = au_xino_create(sb, args[0].from, /*silent*/0); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ ++ err = -EINVAL; ++ if (unlikely(file->f_path.dentry->d_sb == sb)) { ++ fput(file); ++ pr_err("%s must be outside\n", args[0].from); ++ goto out; ++ } ++ ++ err = 0; ++ xino->file = file; ++ xino->path = args[0].from; ++ ++out: ++ return err; ++} ++ ++static int noinline_for_stack ++au_opts_parse_xino_itrunc_path(struct super_block *sb, ++ struct au_opt_xino_itrunc *xino_itrunc, ++ substring_t args[]) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct path path; ++ struct dentry *root; ++ ++ err = vfsub_kern_path(args[0].from, lkup_dirflags, &path); ++ if (unlikely(err)) { ++ pr_err("lookup failed %s (%d)\n", args[0].from, err); ++ goto out; ++ } ++ ++ xino_itrunc->bindex = -1; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ if (au_h_dptr(root, bindex) == path.dentry) { ++ xino_itrunc->bindex = bindex; ++ break; ++ } ++ } ++ aufs_read_unlock(root, !AuLock_IR); ++ path_put(&path); ++ ++ if (unlikely(xino_itrunc->bindex < 0)) { ++ pr_err("no such branch %s\n", args[0].from); ++ err = -EINVAL; ++ } ++ ++out: ++ return err; ++} ++ ++/* called without aufs lock */ ++int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) ++{ ++ int err, n, token; ++ aufs_bindex_t bindex; ++ unsigned char skipped; ++ struct dentry *root; ++ struct au_opt *opt, *opt_tail; ++ char *opt_str; ++ /* reduce the stack space */ ++ union { ++ struct au_opt_xino_itrunc *xino_itrunc; ++ struct au_opt_wbr_create *create; ++ } u; ++ struct { ++ substring_t args[MAX_OPT_ARGS]; ++ } *a; ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ root = sb->s_root; ++ err = 0; ++ bindex = 0; ++ opt = opts->opt; ++ opt_tail = opt + opts->max_opt - 1; ++ opt->type = Opt_tail; ++ while (!err && (opt_str = strsep(&str, ",")) && *opt_str) { ++ err = -EINVAL; ++ skipped = 0; ++ token = match_token(opt_str, options, a->args); ++ switch (token) { ++ case Opt_br: ++ err = 0; ++ while (!err && (opt_str = strsep(&a->args[0].from, ":")) ++ && *opt_str) { ++ err = opt_add(opt, opt_str, opts->sb_flags, ++ bindex++); ++ if (unlikely(!err && ++opt > opt_tail)) { ++ err = -E2BIG; ++ break; ++ } ++ opt->type = Opt_tail; ++ skipped = 1; ++ } ++ break; ++ case Opt_add: ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ bindex = n; ++ err = opt_add(opt, a->args[1].from, opts->sb_flags, ++ bindex); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_append: ++ err = opt_add(opt, a->args[0].from, opts->sb_flags, ++ /*dummy bindex*/1); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_prepend: ++ err = opt_add(opt, a->args[0].from, opts->sb_flags, ++ /*bindex*/0); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_del: ++ err = au_opts_parse_del(&opt->del, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#if 0 /* reserved for future use */ ++ case Opt_idel: ++ del->pathname = "(indexed)"; ++ if (unlikely(match_int(&args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ err = au_opts_parse_idel(sb, n, &opt->del, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#endif ++ case Opt_mod: ++ err = au_opts_parse_mod(&opt->mod, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#ifdef IMOD /* reserved for future use */ ++ case Opt_imod: ++ u.mod->path = "(indexed)"; ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ err = au_opts_parse_imod(sb, n, &opt->mod, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#endif ++ case Opt_xino: ++ err = au_opts_parse_xino(sb, &opt->xino, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++ ++ case Opt_trunc_xino_path: ++ err = au_opts_parse_xino_itrunc_path ++ (sb, &opt->xino_itrunc, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++ ++ case Opt_itrunc_xino: ++ u.xino_itrunc = &opt->xino_itrunc; ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ u.xino_itrunc->bindex = n; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (n < 0 || au_sbend(sb) < n) { ++ pr_err("out of bounds, %d\n", n); ++ aufs_read_unlock(root, !AuLock_IR); ++ break; ++ } ++ aufs_read_unlock(root, !AuLock_IR); ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_dirwh: ++ if (unlikely(match_int(&a->args[0], &opt->dirwh))) ++ break; ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_rdcache: ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ if (unlikely(n > AUFS_RDCACHE_MAX)) { ++ pr_err("rdcache must be smaller than %d\n", ++ AUFS_RDCACHE_MAX); ++ break; ++ } ++ opt->rdcache = n; ++ err = 0; ++ opt->type = token; ++ break; ++ case Opt_rdblk: ++ if (unlikely(match_int(&a->args[0], &n) ++ || n < 0 ++ || n > KMALLOC_MAX_SIZE)) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ if (unlikely(n && n < NAME_MAX)) { ++ pr_err("rdblk must be larger than %d\n", ++ NAME_MAX); ++ break; ++ } ++ opt->rdblk = n; ++ err = 0; ++ opt->type = token; ++ break; ++ case Opt_rdhash: ++ if (unlikely(match_int(&a->args[0], &n) ++ || n < 0 ++ || n * sizeof(struct hlist_head) ++ > KMALLOC_MAX_SIZE)) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ opt->rdhash = n; ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_trunc_xino: ++ case Opt_notrunc_xino: ++ case Opt_noxino: ++ case Opt_trunc_xib: ++ case Opt_notrunc_xib: ++ case Opt_shwh: ++ case Opt_noshwh: ++ case Opt_dirperm1: ++ case Opt_nodirperm1: ++ case Opt_plink: ++ case Opt_noplink: ++ case Opt_list_plink: ++ case Opt_dio: ++ case Opt_nodio: ++ case Opt_diropq_a: ++ case Opt_diropq_w: ++ case Opt_warn_perm: ++ case Opt_nowarn_perm: ++ case Opt_verbose: ++ case Opt_noverbose: ++ case Opt_sum: ++ case Opt_nosum: ++ case Opt_wsum: ++ case Opt_rdblk_def: ++ case Opt_rdhash_def: ++ case Opt_acl: ++ case Opt_noacl: ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_udba: ++ opt->udba = udba_val(a->args[0].from); ++ if (opt->udba >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ pr_err("wrong value, %s\n", opt_str); ++ break; ++ ++ case Opt_wbr_create: ++ u.create = &opt->wbr_create; ++ u.create->wbr_create ++ = au_wbr_create_val(a->args[0].from, u.create); ++ if (u.create->wbr_create >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ pr_err("wrong value, %s\n", opt_str); ++ break; ++ case Opt_wbr_copyup: ++ opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from); ++ if (opt->wbr_copyup >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ pr_err("wrong value, %s\n", opt_str); ++ break; ++ ++ case Opt_fhsm_sec: ++ if (unlikely(match_int(&a->args[0], &n) ++ || n < 0)) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ if (sysaufs_brs) { ++ opt->fhsm_second = n; ++ opt->type = token; ++ } else ++ pr_warn("ignored %s\n", opt_str); ++ err = 0; ++ break; ++ ++ case Opt_ignore: ++ pr_warn("ignored %s\n", opt_str); ++ /*FALLTHROUGH*/ ++ case Opt_ignore_silent: ++ skipped = 1; ++ err = 0; ++ break; ++ case Opt_err: ++ pr_err("unknown option %s\n", opt_str); ++ break; ++ } ++ ++ if (!err && !skipped) { ++ if (unlikely(++opt > opt_tail)) { ++ err = -E2BIG; ++ opt--; ++ opt->type = Opt_tail; ++ break; ++ } ++ opt->type = Opt_tail; ++ } ++ } ++ ++ kfree(a); ++ dump_opts(opts); ++ if (unlikely(err)) ++ au_opts_free(opts); ++ ++out: ++ return err; ++} ++ ++static int au_opt_wbr_create(struct super_block *sb, ++ struct au_opt_wbr_create *create) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ err = 1; /* handled */ ++ sbinfo = au_sbi(sb); ++ if (sbinfo->si_wbr_create_ops->fin) { ++ err = sbinfo->si_wbr_create_ops->fin(sb); ++ if (!err) ++ err = 1; ++ } ++ ++ sbinfo->si_wbr_create = create->wbr_create; ++ sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create; ++ switch (create->wbr_create) { ++ case AuWbrCreate_MFSRRV: ++ case AuWbrCreate_MFSRR: ++ case AuWbrCreate_PMFSRR: ++ case AuWbrCreate_PMFSRRV: ++ sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; ++ /*FALLTHROUGH*/ ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFS: ++ case AuWbrCreate_PMFSV: ++ sbinfo->si_wbr_mfs.mfs_expire ++ = msecs_to_jiffies(create->mfs_second * MSEC_PER_SEC); ++ break; ++ } ++ ++ if (sbinfo->si_wbr_create_ops->init) ++ sbinfo->si_wbr_create_ops->init(sb); /* ignore */ ++ ++ return err; ++} ++ ++/* ++ * returns, ++ * plus: processed without an error ++ * zero: unprocessed ++ */ ++static int au_opt_simple(struct super_block *sb, struct au_opt *opt, ++ struct au_opts *opts) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ err = 1; /* handled */ ++ sbinfo = au_sbi(sb); ++ switch (opt->type) { ++ case Opt_udba: ++ sbinfo->si_mntflags &= ~AuOptMask_UDBA; ++ sbinfo->si_mntflags |= opt->udba; ++ opts->given_udba |= opt->udba; ++ break; ++ ++ case Opt_plink: ++ au_opt_set(sbinfo->si_mntflags, PLINK); ++ break; ++ case Opt_noplink: ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_put(sb, /*verbose*/1); ++ au_opt_clr(sbinfo->si_mntflags, PLINK); ++ break; ++ case Opt_list_plink: ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_list(sb); ++ break; ++ ++ case Opt_dio: ++ au_opt_set(sbinfo->si_mntflags, DIO); ++ au_fset_opts(opts->flags, REFRESH_DYAOP); ++ break; ++ case Opt_nodio: ++ au_opt_clr(sbinfo->si_mntflags, DIO); ++ au_fset_opts(opts->flags, REFRESH_DYAOP); ++ break; ++ ++ case Opt_fhsm_sec: ++ au_fhsm_set(sbinfo, opt->fhsm_second); ++ break; ++ ++ case Opt_diropq_a: ++ au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); ++ break; ++ case Opt_diropq_w: ++ au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); ++ break; ++ ++ case Opt_warn_perm: ++ au_opt_set(sbinfo->si_mntflags, WARN_PERM); ++ break; ++ case Opt_nowarn_perm: ++ au_opt_clr(sbinfo->si_mntflags, WARN_PERM); ++ break; ++ ++ case Opt_verbose: ++ au_opt_set(sbinfo->si_mntflags, VERBOSE); ++ break; ++ case Opt_noverbose: ++ au_opt_clr(sbinfo->si_mntflags, VERBOSE); ++ break; ++ ++ case Opt_sum: ++ au_opt_set(sbinfo->si_mntflags, SUM); ++ break; ++ case Opt_wsum: ++ au_opt_clr(sbinfo->si_mntflags, SUM); ++ au_opt_set(sbinfo->si_mntflags, SUM_W); ++ case Opt_nosum: ++ au_opt_clr(sbinfo->si_mntflags, SUM); ++ au_opt_clr(sbinfo->si_mntflags, SUM_W); ++ break; ++ ++ case Opt_wbr_create: ++ err = au_opt_wbr_create(sb, &opt->wbr_create); ++ break; ++ case Opt_wbr_copyup: ++ sbinfo->si_wbr_copyup = opt->wbr_copyup; ++ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; ++ break; ++ ++ case Opt_dirwh: ++ sbinfo->si_dirwh = opt->dirwh; ++ break; ++ ++ case Opt_rdcache: ++ sbinfo->si_rdcache ++ = msecs_to_jiffies(opt->rdcache * MSEC_PER_SEC); ++ break; ++ case Opt_rdblk: ++ sbinfo->si_rdblk = opt->rdblk; ++ break; ++ case Opt_rdblk_def: ++ sbinfo->si_rdblk = AUFS_RDBLK_DEF; ++ break; ++ case Opt_rdhash: ++ sbinfo->si_rdhash = opt->rdhash; ++ break; ++ case Opt_rdhash_def: ++ sbinfo->si_rdhash = AUFS_RDHASH_DEF; ++ break; ++ ++ case Opt_shwh: ++ au_opt_set(sbinfo->si_mntflags, SHWH); ++ break; ++ case Opt_noshwh: ++ au_opt_clr(sbinfo->si_mntflags, SHWH); ++ break; ++ ++ case Opt_dirperm1: ++ au_opt_set(sbinfo->si_mntflags, DIRPERM1); ++ break; ++ case Opt_nodirperm1: ++ au_opt_clr(sbinfo->si_mntflags, DIRPERM1); ++ break; ++ ++ case Opt_trunc_xino: ++ au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); ++ break; ++ case Opt_notrunc_xino: ++ au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); ++ break; ++ ++ case Opt_trunc_xino_path: ++ case Opt_itrunc_xino: ++ err = au_xino_trunc(sb, opt->xino_itrunc.bindex); ++ if (!err) ++ err = 1; ++ break; ++ ++ case Opt_trunc_xib: ++ au_fset_opts(opts->flags, TRUNC_XIB); ++ break; ++ case Opt_notrunc_xib: ++ au_fclr_opts(opts->flags, TRUNC_XIB); ++ break; ++ ++ case Opt_acl: ++ sb->s_flags |= MS_POSIXACL; ++ break; ++ case Opt_noacl: ++ sb->s_flags &= ~MS_POSIXACL; ++ break; ++ ++ default: ++ err = 0; ++ break; ++ } ++ ++ return err; ++} ++ ++/* ++ * returns tri-state. ++ * plus: processed without an error ++ * zero: unprocessed ++ * minus: error ++ */ ++static int au_opt_br(struct super_block *sb, struct au_opt *opt, ++ struct au_opts *opts) ++{ ++ int err, do_refresh; ++ ++ err = 0; ++ switch (opt->type) { ++ case Opt_append: ++ opt->add.bindex = au_sbend(sb) + 1; ++ if (opt->add.bindex < 0) ++ opt->add.bindex = 0; ++ goto add; ++ case Opt_prepend: ++ opt->add.bindex = 0; ++ add: /* indented label */ ++ case Opt_add: ++ err = au_br_add(sb, &opt->add, ++ au_ftest_opts(opts->flags, REMOUNT)); ++ if (!err) { ++ err = 1; ++ au_fset_opts(opts->flags, REFRESH); ++ } ++ break; ++ ++ case Opt_del: ++ case Opt_idel: ++ err = au_br_del(sb, &opt->del, ++ au_ftest_opts(opts->flags, REMOUNT)); ++ if (!err) { ++ err = 1; ++ au_fset_opts(opts->flags, TRUNC_XIB); ++ au_fset_opts(opts->flags, REFRESH); ++ } ++ break; ++ ++ case Opt_mod: ++ case Opt_imod: ++ err = au_br_mod(sb, &opt->mod, ++ au_ftest_opts(opts->flags, REMOUNT), ++ &do_refresh); ++ if (!err) { ++ err = 1; ++ if (do_refresh) ++ au_fset_opts(opts->flags, REFRESH); ++ } ++ break; ++ } ++ ++ return err; ++} ++ ++static int au_opt_xino(struct super_block *sb, struct au_opt *opt, ++ struct au_opt_xino **opt_xino, ++ struct au_opts *opts) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct dentry *root, *parent, *h_root; ++ ++ err = 0; ++ switch (opt->type) { ++ case Opt_xino: ++ err = au_xino_set(sb, &opt->xino, ++ !!au_ftest_opts(opts->flags, REMOUNT)); ++ if (unlikely(err)) ++ break; ++ ++ *opt_xino = &opt->xino; ++ au_xino_brid_set(sb, -1); ++ ++ /* safe d_parent access */ ++ parent = opt->xino.file->f_path.dentry->d_parent; ++ root = sb->s_root; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ h_root = au_h_dptr(root, bindex); ++ if (h_root == parent) { ++ au_xino_brid_set(sb, au_sbr_id(sb, bindex)); ++ break; ++ } ++ } ++ break; ++ ++ case Opt_noxino: ++ au_xino_clr(sb); ++ au_xino_brid_set(sb, -1); ++ *opt_xino = (void *)-1; ++ break; ++ } ++ ++ return err; ++} ++ ++int au_opts_verify(struct super_block *sb, unsigned long sb_flags, ++ unsigned int pending) ++{ ++ int err, fhsm; ++ aufs_bindex_t bindex, bend; ++ unsigned char do_plink, skip, do_free; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ struct dentry *root; ++ struct inode *dir, *h_dir; ++ struct au_sbinfo *sbinfo; ++ struct au_hinode *hdir; ++ ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); ++ ++ if (!(sb_flags & MS_RDONLY)) { ++ if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) ++ pr_warn("first branch should be rw\n"); ++ if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) ++ pr_warn("shwh should be used with ro\n"); ++ } ++ ++ if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) ++ && !au_opt_test(sbinfo->si_mntflags, XINO)) ++ pr_warn("udba=*notify requires xino\n"); ++ ++ if (au_opt_test(sbinfo->si_mntflags, DIRPERM1)) ++ pr_warn("dirperm1 breaks the protection" ++ " by the permission bits on the lower branch\n"); ++ ++ err = 0; ++ fhsm = 0; ++ root = sb->s_root; ++ dir = root->d_inode; ++ do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); ++ bend = au_sbend(sb); ++ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ skip = 0; ++ h_dir = au_h_iptr(dir, bindex); ++ br = au_sbr(sb, bindex); ++ ++ if ((br->br_perm & AuBrAttr_ICEX) ++ && !h_dir->i_op->listxattr) ++ br->br_perm &= ~AuBrAttr_ICEX; ++#if 0 ++ if ((br->br_perm & AuBrAttr_ICEX_SEC) ++ && (au_br_sb(br)->s_flags & MS_NOSEC)) ++ br->br_perm &= ~AuBrAttr_ICEX_SEC; ++#endif ++ ++ do_free = 0; ++ wbr = br->br_wbr; ++ if (wbr) ++ wbr_wh_read_lock(wbr); ++ ++ if (!au_br_writable(br->br_perm)) { ++ do_free = !!wbr; ++ skip = (!wbr ++ || (!wbr->wbr_whbase ++ && !wbr->wbr_plink ++ && !wbr->wbr_orph)); ++ } else if (!au_br_wh_linkable(br->br_perm)) { ++ /* skip = (!br->br_whbase && !br->br_orph); */ ++ skip = (!wbr || !wbr->wbr_whbase); ++ if (skip && wbr) { ++ if (do_plink) ++ skip = !!wbr->wbr_plink; ++ else ++ skip = !wbr->wbr_plink; ++ } ++ } else { ++ /* skip = (br->br_whbase && br->br_ohph); */ ++ skip = (wbr && wbr->wbr_whbase); ++ if (skip) { ++ if (do_plink) ++ skip = !!wbr->wbr_plink; ++ else ++ skip = !wbr->wbr_plink; ++ } ++ } ++ if (wbr) ++ wbr_wh_read_unlock(wbr); ++ ++ if (au_br_fhsm(br->br_perm)) { ++ fhsm++; ++ AuDebugOn(!br->br_fhsm); ++ } ++ ++ if (skip) ++ continue; ++ ++ hdir = au_hi(dir, bindex); ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ if (wbr) ++ wbr_wh_write_lock(wbr); ++ err = au_wh_init(br, sb); ++ if (wbr) ++ wbr_wh_write_unlock(wbr); ++ au_hn_imtx_unlock(hdir); ++ ++ if (!err && do_free) { ++ kfree(wbr); ++ br->br_wbr = NULL; ++ } ++ } ++ ++ if (fhsm >= 2) { ++ au_fset_si(sbinfo, FHSM); ++ for (bindex = bend; bindex >= 0; bindex--) { ++ br = au_sbr(sb, bindex); ++ if (au_br_fhsm(br->br_perm)) { ++ au_fhsm_set_bottom(sb, bindex); ++ break; ++ } ++ } ++ } else { ++ au_fclr_si(sbinfo, FHSM); ++ au_fhsm_set_bottom(sb, -1); ++ } ++ ++ return err; ++} ++ ++int au_opts_mount(struct super_block *sb, struct au_opts *opts) ++{ ++ int err; ++ unsigned int tmp; ++ aufs_bindex_t bindex, bend; ++ struct au_opt *opt; ++ struct au_opt_xino *opt_xino, xino; ++ struct au_sbinfo *sbinfo; ++ struct au_branch *br; ++ struct inode *dir; ++ ++ SiMustWriteLock(sb); ++ ++ err = 0; ++ opt_xino = NULL; ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) ++ err = au_opt_simple(sb, opt++, opts); ++ if (err > 0) ++ err = 0; ++ else if (unlikely(err < 0)) ++ goto out; ++ ++ /* disable xino and udba temporary */ ++ sbinfo = au_sbi(sb); ++ tmp = sbinfo->si_mntflags; ++ au_opt_clr(sbinfo->si_mntflags, XINO); ++ au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); ++ ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) ++ err = au_opt_br(sb, opt++, opts); ++ if (err > 0) ++ err = 0; ++ else if (unlikely(err < 0)) ++ goto out; ++ ++ bend = au_sbend(sb); ++ if (unlikely(bend < 0)) { ++ err = -EINVAL; ++ pr_err("no branches\n"); ++ goto out; ++ } ++ ++ if (au_opt_test(tmp, XINO)) ++ au_opt_set(sbinfo->si_mntflags, XINO); ++ opt = opts->opt; ++ while (!err && opt->type != Opt_tail) ++ err = au_opt_xino(sb, opt++, &opt_xino, opts); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_opts_verify(sb, sb->s_flags, tmp); ++ if (unlikely(err)) ++ goto out; ++ ++ /* restore xino */ ++ if (au_opt_test(tmp, XINO) && !opt_xino) { ++ xino.file = au_xino_def(sb); ++ err = PTR_ERR(xino.file); ++ if (IS_ERR(xino.file)) ++ goto out; ++ ++ err = au_xino_set(sb, &xino, /*remount*/0); ++ fput(xino.file); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ /* restore udba */ ++ tmp &= AuOptMask_UDBA; ++ sbinfo->si_mntflags &= ~AuOptMask_UDBA; ++ sbinfo->si_mntflags |= tmp; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ err = au_hnotify_reset_br(tmp, br, br->br_perm); ++ if (unlikely(err)) ++ AuIOErr("hnotify failed on br %d, %d, ignored\n", ++ bindex, err); ++ /* go on even if err */ ++ } ++ if (au_opt_test(tmp, UDBA_HNOTIFY)) { ++ dir = sb->s_root->d_inode; ++ au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); ++ } ++ ++out: ++ return err; ++} ++ ++int au_opts_remount(struct super_block *sb, struct au_opts *opts) ++{ ++ int err, rerr; ++ struct inode *dir; ++ struct au_opt_xino *opt_xino; ++ struct au_opt *opt; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ dir = sb->s_root->d_inode; ++ sbinfo = au_sbi(sb); ++ err = 0; ++ opt_xino = NULL; ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) { ++ err = au_opt_simple(sb, opt, opts); ++ if (!err) ++ err = au_opt_br(sb, opt, opts); ++ if (!err) ++ err = au_opt_xino(sb, opt, &opt_xino, opts); ++ opt++; ++ } ++ if (err > 0) ++ err = 0; ++ AuTraceErr(err); ++ /* go on even err */ ++ ++ rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); ++ if (unlikely(rerr && !err)) ++ err = rerr; ++ ++ if (au_ftest_opts(opts->flags, TRUNC_XIB)) { ++ rerr = au_xib_trunc(sb); ++ if (unlikely(rerr && !err)) ++ err = rerr; ++ } ++ ++ /* will be handled by the caller */ ++ if (!au_ftest_opts(opts->flags, REFRESH) ++ && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO))) ++ au_fset_opts(opts->flags, REFRESH); ++ ++ AuDbg("status 0x%x\n", opts->flags); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++unsigned int au_opt_udba(struct super_block *sb) ++{ ++ return au_mntflags(sb) & AuOptMask_UDBA; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/opts.h linux-4.0.4/fs/aufs/opts.h +--- linux-4.0.4.orig/fs/aufs/opts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/opts.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,197 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * mount options/flags ++ */ ++ ++#ifndef __AUFS_OPTS_H__ ++#define __AUFS_OPTS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++struct file; ++struct super_block; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* mount flags */ ++#define AuOpt_XINO 1 /* external inode number bitmap ++ and translation table */ ++#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */ ++#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */ ++#define AuOpt_UDBA_REVAL (1 << 3) ++#define AuOpt_UDBA_HNOTIFY (1 << 4) ++#define AuOpt_SHWH (1 << 5) /* show whiteout */ ++#define AuOpt_PLINK (1 << 6) /* pseudo-link */ ++#define AuOpt_DIRPERM1 (1 << 7) /* ignore the lower dir's perm ++ bits */ ++#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */ ++#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */ ++#define AuOpt_SUM_W (1 << 11) /* unimplemented */ ++#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */ ++#define AuOpt_VERBOSE (1 << 13) /* busy inode when del-branch */ ++#define AuOpt_DIO (1 << 14) /* direct io */ ++ ++#ifndef CONFIG_AUFS_HNOTIFY ++#undef AuOpt_UDBA_HNOTIFY ++#define AuOpt_UDBA_HNOTIFY 0 ++#endif ++#ifndef CONFIG_AUFS_SHWH ++#undef AuOpt_SHWH ++#define AuOpt_SHWH 0 ++#endif ++ ++#define AuOpt_Def (AuOpt_XINO \ ++ | AuOpt_UDBA_REVAL \ ++ | AuOpt_PLINK \ ++ /* | AuOpt_DIRPERM1 */ \ ++ | AuOpt_WARN_PERM) ++#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ ++ | AuOpt_UDBA_REVAL \ ++ | AuOpt_UDBA_HNOTIFY) ++ ++#define au_opt_test(flags, name) (flags & AuOpt_##name) ++#define au_opt_set(flags, name) do { \ ++ BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \ ++ ((flags) |= AuOpt_##name); \ ++} while (0) ++#define au_opt_set_udba(flags, name) do { \ ++ (flags) &= ~AuOptMask_UDBA; \ ++ ((flags) |= AuOpt_##name); \ ++} while (0) ++#define au_opt_clr(flags, name) do { \ ++ ((flags) &= ~AuOpt_##name); \ ++} while (0) ++ ++static inline unsigned int au_opts_plink(unsigned int mntflags) ++{ ++#ifdef CONFIG_PROC_FS ++ return mntflags; ++#else ++ return mntflags & ~AuOpt_PLINK; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies to select one among multiple writable branches */ ++enum { ++ AuWbrCreate_TDP, /* top down parent */ ++ AuWbrCreate_RR, /* round robin */ ++ AuWbrCreate_MFS, /* most free space */ ++ AuWbrCreate_MFSV, /* mfs with seconds */ ++ AuWbrCreate_MFSRR, /* mfs then rr */ ++ AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ ++ AuWbrCreate_PMFS, /* parent and mfs */ ++ AuWbrCreate_PMFSV, /* parent and mfs with seconds */ ++ AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */ ++ AuWbrCreate_PMFSRRV, /* plus seconds */ ++ ++ AuWbrCreate_Def = AuWbrCreate_TDP ++}; ++ ++enum { ++ AuWbrCopyup_TDP, /* top down parent */ ++ AuWbrCopyup_BUP, /* bottom up parent */ ++ AuWbrCopyup_BU, /* bottom up */ ++ ++ AuWbrCopyup_Def = AuWbrCopyup_TDP ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_opt_add { ++ aufs_bindex_t bindex; ++ char *pathname; ++ int perm; ++ struct path path; ++}; ++ ++struct au_opt_del { ++ char *pathname; ++ struct path h_path; ++}; ++ ++struct au_opt_mod { ++ char *path; ++ int perm; ++ struct dentry *h_root; ++}; ++ ++struct au_opt_xino { ++ char *path; ++ struct file *file; ++}; ++ ++struct au_opt_xino_itrunc { ++ aufs_bindex_t bindex; ++}; ++ ++struct au_opt_wbr_create { ++ int wbr_create; ++ int mfs_second; ++ unsigned long long mfsrr_watermark; ++}; ++ ++struct au_opt { ++ int type; ++ union { ++ struct au_opt_xino xino; ++ struct au_opt_xino_itrunc xino_itrunc; ++ struct au_opt_add add; ++ struct au_opt_del del; ++ struct au_opt_mod mod; ++ int dirwh; ++ int rdcache; ++ unsigned int rdblk; ++ unsigned int rdhash; ++ int udba; ++ struct au_opt_wbr_create wbr_create; ++ int wbr_copyup; ++ unsigned int fhsm_second; ++ }; ++}; ++ ++/* opts flags */ ++#define AuOpts_REMOUNT 1 ++#define AuOpts_REFRESH (1 << 1) ++#define AuOpts_TRUNC_XIB (1 << 2) ++#define AuOpts_REFRESH_DYAOP (1 << 3) ++#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) ++#define au_fset_opts(flags, name) \ ++ do { (flags) |= AuOpts_##name; } while (0) ++#define au_fclr_opts(flags, name) \ ++ do { (flags) &= ~AuOpts_##name; } while (0) ++ ++struct au_opts { ++ struct au_opt *opt; ++ int max_opt; ++ ++ unsigned int given_udba; ++ unsigned int flags; ++ unsigned long sb_flags; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* opts.c */ ++void au_optstr_br_perm(au_br_perm_str_t *str, int perm); ++const char *au_optstr_udba(int udba); ++const char *au_optstr_wbr_copyup(int wbr_copyup); ++const char *au_optstr_wbr_create(int wbr_create); ++ ++void au_opts_free(struct au_opts *opts); ++int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts); ++int au_opts_verify(struct super_block *sb, unsigned long sb_flags, ++ unsigned int pending); ++int au_opts_mount(struct super_block *sb, struct au_opts *opts); ++int au_opts_remount(struct super_block *sb, struct au_opts *opts); ++ ++unsigned int au_opt_udba(struct super_block *sb); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_OPTS_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/plink.c linux-4.0.4/fs/aufs/plink.c +--- linux-4.0.4.orig/fs/aufs/plink.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/plink.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,519 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * pseudo-link ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * the pseudo-link maintenance mode. ++ * during a user process maintains the pseudo-links, ++ * prohibit adding a new plink and branch manipulation. ++ * ++ * Flags ++ * NOPLM: ++ * For entry functions which will handle plink, and i_mutex is already held ++ * in VFS. ++ * They cannot wait and should return an error at once. ++ * Callers has to check the error. ++ * NOPLMW: ++ * For entry functions which will handle plink, but i_mutex is not held ++ * in VFS. ++ * They can wait the plink maintenance mode to finish. ++ * ++ * They behave like F_SETLK and F_SETLKW. ++ * If the caller never handle plink, then both flags are unnecessary. ++ */ ++ ++int au_plink_maint(struct super_block *sb, int flags) ++{ ++ int err; ++ pid_t pid, ppid; ++ struct au_sbinfo *sbi; ++ ++ SiMustAnyLock(sb); ++ ++ err = 0; ++ if (!au_opt_test(au_mntflags(sb), PLINK)) ++ goto out; ++ ++ sbi = au_sbi(sb); ++ pid = sbi->si_plink_maint_pid; ++ if (!pid || pid == current->pid) ++ goto out; ++ ++ /* todo: it highly depends upon /sbin/mount.aufs */ ++ rcu_read_lock(); ++ ppid = task_pid_vnr(rcu_dereference(current->real_parent)); ++ rcu_read_unlock(); ++ if (pid == ppid) ++ goto out; ++ ++ if (au_ftest_lock(flags, NOPLMW)) { ++ /* if there is no i_mutex lock in VFS, we don't need to wait */ ++ /* AuDebugOn(!lockdep_depth(current)); */ ++ while (sbi->si_plink_maint_pid) { ++ si_read_unlock(sb); ++ /* gave up wake_up_bit() */ ++ wait_event(sbi->si_plink_wq, !sbi->si_plink_maint_pid); ++ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&sbi->si_nowait); ++ si_noflush_read_lock(sb); ++ } ++ } else if (au_ftest_lock(flags, NOPLM)) { ++ AuDbg("ppid %d, pid %d\n", ppid, pid); ++ err = -EAGAIN; ++ } ++ ++out: ++ return err; ++} ++ ++void au_plink_maint_leave(struct au_sbinfo *sbinfo) ++{ ++ spin_lock(&sbinfo->si_plink_maint_lock); ++ sbinfo->si_plink_maint_pid = 0; ++ spin_unlock(&sbinfo->si_plink_maint_lock); ++ wake_up_all(&sbinfo->si_plink_wq); ++} ++ ++int au_plink_maint_enter(struct super_block *sb) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ /* make sure i am the only one in this fs */ ++ si_write_lock(sb, AuLock_FLUSH); ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ spin_lock(&sbinfo->si_plink_maint_lock); ++ if (!sbinfo->si_plink_maint_pid) ++ sbinfo->si_plink_maint_pid = current->pid; ++ else ++ err = -EBUSY; ++ spin_unlock(&sbinfo->si_plink_maint_lock); ++ } ++ si_write_unlock(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_DEBUG ++void au_plink_list(struct super_block *sb) ++{ ++ int i; ++ struct au_sbinfo *sbinfo; ++ struct hlist_head *plink_hlist; ++ struct pseudo_link *plink; ++ ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ rcu_read_lock(); ++ hlist_for_each_entry_rcu(plink, plink_hlist, hlist) ++ AuDbg("%lu\n", plink->inode->i_ino); ++ rcu_read_unlock(); ++ } ++} ++#endif ++ ++/* is the inode pseudo-linked? */ ++int au_plink_test(struct inode *inode) ++{ ++ int found, i; ++ struct au_sbinfo *sbinfo; ++ struct hlist_head *plink_hlist; ++ struct pseudo_link *plink; ++ ++ sbinfo = au_sbi(inode->i_sb); ++ AuRwMustAnyLock(&sbinfo->si_rwsem); ++ AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); ++ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); ++ ++ found = 0; ++ i = au_plink_hash(inode->i_ino); ++ plink_hlist = &sbinfo->si_plink[i].head; ++ rcu_read_lock(); ++ hlist_for_each_entry_rcu(plink, plink_hlist, hlist) ++ if (plink->inode == inode) { ++ found = 1; ++ break; ++ } ++ rcu_read_unlock(); ++ return found; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * generate a name for plink. ++ * the file will be stored under AUFS_WH_PLINKDIR. ++ */ ++/* 20 is max digits length of ulong 64 */ ++#define PLINK_NAME_LEN ((20 + 1) * 2) ++ ++static int plink_name(char *name, int len, struct inode *inode, ++ aufs_bindex_t bindex) ++{ ++ int rlen; ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, bindex); ++ rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); ++ return rlen; ++} ++ ++struct au_do_plink_lkup_args { ++ struct dentry **errp; ++ struct qstr *tgtname; ++ struct dentry *h_parent; ++ struct au_branch *br; ++}; ++ ++static struct dentry *au_do_plink_lkup(struct qstr *tgtname, ++ struct dentry *h_parent, ++ struct au_branch *br) ++{ ++ struct dentry *h_dentry; ++ struct mutex *h_mtx; ++ ++ h_mtx = &h_parent->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ h_dentry = vfsub_lkup_one(tgtname, h_parent); ++ mutex_unlock(h_mtx); ++ return h_dentry; ++} ++ ++static void au_call_do_plink_lkup(void *args) ++{ ++ struct au_do_plink_lkup_args *a = args; ++ *a->errp = au_do_plink_lkup(a->tgtname, a->h_parent, a->br); ++} ++ ++/* lookup the plink-ed @inode under the branch at @bindex */ ++struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) ++{ ++ struct dentry *h_dentry, *h_parent; ++ struct au_branch *br; ++ struct inode *h_dir; ++ int wkq_err; ++ char a[PLINK_NAME_LEN]; ++ struct qstr tgtname = QSTR_INIT(a, 0); ++ ++ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); ++ ++ br = au_sbr(inode->i_sb, bindex); ++ h_parent = br->br_wbr->wbr_plink; ++ h_dir = h_parent->d_inode; ++ tgtname.len = plink_name(a, sizeof(a), inode, bindex); ++ ++ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { ++ struct au_do_plink_lkup_args args = { ++ .errp = &h_dentry, ++ .tgtname = &tgtname, ++ .h_parent = h_parent, ++ .br = br ++ }; ++ ++ wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args); ++ if (unlikely(wkq_err)) ++ h_dentry = ERR_PTR(wkq_err); ++ } else ++ h_dentry = au_do_plink_lkup(&tgtname, h_parent, br); ++ ++ return h_dentry; ++} ++ ++/* create a pseudo-link */ ++static int do_whplink(struct qstr *tgt, struct dentry *h_parent, ++ struct dentry *h_dentry, struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = au_br_mnt(br) ++ }; ++ struct inode *h_dir, *delegated; ++ ++ h_dir = h_parent->d_inode; ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); ++again: ++ h_path.dentry = vfsub_lkup_one(tgt, h_parent); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ err = 0; ++ /* wh.plink dir is not monitored */ ++ /* todo: is it really safe? */ ++ if (h_path.dentry->d_inode ++ && h_path.dentry->d_inode != h_dentry->d_inode) { ++ delegated = NULL; ++ err = vfsub_unlink(h_dir, &h_path, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ dput(h_path.dentry); ++ h_path.dentry = NULL; ++ if (!err) ++ goto again; ++ } ++ if (!err && !h_path.dentry->d_inode) { ++ delegated = NULL; ++ err = vfsub_link(h_dentry, h_dir, &h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ } ++ dput(h_path.dentry); ++ ++out: ++ mutex_unlock(&h_dir->i_mutex); ++ return err; ++} ++ ++struct do_whplink_args { ++ int *errp; ++ struct qstr *tgt; ++ struct dentry *h_parent; ++ struct dentry *h_dentry; ++ struct au_branch *br; ++}; ++ ++static void call_do_whplink(void *args) ++{ ++ struct do_whplink_args *a = args; ++ *a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br); ++} ++ ++static int whplink(struct dentry *h_dentry, struct inode *inode, ++ aufs_bindex_t bindex, struct au_branch *br) ++{ ++ int err, wkq_err; ++ struct au_wbr *wbr; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ char a[PLINK_NAME_LEN]; ++ struct qstr tgtname = QSTR_INIT(a, 0); ++ ++ wbr = au_sbr(inode->i_sb, bindex)->br_wbr; ++ h_parent = wbr->wbr_plink; ++ h_dir = h_parent->d_inode; ++ tgtname.len = plink_name(a, sizeof(a), inode, bindex); ++ ++ /* always superio. */ ++ if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) { ++ struct do_whplink_args args = { ++ .errp = &err, ++ .tgt = &tgtname, ++ .h_parent = h_parent, ++ .h_dentry = h_dentry, ++ .br = br ++ }; ++ wkq_err = au_wkq_wait(call_do_whplink, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } else ++ err = do_whplink(&tgtname, h_parent, h_dentry, br); ++ ++ return err; ++} ++ ++/* free a single plink */ ++static void do_put_plink(struct pseudo_link *plink, int do_del) ++{ ++ if (do_del) ++ hlist_del(&plink->hlist); ++ iput(plink->inode); ++ kfree(plink); ++} ++ ++static void do_put_plink_rcu(struct rcu_head *rcu) ++{ ++ struct pseudo_link *plink; ++ ++ plink = container_of(rcu, struct pseudo_link, rcu); ++ iput(plink->inode); ++ kfree(plink); ++} ++ ++/* ++ * create a new pseudo-link for @h_dentry on @bindex. ++ * the linked inode is held in aufs @inode. ++ */ ++void au_plink_append(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry) ++{ ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ struct hlist_head *plink_hlist; ++ struct pseudo_link *plink, *tmp; ++ struct au_sphlhead *sphl; ++ int found, err, cnt, i; ++ ++ sb = inode->i_sb; ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ found = au_plink_test(inode); ++ if (found) ++ return; ++ ++ i = au_plink_hash(inode->i_ino); ++ sphl = sbinfo->si_plink + i; ++ plink_hlist = &sphl->head; ++ tmp = kmalloc(sizeof(*plink), GFP_NOFS); ++ if (tmp) ++ tmp->inode = au_igrab(inode); ++ else { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ spin_lock(&sphl->spin); ++ hlist_for_each_entry(plink, plink_hlist, hlist) { ++ if (plink->inode == inode) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) ++ hlist_add_head_rcu(&tmp->hlist, plink_hlist); ++ spin_unlock(&sphl->spin); ++ if (!found) { ++ cnt = au_sphl_count(sphl); ++#define msg "unexpectedly unblanced or too many pseudo-links" ++ if (cnt > AUFS_PLINK_WARN) ++ AuWarn1(msg ", %d\n", cnt); ++#undef msg ++ err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); ++ } else { ++ do_put_plink(tmp, 0); ++ return; ++ } ++ ++out: ++ if (unlikely(err)) { ++ pr_warn("err %d, damaged pseudo link.\n", err); ++ if (tmp) { ++ au_sphl_del_rcu(&tmp->hlist, sphl); ++ call_rcu(&tmp->rcu, do_put_plink_rcu); ++ } ++ } ++} ++ ++/* free all plinks */ ++void au_plink_put(struct super_block *sb, int verbose) ++{ ++ int i, warned; ++ struct au_sbinfo *sbinfo; ++ struct hlist_head *plink_hlist; ++ struct hlist_node *tmp; ++ struct pseudo_link *plink; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ /* no spin_lock since sbinfo is write-locked */ ++ warned = 0; ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ if (!warned && verbose && !hlist_empty(plink_hlist)) { ++ pr_warn("pseudo-link is not flushed"); ++ warned = 1; ++ } ++ hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) ++ do_put_plink(plink, 0); ++ INIT_HLIST_HEAD(plink_hlist); ++ } ++} ++ ++void au_plink_clean(struct super_block *sb, int verbose) ++{ ++ struct dentry *root; ++ ++ root = sb->s_root; ++ aufs_write_lock(root); ++ if (au_opt_test(au_mntflags(sb), PLINK)) ++ au_plink_put(sb, verbose); ++ aufs_write_unlock(root); ++} ++ ++static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) ++{ ++ int do_put; ++ aufs_bindex_t bstart, bend, bindex; ++ ++ do_put = 0; ++ bstart = au_ibstart(inode); ++ bend = au_ibend(inode); ++ if (bstart >= 0) { ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ if (!au_h_iptr(inode, bindex) ++ || au_ii_br_id(inode, bindex) != br_id) ++ continue; ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ do_put = 1; ++ break; ++ } ++ if (do_put) ++ for (bindex = bstart; bindex <= bend; bindex++) ++ if (au_h_iptr(inode, bindex)) { ++ do_put = 0; ++ break; ++ } ++ } else ++ do_put = 1; ++ ++ return do_put; ++} ++ ++/* free the plinks on a branch specified by @br_id */ ++void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) ++{ ++ struct au_sbinfo *sbinfo; ++ struct hlist_head *plink_hlist; ++ struct hlist_node *tmp; ++ struct pseudo_link *plink; ++ struct inode *inode; ++ int i, do_put; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ /* no spin_lock since sbinfo is write-locked */ ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) { ++ inode = au_igrab(plink->inode); ++ ii_write_lock_child(inode); ++ do_put = au_plink_do_half_refresh(inode, br_id); ++ if (do_put) ++ do_put_plink(plink, 1); ++ ii_write_unlock(inode); ++ iput(inode); ++ } ++ } ++} +diff -Nur linux-4.0.4.orig/fs/aufs/poll.c linux-4.0.4/fs/aufs/poll.c +--- linux-4.0.4.orig/fs/aufs/poll.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/poll.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * poll operation ++ * There is only one filesystem which implements ->poll operation, currently. ++ */ ++ ++#include "aufs.h" ++ ++unsigned int aufs_poll(struct file *file, poll_table *wait) ++{ ++ unsigned int mask; ++ int err; ++ struct file *h_file; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ /* We should pretend an error happened. */ ++ mask = POLLERR /* | POLLIN | POLLOUT */; ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ /* it is not an error if h_file has no operation */ ++ mask = DEFAULT_POLLMASK; ++ h_file = au_hf_top(file); ++ if (h_file->f_op->poll) ++ mask = h_file->f_op->poll(h_file, wait); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++out: ++ si_read_unlock(sb); ++ AuTraceErr((int)mask); ++ return mask; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/posix_acl.c linux-4.0.4/fs/aufs/posix_acl.c +--- linux-4.0.4.orig/fs/aufs/posix_acl.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/posix_acl.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,86 @@ ++/* ++ * Copyright (C) 2014-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * posix acl operations ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++struct posix_acl *aufs_get_acl(struct inode *inode, int type) ++{ ++ struct posix_acl *acl; ++ int err; ++ aufs_bindex_t bindex; ++ struct inode *h_inode; ++ struct super_block *sb; ++ ++ acl = NULL; ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_read_lock_child(inode); ++ if (!(sb->s_flags & MS_POSIXACL)) ++ goto out; ++ ++ bindex = au_ibstart(inode); ++ h_inode = au_h_iptr(inode, bindex); ++ if (unlikely(!h_inode ++ || ((h_inode->i_mode & S_IFMT) ++ != (inode->i_mode & S_IFMT)))) { ++ err = au_busy_or_stale(); ++ acl = ERR_PTR(err); ++ goto out; ++ } ++ ++ /* always topmost only */ ++ acl = get_acl(h_inode, type); ++ ++out: ++ ii_read_unlock(inode); ++ si_read_unlock(sb); ++ ++ AuTraceErrPtr(acl); ++ return acl; ++} ++ ++int aufs_set_acl(struct inode *inode, struct posix_acl *acl, int type) ++{ ++ int err; ++ ssize_t ssz; ++ struct dentry *dentry; ++ struct au_srxattr arg = { ++ .type = AU_ACL_SET, ++ .u.acl_set = { ++ .acl = acl, ++ .type = type ++ }, ++ }; ++ ++ mutex_lock(&inode->i_mutex); ++ if (inode->i_ino == AUFS_ROOT_INO) ++ dentry = dget(inode->i_sb->s_root); ++ else { ++ dentry = d_find_alias(inode); ++ if (!dentry) ++ dentry = d_find_any_alias(inode); ++ if (!dentry) { ++ pr_warn("cannot handle this inode, " ++ "please report to aufs-users ML\n"); ++ err = -ENOENT; ++ goto out; ++ } ++ } ++ ++ ssz = au_srxattr(dentry, &arg); ++ dput(dentry); ++ err = ssz; ++ if (ssz >= 0) ++ err = 0; ++ ++out: ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/procfs.c linux-4.0.4/fs/aufs/procfs.c +--- linux-4.0.4.orig/fs/aufs/procfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/procfs.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,156 @@ ++/* ++ * Copyright (C) 2010-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * procfs interfaces ++ */ ++ ++#include ++#include "aufs.h" ++ ++static int au_procfs_plm_release(struct inode *inode, struct file *file) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = file->private_data; ++ if (sbinfo) { ++ au_plink_maint_leave(sbinfo); ++ kobject_put(&sbinfo->si_kobj); ++ } ++ ++ return 0; ++} ++ ++static void au_procfs_plm_write_clean(struct file *file) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = file->private_data; ++ if (sbinfo) ++ au_plink_clean(sbinfo->si_sb, /*verbose*/0); ++} ++ ++static int au_procfs_plm_write_si(struct file *file, unsigned long id) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ err = -EBUSY; ++ if (unlikely(file->private_data)) ++ goto out; ++ ++ sb = NULL; ++ /* don't use au_sbilist_lock() here */ ++ spin_lock(&au_sbilist.spin); ++ list_for_each_entry(sbinfo, &au_sbilist.head, si_list) ++ if (id == sysaufs_si_id(sbinfo)) { ++ kobject_get(&sbinfo->si_kobj); ++ sb = sbinfo->si_sb; ++ break; ++ } ++ spin_unlock(&au_sbilist.spin); ++ ++ err = -EINVAL; ++ if (unlikely(!sb)) ++ goto out; ++ ++ err = au_plink_maint_enter(sb); ++ if (!err) ++ /* keep kobject_get() */ ++ file->private_data = sbinfo; ++ else ++ kobject_put(&sbinfo->si_kobj); ++out: ++ return err; ++} ++ ++/* ++ * Accept a valid "si=xxxx" only. ++ * Once it is accepted successfully, accept "clean" too. ++ */ ++static ssize_t au_procfs_plm_write(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ unsigned long id; ++ /* last newline is allowed */ ++ char buf[3 + sizeof(unsigned long) * 2 + 1]; ++ ++ err = -EACCES; ++ if (unlikely(!capable(CAP_SYS_ADMIN))) ++ goto out; ++ ++ err = -EINVAL; ++ if (unlikely(count > sizeof(buf))) ++ goto out; ++ ++ err = copy_from_user(buf, ubuf, count); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ goto out; ++ } ++ buf[count] = 0; ++ ++ err = -EINVAL; ++ if (!strcmp("clean", buf)) { ++ au_procfs_plm_write_clean(file); ++ goto out_success; ++ } else if (unlikely(strncmp("si=", buf, 3))) ++ goto out; ++ ++ err = kstrtoul(buf + 3, 16, &id); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_procfs_plm_write_si(file, id); ++ if (unlikely(err)) ++ goto out; ++ ++out_success: ++ err = count; /* success */ ++out: ++ return err; ++} ++ ++static const struct file_operations au_procfs_plm_fop = { ++ .write = au_procfs_plm_write, ++ .release = au_procfs_plm_release, ++ .owner = THIS_MODULE ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct proc_dir_entry *au_procfs_dir; ++ ++void au_procfs_fin(void) ++{ ++ remove_proc_entry(AUFS_PLINK_MAINT_NAME, au_procfs_dir); ++ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); ++} ++ ++int __init au_procfs_init(void) ++{ ++ int err; ++ struct proc_dir_entry *entry; ++ ++ err = -ENOMEM; ++ au_procfs_dir = proc_mkdir(AUFS_PLINK_MAINT_DIR, NULL); ++ if (unlikely(!au_procfs_dir)) ++ goto out; ++ ++ entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | S_IWUSR, ++ au_procfs_dir, &au_procfs_plm_fop); ++ if (unlikely(!entry)) ++ goto out_dir; ++ ++ err = 0; ++ goto out; /* success */ ++ ++ ++out_dir: ++ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); ++out: ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/rdu.c linux-4.0.4/fs/aufs/rdu.c +--- linux-4.0.4.orig/fs/aufs/rdu.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/rdu.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,375 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * readdir in userspace. ++ */ ++ ++#include ++#include ++#include ++#include "aufs.h" ++ ++/* bits for struct aufs_rdu.flags */ ++#define AuRdu_CALLED 1 ++#define AuRdu_CONT (1 << 1) ++#define AuRdu_FULL (1 << 2) ++#define au_ftest_rdu(flags, name) ((flags) & AuRdu_##name) ++#define au_fset_rdu(flags, name) \ ++ do { (flags) |= AuRdu_##name; } while (0) ++#define au_fclr_rdu(flags, name) \ ++ do { (flags) &= ~AuRdu_##name; } while (0) ++ ++struct au_rdu_arg { ++ struct dir_context ctx; ++ struct aufs_rdu *rdu; ++ union au_rdu_ent_ul ent; ++ unsigned long end; ++ ++ struct super_block *sb; ++ int err; ++}; ++ ++static int au_rdu_fill(struct dir_context *ctx, const char *name, int nlen, ++ loff_t offset, u64 h_ino, unsigned int d_type) ++{ ++ int err, len; ++ struct au_rdu_arg *arg = container_of(ctx, struct au_rdu_arg, ctx); ++ struct aufs_rdu *rdu = arg->rdu; ++ struct au_rdu_ent ent; ++ ++ err = 0; ++ arg->err = 0; ++ au_fset_rdu(rdu->cookie.flags, CALLED); ++ len = au_rdu_len(nlen); ++ if (arg->ent.ul + len < arg->end) { ++ ent.ino = h_ino; ++ ent.bindex = rdu->cookie.bindex; ++ ent.type = d_type; ++ ent.nlen = nlen; ++ if (unlikely(nlen > AUFS_MAX_NAMELEN)) ++ ent.type = DT_UNKNOWN; ++ ++ /* unnecessary to support mmap_sem since this is a dir */ ++ err = -EFAULT; ++ if (copy_to_user(arg->ent.e, &ent, sizeof(ent))) ++ goto out; ++ if (copy_to_user(arg->ent.e->name, name, nlen)) ++ goto out; ++ /* the terminating NULL */ ++ if (__put_user(0, arg->ent.e->name + nlen)) ++ goto out; ++ err = 0; ++ /* AuDbg("%p, %.*s\n", arg->ent.p, nlen, name); */ ++ arg->ent.ul += len; ++ rdu->rent++; ++ } else { ++ err = -EFAULT; ++ au_fset_rdu(rdu->cookie.flags, FULL); ++ rdu->full = 1; ++ rdu->tail = arg->ent; ++ } ++ ++out: ++ /* AuTraceErr(err); */ ++ return err; ++} ++ ++static int au_rdu_do(struct file *h_file, struct au_rdu_arg *arg) ++{ ++ int err; ++ loff_t offset; ++ struct au_rdu_cookie *cookie = &arg->rdu->cookie; ++ ++ /* we don't have to care (FMODE_32BITHASH | FMODE_64BITHASH) for ext4 */ ++ offset = vfsub_llseek(h_file, cookie->h_pos, SEEK_SET); ++ err = offset; ++ if (unlikely(offset != cookie->h_pos)) ++ goto out; ++ ++ err = 0; ++ do { ++ arg->err = 0; ++ au_fclr_rdu(cookie->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_iterate_dir(h_file, &arg->ctx); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err ++ && au_ftest_rdu(cookie->flags, CALLED) ++ && !au_ftest_rdu(cookie->flags, FULL)); ++ cookie->h_pos = h_file->f_pos; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_rdu(struct file *file, struct aufs_rdu *rdu) ++{ ++ int err; ++ aufs_bindex_t bend; ++ struct au_rdu_arg arg = { ++ .ctx = { ++ .actor = au_rdu_fill ++ } ++ }; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct file *h_file; ++ struct au_rdu_cookie *cookie = &rdu->cookie; ++ ++ err = !access_ok(VERIFY_WRITE, rdu->ent.e, rdu->sz); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ rdu->rent = 0; ++ rdu->tail = rdu->ent; ++ rdu->full = 0; ++ arg.rdu = rdu; ++ arg.ent = rdu->ent; ++ arg.end = arg.ent.ul; ++ arg.end += rdu->sz; ++ ++ err = -ENOTDIR; ++ if (unlikely(!file->f_op->iterate)) ++ goto out; ++ ++ err = security_file_permission(file, MAY_READ); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out; ++ ++ dentry = file->f_path.dentry; ++ inode = dentry->d_inode; ++#if 1 ++ mutex_lock(&inode->i_mutex); ++#else ++ err = mutex_lock_killable(&inode->i_mutex); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out; ++#endif ++ ++ arg.sb = inode->i_sb; ++ err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_mtx; ++ err = au_alive_dir(dentry); ++ if (unlikely(err)) ++ goto out_si; ++ /* todo: reval? */ ++ fi_read_lock(file); ++ ++ err = -EAGAIN; ++ if (unlikely(au_ftest_rdu(cookie->flags, CONT) ++ && cookie->generation != au_figen(file))) ++ goto out_unlock; ++ ++ err = 0; ++ if (!rdu->blk) { ++ rdu->blk = au_sbi(arg.sb)->si_rdblk; ++ if (!rdu->blk) ++ rdu->blk = au_dir_size(file, /*dentry*/NULL); ++ } ++ bend = au_fbstart(file); ++ if (cookie->bindex < bend) ++ cookie->bindex = bend; ++ bend = au_fbend_dir(file); ++ /* AuDbg("b%d, b%d\n", cookie->bindex, bend); */ ++ for (; !err && cookie->bindex <= bend; ++ cookie->bindex++, cookie->h_pos = 0) { ++ h_file = au_hf_dir(file, cookie->bindex); ++ if (!h_file) ++ continue; ++ ++ au_fclr_rdu(cookie->flags, FULL); ++ err = au_rdu_do(h_file, &arg); ++ AuTraceErr(err); ++ if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err)) ++ break; ++ } ++ AuDbg("rent %llu\n", rdu->rent); ++ ++ if (!err && !au_ftest_rdu(cookie->flags, CONT)) { ++ rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH); ++ au_fset_rdu(cookie->flags, CONT); ++ cookie->generation = au_figen(file); ++ } ++ ++ ii_read_lock_child(inode); ++ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); ++ ii_read_unlock(inode); ++ ++out_unlock: ++ fi_read_unlock(file); ++out_si: ++ si_read_unlock(arg.sb); ++out_mtx: ++ mutex_unlock(&inode->i_mutex); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu) ++{ ++ int err; ++ ino_t ino; ++ unsigned long long nent; ++ union au_rdu_ent_ul *u; ++ struct au_rdu_ent ent; ++ struct super_block *sb; ++ ++ err = 0; ++ nent = rdu->nent; ++ u = &rdu->ent; ++ sb = file->f_path.dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ while (nent-- > 0) { ++ /* unnecessary to support mmap_sem since this is a dir */ ++ err = copy_from_user(&ent, u->e, sizeof(ent)); ++ if (!err) ++ err = !access_ok(VERIFY_WRITE, &u->e->ino, sizeof(ino)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ break; ++ } ++ ++ /* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */ ++ if (!ent.wh) ++ err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino); ++ else ++ err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type, ++ &ino); ++ if (unlikely(err)) { ++ AuTraceErr(err); ++ break; ++ } ++ ++ err = __put_user(ino, &u->e->ino); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ break; ++ } ++ u->ul += au_rdu_len(ent.nlen); ++ } ++ si_read_unlock(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_rdu_verify(struct aufs_rdu *rdu) ++{ ++ AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | " ++ "%llu, b%d, 0x%x, g%u}\n", ++ rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ], ++ rdu->blk, ++ rdu->rent, rdu->shwh, rdu->full, ++ rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags, ++ rdu->cookie.generation); ++ ++ if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu)) ++ return 0; ++ ++ AuDbg("%u:%u\n", ++ rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu)); ++ return -EINVAL; ++} ++ ++long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err, e; ++ struct aufs_rdu rdu; ++ void __user *p = (void __user *)arg; ++ ++ err = copy_from_user(&rdu, p, sizeof(rdu)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ err = au_rdu_verify(&rdu); ++ if (unlikely(err)) ++ goto out; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ err = au_rdu(file, &rdu); ++ if (unlikely(err)) ++ break; ++ ++ e = copy_to_user(p, &rdu, sizeof(rdu)); ++ if (unlikely(e)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ } ++ break; ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_ino(file, &rdu); ++ break; ++ ++ default: ++ /* err = -ENOTTY; */ ++ err = -EINVAL; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++#ifdef CONFIG_COMPAT ++long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err, e; ++ struct aufs_rdu rdu; ++ void __user *p = compat_ptr(arg); ++ ++ /* todo: get_user()? */ ++ err = copy_from_user(&rdu, p, sizeof(rdu)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ rdu.ent.e = compat_ptr(rdu.ent.ul); ++ err = au_rdu_verify(&rdu); ++ if (unlikely(err)) ++ goto out; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ err = au_rdu(file, &rdu); ++ if (unlikely(err)) ++ break; ++ ++ rdu.ent.ul = ptr_to_compat(rdu.ent.e); ++ rdu.tail.ul = ptr_to_compat(rdu.tail.e); ++ e = copy_to_user(p, &rdu, sizeof(rdu)); ++ if (unlikely(e)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ } ++ break; ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_ino(file, &rdu); ++ break; ++ ++ default: ++ /* err = -ENOTTY; */ ++ err = -EINVAL; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++#endif +diff -Nur linux-4.0.4.orig/fs/aufs/rwsem.h linux-4.0.4/fs/aufs/rwsem.h +--- linux-4.0.4.orig/fs/aufs/rwsem.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/rwsem.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,178 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * simple read-write semaphore wrappers ++ */ ++ ++#ifndef __AUFS_RWSEM_H__ ++#define __AUFS_RWSEM_H__ ++ ++#ifdef __KERNEL__ ++ ++#include "debug.h" ++ ++struct au_rwsem { ++ struct rw_semaphore rwsem; ++#ifdef CONFIG_AUFS_DEBUG ++ /* just for debugging, not almighty counter */ ++ atomic_t rcnt, wcnt; ++#endif ++}; ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define AuDbgCntInit(rw) do { \ ++ atomic_set(&(rw)->rcnt, 0); \ ++ atomic_set(&(rw)->wcnt, 0); \ ++ smp_mb(); /* atomic set */ \ ++} while (0) ++ ++#define AuDbgRcntInc(rw) atomic_inc(&(rw)->rcnt) ++#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0) ++#define AuDbgWcntInc(rw) atomic_inc(&(rw)->wcnt) ++#define AuDbgWcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0) ++#else ++#define AuDbgCntInit(rw) do {} while (0) ++#define AuDbgRcntInc(rw) do {} while (0) ++#define AuDbgRcntDec(rw) do {} while (0) ++#define AuDbgWcntInc(rw) do {} while (0) ++#define AuDbgWcntDec(rw) do {} while (0) ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++/* to debug easier, do not make them inlined functions */ ++#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list)) ++/* rwsem_is_locked() is unusable */ ++#define AuRwMustReadLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0) ++#define AuRwMustWriteLock(rw) AuDebugOn(atomic_read(&(rw)->wcnt) <= 0) ++#define AuRwMustAnyLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \ ++ && atomic_read(&(rw)->wcnt) <= 0) ++#define AuRwDestroy(rw) AuDebugOn(atomic_read(&(rw)->rcnt) \ ++ || atomic_read(&(rw)->wcnt)) ++ ++#define au_rw_class(rw, key) lockdep_set_class(&(rw)->rwsem, key) ++ ++static inline void au_rw_init(struct au_rwsem *rw) ++{ ++ AuDbgCntInit(rw); ++ init_rwsem(&rw->rwsem); ++} ++ ++static inline void au_rw_init_wlock(struct au_rwsem *rw) ++{ ++ au_rw_init(rw); ++ down_write(&rw->rwsem); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_init_wlock_nested(struct au_rwsem *rw, ++ unsigned int lsc) ++{ ++ au_rw_init(rw); ++ down_write_nested(&rw->rwsem, lsc); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_read_lock(struct au_rwsem *rw) ++{ ++ down_read(&rw->rwsem); ++ AuDbgRcntInc(rw); ++} ++ ++static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc) ++{ ++ down_read_nested(&rw->rwsem, lsc); ++ AuDbgRcntInc(rw); ++} ++ ++static inline void au_rw_read_unlock(struct au_rwsem *rw) ++{ ++ AuRwMustReadLock(rw); ++ AuDbgRcntDec(rw); ++ up_read(&rw->rwsem); ++} ++ ++static inline void au_rw_dgrade_lock(struct au_rwsem *rw) ++{ ++ AuRwMustWriteLock(rw); ++ AuDbgRcntInc(rw); ++ AuDbgWcntDec(rw); ++ downgrade_write(&rw->rwsem); ++} ++ ++static inline void au_rw_write_lock(struct au_rwsem *rw) ++{ ++ down_write(&rw->rwsem); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_write_lock_nested(struct au_rwsem *rw, ++ unsigned int lsc) ++{ ++ down_write_nested(&rw->rwsem, lsc); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_write_unlock(struct au_rwsem *rw) ++{ ++ AuRwMustWriteLock(rw); ++ AuDbgWcntDec(rw); ++ up_write(&rw->rwsem); ++} ++ ++/* why is not _nested version defined */ ++static inline int au_rw_read_trylock(struct au_rwsem *rw) ++{ ++ int ret; ++ ++ ret = down_read_trylock(&rw->rwsem); ++ if (ret) ++ AuDbgRcntInc(rw); ++ return ret; ++} ++ ++static inline int au_rw_write_trylock(struct au_rwsem *rw) ++{ ++ int ret; ++ ++ ret = down_write_trylock(&rw->rwsem); ++ if (ret) ++ AuDbgWcntInc(rw); ++ return ret; ++} ++ ++#undef AuDbgCntInit ++#undef AuDbgRcntInc ++#undef AuDbgRcntDec ++#undef AuDbgWcntInc ++#undef AuDbgWcntDec ++ ++#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ ++static inline void prefix##_read_lock(param) \ ++{ au_rw_read_lock(rwsem); } \ ++static inline void prefix##_write_lock(param) \ ++{ au_rw_write_lock(rwsem); } \ ++static inline int prefix##_read_trylock(param) \ ++{ return au_rw_read_trylock(rwsem); } \ ++static inline int prefix##_write_trylock(param) \ ++{ return au_rw_write_trylock(rwsem); } ++/* why is not _nested version defined */ ++/* static inline void prefix##_read_trylock_nested(param, lsc) ++{ au_rw_read_trylock_nested(rwsem, lsc)); } ++static inline void prefix##_write_trylock_nestd(param, lsc) ++{ au_rw_write_trylock_nested(rwsem, lsc); } */ ++ ++#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \ ++static inline void prefix##_read_unlock(param) \ ++{ au_rw_read_unlock(rwsem); } \ ++static inline void prefix##_write_unlock(param) \ ++{ au_rw_write_unlock(rwsem); } \ ++static inline void prefix##_downgrade_lock(param) \ ++{ au_rw_dgrade_lock(rwsem); } ++ ++#define AuSimpleRwsemFuncs(prefix, param, rwsem) \ ++ AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ ++ AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_RWSEM_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/sbinfo.c linux-4.0.4/fs/aufs/sbinfo.c +--- linux-4.0.4.orig/fs/aufs/sbinfo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/sbinfo.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,341 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * superblock private data ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * they are necessary regardless sysfs is disabled. ++ */ ++void au_si_free(struct kobject *kobj) ++{ ++ int i; ++ struct au_sbinfo *sbinfo; ++ char *locked __maybe_unused; /* debug only */ ++ ++ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); ++ for (i = 0; i < AuPlink_NHASH; i++) ++ AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); ++ AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); ++ ++ au_rw_write_lock(&sbinfo->si_rwsem); ++ au_br_free(sbinfo); ++ au_rw_write_unlock(&sbinfo->si_rwsem); ++ ++ AuDebugOn(radix_tree_gang_lookup ++ (&sbinfo->au_si_pid.tree, (void **)&locked, ++ /*first_index*/PID_MAX_DEFAULT - 1, ++ /*max_items*/sizeof(locked)/sizeof(*locked))); ++ ++ kfree(sbinfo->si_branch); ++ kfree(sbinfo->au_si_pid.bitmap); ++ mutex_destroy(&sbinfo->si_xib_mtx); ++ AuRwDestroy(&sbinfo->si_rwsem); ++ ++ kfree(sbinfo); ++} ++ ++int au_si_alloc(struct super_block *sb) ++{ ++ int err, i; ++ struct au_sbinfo *sbinfo; ++ static struct lock_class_key aufs_si; ++ ++ err = -ENOMEM; ++ sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); ++ if (unlikely(!sbinfo)) ++ goto out; ++ ++ BUILD_BUG_ON(sizeof(unsigned long) != ++ sizeof(*sbinfo->au_si_pid.bitmap)); ++ sbinfo->au_si_pid.bitmap = kcalloc(BITS_TO_LONGS(PID_MAX_DEFAULT), ++ sizeof(*sbinfo->au_si_pid.bitmap), ++ GFP_NOFS); ++ if (unlikely(!sbinfo->au_si_pid.bitmap)) ++ goto out_sbinfo; ++ ++ /* will be reallocated separately */ ++ sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); ++ if (unlikely(!sbinfo->si_branch)) ++ goto out_pidmap; ++ ++ err = sysaufs_si_init(sbinfo); ++ if (unlikely(err)) ++ goto out_br; ++ ++ au_nwt_init(&sbinfo->si_nowait); ++ au_rw_init_wlock(&sbinfo->si_rwsem); ++ au_rw_class(&sbinfo->si_rwsem, &aufs_si); ++ spin_lock_init(&sbinfo->au_si_pid.tree_lock); ++ INIT_RADIX_TREE(&sbinfo->au_si_pid.tree, GFP_ATOMIC | __GFP_NOFAIL); ++ ++ atomic_long_set(&sbinfo->si_ninodes, 0); ++ atomic_long_set(&sbinfo->si_nfiles, 0); ++ ++ sbinfo->si_bend = -1; ++ sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; ++ ++ sbinfo->si_wbr_copyup = AuWbrCopyup_Def; ++ sbinfo->si_wbr_create = AuWbrCreate_Def; ++ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup; ++ sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create; ++ ++ au_fhsm_init(sbinfo); ++ ++ sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); ++ ++ sbinfo->si_xino_jiffy = jiffies; ++ sbinfo->si_xino_expire ++ = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); ++ mutex_init(&sbinfo->si_xib_mtx); ++ sbinfo->si_xino_brid = -1; ++ /* leave si_xib_last_pindex and si_xib_next_bit */ ++ ++ au_sphl_init(&sbinfo->si_aopen); ++ ++ sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC); ++ sbinfo->si_rdblk = AUFS_RDBLK_DEF; ++ sbinfo->si_rdhash = AUFS_RDHASH_DEF; ++ sbinfo->si_dirwh = AUFS_DIRWH_DEF; ++ ++ for (i = 0; i < AuPlink_NHASH; i++) ++ au_sphl_init(sbinfo->si_plink + i); ++ init_waitqueue_head(&sbinfo->si_plink_wq); ++ spin_lock_init(&sbinfo->si_plink_maint_lock); ++ ++ au_sphl_init(&sbinfo->si_files); ++ ++ /* leave other members for sysaufs and si_mnt. */ ++ sbinfo->si_sb = sb; ++ sb->s_fs_info = sbinfo; ++ si_pid_set(sb); ++ return 0; /* success */ ++ ++out_br: ++ kfree(sbinfo->si_branch); ++out_pidmap: ++ kfree(sbinfo->au_si_pid.bitmap); ++out_sbinfo: ++ kfree(sbinfo); ++out: ++ return err; ++} ++ ++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr) ++{ ++ int err, sz; ++ struct au_branch **brp; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ err = -ENOMEM; ++ sz = sizeof(*brp) * (sbinfo->si_bend + 1); ++ if (unlikely(!sz)) ++ sz = sizeof(*brp); ++ brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS); ++ if (brp) { ++ sbinfo->si_branch = brp; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++unsigned int au_sigen_inc(struct super_block *sb) ++{ ++ unsigned int gen; ++ ++ SiMustWriteLock(sb); ++ ++ gen = ++au_sbi(sb)->si_generation; ++ au_update_digen(sb->s_root); ++ au_update_iigen(sb->s_root->d_inode, /*half*/0); ++ sb->s_root->d_inode->i_version++; ++ return gen; ++} ++ ++aufs_bindex_t au_new_br_id(struct super_block *sb) ++{ ++ aufs_bindex_t br_id; ++ int i; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ for (i = 0; i <= AUFS_BRANCH_MAX; i++) { ++ br_id = ++sbinfo->si_last_br_id; ++ AuDebugOn(br_id < 0); ++ if (br_id && au_br_index(sb, br_id) < 0) ++ return br_id; ++ } ++ ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* it is ok that new 'nwt' tasks are appended while we are sleeping */ ++int si_read_lock(struct super_block *sb, int flags) ++{ ++ int err; ++ ++ err = 0; ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ ++ si_noflush_read_lock(sb); ++ err = au_plink_maint(sb, flags); ++ if (unlikely(err)) ++ si_read_unlock(sb); ++ ++ return err; ++} ++ ++int si_write_lock(struct super_block *sb, int flags) ++{ ++ int err; ++ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ ++ si_noflush_write_lock(sb); ++ err = au_plink_maint(sb, flags); ++ if (unlikely(err)) ++ si_write_unlock(sb); ++ ++ return err; ++} ++ ++/* dentry and super_block lock. call at entry point */ ++int aufs_read_lock(struct dentry *dentry, int flags) ++{ ++ int err; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, flags); ++ if (unlikely(err)) ++ goto out; ++ ++ if (au_ftest_lock(flags, DW)) ++ di_write_lock_child(dentry); ++ else ++ di_read_lock_child(dentry, flags); ++ ++ if (au_ftest_lock(flags, GEN)) { ++ err = au_digen_test(dentry, au_sigen(sb)); ++ AuDebugOn(!err && au_dbrange_test(dentry)); ++ if (unlikely(err)) ++ aufs_read_unlock(dentry, flags); ++ } ++ ++out: ++ return err; ++} ++ ++void aufs_read_unlock(struct dentry *dentry, int flags) ++{ ++ if (au_ftest_lock(flags, DW)) ++ di_write_unlock(dentry); ++ else ++ di_read_unlock(dentry, flags); ++ si_read_unlock(dentry->d_sb); ++} ++ ++void aufs_write_lock(struct dentry *dentry) ++{ ++ si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW); ++ di_write_lock_child(dentry); ++} ++ ++void aufs_write_unlock(struct dentry *dentry) ++{ ++ di_write_unlock(dentry); ++ si_write_unlock(dentry->d_sb); ++} ++ ++int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) ++{ ++ int err; ++ unsigned int sigen; ++ struct super_block *sb; ++ ++ sb = d1->d_sb; ++ err = si_read_lock(sb, flags); ++ if (unlikely(err)) ++ goto out; ++ ++ di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); ++ ++ if (au_ftest_lock(flags, GEN)) { ++ sigen = au_sigen(sb); ++ err = au_digen_test(d1, sigen); ++ AuDebugOn(!err && au_dbrange_test(d1)); ++ if (!err) { ++ err = au_digen_test(d2, sigen); ++ AuDebugOn(!err && au_dbrange_test(d2)); ++ } ++ if (unlikely(err)) ++ aufs_read_and_write_unlock2(d1, d2); ++ } ++ ++out: ++ return err; ++} ++ ++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) ++{ ++ di_write_unlock2(d1, d2); ++ si_read_unlock(d1->d_sb); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int si_pid_test_slow(struct super_block *sb) ++{ ++ void *p; ++ ++ rcu_read_lock(); ++ p = radix_tree_lookup(&au_sbi(sb)->au_si_pid.tree, current->pid); ++ rcu_read_unlock(); ++ ++ return (long)!!p; ++} ++ ++void si_pid_set_slow(struct super_block *sb) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(si_pid_test_slow(sb)); ++ ++ sbinfo = au_sbi(sb); ++ err = radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); ++ AuDebugOn(err); ++ spin_lock(&sbinfo->au_si_pid.tree_lock); ++ err = radix_tree_insert(&sbinfo->au_si_pid.tree, current->pid, ++ /*any valid ptr*/sb); ++ spin_unlock(&sbinfo->au_si_pid.tree_lock); ++ AuDebugOn(err); ++ radix_tree_preload_end(); ++} ++ ++void si_pid_clr_slow(struct super_block *sb) ++{ ++ void *p; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(!si_pid_test_slow(sb)); ++ ++ sbinfo = au_sbi(sb); ++ spin_lock(&sbinfo->au_si_pid.tree_lock); ++ p = radix_tree_delete(&sbinfo->au_si_pid.tree, current->pid); ++ spin_unlock(&sbinfo->au_si_pid.tree_lock); ++} +diff -Nur linux-4.0.4.orig/fs/aufs/spl.h linux-4.0.4/fs/aufs/spl.h +--- linux-4.0.4.orig/fs/aufs/spl.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/spl.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,98 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * simple list protected by a spinlock ++ */ ++ ++#ifndef __AUFS_SPL_H__ ++#define __AUFS_SPL_H__ ++ ++#ifdef __KERNEL__ ++ ++struct au_splhead { ++ spinlock_t spin; ++ struct list_head head; ++}; ++ ++static inline void au_spl_init(struct au_splhead *spl) ++{ ++ spin_lock_init(&spl->spin); ++ INIT_LIST_HEAD(&spl->head); ++} ++ ++static inline void au_spl_add(struct list_head *list, struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_add(list, &spl->head); ++ spin_unlock(&spl->spin); ++} ++ ++static inline void au_spl_del(struct list_head *list, struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_del(list); ++ spin_unlock(&spl->spin); ++} ++ ++static inline void au_spl_del_rcu(struct list_head *list, ++ struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_del_rcu(list); ++ spin_unlock(&spl->spin); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_sphlhead { ++ spinlock_t spin; ++ struct hlist_head head; ++}; ++ ++static inline void au_sphl_init(struct au_sphlhead *sphl) ++{ ++ spin_lock_init(&sphl->spin); ++ INIT_HLIST_HEAD(&sphl->head); ++} ++ ++static inline void au_sphl_add(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_add_head(hlist, &sphl->head); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline void au_sphl_del(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_del(hlist); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline void au_sphl_del_rcu(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_del_rcu(hlist); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline unsigned long au_sphl_count(struct au_sphlhead *sphl) ++{ ++ unsigned long cnt; ++ struct hlist_node *pos; ++ ++ cnt = 0; ++ spin_lock(&sphl->spin); ++ hlist_for_each(pos, &sphl->head) ++ cnt++; ++ spin_unlock(&sphl->spin); ++ return cnt; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_SPL_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/super.c linux-4.0.4/fs/aufs/super.c +--- linux-4.0.4.orig/fs/aufs/super.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/super.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,993 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * mount and super_block operations ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++/* ++ * super_operations ++ */ ++static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) ++{ ++ struct au_icntnr *c; ++ ++ c = au_cache_alloc_icntnr(); ++ if (c) { ++ au_icntnr_init(c); ++ c->vfs_inode.i_version = 1; /* sigen(sb); */ ++ c->iinfo.ii_hinode = NULL; ++ return &c->vfs_inode; ++ } ++ return NULL; ++} ++ ++static void aufs_destroy_inode_cb(struct rcu_head *head) ++{ ++ struct inode *inode = container_of(head, struct inode, i_rcu); ++ ++ INIT_HLIST_HEAD(&inode->i_dentry); ++ au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); ++} ++ ++static void aufs_destroy_inode(struct inode *inode) ++{ ++ au_iinfo_fin(inode); ++ call_rcu(&inode->i_rcu, aufs_destroy_inode_cb); ++} ++ ++struct inode *au_iget_locked(struct super_block *sb, ino_t ino) ++{ ++ struct inode *inode; ++ int err; ++ ++ inode = iget_locked(sb, ino); ++ if (unlikely(!inode)) { ++ inode = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ if (!(inode->i_state & I_NEW)) ++ goto out; ++ ++ err = au_xigen_new(inode); ++ if (!err) ++ err = au_iinfo_init(inode); ++ if (!err) ++ inode->i_version++; ++ else { ++ iget_failed(inode); ++ inode = ERR_PTR(err); ++ } ++ ++out: ++ /* never return NULL */ ++ AuDebugOn(!inode); ++ AuTraceErrPtr(inode); ++ return inode; ++} ++ ++/* lock free root dinfo */ ++static int au_show_brs(struct seq_file *seq, struct super_block *sb) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct path path; ++ struct au_hdentry *hdp; ++ struct au_branch *br; ++ au_br_perm_str_t perm; ++ ++ err = 0; ++ bend = au_sbend(sb); ++ hdp = au_di(sb->s_root)->di_hdentry; ++ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ path.mnt = au_br_mnt(br); ++ path.dentry = hdp[bindex].hd_dentry; ++ err = au_seq_path(seq, &path); ++ if (err > 0) { ++ au_optstr_br_perm(&perm, br->br_perm); ++ err = seq_printf(seq, "=%s", perm.a); ++ if (err == -1) ++ err = -E2BIG; ++ } ++ if (!err && bindex != bend) ++ err = seq_putc(seq, ':'); ++ } ++ ++ return err; ++} ++ ++static void au_show_wbr_create(struct seq_file *m, int v, ++ struct au_sbinfo *sbinfo) ++{ ++ const char *pat; ++ ++ AuRwMustAnyLock(&sbinfo->si_rwsem); ++ ++ seq_puts(m, ",create="); ++ pat = au_optstr_wbr_create(v); ++ switch (v) { ++ case AuWbrCreate_TDP: ++ case AuWbrCreate_RR: ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_PMFS: ++ seq_puts(m, pat); ++ break; ++ case AuWbrCreate_MFSV: ++ seq_printf(m, /*pat*/"mfs:%lu", ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ case AuWbrCreate_PMFSV: ++ seq_printf(m, /*pat*/"pmfs:%lu", ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ case AuWbrCreate_MFSRR: ++ seq_printf(m, /*pat*/"mfsrr:%llu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark); ++ break; ++ case AuWbrCreate_MFSRRV: ++ seq_printf(m, /*pat*/"mfsrr:%llu:%lu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark, ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ case AuWbrCreate_PMFSRR: ++ seq_printf(m, /*pat*/"pmfsrr:%llu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark); ++ break; ++ case AuWbrCreate_PMFSRRV: ++ seq_printf(m, /*pat*/"pmfsrr:%llu:%lu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark, ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ } ++} ++ ++static int au_show_xino(struct seq_file *seq, struct super_block *sb) ++{ ++#ifdef CONFIG_SYSFS ++ return 0; ++#else ++ int err; ++ const int len = sizeof(AUFS_XINO_FNAME) - 1; ++ aufs_bindex_t bindex, brid; ++ struct qstr *name; ++ struct file *f; ++ struct dentry *d, *h_root; ++ struct au_hdentry *hdp; ++ ++ AuRwMustAnyLock(&sbinfo->si_rwsem); ++ ++ err = 0; ++ f = au_sbi(sb)->si_xib; ++ if (!f) ++ goto out; ++ ++ /* stop printing the default xino path on the first writable branch */ ++ h_root = NULL; ++ brid = au_xino_brid(sb); ++ if (brid >= 0) { ++ bindex = au_br_index(sb, brid); ++ hdp = au_di(sb->s_root)->di_hdentry; ++ h_root = hdp[0 + bindex].hd_dentry; ++ } ++ d = f->f_path.dentry; ++ name = &d->d_name; ++ /* safe ->d_parent because the file is unlinked */ ++ if (d->d_parent == h_root ++ && name->len == len ++ && !memcmp(name->name, AUFS_XINO_FNAME, len)) ++ goto out; ++ ++ seq_puts(seq, ",xino="); ++ err = au_xino_path(seq, f); ++ ++out: ++ return err; ++#endif ++} ++ ++/* seq_file will re-call me in case of too long string */ ++static int aufs_show_options(struct seq_file *m, struct dentry *dentry) ++{ ++ int err; ++ unsigned int mnt_flags, v; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++#define AuBool(name, str) do { \ ++ v = au_opt_test(mnt_flags, name); \ ++ if (v != au_opt_test(AuOpt_Def, name)) \ ++ seq_printf(m, ",%s" #str, v ? "" : "no"); \ ++} while (0) ++ ++#define AuStr(name, str) do { \ ++ v = mnt_flags & AuOptMask_##name; \ ++ if (v != (AuOpt_Def & AuOptMask_##name)) \ ++ seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ ++} while (0) ++ ++#define AuUInt(name, str, val) do { \ ++ if (val != AUFS_##name##_DEF) \ ++ seq_printf(m, "," #str "=%u", val); \ ++} while (0) ++ ++ sb = dentry->d_sb; ++ if (sb->s_flags & MS_POSIXACL) ++ seq_puts(m, ",acl"); ++ ++ /* lock free root dinfo */ ++ si_noflush_read_lock(sb); ++ sbinfo = au_sbi(sb); ++ seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo)); ++ ++ mnt_flags = au_mntflags(sb); ++ if (au_opt_test(mnt_flags, XINO)) { ++ err = au_show_xino(m, sb); ++ if (unlikely(err)) ++ goto out; ++ } else ++ seq_puts(m, ",noxino"); ++ ++ AuBool(TRUNC_XINO, trunc_xino); ++ AuStr(UDBA, udba); ++ AuBool(SHWH, shwh); ++ AuBool(PLINK, plink); ++ AuBool(DIO, dio); ++ AuBool(DIRPERM1, dirperm1); ++ ++ v = sbinfo->si_wbr_create; ++ if (v != AuWbrCreate_Def) ++ au_show_wbr_create(m, v, sbinfo); ++ ++ v = sbinfo->si_wbr_copyup; ++ if (v != AuWbrCopyup_Def) ++ seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); ++ ++ v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); ++ if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) ++ seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); ++ ++ AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); ++ ++ v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; ++ AuUInt(RDCACHE, rdcache, v); ++ ++ AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); ++ AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); ++ ++ au_fhsm_show(m, sbinfo); ++ ++ AuBool(SUM, sum); ++ /* AuBool(SUM_W, wsum); */ ++ AuBool(WARN_PERM, warn_perm); ++ AuBool(VERBOSE, verbose); ++ ++out: ++ /* be sure to print "br:" last */ ++ if (!sysaufs_brs) { ++ seq_puts(m, ",br:"); ++ au_show_brs(m, sb); ++ } ++ si_read_unlock(sb); ++ return 0; ++ ++#undef AuBool ++#undef AuStr ++#undef AuUInt ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* sum mode which returns the summation for statfs(2) */ ++ ++static u64 au_add_till_max(u64 a, u64 b) ++{ ++ u64 old; ++ ++ old = a; ++ a += b; ++ if (old <= a) ++ return a; ++ return ULLONG_MAX; ++} ++ ++static u64 au_mul_till_max(u64 a, long mul) ++{ ++ u64 old; ++ ++ old = a; ++ a *= mul; ++ if (old <= a) ++ return a; ++ return ULLONG_MAX; ++} ++ ++static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf) ++{ ++ int err; ++ long bsize, factor; ++ u64 blocks, bfree, bavail, files, ffree; ++ aufs_bindex_t bend, bindex, i; ++ unsigned char shared; ++ struct path h_path; ++ struct super_block *h_sb; ++ ++ err = 0; ++ bsize = LONG_MAX; ++ files = 0; ++ ffree = 0; ++ blocks = 0; ++ bfree = 0; ++ bavail = 0; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ h_sb = h_path.mnt->mnt_sb; ++ shared = 0; ++ for (i = 0; !shared && i < bindex; i++) ++ shared = (au_sbr_sb(sb, i) == h_sb); ++ if (shared) ++ continue; ++ ++ /* sb->s_root for NFS is unreliable */ ++ h_path.dentry = h_path.mnt->mnt_root; ++ err = vfs_statfs(&h_path, buf); ++ if (unlikely(err)) ++ goto out; ++ ++ if (bsize > buf->f_bsize) { ++ /* ++ * we will reduce bsize, so we have to expand blocks ++ * etc. to match them again ++ */ ++ factor = (bsize / buf->f_bsize); ++ blocks = au_mul_till_max(blocks, factor); ++ bfree = au_mul_till_max(bfree, factor); ++ bavail = au_mul_till_max(bavail, factor); ++ bsize = buf->f_bsize; ++ } ++ ++ factor = (buf->f_bsize / bsize); ++ blocks = au_add_till_max(blocks, ++ au_mul_till_max(buf->f_blocks, factor)); ++ bfree = au_add_till_max(bfree, ++ au_mul_till_max(buf->f_bfree, factor)); ++ bavail = au_add_till_max(bavail, ++ au_mul_till_max(buf->f_bavail, factor)); ++ files = au_add_till_max(files, buf->f_files); ++ ffree = au_add_till_max(ffree, buf->f_ffree); ++ } ++ ++ buf->f_bsize = bsize; ++ buf->f_blocks = blocks; ++ buf->f_bfree = bfree; ++ buf->f_bavail = bavail; ++ buf->f_files = files; ++ buf->f_ffree = ffree; ++ buf->f_frsize = 0; ++ ++out: ++ return err; ++} ++ ++static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ int err; ++ struct path h_path; ++ struct super_block *sb; ++ ++ /* lock free root dinfo */ ++ sb = dentry->d_sb; ++ si_noflush_read_lock(sb); ++ if (!au_opt_test(au_mntflags(sb), SUM)) { ++ /* sb->s_root for NFS is unreliable */ ++ h_path.mnt = au_sbr_mnt(sb, 0); ++ h_path.dentry = h_path.mnt->mnt_root; ++ err = vfs_statfs(&h_path, buf); ++ } else ++ err = au_statfs_sum(sb, buf); ++ si_read_unlock(sb); ++ ++ if (!err) { ++ buf->f_type = AUFS_SUPER_MAGIC; ++ buf->f_namelen = AUFS_MAX_NAMELEN; ++ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); ++ } ++ /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_sync_fs(struct super_block *sb, int wait) ++{ ++ int err, e; ++ aufs_bindex_t bend, bindex; ++ struct au_branch *br; ++ struct super_block *h_sb; ++ ++ err = 0; ++ si_noflush_read_lock(sb); ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!au_br_writable(br->br_perm)) ++ continue; ++ ++ h_sb = au_sbr_sb(sb, bindex); ++ if (h_sb->s_op->sync_fs) { ++ e = h_sb->s_op->sync_fs(h_sb, wait); ++ if (unlikely(e && !err)) ++ err = e; ++ /* go on even if an error happens */ ++ } ++ } ++ si_read_unlock(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* final actions when unmounting a file system */ ++static void aufs_put_super(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ if (!sbinfo) ++ return; ++ ++ dbgaufs_si_fin(sbinfo); ++ kobject_put(&sbinfo->si_kobj); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_array_free(void *array) ++{ ++ if (array) { ++ if (!is_vmalloc_addr(array)) ++ kfree(array); ++ else ++ vfree(array); ++ } ++} ++ ++void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg) ++{ ++ void *array; ++ unsigned long long n, sz; ++ ++ array = NULL; ++ n = 0; ++ if (!*hint) ++ goto out; ++ ++ if (*hint > ULLONG_MAX / sizeof(array)) { ++ array = ERR_PTR(-EMFILE); ++ pr_err("hint %llu\n", *hint); ++ goto out; ++ } ++ ++ sz = sizeof(array) * *hint; ++ array = kzalloc(sz, GFP_NOFS); ++ if (unlikely(!array)) ++ array = vzalloc(sz); ++ if (unlikely(!array)) { ++ array = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ n = cb(array, *hint, arg); ++ AuDebugOn(n > *hint); ++ ++out: ++ *hint = n; ++ return array; ++} ++ ++static unsigned long long au_iarray_cb(void *a, ++ unsigned long long max __maybe_unused, ++ void *arg) ++{ ++ unsigned long long n; ++ struct inode **p, *inode; ++ struct list_head *head; ++ ++ n = 0; ++ p = a; ++ head = arg; ++ spin_lock(&inode_sb_list_lock); ++ list_for_each_entry(inode, head, i_sb_list) { ++ if (!is_bad_inode(inode) ++ && au_ii(inode)->ii_bstart >= 0) { ++ spin_lock(&inode->i_lock); ++ if (atomic_read(&inode->i_count)) { ++ au_igrab(inode); ++ *p++ = inode; ++ n++; ++ AuDebugOn(n > max); ++ } ++ spin_unlock(&inode->i_lock); ++ } ++ } ++ spin_unlock(&inode_sb_list_lock); ++ ++ return n; ++} ++ ++struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) ++{ ++ *max = atomic_long_read(&au_sbi(sb)->si_ninodes); ++ return au_array_alloc(max, au_iarray_cb, &sb->s_inodes); ++} ++ ++void au_iarray_free(struct inode **a, unsigned long long max) ++{ ++ unsigned long long ull; ++ ++ for (ull = 0; ull < max; ull++) ++ iput(a[ull]); ++ au_array_free(a); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * refresh dentry and inode at remount time. ++ */ ++/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */ ++static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags, ++ struct dentry *parent) ++{ ++ int err; ++ ++ di_write_lock_child(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_refresh_dentry(dentry, parent); ++ if (!err && dir_flags) ++ au_hn_reset(dentry->d_inode, dir_flags); ++ di_read_unlock(parent, AuLock_IR); ++ di_write_unlock(dentry); ++ ++ return err; ++} ++ ++static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen, ++ struct au_sbinfo *sbinfo, ++ const unsigned int dir_flags) ++{ ++ int err; ++ struct dentry *parent; ++ struct inode *inode; ++ ++ err = 0; ++ parent = dget_parent(dentry); ++ if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) { ++ inode = dentry->d_inode; ++ if (inode) { ++ if (!S_ISDIR(inode->i_mode)) ++ err = au_do_refresh(dentry, /*dir_flags*/0, ++ parent); ++ else { ++ err = au_do_refresh(dentry, dir_flags, parent); ++ if (unlikely(err)) ++ au_fset_si(sbinfo, FAILED_REFRESH_DIR); ++ } ++ } else ++ err = au_do_refresh(dentry, /*dir_flags*/0, parent); ++ AuDbgDentry(dentry); ++ } ++ dput(parent); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_refresh_d(struct super_block *sb) ++{ ++ int err, i, j, ndentry, e; ++ unsigned int sigen; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries, *d; ++ struct au_sbinfo *sbinfo; ++ struct dentry *root = sb->s_root; ++ const unsigned int dir_flags = au_hi_flags(root->d_inode, /*isdir*/1); ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, root, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ sigen = au_sigen(sb); ++ sbinfo = au_sbi(sb); ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) { ++ d = dentries[j]; ++ e = au_do_refresh_d(d, sigen, sbinfo, dir_flags); ++ if (unlikely(e && !err)) ++ err = e; ++ /* go on even err */ ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static int au_refresh_i(struct super_block *sb) ++{ ++ int err, e; ++ unsigned int sigen; ++ unsigned long long max, ull; ++ struct inode *inode, **array; ++ ++ array = au_iarray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ err = 0; ++ sigen = au_sigen(sb); ++ for (ull = 0; ull < max; ull++) { ++ inode = array[ull]; ++ if (unlikely(!inode)) ++ break; ++ if (au_iigen(inode, NULL) != sigen) { ++ ii_write_lock_child(inode); ++ e = au_refresh_hinode_self(inode); ++ ii_write_unlock(inode); ++ if (unlikely(e)) { ++ pr_err("error %d, i%lu\n", e, inode->i_ino); ++ if (!err) ++ err = e; ++ /* go on even if err */ ++ } ++ } ++ } ++ ++ au_iarray_free(array, max); ++ ++out: ++ return err; ++} ++ ++static void au_remount_refresh(struct super_block *sb) ++{ ++ int err, e; ++ unsigned int udba; ++ aufs_bindex_t bindex, bend; ++ struct dentry *root; ++ struct inode *inode; ++ struct au_branch *br; ++ ++ au_sigen_inc(sb); ++ au_fclr_si(au_sbi(sb), FAILED_REFRESH_DIR); ++ ++ root = sb->s_root; ++ DiMustNoWaiters(root); ++ inode = root->d_inode; ++ IiMustNoWaiters(inode); ++ ++ udba = au_opt_udba(sb); ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ err = au_hnotify_reset_br(udba, br, br->br_perm); ++ if (unlikely(err)) ++ AuIOErr("hnotify failed on br %d, %d, ignored\n", ++ bindex, err); ++ /* go on even if err */ ++ } ++ au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); ++ ++ di_write_unlock(root); ++ err = au_refresh_d(sb); ++ e = au_refresh_i(sb); ++ if (unlikely(e && !err)) ++ err = e; ++ /* aufs_write_lock() calls ..._child() */ ++ di_write_lock_child(root); ++ ++ au_cpup_attr_all(inode, /*force*/1); ++ ++ if (unlikely(err)) ++ AuIOErr("refresh failed, ignored, %d\n", err); ++} ++ ++/* stop extra interpretation of errno in mount(8), and strange error messages */ ++static int cvt_err(int err) ++{ ++ AuTraceErr(err); ++ ++ switch (err) { ++ case -ENOENT: ++ case -ENOTDIR: ++ case -EEXIST: ++ case -EIO: ++ err = -EINVAL; ++ } ++ return err; ++} ++ ++static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) ++{ ++ int err, do_dx; ++ unsigned int mntflags; ++ struct au_opts opts; ++ struct dentry *root; ++ struct inode *inode; ++ struct au_sbinfo *sbinfo; ++ ++ err = 0; ++ root = sb->s_root; ++ if (!data || !*data) { ++ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (!err) { ++ di_write_lock_child(root); ++ err = au_opts_verify(sb, *flags, /*pending*/0); ++ aufs_write_unlock(root); ++ } ++ goto out; ++ } ++ ++ err = -ENOMEM; ++ memset(&opts, 0, sizeof(opts)); ++ opts.opt = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!opts.opt)) ++ goto out; ++ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); ++ opts.flags = AuOpts_REMOUNT; ++ opts.sb_flags = *flags; ++ ++ /* parse it before aufs lock */ ++ err = au_opts_parse(sb, data, &opts); ++ if (unlikely(err)) ++ goto out_opts; ++ ++ sbinfo = au_sbi(sb); ++ inode = root->d_inode; ++ mutex_lock(&inode->i_mutex); ++ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_mtx; ++ di_write_lock_child(root); ++ ++ /* au_opts_remount() may return an error */ ++ err = au_opts_remount(sb, &opts); ++ au_opts_free(&opts); ++ ++ if (au_ftest_opts(opts.flags, REFRESH)) ++ au_remount_refresh(sb); ++ ++ if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) { ++ mntflags = au_mntflags(sb); ++ do_dx = !!au_opt_test(mntflags, DIO); ++ au_dy_arefresh(do_dx); ++ } ++ ++ au_fhsm_wrote_all(sb, /*force*/1); /* ?? */ ++ aufs_write_unlock(root); ++ ++out_mtx: ++ mutex_unlock(&inode->i_mutex); ++out_opts: ++ free_page((unsigned long)opts.opt); ++out: ++ err = cvt_err(err); ++ AuTraceErr(err); ++ return err; ++} ++ ++static const struct super_operations aufs_sop = { ++ .alloc_inode = aufs_alloc_inode, ++ .destroy_inode = aufs_destroy_inode, ++ /* always deleting, no clearing */ ++ .drop_inode = generic_delete_inode, ++ .show_options = aufs_show_options, ++ .statfs = aufs_statfs, ++ .put_super = aufs_put_super, ++ .sync_fs = aufs_sync_fs, ++ .remount_fs = aufs_remount_fs ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int alloc_root(struct super_block *sb) ++{ ++ int err; ++ struct inode *inode; ++ struct dentry *root; ++ ++ err = -ENOMEM; ++ inode = au_iget_locked(sb, AUFS_ROOT_INO); ++ err = PTR_ERR(inode); ++ if (IS_ERR(inode)) ++ goto out; ++ ++ inode->i_op = &aufs_dir_iop; ++ inode->i_fop = &aufs_dir_fop; ++ inode->i_mode = S_IFDIR; ++ set_nlink(inode, 2); ++ unlock_new_inode(inode); ++ ++ root = d_make_root(inode); ++ if (unlikely(!root)) ++ goto out; ++ err = PTR_ERR(root); ++ if (IS_ERR(root)) ++ goto out; ++ ++ err = au_di_init(root); ++ if (!err) { ++ sb->s_root = root; ++ return 0; /* success */ ++ } ++ dput(root); ++ ++out: ++ return err; ++} ++ ++static int aufs_fill_super(struct super_block *sb, void *raw_data, ++ int silent __maybe_unused) ++{ ++ int err; ++ struct au_opts opts; ++ struct dentry *root; ++ struct inode *inode; ++ char *arg = raw_data; ++ ++ if (unlikely(!arg || !*arg)) { ++ err = -EINVAL; ++ pr_err("no arg\n"); ++ goto out; ++ } ++ ++ err = -ENOMEM; ++ memset(&opts, 0, sizeof(opts)); ++ opts.opt = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!opts.opt)) ++ goto out; ++ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); ++ opts.sb_flags = sb->s_flags; ++ ++ err = au_si_alloc(sb); ++ if (unlikely(err)) ++ goto out_opts; ++ ++ /* all timestamps always follow the ones on the branch */ ++ sb->s_flags |= MS_NOATIME | MS_NODIRATIME; ++ sb->s_op = &aufs_sop; ++ sb->s_d_op = &aufs_dop; ++ sb->s_magic = AUFS_SUPER_MAGIC; ++ sb->s_maxbytes = 0; ++ sb->s_stack_depth = 1; ++ au_export_init(sb); ++ /* au_xattr_init(sb); */ ++ ++ err = alloc_root(sb); ++ if (unlikely(err)) { ++ si_write_unlock(sb); ++ goto out_info; ++ } ++ root = sb->s_root; ++ inode = root->d_inode; ++ ++ /* ++ * actually we can parse options regardless aufs lock here. ++ * but at remount time, parsing must be done before aufs lock. ++ * so we follow the same rule. ++ */ ++ ii_write_lock_parent(inode); ++ aufs_write_unlock(root); ++ err = au_opts_parse(sb, arg, &opts); ++ if (unlikely(err)) ++ goto out_root; ++ ++ /* lock vfs_inode first, then aufs. */ ++ mutex_lock(&inode->i_mutex); ++ aufs_write_lock(root); ++ err = au_opts_mount(sb, &opts); ++ au_opts_free(&opts); ++ aufs_write_unlock(root); ++ mutex_unlock(&inode->i_mutex); ++ if (!err) ++ goto out_opts; /* success */ ++ ++out_root: ++ dput(root); ++ sb->s_root = NULL; ++out_info: ++ dbgaufs_si_fin(au_sbi(sb)); ++ kobject_put(&au_sbi(sb)->si_kobj); ++ sb->s_fs_info = NULL; ++out_opts: ++ free_page((unsigned long)opts.opt); ++out: ++ AuTraceErr(err); ++ err = cvt_err(err); ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *aufs_mount(struct file_system_type *fs_type, int flags, ++ const char *dev_name __maybe_unused, ++ void *raw_data) ++{ ++ struct dentry *root; ++ struct super_block *sb; ++ ++ /* all timestamps always follow the ones on the branch */ ++ /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */ ++ root = mount_nodev(fs_type, flags, raw_data, aufs_fill_super); ++ if (IS_ERR(root)) ++ goto out; ++ ++ sb = root->d_sb; ++ si_write_lock(sb, !AuLock_FLUSH); ++ sysaufs_brs_add(sb, 0); ++ si_write_unlock(sb); ++ au_sbilist_add(sb); ++ ++out: ++ return root; ++} ++ ++static void aufs_kill_sb(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ if (sbinfo) { ++ au_sbilist_del(sb); ++ aufs_write_lock(sb->s_root); ++ au_fhsm_fin(sb); ++ if (sbinfo->si_wbr_create_ops->fin) ++ sbinfo->si_wbr_create_ops->fin(sb); ++ if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) { ++ au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE); ++ au_remount_refresh(sb); ++ } ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_put(sb, /*verbose*/1); ++ au_xino_clr(sb); ++ sbinfo->si_sb = NULL; ++ aufs_write_unlock(sb->s_root); ++ au_nwt_flush(&sbinfo->si_nowait); ++ } ++ kill_anon_super(sb); ++} ++ ++struct file_system_type aufs_fs_type = { ++ .name = AUFS_FSTYPE, ++ /* a race between rename and others */ ++ .fs_flags = FS_RENAME_DOES_D_MOVE, ++ .mount = aufs_mount, ++ .kill_sb = aufs_kill_sb, ++ /* no need to __module_get() and module_put(). */ ++ .owner = THIS_MODULE, ++}; +diff -Nur linux-4.0.4.orig/fs/aufs/super.h linux-4.0.4/fs/aufs/super.h +--- linux-4.0.4.orig/fs/aufs/super.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/super.h 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,626 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * super_block operations ++ */ ++ ++#ifndef __AUFS_SUPER_H__ ++#define __AUFS_SUPER_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "rwsem.h" ++#include "spl.h" ++#include "wkq.h" ++ ++typedef ssize_t (*au_readf_t)(struct file *, char __user *, size_t, loff_t *); ++typedef ssize_t (*au_writef_t)(struct file *, const char __user *, size_t, ++ loff_t *); ++ ++/* policies to select one among multiple writable branches */ ++struct au_wbr_copyup_operations { ++ int (*copyup)(struct dentry *dentry); ++}; ++ ++#define AuWbr_DIR 1 /* target is a dir */ ++#define AuWbr_PARENT (1 << 1) /* always require a parent */ ++ ++#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name) ++#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; } ++#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; } ++ ++struct au_wbr_create_operations { ++ int (*create)(struct dentry *dentry, unsigned int flags); ++ int (*init)(struct super_block *sb); ++ int (*fin)(struct super_block *sb); ++}; ++ ++struct au_wbr_mfs { ++ struct mutex mfs_lock; /* protect this structure */ ++ unsigned long mfs_jiffy; ++ unsigned long mfs_expire; ++ aufs_bindex_t mfs_bindex; ++ ++ unsigned long long mfsrr_bytes; ++ unsigned long long mfsrr_watermark; ++}; ++ ++struct pseudo_link { ++ union { ++ struct hlist_node hlist; ++ struct rcu_head rcu; ++ }; ++ struct inode *inode; ++}; ++ ++#define AuPlink_NHASH 100 ++static inline int au_plink_hash(ino_t ino) ++{ ++ return ino % AuPlink_NHASH; ++} ++ ++/* File-based Hierarchical Storage Management */ ++struct au_fhsm { ++#ifdef CONFIG_AUFS_FHSM ++ /* allow only one process who can receive the notification */ ++ spinlock_t fhsm_spin; ++ pid_t fhsm_pid; ++ wait_queue_head_t fhsm_wqh; ++ atomic_t fhsm_readable; ++ ++ /* these are protected by si_rwsem */ ++ unsigned long fhsm_expire; ++ aufs_bindex_t fhsm_bottom; ++#endif ++}; ++ ++struct au_branch; ++struct au_sbinfo { ++ /* nowait tasks in the system-wide workqueue */ ++ struct au_nowait_tasks si_nowait; ++ ++ /* ++ * tried sb->s_umount, but failed due to the dependecy between i_mutex. ++ * rwsem for au_sbinfo is necessary. ++ */ ++ struct au_rwsem si_rwsem; ++ ++ /* prevent recursive locking in deleting inode */ ++ struct { ++ unsigned long *bitmap; ++ spinlock_t tree_lock; ++ struct radix_tree_root tree; ++ } au_si_pid; ++ ++ /* ++ * dirty approach to protect sb->sb_inodes and ->s_files (gone) from ++ * remount. ++ */ ++ atomic_long_t si_ninodes, si_nfiles; ++ ++ /* branch management */ ++ unsigned int si_generation; ++ ++ /* see AuSi_ flags */ ++ unsigned char au_si_status; ++ ++ aufs_bindex_t si_bend; ++ ++ /* dirty trick to keep br_id plus */ ++ unsigned int si_last_br_id : ++ sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1; ++ struct au_branch **si_branch; ++ ++ /* policy to select a writable branch */ ++ unsigned char si_wbr_copyup; ++ unsigned char si_wbr_create; ++ struct au_wbr_copyup_operations *si_wbr_copyup_ops; ++ struct au_wbr_create_operations *si_wbr_create_ops; ++ ++ /* round robin */ ++ atomic_t si_wbr_rr_next; ++ ++ /* most free space */ ++ struct au_wbr_mfs si_wbr_mfs; ++ ++ /* File-based Hierarchical Storage Management */ ++ struct au_fhsm si_fhsm; ++ ++ /* mount flags */ ++ /* include/asm-ia64/siginfo.h defines a macro named si_flags */ ++ unsigned int si_mntflags; ++ ++ /* external inode number (bitmap and translation table) */ ++ au_readf_t si_xread; ++ au_writef_t si_xwrite; ++ struct file *si_xib; ++ struct mutex si_xib_mtx; /* protect xib members */ ++ unsigned long *si_xib_buf; ++ unsigned long si_xib_last_pindex; ++ int si_xib_next_bit; ++ aufs_bindex_t si_xino_brid; ++ unsigned long si_xino_jiffy; ++ unsigned long si_xino_expire; ++ /* reserved for future use */ ++ /* unsigned long long si_xib_limit; */ /* Max xib file size */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++ /* i_generation */ ++ struct file *si_xigen; ++ atomic_t si_xigen_next; ++#endif ++ ++ /* dirty trick to suppoer atomic_open */ ++ struct au_sphlhead si_aopen; ++ ++ /* vdir parameters */ ++ unsigned long si_rdcache; /* max cache time in jiffies */ ++ unsigned int si_rdblk; /* deblk size */ ++ unsigned int si_rdhash; /* hash size */ ++ ++ /* ++ * If the number of whiteouts are larger than si_dirwh, leave all of ++ * them after au_whtmp_ren to reduce the cost of rmdir(2). ++ * future fsck.aufs or kernel thread will remove them later. ++ * Otherwise, remove all whiteouts and the dir in rmdir(2). ++ */ ++ unsigned int si_dirwh; ++ ++ /* pseudo_link list */ ++ struct au_sphlhead si_plink[AuPlink_NHASH]; ++ wait_queue_head_t si_plink_wq; ++ spinlock_t si_plink_maint_lock; ++ pid_t si_plink_maint_pid; ++ ++ /* file list */ ++ struct au_sphlhead si_files; ++ ++ /* ++ * sysfs and lifetime management. ++ * this is not a small structure and it may be a waste of memory in case ++ * of sysfs is disabled, particulary when many aufs-es are mounted. ++ * but using sysfs is majority. ++ */ ++ struct kobject si_kobj; ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *si_dbgaufs; ++ struct dentry *si_dbgaufs_plink; ++ struct dentry *si_dbgaufs_xib; ++#ifdef CONFIG_AUFS_EXPORT ++ struct dentry *si_dbgaufs_xigen; ++#endif ++#endif ++ ++#ifdef CONFIG_AUFS_SBILIST ++ struct list_head si_list; ++#endif ++ ++ /* dirty, necessary for unmounting, sysfs and sysrq */ ++ struct super_block *si_sb; ++}; ++ ++/* sbinfo status flags */ ++/* ++ * set true when refresh_dirs() failed at remount time. ++ * then try refreshing dirs at access time again. ++ * if it is false, refreshing dirs at access time is unnecesary ++ */ ++#define AuSi_FAILED_REFRESH_DIR 1 ++ ++#define AuSi_FHSM (1 << 1) /* fhsm is active now */ ++ ++#ifndef CONFIG_AUFS_FHSM ++#undef AuSi_FHSM ++#define AuSi_FHSM 0 ++#endif ++ ++static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, ++ unsigned int flag) ++{ ++ AuRwMustAnyLock(&sbi->si_rwsem); ++ return sbi->au_si_status & flag; ++} ++#define au_ftest_si(sbinfo, name) au_do_ftest_si(sbinfo, AuSi_##name) ++#define au_fset_si(sbinfo, name) do { \ ++ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ ++ (sbinfo)->au_si_status |= AuSi_##name; \ ++} while (0) ++#define au_fclr_si(sbinfo, name) do { \ ++ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ ++ (sbinfo)->au_si_status &= ~AuSi_##name; \ ++} while (0) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policy to select one among writable branches */ ++#define AuWbrCopyup(sbinfo, ...) \ ++ ((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__)) ++#define AuWbrCreate(sbinfo, ...) \ ++ ((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__)) ++ ++/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ ++#define AuLock_DW 1 /* write-lock dentry */ ++#define AuLock_IR (1 << 1) /* read-lock inode */ ++#define AuLock_IW (1 << 2) /* write-lock inode */ ++#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ ++#define AuLock_DIR (1 << 4) /* target is a dir */ ++#define AuLock_NOPLM (1 << 5) /* return err in plm mode */ ++#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */ ++#define AuLock_GEN (1 << 7) /* test digen/iigen */ ++#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) ++#define au_fset_lock(flags, name) \ ++ do { (flags) |= AuLock_##name; } while (0) ++#define au_fclr_lock(flags, name) \ ++ do { (flags) &= ~AuLock_##name; } while (0) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* super.c */ ++extern struct file_system_type aufs_fs_type; ++struct inode *au_iget_locked(struct super_block *sb, ino_t ino); ++typedef unsigned long long (*au_arraycb_t)(void *array, unsigned long long max, ++ void *arg); ++void au_array_free(void *array); ++void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg); ++struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max); ++void au_iarray_free(struct inode **a, unsigned long long max); ++ ++/* sbinfo.c */ ++void au_si_free(struct kobject *kobj); ++int au_si_alloc(struct super_block *sb); ++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr); ++ ++unsigned int au_sigen_inc(struct super_block *sb); ++aufs_bindex_t au_new_br_id(struct super_block *sb); ++ ++int si_read_lock(struct super_block *sb, int flags); ++int si_write_lock(struct super_block *sb, int flags); ++int aufs_read_lock(struct dentry *dentry, int flags); ++void aufs_read_unlock(struct dentry *dentry, int flags); ++void aufs_write_lock(struct dentry *dentry); ++void aufs_write_unlock(struct dentry *dentry); ++int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); ++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); ++ ++int si_pid_test_slow(struct super_block *sb); ++void si_pid_set_slow(struct super_block *sb); ++void si_pid_clr_slow(struct super_block *sb); ++ ++/* wbr_policy.c */ ++extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; ++extern struct au_wbr_create_operations au_wbr_create_ops[]; ++int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst); ++int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex); ++int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart); ++ ++/* mvdown.c */ ++int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *arg); ++ ++#ifdef CONFIG_AUFS_FHSM ++/* fhsm.c */ ++ ++static inline pid_t au_fhsm_pid(struct au_fhsm *fhsm) ++{ ++ pid_t pid; ++ ++ spin_lock(&fhsm->fhsm_spin); ++ pid = fhsm->fhsm_pid; ++ spin_unlock(&fhsm->fhsm_spin); ++ ++ return pid; ++} ++ ++void au_fhsm_wrote(struct super_block *sb, aufs_bindex_t bindex, int force); ++void au_fhsm_wrote_all(struct super_block *sb, int force); ++int au_fhsm_fd(struct super_block *sb, int oflags); ++int au_fhsm_br_alloc(struct au_branch *br); ++void au_fhsm_set_bottom(struct super_block *sb, aufs_bindex_t bindex); ++void au_fhsm_fin(struct super_block *sb); ++void au_fhsm_init(struct au_sbinfo *sbinfo); ++void au_fhsm_set(struct au_sbinfo *sbinfo, unsigned int sec); ++void au_fhsm_show(struct seq_file *seq, struct au_sbinfo *sbinfo); ++#else ++AuStubVoid(au_fhsm_wrote, struct super_block *sb, aufs_bindex_t bindex, ++ int force) ++AuStubVoid(au_fhsm_wrote_all, struct super_block *sb, int force) ++AuStub(int, au_fhsm_fd, return -EOPNOTSUPP, struct super_block *sb, int oflags) ++AuStub(pid_t, au_fhsm_pid, return 0, struct au_fhsm *fhsm) ++AuStubInt0(au_fhsm_br_alloc, struct au_branch *br) ++AuStubVoid(au_fhsm_set_bottom, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(au_fhsm_fin, struct super_block *sb) ++AuStubVoid(au_fhsm_init, struct au_sbinfo *sbinfo) ++AuStubVoid(au_fhsm_set, struct au_sbinfo *sbinfo, unsigned int sec) ++AuStubVoid(au_fhsm_show, struct seq_file *seq, struct au_sbinfo *sbinfo) ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_sbinfo *au_sbi(struct super_block *sb) ++{ ++ return sb->s_fs_info; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++int au_test_nfsd(void); ++void au_export_init(struct super_block *sb); ++void au_xigen_inc(struct inode *inode); ++int au_xigen_new(struct inode *inode); ++int au_xigen_set(struct super_block *sb, struct file *base); ++void au_xigen_clr(struct super_block *sb); ++ ++static inline int au_busy_or_stale(void) ++{ ++ if (!au_test_nfsd()) ++ return -EBUSY; ++ return -ESTALE; ++} ++#else ++AuStubInt0(au_test_nfsd, void) ++AuStubVoid(au_export_init, struct super_block *sb) ++AuStubVoid(au_xigen_inc, struct inode *inode) ++AuStubInt0(au_xigen_new, struct inode *inode) ++AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base) ++AuStubVoid(au_xigen_clr, struct super_block *sb) ++AuStub(int, au_busy_or_stale, return -EBUSY, void) ++#endif /* CONFIG_AUFS_EXPORT */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_SBILIST ++/* module.c */ ++extern struct au_splhead au_sbilist; ++ ++static inline void au_sbilist_init(void) ++{ ++ au_spl_init(&au_sbilist); ++} ++ ++static inline void au_sbilist_add(struct super_block *sb) ++{ ++ au_spl_add(&au_sbi(sb)->si_list, &au_sbilist); ++} ++ ++static inline void au_sbilist_del(struct super_block *sb) ++{ ++ au_spl_del(&au_sbi(sb)->si_list, &au_sbilist); ++} ++ ++#ifdef CONFIG_AUFS_MAGIC_SYSRQ ++static inline void au_sbilist_lock(void) ++{ ++ spin_lock(&au_sbilist.spin); ++} ++ ++static inline void au_sbilist_unlock(void) ++{ ++ spin_unlock(&au_sbilist.spin); ++} ++#define AuGFP_SBILIST GFP_ATOMIC ++#else ++AuStubVoid(au_sbilist_lock, void) ++AuStubVoid(au_sbilist_unlock, void) ++#define AuGFP_SBILIST GFP_NOFS ++#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ ++#else ++AuStubVoid(au_sbilist_init, void) ++AuStubVoid(au_sbilist_add, struct super_block *sb) ++AuStubVoid(au_sbilist_del, struct super_block *sb) ++AuStubVoid(au_sbilist_lock, void) ++AuStubVoid(au_sbilist_unlock, void) ++#define AuGFP_SBILIST GFP_NOFS ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) ++{ ++ /* ++ * This function is a dynamic '__init' function actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++#ifdef CONFIG_DEBUG_FS ++ sbinfo->si_dbgaufs = NULL; ++ sbinfo->si_dbgaufs_plink = NULL; ++ sbinfo->si_dbgaufs_xib = NULL; ++#ifdef CONFIG_AUFS_EXPORT ++ sbinfo->si_dbgaufs_xigen = NULL; ++#endif ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline pid_t si_pid_bit(void) ++{ ++ /* the origin of pid is 1, but the bitmap's is 0 */ ++ return current->pid - 1; ++} ++ ++static inline int si_pid_test(struct super_block *sb) ++{ ++ pid_t bit; ++ ++ bit = si_pid_bit(); ++ if (bit < PID_MAX_DEFAULT) ++ return test_bit(bit, au_sbi(sb)->au_si_pid.bitmap); ++ return si_pid_test_slow(sb); ++} ++ ++static inline void si_pid_set(struct super_block *sb) ++{ ++ pid_t bit; ++ ++ bit = si_pid_bit(); ++ if (bit < PID_MAX_DEFAULT) { ++ AuDebugOn(test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); ++ set_bit(bit, au_sbi(sb)->au_si_pid.bitmap); ++ /* smp_mb(); */ ++ } else ++ si_pid_set_slow(sb); ++} ++ ++static inline void si_pid_clr(struct super_block *sb) ++{ ++ pid_t bit; ++ ++ bit = si_pid_bit(); ++ if (bit < PID_MAX_DEFAULT) { ++ AuDebugOn(!test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); ++ clear_bit(bit, au_sbi(sb)->au_si_pid.bitmap); ++ /* smp_mb(); */ ++ } else ++ si_pid_clr_slow(sb); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock superblock. mainly for entry point functions */ ++/* ++ * __si_read_lock, __si_write_lock, ++ * __si_read_unlock, __si_write_unlock, __si_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(__si, struct super_block *sb, &au_sbi(sb)->si_rwsem); ++ ++#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem) ++#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) ++#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem) ++ ++static inline void si_noflush_read_lock(struct super_block *sb) ++{ ++ __si_read_lock(sb); ++ si_pid_set(sb); ++} ++ ++static inline int si_noflush_read_trylock(struct super_block *sb) ++{ ++ int locked; ++ ++ locked = __si_read_trylock(sb); ++ if (locked) ++ si_pid_set(sb); ++ return locked; ++} ++ ++static inline void si_noflush_write_lock(struct super_block *sb) ++{ ++ __si_write_lock(sb); ++ si_pid_set(sb); ++} ++ ++static inline int si_noflush_write_trylock(struct super_block *sb) ++{ ++ int locked; ++ ++ locked = __si_write_trylock(sb); ++ if (locked) ++ si_pid_set(sb); ++ return locked; ++} ++ ++#if 0 /* reserved */ ++static inline int si_read_trylock(struct super_block *sb, int flags) ++{ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ return si_noflush_read_trylock(sb); ++} ++#endif ++ ++static inline void si_read_unlock(struct super_block *sb) ++{ ++ si_pid_clr(sb); ++ __si_read_unlock(sb); ++} ++ ++#if 0 /* reserved */ ++static inline int si_write_trylock(struct super_block *sb, int flags) ++{ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ return si_noflush_write_trylock(sb); ++} ++#endif ++ ++static inline void si_write_unlock(struct super_block *sb) ++{ ++ si_pid_clr(sb); ++ __si_write_unlock(sb); ++} ++ ++#if 0 /* reserved */ ++static inline void si_downgrade_lock(struct super_block *sb) ++{ ++ __si_downgrade_lock(sb); ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline aufs_bindex_t au_sbend(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_bend; ++} ++ ++static inline unsigned int au_mntflags(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_mntflags; ++} ++ ++static inline unsigned int au_sigen(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_generation; ++} ++ ++static inline void au_ninodes_inc(struct super_block *sb) ++{ ++ atomic_long_inc(&au_sbi(sb)->si_ninodes); ++} ++ ++static inline void au_ninodes_dec(struct super_block *sb) ++{ ++ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_ninodes)); ++ atomic_long_dec(&au_sbi(sb)->si_ninodes); ++} ++ ++static inline void au_nfiles_inc(struct super_block *sb) ++{ ++ atomic_long_inc(&au_sbi(sb)->si_nfiles); ++} ++ ++static inline void au_nfiles_dec(struct super_block *sb) ++{ ++ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_nfiles)); ++ atomic_long_dec(&au_sbi(sb)->si_nfiles); ++} ++ ++static inline struct au_branch *au_sbr(struct super_block *sb, ++ aufs_bindex_t bindex) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_branch[0 + bindex]; ++} ++ ++static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid) ++{ ++ SiMustWriteLock(sb); ++ au_sbi(sb)->si_xino_brid = brid; ++} ++ ++static inline aufs_bindex_t au_xino_brid(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_xino_brid; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_SUPER_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/sysaufs.c linux-4.0.4/fs/aufs/sysaufs.c +--- linux-4.0.4.orig/fs/aufs/sysaufs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/sysaufs.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,91 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sysfs interface and lifetime management ++ * they are necessary regardless sysfs is disabled. ++ */ ++ ++#include ++#include "aufs.h" ++ ++unsigned long sysaufs_si_mask; ++struct kset *sysaufs_kset; ++ ++#define AuSiAttr(_name) { \ ++ .attr = { .name = __stringify(_name), .mode = 0444 }, \ ++ .show = sysaufs_si_##_name, \ ++} ++ ++static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path); ++struct attribute *sysaufs_si_attrs[] = { ++ &sysaufs_si_attr_xi_path.attr, ++ NULL, ++}; ++ ++static const struct sysfs_ops au_sbi_ops = { ++ .show = sysaufs_si_show ++}; ++ ++static struct kobj_type au_sbi_ktype = { ++ .release = au_si_free, ++ .sysfs_ops = &au_sbi_ops, ++ .default_attrs = sysaufs_si_attrs ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int sysaufs_si_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ ++ sbinfo->si_kobj.kset = sysaufs_kset; ++ /* cf. sysaufs_name() */ ++ err = kobject_init_and_add ++ (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL, ++ SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); ++ ++ dbgaufs_si_null(sbinfo); ++ if (!err) { ++ err = dbgaufs_si_init(sbinfo); ++ if (unlikely(err)) ++ kobject_put(&sbinfo->si_kobj); ++ } ++ return err; ++} ++ ++void sysaufs_fin(void) ++{ ++ dbgaufs_fin(); ++ sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group); ++ kset_unregister(sysaufs_kset); ++} ++ ++int __init sysaufs_init(void) ++{ ++ int err; ++ ++ do { ++ get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask)); ++ } while (!sysaufs_si_mask); ++ ++ err = -EINVAL; ++ sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj); ++ if (unlikely(!sysaufs_kset)) ++ goto out; ++ err = PTR_ERR(sysaufs_kset); ++ if (IS_ERR(sysaufs_kset)) ++ goto out; ++ err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group); ++ if (unlikely(err)) { ++ kset_unregister(sysaufs_kset); ++ goto out; ++ } ++ ++ err = dbgaufs_init(); ++ if (unlikely(err)) ++ sysaufs_fin(); ++out: ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/sysaufs.h linux-4.0.4/fs/aufs/sysaufs.h +--- linux-4.0.4.orig/fs/aufs/sysaufs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/sysaufs.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,88 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sysfs interface and mount lifetime management ++ */ ++ ++#ifndef __SYSAUFS_H__ ++#define __SYSAUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "module.h" ++ ++struct super_block; ++struct au_sbinfo; ++ ++struct sysaufs_si_attr { ++ struct attribute attr; ++ int (*show)(struct seq_file *seq, struct super_block *sb); ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* sysaufs.c */ ++extern unsigned long sysaufs_si_mask; ++extern struct kset *sysaufs_kset; ++extern struct attribute *sysaufs_si_attrs[]; ++int sysaufs_si_init(struct au_sbinfo *sbinfo); ++int __init sysaufs_init(void); ++void sysaufs_fin(void); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* some people doesn't like to show a pointer in kernel */ ++static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo) ++{ ++ return sysaufs_si_mask ^ (unsigned long)sbinfo; ++} ++ ++#define SysaufsSiNamePrefix "si_" ++#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16) ++static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name) ++{ ++ snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx", ++ sysaufs_si_id(sbinfo)); ++} ++ ++struct au_branch; ++#ifdef CONFIG_SYSFS ++/* sysfs.c */ ++extern struct attribute_group *sysaufs_attr_group; ++ ++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb); ++ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, ++ char *buf); ++long au_brinfo_ioctl(struct file *file, unsigned long arg); ++#ifdef CONFIG_COMPAT ++long au_brinfo_compat_ioctl(struct file *file, unsigned long arg); ++#endif ++ ++void sysaufs_br_init(struct au_branch *br); ++void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); ++void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); ++ ++#define sysaufs_brs_init() do {} while (0) ++ ++#else ++#define sysaufs_attr_group NULL ++ ++AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb) ++AuStub(ssize_t, sysaufs_si_show, return 0, struct kobject *kobj, ++ struct attribute *attr, char *buf) ++AuStubVoid(sysaufs_br_init, struct au_branch *br) ++AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(sysaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) ++ ++static inline void sysaufs_brs_init(void) ++{ ++ sysaufs_brs = 0; ++} ++ ++#endif /* CONFIG_SYSFS */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __SYSAUFS_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/sysfs.c linux-4.0.4/fs/aufs/sysfs.c +--- linux-4.0.4.orig/fs/aufs/sysfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/sysfs.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,336 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sysfs interface ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++static struct attribute *au_attr[] = { ++ NULL, /* need to NULL terminate the list of attributes */ ++}; ++ ++static struct attribute_group sysaufs_attr_group_body = { ++ .attrs = au_attr ++}; ++ ++struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) ++{ ++ int err; ++ ++ SiMustAnyLock(sb); ++ ++ err = 0; ++ if (au_opt_test(au_mntflags(sb), XINO)) { ++ err = au_xino_path(seq, au_sbi(sb)->si_xib); ++ seq_putc(seq, '\n'); ++ } ++ return err; ++} ++ ++/* ++ * the lifetime of branch is independent from the entry under sysfs. ++ * sysfs handles the lifetime of the entry, and never call ->show() after it is ++ * unlinked. ++ */ ++static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, ++ aufs_bindex_t bindex, int idx) ++{ ++ int err; ++ struct path path; ++ struct dentry *root; ++ struct au_branch *br; ++ au_br_perm_str_t perm; ++ ++ AuDbg("b%d\n", bindex); ++ ++ err = 0; ++ root = sb->s_root; ++ di_read_lock_parent(root, !AuLock_IR); ++ br = au_sbr(sb, bindex); ++ ++ switch (idx) { ++ case AuBrSysfs_BR: ++ path.mnt = au_br_mnt(br); ++ path.dentry = au_h_dptr(root, bindex); ++ au_seq_path(seq, &path); ++ au_optstr_br_perm(&perm, br->br_perm); ++ err = seq_printf(seq, "=%s\n", perm.a); ++ break; ++ case AuBrSysfs_BRID: ++ err = seq_printf(seq, "%d\n", br->br_id); ++ break; ++ } ++ di_read_unlock(root, !AuLock_IR); ++ if (err == -1) ++ err = -E2BIG; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct seq_file *au_seq(char *p, ssize_t len) ++{ ++ struct seq_file *seq; ++ ++ seq = kzalloc(sizeof(*seq), GFP_NOFS); ++ if (seq) { ++ /* mutex_init(&seq.lock); */ ++ seq->buf = p; ++ seq->size = len; ++ return seq; /* success */ ++ } ++ ++ seq = ERR_PTR(-ENOMEM); ++ return seq; ++} ++ ++#define SysaufsBr_PREFIX "br" ++#define SysaufsBrid_PREFIX "brid" ++ ++/* todo: file size may exceed PAGE_SIZE */ ++ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, ++ char *buf) ++{ ++ ssize_t err; ++ int idx; ++ long l; ++ aufs_bindex_t bend; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct seq_file *seq; ++ char *name; ++ struct attribute **cattr; ++ ++ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); ++ sb = sbinfo->si_sb; ++ ++ /* ++ * prevent a race condition between sysfs and aufs. ++ * for instance, sysfs_file_read() calls sysfs_get_active_two() which ++ * prohibits maintaining the sysfs entries. ++ * hew we acquire read lock after sysfs_get_active_two(). ++ * on the other hand, the remount process may maintain the sysfs/aufs ++ * entries after acquiring write lock. ++ * it can cause a deadlock. ++ * simply we gave up processing read here. ++ */ ++ err = -EBUSY; ++ if (unlikely(!si_noflush_read_trylock(sb))) ++ goto out; ++ ++ seq = au_seq(buf, PAGE_SIZE); ++ err = PTR_ERR(seq); ++ if (IS_ERR(seq)) ++ goto out_unlock; ++ ++ name = (void *)attr->name; ++ cattr = sysaufs_si_attrs; ++ while (*cattr) { ++ if (!strcmp(name, (*cattr)->name)) { ++ err = container_of(*cattr, struct sysaufs_si_attr, attr) ++ ->show(seq, sb); ++ goto out_seq; ++ } ++ cattr++; ++ } ++ ++ if (!strncmp(name, SysaufsBrid_PREFIX, ++ sizeof(SysaufsBrid_PREFIX) - 1)) { ++ idx = AuBrSysfs_BRID; ++ name += sizeof(SysaufsBrid_PREFIX) - 1; ++ } else if (!strncmp(name, SysaufsBr_PREFIX, ++ sizeof(SysaufsBr_PREFIX) - 1)) { ++ idx = AuBrSysfs_BR; ++ name += sizeof(SysaufsBr_PREFIX) - 1; ++ } else ++ BUG(); ++ ++ err = kstrtol(name, 10, &l); ++ if (!err) { ++ bend = au_sbend(sb); ++ if (l <= bend) ++ err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); ++ else ++ err = -ENOENT; ++ } ++ ++out_seq: ++ if (!err) { ++ err = seq->count; ++ /* sysfs limit */ ++ if (unlikely(err == PAGE_SIZE)) ++ err = -EFBIG; ++ } ++ kfree(seq); ++out_unlock: ++ si_read_unlock(sb); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg) ++{ ++ int err; ++ int16_t brid; ++ aufs_bindex_t bindex, bend; ++ size_t sz; ++ char *buf; ++ struct seq_file *seq; ++ struct au_branch *br; ++ ++ si_read_lock(sb, AuLock_FLUSH); ++ bend = au_sbend(sb); ++ err = bend + 1; ++ if (!arg) ++ goto out; ++ ++ err = -ENOMEM; ++ buf = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!buf)) ++ goto out; ++ ++ seq = au_seq(buf, PAGE_SIZE); ++ err = PTR_ERR(seq); ++ if (IS_ERR(seq)) ++ goto out_buf; ++ ++ sz = sizeof(*arg) - offsetof(union aufs_brinfo, path); ++ for (bindex = 0; bindex <= bend; bindex++, arg++) { ++ err = !access_ok(VERIFY_WRITE, arg, sizeof(*arg)); ++ if (unlikely(err)) ++ break; ++ ++ br = au_sbr(sb, bindex); ++ brid = br->br_id; ++ BUILD_BUG_ON(sizeof(brid) != sizeof(arg->id)); ++ err = __put_user(brid, &arg->id); ++ if (unlikely(err)) ++ break; ++ ++ BUILD_BUG_ON(sizeof(br->br_perm) != sizeof(arg->perm)); ++ err = __put_user(br->br_perm, &arg->perm); ++ if (unlikely(err)) ++ break; ++ ++ au_seq_path(seq, &br->br_path); ++ err = seq_putc(seq, '\0'); ++ if (!err && seq->count <= sz) { ++ err = copy_to_user(arg->path, seq->buf, seq->count); ++ seq->count = 0; ++ if (unlikely(err)) ++ break; ++ } else { ++ err = -E2BIG; ++ goto out_seq; ++ } ++ } ++ if (unlikely(err)) ++ err = -EFAULT; ++ ++out_seq: ++ kfree(seq); ++out_buf: ++ free_page((unsigned long)buf); ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++long au_brinfo_ioctl(struct file *file, unsigned long arg) ++{ ++ return au_brinfo(file->f_path.dentry->d_sb, (void __user *)arg); ++} ++ ++#ifdef CONFIG_COMPAT ++long au_brinfo_compat_ioctl(struct file *file, unsigned long arg) ++{ ++ return au_brinfo(file->f_path.dentry->d_sb, compat_ptr(arg)); ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++void sysaufs_br_init(struct au_branch *br) ++{ ++ int i; ++ struct au_brsysfs *br_sysfs; ++ struct attribute *attr; ++ ++ br_sysfs = br->br_sysfs; ++ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { ++ attr = &br_sysfs->attr; ++ sysfs_attr_init(attr); ++ attr->name = br_sysfs->name; ++ attr->mode = S_IRUGO; ++ br_sysfs++; ++ } ++} ++ ++void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ struct au_branch *br; ++ struct kobject *kobj; ++ struct au_brsysfs *br_sysfs; ++ int i; ++ aufs_bindex_t bend; ++ ++ dbgaufs_brs_del(sb, bindex); ++ ++ if (!sysaufs_brs) ++ return; ++ ++ kobj = &au_sbi(sb)->si_kobj; ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ br_sysfs = br->br_sysfs; ++ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { ++ sysfs_remove_file(kobj, &br_sysfs->attr); ++ br_sysfs++; ++ } ++ } ++} ++ ++void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err, i; ++ aufs_bindex_t bend; ++ struct kobject *kobj; ++ struct au_branch *br; ++ struct au_brsysfs *br_sysfs; ++ ++ dbgaufs_brs_add(sb, bindex); ++ ++ if (!sysaufs_brs) ++ return; ++ ++ kobj = &au_sbi(sb)->si_kobj; ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ br_sysfs = br->br_sysfs; ++ snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), ++ SysaufsBr_PREFIX "%d", bindex); ++ snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name), ++ SysaufsBrid_PREFIX "%d", bindex); ++ for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { ++ err = sysfs_create_file(kobj, &br_sysfs->attr); ++ if (unlikely(err)) ++ pr_warn("failed %s under sysfs(%d)\n", ++ br_sysfs->name, err); ++ br_sysfs++; ++ } ++ } ++} +diff -Nur linux-4.0.4.orig/fs/aufs/sysrq.c linux-4.0.4/fs/aufs/sysrq.c +--- linux-4.0.4.orig/fs/aufs/sysrq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/sysrq.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,144 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * magic sysrq hanlder ++ */ ++ ++/* #include */ ++#include ++#include "aufs.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void sysrq_sb(struct super_block *sb) ++{ ++ char *plevel; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ struct au_sphlhead *files; ++ struct au_finfo *finfo; ++ ++ plevel = au_plevel; ++ au_plevel = KERN_WARNING; ++ ++ /* since we define pr_fmt, call printk directly */ ++#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str) ++ ++ sbinfo = au_sbi(sb); ++ printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); ++ pr("superblock\n"); ++ au_dpri_sb(sb); ++ ++#if 0 ++ pr("root dentry\n"); ++ au_dpri_dentry(sb->s_root); ++ pr("root inode\n"); ++ au_dpri_inode(sb->s_root->d_inode); ++#endif ++ ++#if 0 ++ do { ++ int err, i, j, ndentry; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ ++ err = au_dpages_init(&dpages, GFP_ATOMIC); ++ if (unlikely(err)) ++ break; ++ err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL); ++ if (!err) ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) ++ au_dpri_dentry(dpage->dentries[j]); ++ } ++ au_dpages_free(&dpages); ++ } while (0); ++#endif ++ ++#if 1 ++ { ++ struct inode *i; ++ ++ pr("isolated inode\n"); ++ spin_lock(&inode_sb_list_lock); ++ list_for_each_entry(i, &sb->s_inodes, i_sb_list) { ++ spin_lock(&i->i_lock); ++ if (1 || hlist_empty(&i->i_dentry)) ++ au_dpri_inode(i); ++ spin_unlock(&i->i_lock); ++ } ++ spin_unlock(&inode_sb_list_lock); ++ } ++#endif ++ pr("files\n"); ++ files = &au_sbi(sb)->si_files; ++ spin_lock(&files->spin); ++ hlist_for_each_entry(finfo, &files->head, fi_hlist) { ++ umode_t mode; ++ ++ file = finfo->fi_file; ++ mode = file_inode(file)->i_mode; ++ if (!special_file(mode)) ++ au_dpri_file(file); ++ } ++ spin_unlock(&files->spin); ++ pr("done\n"); ++ ++#undef pr ++ au_plevel = plevel; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* module parameter */ ++static char *aufs_sysrq_key = "a"; ++module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO); ++MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); ++ ++static void au_sysrq(int key __maybe_unused) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ lockdep_off(); ++ au_sbilist_lock(); ++ list_for_each_entry(sbinfo, &au_sbilist.head, si_list) ++ sysrq_sb(sbinfo->si_sb); ++ au_sbilist_unlock(); ++ lockdep_on(); ++} ++ ++static struct sysrq_key_op au_sysrq_op = { ++ .handler = au_sysrq, ++ .help_msg = "Aufs", ++ .action_msg = "Aufs", ++ .enable_mask = SYSRQ_ENABLE_DUMP ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int __init au_sysrq_init(void) ++{ ++ int err; ++ char key; ++ ++ err = -1; ++ key = *aufs_sysrq_key; ++ if ('a' <= key && key <= 'z') ++ err = register_sysrq_key(key, &au_sysrq_op); ++ if (unlikely(err)) ++ pr_err("err %d, sysrq=%c\n", err, key); ++ return err; ++} ++ ++void au_sysrq_fin(void) ++{ ++ int err; ++ ++ err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op); ++ if (unlikely(err)) ++ pr_err("err %d (ignored)\n", err); ++} +diff -Nur linux-4.0.4.orig/fs/aufs/vdir.c linux-4.0.4/fs/aufs/vdir.c +--- linux-4.0.4.orig/fs/aufs/vdir.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/vdir.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,876 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * virtual or vertical directory ++ */ ++ ++#include "aufs.h" ++ ++static unsigned int calc_size(int nlen) ++{ ++ return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); ++} ++ ++static int set_deblk_end(union au_vdir_deblk_p *p, ++ union au_vdir_deblk_p *deblk_end) ++{ ++ if (calc_size(0) <= deblk_end->deblk - p->deblk) { ++ p->de->de_str.len = 0; ++ /* smp_mb(); */ ++ return 0; ++ } ++ return -1; /* error */ ++} ++ ++/* returns true or false */ ++static int is_deblk_end(union au_vdir_deblk_p *p, ++ union au_vdir_deblk_p *deblk_end) ++{ ++ if (calc_size(0) <= deblk_end->deblk - p->deblk) ++ return !p->de->de_str.len; ++ return 1; ++} ++ ++static unsigned char *last_deblk(struct au_vdir *vdir) ++{ ++ return vdir->vd_deblk[vdir->vd_nblk - 1]; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* estimate the apropriate size for name hash table */ ++unsigned int au_rdhash_est(loff_t sz) ++{ ++ unsigned int n; ++ ++ n = UINT_MAX; ++ sz >>= 10; ++ if (sz < n) ++ n = sz; ++ if (sz < AUFS_RDHASH_DEF) ++ n = AUFS_RDHASH_DEF; ++ /* pr_info("n %u\n", n); */ ++ return n; ++} ++ ++/* ++ * the allocated memory has to be freed by ++ * au_nhash_wh_free() or au_nhash_de_free(). ++ */ ++int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) ++{ ++ struct hlist_head *head; ++ unsigned int u; ++ size_t sz; ++ ++ sz = sizeof(*nhash->nh_head) * num_hash; ++ head = kmalloc(sz, gfp); ++ if (head) { ++ nhash->nh_num = num_hash; ++ nhash->nh_head = head; ++ for (u = 0; u < num_hash; u++) ++ INIT_HLIST_HEAD(head++); ++ return 0; /* success */ ++ } ++ ++ return -ENOMEM; ++} ++ ++static void nhash_count(struct hlist_head *head) ++{ ++#if 0 ++ unsigned long n; ++ struct hlist_node *pos; ++ ++ n = 0; ++ hlist_for_each(pos, head) ++ n++; ++ pr_info("%lu\n", n); ++#endif ++} ++ ++static void au_nhash_wh_do_free(struct hlist_head *head) ++{ ++ struct au_vdir_wh *pos; ++ struct hlist_node *node; ++ ++ hlist_for_each_entry_safe(pos, node, head, wh_hash) ++ kfree(pos); ++} ++ ++static void au_nhash_de_do_free(struct hlist_head *head) ++{ ++ struct au_vdir_dehstr *pos; ++ struct hlist_node *node; ++ ++ hlist_for_each_entry_safe(pos, node, head, hash) ++ au_cache_free_vdir_dehstr(pos); ++} ++ ++static void au_nhash_do_free(struct au_nhash *nhash, ++ void (*free)(struct hlist_head *head)) ++{ ++ unsigned int n; ++ struct hlist_head *head; ++ ++ n = nhash->nh_num; ++ if (!n) ++ return; ++ ++ head = nhash->nh_head; ++ while (n-- > 0) { ++ nhash_count(head); ++ free(head++); ++ } ++ kfree(nhash->nh_head); ++} ++ ++void au_nhash_wh_free(struct au_nhash *whlist) ++{ ++ au_nhash_do_free(whlist, au_nhash_wh_do_free); ++} ++ ++static void au_nhash_de_free(struct au_nhash *delist) ++{ ++ au_nhash_do_free(delist, au_nhash_de_do_free); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, ++ int limit) ++{ ++ int num; ++ unsigned int u, n; ++ struct hlist_head *head; ++ struct au_vdir_wh *pos; ++ ++ num = 0; ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (u = 0; u < n; u++, head++) ++ hlist_for_each_entry(pos, head, wh_hash) ++ if (pos->wh_bindex == btgt && ++num > limit) ++ return 1; ++ return 0; ++} ++ ++static struct hlist_head *au_name_hash(struct au_nhash *nhash, ++ unsigned char *name, ++ unsigned int len) ++{ ++ unsigned int v; ++ /* const unsigned int magic_bit = 12; */ ++ ++ AuDebugOn(!nhash->nh_num || !nhash->nh_head); ++ ++ v = 0; ++ while (len--) ++ v += *name++; ++ /* v = hash_long(v, magic_bit); */ ++ v %= nhash->nh_num; ++ return nhash->nh_head + v; ++} ++ ++static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, ++ int nlen) ++{ ++ return str->len == nlen && !memcmp(str->name, name, nlen); ++} ++ ++/* returns found or not */ ++int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) ++{ ++ struct hlist_head *head; ++ struct au_vdir_wh *pos; ++ struct au_vdir_destr *str; ++ ++ head = au_name_hash(whlist, name, nlen); ++ hlist_for_each_entry(pos, head, wh_hash) { ++ str = &pos->wh_str; ++ AuDbg("%.*s\n", str->len, str->name); ++ if (au_nhash_test_name(str, name, nlen)) ++ return 1; ++ } ++ return 0; ++} ++ ++/* returns found(true) or not */ ++static int test_known(struct au_nhash *delist, char *name, int nlen) ++{ ++ struct hlist_head *head; ++ struct au_vdir_dehstr *pos; ++ struct au_vdir_destr *str; ++ ++ head = au_name_hash(delist, name, nlen); ++ hlist_for_each_entry(pos, head, hash) { ++ str = pos->str; ++ AuDbg("%.*s\n", str->len, str->name); ++ if (au_nhash_test_name(str, name, nlen)) ++ return 1; ++ } ++ return 0; ++} ++ ++static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, ++ unsigned char d_type) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ wh->wh_ino = ino; ++ wh->wh_type = d_type; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, ++ unsigned int d_type, aufs_bindex_t bindex, ++ unsigned char shwh) ++{ ++ int err; ++ struct au_vdir_destr *str; ++ struct au_vdir_wh *wh; ++ ++ AuDbg("%.*s\n", nlen, name); ++ AuDebugOn(!whlist->nh_num || !whlist->nh_head); ++ ++ err = -ENOMEM; ++ wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); ++ if (unlikely(!wh)) ++ goto out; ++ ++ err = 0; ++ wh->wh_bindex = bindex; ++ if (shwh) ++ au_shwh_init_wh(wh, ino, d_type); ++ str = &wh->wh_str; ++ str->len = nlen; ++ memcpy(str->name, name, nlen); ++ hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); ++ /* smp_mb(); */ ++ ++out: ++ return err; ++} ++ ++static int append_deblk(struct au_vdir *vdir) ++{ ++ int err; ++ unsigned long ul; ++ const unsigned int deblk_sz = vdir->vd_deblk_sz; ++ union au_vdir_deblk_p p, deblk_end; ++ unsigned char **o; ++ ++ err = -ENOMEM; ++ o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), ++ GFP_NOFS); ++ if (unlikely(!o)) ++ goto out; ++ ++ vdir->vd_deblk = o; ++ p.deblk = kmalloc(deblk_sz, GFP_NOFS); ++ if (p.deblk) { ++ ul = vdir->vd_nblk++; ++ vdir->vd_deblk[ul] = p.deblk; ++ vdir->vd_last.ul = ul; ++ vdir->vd_last.p.deblk = p.deblk; ++ deblk_end.deblk = p.deblk + deblk_sz; ++ err = set_deblk_end(&p, &deblk_end); ++ } ++ ++out: ++ return err; ++} ++ ++static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, ++ unsigned int d_type, struct au_nhash *delist) ++{ ++ int err; ++ unsigned int sz; ++ const unsigned int deblk_sz = vdir->vd_deblk_sz; ++ union au_vdir_deblk_p p, *room, deblk_end; ++ struct au_vdir_dehstr *dehstr; ++ ++ p.deblk = last_deblk(vdir); ++ deblk_end.deblk = p.deblk + deblk_sz; ++ room = &vdir->vd_last.p; ++ AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk ++ || !is_deblk_end(room, &deblk_end)); ++ ++ sz = calc_size(nlen); ++ if (unlikely(sz > deblk_end.deblk - room->deblk)) { ++ err = append_deblk(vdir); ++ if (unlikely(err)) ++ goto out; ++ ++ p.deblk = last_deblk(vdir); ++ deblk_end.deblk = p.deblk + deblk_sz; ++ /* smp_mb(); */ ++ AuDebugOn(room->deblk != p.deblk); ++ } ++ ++ err = -ENOMEM; ++ dehstr = au_cache_alloc_vdir_dehstr(); ++ if (unlikely(!dehstr)) ++ goto out; ++ ++ dehstr->str = &room->de->de_str; ++ hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); ++ room->de->de_ino = ino; ++ room->de->de_type = d_type; ++ room->de->de_str.len = nlen; ++ memcpy(room->de->de_str.name, name, nlen); ++ ++ err = 0; ++ room->deblk += sz; ++ if (unlikely(set_deblk_end(room, &deblk_end))) ++ err = append_deblk(vdir); ++ /* smp_mb(); */ ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_vdir_free(struct au_vdir *vdir) ++{ ++ unsigned char **deblk; ++ ++ deblk = vdir->vd_deblk; ++ while (vdir->vd_nblk--) ++ kfree(*deblk++); ++ kfree(vdir->vd_deblk); ++ au_cache_free_vdir(vdir); ++} ++ ++static struct au_vdir *alloc_vdir(struct file *file) ++{ ++ struct au_vdir *vdir; ++ struct super_block *sb; ++ int err; ++ ++ sb = file->f_path.dentry->d_sb; ++ SiMustAnyLock(sb); ++ ++ err = -ENOMEM; ++ vdir = au_cache_alloc_vdir(); ++ if (unlikely(!vdir)) ++ goto out; ++ ++ vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); ++ if (unlikely(!vdir->vd_deblk)) ++ goto out_free; ++ ++ vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; ++ if (!vdir->vd_deblk_sz) { ++ /* estimate the apropriate size for deblk */ ++ vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); ++ /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ ++ } ++ vdir->vd_nblk = 0; ++ vdir->vd_version = 0; ++ vdir->vd_jiffy = 0; ++ err = append_deblk(vdir); ++ if (!err) ++ return vdir; /* success */ ++ ++ kfree(vdir->vd_deblk); ++ ++out_free: ++ au_cache_free_vdir(vdir); ++out: ++ vdir = ERR_PTR(err); ++ return vdir; ++} ++ ++static int reinit_vdir(struct au_vdir *vdir) ++{ ++ int err; ++ union au_vdir_deblk_p p, deblk_end; ++ ++ while (vdir->vd_nblk > 1) { ++ kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); ++ /* vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; */ ++ vdir->vd_nblk--; ++ } ++ p.deblk = vdir->vd_deblk[0]; ++ deblk_end.deblk = p.deblk + vdir->vd_deblk_sz; ++ err = set_deblk_end(&p, &deblk_end); ++ /* keep vd_dblk_sz */ ++ vdir->vd_last.ul = 0; ++ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; ++ vdir->vd_version = 0; ++ vdir->vd_jiffy = 0; ++ /* smp_mb(); */ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AuFillVdir_CALLED 1 ++#define AuFillVdir_WHABLE (1 << 1) ++#define AuFillVdir_SHWH (1 << 2) ++#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) ++#define au_fset_fillvdir(flags, name) \ ++ do { (flags) |= AuFillVdir_##name; } while (0) ++#define au_fclr_fillvdir(flags, name) \ ++ do { (flags) &= ~AuFillVdir_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_SHWH ++#undef AuFillVdir_SHWH ++#define AuFillVdir_SHWH 0 ++#endif ++ ++struct fillvdir_arg { ++ struct dir_context ctx; ++ struct file *file; ++ struct au_vdir *vdir; ++ struct au_nhash delist; ++ struct au_nhash whlist; ++ aufs_bindex_t bindex; ++ unsigned int flags; ++ int err; ++}; ++ ++static int fillvdir(struct dir_context *ctx, const char *__name, int nlen, ++ loff_t offset __maybe_unused, u64 h_ino, ++ unsigned int d_type) ++{ ++ struct fillvdir_arg *arg = container_of(ctx, struct fillvdir_arg, ctx); ++ char *name = (void *)__name; ++ struct super_block *sb; ++ ino_t ino; ++ const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); ++ ++ arg->err = 0; ++ sb = arg->file->f_path.dentry->d_sb; ++ au_fset_fillvdir(arg->flags, CALLED); ++ /* smp_mb(); */ ++ if (nlen <= AUFS_WH_PFX_LEN ++ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ if (test_known(&arg->delist, name, nlen) ++ || au_nhash_test_known_wh(&arg->whlist, name, nlen)) ++ goto out; /* already exists or whiteouted */ ++ ++ sb = arg->file->f_path.dentry->d_sb; ++ arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); ++ if (!arg->err) { ++ if (unlikely(nlen > AUFS_MAX_NAMELEN)) ++ d_type = DT_UNKNOWN; ++ arg->err = append_de(arg->vdir, name, nlen, ino, ++ d_type, &arg->delist); ++ } ++ } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { ++ name += AUFS_WH_PFX_LEN; ++ nlen -= AUFS_WH_PFX_LEN; ++ if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) ++ goto out; /* already whiteouted */ ++ ++ if (shwh) ++ arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, ++ &ino); ++ if (!arg->err) { ++ if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) ++ d_type = DT_UNKNOWN; ++ arg->err = au_nhash_append_wh ++ (&arg->whlist, name, nlen, ino, d_type, ++ arg->bindex, shwh); ++ } ++ } ++ ++out: ++ if (!arg->err) ++ arg->vdir->vd_jiffy = jiffies; ++ /* smp_mb(); */ ++ AuTraceErr(arg->err); ++ return arg->err; ++} ++ ++static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, ++ struct au_nhash *whlist, struct au_nhash *delist) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ int err; ++ unsigned int nh, u; ++ struct hlist_head *head; ++ struct au_vdir_wh *pos; ++ struct hlist_node *n; ++ char *p, *o; ++ struct au_vdir_destr *destr; ++ ++ AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); ++ ++ err = -ENOMEM; ++ o = p = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = 0; ++ nh = whlist->nh_num; ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ p += AUFS_WH_PFX_LEN; ++ for (u = 0; u < nh; u++) { ++ head = whlist->nh_head + u; ++ hlist_for_each_entry_safe(pos, n, head, wh_hash) { ++ destr = &pos->wh_str; ++ memcpy(p, destr->name, destr->len); ++ err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, ++ pos->wh_ino, pos->wh_type, delist); ++ if (unlikely(err)) ++ break; ++ } ++ } ++ ++ free_page((unsigned long)o); ++ ++out: ++ AuTraceErr(err); ++ return err; ++#else ++ return 0; ++#endif ++} ++ ++static int au_do_read_vdir(struct fillvdir_arg *arg) ++{ ++ int err; ++ unsigned int rdhash; ++ loff_t offset; ++ aufs_bindex_t bend, bindex, bstart; ++ unsigned char shwh; ++ struct file *hf, *file; ++ struct super_block *sb; ++ ++ file = arg->file; ++ sb = file->f_path.dentry->d_sb; ++ SiMustAnyLock(sb); ++ ++ rdhash = au_sbi(sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); ++ err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out_delist; ++ ++ err = 0; ++ arg->flags = 0; ++ shwh = 0; ++ if (au_opt_test(au_mntflags(sb), SHWH)) { ++ shwh = 1; ++ au_fset_fillvdir(arg->flags, SHWH); ++ } ++ bstart = au_fbstart(file); ++ bend = au_fbend_dir(file); ++ for (bindex = bstart; !err && bindex <= bend; bindex++) { ++ hf = au_hf_dir(file, bindex); ++ if (!hf) ++ continue; ++ ++ offset = vfsub_llseek(hf, 0, SEEK_SET); ++ err = offset; ++ if (unlikely(offset)) ++ break; ++ ++ arg->bindex = bindex; ++ au_fclr_fillvdir(arg->flags, WHABLE); ++ if (shwh ++ || (bindex != bend ++ && au_br_whable(au_sbr_perm(sb, bindex)))) ++ au_fset_fillvdir(arg->flags, WHABLE); ++ do { ++ arg->err = 0; ++ au_fclr_fillvdir(arg->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_iterate_dir(hf, &arg->ctx); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); ++ ++ /* ++ * dir_relax() may be good for concurrency, but aufs should not ++ * use it since it will cause a lockdep problem. ++ */ ++ } ++ ++ if (!err && shwh) ++ err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); ++ ++ au_nhash_wh_free(&arg->whlist); ++ ++out_delist: ++ au_nhash_de_free(&arg->delist); ++out: ++ return err; ++} ++ ++static int read_vdir(struct file *file, int may_read) ++{ ++ int err; ++ unsigned long expire; ++ unsigned char do_read; ++ struct fillvdir_arg arg = { ++ .ctx = { ++ .actor = fillvdir ++ } ++ }; ++ struct inode *inode; ++ struct au_vdir *vdir, *allocated; ++ ++ err = 0; ++ inode = file_inode(file); ++ IMustLock(inode); ++ SiMustAnyLock(inode->i_sb); ++ ++ allocated = NULL; ++ do_read = 0; ++ expire = au_sbi(inode->i_sb)->si_rdcache; ++ vdir = au_ivdir(inode); ++ if (!vdir) { ++ do_read = 1; ++ vdir = alloc_vdir(file); ++ err = PTR_ERR(vdir); ++ if (IS_ERR(vdir)) ++ goto out; ++ err = 0; ++ allocated = vdir; ++ } else if (may_read ++ && (inode->i_version != vdir->vd_version ++ || time_after(jiffies, vdir->vd_jiffy + expire))) { ++ do_read = 1; ++ err = reinit_vdir(vdir); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ if (!do_read) ++ return 0; /* success */ ++ ++ arg.file = file; ++ arg.vdir = vdir; ++ err = au_do_read_vdir(&arg); ++ if (!err) { ++ /* file->f_pos = 0; */ /* todo: ctx->pos? */ ++ vdir->vd_version = inode->i_version; ++ vdir->vd_last.ul = 0; ++ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; ++ if (allocated) ++ au_set_ivdir(inode, allocated); ++ } else if (allocated) ++ au_vdir_free(allocated); ++ ++out: ++ return err; ++} ++ ++static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) ++{ ++ int err, rerr; ++ unsigned long ul, n; ++ const unsigned int deblk_sz = src->vd_deblk_sz; ++ ++ AuDebugOn(tgt->vd_nblk != 1); ++ ++ err = -ENOMEM; ++ if (tgt->vd_nblk < src->vd_nblk) { ++ unsigned char **p; ++ ++ p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, ++ GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ tgt->vd_deblk = p; ++ } ++ ++ if (tgt->vd_deblk_sz != deblk_sz) { ++ unsigned char *p; ++ ++ tgt->vd_deblk_sz = deblk_sz; ++ p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ tgt->vd_deblk[0] = p; ++ } ++ memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); ++ tgt->vd_version = src->vd_version; ++ tgt->vd_jiffy = src->vd_jiffy; ++ ++ n = src->vd_nblk; ++ for (ul = 1; ul < n; ul++) { ++ tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, ++ GFP_NOFS); ++ if (unlikely(!tgt->vd_deblk[ul])) ++ goto out; ++ tgt->vd_nblk++; ++ } ++ tgt->vd_nblk = n; ++ tgt->vd_last.ul = tgt->vd_last.ul; ++ tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; ++ tgt->vd_last.p.deblk += src->vd_last.p.deblk ++ - src->vd_deblk[src->vd_last.ul]; ++ /* smp_mb(); */ ++ return 0; /* success */ ++ ++out: ++ rerr = reinit_vdir(tgt); ++ BUG_ON(rerr); ++ return err; ++} ++ ++int au_vdir_init(struct file *file) ++{ ++ int err; ++ struct inode *inode; ++ struct au_vdir *vdir_cache, *allocated; ++ ++ /* test file->f_pos here instead of ctx->pos */ ++ err = read_vdir(file, !file->f_pos); ++ if (unlikely(err)) ++ goto out; ++ ++ allocated = NULL; ++ vdir_cache = au_fvdir_cache(file); ++ if (!vdir_cache) { ++ vdir_cache = alloc_vdir(file); ++ err = PTR_ERR(vdir_cache); ++ if (IS_ERR(vdir_cache)) ++ goto out; ++ allocated = vdir_cache; ++ } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { ++ /* test file->f_pos here instead of ctx->pos */ ++ err = reinit_vdir(vdir_cache); ++ if (unlikely(err)) ++ goto out; ++ } else ++ return 0; /* success */ ++ ++ inode = file_inode(file); ++ err = copy_vdir(vdir_cache, au_ivdir(inode)); ++ if (!err) { ++ file->f_version = inode->i_version; ++ if (allocated) ++ au_set_fvdir_cache(file, allocated); ++ } else if (allocated) ++ au_vdir_free(allocated); ++ ++out: ++ return err; ++} ++ ++static loff_t calc_offset(struct au_vdir *vdir) ++{ ++ loff_t offset; ++ union au_vdir_deblk_p p; ++ ++ p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; ++ offset = vdir->vd_last.p.deblk - p.deblk; ++ offset += vdir->vd_deblk_sz * vdir->vd_last.ul; ++ return offset; ++} ++ ++/* returns true or false */ ++static int seek_vdir(struct file *file, struct dir_context *ctx) ++{ ++ int valid; ++ unsigned int deblk_sz; ++ unsigned long ul, n; ++ loff_t offset; ++ union au_vdir_deblk_p p, deblk_end; ++ struct au_vdir *vdir_cache; ++ ++ valid = 1; ++ vdir_cache = au_fvdir_cache(file); ++ offset = calc_offset(vdir_cache); ++ AuDbg("offset %lld\n", offset); ++ if (ctx->pos == offset) ++ goto out; ++ ++ vdir_cache->vd_last.ul = 0; ++ vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; ++ if (!ctx->pos) ++ goto out; ++ ++ valid = 0; ++ deblk_sz = vdir_cache->vd_deblk_sz; ++ ul = div64_u64(ctx->pos, deblk_sz); ++ AuDbg("ul %lu\n", ul); ++ if (ul >= vdir_cache->vd_nblk) ++ goto out; ++ ++ n = vdir_cache->vd_nblk; ++ for (; ul < n; ul++) { ++ p.deblk = vdir_cache->vd_deblk[ul]; ++ deblk_end.deblk = p.deblk + deblk_sz; ++ offset = ul; ++ offset *= deblk_sz; ++ while (!is_deblk_end(&p, &deblk_end) && offset < ctx->pos) { ++ unsigned int l; ++ ++ l = calc_size(p.de->de_str.len); ++ offset += l; ++ p.deblk += l; ++ } ++ if (!is_deblk_end(&p, &deblk_end)) { ++ valid = 1; ++ vdir_cache->vd_last.ul = ul; ++ vdir_cache->vd_last.p = p; ++ break; ++ } ++ } ++ ++out: ++ /* smp_mb(); */ ++ AuTraceErr(!valid); ++ return valid; ++} ++ ++int au_vdir_fill_de(struct file *file, struct dir_context *ctx) ++{ ++ unsigned int l, deblk_sz; ++ union au_vdir_deblk_p deblk_end; ++ struct au_vdir *vdir_cache; ++ struct au_vdir_de *de; ++ ++ vdir_cache = au_fvdir_cache(file); ++ if (!seek_vdir(file, ctx)) ++ return 0; ++ ++ deblk_sz = vdir_cache->vd_deblk_sz; ++ while (1) { ++ deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; ++ deblk_end.deblk += deblk_sz; ++ while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { ++ de = vdir_cache->vd_last.p.de; ++ AuDbg("%.*s, off%lld, i%lu, dt%d\n", ++ de->de_str.len, de->de_str.name, ctx->pos, ++ (unsigned long)de->de_ino, de->de_type); ++ if (unlikely(!dir_emit(ctx, de->de_str.name, ++ de->de_str.len, de->de_ino, ++ de->de_type))) { ++ /* todo: ignore the error caused by udba? */ ++ /* return err; */ ++ return 0; ++ } ++ ++ l = calc_size(de->de_str.len); ++ vdir_cache->vd_last.p.deblk += l; ++ ctx->pos += l; ++ } ++ if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { ++ vdir_cache->vd_last.ul++; ++ vdir_cache->vd_last.p.deblk ++ = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; ++ ctx->pos = deblk_sz * vdir_cache->vd_last.ul; ++ continue; ++ } ++ break; ++ } ++ ++ /* smp_mb(); */ ++ return 0; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/vfsub.c linux-4.0.4/fs/aufs/vfsub.c +--- linux-4.0.4.orig/fs/aufs/vfsub.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/vfsub.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,833 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sub-routines for VFS ++ */ ++ ++#include ++#include ++#include ++#include "aufs.h" ++ ++int vfsub_update_h_iattr(struct path *h_path, int *did) ++{ ++ int err; ++ struct kstat st; ++ struct super_block *h_sb; ++ ++ /* for remote fs, leave work for its getattr or d_revalidate */ ++ /* for bad i_attr fs, handle them in aufs_getattr() */ ++ /* still some fs may acquire i_mutex. we need to skip them */ ++ err = 0; ++ if (!did) ++ did = &err; ++ h_sb = h_path->dentry->d_sb; ++ *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); ++ if (*did) ++ err = vfs_getattr(h_path, &st); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct file *vfsub_dentry_open(struct path *path, int flags) ++{ ++ struct file *file; ++ ++ file = dentry_open(path, flags /* | __FMODE_NONOTIFY */, ++ current_cred()); ++ if (!IS_ERR_OR_NULL(file) ++ && (file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) ++ i_readcount_inc(path->dentry->d_inode); ++ ++ return file; ++} ++ ++struct file *vfsub_filp_open(const char *path, int oflags, int mode) ++{ ++ struct file *file; ++ ++ lockdep_off(); ++ file = filp_open(path, ++ oflags /* | __FMODE_NONOTIFY */, ++ mode); ++ lockdep_on(); ++ if (IS_ERR(file)) ++ goto out; ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ ++out: ++ return file; ++} ++ ++/* ++ * Ideally this function should call VFS:do_last() in order to keep all its ++ * checkings. But it is very hard for aufs to regenerate several VFS internal ++ * structure such as nameidata. This is a second (or third) best approach. ++ * cf. linux/fs/namei.c:do_last(), lookup_open() and atomic_open(). ++ */ ++int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, ++ struct vfsub_aopen_args *args, struct au_branch *br) ++{ ++ int err; ++ struct file *file = args->file; ++ /* copied from linux/fs/namei.c:atomic_open() */ ++ struct dentry *const DENTRY_NOT_SET = (void *)-1UL; ++ ++ IMustLock(dir); ++ AuDebugOn(!dir->i_op->atomic_open); ++ ++ err = au_br_test_oflag(args->open_flag, br); ++ if (unlikely(err)) ++ goto out; ++ ++ args->file->f_path.dentry = DENTRY_NOT_SET; ++ args->file->f_path.mnt = au_br_mnt(br); ++ err = dir->i_op->atomic_open(dir, dentry, file, args->open_flag, ++ args->create_mode, args->opened); ++ if (err >= 0) { ++ /* some filesystems don't set FILE_CREATED while succeeded? */ ++ if (*args->opened & FILE_CREATED) ++ fsnotify_create(dir, dentry); ++ } else ++ goto out; ++ ++ ++ if (!err) { ++ /* todo: call VFS:may_open() here */ ++ err = open_check_o_direct(file); ++ /* todo: ima_file_check() too? */ ++ if (!err && (args->open_flag & __FMODE_EXEC)) ++ err = deny_write_access(file); ++ if (unlikely(err)) ++ /* note that the file is created and still opened */ ++ goto out; ++ } ++ ++ atomic_inc(&br->br_count); ++ fsnotify_open(file); ++ ++out: ++ return err; ++} ++ ++int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) ++{ ++ int err; ++ ++ err = kern_path(name, flags, path); ++ if (!err && path->dentry->d_inode) ++ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, ++ int len) ++{ ++ struct path path = { ++ .mnt = NULL ++ }; ++ ++ /* VFS checks it too, but by WARN_ON_ONCE() */ ++ IMustLock(parent->d_inode); ++ ++ path.dentry = lookup_one_len(name, parent, len); ++ if (IS_ERR(path.dentry)) ++ goto out; ++ if (path.dentry->d_inode) ++ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ ++ ++out: ++ AuTraceErrPtr(path.dentry); ++ return path.dentry; ++} ++ ++void vfsub_call_lkup_one(void *args) ++{ ++ struct vfsub_lkup_one_args *a = args; ++ *a->errp = vfsub_lkup_one(a->name, a->parent); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2) ++{ ++ struct dentry *d; ++ ++ lockdep_off(); ++ d = lock_rename(d1, d2); ++ lockdep_on(); ++ au_hn_suspend(hdir1); ++ if (hdir1 != hdir2) ++ au_hn_suspend(hdir2); ++ ++ return d; ++} ++ ++void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2) ++{ ++ au_hn_resume(hdir1); ++ if (hdir1 != hdir2) ++ au_hn_resume(hdir2); ++ lockdep_off(); ++ unlock_rename(d1, d2); ++ lockdep_on(); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mknod(path, d, mode, 0); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_create(dir, path->dentry, mode, want_excl); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_symlink(struct inode *dir, struct path *path, const char *symname) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_symlink(path, d, symname); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_symlink(dir, path->dentry, symname); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mknod(path, d, mode, new_encode_dev(dev)); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_mknod(dir, path->dentry, mode, dev); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++static int au_test_nlink(struct inode *inode) ++{ ++ const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ ++ ++ if (!au_test_fs_no_limit_nlink(inode->i_sb) ++ || inode->i_nlink < link_max) ++ return 0; ++ return -EMLINK; ++} ++ ++int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path, ++ struct inode **delegated_inode) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ err = au_test_nlink(src_dentry->d_inode); ++ if (unlikely(err)) ++ return err; ++ ++ /* we don't call may_linkat() */ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_link(src_dentry, path, d); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_link(src_dentry, dir, path->dentry, delegated_inode); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ /* fuse has different memory inode for the same inumber */ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ tmp.dentry = src_dentry; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, ++ struct inode *dir, struct path *path, ++ struct inode **delegated_inode) ++{ ++ int err; ++ struct path tmp = { ++ .mnt = path->mnt ++ }; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ IMustLock(src_dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ tmp.dentry = src_dentry->d_parent; ++ err = security_path_rename(&tmp, src_dentry, path, d, /*flags*/0); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_rename(src_dir, src_dentry, dir, path->dentry, ++ delegated_inode, /*flags*/0); ++ lockdep_on(); ++ if (!err) { ++ int did; ++ ++ tmp.dentry = d->d_parent; ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = src_dentry; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ tmp.dentry = src_dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_mkdir(struct inode *dir, struct path *path, int mode) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mkdir(path, d, mode); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_mkdir(dir, path->dentry, mode); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_rmdir(struct inode *dir, struct path *path) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_rmdir(path, d); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ lockdep_off(); ++ err = vfs_rmdir(dir, path->dentry); ++ lockdep_on(); ++ if (!err) { ++ struct path tmp = { ++ .dentry = path->dentry->d_parent, ++ .mnt = path->mnt ++ }; ++ ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: support mmap_sem? */ ++ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ ++ lockdep_off(); ++ err = vfs_read(file, ubuf, count, ppos); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++/* todo: kernel_read()? */ ++ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = vfsub_read_u(file, buf.u, count, ppos); ++ set_fs(oldfs); ++ return err; ++} ++ ++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ ++ lockdep_off(); ++ err = vfs_write(file, ubuf, count, ppos); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ const char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = vfsub_write_u(file, buf.u, count, ppos); ++ set_fs(oldfs); ++ return err; ++} ++ ++int vfsub_flush(struct file *file, fl_owner_t id) ++{ ++ int err; ++ ++ err = 0; ++ if (file->f_op->flush) { ++ if (!au_test_nfs(file->f_path.dentry->d_sb)) ++ err = file->f_op->flush(file, id); ++ else { ++ lockdep_off(); ++ err = file->f_op->flush(file, id); ++ lockdep_on(); ++ } ++ if (!err) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); ++ /*ignore*/ ++ } ++ return err; ++} ++ ++int vfsub_iterate_dir(struct file *file, struct dir_context *ctx) ++{ ++ int err; ++ ++ AuDbg("%pD, ctx{%pf, %llu}\n", file, ctx->actor, ctx->pos); ++ ++ lockdep_off(); ++ err = iterate_dir(file, ctx); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++long vfsub_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ long err; ++ ++ lockdep_off(); ++ err = do_splice_to(in, ppos, pipe, len, flags); ++ lockdep_on(); ++ file_accessed(in); ++ if (err >= 0) ++ vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) ++{ ++ long err; ++ ++ lockdep_off(); ++ err = do_splice_from(pipe, out, ppos, len, flags); ++ lockdep_on(); ++ if (err >= 0) ++ vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++int vfsub_fsync(struct file *file, struct path *path, int datasync) ++{ ++ int err; ++ ++ /* file can be NULL */ ++ lockdep_off(); ++ err = vfs_fsync(file, datasync); ++ lockdep_on(); ++ if (!err) { ++ if (!path) { ++ AuDebugOn(!file); ++ path = &file->f_path; ++ } ++ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ ++ } ++ return err; ++} ++ ++/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */ ++int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, ++ struct file *h_file) ++{ ++ int err; ++ struct inode *h_inode; ++ struct super_block *h_sb; ++ ++ if (!h_file) { ++ err = vfsub_truncate(h_path, length); ++ goto out; ++ } ++ ++ h_inode = h_path->dentry->d_inode; ++ h_sb = h_inode->i_sb; ++ lockdep_off(); ++ sb_start_write(h_sb); ++ lockdep_on(); ++ err = locks_verify_truncate(h_inode, h_file, length); ++ if (!err) ++ err = security_path_truncate(h_path); ++ if (!err) { ++ lockdep_off(); ++ err = do_truncate(h_path->dentry, length, attr, h_file); ++ lockdep_on(); ++ } ++ lockdep_off(); ++ sb_end_write(h_sb); ++ lockdep_on(); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_vfsub_mkdir_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++ int mode; ++}; ++ ++static void au_call_vfsub_mkdir(void *args) ++{ ++ struct au_vfsub_mkdir_args *a = args; ++ *a->errp = vfsub_mkdir(a->dir, a->path, a->mode); ++} ++ ++int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode) ++{ ++ int err, do_sio, wkq_err; ++ ++ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); ++ if (!do_sio) { ++ lockdep_off(); ++ err = vfsub_mkdir(dir, path, mode); ++ lockdep_on(); ++ } else { ++ struct au_vfsub_mkdir_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path, ++ .mode = mode ++ }; ++ wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++struct au_vfsub_rmdir_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++}; ++ ++static void au_call_vfsub_rmdir(void *args) ++{ ++ struct au_vfsub_rmdir_args *a = args; ++ *a->errp = vfsub_rmdir(a->dir, a->path); ++} ++ ++int vfsub_sio_rmdir(struct inode *dir, struct path *path) ++{ ++ int err, do_sio, wkq_err; ++ ++ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); ++ if (!do_sio) { ++ lockdep_off(); ++ err = vfsub_rmdir(dir, path); ++ lockdep_on(); ++ } else { ++ struct au_vfsub_rmdir_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path ++ }; ++ wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct notify_change_args { ++ int *errp; ++ struct path *path; ++ struct iattr *ia; ++ struct inode **delegated_inode; ++}; ++ ++static void call_notify_change(void *args) ++{ ++ struct notify_change_args *a = args; ++ struct inode *h_inode; ++ ++ h_inode = a->path->dentry->d_inode; ++ IMustLock(h_inode); ++ ++ *a->errp = -EPERM; ++ if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { ++ lockdep_off(); ++ *a->errp = notify_change(a->path->dentry, a->ia, ++ a->delegated_inode); ++ lockdep_on(); ++ if (!*a->errp) ++ vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/ ++ } ++ AuTraceErr(*a->errp); ++} ++ ++int vfsub_notify_change(struct path *path, struct iattr *ia, ++ struct inode **delegated_inode) ++{ ++ int err; ++ struct notify_change_args args = { ++ .errp = &err, ++ .path = path, ++ .ia = ia, ++ .delegated_inode = delegated_inode ++ }; ++ ++ call_notify_change(&args); ++ ++ return err; ++} ++ ++int vfsub_sio_notify_change(struct path *path, struct iattr *ia, ++ struct inode **delegated_inode) ++{ ++ int err, wkq_err; ++ struct notify_change_args args = { ++ .errp = &err, ++ .path = path, ++ .ia = ia, ++ .delegated_inode = delegated_inode ++ }; ++ ++ wkq_err = au_wkq_wait(call_notify_change, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct unlink_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++ struct inode **delegated_inode; ++}; ++ ++static void call_unlink(void *args) ++{ ++ struct unlink_args *a = args; ++ struct dentry *d = a->path->dentry; ++ struct inode *h_inode; ++ const int stop_sillyrename = (au_test_nfs(d->d_sb) ++ && au_dcount(d) == 1); ++ ++ IMustLock(a->dir); ++ ++ a->path->dentry = d->d_parent; ++ *a->errp = security_path_unlink(a->path, d); ++ a->path->dentry = d; ++ if (unlikely(*a->errp)) ++ return; ++ ++ if (!stop_sillyrename) ++ dget(d); ++ h_inode = d->d_inode; ++ if (h_inode) ++ ihold(h_inode); ++ ++ lockdep_off(); ++ *a->errp = vfs_unlink(a->dir, d, a->delegated_inode); ++ lockdep_on(); ++ if (!*a->errp) { ++ struct path tmp = { ++ .dentry = d->d_parent, ++ .mnt = a->path->mnt ++ }; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ ++ } ++ ++ if (!stop_sillyrename) ++ dput(d); ++ if (h_inode) ++ iput(h_inode); ++ ++ AuTraceErr(*a->errp); ++} ++ ++/* ++ * @dir: must be locked. ++ * @dentry: target dentry. ++ */ ++int vfsub_unlink(struct inode *dir, struct path *path, ++ struct inode **delegated_inode, int force) ++{ ++ int err; ++ struct unlink_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path, ++ .delegated_inode = delegated_inode ++ }; ++ ++ if (!force) ++ call_unlink(&args); ++ else { ++ int wkq_err; ++ ++ wkq_err = au_wkq_wait(call_unlink, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/vfsub.h linux-4.0.4/fs/aufs/vfsub.h +--- linux-4.0.4.orig/fs/aufs/vfsub.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/vfsub.h 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,273 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * sub-routines for VFS ++ */ ++ ++#ifndef __AUFS_VFSUB_H__ ++#define __AUFS_VFSUB_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include "debug.h" ++ ++/* copied from linux/fs/internal.h */ ++/* todo: BAD approach!! */ ++extern void __mnt_drop_write(struct vfsmount *); ++extern spinlock_t inode_sb_list_lock; ++extern int open_check_o_direct(struct file *f); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for lower inode */ ++/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ ++/* reduce? gave up. */ ++enum { ++ AuLsc_I_Begin = I_MUTEX_PARENT2, /* 5 */ ++ AuLsc_I_PARENT, /* lower inode, parent first */ ++ AuLsc_I_PARENT2, /* copyup dirs */ ++ AuLsc_I_PARENT3, /* copyup wh */ ++ AuLsc_I_CHILD, ++ AuLsc_I_CHILD2, ++ AuLsc_I_End ++}; ++ ++/* to debug easier, do not make them inlined functions */ ++#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) ++#define IMustLock(i) MtxMustLock(&(i)->i_mutex) ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void vfsub_drop_nlink(struct inode *inode) ++{ ++ AuDebugOn(!inode->i_nlink); ++ drop_nlink(inode); ++} ++ ++static inline void vfsub_dead_dir(struct inode *inode) ++{ ++ AuDebugOn(!S_ISDIR(inode->i_mode)); ++ inode->i_flags |= S_DEAD; ++ clear_nlink(inode); ++} ++ ++static inline int vfsub_native_ro(struct inode *inode) ++{ ++ return (inode->i_sb->s_flags & MS_RDONLY) ++ || IS_RDONLY(inode) ++ /* || IS_APPEND(inode) */ ++ || IS_IMMUTABLE(inode); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_update_h_iattr(struct path *h_path, int *did); ++struct file *vfsub_dentry_open(struct path *path, int flags); ++struct file *vfsub_filp_open(const char *path, int oflags, int mode); ++struct vfsub_aopen_args { ++ struct file *file; ++ unsigned int open_flag; ++ umode_t create_mode; ++ int *opened; ++}; ++struct au_branch; ++int vfsub_atomic_open(struct inode *dir, struct dentry *dentry, ++ struct vfsub_aopen_args *args, struct au_branch *br); ++int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); ++ ++struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, ++ int len); ++ ++struct vfsub_lkup_one_args { ++ struct dentry **errp; ++ struct qstr *name; ++ struct dentry *parent; ++}; ++ ++static inline struct dentry *vfsub_lkup_one(struct qstr *name, ++ struct dentry *parent) ++{ ++ return vfsub_lookup_one_len(name->name, parent, name->len); ++} ++ ++void vfsub_call_lkup_one(void *args); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline int vfsub_mnt_want_write(struct vfsmount *mnt) ++{ ++ int err; ++ ++ lockdep_off(); ++ err = mnt_want_write(mnt); ++ lockdep_on(); ++ return err; ++} ++ ++static inline void vfsub_mnt_drop_write(struct vfsmount *mnt) ++{ ++ lockdep_off(); ++ mnt_drop_write(mnt); ++ lockdep_on(); ++} ++ ++#if 0 /* reserved */ ++static inline void vfsub_mnt_drop_write_file(struct file *file) ++{ ++ lockdep_off(); ++ mnt_drop_write_file(file); ++ lockdep_on(); ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_hinode; ++struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2); ++void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2); ++ ++int vfsub_create(struct inode *dir, struct path *path, int mode, ++ bool want_excl); ++int vfsub_symlink(struct inode *dir, struct path *path, ++ const char *symname); ++int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev); ++int vfsub_link(struct dentry *src_dentry, struct inode *dir, ++ struct path *path, struct inode **delegated_inode); ++int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, ++ struct inode *hdir, struct path *path, ++ struct inode **delegated_inode); ++int vfsub_mkdir(struct inode *dir, struct path *path, int mode); ++int vfsub_rmdir(struct inode *dir, struct path *path); ++ ++/* ---------------------------------------------------------------------- */ ++ ++ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos); ++int vfsub_flush(struct file *file, fl_owner_t id); ++int vfsub_iterate_dir(struct file *file, struct dir_context *ctx); ++ ++static inline loff_t vfsub_f_size_read(struct file *file) ++{ ++ return i_size_read(file_inode(file)); ++} ++ ++static inline unsigned int vfsub_file_flags(struct file *file) ++{ ++ unsigned int flags; ++ ++ spin_lock(&file->f_lock); ++ flags = file->f_flags; ++ spin_unlock(&file->f_lock); ++ ++ return flags; ++} ++ ++#if 0 /* reserved */ ++static inline void vfsub_file_accessed(struct file *h_file) ++{ ++ file_accessed(h_file); ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/ ++} ++#endif ++ ++static inline void vfsub_touch_atime(struct vfsmount *h_mnt, ++ struct dentry *h_dentry) ++{ ++ struct path h_path = { ++ .dentry = h_dentry, ++ .mnt = h_mnt ++ }; ++ touch_atime(&h_path); ++ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ ++} ++ ++static inline int vfsub_update_time(struct inode *h_inode, struct timespec *ts, ++ int flags) ++{ ++ return generic_update_time(h_inode, ts, flags); ++ /* no vfsub_update_h_iattr() since we don't have struct path */ ++} ++ ++long vfsub_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags); ++long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags); ++ ++static inline long vfsub_truncate(struct path *path, loff_t length) ++{ ++ long err; ++ ++ lockdep_off(); ++ err = vfs_truncate(path, length); ++ lockdep_on(); ++ return err; ++} ++ ++int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, ++ struct file *h_file); ++int vfsub_fsync(struct file *file, struct path *path, int datasync); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) ++{ ++ loff_t err; ++ ++ lockdep_off(); ++ err = vfs_llseek(file, offset, origin); ++ lockdep_on(); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode); ++int vfsub_sio_rmdir(struct inode *dir, struct path *path); ++int vfsub_sio_notify_change(struct path *path, struct iattr *ia, ++ struct inode **delegated_inode); ++int vfsub_notify_change(struct path *path, struct iattr *ia, ++ struct inode **delegated_inode); ++int vfsub_unlink(struct inode *dir, struct path *path, ++ struct inode **delegated_inode, int force); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline int vfsub_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ int err; ++ ++ lockdep_off(); ++ err = vfs_setxattr(dentry, name, value, size, flags); ++ lockdep_on(); ++ ++ return err; ++} ++ ++static inline int vfsub_removexattr(struct dentry *dentry, const char *name) ++{ ++ int err; ++ ++ lockdep_off(); ++ err = vfs_removexattr(dentry, name); ++ lockdep_on(); ++ ++ return err; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_VFSUB_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/wbr_policy.c linux-4.0.4/fs/aufs/wbr_policy.c +--- linux-4.0.4.orig/fs/aufs/wbr_policy.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/wbr_policy.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,752 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * policies for selecting one among multiple writable branches ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* subset of cpup_attr() */ ++static noinline_for_stack ++int au_cpdown_attr(struct path *h_path, struct dentry *h_src) ++{ ++ int err, sbits; ++ struct iattr ia; ++ struct inode *h_isrc; ++ ++ h_isrc = h_src->d_inode; ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; ++ ia.ia_mode = h_isrc->i_mode; ++ ia.ia_uid = h_isrc->i_uid; ++ ia.ia_gid = h_isrc->i_gid; ++ sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc->i_flags); ++ /* no delegation since it is just created */ ++ err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); ++ ++ /* is this nfs only? */ ++ if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ err = vfsub_sio_notify_change(h_path, &ia, /*delegated*/NULL); ++ } ++ ++ return err; ++} ++ ++#define AuCpdown_PARENT_OPQ 1 ++#define AuCpdown_WHED (1 << 1) ++#define AuCpdown_MADE_DIR (1 << 2) ++#define AuCpdown_DIROPQ (1 << 3) ++#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name) ++#define au_fset_cpdown(flags, name) \ ++ do { (flags) |= AuCpdown_##name; } while (0) ++#define au_fclr_cpdown(flags, name) \ ++ do { (flags) &= ~AuCpdown_##name; } while (0) ++ ++static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, ++ unsigned int *flags) ++{ ++ int err; ++ struct dentry *opq_dentry; ++ ++ opq_dentry = au_diropq_create(dentry, bdst); ++ err = PTR_ERR(opq_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out; ++ dput(opq_dentry); ++ au_fset_cpdown(*flags, DIROPQ); ++ ++out: ++ return err; ++} ++ ++static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, ++ struct inode *dir, aufs_bindex_t bdst) ++{ ++ int err; ++ struct path h_path; ++ struct au_branch *br; ++ ++ br = au_sbr(dentry->d_sb, bdst); ++ h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ err = 0; ++ if (h_path.dentry->d_inode) { ++ h_path.mnt = au_br_mnt(br); ++ err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, ++ dentry); ++ } ++ dput(h_path.dentry); ++ ++out: ++ return err; ++} ++ ++static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, ++ struct dentry *h_parent, void *arg) ++{ ++ int err, rerr; ++ aufs_bindex_t bopq, bstart; ++ struct path h_path; ++ struct dentry *parent; ++ struct inode *h_dir, *h_inode, *inode, *dir; ++ unsigned int *flags = arg; ++ ++ bstart = au_dbstart(dentry); ++ /* dentry is di-locked */ ++ parent = dget_parent(dentry); ++ dir = parent->d_inode; ++ h_dir = h_parent->d_inode; ++ AuDebugOn(h_dir != au_h_iptr(dir, bdst)); ++ IMustLock(h_dir); ++ ++ err = au_lkup_neg(dentry, bdst, /*wh*/0); ++ if (unlikely(err < 0)) ++ goto out; ++ h_path.dentry = au_h_dptr(dentry, bdst); ++ h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); ++ err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, ++ S_IRWXU | S_IRUGO | S_IXUGO); ++ if (unlikely(err)) ++ goto out_put; ++ au_fset_cpdown(*flags, MADE_DIR); ++ ++ bopq = au_dbdiropq(dentry); ++ au_fclr_cpdown(*flags, WHED); ++ au_fclr_cpdown(*flags, DIROPQ); ++ if (au_dbwh(dentry) == bdst) ++ au_fset_cpdown(*flags, WHED); ++ if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) ++ au_fset_cpdown(*flags, PARENT_OPQ); ++ h_inode = h_path.dentry->d_inode; ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ if (au_ftest_cpdown(*flags, WHED)) { ++ err = au_cpdown_dir_opq(dentry, bdst, flags); ++ if (unlikely(err)) { ++ mutex_unlock(&h_inode->i_mutex); ++ goto out_dir; ++ } ++ } ++ ++ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); ++ mutex_unlock(&h_inode->i_mutex); ++ if (unlikely(err)) ++ goto out_opq; ++ ++ if (au_ftest_cpdown(*flags, WHED)) { ++ err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); ++ if (unlikely(err)) ++ goto out_opq; ++ } ++ ++ inode = dentry->d_inode; ++ if (au_ibend(inode) < bdst) ++ au_set_ibend(inode, bdst); ++ au_set_h_iptr(inode, bdst, au_igrab(h_inode), ++ au_hi_flags(inode, /*isdir*/1)); ++ au_fhsm_wrote(dentry->d_sb, bdst, /*force*/0); ++ goto out; /* success */ ++ ++ /* revert */ ++out_opq: ++ if (au_ftest_cpdown(*flags, DIROPQ)) { ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(dentry, bdst); ++ mutex_unlock(&h_inode->i_mutex); ++ if (unlikely(rerr)) { ++ AuIOErr("failed removing diropq for %pd b%d (%d)\n", ++ dentry, bdst, rerr); ++ err = -EIO; ++ goto out; ++ } ++ } ++out_dir: ++ if (au_ftest_cpdown(*flags, MADE_DIR)) { ++ rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); ++ if (unlikely(rerr)) { ++ AuIOErr("failed removing %pd b%d (%d)\n", ++ dentry, bdst, rerr); ++ err = -EIO; ++ } ++ } ++out_put: ++ au_set_h_dptr(dentry, bdst, NULL); ++ if (au_dbend(dentry) == bdst) ++ au_update_dbend(dentry); ++out: ++ dput(parent); ++ return err; ++} ++ ++int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ int err; ++ unsigned int flags; ++ ++ flags = 0; ++ err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies for create */ ++ ++int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ int err, i, j, ndentry; ++ aufs_bindex_t bopq; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries, *parent, *d; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ parent = dget_parent(dentry); ++ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0); ++ if (unlikely(err)) ++ goto out_free; ++ ++ err = bindex; ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) { ++ d = dentries[j]; ++ di_read_lock_parent2(d, !AuLock_IR); ++ bopq = au_dbdiropq(d); ++ di_read_unlock(d, !AuLock_IR); ++ if (bopq >= 0 && bopq < err) ++ err = bopq; ++ } ++ } ++ ++out_free: ++ dput(parent); ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ for (; bindex >= 0; bindex--) ++ if (!au_br_rdonly(au_sbr(sb, bindex))) ++ return bindex; ++ return -EROFS; ++} ++ ++/* top down parent */ ++static int au_wbr_create_tdp(struct dentry *dentry, ++ unsigned int flags __maybe_unused) ++{ ++ int err; ++ aufs_bindex_t bstart, bindex; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ err = bstart; ++ if (!au_br_rdonly(au_sbr(sb, bstart))) ++ goto out; ++ ++ err = -EROFS; ++ parent = dget_parent(dentry); ++ for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !h_parent->d_inode) ++ continue; ++ ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = bindex; ++ break; ++ } ++ } ++ dput(parent); ++ ++ /* bottom up here */ ++ if (unlikely(err < 0)) { ++ err = au_wbr_bu(sb, bstart - 1); ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ } ++ ++out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* an exception for the policy other than tdp */ ++static int au_wbr_create_exp(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bwh, bdiropq; ++ struct dentry *parent; ++ ++ err = -1; ++ bwh = au_dbwh(dentry); ++ parent = dget_parent(dentry); ++ bdiropq = au_dbdiropq(parent); ++ if (bwh >= 0) { ++ if (bdiropq >= 0) ++ err = min(bdiropq, bwh); ++ else ++ err = bwh; ++ AuDbg("%d\n", err); ++ } else if (bdiropq >= 0) { ++ err = bdiropq; ++ AuDbg("%d\n", err); ++ } ++ dput(parent); ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++ if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) ++ err = -1; ++ ++ AuDbg("%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* round robin */ ++static int au_wbr_create_init_rr(struct super_block *sb) ++{ ++ int err; ++ ++ err = au_wbr_bu(sb, au_sbend(sb)); ++ atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ ++ /* smp_mb(); */ ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags) ++{ ++ int err, nbr; ++ unsigned int u; ++ aufs_bindex_t bindex, bend; ++ struct super_block *sb; ++ atomic_t *next; ++ ++ err = au_wbr_create_exp(dentry); ++ if (err >= 0) ++ goto out; ++ ++ sb = dentry->d_sb; ++ next = &au_sbi(sb)->si_wbr_rr_next; ++ bend = au_sbend(sb); ++ nbr = bend + 1; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ if (!au_ftest_wbr(flags, DIR)) { ++ err = atomic_dec_return(next) + 1; ++ /* modulo for 0 is meaningless */ ++ if (unlikely(!err)) ++ err = atomic_dec_return(next) + 1; ++ } else ++ err = atomic_read(next); ++ AuDbg("%d\n", err); ++ u = err; ++ err = u % nbr; ++ AuDbg("%d\n", err); ++ if (!au_br_rdonly(au_sbr(sb, err))) ++ break; ++ err = -EROFS; ++ } ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++out: ++ AuDbg("%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* most free space */ ++static void au_mfs(struct dentry *dentry, struct dentry *parent) ++{ ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_wbr_mfs *mfs; ++ struct dentry *h_parent; ++ aufs_bindex_t bindex, bend; ++ int err; ++ unsigned long long b, bavail; ++ struct path h_path; ++ /* reduce the stack usage */ ++ struct kstatfs *st; ++ ++ st = kmalloc(sizeof(*st), GFP_NOFS); ++ if (unlikely(!st)) { ++ AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); ++ return; ++ } ++ ++ bavail = 0; ++ sb = dentry->d_sb; ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ MtxMustLock(&mfs->mfs_lock); ++ mfs->mfs_bindex = -EROFS; ++ mfs->mfsrr_bytes = 0; ++ if (!parent) { ++ bindex = 0; ++ bend = au_sbend(sb); ++ } else { ++ bindex = au_dbstart(parent); ++ bend = au_dbtaildir(parent); ++ } ++ ++ for (; bindex <= bend; bindex++) { ++ if (parent) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !h_parent->d_inode) ++ continue; ++ } ++ br = au_sbr(sb, bindex); ++ if (au_br_rdonly(br)) ++ continue; ++ ++ /* sb->s_root for NFS is unreliable */ ++ h_path.mnt = au_br_mnt(br); ++ h_path.dentry = h_path.mnt->mnt_root; ++ err = vfs_statfs(&h_path, st); ++ if (unlikely(err)) { ++ AuWarn1("failed statfs, b%d, %d\n", bindex, err); ++ continue; ++ } ++ ++ /* when the available size is equal, select the lower one */ ++ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) ++ || sizeof(b) < sizeof(st->f_bsize)); ++ b = st->f_bavail * st->f_bsize; ++ br->br_wbr->wbr_bytes = b; ++ if (b >= bavail) { ++ bavail = b; ++ mfs->mfs_bindex = bindex; ++ mfs->mfs_jiffy = jiffies; ++ } ++ } ++ ++ mfs->mfsrr_bytes = bavail; ++ AuDbg("b%d\n", mfs->mfs_bindex); ++ kfree(st); ++} ++ ++static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) ++{ ++ int err; ++ struct dentry *parent; ++ struct super_block *sb; ++ struct au_wbr_mfs *mfs; ++ ++ err = au_wbr_create_exp(dentry); ++ if (err >= 0) ++ goto out; ++ ++ sb = dentry->d_sb; ++ parent = NULL; ++ if (au_ftest_wbr(flags, PARENT)) ++ parent = dget_parent(dentry); ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ mutex_lock(&mfs->mfs_lock); ++ if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) ++ || mfs->mfs_bindex < 0 ++ || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) ++ au_mfs(dentry, parent); ++ mutex_unlock(&mfs->mfs_lock); ++ err = mfs->mfs_bindex; ++ dput(parent); ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_init_mfs(struct super_block *sb) ++{ ++ struct au_wbr_mfs *mfs; ++ ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ mutex_init(&mfs->mfs_lock); ++ mfs->mfs_jiffy = 0; ++ mfs->mfs_bindex = -EROFS; ++ ++ return 0; ++} ++ ++static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused) ++{ ++ mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* most free space and then round robin */ ++static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags) ++{ ++ int err; ++ struct au_wbr_mfs *mfs; ++ ++ err = au_wbr_create_mfs(dentry, flags); ++ if (err >= 0) { ++ mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; ++ mutex_lock(&mfs->mfs_lock); ++ if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) ++ err = au_wbr_create_rr(dentry, flags); ++ mutex_unlock(&mfs->mfs_lock); ++ } ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_init_mfsrr(struct super_block *sb) ++{ ++ int err; ++ ++ au_wbr_create_init_mfs(sb); /* ignore */ ++ err = au_wbr_create_init_rr(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* top down parent and most free space */ ++static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags) ++{ ++ int err, e2; ++ unsigned long long b; ++ aufs_bindex_t bindex, bstart, bend; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent; ++ struct au_branch *br; ++ ++ err = au_wbr_create_tdp(dentry, flags); ++ if (unlikely(err < 0)) ++ goto out; ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(parent); ++ bend = au_dbtaildir(parent); ++ if (bstart == bend) ++ goto out_parent; /* success */ ++ ++ e2 = au_wbr_create_mfs(dentry, flags); ++ if (e2 < 0) ++ goto out_parent; /* success */ ++ ++ /* when the available size is equal, select upper one */ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, err); ++ b = br->br_wbr->wbr_bytes; ++ AuDbg("b%d, %llu\n", err, b); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !h_parent->d_inode) ++ continue; ++ ++ br = au_sbr(sb, bindex); ++ if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) { ++ b = br->br_wbr->wbr_bytes; ++ err = bindex; ++ AuDbg("b%d, %llu\n", err, b); ++ } ++ } ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++out_parent: ++ dput(parent); ++out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * - top down parent ++ * - most free space with parent ++ * - most free space round-robin regardless parent ++ */ ++static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags) ++{ ++ int err; ++ unsigned long long watermark; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_wbr_mfs *mfs; ++ ++ err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, err); ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ mutex_lock(&mfs->mfs_lock); ++ watermark = mfs->mfsrr_watermark; ++ mutex_unlock(&mfs->mfs_lock); ++ if (br->br_wbr->wbr_bytes < watermark) ++ /* regardless the parent dir */ ++ err = au_wbr_create_mfsrr(dentry, flags); ++ ++out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies for copyup */ ++ ++/* top down parent */ ++static int au_wbr_copyup_tdp(struct dentry *dentry) ++{ ++ return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0); ++} ++ ++/* bottom up parent */ ++static int au_wbr_copyup_bup(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bindex, bstart; ++ struct dentry *parent, *h_parent; ++ struct super_block *sb; ++ ++ err = -EROFS; ++ sb = dentry->d_sb; ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(parent); ++ for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !h_parent->d_inode) ++ continue; ++ ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = bindex; ++ break; ++ } ++ } ++ dput(parent); ++ ++ /* bottom up here */ ++ if (unlikely(err < 0)) ++ err = au_wbr_bu(sb, bstart - 1); ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* bottom up */ ++int au_wbr_do_copyup_bu(struct dentry *dentry, aufs_bindex_t bstart) ++{ ++ int err; ++ ++ err = au_wbr_bu(dentry->d_sb, bstart); ++ AuDbg("b%d\n", err); ++ if (err > bstart) ++ err = au_wbr_nonopq(dentry, err); ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_copyup_bu(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ ++ bstart = au_dbstart(dentry); ++ err = au_wbr_do_copyup_bu(dentry, bstart); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_wbr_copyup_operations au_wbr_copyup_ops[] = { ++ [AuWbrCopyup_TDP] = { ++ .copyup = au_wbr_copyup_tdp ++ }, ++ [AuWbrCopyup_BUP] = { ++ .copyup = au_wbr_copyup_bup ++ }, ++ [AuWbrCopyup_BU] = { ++ .copyup = au_wbr_copyup_bu ++ } ++}; ++ ++struct au_wbr_create_operations au_wbr_create_ops[] = { ++ [AuWbrCreate_TDP] = { ++ .create = au_wbr_create_tdp ++ }, ++ [AuWbrCreate_RR] = { ++ .create = au_wbr_create_rr, ++ .init = au_wbr_create_init_rr ++ }, ++ [AuWbrCreate_MFS] = { ++ .create = au_wbr_create_mfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_MFSV] = { ++ .create = au_wbr_create_mfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_MFSRR] = { ++ .create = au_wbr_create_mfsrr, ++ .init = au_wbr_create_init_mfsrr, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_MFSRRV] = { ++ .create = au_wbr_create_mfsrr, ++ .init = au_wbr_create_init_mfsrr, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_PMFS] = { ++ .create = au_wbr_create_pmfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_PMFSV] = { ++ .create = au_wbr_create_pmfs, ++ .init = au_wbr_create_init_mfs, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_PMFSRR] = { ++ .create = au_wbr_create_pmfsrr, ++ .init = au_wbr_create_init_mfsrr, ++ .fin = au_wbr_create_fin_mfs ++ }, ++ [AuWbrCreate_PMFSRRV] = { ++ .create = au_wbr_create_pmfsrr, ++ .init = au_wbr_create_init_mfsrr, ++ .fin = au_wbr_create_fin_mfs ++ } ++}; +diff -Nur linux-4.0.4.orig/fs/aufs/whout.c linux-4.0.4/fs/aufs/whout.c +--- linux-4.0.4.orig/fs/aufs/whout.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/whout.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,1051 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * whiteout for logical deletion and opaque directory ++ */ ++ ++#include "aufs.h" ++ ++#define WH_MASK S_IRUGO ++ ++/* ++ * If a directory contains this file, then it is opaque. We start with the ++ * .wh. flag so that it is blocked by lookup. ++ */ ++static struct qstr diropq_name = QSTR_INIT(AUFS_WH_DIROPQ, ++ sizeof(AUFS_WH_DIROPQ) - 1); ++ ++/* ++ * generate whiteout name, which is NOT terminated by NULL. ++ * @name: original d_name.name ++ * @len: original d_name.len ++ * @wh: whiteout qstr ++ * returns zero when succeeds, otherwise error. ++ * succeeded value as wh->name should be freed by kfree(). ++ */ ++int au_wh_name_alloc(struct qstr *wh, const struct qstr *name) ++{ ++ char *p; ++ ++ if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN)) ++ return -ENAMETOOLONG; ++ ++ wh->len = name->len + AUFS_WH_PFX_LEN; ++ p = kmalloc(wh->len, GFP_NOFS); ++ wh->name = p; ++ if (p) { ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len); ++ /* smp_mb(); */ ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * test if the @wh_name exists under @h_parent. ++ * @try_sio specifies the necessary of super-io. ++ */ ++int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio) ++{ ++ int err; ++ struct dentry *wh_dentry; ++ ++ if (!try_sio) ++ wh_dentry = vfsub_lkup_one(wh_name, h_parent); ++ else ++ wh_dentry = au_sio_lkup_one(wh_name, h_parent); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) { ++ if (err == -ENAMETOOLONG) ++ err = 0; ++ goto out; ++ } ++ ++ err = 0; ++ if (!wh_dentry->d_inode) ++ goto out_wh; /* success */ ++ ++ err = 1; ++ if (d_is_reg(wh_dentry)) ++ goto out_wh; /* success */ ++ ++ err = -EIO; ++ AuIOErr("%pd Invalid whiteout entry type 0%o.\n", ++ wh_dentry, wh_dentry->d_inode->i_mode); ++ ++out_wh: ++ dput(wh_dentry); ++out: ++ return err; ++} ++ ++/* ++ * test if the @h_dentry sets opaque or not. ++ */ ++int au_diropq_test(struct dentry *h_dentry) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ h_dir = h_dentry->d_inode; ++ err = au_wh_test(h_dentry, &diropq_name, ++ au_test_h_perm_sio(h_dir, MAY_EXEC)); ++ return err; ++} ++ ++/* ++ * returns a negative dentry whose name is unique and temporary. ++ */ ++struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, ++ struct qstr *prefix) ++{ ++ struct dentry *dentry; ++ int i; ++ char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN + 1], ++ *name, *p; ++ /* strict atomic_t is unnecessary here */ ++ static unsigned short cnt; ++ struct qstr qs; ++ ++ BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); ++ ++ name = defname; ++ qs.len = sizeof(defname) - DNAME_INLINE_LEN + prefix->len - 1; ++ if (unlikely(prefix->len > DNAME_INLINE_LEN)) { ++ dentry = ERR_PTR(-ENAMETOOLONG); ++ if (unlikely(qs.len > NAME_MAX)) ++ goto out; ++ dentry = ERR_PTR(-ENOMEM); ++ name = kmalloc(qs.len + 1, GFP_NOFS); ++ if (unlikely(!name)) ++ goto out; ++ } ++ ++ /* doubly whiteout-ed */ ++ memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); ++ p = name + AUFS_WH_PFX_LEN * 2; ++ memcpy(p, prefix->name, prefix->len); ++ p += prefix->len; ++ *p++ = '.'; ++ AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN); ++ ++ qs.name = name; ++ for (i = 0; i < 3; i++) { ++ sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); ++ dentry = au_sio_lkup_one(&qs, h_parent); ++ if (IS_ERR(dentry) || !dentry->d_inode) ++ goto out_name; ++ dput(dentry); ++ } ++ /* pr_warn("could not get random name\n"); */ ++ dentry = ERR_PTR(-EEXIST); ++ AuDbg("%.*s\n", AuLNPair(&qs)); ++ BUG(); ++ ++out_name: ++ if (name != defname) ++ kfree(name); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ++ * rename the @h_dentry on @br to the whiteouted temporary name. ++ */ ++int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = au_br_mnt(br) ++ }; ++ struct inode *h_dir, *delegated; ++ struct dentry *h_parent; ++ ++ h_parent = h_dentry->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ ++ h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ /* under the same dir, no need to lock_rename() */ ++ delegated = NULL; ++ err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path, &delegated); ++ AuTraceErr(err); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal rename\n"); ++ iput(delegated); ++ } ++ dput(h_path.dentry); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * functions for removing a whiteout ++ */ ++ ++static int do_unlink_wh(struct inode *h_dir, struct path *h_path) ++{ ++ int err, force; ++ struct inode *delegated; ++ ++ /* ++ * forces superio when the dir has a sticky bit. ++ * this may be a violation of unix fs semantics. ++ */ ++ force = (h_dir->i_mode & S_ISVTX) ++ && !uid_eq(current_fsuid(), h_path->dentry->d_inode->i_uid); ++ delegated = NULL; ++ err = vfsub_unlink(h_dir, h_path, &delegated, force); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ return err; ++} ++ ++int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, ++ struct dentry *dentry) ++{ ++ int err; ++ ++ err = do_unlink_wh(h_dir, h_path); ++ if (!err && dentry) ++ au_set_dbwh(dentry, -1); ++ ++ return err; ++} ++ ++static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh, ++ struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = au_br_mnt(br) ++ }; ++ ++ err = 0; ++ h_path.dentry = vfsub_lkup_one(wh, h_parent); ++ if (IS_ERR(h_path.dentry)) ++ err = PTR_ERR(h_path.dentry); ++ else { ++ if (h_path.dentry->d_inode ++ && d_is_reg(h_path.dentry)) ++ err = do_unlink_wh(h_parent->d_inode, &h_path); ++ dput(h_path.dentry); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * initialize/clean whiteout for a branch ++ */ ++ ++static void au_wh_clean(struct inode *h_dir, struct path *whpath, ++ const int isdir) ++{ ++ int err; ++ struct inode *delegated; ++ ++ if (!whpath->dentry->d_inode) ++ return; ++ ++ if (isdir) ++ err = vfsub_rmdir(h_dir, whpath); ++ else { ++ delegated = NULL; ++ err = vfsub_unlink(h_dir, whpath, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ } ++ if (unlikely(err)) ++ pr_warn("failed removing %pd (%d), ignored.\n", ++ whpath->dentry, err); ++} ++ ++static int test_linkable(struct dentry *h_root) ++{ ++ struct inode *h_dir = h_root->d_inode; ++ ++ if (h_dir->i_op->link) ++ return 0; ++ ++ pr_err("%pd (%s) doesn't support link(2), use noplink and rw+nolwh\n", ++ h_root, au_sbtype(h_root->d_sb)); ++ return -ENOSYS; ++} ++ ++/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ ++static int au_whdir(struct inode *h_dir, struct path *path) ++{ ++ int err; ++ ++ err = -EEXIST; ++ if (!path->dentry->d_inode) { ++ int mode = S_IRWXU; ++ ++ if (au_test_nfs(path->dentry->d_sb)) ++ mode |= S_IXUGO; ++ err = vfsub_mkdir(h_dir, path, mode); ++ } else if (d_is_dir(path->dentry)) ++ err = 0; ++ else ++ pr_err("unknown %pd exists\n", path->dentry); ++ ++ return err; ++} ++ ++struct au_wh_base { ++ const struct qstr *name; ++ struct dentry *dentry; ++}; ++ ++static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/0); ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++} ++ ++/* ++ * returns tri-state, ++ * minus: error, caller should print the message ++ * zero: succuess ++ * plus: error, caller should NOT print the message ++ */ ++static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, ++ int do_plink, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ h_dir = h_root->d_inode; ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/0); ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ if (do_plink) { ++ err = test_linkable(h_root); ++ if (unlikely(err)) { ++ err = 1; ++ goto out; ++ } ++ ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); ++ } else ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); ++ ++out: ++ return err; ++} ++ ++/* ++ * for the moment, aufs supports the branch filesystem which does not support ++ * link(2). testing on FAT which does not support i_op->setattr() fully either, ++ * copyup failed. finally, such filesystem will not be used as the writable ++ * branch. ++ * ++ * returns tri-state, see above. ++ */ ++static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, ++ int do_plink, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ WbrWhMustWriteLock(wbr); ++ ++ err = test_linkable(h_root); ++ if (unlikely(err)) { ++ err = 1; ++ goto out; ++ } ++ ++ /* ++ * todo: should this create be done in /sbin/mount.aufs helper? ++ */ ++ err = -EEXIST; ++ h_dir = h_root->d_inode; ++ if (!base[AuBrWh_BASE].dentry->d_inode) { ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); ++ } else if (d_is_reg(base[AuBrWh_BASE].dentry)) ++ err = 0; ++ else ++ pr_err("unknown %pd2 exists\n", base[AuBrWh_BASE].dentry); ++ if (unlikely(err)) ++ goto out; ++ ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ if (do_plink) { ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); ++ } else ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); ++ ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); ++ ++out: ++ return err; ++} ++ ++/* ++ * initialize the whiteout base file/dir for @br. ++ */ ++int au_wh_init(struct au_branch *br, struct super_block *sb) ++{ ++ int err, i; ++ const unsigned char do_plink ++ = !!au_opt_test(au_mntflags(sb), PLINK); ++ struct inode *h_dir; ++ struct path path = br->br_path; ++ struct dentry *h_root = path.dentry; ++ struct au_wbr *wbr = br->br_wbr; ++ static const struct qstr base_name[] = { ++ [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, ++ sizeof(AUFS_BASE_NAME) - 1), ++ [AuBrWh_PLINK] = QSTR_INIT(AUFS_PLINKDIR_NAME, ++ sizeof(AUFS_PLINKDIR_NAME) - 1), ++ [AuBrWh_ORPH] = QSTR_INIT(AUFS_ORPHDIR_NAME, ++ sizeof(AUFS_ORPHDIR_NAME) - 1) ++ }; ++ struct au_wh_base base[] = { ++ [AuBrWh_BASE] = { ++ .name = base_name + AuBrWh_BASE, ++ .dentry = NULL ++ }, ++ [AuBrWh_PLINK] = { ++ .name = base_name + AuBrWh_PLINK, ++ .dentry = NULL ++ }, ++ [AuBrWh_ORPH] = { ++ .name = base_name + AuBrWh_ORPH, ++ .dentry = NULL ++ } ++ }; ++ ++ if (wbr) ++ WbrWhMustWriteLock(wbr); ++ ++ for (i = 0; i < AuBrWh_Last; i++) { ++ /* doubly whiteouted */ ++ struct dentry *d; ++ ++ d = au_wh_lkup(h_root, (void *)base[i].name, br); ++ err = PTR_ERR(d); ++ if (IS_ERR(d)) ++ goto out; ++ ++ base[i].dentry = d; ++ AuDebugOn(wbr ++ && wbr->wbr_wh[i] ++ && wbr->wbr_wh[i] != base[i].dentry); ++ } ++ ++ if (wbr) ++ for (i = 0; i < AuBrWh_Last; i++) { ++ dput(wbr->wbr_wh[i]); ++ wbr->wbr_wh[i] = NULL; ++ } ++ ++ err = 0; ++ if (!au_br_writable(br->br_perm)) { ++ h_dir = h_root->d_inode; ++ au_wh_init_ro(h_dir, base, &path); ++ } else if (!au_br_wh_linkable(br->br_perm)) { ++ err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path); ++ if (err > 0) ++ goto out; ++ else if (err) ++ goto out_err; ++ } else { ++ err = au_wh_init_rw(h_root, wbr, do_plink, base, &path); ++ if (err > 0) ++ goto out; ++ else if (err) ++ goto out_err; ++ } ++ goto out; /* success */ ++ ++out_err: ++ pr_err("an error(%d) on the writable branch %pd(%s)\n", ++ err, h_root, au_sbtype(h_root->d_sb)); ++out: ++ for (i = 0; i < AuBrWh_Last; i++) ++ dput(base[i].dentry); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * whiteouts are all hard-linked usually. ++ * when its link count reaches a ceiling, we create a new whiteout base ++ * asynchronously. ++ */ ++ ++struct reinit_br_wh { ++ struct super_block *sb; ++ struct au_branch *br; ++}; ++ ++static void reinit_br_wh(void *arg) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct path h_path; ++ struct reinit_br_wh *a = arg; ++ struct au_wbr *wbr; ++ struct inode *dir, *delegated; ++ struct dentry *h_root; ++ struct au_hinode *hdir; ++ ++ err = 0; ++ wbr = a->br->br_wbr; ++ /* big aufs lock */ ++ si_noflush_write_lock(a->sb); ++ if (!au_br_writable(a->br->br_perm)) ++ goto out; ++ bindex = au_br_index(a->sb, a->br->br_id); ++ if (unlikely(bindex < 0)) ++ goto out; ++ ++ di_read_lock_parent(a->sb->s_root, AuLock_IR); ++ dir = a->sb->s_root->d_inode; ++ hdir = au_hi(dir, bindex); ++ h_root = au_h_dptr(a->sb->s_root, bindex); ++ AuDebugOn(h_root != au_br_dentry(a->br)); ++ ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ wbr_wh_write_lock(wbr); ++ err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, ++ h_root, a->br); ++ if (!err) { ++ h_path.dentry = wbr->wbr_whbase; ++ h_path.mnt = au_br_mnt(a->br); ++ delegated = NULL; ++ err = vfsub_unlink(hdir->hi_inode, &h_path, &delegated, ++ /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ } else { ++ pr_warn("%pd is moved, ignored\n", wbr->wbr_whbase); ++ err = 0; ++ } ++ dput(wbr->wbr_whbase); ++ wbr->wbr_whbase = NULL; ++ if (!err) ++ err = au_wh_init(a->br, a->sb); ++ wbr_wh_write_unlock(wbr); ++ au_hn_imtx_unlock(hdir); ++ di_read_unlock(a->sb->s_root, AuLock_IR); ++ if (!err) ++ au_fhsm_wrote(a->sb, bindex, /*force*/0); ++ ++out: ++ if (wbr) ++ atomic_dec(&wbr->wbr_wh_running); ++ atomic_dec(&a->br->br_count); ++ si_write_unlock(a->sb); ++ au_nwt_done(&au_sbi(a->sb)->si_nowait); ++ kfree(arg); ++ if (unlikely(err)) ++ AuIOErr("err %d\n", err); ++} ++ ++static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) ++{ ++ int do_dec, wkq_err; ++ struct reinit_br_wh *arg; ++ ++ do_dec = 1; ++ if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1) ++ goto out; ++ ++ /* ignore ENOMEM */ ++ arg = kmalloc(sizeof(*arg), GFP_NOFS); ++ if (arg) { ++ /* ++ * dec(wh_running), kfree(arg) and dec(br_count) ++ * in reinit function ++ */ ++ arg->sb = sb; ++ arg->br = br; ++ atomic_inc(&br->br_count); ++ wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*flags*/0); ++ if (unlikely(wkq_err)) { ++ atomic_dec(&br->br_wbr->wbr_wh_running); ++ atomic_dec(&br->br_count); ++ kfree(arg); ++ } ++ do_dec = 0; ++ } ++ ++out: ++ if (do_dec) ++ atomic_dec(&br->br_wbr->wbr_wh_running); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create the whiteout @wh. ++ */ ++static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, ++ struct dentry *wh) ++{ ++ int err; ++ struct path h_path = { ++ .dentry = wh ++ }; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ struct dentry *h_parent; ++ struct inode *h_dir, *delegated; ++ ++ h_parent = wh->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ ++ br = au_sbr(sb, bindex); ++ h_path.mnt = au_br_mnt(br); ++ wbr = br->br_wbr; ++ wbr_wh_read_lock(wbr); ++ if (wbr->wbr_whbase) { ++ delegated = NULL; ++ err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path, &delegated); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal link\n"); ++ iput(delegated); ++ } ++ if (!err || err != -EMLINK) ++ goto out; ++ ++ /* link count full. re-initialize br_whbase. */ ++ kick_reinit_br_wh(sb, br); ++ } ++ ++ /* return this error in this context */ ++ err = vfsub_create(h_dir, &h_path, WH_MASK, /*want_excl*/true); ++ if (!err) ++ au_fhsm_wrote(sb, bindex, /*force*/0); ++ ++out: ++ wbr_wh_read_unlock(wbr); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create or remove the diropq. ++ */ ++static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags) ++{ ++ struct dentry *opq_dentry, *h_dentry; ++ struct super_block *sb; ++ struct au_branch *br; ++ int err; ++ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, bindex); ++ h_dentry = au_h_dptr(dentry, bindex); ++ opq_dentry = vfsub_lkup_one(&diropq_name, h_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out; ++ ++ if (au_ftest_diropq(flags, CREATE)) { ++ err = link_or_create_wh(sb, bindex, opq_dentry); ++ if (!err) { ++ au_set_dbdiropq(dentry, bindex); ++ goto out; /* success */ ++ } ++ } else { ++ struct path tmp = { ++ .dentry = opq_dentry, ++ .mnt = au_br_mnt(br) ++ }; ++ err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp); ++ if (!err) ++ au_set_dbdiropq(dentry, -1); ++ } ++ dput(opq_dentry); ++ opq_dentry = ERR_PTR(err); ++ ++out: ++ return opq_dentry; ++} ++ ++struct do_diropq_args { ++ struct dentry **errp; ++ struct dentry *dentry; ++ aufs_bindex_t bindex; ++ unsigned int flags; ++}; ++ ++static void call_do_diropq(void *args) ++{ ++ struct do_diropq_args *a = args; ++ *a->errp = do_diropq(a->dentry, a->bindex, a->flags); ++} ++ ++struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags) ++{ ++ struct dentry *diropq, *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!au_test_h_perm_sio(h_dentry->d_inode, MAY_EXEC | MAY_WRITE)) ++ diropq = do_diropq(dentry, bindex, flags); ++ else { ++ int wkq_err; ++ struct do_diropq_args args = { ++ .errp = &diropq, ++ .dentry = dentry, ++ .bindex = bindex, ++ .flags = flags ++ }; ++ ++ wkq_err = au_wkq_wait(call_do_diropq, &args); ++ if (unlikely(wkq_err)) ++ diropq = ERR_PTR(wkq_err); ++ } ++ ++ return diropq; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * lookup whiteout dentry. ++ * @h_parent: lower parent dentry which must exist and be locked ++ * @base_name: name of dentry which will be whiteouted ++ * returns dentry for whiteout. ++ */ ++struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, ++ struct au_branch *br) ++{ ++ int err; ++ struct qstr wh_name; ++ struct dentry *wh_dentry; ++ ++ err = au_wh_name_alloc(&wh_name, base_name); ++ wh_dentry = ERR_PTR(err); ++ if (!err) { ++ wh_dentry = vfsub_lkup_one(&wh_name, h_parent); ++ kfree(wh_name.name); ++ } ++ return wh_dentry; ++} ++ ++/* ++ * link/create a whiteout for @dentry on @bindex. ++ */ ++struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent) ++{ ++ struct dentry *wh_dentry; ++ struct super_block *sb; ++ int err; ++ ++ sb = dentry->d_sb; ++ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); ++ if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) { ++ err = link_or_create_wh(sb, bindex, wh_dentry); ++ if (!err) { ++ au_set_dbwh(dentry, bindex); ++ au_fhsm_wrote(sb, bindex, /*force*/0); ++ } else { ++ dput(wh_dentry); ++ wh_dentry = ERR_PTR(err); ++ } ++ } ++ ++ return wh_dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Delete all whiteouts in this directory on branch bindex. */ ++static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist, ++ aufs_bindex_t bindex, struct au_branch *br) ++{ ++ int err; ++ unsigned long ul, n; ++ struct qstr wh_name; ++ char *p; ++ struct hlist_head *head; ++ struct au_vdir_wh *pos; ++ struct au_vdir_destr *str; ++ ++ err = -ENOMEM; ++ p = (void *)__get_free_page(GFP_NOFS); ++ wh_name.name = p; ++ if (unlikely(!wh_name.name)) ++ goto out; ++ ++ err = 0; ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ p += AUFS_WH_PFX_LEN; ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (ul = 0; !err && ul < n; ul++, head++) { ++ hlist_for_each_entry(pos, head, wh_hash) { ++ if (pos->wh_bindex != bindex) ++ continue; ++ ++ str = &pos->wh_str; ++ if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) { ++ memcpy(p, str->name, str->len); ++ wh_name.len = AUFS_WH_PFX_LEN + str->len; ++ err = unlink_wh_name(h_dentry, &wh_name, br); ++ if (!err) ++ continue; ++ break; ++ } ++ AuIOErr("whiteout name too long %.*s\n", ++ str->len, str->name); ++ err = -EIO; ++ break; ++ } ++ } ++ free_page((unsigned long)wh_name.name); ++ ++out: ++ return err; ++} ++ ++struct del_wh_children_args { ++ int *errp; ++ struct dentry *h_dentry; ++ struct au_nhash *whlist; ++ aufs_bindex_t bindex; ++ struct au_branch *br; ++}; ++ ++static void call_del_wh_children(void *args) ++{ ++ struct del_wh_children_args *a = args; ++ *a->errp = del_wh_children(a->h_dentry, a->whlist, a->bindex, a->br); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) ++{ ++ struct au_whtmp_rmdir *whtmp; ++ int err; ++ unsigned int rdhash; ++ ++ SiMustAnyLock(sb); ++ ++ whtmp = kmalloc(sizeof(*whtmp), gfp); ++ if (unlikely(!whtmp)) { ++ whtmp = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ whtmp->dir = NULL; ++ whtmp->br = NULL; ++ whtmp->wh_dentry = NULL; ++ /* no estimation for dir size */ ++ rdhash = au_sbi(sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = AUFS_RDHASH_DEF; ++ err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp); ++ if (unlikely(err)) { ++ kfree(whtmp); ++ whtmp = ERR_PTR(err); ++ } ++ ++out: ++ return whtmp; ++} ++ ++void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) ++{ ++ if (whtmp->br) ++ atomic_dec(&whtmp->br->br_count); ++ dput(whtmp->wh_dentry); ++ iput(whtmp->dir); ++ au_nhash_wh_free(&whtmp->whlist); ++ kfree(whtmp); ++} ++ ++/* ++ * rmdir the whiteouted temporary named dir @h_dentry. ++ * @whlist: whiteouted children. ++ */ ++int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_nhash *whlist) ++{ ++ int err; ++ unsigned int h_nlink; ++ struct path h_tmp; ++ struct inode *wh_inode, *h_dir; ++ struct au_branch *br; ++ ++ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ ++ IMustLock(h_dir); ++ ++ br = au_sbr(dir->i_sb, bindex); ++ wh_inode = wh_dentry->d_inode; ++ mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD); ++ ++ /* ++ * someone else might change some whiteouts while we were sleeping. ++ * it means this whlist may have an obsoleted entry. ++ */ ++ if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE)) ++ err = del_wh_children(wh_dentry, whlist, bindex, br); ++ else { ++ int wkq_err; ++ struct del_wh_children_args args = { ++ .errp = &err, ++ .h_dentry = wh_dentry, ++ .whlist = whlist, ++ .bindex = bindex, ++ .br = br ++ }; ++ ++ wkq_err = au_wkq_wait(call_del_wh_children, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ mutex_unlock(&wh_inode->i_mutex); ++ ++ if (!err) { ++ h_tmp.dentry = wh_dentry; ++ h_tmp.mnt = au_br_mnt(br); ++ h_nlink = h_dir->i_nlink; ++ err = vfsub_rmdir(h_dir, &h_tmp); ++ /* some fs doesn't change the parent nlink in some cases */ ++ h_nlink -= h_dir->i_nlink; ++ } ++ ++ if (!err) { ++ if (au_ibstart(dir) == bindex) { ++ /* todo: dir->i_mutex is necessary */ ++ au_cpup_attr_timesizes(dir); ++ if (h_nlink) ++ vfsub_drop_nlink(dir); ++ } ++ return 0; /* success */ ++ } ++ ++ pr_warn("failed removing %pd(%d), ignored\n", wh_dentry, err); ++ return err; ++} ++ ++static void call_rmdir_whtmp(void *args) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct au_whtmp_rmdir *a = args; ++ struct super_block *sb; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ struct au_hinode *hdir; ++ ++ /* rmdir by nfsd may cause deadlock with this i_mutex */ ++ /* mutex_lock(&a->dir->i_mutex); */ ++ err = -EROFS; ++ sb = a->dir->i_sb; ++ si_read_lock(sb, !AuLock_FLUSH); ++ if (!au_br_writable(a->br->br_perm)) ++ goto out; ++ bindex = au_br_index(sb, a->br->br_id); ++ if (unlikely(bindex < 0)) ++ goto out; ++ ++ err = -EIO; ++ ii_write_lock_parent(a->dir); ++ h_parent = dget_parent(a->wh_dentry); ++ h_dir = h_parent->d_inode; ++ hdir = au_hi(a->dir, bindex); ++ err = vfsub_mnt_want_write(au_br_mnt(a->br)); ++ if (unlikely(err)) ++ goto out_mnt; ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, ++ a->br); ++ if (!err) ++ err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); ++ au_hn_imtx_unlock(hdir); ++ vfsub_mnt_drop_write(au_br_mnt(a->br)); ++ ++out_mnt: ++ dput(h_parent); ++ ii_write_unlock(a->dir); ++out: ++ /* mutex_unlock(&a->dir->i_mutex); */ ++ au_whtmp_rmdir_free(a); ++ si_read_unlock(sb); ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ if (unlikely(err)) ++ AuIOErr("err %d\n", err); ++} ++ ++void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_whtmp_rmdir *args) ++{ ++ int wkq_err; ++ struct super_block *sb; ++ ++ IMustLock(dir); ++ ++ /* all post-process will be done in do_rmdir_whtmp(). */ ++ sb = dir->i_sb; ++ args->dir = au_igrab(dir); ++ args->br = au_sbr(sb, bindex); ++ atomic_inc(&args->br->br_count); ++ args->wh_dentry = dget(wh_dentry); ++ wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb, /*flags*/0); ++ if (unlikely(wkq_err)) { ++ pr_warn("rmdir error %pd (%d), ignored\n", wh_dentry, wkq_err); ++ au_whtmp_rmdir_free(args); ++ } ++} +diff -Nur linux-4.0.4.orig/fs/aufs/whout.h linux-4.0.4/fs/aufs/whout.h +--- linux-4.0.4.orig/fs/aufs/whout.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/whout.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,72 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * whiteout for logical deletion and opaque directory ++ */ ++ ++#ifndef __AUFS_WHOUT_H__ ++#define __AUFS_WHOUT_H__ ++ ++#ifdef __KERNEL__ ++ ++#include "dir.h" ++ ++/* whout.c */ ++int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); ++int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio); ++int au_diropq_test(struct dentry *h_dentry); ++struct au_branch; ++struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, ++ struct qstr *prefix); ++int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); ++int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, ++ struct dentry *dentry); ++int au_wh_init(struct au_branch *br, struct super_block *sb); ++ ++/* diropq flags */ ++#define AuDiropq_CREATE 1 ++#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) ++#define au_fset_diropq(flags, name) \ ++ do { (flags) |= AuDiropq_##name; } while (0) ++#define au_fclr_diropq(flags, name) \ ++ do { (flags) &= ~AuDiropq_##name; } while (0) ++ ++struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags); ++struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, ++ struct au_branch *br); ++struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent); ++ ++/* real rmdir for the whiteout-ed dir */ ++struct au_whtmp_rmdir { ++ struct inode *dir; ++ struct au_branch *br; ++ struct dentry *wh_dentry; ++ struct au_nhash whlist; ++}; ++ ++struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp); ++void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp); ++int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_nhash *whlist); ++void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_whtmp_rmdir *args); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct dentry *au_diropq_create(struct dentry *dentry, ++ aufs_bindex_t bindex) ++{ ++ return au_diropq_sio(dentry, bindex, AuDiropq_CREATE); ++} ++ ++static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE)); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_WHOUT_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/wkq.c linux-4.0.4/fs/aufs/wkq.c +--- linux-4.0.4.orig/fs/aufs/wkq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/wkq.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,200 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * workqueue for asynchronous/super-io operations ++ * todo: try new dredential scheme ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* internal workqueue named AUFS_WKQ_NAME */ ++ ++static struct workqueue_struct *au_wkq; ++ ++struct au_wkinfo { ++ struct work_struct wk; ++ struct kobject *kobj; ++ ++ unsigned int flags; /* see wkq.h */ ++ ++ au_wkq_func_t func; ++ void *args; ++ ++ struct completion *comp; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void wkq_func(struct work_struct *wk) ++{ ++ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); ++ ++ AuDebugOn(!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)); ++ AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY); ++ ++ wkinfo->func(wkinfo->args); ++ if (au_ftest_wkq(wkinfo->flags, WAIT)) ++ complete(wkinfo->comp); ++ else { ++ kobject_put(wkinfo->kobj); ++ module_put(THIS_MODULE); /* todo: ?? */ ++ kfree(wkinfo); ++ } ++} ++ ++/* ++ * Since struct completion is large, try allocating it dynamically. ++ */ ++#if 1 /* defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) */ ++#define AuWkqCompDeclare(name) struct completion *comp = NULL ++ ++static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) ++{ ++ *comp = kmalloc(sizeof(**comp), GFP_NOFS); ++ if (*comp) { ++ init_completion(*comp); ++ wkinfo->comp = *comp; ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++static void au_wkq_comp_free(struct completion *comp) ++{ ++ kfree(comp); ++} ++ ++#else ++ ++/* no braces */ ++#define AuWkqCompDeclare(name) \ ++ DECLARE_COMPLETION_ONSTACK(_ ## name); \ ++ struct completion *comp = &_ ## name ++ ++static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) ++{ ++ wkinfo->comp = *comp; ++ return 0; ++} ++ ++static void au_wkq_comp_free(struct completion *comp __maybe_unused) ++{ ++ /* empty */ ++} ++#endif /* 4KSTACKS */ ++ ++static void au_wkq_run(struct au_wkinfo *wkinfo) ++{ ++ if (au_ftest_wkq(wkinfo->flags, NEST)) { ++ if (au_wkq_test()) { ++ AuWarn1("wkq from wkq, unless silly-rename on NFS," ++ " due to a dead dir by UDBA?\n"); ++ AuDebugOn(au_ftest_wkq(wkinfo->flags, WAIT)); ++ } ++ } else ++ au_dbg_verify_kthread(); ++ ++ if (au_ftest_wkq(wkinfo->flags, WAIT)) { ++ INIT_WORK_ONSTACK(&wkinfo->wk, wkq_func); ++ queue_work(au_wkq, &wkinfo->wk); ++ } else { ++ INIT_WORK(&wkinfo->wk, wkq_func); ++ schedule_work(&wkinfo->wk); ++ } ++} ++ ++/* ++ * Be careful. It is easy to make deadlock happen. ++ * processA: lock, wkq and wait ++ * processB: wkq and wait, lock in wkq ++ * --> deadlock ++ */ ++int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args) ++{ ++ int err; ++ AuWkqCompDeclare(comp); ++ struct au_wkinfo wkinfo = { ++ .flags = flags, ++ .func = func, ++ .args = args ++ }; ++ ++ err = au_wkq_comp_alloc(&wkinfo, &comp); ++ if (!err) { ++ au_wkq_run(&wkinfo); ++ /* no timeout, no interrupt */ ++ wait_for_completion(wkinfo.comp); ++ au_wkq_comp_free(comp); ++ destroy_work_on_stack(&wkinfo.wk); ++ } ++ ++ return err; ++ ++} ++ ++/* ++ * Note: dget/dput() in func for aufs dentries are not supported. It will be a ++ * problem in a concurrent umounting. ++ */ ++int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, ++ unsigned int flags) ++{ ++ int err; ++ struct au_wkinfo *wkinfo; ++ ++ atomic_inc(&au_sbi(sb)->si_nowait.nw_len); ++ ++ /* ++ * wkq_func() must free this wkinfo. ++ * it highly depends upon the implementation of workqueue. ++ */ ++ err = 0; ++ wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS); ++ if (wkinfo) { ++ wkinfo->kobj = &au_sbi(sb)->si_kobj; ++ wkinfo->flags = flags & ~AuWkq_WAIT; ++ wkinfo->func = func; ++ wkinfo->args = args; ++ wkinfo->comp = NULL; ++ kobject_get(wkinfo->kobj); ++ __module_get(THIS_MODULE); /* todo: ?? */ ++ ++ au_wkq_run(wkinfo); ++ } else { ++ err = -ENOMEM; ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_nwt_init(struct au_nowait_tasks *nwt) ++{ ++ atomic_set(&nwt->nw_len, 0); ++ /* smp_mb(); */ /* atomic_set */ ++ init_waitqueue_head(&nwt->nw_wq); ++} ++ ++void au_wkq_fin(void) ++{ ++ destroy_workqueue(au_wkq); ++} ++ ++int __init au_wkq_init(void) ++{ ++ int err; ++ ++ err = 0; ++ au_wkq = alloc_workqueue(AUFS_WKQ_NAME, 0, WQ_DFL_ACTIVE); ++ if (IS_ERR(au_wkq)) ++ err = PTR_ERR(au_wkq); ++ else if (!au_wkq) ++ err = -ENOMEM; ++ ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/aufs/wkq.h linux-4.0.4/fs/aufs/wkq.h +--- linux-4.0.4.orig/fs/aufs/wkq.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/wkq.h 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,78 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * workqueue for asynchronous/super-io operations ++ * todo: try new credentials management scheme ++ */ ++ ++#ifndef __AUFS_WKQ_H__ ++#define __AUFS_WKQ_H__ ++ ++#ifdef __KERNEL__ ++ ++struct super_block; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * in the next operation, wait for the 'nowait' tasks in system-wide workqueue ++ */ ++struct au_nowait_tasks { ++ atomic_t nw_len; ++ wait_queue_head_t nw_wq; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++typedef void (*au_wkq_func_t)(void *args); ++ ++/* wkq flags */ ++#define AuWkq_WAIT 1 ++#define AuWkq_NEST (1 << 1) ++#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) ++#define au_fset_wkq(flags, name) \ ++ do { (flags) |= AuWkq_##name; } while (0) ++#define au_fclr_wkq(flags, name) \ ++ do { (flags) &= ~AuWkq_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_HNOTIFY ++#undef AuWkq_NEST ++#define AuWkq_NEST 0 ++#endif ++ ++/* wkq.c */ ++int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args); ++int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb, ++ unsigned int flags); ++void au_nwt_init(struct au_nowait_tasks *nwt); ++int __init au_wkq_init(void); ++void au_wkq_fin(void); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline int au_wkq_test(void) ++{ ++ return current->flags & PF_WQ_WORKER; ++} ++ ++static inline int au_wkq_wait(au_wkq_func_t func, void *args) ++{ ++ return au_wkq_do_wait(AuWkq_WAIT, func, args); ++} ++ ++static inline void au_nwt_done(struct au_nowait_tasks *nwt) ++{ ++ if (atomic_dec_and_test(&nwt->nw_len)) ++ wake_up_all(&nwt->nw_wq); ++} ++ ++static inline int au_nwt_flush(struct au_nowait_tasks *nwt) ++{ ++ wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); ++ return 0; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_WKQ_H__ */ +diff -Nur linux-4.0.4.orig/fs/aufs/xattr.c linux-4.0.4/fs/aufs/xattr.c +--- linux-4.0.4.orig/fs/aufs/xattr.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/xattr.c 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,331 @@ ++/* ++ * Copyright (C) 2014-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * handling xattr functions ++ */ ++ ++#include ++#include "aufs.h" ++ ++static int au_xattr_ignore(int err, char *name, unsigned int ignore_flags) ++{ ++ if (!ignore_flags) ++ goto out; ++ switch (err) { ++ case -ENOMEM: ++ case -EDQUOT: ++ goto out; ++ } ++ ++ if ((ignore_flags & AuBrAttr_ICEX) == AuBrAttr_ICEX) { ++ err = 0; ++ goto out; ++ } ++ ++#define cmp(brattr, prefix) do { \ ++ if (!strncmp(name, XATTR_##prefix##_PREFIX, \ ++ XATTR_##prefix##_PREFIX_LEN)) { \ ++ if (ignore_flags & AuBrAttr_ICEX_##brattr) \ ++ err = 0; \ ++ goto out; \ ++ } \ ++ } while (0) ++ ++ cmp(SEC, SECURITY); ++ cmp(SYS, SYSTEM); ++ cmp(TR, TRUSTED); ++ cmp(USR, USER); ++#undef cmp ++ ++ if (ignore_flags & AuBrAttr_ICEX_OTH) ++ err = 0; ++ ++out: ++ return err; ++} ++ ++static const int au_xattr_out_of_list = AuBrAttr_ICEX_OTH << 1; ++ ++static int au_do_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, ++ char *name, char **buf, unsigned int ignore_flags, ++ unsigned int verbose) ++{ ++ int err; ++ ssize_t ssz; ++ struct inode *h_idst; ++ ++ ssz = vfs_getxattr_alloc(h_src, name, buf, 0, GFP_NOFS); ++ err = ssz; ++ if (unlikely(err <= 0)) { ++ if (err == -ENODATA ++ || (err == -EOPNOTSUPP ++ && ((ignore_flags & au_xattr_out_of_list) ++ || (au_test_nfs_noacl(h_src->d_inode) ++ && (!strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) ++ || !strcmp(name, ++ XATTR_NAME_POSIX_ACL_DEFAULT)))) ++ )) ++ err = 0; ++ if (err && (verbose || au_debug_test())) ++ pr_err("%s, err %d\n", name, err); ++ goto out; ++ } ++ ++ /* unlock it temporary */ ++ h_idst = h_dst->d_inode; ++ mutex_unlock(&h_idst->i_mutex); ++ err = vfsub_setxattr(h_dst, name, *buf, ssz, /*flags*/0); ++ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); ++ if (unlikely(err)) { ++ if (verbose || au_debug_test()) ++ pr_err("%s, err %d\n", name, err); ++ err = au_xattr_ignore(err, name, ignore_flags); ++ } ++ ++out: ++ return err; ++} ++ ++int au_cpup_xattr(struct dentry *h_dst, struct dentry *h_src, int ignore_flags, ++ unsigned int verbose) ++{ ++ int err, unlocked, acl_access, acl_default; ++ ssize_t ssz; ++ struct inode *h_isrc, *h_idst; ++ char *value, *p, *o, *e; ++ ++ /* try stopping to update the source inode while we are referencing */ ++ /* there should not be the parent-child relationship between them */ ++ h_isrc = h_src->d_inode; ++ h_idst = h_dst->d_inode; ++ mutex_unlock(&h_idst->i_mutex); ++ mutex_lock_nested(&h_isrc->i_mutex, AuLsc_I_CHILD); ++ mutex_lock_nested(&h_idst->i_mutex, AuLsc_I_CHILD2); ++ unlocked = 0; ++ ++ /* some filesystems don't list POSIX ACL, for example tmpfs */ ++ ssz = vfs_listxattr(h_src, NULL, 0); ++ err = ssz; ++ if (unlikely(err < 0)) { ++ AuTraceErr(err); ++ if (err == -ENODATA ++ || err == -EOPNOTSUPP) ++ err = 0; /* ignore */ ++ goto out; ++ } ++ ++ err = 0; ++ p = NULL; ++ o = NULL; ++ if (ssz) { ++ err = -ENOMEM; ++ p = kmalloc(ssz, GFP_NOFS); ++ o = p; ++ if (unlikely(!p)) ++ goto out; ++ err = vfs_listxattr(h_src, p, ssz); ++ } ++ mutex_unlock(&h_isrc->i_mutex); ++ unlocked = 1; ++ AuDbg("err %d, ssz %zd\n", err, ssz); ++ if (unlikely(err < 0)) ++ goto out_free; ++ ++ err = 0; ++ e = p + ssz; ++ value = NULL; ++ acl_access = 0; ++ acl_default = 0; ++ while (!err && p < e) { ++ acl_access |= !strncmp(p, XATTR_NAME_POSIX_ACL_ACCESS, ++ sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1); ++ acl_default |= !strncmp(p, XATTR_NAME_POSIX_ACL_DEFAULT, ++ sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) ++ - 1); ++ err = au_do_cpup_xattr(h_dst, h_src, p, &value, ignore_flags, ++ verbose); ++ p += strlen(p) + 1; ++ } ++ AuTraceErr(err); ++ ignore_flags |= au_xattr_out_of_list; ++ if (!err && !acl_access) { ++ err = au_do_cpup_xattr(h_dst, h_src, ++ XATTR_NAME_POSIX_ACL_ACCESS, &value, ++ ignore_flags, verbose); ++ AuTraceErr(err); ++ } ++ if (!err && !acl_default) { ++ err = au_do_cpup_xattr(h_dst, h_src, ++ XATTR_NAME_POSIX_ACL_DEFAULT, &value, ++ ignore_flags, verbose); ++ AuTraceErr(err); ++ } ++ ++ kfree(value); ++ ++out_free: ++ kfree(o); ++out: ++ if (!unlocked) ++ mutex_unlock(&h_isrc->i_mutex); ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++enum { ++ AU_XATTR_LIST, ++ AU_XATTR_GET ++}; ++ ++struct au_lgxattr { ++ int type; ++ union { ++ struct { ++ char *list; ++ size_t size; ++ } list; ++ struct { ++ const char *name; ++ void *value; ++ size_t size; ++ } get; ++ } u; ++}; ++ ++static ssize_t au_lgxattr(struct dentry *dentry, struct au_lgxattr *arg) ++{ ++ ssize_t err; ++ struct path h_path; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ err = au_h_path_getattr(dentry, /*force*/1, &h_path); ++ if (unlikely(err)) ++ goto out_si; ++ if (unlikely(!h_path.dentry)) ++ /* illegally overlapped or something */ ++ goto out_di; /* pretending success */ ++ ++ /* always topmost entry only */ ++ switch (arg->type) { ++ case AU_XATTR_LIST: ++ err = vfs_listxattr(h_path.dentry, ++ arg->u.list.list, arg->u.list.size); ++ break; ++ case AU_XATTR_GET: ++ err = vfs_getxattr(h_path.dentry, ++ arg->u.get.name, arg->u.get.value, ++ arg->u.get.size); ++ break; ++ } ++ ++out_di: ++ di_read_unlock(dentry, AuLock_IR); ++out_si: ++ si_read_unlock(sb); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t size) ++{ ++ struct au_lgxattr arg = { ++ .type = AU_XATTR_LIST, ++ .u.list = { ++ .list = list, ++ .size = size ++ }, ++ }; ++ ++ return au_lgxattr(dentry, &arg); ++} ++ ++ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value, ++ size_t size) ++{ ++ struct au_lgxattr arg = { ++ .type = AU_XATTR_GET, ++ .u.get = { ++ .name = name, ++ .value = value, ++ .size = size ++ }, ++ }; ++ ++ return au_lgxattr(dentry, &arg); ++} ++ ++int aufs_setxattr(struct dentry *dentry, const char *name, const void *value, ++ size_t size, int flags) ++{ ++ struct au_srxattr arg = { ++ .type = AU_XATTR_SET, ++ .u.set = { ++ .name = name, ++ .value = value, ++ .size = size, ++ .flags = flags ++ }, ++ }; ++ ++ return au_srxattr(dentry, &arg); ++} ++ ++int aufs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct au_srxattr arg = { ++ .type = AU_XATTR_REMOVE, ++ .u.remove = { ++ .name = name ++ }, ++ }; ++ ++ return au_srxattr(dentry, &arg); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#if 0 ++static size_t au_xattr_list(struct dentry *dentry, char *list, size_t list_size, ++ const char *name, size_t name_len, int type) ++{ ++ return aufs_listxattr(dentry, list, list_size); ++} ++ ++static int au_xattr_get(struct dentry *dentry, const char *name, void *buffer, ++ size_t size, int type) ++{ ++ return aufs_getxattr(dentry, name, buffer, size); ++} ++ ++static int au_xattr_set(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags, int type) ++{ ++ return aufs_setxattr(dentry, name, value, size, flags); ++} ++ ++static const struct xattr_handler au_xattr_handler = { ++ /* no prefix, no flags */ ++ .list = au_xattr_list, ++ .get = au_xattr_get, ++ .set = au_xattr_set ++ /* why no remove? */ ++}; ++ ++static const struct xattr_handler *au_xattr_handlers[] = { ++ &au_xattr_handler ++}; ++ ++void au_xattr_init(struct super_block *sb) ++{ ++ /* sb->s_xattr = au_xattr_handlers; */ ++} ++#endif +diff -Nur linux-4.0.4.orig/fs/aufs/xino.c linux-4.0.4/fs/aufs/xino.c +--- linux-4.0.4.orig/fs/aufs/xino.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/fs/aufs/xino.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,1305 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++/* ++ * external inode number translation table and bitmap ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++/* todo: unnecessary to support mmap_sem since kernel-space? */ ++ssize_t xino_fread(au_readf_t func, struct file *file, void *kbuf, size_t size, ++ loff_t *pos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ /* todo: signal_pending? */ ++ err = func(file, buf.u, size, pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ set_fs(oldfs); ++ ++#if 0 /* reserved for future use */ ++ if (err > 0) ++ fsnotify_access(file->f_path.dentry); ++#endif ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static ssize_t do_xino_fwrite(au_writef_t func, struct file *file, void *kbuf, ++ size_t size, loff_t *pos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ const char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ /* todo: signal_pending? */ ++ err = func(file, buf.u, size, pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ set_fs(oldfs); ++ ++#if 0 /* reserved for future use */ ++ if (err > 0) ++ fsnotify_modify(file->f_path.dentry); ++#endif ++ ++ return err; ++} ++ ++struct do_xino_fwrite_args { ++ ssize_t *errp; ++ au_writef_t func; ++ struct file *file; ++ void *buf; ++ size_t size; ++ loff_t *pos; ++}; ++ ++static void call_do_xino_fwrite(void *args) ++{ ++ struct do_xino_fwrite_args *a = args; ++ *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); ++} ++ ++ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos) ++{ ++ ssize_t err; ++ ++ /* todo: signal block and no wkq? */ ++ if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { ++ lockdep_off(); ++ err = do_xino_fwrite(func, file, buf, size, pos); ++ lockdep_on(); ++ } else { ++ /* ++ * it breaks RLIMIT_FSIZE and normal user's limit, ++ * users should care about quota and real 'filesystem full.' ++ */ ++ int wkq_err; ++ struct do_xino_fwrite_args args = { ++ .errp = &err, ++ .func = func, ++ .file = file, ++ .buf = buf, ++ .size = size, ++ .pos = pos ++ }; ++ ++ wkq_err = au_wkq_wait(call_do_xino_fwrite, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create a new xinofile at the same place/path as @base_file. ++ */ ++struct file *au_xino_create2(struct file *base_file, struct file *copy_src) ++{ ++ struct file *file; ++ struct dentry *base, *parent; ++ struct inode *dir, *delegated; ++ struct qstr *name; ++ struct path path; ++ int err; ++ ++ base = base_file->f_path.dentry; ++ parent = base->d_parent; /* dir inode is locked */ ++ dir = parent->d_inode; ++ IMustLock(dir); ++ ++ file = ERR_PTR(-EINVAL); ++ name = &base->d_name; ++ path.dentry = vfsub_lookup_one_len(name->name, parent, name->len); ++ if (IS_ERR(path.dentry)) { ++ file = (void *)path.dentry; ++ pr_err("%pd lookup err %ld\n", ++ base, PTR_ERR(path.dentry)); ++ goto out; ++ } ++ ++ /* no need to mnt_want_write() since we call dentry_open() later */ ++ err = vfs_create(dir, path.dentry, S_IRUGO | S_IWUGO, NULL); ++ if (unlikely(err)) { ++ file = ERR_PTR(err); ++ pr_err("%pd create err %d\n", base, err); ++ goto out_dput; ++ } ++ ++ path.mnt = base_file->f_path.mnt; ++ file = vfsub_dentry_open(&path, ++ O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE ++ /* | __FMODE_NONOTIFY */); ++ if (IS_ERR(file)) { ++ pr_err("%pd open err %ld\n", base, PTR_ERR(file)); ++ goto out_dput; ++ } ++ ++ delegated = NULL; ++ err = vfsub_unlink(dir, &file->f_path, &delegated, /*force*/0); ++ if (unlikely(err == -EWOULDBLOCK)) { ++ pr_warn("cannot retry for NFSv4 delegation" ++ " for an internal unlink\n"); ++ iput(delegated); ++ } ++ if (unlikely(err)) { ++ pr_err("%pd unlink err %d\n", base, err); ++ goto out_fput; ++ } ++ ++ if (copy_src) { ++ /* no one can touch copy_src xino */ ++ err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); ++ if (unlikely(err)) { ++ pr_err("%pd copy err %d\n", base, err); ++ goto out_fput; ++ } ++ } ++ goto out_dput; /* success */ ++ ++out_fput: ++ fput(file); ++ file = ERR_PTR(err); ++out_dput: ++ dput(path.dentry); ++out: ++ return file; ++} ++ ++struct au_xino_lock_dir { ++ struct au_hinode *hdir; ++ struct dentry *parent; ++ struct mutex *mtx; ++}; ++ ++static void au_xino_lock_dir(struct super_block *sb, struct file *xino, ++ struct au_xino_lock_dir *ldir) ++{ ++ aufs_bindex_t brid, bindex; ++ ++ ldir->hdir = NULL; ++ bindex = -1; ++ brid = au_xino_brid(sb); ++ if (brid >= 0) ++ bindex = au_br_index(sb, brid); ++ if (bindex >= 0) { ++ ldir->hdir = au_hi(sb->s_root->d_inode, bindex); ++ au_hn_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); ++ } else { ++ ldir->parent = dget_parent(xino->f_path.dentry); ++ ldir->mtx = &ldir->parent->d_inode->i_mutex; ++ mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); ++ } ++} ++ ++static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) ++{ ++ if (ldir->hdir) ++ au_hn_imtx_unlock(ldir->hdir); ++ else { ++ mutex_unlock(ldir->mtx); ++ dput(ldir->parent); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* trucate xino files asynchronously */ ++ ++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err; ++ unsigned long jiffy; ++ blkcnt_t blocks; ++ aufs_bindex_t bi, bend; ++ struct kstatfs *st; ++ struct au_branch *br; ++ struct file *new_xino, *file; ++ struct super_block *h_sb; ++ struct au_xino_lock_dir ldir; ++ ++ err = -ENOMEM; ++ st = kzalloc(sizeof(*st), GFP_NOFS); ++ if (unlikely(!st)) ++ goto out; ++ ++ err = -EINVAL; ++ bend = au_sbend(sb); ++ if (unlikely(bindex < 0 || bend < bindex)) ++ goto out_st; ++ br = au_sbr(sb, bindex); ++ file = br->br_xino.xi_file; ++ if (!file) ++ goto out_st; ++ ++ err = vfs_statfs(&file->f_path, st); ++ if (unlikely(err)) ++ AuErr1("statfs err %d, ignored\n", err); ++ jiffy = jiffies; ++ blocks = file_inode(file)->i_blocks; ++ pr_info("begin truncating xino(b%d), ib%llu, %llu/%llu free blks\n", ++ bindex, (u64)blocks, st->f_bfree, st->f_blocks); ++ ++ au_xino_lock_dir(sb, file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ new_xino = au_xino_create2(file, file); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(new_xino); ++ if (IS_ERR(new_xino)) { ++ pr_err("err %d, ignored\n", err); ++ goto out_st; ++ } ++ err = 0; ++ fput(file); ++ br->br_xino.xi_file = new_xino; ++ ++ h_sb = au_br_sb(br); ++ for (bi = 0; bi <= bend; bi++) { ++ if (unlikely(bi == bindex)) ++ continue; ++ br = au_sbr(sb, bi); ++ if (au_br_sb(br) != h_sb) ++ continue; ++ ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = new_xino; ++ get_file(new_xino); ++ } ++ ++ err = vfs_statfs(&new_xino->f_path, st); ++ if (!err) { ++ pr_info("end truncating xino(b%d), ib%llu, %llu/%llu free blks\n", ++ bindex, (u64)file_inode(new_xino)->i_blocks, ++ st->f_bfree, st->f_blocks); ++ if (file_inode(new_xino)->i_blocks < blocks) ++ au_sbi(sb)->si_xino_jiffy = jiffy; ++ } else ++ AuErr1("statfs err %d, ignored\n", err); ++ ++out_st: ++ kfree(st); ++out: ++ return err; ++} ++ ++struct xino_do_trunc_args { ++ struct super_block *sb; ++ struct au_branch *br; ++}; ++ ++static void xino_do_trunc(void *_args) ++{ ++ struct xino_do_trunc_args *args = _args; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct inode *dir; ++ int err; ++ aufs_bindex_t bindex; ++ ++ err = 0; ++ sb = args->sb; ++ dir = sb->s_root->d_inode; ++ br = args->br; ++ ++ si_noflush_write_lock(sb); ++ ii_read_lock_parent(dir); ++ bindex = au_br_index(sb, br->br_id); ++ err = au_xino_trunc(sb, bindex); ++ ii_read_unlock(dir); ++ if (unlikely(err)) ++ pr_warn("err b%d, (%d)\n", bindex, err); ++ atomic_dec(&br->br_xino_running); ++ atomic_dec(&br->br_count); ++ si_write_unlock(sb); ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ kfree(args); ++} ++ ++static int xino_trunc_test(struct super_block *sb, struct au_branch *br) ++{ ++ int err; ++ struct kstatfs st; ++ struct au_sbinfo *sbinfo; ++ ++ /* todo: si_xino_expire and the ratio should be customizable */ ++ sbinfo = au_sbi(sb); ++ if (time_before(jiffies, ++ sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) ++ return 0; ++ ++ /* truncation border */ ++ err = vfs_statfs(&br->br_xino.xi_file->f_path, &st); ++ if (unlikely(err)) { ++ AuErr1("statfs err %d, ignored\n", err); ++ return 0; ++ } ++ if (div64_u64(st.f_bfree * 100, st.f_blocks) >= AUFS_XINO_DEF_TRUNC) ++ return 0; ++ ++ return 1; ++} ++ ++static void xino_try_trunc(struct super_block *sb, struct au_branch *br) ++{ ++ struct xino_do_trunc_args *args; ++ int wkq_err; ++ ++ if (!xino_trunc_test(sb, br)) ++ return; ++ ++ if (atomic_inc_return(&br->br_xino_running) > 1) ++ goto out; ++ ++ /* lock and kfree() will be called in trunc_xino() */ ++ args = kmalloc(sizeof(*args), GFP_NOFS); ++ if (unlikely(!args)) { ++ AuErr1("no memory\n"); ++ goto out_args; ++ } ++ ++ atomic_inc(&br->br_count); ++ args->sb = sb; ++ args->br = br; ++ wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*flags*/0); ++ if (!wkq_err) ++ return; /* success */ ++ ++ pr_err("wkq %d\n", wkq_err); ++ atomic_dec(&br->br_count); ++ ++out_args: ++ kfree(args); ++out: ++ atomic_dec(&br->br_xino_running); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_xino_do_write(au_writef_t write, struct file *file, ++ ino_t h_ino, ino_t ino) ++{ ++ loff_t pos; ++ ssize_t sz; ++ ++ pos = h_ino; ++ if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) { ++ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); ++ return -EFBIG; ++ } ++ pos *= sizeof(ino); ++ sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos); ++ if (sz == sizeof(ino)) ++ return 0; /* success */ ++ ++ AuIOErr("write failed (%zd)\n", sz); ++ return -EIO; ++} ++ ++/* ++ * write @ino to the xinofile for the specified branch{@sb, @bindex} ++ * at the position of @h_ino. ++ * even if @ino is zero, it is written to the xinofile and means no entry. ++ * if the size of the xino file on a specific filesystem exceeds the watermark, ++ * try truncating it. ++ */ ++int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino) ++{ ++ int err; ++ unsigned int mnt_flags; ++ struct au_branch *br; ++ ++ BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max) ++ || ((loff_t)-1) > 0); ++ SiMustAnyLock(sb); ++ ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, XINO)) ++ return 0; ++ ++ br = au_sbr(sb, bindex); ++ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, ++ h_ino, ino); ++ if (!err) { ++ if (au_opt_test(mnt_flags, TRUNC_XINO) ++ && au_test_fs_trunc_xino(au_br_sb(br))) ++ xino_try_trunc(sb, br); ++ return 0; /* success */ ++ } ++ ++ AuIOErr("write failed (%d)\n", err); ++ return -EIO; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* aufs inode number bitmap */ ++ ++static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; ++static ino_t xib_calc_ino(unsigned long pindex, int bit) ++{ ++ ino_t ino; ++ ++ AuDebugOn(bit < 0 || page_bits <= bit); ++ ino = AUFS_FIRST_INO + pindex * page_bits + bit; ++ return ino; ++} ++ ++static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) ++{ ++ AuDebugOn(ino < AUFS_FIRST_INO); ++ ino -= AUFS_FIRST_INO; ++ *pindex = ino / page_bits; ++ *bit = ino % page_bits; ++} ++ ++static int xib_pindex(struct super_block *sb, unsigned long pindex) ++{ ++ int err; ++ loff_t pos; ++ ssize_t sz; ++ struct au_sbinfo *sbinfo; ++ struct file *xib; ++ unsigned long *p; ++ ++ sbinfo = au_sbi(sb); ++ MtxMustLock(&sbinfo->si_xib_mtx); ++ AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE ++ || !au_opt_test(sbinfo->si_mntflags, XINO)); ++ ++ if (pindex == sbinfo->si_xib_last_pindex) ++ return 0; ++ ++ xib = sbinfo->si_xib; ++ p = sbinfo->si_xib_buf; ++ pos = sbinfo->si_xib_last_pindex; ++ pos *= PAGE_SIZE; ++ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); ++ if (unlikely(sz != PAGE_SIZE)) ++ goto out; ++ ++ pos = pindex; ++ pos *= PAGE_SIZE; ++ if (vfsub_f_size_read(xib) >= pos + PAGE_SIZE) ++ sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); ++ else { ++ memset(p, 0, PAGE_SIZE); ++ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); ++ } ++ if (sz == PAGE_SIZE) { ++ sbinfo->si_xib_last_pindex = pindex; ++ return 0; /* success */ ++ } ++ ++out: ++ AuIOErr1("write failed (%zd)\n", sz); ++ err = sz; ++ if (sz >= 0) ++ err = -EIO; ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_xib_clear_bit(struct inode *inode) ++{ ++ int err, bit; ++ unsigned long pindex; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(inode->i_nlink); ++ ++ sb = inode->i_sb; ++ xib_calc_bit(inode->i_ino, &pindex, &bit); ++ AuDebugOn(page_bits <= bit); ++ sbinfo = au_sbi(sb); ++ mutex_lock(&sbinfo->si_xib_mtx); ++ err = xib_pindex(sb, pindex); ++ if (!err) { ++ clear_bit(bit, sbinfo->si_xib_buf); ++ sbinfo->si_xib_next_bit = bit; ++ } ++ mutex_unlock(&sbinfo->si_xib_mtx); ++} ++ ++/* for s_op->delete_inode() */ ++void au_xino_delete_inode(struct inode *inode, const int unlinked) ++{ ++ int err; ++ unsigned int mnt_flags; ++ aufs_bindex_t bindex, bend, bi; ++ unsigned char try_trunc; ++ struct au_iinfo *iinfo; ++ struct super_block *sb; ++ struct au_hinode *hi; ++ struct inode *h_inode; ++ struct au_branch *br; ++ au_writef_t xwrite; ++ ++ sb = inode->i_sb; ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, XINO) ++ || inode->i_ino == AUFS_ROOT_INO) ++ return; ++ ++ if (unlinked) { ++ au_xigen_inc(inode); ++ au_xib_clear_bit(inode); ++ } ++ ++ iinfo = au_ii(inode); ++ if (!iinfo) ++ return; ++ ++ bindex = iinfo->ii_bstart; ++ if (bindex < 0) ++ return; ++ ++ xwrite = au_sbi(sb)->si_xwrite; ++ try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); ++ hi = iinfo->ii_hinode + bindex; ++ bend = iinfo->ii_bend; ++ for (; bindex <= bend; bindex++, hi++) { ++ h_inode = hi->hi_inode; ++ if (!h_inode ++ || (!unlinked && h_inode->i_nlink)) ++ continue; ++ ++ /* inode may not be revalidated */ ++ bi = au_br_index(sb, hi->hi_id); ++ if (bi < 0) ++ continue; ++ ++ br = au_sbr(sb, bi); ++ err = au_xino_do_write(xwrite, br->br_xino.xi_file, ++ h_inode->i_ino, /*ino*/0); ++ if (!err && try_trunc ++ && au_test_fs_trunc_xino(au_br_sb(br))) ++ xino_try_trunc(sb, br); ++ } ++} ++ ++/* get an unused inode number from bitmap */ ++ino_t au_xino_new_ino(struct super_block *sb) ++{ ++ ino_t ino; ++ unsigned long *p, pindex, ul, pend; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ int free_bit, err; ++ ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return iunique(sb, AUFS_FIRST_INO); ++ ++ sbinfo = au_sbi(sb); ++ mutex_lock(&sbinfo->si_xib_mtx); ++ p = sbinfo->si_xib_buf; ++ free_bit = sbinfo->si_xib_next_bit; ++ if (free_bit < page_bits && !test_bit(free_bit, p)) ++ goto out; /* success */ ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ ++ pindex = sbinfo->si_xib_last_pindex; ++ for (ul = pindex - 1; ul < ULONG_MAX; ul--) { ++ err = xib_pindex(sb, ul); ++ if (unlikely(err)) ++ goto out_err; ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ } ++ ++ file = sbinfo->si_xib; ++ pend = vfsub_f_size_read(file) / PAGE_SIZE; ++ for (ul = pindex + 1; ul <= pend; ul++) { ++ err = xib_pindex(sb, ul); ++ if (unlikely(err)) ++ goto out_err; ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ } ++ BUG(); ++ ++out: ++ set_bit(free_bit, p); ++ sbinfo->si_xib_next_bit = free_bit + 1; ++ pindex = sbinfo->si_xib_last_pindex; ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ ino = xib_calc_ino(pindex, free_bit); ++ AuDbg("i%lu\n", (unsigned long)ino); ++ return ino; ++out_err: ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ AuDbg("i0\n"); ++ return 0; ++} ++ ++/* ++ * read @ino from xinofile for the specified branch{@sb, @bindex} ++ * at the position of @h_ino. ++ * if @ino does not exist and @do_new is true, get new one. ++ */ ++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t *ino) ++{ ++ int err; ++ ssize_t sz; ++ loff_t pos; ++ struct file *file; ++ struct au_sbinfo *sbinfo; ++ ++ *ino = 0; ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return 0; /* no xino */ ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ pos = h_ino; ++ if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) { ++ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); ++ return -EFBIG; ++ } ++ pos *= sizeof(*ino); ++ ++ file = au_sbr(sb, bindex)->br_xino.xi_file; ++ if (vfsub_f_size_read(file) < pos + sizeof(*ino)) ++ return 0; /* no ino */ ++ ++ sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); ++ if (sz == sizeof(*ino)) ++ return 0; /* success */ ++ ++ err = sz; ++ if (unlikely(sz >= 0)) { ++ err = -EIO; ++ AuIOErr("xino read error (%zd)\n", sz); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* create and set a new xino file */ ++ ++struct file *au_xino_create(struct super_block *sb, char *fname, int silent) ++{ ++ struct file *file; ++ struct dentry *h_parent, *d; ++ struct inode *h_dir; ++ int err; ++ ++ /* ++ * at mount-time, and the xino file is the default path, ++ * hnotify is disabled so we have no notify events to ignore. ++ * when a user specified the xino, we cannot get au_hdir to be ignored. ++ */ ++ file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE ++ /* | __FMODE_NONOTIFY */, ++ S_IRUGO | S_IWUGO); ++ if (IS_ERR(file)) { ++ if (!silent) ++ pr_err("open %s(%ld)\n", fname, PTR_ERR(file)); ++ return file; ++ } ++ ++ /* keep file count */ ++ h_parent = dget_parent(file->f_path.dentry); ++ h_dir = h_parent->d_inode; ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ /* mnt_want_write() is unnecessary here */ ++ /* no delegation since it is just created */ ++ err = vfsub_unlink(h_dir, &file->f_path, /*delegated*/NULL, /*force*/0); ++ mutex_unlock(&h_dir->i_mutex); ++ dput(h_parent); ++ if (unlikely(err)) { ++ if (!silent) ++ pr_err("unlink %s(%d)\n", fname, err); ++ goto out; ++ } ++ ++ err = -EINVAL; ++ d = file->f_path.dentry; ++ if (unlikely(sb == d->d_sb)) { ++ if (!silent) ++ pr_err("%s must be outside\n", fname); ++ goto out; ++ } ++ if (unlikely(au_test_fs_bad_xino(d->d_sb))) { ++ if (!silent) ++ pr_err("xino doesn't support %s(%s)\n", ++ fname, au_sbtype(d->d_sb)); ++ goto out; ++ } ++ return file; /* success */ ++ ++out: ++ fput(file); ++ file = ERR_PTR(err); ++ return file; ++} ++ ++/* ++ * find another branch who is on the same filesystem of the specified ++ * branch{@btgt}. search until @bend. ++ */ ++static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, ++ aufs_bindex_t bend) ++{ ++ aufs_bindex_t bindex; ++ struct super_block *tgt_sb = au_sbr_sb(sb, btgt); ++ ++ for (bindex = 0; bindex < btgt; bindex++) ++ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) ++ return bindex; ++ for (bindex++; bindex <= bend; bindex++) ++ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) ++ return bindex; ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * initialize the xinofile for the specified branch @br ++ * at the place/path where @base_file indicates. ++ * test whether another branch is on the same filesystem or not, ++ * if @do_test is true. ++ */ ++int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, ++ struct file *base_file, int do_test) ++{ ++ int err; ++ ino_t ino; ++ aufs_bindex_t bend, bindex; ++ struct au_branch *shared_br, *b; ++ struct file *file; ++ struct super_block *tgt_sb; ++ ++ shared_br = NULL; ++ bend = au_sbend(sb); ++ if (do_test) { ++ tgt_sb = au_br_sb(br); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ b = au_sbr(sb, bindex); ++ if (tgt_sb == au_br_sb(b)) { ++ shared_br = b; ++ break; ++ } ++ } ++ } ++ ++ if (!shared_br || !shared_br->br_xino.xi_file) { ++ struct au_xino_lock_dir ldir; ++ ++ au_xino_lock_dir(sb, base_file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ file = au_xino_create2(base_file, NULL); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ br->br_xino.xi_file = file; ++ } else { ++ br->br_xino.xi_file = shared_br->br_xino.xi_file; ++ get_file(br->br_xino.xi_file); ++ } ++ ++ ino = AUFS_ROOT_INO; ++ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, ++ h_ino, ino); ++ if (unlikely(err)) { ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = NULL; ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* trucate a xino bitmap file */ ++ ++/* todo: slow */ ++static int do_xib_restore(struct super_block *sb, struct file *file, void *page) ++{ ++ int err, bit; ++ ssize_t sz; ++ unsigned long pindex; ++ loff_t pos, pend; ++ struct au_sbinfo *sbinfo; ++ au_readf_t func; ++ ino_t *ino; ++ unsigned long *p; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ MtxMustLock(&sbinfo->si_xib_mtx); ++ p = sbinfo->si_xib_buf; ++ func = sbinfo->si_xread; ++ pend = vfsub_f_size_read(file); ++ pos = 0; ++ while (pos < pend) { ++ sz = xino_fread(func, file, page, PAGE_SIZE, &pos); ++ err = sz; ++ if (unlikely(sz <= 0)) ++ goto out; ++ ++ err = 0; ++ for (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { ++ if (unlikely(*ino < AUFS_FIRST_INO)) ++ continue; ++ ++ xib_calc_bit(*ino, &pindex, &bit); ++ AuDebugOn(page_bits <= bit); ++ err = xib_pindex(sb, pindex); ++ if (!err) ++ set_bit(bit, p); ++ else ++ goto out; ++ } ++ } ++ ++out: ++ return err; ++} ++ ++static int xib_restore(struct super_block *sb) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ void *page; ++ ++ err = -ENOMEM; ++ page = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!page)) ++ goto out; ++ ++ err = 0; ++ bend = au_sbend(sb); ++ for (bindex = 0; !err && bindex <= bend; bindex++) ++ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) ++ err = do_xib_restore ++ (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); ++ else ++ AuDbg("b%d\n", bindex); ++ free_page((unsigned long)page); ++ ++out: ++ return err; ++} ++ ++int au_xib_trunc(struct super_block *sb) ++{ ++ int err; ++ ssize_t sz; ++ loff_t pos; ++ struct au_xino_lock_dir ldir; ++ struct au_sbinfo *sbinfo; ++ unsigned long *p; ++ struct file *file; ++ ++ SiMustWriteLock(sb); ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ if (!au_opt_test(sbinfo->si_mntflags, XINO)) ++ goto out; ++ ++ file = sbinfo->si_xib; ++ if (vfsub_f_size_read(file) <= PAGE_SIZE) ++ goto out; ++ ++ au_xino_lock_dir(sb, file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ file = au_xino_create2(sbinfo->si_xib, NULL); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = file; ++ ++ p = sbinfo->si_xib_buf; ++ memset(p, 0, PAGE_SIZE); ++ pos = 0; ++ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos); ++ if (unlikely(sz != PAGE_SIZE)) { ++ err = sz; ++ AuIOErr("err %d\n", err); ++ if (sz >= 0) ++ err = -EIO; ++ goto out; ++ } ++ ++ mutex_lock(&sbinfo->si_xib_mtx); ++ /* mnt_want_write() is unnecessary here */ ++ err = xib_restore(sb); ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * xino mount option handlers ++ */ ++static au_readf_t find_readf(struct file *h_file) ++{ ++ const struct file_operations *fop = h_file->f_op; ++ ++ if (fop->read) ++ return fop->read; ++ if (fop->aio_read) ++ return do_sync_read; ++ if (fop->read_iter) ++ return new_sync_read; ++ return ERR_PTR(-ENOSYS); ++} ++ ++static au_writef_t find_writef(struct file *h_file) ++{ ++ const struct file_operations *fop = h_file->f_op; ++ ++ if (fop->write) ++ return fop->write; ++ if (fop->aio_write) ++ return do_sync_write; ++ if (fop->write_iter) ++ return new_sync_write; ++ return ERR_PTR(-ENOSYS); ++} ++ ++/* xino bitmap */ ++static void xino_clear_xib(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ sbinfo->si_xread = NULL; ++ sbinfo->si_xwrite = NULL; ++ if (sbinfo->si_xib) ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = NULL; ++ free_page((unsigned long)sbinfo->si_xib_buf); ++ sbinfo->si_xib_buf = NULL; ++} ++ ++static int au_xino_set_xib(struct super_block *sb, struct file *base) ++{ ++ int err; ++ loff_t pos; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ file = au_xino_create2(base, sbinfo->si_xib); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ if (sbinfo->si_xib) ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = file; ++ sbinfo->si_xread = find_readf(file); ++ sbinfo->si_xwrite = find_writef(file); ++ ++ err = -ENOMEM; ++ if (!sbinfo->si_xib_buf) ++ sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); ++ if (unlikely(!sbinfo->si_xib_buf)) ++ goto out_unset; ++ ++ sbinfo->si_xib_last_pindex = 0; ++ sbinfo->si_xib_next_bit = 0; ++ if (vfsub_f_size_read(file) < PAGE_SIZE) { ++ pos = 0; ++ err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf, ++ PAGE_SIZE, &pos); ++ if (unlikely(err != PAGE_SIZE)) ++ goto out_free; ++ } ++ err = 0; ++ goto out; /* success */ ++ ++out_free: ++ free_page((unsigned long)sbinfo->si_xib_buf); ++ sbinfo->si_xib_buf = NULL; ++ if (err >= 0) ++ err = -EIO; ++out_unset: ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = NULL; ++ sbinfo->si_xread = NULL; ++ sbinfo->si_xwrite = NULL; ++out: ++ return err; ++} ++ ++/* xino for each branch */ ++static void xino_clear_br(struct super_block *sb) ++{ ++ aufs_bindex_t bindex, bend; ++ struct au_branch *br; ++ ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!br || !br->br_xino.xi_file) ++ continue; ++ ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = NULL; ++ } ++} ++ ++static int au_xino_set_br(struct super_block *sb, struct file *base) ++{ ++ int err; ++ ino_t ino; ++ aufs_bindex_t bindex, bend, bshared; ++ struct { ++ struct file *old, *new; ++ } *fpair, *p; ++ struct au_branch *br; ++ struct inode *inode; ++ au_writef_t writef; ++ ++ SiMustWriteLock(sb); ++ ++ err = -ENOMEM; ++ bend = au_sbend(sb); ++ fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); ++ if (unlikely(!fpair)) ++ goto out; ++ ++ inode = sb->s_root->d_inode; ++ ino = AUFS_ROOT_INO; ++ writef = au_sbi(sb)->si_xwrite; ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { ++ br = au_sbr(sb, bindex); ++ bshared = is_sb_shared(sb, bindex, bindex - 1); ++ if (bshared >= 0) { ++ /* shared xino */ ++ *p = fpair[bshared]; ++ get_file(p->new); ++ } ++ ++ if (!p->new) { ++ /* new xino */ ++ p->old = br->br_xino.xi_file; ++ p->new = au_xino_create2(base, br->br_xino.xi_file); ++ err = PTR_ERR(p->new); ++ if (IS_ERR(p->new)) { ++ p->new = NULL; ++ goto out_pair; ++ } ++ } ++ ++ err = au_xino_do_write(writef, p->new, ++ au_h_iptr(inode, bindex)->i_ino, ino); ++ if (unlikely(err)) ++ goto out_pair; ++ } ++ ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { ++ br = au_sbr(sb, bindex); ++ if (br->br_xino.xi_file) ++ fput(br->br_xino.xi_file); ++ get_file(p->new); ++ br->br_xino.xi_file = p->new; ++ } ++ ++out_pair: ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) ++ if (p->new) ++ fput(p->new); ++ else ++ break; ++ kfree(fpair); ++out: ++ return err; ++} ++ ++void au_xino_clr(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ au_xigen_clr(sb); ++ xino_clear_xib(sb); ++ xino_clear_br(sb); ++ sbinfo = au_sbi(sb); ++ /* lvalue, do not call au_mntflags() */ ++ au_opt_clr(sbinfo->si_mntflags, XINO); ++} ++ ++int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) ++{ ++ int err, skip; ++ struct dentry *parent, *cur_parent; ++ struct qstr *dname, *cur_name; ++ struct file *cur_xino; ++ struct inode *dir; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ parent = dget_parent(xino->file->f_path.dentry); ++ if (remount) { ++ skip = 0; ++ dname = &xino->file->f_path.dentry->d_name; ++ cur_xino = sbinfo->si_xib; ++ if (cur_xino) { ++ cur_parent = dget_parent(cur_xino->f_path.dentry); ++ cur_name = &cur_xino->f_path.dentry->d_name; ++ skip = (cur_parent == parent ++ && au_qstreq(dname, cur_name)); ++ dput(cur_parent); ++ } ++ if (skip) ++ goto out; ++ } ++ ++ au_opt_set(sbinfo->si_mntflags, XINO); ++ dir = parent->d_inode; ++ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); ++ /* mnt_want_write() is unnecessary here */ ++ err = au_xino_set_xib(sb, xino->file); ++ if (!err) ++ err = au_xigen_set(sb, xino->file); ++ if (!err) ++ err = au_xino_set_br(sb, xino->file); ++ mutex_unlock(&dir->i_mutex); ++ if (!err) ++ goto out; /* success */ ++ ++ /* reset all */ ++ AuIOErr("failed creating xino(%d).\n", err); ++ au_xigen_clr(sb); ++ xino_clear_xib(sb); ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create a xinofile at the default place/path. ++ */ ++struct file *au_xino_def(struct super_block *sb) ++{ ++ struct file *file; ++ char *page, *p; ++ struct au_branch *br; ++ struct super_block *h_sb; ++ struct path path; ++ aufs_bindex_t bend, bindex, bwr; ++ ++ br = NULL; ++ bend = au_sbend(sb); ++ bwr = -1; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_writable(br->br_perm) ++ && !au_test_fs_bad_xino(au_br_sb(br))) { ++ bwr = bindex; ++ break; ++ } ++ } ++ ++ if (bwr >= 0) { ++ file = ERR_PTR(-ENOMEM); ++ page = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!page)) ++ goto out; ++ path.mnt = au_br_mnt(br); ++ path.dentry = au_h_dptr(sb->s_root, bwr); ++ p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); ++ file = (void *)p; ++ if (!IS_ERR(p)) { ++ strcat(p, "/" AUFS_XINO_FNAME); ++ AuDbg("%s\n", p); ++ file = au_xino_create(sb, p, /*silent*/0); ++ if (!IS_ERR(file)) ++ au_xino_brid_set(sb, br->br_id); ++ } ++ free_page((unsigned long)page); ++ } else { ++ file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0); ++ if (IS_ERR(file)) ++ goto out; ++ h_sb = file->f_path.dentry->d_sb; ++ if (unlikely(au_test_fs_bad_xino(h_sb))) { ++ pr_err("xino doesn't support %s(%s)\n", ++ AUFS_XINO_DEFPATH, au_sbtype(h_sb)); ++ fput(file); ++ file = ERR_PTR(-EINVAL); ++ } ++ if (!IS_ERR(file)) ++ au_xino_brid_set(sb, -1); ++ } ++ ++out: ++ return file; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_xino_path(struct seq_file *seq, struct file *file) ++{ ++ int err; ++ ++ err = au_seq_path(seq, &file->f_path); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ err = 0; ++#define Deleted "\\040(deleted)" ++ seq->count -= sizeof(Deleted) - 1; ++ AuDebugOn(memcmp(seq->buf + seq->count, Deleted, ++ sizeof(Deleted) - 1)); ++#undef Deleted ++ ++out: ++ return err; ++} +diff -Nur linux-4.0.4.orig/fs/buffer.c linux-4.0.4/fs/buffer.c +--- linux-4.0.4.orig/fs/buffer.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/fs/buffer.c 2015-05-30 22:11:30.000000000 +0200 +@@ -2450,7 +2450,7 @@ + * Update file times before taking page lock. We may end up failing the + * fault so this update may be superfluous but who really cares... + */ +- file_update_time(vma->vm_file); ++ vma_file_update_time(vma); + + ret = __block_page_mkwrite(vma, vmf, get_block); + sb_end_pagefault(sb); +diff -Nur linux-4.0.4.orig/fs/dcache.c linux-4.0.4/fs/dcache.c +--- linux-4.0.4.orig/fs/dcache.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/fs/dcache.c 2015-05-30 22:11:30.000000000 +0200 +@@ -1130,7 +1130,7 @@ + * + * The @enter() and @finish() callbacks are called with d_lock held. + */ +-static void d_walk(struct dentry *parent, void *data, ++void d_walk(struct dentry *parent, void *data, + enum d_walk_ret (*enter)(void *, struct dentry *), + void (*finish)(void *)) + { +diff -Nur linux-4.0.4.orig/fs/Kconfig linux-4.0.4/fs/Kconfig +--- linux-4.0.4.orig/fs/Kconfig 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/fs/Kconfig 2015-05-30 22:11:31.000000000 +0200 +@@ -218,6 +218,7 @@ + source "fs/ufs/Kconfig" + source "fs/exofs/Kconfig" + source "fs/f2fs/Kconfig" ++source "fs/aufs/Kconfig" + + endif # MISC_FILESYSTEMS + +diff -Nur linux-4.0.4.orig/fs/Makefile linux-4.0.4/fs/Makefile +--- linux-4.0.4.orig/fs/Makefile 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/fs/Makefile 2015-05-30 22:11:31.000000000 +0200 +@@ -126,3 +126,4 @@ + obj-$(CONFIG_CEPH_FS) += ceph/ + obj-$(CONFIG_PSTORE) += pstore/ + obj-$(CONFIG_EFIVAR_FS) += efivarfs/ ++obj-$(CONFIG_AUFS_FS) += aufs/ +diff -Nur linux-4.0.4.orig/fs/proc/base.c linux-4.0.4/fs/proc/base.c +--- linux-4.0.4.orig/fs/proc/base.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/fs/proc/base.c 2015-05-30 22:11:30.000000000 +0200 +@@ -1735,7 +1735,7 @@ + down_read(&mm->mmap_sem); + vma = find_exact_vma(mm, vm_start, vm_end); + if (vma && vma->vm_file) { +- *path = vma->vm_file->f_path; ++ *path = vma_pr_or_file(vma)->f_path; + path_get(path); + rc = 0; + } +diff -Nur linux-4.0.4.orig/fs/proc/nommu.c linux-4.0.4/fs/proc/nommu.c +--- linux-4.0.4.orig/fs/proc/nommu.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/fs/proc/nommu.c 2015-05-30 22:11:30.000000000 +0200 +@@ -45,7 +45,10 @@ + file = region->vm_file; + + if (file) { +- struct inode *inode = file_inode(region->vm_file); ++ struct inode *inode; ++ ++ file = vmr_pr_or_file(region); ++ inode = file_inode(file); + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + } +diff -Nur linux-4.0.4.orig/fs/proc/task_mmu.c linux-4.0.4/fs/proc/task_mmu.c +--- linux-4.0.4.orig/fs/proc/task_mmu.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/fs/proc/task_mmu.c 2015-05-30 22:11:30.000000000 +0200 +@@ -279,7 +279,10 @@ + const char *name = NULL; + + if (file) { +- struct inode *inode = file_inode(vma->vm_file); ++ struct inode *inode; ++ ++ file = vma_pr_or_file(vma); ++ inode = file_inode(file); + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; +@@ -1479,7 +1482,7 @@ + struct proc_maps_private *proc_priv = &numa_priv->proc_maps; + struct vm_area_struct *vma = v; + struct numa_maps *md = &numa_priv->md; +- struct file *file = vma->vm_file; ++ struct file *file = vma_pr_or_file(vma); + struct mm_struct *mm = vma->vm_mm; + struct mm_walk walk = { + .hugetlb_entry = gather_hugetlb_stats, +diff -Nur linux-4.0.4.orig/fs/proc/task_nommu.c linux-4.0.4/fs/proc/task_nommu.c +--- linux-4.0.4.orig/fs/proc/task_nommu.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/fs/proc/task_nommu.c 2015-05-30 22:11:30.000000000 +0200 +@@ -160,7 +160,10 @@ + file = vma->vm_file; + + if (file) { +- struct inode *inode = file_inode(vma->vm_file); ++ struct inode *inode; ++ ++ file = vma_pr_or_file(file); ++ inode = file_inode(file); + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; +diff -Nur linux-4.0.4.orig/fs/splice.c linux-4.0.4/fs/splice.c +--- linux-4.0.4.orig/fs/splice.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/fs/splice.c 2015-05-30 22:11:30.000000000 +0200 +@@ -1099,8 +1099,8 @@ + /* + * Attempt to initiate a splice from pipe to file. + */ +-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +- loff_t *ppos, size_t len, unsigned int flags) ++long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) + { + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, + loff_t *, size_t, unsigned int); +@@ -1116,9 +1116,9 @@ + /* + * Attempt to initiate a splice from a file to a pipe. + */ +-static long do_splice_to(struct file *in, loff_t *ppos, +- struct pipe_inode_info *pipe, size_t len, +- unsigned int flags) ++long do_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) + { + ssize_t (*splice_read)(struct file *, loff_t *, + struct pipe_inode_info *, size_t, unsigned int); +diff -Nur linux-4.0.4.orig/include/linux/file.h linux-4.0.4/include/linux/file.h +--- linux-4.0.4.orig/include/linux/file.h 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/include/linux/file.h 2015-05-30 22:11:31.000000000 +0200 +@@ -19,6 +19,7 @@ + struct path; + extern struct file *alloc_file(struct path *, fmode_t mode, + const struct file_operations *fop); ++extern struct file *get_empty_filp(void); + + static inline void fput_light(struct file *file, int fput_needed) + { +diff -Nur linux-4.0.4.orig/include/linux/mm.h linux-4.0.4/include/linux/mm.h +--- linux-4.0.4.orig/include/linux/mm.h 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/include/linux/mm.h 2015-05-30 22:11:30.000000000 +0200 +@@ -1254,6 +1254,28 @@ + } + #endif + ++#ifdef CONFIG_MMU ++extern void vma_do_file_update_time(struct vm_area_struct *, const char[], int); ++extern struct file *vma_do_pr_or_file(struct vm_area_struct *, const char[], ++ int); ++extern void vma_do_get_file(struct vm_area_struct *, const char[], int); ++extern void vma_do_fput(struct vm_area_struct *, const char[], int); ++ ++#define vma_file_update_time(vma) vma_do_file_update_time(vma, __func__, \ ++ __LINE__) ++#define vma_pr_or_file(vma) vma_do_pr_or_file(vma, __func__, \ ++ __LINE__) ++#define vma_get_file(vma) vma_do_get_file(vma, __func__, __LINE__) ++#define vma_fput(vma) vma_do_fput(vma, __func__, __LINE__) ++#else ++extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int); ++extern void vmr_do_fput(struct vm_region *, const char[], int); ++ ++#define vmr_pr_or_file(region) vmr_do_pr_or_file(region, __func__, \ ++ __LINE__) ++#define vmr_fput(region) vmr_do_fput(region, __func__, __LINE__) ++#endif /* CONFIG_MMU */ ++ + extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); + extern int access_remote_vm(struct mm_struct *mm, unsigned long addr, + void *buf, int len, int write); +diff -Nur linux-4.0.4.orig/include/linux/mm_types.h linux-4.0.4/include/linux/mm_types.h +--- linux-4.0.4.orig/include/linux/mm_types.h 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/include/linux/mm_types.h 2015-05-30 22:11:30.000000000 +0200 +@@ -241,6 +241,7 @@ + unsigned long vm_top; /* region allocated to here */ + unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ + struct file *vm_file; /* the backing file or NULL */ ++ struct file *vm_prfile; /* the virtual backing file or NULL */ + + int vm_usage; /* region usage count (access under nommu_region_sem) */ + bool vm_icache_flushed : 1; /* true if the icache has been flushed for +@@ -305,6 +306,7 @@ + unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE + units, *not* PAGE_CACHE_SIZE */ + struct file * vm_file; /* File we map to (can be NULL). */ ++ struct file *vm_prfile; /* shadow of vm_file */ + void * vm_private_data; /* was vm_pte (shared mem) */ + + #ifndef CONFIG_MMU +diff -Nur linux-4.0.4.orig/include/linux/splice.h linux-4.0.4/include/linux/splice.h +--- linux-4.0.4.orig/include/linux/splice.h 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/include/linux/splice.h 2015-05-30 22:11:30.000000000 +0200 +@@ -83,4 +83,10 @@ + extern void spd_release_page(struct splice_pipe_desc *, unsigned int); + + extern const struct pipe_buf_operations page_cache_pipe_buf_ops; ++ ++extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags); ++extern long do_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags); + #endif +diff -Nur linux-4.0.4.orig/include/uapi/linux/aufs_type.h linux-4.0.4/include/uapi/linux/aufs_type.h +--- linux-4.0.4.orig/include/uapi/linux/aufs_type.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/include/uapi/linux/aufs_type.h 2015-05-30 22:11:31.000000000 +0200 +@@ -0,0 +1,406 @@ ++/* ++ * Copyright (C) 2005-2015 Junjiro R. Okajima ++ */ ++ ++#ifndef __AUFS_TYPE_H__ ++#define __AUFS_TYPE_H__ ++ ++#define AUFS_NAME "aufs" ++ ++#ifdef __KERNEL__ ++/* ++ * define it before including all other headers. ++ * sched.h may use pr_* macros before defining "current", so define the ++ * no-current version first, and re-define later. ++ */ ++#define pr_fmt(fmt) AUFS_NAME " %s:%d: " fmt, __func__, __LINE__ ++#include ++#undef pr_fmt ++#define pr_fmt(fmt) \ ++ AUFS_NAME " %s:%d:%.*s[%d]: " fmt, __func__, __LINE__, \ ++ (int)sizeof(current->comm), current->comm, current->pid ++#else ++#include ++#include ++#endif /* __KERNEL__ */ ++ ++#include ++ ++#define AUFS_VERSION "4.0" ++ ++/* todo? move this to linux-2.6.19/include/magic.h */ ++#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_BRANCH_MAX_127 ++typedef int8_t aufs_bindex_t; ++#define AUFS_BRANCH_MAX 127 ++#else ++typedef int16_t aufs_bindex_t; ++#ifdef CONFIG_AUFS_BRANCH_MAX_511 ++#define AUFS_BRANCH_MAX 511 ++#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) ++#define AUFS_BRANCH_MAX 1023 ++#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) ++#define AUFS_BRANCH_MAX 32767 ++#endif ++#endif ++ ++#ifdef __KERNEL__ ++#ifndef AUFS_BRANCH_MAX ++#error unknown CONFIG_AUFS_BRANCH_MAX value ++#endif ++#endif /* __KERNEL__ */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AUFS_FSTYPE AUFS_NAME ++ ++#define AUFS_ROOT_INO 2 ++#define AUFS_FIRST_INO 11 ++ ++#define AUFS_WH_PFX ".wh." ++#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) ++#define AUFS_WH_TMP_LEN 4 ++/* a limit for rmdir/rename a dir and copyup */ ++#define AUFS_MAX_NAMELEN (NAME_MAX \ ++ - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ ++ - 1 /* dot */\ ++ - AUFS_WH_TMP_LEN) /* hex */ ++#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" ++#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME ++#define AUFS_XINO_DEF_SEC 30 /* seconds */ ++#define AUFS_XINO_DEF_TRUNC 45 /* percentage */ ++#define AUFS_DIRWH_DEF 3 ++#define AUFS_RDCACHE_DEF 10 /* seconds */ ++#define AUFS_RDCACHE_MAX 3600 /* seconds */ ++#define AUFS_RDBLK_DEF 512 /* bytes */ ++#define AUFS_RDHASH_DEF 32 ++#define AUFS_WKQ_NAME AUFS_NAME "d" ++#define AUFS_MFS_DEF_SEC 30 /* seconds */ ++#define AUFS_MFS_MAX_SEC 3600 /* seconds */ ++#define AUFS_FHSM_CACHE_DEF_SEC 30 /* seconds */ ++#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */ ++ ++/* pseudo-link maintenace under /proc */ ++#define AUFS_PLINK_MAINT_NAME "plink_maint" ++#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME ++#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME ++ ++#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ ++#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME ++ ++#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME ++#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" ++#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" ++ ++/* doubly whiteouted */ ++#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME ++#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME ++#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME ++ ++/* branch permissions and attributes */ ++#define AUFS_BRPERM_RW "rw" ++#define AUFS_BRPERM_RO "ro" ++#define AUFS_BRPERM_RR "rr" ++#define AUFS_BRATTR_COO_REG "coo_reg" ++#define AUFS_BRATTR_COO_ALL "coo_all" ++#define AUFS_BRATTR_FHSM "fhsm" ++#define AUFS_BRATTR_UNPIN "unpin" ++#define AUFS_BRATTR_ICEX "icex" ++#define AUFS_BRATTR_ICEX_SEC "icexsec" ++#define AUFS_BRATTR_ICEX_SYS "icexsys" ++#define AUFS_BRATTR_ICEX_TR "icextr" ++#define AUFS_BRATTR_ICEX_USR "icexusr" ++#define AUFS_BRATTR_ICEX_OTH "icexoth" ++#define AUFS_BRRATTR_WH "wh" ++#define AUFS_BRWATTR_NLWH "nolwh" ++#define AUFS_BRWATTR_MOO "moo" ++ ++#define AuBrPerm_RW 1 /* writable, hardlinkable wh */ ++#define AuBrPerm_RO (1 << 1) /* readonly */ ++#define AuBrPerm_RR (1 << 2) /* natively readonly */ ++#define AuBrPerm_Mask (AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR) ++ ++#define AuBrAttr_COO_REG (1 << 3) /* copy-up on open */ ++#define AuBrAttr_COO_ALL (1 << 4) ++#define AuBrAttr_COO_Mask (AuBrAttr_COO_REG | AuBrAttr_COO_ALL) ++ ++#define AuBrAttr_FHSM (1 << 5) /* file-based hsm */ ++#define AuBrAttr_UNPIN (1 << 6) /* rename-able top dir of ++ branch. meaningless since ++ linux-3.18-rc1 */ ++ ++/* ignore error in copying XATTR */ ++#define AuBrAttr_ICEX_SEC (1 << 7) ++#define AuBrAttr_ICEX_SYS (1 << 8) ++#define AuBrAttr_ICEX_TR (1 << 9) ++#define AuBrAttr_ICEX_USR (1 << 10) ++#define AuBrAttr_ICEX_OTH (1 << 11) ++#define AuBrAttr_ICEX (AuBrAttr_ICEX_SEC \ ++ | AuBrAttr_ICEX_SYS \ ++ | AuBrAttr_ICEX_TR \ ++ | AuBrAttr_ICEX_USR \ ++ | AuBrAttr_ICEX_OTH) ++ ++#define AuBrRAttr_WH (1 << 12) /* whiteout-able */ ++#define AuBrRAttr_Mask AuBrRAttr_WH ++ ++#define AuBrWAttr_NoLinkWH (1 << 13) /* un-hardlinkable whiteouts */ ++#define AuBrWAttr_MOO (1 << 14) /* move-up on open */ ++#define AuBrWAttr_Mask (AuBrWAttr_NoLinkWH | AuBrWAttr_MOO) ++ ++#define AuBrAttr_CMOO_Mask (AuBrAttr_COO_Mask | AuBrWAttr_MOO) ++ ++/* #warning test userspace */ ++#ifdef __KERNEL__ ++#ifndef CONFIG_AUFS_FHSM ++#undef AuBrAttr_FHSM ++#define AuBrAttr_FHSM 0 ++#endif ++#ifndef CONFIG_AUFS_XATTR ++#undef AuBrAttr_ICEX ++#define AuBrAttr_ICEX 0 ++#undef AuBrAttr_ICEX_SEC ++#define AuBrAttr_ICEX_SEC 0 ++#undef AuBrAttr_ICEX_SYS ++#define AuBrAttr_ICEX_SYS 0 ++#undef AuBrAttr_ICEX_TR ++#define AuBrAttr_ICEX_TR 0 ++#undef AuBrAttr_ICEX_USR ++#define AuBrAttr_ICEX_USR 0 ++#undef AuBrAttr_ICEX_OTH ++#define AuBrAttr_ICEX_OTH 0 ++#endif ++#endif ++ ++/* the longest combination */ ++/* AUFS_BRATTR_ICEX and AUFS_BRATTR_ICEX_TR don't affect here */ ++#define AuBrPermStrSz sizeof(AUFS_BRPERM_RW \ ++ "+" AUFS_BRATTR_COO_REG \ ++ "+" AUFS_BRATTR_FHSM \ ++ "+" AUFS_BRATTR_UNPIN \ ++ "+" AUFS_BRATTR_ICEX_SEC \ ++ "+" AUFS_BRATTR_ICEX_SYS \ ++ "+" AUFS_BRATTR_ICEX_USR \ ++ "+" AUFS_BRATTR_ICEX_OTH \ ++ "+" AUFS_BRWATTR_NLWH) ++ ++typedef struct { ++ char a[AuBrPermStrSz]; ++} au_br_perm_str_t; ++ ++static inline int au_br_writable(int brperm) ++{ ++ return brperm & AuBrPerm_RW; ++} ++ ++static inline int au_br_whable(int brperm) ++{ ++ return brperm & (AuBrPerm_RW | AuBrRAttr_WH); ++} ++ ++static inline int au_br_wh_linkable(int brperm) ++{ ++ return !(brperm & AuBrWAttr_NoLinkWH); ++} ++ ++static inline int au_br_cmoo(int brperm) ++{ ++ return brperm & AuBrAttr_CMOO_Mask; ++} ++ ++static inline int au_br_fhsm(int brperm) ++{ ++ return brperm & AuBrAttr_FHSM; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ioctl */ ++enum { ++ /* readdir in userspace */ ++ AuCtl_RDU, ++ AuCtl_RDU_INO, ++ ++ AuCtl_WBR_FD, /* pathconf wrapper */ ++ AuCtl_IBUSY, /* busy inode */ ++ AuCtl_MVDOWN, /* move-down */ ++ AuCtl_BR, /* info about branches */ ++ AuCtl_FHSM_FD /* connection for fhsm */ ++}; ++ ++/* borrowed from linux/include/linux/kernel.h */ ++#ifndef ALIGN ++#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) ++#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) ++#endif ++ ++/* borrowed from linux/include/linux/compiler-gcc3.h */ ++#ifndef __aligned ++#define __aligned(x) __attribute__((aligned(x))) ++#endif ++ ++#ifdef __KERNEL__ ++#ifndef __packed ++#define __packed __attribute__((packed)) ++#endif ++#endif ++ ++struct au_rdu_cookie { ++ uint64_t h_pos; ++ int16_t bindex; ++ uint8_t flags; ++ uint8_t pad; ++ uint32_t generation; ++} __aligned(8); ++ ++struct au_rdu_ent { ++ uint64_t ino; ++ int16_t bindex; ++ uint8_t type; ++ uint8_t nlen; ++ uint8_t wh; ++ char name[0]; ++} __aligned(8); ++ ++static inline int au_rdu_len(int nlen) ++{ ++ /* include the terminating NULL */ ++ return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, ++ sizeof(uint64_t)); ++} ++ ++union au_rdu_ent_ul { ++ struct au_rdu_ent __user *e; ++ uint64_t ul; ++}; ++ ++enum { ++ AufsCtlRduV_SZ, ++ AufsCtlRduV_End ++}; ++ ++struct aufs_rdu { ++ /* input */ ++ union { ++ uint64_t sz; /* AuCtl_RDU */ ++ uint64_t nent; /* AuCtl_RDU_INO */ ++ }; ++ union au_rdu_ent_ul ent; ++ uint16_t verify[AufsCtlRduV_End]; ++ ++ /* input/output */ ++ uint32_t blk; ++ ++ /* output */ ++ union au_rdu_ent_ul tail; ++ /* number of entries which were added in a single call */ ++ uint64_t rent; ++ uint8_t full; ++ uint8_t shwh; ++ ++ struct au_rdu_cookie cookie; ++} __aligned(8); ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct aufs_wbr_fd { ++ uint32_t oflags; ++ int16_t brid; ++} __aligned(8); ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct aufs_ibusy { ++ uint64_t ino, h_ino; ++ int16_t bindex; ++} __aligned(8); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* error code for move-down */ ++/* the actual message strings are implemented in aufs-util.git */ ++enum { ++ EAU_MVDOWN_OPAQUE = 1, ++ EAU_MVDOWN_WHITEOUT, ++ EAU_MVDOWN_UPPER, ++ EAU_MVDOWN_BOTTOM, ++ EAU_MVDOWN_NOUPPER, ++ EAU_MVDOWN_NOLOWERBR, ++ EAU_Last ++}; ++ ++/* flags for move-down */ ++#define AUFS_MVDOWN_DMSG 1 ++#define AUFS_MVDOWN_OWLOWER (1 << 1) /* overwrite lower */ ++#define AUFS_MVDOWN_KUPPER (1 << 2) /* keep upper */ ++#define AUFS_MVDOWN_ROLOWER (1 << 3) /* do even if lower is RO */ ++#define AUFS_MVDOWN_ROLOWER_R (1 << 4) /* did on lower RO */ ++#define AUFS_MVDOWN_ROUPPER (1 << 5) /* do even if upper is RO */ ++#define AUFS_MVDOWN_ROUPPER_R (1 << 6) /* did on upper RO */ ++#define AUFS_MVDOWN_BRID_UPPER (1 << 7) /* upper brid */ ++#define AUFS_MVDOWN_BRID_LOWER (1 << 8) /* lower brid */ ++#define AUFS_MVDOWN_FHSM_LOWER (1 << 9) /* find fhsm attr for lower */ ++#define AUFS_MVDOWN_STFS (1 << 10) /* req. stfs */ ++#define AUFS_MVDOWN_STFS_FAILED (1 << 11) /* output: stfs is unusable */ ++#define AUFS_MVDOWN_BOTTOM (1 << 12) /* output: no more lowers */ ++ ++/* index for move-down */ ++enum { ++ AUFS_MVDOWN_UPPER, ++ AUFS_MVDOWN_LOWER, ++ AUFS_MVDOWN_NARRAY ++}; ++ ++/* ++ * additional info of move-down ++ * number of free blocks and inodes. ++ * subset of struct kstatfs, but smaller and always 64bit. ++ */ ++struct aufs_stfs { ++ uint64_t f_blocks; ++ uint64_t f_bavail; ++ uint64_t f_files; ++ uint64_t f_ffree; ++}; ++ ++struct aufs_stbr { ++ int16_t brid; /* optional input */ ++ int16_t bindex; /* output */ ++ struct aufs_stfs stfs; /* output when AUFS_MVDOWN_STFS set */ ++} __aligned(8); ++ ++struct aufs_mvdown { ++ uint32_t flags; /* input/output */ ++ struct aufs_stbr stbr[AUFS_MVDOWN_NARRAY]; /* input/output */ ++ int8_t au_errno; /* output */ ++} __aligned(8); ++ ++/* ---------------------------------------------------------------------- */ ++ ++union aufs_brinfo { ++ /* PATH_MAX may differ between kernel-space and user-space */ ++ char _spacer[4096]; ++ struct { ++ int16_t id; ++ int perm; ++ char path[0]; ++ }; ++} __aligned(8); ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AuCtlType 'A' ++#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) ++#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) ++#define AUFS_CTL_WBR_FD _IOW(AuCtlType, AuCtl_WBR_FD, \ ++ struct aufs_wbr_fd) ++#define AUFS_CTL_IBUSY _IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy) ++#define AUFS_CTL_MVDOWN _IOWR(AuCtlType, AuCtl_MVDOWN, \ ++ struct aufs_mvdown) ++#define AUFS_CTL_BRINFO _IOW(AuCtlType, AuCtl_BR, union aufs_brinfo) ++#define AUFS_CTL_FHSM_FD _IOW(AuCtlType, AuCtl_FHSM_FD, int) ++ ++#endif /* __AUFS_TYPE_H__ */ +diff -Nur linux-4.0.4.orig/include/uapi/linux/Kbuild linux-4.0.4/include/uapi/linux/Kbuild +--- linux-4.0.4.orig/include/uapi/linux/Kbuild 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/include/uapi/linux/Kbuild 2015-05-30 22:11:31.000000000 +0200 +@@ -58,6 +58,7 @@ + header-y += atm_tcp.h + header-y += atm_zatm.h + header-y += audit.h ++header-y += aufs_type.h + header-y += auto_fs4.h + header-y += auto_fs.h + header-y += auxvec.h +diff -Nur linux-4.0.4.orig/kernel/fork.c linux-4.0.4/kernel/fork.c +--- linux-4.0.4.orig/kernel/fork.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/kernel/fork.c 2015-05-30 22:11:30.000000000 +0200 +@@ -430,7 +430,7 @@ + struct inode *inode = file_inode(file); + struct address_space *mapping = file->f_mapping; + +- get_file(file); ++ vma_get_file(tmp); + if (tmp->vm_flags & VM_DENYWRITE) + atomic_dec(&inode->i_writecount); + i_mmap_lock_write(mapping); +diff -Nur linux-4.0.4.orig/MAINTAINERS linux-4.0.4/MAINTAINERS +--- linux-4.0.4.orig/MAINTAINERS 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/MAINTAINERS 2015-05-30 22:11:31.000000000 +0200 +@@ -1864,6 +1864,19 @@ + F: include/uapi/linux/audit.h + F: kernel/audit* + ++AUFS (advanced multi layered unification filesystem) FILESYSTEM ++M: "J. R. Okajima" ++L: linux-unionfs@vger.kernel.org ++L: aufs-users@lists.sourceforge.net (members only) ++W: http://aufs.sourceforge.net ++T: git://github.com/sfjro/aufs4-linux.git ++S: Supported ++F: Documentation/filesystems/aufs/ ++F: Documentation/ABI/testing/debugfs-aufs ++F: Documentation/ABI/testing/sysfs-aufs ++F: fs/aufs/ ++F: include/uapi/linux/aufs_type.h ++ + AUXILIARY DISPLAY DRIVERS + M: Miguel Ojeda Sandonis + W: http://miguelojeda.es/auxdisplay.htm +diff -Nur linux-4.0.4.orig/mm/filemap.c linux-4.0.4/mm/filemap.c +--- linux-4.0.4.orig/mm/filemap.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/mm/filemap.c 2015-05-30 22:11:30.000000000 +0200 +@@ -2064,7 +2064,7 @@ + int ret = VM_FAULT_LOCKED; + + sb_start_pagefault(inode->i_sb); +- file_update_time(vma->vm_file); ++ vma_file_update_time(vma); + lock_page(page); + if (page->mapping != inode->i_mapping) { + unlock_page(page); +diff -Nur linux-4.0.4.orig/mm/madvise.c linux-4.0.4/mm/madvise.c +--- linux-4.0.4.orig/mm/madvise.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/mm/madvise.c 2015-05-30 22:11:30.000000000 +0200 +@@ -320,12 +320,12 @@ + * vma's reference to the file) can go away as soon as we drop + * mmap_sem. + */ +- get_file(f); ++ vma_get_file(vma); + up_read(¤t->mm->mmap_sem); + error = vfs_fallocate(f, + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + offset, end - start); +- fput(f); ++ vma_fput(vma); + down_read(¤t->mm->mmap_sem); + return error; + } +diff -Nur linux-4.0.4.orig/mm/Makefile linux-4.0.4/mm/Makefile +--- linux-4.0.4.orig/mm/Makefile 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/mm/Makefile 2015-05-30 22:11:30.000000000 +0200 +@@ -21,7 +21,7 @@ + mm_init.o mmu_context.o percpu.o slab_common.o \ + compaction.o vmacache.o \ + interval_tree.o list_lru.o workingset.o \ +- debug.o $(mmu-y) ++ prfile.o debug.o $(mmu-y) + + obj-y += init-mm.o + +diff -Nur linux-4.0.4.orig/mm/memory.c linux-4.0.4/mm/memory.c +--- linux-4.0.4.orig/mm/memory.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/mm/memory.c 2015-05-30 22:11:30.000000000 +0200 +@@ -2132,7 +2132,7 @@ + } + + if (!page_mkwrite) +- file_update_time(vma->vm_file); ++ vma_file_update_time(vma); + } + + return ret; +diff -Nur linux-4.0.4.orig/mm/mmap.c linux-4.0.4/mm/mmap.c +--- linux-4.0.4.orig/mm/mmap.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/mm/mmap.c 2015-05-30 22:11:30.000000000 +0200 +@@ -274,7 +274,7 @@ + if (vma->vm_ops && vma->vm_ops->close) + vma->vm_ops->close(vma); + if (vma->vm_file) +- fput(vma->vm_file); ++ vma_fput(vma); + mpol_put(vma_policy(vma)); + kmem_cache_free(vm_area_cachep, vma); + return next; +@@ -886,7 +886,7 @@ + if (remove_next) { + if (file) { + uprobe_munmap(next, next->vm_start, next->vm_end); +- fput(file); ++ vma_fput(vma); + } + if (next->anon_vma) + anon_vma_merge(vma, next); +@@ -1671,8 +1671,8 @@ + return addr; + + unmap_and_free_vma: ++ vma_fput(vma); + vma->vm_file = NULL; +- fput(file); + + /* Undo any partial mapping done by a device driver. */ + unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); +@@ -2473,7 +2473,7 @@ + goto out_free_mpol; + + if (new->vm_file) +- get_file(new->vm_file); ++ vma_get_file(new); + + if (new->vm_ops && new->vm_ops->open) + new->vm_ops->open(new); +@@ -2492,7 +2492,7 @@ + if (new->vm_ops && new->vm_ops->close) + new->vm_ops->close(new); + if (new->vm_file) +- fput(new->vm_file); ++ vma_fput(new); + unlink_anon_vmas(new); + out_free_mpol: + mpol_put(vma_policy(new)); +@@ -2635,7 +2635,6 @@ + struct vm_area_struct *vma; + unsigned long populate = 0; + unsigned long ret = -EINVAL; +- struct file *file; + + pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. " + "See Documentation/vm/remap_file_pages.txt.\n", +@@ -2679,10 +2678,10 @@ + munlock_vma_pages_range(vma, start, start + size); + } + +- file = get_file(vma->vm_file); ++ vma_get_file(vma); + ret = do_mmap_pgoff(vma->vm_file, start, size, + prot, flags, pgoff, &populate); +- fput(file); ++ vma_fput(vma); + out: + up_write(&mm->mmap_sem); + if (populate) +@@ -2950,7 +2949,7 @@ + if (anon_vma_clone(new_vma, vma)) + goto out_free_mempol; + if (new_vma->vm_file) +- get_file(new_vma->vm_file); ++ vma_get_file(new_vma); + if (new_vma->vm_ops && new_vma->vm_ops->open) + new_vma->vm_ops->open(new_vma); + vma_link(mm, new_vma, prev, rb_link, rb_parent); +diff -Nur linux-4.0.4.orig/mm/msync.c linux-4.0.4/mm/msync.c +--- linux-4.0.4.orig/mm/msync.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/mm/msync.c 2015-05-30 22:11:30.000000000 +0200 +@@ -84,10 +84,10 @@ + start = vma->vm_end; + if ((flags & MS_SYNC) && file && + (vma->vm_flags & VM_SHARED)) { +- get_file(file); ++ vma_get_file(vma); + up_read(&mm->mmap_sem); + error = vfs_fsync_range(file, fstart, fend, 1); +- fput(file); ++ vma_fput(vma); + if (error || start >= end) + goto out; + down_read(&mm->mmap_sem); +diff -Nur linux-4.0.4.orig/mm/nommu.c linux-4.0.4/mm/nommu.c +--- linux-4.0.4.orig/mm/nommu.c 2015-05-17 18:55:21.000000000 +0200 ++++ linux-4.0.4/mm/nommu.c 2015-05-30 22:11:30.000000000 +0200 +@@ -693,7 +693,7 @@ + up_write(&nommu_region_sem); + + if (region->vm_file) +- fput(region->vm_file); ++ vmr_fput(region); + + /* IO memory and memory shared directly out of the pagecache + * from ramfs/tmpfs mustn't be released here */ +@@ -858,7 +858,7 @@ + if (vma->vm_ops && vma->vm_ops->close) + vma->vm_ops->close(vma); + if (vma->vm_file) +- fput(vma->vm_file); ++ vma_fput(vma); + put_nommu_region(vma->vm_region); + kmem_cache_free(vm_area_cachep, vma); + } +@@ -1398,7 +1398,7 @@ + goto error_just_free; + } + } +- fput(region->vm_file); ++ vmr_fput(region); + kmem_cache_free(vm_region_jar, region); + region = pregion; + result = start; +@@ -1474,10 +1474,10 @@ + up_write(&nommu_region_sem); + error: + if (region->vm_file) +- fput(region->vm_file); ++ vmr_fput(region); + kmem_cache_free(vm_region_jar, region); + if (vma->vm_file) +- fput(vma->vm_file); ++ vma_fput(vma); + kmem_cache_free(vm_area_cachep, vma); + kleave(" = %d", ret); + return ret; +diff -Nur linux-4.0.4.orig/mm/prfile.c linux-4.0.4/mm/prfile.c +--- linux-4.0.4.orig/mm/prfile.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-4.0.4/mm/prfile.c 2015-05-30 22:11:30.000000000 +0200 +@@ -0,0 +1,86 @@ ++/* ++ * Mainly for aufs which mmap(2) diffrent file and wants to print different path ++ * in /proc/PID/maps. ++ * Call these functions via macros defined in linux/mm.h. ++ * ++ * See Documentation/filesystems/aufs/design/06mmap.txt ++ * ++ * Copyright (c) 2014 Junjro R. Okajima ++ * Copyright (c) 2014 Ian Campbell ++ */ ++ ++#include ++#include ++#include ++ ++/* #define PRFILE_TRACE */ ++static inline void prfile_trace(struct file *f, struct file *pr, ++ const char func[], int line, const char func2[]) ++{ ++#ifdef PRFILE_TRACE ++ if (pr) ++ pr_info("%s:%d: %s, %p\n", func, line, func2, ++ f ? (char *)f->f_path.dentry->d_name.name : "(null)"); ++#endif ++} ++ ++#ifdef CONFIG_MMU ++void vma_do_file_update_time(struct vm_area_struct *vma, const char func[], ++ int line) ++{ ++ struct file *f = vma->vm_file, *pr = vma->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ file_update_time(f); ++ if (f && pr) ++ file_update_time(pr); ++} ++ ++struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[], ++ int line) ++{ ++ struct file *f = vma->vm_file, *pr = vma->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ return (f && pr) ? pr : f; ++} ++ ++void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line) ++{ ++ struct file *f = vma->vm_file, *pr = vma->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ get_file(f); ++ if (f && pr) ++ get_file(pr); ++} ++ ++void vma_do_fput(struct vm_area_struct *vma, const char func[], int line) ++{ ++ struct file *f = vma->vm_file, *pr = vma->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ fput(f); ++ if (f && pr) ++ fput(pr); ++} ++#else ++struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[], ++ int line) ++{ ++ struct file *f = region->vm_file, *pr = region->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ return (f && pr) ? pr : f; ++} ++ ++void vmr_do_fput(struct vm_region *region, const char func[], int line) ++{ ++ struct file *f = region->vm_file, *pr = region->vm_prfile; ++ ++ prfile_trace(f, pr, func, line, __func__); ++ fput(f); ++ if (f && pr) ++ fput(pr); ++} ++#endif /* CONFIG_MMU */ diff --git a/target/linux/patches/4.0.6/cleankernel.patch b/target/linux/patches/4.0.6/cleankernel.patch new file mode 100644 index 000000000..d8c055dc3 --- /dev/null +++ b/target/linux/patches/4.0.6/cleankernel.patch @@ -0,0 +1,11 @@ +diff -Nur linux-3.11.5.orig/scripts/Makefile.headersinst linux-3.11.5/scripts/Makefile.headersinst +--- linux-3.11.5.orig/scripts/Makefile.headersinst 2013-10-14 03:14:45.000000000 +0200 ++++ linux-3.11.5/scripts/Makefile.headersinst 2013-10-15 16:33:10.000000000 +0200 +@@ -107,7 +107,6 @@ + + targets += $(install-file) + $(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE +- $(if $(unwanted),$(call cmd,remove),) + $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) + $(call if_changed,install) + diff --git a/target/linux/patches/4.0.6/cris-header.patch b/target/linux/patches/4.0.6/cris-header.patch new file mode 100644 index 000000000..2b5a88461 --- /dev/null +++ b/target/linux/patches/4.0.6/cris-header.patch @@ -0,0 +1,12 @@ +diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild +--- linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 ++++ linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild 2014-09-26 19:24:50.000000000 +0200 +@@ -1 +1,2 @@ + # CRISv10 arch ++header-y += ptrace.h +diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild +--- linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 ++++ linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild 2014-09-26 19:24:31.000000000 +0200 +@@ -1 +1,2 @@ + # CRISv32 arch ++header-y += ptrace.h diff --git a/target/linux/patches/4.0.6/initramfs-nosizelimit.patch b/target/linux/patches/4.0.6/initramfs-nosizelimit.patch new file mode 100644 index 000000000..40d2f6bd8 --- /dev/null +++ b/target/linux/patches/4.0.6/initramfs-nosizelimit.patch @@ -0,0 +1,57 @@ +From 9a18df7a71bfa620b1278777d64783a359d7eb4e Mon Sep 17 00:00:00 2001 +From: Thorsten Glaser +Date: Sun, 4 May 2014 01:37:54 +0200 +Subject: [PATCH] mount tmpfs-as-rootfs (initramfs) with -o + nr_blocks=0,nr_inodes=0 + +I would have preferred to write this patch to be able to pass +rootflags=nr_blocks=0,nr_inodes=0 on the kernel command line, +and then hand these rootflags over to the initramfs (tmpfs) +mount in the same way the kernel hands them over to the block +device rootfs mount. But at least the Debian/m68k initrd also +parses $rootflags from the environment and adds it to the call +to the user-space mount for the eventual root device, which +would make the kernel command line rootflags option be used in +both places (tmpfs and e.g. ext4) which is guaranteed to error +out in at least one of them. + +This change is intended to aid people in a setup where the +initrd is the final root filesystem, i.e. not mounted over. +This is especially useful in automated tests running on qemu +for boards with constrained memory (e.g. 64 MiB on sh4). + +Considering that the initramfs is normally emptied out then +overmounted, this change is probably safe for setups where +initramfs just hosts early userspace, too, since the tmpfs +backing it is not accessible any more later on, AFAICT. + +Signed-off-by: Thorsten Glaser +--- + init/do_mounts.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/init/do_mounts.c b/init/do_mounts.c +index 82f2288..55a4cfe 100644 +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -594,6 +594,7 @@ out: + } + + static bool is_tmpfs; ++static char tmpfs_rootflags[] = "nr_blocks=0,nr_inodes=0"; + static struct dentry *rootfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) + { +@@ -606,6 +607,9 @@ static struct dentry *rootfs_mount(struct file_system_type *fs_type, + if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) + fill = shmem_fill_super; + ++ if (is_tmpfs) ++ data = tmpfs_rootflags; ++ + return mount_nodev(fs_type, flags, data, fill); + } + +-- +2.0.0.rc0 + diff --git a/target/linux/patches/4.0.6/startup.patch b/target/linux/patches/4.0.6/startup.patch new file mode 100644 index 000000000..d396b75e4 --- /dev/null +++ b/target/linux/patches/4.0.6/startup.patch @@ -0,0 +1,37 @@ +diff -Nur linux-3.13.3.orig/init/main.c linux-3.13.3/init/main.c +--- linux-3.13.3.orig/init/main.c 2014-02-13 23:00:14.000000000 +0100 ++++ linux-3.13.3/init/main.c 2014-02-17 11:35:14.000000000 +0100 +@@ -916,6 +917,8 @@ + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + pr_err("Warning: unable to open an initial console.\n"); + ++ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); ++ + (void) sys_dup(0); + (void) sys_dup(0); + /* +diff -Nur linux-3.13.6.orig/init/initramfs.c linux-3.13.6/init/initramfs.c +--- linux-3.13.6.orig/init/initramfs.c 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/init/initramfs.c 2014-03-15 12:11:31.882731916 +0100 +@@ -622,6 +622,9 @@ + */ + load_default_modules(); + } ++#ifdef CONFIG_DEVTMPFS_MOUNT ++ devtmpfs_mount("dev"); ++#endif + return 0; + } + rootfs_initcall(populate_rootfs); +diff -Nur linux-3.13.6.orig/init/main.c linux-3.13.6/init/main.c +--- linux-3.13.6.orig/init/main.c 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/init/main.c 2014-03-15 12:13:16.459024452 +0100 +@@ -924,7 +924,7 @@ + */ + + if (!ramdisk_execute_command) +- ramdisk_execute_command = "/init"; ++ ramdisk_execute_command = "/sbin/init"; + + if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { + ramdisk_execute_command = NULL; diff --git a/target/linux/patches/4.1-rc5/cris-header.patch b/target/linux/patches/4.1-rc5/cris-header.patch deleted file mode 100644 index 2b5a88461..000000000 --- a/target/linux/patches/4.1-rc5/cris-header.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild ---- linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 -+++ linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild 2014-09-26 19:24:50.000000000 +0200 -@@ -1 +1,2 @@ - # CRISv10 arch -+header-y += ptrace.h -diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild ---- linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 -+++ linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild 2014-09-26 19:24:31.000000000 +0200 -@@ -1 +1,2 @@ - # CRISv32 arch -+header-y += ptrace.h diff --git a/target/linux/patches/4.1-rc5/initramfs-nosizelimit.patch b/target/linux/patches/4.1-rc5/initramfs-nosizelimit.patch deleted file mode 100644 index 40d2f6bd8..000000000 --- a/target/linux/patches/4.1-rc5/initramfs-nosizelimit.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 9a18df7a71bfa620b1278777d64783a359d7eb4e Mon Sep 17 00:00:00 2001 -From: Thorsten Glaser -Date: Sun, 4 May 2014 01:37:54 +0200 -Subject: [PATCH] mount tmpfs-as-rootfs (initramfs) with -o - nr_blocks=0,nr_inodes=0 - -I would have preferred to write this patch to be able to pass -rootflags=nr_blocks=0,nr_inodes=0 on the kernel command line, -and then hand these rootflags over to the initramfs (tmpfs) -mount in the same way the kernel hands them over to the block -device rootfs mount. But at least the Debian/m68k initrd also -parses $rootflags from the environment and adds it to the call -to the user-space mount for the eventual root device, which -would make the kernel command line rootflags option be used in -both places (tmpfs and e.g. ext4) which is guaranteed to error -out in at least one of them. - -This change is intended to aid people in a setup where the -initrd is the final root filesystem, i.e. not mounted over. -This is especially useful in automated tests running on qemu -for boards with constrained memory (e.g. 64 MiB on sh4). - -Considering that the initramfs is normally emptied out then -overmounted, this change is probably safe for setups where -initramfs just hosts early userspace, too, since the tmpfs -backing it is not accessible any more later on, AFAICT. - -Signed-off-by: Thorsten Glaser ---- - init/do_mounts.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/init/do_mounts.c b/init/do_mounts.c -index 82f2288..55a4cfe 100644 ---- a/init/do_mounts.c -+++ b/init/do_mounts.c -@@ -594,6 +594,7 @@ out: - } - - static bool is_tmpfs; -+static char tmpfs_rootflags[] = "nr_blocks=0,nr_inodes=0"; - static struct dentry *rootfs_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) - { -@@ -606,6 +607,9 @@ static struct dentry *rootfs_mount(struct file_system_type *fs_type, - if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) - fill = shmem_fill_super; - -+ if (is_tmpfs) -+ data = tmpfs_rootflags; -+ - return mount_nodev(fs_type, flags, data, fill); - } - --- -2.0.0.rc0 - diff --git a/target/linux/patches/4.1-rc5/startup.patch b/target/linux/patches/4.1-rc5/startup.patch deleted file mode 100644 index d396b75e4..000000000 --- a/target/linux/patches/4.1-rc5/startup.patch +++ /dev/null @@ -1,37 +0,0 @@ -diff -Nur linux-3.13.3.orig/init/main.c linux-3.13.3/init/main.c ---- linux-3.13.3.orig/init/main.c 2014-02-13 23:00:14.000000000 +0100 -+++ linux-3.13.3/init/main.c 2014-02-17 11:35:14.000000000 +0100 -@@ -916,6 +917,8 @@ - if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - pr_err("Warning: unable to open an initial console.\n"); - -+ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); -+ - (void) sys_dup(0); - (void) sys_dup(0); - /* -diff -Nur linux-3.13.6.orig/init/initramfs.c linux-3.13.6/init/initramfs.c ---- linux-3.13.6.orig/init/initramfs.c 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/init/initramfs.c 2014-03-15 12:11:31.882731916 +0100 -@@ -622,6 +622,9 @@ - */ - load_default_modules(); - } -+#ifdef CONFIG_DEVTMPFS_MOUNT -+ devtmpfs_mount("dev"); -+#endif - return 0; - } - rootfs_initcall(populate_rootfs); -diff -Nur linux-3.13.6.orig/init/main.c linux-3.13.6/init/main.c ---- linux-3.13.6.orig/init/main.c 2014-03-07 07:07:02.000000000 +0100 -+++ linux-3.13.6/init/main.c 2014-03-15 12:13:16.459024452 +0100 -@@ -924,7 +924,7 @@ - */ - - if (!ramdisk_execute_command) -- ramdisk_execute_command = "/init"; -+ ramdisk_execute_command = "/sbin/init"; - - if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { - ramdisk_execute_command = NULL; diff --git a/target/linux/patches/4.1/cris-header.patch b/target/linux/patches/4.1/cris-header.patch new file mode 100644 index 000000000..2b5a88461 --- /dev/null +++ b/target/linux/patches/4.1/cris-header.patch @@ -0,0 +1,12 @@ +diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild +--- linux-3.16.2.orig/arch/cris/include/arch-v10/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 ++++ linux-3.16.2/arch/cris/include/arch-v10/arch/Kbuild 2014-09-26 19:24:50.000000000 +0200 +@@ -1 +1,2 @@ + # CRISv10 arch ++header-y += ptrace.h +diff -Nur linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild +--- linux-3.16.2.orig/arch/cris/include/arch-v32/arch/Kbuild 2014-09-06 01:37:11.000000000 +0200 ++++ linux-3.16.2/arch/cris/include/arch-v32/arch/Kbuild 2014-09-26 19:24:31.000000000 +0200 +@@ -1 +1,2 @@ + # CRISv32 arch ++header-y += ptrace.h diff --git a/target/linux/patches/4.1/initramfs-nosizelimit.patch b/target/linux/patches/4.1/initramfs-nosizelimit.patch new file mode 100644 index 000000000..40d2f6bd8 --- /dev/null +++ b/target/linux/patches/4.1/initramfs-nosizelimit.patch @@ -0,0 +1,57 @@ +From 9a18df7a71bfa620b1278777d64783a359d7eb4e Mon Sep 17 00:00:00 2001 +From: Thorsten Glaser +Date: Sun, 4 May 2014 01:37:54 +0200 +Subject: [PATCH] mount tmpfs-as-rootfs (initramfs) with -o + nr_blocks=0,nr_inodes=0 + +I would have preferred to write this patch to be able to pass +rootflags=nr_blocks=0,nr_inodes=0 on the kernel command line, +and then hand these rootflags over to the initramfs (tmpfs) +mount in the same way the kernel hands them over to the block +device rootfs mount. But at least the Debian/m68k initrd also +parses $rootflags from the environment and adds it to the call +to the user-space mount for the eventual root device, which +would make the kernel command line rootflags option be used in +both places (tmpfs and e.g. ext4) which is guaranteed to error +out in at least one of them. + +This change is intended to aid people in a setup where the +initrd is the final root filesystem, i.e. not mounted over. +This is especially useful in automated tests running on qemu +for boards with constrained memory (e.g. 64 MiB on sh4). + +Considering that the initramfs is normally emptied out then +overmounted, this change is probably safe for setups where +initramfs just hosts early userspace, too, since the tmpfs +backing it is not accessible any more later on, AFAICT. + +Signed-off-by: Thorsten Glaser +--- + init/do_mounts.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/init/do_mounts.c b/init/do_mounts.c +index 82f2288..55a4cfe 100644 +--- a/init/do_mounts.c ++++ b/init/do_mounts.c +@@ -594,6 +594,7 @@ out: + } + + static bool is_tmpfs; ++static char tmpfs_rootflags[] = "nr_blocks=0,nr_inodes=0"; + static struct dentry *rootfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) + { +@@ -606,6 +607,9 @@ static struct dentry *rootfs_mount(struct file_system_type *fs_type, + if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) + fill = shmem_fill_super; + ++ if (is_tmpfs) ++ data = tmpfs_rootflags; ++ + return mount_nodev(fs_type, flags, data, fill); + } + +-- +2.0.0.rc0 + diff --git a/target/linux/patches/4.1/startup.patch b/target/linux/patches/4.1/startup.patch new file mode 100644 index 000000000..d396b75e4 --- /dev/null +++ b/target/linux/patches/4.1/startup.patch @@ -0,0 +1,37 @@ +diff -Nur linux-3.13.3.orig/init/main.c linux-3.13.3/init/main.c +--- linux-3.13.3.orig/init/main.c 2014-02-13 23:00:14.000000000 +0100 ++++ linux-3.13.3/init/main.c 2014-02-17 11:35:14.000000000 +0100 +@@ -916,6 +917,8 @@ + if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + pr_err("Warning: unable to open an initial console.\n"); + ++ printk(KERN_WARNING "Starting Linux (built with OpenADK).\n"); ++ + (void) sys_dup(0); + (void) sys_dup(0); + /* +diff -Nur linux-3.13.6.orig/init/initramfs.c linux-3.13.6/init/initramfs.c +--- linux-3.13.6.orig/init/initramfs.c 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/init/initramfs.c 2014-03-15 12:11:31.882731916 +0100 +@@ -622,6 +622,9 @@ + */ + load_default_modules(); + } ++#ifdef CONFIG_DEVTMPFS_MOUNT ++ devtmpfs_mount("dev"); ++#endif + return 0; + } + rootfs_initcall(populate_rootfs); +diff -Nur linux-3.13.6.orig/init/main.c linux-3.13.6/init/main.c +--- linux-3.13.6.orig/init/main.c 2014-03-07 07:07:02.000000000 +0100 ++++ linux-3.13.6/init/main.c 2014-03-15 12:13:16.459024452 +0100 +@@ -924,7 +924,7 @@ + */ + + if (!ramdisk_execute_command) +- ramdisk_execute_command = "/init"; ++ ramdisk_execute_command = "/sbin/init"; + + if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { + ramdisk_execute_command = NULL; -- cgit v1.2.3